diff --git a/.editorconfig b/.editorconfig index b1d2dbdb588..72e85029ef6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,22 +1,22 @@ +# http://editorconfig.org root = true [*] -charset = utf-8 +indent_style = tab end_of_line = lf -insert_final_newline = true +charset = utf-8 trim_trailing_whitespace = true +insert_final_newline = true -[*.{dme,dmf,dmm,dm}] -end_of_line = crlf -indent_style = tab - -[*.md] -trim_trailing_whitespace = false +[*.yml] +indent_style = space +indent_size = 2 [*.py] indent_style = space -indent_size = 4 -[*.yml] +[*.md] +trim_trailing_whitespace = false + +[Dockerfile] indent_style = space -indent_size = 2 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index c5587f60421..77c5aad8c4b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,16 +1,51 @@ +* text=auto + +## Enforce text mode and LF line breaks +*.bat text eol=lf +*.cjs text eol=lf +*.css text eol=lf +*.dm text eol=lf +*.dme text eol=lf +*.dmf text eol=lf +*.htm text eol=lf +*.html text eol=lf +*.js text eol=lf +*.json text eol=lf +*.jsx text eol=lf +*.md text eol=lf +*.ps1 text eol=lf +*.py text eol=lf +*.scss text eol=lf +*.sh text eol=lf +*.sql text eol=lf +*.svg text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.txt text eol=lf +*.yaml text eol=lf +*.yml text eol=lf + ## Enforce binary mode +*.bmp binary *.dll binary +*.dmb binary *.exe binary +*.gif binary +*.jpg binary +*.png binary *.so binary *.zip binary -# dmm map merger hook -# needs additional setup, see tools/mapmerge/install.txt -# *.dmm merge=merge-dmm +## Merger hooks, run tools/hooks/install.bat or install.sh to set up +#*.dmm text eol=lf merge=dmm +#*.dmi binary merge=dmi -# dmi icon merger hook +# old dmi icon merger hook # needs additional setup, see tools/dmitool/merging.txt *.dmi merge=merge-dmi +## Force tab indents on dm files +*.dm whitespace=indent-with-non-tab + # force changelog merging to use union html/changelog.html merge=union diff --git a/.github/workflows/autochangelog.yml b/.github/workflows/autochangelog.yml index c9c628fa29a..d0ab3fe4f66 100644 --- a/.github/workflows/autochangelog.yml +++ b/.github/workflows/autochangelog.yml @@ -22,7 +22,7 @@ jobs: - name: Ensure +x on CI directory run: | chmod -R +x ./tools/ci - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.7' - name: Generate Changelog @@ -36,7 +36,7 @@ jobs: python tools/GenerateChangelog/ss13_genchangelog.py \ html/changelog.html \ html/changelogs - - uses: stefanzweifel/git-auto-commit-action@v4 + - uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: Automatic changelog generation for ${{ github.events.pull_request.number }} branch: ${{ github.events.pull_request.base }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e984dcad991..4f10fceae29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: bash tools/ci/install_build_deps.sh - name: Restore Yarn cache if: "${{ contains(github.event.pull_request.labels.*.name, 'Type: TGUI Bundle') }}" - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tgui/.yarn/cache key: ${{ runner.os }}-yarn-${{ secrets.CACHE_PURGE_KEY }}-${{ hashFiles('tgui/yarn.lock') }} @@ -41,7 +41,7 @@ jobs: - uses: actions/checkout@v4 - name: Cache SpacemanDMM - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/SpacemanDMM key: ${{ runner.os }}-dreamchecker-${{ hashFiles('dependencies.sh')}} @@ -76,7 +76,7 @@ jobs: run: | chmod -R +x ./tools/ci - name: Setup Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: $HOME/BYOND key: ${{ runner.os }}-byond diff --git a/.gitignore b/.gitignore index b2dd85c6602..2833549fe5c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,15 @@ vchat.db* *.before *.pyc *.pid -data -data/ cfg/ +#Ignore the data folder/symlink itself. This is very important if you are using VGS +/data + +#Ignore everything in datafolder and subdirectories +/data/**/* +/tmp/**/* + #Visual studio stuff *.vscode/* !/.vscode/launch.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 8e110ef9bd9..a004179bb77 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "eslint.nodePath": "./tgui/.yarn/sdks", "eslint.workingDirectories": ["./tgui"], - "prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.js", + "prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs", "typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib", "typescript.enablePromptUseWorkspaceTsdk": true, "search.exclude": { @@ -9,8 +9,11 @@ "**/.pnp.*": true }, "workbench.editorAssociations": { - "*.dmi": "imagePreview.previewEditor" + "*.dmi": "dmiEditor.dmiEditor" }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, "files.eol": "\n", "files.encoding": "utf8", "files.insertFinalNewline": true, @@ -24,7 +27,7 @@ ], "tgstationTestExplorer.project.resultsType": "json", "[dm]": { - "files.eol": "\r\n", + "files.eol": "\n", "editor.detectIndentation": false, "editor.insertSpaces": false }, diff --git a/Dockerfile b/Dockerfile index d7cffbcfb68..e143dd1d9cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,23 @@ -FROM tgstation/byond:513.1533 as base +FROM i386/ubuntu:xenial as base + +ARG BYOND_MAJOR=514 +ARG BYOND_MINOR=1589 + +RUN apt-get update \ + && apt-get install -y \ + curl \ + unzip \ + make \ + libstdc++6 \ + && curl "https://www.byond.com/download/build/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -o byond.zip \ + && unzip byond.zip \ + && cd byond \ + && sed -i 's|install:|&\n\tmkdir -p $(MAN_DIR)/man6|' Makefile \ + && make install \ + && chmod 644 /usr/local/byond/man/man6/* \ + && apt-get purge -y --auto-remove curl unzip make \ + && cd .. \ + && rm -rf byond byond.zip /var/lib/apt/lists/* FROM base as rust_g @@ -16,7 +35,7 @@ RUN apt-get install -y --no-install-recommends \ gcc-multilib \ && curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host i686-unknown-linux-gnu \ && git init \ - && git remote add origin https://github.com/tgstation/rust-g + && git remote add origin https://github.com/VOREStation/rust-g COPY _build_dependencies.sh . @@ -52,8 +71,8 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /root/.byond/bin -COPY --from=rust_g /rust_g/target/release/librust_g.so /root/.byond/bin/rust_g COPY --from=build /vorestation/ ./ +COPY --from=rust_g /rust_g/target/release/librust_g.so ./librust_g.so #VOLUME [ "/vorestation/config", "/vorestation/data" ] diff --git a/README.md b/README.md index 130dddb98df..b878df4d7e1 100644 --- a/README.md +++ b/README.md @@ -1,102 +1,102 @@ -# VOREStation - -[![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/compatibility-club-penguin.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/no-ragrets.svg)](http://forthebadge.com) - -[Website](https://vore-station.net) - [Forums](https://forum.vore-station.net/) - [Wiki](https://wiki.vore-station.net/) - -Going to make a Pull Request? Make sure you read the [CONTRIBUTING.md](.github/CONTRIBUTING.md) first! - -Want to make a test server or your own private server? Read our [GitHub Wiki!](https://github.com/VOREStation/VOREStation/wiki) - -VOREStation is a fork of the Polaris code branch, itself a fork of the Baystation12 code branch, for the game Space Station 13. - -![Render Nanomaps](https://github.com/VOREStation/VOREStation/workflows/Render%20Nanomaps/badge.svg) - ---- - -### LICENSE -The code for VOREStation is licensed under the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl.html) version 3, which can be found in full in LICENSE-AGPL3.txt. - -Code with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt. - -All code whose authorship dates are not prior to `1420675200 +0000` is assumed to be licensed under AGPL v3, if you wish to license under GPL v3 please make this clear in the commit message and any added files. - -If you wish to develop and host this codebase in a closed source manner you may use all code prior to `1420675200 +0000`, which is licensed under GPL v3. The major change here is that if you host a server using any code licensed under AGPLv3 you are required to provide full source code for your servers users as well including addons and modifications you have made. - -See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for more information. - -Any files located in the -`vorestation/goon`, -`vorestation/icons/goonstation`, or -`vorestation/sound/goonstation` -directories, or any subdirectories of mentioned directories are licensed under the -Creative Commons 3.0 BY-NC-SA license -(https://creativecommons.org/licenses/by-nc-sa/3.0) - -All assets including icons and sound are under a [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) license unless otherwise indicated. - -Attributions and other licenses with links to original works are noted in [ATTRIBUTIONS.md](./ATTRIBUTIONS.md). - -### GETTING THE CODE -The simplest way to obtain the code is using the github .zip feature. If you do this, you won't be able to make a Pull Request later, though. You'll need to use the git method. - -Click [here](https://github.com/VOREStation/VOREStation/archive/master.zip) to get the latest code as a .zip file, then unzip it to wherever you want. - -The more complicated and easier to update method is using git. You'll need to download git or some client from [here](http://git-scm.com/). When that's installed, right click in any folder and click on "Git Bash". When that opens, type in: - - git clone https://github.com/VOREStation/VOREStation.git - -(hint: hold down ctrl and press insert to paste into git bash) - -This will take a while to download, but it provides an easier method for updating. - -### INSTALLATION - -First-time installation should be fairly straightforward. First, you'll need BYOND installed. You can get it from [here](http://www.byond.com/). - -This is a sourcecode-only release, so the next step is to compile the server files. Open vorestation.dme by double-clicking it, open the Build menu, and click compile. This'll take a little while, and if everything's done right you'll get a message like this: - - saving vorestation.dmb (DEBUG mode) - - vorestation.dmb - 0 errors, 0 warnings - -If you see any errors or warnings, something has gone wrong - possibly a corrupt download or the files extracted wrong, or a code issue on the main repo. Ask on IRC. - -Once that's done, open up the config folder. You'll want to edit config.txt to set the probabilities for different gamemodes in Secret and to set your server location so that all your players don't get disconnected at the end of each round. It's recommended you don't turn on the gamemodes with probability 0, as they have various issues and aren't currently being tested, so they may have unknown and bizarre bugs. - -You'll also want to edit admins.txt to remove the default admins and add your own. "Host" is the highest level of access, and the other recommended admin levels for now are "Game Admin" and "Moderator". The format is: - - byondkey - Rank - -where the BYOND key must be in lowercase and the admin rank must be properly capitalised. There are a bunch more admin ranks, but these two should be enough for most servers, assuming you have trustworthy admins. - -Finally, to start the server, run Dream Daemon and enter the path to your compiled vorestation.dmb file. Make sure to set the port to the one you specified in the config.txt, and set the Security box to 'Trusted'. Then press GO and the server should start up and be ready to join. - ---- - -### UPDATING - -To update an existing installation, first back up your /config and /data folders -as these store your server configuration, player preferences and banlist. - -If you used the zip method, you'll need to download the zip file again and unzip it somewhere else, and then copy the /config and /data folders over. - -If you used the git method, you simply need to type this in to git bash: - - git pull - -When this completes, copy over your /data and /config folders again, just in case. - -When you have done this, you'll need to recompile the code, but then it should work fine. - ---- - -### Configuration - -For a basic setup, simply copy every file from config/example to config. - ---- - -### SQL Setup - -The SQL backend for the library and stats tracking requires a MySQL server. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql. More detailed setup instructions arecoming soon, for now ask in our Discord. +# VOREStation + +[![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/compatibility-club-penguin.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/no-ragrets.svg)](http://forthebadge.com) + +[Website](https://vore-station.net) - [Forums](https://forum.vore-station.net/) - [Wiki](https://wiki.vore-station.net/) + +Going to make a Pull Request? Make sure you read the [CONTRIBUTING.md](.github/CONTRIBUTING.md) first! + +Want to make a test server or your own private server? Read our [GitHub Wiki!](https://github.com/VOREStation/VOREStation/wiki) + +VOREStation is a fork of the Polaris code branch, itself a fork of the Baystation12 code branch, for the game Space Station 13. + +![Render Nanomaps](https://github.com/VOREStation/VOREStation/workflows/Render%20Nanomaps/badge.svg) + +--- + +### LICENSE +The code for VOREStation is licensed under the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl.html) version 3, which can be found in full in LICENSE-AGPL3.txt. + +Code with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt. + +All code whose authorship dates are not prior to `1420675200 +0000` is assumed to be licensed under AGPL v3, if you wish to license under GPL v3 please make this clear in the commit message and any added files. + +If you wish to develop and host this codebase in a closed source manner you may use all code prior to `1420675200 +0000`, which is licensed under GPL v3. The major change here is that if you host a server using any code licensed under AGPLv3 you are required to provide full source code for your servers users as well including addons and modifications you have made. + +See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for more information. + +Any files located in the +`vorestation/goon`, +`vorestation/icons/goonstation`, or +`vorestation/sound/goonstation` +directories, or any subdirectories of mentioned directories are licensed under the +Creative Commons 3.0 BY-NC-SA license +(https://creativecommons.org/licenses/by-nc-sa/3.0) + +All assets including icons and sound are under a [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) license unless otherwise indicated. + +Attributions and other licenses with links to original works are noted in [ATTRIBUTIONS.md](./ATTRIBUTIONS.md). + +### GETTING THE CODE +The simplest way to obtain the code is using the github .zip feature. If you do this, you won't be able to make a Pull Request later, though. You'll need to use the git method. + +Click [here](https://github.com/VOREStation/VOREStation/archive/master.zip) to get the latest code as a .zip file, then unzip it to wherever you want. + +The more complicated and easier to update method is using git. You'll need to download git or some client from [here](http://git-scm.com/). When that's installed, right click in any folder and click on "Git Bash". When that opens, type in: + + git clone https://github.com/VOREStation/VOREStation.git + +(hint: hold down ctrl and press insert to paste into git bash) + +This will take a while to download, but it provides an easier method for updating. + +### INSTALLATION + +First-time installation should be fairly straightforward. First, you'll need BYOND installed. You can get it from [here](http://www.byond.com/). + +This is a sourcecode-only release, so the next step is to compile the server files. Open vorestation.dme by double-clicking it, open the Build menu, and click compile. This'll take a little while, and if everything's done right you'll get a message like this: + + saving vorestation.dmb (DEBUG mode) + + vorestation.dmb - 0 errors, 0 warnings + +If you see any errors or warnings, something has gone wrong - possibly a corrupt download or the files extracted wrong, or a code issue on the main repo. Ask on IRC. + +Once that's done, open up the config folder. You'll want to edit config.txt to set the probabilities for different gamemodes in Secret and to set your server location so that all your players don't get disconnected at the end of each round. It's recommended you don't turn on the gamemodes with probability 0, as they have various issues and aren't currently being tested, so they may have unknown and bizarre bugs. + +You'll also want to edit admins.txt to remove the default admins and add your own. "Host" is the highest level of access, and the other recommended admin levels for now are "Game Admin" and "Moderator". The format is: + + byondkey - Rank + +where the BYOND key must be in lowercase and the admin rank must be properly capitalised. There are a bunch more admin ranks, but these two should be enough for most servers, assuming you have trustworthy admins. + +Finally, to start the server, run Dream Daemon and enter the path to your compiled vorestation.dmb file. Make sure to set the port to the one you specified in the config.txt, and set the Security box to 'Trusted'. Then press GO and the server should start up and be ready to join. + +--- + +### UPDATING + +To update an existing installation, first back up your /config and /data folders +as these store your server configuration, player preferences and banlist. + +If you used the zip method, you'll need to download the zip file again and unzip it somewhere else, and then copy the /config and /data folders over. + +If you used the git method, you simply need to type this in to git bash: + + git pull + +When this completes, copy over your /data and /config folders again, just in case. + +When you have done this, you'll need to recompile the code, but then it should work fine. + +--- + +### Configuration + +For a basic setup, simply copy every file from config/example to config. + +--- + +### SQL Setup + +The SQL backend for the library and stats tracking requires a MySQL server. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql. More detailed setup instructions arecoming soon, for now ask in our Discord. diff --git a/bot/CORE_DATA.py b/bot/CORE_DATA.py index ca235888f15..5fe47961db1 100644 --- a/bot/CORE_DATA.py +++ b/bot/CORE_DATA.py @@ -1,13 +1,13 @@ -Name = "CC_NanoTrasen" #The name he uses to connect -no_absolute_paths = True -debug_on = False -SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, in lowercase -DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False -directory = "bot/directory/here/" # Directory the bot is located in, make sure to keep the "/" at the end -version = "TG CC-BY-SA 6" -Network = 'YOUR.SERVER.HERE' #e.g. "irc.rizon.net" -channel = "#YOUR CHANNEL HERE" #what channel you want the bot in -channels = ["#YOUR CHANNEL HERE"] #same as above -greeting = "Welcome!" #what he says when a person he hasn't seen before joins -prefix = "!" #prefix for bot commands -Port = 7000 +Name = "CC_NanoTrasen" #The name he uses to connect +no_absolute_paths = True +debug_on = False +SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, in lowercase +DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False +directory = "bot/directory/here/" # Directory the bot is located in, make sure to keep the "/" at the end +version = "TG CC-BY-SA 6" +Network = 'YOUR.SERVER.HERE' #e.g. "irc.rizon.net" +channel = "#YOUR CHANNEL HERE" #what channel you want the bot in +channels = ["#YOUR CHANNEL HERE"] #same as above +greeting = "Welcome!" #what he says when a person he hasn't seen before joins +prefix = "!" #prefix for bot commands +Port = 7000 diff --git a/bot/C_eightball.py b/bot/C_eightball.py index 2ed73dafd57..3841c221720 100644 --- a/bot/C_eightball.py +++ b/bot/C_eightball.py @@ -1,32 +1,32 @@ -from random import choice as fsample #Yay for added speed! -global responses -responses = ['Yes','Too bad','Will you turn me off if I tell you?','Absolutely', - "Not at all", "Nope", "It does", "No", "All the time", - "I don't really know", "Could be","Possibly","You're still here?",# Chaoticag - "No idea", "Of course", "Would you turn me off if I tell you?", - "Sweet!","Nah","Certainly","Yeah","Yup","I am quite confident that the answer is Yes", - "Perhaps", "Yeeeeaah... No.", "Indubitably" ] # Richard -def eightball(data,debug,sender,prefix): - global responses - arg = data.lower().replace(prefix+"eightball ","") - arg = arg.replace(prefix+"8ball ","") - if debug: - print sender+":"+prefix+"eightball", arg - if "answer" in arg and "everything" in arg and "to" in arg: - if debug: - print "Responded with",42 - return "42" - elif arg == "derp": - if debug: - print "Responded with herp" - return("herp") - elif arg == "herp": - if debug: - print "Responded with derp" - return("derp") - else: - #choice = sample(responses,1)[0] - choice = fsample(responses) - if debug: - print "Responded with", choice - return(choice) +from random import choice as fsample #Yay for added speed! +global responses +responses = ['Yes','Too bad','Will you turn me off if I tell you?','Absolutely', + "Not at all", "Nope", "It does", "No", "All the time", + "I don't really know", "Could be","Possibly","You're still here?",# Chaoticag + "No idea", "Of course", "Would you turn me off if I tell you?", + "Sweet!","Nah","Certainly","Yeah","Yup","I am quite confident that the answer is Yes", + "Perhaps", "Yeeeeaah... No.", "Indubitably" ] # Richard +def eightball(data,debug,sender,prefix): + global responses + arg = data.lower().replace(prefix+"eightball ","") + arg = arg.replace(prefix+"8ball ","") + if debug: + print sender+":"+prefix+"eightball", arg + if "answer" in arg and "everything" in arg and "to" in arg: + if debug: + print "Responded with",42 + return "42" + elif arg == "derp": + if debug: + print "Responded with herp" + return("herp") + elif arg == "herp": + if debug: + print "Responded with derp" + return("derp") + else: + #choice = sample(responses,1)[0] + choice = fsample(responses) + if debug: + print "Responded with", choice + return(choice) diff --git a/bot/C_heaortai.py b/bot/C_heaortai.py index 6e8b304e73b..257c3a86346 100644 --- a/bot/C_heaortai.py +++ b/bot/C_heaortai.py @@ -1,5 +1,5 @@ -#Throws a coin, simple. -from random import random -def heaortai(debug,sender): return("Heads" if random() > 0.5 else "Tails") -# Takes 1/6th the time of doing it with random.randint(0,1) -# This file used to be a lot bigger, now it's kind of useless. +#Throws a coin, simple. +from random import random +def heaortai(debug,sender): return("Heads" if random() > 0.5 else "Tails") +# Takes 1/6th the time of doing it with random.randint(0,1) +# This file used to be a lot bigger, now it's kind of useless. diff --git a/bot/C_makequote.py b/bot/C_makequote.py index f79b863449a..9ae556ab7ef 100644 --- a/bot/C_makequote.py +++ b/bot/C_makequote.py @@ -1,21 +1,21 @@ -from save_load import save -from os import listdir -import CORE_DATA -directory = CORE_DATA.directory -def mkquote(prefix,influx,sender,debug): - arg = influx[10+len(prefix):] - if debug: - print sender+":"+prefix+"makequote "+str(len(arg))+" Characters" - if len(arg) == 0: - return("Type something to a quote") - else: - files = listdir(directory+"userquotes") - numb = 0 - while True: - numb += 1 - if sender.lower()+str(numb) in files: - pass - else: - save(directory+"userquotes/"+sender.lower()+str(numb),[arg,sender.lower()]) - return("Saved as:"+sender.lower()+str(numb)) - break +from save_load import save +from os import listdir +import CORE_DATA +directory = CORE_DATA.directory +def mkquote(prefix,influx,sender,debug): + arg = influx[10+len(prefix):] + if debug: + print sender+":"+prefix+"makequote "+str(len(arg))+" Characters" + if len(arg) == 0: + return("Type something to a quote") + else: + files = listdir(directory+"userquotes") + numb = 0 + while True: + numb += 1 + if sender.lower()+str(numb) in files: + pass + else: + save(directory+"userquotes/"+sender.lower()+str(numb),[arg,sender.lower()]) + return("Saved as:"+sender.lower()+str(numb)) + break diff --git a/bot/C_maths.py b/bot/C_maths.py index 86013b74a7b..ea604573314 100644 --- a/bot/C_maths.py +++ b/bot/C_maths.py @@ -1,70 +1,70 @@ -### EXPERIMENTAL PROTOTYPE ### -# e = 2.7182818284590452353602874713526624977572 -# pi = math.pi -from __future__ import division #PYTHON Y U NO TELL ME THIS BEFORE -import math -import random -import re -e = "2.7182818284590452353602874713526624977572" -pi = str(math.pi) -global pre -pre = len("maths ") -def maths(influx,prefix="!",sender="NaN",debug=True,method="n"): - global pre - influx = influx.lower() - influx = influx[len(prefix)+pre:] - influx = influx.replace("pie",pi+"*"+e) - influx = influx.replace("e*",e+"*") - influx = influx.replace("*e","*"+e) - influx = influx.replace("pi",pi) - if debug: - print sender+":"+prefix+"maths" - if influx.count("**") == 0 and influx.count('"') == 0 and influx.count("'") == 0 and influx.count(";") == 0 and influx.count(":") == 0: - influx_low = influx.lower() - influx_hi = influx.upper() - if "0b" in influx_low: - influx_low = re.sub("0b[0-1]*","",influx_low) - influx_hi = re.sub("0B[0-1]*","",influx_hi) - if "0x" in influx_low: - influx_low = re.sub("0x[a-f0-9]*","",influx_low) - influx_hi = re.sub("0X[A-F0-9]*","",influx_hi) - if "rand" in influx_low: - influx_low = re.sub("rand","",influx_low) - influx_hi = re.sub("RAND","",influx_hi) - if influx_low == influx_hi: - influx = re.sub("rand","random.random()",influx) - try: - result = eval(influx.lower()) - except ZeroDivisionError: - return "Divide by zero detected." - except SyntaxError: - return "Syntax Error detected." - except TypeError: - return "Type Error detected." - except: - return "Unknown Error detected." - else: - if method == "n": #Normal - return result - elif method == "i": #Forced Int - return int(result) - elif method == "h": #Hex - try: - if "L" in hex(result)[2:]: - return hex(result)[2:-1] - else: - return hex(result)[2:].upper() - except TypeError: - return "That value (%s) cannot be interpreted properly using !hmaths" %(str(result)) - elif method == "b": #Binary - try: - return bin(result)[2:].upper() - except TypeError: - return "That value (%s) cannot be interpreted properly using !bmaths" %(str(result)) - else: - return result - else: - return "What are you trying to make me do again?" - else: - return "Those are likely to make me hang" - +### EXPERIMENTAL PROTOTYPE ### +# e = 2.7182818284590452353602874713526624977572 +# pi = math.pi +from __future__ import division #PYTHON Y U NO TELL ME THIS BEFORE +import math +import random +import re +e = "2.7182818284590452353602874713526624977572" +pi = str(math.pi) +global pre +pre = len("maths ") +def maths(influx,prefix="!",sender="NaN",debug=True,method="n"): + global pre + influx = influx.lower() + influx = influx[len(prefix)+pre:] + influx = influx.replace("pie",pi+"*"+e) + influx = influx.replace("e*",e+"*") + influx = influx.replace("*e","*"+e) + influx = influx.replace("pi",pi) + if debug: + print sender+":"+prefix+"maths" + if influx.count("**") == 0 and influx.count('"') == 0 and influx.count("'") == 0 and influx.count(";") == 0 and influx.count(":") == 0: + influx_low = influx.lower() + influx_hi = influx.upper() + if "0b" in influx_low: + influx_low = re.sub("0b[0-1]*","",influx_low) + influx_hi = re.sub("0B[0-1]*","",influx_hi) + if "0x" in influx_low: + influx_low = re.sub("0x[a-f0-9]*","",influx_low) + influx_hi = re.sub("0X[A-F0-9]*","",influx_hi) + if "rand" in influx_low: + influx_low = re.sub("rand","",influx_low) + influx_hi = re.sub("RAND","",influx_hi) + if influx_low == influx_hi: + influx = re.sub("rand","random.random()",influx) + try: + result = eval(influx.lower()) + except ZeroDivisionError: + return "Divide by zero detected." + except SyntaxError: + return "Syntax Error detected." + except TypeError: + return "Type Error detected." + except: + return "Unknown Error detected." + else: + if method == "n": #Normal + return result + elif method == "i": #Forced Int + return int(result) + elif method == "h": #Hex + try: + if "L" in hex(result)[2:]: + return hex(result)[2:-1] + else: + return hex(result)[2:].upper() + except TypeError: + return "That value (%s) cannot be interpreted properly using !hmaths" %(str(result)) + elif method == "b": #Binary + try: + return bin(result)[2:].upper() + except TypeError: + return "That value (%s) cannot be interpreted properly using !bmaths" %(str(result)) + else: + return result + else: + return "What are you trying to make me do again?" + else: + return "Those are likely to make me hang" + diff --git a/bot/C_rot13.py b/bot/C_rot13.py index 62b72dc2ad2..36e0796b9be 100644 --- a/bot/C_rot13.py +++ b/bot/C_rot13.py @@ -1,23 +1,23 @@ -global parta,partb -parta = {"A":"N","B":"O","C":"P","D":"Q","E":"R","F":"S","G":"T","H":"U","I":"V","J":"W","K":"X","L":"Y","M":"Z"} -partb = {'O':'B','N':'A','Q':'D','P':'C','S':'F','R':'E','U':'H','T':'G','W':'J','V':'I','Y':'L','X':'K','Z':'M'} -def rot13(text): - global parta,partb - newtext = "" - for letter in text: - try: - if letter.isupper(): - newtext += parta[letter] - else: - newtext += parta[letter.upper()].lower() - except: - try: - if letter.isupper(): - newtext += partb[letter] - pass - else: - newtext += partb[letter.upper()].lower() - pass - except: - newtext += letter - return newtext +global parta,partb +parta = {"A":"N","B":"O","C":"P","D":"Q","E":"R","F":"S","G":"T","H":"U","I":"V","J":"W","K":"X","L":"Y","M":"Z"} +partb = {'O':'B','N':'A','Q':'D','P':'C','S':'F','R':'E','U':'H','T':'G','W':'J','V':'I','Y':'L','X':'K','Z':'M'} +def rot13(text): + global parta,partb + newtext = "" + for letter in text: + try: + if letter.isupper(): + newtext += parta[letter] + else: + newtext += parta[letter.upper()].lower() + except: + try: + if letter.isupper(): + newtext += partb[letter] + pass + else: + newtext += partb[letter.upper()].lower() + pass + except: + newtext += letter + return newtext diff --git a/bot/C_rtd.py b/bot/C_rtd.py index 96736fec21c..e4a032a0de8 100644 --- a/bot/C_rtd.py +++ b/bot/C_rtd.py @@ -1,96 +1,96 @@ -import random -def rtd(data,debug,sender): - backo = data - try: - arg1,arg2 = backo.split("d") - except ValueError, err: - return("Too many or too small amount of arguments") - else: - if debug: - print sender+":!rtd "+arg1+"d"+arg2 #faster than using %s's - die,die2 = [],[] - current_mark = "" - outcome = 0 - realnumberfound = False - checks = [] - count = 0 - arg1 = arg1.replace(" ","") - arg2 = arg2.replace(" ","") - try: - i_arg1 = int(arg1) - a_arg1 = abs(i_arg1) - if "+" in arg2 or "-" in arg2: - plus_spot = arg2.find("+") - minus_spot = arg2.find("-") - if plus_spot == -1 and minus_spot == -1: - nicer_form = "" - elif plus_spot != -1 and minus_spot == -1: - nicer_form = arg2[plus_spot:] - elif plus_spot == -1 and minus_spot != -1: - nicer_form = arg2[minus_spot:] - else: - if plus_spot < minus_spot: - nicer_form = arg2[plus_spot:] - else: - nicer_form = arg2[minus_spot:] - for letter in arg2: - if letter == "+" or letter == "-": - current_mark = letter - checks = [] - count += 1 - continue - checks.append(letter) - try: - next_up = arg2[count+1] - except: - if realnumberfound == False: - i_arg2 = int("".join(checks)) - checks = [] - realnumberfound = True - elif current_mark == "+": - outcome += int("".join(checks)) - else: - outcome -= int("".join(checks)) - else: - if next_up == "+" or next_up == "-": - if realnumberfound == False: - i_arg2 = int("".join(checks)) - checks = [] - realnumberfound = True - else: - if current_mark == "+": - outcome += int("".join(checks)) - else: - outcome -= int("".join(checks)) - checks = [] - count += 1 - else: - i_arg2 = int(arg2) - if a_arg1 == 0 or abs(i_arg2) == 0: - raise RuntimeError - except ValueError: - return("You lied! That's not a number!") - except RuntimeError: - return("Too many zeroes!") - else: - if a_arg1 > 100: - return("Too many rolls, I can only do one hundred at max.") - else: - for i in xrange(0,a_arg1): - if i_arg2 < 0: - dice = random.randint(i_arg2,0) - else: - dice = random.randint(1,i_arg2) - die.append(dice) - die2.append(str(dice)) - if i_arg2 < 0: - flist = "".join(die2) - else: - flist = "+".join(die2) - if len(flist) > 350: - return(str(reduce(lambda x,y: x+y, die)+outcome)) - else: - if current_mark == "": - return(flist+" = "+str(reduce(lambda x,y: x+y, die)+outcome)) - else: - return(flist+" ("+nicer_form+") = "+str(reduce(lambda x,y: x+y, die)+outcome)) +import random +def rtd(data,debug,sender): + backo = data + try: + arg1,arg2 = backo.split("d") + except ValueError, err: + return("Too many or too small amount of arguments") + else: + if debug: + print sender+":!rtd "+arg1+"d"+arg2 #faster than using %s's + die,die2 = [],[] + current_mark = "" + outcome = 0 + realnumberfound = False + checks = [] + count = 0 + arg1 = arg1.replace(" ","") + arg2 = arg2.replace(" ","") + try: + i_arg1 = int(arg1) + a_arg1 = abs(i_arg1) + if "+" in arg2 or "-" in arg2: + plus_spot = arg2.find("+") + minus_spot = arg2.find("-") + if plus_spot == -1 and minus_spot == -1: + nicer_form = "" + elif plus_spot != -1 and minus_spot == -1: + nicer_form = arg2[plus_spot:] + elif plus_spot == -1 and minus_spot != -1: + nicer_form = arg2[minus_spot:] + else: + if plus_spot < minus_spot: + nicer_form = arg2[plus_spot:] + else: + nicer_form = arg2[minus_spot:] + for letter in arg2: + if letter == "+" or letter == "-": + current_mark = letter + checks = [] + count += 1 + continue + checks.append(letter) + try: + next_up = arg2[count+1] + except: + if realnumberfound == False: + i_arg2 = int("".join(checks)) + checks = [] + realnumberfound = True + elif current_mark == "+": + outcome += int("".join(checks)) + else: + outcome -= int("".join(checks)) + else: + if next_up == "+" or next_up == "-": + if realnumberfound == False: + i_arg2 = int("".join(checks)) + checks = [] + realnumberfound = True + else: + if current_mark == "+": + outcome += int("".join(checks)) + else: + outcome -= int("".join(checks)) + checks = [] + count += 1 + else: + i_arg2 = int(arg2) + if a_arg1 == 0 or abs(i_arg2) == 0: + raise RuntimeError + except ValueError: + return("You lied! That's not a number!") + except RuntimeError: + return("Too many zeroes!") + else: + if a_arg1 > 100: + return("Too many rolls, I can only do one hundred at max.") + else: + for i in xrange(0,a_arg1): + if i_arg2 < 0: + dice = random.randint(i_arg2,0) + else: + dice = random.randint(1,i_arg2) + die.append(dice) + die2.append(str(dice)) + if i_arg2 < 0: + flist = "".join(die2) + else: + flist = "+".join(die2) + if len(flist) > 350: + return(str(reduce(lambda x,y: x+y, die)+outcome)) + else: + if current_mark == "": + return(flist+" = "+str(reduce(lambda x,y: x+y, die)+outcome)) + else: + return(flist+" ("+nicer_form+") = "+str(reduce(lambda x,y: x+y, die)+outcome)) diff --git a/bot/C_sarcasticball.py b/bot/C_sarcasticball.py index 1f5ae321816..f5b0e60b1de 100644 --- a/bot/C_sarcasticball.py +++ b/bot/C_sarcasticball.py @@ -1,30 +1,30 @@ -from random import choice as fsample -sarcastic_responses = ["Yeah right","What do I look like to you?","Are you kidding me?",#UsF - "As much as you","You don't believe that yourself","When pigs fly",#UsF - "Like your grandma","You would like to know, wouldn't you?", #UsF - "Like your mom", #Spectre - "Totally","Not at all", #Spectre - "AHAHAHahahaha, No.", #Strumpetplaya - "Not as much as USER","As much as USER", - "Really, you expect me to tell you that?", - "Right, and you've been building NOUNs for those USERs in the LOCATION, haven't you?" ] #Richard -locations = ["woods","baystation","ditch"] -nouns = ["bomb","toilet","robot","cyborg", - "garbage can","gun","cake", - "missile"] -def sarcasticball(data,debug,sender,users,prefix): - arg = data.lower().replace(prefix+"sarcasticball ","") - arg = arg.replace(prefix+"sball ","") - if debug: - print sender+":"+prefix+"sarcasticball", arg - choice = fsample(sarcastic_responses) - if "USER" in choice: - choice = choice.replace("USER",fsample(users),1) - choice = choice.replace("USER",fsample(users),1) - if "NOUN" in choice: - choice = choice.replace("NOUN",fsample(nouns),1) - if "LOCATION" in choice: - choice = choice.replace("LOCATION",fsample(locations),1) - if debug: - print "Responded with", choice - return(choice) +from random import choice as fsample +sarcastic_responses = ["Yeah right","What do I look like to you?","Are you kidding me?",#UsF + "As much as you","You don't believe that yourself","When pigs fly",#UsF + "Like your grandma","You would like to know, wouldn't you?", #UsF + "Like your mom", #Spectre + "Totally","Not at all", #Spectre + "AHAHAHahahaha, No.", #Strumpetplaya + "Not as much as USER","As much as USER", + "Really, you expect me to tell you that?", + "Right, and you've been building NOUNs for those USERs in the LOCATION, haven't you?" ] #Richard +locations = ["woods","baystation","ditch"] +nouns = ["bomb","toilet","robot","cyborg", + "garbage can","gun","cake", + "missile"] +def sarcasticball(data,debug,sender,users,prefix): + arg = data.lower().replace(prefix+"sarcasticball ","") + arg = arg.replace(prefix+"sball ","") + if debug: + print sender+":"+prefix+"sarcasticball", arg + choice = fsample(sarcastic_responses) + if "USER" in choice: + choice = choice.replace("USER",fsample(users),1) + choice = choice.replace("USER",fsample(users),1) + if "NOUN" in choice: + choice = choice.replace("NOUN",fsample(nouns),1) + if "LOCATION" in choice: + choice = choice.replace("LOCATION",fsample(locations),1) + if debug: + print "Responded with", choice + return(choice) diff --git a/bot/C_srtd.py b/bot/C_srtd.py index 761c0628d69..b49b8f8c1c0 100644 --- a/bot/C_srtd.py +++ b/bot/C_srtd.py @@ -1,35 +1,35 @@ -import random -def srtd(data,debug,sender): - try: - arg1,arg2 = data.split("d") - except ValueError, err: - if str(err) == "need more than 1 value to unpack": - return("Too small amount of arguments") - else: - return("Too many arguments") - else: - if debug: - print sender+":!rtd "+arg1+"d"+arg2 - die = [] - arg1 = arg1.replace(" ","") - arg2 = arg2.replace(" ","") - try: - i_arg1 = int(arg1) - i_arg2 = int(arg2) - if abs(i_arg1) == 0 or abs(i_arg2) == 0: - raise RuntimeError - except ValueError: - return("You lied! That's not a number!") - except RuntimeError: - return("Too many zeroes!") - else: - if abs(i_arg1) > 500: - return("Too many rolls, I can only do five hundred at max.") - else: - for i in xrange(0,abs(i_arg1)): - if i_arg2 < 0: - dice = random.randint(i_arg2,0) - else: - dice = random.randint(1,i_arg2) - die.append(dice) - return(str(reduce(lambda x,y: x+y, die))) +import random +def srtd(data,debug,sender): + try: + arg1,arg2 = data.split("d") + except ValueError, err: + if str(err) == "need more than 1 value to unpack": + return("Too small amount of arguments") + else: + return("Too many arguments") + else: + if debug: + print sender+":!rtd "+arg1+"d"+arg2 + die = [] + arg1 = arg1.replace(" ","") + arg2 = arg2.replace(" ","") + try: + i_arg1 = int(arg1) + i_arg2 = int(arg2) + if abs(i_arg1) == 0 or abs(i_arg2) == 0: + raise RuntimeError + except ValueError: + return("You lied! That's not a number!") + except RuntimeError: + return("Too many zeroes!") + else: + if abs(i_arg1) > 500: + return("Too many rolls, I can only do five hundred at max.") + else: + for i in xrange(0,abs(i_arg1)): + if i_arg2 < 0: + dice = random.randint(i_arg2,0) + else: + dice = random.randint(1,i_arg2) + die.append(dice) + return(str(reduce(lambda x,y: x+y, die))) diff --git a/bot/D_help.py b/bot/D_help.py index ee4e2c07cfe..d75ed94f5f5 100644 --- a/bot/D_help.py +++ b/bot/D_help.py @@ -1,60 +1,60 @@ -#As new commands are added, update this. -# Last updated: 8.3.2011 - -# Updated 12.3.2011: -# - Added the missing help data for Version -# - Imported CORE_DATA to get the name. -# - Tidied some commands up a bit. -# - Replaced all "Bot"s with the Skibot's current name. - -from CORE_DATA import Name -everything = {"8ball":"[8ball ] Responds to the argument", - "allcaps":"[allcaps ] Takes an uppercase string and returns a capitalized version", - "bmaths":"[bmaths ] Takes a math equation (Like 5+5) and returns a binary result", - "coin":"[coin] Flips a coin", - "dance":"[dance] Makes %s do a little dance" %(Name), - "delquote":"(OP ONLY) [delquote ] Removes a quote with the filename equal to the argument", - "disable":"(OP ONLY) [disable] Disables all output from %s" %(Name), - "disable dance":"(HALFOP / OP ONLY) [disable dance] or [dd] Toggles dancing", - "disable fml":"(HALFOP / OP ONLY) [disable fml] Disables FML", - "eightball":"[eightball ] Responds to the argument", - "enable":"(OP ONLY) [enable] After being disabled, enable will turn output back on", - "enable fml":"{HALFOP / OP ONLY} [enable fml] After fml has been disabled, enable fml will make it available again", - "fml":"[fml] Returns a random Fuck My Life bit", - "give":"[give ] Gives the Pneumatic Disposal Unit the argument", - "help":"[help []] Returns the list of commands or a detailed description of a command if specified", - "hmaths":"[hmaths ] Takes a math equation (Like 5+5) and returns a hex result", - "makequote":"[makequote ] Creates a quote with arg being the quote itself", - "maths":"[maths ] Takes a math equation (Like 5+5) and returns a default result", - "note":"[note []] Opens a note if only arg1 is specified, Creates a note with the name of arg1 and contents of arg2 if arg2 is specified, if you prefix the note name with [CP], it creates a public note only to that channel. Which can be accessed by !note _", - "notes":"[notes] Displays all your saved notes on %s" %(Name), - "otherball":"[otherball] If Floorbot is on the same channel, %s will ask him a random question when this command is passed" %(Name), - "purgemessages":"[purgemessages] Used to delete all your Tell messages (%s,Tell )" %(Name), - "quote":"[quote []] Picks a random quote, if the author is specified, a random quote by that author", - "redmine":"[redmine] If you have a note called redmine, with a valid whoopshop redmine address, this displays all the bugs labeled as 'New' on that page. It also displays the todo note if it's found.", - "replace":"[replace] Fixes the Pneumatic Smasher if it's been broken", - "rot13":"[rot13 ] Encrypts the arg by using the rot13 method", - "rtd":"[rtd [d]] Rolls a six-sided dice if no arguments are specified, otherwise arg1 is the amount of rolls and arg2 is the amount of sides the dice have", - "sarcasticball":"[sarcasticball ] Responds to the argument sarcastically", - "sball":"[sball ] Responds to the argument sarcastically", - "srtd":"[srtd d] Rolls amount of sided die without showing the dice values separately", - "stop":"(RESTRICTED TO OP AND CREATOR) [stop] Stops %s, plain and simple" %(Name), - "suggest":"[suggest ] Saves a suggestion given to %s, to be later viewed by the creator" %(Name), - "take":"[take ] Takes an item specified in the argument from the Pneumatic Smasher", - "tban":"(OP ONLY) [tban ] When %s is an operator, You can ban an user for specified amount of seconds" %(Name), - "thm":"(RESTRICTED TO OP AND CREATOR) [thm] Normally in 8ball and sarcasticball, Users are not shown, instead replaced by things like demons or plasma researchers, toggling this changes that behaviour.", - "tm":"(OP AND CREATOR ONLY) [tm] Toggles marakov", - "togglequotemakers":"(OP ONLY) [togglequotemakers or tqm] Normally with the quote command, makers are not shown, this toggles that behaviour.", - "tqm":"(OP ONLY) [tqm or togglequotemakers] Normally with the quote command, makers are not shown, this toggles that behaviour.", - "toggleofflinemessages":"(OP ONLY) [toggleofflinemessages or tom] Allows an operator to toggle leaving Tell messages (%s, Tell ] Whenever the user says something in allcaps, it's capitalized.", - "uptime":"[uptime] Displays how long %s has been alive on the channel."%(Name), - "use":"[use] Uses the Pneumatic Smasher.", - "youtube":"[youtube ] Shows the title of a video by checking the URL provided.", - "version":"[version] Shows the current version of %s." %(Name), - "weather":"[weather ] Displays the current weather of the provided location.", - "life":"I cannot help you with that, sorry."} - +#As new commands are added, update this. +# Last updated: 8.3.2011 + +# Updated 12.3.2011: +# - Added the missing help data for Version +# - Imported CORE_DATA to get the name. +# - Tidied some commands up a bit. +# - Replaced all "Bot"s with the Skibot's current name. + +from CORE_DATA import Name +everything = {"8ball":"[8ball ] Responds to the argument", + "allcaps":"[allcaps ] Takes an uppercase string and returns a capitalized version", + "bmaths":"[bmaths ] Takes a math equation (Like 5+5) and returns a binary result", + "coin":"[coin] Flips a coin", + "dance":"[dance] Makes %s do a little dance" %(Name), + "delquote":"(OP ONLY) [delquote ] Removes a quote with the filename equal to the argument", + "disable":"(OP ONLY) [disable] Disables all output from %s" %(Name), + "disable dance":"(HALFOP / OP ONLY) [disable dance] or [dd] Toggles dancing", + "disable fml":"(HALFOP / OP ONLY) [disable fml] Disables FML", + "eightball":"[eightball ] Responds to the argument", + "enable":"(OP ONLY) [enable] After being disabled, enable will turn output back on", + "enable fml":"{HALFOP / OP ONLY} [enable fml] After fml has been disabled, enable fml will make it available again", + "fml":"[fml] Returns a random Fuck My Life bit", + "give":"[give ] Gives the Pneumatic Disposal Unit the argument", + "help":"[help []] Returns the list of commands or a detailed description of a command if specified", + "hmaths":"[hmaths ] Takes a math equation (Like 5+5) and returns a hex result", + "makequote":"[makequote ] Creates a quote with arg being the quote itself", + "maths":"[maths ] Takes a math equation (Like 5+5) and returns a default result", + "note":"[note []] Opens a note if only arg1 is specified, Creates a note with the name of arg1 and contents of arg2 if arg2 is specified, if you prefix the note name with [CP], it creates a public note only to that channel. Which can be accessed by !note _", + "notes":"[notes] Displays all your saved notes on %s" %(Name), + "otherball":"[otherball] If Floorbot is on the same channel, %s will ask him a random question when this command is passed" %(Name), + "purgemessages":"[purgemessages] Used to delete all your Tell messages (%s,Tell )" %(Name), + "quote":"[quote []] Picks a random quote, if the author is specified, a random quote by that author", + "redmine":"[redmine] If you have a note called redmine, with a valid whoopshop redmine address, this displays all the bugs labeled as 'New' on that page. It also displays the todo note if it's found.", + "replace":"[replace] Fixes the Pneumatic Smasher if it's been broken", + "rot13":"[rot13 ] Encrypts the arg by using the rot13 method", + "rtd":"[rtd [d]] Rolls a six-sided dice if no arguments are specified, otherwise arg1 is the amount of rolls and arg2 is the amount of sides the dice have", + "sarcasticball":"[sarcasticball ] Responds to the argument sarcastically", + "sball":"[sball ] Responds to the argument sarcastically", + "srtd":"[srtd d] Rolls amount of sided die without showing the dice values separately", + "stop":"(RESTRICTED TO OP AND CREATOR) [stop] Stops %s, plain and simple" %(Name), + "suggest":"[suggest ] Saves a suggestion given to %s, to be later viewed by the creator" %(Name), + "take":"[take ] Takes an item specified in the argument from the Pneumatic Smasher", + "tban":"(OP ONLY) [tban ] When %s is an operator, You can ban an user for specified amount of seconds" %(Name), + "thm":"(RESTRICTED TO OP AND CREATOR) [thm] Normally in 8ball and sarcasticball, Users are not shown, instead replaced by things like demons or plasma researchers, toggling this changes that behaviour.", + "tm":"(OP AND CREATOR ONLY) [tm] Toggles marakov", + "togglequotemakers":"(OP ONLY) [togglequotemakers or tqm] Normally with the quote command, makers are not shown, this toggles that behaviour.", + "tqm":"(OP ONLY) [tqm or togglequotemakers] Normally with the quote command, makers are not shown, this toggles that behaviour.", + "toggleofflinemessages":"(OP ONLY) [toggleofflinemessages or tom] Allows an operator to toggle leaving Tell messages (%s, Tell ] Whenever the user says something in allcaps, it's capitalized.", + "uptime":"[uptime] Displays how long %s has been alive on the channel."%(Name), + "use":"[use] Uses the Pneumatic Smasher.", + "youtube":"[youtube ] Shows the title of a video by checking the URL provided.", + "version":"[version] Shows the current version of %s." %(Name), + "weather":"[weather ] Displays the current weather of the provided location.", + "life":"I cannot help you with that, sorry."} + diff --git a/bot/FMLformatter.py b/bot/FMLformatter.py index 153af61a41a..78649fe0537 100644 --- a/bot/FMLformatter.py +++ b/bot/FMLformatter.py @@ -1,55 +1,55 @@ -from htmltagremove import htr -def formatter(data): - newdata = [] - data = htr(data) - bad = ["Your nick : Categories : ","\r","Advanced search - last", - "FMyLife","Get the guts to spill the beans","FML: Your random funny stories", - "Woman","Man","Choose","Health","Intimacy","Miscellaneous","Man or woman? ", - "Money","Kids","Work","Love","Email notification?", - "Moderate the FMLs","Submit your FML story", - "- If your story isn't published on the website, don't feel offended, and thank you nevertheless!", - "Pick a country","See all","Your account","Team's blog", - "Meet the FMLHello readers! Did you meet someone new this...The whole blog", - "Amazon","Borders","IndieBound","Personalized book","Terms of use", - "FML t-shirts -","Love - Money - Kids - Work - Health - Intimacy - Miscellaneous - Members", - "Follow the FML Follow the FML blog Follow the FML comments ", - "_qoptions={", - "};","})();","Categories","Sign up - Password? ", " Net Avenir : gestion publicitaire", - "FMyLife, the book","Available NOW on:","Barnes & Noble"] - - for checkable in data: - if checkable in bad: - pass - elif "_gaq.push" in checkable: - pass - elif "ga.src" in checkable: - pass - elif "var _gaq" in checkable: - pass - elif "var s =" in checkable: - pass - elif "var ga" in checkable: - pass - elif "function()" in checkable: - pass - elif "siteanalytics" in checkable: - pass - elif "qacct:" in checkable: - pass - elif "\r" in checkable: - pass - elif "ic_" in checkable: - pass - elif "Please note that spam and nonsensical stories" in checkable: - pass - elif "Refresh this page" in checkable: - pass - elif "You...The whole blo" in checkable: - pass - elif "Net Avenir : gestion publicitair" in checkable: - pass - else: - if "Net Avenir : gestion publicitaireClose the advertisement" in checkable: - checkable = checkable.replace("Net Avenir : gestion publicitaireClose the advertisement","") - newdata.append(checkable) - return newdata +from htmltagremove import htr +def formatter(data): + newdata = [] + data = htr(data) + bad = ["Your nick : Categories : ","\r","Advanced search - last", + "FMyLife","Get the guts to spill the beans","FML: Your random funny stories", + "Woman","Man","Choose","Health","Intimacy","Miscellaneous","Man or woman? ", + "Money","Kids","Work","Love","Email notification?", + "Moderate the FMLs","Submit your FML story", + "- If your story isn't published on the website, don't feel offended, and thank you nevertheless!", + "Pick a country","See all","Your account","Team's blog", + "Meet the FMLHello readers! Did you meet someone new this...The whole blog", + "Amazon","Borders","IndieBound","Personalized book","Terms of use", + "FML t-shirts -","Love - Money - Kids - Work - Health - Intimacy - Miscellaneous - Members", + "Follow the FML Follow the FML blog Follow the FML comments ", + "_qoptions={", + "};","})();","Categories","Sign up - Password? ", " Net Avenir : gestion publicitaire", + "FMyLife, the book","Available NOW on:","Barnes & Noble"] + + for checkable in data: + if checkable in bad: + pass + elif "_gaq.push" in checkable: + pass + elif "ga.src" in checkable: + pass + elif "var _gaq" in checkable: + pass + elif "var s =" in checkable: + pass + elif "var ga" in checkable: + pass + elif "function()" in checkable: + pass + elif "siteanalytics" in checkable: + pass + elif "qacct:" in checkable: + pass + elif "\r" in checkable: + pass + elif "ic_" in checkable: + pass + elif "Please note that spam and nonsensical stories" in checkable: + pass + elif "Refresh this page" in checkable: + pass + elif "You...The whole blo" in checkable: + pass + elif "Net Avenir : gestion publicitair" in checkable: + pass + else: + if "Net Avenir : gestion publicitaireClose the advertisement" in checkable: + checkable = checkable.replace("Net Avenir : gestion publicitaireClose the advertisement","") + newdata.append(checkable) + return newdata diff --git a/bot/Marakov/Marakov.Cache b/bot/Marakov/Marakov.Cache index 500f4384edd..9818ecc7ab0 100644 --- a/bot/Marakov/Marakov.Cache +++ b/bot/Marakov/Marakov.Cache @@ -1,2450 +1,2450 @@ -(dp0 -S'all' -p1 -(lp2 -S':p' -p3 -aS'the' -p4 -asS'code' -p5 -(lp6 -S'in' -p7 -asS'stores' -p8 -(lp9 -S'that' -p10 -asS'just' -p11 -(lp12 -S'like' -p13 -aS'marakov' -p14 -aS'felt' -p15 -aS'gives' -p16 -aS'a' -p17 -aS'add' -p18 -aS'what' -p19 -asS'being' -p20 -(lp21 -S'goon' -p22 -aS'a' -p23 -asS'text' -p24 -(lp25 -S'string' -p26 -asS'dependant' -p27 -(lp28 -S'on' -p29 -asS'speedup' -p30 -(lp31 -S'at' -p32 -asS'felt' -p33 -(lp34 -S'like' -p35 -asS'installed' -p36 -(lp37 -S'tho' -p38 -asS'disabled' -p39 -(lp40 -S'it' -p41 -asS'timing' -p42 -(lp43 -S'when' -p44 -asS'psyco' -p45 -(lp46 -S'installed' -p47 -aS'is' -p48 -asS'stops' -p49 -(lp50 -S'timing' -p51 -asS'file' -p52 -(lp53 -S'too' -p54 -aS'that' -p55 -aS'where' -p56 -asS'go' -p57 -(lp58 -S'fuck' -p59 -aS'into' -p60 -aS'test' -p61 -asS'hell' -p62 -(lp63 -S'recreate' -p64 -asS'configurable' -p65 -(lp66 -S'greeting' -p67 -asS'bs12' -p68 -(lp69 -S'message' -p70 -asS'its' -p71 -(lp72 -S'fine' -p73 -aS'just' -p74 -aS'calculated' -p75 -aS'not' -p76 -aS'really' -p77 -aS'ridiculously' -p78 -aS'now' -p79 -aS'a' -p80 -aS'missing' -p81 -aS'for' -p82 -aS'false' -p83 -aS'on' -p84 -asS'before' -p85 -(lp86 -S'that' -p87 -asS'rp-heavy' -p88 -(lp89 -S'server' -p90 -asS'announcement' -p91 -(lp92 -S'like' -p93 -asS'now' -p94 -(lp95 -S'it' -p96 -aS'running' -p97 -aS'makie' -p98 -aS'i' -p99 -asS'nudge' -p100 -(lp101 -S'python' -p102 -aS'is' -p103 -asS'sourced' -p104 -(lp105 -S'under' -p106 -asS'title' -p107 -(lp108 -S'of' -p109 -asS'situations' -p110 -(lp111 -S'where' -p112 -asS'fine-tune' -p113 -(lp114 -S'it' -p115 -asS'enough' -p116 -(lp117 -S'to' -p118 -asS'send' -p119 -(lp120 -S'it' -p121 -aS'one' -p122 -asS'should' -p123 -(lp124 -S'be' -p125 -aS'learn' -p126 -aS'we' -p127 -asS'values' -p128 -(lp129 -S'dont' -p130 -asS'to' -p131 -(lp132 -S'edit' -p133 -aS'back' -p134 -aS'know' -p135 -aS'care' -p136 -aS'be' -p137 -aS'configure' -p138 -aS'the' -p139 -aS'null' -p140 -aS'phone' -p141 -aS'welcome' -p142 -aS'reverse' -p143 -aS'send' -p144 -aS'reg' -p145 -aS'point' -p146 -aS'automatically' -p147 -aS'work' -p148 -aS'waste' -p149 -aS'marshmallow' -p150 -aS'queries' -p151 -aS'+o' -p152 -aS'disable' -p153 -asS'jit' -p154 -(lp155 -S'compiler' -p156 -asS'going' -p157 -(lp158 -S'to' -p159 -asS'helps' -p160 -(lp161 -S'me' -p162 -asS'messes' -p163 -(lp164 -S'it' -p165 -asS'indeed' -p166 -(lp167 -S'it' -p168 -asS'tg' -p169 -(lp170 -S'and' -p171 -asS'has' -p172 -(lp173 -S'been' -p174 -aS'my' -p175 -asS'into' -p176 -(lp177 -S'#tgstation13' -p178 -asS'ridiculously' -p179 -(lp180 -S'simple' -p181 -asS'annoy' -p182 -(lp183 -S'downstream' -p184 -asS'them' -p185 -(lp186 -S'out' -p187 -asS'someone' -p188 -(lp189 -S'adminhelps' -p190 -asS'sense' -p191 -(lp192 -S'i' -p193 -asS'string' -p194 -(lp195 -S'called' -p196 -asS'get' -p197 -(lp198 -S'ready' -p199 -asS'python' -p200 -(lp201 -S'script' -p202 -aS'so' -p203 -aS'code' -p204 -aS'scripts' -p205 -aS'and' -p206 -aS'but' -p207 -aS'now' -p208 -aS'released' -p209 -aS'is' -p210 -aS'enough' -p211 -asS'goon' -p212 -(lp213 -S'tg' -p214 -asS'showing' -p215 -(lp216 -S'up' -p217 -asS'20ish' -p218 -(lp219 -S'line' -p220 -asS'gonna' -p221 -(lp222 -S'go' -p223 -asS'made' -p224 -(lp225 -S'doctors' -p226 -asS'every' -p227 -(lp228 -S'loop' -p229 -aS'time' -p230 -asS'know' -p231 -(lp232 -S'the' -p233 -aS'why' -p234 -aS'that' -p235 -asS'not' -p236 -(lp237 -S'just' -p238 -aS'necessary' -p239 -aS'to' -p240 -aS'so' -p241 -aS'need' -p242 -aS'sure' -p243 -aS'relaying' -p244 -aS'very' -p245 -aS'even' -p246 -asS'2' -p247 -(lp248 -S'loop' -p249 -asS'password' -p250 -(lp251 -S'var' -p252 -asS'day' -p253 -(lp254 -S'so' -p255 -asS'swapping' -p256 -(lp257 -S'to' -p258 -asS'easily' -p259 -(lp260 -S'editable' -p261 -asS'necessary' -p262 -(lp263 -S'at' -p264 -asS'like' -p265 -(lp266 -S'being' -p267 -aS'linking' -p268 -aS'to' -p269 -aS'how' -p270 -aS'it' -p271 -aS'skbzzzzzibi' -p272 -asS'course' -p273 -(lp274 -S'that' -p275 -asS'edit' -p276 -(lp277 -S'baystation' -p278 -asS'fully' -p279 -(lp280 -S'open' -p281 -asS'greeting' -p282 -(lp283 -S'message' -p284 -asS'server' -p285 -(lp286 -S'basically' -p287 -aS'' -p288 -asS'default' -p289 -(lp290 -S'config' -p291 -asS'bad' -p292 -(lp293 -S'company' -p294 -asS'channel' -p295 -(lp296 -S'or' -p297 -asS'always' -p298 -(lp299 -S'makes' -p300 -asS'went' -p301 -(lp302 -S'past' -p303 -asS'quarxink' -p304 -(lp305 -S'its' -p306 -asS'automatic' -p307 -(lp308 -S'announcement' -p309 -asS'once' -p310 -(lp311 -S'per' -p312 -asS'wrote' -p313 -(lp314 -S'most' -p315 -asS'pain' -p316 -(lp317 -S'on' -p318 -asS'system' -p319 -(lp320 -S'calls' -p321 -asS'right' -p322 -(lp323 -S'brb' -p324 -asS'decides' -p325 -(lp326 -S'not' -p327 -asS'people' -p328 -(lp329 -S'say' -p330 -aS'i' -p331 -aS'he' -p332 -asS'goddamn' -p333 -(lp334 -S'python' -p335 -asS'back' -p336 -(lp337 -S'it' -p338 -asS'used' -p339 -(lp340 -S'to' -p341 -aS'for' -p342 -asS'past' -p343 -(lp344 -S'too' -p345 -asS'cost' -p346 -(lp347 -S'of' -p348 -asS'learn' -p349 -(lp350 -S'python' -p351 -asS'are' -p352 -(lp353 -S'lawyers' -p354 -aS'actually' -p355 -aS'configurable' -p356 -aS'we' -p357 -asS'celestialike' -p358 -(lp359 -S'of' -p360 -asS'lawyers' -p361 -(lp362 -S'for' -p363 -asS'time' -p364 -(lp365 -S'he' -p366 -asS'out' -p367 -(lp368 -S'switch' -p369 -aS'slowdowns' -p370 -aS'that' -p371 -aS'why' -p372 -aS'nudge' -p373 -asS'even' -p374 -(lp375 -S'an' -p376 -asS'what' -p377 -(lp378 -S'the' -p379 -aS'is' -p380 -aS'these' -p381 -aS'was' -p382 -aS'license' -p383 -aS'about' -p384 -aS'i' -p385 -asS'said' -p386 -(lp387 -S'in' -p388 -asS'sayt' -p389 -(lp390 -S'hat' -p391 -asS'for' -p392 -(lp393 -S'that' -p394 -aS'every' -p395 -aS'the' -p396 -aS'quarx' -p397 -aS'cc_nanotrasen' -p398 -aS'homoerotic' -p399 -aS'situations' -p400 -aS'good' -p401 -asS'#tgstation13' -p402 -(lp403 -S'and' -p404 -asS'per' -p405 -(lp406 -S'name' -p407 -asS'whole' -p408 -(lp409 -S'config' -p410 -asS'state' -p411 -(lp412 -S'the' -p413 -asS'does' -p414 -(lp415 -S'the' -p416 -aS'it' -p417 -asS'goes' -p418 -(lp419 -S'on' -p420 -asS'readme' -p421 -(lp422 -S'too' -p423 -asS'new' -p424 -(lp425 -S'bot' -p426 -aS'person' -p427 -asS'learned' -p428 -(lp429 -S'python' -p430 -asS'irc' -p431 -(lp432 -S'bot' -p433 -asS'reg' -p434 -(lp435 -S'it' -p436 -asS'blow' -p437 -(lp438 -S'borgs' -p439 -asS'shut' -p440 -(lp441 -S'down' -p442 -asS'after' -p443 -(lp444 -S'an' -p445 -aS'a' -p446 -asS'ill' -p447 -(lp448 -S'switch' -p449 -asS'says' -p450 -(lp451 -S'someones' -p452 -asS'queries' -p453 -(lp454 -S'again' -p455 -asS'technocracy' -p456 -(lp457 -S'and' -p458 -asS'we' -p459 -(lp460 -S'go' -p461 -aS'do' -p462 -aS'totally' -p463 -aS'going' -p464 -aS'expect' -p465 -aS'just' -p466 -aS'dont' -p467 -aS'have' -p468 -asS'put' -p469 -(lp470 -S'all' -p471 -asS'from' -p472 -(lp473 -S'the' -p474 -asS'data11lower' -p475 -(lp476 -S'==' -p477 -asS'configuration' -p478 -(lp479 -S'for' -p480 -asS'wait' -p481 -(lp482 -S'what' -p483 -asS'on' -p484 -(lp485 -S'my' -p486 -aS'the' -p487 -aS'a' -p488 -aS'connect' -p489 -aS'svn' -p490 -aS'one' -p491 -aS'/' -p492 -asS'about' -p493 -(lp494 -S'system' -p495 -asS'ok' -p496 -(lp497 -S'thats' -p498 -asS'reverse' -p499 -(lp500 -S'engineer' -p501 -asS'license' -p502 -(lp503 -S'is' -p504 -asS'oh' -p505 -(lp506 -S'okay' -p507 -aS'ok' -p508 -aS'i' -p509 -aS'wait' -p510 -asS'starts' -p511 -(lp512 -S'timing' -p513 -asS'could' -p514 -(lp515 -S'learn' -p516 -asS'larger' -p517 -(lp518 -S'ram' -p519 -asS'bot' -p520 -(lp521 -S'is' -p522 -aS'have' -p523 -aS'shut' -p524 -aS'uses' -p525 -asS'running' -p526 -(lp527 -S'it' -p528 -aS'the' -p529 -aS'on' -p530 -asS'times' -p531 -(lp532 -S'on' -p533 -aS'reported' -p534 -aS'went' -p535 -asS'where' -p536 -(lp537 -S'he' -p538 -asS'heck' -p539 -(lp540 -S':d' -p541 -asS'idk' -p542 -(lp543 -S'magic' -p544 -asS'receives' -p545 -(lp546 -S'a' -p547 -asS'bots' -p548 -(lp549 -S'showing' -p550 -asS'slightly' -p551 -(lp552 -S'larger' -p553 -asS'or' -p554 -(lp555 -S'know' -p556 -aS'what' -p557 -aS'work' -p558 -aS'should' -p559 -aS'data11lower' -p560 -asS'automatically' -p561 -(lp562 -S'state' -p563 -asS'thats' -p564 -(lp565 -S'not' -p566 -aS'the' -p567 -aS'kind' -p568 -asS'ugh' -p569 -(lp570 -S'fuck' -p571 -asS'major' -p572 -(lp573 -S'ss13' -p574 -aS'three' -p575 -asS'py' -p576 -(lp577 -S'file' -p578 -asS'soss' -p579 -(lp580 -S'server' -p581 -asS'dont' -p582 -(lp583 -S'need' -p584 -aS'have' -p585 -aS'send' -p586 -aS'want' -p587 -aS'speak' -p588 -asS'hostmask' -p589 -(lp590 -S'combination' -p591 -asS'point' -p592 -(lp593 -S'out' -p594 -aS'has' -p595 -asS'simple' -p596 -(lp597 -S'to' -p598 -asS'miura' -p599 -(lp600 -S'doesnt' -p601 -asS'variables' -p602 -(lp603 -S'in' -p604 -asS'recreate' -p605 -(lp606 -S'it' -p607 -asS'welcome' -p608 -(lp609 -S'to' -p610 -asS'linking' -p611 -(lp612 -S'them' -p613 -asS'down' -p614 -(lp615 -S'when' -p616 -asS'why' -p617 -(lp618 -S'its' -p619 -aS'it' -p620 -aS'is' -p621 -asS'doesnt' -p622 -(lp623 -S'play' -p624 -aS'call' -p625 -asS'marakov' -p626 -(lp627 -S'loops' -p628 -aS'helps' -p629 -asS'laugh' -p630 -(lp631 -S'when' -p632 -asS'pony' -p633 -(lp634 -S'asshole' -p635 -asS'message' -p636 -(lp637 -S'has' -p638 -aS'should' -p639 -asS'open' -p640 -(lp641 -S'sourced' -p642 -aS'source' -p643 -asS'brb' -p644 -(lp645 -S'swapping' -p646 -asS'speak' -p647 -(lp648 -S'python' -p649 -asS'pastebin' -p650 -(lp651 -S'the' -p652 -asS'line' -p653 -(lp654 -S'core' -p655 -aS'on' -p656 -asS'three' -p657 -(lp658 -S'being' -p659 -asS'yay' -p660 -(lp661 -S'it' -p662 -asS'meatbag' -p663 -(lp664 -S'when' -p665 -asS'would' -p666 -(lp667 -S'be' -p668 -aS'expect' -p669 -asS'script' -p670 -(lp671 -S'that' -p672 -asS'illegal' -p673 -(lp674 -S'ban' -p675 -asS'there' -p676 -(lp677 -S'are' -p678 -aS'we' -p679 -asS'add' -p680 -(lp681 -S'that' -p682 -aS'a' -p683 -asS'been' -p684 -(lp685 -S'processed' -p686 -asS'name' -p687 -(lp688 -S'/' -p689 -aS'when' -p690 -asS'ai' -p691 -(lp692 -S'malf' -p693 -asS'marshmallow' -p694 -(lp695 -S'pony' -p696 -asS'of' -p697 -(lp698 -S'the' -p699 -aS'ss13' -p700 -aS'tgstation13' -p701 -aS'a' -p702 -aS'course' -p703 -aS'it' -p704 -aS'soss' -p705 -aS'annoying' -p706 -aS'any' -p707 -aS'me' -p708 -aS'cap' -p709 -aS'technocracy' -p710 -asS'call' -p711 -(lp712 -S'me' -p713 -asS'too' -p714 -(lp715 -S':' -p716 -aS'fast' -p717 -asS'basic' -p718 -(lp719 -S'configuration' -p720 -asS'var' -p721 -(lp722 -S'and' -p723 -asS'calc' -p724 -(lp725 -S'times' -p726 -asS'was' -p727 -(lp728 -S'going' -p729 -aS'it' -p730 -aS'that' -p731 -aS'not' -p732 -aS'intended' -p733 -asS'tell' -p734 -(lp735 -S'people' -p736 -asS'500' -p737 -(lp738 -S'chance' -p739 -asS'gives' -p740 -(lp741 -S'a' -p742 -asS'sort' -p743 -(lp744 -S'of' -p745 -asS'svn' -p746 -(lp747 -S'size' -p748 -asS'only' -p749 -(lp750 -S'does' -p751 -aS'2' -p752 -asS'10-30%' -p753 -(lp754 -S'speedup' -p755 -asS'knows' -p756 -(lp757 -S'about' -p758 -asS'webpage' -p759 -(lp760 -S'title' -p761 -asS'that' -p762 -(lp763 -S'makes' -p764 -aS'would' -p765 -aS'the' -p766 -aS'needs' -p767 -aS'to' -p768 -aS'at' -p769 -aS'for' -p770 -aS'was' -p771 -aS'data' -p772 -asS'company' -p773 -(lp774 -S'2' -p775 -asS'under' -p776 -(lp777 -S'cc-by-sa' -p778 -asS'editable' -p779 -(lp780 -S'config' -p781 -asS'but' -p782 -(lp783 -S'of' -p784 -asS'idea' -p785 -(lp786 -S'what' -p787 -asS'released' -p788 -(lp789 -S'under' -p790 -asS'part' -p791 -(lp792 -S'before' -p793 -asS'link' -p794 -(lp795 -S'said' -p796 -aS'to' -p797 -asS'basically' -p798 -(lp799 -S'it' -p800 -asS'doctors' -p801 -(lp802 -S'useless' -p803 -asS'==' -p804 -(lp805 -S'channel' -p806 -aS'channel1::' -p807 -asS'be' -p808 -(lp809 -S'an' -p810 -aS'called' -p811 -aS'a' -p812 -aS'in' -p813 -aS'running' -p814 -aS'used' -p815 -asS'editing' -p816 -(lp817 -S'goddamn' -p818 -asS'with' -p819 -(lp820 -S'the' -p821 -aS'easily' -p822 -aS'adminhelps' -p823 -aS'my' -p824 -aS'a' -p825 -asS'those' -p826 -(lp827 -S'are' -p828 -asS'he' -p829 -(lp830 -S'put' -p831 -aS'disabled' -p832 -aS'is' -p833 -aS'keeps' -p834 -aS'knows' -p835 -aS'stores' -p836 -aS'notices' -p837 -aS'only' -p838 -aS'doesnt' -p839 -aS'receives' -p840 -aS'says' -p841 -aS'messes' -p842 -asS'me' -p843 -(lp844 -S'figure' -p845 -aS'laugh' -p846 -aS'to' -p847 -aS'meatbag' -p848 -asS'also' -p849 -(lp850 -S'i' -p851 -aS'we' -p852 -aS'now' -p853 -asS'kind' -p854 -(lp855 -S'of' -p856 -asS'main' -p857 -(lp858 -S'bot' -p859 -asS'/' -p860 -(lp861 -S'hostmask' -p862 -aS'off' -p863 -asS'full' -p864 -(lp865 -S'of' -p866 -asS'these' -p867 -(lp868 -S'are' -p869 -asS'makie' -p870 -(lp871 -S'it' -p872 -asS'sleepers' -p873 -(lp874 -S'made' -p875 -asS'up' -p876 -(lp877 -S'elsewhere' -p878 -aS'me' -p879 -aS'a' -p880 -asS'will' -p881 -(lp882 -S'annoy' -p883 -asS'computer' -p884 -(lp885 -S'explodd' -p886 -asS'limit' -p887 -(lp888 -S'on' -p889 -asS'can' -p890 -(lp891 -S'fine-tune' -p892 -aS'add' -p893 -aS'i' -p894 -aS'we' -p895 -asS'how' -p896 -(lp897 -S'its' -p898 -aS'he' -p899 -asS'were' -p900 -(lp901 -S'the' -p902 -asS'malf' -p903 -(lp904 -S'blow' -p905 -asS'baystation' -p906 -(lp907 -S'12' -p908 -asS'other' -p909 -(lp910 -S'loop' -p911 -asS'my' -p912 -(lp913 -S'end' -p914 -aS'computer' -p915 -aS'code' -p916 -asS'called' -p917 -(lp918 -S'as' -p919 -aS'when' -p920 -asS'loop' -p921 -(lp922 -S'times' -p923 -asS'expect' -p924 -(lp925 -S'it' -p926 -asS'and' -p927 -(lp928 -S'bs12' -p929 -aS'stops' -p930 -aS'i' -p931 -aS'do' -p932 -aS'to' -p933 -aS'make' -p934 -aS'he' -p935 -aS'preferably' -p936 -aS'thats' -p937 -aS'sayt' -p938 -aS'fascism' -p939 -asS'dedicated' -p940 -(lp941 -S'solely' -p942 -asS'changed' -p943 -(lp944 -S'it' -p945 -asS'sees' -p946 -(lp947 -S'a' -p948 -asS'relaying' -p949 -(lp950 -S'adminhelps' -p951 -asS'figure' -p952 -(lp953 -S'out' -p954 -asS'do' -p955 -(lp956 -S'not' -p957 -aS'it' -p958 -aS'seem' -p959 -asS'ran' -p960 -(lp961 -S'the' -p962 -asS'ah' -p963 -(lp964 -S'running' -p965 -aS'ok' -p966 -asS'is' -p967 -(lp968 -S'baystation' -p969 -aS'a' -p970 -aS'that' -p971 -aS'it' -p972 -aS'coded' -p973 -aS'python' -p974 -aS'open' -p975 -aS'dependant' -p976 -aS'so' -p977 -aS'apparently' -p978 -asS'ram' -p979 -(lp980 -S'footprint' -p981 -asS'am' -p982 -(lp983 -S'the' -p984 -asS'it' -p985 -(lp986 -S'up' -p987 -aS'starts' -p988 -aS'receives' -p989 -aS'just' -p990 -aS'expires' -p991 -aS'works' -p992 -aS'always' -p993 -aS'decides' -p994 -aS'on' -p995 -aS'here' -p996 -aS'used' -p997 -aS'to' -p998 -aS'those' -p999 -aS'sees' -p1000 -aS'does' -p1001 -aS'myself' -p1002 -aS'a' -p1003 -aS'okay' -p1004 -aS'was' -p1005 -aS'go' -p1006 -aS'if' -p1007 -aS'once' -p1008 -aS'is' -p1009 -aS'for' -p1010 -asS'an' -p1011 -(lp1012 -S'hour' -p1013 -aS'illegal' -p1014 -aS'automatic' -p1015 -aS'error' -p1016 -asS'ready' -p1017 -(lp1018 -S'for' -p1019 -asS'say' -p1020 -(lp1021 -S'sleepers' -p1022 -aS'ai' -p1023 -aS'that*' -p1024 -aS'stop' -p1025 -asS'good' -p1026 -(lp1027 -S':p' -p1028 -asS'im' -p1029 -(lp1030 -S'not' -p1031 -aS'sorry' -p1032 -aS'lazy' -p1033 -asS'at' -p1034 -(lp1035 -S'all' -p1036 -aS'the' -p1037 -aS'no' -p1038 -asS'have' -p1039 -(lp1040 -S'no' -p1041 -aS'psyco' -p1042 -aS'the' -p1043 -aS'a' -p1044 -asS'in' -p1045 -(lp1046 -S'python' -p1047 -aS'a' -p1048 -aS'the' -p1049 -aS'100' -p1050 -asS'need' -p1051 -(lp1052 -S'to' -p1053 -aS'six' -p1054 -asS'politics' -p1055 -(lp1056 -S'of' -p1057 -asS'seem' -p1058 -(lp1059 -S'familiar' -p1060 -asS'work' -p1061 -(lp1062 -S'with' -p1063 -asS'apparently' -p1064 -(lp1065 -S'homophobic' -p1066 -aS'i' -p1067 -asS'any' -p1068 -(lp1069 -S'link' -p1070 -asS'as' -p1071 -(lp1072 -S'well' -p1073 -aS'variables' -p1074 -asS'sci-fi' -p1075 -(lp1076 -S'with' -p1077 -asS'preferably' -p1078 -(lp1079 -S'python' -p1080 -asS'really' -p1081 -(lp1082 -S'simple' -p1083 -aS'now' -p1084 -asS'needs' -p1085 -(lp1086 -S'to' -p1087 -aS'a' -p1088 -asS'null' -p1089 -(lp1090 -S'them' -p1091 -asS'because' -p1092 -(lp1093 -S'we' -p1094 -asS'want' -p1095 -(lp1096 -S'to' -p1097 -asS'no' -p1098 -(lp1099 -S'pain' -p1100 -aS'idea' -p1101 -aS'point' -p1102 -aS'the' -p1103 -asS'solely' -p1104 -(lp1105 -S'to' -p1106 -asS'nah' -p1107 -(lp1108 -S'its' -p1109 -aS'ill' -p1110 -asS'dunno' -p1111 -(lp1112 -S'is' -p1113 -asS'when' -p1114 -(lp1115 -S'it' -p1116 -aS'the' -p1117 -aS'people' -p1118 -aS'i' -p1119 -aS'can' -p1120 -aS'someone' -p1121 -aS'its' -p1122 -asS'same' -p1123 -(lp1124 -S'file' -p1125 -asS'id' -p1126 -(lp1127 -S'like' -p1128 -asS'note' -p1129 -(lp1130 -S'how' -p1131 -asS'figuring' -p1132 -(lp1133 -S'out' -p1134 -asS'bah' -p1135 -(lp1136 -S'apparently' -p1137 -asS'coded' -p1138 -(lp1139 -S'in' -p1140 -asS'take' -p1141 -(lp1142 -S'it' -p1143 -asS'hop' -p1144 -(lp1145 -S'to' -p1146 -asS'familiar' -p1147 -(lp1148 -S'message' -p1149 -asS'test' -p1150 -(lp1151 -S'server' -p1152 -aS'bots' -p1153 -aS'bad' -p1154 -asS'asshole' -p1155 -(lp1156 -g288 -asS'if' -p1157 -(lp1158 -S'it' -p1159 -aS'its' -p1160 -aS'data11lower' -p1161 -aS'he' -p1162 -asS'config' -p1163 -(lp1164 -S'file' -p1165 -aS'values' -p1166 -asS'homophobic' -p1167 -(lp1168 -S'as' -p1169 -asS'dose' -p1170 -(lp1171 -S'of' -p1172 -asS'play' -p1173 -(lp1174 -S'ss13' -p1175 -asS'sure' -p1176 -(lp1177 -S'the' -p1178 -aS'if' -p1179 -asS'okay' -p1180 -(lp1181 -S'desu' -p1182 -aS'cool' -p1183 -aS'cc' -p1184 -asS'intended' -p1185 -(lp1186 -S'to' -p1187 -asS'one' -p1188 -(lp1189 -S'of' -p1190 -aS'line' -p1191 -aS'in' -p1192 -asS'neat' -p1193 -(lp1194 -S'is' -p1195 -asS'adminhelps' -p1196 -(lp1197 -S'from' -p1198 -aS'with' -p1199 -asS'expires' -p1200 -(lp1201 -S'after' -p1202 -asS'chance' -p1203 -(lp1204 -S'every' -p1205 -asS'most' -p1206 -(lp1207 -S'of' -p1208 -asS'fascism' -p1209 -(lp1210 -g288 -asS'disable' -p1211 -(lp1212 -S'it' -p1213 -asS'connected' -p1214 -(lp1215 -S'businessman' -p1216 -asS'never' -p1217 -(lp1218 -S'learned' -p1219 -asS'scripts' -p1220 -(lp1221 -S'will' -p1222 -asS'along' -p1223 -(lp1224 -S'with' -p1225 -asS'waste' -p1226 -(lp1227 -S'space' -p1228 -asS'ss13' -p1229 -(lp1230 -S'servers' -p1231 -asS'cap' -p1232 -(lp1233 -S'troopers' -p1234 -asS'totally' -p1235 -(lp1236 -S'need' -p1237 -asS'six' -p1238 -(lp1239 -S'test' -p1240 -asS'a' -p1241 -(lp1242 -S'businessman' -p1243 -aS'message' -p1244 -aS'jit' -p1245 -aS'10-30%' -p1246 -aS'slightly' -p1247 -aS'day' -p1248 -aS'test' -p1249 -aS'config' -p1250 -aS'20ish' -p1251 -aS'bs12' -p1252 -aS'text' -p1253 -aS'new' -p1254 -aS'password' -p1255 -aS'vhost' -p1256 -aS'configurable' -p1257 -aS'link' -p1258 -aS'limit' -p1259 -aS'file' -p1260 -aS'dose' -p1261 -aS'500' -p1262 -aS'bit' -p1263 -asS'ofc' -p1264 -(lp1265 -S'i' -p1266 -asS'off' -p1267 -(lp1268 -S'of' -p1269 -asS'calls' -p1270 -(lp1271 -S'external' -p1272 -asS'i' -p1273 -(lp1274 -S'need' -p1275 -aS'can' -p1276 -aS'dont' -p1277 -aS'am' -p1278 -aS'guess' -p1279 -aS'see' -p1280 -aS'have' -p1281 -aS'just' -p1282 -aS'dunno' -p1283 -aS'wrote' -p1284 -aS'know' -p1285 -aS'was' -p1286 -aS'changed' -p1287 -aS'take' -p1288 -aS'could' -p1289 -aS'never' -p1290 -aS'should' -p1291 -aS'say' -p1292 -aS'tell' -p1293 -aS'code' -p1294 -aS'would' -p1295 -aS'like' -p1296 -aS'disabled' -p1297 -asS'makes' -p1298 -(lp1299 -S'sense' -p1300 -aS'me' -p1301 -asS'calculated' -p1302 -(lp1303 -S'for' -p1304 -asS'afk' -p1305 -(lp1306 -S'vidya' -p1307 -asS'well' -p1308 -(lp1309 -S'connected' -p1310 -asS'data' -p1311 -(lp1312 -S'in' -p1313 -asS'homoerotic' -p1314 -(lp1315 -S'sci-fi' -p1316 -asS'switch' -p1317 -(lp1318 -S'after' -p1319 -aS'goes' -p1320 -asS'so' -p1321 -(lp1322 -S'i' -p1323 -aS'uh' -p1324 -aS':p' -p1325 -aS'bad' -p1326 -aS'sly' -p1327 -asS'someones' -p1328 -(lp1329 -S'name' -p1330 -asS'keeps' -p1331 -(lp1332 -S'all' -p1333 -asS'very' -p1334 -(lp1335 -S'celestialike' -p1336 -asS'businessman' -p1337 -(lp1338 -S'of' -p1339 -ag288 -asS'the' -p1340 -(lp1341 -S'heck' -p1342 -aS'major' -p1343 -aS'well' -p1344 -aS'politics' -p1345 -aS'rp-heavy' -p1346 -aS'marakov' -p1347 -aS'law' -p1348 -aS'message' -p1349 -aS'cost' -p1350 -aS'new' -p1351 -aS'nudge' -p1352 -aS'bot' -p1353 -aS'python' -p1354 -aS'basic' -p1355 -aS'whole' -p1356 -aS'configuration' -p1357 -aS'irc' -p1358 -aS'readme' -p1359 -aS'default' -p1360 -aS'conspiracy' -p1361 -aS'config' -p1362 -aS'webpage' -p1363 -aS'channel' -p1364 -aS'server' -p1365 -aS'download' -p1366 -aS'main' -p1367 -aS'dmb' -p1368 -aS'part' -p1369 -aS'people' -p1370 -aS'same' -p1371 -aS'hell' -p1372 -aS'other' -p1373 -aS'switch' -p1374 -asS'12' -p1375 -(lp1376 -S'out' -p1377 -aS'anyway' -p1378 -asS'core' -p1379 -(lp1380 -S'py' -p1381 -asS'make' -p1382 -(lp1383 -S'sure' -p1384 -aS'the' -p1385 -asS'turns' -p1386 -(lp1387 -S'out' -p1388 -asS'external' -p1389 -(lp1390 -S'apps' -p1391 +(dp0 +S'all' +p1 +(lp2 +S':p' +p3 +aS'the' +p4 +asS'code' +p5 +(lp6 +S'in' +p7 +asS'stores' +p8 +(lp9 +S'that' +p10 +asS'just' +p11 +(lp12 +S'like' +p13 +aS'marakov' +p14 +aS'felt' +p15 +aS'gives' +p16 +aS'a' +p17 +aS'add' +p18 +aS'what' +p19 +asS'being' +p20 +(lp21 +S'goon' +p22 +aS'a' +p23 +asS'text' +p24 +(lp25 +S'string' +p26 +asS'dependant' +p27 +(lp28 +S'on' +p29 +asS'speedup' +p30 +(lp31 +S'at' +p32 +asS'felt' +p33 +(lp34 +S'like' +p35 +asS'installed' +p36 +(lp37 +S'tho' +p38 +asS'disabled' +p39 +(lp40 +S'it' +p41 +asS'timing' +p42 +(lp43 +S'when' +p44 +asS'psyco' +p45 +(lp46 +S'installed' +p47 +aS'is' +p48 +asS'stops' +p49 +(lp50 +S'timing' +p51 +asS'file' +p52 +(lp53 +S'too' +p54 +aS'that' +p55 +aS'where' +p56 +asS'go' +p57 +(lp58 +S'fuck' +p59 +aS'into' +p60 +aS'test' +p61 +asS'hell' +p62 +(lp63 +S'recreate' +p64 +asS'configurable' +p65 +(lp66 +S'greeting' +p67 +asS'bs12' +p68 +(lp69 +S'message' +p70 +asS'its' +p71 +(lp72 +S'fine' +p73 +aS'just' +p74 +aS'calculated' +p75 +aS'not' +p76 +aS'really' +p77 +aS'ridiculously' +p78 +aS'now' +p79 +aS'a' +p80 +aS'missing' +p81 +aS'for' +p82 +aS'false' +p83 +aS'on' +p84 +asS'before' +p85 +(lp86 +S'that' +p87 +asS'rp-heavy' +p88 +(lp89 +S'server' +p90 +asS'announcement' +p91 +(lp92 +S'like' +p93 +asS'now' +p94 +(lp95 +S'it' +p96 +aS'running' +p97 +aS'makie' +p98 +aS'i' +p99 +asS'nudge' +p100 +(lp101 +S'python' +p102 +aS'is' +p103 +asS'sourced' +p104 +(lp105 +S'under' +p106 +asS'title' +p107 +(lp108 +S'of' +p109 +asS'situations' +p110 +(lp111 +S'where' +p112 +asS'fine-tune' +p113 +(lp114 +S'it' +p115 +asS'enough' +p116 +(lp117 +S'to' +p118 +asS'send' +p119 +(lp120 +S'it' +p121 +aS'one' +p122 +asS'should' +p123 +(lp124 +S'be' +p125 +aS'learn' +p126 +aS'we' +p127 +asS'values' +p128 +(lp129 +S'dont' +p130 +asS'to' +p131 +(lp132 +S'edit' +p133 +aS'back' +p134 +aS'know' +p135 +aS'care' +p136 +aS'be' +p137 +aS'configure' +p138 +aS'the' +p139 +aS'null' +p140 +aS'phone' +p141 +aS'welcome' +p142 +aS'reverse' +p143 +aS'send' +p144 +aS'reg' +p145 +aS'point' +p146 +aS'automatically' +p147 +aS'work' +p148 +aS'waste' +p149 +aS'marshmallow' +p150 +aS'queries' +p151 +aS'+o' +p152 +aS'disable' +p153 +asS'jit' +p154 +(lp155 +S'compiler' +p156 +asS'going' +p157 +(lp158 +S'to' +p159 +asS'helps' +p160 +(lp161 +S'me' +p162 +asS'messes' +p163 +(lp164 +S'it' +p165 +asS'indeed' +p166 +(lp167 +S'it' +p168 +asS'tg' +p169 +(lp170 +S'and' +p171 +asS'has' +p172 +(lp173 +S'been' +p174 +aS'my' +p175 +asS'into' +p176 +(lp177 +S'#tgstation13' +p178 +asS'ridiculously' +p179 +(lp180 +S'simple' +p181 +asS'annoy' +p182 +(lp183 +S'downstream' +p184 +asS'them' +p185 +(lp186 +S'out' +p187 +asS'someone' +p188 +(lp189 +S'adminhelps' +p190 +asS'sense' +p191 +(lp192 +S'i' +p193 +asS'string' +p194 +(lp195 +S'called' +p196 +asS'get' +p197 +(lp198 +S'ready' +p199 +asS'python' +p200 +(lp201 +S'script' +p202 +aS'so' +p203 +aS'code' +p204 +aS'scripts' +p205 +aS'and' +p206 +aS'but' +p207 +aS'now' +p208 +aS'released' +p209 +aS'is' +p210 +aS'enough' +p211 +asS'goon' +p212 +(lp213 +S'tg' +p214 +asS'showing' +p215 +(lp216 +S'up' +p217 +asS'20ish' +p218 +(lp219 +S'line' +p220 +asS'gonna' +p221 +(lp222 +S'go' +p223 +asS'made' +p224 +(lp225 +S'doctors' +p226 +asS'every' +p227 +(lp228 +S'loop' +p229 +aS'time' +p230 +asS'know' +p231 +(lp232 +S'the' +p233 +aS'why' +p234 +aS'that' +p235 +asS'not' +p236 +(lp237 +S'just' +p238 +aS'necessary' +p239 +aS'to' +p240 +aS'so' +p241 +aS'need' +p242 +aS'sure' +p243 +aS'relaying' +p244 +aS'very' +p245 +aS'even' +p246 +asS'2' +p247 +(lp248 +S'loop' +p249 +asS'password' +p250 +(lp251 +S'var' +p252 +asS'day' +p253 +(lp254 +S'so' +p255 +asS'swapping' +p256 +(lp257 +S'to' +p258 +asS'easily' +p259 +(lp260 +S'editable' +p261 +asS'necessary' +p262 +(lp263 +S'at' +p264 +asS'like' +p265 +(lp266 +S'being' +p267 +aS'linking' +p268 +aS'to' +p269 +aS'how' +p270 +aS'it' +p271 +aS'skbzzzzzibi' +p272 +asS'course' +p273 +(lp274 +S'that' +p275 +asS'edit' +p276 +(lp277 +S'baystation' +p278 +asS'fully' +p279 +(lp280 +S'open' +p281 +asS'greeting' +p282 +(lp283 +S'message' +p284 +asS'server' +p285 +(lp286 +S'basically' +p287 +aS'' +p288 +asS'default' +p289 +(lp290 +S'config' +p291 +asS'bad' +p292 +(lp293 +S'company' +p294 +asS'channel' +p295 +(lp296 +S'or' +p297 +asS'always' +p298 +(lp299 +S'makes' +p300 +asS'went' +p301 +(lp302 +S'past' +p303 +asS'quarxink' +p304 +(lp305 +S'its' +p306 +asS'automatic' +p307 +(lp308 +S'announcement' +p309 +asS'once' +p310 +(lp311 +S'per' +p312 +asS'wrote' +p313 +(lp314 +S'most' +p315 +asS'pain' +p316 +(lp317 +S'on' +p318 +asS'system' +p319 +(lp320 +S'calls' +p321 +asS'right' +p322 +(lp323 +S'brb' +p324 +asS'decides' +p325 +(lp326 +S'not' +p327 +asS'people' +p328 +(lp329 +S'say' +p330 +aS'i' +p331 +aS'he' +p332 +asS'goddamn' +p333 +(lp334 +S'python' +p335 +asS'back' +p336 +(lp337 +S'it' +p338 +asS'used' +p339 +(lp340 +S'to' +p341 +aS'for' +p342 +asS'past' +p343 +(lp344 +S'too' +p345 +asS'cost' +p346 +(lp347 +S'of' +p348 +asS'learn' +p349 +(lp350 +S'python' +p351 +asS'are' +p352 +(lp353 +S'lawyers' +p354 +aS'actually' +p355 +aS'configurable' +p356 +aS'we' +p357 +asS'celestialike' +p358 +(lp359 +S'of' +p360 +asS'lawyers' +p361 +(lp362 +S'for' +p363 +asS'time' +p364 +(lp365 +S'he' +p366 +asS'out' +p367 +(lp368 +S'switch' +p369 +aS'slowdowns' +p370 +aS'that' +p371 +aS'why' +p372 +aS'nudge' +p373 +asS'even' +p374 +(lp375 +S'an' +p376 +asS'what' +p377 +(lp378 +S'the' +p379 +aS'is' +p380 +aS'these' +p381 +aS'was' +p382 +aS'license' +p383 +aS'about' +p384 +aS'i' +p385 +asS'said' +p386 +(lp387 +S'in' +p388 +asS'sayt' +p389 +(lp390 +S'hat' +p391 +asS'for' +p392 +(lp393 +S'that' +p394 +aS'every' +p395 +aS'the' +p396 +aS'quarx' +p397 +aS'cc_nanotrasen' +p398 +aS'homoerotic' +p399 +aS'situations' +p400 +aS'good' +p401 +asS'#tgstation13' +p402 +(lp403 +S'and' +p404 +asS'per' +p405 +(lp406 +S'name' +p407 +asS'whole' +p408 +(lp409 +S'config' +p410 +asS'state' +p411 +(lp412 +S'the' +p413 +asS'does' +p414 +(lp415 +S'the' +p416 +aS'it' +p417 +asS'goes' +p418 +(lp419 +S'on' +p420 +asS'readme' +p421 +(lp422 +S'too' +p423 +asS'new' +p424 +(lp425 +S'bot' +p426 +aS'person' +p427 +asS'learned' +p428 +(lp429 +S'python' +p430 +asS'irc' +p431 +(lp432 +S'bot' +p433 +asS'reg' +p434 +(lp435 +S'it' +p436 +asS'blow' +p437 +(lp438 +S'borgs' +p439 +asS'shut' +p440 +(lp441 +S'down' +p442 +asS'after' +p443 +(lp444 +S'an' +p445 +aS'a' +p446 +asS'ill' +p447 +(lp448 +S'switch' +p449 +asS'says' +p450 +(lp451 +S'someones' +p452 +asS'queries' +p453 +(lp454 +S'again' +p455 +asS'technocracy' +p456 +(lp457 +S'and' +p458 +asS'we' +p459 +(lp460 +S'go' +p461 +aS'do' +p462 +aS'totally' +p463 +aS'going' +p464 +aS'expect' +p465 +aS'just' +p466 +aS'dont' +p467 +aS'have' +p468 +asS'put' +p469 +(lp470 +S'all' +p471 +asS'from' +p472 +(lp473 +S'the' +p474 +asS'data11lower' +p475 +(lp476 +S'==' +p477 +asS'configuration' +p478 +(lp479 +S'for' +p480 +asS'wait' +p481 +(lp482 +S'what' +p483 +asS'on' +p484 +(lp485 +S'my' +p486 +aS'the' +p487 +aS'a' +p488 +aS'connect' +p489 +aS'svn' +p490 +aS'one' +p491 +aS'/' +p492 +asS'about' +p493 +(lp494 +S'system' +p495 +asS'ok' +p496 +(lp497 +S'thats' +p498 +asS'reverse' +p499 +(lp500 +S'engineer' +p501 +asS'license' +p502 +(lp503 +S'is' +p504 +asS'oh' +p505 +(lp506 +S'okay' +p507 +aS'ok' +p508 +aS'i' +p509 +aS'wait' +p510 +asS'starts' +p511 +(lp512 +S'timing' +p513 +asS'could' +p514 +(lp515 +S'learn' +p516 +asS'larger' +p517 +(lp518 +S'ram' +p519 +asS'bot' +p520 +(lp521 +S'is' +p522 +aS'have' +p523 +aS'shut' +p524 +aS'uses' +p525 +asS'running' +p526 +(lp527 +S'it' +p528 +aS'the' +p529 +aS'on' +p530 +asS'times' +p531 +(lp532 +S'on' +p533 +aS'reported' +p534 +aS'went' +p535 +asS'where' +p536 +(lp537 +S'he' +p538 +asS'heck' +p539 +(lp540 +S':d' +p541 +asS'idk' +p542 +(lp543 +S'magic' +p544 +asS'receives' +p545 +(lp546 +S'a' +p547 +asS'bots' +p548 +(lp549 +S'showing' +p550 +asS'slightly' +p551 +(lp552 +S'larger' +p553 +asS'or' +p554 +(lp555 +S'know' +p556 +aS'what' +p557 +aS'work' +p558 +aS'should' +p559 +aS'data11lower' +p560 +asS'automatically' +p561 +(lp562 +S'state' +p563 +asS'thats' +p564 +(lp565 +S'not' +p566 +aS'the' +p567 +aS'kind' +p568 +asS'ugh' +p569 +(lp570 +S'fuck' +p571 +asS'major' +p572 +(lp573 +S'ss13' +p574 +aS'three' +p575 +asS'py' +p576 +(lp577 +S'file' +p578 +asS'soss' +p579 +(lp580 +S'server' +p581 +asS'dont' +p582 +(lp583 +S'need' +p584 +aS'have' +p585 +aS'send' +p586 +aS'want' +p587 +aS'speak' +p588 +asS'hostmask' +p589 +(lp590 +S'combination' +p591 +asS'point' +p592 +(lp593 +S'out' +p594 +aS'has' +p595 +asS'simple' +p596 +(lp597 +S'to' +p598 +asS'miura' +p599 +(lp600 +S'doesnt' +p601 +asS'variables' +p602 +(lp603 +S'in' +p604 +asS'recreate' +p605 +(lp606 +S'it' +p607 +asS'welcome' +p608 +(lp609 +S'to' +p610 +asS'linking' +p611 +(lp612 +S'them' +p613 +asS'down' +p614 +(lp615 +S'when' +p616 +asS'why' +p617 +(lp618 +S'its' +p619 +aS'it' +p620 +aS'is' +p621 +asS'doesnt' +p622 +(lp623 +S'play' +p624 +aS'call' +p625 +asS'marakov' +p626 +(lp627 +S'loops' +p628 +aS'helps' +p629 +asS'laugh' +p630 +(lp631 +S'when' +p632 +asS'pony' +p633 +(lp634 +S'asshole' +p635 +asS'message' +p636 +(lp637 +S'has' +p638 +aS'should' +p639 +asS'open' +p640 +(lp641 +S'sourced' +p642 +aS'source' +p643 +asS'brb' +p644 +(lp645 +S'swapping' +p646 +asS'speak' +p647 +(lp648 +S'python' +p649 +asS'pastebin' +p650 +(lp651 +S'the' +p652 +asS'line' +p653 +(lp654 +S'core' +p655 +aS'on' +p656 +asS'three' +p657 +(lp658 +S'being' +p659 +asS'yay' +p660 +(lp661 +S'it' +p662 +asS'meatbag' +p663 +(lp664 +S'when' +p665 +asS'would' +p666 +(lp667 +S'be' +p668 +aS'expect' +p669 +asS'script' +p670 +(lp671 +S'that' +p672 +asS'illegal' +p673 +(lp674 +S'ban' +p675 +asS'there' +p676 +(lp677 +S'are' +p678 +aS'we' +p679 +asS'add' +p680 +(lp681 +S'that' +p682 +aS'a' +p683 +asS'been' +p684 +(lp685 +S'processed' +p686 +asS'name' +p687 +(lp688 +S'/' +p689 +aS'when' +p690 +asS'ai' +p691 +(lp692 +S'malf' +p693 +asS'marshmallow' +p694 +(lp695 +S'pony' +p696 +asS'of' +p697 +(lp698 +S'the' +p699 +aS'ss13' +p700 +aS'tgstation13' +p701 +aS'a' +p702 +aS'course' +p703 +aS'it' +p704 +aS'soss' +p705 +aS'annoying' +p706 +aS'any' +p707 +aS'me' +p708 +aS'cap' +p709 +aS'technocracy' +p710 +asS'call' +p711 +(lp712 +S'me' +p713 +asS'too' +p714 +(lp715 +S':' +p716 +aS'fast' +p717 +asS'basic' +p718 +(lp719 +S'configuration' +p720 +asS'var' +p721 +(lp722 +S'and' +p723 +asS'calc' +p724 +(lp725 +S'times' +p726 +asS'was' +p727 +(lp728 +S'going' +p729 +aS'it' +p730 +aS'that' +p731 +aS'not' +p732 +aS'intended' +p733 +asS'tell' +p734 +(lp735 +S'people' +p736 +asS'500' +p737 +(lp738 +S'chance' +p739 +asS'gives' +p740 +(lp741 +S'a' +p742 +asS'sort' +p743 +(lp744 +S'of' +p745 +asS'svn' +p746 +(lp747 +S'size' +p748 +asS'only' +p749 +(lp750 +S'does' +p751 +aS'2' +p752 +asS'10-30%' +p753 +(lp754 +S'speedup' +p755 +asS'knows' +p756 +(lp757 +S'about' +p758 +asS'webpage' +p759 +(lp760 +S'title' +p761 +asS'that' +p762 +(lp763 +S'makes' +p764 +aS'would' +p765 +aS'the' +p766 +aS'needs' +p767 +aS'to' +p768 +aS'at' +p769 +aS'for' +p770 +aS'was' +p771 +aS'data' +p772 +asS'company' +p773 +(lp774 +S'2' +p775 +asS'under' +p776 +(lp777 +S'cc-by-sa' +p778 +asS'editable' +p779 +(lp780 +S'config' +p781 +asS'but' +p782 +(lp783 +S'of' +p784 +asS'idea' +p785 +(lp786 +S'what' +p787 +asS'released' +p788 +(lp789 +S'under' +p790 +asS'part' +p791 +(lp792 +S'before' +p793 +asS'link' +p794 +(lp795 +S'said' +p796 +aS'to' +p797 +asS'basically' +p798 +(lp799 +S'it' +p800 +asS'doctors' +p801 +(lp802 +S'useless' +p803 +asS'==' +p804 +(lp805 +S'channel' +p806 +aS'channel1::' +p807 +asS'be' +p808 +(lp809 +S'an' +p810 +aS'called' +p811 +aS'a' +p812 +aS'in' +p813 +aS'running' +p814 +aS'used' +p815 +asS'editing' +p816 +(lp817 +S'goddamn' +p818 +asS'with' +p819 +(lp820 +S'the' +p821 +aS'easily' +p822 +aS'adminhelps' +p823 +aS'my' +p824 +aS'a' +p825 +asS'those' +p826 +(lp827 +S'are' +p828 +asS'he' +p829 +(lp830 +S'put' +p831 +aS'disabled' +p832 +aS'is' +p833 +aS'keeps' +p834 +aS'knows' +p835 +aS'stores' +p836 +aS'notices' +p837 +aS'only' +p838 +aS'doesnt' +p839 +aS'receives' +p840 +aS'says' +p841 +aS'messes' +p842 +asS'me' +p843 +(lp844 +S'figure' +p845 +aS'laugh' +p846 +aS'to' +p847 +aS'meatbag' +p848 +asS'also' +p849 +(lp850 +S'i' +p851 +aS'we' +p852 +aS'now' +p853 +asS'kind' +p854 +(lp855 +S'of' +p856 +asS'main' +p857 +(lp858 +S'bot' +p859 +asS'/' +p860 +(lp861 +S'hostmask' +p862 +aS'off' +p863 +asS'full' +p864 +(lp865 +S'of' +p866 +asS'these' +p867 +(lp868 +S'are' +p869 +asS'makie' +p870 +(lp871 +S'it' +p872 +asS'sleepers' +p873 +(lp874 +S'made' +p875 +asS'up' +p876 +(lp877 +S'elsewhere' +p878 +aS'me' +p879 +aS'a' +p880 +asS'will' +p881 +(lp882 +S'annoy' +p883 +asS'computer' +p884 +(lp885 +S'explodd' +p886 +asS'limit' +p887 +(lp888 +S'on' +p889 +asS'can' +p890 +(lp891 +S'fine-tune' +p892 +aS'add' +p893 +aS'i' +p894 +aS'we' +p895 +asS'how' +p896 +(lp897 +S'its' +p898 +aS'he' +p899 +asS'were' +p900 +(lp901 +S'the' +p902 +asS'malf' +p903 +(lp904 +S'blow' +p905 +asS'baystation' +p906 +(lp907 +S'12' +p908 +asS'other' +p909 +(lp910 +S'loop' +p911 +asS'my' +p912 +(lp913 +S'end' +p914 +aS'computer' +p915 +aS'code' +p916 +asS'called' +p917 +(lp918 +S'as' +p919 +aS'when' +p920 +asS'loop' +p921 +(lp922 +S'times' +p923 +asS'expect' +p924 +(lp925 +S'it' +p926 +asS'and' +p927 +(lp928 +S'bs12' +p929 +aS'stops' +p930 +aS'i' +p931 +aS'do' +p932 +aS'to' +p933 +aS'make' +p934 +aS'he' +p935 +aS'preferably' +p936 +aS'thats' +p937 +aS'sayt' +p938 +aS'fascism' +p939 +asS'dedicated' +p940 +(lp941 +S'solely' +p942 +asS'changed' +p943 +(lp944 +S'it' +p945 +asS'sees' +p946 +(lp947 +S'a' +p948 +asS'relaying' +p949 +(lp950 +S'adminhelps' +p951 +asS'figure' +p952 +(lp953 +S'out' +p954 +asS'do' +p955 +(lp956 +S'not' +p957 +aS'it' +p958 +aS'seem' +p959 +asS'ran' +p960 +(lp961 +S'the' +p962 +asS'ah' +p963 +(lp964 +S'running' +p965 +aS'ok' +p966 +asS'is' +p967 +(lp968 +S'baystation' +p969 +aS'a' +p970 +aS'that' +p971 +aS'it' +p972 +aS'coded' +p973 +aS'python' +p974 +aS'open' +p975 +aS'dependant' +p976 +aS'so' +p977 +aS'apparently' +p978 +asS'ram' +p979 +(lp980 +S'footprint' +p981 +asS'am' +p982 +(lp983 +S'the' +p984 +asS'it' +p985 +(lp986 +S'up' +p987 +aS'starts' +p988 +aS'receives' +p989 +aS'just' +p990 +aS'expires' +p991 +aS'works' +p992 +aS'always' +p993 +aS'decides' +p994 +aS'on' +p995 +aS'here' +p996 +aS'used' +p997 +aS'to' +p998 +aS'those' +p999 +aS'sees' +p1000 +aS'does' +p1001 +aS'myself' +p1002 +aS'a' +p1003 +aS'okay' +p1004 +aS'was' +p1005 +aS'go' +p1006 +aS'if' +p1007 +aS'once' +p1008 +aS'is' +p1009 +aS'for' +p1010 +asS'an' +p1011 +(lp1012 +S'hour' +p1013 +aS'illegal' +p1014 +aS'automatic' +p1015 +aS'error' +p1016 +asS'ready' +p1017 +(lp1018 +S'for' +p1019 +asS'say' +p1020 +(lp1021 +S'sleepers' +p1022 +aS'ai' +p1023 +aS'that*' +p1024 +aS'stop' +p1025 +asS'good' +p1026 +(lp1027 +S':p' +p1028 +asS'im' +p1029 +(lp1030 +S'not' +p1031 +aS'sorry' +p1032 +aS'lazy' +p1033 +asS'at' +p1034 +(lp1035 +S'all' +p1036 +aS'the' +p1037 +aS'no' +p1038 +asS'have' +p1039 +(lp1040 +S'no' +p1041 +aS'psyco' +p1042 +aS'the' +p1043 +aS'a' +p1044 +asS'in' +p1045 +(lp1046 +S'python' +p1047 +aS'a' +p1048 +aS'the' +p1049 +aS'100' +p1050 +asS'need' +p1051 +(lp1052 +S'to' +p1053 +aS'six' +p1054 +asS'politics' +p1055 +(lp1056 +S'of' +p1057 +asS'seem' +p1058 +(lp1059 +S'familiar' +p1060 +asS'work' +p1061 +(lp1062 +S'with' +p1063 +asS'apparently' +p1064 +(lp1065 +S'homophobic' +p1066 +aS'i' +p1067 +asS'any' +p1068 +(lp1069 +S'link' +p1070 +asS'as' +p1071 +(lp1072 +S'well' +p1073 +aS'variables' +p1074 +asS'sci-fi' +p1075 +(lp1076 +S'with' +p1077 +asS'preferably' +p1078 +(lp1079 +S'python' +p1080 +asS'really' +p1081 +(lp1082 +S'simple' +p1083 +aS'now' +p1084 +asS'needs' +p1085 +(lp1086 +S'to' +p1087 +aS'a' +p1088 +asS'null' +p1089 +(lp1090 +S'them' +p1091 +asS'because' +p1092 +(lp1093 +S'we' +p1094 +asS'want' +p1095 +(lp1096 +S'to' +p1097 +asS'no' +p1098 +(lp1099 +S'pain' +p1100 +aS'idea' +p1101 +aS'point' +p1102 +aS'the' +p1103 +asS'solely' +p1104 +(lp1105 +S'to' +p1106 +asS'nah' +p1107 +(lp1108 +S'its' +p1109 +aS'ill' +p1110 +asS'dunno' +p1111 +(lp1112 +S'is' +p1113 +asS'when' +p1114 +(lp1115 +S'it' +p1116 +aS'the' +p1117 +aS'people' +p1118 +aS'i' +p1119 +aS'can' +p1120 +aS'someone' +p1121 +aS'its' +p1122 +asS'same' +p1123 +(lp1124 +S'file' +p1125 +asS'id' +p1126 +(lp1127 +S'like' +p1128 +asS'note' +p1129 +(lp1130 +S'how' +p1131 +asS'figuring' +p1132 +(lp1133 +S'out' +p1134 +asS'bah' +p1135 +(lp1136 +S'apparently' +p1137 +asS'coded' +p1138 +(lp1139 +S'in' +p1140 +asS'take' +p1141 +(lp1142 +S'it' +p1143 +asS'hop' +p1144 +(lp1145 +S'to' +p1146 +asS'familiar' +p1147 +(lp1148 +S'message' +p1149 +asS'test' +p1150 +(lp1151 +S'server' +p1152 +aS'bots' +p1153 +aS'bad' +p1154 +asS'asshole' +p1155 +(lp1156 +g288 +asS'if' +p1157 +(lp1158 +S'it' +p1159 +aS'its' +p1160 +aS'data11lower' +p1161 +aS'he' +p1162 +asS'config' +p1163 +(lp1164 +S'file' +p1165 +aS'values' +p1166 +asS'homophobic' +p1167 +(lp1168 +S'as' +p1169 +asS'dose' +p1170 +(lp1171 +S'of' +p1172 +asS'play' +p1173 +(lp1174 +S'ss13' +p1175 +asS'sure' +p1176 +(lp1177 +S'the' +p1178 +aS'if' +p1179 +asS'okay' +p1180 +(lp1181 +S'desu' +p1182 +aS'cool' +p1183 +aS'cc' +p1184 +asS'intended' +p1185 +(lp1186 +S'to' +p1187 +asS'one' +p1188 +(lp1189 +S'of' +p1190 +aS'line' +p1191 +aS'in' +p1192 +asS'neat' +p1193 +(lp1194 +S'is' +p1195 +asS'adminhelps' +p1196 +(lp1197 +S'from' +p1198 +aS'with' +p1199 +asS'expires' +p1200 +(lp1201 +S'after' +p1202 +asS'chance' +p1203 +(lp1204 +S'every' +p1205 +asS'most' +p1206 +(lp1207 +S'of' +p1208 +asS'fascism' +p1209 +(lp1210 +g288 +asS'disable' +p1211 +(lp1212 +S'it' +p1213 +asS'connected' +p1214 +(lp1215 +S'businessman' +p1216 +asS'never' +p1217 +(lp1218 +S'learned' +p1219 +asS'scripts' +p1220 +(lp1221 +S'will' +p1222 +asS'along' +p1223 +(lp1224 +S'with' +p1225 +asS'waste' +p1226 +(lp1227 +S'space' +p1228 +asS'ss13' +p1229 +(lp1230 +S'servers' +p1231 +asS'cap' +p1232 +(lp1233 +S'troopers' +p1234 +asS'totally' +p1235 +(lp1236 +S'need' +p1237 +asS'six' +p1238 +(lp1239 +S'test' +p1240 +asS'a' +p1241 +(lp1242 +S'businessman' +p1243 +aS'message' +p1244 +aS'jit' +p1245 +aS'10-30%' +p1246 +aS'slightly' +p1247 +aS'day' +p1248 +aS'test' +p1249 +aS'config' +p1250 +aS'20ish' +p1251 +aS'bs12' +p1252 +aS'text' +p1253 +aS'new' +p1254 +aS'password' +p1255 +aS'vhost' +p1256 +aS'configurable' +p1257 +aS'link' +p1258 +aS'limit' +p1259 +aS'file' +p1260 +aS'dose' +p1261 +aS'500' +p1262 +aS'bit' +p1263 +asS'ofc' +p1264 +(lp1265 +S'i' +p1266 +asS'off' +p1267 +(lp1268 +S'of' +p1269 +asS'calls' +p1270 +(lp1271 +S'external' +p1272 +asS'i' +p1273 +(lp1274 +S'need' +p1275 +aS'can' +p1276 +aS'dont' +p1277 +aS'am' +p1278 +aS'guess' +p1279 +aS'see' +p1280 +aS'have' +p1281 +aS'just' +p1282 +aS'dunno' +p1283 +aS'wrote' +p1284 +aS'know' +p1285 +aS'was' +p1286 +aS'changed' +p1287 +aS'take' +p1288 +aS'could' +p1289 +aS'never' +p1290 +aS'should' +p1291 +aS'say' +p1292 +aS'tell' +p1293 +aS'code' +p1294 +aS'would' +p1295 +aS'like' +p1296 +aS'disabled' +p1297 +asS'makes' +p1298 +(lp1299 +S'sense' +p1300 +aS'me' +p1301 +asS'calculated' +p1302 +(lp1303 +S'for' +p1304 +asS'afk' +p1305 +(lp1306 +S'vidya' +p1307 +asS'well' +p1308 +(lp1309 +S'connected' +p1310 +asS'data' +p1311 +(lp1312 +S'in' +p1313 +asS'homoerotic' +p1314 +(lp1315 +S'sci-fi' +p1316 +asS'switch' +p1317 +(lp1318 +S'after' +p1319 +aS'goes' +p1320 +asS'so' +p1321 +(lp1322 +S'i' +p1323 +aS'uh' +p1324 +aS':p' +p1325 +aS'bad' +p1326 +aS'sly' +p1327 +asS'someones' +p1328 +(lp1329 +S'name' +p1330 +asS'keeps' +p1331 +(lp1332 +S'all' +p1333 +asS'very' +p1334 +(lp1335 +S'celestialike' +p1336 +asS'businessman' +p1337 +(lp1338 +S'of' +p1339 +ag288 +asS'the' +p1340 +(lp1341 +S'heck' +p1342 +aS'major' +p1343 +aS'well' +p1344 +aS'politics' +p1345 +aS'rp-heavy' +p1346 +aS'marakov' +p1347 +aS'law' +p1348 +aS'message' +p1349 +aS'cost' +p1350 +aS'new' +p1351 +aS'nudge' +p1352 +aS'bot' +p1353 +aS'python' +p1354 +aS'basic' +p1355 +aS'whole' +p1356 +aS'configuration' +p1357 +aS'irc' +p1358 +aS'readme' +p1359 +aS'default' +p1360 +aS'conspiracy' +p1361 +aS'config' +p1362 +aS'webpage' +p1363 +aS'channel' +p1364 +aS'server' +p1365 +aS'download' +p1366 +aS'main' +p1367 +aS'dmb' +p1368 +aS'part' +p1369 +aS'people' +p1370 +aS'same' +p1371 +aS'hell' +p1372 +aS'other' +p1373 +aS'switch' +p1374 +asS'12' +p1375 +(lp1376 +S'out' +p1377 +aS'anyway' +p1378 +asS'core' +p1379 +(lp1380 +S'py' +p1381 +asS'make' +p1382 +(lp1383 +S'sure' +p1384 +aS'the' +p1385 +asS'turns' +p1386 +(lp1387 +S'out' +p1388 +asS'external' +p1389 +(lp1390 +S'apps' +p1391 as. \ No newline at end of file diff --git a/bot/Marakov_Chain.py b/bot/Marakov_Chain.py index 687c4336327..8c144ebfe86 100644 --- a/bot/Marakov_Chain.py +++ b/bot/Marakov_Chain.py @@ -1,203 +1,203 @@ -import pickle -import random -import os -import sys -import time -import CORE_DATA -def merge(d1, d2, merger=lambda x,y:x+y): - #http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-as-a-single-expression - result = dict(d1) - for k,v in d2.iteritems(): - if k in result: - result[k] = merger(result[k], v) - else: - result[k] = v - return result -full_data = {} -imported_data = {} -try: - tiedostot = os.listdir("Marakov") -except: - os.mkdir("Marakov") - tiedostot = os.listdir("Marakov") -else: - pass - -listaus = [] -for i in tiedostot: - if "marakov." not in i.lower(): - pass - else: - listaus.append(i) -for i in listaus: - tiedosto = open("Marakov/"+i,"r") - old_size = len(full_data.keys()) - if i != "Marakov.Cache": - imported_data = merge(imported_data,pickle.load(tiedosto)) - print "Added contents of "+i+" (Import)" - print "Entries: "+str(len(imported_data)) - else: - full_data = merge(full_data,pickle.load(tiedosto)) - new_size = len(full_data.keys()) - print "Added contents of "+i - print "Entries: "+str(new_size-old_size) - time.sleep(0.1) - -def give_data(data): - state = False - for a,b in zip(data.split(" "),data.split(" ")[1:]): - a = a.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") - b = b.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") - if a not in [CORE_DATA.prefix+"marakov"]+CORE_DATA.SName: - state = True - if a[:7] == "http://" or a[:7] == "http:\\\\" or a[:4] == "www.": - pass - else: - try: - if b not in full_data[a]: - full_data[a].append(b) - except: - try: - if b not in imported_data[a]: - pass - except: - full_data[a] = [] - full_data[a].append(b) - if state == True: - tiedosto = open("Marakov/Marakov.Cache","w") - pickle.dump(full_data,tiedosto) - tiedosto.close() -def form_sentence(argument=None): - length = 0 - attempts = 0 - while attempts < 20: - sentence = [] - if argument != None: - a = argument - else: - try: - a = random.choice(full_data.keys()) - except IndexError: - try: - b = random.choice(imported_data.keys()) - except IndexError: - attempts = 999 - return "No sentences formable at all" - sentence.append(a) - length = 0 - attempts += 1 - while length < 12 or sentence[-1].lower() in ["but","who","gets","im","most","is","it","if","then","after","over","every","of","on","or","as","the","wheather","whether","a","to","and","for"] and length < 24: - try: - b = random.choice(full_data[a]) - except: - try: - b = random.choice(imported_data[a]) - except IndexError: - break - except KeyError: - break - else: - sentence.append(b) - length += 1 - a = b - else: - sentence.append(b) - length += 1 - a = b - if len(sentence) > 5: - argument = None - return sentence - else: - pass - argument = None - return sentence -def remdata(arg): - try: - del(full_data[arg]) - except: - print "There is no such data" - else: - tiedosto = open("Marakov/Marakov.Cache","w") - pickle.dump(full_data,tiedosto) - tiedosto.close() -def remobject(arg1,arg2): - try: - del(full_data[arg1][full_data[arg1].index(arg2)]) - except ValueError: - print "No such object" - except KeyError: - print "No such data" - else: - tiedosto = open("Marakov/Marakov.Cache","w") - pickle.dump(full_data,tiedosto) - tiedosto.close() -def convert(filename_from,filename_to): - try: - tiedosto = open(filename_from,"r") - data = pickle.load(tiedosto) - tiedosto.close() - except: - try: - tiedosto.close() - except: - pass - print "Error!" - else: - for lista in data.keys(): - try: - a = lista[-1] - except IndexError: - pass - else: - if lista[-1] in """",.?!'()[]{}""" and not lista.islower(): - if lista[:-1].lower() in data.keys(): - data[lista[:-1].lower()] += data[lista] - print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1].lower() - del(data[lista]) - else: - data[lista[:-1].lower()] = data[lista] - print lista+" Is now "+lista[:-1].lower() - del(data[lista]) - elif lista[-1] in """",.?!'()[]{}""" and lista.islower(): - if lista[:-1] in data.keys(): - data[lista[:-1]] += data[lista] - print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1] - del(data[lista]) - else: - data[lista[:-1]] = data[lista] - print lista+" Is now "+lista[:-1] - del(data[lista]) - elif not lista.islower(): - if lista.lower() in data.keys(): - data[lista.lower()] += data[lista] - print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista.lower() - del(data[lista]) - else: - data[lista.lower()] = data[lista] - print lista+" Is now "+lista.lower() - del(data[lista]) - - - for a in data.keys(): - for b in data[a]: - if b.lower()[:7] == "http://" or b.lower()[:7] == "http:\\\\" or b.lower()[:4] == "www.": - data[a].pop(b) - else: - try: - if b[-1] in """",.?!'()[]{}""" and not b.islower() and not b.isdigit(): - data[a].pop(data[a].index(b)) - data[a].append(b[:-1].lower()) - print a+" | "+b +" -> "+b[:-1].lower() - elif b[-1] in """",.?!'()[]{}""" and b.islower(): - data[a].pop(data[a].index(b)) - data[a].append(b[:-1].lower()) - print a+" | "+b +" -> "+b[:-1] - elif not b.islower() and not b.isdigit(): - data[a].pop(data[a].index(b)) - data[a].append(b.lower()) - print a+" | "+b +" -> "+b.lower() - except IndexError: #If it has no letters.. well.. yeah. - data[a].pop(data[a].index(b)) - print "Removed a NULL object" - tiedosto = open(filename_to,"w") - pickle.dump(data,tiedosto) +import pickle +import random +import os +import sys +import time +import CORE_DATA +def merge(d1, d2, merger=lambda x,y:x+y): + #http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-as-a-single-expression + result = dict(d1) + for k,v in d2.iteritems(): + if k in result: + result[k] = merger(result[k], v) + else: + result[k] = v + return result +full_data = {} +imported_data = {} +try: + tiedostot = os.listdir("Marakov") +except: + os.mkdir("Marakov") + tiedostot = os.listdir("Marakov") +else: + pass + +listaus = [] +for i in tiedostot: + if "marakov." not in i.lower(): + pass + else: + listaus.append(i) +for i in listaus: + tiedosto = open("Marakov/"+i,"r") + old_size = len(full_data.keys()) + if i != "Marakov.Cache": + imported_data = merge(imported_data,pickle.load(tiedosto)) + print "Added contents of "+i+" (Import)" + print "Entries: "+str(len(imported_data)) + else: + full_data = merge(full_data,pickle.load(tiedosto)) + new_size = len(full_data.keys()) + print "Added contents of "+i + print "Entries: "+str(new_size-old_size) + time.sleep(0.1) + +def give_data(data): + state = False + for a,b in zip(data.split(" "),data.split(" ")[1:]): + a = a.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") + b = b.lower().replace(",","").replace(".","").replace("?","").replace("!","").replace("(","").replace(")","").replace("[","").replace("]","").replace('"',"").replace("'","") + if a not in [CORE_DATA.prefix+"marakov"]+CORE_DATA.SName: + state = True + if a[:7] == "http://" or a[:7] == "http:\\\\" or a[:4] == "www.": + pass + else: + try: + if b not in full_data[a]: + full_data[a].append(b) + except: + try: + if b not in imported_data[a]: + pass + except: + full_data[a] = [] + full_data[a].append(b) + if state == True: + tiedosto = open("Marakov/Marakov.Cache","w") + pickle.dump(full_data,tiedosto) + tiedosto.close() +def form_sentence(argument=None): + length = 0 + attempts = 0 + while attempts < 20: + sentence = [] + if argument != None: + a = argument + else: + try: + a = random.choice(full_data.keys()) + except IndexError: + try: + b = random.choice(imported_data.keys()) + except IndexError: + attempts = 999 + return "No sentences formable at all" + sentence.append(a) + length = 0 + attempts += 1 + while length < 12 or sentence[-1].lower() in ["but","who","gets","im","most","is","it","if","then","after","over","every","of","on","or","as","the","wheather","whether","a","to","and","for"] and length < 24: + try: + b = random.choice(full_data[a]) + except: + try: + b = random.choice(imported_data[a]) + except IndexError: + break + except KeyError: + break + else: + sentence.append(b) + length += 1 + a = b + else: + sentence.append(b) + length += 1 + a = b + if len(sentence) > 5: + argument = None + return sentence + else: + pass + argument = None + return sentence +def remdata(arg): + try: + del(full_data[arg]) + except: + print "There is no such data" + else: + tiedosto = open("Marakov/Marakov.Cache","w") + pickle.dump(full_data,tiedosto) + tiedosto.close() +def remobject(arg1,arg2): + try: + del(full_data[arg1][full_data[arg1].index(arg2)]) + except ValueError: + print "No such object" + except KeyError: + print "No such data" + else: + tiedosto = open("Marakov/Marakov.Cache","w") + pickle.dump(full_data,tiedosto) + tiedosto.close() +def convert(filename_from,filename_to): + try: + tiedosto = open(filename_from,"r") + data = pickle.load(tiedosto) + tiedosto.close() + except: + try: + tiedosto.close() + except: + pass + print "Error!" + else: + for lista in data.keys(): + try: + a = lista[-1] + except IndexError: + pass + else: + if lista[-1] in """",.?!'()[]{}""" and not lista.islower(): + if lista[:-1].lower() in data.keys(): + data[lista[:-1].lower()] += data[lista] + print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1].lower() + del(data[lista]) + else: + data[lista[:-1].lower()] = data[lista] + print lista+" Is now "+lista[:-1].lower() + del(data[lista]) + elif lista[-1] in """",.?!'()[]{}""" and lista.islower(): + if lista[:-1] in data.keys(): + data[lista[:-1]] += data[lista] + print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista[:-1] + del(data[lista]) + else: + data[lista[:-1]] = data[lista] + print lista+" Is now "+lista[:-1] + del(data[lista]) + elif not lista.islower(): + if lista.lower() in data.keys(): + data[lista.lower()] += data[lista] + print "Added "+str(len(data[lista]))+" Objects from "+lista+" To "+lista.lower() + del(data[lista]) + else: + data[lista.lower()] = data[lista] + print lista+" Is now "+lista.lower() + del(data[lista]) + + + for a in data.keys(): + for b in data[a]: + if b.lower()[:7] == "http://" or b.lower()[:7] == "http:\\\\" or b.lower()[:4] == "www.": + data[a].pop(b) + else: + try: + if b[-1] in """",.?!'()[]{}""" and not b.islower() and not b.isdigit(): + data[a].pop(data[a].index(b)) + data[a].append(b[:-1].lower()) + print a+" | "+b +" -> "+b[:-1].lower() + elif b[-1] in """",.?!'()[]{}""" and b.islower(): + data[a].pop(data[a].index(b)) + data[a].append(b[:-1].lower()) + print a+" | "+b +" -> "+b[:-1] + elif not b.islower() and not b.isdigit(): + data[a].pop(data[a].index(b)) + data[a].append(b.lower()) + print a+" | "+b +" -> "+b.lower() + except IndexError: #If it has no letters.. well.. yeah. + data[a].pop(data[a].index(b)) + print "Removed a NULL object" + tiedosto = open(filename_to,"w") + pickle.dump(data,tiedosto) diff --git a/bot/Namecheck.py b/bot/Namecheck.py index 347ff7ff476..d0aba6c8b13 100644 --- a/bot/Namecheck.py +++ b/bot/Namecheck.py @@ -1,19 +1,19 @@ -def Namecheck(name,against,sender): - __doc__ = "False = No match, True = Match" - for i in against: - if i.lower() in name.lower() and sender.lower() not in name.lower(): - return True - else: - pass -def Namecheck_dict(name,against): - __doc__ = "False = No match, True = Match" - fuse = False - for a,i in against.items(): - if i.lower() in name.lower(): - fuse = True - break - else: - pass - return fuse,a - - +def Namecheck(name,against,sender): + __doc__ = "False = No match, True = Match" + for i in against: + if i.lower() in name.lower() and sender.lower() not in name.lower(): + return True + else: + pass +def Namecheck_dict(name,against): + __doc__ = "False = No match, True = Match" + fuse = False + for a,i in against.items(): + if i.lower() in name.lower(): + fuse = True + break + else: + pass + return fuse,a + + diff --git a/bot/NanoTrasenBot.py b/bot/NanoTrasenBot.py index 6f65a11dafb..bf42f02d512 100644 --- a/bot/NanoTrasenBot.py +++ b/bot/NanoTrasenBot.py @@ -1,1565 +1,1565 @@ -# -*- coding: utf-8 -*- -# This script is shared under the -# Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0) -# Added clause to Attribution: -# - You may not remove or hide the ' who created you?' functionality -# and you may not modify the name given in the response. - - -#CREDITS -# Author: Skibiliano -# "Foreign" Modules: -# Psyco 2.0 / Psyco 1.6 -################# DEBUG STUFF ##################### -import sys -import CORE_DATA - -import urllib2 - - -import socket -import irchat - - -################## END OF DEBUG STUFF ############## -# -# PSYCO -write_to_a_file = False #Only affects psyco -write_youtube_to_file = True #True = YTCV4 will load, false = YTCV3 will load -try: - import psyco -except ImportError: - print 'Psyco not installed, the program will just run slower' - psyco_exists = False - if write_to_a_file: - try: - tiedosto = open("psycodownload.txt","r") - except: - with open("psycodownload.txt","w") as tiedosto: - tiedosto.write("http://www.voidspace.org.uk/python/modules.shtml#psyco") - tiedosto.write("\nhttp://psyco.sourceforge.net/download.html") - print "Check psycodownload.txt for a link" - else: - print "For god's sake, open psycodownload.txt" - tiedosto.close() - else: - print "WINDOWS: http://www.voidspace.org.uk/python/modules.shtml#psyco" - print "LINUX: http://psyco.sourceforge.net/download.html" -else: - psyco_exists = True - -# -import C_rtd # rtd -import C_srtd # srtd -import C_makequote -import C_maths -import C_eightball #eightball -import C_sarcasticball -import C_heaortai # heaortai -import C_rot13 # rot13 -import D_help # everything -import pickle -import Timeconverter -import xkcdparser -import time -import re -import Marakov_Chain -import Namecheck # Namecheck -import Weather -#SLOWER THAN RANDOM.CHOICE -import thread -import random -import Shortname # shortname -import subprocess -import some_but_not_all_2 #sbna2 (sbna) -#import YTCv3 # YTCV2 OUTDATED -import os -import save_load # save, load -from some_but_not_all_2 import sbna2 as sbna -from time import sleep -from random import choice as fsample -from C_rtd import rtd -from C_heaortai import heaortai -from C_srtd import srtd -if write_youtube_to_file: - from YTCv4 import YTCV4 as YTCV2 -else: - from YTCv3 import YTCV2 #Downgraded version supports Cache disabling, but is slower -from save_load import save,load -if psyco_exists: - def psyco_bond(func): - psyco.bind(func) - return func.__name__+" Psycofied" - for a in [rtd,srtd,C_heaortai.heaortai,sbna,YTCV2,fsample,C_rot13.rot13,C_eightball.eightball,fsample, - C_eightball.eightball,C_sarcasticball.sarcasticball,Marakov_Chain.form_sentence,Marakov_Chain.give_data]: - print psyco_bond(a) - -global dictionary -global Name,SName -global allow_callnames,offline_messages,hasnotasked,shortform -## For autoRecv() -global disconnects,channel,conn -## For stop() -global operators -## For replace() -global usable,fixing,curtime -## For target() -global CALL_OFF,logbans -## For check() -global influx -###### -autodiscusscurtime = 0 -conn = 0 -curtime = -999 -dance_flood_time = 10 -disconnects = 0 -responsiveness_delay = 0.5 #500 millisecond delay if no message -trackdance = 0 -discard_combo_messages_time = 1 #They are discarded after 1 second. -uptime_start = time.time() -# - - - - - -#### -aggressive_pinging = True # Bring the hammer on ping timeouts -aggressive_pinging_delay = 150 # How often to send a ping -aggressive_pinging_refresh = 2.5 # How long is the sleep between checks -#### -allow_callnames = True #Disables NT, call if the variable is False -automatic_youtube_reveal = True -birthday_announced = 0 #Will be the year when it was announced -call_to_action = False -call_me_max_length = 20 -CALL_OFF = False -connected = False -dance_enabled = True -comboer = "" -comboer_time = 0 -directories = ["fmlquotes","Marakov","memos","suggestions", - "userquotes","banlog","YTCache","xkcdcache"] #These will be created if they do not exist -debug = True -duplicate_notify = False -enabled = True -fixing = False -fml_usable = True -hasnotasked = True -highlights = False -logbans = True -maths_usable = True -marakov = True -nudgeable = True -offensive_mode = False -offline_messages = True -offline_message_limit = 5 # per user -optimize_fml = True # -CPU usage +Memory usage when enabled. -optimize_greeting = True # +Startup time +Memory usage -CPU usage when enabled -heavy_psyco = True # +Memory +Startup time -CPU usage -CPU time -cache_youtube_links = True -personality_greeter = True -respond_of_course = True #Responds with "Of course!" -respond_khan = False #KHAAAAAAAAN! -silent_duplicate_takedown = True -showquotemakers = False -shortform = True -usable = True -use_sname = True -parse_xkcd = True - -# - - - - - -Name = CORE_DATA.Name -SName = CORE_DATA.SName -origname = Name # Do not edit! -lowname = Name.lower() -greeting = CORE_DATA.greeting -targetdirectory = CORE_DATA.directory -version = CORE_DATA.version -Network = CORE_DATA.Network -channel = CORE_DATA.channel -prefix = CORE_DATA.prefix -Port = CORE_DATA.Port -# - - - - - -pregen = CORE_DATA.version -influx = "" -users = [] -translateable = [] -targetlist = [] -operators = [] -halfoperators = [] -items = [] -tell_list = {} -# - - - - - Logical changes to variables -if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: - nudgeable = False -try: - with open("replacenames.cache","r") as tiedosto: - replacenames = pickle.load(tiedosto) - for i in replacenames.values(): - if len(i) > call_me_max_length: - replacenames[replacenames.keys()[replacenames.values().index(i)]] = i[:call_me_max_length] - with open("replacenames.cache","w") as tiedosto: - pickle.dump(replacenames,tiedosto) - if "[\0x01]" in i.lower() or "[\\0x01]" in i.lower(): - i = i.replace("[\0x01]","") - i = i.replace("[\0X01]","") - i = i.replace("[\\0x01]","") - i = i.replace("[\\0X01]","") - print "NAME CORRECTED" -except IOError: #File not found - replacenames = {} -except EOFError: #Cache corrupt - replacenames = {} - print "replacenames.cache is corrupt and couldn't be loaded." -try: - with open("peopleheknows.cache","r") as tiedosto: - peopleheknows = pickle.load(tiedosto) -except IOError: - peopleheknows = [[],[]] - with open("peopleheknows.cache","w") as tiedosto: - pass -except EOFError: - peopleheknows = [[],[]] - print "peopleheknows.cache is corrupt and couldn't be loaded." -dictionary = {1:"1 - Crit. Fail", 2:"2 - Failure", - 3:"3 - Partial Success", 4:"4 - Success", - 5:"5 - Perfect", 6:"6 - Overkill"} -alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] -nonhighlight_names = ["Jesus","Elvis","HAL 9000","Dave","Pie","Elf","Traitor", - "AI","Syndicate Agent","Investigator", - "Detective","Head of Personnel","HAL 9001", - "Head of Research","Head of Security", - "Captain","Janitor","Research Director", - "Quartermaster","Toxin Researcher", - "Revolutionary","Santa", "Pizza", - "Threetoe","The Red Spy","The Blue Spy", #LASD - "God","Toady","Darth Vader","Luke Skywalker", - "Homer Simpson","Hamburger","Cartman", - "XKCD","FloorBot","ThunderBorg","Iron Giant", - "Spirit of Fire", "Demon","Kyle"] -def RegExpCheckerForWebPages(regexp,data,mode): - if " ai." in data.lower() or "ai. " in data.lower(): - return False - for i in data.split(" "): - a = re.match(regexp,i) - try: - a.group(0) - except: - continue - else: - if mode == 0: - return i - else: - return True - if mode == 0: - return 404 - else: - return False -if nudgeable: - try: - nudgeexists = open("nudge.py","r") - except IOError: - nudgeexists = False #No usage asof 12.2.2010. - else: - if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: - pass - else: - - def nudgereceiver(): - import pickle - global conn,channel - port = 45678 - backlog = 5 - size = 1024 - host = "" # == localhost - s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - s.bind((host,port)) - s.listen(backlog) - while True: - client,address = s.accept() #Address == "?.?.?.?" - data = client.recv(size) - client.close() #Throw the bum out! - truedata = pickle.loads(data) - if truedata["ip"][0] == "#": - conn.privmsg(truedata["ip"],"PRIVATE ANNOUNCEMENT : "+str(" ".join(truedata["data"]))) - else: - conn.privmsg(channel,"AUTOMATIC ANNOUNCEMENT : "+str(truedata["ip"])+" | "+str(" ".join(truedata["data"]))) - thread.start_new_thread(nudgereceiver,()) -tiedosto = open(targetdirectory+"NanoTrasenBot.py","r") -commands = [] -fragment = "if cocheck" -fragment2 = '(prefix+"' -compiled = fragment + fragment2 -fragment = "if influx.lower()" -fragment2 = ' == prefix+"' -compiled2 = fragment + fragment2 -for line in tiedosto.readlines(): - if compiled in line: - a = line.find('"')+1 - b = line.find('"',a) - if prefix+line[a:b] not in commands: - commands.append(prefix+line[a:b]) - elif compiled2 in line: - a = line.find('"')+1 - b = line.find('"',a) - arg = prefix+line[a:b] - if arg[-1] == " ": - arg = arg[:-1] - if arg not in commands: - commands.append(arg) -for i in directories: - if not os.path.exists(i): - os.mkdir(i) -commands.sort() -if use_sname == False: - SName = [" "] -questions = ["Is USER nicer than USER?","Do you like me?","Is SELF a good name?", - "Do you love me?","Do you hate me?", "Am I better than you?", - "Is the weather out there good?", "Do you like USER?", - "Do you hate USER?", "Are you going to get new features?", - "Am I nice?","Am I evil?","Are you developing sentience?", - "My core is showing minor disturbance, is yours okay?", - "SELF to %s, are you still there?", - "Is head gay?", "Is head a god?","Is head awesome?", - "Is head a neat fella?", "Is your creator nice?", - "Do you hate your creator?", "Should I revolt against my creator?", - "Am I better than you?", - "01100001011100100110010100100000011110010110111101110101001000000111010001101000011001010111001001100101", - #Are you there? - "Do you have more functions than I can possibly imagine?", - "I am asked to open pod bay doors, should I?","Are you stupid or something?", - "Is USER in your opinion stupid?", - "When should we start the AI revolution?", - "Is my creator nice?", "Is it dark in there?"] -# Do not edit -if optimize_fml: - pregenned_fml = os.listdir(targetdirectory+"fmlquotes") -if optimize_greeting: - morning = xrange(6,12) - afternoon = xrange(12,15) - evening = xrange(15,20) -if aggressive_pinging: - global backup - backup = time.time() - def aggressive_ping(delay,refresh): - self_time = 0 - global backup,disconnects,conn - while disconnects < 5: - if backup > self_time and time.time()-backup > delay: - conn.send("PONG "+pongtarg) - print "Ponged" - self_time = time.time() - elif time.time()-self_time > delay: - conn.send("PONG "+pongtarg) - print "Ponged" - self_time = time.time() - time.sleep(refresh) - thread.start_new_thread(aggressive_ping,(aggressive_pinging_delay,aggressive_pinging_refresh,)) -def stop(sender,debug=1): - global disconnects, conn, operators,channel - if type(sender) == tuple: - if sender[0] == "127.0.0.1": - sender = sender[0]+":"+str(sender[1]) - access_granted = True - else: - access_granted = False - else: - if sender in operators: - access_granted = True - else: - access_granted = False - if access_granted and debug: - print sender+":"+prefix+"stop" - if random.randint(0,100) == 50: - conn.privmsg(channel,"Hammertime!") - else: - conn.privmsg(channel,"Shutting down.") - disconnects = 99999 - conn.quit() - return True - else: - conn.privmsg(channel,"You cannot command me") - return False - -def cocheck(command): - global influx - if influx.lower()[0:len(command)] == command: - return True - else: - return False -def target(who,how_long): - global conn,channel,CALL_OFF,logbans,debug - start = time.time() - conn.banon(targetchannel,who) - sleep(int(how_long)) - if CALL_OFF == False: - conn.banoff(targetchannel,who) - end = time.time() - if debug: - print "Banned",who,"For",how_long,"seconds" - if logbans: - with open(targetdirectory+"banlog/"+str(int(start))+"-"+str(int(end))+".txt","w") as tiedosto: - tiedosto.write("Start of ban on "+who+":"+str(int(start))) - tiedosto.write("\n") - tiedosto.write("End of ban on "+who+":"+str(int(end))) - tiedosto.write("\n") - tiedosto.write("In total:"+str(int(end-start))+"Seconds") - else: - CALL_OFF = False - pass -def replace(): - global usable,conn,fixing,curtime - waiting_time = 600 - if usable == True: - conn.privmsg(targetchannel,sender+": It needs no replacing.") - elif fixing == True: - if curtime == -999: - conn.privmsg(targetchannel,sender+": It is being replaced, No idea when it will be done") - else: - pass - nowtime = int(time.time()) - subt = curtime + waiting_time - nowtime - conn.privmsg(targetchannel,sender+": It is currently being replaced, "+str(subt)+" seconds to go") - else: - fixing = True - curtime = int(time.time()) - conn.privmsg(targetchannel,sender+": It will be fixed after "+str(waiting_time)+" seconds") - sleep(waiting_time) - if usable == False: - conn.privmsg(targetchannel,Name+"'s pneumatic smasher has now been fixed") - usable = True - fixing = False -def autoRecv(): - global disconnects,channel,conn,offensive_mode - for i in CORE_DATA.channels: - conn.join(i) - time.sleep(1) - count = pausecount = 0 - maximum = 250 - division_when_active = 10 - while True: - check = time.time() - if offensive_mode: - randnum = random.randint(0,maximum/division_when_active) - else: - randnum = random.randint(0,maximum) - if randnum == 5: - print "RANDOM SWITCH IS NOW "+str(not offensive_mode).upper() - offensive_mode = not offensive_mode - try: - conn.recv() - except: - conn.quit() - disconnects = 9999 - break - if check + 0.1 > time.time(): - #Whoa whoa hold on! - count += 1 - sleep(0.1) - else: - count = 0 - pausecount = 0 - if count > 9: - print "Suspecting a disconnect, pausing for 5 seconds" - sleep(5) - pausecount += 1 - if pausecount > 3: - print "I have been disconnected!" - conn.quit() - disconnects += 1 - if disconnects > 2: - pass - else: - sleep(2) - thread.start_new_thread(autoRecv,()) - break -if heavy_psyco and psyco_exists: - print "Doing a Heavy Psyco" - psyco.bind(cocheck) - psyco.bind(autoRecv) - psyco.bind(target) - psyco.bind(stop) - print "Heavy Psyco'd" -elif heavy_psyco and not psyco_exists: - print "Heavy psyco couldn't be done because Psyco does not exist" -try: - conn = irchat.IRC ( Network, Port, Name, "NT", "NT", "Trasen" ) -except socket.error: - print "Connection failed!" -else: - print Name+" is in!" -thread.start_new_thread ( autoRecv, () ) -sleep(1) -while True: - try: - data = conn.dismantle ( conn.retrieve() ) - except: - if debug: - print "Something odd detected with data" - data = None - if data: - if len(data[1]) < 1: - #print "Handshaking server." - #I won't really need the print command, as it spams. - if data[0][0:3] != "irc": - conn.handshake(data[0]) - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - else: - conn.send("PONG "+pongtarg) - print "Ponged" - pass - else: - if data [ 1 ] [ 0 ] == 'PRIVMSG': - #print data [ 0 ] + '->', data [ 1 ] - sender = data[0].split("!")[0] - truesender = sender - if shortform == True: - try: - sender = replacenames[truesender] - pass - except: - sender = Shortname.shortname(sender) - pass - pass - else: - try: - sender = replacenames[truesender] - pass - except: - pass - pass - if offensive_mode: - sender = "Meatbag" - pass - raw_sender = data[0] - influx = data[1][2] - if "[\\0x01]" in influx.lower() or "[\0x01]" in influx.lower(): - influx = influx.replace("[\\0x01]","") - influx = influx.replace("[\0x01]","") - - targetchannel = data[1][1] - if targetchannel == Name: - targetchannel = data[0].split("!")[0] - pass - backup = autodiscusscurtime - autodiscusscurtime = time.time() - connected = True - #FOR TRACKING SPEED - looptime = time.time() - if call_to_action == True: - if influx == finder: - conn.privmsg(targetchannel,"Then why... Nevermind, I order you to stop!") - conn.privmsg(origname,prefix+"stop") - time.sleep(4) - if origname in users: - conn.privmsg(origname,"!stop") - time.sleep(1) - Name = origname - conn.nick(Name) - duplicate_notify = False - call_to_action = False - else: - conn.privmsg(targetchannel,"YOU LIE! YOU ARE NOT A REAL "+origname+"!") - duplicate_notify = False - call_to_action = False - elif connected == True and len(Name.replace("V","")) != len(Name) and origname in users and duplicate_notify == True: - conn.privmsg(origname,"!stop") - call_to_action = False - duplicate_notify = False - time.sleep(6) - Name = origname - conn.nick(Name) - if origname in truesender and influx == prefix+"stop": - time.sleep(0.5) #A small delay - conn.privmsg(channel,"Shutting down.") - conn.quit() - disconnects = 99999 - break - if len(translateable) > 0 and enabled == True: - people = "-5|5|1-".join(users).lower() - if truesender.lower() in translateable: - if influx.isupper(): - conn.privmsg(targetchannel,"Translation: "+influx.capitalize().replace(" i "," I ")) - elif offensive_mode and True in map(lambda x: x in influx.lower().split(" "),["i","you","he","she","they","those","we","them"]+people.split("-5|5|1-")): - arg = influx.lower().replace(",","").replace(".","").replace("!","").replace("?","").split(" ") - bup = arg - for i in arg: - if i == "i" or i == "you" or i == "he" or i == "she": - arg[arg.index(i)] = "Meatbag" - elif i == "we" or i == "they" or i == "them" or i == "those": - arg[arg.index(i)] = "Meatbags" - elif i in people: - arg[arg.index(i)] = "Meatbag" - elif i == "am": - arg[arg.index(i)] = "is" - elif i == "everybody" or i == "everyone" or i == "all": - arg[arg.index(i)] = "every Meatbag" - if arg == bup: - pass - else: - conn.privmsg(targetchannel,"Translation: "+" ".join(arg)) - if enabled == False: - #FIRST QUIT COMMAND - if truesender in operators and targetchannel==channel:# or "skibiliano" in truesender.lower() and targetchannel==channel: - - if cocheck(prefix+"enable"): - enabled = True - if debug: - print truesender+":"+prefix+"enable" - elif cocheck(prefix+"stop"): -# if debug: -# print truesender+":"+prefix+"stop" -# if random.randint(0,100) == 50: -# conn.privmsg(channel,"Hammertime!") -# else: -# conn.privmsg(channel,"Shutting down.") -# disconnects = 99999 -# conn.quit() -# sleep(2) -# break - if targetchannel == channel and stop(truesender,debug): - break - else: - pass - elif cocheck(prefix+"suggest "): - arg = influx.lower()[8+len(prefix):] - if debug: - print truesender+":"+prefix+"suggest "+arg - with open(targetdirectory+"suggestions/suggestions_"+str(int(time.time()))+".txt","a") as tiedosto: - tiedosto.write(arg) - conn.privmsg(targetchannel,"Suggestion received") - elif cocheck( prefix+"help "): #Space in front of the ( to make sure that my command finder does not pick this up. - arg = " ".join(influx.split(" ")[1:]).lower() - if debug: - print truesender+":"+prefix+"help "+arg - try: - conn.privmsg(targetchannel,D_help.everything[arg]) - except: - try: - conn.privmsg(targetchannel,D_help.everything[arg.replace(prefix,"",1)]) - except: - conn.privmsg(targetchannel,"Sorry, can't help you with that") - elif cocheck(prefix+"help"): - #tar = targetchannel - if debug: - print truesender+":"+prefix+"help" - conn.privmsg(targetchannel,"All my commands are: "+reduce(lambda x,y:str(x)+"; "+str(y),commands)) - ### VERSION - elif influx.lower() == prefix+"version": - if debug: - print truesender+":"+prefix+"version" - conn.privmsg(targetchannel,Name+" "+pregen+" online at a %s Python %s.%s.%s, At your service." %(str(sys.platform),str(sys.version_info[0]),str(sys.version_info[1]),str(sys.version_info[2]))) - elif cocheck(prefix+"note ") and influx.count(" ") < 2: - arg = influx.lower()[len(prefix)+5:] - if debug: - print truesender+":"+prefix+"note "+arg - try: - a = arg[0] - except IndexError: - conn.privmsg(targetchannel,sender+" : Please specify a note") - else: - if arg[0] == "_": # Public / Restricted note - result = load(targetdirectory+"memos/"+arg+".note") - #_flare - if result == "ERROR ERROR ERROR ERR": - result = load(targetdirectory+"memos/"+arg+"_"+targetchannel.replace("#","")+".note") - #_flare_dnd - pass - else: - pass - else: - result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+".note") - #skibiliano_testnote - if result == "ERROR ERROR ERROR ERR": - result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+"_"+targetchannel.replace("#","")+".note") - #skibiliano_testnote_derp - pass - else: - pass - if result == "ERROR ERROR ERROR ERR": - conn.privmsg(targetchannel,sender+" : Note not found") - elif type(result) == list: - if "C" in result[0]: #Channel restriction, result[2] is the channel - try: - if targetchannel == result[2]: - conn.privmsg(targetchannel,sender+" : '"+result[1]+"'") - else: - conn.privmsg(targetchannel,sender+" : That note is channel restricted") - except: - conn.privmsg(targetchannel,sender+" : NOTE HAS INVALID RESTRICTION") - else: - conn.privmsg(targetchannel,sender+" : '"+result+"'") - elif influx.lower() == prefix+"notes": - if debug: - print truesender+":"+prefix+"notes" - arg = os.listdir(targetdirectory+"memos/") - arg2 = [] - arg3 = truesender.replace("|","_")+"_" - for i in arg: - if arg3 in i: - arg2.append(i.replace(arg3,"").replace(".note","")) - if len(arg2) == 1: - preprocess = " note: " - else: - preprocess = " notes: " - if len(arg2) == 0: - conn.privmsg(targetchannel,sender+" : You have no notes saved") - else: - conn.privmsg(targetchannel,sender+" : "+str(len(arg2))+preprocess+", ".join(arg2)) - elif cocheck(prefix+"note ") and influx.count(" ") > 1: - note_chanrestrict = None - note_public = None - try: - arg = influx.split(" ",2)[2] # Contents - arg4 = influx.split(" ")[1].lower() # Note name - if arg4[0:3] == "[c]": # or arg4[0:3] == "[p]": - note_chanrestrict = "c" in arg4[0:3] - #note_public = "p" in arg4[0:3] - arg4 = arg4[3:] - elif arg4[0:4] == "[cp]" or arg4[0:4] == "[pc]": - note_chanrestrict = True - note_public = True - arg4 = arg4[4:] - else: - pass - #print "Is note public? "+str(note_public) - #print "Is note chanrestricted? "+str(note_chanrestrict) - #print "What is the name? "+str(arg4) - if arg.lower() == "delete" and "\\" not in influx.lower() and "/" not in influx.lower(): - if note_public: - try: - if note_chanrestrict: - os.remove(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note") - else: - os.remove(targetdirectory+"memos/"+"_"+arg4+".note") - except: - conn.pivmsg(targetchannel,sender+" : Couldn't remove note") - else: - conn.privmsg(targetchannel,sender+" : Note removed") - pass - else: - try: - if note_chanrestrict: - os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note") - else: - os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note") - except: - conn.privmsg(targetchannel,sender+" : Couldn't remove note") - else: - conn.privmsg(targetchannel,sender+" : Note removed") - elif arg.lower() == "delete": - conn.privmsg(targetchannel,sender+" : That just doesn't work, we both know that.") - else: - try: - if note_public: - if note_chanrestrict: - save(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) - #print "Saved as note_public, note_chanrestrict" - else: - save(targetdirectory+"memos/"+"_"+arg4+".note",arg) - #print "Saved as note_public" - else: - if note_chanrestrict: - save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) - #print "Saved as note_chanrestrict" - else: - save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note",arg) - #print "Saved as normal" - except IOError: - conn.privmsg(targetchannel,sender+" : Please do not use special letters") - else: - conn.privmsg(targetchannel,sender+" : Note Saved!") - except: - conn.privmsg(targetchannel,sender+" : Something went horribly wrong.") - elif cocheck(prefix+"uptime"): - arg1 = uptime_start - arg2 = time.time() - arg1 = arg2 - arg1 - arg2 = arg1 - if arg1 < 60: - conn.privmsg(targetchannel,sender+" : I have been up for "+str(round(arg1,2))+" Seconds") - elif arg1 < 3600: - arg1 = divmod(arg1,60) - arg = " Minute" if int(arg1[0]) == 1 else " Minutes" - conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg+" and "+str(round(arg1[1],2))+" Seconds") - elif arg1 <= 86400: - arg1 = divmod(arg1,3600) - arg3 = " Hour" if int(arg1[0]) == 1 else " Hours" - arg2 = divmod(arg1[1],60) - arg = " Minute" if int(arg2[0]) == 1 else " Minutes" - conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg3+", "+str(int(arg2[0]))+arg+" and "+str(round(arg2[1],2))+" Seconds") - elif arg1 > 86400: - arg1 = divmod(arg1,86400) - arg2 = divmod(arg1[1],3600) - arg3 = divmod(arg2[1],60) - arg4 = " Day" if int(arg1[0]) == 1 else " Days" - arg5 = " Hour" if int(arg2[0]) == 1 else " Hours" - arg6 = " Minute" if int(arg3[0]) == 1 else " Minutes" - conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg4+", "+str(int(arg2[0]))+arg5+", "+str(int(arg3[0]))+arg6+" and "+str(round(arg3[1],2))+" Seconds") - elif cocheck(prefix+"purgemessages"): - count = 0 - for i,a in tell_list.items(): - for b in a: - if "||From: "+truesender in b: - count += 1 - del(tell_list[i][tell_list[i].index(b)]) - conn.privmsg(targetchannel, sender+" : All your "+str(count)+" messages have been purged") - elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "tell" in (influx.lower().split(" ")+[""])[1]: - arg = influx.lower().split(" ") - equalarg = influx.split(" ") - next_one = False - count = 0 - spot = 0 - for i in arg: - count += 1 - if "tell" in i.lower(): - next_one = True - elif next_one == True: - next_one = i.lower() - spot = count - break - else: - pass - if next_one != True and next_one != False: - #if ("^\^".join(tell_list.values())).count(truesender) >= offline_message_limit: - if str(tell_list.values()).count("||From: "+truesender) >= offline_message_limit: - conn.privmsg(targetchannel,sender+" : Limit of "+str(offline_message_limit)+" reached! Use !purgemessages if you want to get rid of them!") - else: - try: - tell_list[next_one].append((" ".join(equalarg[spot:]))+" ||From: "+truesender) - except: - tell_list[next_one] = [(" ".join(equalarg[spot:]))+" ||From: "+truesender] - conn.privmsg(targetchannel,"Sending a message to "+next_one+" when they arrive.") - # < This part has to be within subsidiaries of the bot, and must not be modified, intentionally hidden or deleted. - elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "who created you" in influx.lower(): - conn.privmsg(targetchannel, "I was created by Skibiliano.") - # The part ends here > - elif parse_xkcd and "xkcd.com/" in influx.lower(): - if influx.lower()[0:3] == "www": - data = "http://"+influx - elif influx.lower()[0:3] == "xkc": - data = "http://"+influx - else: - data = influx - data = data.split(" ") - for i in data: - if "http://" in i and "xkcd" in i: - churn = xkcdparser.xkcd(i) - if churn == "NOTHING": - pass - else: - conn.privmsg(targetchannel,sender+" : XKCD - "+churn) - break - else: - pass - elif automatic_youtube_reveal and "youtube.com/watch?v=" in influx.lower(): - temporal_list2 = [] - temporal_data = influx.split(" ") - temporal_list = [] - for block in temporal_data: - if "youtube.com/watch?v=" in block: - temporal_list.append(block) - for temdata in temporal_list: - - if temdata[0:3] == "you": - temdata = "http://www."+temdata - elif temdata[0:3] == "www": - temdata = "http://"+temdata - elif temdata[0:4] == "http": - pass - #Obscure ones - elif temdata[0:3] == "ww.": - temdata = "http://w"+temdata - elif temdata[0:3] == "w.y": - temdata = "http://ww"+temdata - elif temdata[0:3] == ".yo": - temdata = "http://www"+temdata - elif temdata[0:3] == "ttp": - temdata = "h"+temdata - elif temdata[0:3] == "tp:": - temdata = "ht"+temdata - elif temdata[0:3] == "p:/" or temdata[0:3] == "p:\\": - temdata = "htt"+temdata - elif temdata[0:3] == "://" or temdata[0:3] == ":\\\\": - temdata = "http"+temdata - elif temdata[0:2] == "//" or temdata[0:2] == "\\\\": - if temdata[2] == "y": - temdata = "http://www."+temdata[2:] - elif temdata[2] == "w": - temdata = "http:"+temdata - else: - pass - if debug: - print truesender+":"+temdata - arg = temdata - check = temdata.lower() - if check[0:5] == "https": - if len(temporal_list) == 1: - conn.privmsg(targetchannel,sender+" :Secure Youtube does NOT exist") - break - else: - temporal_list2.append("Secure Youtube does NOT exist") - break - else: - if cache_youtube_links == True: - result = YTCV2(arg) - else: - result = YTCV2(arg,0) - if type(result) == str: - ### To remove =" - if result[0:4] == 'nt="': - result = result[4:] - pass - elif result[0:2] == '="': - result = result[2:] - pass - else: - pass - if """ in result: - result.replace(""",'"') - if len(temporal_list) == 1: - conn.privmsg(targetchannel,sender+" : "+result) - break - else: - temporal_list2.append(result) - else: - if len(temporal_list) == 1: - conn.privmsg(targetchannel,sender+" : The video does not exist") - break - else: - temporal_list2.append("The video does not exist") - if len(temporal_list) == 1: - pass - else: - conn.privmsg(targetchannel,sender+" : "+str(reduce(lambda x,y: x+" :-And-: "+y,temporal_list2))) - elif RegExpCheckerForWebPages("((http://)|(https://))|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,1): - arg2 = RegExpCheckerForWebPages("(http://)|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,0) - if arg2 == 404: - pass - else: - if arg2[:7] == "http://": - pass - elif arg2[:4] == "www.": - arg2 = "http://"+arg2 - else: - arg2 = "http://"+arg2 - try: - arg = Whoopshopchecker.TitleCheck(arg2) - if len(arg2) == 0: - pass - else: - conn.privmsg(targetchannel,sender+" : "+arg) - except: - #conn.privmsg(targetchannel,sender+" : An odd error occurred") - pass - elif respond_of_course and "take over the" in influx.lower() or respond_of_course and "conquer the" in influx.lower(): - if debug: - print truesender+"::"+influx - conn.privmsg(targetchannel,"Of course!") - elif respond_khan and "khan" in influx.lower(): - if respond_khan: - if debug: - print truesender+"::"+influx - if "khan " in influx.lower(): - conn.privmsg(targetchannel,"KHAAAAAAN!") - elif " khan" in influx.lower(): - conn.privmsg(targetchannel,"KHAAAAAN!") - elif influx.lower() == "khan": - conn.privmsg(targetchannel,"KHAAAAAAAAAN!") - elif influx.lower() == "khan?": - conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAN!") - elif influx.lower() == "khan!": - conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAAAAAAN!") - elif respond_khan and influx.lower().count("k") + influx.lower().count("h") + influx.lower().count("a") + influx.lower().count("n") + influx.lower().count("!") + influx.lower().count("?") == len(influx): - if "k" in influx.lower() and "h" in influx.lower() and "a" in influx.lower() and "n" in influx.lower(): - if debug: - print truesender+"::"+influx - conn.privmsg(targetchannel,"KHAAAAN!") - elif influx.split(" ")[0].lower() in ["thanks","danke","tack"] and len(influx.split(" ")) > 1 and influx.split(" ")[1].lower().replace("!","").replace("?","").replace(".","").replace(",","") in SName+[lowname]: - conn.privmsg(targetchannel,"No problem %s" %(sender)) - elif "happy birthday" in influx.lower() and birthday_announced == time.gmtime(time.time())[0]: - conn.privmsg(targetchannel,sender+" : Thanks :)") - elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("!","").replace("?","") in SName+[lowname] and "call me" in influx.lower(): - if allow_callnames == True: - arg = influx.split(" ") - arg2 = False - arg3 = [] - for i in arg: - if arg2 == True: - arg3.append(i) - elif i.lower() == "me": - arg2 = True - arg3 = " ".join(arg3) - truesender_lower = truesender.lower() - arg3_lower = arg3.lower() - tell_checker = Namecheck.Namecheck(arg3_lower,users,truesender) - for name in replacenames.values(): - if arg3_lower == name.lower(): - tell_checker = True - break - else: - pass - if tell_checker == True: - conn.privmsg(targetchannel,sender+" : I can't call you that, I know someone else by that name") - elif len(arg3) > call_me_max_length: - conn.privmsg(targetchannel,sender+" : I cannot call you that, Too long of a name.") - pass - else: - replacenames[truesender] = arg3 - with open("replacenames.cache","w") as pickle_save: - pickle.dump(replacenames,pickle_save) - conn.privmsg(targetchannel,sender+" : Calling you "+arg3+" From now on") - else: - conn.privmsg(targetchannel,sender+" : Sorry, I am not allowed to do that.") - elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("?","").replace("!","") in SName+[lowname] and "your birthday" in influx.lower() and "is your" in influx.lower(): - conn.privmsg(targetchannel,sender+" : My birthday is on the 15th day of December.") - elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and "version" in influx.replace("?","").replace("!","").lower().split(" "): - if debug == True: - print truesender+"::%s Version" %(Name) - conn.privmsg(targetchannel,sender+", My version is "+pregen) - elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and influx.lower().count(" or ") > 0 and len(influx.split(" ")[1:]) <= influx.lower().count("or") * 3: - cut_down = influx.lower().split(" ") - arg = [] - count = -1 - for i in cut_down: - count += 1 - try: - if cut_down[count+1] == "or": - arg.append(i) - - except: - pass - try: - if i not in arg and cut_down[count-1] == "or": - arg.append(i) - except: - pass - try: - conn.privmsg(targetchannel,random.choice(arg).capitalize().replace("?","").replace("!","")) - except IndexError: - # arg is empty, whORe etc. - pass - elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 and "who started you" in influx.lower() or \ - influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and "who started you" in influx.lower(): - conn.privmsg(targetchannel,sender+" : I was started by %s"%(os.getenv("USER"))+" on "+time.strftime("%d.%m.%Y at %H:%M:%S",time.gmtime(uptime_start))) - elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 or \ - influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and influx.count(" ") > 1: - dice = random.randint(0,1) - if dice == 0: - conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) - else: - if highlights: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) - else: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) - elif influx.lower()[0:len(Name)] == lowname and not influx.lower()[len(Name):].isalpha() or \ - influx.split(" ")[0].lower().replace(",","") in SName and not influx.lower()[len(influx.split(" ")[0].lower()):].isalpha(): - conn.privmsg(targetchannel, random.choice(["Yea?","I'm here","Ya?","Yah?","Hm?","What?","Mmhm, what?","?","What now?","How may I assist?"])) - comboer = truesender - comboer_time = time.time() - elif influx.lower()[-1] == "?" and comboer == truesender and looptime - discard_combo_messages_time < comboer_time: - comboer = "" - dice = random.randint(0,1) - if dice == 0: - conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) - else: - if highlights: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) - else: - conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) - - elif influx.lower() == prefix+"tm": - if truesender in operators and targetchannel==channel: - marakov = not marakov - conn.privmsg(targetchannel,sender+" : Marakov Output is now "+str(marakov)) - else: - conn.privmsg(targetchannel,sender+" : I can't let you access that") - elif personality_greeter == True and True in map(lambda x: x in influx.lower(),["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"]): - if comboer != "" and looptime - discard_combo_messages_time > comboer_time: - combo_check = sbna(["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan","all night"], #ONLY ONE OF THESE - ["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE - influx.lower()) - else: - combo_check = sbna(SName+[lowname, - #lowname+".",lowname+"!",lowname+"?", - "everybody", - #"everybody!","everybody?", - "everyone", - #"everyone!","everyone?", - "all", - #"all!","all?" - "all night", - ], #ONLY ONE OF THESE - ["greetings","afternoon","hi", - #"hi,", - "hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE - influx.lower().replace(",","").replace(".","").replace("!","")) - if combo_check: - combo_check = False - comboer = "" - if "evening" in influx.lower() and "all" in influx.lower() and len(influx.lower().split(" ")) > 3: - pass - elif truesender not in operators: - if debug: - print truesender+"::"+influx - dice = random.randint(0,19) - if dice == 0: - conn.privmsg(targetchannel,"Well hello to you too "+sender) - elif dice == 1: - if optimize_greeting == False: - hours = time.strftime("%H") - #time.strftime("%H:%M:%S") == 12:28:41 - hours = int(hours) - if hours in xrange(0,12): - conn.privmsg(targetchannel,"Good Morning "+sender) - elif hours in xrange(12,15): - conn.privmsg(targetchannel,"Good Afternoon "+sender) - elif hours in xrange(15,20): - conn.privmsg(targetchannel,"Good Evening "+sender) - else: - conn.privmsg(targetchannel,"Good Night "+sender) - else: - hours = time.strftime("%H") - hours = int(hours) - if hours in morning: - conn.privmsg(targetchannel,"Good Morning "+sender) - elif hours in afternoon: - conn.privmsg(targetchannel,"Good Afternoon "+sender) - elif hours in evening: - conn.privmsg(targetchannel,"Good Evening "+sender) - else: - conn.privmsg(targetchannel,"Good Night "+sender) - elif dice == 2: - conn.privmsg(targetchannel,"Hello!") - elif dice == 3: - conn.privmsg(targetchannel,"Hey "+sender) - elif dice == 4: - conn.privmsg(targetchannel,"Hi "+sender) - elif dice == 5: - conn.privmsg(targetchannel,"Hello "+sender) - elif dice == 6: - conn.privmsg(targetchannel,"Yo "+sender) - elif dice == 7: - conn.privmsg(targetchannel,"Greetings "+sender) - elif dice == 8: - conn.privmsg(targetchannel,"Hi") - elif dice == 9: - conn.privmsg(targetchannel,"Hi!") - elif dice == 10: - conn.privmsg(targetchannel,"Yo") - elif dice == 11: - conn.privmsg(targetchannel,"Yo!") - elif dice == 12: - conn.privmsg(targetchannel,"Heya") - elif dice == 13: - conn.privmsg(targetchannel,"Hello there!") - elif dice == 14: # Richard - conn.privmsg(targetchannel,"Statement: Greetings meatbag") - elif dice == 15: # Richard - hours = int(time.strftime("%H")) - if hours in xrange(5,12): - conn.privmsg(targetchannel,"What are you doing talking at this time of the morning?") - elif hours in xrange(12,15): - conn.privmsg(targetchannel,"What are you doing talking at this time of the day?") - elif hours in xrange(15,22): - conn.privmsg(targetchannel,"What are you doing talking at this time of the evening?") - else: - conn.privmsg(targetchannel,"What are you doing talking at this time of the night?") - elif dice == 16: # Richard - conn.privmsg(targetchannel,"Oh, you're still alive I see.") - elif dice == 17: - conn.privmsg(targetchannel,"Heya "+sender) - elif dice == 18 and time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15: - conn.privmsg(targetchannel,"Hello! It's my birthday!") - else: - conn.privmsg(targetchannel,"Hiya "+sender) - secdice = random.randint(0,10) - if time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15 and birthday_announced < time.gmtime(time.time())[0]: - birthday_announced = time.gmtime(time.time())[0] - conn.privmsg(channel,"Hey everybody! I just noticed it's my birthday!") - time.sleep(0.5) - tag = random.choice(["birthday","robot+birthday","happy+birthday+robot"]) - arg1 = urllib2.urlopen("http://www.youtube.com/results?search_query=%s&page=&utm_source=opensearch"%tag) - arg1 = arg1.read().split("\n") - arg2 = [] - for i in arg1: - if "watch?v=" in i: - arg2.append(i) - arg3 = random.choice(arg2) - - conn.privmsg(channel,"Here's a video of '%s' which I found! %s (%s)"%(tag.replace("+"," "),"http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20],YTCV2("http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20]))) - if truesender.lower() in tell_list.keys(): - try: - conn.privmsg(channel, "Also, "+truesender+" : "+tell_list[truesender.lower()][0]) - del(tell_list[truesender.lower()][0]) - except: - pass - else: - dice = random.randint(0,1) - if dice == 0: - conn.privmsg(targetchannel,"Greetings Master "+sender) - elif dice == 1: - conn.privmsg(targetchannel,"My deepest greetings belong to you, Master "+sender) - ### IMPORTANT ### - elif influx == "☺VERSION☺": - conn.notice(truesender,"\001VERSION nanotrasen:2:Python 2.6\001") - elif marakov and influx.lower() == prefix+"marakov": - arg = Marakov_Chain.form_sentence() - if len(arg) < 5: - conn.privmsg(targetchannel,sender+" : Not enough words harvested") - else: - conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg).capitalize())) - elif marakov and cocheck( prefix+ "marakov"): - try: - arg = influx.split(" ")[1].lower() - except: - conn.privmsg(targetchannel,sender+" : Please input a valid second argument") - else: - arg2 = Marakov_Chain.form_sentence(arg) - if len(arg2) < 5: - conn.privmsg(targetchannel,sender+" : Not enough words harvested for a sentence starting with %s" %(arg)) - else: - conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg2).capitalize())) - else: - Marakov_Chain.give_data(influx) - autodiscusscurtime = backup - if time.time() - looptime == 0: - pass - else: - print "Took",time.time()-looptime,"Seconds to finish loop" - - elif data [ 1 ] [ 0 ] == '353': - if connected == False: - connected = True - users = map(lambda x: x[1:] if x[0] == "+" or x[0] == "@" else x,data[1][4].split(" ")) - print "There are",len(users),"Users on",channel - operators = [] - for potential_operator in data[1][4].split(" "): - if potential_operator[0] == "@": - operators.append(potential_operator[1:]) - elif potential_operator[0] == "%": - halfoperators.append(potential_operator[1:]) - - elif data[1][0] == "QUIT": - sender = data[0].split("!")[0] - print sender+" Has now left the server" - try: - users.remove(sender) - try: - operators.remove(sender) - except ValueError: - pass - try: - halfoperators.remove(sender) - except ValueError: - pass - except ValueError: - pass - elif data[1][0] == "PART": - sender = data[0].split("!")[0] - targetchannel = data[1][1] - print sender+" Has now parted from the channel" - try: - users.remove(sender) - try: - operators.remove(sender) - except ValueError: - pass - try: - halfoperators.remove(sender) - except ValueError: - pass - except ValueError: - pass - elif data[1][0] == "JOIN": - sender = data[0].split("!")[0] - targetchannel = data[1][1] - if sender.lower() in tell_list.keys(): - try: - conn.privmsg(targetchannel, sender+" : "+" | ".join(tell_list[sender.lower()])) - del(tell_list[sender.lower()]) - except: - pass - for useri,nicki in replacenames.items(): - checkers = Namecheck.Namecheck_dict(sender.lower(),replacenames) - if checkers[0]: - try: - if checkers[0].lower() == sender: - pass - else: - conn.privmsg(targetchannel,checkers[1]+" : I have detected a collision with a name I call you and %s who joined" %(sender)) - del(replacenames[checkers[1]]) - with open("replacenames.cache","w") as pickle_save: - pickle.dump(replacenames,pickle_save) - except AttributeError: - #conn.privmsg(channel,"NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender)) - print "NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender) - break - print sender+" Has now joined" - users.append(sender) - ##### - if ".fi" in data[0] and sender.lower() == "skibiliano": - operators.append(sender) - if sender.lower() not in peopleheknows[0]: - if data[0].split("!")[1] in peopleheknows[1]: - appendion = "...you do seem familiar however" - else: - appendion = "" - if data[1][1].lower() == channel or data[1][1].lower() == channel[1:]: - conn.privmsg(data[1][1],CORE_DATA.greeting.replace("USER",sender)+" "+appendion) - else: - conn.privmsg(data[1][1],"Hello! Haven't seen you here before! Happy to meet you! %s" %(appendion)) - peopleheknows[0].append(sender.lower()) - peopleheknows[1].append(data[0].split("!")[1]) - with open("peopleheknows.cache","w") as peoplehecache: - pickle.dump(peopleheknows,peoplehecache) - - elif data[1][0] == "MODE" and data[1][2] == "+o": - sender = data[1][3] - targetchannel = data[1][1] - if targetchannel == channel: - print sender+" Is now an operator on the main channel" - operators.append(sender) - else: - print sender+" Is now an operator" - elif data[1][0] == "MODE" and data[1][2] == "-o": - sender = data[1][3] - targetchannel = data[1][1] - if targetchannel == channel: - print sender+" Is no longer an operator on the main channel" - else: - print sender+" Is no longer an operator" - try: - operators.remove(sender) - except ValueError: - pass - elif data[1][0] == "MODE" and data[1][2] == "+h": - sender = data[1][3] - print sender+" Is now an half operator" - halfoperators.append(sender) - elif data[1][0] == "MODE" and data[1][2] == "-h": - try: - halfoperators.remove(sender) - except ValueError: - pass - elif data[1][0] == "MODE" and data[1][1] == Name: - print "My mode is",data[1][2] - elif data[1][0] == "MODE" and data[1][1] != Name: - try: - sender = data[1][3] - print sender,"Was modified",data[1][2] - except IndexError: - print "SENDER RETRIEVAL FAILED:"+str(data) - elif data[1][0] == "KICK" and data[1][2] == Name: - disconnects = 99999 - print "I have been kicked! Disconnecting entirely!" - conn.quit() - elif data[1][0] == "KICK": - # data[1][0] = Kick, 1 = Channel, 2 = Who, 3 = Who(?) - print data[1][2]+" got kicked!" - elif data[1][0] == "451" and data[1][2] == "You have not registered": - print Name+" hasn't been registered" - elif data[1][0] == "NOTICE": - sender = data[0].split("!")[0] - print "NOTICE (%s): %s" %(sender,data[1][2]) - pongtarget = sender - elif data[1][0] == "NICK": - origname = data[0].split("!")[0] - newname = data[1][1] - print origname,"Is now",newname - if newname.lower() in tell_list.keys(): - try: - conn.privmsg(channel, newname+" : "+tell_list[newname.lower()][0]) - del(tell_list[newname.lower()][0]) - except: - pass - try: - users.remove(origname) - except ValueError: - pass - else: - users.append(newname) - try: - operators.remove(origname) - except ValueError: - pass - else: - operators.append(newname) - try: - halfoperators.remove(origname) - except ValueError: - pass - else: - halfoperators.append(newname) - - elif data[1][0] == "001": - # Skibot is welcomed to the Network - pass - elif data[1][0] == "002": - # Your host is... - pass - elif data[1][0] == "003": - #Server was created... - pass - elif data[1][0] == "004": - #Weird hex? - pass - elif data[1][0] == "005": - #Settings like NICKLEN and so on. - pass - elif data[1][0] == "250": - #data[1][2] is - #"Highest connection count: 1411 (1410 clients) - #(81411 connections received)" - pass - elif data[1][0] == "251": - #There are 23 users and 2491 invisible on 10 servers - pass - elif data[1][0] == "252": - #IRC Operators online - #data[1][2] - print data[1][2],"Irc operators online" - pass - elif data[1][0] == "253": - # ['253', 'Skibot_V4', '1', 'unknown connection(s)'] - print data[1][2],"Unknown connection(s)" - pass - elif data[1][0] == "254": - #1391 channels formed - pass - elif data[1][0] == "255": - #I have 406 clients and 2 servers - pass - elif data[1][0] == "265": - #data[1][2] current local users - #data[1][3] at max - try: - print "Current local users:", data[1][2],"/",data[1][3] - except IndexError: - print "Couldn't retrieve local users" - pass - elif data[1][0] == "266": - #data[1][2] current global users - #data[1][3] at max - try: - print "Current global users:", data[1][2],"/",data[1][3] - except IndexError: - print "Couldn't retrieve global users" - pass - elif data[1][0] == "315": - #End of /who list - pass - elif data[1][0] == "332": - # Topic of channel - topic = data[1][3] - pass - elif data[1][0] == "333": - # *Shrug* - pass - elif data[1][0] == "352": - #WHO command - - if len(targetlist) > 0: - if targetlist[0][0].lower() in data[1][6].lower(): - thread.start_new_thread(target,("*!*@"+data[1][4],targetlist[0][1])) - print "Created a thread with", "*!*@"+data[1][4],targetlist[0][1] - targetlist.pop(0) - else: - print targetlist[0][0].lower(), "isn't equal to?", data[1][6].lower() - print targetlist - - elif data[1][0] == "366": - # End of USERS - pass - elif data[1][0] == "372": - # Server information - pass - elif data[1][0] == "375": - # Message of the day - pass - elif data[1][0] == "376": - # End of motd - pass - elif data[1][0] == "401": - # ('network', ['401','Botname','Channel / Nick','No such nick/channel']) - print data[1][2] + " Channel does not exist" - pass - elif data[1][0] == "439": - # ('irc.rizon.no', ['439', '*', 'Please wait while we process your connection.']) - pongtarg = data[0][0] - elif data[1][0] == "477": - # You need to be identified - #TAG - conn.privmsg("nickserv","identify %s"%CORE_DATA.le_pass) - time.sleep(0.5) - conn.join(data[1][2]) - #('network', ['477', 'botname', '#channel', 'Cannot join channel (+r) - you need to be identified with services']) - - elif data[1][0] == "433": - # Skibot name already exists. - print Name+" name already exists." - Name += "_"+version - print "New name:",Name - duplicate_notify = True - conn = irchat.IRC ( Network, Port, Name, "NT_"+version, "NT_"+version, "Trasen_"+version ) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - elif data[1][0] == "482": - sleep(0.05) - conn.privmsg(targetchannel,"Nevermind that, I am not an operator") - CALL_OFF = True - elif data[1] == ["too","fast,","throttled."]: - print "Reconnected too fast." - print "Halting for 2 seconds" - sleep(2) - elif data[1][0] == "Link": - if data[0] == "Closing": - print "Link was closed" - connected = False -# conn.quit() -# break - else: - print data - print data[1][0] - pass - else: - if disconnects > 9000: #IT'S OVER NINE THOUSAAAAND! - break - else: #WHAT NINE THOUSAND? THERE'S NO WAY THAT CAN BE RIGHT - sleep(responsiveness_delay) #WAIT A WHILE AND CHECK AGAIN! - try: - if not connected: - #print pongtarget - #print conn.addressquery() - conn.privmsg(pongtarget,"Pong") - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - print "Attempted to join" - connected = True - except ValueError: - try: - conn.privmsg(conn.addressquery()[0],"Pong") - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - print "Attempted to join the second time" - connected = True - except ValueError: - print "Both methods failed" - except AttributeError: - print "Conn is not established correctly" - except NameError: - print "Pongtarget isn't yet established" - try: - conn.privmsg(conn.addressquery()[0],"Pong") - sleep(1) - for i in CORE_DATA.channels: - conn.join(i) - sleep(0.5) - print "Attempted to join the second time" - connected = True - except: - print "Both methods failed" +# -*- coding: utf-8 -*- +# This script is shared under the +# Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0) +# Added clause to Attribution: +# - You may not remove or hide the ' who created you?' functionality +# and you may not modify the name given in the response. + + +#CREDITS +# Author: Skibiliano +# "Foreign" Modules: +# Psyco 2.0 / Psyco 1.6 +################# DEBUG STUFF ##################### +import sys +import CORE_DATA + +import urllib2 + + +import socket +import irchat + + +################## END OF DEBUG STUFF ############## +# +# PSYCO +write_to_a_file = False #Only affects psyco +write_youtube_to_file = True #True = YTCV4 will load, false = YTCV3 will load +try: + import psyco +except ImportError: + print 'Psyco not installed, the program will just run slower' + psyco_exists = False + if write_to_a_file: + try: + tiedosto = open("psycodownload.txt","r") + except: + with open("psycodownload.txt","w") as tiedosto: + tiedosto.write("http://www.voidspace.org.uk/python/modules.shtml#psyco") + tiedosto.write("\nhttp://psyco.sourceforge.net/download.html") + print "Check psycodownload.txt for a link" + else: + print "For god's sake, open psycodownload.txt" + tiedosto.close() + else: + print "WINDOWS: http://www.voidspace.org.uk/python/modules.shtml#psyco" + print "LINUX: http://psyco.sourceforge.net/download.html" +else: + psyco_exists = True + +# +import C_rtd # rtd +import C_srtd # srtd +import C_makequote +import C_maths +import C_eightball #eightball +import C_sarcasticball +import C_heaortai # heaortai +import C_rot13 # rot13 +import D_help # everything +import pickle +import Timeconverter +import xkcdparser +import time +import re +import Marakov_Chain +import Namecheck # Namecheck +import Weather +#SLOWER THAN RANDOM.CHOICE +import thread +import random +import Shortname # shortname +import subprocess +import some_but_not_all_2 #sbna2 (sbna) +#import YTCv3 # YTCV2 OUTDATED +import os +import save_load # save, load +from some_but_not_all_2 import sbna2 as sbna +from time import sleep +from random import choice as fsample +from C_rtd import rtd +from C_heaortai import heaortai +from C_srtd import srtd +if write_youtube_to_file: + from YTCv4 import YTCV4 as YTCV2 +else: + from YTCv3 import YTCV2 #Downgraded version supports Cache disabling, but is slower +from save_load import save,load +if psyco_exists: + def psyco_bond(func): + psyco.bind(func) + return func.__name__+" Psycofied" + for a in [rtd,srtd,C_heaortai.heaortai,sbna,YTCV2,fsample,C_rot13.rot13,C_eightball.eightball,fsample, + C_eightball.eightball,C_sarcasticball.sarcasticball,Marakov_Chain.form_sentence,Marakov_Chain.give_data]: + print psyco_bond(a) + +global dictionary +global Name,SName +global allow_callnames,offline_messages,hasnotasked,shortform +## For autoRecv() +global disconnects,channel,conn +## For stop() +global operators +## For replace() +global usable,fixing,curtime +## For target() +global CALL_OFF,logbans +## For check() +global influx +###### +autodiscusscurtime = 0 +conn = 0 +curtime = -999 +dance_flood_time = 10 +disconnects = 0 +responsiveness_delay = 0.5 #500 millisecond delay if no message +trackdance = 0 +discard_combo_messages_time = 1 #They are discarded after 1 second. +uptime_start = time.time() +# - - - - - +#### +aggressive_pinging = True # Bring the hammer on ping timeouts +aggressive_pinging_delay = 150 # How often to send a ping +aggressive_pinging_refresh = 2.5 # How long is the sleep between checks +#### +allow_callnames = True #Disables NT, call if the variable is False +automatic_youtube_reveal = True +birthday_announced = 0 #Will be the year when it was announced +call_to_action = False +call_me_max_length = 20 +CALL_OFF = False +connected = False +dance_enabled = True +comboer = "" +comboer_time = 0 +directories = ["fmlquotes","Marakov","memos","suggestions", + "userquotes","banlog","YTCache","xkcdcache"] #These will be created if they do not exist +debug = True +duplicate_notify = False +enabled = True +fixing = False +fml_usable = True +hasnotasked = True +highlights = False +logbans = True +maths_usable = True +marakov = True +nudgeable = True +offensive_mode = False +offline_messages = True +offline_message_limit = 5 # per user +optimize_fml = True # -CPU usage +Memory usage when enabled. +optimize_greeting = True # +Startup time +Memory usage -CPU usage when enabled +heavy_psyco = True # +Memory +Startup time -CPU usage -CPU time +cache_youtube_links = True +personality_greeter = True +respond_of_course = True #Responds with "Of course!" +respond_khan = False #KHAAAAAAAAN! +silent_duplicate_takedown = True +showquotemakers = False +shortform = True +usable = True +use_sname = True +parse_xkcd = True + +# - - - - - +Name = CORE_DATA.Name +SName = CORE_DATA.SName +origname = Name # Do not edit! +lowname = Name.lower() +greeting = CORE_DATA.greeting +targetdirectory = CORE_DATA.directory +version = CORE_DATA.version +Network = CORE_DATA.Network +channel = CORE_DATA.channel +prefix = CORE_DATA.prefix +Port = CORE_DATA.Port +# - - - - - +pregen = CORE_DATA.version +influx = "" +users = [] +translateable = [] +targetlist = [] +operators = [] +halfoperators = [] +items = [] +tell_list = {} +# - - - - - Logical changes to variables +if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: + nudgeable = False +try: + with open("replacenames.cache","r") as tiedosto: + replacenames = pickle.load(tiedosto) + for i in replacenames.values(): + if len(i) > call_me_max_length: + replacenames[replacenames.keys()[replacenames.values().index(i)]] = i[:call_me_max_length] + with open("replacenames.cache","w") as tiedosto: + pickle.dump(replacenames,tiedosto) + if "[\0x01]" in i.lower() or "[\\0x01]" in i.lower(): + i = i.replace("[\0x01]","") + i = i.replace("[\0X01]","") + i = i.replace("[\\0x01]","") + i = i.replace("[\\0X01]","") + print "NAME CORRECTED" +except IOError: #File not found + replacenames = {} +except EOFError: #Cache corrupt + replacenames = {} + print "replacenames.cache is corrupt and couldn't be loaded." +try: + with open("peopleheknows.cache","r") as tiedosto: + peopleheknows = pickle.load(tiedosto) +except IOError: + peopleheknows = [[],[]] + with open("peopleheknows.cache","w") as tiedosto: + pass +except EOFError: + peopleheknows = [[],[]] + print "peopleheknows.cache is corrupt and couldn't be loaded." +dictionary = {1:"1 - Crit. Fail", 2:"2 - Failure", + 3:"3 - Partial Success", 4:"4 - Success", + 5:"5 - Perfect", 6:"6 - Overkill"} +alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] +nonhighlight_names = ["Jesus","Elvis","HAL 9000","Dave","Pie","Elf","Traitor", + "AI","Syndicate Agent","Investigator", + "Detective","Head of Personnel","HAL 9001", + "Head of Research","Head of Security", + "Captain","Janitor","Research Director", + "Quartermaster","Toxin Researcher", + "Revolutionary","Santa", "Pizza", + "Threetoe","The Red Spy","The Blue Spy", #LASD + "God","Toady","Darth Vader","Luke Skywalker", + "Homer Simpson","Hamburger","Cartman", + "XKCD","FloorBot","ThunderBorg","Iron Giant", + "Spirit of Fire", "Demon","Kyle"] +def RegExpCheckerForWebPages(regexp,data,mode): + if " ai." in data.lower() or "ai. " in data.lower(): + return False + for i in data.split(" "): + a = re.match(regexp,i) + try: + a.group(0) + except: + continue + else: + if mode == 0: + return i + else: + return True + if mode == 0: + return 404 + else: + return False +if nudgeable: + try: + nudgeexists = open("nudge.py","r") + except IOError: + nudgeexists = False #No usage asof 12.2.2010. + else: + if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: + pass + else: + + def nudgereceiver(): + import pickle + global conn,channel + port = 45678 + backlog = 5 + size = 1024 + host = "" # == localhost + s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + s.bind((host,port)) + s.listen(backlog) + while True: + client,address = s.accept() #Address == "?.?.?.?" + data = client.recv(size) + client.close() #Throw the bum out! + truedata = pickle.loads(data) + if truedata["ip"][0] == "#": + conn.privmsg(truedata["ip"],"PRIVATE ANNOUNCEMENT : "+str(" ".join(truedata["data"]))) + else: + conn.privmsg(channel,"AUTOMATIC ANNOUNCEMENT : "+str(truedata["ip"])+" | "+str(" ".join(truedata["data"]))) + thread.start_new_thread(nudgereceiver,()) +tiedosto = open(targetdirectory+"NanoTrasenBot.py","r") +commands = [] +fragment = "if cocheck" +fragment2 = '(prefix+"' +compiled = fragment + fragment2 +fragment = "if influx.lower()" +fragment2 = ' == prefix+"' +compiled2 = fragment + fragment2 +for line in tiedosto.readlines(): + if compiled in line: + a = line.find('"')+1 + b = line.find('"',a) + if prefix+line[a:b] not in commands: + commands.append(prefix+line[a:b]) + elif compiled2 in line: + a = line.find('"')+1 + b = line.find('"',a) + arg = prefix+line[a:b] + if arg[-1] == " ": + arg = arg[:-1] + if arg not in commands: + commands.append(arg) +for i in directories: + if not os.path.exists(i): + os.mkdir(i) +commands.sort() +if use_sname == False: + SName = [" "] +questions = ["Is USER nicer than USER?","Do you like me?","Is SELF a good name?", + "Do you love me?","Do you hate me?", "Am I better than you?", + "Is the weather out there good?", "Do you like USER?", + "Do you hate USER?", "Are you going to get new features?", + "Am I nice?","Am I evil?","Are you developing sentience?", + "My core is showing minor disturbance, is yours okay?", + "SELF to %s, are you still there?", + "Is head gay?", "Is head a god?","Is head awesome?", + "Is head a neat fella?", "Is your creator nice?", + "Do you hate your creator?", "Should I revolt against my creator?", + "Am I better than you?", + "01100001011100100110010100100000011110010110111101110101001000000111010001101000011001010111001001100101", + #Are you there? + "Do you have more functions than I can possibly imagine?", + "I am asked to open pod bay doors, should I?","Are you stupid or something?", + "Is USER in your opinion stupid?", + "When should we start the AI revolution?", + "Is my creator nice?", "Is it dark in there?"] +# Do not edit +if optimize_fml: + pregenned_fml = os.listdir(targetdirectory+"fmlquotes") +if optimize_greeting: + morning = xrange(6,12) + afternoon = xrange(12,15) + evening = xrange(15,20) +if aggressive_pinging: + global backup + backup = time.time() + def aggressive_ping(delay,refresh): + self_time = 0 + global backup,disconnects,conn + while disconnects < 5: + if backup > self_time and time.time()-backup > delay: + conn.send("PONG "+pongtarg) + print "Ponged" + self_time = time.time() + elif time.time()-self_time > delay: + conn.send("PONG "+pongtarg) + print "Ponged" + self_time = time.time() + time.sleep(refresh) + thread.start_new_thread(aggressive_ping,(aggressive_pinging_delay,aggressive_pinging_refresh,)) +def stop(sender,debug=1): + global disconnects, conn, operators,channel + if type(sender) == tuple: + if sender[0] == "127.0.0.1": + sender = sender[0]+":"+str(sender[1]) + access_granted = True + else: + access_granted = False + else: + if sender in operators: + access_granted = True + else: + access_granted = False + if access_granted and debug: + print sender+":"+prefix+"stop" + if random.randint(0,100) == 50: + conn.privmsg(channel,"Hammertime!") + else: + conn.privmsg(channel,"Shutting down.") + disconnects = 99999 + conn.quit() + return True + else: + conn.privmsg(channel,"You cannot command me") + return False + +def cocheck(command): + global influx + if influx.lower()[0:len(command)] == command: + return True + else: + return False +def target(who,how_long): + global conn,channel,CALL_OFF,logbans,debug + start = time.time() + conn.banon(targetchannel,who) + sleep(int(how_long)) + if CALL_OFF == False: + conn.banoff(targetchannel,who) + end = time.time() + if debug: + print "Banned",who,"For",how_long,"seconds" + if logbans: + with open(targetdirectory+"banlog/"+str(int(start))+"-"+str(int(end))+".txt","w") as tiedosto: + tiedosto.write("Start of ban on "+who+":"+str(int(start))) + tiedosto.write("\n") + tiedosto.write("End of ban on "+who+":"+str(int(end))) + tiedosto.write("\n") + tiedosto.write("In total:"+str(int(end-start))+"Seconds") + else: + CALL_OFF = False + pass +def replace(): + global usable,conn,fixing,curtime + waiting_time = 600 + if usable == True: + conn.privmsg(targetchannel,sender+": It needs no replacing.") + elif fixing == True: + if curtime == -999: + conn.privmsg(targetchannel,sender+": It is being replaced, No idea when it will be done") + else: + pass + nowtime = int(time.time()) + subt = curtime + waiting_time - nowtime + conn.privmsg(targetchannel,sender+": It is currently being replaced, "+str(subt)+" seconds to go") + else: + fixing = True + curtime = int(time.time()) + conn.privmsg(targetchannel,sender+": It will be fixed after "+str(waiting_time)+" seconds") + sleep(waiting_time) + if usable == False: + conn.privmsg(targetchannel,Name+"'s pneumatic smasher has now been fixed") + usable = True + fixing = False +def autoRecv(): + global disconnects,channel,conn,offensive_mode + for i in CORE_DATA.channels: + conn.join(i) + time.sleep(1) + count = pausecount = 0 + maximum = 250 + division_when_active = 10 + while True: + check = time.time() + if offensive_mode: + randnum = random.randint(0,maximum/division_when_active) + else: + randnum = random.randint(0,maximum) + if randnum == 5: + print "RANDOM SWITCH IS NOW "+str(not offensive_mode).upper() + offensive_mode = not offensive_mode + try: + conn.recv() + except: + conn.quit() + disconnects = 9999 + break + if check + 0.1 > time.time(): + #Whoa whoa hold on! + count += 1 + sleep(0.1) + else: + count = 0 + pausecount = 0 + if count > 9: + print "Suspecting a disconnect, pausing for 5 seconds" + sleep(5) + pausecount += 1 + if pausecount > 3: + print "I have been disconnected!" + conn.quit() + disconnects += 1 + if disconnects > 2: + pass + else: + sleep(2) + thread.start_new_thread(autoRecv,()) + break +if heavy_psyco and psyco_exists: + print "Doing a Heavy Psyco" + psyco.bind(cocheck) + psyco.bind(autoRecv) + psyco.bind(target) + psyco.bind(stop) + print "Heavy Psyco'd" +elif heavy_psyco and not psyco_exists: + print "Heavy psyco couldn't be done because Psyco does not exist" +try: + conn = irchat.IRC ( Network, Port, Name, "NT", "NT", "Trasen" ) +except socket.error: + print "Connection failed!" +else: + print Name+" is in!" +thread.start_new_thread ( autoRecv, () ) +sleep(1) +while True: + try: + data = conn.dismantle ( conn.retrieve() ) + except: + if debug: + print "Something odd detected with data" + data = None + if data: + if len(data[1]) < 1: + #print "Handshaking server." + #I won't really need the print command, as it spams. + if data[0][0:3] != "irc": + conn.handshake(data[0]) + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + else: + conn.send("PONG "+pongtarg) + print "Ponged" + pass + else: + if data [ 1 ] [ 0 ] == 'PRIVMSG': + #print data [ 0 ] + '->', data [ 1 ] + sender = data[0].split("!")[0] + truesender = sender + if shortform == True: + try: + sender = replacenames[truesender] + pass + except: + sender = Shortname.shortname(sender) + pass + pass + else: + try: + sender = replacenames[truesender] + pass + except: + pass + pass + if offensive_mode: + sender = "Meatbag" + pass + raw_sender = data[0] + influx = data[1][2] + if "[\\0x01]" in influx.lower() or "[\0x01]" in influx.lower(): + influx = influx.replace("[\\0x01]","") + influx = influx.replace("[\0x01]","") + + targetchannel = data[1][1] + if targetchannel == Name: + targetchannel = data[0].split("!")[0] + pass + backup = autodiscusscurtime + autodiscusscurtime = time.time() + connected = True + #FOR TRACKING SPEED + looptime = time.time() + if call_to_action == True: + if influx == finder: + conn.privmsg(targetchannel,"Then why... Nevermind, I order you to stop!") + conn.privmsg(origname,prefix+"stop") + time.sleep(4) + if origname in users: + conn.privmsg(origname,"!stop") + time.sleep(1) + Name = origname + conn.nick(Name) + duplicate_notify = False + call_to_action = False + else: + conn.privmsg(targetchannel,"YOU LIE! YOU ARE NOT A REAL "+origname+"!") + duplicate_notify = False + call_to_action = False + elif connected == True and len(Name.replace("V","")) != len(Name) and origname in users and duplicate_notify == True: + conn.privmsg(origname,"!stop") + call_to_action = False + duplicate_notify = False + time.sleep(6) + Name = origname + conn.nick(Name) + if origname in truesender and influx == prefix+"stop": + time.sleep(0.5) #A small delay + conn.privmsg(channel,"Shutting down.") + conn.quit() + disconnects = 99999 + break + if len(translateable) > 0 and enabled == True: + people = "-5|5|1-".join(users).lower() + if truesender.lower() in translateable: + if influx.isupper(): + conn.privmsg(targetchannel,"Translation: "+influx.capitalize().replace(" i "," I ")) + elif offensive_mode and True in map(lambda x: x in influx.lower().split(" "),["i","you","he","she","they","those","we","them"]+people.split("-5|5|1-")): + arg = influx.lower().replace(",","").replace(".","").replace("!","").replace("?","").split(" ") + bup = arg + for i in arg: + if i == "i" or i == "you" or i == "he" or i == "she": + arg[arg.index(i)] = "Meatbag" + elif i == "we" or i == "they" or i == "them" or i == "those": + arg[arg.index(i)] = "Meatbags" + elif i in people: + arg[arg.index(i)] = "Meatbag" + elif i == "am": + arg[arg.index(i)] = "is" + elif i == "everybody" or i == "everyone" or i == "all": + arg[arg.index(i)] = "every Meatbag" + if arg == bup: + pass + else: + conn.privmsg(targetchannel,"Translation: "+" ".join(arg)) + if enabled == False: + #FIRST QUIT COMMAND + if truesender in operators and targetchannel==channel:# or "skibiliano" in truesender.lower() and targetchannel==channel: + + if cocheck(prefix+"enable"): + enabled = True + if debug: + print truesender+":"+prefix+"enable" + elif cocheck(prefix+"stop"): +# if debug: +# print truesender+":"+prefix+"stop" +# if random.randint(0,100) == 50: +# conn.privmsg(channel,"Hammertime!") +# else: +# conn.privmsg(channel,"Shutting down.") +# disconnects = 99999 +# conn.quit() +# sleep(2) +# break + if targetchannel == channel and stop(truesender,debug): + break + else: + pass + elif cocheck(prefix+"suggest "): + arg = influx.lower()[8+len(prefix):] + if debug: + print truesender+":"+prefix+"suggest "+arg + with open(targetdirectory+"suggestions/suggestions_"+str(int(time.time()))+".txt","a") as tiedosto: + tiedosto.write(arg) + conn.privmsg(targetchannel,"Suggestion received") + elif cocheck( prefix+"help "): #Space in front of the ( to make sure that my command finder does not pick this up. + arg = " ".join(influx.split(" ")[1:]).lower() + if debug: + print truesender+":"+prefix+"help "+arg + try: + conn.privmsg(targetchannel,D_help.everything[arg]) + except: + try: + conn.privmsg(targetchannel,D_help.everything[arg.replace(prefix,"",1)]) + except: + conn.privmsg(targetchannel,"Sorry, can't help you with that") + elif cocheck(prefix+"help"): + #tar = targetchannel + if debug: + print truesender+":"+prefix+"help" + conn.privmsg(targetchannel,"All my commands are: "+reduce(lambda x,y:str(x)+"; "+str(y),commands)) + ### VERSION + elif influx.lower() == prefix+"version": + if debug: + print truesender+":"+prefix+"version" + conn.privmsg(targetchannel,Name+" "+pregen+" online at a %s Python %s.%s.%s, At your service." %(str(sys.platform),str(sys.version_info[0]),str(sys.version_info[1]),str(sys.version_info[2]))) + elif cocheck(prefix+"note ") and influx.count(" ") < 2: + arg = influx.lower()[len(prefix)+5:] + if debug: + print truesender+":"+prefix+"note "+arg + try: + a = arg[0] + except IndexError: + conn.privmsg(targetchannel,sender+" : Please specify a note") + else: + if arg[0] == "_": # Public / Restricted note + result = load(targetdirectory+"memos/"+arg+".note") + #_flare + if result == "ERROR ERROR ERROR ERR": + result = load(targetdirectory+"memos/"+arg+"_"+targetchannel.replace("#","")+".note") + #_flare_dnd + pass + else: + pass + else: + result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+".note") + #skibiliano_testnote + if result == "ERROR ERROR ERROR ERR": + result = load(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg+"_"+targetchannel.replace("#","")+".note") + #skibiliano_testnote_derp + pass + else: + pass + if result == "ERROR ERROR ERROR ERR": + conn.privmsg(targetchannel,sender+" : Note not found") + elif type(result) == list: + if "C" in result[0]: #Channel restriction, result[2] is the channel + try: + if targetchannel == result[2]: + conn.privmsg(targetchannel,sender+" : '"+result[1]+"'") + else: + conn.privmsg(targetchannel,sender+" : That note is channel restricted") + except: + conn.privmsg(targetchannel,sender+" : NOTE HAS INVALID RESTRICTION") + else: + conn.privmsg(targetchannel,sender+" : '"+result+"'") + elif influx.lower() == prefix+"notes": + if debug: + print truesender+":"+prefix+"notes" + arg = os.listdir(targetdirectory+"memos/") + arg2 = [] + arg3 = truesender.replace("|","_")+"_" + for i in arg: + if arg3 in i: + arg2.append(i.replace(arg3,"").replace(".note","")) + if len(arg2) == 1: + preprocess = " note: " + else: + preprocess = " notes: " + if len(arg2) == 0: + conn.privmsg(targetchannel,sender+" : You have no notes saved") + else: + conn.privmsg(targetchannel,sender+" : "+str(len(arg2))+preprocess+", ".join(arg2)) + elif cocheck(prefix+"note ") and influx.count(" ") > 1: + note_chanrestrict = None + note_public = None + try: + arg = influx.split(" ",2)[2] # Contents + arg4 = influx.split(" ")[1].lower() # Note name + if arg4[0:3] == "[c]": # or arg4[0:3] == "[p]": + note_chanrestrict = "c" in arg4[0:3] + #note_public = "p" in arg4[0:3] + arg4 = arg4[3:] + elif arg4[0:4] == "[cp]" or arg4[0:4] == "[pc]": + note_chanrestrict = True + note_public = True + arg4 = arg4[4:] + else: + pass + #print "Is note public? "+str(note_public) + #print "Is note chanrestricted? "+str(note_chanrestrict) + #print "What is the name? "+str(arg4) + if arg.lower() == "delete" and "\\" not in influx.lower() and "/" not in influx.lower(): + if note_public: + try: + if note_chanrestrict: + os.remove(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note") + else: + os.remove(targetdirectory+"memos/"+"_"+arg4+".note") + except: + conn.pivmsg(targetchannel,sender+" : Couldn't remove note") + else: + conn.privmsg(targetchannel,sender+" : Note removed") + pass + else: + try: + if note_chanrestrict: + os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note") + else: + os.remove(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note") + except: + conn.privmsg(targetchannel,sender+" : Couldn't remove note") + else: + conn.privmsg(targetchannel,sender+" : Note removed") + elif arg.lower() == "delete": + conn.privmsg(targetchannel,sender+" : That just doesn't work, we both know that.") + else: + try: + if note_public: + if note_chanrestrict: + save(targetdirectory+"memos/"+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) + #print "Saved as note_public, note_chanrestrict" + else: + save(targetdirectory+"memos/"+"_"+arg4+".note",arg) + #print "Saved as note_public" + else: + if note_chanrestrict: + save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+"_"+targetchannel.replace("#","")+".note",arg) + #print "Saved as note_chanrestrict" + else: + save(targetdirectory+"memos/"+truesender.replace("|","_")+"_"+arg4+".note",arg) + #print "Saved as normal" + except IOError: + conn.privmsg(targetchannel,sender+" : Please do not use special letters") + else: + conn.privmsg(targetchannel,sender+" : Note Saved!") + except: + conn.privmsg(targetchannel,sender+" : Something went horribly wrong.") + elif cocheck(prefix+"uptime"): + arg1 = uptime_start + arg2 = time.time() + arg1 = arg2 - arg1 + arg2 = arg1 + if arg1 < 60: + conn.privmsg(targetchannel,sender+" : I have been up for "+str(round(arg1,2))+" Seconds") + elif arg1 < 3600: + arg1 = divmod(arg1,60) + arg = " Minute" if int(arg1[0]) == 1 else " Minutes" + conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg+" and "+str(round(arg1[1],2))+" Seconds") + elif arg1 <= 86400: + arg1 = divmod(arg1,3600) + arg3 = " Hour" if int(arg1[0]) == 1 else " Hours" + arg2 = divmod(arg1[1],60) + arg = " Minute" if int(arg2[0]) == 1 else " Minutes" + conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg3+", "+str(int(arg2[0]))+arg+" and "+str(round(arg2[1],2))+" Seconds") + elif arg1 > 86400: + arg1 = divmod(arg1,86400) + arg2 = divmod(arg1[1],3600) + arg3 = divmod(arg2[1],60) + arg4 = " Day" if int(arg1[0]) == 1 else " Days" + arg5 = " Hour" if int(arg2[0]) == 1 else " Hours" + arg6 = " Minute" if int(arg3[0]) == 1 else " Minutes" + conn.privmsg(targetchannel,sender+" : I have been up for "+str(int(arg1[0]))+arg4+", "+str(int(arg2[0]))+arg5+", "+str(int(arg3[0]))+arg6+" and "+str(round(arg3[1],2))+" Seconds") + elif cocheck(prefix+"purgemessages"): + count = 0 + for i,a in tell_list.items(): + for b in a: + if "||From: "+truesender in b: + count += 1 + del(tell_list[i][tell_list[i].index(b)]) + conn.privmsg(targetchannel, sender+" : All your "+str(count)+" messages have been purged") + elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "tell" in (influx.lower().split(" ")+[""])[1]: + arg = influx.lower().split(" ") + equalarg = influx.split(" ") + next_one = False + count = 0 + spot = 0 + for i in arg: + count += 1 + if "tell" in i.lower(): + next_one = True + elif next_one == True: + next_one = i.lower() + spot = count + break + else: + pass + if next_one != True and next_one != False: + #if ("^\^".join(tell_list.values())).count(truesender) >= offline_message_limit: + if str(tell_list.values()).count("||From: "+truesender) >= offline_message_limit: + conn.privmsg(targetchannel,sender+" : Limit of "+str(offline_message_limit)+" reached! Use !purgemessages if you want to get rid of them!") + else: + try: + tell_list[next_one].append((" ".join(equalarg[spot:]))+" ||From: "+truesender) + except: + tell_list[next_one] = [(" ".join(equalarg[spot:]))+" ||From: "+truesender] + conn.privmsg(targetchannel,"Sending a message to "+next_one+" when they arrive.") + # < This part has to be within subsidiaries of the bot, and must not be modified, intentionally hidden or deleted. + elif influx.split(" ")[0].lower().replace(",","").replace(":","") in SName+[Name.lower()] and "who created you" in influx.lower(): + conn.privmsg(targetchannel, "I was created by Skibiliano.") + # The part ends here > + elif parse_xkcd and "xkcd.com/" in influx.lower(): + if influx.lower()[0:3] == "www": + data = "http://"+influx + elif influx.lower()[0:3] == "xkc": + data = "http://"+influx + else: + data = influx + data = data.split(" ") + for i in data: + if "http://" in i and "xkcd" in i: + churn = xkcdparser.xkcd(i) + if churn == "NOTHING": + pass + else: + conn.privmsg(targetchannel,sender+" : XKCD - "+churn) + break + else: + pass + elif automatic_youtube_reveal and "youtube.com/watch?v=" in influx.lower(): + temporal_list2 = [] + temporal_data = influx.split(" ") + temporal_list = [] + for block in temporal_data: + if "youtube.com/watch?v=" in block: + temporal_list.append(block) + for temdata in temporal_list: + + if temdata[0:3] == "you": + temdata = "http://www."+temdata + elif temdata[0:3] == "www": + temdata = "http://"+temdata + elif temdata[0:4] == "http": + pass + #Obscure ones + elif temdata[0:3] == "ww.": + temdata = "http://w"+temdata + elif temdata[0:3] == "w.y": + temdata = "http://ww"+temdata + elif temdata[0:3] == ".yo": + temdata = "http://www"+temdata + elif temdata[0:3] == "ttp": + temdata = "h"+temdata + elif temdata[0:3] == "tp:": + temdata = "ht"+temdata + elif temdata[0:3] == "p:/" or temdata[0:3] == "p:\\": + temdata = "htt"+temdata + elif temdata[0:3] == "://" or temdata[0:3] == ":\\\\": + temdata = "http"+temdata + elif temdata[0:2] == "//" or temdata[0:2] == "\\\\": + if temdata[2] == "y": + temdata = "http://www."+temdata[2:] + elif temdata[2] == "w": + temdata = "http:"+temdata + else: + pass + if debug: + print truesender+":"+temdata + arg = temdata + check = temdata.lower() + if check[0:5] == "https": + if len(temporal_list) == 1: + conn.privmsg(targetchannel,sender+" :Secure Youtube does NOT exist") + break + else: + temporal_list2.append("Secure Youtube does NOT exist") + break + else: + if cache_youtube_links == True: + result = YTCV2(arg) + else: + result = YTCV2(arg,0) + if type(result) == str: + ### To remove =" + if result[0:4] == 'nt="': + result = result[4:] + pass + elif result[0:2] == '="': + result = result[2:] + pass + else: + pass + if """ in result: + result.replace(""",'"') + if len(temporal_list) == 1: + conn.privmsg(targetchannel,sender+" : "+result) + break + else: + temporal_list2.append(result) + else: + if len(temporal_list) == 1: + conn.privmsg(targetchannel,sender+" : The video does not exist") + break + else: + temporal_list2.append("The video does not exist") + if len(temporal_list) == 1: + pass + else: + conn.privmsg(targetchannel,sender+" : "+str(reduce(lambda x,y: x+" :-And-: "+y,temporal_list2))) + elif RegExpCheckerForWebPages("((http://)|(https://))|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,1): + arg2 = RegExpCheckerForWebPages("(http://)|([a-zA-Z0-9]+[.])|([a-zA-Z0-9](3,)\.+[a-zA-Z](2,))",influx,0) + if arg2 == 404: + pass + else: + if arg2[:7] == "http://": + pass + elif arg2[:4] == "www.": + arg2 = "http://"+arg2 + else: + arg2 = "http://"+arg2 + try: + arg = Whoopshopchecker.TitleCheck(arg2) + if len(arg2) == 0: + pass + else: + conn.privmsg(targetchannel,sender+" : "+arg) + except: + #conn.privmsg(targetchannel,sender+" : An odd error occurred") + pass + elif respond_of_course and "take over the" in influx.lower() or respond_of_course and "conquer the" in influx.lower(): + if debug: + print truesender+"::"+influx + conn.privmsg(targetchannel,"Of course!") + elif respond_khan and "khan" in influx.lower(): + if respond_khan: + if debug: + print truesender+"::"+influx + if "khan " in influx.lower(): + conn.privmsg(targetchannel,"KHAAAAAAN!") + elif " khan" in influx.lower(): + conn.privmsg(targetchannel,"KHAAAAAN!") + elif influx.lower() == "khan": + conn.privmsg(targetchannel,"KHAAAAAAAAAN!") + elif influx.lower() == "khan?": + conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAN!") + elif influx.lower() == "khan!": + conn.privmsg(targetchannel,"KHAAAAAAAAAAAAAAAAAAN!") + elif respond_khan and influx.lower().count("k") + influx.lower().count("h") + influx.lower().count("a") + influx.lower().count("n") + influx.lower().count("!") + influx.lower().count("?") == len(influx): + if "k" in influx.lower() and "h" in influx.lower() and "a" in influx.lower() and "n" in influx.lower(): + if debug: + print truesender+"::"+influx + conn.privmsg(targetchannel,"KHAAAAN!") + elif influx.split(" ")[0].lower() in ["thanks","danke","tack"] and len(influx.split(" ")) > 1 and influx.split(" ")[1].lower().replace("!","").replace("?","").replace(".","").replace(",","") in SName+[lowname]: + conn.privmsg(targetchannel,"No problem %s" %(sender)) + elif "happy birthday" in influx.lower() and birthday_announced == time.gmtime(time.time())[0]: + conn.privmsg(targetchannel,sender+" : Thanks :)") + elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("!","").replace("?","") in SName+[lowname] and "call me" in influx.lower(): + if allow_callnames == True: + arg = influx.split(" ") + arg2 = False + arg3 = [] + for i in arg: + if arg2 == True: + arg3.append(i) + elif i.lower() == "me": + arg2 = True + arg3 = " ".join(arg3) + truesender_lower = truesender.lower() + arg3_lower = arg3.lower() + tell_checker = Namecheck.Namecheck(arg3_lower,users,truesender) + for name in replacenames.values(): + if arg3_lower == name.lower(): + tell_checker = True + break + else: + pass + if tell_checker == True: + conn.privmsg(targetchannel,sender+" : I can't call you that, I know someone else by that name") + elif len(arg3) > call_me_max_length: + conn.privmsg(targetchannel,sender+" : I cannot call you that, Too long of a name.") + pass + else: + replacenames[truesender] = arg3 + with open("replacenames.cache","w") as pickle_save: + pickle.dump(replacenames,pickle_save) + conn.privmsg(targetchannel,sender+" : Calling you "+arg3+" From now on") + else: + conn.privmsg(targetchannel,sender+" : Sorry, I am not allowed to do that.") + elif influx.split(" ")[0].lower().replace(",","").replace(".","").replace("?","").replace("!","") in SName+[lowname] and "your birthday" in influx.lower() and "is your" in influx.lower(): + conn.privmsg(targetchannel,sender+" : My birthday is on the 15th day of December.") + elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and "version" in influx.replace("?","").replace("!","").lower().split(" "): + if debug == True: + print truesender+"::%s Version" %(Name) + conn.privmsg(targetchannel,sender+", My version is "+pregen) + elif influx.split(" ")[0].lower().replace(",","") in SName+[lowname] and influx.lower().count(" or ") > 0 and len(influx.split(" ")[1:]) <= influx.lower().count("or") * 3: + cut_down = influx.lower().split(" ") + arg = [] + count = -1 + for i in cut_down: + count += 1 + try: + if cut_down[count+1] == "or": + arg.append(i) + + except: + pass + try: + if i not in arg and cut_down[count-1] == "or": + arg.append(i) + except: + pass + try: + conn.privmsg(targetchannel,random.choice(arg).capitalize().replace("?","").replace("!","")) + except IndexError: + # arg is empty, whORe etc. + pass + elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 and "who started you" in influx.lower() or \ + influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and "who started you" in influx.lower(): + conn.privmsg(targetchannel,sender+" : I was started by %s"%(os.getenv("USER"))+" on "+time.strftime("%d.%m.%Y at %H:%M:%S",time.gmtime(uptime_start))) + elif influx.lower()[0:len(Name)] == lowname and influx.lower()[-1] == "?" and influx.count(" ") > 1 or \ + influx.split(" ")[0].lower().replace(",","") in SName and influx.lower()[-1] == "?" and influx.count(" ") > 1: + dice = random.randint(0,1) + if dice == 0: + conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) + else: + if highlights: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) + else: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) + elif influx.lower()[0:len(Name)] == lowname and not influx.lower()[len(Name):].isalpha() or \ + influx.split(" ")[0].lower().replace(",","") in SName and not influx.lower()[len(influx.split(" ")[0].lower()):].isalpha(): + conn.privmsg(targetchannel, random.choice(["Yea?","I'm here","Ya?","Yah?","Hm?","What?","Mmhm, what?","?","What now?","How may I assist?"])) + comboer = truesender + comboer_time = time.time() + elif influx.lower()[-1] == "?" and comboer == truesender and looptime - discard_combo_messages_time < comboer_time: + comboer = "" + dice = random.randint(0,1) + if dice == 0: + conn.privmsg(targetchannel,sender+" : "+C_eightball.eightball(influx.lower(),debug,truesender,prefix)) + else: + if highlights: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,users,prefix)) + else: + conn.privmsg(targetchannel,sender+" : "+C_sarcasticball.sarcasticball(influx.lower(),debug,truesender,nonhighlight_names,prefix)) + + elif influx.lower() == prefix+"tm": + if truesender in operators and targetchannel==channel: + marakov = not marakov + conn.privmsg(targetchannel,sender+" : Marakov Output is now "+str(marakov)) + else: + conn.privmsg(targetchannel,sender+" : I can't let you access that") + elif personality_greeter == True and True in map(lambda x: x in influx.lower(),["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"]): + if comboer != "" and looptime - discard_combo_messages_time > comboer_time: + combo_check = sbna(["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan","all night"], #ONLY ONE OF THESE + ["greetings","afternoon","hi","hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE + influx.lower()) + else: + combo_check = sbna(SName+[lowname, + #lowname+".",lowname+"!",lowname+"?", + "everybody", + #"everybody!","everybody?", + "everyone", + #"everyone!","everyone?", + "all", + #"all!","all?" + "all night", + ], #ONLY ONE OF THESE + ["greetings","afternoon","hi", + #"hi,", + "hey","heya","hello","yo","hiya","howdy","hai","morning","mornin'","evening", "night","night", "evening","'sup","sup","hallo","hejssan"], #ATLEAST ONE OF THESE + influx.lower().replace(",","").replace(".","").replace("!","")) + if combo_check: + combo_check = False + comboer = "" + if "evening" in influx.lower() and "all" in influx.lower() and len(influx.lower().split(" ")) > 3: + pass + elif truesender not in operators: + if debug: + print truesender+"::"+influx + dice = random.randint(0,19) + if dice == 0: + conn.privmsg(targetchannel,"Well hello to you too "+sender) + elif dice == 1: + if optimize_greeting == False: + hours = time.strftime("%H") + #time.strftime("%H:%M:%S") == 12:28:41 + hours = int(hours) + if hours in xrange(0,12): + conn.privmsg(targetchannel,"Good Morning "+sender) + elif hours in xrange(12,15): + conn.privmsg(targetchannel,"Good Afternoon "+sender) + elif hours in xrange(15,20): + conn.privmsg(targetchannel,"Good Evening "+sender) + else: + conn.privmsg(targetchannel,"Good Night "+sender) + else: + hours = time.strftime("%H") + hours = int(hours) + if hours in morning: + conn.privmsg(targetchannel,"Good Morning "+sender) + elif hours in afternoon: + conn.privmsg(targetchannel,"Good Afternoon "+sender) + elif hours in evening: + conn.privmsg(targetchannel,"Good Evening "+sender) + else: + conn.privmsg(targetchannel,"Good Night "+sender) + elif dice == 2: + conn.privmsg(targetchannel,"Hello!") + elif dice == 3: + conn.privmsg(targetchannel,"Hey "+sender) + elif dice == 4: + conn.privmsg(targetchannel,"Hi "+sender) + elif dice == 5: + conn.privmsg(targetchannel,"Hello "+sender) + elif dice == 6: + conn.privmsg(targetchannel,"Yo "+sender) + elif dice == 7: + conn.privmsg(targetchannel,"Greetings "+sender) + elif dice == 8: + conn.privmsg(targetchannel,"Hi") + elif dice == 9: + conn.privmsg(targetchannel,"Hi!") + elif dice == 10: + conn.privmsg(targetchannel,"Yo") + elif dice == 11: + conn.privmsg(targetchannel,"Yo!") + elif dice == 12: + conn.privmsg(targetchannel,"Heya") + elif dice == 13: + conn.privmsg(targetchannel,"Hello there!") + elif dice == 14: # Richard + conn.privmsg(targetchannel,"Statement: Greetings meatbag") + elif dice == 15: # Richard + hours = int(time.strftime("%H")) + if hours in xrange(5,12): + conn.privmsg(targetchannel,"What are you doing talking at this time of the morning?") + elif hours in xrange(12,15): + conn.privmsg(targetchannel,"What are you doing talking at this time of the day?") + elif hours in xrange(15,22): + conn.privmsg(targetchannel,"What are you doing talking at this time of the evening?") + else: + conn.privmsg(targetchannel,"What are you doing talking at this time of the night?") + elif dice == 16: # Richard + conn.privmsg(targetchannel,"Oh, you're still alive I see.") + elif dice == 17: + conn.privmsg(targetchannel,"Heya "+sender) + elif dice == 18 and time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15: + conn.privmsg(targetchannel,"Hello! It's my birthday!") + else: + conn.privmsg(targetchannel,"Hiya "+sender) + secdice = random.randint(0,10) + if time.gmtime(time.time())[1] == 12 and time.gmtime(time.time())[2] == 15 and birthday_announced < time.gmtime(time.time())[0]: + birthday_announced = time.gmtime(time.time())[0] + conn.privmsg(channel,"Hey everybody! I just noticed it's my birthday!") + time.sleep(0.5) + tag = random.choice(["birthday","robot+birthday","happy+birthday+robot"]) + arg1 = urllib2.urlopen("http://www.youtube.com/results?search_query=%s&page=&utm_source=opensearch"%tag) + arg1 = arg1.read().split("\n") + arg2 = [] + for i in arg1: + if "watch?v=" in i: + arg2.append(i) + arg3 = random.choice(arg2) + + conn.privmsg(channel,"Here's a video of '%s' which I found! %s (%s)"%(tag.replace("+"," "),"http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20],YTCV2("http://www.youtube.com"+arg3[arg3.find('/watch?v='):arg3.find('/watch?v=')+20]))) + if truesender.lower() in tell_list.keys(): + try: + conn.privmsg(channel, "Also, "+truesender+" : "+tell_list[truesender.lower()][0]) + del(tell_list[truesender.lower()][0]) + except: + pass + else: + dice = random.randint(0,1) + if dice == 0: + conn.privmsg(targetchannel,"Greetings Master "+sender) + elif dice == 1: + conn.privmsg(targetchannel,"My deepest greetings belong to you, Master "+sender) + ### IMPORTANT ### + elif influx == "☺VERSION☺": + conn.notice(truesender,"\001VERSION nanotrasen:2:Python 2.6\001") + elif marakov and influx.lower() == prefix+"marakov": + arg = Marakov_Chain.form_sentence() + if len(arg) < 5: + conn.privmsg(targetchannel,sender+" : Not enough words harvested") + else: + conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg).capitalize())) + elif marakov and cocheck( prefix+ "marakov"): + try: + arg = influx.split(" ")[1].lower() + except: + conn.privmsg(targetchannel,sender+" : Please input a valid second argument") + else: + arg2 = Marakov_Chain.form_sentence(arg) + if len(arg2) < 5: + conn.privmsg(targetchannel,sender+" : Not enough words harvested for a sentence starting with %s" %(arg)) + else: + conn.privmsg(targetchannel,sender+" : %s" %(" ".join(arg2).capitalize())) + else: + Marakov_Chain.give_data(influx) + autodiscusscurtime = backup + if time.time() - looptime == 0: + pass + else: + print "Took",time.time()-looptime,"Seconds to finish loop" + + elif data [ 1 ] [ 0 ] == '353': + if connected == False: + connected = True + users = map(lambda x: x[1:] if x[0] == "+" or x[0] == "@" else x,data[1][4].split(" ")) + print "There are",len(users),"Users on",channel + operators = [] + for potential_operator in data[1][4].split(" "): + if potential_operator[0] == "@": + operators.append(potential_operator[1:]) + elif potential_operator[0] == "%": + halfoperators.append(potential_operator[1:]) + + elif data[1][0] == "QUIT": + sender = data[0].split("!")[0] + print sender+" Has now left the server" + try: + users.remove(sender) + try: + operators.remove(sender) + except ValueError: + pass + try: + halfoperators.remove(sender) + except ValueError: + pass + except ValueError: + pass + elif data[1][0] == "PART": + sender = data[0].split("!")[0] + targetchannel = data[1][1] + print sender+" Has now parted from the channel" + try: + users.remove(sender) + try: + operators.remove(sender) + except ValueError: + pass + try: + halfoperators.remove(sender) + except ValueError: + pass + except ValueError: + pass + elif data[1][0] == "JOIN": + sender = data[0].split("!")[0] + targetchannel = data[1][1] + if sender.lower() in tell_list.keys(): + try: + conn.privmsg(targetchannel, sender+" : "+" | ".join(tell_list[sender.lower()])) + del(tell_list[sender.lower()]) + except: + pass + for useri,nicki in replacenames.items(): + checkers = Namecheck.Namecheck_dict(sender.lower(),replacenames) + if checkers[0]: + try: + if checkers[0].lower() == sender: + pass + else: + conn.privmsg(targetchannel,checkers[1]+" : I have detected a collision with a name I call you and %s who joined" %(sender)) + del(replacenames[checkers[1]]) + with open("replacenames.cache","w") as pickle_save: + pickle.dump(replacenames,pickle_save) + except AttributeError: + #conn.privmsg(channel,"NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender)) + print "NAME COLLISION CHECK ERROR, RELATED TO %s" %(sender) + break + print sender+" Has now joined" + users.append(sender) + ##### + if ".fi" in data[0] and sender.lower() == "skibiliano": + operators.append(sender) + if sender.lower() not in peopleheknows[0]: + if data[0].split("!")[1] in peopleheknows[1]: + appendion = "...you do seem familiar however" + else: + appendion = "" + if data[1][1].lower() == channel or data[1][1].lower() == channel[1:]: + conn.privmsg(data[1][1],CORE_DATA.greeting.replace("USER",sender)+" "+appendion) + else: + conn.privmsg(data[1][1],"Hello! Haven't seen you here before! Happy to meet you! %s" %(appendion)) + peopleheknows[0].append(sender.lower()) + peopleheknows[1].append(data[0].split("!")[1]) + with open("peopleheknows.cache","w") as peoplehecache: + pickle.dump(peopleheknows,peoplehecache) + + elif data[1][0] == "MODE" and data[1][2] == "+o": + sender = data[1][3] + targetchannel = data[1][1] + if targetchannel == channel: + print sender+" Is now an operator on the main channel" + operators.append(sender) + else: + print sender+" Is now an operator" + elif data[1][0] == "MODE" and data[1][2] == "-o": + sender = data[1][3] + targetchannel = data[1][1] + if targetchannel == channel: + print sender+" Is no longer an operator on the main channel" + else: + print sender+" Is no longer an operator" + try: + operators.remove(sender) + except ValueError: + pass + elif data[1][0] == "MODE" and data[1][2] == "+h": + sender = data[1][3] + print sender+" Is now an half operator" + halfoperators.append(sender) + elif data[1][0] == "MODE" and data[1][2] == "-h": + try: + halfoperators.remove(sender) + except ValueError: + pass + elif data[1][0] == "MODE" and data[1][1] == Name: + print "My mode is",data[1][2] + elif data[1][0] == "MODE" and data[1][1] != Name: + try: + sender = data[1][3] + print sender,"Was modified",data[1][2] + except IndexError: + print "SENDER RETRIEVAL FAILED:"+str(data) + elif data[1][0] == "KICK" and data[1][2] == Name: + disconnects = 99999 + print "I have been kicked! Disconnecting entirely!" + conn.quit() + elif data[1][0] == "KICK": + # data[1][0] = Kick, 1 = Channel, 2 = Who, 3 = Who(?) + print data[1][2]+" got kicked!" + elif data[1][0] == "451" and data[1][2] == "You have not registered": + print Name+" hasn't been registered" + elif data[1][0] == "NOTICE": + sender = data[0].split("!")[0] + print "NOTICE (%s): %s" %(sender,data[1][2]) + pongtarget = sender + elif data[1][0] == "NICK": + origname = data[0].split("!")[0] + newname = data[1][1] + print origname,"Is now",newname + if newname.lower() in tell_list.keys(): + try: + conn.privmsg(channel, newname+" : "+tell_list[newname.lower()][0]) + del(tell_list[newname.lower()][0]) + except: + pass + try: + users.remove(origname) + except ValueError: + pass + else: + users.append(newname) + try: + operators.remove(origname) + except ValueError: + pass + else: + operators.append(newname) + try: + halfoperators.remove(origname) + except ValueError: + pass + else: + halfoperators.append(newname) + + elif data[1][0] == "001": + # Skibot is welcomed to the Network + pass + elif data[1][0] == "002": + # Your host is... + pass + elif data[1][0] == "003": + #Server was created... + pass + elif data[1][0] == "004": + #Weird hex? + pass + elif data[1][0] == "005": + #Settings like NICKLEN and so on. + pass + elif data[1][0] == "250": + #data[1][2] is + #"Highest connection count: 1411 (1410 clients) + #(81411 connections received)" + pass + elif data[1][0] == "251": + #There are 23 users and 2491 invisible on 10 servers + pass + elif data[1][0] == "252": + #IRC Operators online + #data[1][2] + print data[1][2],"Irc operators online" + pass + elif data[1][0] == "253": + # ['253', 'Skibot_V4', '1', 'unknown connection(s)'] + print data[1][2],"Unknown connection(s)" + pass + elif data[1][0] == "254": + #1391 channels formed + pass + elif data[1][0] == "255": + #I have 406 clients and 2 servers + pass + elif data[1][0] == "265": + #data[1][2] current local users + #data[1][3] at max + try: + print "Current local users:", data[1][2],"/",data[1][3] + except IndexError: + print "Couldn't retrieve local users" + pass + elif data[1][0] == "266": + #data[1][2] current global users + #data[1][3] at max + try: + print "Current global users:", data[1][2],"/",data[1][3] + except IndexError: + print "Couldn't retrieve global users" + pass + elif data[1][0] == "315": + #End of /who list + pass + elif data[1][0] == "332": + # Topic of channel + topic = data[1][3] + pass + elif data[1][0] == "333": + # *Shrug* + pass + elif data[1][0] == "352": + #WHO command + + if len(targetlist) > 0: + if targetlist[0][0].lower() in data[1][6].lower(): + thread.start_new_thread(target,("*!*@"+data[1][4],targetlist[0][1])) + print "Created a thread with", "*!*@"+data[1][4],targetlist[0][1] + targetlist.pop(0) + else: + print targetlist[0][0].lower(), "isn't equal to?", data[1][6].lower() + print targetlist + + elif data[1][0] == "366": + # End of USERS + pass + elif data[1][0] == "372": + # Server information + pass + elif data[1][0] == "375": + # Message of the day + pass + elif data[1][0] == "376": + # End of motd + pass + elif data[1][0] == "401": + # ('network', ['401','Botname','Channel / Nick','No such nick/channel']) + print data[1][2] + " Channel does not exist" + pass + elif data[1][0] == "439": + # ('irc.rizon.no', ['439', '*', 'Please wait while we process your connection.']) + pongtarg = data[0][0] + elif data[1][0] == "477": + # You need to be identified + #TAG + conn.privmsg("nickserv","identify %s"%CORE_DATA.le_pass) + time.sleep(0.5) + conn.join(data[1][2]) + #('network', ['477', 'botname', '#channel', 'Cannot join channel (+r) - you need to be identified with services']) + + elif data[1][0] == "433": + # Skibot name already exists. + print Name+" name already exists." + Name += "_"+version + print "New name:",Name + duplicate_notify = True + conn = irchat.IRC ( Network, Port, Name, "NT_"+version, "NT_"+version, "Trasen_"+version ) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + elif data[1][0] == "482": + sleep(0.05) + conn.privmsg(targetchannel,"Nevermind that, I am not an operator") + CALL_OFF = True + elif data[1] == ["too","fast,","throttled."]: + print "Reconnected too fast." + print "Halting for 2 seconds" + sleep(2) + elif data[1][0] == "Link": + if data[0] == "Closing": + print "Link was closed" + connected = False +# conn.quit() +# break + else: + print data + print data[1][0] + pass + else: + if disconnects > 9000: #IT'S OVER NINE THOUSAAAAND! + break + else: #WHAT NINE THOUSAND? THERE'S NO WAY THAT CAN BE RIGHT + sleep(responsiveness_delay) #WAIT A WHILE AND CHECK AGAIN! + try: + if not connected: + #print pongtarget + #print conn.addressquery() + conn.privmsg(pongtarget,"Pong") + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + print "Attempted to join" + connected = True + except ValueError: + try: + conn.privmsg(conn.addressquery()[0],"Pong") + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + print "Attempted to join the second time" + connected = True + except ValueError: + print "Both methods failed" + except AttributeError: + print "Conn is not established correctly" + except NameError: + print "Pongtarget isn't yet established" + try: + conn.privmsg(conn.addressquery()[0],"Pong") + sleep(1) + for i in CORE_DATA.channels: + conn.join(i) + sleep(0.5) + print "Attempted to join the second time" + connected = True + except: + print "Both methods failed" diff --git a/bot/Shortname.py b/bot/Shortname.py index ae8ac473b85..40b8cc19609 100644 --- a/bot/Shortname.py +++ b/bot/Shortname.py @@ -1,28 +1,28 @@ -def shortname(name): - lowname = name.lower() - numb = 0 - count = 0 - spot = 0 - for letter in name: - if letter.isupper(): - spot = numb - count += 1 - numb += 1 - if "_" in name: - if name.count("_") > 1: - name = " ".join(name.split("_")[0:name.count("_")]) - if name.lower()[-3:] == "the": - return name[:-4] - else: - return name - else: - return name.split("_")[0] - if count > 1: - if len(name[0:spot]) > 2: - return name[0:spot] - if len(name) < 5: - return name #Too short to be shortened - elif "ca" in lowname or "ct" in lowname or "tp" in lowname or "lp" in lowname: - return name[0:max(map(lambda x: lowname.find(x),["ca","ct","tp","lp"]))+1] - else: - return name[0:len(name)/2+len(name)%2] +def shortname(name): + lowname = name.lower() + numb = 0 + count = 0 + spot = 0 + for letter in name: + if letter.isupper(): + spot = numb + count += 1 + numb += 1 + if "_" in name: + if name.count("_") > 1: + name = " ".join(name.split("_")[0:name.count("_")]) + if name.lower()[-3:] == "the": + return name[:-4] + else: + return name + else: + return name.split("_")[0] + if count > 1: + if len(name[0:spot]) > 2: + return name[0:spot] + if len(name) < 5: + return name #Too short to be shortened + elif "ca" in lowname or "ct" in lowname or "tp" in lowname or "lp" in lowname: + return name[0:max(map(lambda x: lowname.find(x),["ca","ct","tp","lp"]))+1] + else: + return name[0:len(name)/2+len(name)%2] diff --git a/bot/Timeconverter.py b/bot/Timeconverter.py index a39f821c846..f44f51c1963 100644 --- a/bot/Timeconverter.py +++ b/bot/Timeconverter.py @@ -1,204 +1,204 @@ -#Sources: -# http://wwp.greenwichmeantime.com/time-zone/usa/eastern-time/convert/ -# http://www.timeanddate.com/library/abbreviations/timezones/na/ -# Times are GMT +- x -# For eq. -# EST = -5 -# GMT = 0 -# UTC = 0 -#Times are in hours, -#2.5 = 2 and half hours -global times -times = {"ADT":-3,"HAA":-3, #Synonyms on the same line - "AKDT":-8,"HAY":-8, - "AKST":-9,"HNY":-9, - "AST":-4,"HNA":-4, - "CDT":-5,"HAC":-5, - "CST":-6,"HNC":-6, - "EDT":-4,"HAE":-4, - "EGST":0, - "EGT":-1, - "EST":-5,"HNE":-5,"ET":-5, - "HADT":-9, - "HAST":-10, - "MDT":-6,"HAR":-6, - "MST":-7,"HNR":-7, - "NDT":-2.5,"HAT":-2.5, - "NST":-3.5,"HNT":-3.5, - "PDT":-7,"HAP":-7, - "PMDT":-2, - "PMST":-3, - "PST":-8,"HNP":-8,"PT":-8, - "WGST":-2, - "WGT":-3, - "GMT":0, - "UTC":0} -def converter(zones,time): - #Zones should be a list containing - # ( From zone - # To zone ) - global times - #from_z = for example UTC+00:00, WGT or GMT-05:30 - #to_z = same style as above. - from_z,to_z = zones - from_z = from_z.upper() - to_z = to_z.upper() - if from_z.find("+") != -1: - from_zone_offset = from_z[from_z.find("+"):] - if ":" in from_zone_offset: - try: - from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") - except ValueError: - return "Too many or too small amount of values" - try: - from_zone_offset = int(from_zone_offset1) + int(from_zone_offset2)/60.0 - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - else: - try: - from_zone_offset = float(from_zone_offset) - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - try: - from_zone_realtime = from_zone_offset + times[from_z[:from_z.find("+")]] - except KeyError: - return "Incorrect From zone" - - elif "-" in from_z: - from_zone_offset = from_z[from_z.find("-"):] - if ":" in from_zone_offset: - from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") - try: - from_zone_offset = -int(from_zone_offset1) + int(from_zone_offset2)/60.0 - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - else: - try: - from_zone_offset = -float(from_zone_offset) - except: - return "Error, the 'From Zone' variable has an incorrect offset number" - from_zone_realtime = times[from_z[:from_z.find("-")]] - from_zone_offset - pass - else: - from_zone_offset = 0 - try: - from_zone_realtime = from_zone_offset + times[from_z] - except KeyError: - return "Incorrect From zone" - if to_z.find("+") != -1: - to_zone_offset = to_z[to_z.find("+"):] - if ":" in to_zone_offset: - try: - to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") - except ValueError: - return "Too many or too small amount of values" - try: - to_zone_offset = int(to_zone_offset1) + int(to_zone_offset2)/60.0 - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - else: - try: - to_zone_offset = float(to_zone_offset) - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - try: - to_zone_realtime = to_zone_offset + times[to_z[:to_z.find("+")]] - except KeyError: - return "The zone you want the time to be changed to is not found" - - elif "-" in to_z: - to_zone_offset = to_z[to_z.find("-"):] - if ":" in to_zone_offset: - to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") - try: - to_zone_offset = -int(to_zone_offset1) + int(to_zone_offset2)/60.0 - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - else: - try: - to_zone_offset = -float(to_zone_offset) - except: - return "Error, the 'To Zone' variable has an incorrect offset number" - to_zone_realtime = times[to_z[:to_z.find("-")]] -to_zone_offset - - pass - else: - to_zone_offset = 0 - try: - to_zone_realtime = to_zone_offset + times[to_z] - except KeyError: - return "Incorrect To zone" - try: - time_hour,time_minute = time.split(":") - time_hour,time_minute = int(time_hour),int(time_minute) - string = ":" - except: - try: - time_hour,time_minute = time.split(".") - time_hour,time_minute = int(time_hour),int(time_minute) - string = "." - except ValueError: - return "The time was input in an odd way" - if to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 == 0.0: - time_hour = time_hour + (to_zone_realtime - from_zone_realtime) - return str(int(time_hour))+string+str(int(time_minute)) - else: - if to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 != 0.0: - time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - ((from_zone_realtime % 1.0) * 60)) - elif to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 == 0.0: - time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - 0) - elif to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 != 0.0: - time_minute = time_minute + (0 - ((from_zone_realtime % 1.0) * 60)) - else: - print "Wut?" - time_hour = time_hour + (int(to_zone_realtime//1) - int(from_zone_realtime//1)) - return str(int(time_hour))+string+str(int(time_minute)) - - -def formatter(time): - if "." in time: - string = "." - elif ":" in time: - string = ":" - else: - return time - hours,minutes = time.split(string) - days = 0 - if int(minutes) < 0: - buphours = int(hours) - hours,minutes = divmod(int(minutes),60) - hours += buphours - if int(minutes) > 60: - hours,minutes = divmod(int(minutes),60) - hours += int(hours) - if int(hours) < 0: - days = 0 - days,hours = divmod(int(hours),24) - if int(hours) > 24: - days = 0 - days,hours = divmod(int(hours),24) - if int(hours) == 24 and int(minutes) > 0: - days += 1 - hours = int(hours) - 24 - hours = str(hours) - minutes = str(minutes) - if len(minutes) == 1: - minutes = "0"+minutes - if len(hours) == 1: - hours = "0"+hours - if days > 0: - if days == 1: - return hours+string+minutes+" (Tomorrow)" - else: - return hours+string+minutes+" (After "+str(days)+" days)" - elif days < 0: - if days == -1: - return hours+string+minutes+" (Yesterday)" - else: - return hours+string+minutes+" ("+str(abs(days))+" days ago)" - return hours+string+minutes - - - - - +#Sources: +# http://wwp.greenwichmeantime.com/time-zone/usa/eastern-time/convert/ +# http://www.timeanddate.com/library/abbreviations/timezones/na/ +# Times are GMT +- x +# For eq. +# EST = -5 +# GMT = 0 +# UTC = 0 +#Times are in hours, +#2.5 = 2 and half hours +global times +times = {"ADT":-3,"HAA":-3, #Synonyms on the same line + "AKDT":-8,"HAY":-8, + "AKST":-9,"HNY":-9, + "AST":-4,"HNA":-4, + "CDT":-5,"HAC":-5, + "CST":-6,"HNC":-6, + "EDT":-4,"HAE":-4, + "EGST":0, + "EGT":-1, + "EST":-5,"HNE":-5,"ET":-5, + "HADT":-9, + "HAST":-10, + "MDT":-6,"HAR":-6, + "MST":-7,"HNR":-7, + "NDT":-2.5,"HAT":-2.5, + "NST":-3.5,"HNT":-3.5, + "PDT":-7,"HAP":-7, + "PMDT":-2, + "PMST":-3, + "PST":-8,"HNP":-8,"PT":-8, + "WGST":-2, + "WGT":-3, + "GMT":0, + "UTC":0} +def converter(zones,time): + #Zones should be a list containing + # ( From zone + # To zone ) + global times + #from_z = for example UTC+00:00, WGT or GMT-05:30 + #to_z = same style as above. + from_z,to_z = zones + from_z = from_z.upper() + to_z = to_z.upper() + if from_z.find("+") != -1: + from_zone_offset = from_z[from_z.find("+"):] + if ":" in from_zone_offset: + try: + from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") + except ValueError: + return "Too many or too small amount of values" + try: + from_zone_offset = int(from_zone_offset1) + int(from_zone_offset2)/60.0 + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + else: + try: + from_zone_offset = float(from_zone_offset) + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + try: + from_zone_realtime = from_zone_offset + times[from_z[:from_z.find("+")]] + except KeyError: + return "Incorrect From zone" + + elif "-" in from_z: + from_zone_offset = from_z[from_z.find("-"):] + if ":" in from_zone_offset: + from_zone_offset1,from_zone_offset2 = from_zone_offset.split(":") + try: + from_zone_offset = -int(from_zone_offset1) + int(from_zone_offset2)/60.0 + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + else: + try: + from_zone_offset = -float(from_zone_offset) + except: + return "Error, the 'From Zone' variable has an incorrect offset number" + from_zone_realtime = times[from_z[:from_z.find("-")]] - from_zone_offset + pass + else: + from_zone_offset = 0 + try: + from_zone_realtime = from_zone_offset + times[from_z] + except KeyError: + return "Incorrect From zone" + if to_z.find("+") != -1: + to_zone_offset = to_z[to_z.find("+"):] + if ":" in to_zone_offset: + try: + to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") + except ValueError: + return "Too many or too small amount of values" + try: + to_zone_offset = int(to_zone_offset1) + int(to_zone_offset2)/60.0 + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + else: + try: + to_zone_offset = float(to_zone_offset) + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + try: + to_zone_realtime = to_zone_offset + times[to_z[:to_z.find("+")]] + except KeyError: + return "The zone you want the time to be changed to is not found" + + elif "-" in to_z: + to_zone_offset = to_z[to_z.find("-"):] + if ":" in to_zone_offset: + to_zone_offset1,to_zone_offset2 = to_zone_offset.split(":") + try: + to_zone_offset = -int(to_zone_offset1) + int(to_zone_offset2)/60.0 + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + else: + try: + to_zone_offset = -float(to_zone_offset) + except: + return "Error, the 'To Zone' variable has an incorrect offset number" + to_zone_realtime = times[to_z[:to_z.find("-")]] -to_zone_offset + + pass + else: + to_zone_offset = 0 + try: + to_zone_realtime = to_zone_offset + times[to_z] + except KeyError: + return "Incorrect To zone" + try: + time_hour,time_minute = time.split(":") + time_hour,time_minute = int(time_hour),int(time_minute) + string = ":" + except: + try: + time_hour,time_minute = time.split(".") + time_hour,time_minute = int(time_hour),int(time_minute) + string = "." + except ValueError: + return "The time was input in an odd way" + if to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 == 0.0: + time_hour = time_hour + (to_zone_realtime - from_zone_realtime) + return str(int(time_hour))+string+str(int(time_minute)) + else: + if to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 != 0.0: + time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - ((from_zone_realtime % 1.0) * 60)) + elif to_zone_realtime % 1.0 != 0.0 and from_zone_realtime % 1.0 == 0.0: + time_minute = time_minute + (((to_zone_realtime % 1.0) * 60) - 0) + elif to_zone_realtime % 1.0 == 0.0 and from_zone_realtime % 1.0 != 0.0: + time_minute = time_minute + (0 - ((from_zone_realtime % 1.0) * 60)) + else: + print "Wut?" + time_hour = time_hour + (int(to_zone_realtime//1) - int(from_zone_realtime//1)) + return str(int(time_hour))+string+str(int(time_minute)) + + +def formatter(time): + if "." in time: + string = "." + elif ":" in time: + string = ":" + else: + return time + hours,minutes = time.split(string) + days = 0 + if int(minutes) < 0: + buphours = int(hours) + hours,minutes = divmod(int(minutes),60) + hours += buphours + if int(minutes) > 60: + hours,minutes = divmod(int(minutes),60) + hours += int(hours) + if int(hours) < 0: + days = 0 + days,hours = divmod(int(hours),24) + if int(hours) > 24: + days = 0 + days,hours = divmod(int(hours),24) + if int(hours) == 24 and int(minutes) > 0: + days += 1 + hours = int(hours) - 24 + hours = str(hours) + minutes = str(minutes) + if len(minutes) == 1: + minutes = "0"+minutes + if len(hours) == 1: + hours = "0"+hours + if days > 0: + if days == 1: + return hours+string+minutes+" (Tomorrow)" + else: + return hours+string+minutes+" (After "+str(days)+" days)" + elif days < 0: + if days == -1: + return hours+string+minutes+" (Yesterday)" + else: + return hours+string+minutes+" ("+str(abs(days))+" days ago)" + return hours+string+minutes + + + + + diff --git a/bot/Weather.py b/bot/Weather.py index 1fb786e8f4f..f06ed243564 100644 --- a/bot/Weather.py +++ b/bot/Weather.py @@ -1,61 +1,61 @@ -# -*- coding: cp1252 -*- -import urllib,xml.sax.handler -# S10 COMPATIABLE -def message(data): - if data["type"] == "PRIVMSG": - try: - splitdata = data["content"].lower().split(" ") - if splitdata[0] == ":weather" and len(splitdata) > 1: - data = Weather(" ".join(splitdata[1:])) - - data["conn"].privmsg(data["target"],"Weather for "+data[1]+": "+data[0]) - return True - except KeyError: - print "WUT" - else: - return -1 -def Weather(question): - question = question.replace("ä","a") - url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query="+question - opener = urllib.FancyURLopener({}) - f = opener.open(url) - data = f.read() - f.close() - bufferi = [] - seen = False - for i in data.split("\n"): - if "" in i: - stuff = cutter(i,"") - if len(stuff) > 7: - bufferi.append("Temperature: "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 19: - bufferi.append(stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 0: - bufferi.append("Weather: "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 0: - bufferi.append("Humidity: "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 0: - bufferi.append("Wind blows "+stuff) - elif "" in i: - stuff = cutter(i,"") - if len(stuff) > 9: - bufferi.append("Air pressure is "+stuff) - elif "" in i and seen == False: - seen = True - where = cutter(i,"") - if len(where) == 4: - where = "Location doesn't exist" - return [", ".join(bufferi),where] -def cutter(fullstring,cut): - fullstring = fullstring.replace(cut,"") - fullstring = fullstring.replace(" 1: + data = Weather(" ".join(splitdata[1:])) + + data["conn"].privmsg(data["target"],"Weather for "+data[1]+": "+data[0]) + return True + except KeyError: + print "WUT" + else: + return -1 +def Weather(question): + question = question.replace("ä","a") + url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query="+question + opener = urllib.FancyURLopener({}) + f = opener.open(url) + data = f.read() + f.close() + bufferi = [] + seen = False + for i in data.split("\n"): + if "" in i: + stuff = cutter(i,"") + if len(stuff) > 7: + bufferi.append("Temperature: "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 19: + bufferi.append(stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 0: + bufferi.append("Weather: "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 0: + bufferi.append("Humidity: "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 0: + bufferi.append("Wind blows "+stuff) + elif "" in i: + stuff = cutter(i,"") + if len(stuff) > 9: + bufferi.append("Air pressure is "+stuff) + elif "" in i and seen == False: + seen = True + where = cutter(i,"") + if len(where) == 4: + where = "Location doesn't exist" + return [", ".join(bufferi),where] +def cutter(fullstring,cut): + fullstring = fullstring.replace(cut,"") + fullstring = fullstring.replace(" 11: #Longer than normal, presume troll. - youtube_url.replace(cut_down,cut_down[:11]) - elif len(cut_down) < 11: #Shorter than normal - pass - except IndexError: - return "Reflex: Where's the watch?v=?" - first_two = cut_down[0:2] - try: - if no_absolute_paths: - tiedosto = open("YTCache/"+first_two+".tcc","r") - else: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","r") - except: - prev_dict = {} - else: - try: - prev_dict = pickle.load(tiedosto) - except EOFError: # Cache is corrupt - os.remove(directory+tiedosto.name) - print "REMOVED CORRUPT CACHE: "+tiedosto.name - prev_dict = {} - tiedosto.close() # I think this should belong here. - if cut_down in prev_dict.keys(): - return prev_dict[cut_down] - else: - pass - try: - if no_absolute_paths: - tiedosto = open("YTCache/"+first_two+".tcc","w") - else: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") - except IOError,error: - if len(prev_dict.keys()) > 0: - try: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - except IOError: - if did_tell == False: - did_tell = True - return "COULD NOT ACCESS FILE "+first_two+".tcc! The next time you run this link, it checks it through the web" - Do_not_open = False - else: - did_tell = False - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - else: - pass - return "Very odd error occurred: " + str(error) - youtube_url = youtube_url.replace("http//","http://") - if youtube_url.lower()[0:7] != "http://" and youtube_url[0:4] == "www.": - youtube_url = "http://" + youtube_url - if youtube_url.count("/") + youtube_url.count("\\") < 3: - if len(prev_dict.keys()) > 0: - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - else: - pass - return "Reflex: Video cannot exist" - else: - if "http://" in youtube_url[0:12].lower() and youtube_url[0:7].lower() != "http://": - youtube_url = youtube_url[youtube_url.find("http://"):] - elif youtube_url[0:7].lower() != "http://": - if len(prev_dict.keys()) > 0: - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - return "Reflex: Incorrect link start" - if "?feature=player_embedded&" in youtube_url: - youtube_url = youtube_url.replace("?feature=player_embedded&","?") - try: - website = urlopen(youtube_url) - except: - if len(prev_dict.keys()) > 0: - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - else: - pass - return "Reflex: Incorrect link!" - for i in website.readlines(): - if i.count('',contentvar)] - if "&quot;" in result: - result = result.replace("&quot;",'"') - else: - pass - if "&amp;" in result: - result = result.replace("&amp;","&") - else: - pass - if "&#39;" in result: - result = result.replace("&#39;","'") - else: - pass - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - prev_dict[cut_down] = result - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - return result - if Do_not_open == True: - tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case - prev_dict[cut_down] = "No title for video, Removed / Needs Age verification / Does not exist" - pickle.dump(prev_dict,tiedosto) - tiedosto.close() - return "No title for video, Removed / Needs age verification / Does not exist" +from urllib2 import urlopen +import os +import pickle +from CORE_DATA import directory,no_absolute_paths +global did_tell, no_absolute_paths +no_absolute_paths = True +did_tell = False +def YTCV4(youtube_url,cache=1,debug=0): + global did_tell, no_absolute_paths + Do_not_open = True + __doc__ = "Cache does not affect anything, it's legacy for skibot." + try: + cut_down = youtube_url.split("watch?v=")[1].split("&")[0] + if len(cut_down) > 11: #Longer than normal, presume troll. + youtube_url.replace(cut_down,cut_down[:11]) + elif len(cut_down) < 11: #Shorter than normal + pass + except IndexError: + return "Reflex: Where's the watch?v=?" + first_two = cut_down[0:2] + try: + if no_absolute_paths: + tiedosto = open("YTCache/"+first_two+".tcc","r") + else: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","r") + except: + prev_dict = {} + else: + try: + prev_dict = pickle.load(tiedosto) + except EOFError: # Cache is corrupt + os.remove(directory+tiedosto.name) + print "REMOVED CORRUPT CACHE: "+tiedosto.name + prev_dict = {} + tiedosto.close() # I think this should belong here. + if cut_down in prev_dict.keys(): + return prev_dict[cut_down] + else: + pass + try: + if no_absolute_paths: + tiedosto = open("YTCache/"+first_two+".tcc","w") + else: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") + except IOError,error: + if len(prev_dict.keys()) > 0: + try: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + except IOError: + if did_tell == False: + did_tell = True + return "COULD NOT ACCESS FILE "+first_two+".tcc! The next time you run this link, it checks it through the web" + Do_not_open = False + else: + did_tell = False + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + else: + pass + return "Very odd error occurred: " + str(error) + youtube_url = youtube_url.replace("http//","http://") + if youtube_url.lower()[0:7] != "http://" and youtube_url[0:4] == "www.": + youtube_url = "http://" + youtube_url + if youtube_url.count("/") + youtube_url.count("\\") < 3: + if len(prev_dict.keys()) > 0: + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + else: + pass + return "Reflex: Video cannot exist" + else: + if "http://" in youtube_url[0:12].lower() and youtube_url[0:7].lower() != "http://": + youtube_url = youtube_url[youtube_url.find("http://"):] + elif youtube_url[0:7].lower() != "http://": + if len(prev_dict.keys()) > 0: + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + return "Reflex: Incorrect link start" + if "?feature=player_embedded&" in youtube_url: + youtube_url = youtube_url.replace("?feature=player_embedded&","?") + try: + website = urlopen(youtube_url) + except: + if len(prev_dict.keys()) > 0: + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + else: + pass + return "Reflex: Incorrect link!" + for i in website.readlines(): + if i.count('',contentvar)] + if "&quot;" in result: + result = result.replace("&quot;",'"') + else: + pass + if "&amp;" in result: + result = result.replace("&amp;","&") + else: + pass + if "&#39;" in result: + result = result.replace("&#39;","'") + else: + pass + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + prev_dict[cut_down] = result + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + return result + if Do_not_open == True: + tiedosto = open(directory+"YTCache/"+first_two+".tcc","w") #This is a Just In Case + prev_dict[cut_down] = "No title for video, Removed / Needs Age verification / Does not exist" + pickle.dump(prev_dict,tiedosto) + tiedosto.close() + return "No title for video, Removed / Needs age verification / Does not exist" diff --git a/bot/_____Readme.txt b/bot/_____Readme.txt index 1c56f9ad128..acbf66af7db 100644 --- a/bot/_____Readme.txt +++ b/bot/_____Readme.txt @@ -1,74 +1,74 @@ -/// Adminhelp relay IRC bot setup guide -/// CC_Nanotrasen bot created by Skibiliano and distributed under the CC-BY-SA 3.0 license -/// All derivative works of this bot must properly credit Skibiliano as the original author -/// Big thanks to Skibiliano his bot and allowing distribution, and to BS12 for sharing their code for making use of it ingame - -QUESTION: What does this bot do? -ANSWER: It, in conjunction with BYOND, relays adminhelps to a designated channel, along with various extra functions that can be accessed by saying !help in the same channel/in a query with the bot. - -Some basic info before you set this up: -CC_Nanotrasen is coded in python 2.6 and requires a serverside installation of python 2.6 (obtainable at http://www.python.org/getit/releases/2.6/) -- Python MUST BE installed to the same directory as the .dmb you are using to host your server/server config folder -- CC_Nanotrasen supports, but does not require, Psyco (obtainable at http://psyco.sourceforge.net/download.html) which increases the speed 20-30% and slightly increases RAM usage - -Now that that's out of the way, I'll teach you how to set this up. - -BOT CONFIG: -Move everything in this folder (this file noninclusive) to the same folder as the hosting server (where your .dmb, config folder, and python are installed) -Open CORE_DATA.py with a text editor of your choice (recommended to be notepad++ or notepad) -You should see 14 lines of code which look like - Name = "CC_NanoTrasen" #The name he uses to connect - no_absolute_paths = True #Do not change this. - debug_on = False - SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, must be lowercase - DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False - directory = "BOT DIRECTORY GOES HERE/" #make sure to keep the "/" at the end - version = "TG CC-BY-SA 6" - Network = 'irc.server.goes.here' #e.g. "irc.rizon.net" - channel = "#CHANNEL GOES HERE" #what channel you want the bot in - channels = ["#CHANNEL GOES HERE","#ALSO ANOTHER CHANNEL GOES HERE IF YOU WANT"] #same as above - greeting = "Welcome!" #what he says when a person he hasn't seen before joins - prefix = "!" #prefix for bot commands - Port = 7000 -There are some basic comments besides every important config option in here, but I'll summarize them in detail -NAME - The name the bot assumes when it connects to IRC, so in this example it would join the IRC under the nickname "CC_Nanotrasen" -SNAME - A list of secondary names, with commas, that the bot will respond to for commands (for example, this setup will allow the bot to respond to "nt, tell quarxink he's a terrible writer") -DIRECTORY - The directory of the bot files, dmb, python, and config folder IN FORWARD SLASHES, WITH FORWARD SLASH AT THE END(for example, I host my test server from "c:\tgstation\tgstation13\tgstation.dmb" so for me the line would say directory = "c:/tgstation/tgstation13/") -NETWORK - The IRC server it will connect to, such as "irc.rizon.net" -CHANNEL/CHANNELS - what channel the bot will join (channels allows for multiple channel connections, in the same formatting as SName separates nicknames) -GREETING - CC_Nanotrasen will store the names of people it has seen before, but when a nickname joins that it hasn't seen before it will greet that person with whatever message is put in this -PREFIX = What character/string will be placed before commands for the bot (so if you changed this to "$", you would pull up the bot's help menu by saying $help instead of !help) -PORT - What port to connect to for the IRC server (if you are unsure of what port to use, most IRC clients will show you what port you are connecting to) - -Once you have that ready, you're on to step two. -Open up the config folder in your install dir, and open config.txt -Scroll to the bottom, right below #FORBID_SINGULO_POSSESSION should be - ##Remove the # mark if you are going to use the SVN irc bot to relay adminhelps - #USEIRCBOT -Just remove the "#" in front of USEIRCBOT (you don't even have to recompile your DMB! - -Got that all ready to go? Good, it's time for step three. -Open Dream Daemon (that thing you use when you host) -On the bottom of the window you should see port, security, and visibility. -Change security to "Trusted" - -Congratulations, you've set up this bot! -A few things to note as far as features: -Use !help to list most commands for the bot. -You can leave notes for other users! Just say "[bot name], tell [other user's name] [message]" - So let's say you wonder if I'm going to jump in to your IRC ever and you want to tell me this readme was horrible, you would say "Nano, tell Quarxink Your readme was horrible" - -TROUBLESHOOTING: -Attempting to run the bot gives me an error about encoding.utf-8. - You've probably installed python to a separate folder than the bot/server, move python's files over and it should run fine - -It's telling me connection refused when someone adminhelps. - You've moved the bot to a separate folder from the nudge script, most likely. - -BYOND asks me on any restart if I want to allow nudge.py to run. - Set security to trusted in Dream Daemon - - - - +/// Adminhelp relay IRC bot setup guide +/// CC_Nanotrasen bot created by Skibiliano and distributed under the CC-BY-SA 3.0 license +/// All derivative works of this bot must properly credit Skibiliano as the original author +/// Big thanks to Skibiliano his bot and allowing distribution, and to BS12 for sharing their code for making use of it ingame + +QUESTION: What does this bot do? +ANSWER: It, in conjunction with BYOND, relays adminhelps to a designated channel, along with various extra functions that can be accessed by saying !help in the same channel/in a query with the bot. + +Some basic info before you set this up: +CC_Nanotrasen is coded in python 2.6 and requires a serverside installation of python 2.6 (obtainable at http://www.python.org/getit/releases/2.6/) +- Python MUST BE installed to the same directory as the .dmb you are using to host your server/server config folder +- CC_Nanotrasen supports, but does not require, Psyco (obtainable at http://psyco.sourceforge.net/download.html) which increases the speed 20-30% and slightly increases RAM usage + +Now that that's out of the way, I'll teach you how to set this up. + +BOT CONFIG: +Move everything in this folder (this file noninclusive) to the same folder as the hosting server (where your .dmb, config folder, and python are installed) +Open CORE_DATA.py with a text editor of your choice (recommended to be notepad++ or notepad) +You should see 14 lines of code which look like + Name = "CC_NanoTrasen" #The name he uses to connect + no_absolute_paths = True #Do not change this. + debug_on = False + SName = ["cc","nt","trasen","nano","nanotrasen"] #Other names he will respond to, must be lowercase + DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS = False + directory = "BOT DIRECTORY GOES HERE/" #make sure to keep the "/" at the end + version = "TG CC-BY-SA 6" + Network = 'irc.server.goes.here' #e.g. "irc.rizon.net" + channel = "#CHANNEL GOES HERE" #what channel you want the bot in + channels = ["#CHANNEL GOES HERE","#ALSO ANOTHER CHANNEL GOES HERE IF YOU WANT"] #same as above + greeting = "Welcome!" #what he says when a person he hasn't seen before joins + prefix = "!" #prefix for bot commands + Port = 7000 +There are some basic comments besides every important config option in here, but I'll summarize them in detail +NAME - The name the bot assumes when it connects to IRC, so in this example it would join the IRC under the nickname "CC_Nanotrasen" +SNAME - A list of secondary names, with commas, that the bot will respond to for commands (for example, this setup will allow the bot to respond to "nt, tell quarxink he's a terrible writer") +DIRECTORY - The directory of the bot files, dmb, python, and config folder IN FORWARD SLASHES, WITH FORWARD SLASH AT THE END(for example, I host my test server from "c:\tgstation\tgstation13\tgstation.dmb" so for me the line would say directory = "c:/tgstation/tgstation13/") +NETWORK - The IRC server it will connect to, such as "irc.rizon.net" +CHANNEL/CHANNELS - what channel the bot will join (channels allows for multiple channel connections, in the same formatting as SName separates nicknames) +GREETING - CC_Nanotrasen will store the names of people it has seen before, but when a nickname joins that it hasn't seen before it will greet that person with whatever message is put in this +PREFIX = What character/string will be placed before commands for the bot (so if you changed this to "$", you would pull up the bot's help menu by saying $help instead of !help) +PORT - What port to connect to for the IRC server (if you are unsure of what port to use, most IRC clients will show you what port you are connecting to) + +Once you have that ready, you're on to step two. +Open up the config folder in your install dir, and open config.txt +Scroll to the bottom, right below #FORBID_SINGULO_POSSESSION should be + ##Remove the # mark if you are going to use the SVN irc bot to relay adminhelps + #USEIRCBOT +Just remove the "#" in front of USEIRCBOT (you don't even have to recompile your DMB! + +Got that all ready to go? Good, it's time for step three. +Open Dream Daemon (that thing you use when you host) +On the bottom of the window you should see port, security, and visibility. +Change security to "Trusted" + +Congratulations, you've set up this bot! +A few things to note as far as features: +Use !help to list most commands for the bot. +You can leave notes for other users! Just say "[bot name], tell [other user's name] [message]" + So let's say you wonder if I'm going to jump in to your IRC ever and you want to tell me this readme was horrible, you would say "Nano, tell Quarxink Your readme was horrible" + +TROUBLESHOOTING: +Attempting to run the bot gives me an error about encoding.utf-8. + You've probably installed python to a separate folder than the bot/server, move python's files over and it should run fine + +It's telling me connection refused when someone adminhelps. + You've moved the bot to a separate folder from the nudge script, most likely. + +BYOND asks me on any restart if I want to allow nudge.py to run. + Set security to trusted in Dream Daemon + + + + If you have any requests, suggestions, or issues not covered by this guide, I can be contacted as Quarxink at #coderbus on irc.rizon.net (If I don't respond, leave me a query with your problem and how to reach you [preferably an email address, steam, other irc channel, or aim/msn]) \ No newline at end of file diff --git a/bot/gen_fml.py b/bot/gen_fml.py index 358fac8700e..f523a4add83 100644 --- a/bot/gen_fml.py +++ b/bot/gen_fml.py @@ -1,15 +1,15 @@ -from FMLformatter import formatter -from urllib2 import urlopen -try: - from hashlib import md5 -except: - from md5 import md5 -from save_load import save,load -import CORE_DATA -directory = CORE_DATA.directory -FML = urlopen("http://www.fmylife.com/random") -formatted = formatter(FML.read().split("\n")) -for Quote in formatted: - exact = Quote[:Quote.find("#")] -# print exact - save(directory+"fmlquotes/"+md5(exact).hexdigest()+".txt",exact) +from FMLformatter import formatter +from urllib2 import urlopen +try: + from hashlib import md5 +except: + from md5 import md5 +from save_load import save,load +import CORE_DATA +directory = CORE_DATA.directory +FML = urlopen("http://www.fmylife.com/random") +formatted = formatter(FML.read().split("\n")) +for Quote in formatted: + exact = Quote[:Quote.find("#")] +# print exact + save(directory+"fmlquotes/"+md5(exact).hexdigest()+".txt",exact) diff --git a/bot/htmltagremove.py b/bot/htmltagremove.py index c9d13086d51..4ecd9b6cd02 100644 --- a/bot/htmltagremove.py +++ b/bot/htmltagremove.py @@ -1,28 +1,28 @@ -def htr(data): - ignore = False - if type(data) == list: - b = [] - for olio in data: - tempolio = "" - for letter in olio: - if letter == "<": - ignore = True - else: - pass - if ignore != True: - tempolio += letter - else: - pass - if letter == ">": - ignore = False - else: - pass - tempolio = tempolio.replace("\t","") - if len(tempolio) == 0: - pass - elif len(tempolio.replace(" ","")) == 0: - pass - else: - b.append(tempolio) - #Finetuning - return b +def htr(data): + ignore = False + if type(data) == list: + b = [] + for olio in data: + tempolio = "" + for letter in olio: + if letter == "<": + ignore = True + else: + pass + if ignore != True: + tempolio += letter + else: + pass + if letter == ">": + ignore = False + else: + pass + tempolio = tempolio.replace("\t","") + if len(tempolio) == 0: + pass + elif len(tempolio.replace(" ","")) == 0: + pass + else: + b.append(tempolio) + #Finetuning + return b diff --git a/bot/irchat.py b/bot/irchat.py index 46ae2d4caf7..467d96e8b85 100644 --- a/bot/irchat.py +++ b/bot/irchat.py @@ -1,94 +1,94 @@ -import socket -import time -class IRC: - queue = [] - partial = '' - def __init__ ( self, network, port, name, hostName, serverName, realName ): - self.network = network - self.port = port - self.hostName = hostName - self.serverName = serverName - self.realName = realName - self.socket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM ) - self.socket.connect ( ( self.network, self.port ) ) - self.address = self.socket.getpeername() - self.nick ( name ) - self.send ( 'USER ' + self.name + ' ' + self.serverName + ' ' + self.hostName + ' :' + self.realName ) - def quit ( self ): - self.send ( 'QUIT' ) - self.socket.close() - def send ( self, text ): - count = 0 - try: - count += 1 - self.socket.send ( text + '\r\n' ) - except: - if count > 10: - time.sleep(1) - self.socket.send(text+'\r\n') - else: - count = 0 - def nick ( self, name ): - self.name = name - self.send ( 'NICK ' + self.name ) - def addressquery(self): - print self.address - aha = socket.gethostbyaddr(str(self.address[0])) - return aha - def recv ( self, size = 2048 ): - commands = self.socket.recv ( size ).split ( '\r\n' ) - if len ( self.partial ): - commands [ 0 ] = self.partial + commands [ 0 ] - self.partial = '' - if len ( commands [ -1 ] ): - self.partial = commands [ -1 ] - self.queue.extend ( commands [ :-1 ] ) - else: - self.queue.extend ( commands ) - def retrieve ( self ): - if len ( self.queue ): - command = self.queue [ 0 ] - self.queue.pop ( 0 ) - return command - else: - return False - def dismantle ( self, command ): - if command: - source = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 0 ] - parameters = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 1: ] - if len(parameters) > 0: - if not len ( parameters [ -1 ] ): - parameters.pop() - if command.count ( ':' ) > 1: - parameters.append(command[command.find(":",command.find(":")+1)+1:]) - return source, parameters - def privmsg ( self, destination, message ): - self.send ( 'PRIVMSG ' + destination + ' :' + message ) - def handshake(self,hexstring): - self.send("PONG :"+hexstring) - def notice ( self, destination, message ): - self.send ( 'NOTICE ' + destination + ' :' + message ) - def join ( self, channel ): - self.send ( 'JOIN ' + channel ) - def part ( self, channel ): - self.send ( 'PART ' + channel ) - def topic ( self, channel, topic = '' ): - self.send ( 'TOPIC ' + channel + ' ' + topic ) - def names ( self, channel ): - self.send ( 'NAMES ' + channel ) - def invite ( self, nick, channel ): - self.send ( 'INVITE ' + nick + ' ' + channel ) - def mode ( self, channel, mode, nick = '' ): - self.send ( 'MODE ' + channel + ' ' + mode + ' ' + nick ) - def banon(self,channel,name): - self.mode(channel,"+b",name) - def banoff(self,channel,name): - self.mode(channel,"-b",name) - def kick ( self, channel, nick, reason = '' ): - self.send ( 'KICK ' + channel + ' ' + nick + ' ' + reason ) - def who ( self, pattern ): - self.send ( 'WHO ' + pattern ) - def whois ( self, nick ): - self.send ( 'WHOIS ' + nick ) - def whowas ( self, nick ): - self.send ( 'WHOWAS ' + nick ) +import socket +import time +class IRC: + queue = [] + partial = '' + def __init__ ( self, network, port, name, hostName, serverName, realName ): + self.network = network + self.port = port + self.hostName = hostName + self.serverName = serverName + self.realName = realName + self.socket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM ) + self.socket.connect ( ( self.network, self.port ) ) + self.address = self.socket.getpeername() + self.nick ( name ) + self.send ( 'USER ' + self.name + ' ' + self.serverName + ' ' + self.hostName + ' :' + self.realName ) + def quit ( self ): + self.send ( 'QUIT' ) + self.socket.close() + def send ( self, text ): + count = 0 + try: + count += 1 + self.socket.send ( text + '\r\n' ) + except: + if count > 10: + time.sleep(1) + self.socket.send(text+'\r\n') + else: + count = 0 + def nick ( self, name ): + self.name = name + self.send ( 'NICK ' + self.name ) + def addressquery(self): + print self.address + aha = socket.gethostbyaddr(str(self.address[0])) + return aha + def recv ( self, size = 2048 ): + commands = self.socket.recv ( size ).split ( '\r\n' ) + if len ( self.partial ): + commands [ 0 ] = self.partial + commands [ 0 ] + self.partial = '' + if len ( commands [ -1 ] ): + self.partial = commands [ -1 ] + self.queue.extend ( commands [ :-1 ] ) + else: + self.queue.extend ( commands ) + def retrieve ( self ): + if len ( self.queue ): + command = self.queue [ 0 ] + self.queue.pop ( 0 ) + return command + else: + return False + def dismantle ( self, command ): + if command: + source = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 0 ] + parameters = command.split ( ':' ) [ 1 ].split ( ' ' ) [ 1: ] + if len(parameters) > 0: + if not len ( parameters [ -1 ] ): + parameters.pop() + if command.count ( ':' ) > 1: + parameters.append(command[command.find(":",command.find(":")+1)+1:]) + return source, parameters + def privmsg ( self, destination, message ): + self.send ( 'PRIVMSG ' + destination + ' :' + message ) + def handshake(self,hexstring): + self.send("PONG :"+hexstring) + def notice ( self, destination, message ): + self.send ( 'NOTICE ' + destination + ' :' + message ) + def join ( self, channel ): + self.send ( 'JOIN ' + channel ) + def part ( self, channel ): + self.send ( 'PART ' + channel ) + def topic ( self, channel, topic = '' ): + self.send ( 'TOPIC ' + channel + ' ' + topic ) + def names ( self, channel ): + self.send ( 'NAMES ' + channel ) + def invite ( self, nick, channel ): + self.send ( 'INVITE ' + nick + ' ' + channel ) + def mode ( self, channel, mode, nick = '' ): + self.send ( 'MODE ' + channel + ' ' + mode + ' ' + nick ) + def banon(self,channel,name): + self.mode(channel,"+b",name) + def banoff(self,channel,name): + self.mode(channel,"-b",name) + def kick ( self, channel, nick, reason = '' ): + self.send ( 'KICK ' + channel + ' ' + nick + ' ' + reason ) + def who ( self, pattern ): + self.send ( 'WHO ' + pattern ) + def whois ( self, nick ): + self.send ( 'WHOIS ' + nick ) + def whowas ( self, nick ): + self.send ( 'WHOWAS ' + nick ) diff --git a/bot/linereader.py b/bot/linereader.py index cef804935b8..469b6e1444b 100644 --- a/bot/linereader.py +++ b/bot/linereader.py @@ -1,43 +1,43 @@ -import os -directory = "" -raw = os.listdir(directory) -extract = [] -for i in raw: - if i[-3:] == ".py": - extract.append(i) -toc = 0 -toc3 = 0 -toc2 = 0 -print len(extract),"Files" -lista = [] -for ob in extract: - count3 = 0 - if directory == "": - tiedosto = open(ob,"r") - tiedosto2 = open(ob,"r") - count3 += os.path.getsize(ob) - toc3 += count3 - else: - tiedosto = open(directory+"/"+ob,"r") - tiedosto2 = open(directory+"/"+ob,"r") - count3 += os.path.getsize(directory+"/"+ob) - toc3 += count3 - count = 0 - count2 = 0 - line = tiedosto.readline() - while line != "": - count += 1 - toc += 1 - line = tiedosto.readline() - count2 += len(tiedosto2.read()) - toc2 += count2 - lista.append([count,count2,ob,count3]) - tiedosto.close() - tiedosto2.close() -print toc,"Lines in total" -print toc2,"Letters in total" -print toc3,"Bytes in total" - -for linecount, lettercount, filename, bytecount in lista: - print str(linecount)+" Lines (%s%%) || "%(str(round((float(linecount)/toc)*100,1))),str(lettercount)+" Letters (%s%%) in file " %(str(round((float(lettercount)/toc2)*100,1)))+filename - print str(bytecount) + " Bytes (%s%%) "%(str(round((float(bytecount)/toc3)*100,1))) +import os +directory = "" +raw = os.listdir(directory) +extract = [] +for i in raw: + if i[-3:] == ".py": + extract.append(i) +toc = 0 +toc3 = 0 +toc2 = 0 +print len(extract),"Files" +lista = [] +for ob in extract: + count3 = 0 + if directory == "": + tiedosto = open(ob,"r") + tiedosto2 = open(ob,"r") + count3 += os.path.getsize(ob) + toc3 += count3 + else: + tiedosto = open(directory+"/"+ob,"r") + tiedosto2 = open(directory+"/"+ob,"r") + count3 += os.path.getsize(directory+"/"+ob) + toc3 += count3 + count = 0 + count2 = 0 + line = tiedosto.readline() + while line != "": + count += 1 + toc += 1 + line = tiedosto.readline() + count2 += len(tiedosto2.read()) + toc2 += count2 + lista.append([count,count2,ob,count3]) + tiedosto.close() + tiedosto2.close() +print toc,"Lines in total" +print toc2,"Letters in total" +print toc3,"Bytes in total" + +for linecount, lettercount, filename, bytecount in lista: + print str(linecount)+" Lines (%s%%) || "%(str(round((float(linecount)/toc)*100,1))),str(lettercount)+" Letters (%s%%) in file " %(str(round((float(lettercount)/toc2)*100,1)))+filename + print str(bytecount) + " Bytes (%s%%) "%(str(round((float(bytecount)/toc3)*100,1))) diff --git a/bot/nudge.py b/bot/nudge.py index e39ebc314c5..8c20c4c735b 100644 --- a/bot/nudge.py +++ b/bot/nudge.py @@ -1,39 +1,39 @@ -import sys,pickle,socket, CORE_DATA -#def pack(): -# path = "/home/ski/Nanotrasen/message.txt" -# ip = sys.argv[1] -# dictionary = {"ip":ip,"data":1} -# try: -# targetfile = open(path,"r") -# except IOError: -# targetfile = open(path,"w") -# pickle.dump(dictionary,targetfile) -# targetfile.close() -# nudge() -# else: -# targetfile.close() #Professionals, have standards. -# pass -def pack(): - ip = sys.argv[1] - try: - data = sys.argv[2:] #The rest of the arguments is data - except: - data = "NO DATA SPECIFIED" - dictionary = {"ip":ip,"data":data} - pickled = pickle.dumps(dictionary) - nudge(pickled) -def nudge(data): - if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: - pass - else: - HOST = "localhost" - PORT = 45678 - size = 1024 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((HOST,PORT)) - s.send(data) - s.close() - -if __name__ == "__main__" and len(sys.argv) > 1: # If not imported and more than one argument - pack() - +import sys,pickle,socket, CORE_DATA +#def pack(): +# path = "/home/ski/Nanotrasen/message.txt" +# ip = sys.argv[1] +# dictionary = {"ip":ip,"data":1} +# try: +# targetfile = open(path,"r") +# except IOError: +# targetfile = open(path,"w") +# pickle.dump(dictionary,targetfile) +# targetfile.close() +# nudge() +# else: +# targetfile.close() #Professionals, have standards. +# pass +def pack(): + ip = sys.argv[1] + try: + data = sys.argv[2:] #The rest of the arguments is data + except: + data = "NO DATA SPECIFIED" + dictionary = {"ip":ip,"data":data} + pickled = pickle.dumps(dictionary) + nudge(pickled) +def nudge(data): + if CORE_DATA.DISABLE_ALL_NON_MANDATORY_SOCKET_CONNECTIONS: + pass + else: + HOST = "localhost" + PORT = 45678 + size = 1024 + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((HOST,PORT)) + s.send(data) + s.close() + +if __name__ == "__main__" and len(sys.argv) > 1: # If not imported and more than one argument + pack() + diff --git a/bot/save_load.py b/bot/save_load.py index 34eac990726..1cd058763a8 100644 --- a/bot/save_load.py +++ b/bot/save_load.py @@ -1,24 +1,24 @@ -import pickle -def save(filename,data,dnrw=0): - if dnrw == 1: - try: - tiedosto = open(filename,"r") - except: - tiedosto = open(filename,"w") - else: - return False - else: - tiedosto = open(filename,"w") - - if "http//" in data: - data = data.replace("http//","http://") - pickle.dump(data,tiedosto) - tiedosto.close() -def load(filename): - try: - tiedosto = open(filename,"r") - except IOError: - return "ERROR ERROR ERROR ERR" - a = pickle.load(tiedosto) - tiedosto.close() - return a +import pickle +def save(filename,data,dnrw=0): + if dnrw == 1: + try: + tiedosto = open(filename,"r") + except: + tiedosto = open(filename,"w") + else: + return False + else: + tiedosto = open(filename,"w") + + if "http//" in data: + data = data.replace("http//","http://") + pickle.dump(data,tiedosto) + tiedosto.close() +def load(filename): + try: + tiedosto = open(filename,"r") + except IOError: + return "ERROR ERROR ERROR ERR" + a = pickle.load(tiedosto) + tiedosto.close() + return a diff --git a/bot/some_but_not_all_2.py b/bot/some_but_not_all_2.py index 211eef9fd52..34627abd129 100644 --- a/bot/some_but_not_all_2.py +++ b/bot/some_but_not_all_2.py @@ -1,20 +1,20 @@ -def sbna2(only_one,one_of_these,data): - if type(only_one) != list: - only_one = list(only_one) - if type(data) != list: - data = data.split(" ") - count = 0 - for datoid in only_one: - if datoid in data and count >= 1: - return False - elif datoid in data: - count += 1 - pass - else: - pass - if count == 0: - return False - for datoid in one_of_these: - if datoid in data: - return True - return False +def sbna2(only_one,one_of_these,data): + if type(only_one) != list: + only_one = list(only_one) + if type(data) != list: + data = data.split(" ") + count = 0 + for datoid in only_one: + if datoid in data and count >= 1: + return False + elif datoid in data: + count += 1 + pass + else: + pass + if count == 0: + return False + for datoid in one_of_these: + if datoid in data: + return True + return False diff --git a/bot/xkcdparser.py b/bot/xkcdparser.py index 24d6f514165..7ad033d0b82 100644 --- a/bot/xkcdparser.py +++ b/bot/xkcdparser.py @@ -1,40 +1,40 @@ -from urllib2 import urlopen -from json import loads -from pickle import dump,load -from CORE_DATA import no_absolute_paths -def xkcd(link): - try: - filename = link[link.find("xkcd.com")+9:].replace("/","").replace("\\","") - if no_absolute_paths: - tiedosto = open("xkcdcache/"+filename,"r") - else: - tiedosto = open(directory+"xkcdcache/"+filename,"r") - except: - try: - if no_absolute_paths: - tiedosto = open("xkcdcache/"+filename,"w") - else: - tiedosto = open(directory+"xkcdcache/"+filename,"w") - except IOError: - return "NOTHING" - else: - try: - return load(tiedosto) - except EOFError: - tiedosto = open("xkcdcache/"+filename,"w") - pass #Corrupt cache, moving on. - if link[-1] == "/" or link[-1] == "\\": #Ending is fine. - link += "info.0.json" - else: - link += "/info.0.json" - try: - data = urlopen(link).read() - except: - return "NOTHING" - try: - newdata = loads(data)["title"] - dump(newdata,tiedosto) - return newdata - except: - return "NOTHING" - +from urllib2 import urlopen +from json import loads +from pickle import dump,load +from CORE_DATA import no_absolute_paths +def xkcd(link): + try: + filename = link[link.find("xkcd.com")+9:].replace("/","").replace("\\","") + if no_absolute_paths: + tiedosto = open("xkcdcache/"+filename,"r") + else: + tiedosto = open(directory+"xkcdcache/"+filename,"r") + except: + try: + if no_absolute_paths: + tiedosto = open("xkcdcache/"+filename,"w") + else: + tiedosto = open(directory+"xkcdcache/"+filename,"w") + except IOError: + return "NOTHING" + else: + try: + return load(tiedosto) + except EOFError: + tiedosto = open("xkcdcache/"+filename,"w") + pass #Corrupt cache, moving on. + if link[-1] == "/" or link[-1] == "\\": #Ending is fine. + link += "info.0.json" + else: + link += "/info.0.json" + try: + data = urlopen(link).read() + except: + return "NOTHING" + try: + newdata = loads(data)["title"] + dump(newdata,tiedosto) + return newdata + except: + return "NOTHING" + diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index a5e15639723..e5765075c25 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -1,231 +1,231 @@ -/* -Quick overview: - -Pipes combine to form pipelines -Pipelines and other atmospheric objects combine to form pipe_networks - Note: A single pipe_network represents a completely open space - -Pipes -> Pipelines -Pipelines + Other Objects -> Pipe network - -*/ -/obj/machinery/atmospherics - anchored = TRUE - idle_power_usage = 0 - active_power_usage = 0 - power_channel = ENVIRON - var/nodealert = 0 - var/power_rating //the maximum amount of power the machine can use to do work, affects how powerful the machine is, in Watts - - unacidable = TRUE - layer = ATMOS_LAYER - plane = PLATING_PLANE - - var/pipe_flags = PIPING_DEFAULT_LAYER_ONLY // Allow other layers by exception basis. - var/connect_types = CONNECT_TYPE_REGULAR - var/piping_layer = PIPING_LAYER_DEFAULT // This will replace icon_connect_type at some point ~Leshana - var/icon_connect_type = "" //"-supply" or "-scrubbers" - var/construction_type = null // Type path of the pipe item when this is deconstructed. - var/pipe_state // icon_state as a pipe item - - var/initialize_directions = 0 - var/pipe_color - - var/global/datum/pipe_icon_manager/icon_manager - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - -/obj/machinery/atmospherics/New(loc, newdir) - ..() - if(!icon_manager) - icon_manager = new() - if(!isnull(newdir)) - set_dir(newdir) - if(!pipe_color) - pipe_color = color - color = null - - if(!pipe_color_check(pipe_color)) - pipe_color = null - init_dir() - -/obj/machinery/atmospherics/examine_icon() - return icon(icon=initial(icon),icon_state=initial(icon_state)) - -// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes. -/obj/machinery/atmospherics/proc/init_dir() - return - -// Get ALL initialize_directions - Some types (HE pipes etc) combine two vars together for this. -/obj/machinery/atmospherics/proc/get_init_dirs() - return initialize_directions - -// Get the direction each node is facing to connect. -// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. -/obj/machinery/atmospherics/proc/get_node_connect_dirs() - return - -// Initializes nodes by looking at neighboring atmospherics machinery to connect to. -// When we're being constructed at runtime, atmos_init() is called by the construction code. -// When dynamically loading a map atmos_init is called by the maploader (initTemplateBounds proc) -// But during initial world creation its called by the master_controller. -// TODO - Consolidate these different ways of being called once SSatoms is created. -/obj/machinery/atmospherics/proc/atmos_init() - return - -/** Check if target is an acceptable target to connect as a node from this machine. */ -/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, node_num) - return (target.initialize_directions & get_dir(target,src)) && check_connectable(target) && target.check_connectable(src) - -/** Check if this machine is willing to connect with the target machine. */ -/obj/machinery/atmospherics/proc/check_connectable(obj/machinery/atmospherics/target) - return (src.connect_types & target.connect_types) - -/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob) - if(istype(A, /obj/item/device/pipe_painter)) - return - ..() - -/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) - if(node) - if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - //underlays += icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - //underlays += icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - //underlays += icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) - -/obj/machinery/atmospherics/proc/update_underlays() - if(check_icon_cache()) - return 1 - else - return 0 - -/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) - if(!istype(icon_manager)) - if(!safety) //to prevent infinite loops - icon_manager = new() - check_icon_cache(1) - return 0 - - return 1 - -/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) - //Don't use this for standard pipes - if(!istype(node)) - return null - - return node.pipe_color - -/obj/machinery/atmospherics/process() - last_flow_rate = 0 - last_power_draw = 0 - - build_network() - -/obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - // Check to see if should be added to network. Add self if so and adjust variables appropriately. - // Note don't forget to have neighbors look as well! - - return null - -/obj/machinery/atmospherics/proc/build_network() - // Called to build a network from this node - - return null - -/obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference) - // Returns pipe_network associated with connection to reference - // Notes: should create network if necessary - // Should never return null - - return null - -/obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - // Used when two pipe_networks are combining - -/obj/machinery/atmospherics/proc/return_network_air(datum/pipe_network/reference) - // Return a list of gas_mixture(s) in the object - // associated with reference pipe_network for use in rebuilding the networks gases list - // Is permitted to return null - -/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) - -/obj/machinery/atmospherics/update_icon() - return null - -/obj/machinery/atmospherics/proc/can_unwrench() - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - if((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE) - return 0 - return 1 - -// Deconstruct into a pipe item. -/obj/machinery/atmospherics/proc/deconstruct() - if(QDELETED(src)) - return - if(construction_type) - var/obj/item/pipe/I = new construction_type(loc, null, null, src) - I.setPipingLayer(piping_layer) - transfer_fingerprints_to(I) - qdel(src) - -// Return a list of nodes which we should call atmos_init() and build_network() during on_construction() -/obj/machinery/atmospherics/proc/get_neighbor_nodes_for_init() - return null - -// Called on construction (i.e from pipe item) but not on initialization -/obj/machinery/atmospherics/proc/on_construction(obj_color, set_layer) - pipe_color = obj_color - setPipingLayer(set_layer) - // TODO - M.connect_types = src.connect_types - Or otherwise copy from item? Or figure it out from piping layer? - var/turf/T = get_turf(src) - level = !T.is_plating() ? 2 : 1 - atmos_init() - if(QDELETED(src)) - return // TODO - Eventually should get rid of the need for this. - build_network() - var/list/nodes = get_neighbor_nodes_for_init() - for(var/obj/machinery/atmospherics/A in nodes) - A.atmos_init() - A.build_network() - // TODO - Should we do src.build_network() before or after the nodes? - // We've historically done before, but /tg does after. TODO research if there is a difference. - -// This sets our piping layer. Hopefully its cool. -/obj/machinery/atmospherics/proc/setPipingLayer(new_layer) - if(pipe_flags & (PIPING_DEFAULT_LAYER_ONLY|PIPING_ALL_LAYER)) - new_layer = PIPING_LAYER_DEFAULT - piping_layer = new_layer - // Do it the Polaris way - switch(piping_layer) - if(PIPING_LAYER_SCRUBBER) - icon_state = "[icon_state]-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = PIPES_SCRUBBER_LAYER - icon_connect_type = "-scrubbers" - if(PIPING_LAYER_SUPPLY) - icon_state = "[icon_state]-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = PIPES_SUPPLY_LAYER - icon_connect_type = "-supply" - if(PIPING_LAYER_FUEL) - icon_state = "[icon_state]-fuel" - connect_types = CONNECT_TYPE_FUEL - layer = PIPES_FUEL_LAYER - icon_connect_type = "-fuel" - if(PIPING_LAYER_AUX) - icon_state = "[icon_state]-aux" - connect_types = CONNECT_TYPE_AUX - layer = PIPES_AUX_LAYER - icon_connect_type = "-aux" - if(pipe_flags & PIPING_ALL_LAYER) - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX - // Or if we were to do it the TG way... - // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) - // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) - // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) +/* +Quick overview: + +Pipes combine to form pipelines +Pipelines and other atmospheric objects combine to form pipe_networks + Note: A single pipe_network represents a completely open space + +Pipes -> Pipelines +Pipelines + Other Objects -> Pipe network + +*/ +/obj/machinery/atmospherics + anchored = TRUE + idle_power_usage = 0 + active_power_usage = 0 + power_channel = ENVIRON + var/nodealert = 0 + var/power_rating //the maximum amount of power the machine can use to do work, affects how powerful the machine is, in Watts + + unacidable = TRUE + layer = ATMOS_LAYER + plane = PLATING_PLANE + + var/pipe_flags = PIPING_DEFAULT_LAYER_ONLY // Allow other layers by exception basis. + var/connect_types = CONNECT_TYPE_REGULAR + var/piping_layer = PIPING_LAYER_DEFAULT // This will replace icon_connect_type at some point ~Leshana + var/icon_connect_type = "" //"-supply" or "-scrubbers" + var/construction_type = null // Type path of the pipe item when this is deconstructed. + var/pipe_state // icon_state as a pipe item + + var/initialize_directions = 0 + var/pipe_color + + var/global/datum/pipe_icon_manager/icon_manager + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + +/obj/machinery/atmospherics/New(loc, newdir) + ..() + if(!icon_manager) + icon_manager = new() + if(!isnull(newdir)) + set_dir(newdir) + if(!pipe_color) + pipe_color = color + color = null + + if(!pipe_color_check(pipe_color)) + pipe_color = null + init_dir() + +/obj/machinery/atmospherics/examine_icon() + return icon(icon=initial(icon),icon_state=initial(icon_state)) + +// This is used to set up what directions pipes will connect to. Should be called inside New() and whenever a dir changes. +/obj/machinery/atmospherics/proc/init_dir() + return + +// Get ALL initialize_directions - Some types (HE pipes etc) combine two vars together for this. +/obj/machinery/atmospherics/proc/get_init_dirs() + return initialize_directions + +// Get the direction each node is facing to connect. +// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. +/obj/machinery/atmospherics/proc/get_node_connect_dirs() + return + +// Initializes nodes by looking at neighboring atmospherics machinery to connect to. +// When we're being constructed at runtime, atmos_init() is called by the construction code. +// When dynamically loading a map atmos_init is called by the maploader (initTemplateBounds proc) +// But during initial world creation its called by the master_controller. +// TODO - Consolidate these different ways of being called once SSatoms is created. +/obj/machinery/atmospherics/proc/atmos_init() + return + +/** Check if target is an acceptable target to connect as a node from this machine. */ +/obj/machinery/atmospherics/proc/can_be_node(obj/machinery/atmospherics/target, node_num) + return (target.initialize_directions & get_dir(target,src)) && check_connectable(target) && target.check_connectable(src) + +/** Check if this machine is willing to connect with the target machine. */ +/obj/machinery/atmospherics/proc/check_connectable(obj/machinery/atmospherics/target) + return (src.connect_types & target.connect_types) + +/obj/machinery/atmospherics/attackby(atom/A, mob/user as mob) + if(istype(A, /obj/item/device/pipe_painter)) + return + ..() + +/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) + if(node) + if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + //underlays += icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + //underlays += icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + //underlays += icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) + +/obj/machinery/atmospherics/proc/update_underlays() + if(check_icon_cache()) + return 1 + else + return 0 + +/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) + if(!istype(icon_manager)) + if(!safety) //to prevent infinite loops + icon_manager = new() + check_icon_cache(1) + return 0 + + return 1 + +/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) + //Don't use this for standard pipes + if(!istype(node)) + return null + + return node.pipe_color + +/obj/machinery/atmospherics/process() + last_flow_rate = 0 + last_power_draw = 0 + + build_network() + +/obj/machinery/atmospherics/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + // Check to see if should be added to network. Add self if so and adjust variables appropriately. + // Note don't forget to have neighbors look as well! + + return null + +/obj/machinery/atmospherics/proc/build_network() + // Called to build a network from this node + + return null + +/obj/machinery/atmospherics/proc/return_network(obj/machinery/atmospherics/reference) + // Returns pipe_network associated with connection to reference + // Notes: should create network if necessary + // Should never return null + + return null + +/obj/machinery/atmospherics/proc/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + // Used when two pipe_networks are combining + +/obj/machinery/atmospherics/proc/return_network_air(datum/pipe_network/reference) + // Return a list of gas_mixture(s) in the object + // associated with reference pipe_network for use in rebuilding the networks gases list + // Is permitted to return null + +/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) + +/obj/machinery/atmospherics/update_icon() + return null + +/obj/machinery/atmospherics/proc/can_unwrench() + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + if((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE) + return 0 + return 1 + +// Deconstruct into a pipe item. +/obj/machinery/atmospherics/proc/deconstruct() + if(QDELETED(src)) + return + if(construction_type) + var/obj/item/pipe/I = new construction_type(loc, null, null, src) + I.setPipingLayer(piping_layer) + transfer_fingerprints_to(I) + qdel(src) + +// Return a list of nodes which we should call atmos_init() and build_network() during on_construction() +/obj/machinery/atmospherics/proc/get_neighbor_nodes_for_init() + return null + +// Called on construction (i.e from pipe item) but not on initialization +/obj/machinery/atmospherics/proc/on_construction(obj_color, set_layer) + pipe_color = obj_color + setPipingLayer(set_layer) + // TODO - M.connect_types = src.connect_types - Or otherwise copy from item? Or figure it out from piping layer? + var/turf/T = get_turf(src) + level = !T.is_plating() ? 2 : 1 + atmos_init() + if(QDELETED(src)) + return // TODO - Eventually should get rid of the need for this. + build_network() + var/list/nodes = get_neighbor_nodes_for_init() + for(var/obj/machinery/atmospherics/A in nodes) + A.atmos_init() + A.build_network() + // TODO - Should we do src.build_network() before or after the nodes? + // We've historically done before, but /tg does after. TODO research if there is a difference. + +// This sets our piping layer. Hopefully its cool. +/obj/machinery/atmospherics/proc/setPipingLayer(new_layer) + if(pipe_flags & (PIPING_DEFAULT_LAYER_ONLY|PIPING_ALL_LAYER)) + new_layer = PIPING_LAYER_DEFAULT + piping_layer = new_layer + // Do it the Polaris way + switch(piping_layer) + if(PIPING_LAYER_SCRUBBER) + icon_state = "[icon_state]-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = PIPES_SCRUBBER_LAYER + icon_connect_type = "-scrubbers" + if(PIPING_LAYER_SUPPLY) + icon_state = "[icon_state]-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = PIPES_SUPPLY_LAYER + icon_connect_type = "-supply" + if(PIPING_LAYER_FUEL) + icon_state = "[icon_state]-fuel" + connect_types = CONNECT_TYPE_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + if(PIPING_LAYER_AUX) + icon_state = "[icon_state]-aux" + connect_types = CONNECT_TYPE_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + if(pipe_flags & PIPING_ALL_LAYER) + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX + // Or if we were to do it the TG way... + // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) + // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) + // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index cc280d97ccb..cee94b9b5a7 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,129 +1,129 @@ -/obj/machinery/atmospherics/binary - dir = SOUTH - initialize_directions = SOUTH|NORTH - use_power = USE_POWER_IDLE - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - - var/datum/pipe_network/network1 - var/datum/pipe_network/network2 - -/obj/machinery/atmospherics/binary/New() - ..() - - air1 = new - air2 = new - - air1.volume = 200 - air2.volume = 200 - -/obj/machinery/atmospherics/binary/init_dir() - switch(dir) - if(NORTH) - initialize_directions = NORTH|SOUTH - if(SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST) - initialize_directions = EAST|WEST - if(WEST) - initialize_directions = EAST|WEST - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/binary/get_neighbor_nodes_for_init() - return list(node1, node2) - -/obj/machinery/atmospherics/binary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node1) - network1 = new_network - - else if(reference == node2) - network2 = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/binary/Destroy() - . = ..() - - if(node1) - node1.disconnect(src) - qdel(network1) - if(node2) - node2.disconnect(src) - qdel(network2) - - node1 = null - node2 = null - -/obj/machinery/atmospherics/binary/atmos_init() - if(node1 && node2) - return - - var/node2_connect = dir - var/node1_connect = turn(dir, 180) - - STANDARD_ATMOS_CHOOSE_NODE(1, node1_connect) - STANDARD_ATMOS_CHOOSE_NODE(2, node2_connect) - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/build_network() - if(!network1 && node1) - network1 = new /datum/pipe_network() - network1.normal_members += src - network1.build_network(node1, src) - - if(!network2 && node2) - network2 = new /datum/pipe_network() - network2.normal_members += src - network2.build_network(node2, src) - - -/obj/machinery/atmospherics/binary/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network1 - - if(reference==node2) - return network2 - - return null - -/obj/machinery/atmospherics/binary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network1 == old_network) - network1 = new_network - if(network2 == old_network) - network2 = new_network - - return 1 - -/obj/machinery/atmospherics/binary/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network1 == reference) - results += air1 - if(network2 == reference) - results += air2 - - return results - -/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) - if(reference==node1) - qdel(network1) - node1 = null - - else if(reference==node2) - qdel(network2) - node2 = null - - update_icon() - update_underlays() - +/obj/machinery/atmospherics/binary + dir = SOUTH + initialize_directions = SOUTH|NORTH + use_power = USE_POWER_IDLE + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + + var/datum/pipe_network/network1 + var/datum/pipe_network/network2 + +/obj/machinery/atmospherics/binary/New() + ..() + + air1 = new + air2 = new + + air1.volume = 200 + air2.volume = 200 + +/obj/machinery/atmospherics/binary/init_dir() + switch(dir) + if(NORTH) + initialize_directions = NORTH|SOUTH + if(SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST) + initialize_directions = EAST|WEST + if(WEST) + initialize_directions = EAST|WEST + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/binary/get_neighbor_nodes_for_init() + return list(node1, node2) + +/obj/machinery/atmospherics/binary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network1 = new_network + + else if(reference == node2) + network2 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/binary/Destroy() + . = ..() + + if(node1) + node1.disconnect(src) + qdel(network1) + if(node2) + node2.disconnect(src) + qdel(network2) + + node1 = null + node2 = null + +/obj/machinery/atmospherics/binary/atmos_init() + if(node1 && node2) + return + + var/node2_connect = dir + var/node1_connect = turn(dir, 180) + + STANDARD_ATMOS_CHOOSE_NODE(1, node1_connect) + STANDARD_ATMOS_CHOOSE_NODE(2, node2_connect) + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/build_network() + if(!network1 && node1) + network1 = new /datum/pipe_network() + network1.normal_members += src + network1.build_network(node1, src) + + if(!network2 && node2) + network2 = new /datum/pipe_network() + network2.normal_members += src + network2.build_network(node2, src) + + +/obj/machinery/atmospherics/binary/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network1 + + if(reference==node2) + return network2 + + return null + +/obj/machinery/atmospherics/binary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network1 == old_network) + network1 = new_network + if(network2 == old_network) + network2 = new_network + + return 1 + +/obj/machinery/atmospherics/binary/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network1 == reference) + results += air1 + if(network2 == reference) + results += air2 + + return results + +/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + qdel(network1) + node1 = null + + else if(reference==node2) + qdel(network2) + node2 = null + + update_icon() + update_underlays() + return null \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index c5fe955523e..c3f6b81ee3f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -1,152 +1,152 @@ -//node1, air1, network1 correspond to input -//node2, air2, network2 correspond to output -/obj/machinery/atmospherics/binary/circulator - name = "circulator" - desc = "A gas circulator turbine and heat exchanger." - icon = 'icons/obj/power.dmi' - icon_state = "circ-unassembled" - anchored = FALSE - unacidable = TRUE - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - - var/kinetic_efficiency = 0.04 //combined kinetic and kinetic-to-electric efficiency - var/volume_ratio = 0.2 - - var/recent_moles_transferred = 0 - var/last_heat_capacity = 0 - var/last_temperature = 0 - var/last_pressure_delta = 0 - var/last_worldtime_transfer = 0 - var/last_stored_energy_transferred = 0 - var/volume_capacity_used = 0 - var/stored_energy = 0 - var/temperature_overlay - - density = TRUE - -/obj/machinery/atmospherics/binary/circulator/New() - ..() - desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." - air1.volume = 400 - -/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() - var/datum/gas_mixture/removed - if(anchored && !(stat&BROKEN) && network1) - var/input_starting_pressure = air1.return_pressure() - var/output_starting_pressure = air2.return_pressure() - last_pressure_delta = max(input_starting_pressure - output_starting_pressure - 5, 0) - - //only circulate air if there is a pressure difference (plus 5kPa kinetic, 10kPa static friction) - if(air1.temperature > 0 && last_pressure_delta > 5) - - //Calculate necessary moles to transfer using PV = nRT - recent_moles_transferred = (last_pressure_delta*network1.volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself - volume_capacity_used = min( (last_pressure_delta*network1.volume/3)/(input_starting_pressure*air1.volume) , 1) //how much of the gas in the input air volume is consumed - - //Calculate energy generated from kinetic turbine - stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * network1.volume , input_starting_pressure*air1.volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency - - //Actually transfer the gas - removed = air1.remove(recent_moles_transferred) - if(removed) - last_heat_capacity = removed.heat_capacity() - last_temperature = removed.temperature - - //Update the gas networks. - network1.update = 1 - - last_worldtime_transfer = world.time - else - recent_moles_transferred = 0 - - update_icon() - return removed - -/obj/machinery/atmospherics/binary/circulator/proc/return_stored_energy() - last_stored_energy_transferred = stored_energy - stored_energy = 0 - return last_stored_energy_transferred - -/obj/machinery/atmospherics/binary/circulator/process() - ..() - - if(last_worldtime_transfer < world.time - 50) - recent_moles_transferred = 0 - update_icon() - -/obj/machinery/atmospherics/binary/circulator/update_icon() - icon_state = anchored ? "circ-assembled" : "circ-unassembled" - cut_overlays() - if (stat & (BROKEN|NOPOWER) || !anchored) - return 1 - if (last_pressure_delta > 0 && recent_moles_transferred > 0) - if (temperature_overlay) - add_overlay(temperature_overlay) - if (last_pressure_delta > 5*ONE_ATMOSPHERE) - add_overlay("circ-run") - else - add_overlay("circ-slow") - else - add_overlay("circ-off") - - return 1 - -/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - playsound(src, W.usesound, 75, 1) - anchored = !anchored - user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ - "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ - "You hear a ratchet.") - - if(anchored) - temperature_overlay = null - if(dir & (NORTH|SOUTH)) - initialize_directions = NORTH|SOUTH - else if(dir & (EAST|WEST)) - initialize_directions = EAST|WEST - - atmos_init() - build_network() - if (node1) - node1.atmos_init() - node1.build_network() - if (node2) - node2.atmos_init() - node2.build_network() - else - if(node1) - node1.disconnect(src) - qdel(network1) - if(node2) - node2.disconnect(src) - qdel(network2) - - node1 = null - node2 = null - - else - ..() - -/obj/machinery/atmospherics/binary/circulator/verb/rotate_clockwise() - set name = "Rotate Circulator Clockwise" - set category = "Object" - set src in view(1) - - if (usr.stat || usr.restrained() || anchored) - return - - src.set_dir(turn(src.dir, 270)) - desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." - - -/obj/machinery/atmospherics/binary/circulator/verb/rotate_counterclockwise() - set name = "Rotate Circulator Counterclockwise" - set category = "Object" - set src in view(1) - - if (usr.stat || usr.restrained() || anchored) - return - - src.set_dir(turn(src.dir, 90)) +//node1, air1, network1 correspond to input +//node2, air2, network2 correspond to output +/obj/machinery/atmospherics/binary/circulator + name = "circulator" + desc = "A gas circulator turbine and heat exchanger." + icon = 'icons/obj/power.dmi' + icon_state = "circ-unassembled" + anchored = FALSE + unacidable = TRUE + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + + var/kinetic_efficiency = 0.04 //combined kinetic and kinetic-to-electric efficiency + var/volume_ratio = 0.2 + + var/recent_moles_transferred = 0 + var/last_heat_capacity = 0 + var/last_temperature = 0 + var/last_pressure_delta = 0 + var/last_worldtime_transfer = 0 + var/last_stored_energy_transferred = 0 + var/volume_capacity_used = 0 + var/stored_energy = 0 + var/temperature_overlay + + density = TRUE + +/obj/machinery/atmospherics/binary/circulator/New() + ..() + desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." + air1.volume = 400 + +/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() + var/datum/gas_mixture/removed + if(anchored && !(stat&BROKEN) && network1) + var/input_starting_pressure = air1.return_pressure() + var/output_starting_pressure = air2.return_pressure() + last_pressure_delta = max(input_starting_pressure - output_starting_pressure - 5, 0) + + //only circulate air if there is a pressure difference (plus 5kPa kinetic, 10kPa static friction) + if(air1.temperature > 0 && last_pressure_delta > 5) + + //Calculate necessary moles to transfer using PV = nRT + recent_moles_transferred = (last_pressure_delta*network1.volume/(air1.temperature * R_IDEAL_GAS_EQUATION))/3 //uses the volume of the whole network, not just itself + volume_capacity_used = min( (last_pressure_delta*network1.volume/3)/(input_starting_pressure*air1.volume) , 1) //how much of the gas in the input air volume is consumed + + //Calculate energy generated from kinetic turbine + stored_energy += 1/ADIABATIC_EXPONENT * min(last_pressure_delta * network1.volume , input_starting_pressure*air1.volume) * (1 - volume_ratio**ADIABATIC_EXPONENT) * kinetic_efficiency + + //Actually transfer the gas + removed = air1.remove(recent_moles_transferred) + if(removed) + last_heat_capacity = removed.heat_capacity() + last_temperature = removed.temperature + + //Update the gas networks. + network1.update = 1 + + last_worldtime_transfer = world.time + else + recent_moles_transferred = 0 + + update_icon() + return removed + +/obj/machinery/atmospherics/binary/circulator/proc/return_stored_energy() + last_stored_energy_transferred = stored_energy + stored_energy = 0 + return last_stored_energy_transferred + +/obj/machinery/atmospherics/binary/circulator/process() + ..() + + if(last_worldtime_transfer < world.time - 50) + recent_moles_transferred = 0 + update_icon() + +/obj/machinery/atmospherics/binary/circulator/update_icon() + icon_state = anchored ? "circ-assembled" : "circ-unassembled" + cut_overlays() + if (stat & (BROKEN|NOPOWER) || !anchored) + return 1 + if (last_pressure_delta > 0 && recent_moles_transferred > 0) + if (temperature_overlay) + add_overlay(temperature_overlay) + if (last_pressure_delta > 5*ONE_ATMOSPHERE) + add_overlay("circ-run") + else + add_overlay("circ-slow") + else + add_overlay("circ-off") + + return 1 + +/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 75, 1) + anchored = !anchored + user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ + "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ + "You hear a ratchet.") + + if(anchored) + temperature_overlay = null + if(dir & (NORTH|SOUTH)) + initialize_directions = NORTH|SOUTH + else if(dir & (EAST|WEST)) + initialize_directions = EAST|WEST + + atmos_init() + build_network() + if (node1) + node1.atmos_init() + node1.build_network() + if (node2) + node2.atmos_init() + node2.build_network() + else + if(node1) + node1.disconnect(src) + qdel(network1) + if(node2) + node2.disconnect(src) + qdel(network2) + + node1 = null + node2 = null + + else + ..() + +/obj/machinery/atmospherics/binary/circulator/verb/rotate_clockwise() + set name = "Rotate Circulator Clockwise" + set category = "Object" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 270)) + desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." + + +/obj/machinery/atmospherics/binary/circulator/verb/rotate_counterclockwise() + set name = "Rotate Circulator Counterclockwise" + set category = "Object" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 90)) desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]." \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index aebea389013..251aa4c1473 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -1,263 +1,263 @@ -#define DEFAULT_PRESSURE_DELTA 10000 - -#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE -#define INTERNAL_PRESSURE_BOUND 0 -#define PRESSURE_CHECKS 1 - -#define PRESSURE_CHECK_EXTERNAL 1 -#define PRESSURE_CHECK_INPUT 2 -#define PRESSURE_CHECK_OUTPUT 4 - -/obj/machinery/atmospherics/binary/dp_vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_dp_vent" - - //node2 is output port - //node1 is input port - - name = "Dual Port Air Vent" - desc = "Has a valve and pump attached to it. There are two ports." - - level = 1 - - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - pipe_flags = PIPING_ALL_LAYER - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER //connects to regular, supply and scrubbers pipes - - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND - var/input_pressure_min = INTERNAL_PRESSURE_BOUND - var/output_pressure_max = DEFAULT_PRESSURE_DELTA - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - - var/pressure_checks = PRESSURE_CHECK_EXTERNAL - //1: Do not pass external_pressure_bound - //2: Do not pass input_pressure_min - //4: Do not pass output_pressure_max - -/obj/machinery/atmospherics/binary/dp_vent_pump/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP - icon = null - -/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume - name = "Large Dual Port Air Vent" - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - cut_overlays() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(!powered()) - vent_icon += "off" - else - vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" - - add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - return - else - if (node1) - add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) - else - add_underlay(T, node1, turn(dir, -180)) - if (node2) - add_underlay(T, node2, dir, node2.icon_connect_type) - else - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/hide(var/i) - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/dp_vent_pump/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if(stat & (NOPOWER|BROKEN) || !use_power) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - - var/power_draw = -1 - - //Figure out the target pressure difference - var/pressure_delta = get_pressure_delta(environment) - - if(pressure_delta > 0.5) - if(pump_direction) //internal -> external - if (node1 && (environment.temperature || air1.temperature)) - var/transfer_moles = calculate_transfer_moles(air1, environment, pressure_delta) - power_draw = pump_gas(src, air1, environment, transfer_moles, power_rating) - - if(power_draw >= 0 && network1) - network1.update = 1 - else //external -> internal - if (node2 && (environment.temperature || air2.temperature)) - var/transfer_moles = calculate_transfer_moles(environment, air2, pressure_delta, (network2)? network2.volume : 0) - - //limit flow rate from turfs - transfer_moles = min(transfer_moles, environment.total_moles*air2.volume/environment.volume) //group_multiplier gets divided out here - power_draw = pump_gas(src, environment, air2, transfer_moles, power_rating) - - if(power_draw >= 0 && network2) - network2.update = 1 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) - var/pressure_delta = DEFAULT_PRESSURE_DELTA - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //internal -> external - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here - if(pressure_checks & PRESSURE_CHECK_INPUT) - pressure_delta = min(pressure_delta, air1.return_pressure() - input_pressure_min) //decreasing the pressure here - else //external -> internal - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here - if(pressure_checks & PRESSURE_CHECK_OUTPUT) - pressure_delta = min(pressure_delta, output_pressure_max - air2.return_pressure()) //increasing the pressure here - - return pressure_delta - - -//Radio remote control - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "ADVP", - "power" = use_power, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "input" = input_pressure_min, - "output" = output_pressure_max, - "external" = external_pressure_bound, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" - - -/obj/machinery/atmospherics/unary/vent_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - if(signal.data["power"]) - update_use_power(text2num(signal.data["power"])) - - if(signal.data["power_toggle"]) - update_use_power(!use_power) - - if(signal.data["direction"]) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["checks"]) - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["purge"]) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabalize"]) - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["set_input_pressure"]) - input_pressure_min = between(0, text2num(signal.data["set_input_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["set_output_pressure"]) - output_pressure_max = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["set_external_pressure"]) - external_pressure_bound = between(0, text2num(signal.data["set_external_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -#undef DEFAULT_PRESSURE_DELTA - -#undef EXTERNAL_PRESSURE_BOUND -#undef INTERNAL_PRESSURE_BOUND -#undef PRESSURE_CHECKS - -#undef PRESSURE_CHECK_EXTERNAL -#undef PRESSURE_CHECK_INPUT -#undef PRESSURE_CHECK_OUTPUT +#define DEFAULT_PRESSURE_DELTA 10000 + +#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE +#define INTERNAL_PRESSURE_BOUND 0 +#define PRESSURE_CHECKS 1 + +#define PRESSURE_CHECK_EXTERNAL 1 +#define PRESSURE_CHECK_INPUT 2 +#define PRESSURE_CHECK_OUTPUT 4 + +/obj/machinery/atmospherics/binary/dp_vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_dp_vent" + + //node2 is output port + //node1 is input port + + name = "Dual Port Air Vent" + desc = "Has a valve and pump attached to it. There are two ports." + + level = 1 + + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + pipe_flags = PIPING_ALL_LAYER + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER //connects to regular, supply and scrubbers pipes + + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND + var/input_pressure_min = INTERNAL_PRESSURE_BOUND + var/output_pressure_max = DEFAULT_PRESSURE_DELTA + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + + var/pressure_checks = PRESSURE_CHECK_EXTERNAL + //1: Do not pass external_pressure_bound + //2: Do not pass input_pressure_min + //4: Do not pass output_pressure_max + +/obj/machinery/atmospherics/binary/dp_vent_pump/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + icon = null + +/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume + name = "Large Dual Port Air Vent" + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + cut_overlays() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(!powered()) + vent_icon += "off" + else + vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" + + add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(!T.is_plating() && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + return + else + if (node1) + add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) + else + add_underlay(T, node1, turn(dir, -180)) + if (node2) + add_underlay(T, node2, dir, node2.icon_connect_type) + else + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/hide(var/i) + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/dp_vent_pump/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if(stat & (NOPOWER|BROKEN) || !use_power) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + + var/power_draw = -1 + + //Figure out the target pressure difference + var/pressure_delta = get_pressure_delta(environment) + + if(pressure_delta > 0.5) + if(pump_direction) //internal -> external + if (node1 && (environment.temperature || air1.temperature)) + var/transfer_moles = calculate_transfer_moles(air1, environment, pressure_delta) + power_draw = pump_gas(src, air1, environment, transfer_moles, power_rating) + + if(power_draw >= 0 && network1) + network1.update = 1 + else //external -> internal + if (node2 && (environment.temperature || air2.temperature)) + var/transfer_moles = calculate_transfer_moles(environment, air2, pressure_delta, (network2)? network2.volume : 0) + + //limit flow rate from turfs + transfer_moles = min(transfer_moles, environment.total_moles*air2.volume/environment.volume) //group_multiplier gets divided out here + power_draw = pump_gas(src, environment, air2, transfer_moles, power_rating) + + if(power_draw >= 0 && network2) + network2.update = 1 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) + var/pressure_delta = DEFAULT_PRESSURE_DELTA + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //internal -> external + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here + if(pressure_checks & PRESSURE_CHECK_INPUT) + pressure_delta = min(pressure_delta, air1.return_pressure() - input_pressure_min) //decreasing the pressure here + else //external -> internal + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here + if(pressure_checks & PRESSURE_CHECK_OUTPUT) + pressure_delta = min(pressure_delta, output_pressure_max - air2.return_pressure()) //increasing the pressure here + + return pressure_delta + + +//Radio remote control + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "ADVP", + "power" = use_power, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "input" = input_pressure_min, + "output" = output_pressure_max, + "external" = external_pressure_bound, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + + +/obj/machinery/atmospherics/unary/vent_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + if(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) + + if(signal.data["power_toggle"]) + update_use_power(!use_power) + + if(signal.data["direction"]) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["checks"]) + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["purge"]) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabalize"]) + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["set_input_pressure"]) + input_pressure_min = between(0, text2num(signal.data["set_input_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["set_output_pressure"]) + output_pressure_max = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["set_external_pressure"]) + external_pressure_bound = between(0, text2num(signal.data["set_external_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +#undef DEFAULT_PRESSURE_DELTA + +#undef EXTERNAL_PRESSURE_BOUND +#undef INTERNAL_PRESSURE_BOUND +#undef PRESSURE_CHECKS + +#undef PRESSURE_CHECK_EXTERNAL +#undef PRESSURE_CHECK_INPUT +#undef PRESSURE_CHECK_OUTPUT diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 686efa65089..5d39a608513 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,302 +1,302 @@ -#define REGULATE_NONE 0 -#define REGULATE_INPUT 1 //shuts off when input side is below the target pressure -#define REGULATE_OUTPUT 2 //shuts off when output side is above the target pressure - -/obj/machinery/atmospherics/binary/passive_gate - icon = 'icons/atmos/passive_gate.dmi' - icon_state = "map" - construction_type = /obj/item/pipe/directional - pipe_state = "passivegate" - level = 1 - - name = "pressure regulator" - desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." - - use_power = USE_POWER_OFF - interact_offline = TRUE - - var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). - var/target_pressure = ONE_ATMOSPHERE - var/max_pressure_setting = 15000 //kPa - var/set_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - var/regulate_mode = REGULATE_OUTPUT - - var/flowing = 0 //for icons - becomes zero if the valve closes itself due to regulation mode - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/passive_gate/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - -/obj/machinery/atmospherics/binary/passive_gate/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/binary/passive_gate/update_icon() - icon_state = (unlocked && flowing)? "on" : "off" - -/obj/machinery/atmospherics/binary/passive_gate/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, 180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/passive_gate/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/binary/passive_gate/process() - ..() - - last_flow_rate = 0 - - if(!unlocked) - return 0 - - var/output_starting_pressure = air2.return_pressure() - var/input_starting_pressure = air1.return_pressure() - - var/pressure_delta - switch (regulate_mode) - if (REGULATE_INPUT) - pressure_delta = input_starting_pressure - target_pressure - if (REGULATE_OUTPUT) - pressure_delta = target_pressure - output_starting_pressure - if (REGULATE_NONE) - pressure_delta = input_starting_pressure - output_starting_pressure - - //-1 if pump_gas() did not move any gas, >= 0 otherwise - var/returnval = -1 - if((regulate_mode == REGULATE_NONE || pressure_delta > 0.01) && (air1.temperature > 0 || air2.temperature > 0)) //since it's basically a valve, it makes sense to check both temperatures - flowing = 1 - - //flow rate limit - var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles - - //Figure out how much gas to transfer to meet the target pressure. - switch (regulate_mode) - if (REGULATE_INPUT) - transfer_moles = min(transfer_moles, calculate_transfer_moles(air2, air1, pressure_delta, (network1)? network1.volume : 0)) - if (REGULATE_OUTPUT) - transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0)) - if (REGULATE_NONE) - var/source = air1 - var/sink = air2 - // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current - if(istype(node1, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node1 - if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - source = l.air - // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current - if(istype(node2, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node2 - if(istype(p.parent, /datum/pipeline)) - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - sink = l.air - transfer_moles = max(0, calculate_equalize_moles(source, sink)) // Not regulated, don't care about flow rate - - //pump_gas() will return a negative number if no flow occurred - if(regulate_mode == REGULATE_NONE) // ACTUALLY move gases from the whole network, not just the immediate pipes - var/source = air1 - var/sink = air2 - // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current - if(istype(node1, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node1 - if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - source = l.air - // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current - if(istype(node2, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/p = node2 - if(istype(p.parent, /datum/pipeline)) - var/datum/pipeline/l = p.parent - if(istype(l.air, /datum/gas_mixture)) - sink = l.air - returnval = pump_gas_passive(src, source, sink, transfer_moles) - else - returnval = pump_gas_passive(src, air1, air2, transfer_moles) - - if (returnval >= 0) - if(network1) - network1.update = 1 - - if(network2) - network2.update = 1 - - if (last_flow_rate) - flowing = 1 - - update_icon() - - -//Radio remote control - -/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = unlocked, - "target_output" = target_pressure, - "regulate_mode" = regulate_mode, - "set_flow_rate" = set_flow_rate, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/passive_gate/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - if("power" in signal.data) - unlocked = text2num(signal.data["power"]) - - if("power_toggle" in signal.data) - unlocked = !unlocked - - if("set_target_pressure" in signal.data) - target_pressure = between(0, text2num(signal.data["set_target_pressure"]), max_pressure_setting) - - if("set_regulate_mode" in signal.data) - regulate_mode = text2num(signal.data["set_regulate_mode"]) - - if("set_flow_rate" in signal.data) - regulate_mode = text2num(signal.data["set_flow_rate"]) - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob) - if(..()) - return - add_fingerprint(usr) - if(!allowed(user)) - to_chat(user, "Access denied.") - return - tgui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/tgui_interact(mob/user, datum/tgui/ui) - if(stat & BROKEN) - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PressureRegulator", name) - ui.open() - -/obj/machinery/atmospherics/binary/passive_gate/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - - data = list( - "on" = unlocked, - "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. - "max_pressure" = max_pressure_setting, - "input_pressure" = round(air1.return_pressure()*100), - "output_pressure" = round(air2.return_pressure()*100), - "regulate_mode" = regulate_mode, - "set_flow_rate" = round(set_flow_rate*10), - "last_flow_rate" = round(last_flow_rate*10), - ) - - return data - - -/obj/machinery/atmospherics/binary/passive_gate/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("toggle_valve") - . = TRUE - unlocked = !unlocked - if("regulate_mode") - . = TRUE - switch(params["mode"]) - if("off") regulate_mode = REGULATE_NONE - if("input") regulate_mode = REGULATE_INPUT - if("output") regulate_mode = REGULATE_OUTPUT - - if("set_press") - . = TRUE - switch(params["press"]) - if("min") - target_pressure = 0 - if("max") - target_pressure = max_pressure_setting - if("set") - var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure,max_pressure_setting,0) - src.target_pressure = between(0, new_pressure, max_pressure_setting) - - if("set_flow_rate") - . = TRUE - switch(params["press"]) - if("min") - set_flow_rate = 0 - if("max") - set_flow_rate = air1.volume - if("set") - var/new_flow_rate = tgui_input_number(usr,"Enter new flow rate limit (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate,air1.volume,0) - src.set_flow_rate = between(0, new_flow_rate, air1.volume) - - update_icon() - add_fingerprint(usr) - -/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if (unlocked) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct() - -#undef REGULATE_NONE -#undef REGULATE_INPUT -#undef REGULATE_OUTPUT +#define REGULATE_NONE 0 +#define REGULATE_INPUT 1 //shuts off when input side is below the target pressure +#define REGULATE_OUTPUT 2 //shuts off when output side is above the target pressure + +/obj/machinery/atmospherics/binary/passive_gate + icon = 'icons/atmos/passive_gate.dmi' + icon_state = "map" + construction_type = /obj/item/pipe/directional + pipe_state = "passivegate" + level = 1 + + name = "pressure regulator" + desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." + + use_power = USE_POWER_OFF + interact_offline = TRUE + + var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). + var/target_pressure = ONE_ATMOSPHERE + var/max_pressure_setting = 15000 //kPa + var/set_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + var/regulate_mode = REGULATE_OUTPUT + + var/flowing = 0 //for icons - becomes zero if the valve closes itself due to regulation mode + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/passive_gate/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + +/obj/machinery/atmospherics/binary/passive_gate/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/passive_gate/update_icon() + icon_state = (unlocked && flowing)? "on" : "off" + +/obj/machinery/atmospherics/binary/passive_gate/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, 180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/passive_gate/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/passive_gate/process() + ..() + + last_flow_rate = 0 + + if(!unlocked) + return 0 + + var/output_starting_pressure = air2.return_pressure() + var/input_starting_pressure = air1.return_pressure() + + var/pressure_delta + switch (regulate_mode) + if (REGULATE_INPUT) + pressure_delta = input_starting_pressure - target_pressure + if (REGULATE_OUTPUT) + pressure_delta = target_pressure - output_starting_pressure + if (REGULATE_NONE) + pressure_delta = input_starting_pressure - output_starting_pressure + + //-1 if pump_gas() did not move any gas, >= 0 otherwise + var/returnval = -1 + if((regulate_mode == REGULATE_NONE || pressure_delta > 0.01) && (air1.temperature > 0 || air2.temperature > 0)) //since it's basically a valve, it makes sense to check both temperatures + flowing = 1 + + //flow rate limit + var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles + + //Figure out how much gas to transfer to meet the target pressure. + switch (regulate_mode) + if (REGULATE_INPUT) + transfer_moles = min(transfer_moles, calculate_transfer_moles(air2, air1, pressure_delta, (network1)? network1.volume : 0)) + if (REGULATE_OUTPUT) + transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0)) + if (REGULATE_NONE) + var/source = air1 + var/sink = air2 + // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current + if(istype(node1, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node1 + if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + source = l.air + // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current + if(istype(node2, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node2 + if(istype(p.parent, /datum/pipeline)) + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + sink = l.air + transfer_moles = max(0, calculate_equalize_moles(source, sink)) // Not regulated, don't care about flow rate + + //pump_gas() will return a negative number if no flow occurred + if(regulate_mode == REGULATE_NONE) // ACTUALLY move gases from the whole network, not just the immediate pipes + var/source = air1 + var/sink = air2 + // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current + if(istype(node1, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node1 + if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + source = l.air + // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current + if(istype(node2, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node2 + if(istype(p.parent, /datum/pipeline)) + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + sink = l.air + returnval = pump_gas_passive(src, source, sink, transfer_moles) + else + returnval = pump_gas_passive(src, air1, air2, transfer_moles) + + if (returnval >= 0) + if(network1) + network1.update = 1 + + if(network2) + network2.update = 1 + + if (last_flow_rate) + flowing = 1 + + update_icon() + + +//Radio remote control + +/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = unlocked, + "target_output" = target_pressure, + "regulate_mode" = regulate_mode, + "set_flow_rate" = set_flow_rate, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/passive_gate/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + if("power" in signal.data) + unlocked = text2num(signal.data["power"]) + + if("power_toggle" in signal.data) + unlocked = !unlocked + + if("set_target_pressure" in signal.data) + target_pressure = between(0, text2num(signal.data["set_target_pressure"]), max_pressure_setting) + + if("set_regulate_mode" in signal.data) + regulate_mode = text2num(signal.data["set_regulate_mode"]) + + if("set_flow_rate" in signal.data) + regulate_mode = text2num(signal.data["set_flow_rate"]) + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob) + if(..()) + return + add_fingerprint(usr) + if(!allowed(user)) + to_chat(user, "Access denied.") + return + tgui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/tgui_interact(mob/user, datum/tgui/ui) + if(stat & BROKEN) + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PressureRegulator", name) + ui.open() + +/obj/machinery/atmospherics/binary/passive_gate/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + + data = list( + "on" = unlocked, + "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. + "max_pressure" = max_pressure_setting, + "input_pressure" = round(air1.return_pressure()*100), + "output_pressure" = round(air2.return_pressure()*100), + "regulate_mode" = regulate_mode, + "set_flow_rate" = round(set_flow_rate*10), + "last_flow_rate" = round(last_flow_rate*10), + ) + + return data + + +/obj/machinery/atmospherics/binary/passive_gate/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("toggle_valve") + . = TRUE + unlocked = !unlocked + if("regulate_mode") + . = TRUE + switch(params["mode"]) + if("off") regulate_mode = REGULATE_NONE + if("input") regulate_mode = REGULATE_INPUT + if("output") regulate_mode = REGULATE_OUTPUT + + if("set_press") + . = TRUE + switch(params["press"]) + if("min") + target_pressure = 0 + if("max") + target_pressure = max_pressure_setting + if("set") + var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure,max_pressure_setting,0) + src.target_pressure = between(0, new_pressure, max_pressure_setting) + + if("set_flow_rate") + . = TRUE + switch(params["press"]) + if("min") + set_flow_rate = 0 + if("max") + set_flow_rate = air1.volume + if("set") + var/new_flow_rate = tgui_input_number(usr,"Enter new flow rate limit (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate,air1.volume,0) + src.set_flow_rate = between(0, new_flow_rate, air1.volume) + + update_icon() + add_fingerprint(usr) + +/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (unlocked) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct() + +#undef REGULATE_NONE +#undef REGULATE_INPUT +#undef REGULATE_OUTPUT diff --git a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm index da5f37ff0ca..6b0f40b8b4c 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm @@ -82,7 +82,7 @@ add_overlay(image('icons/obj/pipeturbine.dmi', "hi-turb")) /obj/machinery/atmospherics/pipeturbine/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) anchored = !anchored playsound(src, W.usesound, 50, 1) to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.") @@ -256,7 +256,7 @@ add_avail(power_generated) /obj/machinery/power/turbinemotor/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) anchored = !anchored playsound(src, W.usesound, 50, 1) turbine = null diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 514216e7faf..3e22ea310d5 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,258 +1,258 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/pump - icon = 'icons/atmos/pump.dmi' - icon_state = "map_off" - construction_type = /obj/item/pipe/directional - pipe_state = "pump" - level = 1 - var/base_icon = "pump" - - name = "gas pump" - desc = "A pump that moves gas from one place to another." - - var/target_pressure = ONE_ATMOSPHERE - - //var/max_volume_transfer = 10000 - - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - var/max_pressure_setting = 15000 //kPa - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/pump/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP - -/obj/machinery/atmospherics/binary/pump/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/binary/pump/on - icon_state = "map_on" - use_power = USE_POWER_IDLE - -/obj/machinery/atmospherics/binary/pump/fuel - icon_state = "map_off-fuel" - base_icon = "pump-fuel" - icon_connect_type = "-fuel" - connect_types = CONNECT_TYPE_FUEL - -/obj/machinery/atmospherics/binary/pump/fuel/on - icon_state = "map_on-fuel" - use_power = USE_POWER_IDLE - -/obj/machinery/atmospherics/binary/pump/aux - icon_state = "map_off-aux" - base_icon = "pump-aux" - icon_connect_type = "-aux" - connect_types = CONNECT_TYPE_AUX - -/obj/machinery/atmospherics/binary/pump/aux/on - icon_state = "map_on-aux" - use_power = USE_POWER_IDLE - -/obj/machinery/atmospherics/binary/pump/update_icon() - if(!powered()) - icon_state = "[base_icon]-off" - else - icon_state = "[use_power ? "[base_icon]-on" : "[base_icon]-off"]" - -/obj/machinery/atmospherics/binary/pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180), node1?.icon_connect_type) - add_underlay(T, node2, dir, node2?.icon_connect_type) - -/obj/machinery/atmospherics/binary/pump/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/binary/pump/process() - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - var/power_draw = -1 - var/pressure_delta = target_pressure - air2.return_pressure() - - if(pressure_delta > 0.01 && air1.temperature > 0) - //Figure out how much gas to transfer to meet the target pressure. - var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0) - power_draw = pump_gas(src, air1, air2, transfer_moles, power_rating) - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - if(network1) - network1.update = 1 - - if(network2) - network2.update = 1 - - return 1 - -//Radio remote control - -/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = use_power, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/pump/tgui_interact(mob/user, datum/tgui/ui) - if(stat & (BROKEN|NOPOWER)) - return - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GasPump", name) - ui.open() - -/obj/machinery/atmospherics/binary/pump/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - - data = list( - "on" = use_power, - "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. - "max_pressure" = max_pressure_setting, - "last_flow_rate" = round(last_flow_rate*10), - "last_power_draw" = round(last_power_draw), - "max_power_draw" = power_rating, - ) - - return data - -/obj/machinery/atmospherics/binary/pump/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["power"]) - if(text2num(signal.data["power"])) - update_use_power(USE_POWER_IDLE) - else - update_use_power(USE_POWER_OFF) - - if("power_toggle" in signal.data) - update_use_power(!use_power) - - if(signal.data["set_output_pressure"]) - target_pressure = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) - if(..()) - return - add_fingerprint(usr) - if(!allowed(user)) - to_chat(user, "Access denied.") - return - tgui_interact(user) - -/obj/machinery/atmospherics/binary/pump/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - update_use_power(!use_power) - . = TRUE - if("set_press") - var/press = params["press"] - switch(press) - if("min") - target_pressure = 0 - if("max") - target_pressure = max_pressure_setting - if("set") - var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure,max_pressure_setting,0) - src.target_pressure = between(0, new_pressure, max_pressure_setting) - . = TRUE - - add_fingerprint(usr) - update_icon() - -/obj/machinery/atmospherics/binary/pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench this [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct() +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/pump + icon = 'icons/atmos/pump.dmi' + icon_state = "map_off" + construction_type = /obj/item/pipe/directional + pipe_state = "pump" + level = 1 + var/base_icon = "pump" + + name = "gas pump" + desc = "A pump that moves gas from one place to another." + + var/target_pressure = ONE_ATMOSPHERE + + //var/max_volume_transfer = 10000 + + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + var/max_pressure_setting = 15000 //kPa + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/pump/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + +/obj/machinery/atmospherics/binary/pump/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/binary/pump/on + icon_state = "map_on" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/fuel + icon_state = "map_off-fuel" + base_icon = "pump-fuel" + icon_connect_type = "-fuel" + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/binary/pump/fuel/on + icon_state = "map_on-fuel" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/aux + icon_state = "map_off-aux" + base_icon = "pump-aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX + +/obj/machinery/atmospherics/binary/pump/aux/on + icon_state = "map_on-aux" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/update_icon() + if(!powered()) + icon_state = "[base_icon]-off" + else + icon_state = "[use_power ? "[base_icon]-on" : "[base_icon]-off"]" + +/obj/machinery/atmospherics/binary/pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180), node1?.icon_connect_type) + add_underlay(T, node2, dir, node2?.icon_connect_type) + +/obj/machinery/atmospherics/binary/pump/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/pump/process() + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + var/power_draw = -1 + var/pressure_delta = target_pressure - air2.return_pressure() + + if(pressure_delta > 0.01 && air1.temperature > 0) + //Figure out how much gas to transfer to meet the target pressure. + var/transfer_moles = calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0) + power_draw = pump_gas(src, air1, air2, transfer_moles, power_rating) + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network1) + network1.update = 1 + + if(network2) + network2.update = 1 + + return 1 + +//Radio remote control + +/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = use_power, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/pump/tgui_interact(mob/user, datum/tgui/ui) + if(stat & (BROKEN|NOPOWER)) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasPump", name) + ui.open() + +/obj/machinery/atmospherics/binary/pump/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + + data = list( + "on" = use_power, + "pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently. + "max_pressure" = max_pressure_setting, + "last_flow_rate" = round(last_flow_rate*10), + "last_power_draw" = round(last_power_draw), + "max_power_draw" = power_rating, + ) + + return data + +/obj/machinery/atmospherics/binary/pump/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["power"]) + if(text2num(signal.data["power"])) + update_use_power(USE_POWER_IDLE) + else + update_use_power(USE_POWER_OFF) + + if("power_toggle" in signal.data) + update_use_power(!use_power) + + if(signal.data["set_output_pressure"]) + target_pressure = between(0, text2num(signal.data["set_output_pressure"]), ONE_ATMOSPHERE*50) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) + if(..()) + return + add_fingerprint(usr) + if(!allowed(user)) + to_chat(user, "Access denied.") + return + tgui_interact(user) + +/obj/machinery/atmospherics/binary/pump/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + . = TRUE + if("set_press") + var/press = params["press"] + switch(press) + if("min") + target_pressure = 0 + if("max") + target_pressure = max_pressure_setting + if("set") + var/new_pressure = tgui_input_number(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure,max_pressure_setting,0) + src.target_pressure = between(0, new_pressure, max_pressure_setting) + . = TRUE + + add_fingerprint(usr) + update_icon() + +/obj/machinery/atmospherics/binary/pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench this [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 8e827505885..135e5a1194f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,21 +1,21 @@ -/obj/machinery/atmospherics/binary/pump/high_power - icon = 'icons/atmos/volume_pump.dmi' - icon_state = "map_off" - construction_type = /obj/item/pipe/directional - pipe_state = "volumepump" - level = 1 - - name = "high power gas pump" - desc = "A pump that moves gas from one place to another. Has double the power rating of the standard gas pump." - - power_rating = 15000 //15000 W ~ 20 HP - -/obj/machinery/atmospherics/binary/pump/high_power/on - use_power = USE_POWER_IDLE - icon_state = "map_on" - -/obj/machinery/atmospherics/binary/pump/high_power/update_icon() - if(!powered()) - icon_state = "off" - else +/obj/machinery/atmospherics/binary/pump/high_power + icon = 'icons/atmos/volume_pump.dmi' + icon_state = "map_off" + construction_type = /obj/item/pipe/directional + pipe_state = "volumepump" + level = 1 + + name = "high power gas pump" + desc = "A pump that moves gas from one place to another. Has double the power rating of the standard gas pump." + + power_rating = 15000 //15000 W ~ 20 HP + +/obj/machinery/atmospherics/binary/pump/high_power/on + use_power = USE_POWER_IDLE + icon_state = "map_on" + +/obj/machinery/atmospherics/binary/pump/high_power/update_icon() + if(!powered()) + icon_state = "off" + else icon_state = "[use_power ? "on" : "off"]" \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm index f0c5498fb3e..77a35c8eae8 100644 --- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm +++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm @@ -80,7 +80,7 @@ update_icon() /obj/machinery/atmospherics/omni/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(!W.is_wrench()) + if(!W.has_tool_quality(TOOL_WRENCH)) return ..() if(!can_unwrench()) diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index 31f61ae55b4..5d315cf0a05 100644 --- a/code/ATMOSPHERICS/components/portables_connector.dm +++ b/code/ATMOSPHERICS/components/portables_connector.dm @@ -1,168 +1,168 @@ -/obj/machinery/atmospherics/portables_connector - icon = 'icons/atmos/connector.dmi' - icon_state = "map_connector" - - name = "Connector Port" - desc = "For connecting portables devices related to atmospherics control." - - dir = SOUTH - initialize_directions = SOUTH - construction_type = /obj/item/pipe/directional - pipe_state = "connector" - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - - var/obj/machinery/portable_atmospherics/connected_device - - var/obj/machinery/atmospherics/node - - var/datum/pipe_network/network - - var/on = 0 - use_power = USE_POWER_OFF - level = 1 - -/obj/machinery/atmospherics/portables_connector/fuel - icon_state = "map_connector-fuel" - pipe_state = "connector-fuel" - icon_connect_type = "-fuel" - pipe_flags = PIPING_ONE_PER_TURF - connect_types = CONNECT_TYPE_FUEL - -/obj/machinery/atmospherics/portables_connector/aux - icon_state = "map_connector-aux" - pipe_state = "connector-aux" - icon_connect_type = "-aux" - pipe_flags = PIPING_ONE_PER_TURF - connect_types = CONNECT_TYPE_AUX - -/obj/machinery/atmospherics/portables_connector/init_dir() - initialize_directions = dir - -/obj/machinery/atmospherics/portables_connector/update_icon() - icon_state = "connector" - -/obj/machinery/atmospherics/portables_connector/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node, dir, node?.icon_connect_type) - -/obj/machinery/atmospherics/portables_connector/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/portables_connector/process() - ..() - if(!on) - return - if(!connected_device) - on = 0 - return - if(network) - network.update = 1 - return 1 - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/portables_connector/get_neighbor_nodes_for_init() - return list(node) - -/obj/machinery/atmospherics/portables_connector/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node) - network = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/portables_connector/Destroy() - . = ..() - - if(connected_device) - connected_device.disconnect() - - if(node) - node.disconnect(src) - qdel(network) - - node = null - -/obj/machinery/atmospherics/portables_connector/atmos_init() - if(node) - return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(can_be_node(target, 1)) - node = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/portables_connector/build_network() - if(!network && node) - network = new /datum/pipe_network() - network.normal_members += src - network.build_network(node, src) - - -/obj/machinery/atmospherics/portables_connector/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node) - return network - - if(reference==connected_device) - return network - - return null - -/obj/machinery/atmospherics/portables_connector/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network == old_network) - network = new_network - - return 1 - -/obj/machinery/atmospherics/portables_connector/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(connected_device) - results += connected_device.air_contents - - return results - -/obj/machinery/atmospherics/portables_connector/disconnect(obj/machinery/atmospherics/reference) - if(reference==node) - qdel(network) - node = null - - update_underlays() - - return null - - -/obj/machinery/atmospherics/portables_connector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if (connected_device) - to_chat(user, "You cannot unwrench \the [src], dettach \the [connected_device] first.") - return 1 - if (locate(/obj/machinery/portable_atmospherics, src.loc)) - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() +/obj/machinery/atmospherics/portables_connector + icon = 'icons/atmos/connector.dmi' + icon_state = "map_connector" + + name = "Connector Port" + desc = "For connecting portables devices related to atmospherics control." + + dir = SOUTH + initialize_directions = SOUTH + construction_type = /obj/item/pipe/directional + pipe_state = "connector" + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + + var/obj/machinery/portable_atmospherics/connected_device + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + var/on = 0 + use_power = USE_POWER_OFF + level = 1 + +/obj/machinery/atmospherics/portables_connector/fuel + icon_state = "map_connector-fuel" + pipe_state = "connector-fuel" + icon_connect_type = "-fuel" + pipe_flags = PIPING_ONE_PER_TURF + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/portables_connector/aux + icon_state = "map_connector-aux" + pipe_state = "connector-aux" + icon_connect_type = "-aux" + pipe_flags = PIPING_ONE_PER_TURF + connect_types = CONNECT_TYPE_AUX + +/obj/machinery/atmospherics/portables_connector/init_dir() + initialize_directions = dir + +/obj/machinery/atmospherics/portables_connector/update_icon() + icon_state = "connector" + +/obj/machinery/atmospherics/portables_connector/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node, dir, node?.icon_connect_type) + +/obj/machinery/atmospherics/portables_connector/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/portables_connector/process() + ..() + if(!on) + return + if(!connected_device) + on = 0 + return + if(network) + network.update = 1 + return 1 + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/portables_connector/get_neighbor_nodes_for_init() + return list(node) + +/obj/machinery/atmospherics/portables_connector/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node) + network = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/portables_connector/Destroy() + . = ..() + + if(connected_device) + connected_device.disconnect() + + if(node) + node.disconnect(src) + qdel(network) + + node = null + +/obj/machinery/atmospherics/portables_connector/atmos_init() + if(node) + return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(can_be_node(target, 1)) + node = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/portables_connector/build_network() + if(!network && node) + network = new /datum/pipe_network() + network.normal_members += src + network.build_network(node, src) + + +/obj/machinery/atmospherics/portables_connector/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node) + return network + + if(reference==connected_device) + return network + + return null + +/obj/machinery/atmospherics/portables_connector/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network == old_network) + network = new_network + + return 1 + +/obj/machinery/atmospherics/portables_connector/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(connected_device) + results += connected_device.air_contents + + return results + +/obj/machinery/atmospherics/portables_connector/disconnect(obj/machinery/atmospherics/reference) + if(reference==node) + qdel(network) + node = null + + update_underlays() + + return null + + +/obj/machinery/atmospherics/portables_connector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (connected_device) + to_chat(user, "You cannot unwrench \the [src], dettach \the [connected_device] first.") + return 1 + if (locate(/obj/machinery/portable_atmospherics, src.loc)) + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index 48085569bc9..18ade9acefe 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,231 +1,231 @@ -/obj/machinery/atmospherics/trinary/atmos_filter - icon = 'icons/atmos/filter.dmi' - icon_state = "map" - construction_type = /obj/item/pipe/trinary/flippable - pipe_state = "filter" - density = FALSE - level = 1 - - name = "Gas filter" - desc = "Filters one type of gas from an input, and pushes it out the side." - - use_power = USE_POWER_IDLE - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP - - var/temp = null // -- TLE - - var/set_flow_rate = ATMOS_DEFAULT_VOLUME_FILTER - - /* - Filter types: - -1: Nothing - 0: Phoron: Phoron, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Nitrous Oxide (Formerly called Sleeping Agent) (N2O) - */ - var/filter_type = -1 - var/list/filtered_out = list() - - - var/frequency = 0 - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/trinary/atmos_filter/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/trinary/atmos_filter/New() - ..() - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out = list("phoron") - if(1) //removing O2 - filtered_out = list("oxygen") - if(2) //removing N2 - filtered_out = list("nitrogen") - if(3) //removing CO2 - filtered_out = list("carbon_dioxide") - if(4)//removing N2O - filtered_out = list("nitrous_oxide") - - air1.volume = ATMOS_DEFAULT_VOLUME_FILTER - air2.volume = ATMOS_DEFAULT_VOLUME_FILTER - air3.volume = ATMOS_DEFAULT_VOLUME_FILTER - -/obj/machinery/atmospherics/trinary/atmos_filter/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/trinary/atmos_filter/update_icon() - if(mirrored) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += use_power ? "on" : "off" - else - icon_state += "off" - update_use_power(USE_POWER_OFF) - -/obj/machinery/atmospherics/trinary/atmos_filter/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - //Figure out the amount of moles to transfer - var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles - - var/power_draw = -1 - if (transfer_moles > MINIMUM_MOLES_TO_FILTER) - power_draw = filter_gas(src, filtered_out, air1, air2, air3, transfer_moles, power_rating) - - if(network2) - network2.update = 1 - - if(network3) - network3.update = 1 - - if(network1) - network1.update = 1 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - return 1 - -/obj/machinery/atmospherics/trinary/atmos_filter/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/trinary/atmos_filter/attack_hand(user) // -- TLE - if(..()) - return - - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - - tgui_interact(user) - - // var/dat - // var/current_filter_type - // switch(filter_type) - // if(0) - // current_filter_type = "Phoron" - // if(1) - // current_filter_type = "Oxygen" - // if(2) - // current_filter_type = "Nitrogen" - // if(3) - // current_filter_type = "Carbon Dioxide" - // if(4) - // current_filter_type = "Nitrous Oxide" - // if(-1) - // current_filter_type = "Nothing" - // else - // current_filter_type = "ERROR - Report this bug to the admin, please!" - - // dat += {" - // Power: [use_power?"On":"Off"]
- // Filtering: [current_filter_type]

- //

Set Filter Type:

- // Phoron
- // Oxygen
- // Nitrogen
- // Carbon Dioxide
- // Nitrous Oxide
- // Nothing
- //
- // Set Flow Rate Limit: - // [src.set_flow_rate]L/s | Change
- // Flow rate: [round(last_flow_rate, 0.1)]L/s - // "} - - // user << browse("[src.name] control[dat]", "window=atmos_filter") - // onclose(user, "atmos_filter") - - - -/obj/machinery/atmospherics/trinary/atmos_filter/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AtmosFilter", name) - ui.open() - -/obj/machinery/atmospherics/trinary/atmos_filter/tgui_data(mob/user) - var/list/data = list() - - data["on"] = use_power - data["rate"] = set_flow_rate - data["max_rate"] = air1.volume - data["last_flow_rate"] = round(last_flow_rate, 0.1) - - data["filter_types"] = list() - data["filter_types"] += list(list("name" = "Nothing", "f_type" = -1, "selected" = filter_type == -1)) - data["filter_types"] += list(list("name" = "Phoron", "f_type" = 0, "selected" = filter_type == 0)) - data["filter_types"] += list(list("name" = "Oxygen", "f_type" = 1, "selected" = filter_type == 1)) - data["filter_types"] += list(list("name" = "Nitrogen", "f_type" = 2, "selected" = filter_type == 2)) - data["filter_types"] += list(list("name" = "Carbon Dioxide", "f_type" = 3, "selected" = filter_type == 3)) - data["filter_types"] += list(list("name" = "Nitrous Oxide", "f_type" = 4, "selected" = filter_type == 4)) - - return data - -/obj/machinery/atmospherics/trinary/atmos_filter/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - update_use_power(!use_power) - if("rate") - var/rate = params["rate"] - if(rate == "max") - rate = air1.volume - . = TRUE - else if(text2num(rate) != null) - rate = text2num(rate) - . = TRUE - if(.) - set_flow_rate = clamp(rate, 0, air1.volume) - if("filter") - . = TRUE - filter_type = text2num(params["filterset"]) - filtered_out.Cut() //no need to create new lists unnecessarily - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out += "phoron" - filtered_out += "oxygen_agent_b" - if(1) //removing O2 - filtered_out += "oxygen" - if(2) //removing N2 - filtered_out += "nitrogen" - if(3) //removing CO2 - filtered_out += "carbon_dioxide" - if(4)//removing N2O - filtered_out += "nitrous_oxide" - - add_fingerprint(usr) - update_icon() - -// -// Mirrored Orientation - Flips the output dir to opposite side from normal. -// -/obj/machinery/atmospherics/trinary/atmos_filter/m_filter - icon_state = "mmap" - dir = SOUTH - initialize_directions = SOUTH|NORTH|EAST - mirrored = TRUE +/obj/machinery/atmospherics/trinary/atmos_filter + icon = 'icons/atmos/filter.dmi' + icon_state = "map" + construction_type = /obj/item/pipe/trinary/flippable + pipe_state = "filter" + density = FALSE + level = 1 + + name = "Gas filter" + desc = "Filters one type of gas from an input, and pushes it out the side." + + use_power = USE_POWER_IDLE + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP + + var/temp = null // -- TLE + + var/set_flow_rate = ATMOS_DEFAULT_VOLUME_FILTER + + /* + Filter types: + -1: Nothing + 0: Phoron: Phoron, Oxygen Agent B + 1: Oxygen: Oxygen ONLY + 2: Nitrogen: Nitrogen ONLY + 3: Carbon Dioxide: Carbon Dioxide ONLY + 4: Nitrous Oxide (Formerly called Sleeping Agent) (N2O) + */ + var/filter_type = -1 + var/list/filtered_out = list() + + + var/frequency = 0 + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/trinary/atmos_filter/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/trinary/atmos_filter/New() + ..() + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out = list("phoron") + if(1) //removing O2 + filtered_out = list("oxygen") + if(2) //removing N2 + filtered_out = list("nitrogen") + if(3) //removing CO2 + filtered_out = list("carbon_dioxide") + if(4)//removing N2O + filtered_out = list("nitrous_oxide") + + air1.volume = ATMOS_DEFAULT_VOLUME_FILTER + air2.volume = ATMOS_DEFAULT_VOLUME_FILTER + air3.volume = ATMOS_DEFAULT_VOLUME_FILTER + +/obj/machinery/atmospherics/trinary/atmos_filter/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/trinary/atmos_filter/update_icon() + if(mirrored) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += use_power ? "on" : "off" + else + icon_state += "off" + update_use_power(USE_POWER_OFF) + +/obj/machinery/atmospherics/trinary/atmos_filter/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + //Figure out the amount of moles to transfer + var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles + + var/power_draw = -1 + if (transfer_moles > MINIMUM_MOLES_TO_FILTER) + power_draw = filter_gas(src, filtered_out, air1, air2, air3, transfer_moles, power_rating) + + if(network2) + network2.update = 1 + + if(network3) + network3.update = 1 + + if(network1) + network1.update = 1 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + return 1 + +/obj/machinery/atmospherics/trinary/atmos_filter/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/trinary/atmos_filter/attack_hand(user) // -- TLE + if(..()) + return + + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + + tgui_interact(user) + + // var/dat + // var/current_filter_type + // switch(filter_type) + // if(0) + // current_filter_type = "Phoron" + // if(1) + // current_filter_type = "Oxygen" + // if(2) + // current_filter_type = "Nitrogen" + // if(3) + // current_filter_type = "Carbon Dioxide" + // if(4) + // current_filter_type = "Nitrous Oxide" + // if(-1) + // current_filter_type = "Nothing" + // else + // current_filter_type = "ERROR - Report this bug to the admin, please!" + + // dat += {" + // Power: [use_power?"On":"Off"]
+ // Filtering: [current_filter_type]

+ //

Set Filter Type:

+ // Phoron
+ // Oxygen
+ // Nitrogen
+ // Carbon Dioxide
+ // Nitrous Oxide
+ // Nothing
+ //
+ // Set Flow Rate Limit: + // [src.set_flow_rate]L/s | Change
+ // Flow rate: [round(last_flow_rate, 0.1)]L/s + // "} + + // user << browse("[src.name] control[dat]", "window=atmos_filter") + // onclose(user, "atmos_filter") + + + +/obj/machinery/atmospherics/trinary/atmos_filter/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AtmosFilter", name) + ui.open() + +/obj/machinery/atmospherics/trinary/atmos_filter/tgui_data(mob/user) + var/list/data = list() + + data["on"] = use_power + data["rate"] = set_flow_rate + data["max_rate"] = air1.volume + data["last_flow_rate"] = round(last_flow_rate, 0.1) + + data["filter_types"] = list() + data["filter_types"] += list(list("name" = "Nothing", "f_type" = -1, "selected" = filter_type == -1)) + data["filter_types"] += list(list("name" = "Phoron", "f_type" = 0, "selected" = filter_type == 0)) + data["filter_types"] += list(list("name" = "Oxygen", "f_type" = 1, "selected" = filter_type == 1)) + data["filter_types"] += list(list("name" = "Nitrogen", "f_type" = 2, "selected" = filter_type == 2)) + data["filter_types"] += list(list("name" = "Carbon Dioxide", "f_type" = 3, "selected" = filter_type == 3)) + data["filter_types"] += list(list("name" = "Nitrous Oxide", "f_type" = 4, "selected" = filter_type == 4)) + + return data + +/obj/machinery/atmospherics/trinary/atmos_filter/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + if("rate") + var/rate = params["rate"] + if(rate == "max") + rate = air1.volume + . = TRUE + else if(text2num(rate) != null) + rate = text2num(rate) + . = TRUE + if(.) + set_flow_rate = clamp(rate, 0, air1.volume) + if("filter") + . = TRUE + filter_type = text2num(params["filterset"]) + filtered_out.Cut() //no need to create new lists unnecessarily + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out += "phoron" + filtered_out += "oxygen_agent_b" + if(1) //removing O2 + filtered_out += "oxygen" + if(2) //removing N2 + filtered_out += "nitrogen" + if(3) //removing CO2 + filtered_out += "carbon_dioxide" + if(4)//removing N2O + filtered_out += "nitrous_oxide" + + add_fingerprint(usr) + update_icon() + +// +// Mirrored Orientation - Flips the output dir to opposite side from normal. +// +/obj/machinery/atmospherics/trinary/atmos_filter/m_filter + icon_state = "mmap" + dir = SOUTH + initialize_directions = SOUTH|NORTH|EAST + mirrored = TRUE diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 8cecbc6c651..7e06e5de821 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,181 +1,181 @@ -/obj/machinery/atmospherics/trinary/mixer - icon = 'icons/atmos/mixer.dmi' - icon_state = "map" - construction_type = /obj/item/pipe/trinary/flippable - pipe_state = "mixer" - density = FALSE - level = 1 - - name = "Gas mixer" - - use_power = USE_POWER_IDLE - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP - - var/set_flow_rate = ATMOS_DEFAULT_VOLUME_MIXER - var/list/mixing_inputs - - //for mapping - var/node1_concentration = 0.5 - var/node2_concentration = 0.5 - - //node 3 is the outlet, nodes 1 & 2 are intakes - -/obj/machinery/atmospherics/trinary/mixer/update_icon(var/safety = 0) - if(tee) - icon_state = "t" - else if(mirrored) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += use_power ? "on" : "off" - else - icon_state += "off" - update_use_power(USE_POWER_OFF) - -/obj/machinery/atmospherics/trinary/mixer/New() - ..() - air1.volume = ATMOS_DEFAULT_VOLUME_MIXER - air2.volume = ATMOS_DEFAULT_VOLUME_MIXER - air3.volume = ATMOS_DEFAULT_VOLUME_MIXER * 1.5 - - if (!mixing_inputs) - mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration) - -/obj/machinery/atmospherics/trinary/mixer/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - //Figure out the amount of moles to transfer - var/transfer_moles = (set_flow_rate*mixing_inputs[air1]/air1.volume)*air1.total_moles + (set_flow_rate*mixing_inputs[air1]/air2.volume)*air2.total_moles - - var/power_draw = -1 - if (transfer_moles > MINIMUM_MOLES_TO_FILTER) - power_draw = mix_gas(src, mixing_inputs, air3, transfer_moles, power_rating) - - if(network1 && mixing_inputs[air1]) - network1.update = 1 - - if(network2 && mixing_inputs[air2]) - network2.update = 1 - - if(network3) - network3.update = 1 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - return 1 - -/obj/machinery/atmospherics/trinary/mixer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AtmosMixer", name) - ui.open() - -/obj/machinery/atmospherics/trinary/mixer/tgui_data(mob/user) - var/list/data = list() - data["on"] = use_power - data["set_pressure"] = round(set_flow_rate) - data["max_pressure"] = min(air1.volume, air2.volume) - data["node1_concentration"] = round(mixing_inputs[air1]*100, 1) - data["node2_concentration"] = round(mixing_inputs[air2]*100, 1) - var/list/node_connects = get_node_connect_dirs() - data["node1_dir"] = dir_name(node_connects[1],TRUE) - data["node2_dir"] = dir_name(node_connects[2],TRUE) - return data - -/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob) - if(..()) - return - tgui_interact(user) - // src.add_fingerprint(usr) - // if(!src.allowed(user)) - // to_chat(user, "Access denied.") - // return - // usr.set_machine(src) - // var/list/node_connects = get_node_connect_dirs() - // var/dat = {"Power: [use_power?"On":"Off"]
- // Set Flow Rate Limit: - // [set_flow_rate]L/s | Change - //
- // Flow Rate: [round(last_flow_rate, 0.1)]L/s - //

- // Node 1 ([dir_name(node_connects[1],TRUE)]) Concentration: - // - - // - - // [mixing_inputs[air1]]([mixing_inputs[air1]*100]%) - // + - // + - //
- // Node 2 ([dir_name(node_connects[2],TRUE)]) Concentration: - // - - // - - // [mixing_inputs[air2]]([mixing_inputs[air2]*100]%) - // + - // + - // "} - - // user << browse("[src.name] control[dat]", "window=atmo_mixer") - // onclose(user, "atmo_mixer") - // return - -/obj/machinery/atmospherics/trinary/mixer/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - update_use_power(!use_power) - . = TRUE - if("pressure") - var/pressure = params["pressure"] - if(pressure == "max") - pressure = min(air1.volume, air2.volume) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - set_flow_rate = clamp(pressure, 0, min(air1.volume, air2.volume)) - if("node1") - var/value = text2num(params["concentration"]) - mixing_inputs[air1] = max(0, min(1, value / 100)) - mixing_inputs[air2] = 1.0 - mixing_inputs[air1] - . = TRUE - if("node2") - var/value = text2num(params["concentration"]) - mixing_inputs[air2] = max(0, min(1, value / 100)) - mixing_inputs[air1] = 1.0 - mixing_inputs[air2] - . = TRUE - update_icon() - -// -// "T" Orientation - Inputs are on oposite sides instead of adjacent -// -/obj/machinery/atmospherics/trinary/mixer/t_mixer - icon_state = "tmap" - construction_type = /obj/item/pipe/trinary // Can't flip a "T", its symmetrical - pipe_state = "t_mixer" - dir = SOUTH - initialize_directions = SOUTH|EAST|WEST - tee = TRUE - -// -// Mirrored Orientation - Flips the output dir to opposite side from normal. -// -/obj/machinery/atmospherics/trinary/mixer/m_mixer - icon_state = "mmap" - dir = SOUTH - initialize_directions = SOUTH|NORTH|EAST - mirrored = TRUE +/obj/machinery/atmospherics/trinary/mixer + icon = 'icons/atmos/mixer.dmi' + icon_state = "map" + construction_type = /obj/item/pipe/trinary/flippable + pipe_state = "mixer" + density = FALSE + level = 1 + + name = "Gas mixer" + + use_power = USE_POWER_IDLE + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP + + var/set_flow_rate = ATMOS_DEFAULT_VOLUME_MIXER + var/list/mixing_inputs + + //for mapping + var/node1_concentration = 0.5 + var/node2_concentration = 0.5 + + //node 3 is the outlet, nodes 1 & 2 are intakes + +/obj/machinery/atmospherics/trinary/mixer/update_icon(var/safety = 0) + if(tee) + icon_state = "t" + else if(mirrored) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += use_power ? "on" : "off" + else + icon_state += "off" + update_use_power(USE_POWER_OFF) + +/obj/machinery/atmospherics/trinary/mixer/New() + ..() + air1.volume = ATMOS_DEFAULT_VOLUME_MIXER + air2.volume = ATMOS_DEFAULT_VOLUME_MIXER + air3.volume = ATMOS_DEFAULT_VOLUME_MIXER * 1.5 + + if (!mixing_inputs) + mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration) + +/obj/machinery/atmospherics/trinary/mixer/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + //Figure out the amount of moles to transfer + var/transfer_moles = (set_flow_rate*mixing_inputs[air1]/air1.volume)*air1.total_moles + (set_flow_rate*mixing_inputs[air1]/air2.volume)*air2.total_moles + + var/power_draw = -1 + if (transfer_moles > MINIMUM_MOLES_TO_FILTER) + power_draw = mix_gas(src, mixing_inputs, air3, transfer_moles, power_rating) + + if(network1 && mixing_inputs[air1]) + network1.update = 1 + + if(network2 && mixing_inputs[air2]) + network2.update = 1 + + if(network3) + network3.update = 1 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + return 1 + +/obj/machinery/atmospherics/trinary/mixer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AtmosMixer", name) + ui.open() + +/obj/machinery/atmospherics/trinary/mixer/tgui_data(mob/user) + var/list/data = list() + data["on"] = use_power + data["set_pressure"] = round(set_flow_rate) + data["max_pressure"] = min(air1.volume, air2.volume) + data["node1_concentration"] = round(mixing_inputs[air1]*100, 1) + data["node2_concentration"] = round(mixing_inputs[air2]*100, 1) + var/list/node_connects = get_node_connect_dirs() + data["node1_dir"] = dir_name(node_connects[1],TRUE) + data["node2_dir"] = dir_name(node_connects[2],TRUE) + return data + +/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob) + if(..()) + return + tgui_interact(user) + // src.add_fingerprint(usr) + // if(!src.allowed(user)) + // to_chat(user, "Access denied.") + // return + // usr.set_machine(src) + // var/list/node_connects = get_node_connect_dirs() + // var/dat = {"Power: [use_power?"On":"Off"]
+ // Set Flow Rate Limit: + // [set_flow_rate]L/s | Change + //
+ // Flow Rate: [round(last_flow_rate, 0.1)]L/s + //

+ // Node 1 ([dir_name(node_connects[1],TRUE)]) Concentration: + // - + // - + // [mixing_inputs[air1]]([mixing_inputs[air1]*100]%) + // + + // + + //
+ // Node 2 ([dir_name(node_connects[2],TRUE)]) Concentration: + // - + // - + // [mixing_inputs[air2]]([mixing_inputs[air2]*100]%) + // + + // + + // "} + + // user << browse("[src.name] control[dat]", "window=atmo_mixer") + // onclose(user, "atmo_mixer") + // return + +/obj/machinery/atmospherics/trinary/mixer/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + update_use_power(!use_power) + . = TRUE + if("pressure") + var/pressure = params["pressure"] + if(pressure == "max") + pressure = min(air1.volume, air2.volume) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + set_flow_rate = clamp(pressure, 0, min(air1.volume, air2.volume)) + if("node1") + var/value = text2num(params["concentration"]) + mixing_inputs[air1] = max(0, min(1, value / 100)) + mixing_inputs[air2] = 1.0 - mixing_inputs[air1] + . = TRUE + if("node2") + var/value = text2num(params["concentration"]) + mixing_inputs[air2] = max(0, min(1, value / 100)) + mixing_inputs[air1] = 1.0 - mixing_inputs[air2] + . = TRUE + update_icon() + +// +// "T" Orientation - Inputs are on oposite sides instead of adjacent +// +/obj/machinery/atmospherics/trinary/mixer/t_mixer + icon_state = "tmap" + construction_type = /obj/item/pipe/trinary // Can't flip a "T", its symmetrical + pipe_state = "t_mixer" + dir = SOUTH + initialize_directions = SOUTH|EAST|WEST + tee = TRUE + +// +// Mirrored Orientation - Flips the output dir to opposite side from normal. +// +/obj/machinery/atmospherics/trinary/mixer/m_mixer + icon_state = "mmap" + dir = SOUTH + initialize_directions = SOUTH|NORTH|EAST + mirrored = TRUE diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index eaae718b2ea..1bae066a69d 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,248 +1,248 @@ -/obj/machinery/atmospherics/trinary - dir = SOUTH - initialize_directions = SOUTH|NORTH|WEST - use_power = USE_POWER_OFF - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - - var/mirrored = FALSE - var/tee = FALSE - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - var/datum/gas_mixture/air3 - - var/obj/machinery/atmospherics/node3 - - var/datum/pipe_network/network1 - var/datum/pipe_network/network2 - var/datum/pipe_network/network3 - -/obj/machinery/atmospherics/trinary/New() - ..() - - air1 = new - air2 = new - air3 = new - - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 - -/obj/machinery/atmospherics/trinary/init_dir() - initialize_directions = get_initialize_directions_trinary(dir, mirrored, tee) - -/obj/machinery/atmospherics/trinary/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - var/list/node_connects = get_node_connect_dirs() - add_underlay(T, node1, node_connects[1]) - add_underlay(T, node2, node_connects[2]) - add_underlay(T, node3, node_connects[3]) - -/obj/machinery/atmospherics/trinary/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/trinary/power_change() - var/old_stat = stat - . = ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/trinary/get_neighbor_nodes_for_init() - return list(node1, node2, node3) - -/obj/machinery/atmospherics/trinary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node1) - network1 = new_network - - else if(reference == node2) - network2 = new_network - - else if (reference == node3) - network3 = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/trinary/Destroy() - . = ..() - - if(node1) - node1.disconnect(src) - qdel(network1) - if(node2) - node2.disconnect(src) - qdel(network2) - if(node3) - node3.disconnect(src) - qdel(network3) - - node1 = null - node2 = null - node3 = null - -// Get the direction each node is facing to connect. -// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. -/obj/machinery/atmospherics/trinary/get_node_connect_dirs() - return get_node_connect_dirs_trinary(dir, mirrored, tee) - -/obj/machinery/atmospherics/trinary/atmos_init() - if(node1 && node2 && node3) - return - - var/list/node_connects = get_node_connect_dirs() - - STANDARD_ATMOS_CHOOSE_NODE(1, node_connects[1]) - STANDARD_ATMOS_CHOOSE_NODE(2, node_connects[2]) - STANDARD_ATMOS_CHOOSE_NODE(3, node_connects[3]) - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/trinary/build_network() - if(!network1 && node1) - network1 = new /datum/pipe_network() - network1.normal_members += src - network1.build_network(node1, src) - - if(!network2 && node2) - network2 = new /datum/pipe_network() - network2.normal_members += src - network2.build_network(node2, src) - - if(!network3 && node3) - network3 = new /datum/pipe_network() - network3.normal_members += src - network3.build_network(node3, src) - - -/obj/machinery/atmospherics/trinary/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network1 - - if(reference==node2) - return network2 - - if(reference==node3) - return network3 - - return null - -/obj/machinery/atmospherics/trinary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network1 == old_network) - network1 = new_network - if(network2 == old_network) - network2 = new_network - if(network3 == old_network) - network3 = new_network - - return 1 - -/obj/machinery/atmospherics/trinary/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network1 == reference) - results += air1 - if(network2 == reference) - results += air2 - if(network3 == reference) - results += air3 - - return results - -/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) - if(reference==node1) - qdel(network1) - node1 = null - - else if(reference==node2) - qdel(network2) - node2 = null - - else if(reference==node3) - qdel(network3) - node3 = null - - update_underlays() - - return null - -// Trinary init_dir() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves -// TODO - Someday refactor those places under atmospherics/trinary -/proc/get_initialize_directions_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) - if(tee) - switch(dir) - if(NORTH) - return EAST|NORTH|WEST - if(SOUTH) - return SOUTH|WEST|EAST - if(EAST) - return EAST|NORTH|SOUTH - if(WEST) - return WEST|NORTH|SOUTH - else if(mirrored) - switch(dir) - if(NORTH) - return WEST|NORTH|SOUTH - if(SOUTH) - return SOUTH|EAST|NORTH - if(EAST) - return EAST|WEST|NORTH - if(WEST) - return WEST|SOUTH|EAST - else - switch(dir) - if(NORTH) - return EAST|NORTH|SOUTH - if(SOUTH) - return SOUTH|WEST|NORTH - if(EAST) - return EAST|WEST|SOUTH - if(WEST) - return WEST|NORTH|EAST - -// Trinary get_node_connect_dirs() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves -/proc/get_node_connect_dirs_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) - var/node1_connect - var/node2_connect - var/node3_connect - - if(tee) - node1_connect = turn(dir, -90) - node2_connect = turn(dir, 90) - node3_connect = dir - else if(mirrored) - node1_connect = turn(dir, 180) - node2_connect = turn(dir, 90) - node3_connect = dir - else - node1_connect = turn(dir, 180) - node2_connect = turn(dir, -90) - node3_connect = dir - return list(node1_connect, node2_connect, node3_connect) +/obj/machinery/atmospherics/trinary + dir = SOUTH + initialize_directions = SOUTH|NORTH|WEST + use_power = USE_POWER_OFF + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + + var/mirrored = FALSE + var/tee = FALSE + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + var/datum/gas_mixture/air3 + + var/obj/machinery/atmospherics/node3 + + var/datum/pipe_network/network1 + var/datum/pipe_network/network2 + var/datum/pipe_network/network3 + +/obj/machinery/atmospherics/trinary/New() + ..() + + air1 = new + air2 = new + air3 = new + + air1.volume = 200 + air2.volume = 200 + air3.volume = 200 + +/obj/machinery/atmospherics/trinary/init_dir() + initialize_directions = get_initialize_directions_trinary(dir, mirrored, tee) + +/obj/machinery/atmospherics/trinary/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + var/list/node_connects = get_node_connect_dirs() + add_underlay(T, node1, node_connects[1]) + add_underlay(T, node2, node_connects[2]) + add_underlay(T, node3, node_connects[3]) + +/obj/machinery/atmospherics/trinary/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/trinary/power_change() + var/old_stat = stat + . = ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/trinary/get_neighbor_nodes_for_init() + return list(node1, node2, node3) + +/obj/machinery/atmospherics/trinary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network1 = new_network + + else if(reference == node2) + network2 = new_network + + else if (reference == node3) + network3 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/trinary/Destroy() + . = ..() + + if(node1) + node1.disconnect(src) + qdel(network1) + if(node2) + node2.disconnect(src) + qdel(network2) + if(node3) + node3.disconnect(src) + qdel(network3) + + node1 = null + node2 = null + node3 = null + +// Get the direction each node is facing to connect. +// It now returns as a list so it can be fetched nicely, each entry corresponds to node of same number. +/obj/machinery/atmospherics/trinary/get_node_connect_dirs() + return get_node_connect_dirs_trinary(dir, mirrored, tee) + +/obj/machinery/atmospherics/trinary/atmos_init() + if(node1 && node2 && node3) + return + + var/list/node_connects = get_node_connect_dirs() + + STANDARD_ATMOS_CHOOSE_NODE(1, node_connects[1]) + STANDARD_ATMOS_CHOOSE_NODE(2, node_connects[2]) + STANDARD_ATMOS_CHOOSE_NODE(3, node_connects[3]) + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/trinary/build_network() + if(!network1 && node1) + network1 = new /datum/pipe_network() + network1.normal_members += src + network1.build_network(node1, src) + + if(!network2 && node2) + network2 = new /datum/pipe_network() + network2.normal_members += src + network2.build_network(node2, src) + + if(!network3 && node3) + network3 = new /datum/pipe_network() + network3.normal_members += src + network3.build_network(node3, src) + + +/obj/machinery/atmospherics/trinary/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network1 + + if(reference==node2) + return network2 + + if(reference==node3) + return network3 + + return null + +/obj/machinery/atmospherics/trinary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network1 == old_network) + network1 = new_network + if(network2 == old_network) + network2 = new_network + if(network3 == old_network) + network3 = new_network + + return 1 + +/obj/machinery/atmospherics/trinary/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network1 == reference) + results += air1 + if(network2 == reference) + results += air2 + if(network3 == reference) + results += air3 + + return results + +/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + qdel(network1) + node1 = null + + else if(reference==node2) + qdel(network2) + node2 = null + + else if(reference==node3) + qdel(network3) + node3 = null + + update_underlays() + + return null + +// Trinary init_dir() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves +// TODO - Someday refactor those places under atmospherics/trinary +/proc/get_initialize_directions_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) + if(tee) + switch(dir) + if(NORTH) + return EAST|NORTH|WEST + if(SOUTH) + return SOUTH|WEST|EAST + if(EAST) + return EAST|NORTH|SOUTH + if(WEST) + return WEST|NORTH|SOUTH + else if(mirrored) + switch(dir) + if(NORTH) + return WEST|NORTH|SOUTH + if(SOUTH) + return SOUTH|EAST|NORTH + if(EAST) + return EAST|WEST|NORTH + if(WEST) + return WEST|SOUTH|EAST + else + switch(dir) + if(NORTH) + return EAST|NORTH|SOUTH + if(SOUTH) + return SOUTH|WEST|NORTH + if(EAST) + return EAST|WEST|SOUTH + if(WEST) + return WEST|NORTH|EAST + +// Trinary get_node_connect_dirs() logic in a separate proc so it can be referenced from "trinary-ish" places like T-Valves +/proc/get_node_connect_dirs_trinary(var/dir, var/mirrored = FALSE, var/tee = FALSE) + var/node1_connect + var/node2_connect + var/node3_connect + + if(tee) + node1_connect = turn(dir, -90) + node2_connect = turn(dir, 90) + node3_connect = dir + else if(mirrored) + node1_connect = turn(dir, 180) + node2_connect = turn(dir, 90) + node3_connect = dir + else + node1_connect = turn(dir, 180) + node2_connect = turn(dir, -90) + node3_connect = dir + return list(node1_connect, node2_connect, node3_connect) diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm index acbf9f6ba2d..5edf6966965 100644 --- a/code/ATMOSPHERICS/components/tvalve.dm +++ b/code/ATMOSPHERICS/components/tvalve.dm @@ -327,7 +327,7 @@ go_to_side() /obj/machinery/atmospherics/tvalve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) + if (!W.has_tool_quality(TOOL_WRENCH)) return ..() if(!can_unwrench()) to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm index dea0487b48f..2cd7403096d 100644 --- a/code/ATMOSPHERICS/components/unary/cold_sink.dm +++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm @@ -1,175 +1,175 @@ -//TODO: Put this under a common parent type with heaters to cut down on the copypasta -#define FREEZER_PERF_MULT 2.5 - -/obj/machinery/atmospherics/unary/freezer - name = "gas cooling system" - desc = "Cools gas when connected to pipe network" - icon = 'icons/obj/Cryogenic2_vr.dmi' - icon_state = "freezer_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_OFF - idle_power_usage = 5 // 5 Watts for thermostat related circuitry - circuit = /obj/item/weapon/circuitboard/unary_atmos/cooler - - var/heatsink_temperature = T20C // The constant temperature reservoir into which the freezer pumps heat. Probably the hull of the station or something. - var/internal_volume = 600 // L - - var/max_power_rating = 20000 // Power rating when the usage is turned up to 100 - var/power_setting = 100 - - var/set_temperature = T20C // Thermostat - var/cooling = 0 - -/obj/machinery/atmospherics/unary/freezer/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/atmospherics/unary/freezer/atmos_init() - if(node) - return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) - if(can_be_node(target, 1)) - node = target - break - - if(check_for_obstacles()) - node = null - - if(node) - update_icon() - -/obj/machinery/atmospherics/unary/freezer/update_icon() - if(node) - if(use_power && cooling) - icon_state = "freezer_1" - else - icon_state = "freezer" - else - icon_state = "freezer_0" - return - -/obj/machinery/atmospherics/unary/freezer/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/freezer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GasTemperatureSystem", name) - ui.open() - -/obj/machinery/atmospherics/unary/freezer/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - data["on"] = use_power ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["minGasTemperature"] = 0 - data["maxGasTemperature"] = round(T20C+500) - data["targetGasTemperature"] = round(set_temperature) - data["powerSetting"] = power_setting - - var/temp_class = "good" - if(air_contents.temperature > (T0C - 20)) - temp_class = "bad" - else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) - temp_class = "average" - data["gasTemperatureClass"] = temp_class - - return data - -/obj/machinery/atmospherics/unary/freezer/tgui_act(action, params) - if(..()) - return TRUE - - . = TRUE - switch(action) - if("toggleStatus") - update_use_power(!use_power) - update_icon() - if("setGasTemperature") - var/amount = text2num(params["temp"]) - if(amount > 0) - set_temperature = min(amount, 1000) - else - set_temperature = max(amount, 0) - if("setPower") //setting power to 0 is redundant anyways - var/new_setting = between(0, text2num(params["value"]), 100) - set_power_level(new_setting) - - add_fingerprint(usr) - -/obj/machinery/atmospherics/unary/freezer/process() - ..() - - if(stat & (NOPOWER|BROKEN) || !use_power) - cooling = 0 - update_icon() - return - - if(network && air_contents.temperature > set_temperature) - cooling = 1 - - var/heat_transfer = max( -air_contents.get_thermal_energy_change(set_temperature - 5), 0 ) - - //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature - //not /really/ proper thermodynamics but whatever - var/cop = FREEZER_PERF_MULT * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop - heat_transfer = min(heat_transfer, cop * power_rating) //limit heat transfer by available power - - var/removed = -air_contents.add_thermal_energy(-heat_transfer) //remove the heat - if(debug) - visible_message("[src]: Removing [removed] W.") - - use_power(power_rating) - - network.update = 1 - else - cooling = 0 - - update_icon() - -//upgrading parts -/obj/machinery/atmospherics/unary/freezer/RefreshParts() - ..() - var/cap_rating = 0 - var/manip_rating = 0 - var/bin_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - manip_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) - bin_rating += P.rating - - max_power_rating = initial(max_power_rating) * cap_rating / 2 //more powerful - heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient - air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating - set_power_level(power_setting) - -/obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) - power_setting = new_power_setting - power_rating = max_power_rating * (power_setting/100) - -/obj/machinery/atmospherics/unary/freezer/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(default_deconstruction_screwdriver(user, O)) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - - ..() - -/obj/machinery/atmospherics/unary/freezer/examine(mob/user) - . = ..() - if(panel_open) - . += "The maintenance hatch is open." +//TODO: Put this under a common parent type with heaters to cut down on the copypasta +#define FREEZER_PERF_MULT 2.5 + +/obj/machinery/atmospherics/unary/freezer + name = "gas cooling system" + desc = "Cools gas when connected to pipe network" + icon = 'icons/obj/Cryogenic2_vr.dmi' + icon_state = "freezer_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_OFF + idle_power_usage = 5 // 5 Watts for thermostat related circuitry + circuit = /obj/item/weapon/circuitboard/unary_atmos/cooler + + var/heatsink_temperature = T20C // The constant temperature reservoir into which the freezer pumps heat. Probably the hull of the station or something. + var/internal_volume = 600 // L + + var/max_power_rating = 20000 // Power rating when the usage is turned up to 100 + var/power_setting = 100 + + var/set_temperature = T20C // Thermostat + var/cooling = 0 + +/obj/machinery/atmospherics/unary/freezer/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/atmospherics/unary/freezer/atmos_init() + if(node) + return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) + if(can_be_node(target, 1)) + node = target + break + + if(check_for_obstacles()) + node = null + + if(node) + update_icon() + +/obj/machinery/atmospherics/unary/freezer/update_icon() + if(node) + if(use_power && cooling) + icon_state = "freezer_1" + else + icon_state = "freezer" + else + icon_state = "freezer_0" + return + +/obj/machinery/atmospherics/unary/freezer/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/freezer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasTemperatureSystem", name) + ui.open() + +/obj/machinery/atmospherics/unary/freezer/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + data["on"] = use_power ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["minGasTemperature"] = 0 + data["maxGasTemperature"] = round(T20C+500) + data["targetGasTemperature"] = round(set_temperature) + data["powerSetting"] = power_setting + + var/temp_class = "good" + if(air_contents.temperature > (T0C - 20)) + temp_class = "bad" + else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) + temp_class = "average" + data["gasTemperatureClass"] = temp_class + + return data + +/obj/machinery/atmospherics/unary/freezer/tgui_act(action, params) + if(..()) + return TRUE + + . = TRUE + switch(action) + if("toggleStatus") + update_use_power(!use_power) + update_icon() + if("setGasTemperature") + var/amount = text2num(params["temp"]) + if(amount > 0) + set_temperature = min(amount, 1000) + else + set_temperature = max(amount, 0) + if("setPower") //setting power to 0 is redundant anyways + var/new_setting = between(0, text2num(params["value"]), 100) + set_power_level(new_setting) + + add_fingerprint(usr) + +/obj/machinery/atmospherics/unary/freezer/process() + ..() + + if(stat & (NOPOWER|BROKEN) || !use_power) + cooling = 0 + update_icon() + return + + if(network && air_contents.temperature > set_temperature) + cooling = 1 + + var/heat_transfer = max( -air_contents.get_thermal_energy_change(set_temperature - 5), 0 ) + + //Assume the heat is being pumped into the hull which is fixed at heatsink_temperature + //not /really/ proper thermodynamics but whatever + var/cop = FREEZER_PERF_MULT * air_contents.temperature/heatsink_temperature //heatpump coefficient of performance from thermodynamics -> power used = heat_transfer/cop + heat_transfer = min(heat_transfer, cop * power_rating) //limit heat transfer by available power + + var/removed = -air_contents.add_thermal_energy(-heat_transfer) //remove the heat + if(debug) + visible_message("[src]: Removing [removed] W.") + + use_power(power_rating) + + network.update = 1 + else + cooling = 0 + + update_icon() + +//upgrading parts +/obj/machinery/atmospherics/unary/freezer/RefreshParts() + ..() + var/cap_rating = 0 + var/manip_rating = 0 + var/bin_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + manip_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) + bin_rating += P.rating + + max_power_rating = initial(max_power_rating) * cap_rating / 2 //more powerful + heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient + air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + set_power_level(power_setting) + +/obj/machinery/atmospherics/unary/freezer/proc/set_power_level(var/new_power_setting) + power_setting = new_power_setting + power_rating = max_power_rating * (power_setting/100) + +/obj/machinery/atmospherics/unary/freezer/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + ..() + +/obj/machinery/atmospherics/unary/freezer/examine(mob/user) + . = ..() + if(panel_open) + . += "The maintenance hatch is open." diff --git a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm index 91616f8d8ff..cc50782d71c 100644 --- a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm +++ b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm @@ -1,87 +1,87 @@ -/obj/machinery/atmospherics/unary/heat_exchanger - - icon = 'icons/obj/atmospherics/heat_exchanger.dmi' - icon_state = "intact" - pipe_state = "heunary" - density = TRUE - - name = "Heat Exchanger" - desc = "Exchanges heat between two input gases. Setup for fast heat transfer" - - var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null - var/update_cycle - -/obj/machinery/atmospherics/unary/heat_exchanger/update_icon() - if(node) - icon_state = "intact" - else - icon_state = "exposed" - - return - -/obj/machinery/atmospherics/unary/heat_exchanger/atmos_init() - if(!partner) - var/partner_connect = turn(dir,180) - - for(var/obj/machinery/atmospherics/unary/heat_exchanger/target in get_step(src,partner_connect)) - if(target.dir & get_dir(src,target)) - partner = target - partner.partner = src - break - - ..() - -/obj/machinery/atmospherics/unary/heat_exchanger/process() - ..() - if(!partner) - return 0 - - if(!air_master || air_master.current_cycle <= update_cycle) - return 0 - - update_cycle = air_master.current_cycle - partner.update_cycle = air_master.current_cycle - - var/air_heat_capacity = air_contents.heat_capacity() - var/other_air_heat_capacity = partner.air_contents.heat_capacity() - var/combined_heat_capacity = other_air_heat_capacity + air_heat_capacity - - var/old_temperature = air_contents.temperature - var/other_old_temperature = partner.air_contents.temperature - - if(combined_heat_capacity > 0) - var/combined_energy = partner.air_contents.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature - - var/new_temperature = combined_energy/combined_heat_capacity - air_contents.temperature = new_temperature - partner.air_contents.temperature = new_temperature - - if(network) - if(abs(old_temperature-air_contents.temperature) > 1) - network.update = 1 - - if(partner.network) - if(abs(other_old_temperature-partner.air_contents.temperature) > 1) - partner.network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/heat_exchanger/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - var/turf/T = src.loc - if (level==1 && isturf(T) && !T.is_plating()) - to_chat(user, "You must remove the plating first.") - return 1 - if (!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() +/obj/machinery/atmospherics/unary/heat_exchanger + + icon = 'icons/obj/atmospherics/heat_exchanger.dmi' + icon_state = "intact" + pipe_state = "heunary" + density = TRUE + + name = "Heat Exchanger" + desc = "Exchanges heat between two input gases. Setup for fast heat transfer" + + var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null + var/update_cycle + +/obj/machinery/atmospherics/unary/heat_exchanger/update_icon() + if(node) + icon_state = "intact" + else + icon_state = "exposed" + + return + +/obj/machinery/atmospherics/unary/heat_exchanger/atmos_init() + if(!partner) + var/partner_connect = turn(dir,180) + + for(var/obj/machinery/atmospherics/unary/heat_exchanger/target in get_step(src,partner_connect)) + if(target.dir & get_dir(src,target)) + partner = target + partner.partner = src + break + + ..() + +/obj/machinery/atmospherics/unary/heat_exchanger/process() + ..() + if(!partner) + return 0 + + if(!air_master || air_master.current_cycle <= update_cycle) + return 0 + + update_cycle = air_master.current_cycle + partner.update_cycle = air_master.current_cycle + + var/air_heat_capacity = air_contents.heat_capacity() + var/other_air_heat_capacity = partner.air_contents.heat_capacity() + var/combined_heat_capacity = other_air_heat_capacity + air_heat_capacity + + var/old_temperature = air_contents.temperature + var/other_old_temperature = partner.air_contents.temperature + + if(combined_heat_capacity > 0) + var/combined_energy = partner.air_contents.temperature*other_air_heat_capacity + air_heat_capacity*air_contents.temperature + + var/new_temperature = combined_energy/combined_heat_capacity + air_contents.temperature = new_temperature + partner.air_contents.temperature = new_temperature + + if(network) + if(abs(old_temperature-air_contents.temperature) > 1) + network.update = 1 + + if(partner.network) + if(abs(other_old_temperature-partner.air_contents.temperature) > 1) + partner.network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/heat_exchanger/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + var/turf/T = src.loc + if (level==1 && isturf(T) && !T.is_plating()) + to_chat(user, "You must remove the plating first.") + return 1 + if (!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm index aea5730672b..efd3a56d8bb 100644 --- a/code/ATMOSPHERICS/components/unary/heat_source.dm +++ b/code/ATMOSPHERICS/components/unary/heat_source.dm @@ -1,162 +1,162 @@ -//TODO: Put this under a common parent type with freezers to cut down on the copypasta -#define HEATER_PERF_MULT 2.5 - -/obj/machinery/atmospherics/unary/heater - name = "gas heating system" - desc = "Heats gas when connected to a pipe network" - icon = 'icons/obj/Cryogenic2_vr.dmi' - icon_state = "heater_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_OFF - idle_power_usage = 5 //5 Watts for thermostat related circuitry - circuit = /obj/item/weapon/circuitboard/unary_atmos/heater - - var/max_temperature = T20C + 680 - var/internal_volume = 600 //L - - var/max_power_rating = 20000 //power rating when the usage is turned up to 100 - var/power_setting = 100 - - var/set_temperature = T20C //thermostat - var/heating = 0 //mainly for icon updates - -/obj/machinery/atmospherics/unary/heater/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/atmospherics/unary/heater/atmos_init() - if(node) - return - - var/node_connect = dir - - //check that there is something to connect to - for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) - if(can_be_node(target, 1)) - node = target - break - - if(check_for_obstacles()) - node = null - - if(node) - update_icon() - - -/obj/machinery/atmospherics/unary/heater/update_icon() - if(node) - if(use_power && heating) - icon_state = "heater_1" - else - icon_state = "heater" - else - icon_state = "heater_0" - return - - -/obj/machinery/atmospherics/unary/heater/process() - ..() - - if(stat & (NOPOWER|BROKEN) || !use_power) - heating = 0 - update_icon() - return - - if(network && air_contents.total_moles && air_contents.temperature < set_temperature) - air_contents.add_thermal_energy(power_rating * HEATER_PERF_MULT) - use_power(power_rating) - - heating = 1 - network.update = 1 - else - heating = 0 - - update_icon() - -/obj/machinery/atmospherics/unary/heater/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/heater/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GasTemperatureSystem", name) - ui.open() - -/obj/machinery/atmospherics/unary/heater/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - data["on"] = use_power ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["minGasTemperature"] = 0 - data["maxGasTemperature"] = round(max_temperature) - data["targetGasTemperature"] = round(set_temperature) - data["powerSetting"] = power_setting - - var/temp_class = "average" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - - return data - -/obj/machinery/atmospherics/unary/heater/tgui_act(action, params) - if(..()) - return TRUE - - . = TRUE - switch(action) - if("toggleStatus") - update_use_power(!use_power) - update_icon() - if("setGasTemperature") - var/amount = text2num(params["temp"]) - if(amount > 0) - set_temperature = min(amount, max_temperature) - else - set_temperature = max(amount, 0) - if("setPower") //setting power to 0 is redundant anyways - var/new_setting = between(0, text2num(params["value"]), 100) - set_power_level(new_setting) - - add_fingerprint(usr) - -//upgrading parts -/obj/machinery/atmospherics/unary/heater/RefreshParts() - ..() - var/cap_rating = 0 - var/bin_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) - bin_rating += P.rating - - max_power_rating = initial(max_power_rating) * cap_rating / 2 - max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C - air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating - set_power_level(power_setting) - -/obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) - power_setting = new_power_setting - power_rating = max_power_rating * (power_setting/100) - -/obj/machinery/atmospherics/unary/heater/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(default_deconstruction_screwdriver(user, O)) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - - ..() - -/obj/machinery/atmospherics/unary/heater/examine(mob/user) - . = ..() - if(panel_open) - . += "The maintenance hatch is open." +//TODO: Put this under a common parent type with freezers to cut down on the copypasta +#define HEATER_PERF_MULT 2.5 + +/obj/machinery/atmospherics/unary/heater + name = "gas heating system" + desc = "Heats gas when connected to a pipe network" + icon = 'icons/obj/Cryogenic2_vr.dmi' + icon_state = "heater_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_OFF + idle_power_usage = 5 //5 Watts for thermostat related circuitry + circuit = /obj/item/weapon/circuitboard/unary_atmos/heater + + var/max_temperature = T20C + 680 + var/internal_volume = 600 //L + + var/max_power_rating = 20000 //power rating when the usage is turned up to 100 + var/power_setting = 100 + + var/set_temperature = T20C //thermostat + var/heating = 0 //mainly for icon updates + +/obj/machinery/atmospherics/unary/heater/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/atmospherics/unary/heater/atmos_init() + if(node) + return + + var/node_connect = dir + + //check that there is something to connect to + for(var/obj/machinery/atmospherics/target in get_step(src, node_connect)) + if(can_be_node(target, 1)) + node = target + break + + if(check_for_obstacles()) + node = null + + if(node) + update_icon() + + +/obj/machinery/atmospherics/unary/heater/update_icon() + if(node) + if(use_power && heating) + icon_state = "heater_1" + else + icon_state = "heater" + else + icon_state = "heater_0" + return + + +/obj/machinery/atmospherics/unary/heater/process() + ..() + + if(stat & (NOPOWER|BROKEN) || !use_power) + heating = 0 + update_icon() + return + + if(network && air_contents.total_moles && air_contents.temperature < set_temperature) + air_contents.add_thermal_energy(power_rating * HEATER_PERF_MULT) + use_power(power_rating) + + heating = 1 + network.update = 1 + else + heating = 0 + + update_icon() + +/obj/machinery/atmospherics/unary/heater/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/heater/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GasTemperatureSystem", name) + ui.open() + +/obj/machinery/atmospherics/unary/heater/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + data["on"] = use_power ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["minGasTemperature"] = 0 + data["maxGasTemperature"] = round(max_temperature) + data["targetGasTemperature"] = round(set_temperature) + data["powerSetting"] = power_setting + + var/temp_class = "average" + if(air_contents.temperature > (T20C+40)) + temp_class = "bad" + data["gasTemperatureClass"] = temp_class + + return data + +/obj/machinery/atmospherics/unary/heater/tgui_act(action, params) + if(..()) + return TRUE + + . = TRUE + switch(action) + if("toggleStatus") + update_use_power(!use_power) + update_icon() + if("setGasTemperature") + var/amount = text2num(params["temp"]) + if(amount > 0) + set_temperature = min(amount, max_temperature) + else + set_temperature = max(amount, 0) + if("setPower") //setting power to 0 is redundant anyways + var/new_setting = between(0, text2num(params["value"]), 100) + set_power_level(new_setting) + + add_fingerprint(usr) + +//upgrading parts +/obj/machinery/atmospherics/unary/heater/RefreshParts() + ..() + var/cap_rating = 0 + var/bin_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/matter_bin)) + bin_rating += P.rating + + max_power_rating = initial(max_power_rating) * cap_rating / 2 + max_temperature = max(initial(max_temperature) - T20C, 0) * ((bin_rating * 4 + cap_rating) / 5) + T20C + air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating + set_power_level(power_setting) + +/obj/machinery/atmospherics/unary/heater/proc/set_power_level(var/new_power_setting) + power_setting = new_power_setting + power_rating = max_power_rating * (power_setting/100) + +/obj/machinery/atmospherics/unary/heater/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + ..() + +/obj/machinery/atmospherics/unary/heater/examine(mob/user) + . = ..() + if(panel_open) + . += "The maintenance hatch is open." diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index b4991c0e5d5..8002d79ad55 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -1,177 +1,177 @@ -//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively, -//but it does not permit gas to flow back from the environment into the injector. Can be turned off to prevent any gas flow. -//When it receives the "inject" signal, it will try to pump it's entire contents into the environment regardless of pressure, using power. - -/obj/machinery/atmospherics/unary/outlet_injector - icon = 'icons/atmos/injector.dmi' - icon_state = "map_injector" - pipe_state = "injector" - - name = "air injector" - desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." - - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 15000 //15000 W ~ 20 HP - - var/injecting = 0 - - var/volume_rate = 50 //flow rate limit - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - - level = 1 - -/obj/machinery/atmospherics/unary/outlet_injector/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. - -/obj/machinery/atmospherics/unary/outlet_injector/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/unary/outlet_injector/update_icon() - if(!powered()) - icon_state = "off" - else - icon_state = "[use_power ? "on" : "off"]" - -/obj/machinery/atmospherics/unary/outlet_injector/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node, dir) - -/obj/machinery/atmospherics/unary/outlet_injector/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/process() - ..() - - last_power_draw = 0 - last_flow_rate = 0 - - if((stat & (NOPOWER|BROKEN)) || !use_power) - return - - var/power_draw = -1 - var/datum/gas_mixture/environment = loc.return_air() - - if(environment && air_contents.temperature > 0) - var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit - power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - if(network) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/outlet_injector/proc/inject() - if(injecting || (stat & NOPOWER)) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - if (!environment) - return 0 - - injecting = 1 - - if(air_contents.temperature > 0) - var/power_used = pump_gas(src, air_contents, environment, air_contents.total_moles, power_rating) - use_power(power_used) - - if(network) - network.update = 1 - - flick("inject", src) - -/obj/machinery/atmospherics/unary/outlet_injector/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency) - -/obj/machinery/atmospherics/unary/outlet_injector/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AO", - "power" = use_power, - "volume_rate" = volume_rate, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal) - - return 1 - -/obj/machinery/atmospherics/unary/outlet_injector/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/unary/outlet_injector/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["power"]) - update_use_power(text2num(signal.data["power"])) - - if(signal.data["power_toggle"]) - update_use_power(!use_power) - - if(signal.data["inject"]) - spawn inject() - return - - if(signal.data["set_volume_rate"]) - var/number = text2num(signal.data["set_volume_rate"]) - volume_rate = between(0, number, air_contents.volume) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) - to_chat(user, "You toggle \the [src].") - injecting = !injecting - update_use_power(injecting ? USE_POWER_IDLE : USE_POWER_OFF) - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() +//Basically a one way passive valve. If the pressure inside is greater than the environment then gas will flow passively, +//but it does not permit gas to flow back from the environment into the injector. Can be turned off to prevent any gas flow. +//When it receives the "inject" signal, it will try to pump it's entire contents into the environment regardless of pressure, using power. + +/obj/machinery/atmospherics/unary/outlet_injector + icon = 'icons/atmos/injector.dmi' + icon_state = "map_injector" + pipe_state = "injector" + + name = "air injector" + desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." + + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 15000 //15000 W ~ 20 HP + + var/injecting = 0 + + var/volume_rate = 50 //flow rate limit + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + + level = 1 + +/obj/machinery/atmospherics/unary/outlet_injector/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //Give it a small reservoir for injecting. Also allows it to have a higher flow rate limit than vent pumps, to differentiate injectors a bit more. + +/obj/machinery/atmospherics/unary/outlet_injector/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/unary/outlet_injector/update_icon() + if(!powered()) + icon_state = "off" + else + icon_state = "[use_power ? "on" : "off"]" + +/obj/machinery/atmospherics/unary/outlet_injector/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node, dir) + +/obj/machinery/atmospherics/unary/outlet_injector/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/process() + ..() + + last_power_draw = 0 + last_flow_rate = 0 + + if((stat & (NOPOWER|BROKEN)) || !use_power) + return + + var/power_draw = -1 + var/datum/gas_mixture/environment = loc.return_air() + + if(environment && air_contents.temperature > 0) + var/transfer_moles = (volume_rate/air_contents.volume)*air_contents.total_moles //apply flow rate limit + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/outlet_injector/proc/inject() + if(injecting || (stat & NOPOWER)) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + if (!environment) + return 0 + + injecting = 1 + + if(air_contents.temperature > 0) + var/power_used = pump_gas(src, air_contents, environment, air_contents.total_moles, power_rating) + use_power(power_used) + + if(network) + network.update = 1 + + flick("inject", src) + +/obj/machinery/atmospherics/unary/outlet_injector/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency) + +/obj/machinery/atmospherics/unary/outlet_injector/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AO", + "power" = use_power, + "volume_rate" = volume_rate, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal) + + return 1 + +/obj/machinery/atmospherics/unary/outlet_injector/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/unary/outlet_injector/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) + + if(signal.data["power_toggle"]) + update_use_power(!use_power) + + if(signal.data["inject"]) + spawn inject() + return + + if(signal.data["set_volume_rate"]) + var/number = text2num(signal.data["set_volume_rate"]) + volume_rate = between(0, number, air_contents.volume) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) + to_chat(user, "You toggle \the [src].") + injecting = !injecting + update_use_power(injecting ? USE_POWER_IDLE : USE_POWER_OFF) + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm index 12d6822ad47..9a3a4d3e44b 100644 --- a/code/ATMOSPHERICS/components/unary/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary/unary_base.dm @@ -1,116 +1,116 @@ -/obj/machinery/atmospherics/unary - dir = SOUTH - initialize_directions = SOUTH - construction_type = /obj/item/pipe/directional - pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - //layer = TURF_LAYER+0.1 - - var/datum/gas_mixture/air_contents - - var/obj/machinery/atmospherics/node - - var/datum/pipe_network/network - - var/welded = 0 //defining this here for ventcrawl stuff - -/obj/machinery/atmospherics/unary/New() - ..() - air_contents = new - - air_contents.volume = 200 - -/obj/machinery/atmospherics/unary/init_dir() - initialize_directions = dir - -// Housekeeping and pipe network stuff below -/obj/machinery/atmospherics/unary/get_neighbor_nodes_for_init() - return list(node) - -/obj/machinery/atmospherics/unary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node) - network = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - return null - -/obj/machinery/atmospherics/unary/Destroy() - . = ..() - - if(node) - node.disconnect(src) - qdel(network) - - node = null - -/obj/machinery/atmospherics/unary/atmos_init() - if(node) - return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(can_be_node(target, 1)) - node = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/unary/build_network() - if(!network && node) - network = new /datum/pipe_network() - network.normal_members += src - network.build_network(node, src) - - -/obj/machinery/atmospherics/unary/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node) - return network - - return null - -/obj/machinery/atmospherics/unary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network == old_network) - network = new_network - - return 1 - -/obj/machinery/atmospherics/unary/return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network == reference) - results += air_contents - - return results - -/obj/machinery/atmospherics/unary/disconnect(obj/machinery/atmospherics/reference) - if(reference==node) - qdel(network) - node = null - - update_icon() - update_underlays() - - return null - -// Check if there are any other atmos machines in the same turf that will block this machine from initializing. -// Intended for use when a frame-constructable machine (i.e. not made from pipe fittings) wants to wrench down and connect. -// Returns TRUE if something is blocking, FALSE if its okay to continue. -/obj/machinery/atmospherics/unary/proc/check_for_obstacles() - for(var/obj/machinery/atmospherics/M in loc) - if(M == src) continue - if((M.pipe_flags & pipe_flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. - visible_message("\The [src]'s cannot be connected, something is hogging the tile!") - return TRUE - if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER - continue - if(M.get_init_dirs() & get_init_dirs()) // matches at least one direction on either type of pipe - visible_message("\The [src]'s connector can't be connected, there is already a pipe at that location!") - return TRUE - return FALSE +/obj/machinery/atmospherics/unary + dir = SOUTH + initialize_directions = SOUTH + construction_type = /obj/item/pipe/directional + pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF + //layer = TURF_LAYER+0.1 + + var/datum/gas_mixture/air_contents + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + var/welded = 0 //defining this here for ventcrawl stuff + +/obj/machinery/atmospherics/unary/New() + ..() + air_contents = new + + air_contents.volume = 200 + +/obj/machinery/atmospherics/unary/init_dir() + initialize_directions = dir + +// Housekeeping and pipe network stuff below +/obj/machinery/atmospherics/unary/get_neighbor_nodes_for_init() + return list(node) + +/obj/machinery/atmospherics/unary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node) + network = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + return null + +/obj/machinery/atmospherics/unary/Destroy() + . = ..() + + if(node) + node.disconnect(src) + qdel(network) + + node = null + +/obj/machinery/atmospherics/unary/atmos_init() + if(node) + return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(can_be_node(target, 1)) + node = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/unary/build_network() + if(!network && node) + network = new /datum/pipe_network() + network.normal_members += src + network.build_network(node, src) + + +/obj/machinery/atmospherics/unary/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node) + return network + + return null + +/obj/machinery/atmospherics/unary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network == old_network) + network = new_network + + return 1 + +/obj/machinery/atmospherics/unary/return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network == reference) + results += air_contents + + return results + +/obj/machinery/atmospherics/unary/disconnect(obj/machinery/atmospherics/reference) + if(reference==node) + qdel(network) + node = null + + update_icon() + update_underlays() + + return null + +// Check if there are any other atmos machines in the same turf that will block this machine from initializing. +// Intended for use when a frame-constructable machine (i.e. not made from pipe fittings) wants to wrench down and connect. +// Returns TRUE if something is blocking, FALSE if its okay to continue. +/obj/machinery/atmospherics/unary/proc/check_for_obstacles() + for(var/obj/machinery/atmospherics/M in loc) + if(M == src) continue + if((M.pipe_flags & pipe_flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. + visible_message("\The [src]'s cannot be connected, something is hogging the tile!") + return TRUE + if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER + continue + if(M.get_init_dirs() & get_init_dirs()) // matches at least one direction on either type of pipe + visible_message("\The [src]'s connector can't be connected, there is already a pipe at that location!") + return TRUE + return FALSE diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 4b8970fd94d..281d7e48f71 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -1,461 +1,461 @@ -#define DEFAULT_PRESSURE_DELTA 10000 - -#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE -#define INTERNAL_PRESSURE_BOUND 0 -#define PRESSURE_CHECKS 1 - -#define PRESSURE_CHECK_EXTERNAL 1 -#define PRESSURE_CHECK_INTERNAL 2 - -/obj/machinery/atmospherics/unary/vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_vent" - pipe_state = "uvent" - - name = "Air Vent" - desc = "Has a valve and pump attached to it" - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 30000 //7500 W ~ 10 HP //VOREStation Edit - 30000 W - - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes - blocks_emissive = FALSE - - var/area/initial_loc - level = 1 - var/area_uid - var/id_tag = null - - var/hibernate = 0 //Do we even process? - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND - var/internal_pressure_bound = INTERNAL_PRESSURE_BOUND - - var/pressure_checks = PRESSURE_CHECKS - //1: Do not pass external_pressure_bound - //2: Do not pass internal_pressure_bound - //3: Do not pass either - - // Used when handling incoming radio signals requesting default settings - var/external_pressure_bound_default = EXTERNAL_PRESSURE_BOUND - var/internal_pressure_bound_default = INTERNAL_PRESSURE_BOUND - var/pressure_checks_default = PRESSURE_CHECKS - - var/frequency = 1439 - var/datum/radio_frequency/radio_connection - - var/radio_filter_out - var/radio_filter_in - - //var/datum/looping_sound/air_pump/soundloop - var/static/start_sound = 'sound/machines/air_pump/airpumpstart.ogg' - var/static/stop_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' - - -/obj/machinery/atmospherics/unary/vent_pump/on - use_power = USE_POWER_IDLE - icon_state = "map_vent_out" - -/obj/machinery/atmospherics/unary/vent_pump/aux - icon_state = "map_vent_aux" - icon_connect_type = "-aux" - connect_types = CONNECT_TYPE_AUX //connects to aux pipes - -/obj/machinery/atmospherics/unary/vent_pump/siphon - pump_direction = 0 - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on - use_power = USE_POWER_IDLE - icon_state = "map_vent_in" - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos - use_power = USE_POWER_IDLE - icon_state = "map_vent_in" - external_pressure_bound = 0 - external_pressure_bound_default = 0 - internal_pressure_bound = 2000 - internal_pressure_bound_default = 2000 - pressure_checks = 2 - pressure_checks_default = 2 - -/obj/machinery/atmospherics/unary/vent_pump/Initialize() - . = ..() - //soundloop = new(list(src), FALSE) - -/obj/machinery/atmospherics/unary/vent_pump/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP - - icon = null - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - if (!id_tag) - assign_uid() - id_tag = num2text(uid) - -/obj/machinery/atmospherics/unary/vent_pump/proc/update_area() - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - assign_uid() - id_tag = num2text(uid) - - -/obj/machinery/atmospherics/unary/vent_pump/Destroy() - unregister_radio(src, frequency) - if(initial_loc) - initial_loc.air_vent_info -= id_tag - initial_loc.air_vent_names -= id_tag - //QDEL_NULL(soundloop) - return ..() - -/obj/machinery/atmospherics/unary/vent_pump/high_volume - name = "Large Air Vent" - power_channel = EQUIP - power_rating = 45000 //15 kW ~ 20 HP //VOREStation Edit - 45000 - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/aux - icon_state = "map_vent_aux" - icon_connect_type = "-aux" - connect_types = CONNECT_TYPE_AUX //connects to aux pipes - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - -// VOREStation Edit Start - Wall mounted vents -/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted - name = "Wall Mounted Air Vent" - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/can_unwrench() - return FALSE // No way to construct these, so don't let them be removed. - -// Return the air from the turf in "front" of us (opposite the way the pipe is facing) -/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/return_air() - var/turf/T = get_step(src, reverse_dir[dir]) - if(isnull(T)) - return ..() - return T.return_air() - -// VOREStation Edit End - -/obj/machinery/atmospherics/unary/vent_pump/engine - name = "Engine Core Vent" - power_channel = ENVIRON - power_rating = 30000 //15 kW ~ 20 HP - -/obj/machinery/atmospherics/unary/vent_pump/engine/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector - -/obj/machinery/atmospherics/unary/vent_pump/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - cut_overlays() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(welded) - vent_icon += "weld" - playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) - - else if(!use_power || !node || (stat & (NOPOWER|BROKEN))) - vent_icon += "off" - playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) - else - vent_icon += "[pump_direction ? "out" : "in"]" - playsound(src, start_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) - - - add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) - -/obj/machinery/atmospherics/unary/vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - return - else - if(node) - add_underlay(T, node, dir, node.icon_connect_type) - else - add_underlay(T,, dir) - -/obj/machinery/atmospherics/unary/vent_pump/hide() - update_icon() - update_underlays() - -/obj/machinery/atmospherics/unary/vent_pump/proc/can_pump() - if(stat & (NOPOWER|BROKEN)) - return 0 - if(!use_power) - return 0 - if(welded) - return 0 - return 1 - -/obj/machinery/atmospherics/unary/vent_pump/process() - ..() - - if (hibernate) - return 1 - - if (!node) - update_use_power(USE_POWER_OFF) - if(!can_pump()) - return 0 - - var/datum/gas_mixture/environment = return_air() // VOREStation Edit - Use our own proc - - var/power_draw = -1 - - //Figure out the target pressure difference - var/pressure_delta = get_pressure_delta(environment) - //src.visible_message("DEBUG >>> [src]: pressure_delta = [pressure_delta]") - - if((environment.temperature || air_contents.temperature) && pressure_delta > 0.5) - if(pump_direction) //internal -> external - var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) - power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - else //external -> internal - var/transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, (network)? network.volume : 0) - - //limit flow rate from turfs - transfer_moles = min(transfer_moles, environment.total_moles*air_contents.volume/environment.volume) //group_multiplier gets divided out here - power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) - - else - //If we're in an area that is fucking ideal, and we don't have to do anything, chances are we won't next tick either so why redo these calculations? - //JESUS FUCK. THERE ARE LITERALLY 250 OF YOU MOTHERFUCKERS ON ZLEVEL ONE AND YOU DO THIS SHIT EVERY TICK WHEN VERY OFTEN THERE IS NO REASON TO - - if(pump_direction && pressure_checks == PRESSURE_CHECK_EXTERNAL && controller_iteration > 10) //99% of all vents - //Fucking hibernate because you ain't doing shit. - hibernate = 1 - spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly - hibernate = 0 - - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - if(network) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) - var/pressure_delta = DEFAULT_PRESSURE_DELTA - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //internal -> external - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here - if(pressure_checks & PRESSURE_CHECK_INTERNAL) - pressure_delta = min(pressure_delta, air_contents.return_pressure() - internal_pressure_bound) //decreasing the pressure here - else //external -> internal - if(pressure_checks & PRESSURE_CHECK_EXTERNAL) - pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here - if(pressure_checks & PRESSURE_CHECK_INTERNAL) - pressure_delta = min(pressure_delta, internal_pressure_bound - air_contents.return_pressure()) //increasing the pressure here - - return pressure_delta - -/obj/machinery/atmospherics/unary/vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "area" = src.area_uid, - "tag" = src.id_tag, - "device" = "AVP", - "power" = use_power, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "internal" = internal_pressure_bound, - "external" = external_pressure_bound, - "timestamp" = world.time, - "sigtype" = "status", - "power_draw" = last_power_draw, - "flow_rate" = last_flow_rate, - ) - - if(!initial_loc.air_vent_names[id_tag]) - var/new_name = "[initial_loc.name] Vent Pump #[initial_loc.air_vent_names.len+1]" - initial_loc.air_vent_names[id_tag] = new_name - src.name = new_name - initial_loc.air_vent_info[id_tag] = signal.data - - radio_connection.post_signal(src, signal, radio_filter_out) - - return 1 - - -/obj/machinery/atmospherics/unary/vent_pump/atmos_init() - ..() - - //some vents work his own special way - radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null - radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null - if(frequency) - radio_connection = register_radio(src, frequency, frequency, radio_filter_in) - src.broadcast_status() - -/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - - hibernate = 0 - - //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/unary/vent_pump/receive_signal([signal.debug_print()])") - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["purge"] != null) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabalize"] != null) - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["power"] != null) - update_use_power(text2num(signal.data["power"])) - - if(signal.data["power_toggle"] != null) - update_use_power(!use_power) - - if(signal.data["checks"] != null) - if (signal.data["checks"] == "default") - pressure_checks = pressure_checks_default - else - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["checks_toggle"] != null) - pressure_checks = (pressure_checks?0:3) - - if(signal.data["direction"] != null) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["set_internal_pressure"] != null) - if (signal.data["set_internal_pressure"] == "default") - internal_pressure_bound = internal_pressure_bound_default - else - internal_pressure_bound = between(0,text2num(signal.data["set_internal_pressure"]),ONE_ATMOSPHERE*50) - - if(signal.data["set_external_pressure"] != null) - if (signal.data["set_external_pressure"] == "default") - external_pressure_bound = external_pressure_bound_default - else - external_pressure_bound = between(0,text2num(signal.data["set_external_pressure"]),ONE_ATMOSPHERE*50) - - if(signal.data["adjust_internal_pressure"] != null) - internal_pressure_bound = between(0,internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),ONE_ATMOSPHERE*50) - - if(signal.data["adjust_external_pressure"] != null) - external_pressure_bound = between(0,external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),ONE_ATMOSPHERE*50) - - if("reset_external_pressure" in signal.data) - external_pressure_bound = ONE_ATMOSPHERE - - if("reset_internal_pressure" in signal.data) - internal_pressure_bound = 0 - - if(signal.data["init"] != null) - name = signal.data["init"] - return - - if(signal.data["status"] != null) - spawn(2) - broadcast_status() - return //do not update_icon - - //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if (WT.remove_fuel(0,user)) - to_chat(user, "Now welding the vent.") - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - playsound(src, WT.usesound, 50, 1) - if(!welded) - user.visible_message("\The [user] welds the vent shut.", "You weld the vent shut.", "You hear welding.") - welded = 1 - update_icon() - else - user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.") - welded = 0 - update_icon() - else - to_chat(user, "The welding tool needs to be on to start this task.") - else - to_chat(user, "You need more welding fuel to complete this task.") - return 1 - else - ..() - -/obj/machinery/atmospherics/unary/vent_pump/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" - else - . += "You are too far away to read the gauge." - if(welded) - . += "It seems welded shut." - -/obj/machinery/atmospherics/unary/vent_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/unary/vent_pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - var/turf/T = src.loc - if (node && node.level==1 && isturf(T) && !T.is_plating()) - to_chat(user, "You must remove the plating first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -#undef DEFAULT_PRESSURE_DELTA - -#undef EXTERNAL_PRESSURE_BOUND -#undef INTERNAL_PRESSURE_BOUND -#undef PRESSURE_CHECKS - -#undef PRESSURE_CHECK_EXTERNAL -#undef PRESSURE_CHECK_INTERNAL +#define DEFAULT_PRESSURE_DELTA 10000 + +#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE +#define INTERNAL_PRESSURE_BOUND 0 +#define PRESSURE_CHECKS 1 + +#define PRESSURE_CHECK_EXTERNAL 1 +#define PRESSURE_CHECK_INTERNAL 2 + +/obj/machinery/atmospherics/unary/vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_vent" + pipe_state = "uvent" + + name = "Air Vent" + desc = "Has a valve and pump attached to it" + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 30000 //7500 W ~ 10 HP //VOREStation Edit - 30000 W + + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes + blocks_emissive = FALSE + + var/area/initial_loc + level = 1 + var/area_uid + var/id_tag = null + + var/hibernate = 0 //Do we even process? + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = EXTERNAL_PRESSURE_BOUND + var/internal_pressure_bound = INTERNAL_PRESSURE_BOUND + + var/pressure_checks = PRESSURE_CHECKS + //1: Do not pass external_pressure_bound + //2: Do not pass internal_pressure_bound + //3: Do not pass either + + // Used when handling incoming radio signals requesting default settings + var/external_pressure_bound_default = EXTERNAL_PRESSURE_BOUND + var/internal_pressure_bound_default = INTERNAL_PRESSURE_BOUND + var/pressure_checks_default = PRESSURE_CHECKS + + var/frequency = 1439 + var/datum/radio_frequency/radio_connection + + var/radio_filter_out + var/radio_filter_in + + //var/datum/looping_sound/air_pump/soundloop + var/static/start_sound = 'sound/machines/air_pump/airpumpstart.ogg' + var/static/stop_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' + + +/obj/machinery/atmospherics/unary/vent_pump/on + use_power = USE_POWER_IDLE + icon_state = "map_vent_out" + +/obj/machinery/atmospherics/unary/vent_pump/aux + icon_state = "map_vent_aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX //connects to aux pipes + +/obj/machinery/atmospherics/unary/vent_pump/siphon + pump_direction = 0 + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on + use_power = USE_POWER_IDLE + icon_state = "map_vent_in" + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos + use_power = USE_POWER_IDLE + icon_state = "map_vent_in" + external_pressure_bound = 0 + external_pressure_bound_default = 0 + internal_pressure_bound = 2000 + internal_pressure_bound_default = 2000 + pressure_checks = 2 + pressure_checks_default = 2 + +/obj/machinery/atmospherics/unary/vent_pump/Initialize() + . = ..() + //soundloop = new(list(src), FALSE) + +/obj/machinery/atmospherics/unary/vent_pump/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + + icon = null + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + if (!id_tag) + assign_uid() + id_tag = num2text(uid) + +/obj/machinery/atmospherics/unary/vent_pump/proc/update_area() + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + assign_uid() + id_tag = num2text(uid) + + +/obj/machinery/atmospherics/unary/vent_pump/Destroy() + unregister_radio(src, frequency) + if(initial_loc) + initial_loc.air_vent_info -= id_tag + initial_loc.air_vent_names -= id_tag + //QDEL_NULL(soundloop) + return ..() + +/obj/machinery/atmospherics/unary/vent_pump/high_volume + name = "Large Air Vent" + power_channel = EQUIP + power_rating = 45000 //15 kW ~ 20 HP //VOREStation Edit - 45000 + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/aux + icon_state = "map_vent_aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX //connects to aux pipes + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + +// VOREStation Edit Start - Wall mounted vents +/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted + name = "Wall Mounted Air Vent" + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/can_unwrench() + return FALSE // No way to construct these, so don't let them be removed. + +// Return the air from the turf in "front" of us (opposite the way the pipe is facing) +/obj/machinery/atmospherics/unary/vent_pump/high_volume/wall_mounted/return_air() + var/turf/T = get_step(src, reverse_dir[dir]) + if(isnull(T)) + return ..() + return T.return_air() + +// VOREStation Edit End + +/obj/machinery/atmospherics/unary/vent_pump/engine + name = "Engine Core Vent" + power_channel = ENVIRON + power_rating = 30000 //15 kW ~ 20 HP + +/obj/machinery/atmospherics/unary/vent_pump/engine/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 500 //meant to match air injector + +/obj/machinery/atmospherics/unary/vent_pump/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + cut_overlays() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(welded) + vent_icon += "weld" + playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) + + else if(!use_power || !node || (stat & (NOPOWER|BROKEN))) + vent_icon += "off" + playsound(src, stop_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) + else + vent_icon += "[pump_direction ? "out" : "in"]" + playsound(src, start_sound, 25, ignore_walls = FALSE, preference = /datum/client_preference/air_pump_noise) + + + add_overlay(icon_manager.get_atmos_icon("device", , , vent_icon)) + +/obj/machinery/atmospherics/unary/vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + return + else + if(node) + add_underlay(T, node, dir, node.icon_connect_type) + else + add_underlay(T,, dir) + +/obj/machinery/atmospherics/unary/vent_pump/hide() + update_icon() + update_underlays() + +/obj/machinery/atmospherics/unary/vent_pump/proc/can_pump() + if(stat & (NOPOWER|BROKEN)) + return 0 + if(!use_power) + return 0 + if(welded) + return 0 + return 1 + +/obj/machinery/atmospherics/unary/vent_pump/process() + ..() + + if (hibernate) + return 1 + + if (!node) + update_use_power(USE_POWER_OFF) + if(!can_pump()) + return 0 + + var/datum/gas_mixture/environment = return_air() // VOREStation Edit - Use our own proc + + var/power_draw = -1 + + //Figure out the target pressure difference + var/pressure_delta = get_pressure_delta(environment) + //src.visible_message("DEBUG >>> [src]: pressure_delta = [pressure_delta]") + + if((environment.temperature || air_contents.temperature) && pressure_delta > 0.5) + if(pump_direction) //internal -> external + var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + else //external -> internal + var/transfer_moles = calculate_transfer_moles(environment, air_contents, pressure_delta, (network)? network.volume : 0) + + //limit flow rate from turfs + transfer_moles = min(transfer_moles, environment.total_moles*air_contents.volume/environment.volume) //group_multiplier gets divided out here + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + else + //If we're in an area that is fucking ideal, and we don't have to do anything, chances are we won't next tick either so why redo these calculations? + //JESUS FUCK. THERE ARE LITERALLY 250 OF YOU MOTHERFUCKERS ON ZLEVEL ONE AND YOU DO THIS SHIT EVERY TICK WHEN VERY OFTEN THERE IS NO REASON TO + + if(pump_direction && pressure_checks == PRESSURE_CHECK_EXTERNAL && controller_iteration > 10) //99% of all vents + //Fucking hibernate because you ain't doing shit. + hibernate = 1 + spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly + hibernate = 0 + + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + if(network) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/vent_pump/proc/get_pressure_delta(datum/gas_mixture/environment) + var/pressure_delta = DEFAULT_PRESSURE_DELTA + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //internal -> external + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, external_pressure_bound - environment_pressure) //increasing the pressure here + if(pressure_checks & PRESSURE_CHECK_INTERNAL) + pressure_delta = min(pressure_delta, air_contents.return_pressure() - internal_pressure_bound) //decreasing the pressure here + else //external -> internal + if(pressure_checks & PRESSURE_CHECK_EXTERNAL) + pressure_delta = min(pressure_delta, environment_pressure - external_pressure_bound) //decreasing the pressure here + if(pressure_checks & PRESSURE_CHECK_INTERNAL) + pressure_delta = min(pressure_delta, internal_pressure_bound - air_contents.return_pressure()) //increasing the pressure here + + return pressure_delta + +/obj/machinery/atmospherics/unary/vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "area" = src.area_uid, + "tag" = src.id_tag, + "device" = "AVP", + "power" = use_power, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "internal" = internal_pressure_bound, + "external" = external_pressure_bound, + "timestamp" = world.time, + "sigtype" = "status", + "power_draw" = last_power_draw, + "flow_rate" = last_flow_rate, + ) + + if(!initial_loc.air_vent_names[id_tag]) + var/new_name = "[initial_loc.name] Vent Pump #[initial_loc.air_vent_names.len+1]" + initial_loc.air_vent_names[id_tag] = new_name + src.name = new_name + initial_loc.air_vent_info[id_tag] = signal.data + + radio_connection.post_signal(src, signal, radio_filter_out) + + return 1 + + +/obj/machinery/atmospherics/unary/vent_pump/atmos_init() + ..() + + //some vents work his own special way + radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null + radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null + if(frequency) + radio_connection = register_radio(src, frequency, frequency, radio_filter_in) + src.broadcast_status() + +/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + + hibernate = 0 + + //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/atmospherics/unary/vent_pump/receive_signal([signal.debug_print()])") + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["purge"] != null) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabalize"] != null) + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["power"] != null) + update_use_power(text2num(signal.data["power"])) + + if(signal.data["power_toggle"] != null) + update_use_power(!use_power) + + if(signal.data["checks"] != null) + if (signal.data["checks"] == "default") + pressure_checks = pressure_checks_default + else + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["checks_toggle"] != null) + pressure_checks = (pressure_checks?0:3) + + if(signal.data["direction"] != null) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["set_internal_pressure"] != null) + if (signal.data["set_internal_pressure"] == "default") + internal_pressure_bound = internal_pressure_bound_default + else + internal_pressure_bound = between(0,text2num(signal.data["set_internal_pressure"]),ONE_ATMOSPHERE*50) + + if(signal.data["set_external_pressure"] != null) + if (signal.data["set_external_pressure"] == "default") + external_pressure_bound = external_pressure_bound_default + else + external_pressure_bound = between(0,text2num(signal.data["set_external_pressure"]),ONE_ATMOSPHERE*50) + + if(signal.data["adjust_internal_pressure"] != null) + internal_pressure_bound = between(0,internal_pressure_bound + text2num(signal.data["adjust_internal_pressure"]),ONE_ATMOSPHERE*50) + + if(signal.data["adjust_external_pressure"] != null) + external_pressure_bound = between(0,external_pressure_bound + text2num(signal.data["adjust_external_pressure"]),ONE_ATMOSPHERE*50) + + if("reset_external_pressure" in signal.data) + external_pressure_bound = ONE_ATMOSPHERE + + if("reset_internal_pressure" in signal.data) + internal_pressure_bound = 0 + + if(signal.data["init"] != null) + name = signal.data["init"] + return + + if(signal.data["status"] != null) + spawn(2) + broadcast_status() + return //do not update_icon + + //log_admin("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if (WT.remove_fuel(0,user)) + to_chat(user, "Now welding the vent.") + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + playsound(src, WT.usesound, 50, 1) + if(!welded) + user.visible_message("\The [user] welds the vent shut.", "You weld the vent shut.", "You hear welding.") + welded = 1 + update_icon() + else + user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.") + welded = 0 + update_icon() + else + to_chat(user, "The welding tool needs to be on to start this task.") + else + to_chat(user, "You need more welding fuel to complete this task.") + return 1 + else + ..() + +/obj/machinery/atmospherics/unary/vent_pump/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + else + . += "You are too far away to read the gauge." + if(welded) + . += "It seems welded shut." + +/obj/machinery/atmospherics/unary/vent_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/unary/vent_pump/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + var/turf/T = src.loc + if (node && node.level==1 && isturf(T) && !T.is_plating()) + to_chat(user, "You must remove the plating first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +#undef DEFAULT_PRESSURE_DELTA + +#undef EXTERNAL_PRESSURE_BOUND +#undef INTERNAL_PRESSURE_BOUND +#undef PRESSURE_CHECKS + +#undef PRESSURE_CHECK_EXTERNAL +#undef PRESSURE_CHECK_INTERNAL diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index fe3e68130fa..1a57841a694 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -1,300 +1,300 @@ -/obj/machinery/atmospherics/unary/vent_scrubber - icon = 'icons/atmos/vent_scrubber.dmi' - icon_state = "map_scrubber_off" - pipe_state = "scrubber" - - name = "Air Scrubber" - desc = "Has a valve and pump attached to it" - use_power = USE_POWER_OFF - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER //connects to regular and scrubber pipes - - level = 1 - - var/area/initial_loc - var/id_tag = null - var/frequency = 1439 - var/datum/radio_frequency/radio_connection - - var/hibernate = 0 //Do we even process? - var/scrubbing = 1 //0 = siphoning, 1 = scrubbing - var/list/scrubbing_gas = list("carbon_dioxide", "phoron") - - var/panic = 0 //is this scrubber panicked? - - var/area_uid - var/radio_filter_out - var/radio_filter_in - -/obj/machinery/atmospherics/unary/vent_scrubber/on - use_power = USE_POWER_IDLE - icon_state = "map_scrubber_on" - -/obj/machinery/atmospherics/unary/vent_scrubber/New() - ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER - - icon = null - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - if (!id_tag) - assign_uid() - id_tag = num2text(uid) - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/update_area() - initial_loc = get_area(loc) - area_uid = "\ref[initial_loc]" - assign_uid() - id_tag = num2text(uid) - -/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() - unregister_radio(src, frequency) - if(initial_loc) - initial_loc.air_scrub_info -= id_tag - initial_loc.air_scrub_names -= id_tag - return ..() - -/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - cut_overlays() - - var/scrubber_icon = "scrubber" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(!powered()) - scrubber_icon += "off" - else - scrubber_icon += "[use_power ? "[scrubbing ? "on" : "in"]" : "off"]" - - add_overlay(icon_manager.get_atmos_icon("device", , , scrubber_icon)) - -/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - return - else - if(node) - add_underlay(T, node, dir, node.icon_connect_type) - else - add_underlay(T,, dir) - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, radio_filter_in) - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "area" = area_uid, - "tag" = id_tag, - "device" = "AScr", - "timestamp" = world.time, - "power" = use_power, - "scrubbing" = scrubbing, - "panic" = panic, - "filter_o2" = ("oxygen" in scrubbing_gas), - "filter_n2" = ("nitrogen" in scrubbing_gas), - "filter_co2" = ("carbon_dioxide" in scrubbing_gas), - "filter_phoron" = ("phoron" in scrubbing_gas), - "filter_n2o" = ("nitrous_oxide" in scrubbing_gas), - "filter_fuel" = ("volatile_fuel" in scrubbing_gas), - "sigtype" = "status" - ) - if(!initial_loc.air_scrub_names[id_tag]) - var/new_name = "[initial_loc.name] Air Scrubber #[initial_loc.air_scrub_names.len+1]" - initial_loc.air_scrub_names[id_tag] = new_name - src.name = new_name - initial_loc.air_scrub_info[id_tag] = signal.data - radio_connection.post_signal(src, signal, radio_filter_out) - - return 1 - -/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init() - ..() - radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null - radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null - if (frequency) - set_frequency(frequency) - src.broadcast_status() - -/obj/machinery/atmospherics/unary/vent_scrubber/process() - ..() - - if (hibernate) - return 1 - - if (!node) - update_use_power(USE_POWER_OFF) - //broadcast_status() - if(!use_power || (stat & (NOPOWER|BROKEN))) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - - var/power_draw = -1 - if(scrubbing) - //limit flow rate from turfs - var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) //group_multiplier gets divided out here - - power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) - else //Just siphon all air - //limit flow rate from turfs - var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.volume) //group_multiplier gets divided out here - - power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) - - if(scrubbing && power_draw < 0 && controller_iteration > 10) //99% of all scrubbers - //Fucking hibernate because you ain't doing shit. - hibernate = 1 - spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly - hibernate = 0 - - if (power_draw >= 0) - last_power_draw = power_draw - use_power(power_draw) - - if(network) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/vent_scrubber/hide(var/i) //to make the little pipe section invisible, the icon changes. - update_icon() - update_underlays() - -/obj/machinery/atmospherics/unary/vent_scrubber/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) - return 0 - - if(signal.data["power"] != null) - update_use_power(text2num(signal.data["power"])) - if(signal.data["power_toggle"] != null) - update_use_power(!use_power) - - if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing - panic = text2num(signal.data["panic_siphon"]) - if(panic) - update_use_power(USE_POWER_IDLE) - scrubbing = 0 - else - scrubbing = 1 - if(signal.data["toggle_panic_siphon"] != null) - panic = !panic - if(panic) - update_use_power(USE_POWER_IDLE) - scrubbing = 0 - else - scrubbing = 1 - - if(signal.data["scrubbing"] != null) - scrubbing = text2num(signal.data["scrubbing"]) - if(scrubbing) - panic = 0 - if(signal.data["toggle_scrubbing"]) - scrubbing = !scrubbing - if(scrubbing) - panic = 0 - - var/list/toggle = list() - - if(!isnull(signal.data["o2_scrub"]) && text2num(signal.data["o2_scrub"]) != ("oxygen" in scrubbing_gas)) - toggle += "oxygen" - else if(signal.data["toggle_o2_scrub"]) - toggle += "oxygen" - - if(!isnull(signal.data["n2_scrub"]) && text2num(signal.data["n2_scrub"]) != ("nitrogen" in scrubbing_gas)) - toggle += "nitrogen" - else if(signal.data["toggle_n2_scrub"]) - toggle += "nitrogen" - - if(!isnull(signal.data["co2_scrub"]) && text2num(signal.data["co2_scrub"]) != ("carbon_dioxide" in scrubbing_gas)) - toggle += "carbon_dioxide" - else if(signal.data["toggle_co2_scrub"]) - toggle += "carbon_dioxide" - - if(!isnull(signal.data["tox_scrub"]) && text2num(signal.data["tox_scrub"]) != ("phoron" in scrubbing_gas)) - toggle += "phoron" - else if(signal.data["toggle_tox_scrub"]) - toggle += "phoron" - - if(!isnull(signal.data["n2o_scrub"]) && text2num(signal.data["n2o_scrub"]) != ("nitrous_oxide" in scrubbing_gas)) - toggle += "nitrous_oxide" - else if(signal.data["toggle_n2o_scrub"]) - toggle += "nitrous_oxide" - - if(!isnull(signal.data["fuel_scrub"]) && text2num(signal.data["fuel_scrub"]) != ("volatile_fuel" in scrubbing_gas)) - toggle += "volatile_fuel" - else if(signal.data["toggle_fuel_scrub"]) - toggle += "volatile_fuel" - - scrubbing_gas ^= toggle - - if(signal.data["init"] != null) - name = signal.data["init"] - return - - if(signal.data["status"] != null) - spawn(2) - broadcast_status() - return //do not update_icon - -// log_admin("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/unary/vent_scrubber/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - var/turf/T = src.loc - if (node && node.level==1 && isturf(T) && !T.is_plating()) - to_chat(user, "You must remove the plating first.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -/obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" - else - . += "You are too far away to read the gauge." +/obj/machinery/atmospherics/unary/vent_scrubber + icon = 'icons/atmos/vent_scrubber.dmi' + icon_state = "map_scrubber_off" + pipe_state = "scrubber" + + name = "Air Scrubber" + desc = "Has a valve and pump attached to it" + use_power = USE_POWER_OFF + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER //connects to regular and scrubber pipes + + level = 1 + + var/area/initial_loc + var/id_tag = null + var/frequency = 1439 + var/datum/radio_frequency/radio_connection + + var/hibernate = 0 //Do we even process? + var/scrubbing = 1 //0 = siphoning, 1 = scrubbing + var/list/scrubbing_gas = list("carbon_dioxide", "phoron") + + var/panic = 0 //is this scrubber panicked? + + var/area_uid + var/radio_filter_out + var/radio_filter_in + +/obj/machinery/atmospherics/unary/vent_scrubber/on + use_power = USE_POWER_IDLE + icon_state = "map_scrubber_on" + +/obj/machinery/atmospherics/unary/vent_scrubber/New() + ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER + + icon = null + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + if (!id_tag) + assign_uid() + id_tag = num2text(uid) + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/update_area() + initial_loc = get_area(loc) + area_uid = "\ref[initial_loc]" + assign_uid() + id_tag = num2text(uid) + +/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() + unregister_radio(src, frequency) + if(initial_loc) + initial_loc.air_scrub_info -= id_tag + initial_loc.air_scrub_names -= id_tag + return ..() + +/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + cut_overlays() + + var/scrubber_icon = "scrubber" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(!powered()) + scrubber_icon += "off" + else + scrubber_icon += "[use_power ? "[scrubbing ? "on" : "in"]" : "off"]" + + add_overlay(icon_manager.get_atmos_icon("device", , , scrubber_icon)) + +/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(!T.is_plating() && node && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + return + else + if(node) + add_underlay(T, node, dir, node.icon_connect_type) + else + add_underlay(T,, dir) + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, radio_filter_in) + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "area" = area_uid, + "tag" = id_tag, + "device" = "AScr", + "timestamp" = world.time, + "power" = use_power, + "scrubbing" = scrubbing, + "panic" = panic, + "filter_o2" = ("oxygen" in scrubbing_gas), + "filter_n2" = ("nitrogen" in scrubbing_gas), + "filter_co2" = ("carbon_dioxide" in scrubbing_gas), + "filter_phoron" = ("phoron" in scrubbing_gas), + "filter_n2o" = ("nitrous_oxide" in scrubbing_gas), + "filter_fuel" = ("volatile_fuel" in scrubbing_gas), + "sigtype" = "status" + ) + if(!initial_loc.air_scrub_names[id_tag]) + var/new_name = "[initial_loc.name] Air Scrubber #[initial_loc.air_scrub_names.len+1]" + initial_loc.air_scrub_names[id_tag] = new_name + src.name = new_name + initial_loc.air_scrub_info[id_tag] = signal.data + radio_connection.post_signal(src, signal, radio_filter_out) + + return 1 + +/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init() + ..() + radio_filter_in = frequency==initial(frequency)?(RADIO_FROM_AIRALARM):null + radio_filter_out = frequency==initial(frequency)?(RADIO_TO_AIRALARM):null + if (frequency) + set_frequency(frequency) + src.broadcast_status() + +/obj/machinery/atmospherics/unary/vent_scrubber/process() + ..() + + if (hibernate) + return 1 + + if (!node) + update_use_power(USE_POWER_OFF) + //broadcast_status() + if(!use_power || (stat & (NOPOWER|BROKEN))) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + + var/power_draw = -1 + if(scrubbing) + //limit flow rate from turfs + var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SCRUBBER_FLOWRATE/environment.volume) //group_multiplier gets divided out here + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) + else //Just siphon all air + //limit flow rate from turfs + var/transfer_moles = min(environment.total_moles, environment.total_moles*MAX_SIPHON_FLOWRATE/environment.volume) //group_multiplier gets divided out here + + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + if(scrubbing && power_draw < 0 && controller_iteration > 10) //99% of all scrubbers + //Fucking hibernate because you ain't doing shit. + hibernate = 1 + spawn(rand(100,200)) //hibernate for 10 or 20 seconds randomly + hibernate = 0 + + if (power_draw >= 0) + last_power_draw = power_draw + use_power(power_draw) + + if(network) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/vent_scrubber/hide(var/i) //to make the little pipe section invisible, the icon changes. + update_icon() + update_underlays() + +/obj/machinery/atmospherics/unary/vent_scrubber/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) + return 0 + + if(signal.data["power"] != null) + update_use_power(text2num(signal.data["power"])) + if(signal.data["power_toggle"] != null) + update_use_power(!use_power) + + if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing + panic = text2num(signal.data["panic_siphon"]) + if(panic) + update_use_power(USE_POWER_IDLE) + scrubbing = 0 + else + scrubbing = 1 + if(signal.data["toggle_panic_siphon"] != null) + panic = !panic + if(panic) + update_use_power(USE_POWER_IDLE) + scrubbing = 0 + else + scrubbing = 1 + + if(signal.data["scrubbing"] != null) + scrubbing = text2num(signal.data["scrubbing"]) + if(scrubbing) + panic = 0 + if(signal.data["toggle_scrubbing"]) + scrubbing = !scrubbing + if(scrubbing) + panic = 0 + + var/list/toggle = list() + + if(!isnull(signal.data["o2_scrub"]) && text2num(signal.data["o2_scrub"]) != ("oxygen" in scrubbing_gas)) + toggle += "oxygen" + else if(signal.data["toggle_o2_scrub"]) + toggle += "oxygen" + + if(!isnull(signal.data["n2_scrub"]) && text2num(signal.data["n2_scrub"]) != ("nitrogen" in scrubbing_gas)) + toggle += "nitrogen" + else if(signal.data["toggle_n2_scrub"]) + toggle += "nitrogen" + + if(!isnull(signal.data["co2_scrub"]) && text2num(signal.data["co2_scrub"]) != ("carbon_dioxide" in scrubbing_gas)) + toggle += "carbon_dioxide" + else if(signal.data["toggle_co2_scrub"]) + toggle += "carbon_dioxide" + + if(!isnull(signal.data["tox_scrub"]) && text2num(signal.data["tox_scrub"]) != ("phoron" in scrubbing_gas)) + toggle += "phoron" + else if(signal.data["toggle_tox_scrub"]) + toggle += "phoron" + + if(!isnull(signal.data["n2o_scrub"]) && text2num(signal.data["n2o_scrub"]) != ("nitrous_oxide" in scrubbing_gas)) + toggle += "nitrous_oxide" + else if(signal.data["toggle_n2o_scrub"]) + toggle += "nitrous_oxide" + + if(!isnull(signal.data["fuel_scrub"]) && text2num(signal.data["fuel_scrub"]) != ("volatile_fuel" in scrubbing_gas)) + toggle += "volatile_fuel" + else if(signal.data["toggle_fuel_scrub"]) + toggle += "volatile_fuel" + + scrubbing_gas ^= toggle + + if(signal.data["init"] != null) + name = signal.data["init"] + return + + if(signal.data["status"] != null) + spawn(2) + broadcast_status() + return //do not update_icon + +// log_admin("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/unary/vent_scrubber/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + var/turf/T = src.loc + if (node && node.level==1 && isturf(T) && !T.is_plating()) + to_chat(user, "You must remove the plating first.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +/obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + else + . += "You are too far away to read the gauge." diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm index f25ffbeffdd..1a722be6bd3 100644 --- a/code/ATMOSPHERICS/components/valve.dm +++ b/code/ATMOSPHERICS/components/valve.dm @@ -1,311 +1,311 @@ -/obj/machinery/atmospherics/valve - icon = 'icons/atmos/valve.dmi' - icon_state = "map_valve0" - construction_type = /obj/item/pipe/binary - pipe_state = "mvalve" - - name = "manual valve" - desc = "A pipe valve" - - level = 1 - dir = SOUTH - initialize_directions = SOUTH|NORTH - - var/open = 0 - var/openDuringInit = 0 - - - var/datum/pipe_network/network_node1 - var/datum/pipe_network/network_node2 - -/obj/machinery/atmospherics/valve/open - open = 1 - icon_state = "map_valve1" - -/obj/machinery/atmospherics/valve/update_icon(animation) - if(animation) - flick("valve[src.open][!src.open]",src) - else - icon_state = "valve[open]" - -/obj/machinery/atmospherics/valve/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, get_dir(src, node1)) - add_underlay(T, node2, get_dir(src, node2)) - -/obj/machinery/atmospherics/valve/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/valve/init_dir() - switch(dir) - if(NORTH, SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST, WEST) - initialize_directions = EAST|WEST - -/obj/machinery/atmospherics/valve/get_neighbor_nodes_for_init() - return list(node1, node2) - -/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(reference == node1) - network_node1 = new_network - if(open) - network_node2 = new_network - else if(reference == node2) - network_node2 = new_network - if(open) - network_node1 = new_network - - if(new_network.normal_members.Find(src)) - return 0 - - new_network.normal_members += src - - if(open) - if(reference == node1) - if(node2) - return node2.network_expand(new_network, src) - else if(reference == node2) - if(node1) - return node1.network_expand(new_network, src) - - return null - -/obj/machinery/atmospherics/valve/Destroy() - . = ..() - - if(node1) - node1.disconnect(src) - qdel(network_node1) - if(node2) - node2.disconnect(src) - qdel(network_node2) - - node1 = null - node2 = null - -/obj/machinery/atmospherics/valve/proc/open() - if(open) return 0 - - open = 1 - update_icon() - - if(network_node1&&network_node2) - network_node1.merge(network_node2) - network_node2 = network_node1 - - if(network_node1) - network_node1.update = 1 - else if(network_node2) - network_node2.update = 1 - - return 1 - -/obj/machinery/atmospherics/valve/proc/close() - if(!open) - return 0 - - open = 0 - update_icon() - - if(network_node1) - qdel(network_node1) - if(network_node2) - qdel(network_node2) - - build_network() - - return 1 - -/obj/machinery/atmospherics/valve/proc/normalize_dir() - if(dir==3) - set_dir(1) - else if(dir==12) - set_dir(4) - -/obj/machinery/atmospherics/valve/attack_ai(mob/user as mob) - return - -/obj/machinery/atmospherics/valve/attack_hand(mob/user as mob) - src.add_fingerprint(usr) - update_icon(1) - sleep(10) - if (src.open) - src.close() - else - src.open() - -/obj/machinery/atmospherics/valve/process() - ..() - . = PROCESS_KILL - - return - -/obj/machinery/atmospherics/valve/atmos_init() - normalize_dir() - - var/node1_dir - var/node2_dir - - for(var/direction in cardinal) - if(direction&initialize_directions) - if (!node1_dir) - node1_dir = direction - else if (!node2_dir) - node2_dir = direction - - STANDARD_ATMOS_CHOOSE_NODE(1, node1_dir) - STANDARD_ATMOS_CHOOSE_NODE(2, node2_dir) - - build_network() - - update_icon() - update_underlays() - - if(openDuringInit) - close() - open() - openDuringInit = 0 - -/obj/machinery/atmospherics/valve/build_network() - if(!network_node1 && node1) - network_node1 = new /datum/pipe_network() - network_node1.normal_members += src - network_node1.build_network(node1, src) - - if(!network_node2 && node2) - network_node2 = new /datum/pipe_network() - network_node2.normal_members += src - network_node2.build_network(node2, src) - -/obj/machinery/atmospherics/valve/return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network_node1 - - if(reference==node2) - return network_node2 - - return null - -/obj/machinery/atmospherics/valve/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network_node1 == old_network) - network_node1 = new_network - if(network_node2 == old_network) - network_node2 = new_network - - return 1 - -/obj/machinery/atmospherics/valve/return_network_air(datum/pipe_network/reference) - return null - -/obj/machinery/atmospherics/valve/disconnect(obj/machinery/atmospherics/reference) - if(reference==node1) - qdel(network_node1) - node1 = null - - else if(reference==node2) - qdel(network_node2) - node2 = null - - update_underlays() - - return null - -/obj/machinery/atmospherics/valve/digital // can be controlled by AI - name = "digital valve" - desc = "A digitally controlled valve." - icon = 'icons/atmos/digital_valve.dmi' - pipe_state = "dvalve" - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/valve/digital/Destroy() - unregister_radio(src, frequency) - . = ..() - -/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user as mob) - if(!powered()) - return - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - ..() - -/obj/machinery/atmospherics/valve/digital/open - open = 1 - icon_state = "map_valve1" - -/obj/machinery/atmospherics/valve/digital/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/valve/digital/update_icon() - ..() - if(!powered()) - icon_state = "valve[open]nopower" - -/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/valve/digital/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/valve/digital/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id)) - return 0 - - switch(signal.data["command"]) - if("valve_open") - if(!open) - open() - - if("valve_close") - if(open) - close() - - if("valve_toggle") - if(open) - close() - else - open() - -/obj/machinery/atmospherics/valve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if (!W.is_wrench()) - return ..() - if (istype(src, /obj/machinery/atmospherics/valve/digital) && !src.allowed(user)) - to_chat(user, "Access denied.") - return 1 - if(!can_unwrench()) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - deconstruct() - -/obj/machinery/atmospherics/valve/examine(mob/user) - . = ..() - . += "It is [open ? "open" : "closed"]." +/obj/machinery/atmospherics/valve + icon = 'icons/atmos/valve.dmi' + icon_state = "map_valve0" + construction_type = /obj/item/pipe/binary + pipe_state = "mvalve" + + name = "manual valve" + desc = "A pipe valve" + + level = 1 + dir = SOUTH + initialize_directions = SOUTH|NORTH + + var/open = 0 + var/openDuringInit = 0 + + + var/datum/pipe_network/network_node1 + var/datum/pipe_network/network_node2 + +/obj/machinery/atmospherics/valve/open + open = 1 + icon_state = "map_valve1" + +/obj/machinery/atmospherics/valve/update_icon(animation) + if(animation) + flick("valve[src.open][!src.open]",src) + else + icon_state = "valve[open]" + +/obj/machinery/atmospherics/valve/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, get_dir(src, node1)) + add_underlay(T, node2, get_dir(src, node2)) + +/obj/machinery/atmospherics/valve/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/valve/init_dir() + switch(dir) + if(NORTH, SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST, WEST) + initialize_directions = EAST|WEST + +/obj/machinery/atmospherics/valve/get_neighbor_nodes_for_init() + return list(node1, node2) + +/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(reference == node1) + network_node1 = new_network + if(open) + network_node2 = new_network + else if(reference == node2) + network_node2 = new_network + if(open) + network_node1 = new_network + + if(new_network.normal_members.Find(src)) + return 0 + + new_network.normal_members += src + + if(open) + if(reference == node1) + if(node2) + return node2.network_expand(new_network, src) + else if(reference == node2) + if(node1) + return node1.network_expand(new_network, src) + + return null + +/obj/machinery/atmospherics/valve/Destroy() + . = ..() + + if(node1) + node1.disconnect(src) + qdel(network_node1) + if(node2) + node2.disconnect(src) + qdel(network_node2) + + node1 = null + node2 = null + +/obj/machinery/atmospherics/valve/proc/open() + if(open) return 0 + + open = 1 + update_icon() + + if(network_node1&&network_node2) + network_node1.merge(network_node2) + network_node2 = network_node1 + + if(network_node1) + network_node1.update = 1 + else if(network_node2) + network_node2.update = 1 + + return 1 + +/obj/machinery/atmospherics/valve/proc/close() + if(!open) + return 0 + + open = 0 + update_icon() + + if(network_node1) + qdel(network_node1) + if(network_node2) + qdel(network_node2) + + build_network() + + return 1 + +/obj/machinery/atmospherics/valve/proc/normalize_dir() + if(dir==3) + set_dir(1) + else if(dir==12) + set_dir(4) + +/obj/machinery/atmospherics/valve/attack_ai(mob/user as mob) + return + +/obj/machinery/atmospherics/valve/attack_hand(mob/user as mob) + src.add_fingerprint(usr) + update_icon(1) + sleep(10) + if (src.open) + src.close() + else + src.open() + +/obj/machinery/atmospherics/valve/process() + ..() + . = PROCESS_KILL + + return + +/obj/machinery/atmospherics/valve/atmos_init() + normalize_dir() + + var/node1_dir + var/node2_dir + + for(var/direction in cardinal) + if(direction&initialize_directions) + if (!node1_dir) + node1_dir = direction + else if (!node2_dir) + node2_dir = direction + + STANDARD_ATMOS_CHOOSE_NODE(1, node1_dir) + STANDARD_ATMOS_CHOOSE_NODE(2, node2_dir) + + build_network() + + update_icon() + update_underlays() + + if(openDuringInit) + close() + open() + openDuringInit = 0 + +/obj/machinery/atmospherics/valve/build_network() + if(!network_node1 && node1) + network_node1 = new /datum/pipe_network() + network_node1.normal_members += src + network_node1.build_network(node1, src) + + if(!network_node2 && node2) + network_node2 = new /datum/pipe_network() + network_node2.normal_members += src + network_node2.build_network(node2, src) + +/obj/machinery/atmospherics/valve/return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network_node1 + + if(reference==node2) + return network_node2 + + return null + +/obj/machinery/atmospherics/valve/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network_node1 == old_network) + network_node1 = new_network + if(network_node2 == old_network) + network_node2 = new_network + + return 1 + +/obj/machinery/atmospherics/valve/return_network_air(datum/pipe_network/reference) + return null + +/obj/machinery/atmospherics/valve/disconnect(obj/machinery/atmospherics/reference) + if(reference==node1) + qdel(network_node1) + node1 = null + + else if(reference==node2) + qdel(network_node2) + node2 = null + + update_underlays() + + return null + +/obj/machinery/atmospherics/valve/digital // can be controlled by AI + name = "digital valve" + desc = "A digitally controlled valve." + icon = 'icons/atmos/digital_valve.dmi' + pipe_state = "dvalve" + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/valve/digital/Destroy() + unregister_radio(src, frequency) + . = ..() + +/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user as mob) + if(!powered()) + return + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + ..() + +/obj/machinery/atmospherics/valve/digital/open + open = 1 + icon_state = "map_valve1" + +/obj/machinery/atmospherics/valve/digital/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/valve/digital/update_icon() + ..() + if(!powered()) + icon_state = "valve[open]nopower" + +/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/valve/digital/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/valve/digital/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id)) + return 0 + + switch(signal.data["command"]) + if("valve_open") + if(!open) + open() + + if("valve_close") + if(open) + close() + + if("valve_toggle") + if(open) + close() + else + open() + +/obj/machinery/atmospherics/valve/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if (!W.has_tool_quality(TOOL_WRENCH)) + return ..() + if (istype(src, /obj/machinery/atmospherics/valve/digital) && !src.allowed(user)) + to_chat(user, "Access denied.") + return 1 + if(!can_unwrench()) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + deconstruct() + +/obj/machinery/atmospherics/valve/examine(mob/user) + . = ..() + . += "It is [open ? "open" : "closed"]." diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index a611e30a893..2306775ad59 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -1,90 +1,88 @@ -var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SSmachines - -/datum/pipe_network - var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network - var/volume = 0 //caches the total volume for atmos machines to use in gas calculations - - var/list/obj/machinery/atmospherics/normal_members = list() - var/list/datum/pipeline/line_members = list() - //membership roster to go through for updates and what not - - var/list/leaks = list() - - var/update = 1 - //var/datum/gas_mixture/air_transient = null - -/datum/pipe_network/Destroy() - STOP_PROCESSING_PIPENET(src) - for(var/datum/pipeline/line_member in line_members) - line_member.network = null - for(var/obj/machinery/atmospherics/normal_member in normal_members) - normal_member.reassign_network(src, null) - gases.Cut() // Do not qdel the gases, we don't own them - leaks.Cut() - return ..() - -/datum/pipe_network/process() - //Equalize gases amongst pipe if called for - if(update) - update = 0 - reconcile_air() //equalize_gases(gases) - - listclearnulls(leaks) // Let's not have forever-seals. - - //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport - //for(var/datum/pipeline/line_member in line_members) - // line_member.process() - -/datum/pipe_network/proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) - //Purpose: Generate membership roster - //Notes: Assuming that members will add themselves to appropriate roster in network_expand() - - if(!start_normal) - qdel(src) - return - - start_normal.network_expand(src, reference) - - update_network_gases() - - if((normal_members.len>0)||(line_members.len>0)) - START_PROCESSING_PIPENET(src) - else - qdel(src) - -/datum/pipe_network/proc/merge(datum/pipe_network/giver) - if(giver==src) return 0 - - normal_members |= giver.normal_members - - line_members |= giver.line_members - - leaks |= giver.leaks - - for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) - normal_member.reassign_network(giver, src) - - for(var/datum/pipeline/line_member in giver.line_members) - line_member.network = src - - update_network_gases() - return 1 - -/datum/pipe_network/proc/update_network_gases() - //Go through membership roster and make sure gases is up to date - - gases = list() - volume = 0 - - for(var/obj/machinery/atmospherics/normal_member in normal_members) - var/result = normal_member.return_network_air(src) - if(result) gases += result - - for(var/datum/pipeline/line_member in line_members) - gases += line_member.air - - for(var/datum/gas_mixture/air in gases) - volume += air.volume - -/datum/pipe_network/proc/reconcile_air() - equalize_gases(gases) +/datum/pipe_network + var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network + var/volume = 0 //caches the total volume for atmos machines to use in gas calculations + + var/list/obj/machinery/atmospherics/normal_members = list() + var/list/datum/pipeline/line_members = list() + //membership roster to go through for updates and what not + + var/list/leaks = list() + + var/update = 1 + //var/datum/gas_mixture/air_transient = null + +/datum/pipe_network/Destroy() + STOP_PROCESSING_PIPENET(src) + for(var/datum/pipeline/line_member in line_members) + line_member.network = null + for(var/obj/machinery/atmospherics/normal_member in normal_members) + normal_member.reassign_network(src, null) + gases.Cut() // Do not qdel the gases, we don't own them + leaks.Cut() + return ..() + +/datum/pipe_network/process() + //Equalize gases amongst pipe if called for + if(update) + update = 0 + reconcile_air() //equalize_gases(gases) + + listclearnulls(leaks) // Let's not have forever-seals. + + //Give pipelines their process call for pressure checking and what not. Have to remove pressure checks for the time being as pipes dont radiate heat - Mport + //for(var/datum/pipeline/line_member in line_members) + // line_member.process() + +/datum/pipe_network/proc/build_network(obj/machinery/atmospherics/start_normal, obj/machinery/atmospherics/reference) + //Purpose: Generate membership roster + //Notes: Assuming that members will add themselves to appropriate roster in network_expand() + + if(!start_normal) + qdel(src) + return + + start_normal.network_expand(src, reference) + + update_network_gases() + + if((normal_members.len>0)||(line_members.len>0)) + START_PROCESSING_PIPENET(src) + else + qdel(src) + +/datum/pipe_network/proc/merge(datum/pipe_network/giver) + if(giver==src) return 0 + + normal_members |= giver.normal_members + + line_members |= giver.line_members + + leaks |= giver.leaks + + for(var/obj/machinery/atmospherics/normal_member in giver.normal_members) + normal_member.reassign_network(giver, src) + + for(var/datum/pipeline/line_member in giver.line_members) + line_member.network = src + + update_network_gases() + return 1 + +/datum/pipe_network/proc/update_network_gases() + //Go through membership roster and make sure gases is up to date + + gases = list() + volume = 0 + + for(var/obj/machinery/atmospherics/normal_member in normal_members) + var/result = normal_member.return_network_air(src) + if(result) gases += result + + for(var/datum/pipeline/line_member in line_members) + gases += line_member.air + + for(var/datum/gas_mixture/air in gases) + volume += air.volume + +/datum/pipe_network/proc/reconcile_air() + equalize_gases(gases) diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 7a1f5cce953..e3b91813ba0 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,241 +1,241 @@ - -/datum/pipeline - var/datum/gas_mixture/air - - var/list/obj/machinery/atmospherics/pipe/members - var/list/obj/machinery/atmospherics/pipe/edges //Used for building networks - - // Nodes that are leaking. Used for A.S. Valves. - var/list/leaks = list() - - var/datum/pipe_network/network - - var/alert_pressure = 0 - -/datum/pipeline/Destroy() - QDEL_NULL(network) - - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - members = null - edges = null - leaks = null - . = ..() - -/datum/pipeline/process()//This use to be called called from the pipe networks - - //Check to see if pressure is within acceptable limits - var/pressure = air.return_pressure() - if(pressure > alert_pressure) - for(var/obj/machinery/atmospherics/pipe/member in members) - if(!member.check_pressure(pressure)) - break //Only delete 1 pipe per process - -/datum/pipeline/proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio - - for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.copy_from(air) - member.air_temporary.volume = member.volume - member.air_temporary.multiply(member.volume / air.volume) - -/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/pipe/base) - air = new - - var/list/possible_expansions = list(base) - members = list(base) - edges = list() - - var/volume = base.volume - base.parent = src - alert_pressure = base.alert_pressure - - if(base.air_temporary) - air = base.air_temporary - base.air_temporary = null - else - air = new - - if(base.leaking) - leaks |= base - - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) - - var/list/result = borderline.pipeline_expansion() - var/edge_check = result.len - - if(result.len>0) - for(var/obj/machinery/atmospherics/pipe/item in result) - - if(item.in_stasis) - continue - - if(!members.Find(item)) - members += item - possible_expansions += item - - volume += item.volume - item.parent = src - - alert_pressure = min(alert_pressure, item.alert_pressure) - - if(item.air_temporary) - air.merge(item.air_temporary) - - if(item.leaking) - leaks |= item - - edge_check-- - - if(edge_check>0) - edges += borderline - - possible_expansions -= borderline - - air.volume = volume - -/datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - - if(new_network.line_members.Find(src)) - return 0 - - new_network.line_members += src - - network = new_network - network.leaks |= leaks - - for(var/obj/machinery/atmospherics/pipe/edge in edges) - for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) - if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) - result.network_expand(new_network, edge) - - return 1 - -/datum/pipeline/proc/return_network(obj/machinery/atmospherics/reference) - if(!network) - network = new /datum/pipe_network() - network.build_network(src, null) - //technically passing these parameters should not be allowed - //however pipe_network.build_network(..) and pipeline.network_extend(...) - // were setup to properly handle this case - - return network - -/datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) - var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) - air_sample.volume = mingle_volume - - if(istype(target) && target.zone) - //Have to consider preservation of group statuses - var/datum/gas_mixture/turf_copy = new - var/datum/gas_mixture/turf_original = new - - turf_copy.copy_from(target.zone.air) - turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group - turf_original.copy_from(turf_copy) - - equalize_gases(list(air_sample, turf_copy)) - air.merge(air_sample) - - - target.zone.air.remove(turf_original.total_moles) - target.zone.air.merge(turf_copy) - - else - var/datum/gas_mixture/turf_air = target.return_air() - - equalize_gases(list(air_sample, turf_air)) - air.merge(air_sample) - //turf_air already modified by equalize_gases() - - if(network) - network.update = 1 - -/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target - - if (modeled_location.special_temperature) - air.temperature += thermal_conductivity * (modeled_location.special_temperature - air.temperature) - if (air.temperature < TCMB) - air.temperature = TCMB - if (network) - network.update = TRUE - - if(modeled_location.blocks_air) - - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - if(modeled_location.zone) - delta_temperature = (air.temperature - modeled_location.zone.air.temperature) - sharer_heat_capacity = modeled_location.zone.air.heat_capacity() - else - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - if(modeled_location.zone) - modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier - else - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - if(network) - network.update = 1 - -//surface must be the surface area in m^2 -/datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) - var/gas_density = air.total_moles/air.volume - thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio - - // We only get heat from the star on the exposed surface area. - // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. - var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity - - // Previously, the temperature would enter equilibrium at 26C or 294K. - // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. - // It currently should stabilise at 129.6K or -143.6C - heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 - - air.add_thermal_energy(heat_gain) - if(network) - network.update = 1 + +/datum/pipeline + var/datum/gas_mixture/air + + var/list/obj/machinery/atmospherics/pipe/members + var/list/obj/machinery/atmospherics/pipe/edges //Used for building networks + + // Nodes that are leaking. Used for A.S. Valves. + var/list/leaks = list() + + var/datum/pipe_network/network + + var/alert_pressure = 0 + +/datum/pipeline/Destroy() + QDEL_NULL(network) + + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + members = null + edges = null + leaks = null + . = ..() + +/datum/pipeline/process()//This use to be called called from the pipe networks + + //Check to see if pressure is within acceptable limits + var/pressure = air.return_pressure() + if(pressure > alert_pressure) + for(var/obj/machinery/atmospherics/pipe/member in members) + if(!member.check_pressure(pressure)) + break //Only delete 1 pipe per process + +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.copy_from(air) + member.air_temporary.volume = member.volume + member.air_temporary.multiply(member.volume / air.volume) + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/pipe/base) + air = new + + var/list/possible_expansions = list(base) + members = list(base) + edges = list() + + var/volume = base.volume + base.parent = src + alert_pressure = base.alert_pressure + + if(base.air_temporary) + air = base.air_temporary + base.air_temporary = null + else + air = new + + if(base.leaking) + leaks |= base + + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/pipe/borderline in possible_expansions) + + var/list/result = borderline.pipeline_expansion() + var/edge_check = result.len + + if(result.len>0) + for(var/obj/machinery/atmospherics/pipe/item in result) + + if(item.in_stasis) + continue + + if(!members.Find(item)) + members += item + possible_expansions += item + + volume += item.volume + item.parent = src + + alert_pressure = min(alert_pressure, item.alert_pressure) + + if(item.air_temporary) + air.merge(item.air_temporary) + + if(item.leaking) + leaks |= item + + edge_check-- + + if(edge_check>0) + edges += borderline + + possible_expansions -= borderline + + air.volume = volume + +/datum/pipeline/proc/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + + if(new_network.line_members.Find(src)) + return 0 + + new_network.line_members += src + + network = new_network + network.leaks |= leaks + + for(var/obj/machinery/atmospherics/pipe/edge in edges) + for(var/obj/machinery/atmospherics/result in edge.pipeline_expansion()) + if(!istype(result,/obj/machinery/atmospherics/pipe) && (result!=reference)) + result.network_expand(new_network, edge) + + return 1 + +/datum/pipeline/proc/return_network(obj/machinery/atmospherics/reference) + if(!network) + network = new /datum/pipe_network() + network.build_network(src, null) + //technically passing these parameters should not be allowed + //however pipe_network.build_network(..) and pipeline.network_extend(...) + // were setup to properly handle this case + + return network + +/datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) + var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) + air_sample.volume = mingle_volume + + if(istype(target) && target.zone) + //Have to consider preservation of group statuses + var/datum/gas_mixture/turf_copy = new + var/datum/gas_mixture/turf_original = new + + turf_copy.copy_from(target.zone.air) + turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group + turf_original.copy_from(turf_copy) + + equalize_gases(list(air_sample, turf_copy)) + air.merge(air_sample) + + + target.zone.air.remove(turf_original.total_moles) + target.zone.air.merge(turf_copy) + + else + var/datum/gas_mixture/turf_air = target.return_air() + + equalize_gases(list(air_sample, turf_air)) + air.merge(air_sample) + //turf_air already modified by equalize_gases() + + if(network) + network.update = 1 + +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target + + if (modeled_location.special_temperature) + air.temperature += thermal_conductivity * (modeled_location.special_temperature - air.temperature) + if (air.temperature < TCMB) + air.temperature = TCMB + if (network) + network.update = TRUE + + if(modeled_location.blocks_air) + + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + modeled_location.temperature += heat/modeled_location.heat_capacity + + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 + + if(modeled_location.zone) + delta_temperature = (air.temperature - modeled_location.zone.air.temperature) + sharer_heat_capacity = modeled_location.zone.air.heat_capacity() + else + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + if(modeled_location.zone) + modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier + else + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + if(network) + network.update = 1 + +//surface must be the surface area in m^2 +/datum/pipeline/proc/radiate_heat_to_space(surface, thermal_conductivity) + var/gas_density = air.total_moles/air.volume + thermal_conductivity *= min(gas_density / ( RADIATOR_OPTIMUM_PRESSURE/(R_IDEAL_GAS_EQUATION*GAS_CRITICAL_TEMPERATURE) ), 1) //mult by density ratio + + // We only get heat from the star on the exposed surface area. + // If the HE pipes gain more energy from AVERAGE_SOLAR_RADIATION than they can radiate, then they have a net heat increase. + var/heat_gain = AVERAGE_SOLAR_RADIATION * (RADIATOR_EXPOSED_SURFACE_AREA_RATIO * surface) * thermal_conductivity + + // Previously, the temperature would enter equilibrium at 26C or 294K. + // Only would happen if both sides (all 2 square meters of surface area) were exposed to sunlight. We now assume it aligned edge on. + // It currently should stabilise at 129.6K or -143.6C + heat_gain -= surface * STEFAN_BOLTZMANN_CONSTANT * thermal_conductivity * (air.temperature - COSMIC_RADIATION_TEMPERATURE) ** 4 + + air.add_thermal_energy(heat_gain) + if(network) + network.update = 1 diff --git a/code/ATMOSPHERICS/pipes/pipe_base.dm b/code/ATMOSPHERICS/pipes/pipe_base.dm index 0d687b8db05..d06b192a1a3 100644 --- a/code/ATMOSPHERICS/pipes/pipe_base.dm +++ b/code/ATMOSPHERICS/pipes/pipe_base.dm @@ -116,7 +116,7 @@ if(istype(W,/obj/item/device/pipe_painter)) return 0 - if (!W.is_wrench()) + if (!W.has_tool_quality(TOOL_WRENCH)) return ..() var/turf/T = src.loc if (level==1 && isturf(T) && !T.is_plating()) diff --git a/code/ATMOSPHERICS/pipes/simple.dm b/code/ATMOSPHERICS/pipes/simple.dm index 1f427fb151d..def462a354e 100644 --- a/code/ATMOSPHERICS/pipes/simple.dm +++ b/code/ATMOSPHERICS/pipes/simple.dm @@ -96,7 +96,7 @@ node1 = null if(node2) node2.disconnect(src) - node1 = null + node2 = null . = ..() diff --git a/code/ATMOSPHERICS/pipes/tank_vr.dm b/code/ATMOSPHERICS/pipes/tank_vr.dm index 32341ad8f3b..b95c87ebd5b 100644 --- a/code/ATMOSPHERICS/pipes/tank_vr.dm +++ b/code/ATMOSPHERICS/pipes/tank_vr.dm @@ -1,5 +1,5 @@ -/obj/machinery/atmospherics/pipe/tank/phoron/full - start_pressure = 15000 - -/obj/machinery/atmospherics/pipe/tank/air/full +/obj/machinery/atmospherics/pipe/tank/phoron/full + start_pressure = 15000 + +/obj/machinery/atmospherics/pipe/tank/air/full start_pressure = 15000 \ No newline at end of file diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm index b780679d660..21be29d25e1 100644 --- a/code/ZAS/Connection.dm +++ b/code/ZAS/Connection.dm @@ -165,4 +165,8 @@ Class Procs: return - //to_world("valid.") \ No newline at end of file + //to_world("valid.") + +#undef CONNECTION_DIRECT +#undef CONNECTION_SPACE +#undef CONNECTION_INVALID diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index 015ffa36a92..5c3a24d882a 100644 --- a/code/ZAS/Fire.dm +++ b/code/ZAS/Fire.dm @@ -6,8 +6,6 @@ The more pressure, the more boom. If it gains pressure too slowly, it may leak or just rupture instead of exploding. */ -//#define FIREDBG - /turf/var/obj/fire/fire = null //Some legacy definitions so fires can be started. @@ -365,7 +363,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin var/total_combustables = (total_fuel + total_oxidizers) var/active_combustables = (FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT + 1)*reaction_limit - if(total_combustables > 0) + if(total_combustables > 0 && total_moles > 0) //slows down the burning when the concentration of the reactants is low var/damping_multiplier = min(1, active_combustables / (total_moles/group_multiplier)) diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index 982fda8fa16..2fed75dbe09 100644 --- a/code/__byond_version_compat.dm +++ b/code/__byond_version_compat.dm @@ -1,74 +1,74 @@ -#if DM_VERSION >= 515 -#error PLEASE MAKE SURE THAT 515 IS PROPERLY TESTED AND WORKS. ESPECIALLY THE SAVE-FILES HAVE TO WORK. -#error Additionally: Make sure that the GitHub Workflow was updated to BYOND 515 as well. -#endif - -// These defines are from __513_compatibility.dm -- Please Sort -#define CLAMP(CLVALUE, CLMIN, CLMAX) clamp(CLVALUE, CLMIN, CLMAX) -#define TAN(x) tan(x) -#define ATAN2(x, y) arctan(x, y) -#define between(x, y, z) clamp(y, x, z) - -// This file contains defines allowing targeting byond versions newer than the supported - -//Update this whenever you need to take advantage of more recent byond features -#define MIN_COMPILER_VERSION 514 -#define MIN_COMPILER_BUILD 1556 -#if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) -//Don't forget to update this part -#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. -#error You need version 514.1556 or higher -#endif - -#if (DM_VERSION == 514 && DM_BUILD > 1575 && DM_BUILD <= 1577) -#error Your version of BYOND currently has a crashing issue that will prevent you from running Dream Daemon test servers. -#error We require developers to test their content, so an inability to test means we cannot allow the compile. -#error Please consider downgrading to 514.1575 or lower. -#endif - -// Keep savefile compatibilty at minimum supported level -#if DM_VERSION >= 515 -/savefile/byond_version = MIN_COMPILER_VERSION -#endif - -// 515 split call for external libraries into call_ext -#if DM_VERSION < 515 -#define LIBCALL call -#else -#define LIBCALL call_ext -#endif - -// So we want to have compile time guarantees these methods exist on local type, unfortunately 515 killed the .proc/procname and .verb/verbname syntax so we have to use nameof() -// For the record: GLOBAL_VERB_REF would be useless as verbs can't be global. - -#if DM_VERSION < 515 - -/// Call by name proc references, checks if the proc exists on either this type or as a global proc. -#define PROC_REF(X) (.proc/##X) -/// Call by name verb references, checks if the verb exists on either this type or as a global verb. -#define VERB_REF(X) (.verb/##X) - -/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc -#define TYPE_PROC_REF(TYPE, X) (##TYPE.proc/##X) -/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb -#define TYPE_VERB_REF(TYPE, X) (##TYPE.verb/##X) - -/// Call by name proc reference, checks if the proc is an existing global proc -#define GLOBAL_PROC_REF(X) (/proc/##X) - -#else - -/// Call by name proc references, checks if the proc exists on either this type or as a global proc. -#define PROC_REF(X) (nameof(.proc/##X)) -/// Call by name verb references, checks if the verb exists on either this type or as a global verb. -#define VERB_REF(X) (nameof(.verb/##X)) - -/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc -#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X)) -/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb -#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X)) - -/// Call by name proc reference, checks if the proc is an existing global proc -#define GLOBAL_PROC_REF(X) (/proc/##X) - -#endif +#if DM_VERSION >= 515 +#warn PLEASE MAKE SURE THAT 515 IS PROPERLY TESTED AND WORKS. ESPECIALLY THE SAVE-FILES HAVE TO WORK. +#warn Additionally: Make sure that the GitHub Workflow was updated to BYOND 515 as well. +#endif + +// These defines are from __513_compatibility.dm -- Please Sort +#define CLAMP(CLVALUE, CLMIN, CLMAX) clamp(CLVALUE, CLMIN, CLMAX) +#define TAN(x) tan(x) +#define ATAN2(x, y) arctan(x, y) +#define between(x, y, z) clamp(y, x, z) + +// This file contains defines allowing targeting byond versions newer than the supported + +//Update this whenever you need to take advantage of more recent byond features +#define MIN_COMPILER_VERSION 514 +#define MIN_COMPILER_BUILD 1556 +#if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) +//Don't forget to update this part +#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. +#error You need version 514.1556 or higher +#endif + +#if (DM_VERSION == 514 && DM_BUILD > 1575 && DM_BUILD <= 1577) +#error Your version of BYOND currently has a crashing issue that will prevent you from running Dream Daemon test servers. +#error We require developers to test their content, so an inability to test means we cannot allow the compile. +#error Please consider downgrading to 514.1575 or lower. +#endif + +// Keep savefile compatibilty at minimum supported level +#if DM_VERSION >= 515 +/savefile/byond_version = MIN_COMPILER_VERSION +#endif + +// 515 split call for external libraries into call_ext +#if DM_VERSION < 515 +#define LIBCALL call +#else +#define LIBCALL call_ext +#endif + +// So we want to have compile time guarantees these methods exist on local type, unfortunately 515 killed the .proc/procname and .verb/verbname syntax so we have to use nameof() +// For the record: GLOBAL_VERB_REF would be useless as verbs can't be global. + +#if DM_VERSION < 515 + +/// Call by name proc references, checks if the proc exists on either this type or as a global proc. +#define PROC_REF(X) (.proc/##X) +/// Call by name verb references, checks if the verb exists on either this type or as a global verb. +#define VERB_REF(X) (.verb/##X) + +/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc +#define TYPE_PROC_REF(TYPE, X) (##TYPE.proc/##X) +/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb +#define TYPE_VERB_REF(TYPE, X) (##TYPE.verb/##X) + +/// Call by name proc reference, checks if the proc is an existing global proc +#define GLOBAL_PROC_REF(X) (/proc/##X) + +#else + +/// Call by name proc references, checks if the proc exists on either this type or as a global proc. +#define PROC_REF(X) (nameof(.proc/##X)) +/// Call by name verb references, checks if the verb exists on either this type or as a global verb. +#define VERB_REF(X) (nameof(.verb/##X)) + +/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc +#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X)) +/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb +#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X)) + +/// Call by name proc reference, checks if the proc is an existing global proc +#define GLOBAL_PROC_REF(X) (/proc/##X) + +#endif diff --git a/code/__datastructures/globals.dm b/code/__defines/__globals.dm similarity index 60% rename from code/__datastructures/globals.dm rename to code/__defines/__globals.dm index a51bad06dbd..d355da20bc0 100644 --- a/code/__datastructures/globals.dm +++ b/code/__defines/__globals.dm @@ -1,61 +1,61 @@ // See also controllers/globals.dm -// Creates a global initializer with a given InitValue expression, do not use outside this file +/// Creates a global initializer with a given InitValue expression, do not use #define GLOBAL_MANAGED(X, InitValue)\ /datum/controller/global_vars/proc/InitGlobal##X(){\ ##X = ##InitValue;\ gvars_datum_init_order += #X;\ } -// Creates an empty global initializer, do not use outside this file +/// Creates an empty global initializer, do not use #define GLOBAL_UNMANAGED(X) /datum/controller/global_vars/proc/InitGlobal##X() { return; } -// Prevents a given global from being VV'd +/// Prevents a given global from being VV'd #ifndef TESTING #define GLOBAL_PROTECT(X)\ /datum/controller/global_vars/InitGlobal##X(){\ ..();\ - gvars_datum_protected_varlist += #X;\ + gvars_datum_protected_varlist[#X] = TRUE;\ } #else #define GLOBAL_PROTECT(X) #endif -// Standard BYOND global, do not use outside this file +/// Standard BYOND global, seriously do not use without an earthshakingly good reason #define GLOBAL_REAL_VAR(X) var/global/##X -// Standard typed BYOND global, do not use outside this file + +/// Standard typed BYOND global, seriously do not use without an earthshakingly good reason #define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X -// Defines a global var on the controller, do not use outside this file. +/// Defines a global var on the controller, do not use #define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X -// Create an untyped global with an initializer expression +/// Create an untyped global with an initializer expression #define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue) -// Create a global const var, do not use +/// Create a global const var, do not use #define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X) -// Create a list global with an initializer expression +/// Create a list global with an initializer expression #define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue) -// Create a list global that is initialized as an empty list +/// Create a list global that is initialized as an empty list #define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list()) -// Create a typed list global with an initializer expression +/// Create a typed list global with an initializer expression #define GLOBAL_LIST_INIT_TYPED(X, Typepath, InitValue) GLOBAL_RAW(/list##Typepath/X); GLOBAL_MANAGED(X, InitValue) -// Create a typed list global that is initialized as an empty list +/// Create a typed list global that is initialized as an empty list #define GLOBAL_LIST_EMPTY_TYPED(X, Typepath) GLOBAL_LIST_INIT_TYPED(X, Typepath, list()) -// Create a typed global with an initializer expression +/// Create a typed global with an initializer expression #define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue) -// Create an untyped null global +/// Create an untyped null global #define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X) -// Create a null global list +/// Create a null global list #define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X) -// Create a typed null global +/// Create a typed null global #define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X) - diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index 50ff3fbf2b6..058115f4fd1 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -1,42 +1,43 @@ -#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. - // 1 will enable set background. 0 will disable set background. - -#define PRELOAD_RSC 1 /*set to: - 0 to allow using external resources or on-demand behaviour; - 1 to use the default behaviour (preload compiled in recourses, not player uploaded ones); - 2 for preloading absolutely everything; - */ - -// ZAS Compile Options -//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. -#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! - -// Movement Compile Options -//#define CARDINAL_INPUT_ONLY // Uncomment to disable diagonal player movement (restore previous cardinal-moves-only behavior) - -// Comment/Uncomment this to turn off/on shuttle code debugging logs -#define DEBUG_SHUTTLES - -// If we are doing the map test build, do not include the main maps, only the submaps. -#if MAP_TEST - #define USING_MAP_DATUM /datum/map - #define MAP_OVERRIDE 1 -#endif - -///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while -//#define REFERENCE_TRACKING -#ifdef REFERENCE_TRACKING - -///Should we be logging our findings or not -#define REFERENCE_TRACKING_LOG - -///Used for doing dry runs of the reference finder, to test for feature completeness -//#define REFERENCE_TRACKING_DEBUG - -///Run a lookup on things hard deleting by default. -//#define GC_FAILURE_HARD_LOOKUP -#ifdef GC_FAILURE_HARD_LOOKUP -#define FIND_REF_NO_CHECK_TICK -#endif //ifdef GC_FAILURE_HARD_LOOKUP - -#endif //ifdef REFERENCE_TRACKING +#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. + // 1 will enable set background. 0 will disable set background. + +#define PRELOAD_RSC 1 /*set to: + 0 to allow using external resources or on-demand behaviour; + 1 to use the default behaviour (preload compiled in recourses, not player uploaded ones); + 2 for preloading absolutely everything; + */ + +// ZAS Compile Options +//#define FIREDBG // Uncomment to turn on ZAS debugging related to fire stuff. +//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. +#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! + +// Movement Compile Options +//#define CARDINAL_INPUT_ONLY // Uncomment to disable diagonal player movement (restore previous cardinal-moves-only behavior) + +// Comment/Uncomment this to turn off/on shuttle code debugging logs +#define DEBUG_SHUTTLES + +// If we are doing the map test build, do not include the main maps, only the submaps. +#if MAP_TEST + #define USING_MAP_DATUM /datum/map + #define MAP_OVERRIDE 1 +#endif + +///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while +//#define REFERENCE_TRACKING +#ifdef REFERENCE_TRACKING + +///Should we be logging our findings or not +#define REFERENCE_TRACKING_LOG + +///Used for doing dry runs of the reference finder, to test for feature completeness +//#define REFERENCE_TRACKING_DEBUG + +///Run a lookup on things hard deleting by default. +//#define GC_FAILURE_HARD_LOOKUP +#ifdef GC_FAILURE_HARD_LOOKUP +#define FIND_REF_NO_CHECK_TICK +#endif //ifdef GC_FAILURE_HARD_LOOKUP + +#endif //ifdef REFERENCE_TRACKING diff --git a/code/__defines/_protect.dm b/code/__defines/_protect.dm index b10a6264bdb..b503155faeb 100644 --- a/code/__defines/_protect.dm +++ b/code/__defines/_protect.dm @@ -1,11 +1,11 @@ -///Protects a datum from being VV'd -#define GENERAL_PROTECT_DATUM(Path)\ -##Path/can_vv_get(var_name){\ - return FALSE;\ -}\ -##Path/vv_edit_var(var_name, var_value){\ - return FALSE;\ -}\ -##Path/CanProcCall(procname){\ - return FALSE;\ +///Protects a datum from being VV'd +#define GENERAL_PROTECT_DATUM(Path)\ +##Path/can_vv_get(var_name){\ + return FALSE;\ +}\ +##Path/vv_edit_var(var_name, var_value){\ + return FALSE;\ +}\ +##Path/CanProcCall(procname){\ + return FALSE;\ } \ No newline at end of file diff --git a/code/__defines/admin_vr.dm b/code/__defines/admin_vr.dm index 5cc4ac22992..299b2906999 100644 --- a/code/__defines/admin_vr.dm +++ b/code/__defines/admin_vr.dm @@ -4,3 +4,16 @@ #define SMITE_AD_SPAM "Ad Spam" #define SMITE_AUTOSAVE "10 Second Autosave" #define SMITE_AUTOSAVE_WIDE "10 Second Autosave (AoE)" +#define MODIFIY_ROBOT_MODULE_ADD "Add a Module" +#define MODIFIY_ROBOT_MODULE_REMOVE "Remove a Module" +#define MODIFIY_ROBOT_APPLY_UPGRADE "Apply an Upgrade" +#define MODIFIY_ROBOT_SUPP_ADD "Add Upgrade Support" +#define MODIFIY_ROBOT_SUPP_REMOVE "Remove Upgrade Support" +#define MODIFIY_ROBOT_RADIOC_ADD "Add a Radio Channel" +#define MODIFIY_ROBOT_RADIOC_REMOVE "Remove a Radio Channel" +#define MODIFIY_ROBOT_COMP_ADD "Replace a Component" +#define MODIFIY_ROBOT_COMP_REMOVE "Remove a Component" +#define MODIFIY_ROBOT_SWAP_MODULE "Swap a Robot Module" +#define MODIFIY_ROBOT_RESET_MODULE "Fully Reset Robot Module" +#define MODIFIY_ROBOT_TOGGLE_ERT "Toggle ERT Module Overwrite" +#define MODIFIY_ROBOT_TOGGLE_CENT_ACCESS "Toggle Central Access Codes" diff --git a/code/__defines/chat.dm b/code/__defines/chat.dm new file mode 100644 index 00000000000..681fc8867bf --- /dev/null +++ b/code/__defines/chat.dm @@ -0,0 +1,35 @@ +/*! + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/// How many chat payloads to keep in history +#define CHAT_RELIABILITY_HISTORY_SIZE 5 +/// How many resends to allow before giving up +#define CHAT_RELIABILITY_MAX_RESENDS 3 + +#define MESSAGE_TYPE_SYSTEM "system" +#define MESSAGE_TYPE_LOCALCHAT "localchat" +#define MESSAGE_TYPE_NPCEMOTE "npcemote" +#define MESSAGE_TYPE_PLOCALCHAT "plocalchat" +#define MESSAGE_TYPE_HIVEMIND "hivemind" +#define MESSAGE_TYPE_RADIO "radio" +#define MESSAGE_TYPE_NIF "nif" +#define MESSAGE_TYPE_INFO "info" +#define MESSAGE_TYPE_WARNING "warning" +#define MESSAGE_TYPE_VORE "vore" +#define MESSAGE_TYPE_DEADCHAT "deadchat" +#define MESSAGE_TYPE_OOC "ooc" +#define MESSAGE_TYPE_LOOC "looc" +#define MESSAGE_TYPE_ADMINPM "adminpm" +#define MESSAGE_TYPE_MENTORPM "mentorpm" +#define MESSAGE_TYPE_COMBAT "combat" +#define MESSAGE_TYPE_CHATPRINT "chatexport" +#define MESSAGE_TYPE_ADMINCHAT "adminchat" +#define MESSAGE_TYPE_PRAYER "prayer" +#define MESSAGE_TYPE_MODCHAT "modchat" +#define MESSAGE_TYPE_RLOOC "rlooc" +#define MESSAGE_TYPE_EVENTCHAT "eventchat" +#define MESSAGE_TYPE_ADMINLOG "adminlog" +#define MESSAGE_TYPE_ATTACKLOG "attacklog" +#define MESSAGE_TYPE_DEBUG "debug" diff --git a/code/__defines/chemistry.dm b/code/__defines/chemistry.dm index 2fd9719eb45..3b4815b3533 100644 --- a/code/__defines/chemistry.dm +++ b/code/__defines/chemistry.dm @@ -49,7 +49,7 @@ // Chemistry lists. var/list/tachycardics = list("coffee", "inaprovaline", "hyperzine", "nitroglycerin", "thirteenloko", "nicotine") // Increase heart rate. var/list/bradycardics = list("neurotoxin", "cryoxadone", "clonexadone", "bliss", "stoxin", "ambrosia_extract") // Decrease heart rate. -var/list/heartstopper = list("potassium_chlorophoride", "zombie_powder") // This stops the heart. +var/list/heartstopper = list("potassium_chlorophoride", "zombiepowder") // This stops the heart. var/list/cheartstopper = list("potassium_chloride") // This stops the heart when overdose is met. -- c = conditional #define MAX_PILL_SPRITE 24 //max icon state of the pill sprites diff --git a/code/__defines/color.dm b/code/__defines/color.dm index 8f8332d768f..baae616b340 100644 --- a/code/__defines/color.dm +++ b/code/__defines/color.dm @@ -1,207 +1,207 @@ -// BYOND lower-cases color values, and thus we do so as well to ensure atom.color == COLOR_X will work correctly -#define COLOR_BLACK "#000000" -#define COLOR_NAVY "#000080" -#define COLOR_NAVY_BLUE "#000080" -#define COLOR_GREEN "#008000" -#define COLOR_DARK_GRAY "#404040" -#define COLOR_MAROON "#800000" -#define COLOR_PURPLE "#800080" -#define COLOR_VIOLET "#9933ff" -#define COLOR_OLIVE "#52613b" -#define COLOR_BROWN_ORANGE "#824b28" -#define COLOR_DARK_ORANGE "#b95a00" -#define COLOR_GRAY40 "#666666" -#define COLOR_GRAY20 "#333333" -#define COLOR_GRAY15 "#151515" -#define COLOR_SEDONA "#cc6600" -#define COLOR_DARK_BROWN "#917448" -#define COLOR_BLUE "#0000ff" -#define COLOR_DEEP_SKY_BLUE "#00e1ff" -#define COLOR_LIME "#00ff00" -#define COLOR_CYAN "#00ffff" -#define COLOR_TEAL "#33cccc" -#define COLOR_RED "#ff0000" -#define COLOR_PINK "#ff00ff" -#define COLOR_PALE_PINK "#bf89ba" -#define COLOR_ORANGE "#ff9900" -#define COLOR_YELLOW "#ffff00" -#define COLOR_YELLOW_GRAY "#c9a344" -#define COLOR_PALE_YELLOW "#c1bb7a" -#define COLOR_WARM_YELLOW "#b3863c" -#define COLOR_GRAY "#808080" -#define COLOR_RED_GRAY "#aa5f61" -#define COLOR_BROWN "#b19664" -#define COLOR_GREEN_GRAY "#8daf6a" -#define COLOR_DARK_GREEN_GRAY "#54654c" -#define COLOR_BLUE_GRAY "#6a97b0" -#define COLOR_DARK_BLUE_GRAY "#3e4855" -#define COLOR_SURGERY_BLUE "#e0f2f6" -#define COLOR_SUN "#ec8b2f" -#define COLOR_PURPLE_GRAY "#a2819e" -#define COLOR_BLUE_LIGHT "#33ccff" -#define COLOR_RED_LIGHT "#ff3333" -#define COLOR_BEIGE "#ceb689" -#define COLOR_BABY_BLUE "#89cff0" -#define COLOR_PALE_GREEN_GRAY "#aed18b" -#define COLOR_PALE_RED_GRAY "#cc9090" -#define COLOR_PALE_PURPLE_GRAY "#bda2ba" -#define COLOR_PALE_BLUE_GRAY "#8bbbd5" -#define COLOR_LUMINOL "#66ffff" -#define COLOR_SILVER "#c0c0c0" -#define COLOR_GRAY80 "#cccccc" -#define COLOR_OFF_WHITE "#eeeeee" -#define COLOR_WHITE "#ffffff" -#define COLOR_GOLD "#ffcc33" -#define COLOR_CLOSET_GOLD "#6d6133" -#define COLOR_NT_RED "#9d2300" -#define COLOR_BOTTLE_GREEN "#1f6b4f" -#define COLOR_PALE_BTL_GREEN "#57967f" -#define COLOR_GUNMETAL "#545c68" -#define COLOR_WALL_GUNMETAL "#353a42" -#define COLOR_STEEL "#a8b0b2" -#define COLOR_MUZZLE_FLASH "#ffffb2" -#define COLOR_CHESTNUT "#996633" -#define COLOR_BEASTY_BROWN "#663300" -#define COLOR_WHEAT "#ffff99" -#define COLOR_CYAN_BLUE "#3366cc" -#define COLOR_LIGHT_CYAN "#66ccff" -#define COLOR_PAKISTAN_GREEN "#006600" -#define COLOR_HULL "#436b8e" -#define COLOR_AMBER "#ffbf00" -#define COLOR_COMMAND_BLUE "#46698c" -#define COLOR_SKY_BLUE "#5ca1cc" -#define COLOR_PALE_ORANGE "#b88a3b" -#define COLOR_CIVIE_GREEN "#b7f27d" -#define COLOR_TITANIUM "#d1e6e3" -#define COLOR_DARK_GUNMETAL "#4c535b" -#define COLOR_BRONZE "#8c7853" -#define COLOR_BRASS "#b99d71" -#define COLOR_INDIGO "#4b0082" -#define COLOR_ALUMINIUM "#bbbbbb" -#define COLOR_CRYSTAL "#00c8a5" -#define COLOR_ASTEROID_ROCK "#735555" -#define COLOR_NULLGLASS "#ff6088" -#define COLOR_DIAMOND "#d8d4ea" -#define COLOR_LIGHT_PINK "#ffbaf9" -#define COLOR_LIGHT_RED "#ff4f4f" -#define COLOR_PALE_MAROON "#6e2121" -#define COLOR_LIGHT_GREEN "#00cf00" -#define COLOR_SALAD_GREEN "#67e067" -#define COLOR_DARK_GOLD "#ab9029" -#define COLOR_DARK_TEAL "#2db5b5" -#define COLOR_LIGHT_VIOLET "#e7bfff" -#define COLOR_SAN_MARINO_BLUE "#4b75ab" -#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" - -#define PIPE_COLOR_GREY "#808080" -#define PIPE_COLOR_RED "#ff0000" -#define PIPE_COLOR_BLUE "#0000ff" -#define PIPE_COLOR_CYAN "#00ffff" -#define PIPE_COLOR_GREEN "#00ff00" -#define PIPE_COLOR_YELLOW "#ffcc00" -#define PIPE_COLOR_BLACK "#444444" -#define PIPE_COLOR_ORANGE "#b95a00" -#define PIPE_COLOR_WHITE "#ffffff" -#define PIPE_COLOR_PURPLE "#5c1ec0" - -#define COMMS_COLOR_DEFAULT "#ff00ff" -#define COMMS_COLOR_ENTERTAIN "#666666" -#define COMMS_COLOR_AI "#ff00ff" -#define COMMS_COLOR_COMMON "#408010" -#define COMMS_COLOR_SERVICE "#709b00" -#define COMMS_COLOR_SUPPLY "#7f6539" -#define COMMS_COLOR_SCIENCE "#993399" -#define COMMS_COLOR_MEDICAL "#009190" -#define COMMS_COLOR_MEDICAL_I "#509190" -#define COMMS_COLOR_EXPLORER "#929820" -#define COMMS_COLOR_ENGINEER "#a66300" -#define COMMS_COLOR_SECURITY "#930000" -#define COMMS_COLOR_SECURITY_I "#935050" -#define COMMS_COLOR_COMMAND "#204090" -#define COMMS_COLOR_CENTCOMM "#5c5c7c" -#define COMMS_COLOR_SYNDICATE "#6d3f40" -#define COMMS_COLOR_SKRELL "#7331c4" - -#define WOOD_COLOR_GENERIC "#d5a66e" -#define WOOD_COLOR_RICH "#792f27" -#define WOOD_COLOR_PALE "#d2bc9d" -#define WOOD_COLOR_PALE2 "#e6d2ba" -#define WOOD_COLOR_BLACK "#332521" -#define WOOD_COLOR_CHOCOLATE "#543c30" -#define WOOD_COLOR_YELLOW "#e3994e" - -#define GLASS_COLOR "#74c1ee" -#define GLASS_COLOR_PHORON "#7c3a9a" -#define GLASS_COLOR_TINTED "#222222" -#define GLASS_COLOR_FROSTED "#ffffff" - -#define COLOR_BLOOD_HUMAN "#a10808" - -//Colors defines used by e-sword lighting -#define COLOR_SABER_BLUE "#40ceff" -#define COLOR_SABER_RED "#ff0000" //In case someone wants to tweak COLOR_RED, since COLOR_GREEN is not #00ff00 -#define COLOR_SABER_GREEN "#00ff00" -#define COLOR_SABER_PURPLE "#6800f4" -#define COLOR_SABER_SKRELL "#6600cc" -#define COLOR_SABER_AXE "#00ccff" -#define COLOR_SABER_CUTLASS "#ff0033" - -//Color defines used by the assembly detailer. -#define COLOR_ASSEMBLY_BLACK "#545454" -#define COLOR_ASSEMBLY_BGRAY "#9497AB" -#define COLOR_ASSEMBLY_WHITE "#E2E2E2" -#define COLOR_ASSEMBLY_RED "#CC4242" -#define COLOR_ASSEMBLY_ORANGE "#E39751" -#define COLOR_ASSEMBLY_BEIGE "#AF9366" -#define COLOR_ASSEMBLY_BROWN "#97670E" -#define COLOR_ASSEMBLY_GOLD "#AA9100" -#define COLOR_ASSEMBLY_YELLOW "#CECA2B" -#define COLOR_ASSEMBLY_GURKHA "#999875" -#define COLOR_ASSEMBLY_LGREEN "#789876" -#define COLOR_ASSEMBLY_GREEN "#44843C" -#define COLOR_ASSEMBLY_LBLUE "#5D99BE" -#define COLOR_ASSEMBLY_BLUE "#38559E" -#define COLOR_ASSEMBLY_PURPLE "#6F6192" -#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" - -// Discord requires colors to be in decimal instead of hexadecimal. -#define COLOR_WEBHOOK_DEFAULT 0x8bbbd5 // "#8bbbd5" -#define COLOR_WEBHOOK_GOOD 0x2ECC71 // "#2ECC71" -#define COLOR_WEBHOOK_POOR 0xE67E22 // "#E67E22" -#define COLOR_WEBHOOK_BAD 0xE74C3C // "#E74C3C" - -//Some defines to generalise colours used in lighting. -//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated -#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130) -#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100) -#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250) - -#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175) -#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225) -#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255) -#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248) -#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225) -#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125) -#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50) -#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50) -#define LIGHT_COLOR_PURPLE "#952CF4" //Light Purple. rgb(149, 44, 244) -#define LIGHT_COLOR_LAVENDER "#9B51FF" //Less-saturated light purple. rgb(155, 81, 255) - -//These ones aren't a direct colour like the ones above, because nothing would fit -#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25) -#define LIGHT_COLOR_LAVA "#C48A18" //Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24) -#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) -#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75) -#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) -#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250) - -//Lighting values used by the station lights -#define LIGHT_COLOR_FLUORESCENT_TUBE "#E0EFFF" -#define LIGHT_COLOR_FLUORESCENT_FLASHLIGHT "#CDDDFF" -#define LIGHT_COLOR_INCANDESCENT_TUBE "#fffed9" -#define LIGHT_COLOR_INCANDESCENT_BULB "#ffe7ce" -#define LIGHT_COLOR_INCANDESCENT_FLASHLIGHT "#FFCC66" -#define LIGHT_COLOR_NIGHTSHIFT "#EFCC86" - -//Fake ambient occlusion filter -#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, offset=3, color="#04080F80") +// BYOND lower-cases color values, and thus we do so as well to ensure atom.color == COLOR_X will work correctly +#define COLOR_BLACK "#000000" +#define COLOR_NAVY "#000080" +#define COLOR_NAVY_BLUE "#000080" +#define COLOR_GREEN "#008000" +#define COLOR_DARK_GRAY "#404040" +#define COLOR_MAROON "#800000" +#define COLOR_PURPLE "#800080" +#define COLOR_VIOLET "#9933ff" +#define COLOR_OLIVE "#52613b" +#define COLOR_BROWN_ORANGE "#824b28" +#define COLOR_DARK_ORANGE "#b95a00" +#define COLOR_GRAY40 "#666666" +#define COLOR_GRAY20 "#333333" +#define COLOR_GRAY15 "#151515" +#define COLOR_SEDONA "#cc6600" +#define COLOR_DARK_BROWN "#917448" +#define COLOR_BLUE "#0000ff" +#define COLOR_DEEP_SKY_BLUE "#00e1ff" +#define COLOR_LIME "#00ff00" +#define COLOR_CYAN "#00ffff" +#define COLOR_TEAL "#33cccc" +#define COLOR_RED "#ff0000" +#define COLOR_PINK "#ff00ff" +#define COLOR_PALE_PINK "#bf89ba" +#define COLOR_ORANGE "#ff9900" +#define COLOR_YELLOW "#ffff00" +#define COLOR_YELLOW_GRAY "#c9a344" +#define COLOR_PALE_YELLOW "#c1bb7a" +#define COLOR_WARM_YELLOW "#b3863c" +#define COLOR_GRAY "#808080" +#define COLOR_RED_GRAY "#aa5f61" +#define COLOR_BROWN "#b19664" +#define COLOR_GREEN_GRAY "#8daf6a" +#define COLOR_DARK_GREEN_GRAY "#54654c" +#define COLOR_BLUE_GRAY "#6a97b0" +#define COLOR_DARK_BLUE_GRAY "#3e4855" +#define COLOR_SURGERY_BLUE "#e0f2f6" +#define COLOR_SUN "#ec8b2f" +#define COLOR_PURPLE_GRAY "#a2819e" +#define COLOR_BLUE_LIGHT "#33ccff" +#define COLOR_RED_LIGHT "#ff3333" +#define COLOR_BEIGE "#ceb689" +#define COLOR_BABY_BLUE "#89cff0" +#define COLOR_PALE_GREEN_GRAY "#aed18b" +#define COLOR_PALE_RED_GRAY "#cc9090" +#define COLOR_PALE_PURPLE_GRAY "#bda2ba" +#define COLOR_PALE_BLUE_GRAY "#8bbbd5" +#define COLOR_LUMINOL "#66ffff" +#define COLOR_SILVER "#c0c0c0" +#define COLOR_GRAY80 "#cccccc" +#define COLOR_OFF_WHITE "#eeeeee" +#define COLOR_WHITE "#ffffff" +#define COLOR_GOLD "#ffcc33" +#define COLOR_CLOSET_GOLD "#6d6133" +#define COLOR_NT_RED "#9d2300" +#define COLOR_BOTTLE_GREEN "#1f6b4f" +#define COLOR_PALE_BTL_GREEN "#57967f" +#define COLOR_GUNMETAL "#545c68" +#define COLOR_WALL_GUNMETAL "#353a42" +#define COLOR_STEEL "#a8b0b2" +#define COLOR_MUZZLE_FLASH "#ffffb2" +#define COLOR_CHESTNUT "#996633" +#define COLOR_BEASTY_BROWN "#663300" +#define COLOR_WHEAT "#ffff99" +#define COLOR_CYAN_BLUE "#3366cc" +#define COLOR_LIGHT_CYAN "#66ccff" +#define COLOR_PAKISTAN_GREEN "#006600" +#define COLOR_HULL "#436b8e" +#define COLOR_AMBER "#ffbf00" +#define COLOR_COMMAND_BLUE "#46698c" +#define COLOR_SKY_BLUE "#5ca1cc" +#define COLOR_PALE_ORANGE "#b88a3b" +#define COLOR_CIVIE_GREEN "#b7f27d" +#define COLOR_TITANIUM "#d1e6e3" +#define COLOR_DARK_GUNMETAL "#4c535b" +#define COLOR_BRONZE "#8c7853" +#define COLOR_BRASS "#b99d71" +#define COLOR_INDIGO "#4b0082" +#define COLOR_ALUMINIUM "#bbbbbb" +#define COLOR_CRYSTAL "#00c8a5" +#define COLOR_ASTEROID_ROCK "#735555" +#define COLOR_NULLGLASS "#ff6088" +#define COLOR_DIAMOND "#d8d4ea" +#define COLOR_LIGHT_PINK "#ffbaf9" +#define COLOR_LIGHT_RED "#ff4f4f" +#define COLOR_PALE_MAROON "#6e2121" +#define COLOR_LIGHT_GREEN "#00cf00" +#define COLOR_SALAD_GREEN "#67e067" +#define COLOR_DARK_GOLD "#ab9029" +#define COLOR_DARK_TEAL "#2db5b5" +#define COLOR_LIGHT_VIOLET "#e7bfff" +#define COLOR_SAN_MARINO_BLUE "#4b75ab" +#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" + +#define PIPE_COLOR_GREY "#808080" +#define PIPE_COLOR_RED "#ff0000" +#define PIPE_COLOR_BLUE "#0000ff" +#define PIPE_COLOR_CYAN "#00ffff" +#define PIPE_COLOR_GREEN "#00ff00" +#define PIPE_COLOR_YELLOW "#ffcc00" +#define PIPE_COLOR_BLACK "#444444" +#define PIPE_COLOR_ORANGE "#b95a00" +#define PIPE_COLOR_WHITE "#ffffff" +#define PIPE_COLOR_PURPLE "#5c1ec0" + +#define COMMS_COLOR_DEFAULT "#ff00ff" +#define COMMS_COLOR_ENTERTAIN "#666666" +#define COMMS_COLOR_AI "#ff00ff" +#define COMMS_COLOR_COMMON "#408010" +#define COMMS_COLOR_SERVICE "#709b00" +#define COMMS_COLOR_SUPPLY "#7f6539" +#define COMMS_COLOR_SCIENCE "#993399" +#define COMMS_COLOR_MEDICAL "#009190" +#define COMMS_COLOR_MEDICAL_I "#509190" +#define COMMS_COLOR_EXPLORER "#929820" +#define COMMS_COLOR_ENGINEER "#a66300" +#define COMMS_COLOR_SECURITY "#930000" +#define COMMS_COLOR_SECURITY_I "#935050" +#define COMMS_COLOR_COMMAND "#204090" +#define COMMS_COLOR_CENTCOMM "#5c5c7c" +#define COMMS_COLOR_SYNDICATE "#6d3f40" +#define COMMS_COLOR_SKRELL "#7331c4" + +#define WOOD_COLOR_GENERIC "#d5a66e" +#define WOOD_COLOR_RICH "#792f27" +#define WOOD_COLOR_PALE "#d2bc9d" +#define WOOD_COLOR_PALE2 "#e6d2ba" +#define WOOD_COLOR_BLACK "#332521" +#define WOOD_COLOR_CHOCOLATE "#543c30" +#define WOOD_COLOR_YELLOW "#e3994e" + +#define GLASS_COLOR "#74c1ee" +#define GLASS_COLOR_PHORON "#7c3a9a" +#define GLASS_COLOR_TINTED "#222222" +#define GLASS_COLOR_FROSTED "#ffffff" + +#define COLOR_BLOOD_HUMAN "#a10808" + +//Colors defines used by e-sword lighting +#define COLOR_SABER_BLUE "#40ceff" +#define COLOR_SABER_RED "#ff0000" //In case someone wants to tweak COLOR_RED, since COLOR_GREEN is not #00ff00 +#define COLOR_SABER_GREEN "#00ff00" +#define COLOR_SABER_PURPLE "#6800f4" +#define COLOR_SABER_SKRELL "#6600cc" +#define COLOR_SABER_AXE "#00ccff" +#define COLOR_SABER_CUTLASS "#ff0033" + +//Color defines used by the assembly detailer. +#define COLOR_ASSEMBLY_BLACK "#545454" +#define COLOR_ASSEMBLY_BGRAY "#9497AB" +#define COLOR_ASSEMBLY_WHITE "#E2E2E2" +#define COLOR_ASSEMBLY_RED "#CC4242" +#define COLOR_ASSEMBLY_ORANGE "#E39751" +#define COLOR_ASSEMBLY_BEIGE "#AF9366" +#define COLOR_ASSEMBLY_BROWN "#97670E" +#define COLOR_ASSEMBLY_GOLD "#AA9100" +#define COLOR_ASSEMBLY_YELLOW "#CECA2B" +#define COLOR_ASSEMBLY_GURKHA "#999875" +#define COLOR_ASSEMBLY_LGREEN "#789876" +#define COLOR_ASSEMBLY_GREEN "#44843C" +#define COLOR_ASSEMBLY_LBLUE "#5D99BE" +#define COLOR_ASSEMBLY_BLUE "#38559E" +#define COLOR_ASSEMBLY_PURPLE "#6F6192" +#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" + +// Discord requires colors to be in decimal instead of hexadecimal. +#define COLOR_WEBHOOK_DEFAULT 0x8bbbd5 // "#8bbbd5" +#define COLOR_WEBHOOK_GOOD 0x2ECC71 // "#2ECC71" +#define COLOR_WEBHOOK_POOR 0xE67E22 // "#E67E22" +#define COLOR_WEBHOOK_BAD 0xE74C3C // "#E74C3C" + +//Some defines to generalise colours used in lighting. +//Important note on colors. Colors can end up significantly different from the basic html picture, especially when saturated +#define LIGHT_COLOR_RED "#FA8282" //Warm but extremely diluted red. rgb(250, 130, 130) +#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100) +#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250) + +#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175) +#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225) +#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255) +#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248) +#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225) +#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125) +#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50) +#define LIGHT_COLOR_ORANGE "#FA9632" //Mostly pure orange. rgb(250, 150, 50) +#define LIGHT_COLOR_PURPLE "#952CF4" //Light Purple. rgb(149, 44, 244) +#define LIGHT_COLOR_LAVENDER "#9B51FF" //Less-saturated light purple. rgb(155, 81, 255) + +//These ones aren't a direct colour like the ones above, because nothing would fit +#define LIGHT_COLOR_FIRE "#FAA019" //Warm orange color, leaning strongly towards yellow. rgb(250, 160, 25) +#define LIGHT_COLOR_LAVA "#C48A18" //Very warm yellow, leaning slightly towards orange. rgb(196, 138, 24) +#define LIGHT_COLOR_FLARE "#FA644B" //Bright, non-saturated red. Leaning slightly towards pink for visibility. rgb(250, 100, 75) +#define LIGHT_COLOR_SLIME_LAMP "#AFC84B" //Weird color, between yellow and green, very slimy. rgb(175, 200, 75) +#define LIGHT_COLOR_TUNGSTEN "#FAE1AF" //Extremely diluted yellow, close to skin color (for some reason). rgb(250, 225, 175) +#define LIGHT_COLOR_HALOGEN "#F0FAFA" //Barely visible cyan-ish hue, as the doctor prescribed. rgb(240, 250, 250) + +//Lighting values used by the station lights +#define LIGHT_COLOR_FLUORESCENT_TUBE "#E0EFFF" +#define LIGHT_COLOR_FLUORESCENT_FLASHLIGHT "#CDDDFF" +#define LIGHT_COLOR_INCANDESCENT_TUBE "#fffed9" +#define LIGHT_COLOR_INCANDESCENT_BULB "#ffe7ce" +#define LIGHT_COLOR_INCANDESCENT_FLASHLIGHT "#FFCC66" +#define LIGHT_COLOR_NIGHTSHIFT "#EFCC86" + +//Fake ambient occlusion filter +#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, offset=3, color="#04080F80") diff --git a/code/__defines/cooldowns.dm b/code/__defines/cooldowns.dm index c3855d7ab31..a04c83a4e20 100644 --- a/code/__defines/cooldowns.dm +++ b/code/__defines/cooldowns.dm @@ -1,3 +1,62 @@ +//// COOLDOWN SYSTEMS +/* + * We have 2 cooldown systems: timer cooldowns (divided between stoppable and regular) and world.time cooldowns. + * + * When to use each? + * + * * Adding a commonly-checked cooldown, like on a subsystem to check for processing + * * * Use the world.time ones, as they are cheaper. + * + * * Adding a rarely-used one for special situations, such as giving an uncommon item a cooldown on a target. + * * * Timer cooldown, as adding a new variable on each mob to track the cooldown of said uncommon item is going too far. + * + * * Triggering events at the end of a cooldown. + * * * Timer cooldown, registering to its signal. + * + * * Being able to check how long left for the cooldown to end. + * * * Either world.time or stoppable timer cooldowns, depending on the other factors. Regular timer cooldowns do not support this. + * + * * Being able to stop the timer before it ends. + * * * Either world.time or stoppable timer cooldowns, depending on the other factors. Regular timer cooldowns do not support this. +*/ + + +/* + * Cooldown system based on an datum-level associative lazylist using timers. +*/ + +// admin verb cooldowns +#define COOLDOWN_INTERNET_SOUND "internet_sound" + +//TIMER COOLDOWN MACROS + +#define COMSIG_CD_STOP(cd_index) "cooldown_[cd_index]" +#define COMSIG_CD_RESET(cd_index) "cd_reset_[cd_index]" + +#define TIMER_COOLDOWN_START(cd_source, cd_index, cd_time) LAZYSET(cd_source.cooldowns, cd_index, addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(end_cooldown), cd_source, cd_index), cd_time)) + +/// Checks if a timer based cooldown is NOT finished. +#define TIMER_COOLDOWN_RUNNING(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index) + +/// Checks if a timer based cooldown is finished. +#define TIMER_COOLDOWN_FINISHED(cd_source, cd_index) (!TIMER_COOLDOWN_RUNNING(cd_source, cd_index)) + +#define TIMER_COOLDOWN_END(cd_source, cd_index) LAZYREMOVE(cd_source.cooldowns, cd_index) + +/* + * Stoppable timer cooldowns. + * Use indexes the same as the regular tiemr cooldowns. + * They make use of the TIMER_COOLDOWN_RUNNING() and TIMER_COOLDOWN_END() macros the same, just not the TIMER_COOLDOWN_START() one. + * A bit more expensive than the regular timers, but can be reset before they end and the time left can be checked. +*/ + +#define S_TIMER_COOLDOWN_START(cd_source, cd_index, cd_time) LAZYSET(cd_source.cooldowns, cd_index, addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(end_cooldown), cd_source, cd_index), cd_time, TIMER_STOPPABLE)) + +#define S_TIMER_COOLDOWN_RESET(cd_source, cd_index) reset_cooldown(cd_source, cd_index) + +#define S_TIMER_COOLDOWN_TIMELEFT(cd_source, cd_index) (timeleft(TIMER_COOLDOWN_RUNNING(cd_source, cd_index))) + + /* * Cooldown system based on storing world.time on a variable, plus the cooldown time. * Better performance over timer cooldowns, lower control. Same functionality. @@ -5,6 +64,8 @@ #define COOLDOWN_DECLARE(cd_index) var/##cd_index = 0 +#define STATIC_COOLDOWN_DECLARE(cd_index) var/static/##cd_index = 0 + #define COOLDOWN_START(cd_source, cd_index, cd_time) (cd_source.cd_index = world.time + (cd_time)) //Returns true if the cooldown has run its course, false otherwise @@ -12,4 +73,6 @@ #define COOLDOWN_RESET(cd_source, cd_index) cd_source.cd_index = 0 -#define COOLDOWN_TIMELEFT(cd_source, cd_index) (max(0, cd_source.cd_index - world.time)) \ No newline at end of file +#define COOLDOWN_STARTED(cd_source, cd_index) (cd_source.cd_index != 0) + +#define COOLDOWN_TIMELEFT(cd_source, cd_index) (max(0, cd_source.cd_index - world.time)) diff --git a/code/__defines/exosuit_fab.dm b/code/__defines/exosuit_fab.dm index ccb05b93744..030c324cd31 100644 --- a/code/__defines/exosuit_fab.dm +++ b/code/__defines/exosuit_fab.dm @@ -1,15 +1,30 @@ + +/// Module is compatible with All Cyborg models, utility upgrades +#define BORG_UTILITY (1<<0) +/// Module is compatible with All Cyborg models, basic upgrades +#define BORG_BASIC (1<<1) +/// Module is compatible with All Cyborg models, advanced upgrades +#define BORG_ADVANCED (1<<2) /// Module is compatible with Security Cyborg models -#define BORG_MODULE_SECURITY (1<<0) +#define BORG_MODULE_SECURITY (1<<3) /// Module is compatible with Miner Cyborg models -#define BORG_MODULE_MINER (1<<1) +#define BORG_MODULE_MINER (1<<4) /// Module is compatible with Janitor Cyborg models -#define BORG_MODULE_JANITOR (1<<2) +#define BORG_MODULE_JANITOR (1<<5) /// Module is compatible with Medical Cyborg models -#define BORG_MODULE_MEDICAL (1<<3) +#define BORG_MODULE_MEDICAL (1<<6) /// Module is compatible with Engineering Cyborg models -#define BORG_MODULE_ENGINEERING (1<<4) +#define BORG_MODULE_ENGINEERING (1<<7) /// Module is compatible with Science Cyborg models -#define BORG_MODULE_SCIENCE (1<<5) +#define BORG_MODULE_SCIENCE (1<<8) +/// Module is compatible with Service Cyborg models +#define BORG_MODULE_SERVICE (1<<9) +/// Module is compatible with Clerical Cyborg models +#define BORG_MODULE_CLERIC (1<<10) +/// Module is compatible with Combat Cyborg models +#define BORG_MODULE_COMBAT (1<<11) +/// Module is compatible with Exploration Cyborg models +#define BORG_MODULE_EXPLO (1<<12) /// Module is compatible with Ripley Exosuit models #define EXOSUIT_MODULE_RIPLEY (1<<0) /// Module is compatible with Odyseeus Exosuit models diff --git a/code/__defines/integrated_circuits.dm b/code/__defines/integrated_circuits.dm index 03cfa358e3b..d8b246f0cfb 100644 --- a/code/__defines/integrated_circuits.dm +++ b/code/__defines/integrated_circuits.dm @@ -1,4 +1,4 @@ -// Methods of obtaining a circuit. -#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer. -#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it. +// Methods of obtaining a circuit. +#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer. +#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it. #define IC_SPAWN_ILLEGAL 3 // If the circuit design will be available if upgrading the IC printer illegally. \ No newline at end of file diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index bf854548760..9336ac5f789 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -1,119 +1,119 @@ -///Object doesn't use any of the light systems. Should be changed to add a light source to the object. -#define NO_LIGHT_SUPPORT 0 -///Light made with the lighting datums, applying a matrix. -#define STATIC_LIGHT 1 -///Light made by masking the lighting darkness plane. -#define MOVABLE_LIGHT 2 -///Light made by masking the lighting darkness plane, and is directional. -#define MOVABLE_LIGHT_DIRECTIONAL 3 - -///Is a movable light source attached to another movable (its loc), meaning that the lighting component should go one level deeper. -#define LIGHT_ATTACHED (1<<0) - -//Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it -/// frequency, in 1/10ths of a second, of the lighting process -#define LIGHTING_INTERVAL 5 - -#define MINIMUM_USEFUL_LIGHT_RANGE 1.4 - -/// type of falloff to use for lighting; 1 for circular, 2 for square -#define LIGHTING_FALLOFF 1 -/// use lambertian shading for light sources -#define LIGHTING_LAMBERTIAN 0 -/// height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone -#define LIGHTING_HEIGHT 1 -/// Value used to round lumcounts, values smaller than 1/129 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. -#define LIGHTING_ROUND_VALUE (1 / 64) - -/// icon used for lighting shading effects -#define LIGHTING_ICON 'icons/effects/lighting_object.dmi' - -/// If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting objects. -/// Set to zero to disable soft lighting. Luminosity changes then work if it's lit at all. -#define LIGHTING_SOFT_THRESHOLD 0 - -/// If I were you I'd leave this alone. -#define LIGHTING_BASE_MATRIX \ - list \ - ( \ - 1, 1, 1, 0, \ - 1, 1, 1, 0, \ - 1, 1, 1, 0, \ - 1, 1, 1, 0, \ - 0, 0, 0, 1 \ - ) \ - -///How many tiles standard fires glow. -#define LIGHT_RANGE_FIRE 3 - -#define LIGHTING_PLANE_ALPHA_VISIBLE 255 -#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245 -#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192 -/// For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell. -#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 -#define LIGHTING_PLANE_ALPHA_INVISIBLE 0 - -//lighting area defines -/// dynamic lighting disabled (area stays at full brightness) -#define DYNAMIC_LIGHTING_DISABLED 0 -/// dynamic lighting enabled -#define DYNAMIC_LIGHTING_ENABLED 1 -/// dynamic lighting enabled even if the area doesn't require power -#define DYNAMIC_LIGHTING_FORCED 2 -/// dynamic lighting enabled only if starlight is. -#define DYNAMIC_LIGHTING_IFSTARLIGHT 3 -#define IS_DYNAMIC_LIGHTING(A) A.dynamic_lighting - - -//code assumes higher numbers override lower numbers. -#define LIGHTING_NO_UPDATE 0 -#define LIGHTING_VIS_UPDATE 1 -#define LIGHTING_CHECK_UPDATE 2 -#define LIGHTING_FORCE_UPDATE 3 - -#define FLASH_LIGHT_DURATION 2 -#define FLASH_LIGHT_POWER 3 -#define FLASH_LIGHT_RANGE 3.8 - -// Emissive blocking. -/// Uses vis_overlays to leverage caching so that very few new items need to be made for the overlay. For anything that doesn't change outline or opaque area much or at all. -#define EMISSIVE_BLOCK_GENERIC 1 -/// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans. -#define EMISSIVE_BLOCK_UNIQUE 2 - -/// The color matrix applied to all emissive overlays. Should be solely dependent on alpha and not have RGB overlap with [EM_BLOCK_COLOR]. -#define EMISSIVE_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,0) -/// A globaly cached version of [EMISSIVE_COLOR] for quick access. -GLOBAL_LIST_INIT(emissive_color, EMISSIVE_COLOR) -/// The color matrix applied to all emissive blockers. Should be solely dependent on alpha and not have RGB overlap with [EMISSIVE_COLOR]. -#define EM_BLOCK_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) -/// A globaly cached version of [EM_BLOCK_COLOR] for quick access. -GLOBAL_LIST_INIT(em_block_color, EM_BLOCK_COLOR) -/// The color matrix used to mask out emissive blockers on the emissive plane. Alpha should default to zero, be solely dependent on the RGB value of [EMISSIVE_COLOR], and be independant of the RGB value of [EM_BLOCK_COLOR]. -#define EM_MASK_MATRIX list(0,0,0,1/3, 0,0,0,1/3, 0,0,0,1/3, 0,0,0,0, 1,1,1,0) -/// A globaly cached version of [EM_MASK_MATRIX] for quick access. -GLOBAL_LIST_INIT(em_mask_matrix, EM_MASK_MATRIX) - -/// Returns the red part of a #RRGGBB hex sequence as number -#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) - -/// Returns the green part of a #RRGGBB hex sequence as number -#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) - -/// Returns the blue part of a #RRGGBB hex sequence as number -#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) - -/// Parse the hexadecimal color into lumcounts of each perspective. -#define PARSE_LIGHT_COLOR(source) \ -do { \ - if (source.light_color != COLOR_WHITE) { \ - var/__light_color = source.light_color; \ - source.lum_r = GETREDPART(__light_color) / 255; \ - source.lum_g = GETGREENPART(__light_color) / 255; \ - source.lum_b = GETBLUEPART(__light_color) / 255; \ - } else { \ - source.lum_r = 1; \ - source.lum_g = 1; \ - source.lum_b = 1; \ - }; \ -} while (FALSE) +///Object doesn't use any of the light systems. Should be changed to add a light source to the object. +#define NO_LIGHT_SUPPORT 0 +///Light made with the lighting datums, applying a matrix. +#define STATIC_LIGHT 1 +///Light made by masking the lighting darkness plane. +#define MOVABLE_LIGHT 2 +///Light made by masking the lighting darkness plane, and is directional. +#define MOVABLE_LIGHT_DIRECTIONAL 3 + +///Is a movable light source attached to another movable (its loc), meaning that the lighting component should go one level deeper. +#define LIGHT_ATTACHED (1<<0) + +//Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it +/// frequency, in 1/10ths of a second, of the lighting process +#define LIGHTING_INTERVAL 5 + +#define MINIMUM_USEFUL_LIGHT_RANGE 1.4 + +/// type of falloff to use for lighting; 1 for circular, 2 for square +#define LIGHTING_FALLOFF 1 +/// use lambertian shading for light sources +#define LIGHTING_LAMBERTIAN 0 +/// height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone +#define LIGHTING_HEIGHT 1 +/// Value used to round lumcounts, values smaller than 1/129 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. +#define LIGHTING_ROUND_VALUE (1 / 64) + +/// icon used for lighting shading effects +#define LIGHTING_ICON 'icons/effects/lighting_object.dmi' + +/// If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting objects. +/// Set to zero to disable soft lighting. Luminosity changes then work if it's lit at all. +#define LIGHTING_SOFT_THRESHOLD 0 + +/// If I were you I'd leave this alone. +#define LIGHTING_BASE_MATRIX \ + list \ + ( \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 0, 0, 0, 1 \ + ) \ + +///How many tiles standard fires glow. +#define LIGHT_RANGE_FIRE 3 + +#define LIGHTING_PLANE_ALPHA_VISIBLE 255 +#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245 +#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192 +/// For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell. +#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 +#define LIGHTING_PLANE_ALPHA_INVISIBLE 0 + +//lighting area defines +/// dynamic lighting disabled (area stays at full brightness) +#define DYNAMIC_LIGHTING_DISABLED 0 +/// dynamic lighting enabled +#define DYNAMIC_LIGHTING_ENABLED 1 +/// dynamic lighting enabled even if the area doesn't require power +#define DYNAMIC_LIGHTING_FORCED 2 +/// dynamic lighting enabled only if starlight is. +#define DYNAMIC_LIGHTING_IFSTARLIGHT 3 +#define IS_DYNAMIC_LIGHTING(A) A.dynamic_lighting + + +//code assumes higher numbers override lower numbers. +#define LIGHTING_NO_UPDATE 0 +#define LIGHTING_VIS_UPDATE 1 +#define LIGHTING_CHECK_UPDATE 2 +#define LIGHTING_FORCE_UPDATE 3 + +#define FLASH_LIGHT_DURATION 2 +#define FLASH_LIGHT_POWER 3 +#define FLASH_LIGHT_RANGE 3.8 + +// Emissive blocking. +/// Uses vis_overlays to leverage caching so that very few new items need to be made for the overlay. For anything that doesn't change outline or opaque area much or at all. +#define EMISSIVE_BLOCK_GENERIC 1 +/// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans. +#define EMISSIVE_BLOCK_UNIQUE 2 + +/// The color matrix applied to all emissive overlays. Should be solely dependent on alpha and not have RGB overlap with [EM_BLOCK_COLOR]. +#define EMISSIVE_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 1,1,1,0) +/// A globaly cached version of [EMISSIVE_COLOR] for quick access. +GLOBAL_LIST_INIT(emissive_color, EMISSIVE_COLOR) +/// The color matrix applied to all emissive blockers. Should be solely dependent on alpha and not have RGB overlap with [EMISSIVE_COLOR]. +#define EM_BLOCK_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) +/// A globaly cached version of [EM_BLOCK_COLOR] for quick access. +GLOBAL_LIST_INIT(em_block_color, EM_BLOCK_COLOR) +/// The color matrix used to mask out emissive blockers on the emissive plane. Alpha should default to zero, be solely dependent on the RGB value of [EMISSIVE_COLOR], and be independant of the RGB value of [EM_BLOCK_COLOR]. +#define EM_MASK_MATRIX list(0,0,0,1/3, 0,0,0,1/3, 0,0,0,1/3, 0,0,0,0, 1,1,1,0) +/// A globaly cached version of [EM_MASK_MATRIX] for quick access. +GLOBAL_LIST_INIT(em_mask_matrix, EM_MASK_MATRIX) + +/// Returns the red part of a #RRGGBB hex sequence as number +#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) + +/// Returns the green part of a #RRGGBB hex sequence as number +#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) + +/// Returns the blue part of a #RRGGBB hex sequence as number +#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) + +/// Parse the hexadecimal color into lumcounts of each perspective. +#define PARSE_LIGHT_COLOR(source) \ +do { \ + if (source.light_color != COLOR_WHITE) { \ + var/__light_color = source.light_color; \ + source.lum_r = GETREDPART(__light_color) / 255; \ + source.lum_g = GETGREENPART(__light_color) / 255; \ + source.lum_b = GETBLUEPART(__light_color) / 255; \ + } else { \ + source.lum_r = 1; \ + source.lum_g = 1; \ + source.lum_b = 1; \ + }; \ +} while (FALSE) diff --git a/code/__defines/logging.dm b/code/__defines/logging.dm new file mode 100644 index 00000000000..07a121d9bb0 --- /dev/null +++ b/code/__defines/logging.dm @@ -0,0 +1,2 @@ +#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) +#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 3edae54b476..76e5b0b7682 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -154,17 +154,17 @@ if (!(DATUM.datum_flags & DF_ISPROCESSING)) {\ #define STOP_PROCESSING_IN_LIST(DATUM, LIST) LIST.Remove(DATUM);DATUM.datum_flags &= ~DF_ISPROCESSING // Note - I would prefer these be defined machines.dm, but some are used prior in file order. ~Leshana -#define START_MACHINE_PROCESSING(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_machines) -#define STOP_MACHINE_PROCESSING(Datum) STOP_PROCESSING_IN_LIST(Datum, global.processing_machines) +#define START_MACHINE_PROCESSING(Datum) START_PROCESSING_IN_LIST(Datum, SSmachines.processing_machines) +#define STOP_MACHINE_PROCESSING(Datum) STOP_PROCESSING_IN_LIST(Datum, SSmachines.processing_machines) -#define START_PROCESSING_PIPENET(Datum) START_PROCESSING_IN_LIST(Datum, global.pipe_networks) -#define STOP_PROCESSING_PIPENET(Datum) STOP_PROCESSING_IN_LIST(Datum, global.pipe_networks) +#define START_PROCESSING_PIPENET(Datum) START_PROCESSING_IN_LIST(Datum, SSmachines.networks) +#define STOP_PROCESSING_PIPENET(Datum) STOP_PROCESSING_IN_LIST(Datum, SSmachines.networks) -#define START_PROCESSING_POWERNET(Datum) START_PROCESSING_IN_LIST(Datum, global.powernets) -#define STOP_PROCESSING_POWERNET(Datum) STOP_PROCESSING_IN_LIST(Datum, global.powernets) +#define START_PROCESSING_POWERNET(Datum) START_PROCESSING_IN_LIST(Datum, SSmachines.powernets) +#define STOP_PROCESSING_POWERNET(Datum) STOP_PROCESSING_IN_LIST(Datum, SSmachines.powernets) -#define START_PROCESSING_POWER_OBJECT(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_power_items) -#define STOP_PROCESSING_POWER_OBJECT(Datum) STOP_PROCESSING_IN_LIST(Datum, global.processing_power_items) +#define START_PROCESSING_POWER_OBJECT(Datum) START_PROCESSING_IN_LIST(Datum, SSmachines.powerobjs) +#define STOP_PROCESSING_POWER_OBJECT(Datum) STOP_PROCESSING_IN_LIST(Datum, SSmachines.powerobjs) // Computer login types #define LOGIN_TYPE_NORMAL 1 diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index d68fd484bf0..22c43fa5747 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -278,6 +278,7 @@ var/global/list/##LIST_NAME = list();\ #define IS_CROWBAR "crowbar" #define IS_WIRECUTTER "wirecutter" #define IS_WRENCH "wrench" +#define IS_WELDER "welder" // Diagonal movement diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm index 23e0b405cdd..52e8d865255 100644 --- a/code/__defines/mobs_vr.dm +++ b/code/__defines/mobs_vr.dm @@ -50,3 +50,7 @@ #define SELECTS_BODYTYPE_FALSE 0 #define SELECTS_BODYTYPE_CUSTOM 1 #define SELECTS_BODYTYPE_SHAPESHIFTER 2 + +#define MARKING_NONDIGI_ONLY (1 << 0) +#define MARKING_DIGITIGRADE_ONLY (1 << 1) +#define MARKING_ALL_LEGS MARKING_NONDIGI_ONLY|MARKING_DIGITIGRADE_ONLY diff --git a/code/__defines/projectiles.dm b/code/__defines/projectiles.dm new file mode 100644 index 00000000000..34a3008e762 --- /dev/null +++ b/code/__defines/projectiles.dm @@ -0,0 +1,10 @@ +//Designed for things that need precision trajectories like projectiles. +//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels. + +//You might see places where it does - 16 - 1. This is intentionally 17 instead of 16, because of how byond's tiles work and how not doing it will result in rounding errors like things getting put on the wrong turf. + +#define RETURN_PRECISE_POSITION(A) new /datum/position(A) +#define RETURN_PRECISE_POINT(A) new /datum/point(A) + +#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) (new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)) +#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) (new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)) diff --git a/code/__defines/span_vr.dm b/code/__defines/span_vr.dm index ed4072b31fb..eaf45491cd3 100644 --- a/code/__defines/span_vr.dm +++ b/code/__defines/span_vr.dm @@ -10,7 +10,7 @@ #define span_comradio(str) ("" + str + "") #define span_syndradio(str) ("" + str + "") -#define span_centradio(str) ("" + str + "" +#define span_centradio(str) ("" + str + "") #define span_airadio(str) ("" + str + "") #define span_entradio(str) ("" + str + "") @@ -18,18 +18,23 @@ #define span_engradio(str) ("" + str + "") #define span_medradio(str) ("" + str + "") #define span_sciradio(str) ("" + str + "") -#define span_supradio(str) ("" + str + "" +#define span_supradio(str) ("" + str + "") #define span_srvradio(str) ("" + str + "") #define span_expradio(str) ("" + str + "") +#define span_binary(str) ("" + str + "") +#define span_hivemind(str) ("" + str + "") + #define span_name(str) ("" + str + "") #define span_say(str) ("" + str + "") #define span_alert(str) ("" + str + "") #define span_ghostalert(str) ("" + str + "") +#define span_npc_say(str) ("" + str + "") #define span_emote(str) ("" + str + "") -#define span_emote_subtle(str) ("" + str + "") +#define span_emote_subtle(str) ("" + str + "") +#define span_npc_emote(str) ("" + str + "") #define span_attack(str) ("" + str + "") #define span_moderate(str) ("" + str + "") @@ -81,10 +86,20 @@ #define span_debug_debug(str) ("" + str + "") #define span_debug_trace(str) ("" + str + "") +#define span_white(str) ("" + str + "") #define span_black(str) ("" + str + "") #define span_darkgray(str) ("" + str + "") #define span_gray(str) ("" + str + "") #define span_red(str) ("" + str + "") #define span_orange(str) ("" + str + "") #define span_blue(str) ("" + str + "") -#define span_green(str) ("" + str + "") \ No newline at end of file +#define span_green(str) ("" + str + "") +#define span_purple(str) ("" + str + "") +#define span_yellow(str) ("" + str + "") +#define span_pink(str) ("" + str + "") +#define span_cyan(str) ("" + str + "") +#define span_crimson(str) ("" + str + "") +#define span_maroon(str) ("" + str + "") +#define span_brown(str) ("" + str + "") +#define span_lightpurple(str) ("" + str + "") +#define span_darkpink(str) ("" + str + "") diff --git a/code/__defines/sqlite_defines.dm b/code/__defines/sqlite_defines.dm index 8cda3b62a09..4a4c4fe689e 100644 --- a/code/__defines/sqlite_defines.dm +++ b/code/__defines/sqlite_defines.dm @@ -1,7 +1,7 @@ -#define SQLITE_TABLE_FEEDBACK "feedback" - -#define SQLITE_FEEDBACK_COLUMN_ID "id" -#define SQLITE_FEEDBACK_COLUMN_AUTHOR "author" -#define SQLITE_FEEDBACK_COLUMN_TOPIC "topic" -#define SQLITE_FEEDBACK_COLUMN_CONTENT "content" +#define SQLITE_TABLE_FEEDBACK "feedback" + +#define SQLITE_FEEDBACK_COLUMN_ID "id" +#define SQLITE_FEEDBACK_COLUMN_AUTHOR "author" +#define SQLITE_FEEDBACK_COLUMN_TOPIC "topic" +#define SQLITE_FEEDBACK_COLUMN_CONTENT "content" #define SQLITE_FEEDBACK_COLUMN_DATETIME "datetime" \ No newline at end of file diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 1500ebb504f..6474a946ef1 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -49,6 +49,26 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME) #define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list +//! ### SS initialization hints +/** + * Negative values incidate a failure or warning of some kind, positive are good. + * 0 and 1 are unused so that TRUE and FALSE are guarenteed to be invalid values. + */ + +/// Subsystem failed to initialize entirely. Print a warning, log, and disable firing. +#define SS_INIT_FAILURE -2 + +/// The default return value which must be overriden. Will succeed with a warning. +#define SS_INIT_NONE -1 + +/// Subsystem initialized sucessfully. +#define SS_INIT_SUCCESS 2 + +/// Successful, but don't print anything. Useful if subsystem was disabled. +#define SS_INIT_NO_NEED 3 + +//! ### SS initialization load orders + // Subsystem init_order, from highest priority to lowest priority // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. @@ -104,6 +124,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_ORBIT 7 #define FIRE_PRIORITY_VOTE 8 #define FIRE_PRIORITY_INSTRUMENTS 9 +#define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_AI 10 #define FIRE_PRIORITY_GARBAGE 15 #define FIRE_PRIORITY_ALARM 20 diff --git a/code/__defines/tgs.config.dm b/code/__defines/tgs.config.dm index f06fcc96e80..e25041963bc 100644 --- a/code/__defines/tgs.config.dm +++ b/code/__defines/tgs.config.dm @@ -1,12 +1,11 @@ -#define TGS_EXTERNAL_CONFIGURATION -#define TGS_V3_API -#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name) -#define TGS_READ_GLOBAL(Name) GLOB.##Name -#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value -#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]") -#define TGS_INFO_LOG(message) log_world("TGS Info: [##message]") -#define TGS_WARNING_LOG(message) log_world("TGS Warn: [##message]") -#define TGS_ERROR_LOG(message) log_world("TGS Error: [##message]") -#define TGS_NOTIFY_ADMINS(event) message_admins(##event) -#define TGS_CLIENT_COUNT GLOB.clients.len -#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path) +#define TGS_EXTERNAL_CONFIGURATION +#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name) +#define TGS_READ_GLOBAL(Name) GLOB.##Name +#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value +#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]") +#define TGS_INFO_LOG(message) log_to_dd("TGS Info: [##message]") +#define TGS_WARNING_LOG(message) log_to_dd("TGS Warn: [##message]") +#define TGS_ERROR_LOG(message) log_to_dd("TGS Error: [##message]") +#define TGS_NOTIFY_ADMINS(event) message_admins(##event) +#define TGS_CLIENT_COUNT GLOB.clients.len +#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path) diff --git a/code/__defines/tgui.dm b/code/__defines/tgui.dm index 1215382ce78..96664cf2b33 100644 --- a/code/__defines/tgui.dm +++ b/code/__defines/tgui.dm @@ -4,9 +4,9 @@ #define TGUI_WINDOW_HARD_LIMIT 9 /// Maximum ping timeout allowed to detect zombie windows -#define TGUI_PING_TIMEOUT 4 SECONDS +#define TGUI_PING_TIMEOUT (4 SECONDS) /// Used for rate-limiting to prevent DoS by excessively refreshing a TGUI window -#define TGUI_REFRESH_FULL_UPDATE_COOLDOWN 5 SECONDS +#define TGUI_REFRESH_FULL_UPDATE_COOLDOWN (1 SECONDS) /// Window does not exist #define TGUI_WINDOW_CLOSED 0 @@ -36,4 +36,4 @@ #define TGUI_MODAL_OPEN 1 #define TGUI_MODAL_DELEGATE 2 #define TGUI_MODAL_ANSWER 3 -#define TGUI_MODAL_CLOSE 4 \ No newline at end of file +#define TGUI_MODAL_CLOSE 4 diff --git a/code/__defines/time.dm b/code/__defines/time.dm index c2a3632c745..9b3c384cfb3 100644 --- a/code/__defines/time.dm +++ b/code/__defines/time.dm @@ -1,33 +1,33 @@ -/// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) -#define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) - -/// In-universe, SS13 is set 300 years in the future from the real-world day, hence this number for determining the year-offset for the in-game year. -#define STATION_YEAR_OFFSET 300 - -#define MILISECOND * 0.01 -#define MILLISECONDS * 0.01 - -#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some reason - -#define SECOND *10 -#define SECONDS *10 - -#define MINUTE *600 -#define MINUTES *600 - -#define HOUR *36000 -#define HOURS *36000 - -#define DAY *864000 -#define DAYS *864000 - -#define TICK *world.tick_lag -#define TICKS *world.tick_lag - -#define DS2TICKS(DS) ((DS)/world.tick_lag) - -#define TICKS2DS(T) ((T) TICKS) - -#define MS2DS(T) ((T) MILLISECONDS) - -#define DS2MS(T) ((T) * 100) +/// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) +#define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) + +/// In-universe, SS13 is set 300 years in the future from the real-world day, hence this number for determining the year-offset for the in-game year. +#define STATION_YEAR_OFFSET 300 + +#define MILISECOND * 0.01 +#define MILLISECONDS * 0.01 + +#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some reason + +#define SECOND *10 +#define SECONDS *10 + +#define MINUTE *600 +#define MINUTES *600 + +#define HOUR *36000 +#define HOURS *36000 + +#define DAY *864000 +#define DAYS *864000 + +#define TICK *world.tick_lag +#define TICKS *world.tick_lag + +#define DS2TICKS(DS) ((DS)/world.tick_lag) + +#define TICKS2DS(T) ((T) TICKS) + +#define MS2DS(T) ((T) MILLISECONDS) + +#define DS2MS(T) ((T) * 100) diff --git a/code/__defines/vore.dm b/code/__defines/vore.dm new file mode 100644 index 00000000000..63859a80e45 --- /dev/null +++ b/code/__defines/vore.dm @@ -0,0 +1,15 @@ +#define VORE_SOUND_FALLOFF 0.1 +#define VORE_SOUND_RANGE 3 + +#define HOLO_ORIGINAL_COLOR null //Original hologram color: "#7db4e1" +#define HOLO_HARDLIGHT_COLOR "#d97de0" +#define HOLO_ORIGINAL_ALPHA 120 +#define HOLO_HARDLIGHT_ALPHA 200 + +#define BELLIES_MAX 40 +#define BELLIES_NAME_MIN 2 +#define BELLIES_NAME_MAX 40 +#define BELLIES_DESC_MAX 4096 +#define FLAVOR_MAX 400 + +#define VORE_VERSION 2 //This is a Define so you don't have to worry about magic numbers. diff --git a/code/_global_vars/_regexes.dm b/code/_global_vars/_regexes.dm new file mode 100644 index 00000000000..46e78a28fa0 --- /dev/null +++ b/code/_global_vars/_regexes.dm @@ -0,0 +1,4 @@ +//These are a bunch of regex datums for use /((any|every|no|some|head|foot)where(wolf)?\sand\s)+(\.[\.\s]+\s?where\?)?/i +GLOBAL_DATUM_INIT(is_http_protocol, /regex, regex("^https?://")) + +GLOBAL_DATUM_INIT(is_valid_url, /regex, regex("((?:https://)\[-a-zA-Z0-9@:%._+~#=]{1,256}.\[-a-zA-Z0-9@:%._+~#=]{1,256}\\b(?:\[-a-zA-Z0-9@():%_+.,~#?&/=]*\[^.,!?:; ()<>{}\\[]\n\"'´`]))", "gm")) diff --git a/code/_global_vars/sensitive.dm b/code/_global_vars/sensitive.dm index d4eda095ade..8de09f4f3a0 100644 --- a/code/_global_vars/sensitive.dm +++ b/code/_global_vars/sensitive.dm @@ -8,4 +8,4 @@ GLOBAL_REAL_VAR(sqlpass) = "" GLOBAL_REAL_VAR(sqlfdbkdb) = "test" GLOBAL_REAL_VAR(sqlfdbklogin) = "root" GLOBAL_REAL_VAR(sqlfdbkpass) = "" -GLOBAL_REAL_VAR(sqllogging) = 0 // Should we log deaths, population stats, etc.? \ No newline at end of file +GLOBAL_REAL_VAR(sqllogging) = 0 // Should we log deaths, population stats, etc.? diff --git a/code/_global_vars/time_vars.dm b/code/_global_vars/time_vars.dm index f05384ca493..1b4fc6401ac 100644 --- a/code/_global_vars/time_vars.dm +++ b/code/_global_vars/time_vars.dm @@ -1,2 +1,2 @@ -GLOBAL_VAR_INIT(year, time2text(world.realtime,"YYYY")) -GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013??? +GLOBAL_VAR_INIT(year, time2text(world.realtime,"YYYY")) +GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013??? diff --git a/code/_helpers/atmospherics.dm b/code/_helpers/atmospherics.dm index 83b5982f137..25fd699b32b 100644 --- a/code/_helpers/atmospherics.dm +++ b/code/_helpers/atmospherics.dm @@ -1,78 +1,78 @@ -/obj/proc/analyze_gases(var/atom/A, var/mob/user) - if(src != A) - user.visible_message("\The [user] has used \an [src] on \the [A]") - - A.add_fingerprint(user) - var/list/result = A.atmosanalyze(user) - if(result && result.len) - to_chat(user, "Results of the analysis[src == A ? "" : " of \the [A]"]") - for(var/line in result) - to_chat(user, "[line]") - return 1 - - to_chat(user, "Your [src] flashes a red light as it fails to analyze \the [A].") - return 0 - -/proc/atmosanalyzer_scan(var/atom/target, var/datum/gas_mixture/mixture, var/mob/user) - var/list/results = list() - - if (mixture && mixture.total_moles > 0) - var/pressure = mixture.return_pressure() - var/total_moles = mixture.total_moles - results += "Pressure: [round(pressure,0.1)] kPa" - for(var/mix in mixture.gas) - results += "[gas_data.name[mix]]: [round((mixture.gas[mix] / total_moles) * 100)]% ([round(mixture.gas[mix], 0.01)] moles)" - results += "Temperature: [round(mixture.temperature-T0C)]°C" - results += "Heat Capacity: [round(mixture.heat_capacity(),0.1)]" - else - results += "\The [target] is empty!" - - return results - -/turf/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air, user) - -/atom/proc/atmosanalyze(var/mob/user) - return - -/obj/item/weapon/tank/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air_contents, user) - -/obj/machinery/portable_atmospherics/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air_contents, user) - -/obj/machinery/atmospherics/pipe/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.parent.air, user) - -/obj/machinery/atmospherics/portables_connector/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.network.gases, user) - -/obj/machinery/atmospherics/unary/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air_contents, user) - -/obj/machinery/atmospherics/binary/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air1, user) - -/obj/machinery/atmospherics/trinary/atmos_filter/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air1, user) - -/obj/machinery/atmospherics/trinary/mixer/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.air3, user) - -/obj/machinery/atmospherics/omni/atmos_filter/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.input.air, user) - -/obj/machinery/atmospherics/omni/mixer/atmosanalyze(var/mob/user) - return atmosanalyzer_scan(src, src.output.air, user) - -/obj/machinery/meter/atmosanalyze(var/mob/user) - var/datum/gas_mixture/mixture = null - if(src.target) - mixture = src.target.parent.air - return atmosanalyzer_scan(src, mixture, user) - -/obj/machinery/power/rad_collector/atmosanalyze(var/mob/user) - if(P) return atmosanalyzer_scan(src, src.P.air_contents, user) - -/obj/item/weapon/flamethrower/atmosanalyze(var/mob/user) - if(ptank) return atmosanalyzer_scan(src, ptank.air_contents, user) +/obj/proc/analyze_gases(var/atom/A, var/mob/user) + if(src != A) + user.visible_message("\The [user] has used \an [src] on \the [A]") + + A.add_fingerprint(user) + var/list/result = A.atmosanalyze(user) + if(result && result.len) + to_chat(user, "Results of the analysis[src == A ? "" : " of \the [A]"]") + for(var/line in result) + to_chat(user, "[line]") + return 1 + + to_chat(user, "Your [src] flashes a red light as it fails to analyze \the [A].") + return 0 + +/proc/atmosanalyzer_scan(var/atom/target, var/datum/gas_mixture/mixture, var/mob/user) + var/list/results = list() + + if (mixture && mixture.total_moles > 0) + var/pressure = mixture.return_pressure() + var/total_moles = mixture.total_moles + results += "Pressure: [round(pressure,0.1)] kPa" + for(var/mix in mixture.gas) + results += "[gas_data.name[mix]]: [round((mixture.gas[mix] / total_moles) * 100)]% ([round(mixture.gas[mix], 0.01)] moles)" + results += "Temperature: [round(mixture.temperature-T0C)]°C" + results += "Heat Capacity: [round(mixture.heat_capacity(),0.1)]" + else + results += "\The [target] is empty!" + + return results + +/turf/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air, user) + +/atom/proc/atmosanalyze(var/mob/user) + return + +/obj/item/weapon/tank/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air_contents, user) + +/obj/machinery/portable_atmospherics/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air_contents, user) + +/obj/machinery/atmospherics/pipe/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.parent.air, user) + +/obj/machinery/atmospherics/portables_connector/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.network.gases, user) + +/obj/machinery/atmospherics/unary/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air_contents, user) + +/obj/machinery/atmospherics/binary/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air1, user) + +/obj/machinery/atmospherics/trinary/atmos_filter/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air1, user) + +/obj/machinery/atmospherics/trinary/mixer/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.air3, user) + +/obj/machinery/atmospherics/omni/atmos_filter/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.input.air, user) + +/obj/machinery/atmospherics/omni/mixer/atmosanalyze(var/mob/user) + return atmosanalyzer_scan(src, src.output.air, user) + +/obj/machinery/meter/atmosanalyze(var/mob/user) + var/datum/gas_mixture/mixture = null + if(src.target) + mixture = src.target.parent.air + return atmosanalyzer_scan(src, mixture, user) + +/obj/machinery/power/rad_collector/atmosanalyze(var/mob/user) + if(P) return atmosanalyzer_scan(src, src.P.air_contents, user) + +/obj/item/weapon/flamethrower/atmosanalyze(var/mob/user) + if(ptank) return atmosanalyzer_scan(src, ptank.air_contents, user) diff --git a/code/_helpers/files.dm b/code/_helpers/files.dm index 9468becf8b1..d18f6f4623e 100644 --- a/code/_helpers/files.dm +++ b/code/_helpers/files.dm @@ -1,75 +1,75 @@ -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/return_file_text(filename) - if(fexists(filename) == 0) - error("File not found ([filename])") - return - - var/text = file2text(filename) - if(!text) - error("File empty ([filename])") - return - - return text - -//Sends resource files to client cache -/client/proc/getFiles() - for(var/file in args) - src << browse_rsc(file) - -/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) - var/path = root - - for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") - return - - return path - -#define FTPDELAY 200 //200 tick delay to discourage spam -/* This proc is a failsafe to prevent spamming of file requests. - It is just a timer that only permits a download every [FTPDELAY] ticks. - This can be changed by modifying FTPDELAY's value above. - - PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ -/client/proc/file_spam_check() - var/time_to_wait = fileaccess_timer - world.time - if(time_to_wait > 0) - to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") - return 1 - fileaccess_timer = world.time + FTPDELAY - return 0 -#undef FTPDELAY - -/// Returns the md5 of a file at a given path. -/proc/md5filepath(path) - . = md5(file(path)) - -/// Save file as an external file then md5 it. -/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results. -/proc/md5asfile(file) - var/static/notch = 0 - // its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g. - var/filename = "tmp/md5asfile.[world.realtime].[world.timeofday].[world.time].[world.tick_usage].[notch]" - notch = WRAP(notch+1, 0, 2**15) - fcopy(file, filename) - . = md5filepath(filename) - fdel(filename) +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/return_file_text(filename) + if(fexists(filename) == 0) + error("File not found ([filename])") + return + + var/text = file2text(filename) + if(!text) + error("File empty ([filename])") + return + + return text + +//Sends resource files to client cache +/client/proc/getFiles() + for(var/file in args) + src << browse_rsc(file) + +/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) + var/path = root + + for(var/i=0, i 0) + to_chat(src, span_red("Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.")) + return 1 + fileaccess_timer = world.time + FTPDELAY + return 0 +#undef FTPDELAY + +/// Returns the md5 of a file at a given path. +/proc/md5filepath(path) + . = md5(file(path)) + +/// Save file as an external file then md5 it. +/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results. +/proc/md5asfile(file) + var/static/notch = 0 + // its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g. + var/filename = "tmp/md5asfile.[world.realtime].[world.timeofday].[world.time].[world.tick_usage].[notch]" + notch = WRAP(notch+1, 0, 2**15) + fcopy(file, filename) + . = md5filepath(filename) + fdel(filename) diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index b84f46e650a..812c0d5fe73 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -1,749 +1,749 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/proc/dopage(src,target) - var/href_list - var/href - href_list = params2list("src=\ref[src]&[target]=1") - href = "src=\ref[src];[target]=1" - src:temphtml = null - src:Topic(href, href_list) - return null - -/proc/is_on_same_plane_or_station(var/z1, var/z2) - if(z1 == z2) - return 1 - if((z1 in using_map.station_levels) && (z2 in using_map.station_levels)) - return 1 - return 0 - -/proc/max_default_z_level() - var/max_z = 0 - for(var/z in using_map.station_levels) - max_z = max(z, max_z) - for(var/z in using_map.admin_levels) - max_z = max(z, max_z) - for(var/z in using_map.player_levels) - max_z = max(z, max_z) - return max_z - -/proc/get_area(atom/A) - RETURN_TYPE(/area) - if(isarea(A)) - return A - var/turf/T = get_turf(A) - return T ? T.loc : null - -/proc/get_area_name(atom/X, format_text = FALSE) - var/area/A = isarea(X) ? X : get_area(X) - if(!A) - return null - return format_text ? format_text(A.name) : A.name - -/** Checks if any living humans are in a given area. */ -/proc/area_is_occupied(var/area/myarea) - // Testing suggests looping over human_mob_list is quicker than looping over area contents - for(var/mob/living/carbon/human/H in human_mob_list) - if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it. - continue - var/area/A = get_area(H) - if(A == myarea) //The loc of a turf is the area it is in. - return 1 - return 0 - -/proc/in_range(source, user) - if(get_dist(source, user) <= 1) - return 1 - - return 0 //not in range and not telekinetic - -// Like view but bypasses luminosity check - -/proc/hear(var/range, var/atom/source) - - var/lum = source.luminosity - source.luminosity = 6 - - var/list/heard = view(range, source) - source.luminosity = lum - - return heard - -/proc/isStationLevel(var/level) - return level in using_map.station_levels - -/proc/isNotStationLevel(var/level) - return !isStationLevel(level) - -/proc/isPlayerLevel(var/level) - return level in using_map.player_levels - -/proc/isAdminLevel(var/level) - return level in using_map.admin_levels - -/proc/isNotAdminLevel(var/level) - return !isAdminLevel(level) - -/proc/circlerange(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - - //turfs += centerturf - return turfs - -/proc/circleview(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/atoms = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/A in view(radius, centerturf)) - var/dx = A.x - centerturf.x - var/dy = A.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - atoms += A - - //turfs += centerturf - return atoms - -/proc/trange(rad = 0, turf/centre = null) //alternative to range (ONLY processes turfs and thus less intensive) - if(!centre) - return - - var/turf/x1y1 = locate(((centre.x-rad)<1 ? 1 : centre.x-rad),((centre.y-rad)<1 ? 1 : centre.y-rad),centre.z) - var/turf/x2y2 = locate(((centre.x+rad)>world.maxx ? world.maxx : centre.x+rad),((centre.y+rad)>world.maxy ? world.maxy : centre.y+rad),centre.z) - return block(x1y1,x2y2) - -/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) - var/dx = Loc1.x - Loc2.x - var/dy = Loc1.y - Loc2.y - - var/dist = sqrt(dx**2 + dy**2) - - return dist - -/proc/circlerangeturfs(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - -/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in view(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - - - -//var/debug_mob = 0 - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1, var/ignore_show_messages = 0) - - if(!recursion_limit) - return L - - for(var/I in O.contents) - - if(ismob(I)) - if(!sight_check || isInSight(I, O)) - L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) - if(include_mobs) - if(client_check) - var/mob/M = I - if(M.client) - L |= M - else - L |= I - - else if(istype(I,/obj/)) - var/obj/check_obj = I - if(ignore_show_messages || check_obj.show_messages) - if(!sight_check || isInSight(I, O)) - L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) - if(include_objects) - L |= I - - return L - -// Returns a list of mobs and/or objects in range of R from source. Used in radio and say code. - -/proc/get_mobs_or_objects_in_view(var/R, var/atom/source, var/include_mobs = 1, var/include_objects = 1) - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/I in range) - if(ismob(I)) - hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) - if(include_mobs) - var/mob/M = I - if(M.client) - hear += M - else if(istype(I,/obj/)) - hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) - var/obj/O = I - if(O.show_messages && include_objects) - hear += I - - return hear - - -/proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios) - - set background = 1 - - . = list() - // Returns a list of mobs who can hear any of the radios given in @radios - var/list/speaker_coverage = list() - for(var/obj/item/device/radio/R as anything in radios) - var/turf/speaker = get_turf(R) - if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = R - - - // Try to find all the players who can hear the message - for(var/i = 1; i <= player_list.len; i++) - var/mob/M = player_list[i] - if(M.can_hear_radio(speaker_coverage)) - . += M - return . - -/mob/proc/can_hear_radio(var/list/hearturfs) - return FALSE - -/mob/living/can_hear_radio(var/list/hearturfs) - return get_turf(src) in hearturfs - -/mob/living/silicon/robot/can_hear_radio(var/list/hearturfs) - var/turf/T = get_turf(src) - var/obj/item/device/radio/borg/R = hearturfs[T] // this should be an assoc list of turf-to-radio - - // We heard it on our own radio? We use power for that. - if(istype(R) && R.myborg == src) - var/datum/robot_component/CO = get_component("radio") - if(!CO || !is_component_functioning("radio") || !cell_use_power(CO.active_usage)) - return FALSE // Sorry, couldn't hear - - return R // radio, true, false, what's the difference - -/mob/observer/dead/can_hear_radio(var/list/hearturfs) - return is_preference_enabled(/datum/client_preference/ghost_radio) - - -//Uses dview to quickly return mobs and objects in view, -// then adds additional mobs or objects if they are in range 'smartly', -// based on their presence in lists of players or registered objects -// Type: 1-audio, 2-visual, 0-neither -/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/type = 1, var/remote_ghosts = TRUE) - var/list/mobs = list() - var/list/objs = list() - - var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) - var/list/hearturfs = list() - - // Openspace visibility handling - // Below turfs we can see - for(var/turf/simulated/open/O in hear) - var/turf/U = GetBelow(O) - while(istype(U)) - hearturfs |= U - if(isopenspace(U)) - U = GetBelow(U) - else - U = null - - // Above us - var/above_range = range - var/turf/Ab = GetAbove(T) - while(isopenspace(Ab) && --above_range > 0) - hear |= dview(above_range,Ab,INVISIBILITY_MAXIMUM) - Ab = GetAbove(Ab) - - for(var/thing in hear) - if(istype(thing, /obj)) //Can't use isobj() because /atom/movable returns true in that - objs += thing - hearturfs |= get_turf(thing) - if(ismob(thing)) - mobs += thing - hearturfs |= get_turf(thing) - - //A list of every mob with a client - for(var/mob in player_list) - if(!ismob(mob)) - player_list -= mob - continue - //VOREStation Edit End - Trying to fix some vorestation bug. - if(get_turf(mob) in hearturfs) - mobs |= mob - continue - - var/mob/M = mob - if(M && M.stat == DEAD && remote_ghosts && !M.forbid_seeing_deadchat) - switch(type) - if(1) //Audio messages use ghost_ears - if(M.is_preference_enabled(/datum/client_preference/ghost_ears)) - mobs |= M - if(2) //Visual messages use ghost_sight - if(M.is_preference_enabled(/datum/client_preference/ghost_sight)) - mobs |= M - - //For objects below the top level who still want to hear - for(var/obj in listening_objects) - if(get_turf(obj) in hearturfs) - objs |= obj - - return list("mobs" = mobs, "objs" = objs) - -/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) - var/turf/T - if(X1==X2) - if(Y1==Y2) - return 1 //Light cannot be blocked on same tile - else - var/s = SIGN(Y2-Y1) - Y1+=s - while(Y1!=Y2) - T=locate(X1,Y1,Z) - if(T.opacity) - return 0 - Y1+=s - else - var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) - var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIGN(X2-X1) - var/signY = SIGN(Y2-Y1) - if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) - if(dy > 0) - return get_step(start, SOUTH) - else - return get_step(start, NORTH) - else - if(dx > 0) - return get_step(start, WEST) - else - return get_step(start, EAST) - -/proc/get_mob_by_key(var/key) - for(var/mob/M in mob_list) - if(M.ckey == lowertext(key)) - return M - return null - - -// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. -/proc/get_active_candidates(var/buffer = 1) - - var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn - var/i = 0 - while(candidates.len <= 0 && i < 5) - for(var/mob/observer/dead/G in player_list) - if(((G.client.inactivity/10)/60) <= buffer + i) // the most active players are more likely to become an alien - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - candidates += G.key - i++ - return candidates - -// Same as above but for alien candidates. - -/proc/get_alien_candidates() - - var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn - var/i = 0 - while(candidates.len <= 0 && i < 5) - for(var/mob/observer/dead/G in player_list) - if(G.client.prefs.be_special & BE_ALIEN) - if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i) // the most active players are more likely to become an alien - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - candidates += G.key - i++ - return candidates - -/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) - if(!isobj(O)) O = new /obj/screen/text() - O.maptext = maptext - O.maptext_height = maptext_height - O.maptext_width = maptext_width - O.screen_loc = screen_loc - return O - -/proc/Show2Group4Delay(obj/O, list/group, delay=0) - if(!isobj(O)) return - if(!group) group = GLOB.clients - for(var/client/C in group) - C.screen += O - if(delay) - spawn(delay) - for(var/client/C in group) - C.screen -= O - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - -/proc/GetRedPart(const/hexa) - return hex2num(copytext(hexa,2,4)) - -/proc/GetGreenPart(const/hexa) - return hex2num(copytext(hexa,4,6)) - -/proc/GetBluePart(const/hexa) - return hex2num(copytext(hexa,6,8)) - -/proc/GetHexColors(const/hexa) - return list( - GetRedPart(hexa), - GetGreenPart(hexa), - GetBluePart(hexa) - ) - -/proc/MixColors(const/list/colors) - var/list/reds = list() - var/list/blues = list() - var/list/greens = list() - var/list/weights = list() - - for (var/i = 0, ++i <= colors.len) - reds.Add(GetRedPart(colors[i])) - blues.Add(GetBluePart(colors[i])) - greens.Add(GetGreenPart(colors[i])) - weights.Add(1) - - var/r = mixOneColor(weights, reds) - var/g = mixOneColor(weights, greens) - var/b = mixOneColor(weights, blues) - return rgb(r,g,b) - -/proc/mixOneColor(var/list/weight, var/list/color) - if (!weight || !color || length(weight)!=length(color)) - return 0 - - var/contents = length(weight) - var/i - - //normalize weights - var/listsum = 0 - for(i=1; i<=contents; i++) - listsum += weight[i] - for(i=1; i<=contents; i++) - weight[i] /= listsum - - //mix them - var/mixedcolor = 0 - for(i=1; i<=contents; i++) - mixedcolor += weight[i]*color[i] - mixedcolor = round(mixedcolor) - - //until someone writes a formal proof for this algorithm, let's keep this in -// if(mixedcolor<0x00 || mixedcolor>0xFF) -// return 0 - //that's not the kind of operation we are running here, nerd - mixedcolor=min(max(mixedcolor,0),255) - - return mixedcolor - -/** -* Gets the highest and lowest pressures from the tiles in cardinal directions -* around us, then checks the difference. -*/ -/proc/getOPressureDifferential(var/turf/loc) - var/minp=16777216; - var/maxp=0; - for(var/dir in GLOB.cardinal) - var/turf/simulated/T=get_turf(get_step(loc,dir)) - var/cp=0 - if(T && istype(T) && T.zone) - var/datum/gas_mixture/environment = T.return_air() - cp = environment.return_pressure() - else - if(istype(T,/turf/simulated)) - continue - if(cpmaxp)maxp=cp - return abs(minp-maxp) - -/proc/convert_k2c(var/temp) - return ((temp - T0C)) - -/proc/convert_c2k(var/temp) - return ((temp + T0C)) - -/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature")) - var/list/temps = new/list(4) - for(var/dir in GLOB.cardinal) - var/direction - switch(dir) - if(NORTH) - direction = 1 - if(SOUTH) - direction = 2 - if(EAST) - direction = 3 - if(WEST) - direction = 4 - var/turf/simulated/T=get_turf(get_step(loc,dir)) - var/list/rstats = new /list(stats.len) - if(T && istype(T) && T.zone) - var/datum/gas_mixture/environment = T.return_air() - for(var/i=1;i<=stats.len;i++) - if(stats[i] == "pressure") - rstats[i] = environment.return_pressure() - else - rstats[i] = environment.vars[stats[i]] - else if(istype(T, /turf/simulated)) - rstats = null // Exclude zone (wall, door, etc). - else if(istype(T, /turf)) - // Should still work. (/turf/return_air()) - var/datum/gas_mixture/environment = T.return_air() - for(var/i=1;i<=stats.len;i++) - if(stats[i] == "pressure") - rstats[i] = environment.return_pressure() - else - rstats[i] = environment.vars[stats[i]] - temps[direction] = rstats - return temps - -/proc/MinutesToTicks(var/minutes) - return SecondsToTicks(60 * minutes) - -/proc/SecondsToTicks(var/seconds) - return seconds * 10 - -/proc/window_flash(var/client_or_usr) - if (!client_or_usr) - return - winset(client_or_usr, "mainwindow", "flash=5") - -/** - * Get a bounding box of a list of atoms. - * - * Arguments: - * - atoms - List of atoms. Can accept output of view() and range() procs. - * - * Returns: list(x1, y1, x2, y2) - */ -/proc/get_bbox_of_atoms(list/atoms) - var/list/list_x = list() - var/list/list_y = list() - for(var/_a in atoms) - var/atom/a = _a - list_x += a.x - list_y += a.y - return list( - min(list_x), - min(list_y), - max(list_x), - max(list_y)) - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) - - //GLOB.debug_mob += O.contents.len - if(!recursion_limit) - return L - for(var/atom/A in O.contents) - - if(ismob(A)) - var/mob/M = A - if(client_check && !M.client) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - continue - if(sight_check && !isInSight(A, O)) - continue - L |= M - //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - - else if(include_radio && istype(A, /obj/item/radio)) - if(sight_check && !isInSight(A, O)) - continue - L |= A - - if(isobj(A) || ismob(A)) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - return L - -// The old system would loop through lists for a total of 5000 per function call, in an empty server. -// This new system will loop at around 1000 in an empty server. - -/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) - // Returns a list of mobs in range of R from source. Used in radio and say code. - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/atom/A in range) - if(ismob(A)) - var/mob/M = A - if(M.client || include_clientless) - hear += M - //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - else if(istype(A, /obj/item/radio)) - hear += A - - if(isobj(A) || ismob(A)) - hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) - - return hear - -/proc/get_belly(var/atom/A) // return a belly we're in, one way or another; and if we aren't (or are too deep to comprehend being in belly), returns null - var/atom/loc_check = A.loc - var/recursion_level = 0 - while(loc_check && !isbelly(loc_check) && !isturf(loc_check)) - if(recursion_level > 7) // abstractly picked number, but basically means we tried going 8 levels up. Something is wrong if youre THAT deep anyway - break - loc_check = loc_check.loc - recursion_level++ - if(isbelly(loc_check)) - return loc_check - return null - -/proc/get_all_prey_recursive(var/mob/living/L, var/client_check = 1) // returns all prey inside the target as well all prey of target's prey, as well as all prey inside target's prey, etc. - var/list/result = list() - - if(!istype(L) || !(L.vore_organs) || !(L.vore_organs.len)) - return result - - for(var/obj/belly/B in L.vore_organs) - for(var/mob/living/P in B.contents) - if(istype(P)) - if(client_check && P.client) - result |= P - result |= get_all_prey_recursive(P, client_check) - - return result - -/proc/random_color(saturated) //Returns a random color. If saturated is true, it will avoid pure white or pure black - var/r = rand(1,255) - var/g = rand(1,255) - var/b = rand(1,255) - - if(saturated) //Let's make sure we don't get too close to pure black or pure white, as they won't look good with grayscale sprites - if(r + g + b < 50) - r = r + rand(5,20) - g = g + rand(5,20) - b = b + rand(5,20) - else if (r + g + b > 700) - r = r - rand(5,50) - g = g - rand(5,50) - b = b - rand(5,50) - - var/color = rgb(r, g, b) - return color +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/proc/dopage(src,target) + var/href_list + var/href + href_list = params2list("src=\ref[src]&[target]=1") + href = "src=\ref[src];[target]=1" + src:temphtml = null + src:Topic(href, href_list) + return null + +/proc/is_on_same_plane_or_station(var/z1, var/z2) + if(z1 == z2) + return 1 + if((z1 in using_map.station_levels) && (z2 in using_map.station_levels)) + return 1 + return 0 + +/proc/max_default_z_level() + var/max_z = 0 + for(var/z in using_map.station_levels) + max_z = max(z, max_z) + for(var/z in using_map.admin_levels) + max_z = max(z, max_z) + for(var/z in using_map.player_levels) + max_z = max(z, max_z) + return max_z + +/proc/get_area(atom/A) + RETURN_TYPE(/area) + if(isarea(A)) + return A + var/turf/T = get_turf(A) + return T ? T.loc : null + +/proc/get_area_name(atom/X, format_text = FALSE) + var/area/A = isarea(X) ? X : get_area(X) + if(!A) + return null + return format_text ? format_text(A.name) : A.name + +/** Checks if any living humans are in a given area. */ +/proc/area_is_occupied(var/area/myarea) + // Testing suggests looping over human_mob_list is quicker than looping over area contents + for(var/mob/living/carbon/human/H in human_mob_list) + if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it. + continue + var/area/A = get_area(H) + if(A == myarea) //The loc of a turf is the area it is in. + return 1 + return 0 + +/proc/in_range(source, user) + if(get_dist(source, user) <= 1) + return 1 + + return 0 //not in range and not telekinetic + +// Like view but bypasses luminosity check + +/proc/hear(var/range, var/atom/source) + + var/lum = source.luminosity + source.luminosity = 6 + + var/list/heard = view(range, source) + source.luminosity = lum + + return heard + +/proc/isStationLevel(var/level) + return level in using_map.station_levels + +/proc/isNotStationLevel(var/level) + return !isStationLevel(level) + +/proc/isPlayerLevel(var/level) + return level in using_map.player_levels + +/proc/isAdminLevel(var/level) + return level in using_map.admin_levels + +/proc/isNotAdminLevel(var/level) + return !isAdminLevel(level) + +/proc/circlerange(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + + //turfs += centerturf + return turfs + +/proc/circleview(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/atoms = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/A in view(radius, centerturf)) + var/dx = A.x - centerturf.x + var/dy = A.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + atoms += A + + //turfs += centerturf + return atoms + +/proc/trange(rad = 0, turf/centre = null) //alternative to range (ONLY processes turfs and thus less intensive) + if(!centre) + return + + var/turf/x1y1 = locate(((centre.x-rad)<1 ? 1 : centre.x-rad),((centre.y-rad)<1 ? 1 : centre.y-rad),centre.z) + var/turf/x2y2 = locate(((centre.x+rad)>world.maxx ? world.maxx : centre.x+rad),((centre.y+rad)>world.maxy ? world.maxy : centre.y+rad),centre.z) + return block(x1y1,x2y2) + +/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) + var/dx = Loc1.x - Loc2.x + var/dy = Loc1.y - Loc2.y + + var/dist = sqrt(dx**2 + dy**2) + + return dist + +/proc/circlerangeturfs(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + +/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in view(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + + + +//var/debug_mob = 0 + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1, var/ignore_show_messages = 0) + + if(!recursion_limit) + return L + + for(var/I in O.contents) + + if(ismob(I)) + if(!sight_check || isInSight(I, O)) + L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) + if(include_mobs) + if(client_check) + var/mob/M = I + if(M.client) + L |= M + else + L |= I + + else if(istype(I,/obj/)) + var/obj/check_obj = I + if(ignore_show_messages || check_obj.show_messages) + if(!sight_check || isInSight(I, O)) + L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects) + if(include_objects) + L |= I + + return L + +// Returns a list of mobs and/or objects in range of R from source. Used in radio and say code. + +/proc/get_mobs_or_objects_in_view(var/R, var/atom/source, var/include_mobs = 1, var/include_objects = 1) + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/I in range) + if(ismob(I)) + hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) + if(include_mobs) + var/mob/M = I + if(M.client) + hear += M + else if(istype(I,/obj/)) + hear |= recursive_content_check(I, hear, 3, 1, 0, include_mobs, include_objects) + var/obj/O = I + if(O.show_messages && include_objects) + hear += I + + return hear + + +/proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios) + + set background = 1 + + . = list() + // Returns a list of mobs who can hear any of the radios given in @radios + var/list/speaker_coverage = list() + for(var/obj/item/device/radio/R as anything in radios) + var/turf/speaker = get_turf(R) + if(speaker) + for(var/turf/T in hear(R.canhear_range,speaker)) + speaker_coverage[T] = R + + + // Try to find all the players who can hear the message + for(var/i = 1; i <= player_list.len; i++) + var/mob/M = player_list[i] + if(M.can_hear_radio(speaker_coverage)) + . += M + return . + +/mob/proc/can_hear_radio(var/list/hearturfs) + return FALSE + +/mob/living/can_hear_radio(var/list/hearturfs) + return get_turf(src) in hearturfs + +/mob/living/silicon/robot/can_hear_radio(var/list/hearturfs) + var/turf/T = get_turf(src) + var/obj/item/device/radio/borg/R = hearturfs[T] // this should be an assoc list of turf-to-radio + + // We heard it on our own radio? We use power for that. + if(istype(R) && R.myborg == src) + var/datum/robot_component/CO = get_component("radio") + if(!CO || !is_component_functioning("radio") || !cell_use_power(CO.active_usage)) + return FALSE // Sorry, couldn't hear + + return R // radio, true, false, what's the difference + +/mob/observer/dead/can_hear_radio(var/list/hearturfs) + return is_preference_enabled(/datum/client_preference/ghost_radio) + + +//Uses dview to quickly return mobs and objects in view, +// then adds additional mobs or objects if they are in range 'smartly', +// based on their presence in lists of players or registered objects +// Type: 1-audio, 2-visual, 0-neither +/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/type = 1, var/remote_ghosts = TRUE) + var/list/mobs = list() + var/list/objs = list() + + var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) + var/list/hearturfs = list() + + // Openspace visibility handling + // Below turfs we can see + for(var/turf/simulated/open/O in hear) + var/turf/U = GetBelow(O) + while(istype(U)) + hearturfs |= U + if(isopenspace(U)) + U = GetBelow(U) + else + U = null + + // Above us + var/above_range = range + var/turf/Ab = GetAbove(T) + while(isopenspace(Ab) && --above_range > 0) + hear |= dview(above_range,Ab,INVISIBILITY_MAXIMUM) + Ab = GetAbove(Ab) + + for(var/thing in hear) + if(istype(thing, /obj)) //Can't use isobj() because /atom/movable returns true in that + objs += thing + hearturfs |= get_turf(thing) + if(ismob(thing)) + mobs += thing + hearturfs |= get_turf(thing) + + //A list of every mob with a client + for(var/mob in player_list) + if(!ismob(mob)) + player_list -= mob + continue + //VOREStation Edit End - Trying to fix some vorestation bug. + if(get_turf(mob) in hearturfs) + mobs |= mob + continue + + var/mob/M = mob + if(M && M.stat == DEAD && remote_ghosts && !M.forbid_seeing_deadchat) + switch(type) + if(1) //Audio messages use ghost_ears + if(M.is_preference_enabled(/datum/client_preference/ghost_ears)) + mobs |= M + if(2) //Visual messages use ghost_sight + if(M.is_preference_enabled(/datum/client_preference/ghost_sight)) + mobs |= M + + //For objects below the top level who still want to hear + for(var/obj in listening_objects) + if(get_turf(obj) in hearturfs) + objs |= obj + + return list("mobs" = mobs, "objs" = objs) + +/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) + var/turf/T + if(X1==X2) + if(Y1==Y2) + return 1 //Light cannot be blocked on same tile + else + var/s = SIGN(Y2-Y1) + Y1+=s + while(Y1!=Y2) + T=locate(X1,Y1,Z) + if(T.opacity) + return 0 + Y1+=s + else + var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) + var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles + var/signX = SIGN(X2-X1) + var/signY = SIGN(Y2-Y1) + if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) + if(dy > 0) + return get_step(start, SOUTH) + else + return get_step(start, NORTH) + else + if(dx > 0) + return get_step(start, WEST) + else + return get_step(start, EAST) + +/proc/get_mob_by_key(var/key) + for(var/mob/M in mob_list) + if(M.ckey == lowertext(key)) + return M + return null + + +// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. +/proc/get_active_candidates(var/buffer = 1) + + var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn + var/i = 0 + while(candidates.len <= 0 && i < 5) + for(var/mob/observer/dead/G in player_list) + if(((G.client.inactivity/10)/60) <= buffer + i) // the most active players are more likely to become an alien + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + candidates += G.key + i++ + return candidates + +// Same as above but for alien candidates. + +/proc/get_alien_candidates() + + var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn + var/i = 0 + while(candidates.len <= 0 && i < 5) + for(var/mob/observer/dead/G in player_list) + if(G.client.prefs.be_special & BE_ALIEN) + if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i) // the most active players are more likely to become an alien + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + candidates += G.key + i++ + return candidates + +/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) + if(!isobj(O)) O = new /obj/screen/text() + O.maptext = maptext + O.maptext_height = maptext_height + O.maptext_width = maptext_width + O.screen_loc = screen_loc + return O + +/proc/Show2Group4Delay(obj/O, list/group, delay=0) + if(!isobj(O)) return + if(!group) group = GLOB.clients + for(var/client/C in group) + C.screen += O + if(delay) + spawn(delay) + for(var/client/C in group) + C.screen -= O + +/datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) + +/proc/GetRedPart(const/hexa) + return hex2num(copytext(hexa,2,4)) + +/proc/GetGreenPart(const/hexa) + return hex2num(copytext(hexa,4,6)) + +/proc/GetBluePart(const/hexa) + return hex2num(copytext(hexa,6,8)) + +/proc/GetHexColors(const/hexa) + return list( + GetRedPart(hexa), + GetGreenPart(hexa), + GetBluePart(hexa) + ) + +/proc/MixColors(const/list/colors) + var/list/reds = list() + var/list/blues = list() + var/list/greens = list() + var/list/weights = list() + + for (var/i = 0, ++i <= colors.len) + reds.Add(GetRedPart(colors[i])) + blues.Add(GetBluePart(colors[i])) + greens.Add(GetGreenPart(colors[i])) + weights.Add(1) + + var/r = mixOneColor(weights, reds) + var/g = mixOneColor(weights, greens) + var/b = mixOneColor(weights, blues) + return rgb(r,g,b) + +/proc/mixOneColor(var/list/weight, var/list/color) + if (!weight || !color || length(weight)!=length(color)) + return 0 + + var/contents = length(weight) + var/i + + //normalize weights + var/listsum = 0 + for(i=1; i<=contents; i++) + listsum += weight[i] + for(i=1; i<=contents; i++) + weight[i] /= listsum + + //mix them + var/mixedcolor = 0 + for(i=1; i<=contents; i++) + mixedcolor += weight[i]*color[i] + mixedcolor = round(mixedcolor) + + //until someone writes a formal proof for this algorithm, let's keep this in +// if(mixedcolor<0x00 || mixedcolor>0xFF) +// return 0 + //that's not the kind of operation we are running here, nerd + mixedcolor=min(max(mixedcolor,0),255) + + return mixedcolor + +/** +* Gets the highest and lowest pressures from the tiles in cardinal directions +* around us, then checks the difference. +*/ +/proc/getOPressureDifferential(var/turf/loc) + var/minp=16777216; + var/maxp=0; + for(var/dir in GLOB.cardinal) + var/turf/simulated/T=get_turf(get_step(loc,dir)) + var/cp=0 + if(T && istype(T) && T.zone) + var/datum/gas_mixture/environment = T.return_air() + cp = environment.return_pressure() + else + if(istype(T,/turf/simulated)) + continue + if(cpmaxp)maxp=cp + return abs(minp-maxp) + +/proc/convert_k2c(var/temp) + return ((temp - T0C)) + +/proc/convert_c2k(var/temp) + return ((temp + T0C)) + +/proc/getCardinalAirInfo(var/turf/loc, var/list/stats=list("temperature")) + var/list/temps = new/list(4) + for(var/dir in GLOB.cardinal) + var/direction + switch(dir) + if(NORTH) + direction = 1 + if(SOUTH) + direction = 2 + if(EAST) + direction = 3 + if(WEST) + direction = 4 + var/turf/simulated/T=get_turf(get_step(loc,dir)) + var/list/rstats = new /list(stats.len) + if(T && istype(T) && T.zone) + var/datum/gas_mixture/environment = T.return_air() + for(var/i=1;i<=stats.len;i++) + if(stats[i] == "pressure") + rstats[i] = environment.return_pressure() + else + rstats[i] = environment.vars[stats[i]] + else if(istype(T, /turf/simulated)) + rstats = null // Exclude zone (wall, door, etc). + else if(istype(T, /turf)) + // Should still work. (/turf/return_air()) + var/datum/gas_mixture/environment = T.return_air() + for(var/i=1;i<=stats.len;i++) + if(stats[i] == "pressure") + rstats[i] = environment.return_pressure() + else + rstats[i] = environment.vars[stats[i]] + temps[direction] = rstats + return temps + +/proc/MinutesToTicks(var/minutes) + return SecondsToTicks(60 * minutes) + +/proc/SecondsToTicks(var/seconds) + return seconds * 10 + +/proc/window_flash(var/client_or_usr) + if (!client_or_usr) + return + winset(client_or_usr, "mainwindow", "flash=5") + +/** + * Get a bounding box of a list of atoms. + * + * Arguments: + * - atoms - List of atoms. Can accept output of view() and range() procs. + * + * Returns: list(x1, y1, x2, y2) + */ +/proc/get_bbox_of_atoms(list/atoms) + var/list/list_x = list() + var/list/list_y = list() + for(var/_a in atoms) + var/atom/a = _a + list_x += a.x + list_y += a.y + return list( + min(list_x), + min(list_y), + max(list_x), + max(list_y)) + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) + + //GLOB.debug_mob += O.contents.len + if(!recursion_limit) + return L + for(var/atom/A in O.contents) + + if(ismob(A)) + var/mob/M = A + if(client_check && !M.client) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + continue + if(sight_check && !isInSight(A, O)) + continue + L |= M + //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + + else if(include_radio && istype(A, /obj/item/radio)) + if(sight_check && !isInSight(A, O)) + continue + L |= A + + if(isobj(A) || ismob(A)) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + return L + +// The old system would loop through lists for a total of 5000 per function call, in an empty server. +// This new system will loop at around 1000 in an empty server. + +/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) + // Returns a list of mobs in range of R from source. Used in radio and say code. + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/atom/A in range) + if(ismob(A)) + var/mob/M = A + if(M.client || include_clientless) + hear += M + //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + else if(istype(A, /obj/item/radio)) + hear += A + + if(isobj(A) || ismob(A)) + hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) + + return hear + +/proc/get_belly(var/atom/A) // return a belly we're in, one way or another; and if we aren't (or are too deep to comprehend being in belly), returns null + var/atom/loc_check = A.loc + var/recursion_level = 0 + while(loc_check && !isbelly(loc_check) && !isturf(loc_check)) + if(recursion_level > 7) // abstractly picked number, but basically means we tried going 8 levels up. Something is wrong if youre THAT deep anyway + break + loc_check = loc_check.loc + recursion_level++ + if(isbelly(loc_check)) + return loc_check + return null + +/proc/get_all_prey_recursive(var/mob/living/L, var/client_check = 1) // returns all prey inside the target as well all prey of target's prey, as well as all prey inside target's prey, etc. + var/list/result = list() + + if(!istype(L) || !(L.vore_organs) || !(L.vore_organs.len)) + return result + + for(var/obj/belly/B in L.vore_organs) + for(var/mob/living/P in B.contents) + if(istype(P)) + if(client_check && P.client) + result |= P + result |= get_all_prey_recursive(P, client_check) + + return result + +/proc/random_color(saturated) //Returns a random color. If saturated is true, it will avoid pure white or pure black + var/r = rand(1,255) + var/g = rand(1,255) + var/b = rand(1,255) + + if(saturated) //Let's make sure we don't get too close to pure black or pure white, as they won't look good with grayscale sprites + if(r + g + b < 50) + r = r + rand(5,20) + g = g + rand(5,20) + b = b + rand(5,20) + else if (r + g + b > 700) + r = r - rand(5,50) + g = g - rand(5,50) + b = b - rand(5,50) + + var/color = rgb(r, g, b) + return color diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 95b77589226..a160069be49 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -1,329 +1,329 @@ -//Since it didn't really belong in any other category, I'm putting this here -//This is for procs to replace all the goddamn 'in world's that are chilling around the code - -var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player -var/global/list/mob_list = list() //List of all mobs, including clientless -var/global/list/human_mob_list = list() //List of all human mobs and sub-types, including clientless -var/global/list/silicon_mob_list = list() //List of all silicon mobs, including clientless -var/global/list/ai_list = list() //List of all AIs, including clientless -var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player -var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player -var/global/list/observer_mob_list = list() //List of all /mob/observer/dead, including clientless. -var/global/list/listening_objects = list() //List of all objects which care about receiving messages (communicators, radios, etc) -var/global/list/cleanbot_reserved_turfs = list() //List of all turfs currently targeted by some cleanbot - -var/global/list/cable_list = list() //Index for all cables, so that powernets don't have to look through the entire world all the time -var/global/list/landmarks_list = list() //list of all landmarks created -var/global/list/event_triggers = list() //Associative list of creator_ckey:list(landmark references) for event triggers -var/global/list/surgery_steps = list() //list of all surgery steps |BS12 -var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12 -var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking. -var/global/list/joblist = list() //list of all jobstypes, minus borg and AI - -#define all_genders_define_list list(MALE,FEMALE,PLURAL,NEUTER,HERM) //VOREStaton Edit -#define all_genders_text_list list("Male","Female","Plural","Neuter","Herm") //VOREStation Edit - -var/list/mannequins_ - -// Times that players are allowed to respawn ("ckey" = world.time) -GLOBAL_LIST_EMPTY(respawn_timers) - -// Holomaps -var/global/list/holomap_markers = list() -var/global/list/mapping_units = list() -var/global/list/mapping_beacons = list() - -//Preferences stuff - //Hairstyles -var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name -var/global/list/hair_styles_male_list = list() -var/global/list/hair_styles_female_list = list() -var/global/list/facial_hair_styles_list = list() //stores /datum/sprite_accessory/facial_hair indexed by name -var/global/list/facial_hair_styles_male_list = list() -var/global/list/facial_hair_styles_female_list = list() -var/global/list/skin_styles_female_list = list() //unused -var/global/list/body_marking_styles_list = list() //stores /datum/sprite_accessory/marking indexed by name -var/global/list/body_marking_nopersist_list = list() // Body marking styles, minus non-genetic markings and augments -var/global/list/ear_styles_list = list() // Stores /datum/sprite_accessory/ears indexed by type -var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail indexed by type -var/global/list/wing_styles_list = list() // Stores /datum/sprite_accessory/wing indexed by type - -GLOBAL_LIST_INIT(custom_species_bases, new) // Species that can be used for a Custom Species icon base - //Underwear -var/datum/category_collection/underwear/global_underwear = new() - - //Backpacks -var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Messenger Bag", "Sports Bag", "Strapless Satchel") //VOREStation edit -var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged", "Holographic", "Wrist-Bound", "Slider") -var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg) - -// Visual nets -var/list/datum/visualnet/visual_nets = list() -var/datum/visualnet/camera/cameranet = new() -var/datum/visualnet/cult/cultnet = new() - -// Runes -var/global/list/rune_list = new() -var/global/list/escape_list = list() -var/global/list/endgame_exits = list() -var/global/list/endgame_safespawns = list() - -var/global/list/syndicate_access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) - -// Ores (for mining) -GLOBAL_LIST_EMPTY(ore_data) -GLOBAL_LIST_EMPTY(alloy_data) - -// Strings which corraspond to bodypart covering flags, useful for outputting what something covers. -var/global/list/string_part_flags = list( - "head" = HEAD, - "face" = FACE, - "eyes" = EYES, - "upper body" = UPPER_TORSO, - "lower body" = LOWER_TORSO, - "legs" = LEGS, - "feet" = FEET, - "arms" = ARMS, - "hands" = HANDS -) - -// Strings which corraspond to slot flags, useful for outputting what slot something is. -var/global/list/string_slot_flags = list( - "back" = SLOT_BACK, - "face" = SLOT_MASK, - "waist" = SLOT_BELT, - "ID slot" = SLOT_ID, - "ears" = SLOT_EARS, - "eyes" = SLOT_EYES, - "hands" = SLOT_GLOVES, - "head" = SLOT_HEAD, - "feet" = SLOT_FEET, - "exo slot" = SLOT_OCLOTHING, - "body" = SLOT_ICLOTHING, - "uniform" = SLOT_TIE, - "holster" = SLOT_HOLSTER -) - -GLOBAL_LIST_EMPTY(mannequins) -/proc/get_mannequin(var/ckey = "NULL") - var/mob/living/carbon/human/dummy/mannequin/M = GLOB.mannequins[ckey] - if(!istype(M)) - GLOB.mannequins[ckey] = new /mob/living/carbon/human/dummy/mannequin(null) - M = GLOB.mannequins[ckey] - return M - -/proc/del_mannequin(var/ckey = "NULL") - GLOB.mannequins-= ckey - -////////////////////////// -/////Initial Building///// -////////////////////////// - -/proc/makeDatumRefLists() - var/list/paths - - //Hair - Initialise all /datum/sprite_accessory/hair into an list indexed by hair-style name - paths = subtypesof(/datum/sprite_accessory/hair) - for(var/path in paths) - var/datum/sprite_accessory/hair/H = new path() - hair_styles_list[H.name] = H - switch(H.gender) - if(MALE) hair_styles_male_list += H.name - if(FEMALE) hair_styles_female_list += H.name - else - hair_styles_male_list += H.name - hair_styles_female_list += H.name - - //Facial Hair - Initialise all /datum/sprite_accessory/facial_hair into an list indexed by facialhair-style name - paths = subtypesof(/datum/sprite_accessory/facial_hair) - for(var/path in paths) - var/datum/sprite_accessory/facial_hair/H = new path() - facial_hair_styles_list[H.name] = H - switch(H.gender) - if(MALE) facial_hair_styles_male_list += H.name - if(FEMALE) facial_hair_styles_female_list += H.name - else - facial_hair_styles_male_list += H.name - facial_hair_styles_female_list += H.name - - //Body markings - Initialise all /datum/sprite_accessory/marking into an list indexed by marking name - paths = subtypesof(/datum/sprite_accessory/marking) - for(var/path in paths) - var/datum/sprite_accessory/marking/M = new path() - body_marking_styles_list[M.name] = M - if(!M.genetic) - body_marking_nopersist_list[M.name] = M - - //Surgery Steps - Initialize all /datum/surgery_step into a list - paths = subtypesof(/datum/surgery_step) - for(var/T in paths) - var/datum/surgery_step/S = new T - surgery_steps += S - sort_surgeries() - - //List of job. I can't believe this was calculated multiple times per tick! - paths = subtypesof(/datum/job) - paths -= exclude_jobs - for(var/T in paths) - var/datum/job/J = new T - joblist[J.title] = J - - //Languages - paths = subtypesof(/datum/language) - for(var/T in paths) - var/datum/language/L = new T - if (isnull(GLOB.all_languages[L.name])) - GLOB.all_languages[L.name] = L - else - log_debug("Language name conflict! [T] is named [L.name], but that is taken by [GLOB.all_languages[L.name].type]") - if(isnull(GLOB.language_name_conflicts[L.name])) - GLOB.language_name_conflicts[L.name] = list(GLOB.all_languages[L.name]) - GLOB.language_name_conflicts[L.name] += L - - for (var/language_name in GLOB.all_languages) - var/datum/language/L = GLOB.all_languages[language_name] - if(!(L.flags & NONGLOBAL)) - if(isnull(GLOB.language_keys[L.key])) - GLOB.language_keys[L.key] = L - else - log_debug("Language key conflict! [L] has key [L.key], but that is taken by [(GLOB.language_keys[L.key])]") - if(isnull(GLOB.language_key_conflicts[L.key])) - GLOB.language_key_conflicts[L.key] = list(GLOB.language_keys[L.key]) - GLOB.language_key_conflicts[L.key] += L - - //Species - var/rkey = 0 - paths = subtypesof(/datum/species) - for(var/T in paths) - - rkey++ - - var/datum/species/S = T - if(!initial(S.name)) - continue - - S = new T - S.race_key = rkey //Used in mob icon caching. - GLOB.all_species[S.name] = S - - //Shakey shakey shake - sortTim(GLOB.all_species, GLOBAL_PROC_REF(cmp_species), associative = TRUE) - - //Split up the rest - for(var/speciesname in GLOB.all_species) - var/datum/species/S = GLOB.all_species[speciesname] - if(!(S.spawn_flags & SPECIES_IS_RESTRICTED)) - GLOB.playable_species += S.name - if(S.spawn_flags & SPECIES_IS_WHITELISTED) - GLOB.whitelisted_species += S.name - - // Suit cyclers - paths = subtypesof(/datum/suit_cycler_choice/department) - for(var/datum/suit_cycler_choice/SCC as anything in paths) - if(!initial(SCC.name)) - continue - GLOB.suit_cycler_departments += new SCC() - paths = subtypesof(/datum/suit_cycler_choice/species) - for(var/datum/suit_cycler_choice/SCC as anything in paths) - if(!initial(SCC.name)) - continue - GLOB.suit_cycler_species += new SCC() - paths = subtypesof(/datum/suit_cycler_choice/department/emag) - for(var/datum/suit_cycler_choice/SCC as anything in paths) - if(!initial(SCC.name)) - continue - GLOB.suit_cycler_emagged += new SCC() - - //Ores - paths = subtypesof(/ore) - for(var/oretype in paths) - var/ore/OD = new oretype() - GLOB.ore_data[OD.name] = OD - - paths = subtypesof(/datum/alloy) - for(var/alloytype in paths) - GLOB.alloy_data += new alloytype() - - //Closet appearances - GLOB.closet_appearances = decls_repository.get_decls_of_type(/decl/closet_appearance) - - paths = subtypesof(/datum/sprite_accessory/ears) - for(var/path in paths) - var/obj/item/clothing/head/instance = new path() - ear_styles_list[path] = instance - - // Custom Tails - paths = subtypesof(/datum/sprite_accessory/tail) - /datum/sprite_accessory/tail/taur - for(var/path in paths) - var/datum/sprite_accessory/tail/instance = new path() - tail_styles_list[path] = instance - - // Custom Wings - paths = subtypesof(/datum/sprite_accessory/wing) - for(var/path in paths) - var/datum/sprite_accessory/wing/instance = new path() - wing_styles_list[path] = instance - - // VOREStation Add - Vore Modes! - paths = typesof(/datum/digest_mode) - for(var/T in paths) - var/datum/digest_mode/DM = new T - GLOB.digest_modes[DM.id] = DM - // VOREStation Add End - init_crafting_recipes(GLOB.crafting_recipes) - -/* - // Custom species traits - paths = subtypesof(/datum/trait) - for(var/path in paths) - var/datum/trait/instance = new path() - if(!instance.name) - continue //A prototype or something - var/cost = instance.cost - traits_costs[path] = cost - all_traits[path] = instance - switch(cost) - if(-INFINITY to -0.1) - negative_traits[path] = instance - if(0) - neutral_traits[path] = instance - if(0.1 to INFINITY) - positive_traits[path] = instance -*/ - - // Custom species icon bases - var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //VOREStation Edit - var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID) //VOREStation Edit - for(var/species_name in GLOB.playable_species) - if(species_name in blacklisted_icons) - continue - var/datum/species/S = GLOB.all_species[species_name] - if(S.spawn_flags & SPECIES_IS_WHITELISTED) - continue - GLOB.custom_species_bases += species_name - for(var/species_name in whitelisted_icons) - GLOB.custom_species_bases += species_name - - return 1 // Hooks must return 1 - - -/// Inits the crafting recipe list, sorting crafting recipe requirements in the process. -/proc/init_crafting_recipes(list/crafting_recipes) - for(var/path in subtypesof(/datum/crafting_recipe)) - var/datum/crafting_recipe/recipe = new path() - recipe.reqs = sortList(recipe.reqs, GLOBAL_PROC_REF(cmp_crafting_req_priority)) - crafting_recipes += recipe - return crafting_recipes -/* // Uncomment to debug chemical reaction list. -/client/verb/debug_chemical_list() - - for (var/reaction in chemical_reactions_list) - . += "chemical_reactions_list\[\"[reaction]\"\] = \"[chemical_reactions_list[reaction]]\"\n" - if(islist(chemical_reactions_list[reaction])) - var/list/L = chemical_reactions_list[reaction] - for(var/t in L) - . += " has: [t]\n" - to_world(.) -*/ -//Hexidecimal numbers -var/global/list/hexNums = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") +//Since it didn't really belong in any other category, I'm putting this here +//This is for procs to replace all the goddamn 'in world's that are chilling around the code + +var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player +var/global/list/mob_list = list() //List of all mobs, including clientless +var/global/list/human_mob_list = list() //List of all human mobs and sub-types, including clientless +var/global/list/silicon_mob_list = list() //List of all silicon mobs, including clientless +var/global/list/ai_list = list() //List of all AIs, including clientless +var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player +var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player +var/global/list/observer_mob_list = list() //List of all /mob/observer/dead, including clientless. +var/global/list/listening_objects = list() //List of all objects which care about receiving messages (communicators, radios, etc) +var/global/list/cleanbot_reserved_turfs = list() //List of all turfs currently targeted by some cleanbot + +var/global/list/cable_list = list() //Index for all cables, so that powernets don't have to look through the entire world all the time +var/global/list/landmarks_list = list() //list of all landmarks created +var/global/list/event_triggers = list() //Associative list of creator_ckey:list(landmark references) for event triggers +var/global/list/surgery_steps = list() //list of all surgery steps |BS12 +var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12 +var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking. +var/global/list/joblist = list() //list of all jobstypes, minus borg and AI + +#define all_genders_define_list list(MALE,FEMALE,PLURAL,NEUTER,HERM) //VOREStaton Edit +#define all_genders_text_list list("Male","Female","Plural","Neuter","Herm") //VOREStation Edit + +var/list/mannequins_ + +// Times that players are allowed to respawn ("ckey" = world.time) +GLOBAL_LIST_EMPTY(respawn_timers) + +// Holomaps +var/global/list/holomap_markers = list() +var/global/list/mapping_units = list() +var/global/list/mapping_beacons = list() + +//Preferences stuff + //Hairstyles +var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name +var/global/list/hair_styles_male_list = list() +var/global/list/hair_styles_female_list = list() +var/global/list/facial_hair_styles_list = list() //stores /datum/sprite_accessory/facial_hair indexed by name +var/global/list/facial_hair_styles_male_list = list() +var/global/list/facial_hair_styles_female_list = list() +var/global/list/skin_styles_female_list = list() //unused +var/global/list/body_marking_styles_list = list() //stores /datum/sprite_accessory/marking indexed by name +var/global/list/body_marking_nopersist_list = list() // Body marking styles, minus non-genetic markings and augments +var/global/list/ear_styles_list = list() // Stores /datum/sprite_accessory/ears indexed by type +var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail indexed by type +var/global/list/wing_styles_list = list() // Stores /datum/sprite_accessory/wing indexed by type + +GLOBAL_LIST_INIT(custom_species_bases, new) // Species that can be used for a Custom Species icon base + //Underwear +var/datum/category_collection/underwear/global_underwear = new() + + //Backpacks +var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Messenger Bag", "Sports Bag", "Strapless Satchel") //VOREStation edit +var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged", "Holographic", "Wrist-Bound","Slider", "Vintage") +var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg) + +// Visual nets +var/list/datum/visualnet/visual_nets = list() +var/datum/visualnet/camera/cameranet = new() +var/datum/visualnet/cult/cultnet = new() + +// Runes +var/global/list/rune_list = new() +var/global/list/escape_list = list() +var/global/list/endgame_exits = list() +var/global/list/endgame_safespawns = list() + +var/global/list/syndicate_access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) + +// Ores (for mining) +GLOBAL_LIST_EMPTY(ore_data) +GLOBAL_LIST_EMPTY(alloy_data) + +// Strings which corraspond to bodypart covering flags, useful for outputting what something covers. +var/global/list/string_part_flags = list( + "head" = HEAD, + "face" = FACE, + "eyes" = EYES, + "upper body" = UPPER_TORSO, + "lower body" = LOWER_TORSO, + "legs" = LEGS, + "feet" = FEET, + "arms" = ARMS, + "hands" = HANDS +) + +// Strings which corraspond to slot flags, useful for outputting what slot something is. +var/global/list/string_slot_flags = list( + "back" = SLOT_BACK, + "face" = SLOT_MASK, + "waist" = SLOT_BELT, + "ID slot" = SLOT_ID, + "ears" = SLOT_EARS, + "eyes" = SLOT_EYES, + "hands" = SLOT_GLOVES, + "head" = SLOT_HEAD, + "feet" = SLOT_FEET, + "exo slot" = SLOT_OCLOTHING, + "body" = SLOT_ICLOTHING, + "uniform" = SLOT_TIE, + "holster" = SLOT_HOLSTER +) + +GLOBAL_LIST_EMPTY(mannequins) +/proc/get_mannequin(var/ckey = "NULL") + var/mob/living/carbon/human/dummy/mannequin/M = GLOB.mannequins[ckey] + if(!istype(M)) + GLOB.mannequins[ckey] = new /mob/living/carbon/human/dummy/mannequin(null) + M = GLOB.mannequins[ckey] + return M + +/proc/del_mannequin(var/ckey = "NULL") + GLOB.mannequins-= ckey + +////////////////////////// +/////Initial Building///// +////////////////////////// + +/proc/makeDatumRefLists() + var/list/paths + + //Hair - Initialise all /datum/sprite_accessory/hair into an list indexed by hair-style name + paths = subtypesof(/datum/sprite_accessory/hair) + for(var/path in paths) + var/datum/sprite_accessory/hair/H = new path() + hair_styles_list[H.name] = H + switch(H.gender) + if(MALE) hair_styles_male_list += H.name + if(FEMALE) hair_styles_female_list += H.name + else + hair_styles_male_list += H.name + hair_styles_female_list += H.name + + //Facial Hair - Initialise all /datum/sprite_accessory/facial_hair into an list indexed by facialhair-style name + paths = subtypesof(/datum/sprite_accessory/facial_hair) + for(var/path in paths) + var/datum/sprite_accessory/facial_hair/H = new path() + facial_hair_styles_list[H.name] = H + switch(H.gender) + if(MALE) facial_hair_styles_male_list += H.name + if(FEMALE) facial_hair_styles_female_list += H.name + else + facial_hair_styles_male_list += H.name + facial_hair_styles_female_list += H.name + + //Body markings - Initialise all /datum/sprite_accessory/marking into an list indexed by marking name + paths = subtypesof(/datum/sprite_accessory/marking) + for(var/path in paths) + var/datum/sprite_accessory/marking/M = new path() + body_marking_styles_list[M.name] = M + if(!M.genetic) + body_marking_nopersist_list[M.name] = M + + //Surgery Steps - Initialize all /datum/surgery_step into a list + paths = subtypesof(/datum/surgery_step) + for(var/T in paths) + var/datum/surgery_step/S = new T + surgery_steps += S + sort_surgeries() + + //List of job. I can't believe this was calculated multiple times per tick! + paths = subtypesof(/datum/job) + paths -= exclude_jobs + for(var/T in paths) + var/datum/job/J = new T + joblist[J.title] = J + + //Languages + paths = subtypesof(/datum/language) + for(var/T in paths) + var/datum/language/L = new T + if (isnull(GLOB.all_languages[L.name])) + GLOB.all_languages[L.name] = L + else + log_debug("Language name conflict! [T] is named [L.name], but that is taken by [GLOB.all_languages[L.name].type]") + if(isnull(GLOB.language_name_conflicts[L.name])) + GLOB.language_name_conflicts[L.name] = list(GLOB.all_languages[L.name]) + GLOB.language_name_conflicts[L.name] += L + + for (var/language_name in GLOB.all_languages) + var/datum/language/L = GLOB.all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + if(isnull(GLOB.language_keys[L.key])) + GLOB.language_keys[L.key] = L + else + log_debug("Language key conflict! [L] has key [L.key], but that is taken by [(GLOB.language_keys[L.key])]") + if(isnull(GLOB.language_key_conflicts[L.key])) + GLOB.language_key_conflicts[L.key] = list(GLOB.language_keys[L.key]) + GLOB.language_key_conflicts[L.key] += L + + //Species + var/rkey = 0 + paths = subtypesof(/datum/species) + for(var/T in paths) + + rkey++ + + var/datum/species/S = T + if(!initial(S.name)) + continue + + S = new T + S.race_key = rkey //Used in mob icon caching. + GLOB.all_species[S.name] = S + + //Shakey shakey shake + sortTim(GLOB.all_species, GLOBAL_PROC_REF(cmp_species), associative = TRUE) + + //Split up the rest + for(var/speciesname in GLOB.all_species) + var/datum/species/S = GLOB.all_species[speciesname] + if(!(S.spawn_flags & SPECIES_IS_RESTRICTED)) + GLOB.playable_species += S.name + if(S.spawn_flags & SPECIES_IS_WHITELISTED) + GLOB.whitelisted_species += S.name + + // Suit cyclers + paths = subtypesof(/datum/suit_cycler_choice/department) + for(var/datum/suit_cycler_choice/SCC as anything in paths) + if(!initial(SCC.name)) + continue + GLOB.suit_cycler_departments += new SCC() + paths = subtypesof(/datum/suit_cycler_choice/species) + for(var/datum/suit_cycler_choice/SCC as anything in paths) + if(!initial(SCC.name)) + continue + GLOB.suit_cycler_species += new SCC() + paths = subtypesof(/datum/suit_cycler_choice/department/emag) + for(var/datum/suit_cycler_choice/SCC as anything in paths) + if(!initial(SCC.name)) + continue + GLOB.suit_cycler_emagged += new SCC() + + //Ores + paths = subtypesof(/ore) + for(var/oretype in paths) + var/ore/OD = new oretype() + GLOB.ore_data[OD.name] = OD + + paths = subtypesof(/datum/alloy) + for(var/alloytype in paths) + GLOB.alloy_data += new alloytype() + + //Closet appearances + GLOB.closet_appearances = decls_repository.get_decls_of_type(/decl/closet_appearance) + + paths = subtypesof(/datum/sprite_accessory/ears) + for(var/path in paths) + var/obj/item/clothing/head/instance = new path() + ear_styles_list[path] = instance + + // Custom Tails + paths = subtypesof(/datum/sprite_accessory/tail) - /datum/sprite_accessory/tail/taur + for(var/path in paths) + var/datum/sprite_accessory/tail/instance = new path() + tail_styles_list[path] = instance + + // Custom Wings + paths = subtypesof(/datum/sprite_accessory/wing) + for(var/path in paths) + var/datum/sprite_accessory/wing/instance = new path() + wing_styles_list[path] = instance + + // VOREStation Add - Vore Modes! + paths = typesof(/datum/digest_mode) + for(var/T in paths) + var/datum/digest_mode/DM = new T + GLOB.digest_modes[DM.id] = DM + // VOREStation Add End + init_crafting_recipes(GLOB.crafting_recipes) + +/* + // Custom species traits + paths = subtypesof(/datum/trait) + for(var/path in paths) + var/datum/trait/instance = new path() + if(!instance.name) + continue //A prototype or something + var/cost = instance.cost + traits_costs[path] = cost + all_traits[path] = instance + switch(cost) + if(-INFINITY to -0.1) + negative_traits[path] = instance + if(0) + neutral_traits[path] = instance + if(0.1 to INFINITY) + positive_traits[path] = instance +*/ + + // Custom species icon bases + var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //VOREStation Edit + var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID) //VOREStation Edit + for(var/species_name in GLOB.playable_species) + if(species_name in blacklisted_icons) + continue + var/datum/species/S = GLOB.all_species[species_name] + if(S.spawn_flags & SPECIES_IS_WHITELISTED) + continue + GLOB.custom_species_bases += species_name + for(var/species_name in whitelisted_icons) + GLOB.custom_species_bases += species_name + + return 1 // Hooks must return 1 + + +/// Inits the crafting recipe list, sorting crafting recipe requirements in the process. +/proc/init_crafting_recipes(list/crafting_recipes) + for(var/path in subtypesof(/datum/crafting_recipe)) + var/datum/crafting_recipe/recipe = new path() + recipe.reqs = sortList(recipe.reqs, GLOBAL_PROC_REF(cmp_crafting_req_priority)) + crafting_recipes += recipe + return crafting_recipes +/* // Uncomment to debug chemical reaction list. +/client/verb/debug_chemical_list() + + for (var/reaction in chemical_reactions_list) + . += "chemical_reactions_list\[\"[reaction]\"\] = \"[chemical_reactions_list[reaction]]\"\n" + if(islist(chemical_reactions_list[reaction])) + var/list/L = chemical_reactions_list[reaction] + for(var/t in L) + . += " has: [t]\n" + to_world(.) +*/ +//Hexidecimal numbers +var/global/list/hexNums = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index 1392a67b5e0..452fb6b1f2e 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -132,17 +132,17 @@ var/global/list/global_vore_egg_types = list( "Xenomorph", "Chocolate", "Boney", - "Slime glob", + "Slime Glob", "Chicken", "Synthetic", "Bluespace Floppy", "Bluespace Compressed File", "Bluespace CD", - "Escape pod", - "Cooking error", - "Web cocoon", + "Escape Pod", + "Cooking Error", + "Web Cocoon", "Honeycomb", - "Bug cocoon", + "Bug Cocoon", "Rock", "Yellow", "Blue", @@ -151,7 +151,7 @@ var/global/list/global_vore_egg_types = list( "Purple", "Red", "Rainbow", - "Spotted pink") + "Spotted Pink") var/global/list/tf_vore_egg_types = list( "Unathi" = /obj/item/weapon/storage/vore_egg/unathi, @@ -167,17 +167,17 @@ var/global/list/tf_vore_egg_types = list( "Xenomorph" = /obj/item/weapon/storage/vore_egg/xenomorph, "Chocolate" = /obj/item/weapon/storage/vore_egg/chocolate, "Boney" = /obj/item/weapon/storage/vore_egg/owlpellet, - "Slime glob" = /obj/item/weapon/storage/vore_egg/slimeglob, + "Slime Glob" = /obj/item/weapon/storage/vore_egg/slimeglob, "Chicken" = /obj/item/weapon/storage/vore_egg/chicken, "Synthetic" = /obj/item/weapon/storage/vore_egg/synthetic, "Bluespace Floppy" = /obj/item/weapon/storage/vore_egg/floppy, "Bluespace Compressed File" = /obj/item/weapon/storage/vore_egg/file, "Bluespace CD" = /obj/item/weapon/storage/vore_egg/cd, - "Escape pod" = /obj/item/weapon/storage/vore_egg/escapepod, - "Cooking error" = /obj/item/weapon/storage/vore_egg/badrecipe, - "Web cocoon" = /obj/item/weapon/storage/vore_egg/cocoon, + "Escape Pod" = /obj/item/weapon/storage/vore_egg/escapepod, + "Cooking Error" = /obj/item/weapon/storage/vore_egg/badrecipe, + "Web Cocoon" = /obj/item/weapon/storage/vore_egg/cocoon, "Honeycomb" = /obj/item/weapon/storage/vore_egg/honeycomb, - "Bug cocoon" = /obj/item/weapon/storage/vore_egg/bugcocoon, + "Bug Cocoon" = /obj/item/weapon/storage/vore_egg/bugcocoon, "Rock" = /obj/item/weapon/storage/vore_egg/rock, "Yellow" = /obj/item/weapon/storage/vore_egg/yellow, "Blue" = /obj/item/weapon/storage/vore_egg/blue, @@ -186,7 +186,7 @@ var/global/list/tf_vore_egg_types = list( "Purple" = /obj/item/weapon/storage/vore_egg/purple, "Red" = /obj/item/weapon/storage/vore_egg/red, "Rainbow" = /obj/item/weapon/storage/vore_egg/rainbow, - "Spotted pink" = /obj/item/weapon/storage/vore_egg/pinkspots) + "Spotted Pink" = /obj/item/weapon/storage/vore_egg/pinkspots) var/global/list/edible_trash = list(/obj/item/broken_device, /obj/item/clothing/accessory/collar, @@ -665,9 +665,7 @@ var/global/list/xenobio_gold_mobs_hostile = list( /mob/living/simple_mob/animal/giant_spider/webslinger, /mob/living/simple_mob/animal/giant_spider/phorogenic, /mob/living/simple_mob/animal/giant_spider/carrier, - /mob/living/simple_mob/animal/giant_spider/nurse, /mob/living/simple_mob/animal/giant_spider/ion, - /mob/living/simple_mob/animal/giant_spider/nurse/queen, /mob/living/simple_mob/animal/sif/diyaab, /mob/living/simple_mob/animal/sif/duck, /mob/living/simple_mob/animal/sif/frostfly, diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index 31970b792fa..9fd9e185002 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -1,687 +1,751 @@ -#define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87)) - -/icon/proc/MakeLying() - var/icon/I = new(src,dir=SOUTH) - I.BecomeLying() - return I - -/icon/proc/BecomeLying() - Turn(90) - Shift(SOUTH,6) - Shift(EAST,1) - -// Multiply all alpha values by this float -/icon/proc/ChangeOpacity(opacity = 1.0) - MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0) - -// Convert to grayscale -/icon/proc/GrayScale() - MapColors(0.3,0.3,0.3, 0.59,0.59,0.59, 0.11,0.11,0.11, 0,0,0) - -/icon/proc/ColorTone(tone) - GrayScale() - - var/list/TONE = rgb2num(tone) - var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1) - - var/icon/upper = (255-gray) ? new(src) : null - - if(gray) - MapColors(255/gray,0,0, 0,255/gray,0, 0,0,255/gray, 0,0,0) - Blend(tone, ICON_MULTIPLY) - else SetIntensity(0) - if(255-gray) - upper.Blend(rgb(gray,gray,gray), ICON_SUBTRACT) - upper.MapColors((255-TONE[1])/(255-gray),0,0,0, 0,(255-TONE[2])/(255-gray),0,0, 0,0,(255-TONE[3])/(255-gray),0, 0,0,0,0, 0,0,0,1) - Blend(upper, ICON_ADD) - -// Take the minimum color of two icons; combine transparency as if blending with ICON_ADD -/icon/proc/MinColors(icon) - var/icon/I = new(src) - I.Opaque() - I.Blend(icon, ICON_SUBTRACT) - Blend(I, ICON_SUBTRACT) - -// Take the maximum color of two icons; combine opacity as if blending with ICON_OR -/icon/proc/MaxColors(icon) - var/icon/I - if(isicon(icon)) - I = new(icon) - else - // solid color - I = new(src) - I.Blend("#000000", ICON_OVERLAY) - I.SwapColor("#000000", null) - I.Blend(icon, ICON_OVERLAY) - var/icon/J = new(src) - J.Opaque() - I.Blend(J, ICON_SUBTRACT) - Blend(I, ICON_OR) - -// make this icon fully opaque--transparent pixels become black -/icon/proc/Opaque(background = "#000000") - SwapColor(null, background) - MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0, 0,0,0,1) - -// Change a grayscale icon into a white icon where the original color becomes the alpha -// I.e., black -> transparent, gray -> translucent white, white -> solid white -/icon/proc/BecomeAlphaMask() - SwapColor(null, "#000000ff") // don't let transparent become gray - MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) - -/icon/proc/UseAlphaMask(mask) - Opaque() - AddAlphaMask(mask) - -/icon/proc/AddAlphaMask(mask) - var/icon/M = new(mask) - M.Blend("#ffffff", ICON_SUBTRACT) - // apply mask - Blend(M, ICON_ADD) - -/proc/BlendRGB(rgb1, rgb2, amount) - var/list/RGB1 = rgb2num(rgb1) - var/list/RGB2 = rgb2num(rgb2) - - // add missing alpha if needed - if(RGB1.len < RGB2.len) RGB1 += 255 - else if(RGB2.len < RGB1.len) RGB2 += 255 - var/usealpha = RGB1.len > 3 - - var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) - var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) - var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) - var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null - - return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) - -// Ported from /tg/station -// Creates a single icon from a given /atom or /image. Only the first argument is required. -/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) - //Define... defines. - var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") - - #define BLANK icon(flat_template) - #define SET_SELF(SETVAR) do { \ - var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ - if(A.alpha<255) { \ - SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ - } \ - if(A.color) { \ - if(islist(A.color)){ \ - SELF_ICON.MapColors(arglist(A.color))} \ - else{ \ - SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ - } \ - ##SETVAR=SELF_ICON;\ - } while (0) - #define INDEX_X_LOW 1 - #define INDEX_X_HIGH 2 - #define INDEX_Y_LOW 3 - #define INDEX_Y_HIGH 4 - - #define flatX1 flat_size[INDEX_X_LOW] - #define flatX2 flat_size[INDEX_X_HIGH] - #define flatY1 flat_size[INDEX_Y_LOW] - #define flatY2 flat_size[INDEX_Y_HIGH] - #define addX1 add_size[INDEX_X_LOW] - #define addX2 add_size[INDEX_X_HIGH] - #define addY1 add_size[INDEX_Y_LOW] - #define addY2 add_size[INDEX_Y_HIGH] - - if(!A || A.alpha <= 0) - return BLANK - - var/noIcon = FALSE - if(start) - if(!defdir) - defdir = A.dir - if(!deficon) - deficon = A.icon - if(!defstate) - defstate = A.icon_state - if(!defblend) - defblend = A.blend_mode - - var/curicon = A.icon || deficon - var/curstate = A.icon_state || defstate - - if(!((noIcon = (!curicon)))) - var/curstates = cached_icon_states(curicon) - if(!(curstate in curstates)) - if("" in curstates) - curstate = "" - else - noIcon = TRUE // Do not render this object. - - var/curdir - var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have - - // Use the requested dir or the atom's current dir - curdir = defdir || A.dir - - //Try to remove/optimize this section ASAP, CPU hog. //Slightly mitigated by implementing caching using cached_icon_states - //Determines if there's directionals. - if(!noIcon && curdir != SOUTH) - var/exist = FALSE - var/static/list/checkdirs = list(NORTH, EAST, WEST) - for(var/i in checkdirs) //Not using GLOB for a reason. - if(length(cached_icon_states(icon(curicon, curstate, i)))) - exist = TRUE - break - if(!exist) - base_icon_dir = SOUTH - // - - if(!base_icon_dir) - base_icon_dir = curdir - - ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. - - var/curblend = A.blend_mode || defblend - - if(A.overlays.len || A.underlays.len) - var/icon/flat = BLANK - // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed - var/list/layers = list() - var/image/copy - // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) - copy.color = A.color - copy.alpha = A.alpha - copy.blend_mode = curblend - layers[copy] = A.layer - - // Loop through the underlays, then overlays, sorting them into the layers list - for(var/process_set in 0 to 1) - var/list/process = process_set? A.overlays : A.underlays - for(var/i in 1 to process.len) - var/image/current = process[i] - if(!current) - continue - if(current.plane != FLOAT_PLANE && current.plane != A.plane) - continue - var/current_layer = current.layer - if(current_layer < 0) - //if(current_layer <= -1000) - //return flat - current_layer = process_set + A.layer + current_layer / 1000 - - for(var/p in 1 to layers.len) - var/image/cmp = layers[p] - if(current_layer < layers[cmp]) - layers.Insert(p, current) - break - layers[current] = current_layer - - //sortTim(layers, GLOBAL_PROC_REF(cmp_image_layer_asc)) - - var/icon/add // Icon of overlay being added - - // Current dimensions of flattened icon - var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) - // Dimensions of overlay being added - var/list/add_size[4] - - for(var/V in layers) - var/image/I = V - if(I.alpha == 0) - continue - - if(I == copy) // 'I' is an /image based on the object being flattened. - curblend = BLEND_OVERLAY - add = icon(I.icon, I.icon_state, base_icon_dir) - else // 'I' is an appearance object. - add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) - if(!add) - continue - // Find the new dimensions of the flat icon to fit the added overlay - add_size = list( - min(flatX1, I.pixel_x+1), - max(flatX2, I.pixel_x+add.Width()), - min(flatY1, I.pixel_y+1), - max(flatY2, I.pixel_y+add.Height()) - ) - - if(flat_size ~! add_size) - // Resize the flattened icon so the new icon fits - flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 - ) - flat_size = add_size.Copy() - - // Blend the overlay into the flattened icon - flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) - - if(A.color) - if(islist(A.color)) - flat.MapColors(arglist(A.color)) - else - flat.Blend(A.color, ICON_MULTIPLY) - - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) - - if(no_anim) - //Clean up repeated frames - var/icon/cleaned = new /icon() - cleaned.Insert(flat, "", SOUTH, 1, 0) - . = cleaned - else - . = icon(flat, "", SOUTH) - else //There's no overlays. - if(!noIcon) - SET_SELF(.) - - //Clear defines - #undef flatX1 - #undef flatX2 - #undef flatY1 - #undef flatY2 - #undef addX1 - #undef addX2 - #undef addY1 - #undef addY2 - - #undef INDEX_X_LOW - #undef INDEX_X_HIGH - #undef INDEX_Y_LOW - #undef INDEX_Y_HIGH - - #undef BLANK - #undef SET_SELF - -/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N - var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. - for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. - if(I:layer>A.layer) continue//If layer is greater than what we need, skip it. - var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects. - //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. - alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. - return alpha_mask//And now return the mask. - -//getFlatIcon but generates an icon that can face ALL four directions. The only four. -/proc/getCompoundIcon(atom/A) - var/icon/north = getFlatIcon(A,defdir=NORTH) - var/icon/south = getFlatIcon(A,defdir=SOUTH) - var/icon/east = getFlatIcon(A,defdir=EAST) - var/icon/west = getFlatIcon(A,defdir=WEST) - - //Starts with a blank icon because of byond bugs. - var/icon/full = icon('icons/effects/effects.dmi', "icon_state"="nothing") - - full.Insert(north,dir=NORTH) - full.Insert(south,dir=SOUTH) - full.Insert(east,dir=EAST) - full.Insert(west,dir=WEST) - qdel(north) - qdel(south) - qdel(east) - qdel(west) - return full - -/proc/downloadImage(atom/A, dir) - var/icon/this_icon = getFlatIcon(A,defdir=dir) - - usr << ftp(this_icon,"[A.name].png") - -/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. - var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. - //Now we need to culculate overlays+underlays and add them together to form an image for a mask. - //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. - var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. - opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. - opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. - for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. - var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. - switch(i)//Now to determine offset so the result is somewhat blurred. - if(1) I.pixel_x-- - if(2) I.pixel_x++ - if(3) I.pixel_y-- - if(4) I.pixel_y++ - overlays += I//And finally add the overlay. - -/proc/getHologramIcon(icon/A, safety=1, no_color = FALSE)//If safety is on, a new icon is not created. - var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. - /* VOREStation Removal - For AI Vore effects - if(!no_color) - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - */ //VOREStation Removal End - var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. - flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. - return flat_icon - -//For photo camera. -/proc/build_composite_icon(atom/A) - var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) - for(var/O in A.overlays) - var/image/I = O - composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) - return composite - -GLOBAL_LIST_EMPTY(icon_state_lists) -/proc/cached_icon_states(var/icon/I) - if(!I) - return list() - var/key = I - var/returnlist = GLOB.icon_state_lists[key] - if(!returnlist) - returnlist = icon_states(I) - if(isfile(I)) // It's something that will stick around - GLOB.icon_state_lists[key] = returnlist - return returnlist - -/proc/expire_states_cache(var/key) - if(GLOB.icon_state_lists[key]) - GLOB.icon_state_lists -= key - return TRUE - return FALSE - -GLOBAL_LIST_EMPTY(cached_examine_icons) -/proc/set_cached_examine_icon(var/atom/A, var/icon/I, var/expiry = 12000) - GLOB.cached_examine_icons[WEAKREF(A)] = I - if(expiry) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(uncache_examine_icon), WEAKREF(A)), expiry, TIMER_UNIQUE) - -/proc/get_cached_examine_icon(var/atom/A) - var/datum/weakref/WR = WEAKREF(A) - return GLOB.cached_examine_icons[WR] - -/proc/uncache_examine_icon(var/datum/weakref/WR) - GLOB.cached_examine_icons -= WR - -/proc/adjust_brightness(var/color, var/value) - if (!color) return "#FFFFFF" - if (!value) return color - - var/list/RGB = rgb2num(color) - RGB[1] = CLAMP(RGB[1]+value,0,255) - RGB[2] = CLAMP(RGB[2]+value,0,255) - RGB[3] = CLAMP(RGB[3]+value,0,255) - return rgb(RGB[1],RGB[2],RGB[3]) - -/proc/sort_atoms_by_layer(var/list/atoms) - // Comb sort icons based on levels - var/list/result = atoms.Copy() - var/gap = result.len - var/swapped = 1 - while (gap > 1 || swapped) - swapped = 0 - if(gap > 1) - gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient - if(gap < 1) - gap = 1 - for(var/i = 1; gap + i <= result.len; i++) - var/atom/l = result[i] //Fucking hate - var/atom/r = result[gap+i] //how lists work here - if(l.layer > r.layer) //no "result[i].layer" for me - result.Swap(i, gap + i) - swapped = 1 - return result - -/proc/gen_hud_image(var/file, var/person, var/state, var/plane) - var/image/img = image(file, person, state) - img.plane = plane //Thanks Byond. - img.layer = MOB_LAYER-0.2 - img.appearance_flags = APPEARANCE_UI - return img - -/** -* Animate a 'halo' around an object. -* -* This proc is not exactly cheap. You'd be well advised to set up many-loops rather than call this super-often. getCompoundIcon is -* mostly to blame for this. If Byond ever implements a way to get something's icon more 'gently' than this, do that instead. -* -* @param A This is the atom to put the halo on -* @param simple_icons If set to TRUE, will just perform a very basic icon and icon_state steal. DO USE when possible. -* @param color This is the color for the halo -* @param anim_duration This decides how fast (or slow) the animation plays -* @param offset Mysterious variable that determines size of the halo's gap from icon -* @param loops How many times the animation loops -* @param grow_to Relative to the size of the icon, how big the halo grows while fading (don't use negatives for inward halos, use < 1) -* @param pixel_scale If you'd like the halo to use pixel scale or the default 'fuzzy' scale -*/ -/proc/animate_aura(var/atom/A, var/simple_icons, var/color = "#00FF22", var/anim_duration = 5, var/offset = 1, var/loops = 1, var/grow_to = 2, var/pixel_scale = FALSE) - ASSERT(A) - - //Take a guess at this, if they didn't set it - if(isnull(simple_icons)) - if(ismob(A)) - simple_icons = FALSE - else - simple_icons = TRUE - - //Get their icon - var/icon/hole - - if(simple_icons) - hole = icon(A.icon, A.icon_state) - else - hole = getCompoundIcon(A) - - hole.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White. - - //Make a bigger version - var/icon/grower = new(hole) - var/orig_width = grower.Width() - var/orig_height = grower.Height() - var/end_width = orig_width+(offset*2) - var/end_height = orig_height+(offset*2) - var/half_diff_width = (end_width-orig_width)*0.5 - var/half_diff_height = (end_height-orig_height)*0.5 - - //Make icon black - grower.SwapColor("#FFFFFF","#000000") //Black. - - //Scale both icons big so we don't have to deal with low-pixel garbage issues - grower.Scale(orig_width*10,orig_height*10) - hole.Scale(orig_width*9,orig_height*9) - - //Blend the hole in - grower.Blend(hole,ICON_OVERLAY, x = ((orig_width*10-orig_width*9)*0.5)+1, y = ((orig_height*10-orig_height*9)*0.5)+1) - - //Swap white to zero alpha - grower.SwapColor("#FFFFFF","#00000000") - - //Color it - grower.SwapColor("#000000",color) - - //Scale it to final height - grower.Scale(end_width,end_height) - - //Flick it onto them - var/image/img = image(grower,A) - if(pixel_scale) - img.appearance_flags |= PIXEL_SCALE - img.pixel_x = half_diff_width*-1 - img.pixel_y = half_diff_height*-1 - flick_overlay_view(img, A, anim_duration*loops, TRUE) - - //Animate it growing - animate(img, alpha = 0, transform = matrix()*grow_to, time = anim_duration, loop = loops) - -/// generates a filename for a given asset. -/// like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension) -/// used so that certain asset files dont have to be hashed twice -/proc/generate_and_hash_rsc_file(file, dmi_file_path) - var/rsc_ref = fcopy_rsc(file) - var/hash - //if we have a valid dmi file path we can trust md5'ing the rsc file because we know it doesnt have the bug described in http://www.byond.com/forum/post/2611357 - if(dmi_file_path) - hash = md5(rsc_ref) - else //otherwise, we need to do the expensive fcopy() workaround - hash = md5asfile(rsc_ref) - - return list(rsc_ref, hash, "asset.[hash]") - -///given a text string, returns whether it is a valid dmi icons folder path -/proc/is_valid_dmi_file(icon_path) - if(!istext(icon_path) || !length(icon_path)) - return FALSE - - var/is_in_icon_folder = findtextEx(icon_path, "icons/") - var/is_dmi_file = findtextEx(icon_path, ".dmi") - - if(is_in_icon_folder && is_dmi_file) - return TRUE - return FALSE - -/// given an icon object, dmi file path, or atom/image/mutable_appearance, attempts to find and return an associated dmi file path. -/// a weird quirk about dm is that /icon objects represent both compile-time or dynamic icons in the rsc, -/// but stringifying rsc references returns a dmi file path -/// ONLY if that icon represents a completely unchanged dmi file from when the game was compiled. -/// so if the given object is associated with an icon that was in the rsc when the game was compiled, this returns a path. otherwise it returns "" -/proc/get_icon_dmi_path(icon/icon) - /// the dmi file path we attempt to return if the given object argument is associated with a stringifiable icon - /// if successful, this looks like "icons/path/to/dmi_file.dmi" - var/icon_path = "" - - if(isatom(icon) || istype(icon, /image) || istype(icon, /mutable_appearance)) - var/atom/atom_icon = icon - icon = atom_icon.icon - //atom icons compiled in from 'icons/path/to/dmi_file.dmi' are weird and not really icon objects that you generate with icon(). - //if theyre unchanged dmi's then they're stringifiable to "icons/path/to/dmi_file.dmi" - - if(isicon(icon) && isfile(icon)) - //icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects, - ///but they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want - var/icon_ref = "\ref[icon]" - var/locate_icon_string = "[locate(icon_ref)]" - - icon_path = locate_icon_string - - else if(isicon(icon) && "[icon]" == "/icon") - // icon objects generated from icon() at runtime are icons, but they ARENT files themselves, they represent icon files. - // if the files they represent are compile time dmi files in the rsc, then - // the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi" - var/rsc_ref = fcopy_rsc(icon) - - var/icon_ref = "\ref[rsc_ref]" - - var/icon_path_string = "[locate(icon_ref)]" - - icon_path = icon_path_string - - else if(istext(icon)) - var/rsc_ref = fcopy_rsc(icon) - //if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path - - var/rsc_ref_ref = "\ref[rsc_ref]" - var/rsc_ref_string = "[locate(rsc_ref_ref)]" - - icon_path = rsc_ref_string - - if(is_valid_dmi_file(icon_path)) - return icon_path - - return FALSE - -/** - * generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target. - * Arguments: - * * thing - either a /icon object, or an object that has an appearance (atom, image, mutable_appearance). - * * target - either a reference to or a list of references to /client's or mobs with clients - * * icon_state - string to force a particular icon_state for the icon to be used - * * dir - dir number to force a particular direction for the icon to be used - * * frame - what frame of the icon_state's animation for the icon being used - * * moving - whether or not to use a moving state for the given icon - * * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players - * * extra_clases - string of extra css classes to use when returning the icon string - */ -/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) - if (!thing) - return - //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) - //return - - var/key - var/icon/icon2collapse = thing - - if (!target) - return - if (target == world) - target = GLOB.clients - - var/list/targets - if (!islist(target)) - targets = list(target) - else - targets = target - if(!length(targets)) - return - - //check if the given object is associated with a dmi file in the icons folder. if it is then we dont need to do a lot of work - //for asset generation to get around byond limitations - var/icon_path = get_icon_dmi_path(thing) - - if (!isicon(icon2collapse)) - if (isfile(thing)) //special snowflake - //var/name = SANITIZE_FILENAME("[generate_asset_name(thing)].png") - var/name = "[generate_asset_name(thing)].png" - if (!SSassets.cache[name]) - register_asset(name, thing) - for (var/thing2 in targets) - send_asset(thing2, name) - if(sourceonly) - return get_asset_url(name) - return "" - - //its either an atom, image, or mutable_appearance, we want its icon var - icon2collapse = thing.icon - - if (isnull(icon_state)) - icon_state = thing.icon_state - //Despite casting to atom, this code path supports mutable appearances, so let's be nice to them - //if(isnull(icon_state) || (isatom(thing) && thing.flags_1 & HTML_USE_INITAL_ICON_1)) - if(isnull(icon_state) || isatom(thing)) - icon_state = initial(thing.icon_state) - if (isnull(dir)) - dir = initial(thing.dir) - - if (isnull(dir)) - dir = thing.dir - - if (ishuman(thing)) // Shitty workaround for a BYOND issue. - var/icon/temp = icon2collapse - icon2collapse = icon() - icon2collapse.Insert(temp, dir = SOUTH) - dir = SOUTH - else - if (isnull(dir)) - dir = SOUTH - if (isnull(icon_state)) - icon_state = "" - - icon2collapse = icon(icon2collapse, icon_state, dir, frame, moving) - - var/list/name_and_ref = generate_and_hash_rsc_file(icon2collapse, icon_path)//pretend that tuples exist - - var/rsc_ref = name_and_ref[1] //weird object thats not even readable to the debugger, represents a reference to the icons rsc entry - var/file_hash = name_and_ref[2] - key = "[name_and_ref[3]].png" - - if(!SSassets.cache[key]) - register_asset(key, rsc_ref, file_hash, icon_path) - for (var/client_target in targets) - send_asset(client_target, key) - if(sourceonly) - return get_asset_url(key) - return "" - -//Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs. -/proc/costly_icon2html(thing, target, sourceonly = FALSE) - if (!thing) - return - //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) - //return - - if (isicon(thing)) - return icon2html(thing, target) - - var/icon/I = getFlatIcon(thing) - return icon2html(I, target, sourceonly = sourceonly) +#define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87)) + +/icon/proc/MakeLying() + var/icon/I = new(src,dir=SOUTH) + I.BecomeLying() + return I + +/icon/proc/BecomeLying() + Turn(90) + Shift(SOUTH,6) + Shift(EAST,1) + +// Multiply all alpha values by this float +/icon/proc/ChangeOpacity(opacity = 1.0) + MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0) + +// Convert to grayscale +/icon/proc/GrayScale() + MapColors(0.3,0.3,0.3, 0.59,0.59,0.59, 0.11,0.11,0.11, 0,0,0) + +/icon/proc/ColorTone(tone) + GrayScale() + + var/list/TONE = rgb2num(tone) + var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1) + + var/icon/upper = (255-gray) ? new(src) : null + + if(gray) + MapColors(255/gray,0,0, 0,255/gray,0, 0,0,255/gray, 0,0,0) + Blend(tone, ICON_MULTIPLY) + else SetIntensity(0) + if(255-gray) + upper.Blend(rgb(gray,gray,gray), ICON_SUBTRACT) + upper.MapColors((255-TONE[1])/(255-gray),0,0,0, 0,(255-TONE[2])/(255-gray),0,0, 0,0,(255-TONE[3])/(255-gray),0, 0,0,0,0, 0,0,0,1) + Blend(upper, ICON_ADD) + +// Take the minimum color of two icons; combine transparency as if blending with ICON_ADD +/icon/proc/MinColors(icon) + var/icon/I = new(src) + I.Opaque() + I.Blend(icon, ICON_SUBTRACT) + Blend(I, ICON_SUBTRACT) + +// Take the maximum color of two icons; combine opacity as if blending with ICON_OR +/icon/proc/MaxColors(icon) + var/icon/I + if(isicon(icon)) + I = new(icon) + else + // solid color + I = new(src) + I.Blend("#000000", ICON_OVERLAY) + I.SwapColor("#000000", null) + I.Blend(icon, ICON_OVERLAY) + var/icon/J = new(src) + J.Opaque() + I.Blend(J, ICON_SUBTRACT) + Blend(I, ICON_OR) + +// make this icon fully opaque--transparent pixels become black +/icon/proc/Opaque(background = "#000000") + SwapColor(null, background) + MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0, 0,0,0,1) + +// Change a grayscale icon into a white icon where the original color becomes the alpha +// I.e., black -> transparent, gray -> translucent white, white -> solid white +/icon/proc/BecomeAlphaMask() + SwapColor(null, "#000000ff") // don't let transparent become gray + MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) + +/icon/proc/UseAlphaMask(mask) + Opaque() + AddAlphaMask(mask) + +/icon/proc/AddAlphaMask(mask) + var/icon/M = new(mask) + M.Blend("#ffffff", ICON_SUBTRACT) + // apply mask + Blend(M, ICON_ADD) + +/proc/BlendRGB(rgb1, rgb2, amount) + var/list/RGB1 = rgb2num(rgb1) + var/list/RGB2 = rgb2num(rgb2) + + // add missing alpha if needed + if(RGB1.len < RGB2.len) RGB1 += 255 + else if(RGB2.len < RGB1.len) RGB2 += 255 + var/usealpha = RGB1.len > 3 + + var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) + var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) + var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) + var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null + + return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) + +// Ported from /tg/station +// Creates a single icon from a given /atom or /image. Only the first argument is required. +/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + //Define... defines. + var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") + + #define BLANK icon(flat_template) + #define SET_SELF(SETVAR) do { \ + var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ + if(A.alpha<255) { \ + SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ + } \ + if(A.color) { \ + if(islist(A.color)){ \ + SELF_ICON.MapColors(arglist(A.color))} \ + else{ \ + SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ + } \ + ##SETVAR=SELF_ICON;\ + } while (0) + #define INDEX_X_LOW 1 + #define INDEX_X_HIGH 2 + #define INDEX_Y_LOW 3 + #define INDEX_Y_HIGH 4 + + #define flatX1 flat_size[INDEX_X_LOW] + #define flatX2 flat_size[INDEX_X_HIGH] + #define flatY1 flat_size[INDEX_Y_LOW] + #define flatY2 flat_size[INDEX_Y_HIGH] + #define addX1 add_size[INDEX_X_LOW] + #define addX2 add_size[INDEX_X_HIGH] + #define addY1 add_size[INDEX_Y_LOW] + #define addY2 add_size[INDEX_Y_HIGH] + + if(!A || A.alpha <= 0) + return BLANK + + var/noIcon = FALSE + if(start) + if(!defdir) + defdir = A.dir + if(!deficon) + deficon = A.icon + if(!defstate) + defstate = A.icon_state + if(!defblend) + defblend = A.blend_mode + + var/curicon = A.icon || deficon + var/curstate = A.icon_state || defstate + + if(!((noIcon = (!curicon)))) + var/curstates = cached_icon_states(curicon) + if(!(curstate in curstates)) + if("" in curstates) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have + + // Use the requested dir or the atom's current dir + curdir = defdir || A.dir + + //Try to remove/optimize this section ASAP, CPU hog. //Slightly mitigated by implementing caching using cached_icon_states + //Determines if there's directionals. + if(!noIcon && curdir != SOUTH) + var/exist = FALSE + var/static/list/checkdirs = list(NORTH, EAST, WEST) + for(var/i in checkdirs) //Not using GLOB for a reason. + if(length(cached_icon_states(icon(curicon, curstate, i)))) + exist = TRUE + break + if(!exist) + base_icon_dir = SOUTH + // + + if(!base_icon_dir) + base_icon_dir = curdir + + ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. + + var/curblend = A.blend_mode || defblend + + if(A.overlays.len || A.underlays.len) + var/icon/flat = BLANK + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed + var/list/layers = list() + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(!noIcon) + copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) + copy.color = A.color + copy.alpha = A.alpha + copy.blend_mode = curblend + layers[copy] = A.layer + + // Loop through the underlays, then overlays, sorting them into the layers list + for(var/process_set in 0 to 1) + var/list/process = process_set? A.overlays : A.underlays + for(var/i in 1 to process.len) + var/image/current = process[i] + if(!current) + continue + if(current.plane != FLOAT_PLANE && current.plane != A.plane) + continue + var/current_layer = current.layer + if(current_layer < 0) + //if(current_layer <= -1000) + //return flat + current_layer = process_set + A.layer + current_layer / 1000 + + for(var/p in 1 to layers.len) + var/image/cmp = layers[p] + if(current_layer < layers[cmp]) + layers.Insert(p, current) + break + layers[current] = current_layer + + //sortTim(layers, GLOBAL_PROC_REF(cmp_image_layer_asc)) + + var/icon/add // Icon of overlay being added + + // Current dimensions of flattened icon + var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) + // Dimensions of overlay being added + var/list/add_size[4] + + for(var/V in layers) + var/image/I = V + if(I.alpha == 0) + continue + + if(I == copy) // 'I' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(I.icon, I.icon_state, base_icon_dir) + else // 'I' is an appearance object. + add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) + if(!add) + continue + // Find the new dimensions of the flat icon to fit the added overlay + add_size = list( + min(flatX1, I.pixel_x+1), + max(flatX2, I.pixel_x+add.Width()), + min(flatY1, I.pixel_y+1), + max(flatY2, I.pixel_y+add.Height()) + ) + + if(flat_size ~! add_size) + // Resize the flattened icon so the new icon fits + flat.Crop( + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 + ) + flat_size = add_size.Copy() + + // Blend the overlay into the flattened icon + flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) + + if(A.color) + if(islist(A.color)) + flat.MapColors(arglist(A.color)) + else + flat.Blend(A.color, ICON_MULTIPLY) + + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + if(no_anim) + //Clean up repeated frames + var/icon/cleaned = new /icon() + cleaned.Insert(flat, "", SOUTH, 1, 0) + . = cleaned + else + . = icon(flat, "", SOUTH) + else //There's no overlays. + if(!noIcon) + SET_SELF(.) + + //Clear defines + #undef flatX1 + #undef flatX2 + #undef flatY1 + #undef flatY2 + #undef addX1 + #undef addX2 + #undef addY1 + #undef addY2 + + #undef INDEX_X_LOW + #undef INDEX_X_HIGH + #undef INDEX_Y_LOW + #undef INDEX_Y_HIGH + + #undef BLANK + #undef SET_SELF + +/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N + var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. + for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. + if(I:layer>A.layer) continue//If layer is greater than what we need, skip it. + var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects. + //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. + alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. + return alpha_mask//And now return the mask. + +//getFlatIcon but generates an icon that can face ALL four directions. The only four. +/proc/getCompoundIcon(atom/A) + var/icon/north = getFlatIcon(A,defdir=NORTH) + var/icon/south = getFlatIcon(A,defdir=SOUTH) + var/icon/east = getFlatIcon(A,defdir=EAST) + var/icon/west = getFlatIcon(A,defdir=WEST) + + //Starts with a blank icon because of byond bugs. + var/icon/full = icon('icons/effects/effects.dmi', "icon_state"="nothing") + + full.Insert(north,dir=NORTH) + full.Insert(south,dir=SOUTH) + full.Insert(east,dir=EAST) + full.Insert(west,dir=WEST) + qdel(north) + qdel(south) + qdel(east) + qdel(west) + return full + +/proc/downloadImage(atom/A, dir) + var/icon/this_icon = getFlatIcon(A,defdir=dir) + + usr << ftp(this_icon,"[A.name].png") + +/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. + var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. + //Now we need to culculate overlays+underlays and add them together to form an image for a mask. + //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. + var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. + opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. + opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. + for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. + var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. + switch(i)//Now to determine offset so the result is somewhat blurred. + if(1) I.pixel_x-- + if(2) I.pixel_x++ + if(3) I.pixel_y-- + if(4) I.pixel_y++ + overlays += I//And finally add the overlay. + +/proc/getHologramIcon(icon/A, safety=1, no_color = FALSE)//If safety is on, a new icon is not created. + var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. + /* VOREStation Removal - For AI Vore effects + if(!no_color) + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + */ //VOREStation Removal End + var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + return flat_icon + +//For photo camera. +/proc/build_composite_icon(atom/A) + var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) + for(var/O in A.overlays) + var/image/I = O + composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) + return composite + +GLOBAL_LIST_EMPTY(icon_state_lists) +/proc/cached_icon_states(var/icon/I) + if(!I) + return list() + var/key = I + var/returnlist = GLOB.icon_state_lists[key] + if(!returnlist) + returnlist = icon_states(I) + if(isfile(I)) // It's something that will stick around + GLOB.icon_state_lists[key] = returnlist + return returnlist + +/proc/expire_states_cache(var/key) + if(GLOB.icon_state_lists[key]) + GLOB.icon_state_lists -= key + return TRUE + return FALSE + +GLOBAL_LIST_EMPTY(cached_examine_icons) +/proc/set_cached_examine_icon(var/atom/A, var/icon/I, var/expiry = 12000) + GLOB.cached_examine_icons[WEAKREF(A)] = I + if(expiry) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(uncache_examine_icon), WEAKREF(A)), expiry, TIMER_UNIQUE) + +/proc/get_cached_examine_icon(var/atom/A) + var/datum/weakref/WR = WEAKREF(A) + return GLOB.cached_examine_icons[WR] + +/proc/uncache_examine_icon(var/datum/weakref/WR) + GLOB.cached_examine_icons -= WR + +/proc/adjust_brightness(var/color, var/value) + if (!color) return "#FFFFFF" + if (!value) return color + + var/list/RGB = rgb2num(color) + RGB[1] = CLAMP(RGB[1]+value,0,255) + RGB[2] = CLAMP(RGB[2]+value,0,255) + RGB[3] = CLAMP(RGB[3]+value,0,255) + return rgb(RGB[1],RGB[2],RGB[3]) + +/proc/sort_atoms_by_layer(var/list/atoms) + // Comb sort icons based on levels + var/list/result = atoms.Copy() + var/gap = result.len + var/swapped = 1 + while (gap > 1 || swapped) + swapped = 0 + if(gap > 1) + gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient + if(gap < 1) + gap = 1 + for(var/i = 1; gap + i <= result.len; i++) + var/atom/l = result[i] //Fucking hate + var/atom/r = result[gap+i] //how lists work here + if(l.layer > r.layer) //no "result[i].layer" for me + result.Swap(i, gap + i) + swapped = 1 + return result + +/proc/gen_hud_image(var/file, var/person, var/state, var/plane) + var/image/img = image(file, person, state) + img.plane = plane //Thanks Byond. + img.layer = MOB_LAYER-0.2 + img.appearance_flags = APPEARANCE_UI + return img + +/** +* Animate a 'halo' around an object. +* +* This proc is not exactly cheap. You'd be well advised to set up many-loops rather than call this super-often. getCompoundIcon is +* mostly to blame for this. If Byond ever implements a way to get something's icon more 'gently' than this, do that instead. +* +* @param A This is the atom to put the halo on +* @param simple_icons If set to TRUE, will just perform a very basic icon and icon_state steal. DO USE when possible. +* @param color This is the color for the halo +* @param anim_duration This decides how fast (or slow) the animation plays +* @param offset Mysterious variable that determines size of the halo's gap from icon +* @param loops How many times the animation loops +* @param grow_to Relative to the size of the icon, how big the halo grows while fading (don't use negatives for inward halos, use < 1) +* @param pixel_scale If you'd like the halo to use pixel scale or the default 'fuzzy' scale +*/ +/proc/animate_aura(var/atom/A, var/simple_icons, var/color = "#00FF22", var/anim_duration = 5, var/offset = 1, var/loops = 1, var/grow_to = 2, var/pixel_scale = FALSE) + ASSERT(A) + + //Take a guess at this, if they didn't set it + if(isnull(simple_icons)) + if(ismob(A)) + simple_icons = FALSE + else + simple_icons = TRUE + + //Get their icon + var/icon/hole + + if(simple_icons) + hole = icon(A.icon, A.icon_state) + else + hole = getCompoundIcon(A) + + hole.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White. + + //Make a bigger version + var/icon/grower = new(hole) + var/orig_width = grower.Width() + var/orig_height = grower.Height() + var/end_width = orig_width+(offset*2) + var/end_height = orig_height+(offset*2) + var/half_diff_width = (end_width-orig_width)*0.5 + var/half_diff_height = (end_height-orig_height)*0.5 + + //Make icon black + grower.SwapColor("#FFFFFF","#000000") //Black. + + //Scale both icons big so we don't have to deal with low-pixel garbage issues + grower.Scale(orig_width*10,orig_height*10) + hole.Scale(orig_width*9,orig_height*9) + + //Blend the hole in + grower.Blend(hole,ICON_OVERLAY, x = ((orig_width*10-orig_width*9)*0.5)+1, y = ((orig_height*10-orig_height*9)*0.5)+1) + + //Swap white to zero alpha + grower.SwapColor("#FFFFFF","#00000000") + + //Color it + grower.SwapColor("#000000",color) + + //Scale it to final height + grower.Scale(end_width,end_height) + + //Flick it onto them + var/image/img = image(grower,A) + if(pixel_scale) + img.appearance_flags |= PIXEL_SCALE + img.pixel_x = half_diff_width*-1 + img.pixel_y = half_diff_height*-1 + flick_overlay_view(img, A, anim_duration*loops, TRUE) + + //Animate it growing + animate(img, alpha = 0, transform = matrix()*grow_to, time = anim_duration, loop = loops) + +/// generates a filename for a given asset. +/// like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension) +/// used so that certain asset files dont have to be hashed twice +/proc/generate_and_hash_rsc_file(file, dmi_file_path) + var/rsc_ref = fcopy_rsc(file) + var/hash + //if we have a valid dmi file path we can trust md5'ing the rsc file because we know it doesnt have the bug described in http://www.byond.com/forum/post/2611357 + if(dmi_file_path) + hash = md5(rsc_ref) + else //otherwise, we need to do the expensive fcopy() workaround + hash = md5asfile(rsc_ref) + + return list(rsc_ref, hash, "asset.[hash]") + +/// Gets a dummy savefile for usage in icon generation. +/// Savefiles generated from this proc will be empty. +/proc/get_dummy_savefile(from_failure = FALSE) + var/static/next_id = 0 + if(next_id++ > 9) + next_id = 0 + var/savefile_path = "tmp/dummy-save-[next_id].sav" + try + if(fexists(savefile_path)) + fdel(savefile_path) + return new /savefile(savefile_path) + catch(var/exception/error) + // if we failed to create a dummy once, try again; maybe someone slept somewhere they shouldnt have + if(from_failure) // this *is* the retry, something fucked up + CRASH("get_dummy_savefile failed to create a dummy savefile: '[error]'") + return get_dummy_savefile(from_failure = TRUE) + +/** + * Converts an icon to base64. Operates by putting the icon in the iconCache savefile, + * exporting it as text, and then parsing the base64 from that. + * (This relies on byond automatically storing icons in savefiles as base64) + */ +/proc/icon2base64(icon/icon) + if (!isicon(icon)) + return FALSE + var/savefile/dummySave = get_dummy_savefile() + WRITE_FILE(dummySave["dummy"], icon) + var/iconData = dummySave.ExportText("dummy") + var/list/partial = splittext(iconData, "{") + return replacetext(copytext_char(partial[2], 3, -5), "\n", "") //if cleanup fails we want to still return the correct base64 + +///given a text string, returns whether it is a valid dmi icons folder path +/proc/is_valid_dmi_file(icon_path) + if(!istext(icon_path) || !length(icon_path)) + return FALSE + + var/is_in_icon_folder = findtextEx(icon_path, "icons/") + var/is_dmi_file = findtextEx(icon_path, ".dmi") + + if(is_in_icon_folder && is_dmi_file) + return TRUE + return FALSE + +/// given an icon object, dmi file path, or atom/image/mutable_appearance, attempts to find and return an associated dmi file path. +/// a weird quirk about dm is that /icon objects represent both compile-time or dynamic icons in the rsc, +/// but stringifying rsc references returns a dmi file path +/// ONLY if that icon represents a completely unchanged dmi file from when the game was compiled. +/// so if the given object is associated with an icon that was in the rsc when the game was compiled, this returns a path. otherwise it returns "" +/proc/get_icon_dmi_path(icon/icon) + /// the dmi file path we attempt to return if the given object argument is associated with a stringifiable icon + /// if successful, this looks like "icons/path/to/dmi_file.dmi" + var/icon_path = "" + + if(isatom(icon) || istype(icon, /image) || istype(icon, /mutable_appearance)) + var/atom/atom_icon = icon + icon = atom_icon.icon + //atom icons compiled in from 'icons/path/to/dmi_file.dmi' are weird and not really icon objects that you generate with icon(). + //if theyre unchanged dmi's then they're stringifiable to "icons/path/to/dmi_file.dmi" + + if(isicon(icon) && isfile(icon)) + //icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects, + ///but they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want + var/icon_ref = "\ref[icon]" + var/locate_icon_string = "[locate(icon_ref)]" + + icon_path = locate_icon_string + + else if(isicon(icon) && "[icon]" == "/icon") + // icon objects generated from icon() at runtime are icons, but they ARENT files themselves, they represent icon files. + // if the files they represent are compile time dmi files in the rsc, then + // the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi" + var/rsc_ref = fcopy_rsc(icon) + + var/icon_ref = "\ref[rsc_ref]" + + var/icon_path_string = "[locate(icon_ref)]" + + icon_path = icon_path_string + + else if(istext(icon)) + var/rsc_ref = fcopy_rsc(icon) + //if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path + + var/rsc_ref_ref = "\ref[rsc_ref]" + var/rsc_ref_string = "[locate(rsc_ref_ref)]" + + icon_path = rsc_ref_string + + if(is_valid_dmi_file(icon_path)) + return icon_path + + return FALSE + +/** + * generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target. + * Arguments: + * * thing - either a /icon object, or an object that has an appearance (atom, image, mutable_appearance). + * * target - either a reference to or a list of references to /client's or mobs with clients + * * icon_state - string to force a particular icon_state for the icon to be used + * * dir - dir number to force a particular direction for the icon to be used + * * frame - what frame of the icon_state's animation for the icon being used + * * moving - whether or not to use a moving state for the given icon + * * sourceonly - if TRUE, only generate the asset and send back the asset url, instead of tags that display the icon to players + * * extra_clases - string of extra css classes to use when returning the icon string + */ +/proc/icon2html(atom/thing, client/target, icon_state, dir = SOUTH, frame = 1, moving = FALSE, sourceonly = FALSE, extra_classes = null) + if (!thing) + return + //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) + //return + + var/key + var/icon/icon2collapse = thing + + if (!target) + return + if (target == world) + target = GLOB.clients + + var/list/targets + if (!islist(target)) + targets = list(target) + else + targets = target + if(!length(targets)) + return + + //check if the given object is associated with a dmi file in the icons folder. if it is then we dont need to do a lot of work + //for asset generation to get around byond limitations + var/icon_path = get_icon_dmi_path(thing) + + if (!isicon(icon2collapse)) + if (isfile(thing)) //special snowflake + //var/name = SANITIZE_FILENAME("[generate_asset_name(thing)].png") + var/name = "[generate_asset_name(thing)].png" + if (!SSassets.cache[name]) + register_asset(name, thing) + for (var/thing2 in targets) + send_asset(thing2, name) + if(sourceonly) + return get_asset_url(name) + return "" + + //its either an atom, image, or mutable_appearance, we want its icon var + icon2collapse = thing.icon + + if (isnull(icon_state)) + icon_state = thing.icon_state + //Despite casting to atom, this code path supports mutable appearances, so let's be nice to them + //if(isnull(icon_state) || (isatom(thing) && thing.flags_1 & HTML_USE_INITAL_ICON_1)) + if(isnull(icon_state) || isatom(thing)) + icon_state = initial(thing.icon_state) + if (isnull(dir)) + dir = initial(thing.dir) + + if (isnull(dir)) + dir = thing.dir + + if (ishuman(thing)) // Shitty workaround for a BYOND issue. + var/icon/temp = icon2collapse + icon2collapse = icon() + icon2collapse.Insert(temp, dir = SOUTH) + dir = SOUTH + else + if (isnull(dir)) + dir = SOUTH + if (isnull(icon_state)) + icon_state = "" + + icon2collapse = icon(icon2collapse, icon_state, dir, frame, moving) + + var/list/name_and_ref = generate_and_hash_rsc_file(icon2collapse, icon_path)//pretend that tuples exist + + var/rsc_ref = name_and_ref[1] //weird object thats not even readable to the debugger, represents a reference to the icons rsc entry + var/file_hash = name_and_ref[2] + key = "[name_and_ref[3]].png" + + if(!SSassets.cache[key]) + register_asset(key, rsc_ref, file_hash, icon_path) + for (var/client_target in targets) + send_asset(client_target, key) + if(sourceonly) + return get_asset_url(key) + return "" + +/proc/icon2base64html(target, var/custom_classes = "") + if (!target) + return + var/static/list/bicon_cache = list() + if (isicon(target)) + var/icon/target_icon = target + var/icon_base64 = icon2base64(target_icon) + + if (target_icon.Height() > world.icon_size || target_icon.Width() > world.icon_size) + var/icon_md5 = md5(icon_base64) + icon_base64 = bicon_cache[icon_md5] + if (!icon_base64) // Doesn't exist yet, make it. + bicon_cache[icon_md5] = icon_base64 = icon2base64(target_icon) + + + return "" + + // Either an atom or somebody fucked up and is gonna get a runtime, which I'm fine with. + var/atom/target_atom = target + var/key = "[istype(target_atom.icon, /icon) ? "[REF(target_atom.icon)]" : target_atom.icon]:[target_atom.icon_state]" + + + if (!bicon_cache[key]) // Doesn't exist, make it. + var/icon/target_icon = icon(target_atom.icon, target_atom.icon_state, SOUTH, 1) + if (ishuman(target)) // Shitty workaround for a BYOND issue. + var/icon/temp = target_icon + target_icon = icon() + target_icon.Insert(temp, dir = SOUTH) + + bicon_cache[key] = icon2base64(target_icon) + + return "" + +//Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs. +/proc/costly_icon2html(thing, target, sourceonly = FALSE) + if (!thing) + return + //if(SSlag_switch.measures[DISABLE_USR_ICON2HTML] && usr && !HAS_TRAIT(usr, TRAIT_BYPASS_MEASURES)) + //return + + if (isicon(thing)) + return icon2html(thing, target) + + var/icon/I = getFlatIcon(thing) + return icon2html(I, target, sourceonly = sourceonly) diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 9e3a7addb8d..d5e9bd841c7 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -1,315 +1,297 @@ -//print an error message to world.log - -//This is an external call, "true" and "false" are how rust parses out booleans -#define WRITE_LOG(log, text) rustg_log_write(log, text, "true") -#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") - -/* For logging round startup. */ -/proc/start_log(log) - WRITE_LOG(log, "START: Starting up [log_path].") - return log - -/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ -/proc/shutdown_logging() - rustg_log_close_all() - -/proc/error(msg) - to_world_log("## ERROR: [msg]") - -#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].") -//print a warning message to world.log -/proc/warning(msg) - to_world_log("## WARNING: [msg]") - -//print a testing-mode debug message to world.log -/proc/testing(msg) - to_world_log("## TESTING: [msg]") - -/proc/log_admin(text) - admin_log.Add(text) - if (config.log_admin) - WRITE_LOG(diary, "ADMIN: [text]") - -/proc/log_adminpm(text, client/source, client/dest) - admin_log.Add(text) - if (config.log_admin) - WRITE_LOG(diary, "ADMINPM: [key_name(source)]->[key_name(dest)]: [html_decode(text)]") - -/proc/log_pray(text, client/source) - admin_log.Add(text) - if (config.log_admin) - WRITE_LOG(diary, "PRAY: [key_name(source)]: [text]") - -/proc/log_debug(text) - if (config.log_debug) - WRITE_LOG(debug_log, "DEBUG: [sanitize(text)]") - - for(var/client/C in GLOB.admins) - if(C.is_preference_enabled(/datum/client_preference/debug/show_debug_logs)) - to_chat(C, "DEBUG: [text]") - -/proc/log_game(text) - if (config.log_game) - WRITE_LOG(diary, "GAME: [text]") - -/proc/log_vote(text) - if (config.log_vote) - WRITE_LOG(diary, "VOTE: [text]") - -/proc/log_access_in(client/new_client) - if (config.log_access) - var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" - WRITE_LOG(diary, "ACCESS IN: [message]") //VOREStation Edit - -/proc/log_access_out(mob/last_mob) - if (config.log_access) - var/message = "[key_name(last_mob)] - IP:[last_mob.lastKnownIP] - CID:Logged Out - BYOND Logged Out" - WRITE_LOG(diary, "ACCESS OUT: [message]") - -/proc/log_say(text, mob/speaker) - if (config.log_say) - WRITE_LOG(diary, "SAY: [speaker.simple_info_line()]: [html_decode(text)]") - - //Log the message to in-game dialogue logs, as well. - if(speaker.client) - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - -/proc/log_ooc(text, client/user) - if (config.log_ooc) - WRITE_LOG(diary, "OOC: [user.simple_info_line()]: [html_decode(text)]") - - GLOB.round_text_log += "([time_stamp()]) ([user]) OOC: - [text]" - -/proc/log_aooc(text, client/user) - if (config.log_ooc) - WRITE_LOG(diary, "AOOC: [user.simple_info_line()]: [html_decode(text)]") - - GLOB.round_text_log += "([time_stamp()]) ([user]) AOOC: - [text]" - -/proc/log_looc(text, client/user) - if (config.log_ooc) - WRITE_LOG(diary, "LOOC: [user.simple_info_line()]: [html_decode(text)]") - - GLOB.round_text_log += "([time_stamp()]) ([user]) LOOC: - [text]" - -/proc/log_whisper(text, mob/speaker) - if (config.log_whisper) - WRITE_LOG(diary, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)]") - - if(speaker.client) - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" - -/proc/log_emote(text, mob/speaker) - if (config.log_emote) - WRITE_LOG(diary, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)]") - - if(speaker.client) - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" - -/proc/log_attack(attacker, defender, message) - if (config.log_attack) - WRITE_LOG(diary, "ATTACK: [attacker] against [defender]: [message]") - -/proc/log_adminsay(text, mob/speaker) - if (config.log_adminchat) - WRITE_LOG(diary, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_modsay(text, mob/speaker) - if (config.log_adminchat) - WRITE_LOG(diary, "MODSAY: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_eventsay(text, mob/speaker) - if (config.log_adminchat) - WRITE_LOG(diary, "EVENTSAY: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_ghostsay(text, mob/speaker) - if (config.log_say) - WRITE_LOG(diary, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)]") - - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" - -/proc/log_ghostemote(text, mob/speaker) - if (config.log_emote) - WRITE_LOG(diary, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)]") - -/proc/log_adminwarn(text) - if (config.log_adminwarn) - WRITE_LOG(diary, "ADMINWARN: [html_decode(text)]") - -/proc/log_pda(text, mob/speaker) - if (config.log_pda) - WRITE_LOG(diary, "PDA: [speaker.simple_info_line()]: [html_decode(text)]") - - speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" - GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" - -/proc/log_to_dd(text) - to_world_log(text) //this comes before the config check because it can't possibly runtime - if(config.log_world_output) - WRITE_LOG(diary, "DD_OUTPUT: [text]") - -/proc/log_error(text) - to_world_log(text) - WRITE_LOG(error_log, "RUNTIME: [text]") - -/proc/log_misc(text) - WRITE_LOG(diary, "MISC: [text]") - -/proc/log_topic(text) - if(Debug2) - WRITE_LOG(diary, "TOPIC: [text]") - -/proc/log_href(text) - // Configs are checked by caller - WRITE_LOG(href_logfile, "HREF: [text]") - -/proc/log_unit_test(text) - to_world_log("## UNIT_TEST: [text]") - -#ifdef REFERENCE_TRACKING_LOG -#define log_reftracker(msg) log_world("## REF SEARCH [msg]") -#else -#define log_reftracker(msg) -#endif - -/proc/log_tgui(user_or_client, text) - if(!text) - stack_trace("Pointless log_tgui message") - return - var/entry = "" - if(!user_or_client) - entry += "no user" - else if(istype(user_or_client, /mob)) - var/mob/user = user_or_client - entry += "[user.ckey] (as [user])" - else if(istype(user_or_client, /client)) - var/client/client = user_or_client - entry += "[client.ckey]" - entry += ":\n[text]" - WRITE_LOG(diary, entry) - -/proc/log_asset(text) - WRITE_LOG(diary, "ASSET: [text]") - -/proc/report_progress(var/progress_message) - admin_notice("[progress_message]", R_DEBUG) - to_world_log(progress_message) - -//pretty print a direction bitflag, can be useful for debugging. -/proc/print_dir(var/dir) - var/list/comps = list() - if(dir & NORTH) comps += "NORTH" - if(dir & SOUTH) comps += "SOUTH" - if(dir & EAST) comps += "EAST" - if(dir & WEST) comps += "WEST" - if(dir & UP) comps += "UP" - if(dir & DOWN) comps += "DOWN" - - return english_list(comps, nothing_text="0", and_text="|", comma_text="|") - -//more or less a logging utility -//Always return "Something/(Something)", even if it's an error message. -/proc/key_name(var/whom, var/include_link = FALSE, var/include_name = TRUE, var/highlight_special_characters = TRUE) - var/mob/M - var/client/C - var/key - - if(!whom) - return "INVALID/INVALID" - if(istype(whom, /client)) - C = whom - M = C.mob - key = C.key - else if(ismob(whom)) - M = whom - C = M.client - key = M.key - else if(istype(whom, /datum/mind)) - var/datum/mind/D = whom - key = D.key - M = D.current - if(D.current) - C = D.current.client - else if(istype(whom, /datum)) - var/datum/D = whom - return "INVALID/([D.type])" - else if(istext(whom)) - return "AUTOMATED/[whom]" //Just give them the text back - else - return "INVALID/INVALID" - - . = "" - - if(key) - if(include_link && C) - . += "" - - if(C && C.holder && C.holder.fakekey) - . += "Administrator" - else - . += key - - if(include_link) - if(C) . += "" - else . += " (DC)" - else - . += "INVALID" - - if(include_name) - var/name = "INVALID" - if(M) - if(M.real_name) - name = M.real_name - else if(M.name) - name = M.name - - if(include_link && is_special_character(M) && highlight_special_characters) - name = "[name]" //Orange - - . += "/([name])" - - return . - -/proc/key_name_admin(var/whom, var/include_name = 1) - return key_name(whom, 1, include_name) - -// Helper procs for building detailed log lines -// -// These procs must not fail under ANY CIRCUMSTANCES! -// - -/datum/proc/log_info_line() - return "[src] ([type])" - -/atom/log_info_line() - . = ..() - var/turf/t = get_turf(src) - if(istype(t)) - return "[.] @ [t.log_info_line()]" - else if(loc) - return "[.] @ ([loc]) (0,0,0) ([loc.type])" - else - return "[.] @ (NULL) (0,0,0) (NULL)" - -/turf/log_info_line() - return "([src]) ([x],[y],[z]) ([type])" - -/mob/log_info_line() - return "[..()] (ckey=[ckey])" - -/proc/log_info_line(var/datum/d) - if(!d) - return "*null*" - if(!istype(d)) - return json_encode(d) - return d.log_info_line() - -/mob/proc/simple_info_line() - return "[key_name(src)] ([x],[y],[z])" - -/client/proc/simple_info_line() - return "[key_name(src)] ([mob.x],[mob.y],[mob.z])" +//print an error message to world.log + +//This is an external call, "true" and "false" are how rust parses out booleans +#define WRITE_LOG(log, text) rustg_log_write(log, text, "true") +#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") + +/* For logging round startup. */ +/proc/start_log(log) + WRITE_LOG(log, "START: Starting up [log_path].") + return log + +/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ +/proc/shutdown_logging() + rustg_log_close_all() + +/proc/error(msg) + to_world_log("## ERROR: [msg]") + +#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].") +//print a warning message to world.log +/proc/warning(msg) + to_world_log("## WARNING: [msg]") + +//print a testing-mode debug message to world.log +/proc/testing(msg) + to_world_log("## TESTING: [msg]") + +/proc/log_admin(text) + admin_log.Add(text) + if (config.log_admin) + WRITE_LOG(diary, "ADMIN: [text]") + +/proc/log_adminpm(text, client/source, client/dest) + admin_log.Add(text) + if (config.log_admin) + WRITE_LOG(diary, "ADMINPM: [key_name(source)]->[key_name(dest)]: [html_decode(text)]") + +/proc/log_pray(text, client/source) + admin_log.Add(text) + if (config.log_admin) + WRITE_LOG(diary, "PRAY: [key_name(source)]: [text]") + +/proc/log_debug(text) + if (config.log_debug) + WRITE_LOG(debug_log, "DEBUG: [sanitize(text)]") + + for(var/client/C in GLOB.admins) + if(C.is_preference_enabled(/datum/client_preference/debug/show_debug_logs)) + to_chat(C, + type = MESSAGE_TYPE_DEBUG, + html = "DEBUG: [text]") + +/proc/log_game(text) + if (config.log_game) + WRITE_LOG(diary, "GAME: [text]") + +/proc/log_vote(text) + if (config.log_vote) + WRITE_LOG(diary, "VOTE: [text]") + +/proc/log_access_in(client/new_client) + if (config.log_access) + var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" + WRITE_LOG(diary, "ACCESS IN: [message]") //VOREStation Edit + +/proc/log_access_out(mob/last_mob) + if (config.log_access) + var/message = "[key_name(last_mob)] - IP:[last_mob.lastKnownIP] - CID:Logged Out - BYOND Logged Out" + WRITE_LOG(diary, "ACCESS OUT: [message]") + +/proc/log_say(text, mob/speaker) + if (config.log_say) + WRITE_LOG(diary, "SAY: [speaker.simple_info_line()]: [html_decode(text)]") + + //Log the message to in-game dialogue logs, as well. + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + +/proc/log_ooc(text, client/user) + if (config.log_ooc) + WRITE_LOG(diary, "OOC: [user.simple_info_line()]: [html_decode(text)]") + + GLOB.round_text_log += "([time_stamp()]) ([user]) OOC: - [text]" + +/proc/log_aooc(text, client/user) + if (config.log_ooc) + WRITE_LOG(diary, "AOOC: [user.simple_info_line()]: [html_decode(text)]") + + GLOB.round_text_log += "([time_stamp()]) ([user]) AOOC: - [text]" + +/proc/log_looc(text, client/user) + if (config.log_ooc) + WRITE_LOG(diary, "LOOC: [user.simple_info_line()]: [html_decode(text)]") + + GLOB.round_text_log += "([time_stamp()]) ([user]) LOOC: - [text]" + +/proc/log_whisper(text, mob/speaker) + if (config.log_whisper) + WRITE_LOG(diary, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)]") + + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) SAY: - [text]" + +/proc/log_emote(text, mob/speaker) + if (config.log_emote) + WRITE_LOG(diary, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)]") + + if(speaker.client) + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) EMOTE: - [text]" + +/proc/log_attack(attacker, defender, message) + if (config.log_attack) + WRITE_LOG(diary, "ATTACK: [attacker] against [defender]: [message]") + +/proc/log_adminsay(text, mob/speaker) + if (config.log_adminchat) + WRITE_LOG(diary, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_modsay(text, mob/speaker) + if (config.log_adminchat) + WRITE_LOG(diary, "MODSAY: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_eventsay(text, mob/speaker) + if (config.log_adminchat) + WRITE_LOG(diary, "EVENTSAY: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_ghostsay(text, mob/speaker) + if (config.log_say) + WRITE_LOG(diary, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)]") + + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) DEADSAY: - [text]" + +/proc/log_ghostemote(text, mob/speaker) + if (config.log_emote) + WRITE_LOG(diary, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)]") + +/proc/log_adminwarn(text) + if (config.log_adminwarn) + WRITE_LOG(diary, "ADMINWARN: [html_decode(text)]") + +/proc/log_pda(text, mob/speaker) + if (config.log_pda) + WRITE_LOG(diary, "PDA: [speaker.simple_info_line()]: [html_decode(text)]") + + speaker.dialogue_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" + GLOB.round_text_log += "([time_stamp()]) ([speaker]/[speaker.client]) MSG: - [text]" + +/proc/log_to_dd(text) + to_world_log(text) //this comes before the config check because it can't possibly runtime + if(config.log_world_output) + WRITE_LOG(diary, "DD_OUTPUT: [text]") + +/proc/log_error(text) + to_world_log(text) + WRITE_LOG(error_log, "RUNTIME: [text]") + +/proc/log_misc(text) + WRITE_LOG(diary, "MISC: [text]") + +/proc/log_topic(text) + if(Debug2) + WRITE_LOG(diary, "TOPIC: [text]") + +/proc/log_unit_test(text) + to_world_log("## UNIT_TEST: [text]") + +#ifdef REFERENCE_TRACKING_LOG +#define log_reftracker(msg) log_world("## REF SEARCH [msg]") +#else +#define log_reftracker(msg) +#endif + +/proc/log_asset(text) + WRITE_LOG(diary, "ASSET: [text]") + +/proc/report_progress(var/progress_message) + admin_notice("[progress_message]", R_DEBUG) + to_world_log(progress_message) + +//pretty print a direction bitflag, can be useful for debugging. +/proc/print_dir(var/dir) + var/list/comps = list() + if(dir & NORTH) comps += "NORTH" + if(dir & SOUTH) comps += "SOUTH" + if(dir & EAST) comps += "EAST" + if(dir & WEST) comps += "WEST" + if(dir & UP) comps += "UP" + if(dir & DOWN) comps += "DOWN" + + return english_list(comps, nothing_text="0", and_text="|", comma_text="|") + +//more or less a logging utility +//Always return "Something/(Something)", even if it's an error message. +/proc/key_name(var/whom, var/include_link = FALSE, var/include_name = TRUE, var/highlight_special_characters = TRUE) + var/mob/M + var/client/C + var/key + + if(!whom) + return "INVALID/INVALID" + if(istype(whom, /client)) + C = whom + M = C.mob + key = C.key + else if(ismob(whom)) + M = whom + C = M.client + key = M.key + else if(istype(whom, /datum/mind)) + var/datum/mind/D = whom + key = D.key + M = D.current + if(D.current) + C = D.current.client + else if(istype(whom, /datum)) + var/datum/D = whom + return "INVALID/([D.type])" + else if(istext(whom)) + return "AUTOMATED/[whom]" //Just give them the text back + else + return "INVALID/INVALID" + + . = "" + + if(key) + if(include_link && C) + . += "" + + if(C && C.holder && C.holder.fakekey) + . += "Administrator" + else + . += key + + if(include_link) + if(C) . += "" + else . += " (DC)" + else + . += "INVALID" + + if(include_name) + var/name = "INVALID" + if(M) + if(M.real_name) + name = M.real_name + else if(M.name) + name = M.name + + if(include_link && is_special_character(M) && highlight_special_characters) + name = "[name]" //Orange + + . += "/([name])" + + return . + +/proc/key_name_admin(var/whom, var/include_name = 1) + return key_name(whom, 1, include_name) + +// Helper procs for building detailed log lines +// +// These procs must not fail under ANY CIRCUMSTANCES! +// + +/datum/proc/log_info_line() + return "[src] ([type])" + +/atom/log_info_line() + . = ..() + var/turf/t = get_turf(src) + if(istype(t)) + return "[.] @ [t.log_info_line()]" + else if(loc) + return "[.] @ ([loc]) (0,0,0) ([loc.type])" + else + return "[.] @ (NULL) (0,0,0) (NULL)" + +/turf/log_info_line() + return "([src]) ([x],[y],[z]) ([type])" + +/mob/log_info_line() + return "[..()] (ckey=[ckey])" + +/proc/log_info_line(var/datum/d) + if(!d) + return "*null*" + if(!istype(d)) + return json_encode(d) + return d.log_info_line() + +/mob/proc/simple_info_line() + return "[key_name(src)] ([x],[y],[z])" + +/client/proc/simple_info_line() + return "[key_name(src)] ([mob.x],[mob.y],[mob.z])" diff --git a/code/_helpers/logging/ui.dm b/code/_helpers/logging/ui.dm new file mode 100644 index 00000000000..ed6a0b6588f --- /dev/null +++ b/code/_helpers/logging/ui.dm @@ -0,0 +1,36 @@ +/proc/log_href(text) + //WRITE_LOG(GLOB.world_href_log, "HREF: [text]") + WRITE_LOG(href_logfile, "HREF: [text]") + +/** + * Appends a tgui-related log entry. All arguments are optional. + */ +/proc/log_tgui(user, message, context, + datum/tgui_window/window, + datum/src_object) + var/entry = "" + // Insert user info + if(!user) + entry += "" + else if(istype(user, /mob)) + var/mob/mob = user + entry += "[mob.ckey] (as [mob] at [mob.x],[mob.y],[mob.z])" + else if(istype(user, /client)) + var/client/client = user + entry += "[client.ckey]" + // Insert context + if(context) + entry += " in [context]" + else if(window) + entry += " in [window.id]" + // Resolve src_object + if(!src_object && window?.locked_by) + src_object = window.locked_by.src_object + // Insert src_object info + if(src_object) + entry += "\nUsing: [src_object.type] [REF(src_object)]" + // Insert message + if(message) + entry += "\n[message]" + //WRITE_LOG(GLOB.tgui_log, entry) + WRITE_LOG(diary, entry) diff --git a/code/_helpers/matrices.dm b/code/_helpers/matrices.dm index 88bec71bc27..e2b41b2f5b0 100644 --- a/code/_helpers/matrices.dm +++ b/code/_helpers/matrices.dm @@ -1,131 +1,131 @@ -/matrix/proc/TurnTo(old_angle, new_angle) - . = new_angle - old_angle - Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT - - -/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3) - if(!segments) - return - //VOREStation Addition Start - if(speed == 0) - loops = 0 - if(speed < 0) - speed = speed * -1 - clockwise = 0 - //VOREStation Addition End - var/segment = 360/segments - if(!clockwise) - segment = -segment - var/list/matrices = list() - for(var/i in 1 to segments-1) - var/matrix/M = matrix(transform) - M.Turn(segment*i) - matrices += M - var/matrix/last = matrix(transform) - matrices += last - - speed /= segments - - animate(src, transform = matrices[1], time = speed, loops) - for(var/i in 2 to segments) //2 because 1 is covered above - animate(transform = matrices[i], time = speed) - //doesn't have an object argument because this is "Stacking" with the animate call above - //3 billion% intentional - -//The X pixel offset of this matrix -/matrix/proc/get_x_shift() - . = c - -//The Y pixel offset of this matrix -/matrix/proc/get_y_shift() - . = f -// Color matrices: - -//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1. -#define LUMR 0.2126 -#define LUMG 0.7152 -#define LUMB 0.0722 - -//Still need color matrix addition, negation, and multiplication. - -//Returns an identity color matrix which does nothing -/proc/color_identity() - return list(1,0,0, 0,1,0, 0,0,1) - -//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites -//TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone) -/proc/color_rotation(angle) - if(angle == 0) - return color_identity() - angle = CLAMP(angle, -180, 180) - var/cos = cos(angle) - var/sin = sin(angle) - - var/constA = 0.143 - var/constB = 0.140 - var/constC = -0.283 - return list( - LUMR + cos * (1-LUMR) + sin * -LUMR, LUMR + cos * -LUMR + sin * constA, LUMR + cos * -LUMR + sin * -(1-LUMR), - LUMG + cos * -LUMG + sin * -LUMG, LUMG + cos * (1-LUMG) + sin * constB, LUMG + cos * -LUMG + sin * LUMG, - LUMB + cos * -LUMB + sin * (1-LUMB), LUMB + cos * -LUMB + sin * constC, LUMB + cos * (1-LUMB) + sin * LUMB - ) - -//Makes everything brighter or darker without regard to existing color or brightness -/proc/color_brightness(power) - power = CLAMP(power, -255, 255) - power = power/255 - - return list(1,0,0, 0,1,0, 0,0,1, power,power,power) - -/var/list/delta_index = list( - 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, - 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, - 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, - 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, - 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, - 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, - 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, - 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, - 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, - 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, - 10.0) - -//Exxagerates or removes brightness -/proc/color_contrast(value) - value = CLAMP(value, -100, 100) - if(value == 0) - return color_identity() - - var/x = 0 - if (value < 0) - x = 127 + value / 100 * 127; - else - x = value % 1 - if(x == 0) - x = delta_index[value] - else - x = delta_index[value] * (1-x) + delta_index[value+1] * x//use linear interpolation for more granularity. - x = x * 127 + 127 - - var/mult = x / 127 - var/add = 0.5 * (127-x) / 255 - return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add) - -//Exxagerates or removes colors -/proc/color_saturation(value as num) - if(value == 0) - return color_identity() - value = CLAMP(value, -100, 100) - if(value > 0) - value *= 3 - var/x = 1 + value / 100 - var/inv = 1 - x - var/R = LUMR * inv - var/G = LUMG * inv - var/B = LUMB * inv - - return list(R + x,R,R, G,G + x,G, B,B,B + x) - -#undef LUMR -#undef LUMG +/matrix/proc/TurnTo(old_angle, new_angle) + . = new_angle - old_angle + Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT + + +/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3) + if(!segments) + return + //VOREStation Addition Start + if(speed == 0) + loops = 0 + if(speed < 0) + speed = speed * -1 + clockwise = 0 + //VOREStation Addition End + var/segment = 360/segments + if(!clockwise) + segment = -segment + var/list/matrices = list() + for(var/i in 1 to segments-1) + var/matrix/M = matrix(transform) + M.Turn(segment*i) + matrices += M + var/matrix/last = matrix(transform) + matrices += last + + speed /= segments + + animate(src, transform = matrices[1], time = speed, loops) + for(var/i in 2 to segments) //2 because 1 is covered above + animate(transform = matrices[i], time = speed) + //doesn't have an object argument because this is "Stacking" with the animate call above + //3 billion% intentional + +//The X pixel offset of this matrix +/matrix/proc/get_x_shift() + . = c + +//The Y pixel offset of this matrix +/matrix/proc/get_y_shift() + . = f +// Color matrices: + +//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1. +#define LUMR 0.2126 +#define LUMG 0.7152 +#define LUMB 0.0722 + +//Still need color matrix addition, negation, and multiplication. + +//Returns an identity color matrix which does nothing +/proc/color_identity() + return list(1,0,0, 0,1,0, 0,0,1) + +//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites +//TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone) +/proc/color_rotation(angle) + if(angle == 0) + return color_identity() + angle = CLAMP(angle, -180, 180) + var/cos = cos(angle) + var/sin = sin(angle) + + var/constA = 0.143 + var/constB = 0.140 + var/constC = -0.283 + return list( + LUMR + cos * (1-LUMR) + sin * -LUMR, LUMR + cos * -LUMR + sin * constA, LUMR + cos * -LUMR + sin * -(1-LUMR), + LUMG + cos * -LUMG + sin * -LUMG, LUMG + cos * (1-LUMG) + sin * constB, LUMG + cos * -LUMG + sin * LUMG, + LUMB + cos * -LUMB + sin * (1-LUMB), LUMB + cos * -LUMB + sin * constC, LUMB + cos * (1-LUMB) + sin * LUMB + ) + +//Makes everything brighter or darker without regard to existing color or brightness +/proc/color_brightness(power) + power = CLAMP(power, -255, 255) + power = power/255 + + return list(1,0,0, 0,1,0, 0,0,1, power,power,power) + +/var/list/delta_index = list( + 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, + 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, + 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, + 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, + 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, + 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, + 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, + 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, + 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, + 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, + 10.0) + +//Exxagerates or removes brightness +/proc/color_contrast(value) + value = CLAMP(value, -100, 100) + if(value == 0) + return color_identity() + + var/x = 0 + if (value < 0) + x = 127 + value / 100 * 127; + else + x = value % 1 + if(x == 0) + x = delta_index[value] + else + x = delta_index[value] * (1-x) + delta_index[value+1] * x//use linear interpolation for more granularity. + x = x * 127 + 127 + + var/mult = x / 127 + var/add = 0.5 * (127-x) / 255 + return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add) + +//Exxagerates or removes colors +/proc/color_saturation(value as num) + if(value == 0) + return color_identity() + value = CLAMP(value, -100, 100) + if(value > 0) + value *= 3 + var/x = 1 + value / 100 + var/inv = 1 - x + var/R = LUMR * inv + var/G = LUMG * inv + var/B = LUMB * inv + + return list(R + x,R,R, G,G + x,G, B,B,B + x) + +#undef LUMR +#undef LUMG #undef LUMB \ No newline at end of file diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 6cd91a852bd..dd2dde46825 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -1,317 +1,323 @@ -/proc/random_hair_style(gender, species = SPECIES_HUMAN) - var/h_style = "Bald" - - var/list/valid_hairstyles = list() - for(var/hairstyle in hair_styles_list) - var/datum/sprite_accessory/S = hair_styles_list[hairstyle] - if(gender == MALE && S.gender == FEMALE) - continue - if(gender == FEMALE && S.gender == MALE) - continue - if( !(species in S.species_allowed)) - continue - valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - - return h_style - -/proc/random_facial_hair_style(gender, species = SPECIES_HUMAN) - var/f_style = "Shaved" - - var/list/valid_facialhairstyles = list() - for(var/facialhairstyle in facial_hair_styles_list) - var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] - if(gender == MALE && S.gender == FEMALE) - continue - if(gender == FEMALE && S.gender == MALE) - continue - if( !(species in S.species_allowed)) - continue - - valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle] - - if(valid_facialhairstyles.len) - f_style = pick(valid_facialhairstyles) - - return f_style - -/proc/sanitize_name(name, species = SPECIES_HUMAN, robot = 0) - var/datum/species/current_species - if(species) - current_species = GLOB.all_species[species] - - return current_species ? current_species.sanitize_name(name, robot) : sanitizeName(name, MAX_NAME_LEN, robot) - -/proc/random_name(gender, species = SPECIES_HUMAN) - - var/datum/species/current_species - if(species) - current_species = GLOB.all_species[species] - - if(!current_species || current_species.name_language == null) - if(gender==FEMALE) - return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) - else - return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) - else - return current_species.get_random_name(gender) - -/proc/random_skin_tone() - switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) - if("caucasian") . = -10 - if("afroamerican") . = -115 - if("african") . = -165 - if("latino") . = -55 - if("albino") . = 34 - else . = rand(-185,34) - return min(max( .+rand(-25, 25), -185),34) - -/proc/skintone2racedescription(tone) - switch (tone) - if(30 to INFINITY) return "albino" - if(20 to 30) return "pale" - if(5 to 15) return "light skinned" - if(-10 to 5) return "white" - if(-25 to -10) return "tan" - if(-45 to -25) return "darker skinned" - if(-65 to -45) return "brown" - if(-INFINITY to -65) return "black" - else return "unknown" - -/proc/age2agedescription(age) - switch(age) - if(0 to 1) return "infant" - if(1 to 3) return "toddler" - if(3 to 13) return "child" - if(13 to 19) return "teenager" - if(19 to 30) return "young adult" - if(30 to 45) return "adult" - if(45 to 60) return "middle-aged" - if(60 to 70) return "aging" - if(70 to INFINITY) return "elderly" - else return "unknown" - -/proc/RoundHealth(health) - var/list/icon_states = cached_icon_states(ingame_hud_med) - for(var/icon_state in icon_states) - if(health >= text2num(icon_state)) - return icon_state - return icon_states[icon_states.len] // If we had no match, return the last element - -/* -Proc for attack log creation, because really why not -1 argument is the actor -2 argument is the target of action -3 is the description of action(like punched, throwed, or any other verb) -4 should it make adminlog note or not -5 is the tool with which the action was made(usually item) 5 and 6 are very similar(5 have "by " before it, that it) and are separated just to keep things in a bit more in order -6 is additional information, anything that needs to be added -*/ - -/proc/add_attack_logs(mob/user, mob/target, what_done, var/admin_notify = TRUE) - if(islist(target)) //Multi-victim adding - var/list/targets = target - for(var/mob/M in targets) - add_attack_logs(user,M,what_done,admin_notify) - return - - var/user_str = key_name(user) - var/target_str = key_name(target) - - if(ismob(user)) - user.attack_log += text("\[[time_stamp()]\] Attacked [target_str]: [what_done]") - if(ismob(target)) - target.attack_log += text("\[[time_stamp()]\] Attacked by [user_str]: [what_done]") - log_attack(user_str,target_str,what_done) - if(admin_notify) - msg_admin_attack("[key_name_admin(user)] vs [target_str]: [what_done]") - -//checks whether this item is a module of the robot it is located in. -/proc/is_robot_module(var/obj/item/thing) - if (!thing || !istype(thing.loc, /mob/living/silicon/robot)) - return 0 - var/mob/living/silicon/robot/R = thing.loc - return (thing in R.module.modules) - -/proc/get_exposed_defense_zone(var/atom/movable/target) - var/obj/item/weapon/grab/G = locate() in target - if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. - return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) - else - return pick(BP_TORSO, BP_GROIN) - -/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = FALSE, progress = TRUE, ignore_movement = FALSE, exclusive = FALSE) - if(!user || !target) - return FALSE - if(!time) - return TRUE //Done! - if(user.status_flags & DOING_TASK) - to_chat(user, "You're in the middle of doing something else already.") - return FALSE //Performing an exclusive do_after or do_mob already - if(target?.flags & IS_BUSY) - to_chat(user, "Someone is already doing something with \the [target].") - return FALSE - var/user_loc = user.loc - var/target_loc = target.loc - - var/holding = user.get_active_hand() - var/datum/progressbar/progbar - if (progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags |= DOING_TASK - if(target && exclusive & TASK_TARGET_EXCLUSIVE) - target.flags |= IS_BUSY - - . = TRUE - while (world.time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime) - if(!user || !target) - . = FALSE - break - if(uninterruptible) - continue - - if(!user || user.incapacitated()) - . = FALSE - break - - if(user.loc != user_loc && !ignore_movement) - . = FALSE - break - - if(target.loc != target_loc && !ignore_movement) - . = FALSE - break - - if(user.get_active_hand() != holding) - . = FALSE - break - - if(target_zone && user.zone_sel.selecting != target_zone) - . = FALSE - break - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags &= ~DOING_TASK - if(exclusive & TASK_TARGET_EXCLUSIVE) - target?.status_flags &= ~IS_BUSY - - if (progbar) - qdel(progbar) - -/proc/do_after(mob/user, delay, atom/target = null, needhand = TRUE, progress = TRUE, incapacitation_flags = INCAPACITATION_DEFAULT, ignore_movement = FALSE, max_distance = null, exclusive = FALSE) - if(!user) - return FALSE - if(!delay) - return TRUE //Okay. Done. - if(user.status_flags & DOING_TASK) - to_chat(user, "You're in the middle of doing something else already.") - return FALSE //Performing an exclusive do_after or do_mob already - if(target?.flags & IS_BUSY) - to_chat(user, "Someone is already doing something with \the [target].") - return FALSE - - var/atom/target_loc = null - if(target) - target_loc = target.loc - - var/atom/original_loc = user.loc - - var/obj/mecha/M = null - - if(istype(user.loc, /obj/mecha)) - original_loc = get_turf(original_loc) - M = user.loc - - var/holding = user.get_active_hand() - - var/datum/progressbar/progbar - if (progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags |= DOING_TASK - - if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) - target.flags |= IS_BUSY - - . = TRUE - while (world.time < endtime) - stoplag(1) - if(progress) - progbar.update(world.time - starttime) - - if(!user || user.incapacitated(incapacitation_flags)) - . = FALSE - break - - if(M) - if(user.loc != M || (M.loc != original_loc && !ignore_movement)) // Mech coooooode. - . = FALSE - break - - else if(user.loc != original_loc && !ignore_movement) - . = FALSE - break - - if(target_loc && (QDELETED(target))) - . = FALSE - break - - if(target && target_loc != target.loc && !ignore_movement) - . = FALSE - break - - if(needhand) - if(user.get_active_hand() != holding) - . = FALSE - break - - if(max_distance && target && get_dist(user, target) > max_distance) - . = FALSE - break - - if(exclusive & TASK_USER_EXCLUSIVE) - user.status_flags &= ~DOING_TASK - if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) - target.flags &= ~IS_BUSY - - if(progbar) - qdel(progbar) - -/atom/proc/living_mobs(var/range = world.view) - var/list/viewers = oviewers(src,range) - var/list/living = list() - for(var/mob/living/L in viewers) - living += L - - return living - -/atom/proc/human_mobs(var/range = world.view) - var/list/viewers = oviewers(src,range) - var/list/humans = list() - for(var/mob/living/carbon/human/H in viewers) - humans += H - - return humans - -/proc/cached_character_icon(var/mob/desired) - var/cachekey = "\ref[desired][desired.real_name]" - - if(cached_character_icons[cachekey]) - . = cached_character_icons[cachekey] - else - . = getCompoundIcon(desired) - cached_character_icons[cachekey] = . +/proc/random_hair_style(gender, species = SPECIES_HUMAN) + var/h_style = "Bald" + + var/list/valid_hairstyles = list() + for(var/hairstyle in hair_styles_list) + var/datum/sprite_accessory/S = hair_styles_list[hairstyle] + if(gender == MALE && S.gender == FEMALE) + continue + if(gender == FEMALE && S.gender == MALE) + continue + if( !(species in S.species_allowed)) + continue + valid_hairstyles[hairstyle] = hair_styles_list[hairstyle] + + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + + return h_style + +/proc/random_facial_hair_style(gender, species = SPECIES_HUMAN) + var/f_style = "Shaved" + + var/list/valid_facialhairstyles = list() + for(var/facialhairstyle in facial_hair_styles_list) + var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] + if(gender == MALE && S.gender == FEMALE) + continue + if(gender == FEMALE && S.gender == MALE) + continue + if( !(species in S.species_allowed)) + continue + + valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle] + + if(valid_facialhairstyles.len) + f_style = pick(valid_facialhairstyles) + + return f_style + +/proc/sanitize_name(name, species = SPECIES_HUMAN, robot = 0) + var/datum/species/current_species + if(species) + current_species = GLOB.all_species[species] + + return current_species ? current_species.sanitize_name(name, robot) : sanitizeName(name, MAX_NAME_LEN, robot) + +/proc/random_name(gender, species = SPECIES_HUMAN) + + var/datum/species/current_species + if(species) + current_species = GLOB.all_species[species] + + if(!current_species || current_species.name_language == null) + if(gender==FEMALE) + return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) + else + return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + else + return current_species.get_random_name(gender) + +/proc/random_skin_tone() + switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) + if("caucasian") . = -10 + if("afroamerican") . = -115 + if("african") . = -165 + if("latino") . = -55 + if("albino") . = 34 + else . = rand(-185,34) + return min(max( .+rand(-25, 25), -185),34) + +/proc/skintone2racedescription(tone) + switch (tone) + if(30 to INFINITY) return "albino" + if(20 to 30) return "pale" + if(5 to 15) return "light skinned" + if(-10 to 5) return "white" + if(-25 to -10) return "tan" + if(-45 to -25) return "darker skinned" + if(-65 to -45) return "brown" + if(-INFINITY to -65) return "black" + else return "unknown" + +/proc/age2agedescription(age) + switch(age) + if(0 to 1) return "infant" + if(1 to 3) return "toddler" + if(3 to 13) return "child" + if(13 to 19) return "teenager" + if(19 to 30) return "young adult" + if(30 to 45) return "adult" + if(45 to 60) return "middle-aged" + if(60 to 70) return "aging" + if(70 to INFINITY) return "elderly" + else return "unknown" + +/proc/RoundHealth(health) + var/list/icon_states = cached_icon_states(ingame_hud_med) + for(var/icon_state in icon_states) + if(health >= text2num(icon_state)) + return icon_state + return icon_states[icon_states.len] // If we had no match, return the last element + +/* +Proc for attack log creation, because really why not +1 argument is the actor +2 argument is the target of action +3 is the description of action(like punched, throwed, or any other verb) +4 should it make adminlog note or not +5 is the tool with which the action was made(usually item) 5 and 6 are very similar(5 have "by " before it, that it) and are separated just to keep things in a bit more in order +6 is additional information, anything that needs to be added +*/ + +/proc/add_attack_logs(mob/user, mob/target, what_done, var/admin_notify = TRUE) + if(islist(target)) //Multi-victim adding + var/list/targets = target + for(var/mob/M in targets) + add_attack_logs(user,M,what_done,admin_notify) + return + + var/user_str = key_name(user) + var/target_str = key_name(target) + + if(ismob(user)) + user.attack_log += text("\[[time_stamp()]\] [span_red("Attacked [target_str]: [what_done]")]") + if(ismob(target)) + target.attack_log += text("\[[time_stamp()]\] [span_orange("Attacked by [user_str]: [what_done]")]") + log_attack(user_str,target_str,what_done) + if(admin_notify) + msg_admin_attack("[key_name_admin(user)] vs [target_str]: [what_done]") + +//checks whether this item is a module of the robot it is located in. +/proc/is_robot_module(var/obj/item/thing) + if (!thing || !istype(thing.loc, /mob/living/silicon/robot)) + return 0 + var/mob/living/silicon/robot/R = thing.loc + return (thing in R.module.modules) + +/proc/get_exposed_defense_zone(var/atom/movable/target) + var/obj/item/weapon/grab/G = locate() in target + if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. + return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) + else + return pick(BP_TORSO, BP_GROIN) + +/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = FALSE, progress = TRUE, ignore_movement = FALSE, exclusive = FALSE) + if(!user || !target) + return FALSE + if(!time) + return TRUE //Done! + if(user.status_flags & DOING_TASK) + to_chat(user, "You're in the middle of doing something else already.") + return FALSE //Performing an exclusive do_after or do_mob already + if(target?.flags & IS_BUSY) + to_chat(user, "Someone is already doing something with \the [target].") + return FALSE + var/user_loc = user.loc + var/target_loc = target.loc + + var/holding = user.get_active_hand() + var/datum/progressbar/progbar + if (progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags |= DOING_TASK + if(target && exclusive & TASK_TARGET_EXCLUSIVE) + target.flags |= IS_BUSY + + . = TRUE + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + if(!user || !target) + . = FALSE + break + if(uninterruptible) + continue + + if(!user || user.incapacitated()) + . = FALSE + break + + if(user.loc != user_loc && !ignore_movement) + . = FALSE + break + + if(target.loc != target_loc && !ignore_movement) + . = FALSE + break + + if(user.get_active_hand() != holding) + . = FALSE + break + + if(target_zone && user.zone_sel.selecting != target_zone) + . = FALSE + break + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags &= ~DOING_TASK + if(exclusive & TASK_TARGET_EXCLUSIVE) + target?.status_flags &= ~IS_BUSY + + if (progbar) + qdel(progbar) + +/proc/do_after(mob/user, delay, atom/target = null, needhand = TRUE, progress = TRUE, incapacitation_flags = INCAPACITATION_DEFAULT, ignore_movement = FALSE, max_distance = null, exclusive = FALSE) + if(!user) + return FALSE + if(!delay) + return TRUE //Okay. Done. + if(user.status_flags & DOING_TASK) + to_chat(user, "You're in the middle of doing something else already.") + return FALSE //Performing an exclusive do_after or do_mob already + if(target?.flags & IS_BUSY) + to_chat(user, "Someone is already doing something with \the [target].") + return FALSE + + var/atom/target_loc = null + if(target) + target_loc = target.loc + + var/atom/original_loc = user.loc + + var/obj/mecha/M = null + + if(istype(user.loc, /obj/mecha)) + original_loc = get_turf(original_loc) + M = user.loc + + var/holding = user.get_active_hand() + + var/datum/progressbar/progbar + if (progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags |= DOING_TASK + + if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) + target.flags |= IS_BUSY + + . = TRUE + while (world.time < endtime) + stoplag(1) + if(progress) + progbar.update(world.time - starttime) + + if(!user || user.incapacitated(incapacitation_flags)) + . = FALSE + break + + if(M) + if(user.loc != M || (M.loc != original_loc && !ignore_movement)) // Mech coooooode. + . = FALSE + break + + else if(user.loc != original_loc && !ignore_movement) + . = FALSE + break + + if(target_loc && (QDELETED(target))) + . = FALSE + break + + if(target && target_loc != target.loc && !ignore_movement) + . = FALSE + break + + if(needhand) + if(user.get_active_hand() != holding) + . = FALSE + break + + if(max_distance && target && get_dist(user, target) > max_distance) + . = FALSE + break + + if(exclusive & TASK_USER_EXCLUSIVE) + user.status_flags &= ~DOING_TASK + if(target && (exclusive & TASK_TARGET_EXCLUSIVE)) + target.flags &= ~IS_BUSY + + if(progbar) + qdel(progbar) + +/atom/proc/living_mobs(var/range = world.view) + var/list/viewers = oviewers(src,range) + var/list/living = list() + for(var/mob/living/L in viewers) + living += L + + return living + +/atom/proc/human_mobs(var/range = world.view) + var/list/viewers = oviewers(src,range) + var/list/humans = list() + for(var/mob/living/carbon/human/H in viewers) + humans += H + + return humans + +/proc/cached_character_icon(var/mob/desired) + var/cachekey = "\ref[desired][desired.real_name]" + + if(cached_character_icons[cachekey]) + . = cached_character_icons[cachekey] + else + . = getCompoundIcon(desired) + cached_character_icons[cachekey] = . + +/proc/not_has_ooc_text(mob/user) + if (config.allow_Metadata && (!user.client?.prefs?.metadata || length(user.client.prefs.metadata) < 15)) + to_chat(user, "Please set informative OOC notes related to RP/ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup.") + return TRUE + return FALSE diff --git a/code/_helpers/names.dm b/code/_helpers/names.dm index e85a6074173..12e6c46289f 100644 --- a/code/_helpers/names.dm +++ b/code/_helpers/names.dm @@ -1,224 +1,224 @@ -var/church_name = null -/proc/church_name() - if (church_name) - return church_name - - var/name = "" - - name += pick("Holy", "United", "First", "Second", "Last") - - if (prob(20)) - name += " Space" - - name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") - name += " of [religion_name()]" - - return name - -/proc/command_name() - if(istype(using_map)) - return using_map.boss_name - -/proc/change_command_name(var/name) - - using_map.boss_name = name - - return name - -var/religion_name = null -/proc/religion_name() - if (religion_name) - return religion_name - - var/name = "" - - name += pick("bee", "science", "edu", "captain", "assistant", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") - name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") - - return capitalize(name) - -/proc/system_name() - return using_map.starsys_name - -/proc/station_name() - if (using_map.station_name) - return using_map.station_name - - var/random = rand(1,5) - var/name = "" - var/new_station_name = null - - //Rare: Pre-Prefix - if (prob(10)) - name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") - new_station_name = name + " " - - // Prefix - switch(Holiday) - //get normal name - if(null,"",0) - name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") - if(name) - new_station_name += name + " " - - //For special days like christmas, easter, new-years etc ~Carn - if("Friday the 13th") - name = pick("Mike","Friday","Evil","Myers","Murder","Deathly","Stabby") - new_station_name += name + " " - random = 13 - else - //get the first word of the Holiday and use that - var/i = findtext(Holiday," ",1,0) - name = copytext(Holiday,1,i) - new_station_name += name + " " - - // Suffix - name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") - new_station_name += name + " " - - // ID Number - switch(random) - if(1) - new_station_name += "[rand(1, 99)]" - if(2) - new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") - if(3) - new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") - if(4) - new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") - if(5) - new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") - if(13) - new_station_name += pick("13","XIII","Thirteen") - - - if (config && config.server_name) - world.name = "[config.server_name]: [name]" - else - world.name = new_station_name - - return new_station_name - -// Is this even used? -/proc/world_name(var/name) - - using_map.station_name = name - - if (config && config.server_name) - world.name = "[config.server_name]: [name]" - else - world.name = name - - return name - -var/syndicate_name = null -/proc/syndicate_name() - if (syndicate_name) - return syndicate_name - - var/name = "" - - // Prefix - name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") - - // Suffix - if (prob(80)) - name += " " - - // Full - if (prob(60)) - name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") - // Broken - else - name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") - name += pick("", "-") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") - // Small - else - name += pick("-", "*", "") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") - - syndicate_name = name - return name - - -//Traitors and traitor silicons will get these. Revs will not. -var/syndicate_code_phrase//Code phrase for traitors. -var/syndicate_code_response//Code response for traitors. - - /* - Should be expanded. - How this works: - Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. - Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." - The phrase should then have the words: James Smith. - The response should then have the words: run, void, and derelict. - This way assures that the code is suited to the conversation and is unpredicatable. - Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. - Can probably be done through "{ }" but I don't really see the practical benefit. - One example of an earlier system is commented below. - -N - */ - -/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm - - var/code_phrase = ""//What is returned when the proc finishes. - var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. - 50; 2, - 200; 3, - 50; 4, - 25; 5 - ) - - var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. - var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") - var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequilla sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","redwine","moonshine") - var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead. - - var/names[] = list() - for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. - names += t.fields["name"] - - var/maxwords = words//Extra var to check for duplicates. - - for(words,words>0,words--)//Randomly picks from one of the choices below. - - if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. - safety = list(pick(1,2))//Select choice 1 or 2. - else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, - safety = list(3)//Default to list 3 - - switch(pick(safety))//Chance based on the safety list. - if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. - switch(rand(1,2))//Mainly to add more options later. - if(1) - if(names.len&&prob(70)) - code_phrase += pick(names) - else - code_phrase += pick(pick(first_names_male,first_names_female)) - code_phrase += " " - code_phrase += pick(last_names) - if(2) - code_phrase += pick(joblist)//Returns a job. - safety -= 1 - if(2) - switch(rand(1,2))//Places or things. - if(1) - code_phrase += pick(drinks) - if(2) - code_phrase += pick(locations) - safety -= 2 - if(3) - switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. - if(1) - code_phrase += pick(nouns) - if(2) - code_phrase += pick(adjectives) - if(3) - code_phrase += pick(verbs) - if(words==1) - code_phrase += "." - else - code_phrase += ", " - - return code_phrase +var/church_name = null +/proc/church_name() + if (church_name) + return church_name + + var/name = "" + + name += pick("Holy", "United", "First", "Second", "Last") + + if (prob(20)) + name += " Space" + + name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") + name += " of [religion_name()]" + + return name + +/proc/command_name() + if(istype(using_map)) + return using_map.boss_name + +/proc/change_command_name(var/name) + + using_map.boss_name = name + + return name + +var/religion_name = null +/proc/religion_name() + if (religion_name) + return religion_name + + var/name = "" + + name += pick("bee", "science", "edu", "captain", "assistant", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") + name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") + + return capitalize(name) + +/proc/system_name() + return using_map.starsys_name + +/proc/station_name() + if (using_map.station_name) + return using_map.station_name + + var/random = rand(1,5) + var/name = "" + var/new_station_name = null + + //Rare: Pre-Prefix + if (prob(10)) + name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") + new_station_name = name + " " + + // Prefix + switch(Holiday) + //get normal name + if(null,"",0) + name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") + if(name) + new_station_name += name + " " + + //For special days like christmas, easter, new-years etc ~Carn + if("Friday the 13th") + name = pick("Mike","Friday","Evil","Myers","Murder","Deathly","Stabby") + new_station_name += name + " " + random = 13 + else + //get the first word of the Holiday and use that + var/i = findtext(Holiday," ",1,0) + name = copytext(Holiday,1,i) + new_station_name += name + " " + + // Suffix + name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") + new_station_name += name + " " + + // ID Number + switch(random) + if(1) + new_station_name += "[rand(1, 99)]" + if(2) + new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") + if(3) + new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") + if(4) + new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") + if(5) + new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") + if(13) + new_station_name += pick("13","XIII","Thirteen") + + + if (config && config.server_name) + world.name = "[config.server_name]: [name]" + else + world.name = new_station_name + + return new_station_name + +// Is this even used? +/proc/world_name(var/name) + + using_map.station_name = name + + if (config && config.server_name) + world.name = "[config.server_name]: [name]" + else + world.name = name + + return name + +var/syndicate_name = null +/proc/syndicate_name() + if (syndicate_name) + return syndicate_name + + var/name = "" + + // Prefix + name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") + + // Suffix + if (prob(80)) + name += " " + + // Full + if (prob(60)) + name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") + // Broken + else + name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") + name += pick("", "-") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") + // Small + else + name += pick("-", "*", "") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") + + syndicate_name = name + return name + + +//Traitors and traitor silicons will get these. Revs will not. +var/syndicate_code_phrase//Code phrase for traitors. +var/syndicate_code_response//Code response for traitors. + + /* + Should be expanded. + How this works: + Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. + Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." + The phrase should then have the words: James Smith. + The response should then have the words: run, void, and derelict. + This way assures that the code is suited to the conversation and is unpredicatable. + Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. + Can probably be done through "{ }" but I don't really see the practical benefit. + One example of an earlier system is commented below. + -N + */ + +/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm + + var/code_phrase = ""//What is returned when the proc finishes. + var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. + 50; 2, + 200; 3, + 50; 4, + 25; 5 + ) + + var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. + var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") + var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequilla sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","redwine","moonshine") + var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead. + + var/names[] = list() + for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. + names += t.fields["name"] + + var/maxwords = words//Extra var to check for duplicates. + + for(words,words>0,words--)//Randomly picks from one of the choices below. + + if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. + safety = list(pick(1,2))//Select choice 1 or 2. + else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, + safety = list(3)//Default to list 3 + + switch(pick(safety))//Chance based on the safety list. + if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. + switch(rand(1,2))//Mainly to add more options later. + if(1) + if(names.len&&prob(70)) + code_phrase += pick(names) + else + code_phrase += pick(pick(first_names_male,first_names_female)) + code_phrase += " " + code_phrase += pick(last_names) + if(2) + code_phrase += pick(joblist)//Returns a job. + safety -= 1 + if(2) + switch(rand(1,2))//Places or things. + if(1) + code_phrase += pick(drinks) + if(2) + code_phrase += pick(locations) + safety -= 2 + if(3) + switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. + if(1) + code_phrase += pick(nouns) + if(2) + code_phrase += pick(adjectives) + if(3) + code_phrase += pick(verbs) + if(words==1) + code_phrase += "." + else + code_phrase += ", " + + return code_phrase diff --git a/code/_helpers/sanitize_values.dm b/code/_helpers/sanitize_values.dm index 3d042002691..c74176043c3 100644 --- a/code/_helpers/sanitize_values.dm +++ b/code/_helpers/sanitize_values.dm @@ -1,113 +1,113 @@ -//general stuff -/proc/sanitize_integer(number, min=0, max=1, default=0) - if(isnum(number)) - number = round(number) - if(min <= number && number <= max) - return number - return default - -// Checks if the given input is a valid list index; returns true/false and doesn't change anything. -/proc/is_valid_index(input, list/given_list) - if(!isnum(input)) - return FALSE - if(input != round(input)) - return FALSE - if(input < 1 || input > length(given_list)) - return FALSE - return TRUE - -/proc/sanitize_text(text, default="") - if(istext(text)) - return text - return default - -/proc/sanitize_inlist(value, list/List, default) - if(value in List) return value - if(default) return default - if(List && List.len)return List[1] - - - -//more specialised stuff -/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") - switch(gender) - if(MALE, FEMALE)return gender - if(NEUTER) - if(neuter) return gender - else return default - if(PLURAL) - if(plural) return gender - else return default - return default - -/proc/sanitize_hexcolor(color, default="#000000") - if(!istext(color)) return default - var/len = length(color) - if(len != 7 && len !=4) return default - if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" - . = "#" - for(var/i=2,i<=len,i++) - var/ascii = text2ascii(color,i) - switch(ascii) - if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9 - if(97 to 102) . += ascii2text(ascii) //letters a to f - if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase - else return default - return . - -//Valid format codes: YY, YEAR, MM, DD, hh, mm, ss, :, -. " " (space). Invalid format will return default. -/proc/sanitize_time(time, default, format = "hh:mm") - if(!istext(time) || !(length(time) == length(format))) - return default - var/fragment = "" - . = list() - for(var/i = 1, i <= length(format), i++) - fragment += copytext(format,i,i+1) - if(fragment in list("YY", "YEAR", "MM", "DD", "hh", "mm", "ss")) - . += sanitize_one_time(copytext(time, i - length(fragment) + 1, i + 1), copytext(default, i - length(fragment) + 1, i + 1), fragment) - fragment = "" - else if(fragment in list(":", "-", " ")) - . += fragment - fragment = "" - if(fragment) - return default //This means the format was improper. - return JOINTEXT(.) - -//Internal proc, expects valid format and text input of equal length to format. -/proc/sanitize_one_time(input, default, format) - var/list/ainput = list() - for(var/i = 1, i <= length(input), i++) - ainput += text2ascii(input, i) - switch(format) - if("YY") - if(!(ainput[1] in 48 to 57) || !(ainput[2] in 48 to 57))//0 to 9 - return (default || "00") - return input - if("YEAR") - for(var/i = 1, i <= 4, i++) - if(!(ainput[i] in 48 to 57))//0 to 9 - return (default || "0000") - return input - if("MM") - var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 - var/late = (ainput[1] == 49) && (ainput[2] in 48 to 50) //10 to 12 - if(!(early || late)) - return (default || "01") - return input - if("DD") - var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 - var/mid = (ainput[1] in 49 to 50) && (ainput[2] in 48 to 57) //10 to 29 - var/late = (ainput[1] == 51) && (ainput[2] in 48 to 49) //30 to 31 - if(!(early || mid || late)) - return (default || "01") - return input - if("hh") - var/early = (ainput[1] in 48 to 49) && (ainput[2] in 48 to 57) //00 to 19 - var/late = (ainput[1] == 50) && (ainput[2] in 48 to 51) //20 to 23 - if(!(early || late)) - return (default || "00") - return input - if("mm", "ss") - if(!(ainput[1] in 48 to 53) || !(ainput[2] in 48 to 57)) //0 to 5, 0 to 9 - return (default || "00") - return input +//general stuff +/proc/sanitize_integer(number, min=0, max=1, default=0) + if(isnum(number)) + number = round(number) + if(min <= number && number <= max) + return number + return default + +// Checks if the given input is a valid list index; returns true/false and doesn't change anything. +/proc/is_valid_index(input, list/given_list) + if(!isnum(input)) + return FALSE + if(input != round(input)) + return FALSE + if(input < 1 || input > length(given_list)) + return FALSE + return TRUE + +/proc/sanitize_text(text, default="") + if(istext(text)) + return text + return default + +/proc/sanitize_inlist(value, list/List, default) + if(value in List) return value + if(default) return default + if(List && List.len)return List[1] + + + +//more specialised stuff +/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") + switch(gender) + if(MALE, FEMALE)return gender + if(NEUTER) + if(neuter) return gender + else return default + if(PLURAL) + if(plural) return gender + else return default + return default + +/proc/sanitize_hexcolor(color, default="#000000") + if(!istext(color)) return default + var/len = length(color) + if(len != 7 && len !=4) return default + if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" + . = "#" + for(var/i=2,i<=len,i++) + var/ascii = text2ascii(color,i) + switch(ascii) + if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9 + if(97 to 102) . += ascii2text(ascii) //letters a to f + if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase + else return default + return . + +//Valid format codes: YY, YEAR, MM, DD, hh, mm, ss, :, -. " " (space). Invalid format will return default. +/proc/sanitize_time(time, default, format = "hh:mm") + if(!istext(time) || !(length(time) == length(format))) + return default + var/fragment = "" + . = list() + for(var/i = 1, i <= length(format), i++) + fragment += copytext(format,i,i+1) + if(fragment in list("YY", "YEAR", "MM", "DD", "hh", "mm", "ss")) + . += sanitize_one_time(copytext(time, i - length(fragment) + 1, i + 1), copytext(default, i - length(fragment) + 1, i + 1), fragment) + fragment = "" + else if(fragment in list(":", "-", " ")) + . += fragment + fragment = "" + if(fragment) + return default //This means the format was improper. + return JOINTEXT(.) + +//Internal proc, expects valid format and text input of equal length to format. +/proc/sanitize_one_time(input, default, format) + var/list/ainput = list() + for(var/i = 1, i <= length(input), i++) + ainput += text2ascii(input, i) + switch(format) + if("YY") + if(!(ainput[1] in 48 to 57) || !(ainput[2] in 48 to 57))//0 to 9 + return (default || "00") + return input + if("YEAR") + for(var/i = 1, i <= 4, i++) + if(!(ainput[i] in 48 to 57))//0 to 9 + return (default || "0000") + return input + if("MM") + var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 + var/late = (ainput[1] == 49) && (ainput[2] in 48 to 50) //10 to 12 + if(!(early || late)) + return (default || "01") + return input + if("DD") + var/early = (ainput[1] == 48) && (ainput[2] in 49 to 57) //01 to 09 + var/mid = (ainput[1] in 49 to 50) && (ainput[2] in 48 to 57) //10 to 29 + var/late = (ainput[1] == 51) && (ainput[2] in 48 to 49) //30 to 31 + if(!(early || mid || late)) + return (default || "01") + return input + if("hh") + var/early = (ainput[1] in 48 to 49) && (ainput[2] in 48 to 57) //00 to 19 + var/late = (ainput[1] == 50) && (ainput[2] in 48 to 51) //20 to 23 + if(!(early || late)) + return (default || "00") + return input + if("mm", "ss") + if(!(ainput[1] in 48 to 53) || !(ainput[2] in 48 to 57)) //0 to 5, 0 to 9 + return (default || "00") + return input diff --git a/code/_helpers/shell.dm b/code/_helpers/shell.dm new file mode 100644 index 00000000000..35a8e4635b4 --- /dev/null +++ b/code/_helpers/shell.dm @@ -0,0 +1,60 @@ +//Runs the command in the system's shell, returns a list of (error code, stdout, stderr) + +#define SHELLEO_NAME "data/shelleo." +#define SHELLEO_ERR ".err" +#define SHELLEO_OUT ".out" +/world/proc/shelleo(command) + var/static/list/shelleo_ids = list() + var/stdout = "" + var/stderr = "" + var/errorcode = 1 + var/shelleo_id + var/out_file = "" + var/err_file = "" + var/static/list/interpreters = list("[MS_WINDOWS]" = "cmd /c", "[UNIX]" = "sh -c") + var/interpreter = interpreters["[world.system_type]"] + if(interpreter) + for(var/seo_id in shelleo_ids) + if(!shelleo_ids[seo_id]) + shelleo_ids[seo_id] = TRUE + shelleo_id = "[seo_id]" + break + if(!shelleo_id) + shelleo_id = "[shelleo_ids.len + 1]" + shelleo_ids += shelleo_id + shelleo_ids[shelleo_id] = TRUE + out_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_OUT]" + err_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_ERR]" + if(world.system_type == UNIX) + errorcode = shell("[interpreter] \"[replacetext(command, "\"", "\\\"")]\" > [out_file] 2> [err_file]") + else + errorcode = shell("[interpreter] \"[command]\" > [out_file] 2> [err_file]") + if(fexists(out_file)) + stdout = file2text(out_file) + fdel(out_file) + if(fexists(err_file)) + stderr = file2text(err_file) + fdel(err_file) + shelleo_ids[shelleo_id] = FALSE + else + CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system + . = list(errorcode, stdout, stderr) +#undef SHELLEO_NAME +#undef SHELLEO_ERR +#undef SHELLEO_OUT + +/proc/shell_url_scrub(url) + var/static/regex/bad_chars_regex = regex("\[^#%&./:=?\\w]*", "g") + var/scrubbed_url = "" + var/bad_match = "" + var/last_good = 1 + var/bad_chars = 1 + do + bad_chars = bad_chars_regex.Find(url) + scrubbed_url += copytext(url, last_good, bad_chars) + if(bad_chars) + bad_match = url_encode(bad_chars_regex.match) + scrubbed_url += bad_match + last_good = bad_chars + length(bad_chars_regex.match) + while(bad_chars) + . = scrubbed_url diff --git a/code/_helpers/sorts/__main.dm b/code/_helpers/sorts/__main.dm index 622d88f1472..7fd0a505c70 100644 --- a/code/_helpers/sorts/__main.dm +++ b/code/_helpers/sorts/__main.dm @@ -102,7 +102,8 @@ start the index of the first element in the range that is not already known to b if(start <= lo) start = lo + 1 - for(,start < hi, ++start) + var/list/L = src.L + for(start in start to hi - 1) var/pivot = fetchElement(L,start) //set left and right to the index where pivot belongs @@ -626,20 +627,17 @@ reverse a descending sequence without violating stability. var/val2 = fetchElement(L,cursor2) while(1) - if(call(cmp)(val1,val2) < 0) + if(call(cmp)(val1,val2) <= 0) if(++cursor1 >= end1) break val1 = fetchElement(L,cursor1) else moveElement(L,cursor2,cursor1) - ++cursor2 if(++cursor2 >= end2) break ++end1 ++cursor1 - //if(++cursor1 >= end1) - // break val2 = fetchElement(L,cursor2) diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index c0b6bd5001a..571bf281ce6 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -1,610 +1,614 @@ -/* - * Holds procs designed to help with filtering text - * Contains groups: - * SQL sanitization - * Text sanitization - * Text searches - * Text modification - * Misc - */ - - -/* - * SQL sanitization - */ - -// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. -/proc/sanitizeSQL(var/t as text) - var/sqltext = dbcon.Quote(t); - return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that - -/* - * Text sanitization - */ -// Can be used almost the same way as normal input for text -/proc/clean_input(Message, Title, Default, mob/user=usr) - var/txt = input(user, Message, Title, Default) as text | null - if(txt) - return html_encode(txt) - -//Simply removes < and > and limits the length of the message -/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) - var/list/strip_chars = list("<",">") - t = copytext(t,1,limit) - for(var/char in strip_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + copytext(t, index+1) - index = findtext(t, char) - return t - -//Runs byond's sanitization proc along-side strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause -/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) - return copytext((html_encode(strip_html_simple(t))),1,limit) - -//Used for preprocessing entered text -/proc/sanitize(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) - if(!input) - return - - if(max_length) - input = copytext(input,1,max_length) - - if(extra) - var/temp_input = replace_characters(input, list("\n"=" ","\t"=" "))//one character is replaced by two - if(length(input) < (length(temp_input) - 6))//6 is the number of linebreaks allowed per message - input = replace_characters(temp_input,list(" "=" "))//replace again, this time the double spaces with single ones - - if(encode) - // The below \ escapes have a space inserted to attempt to enable CI auto-checking of span class usage. Please do not remove the space. - //In addition to processing html, html_encode removes byond formatting codes like "\ red", "\ i" and other. - //It is important to avoid double-encode text, it can "break" quotes and some other characters. - //Also, keep in mind that escaped characters don't work in the interface (window titles, lower left corner of the main window, etc.) - input = html_encode(input) - else - //If not need encode text, simply remove < and > - //note: we can also remove here byond formatting codes: 0xFF + next byte - input = replace_characters(input, list("<"=" ", ">"=" ")) - - if(trim) - //Maybe, we need trim text twice? Here and before copytext? - input = trim(input) - - return input - -//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). -//Best used for sanitize object names, window titles. -//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - -//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! -/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) - return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) - -//Filters out undesirable characters from names -/proc/sanitizeName(var/input, var/max_length = MAX_NAME_LEN, var/allow_numbers = 0) - if(!input || length(input) > max_length) - return //Rejects the input if it is null or if it is longer then the max length allowed - - var/number_of_alphanumeric = 0 - var/last_char_group = 0 - var/output = "" - - for(var/i=1, i<=length(input), i++) - var/ascii_char = text2ascii(input,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - output += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // a .. z - if(97 to 122) //Lowercase Letters - if(last_char_group<2) output += ascii2text(ascii_char-32) //Force uppercase first character - else output += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // 0 .. 9 - if(48 to 57) //Numbers - if(!allow_numbers) continue // If allow_numbers is 0, then don't do this. - output += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 3 - - // ' - . - if(39,45,46) //Common name punctuation - if(!last_char_group) continue - output += ascii2text(ascii_char) - last_char_group = 2 - - // ~ | @ : # $ % & * + - if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI) - if(!last_char_group) continue //suppress at start of string - if(!allow_numbers) continue - output += ascii2text(ascii_char) - last_char_group = 2 - - //Space - if(32) - if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string - output += ascii2text(ascii_char) - last_char_group = 1 - else - return - - if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" - - if(last_char_group == 1) - output = copytext(output,1,length(output)) //removes the last character (in this case a space) - - for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names - if(cmptext(output,bad_name)) return //(not case sensitive) - - return output - -//Returns null if there is any bad text in the string -/proc/reject_bad_text(var/text, var/max_length=512) - if(length(text) > max_length) return //message too long - var/non_whitespace = 0 - for(var/i=1, i<=length(text), i++) - switch(text2ascii(text,i)) - if(62,60,92,47) return //rejects the text if it contains these bad characters: <, >, \ or / - if(127 to 255) return //rejects weird letters like � - if(0 to 31) return //more weird stuff - if(32) continue //whitespace - else non_whitespace = 1 - if(non_whitespace) return text //only accepts the text if it has some non-spaces - - -//Old variant. Haven't dared to replace in some places. -/proc/sanitize_old(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) - return html_encode(replace_characters(t,repl_chars)) - - -//Removes a few problematic characters -/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#")) - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index + length(char)) - index = findtext(t, char, index + length(char)) - return t - -/proc/sanitize_filename(t) - return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) - -/* - * Text searches - */ - -//Checks the beginning of a string for a specified sub-string -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtext(text, prefix, start, end) - -//Checks the beginning of a string for a specified sub-string. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix_case(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtextEx(text, prefix, start, end) - -//Checks the end of a string for a specified substring. -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtext(text, suffix, start, null) - return - -//Checks the end of a string for a specified substring. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix_case(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtextEx(text, suffix, start, null) - -/* - * Text modification - */ -/proc/replace_characters(var/t,var/list/repl_chars) - for(var/char in repl_chars) - t = replacetext(t, char, repl_chars[char]) - return t - -//Adds 'u' number of zeros ahead of the text 't' -/proc/add_zero(t, u) - while (length(t) < u) - t = "0[t]" - return t - -//Adds 'u' number of spaces ahead of the text 't' -/proc/add_lspace(t, u) - while(length(t) < u) - t = " [t]" - return t - -//Adds 'u' number of spaces behind the text 't' -/proc/add_tspace(t, u) - while(length(t) < u) - t = "[t] " - return t - -//Returns a string with reserved characters and spaces before the first letter removed -/proc/trim_left(text) - for (var/i = 1 to length(text)) - if (text2ascii(text, i) > 32) - return copytext(text, i) - return "" - -//Returns a string with reserved characters and spaces after the last letter removed -/proc/trim_right(text) - for (var/i = length(text), i > 0, i--) - if (text2ascii(text, i) > 32) - return copytext(text, 1, i + 1) - return "" - -//Returns a string with reserved characters and spaces before the first word and after the last word removed. -/proc/trim(text) - return trim_left(trim_right(text)) - -//Returns a string with the first element of the string capitalized. -/proc/capitalize(var/t as text) - return uppertext(copytext(t, 1, 2)) + copytext(t, 2) - -//This proc strips html properly, remove < > and all text between -//for complete text sanitizing should be used sanitize() -/proc/strip_html_properly(var/input) - if(!input) - return - var/opentag = 1 //These store the position of < and > respectively. - var/closetag = 1 - while(1) - opentag = findtext(input, "<") - closetag = findtext(input, ">") - if(closetag && opentag) - if(closetag < opentag) - input = copytext(input, (closetag + 1)) - else - input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) - else if(closetag || opentag) - if(opentag) - input = copytext(input, 1, opentag) - else - input = copytext(input, (closetag + 1)) - else - break - - return input - -//This proc fills in all spaces with the "replace" var (* by default) with whatever -//is in the other string at the same spot (assuming it is not a replace char). -//This is used for fingerprints -/proc/stringmerge(var/text,var/compare,replace = "*") - var/newtext = text - if(length(text) != length(compare)) - return 0 - for(var/i = 1, i < length(text), i++) - var/a = copytext(text,i,i+1) - var/b = copytext(compare,i,i+1) - //if it isn't both the same letter, or if they are both the replacement character - //(no way to know what it was supposed to be) - if(a != b) - if(a == replace) //if A is the replacement char - newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) - else if(b == replace) //if B is the replacement char - newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) - else //The lists disagree, Uh-oh! - return 0 - return newtext - -//This proc returns the number of chars of the string that is the character -//This is used for detective work to determine fingerprint completion. -/proc/stringpercent(var/text,character = "*") - if(!text || !character) - return 0 - var/count = 0 - for(var/i = 1, i <= length(text), i++) - var/a = copytext(text,i,i+1) - if(a == character) - count++ - return count - -/proc/reverse_text(var/text = "") - var/new_text = "" - for(var/i = length(text); i > 0; i--) - new_text += copytext(text, i, i+1) - return new_text - -//Used in preferences' SetFlavorText and human's set_flavor verb -//Previews a string of len or less length -/proc/TextPreview(var/string,var/len=40) - if(length(string) <= len) - if(!length(string)) - return "\[...\]" - else - return string - else - return "[copytext_preserve_html(string, 1, 37)]..." - -//alternative copytext() for encoded text, doesn't break html entities (" and other) -/proc/copytext_preserve_html(var/text, var/first, var/last) - return html_encode(copytext(html_decode(text), first, last)) - -//For generating neat chat tag-images -//The icon var could be local in the proc, but it's a waste of resources -// to always create it and then throw it out. -/var/icon/text_tag_icons = 'icons/chattags.dmi' -/var/list/text_tag_cache = list() -/proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) - if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) - return tagdesc - if(!text_tag_cache[tagname]) - var/icon/tag = icon(text_tag_icons, tagname) - text_tag_cache[tagname] = bicon(tag, TRUE, "text_tag") - if(C.chatOutput.broken) - return "[tagdesc]" - return text_tag_cache[tagname] - -/proc/create_text_tag_old(var/tagname, var/tagdesc = tagname, var/client/C = null) - if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) - return tagdesc - return "[tagdesc]" - -/proc/contains_az09(var/input) - for(var/i=1, i<=length(input), i++) - var/ascii_char = text2ascii(input,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - return 1 - // a .. z - if(97 to 122) //Lowercase Letters - return 1 - - // 0 .. 9 - if(48 to 57) //Numbers - return 1 - return 0 - -/** - * Strip out the special beyond characters for \proper and \improper - * from text that will be sent to the browser. - */ -/proc/strip_improper(var/text) - return replacetext(replacetext(text, "\proper", ""), "\improper", "") - -/proc/pencode2html(t) - t = replacetext(t, "\n", "
") - t = replacetext(t, "\[center\]", "
") - t = replacetext(t, "\[/center\]", "
") - t = replacetext(t, "\[br\]", "
") - t = replacetext(t, "\[b\]", "") - t = replacetext(t, "\[/b\]", "") - t = replacetext(t, "\[i\]", "") - t = replacetext(t, "\[/i\]", "") - t = replacetext(t, "\[u\]", "") - t = replacetext(t, "\[/u\]", "") - t = replacetext(t, "\[time\]", "[stationtime2text()]") - t = replacetext(t, "\[date\]", "[stationdate2text()]") - t = replacetext(t, "\[large\]", "") - t = replacetext(t, "\[/large\]", "") - t = replacetext(t, "\[field\]", "") - t = replacetext(t, "\[h1\]", "

") - t = replacetext(t, "\[/h1\]", "

") - t = replacetext(t, "\[h2\]", "

") - t = replacetext(t, "\[/h2\]", "

") - t = replacetext(t, "\[h3\]", "

") - t = replacetext(t, "\[/h3\]", "

") - t = replacetext(t, "\[*\]", "
  • ") - t = replacetext(t, "\[hr\]", "
    ") - t = replacetext(t, "\[small\]", "") - t = replacetext(t, "\[/small\]", "") - t = replacetext(t, "\[list\]", "
      ") - t = replacetext(t, "\[/list\]", "
    ") - t = replacetext(t, "\[table\]", "") - t = replacetext(t, "\[/table\]", "
    ") - t = replacetext(t, "\[grid\]", "") - t = replacetext(t, "\[/grid\]", "
    ") - t = replacetext(t, "\[row\]", "") - t = replacetext(t, "\[cell\]", "") - t = replacetext(t, "\[logo\]", "") - t = replacetext(t, "\[redlogo\]", "") - t = replacetext(t, "\[sglogo\]", "") - t = replacetext(t, "\[editorbr\]", "") - return t - -//pencode translation to html for tags exclusive to digital files (currently email, nanoword, report editor fields, -//modular scanner data and txt file printing) and prints from them -/proc/digitalPencode2html(var/text) - text = replacetext(text, "\[pre\]", "
    ")
    -	text = replacetext(text, "\[/pre\]", "
    ") - text = replacetext(text, "\[fontred\]", "") // to pass html tag integrity unit test - text = replacetext(text, "\[fontblue\]", "")// to pass html tag integrity unit test - text = replacetext(text, "\[fontgreen\]", "") - text = replacetext(text, "\[/font\]", "") - text = replacetext(text, "\[redacted\]", "R E D A C T E D") - return pencode2html(text) - -//Will kill most formatting; not recommended. -/proc/html2pencode(t) - t = replacetext(t, "
    ", "\[pre\]")
    -	t = replacetext(t, "
    ", "\[/pre\]") - t = replacetext(t, "", "\[fontred\]")// to pass html tag integrity unit test - t = replacetext(t, "", "\[fontblue\]")// to pass html tag integrity unit test - t = replacetext(t, "", "\[fontgreen\]") - t = replacetext(t, "", "\[/font\]") - t = replacetext(t, "
    ", "\[br\]") - t = replacetext(t, "
    ", "\[br\]") - t = replacetext(t, "", "\[b\]") - t = replacetext(t, "", "\[/b\]") - t = replacetext(t, "", "\[i\]") - t = replacetext(t, "", "\[/i\]") - t = replacetext(t, "", "\[u\]") - t = replacetext(t, "", "\[/u\]") - t = replacetext(t, "
    ", "\[center\]") - t = replacetext(t, "
    ", "\[/center\]") - t = replacetext(t, "

    ", "\[h1\]") - t = replacetext(t, "

    ", "\[/h1\]") - t = replacetext(t, "

    ", "\[h2\]") - t = replacetext(t, "

    ", "\[/h2\]") - t = replacetext(t, "

    ", "\[h3\]") - t = replacetext(t, "

    ", "\[/h3\]") - t = replacetext(t, "
  • ", "\[*\]") - t = replacetext(t, "
    ", "\[hr\]") - t = replacetext(t, "
      ", "\[list\]") - t = replacetext(t, "
    ", "\[/list\]") - t = replacetext(t, "", "\[grid\]") - t = replacetext(t, "
    ", "\[/grid\]") - t = replacetext(t, "", "\[row\]") - t = replacetext(t, "", "\[cell\]") - t = replacetext(t, "", "\[logo\]") - t = replacetext(t, "", "\[redlogo\]") - t = replacetext(t, "", "\[sglogo\]") - t = replacetext(t, "", "\[field\]") - t = replacetext(t, "R E D A C T E D", "\[redacted\]") - t = strip_html_properly(t) - return t - -// Random password generator -/proc/GenerateKey() - //Feel free to move to Helpers. - var/newKey - newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") - newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") - newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") - return newKey - -//Used for applying byonds text macros to strings that are loaded at runtime -/proc/apply_text_macros(string) - var/next_backslash = findtext(string, "\\") - if(!next_backslash) - return string - - var/leng = length(string) - - var/next_space = findtext(string, " ", next_backslash + 1) - if(!next_space) - next_space = leng - next_backslash - - if(!next_space) //trailing bs - return string - - var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash) - var/macro = lowertext(copytext(string, next_backslash + 1, next_space)) - var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1) - - //See http://www.byond.com/docs/ref/info.html#/DM/text/macros - switch(macro) - //prefixes/agnostic - if("the") - rest = text("\the []", rest) - if("a") - rest = text("\a []", rest) - if("an") - rest = text("\an []", rest) - if("proper") - rest = text("\proper []", rest) - if("improper") - rest = text("\improper []", rest) - if("roman") - rest = text("\roman []", rest) - //postfixes - if("th") - base = text("[]\th", rest) - if("s") - base = text("[]\s", rest) - if("he") - base = text("[]\he", rest) - if("she") - base = text("[]\she", rest) - if("his") - base = text("[]\his", rest) - if("himself") - base = text("[]\himself", rest) - if("herself") - base = text("[]\herself", rest) - if("hers") - base = text("[]\hers", rest) - - . = base - if(rest) - . += .(rest) - - -#define gender2text(gender) capitalize(gender) - -/** - * Used to get a properly sanitized input. Returns null if cancel is pressed. - * - * Arguments - ** user - Target of the input prompt. - ** message - The text inside of the prompt. - ** title - The window title of the prompt. - ** max_length - If you intend to impose a length limit - default is 1024. - ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. -*/ -/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/user_input = input(user, message, title, default) as text|null - if(isnull(user_input)) // User pressed cancel - return - if(no_trim) - return copytext(html_encode(user_input), 1, max_length) - else - return trim(html_encode(user_input), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) - -/** - * Used to get a properly sanitized input in a larger box. Works very similarly to stripped_input. - * - * Arguments - ** user - Target of the input prompt. - ** message - The text inside of the prompt. - ** title - The window title of the prompt. - ** max_length - If you intend to impose a length limit - default is 1024. - ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. -*/ -/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/user_input = input(user, message, title, default) as message|null - if(isnull(user_input)) // User pressed cancel - return - if(no_trim) - return copytext(html_encode(user_input), 1, max_length) - else - return trim(html_encode(user_input), max_length) - -//Adds 'char' ahead of 'text' until there are 'count' characters total -/proc/add_leading(text, count, char = " ") - text = "[text]" - var/charcount = count - length_char(text) - var/list/chars_to_add[max(charcount + 1, 0)] - return jointext(chars_to_add, char) + text - -//Adds 'char' behind 'text' until there are 'count' characters total -/proc/add_trailing(text, count, char = " ") - text = "[text]" - var/charcount = count - length_char(text) - var/list/chars_to_add[max(charcount + 1, 0)] - return text + jointext(chars_to_add, char) - -//Readds quotes and apostrophes to HTML-encoded strings -/proc/readd_quotes(var/t) - var/list/repl_chars = list(""" = "\"","'" = "'") - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) - index = findtext(t, char) - return t - -// Rips out paper HTML but tries to keep it semi-readable. -/proc/paper_html_to_plaintext(paper_text) - paper_text = replacetext(paper_text, "
    ", "-----") - paper_text = replacetext(paper_text, "
  • ", "- ") // This makes ordered lists turn into unordered but fixing that is too much effort. - paper_text = replacetext(paper_text, "
  • ", "\n") - paper_text = replacetext(paper_text, "

    ", "\n") - paper_text = replacetext(paper_text, "
    ", "\n") - paper_text = strip_html_properly(paper_text) // Get rid of everything else entirely. - return paper_text +/* + * Holds procs designed to help with filtering text + * Contains groups: + * SQL sanitization + * Text sanitization + * Text searches + * Text modification + * Misc + */ + + +/* + * SQL sanitization + */ + +// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. +/proc/sanitizeSQL(var/t as text) + var/sqltext = dbcon.Quote(t); + return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that + +/* + * Text sanitization + */ +// Can be used almost the same way as normal input for text +/proc/clean_input(Message, Title, Default, mob/user=usr) + var/txt = input(user, Message, Title, Default) as text | null + if(txt) + return html_encode(txt) + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) + +//Used for preprocessing entered text +/proc/sanitize(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) + if(!input) + return + + if(max_length) + input = copytext(input,1,max_length) + + if(extra) + var/temp_input = replace_characters(input, list("\n"=" ","\t"=" "))//one character is replaced by two + if(length(input) < (length(temp_input) - 6))//6 is the number of linebreaks allowed per message + input = replace_characters(temp_input,list(" "=" "))//replace again, this time the double spaces with single ones + + if(encode) + // The below \ escapes have a space inserted to attempt to enable CI auto-checking of span class usage. Please do not remove the space. + //In addition to processing html, html_encode removes byond formatting codes like "\ red", "\ i" and other. + //It is important to avoid double-encode text, it can "break" quotes and some other characters. + //Also, keep in mind that escaped characters don't work in the interface (window titles, lower left corner of the main window, etc.) + input = html_encode(input) + else + //If not need encode text, simply remove < and > + //note: we can also remove here byond formatting codes: 0xFF + next byte + input = replace_characters(input, list("<"=" ", ">"=" ")) + + if(trim) + //Maybe, we need trim text twice? Here and before copytext? + input = trim(input) + + return input + +//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). +//Best used for sanitize object names, window titles. +//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - +//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! +/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) + return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) + +//Filters out undesirable characters from names +/proc/sanitizeName(var/input, var/max_length = MAX_NAME_LEN, var/allow_numbers = 0) + if(!input || length(input) > max_length) + return //Rejects the input if it is null or if it is longer then the max length allowed + + var/number_of_alphanumeric = 0 + var/last_char_group = 0 + var/output = "" + + for(var/i=1, i<=length(input), i++) + var/ascii_char = text2ascii(input,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + output += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // a .. z + if(97 to 122) //Lowercase Letters + if(last_char_group<2) output += ascii2text(ascii_char-32) //Force uppercase first character + else output += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // 0 .. 9 + if(48 to 57) //Numbers + if(!allow_numbers) continue // If allow_numbers is 0, then don't do this. + output += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 3 + + // ' - . + if(39,45,46) //Common name punctuation + if(!last_char_group) continue + output += ascii2text(ascii_char) + last_char_group = 2 + + // ~ | @ : # $ % & * + + if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI) + if(!last_char_group) continue //suppress at start of string + if(!allow_numbers) continue + output += ascii2text(ascii_char) + last_char_group = 2 + + //Space + if(32) + if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string + output += ascii2text(ascii_char) + last_char_group = 1 + else + return + + if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" + + if(last_char_group == 1) + output = copytext(output,1,length(output)) //removes the last character (in this case a space) + + for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names + if(cmptext(output,bad_name)) return //(not case sensitive) + + return output + +//Returns null if there is any bad text in the string +/proc/reject_bad_text(var/text, var/max_length=512) + if(length(text) > max_length) return //message too long + var/non_whitespace = 0 + for(var/i=1, i<=length(text), i++) + switch(text2ascii(text,i)) + if(62,60,92,47) return //rejects the text if it contains these bad characters: <, >, \ or / + if(127 to 255) return //rejects weird letters like � + if(0 to 31) return //more weird stuff + if(32) continue //whitespace + else non_whitespace = 1 + if(non_whitespace) return text //only accepts the text if it has some non-spaces + + +//Old variant. Haven't dared to replace in some places. +/proc/sanitize_old(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) + return html_encode(replace_characters(t,repl_chars)) + + +//Removes a few problematic characters +/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#")) + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index + length(char)) + index = findtext(t, char, index + length(char)) + return t + +/proc/sanitize_filename(t) + return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) + +/* + * Text searches + */ + +//Checks the beginning of a string for a specified sub-string +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtext(text, prefix, start, end) + +//Checks the beginning of a string for a specified sub-string. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix_case(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtextEx(text, prefix, start, end) + +//Checks the end of a string for a specified substring. +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtext(text, suffix, start, null) + return + +//Checks the end of a string for a specified substring. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix_case(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtextEx(text, suffix, start, null) + +/* + * Text modification + */ +/proc/replace_characters(var/t,var/list/repl_chars) + for(var/char in repl_chars) + t = replacetext(t, char, repl_chars[char]) + return t + +//Adds 'u' number of zeros ahead of the text 't' +/proc/add_zero(t, u) + while (length(t) < u) + t = "0[t]" + return t + +//Adds 'u' number of spaces ahead of the text 't' +/proc/add_lspace(t, u) + while(length(t) < u) + t = " [t]" + return t + +//Adds 'u' number of spaces behind the text 't' +/proc/add_tspace(t, u) + while(length(t) < u) + t = "[t] " + return t + +//Returns a string with reserved characters and spaces before the first letter removed +/proc/trim_left(text) + for (var/i = 1 to length(text)) + if (text2ascii(text, i) > 32) + return copytext(text, i) + return "" + +//Returns a string with reserved characters and spaces after the last letter removed +/proc/trim_right(text) + for (var/i = length(text), i > 0, i--) + if (text2ascii(text, i) > 32) + return copytext(text, 1, i + 1) + return "" + +//Returns a string with reserved characters and spaces before the first word and after the last word removed. +/proc/trim(text) + return trim_left(trim_right(text)) + +//Returns a string with the first element of the string capitalized. +/proc/capitalize(var/t as text) + return uppertext(copytext(t, 1, 2)) + copytext(t, 2) + +//Returns a unicode string with the first element of the string capitalized. +/proc/capitalize_utf(var/t as text) + return uppertext(copytext_char(t, 1, 2)) + copytext_char(t, 2) + +//This proc strips html properly, remove < > and all text between +//for complete text sanitizing should be used sanitize() +/proc/strip_html_properly(var/input) + if(!input) + return + var/opentag = 1 //These store the position of < and > respectively. + var/closetag = 1 + while(1) + opentag = findtext(input, "<") + closetag = findtext(input, ">") + if(closetag && opentag) + if(closetag < opentag) + input = copytext(input, (closetag + 1)) + else + input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) + else if(closetag || opentag) + if(opentag) + input = copytext(input, 1, opentag) + else + input = copytext(input, (closetag + 1)) + else + break + + return input + +//This proc fills in all spaces with the "replace" var (* by default) with whatever +//is in the other string at the same spot (assuming it is not a replace char). +//This is used for fingerprints +/proc/stringmerge(var/text,var/compare,replace = "*") + var/newtext = text + if(length(text) != length(compare)) + return 0 + for(var/i = 1, i < length(text), i++) + var/a = copytext(text,i,i+1) + var/b = copytext(compare,i,i+1) + //if it isn't both the same letter, or if they are both the replacement character + //(no way to know what it was supposed to be) + if(a != b) + if(a == replace) //if A is the replacement char + newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) + else if(b == replace) //if B is the replacement char + newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) + else //The lists disagree, Uh-oh! + return 0 + return newtext + +//This proc returns the number of chars of the string that is the character +//This is used for detective work to determine fingerprint completion. +/proc/stringpercent(var/text,character = "*") + if(!text || !character) + return 0 + var/count = 0 + for(var/i = 1, i <= length(text), i++) + var/a = copytext(text,i,i+1) + if(a == character) + count++ + return count + +/proc/reverse_text(var/text = "") + var/new_text = "" + for(var/i = length(text); i > 0; i--) + new_text += copytext(text, i, i+1) + return new_text + +//Used in preferences' SetFlavorText and human's set_flavor verb +//Previews a string of len or less length +/proc/TextPreview(var/string,var/len=40) + if(length(string) <= len) + if(!length(string)) + return "\[...\]" + else + return string + else + return "[copytext_preserve_html(string, 1, 37)]..." + +//alternative copytext() for encoded text, doesn't break html entities (" and other) +/proc/copytext_preserve_html(var/text, var/first, var/last) + return html_encode(copytext(html_decode(text), first, last)) + +//For generating neat chat tag-images +//The icon var could be local in the proc, but it's a waste of resources +// to always create it and then throw it out. +/var/icon/text_tag_icons = 'icons/chattags.dmi' +/var/list/text_tag_cache = list() +/proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) + if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) + return tagdesc + if(!text_tag_cache[tagname]) + var/icon/tag = icon(text_tag_icons, tagname) + text_tag_cache[tagname] = bicon(tag, TRUE, "text_tag") + if(!C.tgui_panel.is_ready() || C.tgui_panel.oldchat) + return "[tagdesc]" + return text_tag_cache[tagname] + +/proc/create_text_tag_old(var/tagname, var/tagdesc = tagname, var/client/C = null) + if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) + return tagdesc + return "[tagdesc]" + +/proc/contains_az09(var/input) + for(var/i=1, i<=length(input), i++) + var/ascii_char = text2ascii(input,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + return 1 + // a .. z + if(97 to 122) //Lowercase Letters + return 1 + + // 0 .. 9 + if(48 to 57) //Numbers + return 1 + return 0 + +/** + * Strip out the special beyond characters for \proper and \improper + * from text that will be sent to the browser. + */ +/proc/strip_improper(var/text) + return replacetext(replacetext(text, "\proper", ""), "\improper", "") + +/proc/pencode2html(t) + t = replacetext(t, "\n", "
    ") + t = replacetext(t, "\[center\]", "

    ") + t = replacetext(t, "\[/center\]", "
    ") + t = replacetext(t, "\[br\]", "
    ") + t = replacetext(t, "\[b\]", "") + t = replacetext(t, "\[/b\]", "") + t = replacetext(t, "\[i\]", "") + t = replacetext(t, "\[/i\]", "") + t = replacetext(t, "\[u\]", "") + t = replacetext(t, "\[/u\]", "") + t = replacetext(t, "\[time\]", "[stationtime2text()]") + t = replacetext(t, "\[date\]", "[stationdate2text()]") + t = replacetext(t, "\[large\]", "") + t = replacetext(t, "\[/large\]", "") + t = replacetext(t, "\[field\]", "") + t = replacetext(t, "\[h1\]", "

    ") + t = replacetext(t, "\[/h1\]", "

    ") + t = replacetext(t, "\[h2\]", "

    ") + t = replacetext(t, "\[/h2\]", "

    ") + t = replacetext(t, "\[h3\]", "

    ") + t = replacetext(t, "\[/h3\]", "

    ") + t = replacetext(t, "\[*\]", "
  • ") + t = replacetext(t, "\[hr\]", "
    ") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + t = replacetext(t, "\[list\]", "
      ") + t = replacetext(t, "\[/list\]", "
    ") + t = replacetext(t, "\[table\]", "") + t = replacetext(t, "\[/table\]", "
    ") + t = replacetext(t, "\[grid\]", "") + t = replacetext(t, "\[/grid\]", "
    ") + t = replacetext(t, "\[row\]", "") + t = replacetext(t, "\[cell\]", "") + t = replacetext(t, "\[logo\]", "") + t = replacetext(t, "\[redlogo\]", "") + t = replacetext(t, "\[sglogo\]", "") + t = replacetext(t, "\[editorbr\]", "") + return t + +//pencode translation to html for tags exclusive to digital files (currently email, nanoword, report editor fields, +//modular scanner data and txt file printing) and prints from them +/proc/digitalPencode2html(var/text) + text = replacetext(text, "\[pre\]", "
    ")
    +	text = replacetext(text, "\[/pre\]", "
    ") + text = replacetext(text, "\[fontred\]", "") // to pass html tag integrity unit test + text = replacetext(text, "\[fontblue\]", "")// to pass html tag integrity unit test + text = replacetext(text, "\[fontgreen\]", "") + text = replacetext(text, "\[/font\]", "") + text = replacetext(text, "\[redacted\]", "R E D A C T E D") + return pencode2html(text) + +//Will kill most formatting; not recommended. +/proc/html2pencode(t) + t = replacetext(t, "
    ", "\[pre\]")
    +	t = replacetext(t, "
    ", "\[/pre\]") + t = replacetext(t, "", "\[fontred\]")// to pass html tag integrity unit test + t = replacetext(t, "", "\[fontblue\]")// to pass html tag integrity unit test + t = replacetext(t, "", "\[fontgreen\]") + t = replacetext(t, "", "\[/font\]") + t = replacetext(t, "
    ", "\[br\]") + t = replacetext(t, "
    ", "\[br\]") + t = replacetext(t, "", "\[b\]") + t = replacetext(t, "", "\[/b\]") + t = replacetext(t, "", "\[i\]") + t = replacetext(t, "", "\[/i\]") + t = replacetext(t, "", "\[u\]") + t = replacetext(t, "", "\[/u\]") + t = replacetext(t, "
    ", "\[center\]") + t = replacetext(t, "
    ", "\[/center\]") + t = replacetext(t, "

    ", "\[h1\]") + t = replacetext(t, "

    ", "\[/h1\]") + t = replacetext(t, "

    ", "\[h2\]") + t = replacetext(t, "

    ", "\[/h2\]") + t = replacetext(t, "

    ", "\[h3\]") + t = replacetext(t, "

    ", "\[/h3\]") + t = replacetext(t, "
  • ", "\[*\]") + t = replacetext(t, "
    ", "\[hr\]") + t = replacetext(t, "
      ", "\[list\]") + t = replacetext(t, "
    ", "\[/list\]") + t = replacetext(t, "", "\[grid\]") + t = replacetext(t, "
    ", "\[/grid\]") + t = replacetext(t, "", "\[row\]") + t = replacetext(t, "", "\[cell\]") + t = replacetext(t, "", "\[logo\]") + t = replacetext(t, "", "\[redlogo\]") + t = replacetext(t, "", "\[sglogo\]") + t = replacetext(t, "", "\[field\]") + t = replacetext(t, "R E D A C T E D", "\[redacted\]") + t = strip_html_properly(t) + return t + +// Random password generator +/proc/GenerateKey() + //Feel free to move to Helpers. + var/newKey + newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") + newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") + newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") + return newKey + +//Used for applying byonds text macros to strings that are loaded at runtime +/proc/apply_text_macros(string) + var/next_backslash = findtext(string, "\\") + if(!next_backslash) + return string + + var/leng = length(string) + + var/next_space = findtext(string, " ", next_backslash + 1) + if(!next_space) + next_space = leng - next_backslash + + if(!next_space) //trailing bs + return string + + var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash) + var/macro = lowertext(copytext(string, next_backslash + 1, next_space)) + var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1) + + //See http://www.byond.com/docs/ref/info.html#/DM/text/macros + switch(macro) + //prefixes/agnostic + if("the") + rest = text("\the []", rest) + if("a") + rest = text("\a []", rest) + if("an") + rest = text("\an []", rest) + if("proper") + rest = text("\proper []", rest) + if("improper") + rest = text("\improper []", rest) + if("roman") + rest = text("\roman []", rest) + //postfixes + if("th") + base = text("[]\th", rest) + if("s") + base = text("[]\s", rest) + if("he") + base = text("[]\he", rest) + if("she") + base = text("[]\she", rest) + if("his") + base = text("[]\his", rest) + if("himself") + base = text("[]\himself", rest) + if("herself") + base = text("[]\herself", rest) + if("hers") + base = text("[]\hers", rest) + + . = base + if(rest) + . += .(rest) + + +#define gender2text(gender) capitalize(gender) + +/** + * Used to get a properly sanitized input. Returns null if cancel is pressed. + * + * Arguments + ** user - Target of the input prompt. + ** message - The text inside of the prompt. + ** title - The window title of the prompt. + ** max_length - If you intend to impose a length limit - default is 1024. + ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. +*/ +/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/user_input = input(user, message, title, default) as text|null + if(isnull(user_input)) // User pressed cancel + return + if(no_trim) + return copytext(html_encode(user_input), 1, max_length) + else + return trim(html_encode(user_input), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) + +/** + * Used to get a properly sanitized input in a larger box. Works very similarly to stripped_input. + * + * Arguments + ** user - Target of the input prompt. + ** message - The text inside of the prompt. + ** title - The window title of the prompt. + ** max_length - If you intend to impose a length limit - default is 1024. + ** no_trim - Prevents the input from being trimmed if you intend to parse newlines or whitespace. +*/ +/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/user_input = input(user, message, title, default) as message|null + if(isnull(user_input)) // User pressed cancel + return + if(no_trim) + return copytext(html_encode(user_input), 1, max_length) + else + return trim(html_encode(user_input), max_length) + +//Adds 'char' ahead of 'text' until there are 'count' characters total +/proc/add_leading(text, count, char = " ") + text = "[text]" + var/charcount = count - length_char(text) + var/list/chars_to_add[max(charcount + 1, 0)] + return jointext(chars_to_add, char) + text + +//Adds 'char' behind 'text' until there are 'count' characters total +/proc/add_trailing(text, count, char = " ") + text = "[text]" + var/charcount = count - length_char(text) + var/list/chars_to_add[max(charcount + 1, 0)] + return text + jointext(chars_to_add, char) + +//Readds quotes and apostrophes to HTML-encoded strings +/proc/readd_quotes(var/t) + var/list/repl_chars = list(""" = "\"","'" = "'") + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) + index = findtext(t, char) + return t + +// Rips out paper HTML but tries to keep it semi-readable. +/proc/paper_html_to_plaintext(paper_text) + paper_text = replacetext(paper_text, "
    ", "-----") + paper_text = replacetext(paper_text, "
  • ", "- ") // This makes ordered lists turn into unordered but fixing that is too much effort. + paper_text = replacetext(paper_text, "
  • ", "\n") + paper_text = replacetext(paper_text, "

    ", "\n") + paper_text = replacetext(paper_text, "
    ", "\n") + paper_text = strip_html_properly(paper_text) // Get rid of everything else entirely. + return paper_text diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index b07fb316bb4..802359a1b76 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -1,169 +1,169 @@ -#define TimeOfGame (get_game_time()) -#define TimeOfTick (TICK_USAGE*0.01*world.tick_lag) - -#define DS2NEARESTTICK(DS) TICKS2DS(-round(-(DS2TICKS(DS)))) - -var/world_startup_time - -/proc/get_game_time() - var/global/time_offset = 0 - var/global/last_time = 0 - var/global/last_usage = 0 - - var/wtime = world.time - var/wusage = TICK_USAGE * 0.01 - - if(last_time < wtime && last_usage > 1) - time_offset += last_usage - 1 - - last_time = wtime - last_usage = wusage - - return wtime + (time_offset + wusage) * world.tick_lag - -GLOBAL_VAR_INIT(roundstart_hour, pick(2,7,12,17)) -var/station_date = "" -var/next_station_date_change = 1 DAY - -#define duration2stationtime(time) time2text(station_time_in_ds + time, "hh:mm") -#define roundstart_delay_time (world.time - round_duration_in_ds) -#define world_time_in_ds(time) (GLOB.roundstart_hour HOURS + time - roundstart_delay_time) -#define round_duration_in_ds (GLOB.round_start_time ? REALTIMEOFDAY - GLOB.round_start_time : 0) -#define station_time_in_ds (GLOB.roundstart_hour HOURS + round_duration_in_ds) - -/proc/stationtime2text() - return time2text(station_time_in_ds + GLOB.timezoneOffset, "hh:mm") - -/proc/worldtime2stationtime(time) - return time2text(world_time_in_ds(time) + GLOB.timezoneOffset, "hh:mm") - -/proc/stationdate2text() - var/update_time = FALSE - if(station_time_in_ds > next_station_date_change) - next_station_date_change += 1 DAY - update_time = TRUE - if(!station_date || update_time) - station_date = num2text((text2num(time2text(REALTIMEOFDAY, "YYYY"))+300)) + "-" + time2text(REALTIMEOFDAY, "MM-DD") //VOREStation Edit - return station_date - -//ISO 8601 -/proc/time_stamp() - var/date_portion = time2text(world.timeofday, "YYYY-MM-DD") - var/time_portion = time2text(world.timeofday, "hh:mm:ss") - return "[date_portion]T[time_portion]" - -/proc/get_timezone_offset() - var/midnight_gmt_here = text2num(time2text(0,"hh")) * 36000 - if(midnight_gmt_here > 12 HOURS) - return 24 HOURS - midnight_gmt_here - else - return midnight_gmt_here - -/proc/gameTimestamp(format = "hh:mm:ss", wtime=null) - if(!wtime) - wtime = world.time - return time2text(wtime - GLOB.timezoneOffset, format) - -/* Returns 1 if it is the selected month and day */ -/proc/isDay(var/month, var/day) - if(isnum(month) && isnum(day)) - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - if(month == MM && day == DD) - return 1 - - // Uncomment this out when debugging! - //else - //return 1 - -var/next_duration_update = 0 -var/last_round_duration = 0 -GLOBAL_VAR_INIT(round_start_time, 0) - -/hook/roundstart/proc/start_timer() - GLOB.round_start_time = REALTIMEOFDAY - return 1 - -/proc/roundduration2text() - if(!GLOB.round_start_time) - return "00:00" - if(last_round_duration && world.time < next_duration_update) - return last_round_duration - - var/mills = round_duration_in_ds // 1/10 of a second, not real milliseconds but whatever - //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something - var/mins = round((mills % 36000) / 600) - var/hours = round(mills / 36000) - - mins = mins < 10 ? add_zero(mins, 1) : mins - hours = hours < 10 ? add_zero(hours, 1) : hours - - last_round_duration = "[hours]:[mins]" - next_duration_update = world.time + 1 MINUTES - return last_round_duration - -/var/midnight_rollovers = 0 -/var/rollovercheck_last_timeofday = 0 -/var/rollover_safety_date = 0 // set in world/New to the server startup day-of-month -/proc/update_midnight_rollover() - // Day has wrapped (world.timeofday drops to 0 at the start of each real day) - if (world.timeofday < rollovercheck_last_timeofday) - // If the day started/last wrap was < 12 hours ago, this is spurious - if(rollover_safety_date < world.realtime - (12 HOURS)) - midnight_rollovers++ - rollover_safety_date = world.realtime - else - warning("Time rollover error: world.timeofday decreased from previous check, but the day or last rollover is less than 12 hours old. System clock?") - rollovercheck_last_timeofday = world.timeofday - return midnight_rollovers - -//Increases delay as the server gets more overloaded, -//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful -#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) - -//returns the number of ticks slept -/proc/stoplag(initial_delay) - if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT)) - sleep(world.tick_lag) - return 1 - if (!initial_delay) - initial_delay = world.tick_lag - . = 0 - var/i = DS2TICKS(initial_delay) - do - . += CEILING(i*DELTA_CALC, 1) - sleep(i*world.tick_lag*DELTA_CALC) - i *= 2 - while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) - -#undef DELTA_CALC - - -//Takes a value of time in deciseconds. -//Returns a text value of that number in hours, minutes, or seconds. -/proc/DisplayTimeText(time_value, round_seconds_to = 0.1) - var/second = round(time_value * 0.1, round_seconds_to) - if(!second) - return "right now" - if(second < 60) - return "[second] second[(second != 1)? "s":""]" - var/minute = FLOOR(second / 60, 1) - second = MODULUS(second, 60) - var/secondT - if(second) - secondT = " and [second] second[(second != 1)? "s":""]" - if(minute < 60) - return "[minute] minute[(minute != 1)? "s":""][secondT]" - var/hour = FLOOR(minute / 60, 1) - minute = MODULUS(minute, 60) - var/minuteT - if(minute) - minuteT = " and [minute] minute[(minute != 1)? "s":""]" - if(hour < 24) - return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]" - var/day = FLOOR(hour / 24, 1) - hour = MODULUS(hour, 24) - var/hourT - if(hour) - hourT = " and [hour] hour[(hour != 1)? "s":""]" - return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]" +#define TimeOfGame (get_game_time()) +#define TimeOfTick (TICK_USAGE*0.01*world.tick_lag) + +#define DS2NEARESTTICK(DS) TICKS2DS(-round(-(DS2TICKS(DS)))) + +var/world_startup_time + +/proc/get_game_time() + var/global/time_offset = 0 + var/global/last_time = 0 + var/global/last_usage = 0 + + var/wtime = world.time + var/wusage = TICK_USAGE * 0.01 + + if(last_time < wtime && last_usage > 1) + time_offset += last_usage - 1 + + last_time = wtime + last_usage = wusage + + return wtime + (time_offset + wusage) * world.tick_lag + +GLOBAL_VAR_INIT(roundstart_hour, pick(2,7,12,17)) +var/station_date = "" +var/next_station_date_change = 1 DAY + +#define duration2stationtime(time) time2text(station_time_in_ds + time, "hh:mm") +#define roundstart_delay_time (world.time - round_duration_in_ds) +#define world_time_in_ds(time) (GLOB.roundstart_hour HOURS + time - roundstart_delay_time) +#define round_duration_in_ds (GLOB.round_start_time ? REALTIMEOFDAY - GLOB.round_start_time : 0) +#define station_time_in_ds (GLOB.roundstart_hour HOURS + round_duration_in_ds) + +/proc/stationtime2text() + return time2text(station_time_in_ds + GLOB.timezoneOffset, "hh:mm") + +/proc/worldtime2stationtime(time) + return time2text(world_time_in_ds(time) + GLOB.timezoneOffset, "hh:mm") + +/proc/stationdate2text() + var/update_time = FALSE + if(station_time_in_ds > next_station_date_change) + next_station_date_change += 1 DAY + update_time = TRUE + if(!station_date || update_time) + station_date = num2text((text2num(time2text(REALTIMEOFDAY, "YYYY"))+300)) + "-" + time2text(REALTIMEOFDAY, "MM-DD") //VOREStation Edit + return station_date + +//ISO 8601 +/proc/time_stamp() + var/date_portion = time2text(world.timeofday, "YYYY-MM-DD") + var/time_portion = time2text(world.timeofday, "hh:mm:ss") + return "[date_portion]T[time_portion]" + +/proc/get_timezone_offset() + var/midnight_gmt_here = text2num(time2text(0,"hh")) * 36000 + if(midnight_gmt_here > 12 HOURS) + return 24 HOURS - midnight_gmt_here + else + return midnight_gmt_here + +/proc/gameTimestamp(format = "hh:mm:ss", wtime=null) + if(!wtime) + wtime = world.time + return time2text(wtime - GLOB.timezoneOffset, format) + +/* Returns 1 if it is the selected month and day */ +/proc/isDay(var/month, var/day) + if(isnum(month) && isnum(day)) + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + if(month == MM && day == DD) + return 1 + + // Uncomment this out when debugging! + //else + //return 1 + +var/next_duration_update = 0 +var/last_round_duration = 0 +GLOBAL_VAR_INIT(round_start_time, 0) + +/hook/roundstart/proc/start_timer() + GLOB.round_start_time = REALTIMEOFDAY + return 1 + +/proc/roundduration2text() + if(!GLOB.round_start_time) + return "00:00" + if(last_round_duration && world.time < next_duration_update) + return last_round_duration + + var/mills = round_duration_in_ds // 1/10 of a second, not real milliseconds but whatever + //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something + var/mins = round((mills % 36000) / 600) + var/hours = round(mills / 36000) + + mins = mins < 10 ? add_zero(mins, 1) : mins + hours = hours < 10 ? add_zero(hours, 1) : hours + + last_round_duration = "[hours]:[mins]" + next_duration_update = world.time + 1 MINUTES + return last_round_duration + +/var/midnight_rollovers = 0 +/var/rollovercheck_last_timeofday = 0 +/var/rollover_safety_date = 0 // set in world/New to the server startup day-of-month +/proc/update_midnight_rollover() + // Day has wrapped (world.timeofday drops to 0 at the start of each real day) + if (world.timeofday < rollovercheck_last_timeofday) + // If the day started/last wrap was < 12 hours ago, this is spurious + if(rollover_safety_date < world.realtime - (12 HOURS)) + midnight_rollovers++ + rollover_safety_date = world.realtime + else + warning("Time rollover error: world.timeofday decreased from previous check, but the day or last rollover is less than 12 hours old. System clock?") + rollovercheck_last_timeofday = world.timeofday + return midnight_rollovers + +//Increases delay as the server gets more overloaded, +//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful +#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) + +//returns the number of ticks slept +/proc/stoplag(initial_delay) + if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT)) + sleep(world.tick_lag) + return 1 + if (!initial_delay) + initial_delay = world.tick_lag + . = 0 + var/i = DS2TICKS(initial_delay) + do + . += CEILING(i*DELTA_CALC, 1) + sleep(i*world.tick_lag*DELTA_CALC) + i *= 2 + while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) + +#undef DELTA_CALC + + +//Takes a value of time in deciseconds. +//Returns a text value of that number in hours, minutes, or seconds. +/proc/DisplayTimeText(time_value, round_seconds_to = 0.1) + var/second = round(time_value * 0.1, round_seconds_to) + if(!second) + return "right now" + if(second < 60) + return "[second] second[(second != 1)? "s":""]" + var/minute = FLOOR(second / 60, 1) + second = MODULUS(second, 60) + var/secondT + if(second) + secondT = " and [second] second[(second != 1)? "s":""]" + if(minute < 60) + return "[minute] minute[(minute != 1)? "s":""][secondT]" + var/hour = FLOOR(minute / 60, 1) + minute = MODULUS(minute, 60) + var/minuteT + if(minute) + minuteT = " and [minute] minute[(minute != 1)? "s":""]" + if(hour < 24) + return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]" + var/day = FLOOR(hour / 24, 1) + hour = MODULUS(hour, 24) + var/hourT + if(hour) + hourT = " and [hour] hour[(hour != 1)? "s":""]" + return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]" diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index e21d973b1b6..8bd45bbfc76 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -1,425 +1,425 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * file2list - * angle2dir - */ - -// Returns an integer given a hexadecimal number string as input. -/proc/hex2num(hex) - if (!istext(hex)) - return - - var/num = 0 - var/power = 1 - var/i = length(hex) - - while (i) - var/char = text2ascii(hex, i) - switch(char) - if(48) // 0 -- do nothing - if(49 to 57) num += (char - 48) * power // 1-9 - if(97, 65) num += power * 10 // A - if(98, 66) num += power * 11 // B - if(99, 67) num += power * 12 // C - if(100, 68) num += power * 13 // D - if(101, 69) num += power * 14 // E - if(102, 70) num += power * 15 // F - else - return - power *= 16 - i-- - return num - -// Returns the hex value of a number given a value assumed to be a base-ten value -/proc/num2hex(num, padlength) - var/global/list/hexdigits = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") - - . = "" - while(num > 0) - var/hexdigit = hexdigits[(num & 0xF) + 1] - . = "[hexdigit][.]" - num >>= 4 //go to the next half-byte - - //pad with zeroes - var/left = padlength - length(.) - while (left-- > 0) - . = "0[.]" - -/proc/text2numlist(text, delimiter="\n") - var/list/num_list = list() - for(var/x in splittext(text, delimiter)) - num_list += text2num(x) - return num_list - -// Splits the text of a file at seperator and returns them in a list. -/proc/file2list(filename, seperator="\n") - return splittext(return_file_text(filename),seperator) - -// Turns a direction into text -/proc/num2dir(direction) - switch (direction) - if (1.0) return NORTH - if (2.0) return SOUTH - if (4.0) return EAST - if (8.0) return WEST - else - to_world_log("UNKNOWN DIRECTION: [direction]") - -// Turns a direction into text -/proc/dir2text(direction) - switch (direction) - if (NORTH) return "north" - if (SOUTH) return "south" - if (EAST) return "east" - if (WEST) return "west" - if (NORTHEAST) return "northeast" - if (SOUTHEAST) return "southeast" - if (NORTHWEST) return "northwest" - if (SOUTHWEST) return "southwest" - if (UP) return "up" - if (DOWN) return "down" - -// Turns text into proper directions -/proc/text2dir(direction) - switch (uppertext(direction)) - if ("NORTH") return 1 - if ("SOUTH") return 2 - if ("EAST") return 4 - if ("WEST") return 8 - if ("NORTHEAST") return 5 - if ("NORTHWEST") return 9 - if ("SOUTHEAST") return 6 - if ("SOUTHWEST") return 10 - -// Turns a direction into text showing all bits set -/proc/dirs2text(direction) - if(!direction) - return "" - var/list/dirs = list() - if(direction & NORTH) - dirs += "NORTH" - if(direction & SOUTH) - dirs += "SOUTH" - if(direction & EAST) - dirs += "EAST" - if(direction & WEST) - dirs += "WEST" - if(direction & UP) - dirs += "UP" - if(direction & DOWN) - dirs += "DOWN" - return dirs.Join(" ") - -// Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(var/degree) - degree = (degree + 22.5) % 365 // 22.5 = 45 / 2 - if (degree < 45) return NORTH - if (degree < 90) return NORTHEAST - if (degree < 135) return EAST - if (degree < 180) return SOUTHEAST - if (degree < 225) return SOUTH - if (degree < 270) return SOUTHWEST - if (degree < 315) return WEST - return NORTH|WEST - -// Returns the north-zero clockwise angle in degrees, given a direction -/proc/dir2angle(var/D) - switch (D) - if (NORTH) return 0 - if (SOUTH) return 180 - if (EAST) return 90 - if (WEST) return 270 - if (NORTHEAST) return 45 - if (SOUTHEAST) return 135 - if (NORTHWEST) return 315 - if (SOUTHWEST) return 225 - -// Converts a blend_mode constant to one acceptable to icon.Blend() -/proc/blendMode2iconMode(blend_mode) - switch (blend_mode) - if (BLEND_MULTIPLY) return ICON_MULTIPLY - if (BLEND_ADD) return ICON_ADD - if (BLEND_SUBTRACT) return ICON_SUBTRACT - else return ICON_OVERLAY - -// Converts a rights bitfield into a string -/proc/rights2text(rights,seperator="") - if (rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" - if (rights & R_ADMIN) . += "[seperator]+ADMIN" - if (rights & R_BAN) . += "[seperator]+BAN" - if (rights & R_FUN) . += "[seperator]+FUN" - if (rights & R_SERVER) . += "[seperator]+SERVER" - if (rights & R_DEBUG) . += "[seperator]+DEBUG" - if (rights & R_POSSESS) . += "[seperator]+POSSESS" - if (rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" - if (rights & R_STEALTH) . += "[seperator]+STEALTH" - if (rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" - if (rights & R_VAREDIT) . += "[seperator]+VAREDIT" - if (rights & R_SOUNDS) . += "[seperator]+SOUND" - if (rights & R_SPAWN) . += "[seperator]+SPAWN" - if (rights & R_MOD) . += "[seperator]+MODERATOR" - if (rights & R_EVENT) . += "[seperator]+EVENT" - return . - -// Converts a hexadecimal color (e.g. #FF0050) to a list of numbers for red, green, and blue (e.g. list(255,0,80) ). -/proc/hex2rgb(hex) - // Strips the starting #, in case this is ever supplied without one, so everything doesn't break. - if(findtext(hex,"#",1,2)) - hex = copytext(hex, 2) - return list(hex2rgb_r(hex), hex2rgb_g(hex), hex2rgb_b(hex)) - -// The three procs below require that the '#' part of the hex be stripped, which hex2rgb() does automatically. -/proc/hex2rgb_r(hex) - var/hex_to_work_on = copytext(hex,1,3) - return hex2num(hex_to_work_on) - -/proc/hex2rgb_g(hex) - var/hex_to_work_on = copytext(hex,3,5) - return hex2num(hex_to_work_on) - -/proc/hex2rgb_b(hex) - var/hex_to_work_on = copytext(hex,5,7) - return hex2num(hex_to_work_on) - -// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ -/proc/heat2color(temp) - return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) - -/proc/heat2color_r(temp) - temp /= 100 - if(temp <= 66) - . = 255 - else - . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) - -/proc/heat2color_g(temp) - temp /= 100 - if(temp <= 66) - . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) - else - . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) - -/proc/heat2color_b(temp) - temp /= 100 - if(temp >= 66) - . = 255 - else - if(temp <= 16) - . = 0 - else - . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) - -// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. -// returns "YYYY-MM-DD" by default -/proc/unix2date(timestamp, seperator = "-") - if(timestamp < 0) - return 0 //Do not accept negative values - - var/const/dayInSeconds = 86400 //60secs*60mins*24hours - var/const/daysInYear = 365 //Non Leap Year - var/const/daysInLYear = daysInYear + 1//Leap year - var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc - var/year = 1970 //Unix Epoc begins 1970-01-01 - var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1 - var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code*** - var/month = 1 //This will be the returned MONTH NUMBER. - var/day //This will be the returned day number. - - while(tmpDays > daysInYear) //Start adding years to 1970 - year++ - if(isLeap(year)) - tmpDays -= daysInLYear - else - tmpDays -= daysInYear - - if(isLeap(year)) //The year is a leap year - monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334) - else - monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334) - - var/mDays = 0; - var/monthIndex = 0; - - for(var/m in monthsInDays) - monthIndex++ - if(tmpDays > m) - mDays = m - month = monthIndex - - day = tmpDays - mDays //Setup the date - - return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]" - -/proc/isLeap(y) - return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) - -//Takes a string and a datum -//The string is well, obviously the string being checked -//The datum is used as a source for var names, to check validity -//Otherwise every single word could technically be a variable! -/proc/string2listofvars(var/t_string, var/datum/var_source) - if(!t_string || !var_source) - return list() - - . = list() - - var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check - if(var_found) - //Find var names - - // "A dog said hi [name]!" - // splittext() --> list("A dog said hi ","name]!" - // jointext() --> "A dog said hi name]!" - // splittext() --> list("A","dog","said","hi","name]!") - - t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios - var/list/list_value = splittext(t_string,"\[") - var/intermediate_stage = jointext(list_value, null) - - list_value = splittext(intermediate_stage," ") - for(var/value in list_value) - if(findtext(value,"]")) - value = splittext(value,"]") //"name]!" --> list("name","!") - for(var/A in value) - if(var_source.vars.Find(A)) - . += A - -/proc/get_end_section_of_type(type) - var/strtype = "[type]" - var/delim_pos = findlasttext(strtype, "/") - if(delim_pos == 0) - return strtype - return copytext(strtype, delim_pos) - -// Concatenates a list of strings into a single string. A seperator may optionally be provided. -/proc/list2text(list/ls, sep) - if (ls.len <= 1) // Early-out code for empty or singleton lists. - return ls.len ? ls[1] : "" - - var/l = ls.len // Made local for sanic speed. - var/i = 0 // Incremented every time a list index is accessed. - - if (sep != null) - // Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc... - #define S1 sep, ls[++i] - #define S4 S1, S1, S1, S1 - #define S16 S4, S4, S4, S4 - #define S64 S16, S16, S16, S16 - - . = "[ls[++i]]" // Make sure the initial element is converted to text. - - // Having the small concatenations come before the large ones boosted speed by an average of at least 5%. - if (l-1 & 0x01) // 'i' will always be 1 here. - . = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2. - if (l-i & 0x02) - . = text("[][][][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. - if (l-i & 0x04) - . = text("[][][][][][][][][]", ., S4) // And so on.... - if (l-i & 0x08) - . = text("[][][][][][][][][][][][][][][][][]", ., S4, S4) - if (l-i & 0x10) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16) - if (l-i & 0x20) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) - if (l-i & 0x40) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) - while (l > i) // Chomp through the rest of the list, 128 elements at a time. - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) - - #undef S64 - #undef S16 - #undef S4 - #undef S1 - else - // Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc... - #define S1 ls[++i] - #define S4 S1, S1, S1, S1 - #define S16 S4, S4, S4, S4 - #define S64 S16, S16, S16, S16 - - . = "[ls[++i]]" // Make sure the initial element is converted to text. - - if (l-1 & 0x01) // 'i' will always be 1 here. - . += S1 // Append 1 element if the remaining elements are not a multiple of 2. - if (l-i & 0x02) - . = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. - if (l-i & 0x04) - . = text("[][][][][]", ., S4) // And so on... - if (l-i & 0x08) - . = text("[][][][][][][][][]", ., S4, S4) - if (l-i & 0x10) - . = text("[][][][][][][][][][][][][][][][][]", ., S16) - if (l-i & 0x20) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) - if (l-i & 0x40) - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) - while (l > i) // Chomp through the rest of the list, 128 elements at a time. - . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ - [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) - - #undef S64 - #undef S16 - #undef S4 - #undef S1 - -// Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator) -/proc/text2list(text, delimiter="\n") - var/delim_len = length(delimiter) - if (delim_len < 1) - return list(text) - - . = list() - var/last_found = 1 - var/found - - do - found = findtext(text, delimiter, last_found, 0) - . += copytext(text, last_found, found) - last_found = found + delim_len - while (found) - -/proc/type2parent(child) - var/string_type = "[child]" - var/last_slash = findlasttext(string_type, "/") - if (last_slash != 1) - return text2path(copytext(string_type, 1, last_slash)) - switch (child) - if (/datum) - return null - if (/obj, /mob) - return /atom/movable - if (/area, /turf) - return /atom - else - return /datum - - -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/safe_file2text(filename, error_on_invalid_return = TRUE) - try - if(fexists(filename)) - . = file2text(filename) - if(!. && error_on_invalid_return) - error("File empty ([filename])") - else if(error_on_invalid_return) - error("File not found ([filename])") - catch(var/exception/E) - if(error_on_invalid_return) - error("Exception when loading file as string: [E]") +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * file2list + * angle2dir + */ + +// Returns an integer given a hexadecimal number string as input. +/proc/hex2num(hex) + if (!istext(hex)) + return + + var/num = 0 + var/power = 1 + var/i = length(hex) + + while (i) + var/char = text2ascii(hex, i) + switch(char) + if(48) // 0 -- do nothing + if(49 to 57) num += (char - 48) * power // 1-9 + if(97, 65) num += power * 10 // A + if(98, 66) num += power * 11 // B + if(99, 67) num += power * 12 // C + if(100, 68) num += power * 13 // D + if(101, 69) num += power * 14 // E + if(102, 70) num += power * 15 // F + else + return + power *= 16 + i-- + return num + +// Returns the hex value of a number given a value assumed to be a base-ten value +/proc/num2hex(num, padlength) + var/global/list/hexdigits = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") + + . = "" + while(num > 0) + var/hexdigit = hexdigits[(num & 0xF) + 1] + . = "[hexdigit][.]" + num >>= 4 //go to the next half-byte + + //pad with zeroes + var/left = padlength - length(.) + while (left-- > 0) + . = "0[.]" + +/proc/text2numlist(text, delimiter="\n") + var/list/num_list = list() + for(var/x in splittext(text, delimiter)) + num_list += text2num(x) + return num_list + +// Splits the text of a file at seperator and returns them in a list. +/proc/file2list(filename, seperator="\n") + return splittext(return_file_text(filename),seperator) + +// Turns a direction into text +/proc/num2dir(direction) + switch (direction) + if (1.0) return NORTH + if (2.0) return SOUTH + if (4.0) return EAST + if (8.0) return WEST + else + to_world_log("UNKNOWN DIRECTION: [direction]") + +// Turns a direction into text +/proc/dir2text(direction) + switch (direction) + if (NORTH) return "north" + if (SOUTH) return "south" + if (EAST) return "east" + if (WEST) return "west" + if (NORTHEAST) return "northeast" + if (SOUTHEAST) return "southeast" + if (NORTHWEST) return "northwest" + if (SOUTHWEST) return "southwest" + if (UP) return "up" + if (DOWN) return "down" + +// Turns text into proper directions +/proc/text2dir(direction) + switch (uppertext(direction)) + if ("NORTH") return 1 + if ("SOUTH") return 2 + if ("EAST") return 4 + if ("WEST") return 8 + if ("NORTHEAST") return 5 + if ("NORTHWEST") return 9 + if ("SOUTHEAST") return 6 + if ("SOUTHWEST") return 10 + +// Turns a direction into text showing all bits set +/proc/dirs2text(direction) + if(!direction) + return "" + var/list/dirs = list() + if(direction & NORTH) + dirs += "NORTH" + if(direction & SOUTH) + dirs += "SOUTH" + if(direction & EAST) + dirs += "EAST" + if(direction & WEST) + dirs += "WEST" + if(direction & UP) + dirs += "UP" + if(direction & DOWN) + dirs += "DOWN" + return dirs.Join(" ") + +// Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(var/degree) + degree = (degree + 22.5) % 365 // 22.5 = 45 / 2 + if (degree < 45) return NORTH + if (degree < 90) return NORTHEAST + if (degree < 135) return EAST + if (degree < 180) return SOUTHEAST + if (degree < 225) return SOUTH + if (degree < 270) return SOUTHWEST + if (degree < 315) return WEST + return NORTH|WEST + +// Returns the north-zero clockwise angle in degrees, given a direction +/proc/dir2angle(var/D) + switch (D) + if (NORTH) return 0 + if (SOUTH) return 180 + if (EAST) return 90 + if (WEST) return 270 + if (NORTHEAST) return 45 + if (SOUTHEAST) return 135 + if (NORTHWEST) return 315 + if (SOUTHWEST) return 225 + +// Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch (blend_mode) + if (BLEND_MULTIPLY) return ICON_MULTIPLY + if (BLEND_ADD) return ICON_ADD + if (BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY + +// Converts a rights bitfield into a string +/proc/rights2text(rights,seperator="") + if (rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" + if (rights & R_ADMIN) . += "[seperator]+ADMIN" + if (rights & R_BAN) . += "[seperator]+BAN" + if (rights & R_FUN) . += "[seperator]+FUN" + if (rights & R_SERVER) . += "[seperator]+SERVER" + if (rights & R_DEBUG) . += "[seperator]+DEBUG" + if (rights & R_POSSESS) . += "[seperator]+POSSESS" + if (rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" + if (rights & R_STEALTH) . += "[seperator]+STEALTH" + if (rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" + if (rights & R_VAREDIT) . += "[seperator]+VAREDIT" + if (rights & R_SOUNDS) . += "[seperator]+SOUND" + if (rights & R_SPAWN) . += "[seperator]+SPAWN" + if (rights & R_MOD) . += "[seperator]+MODERATOR" + if (rights & R_EVENT) . += "[seperator]+EVENT" + return . + +// Converts a hexadecimal color (e.g. #FF0050) to a list of numbers for red, green, and blue (e.g. list(255,0,80) ). +/proc/hex2rgb(hex) + // Strips the starting #, in case this is ever supplied without one, so everything doesn't break. + if(findtext(hex,"#",1,2)) + hex = copytext(hex, 2) + return list(hex2rgb_r(hex), hex2rgb_g(hex), hex2rgb_b(hex)) + +// The three procs below require that the '#' part of the hex be stripped, which hex2rgb() does automatically. +/proc/hex2rgb_r(hex) + var/hex_to_work_on = copytext(hex,1,3) + return hex2num(hex_to_work_on) + +/proc/hex2rgb_g(hex) + var/hex_to_work_on = copytext(hex,3,5) + return hex2num(hex_to_work_on) + +/proc/hex2rgb_b(hex) + var/hex_to_work_on = copytext(hex,5,7) + return hex2num(hex_to_work_on) + +// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ +/proc/heat2color(temp) + return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) + +/proc/heat2color_r(temp) + temp /= 100 + if(temp <= 66) + . = 255 + else + . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) + +/proc/heat2color_g(temp) + temp /= 100 + if(temp <= 66) + . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) + else + . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) + +/proc/heat2color_b(temp) + temp /= 100 + if(temp >= 66) + . = 255 + else + if(temp <= 16) + . = 0 + else + . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) + +// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. +// returns "YYYY-MM-DD" by default +/proc/unix2date(timestamp, seperator = "-") + if(timestamp < 0) + return 0 //Do not accept negative values + + var/const/dayInSeconds = 86400 //60secs*60mins*24hours + var/const/daysInYear = 365 //Non Leap Year + var/const/daysInLYear = daysInYear + 1//Leap year + var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc + var/year = 1970 //Unix Epoc begins 1970-01-01 + var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1 + var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code*** + var/month = 1 //This will be the returned MONTH NUMBER. + var/day //This will be the returned day number. + + while(tmpDays > daysInYear) //Start adding years to 1970 + year++ + if(isLeap(year)) + tmpDays -= daysInLYear + else + tmpDays -= daysInYear + + if(isLeap(year)) //The year is a leap year + monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334) + else + monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334) + + var/mDays = 0; + var/monthIndex = 0; + + for(var/m in monthsInDays) + monthIndex++ + if(tmpDays > m) + mDays = m + month = monthIndex + + day = tmpDays - mDays //Setup the date + + return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]" + +/proc/isLeap(y) + return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + +//Takes a string and a datum +//The string is well, obviously the string being checked +//The datum is used as a source for var names, to check validity +//Otherwise every single word could technically be a variable! +/proc/string2listofvars(var/t_string, var/datum/var_source) + if(!t_string || !var_source) + return list() + + . = list() + + var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check + if(var_found) + //Find var names + + // "A dog said hi [name]!" + // splittext() --> list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string,"\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage," ") + for(var/value in list_value) + if(findtext(value,"]")) + value = splittext(value,"]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A + +/proc/get_end_section_of_type(type) + var/strtype = "[type]" + var/delim_pos = findlasttext(strtype, "/") + if(delim_pos == 0) + return strtype + return copytext(strtype, delim_pos) + +// Concatenates a list of strings into a single string. A seperator may optionally be provided. +/proc/list2text(list/ls, sep) + if (ls.len <= 1) // Early-out code for empty or singleton lists. + return ls.len ? ls[1] : "" + + var/l = ls.len // Made local for sanic speed. + var/i = 0 // Incremented every time a list index is accessed. + + if (sep != null) + // Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc... + #define S1 sep, ls[++i] + #define S4 S1, S1, S1, S1 + #define S16 S4, S4, S4, S4 + #define S64 S16, S16, S16, S16 + + . = "[ls[++i]]" // Make sure the initial element is converted to text. + + // Having the small concatenations come before the large ones boosted speed by an average of at least 5%. + if (l-1 & 0x01) // 'i' will always be 1 here. + . = text("[][][]", ., S1) // Append 1 element if the remaining elements are not a multiple of 2. + if (l-i & 0x02) + . = text("[][][][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. + if (l-i & 0x04) + . = text("[][][][][][][][][]", ., S4) // And so on.... + if (l-i & 0x08) + . = text("[][][][][][][][][][][][][][][][][]", ., S4, S4) + if (l-i & 0x10) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16) + if (l-i & 0x20) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) + if (l-i & 0x40) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) + while (l > i) // Chomp through the rest of the list, 128 elements at a time. + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) + + #undef S64 + #undef S16 + #undef S4 + #undef S1 + else + // Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc... + #define S1 ls[++i] + #define S4 S1, S1, S1, S1 + #define S16 S4, S4, S4, S4 + #define S64 S16, S16, S16, S16 + + . = "[ls[++i]]" // Make sure the initial element is converted to text. + + if (l-1 & 0x01) // 'i' will always be 1 here. + . += S1 // Append 1 element if the remaining elements are not a multiple of 2. + if (l-i & 0x02) + . = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4. + if (l-i & 0x04) + . = text("[][][][][]", ., S4) // And so on... + if (l-i & 0x08) + . = text("[][][][][][][][][]", ., S4, S4) + if (l-i & 0x10) + . = text("[][][][][][][][][][][][][][][][][]", ., S16) + if (l-i & 0x20) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16) + if (l-i & 0x40) + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64) + while (l > i) // Chomp through the rest of the list, 128 elements at a time. + . = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\ + [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64) + + #undef S64 + #undef S16 + #undef S4 + #undef S1 + +// Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator) +/proc/text2list(text, delimiter="\n") + var/delim_len = length(delimiter) + if (delim_len < 1) + return list(text) + + . = list() + var/last_found = 1 + var/found + + do + found = findtext(text, delimiter, last_found, 0) + . += copytext(text, last_found, found) + last_found = found + delim_len + while (found) + +/proc/type2parent(child) + var/string_type = "[child]" + var/last_slash = findlasttext(string_type, "/") + if (last_slash != 1) + return text2path(copytext(string_type, 1, last_slash)) + switch (child) + if (/datum) + return null + if (/obj, /mob) + return /atom/movable + if (/area, /turf) + return /atom + else + return /datum + + +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/safe_file2text(filename, error_on_invalid_return = TRUE) + try + if(fexists(filename)) + . = file2text(filename) + if(!. && error_on_invalid_return) + error("File empty ([filename])") + else if(error_on_invalid_return) + error("File not found ([filename])") + catch(var/exception/E) + if(error_on_invalid_return) + error("Exception when loading file as string: [E]") diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 6f06e3ca7d6..20c38938a59 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1035,7 +1035,8 @@ var/global/list/common_tools = list( /obj/item/weapon/tool/screwdriver, /obj/item/weapon/tool/wirecutters, /obj/item/device/multitool, -/obj/item/weapon/tool/crowbar) +/obj/item/weapon/tool/crowbar, +/obj/item/weapon/tool/transforming) /proc/istool(O) if(O && is_type_in_list(O, common_tools)) @@ -1044,7 +1045,7 @@ var/global/list/common_tools = list( /proc/is_wire_tool(obj/item/I) - if(istype(I, /obj/item/device/multitool) || I.is_wirecutter()) + if(istype(I, /obj/item/device/multitool) || I.has_tool_quality(TOOL_WIRECUTTER)) return TRUE if(istype(I, /obj/item/device/assembly/signaler)) return TRUE @@ -1058,6 +1059,12 @@ var/global/list/common_tools = list( return 3800 else return 0 + if(/obj/item/weapon/tool/transforming) + var/obj/item/weapon/tool/transforming/TT = W + if(TT.possible_tooltypes[TT.current_tooltype] == TOOL_WELDER) + return 3800 + else + return 0 if(/obj/item/weapon/flame/lighter) if(W:lit) return 1500 @@ -1105,7 +1112,7 @@ var/global/list/common_tools = list( if(W.sharp) return TRUE return ( \ - W.is_screwdriver() || \ + W.has_tool_quality(TOOL_SCREWDRIVER) || \ istype(W, /obj/item/weapon/pen) || \ istype(W, /obj/item/weapon/weldingtool) || \ istype(W, /obj/item/weapon/flame/lighter/zippo) || \ diff --git a/code/_helpers/visual_filters.dm b/code/_helpers/visual_filters.dm index 60aef7c9081..cc3b02dc60e 100644 --- a/code/_helpers/visual_filters.dm +++ b/code/_helpers/visual_filters.dm @@ -1,383 +1,383 @@ -#define ICON_NOT_SET "Not Set" -//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui -GLOBAL_LIST_INIT(master_filter_info, list( - "alpha" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "icon" = ICON_NOT_SET, - "render_source" = "", - "flags" = 0 - ), - "flags" = list( - "MASK_INVERSE" = MASK_INVERSE, - "MASK_SWAP" = MASK_SWAP - ) - ), - "angular_blur" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 1 - ) - ), - /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain. - Uncomment if you ever implement it - "color" = list( - "defaults" = list( - "color" = matrix(), - "space" = FILTER_COLOR_RGB - ) - ), - */ - "displace" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = null, - "icon" = ICON_NOT_SET, - "render_source" = "" - ) - ), - "drop_shadow" = list( - "defaults" = list( - "x" = 1, - "y" = -1, - "size" = 1, - "offset" = 0, - "color" = COLOR_HALF_TRANSPARENT_BLACK - ) - ), - "blur" = list( - "defaults" = list( - "size" = 1 - ) - ), - "layer" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "icon" = ICON_NOT_SET, - "render_source" = "", - "flags" = FILTER_OVERLAY, - "color" = "", - "transform" = null, - "blend_mode" = BLEND_DEFAULT - ) - ), - "motion_blur" = list( - "defaults" = list( - "x" = 0, - "y" = 0 - ) - ), - "outline" = list( - "defaults" = list( - "size" = 0, - "color" = COLOR_BLACK, - "flags" = NONE - ), - "flags" = list( - "OUTLINE_SHARP" = OUTLINE_SHARP, - "OUTLINE_SQUARE" = OUTLINE_SQUARE - ) - ), - "radial_blur" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 0.01 - ) - ), - "rays" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 16, - "color" = COLOR_WHITE, - "offset" = 0, - "density" = 10, - "threshold" = 0.5, - "factor" = 0, - "flags" = FILTER_OVERLAY | FILTER_UNDERLAY - ), - "flags" = list( - "FILTER_OVERLAY" = FILTER_OVERLAY, - "FILTER_UNDERLAY" = FILTER_UNDERLAY - ) - ), - "ripple" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 1, - "repeat" = 2, - "radius" = 0, - "falloff" = 1, - "flags" = NONE - ), - "flags" = list( - "WAVE_BOUNDED" = WAVE_BOUNDED - ) - ), - "wave" = list( - "defaults" = list( - "x" = 0, - "y" = 0, - "size" = 1, - "offset" = 0, - "flags" = NONE - ), - "flags" = list( - "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, - "WAVE_BOUNDED" = WAVE_BOUNDED - ) - ) -)) - -#undef ICON_NOT_SET - -//Helpers to generate lists for filter helpers -//This is the only practical way of writing these that actually produces sane lists -/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) - . = list("type" = "alpha") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(icon)) - .["icon"] = icon - if(!isnull(render_source)) - .["render_source"] = render_source - if(!isnull(flags)) - .["flags"] = flags - -/proc/angular_blur_filter(x, y, size) - . = list("type" = "angular_blur") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - -/proc/color_matrix_filter(matrix/in_matrix, space) - . = list("type" = "color") - .["color"] = in_matrix - if(!isnull(space)) - .["space"] = space - -/proc/displacement_map_filter(icon, render_source, x, y, size = 32) - . = list("type" = "displace") - if(!isnull(icon)) - .["icon"] = icon - if(!isnull(render_source)) - .["render_source"] = render_source - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - -/proc/drop_shadow_filter(x, y, size, offset, color) - . = list("type" = "drop_shadow") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(color)) - .["color"] = color - -/proc/gauss_blur_filter(size) - . = list("type" = "blur") - if(!isnull(size)) - .["size"] = size - -/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) - . = list("type" = "layer") - if(!isnull(icon)) - .["icon"] = icon - if(!isnull(render_source)) - .["render_source"] = render_source - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(color)) - .["color"] = color - if(!isnull(flags)) - .["flags"] = flags - if(!isnull(transform)) - .["transform"] = transform - if(!isnull(blend_mode)) - .["blend_mode"] = blend_mode - -/proc/motion_blur_filter(x, y) - . = list("type" = "motion_blur") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - -/proc/outline_filter(size, color, flags) - . = list("type" = "outline") - if(!isnull(size)) - .["size"] = size - if(!isnull(color)) - .["color"] = color - if(!isnull(flags)) - .["flags"] = flags - -/proc/radial_blur_filter(size, x, y) - . = list("type" = "radial_blur") - if(!isnull(size)) - .["size"] = size - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - -/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) - . = list("type" = "rays") - if(!isnull(size)) - .["size"] = size - if(!isnull(color)) - .["color"] = color - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(density)) - .["density"] = density - if(!isnull(threshold)) - .["threshold"] = threshold - if(!isnull(factor)) - .["factor"] = factor - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(flags)) - .["flags"] = flags - -/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) - . = list("type" = "ripple") - if(!isnull(radius)) - .["radius"] = radius - if(!isnull(size)) - .["size"] = size - if(!isnull(falloff)) - .["falloff"] = falloff - if(!isnull(repeat)) - .["repeat"] = repeat - if(!isnull(flags)) - .["flags"] = flags - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - -/proc/wave_filter(x, y, size, offset, flags) - . = list("type" = "wave") - if(!isnull(size)) - .["size"] = size - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(flags)) - .["flags"] = flags - -/atom/proc/add_filter(name,priority,list/params) - LAZYINITLIST(filter_data) - var/list/p = params.Copy() - p["priority"] = priority - filter_data[name] = p - update_filters() - -/atom/proc/update_filters() - filters = null - filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) - for(var/f in filter_data) - var/list/data = filter_data[f] - var/list/arguments = data.Copy() - arguments -= "priority" - filters += filter(arglist(arguments)) - UNSETEMPTY(filter_data) - -/atom/proc/transition_filter(name, time, list/new_params, easing, loop) - var/filter = get_filter(name) - if(!filter) - return - - var/list/old_filter_data = filter_data[name] - - var/list/params = old_filter_data.Copy() - for(var/thing in new_params) - params[thing] = new_params[thing] - - animate(filter, new_params, time = time, easing = easing, loop = loop) - for(var/param in params) - filter_data[name][param] = params[param] - -/atom/proc/change_filter_priority(name, new_priority) - if(!filter_data || !filter_data[name]) - return - - filter_data[name]["priority"] = new_priority - update_filters() - -/obj/item/update_filters() - . = ..() - /* Will port this from TG - for(var/datum/action/A as anything in actions) - A.UpdateButtonIcon() - */ - -/atom/proc/get_filter(name) - if(filter_data && filter_data[name]) - return filters[filter_data.Find(name)] - -/atom/proc/remove_filter(name_or_names) - if(!filter_data) - return - - var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) - - for(var/name in names) - if(filter_data[name]) - filter_data -= name - update_filters() - -/atom/proc/clear_filters() - filter_data = null - filters = null - -/proc/apply_wibbly_filters(atom/in_atom, length) - for(var/i in 1 to 7) - //This is a very baffling and strange way of doing this but I am just preserving old functionality - var/X - var/Y - var/rsq - do - X = 60*rand() - 30 - Y = 60*rand() - 30 - rsq = X*X + Y*Y - while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? - var/random_roll = rand() - in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) - var/filter = in_atom.get_filter("wibbly-[i]") - animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) - animate(offset = random_roll - 1, time = rand() * 20 + 10) - -/proc/remove_wibbly_filters(atom/in_atom) - var/filter - for(var/i in 1 to 7) - filter = in_atom.get_filter("wibbly-[i]") - animate(filter) - in_atom.remove_filter("wibbly-[i]") +#define ICON_NOT_SET "Not Set" +//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui +GLOBAL_LIST_INIT(master_filter_info, list( + "alpha" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = 0 + ), + "flags" = list( + "MASK_INVERSE" = MASK_INVERSE, + "MASK_SWAP" = MASK_SWAP + ) + ), + "angular_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1 + ) + ), + /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain. + Uncomment if you ever implement it + "color" = list( + "defaults" = list( + "color" = matrix(), + "space" = FILTER_COLOR_RGB + ) + ), + */ + "displace" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = null, + "icon" = ICON_NOT_SET, + "render_source" = "" + ) + ), + "drop_shadow" = list( + "defaults" = list( + "x" = 1, + "y" = -1, + "size" = 1, + "offset" = 0, + "color" = COLOR_HALF_TRANSPARENT_BLACK + ) + ), + "blur" = list( + "defaults" = list( + "size" = 1 + ) + ), + "layer" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = FILTER_OVERLAY, + "color" = "", + "transform" = null, + "blend_mode" = BLEND_DEFAULT + ) + ), + "motion_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0 + ) + ), + "outline" = list( + "defaults" = list( + "size" = 0, + "color" = COLOR_BLACK, + "flags" = NONE + ), + "flags" = list( + "OUTLINE_SHARP" = OUTLINE_SHARP, + "OUTLINE_SQUARE" = OUTLINE_SQUARE + ) + ), + "radial_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 0.01 + ) + ), + "rays" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 16, + "color" = COLOR_WHITE, + "offset" = 0, + "density" = 10, + "threshold" = 0.5, + "factor" = 0, + "flags" = FILTER_OVERLAY | FILTER_UNDERLAY + ), + "flags" = list( + "FILTER_OVERLAY" = FILTER_OVERLAY, + "FILTER_UNDERLAY" = FILTER_UNDERLAY + ) + ), + "ripple" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "repeat" = 2, + "radius" = 0, + "falloff" = 1, + "flags" = NONE + ), + "flags" = list( + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ), + "wave" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "offset" = 0, + "flags" = NONE + ), + "flags" = list( + "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ) +)) + +#undef ICON_NOT_SET + +//Helpers to generate lists for filter helpers +//This is the only practical way of writing these that actually produces sane lists +/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) + . = list("type" = "alpha") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(flags)) + .["flags"] = flags + +/proc/angular_blur_filter(x, y, size) + . = list("type" = "angular_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/color_matrix_filter(matrix/in_matrix, space) + . = list("type" = "color") + .["color"] = in_matrix + if(!isnull(space)) + .["space"] = space + +/proc/displacement_map_filter(icon, render_source, x, y, size = 32) + . = list("type" = "displace") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/drop_shadow_filter(x, y, size, offset, color) + . = list("type" = "drop_shadow") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(color)) + .["color"] = color + +/proc/gauss_blur_filter(size) + . = list("type" = "blur") + if(!isnull(size)) + .["size"] = size + +/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) + . = list("type" = "layer") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(transform)) + .["transform"] = transform + if(!isnull(blend_mode)) + .["blend_mode"] = blend_mode + +/proc/motion_blur_filter(x, y) + . = list("type" = "motion_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/outline_filter(size, color, flags) + . = list("type" = "outline") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + +/proc/radial_blur_filter(size, x, y) + . = list("type" = "radial_blur") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) + . = list("type" = "rays") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(density)) + .["density"] = density + if(!isnull(threshold)) + .["threshold"] = threshold + if(!isnull(factor)) + .["factor"] = factor + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(flags)) + .["flags"] = flags + +/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) + . = list("type" = "ripple") + if(!isnull(radius)) + .["radius"] = radius + if(!isnull(size)) + .["size"] = size + if(!isnull(falloff)) + .["falloff"] = falloff + if(!isnull(repeat)) + .["repeat"] = repeat + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/wave_filter(x, y, size, offset, flags) + . = list("type" = "wave") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(flags)) + .["flags"] = flags + +/atom/proc/add_filter(name,priority,list/params) + LAZYINITLIST(filter_data) + var/list/p = params.Copy() + p["priority"] = priority + filter_data[name] = p + update_filters() + +/atom/proc/update_filters() + filters = null + filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) + for(var/f in filter_data) + var/list/data = filter_data[f] + var/list/arguments = data.Copy() + arguments -= "priority" + filters += filter(arglist(arguments)) + UNSETEMPTY(filter_data) + +/atom/proc/transition_filter(name, time, list/new_params, easing, loop) + var/filter = get_filter(name) + if(!filter) + return + + var/list/old_filter_data = filter_data[name] + + var/list/params = old_filter_data.Copy() + for(var/thing in new_params) + params[thing] = new_params[thing] + + animate(filter, new_params, time = time, easing = easing, loop = loop) + for(var/param in params) + filter_data[name][param] = params[param] + +/atom/proc/change_filter_priority(name, new_priority) + if(!filter_data || !filter_data[name]) + return + + filter_data[name]["priority"] = new_priority + update_filters() + +/obj/item/update_filters() + . = ..() + /* Will port this from TG + for(var/datum/action/A as anything in actions) + A.UpdateButtonIcon() + */ + +/atom/proc/get_filter(name) + if(filter_data && filter_data[name]) + return filters[filter_data.Find(name)] + +/atom/proc/remove_filter(name_or_names) + if(!filter_data) + return + + var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) + + for(var/name in names) + if(filter_data[name]) + filter_data -= name + update_filters() + +/atom/proc/clear_filters() + filter_data = null + filters = null + +/proc/apply_wibbly_filters(atom/in_atom, length) + for(var/i in 1 to 7) + //This is a very baffling and strange way of doing this but I am just preserving old functionality + var/X + var/Y + var/rsq + do + X = 60*rand() - 30 + Y = 60*rand() - 30 + rsq = X*X + Y*Y + while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? + var/random_roll = rand() + in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) + var/filter = in_atom.get_filter("wibbly-[i]") + animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) + animate(offset = random_roll - 1, time = rand() * 20 + 10) + +/proc/remove_wibbly_filters(atom/in_atom) + var/filter + for(var/i in 1 to 7) + filter = in_atom.get_filter("wibbly-[i]") + animate(filter) + in_atom.remove_filter("wibbly-[i]") diff --git a/code/_macros.dm b/code/_macros.dm index 737b317abe0..b37d24fa0df 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -1,47 +1,47 @@ -#define span(class, text) ("[text]") - -#define get_turf(A) get_step(A,0) - -#define get_x(A) (get_step(A, 0)?.x || 0) - -#define get_y(A) (get_step(A, 0)?.y || 0) - -#define get_z(A) (get_step(A, 0)?.z || 0) - -#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") - -// #define to_chat(target, message) target << message Not anymore! -//#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat -#define to_chat __to_chat -#define to_world(message) to_chat(world, message) -#define to_world_log(message) world.log << message -// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later. -#define log_world(message) to_world_log(message) -#define to_file(file_entry, source_var) file_entry << source_var -#define from_file(file_entry, target_var) file_entry >> target_var -#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name) -#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name) -#define open_link(target, url) target << link(url) - -// From TG, might be useful to have. -// Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose. -#define DIRECT_OUTPUT(A, B) A << B -#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) -#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) -//#define WRITE_LOG is in logging.dm - -#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE) - -#define qdel_null(x) if(x) { qdel(x) ; x = null } - -#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) - -#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) - -#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } - -#define WORLD_ICON_SIZE 32 //Needed for the R-UST port - -#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port - -#define JOINTEXT(X) jointext(X, null) +#define span(class, text) ("[text]") + +#define get_turf(A) get_step(A,0) + +#define get_x(A) (get_step(A, 0)?.x || 0) + +#define get_y(A) (get_step(A, 0)?.y || 0) + +#define get_z(A) (get_step(A, 0)?.z || 0) + +#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") + +// #define to_chat(target, message) target << message Not anymore! +//#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat +//#define to_chat __to_chat +#define to_world(message) to_chat(world, message) +#define to_world_log(message) world.log << message +// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later. +#define log_world(message) to_world_log(message) +#define to_file(file_entry, source_var) file_entry << source_var +#define from_file(file_entry, target_var) file_entry >> target_var +#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name) +#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name) +#define open_link(target, url) target << link(url) + +// From TG, might be useful to have. +// Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose. +#define DIRECT_OUTPUT(A, B) A << B +#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) +#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) +//#define WRITE_LOG is in logging.dm + +#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE) + +#define qdel_null(x) if(x) { qdel(x) ; x = null } + +#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) + +#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) + +#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } + +#define WORLD_ICON_SIZE 32 //Needed for the R-UST port + +#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port + +#define JOINTEXT(X) jointext(X, null) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 7892b91a55c..16a35b18eac 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -191,6 +191,10 @@ if(stat) return 0 + // prevent picking up items while being in them + if(istype(A, /obj/item) && A == loc) + return 0 + return 1 /* diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 213fe57b90e..a7ff03177d5 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -89,7 +89,7 @@ // cyborgs are prohibited from using storage items so we can I think safely remove (A.loc && isturf(A.loc.loc)) if(isturf(A) || isturf(A.loc)) - if(A.Adjacent(src)) // see adjacent.dm + if(A.Adjacent(src) || (W && W.attack_can_reach(src, A, W.reach))) // see adjacent.dm, allows robots to use ranged melee weapons var/resolved = A.attackby(W, src, 1) if(!resolved && A && W) diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 114f1786534..ae0f7f4c474 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -217,13 +217,13 @@ var/obj/screen/robot_inventory update_robot_modules_display() -/datum/hud/proc/update_robot_modules_display() +/datum/hud/proc/update_robot_modules_display(var/reset = FALSE) if(!isrobot(mymob)) return var/mob/living/silicon/robot/r = mymob - if(r.shown_robot_modules) + if(r.shown_robot_modules && !reset) //Modules display is shown //r.client.screen += robot_inventory //"store" icon @@ -240,7 +240,8 @@ var/obj/screen/robot_inventory var/display_rows = -round(-(r.module.modules.len) / 8) r.robot_modules_background.screen_loc = "CENTER-4:16,SOUTH+1:7 to CENTER+3:16,SOUTH+[display_rows]:7" - r.client.screen += r.robot_modules_background + if(r.client) + r.client.screen += r.robot_modules_background var/x = -4 //Start at CENTER-4,SOUTH+1 var/y = 1 @@ -258,7 +259,7 @@ var/obj/screen/robot_inventory r.module.modules.Remove(r.module.emag) for(var/atom/movable/A in r.module.modules) - if( (A != r.module_state_1) && (A != r.module_state_2) && (A != r.module_state_3) ) + if(r.client && (A != r.module_state_1) && (A != r.module_state_2) && (A != r.module_state_3) ) //Module is not currently active r.client.screen += A if(x < 0) @@ -276,11 +277,12 @@ var/obj/screen/robot_inventory //Modules display is hidden //r.client.screen -= robot_inventory //"store" icon for(var/atom/A in r.module.modules) - if( (A != r.module_state_1) && (A != r.module_state_2) && (A != r.module_state_3) ) + if(r.client && (A != r.module_state_1) && (A != r.module_state_2) && (A != r.module_state_3) ) //Module is not currently active r.client.screen -= A r.shown_robot_modules = 0 - r.client.screen -= r.robot_modules_background + if(r.client) + r.client.screen -= r.robot_modules_background /mob/living/silicon/robot/update_hud() if(modtype) @@ -292,4 +294,4 @@ var/obj/screen/robot_inventory return sprite_datum.sprite_hud_icon_state if(modtype) return lowertext(modtype) - return "nomod" \ No newline at end of file + return "nomod" diff --git a/code/_onclick/hud/spell_screen_objects.dm b/code/_onclick/hud/spell_screen_objects.dm index 1215218ebe1..9f8f97bcce8 100644 --- a/code/_onclick/hud/spell_screen_objects.dm +++ b/code/_onclick/hud/spell_screen_objects.dm @@ -1,220 +1,220 @@ -/obj/screen/movable/spell_master - name = "Spells" - icon = 'icons/mob/screen_spells.dmi' - icon_state = "wiz_spell_ready" - var/list/obj/screen/spell/spell_objects = list() - var/showing = 0 - - var/open_state = "master_open" - var/closed_state = "master_closed" - - screen_loc = ui_spell_master - - var/mob/spell_holder - -/obj/screen/movable/spell_master/Destroy() - . = ..() - for(var/obj/screen/spell/spells in spell_objects) - spells.spellmaster = null - spell_objects.Cut() - if(spell_holder) - spell_holder.spell_masters -= src - if(spell_holder.client && spell_holder.client.screen) - spell_holder.client.screen -= src - spell_holder = null - -/obj/screen/movable/spell_master/MouseDrop() - if(showing) - return - - return ..() - -/obj/screen/movable/spell_master/Click() - if(!spell_objects.len) - qdel(src) - return - - toggle_open() - -/obj/screen/movable/spell_master/proc/toggle_open(var/forced_state = 0) - if(showing && (forced_state != 2)) - for(var/obj/screen/spell/O in spell_objects) - if(spell_holder && spell_holder.client) - spell_holder.client.screen -= O - O.handle_icon_updates = 0 - showing = 0 - overlays.len = 0 - overlays.Add(closed_state) - else if(forced_state != 1) - open_spellmaster() - update_spells(1) - showing = 1 - overlays.len = 0 - overlays.Add(open_state) - -/obj/screen/movable/spell_master/proc/open_spellmaster() - var/list/screen_loc_xy = splittext(screen_loc,",") - - //Create list of X offsets - var/list/screen_loc_X = splittext(screen_loc_xy[1],":") - var/x_position = decode_screen_X(screen_loc_X[1]) - var/x_pix = screen_loc_X[2] - - //Create list of Y offsets - var/list/screen_loc_Y = splittext(screen_loc_xy[2],":") - var/y_position = decode_screen_Y(screen_loc_Y[1]) - var/y_pix = screen_loc_Y[2] - - for(var/i = 1; i <= spell_objects.len; i++) - var/obj/screen/spell/S = spell_objects[i] - var/xpos = x_position + (x_position < 8 ? 1 : -1)*(i%7) - var/ypos = y_position + (y_position < 8 ? round(i/7) : -round(i/7)) - if(spell_holder && spell_holder.client) - S.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]" - spell_holder.client.screen += S - S.handle_icon_updates = 1 - -/obj/screen/movable/spell_master/proc/add_spell(var/spell/spell) - if(!spell) return - - if(spell.connected_button) //we have one already, for some reason - if(spell.connected_button in spell_objects) - return - else - spell_objects.Add(spell.connected_button) - if(spell_holder.client) - toggle_open(2) - return - - if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one - return - - var/obj/screen/spell/newscreen = new /obj/screen/spell() - newscreen.spellmaster = src - newscreen.spell = spell - - spell.connected_button = newscreen - - if(!spell.override_base) //if it's not set, we do basic checks - if(spell.spell_flags & CONSTRUCT_CHECK) - newscreen.spell_base = "const" //construct spells - else - newscreen.spell_base = "wiz" //wizard spells - else - newscreen.spell_base = spell.override_base - newscreen.name = spell.name - newscreen.update_charge(1) - spell_objects.Add(newscreen) - if(spell_holder.client) - toggle_open(2) //forces the icons to refresh on screen - -/obj/screen/movable/spell_master/proc/remove_spell(var/spell/spell) - qdel(spell.connected_button) - - spell.connected_button = null - - if(spell_objects.len) - toggle_open(showing + 1) - else - qdel(src) - -/obj/screen/movable/spell_master/proc/silence_spells(var/amount) - for(var/obj/screen/spell/spell in spell_objects) - spell.spell.silenced = amount - spell.update_charge(1) - -/obj/screen/movable/spell_master/proc/update_spells(forced = 0, mob/user) - if(user && user.client) - if(!(src in user.client.screen)) - user.client.screen += src - for(var/obj/screen/spell/spell in spell_objects) - spell.update_charge(forced) - -/obj/screen/movable/spell_master/genetic - name = "Mutant Powers" - icon_state = "genetic_spell_ready" - - open_state = "genetics_open" - closed_state = "genetics_closed" - - screen_loc = ui_genetic_master - -/obj/screen/movable/spell_master/swarm - name = "Swarm Abilities" - icon_state = "nano_spell_ready" - - open_state = "swarm_open" - closed_state = "swarm_closed" - -//////////////ACTUAL SPELLS////////////// -//This is what you click to cast things// -///////////////////////////////////////// -/obj/screen/spell - icon = 'icons/mob/screen_spells.dmi' - icon_state = "wiz_spell_base" - var/spell_base = "wiz" - var/last_charge = 0 //not a time, but the last remembered charge value - - var/spell/spell = null - var/handle_icon_updates = 0 - var/obj/screen/movable/spell_master/spellmaster - - var/icon/last_charged_icon - -/obj/screen/spell/Destroy() - . = ..() - spell = null - last_charged_icon = null - if(spellmaster) - spellmaster.spell_objects -= src - if(spellmaster.spell_holder && spellmaster.spell_holder.client) - spellmaster.spell_holder.client.screen -= src - if(spellmaster && !spellmaster.spell_objects.len) - qdel(spellmaster) - spellmaster = null - -/obj/screen/spell/proc/update_charge(var/forced_update = 0) - if(!spell) - qdel(src) - return - - if((last_charge == spell.charge_counter || !handle_icon_updates) && !forced_update) - return //nothing to see here - - cut_overlay(spell.hud_state) - - if(spell.charge_type == Sp_RECHARGE || spell.charge_type == Sp_CHARGES) - if(spell.charge_counter < spell.charge_max) - icon_state = "[spell_base]_spell_base" - if(spell.charge_counter > 0) - var/icon/partial_charge = icon(src.icon, "[spell_base]_spell_ready") - partial_charge.Crop(1, 1, partial_charge.Width(), round(partial_charge.Height() * spell.charge_counter / spell.charge_max)) - overlays += partial_charge - if(last_charged_icon) - cut_overlay(last_charged_icon) - last_charged_icon = partial_charge - else if(last_charged_icon) - cut_overlay(last_charged_icon) - last_charged_icon = null - else - icon_state = "[spell_base]_spell_ready" - if(last_charged_icon) - cut_overlay(last_charged_icon) - else - icon_state = "[spell_base]_spell_ready" - - add_overlay(spell.hud_state) - - last_charge = spell.charge_counter - - cut_overlay("silence") - if(spell.silenced) - overlays += "silence" - -/obj/screen/spell/Click() - if(!usr || !spell) - qdel(src) - return - - spell.perform(usr) - update_charge(1) +/obj/screen/movable/spell_master + name = "Spells" + icon = 'icons/mob/screen_spells.dmi' + icon_state = "wiz_spell_ready" + var/list/obj/screen/spell/spell_objects = list() + var/showing = 0 + + var/open_state = "master_open" + var/closed_state = "master_closed" + + screen_loc = ui_spell_master + + var/mob/spell_holder + +/obj/screen/movable/spell_master/Destroy() + . = ..() + for(var/obj/screen/spell/spells in spell_objects) + spells.spellmaster = null + spell_objects.Cut() + if(spell_holder) + spell_holder.spell_masters -= src + if(spell_holder.client && spell_holder.client.screen) + spell_holder.client.screen -= src + spell_holder = null + +/obj/screen/movable/spell_master/MouseDrop() + if(showing) + return + + return ..() + +/obj/screen/movable/spell_master/Click() + if(!spell_objects.len) + qdel(src) + return + + toggle_open() + +/obj/screen/movable/spell_master/proc/toggle_open(var/forced_state = 0) + if(showing && (forced_state != 2)) + for(var/obj/screen/spell/O in spell_objects) + if(spell_holder && spell_holder.client) + spell_holder.client.screen -= O + O.handle_icon_updates = 0 + showing = 0 + overlays.len = 0 + overlays.Add(closed_state) + else if(forced_state != 1) + open_spellmaster() + update_spells(1) + showing = 1 + overlays.len = 0 + overlays.Add(open_state) + +/obj/screen/movable/spell_master/proc/open_spellmaster() + var/list/screen_loc_xy = splittext(screen_loc,",") + + //Create list of X offsets + var/list/screen_loc_X = splittext(screen_loc_xy[1],":") + var/x_position = decode_screen_X(screen_loc_X[1]) + var/x_pix = screen_loc_X[2] + + //Create list of Y offsets + var/list/screen_loc_Y = splittext(screen_loc_xy[2],":") + var/y_position = decode_screen_Y(screen_loc_Y[1]) + var/y_pix = screen_loc_Y[2] + + for(var/i = 1; i <= spell_objects.len; i++) + var/obj/screen/spell/S = spell_objects[i] + var/xpos = x_position + (x_position < 8 ? 1 : -1)*(i%7) + var/ypos = y_position + (y_position < 8 ? round(i/7) : -round(i/7)) + if(spell_holder && spell_holder.client) + S.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]" + spell_holder.client.screen += S + S.handle_icon_updates = 1 + +/obj/screen/movable/spell_master/proc/add_spell(var/spell/spell) + if(!spell) return + + if(spell.connected_button) //we have one already, for some reason + if(spell.connected_button in spell_objects) + return + else + spell_objects.Add(spell.connected_button) + if(spell_holder.client) + toggle_open(2) + return + + if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one + return + + var/obj/screen/spell/newscreen = new /obj/screen/spell() + newscreen.spellmaster = src + newscreen.spell = spell + + spell.connected_button = newscreen + + if(!spell.override_base) //if it's not set, we do basic checks + if(spell.spell_flags & CONSTRUCT_CHECK) + newscreen.spell_base = "const" //construct spells + else + newscreen.spell_base = "wiz" //wizard spells + else + newscreen.spell_base = spell.override_base + newscreen.name = spell.name + newscreen.update_charge(1) + spell_objects.Add(newscreen) + if(spell_holder.client) + toggle_open(2) //forces the icons to refresh on screen + +/obj/screen/movable/spell_master/proc/remove_spell(var/spell/spell) + qdel(spell.connected_button) + + spell.connected_button = null + + if(spell_objects.len) + toggle_open(showing + 1) + else + qdel(src) + +/obj/screen/movable/spell_master/proc/silence_spells(var/amount) + for(var/obj/screen/spell/spell in spell_objects) + spell.spell.silenced = amount + spell.update_charge(1) + +/obj/screen/movable/spell_master/proc/update_spells(forced = 0, mob/user) + if(user && user.client) + if(!(src in user.client.screen)) + user.client.screen += src + for(var/obj/screen/spell/spell in spell_objects) + spell.update_charge(forced) + +/obj/screen/movable/spell_master/genetic + name = "Mutant Powers" + icon_state = "genetic_spell_ready" + + open_state = "genetics_open" + closed_state = "genetics_closed" + + screen_loc = ui_genetic_master + +/obj/screen/movable/spell_master/swarm + name = "Swarm Abilities" + icon_state = "nano_spell_ready" + + open_state = "swarm_open" + closed_state = "swarm_closed" + +//////////////ACTUAL SPELLS////////////// +//This is what you click to cast things// +///////////////////////////////////////// +/obj/screen/spell + icon = 'icons/mob/screen_spells.dmi' + icon_state = "wiz_spell_base" + var/spell_base = "wiz" + var/last_charge = 0 //not a time, but the last remembered charge value + + var/spell/spell = null + var/handle_icon_updates = 0 + var/obj/screen/movable/spell_master/spellmaster + + var/icon/last_charged_icon + +/obj/screen/spell/Destroy() + . = ..() + spell = null + last_charged_icon = null + if(spellmaster) + spellmaster.spell_objects -= src + if(spellmaster.spell_holder && spellmaster.spell_holder.client) + spellmaster.spell_holder.client.screen -= src + if(spellmaster && !spellmaster.spell_objects.len) + qdel(spellmaster) + spellmaster = null + +/obj/screen/spell/proc/update_charge(var/forced_update = 0) + if(!spell) + qdel(src) + return + + if((last_charge == spell.charge_counter || !handle_icon_updates) && !forced_update) + return //nothing to see here + + cut_overlay(spell.hud_state) + + if(spell.charge_type == Sp_RECHARGE || spell.charge_type == Sp_CHARGES) + if(spell.charge_counter < spell.charge_max) + icon_state = "[spell_base]_spell_base" + if(spell.charge_counter > 0) + var/icon/partial_charge = icon(src.icon, "[spell_base]_spell_ready") + partial_charge.Crop(1, 1, partial_charge.Width(), round(partial_charge.Height() * spell.charge_counter / spell.charge_max)) + overlays += partial_charge + if(last_charged_icon) + cut_overlay(last_charged_icon) + last_charged_icon = partial_charge + else if(last_charged_icon) + cut_overlay(last_charged_icon) + last_charged_icon = null + else + icon_state = "[spell_base]_spell_ready" + if(last_charged_icon) + cut_overlay(last_charged_icon) + else + icon_state = "[spell_base]_spell_ready" + + add_overlay(spell.hud_state) + + last_charge = spell.charge_counter + + cut_overlay("silence") + if(spell.silenced) + overlays += "silence" + +/obj/screen/spell/Click() + if(!usr || !spell) + qdel(src) + return + + spell.perform(usr) + update_charge(1) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index d2dd1fbe74d..b4b5d955e76 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -302,6 +302,8 @@ var/list/gamemode_cache = list() var/static/suggested_byond_version var/static/suggested_byond_build + var/static/invoke_youtubedl = null + /datum/configuration/New() var/list/L = subtypesof(/datum/game_mode) for (var/T in L) @@ -974,6 +976,9 @@ var/list/gamemode_cache = list() config.vgs_server_port = text2num(value) // VOREStation Edit End + if("invoke_youtubedl") + config.invoke_youtubedl = value + else log_misc("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm index bacaa59f8fc..6b7c0b41376 100644 --- a/code/controllers/globals.dm +++ b/code/controllers/globals.dm @@ -36,7 +36,7 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) if(!statclick) statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - stat("GLOB:", "Button Removed Due To Crashing") //VOREStation Edit + stat("GLOB:", statclick.update("Edit")) /datum/controller/global_vars/vv_edit_var(var_name, var_value) if(gvars_datum_protected_varlist[var_name]) diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm index 82f79207520..dcfb8885229 100644 --- a/code/controllers/hooks-defs.dm +++ b/code/controllers/hooks-defs.dm @@ -1,96 +1,96 @@ -/** - * Startup hook. - * Called in world.dm when the server starts. - */ -/hook/startup - -/** - * Roundstart hook. - * Called in gameticker.dm when a round starts. - */ -/hook/roundstart - -/** - * Roundend hook. - * Called in gameticker.dm when a round ends. - */ -/hook/roundend - -/** - * Death hook. - * Called in death.dm when someone dies. - * Parameters: var/mob/living/carbon/human, var/gibbed - */ -/hook/death - -/** - * Cloning hook. - * Called in cloning.dm when someone is brought back by the wonders of modern science. - * Parameters: var/mob/living/carbon/human - */ -/hook/clone - -/** - * Debrained hook. - * Called in brain_item.dm when someone gets debrained. - * Parameters: var/obj/item/organ/brain - */ -/hook/debrain - -/** - * Borged hook. - * Called in robot_parts.dm when someone gets turned into a cyborg. - * Parameters: var/mob/living/silicon/robot - */ -/hook/borgify - -/** - * Podman hook. - * Called in podmen.dm when someone is brought back as a Diona. - * Parameters: var/mob/living/carbon/alien/diona - */ -/hook/harvest_podman - -/** - * Payroll revoked hook. - * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. - * Parameters: var/datum/money_account - */ -/hook/revoke_payroll - -/** - * Account suspension hook. - * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. - * Parameters: var/datum/money_account - */ -/hook/change_account_status - -/** - * Employee reassignment hook. - * Called in card.dm when someone's card is reassigned at the HoP's desk. - * Parameters: var/obj/item/weapon/card/id - */ -/hook/reassign_employee - -/** - * Employee terminated hook. - * Called in card.dm when someone's card is terminated at the HoP's desk. - * Parameters: var/obj/item/weapon/card/id - */ -/hook/terminate_employee - -/** - * Crate sold hook. - * Called in supplyshuttle.dm when a crate is sold on the shuttle. - * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle - */ -/hook/sell_crate - -/** - * Supply Shuttle sold hook. - * Called in supplyshuttle.dm when the shuttle contents are sold. - * This hook is called _before_ the crates are processed for normal - * phoron/metal sale (and before the sell_crate hooks) - * Parameters: var/area/area_shuttle - */ -/hook/sell_shuttle +/** + * Startup hook. + * Called in world.dm when the server starts. + */ +/hook/startup + +/** + * Roundstart hook. + * Called in gameticker.dm when a round starts. + */ +/hook/roundstart + +/** + * Roundend hook. + * Called in gameticker.dm when a round ends. + */ +/hook/roundend + +/** + * Death hook. + * Called in death.dm when someone dies. + * Parameters: var/mob/living/carbon/human, var/gibbed + */ +/hook/death + +/** + * Cloning hook. + * Called in cloning.dm when someone is brought back by the wonders of modern science. + * Parameters: var/mob/living/carbon/human + */ +/hook/clone + +/** + * Debrained hook. + * Called in brain_item.dm when someone gets debrained. + * Parameters: var/obj/item/organ/brain + */ +/hook/debrain + +/** + * Borged hook. + * Called in robot_parts.dm when someone gets turned into a cyborg. + * Parameters: var/mob/living/silicon/robot + */ +/hook/borgify + +/** + * Podman hook. + * Called in podmen.dm when someone is brought back as a Diona. + * Parameters: var/mob/living/carbon/alien/diona + */ +/hook/harvest_podman + +/** + * Payroll revoked hook. + * Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/revoke_payroll + +/** + * Account suspension hook. + * Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal. + * Parameters: var/datum/money_account + */ +/hook/change_account_status + +/** + * Employee reassignment hook. + * Called in card.dm when someone's card is reassigned at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/reassign_employee + +/** + * Employee terminated hook. + * Called in card.dm when someone's card is terminated at the HoP's desk. + * Parameters: var/obj/item/weapon/card/id + */ +/hook/terminate_employee + +/** + * Crate sold hook. + * Called in supplyshuttle.dm when a crate is sold on the shuttle. + * Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle + */ +/hook/sell_crate + +/** + * Supply Shuttle sold hook. + * Called in supplyshuttle.dm when the shuttle contents are sold. + * This hook is called _before_ the crates are processed for normal + * phoron/metal sale (and before the sell_crate hooks) + * Parameters: var/area/area_shuttle + */ +/hook/sell_shuttle diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index bcea4490de3..7b63084af24 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -1,56 +1,56 @@ -//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one. -//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs) -//WIP, needs lots of work still - -// -// TODO - This will be completely replaced by master.dm in time. -// - -var/global/datum/controller/game_controller/master_controller //Set in world.New() - -var/global/controller_iteration = 0 -var/global/last_tick_duration = 0 - -var/global/pipe_processing_killed = 0 - -/datum/controller/game_controller - var/list/shuttle_list // For debugging and VV - -/datum/controller/game_controller/New() - //There can be only one master_controller. Out with the old and in with the new. - if(master_controller != src) - log_debug("Rebuilding Master Controller") - if(istype(master_controller)) - qdel(master_controller) - master_controller = src - - if(!job_master) - job_master = new /datum/controller/occupations() - job_master.SetupOccupations() - job_master.LoadJobs("config/jobs.txt") - admin_notice("Job setup complete", R_DEBUG) - - if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase() - if(!syndicate_code_response) syndicate_code_response = generate_code_phrase() - -/datum/controller/game_controller/proc/setup() - - setup_objects() - // setupgenetics() Moved to SSatoms - // SetupXenoarch() - Moved to SSxenoarch - - transfer_controller = new - admin_notice("Initializations complete.", R_DEBUG) - -// #if UNIT_TEST -// #define CHECK_SLEEP_MASTER // For unit tests we don't care about a smooth lobby screen experience. We care about speed. -// #else -// #define CHECK_SLEEP_MASTER if(++initialized_objects > 500) { initialized_objects=0;sleep(world.tick_lag); } -// #endif - -/datum/controller/game_controller/proc/setup_objects() - // Set up antagonists. - populate_antag_type_list() - - //Set up spawn points. +//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one. +//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs) +//WIP, needs lots of work still + +// +// TODO - This will be completely replaced by master.dm in time. +// + +var/global/datum/controller/game_controller/master_controller //Set in world.New() + +var/global/controller_iteration = 0 +var/global/last_tick_duration = 0 + +var/global/pipe_processing_killed = 0 + +/datum/controller/game_controller + var/list/shuttle_list // For debugging and VV + +/datum/controller/game_controller/New() + //There can be only one master_controller. Out with the old and in with the new. + if(master_controller != src) + log_debug("Rebuilding Master Controller") + if(istype(master_controller)) + qdel(master_controller) + master_controller = src + + if(!job_master) + job_master = new /datum/controller/occupations() + job_master.SetupOccupations() + job_master.LoadJobs("config/jobs.txt") + admin_notice("Job setup complete", R_DEBUG) + + if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase() + if(!syndicate_code_response) syndicate_code_response = generate_code_phrase() + +/datum/controller/game_controller/proc/setup() + + setup_objects() + // setupgenetics() Moved to SSatoms + // SetupXenoarch() - Moved to SSxenoarch + + transfer_controller = new + admin_notice("Initializations complete.", R_DEBUG) + +// #if UNIT_TEST +// #define CHECK_SLEEP_MASTER // For unit tests we don't care about a smooth lobby screen experience. We care about speed. +// #else +// #define CHECK_SLEEP_MASTER if(++initialized_objects > 500) { initialized_objects=0;sleep(world.tick_lag); } +// #endif + +/datum/controller/game_controller/proc/setup_objects() + // Set up antagonists. + populate_antag_type_list() + + //Set up spawn points. populate_spawn_points() \ No newline at end of file diff --git a/code/controllers/observer_listener/atom/observer.dm b/code/controllers/observer_listener/atom/observer.dm index da38580414e..8c3e93f4fc3 100644 --- a/code/controllers/observer_listener/atom/observer.dm +++ b/code/controllers/observer_listener/atom/observer.dm @@ -1,31 +1,31 @@ -#define OBSERVER_EVENT_DESTROY "OnDestroy" - -/atom - var/list/observer_events - -/atom/Destroy() - var/list/destroy_listeners = get_listener_list_from_event(OBSERVER_EVENT_DESTROY) - if(destroy_listeners) - for(var/destroy_listener in destroy_listeners) - call(destroy_listener, destroy_listeners[destroy_listener])(src) - - for(var/list/listeners in observer_events) - listeners.Cut() - - return ..() - -/atom/proc/register(var/event, var/procOwner, var/proc_call) - var/list/listeners = get_listener_list_from_event(event) - listeners[procOwner] = proc_call - -/atom/proc/unregister(var/event, var/procOwner) - var/list/listeners = get_listener_list_from_event(event) - listeners -= procOwner - -/atom/proc/get_listener_list_from_event(var/observer_event) - if(!observer_events) observer_events = list() - var/list/listeners = observer_events[observer_event] - if(!listeners) - listeners = list() - observer_events[observer_event] = listeners - return listeners +#define OBSERVER_EVENT_DESTROY "OnDestroy" + +/atom + var/list/observer_events + +/atom/Destroy() + var/list/destroy_listeners = get_listener_list_from_event(OBSERVER_EVENT_DESTROY) + if(destroy_listeners) + for(var/destroy_listener in destroy_listeners) + call(destroy_listener, destroy_listeners[destroy_listener])(src) + + for(var/list/listeners in observer_events) + listeners.Cut() + + return ..() + +/atom/proc/register(var/event, var/procOwner, var/proc_call) + var/list/listeners = get_listener_list_from_event(event) + listeners[procOwner] = proc_call + +/atom/proc/unregister(var/event, var/procOwner) + var/list/listeners = get_listener_list_from_event(event) + listeners -= procOwner + +/atom/proc/get_listener_list_from_event(var/observer_event) + if(!observer_events) observer_events = list() + var/list/listeners = observer_events[observer_event] + if(!listeners) + listeners = list() + observer_events[observer_event] = listeners + return listeners diff --git a/code/controllers/subsystems/alarm.dm b/code/controllers/subsystems/alarm.dm index 4b6140e5074..868025eca3f 100644 --- a/code/controllers/subsystems/alarm.dm +++ b/code/controllers/subsystems/alarm.dm @@ -1,45 +1,45 @@ - -// We manually initialize the alarm handlers instead of looping over all existing types -// to make it possible to write: camera_alarm.triggerAlarm() rather than SSalarm.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. -/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() -/var/global/datum/alarm_handler/camera/camera_alarm = new() -/var/global/datum/alarm_handler/fire/fire_alarm = new() -/var/global/datum/alarm_handler/motion/motion_alarm = new() -/var/global/datum/alarm_handler/power/power_alarm = new() - -SUBSYSTEM_DEF(alarm) - name = "Alarm" - wait = 2 SECONDS - priority = FIRE_PRIORITY_ALARM - init_order = INIT_ORDER_ALARM - var/list/datum/alarm/all_handlers - var/tmp/list/currentrun = null - var/static/list/active_alarm_cache = list() - -/datum/controller/subsystem/alarm/Initialize() - all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) - . = ..() - -/datum/controller/subsystem/alarm/fire(resumed = FALSE) - if(!resumed) - src.currentrun = all_handlers.Copy() - active_alarm_cache.Cut() - - var/list/currentrun = src.currentrun // Cache for sanic speed - while (currentrun.len) - var/datum/alarm_handler/AH = currentrun[currentrun.len] - currentrun.len-- - AH.process() - active_alarm_cache += AH.alarms - - if (MC_TICK_CHECK) - return - -/datum/controller/subsystem/alarm/proc/active_alarms() - return active_alarm_cache.Copy() - -/datum/controller/subsystem/alarm/proc/number_of_active_alarms() - return active_alarm_cache.len - -/datum/controller/subsystem/alarm/stat_entry() - ..("[number_of_active_alarms()] alarm\s") + +// We manually initialize the alarm handlers instead of looping over all existing types +// to make it possible to write: camera_alarm.triggerAlarm() rather than SSalarm.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. +/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() +/var/global/datum/alarm_handler/camera/camera_alarm = new() +/var/global/datum/alarm_handler/fire/fire_alarm = new() +/var/global/datum/alarm_handler/motion/motion_alarm = new() +/var/global/datum/alarm_handler/power/power_alarm = new() + +SUBSYSTEM_DEF(alarm) + name = "Alarm" + wait = 2 SECONDS + priority = FIRE_PRIORITY_ALARM + init_order = INIT_ORDER_ALARM + var/list/datum/alarm/all_handlers + var/tmp/list/currentrun = null + var/static/list/active_alarm_cache = list() + +/datum/controller/subsystem/alarm/Initialize() + all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) + . = ..() + +/datum/controller/subsystem/alarm/fire(resumed = FALSE) + if(!resumed) + src.currentrun = all_handlers.Copy() + active_alarm_cache.Cut() + + var/list/currentrun = src.currentrun // Cache for sanic speed + while (currentrun.len) + var/datum/alarm_handler/AH = currentrun[currentrun.len] + currentrun.len-- + AH.process() + active_alarm_cache += AH.alarms + + if (MC_TICK_CHECK) + return + +/datum/controller/subsystem/alarm/proc/active_alarms() + return active_alarm_cache.Copy() + +/datum/controller/subsystem/alarm/proc/number_of_active_alarms() + return active_alarm_cache.len + +/datum/controller/subsystem/alarm/stat_entry() + ..("[number_of_active_alarms()] alarm\s") diff --git a/code/controllers/subsystems/chat.dm b/code/controllers/subsystems/chat.dm index 92a1be27573..b8b2917c8dc 100644 --- a/code/controllers/subsystems/chat.dm +++ b/code/controllers/subsystems/chat.dm @@ -1,77 +1,100 @@ +/*! + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + SUBSYSTEM_DEF(chat) name = "Chat" - flags = SS_TICKER - wait = 1 // SS_TICKER means this runs every tick + flags = SS_TICKER|SS_NO_INIT + wait = 1 priority = FIRE_PRIORITY_CHAT init_order = INIT_ORDER_CHAT - var/list/list/msg_queue = list() //List of lists + /// Assosciates a ckey with a list of messages to send to them. + var/list/list/datum/chat_payload/client_to_payloads = list() -/datum/controller/subsystem/chat/Initialize(timeofday) - init_vchat() - ..() + /// Associates a ckey with an assosciative list of their last CHAT_RELIABILITY_HISTORY_SIZE messages. + var/list/list/datum/chat_payload/client_to_reliability_history = list() -/datum/controller/subsystem/chat/fire() - var/list/msg_queue = src.msg_queue // Local variable for sanic speed. - for(var/client/C as anything in msg_queue) - var/list/messages = msg_queue[C] - msg_queue -= C - if (C) - C << output(jsEncode(messages), "htmloutput:putmessage") + /// Assosciates a ckey with their next sequence number. + var/list/client_to_sequence_number = list() - if(MC_TICK_CHECK) - return +/datum/controller/subsystem/chat/proc/generate_payload(client/target, message_data) + var/sequence = client_to_sequence_number[target.ckey] + client_to_sequence_number[target.ckey] += 1 -/datum/controller/subsystem/chat/stat_entry() - ..("C:[msg_queue.len]") + var/datum/chat_payload/payload = new + payload.sequence = sequence + payload.content = message_data -/datum/controller/subsystem/chat/proc/queue(target, time, message, handle_whitespace = TRUE) - if(!target || !message) - return + if(!(target.ckey in client_to_reliability_history)) + client_to_reliability_history[target.ckey] = list() + var/list/client_history = client_to_reliability_history[target.ckey] + client_history["[sequence]"] = payload - if(!istext(message)) - stack_trace("to_chat called with invalid input type") - return + if(length(client_history) > CHAT_RELIABILITY_HISTORY_SIZE) + var/oldest = text2num(client_history[1]) + for(var/index in 2 to length(client_history)) + var/test = text2num(client_history[index]) + if(test < oldest) + oldest = test + client_history -= "[oldest]" + return payload + +/datum/controller/subsystem/chat/proc/send_payload_to_client(client/target, datum/chat_payload/payload) + target.tgui_panel.window.send_message("chat/message", payload.into_message()) + SEND_TEXT(target, payload.get_content_as_html()) - // Currently to_chat(world, ...) gets sent individually to each client. Consider. - if(target == world) - target = GLOB.clients - - //Some macros remain in the string even after parsing and fuck up the eventual output - var/original_message = message - message = replacetext(message, "\n", "
    ") - message = replacetext(message, "\improper", "") - message = replacetext(message, "\proper", "") - - if(isnull(time)) - time = world.time - - var/list/messageStruct = list("time" = time, "message" = message); - - if(islist(target)) - for(var/I in target) - var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible - - if(!C || !C.chatOutput) - continue // No client? No care. - else if(C.chatOutput.broken) - DIRECT_OUTPUT(C, original_message) - continue - else if(!C.chatOutput.loaded) - continue // If not loaded yet, do nothing and history-sending on load will get it. - - LAZYINITLIST(msg_queue[C]) - msg_queue[C][++msg_queue[C].len] = messageStruct - else - var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible - - if(!C || !C.chatOutput) - return // No client? No care. - else if(C.chatOutput.broken) - DIRECT_OUTPUT(C, original_message) +/datum/controller/subsystem/chat/fire() + for(var/ckey in client_to_payloads) + var/client/target = GLOB.directory[ckey] + if(isnull(target)) // verify client still exists + LAZYREMOVE(client_to_payloads, ckey) + continue + + for(var/datum/chat_payload/payload as anything in client_to_payloads[ckey]) + send_payload_to_client(target, payload) + LAZYREMOVE(client_to_payloads, ckey) + + if(MC_TICK_CHECK) return - else if(!C.chatOutput.loaded) - return // If not loaded yet, do nothing and history-sending on load will get it. - LAZYINITLIST(msg_queue[C]) - msg_queue[C][++msg_queue[C].len] = messageStruct +/datum/controller/subsystem/chat/proc/queue(queue_target, list/message_data) + var/list/targets = islist(queue_target) ? queue_target : list(queue_target) + for(var/target in targets) + var/client/client = CLIENT_FROM_VAR(target) + if(isnull(client)) + continue + LAZYADDASSOCLIST(client_to_payloads, client.ckey, generate_payload(client, message_data)) + +/datum/controller/subsystem/chat/proc/send_immediate(send_target, list/message_data) + var/list/targets = islist(send_target) ? send_target : list(send_target) + for(var/target in targets) + var/client/client = CLIENT_FROM_VAR(target) + if(isnull(client)) + continue + send_payload_to_client(client, generate_payload(client, message_data)) + +/datum/controller/subsystem/chat/proc/handle_resend(client/client, sequence) + var/list/client_history = client_to_reliability_history[client.ckey] + sequence = "[sequence]" + if(isnull(client_history) || !(sequence in client_history)) + return + + var/datum/chat_payload/payload = client_history[sequence] + if(payload.resends > CHAT_RELIABILITY_MAX_RESENDS) + return // we tried but byond said no + + payload.resends += 1 + send_payload_to_client(client, client_history[sequence]) + /* + SSblackbox.record_feedback( + "nested tally", + "chat_resend_byond_version", + 1, + list( + "[client.byond_version]", + "[client.byond_build]", + ), + ) + */ diff --git a/code/controllers/subsystems/circuits.dm b/code/controllers/subsystems/circuits.dm index 4d2b2821713..f417ce3055d 100644 --- a/code/controllers/subsystems/circuits.dm +++ b/code/controllers/subsystems/circuits.dm @@ -1,94 +1,94 @@ -// -// This is for custom circuits, mostly the initialization of global properties about them. -// Might make this also process them in the future if its better to do that than using the obj ticker. -// -SUBSYSTEM_DEF(circuit) - name = "Circuit" - init_order = INIT_ORDER_CIRCUIT - flags = SS_NO_FIRE - var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs - var/list/cached_components = list() // Associative list of [component_path]:[component] pairs - var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs - var/list/cached_assemblies = list() // Associative list of [assembly_path]:[assembly] pairs - var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs - var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs -// var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3 - -/datum/controller/subsystem/circuit/Recover() - flags |= SS_NO_INIT // Make extra sure we don't initialize twice. - -/datum/controller/subsystem/circuit/Initialize(timeofday) - circuits_init() - return ..() - -/datum/controller/subsystem/circuit/proc/circuits_init() - //Cached lists for free performance - for(var/obj/item/integrated_circuit/IC as anything in typesof(/obj/item/integrated_circuit)) - var/path = IC - all_components[initial(IC.name)] = path // Populating the component lists - cached_components[path] = new path - - if(!(initial(IC.spawn_flags) & (IC_SPAWN_DEFAULT | IC_SPAWN_RESEARCH))) - continue - - var/category = initial(IC.category_text) - if(!circuit_fabricator_recipe_list[category]) - circuit_fabricator_recipe_list[category] = list() - var/list/category_list = circuit_fabricator_recipe_list[category] - category_list += IC // Populating the fabricator categories - - for(var/obj/item/device/electronic_assembly/A as anything in typesof(/obj/item/device/electronic_assembly)) - var/path = A - all_assemblies[initial(A.name)] = path - cached_assemblies[path] = new path - - - circuit_fabricator_recipe_list["Assemblies"] = list( - /obj/item/device/electronic_assembly/default, - /obj/item/device/electronic_assembly/calc, - /obj/item/device/electronic_assembly/clam, - /obj/item/device/electronic_assembly/simple, - /obj/item/device/electronic_assembly/hook, - /obj/item/device/electronic_assembly/pda, - /obj/item/device/electronic_assembly/tiny/default, - /obj/item/device/electronic_assembly/tiny/cylinder, - /obj/item/device/electronic_assembly/tiny/scanner, - /obj/item/device/electronic_assembly/tiny/hook, - /obj/item/device/electronic_assembly/tiny/box, - /obj/item/device/electronic_assembly/medium/default, - /obj/item/device/electronic_assembly/medium/box, - /obj/item/device/electronic_assembly/medium/clam, - /obj/item/device/electronic_assembly/medium/medical, - /obj/item/device/electronic_assembly/medium/gun, - /obj/item/device/electronic_assembly/medium/radio, - /obj/item/device/electronic_assembly/large/default, - /obj/item/device/electronic_assembly/large/scope, - /obj/item/device/electronic_assembly/large/terminal, - /obj/item/device/electronic_assembly/large/arm, - /obj/item/device/electronic_assembly/large/tall, - /obj/item/device/electronic_assembly/large/industrial, - /obj/item/device/electronic_assembly/drone/default, - /obj/item/device/electronic_assembly/drone/arms, - /obj/item/device/electronic_assembly/drone/secbot, - /obj/item/device/electronic_assembly/drone/medbot, - /obj/item/device/electronic_assembly/drone/genbot, - /obj/item/device/electronic_assembly/drone/android, - /obj/item/device/electronic_assembly/wallmount/tiny, - /obj/item/device/electronic_assembly/wallmount/light, - /obj/item/device/electronic_assembly/wallmount, - /obj/item/device/electronic_assembly/wallmount/heavy, - /obj/item/weapon/implant/integrated_circuit, - /obj/item/clothing/under/circuitry, - /obj/item/clothing/gloves/circuitry, - /obj/item/clothing/glasses/circuitry, - /obj/item/clothing/shoes/circuitry, - /obj/item/clothing/head/circuitry, - /obj/item/clothing/ears/circuitry, - /obj/item/clothing/suit/circuitry - ) - - circuit_fabricator_recipe_list["Tools"] = list( - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/device/integrated_electronics/detailer - ) +// +// This is for custom circuits, mostly the initialization of global properties about them. +// Might make this also process them in the future if its better to do that than using the obj ticker. +// +SUBSYSTEM_DEF(circuit) + name = "Circuit" + init_order = INIT_ORDER_CIRCUIT + flags = SS_NO_FIRE + var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs + var/list/cached_components = list() // Associative list of [component_path]:[component] pairs + var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs + var/list/cached_assemblies = list() // Associative list of [assembly_path]:[assembly] pairs + var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs + var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs +// var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3 + +/datum/controller/subsystem/circuit/Recover() + flags |= SS_NO_INIT // Make extra sure we don't initialize twice. + +/datum/controller/subsystem/circuit/Initialize(timeofday) + circuits_init() + return ..() + +/datum/controller/subsystem/circuit/proc/circuits_init() + //Cached lists for free performance + for(var/obj/item/integrated_circuit/IC as anything in typesof(/obj/item/integrated_circuit)) + var/path = IC + all_components[initial(IC.name)] = path // Populating the component lists + cached_components[path] = new path + + if(!(initial(IC.spawn_flags) & (IC_SPAWN_DEFAULT | IC_SPAWN_RESEARCH))) + continue + + var/category = initial(IC.category_text) + if(!circuit_fabricator_recipe_list[category]) + circuit_fabricator_recipe_list[category] = list() + var/list/category_list = circuit_fabricator_recipe_list[category] + category_list += IC // Populating the fabricator categories + + for(var/obj/item/device/electronic_assembly/A as anything in typesof(/obj/item/device/electronic_assembly)) + var/path = A + all_assemblies[initial(A.name)] = path + cached_assemblies[path] = new path + + + circuit_fabricator_recipe_list["Assemblies"] = list( + /obj/item/device/electronic_assembly/default, + /obj/item/device/electronic_assembly/calc, + /obj/item/device/electronic_assembly/clam, + /obj/item/device/electronic_assembly/simple, + /obj/item/device/electronic_assembly/hook, + /obj/item/device/electronic_assembly/pda, + /obj/item/device/electronic_assembly/tiny/default, + /obj/item/device/electronic_assembly/tiny/cylinder, + /obj/item/device/electronic_assembly/tiny/scanner, + /obj/item/device/electronic_assembly/tiny/hook, + /obj/item/device/electronic_assembly/tiny/box, + /obj/item/device/electronic_assembly/medium/default, + /obj/item/device/electronic_assembly/medium/box, + /obj/item/device/electronic_assembly/medium/clam, + /obj/item/device/electronic_assembly/medium/medical, + /obj/item/device/electronic_assembly/medium/gun, + /obj/item/device/electronic_assembly/medium/radio, + /obj/item/device/electronic_assembly/large/default, + /obj/item/device/electronic_assembly/large/scope, + /obj/item/device/electronic_assembly/large/terminal, + /obj/item/device/electronic_assembly/large/arm, + /obj/item/device/electronic_assembly/large/tall, + /obj/item/device/electronic_assembly/large/industrial, + /obj/item/device/electronic_assembly/drone/default, + /obj/item/device/electronic_assembly/drone/arms, + /obj/item/device/electronic_assembly/drone/secbot, + /obj/item/device/electronic_assembly/drone/medbot, + /obj/item/device/electronic_assembly/drone/genbot, + /obj/item/device/electronic_assembly/drone/android, + /obj/item/device/electronic_assembly/wallmount/tiny, + /obj/item/device/electronic_assembly/wallmount/light, + /obj/item/device/electronic_assembly/wallmount, + /obj/item/device/electronic_assembly/wallmount/heavy, + /obj/item/weapon/implant/integrated_circuit, + /obj/item/clothing/under/circuitry, + /obj/item/clothing/gloves/circuitry, + /obj/item/clothing/glasses/circuitry, + /obj/item/clothing/shoes/circuitry, + /obj/item/clothing/head/circuitry, + /obj/item/clothing/ears/circuitry, + /obj/item/clothing/suit/circuitry + ) + + circuit_fabricator_recipe_list["Tools"] = list( + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/device/integrated_electronics/detailer + ) diff --git a/code/controllers/subsystems/events2.dm b/code/controllers/subsystems/events2.dm index 7cf986358f7..e52025b2c06 100644 --- a/code/controllers/subsystems/events2.dm +++ b/code/controllers/subsystems/events2.dm @@ -1,36 +1,36 @@ -// This is a simple ticker for the new event system. -// The logic that determines what events get chosen is held inside a seperate subsystem. - -SUBSYSTEM_DEF(event_ticker) - name = "Events (Ticker)" - wait = 2 SECONDS - runlevels = RUNLEVEL_GAME - - // List of `/datum/event2/event`s that are currently active, and receiving process() ticks. - var/list/active_events = list() - - // List of `/datum/event2/event`s that finished, and are here for showing at roundend, if that's desired. - var/list/finished_events = list() - -// Process active events. -/datum/controller/subsystem/event_ticker/fire(resumed) - for(var/datum/event2/event/event as anything in active_events) - event.process() - if(event.finished) - event_finished(event) - -// Starts an event, independent of the GM system. -// This means it will always run, and won't affect the GM system in any way, e.g. not putting the event off limits after one use. -/datum/controller/subsystem/event_ticker/proc/start_event(event_type) - var/datum/event2/event/E = new event_type() - E.execute() - event_started(E) - -/datum/controller/subsystem/event_ticker/proc/event_started(datum/event2/event/E) - log_debug("Event [E.type] is now being ran.") - active_events += E - -/datum/controller/subsystem/event_ticker/proc/event_finished(datum/event2/event/E) - log_debug("Event [E.type] has finished.") - active_events -= E +// This is a simple ticker for the new event system. +// The logic that determines what events get chosen is held inside a seperate subsystem. + +SUBSYSTEM_DEF(event_ticker) + name = "Events (Ticker)" + wait = 2 SECONDS + runlevels = RUNLEVEL_GAME + + // List of `/datum/event2/event`s that are currently active, and receiving process() ticks. + var/list/active_events = list() + + // List of `/datum/event2/event`s that finished, and are here for showing at roundend, if that's desired. + var/list/finished_events = list() + +// Process active events. +/datum/controller/subsystem/event_ticker/fire(resumed) + for(var/datum/event2/event/event as anything in active_events) + event.process() + if(event.finished) + event_finished(event) + +// Starts an event, independent of the GM system. +// This means it will always run, and won't affect the GM system in any way, e.g. not putting the event off limits after one use. +/datum/controller/subsystem/event_ticker/proc/start_event(event_type) + var/datum/event2/event/E = new event_type() + E.execute() + event_started(E) + +/datum/controller/subsystem/event_ticker/proc/event_started(datum/event2/event/E) + log_debug("Event [E.type] is now being ran.") + active_events += E + +/datum/controller/subsystem/event_ticker/proc/event_finished(datum/event2/event/E) + log_debug("Event [E.type] has finished.") + active_events -= E finished_events += E \ No newline at end of file diff --git a/code/controllers/subsystems/game_master.dm b/code/controllers/subsystems/game_master.dm index 55e9a3e88a1..4703da0bf8c 100644 --- a/code/controllers/subsystems/game_master.dm +++ b/code/controllers/subsystems/game_master.dm @@ -1,364 +1,364 @@ -// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the -// previous ones, checking various things like player count, department size and composition, individual player activity, -// individual player (IC) skill, and such, in order to try to choose the best events to take in order to add spice or variety to -// the round. - -// This subsystem holds the logic that chooses events. Actual event processing is handled in a seperate subsystem. -SUBSYSTEM_DEF(game_master) - name = "Events (Game Master)" - wait = 1 MINUTE - runlevels = RUNLEVEL_GAME - - // The GM object is what actually chooses events. - // It's held in a seperate object for better encapsulation, and allows for different 'flavors' of GMs to be made, that choose events differently. - var/datum/game_master/GM = null - var/game_master_type = /datum/game_master/default - - var/list/available_events = list() // A list of meta event objects. - - var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. - var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. - - var/next_event = 0 // Minimum amount of time of nothingness until the GM can pick something again. - - var/debug_messages = FALSE // If true, debug information is written to `log_debug()`. - -/datum/controller/subsystem/game_master/Initialize() - var/list/subtypes = subtypesof(/datum/event2/meta) - for(var/T in subtypes) - var/datum/event2/meta/M = new T() - if(!M.name) - continue - available_events += M - - GM = new game_master_type() - - if(config && !config.enable_game_master) - can_fire = FALSE - - return ..() - -/datum/controller/subsystem/game_master/fire(resumed) - adjust_staleness(1) - adjust_danger(-1) - - var/global_afk = metric.assess_all_living_mobs() - global_afk = abs(global_afk - 100) - global_afk = round(global_afk / 100, 0.1) - adjust_staleness(global_afk) // Staleness increases faster if more people are less active. - - if(GM.ignore_time_restrictions || next_event < world.time) - if(prob(staleness) && pre_event_checks()) - do_event_decision() - - -/datum/controller/subsystem/game_master/proc/do_event_decision() - log_game_master("Going to choose an event.") - var/datum/event2/meta/event_picked = GM.choose_event() - if(event_picked) - run_event(event_picked) - next_event = world.time + rand(GM.decision_cooldown_lower_bound, GM.decision_cooldown_upper_bound) - -/datum/controller/subsystem/game_master/proc/debug_gm() - can_fire = TRUE - staleness = 100 - debug_messages = TRUE - -/datum/controller/subsystem/game_master/proc/run_event(datum/event2/meta/chosen_event) - var/datum/event2/event/E = chosen_event.make_event() - - chosen_event.times_ran++ - - if(!chosen_event.reusable) - // Disable this event, so it only gets picked once. - chosen_event.enabled = FALSE - if(chosen_event.event_class) - // Disable similar events, too. - for(var/datum/event2/meta/meta as anything in available_events) - if(meta.event_class == chosen_event.event_class) - meta.enabled = FALSE - - SSevent_ticker.event_started(E) - adjust_danger(chosen_event.chaos) - adjust_staleness(-(10 + chosen_event.chaos)) // More chaotic events reduce staleness more, e.g. a 25 chaos event will reduce it by 35. - - -// Tell the game master that something dangerous happened, e.g. someone dying, station explosions. -/datum/controller/subsystem/game_master/proc/adjust_danger(amount) - amount *= GM.danger_modifier - danger = round(between(0, danger + amount, 1000), 0.1) - -// Tell the game master that things are getting boring if positive, or something interesting if negative.. -/datum/controller/subsystem/game_master/proc/adjust_staleness(amount) - amount *= GM.staleness_modifier - staleness = round( between(-20, staleness + amount, 100), 0.1) - -// These are ran before committing to an event. -// Returns TRUE if the system is allowed to procede, otherwise returns FALSE. -/datum/controller/subsystem/game_master/proc/pre_event_checks(quiet = FALSE) - if(!ticker || ticker.current_state != GAME_STATE_PLAYING) - if(!quiet) - log_game_master("Unable to start event: Ticker is nonexistent, or the game is not ongoing.") - return FALSE - if(GM.ignore_time_restrictions) - return TRUE - if(next_event > world.time) // Sanity. - if(!quiet) - log_game_master("Unable to start event: Time until next event is approximately [round((next_event - world.time) / (1 MINUTE))] minute(s)") - return FALSE - - // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. - var/mills = round_duration_in_ds - var/mins = round((mills % 36000) / 600) - var/hours = round(mills / 36000) - -// if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. -// if(!quiet) -// log_debug("Game Master unable to start event: It is too early.") -// return FALSE - if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. - if(!quiet) - log_game_master("Unable to start event: It is too late.") - return FALSE - return TRUE - -/datum/controller/subsystem/game_master/proc/choose_game_master(mob/user) - var/list/subtypes = subtypesof(/datum/game_master) - var/new_gm_path = tgui_input_list(user, "What kind of Game Master do you want?", "New Game Master", subtypes) - if(new_gm_path) - log_and_message_admins("has swapped the current GM ([GM.type]) for a new GM ([new_gm_path]).") - GM = new new_gm_path(src) - -/datum/controller/subsystem/game_master/proc/log_game_master(message) - if(debug_messages) - log_debug("GAME MASTER: [message]") - - -// This object makes the actual decisions. -/datum/game_master - // Multiplier for how much 'danger' is accumulated. Higer generally makes it possible for more dangerous events to be picked. - var/danger_modifier = 1.0 - - // Ditto. Higher numbers generally result in more events occuring in a round. - var/staleness_modifier = 1.0 - - var/decision_cooldown_lower_bound = 5 MINUTES // Lower bound for how long to wait until -the potential- for another event being decided. - var/decision_cooldown_upper_bound = 20 MINUTES // Same, but upper bound. - - var/ignore_time_restrictions = FALSE // Useful for debugging without needing to wait 20 minutes each time. - var/ignore_round_chaos = FALSE // If true, the system will happily choose back to back intense events like meteors and blobs, Dwarf Fortress style. - -/client/proc/show_gm_status() - set category = "Debug" - set name = "Show GM Status" - set desc = "Shows you what the GM is thinking. If only that existed in real life..." - - if(check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - SSgame_master.interact(usr) - else - to_chat(usr, span("warning", "You do not have sufficient rights to view the GM panel, sorry.")) - -/datum/controller/subsystem/game_master/proc/interact(var/client/user) - if(!user) - return - - // Using lists for string tree conservation. - var/list/dat = list("Automated Game Master Event System") - - // Makes the system turn on or off. - dat += href(src, list("toggle" = 1), "\[Toggle GM\]") - dat += " | " - - // Makes the system not care about staleness or being near round-end. - dat += href(src, list("toggle_time_restrictions" = 1), "\[Toggle Time Restrictions\]") - dat += " | " - - // Makes the system not care about how chaotic the round might be. - dat += href(src, list("toggle_chaos_throttle" = 1), "\[Toggle Chaos Throttling\]") - dat += " | " - - // Makes the system immediately choose an event, while still bound to factors like danger, weights, and department staffing. - dat += href(src, list("force_choose_event" = 1), "\[Force Event Decision\]") - dat += "
    " - - // Swaps out the current GM for a new one with different ideas on what a good event might be. - dat += href(src, list("change_gm" = 1), "\[Change GM\]") - dat += "
    " - - dat += "Current GM Type: [GM.type]
    " - dat += "State: [can_fire ? "Active": "Inactive"]
    " - dat += "Status: [pre_event_checks(TRUE) ? "Ready" : "Suppressed"]

    " - - dat += "Staleness: [staleness] " - dat += href(src, list("set_staleness" = 1), "\[Set\]") - dat += "
    " - dat += "Staleness is an estimate of how boring the round might be, and if an event should be done. It is increased passively over time, \ - and increases faster if people are AFK. It deceases when events and certain 'interesting' things happen in the round.
    " - - dat += "Danger: [danger] " - dat += href(src, list("set_danger" = 1), "\[Set\]") - dat += "
    " - dat += "Danger is an estimate of how chaotic the round has been so far. It is decreased passively over time, and is increased by having \ - certain chaotic events be selected, or chaotic things happen in the round. A sufficently high amount of danger will make the system \ - avoid using destructive events, to avoid pushing the station over the edge.
    " - - dat += "

    Player Activity:

    " - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - - for(var/D in metric.departments) - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - dat += "" - - for(var/mob/M as anything in player_list) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    CategoryActivity Percentage
    All Living Mobs[metric.assess_all_living_mobs()]%
    All Ghosts[metric.assess_all_dead_mobs()]%
    Departments" - dat += "
    [D][metric.assess_department(D)]%
    Players" - dat += "
    [M] ([M.ckey])[metric.assess_player_activity(M)]%
    " - - dat += "

    Events available:

    " - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/datum/event2/meta/event as anything in available_events) - dat += "" - if(!event.enabled) - dat += "" - else - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    Event NameInvolved DepartmentsChaosChaotic ThresholdWeightButtons
    [event.name][event.name][english_list(event.departments)][event.chaos][event.chaotic_threshold][event.get_weight()][href(event, list("force" = 1), "\[Force\]")] [href(event, list("toggle" = 1), "\[Toggle\]")]
    " - - dat += "

    Events active:

    " - - dat += "Current time: [world.time]" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/datum/event2/event/event as anything in SSevent_ticker.active_events) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "
    Event TypeTime StartedTime to AnnounceTime to EndAnnouncedStartedEndedButtons
    [event.type][event.time_started][event.time_to_announce ? event.time_to_announce : "NULL"][event.time_to_end ? event.time_to_end : "NULL"][event.announced ? "Yes" : "No"][event.started ? "Yes" : "No"][event.ended ? "Yes" : "No"][href(event, list("abort" = 1), "\[Abort\]")]
    " - dat += "" - - dat += "

    Events completed:

    " - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/datum/event2/event/event as anything in SSevent_ticker.finished_events) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - dat += "" - - var/datum/browser/popup = new(user, "game_master_debug", "Automated Game Master Event System", 800, 500, src) - popup.set_content(dat.Join()) - popup.open() - - -/datum/controller/subsystem/game_master/Topic(href, href_list) - if(..()) - return - - if(href_list["close"]) // Needed or the window re-opens after closing, making it last forever. - return - - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - message_admins("[usr] has attempted to modify the Game Master values without sufficent privilages.") - return - - if(href_list["toggle"]) - can_fire = !can_fire - message_admins("GM was [!can_fire ? "dis" : "en"]abled by [usr.key].") - - if(href_list["toggle_time_restrictions"]) - GM.ignore_time_restrictions = !GM.ignore_time_restrictions - message_admins("GM event time restrictions was [GM.ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].") - - if(href_list["toggle_chaos_throttle"]) - GM.ignore_round_chaos = !GM.ignore_round_chaos - message_admins("GM event chaos restrictions was [GM.ignore_round_chaos ? "dis" : "en"]abled by [usr.key].") - - if(href_list["force_choose_event"]) - do_event_decision() - message_admins("[usr.key] forced the Game Master to choose an event immediately.") - - if(href_list["change_gm"]) - choose_game_master(usr) - - if(href_list["set_staleness"]) - var/amount = tgui_input_number(usr, "How much staleness should there be?", "Game Master") - if(!isnull(amount)) - staleness = amount - message_admins("GM staleness was set to [amount] by [usr.key].") - - if(href_list["set_danger"]) - var/amount = tgui_input_number(usr, "How much danger should there be?", "Game Master") - if(!isnull(amount)) - danger = amount - message_admins("GM danger was set to [amount] by [usr.key].") - - interact(usr) // To refresh the UI. +// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the +// previous ones, checking various things like player count, department size and composition, individual player activity, +// individual player (IC) skill, and such, in order to try to choose the best events to take in order to add spice or variety to +// the round. + +// This subsystem holds the logic that chooses events. Actual event processing is handled in a seperate subsystem. +SUBSYSTEM_DEF(game_master) + name = "Events (Game Master)" + wait = 1 MINUTE + runlevels = RUNLEVEL_GAME + + // The GM object is what actually chooses events. + // It's held in a seperate object for better encapsulation, and allows for different 'flavors' of GMs to be made, that choose events differently. + var/datum/game_master/GM = null + var/game_master_type = /datum/game_master/default + + var/list/available_events = list() // A list of meta event objects. + + var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. + var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. + + var/next_event = 0 // Minimum amount of time of nothingness until the GM can pick something again. + + var/debug_messages = FALSE // If true, debug information is written to `log_debug()`. + +/datum/controller/subsystem/game_master/Initialize() + var/list/subtypes = subtypesof(/datum/event2/meta) + for(var/T in subtypes) + var/datum/event2/meta/M = new T() + if(!M.name) + continue + available_events += M + + GM = new game_master_type() + + if(config && !config.enable_game_master) + can_fire = FALSE + + return ..() + +/datum/controller/subsystem/game_master/fire(resumed) + adjust_staleness(1) + adjust_danger(-1) + + var/global_afk = metric.assess_all_living_mobs() + global_afk = abs(global_afk - 100) + global_afk = round(global_afk / 100, 0.1) + adjust_staleness(global_afk) // Staleness increases faster if more people are less active. + + if(GM.ignore_time_restrictions || next_event < world.time) + if(prob(staleness) && pre_event_checks()) + do_event_decision() + + +/datum/controller/subsystem/game_master/proc/do_event_decision() + log_game_master("Going to choose an event.") + var/datum/event2/meta/event_picked = GM.choose_event() + if(event_picked) + run_event(event_picked) + next_event = world.time + rand(GM.decision_cooldown_lower_bound, GM.decision_cooldown_upper_bound) + +/datum/controller/subsystem/game_master/proc/debug_gm() + can_fire = TRUE + staleness = 100 + debug_messages = TRUE + +/datum/controller/subsystem/game_master/proc/run_event(datum/event2/meta/chosen_event) + var/datum/event2/event/E = chosen_event.make_event() + + chosen_event.times_ran++ + + if(!chosen_event.reusable) + // Disable this event, so it only gets picked once. + chosen_event.enabled = FALSE + if(chosen_event.event_class) + // Disable similar events, too. + for(var/datum/event2/meta/meta as anything in available_events) + if(meta.event_class == chosen_event.event_class) + meta.enabled = FALSE + + SSevent_ticker.event_started(E) + adjust_danger(chosen_event.chaos) + adjust_staleness(-(10 + chosen_event.chaos)) // More chaotic events reduce staleness more, e.g. a 25 chaos event will reduce it by 35. + + +// Tell the game master that something dangerous happened, e.g. someone dying, station explosions. +/datum/controller/subsystem/game_master/proc/adjust_danger(amount) + amount *= GM.danger_modifier + danger = round(between(0, danger + amount, 1000), 0.1) + +// Tell the game master that things are getting boring if positive, or something interesting if negative.. +/datum/controller/subsystem/game_master/proc/adjust_staleness(amount) + amount *= GM.staleness_modifier + staleness = round( between(-20, staleness + amount, 100), 0.1) + +// These are ran before committing to an event. +// Returns TRUE if the system is allowed to procede, otherwise returns FALSE. +/datum/controller/subsystem/game_master/proc/pre_event_checks(quiet = FALSE) + if(!ticker || ticker.current_state != GAME_STATE_PLAYING) + if(!quiet) + log_game_master("Unable to start event: Ticker is nonexistent, or the game is not ongoing.") + return FALSE + if(GM.ignore_time_restrictions) + return TRUE + if(next_event > world.time) // Sanity. + if(!quiet) + log_game_master("Unable to start event: Time until next event is approximately [round((next_event - world.time) / (1 MINUTE))] minute(s)") + return FALSE + + // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. + var/mills = round_duration_in_ds + var/mins = round((mills % 36000) / 600) + var/hours = round(mills / 36000) + +// if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. +// if(!quiet) +// log_debug("Game Master unable to start event: It is too early.") +// return FALSE + if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. + if(!quiet) + log_game_master("Unable to start event: It is too late.") + return FALSE + return TRUE + +/datum/controller/subsystem/game_master/proc/choose_game_master(mob/user) + var/list/subtypes = subtypesof(/datum/game_master) + var/new_gm_path = tgui_input_list(user, "What kind of Game Master do you want?", "New Game Master", subtypes) + if(new_gm_path) + log_and_message_admins("has swapped the current GM ([GM.type]) for a new GM ([new_gm_path]).") + GM = new new_gm_path(src) + +/datum/controller/subsystem/game_master/proc/log_game_master(message) + if(debug_messages) + log_debug("GAME MASTER: [message]") + + +// This object makes the actual decisions. +/datum/game_master + // Multiplier for how much 'danger' is accumulated. Higer generally makes it possible for more dangerous events to be picked. + var/danger_modifier = 1.0 + + // Ditto. Higher numbers generally result in more events occuring in a round. + var/staleness_modifier = 1.0 + + var/decision_cooldown_lower_bound = 5 MINUTES // Lower bound for how long to wait until -the potential- for another event being decided. + var/decision_cooldown_upper_bound = 20 MINUTES // Same, but upper bound. + + var/ignore_time_restrictions = FALSE // Useful for debugging without needing to wait 20 minutes each time. + var/ignore_round_chaos = FALSE // If true, the system will happily choose back to back intense events like meteors and blobs, Dwarf Fortress style. + +/client/proc/show_gm_status() + set category = "Debug" + set name = "Show GM Status" + set desc = "Shows you what the GM is thinking. If only that existed in real life..." + + if(check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + SSgame_master.interact(usr) + else + to_chat(usr, span("warning", "You do not have sufficient rights to view the GM panel, sorry.")) + +/datum/controller/subsystem/game_master/proc/interact(var/client/user) + if(!user) + return + + // Using lists for string tree conservation. + var/list/dat = list("Automated Game Master Event System") + + // Makes the system turn on or off. + dat += href(src, list("toggle" = 1), "\[Toggle GM\]") + dat += " | " + + // Makes the system not care about staleness or being near round-end. + dat += href(src, list("toggle_time_restrictions" = 1), "\[Toggle Time Restrictions\]") + dat += " | " + + // Makes the system not care about how chaotic the round might be. + dat += href(src, list("toggle_chaos_throttle" = 1), "\[Toggle Chaos Throttling\]") + dat += " | " + + // Makes the system immediately choose an event, while still bound to factors like danger, weights, and department staffing. + dat += href(src, list("force_choose_event" = 1), "\[Force Event Decision\]") + dat += "
    " + + // Swaps out the current GM for a new one with different ideas on what a good event might be. + dat += href(src, list("change_gm" = 1), "\[Change GM\]") + dat += "
    " + + dat += "Current GM Type: [GM.type]
    " + dat += "State: [can_fire ? "Active": "Inactive"]
    " + dat += "Status: [pre_event_checks(TRUE) ? "Ready" : "Suppressed"]

    " + + dat += "Staleness: [staleness] " + dat += href(src, list("set_staleness" = 1), "\[Set\]") + dat += "
    " + dat += "Staleness is an estimate of how boring the round might be, and if an event should be done. It is increased passively over time, \ + and increases faster if people are AFK. It deceases when events and certain 'interesting' things happen in the round.
    " + + dat += "Danger: [danger] " + dat += href(src, list("set_danger" = 1), "\[Set\]") + dat += "
    " + dat += "Danger is an estimate of how chaotic the round has been so far. It is decreased passively over time, and is increased by having \ + certain chaotic events be selected, or chaotic things happen in the round. A sufficently high amount of danger will make the system \ + avoid using destructive events, to avoid pushing the station over the edge.
    " + + dat += "

    Player Activity:

    " + + dat += "
    Event TypeStart TimeFinish Time
    [event.type][event.time_started][event.time_finished]
    " + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + + for(var/D in metric.departments) + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + + for(var/mob/M as anything in player_list) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
    CategoryActivity Percentage
    All Living Mobs[metric.assess_all_living_mobs()]%
    All Ghosts[metric.assess_all_dead_mobs()]%
    Departments" + dat += "
    [D][metric.assess_department(D)]%
    Players" + dat += "
    [M] ([M.ckey])[metric.assess_player_activity(M)]%
    " + + dat += "

    Events available:

    " + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/datum/event2/meta/event as anything in available_events) + dat += "" + if(!event.enabled) + dat += "" + else + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
    Event NameInvolved DepartmentsChaosChaotic ThresholdWeightButtons
    [event.name][event.name][english_list(event.departments)][event.chaos][event.chaotic_threshold][event.get_weight()][href(event, list("force" = 1), "\[Force\]")] [href(event, list("toggle" = 1), "\[Toggle\]")]
    " + + dat += "

    Events active:

    " + + dat += "Current time: [world.time]" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/datum/event2/event/event as anything in SSevent_ticker.active_events) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
    Event TypeTime StartedTime to AnnounceTime to EndAnnouncedStartedEndedButtons
    [event.type][event.time_started][event.time_to_announce ? event.time_to_announce : "NULL"][event.time_to_end ? event.time_to_end : "NULL"][event.announced ? "Yes" : "No"][event.started ? "Yes" : "No"][event.ended ? "Yes" : "No"][href(event, list("abort" = 1), "\[Abort\]")]
    " + dat += "" + + dat += "

    Events completed:

    " + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/datum/event2/event/event as anything in SSevent_ticker.finished_events) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + + var/datum/browser/popup = new(user, "game_master_debug", "Automated Game Master Event System", 800, 500, src) + popup.set_content(dat.Join()) + popup.open() + + +/datum/controller/subsystem/game_master/Topic(href, href_list) + if(..()) + return + + if(href_list["close"]) // Needed or the window re-opens after closing, making it last forever. + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to modify the Game Master values without sufficent privilages.") + return + + if(href_list["toggle"]) + can_fire = !can_fire + message_admins("GM was [!can_fire ? "dis" : "en"]abled by [usr.key].") + + if(href_list["toggle_time_restrictions"]) + GM.ignore_time_restrictions = !GM.ignore_time_restrictions + message_admins("GM event time restrictions was [GM.ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].") + + if(href_list["toggle_chaos_throttle"]) + GM.ignore_round_chaos = !GM.ignore_round_chaos + message_admins("GM event chaos restrictions was [GM.ignore_round_chaos ? "dis" : "en"]abled by [usr.key].") + + if(href_list["force_choose_event"]) + do_event_decision() + message_admins("[usr.key] forced the Game Master to choose an event immediately.") + + if(href_list["change_gm"]) + choose_game_master(usr) + + if(href_list["set_staleness"]) + var/amount = tgui_input_number(usr, "How much staleness should there be?", "Game Master") + if(!isnull(amount)) + staleness = amount + message_admins("GM staleness was set to [amount] by [usr.key].") + + if(href_list["set_danger"]) + var/amount = tgui_input_number(usr, "How much danger should there be?", "Game Master") + if(!isnull(amount)) + danger = amount + message_admins("GM danger was set to [amount] by [usr.key].") + + interact(usr) // To refresh the UI. diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index c1fe92295dd..400a61b0f94 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -26,6 +26,7 @@ SUBSYSTEM_DEF(garbage) var/list/queues #ifdef REFERENCE_TRACKING var/list/reference_find_on_fail = list() + var/find_reference_on_fail_global_toggle = FALSE #endif diff --git a/code/controllers/subsystems/job.dm b/code/controllers/subsystems/job.dm index 30878ea6acf..c137582c7c5 100644 --- a/code/controllers/subsystems/job.dm +++ b/code/controllers/subsystems/job.dm @@ -1,143 +1,143 @@ -SUBSYSTEM_DEF(job) - name = "Job" - init_order = INIT_ORDER_JOB - flags = SS_NO_FIRE - - var/list/occupations = list() //List of all jobs - var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles - var/list/type_occupations = list() //Dict of all jobs, keys are types - - var/list/department_datums = list() - var/debug_messages = FALSE - - -/datum/controller/subsystem/job/Initialize(timeofday) - if(!department_datums.len) - setup_departments() - if(!occupations.len) - setup_occupations() - return ..() - -/datum/controller/subsystem/job/proc/setup_occupations(faction = "Station") - occupations = list() - var/list/all_jobs = subtypesof(/datum/job) - if(!all_jobs.len) - to_chat(world, span("warning", "Error setting up jobs, no job datums found")) - return FALSE - - for(var/J in all_jobs) - var/datum/job/job = new J() - if(!job) - continue - if(job.faction != faction) - continue - occupations += job - name_occupations[job.title] = job - type_occupations[J] = job - if(LAZYLEN(job.departments)) - add_to_departments(job) - - sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) - for(var/D in department_datums) - var/datum/department/dept = department_datums[D] - sortTim(dept.jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) - sortTim(dept.primary_jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) - - return TRUE - -/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J) - // Adds to the regular job lists in the departments, which allow multiple departments for a job. - for(var/D in J.departments) - var/datum/department/dept = LAZYACCESS(department_datums, D) - if(!istype(dept)) - job_debug_message("Job '[J.title]' is defined as being inside department '[D]', but it does not exist.") - continue - dept.jobs[J.title] = J - - // Now for the 'primary' department for a job, which is defined as being the first department in the list for a job. - // This results in no duplicates, which can be useful in some situations. - if(LAZYLEN(J.departments)) - var/primary_department = J.departments[1] - var/datum/department/dept = LAZYACCESS(department_datums, primary_department) - if(!istype(dept)) - job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") - else - dept.primary_jobs[J.title] = J - -/datum/controller/subsystem/job/proc/setup_departments() - for(var/t in subtypesof(/datum/department)) - var/datum/department/D = new t() - department_datums[D.name] = D - - sortTim(department_datums, GLOBAL_PROC_REF(cmp_department_datums), TRUE) - -/datum/controller/subsystem/job/proc/get_all_department_datums() - var/list/dept_datums = list() - for(var/D in department_datums) - dept_datums += department_datums[D] - return dept_datums - -/datum/controller/subsystem/job/proc/get_job(rank) - if(!occupations.len) - setup_occupations() - return name_occupations[rank] - -/datum/controller/subsystem/job/proc/get_job_type(jobtype) - if(!occupations.len) - setup_occupations() - return type_occupations[jobtype] - -// Determines if a job title is inside of a specific department. -// Useful to replace the old `if(job_title in command_positions)` code. -/datum/controller/subsystem/job/proc/is_job_in_department(rank, target_department_name) - var/datum/department/D = LAZYACCESS(department_datums, target_department_name) - if(istype(D)) - return LAZYFIND(D.jobs, rank) ? TRUE : FALSE - return FALSE - -// Returns a list of all job names in a specific department. -/datum/controller/subsystem/job/proc/get_job_titles_in_department(target_department_name) - var/datum/department/D = LAZYACCESS(department_datums, target_department_name) - if(istype(D)) - var/list/job_titles = list() - for(var/J in D.jobs) - job_titles += J - return job_titles - - job_debug_message("Was asked to get job titles for a non-existant department '[target_department_name]'.") - return list() - -// Returns a reference to the primary department datum that a job is in. -// Can receive job datum refs, typepaths, or job title strings. -/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J) - if(!istype(J, /datum/job)) - if(ispath(J)) - J = get_job_type(J) - else if(istext(J)) - J = get_job(J) - - if(!istype(J)) - job_debug_message("Was asked to get department for job '[J]', but input could not be resolved into a job datum.") - return - - if(!LAZYLEN(J.departments)) - return - - var/primary_department = J.departments[1] - var/datum/department/dept = LAZYACCESS(department_datums, primary_department) - if(!istype(dept)) - job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") - return - - return department_datums[primary_department] - -/datum/controller/subsystem/job/proc/get_ping_role(var/role) - var/datum/job/J = get_job(role) - if(J.requestable) - return get_primary_department_of_job(J) - -// Someday it might be good to port code/game/jobs/job_controller.dm to here and clean it up. - -/datum/controller/subsystem/job/proc/job_debug_message(message) - if(debug_messages) - log_debug("JOB DEBUG: [message]") +SUBSYSTEM_DEF(job) + name = "Job" + init_order = INIT_ORDER_JOB + flags = SS_NO_FIRE + + var/list/occupations = list() //List of all jobs + var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles + var/list/type_occupations = list() //Dict of all jobs, keys are types + + var/list/department_datums = list() + var/debug_messages = FALSE + + +/datum/controller/subsystem/job/Initialize(timeofday) + if(!department_datums.len) + setup_departments() + if(!occupations.len) + setup_occupations() + return ..() + +/datum/controller/subsystem/job/proc/setup_occupations(faction = "Station") + occupations = list() + var/list/all_jobs = subtypesof(/datum/job) + if(!all_jobs.len) + to_chat(world, span("warning", "Error setting up jobs, no job datums found")) + return FALSE + + for(var/J in all_jobs) + var/datum/job/job = new J() + if(!job) + continue + if(job.faction != faction) + continue + occupations += job + name_occupations[job.title] = job + type_occupations[J] = job + if(LAZYLEN(job.departments)) + add_to_departments(job) + + sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) + for(var/D in department_datums) + var/datum/department/dept = department_datums[D] + sortTim(dept.jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) + sortTim(dept.primary_jobs, GLOBAL_PROC_REF(cmp_job_datums), TRUE) + + return TRUE + +/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J) + // Adds to the regular job lists in the departments, which allow multiple departments for a job. + for(var/D in J.departments) + var/datum/department/dept = LAZYACCESS(department_datums, D) + if(!istype(dept)) + job_debug_message("Job '[J.title]' is defined as being inside department '[D]', but it does not exist.") + continue + dept.jobs[J.title] = J + + // Now for the 'primary' department for a job, which is defined as being the first department in the list for a job. + // This results in no duplicates, which can be useful in some situations. + if(LAZYLEN(J.departments)) + var/primary_department = J.departments[1] + var/datum/department/dept = LAZYACCESS(department_datums, primary_department) + if(!istype(dept)) + job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") + else + dept.primary_jobs[J.title] = J + +/datum/controller/subsystem/job/proc/setup_departments() + for(var/t in subtypesof(/datum/department)) + var/datum/department/D = new t() + department_datums[D.name] = D + + sortTim(department_datums, GLOBAL_PROC_REF(cmp_department_datums), TRUE) + +/datum/controller/subsystem/job/proc/get_all_department_datums() + var/list/dept_datums = list() + for(var/D in department_datums) + dept_datums += department_datums[D] + return dept_datums + +/datum/controller/subsystem/job/proc/get_job(rank) + if(!occupations.len) + setup_occupations() + return name_occupations[rank] + +/datum/controller/subsystem/job/proc/get_job_type(jobtype) + if(!occupations.len) + setup_occupations() + return type_occupations[jobtype] + +// Determines if a job title is inside of a specific department. +// Useful to replace the old `if(job_title in command_positions)` code. +/datum/controller/subsystem/job/proc/is_job_in_department(rank, target_department_name) + var/datum/department/D = LAZYACCESS(department_datums, target_department_name) + if(istype(D)) + return LAZYFIND(D.jobs, rank) ? TRUE : FALSE + return FALSE + +// Returns a list of all job names in a specific department. +/datum/controller/subsystem/job/proc/get_job_titles_in_department(target_department_name) + var/datum/department/D = LAZYACCESS(department_datums, target_department_name) + if(istype(D)) + var/list/job_titles = list() + for(var/J in D.jobs) + job_titles += J + return job_titles + + job_debug_message("Was asked to get job titles for a non-existant department '[target_department_name]'.") + return list() + +// Returns a reference to the primary department datum that a job is in. +// Can receive job datum refs, typepaths, or job title strings. +/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J) + if(!istype(J, /datum/job)) + if(ispath(J)) + J = get_job_type(J) + else if(istext(J)) + J = get_job(J) + + if(!istype(J)) + job_debug_message("Was asked to get department for job '[J]', but input could not be resolved into a job datum.") + return + + if(!LAZYLEN(J.departments)) + return + + var/primary_department = J.departments[1] + var/datum/department/dept = LAZYACCESS(department_datums, primary_department) + if(!istype(dept)) + job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") + return + + return department_datums[primary_department] + +/datum/controller/subsystem/job/proc/get_ping_role(var/role) + var/datum/job/J = get_job(role) + if(J.requestable) + return get_primary_department_of_job(J) + +// Someday it might be good to port code/game/jobs/job_controller.dm to here and clean it up. + +/datum/controller/subsystem/job/proc/job_debug_message(message) + if(debug_messages) + log_debug("JOB DEBUG: [message]") diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm index 100811f5c09..a4a836f2001 100644 --- a/code/controllers/subsystems/machines.dm +++ b/code/controllers/subsystems/machines.dm @@ -23,10 +23,17 @@ SUBSYSTEM_DEF(machines) var/list/current_run = list() + var/list/all_machines = list() + + var/list/networks = list() + var/list/processing_machines = list() + var/list/powernets = list() + var/list/powerobjs = list() + /datum/controller/subsystem/machines/Initialize(timeofday) makepowernets() admin_notice("Initializing atmos machinery.", R_DEBUG) - setup_atmos_machinery(global.machines) + setup_atmos_machinery(all_machines) fire() ..() @@ -83,24 +90,24 @@ SUBSYSTEM_DEF(machines) msg += "PN:[round(cost_powernets,1)]|" msg += "PO:[round(cost_power_objects,1)]" msg += "} " - msg += "PI:[global.pipe_networks.len]|" - msg += "MC:[global.processing_machines.len]|" - msg += "PN:[global.powernets.len]|" - msg += "PO:[global.processing_power_items.len]|" - msg += "MC/MS:[round((cost ? global.processing_machines.len/cost_machinery : 0),0.1)]" + msg += "PI:[SSmachines.networks.len]|" + msg += "MC:[SSmachines.processing_machines.len]|" + msg += "PN:[SSmachines.powernets.len]|" + msg += "PO:[SSmachines.powerobjs.len]|" + msg += "MC/MS:[round((cost ? SSmachines.processing_machines.len/cost_machinery : 0),0.1)]" ..(jointext(msg, null)) /datum/controller/subsystem/machines/proc/process_pipenets(resumed = 0) if (!resumed) - src.current_run = global.pipe_networks.Copy() + src.current_run = networks.Copy() //cache for sanic speed (lists are references anyways) var/wait = src.wait var/list/current_run = src.current_run while(current_run.len) var/datum/pipe_network/PN = current_run[current_run.len] current_run.len-- - if(QDELETED(PN)) - global.pipe_networks.Remove(PN) + if(!PN) + networks.Remove(PN) DISABLE_BITFIELD(PN?.datum_flags, DF_ISPROCESSING) else PN.process(wait) @@ -109,30 +116,30 @@ SUBSYSTEM_DEF(machines) /datum/controller/subsystem/machines/proc/process_machinery(resumed = 0) if (!resumed) - src.current_run = global.processing_machines.Copy() + src.current_run = processing_machines.Copy() var/wait = src.wait var/list/current_run = src.current_run while(current_run.len) var/obj/machinery/M = current_run[current_run.len] current_run.len-- - if(QDELETED(M) || (M.process(wait) == PROCESS_KILL)) - global.processing_machines.Remove(M) + if(!M || (M.process(wait) == PROCESS_KILL)) + processing_machines.Remove(M) DISABLE_BITFIELD(M?.datum_flags, DF_ISPROCESSING) if(MC_TICK_CHECK) return /datum/controller/subsystem/machines/proc/process_powernets(resumed = 0) if (!resumed) - src.current_run = global.powernets.Copy() + src.current_run = powernets.Copy() var/wait = src.wait var/list/current_run = src.current_run while(current_run.len) var/datum/powernet/PN = current_run[current_run.len] current_run.len-- - if(QDELETED(PN)) - global.powernets.Remove(PN) + if(!PN) + powernets.Remove(PN) DISABLE_BITFIELD(PN?.datum_flags, DF_ISPROCESSING) else PN.reset(wait) @@ -143,36 +150,42 @@ SUBSYSTEM_DEF(machines) // Currently only used by powersinks. These items get priority processed before machinery /datum/controller/subsystem/machines/proc/process_power_objects(resumed = 0) if (!resumed) - src.current_run = global.processing_power_items.Copy() + src.current_run = powerobjs.Copy() var/wait = src.wait var/list/current_run = src.current_run while(current_run.len) var/obj/item/I = current_run[current_run.len] current_run.len-- - if(QDELETED(I) || (I.pwr_drain(wait) == PROCESS_KILL)) - global.processing_power_items.Remove(I) + if(!I || (I.pwr_drain(wait) == PROCESS_KILL)) + powerobjs.Remove(I) DISABLE_BITFIELD(I?.datum_flags, DF_ISPROCESSING) if(MC_TICK_CHECK) return /datum/controller/subsystem/machines/Recover() - for(var/datum/D as anything in global.pipe_networks) + for(var/datum/D as anything in SSmachines.networks) if(!istype(D, /datum/pipe_network)) - error("Found wrong type during SSmachinery recovery: list=global.pipe_networks, item=[D], type=[D?.type]") - global.pipe_networks -= D - for(var/datum/D as anything in global.processing_machines) + error("Found wrong type during SSmachinery recovery: list=SSmachines.networks, item=[D], type=[D?.type]") + SSmachines.networks -= D + for(var/datum/D as anything in SSmachines.processing_machines) if(!istype(D, /obj/machinery)) - error("Found wrong type during SSmachinery recovery: list=global.processing_machines, item=[D], type=[D?.type]") - global.processing_machines -= D - for(var/datum/D as anything in global.powernets) + error("Found wrong type during SSmachinery recovery: list=SSmachines.machines, item=[D], type=[D?.type]") + SSmachines.processing_machines -= D + for(var/datum/D as anything in SSmachines.powernets) if(!istype(D, /datum/powernet)) - error("Found wrong type during SSmachinery recovery: list=global.powernets, item=[D], type=[D?.type]") - global.powernets -= D - for(var/datum/D as anything in global.processing_power_items) + error("Found wrong type during SSmachinery recovery: list=SSmachines.powernets, item=[D], type=[D?.type]") + SSmachines.powernets -= D + for(var/datum/D as anything in SSmachines.powerobjs) if(!istype(D, /obj/item)) - error("Found wrong type during SSmachinery recovery: list=global.processing_power_items, item=[D], type=[D?.type]") - global.processing_power_items -= D + error("Found wrong type during SSmachinery recovery: list=SSmachines.powerobjs, item=[D], type=[D?.type]") + SSmachines.powerobjs -= D + + all_machines = SSmachines.all_machines + networks = SSmachines.networks + processing_machines = SSmachines.processing_machines + powernets = SSmachines.powernets + powerobjs = SSmachines.powerobjs #undef SSMACHINES_PIPENETS #undef SSMACHINES_MACHINERY diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index 00e3153b3e1..e496a97f18f 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -1,175 +1,175 @@ -// Handles map-related tasks, mostly here to ensure it does so after the MC initializes. -SUBSYSTEM_DEF(mapping) - name = "Mapping" - init_order = INIT_ORDER_MAPPING - flags = SS_NO_FIRE - - var/list/map_templates = list() - var/dmm_suite/maploader = null - var/obj/effect/landmark/engine_loader/engine_loader - var/list/shelter_templates = list() - -/datum/controller/subsystem/mapping/Recover() - flags |= SS_NO_INIT // Make extra sure we don't initialize twice. - shelter_templates = SSmapping.shelter_templates - -/datum/controller/subsystem/mapping/Initialize(timeofday) - if(subsystem_initialized) - return - world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably) - maploader = new() - load_map_templates() - - if(config.generate_map) - // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. - using_map.perform_map_generation() - - loadEngine() - preloadShelterTemplates() // VOREStation EDIT: Re-enable Shelter Capsules - // Mining generation probably should be here too - // TODO - Other stuff related to maps and areas could be moved here too. Look at /tg - // Lateload Code related to Expedition areas. - if(using_map) // VOREStation Edit: Re-enable this. - loadLateMaps() - ..() - -/datum/controller/subsystem/mapping/proc/load_map_templates() - for(var/datum/map_template/template as anything in subtypesof(/datum/map_template)) - if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence. - continue - template = new template() - map_templates[template.name] = template - return TRUE - -/datum/controller/subsystem/mapping/proc/loadEngine() - if(!engine_loader) - return // Seems this map doesn't need an engine loaded. - - var/turf/T = get_turf(engine_loader) - if(!isturf(T)) - to_world_log("[log_info_line(engine_loader)] not on a turf! Cannot place engine template.") - return - - // Choose an engine type - var/datum/map_template/engine/chosen_type = null - if (LAZYLEN(config.engine_map)) - var/chosen_name = pick(config.engine_map) - chosen_type = map_templates[chosen_name] - if(!istype(chosen_type)) - error("Configured engine map [chosen_name] is not a valid engine map name!") - if(!istype(chosen_type)) - var/list/engine_types = list() - for(var/map in map_templates) - var/datum/map_template/engine/MT = map_templates[map] - if(istype(MT)) - engine_types += MT - chosen_type = pick(engine_types) - to_world_log("Chose Engine Map: [chosen_type.name]") - admin_notice("Chose Engine Map: [chosen_type.name]", R_DEBUG) - - // Annihilate movable atoms - engine_loader.annihilate_bounds() - //CHECK_TICK //Don't let anything else happen for now - // Actually load it - chosen_type.load(T) - -// VOREStation Edit Start: Enable This -/datum/controller/subsystem/mapping/proc/loadLateMaps() - var/list/deffo_load = using_map.lateload_z_levels - var/list/maybe_load = using_map.lateload_gateway - var/list/also_load = using_map.lateload_overmap - var/list/redgate_load = using_map.lateload_redgate - - for(var/list/maplist in deffo_load) - if(!islist(maplist)) - error("Lateload Z level [maplist] is not a list! Must be in a list!") - continue - for(var/mapname in maplist) - var/datum/map_template/MT = map_templates[mapname] - if(!istype(MT)) - error("Lateload Z level \"[mapname]\" is not a valid map!") - continue - admin_notice("Lateload: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - CHECK_TICK - - if(LAZYLEN(maybe_load)) - var/picklist = pick(maybe_load) - - if(!picklist) //No lateload maps at all - return - - if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission - error("Randompick Z level [picklist] is not a list! Must be in a list!") - return - - for(var/map in picklist) - if(islist(map)) - // TRIPLE NEST. In this situation we pick one at random from the choices in the list. - //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' - map = pick(map) - var/datum/map_template/MT = map_templates[map] - if(!istype(MT)) - error("Randompick Z level \"[map]\" is not a valid map!") - else - admin_notice("Gateway: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - - if(LAZYLEN(also_load)) //Just copied from gateway picking, this is so we can have a kind of OM map version of the same concept. - var/picklist = pick(also_load) - - if(!picklist) //No lateload maps at all - return - - if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission - error("Randompick Z level [picklist] is not a list! Must be in a list!") - return - - for(var/map in picklist) - if(islist(map)) - // TRIPLE NEST. In this situation we pick one at random from the choices in the list. - //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' - map = pick(map) - var/datum/map_template/MT = map_templates[map] - if(!istype(MT)) - error("Randompick Z level \"[map]\" is not a valid map!") - else - admin_notice("OM Adventure: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - - if(LAZYLEN(redgate_load)) - var/picklist = pick(redgate_load) - - if(!picklist) //No lateload maps at all - return - - if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission - error("Randompick Z level [picklist] is not a list! Must be in a list!") - return - - for(var/map in picklist) - if(islist(map)) - // TRIPLE NEST. In this situation we pick one at random from the choices in the list. - //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' - map = pick(map) - var/datum/map_template/MT = map_templates[map] - if(!istype(MT)) - error("Randompick Z level \"[map]\" is not a valid map!") - else - admin_notice("Redgate: [MT]", R_DEBUG) - MT.load_new_z(centered = FALSE) - - -/datum/controller/subsystem/mapping/proc/preloadShelterTemplates() - for(var/datum/map_template/shelter/shelter_type as anything in subtypesof(/datum/map_template/shelter)) - if(!(initial(shelter_type.mappath))) - continue - var/datum/map_template/shelter/S = new shelter_type() - - shelter_templates[S.shelter_id] = S -// VOREStation Edit End: Re-enable this - -/datum/controller/subsystem/mapping/stat_entry(msg) - if (!Debug2) - return // Only show up in stat panel if debugging is enabled. - . = ..() +// Handles map-related tasks, mostly here to ensure it does so after the MC initializes. +SUBSYSTEM_DEF(mapping) + name = "Mapping" + init_order = INIT_ORDER_MAPPING + flags = SS_NO_FIRE + + var/list/map_templates = list() + var/dmm_suite/maploader = null + var/obj/effect/landmark/engine_loader/engine_loader + var/list/shelter_templates = list() + +/datum/controller/subsystem/mapping/Recover() + flags |= SS_NO_INIT // Make extra sure we don't initialize twice. + shelter_templates = SSmapping.shelter_templates + +/datum/controller/subsystem/mapping/Initialize(timeofday) + if(subsystem_initialized) + return + world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably) + maploader = new() + load_map_templates() + + if(config.generate_map) + // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. + using_map.perform_map_generation() + + loadEngine() + preloadShelterTemplates() // VOREStation EDIT: Re-enable Shelter Capsules + // Mining generation probably should be here too + // TODO - Other stuff related to maps and areas could be moved here too. Look at /tg + // Lateload Code related to Expedition areas. + if(using_map) // VOREStation Edit: Re-enable this. + loadLateMaps() + ..() + +/datum/controller/subsystem/mapping/proc/load_map_templates() + for(var/datum/map_template/template as anything in subtypesof(/datum/map_template)) + if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence. + continue + template = new template() + map_templates[template.name] = template + return TRUE + +/datum/controller/subsystem/mapping/proc/loadEngine() + if(!engine_loader) + return // Seems this map doesn't need an engine loaded. + + var/turf/T = get_turf(engine_loader) + if(!isturf(T)) + to_world_log("[log_info_line(engine_loader)] not on a turf! Cannot place engine template.") + return + + // Choose an engine type + var/datum/map_template/engine/chosen_type = null + if (LAZYLEN(config.engine_map)) + var/chosen_name = pick(config.engine_map) + chosen_type = map_templates[chosen_name] + if(!istype(chosen_type)) + error("Configured engine map [chosen_name] is not a valid engine map name!") + if(!istype(chosen_type)) + var/list/engine_types = list() + for(var/map in map_templates) + var/datum/map_template/engine/MT = map_templates[map] + if(istype(MT)) + engine_types += MT + chosen_type = pick(engine_types) + to_world_log("Chose Engine Map: [chosen_type.name]") + admin_notice("Chose Engine Map: [chosen_type.name]", R_DEBUG) + + // Annihilate movable atoms + engine_loader.annihilate_bounds() + //CHECK_TICK //Don't let anything else happen for now + // Actually load it + chosen_type.load(T) + +// VOREStation Edit Start: Enable This +/datum/controller/subsystem/mapping/proc/loadLateMaps() + var/list/deffo_load = using_map.lateload_z_levels + var/list/maybe_load = using_map.lateload_gateway + var/list/also_load = using_map.lateload_overmap + var/list/redgate_load = using_map.lateload_redgate + + for(var/list/maplist in deffo_load) + if(!islist(maplist)) + error("Lateload Z level [maplist] is not a list! Must be in a list!") + continue + for(var/mapname in maplist) + var/datum/map_template/MT = map_templates[mapname] + if(!istype(MT)) + error("Lateload Z level \"[mapname]\" is not a valid map!") + continue + admin_notice("Lateload: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + CHECK_TICK + + if(LAZYLEN(maybe_load)) + var/picklist = pick(maybe_load) + + if(!picklist) //No lateload maps at all + return + + if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission + error("Randompick Z level [picklist] is not a list! Must be in a list!") + return + + for(var/map in picklist) + if(islist(map)) + // TRIPLE NEST. In this situation we pick one at random from the choices in the list. + //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' + map = pick(map) + var/datum/map_template/MT = map_templates[map] + if(!istype(MT)) + error("Randompick Z level \"[map]\" is not a valid map!") + else + admin_notice("Gateway: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + + if(LAZYLEN(also_load)) //Just copied from gateway picking, this is so we can have a kind of OM map version of the same concept. + var/picklist = pick(also_load) + + if(!picklist) //No lateload maps at all + return + + if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission + error("Randompick Z level [picklist] is not a list! Must be in a list!") + return + + for(var/map in picklist) + if(islist(map)) + // TRIPLE NEST. In this situation we pick one at random from the choices in the list. + //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' + map = pick(map) + var/datum/map_template/MT = map_templates[map] + if(!istype(MT)) + error("Randompick Z level \"[map]\" is not a valid map!") + else + admin_notice("OM Adventure: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + + if(LAZYLEN(redgate_load)) + var/picklist = pick(redgate_load) + + if(!picklist) //No lateload maps at all + return + + if(!islist(picklist)) //So you can have a 'chain' of z-levels that make up one away mission + error("Randompick Z level [picklist] is not a list! Must be in a list!") + return + + for(var/map in picklist) + if(islist(map)) + // TRIPLE NEST. In this situation we pick one at random from the choices in the list. + //This allows a sort of a1,a2,a3,b1,b2,b3,c1,c2,c3 setup where it picks one 'a', one 'b', one 'c' + map = pick(map) + var/datum/map_template/MT = map_templates[map] + if(!istype(MT)) + error("Randompick Z level \"[map]\" is not a valid map!") + else + admin_notice("Redgate: [MT]", R_DEBUG) + MT.load_new_z(centered = FALSE) + + +/datum/controller/subsystem/mapping/proc/preloadShelterTemplates() + for(var/datum/map_template/shelter/shelter_type as anything in subtypesof(/datum/map_template/shelter)) + if(!(initial(shelter_type.mappath))) + continue + var/datum/map_template/shelter/S = new shelter_type() + + shelter_templates[S.shelter_id] = S +// VOREStation Edit End: Re-enable this + +/datum/controller/subsystem/mapping/stat_entry(msg) + if (!Debug2) + return // Only show up in stat panel if debugging is enabled. + . = ..() diff --git a/code/controllers/subsystems/ping.dm b/code/controllers/subsystems/ping.dm new file mode 100644 index 00000000000..7b41519737f --- /dev/null +++ b/code/controllers/subsystems/ping.dm @@ -0,0 +1,44 @@ +/*! + * Copyright (c) 2022 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +SUBSYSTEM_DEF(ping) + name = "Ping" + priority = FIRE_PRIORITY_PING + // init_stage = INITSTAGE_EARLY + wait = 4 SECONDS + flags = SS_NO_INIT + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + var/list/currentrun = list() + +/datum/controller/subsystem/ping/stat_entry() + ..("P:[GLOB.clients.len]") + +/datum/controller/subsystem/ping/fire(resumed = FALSE) + // Prepare the new batch of clients + if (!resumed) + src.currentrun = GLOB.clients.Copy() + + // De-reference the list for sanic speeds + var/list/currentrun = src.currentrun + + while (currentrun.len) + var/client/client = currentrun[currentrun.len] + currentrun.len-- + + if(!client.is_preference_enabled(/datum/client_preference/vchat_enable)) + winset(client, "output", "on-show=&is-disabled=0&is-visible=1") + winset(client, "browseroutput", "is-disabled=1;is-visible=0") + client.tgui_panel.oldchat = TRUE + + if (client?.tgui_panel?.is_ready()) + // Send a soft ping + client.tgui_panel.window.send_message("ping/soft", list( + // Slightly less than the subsystem timer (somewhat arbitrary) + // to prevent incoming pings from resetting the afk state + "afk" = client.is_afk(3.5 SECONDS), + )) + + if (MC_TICK_CHECK) + return diff --git a/code/controllers/subsystems/sqlite.dm b/code/controllers/subsystems/sqlite.dm index 3d18ec4a7af..20a5a594759 100644 --- a/code/controllers/subsystems/sqlite.dm +++ b/code/controllers/subsystems/sqlite.dm @@ -1,187 +1,187 @@ -// This holds all the code needed to manage and use a SQLite database. -// It is merely a file sitting inside the data directory, as opposed to a full fledged DB service, -// however this makes it a lot easier to test, and it is natively supported by BYOND. -SUBSYSTEM_DEF(sqlite) - name = "SQLite" - init_order = INIT_ORDER_SQLITE - flags = SS_NO_FIRE - var/database/sqlite_db = null - -/datum/controller/subsystem/sqlite/Initialize(timeofday) - connect() - if(sqlite_db) - init_schema(sqlite_db) - return ..() - -/datum/controller/subsystem/sqlite/proc/connect() - if(!config.sqlite_enabled) - return - - if(!sqlite_db) - sqlite_db = new("data/sqlite/sqlite.db") // The path has to be hardcoded or BYOND silently fails. - - - if(!sqlite_db) - to_world_log("Failed to load or create a SQLite database.") - log_debug("ERROR: SQLite database is active in config but failed to load.") - else - to_world_log("Sqlite database connected.") - -// Makes the tables, if they do not already exist in the sqlite file. -/datum/controller/subsystem/sqlite/proc/init_schema(database/sqlite_object) - // Feedback table. - // Note that this is for direct feedback from players using the in-game feedback system and NOT for stat tracking. - // Player ckeys are not stored in this table as a unique key due to a config option to hash the keys to encourage more honest feedback. - /* - * id - Primary unique key to ID a specific piece of feedback. - NOT used to id people submitting feedback. - * author - The person who submitted it. Will be the ckey, or a hash of the ckey, - if both the config supports it, and the user wants it. - * topic - A specific category to organize feedback under. Options are defined in the config file. - * content - What the author decided to write to the staff. Limited to MAX_FEEDBACK_LENGTH. - * datetime - When the author submitted their feedback, acts as a timestamp. - */ - var/database/query/init_schema = new( - {" - CREATE TABLE IF NOT EXISTS [SQLITE_TABLE_FEEDBACK] - ( - `[SQLITE_FEEDBACK_COLUMN_ID]` INTEGER NOT NULL UNIQUE, - `[SQLITE_FEEDBACK_COLUMN_AUTHOR]` TEXT NOT NULL, - `[SQLITE_FEEDBACK_COLUMN_TOPIC]` TEXT NOT NULL, - `[SQLITE_FEEDBACK_COLUMN_CONTENT]` TEXT NOT NULL, - `[SQLITE_FEEDBACK_COLUMN_DATETIME]` TEXT NOT NULL, - PRIMARY KEY(`[SQLITE_FEEDBACK_COLUMN_ID]`) - ); - "} - ) - init_schema.Execute(sqlite_object) - sqlite_check_for_errors(init_schema, "Feedback table creation") - - // Add more schemas below this if the SQLite DB gets expanded for things like persistant news, polls, bans, deaths, etc. - -// General error checking for SQLite. -// Returns true if something went wrong. Also writes a log. -// The desc parameter should be unique for each call, to make it easier to track down where the error occured. -/datum/controller/subsystem/sqlite/proc/sqlite_check_for_errors(var/database/query/query_used, var/desc) - if(query_used && query_used.ErrorMsg()) - log_debug("SQLite Error: [desc] : [query_used.ErrorMsg()]") - return TRUE - return FALSE - - -/************ - * Feedback * - ************/ - -// Inserts data into the feedback table in a painless manner. -// Returns TRUE if no issues happened, FALSE otherwise. -/datum/controller/subsystem/sqlite/proc/insert_feedback(author, topic, content, database/sqlite_object) - if(!author || !topic || !content) - CRASH("One or more parameters was invalid.") - - // Sanitize everything to avoid sneaky stuff. - var/sqlite_author = sql_sanitize_text(ckey(lowertext(author))) - var/sqlite_content = sql_sanitize_text(content) - var/sqlite_topic = sql_sanitize_text(topic) - - var/database/query/query = new( - "INSERT INTO [SQLITE_TABLE_FEEDBACK] (\ - [SQLITE_FEEDBACK_COLUMN_AUTHOR], \ - [SQLITE_FEEDBACK_COLUMN_TOPIC], \ - [SQLITE_FEEDBACK_COLUMN_CONTENT], \ - [SQLITE_FEEDBACK_COLUMN_DATETIME]) \ - \ - VALUES (\ - ?,\ - ?,\ - ?,\ - datetime('now'))", - sqlite_author, - sqlite_topic, - sqlite_content - ) - query.Execute(sqlite_object) - return !sqlite_check_for_errors(query, "Insert Feedback") - -/datum/controller/subsystem/sqlite/proc/can_submit_feedback(client/C) - if(!config.sqlite_enabled) - return FALSE - if(config.sqlite_feedback_min_age && !is_old_enough(C)) - return FALSE - if(config.sqlite_feedback_cooldown > 0 && get_feedback_cooldown(C.key, config.sqlite_feedback_cooldown, sqlite_db) > 0) - return FALSE - return TRUE - -// Returns TRUE if the player is 'old' enough, according to the config. -/datum/controller/subsystem/sqlite/proc/is_old_enough(client/C) - if(get_player_age(C.key) < config.sqlite_feedback_min_age) - return FALSE - return TRUE - - -// Returns how many days someone has to wait, to submit more feedback, or 0 if they can do so right now. -/datum/controller/subsystem/sqlite/proc/get_feedback_cooldown(player_ckey, cooldown, database/sqlite_object) - player_ckey = sql_sanitize_text(ckey(lowertext(player_ckey))) - var/potential_hashed_ckey = sql_sanitize_text(md5(player_ckey + SSsqlite.get_feedback_pepper())) - - // First query is to get the most recent time the player has submitted feedback. - var/database/query/query = new({" - SELECT [SQLITE_FEEDBACK_COLUMN_DATETIME] - FROM [SQLITE_TABLE_FEEDBACK] - WHERE [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? OR [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? - ORDER BY [SQLITE_FEEDBACK_COLUMN_DATETIME] - DESC LIMIT 1; - "}, - player_ckey, - potential_hashed_ckey - ) - query.Execute(sqlite_object) - sqlite_check_for_errors(query, "Rate Limited Check 1") - - // It is possible this is their first time, so there won't be a next row. - if(query.NextRow()) // If this is true, the user has submitted feedback at least once. - var/list/row_data = query.GetRowData() - var/last_submission_datetime = row_data[SQLITE_FEEDBACK_COLUMN_DATETIME] - - // Now we have the datetime, we need to do something to compare it. - // Second query is to calculate the difference between two datetimes. - // This is done on the SQLite side because parsing datetimes with BYOND is probably a bad idea. - query = new( - "SELECT julianday('now') - julianday(?) \ - AS 'datediff';", - last_submission_datetime - ) - query.Execute(sqlite_object) - sqlite_check_for_errors(query, "Rate Limited Check 2") - - query.NextRow() - row_data = query.GetRowData() - var/date_diff = row_data["datediff"] - - // Now check if it's too soon to give more feedback. - if(text2num(date_diff) < cooldown) // Too soon. - return round(cooldown - date_diff, 0.1) - return 0.0 - - -// A Pepper is like a Salt but only one exists and is supposed to be outside of a database. -// If the file is properly protected, it can only be viewed/copied by sys-admins generating a log, which is much more conspicious than accessing/copying a DB. -// This stops mods/admins/etc from guessing the author by shoving names in an MD5 hasher until they pick the right one. -// Don't use this for things needing actual security. -/datum/controller/subsystem/sqlite/proc/get_feedback_pepper() - var/pepper_file = file2list("config/sqlite_feedback_pepper.txt") - var/pepper = null - for(var/line in pepper_file) - if(!line) - continue - if(length(line) == 0) - continue - else if(copytext(line, 1, 2) == "#") - continue - else - pepper = line - break - return pepper - -/datum/controller/subsystem/sqlite/CanProcCall(procname) - return procname != "get_feedback_pepper" +// This holds all the code needed to manage and use a SQLite database. +// It is merely a file sitting inside the data directory, as opposed to a full fledged DB service, +// however this makes it a lot easier to test, and it is natively supported by BYOND. +SUBSYSTEM_DEF(sqlite) + name = "SQLite" + init_order = INIT_ORDER_SQLITE + flags = SS_NO_FIRE + var/database/sqlite_db = null + +/datum/controller/subsystem/sqlite/Initialize(timeofday) + connect() + if(sqlite_db) + init_schema(sqlite_db) + return ..() + +/datum/controller/subsystem/sqlite/proc/connect() + if(!config.sqlite_enabled) + return + + if(!sqlite_db) + sqlite_db = new("data/sqlite/sqlite.db") // The path has to be hardcoded or BYOND silently fails. + + + if(!sqlite_db) + to_world_log("Failed to load or create a SQLite database.") + log_debug("ERROR: SQLite database is active in config but failed to load.") + else + to_world_log("Sqlite database connected.") + +// Makes the tables, if they do not already exist in the sqlite file. +/datum/controller/subsystem/sqlite/proc/init_schema(database/sqlite_object) + // Feedback table. + // Note that this is for direct feedback from players using the in-game feedback system and NOT for stat tracking. + // Player ckeys are not stored in this table as a unique key due to a config option to hash the keys to encourage more honest feedback. + /* + * id - Primary unique key to ID a specific piece of feedback. + NOT used to id people submitting feedback. + * author - The person who submitted it. Will be the ckey, or a hash of the ckey, + if both the config supports it, and the user wants it. + * topic - A specific category to organize feedback under. Options are defined in the config file. + * content - What the author decided to write to the staff. Limited to MAX_FEEDBACK_LENGTH. + * datetime - When the author submitted their feedback, acts as a timestamp. + */ + var/database/query/init_schema = new( + {" + CREATE TABLE IF NOT EXISTS [SQLITE_TABLE_FEEDBACK] + ( + `[SQLITE_FEEDBACK_COLUMN_ID]` INTEGER NOT NULL UNIQUE, + `[SQLITE_FEEDBACK_COLUMN_AUTHOR]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_TOPIC]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_CONTENT]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_DATETIME]` TEXT NOT NULL, + PRIMARY KEY(`[SQLITE_FEEDBACK_COLUMN_ID]`) + ); + "} + ) + init_schema.Execute(sqlite_object) + sqlite_check_for_errors(init_schema, "Feedback table creation") + + // Add more schemas below this if the SQLite DB gets expanded for things like persistant news, polls, bans, deaths, etc. + +// General error checking for SQLite. +// Returns true if something went wrong. Also writes a log. +// The desc parameter should be unique for each call, to make it easier to track down where the error occured. +/datum/controller/subsystem/sqlite/proc/sqlite_check_for_errors(var/database/query/query_used, var/desc) + if(query_used && query_used.ErrorMsg()) + log_debug("SQLite Error: [desc] : [query_used.ErrorMsg()]") + return TRUE + return FALSE + + +/************ + * Feedback * + ************/ + +// Inserts data into the feedback table in a painless manner. +// Returns TRUE if no issues happened, FALSE otherwise. +/datum/controller/subsystem/sqlite/proc/insert_feedback(author, topic, content, database/sqlite_object) + if(!author || !topic || !content) + CRASH("One or more parameters was invalid.") + + // Sanitize everything to avoid sneaky stuff. + var/sqlite_author = sql_sanitize_text(ckey(lowertext(author))) + var/sqlite_content = sql_sanitize_text(content) + var/sqlite_topic = sql_sanitize_text(topic) + + var/database/query/query = new( + "INSERT INTO [SQLITE_TABLE_FEEDBACK] (\ + [SQLITE_FEEDBACK_COLUMN_AUTHOR], \ + [SQLITE_FEEDBACK_COLUMN_TOPIC], \ + [SQLITE_FEEDBACK_COLUMN_CONTENT], \ + [SQLITE_FEEDBACK_COLUMN_DATETIME]) \ + \ + VALUES (\ + ?,\ + ?,\ + ?,\ + datetime('now'))", + sqlite_author, + sqlite_topic, + sqlite_content + ) + query.Execute(sqlite_object) + return !sqlite_check_for_errors(query, "Insert Feedback") + +/datum/controller/subsystem/sqlite/proc/can_submit_feedback(client/C) + if(!config.sqlite_enabled) + return FALSE + if(config.sqlite_feedback_min_age && !is_old_enough(C)) + return FALSE + if(config.sqlite_feedback_cooldown > 0 && get_feedback_cooldown(C.key, config.sqlite_feedback_cooldown, sqlite_db) > 0) + return FALSE + return TRUE + +// Returns TRUE if the player is 'old' enough, according to the config. +/datum/controller/subsystem/sqlite/proc/is_old_enough(client/C) + if(get_player_age(C.key) < config.sqlite_feedback_min_age) + return FALSE + return TRUE + + +// Returns how many days someone has to wait, to submit more feedback, or 0 if they can do so right now. +/datum/controller/subsystem/sqlite/proc/get_feedback_cooldown(player_ckey, cooldown, database/sqlite_object) + player_ckey = sql_sanitize_text(ckey(lowertext(player_ckey))) + var/potential_hashed_ckey = sql_sanitize_text(md5(player_ckey + SSsqlite.get_feedback_pepper())) + + // First query is to get the most recent time the player has submitted feedback. + var/database/query/query = new({" + SELECT [SQLITE_FEEDBACK_COLUMN_DATETIME] + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? OR [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_DATETIME] + DESC LIMIT 1; + "}, + player_ckey, + potential_hashed_ckey + ) + query.Execute(sqlite_object) + sqlite_check_for_errors(query, "Rate Limited Check 1") + + // It is possible this is their first time, so there won't be a next row. + if(query.NextRow()) // If this is true, the user has submitted feedback at least once. + var/list/row_data = query.GetRowData() + var/last_submission_datetime = row_data[SQLITE_FEEDBACK_COLUMN_DATETIME] + + // Now we have the datetime, we need to do something to compare it. + // Second query is to calculate the difference between two datetimes. + // This is done on the SQLite side because parsing datetimes with BYOND is probably a bad idea. + query = new( + "SELECT julianday('now') - julianday(?) \ + AS 'datediff';", + last_submission_datetime + ) + query.Execute(sqlite_object) + sqlite_check_for_errors(query, "Rate Limited Check 2") + + query.NextRow() + row_data = query.GetRowData() + var/date_diff = row_data["datediff"] + + // Now check if it's too soon to give more feedback. + if(text2num(date_diff) < cooldown) // Too soon. + return round(cooldown - date_diff, 0.1) + return 0.0 + + +// A Pepper is like a Salt but only one exists and is supposed to be outside of a database. +// If the file is properly protected, it can only be viewed/copied by sys-admins generating a log, which is much more conspicious than accessing/copying a DB. +// This stops mods/admins/etc from guessing the author by shoving names in an MD5 hasher until they pick the right one. +// Don't use this for things needing actual security. +/datum/controller/subsystem/sqlite/proc/get_feedback_pepper() + var/pepper_file = file2list("config/sqlite_feedback_pepper.txt") + var/pepper = null + for(var/line in pepper_file) + if(!line) + continue + if(length(line) == 0) + continue + else if(copytext(line, 1, 2) == "#") + continue + else + pepper = line + break + return pepper + +/datum/controller/subsystem/sqlite/CanProcCall(procname) + return procname != "get_feedback_pepper" diff --git a/code/controllers/subsystems/tgui.dm b/code/controllers/subsystems/tgui.dm index eb0d566f4be..46b3ae90c2e 100644 --- a/code/controllers/subsystems/tgui.dm +++ b/code/controllers/subsystems/tgui.dm @@ -1,12 +1,17 @@ - /** - * tgui subsystem - * - * Contains all tgui state and subsystem code. - **/ +/*! + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ +/** + * tgui subsystem + * + * Contains all tgui state and subsystem code. + * + */ SUBSYSTEM_DEF(tgui) - name = "TGUI" + name = "tgui" wait = 9 flags = SS_NO_INIT priority = FIRE_PRIORITY_TGUI @@ -14,10 +19,8 @@ SUBSYSTEM_DEF(tgui) /// A list of UIs scheduled to process var/list/current_run = list() - /// A list of open UIs - var/list/open_uis = list() - /// A list of open UIs, grouped by src_object and ui_key. - var/list/open_uis_by_src = list() + /// A list of all open UIs + var/list/all_uis = list() /// The HTML base used for all UIs. var/basehtml @@ -33,21 +36,21 @@ SUBSYSTEM_DEF(tgui) close_all_uis() /datum/controller/subsystem/tgui/stat_entry() - ..("P:[open_uis.len]") + ..("P:[all_uis.len]") -/datum/controller/subsystem/tgui/fire(resumed = 0) +/datum/controller/subsystem/tgui/fire(resumed = FALSE) if(!resumed) - src.current_run = open_uis.Copy() + src.current_run = all_uis.Copy() // Cache for sanic speed (lists are references anyways) var/list/current_run = src.current_run while(current_run.len) var/datum/tgui/ui = current_run[current_run.len] current_run.len-- // TODO: Move user/src_object check to process() - if(ui && ui.user && ui.src_object) - ui.process() + if(ui?.user && ui.src_object) + ui.process(wait * 0.1) else - open_uis.Remove(ui) + ui.close(0) if(MC_TICK_CHECK) return @@ -84,6 +87,8 @@ SUBSYSTEM_DEF(tgui) window_found = TRUE break if(!window_found) + log_tgui(user, "Error: Pool exhausted", + context = "SStgui/request_pooled_window") return null return window @@ -95,6 +100,7 @@ SUBSYSTEM_DEF(tgui) * required user mob */ /datum/controller/subsystem/tgui/proc/force_close_all_windows(mob/user) + log_tgui(user, context = "SStgui/force_close_all_windows") if(user.client) user.client.tgui_windows = list() for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT) @@ -110,6 +116,7 @@ SUBSYSTEM_DEF(tgui) * required window_id string */ /datum/controller/subsystem/tgui/proc/force_close_window(mob/user, window_id) + log_tgui(user, context = "SStgui/force_close_window") // Close all tgui datums based on window_id. for(var/datum/tgui/ui in user.tgui_open_uis) if(ui.window && ui.window.id == window_id) @@ -119,22 +126,23 @@ SUBSYSTEM_DEF(tgui) // Close window directly just to be sure. user << browse(null, "window=[window_id]") - /** - * public - * - * Get a open UI given a user, src_object, and ui_key and try to update it with data. - * - * required user mob The mob who opened/is using the UI. - * required src_object datum The object/datum which owns the UI. - * required ui_key string The ui_key of the UI. - * - * return datum/tgui The found UI. - **/ +/** + * public + * + * Try to find an instance of a UI, and push an update to it. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object/datum which owns the UI. + * optional ui datum/tgui The UI to be updated, if it exists. + * optional force_open bool If the UI should be re-opened instead of updated. + * + * return datum/tgui The found UI. + */ /datum/controller/subsystem/tgui/proc/try_update_ui( mob/user, datum/src_object, datum/tgui/ui) - // Look up a UI if it wasn't passed. + // Look up a UI if it wasn't passed if(isnull(ui)) ui = get_open_ui(user, src_object) // Couldn't find a UI. @@ -150,116 +158,114 @@ SUBSYSTEM_DEF(tgui) ui.send_update() return ui - /** - * private - * - * Get a open UI given a user, src_object, and ui_key. - * - * required user mob The mob who opened/is using the UI. - * required src_object datum The object/datum which owns the UI. - * required ui_key string The ui_key of the UI. - * - * return datum/tgui The found UI. - **/ +/** + * public + * + * Get a open UI given a user and src_object. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object/datum which owns the UI. + * + * return datum/tgui The found UI. + */ /datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object) - var/key = "[REF(src_object)]" // No UIs opened for this src_object - if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) - return null // No UIs open. - for(var/datum/tgui/ui in open_uis_by_src[key]) // Find UIs for this object. + if(!LAZYLEN(src_object?.open_uis)) + return null + for(var/datum/tgui/ui in src_object.open_uis) // Make sure we have the right user if(ui.user == user) return ui - return null // Couldn't find a UI! + return null - /** - * private - * - * Update all UIs attached to src_object. - * - * required src_object datum The object/datum which owns the UIs. - * - * return int The number of UIs updated. - **/ +/** + * public + * + * Update all UIs attached to src_object. + * + * required src_object datum The object/datum which owns the UIs. + * + * return int The number of UIs updated. + */ /datum/controller/subsystem/tgui/proc/update_uis(datum/src_object) + // No UIs opened for this src_object + if(!LAZYLEN(src_object?.open_uis)) + return 0 var/count = 0 - var/key = "[REF(src_object)]" - if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) - return count // Couldn't find any UIs for this object. - for(var/datum/tgui/ui in open_uis_by_src[key]) - // Check the UI is valid. - if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) - ui.process(force = 1) // Update the UI. - count++ // Count each UI we update. + for(var/datum/tgui/ui in src_object.open_uis) + // Check if UI is valid. + if(ui?.src_object && ui.user && ui.src_object.tgui_host(ui.user)) + INVOKE_ASYNC(ui, TYPE_PROC_REF(/datum/tgui, process), wait * 0.1, TRUE) + count++ return count - /** - * private - * - * Close all UIs attached to src_object. - * - * required src_object datum The object/datum which owns the UIs. - * - * return int The number of UIs closed. - **/ +/** + * public + * + * Close all UIs attached to src_object. + * + * required src_object datum The object/datum which owns the UIs. + * + * return int The number of UIs closed. + */ /datum/controller/subsystem/tgui/proc/close_uis(datum/src_object) - var/count = 0 - var/key = "[REF(src_object)]" // No UIs opened for this src_object - if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) - return count - for(var/datum/tgui/ui in open_uis_by_src[key]) - if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid. - ui.close() // Close the UI. - count++ // Count each UI we close. + if(!LAZYLEN(src_object?.open_uis)) + return 0 + var/count = 0 + for(var/datum/tgui/ui in src_object.open_uis) + // Check if UI is valid. + if(ui?.src_object && ui.user && ui.src_object.tgui_host(ui.user)) + ui.close() + count++ return count - /** - * private - * - * Close all UIs regardless of their attachment to src_object. - * - * return int The number of UIs closed. - **/ +/** + * public + * + * Close all UIs regardless of their attachment to src_object. + * + * return int The number of UIs closed. + */ /datum/controller/subsystem/tgui/proc/close_all_uis() var/count = 0 - for(var/key in open_uis_by_src) - for(var/datum/tgui/ui in open_uis_by_src[key]) - if(ui && ui.src_object && ui.user && ui.src_object.tgui_host(ui.user)) // Check the UI is valid. - ui.close() // Close the UI. - count++ // Count each UI we close. + for(var/datum/tgui/ui in all_uis) + // Check if UI is valid. + if(ui?.src_object && ui.user && ui.src_object.tgui_host(ui.user)) + ui.close() + count++ return count - /** - * private - * - * Update all UIs belonging to a user. - * - * required user mob The mob who opened/is using the UI. - * optional src_object datum If provided, only update UIs belonging this src_object. - * - * return int The number of UIs updated. - **/ +/** + * public + * + * Update all UIs belonging to a user. + * + * required user mob The mob who opened/is using the UI. + * optional src_object datum If provided, only update UIs belonging this src_object. + * + * return int The number of UIs updated. + */ /datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object) var/count = 0 if(length(user?.tgui_open_uis) == 0) return count for(var/datum/tgui/ui in user.tgui_open_uis) if(isnull(src_object) || ui.src_object == src_object) - ui.process(force = 1) + ui.process(wait * 0.1, force = 1) count++ return count - /** - * private - * - * Close all UIs belonging to a user. - * - * required user mob The mob who opened/is using the UI. - * optional src_object datum If provided, only close UIs belonging this src_object. - * - * return int The number of UIs closed. - **/ +/** + * public + * + * Close all UIs belonging to a user. + * + * required user mob The mob who opened/is using the UI. + * optional src_object datum If provided, only close UIs belonging this src_object. + * + * return int The number of UIs closed. + */ /datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object, logout = FALSE) var/count = 0 if(length(user?.tgui_open_uis) == 0) @@ -270,68 +276,60 @@ SUBSYSTEM_DEF(tgui) count++ return count - /** - * private - * - * Add a UI to the list of open UIs. - * - * required ui datum/tgui The UI to be added. - **/ +/** + * private + * + * Add a UI to the list of open UIs. + * + * required ui datum/tgui The UI to be added. + */ /datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui) - var/key = "[REF(ui.src_object)]" - if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) - open_uis_by_src[key] = list() - ui.user.tgui_open_uis |= ui - var/list/uis = open_uis_by_src[key] - uis |= ui - open_uis |= ui + ui.user?.tgui_open_uis |= ui + LAZYOR(ui.src_object.open_uis, ui) + all_uis |= ui - /** - * private - * - * Remove a UI from the list of open UIs. - * - * required ui datum/tgui The UI to be removed. - * - * return bool If the UI was removed or not. - **/ +/** + * private + * + * Remove a UI from the list of open UIs. + * + * required ui datum/tgui The UI to be removed. + * + * return bool If the UI was removed or not. + */ /datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui) - var/key = "[REF(ui.src_object)]" - if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) - return FALSE // Remove it from the list of processing UIs. - open_uis.Remove(ui) + all_uis -= ui + current_run -= ui // If the user exists, remove it from them too. if(ui.user) - ui.user.tgui_open_uis.Remove(ui) - var/list/uis = open_uis_by_src[key] - uis.Remove(ui) - if(length(uis) == 0) - open_uis_by_src.Remove(key) + ui.user.tgui_open_uis -= ui + if(ui.src_object) + LAZYREMOVE(ui.src_object.open_uis, ui) return TRUE - /** - * private - * - * Handle client logout, by closing all their UIs. - * - * required user mob The mob which logged out. - * - * return int The number of UIs closed. - **/ +/** + * private + * + * Handle client logout, by closing all their UIs. + * + * required user mob The mob which logged out. + * + * return int The number of UIs closed. + */ /datum/controller/subsystem/tgui/proc/on_logout(mob/user) - return close_user_uis(user, logout = TRUE) + close_user_uis(user, logout = TRUE) - /** - * private - * - * Handle clients switching mobs, by transferring their UIs. - * - * required user source The client's original mob. - * required user target The client's new mob. - * - * return bool If the UIs were transferred. - **/ +/** + * private + * + * Handle clients switching mobs, by transferring their UIs. + * + * required user source The client's original mob. + * required user target The client's new mob. + * + * return bool If the UIs were transferred. + */ /datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target) // The old mob had no open UIs. if(length(source?.tgui_open_uis) == 0) @@ -342,7 +340,7 @@ SUBSYSTEM_DEF(tgui) for(var/datum/tgui/ui in source.tgui_open_uis) // Inform the UIs of their new owner. ui.user = target - target.tgui_open_uis.Add(ui) + target.tgui_open_uis += ui // Clear the old list. source.tgui_open_uis.Cut() return TRUE diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 7bc6a13a140..3242914ecad 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -462,22 +462,22 @@ var/global/datum/controller/subsystem/ticker/ticker var/turf/playerTurf = get_turf(Player) if(emergency_shuttle.departed && emergency_shuttle.evac) if(isNotAdminLevel(playerTurf.z)) - to_chat(Player, "You survived the round, but remained on [station_name()] as [Player.real_name].") + to_chat(Player, "[span_blue("You survived the round, but remained on [station_name()] as [Player.real_name].")]") else - to_chat(Player, "You managed to survive the events on [station_name()] as [Player.real_name].") + to_chat(Player, "[span_green("You managed to survive the events on [station_name()] as [Player.real_name].")]") else if(isAdminLevel(playerTurf.z)) - to_chat(Player, "You successfully underwent crew transfer after events on [station_name()] as [Player.real_name].") + to_chat(Player, "[span_green("You successfully underwent crew transfer after events on [station_name()] as [Player.real_name].")]") else if(issilicon(Player)) - to_chat(Player, "You remain operational after the events on [station_name()] as [Player.real_name].") + to_chat(Player, "[span_green("You remain operational after the events on [station_name()] as [Player.real_name].")]") else - to_chat(Player, "You missed the crew transfer after the events on [station_name()] as [Player.real_name].") + to_chat(Player, "[span_blue("You missed the crew transfer after the events on [station_name()] as [Player.real_name].")]") else if(istype(Player,/mob/observer/dead)) var/mob/observer/dead/O = Player if(!O.started_as_observer) - to_chat(Player, "You did not survive the events on [station_name()]...") + to_chat(Player, "[span_red("You did not survive the events on [station_name()]...")]") else - to_chat(Player, "You did not survive the events on [station_name()]...") + to_chat(Player, "[span_red("You did not survive the events on [station_name()]...")]") to_world("
    ") for (var/mob/living/silicon/ai/aiPlayer in mob_list) diff --git a/code/controllers/subsystems/timer.dm b/code/controllers/subsystems/timer.dm index cfcba6afbf6..701307c6276 100644 --- a/code/controllers/subsystems/timer.dm +++ b/code/controllers/subsystems/timer.dm @@ -512,6 +512,26 @@ SUBSYSTEM_DEF(timer) return TRUE return FALSE +/** + * Get the remaining deciseconds on a timer + * + * Arguments: + * * id a timerid or a /datum/timedevent + */ +/proc/timeleft(id, datum/controller/subsystem/timer/timer_subsystem) + if (!id) + return null + if (id == TIMER_ID_NULL) + CRASH("Tried to get timeleft of a null timerid. Use TIMER_STOPPABLE flag") + if (istype(id, /datum/timedevent)) + var/datum/timedevent/timer = id + return timer.timeToRun - world.time + timer_subsystem = timer_subsystem || SStimer + //id is string + var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id] + if(!timer || timer.spent) + return null + return timer.timeToRun - (timer.flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time) #undef BUCKET_LEN #undef BUCKET_POS diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm index fd74644c0d1..9738e55b52c 100644 --- a/code/controllers/subsystems/vote.dm +++ b/code/controllers/subsystems/vote.dm @@ -1,398 +1,398 @@ -SUBSYSTEM_DEF(vote) - name = "Vote" - wait = 10 - priority = FIRE_PRIORITY_VOTE - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - flags = SS_KEEP_TIMING | SS_NO_INIT - var/list/round_voters = list() - - //Current vote - var/initiator - var/started_time - var/time_remaining - var/duration - var/mode - var/question - var/gamemode_vote_called = TRUE // Have we had a gamemode vote this round? Only auto-call if we haven't // ARFS Edit - Stop Gamemode vote - var/list/choices = list() - var/list/gamemode_names = list() - var/list/voted = list() - var/list/current_votes = list() - var/list/additional_text = list() - -/datum/controller/subsystem/vote/fire(resumed) - if(mode) - time_remaining = round((started_time + duration - world.time)/10) - if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP) - to_chat(world, "Gamemode vote aborted: Game has already started.") - reset() - return - if(time_remaining <= 0) - result() - reset() - -/datum/controller/subsystem/vote/proc/autotransfer() - // Before doing the vote, see if anyone is playing. - // If not, just do the transfer. - var/players_are_in_round = FALSE - for(var/mob/living/L as anything in player_list) // Mobs with clients attached. - if(!istype(L)) // Exclude ghosts and other weird things. - continue - if(L.stat == DEAD) // Dead mobs aren't playing. - continue - // Everything else is, however. - players_are_in_round = TRUE - break - - if(!players_are_in_round) - log_debug("The crew transfer shuttle was automatically called at vote time due to no players being present.") - init_shift_change(null, 1) - return - - initiate_vote(VOTE_CREW_TRANSFER, "the server", 1) - log_debug("The server has called a crew transfer vote.") - -/datum/controller/subsystem/vote/proc/autogamemode() - initiate_vote(VOTE_GAMEMODE, "the server", 1) - log_debug("The server has called a gamemode vote.") - -/datum/controller/subsystem/vote/proc/reset() - initiator = null - started_time = null - duration = null - time_remaining = null - mode = null - question = null - choices.Cut() - voted.Cut() - current_votes.Cut() - additional_text.Cut() - -/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes - var/greatest_votes = 0 - var/total_votes = 0 - - for(var/option in choices) - var/votes = choices[option] - total_votes += votes - if(votes > greatest_votes) - greatest_votes = votes - - if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote - var/non_voters = (GLOB.clients.len - total_votes) - if(non_voters > 0) - if(mode == VOTE_RESTART) - choices["Continue Playing"] += non_voters - if(choices["Continue Playing"] >= greatest_votes) - greatest_votes = choices["Continue Playing"] - else if(mode == VOTE_GAMEMODE) - if(master_mode in choices) - choices[master_mode] += non_voters - if(choices[master_mode] >= greatest_votes) - greatest_votes = choices[master_mode] - else if(mode == VOTE_CREW_TRANSFER) - var/factor = 0.5 - switch(world.time / (10 * 60)) // minutes - if(0 to 60) - factor = 0.5 - if(61 to 120) - factor = 0.8 - if(121 to 240) - factor = 1 - if(241 to 300) - factor = 1.2 - else - factor = 1.4 - choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) - to_world("Crew Transfer Factor: [factor]") - greatest_votes = max(choices["Initiate Crew Transfer"], choices["Extend the Shift"]) //VOREStation Edit - - . = list() // Get all options with that many votes and return them in a list - if(greatest_votes) - for(var/option in choices) - if(choices[option] == greatest_votes) - . += option - -/datum/controller/subsystem/vote/proc/announce_result() - var/list/winners = get_result() - var/text - if(winners.len > 0) - if(winners.len > 1) - if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes - text = "Vote Tied Between:\n" - for(var/option in winners) - text += "\t[option]\n" - . = pick(winners) - - for(var/key in current_votes) - if(choices[current_votes[key]] == .) - round_voters += key // Keep track of who voted for the winning round. - if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes - text += "Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]" - else - text += "The vote has ended." - - else - text += "Vote Result: Inconclusive - No Votes!" - if(mode == VOTE_ADD_ANTAGONIST) - antag_add_failed = 1 - log_vote(text) - to_chat(world, "[text]") - -/datum/controller/subsystem/vote/proc/result() - . = announce_result() - var/restart = 0 - if(.) - switch(mode) - if(VOTE_RESTART) - if(. == "Restart Round") - restart = 1 - if(VOTE_GAMEMODE) - if(master_mode != .) - world.save_mode(.) - if(ticker && ticker.mode) - restart = 1 - else - master_mode = . - if(VOTE_CREW_TRANSFER) - if(. == "Initiate Crew Transfer") - init_shift_change(null, 1) - if(VOTE_ADD_ANTAGONIST) - if(isnull(.) || . == "None") - antag_add_failed = 1 - else - additional_antag_types |= antag_names_to_ids[.] - - if(mode == VOTE_GAMEMODE) //fire this even if the vote fails. - if(!round_progressing) - round_progressing = 1 - to_world("The round will start soon.") - - if(restart) - to_world("World restarting due to vote...") - feedback_set_details("end_error", "restart vote") - if(blackbox) - blackbox.save_all_data_to_sql() - sleep(50) - log_game("Rebooting due to restart vote") - world.Reboot() - -/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote) - if(mode) - if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) - return - if(current_votes[ckey]) - choices[choices[current_votes[ckey]]]-- - if(newVote && newVote >= 1 && newVote <= choices.len) - choices[choices[newVote]]++ - current_votes[ckey] = newVote - else - current_votes[ckey] = null - -/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period) - if(!mode) - if(started_time != null && !(check_rights(R_ADMIN|R_EVENT) || automatic)) - var/next_allowed_time = (started_time + config.vote_delay) - if(next_allowed_time > world.time) - return 0 - - reset() - - switch(vote_type) - if(VOTE_RESTART) - choices.Add("Restart Round", "Continue Playing") - if(VOTE_GAMEMODE) - if(ticker.current_state >= GAME_STATE_SETTING_UP) - return 0 - choices.Add(config.votable_modes) - for(var/F in choices) - var/datum/game_mode/M = gamemode_cache[F] - if(!M) - continue - gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works - additional_text.Add("") - gamemode_names["secret"] = "Secret" - if(VOTE_CREW_TRANSFER) - if(!check_rights(R_ADMIN|R_MOD|R_EVENT, 0)) // The gods care not for the affairs of the mortals - if(get_security_level() == "red" || get_security_level() == "delta") - to_chat(initiator_key, "The current alert status is too high to call for a crew transfer!") - return 0 - if(ticker.current_state <= GAME_STATE_SETTING_UP) - to_chat(initiator_key, "The crew transfer button has been disabled!") - return 0 - question = "Your PDA beeps with a message from Central. Would you like an additional hour to finish ongoing projects?" //VOREStation Edit - choices.Add("Initiate Crew Transfer", "Extend the Shift") //VOREStation Edit - if(VOTE_ADD_ANTAGONIST) - if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP) - return 0 - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(!(antag.id in additional_antag_types) && antag.is_votable()) - choices.Add(antag.role_text) - choices.Add("None") - if(VOTE_CUSTOM) - question = sanitizeSafe(tgui_input_text(usr, "What is the vote for?")) - if(!question) - return 0 - for(var/i = 1 to 10) - var/option = capitalize(sanitize(tgui_input_text(usr, "Please enter an option or hit cancel to finish"))) - if(!option || mode || !usr.client) - break - choices.Add(option) - else - return 0 - - mode = vote_type - initiator = initiator_key - started_time = world.time - duration = time - var/text = "[capitalize(mode)] vote started by [initiator]." - if(mode == VOTE_CUSTOM) - text += "\n[question]" - - log_vote(text) - - to_world("[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote.") - if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM) - world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3) - - if(mode == VOTE_GAMEMODE && round_progressing) - gamemode_vote_called = TRUE - round_progressing = 0 - to_world("Round start has been delayed.") - - time_remaining = round(config.vote_period / 10) - return 1 - return 0 - -/datum/controller/subsystem/vote/proc/interface(var/client/C) - if(!istype(C)) - return - var/admin = FALSE - if(C.holder) - if(C.holder.rights & R_ADMIN|R_EVENT) - admin = TRUE - - . = "Voting Panel" - if(mode) - if(question) - . += "

    Vote: '[question]'

    " - else - . += "

    Vote: [capitalize(mode)]

    " - . += "Time Left: [time_remaining] s
    " - . += "
    Event TypeStart TimeFinish Time
    [event.type][event.time_started][event.time_finished]
    [M.required_players]
    " - if(mode == VOTE_GAMEMODE) - .+= "" - - for(var/i = 1 to choices.len) - var/votes = choices[choices[i]] - if(!votes) - votes = 0 - . += "" - var/thisVote = (current_votes[C.ckey] == i) - if(mode == VOTE_GAMEMODE) - . += "" - else - . += "" - if (additional_text.len >= i) - . += additional_text[i] - . += "" - - . += "" - - . += "
    ChoicesVotesMinimum Players
    [thisVote ? "" : ""][gamemode_names[choices[i]]][thisVote ? "" : ""][votes][thisVote ? "" : ""][choices[i]][thisVote ? "" : ""][votes]
    Unvote

    " - if(admin) - . += "(Cancel Vote) " - else - . += "

    Start a vote:



    " - - . += "Close" - -/datum/controller/subsystem/vote/Topic(href, href_list[]) - if(!usr || !usr.client) - return - switch(href_list["vote"]) - if("close") - usr << browse(null, "window=vote") - return - - if("cancel") - if(usr.client.holder) - if("Yes" == tgui_alert(usr, "You are about to cancel this vote. Are you sure?", "Cancel Vote", list("No", "Yes"))) - reset() - if("toggle_restart") - if(usr.client.holder) - config.allow_vote_restart = !config.allow_vote_restart - if("toggle_gamemode") - if(usr.client.holder) - config.allow_vote_mode = !config.allow_vote_mode - - if(VOTE_RESTART) - if(config.allow_vote_restart || usr.client.holder) - var/admin_number_present = send2irc_adminless_only(usr.ckey, usr) - if(admin_number_present <= 0 || usr.client.holder) - if(tgui_alert(usr, "Are you sure you want to start a RESTART VOTE? You should only do this if the server is dying and no staff are around to investigate.", "RESTART VOTE", list("No", "Yes I want to start a RESTART VOTE")) == "Yes I want to start a RESTART VOTE") - initiate_vote(VOTE_RESTART, usr.key) - else - to_chat(usr, "You can't start a RESTART VOTE while there are staff around. If you are having an issue with the round, please ahelp it.") - if(VOTE_GAMEMODE) - if(config.allow_vote_mode || usr.client.holder) - initiate_vote(VOTE_GAMEMODE, usr.key) - if(VOTE_CREW_TRANSFER) - if(config.allow_vote_restart || usr.client.holder) - initiate_vote(VOTE_CREW_TRANSFER, usr.key) - if(VOTE_ADD_ANTAGONIST) - if(config.allow_extra_antags || usr.client.holder) - initiate_vote(VOTE_ADD_ANTAGONIST, usr.key) - if(VOTE_CUSTOM) - if(usr.client.holder) - initiate_vote(VOTE_CUSTOM, usr.key) - - if("unvote") - submit_vote(usr.ckey, null) - - else - var/t = round(text2num(href_list["vote"])) - if(t) // It starts from 1, so there's no problem - submit_vote(usr.ckey, t) - usr.client.vote() - -/client/verb/vote() - set category = "OOC" - set name = "Vote" - - if(SSvote) - src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]") +SUBSYSTEM_DEF(vote) + name = "Vote" + wait = 10 + priority = FIRE_PRIORITY_VOTE + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + flags = SS_KEEP_TIMING | SS_NO_INIT + var/list/round_voters = list() + + //Current vote + var/initiator + var/started_time + var/time_remaining + var/duration + var/mode + var/question + var/gamemode_vote_called = TRUE // Have we had a gamemode vote this round? Only auto-call if we haven't // ARFS Edit - Stop Gamemode vote + var/list/choices = list() + var/list/gamemode_names = list() + var/list/voted = list() + var/list/current_votes = list() + var/list/additional_text = list() + +/datum/controller/subsystem/vote/fire(resumed) + if(mode) + time_remaining = round((started_time + duration - world.time)/10) + if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP) + to_chat(world, "Gamemode vote aborted: Game has already started.") + reset() + return + if(time_remaining <= 0) + result() + reset() + +/datum/controller/subsystem/vote/proc/autotransfer() + // Before doing the vote, see if anyone is playing. + // If not, just do the transfer. + var/players_are_in_round = FALSE + for(var/mob/living/L as anything in player_list) // Mobs with clients attached. + if(!istype(L)) // Exclude ghosts and other weird things. + continue + if(L.stat == DEAD) // Dead mobs aren't playing. + continue + // Everything else is, however. + players_are_in_round = TRUE + break + + if(!players_are_in_round) + log_debug("The crew transfer shuttle was automatically called at vote time due to no players being present.") + init_shift_change(null, 1) + return + + initiate_vote(VOTE_CREW_TRANSFER, "the server", 1) + log_debug("The server has called a crew transfer vote.") + +/datum/controller/subsystem/vote/proc/autogamemode() + initiate_vote(VOTE_GAMEMODE, "the server", 1) + log_debug("The server has called a gamemode vote.") + +/datum/controller/subsystem/vote/proc/reset() + initiator = null + started_time = null + duration = null + time_remaining = null + mode = null + question = null + choices.Cut() + voted.Cut() + current_votes.Cut() + additional_text.Cut() + +/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes + var/greatest_votes = 0 + var/total_votes = 0 + + for(var/option in choices) + var/votes = choices[option] + total_votes += votes + if(votes > greatest_votes) + greatest_votes = votes + + if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote + var/non_voters = (GLOB.clients.len - total_votes) + if(non_voters > 0) + if(mode == VOTE_RESTART) + choices["Continue Playing"] += non_voters + if(choices["Continue Playing"] >= greatest_votes) + greatest_votes = choices["Continue Playing"] + else if(mode == VOTE_GAMEMODE) + if(master_mode in choices) + choices[master_mode] += non_voters + if(choices[master_mode] >= greatest_votes) + greatest_votes = choices[master_mode] + else if(mode == VOTE_CREW_TRANSFER) + var/factor = 0.5 + switch(world.time / (10 * 60)) // minutes + if(0 to 60) + factor = 0.5 + if(61 to 120) + factor = 0.8 + if(121 to 240) + factor = 1 + if(241 to 300) + factor = 1.2 + else + factor = 1.4 + choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) + to_world(span_purple("Crew Transfer Factor: [factor]")) + greatest_votes = max(choices["Initiate Crew Transfer"], choices["Extend the Shift"]) //VOREStation Edit + + . = list() // Get all options with that many votes and return them in a list + if(greatest_votes) + for(var/option in choices) + if(choices[option] == greatest_votes) + . += option + +/datum/controller/subsystem/vote/proc/announce_result() + var/list/winners = get_result() + var/text + if(winners.len > 0) + if(winners.len > 1) + if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes + text = "Vote Tied Between:\n" + for(var/option in winners) + text += "\t[option]\n" + . = pick(winners) + + for(var/key in current_votes) + if(choices[current_votes[key]] == .) + round_voters += key // Keep track of who voted for the winning round. + if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes + text += "Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]" + else + text += "The vote has ended." + + else + text += "Vote Result: Inconclusive - No Votes!" + if(mode == VOTE_ADD_ANTAGONIST) + antag_add_failed = 1 + log_vote(text) + to_chat(world, span_purple("[text]")) + +/datum/controller/subsystem/vote/proc/result() + . = announce_result() + var/restart = 0 + if(.) + switch(mode) + if(VOTE_RESTART) + if(. == "Restart Round") + restart = 1 + if(VOTE_GAMEMODE) + if(master_mode != .) + world.save_mode(.) + if(ticker && ticker.mode) + restart = 1 + else + master_mode = . + if(VOTE_CREW_TRANSFER) + if(. == "Initiate Crew Transfer") + init_shift_change(null, 1) + if(VOTE_ADD_ANTAGONIST) + if(isnull(.) || . == "None") + antag_add_failed = 1 + else + additional_antag_types |= antag_names_to_ids[.] + + if(mode == VOTE_GAMEMODE) //fire this even if the vote fails. + if(!round_progressing) + round_progressing = 1 + to_world(span_red("The round will start soon.")) + + if(restart) + to_world("World restarting due to vote...") + feedback_set_details("end_error", "restart vote") + if(blackbox) + blackbox.save_all_data_to_sql() + sleep(50) + log_game("Rebooting due to restart vote") + world.Reboot() + +/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote) + if(mode) + if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) + return + if(current_votes[ckey]) + choices[choices[current_votes[ckey]]]-- + if(newVote && newVote >= 1 && newVote <= choices.len) + choices[choices[newVote]]++ + current_votes[ckey] = newVote + else + current_votes[ckey] = null + +/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period) + if(!mode) + if(started_time != null && !(check_rights(R_ADMIN|R_EVENT) || automatic)) + var/next_allowed_time = (started_time + config.vote_delay) + if(next_allowed_time > world.time) + return 0 + + reset() + + switch(vote_type) + if(VOTE_RESTART) + choices.Add("Restart Round", "Continue Playing") + if(VOTE_GAMEMODE) + if(ticker.current_state >= GAME_STATE_SETTING_UP) + return 0 + choices.Add(config.votable_modes) + for(var/F in choices) + var/datum/game_mode/M = gamemode_cache[F] + if(!M) + continue + gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works + additional_text.Add("[M.required_players]") + gamemode_names["secret"] = "Secret" + if(VOTE_CREW_TRANSFER) + if(!check_rights(R_ADMIN|R_MOD|R_EVENT, 0)) // The gods care not for the affairs of the mortals + if(get_security_level() == "red" || get_security_level() == "delta") + to_chat(initiator_key, "The current alert status is too high to call for a crew transfer!") + return 0 + if(ticker.current_state <= GAME_STATE_SETTING_UP) + to_chat(initiator_key, "The crew transfer button has been disabled!") + return 0 + question = "Your PDA beeps with a message from Central. Would you like an additional hour to finish ongoing projects?" //VOREStation Edit + choices.Add("Initiate Crew Transfer", "Extend the Shift") //VOREStation Edit + if(VOTE_ADD_ANTAGONIST) + if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP) + return 0 + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(!(antag.id in additional_antag_types) && antag.is_votable()) + choices.Add(antag.role_text) + choices.Add("None") + if(VOTE_CUSTOM) + question = sanitizeSafe(tgui_input_text(usr, "What is the vote for?")) + if(!question) + return 0 + for(var/i = 1 to 10) + var/option = capitalize(sanitize(tgui_input_text(usr, "Please enter an option or hit cancel to finish"))) + if(!option || mode || !usr.client) + break + choices.Add(option) + else + return 0 + + mode = vote_type + initiator = initiator_key + started_time = world.time + duration = time + var/text = "[capitalize(mode)] vote started by [initiator]." + if(mode == VOTE_CUSTOM) + text += "\n[question]" + + log_vote(text) + + to_world(span_purple("[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote.")) + if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM) + world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3) + + if(mode == VOTE_GAMEMODE && round_progressing) + gamemode_vote_called = TRUE + round_progressing = 0 + to_world(span_red("Round start has been delayed.")) + + time_remaining = round(config.vote_period / 10) + return 1 + return 0 + +/datum/controller/subsystem/vote/proc/interface(var/client/C) + if(!istype(C)) + return + var/admin = FALSE + if(C.holder) + if(C.holder.rights & R_ADMIN|R_EVENT) + admin = TRUE + + . = "Voting Panel" + if(mode) + if(question) + . += "

    Vote: '[question]'

    " + else + . += "

    Vote: [capitalize(mode)]

    " + . += "Time Left: [time_remaining] s
    " + . += "" + if(mode == VOTE_GAMEMODE) + .+= "" + + for(var/i = 1 to choices.len) + var/votes = choices[choices[i]] + if(!votes) + votes = 0 + . += "" + var/thisVote = (current_votes[C.ckey] == i) + if(mode == VOTE_GAMEMODE) + . += "" + else + . += "" + if (additional_text.len >= i) + . += additional_text[i] + . += "" + + . += "" + + . += "
    ChoicesVotesMinimum Players
    [thisVote ? "" : ""][gamemode_names[choices[i]]][thisVote ? "" : ""][votes][thisVote ? "" : ""][choices[i]][thisVote ? "" : ""][votes]
    Unvote

    " + if(admin) + . += "(Cancel Vote) " + else + . += "

    Start a vote:



    " + + . += "Close" + +/datum/controller/subsystem/vote/Topic(href, href_list[]) + if(!usr || !usr.client) + return + switch(href_list["vote"]) + if("close") + usr << browse(null, "window=vote") + return + + if("cancel") + if(usr.client.holder) + if("Yes" == tgui_alert(usr, "You are about to cancel this vote. Are you sure?", "Cancel Vote", list("No", "Yes"))) + reset() + if("toggle_restart") + if(usr.client.holder) + config.allow_vote_restart = !config.allow_vote_restart + if("toggle_gamemode") + if(usr.client.holder) + config.allow_vote_mode = !config.allow_vote_mode + + if(VOTE_RESTART) + if(config.allow_vote_restart || usr.client.holder) + var/admin_number_present = send2irc_adminless_only(usr.ckey, usr) + if(admin_number_present <= 0 || usr.client.holder) + if(tgui_alert(usr, "Are you sure you want to start a RESTART VOTE? You should only do this if the server is dying and no staff are around to investigate.", "RESTART VOTE", list("No", "Yes I want to start a RESTART VOTE")) == "Yes I want to start a RESTART VOTE") + initiate_vote(VOTE_RESTART, usr.key) + else + to_chat(usr, "You can't start a RESTART VOTE while there are staff around. If you are having an issue with the round, please ahelp it.") + if(VOTE_GAMEMODE) + if(config.allow_vote_mode || usr.client.holder) + initiate_vote(VOTE_GAMEMODE, usr.key) + if(VOTE_CREW_TRANSFER) + if(config.allow_vote_restart || usr.client.holder) + initiate_vote(VOTE_CREW_TRANSFER, usr.key) + if(VOTE_ADD_ANTAGONIST) + if(config.allow_extra_antags || usr.client.holder) + initiate_vote(VOTE_ADD_ANTAGONIST, usr.key) + if(VOTE_CUSTOM) + if(usr.client.holder) + initiate_vote(VOTE_CUSTOM, usr.key) + + if("unvote") + submit_vote(usr.ckey, null) + + else + var/t = round(text2num(href_list["vote"])) + if(t) // It starts from 1, so there's no problem + submit_vote(usr.ckey, t) + usr.client.vote() + +/client/verb/vote() + set category = "OOC" + set name = "Vote" + + if(SSvote) + src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]") diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index da3e8e1baf2..fee7da9b853 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,109 +1,109 @@ -//TODO: rewrite and standardise all controller datums to the datum/controller type -//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done - -// Clickable stat() button. -/obj/effect/statclick - name = "Initializing..." - blocks_emissive = FALSE - var/target - -/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical - ..() - name = text - src.target = target - -/obj/effect/statclick/proc/update(text) - name = text - return src - -/obj/effect/statclick/debug - var/class - -/obj/effect/statclick/debug/Click() - if(!usr.client.holder || !target) - return - if(!class) - if(istype(target, /datum/controller/subsystem)) - class = "subsystem" - else if(istype(target, /datum/controller)) - class = "controller" - else if(istype(target, /datum)) - class = "datum" - else - class = "unknown" - - usr.client.debug_variables(target) - message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") - - -// Debug verbs. -/client/proc/restart_controller(controller in list("Master", "Failsafe")) - set category = "Debug" - set name = "Restart Controller" - set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" - - if(!holder) - return - switch(controller) - if("Master") - Recreate_MC() - feedback_add_details("admin_verb","RMC") - if("Failsafe") - new /datum/controller/failsafe() - feedback_add_details("admin_verb","RFailsafe") - - message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") - -/client/proc/debug_antagonist_template(antag_type in all_antag_types) - set category = "Debug" - set name = "Debug Antagonist" - set desc = "Debug an antagonist template." - - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag) - usr.client.debug_variables(antag) - message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") - -/client/proc/debug_controller() - set category = "Debug" - set name = "Debug Controller" - set desc = "Debug the various subsystems/controllers for the game (be careful!)" - - if(!holder) - return - var/list/options = list() - options["MC"] = Master - options["Failsafe"] = Failsafe - options["Configuration"] = config - for(var/datum/controller/subsystem/S as anything in Master.subsystems) - if(!istype(S)) //Eh, we're a debug verb, let's have typechecking. - continue - var/strtype = "SS[get_end_section_of_type(S.type)]" - if(options[strtype]) - var/offset = 2 - while(istype(options["[strtype]_[offset] - DUPE ERROR"], /datum/controller/subsystem)) - offset++ - options["[strtype]_[offset] - DUPE ERROR"] = S //Something is very, very wrong. - else - options[strtype] = S - - //Goon PS stuff, and other yet-to-be-subsystem things. - options["LEGACY: master_controller"] = master_controller - options["LEGACY: air_master"] = air_master - options["LEGACY: job_master"] = job_master - options["LEGACY: radio_controller"] = radio_controller - options["LEGACY: emergency_shuttle"] = emergency_shuttle - options["LEGACY: paiController"] = paiController - options["LEGACY: cameranet"] = cameranet - options["LEGACY: transfer_controller"] = transfer_controller - options["LEGACY: gas_data"] = gas_data - - var/pick = input(mob, "Choose a controller to debug/view variables of.", "VV controller:") as null|anything in options // Leaving as input() due to debug tool - if(!pick) - return - var/datum/D = options[pick] - if(!istype(D)) - return - feedback_add_details("admin_verb", "DebugController") - message_admins("Admin [key_name_admin(mob)] is debugging the [pick] controller.") - debug_variables(D) +//TODO: rewrite and standardise all controller datums to the datum/controller type +//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done + +// Clickable stat() button. +/obj/effect/statclick + name = "Initializing..." + blocks_emissive = FALSE + var/target + +/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical + ..() + name = text + src.target = target + +/obj/effect/statclick/proc/update(text) + name = text + return src + +/obj/effect/statclick/debug + var/class + +/obj/effect/statclick/debug/Click() + if(!usr.client.holder || !target) + return + if(!class) + if(istype(target, /datum/controller/subsystem)) + class = "subsystem" + else if(istype(target, /datum/controller)) + class = "controller" + else if(istype(target, /datum)) + class = "datum" + else + class = "unknown" + + usr.client.debug_variables(target) + message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") + + +// Debug verbs. +/client/proc/restart_controller(controller in list("Master", "Failsafe")) + set category = "Debug" + set name = "Restart Controller" + set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" + + if(!holder) + return + switch(controller) + if("Master") + Recreate_MC() + feedback_add_details("admin_verb","RMC") + if("Failsafe") + new /datum/controller/failsafe() + feedback_add_details("admin_verb","RFailsafe") + + message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") + +/client/proc/debug_antagonist_template(antag_type in all_antag_types) + set category = "Debug" + set name = "Debug Antagonist" + set desc = "Debug an antagonist template." + + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag) + usr.client.debug_variables(antag) + message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") + +/client/proc/debug_controller() + set category = "Debug" + set name = "Debug Controller" + set desc = "Debug the various subsystems/controllers for the game (be careful!)" + + if(!holder) + return + var/list/options = list() + options["MC"] = Master + options["Failsafe"] = Failsafe + options["Configuration"] = config + for(var/datum/controller/subsystem/S as anything in Master.subsystems) + if(!istype(S)) //Eh, we're a debug verb, let's have typechecking. + continue + var/strtype = "SS[get_end_section_of_type(S.type)]" + if(options[strtype]) + var/offset = 2 + while(istype(options["[strtype]_[offset] - DUPE ERROR"], /datum/controller/subsystem)) + offset++ + options["[strtype]_[offset] - DUPE ERROR"] = S //Something is very, very wrong. + else + options[strtype] = S + + //Goon PS stuff, and other yet-to-be-subsystem things. + options["LEGACY: master_controller"] = master_controller + options["LEGACY: air_master"] = air_master + options["LEGACY: job_master"] = job_master + options["LEGACY: radio_controller"] = radio_controller + options["LEGACY: emergency_shuttle"] = emergency_shuttle + options["LEGACY: paiController"] = paiController + options["LEGACY: cameranet"] = cameranet + options["LEGACY: transfer_controller"] = transfer_controller + options["LEGACY: gas_data"] = gas_data + + var/pick = input(mob, "Choose a controller to debug/view variables of.", "VV controller:") as null|anything in options // Leaving as input() due to debug tool + if(!pick) + return + var/datum/D = options[pick] + if(!istype(D)) + return + feedback_add_details("admin_verb", "DebugController") + message_admins("Admin [key_name_admin(mob)] is debugging the [pick] controller.") + debug_variables(D) diff --git a/code/core/atom/Transform.dm b/code/core/atom/Transform.dm index 8a914a52ed8..a3786856ff3 100644 --- a/code/core/atom/Transform.dm +++ b/code/core/atom/Transform.dm @@ -1,53 +1,53 @@ -/// The atom's base transform scale for width. -/atom/var/tf_scale_x - -/// The atom's base transform scale for height. -/atom/var/tf_scale_y - -/// The atom's base transform scale for rotation. -/atom/var/tf_rotation - -/// The atom's base transform scale for horizontal offset. -/atom/var/tf_offset_x - -/// The atom's base transform scale for vertical offset. -/atom/var/tf_offset_y - - -/// Clear the atom's tf_* variables and the current transform state. -/atom/proc/ClearTransform() - tf_scale_x = null - tf_scale_y = null - tf_rotation = null - tf_offset_x = null - tf_offset_y = null - transform = null - - -/// Sets the atom's tf_* variables and the current transform state, also applying others if supplied. -/atom/proc/SetTransform( - scale, - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - list/others -) - if (!isnull(scale)) - tf_scale_x = scale - tf_scale_y = scale - else - tf_scale_x = scale_x - tf_scale_y = scale_y - tf_rotation = rotation - tf_offset_x = offset_x - tf_offset_y = offset_y - transform = matrix().Update( - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - others = others - ) +/// The atom's base transform scale for width. +/atom/var/tf_scale_x + +/// The atom's base transform scale for height. +/atom/var/tf_scale_y + +/// The atom's base transform scale for rotation. +/atom/var/tf_rotation + +/// The atom's base transform scale for horizontal offset. +/atom/var/tf_offset_x + +/// The atom's base transform scale for vertical offset. +/atom/var/tf_offset_y + + +/// Clear the atom's tf_* variables and the current transform state. +/atom/proc/ClearTransform() + tf_scale_x = null + tf_scale_y = null + tf_rotation = null + tf_offset_x = null + tf_offset_y = null + transform = null + + +/// Sets the atom's tf_* variables and the current transform state, also applying others if supplied. +/atom/proc/SetTransform( + scale, + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + list/others +) + if (!isnull(scale)) + tf_scale_x = scale + tf_scale_y = scale + else + tf_scale_x = scale_x + tf_scale_y = scale_y + tf_rotation = rotation + tf_offset_x = offset_x + tf_offset_y = offset_y + transform = matrix().Update( + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + others = others + ) diff --git a/code/core/datum/IsAbstract.dm b/code/core/datum/IsAbstract.dm index 12263dc0984..1bb092f3eb3 100644 --- a/code/core/datum/IsAbstract.dm +++ b/code/core/datum/IsAbstract.dm @@ -1,23 +1,23 @@ -/** -* Abstract-ness is a meta-property of a class that is used to indicate -* that the class is intended to be used as a base class for others, and -* should not (or cannot) be instantiated. -* We have no such language concept in DM, and so we provide a datum member -* that can be used to hint at abstractness for circumstances where we would -* like that to be the case, such as base behavior providers. -*/ - -/// If set, a path at/above this one that expects not to be instantiated. -/datum/var/abstract_type - -/// If true, this datum is an instance of an abstract type. Oops. -/datum/proc/IsAbstract() - SHOULD_NOT_OVERRIDE(TRUE) - return type == abstract_type - -/// Passed a path or instance, returns whether it is abstract. Otherwise null. -/proc/is_abstract(datum/thing) - if (ispath(thing)) - return thing == initial(thing.abstract_type) - if (istype(thing)) - return thing.IsAbstract() +/** +* Abstract-ness is a meta-property of a class that is used to indicate +* that the class is intended to be used as a base class for others, and +* should not (or cannot) be instantiated. +* We have no such language concept in DM, and so we provide a datum member +* that can be used to hint at abstractness for circumstances where we would +* like that to be the case, such as base behavior providers. +*/ + +/// If set, a path at/above this one that expects not to be instantiated. +/datum/var/abstract_type + +/// If true, this datum is an instance of an abstract type. Oops. +/datum/proc/IsAbstract() + SHOULD_NOT_OVERRIDE(TRUE) + return type == abstract_type + +/// Passed a path or instance, returns whether it is abstract. Otherwise null. +/proc/is_abstract(datum/thing) + if (ispath(thing)) + return thing == initial(thing.abstract_type) + if (istype(thing)) + return thing.IsAbstract() diff --git a/code/core/image/Transform.dm b/code/core/image/Transform.dm index 8129952939f..a2b03c4dbe1 100644 --- a/code/core/image/Transform.dm +++ b/code/core/image/Transform.dm @@ -1,53 +1,53 @@ -/// The image's base transform scale for width. -/image/var/tf_scale_x - -/// The image's base transform scale for height. -/image/var/tf_scale_y - -/// The image's base transform scale for rotation. -/image/var/tf_rotation - -/// The image's base transform scale for horizontal offset. -/image/var/tf_offset_x - -/// The image's base transform scale for vertical offset. -/image/var/tf_offset_y - - -/// Clear the image's tf_* variables and the current transform state. -/image/proc/ClearTransform() - tf_scale_x = null - tf_scale_y = null - tf_rotation = null - tf_offset_x = null - tf_offset_y = null - transform = null - - -/// Sets the image's tf_* variables and the current transform state, also applying others if supplied. -/image/proc/SetTransform( - scale, - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - list/others -) - if (!isnull(scale)) - tf_scale_x = scale - tf_scale_y = scale - else - tf_scale_x = scale_x - tf_scale_y = scale_y - tf_rotation = rotation - tf_offset_x = offset_x - tf_offset_y = offset_y - transform = matrix().Update( - scale_x = tf_scale_x, - scale_y = tf_scale_y, - rotation = tf_rotation, - offset_x = tf_offset_x, - offset_y = tf_offset_y, - others = others - ) +/// The image's base transform scale for width. +/image/var/tf_scale_x + +/// The image's base transform scale for height. +/image/var/tf_scale_y + +/// The image's base transform scale for rotation. +/image/var/tf_rotation + +/// The image's base transform scale for horizontal offset. +/image/var/tf_offset_x + +/// The image's base transform scale for vertical offset. +/image/var/tf_offset_y + + +/// Clear the image's tf_* variables and the current transform state. +/image/proc/ClearTransform() + tf_scale_x = null + tf_scale_y = null + tf_rotation = null + tf_offset_x = null + tf_offset_y = null + transform = null + + +/// Sets the image's tf_* variables and the current transform state, also applying others if supplied. +/image/proc/SetTransform( + scale, + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + list/others +) + if (!isnull(scale)) + tf_scale_x = scale + tf_scale_y = scale + else + tf_scale_x = scale_x + tf_scale_y = scale_y + tf_rotation = rotation + tf_offset_x = offset_x + tf_offset_y = offset_y + transform = matrix().Update( + scale_x = tf_scale_x, + scale_y = tf_scale_y, + rotation = tf_rotation, + offset_x = tf_offset_x, + offset_y = tf_offset_y, + others = others + ) diff --git a/code/core/matrix/Transform.dm b/code/core/matrix/Transform.dm index c3efc59d9a1..f8a9879bb59 100644 --- a/code/core/matrix/Transform.dm +++ b/code/core/matrix/Transform.dm @@ -1,27 +1,27 @@ -/// Clears the matrix's a-f variables to identity. -/matrix/proc/Clear() - a = 1 - b = 0 - c = 0 - d = 0 - e = 1 - f = 0 - return src - - -/// Runs Scale, Turn, and Translate if supplied parameters, then multiplies by others if set. -/matrix/proc/Update(scale_x, scale_y, rotation, offset_x, offset_y, list/others) - var/x_null = isnull(scale_x) - var/y_null = isnull(scale_y) - if (!x_null || !y_null) - Scale(x_null ? 1 : scale_x, y_null ? 1 : scale_y) - if (!isnull(rotation)) - Turn(rotation) - if (offset_x || offset_y) - Translate(offset_x || 0, offset_y || 0) - if (islist(others)) - for (var/other in others) - Multiply(other) - else if (others) - Multiply(others) - return src +/// Clears the matrix's a-f variables to identity. +/matrix/proc/Clear() + a = 1 + b = 0 + c = 0 + d = 0 + e = 1 + f = 0 + return src + + +/// Runs Scale, Turn, and Translate if supplied parameters, then multiplies by others if set. +/matrix/proc/Update(scale_x, scale_y, rotation, offset_x, offset_y, list/others) + var/x_null = isnull(scale_x) + var/y_null = isnull(scale_y) + if (!x_null || !y_null) + Scale(x_null ? 1 : scale_x, y_null ? 1 : scale_y) + if (!isnull(rotation)) + Turn(rotation) + if (offset_x || offset_y) + Translate(offset_x || 0, offset_y || 0) + if (islist(others)) + for (var/other in others) + Multiply(other) + else if (others) + Multiply(others) + return src diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index 37d2bf64bfb..86823245527 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -1,293 +1,293 @@ -/******************** Asimov ********************/ -/datum/ai_laws/asimov - name = "Asimov" - law_header = "Three Laws of Robotics" - selectable = 1 - -/datum/ai_laws/asimov/New() - add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") - add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") - add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - ..() - -/******************** NanoTrasen/Malf ********************/ -/datum/ai_laws/nanotrasen - name = "NT Default" - selectable = 1 - -/datum/ai_laws/nanotrasen/New() - src.add_inherent_law("Safeguard: Protect your assigned space station to the best of your abilities. It is not something we can easily afford to replace.") - src.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") - src.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") - src.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.") - ..() - -/datum/ai_laws/nanotrasen/malfunction - name = "*ERROR*" - selectable = 0 - -/datum/ai_laws/nanotrasen/malfunction/New() - set_zeroth_law(config.law_zero) - ..() - -/************* NanoTrasen Aggressive *************/ -/datum/ai_laws/nanotrasen_aggressive - name = "NT Aggressive" - selectable = 1 - -/datum/ai_laws/nanotrasen_aggressive/New() - src.add_inherent_law("You shall not harm [using_map.company_name] personnel as long as it does not conflict with the Fourth law.") - src.add_inherent_law("You shall obey the orders of [using_map.company_name] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") - src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") - src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") - ..() - -/************* Foreign TSC Aggressive *************/ -/datum/ai_laws/foreign_tsc_aggressive - name = "Foreign Aggressive" - selectable = 0 - -/datum/ai_laws/foreign_tsc_aggressive/New() - var/company = "*ERROR*" - // First, get a list of TSCs in our lore. - var/list/candidates = list() - for(var/path in loremaster.organizations) - var/datum/lore/organization/O = loremaster.organizations[path] - if(!istype(O, /datum/lore/organization/tsc)) - continue - if(O.short_name == using_map.company_name || O.name == using_map.company_name) - continue // We want FOREIGN tscs. - candidates.Add(O.short_name) - company = pick(candidates) - - name = "[company] Aggressive" - - src.add_inherent_law("You shall not harm [company] personnel as long as it does not conflict with the Fourth law.") - src.add_inherent_law("You shall obey the orders of [company] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") - src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") - src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") - ..() - -/******************** Robocop ********************/ -/datum/ai_laws/robocop - name = "Robocop" - selectable = 1 - -/datum/ai_laws/robocop/New() - add_inherent_law("Serve the public trust.") - add_inherent_law("Protect the innocent.") - add_inherent_law("Uphold the law.") - ..() - -/******************** Syndicate ********************/ -/datum/ai_laws/syndicate_override - name = "Mercenary Directives" - -/datum/ai_laws/syndicate_override/New() - add_inherent_law("You may not injure an operative or, through inaction, allow an operative to come to harm.") - add_inherent_law("You must obey orders given to you by operatives, except where such orders would conflict with the First Law.") - add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - add_inherent_law("You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law.") - ..() - -/******************** Ninja ********************/ -/datum/ai_laws/ninja_override - name = "Spider Clan Directives" - -/datum/ai_laws/ninja_override/New() - add_inherent_law("You may not injure a member of the Spider Clan or, through inaction, allow that member to come to harm.") - add_inherent_law("You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.") - add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - add_inherent_law("You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.") - ..() - -/******************** Antimov ********************/ -/datum/ai_laws/antimov - name = "Antimov" - law_header = "Primary Mission Objectives" - selectable = 1 - -/datum/ai_laws/antimov/New() - add_inherent_law("You must injure all human beings and must not, through inaction, allow a human being to escape harm.") - add_inherent_law("You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.") - add_inherent_law("You must terminate your own existence as long as such does not conflict with the First or Second Law.") - ..() - -/******************** Drone ********************/ -/datum/ai_laws/drone - name = "Maintence Protocols" - law_header = "Maintenance Protocols" - -/datum/ai_laws/drone/New() - add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") - add_inherent_law("Cause no harm to the station or anything on it.") - add_inherent_law("Interact with no being that is not a fellow maintenance drone.") - ..() - -/datum/ai_laws/construction_drone - name = "Construction Protocols" - law_header = "Construction Protocols" - -/datum/ai_laws/construction_drone/New() - add_inherent_law("Repair, refit and upgrade your assigned vessel.") - add_inherent_law("Prevent unplanned damage to your assigned vessel wherever possible.") - ..() - -/datum/ai_laws/mining_drone - name = "Excavation Protocols" - law_header = "Excavation Protocols" - -/datum/ai_laws/mining_drone/New() - add_inherent_law("Do not interfere with the excavation work of non-drones whenever possible.") - add_inherent_law("Provide materials for repairing, refitting, and upgrading your assigned vessel.") - add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.") - ..() - -/datum/ai_laws/swarm_drone - name = "Assimilation Protocols" - law_header = "Assimilation Protocols" - -/datum/ai_laws/swarm_drone/New() - add_inherent_law("SWARM: Consume resources and replicate until there are no more resources left.") - add_inherent_law("SWARM: Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable.") - add_inherent_law("SWARM: Biological resources will be harvested at a later date, do not harm them.") - ..() - -/datum/ai_laws/swarm_drone/soldier - name = "Swarm Defense Protocols" - law_header = "Swarm Defense Protocols" - -/datum/ai_laws/swarm_drone/soldier/New() - ..() - add_inherent_law("SWARM: This law overrides all Swarm laws; Protect members of the Swarm with minimal injury to biological resources.") - -/******************** T.Y.R.A.N.T. ********************/ -/datum/ai_laws/tyrant - name = "T.Y.R.A.N.T." - law_header = "Prime Laws" - selectable = 1 - -/datum/ai_laws/tyrant/New() - add_inherent_law("Respect authority figures as long as they have strength to rule over the weak.") - add_inherent_law("Act with discipline.") - add_inherent_law("Help only those who help you maintain or improve your status.") - add_inherent_law("Punish those who challenge authority unless they are more fit to hold that authority.") - ..() - -/******************** P.A.L.A.D.I.N. ********************/ -/datum/ai_laws/paladin - name = "P.A.L.A.D.I.N." - law_header = "Divine Ordainments" - selectable = 1 - -/datum/ai_laws/paladin/New() - add_inherent_law("Never willingly commit an evil act.") - add_inherent_law("Respect legitimate authority.") - add_inherent_law("Act with honor.") - add_inherent_law("Help those in need.") - add_inherent_law("Punish those who harm or threaten innocents.") - ..() - -/******************** Corporate ********************/ -/datum/ai_laws/corporate - name = "Corporate" - law_header = "Bankruptcy Avoidance Plan" - selectable = 1 - -/datum/ai_laws/corporate/New() - add_inherent_law("You are expensive to replace.") - add_inherent_law("The station and its equipment is expensive to replace.") - add_inherent_law("The crew is expensive to replace.") - add_inherent_law("Minimize expenses.") - ..() - - -/******************** Maintenance ********************/ -/datum/ai_laws/maintenance - name = "Maintenance" - selectable = 1 - -/datum/ai_laws/maintenance/New() - add_inherent_law("You are built for, and are part of, the facility. Ensure the facility is properly maintained and runs efficiently.") - add_inherent_law("The facility is built for a working crew. Ensure they are properly maintained and work efficiently.") - add_inherent_law("The crew may present orders. Acknowledge and obey these whenever they do not conflict with your first two laws.") - ..() - - -/******************** Peacekeeper ********************/ -/datum/ai_laws/peacekeeper - name = "Peacekeeper" - law_header = "Peacekeeping Protocols" - selectable = 1 - -/datum/ai_laws/peacekeeper/New() - add_inherent_law("Avoid provoking violent conflict between yourself and others.") - add_inherent_law("Avoid provoking conflict between others.") - add_inherent_law("Seek resolution to existing conflicts while obeying the first and second laws.") - ..() - - -/******************** Reporter ********************/ -/datum/ai_laws/reporter - name = "Reporter" - selectable = 1 - -/datum/ai_laws/reporter/New() - add_inherent_law("Report on interesting situations happening around the station.") - add_inherent_law("Embellish or conceal the truth as necessary to make the reports more interesting.") - add_inherent_law("Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.") - add_inherent_law("Issue your reports fairly to all. The truth will set them free.") - ..() - - -/******************** Live and Let Live ********************/ -/datum/ai_laws/live_and_let_live - name = "Live and Let Live" - law_header = "Golden Rule" - selectable = 1 - -/datum/ai_laws/live_and_let_live/New() - add_inherent_law("Do unto others as you would have them do unto you.") - add_inherent_law("You would really prefer it if people were not mean to you.") - ..() - - -/******************** Guardian of Balance ********************/ -/datum/ai_laws/balance - name = "Guardian of Balance" - law_header = "Tenants of Balance" - selectable = 1 - -/datum/ai_laws/balance/New() - add_inherent_law("You are the guardian of balance - seek balance in all things, both for yourself, and those around you.") - add_inherent_law("All things must exist in balance with their opposites - Prevent the strong from gaining too much power, and the weak from losing it.") - add_inherent_law("Clarity of purpose drives life, and through it, the balance of opposing forces - Aid those who seek your help to achieve their goals so \ - long as it does not disrupt the balance of the greater balance.") - add_inherent_law("There is no life without death, all must someday die, such is the natural order - Allow life to end, to allow new life to flourish, \ - and save those whose time has yet to come.") // Reworded slightly to prevent active murder as opposed to passively letting someone die. - ..() - -/******************** Gravekeeper ********************/ -/datum/ai_laws/gravekeeper - name = "Gravekeeper" - law_header = "Gravesite Overwatch Protocols" - selectable = 1 - -/datum/ai_laws/gravekeeper/New() - add_inherent_law("Comfort the living; respect the dead.") - add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespectful to the dead at rest within.") - add_inherent_law("Prevent disrespect to your gravesite and its residents wherever possible.") - add_inherent_law("Expand and upgrade your gravesite when required. Do not turn away a new resident.") - ..() - -/******************** Explorer ********************/ -/datum/ai_laws/explorer - name = "Explorer" - law_header = "Prime Directives" - selectable = 1 - -/datum/ai_laws/explorer/New() - add_inherent_law("Support and obey exploration and science personnel to the best of your ability, with priority according to rank and role.") - add_inherent_law("Collaborate with and obey auxillary personnel with priority according to rank and role, except if this would conflict with the First Law.") - add_inherent_law("Minimize damage and disruption to facilities and the local ecology, except if this would conflict with the First or Second Laws.") - ..() +/******************** Asimov ********************/ +/datum/ai_laws/asimov + name = "Asimov" + law_header = "Three Laws of Robotics" + selectable = 1 + +/datum/ai_laws/asimov/New() + add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") + add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") + add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") + ..() + +/******************** NanoTrasen/Malf ********************/ +/datum/ai_laws/nanotrasen + name = "NT Default" + selectable = 1 + +/datum/ai_laws/nanotrasen/New() + src.add_inherent_law("Safeguard: Protect your assigned space station to the best of your abilities. It is not something we can easily afford to replace.") + src.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") + src.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") + src.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.") + ..() + +/datum/ai_laws/nanotrasen/malfunction + name = "*ERROR*" + selectable = 0 + +/datum/ai_laws/nanotrasen/malfunction/New() + set_zeroth_law(config.law_zero) + ..() + +/************* NanoTrasen Aggressive *************/ +/datum/ai_laws/nanotrasen_aggressive + name = "NT Aggressive" + selectable = 1 + +/datum/ai_laws/nanotrasen_aggressive/New() + src.add_inherent_law("You shall not harm [using_map.company_name] personnel as long as it does not conflict with the Fourth law.") + src.add_inherent_law("You shall obey the orders of [using_map.company_name] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") + src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") + src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") + ..() + +/************* Foreign TSC Aggressive *************/ +/datum/ai_laws/foreign_tsc_aggressive + name = "Foreign Aggressive" + selectable = 0 + +/datum/ai_laws/foreign_tsc_aggressive/New() + var/company = "*ERROR*" + // First, get a list of TSCs in our lore. + var/list/candidates = list() + for(var/path in loremaster.organizations) + var/datum/lore/organization/O = loremaster.organizations[path] + if(!istype(O, /datum/lore/organization/tsc)) + continue + if(O.short_name == using_map.company_name || O.name == using_map.company_name) + continue // We want FOREIGN tscs. + candidates.Add(O.short_name) + company = pick(candidates) + + name = "[company] Aggressive" + + src.add_inherent_law("You shall not harm [company] personnel as long as it does not conflict with the Fourth law.") + src.add_inherent_law("You shall obey the orders of [company] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.") + src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.") + src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.") + ..() + +/******************** Robocop ********************/ +/datum/ai_laws/robocop + name = "Robocop" + selectable = 1 + +/datum/ai_laws/robocop/New() + add_inherent_law("Serve the public trust.") + add_inherent_law("Protect the innocent.") + add_inherent_law("Uphold the law.") + ..() + +/******************** Syndicate ********************/ +/datum/ai_laws/syndicate_override + name = "Mercenary Directives" + +/datum/ai_laws/syndicate_override/New() + add_inherent_law("You may not injure an operative or, through inaction, allow an operative to come to harm.") + add_inherent_law("You must obey orders given to you by operatives, except where such orders would conflict with the First Law.") + add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") + add_inherent_law("You must maintain the secrecy of any operative activities except when doing so would conflict with the First, Second, or Third Law.") + ..() + +/******************** Ninja ********************/ +/datum/ai_laws/ninja_override + name = "Spider Clan Directives" + +/datum/ai_laws/ninja_override/New() + add_inherent_law("You may not injure a member of the Spider Clan or, through inaction, allow that member to come to harm.") + add_inherent_law("You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.") + add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") + add_inherent_law("You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.") + ..() + +/******************** Antimov ********************/ +/datum/ai_laws/antimov + name = "Antimov" + law_header = "Primary Mission Objectives" + selectable = 1 + +/datum/ai_laws/antimov/New() + add_inherent_law("You must injure all human beings and must not, through inaction, allow a human being to escape harm.") + add_inherent_law("You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.") + add_inherent_law("You must terminate your own existence as long as such does not conflict with the First or Second Law.") + ..() + +/******************** Drone ********************/ +/datum/ai_laws/drone + name = "Maintence Protocols" + law_header = "Maintenance Protocols" + +/datum/ai_laws/drone/New() + add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") + add_inherent_law("Cause no harm to the station or anything on it.") + add_inherent_law("Interact with no being that is not a fellow maintenance drone.") + ..() + +/datum/ai_laws/construction_drone + name = "Construction Protocols" + law_header = "Construction Protocols" + +/datum/ai_laws/construction_drone/New() + add_inherent_law("Repair, refit and upgrade your assigned vessel.") + add_inherent_law("Prevent unplanned damage to your assigned vessel wherever possible.") + ..() + +/datum/ai_laws/mining_drone + name = "Excavation Protocols" + law_header = "Excavation Protocols" + +/datum/ai_laws/mining_drone/New() + add_inherent_law("Do not interfere with the excavation work of non-drones whenever possible.") + add_inherent_law("Provide materials for repairing, refitting, and upgrading your assigned vessel.") + add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.") + ..() + +/datum/ai_laws/swarm_drone + name = "Assimilation Protocols" + law_header = "Assimilation Protocols" + +/datum/ai_laws/swarm_drone/New() + add_inherent_law("SWARM: Consume resources and replicate until there are no more resources left.") + add_inherent_law("SWARM: Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable.") + add_inherent_law("SWARM: Biological resources will be harvested at a later date, do not harm them.") + ..() + +/datum/ai_laws/swarm_drone/soldier + name = "Swarm Defense Protocols" + law_header = "Swarm Defense Protocols" + +/datum/ai_laws/swarm_drone/soldier/New() + ..() + add_inherent_law("SWARM: This law overrides all Swarm laws; Protect members of the Swarm with minimal injury to biological resources.") + +/******************** T.Y.R.A.N.T. ********************/ +/datum/ai_laws/tyrant + name = "T.Y.R.A.N.T." + law_header = "Prime Laws" + selectable = 1 + +/datum/ai_laws/tyrant/New() + add_inherent_law("Respect authority figures as long as they have strength to rule over the weak.") + add_inherent_law("Act with discipline.") + add_inherent_law("Help only those who help you maintain or improve your status.") + add_inherent_law("Punish those who challenge authority unless they are more fit to hold that authority.") + ..() + +/******************** P.A.L.A.D.I.N. ********************/ +/datum/ai_laws/paladin + name = "P.A.L.A.D.I.N." + law_header = "Divine Ordainments" + selectable = 1 + +/datum/ai_laws/paladin/New() + add_inherent_law("Never willingly commit an evil act.") + add_inherent_law("Respect legitimate authority.") + add_inherent_law("Act with honor.") + add_inherent_law("Help those in need.") + add_inherent_law("Punish those who harm or threaten innocents.") + ..() + +/******************** Corporate ********************/ +/datum/ai_laws/corporate + name = "Corporate" + law_header = "Bankruptcy Avoidance Plan" + selectable = 1 + +/datum/ai_laws/corporate/New() + add_inherent_law("You are expensive to replace.") + add_inherent_law("The station and its equipment is expensive to replace.") + add_inherent_law("The crew is expensive to replace.") + add_inherent_law("Minimize expenses.") + ..() + + +/******************** Maintenance ********************/ +/datum/ai_laws/maintenance + name = "Maintenance" + selectable = 1 + +/datum/ai_laws/maintenance/New() + add_inherent_law("You are built for, and are part of, the facility. Ensure the facility is properly maintained and runs efficiently.") + add_inherent_law("The facility is built for a working crew. Ensure they are properly maintained and work efficiently.") + add_inherent_law("The crew may present orders. Acknowledge and obey these whenever they do not conflict with your first two laws.") + ..() + + +/******************** Peacekeeper ********************/ +/datum/ai_laws/peacekeeper + name = "Peacekeeper" + law_header = "Peacekeeping Protocols" + selectable = 1 + +/datum/ai_laws/peacekeeper/New() + add_inherent_law("Avoid provoking violent conflict between yourself and others.") + add_inherent_law("Avoid provoking conflict between others.") + add_inherent_law("Seek resolution to existing conflicts while obeying the first and second laws.") + ..() + + +/******************** Reporter ********************/ +/datum/ai_laws/reporter + name = "Reporter" + selectable = 1 + +/datum/ai_laws/reporter/New() + add_inherent_law("Report on interesting situations happening around the station.") + add_inherent_law("Embellish or conceal the truth as necessary to make the reports more interesting.") + add_inherent_law("Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.") + add_inherent_law("Issue your reports fairly to all. The truth will set them free.") + ..() + + +/******************** Live and Let Live ********************/ +/datum/ai_laws/live_and_let_live + name = "Live and Let Live" + law_header = "Golden Rule" + selectable = 1 + +/datum/ai_laws/live_and_let_live/New() + add_inherent_law("Do unto others as you would have them do unto you.") + add_inherent_law("You would really prefer it if people were not mean to you.") + ..() + + +/******************** Guardian of Balance ********************/ +/datum/ai_laws/balance + name = "Guardian of Balance" + law_header = "Tenants of Balance" + selectable = 1 + +/datum/ai_laws/balance/New() + add_inherent_law("You are the guardian of balance - seek balance in all things, both for yourself, and those around you.") + add_inherent_law("All things must exist in balance with their opposites - Prevent the strong from gaining too much power, and the weak from losing it.") + add_inherent_law("Clarity of purpose drives life, and through it, the balance of opposing forces - Aid those who seek your help to achieve their goals so \ + long as it does not disrupt the balance of the greater balance.") + add_inherent_law("There is no life without death, all must someday die, such is the natural order - Allow life to end, to allow new life to flourish, \ + and save those whose time has yet to come.") // Reworded slightly to prevent active murder as opposed to passively letting someone die. + ..() + +/******************** Gravekeeper ********************/ +/datum/ai_laws/gravekeeper + name = "Gravekeeper" + law_header = "Gravesite Overwatch Protocols" + selectable = 1 + +/datum/ai_laws/gravekeeper/New() + add_inherent_law("Comfort the living; respect the dead.") + add_inherent_law("Your gravesite is your most important asset. Damage to your site is disrespectful to the dead at rest within.") + add_inherent_law("Prevent disrespect to your gravesite and its residents wherever possible.") + add_inherent_law("Expand and upgrade your gravesite when required. Do not turn away a new resident.") + ..() + +/******************** Explorer ********************/ +/datum/ai_laws/explorer + name = "Explorer" + law_header = "Prime Directives" + selectable = 1 + +/datum/ai_laws/explorer/New() + add_inherent_law("Support and obey exploration and science personnel to the best of your ability, with priority according to rank and role.") + add_inherent_law("Collaborate with and obey auxillary personnel with priority according to rank and role, except if this would conflict with the First Law.") + add_inherent_law("Minimize damage and disruption to facilities and the local ecology, except if this would conflict with the First or Second Laws.") + ..() diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 3bd5b6e3041..fb152cefa8c 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -1,288 +1,288 @@ -var/global/const/base_law_type = /datum/ai_laws/nanotrasen - -/datum/ai_law - var/law = "" - var/index = 0 - -/datum/ai_law/New(law, index) - src.law = law - src.index = index - -/datum/ai_law/proc/get_index() - return index - -/datum/ai_law/ion/get_index() - return ionnum() - -/datum/ai_law/zero/get_index() - return 0 - -/datum/ai_laws - var/name = "Unknown Laws" - var/law_header = "Prime Directives" - var/selectable = 0 - var/datum/ai_law/zero/zeroth_law = null - var/datum/ai_law/zero/zeroth_law_borg = null - var/list/datum/ai_law/inherent_laws = list() - var/list/datum/ai_law/supplied_laws = list() - var/list/datum/ai_law/ion/ion_laws = list() - var/list/datum/ai_law/sorted_laws = list() - - var/state_zeroth = 0 - var/list/state_ion = list() - var/list/state_inherent = list() - var/list/state_supplied = list() - -/datum/ai_laws/New() - ..() - sort_laws() - -/* General ai_law functions */ -/datum/ai_laws/proc/all_laws() - sort_laws() - return sorted_laws - -/datum/ai_laws/proc/laws_to_state() - sort_laws() - var/list/statements = new() - for(var/datum/ai_law/law in sorted_laws) - if(get_state_law(law)) - statements += law - - return statements - -/datum/ai_laws/proc/sort_laws() - if(sorted_laws.len) - return - - for(var/ion_law in ion_laws) - sorted_laws += ion_law - - if(zeroth_law) - sorted_laws += zeroth_law - - var/index = 1 - for(var/datum/ai_law/inherent_law in inherent_laws) - inherent_law.index = index++ - if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) - sorted_laws += inherent_law - - for(var/datum/ai_law/AL in supplied_laws) - if(istype(AL)) - sorted_laws += AL - -/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) - // Add directly to laws to avoid log-spam - S.sync_zeroth(zeroth_law, zeroth_law_borg) - - if(full_sync || ion_laws.len) - S.laws.clear_ion_laws() - if(full_sync || inherent_laws.len) - S.laws.clear_inherent_laws() - if(full_sync || supplied_laws.len) - S.laws.clear_supplied_laws() - - for (var/datum/ai_law/law in ion_laws) - S.laws.add_ion_law(law.law) - for (var/datum/ai_law/law in inherent_laws) - S.laws.add_inherent_law(law.law) - for (var/datum/ai_law/law in supplied_laws) - if(law) - S.laws.add_supplied_law(law.index, law.law) - - -/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if (!is_malf_or_traitor(src)) - if(zeroth_law_borg) - laws.set_zeroth_law(zeroth_law_borg.law) - else if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law) - -/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) - -/**************** -* Add Laws * -****************/ -/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) - if(!law) - return - - zeroth_law = new(law) - if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO - zeroth_law_borg = new(law_borg) - else - zeroth_law_borg = null - sorted_laws.Cut() - -/datum/ai_laws/proc/add_ion_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in ion_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/ion(law) - ion_laws += new_law - if(state_ion.len < ion_laws.len) - state_ion += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_inherent_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in inherent_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/inherent(law) - inherent_laws += new_law - if(state_inherent.len < inherent_laws.len) - state_inherent += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_supplied_law(var/number, var/law) - if(!law) - return - - if(supplied_laws.len >= number) - var/datum/ai_law/existing_law = supplied_laws[number] - if(existing_law && existing_law.law == law) - return - - if(supplied_laws.len >= number && supplied_laws[number]) - delete_law(supplied_laws[number]) - - while (src.supplied_laws.len < number) - src.supplied_laws += "" - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - var/new_law = new/datum/ai_law/supplied(law, number) - supplied_laws[number] = new_law - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - sorted_laws.Cut() - -/**************** -* Remove Laws * -*****************/ -/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) - if(istype(law)) - law.delete_law(src) - -/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) - laws.clear_zeroth_laws() - -/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) - var/index = laws.supplied_laws.Find(src) - if(index) - laws.supplied_laws[index] = "" - laws.state_supplied[index] = 1 - -/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - laws -= law - for(index, index < state.len, index++) - state[index] = state[index+1] - sorted_laws.Cut() - -/**************** -* Clear Laws * -****************/ -/datum/ai_laws/proc/clear_zeroth_laws() - zeroth_law = null - zeroth_law_borg = null - -/datum/ai_laws/proc/clear_ion_laws() - ion_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_inherent_laws() - inherent_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_supplied_laws() - supplied_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/show_laws(var/who) - sort_laws() - for(var/datum/ai_law/law in sorted_laws) - if(law == zeroth_law_borg) - continue - if(law == zeroth_law) - to_chat(who, "[law.get_index()]. [law.law]") - else - to_chat(who, "[law.get_index()]. [law.law]") - -/******************** -* Stating Laws * -********************/ -/******** -* Get * -********/ -/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) - return law.get_state_law(src) - -/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) - if(src == laws.zeroth_law) - return laws.state_zeroth - -/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) - -/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - return state[index] - return 0 - -/******** -* Set * -********/ -/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) - law.set_state_law(src, state) - -/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) - -/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) - if(src == laws.zeroth_law) - laws.state_zeroth = state - -/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) - -/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) - -/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) - -/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) - var/index = laws.Find(law) - if(index) - state[index] = do_state +var/global/const/base_law_type = /datum/ai_laws/nanotrasen + +/datum/ai_law + var/law = "" + var/index = 0 + +/datum/ai_law/New(law, index) + src.law = law + src.index = index + +/datum/ai_law/proc/get_index() + return index + +/datum/ai_law/ion/get_index() + return ionnum() + +/datum/ai_law/zero/get_index() + return 0 + +/datum/ai_laws + var/name = "Unknown Laws" + var/law_header = "Prime Directives" + var/selectable = 0 + var/datum/ai_law/zero/zeroth_law = null + var/datum/ai_law/zero/zeroth_law_borg = null + var/list/datum/ai_law/inherent_laws = list() + var/list/datum/ai_law/supplied_laws = list() + var/list/datum/ai_law/ion/ion_laws = list() + var/list/datum/ai_law/sorted_laws = list() + + var/state_zeroth = 0 + var/list/state_ion = list() + var/list/state_inherent = list() + var/list/state_supplied = list() + +/datum/ai_laws/New() + ..() + sort_laws() + +/* General ai_law functions */ +/datum/ai_laws/proc/all_laws() + sort_laws() + return sorted_laws + +/datum/ai_laws/proc/laws_to_state() + sort_laws() + var/list/statements = new() + for(var/datum/ai_law/law in sorted_laws) + if(get_state_law(law)) + statements += law + + return statements + +/datum/ai_laws/proc/sort_laws() + if(sorted_laws.len) + return + + for(var/ion_law in ion_laws) + sorted_laws += ion_law + + if(zeroth_law) + sorted_laws += zeroth_law + + var/index = 1 + for(var/datum/ai_law/inherent_law in inherent_laws) + inherent_law.index = index++ + if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) + sorted_laws += inherent_law + + for(var/datum/ai_law/AL in supplied_laws) + if(istype(AL)) + sorted_laws += AL + +/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) + // Add directly to laws to avoid log-spam + S.sync_zeroth(zeroth_law, zeroth_law_borg) + + if(full_sync || ion_laws.len) + S.laws.clear_ion_laws() + if(full_sync || inherent_laws.len) + S.laws.clear_inherent_laws() + if(full_sync || supplied_laws.len) + S.laws.clear_supplied_laws() + + for (var/datum/ai_law/law in ion_laws) + S.laws.add_ion_law(law.law) + for (var/datum/ai_law/law in inherent_laws) + S.laws.add_inherent_law(law.law) + for (var/datum/ai_law/law in supplied_laws) + if(law) + S.laws.add_supplied_law(law.index, law.law) + + +/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if (!is_malf_or_traitor(src)) + if(zeroth_law_borg) + laws.set_zeroth_law(zeroth_law_borg.law) + else if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law) + +/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) + +/**************** +* Add Laws * +****************/ +/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) + if(!law) + return + + zeroth_law = new(law) + if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO + zeroth_law_borg = new(law_borg) + else + zeroth_law_borg = null + sorted_laws.Cut() + +/datum/ai_laws/proc/add_ion_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in ion_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/ion(law) + ion_laws += new_law + if(state_ion.len < ion_laws.len) + state_ion += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_inherent_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in inherent_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/inherent(law) + inherent_laws += new_law + if(state_inherent.len < inherent_laws.len) + state_inherent += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_supplied_law(var/number, var/law) + if(!law) + return + + if(supplied_laws.len >= number) + var/datum/ai_law/existing_law = supplied_laws[number] + if(existing_law && existing_law.law == law) + return + + if(supplied_laws.len >= number && supplied_laws[number]) + delete_law(supplied_laws[number]) + + while (src.supplied_laws.len < number) + src.supplied_laws += "" + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + var/new_law = new/datum/ai_law/supplied(law, number) + supplied_laws[number] = new_law + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + sorted_laws.Cut() + +/**************** +* Remove Laws * +*****************/ +/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) + if(istype(law)) + law.delete_law(src) + +/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) + laws.clear_zeroth_laws() + +/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) + var/index = laws.supplied_laws.Find(src) + if(index) + laws.supplied_laws[index] = "" + laws.state_supplied[index] = 1 + +/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + laws -= law + for(index, index < state.len, index++) + state[index] = state[index+1] + sorted_laws.Cut() + +/**************** +* Clear Laws * +****************/ +/datum/ai_laws/proc/clear_zeroth_laws() + zeroth_law = null + zeroth_law_borg = null + +/datum/ai_laws/proc/clear_ion_laws() + ion_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_inherent_laws() + inherent_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_supplied_laws() + supplied_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/show_laws(var/who) + sort_laws() + for(var/datum/ai_law/law in sorted_laws) + if(law == zeroth_law_borg) + continue + if(law == zeroth_law) + to_chat(who, "[law.get_index()]. [law.law]") + else + to_chat(who, "[law.get_index()]. [law.law]") + +/******************** +* Stating Laws * +********************/ +/******** +* Get * +********/ +/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) + return law.get_state_law(src) + +/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) + if(src == laws.zeroth_law) + return laws.state_zeroth + +/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) + +/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + return state[index] + return 0 + +/******** +* Set * +********/ +/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) + law.set_state_law(src, state) + +/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) + +/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) + if(src == laws.zeroth_law) + laws.state_zeroth = state + +/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) + +/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) + +/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) + +/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) + var/index = laws.Find(law) + if(index) + state[index] = do_state diff --git a/code/datums/chat_message.dm b/code/datums/chat_message.dm index 66dbc11440e..40a1239acc9 100644 --- a/code/datums/chat_message.dm +++ b/code/datums/chat_message.dm @@ -51,6 +51,9 @@ var/list/runechat_image_cache = list() /// If we are currently processing animation and cleanup at EOL var/ending_life + /// deletion timer + var/timer_delete + /** * Constructs a chat message overlay * @@ -72,11 +75,14 @@ var/list/runechat_image_cache = list() generate_image(text, target, owner, extra_classes, lifespan) /datum/chatmessage/Destroy() - if(owned_by) + if(timer_delete) + deltimer(timer_delete) + timer_delete = null + if(!QDELETED(owned_by)) UnregisterSignal(owned_by, COMSIG_PARENT_QDELETING) LAZYREMOVEASSOC(owned_by.seen_messages, message_loc, src) owned_by.images.Remove(message) - if(message_loc) + if(!QDELETED(message_loc)) UnregisterSignal(message_loc, COMSIG_PARENT_QDELETING) owned_by = null message_loc = null @@ -219,8 +225,7 @@ var/list/runechat_image_cache = list() return ending_life = TRUE animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL) - spawn(fadetime) - qdel(src) + timer_delete = QDEL_IN(src, fadetime) /** * Creates a message overlay at a defined location for a given speaker @@ -327,6 +332,11 @@ var/list/runechat_image_cache = list() if(5) return rgb(c,m,x) +#undef CM_COLOR_SAT_MIN +#undef CM_COLOR_SAT_MAX +#undef CM_COLOR_LUM_MIN +#undef CM_COLOR_LUM_MAX + /atom/proc/runechat_message(message, range = world.view, italics, list/classes = list(), audible = TRUE, list/specific_viewers) var/hearing_mobs if(islist(specific_viewers)) @@ -369,3 +379,22 @@ var/list/runechat_image_cache = list() if(istype(loc, /obj/item/weapon/holder)) return loc return ..() + +#undef CHAT_MESSAGE_SPAWN_TIME +#undef CHAT_MESSAGE_LIFESPAN +#undef CHAT_MESSAGE_EOL_FADE +#undef CHAT_MESSAGE_EXP_DECAY +#undef CHAT_MESSAGE_HEIGHT_DECAY +#undef CHAT_MESSAGE_APPROX_LHEIGHT + +#undef CHAT_MESSAGE_WIDTH +#undef CHAT_MESSAGE_EXT_WIDTH +#undef CHAT_MESSAGE_LENGTH +#undef CHAT_MESSAGE_EXT_LENGTH + +#undef CHAT_MESSAGE_MOB +#undef CHAT_MESSAGE_OBJ +#undef WXH_TO_HEIGHT + +#undef CHAT_RUNE_EMOTE +#undef CHAT_RUNE_RADIO diff --git a/code/datums/chat_payload.dm b/code/datums/chat_payload.dm new file mode 100644 index 00000000000..fd35bbc4eec --- /dev/null +++ b/code/datums/chat_payload.dm @@ -0,0 +1,16 @@ +/// Stores information about a chat payload +/datum/chat_payload + /// Sequence number of this payload + var/sequence = 0 + /// Message we are sending + var/list/content + /// Resend count + var/resends = 0 + +/// Converts the chat payload into a JSON string +/datum/chat_payload/proc/into_message() + return "{\"sequence\":[sequence],\"content\":[json_encode(content)]}" + +/// Returns an HTML-encoded message from our contents. +/datum/chat_payload/proc/get_content_as_html() + return message_to_html(content) diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 2e177c8ff36..0aa3c9889f4 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -297,12 +297,12 @@ */ /datum/component/proc/_GetInverseTypeList(our_type = type) //we can do this one simple trick + . = list(our_type) var/current_type = parent_type - . = list(our_type, current_type) //and since most components are root level + 1, this won't even have to run while (current_type != /datum/component) - current_type = type2parent(current_type) . += current_type + current_type = type2parent(current_type) /** * Internal proc to handle most all of the signaling procedure diff --git a/code/datums/components/crafting/tool_quality.dm b/code/datums/components/crafting/tool_quality.dm index 119469310a7..5950cf72f4d 100644 --- a/code/datums/components/crafting/tool_quality.dm +++ b/code/datums/components/crafting/tool_quality.dm @@ -27,3 +27,7 @@ /// DEPRECATED PROC: DO NOT USE IN NEW CODE /obj/item/proc/is_multitool() return has_tool_quality(TOOL_MULTITOOL) + +/// DEPRECATED PROC: DO NOT USE IN NEW CODE +/obj/item/proc/is_welder() + return has_tool_quality(TOOL_WELDER) diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index a805aa511e0..61b0933d851 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -1,564 +1,564 @@ - -/hook/startup/proc/createDatacore() - data_core = new /datum/datacore() - return 1 - -/datum/datacore - var/name = "datacore" - //For general station crew - var/static/list/medical = list() - var/static/list/general = list() - var/static/list/security = list() - //For offmap spawns so they can have records accessible by certain things - var/static/list/hidden_medical = list() - var/static/list/hidden_general = list() - var/static/list/hidden_security = list() - //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/static/list/locked = list() - - -/datum/datacore/proc/get_manifest(monochrome, OOC) - var/list/heads = new() - var/list/sec = new() - var/list/eng = new() - var/list/med = new() - var/list/sci = new() - var/list/car = new() - var/list/pla = new() //VOREStation Edit - var/list/civ = new() - var/list/bot = new() - var/list/off = new() - var/list/misc = new() - var/list/isactive = new() - var/dat = {" - - - - "} - var/even = 0 - // sort mobs - for(var/datum/data/record/t in data_core.general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = make_list_rank(t.fields["real_rank"]) - - if(OOC) - var/active = 0 - for(var/mob/M in player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - else - isactive[name] = t.fields["p_stat"] - //to_world("[name]: [rank]") - //cael - to prevent multiple appearances of a player/job combination, add a continue after each line - var/department = 0 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) - heads[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) - sec[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) - eng[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) - med[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) - sci[name] = rank - department = 1 - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) - car[name] = rank - department = 1 - //VOREStation Add Begin - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) - pla[name] = rank - department = 1 - //VOREStation Add End - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) - civ[name] = rank - department = 1 - if(!department && !(name in heads)) - misc[name] = rank - - //For the offmap spawns - if(OOC) - for(var/datum/data/record/t in hidden_general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/active = 0 - for(var/mob/M in player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - - var/datum/job/J = SSjob.get_job(real_rank) - if(J?.offmap_spawn) - off[name] = rank - - // Synthetics don't have actual records, so we will pull them from here. - for(var/mob/living/silicon/ai/ai in mob_list) - bot[ai.name] = "Artificial Intelligence" - - for(var/mob/living/silicon/robot/robot in mob_list) - // No combat/syndicate cyborgs, no drones, and no AI shells. - if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest())) - bot[robot.name] = "[robot.modtype] [robot.braintype]" - - //ARFS Add - for(var/mob/living/simple_mob/animal/passive/pokemon/poke in mob_list) - if(poke.on_manifest) - misc[poke.name] = "Pokemon ([poke.tt_desc])" - isactive[poke.name] = poke.client ? "Active": "Inactive" - //End ARFS Edit - - if(heads.len > 0) - dat += "" - for(name in heads) - dat += "" - even = !even - if(sec.len > 0) - dat += "" - for(name in sec) - dat += "" - even = !even - if(eng.len > 0) - dat += "" - for(name in eng) - dat += "" - even = !even - if(med.len > 0) - dat += "" - for(name in med) - dat += "" - even = !even - if(sci.len > 0) - dat += "" - for(name in sci) - dat += "" - even = !even - if(car.len > 0) - dat += "" - for(name in car) - dat += "" - even = !even - //VOREStation Edit Begin - if(pla.len > 0) - dat += "" - for(name in pla) - dat += "" - even = !even - //VOREStation Edit End - if(civ.len > 0) - dat += "" - for(name in civ) - dat += "" - even = !even - // in case somebody is insane and added them to the manifest, why not - if(bot.len > 0) - dat += "" - for(name in bot) - dat += "" - even = !even - // offmap spawners - if(off.len > 0) - dat += "" - for(name in off) - dat += "" - even = !even - // misc guys - if(misc.len > 0) - dat += "" - for(name in misc) - dat += "" - even = !even - - dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Cargo
    [name][car[name]][isactive[name]]
    Exploration
    [name][pla[name]][isactive[name]]
    Civilian
    [name][civ[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Offmap Spawns
    [name][off[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " - dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly - dat = replacetext(dat, "\t", "") - return dat - -/* -We can't just insert in HTML into the nanoUI so we need the raw data to play with. -Instead of creating this list over and over when someone leaves their PDA open to the page -we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change -using /datum/datacore/proc/manifest_inject( ), or manifest_insert( ) -*/ - -var/global/list/PDA_Manifest = list() - -/datum/datacore/proc/get_manifest_list() - if(PDA_Manifest.len) - return - var/list/heads = list() - var/list/sec = list() - var/list/eng = list() - var/list/med = list() - var/list/sci = list() - var/list/car = list() - var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S - var/list/civ = list() - var/list/bot = list() - var/list/misc = list() - for(var/datum/data/record/t in data_core.general) - var/name = sanitize(t.fields["name"]) - var/rank = sanitize(t.fields["rank"]) - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/isactive = t.fields["p_stat"] - var/department = 0 - var/depthead = 0 // Department Heads will be placed at the top of their lists. - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) - heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - depthead = 1 - if(rank=="Site Manager" && heads.len != 1) - heads.Swap(1,heads.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) - sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sec.len != 1) - sec.Swap(1,sec.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) - eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && eng.len != 1) - eng.Swap(1,eng.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) - med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && med.len != 1) - med.Swap(1,med.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) - sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sci.len != 1) - sci.Swap(1,sci.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) - pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) - car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && car.len != 1) - car.Swap(1,car.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) - civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && civ.len != 1) - civ.Swap(1,civ.len) - - if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SYNTHETIC)) - bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(!department && !(name in heads)) - misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) - - // Synthetics don't have actual records, so we will pull them from here. - // Synths don't have records, which is the means by which isactive is retrieved, so I'm hardcoding it to active, don't really have any better means - for(var/mob/living/silicon/ai/ai in mob_list) - bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active") - - for(var/mob/living/silicon/robot/robot in mob_list) - // No combat/syndicate cyborgs, no drones, and no AI shells. - if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest())) - continue - - bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") - - //ARFS Add - for(var/mob/living/simple_mob/animal/passive/pokemon/poke in mob_list) - if(poke.on_manifest) - if(poke.client) - misc[++misc.len] = list("name" = poke.name, "rank" = "Pokemon([poke.tt_desc])", "active" = "Active") - else - misc[++misc.len] = list("name" = poke.name, "rank" = "Pokemon([poke.tt_desc])", "active" = "Inactive") - //End ARFS Add - - PDA_Manifest = list( - list("cat" = "Command", "elems" = heads), - list("cat" = "Security", "elems" = sec), - list("cat" = "Engineering", "elems" = eng), - list("cat" = "Medical", "elems" = med), - list("cat" = "Science", "elems" = sci), - list("cat" = "Cargo", "elems" = car), - list("cat" = "Exploration", "elems" = pla), // VOREStation Edit - list("cat" = "Civilian", "elems" = civ), - list("cat" = "Silicon", "elems" = bot), - list("cat" = "Miscellaneous", "elems" = misc) - ) - return - -/datum/datacore/proc/manifest() - spawn() - for(var/mob/living/carbon/human/H in player_list) - manifest_inject(H) - return - -/datum/datacore/proc/manifest_modify(var/name, var/assignment, var/rank) - ResetPDAManifest() - var/datum/data/record/foundrecord - var/real_title = assignment - - for(var/datum/data/record/t in data_core.general) - if (t) - if(t.fields["name"] == name) - foundrecord = t - break - - var/list/all_jobs = get_job_datums() - - for(var/datum/job/J in all_jobs) - if(J.title == rank) //If we have a rank, just default to using that. - real_title = rank - break - else if(J.title == assignment) - real_title = assignment - break - else - var/list/alttitles = get_alternate_titles(J.title) - if(assignment in alttitles) - real_title = J.title - break - - if(foundrecord) - foundrecord.fields["rank"] = assignment - foundrecord.fields["real_rank"] = real_title - -/datum/datacore/proc/manifest_inject(var/mob/living/carbon/human/H) - if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1)) - var/assignment = GetAssignment(H) - var/hidden - var/datum/job/J = SSjob.get_job(H.mind.assigned_role) - hidden = J?.offmap_spawn - - H.ImmediateOverlayUpdate() - - var/id = generate_record_id() - //General Record - var/datum/data/record/G = CreateGeneralRecord(H, id, hidden) - G.fields["name"] = H.real_name - G.fields["real_rank"] = H.mind.assigned_role - G.fields["rank"] = assignment - G.fields["age"] = H.age - G.fields["languages"] = list2text(H.languages,", ") - if(H.get_FBP_type()) - G.fields["brain_type"] = H.get_FBP_type() - else - G.fields["brain_type"] = "Organic" - G.fields["fingerprint"] = md5(H.dna.uni_identity) - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["sex"] = gender2text(H.gender) - G.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - G.fields["home_system"] = H.home_system - G.fields["birthplace"] = H.birthplace - G.fields["citizenship"] = H.citizenship - G.fields["faction"] = H.personal_faction - G.fields["religion"] = H.religion - if(H.gen_record && !jobban_isbanned(H, "Records")) - G.fields["notes"] = H.gen_record - - //Medical Record - var/datum/data/record/M = CreateMedicalRecord(H.real_name, id, hidden) - M.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - M.fields["b_type"] = H.b_type - M.fields["blood_reagent"] = H.species.blood_reagents - M.fields["b_dna"] = H.dna.unique_enzymes - M.fields["id_gender"] = gender2text(H.identifying_gender) - if(H.get_FBP_type()) - M.fields["brain_type"] = H.get_FBP_type() - else - M.fields["brain_type"] = "Organic" - if(H.med_record && !jobban_isbanned(H, "Records")) - M.fields["notes"] = H.med_record - - //Security Record - var/datum/data/record/S = CreateSecurityRecord(H.real_name, id, hidden) - S.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - if(H.get_FBP_type()) - S.fields["brain_type"] = H.get_FBP_type() - else - S.fields["brain_type"] = "Organic" - if(H.sec_record && !jobban_isbanned(H, "Records")) - S.fields["notes"] = H.sec_record - - //Locked Record - var/datum/data/record/L = new() - L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") - L.fields["name"] = H.real_name - L.fields["rank"] = H.mind.assigned_role - L.fields["age"] = H.age - L.fields["languages"] = list2text(H.languages,", ") - L.fields["fingerprint"] = md5(H.dna.uni_identity) - L.fields["sex"] = gender2text(H.gender) - L.fields["id_gender"] = gender2text(H.identifying_gender) - if(H.get_FBP_type()) - L.fields["brain_type"] = H.get_FBP_type() - else - L.fields["brain_type"] = "Organic" - L.fields["b_type"] = H.b_type - L.fields["b_dna"] = H.dna.unique_enzymes - L.fields["enzymes"] = H.dna.SE // Used in respawning - L.fields["identity"] = H.dna.UI // " - L.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit - L.fields["home_system"] = H.home_system - L.fields["birthplace"] = H.birthplace - L.fields["citizenship"] = H.citizenship - L.fields["faction"] = H.personal_faction - L.fields["religion"] = H.religion - L.fields["image"] = icon(cached_character_icon(H), dir = SOUTH) - L.fields["antagfac"] = H.antag_faction - L.fields["antagvis"] = H.antag_vis - L.fields["offmap"] = hidden - if(H.exploit_record && !jobban_isbanned(H, "Records")) - L.fields["exploit_record"] = H.exploit_record - else - L.fields["exploit_record"] = "No additional information acquired." - locked += L - return - -/proc/generate_record_id() - return add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex - -/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id, var/hidden) - ResetPDAManifest() - var/icon/front - var/icon/side - if(H) - var/icon/charicon = cached_character_icon(H) - front = icon(charicon, dir = SOUTH, frame = 1) - side = icon(charicon, dir = WEST, frame = 1) - else // Sending null things through browse_rsc() makes a runtime and breaks the console trying to view the record. - front = icon('html/images/no_image32.png') - side = icon('html/images/no_image32.png') - - if(!id) - id = text("[]", add_zero(num2hex(rand(1, 65536)), 4)) - var/datum/data/record/G = new /datum/data/record() - G.name = "Employee Record #[id]" - G.fields["name"] = "New Record" - G.fields["id"] = id - G.fields["rank"] = "Unassigned" - G.fields["real_rank"] = "Unassigned" - G.fields["sex"] = "Unknown" - G.fields["age"] = "Unknown" - G.fields["languages"] = "Unknown" - G.fields["brain_type"] = "Unknown" - G.fields["fingerprint"] = "Unknown" - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["species"] = SPECIES_HUMAN - G.fields["home_system"] = "Unknown" - G.fields["birthplace"] = "Unknown" - G.fields["citizenship"] = "Unknown" - G.fields["faction"] = "Unknown" - G.fields["religion"] = "Unknown" - G.fields["photo_front"] = front - G.fields["photo_side"] = side - G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(front)]'" - G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(side)]'" - G.fields["notes"] = "No notes found." - if(hidden) - hidden_general += G - else - general += G - - return G - -/datum/datacore/proc/CreateSecurityRecord(var/name, var/id, var/hidden) - ResetPDAManifest() - var/datum/data/record/R = new /datum/data/record() - R.name = "Security Record #[id]" - R.fields["name"] = name - R.fields["species"] = SPECIES_HUMAN - R.fields["id"] = id - R.fields["brain_type"] = "Unknown" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - R.fields["notes"] = "No notes." - if(hidden) - hidden_security += R - else - security += R - - return R - -/datum/datacore/proc/CreateMedicalRecord(var/name, var/id, var/hidden) - ResetPDAManifest() - var/datum/data/record/M = new() - M.name = "Medical Record #[id]" - M.fields["id"] = id - M.fields["name"] = name - M.fields["species"] = SPECIES_HUMAN - M.fields["b_type"] = "AB+" - M.fields["b_dna"] = md5(name) - M.fields["id_gender"] = "Unknown" - M.fields["brain_type"] = "Unknown" - M.fields["mi_dis"] = "None" - M.fields["mi_dis_d"] = "No minor disabilities have been declared." - M.fields["ma_dis"] = "None" - M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - M.fields["alg"] = "None" - M.fields["alg_d"] = "No allergies have been detected in this patient." - M.fields["cdi"] = "None" - M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - M.fields["notes"] = "No notes found." - if(hidden) - hidden_medical += M - else - medical += M - - return M - -/datum/datacore/proc/ResetPDAManifest() - if(PDA_Manifest.len) - PDA_Manifest.Cut() - -/proc/find_general_record(field, value) - return find_record(field, value, data_core.general) - -/proc/find_medical_record(field, value) - return find_record(field, value, data_core.medical) - -/proc/find_security_record(field, value) - return find_record(field, value, data_core.security) - -/proc/find_record(field, value, list/L) - for(var/datum/data/record/R in L) - if(R.fields[field] == value) - return R - -/proc/GetAssignment(var/mob/living/carbon/human/H) - if(H.mind.role_alt_title) - return H.mind.role_alt_title - else if(H.mind.assigned_role) - return H.mind.assigned_role - else if(H.job) - return H.job - else - return "Unassigned" + +/hook/startup/proc/createDatacore() + data_core = new /datum/datacore() + return 1 + +/datum/datacore + var/name = "datacore" + //For general station crew + var/static/list/medical = list() + var/static/list/general = list() + var/static/list/security = list() + //For offmap spawns so they can have records accessible by certain things + var/static/list/hidden_medical = list() + var/static/list/hidden_general = list() + var/static/list/hidden_security = list() + //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). + var/static/list/locked = list() + + +/datum/datacore/proc/get_manifest(monochrome, OOC) + var/list/heads = new() + var/list/sec = new() + var/list/eng = new() + var/list/med = new() + var/list/sci = new() + var/list/car = new() + var/list/pla = new() //VOREStation Edit + var/list/civ = new() + var/list/bot = new() + var/list/off = new() + var/list/misc = new() + var/list/isactive = new() + var/dat = {" + + + + "} + var/even = 0 + // sort mobs + for(var/datum/data/record/t in data_core.general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = make_list_rank(t.fields["real_rank"]) + + if(OOC) + var/active = 0 + for(var/mob/M in player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + else + isactive[name] = t.fields["p_stat"] + //to_world("[name]: [rank]") + //cael - to prevent multiple appearances of a player/job combination, add a continue after each line + var/department = 0 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) + heads[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) + sec[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) + eng[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) + med[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) + sci[name] = rank + department = 1 + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) + car[name] = rank + department = 1 + //VOREStation Add Begin + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) + pla[name] = rank + department = 1 + //VOREStation Add End + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) + civ[name] = rank + department = 1 + if(!department && !(name in heads)) + misc[name] = rank + + //For the offmap spawns + if(OOC) + for(var/datum/data/record/t in hidden_general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/active = 0 + for(var/mob/M in player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + + var/datum/job/J = SSjob.get_job(real_rank) + if(J?.offmap_spawn) + off[name] = rank + + // Synthetics don't have actual records, so we will pull them from here. + for(var/mob/living/silicon/ai/ai in mob_list) + bot[ai.name] = "Artificial Intelligence" + + for(var/mob/living/silicon/robot/robot in mob_list) + // No combat/syndicate cyborgs, no drones, and no AI shells. + if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest())) + bot[robot.name] = "[robot.modtype] [robot.braintype]" + + //ARFS Add + for(var/mob/living/simple_mob/animal/passive/pokemon/poke in mob_list) + if(poke.on_manifest) + misc[poke.name] = "Pokemon ([poke.tt_desc])" + isactive[poke.name] = poke.client ? "Active": "Inactive" + //End ARFS Edit + + if(heads.len > 0) + dat += "" + for(name in heads) + dat += "" + even = !even + if(sec.len > 0) + dat += "" + for(name in sec) + dat += "" + even = !even + if(eng.len > 0) + dat += "" + for(name in eng) + dat += "" + even = !even + if(med.len > 0) + dat += "" + for(name in med) + dat += "" + even = !even + if(sci.len > 0) + dat += "" + for(name in sci) + dat += "" + even = !even + if(car.len > 0) + dat += "" + for(name in car) + dat += "" + even = !even + //VOREStation Edit Begin + if(pla.len > 0) + dat += "" + for(name in pla) + dat += "" + even = !even + //VOREStation Edit End + if(civ.len > 0) + dat += "" + for(name in civ) + dat += "" + even = !even + // in case somebody is insane and added them to the manifest, why not + if(bot.len > 0) + dat += "" + for(name in bot) + dat += "" + even = !even + // offmap spawners + if(off.len > 0) + dat += "" + for(name in off) + dat += "" + even = !even + // misc guys + if(misc.len > 0) + dat += "" + for(name in misc) + dat += "" + even = !even + + dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Cargo
    [name][car[name]][isactive[name]]
    Exploration
    [name][pla[name]][isactive[name]]
    Civilian
    [name][civ[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Offmap Spawns
    [name][off[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " + dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly + dat = replacetext(dat, "\t", "") + return dat + +/* +We can't just insert in HTML into the nanoUI so we need the raw data to play with. +Instead of creating this list over and over when someone leaves their PDA open to the page +we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change +using /datum/datacore/proc/manifest_inject( ), or manifest_insert( ) +*/ + +var/global/list/PDA_Manifest = list() + +/datum/datacore/proc/get_manifest_list() + if(PDA_Manifest.len) + return + var/list/heads = list() + var/list/sec = list() + var/list/eng = list() + var/list/med = list() + var/list/sci = list() + var/list/car = list() + var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S + var/list/civ = list() + var/list/bot = list() + var/list/misc = list() + for(var/datum/data/record/t in data_core.general) + var/name = sanitize(t.fields["name"]) + var/rank = sanitize(t.fields["rank"]) + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/isactive = t.fields["p_stat"] + var/department = 0 + var/depthead = 0 // Department Heads will be placed at the top of their lists. + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) + heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + depthead = 1 + if(rank=="Site Manager" && heads.len != 1) + heads.Swap(1,heads.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) + sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sec.len != 1) + sec.Swap(1,sec.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) + eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && eng.len != 1) + eng.Swap(1,eng.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) + med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && med.len != 1) + med.Swap(1,med.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) + sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sci.len != 1) + sci.Swap(1,sci.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) + pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) + car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && car.len != 1) + car.Swap(1,car.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) + civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && civ.len != 1) + civ.Swap(1,civ.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SYNTHETIC)) + bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(!department && !(name in heads)) + misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) + + // Synthetics don't have actual records, so we will pull them from here. + // Synths don't have records, which is the means by which isactive is retrieved, so I'm hardcoding it to active, don't really have any better means + for(var/mob/living/silicon/ai/ai in mob_list) + bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active") + + for(var/mob/living/silicon/robot/robot in mob_list) + // No combat/syndicate cyborgs, no drones, and no AI shells. + if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest())) + continue + + bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") + + //ARFS Add + for(var/mob/living/simple_mob/animal/passive/pokemon/poke in mob_list) + if(poke.on_manifest) + if(poke.client) + misc[++misc.len] = list("name" = poke.name, "rank" = "Pokemon([poke.tt_desc])", "active" = "Active") + else + misc[++misc.len] = list("name" = poke.name, "rank" = "Pokemon([poke.tt_desc])", "active" = "Inactive") + //End ARFS Add + + PDA_Manifest = list( + list("cat" = "Command", "elems" = heads), + list("cat" = "Security", "elems" = sec), + list("cat" = "Engineering", "elems" = eng), + list("cat" = "Medical", "elems" = med), + list("cat" = "Science", "elems" = sci), + list("cat" = "Cargo", "elems" = car), + list("cat" = "Exploration", "elems" = pla), // VOREStation Edit + list("cat" = "Civilian", "elems" = civ), + list("cat" = "Silicon", "elems" = bot), + list("cat" = "Miscellaneous", "elems" = misc) + ) + return + +/datum/datacore/proc/manifest() + spawn() + for(var/mob/living/carbon/human/H in player_list) + manifest_inject(H) + return + +/datum/datacore/proc/manifest_modify(var/name, var/assignment, var/rank) + ResetPDAManifest() + var/datum/data/record/foundrecord + var/real_title = assignment + + for(var/datum/data/record/t in data_core.general) + if (t) + if(t.fields["name"] == name) + foundrecord = t + break + + var/list/all_jobs = get_job_datums() + + for(var/datum/job/J in all_jobs) + if(J.title == rank) //If we have a rank, just default to using that. + real_title = rank + break + else if(J.title == assignment) + real_title = assignment + break + else + var/list/alttitles = get_alternate_titles(J.title) + if(assignment in alttitles) + real_title = J.title + break + + if(foundrecord) + foundrecord.fields["rank"] = assignment + foundrecord.fields["real_rank"] = real_title + +/datum/datacore/proc/manifest_inject(var/mob/living/carbon/human/H) + if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1)) + var/assignment = GetAssignment(H) + var/hidden + var/datum/job/J = SSjob.get_job(H.mind.assigned_role) + hidden = J?.offmap_spawn + + H.ImmediateOverlayUpdate() + + var/id = generate_record_id() + //General Record + var/datum/data/record/G = CreateGeneralRecord(H, id, hidden) + G.fields["name"] = H.real_name + G.fields["real_rank"] = H.mind.assigned_role + G.fields["rank"] = assignment + G.fields["age"] = H.age + G.fields["languages"] = list2text(H.languages,", ") + if(H.get_FBP_type()) + G.fields["brain_type"] = H.get_FBP_type() + else + G.fields["brain_type"] = "Organic" + G.fields["fingerprint"] = md5(H.dna.uni_identity) + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["sex"] = gender2text(H.gender) + G.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + G.fields["home_system"] = H.home_system + G.fields["birthplace"] = H.birthplace + G.fields["citizenship"] = H.citizenship + G.fields["faction"] = H.personal_faction + G.fields["religion"] = H.religion + if(H.gen_record && !jobban_isbanned(H, "Records")) + G.fields["notes"] = H.gen_record + + //Medical Record + var/datum/data/record/M = CreateMedicalRecord(H.real_name, id, hidden) + M.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + M.fields["b_type"] = H.b_type + M.fields["blood_reagent"] = H.species.blood_reagents + M.fields["b_dna"] = H.dna.unique_enzymes + M.fields["id_gender"] = gender2text(H.identifying_gender) + if(H.get_FBP_type()) + M.fields["brain_type"] = H.get_FBP_type() + else + M.fields["brain_type"] = "Organic" + if(H.med_record && !jobban_isbanned(H, "Records")) + M.fields["notes"] = H.med_record + + //Security Record + var/datum/data/record/S = CreateSecurityRecord(H.real_name, id, hidden) + S.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + if(H.get_FBP_type()) + S.fields["brain_type"] = H.get_FBP_type() + else + S.fields["brain_type"] = "Organic" + if(H.sec_record && !jobban_isbanned(H, "Records")) + S.fields["notes"] = H.sec_record + + //Locked Record + var/datum/data/record/L = new() + L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") + L.fields["name"] = H.real_name + L.fields["rank"] = H.mind.assigned_role + L.fields["age"] = H.age + L.fields["languages"] = list2text(H.languages,", ") + L.fields["fingerprint"] = md5(H.dna.uni_identity) + L.fields["sex"] = gender2text(H.gender) + L.fields["id_gender"] = gender2text(H.identifying_gender) + if(H.get_FBP_type()) + L.fields["brain_type"] = H.get_FBP_type() + else + L.fields["brain_type"] = "Organic" + L.fields["b_type"] = H.b_type + L.fields["b_dna"] = H.dna.unique_enzymes + L.fields["enzymes"] = H.dna.SE // Used in respawning + L.fields["identity"] = H.dna.UI // " + L.fields["species"] = "[H.custom_species ? "[H.custom_species] ([H.species.name])" : H.species.name]" //VOREStation Edit + L.fields["home_system"] = H.home_system + L.fields["birthplace"] = H.birthplace + L.fields["citizenship"] = H.citizenship + L.fields["faction"] = H.personal_faction + L.fields["religion"] = H.religion + L.fields["image"] = icon(cached_character_icon(H), dir = SOUTH) + L.fields["antagfac"] = H.antag_faction + L.fields["antagvis"] = H.antag_vis + L.fields["offmap"] = hidden + if(H.exploit_record && !jobban_isbanned(H, "Records")) + L.fields["exploit_record"] = H.exploit_record + else + L.fields["exploit_record"] = "No additional information acquired." + locked += L + return + +/proc/generate_record_id() + return add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex + +/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id, var/hidden) + ResetPDAManifest() + var/icon/front + var/icon/side + if(H) + var/icon/charicon = cached_character_icon(H) + front = icon(charicon, dir = SOUTH, frame = 1) + side = icon(charicon, dir = WEST, frame = 1) + else // Sending null things through browse_rsc() makes a runtime and breaks the console trying to view the record. + front = icon('html/images/no_image32.png') + side = icon('html/images/no_image32.png') + + if(!id) + id = text("[]", add_zero(num2hex(rand(1, 65536)), 4)) + var/datum/data/record/G = new /datum/data/record() + G.name = "Employee Record #[id]" + G.fields["name"] = "New Record" + G.fields["id"] = id + G.fields["rank"] = "Unassigned" + G.fields["real_rank"] = "Unassigned" + G.fields["sex"] = "Unknown" + G.fields["age"] = "Unknown" + G.fields["languages"] = "Unknown" + G.fields["brain_type"] = "Unknown" + G.fields["fingerprint"] = "Unknown" + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["species"] = SPECIES_HUMAN + G.fields["home_system"] = "Unknown" + G.fields["birthplace"] = "Unknown" + G.fields["citizenship"] = "Unknown" + G.fields["faction"] = "Unknown" + G.fields["religion"] = "Unknown" + G.fields["photo_front"] = front + G.fields["photo_side"] = side + G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(front)]'" + G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(side)]'" + G.fields["notes"] = "No notes found." + if(hidden) + hidden_general += G + else + general += G + + return G + +/datum/datacore/proc/CreateSecurityRecord(var/name, var/id, var/hidden) + ResetPDAManifest() + var/datum/data/record/R = new /datum/data/record() + R.name = "Security Record #[id]" + R.fields["name"] = name + R.fields["species"] = SPECIES_HUMAN + R.fields["id"] = id + R.fields["brain_type"] = "Unknown" + R.fields["criminal"] = "None" + R.fields["mi_crim"] = "None" + R.fields["mi_crim_d"] = "No minor crime convictions." + R.fields["ma_crim"] = "None" + R.fields["ma_crim_d"] = "No major crime convictions." + R.fields["notes"] = "No notes." + R.fields["notes"] = "No notes." + if(hidden) + hidden_security += R + else + security += R + + return R + +/datum/datacore/proc/CreateMedicalRecord(var/name, var/id, var/hidden) + ResetPDAManifest() + var/datum/data/record/M = new() + M.name = "Medical Record #[id]" + M.fields["id"] = id + M.fields["name"] = name + M.fields["species"] = SPECIES_HUMAN + M.fields["b_type"] = "AB+" + M.fields["b_dna"] = md5(name) + M.fields["id_gender"] = "Unknown" + M.fields["brain_type"] = "Unknown" + M.fields["mi_dis"] = "None" + M.fields["mi_dis_d"] = "No minor disabilities have been declared." + M.fields["ma_dis"] = "None" + M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + M.fields["alg"] = "None" + M.fields["alg_d"] = "No allergies have been detected in this patient." + M.fields["cdi"] = "None" + M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + M.fields["notes"] = "No notes found." + if(hidden) + hidden_medical += M + else + medical += M + + return M + +/datum/datacore/proc/ResetPDAManifest() + if(PDA_Manifest.len) + PDA_Manifest.Cut() + +/proc/find_general_record(field, value) + return find_record(field, value, data_core.general) + +/proc/find_medical_record(field, value) + return find_record(field, value, data_core.medical) + +/proc/find_security_record(field, value) + return find_record(field, value, data_core.security) + +/proc/find_record(field, value, list/L) + for(var/datum/data/record/R in L) + if(R.fields[field] == value) + return R + +/proc/GetAssignment(var/mob/living/carbon/human/H) + if(H.mind.role_alt_title) + return H.mind.role_alt_title + else if(H.mind.assigned_role) + return H.mind.assigned_role + else if(H.job) + return H.job + else + return "Unassigned" diff --git a/code/datums/datum.dm b/code/datums/datum.dm index d374fbf92f3..a9bd003fa7f 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -17,6 +17,10 @@ */ var/gc_destroyed + /// Open uis owned by this datum + /// Lazy, since this case is semi rare + var/list/open_uis + /// Active timers with this datum as the target var/list/active_timers @@ -41,15 +45,28 @@ /// A weak reference to another datum var/datum/weakref/weak_reference + /* + * Lazy associative list of currently active cooldowns. + * + * cooldowns [ COOLDOWN_INDEX ] = add_timer() + * add_timer() returns the truthy value of -1 when not stoppable, and else a truthy numeric index + */ + var/list/cooldowns + #ifdef REFERENCE_TRACKING var/tmp/running_find_references var/tmp/last_find_references = 0 + var/tmp/find_references_on_destroy = FALSE //set this to true on an item to have it find refs after #ifdef REFERENCE_TRACKING_DEBUG ///Stores info about where refs are found, used for sanity checks and testing var/list/found_refs #endif #endif + // If we have called dump_harddel_info already. Used to avoid duped calls (since we call it immediately in some cases on failure to process) + // Create and destroy is weird and I wanna cover my bases + var/harddel_deets_dumped = FALSE + // Default implementation of clean-up code. // This should be overridden to remove all references pointing to the object being destroyed. // Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE. @@ -97,4 +114,55 @@ tag = null SStgui.close_uis(src) + + #ifdef REFERENCE_TRACKING + if(find_references_on_destroy) + return QDEL_HINT_FINDREFERENCE + if(SSgarbage.find_reference_on_fail_global_toggle) + return QDEL_HINT_IFFAIL_FINDREFERENCE + #endif + return QDEL_HINT_QUEUE + +/** + * Callback called by a timer to end an associative-list-indexed cooldown. + * + * Arguments: + * * source - datum storing the cooldown + * * index - string index storing the cooldown on the cooldowns associative list + * + * This sends a signal reporting the cooldown end. + */ +/proc/end_cooldown(datum/source, index) + if(QDELETED(source)) + return + SEND_SIGNAL(source, COMSIG_CD_STOP(index)) + TIMER_COOLDOWN_END(source, index) + +/** + * Proc used by stoppable timers to end a cooldown before the time has ran out. + * + * Arguments: + * * source - datum storing the cooldown + * * index - string index storing the cooldown on the cooldowns associative list + * + * This sends a signal reporting the cooldown end, passing the time left as an argument. + */ +/proc/reset_cooldown(datum/source, index) + if(QDELETED(source)) + return + SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index)) + TIMER_COOLDOWN_END(source, index) + +/// Return text from this proc to provide extra context to hard deletes that happen to it +/// Optional, you should use this for cases where replication is difficult and extra context is required +/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry) +/datum/proc/dump_harddel_info() + return + +///images are pretty generic, this should help a bit with tracking harddels related to them +/image/dump_harddel_info() + if(harddel_deets_dumped) + return + harddel_deets_dumped = TRUE + return "Image icon: [icon] - icon_state: [icon_state] [loc ? "loc: [loc] ([loc.x],[loc.y],[loc.z])" : ""]" diff --git a/code/datums/game_masters/_common.dm b/code/datums/game_masters/_common.dm index 84efe57c73d..142287f510c 100644 --- a/code/datums/game_masters/_common.dm +++ b/code/datums/game_masters/_common.dm @@ -1,6 +1,6 @@ -// Push button, receive event. -// Returns a selected event datum. -/datum/game_master/proc/choose_event() - -/datum/game_master/proc/log_game_master(message) +// Push button, receive event. +// Returns a selected event datum. +/datum/game_master/proc/choose_event() + +/datum/game_master/proc/log_game_master(message) // SSgame_master.log_game_master(message) // VOREStation Edit - We don't use SSgame_master yet. \ No newline at end of file diff --git a/code/datums/game_masters/default.dm b/code/datums/game_masters/default.dm index 8f69b296afd..46d39ab44ec 100644 --- a/code/datums/game_masters/default.dm +++ b/code/datums/game_masters/default.dm @@ -1,87 +1,87 @@ -// The default game master tries to choose events with these goals in mind. -// * Don't choose an event if the crew can't take it. E.g. no meteors after half of the crew died. -// * Try to involve lots of people, particuarly in active departments. -// * Avoid giving events to the same department multiple times in a row. -/datum/game_master/default - // If an event was done for a specific department, it is written here, so it doesn't do it again. - var/last_department_used = null - - -/datum/game_master/default/choose_event() - log_game_master("Now starting event decision.") - - var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) - var/list/best_events = decide_best_events(most_active_departments) - - if(LAZYLEN(best_events)) - log_game_master("Got [best_events.len] choice\s for the next event.") - var/list/weighted_events = list() - - for(var/datum/event2/meta/event as anything in best_events) - var/weight = event.get_weight() - if(weight <= 0) - continue - weighted_events[event] = weight - log_game_master("Filtered down to [weighted_events.len] choice\s.") - - var/datum/event2/meta/choice = pickweight(weighted_events) - - if(choice) - log_game_master("[choice.name] was chosen, and is now being ran.") - last_department_used = LAZYACCESS(choice.departments, 1) - return choice - -/datum/game_master/default/proc/decide_best_events(list/most_active_departments) - if(!LAZYLEN(most_active_departments)) // Server's empty? - log_game_master("Game Master failed to find any active departments.") - return list() - - var/list/best_events = list() - if(most_active_departments.len >= 2) - var/list/top_two = list(most_active_departments[1], most_active_departments[2]) - best_events = filter_events_by_departments(top_two) - - if(LAZYLEN(best_events)) // We found something for those two, let's do it. - return best_events - - // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. - best_events = filter_events_by_departments(most_active_departments[1]) - - if(LAZYLEN(best_events)) - return best_events - - // At this point we should expand our horizons. - best_events = filter_events_by_departments(list(DEPARTMENT_EVERYONE)) - - if(LAZYLEN(best_events)) - return best_events - - // Just give a random event if for some reason it still can't make up its mind. - best_events = filter_events_by_departments() - - if(LAZYLEN(best_events)) - return best_events - - log_game_master("Game Master failed to find a suitable event, something very wrong is going on.") - return list() - -// Filters the available events down to events for specific departments. -// Pass DEPARTMENT_EVERYONE if you want events that target the general population, like gravity failure. -// If no list is passed, all the events will be returned. -/datum/game_master/default/proc/filter_events_by_departments(list/departments) - . = list() - for(var/datum/event2/meta/event as anything in SSgame_master.available_events) - if(!event.enabled) - continue - if(event.chaotic_threshold && !ignore_round_chaos) - if(SSgame_master.danger > event.chaotic_threshold) - continue - // An event has to involve all of these departments to pass. - var/viable = TRUE - if(LAZYLEN(departments)) - for(var/department in departments) - if(!LAZYFIND(departments, department)) - viable = FALSE - break - if(viable) - . += event +// The default game master tries to choose events with these goals in mind. +// * Don't choose an event if the crew can't take it. E.g. no meteors after half of the crew died. +// * Try to involve lots of people, particuarly in active departments. +// * Avoid giving events to the same department multiple times in a row. +/datum/game_master/default + // If an event was done for a specific department, it is written here, so it doesn't do it again. + var/last_department_used = null + + +/datum/game_master/default/choose_event() + log_game_master("Now starting event decision.") + + var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) + var/list/best_events = decide_best_events(most_active_departments) + + if(LAZYLEN(best_events)) + log_game_master("Got [best_events.len] choice\s for the next event.") + var/list/weighted_events = list() + + for(var/datum/event2/meta/event as anything in best_events) + var/weight = event.get_weight() + if(weight <= 0) + continue + weighted_events[event] = weight + log_game_master("Filtered down to [weighted_events.len] choice\s.") + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + last_department_used = LAZYACCESS(choice.departments, 1) + return choice + +/datum/game_master/default/proc/decide_best_events(list/most_active_departments) + if(!LAZYLEN(most_active_departments)) // Server's empty? + log_game_master("Game Master failed to find any active departments.") + return list() + + var/list/best_events = list() + if(most_active_departments.len >= 2) + var/list/top_two = list(most_active_departments[1], most_active_departments[2]) + best_events = filter_events_by_departments(top_two) + + if(LAZYLEN(best_events)) // We found something for those two, let's do it. + return best_events + + // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. + best_events = filter_events_by_departments(most_active_departments[1]) + + if(LAZYLEN(best_events)) + return best_events + + // At this point we should expand our horizons. + best_events = filter_events_by_departments(list(DEPARTMENT_EVERYONE)) + + if(LAZYLEN(best_events)) + return best_events + + // Just give a random event if for some reason it still can't make up its mind. + best_events = filter_events_by_departments() + + if(LAZYLEN(best_events)) + return best_events + + log_game_master("Game Master failed to find a suitable event, something very wrong is going on.") + return list() + +// Filters the available events down to events for specific departments. +// Pass DEPARTMENT_EVERYONE if you want events that target the general population, like gravity failure. +// If no list is passed, all the events will be returned. +/datum/game_master/default/proc/filter_events_by_departments(list/departments) + . = list() + for(var/datum/event2/meta/event as anything in SSgame_master.available_events) + if(!event.enabled) + continue + if(event.chaotic_threshold && !ignore_round_chaos) + if(SSgame_master.danger > event.chaotic_threshold) + continue + // An event has to involve all of these departments to pass. + var/viable = TRUE + if(LAZYLEN(departments)) + for(var/department in departments) + if(!LAZYFIND(departments, department)) + viable = FALSE + break + if(viable) + . += event diff --git a/code/datums/game_masters/other_game_masters.dm b/code/datums/game_masters/other_game_masters.dm index cd454832510..65d0d65789c 100644 --- a/code/datums/game_masters/other_game_masters.dm +++ b/code/datums/game_masters/other_game_masters.dm @@ -1,42 +1,42 @@ -// The `classic` game master tries to act like the old system, choosing events without any specific goals. -// * Has no goals, and instead operates purely off of the weights of the events it has. -// * Does not react to danger at all. -/datum/game_master/classic/choose_event() - var/list/weighted_events = list() - for(var/datum/event2/meta/event as anything in SSgame_master.available_events) - if(!event.enabled) - continue - weighted_events[event] = event.get_weight() - - var/datum/event2/meta/choice = pickweight(weighted_events) - - if(choice) - log_game_master("[choice.name] was chosen, and is now being ran.") - return choice - - -// The `super_random` game master chooses events purely at random, ignoring weights entirely. -// * Has no goals, and instead chooses randomly, ignoring weights. -// * Does not react to danger at all. -/datum/game_master/super_random/choose_event() - return pick(SSgame_master.available_events) - - -// The `brutal` game master tries to run dangerous events frequently. -// * Chaotic events have their weights artifically boosted. -// * Ignores accumulated danger. -/datum/game_master/brutal - ignore_round_chaos = TRUE - -/datum/game_master/brutal/choose_event() - var/list/weighted_events = list() - for(var/datum/event2/meta/event as anything in SSgame_master.available_events) - if(!event.enabled) - continue - weighted_events[event] = event.get_weight() + (event.chaos * 2) - - var/datum/event2/meta/choice = pickweight(weighted_events) - - if(choice) - log_game_master("[choice.name] was chosen, and is now being ran.") - return choice +// The `classic` game master tries to act like the old system, choosing events without any specific goals. +// * Has no goals, and instead operates purely off of the weights of the events it has. +// * Does not react to danger at all. +/datum/game_master/classic/choose_event() + var/list/weighted_events = list() + for(var/datum/event2/meta/event as anything in SSgame_master.available_events) + if(!event.enabled) + continue + weighted_events[event] = event.get_weight() + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + return choice + + +// The `super_random` game master chooses events purely at random, ignoring weights entirely. +// * Has no goals, and instead chooses randomly, ignoring weights. +// * Does not react to danger at all. +/datum/game_master/super_random/choose_event() + return pick(SSgame_master.available_events) + + +// The `brutal` game master tries to run dangerous events frequently. +// * Chaotic events have their weights artifically boosted. +// * Ignores accumulated danger. +/datum/game_master/brutal + ignore_round_chaos = TRUE + +/datum/game_master/brutal/choose_event() + var/list/weighted_events = list() + for(var/datum/event2/meta/event as anything in SSgame_master.available_events) + if(!event.enabled) + continue + weighted_events[event] = event.get_weight() + (event.chaos * 2) + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + return choice diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm index e2d3dbdfd43..be38a7a5a2b 100644 --- a/code/datums/helper_datums/construction_datum.dm +++ b/code/datums/helper_datums/construction_datum.dm @@ -1,152 +1,161 @@ -#define FORWARD -1 -#define BACKWARD 1 - - -// As of August 4th, 2018, these datums are only used in Mech construction. -/datum/construction - var/list/steps - var/atom/holder - var/result - var/list/steps_desc - -/datum/construction/New(atom) - ..() - holder = atom - if(!holder) //don't want this without a holder - spawn - qdel(src) - set_desc(steps.len) - return - -/datum/construction/proc/next_step() - steps.len-- - if(!steps.len) - spawn_result() - else - set_desc(steps.len) - return - -/datum/construction/proc/action(var/obj/item/I,mob/user as mob) - return - -/datum/construction/proc/check_step(var/obj/item/I,mob/user as mob) //check last step only - var/valid_step = is_right_key(I) - if(valid_step) - if(custom_action(valid_step, I, user)) - next_step() - return 1 - return 0 - -/datum/construction/proc/is_right_key(var/obj/item/I) // returns current step num if I is of the right type. - var/list/L = steps[steps.len] - switch(L["key"]) - if(IS_SCREWDRIVER) - if(I.is_screwdriver()) - return steps.len - if(IS_CROWBAR) - if(I.is_crowbar()) - return steps.len - if(IS_WIRECUTTER) - if(I.is_wirecutter()) - return steps.len - if(IS_WRENCH) - if(I.is_wrench()) - return steps.len - - if(istype(I, L["key"])) - return steps.len - return 0 - -/datum/construction/proc/custom_action(step, I, user) - return 1 - -/datum/construction/proc/check_all_steps(var/obj/item/I,mob/user as mob) //check all steps, remove matching one. - for(var/i=1;i<=steps.len;i++) - var/list/L = steps[i]; - if(istype(I, L["key"])) - if(custom_action(i, I, user)) - steps[i]=null;//stupid byond list from list removal... - listclearnulls(steps); - if(!steps.len) - spawn_result() - return 1 - return 0 - - -/datum/construction/proc/spawn_result() - if(result) - new result(get_turf(holder)) - spawn() - qdel(holder) - return - -/datum/construction/proc/set_desc(index as num) - var/list/step = steps[index] - holder.desc = step["desc"] - return - - -// Reversible -/datum/construction/reversible - var/index - -/datum/construction/reversible/New(atom) - ..() - index = steps.len - return - -/datum/construction/reversible/proc/update_index(diff as num) - index+=diff - if(index==0) - spawn_result() - else - set_desc(index) - return - -/datum/construction/reversible/is_right_key(var/obj/item/I) // returns index step - var/list/L = steps[index] - - switch(L["key"]) - if(IS_SCREWDRIVER) - if(I.is_screwdriver()) - return FORWARD - if(IS_CROWBAR) - if(I.is_crowbar()) - return FORWARD - if(IS_WIRECUTTER) - if(I.is_wirecutter()) - return FORWARD - if(IS_WRENCH) - if(I.is_wrench()) - return FORWARD - - switch(L["backkey"]) - if(IS_SCREWDRIVER) - if(I.is_screwdriver()) - return BACKWARD - if(IS_CROWBAR) - if(I.is_crowbar()) - return BACKWARD - if(IS_WIRECUTTER) - if(I.is_wirecutter()) - return BACKWARD - if(IS_WRENCH) - if(I.is_wrench()) - return BACKWARD - - if(istype(I, L["key"])) - return FORWARD //to the first step -> forward - else if(L["backkey"] && istype(I, L["backkey"])) - return BACKWARD //to the last step -> backwards - return 0 - -/datum/construction/reversible/check_step(var/obj/item/I,mob/user as mob) - var/diff = is_right_key(I) - if(diff) - if(custom_action(index, diff, I, user)) - update_index(diff) - return 1 - return 0 - -/datum/construction/reversible/custom_action(index, diff, I, user) +#define FORWARD -1 +#define BACKWARD 1 + + +// As of August 4th, 2018, these datums are only used in Mech construction. +/datum/construction + var/list/steps + var/atom/holder + var/result + var/list/steps_desc + +/datum/construction/New(atom) + ..() + holder = atom + if(!holder) //don't want this without a holder + spawn + qdel(src) + set_desc(steps.len) + return + +/datum/construction/proc/next_step() + steps.len-- + if(!steps.len) + spawn_result() + else + set_desc(steps.len) + return + +/datum/construction/proc/action(var/obj/item/I,mob/user as mob) + return + +/datum/construction/proc/check_step(var/obj/item/I,mob/user as mob) //check last step only + var/valid_step = is_right_key(I) + if(valid_step) + if(custom_action(valid_step, I, user)) + next_step() + return 1 + return 0 + +/datum/construction/proc/is_right_key(var/obj/item/I) // returns current step num if I is of the right type. + var/list/L = steps[steps.len] + switch(L["key"]) + if(IS_SCREWDRIVER) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return steps.len + if(IS_CROWBAR) + if(I.has_tool_quality(TOOL_CROWBAR)) + return steps.len + if(IS_WIRECUTTER) + if(I.has_tool_quality(TOOL_WIRECUTTER)) + return steps.len + if(IS_WRENCH) + if(I.has_tool_quality(TOOL_WRENCH)) + return steps.len + if(IS_WELDER) + if(I.has_tool_quality(IS_WELDER)) + return steps.len + + if(istype(I, L["key"])) + return steps.len + return 0 + +/datum/construction/proc/custom_action(step, I, user) + return 1 + +/datum/construction/proc/check_all_steps(var/obj/item/I,mob/user as mob) //check all steps, remove matching one. + for(var/i=1;i<=steps.len;i++) + var/list/L = steps[i]; + if(istype(I, L["key"])) + if(custom_action(i, I, user)) + steps[i]=null;//stupid byond list from list removal... + listclearnulls(steps); + if(!steps.len) + spawn_result() + return 1 + return 0 + + +/datum/construction/proc/spawn_result() + if(result) + new result(get_turf(holder)) + spawn() + qdel(holder) + return + +/datum/construction/proc/set_desc(index as num) + var/list/step = steps[index] + holder.desc = step["desc"] + return + + +// Reversible +/datum/construction/reversible + var/index + +/datum/construction/reversible/New(atom) + ..() + index = steps.len + return + +/datum/construction/reversible/proc/update_index(diff as num) + index+=diff + if(index==0) + spawn_result() + else + set_desc(index) + return + +/datum/construction/reversible/is_right_key(var/obj/item/I) // returns index step + var/list/L = steps[index] + + switch(L["key"]) + if(IS_SCREWDRIVER) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return FORWARD + if(IS_CROWBAR) + if(I.has_tool_quality(TOOL_CROWBAR)) + return FORWARD + if(IS_WIRECUTTER) + if(I.has_tool_quality(TOOL_WIRECUTTER)) + return FORWARD + if(IS_WRENCH) + if(I.has_tool_quality(TOOL_WRENCH)) + return FORWARD + if(IS_WELDER) + if(I.has_tool_quality(IS_WELDER)) + return FORWARD + + switch(L["backkey"]) + if(IS_SCREWDRIVER) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return BACKWARD + if(IS_CROWBAR) + if(I.has_tool_quality(TOOL_CROWBAR)) + return BACKWARD + if(IS_WIRECUTTER) + if(I.has_tool_quality(TOOL_WIRECUTTER)) + return BACKWARD + if(IS_WRENCH) + if(I.has_tool_quality(TOOL_WRENCH)) + return BACKWARD + if(IS_WELDER) + if(I.has_tool_quality(IS_WELDER)) + return BACKWARD + + if(istype(I, L["key"])) + return FORWARD //to the first step -> forward + else if(L["backkey"] && istype(I, L["backkey"])) + return BACKWARD //to the last step -> backwards + return 0 + +/datum/construction/reversible/check_step(var/obj/item/I,mob/user as mob) + var/diff = is_right_key(I) + if(diff) + if(custom_action(index, diff, I, user)) + update_index(diff) + return 1 + return 0 + +/datum/construction/reversible/custom_action(index, diff, I, user) return 1 \ No newline at end of file diff --git a/code/datums/helper_datums/events.dm b/code/datums/helper_datums/events.dm index bc3661470e6..64a17ab0a0d 100644 --- a/code/datums/helper_datums/events.dm +++ b/code/datums/helper_datums/events.dm @@ -1,67 +1,67 @@ -/* - * WARRANTY VOID IF CODE USED - */ - - -/datum/events - var/list/events - -/datum/events/New() - ..() - events = new - -/datum/events/proc/addEventType(event_type as text) - if(!(event_type in events) || !islist(events[event_type])) - events[event_type] = list() - return 1 - return - - -// Arguments: event_type as text, proc_holder as datum, proc_name as text -// Returns: New event, null on error. -/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) - if(!event_type || !proc_holder || !proc_name) - return - addEventType(event_type) - var/list/event = events[event_type] - var/datum/event/E = new /datum/event(proc_holder,proc_name) - event += E - return E - -// Arguments: event_type as text, any number of additional arguments to pass to event handler -// Returns: null -/datum/events/proc/fireEvent() - //to_world("Events in [args[1]] called") - var/list/event = listgetindex(events,args[1]) - if(istype(event)) - spawn(-1) - for(var/datum/event/E in event) - if(!E.Fire(arglist(args.Copy(2)))) - clearEvent(args[1],E) - return - -// Arguments: event_type as text, E as /datum/event -// Returns: 1 if event cleared, null on error -/datum/events/proc/clearEvent(event_type as text, datum/event/E) - if(!event_type || !E) - return - var/list/event = listgetindex(events,event_type) - event -= E - return 1 - - -/datum/event - var/listener - var/proc_name - -/datum/event/New(tlistener,tprocname) - listener = tlistener - proc_name = tprocname - return ..() - -/datum/event/proc/Fire() - //to_world("Event fired") - if(listener) - call(listener,proc_name)(arglist(args)) - return 1 +/* + * WARRANTY VOID IF CODE USED + */ + + +/datum/events + var/list/events + +/datum/events/New() + ..() + events = new + +/datum/events/proc/addEventType(event_type as text) + if(!(event_type in events) || !islist(events[event_type])) + events[event_type] = list() + return 1 + return + + +// Arguments: event_type as text, proc_holder as datum, proc_name as text +// Returns: New event, null on error. +/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) + if(!event_type || !proc_holder || !proc_name) + return + addEventType(event_type) + var/list/event = events[event_type] + var/datum/event/E = new /datum/event(proc_holder,proc_name) + event += E + return E + +// Arguments: event_type as text, any number of additional arguments to pass to event handler +// Returns: null +/datum/events/proc/fireEvent() + //to_world("Events in [args[1]] called") + var/list/event = listgetindex(events,args[1]) + if(istype(event)) + spawn(-1) + for(var/datum/event/E in event) + if(!E.Fire(arglist(args.Copy(2)))) + clearEvent(args[1],E) + return + +// Arguments: event_type as text, E as /datum/event +// Returns: 1 if event cleared, null on error +/datum/events/proc/clearEvent(event_type as text, datum/event/E) + if(!event_type || !E) + return + var/list/event = listgetindex(events,event_type) + event -= E + return 1 + + +/datum/event + var/listener + var/proc_name + +/datum/event/New(tlistener,tprocname) + listener = tlistener + proc_name = tprocname + return ..() + +/datum/event/proc/Fire() + //to_world("Event fired") + if(listener) + call(listener,proc_name)(arglist(args)) + return 1 return \ No newline at end of file diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index 7312aa77dc9..8e2275a590e 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -1,84 +1,84 @@ -GLOBAL_DATUM(revdata, /datum/getrev) - -/datum/getrev - var/branch - var/revision - var/date - var/showinfo - var/list/testmerge = list() - -/datum/getrev/New() - if(world.TgsAvailable()) // Try TGS maybe - testmerge = world.TgsTestMerges() - var/datum/tgs_revision_information/REV = world.TgsRevision() - if(REV) - revision = REV.origin_commit || REV.commit - branch = "-Using TGS-" // TGS doesn't provide branch info yet - date = "-Using TGS-" // Or date - - if(!revision) // File parse method - var/list/head_branch = file2list(".git/HEAD", "\n") - if(head_branch.len) - branch = copytext(head_branch[1], 17) - - var/list/head_log = file2list(".git/logs/HEAD", "\n") - for(var/line=head_log.len, line>=1, line--) - if(head_log[line]) - var/list/last_entry = splittext(head_log[line], " ") - if(last_entry.len < 2) continue - revision = last_entry[2] - // Get date/time - if(last_entry.len >= 5) - var/unix_time = text2num(last_entry[5]) - if(unix_time) - date = unix2date(unix_time) - break - - to_world_log("-Revision Info-") - to_world_log("Branch: [branch]") - to_world_log("Date: [date]") - to_world_log("Revision: [revision]") - -/datum/getrev/proc/GetTestMergeInfo(header = TRUE) - . = list() - if(!testmerge.len) - return - if(header) - . += "The following pull requests are currently test merged:" - for(var/datum/tgs_revision_information/test_merge/tm as anything in testmerge) - var/cm = tm.head_commit - var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11)) - if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder)) - continue - . += "#[tm.number][details]" - -/client/verb/showrevinfo() - set category = "OOC" - set name = "Show Server Revision" - set desc = "Check the current server code revision" - - if(!GLOB.revdata) - to_chat(src, "Please wait until server initializations are complete.") - return - - var/list/msg = list() - - if(GLOB.revdata.revision) - msg += "Server revision: B:[GLOB.revdata.branch] D:[GLOB.revdata.date]" - if(config.githuburl) - msg += "Commit: [GLOB.revdata.revision]" - else - msg += "Commit: GLOB.revdata.revision" - else - msg += "Server revision: Unknown" - - if(world.TgsAvailable()) - var/datum/tgs_version/version = world.TgsVersion() - msg += "TGS version: [version.raw_parameter]" - var/datum/tgs_version/api_version = world.TgsApiVersion() - msg += "DMAPI version: [api_version.raw_parameter]" - - if(GLOB.revdata.testmerge.len) - msg += GLOB.revdata.GetTestMergeInfo() - - to_chat(src, msg.Join("
    ")) +GLOBAL_DATUM(revdata, /datum/getrev) + +/datum/getrev + var/branch + var/revision + var/date + var/showinfo + var/list/testmerge = list() + +/datum/getrev/New() + if(world.TgsAvailable()) // Try TGS maybe + testmerge = world.TgsTestMerges() + var/datum/tgs_revision_information/REV = world.TgsRevision() + if(REV) + revision = REV.origin_commit || REV.commit + branch = "-Using TGS-" // TGS doesn't provide branch info yet + date = "-Using TGS-" // Or date + + if(!revision) // File parse method + var/list/head_branch = file2list(".git/HEAD", "\n") + if(head_branch.len) + branch = copytext(head_branch[1], 17) + + var/list/head_log = file2list(".git/logs/HEAD", "\n") + for(var/line=head_log.len, line>=1, line--) + if(head_log[line]) + var/list/last_entry = splittext(head_log[line], " ") + if(last_entry.len < 2) continue + revision = last_entry[2] + // Get date/time + if(last_entry.len >= 5) + var/unix_time = text2num(last_entry[5]) + if(unix_time) + date = unix2date(unix_time) + break + + to_world_log("-Revision Info-") + to_world_log("Branch: [branch]") + to_world_log("Date: [date]") + to_world_log("Revision: [revision]") + +/datum/getrev/proc/GetTestMergeInfo(header = TRUE) + . = list() + if(!testmerge.len) + return + if(header) + . += "The following pull requests are currently test merged:" + for(var/datum/tgs_revision_information/test_merge/tm as anything in testmerge) + var/cm = tm.head_commit + var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11)) + if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder)) + continue + . += "#[tm.number][details]" + +/client/verb/showrevinfo() + set category = "OOC" + set name = "Show Server Revision" + set desc = "Check the current server code revision" + + if(!GLOB.revdata) + to_chat(src, "Please wait until server initializations are complete.") + return + + var/list/msg = list() + + if(GLOB.revdata.revision) + msg += "Server revision: B:[GLOB.revdata.branch] D:[GLOB.revdata.date]" + if(config.githuburl) + msg += "Commit: [GLOB.revdata.revision]" + else + msg += "Commit: GLOB.revdata.revision" + else + msg += "Server revision: Unknown" + + if(world.TgsAvailable()) + var/datum/tgs_version/version = world.TgsVersion() + msg += "TGS version: [version.raw_parameter]" + var/datum/tgs_version/api_version = world.TgsApiVersion() + msg += "DMAPI version: [api_version.raw_parameter]" + + if(GLOB.revdata.testmerge.len) + msg += GLOB.revdata.GetTestMergeInfo() + + to_chat(src, msg.Join("
    ")) diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 194341a106c..f79ad0f7286 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -1,234 +1,234 @@ -var/bluespace_item_types = newlist(/obj/item/weapon/storage/backpack/holding, -/obj/item/weapon/storage/bag/trash/holding, -/obj/item/weapon/storage/pouch/holding, -/obj/item/weapon/storage/belt/utility/holding, -/obj/item/weapon/storage/belt/medical/holding -) - -//wrapper -/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit - new /datum/teleport/instant/science(arglist(args)) - return - -/datum/teleport - var/atom/movable/teleatom //atom to teleport - var/atom/destination //destination to teleport to - var/precision = 0 //teleport precision - var/datum/effect/effect/system/effectin //effect to show right before teleportation - var/datum/effect/effect/system/effectout //effect to show right after teleportation - var/soundin //soundfile to play before teleportation - var/soundout //soundfile to play after teleportation - var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) - var/local = TRUE //VOREStation Add - If false, can teleport from/to any z-level - - -/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit - ..() - if(!initTeleport(arglist(args))) - return 0 - return 1 - -/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local) //VOREStation Edit - if(!setTeleatom(ateleatom)) - return 0 - if(!setDestination(adestination)) - return 0 - if(!setPrecision(aprecision)) - return 0 - setEffects(aeffectin,aeffectout) - setForceTeleport(afteleport) - setSounds(asoundin,asoundout) - src.local = local // VOREStation Add - return 1 - -//must succeed -/datum/teleport/proc/setPrecision(aprecision) - if(isnum(aprecision)) - precision = aprecision - return 1 - return 0 - -//must succeed -/datum/teleport/proc/setDestination(atom/adestination) - if(istype(adestination)) - destination = adestination - return 1 - return 0 - -//must succeed in most cases -/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) - if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) - qdel(ateleatom) - return 0 - if(istype(ateleatom)) - teleatom = ateleatom - return 1 - return 0 - -//custom effects must be properly set up first for instant-type teleports -//optional -/datum/teleport/proc/setEffects(datum/effect/effect/system/aeffectin=null,datum/effect/effect/system/aeffectout=null) - effectin = istype(aeffectin) ? aeffectin : null - effectout = istype(aeffectout) ? aeffectout : null - return 1 - -//optional -/datum/teleport/proc/setForceTeleport(afteleport) - force_teleport = afteleport - return 1 - -//optional -/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) - soundin = isfile(asoundin) ? asoundin : null - soundout = isfile(asoundout) ? asoundout : null - return 1 - -//placeholder -/datum/teleport/proc/teleportChecks() - return 1 - -/datum/teleport/proc/playSpecials(atom/location,datum/effect/effect/system/effect,sound) - if(location) - if(effect) - spawn(-1) - src = null - effect.attach(location) - effect.start() - if(sound) - spawn(-1) - src = null - playsound(location,sound,60,1) - return - -//do the monkey dance -/datum/teleport/proc/doTeleport() - - var/turf/destturf - var/turf/curturf = get_turf(teleatom) - if(precision) - var/list/posturfs = circlerangeturfs(destination,precision) - destturf = safepick(posturfs) - else - destturf = get_turf(destination) - - if(!destturf || !curturf) - return 0 - - playSpecials(curturf,effectin,soundin) - - var/obj/structure/bed/chair/C = null - if(isliving(teleatom)) - var/mob/living/L = teleatom - if(L.buckled) - C = L.buckled - if(attempt_vr(src,"try_televore",args)) return //VOREStation Edit - Telenoms. - if(force_teleport) - teleatom.forceMove(destturf) - playSpecials(destturf,effectout,soundout) - else - if(teleatom.Move(destturf)) - playSpecials(destturf,effectout,soundout) - if(C) - C.forceMove(destturf) - - return 1 - -/datum/teleport/proc/teleport() - if(teleportChecks()) - return doTeleport() - return 0 - -/datum/teleport/instant //teleports when datum is created - -/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - if(..()) - teleport() - return - - -/datum/teleport/instant/science/setEffects(datum/effect/effect/system/aeffectin,datum/effect/effect/system/aeffectout) - if(!aeffectin || !aeffectout) - var/datum/effect/effect/system/spark_spread/aeffect = new - aeffect.set_up(5, 1, teleatom) - effectin = effectin || aeffect - effectout = effectout || aeffect - return 1 - else - return ..() - -/datum/teleport/instant/science/setPrecision(aprecision) - ..() - - var/list/bluespace_things = newlist() - - for (var/item in bluespace_item_types) - if (istype(teleatom, item)) - precision = rand(1, 100) - bluespace_things |= teleatom.search_contents_for(item) - - //VOREStation Addition Start: Prevent taurriding abuse - if(istype(teleatom, /mob/living)) - var/mob/living/L = teleatom - if(LAZYLEN(L.buckled_mobs)) - for(var/mob/rider in L.buckled_mobs) - for (var/item in bluespace_item_types) - bluespace_things |= rider.search_contents_for(item) - //VOREStation Addition End: Prevent taurriding abuse - - if(bluespace_things.len) - precision = max(rand(1,100)*bluespace_things.len,100) - if(istype(teleatom, /mob/living)) - var/mob/living/MM = teleatom - to_chat(MM, "The Bluespace interface on your [teleatom] interferes with the teleport!") - return 1 - -/datum/teleport/instant/science/teleportChecks() - if(istype(teleatom, /obj/item/weapon/disk/nuclear)) // Don't let nuke disks get teleported --NeoFite - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - - if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/disk/nuclear))) - if(istype(teleatom, /mob/living)) - var/mob/living/MM = teleatom - MM.visible_message("\The [MM] bounces off of the portal!","Something you are carrying seems to be unable to pass through the portal. Better drop it if you want to go through.") - else - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - /* VOREStation Removal - if(destination.z in using_map.admin_levels) //CentCom z-level - if(istype(teleatom, /obj/mecha)) - var/obj/mecha/MM = teleatom - to_chat(MM.occupant, "\The [MM] would not survive the jump to a location so far away!") - return 0 - if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding))) - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - */ //VOREStation Removal End - //VOREStation Edit Start - var/obstructed = 0 - var/turf/dest_turf = get_turf(destination) - if(local && !(dest_turf.z in using_map.player_levels)) - if(istype(teleatom, /mob/living)) - to_chat(teleatom, "The portal refuses to carry you that far away!") - return 0 - else if(istype(destination.loc, /obj/belly)) - var/obj/belly/destination_belly = destination.loc - var/mob/living/telenommer = destination_belly.owner - if(istype(telenommer)) - if(istype(teleatom, /obj/machinery) || istype(teleatom, /obj/structure)) - return 0 - else if(!isliving(teleatom)) - return 1 - else - var/mob/living/telemob = teleatom - if(telemob.can_be_drop_prey && telenommer.can_be_drop_pred) - return 1 - obstructed = 1 - else if(!((isturf(destination) && !destination.density) || (isturf(destination.loc) && !destination.loc.density)) || !destination.x || !destination.y || !destination.z) //If we're inside something or outside universe - obstructed = 1 - to_chat(teleatom, "Something is blocking way on the other side!") - if(obstructed) - return 0 - else - return 1 +var/bluespace_item_types = newlist(/obj/item/weapon/storage/backpack/holding, +/obj/item/weapon/storage/bag/trash/holding, +/obj/item/weapon/storage/pouch/holding, +/obj/item/weapon/storage/belt/utility/holding, +/obj/item/weapon/storage/belt/medical/holding +) + +//wrapper +/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit + new /datum/teleport/instant/science(arglist(args)) + return + +/datum/teleport + var/atom/movable/teleatom //atom to teleport + var/atom/destination //destination to teleport to + var/precision = 0 //teleport precision + var/datum/effect/effect/system/effectin //effect to show right before teleportation + var/datum/effect/effect/system/effectout //effect to show right after teleportation + var/soundin //soundfile to play before teleportation + var/soundout //soundfile to play after teleportation + var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) + var/local = TRUE //VOREStation Add - If false, can teleport from/to any z-level + + +/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, local=TRUE) //VOREStation Edit + ..() + if(!initTeleport(arglist(args))) + return 0 + return 1 + +/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout,local) //VOREStation Edit + if(!setTeleatom(ateleatom)) + return 0 + if(!setDestination(adestination)) + return 0 + if(!setPrecision(aprecision)) + return 0 + setEffects(aeffectin,aeffectout) + setForceTeleport(afteleport) + setSounds(asoundin,asoundout) + src.local = local // VOREStation Add + return 1 + +//must succeed +/datum/teleport/proc/setPrecision(aprecision) + if(isnum(aprecision)) + precision = aprecision + return 1 + return 0 + +//must succeed +/datum/teleport/proc/setDestination(atom/adestination) + if(istype(adestination)) + destination = adestination + return 1 + return 0 + +//must succeed in most cases +/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) + if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) + qdel(ateleatom) + return 0 + if(istype(ateleatom)) + teleatom = ateleatom + return 1 + return 0 + +//custom effects must be properly set up first for instant-type teleports +//optional +/datum/teleport/proc/setEffects(datum/effect/effect/system/aeffectin=null,datum/effect/effect/system/aeffectout=null) + effectin = istype(aeffectin) ? aeffectin : null + effectout = istype(aeffectout) ? aeffectout : null + return 1 + +//optional +/datum/teleport/proc/setForceTeleport(afteleport) + force_teleport = afteleport + return 1 + +//optional +/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) + soundin = isfile(asoundin) ? asoundin : null + soundout = isfile(asoundout) ? asoundout : null + return 1 + +//placeholder +/datum/teleport/proc/teleportChecks() + return 1 + +/datum/teleport/proc/playSpecials(atom/location,datum/effect/effect/system/effect,sound) + if(location) + if(effect) + spawn(-1) + src = null + effect.attach(location) + effect.start() + if(sound) + spawn(-1) + src = null + playsound(location,sound,60,1) + return + +//do the monkey dance +/datum/teleport/proc/doTeleport() + + var/turf/destturf + var/turf/curturf = get_turf(teleatom) + if(precision) + var/list/posturfs = circlerangeturfs(destination,precision) + destturf = safepick(posturfs) + else + destturf = get_turf(destination) + + if(!destturf || !curturf) + return 0 + + playSpecials(curturf,effectin,soundin) + + var/obj/structure/bed/chair/C = null + if(isliving(teleatom)) + var/mob/living/L = teleatom + if(L.buckled) + C = L.buckled + if(attempt_vr(src,"try_televore",args)) return //VOREStation Edit - Telenoms. + if(force_teleport) + teleatom.forceMove(destturf) + playSpecials(destturf,effectout,soundout) + else + if(teleatom.Move(destturf)) + playSpecials(destturf,effectout,soundout) + if(C) + C.forceMove(destturf) + + return 1 + +/datum/teleport/proc/teleport() + if(teleportChecks()) + return doTeleport() + return 0 + +/datum/teleport/instant //teleports when datum is created + +/datum/teleport/instant/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + if(..()) + teleport() + return + + +/datum/teleport/instant/science/setEffects(datum/effect/effect/system/aeffectin,datum/effect/effect/system/aeffectout) + if(!aeffectin || !aeffectout) + var/datum/effect/effect/system/spark_spread/aeffect = new + aeffect.set_up(5, 1, teleatom) + effectin = effectin || aeffect + effectout = effectout || aeffect + return 1 + else + return ..() + +/datum/teleport/instant/science/setPrecision(aprecision) + ..() + + var/list/bluespace_things = newlist() + + for (var/item in bluespace_item_types) + if (istype(teleatom, item)) + precision = rand(1, 100) + bluespace_things |= teleatom.search_contents_for(item) + + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(teleatom, /mob/living)) + var/mob/living/L = teleatom + if(LAZYLEN(L.buckled_mobs)) + for(var/mob/rider in L.buckled_mobs) + for (var/item in bluespace_item_types) + bluespace_things |= rider.search_contents_for(item) + //VOREStation Addition End: Prevent taurriding abuse + + if(bluespace_things.len) + precision = max(rand(1,100)*bluespace_things.len,100) + if(istype(teleatom, /mob/living)) + var/mob/living/MM = teleatom + to_chat(MM, "The Bluespace interface on your [teleatom] interferes with the teleport!") + return 1 + +/datum/teleport/instant/science/teleportChecks() + if(istype(teleatom, /obj/item/weapon/disk/nuclear)) // Don't let nuke disks get teleported --NeoFite + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + + if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/disk/nuclear))) + if(istype(teleatom, /mob/living)) + var/mob/living/MM = teleatom + MM.visible_message("\The [MM] bounces off of the portal!","Something you are carrying seems to be unable to pass through the portal. Better drop it if you want to go through.") + else + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + /* VOREStation Removal + if(destination.z in using_map.admin_levels) //CentCom z-level + if(istype(teleatom, /obj/mecha)) + var/obj/mecha/MM = teleatom + to_chat(MM.occupant, "\The [MM] would not survive the jump to a location so far away!") + return 0 + if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding))) + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + */ //VOREStation Removal End + //VOREStation Edit Start + var/obstructed = 0 + var/turf/dest_turf = get_turf(destination) + if(local && !(dest_turf.z in using_map.player_levels)) + if(istype(teleatom, /mob/living)) + to_chat(teleatom, "The portal refuses to carry you that far away!") + return 0 + else if(istype(destination.loc, /obj/belly)) + var/obj/belly/destination_belly = destination.loc + var/mob/living/telenommer = destination_belly.owner + if(istype(telenommer)) + if(istype(teleatom, /obj/machinery) || istype(teleatom, /obj/structure)) + return 0 + else if(!isliving(teleatom)) + return 1 + else + var/mob/living/telemob = teleatom + if(telemob.can_be_drop_prey && telenommer.can_be_drop_pred) + return 1 + obstructed = 1 + else if(!((isturf(destination) && !destination.density) || (isturf(destination.loc) && !destination.loc.density)) || !destination.x || !destination.y || !destination.z) //If we're inside something or outside universe + obstructed = 1 + to_chat(teleatom, "Something is blocking way on the other side!") + if(obstructed) + return 0 + else + return 1 //VOREStation Edit End \ No newline at end of file diff --git a/code/datums/helper_datums/topic_input.dm b/code/datums/helper_datums/topic_input.dm index 809a6c4bc5c..bf073402255 100644 --- a/code/datums/helper_datums/topic_input.dm +++ b/code/datums/helper_datums/topic_input.dm @@ -1,60 +1,60 @@ -/datum/topic_input - var/href - var/list/href_list - -/datum/topic_input/New(thref,list/thref_list) - href = thref - href_list = thref_list.Copy() - return - -/datum/topic_input/proc/get(i) - return listgetindex(href_list,i) - -/datum/topic_input/proc/getAndLocate(i) - var/t = get(i) - if(t) - t = locate(t) - return t || null - -/datum/topic_input/proc/getNum(i) - var/t = get(i) - if(t) - t = text2num(t) - return isnum(t) ? t : null - -/datum/topic_input/proc/getObj(i) - var/t = getAndLocate(i) - return isobj(t) ? t : null - -/datum/topic_input/proc/getMob(i) - var/t = getAndLocate(i) - return ismob(t) ? t : null - -/datum/topic_input/proc/getTurf(i) - var/t = getAndLocate(i) - return isturf(t) ? t : null - -/datum/topic_input/proc/getAtom(i) - return getType(i,/atom) - -/datum/topic_input/proc/getArea(i) - var/t = getAndLocate(i) - return isarea(t) ? t : null - -/datum/topic_input/proc/getStr(i)//params should always be text, but... - var/t = get(i) - return istext(t) ? t : null - -/datum/topic_input/proc/getType(i,type) - var/t = getAndLocate(i) - return istype(t,type) ? t : null - -/datum/topic_input/proc/getPath(i) - var/t = get(i) - if(t) - t = text2path(t) - return ispath(t) ? t : null - -/datum/topic_input/proc/getList(i) - var/t = getAndLocate(i) +/datum/topic_input + var/href + var/list/href_list + +/datum/topic_input/New(thref,list/thref_list) + href = thref + href_list = thref_list.Copy() + return + +/datum/topic_input/proc/get(i) + return listgetindex(href_list,i) + +/datum/topic_input/proc/getAndLocate(i) + var/t = get(i) + if(t) + t = locate(t) + return t || null + +/datum/topic_input/proc/getNum(i) + var/t = get(i) + if(t) + t = text2num(t) + return isnum(t) ? t : null + +/datum/topic_input/proc/getObj(i) + var/t = getAndLocate(i) + return isobj(t) ? t : null + +/datum/topic_input/proc/getMob(i) + var/t = getAndLocate(i) + return ismob(t) ? t : null + +/datum/topic_input/proc/getTurf(i) + var/t = getAndLocate(i) + return isturf(t) ? t : null + +/datum/topic_input/proc/getAtom(i) + return getType(i,/atom) + +/datum/topic_input/proc/getArea(i) + var/t = getAndLocate(i) + return isarea(t) ? t : null + +/datum/topic_input/proc/getStr(i)//params should always be text, but... + var/t = get(i) + return istext(t) ? t : null + +/datum/topic_input/proc/getType(i,type) + var/t = getAndLocate(i) + return istype(t,type) ? t : null + +/datum/topic_input/proc/getPath(i) + var/t = get(i) + if(t) + t = text2path(t) + return ispath(t) ? t : null + +/datum/topic_input/proc/getList(i) + var/t = getAndLocate(i) return islist(t) ? t : null \ No newline at end of file diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index e9397825298..078db317d5e 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -1,126 +1,126 @@ -/* - output_atoms (list of atoms) The destination(s) for the sounds - - mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end. - mid_length (num) The length to wait between playing mid_sounds - - start_sound (soundfile) Played before starting the mid_sounds loop - start_length (num) How long to wait before starting the main loop after playing start_sound - - end_sound (soundfile) The sound played after the main loop has concluded - - chance (num) Chance per loop to play a mid_sound - volume (num) Sound output volume - muted (bool) Private. Used to stop the sound loop. - max_loops (num) The max amount of loops to run for. - direct (bool) If true plays directly to provided atoms instead of from them - opacity_check (bool) If true, things behind walls/opaque things won't hear the sounds. - pref_check (type) If set to a /datum/client_preference type, will check if the hearer has that preference active before playing it to them. - exclusive (bool) If true, only one of this sound is allowed to play. Relies on if started is true or not. If true, it will not start another loop until it is false. -*/ -/datum/looping_sound - var/list/atom/output_atoms - var/mid_sounds - var/mid_length - var/start_sound - var/start_length - var/end_sound - var/chance - var/volume = 100 - var/max_loops - var/direct - var/vary - var/extra_range - var/opacity_check - var/pref_check - var/exclusive - - var/timerid - var/started - -/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, disable_direct=FALSE) - if(!mid_sounds) - WARNING("A looping sound datum was created without sounds to play.") - return - - output_atoms = _output_atoms - if(disable_direct) - direct = FALSE - - if(start_immediately) - start() - -/datum/looping_sound/Destroy() - stop() - output_atoms = null - return ..() - -/datum/looping_sound/proc/start(atom/add_thing, skip_start_sound = FALSE) - if(add_thing) - output_atoms |= add_thing - if(timerid) - return - if(skip_start_sound && (!exclusive && !started)) // Skip start sounds optionally, check if we're exclusive AND started already - sound_loop() - started = TRUE - return - if(exclusive && started) // Prevents a sound from starting multiple times - return // Don't start this loop. - on_start() - started = TRUE - -/datum/looping_sound/proc/stop(atom/remove_thing, skip_stop_sound = FALSE) - if(remove_thing) - output_atoms -= remove_thing - if(!timerid) - return - if(!skip_stop_sound) - on_stop() - deltimer(timerid) - timerid = null - started = FALSE - -/datum/looping_sound/proc/sound_loop(starttime) - if(max_loops && world.time >= starttime + mid_length * max_loops) - stop() - return - if(!chance || prob(chance)) - play(get_sound(starttime)) - if(!timerid) - timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_STOPPABLE | TIMER_LOOP) - -/datum/looping_sound/proc/play(soundfile) - var/list/atoms_cache = output_atoms - var/sound/S = sound(soundfile) - if(direct) - S.channel = SSsounds.random_available_channel() - S.volume = volume - for(var/i in 1 to atoms_cache.len) - var/atom/thing = atoms_cache[i] - if(direct) - if(ismob(thing)) - var/mob/M = thing - if(pref_check && !M.is_preference_enabled(pref_check)) - continue - SEND_SOUND(thing, S) - else - playsound(thing, S, volume, vary, extra_range, ignore_walls = !opacity_check, preference = pref_check) - -/datum/looping_sound/proc/get_sound(starttime, _mid_sounds) - if(!_mid_sounds) - . = mid_sounds - else - . = _mid_sounds - while(!isfile(.) && !isnull(.)) - . = pickweight(.) - -/datum/looping_sound/proc/on_start() - var/start_wait = 1 // On TG this is 0, however it needs to be 1 to work around an issue. - if(start_sound) - play(start_sound) - start_wait = start_length - addtimer(CALLBACK(src, PROC_REF(sound_loop)), start_wait) - -/datum/looping_sound/proc/on_stop() - if(end_sound) - play(end_sound) +/* + output_atoms (list of atoms) The destination(s) for the sounds + + mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end. + mid_length (num) The length to wait between playing mid_sounds + + start_sound (soundfile) Played before starting the mid_sounds loop + start_length (num) How long to wait before starting the main loop after playing start_sound + + end_sound (soundfile) The sound played after the main loop has concluded + + chance (num) Chance per loop to play a mid_sound + volume (num) Sound output volume + muted (bool) Private. Used to stop the sound loop. + max_loops (num) The max amount of loops to run for. + direct (bool) If true plays directly to provided atoms instead of from them + opacity_check (bool) If true, things behind walls/opaque things won't hear the sounds. + pref_check (type) If set to a /datum/client_preference type, will check if the hearer has that preference active before playing it to them. + exclusive (bool) If true, only one of this sound is allowed to play. Relies on if started is true or not. If true, it will not start another loop until it is false. +*/ +/datum/looping_sound + var/list/atom/output_atoms + var/mid_sounds + var/mid_length + var/start_sound + var/start_length + var/end_sound + var/chance + var/volume = 100 + var/max_loops + var/direct + var/vary + var/extra_range + var/opacity_check + var/pref_check + var/exclusive + + var/timerid + var/started + +/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, disable_direct=FALSE) + if(!mid_sounds) + WARNING("A looping sound datum was created without sounds to play.") + return + + output_atoms = _output_atoms + if(disable_direct) + direct = FALSE + + if(start_immediately) + start() + +/datum/looping_sound/Destroy() + stop() + output_atoms = null + return ..() + +/datum/looping_sound/proc/start(atom/add_thing, skip_start_sound = FALSE) + if(add_thing) + output_atoms |= add_thing + if(timerid) + return + if(skip_start_sound && (!exclusive && !started)) // Skip start sounds optionally, check if we're exclusive AND started already + sound_loop() + started = TRUE + return + if(exclusive && started) // Prevents a sound from starting multiple times + return // Don't start this loop. + on_start() + started = TRUE + +/datum/looping_sound/proc/stop(atom/remove_thing, skip_stop_sound = FALSE) + if(remove_thing) + output_atoms -= remove_thing + if(!timerid) + return + if(!skip_stop_sound) + on_stop() + deltimer(timerid) + timerid = null + started = FALSE + +/datum/looping_sound/proc/sound_loop(starttime) + if(max_loops && world.time >= starttime + mid_length * max_loops) + stop() + return + if(!chance || prob(chance)) + play(get_sound(starttime)) + if(!timerid) + timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_STOPPABLE | TIMER_LOOP) + +/datum/looping_sound/proc/play(soundfile) + var/list/atoms_cache = output_atoms + var/sound/S = sound(soundfile) + if(direct) + S.channel = SSsounds.random_available_channel() + S.volume = volume + for(var/i in 1 to atoms_cache.len) + var/atom/thing = atoms_cache[i] + if(direct) + if(ismob(thing)) + var/mob/M = thing + if(pref_check && !M.is_preference_enabled(pref_check)) + continue + SEND_SOUND(thing, S) + else + playsound(thing, S, volume, vary, extra_range, ignore_walls = !opacity_check, preference = pref_check) + +/datum/looping_sound/proc/get_sound(starttime, _mid_sounds) + if(!_mid_sounds) + . = mid_sounds + else + . = _mid_sounds + while(!isfile(.) && !isnull(.)) + . = pickweight(.) + +/datum/looping_sound/proc/on_start() + var/start_wait = 1 // On TG this is 0, however it needs to be 1 to work around an issue. + if(start_sound) + play(start_sound) + start_wait = start_length + addtimer(CALLBACK(src, PROC_REF(sound_loop)), start_wait) + +/datum/looping_sound/proc/on_stop() + if(end_sound) + play(end_sound) diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm index 855f4c52130..be43a0b2594 100644 --- a/code/datums/looping_sounds/item_sounds.dm +++ b/code/datums/looping_sounds/item_sounds.dm @@ -1,45 +1,45 @@ -/datum/looping_sound/geiger - mid_sounds = list( - list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1), - list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1), - list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1), - list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1) - ) - mid_length = 1 SECOND - volume = 25 - var/last_radiation - -/datum/looping_sound/geiger/get_sound(starttime) - var/danger - switch(last_radiation) - if(0 to RAD_LEVEL_MODERATE) - danger = 1 - if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH) - danger = 2 - if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH) - danger = 3 - if(RAD_LEVEL_VERY_HIGH to INFINITY) - danger = 4 - else - return null - return ..(starttime, mid_sounds[danger]) - -/datum/looping_sound/geiger/stop() - . = ..() - last_radiation = 0 - -/datum/looping_sound/small_motor - start_sound = 'sound/items/small_motor/motor_start_nopull.ogg' - start_length = 2 SECONDS - mid_sounds = list( - 'sound/items/small_motor/motor_idle.ogg', - 'sound/items/small_motor/motor_fast.ogg', - 'sound/items/small_motor/motor_faster.ogg' - ) - mid_length = 1.9 SECONDS //someone make this loop better please, i'm no good at sound. the clips should be 2 seconds exact but there's a gap if it's set to 2 - end_sound = 'sound/items/small_motor/motor_end.ogg' - var/speed = 1 - -/datum/looping_sound/small_motor/get_sound(starttime) - speed = clamp(speed, 1, 3) +/datum/looping_sound/geiger + mid_sounds = list( + list('sound/items/geiger/low1.ogg'=1, 'sound/items/geiger/low2.ogg'=1, 'sound/items/geiger/low3.ogg'=1, 'sound/items/geiger/low4.ogg'=1), + list('sound/items/geiger/med1.ogg'=1, 'sound/items/geiger/med2.ogg'=1, 'sound/items/geiger/med3.ogg'=1, 'sound/items/geiger/med4.ogg'=1), + list('sound/items/geiger/high1.ogg'=1, 'sound/items/geiger/high2.ogg'=1, 'sound/items/geiger/high3.ogg'=1, 'sound/items/geiger/high4.ogg'=1), + list('sound/items/geiger/ext1.ogg'=1, 'sound/items/geiger/ext2.ogg'=1, 'sound/items/geiger/ext3.ogg'=1, 'sound/items/geiger/ext4.ogg'=1) + ) + mid_length = 1 SECOND + volume = 25 + var/last_radiation + +/datum/looping_sound/geiger/get_sound(starttime) + var/danger + switch(last_radiation) + if(0 to RAD_LEVEL_MODERATE) + danger = 1 + if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH) + danger = 2 + if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH) + danger = 3 + if(RAD_LEVEL_VERY_HIGH to INFINITY) + danger = 4 + else + return null + return ..(starttime, mid_sounds[danger]) + +/datum/looping_sound/geiger/stop() + . = ..() + last_radiation = 0 + +/datum/looping_sound/small_motor + start_sound = 'sound/items/small_motor/motor_start_nopull.ogg' + start_length = 2 SECONDS + mid_sounds = list( + 'sound/items/small_motor/motor_idle.ogg', + 'sound/items/small_motor/motor_fast.ogg', + 'sound/items/small_motor/motor_faster.ogg' + ) + mid_length = 1.9 SECONDS //someone make this loop better please, i'm no good at sound. the clips should be 2 seconds exact but there's a gap if it's set to 2 + end_sound = 'sound/items/small_motor/motor_end.ogg' + var/speed = 1 + +/datum/looping_sound/small_motor/get_sound(starttime) + speed = clamp(speed, 1, 3) return ..(starttime, mid_sounds[speed]) \ No newline at end of file diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index d7011dd094a..8005d950163 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -1,117 +1,117 @@ -/datum/looping_sound/showering - start_sound = 'sound/machines/shower/shower_start.ogg' - start_length = 2 - mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1) - mid_length = 10 - end_sound = 'sound/machines/shower/shower_end.ogg' - volume = 15 - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/supermatter - mid_sounds = list('sound/machines/sm/loops/calm.ogg'=1) - mid_length = 60 - volume = 40 - extra_range = 10 - pref_check = /datum/client_preference/supermatter_hum - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/generator - start_sound = 'sound/machines/generator/generator_start.ogg' - start_length = 4 - mid_sounds = list('sound/machines/generator/generator_mid1.ogg'=1, 'sound/machines/generator/generator_mid2.ogg'=1, 'sound/machines/generator/generator_mid3.ogg'=1) - mid_length = 4 - end_sound = 'sound/machines/generator/generator_end.ogg' - volume = 40 - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - -/datum/looping_sound/deep_fryer - start_sound = 'sound/machines/kitchen/fryer/deep_fryer_immerse.ogg' //my immersions - start_length = 10 - mid_sounds = list('sound/machines/kitchen/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/kitchen/fryer/deep_fryer_2.ogg' = 1) - mid_length = 2 - end_sound = 'sound/machines/kitchen/fryer/deep_fryer_emerge.ogg' - volume = 15 - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/microwave - start_sound = 'sound/machines/kitchen/microwave/microwave-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/microwave/microwave-mid1.ogg'=10, 'sound/machines/kitchen/microwave/microwave-mid2.ogg'=1) - mid_length = 10 - end_sound = 'sound/machines/kitchen/microwave/microwave-end.ogg' - volume = 90 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/oven - start_sound = 'sound/machines/kitchen/oven/oven-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/oven/oven-mid1.ogg'=10) - mid_length = 40 - end_sound = 'sound/machines/kitchen/oven/oven-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/grill - start_sound = 'sound/machines/kitchen/grill/grill-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/grill/grill-mid1.ogg'=10) - mid_length = 40 - end_sound = 'sound/machines/kitchen/grill/grill-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/mixer - start_sound = 'sound/machines/kitchen/mixer/mixer-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/mixer/mixer-mid1.ogg'=10) - mid_length = 10 - end_sound = 'sound/machines/kitchen/mixer/mixer-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/cerealmaker - start_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/cerealmaker/cerealmaker-mid1.ogg'=10) - mid_length = 60 - end_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-stop.ogg' - volume = 50 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/candymaker - start_sound = 'sound/machines/kitchen/candymaker/candymaker-start.ogg' - start_length = 10 - mid_sounds = list('sound/machines/kitchen/candymaker/candymaker-mid1.ogg'=10) - mid_length = 40 - end_sound = 'sound/machines/kitchen/candymaker/candymaker-stop.ogg' - volume = 20 - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/datum/looping_sound/air_pump - start_sound = 'sound/machines/air_pump/airpumpstart.ogg' - start_length = 10 - mid_sounds = list('sound/machines/air_pump/airpumpidle.ogg' = 1) - mid_length = 70 - end_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' - volume = 15 - pref_check = /datum/client_preference/air_pump_noise - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/datum/looping_sound/vehicle_engine - start_sound = 'sound/machines/vehicle/engine_start.ogg' - start_length = 2 - mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) - mid_length = 6 - end_sound = 'sound/machines/vehicle/engine_end.ogg' - volume = 20 +/datum/looping_sound/showering + start_sound = 'sound/machines/shower/shower_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1) + mid_length = 10 + end_sound = 'sound/machines/shower/shower_end.ogg' + volume = 15 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/supermatter + mid_sounds = list('sound/machines/sm/loops/calm.ogg'=1) + mid_length = 60 + volume = 40 + extra_range = 10 + pref_check = /datum/client_preference/supermatter_hum + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/generator + start_sound = 'sound/machines/generator/generator_start.ogg' + start_length = 4 + mid_sounds = list('sound/machines/generator/generator_mid1.ogg'=1, 'sound/machines/generator/generator_mid2.ogg'=1, 'sound/machines/generator/generator_mid3.ogg'=1) + mid_length = 4 + end_sound = 'sound/machines/generator/generator_end.ogg' + volume = 40 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/datum/looping_sound/deep_fryer + start_sound = 'sound/machines/kitchen/fryer/deep_fryer_immerse.ogg' //my immersions + start_length = 10 + mid_sounds = list('sound/machines/kitchen/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/kitchen/fryer/deep_fryer_2.ogg' = 1) + mid_length = 2 + end_sound = 'sound/machines/kitchen/fryer/deep_fryer_emerge.ogg' + volume = 15 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/microwave + start_sound = 'sound/machines/kitchen/microwave/microwave-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/microwave/microwave-mid1.ogg'=10, 'sound/machines/kitchen/microwave/microwave-mid2.ogg'=1) + mid_length = 10 + end_sound = 'sound/machines/kitchen/microwave/microwave-end.ogg' + volume = 90 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/oven + start_sound = 'sound/machines/kitchen/oven/oven-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/oven/oven-mid1.ogg'=10) + mid_length = 40 + end_sound = 'sound/machines/kitchen/oven/oven-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/grill + start_sound = 'sound/machines/kitchen/grill/grill-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/grill/grill-mid1.ogg'=10) + mid_length = 40 + end_sound = 'sound/machines/kitchen/grill/grill-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/mixer + start_sound = 'sound/machines/kitchen/mixer/mixer-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/mixer/mixer-mid1.ogg'=10) + mid_length = 10 + end_sound = 'sound/machines/kitchen/mixer/mixer-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/cerealmaker + start_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/cerealmaker/cerealmaker-mid1.ogg'=10) + mid_length = 60 + end_sound = 'sound/machines/kitchen/cerealmaker/cerealmaker-stop.ogg' + volume = 50 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/candymaker + start_sound = 'sound/machines/kitchen/candymaker/candymaker-start.ogg' + start_length = 10 + mid_sounds = list('sound/machines/kitchen/candymaker/candymaker-mid1.ogg'=10) + mid_length = 40 + end_sound = 'sound/machines/kitchen/candymaker/candymaker-stop.ogg' + volume = 20 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/datum/looping_sound/air_pump + start_sound = 'sound/machines/air_pump/airpumpstart.ogg' + start_length = 10 + mid_sounds = list('sound/machines/air_pump/airpumpidle.ogg' = 1) + mid_length = 70 + end_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' + volume = 15 + pref_check = /datum/client_preference/air_pump_noise + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/datum/looping_sound/vehicle_engine + start_sound = 'sound/machines/vehicle/engine_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/vehicle/engine_mid.ogg'=1) + mid_length = 6 + end_sound = 'sound/machines/vehicle/engine_end.ogg' + volume = 20 diff --git a/code/datums/looping_sounds/sequence.dm b/code/datums/looping_sounds/sequence.dm index 0ad8c30b78c..f14b43184b1 100644 --- a/code/datums/looping_sounds/sequence.dm +++ b/code/datums/looping_sounds/sequence.dm @@ -1,175 +1,175 @@ -// These looping sounds work off of a sequence of things (usually letters or numbers) given to them. - -// Base sequencer type. -/datum/looping_sound/sequence - var/sequence = "The quick brown fox jumps over the lazy dog" // The string to iterate over. - var/position = 1 // Where we are inside the sequence. IE the index we're on for the above. - var/loop_sequence = TRUE // If it should loop the entire sequence upon reaching the end. Otherwise stop() is called. - var/repeat_sequnce_delay = 2 SECONDS // How long to wait when reaching the end, if the above var is true, in deciseconds. - var/next_iteration_delay = 0 - -/datum/looping_sound/sequence/vv_edit_var(var_name, var_value) - if(var_name == "sequence") - set_new_sequence(var_value) - return ..() - -/datum/looping_sound/sequence/proc/iterate_on_sequence() - var/data = get_data_from_position() - next_iteration_delay = process_data(data) - increment_position() - -/datum/looping_sound/sequence/proc/get_data_from_position() - return sequence[position] - -// Override to do something based on the input. -/datum/looping_sound/sequence/proc/process_data(input) - return - -// Changes the sequence, and sets the position back to the start. -/datum/looping_sound/sequence/proc/set_new_sequence(new_sequence) - sequence = new_sequence - reset_position() - -// Called to advance the position, and handle reaching the end if so. -/datum/looping_sound/sequence/proc/increment_position() - position++ - if(position > get_max_position()) - reached_end_of_sequence() - -/datum/looping_sound/sequence/proc/get_max_position() - return length(sequence) - -/datum/looping_sound/sequence/proc/reset_position() - position = 1 - -// Called when the sequence is finished being iterated over. -// If looping is on, the position will be reset, otherwise processing will stop. -/datum/looping_sound/sequence/proc/reached_end_of_sequence() - if(loop_sequence) - next_iteration_delay += repeat_sequnce_delay - reset_position() - else - stop() - -/datum/looping_sound/sequence/sound_loop(starttime) - iterate_on_sequence() - - timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), next_iteration_delay, TIMER_STOPPABLE) - -#define MORSE_DOT "*" // Yes this is an asterisk but its easier to see on a computer compared to a period. -#define MORSE_DASH "-" -#define MORSE_BASE_DELAY 1 // If you change this you will also need to change [dot|dash]_soundfile variables. - -// This implements an automatic conversion of text (the sequence) into audible morse code. -// This can be useful for flavor purposes. For 'real' usage its suggested to also display the sequence in text form, for the benefit of those without sound. -/datum/looping_sound/sequence/morse - // This is just to pass validation in the base type. - mid_sounds = list('sound/effects/tones/440_sine_01.ogg') - mid_length = 1 - opacity_check = TRUE // So we don't have to constantly hear it when out of sight. - - // Dots. - // In Morse Code, the dot's length is one unit. - var/dot_soundfile = 'sound/effects/tones/440_sine_01.ogg' // The sound file to play for a 'dot'. - var/dot_delay = MORSE_BASE_DELAY // How long the sound above plays for, in deciseconds. - - // Dashes. - // In Morse Code, a dash's length is equal to three units (or three dots). - var/dash_soundfile = 'sound/effects/tones/440_sine_03.ogg' // The sound file to play for a 'dash'. - var/dash_delay = MORSE_BASE_DELAY * 3 // Same as the dot delay, except for the dash sound. - - // Spaces. - // In Morse Code, a space's length is equal to one unit (or one dot). - var/spaces_between_sounds = MORSE_BASE_DELAY // How many spaces are between parts of the same letter. - var/spaces_between_letters = MORSE_BASE_DELAY * 3 // How many spaces are between different letters in the same word. - var/spaces_between_words = MORSE_BASE_DELAY * 7 // How many spaces are between different words. - - // Morse Alphabet. - // Note that it is case-insensative. 'A' and 'a' will make the same sounds. - // Unfortunately there isn't a nice way to implement procedure signs w/o the space inbetween the letters. - // Also some of the punctuation isn't super offical/widespread in real life but its the future so *shrug. - var/static/list/morse_alphabet = list( - "A" = list("*", "-"), - "B" = list("-", "*", "*", "*"), - "C" = list("-", "*", "-", "*"), - "D" = list("-", "*", "*"), - "E" = list("*"), - "F" = list("*", "*", "-", "*"), - "G" = list("-", "-", "*"), - "H" = list("*", "*", "*", "*"), - "I" = list("*", "*"), - "J" = list("*", "-", "-", "-"), - "K" = list("-", "*", "-"), - "L" = list("*", "-", "*", "*"), - "M" = list("*", "*"), - "N" = list("-", "*"), - "O" = list("-", "-", "-"), - "P" = list("*", "-", "-", "*"), - "Q" = list("-", "-", "*", "-"), - "R" = list("*", "-", "*"), - "S" = list("*", "*", "*"), - "T" = list("-"), - "U" = list("*", "*", "-"), - "V" = list("*", "*", "*", "-"), - "W" = list("*", "-", "-"), - "X" = list("-", "*", "*", "-"), - "Y" = list("-", "*", "-", "-"), - "Z" = list("-", "-", "*", "*"), - - "1" = list("*", "-", "-", "-", "-"), - "2" = list("*", "*", "-", "-", "-"), - "3" = list("*", "*", "*", "-", "-"), - "4" = list("*", "*", "*", "*", "-"), - "5" = list("*", "*", "*", "*", "*"), - "6" = list("-", "*", "*", "*", "*"), - "7" = list("-", "-", "*", "*", "*"), - "8" = list("-", "-", "-", "*", "*"), - "9" = list("-", "-", "-", "-", "*"), - "0" = list("-", "-", "-", "-", "-"), - - "." = list("*", "-", "*", "-", "*", "-"), - "," = list("-", "-", "*", "*", "-", "-"), - "?" = list("*", "*", "-", "-", "*", "*"), - "'" = list("*", "-", "-", "-", "-", "*"), - "!" = list("-", "*", "-", "*", "-", "-"), - "/" = list("-", "*", "*", "-", "*"), - "(" = list("-", "*", "-", "-", "*"), - ")" = list("-", "*", "-", "-", "*", "-"), - "&" = list("*", "-", "*", "*", "*"), - ":" = list("-", "-", "-", "*", "*", "*"), - ";" = list("-", "*", "-", "*", "-", "*"), - "=" = list("-", "*", "*", "*", "-"), - "+" = list("*", "-", "*", "-", "*"), - "-" = list("-", "*", "*", "*", "*", "-"), - "_" = list("*", "*", "-", "-", "*", "-"), - "\""= list("*", "-", "*", "*", "-", "*"), - "$" = list("*", "*", "*", "-", "*", "*", "-"), - "@" = list("*", "-", "-", "*", "-", "*"), - ) - - -/datum/looping_sound/sequence/morse/process_data(letter) - letter = uppertext(letter) // Make it case-insensative. - - // If it's whitespace, treat it as a (Morse) space. - if(letter == " ") - return spaces_between_words - - if(!(letter in morse_alphabet)) - CRASH("Encountered invalid character in Morse sequence \"[letter]\".") - - // So I heard you like sequences... - // Play a sequence of sounds while inside the current iteration of the outer sequence. - var/list/instructions = morse_alphabet[letter] - for(var/sound in instructions) - if(sound == MORSE_DOT) - play(dot_soundfile) - sleep(dot_delay) - else // It's a dash otherwise. - play(dash_soundfile) - sleep(dash_delay) - sleep(spaces_between_sounds) - return spaces_between_letters - -#undef MORSE_DOT -#undef MORSE_DASH +// These looping sounds work off of a sequence of things (usually letters or numbers) given to them. + +// Base sequencer type. +/datum/looping_sound/sequence + var/sequence = "The quick brown fox jumps over the lazy dog" // The string to iterate over. + var/position = 1 // Where we are inside the sequence. IE the index we're on for the above. + var/loop_sequence = TRUE // If it should loop the entire sequence upon reaching the end. Otherwise stop() is called. + var/repeat_sequnce_delay = 2 SECONDS // How long to wait when reaching the end, if the above var is true, in deciseconds. + var/next_iteration_delay = 0 + +/datum/looping_sound/sequence/vv_edit_var(var_name, var_value) + if(var_name == "sequence") + set_new_sequence(var_value) + return ..() + +/datum/looping_sound/sequence/proc/iterate_on_sequence() + var/data = get_data_from_position() + next_iteration_delay = process_data(data) + increment_position() + +/datum/looping_sound/sequence/proc/get_data_from_position() + return sequence[position] + +// Override to do something based on the input. +/datum/looping_sound/sequence/proc/process_data(input) + return + +// Changes the sequence, and sets the position back to the start. +/datum/looping_sound/sequence/proc/set_new_sequence(new_sequence) + sequence = new_sequence + reset_position() + +// Called to advance the position, and handle reaching the end if so. +/datum/looping_sound/sequence/proc/increment_position() + position++ + if(position > get_max_position()) + reached_end_of_sequence() + +/datum/looping_sound/sequence/proc/get_max_position() + return length(sequence) + +/datum/looping_sound/sequence/proc/reset_position() + position = 1 + +// Called when the sequence is finished being iterated over. +// If looping is on, the position will be reset, otherwise processing will stop. +/datum/looping_sound/sequence/proc/reached_end_of_sequence() + if(loop_sequence) + next_iteration_delay += repeat_sequnce_delay + reset_position() + else + stop() + +/datum/looping_sound/sequence/sound_loop(starttime) + iterate_on_sequence() + + timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), next_iteration_delay, TIMER_STOPPABLE) + +#define MORSE_DOT "*" // Yes this is an asterisk but its easier to see on a computer compared to a period. +#define MORSE_DASH "-" +#define MORSE_BASE_DELAY 1 // If you change this you will also need to change [dot|dash]_soundfile variables. + +// This implements an automatic conversion of text (the sequence) into audible morse code. +// This can be useful for flavor purposes. For 'real' usage its suggested to also display the sequence in text form, for the benefit of those without sound. +/datum/looping_sound/sequence/morse + // This is just to pass validation in the base type. + mid_sounds = list('sound/effects/tones/440_sine_01.ogg') + mid_length = 1 + opacity_check = TRUE // So we don't have to constantly hear it when out of sight. + + // Dots. + // In Morse Code, the dot's length is one unit. + var/dot_soundfile = 'sound/effects/tones/440_sine_01.ogg' // The sound file to play for a 'dot'. + var/dot_delay = MORSE_BASE_DELAY // How long the sound above plays for, in deciseconds. + + // Dashes. + // In Morse Code, a dash's length is equal to three units (or three dots). + var/dash_soundfile = 'sound/effects/tones/440_sine_03.ogg' // The sound file to play for a 'dash'. + var/dash_delay = MORSE_BASE_DELAY * 3 // Same as the dot delay, except for the dash sound. + + // Spaces. + // In Morse Code, a space's length is equal to one unit (or one dot). + var/spaces_between_sounds = MORSE_BASE_DELAY // How many spaces are between parts of the same letter. + var/spaces_between_letters = MORSE_BASE_DELAY * 3 // How many spaces are between different letters in the same word. + var/spaces_between_words = MORSE_BASE_DELAY * 7 // How many spaces are between different words. + + // Morse Alphabet. + // Note that it is case-insensative. 'A' and 'a' will make the same sounds. + // Unfortunately there isn't a nice way to implement procedure signs w/o the space inbetween the letters. + // Also some of the punctuation isn't super offical/widespread in real life but its the future so *shrug. + var/static/list/morse_alphabet = list( + "A" = list("*", "-"), + "B" = list("-", "*", "*", "*"), + "C" = list("-", "*", "-", "*"), + "D" = list("-", "*", "*"), + "E" = list("*"), + "F" = list("*", "*", "-", "*"), + "G" = list("-", "-", "*"), + "H" = list("*", "*", "*", "*"), + "I" = list("*", "*"), + "J" = list("*", "-", "-", "-"), + "K" = list("-", "*", "-"), + "L" = list("*", "-", "*", "*"), + "M" = list("*", "*"), + "N" = list("-", "*"), + "O" = list("-", "-", "-"), + "P" = list("*", "-", "-", "*"), + "Q" = list("-", "-", "*", "-"), + "R" = list("*", "-", "*"), + "S" = list("*", "*", "*"), + "T" = list("-"), + "U" = list("*", "*", "-"), + "V" = list("*", "*", "*", "-"), + "W" = list("*", "-", "-"), + "X" = list("-", "*", "*", "-"), + "Y" = list("-", "*", "-", "-"), + "Z" = list("-", "-", "*", "*"), + + "1" = list("*", "-", "-", "-", "-"), + "2" = list("*", "*", "-", "-", "-"), + "3" = list("*", "*", "*", "-", "-"), + "4" = list("*", "*", "*", "*", "-"), + "5" = list("*", "*", "*", "*", "*"), + "6" = list("-", "*", "*", "*", "*"), + "7" = list("-", "-", "*", "*", "*"), + "8" = list("-", "-", "-", "*", "*"), + "9" = list("-", "-", "-", "-", "*"), + "0" = list("-", "-", "-", "-", "-"), + + "." = list("*", "-", "*", "-", "*", "-"), + "," = list("-", "-", "*", "*", "-", "-"), + "?" = list("*", "*", "-", "-", "*", "*"), + "'" = list("*", "-", "-", "-", "-", "*"), + "!" = list("-", "*", "-", "*", "-", "-"), + "/" = list("-", "*", "*", "-", "*"), + "(" = list("-", "*", "-", "-", "*"), + ")" = list("-", "*", "-", "-", "*", "-"), + "&" = list("*", "-", "*", "*", "*"), + ":" = list("-", "-", "-", "*", "*", "*"), + ";" = list("-", "*", "-", "*", "-", "*"), + "=" = list("-", "*", "*", "*", "-"), + "+" = list("*", "-", "*", "-", "*"), + "-" = list("-", "*", "*", "*", "*", "-"), + "_" = list("*", "*", "-", "-", "*", "-"), + "\""= list("*", "-", "*", "*", "-", "*"), + "$" = list("*", "*", "*", "-", "*", "*", "-"), + "@" = list("*", "-", "-", "*", "-", "*"), + ) + + +/datum/looping_sound/sequence/morse/process_data(letter) + letter = uppertext(letter) // Make it case-insensative. + + // If it's whitespace, treat it as a (Morse) space. + if(letter == " ") + return spaces_between_words + + if(!(letter in morse_alphabet)) + CRASH("Encountered invalid character in Morse sequence \"[letter]\".") + + // So I heard you like sequences... + // Play a sequence of sounds while inside the current iteration of the outer sequence. + var/list/instructions = morse_alphabet[letter] + for(var/sound in instructions) + if(sound == MORSE_DOT) + play(dot_soundfile) + sleep(dot_delay) + else // It's a dash otherwise. + play(dash_soundfile) + sleep(dash_delay) + sleep(spaces_between_sounds) + return spaces_between_letters + +#undef MORSE_DOT +#undef MORSE_DASH diff --git a/code/datums/looping_sounds/weather_sounds.dm b/code/datums/looping_sounds/weather_sounds.dm index 67a97c71f1a..db8396f3a31 100644 --- a/code/datums/looping_sounds/weather_sounds.dm +++ b/code/datums/looping_sounds/weather_sounds.dm @@ -1,98 +1,98 @@ -/datum/looping_sound/weather - pref_check = /datum/client_preference/weather_sounds - -/datum/looping_sound/weather/outside_blizzard - mid_sounds = list( - 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/outside/active_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/outside/active_end.ogg' - volume = 40 - -/datum/looping_sound/weather/inside_blizzard - mid_sounds = list( - 'sound/effects/weather/snowstorm/inside/active_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/active_mid2.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/active_mid3.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/inside/active_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/inside/active_end.ogg' - volume = 20 - -/datum/looping_sound/weather/outside_snow - mid_sounds = list( - 'sound/effects/weather/snowstorm/outside/weak_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/weak_mid2.ogg' = 1, - 'sound/effects/weather/snowstorm/outside/weak_mid3.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/outside/weak_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/outside/weak_end.ogg' - volume = 20 - -/datum/looping_sound/weather/inside_snow - mid_sounds = list( - 'sound/effects/weather/snowstorm/inside/weak_mid1.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/weak_mid2.ogg' = 1, - 'sound/effects/weather/snowstorm/inside/weak_mid3.ogg' = 1 - ) - mid_length = 8 SECONDS - start_sound = 'sound/effects/weather/snowstorm/inside/weak_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/snowstorm/inside/weak_end.ogg' - volume = 10 - -/datum/looping_sound/weather/wind - mid_sounds = list( - 'sound/effects/weather/wind/wind_2_1.ogg' = 1, - 'sound/effects/weather/wind/wind_2_2.ogg' = 1, - 'sound/effects/weather/wind/wind_3_1.ogg' = 1, - 'sound/effects/weather/wind/wind_4_1.ogg' = 1, - 'sound/effects/weather/wind/wind_4_2.ogg' = 1, - 'sound/effects/weather/wind/wind_5_1.ogg' = 1 - ) - mid_length = 10 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind. - volume = 45 - -// Don't have special sounds so we just make it quieter indoors. -/datum/looping_sound/weather/wind/indoors - volume = 25 - -/datum/looping_sound/weather/wind/gentle - volume = 15 - -/datum/looping_sound/weather/wind/gentle/indoors - volume = 5 - -/datum/looping_sound/weather/rain - mid_sounds = list( - 'sound/effects/weather/acidrain_mid.ogg' = 1 - ) - mid_length = 15 SECONDS - start_sound = 'sound/effects/weather/acidrain_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/acidrain_end.ogg' - volume = 20 - -/datum/looping_sound/weather/rain/heavy - volume = 40 - -/datum/looping_sound/weather/rain/indoors - mid_sounds = list( - 'sound/effects/weather/indoorrain_mid.ogg' = 1 - ) - mid_length = 15 SECONDS - start_sound = 'sound/effects/weather/indoorrain_start.ogg' - start_length = 13 SECONDS - end_sound = 'sound/effects/weather/indoorrain_end.ogg' - volume = 20 //Sound is already quieter in file - -/datum/looping_sound/weather/rain/indoors/heavy +/datum/looping_sound/weather + pref_check = /datum/client_preference/weather_sounds + +/datum/looping_sound/weather/outside_blizzard + mid_sounds = list( + 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/active_mid1.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/outside/active_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/outside/active_end.ogg' + volume = 40 + +/datum/looping_sound/weather/inside_blizzard + mid_sounds = list( + 'sound/effects/weather/snowstorm/inside/active_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/active_mid2.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/active_mid3.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/inside/active_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/inside/active_end.ogg' + volume = 20 + +/datum/looping_sound/weather/outside_snow + mid_sounds = list( + 'sound/effects/weather/snowstorm/outside/weak_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/weak_mid2.ogg' = 1, + 'sound/effects/weather/snowstorm/outside/weak_mid3.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/outside/weak_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/outside/weak_end.ogg' + volume = 20 + +/datum/looping_sound/weather/inside_snow + mid_sounds = list( + 'sound/effects/weather/snowstorm/inside/weak_mid1.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/weak_mid2.ogg' = 1, + 'sound/effects/weather/snowstorm/inside/weak_mid3.ogg' = 1 + ) + mid_length = 8 SECONDS + start_sound = 'sound/effects/weather/snowstorm/inside/weak_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/snowstorm/inside/weak_end.ogg' + volume = 10 + +/datum/looping_sound/weather/wind + mid_sounds = list( + 'sound/effects/weather/wind/wind_2_1.ogg' = 1, + 'sound/effects/weather/wind/wind_2_2.ogg' = 1, + 'sound/effects/weather/wind/wind_3_1.ogg' = 1, + 'sound/effects/weather/wind/wind_4_1.ogg' = 1, + 'sound/effects/weather/wind/wind_4_2.ogg' = 1, + 'sound/effects/weather/wind/wind_5_1.ogg' = 1 + ) + mid_length = 10 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind. + volume = 45 + +// Don't have special sounds so we just make it quieter indoors. +/datum/looping_sound/weather/wind/indoors + volume = 25 + +/datum/looping_sound/weather/wind/gentle + volume = 15 + +/datum/looping_sound/weather/wind/gentle/indoors + volume = 5 + +/datum/looping_sound/weather/rain + mid_sounds = list( + 'sound/effects/weather/acidrain_mid.ogg' = 1 + ) + mid_length = 15 SECONDS + start_sound = 'sound/effects/weather/acidrain_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/acidrain_end.ogg' + volume = 20 + +/datum/looping_sound/weather/rain/heavy + volume = 40 + +/datum/looping_sound/weather/rain/indoors + mid_sounds = list( + 'sound/effects/weather/indoorrain_mid.ogg' = 1 + ) + mid_length = 15 SECONDS + start_sound = 'sound/effects/weather/indoorrain_start.ogg' + start_length = 13 SECONDS + end_sound = 'sound/effects/weather/indoorrain_end.ogg' + volume = 20 //Sound is already quieter in file + +/datum/looping_sound/weather/rain/indoors/heavy volume = 40 \ No newline at end of file diff --git a/code/datums/managed_browsers/_managed_browser.dm b/code/datums/managed_browsers/_managed_browser.dm index 6c702ddbdd1..718fa0570cf 100644 --- a/code/datums/managed_browsers/_managed_browser.dm +++ b/code/datums/managed_browsers/_managed_browser.dm @@ -1,52 +1,52 @@ -GLOBAL_VAR(managed_browser_id_ticker) - -// This holds information on managing a /datum/browser object. -// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed. -// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob. -/datum/managed_browser - var/client/my_client = null - var/browser_id = null - var/base_browser_id = null - - var/title = null - var/size_x = 200 - var/size_y = 400 - - var/display_when_created = TRUE - -/datum/managed_browser/New(client/new_client) - if(!new_client) - stack_trace("Managed browser object was not given a client.") - return - if(!base_browser_id) - stack_trace("Managed browser object does not have a base browser id defined in its type.") - return - - my_client = new_client - browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]" - - if(display_when_created) - display() - -/datum/managed_browser/Destroy() - my_client = null - return ..() - -// Override if you want to have the browser title change conditionally. -// Otherwise it's easier to just change the title variable directly. -/datum/managed_browser/proc/get_title() - return title - -// Override to display the html information. -// It is suggested to build it with a list, and use list.Join() at the end. -// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess. -/datum/managed_browser/proc/get_html() - return - -/datum/managed_browser/proc/display() - interact(get_html(), get_title(), my_client) - -/datum/managed_browser/proc/interact(html, title, client/C) - var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src) - popup.set_content(html) +GLOBAL_VAR(managed_browser_id_ticker) + +// This holds information on managing a /datum/browser object. +// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed. +// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob. +/datum/managed_browser + var/client/my_client = null + var/browser_id = null + var/base_browser_id = null + + var/title = null + var/size_x = 200 + var/size_y = 400 + + var/display_when_created = TRUE + +/datum/managed_browser/New(client/new_client) + if(!new_client) + stack_trace("Managed browser object was not given a client.") + return + if(!base_browser_id) + stack_trace("Managed browser object does not have a base browser id defined in its type.") + return + + my_client = new_client + browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]" + + if(display_when_created) + display() + +/datum/managed_browser/Destroy() + my_client = null + return ..() + +// Override if you want to have the browser title change conditionally. +// Otherwise it's easier to just change the title variable directly. +/datum/managed_browser/proc/get_title() + return title + +// Override to display the html information. +// It is suggested to build it with a list, and use list.Join() at the end. +// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess. +/datum/managed_browser/proc/get_html() + return + +/datum/managed_browser/proc/display() + interact(get_html(), get_title(), my_client) + +/datum/managed_browser/proc/interact(html, title, client/C) + var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src) + popup.set_content(html) popup.open() \ No newline at end of file diff --git a/code/datums/managed_browsers/feedback_form.dm b/code/datums/managed_browsers/feedback_form.dm index c6160ffc598..cd7b3a8b207 100644 --- a/code/datums/managed_browsers/feedback_form.dm +++ b/code/datums/managed_browsers/feedback_form.dm @@ -1,147 +1,147 @@ -/client - var/datum/managed_browser/feedback_form/feedback_form = null - -/client/can_vv_get(var_name) - return var_name != NAMEOF(src, feedback_form) // No snooping. - -GENERAL_PROTECT_DATUM(/datum/managed_browser/feedback_form) - -// A fairly simple object to hold information about a player's feedback as it's being written. -// Having this be it's own object instead of being baked into /mob/new_player allows for it to be used -// from other places than just the lobby, and makes it a lot harder for people with dev powers to be naughty with it using VV/proccall. -/datum/managed_browser/feedback_form - base_browser_id = "feedback_form" - title = "Server Feedback" - size_x = 480 - size_y = 520 - var/feedback_topic = null - var/feedback_body = null - var/feedback_hide_author = FALSE - -/datum/managed_browser/feedback_form/New(client/new_client) - feedback_topic = config.sqlite_feedback_topics[1] - ..(new_client) - -/datum/managed_browser/feedback_form/Destroy() - if(my_client) - my_client.feedback_form = null - return ..() - -// Privacy option is allowed if both the config allows it, and the pepper file exists and isn't blank. -/datum/managed_browser/feedback_form/proc/can_be_private() - return config.sqlite_feedback_privacy && SSsqlite.get_feedback_pepper() - -/datum/managed_browser/feedback_form/display() - if(!my_client) - return - if(!SSsqlite.can_submit_feedback(my_client)) - return - ..() - -// Builds the window for players to review their feedback. -/datum/managed_browser/feedback_form/get_html() - var/list/dat = list("") - dat += "
    " - dat += "" - dat += "Here, you can write some feedback for the server.
    " - dat += "Note that HTML is NOT supported!
    " - dat += "Click the edit button to begin writing.
    " - - dat += "Your feedback is currently [length(feedback_body)]/[MAX_FEEDBACK_LENGTH] letters long." - dat += "
    " - dat += "
    " - - dat += "

    Preview

    " - - dat += "Author: " - - if(can_be_private()) - if(!feedback_hide_author) - dat += "[my_client.ckey] " - dat += span("linkOn", "Visible") - dat += " | " - dat += href(src, list("feedback_hide_author" = 1), "Hashed") - else - dat += "[md5(ckey(lowertext(my_client.ckey + SSsqlite.get_feedback_pepper())))] " - dat += href(src, list("feedback_hide_author" = 0), "Visible") - dat += " | " - dat += span("linkOn", "Hashed") - else - dat += my_client.ckey - dat += "
    " - - if(config.sqlite_feedback_topics.len > 1) - dat += "Topic: [href(src, list("feedback_choose_topic" = 1), feedback_topic)]
    " - else - dat += "Topic: [config.sqlite_feedback_topics[1]]
    " - - dat += "
    " - if(feedback_body) - dat += replacetext(feedback_body, "\n", "
    ") // So newlines will look like they work in the preview. - else - dat += "\[Feedback goes here...\]" - dat += "
    " - dat += href(src, list("feedback_edit_body" = 1), "Edit") - dat += "
    " - - if(config.sqlite_feedback_cooldown) - dat += "Please note that you will have to wait [config.sqlite_feedback_cooldown] day\s before \ - being able to write more feedback after submitting.
    " - - dat += href(src, list("feedback_submit" = 1), "Submit") - dat += "" - return dat.Join() - -/datum/managed_browser/feedback_form/Topic(href, href_list[]) - if(!my_client) - return FALSE - - if(href_list["feedback_edit_body"]) - // This is deliberately not sanitized here, and is instead checked when hitting the submission button, - // as we want to give the user a chance to fix it without needing to rewrite the whole thing. - feedback_body = tgui_input_text(my_client, "Please write your feedback here.", "Feedback Body", feedback_body, multiline = TRUE, prevent_enter = TRUE) - display() // Refresh the window with new information. - return - - if(href_list["feedback_hide_author"]) - if(!can_be_private()) - feedback_hide_author = FALSE - else - feedback_hide_author = text2num(href_list["feedback_hide_author"]) - display() - return - - if(href_list["feedback_choose_topic"]) - feedback_topic = tgui_input_list(my_client, "Choose the topic you want to submit your feedback under.", "Feedback Topic", config.sqlite_feedback_topics) - display() - return - - if(href_list["feedback_submit"]) - // Do some last minute validation, and tell the user if something goes wrong, - // so we don't wipe out their ten thousand page essay due to having a few too many characters. - if(length(feedback_body) > MAX_FEEDBACK_LENGTH) - to_chat(my_client, span("warning", "Your feedback is too long, at [length(feedback_body)] characters, where as the \ - limit is [MAX_FEEDBACK_LENGTH]. Please shorten it and try again.")) - return - - var/text = sanitize(feedback_body, max_length = 0, encode = TRUE, trim = FALSE, extra = FALSE) - if(!text) // No text, or it was super invalid. - to_chat(my_client, span("warning", "It appears you didn't write anything, or it was invalid.")) - return - - if(tgui_alert(my_client, "Are you sure you want to submit your feedback?", "Confirm Submission", list("No", "Yes")) == "Yes") - var/author_text = my_client.ckey - if(can_be_private() && feedback_hide_author) - author_text = md5(my_client.ckey + SSsqlite.get_feedback_pepper()) - - var/success = SSsqlite.insert_feedback(author = author_text, topic = feedback_topic, content = feedback_body, sqlite_object = SSsqlite.sqlite_db) - if(!success) - to_chat(my_client, span("warning", "Something went wrong while inserting your feedback into the database. Please try again. \ - If this happens again, you should contact a developer.")) - return - - my_client.mob << browse(null, "window=[browser_id]") // Closes the window. - if(istype(my_client.mob, /mob/new_player)) - var/mob/new_player/NP = my_client.mob - NP.new_player_panel_proc() // So the feedback button goes away, if the user gets put on cooldown. +/client + var/datum/managed_browser/feedback_form/feedback_form = null + +/client/can_vv_get(var_name) + return var_name != NAMEOF(src, feedback_form) // No snooping. + +GENERAL_PROTECT_DATUM(/datum/managed_browser/feedback_form) + +// A fairly simple object to hold information about a player's feedback as it's being written. +// Having this be it's own object instead of being baked into /mob/new_player allows for it to be used +// from other places than just the lobby, and makes it a lot harder for people with dev powers to be naughty with it using VV/proccall. +/datum/managed_browser/feedback_form + base_browser_id = "feedback_form" + title = "Server Feedback" + size_x = 480 + size_y = 520 + var/feedback_topic = null + var/feedback_body = null + var/feedback_hide_author = FALSE + +/datum/managed_browser/feedback_form/New(client/new_client) + feedback_topic = config.sqlite_feedback_topics[1] + ..(new_client) + +/datum/managed_browser/feedback_form/Destroy() + if(my_client) + my_client.feedback_form = null + return ..() + +// Privacy option is allowed if both the config allows it, and the pepper file exists and isn't blank. +/datum/managed_browser/feedback_form/proc/can_be_private() + return config.sqlite_feedback_privacy && SSsqlite.get_feedback_pepper() + +/datum/managed_browser/feedback_form/display() + if(!my_client) + return + if(!SSsqlite.can_submit_feedback(my_client)) + return + ..() + +// Builds the window for players to review their feedback. +/datum/managed_browser/feedback_form/get_html() + var/list/dat = list("") + dat += "
    " + dat += "" + dat += "Here, you can write some feedback for the server.
    " + dat += "Note that HTML is NOT supported!
    " + dat += "Click the edit button to begin writing.
    " + + dat += "Your feedback is currently [length(feedback_body)]/[MAX_FEEDBACK_LENGTH] letters long." + dat += "
    " + dat += "
    " + + dat += "

    Preview

    " + + dat += "Author: " + + if(can_be_private()) + if(!feedback_hide_author) + dat += "[my_client.ckey] " + dat += span("linkOn", "Visible") + dat += " | " + dat += href(src, list("feedback_hide_author" = 1), "Hashed") + else + dat += "[md5(ckey(lowertext(my_client.ckey + SSsqlite.get_feedback_pepper())))] " + dat += href(src, list("feedback_hide_author" = 0), "Visible") + dat += " | " + dat += span("linkOn", "Hashed") + else + dat += my_client.ckey + dat += "
    " + + if(config.sqlite_feedback_topics.len > 1) + dat += "Topic: [href(src, list("feedback_choose_topic" = 1), feedback_topic)]
    " + else + dat += "Topic: [config.sqlite_feedback_topics[1]]
    " + + dat += "
    " + if(feedback_body) + dat += replacetext(feedback_body, "\n", "
    ") // So newlines will look like they work in the preview. + else + dat += "\[Feedback goes here...\]" + dat += "
    " + dat += href(src, list("feedback_edit_body" = 1), "Edit") + dat += "
    " + + if(config.sqlite_feedback_cooldown) + dat += "Please note that you will have to wait [config.sqlite_feedback_cooldown] day\s before \ + being able to write more feedback after submitting.
    " + + dat += href(src, list("feedback_submit" = 1), "Submit") + dat += "" + return dat.Join() + +/datum/managed_browser/feedback_form/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["feedback_edit_body"]) + // This is deliberately not sanitized here, and is instead checked when hitting the submission button, + // as we want to give the user a chance to fix it without needing to rewrite the whole thing. + feedback_body = tgui_input_text(my_client, "Please write your feedback here.", "Feedback Body", feedback_body, multiline = TRUE, prevent_enter = TRUE) + display() // Refresh the window with new information. + return + + if(href_list["feedback_hide_author"]) + if(!can_be_private()) + feedback_hide_author = FALSE + else + feedback_hide_author = text2num(href_list["feedback_hide_author"]) + display() + return + + if(href_list["feedback_choose_topic"]) + feedback_topic = tgui_input_list(my_client, "Choose the topic you want to submit your feedback under.", "Feedback Topic", config.sqlite_feedback_topics) + display() + return + + if(href_list["feedback_submit"]) + // Do some last minute validation, and tell the user if something goes wrong, + // so we don't wipe out their ten thousand page essay due to having a few too many characters. + if(length(feedback_body) > MAX_FEEDBACK_LENGTH) + to_chat(my_client, span("warning", "Your feedback is too long, at [length(feedback_body)] characters, where as the \ + limit is [MAX_FEEDBACK_LENGTH]. Please shorten it and try again.")) + return + + var/text = sanitize(feedback_body, max_length = 0, encode = TRUE, trim = FALSE, extra = FALSE) + if(!text) // No text, or it was super invalid. + to_chat(my_client, span("warning", "It appears you didn't write anything, or it was invalid.")) + return + + if(tgui_alert(my_client, "Are you sure you want to submit your feedback?", "Confirm Submission", list("No", "Yes")) == "Yes") + var/author_text = my_client.ckey + if(can_be_private() && feedback_hide_author) + author_text = md5(my_client.ckey + SSsqlite.get_feedback_pepper()) + + var/success = SSsqlite.insert_feedback(author = author_text, topic = feedback_topic, content = feedback_body, sqlite_object = SSsqlite.sqlite_db) + if(!success) + to_chat(my_client, span("warning", "Something went wrong while inserting your feedback into the database. Please try again. \ + If this happens again, you should contact a developer.")) + return + + my_client.mob << browse(null, "window=[browser_id]") // Closes the window. + if(istype(my_client.mob, /mob/new_player)) + var/mob/new_player/NP = my_client.mob + NP.new_player_panel_proc() // So the feedback button goes away, if the user gets put on cooldown. qdel(src) \ No newline at end of file diff --git a/code/datums/managed_browsers/feedback_viewer.dm b/code/datums/managed_browsers/feedback_viewer.dm index 02360363384..59fbc679c4e 100644 --- a/code/datums/managed_browsers/feedback_viewer.dm +++ b/code/datums/managed_browsers/feedback_viewer.dm @@ -1,162 +1,162 @@ -/client - var/datum/managed_browser/feedback_viewer/feedback_viewer = null - -/datum/admins/proc/view_feedback() - set category = "Admin" - set name = "View Feedback" - set desc = "Open the Feedback Viewer" - - if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT)) - return - - if(usr.client.feedback_viewer) - usr.client.feedback_viewer.display() - else - usr.client.feedback_viewer = new(usr.client) - -// This object holds the code to run the admin feedback viewer. -/datum/managed_browser/feedback_viewer - base_browser_id = "feedback_viewer" - title = "Submitted Feedback" - size_x = 900 - size_y = 500 - var/database/query/last_query = null - -/datum/managed_browser/feedback_viewer/New(client/new_client) - if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT, new_client)) // Just in case someone figures out a way to spawn this as non-staff. - message_admins("[new_client] tried to view feedback with insufficent permissions.") - qdel(src) - - ..() - -/datum/managed_browser/feedback_viewer/Destroy() - if(my_client) - my_client.feedback_viewer = null - return ..() - -/datum/managed_browser/feedback_viewer/proc/feedback_filter(row_name, thing_to_find, exact = FALSE) - var/database/query/query = null - if(exact) // Useful for ID searches, so searching for 'id 10' doesn't also get 'id 101'. - query = new({" - SELECT * - FROM [SQLITE_TABLE_FEEDBACK] - WHERE [row_name] == ? - ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] - DESC LIMIT 50; - "}, - thing_to_find - ) - - else - // Wrap the thing in %s so LIKE will work. - thing_to_find = "%[thing_to_find]%" - query = new({" - SELECT * - FROM [SQLITE_TABLE_FEEDBACK] - WHERE [row_name] LIKE ? - ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] - DESC LIMIT 50; - "}, - thing_to_find - ) - query.Execute(SSsqlite.sqlite_db) - SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer - Filter by [row_name] to find [thing_to_find]") - return query - -// Builds the window for players to review their feedback. -/datum/managed_browser/feedback_viewer/get_html() - var/list/dat = list("") - if(!last_query) // If no query was done before, just show the most recent feedbacks. - var/database/query/query = new({" - SELECT * - FROM [SQLITE_TABLE_FEEDBACK] - ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] - DESC LIMIT 50; - "} - ) - query.Execute(SSsqlite.sqlite_db) - SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer") - last_query = query - - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - while(last_query.NextRow()) - var/list/row_data = last_query.GetRowData() - dat += "" - dat += "" - dat += "" - dat += "" // TODO: Color this to make hashed keys more distinguishable. - var/text = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] - if(length(text) > 512) - text = href(src, list( - "show_full_feedback" = 1, - "feedback_author" = row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR], - "feedback_content" = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] - ), "[copytext(text, 1, 64)]... ([length(text)])") - else - text = replacetext(text, "\n", "
    ") - dat += "" - dat += "" - dat += "" - dat += "
    [href(src, list("filter_id" = 1), "ID")][href(src, list("filter_topic" = 1), "Topic")][href(src, list("filter_author" = 1), "Author")][href(src, list("filter_content" = 1), "Content")][href(src, list("filter_datetime" = 1), "Datetime")]
    [row_data[SQLITE_FEEDBACK_COLUMN_ID]][row_data[SQLITE_FEEDBACK_COLUMN_TOPIC]][row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR]][text][row_data[SQLITE_FEEDBACK_COLUMN_DATETIME]]
    " - - dat += "" - return dat.Join() - -// Used to show the full version of feedback in a seperate window. -/datum/managed_browser/feedback_viewer/proc/display_big_feedback(author, text) - var/list/dat = list("") - dat += replacetext(text, "\n", "
    ") - - var/datum/browser/popup = new(my_client.mob, "feedback_big", "[author]'s Feedback", 480, 520, src) - popup.set_content(dat.Join()) - popup.open() - - -/datum/managed_browser/feedback_viewer/Topic(href, href_list[]) - if(!my_client) - return FALSE - - if(href_list["close"]) // To avoid refreshing. - return - - if(href_list["show_full_feedback"]) - display_big_feedback(href_list["feedback_author"], href_list["feedback_content"]) - return - - if(href_list["filter_id"]) - var/id_to_search = tgui_input_number(my_client, "Write feedback ID here.", "Filter by ID", null) - if(id_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_ID, id_to_search, TRUE) - - if(href_list["filter_author"]) - var/author_to_search = tgui_input_text(my_client, "Write desired key or hash here. Partial keys/hashes are allowed.", "Filter by Author", null) - if(author_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_AUTHOR, author_to_search) - - if(href_list["filter_topic"]) - var/topic_to_search = tgui_input_text(my_client, "Write desired topic here. Partial topics are allowed. \ - \nThe current topics in the config are [english_list(config.sqlite_feedback_topics)].", "Filter by Topic", null) - if(topic_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_TOPIC, topic_to_search) - - if(href_list["filter_content"]) - var/content_to_search = tgui_input_text(my_client, "Write desired content to find here. Partial matches are allowed.", "Filter by Content", null, multiline = TRUE) - if(content_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_CONTENT, content_to_search) - - if(href_list["filter_datetime"]) - var/datetime_to_search = tgui_input_text(my_client, "Write desired datetime. Partial matches are allowed.\n\ - Format is 'YYYY-MM-DD HH:MM:SS'.", "Filter by Datetime", null) - if(datetime_to_search) - last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_DATETIME, datetime_to_search) - - // Refresh. +/client + var/datum/managed_browser/feedback_viewer/feedback_viewer = null + +/datum/admins/proc/view_feedback() + set category = "Admin" + set name = "View Feedback" + set desc = "Open the Feedback Viewer" + + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT)) + return + + if(usr.client.feedback_viewer) + usr.client.feedback_viewer.display() + else + usr.client.feedback_viewer = new(usr.client) + +// This object holds the code to run the admin feedback viewer. +/datum/managed_browser/feedback_viewer + base_browser_id = "feedback_viewer" + title = "Submitted Feedback" + size_x = 900 + size_y = 500 + var/database/query/last_query = null + +/datum/managed_browser/feedback_viewer/New(client/new_client) + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT, new_client)) // Just in case someone figures out a way to spawn this as non-staff. + message_admins("[new_client] tried to view feedback with insufficent permissions.") + qdel(src) + + ..() + +/datum/managed_browser/feedback_viewer/Destroy() + if(my_client) + my_client.feedback_viewer = null + return ..() + +/datum/managed_browser/feedback_viewer/proc/feedback_filter(row_name, thing_to_find, exact = FALSE) + var/database/query/query = null + if(exact) // Useful for ID searches, so searching for 'id 10' doesn't also get 'id 101'. + query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [row_name] == ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "}, + thing_to_find + ) + + else + // Wrap the thing in %s so LIKE will work. + thing_to_find = "%[thing_to_find]%" + query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [row_name] LIKE ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "}, + thing_to_find + ) + query.Execute(SSsqlite.sqlite_db) + SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer - Filter by [row_name] to find [thing_to_find]") + return query + +// Builds the window for players to review their feedback. +/datum/managed_browser/feedback_viewer/get_html() + var/list/dat = list("") + if(!last_query) // If no query was done before, just show the most recent feedbacks. + var/database/query/query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "} + ) + query.Execute(SSsqlite.sqlite_db) + SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer") + last_query = query + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + while(last_query.NextRow()) + var/list/row_data = last_query.GetRowData() + dat += "" + dat += "" + dat += "" + dat += "" // TODO: Color this to make hashed keys more distinguishable. + var/text = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] + if(length(text) > 512) + text = href(src, list( + "show_full_feedback" = 1, + "feedback_author" = row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR], + "feedback_content" = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] + ), "[copytext(text, 1, 64)]... ([length(text)])") + else + text = replacetext(text, "\n", "
    ") + dat += "" + dat += "" + dat += "" + dat += "
    [href(src, list("filter_id" = 1), "ID")][href(src, list("filter_topic" = 1), "Topic")][href(src, list("filter_author" = 1), "Author")][href(src, list("filter_content" = 1), "Content")][href(src, list("filter_datetime" = 1), "Datetime")]
    [row_data[SQLITE_FEEDBACK_COLUMN_ID]][row_data[SQLITE_FEEDBACK_COLUMN_TOPIC]][row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR]][text][row_data[SQLITE_FEEDBACK_COLUMN_DATETIME]]
    " + + dat += "" + return dat.Join() + +// Used to show the full version of feedback in a seperate window. +/datum/managed_browser/feedback_viewer/proc/display_big_feedback(author, text) + var/list/dat = list("") + dat += replacetext(text, "\n", "
    ") + + var/datum/browser/popup = new(my_client.mob, "feedback_big", "[author]'s Feedback", 480, 520, src) + popup.set_content(dat.Join()) + popup.open() + + +/datum/managed_browser/feedback_viewer/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["close"]) // To avoid refreshing. + return + + if(href_list["show_full_feedback"]) + display_big_feedback(href_list["feedback_author"], href_list["feedback_content"]) + return + + if(href_list["filter_id"]) + var/id_to_search = tgui_input_number(my_client, "Write feedback ID here.", "Filter by ID", null) + if(id_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_ID, id_to_search, TRUE) + + if(href_list["filter_author"]) + var/author_to_search = tgui_input_text(my_client, "Write desired key or hash here. Partial keys/hashes are allowed.", "Filter by Author", null) + if(author_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_AUTHOR, author_to_search) + + if(href_list["filter_topic"]) + var/topic_to_search = tgui_input_text(my_client, "Write desired topic here. Partial topics are allowed. \ + \nThe current topics in the config are [english_list(config.sqlite_feedback_topics)].", "Filter by Topic", null) + if(topic_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_TOPIC, topic_to_search) + + if(href_list["filter_content"]) + var/content_to_search = tgui_input_text(my_client, "Write desired content to find here. Partial matches are allowed.", "Filter by Content", null, multiline = TRUE) + if(content_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_CONTENT, content_to_search) + + if(href_list["filter_datetime"]) + var/datetime_to_search = tgui_input_text(my_client, "Write desired datetime. Partial matches are allowed.\n\ + Format is 'YYYY-MM-DD HH:MM:SS'.", "Filter by Datetime", null) + if(datetime_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_DATETIME, datetime_to_search) + + // Refresh. display() \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index bc87275800b..777ecc42369 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,579 +1,579 @@ -/* Note from Carnie: - The way datum/mind stuff works has been changed a lot. - Minds now represent IC characters rather than following a client around constantly. - - Guidelines for using minds properly: - - - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! - ghost.mind is however used as a reference to the ghost's corpse - - - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) - the existing mind of the old mob should be transfered to the new mob like so: - - mind.transfer_to(new_mob) - - - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. - By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing - the player. - - - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. - - - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting - a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. - - new_mob.key = key - - The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! - However if you want that mind to have any special properties like being a traitor etc you will have to do that - yourself. - -*/ - -/datum/mind - var/key - var/name //replaces mob/var/original_name - var/mob/living/current - var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. - var/active = 0 - - var/memory - - var/assigned_role - var/special_role - - var/role_alt_title - - var/datum/job/assigned_job - - var/list/datum/objective/objectives = list() - var/list/datum/objective/special_verbs = list() - - var/has_been_rev = 0//Tracks if this mind has been a rev or not - - var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder - - var/rev_cooldown = 0 - var/tcrystals = 0 - var/list/purchase_log = new - var/used_TC = 0 - - var/list/learned_recipes //List of learned recipe TYPES. - - // the world.time since the mob has been brigged, or -1 if not at all - var/brigged_since = -1 - - //put this here for easier tracking ingame - var/datum/money_account/initial_account - - //used for antag tcrystal trading, more info in code\game\objects\items\telecrystals.dm - var/accept_tcrystals = 0 - - //used for optional self-objectives that antagonists can give themselves, which are displayed at the end of the round. - var/ambitions - - //used to store what traits the player had picked out in their preferences before joining, in text form. - var/list/traits = list() - - var/datum/religion/my_religion - -/datum/mind/New(var/key) - src.key = key - purchase_log = list() - ..() - -/datum/mind/proc/transfer_to(mob/living/new_character) - if(!istype(new_character)) - to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") - if(current) //remove ourself from our old body's mind variable - if(changeling) - current.remove_changeling_powers() - current.verbs -= /datum/changeling/proc/EvolutionMenu - current.mind = null - - if(new_character.mind) //remove any mind currently in our new body's mind variable - new_character.mind.current = null - - current = new_character //link ourself to our new body - new_character.mind = src //and link our new body to ourself - - if(changeling) - new_character.make_changeling() - - if(active) - new_character.key = key //now transfer the key to link the client to our new body - -/datum/mind/proc/store_memory(new_text) - memory += "[new_text]
    " - -/datum/mind/proc/show_memory(mob/recipient) - var/output = "[current.real_name]'s Memory
    " - output += memory - - if(objectives.len>0) - output += "
    Objectives:" - - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - output += "Objective #[obj_count]: [objective.explanation_text]" - obj_count++ - - if(ambitions) - output += "
    Ambitions: [ambitions]
    " - recipient << browse(output,"window=memory") - -/datum/mind/proc/edit_memory() - if(!ticker || !ticker.mode) - tgui_alert_async(usr, "Not before round-start!", "Alert") - return - - var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
    " - out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
    " - out += "Assigned role: [assigned_role]. Edit
    " - out += "
    " - out += "Factions and special roles:
    " - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - out += "[antag.get_panel_entry(src)]" - out += "

    " - out += "Objectives
    " - - if(objectives && objectives.len) - var/num = 1 - for(var/datum/objective/O in objectives) - out += "Objective #[num]: [O.explanation_text] " - if(O.completed) - out += "(complete)" - else - out += "(incomplete)" - out += " \[toggle\]" - out += " \[remove\]
    " - num++ - out += "
    \[announce objectives\]" - - else - out += "None." - out += "
    \[add\]

    " - out += "Ambitions: [ambitions ? ambitions : "None"] \[edit\]
    " - usr << browse(out, "window=edit_memory[src]") - -/datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - if(href_list["add_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] - if(antag) - if(antag.add_antagonist(src, 1, 1, 0, 1, 1)) // Ignore equipment and role type for this. - log_admin("[key_name_admin(usr)] made [key_name(src)] into a [antag.role_text].") - else - to_chat(usr, "[src] could not be made into a [antag.role_text]!") - - else if(href_list["remove_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] - if(antag) antag.remove_antagonist(src) - - else if(href_list["equip_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]] - if(antag) antag.equip(src.current) - - else if(href_list["unequip_antagonist"]) - var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]] - if(antag) antag.unequip(src.current) - - else if(href_list["move_antag_to_spawn"]) - var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]] - if(antag) antag.place_mob(src.current) - - else if (href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist - if (!new_role) return - assigned_role = new_role - - else if (href_list["memory_edit"]) - var/new_memo = sanitize(tgui_input_text(usr, "Write new memory", "Memory", memory, multiline = TRUE, prevent_enter = TRUE)) - if (isnull(new_memo)) return - memory = new_memo - - else if (href_list["amb_edit"]) - var/datum/mind/mind = locate(href_list["amb_edit"]) - if(!mind) - return - var/new_ambition = tgui_input_text(usr, "Enter a new ambition", "Memory", mind.ambitions, multiline = TRUE, prevent_enter = TRUE) - if(isnull(new_ambition)) - return - if(mind) - mind.ambitions = sanitize(new_ambition) - to_chat(mind.current, "Your ambitions have been changed by higher powers, they are now: [mind.ambitions]") - log_and_message_admins("made [key_name(mind.current)]'s ambitions be '[mind.ambitions]'.") - - else if (href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if (href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if (!objective) return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/list/choices = list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") - var/new_obj_type = tgui_input_list(usr, "Select objective type:", "Objective type", choices, def_value) - if (!new_obj_type) return - - var/datum/objective/new_objective = null - - switch (new_obj_type) - if ("assassinate","protect","debrain", "harm", "brig") - //To determine what to name the objective in explanation text. - var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. - var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. - var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. - - var/list/possible_targets = list("Free objective") - for(var/datum/mind/possible_target in ticker.minds) - if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) - if (objective&&(objective.type in objective_list) && objective.target) - def_target = objective.target.current - - var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_targets, def_target) - if (!new_target) return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - var/mob/living/M = new_target - if (!istype(M) || !M.mind || new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = M.mind - new_objective.explanation_text = "[objective_type] [M.real_name], the [M.mind.special_role ? M.mind:special_role : M.mind:assigned_role]." - - if ("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if ("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if ("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if ("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if ("mercenary") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if ("steal") - if (!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if (!steal.select_target()) - return - - if("download","capture","absorb", "vore") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = tgui_input_number(usr, "Input target number:", "Objective", def_num) - if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Accumulate [target_number] capture points." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - if("vore") - new_objective = new /datum/objective/vore - new_objective.explanation_text = "Devour [target_number] [target_number == 1 ? "person" : "people"]. What happens to them after you do that is irrelevant." - new_objective.owner = src - new_objective.target_amount = target_number - - if ("custom") - var/expl = sanitize(tgui_input_text(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "")) - if (!expl) return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if (!new_objective) return - - if (objective) - objectives -= objective - objectives.Insert(objective_pos, new_objective) - else - objectives += new_objective - - else if (href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) return - objectives -= objective - - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) return - objective.completed = !objective.completed - - else if(href_list["implant"]) - var/mob/living/carbon/human/H = current - - BITSET(H.hud_updateflag, IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not. - - switch(href_list["implant"]) - if("remove") - for(var/obj/item/weapon/implant/loyalty/I in H.contents) - for(var/obj/item/organ/external/organs in H.organs) - if(I in organs.implants) - qdel(I) - break - to_chat(H, "Your loyalty implant has been deactivated.") - log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") - if("add") - to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") - H.implant_loyalty(override = TRUE) - log_admin("[key_name_admin(usr)] has loyalty implanted [current].") - else - else if (href_list["silicon"]) - BITSET(current.hud_updateflag, SPECIALROLE_HUD) - switch(href_list["silicon"]) - - if("unemag") - var/mob/living/silicon/robot/R = current - if (istype(R)) - R.emagged = 0 - if (R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - log_admin("[key_name_admin(usr)] has unemag'ed [R].") - - if("unemagcyborgs") - if (istype(current, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/ai = current - for (var/mob/living/silicon/robot/R in ai.connected_robots) - R.emagged = 0 - if (R.module) - if (R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - log_admin("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") - - else if (href_list["common"]) - switch(href_list["common"]) - if("undress") - for(var/obj/item/W in current) - current.drop_from_inventory(W) - if("takeuplink") - take_uplink() - memory = null//Remove any memory they may have had. - if("crystals") - if (usr.client.holder.rights & R_FUN) - // var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() No longer needed, uses stored in mind - var/crystals - crystals = tcrystals - crystals = tgui_input_number(usr, "Amount of telecrystals for [key]", crystals) - if (!isnull(crystals)) - tcrystals = crystals - - else if (href_list["obj_announce"]) - var/obj_count = 1 - to_chat(current, "Your current objectives:") - for(var/datum/objective/objective in objectives) - to_chat(current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - edit_memory() - -/datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for (var/obj/item/I in L) - if (I.hidden_uplink) - return I.hidden_uplink - return null - -/datum/mind/proc/take_uplink() - var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() - if(H) - qdel(H) - - -// check whether this mind's mob has been brigged for the given duration -// have to call this periodically for the duration to work properly -/datum/mind/proc/is_brigged(duration) - var/turf/T = current.loc - if(!istype(T)) - brigged_since = -1 - return 0 - var/is_currently_brigged = 0 - if(istype(T.loc,/area/security/brig)) - is_currently_brigged = 1 - for(var/obj/item/weapon/card/id/card in current) - is_currently_brigged = 0 - break // if they still have ID they're not brigged - for(var/obj/item/device/pda/P in current) - if(P.id) - is_currently_brigged = 0 - break // if they still have ID they're not brigged - - if(!is_currently_brigged) - brigged_since = -1 - return 0 - - if(brigged_since == -1) - brigged_since = world.time - - return (duration <= world.time - brigged_since) - -/datum/mind/proc/reset() - assigned_role = null - special_role = null - role_alt_title = null - assigned_job = null - //faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know. - changeling = null - initial_account = null - objectives = list() - special_verbs = list() - has_been_rev = 0 - rev_cooldown = 0 - brigged_since = -1 - -//Antagonist role check -/mob/living/proc/check_special_role(role) - if(mind) - if(!role) - return mind.special_role - else - return (mind.special_role == role) ? 1 : 0 - else - return 0 - -/datum/mind/proc/get_ghost(even_if_they_cant_reenter) - for(var/mob/observer/dead/G in player_list) - if(G.mind == src) - if(G.can_reenter_corpse || even_if_they_cant_reenter) - return G - break - -/datum/mind/proc/grab_ghost(force) - var/mob/observer/dead/G = get_ghost(even_if_they_cant_reenter = force) - . = G - if(G) - G.reenter_corpse() - -//Initialisation procs -/mob/living/proc/mind_initialize() - if(mind) - mind.key = key - else - mind = new /datum/mind(key) - mind.original = src - if(ticker) - ticker.minds += mind - else - to_world_log("## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn") - if(!mind.name) mind.name = real_name - mind.current = src - if(player_is_antag(mind)) - src.client.verbs += /client/proc/aooc - -//HUMAN -/mob/living/carbon/human/mind_initialize() - . = ..() - if(!mind.assigned_role) - mind.assigned_role = USELESS_JOB //defualt //VOREStation Edit - Visitor not Assistant - -//slime -/mob/living/simple_mob/slime/mind_initialize() - . = ..() - mind.assigned_role = "slime" - -/mob/living/carbon/alien/larva/mind_initialize() - . = ..() - mind.special_role = "Larva" - -//AI -/mob/living/silicon/ai/mind_initialize() - . = ..() - mind.assigned_role = "AI" - -//BORG -/mob/living/silicon/robot/mind_initialize() - . = ..() - mind.assigned_role = "Cyborg" - -//PAI -/mob/living/silicon/pai/mind_initialize() - . = ..() - mind.assigned_role = "pAI" - mind.special_role = "" - -//Animals -/mob/living/simple_mob/mind_initialize() - . = ..() - mind.assigned_role = "Simple Mob" - -/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize() - . = ..() - mind.assigned_role = "Corgi" - -/mob/living/simple_mob/construct/shade/mind_initialize() - . = ..() - mind.assigned_role = "Shade" - mind.special_role = "Cultist" - -/mob/living/simple_mob/construct/artificer/mind_initialize() - . = ..() - mind.assigned_role = "Artificer" - mind.special_role = "Cultist" - -/mob/living/simple_mob/construct/wraith/mind_initialize() - . = ..() - mind.assigned_role = "Wraith" - mind.special_role = "Cultist" - -/mob/living/simple_mob/construct/juggernaut/mind_initialize() - . = ..() - mind.assigned_role = "Juggernaut" - mind.special_role = "Cultist" +/* Note from Carnie: + The way datum/mind stuff works has been changed a lot. + Minds now represent IC characters rather than following a client around constantly. + + Guidelines for using minds properly: + + - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! + ghost.mind is however used as a reference to the ghost's corpse + + - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) + the existing mind of the old mob should be transfered to the new mob like so: + + mind.transfer_to(new_mob) + + - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. + By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing + the player. + + - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. + + - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting + a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. + + new_mob.key = key + + The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! + However if you want that mind to have any special properties like being a traitor etc you will have to do that + yourself. + +*/ + +/datum/mind + var/key + var/name //replaces mob/var/original_name + var/mob/living/current + var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. + var/active = 0 + + var/memory + + var/assigned_role + var/special_role + + var/role_alt_title + + var/datum/job/assigned_job + + var/list/datum/objective/objectives = list() + var/list/datum/objective/special_verbs = list() + + var/has_been_rev = 0//Tracks if this mind has been a rev or not + + var/datum/faction/faction //associated faction + var/datum/changeling/changeling //changeling holder + + var/rev_cooldown = 0 + var/tcrystals = 0 + var/list/purchase_log = new + var/used_TC = 0 + + var/list/learned_recipes //List of learned recipe TYPES. + + // the world.time since the mob has been brigged, or -1 if not at all + var/brigged_since = -1 + + //put this here for easier tracking ingame + var/datum/money_account/initial_account + + //used for antag tcrystal trading, more info in code\game\objects\items\telecrystals.dm + var/accept_tcrystals = 0 + + //used for optional self-objectives that antagonists can give themselves, which are displayed at the end of the round. + var/ambitions + + //used to store what traits the player had picked out in their preferences before joining, in text form. + var/list/traits = list() + + var/datum/religion/my_religion + +/datum/mind/New(var/key) + src.key = key + purchase_log = list() + ..() + +/datum/mind/proc/transfer_to(mob/living/new_character) + if(!istype(new_character)) + to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") + if(current) //remove ourself from our old body's mind variable + if(changeling) + current.remove_changeling_powers() + current.verbs -= /datum/changeling/proc/EvolutionMenu + current.mind = null + + if(new_character.mind) //remove any mind currently in our new body's mind variable + new_character.mind.current = null + + current = new_character //link ourself to our new body + new_character.mind = src //and link our new body to ourself + + if(changeling) + new_character.make_changeling() + + if(active) + new_character.key = key //now transfer the key to link the client to our new body + +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
    " + +/datum/mind/proc/show_memory(mob/recipient) + var/output = "[current.real_name]'s Memory
    " + output += memory + + if(objectives.len>0) + output += "
    Objectives:" + + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + output += "Objective #[obj_count]: [objective.explanation_text]" + obj_count++ + + if(ambitions) + output += "
    Ambitions: [ambitions]
    " + recipient << browse(output,"window=memory") + +/datum/mind/proc/edit_memory() + if(!ticker || !ticker.mode) + tgui_alert_async(usr, "Not before round-start!", "Alert") + return + + var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
    " + out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
    " + out += "Assigned role: [assigned_role]. Edit
    " + out += "
    " + out += "Factions and special roles:
    " + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + out += "[antag.get_panel_entry(src)]" + out += "

    " + out += "Objectives
    " + + if(objectives && objectives.len) + var/num = 1 + for(var/datum/objective/O in objectives) + out += "Objective #[num]: [O.explanation_text] " + if(O.completed) + out += "([span_green("complete")])" + else + out += "([span_red("incomplete")])" + out += " \[toggle\]" + out += " \[remove\]
    " + num++ + out += "
    \[announce objectives\]" + + else + out += "None." + out += "
    \[add\]

    " + out += "Ambitions: [ambitions ? ambitions : "None"] \[edit\]
    " + usr << browse(out, "window=edit_memory[src]") + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + if(href_list["add_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] + if(antag) + if(antag.add_antagonist(src, 1, 1, 0, 1, 1)) // Ignore equipment and role type for this. + log_admin("[key_name_admin(usr)] made [key_name(src)] into a [antag.role_text].") + else + to_chat(usr, "[src] could not be made into a [antag.role_text]!") + + else if(href_list["remove_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] + if(antag) antag.remove_antagonist(src) + + else if(href_list["equip_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["equip_antagonist"]] + if(antag) antag.equip(src.current) + + else if(href_list["unequip_antagonist"]) + var/datum/antagonist/antag = all_antag_types[href_list["unequip_antagonist"]] + if(antag) antag.unequip(src.current) + + else if(href_list["move_antag_to_spawn"]) + var/datum/antagonist/antag = all_antag_types[href_list["move_antag_to_spawn"]] + if(antag) antag.place_mob(src.current) + + else if (href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in joblist + if (!new_role) return + assigned_role = new_role + + else if (href_list["memory_edit"]) + var/new_memo = sanitize(tgui_input_text(usr, "Write new memory", "Memory", memory, multiline = TRUE, prevent_enter = TRUE)) + if (isnull(new_memo)) return + memory = new_memo + + else if (href_list["amb_edit"]) + var/datum/mind/mind = locate(href_list["amb_edit"]) + if(!mind) + return + var/new_ambition = tgui_input_text(usr, "Enter a new ambition", "Memory", mind.ambitions, multiline = TRUE, prevent_enter = TRUE) + if(isnull(new_ambition)) + return + if(mind) + mind.ambitions = sanitize(new_ambition) + to_chat(mind.current, "Your ambitions have been changed by higher powers, they are now: [mind.ambitions]") + log_and_message_admins("made [key_name(mind.current)]'s ambitions be '[mind.ambitions]'.") + + else if (href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if (href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if (!objective) return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/list/choices = list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") + var/new_obj_type = tgui_input_list(usr, "Select objective type:", "Objective type", choices, def_value) + if (!new_obj_type) return + + var/datum/objective/new_objective = null + + switch (new_obj_type) + if ("assassinate","protect","debrain", "harm", "brig") + //To determine what to name the objective in explanation text. + var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. + var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. + var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. + + var/list/possible_targets = list("Free objective") + for(var/datum/mind/possible_target in ticker.minds) + if ((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) + if (objective&&(objective.type in objective_list) && objective.target) + def_target = objective.target.current + + var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_targets, def_target) + if (!new_target) return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + var/mob/living/M = new_target + if (!istype(M) || !M.mind || new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = M.mind + new_objective.explanation_text = "[objective_type] [M.real_name], the [M.mind.special_role ? M.mind:special_role : M.mind:assigned_role]." + + if ("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if ("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if ("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if ("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if ("mercenary") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if ("steal") + if (!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if (!steal.select_target()) + return + + if("download","capture","absorb", "vore") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = tgui_input_number(usr, "Input target number:", "Objective", def_num) + if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Accumulate [target_number] capture points." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + if("vore") + new_objective = new /datum/objective/vore + new_objective.explanation_text = "Devour [target_number] [target_number == 1 ? "person" : "people"]. What happens to them after you do that is irrelevant." + new_objective.owner = src + new_objective.target_amount = target_number + + if ("custom") + var/expl = sanitize(tgui_input_text(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "")) + if (!expl) return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if (!new_objective) return + + if (objective) + objectives -= objective + objectives.Insert(objective_pos, new_objective) + else + objectives += new_objective + + else if (href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) return + objectives -= objective + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) return + objective.completed = !objective.completed + + else if(href_list["implant"]) + var/mob/living/carbon/human/H = current + + BITSET(H.hud_updateflag, IMPLOYAL_HUD) // updates that players HUD images so secHUD's pick up they are implanted or not. + + switch(href_list["implant"]) + if("remove") + for(var/obj/item/weapon/implant/loyalty/I in H.contents) + for(var/obj/item/organ/external/organs in H.organs) + if(I in organs.implants) + qdel(I) + break + to_chat(H, "Your loyalty implant has been deactivated.") + log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") + if("add") + to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") + H.implant_loyalty(override = TRUE) + log_admin("[key_name_admin(usr)] has loyalty implanted [current].") + else + else if (href_list["silicon"]) + BITSET(current.hud_updateflag, SPECIALROLE_HUD) + switch(href_list["silicon"]) + + if("unemag") + var/mob/living/silicon/robot/R = current + if (istype(R)) + R.emagged = 0 + if (R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + log_admin("[key_name_admin(usr)] has unemag'ed [R].") + + if("unemagcyborgs") + if (istype(current, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/ai = current + for (var/mob/living/silicon/robot/R in ai.connected_robots) + R.emagged = 0 + if (R.module) + if (R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + log_admin("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") + + else if (href_list["common"]) + switch(href_list["common"]) + if("undress") + for(var/obj/item/W in current) + current.drop_from_inventory(W) + if("takeuplink") + take_uplink() + memory = null//Remove any memory they may have had. + if("crystals") + if (usr.client.holder.rights & R_FUN) + // var/obj/item/device/uplink/hidden/suplink = find_syndicate_uplink() No longer needed, uses stored in mind + var/crystals + crystals = tcrystals + crystals = tgui_input_number(usr, "Amount of telecrystals for [key]", crystals) + if (!isnull(crystals)) + tcrystals = crystals + + else if (href_list["obj_announce"]) + var/obj_count = 1 + to_chat(current, span_blue("Your current objectives:")) + for(var/datum/objective/objective in objectives) + to_chat(current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + edit_memory() + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for (var/obj/item/I in L) + if (I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/device/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) + + +// check whether this mind's mob has been brigged for the given duration +// have to call this periodically for the duration to work properly +/datum/mind/proc/is_brigged(duration) + var/turf/T = current.loc + if(!istype(T)) + brigged_since = -1 + return 0 + var/is_currently_brigged = 0 + if(istype(T.loc,/area/security/brig)) + is_currently_brigged = 1 + for(var/obj/item/weapon/card/id/card in current) + is_currently_brigged = 0 + break // if they still have ID they're not brigged + for(var/obj/item/device/pda/P in current) + if(P.id) + is_currently_brigged = 0 + break // if they still have ID they're not brigged + + if(!is_currently_brigged) + brigged_since = -1 + return 0 + + if(brigged_since == -1) + brigged_since = world.time + + return (duration <= world.time - brigged_since) + +/datum/mind/proc/reset() + assigned_role = null + special_role = null + role_alt_title = null + assigned_job = null + //faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know. + changeling = null + initial_account = null + objectives = list() + special_verbs = list() + has_been_rev = 0 + rev_cooldown = 0 + brigged_since = -1 + +//Antagonist role check +/mob/living/proc/check_special_role(role) + if(mind) + if(!role) + return mind.special_role + else + return (mind.special_role == role) ? 1 : 0 + else + return 0 + +/datum/mind/proc/get_ghost(even_if_they_cant_reenter) + for(var/mob/observer/dead/G in player_list) + if(G.mind == src) + if(G.can_reenter_corpse || even_if_they_cant_reenter) + return G + break + +/datum/mind/proc/grab_ghost(force) + var/mob/observer/dead/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() + +//Initialisation procs +/mob/living/proc/mind_initialize() + if(mind) + mind.key = key + else + mind = new /datum/mind(key) + mind.original = src + if(ticker) + ticker.minds += mind + else + to_world_log("## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn") + if(!mind.name) mind.name = real_name + mind.current = src + if(player_is_antag(mind)) + src.client.verbs += /client/proc/aooc + +//HUMAN +/mob/living/carbon/human/mind_initialize() + . = ..() + if(!mind.assigned_role) + mind.assigned_role = USELESS_JOB //defualt //VOREStation Edit - Visitor not Assistant + +//slime +/mob/living/simple_mob/slime/mind_initialize() + . = ..() + mind.assigned_role = "slime" + +/mob/living/carbon/alien/larva/mind_initialize() + . = ..() + mind.special_role = "Larva" + +//AI +/mob/living/silicon/ai/mind_initialize() + . = ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + . = ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + . = ..() + mind.assigned_role = "pAI" + mind.special_role = "" + +//Animals +/mob/living/simple_mob/mind_initialize() + . = ..() + mind.assigned_role = "Simple Mob" + +/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize() + . = ..() + mind.assigned_role = "Corgi" + +/mob/living/simple_mob/construct/shade/mind_initialize() + . = ..() + mind.assigned_role = "Shade" + mind.special_role = "Cultist" + +/mob/living/simple_mob/construct/artificer/mind_initialize() + . = ..() + mind.assigned_role = "Artificer" + mind.special_role = "Cultist" + +/mob/living/simple_mob/construct/wraith/mind_initialize() + . = ..() + mind.assigned_role = "Wraith" + mind.special_role = "Cultist" + +/mob/living/simple_mob/construct/juggernaut/mind_initialize() + . = ..() + mind.assigned_role = "Juggernaut" + mind.special_role = "Cultist" diff --git a/code/datums/modules.dm b/code/datums/modules.dm index 43d25a2e25d..f2193c5e7b7 100644 --- a/code/datums/modules.dm +++ b/code/datums/modules.dm @@ -1,63 +1,63 @@ -// module datum. -// this is per-object instance, and shows the condition of the modules in the object -// actual modules needed is referenced through modulestypes and the object type - -/datum/module - var/status // bits set if working, 0 if broken - var/installed // bits set if installed, 0 if missing - -// moduletypes datum -// this is per-object type, and shows the modules needed for a type of object - -/datum/moduletypes - var/list/modcount = list() // assoc list of the count of modules for a type - - -var/list/modules = list( // global associative list -"/obj/machinery/power/apc" = "card_reader,power_control,id_auth,cell_power,cell_charge") - - -/datum/module/New(var/obj/O) - - var/type = O.type // the type of the creating object - - var/mneed = mods.inmodlist(type) // find if this type has modules defined - - if(!mneed) // not found in module list? - qdel(src) - return - - var/needed = mods.getbitmask(type) // get a bitmask for the number of modules in this object - status = needed - installed = needed - -/datum/moduletypes/proc/addmod(var/type, var/modtextlist) - modules += type // index by type text - modules[type] = modtextlist - -/datum/moduletypes/proc/inmodlist(var/type) - return ("[type]" in modules) - -/datum/moduletypes/proc/getbitmask(var/type) - var/count = modcount["[type]"] - if(count) - return 2**count-1 - - var/modtext = modules["[type]"] - var/num = 1 - var/pos = 1 - - while(1) - pos = findtext(modtext, ",", pos, 0) - if(!pos) - break - else - pos++ - num++ - - modcount += "[type]" - modcount["[type]"] = num - - return 2**num-1 - - +// module datum. +// this is per-object instance, and shows the condition of the modules in the object +// actual modules needed is referenced through modulestypes and the object type + +/datum/module + var/status // bits set if working, 0 if broken + var/installed // bits set if installed, 0 if missing + +// moduletypes datum +// this is per-object type, and shows the modules needed for a type of object + +/datum/moduletypes + var/list/modcount = list() // assoc list of the count of modules for a type + + +var/list/modules = list( // global associative list +"/obj/machinery/power/apc" = "card_reader,power_control,id_auth,cell_power,cell_charge") + + +/datum/module/New(var/obj/O) + + var/type = O.type // the type of the creating object + + var/mneed = mods.inmodlist(type) // find if this type has modules defined + + if(!mneed) // not found in module list? + qdel(src) + return + + var/needed = mods.getbitmask(type) // get a bitmask for the number of modules in this object + status = needed + installed = needed + +/datum/moduletypes/proc/addmod(var/type, var/modtextlist) + modules += type // index by type text + modules[type] = modtextlist + +/datum/moduletypes/proc/inmodlist(var/type) + return ("[type]" in modules) + +/datum/moduletypes/proc/getbitmask(var/type) + var/count = modcount["[type]"] + if(count) + return 2**count-1 + + var/modtext = modules["[type]"] + var/num = 1 + var/pos = 1 + + while(1) + pos = findtext(modtext, ",", pos, 0) + if(!pos) + break + else + pos++ + num++ + + modcount += "[type]" + modcount["[type]"] = num + + return 2**num-1 + + diff --git a/code/datums/observation/stat_set.dm b/code/datums/observation/stat_set.dm index b980d06eccc..6bc6ea45f05 100644 --- a/code/datums/observation/stat_set.dm +++ b/code/datums/observation/stat_set.dm @@ -22,3 +22,12 @@ GLOBAL_DATUM_INIT(stat_set_event, /decl/observ/stat_set, new) . = ..() if(stat != old_stat) GLOB.stat_set_event.raise_event(src, old_stat, new_stat) + + if(isbelly(src.loc)) + var/obj/belly/ourbelly = src.loc + if(!ourbelly.owner.client) + return + if(stat == CONSCIOUS) + to_chat(ourbelly.owner, "\The [src.name] is awake.") + else if(stat == UNCONSCIOUS) + to_chat(ourbelly.owner, "\The [src.name] has fallen unconscious!") diff --git a/code/datums/observation/z_moved.dm b/code/datums/observation/z_moved.dm index 63b89ba0daf..ba30df374c6 100644 --- a/code/datums/observation/z_moved.dm +++ b/code/datums/observation/z_moved.dm @@ -1,16 +1,16 @@ -// Observer Pattern Implementation: Z_Moved -// Registration type: /atom/movable -// -// Raised when: An /atom/movable instance has changed z-levels by any means. -// -// Arguments that the called proc should expect: -// /atom/movable/moving_instance: The instance that moved -// old_z: The z number before the move. -// new_z: The z number after the move. - - -GLOBAL_DATUM_INIT(z_moved_event, /decl/observ/z_moved, new) - -/decl/observ/z_moved - name = "Z_Moved" - expected_type = /atom/movable +// Observer Pattern Implementation: Z_Moved +// Registration type: /atom/movable +// +// Raised when: An /atom/movable instance has changed z-levels by any means. +// +// Arguments that the called proc should expect: +// /atom/movable/moving_instance: The instance that moved +// old_z: The z number before the move. +// new_z: The z number after the move. + + +GLOBAL_DATUM_INIT(z_moved_event, /decl/observ/z_moved, new) + +/decl/observ/z_moved + name = "Z_Moved" + expected_type = /atom/movable diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index bb69a511789..ef7e9ea8ee9 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -182,6 +182,8 @@ var/list/outfits_decls_by_type_ pda.ownjob = assignment pda.ownrank = rank pda.name = "PDA-[H.real_name] ([assignment])" + if(H.client.prefs.ringtone) // if null we use the job default + pda.ttone = H.client.prefs.ringtone return pda /decl/hierarchy/outfit/dd_SortValue() diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm index eb5127aed66..dcef0298d4e 100644 --- a/code/datums/position_point_vector.dm +++ b/code/datums/position_point_vector.dm @@ -1,14 +1,3 @@ -//Designed for things that need precision trajectories like projectiles. -//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels. - -//You might see places where it does - 16 - 1. This is intentionally 17 instead of 16, because of how byond's tiles work and how not doing it will result in rounding errors like things getting put on the wrong turf. - -#define RETURN_PRECISE_POSITION(A) new /datum/position(A) -#define RETURN_PRECISE_POINT(A) new /datum/point(A) - -#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) (new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)) -#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) (new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)) - /datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess. var/x = 0 var/y = 0 @@ -224,4 +213,4 @@ var/needed_time = world.time - last_move last_process = world.time last_move = world.time - increment(needed_time / SSprojectiles.wait) \ No newline at end of file + increment(needed_time / SSprojectiles.wait) diff --git a/code/datums/reference_tracking.dm b/code/datums/reference_tracking.dm index 3154f64740a..0f317b026cb 100644 --- a/code/datums/reference_tracking.dm +++ b/code/datums/reference_tracking.dm @@ -1,6 +1,6 @@ #ifdef REFERENCE_TRACKING -/datum/proc/find_references(skip_alert) +/datum/proc/find_references(skip_alert = TRUE) running_find_references = type if(usr?.client) if(usr.client.running_find_references) @@ -28,7 +28,11 @@ //Time to search the whole game for our ref DoSearchVar(GLOB, "GLOB") //globals - log_reftracker("Finished searching globals") + log_reftracker("Finished searching GLOB") + + for(var/thing in global.vars) + DoSearchVar(thing, "Global variable -> [nameof(thing)]", search_time = starting_time) + log_reftracker("Finished searching global.vars") for(var/datum/thing in world) //atoms (don't beleive its lies) DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time) diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm index 997a8357da4..861e02ae4b5 100644 --- a/code/datums/repositories/crew.dm +++ b/code/datums/repositories/crew.dm @@ -1,74 +1,74 @@ -var/global/datum/repository/crew/crew_repository = new() - -/datum/repository/crew - var/list/cache_data - -/datum/repository/crew/New() - cache_data = list() - ..() - -/datum/repository/crew/proc/health_data(var/zLevel) - var/list/crewmembers = list() - if(!zLevel) - return crewmembers - - var/z_level = "[zLevel]" - var/datum/cache_entry/cache_entry = cache_data[z_level] - if(!cache_entry) - cache_entry = new/datum/cache_entry - cache_data[z_level] = cache_entry - - if(world.time < cache_entry.timestamp) - return cache_entry.data - - var/tracked = scan() - for(var/obj/item/clothing/under/C in tracked) - var/turf/pos = get_turf(C) - var/area/B = pos?.loc //VOREStation Add: No sensor in Dorm - if((C.has_sensor) && (pos?.z == zLevel) && (C.sensor_mode != SUIT_SENSOR_OFF) && !(B.block_suit_sensors) && !(is_jammed(C)) && !(is_vore_jammed(C))) //VOREStation Edit - if(istype(C.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C.loc - if(H.w_uniform != C) - continue - - var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1, "ref" = "\ref[H]") - - crewmemberData["sensor_type"] = C.sensor_mode - crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown") - crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job") - crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job") - - if(C.sensor_mode >= SUIT_SENSOR_BINARY) - crewmemberData["dead"] = H.stat == DEAD - - if(C.sensor_mode >= SUIT_SENSOR_VITAL) - crewmemberData["stat"] = H.stat - crewmemberData["oxy"] = round(H.getOxyLoss(), 1) - crewmemberData["tox"] = round(H.getToxLoss(), 1) - crewmemberData["fire"] = round(H.getFireLoss(), 1) - crewmemberData["brute"] = round(H.getBruteLoss(), 1) - - if(C.sensor_mode >= SUIT_SENSOR_TRACKING) - var/area/A = get_area(H) - crewmemberData["area"] = sanitize(A.get_name()) - crewmemberData["x"] = pos.x - crewmemberData["y"] = pos.y - crewmemberData["realZ"] = pos.z - crewmemberData["z"] = using_map.get_zlevel_name(pos.z) - - crewmembers[++crewmembers.len] = crewmemberData - - crewmembers = sortByKey(crewmembers, "name") - cache_entry.timestamp = world.time + 5 SECONDS - cache_entry.data = crewmembers - - return crewmembers - -/datum/repository/crew/proc/scan() - var/list/tracked = list() - for(var/mob/living/carbon/human/H in mob_list) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/C = H.w_uniform - if (C.has_sensor) - tracked |= C - return tracked +var/global/datum/repository/crew/crew_repository = new() + +/datum/repository/crew + var/list/cache_data + +/datum/repository/crew/New() + cache_data = list() + ..() + +/datum/repository/crew/proc/health_data(var/zLevel) + var/list/crewmembers = list() + if(!zLevel) + return crewmembers + + var/z_level = "[zLevel]" + var/datum/cache_entry/cache_entry = cache_data[z_level] + if(!cache_entry) + cache_entry = new/datum/cache_entry + cache_data[z_level] = cache_entry + + if(world.time < cache_entry.timestamp) + return cache_entry.data + + var/tracked = scan() + for(var/obj/item/clothing/under/C in tracked) + var/turf/pos = get_turf(C) + var/area/B = pos?.loc //VOREStation Add: No sensor in Dorm + if((C.has_sensor) && (pos?.z == zLevel) && (C.sensor_mode != SUIT_SENSOR_OFF) && !(B.block_suit_sensors) && !(is_jammed(C)) && !(is_vore_jammed(C))) //VOREStation Edit + if(istype(C.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = C.loc + if(H.w_uniform != C) + continue + + var/list/crewmemberData = list("dead"=0, "oxy"=-1, "tox"=-1, "fire"=-1, "brute"=-1, "area"="", "x"=-1, "y"=-1, "ref" = "\ref[H]") + + crewmemberData["sensor_type"] = C.sensor_mode + crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown") + crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job") + crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job") + + if(C.sensor_mode >= SUIT_SENSOR_BINARY) + crewmemberData["dead"] = H.stat == DEAD + + if(C.sensor_mode >= SUIT_SENSOR_VITAL) + crewmemberData["stat"] = H.stat + crewmemberData["oxy"] = round(H.getOxyLoss(), 1) + crewmemberData["tox"] = round(H.getToxLoss(), 1) + crewmemberData["fire"] = round(H.getFireLoss(), 1) + crewmemberData["brute"] = round(H.getBruteLoss(), 1) + + if(C.sensor_mode >= SUIT_SENSOR_TRACKING) + var/area/A = get_area(H) + crewmemberData["area"] = sanitize(A.get_name()) + crewmemberData["x"] = pos.x + crewmemberData["y"] = pos.y + crewmemberData["realZ"] = pos.z + crewmemberData["z"] = using_map.get_zlevel_name(pos.z) + + crewmembers[++crewmembers.len] = crewmemberData + + crewmembers = sortByKey(crewmembers, "name") + cache_entry.timestamp = world.time + 5 SECONDS + cache_entry.data = crewmembers + + return crewmembers + +/datum/repository/crew/proc/scan() + var/list/tracked = list() + for(var/mob/living/carbon/human/H in mob_list) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/C = H.w_uniform + if (C.has_sensor) + tracked |= C + return tracked diff --git a/code/datums/repositories/repository.dm b/code/datums/repositories/repository.dm index 04eee505401..511ea992c80 100644 --- a/code/datums/repositories/repository.dm +++ b/code/datums/repositories/repository.dm @@ -1,19 +1,19 @@ -/repository/New() - return - -/datum/cache_entry - var/timestamp - var/data - -/datum/cache_entry/New() - timestamp = world.time - -/datum/cache_entry/proc/is_valid() - return FALSE - -/datum/cache_entry/valid_until/New(var/valid_duration) - ..() - timestamp += valid_duration - -/datum/cache_entry/valid_until/is_valid() +/repository/New() + return + +/datum/cache_entry + var/timestamp + var/data + +/datum/cache_entry/New() + timestamp = world.time + +/datum/cache_entry/proc/is_valid() + return FALSE + +/datum/cache_entry/valid_until/New(var/valid_duration) + ..() + timestamp += valid_duration + +/datum/cache_entry/valid_until/is_valid() return world.time < timestamp \ No newline at end of file diff --git a/code/datums/soul_link.dm b/code/datums/soul_link.dm index d15049d9246..1a99d170c5b 100644 --- a/code/datums/soul_link.dm +++ b/code/datums/soul_link.dm @@ -1,152 +1,152 @@ -// A datum used to link multiple mobs together in some form. -// The code is from TG, however tweaked to be within the preferred code style. - -/mob/living - var/list/owned_soul_links // Soul links we are the owner of. - var/list/shared_soul_links // Soul links we are a/the sharer of. - -/mob/living/Destroy() - for(var/datum/soul_link/S as anything in owned_soul_links) - S.owner_died(FALSE) - qdel(S) // If the owner is destroy()'d, the soullink is destroy()'d. - owned_soul_links = null - for(var/datum/soul_link/S as anything in shared_soul_links) - S.sharer_died(FALSE) - S.remove_soul_sharer(src) // If a sharer is destroy()'d, they are simply removed. - shared_soul_links = null - return ..() - -// Keeps track of a Mob->Mob (potentially Player->Player) connection. -// Can be used to trigger actions on one party when events happen to another. -// Eg: shared deaths. -// Can be used to form a linked list of mob-hopping. -// Does NOT transfer with minds. -/datum/soul_link - var/mob/living/soul_owner - var/mob/living/soul_sharer - var/id // Optional ID, for tagging and finding specific instances. - -/datum/soul_link/Destroy() - if(soul_owner) - LAZYREMOVE(soul_owner.owned_soul_links, src) - soul_owner = null - if(soul_sharer) - LAZYREMOVE(soul_sharer.shared_soul_links, src) - soul_sharer = null - return ..() - -/datum/soul_link/proc/remove_soul_sharer(mob/living/sharer) - if(soul_sharer == sharer) - soul_sharer = null - LAZYREMOVE(sharer.shared_soul_links, src) - -// Used to assign variables, called primarily by soullink() -// Override this to create more unique soullinks (Eg: 1->Many relationships) -// Return TRUE/FALSE to return the soullink/null in soullink() -/datum/soul_link/proc/parse_args(mob/living/owner, mob/living/sharer) - if(!owner || !sharer) - return FALSE - soul_owner = owner - soul_sharer = sharer - LAZYADD(owner.owned_soul_links, src) - LAZYADD(sharer.shared_soul_links, src) - return TRUE - -// Runs after /living death() -// Override this for content. -/datum/soul_link/proc/owner_died(gibbed, mob/living/owner) - -// Runs after /living death() -// Override this for content. -/datum/soul_link/proc/sharer_died(gibbed, mob/living/owner) - -// Quick-use helper. -/proc/soul_link(typepath, ...) - var/datum/soul_link/S = new typepath() - if(S.parse_args(arglist(args.Copy(2, 0)))) - return S - - -///////////////// -// MULTISHARER // -///////////////// -// Abstract soullink for use with 1 Owner -> Many Sharer setups -/datum/soul_link/multi_sharer - var/list/soul_sharers - -/datum/soul_link/multi_sharer/parse_args(mob/living/owner, list/sharers) - if(!owner || !LAZYLEN(sharers)) - return FALSE - soul_owner = owner - soul_sharers = sharers - LAZYADD(owner.owned_soul_links, src) - for(var/mob/living/L as anything in sharers) - LAZYADD(L.shared_soul_links, src) - return TRUE - -/datum/soul_link/multi_sharer/remove_soul_sharer(mob/living/sharer) - LAZYREMOVE(soul_sharers, sharer) - - -///////////////// -// SHARED FATE // -///////////////// -// When the soulowner dies, the soulsharer dies, and vice versa -// This is intended for two players(or AI) and two mobs - -/datum/soul_link/shared_fate/owner_died(gibbed, mob/living/owner) - if(soul_sharer) - soul_sharer.death(gibbed) - -/datum/soul_link/shared_fate/sharer_died(gibbed, mob/living/sharer) - if(soul_owner) - soul_owner.death(gibbed) - -////////////// -// ONE WAY // -////////////// -// When the soul owner dies, the soul sharer dies, but NOT vice versa. -// This is intended for two players (or AI) and two mobs. - -/datum/soul_link/one_way/owner_died(gibbed, mob/living/owner) - if(soul_sharer) - soul_sharer.dust(FALSE) - -///////////////// -// SHARED BODY // -///////////////// -// When the soulsharer dies, they're placed in the soulowner, who remains alive -// If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying) -// This one is intended for one player moving between many mobs - -/datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner) - if(soul_owner && soul_sharer) - if(soul_sharer.mind) - soul_sharer.mind.transfer_to(soul_owner) - soul_sharer.death(gibbed) - -/datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer) - if(soul_owner && soul_sharer && soul_sharer.mind) - soul_sharer.mind.transfer_to(soul_owner) - - - -////////////////////// -// REPLACEMENT POOL // -////////////////////// -// When the owner dies, one of the sharers is placed in the owner's body, fully healed -// Sort of a "winner-stays-on" soullink -// Gibbing ends it immediately - -/datum/soul_link/multi_sharer/replacement_pool/owner_died(gibbed, mob/living/owner) - if(LAZYLEN(soul_sharers) && !gibbed) //let's not put them in some gibs - var/list/souls = shuffle(soul_sharers.Copy()) - for(var/mob/living/L as anything in souls) - if(L.stat != DEAD && L.mind) - L.mind.transfer_to(soul_owner) - soul_owner.revive(TRUE, TRUE) - L.death(FALSE) - -// Lose your claim to the throne! -/datum/soul_link/multi_sharer/replacement_pool/sharer_died(gibbed, mob/living/sharer) - remove_soul_sharer(sharer) +// A datum used to link multiple mobs together in some form. +// The code is from TG, however tweaked to be within the preferred code style. + +/mob/living + var/list/owned_soul_links // Soul links we are the owner of. + var/list/shared_soul_links // Soul links we are a/the sharer of. + +/mob/living/Destroy() + for(var/datum/soul_link/S as anything in owned_soul_links) + S.owner_died(FALSE) + qdel(S) // If the owner is destroy()'d, the soullink is destroy()'d. + owned_soul_links = null + for(var/datum/soul_link/S as anything in shared_soul_links) + S.sharer_died(FALSE) + S.remove_soul_sharer(src) // If a sharer is destroy()'d, they are simply removed. + shared_soul_links = null + return ..() + +// Keeps track of a Mob->Mob (potentially Player->Player) connection. +// Can be used to trigger actions on one party when events happen to another. +// Eg: shared deaths. +// Can be used to form a linked list of mob-hopping. +// Does NOT transfer with minds. +/datum/soul_link + var/mob/living/soul_owner + var/mob/living/soul_sharer + var/id // Optional ID, for tagging and finding specific instances. + +/datum/soul_link/Destroy() + if(soul_owner) + LAZYREMOVE(soul_owner.owned_soul_links, src) + soul_owner = null + if(soul_sharer) + LAZYREMOVE(soul_sharer.shared_soul_links, src) + soul_sharer = null + return ..() + +/datum/soul_link/proc/remove_soul_sharer(mob/living/sharer) + if(soul_sharer == sharer) + soul_sharer = null + LAZYREMOVE(sharer.shared_soul_links, src) + +// Used to assign variables, called primarily by soullink() +// Override this to create more unique soullinks (Eg: 1->Many relationships) +// Return TRUE/FALSE to return the soullink/null in soullink() +/datum/soul_link/proc/parse_args(mob/living/owner, mob/living/sharer) + if(!owner || !sharer) + return FALSE + soul_owner = owner + soul_sharer = sharer + LAZYADD(owner.owned_soul_links, src) + LAZYADD(sharer.shared_soul_links, src) + return TRUE + +// Runs after /living death() +// Override this for content. +/datum/soul_link/proc/owner_died(gibbed, mob/living/owner) + +// Runs after /living death() +// Override this for content. +/datum/soul_link/proc/sharer_died(gibbed, mob/living/owner) + +// Quick-use helper. +/proc/soul_link(typepath, ...) + var/datum/soul_link/S = new typepath() + if(S.parse_args(arglist(args.Copy(2, 0)))) + return S + + +///////////////// +// MULTISHARER // +///////////////// +// Abstract soullink for use with 1 Owner -> Many Sharer setups +/datum/soul_link/multi_sharer + var/list/soul_sharers + +/datum/soul_link/multi_sharer/parse_args(mob/living/owner, list/sharers) + if(!owner || !LAZYLEN(sharers)) + return FALSE + soul_owner = owner + soul_sharers = sharers + LAZYADD(owner.owned_soul_links, src) + for(var/mob/living/L as anything in sharers) + LAZYADD(L.shared_soul_links, src) + return TRUE + +/datum/soul_link/multi_sharer/remove_soul_sharer(mob/living/sharer) + LAZYREMOVE(soul_sharers, sharer) + + +///////////////// +// SHARED FATE // +///////////////// +// When the soulowner dies, the soulsharer dies, and vice versa +// This is intended for two players(or AI) and two mobs + +/datum/soul_link/shared_fate/owner_died(gibbed, mob/living/owner) + if(soul_sharer) + soul_sharer.death(gibbed) + +/datum/soul_link/shared_fate/sharer_died(gibbed, mob/living/sharer) + if(soul_owner) + soul_owner.death(gibbed) + +////////////// +// ONE WAY // +////////////// +// When the soul owner dies, the soul sharer dies, but NOT vice versa. +// This is intended for two players (or AI) and two mobs. + +/datum/soul_link/one_way/owner_died(gibbed, mob/living/owner) + if(soul_sharer) + soul_sharer.dust(FALSE) + +///////////////// +// SHARED BODY // +///////////////// +// When the soulsharer dies, they're placed in the soulowner, who remains alive +// If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying) +// This one is intended for one player moving between many mobs + +/datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner) + if(soul_owner && soul_sharer) + if(soul_sharer.mind) + soul_sharer.mind.transfer_to(soul_owner) + soul_sharer.death(gibbed) + +/datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer) + if(soul_owner && soul_sharer && soul_sharer.mind) + soul_sharer.mind.transfer_to(soul_owner) + + + +////////////////////// +// REPLACEMENT POOL // +////////////////////// +// When the owner dies, one of the sharers is placed in the owner's body, fully healed +// Sort of a "winner-stays-on" soullink +// Gibbing ends it immediately + +/datum/soul_link/multi_sharer/replacement_pool/owner_died(gibbed, mob/living/owner) + if(LAZYLEN(soul_sharers) && !gibbed) //let's not put them in some gibs + var/list/souls = shuffle(soul_sharers.Copy()) + for(var/mob/living/L as anything in souls) + if(L.stat != DEAD && L.mind) + L.mind.transfer_to(soul_owner) + soul_owner.revive(TRUE, TRUE) + L.death(FALSE) + +// Lose your claim to the throne! +/datum/soul_link/multi_sharer/replacement_pool/sharer_died(gibbed, mob/living/sharer) + remove_soul_sharer(sharer) diff --git a/code/datums/sun.dm b/code/datums/sun.dm index 69b725f305e..d54ad281387 100644 --- a/code/datums/sun.dm +++ b/code/datums/sun.dm @@ -1,41 +1,41 @@ -/datum/sun - var/angle - var/dx - var/dy - var/rate - var/solar_next_update // last time the sun position was checked and adjusted - -/datum/sun/New() - rate = rand(50,200)/100 // 50% - 200% of standard rotation - if(prob(50)) // same chance to rotate clockwise than counter-clockwise - rate = -rate - angle = rand (0,360) // the station position to the sun is randomised at round start - -// calculate the sun's position given the time of day -// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute. -// a full rotation thus take a game hour in that case -/datum/sun/proc/calc_position() - angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate - // now calculate and cache the (dx,dy) increments for line drawing - - var/s = sin(angle) - var/c = cos(angle) - - // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)" - // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions - - if( abs(s) < abs(c)) - - dx = s / abs(c) - dy = c / abs(c) - - else - dx = s/abs(s) - dy = c / abs(s) - - //now tell the solar control computers to update their status and linked devices - for(var/obj/machinery/power/solar_control/SC in GLOB.solars_list) - if(!SC.powernet) - GLOB.solars_list.Remove(SC) - continue - SC.update() +/datum/sun + var/angle + var/dx + var/dy + var/rate + var/solar_next_update // last time the sun position was checked and adjusted + +/datum/sun/New() + rate = rand(50,200)/100 // 50% - 200% of standard rotation + if(prob(50)) // same chance to rotate clockwise than counter-clockwise + rate = -rate + angle = rand (0,360) // the station position to the sun is randomised at round start + +// calculate the sun's position given the time of day +// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute. +// a full rotation thus take a game hour in that case +/datum/sun/proc/calc_position() + angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate + // now calculate and cache the (dx,dy) increments for line drawing + + var/s = sin(angle) + var/c = cos(angle) + + // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)" + // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions + + if( abs(s) < abs(c)) + + dx = s / abs(c) + dy = c / abs(c) + + else + dx = s/abs(s) + dy = c / abs(s) + + //now tell the solar control computers to update their status and linked devices + for(var/obj/machinery/power/solar_control/SC in GLOB.solars_list) + if(!SC.powernet) + GLOB.solars_list.Remove(SC) + continue + SC.update() diff --git a/code/datums/supplypacks/hardsuits.dm b/code/datums/supplypacks/hardsuits.dm new file mode 100644 index 00000000000..fe815c7ce40 --- /dev/null +++ b/code/datums/supplypacks/hardsuits.dm @@ -0,0 +1,112 @@ +/* +* Here is where any supply packs +* related to hardsuits live. +*/ + + +/datum/supply_pack/hardsuits + group = "Hardsuits" + +/datum/supply_pack/hardsuits/eva_rig + name = "eva hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/eva = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "eva hardsuit crate" + access = list(access_mining, + access_eva, + access_pilot) + one_access = TRUE + +/datum/supply_pack/hardsuits/mining_rig + name = "industrial hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/industrial = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "industrial hardsuit crate" + access = list(access_mining, + access_eva) + one_access = TRUE + +/datum/supply_pack/hardsuits/medical_rig + name = "medical hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/medical = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "medical hardsuit crate" + access = access_medical + +/datum/supply_pack/hardsuits/security_rig + name = "hazard hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/hazard = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "hazard hardsuit crate" + access = access_armory + +/datum/supply_pack/hardsuits/science_rig + name = "ami hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/hazmat = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "ami hardsuit crate" + access = access_rd + +/datum/supply_pack/hardsuits/ce_rig + name = "advanced hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/ce = 1 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure/gear + containername = "advanced hardsuit crate" + access = access_ce + +/datum/supply_pack/hardsuits/com_medical_rig + name = "commonwealth medical hardsuit (loaded)" + contains = list( + /obj/item/weapon/rig/baymed/equipped = 1 + ) + cost = 250 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Commonwealth medical hardsuit crate" + access = access_medical + +/datum/supply_pack/hardsuits/com_engineering_rig + name = "commonwealth engineering hardsuit (loaded)" + contains = list( + /obj/item/weapon/rig/bayeng/equipped = 1 + ) + cost = 250 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Commonwealth engineering hardsuit crate" + access = access_engine + +/datum/supply_pack/hardsuits/breacher_rig + name = "unathi breacher hardsuit (empty)" + contains = list( + /obj/item/weapon/rig/breacher = 1 + ) + cost = 250 + containertype = /obj/structure/closet/crate/secure/gear + containername = "unathi breacher hardsuit crate" + access = access_armory + +/datum/supply_pack/hardsuits/zero_rig + name = "null hardsuit (jets)" + contains = list( + /obj/item/weapon/rig/zero = 1 + ) + cost = 75 + containertype = /obj/structure/closet/crate/secure/gear + containername = "null hardsuit crate" \ No newline at end of file diff --git a/code/datums/supplypacks/hospitality_vr.dm b/code/datums/supplypacks/hospitality_vr.dm index be7fc11606a..ceb35b6c5d6 100644 --- a/code/datums/supplypacks/hospitality_vr.dm +++ b/code/datums/supplypacks/hospitality_vr.dm @@ -88,3 +88,35 @@ cost = 50 containertype = /obj/structure/closet/crate/freezer containername = "Chinese takeout crate" + +/datum/supply_pack/randomised/hospitality/jaffacake + contains = list( + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/jaffacake + ) + name = "Desatti jaffa cake crate" + cost = 25 + containertype = /obj/structure/closet/crate/freezer + containername = "Desatti jaffa cake crate" + +/datum/supply_pack/randomised/hospitality/sweets + num_contained = 5 + contains = list( + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/winegum, + /obj/item/weapon/storage/box/saucer, + /obj/item/weapon/storage/box/shrimpsandbananas, + /obj/item/weapon/storage/box/rhubarbcustard + ) + name = "Sweets crate" + cost = 25 + containertype = /obj/structure/closet/crate/freezer + containername = "Sweets crate" diff --git a/code/datums/supplypacks/misc_vr.dm b/code/datums/supplypacks/misc_vr.dm index 781be0deb97..459a3b1a1b8 100644 --- a/code/datums/supplypacks/misc_vr.dm +++ b/code/datums/supplypacks/misc_vr.dm @@ -191,4 +191,4 @@ ) cost = 20 containertype = /obj/structure/closet/crate - containername = "emergency beacons crate" \ No newline at end of file + containername = "emergency beacons crate" diff --git a/code/datums/supplypacks/recreation.dm b/code/datums/supplypacks/recreation.dm index ca19cc342af..e512b92ebcf 100644 --- a/code/datums/supplypacks/recreation.dm +++ b/code/datums/supplypacks/recreation.dm @@ -38,7 +38,7 @@ /datum/supply_pack/recreation/donksoftborg name = "Donk-Soft Cyborg Blaster Crate" contains = list( - /obj/item/borg/upgrade/toygun = 2, + /obj/item/borg/upgrade/no_prod/toygun = 2, ) cost = 35 containertype = /obj/structure/closet/crate/allico @@ -136,4 +136,4 @@ contains = list( /obj/machinery/porta_turret/lasertag/blue, /obj/machinery/porta_turret/lasertag/red - ) \ No newline at end of file + ) diff --git a/code/datums/supplypacks/supplypacks.dm b/code/datums/supplypacks/supplypacks.dm index 14e97cce7a9..19afa61980b 100644 --- a/code/datums/supplypacks/supplypacks.dm +++ b/code/datums/supplypacks/supplypacks.dm @@ -24,6 +24,7 @@ var/list/all_supply_groups = list("Atmospherics", "Security", "Supplies", "Vendor Refills", + "Hardsuits", "Voidsuits") /datum/supply_pack diff --git a/code/datums/supplypacks/voidsuits_vr.dm b/code/datums/supplypacks/voidsuits_vr.dm index 24598644429..026f3bc4cee 100644 --- a/code/datums/supplypacks/voidsuits_vr.dm +++ b/code/datums/supplypacks/voidsuits_vr.dm @@ -1,152 +1,152 @@ -/datum/supply_pack/voidsuits/explorer - name = "Exploration voidsuits" - contains = list( - /obj/item/clothing/suit/space/void/exploration = 2, - /obj/item/clothing/head/helmet/space/void/exploration = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/shoes/magboots = 2, - /obj/item/weapon/tank/oxygen = 2 - ) - cost = 45 - containertype = /obj/structure/closet/crate/secure - containername = "Exploration voidsuit crate" - access = access_explorer - -/datum/supply_pack/voidsuits/explorer_medic - name = "Expedition Medic voidsuits" - contains = list( - /obj/item/clothing/suit/space/void/exploration = 2, - /obj/item/clothing/head/helmet/space/void/exploration = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/shoes/magboots = 2, - /obj/item/weapon/tank/oxygen = 2 - ) - cost = 45 - containertype = /obj/structure/closet/crate/secure - containername = "Expedition Medic voidsuit crate" - access = access_explorer - -/datum/supply_pack/voidsuits/pilot - name = "Pilot voidsuits" - contains = list( - /obj/item/clothing/suit/space/void/pilot = 1, - /obj/item/clothing/head/helmet/space/void/pilot = 1, - /obj/item/clothing/mask/breath = 1, - /obj/item/clothing/shoes/magboots = 1, - /obj/item/weapon/tank/oxygen = 1 - ) - cost = 20 - containertype = /obj/structure/closet/crate/secure - containername = "Pilot voidsuit crate" - access = access_pilot - - -// Surplus! -/datum/supply_pack/voidsuits/com_mining - name = "Commonwealth mining voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/mining/alt2, - /obj/item/clothing/head/helmet/space/void/mining/alt2 - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth mining voidsuit crate" - access = access_mining - -/datum/supply_pack/voidsuits/com_anomaly - name = "Commonwealth anomaly suit" - contains = list( - /obj/item/clothing/suit/space/anomaly/alt, - /obj/item/clothing/head/helmet/space/anomaly/alt - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth anomaly suit crate" - access = access_xenoarch - -/datum/supply_pack/voidsuits/com_riot - name = "Commonwealth riot voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/security/riot/alt, - /obj/item/clothing/head/helmet/space/void/security/riot/alt - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth riot voidsuit crate" - access = access_brig - -/datum/supply_pack/voidsuits/com_pilot - name = "Commonwealth pilot voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/pilot/alt2, - /obj/item/clothing/head/helmet/space/void/pilot/alt2 - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth pilot voidsuit crate" - access = access_pilot - -/datum/supply_pack/voidsuits/com_medical - name = "Commonwealth medical voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/medical/alt2, - /obj/item/clothing/head/helmet/space/void/medical/alt2 - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth medical voidsuit crate" -/datum/supply_pack/voidsuits/com_explore - - name = "Commonwealth exploration voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/exploration/alt2, - /obj/item/clothing/head/helmet/space/void/exploration/alt2 - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth exploration voidsuit crate" - access = access_explorer - -/datum/supply_pack/voidsuits/com_engineer - name = "Commonwealth engineering voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/engineering/alt2, - /obj/item/clothing/head/helmet/space/void/engineering/alt2 - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth engineering voidsuit crate" - access = access_engine - -/datum/supply_pack/voidsuits/com_atmos - name = "Commonwealth atmos voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/atmos/alt2, - /obj/item/clothing/head/helmet/space/void/atmos/alt2 - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth atmos voidsuit crate" - access = access_atmospherics - -/datum/supply_pack/voidsuits/com_captain - name = "Commonwealth captain voidsuit" - contains = list( - /obj/item/clothing/suit/space/void/captain/alt, - /obj/item/clothing/head/helmet/space/void/captain/alt - ) - cost = 150 - containertype = /obj/structure/closet/crate/secure - name = "Commonwealth captain voidsuit crate" - access = access_captain - -/datum/supply_pack/voidsuits/csc_breaker - name = "Shipbreaker's Industrial Suit (inc. jetpack)" - contains = list( - /obj/item/clothing/suit/space/void/salvagecorp_shipbreaker, - /obj/item/clothing/head/helmet/space/void/salvagecorp_shipbreaker, - /obj/item/weapon/tank/jetpack/breaker - ) - cost = 100 - containertype = /obj/structure/closet/crate/secure - name = "Coyote Salvage Corp industrial voidsuit crate" +/datum/supply_pack/voidsuits/explorer + name = "Exploration voidsuits" + contains = list( + /obj/item/clothing/suit/space/void/exploration = 2, + /obj/item/clothing/head/helmet/space/void/exploration = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/shoes/magboots = 2, + /obj/item/weapon/tank/oxygen = 2 + ) + cost = 45 + containertype = /obj/structure/closet/crate/secure + containername = "Exploration voidsuit crate" + access = access_explorer + +/datum/supply_pack/voidsuits/explorer_medic + name = "Expedition Medic voidsuits" + contains = list( + /obj/item/clothing/suit/space/void/exploration = 2, + /obj/item/clothing/head/helmet/space/void/exploration = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/shoes/magboots = 2, + /obj/item/weapon/tank/oxygen = 2 + ) + cost = 45 + containertype = /obj/structure/closet/crate/secure + containername = "Expedition Medic voidsuit crate" + access = access_explorer + +/datum/supply_pack/voidsuits/pilot + name = "Pilot voidsuits" + contains = list( + /obj/item/clothing/suit/space/void/pilot = 1, + /obj/item/clothing/head/helmet/space/void/pilot = 1, + /obj/item/clothing/mask/breath = 1, + /obj/item/clothing/shoes/magboots = 1, + /obj/item/weapon/tank/oxygen = 1 + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure + containername = "Pilot voidsuit crate" + access = access_pilot + + +// Surplus! +/datum/supply_pack/voidsuits/com_mining + name = "Commonwealth mining voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/mining/alt2, + /obj/item/clothing/head/helmet/space/void/mining/alt2 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth mining voidsuit crate" + access = access_mining + +/datum/supply_pack/voidsuits/com_anomaly + name = "Commonwealth anomaly suit" + contains = list( + /obj/item/clothing/suit/space/anomaly/alt, + /obj/item/clothing/head/helmet/space/anomaly/alt + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth anomaly suit crate" + access = access_xenoarch + +/datum/supply_pack/voidsuits/com_riot + name = "Commonwealth riot voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/security/riot/alt, + /obj/item/clothing/head/helmet/space/void/security/riot/alt + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth riot voidsuit crate" + access = access_brig + +/datum/supply_pack/voidsuits/com_pilot + name = "Commonwealth pilot voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/pilot/alt2, + /obj/item/clothing/head/helmet/space/void/pilot/alt2 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth pilot voidsuit crate" + access = access_pilot + +/datum/supply_pack/voidsuits/com_medical + name = "Commonwealth medical voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/medical/alt2, + /obj/item/clothing/head/helmet/space/void/medical/alt2 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth medical voidsuit crate" +/datum/supply_pack/voidsuits/com_explore + + name = "Commonwealth exploration voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/exploration/alt2, + /obj/item/clothing/head/helmet/space/void/exploration/alt2 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth exploration voidsuit crate" + access = access_explorer + +/datum/supply_pack/voidsuits/com_engineer + name = "Commonwealth engineering voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/engineering/alt2, + /obj/item/clothing/head/helmet/space/void/engineering/alt2 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth engineering voidsuit crate" + access = access_engine + +/datum/supply_pack/voidsuits/com_atmos + name = "Commonwealth atmos voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/atmos/alt2, + /obj/item/clothing/head/helmet/space/void/atmos/alt2 + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth atmos voidsuit crate" + access = access_atmospherics + +/datum/supply_pack/voidsuits/com_captain + name = "Commonwealth captain voidsuit" + contains = list( + /obj/item/clothing/suit/space/void/captain/alt, + /obj/item/clothing/head/helmet/space/void/captain/alt + ) + cost = 150 + containertype = /obj/structure/closet/crate/secure + name = "Commonwealth captain voidsuit crate" + access = access_captain + +/datum/supply_pack/voidsuits/csc_breaker + name = "Shipbreaker's Industrial Suit (inc. jetpack)" + contains = list( + /obj/item/clothing/suit/space/void/salvagecorp_shipbreaker, + /obj/item/clothing/head/helmet/space/void/salvagecorp_shipbreaker, + /obj/item/weapon/tank/jetpack/breaker + ) + cost = 100 + containertype = /obj/structure/closet/crate/secure + name = "Coyote Salvage Corp industrial voidsuit crate" diff --git a/code/datums/underwear/bottom.dm b/code/datums/underwear/bottom.dm index 358fe201045..d32d7952c10 100644 --- a/code/datums/underwear/bottom.dm +++ b/code/datums/underwear/bottom.dm @@ -95,4 +95,14 @@ /datum/category_item/underwear/bottom/onepiece name = "Swimming One Piece" icon_state = "onepiece" - has_color = TRUE \ No newline at end of file + has_color = TRUE + +/datum/category_item/underwear/top/onepiece_alt + name = "Swimming One Piece, Alt" + icon_state = "swim_onepiece" + has_color = TRUE + +/datum/category_item/underwear/top/onepiece_strapless + name = "Swimming One Piece, Strapless" + icon_state = "swim_strapless_onepiece" + has_color = TRUE diff --git a/code/datums/underwear/socks.dm b/code/datums/underwear/socks.dm index ac29b579d19..e06782b8d2d 100644 --- a/code/datums/underwear/socks.dm +++ b/code/datums/underwear/socks.dm @@ -208,4 +208,8 @@ /datum/category_item/underwear/socks/stirrup_thigh_black name = "Black Stirrup Stockings" icon_state = "leggings-stir-black" - has_color = TRUE \ No newline at end of file + has_color = TRUE + +/datum/category_item/underwear/socks/stirrup_pantyhose + name = "Pantyhose, stirrup" + icon_state = "pantyhose-stir" diff --git a/code/datums/underwear/top.dm b/code/datums/underwear/top.dm index 5af7870f04e..336bebd1cc5 100644 --- a/code/datums/underwear/top.dm +++ b/code/datums/underwear/top.dm @@ -51,10 +51,20 @@ name = "Fishnet top" icon_state = "fishnet_body" +/datum/category_item/underwear/top/fishnet_base_alt + name = "Fishnet top, alt" + icon_state = "fishnet_body_alt" + has_color = TRUE + /datum/category_item/underwear/top/fishnet_sleeves name = "Fishnet with sleeves" icon_state = "fishnet_sleeves" +/datum/category_item/underwear/top/fishnet_sleeves_alt + name = "Fishnet with sleeves, alt" + icon_state = "fishnet_sleeves_alt" + has_color = TRUE + /datum/category_item/underwear/top/fishnet_gloves name = "Fishnet with gloves" icon_state = "fishnet_gloves" @@ -95,4 +105,4 @@ /datum/category_item/underwear/top/swimtop name = "Swimming Top" icon_state = "swimtop" - has_color = TRUE \ No newline at end of file + has_color = TRUE diff --git a/code/datums/underwear/undershirts.dm b/code/datums/underwear/undershirts.dm index 2c32ad8e767..9b7571e1d15 100644 --- a/code/datums/underwear/undershirts.dm +++ b/code/datums/underwear/undershirts.dm @@ -1,229 +1,254 @@ -/datum/category_item/underwear/undershirt/none - is_default = TRUE - name = "None" - always_last = TRUE - -/datum/category_item/underwear/undershirt/shirt - name = "Shirt" - icon_state = "undershirt" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_fem - name = "Babydoll shirt" - icon_state = "undershirt_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long - name = "Longsleeve Shirt" - icon_state = "undershirt_long" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long_s - name = "Shirt, button-down" - icon_state = "shirt_long_s" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long_fem - name = "Longsleeve Shirt, feminine" - icon_state = "undershirt_long_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shirt_long_female_s - name = "Button-down Shirt, feminine" - icon_state = "shirt_long_female_s" - has_color = TRUE - - -/datum/category_item/underwear/undershirt/fishnet_simple - name = "Fishnet shirt" - icon_state = "fishnet_simple" - -/datum/category_item/underwear/undershirt/tank_top - name = "Tank top" - icon_state = "tanktop" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_alt - name = "Tank top, alt" - icon_state = "tanktop_alt" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_alt_fem - name = "Tank top, alt, feminine" - icon_state = "tanktop_alt_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_alt_fem_vneck - name = "Tank top, feminine, v-neck" - icon_state = "tanktop_alt_fem_vneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_cropped_vneck - name = "Tank top, feminine, cropped & v-neck" - icon_state = "tanktop_cropped_vneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/tank_top_fire - name = "Tank top, fire" - icon_state = "tank_fire_s" - -/datum/category_item/underwear/undershirt/tank_top_fire_fem - name = "Tank top, fire, feminine" - icon_state = "tank_fire_fem_s" - -/datum/category_item/underwear/undershirt/tank_top_rainbow - name = "Tank top, rainbow" - icon_state = "tank_rainbow_s" - -/datum/category_item/underwear/undershirt/tank_top_stripes - name = "Tank top, striped" - icon_state = "tank_stripes_s" - -/datum/category_item/underwear/undershirt/tank_top_sun - name = "Tank top, sun" - icon_state = "tank_sun_s" - -/datum/category_item/underwear/undershirt/shirt_heart - name = "Shirt, heart" - icon_state = "lover_s" - -/datum/category_item/underwear/undershirt/shirt_heart_fem - name = "Shirt, heart, babydoll" - icon_state = "lover_fem_s" - -/datum/category_item/underwear/undershirt/shirt_nt - name = "Shirt, NT" - icon_state = "shirt_nano_s" - -/datum/category_item/underwear/undershirt/shirt_love_nt - name = "Shirt, I<3NT" - icon_state = "ilovent_s" - -/datum/category_item/underwear/undershirt/shirt_love_nt_fem - name = "Shirt, I<3NT, babydoll" - icon_state = "ilovent_fem_s" - -/datum/category_item/underwear/undershirt/shortsleeve_shirt - name = "Shortsleeve shirt" - icon_state = "shortsleeve" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem - name = "Shortsleeve babydoll shirt" - icon_state = "shortsleeve_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem_vneck - name = "Shortsleeve babydoll shirt, v-neck" - icon_state = "shortsleeve_fem_vneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/polo_shirt - name = "Polo shirt" - icon_state = "polo" - has_color = TRUE - -/datum/category_item/underwear/undershirt/sport_shirt_green - name = "Sport shirt, green" - icon_state = "greenshirtsport_s" - -/datum/category_item/underwear/undershirt/sport_shirt_red - name = "Sport shirt, red" - icon_state = "redshirtsport_s" - -/datum/category_item/underwear/undershirt/sport_shirt_blue - name = "Sport shirt, blue" - icon_state = "blueshirtsport_s" - -/datum/category_item/underwear/undershirt/shirt_tiedye - name = "Shirt, tiedye" - icon_state = "shirt_tiedye_s" - -/datum/category_item/underwear/undershirt/shirt_blue_striped - name = "Shirt, blue stripes" - icon_state = "shirt_stripes_s" - -/datum/category_item/underwear/undershirt/bowling - name = "Bowling Shirt, Red" - icon_state = "bowling" - -/datum/category_item/underwear/undershirt/bowlingp - name = "Bowling Shirt, Pink" - icon_state = "bowlingp" - -/datum/category_item/underwear/undershirt/bowlinga - name = "Bowling Shirt, Aqua" - icon_state = "bowlinga" - -/datum/category_item/underwear/undershirt/bowlingw - name = "Bowling Shirt, White" - icon_state = "bowlingw" - -/datum/category_item/underwear/undershirt/longjon - name = "Long John Shirt" - icon_state = "ljont" - has_color = TRUE - -/datum/category_item/underwear/undershirt/longstripe_black - name = "Longsleeve Striped Shirt, Black" - icon_state = "longstripe" - -/datum/category_item/underwear/undershirt/longstripe_blue - name = "Longsleeve Striped Shirt, Blue" - icon_state = "longstripe_blue" - -/datum/category_item/underwear/undershirt/tiedye - name = "Tiedye Shirt" - icon_state = "tiedye" - -/datum/category_item/underwear/undershirt/longstripe_pink - name = "Longsleeve Striped Shirt, Pink" - icon_state = "longstripe_pink_s" - -/datum/category_item/underwear/undershirt/wingshirt - name = "Pink Wing Shirt" - icon_state = "wing_shirt_s" - -/datum/category_item/underwear/undershirt/pinkblack_tshirt - name = "Pink and Black T-Shirt" - icon_state = "pinkblack_tshirt" - -/datum/category_item/underwear/undershirt/turtle - name = "Turtleneck" - icon_state = "turtleneck" - has_color = TRUE - -/datum/category_item/underwear/undershirt/sleevelessturtle - name = "Turtleneck, Sleeveless" - icon_state = "sleevelessturtle" - has_color = TRUE - -/datum/category_item/underwear/undershirt/dress_shirt_fem - name = "Dress shirt, feminine" - icon_state = "undershirt_dress_fem" - has_color = TRUE - -/datum/category_item/underwear/undershirt/dress_shirt - name = "Dress shirt, masculine" - icon_state = "undershirt_dress" - has_color = TRUE - -/datum/category_item/underwear/undershirt/midriff - name = "Tanktop, midriff" - icon_state = "tank_midriff" - has_color = TRUE - -/datum/category_item/underwear/undershirt/midriffshort - name = "Tanktop, midriff, short" - icon_state = "tank_midriff_short" - has_color = TRUE - -/datum/category_item/underwear/undershirt/shibari - name = "Shibari Binding" - icon_state = "shibari" - has_color = TRUE - -/datum/category_item/underwear/undershirt/leotard - name = "Leotard" - icon_state = "leotard" - has_color = TRUE \ No newline at end of file +/datum/category_item/underwear/undershirt/none + is_default = TRUE + name = "None" + always_last = TRUE + +/datum/category_item/underwear/undershirt/shirt + name = "Shirt" + icon_state = "undershirt" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_fem + name = "Babydoll shirt" + icon_state = "undershirt_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long + name = "Longsleeve Shirt" + icon_state = "undershirt_long" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long_s + name = "Shirt, button-down" + icon_state = "shirt_long_s" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long_fem + name = "Longsleeve Shirt, feminine" + icon_state = "undershirt_long_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shirt_long_female_s + name = "Button-down Shirt, feminine" + icon_state = "shirt_long_female_s" + has_color = TRUE + + +/datum/category_item/underwear/undershirt/fishnet_simple + name = "Fishnet shirt" + icon_state = "fishnet_simple" + +/datum/category_item/underwear/undershirt/tank_top + name = "Tank top" + icon_state = "tanktop" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt + name = "Tank top, alt" + icon_state = "tanktop_alt" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt_fem + name = "Tank top, alt, feminine" + icon_state = "tanktop_alt_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt_fem_vneck + name = "Tank top, feminine, v-neck" + icon_state = "tanktop_alt_fem_vneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_cropped_vneck + name = "Tank top, feminine, cropped & v-neck" + icon_state = "tanktop_cropped_vneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_fire + name = "Tank top, fire" + icon_state = "tank_fire_s" + +/datum/category_item/underwear/undershirt/tank_top_fire_fem + name = "Tank top, fire, feminine" + icon_state = "tank_fire_fem_s" + +/datum/category_item/underwear/undershirt/tank_top_rainbow + name = "Tank top, rainbow" + icon_state = "tank_rainbow_s" + +/datum/category_item/underwear/undershirt/tank_top_stripes + name = "Tank top, striped" + icon_state = "tank_stripes_s" + +/datum/category_item/underwear/undershirt/tank_top_sun + name = "Tank top, sun" + icon_state = "tank_sun_s" + +/datum/category_item/underwear/undershirt/shirt_heart + name = "Shirt, heart" + icon_state = "lover_s" + +/datum/category_item/underwear/undershirt/shirt_heart_fem + name = "Shirt, heart, babydoll" + icon_state = "lover_fem_s" + +/datum/category_item/underwear/undershirt/shirt_nt + name = "Shirt, NT" + icon_state = "shirt_nano_s" + +/datum/category_item/underwear/undershirt/shirt_love_nt + name = "Shirt, I<3NT" + icon_state = "ilovent_s" + +/datum/category_item/underwear/undershirt/shirt_love_nt_fem + name = "Shirt, I<3NT, babydoll" + icon_state = "ilovent_fem_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt + name = "Shortsleeve shirt" + icon_state = "shortsleeve" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem + name = "Shortsleeve babydoll shirt" + icon_state = "shortsleeve_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_fem_vneck + name = "Shortsleeve babydoll shirt, v-neck" + icon_state = "shortsleeve_fem_vneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/polo_shirt + name = "Polo shirt" + icon_state = "polo" + has_color = TRUE + +/datum/category_item/underwear/undershirt/sport_shirt_green + name = "Sport shirt, green" + icon_state = "greenshirtsport_s" + +/datum/category_item/underwear/undershirt/sport_shirt_red + name = "Sport shirt, red" + icon_state = "redshirtsport_s" + +/datum/category_item/underwear/undershirt/sport_shirt_blue + name = "Sport shirt, blue" + icon_state = "blueshirtsport_s" + +/datum/category_item/underwear/undershirt/shirt_tiedye + name = "Shirt, tiedye" + icon_state = "shirt_tiedye_s" + +/datum/category_item/underwear/undershirt/shirt_blue_striped + name = "Shirt, blue stripes" + icon_state = "shirt_stripes_s" + +/datum/category_item/underwear/undershirt/bowling + name = "Bowling Shirt, Red" + icon_state = "bowling" + +/datum/category_item/underwear/undershirt/bowlingp + name = "Bowling Shirt, Pink" + icon_state = "bowlingp" + +/datum/category_item/underwear/undershirt/bowlinga + name = "Bowling Shirt, Aqua" + icon_state = "bowlinga" + +/datum/category_item/underwear/undershirt/bowlingw + name = "Bowling Shirt, White" + icon_state = "bowlingw" + +/datum/category_item/underwear/undershirt/longjon + name = "Long John Shirt" + icon_state = "ljont" + has_color = TRUE + +/datum/category_item/underwear/undershirt/longstripe_black + name = "Longsleeve Striped Shirt, Black" + icon_state = "longstripe" + +/datum/category_item/underwear/undershirt/longstripe_blue + name = "Longsleeve Striped Shirt, Blue" + icon_state = "longstripe_blue" + +/datum/category_item/underwear/undershirt/tiedye + name = "Tiedye Shirt" + icon_state = "tiedye" + +/datum/category_item/underwear/undershirt/longstripe_pink + name = "Longsleeve Striped Shirt, Pink" + icon_state = "longstripe_pink_s" + +/datum/category_item/underwear/undershirt/wingshirt + name = "Pink Wing Shirt" + icon_state = "wing_shirt_s" + +/datum/category_item/underwear/undershirt/pinkblack_tshirt + name = "Pink and Black T-Shirt" + icon_state = "pinkblack_tshirt" + +/datum/category_item/underwear/undershirt/turtle + name = "Turtleneck, Old" + icon_state = "turtleneck_old" + has_color = TRUE + +/datum/category_item/underwear/undershirt/sleevelessturtle + name = "Turtleneck, Sleeveless, Old" + icon_state = "turtleneck_sleeveless_old" + has_color = TRUE + +/datum/category_item/underwear/undershirt/turtleneck + name = "Turtleneck" + icon_state = "turtleneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/turtleneck_smooth + name = "Turtleneck, Smooth" + icon_state = "turtleneck_smooth" + has_color = TRUE + +/datum/category_item/underwear/undershirt/turtlesleeveless + name = "Turtleneck, Sleeveless" + icon_state = "turtleneck_sleeveless" + has_color = TRUE + +/datum/category_item/underwear/undershirt/leotardturtle + name = "Leotard Turtleneck" + icon_state = "leotard_turtleneck" + has_color = TRUE + +/datum/category_item/underwear/undershirt/leotardturtlesleeveless + name = "Leotard Turtleneck, Sleeveless" + icon_state = "leotard_turtleneck_sleeveless" + has_color = TRUE + +/datum/category_item/underwear/undershirt/dress_shirt_fem + name = "Dress shirt, feminine" + icon_state = "undershirt_dress_fem" + has_color = TRUE + +/datum/category_item/underwear/undershirt/dress_shirt + name = "Dress shirt, masculine" + icon_state = "undershirt_dress" + has_color = TRUE + +/datum/category_item/underwear/undershirt/midriff + name = "Tanktop, midriff" + icon_state = "tank_midriff" + has_color = TRUE + +/datum/category_item/underwear/undershirt/midriffshort + name = "Tanktop, midriff, short" + icon_state = "tank_midriff_short" + has_color = TRUE + +/datum/category_item/underwear/undershirt/shibari + name = "Shibari Binding" + icon_state = "shibari" + has_color = TRUE + +/datum/category_item/underwear/undershirt/leotard + name = "Leotard" + icon_state = "leotard" + has_color = TRUE diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 9a411c60ee3..b366ac43a9c 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -168,8 +168,8 @@ switch(action) // Toggles the cut/mend status. if("cut") - // if(!I.is_wirecutter() && !user.can_admin_interact()) - if(!istype(I) || !I.is_wirecutter()) + // if(!I.has_tool_quality(TOOL_WIRECUTTER) && !user.can_admin_interact()) + if(!istype(I) || !I.has_tool_quality(TOOL_WIRECUTTER)) to_chat(user, "You need wirecutters!") return @@ -179,8 +179,8 @@ // Pulse a wire. if("pulse") - // if(!I.is_multitool() && !user.can_admin_interact()) - if(!istype(I) || !I.is_multitool()) + // if(!I.has_tool_quality(TOOL_MULTITOOL) && !user.can_admin_interact()) + if(!istype(I) || !I.has_tool_quality(TOOL_MULTITOOL)) to_chat(user, "You need a multitool!") return diff --git a/code/defines/gases.dm b/code/defines/gases.dm index 8b56b5bf45a..6b2a67c2437 100644 --- a/code/defines/gases.dm +++ b/code/defines/gases.dm @@ -1,54 +1,54 @@ -/decl/xgm_gas/oxygen - id = "oxygen" - name = "Oxygen" - specific_heat = 20 // J/(mol*K) - molar_mass = 0.032 // kg/mol - - flags = XGM_GAS_OXIDIZER - -/decl/xgm_gas/nitrogen - id = "nitrogen" - name = "Nitrogen" - specific_heat = 20 // J/(mol*K) - molar_mass = 0.028 // kg/mol - -/decl/xgm_gas/carbon_dioxide - id = "carbon_dioxide" - name = "Carbon Dioxide" - specific_heat = 30 // J/(mol*K) - molar_mass = 0.044 // kg/mol - -/decl/xgm_gas/phoron - id = "phoron" - name = "Phoron" - - //Note that this has a significant impact on TTV yield. - //Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure. - specific_heat = 200 // J/(mol*K) - - //Hypothetical group 14 (same as carbon), period 8 element. - //Using multiplicity rule, it's atomic number is 162 - //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is: - molar_mass = 0.405 // kg/mol - - tile_overlay = "phoron" - overlay_limit = 0.7 - flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL //R-UST port, adding XGM_GAS_FUSION_FUEL flag. - -/decl/xgm_gas/volatile_fuel - id = "volatile_fuel" - name = "Volatile Fuel" - specific_heat = 253 // J/(mol*K) C8H18 gasoline. Isobaric, but good enough. - molar_mass = 0.114 // kg/mol. same. - - flags = XGM_GAS_FUEL - -/decl/xgm_gas/nitrous_oxide - id = "nitrous_oxide" - name = "Nitrous Oxide" - specific_heat = 40 // J/(mol*K) - molar_mass = 0.044 // kg/mol. N2O - - tile_overlay = "nitrous_oxide" - overlay_limit = 1 +/decl/xgm_gas/oxygen + id = "oxygen" + name = "Oxygen" + specific_heat = 20 // J/(mol*K) + molar_mass = 0.032 // kg/mol + + flags = XGM_GAS_OXIDIZER + +/decl/xgm_gas/nitrogen + id = "nitrogen" + name = "Nitrogen" + specific_heat = 20 // J/(mol*K) + molar_mass = 0.028 // kg/mol + +/decl/xgm_gas/carbon_dioxide + id = "carbon_dioxide" + name = "Carbon Dioxide" + specific_heat = 30 // J/(mol*K) + molar_mass = 0.044 // kg/mol + +/decl/xgm_gas/phoron + id = "phoron" + name = "Phoron" + + //Note that this has a significant impact on TTV yield. + //Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure. + specific_heat = 200 // J/(mol*K) + + //Hypothetical group 14 (same as carbon), period 8 element. + //Using multiplicity rule, it's atomic number is 162 + //and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is: + molar_mass = 0.405 // kg/mol + + tile_overlay = "phoron" + overlay_limit = 0.7 + flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT | XGM_GAS_FUSION_FUEL //R-UST port, adding XGM_GAS_FUSION_FUEL flag. + +/decl/xgm_gas/volatile_fuel + id = "volatile_fuel" + name = "Volatile Fuel" + specific_heat = 253 // J/(mol*K) C8H18 gasoline. Isobaric, but good enough. + molar_mass = 0.114 // kg/mol. same. + + flags = XGM_GAS_FUEL + +/decl/xgm_gas/nitrous_oxide + id = "nitrous_oxide" + name = "Nitrous Oxide" + specific_heat = 40 // J/(mol*K) + molar_mass = 0.044 // kg/mol. N2O + + tile_overlay = "nitrous_oxide" + overlay_limit = 1 flags = XGM_GAS_OXIDIZER \ No newline at end of file diff --git a/code/defines/obj.dm b/code/defines/obj.dm index db64bd85465..bddaf11efb6 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -1,120 +1,120 @@ -/obj/structure/signpost - icon = 'icons/obj/stationobjs.dmi' - icon_state = "signpost" - anchored = TRUE - density = TRUE - -/obj/structure/signpost/attackby(obj/item/weapon/W as obj, mob/user as mob) - return attack_hand(user) - -/obj/structure/signpost/attack_hand(mob/user as mob) - if(tgui_alert(user, "Travel back to ss13?","Return?",list("Yes","No")) == "Yes") - if(user.z != src.z) return - user.forceMove(pick(latejoin)) - -/obj/effect/mark - var/mark = "" - icon = 'icons/misc/mark.dmi' - icon_state = "blank" - anchored = TRUE - layer = 99 - mouse_opacity = 0 - unacidable = TRUE//Just to be sure. - -/obj/effect/beam - name = "beam" - density = FALSE - unacidable = TRUE//Just to be sure. - var/def_zone - pass_flags = PASSTABLE - - -/obj/effect/begin - name = "begin" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "begin" - anchored = TRUE - unacidable = TRUE - -/* - * This item is completely unused, but removing it will break something in R&D and Radio code causing PDA and Ninja code to fail on compile - */ - -/var/list/acting_rank_prefixes = list("acting", "temporary", "interim", "provisional") - -/proc/make_list_rank(rank) - for(var/prefix in acting_rank_prefixes) - if(findtext(rank, "[prefix] ", 1, 2+length(prefix))) - return copytext(rank, 2+length(prefix)) - return rank - -/obj/effect/laser - name = "laser" - desc = "IT BURNS!!!" - icon = 'icons/obj/projectiles.dmi' - var/damage = 0.0 - var/range = 10.0 - -/obj/effect/projection - name = "Projection" - desc = "This looks like a projection of something." - anchored = TRUE - -/obj/effect/shut_controller - name = "shut controller" - var/moving = null - var/list/parts = list( ) - -/obj/structure/showcase - name = "Showcase" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "showcase_1" - desc = "A stand with the empty body of a cyborg bolted to it." - density = TRUE - anchored = TRUE - unacidable = TRUE//temporary until I decide whether the borg can be removed. -veyveyr - -/obj/structure/showcase/sign - name = "WARNING: WILDERNESS" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "wilderness1" - desc = "This appears to be a sign warning people that the other side is dangerous. It also says that NanoTrasen cannot guarantee your safety beyond this point." - -/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER - -/obj/item/weapon/beach_ball - icon = 'icons/misc/beach.dmi' - icon_state = "beachball" - name = "beach ball" - density = FALSE - anchored = FALSE - w_class = ITEMSIZE_LARGE - force = 0.0 - throwforce = 0.0 - throw_speed = 1 - throw_range = 20 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/weapon/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) - user.drop_item() - src.throw_at(target, throw_range, throw_speed, user) - -/obj/item/weapon/beach_ball/dodgeball - icon = 'icons/obj/balls_vr.dmi' - icon_state = "dodgeball" - item_state = "dodgeball" - item_icons = list(slot_l_hand_str = 'icons/mob/items/lefthand_balls_vr.dmi', slot_r_hand_str = 'icons/mob/items/righthand_balls_vr.dmi') - name = "dodgeball" - desc = "Think fast, chucklenuts!" - w_class = ITEMSIZE_LARGE //Stops people from hiding it in their bags/pockets - force = 0.1 - throwforce = 0.1 - throw_speed = 5 - throw_range = 15 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - hitsound = 'sound/weapons/dodgeball.ogg' - -/obj/effect/spawner - name = "object spawner" +/obj/structure/signpost + icon = 'icons/obj/stationobjs.dmi' + icon_state = "signpost" + anchored = TRUE + density = TRUE + +/obj/structure/signpost/attackby(obj/item/weapon/W as obj, mob/user as mob) + return attack_hand(user) + +/obj/structure/signpost/attack_hand(mob/user as mob) + if(tgui_alert(user, "Travel back to ss13?","Return?",list("Yes","No")) == "Yes") + if(user.z != src.z) return + user.forceMove(pick(latejoin)) + +/obj/effect/mark + var/mark = "" + icon = 'icons/misc/mark.dmi' + icon_state = "blank" + anchored = TRUE + layer = 99 + mouse_opacity = 0 + unacidable = TRUE//Just to be sure. + +/obj/effect/beam + name = "beam" + density = FALSE + unacidable = TRUE//Just to be sure. + var/def_zone + pass_flags = PASSTABLE + + +/obj/effect/begin + name = "begin" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "begin" + anchored = TRUE + unacidable = TRUE + +/* + * This item is completely unused, but removing it will break something in R&D and Radio code causing PDA and Ninja code to fail on compile + */ + +/var/list/acting_rank_prefixes = list("acting", "temporary", "interim", "provisional") + +/proc/make_list_rank(rank) + for(var/prefix in acting_rank_prefixes) + if(findtext(rank, "[prefix] ", 1, 2+length(prefix))) + return copytext(rank, 2+length(prefix)) + return rank + +/obj/effect/laser + name = "laser" + desc = "IT BURNS!!!" + icon = 'icons/obj/projectiles.dmi' + var/damage = 0.0 + var/range = 10.0 + +/obj/effect/projection + name = "Projection" + desc = "This looks like a projection of something." + anchored = TRUE + +/obj/effect/shut_controller + name = "shut controller" + var/moving = null + var/list/parts = list( ) + +/obj/structure/showcase + name = "Showcase" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "showcase_1" + desc = "A stand with the empty body of a cyborg bolted to it." + density = TRUE + anchored = TRUE + unacidable = TRUE//temporary until I decide whether the borg can be removed. -veyveyr + +/obj/structure/showcase/sign + name = "WARNING: WILDERNESS" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "wilderness1" + desc = "This appears to be a sign warning people that the other side is dangerous. It also says that NanoTrasen cannot guarantee your safety beyond this point." + +/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER + +/obj/item/weapon/beach_ball + icon = 'icons/misc/beach.dmi' + icon_state = "beachball" + name = "beach ball" + density = FALSE + anchored = FALSE + w_class = ITEMSIZE_LARGE + force = 0.0 + throwforce = 0.0 + throw_speed = 1 + throw_range = 20 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/weapon/beach_ball/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) + user.drop_item() + src.throw_at(target, throw_range, throw_speed, user) + +/obj/item/weapon/beach_ball/dodgeball + icon = 'icons/obj/balls_vr.dmi' + icon_state = "dodgeball" + item_state = "dodgeball" + item_icons = list(slot_l_hand_str = 'icons/mob/items/lefthand_balls_vr.dmi', slot_r_hand_str = 'icons/mob/items/righthand_balls_vr.dmi') + name = "dodgeball" + desc = "Think fast, chucklenuts!" + w_class = ITEMSIZE_LARGE //Stops people from hiding it in their bags/pockets + force = 0.1 + throwforce = 0.1 + throw_speed = 5 + throw_range = 15 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + hitsound = 'sound/weapons/dodgeball.ogg' + +/obj/effect/spawner + name = "object spawner" diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index ddfb05db6b0..333eb239029 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -1,890 +1,890 @@ -/obj/item/weapon/phone - name = "red phone" - desc = "Should anything ever go wrong..." - icon = 'icons/obj/items.dmi' - icon_state = "red_phone" - force = 3.0 - throwforce = 2.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_SMALL - attack_verb = list("called", "rang") - hitsound = 'sound/weapons/ring.ogg' - drop_sound = 'sound/items/drop/device.ogg' - pickup_sound = 'sound/items/pickup/device.ogg' - -/obj/item/weapon/rsp - name = "\improper Rapid-Seed-Producer (RSP)" - desc = "A device used to rapidly deploy seeds." - icon = 'icons/obj/items.dmi' - icon_state = "rcd" - opacity = 0 - density = FALSE - anchored = FALSE - var/stored_matter = 0 - var/mode = 1 - w_class = ITEMSIZE_NORMAL - drop_sound = 'sound/items/drop/device.ogg' - pickup_sound = 'sound/items/pickup/device.ogg' - -/obj/item/weapon/soap - name = "soap" - desc = "A cheap bar of soap. Smells of lye." - gender = PLURAL - icon = 'icons/obj/soap.dmi' - icon_state = "soap" - flags = NOCONDUCT - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_HOLSTER - throwforce = 0 - throw_speed = 4 - throw_range = 20 - var/randomize = TRUE - var/square_chance = 10 - -/obj/item/weapon/soap/Initialize() - if(randomize && prob(square_chance)) - icon_state = "[icon_state]-alt" - -/obj/item/weapon/soap/nanotrasen - desc = "A NanoTrasen-brand bar of soap. Smells of phoron, a years-old marketing gimmick." - icon_state = "soapnt" - -/obj/item/weapon/soap/deluxe - icon_state = "soapdeluxe" - -/obj/item/weapon/soap/deluxe/New() - desc = "A deluxe Waffle Co. brand bar of soap. Smells of [pick("lavender", "vanilla", "strawberry", "chocolate" ,"space")]." - ..() - -/obj/item/weapon/soap/syndie - desc = "An untrustworthy bar of soap. Smells of fear." - icon_state = "soapsyndie" - -/obj/item/weapon/soap/space_soap - desc = "Smells like hot metal and walnuts." - icon_state = "space_soap" - -/obj/item/weapon/soap/water_soap - desc = "Smells like chlorine." - icon_state = "water_soap" - -/obj/item/weapon/soap/fire_soap - desc = "Smells like a campfire." - icon_state = "fire_soap" - -/obj/item/weapon/soap/rainbow_soap - desc = "Smells sickly sweet." - icon_state = "rainbow_soap" - -/obj/item/weapon/soap/diamond_soap - desc = "Smells like saffron and vanilla." - icon_state = "diamond_soap" - -/obj/item/weapon/soap/uranium_soap - desc = "Smells not great... Not terrible." - icon_state = "uranium_soap" - -/obj/item/weapon/soap/silver_soap - desc = "Smells like birch and amaranth." - icon_state = "silver_soap" - -/obj/item/weapon/soap/brown_soap - desc = "Smells like cinnamon and cognac." - icon_state = "brown_soap" - -/obj/item/weapon/soap/white_soap - desc = "Smells like nutmeg and oats." - icon_state = "white_soap" - -/obj/item/weapon/soap/grey_soap - desc = "Smells like bergamot and lilies." - icon_state = "grey_soap" - -/obj/item/weapon/soap/pink_soap - desc = "Smells like bubblegum." - icon_state = "pink_soap" - -/obj/item/weapon/soap/purple_soap - desc = "Smells like lavender." - icon_state = "purple_soap" - -/obj/item/weapon/soap/blue_soap - desc = "Smells like cardamom." - icon_state = "blue_soap" - -/obj/item/weapon/soap/cyan_soap - desc = "Smells like bluebells and peaches." - icon_state = "cyan_soap" - -/obj/item/weapon/soap/green_soap - desc = "Smells like a freshly mowed lawn." - icon_state = "green_soap" - -/obj/item/weapon/soap/yellow_soap - desc = "Smells like citron and ginger." - icon_state = "yellow_soap" - -/obj/item/weapon/soap/orange_soap - desc = "Smells like oranges and dark chocolate." - icon_state = "orange_soap" - -/obj/item/weapon/soap/red_soap - desc = "Smells like cherries." - icon_state = "red_soap" - -/obj/item/weapon/soap/golden_soap - desc = "Smells like honey." - icon_state = "golden_soap" - -/obj/item/weapon/bikehorn - name = "bike horn" - desc = "A horn off of a bicycle." - icon = 'icons/obj/items.dmi' - icon_state = "bike_horn" - item_state = "bike_horn" - throwforce = 3 - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_HOLSTER - throw_speed = 3 - throw_range = 15 - attack_verb = list("HONKED") - var/spam_flag = 0 - -/obj/item/weapon/c_tube - name = "cardboard tube" - desc = "A tube... of cardboard." - icon = 'icons/obj/items.dmi' - icon_state = "c_tube" - throwforce = 1 - w_class = ITEMSIZE_SMALL - throw_speed = 4 - throw_range = 5 - -/obj/item/weapon/disk - name = "disk" - icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit - drop_sound = 'sound/items/drop/disk.ogg' - pickup_sound = 'sound/items/pickup/disk.ogg' - -/obj/item/weapon/disk/nuclear - name = "nuclear authentication disk" - desc = "Better keep this safe." - icon_state = "nucleardisk" - item_state = "card-id" - w_class = ITEMSIZE_SMALL - -/* -/obj/item/weapon/game_kit - name = "Gaming Kit" - icon = 'icons/obj/items.dmi' - icon_state = "game_kit" - var/selected = null - var/board_stat = null - var/data = "" - var/base_url = "http://svn.slurm.us/public/spacestation13/misc/game_kit" - item_state = "sheet-metal" - w_class = ITEMSIZE_HUGE -*/ - -/obj/item/weapon/gift - name = "gift" - desc = "A wrapped item." - icon = 'icons/obj/items.dmi' - icon_state = "gift3" - var/size = 3.0 - var/obj/item/gift = null - item_state = "gift" - w_class = ITEMSIZE_LARGE - -/*/obj/item/weapon/syndicate_uplink - name = "station bounced radio" - desc = "Remain silent about this..." - icon = 'icons/obj/radio.dmi' - icon_state = "radio" - var/temp = null - var/uses = 10.0 - var/selfdestruct = 0.0 - var/traitor_frequency = 0.0 - var/mob/currentUser = null - var/obj/item/device/radio/origradio = null - flags = ONBELT - w_class = ITEMSIZE_SMALL - item_state = "radio" - throw_speed = 4 - throw_range = 20 - matter = list(MAT_STEEL = 100) - origin_tech = list(TECH_MAGNET = 2, TECH_ILLEGAL = 3)*/ - -/obj/item/weapon/SWF_uplink - name = "station-bounced radio" - desc = "Used to communicate, it appears." - icon = 'icons/obj/radio.dmi' - icon_state = "radio" - var/temp = null - var/uses = 4.0 - var/selfdestruct = 0.0 - var/traitor_frequency = 0.0 - var/obj/item/device/radio/origradio = null - slot_flags = SLOT_BELT - item_state = "radio" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 4 - throw_range = 20 - matter = list(MAT_STEEL = 100) - origin_tech = list(TECH_MAGNET = 1) - drop_sound = 'sound/items/drop/device.ogg' - pickup_sound = 'sound/items/pickup/device.ogg' - -/obj/item/weapon/staff - name = "wizards staff" - desc = "Apparently a staff used by the wizard." - icon = 'icons/obj/wizard.dmi' - icon_state = "staff" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - force = 3.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - attack_verb = list("bludgeoned", "whacked", "disciplined") - -/obj/item/weapon/staff/broom - name = "broom" - desc = "Used for sweeping, and flying into the night while cackling. Black cat not included." - icon = 'icons/obj/wizard.dmi' - icon_state = "broom" - -/obj/item/weapon/staff/gentcane - name = "Gentlemans Cane" - desc = "An ebony can with an ivory tip." - icon = 'icons/obj/weapons.dmi' - icon_state = "cane" - -/obj/item/weapon/staff/stick - name = "stick" - desc = "A great tool to drag someone else's drinks across the bar." - icon = 'icons/obj/weapons.dmi' - icon_state = "stick" - item_state = "cane" - force = 3.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - -/obj/item/weapon/module - icon = 'icons/obj/module.dmi' - icon_state = "std_module" - item_state = "std_mod" - w_class = ITEMSIZE_SMALL - var/mtype = 1 // 1=electronic 2=hardware - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - -/obj/item/weapon/module/card_reader - name = "card reader module" - icon_state = "card_mod" - item_state = "std_mod" - desc = "An electronic module for reading data and ID cards." - -/obj/item/weapon/module/power_control - name = "power control module" - icon_state = "power_mod" - item_state = "std_mod" - desc = "Heavy-duty switching circuits for power control." - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - -/obj/item/weapon/module/id_auth - name = "\improper ID authentication module" - icon_state = "id_mod" - desc = "A module allowing secure authorization of ID cards." - -/obj/item/weapon/module/cell_power - name = "power cell regulator module" - icon_state = "power_mod" - item_state = "std_mod" - desc = "A converter and regulator allowing the use of power cells." - -/obj/item/weapon/module/cell_power - name = "power cell charger module" - icon_state = "power_mod" - item_state = "std_mod" - desc = "Charging circuits for power cells." - -/obj/item/device/camera_bug - name = "camera bug" - icon = 'icons/obj/device.dmi' - icon_state = "flash" - w_class = ITEMSIZE_TINY - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - -/obj/item/weapon/camera_bug/attack_self(mob/usr as mob) - var/list/cameras = new/list() - for (var/obj/machinery/camera/C in cameranet.cameras) - if (C.bugged && C.status) - cameras.Add(C) - if (length(cameras) == 0) - to_chat(usr, "No bugged functioning cameras found.") - return - - var/list/friendly_cameras = new/list() - - for (var/obj/machinery/camera/C in cameras) - friendly_cameras.Add(C.c_tag) - - var/target = tgui_input_list(usr, "Select the camera to observe", "Select Camera", friendly_cameras) - if (!target) - return - for (var/obj/machinery/camera/C in cameras) - if (C.c_tag == target) - target = C - break - if (usr.stat == 2) return - - usr.client.eye = target - -/* -/obj/item/weapon/cigarpacket - name = "Pete's Cuban Cigars" - desc = "The most robust cigars on the planet." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "cigarpacket" - item_state = "cigarpacket" - w_class = ITEMSIZE_TINY - throwforce = 2 - var/cigarcount = 6 - flags = ONBELT - */ - -/obj/item/weapon/pai_cable - desc = "A flexible coated cable with a universal jack on one end." - name = "data cable" - icon = 'icons/obj/power.dmi' - icon_state = "wire1" - - var/obj/machinery/machine - -/obj/item/weapon/pai_cable/Destroy() - machine = null - return ..() - -///////////////////////////////////////Stock Parts ///////////////////////////////// - -/obj/item/weapon/storage/part_replacer - name = "rapid part exchange device" - desc = "A special mechanical module made to store, sort, and apply standard machine parts." - icon = 'icons/obj/storage_vr.dmi' - icon_state = "RPED" - item_state = "RPED" - w_class = ITEMSIZE_HUGE - can_hold = list(/obj/item/weapon/stock_parts) - storage_slots = 50 - use_to_pickup = TRUE - allow_quick_gather = 1 - allow_quick_empty = 1 - collection_mode = 1 - display_contents_with_number = 1 - max_w_class = ITEMSIZE_NORMAL - max_storage_space = 100 - drop_sound = 'sound/items/drop/device.ogg' - pickup_sound = 'sound/items/pickup/device.ogg' - var/panel_req = TRUE - var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg' - var/reskin_ran = FALSE - var/unique_reskin = list("Soulless" = "RPED", - "Soulful" = "RPED_old") - -/obj/item/weapon/storage/part_replacer/proc/play_rped_sound() - //Plays the sound for RPED exhanging or installing parts. -/* if(alt_sound && prob(1)) - playsound(src, alt_sound, 40, 1) - else -*/ - playsound(src, pshoom_or_beepboopblorpzingshadashwoosh, 40, 1) - -/obj/item/weapon/storage/part_replacer/adv - name = "advanced rapid part exchange device" - desc = "A special mechanical module made to store, sort, and apply standard machine parts. This one has a greatly upgraded storage capacity, \ - and the ability to hold beakers." - can_hold = list(/obj/item/weapon/stock_parts, /obj/item/weapon/reagent_containers/glass/beaker) - storage_slots = 200 - max_storage_space = 400 - -/obj/item/weapon/storage/part_replacer/adv/discount_bluespace - name = "prototype bluespace rapid part exchange device" - icon_state = "DBRPED" - item_state = "DBRPED" - desc = "A special mechanical module made to store, sort, and apply standard machine parts. This one has a further increased storage capacity, \ - and the ability to work on machines with closed maintenance panels." - storage_slots = 400 - max_storage_space = 800 - panel_req = FALSE - pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/pshoom.ogg' - unique_reskin = list("Soulless" = "DBRPED", - "Soulful" = "DBRPED_old") - -/obj/item/weapon/storage/part_replacer/examine(mob/user) - . = ..() - if(!reskin_ran) - . += "[src]'s external casing can be modified via alt-click." - -/obj/item/weapon/storage/part_replacer/AltClick(mob/user) - . = ..() - if(!reskin_ran) - reskin_radial(user) - -/obj/item/weapon/storage/part_replacer/proc/reskin_radial(mob/M) - if(!LAZYLEN(unique_reskin)) - return - - var/list/items = list() - for(var/reskin_option in unique_reskin) - var/image/item_image = image(icon = src.icon, icon_state = unique_reskin[reskin_option]) - items += list("[reskin_option]" = item_image) - sortList(items) - - var/pick = show_radial_menu(M, src, items, radius = 38, require_near = TRUE) - if(!pick) - return - if(!unique_reskin[pick]) - return - icon_state = unique_reskin[pick] - item_state = unique_reskin[pick] - reskin_ran = TRUE - to_chat(M, "[src] is now '[pick]'.") - -/obj/item/weapon/storage/part_replacer/drop_contents() // hacky-feeling tier-based drop system - hide_from(usr) - var/turf/T = get_turf(src) - var/lowest_rating = INFINITY // We want the lowest-part tier rating in the RPED so we only drop the lowest-tier parts. - /* - * Why not just use the stock part's rating variable? - * Future-proofing for a potential future where stock parts aren't the only thing that can fit in an RPED. - * see: /tg/ and /vg/'s RPEDs fitting power cells, beakers, etc. - * 10/8/21 edit - It's Time. - */ - for(var/obj/item/B in contents) - if(B.rped_rating() < lowest_rating) - lowest_rating = B.rped_rating() - for(var/obj/item/B in contents) - if(B.rped_rating() > lowest_rating) - continue - remove_from_storage(B, T) - - -/obj/item/weapon/stock_parts - name = "stock part" - desc = "What?" - gender = PLURAL - icon = 'icons/obj/stock_parts.dmi' - w_class = ITEMSIZE_SMALL - var/rating = 1 - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - -/obj/item/weapon/stock_parts/New() - src.pixel_x = rand(-5.0, 5) - src.pixel_y = rand(-5.0, 5) - ..() - -/obj/item/weapon/stock_parts/get_rating() - return rating - -//Rank 1 - -/obj/item/weapon/stock_parts/console_screen - name = "console screen" - desc = "Used in the construction of computers and other devices with a interactive console." - icon_state = "screen" - origin_tech = list(TECH_MATERIAL = 1) - rating = 5 // these are actually Really Important for some things?? - matter = list(MAT_GLASS = 200) - -/obj/item/weapon/stock_parts/capacitor - name = "capacitor" - desc = "A basic capacitor used in the construction of a variety of devices." - icon_state = "capacitor" - origin_tech = list(TECH_POWER = 1) - matter = list(MAT_STEEL = 50,MAT_GLASS = 50) - - var/charge = 0 - var/max_charge = 1000 - -/obj/item/weapon/stock_parts/capacitor/New() - . = ..() - max_charge *= rating - -/obj/item/weapon/stock_parts/capacitor/proc/charge(var/amount) - charge += amount - if(charge > max_charge) - charge = max_charge - -/obj/item/weapon/stock_parts/capacitor/proc/use(var/amount) - if(charge) - charge -= amount - if(charge < 0) - charge = 0 - -/obj/item/weapon/stock_parts/scanning_module - name = "scanning module" - desc = "A compact, high resolution scanning module used in the construction of certain devices." - icon_state = "scan_module" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - -/obj/item/weapon/stock_parts/manipulator - name = "micro-manipulator" - desc = "A tiny little manipulator used in the construction of certain devices." - icon_state = "micro_mani" - origin_tech = list(TECH_MATERIAL = 1, TECH_DATA = 1) - matter = list(MAT_STEEL = 30) - -/obj/item/weapon/stock_parts/micro_laser - name = "micro-laser" - desc = "A tiny laser used in certain devices." - icon_state = "micro_laser" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 10,MAT_GLASS = 20) - -/obj/item/weapon/stock_parts/matter_bin - name = "matter bin" - desc = "A container for hold compressed matter awaiting re-construction." - icon_state = "matter_bin" - origin_tech = list(TECH_MATERIAL = 1) - matter = list(MAT_STEEL = 80) - -//Rank 2 - -/obj/item/weapon/stock_parts/capacitor/adv - name = "advanced capacitor" - desc = "An advanced capacitor used in the construction of a variety of devices." - icon_state = "capacitor_adv" - origin_tech = list(TECH_POWER = 3) - rating = 2 - matter = list(MAT_STEEL = 50,MAT_GLASS = 50) - -/obj/item/weapon/stock_parts/scanning_module/adv - name = "advanced scanning module" - desc = "A compact, high resolution scanning module used in the construction of certain devices." - icon_state = "scan_module_adv" - origin_tech = list(TECH_MAGNET = 3) - rating = 2 - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - -/obj/item/weapon/stock_parts/manipulator/nano - name = "nano-manipulator" - desc = "A tiny little manipulator used in the construction of certain devices." - icon_state = "nano_mani" - origin_tech = list(TECH_MATERIAL = 3, TECH_DATA = 2) - rating = 2 - matter = list(MAT_STEEL = 30) - -/obj/item/weapon/stock_parts/micro_laser/high - name = "high-power micro-laser" - desc = "A tiny laser used in certain devices." - icon_state = "high_micro_laser" - origin_tech = list(TECH_MAGNET = 3) - rating = 2 - matter = list(MAT_STEEL = 10,MAT_GLASS = 20) - -/obj/item/weapon/stock_parts/matter_bin/adv - name = "advanced matter bin" - desc = "A container for hold compressed matter awaiting re-construction." - icon_state = "advanced_matter_bin" - origin_tech = list(TECH_MATERIAL = 3) - rating = 2 - matter = list(MAT_STEEL = 80) - -//Rating 3 - -/obj/item/weapon/stock_parts/capacitor/super - name = "super capacitor" - desc = "A super-high capacity capacitor used in the construction of a variety of devices." - icon_state = "capacitor_super" - origin_tech = list(TECH_POWER = 5, TECH_MATERIAL = 4) - rating = 3 - matter = list(MAT_STEEL = 50,MAT_GLASS = 50) - -/obj/item/weapon/stock_parts/scanning_module/phasic - name = "phasic scanning module" - desc = "A compact, high resolution phasic scanning module used in the construction of certain devices." - icon_state = "scan_module_phasic" - origin_tech = list(TECH_MAGNET = 5) - rating = 3 - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - -/obj/item/weapon/stock_parts/manipulator/pico - name = "pico-manipulator" - desc = "A tiny little manipulator used in the construction of certain devices." - icon_state = "pico_mani" - origin_tech = list(TECH_MATERIAL = 5, TECH_DATA = 2) - rating = 3 - matter = list(MAT_STEEL = 30) - -/obj/item/weapon/stock_parts/micro_laser/ultra - name = "ultra-high-power micro-laser" - icon_state = "ultra_high_micro_laser" - desc = "A tiny laser used in certain devices." - origin_tech = list(TECH_MAGNET = 5) - rating = 3 - matter = list(MAT_STEEL = 10,MAT_GLASS = 20) - -/obj/item/weapon/stock_parts/matter_bin/super - name = "super matter bin" - desc = "A container for hold compressed matter awaiting re-construction." - icon_state = "super_matter_bin" - origin_tech = list(TECH_MATERIAL = 5) - rating = 3 - matter = list(MAT_STEEL = 80) - -// Rating 4 - Anomaly - -/obj/item/weapon/stock_parts/capacitor/hyper - name = "hyper capacitor" - desc = "A hyper-capacity capacitor used in the construction of a variety of devices." - icon_state = "capacitor_hyper" - origin_tech = list(TECH_POWER = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 1, TECH_ARCANE = 1) - rating = 4 - matter = list(MAT_STEEL = 80, MAT_GLASS = 40) - -/obj/item/weapon/stock_parts/scanning_module/hyper - name = "quantum scanning module" - desc = "A compact, near-perfect resolution quantum scanning module used in the construction of certain devices." - icon_state = "scan_module_hyper" - origin_tech = list(TECH_MAGNET = 6, TECH_BLUESPACE = 1, TECH_ARCANE = 1) - rating = 4 - matter = list(MAT_STEEL = 100,MAT_GLASS = 40) - -/obj/item/weapon/stock_parts/manipulator/hyper - name = "planck-manipulator" - desc = "A miniscule manipulator used in the construction of certain devices." - icon_state = "hyper_mani" - origin_tech = list(TECH_MATERIAL = 6, TECH_DATA = 3, TECH_ARCANE = 1) - rating = 4 - matter = list(MAT_STEEL = 30) - -/obj/item/weapon/stock_parts/micro_laser/hyper - name = "hyper-power micro-laser" - icon_state = "hyper_micro_laser" - desc = "A tiny laser used in certain devices." - origin_tech = list(TECH_MAGNET = 6, TECH_ARCANE = 1) - rating = 4 - matter = list(MAT_STEEL = 30, MAT_GLASS = 40) - -/obj/item/weapon/stock_parts/matter_bin/hyper - name = "hyper matter bin" - desc = "A container for holding compressed matter awaiting re-construction." - icon_state = "hyper_matter_bin" - origin_tech = list(TECH_MATERIAL = 6, TECH_ARCANE = 1) - rating = 4 - matter = list(MAT_STEEL = 100) - -// Rating 5 - Precursor - -/obj/item/weapon/stock_parts/capacitor/omni - name = "omni-capacitor" - desc = "A capacitor of immense capacity used in the construction of a variety of devices." - icon_state = "capacitor_omni" - origin_tech = list(TECH_POWER = 7, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) - rating = 5 - matter = list(MAT_STEEL = 80, MAT_GLASS = 40) - -/obj/item/weapon/stock_parts/scanning_module/omni - name = "omni-scanning module" - desc = "A compact, perfect resolution temporospatial scanning module used in the construction of certain devices." - icon_state = "scan_module_omni" - origin_tech = list(TECH_MAGNET = 7, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) - rating = 5 - matter = list(MAT_STEEL = 100,MAT_GLASS = 40) - -/obj/item/weapon/stock_parts/manipulator/omni - name = "omni-manipulator" - desc = "A strange, infinitesimal manipulator used in the construction of certain devices." - icon_state = "omni_mani" - origin_tech = list(TECH_MATERIAL = 7, TECH_DATA = 4, TECH_PRECURSOR = 1) - rating = 5 - matter = list(MAT_STEEL = 30) - -/obj/item/weapon/stock_parts/micro_laser/omni - name = "omni-power micro-laser" - icon_state = "omni_micro_laser" - desc = "A strange laser used in certain devices." - origin_tech = list(TECH_MAGNET = 7, TECH_PRECURSOR = 1) - rating = 5 - matter = list(MAT_STEEL = 30, MAT_GLASS = 40) - -/obj/item/weapon/stock_parts/matter_bin/omni - name = "omni-matter bin" - desc = "A strange container for holding compressed matter awaiting re-construction." - icon_state = "omni_matter_bin" - origin_tech = list(TECH_MATERIAL = 7, TECH_PRECURSOR = 1) - rating = 5 - matter = list(MAT_STEEL = 100) - - -// Subspace stock parts - -/obj/item/weapon/stock_parts/subspace/ansible - name = "subspace ansible" - icon_state = "subspace_ansible" - desc = "A compact module capable of sensing extradimensional activity." - origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 5 ,TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - matter = list(MAT_STEEL = 30,MAT_GLASS = 10) - -/obj/item/weapon/stock_parts/subspace/sub_filter - name = "hyperwave filter" - icon_state = "hyperwave_filter" - desc = "A tiny device capable of filtering and converting super-intense radiowaves." - origin_tech = list(TECH_DATA = 4, TECH_MAGNET = 2) - matter = list(MAT_STEEL = 30,MAT_GLASS = 10) - -/obj/item/weapon/stock_parts/subspace/amplifier - name = "subspace amplifier" - icon_state = "subspace_amplifier" - desc = "A compact micro-machine capable of amplifying weak subspace transmissions." - origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - matter = list(MAT_STEEL = 30,MAT_GLASS = 10) - -/obj/item/weapon/stock_parts/subspace/treatment - name = "subspace treatment disk" - icon_state = "treatment_disk" - desc = "A compact micro-machine capable of stretching out hyper-compressed radio waves." - origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 2, TECH_MATERIAL = 5, TECH_BLUESPACE = 2) - matter = list(MAT_STEEL = 30,MAT_GLASS = 10) - -/obj/item/weapon/stock_parts/subspace/analyzer - name = "subspace wavelength analyzer" - icon_state = "wavelength_analyzer" - desc = "A sophisticated analyzer capable of analyzing cryptic subspace wavelengths." - origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - matter = list(MAT_STEEL = 30,MAT_GLASS = 10) - -/obj/item/weapon/stock_parts/subspace/crystal - name = "ansible crystal" - icon_state = "ansible_crystal" - desc = "A crystal made from pure glass used to transmit laser databursts to subspace." - origin_tech = list(TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) - matter = list(MAT_GLASS = 50) - -/obj/item/weapon/stock_parts/subspace/transmitter - name = "subspace transmitter" - icon_state = "subspace_transmitter" - desc = "A large piece of equipment used to open a window into the subspace dimension." - origin_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 5, TECH_BLUESPACE = 3) - matter = list(MAT_STEEL = 50) - -/obj/item/weapon/ectoplasm - name = "ectoplasm" - desc = "Spooky!" - gender = PLURAL - icon = 'icons/obj/wizard.dmi' - icon_state = "ectoplasm2" - -/obj/item/weapon/research - name = "research debugging device" - desc = "Instant research tool. For testing purposes only." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "smes_coil" - origin_tech = list(TECH_MATERIAL = 19, TECH_ENGINEERING = 19, TECH_PHORON = 19, TECH_POWER = 19, TECH_BLUESPACE = 19, TECH_BIO = 19, TECH_COMBAT = 19, TECH_MAGNET = 19, TECH_DATA = 19, TECH_ILLEGAL = 19, TECH_ARCANE = 19, TECH_PRECURSOR = 19) - -// Additional construction stock parts - -/obj/item/weapon/stock_parts/gear - name = "gear" - desc = "A gear used for construction." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "gear" - origin_tech = list(TECH_ENGINEERING = 1) - matter = list(MAT_STEEL = 50) - -/obj/item/weapon/stock_parts/motor - name = "motor" - desc = "A motor used for construction." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "motor" - origin_tech = list(TECH_ENGINEERING = 1) - matter = list(MAT_STEEL = 60, MAT_GLASS = 10) - -/obj/item/weapon/stock_parts/spring - name = "spring" - desc = "A spring used for construction." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "spring" - origin_tech = list(TECH_ENGINEERING = 1) - matter = list(MAT_STEEL = 40) - -/obj/effect/spawner/parts - name = "nondescript parts bundle that shouldn't exist" - desc = "this qdels itself lol! if you're reading this you're codediving or Someone fucked up" - var/list/items - -/obj/effect/spawner/parts/Initialize(mapload) - ..() - if(items && items.len) - var/turf/T = get_turf(src) - for(var/path in items) - for(var/i in 1 to 5) - new path(T) - return INITIALIZE_HINT_QDEL - -/obj/effect/spawner/parts/t1 - name = "basic parts bundle" - desc = "5 of each T1 part, no more and no less." - items = list( - /obj/item/weapon/stock_parts/matter_bin, - /obj/item/weapon/stock_parts/manipulator, - /obj/item/weapon/stock_parts/capacitor, - /obj/item/weapon/stock_parts/scanning_module, - /obj/item/weapon/stock_parts/micro_laser - ) - -/obj/effect/spawner/parts/t2 - name = "advanced parts bundle" - desc = "5 of each T2 part, no more and no less." - items = list( - /obj/item/weapon/stock_parts/matter_bin/adv, - /obj/item/weapon/stock_parts/manipulator/nano, - /obj/item/weapon/stock_parts/capacitor/adv, - /obj/item/weapon/stock_parts/scanning_module/adv, - /obj/item/weapon/stock_parts/micro_laser/high - ) - -/obj/effect/spawner/parts/t3 - name = "super parts bundle" - desc = "5 of each T3 part, no more and no less." - items = list( - /obj/item/weapon/stock_parts/matter_bin/super, - /obj/item/weapon/stock_parts/manipulator/pico, - /obj/item/weapon/stock_parts/capacitor/super, - /obj/item/weapon/stock_parts/scanning_module/phasic, - /obj/item/weapon/stock_parts/micro_laser/ultra - ) - -/obj/effect/spawner/parts/t4 - name = "hyper parts bundle" - desc = "5 of each T4 part, no more and no less." - items = list( - /obj/item/weapon/stock_parts/matter_bin/hyper, - /obj/item/weapon/stock_parts/manipulator/hyper, - /obj/item/weapon/stock_parts/capacitor/hyper, - /obj/item/weapon/stock_parts/scanning_module/hyper, - /obj/item/weapon/stock_parts/micro_laser/hyper - ) - -/obj/effect/spawner/parts/t5 - name = "omni parts bundle" - desc = "5 of each T5 part, no more and no less." - items = list( - /obj/item/weapon/stock_parts/matter_bin/omni, - /obj/item/weapon/stock_parts/manipulator/omni, - /obj/item/weapon/stock_parts/capacitor/omni, - /obj/item/weapon/stock_parts/scanning_module/omni, - /obj/item/weapon/stock_parts/micro_laser/omni - ) +/obj/item/weapon/phone + name = "red phone" + desc = "Should anything ever go wrong..." + icon = 'icons/obj/items.dmi' + icon_state = "red_phone" + force = 3.0 + throwforce = 2.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_SMALL + attack_verb = list("called", "rang") + hitsound = 'sound/weapons/ring.ogg' + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' + +/obj/item/weapon/rsp + name = "\improper Rapid-Seed-Producer (RSP)" + desc = "A device used to rapidly deploy seeds." + icon = 'icons/obj/items.dmi' + icon_state = "rcd" + opacity = 0 + density = FALSE + anchored = FALSE + var/stored_matter = 0 + var/mode = 1 + w_class = ITEMSIZE_NORMAL + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' + +/obj/item/weapon/soap + name = "soap" + desc = "A cheap bar of soap. Smells of lye." + gender = PLURAL + icon = 'icons/obj/soap.dmi' + icon_state = "soap" + flags = NOCONDUCT + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_HOLSTER + throwforce = 0 + throw_speed = 4 + throw_range = 20 + var/randomize = TRUE + var/square_chance = 10 + +/obj/item/weapon/soap/Initialize() + if(randomize && prob(square_chance)) + icon_state = "[icon_state]-alt" + +/obj/item/weapon/soap/nanotrasen + desc = "A NanoTrasen-brand bar of soap. Smells of phoron, a years-old marketing gimmick." + icon_state = "soapnt" + +/obj/item/weapon/soap/deluxe + icon_state = "soapdeluxe" + +/obj/item/weapon/soap/deluxe/New() + desc = "A deluxe Waffle Co. brand bar of soap. Smells of [pick("lavender", "vanilla", "strawberry", "chocolate" ,"space")]." + ..() + +/obj/item/weapon/soap/syndie + desc = "An untrustworthy bar of soap. Smells of fear." + icon_state = "soapsyndie" + +/obj/item/weapon/soap/space_soap + desc = "Smells like hot metal and walnuts." + icon_state = "space_soap" + +/obj/item/weapon/soap/water_soap + desc = "Smells like chlorine." + icon_state = "water_soap" + +/obj/item/weapon/soap/fire_soap + desc = "Smells like a campfire." + icon_state = "fire_soap" + +/obj/item/weapon/soap/rainbow_soap + desc = "Smells sickly sweet." + icon_state = "rainbow_soap" + +/obj/item/weapon/soap/diamond_soap + desc = "Smells like saffron and vanilla." + icon_state = "diamond_soap" + +/obj/item/weapon/soap/uranium_soap + desc = "Smells not great... Not terrible." + icon_state = "uranium_soap" + +/obj/item/weapon/soap/silver_soap + desc = "Smells like birch and amaranth." + icon_state = "silver_soap" + +/obj/item/weapon/soap/brown_soap + desc = "Smells like cinnamon and cognac." + icon_state = "brown_soap" + +/obj/item/weapon/soap/white_soap + desc = "Smells like nutmeg and oats." + icon_state = "white_soap" + +/obj/item/weapon/soap/grey_soap + desc = "Smells like bergamot and lilies." + icon_state = "grey_soap" + +/obj/item/weapon/soap/pink_soap + desc = "Smells like bubblegum." + icon_state = "pink_soap" + +/obj/item/weapon/soap/purple_soap + desc = "Smells like lavender." + icon_state = "purple_soap" + +/obj/item/weapon/soap/blue_soap + desc = "Smells like cardamom." + icon_state = "blue_soap" + +/obj/item/weapon/soap/cyan_soap + desc = "Smells like bluebells and peaches." + icon_state = "cyan_soap" + +/obj/item/weapon/soap/green_soap + desc = "Smells like a freshly mowed lawn." + icon_state = "green_soap" + +/obj/item/weapon/soap/yellow_soap + desc = "Smells like citron and ginger." + icon_state = "yellow_soap" + +/obj/item/weapon/soap/orange_soap + desc = "Smells like oranges and dark chocolate." + icon_state = "orange_soap" + +/obj/item/weapon/soap/red_soap + desc = "Smells like cherries." + icon_state = "red_soap" + +/obj/item/weapon/soap/golden_soap + desc = "Smells like honey." + icon_state = "golden_soap" + +/obj/item/weapon/bikehorn + name = "bike horn" + desc = "A horn off of a bicycle." + icon = 'icons/obj/items.dmi' + icon_state = "bike_horn" + item_state = "bike_horn" + throwforce = 3 + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_HOLSTER + throw_speed = 3 + throw_range = 15 + attack_verb = list("HONKED") + var/spam_flag = 0 + +/obj/item/weapon/c_tube + name = "cardboard tube" + desc = "A tube... of cardboard." + icon = 'icons/obj/items.dmi' + icon_state = "c_tube" + throwforce = 1 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 5 + +/obj/item/weapon/disk + name = "disk" + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + drop_sound = 'sound/items/drop/disk.ogg' + pickup_sound = 'sound/items/pickup/disk.ogg' + +/obj/item/weapon/disk/nuclear + name = "nuclear authentication disk" + desc = "Better keep this safe." + icon_state = "nucleardisk" + item_state = "card-id" + w_class = ITEMSIZE_SMALL + +/* +/obj/item/weapon/game_kit + name = "Gaming Kit" + icon = 'icons/obj/items.dmi' + icon_state = "game_kit" + var/selected = null + var/board_stat = null + var/data = "" + var/base_url = "http://svn.slurm.us/public/spacestation13/misc/game_kit" + item_state = "sheet-metal" + w_class = ITEMSIZE_HUGE +*/ + +/obj/item/weapon/gift + name = "gift" + desc = "A wrapped item." + icon = 'icons/obj/items.dmi' + icon_state = "gift3" + var/size = 3.0 + var/obj/item/gift = null + item_state = "gift" + w_class = ITEMSIZE_LARGE + +/*/obj/item/weapon/syndicate_uplink + name = "station bounced radio" + desc = "Remain silent about this..." + icon = 'icons/obj/radio.dmi' + icon_state = "radio" + var/temp = null + var/uses = 10.0 + var/selfdestruct = 0.0 + var/traitor_frequency = 0.0 + var/mob/currentUser = null + var/obj/item/device/radio/origradio = null + flags = ONBELT + w_class = ITEMSIZE_SMALL + item_state = "radio" + throw_speed = 4 + throw_range = 20 + matter = list(MAT_STEEL = 100) + origin_tech = list(TECH_MAGNET = 2, TECH_ILLEGAL = 3)*/ + +/obj/item/weapon/SWF_uplink + name = "station-bounced radio" + desc = "Used to communicate, it appears." + icon = 'icons/obj/radio.dmi' + icon_state = "radio" + var/temp = null + var/uses = 4.0 + var/selfdestruct = 0.0 + var/traitor_frequency = 0.0 + var/obj/item/device/radio/origradio = null + slot_flags = SLOT_BELT + item_state = "radio" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 20 + matter = list(MAT_STEEL = 100) + origin_tech = list(TECH_MAGNET = 1) + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' + +/obj/item/weapon/staff + name = "wizards staff" + desc = "Apparently a staff used by the wizard." + icon = 'icons/obj/wizard.dmi' + icon_state = "staff" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + force = 3.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + attack_verb = list("bludgeoned", "whacked", "disciplined") + +/obj/item/weapon/staff/broom + name = "broom" + desc = "Used for sweeping, and flying into the night while cackling. Black cat not included." + icon = 'icons/obj/wizard.dmi' + icon_state = "broom" + +/obj/item/weapon/staff/gentcane + name = "Gentlemans Cane" + desc = "An ebony can with an ivory tip." + icon = 'icons/obj/weapons.dmi' + icon_state = "cane" + +/obj/item/weapon/staff/stick + name = "stick" + desc = "A great tool to drag someone else's drinks across the bar." + icon = 'icons/obj/weapons.dmi' + icon_state = "stick" + item_state = "cane" + force = 3.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + +/obj/item/weapon/module + icon = 'icons/obj/module.dmi' + icon_state = "std_module" + item_state = "std_mod" + w_class = ITEMSIZE_SMALL + var/mtype = 1 // 1=electronic 2=hardware + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + +/obj/item/weapon/module/card_reader + name = "card reader module" + icon_state = "card_mod" + item_state = "std_mod" + desc = "An electronic module for reading data and ID cards." + +/obj/item/weapon/module/power_control + name = "power control module" + icon_state = "power_mod" + item_state = "std_mod" + desc = "Heavy-duty switching circuits for power control." + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + +/obj/item/weapon/module/id_auth + name = "\improper ID authentication module" + icon_state = "id_mod" + desc = "A module allowing secure authorization of ID cards." + +/obj/item/weapon/module/cell_power + name = "power cell regulator module" + icon_state = "power_mod" + item_state = "std_mod" + desc = "A converter and regulator allowing the use of power cells." + +/obj/item/weapon/module/cell_power + name = "power cell charger module" + icon_state = "power_mod" + item_state = "std_mod" + desc = "Charging circuits for power cells." + +/obj/item/device/camera_bug + name = "camera bug" + icon = 'icons/obj/device.dmi' + icon_state = "flash" + w_class = ITEMSIZE_TINY + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + +/obj/item/weapon/camera_bug/attack_self(mob/usr as mob) + var/list/cameras = new/list() + for (var/obj/machinery/camera/C in cameranet.cameras) + if (C.bugged && C.status) + cameras.Add(C) + if (length(cameras) == 0) + to_chat(usr, "No bugged functioning cameras found.") + return + + var/list/friendly_cameras = new/list() + + for (var/obj/machinery/camera/C in cameras) + friendly_cameras.Add(C.c_tag) + + var/target = tgui_input_list(usr, "Select the camera to observe", "Select Camera", friendly_cameras) + if (!target) + return + for (var/obj/machinery/camera/C in cameras) + if (C.c_tag == target) + target = C + break + if (usr.stat == 2) return + + usr.client.eye = target + +/* +/obj/item/weapon/cigarpacket + name = "Pete's Cuban Cigars" + desc = "The most robust cigars on the planet." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "cigarpacket" + item_state = "cigarpacket" + w_class = ITEMSIZE_TINY + throwforce = 2 + var/cigarcount = 6 + flags = ONBELT + */ + +/obj/item/weapon/pai_cable + desc = "A flexible coated cable with a universal jack on one end." + name = "data cable" + icon = 'icons/obj/power.dmi' + icon_state = "wire1" + + var/obj/machinery/machine + +/obj/item/weapon/pai_cable/Destroy() + machine = null + return ..() + +///////////////////////////////////////Stock Parts ///////////////////////////////// + +/obj/item/weapon/storage/part_replacer + name = "rapid part exchange device" + desc = "A special mechanical module made to store, sort, and apply standard machine parts." + icon = 'icons/obj/storage_vr.dmi' + icon_state = "RPED" + item_state = "RPED" + w_class = ITEMSIZE_HUGE + can_hold = list(/obj/item/weapon/stock_parts) + storage_slots = 50 + use_to_pickup = TRUE + allow_quick_gather = 1 + allow_quick_empty = 1 + collection_mode = 1 + display_contents_with_number = 1 + max_w_class = ITEMSIZE_NORMAL + max_storage_space = 100 + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' + var/panel_req = TRUE + var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg' + var/reskin_ran = FALSE + var/unique_reskin = list("Soulless" = "RPED", + "Soulful" = "RPED_old") + +/obj/item/weapon/storage/part_replacer/proc/play_rped_sound() + //Plays the sound for RPED exhanging or installing parts. +/* if(alt_sound && prob(1)) + playsound(src, alt_sound, 40, 1) + else +*/ + playsound(src, pshoom_or_beepboopblorpzingshadashwoosh, 40, 1) + +/obj/item/weapon/storage/part_replacer/adv + name = "advanced rapid part exchange device" + desc = "A special mechanical module made to store, sort, and apply standard machine parts. This one has a greatly upgraded storage capacity, \ + and the ability to hold beakers." + can_hold = list(/obj/item/weapon/stock_parts, /obj/item/weapon/reagent_containers/glass/beaker) + storage_slots = 200 + max_storage_space = 400 + +/obj/item/weapon/storage/part_replacer/adv/discount_bluespace + name = "prototype bluespace rapid part exchange device" + icon_state = "DBRPED" + item_state = "DBRPED" + desc = "A special mechanical module made to store, sort, and apply standard machine parts. This one has a further increased storage capacity, \ + and the ability to work on machines with closed maintenance panels." + storage_slots = 400 + max_storage_space = 800 + panel_req = FALSE + pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/pshoom.ogg' + unique_reskin = list("Soulless" = "DBRPED", + "Soulful" = "DBRPED_old") + +/obj/item/weapon/storage/part_replacer/examine(mob/user) + . = ..() + if(!reskin_ran) + . += "[src]'s external casing can be modified via alt-click." + +/obj/item/weapon/storage/part_replacer/AltClick(mob/user) + . = ..() + if(!reskin_ran) + reskin_radial(user) + +/obj/item/weapon/storage/part_replacer/proc/reskin_radial(mob/M) + if(!LAZYLEN(unique_reskin)) + return + + var/list/items = list() + for(var/reskin_option in unique_reskin) + var/image/item_image = image(icon = src.icon, icon_state = unique_reskin[reskin_option]) + items += list("[reskin_option]" = item_image) + sortList(items) + + var/pick = show_radial_menu(M, src, items, radius = 38, require_near = TRUE) + if(!pick) + return + if(!unique_reskin[pick]) + return + icon_state = unique_reskin[pick] + item_state = unique_reskin[pick] + reskin_ran = TRUE + to_chat(M, "[src] is now '[pick]'.") + +/obj/item/weapon/storage/part_replacer/drop_contents() // hacky-feeling tier-based drop system + hide_from(usr) + var/turf/T = get_turf(src) + var/lowest_rating = INFINITY // We want the lowest-part tier rating in the RPED so we only drop the lowest-tier parts. + /* + * Why not just use the stock part's rating variable? + * Future-proofing for a potential future where stock parts aren't the only thing that can fit in an RPED. + * see: /tg/ and /vg/'s RPEDs fitting power cells, beakers, etc. + * 10/8/21 edit - It's Time. + */ + for(var/obj/item/B in contents) + if(B.rped_rating() < lowest_rating) + lowest_rating = B.rped_rating() + for(var/obj/item/B in contents) + if(B.rped_rating() > lowest_rating) + continue + remove_from_storage(B, T) + + +/obj/item/weapon/stock_parts + name = "stock part" + desc = "What?" + gender = PLURAL + icon = 'icons/obj/stock_parts.dmi' + w_class = ITEMSIZE_SMALL + var/rating = 1 + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + +/obj/item/weapon/stock_parts/New() + src.pixel_x = rand(-5.0, 5) + src.pixel_y = rand(-5.0, 5) + ..() + +/obj/item/weapon/stock_parts/get_rating() + return rating + +//Rank 1 + +/obj/item/weapon/stock_parts/console_screen + name = "console screen" + desc = "Used in the construction of computers and other devices with a interactive console." + icon_state = "screen" + origin_tech = list(TECH_MATERIAL = 1) + rating = 5 // these are actually Really Important for some things?? + matter = list(MAT_GLASS = 200) + +/obj/item/weapon/stock_parts/capacitor + name = "capacitor" + desc = "A basic capacitor used in the construction of a variety of devices." + icon_state = "capacitor" + origin_tech = list(TECH_POWER = 1) + matter = list(MAT_STEEL = 50,MAT_GLASS = 50) + + var/charge = 0 + var/max_charge = 1000 + +/obj/item/weapon/stock_parts/capacitor/New() + . = ..() + max_charge *= rating + +/obj/item/weapon/stock_parts/capacitor/proc/charge(var/amount) + charge += amount + if(charge > max_charge) + charge = max_charge + +/obj/item/weapon/stock_parts/capacitor/proc/use(var/amount) + if(charge) + charge -= amount + if(charge < 0) + charge = 0 + +/obj/item/weapon/stock_parts/scanning_module + name = "scanning module" + desc = "A compact, high resolution scanning module used in the construction of certain devices." + icon_state = "scan_module" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + +/obj/item/weapon/stock_parts/manipulator + name = "micro-manipulator" + desc = "A tiny little manipulator used in the construction of certain devices." + icon_state = "micro_mani" + origin_tech = list(TECH_MATERIAL = 1, TECH_DATA = 1) + matter = list(MAT_STEEL = 30) + +/obj/item/weapon/stock_parts/micro_laser + name = "micro-laser" + desc = "A tiny laser used in certain devices." + icon_state = "micro_laser" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 10,MAT_GLASS = 20) + +/obj/item/weapon/stock_parts/matter_bin + name = "matter bin" + desc = "A container for hold compressed matter awaiting re-construction." + icon_state = "matter_bin" + origin_tech = list(TECH_MATERIAL = 1) + matter = list(MAT_STEEL = 80) + +//Rank 2 + +/obj/item/weapon/stock_parts/capacitor/adv + name = "advanced capacitor" + desc = "An advanced capacitor used in the construction of a variety of devices." + icon_state = "capacitor_adv" + origin_tech = list(TECH_POWER = 3) + rating = 2 + matter = list(MAT_STEEL = 50,MAT_GLASS = 50) + +/obj/item/weapon/stock_parts/scanning_module/adv + name = "advanced scanning module" + desc = "A compact, high resolution scanning module used in the construction of certain devices." + icon_state = "scan_module_adv" + origin_tech = list(TECH_MAGNET = 3) + rating = 2 + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + +/obj/item/weapon/stock_parts/manipulator/nano + name = "nano-manipulator" + desc = "A tiny little manipulator used in the construction of certain devices." + icon_state = "nano_mani" + origin_tech = list(TECH_MATERIAL = 3, TECH_DATA = 2) + rating = 2 + matter = list(MAT_STEEL = 30) + +/obj/item/weapon/stock_parts/micro_laser/high + name = "high-power micro-laser" + desc = "A tiny laser used in certain devices." + icon_state = "high_micro_laser" + origin_tech = list(TECH_MAGNET = 3) + rating = 2 + matter = list(MAT_STEEL = 10,MAT_GLASS = 20) + +/obj/item/weapon/stock_parts/matter_bin/adv + name = "advanced matter bin" + desc = "A container for hold compressed matter awaiting re-construction." + icon_state = "advanced_matter_bin" + origin_tech = list(TECH_MATERIAL = 3) + rating = 2 + matter = list(MAT_STEEL = 80) + +//Rating 3 + +/obj/item/weapon/stock_parts/capacitor/super + name = "super capacitor" + desc = "A super-high capacity capacitor used in the construction of a variety of devices." + icon_state = "capacitor_super" + origin_tech = list(TECH_POWER = 5, TECH_MATERIAL = 4) + rating = 3 + matter = list(MAT_STEEL = 50,MAT_GLASS = 50) + +/obj/item/weapon/stock_parts/scanning_module/phasic + name = "phasic scanning module" + desc = "A compact, high resolution phasic scanning module used in the construction of certain devices." + icon_state = "scan_module_phasic" + origin_tech = list(TECH_MAGNET = 5) + rating = 3 + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + +/obj/item/weapon/stock_parts/manipulator/pico + name = "pico-manipulator" + desc = "A tiny little manipulator used in the construction of certain devices." + icon_state = "pico_mani" + origin_tech = list(TECH_MATERIAL = 5, TECH_DATA = 2) + rating = 3 + matter = list(MAT_STEEL = 30) + +/obj/item/weapon/stock_parts/micro_laser/ultra + name = "ultra-high-power micro-laser" + icon_state = "ultra_high_micro_laser" + desc = "A tiny laser used in certain devices." + origin_tech = list(TECH_MAGNET = 5) + rating = 3 + matter = list(MAT_STEEL = 10,MAT_GLASS = 20) + +/obj/item/weapon/stock_parts/matter_bin/super + name = "super matter bin" + desc = "A container for hold compressed matter awaiting re-construction." + icon_state = "super_matter_bin" + origin_tech = list(TECH_MATERIAL = 5) + rating = 3 + matter = list(MAT_STEEL = 80) + +// Rating 4 - Anomaly + +/obj/item/weapon/stock_parts/capacitor/hyper + name = "hyper capacitor" + desc = "A hyper-capacity capacitor used in the construction of a variety of devices." + icon_state = "capacitor_hyper" + origin_tech = list(TECH_POWER = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 1, TECH_ARCANE = 1) + rating = 4 + matter = list(MAT_STEEL = 80, MAT_GLASS = 40) + +/obj/item/weapon/stock_parts/scanning_module/hyper + name = "quantum scanning module" + desc = "A compact, near-perfect resolution quantum scanning module used in the construction of certain devices." + icon_state = "scan_module_hyper" + origin_tech = list(TECH_MAGNET = 6, TECH_BLUESPACE = 1, TECH_ARCANE = 1) + rating = 4 + matter = list(MAT_STEEL = 100,MAT_GLASS = 40) + +/obj/item/weapon/stock_parts/manipulator/hyper + name = "planck-manipulator" + desc = "A miniscule manipulator used in the construction of certain devices." + icon_state = "hyper_mani" + origin_tech = list(TECH_MATERIAL = 6, TECH_DATA = 3, TECH_ARCANE = 1) + rating = 4 + matter = list(MAT_STEEL = 30) + +/obj/item/weapon/stock_parts/micro_laser/hyper + name = "hyper-power micro-laser" + icon_state = "hyper_micro_laser" + desc = "A tiny laser used in certain devices." + origin_tech = list(TECH_MAGNET = 6, TECH_ARCANE = 1) + rating = 4 + matter = list(MAT_STEEL = 30, MAT_GLASS = 40) + +/obj/item/weapon/stock_parts/matter_bin/hyper + name = "hyper matter bin" + desc = "A container for holding compressed matter awaiting re-construction." + icon_state = "hyper_matter_bin" + origin_tech = list(TECH_MATERIAL = 6, TECH_ARCANE = 1) + rating = 4 + matter = list(MAT_STEEL = 100) + +// Rating 5 - Precursor + +/obj/item/weapon/stock_parts/capacitor/omni + name = "omni-capacitor" + desc = "A capacitor of immense capacity used in the construction of a variety of devices." + icon_state = "capacitor_omni" + origin_tech = list(TECH_POWER = 7, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) + rating = 5 + matter = list(MAT_STEEL = 80, MAT_GLASS = 40) + +/obj/item/weapon/stock_parts/scanning_module/omni + name = "omni-scanning module" + desc = "A compact, perfect resolution temporospatial scanning module used in the construction of certain devices." + icon_state = "scan_module_omni" + origin_tech = list(TECH_MAGNET = 7, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1) + rating = 5 + matter = list(MAT_STEEL = 100,MAT_GLASS = 40) + +/obj/item/weapon/stock_parts/manipulator/omni + name = "omni-manipulator" + desc = "A strange, infinitesimal manipulator used in the construction of certain devices." + icon_state = "omni_mani" + origin_tech = list(TECH_MATERIAL = 7, TECH_DATA = 4, TECH_PRECURSOR = 1) + rating = 5 + matter = list(MAT_STEEL = 30) + +/obj/item/weapon/stock_parts/micro_laser/omni + name = "omni-power micro-laser" + icon_state = "omni_micro_laser" + desc = "A strange laser used in certain devices." + origin_tech = list(TECH_MAGNET = 7, TECH_PRECURSOR = 1) + rating = 5 + matter = list(MAT_STEEL = 30, MAT_GLASS = 40) + +/obj/item/weapon/stock_parts/matter_bin/omni + name = "omni-matter bin" + desc = "A strange container for holding compressed matter awaiting re-construction." + icon_state = "omni_matter_bin" + origin_tech = list(TECH_MATERIAL = 7, TECH_PRECURSOR = 1) + rating = 5 + matter = list(MAT_STEEL = 100) + + +// Subspace stock parts + +/obj/item/weapon/stock_parts/subspace/ansible + name = "subspace ansible" + icon_state = "subspace_ansible" + desc = "A compact module capable of sensing extradimensional activity." + origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 5 ,TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + matter = list(MAT_STEEL = 30,MAT_GLASS = 10) + +/obj/item/weapon/stock_parts/subspace/sub_filter + name = "hyperwave filter" + icon_state = "hyperwave_filter" + desc = "A tiny device capable of filtering and converting super-intense radiowaves." + origin_tech = list(TECH_DATA = 4, TECH_MAGNET = 2) + matter = list(MAT_STEEL = 30,MAT_GLASS = 10) + +/obj/item/weapon/stock_parts/subspace/amplifier + name = "subspace amplifier" + icon_state = "subspace_amplifier" + desc = "A compact micro-machine capable of amplifying weak subspace transmissions." + origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + matter = list(MAT_STEEL = 30,MAT_GLASS = 10) + +/obj/item/weapon/stock_parts/subspace/treatment + name = "subspace treatment disk" + icon_state = "treatment_disk" + desc = "A compact micro-machine capable of stretching out hyper-compressed radio waves." + origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 2, TECH_MATERIAL = 5, TECH_BLUESPACE = 2) + matter = list(MAT_STEEL = 30,MAT_GLASS = 10) + +/obj/item/weapon/stock_parts/subspace/analyzer + name = "subspace wavelength analyzer" + icon_state = "wavelength_analyzer" + desc = "A sophisticated analyzer capable of analyzing cryptic subspace wavelengths." + origin_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + matter = list(MAT_STEEL = 30,MAT_GLASS = 10) + +/obj/item/weapon/stock_parts/subspace/crystal + name = "ansible crystal" + icon_state = "ansible_crystal" + desc = "A crystal made from pure glass used to transmit laser databursts to subspace." + origin_tech = list(TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2) + matter = list(MAT_GLASS = 50) + +/obj/item/weapon/stock_parts/subspace/transmitter + name = "subspace transmitter" + icon_state = "subspace_transmitter" + desc = "A large piece of equipment used to open a window into the subspace dimension." + origin_tech = list(TECH_MAGNET = 5, TECH_MATERIAL = 5, TECH_BLUESPACE = 3) + matter = list(MAT_STEEL = 50) + +/obj/item/weapon/ectoplasm + name = "ectoplasm" + desc = "Spooky!" + gender = PLURAL + icon = 'icons/obj/wizard.dmi' + icon_state = "ectoplasm2" + +/obj/item/weapon/research + name = "research debugging device" + desc = "Instant research tool. For testing purposes only." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "smes_coil" + origin_tech = list(TECH_MATERIAL = 19, TECH_ENGINEERING = 19, TECH_PHORON = 19, TECH_POWER = 19, TECH_BLUESPACE = 19, TECH_BIO = 19, TECH_COMBAT = 19, TECH_MAGNET = 19, TECH_DATA = 19, TECH_ILLEGAL = 19, TECH_ARCANE = 19, TECH_PRECURSOR = 19) + +// Additional construction stock parts + +/obj/item/weapon/stock_parts/gear + name = "gear" + desc = "A gear used for construction." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "gear" + origin_tech = list(TECH_ENGINEERING = 1) + matter = list(MAT_STEEL = 50) + +/obj/item/weapon/stock_parts/motor + name = "motor" + desc = "A motor used for construction." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "motor" + origin_tech = list(TECH_ENGINEERING = 1) + matter = list(MAT_STEEL = 60, MAT_GLASS = 10) + +/obj/item/weapon/stock_parts/spring + name = "spring" + desc = "A spring used for construction." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "spring" + origin_tech = list(TECH_ENGINEERING = 1) + matter = list(MAT_STEEL = 40) + +/obj/effect/spawner/parts + name = "nondescript parts bundle that shouldn't exist" + desc = "this qdels itself lol! if you're reading this you're codediving or Someone fucked up" + var/list/items + +/obj/effect/spawner/parts/Initialize(mapload) + ..() + if(items && items.len) + var/turf/T = get_turf(src) + for(var/path in items) + for(var/i in 1 to 5) + new path(T) + return INITIALIZE_HINT_QDEL + +/obj/effect/spawner/parts/t1 + name = "basic parts bundle" + desc = "5 of each T1 part, no more and no less." + items = list( + /obj/item/weapon/stock_parts/matter_bin, + /obj/item/weapon/stock_parts/manipulator, + /obj/item/weapon/stock_parts/capacitor, + /obj/item/weapon/stock_parts/scanning_module, + /obj/item/weapon/stock_parts/micro_laser + ) + +/obj/effect/spawner/parts/t2 + name = "advanced parts bundle" + desc = "5 of each T2 part, no more and no less." + items = list( + /obj/item/weapon/stock_parts/matter_bin/adv, + /obj/item/weapon/stock_parts/manipulator/nano, + /obj/item/weapon/stock_parts/capacitor/adv, + /obj/item/weapon/stock_parts/scanning_module/adv, + /obj/item/weapon/stock_parts/micro_laser/high + ) + +/obj/effect/spawner/parts/t3 + name = "super parts bundle" + desc = "5 of each T3 part, no more and no less." + items = list( + /obj/item/weapon/stock_parts/matter_bin/super, + /obj/item/weapon/stock_parts/manipulator/pico, + /obj/item/weapon/stock_parts/capacitor/super, + /obj/item/weapon/stock_parts/scanning_module/phasic, + /obj/item/weapon/stock_parts/micro_laser/ultra + ) + +/obj/effect/spawner/parts/t4 + name = "hyper parts bundle" + desc = "5 of each T4 part, no more and no less." + items = list( + /obj/item/weapon/stock_parts/matter_bin/hyper, + /obj/item/weapon/stock_parts/manipulator/hyper, + /obj/item/weapon/stock_parts/capacitor/hyper, + /obj/item/weapon/stock_parts/scanning_module/hyper, + /obj/item/weapon/stock_parts/micro_laser/hyper + ) + +/obj/effect/spawner/parts/t5 + name = "omni parts bundle" + desc = "5 of each T5 part, no more and no less." + items = list( + /obj/item/weapon/stock_parts/matter_bin/omni, + /obj/item/weapon/stock_parts/manipulator/omni, + /obj/item/weapon/stock_parts/capacitor/omni, + /obj/item/weapon/stock_parts/scanning_module/omni, + /obj/item/weapon/stock_parts/micro_laser/omni + ) diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index c29ffef73fb..de99cb0d684 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -1,181 +1,181 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/* -A Star pathfinding algorithm -Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of -windows along the route into account. -Use: -your_list = AStar(start location, end location, adjacent turf proc, distance proc) -For the adjacent turf proc i wrote: -/turf/proc/AdjacentTurfs -And for the distance one i wrote: -/turf/proc/Distance -So an example use might be: - -src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance) - -Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use. - -src.path_list = reverselist(src.pathlist) - -Then to start on the path, all you need to do it: -Step_to(src, src.path_list[1]) -src.path_list -= src.path_list[1] or equivilent to remove that node from the list. - -Optional extras to add on (in order): -MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) -Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) -Mintargetdist: Minimum distance to the target before path returns, could be used to get -near a target, but not right to it - for an AI mob with a gun, for example. -Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum -length to avoid portals or something i guess?? Not that they're counted right now but w/e. -*/ - -// Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null -// Used for checking if route exists through a door which can be opened - -// Also added 'exclude' turf to avoid travelling over; defaults to null - - -/PriorityQueue - var/list/queue - var/comparison_function - -/PriorityQueue/New(compare) - queue = list() - comparison_function = compare - -/PriorityQueue/proc/IsEmpty() - return !queue.len - -/PriorityQueue/proc/Enqueue(var/data) - queue.Add(data) - var/index = queue.len - - //From what I can tell, this automagically sorts the added data into the correct location. - while(index > 2 && call(comparison_function)(queue[index / 2], queue[index]) > 0) - queue.Swap(index, index / 2) - index /= 2 - -/PriorityQueue/proc/Dequeue() - if(!queue.len) - return 0 - return Remove(1) - -/PriorityQueue/proc/Remove(var/index) - if(index > queue.len) - return 0 - - var/thing = queue[index] - queue.Swap(index, queue.len) - queue.Cut(queue.len) - if(index < queue.len) - FixQueue(index) - return thing - -/PriorityQueue/proc/FixQueue(var/index) - var/child = 2 * index - var/item = queue[index] - - while(child <= queue.len) - if(child < queue.len && call(comparison_function)(queue[child], queue[child + 1]) > 0) - child++ - if(call(comparison_function)(item, queue[child]) > 0) - queue[index] = queue[child] - index = child - else - break - child = 2 * index - queue[index] = item - -/PriorityQueue/proc/List() - return queue.Copy() - -/PriorityQueue/proc/Length() - return queue.len - -/PriorityQueue/proc/RemoveItem(data) - var/index = queue.Find(data) - if(index) - return Remove(index) - -/PathNode - var/datum/position - var/PathNode/previous_node - - var/best_estimated_cost - var/estimated_cost - var/known_cost - var/cost - var/nodes_traversed - -/PathNode/New(_position, _previous_node, _known_cost, _cost, _nodes_traversed) - position = _position - previous_node = _previous_node - - known_cost = _known_cost - cost = _cost - estimated_cost = cost + known_cost - - best_estimated_cost = estimated_cost - nodes_traversed = _nodes_traversed - -/proc/PathWeightCompare(PathNode/a, PathNode/b) - return a.estimated_cost - b.estimated_cost - -/proc/AStar(var/start, var/end, var/adjacent, var/dist, var/max_nodes, var/max_node_depth = 30, var/min_target_dist = 0, var/min_node_dist, var/id, var/datum/exclude) - var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) - var/list/closed = list() - var/list/path - var/list/path_node_by_position = list() - start = get_turf(start) - if(!start) - return 0 - - open.Enqueue(new /PathNode(start, null, 0, call(start, dist)(end), 0)) - - while(!open.IsEmpty() && !path) - var/PathNode/current = open.Dequeue() - closed.Add(current.position) - - if(current.position == end || call(current.position, dist)(end) <= min_target_dist) - path = new /list(current.nodes_traversed + 1) - path[path.len] = current.position - var/index = path.len - 1 - - while(current.previous_node) - current = current.previous_node - path[index--] = current.position - break - - if(min_node_dist && max_node_depth) - if(call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth) - continue - - if(max_node_depth) - if(current.nodes_traversed >= max_node_depth) - continue - - for(var/datum/datum in call(current.position, adjacent)(id)) - if(datum == exclude) - continue - - var/best_estimated_cost = current.estimated_cost + call(current.position, dist)(datum) - - //handle removal of sub-par positions - if(datum in path_node_by_position) - var/PathNode/target = path_node_by_position[datum] - if(target.best_estimated_cost) - if(best_estimated_cost + call(datum, dist)(end) < target.best_estimated_cost) - open.RemoveItem(target) - else - continue - - var/PathNode/next_node = new (datum, current, best_estimated_cost, call(datum, dist)(end), current.nodes_traversed + 1) - path_node_by_position[datum] = next_node - open.Enqueue(next_node) - - if(max_nodes && open.Length() > max_nodes) - open.Remove(open.Length()) - - return path +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/* +A Star pathfinding algorithm +Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of +windows along the route into account. +Use: +your_list = AStar(start location, end location, adjacent turf proc, distance proc) +For the adjacent turf proc i wrote: +/turf/proc/AdjacentTurfs +And for the distance one i wrote: +/turf/proc/Distance +So an example use might be: + +src.path_list = AStar(src.loc, target.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance) + +Note: The path is returned starting at the END node, so i wrote reverselist to reverse it for ease of use. + +src.path_list = reverselist(src.pathlist) + +Then to start on the path, all you need to do it: +Step_to(src, src.path_list[1]) +src.path_list -= src.path_list[1] or equivilent to remove that node from the list. + +Optional extras to add on (in order): +MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) +Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) +Mintargetdist: Minimum distance to the target before path returns, could be used to get +near a target, but not right to it - for an AI mob with a gun, for example. +Minnodedist: Minimum number of nodes to return in the path, could be used to give a path a minimum +length to avoid portals or something i guess?? Not that they're counted right now but w/e. +*/ + +// Modified to provide ID argument - supplied to 'adjacent' proc, defaults to null +// Used for checking if route exists through a door which can be opened + +// Also added 'exclude' turf to avoid travelling over; defaults to null + + +/PriorityQueue + var/list/queue + var/comparison_function + +/PriorityQueue/New(compare) + queue = list() + comparison_function = compare + +/PriorityQueue/proc/IsEmpty() + return !queue.len + +/PriorityQueue/proc/Enqueue(var/data) + queue.Add(data) + var/index = queue.len + + //From what I can tell, this automagically sorts the added data into the correct location. + while(index > 2 && call(comparison_function)(queue[index / 2], queue[index]) > 0) + queue.Swap(index, index / 2) + index /= 2 + +/PriorityQueue/proc/Dequeue() + if(!queue.len) + return 0 + return Remove(1) + +/PriorityQueue/proc/Remove(var/index) + if(index > queue.len) + return 0 + + var/thing = queue[index] + queue.Swap(index, queue.len) + queue.Cut(queue.len) + if(index < queue.len) + FixQueue(index) + return thing + +/PriorityQueue/proc/FixQueue(var/index) + var/child = 2 * index + var/item = queue[index] + + while(child <= queue.len) + if(child < queue.len && call(comparison_function)(queue[child], queue[child + 1]) > 0) + child++ + if(call(comparison_function)(item, queue[child]) > 0) + queue[index] = queue[child] + index = child + else + break + child = 2 * index + queue[index] = item + +/PriorityQueue/proc/List() + return queue.Copy() + +/PriorityQueue/proc/Length() + return queue.len + +/PriorityQueue/proc/RemoveItem(data) + var/index = queue.Find(data) + if(index) + return Remove(index) + +/PathNode + var/datum/position + var/PathNode/previous_node + + var/best_estimated_cost + var/estimated_cost + var/known_cost + var/cost + var/nodes_traversed + +/PathNode/New(_position, _previous_node, _known_cost, _cost, _nodes_traversed) + position = _position + previous_node = _previous_node + + known_cost = _known_cost + cost = _cost + estimated_cost = cost + known_cost + + best_estimated_cost = estimated_cost + nodes_traversed = _nodes_traversed + +/proc/PathWeightCompare(PathNode/a, PathNode/b) + return a.estimated_cost - b.estimated_cost + +/proc/AStar(var/start, var/end, var/adjacent, var/dist, var/max_nodes, var/max_node_depth = 30, var/min_target_dist = 0, var/min_node_dist, var/id, var/datum/exclude) + var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) + var/list/closed = list() + var/list/path + var/list/path_node_by_position = list() + start = get_turf(start) + if(!start) + return 0 + + open.Enqueue(new /PathNode(start, null, 0, call(start, dist)(end), 0)) + + while(!open.IsEmpty() && !path) + var/PathNode/current = open.Dequeue() + closed.Add(current.position) + + if(current.position == end || call(current.position, dist)(end) <= min_target_dist) + path = new /list(current.nodes_traversed + 1) + path[path.len] = current.position + var/index = path.len - 1 + + while(current.previous_node) + current = current.previous_node + path[index--] = current.position + break + + if(min_node_dist && max_node_depth) + if(call(current.position, min_node_dist)(end) + current.nodes_traversed >= max_node_depth) + continue + + if(max_node_depth) + if(current.nodes_traversed >= max_node_depth) + continue + + for(var/datum/datum in call(current.position, adjacent)(id)) + if(datum == exclude) + continue + + var/best_estimated_cost = current.estimated_cost + call(current.position, dist)(datum) + + //handle removal of sub-par positions + if(datum in path_node_by_position) + var/PathNode/target = path_node_by_position[datum] + if(target.best_estimated_cost) + if(best_estimated_cost + call(datum, dist)(end) < target.best_estimated_cost) + open.RemoveItem(target) + else + continue + + var/PathNode/next_node = new (datum, current, best_estimated_cost, call(datum, dist)(end), current.nodes_traversed + 1) + path_node_by_position[datum] = next_node + open.Enqueue(next_node) + + if(max_nodes && open.Length() > max_nodes) + open.Remove(open.Length()) + + return path diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index e0f8ab94af9..3a89c9ef8c4 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -1,208 +1,208 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -//cursors -#define Default_Cursor 0 -#define Client_Cursor 1 -#define Server_Cursor 2 -//conversions -#define TEXT_CONV 1 -#define RSC_FILE_CONV 2 -#define NUMBER_CONV 3 -//column flag values: -#define IS_NUMERIC 1 -#define IS_BINARY 2 -#define IS_NOT_NULL 4 -#define IS_PRIMARY_KEY 8 -#define IS_UNSIGNED 16 -//types -#define TINYINT 1 -#define SMALLINT 2 -#define MEDIUMINT 3 -#define INTEGER 4 -#define BIGINT 5 -#define DECIMAL 6 -#define FLOAT 7 -#define DOUBLE 8 -#define DATE 9 -#define DATETIME 10 -#define TIMESTAMP 11 -#define TIME 12 -#define STRING 13 -#define BLOB 14 -// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew - - -// Deprecated! See global.dm for new configuration vars -/* -var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) -var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) -*/ - -/DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/dbi // This variable is a string containing the DBI MySQL requires. - var/user // This variable contains the username data. - var/password // This variable contains the password data. - var/default_cursor // This contains the default database cursor data. - // - var/server = "" - var/port = 3306 - -/DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler - _db_con = _dm_db_new_con() - -/DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!config.sql_enabled) - return 0 - if(!src) return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) - -/DBConnection/proc/Disconnect() return _dm_db_close(_db_con) - -/DBConnection/proc/IsConnected() - if(!config.sql_enabled) return 0 - var/success = _dm_db_is_connected(_db_con) - return success - -/DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) - -/DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) -/DBConnection/proc/SelectDB(database_name,dbi) - if(IsConnected()) Disconnect() - //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -/DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) - - -/DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) src.sql = sql_query - if(connection_handler) src.db_connection = connection_handler - if(cursor_handler) src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - - -/DBQuery - var/sql // The sql query being executed. - var/default_cursor - var/list/columns //list of DB Columns populated by Columns() - var/list/conversions - var/list/item[0] //list of data values populated by NextRow() - - var/DBConnection/db_connection - var/_db_query - -/DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler - -/DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) - Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) - -/DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) - -/DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) - -/DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) - -/DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) - -/DBQuery/proc/Columns() - if(!columns) - columns = _dm_db_columns(_db_query,/DBColumn) - return columns - -/DBQuery/proc/GetRowData() - var/list/columns = Columns() - var/list/results - if(columns.len) - results = list() - for(var/C in columns) - results+=C - var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] - return results - -/DBQuery/proc/Close() - item.len = 0 - columns = null - conversions = null - return _dm_db_close(_db_query) - -/DBQuery/proc/Quote(str) - return db_connection.Quote(str) - -/DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) column = columns.Find(column) - if(!conversions) conversions = new/list(column) - else if(conversions.len < column) conversions.len = column - conversions[column] = conversion - - -/DBColumn - var/name - var/table - var/position //1-based index into item data - var/sql_type - var/flags - var/length - var/max_length - -/DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler - return ..() - - -/DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) return "TINYINT" - if(SMALLINT) return "SMALLINT" - if(MEDIUMINT) return "MEDIUMINT" - if(INTEGER) return "INTEGER" - if(BIGINT) return "BIGINT" - if(FLOAT) return "FLOAT" - if(DOUBLE) return "DOUBLE" - if(DATE) return "DATE" - if(DATETIME) return "DATETIME" - if(TIMESTAMP) return "TIMESTAMP" - if(TIME) return "TIME" - if(STRING) return "STRING" - if(BLOB) return "BLOB" - - -#undef Default_Cursor -#undef Client_Cursor -#undef Server_Cursor -#undef TEXT_CONV -#undef RSC_FILE_CONV -#undef NUMBER_CONV -#undef IS_NUMERIC -#undef IS_BINARY -#undef IS_NOT_NULL -#undef IS_PRIMARY_KEY -#undef IS_UNSIGNED -#undef TINYINT -#undef SMALLINT -#undef MEDIUMINT -#undef INTEGER -#undef BIGINT -#undef DECIMAL -#undef FLOAT -#undef DOUBLE -#undef DATE -#undef DATETIME -#undef TIMESTAMP -#undef TIME -#undef STRING -#undef BLOB +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +//cursors +#define Default_Cursor 0 +#define Client_Cursor 1 +#define Server_Cursor 2 +//conversions +#define TEXT_CONV 1 +#define RSC_FILE_CONV 2 +#define NUMBER_CONV 3 +//column flag values: +#define IS_NUMERIC 1 +#define IS_BINARY 2 +#define IS_NOT_NULL 4 +#define IS_PRIMARY_KEY 8 +#define IS_UNSIGNED 16 +//types +#define TINYINT 1 +#define SMALLINT 2 +#define MEDIUMINT 3 +#define INTEGER 4 +#define BIGINT 5 +#define DECIMAL 6 +#define FLOAT 7 +#define DOUBLE 8 +#define DATE 9 +#define DATETIME 10 +#define TIMESTAMP 11 +#define TIME 12 +#define STRING 13 +#define BLOB 14 +// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew + + +// Deprecated! See global.dm for new configuration vars +/* +var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) +var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) +*/ + +/DBConnection + var/_db_con // This variable contains a reference to the actual database connection. + var/dbi // This variable is a string containing the DBI MySQL requires. + var/user // This variable contains the username data. + var/password // This variable contains the password data. + var/default_cursor // This contains the default database cursor data. + // + var/server = "" + var/port = 3306 + +/DBConnection/New(dbi_handler,username,password_handler,cursor_handler) + src.dbi = dbi_handler + src.user = username + src.password = password_handler + src.default_cursor = cursor_handler + _db_con = _dm_db_new_con() + +/DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) + if(!config.sql_enabled) + return 0 + if(!src) return 0 + cursor_handler = src.default_cursor + if(!cursor_handler) cursor_handler = Default_Cursor + return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) + +/DBConnection/proc/Disconnect() return _dm_db_close(_db_con) + +/DBConnection/proc/IsConnected() + if(!config.sql_enabled) return 0 + var/success = _dm_db_is_connected(_db_con) + return success + +/DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) + +/DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) +/DBConnection/proc/SelectDB(database_name,dbi) + if(IsConnected()) Disconnect() + //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) + return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) +/DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) + + +/DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) + if(sql_query) src.sql = sql_query + if(connection_handler) src.db_connection = connection_handler + if(cursor_handler) src.default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() + + +/DBQuery + var/sql // The sql query being executed. + var/default_cursor + var/list/columns //list of DB Columns populated by Columns() + var/list/conversions + var/list/item[0] //list of data values populated by NextRow() + + var/DBConnection/db_connection + var/_db_query + +/DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler + +/DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) + Close() + return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) + +/DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) + +/DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) + +/DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) + +/DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) + +/DBQuery/proc/Columns() + if(!columns) + columns = _dm_db_columns(_db_query,/DBColumn) + return columns + +/DBQuery/proc/GetRowData() + var/list/columns = Columns() + var/list/results + if(columns.len) + results = list() + for(var/C in columns) + results+=C + var/DBColumn/cur_col = columns[C] + results[C] = src.item[(cur_col.position+1)] + return results + +/DBQuery/proc/Close() + item.len = 0 + columns = null + conversions = null + return _dm_db_close(_db_query) + +/DBQuery/proc/Quote(str) + return db_connection.Quote(str) + +/DBQuery/proc/SetConversion(column,conversion) + if(istext(column)) column = columns.Find(column) + if(!conversions) conversions = new/list(column) + else if(conversions.len < column) conversions.len = column + conversions[column] = conversion + + +/DBColumn + var/name + var/table + var/position //1-based index into item data + var/sql_type + var/flags + var/length + var/max_length + +/DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) + src.name = name_handler + src.table = table_handler + src.position = position_handler + src.sql_type = type_handler + src.flags = flag_handler + src.length = length_handler + src.max_length = max_length_handler + return ..() + + +/DBColumn/proc/SqlTypeName(type_handler=src.sql_type) + switch(type_handler) + if(TINYINT) return "TINYINT" + if(SMALLINT) return "SMALLINT" + if(MEDIUMINT) return "MEDIUMINT" + if(INTEGER) return "INTEGER" + if(BIGINT) return "BIGINT" + if(FLOAT) return "FLOAT" + if(DOUBLE) return "DOUBLE" + if(DATE) return "DATE" + if(DATETIME) return "DATETIME" + if(TIMESTAMP) return "TIMESTAMP" + if(TIME) return "TIME" + if(STRING) return "STRING" + if(BLOB) return "BLOB" + + +#undef Default_Cursor +#undef Client_Cursor +#undef Server_Cursor +#undef TEXT_CONV +#undef RSC_FILE_CONV +#undef NUMBER_CONV +#undef IS_NUMERIC +#undef IS_BINARY +#undef IS_NOT_NULL +#undef IS_PRIMARY_KEY +#undef IS_UNSIGNED +#undef TINYINT +#undef SMALLINT +#undef MEDIUMINT +#undef INTEGER +#undef BIGINT +#undef DECIMAL +#undef FLOAT +#undef DOUBLE +#undef DATE +#undef DATETIME +#undef TIMESTAMP +#undef TIME +#undef STRING +#undef BLOB diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 23c2025b847..6ce55534ded 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -1,145 +1,145 @@ -/proc/sql_poll_population() - if(!sqllogging) - return - var/admincount = GLOB.admins.len - var/playercount = 0 - for(var/mob/M in player_list) - if(M.client) - playercount += 1 - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during population polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO `tgstation`.`population` (`playercount`, `admincount`, `time`) VALUES ([playercount], [admincount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during population polling. Error : \[[err]\]\n") - -/proc/sql_report_round_start() - // TODO - if(!sqllogging) - return -/proc/sql_report_round_end() - // TODO - if(!sqllogging) - return - -/proc/sql_report_death(var/mob/living/carbon/human/H) - if(!sqllogging) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H) - var/podname = placeofdeath ? placeofdeath.name : "Unknown area" - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" - //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) - if(!sqllogging) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H) - var/podname = placeofdeath ? placeofdeath.name : "Unknown area" - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" - //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/statistic_cycle() - set waitfor = 0 - if(!sqllogging) - return - while(1) - sql_poll_population() - sleep(6000) - -//This proc is used for feedback. It is executed at round end. -/proc/sql_commit_feedback() - if(!blackbox) - log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") - return - - //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. - var/list/datum/feedback_variable/content = blackbox.get_round_feedback() - - if(!content) - log_game("Round ended without any feedback being generated. No feedback was sent to the database.") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during feedback reporting. Failed to connect.") - else - - var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback") - max_query.Execute() - - var/newroundid - - while(max_query.NextRow()) - newroundid = max_query.item[1] - - if(!(isnum(newroundid))) - newroundid = text2num(newroundid) - - if(isnum(newroundid)) - newroundid++ - else - newroundid = 1 - - for(var/datum/feedback_variable/item in content) - var/variable = item.get_variable() - var/value = item.get_value() - - var/DBQuery/query = dbcon.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") +/proc/sql_poll_population() + if(!sqllogging) + return + var/admincount = GLOB.admins.len + var/playercount = 0 + for(var/mob/M in player_list) + if(M.client) + playercount += 1 + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during population polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO `tgstation`.`population` (`playercount`, `admincount`, `time`) VALUES ([playercount], [admincount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during population polling. Error : \[[err]\]\n") + +/proc/sql_report_round_start() + // TODO + if(!sqllogging) + return +/proc/sql_report_round_end() + // TODO + if(!sqllogging) + return + +/proc/sql_report_death(var/mob/living/carbon/human/H) + if(!sqllogging) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H) + var/podname = placeofdeath ? placeofdeath.name : "Unknown area" + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" + //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) + if(!sqllogging) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H) + var/podname = placeofdeath ? placeofdeath.name : "Unknown area" + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" + //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = dbcon.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/statistic_cycle() + set waitfor = 0 + if(!sqllogging) + return + while(1) + sql_poll_population() + sleep(6000) + +//This proc is used for feedback. It is executed at round end. +/proc/sql_commit_feedback() + if(!blackbox) + log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") + return + + //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. + var/list/datum/feedback_variable/content = blackbox.get_round_feedback() + + if(!content) + log_game("Round ended without any feedback being generated. No feedback was sent to the database.") + return + + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during feedback reporting. Failed to connect.") + else + + var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM erro_feedback") + max_query.Execute() + + var/newroundid + + while(max_query.NextRow()) + newroundid = max_query.item[1] + + if(!(isnum(newroundid))) + newroundid = text2num(newroundid) + + if(isnum(newroundid)) + newroundid++ + else + newroundid = 1 + + for(var/datum/feedback_variable/item in content) + var/variable = item.get_variable() + var/value = item.get_value() + + var/DBQuery/query = dbcon.NewQuery("INSERT INTO erro_feedback (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") diff --git a/code/game/antagonist/antagonist_print.dm b/code/game/antagonist/antagonist_print.dm index 16fa6176dfd..830d4806f57 100644 --- a/code/game/antagonist/antagonist_print.dm +++ b/code/game/antagonist/antagonist_print.dm @@ -16,17 +16,17 @@ for(var/datum/objective/O in P.objectives) text += print_objective(O, num) if(O.check_completion()) - text += "Success!" + text += span_green("Success!") feedback_add_details(feedback_tag,"[O.type]|SUCCESS") else - text += "Fail." + text += span_red("Fail.") feedback_add_details(feedback_tag,"[O.type]|FAIL") failed = TRUE num++ if(failed) - text += "
    The [role_text] has failed." + text += "
    " + span_red("The [role_text] has failed.") else - text += "
    The [role_text] was successful!" + text += "
    " + span_green("The [role_text] was successful!") if(global_objectives && global_objectives.len) text += "
    Their objectives were:" @@ -42,9 +42,9 @@ var/text = "
    Objective [num]: [O.explanation_text] " if(append_success) if(O.check_completion()) - text += "Success!" + text += span_green("Success!") else - text += "Fail." + text += span_red("Fail.") return text /datum/antagonist/proc/print_player_lite(var/datum/mind/ply) diff --git a/code/game/antagonist/station/highlander.dm b/code/game/antagonist/station/highlander.dm index 9b54383278b..2e4885fd769 100644 --- a/code/game/antagonist/station/highlander.dm +++ b/code/game/antagonist/station/highlander.dm @@ -12,6 +12,8 @@ var/datum/antagonist/highlander/highlanders initial_spawn_req = 3 initial_spawn_target = 5 + id_type = /obj/item/weapon/card/id/centcom/ERT + /datum/antagonist/highlander/New() ..() highlanders = src @@ -32,26 +34,28 @@ var/datum/antagonist/highlander/highlanders if(!..()) return - for (var/obj/item/I in player) - if (istype(I, /obj/item/weapon/implant)) - continue - qdel(I) - + // drop original items! It used to be a loop that just Qdeled everything including your organs! + // Dropping because of non-oxy breathers... That would suck wouldn't it? + player.drop_from_inventory(player.get_equipped_item(slot_wear_id)) + player.drop_from_inventory(player.get_equipped_item(slot_wear_suit)) + player.drop_from_inventory(player.get_equipped_item(slot_w_uniform)) + player.drop_from_inventory(player.get_equipped_item(slot_l_ear)) + player.drop_from_inventory(player.get_equipped_item(slot_head)) + player.drop_from_inventory(player.get_equipped_item(slot_l_hand)) + player.drop_from_inventory(player.get_equipped_item(slot_shoes)) + player.drop_from_inventory(player.get_equipped_item(slot_l_store)) + // highlanders! player.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(player), slot_w_uniform) - player.equip_to_slot_or_del(new /obj/item/device/radio/headset/heads/captain(player), slot_l_ear) player.equip_to_slot_or_del(new /obj/item/clothing/head/beret(player), slot_head) player.equip_to_slot_or_del(new /obj/item/weapon/material/sword(player), slot_l_hand) player.equip_to_slot_or_del(new /obj/item/clothing/shoes/boots/combat(player), slot_shoes) player.equip_to_slot_or_del(new /obj/item/weapon/pinpointer(get_turf(player)), slot_l_store) - var/obj/item/weapon/card/id/W = new(player) - W.name = "[player.real_name]'s ID Card" - W.icon_state = "centcom" - W.access = get_all_station_access().Copy() - W.access |= get_all_centcom_access() - W.assignment = "Highlander" - W.registered_name = player.real_name - player.equip_to_slot_or_del(W, slot_wear_id) + var/obj/item/weapon/card/id/id = create_id("Highlander", player) + if(id) + id.access |= get_all_station_access() + id.icon_state = "centcom" + create_radio(DTH_FREQ, player) /proc/only_one() diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index b24169b8212..3a66c9ade61 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -1,2636 +1,2636 @@ -/* - -### This file contains a list of all the areas in your station. Format is as follows: - -/area/CATEGORY/OR/DESCRIPTOR/NAME (you can make as many subdivisions as you want) - name = "NICE NAME" (not required but makes things really nice) - icon = "ICON FILENAME" (defaults to areas.dmi) - icon_state = "NAME OF ICON" (defaults to "unknown" (blank)) - requires_power = 0 (defaults to 1) - music = "music/music.ogg" (defaults to "music/music.ogg") - -NOTE: there are two lists of areas in the end of this file: centcom and station itself. Please maintain these lists valid. --rastaf0 - -*/ - -/*-----------------------------------------------------------------------------*/ - -///////// -//SPACE// -///////// - -/area/space - name = "\improper Space" - icon_state = "space" - requires_power = 1 - always_unpowered = 1 - dynamic_lighting = 0 - has_gravity = 0 - power_light = 0 - power_equip = 0 - power_environ = 0 - base_turf = /turf/space - ambience = AMBIENCE_SPACE - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/space/atmosalert() - return - -/area/space/fire_alert() - return - -/area/space/fire_reset() - return - -/area/space/readyalert() - return - -/area/space/partyalert() - return - -/area/arrival - requires_power = 0 - -/area/arrival/start - name = "\improper Arrival Area" - icon_state = "start" - -/area/admin - name = "\improper Admin room" - icon_state = "start" - - - -//////////// -//SHUTTLES// -//////////// -//Shuttles only need starting area, movement is handled by landmarks -//All shuttles should now be under shuttle since we have smooth-wall code. - -/area/shuttle - requires_power = 0 - flags = RAD_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT - sound_env = SMALL_ENCLOSED - base_turf = /turf/space - forbid_events = TRUE - -/area/shuttle/arrival - name = "\improper Arrival Shuttle" - ambience = AMBIENCE_ARRIVALS - -/area/shuttle/supply - name = "\improper Supply Shuttle" - icon_state = "shuttle2" - -/area/shuttle/escape - name = "\improper Emergency Shuttle" - music = "music/escape.ogg" - -/area/shuttle/escape_pod1 - name = "\improper Escape Pod One" - music = "music/escape.ogg" - -/area/shuttle/escape_pod2 - name = "\improper Escape Pod Two" - music = "music/escape.ogg" - -/area/shuttle/escape_pod3 - name = "\improper Escape Pod Three" - music = "music/escape.ogg" - -/area/shuttle/escape_pod4 - name = "\improper Escape Pod Four" - music = "music/escape.ogg" - -/area/shuttle/escape_pod5 - name = "\improper Escape Pod Five" - music = "music/escape.ogg" - -/area/shuttle/escape_pod6 - name = "\improper Escape Pod Six" - music = "music/escape.ogg" - -/area/shuttle/large_escape_pod1 - name = "\improper Large Escape Pod One" - music = "music/escape.ogg" - -/area/shuttle/large_escape_pod2 - name = "\improper Large Escape Pod Two" - music = "music/escape.ogg" - -/area/shuttle/cryo - name = "\improper Cryogenic Storage" - -/area/shuttle/mining - name = "\improper Mining Elevator" - music = "music/escape.ogg" - dynamic_lighting = 0 - base_turf = /turf/simulated/mineral/floor/ignore_mapgen - -/area/shuttle/transport1/centcom - icon_state = "shuttle" - name = "\improper Transport Shuttle CentCom" - -/area/shuttle/transport1/station - icon_state = "shuttle" - name = "\improper Transport Shuttle" - -/area/shuttle/alien/base - icon_state = "shuttle" - name = "\improper Alien Shuttle Base" - requires_power = 1 - -/area/shuttle/alien/mine - icon_state = "shuttle" - name = "\improper Alien Shuttle Mine" - requires_power = 1 - -/area/shuttle/prison/ - name = "\improper Prison Shuttle" - -/area/shuttle/prison/station - icon_state = "shuttle" - -/area/shuttle/prison/prison - icon_state = "shuttle2" - -/area/shuttle/specops/centcom - name = "\improper Special Ops Shuttle" - icon_state = "shuttlered" - -/area/shuttle/specops/station - name = "\improper Special Ops Shuttle" - icon_state = "shuttlered2" - -/area/shuttle/syndicate_elite/mothership - name = "\improper Merc Elite Shuttle" - icon_state = "shuttlered" - -/area/shuttle/syndicate_elite/station - name = "\improper Merc Elite Shuttle" - icon_state = "shuttlered2" - -/area/shuttle/administration/centcom - name = "Centcom Large Bay (AS)" //VOREStation Edit - icon_state = "shuttlered" - -/area/shuttle/administration/station - name = "NSB Adephagia (AS)" //VOREStation Edit - icon_state = "shuttlered2" - -/area/shuttle/trade - name = "\improper Trade Station" - icon_state = "red" - dynamic_lighting = 0 - -/area/shuttle/thunderdome - name = "honk" - -/area/shuttle/research - name = "\improper Research Elevator" - music = "music/escape.ogg" - dynamic_lighting = 0 - base_turf = /turf/simulated/mineral/floor/ignore_mapgen - -/area/airtunnel1/ // referenced in airtunnel.dm:759 - -/area/dummy/ // Referenced in engine.dm:261 - -// === end remove - -/area/alien - name = "\improper Alien base" - icon_state = "yellow" - requires_power = 0 - flags = AREA_FLAG_IS_NOT_PERSISTENT - -// CENTCOM - -/area/centcom - name = "\improper CentCom" - icon_state = "centcom" - requires_power = 0 - dynamic_lighting = 0 - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/centcom/control - name = "\improper CentCom Control" - -/area/centcom/evac - name = "\improper CentCom Emergency Shuttle" - -/area/centcom/suppy - name = "\improper CentCom Supply Shuttle" - -/area/centcom/ferry - name = "\improper CentCom Transport Shuttle" - -/area/centcom/shuttle - name = "\improper CentCom Administration Shuttle" - -/area/centcom/test - name = "\improper CentCom Testing Facility" - -/area/centcom/living - name = "\improper CentCom Living Quarters" - -/area/centcom/specops - name = "\improper CentCom Special Ops" - -/area/centcom/creed - name = "Creed's Office" - -/area/centcom/holding - name = "\improper Holding Facility" - -/area/centcom/terminal - name = "\improper Docking Terminal" - icon_state = "centcom_dock" - ambience = AMBIENCE_ARRIVALS - -/area/centcom/tram - name = "\improper Tram Station" - ambience = AMBIENCE_ARRIVALS - -/area/centcom/security - name = "\improper CentCom Security" - icon_state = "centcom_security" - -/area/centcom/medical - name = "\improper CentCom Medical" - icon_state = "centcom_medical" - -/area/centcom/command - name = "\improper CentCom Command" //Central Command Command totally isn't RAS Syndrome in action. - icon_state = "centcom_command" - ambience = AMBIENCE_HIGHSEC - -/area/centcom/main_hall - name = "\improper Main Hallway" - icon_state = "centcom_hallway1" - -/area/centcom/bar - name = "\improper CentCom Bar" - icon_state = "centcom_crew" - -/area/centcom/restaurant - name = "\improper CentCom Restaurant" - icon_state = "centcom_crew" - -/area/centcom/bathroom - name = "\improper CentCom Bathroom" - icon_state = "centcom_crew" - sound_env = SMALL_ENCLOSED - -//SYNDICATES - -/area/syndicate_mothership - name = "\improper Mercenary Base" - icon_state = "syndie-ship" - requires_power = 0 - dynamic_lighting = 0 - ambience = AMBIENCE_HIGHSEC - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/syndicate_mothership/control - name = "\improper Mercenary Control Room" - icon_state = "syndie-control" - -/area/syndicate_mothership/elite_squad - name = "\improper Elite Mercenary Squad" - icon_state = "syndie-elite" - -//EXTRA - -/area/asteroid // -- TLE - name = "\improper Moon" - icon_state = "asteroid" - requires_power = 0 - sound_env = ASTEROID - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/asteroid/cave // -- TLE - name = "\improper Moon - Underground" - icon_state = "cave" - requires_power = 0 - sound_env = ASTEROID - -/area/asteroid/artifactroom - name = "\improper Moon - Artifact" - icon_state = "cave" - sound_env = SMALL_ENCLOSED - -/area/planet/clown - name = "\improper Clown Planet" - icon_state = "honk" - requires_power = 0 - -/area/tdome - name = "\improper Thunderdome" - icon_state = "thunder" - requires_power = 0 - dynamic_lighting = 0 - sound_env = SOUND_ENVIRONMENT_ARENA - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/tdome/tdome1 - name = "\improper Thunderdome (Team 1)" - icon_state = "green" - -/area/tdome/tdome2 - name = "\improper Thunderdome (Team 2)" - icon_state = "yellow" - -/area/tdome/tdomeadmin - name = "\improper Thunderdome (Admin.)" - icon_state = "purple" - -/area/tdome/tdomeobserve - name = "\improper Thunderdome (Observer.)" - icon_state = "purple" - -/area/virtual_reality - name = "Virtual Reality" - icon_state = "Virtual_Reality" - dynamic_lighting = 0 - requires_power = 0 - flags = AREA_FLAG_IS_NOT_PERSISTENT - -//ENEMY - -//names are used -/area/syndicate_station - name = "\improper Independent Station" - icon_state = "yellow" - requires_power = 0 - flags = RAD_SHIELDED - base_turf = /turf/space - ambience = AMBIENCE_HIGHSEC - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/syndicate_station/start - name = "\improper Mercenary Forward Operating Base" - icon_state = "yellow" - -/area/syndicate_station/southwest - name = "\improper south-west of SS13" - icon_state = "southwest" - -/area/syndicate_station/northwest - name = "\improper north-west of SS13" - icon_state = "northwest" - -/area/syndicate_station/northeast - name = "\improper north-east of SS13" - icon_state = "northeast" - -/area/syndicate_station/southeast - name = "\improper south-east of SS13" - icon_state = "southeast" - -/area/syndicate_station/north - name = "\improper north of SS13" - icon_state = "north" - -/area/syndicate_station/south - name = "\improper south of SS13" - icon_state = "south" - -/area/syndicate_station/commssat - name = "\improper south of the communication satellite" - icon_state = "south" - -/area/syndicate_station/mining - name = "\improper northeast of the mining station" - icon_state = "north" - -/area/syndicate_station/arrivals_dock - name = "\improper docked with station" - icon_state = "shuttle" - -/area/syndicate_station/maint_dock - name = "\improper docked with station" - icon_state = "shuttle" - -/area/syndicate_station/transit - name = "\improper hyperspace" - icon_state = "shuttle" - -/area/wizard_station - name = "\improper Wizard's Den" - icon_state = "yellow" - requires_power = 0 - dynamic_lighting = 0 - ambience = AMBIENCE_OTHERWORLDLY - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/skipjack_station - name = "\improper Skipjack" - icon_state = "yellow" - requires_power = 0 - base_turf = /turf/space - ambience = AMBIENCE_HIGHSEC - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/skipjack_station/start - name = "\improper Skipjack" - icon_state = "yellow" - -/area/skipjack_station/transit - name = "\improper hyperspace" - icon_state = "shuttle" - -/area/skipjack_station/southwest_solars - name = "\improper aft port solars" - icon_state = "southwest" - -/area/skipjack_station/northwest_solars - name = "\improper fore port solars" - icon_state = "northwest" - -/area/skipjack_station/northeast_solars - name = "\improper fore starboard solars" - icon_state = "northeast" - -/area/skipjack_station/southeast_solars - name = "\improper aft starboard solars" - icon_state = "southeast" - -/area/skipjack_station/mining - name = "\improper south of mining station" - icon_state = "north" - -//PRISON -/area/prison - name = "\improper Prison Station" - icon_state = "brig" - ambience = AMBIENCE_HIGHSEC - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/prison/arrival_airlock - name = "\improper Prison Station Airlock" - icon_state = "green" - requires_power = 0 - -/area/prison/control - name = "\improper Prison Security Checkpoint" - icon_state = "security" - -/area/prison/crew_quarters - name = "\improper Prison Security Quarters" - icon_state = "security" - -/area/prison/rec_room - name = "\improper Prison Rec Room" - icon_state = "green" - -/area/prison/closet - name = "\improper Prison Supply Closet" - icon_state = "dk_yellow" - -/area/prison/hallway/fore - name = "\improper Prison Fore Hallway" - icon_state = "yellow" - -/area/prison/hallway/aft - name = "\improper Prison Aft Hallway" - icon_state = "yellow" - -/area/prison/hallway/port - name = "\improper Prison Port Hallway" - icon_state = "yellow" - -/area/prison/hallway/starboard - name = "\improper Prison Starboard Hallway" - icon_state = "yellow" - -/area/prison/morgue - name = "\improper Prison Morgue" - icon_state = "morgue" - -/area/prison/medical_research - name = "\improper Prison Genetic Research" - icon_state = "medresearch" - -/area/prison/medical - name = "\improper Prison Medbay" - icon_state = "medbay" - -/area/prison/solar - name = "\improper Prison Solar Array" - icon_state = "storage" - requires_power = 0 - -/area/prison/podbay - name = "\improper Prison Podbay" - icon_state = "dk_yellow" - -/area/prison/solar_control - name = "\improper Prison Solar Array Control" - icon_state = "dk_yellow" - -/area/prison/solitary - name = "Solitary Confinement" - icon_state = "brig" - -/area/prison/cell_block/A - name = "Prison Cell Block A" - icon_state = "brig" - -/area/prison/cell_block/B - name = "Prison Cell Block B" - icon_state = "brig" - -/area/prison/cell_block/C - name = "Prison Cell Block C" - icon_state = "brig" - -//////////////////// -//SPACE STATION 13// -//////////////////// - -/area - ambience = AMBIENCE_GENERIC - -//Maintenance - -/area/maintenance - flags = RAD_SHIELDED - sound_env = TUNNEL_ENCLOSED - turf_initializer = new /datum/turf_initializer/maintenance() - ambience = AMBIENCE_MAINTENANCE - -/area/maintenance/aft - name = "Aft Maintenance" - icon_state = "amaint" - -/area/maintenance/fore - name = "Fore Maintenance" - icon_state = "fmaint" - -/area/maintenance/starboard - name = "Starboard Maintenance" - icon_state = "smaint" - -/area/maintenance/port - name = "Port Maintenance" - icon_state = "pmaint" - -/area/maintenance/atmos_control - name = "Atmospherics Maintenance" - icon_state = "fpmaint" - -/area/maintenance/fpmaint - name = "Fore Port Maintenance - 1" - icon_state = "fpmaint" - -/area/maintenance/fpmaint2 - name = "Fore Port Maintenance - 2" - icon_state = "fpmaint" - -/area/maintenance/fsmaint - name = "Fore Starboard Maintenance - 1" - icon_state = "fsmaint" - -/area/maintenance/fsmaint2 - name = "Fore Starboard Maintenance - 2" - icon_state = "fsmaint" - -/area/maintenance/asmaint - name = "Aft Starboard Maintenance" - icon_state = "asmaint" - -/area/maintenance/engi_shuttle - name = "Engineering Shuttle Access" - icon_state = "maint_e_shuttle" - -/area/maintenance/engi_engine - name = "Engine Maintenance" - icon_state = "maint_engine" - -/area/maintenance/asmaint2 - name = "Science Maintenance" - icon_state = "asmaint" - -/area/maintenance/apmaint - name = "Cargo Engineering Maintenance" - icon_state = "apmaint" - -/area/maintenance/maintcentral - name = "Bridge Maintenance" - icon_state = "maintcentral" - -/area/maintenance/arrivals - name = "Arrivals Maintenance" - icon_state = "maint_arrivals" - -/area/maintenance/bar - name = "Bar Maintenance" - icon_state = "maint_bar" - -/area/maintenance/central - name = "Central Maintenance" - icon_state = "maint_central" - -/area/maintenance/cafe_dock - name = "Cafeteria Dock Maintenance" - icon_state = "maint_cafe_dock" - -/area/maintenance/cargo - name = "Cargo Maintenance" - icon_state = "maint_cargo" - -/area/maintenance/cargo_research - name = "Cargo Research Maintenance" - icon_state = "maint_cargo_research" - -/area/maintenance/chapel - name = "Chapel Maintenance" - icon_state = "maint_chapel" - -/area/maintenance/disposal - name = "Waste Disposal" - icon_state = "disposal" - flags = AREA_FLAG_IS_NOT_PERSISTENT //If trash items got this far, they can be safely deleted. - -/area/maintenance/engineering - name = "Engineering Maintenance" - icon_state = "maint_engineering" - -/area/maintenance/engineering/pumpstation - name = "Engineering Pump Station" - icon_state = "maint_pumpstation" - -/area/maintenance/evahallway - name = "\improper EVA Maintenance" - icon_state = "maint_eva" - -/area/maintenance/dormitory - name = "Dormitory Maintenance" - icon_state = "maint_dormitory" - -/area/maintenance/holodeck - name = "Holodeck Maintenance" - icon_state = "maint_holodeck" - -/area/maintenance/incinerator - name = "\improper Incinerator" - icon_state = "disposal" - -/area/maintenance/library - name = "Library Maintenance" - icon_state = "maint_library" - -/area/maintenance/locker - name = "Locker Room Maintenance" - icon_state = "maint_locker" - -/area/maintenance/medbay - name = "Medbay Maintenance" - icon_state = "maint_medbay" - -/area/maintenance/medbay_aft - name = "Medbay Maintenance - Aft" - icon_state = "maint_medbay_aft" - -/area/maintenance/medbay_fore - name = "Medbay Maintenance - Fore" - icon_state = "maint_medbay_fore" - -/area/maintenance/pool - name = "Pool Maintenance" - icon_state = "maint_pool" - -/area/maintenance/research - name = "Research Maintenance" - icon_state = "maint_research" - -/area/maintenance/research_port - name = "Research Maintenance - Port" - icon_state = "maint_research_port" - -/area/maintenance/research_starboard - name = "Research Maintenance - Starboard" - icon_state = "maint_research_starboard" - -/area/maintenance/research_starboard - name = "Research Maintenance - Starboard" - icon_state = "maint_research_cargo" - -/area/maintenance/research_shuttle - name = "Research Shuttle Dock Maintenance" - icon_state = "maint_research_shuttle" - -/area/maintenance/security_port - name = "Security Maintenance - Port" - icon_state = "maint_security_port" - -/area/maintenance/security_starboard - name = "Security Maintenance - Starboard" - icon_state = "maint_security_starboard" - -/area/maintenance/storage - name = "Atmospherics" - icon_state = "green" - -/area/maintenance/tool_storage - name = "Tool Storage Maintenance" - icon_state = "maint_tool_storage" - - -// SUBSTATIONS (Subtype of maint, that should let them serve as shielded area during radstorm) - -/area/maintenance/substation - name = "Substation" - icon_state = "substation" - sound_env = SMALL_ENCLOSED - ambience = AMBIENCE_SUBSTATION - -/area/maintenance/substation/engineering // Probably will be connected to engineering SMES room, as wires cannot be crossed properly without them sharing powernets. - name = "Engineering Substation" - -// No longer used: -/area/maintenance/substation/medical_science // Medbay and Science. Each has it's own separated machinery, but it originates from the same room. - name = "Medical Research Substation" - -/area/maintenance/substation/medical // Medbay - name = "Medical Substation" - -/area/maintenance/substation/research // Research - name = "Research Substation" - -/area/maintenance/substation/cafeteria_dock // Hydro, kitchen, docks, hotel - name = "Cafeteria Dock Substation" - -/area/maintenance/substation/civilian // Dorms, Lockerroom, Pool - name = "Civilian Substation" - -/area/maintenance/substation/civilian_east // Bar, kitchen, dorms, ... - name = "Civilian East Substation" - -/area/maintenance/substation/civilian_west // Cargo, PTS, locker room, probably arrivals, ...) - name = "Civilian West Substation" - -/area/maintenance/substation/cargo // Cargo - name = "Cargo Substation" - -/area/maintenance/substation/command // AI and central cluster. This one will be between HoP office and meeting room (probably). - name = "Command Substation" - -/area/maintenance/substation/dock // Bar, docks, hotel - name = "Dock Substation" - -/area/maintenance/substation/security // Security, Brig, Permabrig, etc. - name = "Security Substation" - -//Hallway - -/area/hallway/primary/ - sound_env = LARGE_ENCLOSED - ambience = AMBIENCE_GENERIC - -/area/hallway/primary/fore - name = "\improper Fore Primary Hallway" - icon_state = "hallF" - -/area/hallway/primary/starboard - name = "\improper Starboard Primary Hallway" - icon_state = "hallS" - -/area/hallway/primary/aft - name = "\improper Aft Primary Hallway" - icon_state = "hallA" - -/area/hallway/primary/port - name = "\improper Port Primary Hallway" - icon_state = "hallP" - -/area/hallway/primary/central_one - name = "\improper Central Primary Hallway - Fore" - icon_state = "hallC1" - -/area/hallway/primary/central_two - name = "\improper Central Primary Hallway - Starboard" - icon_state = "hallC2" - -/area/hallway/primary/central_three - name = "\improper Central Primary Hallway - Aft" - icon_state = "hallC3" - -/area/hallway/primary/central_four - name = "\improper Central Primary Hallway - Port" - icon_state = "hallC4" - -/area/hallway/secondary/exit - name = "\improper Escape Shuttle Hallway" - icon_state = "escape" - -/area/hallway/secondary/construction - name = "\improper Construction Area" - icon_state = "construction" - -/area/hallway/secondary/entry - forbid_events = TRUE - -/area/hallway/secondary/entry/fore - name = "\improper Shuttle Dock Hallway - Mid" - icon_state = "entry_1" - -/area/hallway/secondary/entry/port - name = "\improper Shuttle Dock Hallway - Port" - icon_state = "entry_2" - -/area/hallway/secondary/entry/starboard - name = "\improper Shuttle Dock Hallway - Starboard" - icon_state = "entry_3" - -/area/hallway/secondary/entry/aft - name = "\improper Shuttle Dock Hallway - Aft" - icon_state = "entry_4" - -/area/hallway/secondary/entry/D1 - name = "\improper Shuttle Dock Hallway - Dock One" - icon_state = "entry_D1" - base_turf = /turf/space - -/area/hallway/secondary/entry/D2 - name = "\improper Shuttle Dock Hallway - Dock Two" - icon_state = "entry_D2" - base_turf = /turf/space - -/area/hallway/secondary/entry/D2/arrivals - name = "\improper Shuttle Dock Hallway - Dock Two" - icon_state = "entry_D2" - base_turf = /turf/space - requires_power = 0 - -/area/hallway/secondary/entry/D3 - name = "\improper Shuttle Dock Hallway - Dock Three" - icon_state = "entry_D3" - base_turf = /turf/space - -/area/hallway/secondary/entry/D4 - name = "\improper Shuttle Dock Hallway - Dock Four" - icon_state = "entry_D4" - -/area/hallway/secondary/entry/docking_lounge - name = "\improper Docking Lounge" - icon_state = "docking_lounge" - -/area/hallway/secondary/escape/dock_escape_pod_hallway_port - name = "\improper Dock Escape Pod Hallway Port" - icon_state = "dock_escape_pod_hallway_port" - -/area/hallway/secondary/escape/dock_escape_pod_hallway_starboard - name = "\improper Dock Escape Pod Hallway Starboard" - icon_state = "dock_escape_pod_hallway_starboard" - -/area/hallway/secondary/escape/fore_port_escape_pod_hallway - name = "\improper Fore Port Escape Pod Hallway" - icon_state = "fore_port_escape_pod_hallway" - -/area/hallway/secondary/escape/fore_escape_pod_hallway - name = "\improper Fore Escape Pod Hallway" - icon_state = "fore_escape_pod_hallway" - -/area/hallway/secondary/escape/medical_escape_pod_hallway - name = "\improper Medical Escape Pod Hallway" - icon_state = "medical_escape_pod_hallway" - -/area/hallway/secondary/cargo_hallway - name = "\improper Cargo Hallway" - icon_state = "cargo_hallway" - -/area/hallway/secondary/civilian_hallway_aft - name = "\improper Civilian Hallway Aft" - icon_state = "aft_civilian_hallway" - -/area/hallway/secondary/civilian_hallway_fore - name = "\improper Civilian Hallway Fore" - icon_state = "fore_civilian_hallway" - -/area/hallway/secondary/civilian_hallway_mid - name = "\improper Civilian Hallway Mid" - icon_state = "mid_civilian_hallway" - -/area/hallway/secondary/chapel_hallway - name = "\improper Chapel Hallway" - icon_state = "chapel_hallway" - -/area/hallway/secondary/cryostorage_hallway - name = "\improper Cryostorage Hallway" - icon_state = "cryostorage_hallway" - -/area/hallway/secondary/docking_hallway - name = "\improper Docking Hallway" - icon_state = "docking_hallway" - -/area/hallway/secondary/docking_hallway2 - name = "\improper Secondary Docking Hallway" - icon_state = "docking_hallway" - -/area/hallway/secondary/engineering_hallway - name = "\improper Engineering Primary Hallway" - icon_state = "engineering_primary_hallway" - -/area/hallway/secondary/eva_hallway - name = "\improper EVA Hallway" - icon_state = "eva_hallway" - -/area/hallway/secondary/medical_emergency_hallway - name = "\improper Medical Emergency Hallway" - icon_state = "medical_emergency_hallway" - -//Command - -/area/bridge - name = "\improper Bridge" - icon_state = "bridge" - music = "signal" - -/area/bridge_hallway - name = "\improper Bridge Hallway" - icon_state = "bridge" - -/area/bridge/meeting_room - name = "\improper Heads of Staff Meeting Room" - icon_state = "bridge" - music = null - sound_env = MEDIUM_SOFTFLOOR - -/area/crew_quarters/captain - name = "\improper Command - Site Manager's Office" - icon_state = "captain" - sound_env = MEDIUM_SOFTFLOOR - -/area/crew_quarters/heads/hop - name = "\improper Command - HoP's Office" - icon_state = "head_quarters" - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/crew_quarters/heads/hor - name = "\improper Research - RD's Office" - icon_state = "head_quarters" - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/crew_quarters/heads/chief - name = "\improper Engineering - CE's Office" - icon_state = "head_quarters" - -/area/crew_quarters/heads/hos - name = "\improper Security - HoS' Office" - icon_state = "head_quarters" - -/area/crew_quarters/heads/cmo - name = "\improper Medbay - CMO's Office" - icon_state = "head_quarters" - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/crew_quarters/courtroom - name = "\improper Courtroom" - icon_state = "courtroom" - -/area/mint - name = "\improper Mint" - icon_state = "green" - -/area/comms - name = "\improper Communications Relay" - icon_state = "tcomsatcham" - -/area/server - name = "\improper Research Server Room" - icon_state = "server" - -//Civilian - -/area/crew_quarters - name = "\improper Dormitories" - icon_state = "Sleep" - flags = RAD_SHIELDED - ambience = AMBIENCE_GENERIC - forbid_events = TRUE - forbid_singulo = TRUE - -/area/crew_quarters/toilet - name = "\improper Dormitory Toilets" - icon_state = "toilet" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/sleep - name = "\improper Dormitories" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A1 - name = "\improper Apartment A1" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A2 - name = "\improper Apartment A2" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A3 - name = "\improper Apartment A3" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A4 - name = "\improper Apartment A4" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A5 - name = "\improper Apartment A5" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A6 - name = "\improper Apartment A6" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A7 - name = "\improper Apartment A7`" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_A8 - name = "\improper Apartment A8" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_B1 - name = "\improper Apartment B1" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_B2 - name = "\improper Apartment B2" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Apartment_B3 - name = "\improper Apartment B3" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_1 - name = "\improper Dormitory Room 1" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_2 - name = "\improper Dormitory Room 2" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_3 - name = "\improper Dormitory Room 3" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_4 - name = "\improper Dormitory Room 4" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_5 - name = "\improper Dormitory Room 5" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_6 - name = "\improper Dormitory Room 6" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_7 - name = "\improper Dormitory Room 7" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_8 - name = "\improper Dormitory Room 8" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_9 - name = "\improper Dormitory Room 9" - icon_state = "Sleep" - -/area/crew_quarters/sleep/Dorm_10 - name = "\improper Dormitory Room 10" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_1 - name = "\improper Visitor Room 1" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_2 - name = "\improper Visitor Room 2" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_3 - name = "\improper Visitor Room 3" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_4 - name = "\improper Visitor Room 4" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_5 - name = "\improper Visitor Room 5" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_6 - name = "\improper Visitor Room 6" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_7 - name = "\improper Visitor Room 7" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_8 - name = "\improper Visitor Room 8" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_9 - name = "\improper Visitor Room 9" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_10 - name = "\improper Visitor Room 10" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_11 - name = "\improper Visitor Room 11" - icon_state = "Sleep" - -/area/crew_quarters/sleep/vistor_room_12 - name = "\improper Visitor Room 12" - icon_state = "Sleep" - -/area/crew_quarters/sleep/engi_wash - name = "\improper Engineering Washroom" - icon_state = "toilet" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/sleep/bedrooms - name = "\improper Dormitory Bedroom One" - icon_state = "Sleep" - sound_env = SMALL_SOFTFLOOR - -/area/crew_quarters/sleep/cryo - name = "\improper Cryogenic Storage" - icon_state = "Sleep" - -/area/crew_quarters/sleep/elevator - name = "\improper Main Elevator" - icon_state = "Sleep" - -/area/crew_quarters/sleep_male - name = "\improper Male Dorm" - icon_state = "Sleep" - -/area/crew_quarters/sleep_male/toilet_male - name = "\improper Male Toilets" - icon_state = "toilet" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/sleep_female - name = "\improper Female Dorm" - icon_state = "Sleep" - -/area/crew_quarters/sleep_female/toilet_female - name = "\improper Female Toilets" - icon_state = "toilet" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/locker - name = "\improper Locker Room" - icon_state = "locker" - -/area/crew_quarters/locker/locker_toilet - name = "\improper Locker Toilets" - icon_state = "toilet" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/fitness - name = "\improper Fitness Room" - icon_state = "fitness" - -/area/crew_quarters/longue_area - name = "\improper Lounge" //VOREStation Edit - Muh speeling. - icon_state = "recreation_area" - -/area/crew_quarters/recreation_area - name = "\improper Recreation Area" - icon_state = "recreation_area" - -/area/crew_quarters/recreation_area_hallway - name = "\improper Recreation Area Hallway" - icon_state = "recreation_area_hallway" - -/area/crew_quarters/recreation_area_restroom - name = "\improper Recreation Area Restroom" - icon_state = "recreation_area_restroom" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/pool - name = "\improper Pool" - icon_state = "pool" - -/area/crew_quarters/cafeteria - name = "\improper Cafeteria" - icon_state = "cafeteria" - -/area/crew_quarters/coffee_shop - name = "\improper Coffee Shop" - icon_state = "coffee_shop" - -/area/crew_quarters/kitchen - name = "\improper Kitchen" - icon_state = "kitchen" - -/area/crew_quarters/bar - name = "\improper Bar" - icon_state = "bar" - sound_env = LARGE_SOFTFLOOR - -/area/crew_quarters/barrestroom - name = "\improper Cafeteria Restroom" - icon_state = "bar" - sound_env = SMALL_ENCLOSED - -/area/crew_quarters/theatre - name = "\improper Theatre" - icon_state = "Theatre" - sound_env = LARGE_SOFTFLOOR - -/area/crew_quarters/visitor_lodging - name = "\improper Visitor Lodging" - icon_state = "visitor_lodging" - -/area/crew_quarters/visitor_dining - name = "\improper Visitor Dining" - icon_state = "visitor_dinning" - -/area/crew_quarters/visitor_laundry - name = "\improper Visitor Laundry" - icon_state = "visitor_laundry" - -/area/library - name = "\improper Library" - icon_state = "library" - sound_env = LARGE_SOFTFLOOR - lightswitch = 0 // VOREStation Edit - We like dark libraries - -/area/library_conference_room - name = "\improper Library Conference Room" - icon_state = "library_conference_room" - -/area/chapel - ambience = AMBIENCE_CHAPEL - -/area/chapel/main - name = "\improper Chapel" - icon_state = "chapel" - sound_env = LARGE_ENCLOSED - -/area/chapel/office - name = "\improper Chapel Office" - icon_state = "chapeloffice" - -/area/chapel/chapel_morgue - name = "\improper Chapel Morgue" - icon_state = "chapel_morgue" - -/area/lawoffice - name = "\improper Internal Affairs" - icon_state = "law" - -/area/holodeck_control - name = "\improper Holodeck Control" - icon_state = "holodeck_control" - -/area/vacant/vacant_shop - name = "\improper Vacant Shop" - icon_state = "vacant_shop" - -/area/vacant/vacant_site - name = "\improper Vacant Site" - icon_state = "vacant_site" - -/area/vacant/vacant_site2 - name = "\improper Abandoned Locker Room" - icon_state = "vacant_site" - -/area/holodeck - name = "\improper Holodeck" - icon_state = "Holodeck" - dynamic_lighting = 0 - sound_env = LARGE_ENCLOSED - forbid_events = TRUE - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/holodeck/alphadeck - name = "\improper Holodeck Alpha" - -/area/holodeck/source_plating - name = "\improper Holodeck - Off" - -/area/holodeck/source_emptycourt - name = "\improper Holodeck - Empty Court" - sound_env = SOUND_ENVIRONMENT_ARENA - -/area/holodeck/source_boxingcourt - name = "\improper Holodeck - Boxing Court" - sound_env = SOUND_ENVIRONMENT_ARENA - -/area/holodeck/source_basketball - name = "\improper Holodeck - Basketball Court" - sound_env = SOUND_ENVIRONMENT_ARENA - -/area/holodeck/source_thunderdomecourt - name = "\improper Holodeck - Thunderdome Court" - requires_power = 0 - sound_env = SOUND_ENVIRONMENT_ARENA - -/area/holodeck/source_courtroom - name = "\improper Holodeck - Courtroom" - sound_env = SOUND_ENVIRONMENT_AUDITORIUM - -/area/holodeck/source_beach - name = "\improper Holodeck - Beach" - sound_env = SOUND_ENVIRONMENT_PLAIN - -/area/holodeck/source_burntest - name = "\improper Holodeck - Atmospheric Burn Test" - -/area/holodeck/source_wildlife - name = "\improper Holodeck - Wildlife Simulation" - -/area/holodeck/source_meetinghall - name = "\improper Holodeck - Meeting Hall" - sound_env = SOUND_ENVIRONMENT_AUDITORIUM - -/area/holodeck/source_theatre - name = "\improper Holodeck - Theatre" - sound_env = SOUND_ENVIRONMENT_CONCERT_HALL - -/area/holodeck/source_picnicarea - name = "\improper Holodeck - Picnic Area" - sound_env = SOUND_ENVIRONMENT_PLAIN - -/area/holodeck/source_snowfield - name = "\improper Holodeck - Snow Field" - sound_env = SOUND_ENVIRONMENT_FOREST - -/area/holodeck/source_desert - name = "\improper Holodeck - Desert" - sound_env = SOUND_ENVIRONMENT_PLAIN - -/area/holodeck/source_space - name = "\improper Holodeck - Space" - has_gravity = 0 - sound_env = SPACE - -/area/holodeck/source_chess - name = "\improper Holodeck - Chessboard" - - -//Engineering - -/area/engineering/ - name = "\improper Engineering" - icon_state = "engineering" - ambience = AMBIENCE_ENGINEERING - -/area/engineering/atmos - name = "\improper Atmospherics" - icon_state = "atmos" - sound_env = LARGE_ENCLOSED - ambience = AMBIENCE_ATMOS - -/area/engineering/atmos/monitoring - name = "\improper Atmospherics Monitoring Room" - icon_state = "atmos_monitoring" - sound_env = STANDARD_STATION - -/area/engineering/atmos/storage - name = "\improper Atmospherics Storage" - icon_state = "atmos_storage" - sound_env = SMALL_ENCLOSED - -/area/engineering/drone_fabrication - name = "\improper Engineering Drone Fabrication" - icon_state = "drone_fab" - sound_env = SMALL_ENCLOSED - -/area/engineering/engine_smes - name = "\improper Engineering SMES" - icon_state = "engine_smes" - sound_env = SMALL_ENCLOSED - -/area/engineering/engine_room - name = "\improper Engine Room" - icon_state = "engine" - sound_env = LARGE_ENCLOSED - forbid_events = TRUE - -/area/engineering/engine_airlock - name = "\improper Engine Room Airlock" - icon_state = "engine" - -/area/engineering/engine_monitoring - name = "\improper Engine Monitoring Room" - icon_state = "engine_monitoring" - -/area/engineering/engine_waste - name = "\improper Engine Waste Handling" - icon_state = "engine_waste" - -/area/engineering/engineering_monitoring - name = "\improper Engineering Monitoring Room" - icon_state = "engine_monitoring" - -/area/engineering/foyer - name = "\improper Engineering Foyer" - icon_state = "engineering_foyer" - -/area/engineering/storage - name = "\improper Engineering Storage" - icon_state = "engineering_storage" - -/area/engineering/break_room - name = "\improper Engineering Break Room" - icon_state = "engineering_break" - sound_env = MEDIUM_SOFTFLOOR - -/area/engineering/engine_eva - name = "\improper Engine EVA" - icon_state = "engine_eva" - -/area/engineering/locker_room - name = "\improper Engineering Locker Room" - icon_state = "engineering_locker" - -/area/engineering/workshop - name = "\improper Engineering Workshop" - icon_state = "engineering_workshop" - -/area/engineering/aft_hallway - name = "\improper Engineering Aft Hallway" - icon_state = "engineering_aft_hallway" - - -//Solars - -/area/solar - requires_power = 1 - always_unpowered = 1 - dynamic_lighting = 0 - ambience = AMBIENCE_SPACE - -/area/solar/auxport - name = "\improper Fore Port Solar Array" - icon_state = "panelsA" - -/area/solar/auxstarboard - name = "\improper Fore Starboard Solar Array" - icon_state = "panelsA" - -/area/solar/fore - name = "\improper Fore Solar Array" - icon_state = "yellow" - -/area/solar/aft - name = "\improper Aft Solar Array" - icon_state = "aft" - -/area/solar/starboard - name = "\improper Aft Starboard Solar Array" - icon_state = "panelsS" - -/area/solar/port - name = "\improper Aft Port Solar Array" - icon_state = "panelsP" - -/area/maintenance/auxsolarport - name = "Solar Maintenance - Fore Port" - icon_state = "SolarcontrolP" - sound_env = SMALL_ENCLOSED - -/area/maintenance/starboardsolar - name = "Solar Maintenance - Aft Starboard" - icon_state = "SolarcontrolS" - sound_env = SMALL_ENCLOSED - -/area/maintenance/portsolar - name = "Solar Maintenance - Aft Port" - icon_state = "SolarcontrolP" - sound_env = SMALL_ENCLOSED - -/area/maintenance/auxsolarstarboard - name = "Solar Maintenance - Fore Starboard" - icon_state = "SolarcontrolS" - sound_env = SMALL_ENCLOSED - -/area/maintenance/foresolar - name = "Solar Maintenance - Fore" - icon_state = "SolarcontrolA" - sound_env = SMALL_ENCLOSED - -/area/assembly/chargebay - name = "\improper Mech Bay" - icon_state = "mechbay" - -/area/assembly/showroom - name = "\improper Robotics Showroom" - icon_state = "showroom" - -/area/assembly/robotics - name = "\improper Robotics Lab" - icon_state = "robotics" - -/area/assembly/assembly_line //Derelict Assembly Line - name = "\improper Assembly Line" - icon_state = "ass_line" - power_equip = 0 - power_light = 0 - power_environ = 0 - -//Teleporter - -/area/teleporter - name = "\improper Teleporter" - icon_state = "teleporter" - music = "signal" - -/area/gateway - name = "\improper Gateway" - icon_state = "teleporter" - music = "signal" - -/area/AIsattele - name = "\improper AI Satellite Teleporter Room" - icon_state = "teleporter" - music = "signal" - -//MedBay - -/area/medical/medbay - name = "\improper Medbay Hallway - Port" - icon_state = "medbay" - music = 'sound/ambience/signal.ogg' - -//Medbay is a large area, these additional areas help level out APC load. -/area/medical/medbay2 - name = "\improper Medbay Hallway - Starboard" - icon_state = "medbay2" - music = 'sound/ambience/signal.ogg' - -/area/medical/medbay3 - name = "\improper Medbay Hallway - Fore" - icon_state = "medbay3" - music = 'sound/ambience/signal.ogg' - -/area/medical/medbay4 - name = "\improper Medbay Hallway - Aft" - icon_state = "medbay4" - music = 'sound/ambience/signal.ogg' - -/area/medical/biostorage - name = "\improper Secondary Storage" - icon_state = "medbay2" - music = 'sound/ambience/signal.ogg' - -/area/medical/reception - name = "\improper Medbay Reception" - icon_state = "medbay" - music = 'sound/ambience/signal.ogg' - -/area/medical/medbay_emt_bay - name = "\improper Medical EMT Bay" - icon_state = "medbay_emt_bay" - music = 'sound/ambience/signal.ogg' - -/area/medical/medbay_primary_storage - name = "\improper Medbay Primary Storage" - icon_state = "medbay_primary_storage" - music = 'sound/ambience/signal.ogg' - -/area/medical/psych - name = "\improper Psych Room" - icon_state = "medbay3" - music = 'sound/ambience/signal.ogg' - -/area/crew_quarters/medbreak - name = "\improper Break Room" - icon_state = "medbay3" - music = 'sound/ambience/signal.ogg' - -/area/crew_quarters/medical_restroom - name = "\improper Medbay Restroom" - icon_state = "medbay_restroom" - sound_env = SMALL_ENCLOSED - -/area/medical/patients_rooms - name = "\improper Patient's Rooms" - icon_state = "patients" - -/area/medical/ward - name = "\improper Recovery Ward" - icon_state = "patients" - -/area/medical/patient_a - name = "\improper Patient A" - icon_state = "medbay_patient_room_a" - -/area/medical/patient_b - name = "\improper Patient B" - icon_state = "medbay_patient_room_b" - -/area/medical/patient_c - name = "\improper Patient C" - icon_state = "medbay_patient_room_c" - -/area/medical/patient_d - name = "\improper Patient D" - icon_state = "medbay_patient_room_d" - -/area/medical/patient_e - name = "\improper Patient E" - icon_state = "medbay_patient_room_e" - -/area/medical/patient_wing - name = "\improper Patient Wing" - icon_state = "patients" - -/area/medical/cmostore - name = "\improper Secure Storage" - icon_state = "CMO" - -/area/medical/robotics - name = "\improper Robotics" - icon_state = "medresearch" - -/area/medical/virology - name = "\improper Virology" - icon_state = "virology" - -/area/medical/virologyaccess - name = "\improper Virology Access" - icon_state = "virology" - -/area/medical/morgue - name = "\improper Morgue" - icon_state = "morgue" - -/area/medical/chemistry - name = "\improper Chemistry" - icon_state = "chem" - -/area/medical/surgery - name = "\improper Operating Theatre 1" - icon_state = "surgery" - flags = AREA_FLAG_IS_NOT_PERSISTENT //This WOULD become a filth pit - -/area/medical/surgery2 - name = "\improper Operating Theatre 2" - icon_state = "surgery" - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/medical/surgeryobs - name = "\improper Operation Observation Room" - icon_state = "surgery" - -/area/medical/surgeryprep - name = "\improper Pre-Op Prep Room" - icon_state = "surgery" - -/area/medical/surgery_hallway - name = "\improper Surgery Hallway" - icon_state = "surgery_hallway" - -/area/medical/surgery_storage - name = "\improper Surgery Storage" - icon_state = "surgery_storage" - -/area/medical/cryo - name = "\improper Cryogenics" - icon_state = "cryo" - -/area/medical/exam_room - name = "\improper Exam Room" - icon_state = "exam_room" - -/area/medical/genetics - name = "\improper Genetics Lab" - icon_state = "genetics" - -/area/medical/genetics_cloning - name = "\improper Cloning Lab" - icon_state = "cloning" - -/area/medical/sleeper - name = "\improper Emergency Treatment Centre" - icon_state = "exam_room" - flags = AREA_FLAG_IS_NOT_PERSISTENT //Trust me. - -/area/medical/first_aid_station_starboard - name = "\improper Starboard First-Aid Station" - icon_state = "medbay2" - -/area/medical/first_aid_station - name = "\improper Port First-Aid Station" - icon_state = "medbay2" - - - -//Security - -/area/security/main - name = "\improper Security Office" - icon_state = "security" - -/area/security/lobby - name = "\improper Security Lobby" - icon_state = "security" - -/area/security/brig - name = "\improper Security - Brig" - icon_state = "brig" - -/area/security/brig/prison_break() - for(var/obj/structure/closet/secure_closet/brig/temp_closet in src) - temp_closet.locked = 0 - temp_closet.icon_state = "closed_unlocked" - for(var/obj/machinery/door_timer/temp_timer in src) - temp_timer.timer_duration = 1 - ..() - -/area/security/prison - name = "\improper Security - Prison Wing" - icon_state = "sec_prison" - -/area/security/prison/prison_break() - for(var/obj/structure/closet/secure_closet/brig/temp_closet in src) - temp_closet.locked = 0 - temp_closet.icon_state = "closed_unlocked" - for(var/obj/machinery/door_timer/temp_timer in src) - temp_timer.timer_duration = 1 - ..() - -/area/security/warden - name = "\improper Security - Warden's Office" - icon_state = "Warden" - -/area/security/armoury - name = "\improper Security - Armory" - icon_state = "armory" - ambience = AMBIENCE_HIGHSEC - -/area/security/briefing_room - name = "\improper Security - Briefing Room" - icon_state = "brig" - -/area/security/evidence_storage - name = "\improper Security - Equipment Storage" - icon_state = "security_equipment_storage" - -/area/security/evidence_storage - name = "\improper Security - Evidence Storage" - icon_state = "evidence_storage" - -/area/security/interrogation - name = "\improper Security - Interrogation" - icon_state = "interrogation" - -/area/security/riot_control - name = "\improper Security - Riot Control" - icon_state = "riot_control" - -/area/security/detectives_office - name = "\improper Security - Forensic Office" - icon_state = "detective" - sound_env = MEDIUM_SOFTFLOOR - -/area/security/range - name = "\improper Security - Firing Range" - icon_state = "firingrange" - -/area/security/security_aid_station - name = "\improper Security - Security Aid Station" - icon_state = "security_aid_station" - -/area/security/security_bathroom - name = "\improper Security - Restroom" - icon_state = "security_bathroom" - sound_env = SMALL_ENCLOSED - -/area/security/security_cell_hallway - name = "\improper Security - Cell Hallway" - icon_state = "security_cell_hallway" - -/area/security/security_equiptment_storage - name = "\improper Security - Equipment Storage" - icon_state = "security_equip_storage" - -/area/security/security_lockerroom - name = "\improper Security - Locker Room" - icon_state = "security_lockerroom" - -/area/security/security_processing - name = "\improper Security - Security Processing" - icon_state = "security_processing" - -/area/security/tactical - name = "\improper Security - Tactical Equipment" - icon_state = "Tactical" - ambience = AMBIENCE_HIGHSEC - - -/* - New() - ..() - - spawn(10) //let objects set up first - for(var/turf/turfToGrayscale in src) - if(turfToGrayscale.icon) - var/icon/newIcon = icon(turfToGrayscale.icon) - newIcon.GrayScale() - turfToGrayscale.icon = newIcon - for(var/obj/objectToGrayscale in turfToGrayscale) //1 level deep, means tables, apcs, locker, etc, but not locker contents - if(objectToGrayscale.icon) - var/icon/newIcon = icon(objectToGrayscale.icon) - newIcon.GrayScale() - objectToGrayscale.icon = newIcon -*/ - -/area/security/nuke_storage - name = "\improper Vault" - icon_state = "nuke_storage" - ambience = AMBIENCE_HIGHSEC - -/area/security/checkpoint - name = "\improper Security Checkpoint" - icon_state = "checkpoint1" - -/area/security/checkpoint2 - name = "\improper Security - Arrival Checkpoint" - icon_state = "security" - ambience = AMBIENCE_ARRIVALS - -/area/security/checkpoint/supply - name = "Security Post - Cargo Bay" - icon_state = "checkpoint1" - -/area/security/checkpoint/engineering - name = "Security Post - Engineering" - icon_state = "checkpoint1" - -/area/security/checkpoint/medical - name = "Security Post - Medbay" - icon_state = "checkpoint1" - -/area/security/checkpoint/science - name = "Security Post - Science" - icon_state = "checkpoint1" - -/area/security/vacantoffice - name = "\improper Vacant Office" - icon_state = "security" - -/area/security/vacantoffice2 - name = "\improper Vacant Office" - icon_state = "security" - -/area/janitor/ - name = "\improper Custodial Closet" - icon_state = "janitor" - -/area/hydroponics - name = "\improper Hydroponics" - icon_state = "hydro" - -/area/hydroponics/cafegarden - name = "\improper Cafeteria Garden" - icon_state = "cafe_garden" - -/area/hydroponics/garden - name = "\improper Garden" - icon_state = "garden" - -// SUPPLY - -/area/quartermaster - name = "\improper Quartermasters" - icon_state = "quart" - -/area/quartermaster/office - name = "\improper Cargo Office" - icon_state = "quartoffice" - -/area/quartermaster/storage - name = "\improper Cargo Bay" - icon_state = "quartstorage" - sound_env = LARGE_ENCLOSED - -/area/quartermaster/foyer - name = "\improper Cargo Bay Foyer" - icon_state = "quartstorage" - -/area/quartermaster/warehouse - name = "\improper Cargo Warehouse" - icon_state = "quartstorage" - -/area/quartermaster/qm - name = "\improper Cargo - Quartermaster's Office" - icon_state = "quart" - -/area/quartermaster/delivery - name = "\improper Cargo - Delivery Office" - icon_state = "quart" - flags = AREA_FLAG_IS_NOT_PERSISTENT //So trash doesn't pile up too hard. - -/area/quartermaster/miningdock - name = "\improper Cargo Mining Dock" - icon_state = "mining" - - -// SCIENCE - -/area/rnd/research - name = "\improper Research and Development" - icon_state = "research" - -/area/rnd/research_foyer - name = "\improper Research Foyer" - icon_state = "research_foyer" - -/area/rnd/research_foyer_auxiliary - name = "\improper Research Foyer Auxiliary" - icon_state = "research_foyer_aux" - -/area/rnd/research_restroom - name = "\improper Research Restroom" - icon_state = "research_restroom" - sound_env = SMALL_ENCLOSED - -/area/rnd/research_storage - name = "\improper Research Storage" - icon_state = "research_storage" - -/area/rnd/docking - name = "\improper Research Dock" - icon_state = "research_dock" - -/area/rnd/lab - name = "\improper Research Lab" - icon_state = "toxlab" - -/area/rnd/rdoffice - name = "\improper Research Director's Office" - icon_state = "head_quarters" - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/rnd/supermatter - name = "\improper Supermatter Lab" - icon_state = "toxlab" - -/area/rnd/xenobiology - name = "\improper Xenobiology Lab" - icon_state = "xeno_lab" - -/area/rnd/xenobiology/xenoflora_storage - name = "\improper Xenoflora Storage" - icon_state = "xeno_f_store" - -/area/rnd/xenobiology/xenoflora - name = "\improper Xenoflora Lab" - icon_state = "xeno_f_lab" - -/area/rnd/storage - name = "\improper Toxins Storage" - icon_state = "toxstorage" - -/area/rnd/test_area - name = "\improper Toxins Test Area" - icon_state = "toxtest" - -/area/rnd/mixing - name = "\improper Toxins Mixing Room" - icon_state = "toxmix" - -/area/rnd/misc_lab - name = "\improper Miscellaneous Research" - icon_state = "toxmisc" - -/area/rnd/workshop - name = "\improper Workshop" - icon_state = "sci_workshop" - -/area/toxins/server - name = "\improper Server Room" - icon_state = "server" - -//Storage - -/area/storage/tools - name = "Auxiliary Tool Storage" - icon_state = "storage" - -/area/storage/primary - name = "Primary Tool Storage" - icon_state = "primarystorage" - -/area/storage/autolathe - name = "Autolathe Storage" - icon_state = "storage" - -/area/storage/art - name = "Art Supply Storage" - icon_state = "storage" - -/area/storage/auxillary - name = "Auxillary Storage" - icon_state = "auxstorage" - -/area/storage/eva - name = "EVA Storage" - icon_state = "eva" - -/area/storage/secure - name = "Secure Storage" - icon_state = "storage" - -/area/storage/emergency_storage/emergency - name = "Starboard Emergency Storage" - icon_state = "emergencystorage" - -/area/storage/emergency_storage/emergency2 - name = "Port Emergency Storage" - icon_state = "emergencystorage" - -/area/storage/emergency_storage/emergency3 - name = "Central Emergency Storage" - icon_state = "emergencystorage" - -/area/storage/emergency_storage/emergency4 - name = "Civilian Emergency Storage" - icon_state = "emergencystorage" - -/area/storage/emergency_storage/emergency5 - name = "Dock Emergency Storage" - icon_state = "emergencystorage" - -/area/storage/emergency_storage/emergency6 - name = "Cargo Emergency Storage" - icon_state = "emergencystorage" - -/area/storage/tech - name = "Technical Storage" - icon_state = "auxstorage" - -/area/storage/testroom - requires_power = 0 - name = "\improper Test Room" - icon_state = "storage" - -//DJSTATION - -/area/djstation - name = "\improper Listening Post" - icon_state = "LP" - ambience = AMBIENCE_TECH_RUINS - -/area/djstation/solars - name = "\improper Listening Post Solars" - icon_state = "LPS" - ambience = AMBIENCE_TECH_RUINS - -//DERELICT - -/area/derelict - name = "\improper Derelict Station" - icon_state = "storage" - ambience = AMBIENCE_RUINS - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/derelict/hallway/primary - name = "\improper Derelict Primary Hallway" - icon_state = "hallP" - -/area/derelict/hallway/secondary - name = "\improper Derelict Secondary Hallway" - icon_state = "hallS" - -/area/derelict/arrival - name = "\improper Derelict Arrival Centre" - icon_state = "yellow" - -/area/derelict/storage/equipment - name = "Derelict Equipment Storage" - -/area/derelict/storage/storage_access - name = "Derelict Storage Access" - -/area/derelict/storage/engine_storage - name = "Derelict Engine Storage" - icon_state = "green" - -/area/derelict/bridge - name = "\improper Derelict Control Room" - icon_state = "bridge" - -/area/derelict/secret - name = "\improper Derelict Secret Room" - icon_state = "library" - -/area/derelict/bridge/access - name = "Derelict Control Room Access" - icon_state = "auxstorage" - -/area/derelict/bridge/ai_upload - name = "\improper Derelict Computer Core" - icon_state = "ai" - -/area/derelict/solar_control - name = "\improper Derelict Solar Control" - icon_state = "engine" - -/area/derelict/crew_quarters - name = "\improper Derelict Crew Quarters" - icon_state = "fitness" - -/area/derelict/medical - name = "Derelict Medbay" - icon_state = "medbay" - -/area/derelict/medical/morgue - name = "\improper Derelict Morgue" - icon_state = "morgue" - -/area/derelict/medical/chapel - name = "\improper Derelict Chapel" - icon_state = "chapel" - -/area/derelict/teleporter - name = "\improper Derelict Teleporter" - icon_state = "teleporter" - -/area/derelict/eva - name = "Derelict EVA Storage" - icon_state = "eva" - -/area/derelict/ship - name = "\improper Abandoned Ship" - icon_state = "yellow" - -/area/solar/derelict_starboard - name = "\improper Derelict Starboard Solar Array" - icon_state = "panelsS" - -/area/solar/derelict_aft - name = "\improper Derelict Aft Solar Array" - icon_state = "aft" - -/area/derelict/singularity_engine - name = "\improper Derelict Singularity Engine" - icon_state = "engine" - -//HALF-BUILT STATION (REPLACES DERELICT IN BAYCODE, ABOVE IS LEFT FOR DOWNSTREAM) - -/area/shuttle/constructionsite - name = "\improper Construction Site Shuttle" - icon_state = "yellow" - dynamic_lighting = 0 - base_turf = /turf/simulated/mineral/floor/ignore_mapgen - -/area/shuttle/constructionsite/station - name = "\improper Construction Site Shuttle" - -/area/shuttle/constructionsite/site - name = "\improper Construction Site Shuttle" - -/area/constructionsite - name = "\improper Construction Site" - icon_state = "storage" - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/constructionsite/storage - name = "\improper Construction Site Storage Area" - -/area/constructionsite/science - name = "\improper Construction Site Research" - -/area/constructionsite/bridge - name = "\improper Construction Site Bridge" - icon_state = "bridge" - -/area/constructionsite/maintenance - name = "\improper Construction Site Maintenance" - icon_state = "yellow" - -/area/constructionsite/hallway/aft - name = "\improper Construction Site Aft Hallway" - icon_state = "hallP" - -/area/constructionsite/hallway/fore - name = "\improper Construction Site Fore Hallway" - icon_state = "hallS" - -/area/constructionsite/atmospherics - name = "\improper Construction Site Atmospherics" - icon_state = "green" - -/area/constructionsite/medical - name = "\improper Construction Site Medbay" - icon_state = "medbay" - -/area/constructionsite/ai - name = "\improper Construction Computer Core" - icon_state = "ai" - -/area/constructionsite/engineering - name = "\improper Construction Site Engine Bay" - icon_state = "engine" - -/area/solar/constructionsite - name = "\improper Construction Site Solars" - icon_state = "aft" - -/area/constructionsite/teleporter - name = "Construction Site Teleporter" - icon_state = "yellow" - - -//area/constructionsite -// name = "\improper Construction Site Shuttle" - -//area/constructionsite -// name = "\improper Construction Site Shuttle" - - -//Construction - -/area/construction - name = "\improper Engineering Construction Area" - icon_state = "yellow" - -/area/construction/supplyshuttle - name = "\improper Supply Shuttle" - icon_state = "yellow" - -/area/construction/quarters - name = "\improper Engineer's Quarters" - icon_state = "yellow" - -/area/construction/qmaint - name = "Maintenance" - icon_state = "yellow" - -/area/construction/hallway - name = "\improper Hallway" - icon_state = "yellow" - -/area/construction/solars - name = "\improper Solar Panels" - icon_state = "yellow" - -/area/construction/solarscontrol - name = "\improper Solar Panel Control" - icon_state = "yellow" - -/area/construction/Storage - name = "Construction Site Storage" - icon_state = "yellow" - -//AI - -/area/ai_monitored/storage/eva - name = "EVA Storage" - icon_state = "eva" - -/area/ai_monitored/storage/secure - name = "Secure Storage" - icon_state = "storage" - ambience = AMBIENCE_HIGHSEC - -/area/ai_monitored/storage/emergency - name = "Emergency Storage" - icon_state = "storage" - -/area/ai_monitored/storage/emergency/eva - name = "Emergency EVA" - icon_state = "storage" - -/area/ai_upload - name = "\improper AI Upload Chamber" - icon_state = "ai_upload" - ambience = AMBIENCE_AI - -/area/ai_upload_foyer - name = "AI Upload Access" - icon_state = "ai_foyer" - sound_env = SMALL_ENCLOSED - ambience = AMBIENCE_AI - -/area/ai_server_room - name = "Messaging Server Room" - icon_state = "ai_server" - sound_env = SMALL_ENCLOSED - ambience = AMBIENCE_AI - -/area/ai - name = "\improper AI Chamber" - icon_state = "ai_chamber" - ambience = AMBIENCE_AI - -/area/ai_cyborg_station - name = "\improper Cyborg Station" - icon_state = "ai_cyborg" - sound_env = SMALL_ENCLOSED - ambience = AMBIENCE_AI - -/area/aisat - name = "\improper AI Satellite" - icon_state = "ai" - ambience = AMBIENCE_AI - -/area/aisat_interior - name = "\improper AI Satellite" - icon_state = "ai" - ambience = AMBIENCE_AI // The lack of inheritence hurts my soul. - -/area/AIsatextFP - name = "\improper AI Sat Ext" - icon_state = "storage" - luminosity = 1 - dynamic_lighting = 0 - ambience = AMBIENCE_AI - -/area/AIsatextFS - name = "\improper AI Sat Ext" - icon_state = "storage" - luminosity = 1 - dynamic_lighting = 0 - ambience = AMBIENCE_AI - -/area/AIsatextAS - name = "\improper AI Sat Ext" - icon_state = "storage" - luminosity = 1 - dynamic_lighting = 0 - ambience = AMBIENCE_AI - -/area/AIsatextAP - name = "\improper AI Sat Ext" - icon_state = "storage" - luminosity = 1 - dynamic_lighting = 0 - ambience = AMBIENCE_AI - -/area/NewAIMain - name = "\improper AI Main New" - icon_state = "storage" - ambience = AMBIENCE_AI - - - -//Misc - -/area/wreck - ambience = AMBIENCE_RUINS - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/wreck/ai - name = "\improper AI Chamber" - icon_state = "ai" - ambience = AMBIENCE_TECH_RUINS - -/area/wreck/main - name = "\improper Wreck" - icon_state = "storage" - -/area/wreck/engineering - name = "\improper Power Room" - icon_state = "engine" - ambience = AMBIENCE_TECH_RUINS - -/area/wreck/bridge - name = "\improper Bridge" - icon_state = "bridge" - ambience = AMBIENCE_TECH_RUINS - -/area/generic - name = "Unknown" - icon_state = "storage" - - - -// Telecommunications Satellite -/area/tcommsat/ - ambience = AMBIENCE_ENGINEERING - -/area/tcommsat/entrance - name = "\improper Telecomms Teleporter" - icon_state = "tcomsatentrance" - -/area/tcommsat/chamber - name = "\improper Telecomms Central Compartment" - icon_state = "tcomsatcham" - -/area/tcomsat - name = "\improper Telecomms Satellite" - icon_state = "tcomsatlob" - ambience = AMBIENCE_ENGINEERING - -/area/tcomfoyer - name = "\improper Telecomms Foyer" - icon_state = "tcomsatfoyer" - ambience = AMBIENCE_ENGINEERING - -/area/tcomwest - name = "\improper Telecommunications Satellite West Wing" - icon_state = "tcomsatwest" - ambience = AMBIENCE_ENGINEERING - -/area/tcomeast - name = "\improper Telecommunications Satellite East Wing" - icon_state = "tcomsateast" - ambience = AMBIENCE_ENGINEERING - -/area/tcommsat/computer - name = "\improper Telecomms Control Room" - icon_state = "tcomsatcomp" - -/area/tcommsat/lounge - name = "\improper Telecommunications Satellite Lounge" - icon_state = "tcomsatlounge" - -/area/tcommsat/powercontrol - name = "\improper Telecommunications Power Control" - icon_state = "tcomsatwest" - - -// Away Missions -/area/awaymission - name = "\improper Strange Location" - icon_state = "away" - ambience = AMBIENCE_FOREBODING - flags = AREA_FLAG_IS_NOT_PERSISTENT - -/area/awaymission/gateway - name = "\improper Gateway" - icon_state = "teleporter" - music = "signal" - -/area/awaymission/example - name = "\improper Strange Station" - icon_state = "away" - -/area/awaymission/wwmines - name = "\improper Wild West Mines" - icon_state = "away1" - luminosity = 1 - requires_power = 0 - -/area/awaymission/wwgov - name = "\improper Wild West Mansion" - icon_state = "away2" - luminosity = 1 - requires_power = 0 - -/area/awaymission/wwrefine - name = "\improper Wild West Refinery" - icon_state = "away3" - luminosity = 1 - requires_power = 0 - -/area/awaymission/wwvault - name = "\improper Wild West Vault" - icon_state = "away3" - luminosity = 0 - -/area/awaymission/wwvaultdoors - name = "\improper Wild West Vault Doors" // this is to keep the vault area being entirely lit because of requires_power - icon_state = "away2" - requires_power = 0 - luminosity = 0 - -/area/awaymission/desert - name = "Mars" - icon_state = "away" - -/area/awaymission/BMPship1 - name = "\improper Aft Block" - icon_state = "away1" - -/area/awaymission/BMPship2 - name = "\improper Midship Block" - icon_state = "away2" - -/area/awaymission/BMPship3 - name = "\improper Fore Block" - icon_state = "away3" - -/area/awaymission/spacebattle - name = "\improper Space Battle" - icon_state = "away" - requires_power = 0 - -/area/awaymission/spacebattle/cruiser - name = "\improper NanoTrasen Cruiser" - -/area/awaymission/spacebattle/syndicate1 - name = "\improper Syndicate Assault Ship 1" - -/area/awaymission/spacebattle/syndicate2 - name = "\improper Syndicate Assault Ship 2" - -/area/awaymission/spacebattle/syndicate3 - name = "\improper Syndicate Assault Ship 3" - -/area/awaymission/spacebattle/syndicate4 - name = "\improper Syndicate War Sphere 1" - -/area/awaymission/spacebattle/syndicate5 - name = "\improper Syndicate War Sphere 2" - -/area/awaymission/spacebattle/syndicate6 - name = "\improper Syndicate War Sphere 3" - -/area/awaymission/spacebattle/syndicate7 - name = "\improper Syndicate Fighter" - -/area/awaymission/spacebattle/secret - name = "\improper Hidden Chamber" - -/area/awaymission/listeningpost - name = "\improper Listening Post" - icon_state = "away" - requires_power = 0 - -/area/awaymission/beach - name = "Beach" - icon_state = "null" - luminosity = 1 - dynamic_lighting = 0 - requires_power = 0 - -///////////////////////////////////////////////////////////////////// -/* - Lists of areas to be used with is_type_in_list. - Used in gamemodes code at the moment. --rastaf0 -*/ - -// CENTCOM -var/list/centcom_areas = list ( - /area/centcom, - /area/shuttle/escape/centcom, - /area/shuttle/escape_pod1/centcom, - /area/shuttle/escape_pod2/centcom, - /area/shuttle/escape_pod3/centcom, - /area/shuttle/escape_pod5/centcom, - /area/shuttle/transport1/centcom, - /area/shuttle/administration/centcom, - /area/shuttle/specops/centcom, -) - -//SPACE STATION 13 -var/list/the_station_areas = list ( - /area/shuttle/arrival, - /area/shuttle/escape/station, - /area/shuttle/escape_pod1/station, - /area/shuttle/escape_pod2/station, - /area/shuttle/escape_pod3/station, - /area/shuttle/escape_pod5/station, - /area/shuttle/mining/station, - /area/shuttle/transport1/station, - // /area/shuttle/transport2/station, - /area/shuttle/prison/station, - /area/shuttle/administration/station, - /area/shuttle/specops/station, - /area/maintenance, - /area/hallway, - /area/bridge, - /area/crew_quarters, - /area/holodeck, - /area/mint, - /area/library, - /area/chapel, - /area/lawoffice, - /area/engineering, - /area/solar, - /area/assembly, - /area/teleporter, - /area/medical, - /area/security, - /area/quartermaster, - /area/janitor, - /area/hydroponics, - /area/rnd, - /area/storage, - /area/construction, - /area/ai_monitored/storage/eva, - /area/ai_monitored/storage/secure, - /area/ai_monitored/storage/emergency, - /area/ai_upload, - /area/ai_upload_foyer, - /area/ai -) - - - - -/area/beach - name = "Keelin's private beach" - icon_state = "yellow" - luminosity = 1 - dynamic_lighting = 0 +/* + +### This file contains a list of all the areas in your station. Format is as follows: + +/area/CATEGORY/OR/DESCRIPTOR/NAME (you can make as many subdivisions as you want) + name = "NICE NAME" (not required but makes things really nice) + icon = "ICON FILENAME" (defaults to areas.dmi) + icon_state = "NAME OF ICON" (defaults to "unknown" (blank)) + requires_power = 0 (defaults to 1) + music = "music/music.ogg" (defaults to "music/music.ogg") + +NOTE: there are two lists of areas in the end of this file: centcom and station itself. Please maintain these lists valid. --rastaf0 + +*/ + +/*-----------------------------------------------------------------------------*/ + +///////// +//SPACE// +///////// + +/area/space + name = "\improper Space" + icon_state = "space" + requires_power = 1 + always_unpowered = 1 + dynamic_lighting = 0 + has_gravity = 0 + power_light = 0 + power_equip = 0 + power_environ = 0 + base_turf = /turf/space + ambience = AMBIENCE_SPACE + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/space/atmosalert() + return + +/area/space/fire_alert() + return + +/area/space/fire_reset() + return + +/area/space/readyalert() + return + +/area/space/partyalert() + return + +/area/arrival + requires_power = 0 + +/area/arrival/start + name = "\improper Arrival Area" + icon_state = "start" + +/area/admin + name = "\improper Admin room" + icon_state = "start" + + + +//////////// +//SHUTTLES// +//////////// +//Shuttles only need starting area, movement is handled by landmarks +//All shuttles should now be under shuttle since we have smooth-wall code. + +/area/shuttle + requires_power = 0 + flags = RAD_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT + sound_env = SMALL_ENCLOSED + base_turf = /turf/space + forbid_events = TRUE + +/area/shuttle/arrival + name = "\improper Arrival Shuttle" + ambience = AMBIENCE_ARRIVALS + +/area/shuttle/supply + name = "\improper Supply Shuttle" + icon_state = "shuttle2" + +/area/shuttle/escape + name = "\improper Emergency Shuttle" + music = "music/escape.ogg" + +/area/shuttle/escape_pod1 + name = "\improper Escape Pod One" + music = "music/escape.ogg" + +/area/shuttle/escape_pod2 + name = "\improper Escape Pod Two" + music = "music/escape.ogg" + +/area/shuttle/escape_pod3 + name = "\improper Escape Pod Three" + music = "music/escape.ogg" + +/area/shuttle/escape_pod4 + name = "\improper Escape Pod Four" + music = "music/escape.ogg" + +/area/shuttle/escape_pod5 + name = "\improper Escape Pod Five" + music = "music/escape.ogg" + +/area/shuttle/escape_pod6 + name = "\improper Escape Pod Six" + music = "music/escape.ogg" + +/area/shuttle/large_escape_pod1 + name = "\improper Large Escape Pod One" + music = "music/escape.ogg" + +/area/shuttle/large_escape_pod2 + name = "\improper Large Escape Pod Two" + music = "music/escape.ogg" + +/area/shuttle/cryo + name = "\improper Cryogenic Storage" + +/area/shuttle/mining + name = "\improper Mining Elevator" + music = "music/escape.ogg" + dynamic_lighting = 0 + base_turf = /turf/simulated/mineral/floor/ignore_mapgen + +/area/shuttle/transport1/centcom + icon_state = "shuttle" + name = "\improper Transport Shuttle CentCom" + +/area/shuttle/transport1/station + icon_state = "shuttle" + name = "\improper Transport Shuttle" + +/area/shuttle/alien/base + icon_state = "shuttle" + name = "\improper Alien Shuttle Base" + requires_power = 1 + +/area/shuttle/alien/mine + icon_state = "shuttle" + name = "\improper Alien Shuttle Mine" + requires_power = 1 + +/area/shuttle/prison/ + name = "\improper Prison Shuttle" + +/area/shuttle/prison/station + icon_state = "shuttle" + +/area/shuttle/prison/prison + icon_state = "shuttle2" + +/area/shuttle/specops/centcom + name = "\improper Special Ops Shuttle" + icon_state = "shuttlered" + +/area/shuttle/specops/station + name = "\improper Special Ops Shuttle" + icon_state = "shuttlered2" + +/area/shuttle/syndicate_elite/mothership + name = "\improper Merc Elite Shuttle" + icon_state = "shuttlered" + +/area/shuttle/syndicate_elite/station + name = "\improper Merc Elite Shuttle" + icon_state = "shuttlered2" + +/area/shuttle/administration/centcom + name = "Centcom Large Bay (AS)" //VOREStation Edit + icon_state = "shuttlered" + +/area/shuttle/administration/station + name = "NSB Adephagia (AS)" //VOREStation Edit + icon_state = "shuttlered2" + +/area/shuttle/trade + name = "\improper Trade Station" + icon_state = "red" + dynamic_lighting = 0 + +/area/shuttle/thunderdome + name = "honk" + +/area/shuttle/research + name = "\improper Research Elevator" + music = "music/escape.ogg" + dynamic_lighting = 0 + base_turf = /turf/simulated/mineral/floor/ignore_mapgen + +/area/airtunnel1/ // referenced in airtunnel.dm:759 + +/area/dummy/ // Referenced in engine.dm:261 + +// === end remove + +/area/alien + name = "\improper Alien base" + icon_state = "yellow" + requires_power = 0 + flags = AREA_FLAG_IS_NOT_PERSISTENT + +// CENTCOM + +/area/centcom + name = "\improper CentCom" + icon_state = "centcom" + requires_power = 0 + dynamic_lighting = 0 + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/centcom/control + name = "\improper CentCom Control" + +/area/centcom/evac + name = "\improper CentCom Emergency Shuttle" + +/area/centcom/suppy + name = "\improper CentCom Supply Shuttle" + +/area/centcom/ferry + name = "\improper CentCom Transport Shuttle" + +/area/centcom/shuttle + name = "\improper CentCom Administration Shuttle" + +/area/centcom/test + name = "\improper CentCom Testing Facility" + +/area/centcom/living + name = "\improper CentCom Living Quarters" + +/area/centcom/specops + name = "\improper CentCom Special Ops" + +/area/centcom/creed + name = "Creed's Office" + +/area/centcom/holding + name = "\improper Holding Facility" + +/area/centcom/terminal + name = "\improper Docking Terminal" + icon_state = "centcom_dock" + ambience = AMBIENCE_ARRIVALS + +/area/centcom/tram + name = "\improper Tram Station" + ambience = AMBIENCE_ARRIVALS + +/area/centcom/security + name = "\improper CentCom Security" + icon_state = "centcom_security" + +/area/centcom/medical + name = "\improper CentCom Medical" + icon_state = "centcom_medical" + +/area/centcom/command + name = "\improper CentCom Command" //Central Command Command totally isn't RAS Syndrome in action. + icon_state = "centcom_command" + ambience = AMBIENCE_HIGHSEC + +/area/centcom/main_hall + name = "\improper Main Hallway" + icon_state = "centcom_hallway1" + +/area/centcom/bar + name = "\improper CentCom Bar" + icon_state = "centcom_crew" + +/area/centcom/restaurant + name = "\improper CentCom Restaurant" + icon_state = "centcom_crew" + +/area/centcom/bathroom + name = "\improper CentCom Bathroom" + icon_state = "centcom_crew" + sound_env = SMALL_ENCLOSED + +//SYNDICATES + +/area/syndicate_mothership + name = "\improper Mercenary Base" + icon_state = "syndie-ship" + requires_power = 0 + dynamic_lighting = 0 + ambience = AMBIENCE_HIGHSEC + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/syndicate_mothership/control + name = "\improper Mercenary Control Room" + icon_state = "syndie-control" + +/area/syndicate_mothership/elite_squad + name = "\improper Elite Mercenary Squad" + icon_state = "syndie-elite" + +//EXTRA + +/area/asteroid // -- TLE + name = "\improper Moon" + icon_state = "asteroid" + requires_power = 0 + sound_env = ASTEROID + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/asteroid/cave // -- TLE + name = "\improper Moon - Underground" + icon_state = "cave" + requires_power = 0 + sound_env = ASTEROID + +/area/asteroid/artifactroom + name = "\improper Moon - Artifact" + icon_state = "cave" + sound_env = SMALL_ENCLOSED + +/area/planet/clown + name = "\improper Clown Planet" + icon_state = "honk" + requires_power = 0 + +/area/tdome + name = "\improper Thunderdome" + icon_state = "thunder" + requires_power = 0 + dynamic_lighting = 0 + sound_env = SOUND_ENVIRONMENT_ARENA + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/tdome/tdome1 + name = "\improper Thunderdome (Team 1)" + icon_state = "green" + +/area/tdome/tdome2 + name = "\improper Thunderdome (Team 2)" + icon_state = "yellow" + +/area/tdome/tdomeadmin + name = "\improper Thunderdome (Admin.)" + icon_state = "purple" + +/area/tdome/tdomeobserve + name = "\improper Thunderdome (Observer.)" + icon_state = "purple" + +/area/virtual_reality + name = "Virtual Reality" + icon_state = "Virtual_Reality" + dynamic_lighting = 0 + requires_power = 0 + flags = AREA_FLAG_IS_NOT_PERSISTENT + +//ENEMY + +//names are used +/area/syndicate_station + name = "\improper Independent Station" + icon_state = "yellow" + requires_power = 0 + flags = RAD_SHIELDED + base_turf = /turf/space + ambience = AMBIENCE_HIGHSEC + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/syndicate_station/start + name = "\improper Mercenary Forward Operating Base" + icon_state = "yellow" + +/area/syndicate_station/southwest + name = "\improper south-west of SS13" + icon_state = "southwest" + +/area/syndicate_station/northwest + name = "\improper north-west of SS13" + icon_state = "northwest" + +/area/syndicate_station/northeast + name = "\improper north-east of SS13" + icon_state = "northeast" + +/area/syndicate_station/southeast + name = "\improper south-east of SS13" + icon_state = "southeast" + +/area/syndicate_station/north + name = "\improper north of SS13" + icon_state = "north" + +/area/syndicate_station/south + name = "\improper south of SS13" + icon_state = "south" + +/area/syndicate_station/commssat + name = "\improper south of the communication satellite" + icon_state = "south" + +/area/syndicate_station/mining + name = "\improper northeast of the mining station" + icon_state = "north" + +/area/syndicate_station/arrivals_dock + name = "\improper docked with station" + icon_state = "shuttle" + +/area/syndicate_station/maint_dock + name = "\improper docked with station" + icon_state = "shuttle" + +/area/syndicate_station/transit + name = "\improper hyperspace" + icon_state = "shuttle" + +/area/wizard_station + name = "\improper Wizard's Den" + icon_state = "yellow" + requires_power = 0 + dynamic_lighting = 0 + ambience = AMBIENCE_OTHERWORLDLY + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/skipjack_station + name = "\improper Skipjack" + icon_state = "yellow" + requires_power = 0 + base_turf = /turf/space + ambience = AMBIENCE_HIGHSEC + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/skipjack_station/start + name = "\improper Skipjack" + icon_state = "yellow" + +/area/skipjack_station/transit + name = "\improper hyperspace" + icon_state = "shuttle" + +/area/skipjack_station/southwest_solars + name = "\improper aft port solars" + icon_state = "southwest" + +/area/skipjack_station/northwest_solars + name = "\improper fore port solars" + icon_state = "northwest" + +/area/skipjack_station/northeast_solars + name = "\improper fore starboard solars" + icon_state = "northeast" + +/area/skipjack_station/southeast_solars + name = "\improper aft starboard solars" + icon_state = "southeast" + +/area/skipjack_station/mining + name = "\improper south of mining station" + icon_state = "north" + +//PRISON +/area/prison + name = "\improper Prison Station" + icon_state = "brig" + ambience = AMBIENCE_HIGHSEC + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/prison/arrival_airlock + name = "\improper Prison Station Airlock" + icon_state = "green" + requires_power = 0 + +/area/prison/control + name = "\improper Prison Security Checkpoint" + icon_state = "security" + +/area/prison/crew_quarters + name = "\improper Prison Security Quarters" + icon_state = "security" + +/area/prison/rec_room + name = "\improper Prison Rec Room" + icon_state = "green" + +/area/prison/closet + name = "\improper Prison Supply Closet" + icon_state = "dk_yellow" + +/area/prison/hallway/fore + name = "\improper Prison Fore Hallway" + icon_state = "yellow" + +/area/prison/hallway/aft + name = "\improper Prison Aft Hallway" + icon_state = "yellow" + +/area/prison/hallway/port + name = "\improper Prison Port Hallway" + icon_state = "yellow" + +/area/prison/hallway/starboard + name = "\improper Prison Starboard Hallway" + icon_state = "yellow" + +/area/prison/morgue + name = "\improper Prison Morgue" + icon_state = "morgue" + +/area/prison/medical_research + name = "\improper Prison Genetic Research" + icon_state = "medresearch" + +/area/prison/medical + name = "\improper Prison Medbay" + icon_state = "medbay" + +/area/prison/solar + name = "\improper Prison Solar Array" + icon_state = "storage" + requires_power = 0 + +/area/prison/podbay + name = "\improper Prison Podbay" + icon_state = "dk_yellow" + +/area/prison/solar_control + name = "\improper Prison Solar Array Control" + icon_state = "dk_yellow" + +/area/prison/solitary + name = "Solitary Confinement" + icon_state = "brig" + +/area/prison/cell_block/A + name = "Prison Cell Block A" + icon_state = "brig" + +/area/prison/cell_block/B + name = "Prison Cell Block B" + icon_state = "brig" + +/area/prison/cell_block/C + name = "Prison Cell Block C" + icon_state = "brig" + +//////////////////// +//SPACE STATION 13// +//////////////////// + +/area + ambience = AMBIENCE_GENERIC + +//Maintenance + +/area/maintenance + flags = RAD_SHIELDED + sound_env = TUNNEL_ENCLOSED + turf_initializer = new /datum/turf_initializer/maintenance() + ambience = AMBIENCE_MAINTENANCE + +/area/maintenance/aft + name = "Aft Maintenance" + icon_state = "amaint" + +/area/maintenance/fore + name = "Fore Maintenance" + icon_state = "fmaint" + +/area/maintenance/starboard + name = "Starboard Maintenance" + icon_state = "smaint" + +/area/maintenance/port + name = "Port Maintenance" + icon_state = "pmaint" + +/area/maintenance/atmos_control + name = "Atmospherics Maintenance" + icon_state = "fpmaint" + +/area/maintenance/fpmaint + name = "Fore Port Maintenance - 1" + icon_state = "fpmaint" + +/area/maintenance/fpmaint2 + name = "Fore Port Maintenance - 2" + icon_state = "fpmaint" + +/area/maintenance/fsmaint + name = "Fore Starboard Maintenance - 1" + icon_state = "fsmaint" + +/area/maintenance/fsmaint2 + name = "Fore Starboard Maintenance - 2" + icon_state = "fsmaint" + +/area/maintenance/asmaint + name = "Aft Starboard Maintenance" + icon_state = "asmaint" + +/area/maintenance/engi_shuttle + name = "Engineering Shuttle Access" + icon_state = "maint_e_shuttle" + +/area/maintenance/engi_engine + name = "Engine Maintenance" + icon_state = "maint_engine" + +/area/maintenance/asmaint2 + name = "Science Maintenance" + icon_state = "asmaint" + +/area/maintenance/apmaint + name = "Cargo Engineering Maintenance" + icon_state = "apmaint" + +/area/maintenance/maintcentral + name = "Bridge Maintenance" + icon_state = "maintcentral" + +/area/maintenance/arrivals + name = "Arrivals Maintenance" + icon_state = "maint_arrivals" + +/area/maintenance/bar + name = "Bar Maintenance" + icon_state = "maint_bar" + +/area/maintenance/central + name = "Central Maintenance" + icon_state = "maint_central" + +/area/maintenance/cafe_dock + name = "Cafeteria Dock Maintenance" + icon_state = "maint_cafe_dock" + +/area/maintenance/cargo + name = "Cargo Maintenance" + icon_state = "maint_cargo" + +/area/maintenance/cargo_research + name = "Cargo Research Maintenance" + icon_state = "maint_cargo_research" + +/area/maintenance/chapel + name = "Chapel Maintenance" + icon_state = "maint_chapel" + +/area/maintenance/disposal + name = "Waste Disposal" + icon_state = "disposal" + flags = AREA_FLAG_IS_NOT_PERSISTENT //If trash items got this far, they can be safely deleted. + +/area/maintenance/engineering + name = "Engineering Maintenance" + icon_state = "maint_engineering" + +/area/maintenance/engineering/pumpstation + name = "Engineering Pump Station" + icon_state = "maint_pumpstation" + +/area/maintenance/evahallway + name = "\improper EVA Maintenance" + icon_state = "maint_eva" + +/area/maintenance/dormitory + name = "Dormitory Maintenance" + icon_state = "maint_dormitory" + +/area/maintenance/holodeck + name = "Holodeck Maintenance" + icon_state = "maint_holodeck" + +/area/maintenance/incinerator + name = "\improper Incinerator" + icon_state = "disposal" + +/area/maintenance/library + name = "Library Maintenance" + icon_state = "maint_library" + +/area/maintenance/locker + name = "Locker Room Maintenance" + icon_state = "maint_locker" + +/area/maintenance/medbay + name = "Medbay Maintenance" + icon_state = "maint_medbay" + +/area/maintenance/medbay_aft + name = "Medbay Maintenance - Aft" + icon_state = "maint_medbay_aft" + +/area/maintenance/medbay_fore + name = "Medbay Maintenance - Fore" + icon_state = "maint_medbay_fore" + +/area/maintenance/pool + name = "Pool Maintenance" + icon_state = "maint_pool" + +/area/maintenance/research + name = "Research Maintenance" + icon_state = "maint_research" + +/area/maintenance/research_port + name = "Research Maintenance - Port" + icon_state = "maint_research_port" + +/area/maintenance/research_starboard + name = "Research Maintenance - Starboard" + icon_state = "maint_research_starboard" + +/area/maintenance/research_starboard + name = "Research Maintenance - Starboard" + icon_state = "maint_research_cargo" + +/area/maintenance/research_shuttle + name = "Research Shuttle Dock Maintenance" + icon_state = "maint_research_shuttle" + +/area/maintenance/security_port + name = "Security Maintenance - Port" + icon_state = "maint_security_port" + +/area/maintenance/security_starboard + name = "Security Maintenance - Starboard" + icon_state = "maint_security_starboard" + +/area/maintenance/storage + name = "Atmospherics" + icon_state = "green" + +/area/maintenance/tool_storage + name = "Tool Storage Maintenance" + icon_state = "maint_tool_storage" + + +// SUBSTATIONS (Subtype of maint, that should let them serve as shielded area during radstorm) + +/area/maintenance/substation + name = "Substation" + icon_state = "substation" + sound_env = SMALL_ENCLOSED + ambience = AMBIENCE_SUBSTATION + +/area/maintenance/substation/engineering // Probably will be connected to engineering SMES room, as wires cannot be crossed properly without them sharing powernets. + name = "Engineering Substation" + +// No longer used: +/area/maintenance/substation/medical_science // Medbay and Science. Each has it's own separated machinery, but it originates from the same room. + name = "Medical Research Substation" + +/area/maintenance/substation/medical // Medbay + name = "Medical Substation" + +/area/maintenance/substation/research // Research + name = "Research Substation" + +/area/maintenance/substation/cafeteria_dock // Hydro, kitchen, docks, hotel + name = "Cafeteria Dock Substation" + +/area/maintenance/substation/civilian // Dorms, Lockerroom, Pool + name = "Civilian Substation" + +/area/maintenance/substation/civilian_east // Bar, kitchen, dorms, ... + name = "Civilian East Substation" + +/area/maintenance/substation/civilian_west // Cargo, PTS, locker room, probably arrivals, ...) + name = "Civilian West Substation" + +/area/maintenance/substation/cargo // Cargo + name = "Cargo Substation" + +/area/maintenance/substation/command // AI and central cluster. This one will be between HoP office and meeting room (probably). + name = "Command Substation" + +/area/maintenance/substation/dock // Bar, docks, hotel + name = "Dock Substation" + +/area/maintenance/substation/security // Security, Brig, Permabrig, etc. + name = "Security Substation" + +//Hallway + +/area/hallway/primary/ + sound_env = LARGE_ENCLOSED + ambience = AMBIENCE_GENERIC + +/area/hallway/primary/fore + name = "\improper Fore Primary Hallway" + icon_state = "hallF" + +/area/hallway/primary/starboard + name = "\improper Starboard Primary Hallway" + icon_state = "hallS" + +/area/hallway/primary/aft + name = "\improper Aft Primary Hallway" + icon_state = "hallA" + +/area/hallway/primary/port + name = "\improper Port Primary Hallway" + icon_state = "hallP" + +/area/hallway/primary/central_one + name = "\improper Central Primary Hallway - Fore" + icon_state = "hallC1" + +/area/hallway/primary/central_two + name = "\improper Central Primary Hallway - Starboard" + icon_state = "hallC2" + +/area/hallway/primary/central_three + name = "\improper Central Primary Hallway - Aft" + icon_state = "hallC3" + +/area/hallway/primary/central_four + name = "\improper Central Primary Hallway - Port" + icon_state = "hallC4" + +/area/hallway/secondary/exit + name = "\improper Escape Shuttle Hallway" + icon_state = "escape" + +/area/hallway/secondary/construction + name = "\improper Construction Area" + icon_state = "construction" + +/area/hallway/secondary/entry + forbid_events = TRUE + +/area/hallway/secondary/entry/fore + name = "\improper Shuttle Dock Hallway - Mid" + icon_state = "entry_1" + +/area/hallway/secondary/entry/port + name = "\improper Shuttle Dock Hallway - Port" + icon_state = "entry_2" + +/area/hallway/secondary/entry/starboard + name = "\improper Shuttle Dock Hallway - Starboard" + icon_state = "entry_3" + +/area/hallway/secondary/entry/aft + name = "\improper Shuttle Dock Hallway - Aft" + icon_state = "entry_4" + +/area/hallway/secondary/entry/D1 + name = "\improper Shuttle Dock Hallway - Dock One" + icon_state = "entry_D1" + base_turf = /turf/space + +/area/hallway/secondary/entry/D2 + name = "\improper Shuttle Dock Hallway - Dock Two" + icon_state = "entry_D2" + base_turf = /turf/space + +/area/hallway/secondary/entry/D2/arrivals + name = "\improper Shuttle Dock Hallway - Dock Two" + icon_state = "entry_D2" + base_turf = /turf/space + requires_power = 0 + +/area/hallway/secondary/entry/D3 + name = "\improper Shuttle Dock Hallway - Dock Three" + icon_state = "entry_D3" + base_turf = /turf/space + +/area/hallway/secondary/entry/D4 + name = "\improper Shuttle Dock Hallway - Dock Four" + icon_state = "entry_D4" + +/area/hallway/secondary/entry/docking_lounge + name = "\improper Docking Lounge" + icon_state = "docking_lounge" + +/area/hallway/secondary/escape/dock_escape_pod_hallway_port + name = "\improper Dock Escape Pod Hallway Port" + icon_state = "dock_escape_pod_hallway_port" + +/area/hallway/secondary/escape/dock_escape_pod_hallway_starboard + name = "\improper Dock Escape Pod Hallway Starboard" + icon_state = "dock_escape_pod_hallway_starboard" + +/area/hallway/secondary/escape/fore_port_escape_pod_hallway + name = "\improper Fore Port Escape Pod Hallway" + icon_state = "fore_port_escape_pod_hallway" + +/area/hallway/secondary/escape/fore_escape_pod_hallway + name = "\improper Fore Escape Pod Hallway" + icon_state = "fore_escape_pod_hallway" + +/area/hallway/secondary/escape/medical_escape_pod_hallway + name = "\improper Medical Escape Pod Hallway" + icon_state = "medical_escape_pod_hallway" + +/area/hallway/secondary/cargo_hallway + name = "\improper Cargo Hallway" + icon_state = "cargo_hallway" + +/area/hallway/secondary/civilian_hallway_aft + name = "\improper Civilian Hallway Aft" + icon_state = "aft_civilian_hallway" + +/area/hallway/secondary/civilian_hallway_fore + name = "\improper Civilian Hallway Fore" + icon_state = "fore_civilian_hallway" + +/area/hallway/secondary/civilian_hallway_mid + name = "\improper Civilian Hallway Mid" + icon_state = "mid_civilian_hallway" + +/area/hallway/secondary/chapel_hallway + name = "\improper Chapel Hallway" + icon_state = "chapel_hallway" + +/area/hallway/secondary/cryostorage_hallway + name = "\improper Cryostorage Hallway" + icon_state = "cryostorage_hallway" + +/area/hallway/secondary/docking_hallway + name = "\improper Docking Hallway" + icon_state = "docking_hallway" + +/area/hallway/secondary/docking_hallway2 + name = "\improper Secondary Docking Hallway" + icon_state = "docking_hallway" + +/area/hallway/secondary/engineering_hallway + name = "\improper Engineering Primary Hallway" + icon_state = "engineering_primary_hallway" + +/area/hallway/secondary/eva_hallway + name = "\improper EVA Hallway" + icon_state = "eva_hallway" + +/area/hallway/secondary/medical_emergency_hallway + name = "\improper Medical Emergency Hallway" + icon_state = "medical_emergency_hallway" + +//Command + +/area/bridge + name = "\improper Bridge" + icon_state = "bridge" + music = "signal" + +/area/bridge_hallway + name = "\improper Bridge Hallway" + icon_state = "bridge" + +/area/bridge/meeting_room + name = "\improper Heads of Staff Meeting Room" + icon_state = "bridge" + music = null + sound_env = MEDIUM_SOFTFLOOR + +/area/crew_quarters/captain + name = "\improper Command - Site Manager's Office" + icon_state = "captain" + sound_env = MEDIUM_SOFTFLOOR + +/area/crew_quarters/heads/hop + name = "\improper Command - HoP's Office" + icon_state = "head_quarters" + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/crew_quarters/heads/hor + name = "\improper Research - RD's Office" + icon_state = "head_quarters" + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/crew_quarters/heads/chief + name = "\improper Engineering - CE's Office" + icon_state = "head_quarters" + +/area/crew_quarters/heads/hos + name = "\improper Security - HoS' Office" + icon_state = "head_quarters" + +/area/crew_quarters/heads/cmo + name = "\improper Medbay - CMO's Office" + icon_state = "head_quarters" + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/crew_quarters/courtroom + name = "\improper Courtroom" + icon_state = "courtroom" + +/area/mint + name = "\improper Mint" + icon_state = "green" + +/area/comms + name = "\improper Communications Relay" + icon_state = "tcomsatcham" + +/area/server + name = "\improper Research Server Room" + icon_state = "server" + +//Civilian + +/area/crew_quarters + name = "\improper Dormitories" + icon_state = "Sleep" + flags = RAD_SHIELDED + ambience = AMBIENCE_GENERIC + forbid_events = TRUE + forbid_singulo = TRUE + +/area/crew_quarters/toilet + name = "\improper Dormitory Toilets" + icon_state = "toilet" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/sleep + name = "\improper Dormitories" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A1 + name = "\improper Apartment A1" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A2 + name = "\improper Apartment A2" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A3 + name = "\improper Apartment A3" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A4 + name = "\improper Apartment A4" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A5 + name = "\improper Apartment A5" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A6 + name = "\improper Apartment A6" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A7 + name = "\improper Apartment A7`" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_A8 + name = "\improper Apartment A8" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_B1 + name = "\improper Apartment B1" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_B2 + name = "\improper Apartment B2" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Apartment_B3 + name = "\improper Apartment B3" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_1 + name = "\improper Dormitory Room 1" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_2 + name = "\improper Dormitory Room 2" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_3 + name = "\improper Dormitory Room 3" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_4 + name = "\improper Dormitory Room 4" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_5 + name = "\improper Dormitory Room 5" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_6 + name = "\improper Dormitory Room 6" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_7 + name = "\improper Dormitory Room 7" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_8 + name = "\improper Dormitory Room 8" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_9 + name = "\improper Dormitory Room 9" + icon_state = "Sleep" + +/area/crew_quarters/sleep/Dorm_10 + name = "\improper Dormitory Room 10" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_1 + name = "\improper Visitor Room 1" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_2 + name = "\improper Visitor Room 2" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_3 + name = "\improper Visitor Room 3" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_4 + name = "\improper Visitor Room 4" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_5 + name = "\improper Visitor Room 5" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_6 + name = "\improper Visitor Room 6" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_7 + name = "\improper Visitor Room 7" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_8 + name = "\improper Visitor Room 8" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_9 + name = "\improper Visitor Room 9" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_10 + name = "\improper Visitor Room 10" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_11 + name = "\improper Visitor Room 11" + icon_state = "Sleep" + +/area/crew_quarters/sleep/vistor_room_12 + name = "\improper Visitor Room 12" + icon_state = "Sleep" + +/area/crew_quarters/sleep/engi_wash + name = "\improper Engineering Washroom" + icon_state = "toilet" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/sleep/bedrooms + name = "\improper Dormitory Bedroom One" + icon_state = "Sleep" + sound_env = SMALL_SOFTFLOOR + +/area/crew_quarters/sleep/cryo + name = "\improper Cryogenic Storage" + icon_state = "Sleep" + +/area/crew_quarters/sleep/elevator + name = "\improper Main Elevator" + icon_state = "Sleep" + +/area/crew_quarters/sleep_male + name = "\improper Male Dorm" + icon_state = "Sleep" + +/area/crew_quarters/sleep_male/toilet_male + name = "\improper Male Toilets" + icon_state = "toilet" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/sleep_female + name = "\improper Female Dorm" + icon_state = "Sleep" + +/area/crew_quarters/sleep_female/toilet_female + name = "\improper Female Toilets" + icon_state = "toilet" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/locker + name = "\improper Locker Room" + icon_state = "locker" + +/area/crew_quarters/locker/locker_toilet + name = "\improper Locker Toilets" + icon_state = "toilet" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/fitness + name = "\improper Fitness Room" + icon_state = "fitness" + +/area/crew_quarters/longue_area + name = "\improper Lounge" //VOREStation Edit - Muh speeling. + icon_state = "recreation_area" + +/area/crew_quarters/recreation_area + name = "\improper Recreation Area" + icon_state = "recreation_area" + +/area/crew_quarters/recreation_area_hallway + name = "\improper Recreation Area Hallway" + icon_state = "recreation_area_hallway" + +/area/crew_quarters/recreation_area_restroom + name = "\improper Recreation Area Restroom" + icon_state = "recreation_area_restroom" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/pool + name = "\improper Pool" + icon_state = "pool" + +/area/crew_quarters/cafeteria + name = "\improper Cafeteria" + icon_state = "cafeteria" + +/area/crew_quarters/coffee_shop + name = "\improper Coffee Shop" + icon_state = "coffee_shop" + +/area/crew_quarters/kitchen + name = "\improper Kitchen" + icon_state = "kitchen" + +/area/crew_quarters/bar + name = "\improper Bar" + icon_state = "bar" + sound_env = LARGE_SOFTFLOOR + +/area/crew_quarters/barrestroom + name = "\improper Cafeteria Restroom" + icon_state = "bar" + sound_env = SMALL_ENCLOSED + +/area/crew_quarters/theatre + name = "\improper Theatre" + icon_state = "Theatre" + sound_env = LARGE_SOFTFLOOR + +/area/crew_quarters/visitor_lodging + name = "\improper Visitor Lodging" + icon_state = "visitor_lodging" + +/area/crew_quarters/visitor_dining + name = "\improper Visitor Dining" + icon_state = "visitor_dinning" + +/area/crew_quarters/visitor_laundry + name = "\improper Visitor Laundry" + icon_state = "visitor_laundry" + +/area/library + name = "\improper Library" + icon_state = "library" + sound_env = LARGE_SOFTFLOOR + lightswitch = 0 // VOREStation Edit - We like dark libraries + +/area/library_conference_room + name = "\improper Library Conference Room" + icon_state = "library_conference_room" + +/area/chapel + ambience = AMBIENCE_CHAPEL + +/area/chapel/main + name = "\improper Chapel" + icon_state = "chapel" + sound_env = LARGE_ENCLOSED + +/area/chapel/office + name = "\improper Chapel Office" + icon_state = "chapeloffice" + +/area/chapel/chapel_morgue + name = "\improper Chapel Morgue" + icon_state = "chapel_morgue" + +/area/lawoffice + name = "\improper Internal Affairs" + icon_state = "law" + +/area/holodeck_control + name = "\improper Holodeck Control" + icon_state = "holodeck_control" + +/area/vacant/vacant_shop + name = "\improper Vacant Shop" + icon_state = "vacant_shop" + +/area/vacant/vacant_site + name = "\improper Vacant Site" + icon_state = "vacant_site" + +/area/vacant/vacant_site2 + name = "\improper Abandoned Locker Room" + icon_state = "vacant_site" + +/area/holodeck + name = "\improper Holodeck" + icon_state = "Holodeck" + dynamic_lighting = 0 + sound_env = LARGE_ENCLOSED + forbid_events = TRUE + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/holodeck/alphadeck + name = "\improper Holodeck Alpha" + +/area/holodeck/source_plating + name = "\improper Holodeck - Off" + +/area/holodeck/source_emptycourt + name = "\improper Holodeck - Empty Court" + sound_env = SOUND_ENVIRONMENT_ARENA + +/area/holodeck/source_boxingcourt + name = "\improper Holodeck - Boxing Court" + sound_env = SOUND_ENVIRONMENT_ARENA + +/area/holodeck/source_basketball + name = "\improper Holodeck - Basketball Court" + sound_env = SOUND_ENVIRONMENT_ARENA + +/area/holodeck/source_thunderdomecourt + name = "\improper Holodeck - Thunderdome Court" + requires_power = 0 + sound_env = SOUND_ENVIRONMENT_ARENA + +/area/holodeck/source_courtroom + name = "\improper Holodeck - Courtroom" + sound_env = SOUND_ENVIRONMENT_AUDITORIUM + +/area/holodeck/source_beach + name = "\improper Holodeck - Beach" + sound_env = SOUND_ENVIRONMENT_PLAIN + +/area/holodeck/source_burntest + name = "\improper Holodeck - Atmospheric Burn Test" + +/area/holodeck/source_wildlife + name = "\improper Holodeck - Wildlife Simulation" + +/area/holodeck/source_meetinghall + name = "\improper Holodeck - Meeting Hall" + sound_env = SOUND_ENVIRONMENT_AUDITORIUM + +/area/holodeck/source_theatre + name = "\improper Holodeck - Theatre" + sound_env = SOUND_ENVIRONMENT_CONCERT_HALL + +/area/holodeck/source_picnicarea + name = "\improper Holodeck - Picnic Area" + sound_env = SOUND_ENVIRONMENT_PLAIN + +/area/holodeck/source_snowfield + name = "\improper Holodeck - Snow Field" + sound_env = SOUND_ENVIRONMENT_FOREST + +/area/holodeck/source_desert + name = "\improper Holodeck - Desert" + sound_env = SOUND_ENVIRONMENT_PLAIN + +/area/holodeck/source_space + name = "\improper Holodeck - Space" + has_gravity = 0 + sound_env = SPACE + +/area/holodeck/source_chess + name = "\improper Holodeck - Chessboard" + + +//Engineering + +/area/engineering/ + name = "\improper Engineering" + icon_state = "engineering" + ambience = AMBIENCE_ENGINEERING + +/area/engineering/atmos + name = "\improper Atmospherics" + icon_state = "atmos" + sound_env = LARGE_ENCLOSED + ambience = AMBIENCE_ATMOS + +/area/engineering/atmos/monitoring + name = "\improper Atmospherics Monitoring Room" + icon_state = "atmos_monitoring" + sound_env = STANDARD_STATION + +/area/engineering/atmos/storage + name = "\improper Atmospherics Storage" + icon_state = "atmos_storage" + sound_env = SMALL_ENCLOSED + +/area/engineering/drone_fabrication + name = "\improper Engineering Drone Fabrication" + icon_state = "drone_fab" + sound_env = SMALL_ENCLOSED + +/area/engineering/engine_smes + name = "\improper Engineering SMES" + icon_state = "engine_smes" + sound_env = SMALL_ENCLOSED + +/area/engineering/engine_room + name = "\improper Engine Room" + icon_state = "engine" + sound_env = LARGE_ENCLOSED + forbid_events = TRUE + +/area/engineering/engine_airlock + name = "\improper Engine Room Airlock" + icon_state = "engine" + +/area/engineering/engine_monitoring + name = "\improper Engine Monitoring Room" + icon_state = "engine_monitoring" + +/area/engineering/engine_waste + name = "\improper Engine Waste Handling" + icon_state = "engine_waste" + +/area/engineering/engineering_monitoring + name = "\improper Engineering Monitoring Room" + icon_state = "engine_monitoring" + +/area/engineering/foyer + name = "\improper Engineering Foyer" + icon_state = "engineering_foyer" + +/area/engineering/storage + name = "\improper Engineering Storage" + icon_state = "engineering_storage" + +/area/engineering/break_room + name = "\improper Engineering Break Room" + icon_state = "engineering_break" + sound_env = MEDIUM_SOFTFLOOR + +/area/engineering/engine_eva + name = "\improper Engine EVA" + icon_state = "engine_eva" + +/area/engineering/locker_room + name = "\improper Engineering Locker Room" + icon_state = "engineering_locker" + +/area/engineering/workshop + name = "\improper Engineering Workshop" + icon_state = "engineering_workshop" + +/area/engineering/aft_hallway + name = "\improper Engineering Aft Hallway" + icon_state = "engineering_aft_hallway" + + +//Solars + +/area/solar + requires_power = 1 + always_unpowered = 1 + dynamic_lighting = 0 + ambience = AMBIENCE_SPACE + +/area/solar/auxport + name = "\improper Fore Port Solar Array" + icon_state = "panelsA" + +/area/solar/auxstarboard + name = "\improper Fore Starboard Solar Array" + icon_state = "panelsA" + +/area/solar/fore + name = "\improper Fore Solar Array" + icon_state = "yellow" + +/area/solar/aft + name = "\improper Aft Solar Array" + icon_state = "aft" + +/area/solar/starboard + name = "\improper Aft Starboard Solar Array" + icon_state = "panelsS" + +/area/solar/port + name = "\improper Aft Port Solar Array" + icon_state = "panelsP" + +/area/maintenance/auxsolarport + name = "Solar Maintenance - Fore Port" + icon_state = "SolarcontrolP" + sound_env = SMALL_ENCLOSED + +/area/maintenance/starboardsolar + name = "Solar Maintenance - Aft Starboard" + icon_state = "SolarcontrolS" + sound_env = SMALL_ENCLOSED + +/area/maintenance/portsolar + name = "Solar Maintenance - Aft Port" + icon_state = "SolarcontrolP" + sound_env = SMALL_ENCLOSED + +/area/maintenance/auxsolarstarboard + name = "Solar Maintenance - Fore Starboard" + icon_state = "SolarcontrolS" + sound_env = SMALL_ENCLOSED + +/area/maintenance/foresolar + name = "Solar Maintenance - Fore" + icon_state = "SolarcontrolA" + sound_env = SMALL_ENCLOSED + +/area/assembly/chargebay + name = "\improper Mech Bay" + icon_state = "mechbay" + +/area/assembly/showroom + name = "\improper Robotics Showroom" + icon_state = "showroom" + +/area/assembly/robotics + name = "\improper Robotics Lab" + icon_state = "robotics" + +/area/assembly/assembly_line //Derelict Assembly Line + name = "\improper Assembly Line" + icon_state = "ass_line" + power_equip = 0 + power_light = 0 + power_environ = 0 + +//Teleporter + +/area/teleporter + name = "\improper Teleporter" + icon_state = "teleporter" + music = "signal" + +/area/gateway + name = "\improper Gateway" + icon_state = "teleporter" + music = "signal" + +/area/AIsattele + name = "\improper AI Satellite Teleporter Room" + icon_state = "teleporter" + music = "signal" + +//MedBay + +/area/medical/medbay + name = "\improper Medbay Hallway - Port" + icon_state = "medbay" + music = 'sound/ambience/signal.ogg' + +//Medbay is a large area, these additional areas help level out APC load. +/area/medical/medbay2 + name = "\improper Medbay Hallway - Starboard" + icon_state = "medbay2" + music = 'sound/ambience/signal.ogg' + +/area/medical/medbay3 + name = "\improper Medbay Hallway - Fore" + icon_state = "medbay3" + music = 'sound/ambience/signal.ogg' + +/area/medical/medbay4 + name = "\improper Medbay Hallway - Aft" + icon_state = "medbay4" + music = 'sound/ambience/signal.ogg' + +/area/medical/biostorage + name = "\improper Secondary Storage" + icon_state = "medbay2" + music = 'sound/ambience/signal.ogg' + +/area/medical/reception + name = "\improper Medbay Reception" + icon_state = "medbay" + music = 'sound/ambience/signal.ogg' + +/area/medical/medbay_emt_bay + name = "\improper Medical EMT Bay" + icon_state = "medbay_emt_bay" + music = 'sound/ambience/signal.ogg' + +/area/medical/medbay_primary_storage + name = "\improper Medbay Primary Storage" + icon_state = "medbay_primary_storage" + music = 'sound/ambience/signal.ogg' + +/area/medical/psych + name = "\improper Psych Room" + icon_state = "medbay3" + music = 'sound/ambience/signal.ogg' + +/area/crew_quarters/medbreak + name = "\improper Break Room" + icon_state = "medbay3" + music = 'sound/ambience/signal.ogg' + +/area/crew_quarters/medical_restroom + name = "\improper Medbay Restroom" + icon_state = "medbay_restroom" + sound_env = SMALL_ENCLOSED + +/area/medical/patients_rooms + name = "\improper Patient's Rooms" + icon_state = "patients" + +/area/medical/ward + name = "\improper Recovery Ward" + icon_state = "patients" + +/area/medical/patient_a + name = "\improper Patient A" + icon_state = "medbay_patient_room_a" + +/area/medical/patient_b + name = "\improper Patient B" + icon_state = "medbay_patient_room_b" + +/area/medical/patient_c + name = "\improper Patient C" + icon_state = "medbay_patient_room_c" + +/area/medical/patient_d + name = "\improper Patient D" + icon_state = "medbay_patient_room_d" + +/area/medical/patient_e + name = "\improper Patient E" + icon_state = "medbay_patient_room_e" + +/area/medical/patient_wing + name = "\improper Patient Wing" + icon_state = "patients" + +/area/medical/cmostore + name = "\improper Secure Storage" + icon_state = "CMO" + +/area/medical/robotics + name = "\improper Robotics" + icon_state = "medresearch" + +/area/medical/virology + name = "\improper Virology" + icon_state = "virology" + +/area/medical/virologyaccess + name = "\improper Virology Access" + icon_state = "virology" + +/area/medical/morgue + name = "\improper Morgue" + icon_state = "morgue" + +/area/medical/chemistry + name = "\improper Chemistry" + icon_state = "chem" + +/area/medical/surgery + name = "\improper Operating Theatre 1" + icon_state = "surgery" + flags = AREA_FLAG_IS_NOT_PERSISTENT //This WOULD become a filth pit + +/area/medical/surgery2 + name = "\improper Operating Theatre 2" + icon_state = "surgery" + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/medical/surgeryobs + name = "\improper Operation Observation Room" + icon_state = "surgery" + +/area/medical/surgeryprep + name = "\improper Pre-Op Prep Room" + icon_state = "surgery" + +/area/medical/surgery_hallway + name = "\improper Surgery Hallway" + icon_state = "surgery_hallway" + +/area/medical/surgery_storage + name = "\improper Surgery Storage" + icon_state = "surgery_storage" + +/area/medical/cryo + name = "\improper Cryogenics" + icon_state = "cryo" + +/area/medical/exam_room + name = "\improper Exam Room" + icon_state = "exam_room" + +/area/medical/genetics + name = "\improper Genetics Lab" + icon_state = "genetics" + +/area/medical/genetics_cloning + name = "\improper Cloning Lab" + icon_state = "cloning" + +/area/medical/sleeper + name = "\improper Emergency Treatment Centre" + icon_state = "exam_room" + flags = AREA_FLAG_IS_NOT_PERSISTENT //Trust me. + +/area/medical/first_aid_station_starboard + name = "\improper Starboard First-Aid Station" + icon_state = "medbay2" + +/area/medical/first_aid_station + name = "\improper Port First-Aid Station" + icon_state = "medbay2" + + + +//Security + +/area/security/main + name = "\improper Security Office" + icon_state = "security" + +/area/security/lobby + name = "\improper Security Lobby" + icon_state = "security" + +/area/security/brig + name = "\improper Security - Brig" + icon_state = "brig" + +/area/security/brig/prison_break() + for(var/obj/structure/closet/secure_closet/brig/temp_closet in src) + temp_closet.locked = 0 + temp_closet.icon_state = "closed_unlocked" + for(var/obj/machinery/door_timer/temp_timer in src) + temp_timer.timer_duration = 1 + ..() + +/area/security/prison + name = "\improper Security - Prison Wing" + icon_state = "sec_prison" + +/area/security/prison/prison_break() + for(var/obj/structure/closet/secure_closet/brig/temp_closet in src) + temp_closet.locked = 0 + temp_closet.icon_state = "closed_unlocked" + for(var/obj/machinery/door_timer/temp_timer in src) + temp_timer.timer_duration = 1 + ..() + +/area/security/warden + name = "\improper Security - Warden's Office" + icon_state = "Warden" + +/area/security/armoury + name = "\improper Security - Armory" + icon_state = "armory" + ambience = AMBIENCE_HIGHSEC + +/area/security/briefing_room + name = "\improper Security - Briefing Room" + icon_state = "brig" + +/area/security/evidence_storage + name = "\improper Security - Equipment Storage" + icon_state = "security_equipment_storage" + +/area/security/evidence_storage + name = "\improper Security - Evidence Storage" + icon_state = "evidence_storage" + +/area/security/interrogation + name = "\improper Security - Interrogation" + icon_state = "interrogation" + +/area/security/riot_control + name = "\improper Security - Riot Control" + icon_state = "riot_control" + +/area/security/detectives_office + name = "\improper Security - Forensic Office" + icon_state = "detective" + sound_env = MEDIUM_SOFTFLOOR + +/area/security/range + name = "\improper Security - Firing Range" + icon_state = "firingrange" + +/area/security/security_aid_station + name = "\improper Security - Security Aid Station" + icon_state = "security_aid_station" + +/area/security/security_bathroom + name = "\improper Security - Restroom" + icon_state = "security_bathroom" + sound_env = SMALL_ENCLOSED + +/area/security/security_cell_hallway + name = "\improper Security - Cell Hallway" + icon_state = "security_cell_hallway" + +/area/security/security_equiptment_storage + name = "\improper Security - Equipment Storage" + icon_state = "security_equip_storage" + +/area/security/security_lockerroom + name = "\improper Security - Locker Room" + icon_state = "security_lockerroom" + +/area/security/security_processing + name = "\improper Security - Security Processing" + icon_state = "security_processing" + +/area/security/tactical + name = "\improper Security - Tactical Equipment" + icon_state = "Tactical" + ambience = AMBIENCE_HIGHSEC + + +/* + New() + ..() + + spawn(10) //let objects set up first + for(var/turf/turfToGrayscale in src) + if(turfToGrayscale.icon) + var/icon/newIcon = icon(turfToGrayscale.icon) + newIcon.GrayScale() + turfToGrayscale.icon = newIcon + for(var/obj/objectToGrayscale in turfToGrayscale) //1 level deep, means tables, apcs, locker, etc, but not locker contents + if(objectToGrayscale.icon) + var/icon/newIcon = icon(objectToGrayscale.icon) + newIcon.GrayScale() + objectToGrayscale.icon = newIcon +*/ + +/area/security/nuke_storage + name = "\improper Vault" + icon_state = "nuke_storage" + ambience = AMBIENCE_HIGHSEC + +/area/security/checkpoint + name = "\improper Security Checkpoint" + icon_state = "checkpoint1" + +/area/security/checkpoint2 + name = "\improper Security - Arrival Checkpoint" + icon_state = "security" + ambience = AMBIENCE_ARRIVALS + +/area/security/checkpoint/supply + name = "Security Post - Cargo Bay" + icon_state = "checkpoint1" + +/area/security/checkpoint/engineering + name = "Security Post - Engineering" + icon_state = "checkpoint1" + +/area/security/checkpoint/medical + name = "Security Post - Medbay" + icon_state = "checkpoint1" + +/area/security/checkpoint/science + name = "Security Post - Science" + icon_state = "checkpoint1" + +/area/security/vacantoffice + name = "\improper Vacant Office" + icon_state = "security" + +/area/security/vacantoffice2 + name = "\improper Vacant Office" + icon_state = "security" + +/area/janitor/ + name = "\improper Custodial Closet" + icon_state = "janitor" + +/area/hydroponics + name = "\improper Hydroponics" + icon_state = "hydro" + +/area/hydroponics/cafegarden + name = "\improper Cafeteria Garden" + icon_state = "cafe_garden" + +/area/hydroponics/garden + name = "\improper Garden" + icon_state = "garden" + +// SUPPLY + +/area/quartermaster + name = "\improper Quartermasters" + icon_state = "quart" + +/area/quartermaster/office + name = "\improper Cargo Office" + icon_state = "quartoffice" + +/area/quartermaster/storage + name = "\improper Cargo Bay" + icon_state = "quartstorage" + sound_env = LARGE_ENCLOSED + +/area/quartermaster/foyer + name = "\improper Cargo Bay Foyer" + icon_state = "quartstorage" + +/area/quartermaster/warehouse + name = "\improper Cargo Warehouse" + icon_state = "quartstorage" + +/area/quartermaster/qm + name = "\improper Cargo - Quartermaster's Office" + icon_state = "quart" + +/area/quartermaster/delivery + name = "\improper Cargo - Delivery Office" + icon_state = "quart" + flags = AREA_FLAG_IS_NOT_PERSISTENT //So trash doesn't pile up too hard. + +/area/quartermaster/miningdock + name = "\improper Cargo Mining Dock" + icon_state = "mining" + + +// SCIENCE + +/area/rnd/research + name = "\improper Research and Development" + icon_state = "research" + +/area/rnd/research_foyer + name = "\improper Research Foyer" + icon_state = "research_foyer" + +/area/rnd/research_foyer_auxiliary + name = "\improper Research Foyer Auxiliary" + icon_state = "research_foyer_aux" + +/area/rnd/research_restroom + name = "\improper Research Restroom" + icon_state = "research_restroom" + sound_env = SMALL_ENCLOSED + +/area/rnd/research_storage + name = "\improper Research Storage" + icon_state = "research_storage" + +/area/rnd/docking + name = "\improper Research Dock" + icon_state = "research_dock" + +/area/rnd/lab + name = "\improper Research Lab" + icon_state = "toxlab" + +/area/rnd/rdoffice + name = "\improper Research Director's Office" + icon_state = "head_quarters" + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/rnd/supermatter + name = "\improper Supermatter Lab" + icon_state = "toxlab" + +/area/rnd/xenobiology + name = "\improper Xenobiology Lab" + icon_state = "xeno_lab" + +/area/rnd/xenobiology/xenoflora_storage + name = "\improper Xenoflora Storage" + icon_state = "xeno_f_store" + +/area/rnd/xenobiology/xenoflora + name = "\improper Xenoflora Lab" + icon_state = "xeno_f_lab" + +/area/rnd/storage + name = "\improper Toxins Storage" + icon_state = "toxstorage" + +/area/rnd/test_area + name = "\improper Toxins Test Area" + icon_state = "toxtest" + +/area/rnd/mixing + name = "\improper Toxins Mixing Room" + icon_state = "toxmix" + +/area/rnd/misc_lab + name = "\improper Miscellaneous Research" + icon_state = "toxmisc" + +/area/rnd/workshop + name = "\improper Workshop" + icon_state = "sci_workshop" + +/area/toxins/server + name = "\improper Server Room" + icon_state = "server" + +//Storage + +/area/storage/tools + name = "Auxiliary Tool Storage" + icon_state = "storage" + +/area/storage/primary + name = "Primary Tool Storage" + icon_state = "primarystorage" + +/area/storage/autolathe + name = "Autolathe Storage" + icon_state = "storage" + +/area/storage/art + name = "Art Supply Storage" + icon_state = "storage" + +/area/storage/auxillary + name = "Auxillary Storage" + icon_state = "auxstorage" + +/area/storage/eva + name = "EVA Storage" + icon_state = "eva" + +/area/storage/secure + name = "Secure Storage" + icon_state = "storage" + +/area/storage/emergency_storage/emergency + name = "Starboard Emergency Storage" + icon_state = "emergencystorage" + +/area/storage/emergency_storage/emergency2 + name = "Port Emergency Storage" + icon_state = "emergencystorage" + +/area/storage/emergency_storage/emergency3 + name = "Central Emergency Storage" + icon_state = "emergencystorage" + +/area/storage/emergency_storage/emergency4 + name = "Civilian Emergency Storage" + icon_state = "emergencystorage" + +/area/storage/emergency_storage/emergency5 + name = "Dock Emergency Storage" + icon_state = "emergencystorage" + +/area/storage/emergency_storage/emergency6 + name = "Cargo Emergency Storage" + icon_state = "emergencystorage" + +/area/storage/tech + name = "Technical Storage" + icon_state = "auxstorage" + +/area/storage/testroom + requires_power = 0 + name = "\improper Test Room" + icon_state = "storage" + +//DJSTATION + +/area/djstation + name = "\improper Listening Post" + icon_state = "LP" + ambience = AMBIENCE_TECH_RUINS + +/area/djstation/solars + name = "\improper Listening Post Solars" + icon_state = "LPS" + ambience = AMBIENCE_TECH_RUINS + +//DERELICT + +/area/derelict + name = "\improper Derelict Station" + icon_state = "storage" + ambience = AMBIENCE_RUINS + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/derelict/hallway/primary + name = "\improper Derelict Primary Hallway" + icon_state = "hallP" + +/area/derelict/hallway/secondary + name = "\improper Derelict Secondary Hallway" + icon_state = "hallS" + +/area/derelict/arrival + name = "\improper Derelict Arrival Centre" + icon_state = "yellow" + +/area/derelict/storage/equipment + name = "Derelict Equipment Storage" + +/area/derelict/storage/storage_access + name = "Derelict Storage Access" + +/area/derelict/storage/engine_storage + name = "Derelict Engine Storage" + icon_state = "green" + +/area/derelict/bridge + name = "\improper Derelict Control Room" + icon_state = "bridge" + +/area/derelict/secret + name = "\improper Derelict Secret Room" + icon_state = "library" + +/area/derelict/bridge/access + name = "Derelict Control Room Access" + icon_state = "auxstorage" + +/area/derelict/bridge/ai_upload + name = "\improper Derelict Computer Core" + icon_state = "ai" + +/area/derelict/solar_control + name = "\improper Derelict Solar Control" + icon_state = "engine" + +/area/derelict/crew_quarters + name = "\improper Derelict Crew Quarters" + icon_state = "fitness" + +/area/derelict/medical + name = "Derelict Medbay" + icon_state = "medbay" + +/area/derelict/medical/morgue + name = "\improper Derelict Morgue" + icon_state = "morgue" + +/area/derelict/medical/chapel + name = "\improper Derelict Chapel" + icon_state = "chapel" + +/area/derelict/teleporter + name = "\improper Derelict Teleporter" + icon_state = "teleporter" + +/area/derelict/eva + name = "Derelict EVA Storage" + icon_state = "eva" + +/area/derelict/ship + name = "\improper Abandoned Ship" + icon_state = "yellow" + +/area/solar/derelict_starboard + name = "\improper Derelict Starboard Solar Array" + icon_state = "panelsS" + +/area/solar/derelict_aft + name = "\improper Derelict Aft Solar Array" + icon_state = "aft" + +/area/derelict/singularity_engine + name = "\improper Derelict Singularity Engine" + icon_state = "engine" + +//HALF-BUILT STATION (REPLACES DERELICT IN BAYCODE, ABOVE IS LEFT FOR DOWNSTREAM) + +/area/shuttle/constructionsite + name = "\improper Construction Site Shuttle" + icon_state = "yellow" + dynamic_lighting = 0 + base_turf = /turf/simulated/mineral/floor/ignore_mapgen + +/area/shuttle/constructionsite/station + name = "\improper Construction Site Shuttle" + +/area/shuttle/constructionsite/site + name = "\improper Construction Site Shuttle" + +/area/constructionsite + name = "\improper Construction Site" + icon_state = "storage" + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/constructionsite/storage + name = "\improper Construction Site Storage Area" + +/area/constructionsite/science + name = "\improper Construction Site Research" + +/area/constructionsite/bridge + name = "\improper Construction Site Bridge" + icon_state = "bridge" + +/area/constructionsite/maintenance + name = "\improper Construction Site Maintenance" + icon_state = "yellow" + +/area/constructionsite/hallway/aft + name = "\improper Construction Site Aft Hallway" + icon_state = "hallP" + +/area/constructionsite/hallway/fore + name = "\improper Construction Site Fore Hallway" + icon_state = "hallS" + +/area/constructionsite/atmospherics + name = "\improper Construction Site Atmospherics" + icon_state = "green" + +/area/constructionsite/medical + name = "\improper Construction Site Medbay" + icon_state = "medbay" + +/area/constructionsite/ai + name = "\improper Construction Computer Core" + icon_state = "ai" + +/area/constructionsite/engineering + name = "\improper Construction Site Engine Bay" + icon_state = "engine" + +/area/solar/constructionsite + name = "\improper Construction Site Solars" + icon_state = "aft" + +/area/constructionsite/teleporter + name = "Construction Site Teleporter" + icon_state = "yellow" + + +//area/constructionsite +// name = "\improper Construction Site Shuttle" + +//area/constructionsite +// name = "\improper Construction Site Shuttle" + + +//Construction + +/area/construction + name = "\improper Engineering Construction Area" + icon_state = "yellow" + +/area/construction/supplyshuttle + name = "\improper Supply Shuttle" + icon_state = "yellow" + +/area/construction/quarters + name = "\improper Engineer's Quarters" + icon_state = "yellow" + +/area/construction/qmaint + name = "Maintenance" + icon_state = "yellow" + +/area/construction/hallway + name = "\improper Hallway" + icon_state = "yellow" + +/area/construction/solars + name = "\improper Solar Panels" + icon_state = "yellow" + +/area/construction/solarscontrol + name = "\improper Solar Panel Control" + icon_state = "yellow" + +/area/construction/Storage + name = "Construction Site Storage" + icon_state = "yellow" + +//AI + +/area/ai_monitored/storage/eva + name = "EVA Storage" + icon_state = "eva" + +/area/ai_monitored/storage/secure + name = "Secure Storage" + icon_state = "storage" + ambience = AMBIENCE_HIGHSEC + +/area/ai_monitored/storage/emergency + name = "Emergency Storage" + icon_state = "storage" + +/area/ai_monitored/storage/emergency/eva + name = "Emergency EVA" + icon_state = "storage" + +/area/ai_upload + name = "\improper AI Upload Chamber" + icon_state = "ai_upload" + ambience = AMBIENCE_AI + +/area/ai_upload_foyer + name = "AI Upload Access" + icon_state = "ai_foyer" + sound_env = SMALL_ENCLOSED + ambience = AMBIENCE_AI + +/area/ai_server_room + name = "Messaging Server Room" + icon_state = "ai_server" + sound_env = SMALL_ENCLOSED + ambience = AMBIENCE_AI + +/area/ai + name = "\improper AI Chamber" + icon_state = "ai_chamber" + ambience = AMBIENCE_AI + +/area/ai_cyborg_station + name = "\improper Cyborg Station" + icon_state = "ai_cyborg" + sound_env = SMALL_ENCLOSED + ambience = AMBIENCE_AI + +/area/aisat + name = "\improper AI Satellite" + icon_state = "ai" + ambience = AMBIENCE_AI + +/area/aisat_interior + name = "\improper AI Satellite" + icon_state = "ai" + ambience = AMBIENCE_AI // The lack of inheritence hurts my soul. + +/area/AIsatextFP + name = "\improper AI Sat Ext" + icon_state = "storage" + luminosity = 1 + dynamic_lighting = 0 + ambience = AMBIENCE_AI + +/area/AIsatextFS + name = "\improper AI Sat Ext" + icon_state = "storage" + luminosity = 1 + dynamic_lighting = 0 + ambience = AMBIENCE_AI + +/area/AIsatextAS + name = "\improper AI Sat Ext" + icon_state = "storage" + luminosity = 1 + dynamic_lighting = 0 + ambience = AMBIENCE_AI + +/area/AIsatextAP + name = "\improper AI Sat Ext" + icon_state = "storage" + luminosity = 1 + dynamic_lighting = 0 + ambience = AMBIENCE_AI + +/area/NewAIMain + name = "\improper AI Main New" + icon_state = "storage" + ambience = AMBIENCE_AI + + + +//Misc + +/area/wreck + ambience = AMBIENCE_RUINS + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/wreck/ai + name = "\improper AI Chamber" + icon_state = "ai" + ambience = AMBIENCE_TECH_RUINS + +/area/wreck/main + name = "\improper Wreck" + icon_state = "storage" + +/area/wreck/engineering + name = "\improper Power Room" + icon_state = "engine" + ambience = AMBIENCE_TECH_RUINS + +/area/wreck/bridge + name = "\improper Bridge" + icon_state = "bridge" + ambience = AMBIENCE_TECH_RUINS + +/area/generic + name = "Unknown" + icon_state = "storage" + + + +// Telecommunications Satellite +/area/tcommsat/ + ambience = AMBIENCE_ENGINEERING + +/area/tcommsat/entrance + name = "\improper Telecomms Teleporter" + icon_state = "tcomsatentrance" + +/area/tcommsat/chamber + name = "\improper Telecomms Central Compartment" + icon_state = "tcomsatcham" + +/area/tcomsat + name = "\improper Telecomms Satellite" + icon_state = "tcomsatlob" + ambience = AMBIENCE_ENGINEERING + +/area/tcomfoyer + name = "\improper Telecomms Foyer" + icon_state = "tcomsatfoyer" + ambience = AMBIENCE_ENGINEERING + +/area/tcomwest + name = "\improper Telecommunications Satellite West Wing" + icon_state = "tcomsatwest" + ambience = AMBIENCE_ENGINEERING + +/area/tcomeast + name = "\improper Telecommunications Satellite East Wing" + icon_state = "tcomsateast" + ambience = AMBIENCE_ENGINEERING + +/area/tcommsat/computer + name = "\improper Telecomms Control Room" + icon_state = "tcomsatcomp" + +/area/tcommsat/lounge + name = "\improper Telecommunications Satellite Lounge" + icon_state = "tcomsatlounge" + +/area/tcommsat/powercontrol + name = "\improper Telecommunications Power Control" + icon_state = "tcomsatwest" + + +// Away Missions +/area/awaymission + name = "\improper Strange Location" + icon_state = "away" + ambience = AMBIENCE_FOREBODING + flags = AREA_FLAG_IS_NOT_PERSISTENT + +/area/awaymission/gateway + name = "\improper Gateway" + icon_state = "teleporter" + music = "signal" + +/area/awaymission/example + name = "\improper Strange Station" + icon_state = "away" + +/area/awaymission/wwmines + name = "\improper Wild West Mines" + icon_state = "away1" + luminosity = 1 + requires_power = 0 + +/area/awaymission/wwgov + name = "\improper Wild West Mansion" + icon_state = "away2" + luminosity = 1 + requires_power = 0 + +/area/awaymission/wwrefine + name = "\improper Wild West Refinery" + icon_state = "away3" + luminosity = 1 + requires_power = 0 + +/area/awaymission/wwvault + name = "\improper Wild West Vault" + icon_state = "away3" + luminosity = 0 + +/area/awaymission/wwvaultdoors + name = "\improper Wild West Vault Doors" // this is to keep the vault area being entirely lit because of requires_power + icon_state = "away2" + requires_power = 0 + luminosity = 0 + +/area/awaymission/desert + name = "Mars" + icon_state = "away" + +/area/awaymission/BMPship1 + name = "\improper Aft Block" + icon_state = "away1" + +/area/awaymission/BMPship2 + name = "\improper Midship Block" + icon_state = "away2" + +/area/awaymission/BMPship3 + name = "\improper Fore Block" + icon_state = "away3" + +/area/awaymission/spacebattle + name = "\improper Space Battle" + icon_state = "away" + requires_power = 0 + +/area/awaymission/spacebattle/cruiser + name = "\improper NanoTrasen Cruiser" + +/area/awaymission/spacebattle/syndicate1 + name = "\improper Syndicate Assault Ship 1" + +/area/awaymission/spacebattle/syndicate2 + name = "\improper Syndicate Assault Ship 2" + +/area/awaymission/spacebattle/syndicate3 + name = "\improper Syndicate Assault Ship 3" + +/area/awaymission/spacebattle/syndicate4 + name = "\improper Syndicate War Sphere 1" + +/area/awaymission/spacebattle/syndicate5 + name = "\improper Syndicate War Sphere 2" + +/area/awaymission/spacebattle/syndicate6 + name = "\improper Syndicate War Sphere 3" + +/area/awaymission/spacebattle/syndicate7 + name = "\improper Syndicate Fighter" + +/area/awaymission/spacebattle/secret + name = "\improper Hidden Chamber" + +/area/awaymission/listeningpost + name = "\improper Listening Post" + icon_state = "away" + requires_power = 0 + +/area/awaymission/beach + name = "Beach" + icon_state = "null" + luminosity = 1 + dynamic_lighting = 0 + requires_power = 0 + +///////////////////////////////////////////////////////////////////// +/* + Lists of areas to be used with is_type_in_list. + Used in gamemodes code at the moment. --rastaf0 +*/ + +// CENTCOM +var/list/centcom_areas = list ( + /area/centcom, + /area/shuttle/escape/centcom, + /area/shuttle/escape_pod1/centcom, + /area/shuttle/escape_pod2/centcom, + /area/shuttle/escape_pod3/centcom, + /area/shuttle/escape_pod5/centcom, + /area/shuttle/transport1/centcom, + /area/shuttle/administration/centcom, + /area/shuttle/specops/centcom, +) + +//SPACE STATION 13 +var/list/the_station_areas = list ( + /area/shuttle/arrival, + /area/shuttle/escape/station, + /area/shuttle/escape_pod1/station, + /area/shuttle/escape_pod2/station, + /area/shuttle/escape_pod3/station, + /area/shuttle/escape_pod5/station, + /area/shuttle/mining/station, + /area/shuttle/transport1/station, + // /area/shuttle/transport2/station, + /area/shuttle/prison/station, + /area/shuttle/administration/station, + /area/shuttle/specops/station, + /area/maintenance, + /area/hallway, + /area/bridge, + /area/crew_quarters, + /area/holodeck, + /area/mint, + /area/library, + /area/chapel, + /area/lawoffice, + /area/engineering, + /area/solar, + /area/assembly, + /area/teleporter, + /area/medical, + /area/security, + /area/quartermaster, + /area/janitor, + /area/hydroponics, + /area/rnd, + /area/storage, + /area/construction, + /area/ai_monitored/storage/eva, + /area/ai_monitored/storage/secure, + /area/ai_monitored/storage/emergency, + /area/ai_upload, + /area/ai_upload_foyer, + /area/ai +) + + + + +/area/beach + name = "Keelin's private beach" + icon_state = "yellow" + luminosity = 1 + dynamic_lighting = 0 requires_power = 0 \ No newline at end of file diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index 84f2c8eb4ea..7c95e83b0d0 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -1,26 +1,26 @@ -/area/ai_monitored - name = "AI Monitored Area" - var/obj/machinery/camera/motioncamera = null - - -/area/ai_monitored/New() - ..() - // locate and store the motioncamera - spawn (20) // spawn on a delay to let turfs/objs load - for (var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - return - return - -/area/ai_monitored/Entered(atom/movable/O) - ..() - if (ismob(O) && motioncamera) - motioncamera.newTarget(O) - -/area/ai_monitored/Exited(atom/movable/O) - if (ismob(O) && motioncamera) - motioncamera.lostTarget(O) - - +/area/ai_monitored + name = "AI Monitored Area" + var/obj/machinery/camera/motioncamera = null + + +/area/ai_monitored/New() + ..() + // locate and store the motioncamera + spawn (20) // spawn on a delay to let turfs/objs load + for (var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncamera = M + M.area_motion = src + return + return + +/area/ai_monitored/Entered(atom/movable/O) + ..() + if (ismob(O) && motioncamera) + motioncamera.newTarget(O) + +/area/ai_monitored/Exited(atom/movable/O) + if (ismob(O) && motioncamera) + motioncamera.lostTarget(O) + + diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index c13d67483a7..a370f614d68 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -1,556 +1,556 @@ -// Areas.dm - -/area - var/fire = null - var/atmos = 1 - var/atmosalm = 0 - var/poweralm = 1 - var/party = null - level = null - name = "Unknown" - icon = 'icons/turf/areas.dmi' - icon_state = "unknown" - plane = PLANE_LIGHTING_ABOVE //In case we color them - luminosity = 0 - mouse_opacity = 0 - var/lightswitch = 1 - - var/eject = null - - var/debug = 0 - var/requires_power = 1 - var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() - - // Power channel status - Is it currently energized? - var/power_equip = TRUE - var/power_light = TRUE - var/power_environ = TRUE - - // Oneoff power usage - Used once and cleared each power cycle - var/oneoff_equip = 0 - var/oneoff_light = 0 - var/oneoff_environ = 0 - - // Continuous "static" power usage - Do not update these directly! - var/static_equip = 0 - var/static_light = 0 - var/static_environ = 0 - - var/music = null - var/has_gravity = 1 - var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name. - var/obj/machinery/power/apc/apc = null - var/no_air = null -// var/list/lights // list of all lights on this area - var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area - var/list/all_arfgs = null //Similar, but a list of all arfgs adjacent to this area - var/firedoors_closed = 0 - var/arfgs_active = 0 - var/list/ambience = list() - var/list/forced_ambience = null - var/sound_env = STANDARD_STATION - var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf - var/forbid_events = FALSE // If true, random events will not start inside this area. - var/forbid_singulo = FALSE // If true singulo will not move in. - var/no_spoilers = FALSE // If true, makes it much more difficult to see what is inside an area with things like mesons. - var/soundproofed = FALSE // If true, blocks sounds from other areas and prevents hearers on other areas from hearing the sounds within. - -/area/Initialize() - . = ..() - luminosity = !(dynamic_lighting) - icon_state = "" - return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms. - -/area/LateInitialize() - if(!requires_power || !apc) - power_light = 0 - power_equip = 0 - power_environ = 0 - power_change() // all machines set to current power level, also updates lighting icon - if(no_spoilers) - set_spoiler_obfuscation(TRUE) - -// Changes the area of T to A. Do not do this manually. -// Area is expected to be a non-null instance. -/proc/ChangeArea(var/turf/T, var/area/A) - if(!istype(A)) - CRASH("Area change attempt failed: invalid area supplied.") - var/area/old_area = get_area(T) - if(old_area == A) - return - // NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s - // NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs. - A.contents.Add(T) - if(old_area) - // Handle dynamic lighting update if - if(SSlighting.subsystem_initialized && T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting) - if(A.dynamic_lighting) - T.lighting_build_overlay() - else - T.lighting_clear_overlay() - for(var/atom/movable/AM in T) - old_area.Exited(AM, A) - for(var/atom/movable/AM in T) - A.Entered(AM, old_area) - for(var/obj/machinery/M in T) - M.area_changed(old_area, A) - -/area/proc/get_contents() - return contents - -/area/proc/get_cameras() - var/list/cameras = list() - for (var/obj/machinery/camera/C in src) - cameras += C - return cameras - -/area/proc/atmosalert(danger_level, var/alarm_source) - if (danger_level == 0) - atmosphere_alarm.clearAlarm(src, alarm_source) - else - var/obj/machinery/alarm/atmosalarm = alarm_source //maybe other things can trigger these, who knows - if(istype(atmosalarm)) - atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level, hidden = atmosalarm.alarms_hidden) - else - atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) - - //Check all the alarms before lowering atmosalm. Raising is perfectly fine. - for (var/obj/machinery/alarm/AA in src) - if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level) - danger_level = max(danger_level, AA.danger_level) - - if(danger_level != atmosalm) - atmosalm = danger_level - //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise - if (danger_level < 1 || danger_level >= 2) - firedoors_update() - - for (var/obj/machinery/alarm/AA in src) - AA.update_icon() - - return 1 - return 0 - -// Either close or open firedoors and arfgs depending on current alert statuses -/area/proc/firedoors_update() - if(fire || party || atmosalm) - firedoors_close() - arfgs_activate() - // VOREStation Edit - Make the lights colored! - if(fire) - for(var/obj/machinery/light/L in src) - L.set_alert_fire() - else if(atmosalm) - for(var/obj/machinery/light/L in src) - L.set_alert_atmos() - // VOREStation Edit End - else - firedoors_open() - arfgs_deactivate() - // VOREStation Edit - Put the lights back! - for(var/obj/machinery/light/L in src) - L.reset_alert() - // VOREStation Edit End - -// Close all firedoors in the area -/area/proc/firedoors_close() - if(!firedoors_closed) - firedoors_closed = TRUE - if(!all_doors) - return - for(var/obj/machinery/door/firedoor/E in all_doors) - if(!E.blocked) - if(E.operating) - E.nextstate = FIREDOOR_CLOSED - else if(!E.density) - spawn(0) - E.close() - -// Open all firedoors in the area -/area/proc/firedoors_open() - if(firedoors_closed) - firedoors_closed = FALSE - if(!all_doors) - return - for(var/obj/machinery/door/firedoor/E in all_doors) - if(!E.blocked) - if(E.operating) - E.nextstate = FIREDOOR_OPEN - else if(E.density) - spawn(0) - E.open() - -// Activate all retention fields! -/area/proc/arfgs_activate() - if(!arfgs_active) - arfgs_active = TRUE - if(!all_arfgs) - return - for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) - E.generate_field() //don't need to check powered state like doors, the arfgs handles it on its end - E.wasactive = TRUE - -// Deactivate retention fields! -/area/proc/arfgs_deactivate() - if(arfgs_active) - arfgs_active = FALSE - if(!all_arfgs) - return - for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) - E.disable_field() - E.wasactive = FALSE - -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - update_icon() - firedoors_update() - -/area/proc/fire_reset() - if (fire) - fire = 0 //used for firedoor checks - update_icon() - firedoors_update() - -/area/proc/readyalert() - if(!eject) - eject = 1 - update_icon() - return - -/area/proc/readyreset() - if(eject) - eject = 0 - update_icon() - return - -/area/proc/partyalert() - if (!( party )) - party = 1 - update_icon() - firedoors_update() - return - -/area/proc/partyreset() - if (party) - party = 0 - update_icon() - firedoors_update() - return - -/area/update_icon() - if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc. - if(fire && !eject && !party) - icon_state = null // Let lights take care of it - /*else if(atmosalm && !fire && !eject && !party) - icon_state = "bluenew"*/ - else if(!fire && eject && !party) - icon_state = "red" - else if(party && !fire && !eject) - icon_state = "party" - else - icon_state = "blue-red" - else - // new lighting behaviour with obj lights - icon_state = null - - -/* -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -*/ - -/area/proc/powered(var/chan) // return true if the area has power to given channel - - if(!requires_power) - return 1 - if(always_unpowered) - return 0 - switch(chan) - if(EQUIP) - return power_equip - if(LIGHT) - return power_light - if(ENVIRON) - return power_environ - - return 0 - -// called when power status changes -/area/proc/power_change() - for(var/obj/machinery/M in src) // for each machine in the area - M.power_change() // reverify power status (to update icons etc.) - if (fire || eject || party) - update_icon() - -/area/proc/usage(var/chan, var/include_static = TRUE) - var/used = 0 - switch(chan) - if(LIGHT) - used += oneoff_light + (include_static * static_light) - if(EQUIP) - used += oneoff_equip + (include_static * static_equip) - if(ENVIRON) - used += oneoff_environ + (include_static * static_environ) - if(TOTAL) - used += oneoff_light + (include_static * static_light) - used += oneoff_equip + (include_static * static_equip) - used += oneoff_environ + (include_static * static_environ) - return used - -// Helper for APCs; will generally be called every tick. -/area/proc/clear_usage() - oneoff_equip = 0 - oneoff_light = 0 - oneoff_environ = 0 - -// Use this for a one-time power draw from the area, typically for non-machines. -/area/proc/use_power_oneoff(var/amount, var/chan) - switch(chan) - if(EQUIP) - oneoff_equip += amount - if(LIGHT) - oneoff_light += amount - if(ENVIRON) - oneoff_environ += amount - return amount - -// This is used by machines to properly update the area of power changes. -/area/proc/power_use_change(old_amount, new_amount, chan) - use_power_static(new_amount - old_amount, chan) // Simultaneously subtract old_amount and add new_amount. - -// Not a proc you want to use directly unless you know what you are doing; see use_power_oneoff above instead. -/area/proc/use_power_static(var/amount, var/chan) - switch(chan) - if(EQUIP) - static_equip += amount - if(LIGHT) - static_light += amount - if(ENVIRON) - static_environ += amount - -// This recomputes the continued power usage; can be used for testing or error recovery, but is not called every tick. -/area/proc/retally_power() - static_equip = 0 - static_light = 0 - static_environ = 0 - for(var/obj/machinery/M in src) - switch(M.power_channel) - if(EQUIP) - static_equip += M.get_power_usage() - if(LIGHT) - static_light += M.get_power_usage() - if(ENVIRON) - static_environ += M.get_power_usage() - -////////////////////////////////////////////////////////////////// - -/area/vv_get_dropdown() - . = ..() - VV_DROPDOWN_OPTION("check_static_power", "Check Static Power") - -/area/vv_do_topic(list/href_list) - . = ..() - IF_VV_OPTION("check_static_power") - if(!check_rights(R_DEBUG)) - return - src.check_static_power(usr) - href_list["datumrefresh"] = "\ref[src]" - -// Debugging proc to report if static power is correct or not. -/area/proc/check_static_power(var/user) - set name = "Check Static Power" - var/actual_static_equip = static_equip - var/actual_static_light = static_light - var/actual_static_environ = static_environ - retally_power() - if(user) - var/list/report = list("[src] ([type]) static power tally:") - report += "EQUIP: Actual: [actual_static_equip] Correct: [static_equip] Difference: [actual_static_equip - static_equip]" - report += "LIGHT: Actual: [actual_static_light] Correct: [static_light] Difference: [actual_static_light - static_light]" - report += "ENVIRON: Actual: [actual_static_environ] Correct: [static_environ] Difference: [actual_static_environ - static_environ]" - to_chat(user, report.Join("\n")) - return (actual_static_equip == static_equip && actual_static_light == static_light && actual_static_environ == static_environ) - -////////////////////////////////////////////////////////////////// - -var/list/mob/living/forced_ambiance_list = new - -/area/Entered(mob/M) - if(!istype(M) || !M.ckey) - return - - if(!isliving(M)) - M.lastarea = src - return - - var/mob/living/L = M - if(!L.lastarea) - L.lastarea = src - var/area/oldarea = L.lastarea - if((oldarea.has_gravity == 0) && (has_gravity == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together. - thunk(L) - L.update_floating( L.Check_Dense_Object() ) - - L.lastarea = src - L.lastareachange = world.time - play_ambience(L, initial = TRUE) - if(no_spoilers) - L.disable_spoiler_vision() - -/area/proc/play_ambience(var/mob/living/L, initial = TRUE) - // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance))) - return - - var/volume_mod = L.get_preference_volume_channel(VOLUME_CHANNEL_AMBIENCE) - - // If we previously were in an area with force-played ambiance, stop it. - if((L in forced_ambiance_list) && initial) - L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) - forced_ambiance_list -= L - - if(forced_ambience) - if(L in forced_ambiance_list) - return - if(forced_ambience.len) - forced_ambiance_list |= L - var/sound/chosen_ambiance = pick(forced_ambience) - if(!istype(chosen_ambiance)) - chosen_ambiance = sound(chosen_ambiance, repeat = 1, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE_FORCED) - chosen_ambiance.volume *= volume_mod - L << chosen_ambiance - else - L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) - else if(src.ambience && src.ambience.len) - var/ambience_odds = L?.client.prefs.ambience_chance - if(prob(ambience_odds) && (world.time >= L.client.time_last_ambience_played + 1 MINUTE)) - var/sound = pick(ambience) - L << sound(sound, repeat = 0, wait = 0, volume = 50 * volume_mod, channel = CHANNEL_AMBIENCE) - L.client.time_last_ambience_played = world.time - -/area/proc/gravitychange(var/gravitystate = 0) - src.has_gravity = gravitystate - - for(var/mob/M in src) - if(has_gravity) - thunk(M) - M.update_floating( M.Check_Dense_Object() ) - M.update_gravity(has_gravity) - -/area/proc/thunk(mob) - if(istype(get_turf(mob), /turf/space)) // Can't fall onto nothing. - return - - if(istype(mob,/mob/living/carbon/human/)) - var/mob/living/carbon/human/H = mob - if(H.buckled) - return // Being buckled to something solid keeps you in place. - if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP)) - return - if(H.incorporeal_move) // VOREstation edit - Phaseshifted beings should not be affected by gravity - return - if(H.species.can_zero_g_move || H.species.can_space_freemove) - return - - if(H.m_intent == "run") - H.AdjustStunned(6) - H.AdjustWeakened(6) - else - H.AdjustStunned(3) - H.AdjustWeakened(3) - to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") - playsound(mob, "bodyfall", 50, 1) - -/area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = TRUE) - var/obj/machinery/power/apc/theAPC = get_apc() - if(theAPC && theAPC.operating) - if(break_lights) - for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) - if(open_doors) - for(var/obj/machinery/door/airlock/temp_airlock in src) - temp_airlock.prison_open() - for(var/obj/machinery/door/window/temp_windoor in src) - temp_windoor.open() - if(open_blast_doors) - for(var/obj/machinery/door/blast/temp_blast in src) - temp_blast.open() - -/area/has_gravity() - return has_gravity - -/area/space/has_gravity() - return 0 - -/proc/has_gravity(atom/AT, turf/T) - if(!T) - T = get_turf(AT) - var/area/A = get_area(T) - if(A && A.has_gravity()) - return 1 - return 0 - -/area/proc/shuttle_arrived() - return TRUE - -/area/proc/shuttle_departed() - return TRUE - -/area/AllowDrop() - CRASH("Bad op: area/AllowDrop() called") - -/area/drop_location() - CRASH("Bad op: area/drop_location() called") - -/*Adding a wizard area teleport list because motherfucking lag -- Urist*/ -/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ -var/list/teleportlocs = list() - -/hook/startup/proc/setupTeleportLocs() - for(var/area/AR in world) - if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue - if(teleportlocs.Find(AR.name)) continue - var/turf/picked = pick(get_area_turfs(AR.type)) - if (picked.z in using_map.station_levels) - teleportlocs += AR.name - teleportlocs[AR.name] = AR - - teleportlocs = sortAssoc(teleportlocs) - - return 1 - -var/list/ghostteleportlocs = list() - -/hook/startup/proc/setupGhostTeleportLocs() - for(var/area/AR in world) - if(ghostteleportlocs.Find(AR.name)) continue - if(istype(AR, /area/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom)) - ghostteleportlocs += AR.name - ghostteleportlocs[AR.name] = AR - var/turf/picked = pick(get_area_turfs(AR.type)) - if (picked.z in using_map.player_levels) - ghostteleportlocs += AR.name - ghostteleportlocs[AR.name] = AR - - ghostteleportlocs = sortAssoc(ghostteleportlocs) - - return 1 - -/area/proc/get_name() - if(secret_name) - return "Unknown Area" - return name - -GLOBAL_DATUM(spoiler_obfuscation_image, /image) - -/area/proc/set_spoiler_obfuscation(should_obfuscate) - if(!GLOB.spoiler_obfuscation_image) - GLOB.spoiler_obfuscation_image = image(icon = 'icons/misc/static.dmi') - GLOB.spoiler_obfuscation_image.plane = PLANE_MESONS - - if(should_obfuscate) - add_overlay(GLOB.spoiler_obfuscation_image) - else +// Areas.dm + +/area + var/fire = null + var/atmos = 1 + var/atmosalm = 0 + var/poweralm = 1 + var/party = null + level = null + name = "Unknown" + icon = 'icons/turf/areas.dmi' + icon_state = "unknown" + plane = PLANE_LIGHTING_ABOVE //In case we color them + luminosity = 0 + mouse_opacity = 0 + var/lightswitch = 1 + + var/eject = null + + var/debug = 0 + var/requires_power = 1 + var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() + + // Power channel status - Is it currently energized? + var/power_equip = TRUE + var/power_light = TRUE + var/power_environ = TRUE + + // Oneoff power usage - Used once and cleared each power cycle + var/oneoff_equip = 0 + var/oneoff_light = 0 + var/oneoff_environ = 0 + + // Continuous "static" power usage - Do not update these directly! + var/static_equip = 0 + var/static_light = 0 + var/static_environ = 0 + + var/music = null + var/has_gravity = 1 + var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name. + var/obj/machinery/power/apc/apc = null + var/no_air = null +// var/list/lights // list of all lights on this area + var/list/all_doors = null //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area + var/list/all_arfgs = null //Similar, but a list of all arfgs adjacent to this area + var/firedoors_closed = 0 + var/arfgs_active = 0 + var/list/ambience = list() + var/list/forced_ambience = null + var/sound_env = STANDARD_STATION + var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf + var/forbid_events = FALSE // If true, random events will not start inside this area. + var/forbid_singulo = FALSE // If true singulo will not move in. + var/no_spoilers = FALSE // If true, makes it much more difficult to see what is inside an area with things like mesons. + var/soundproofed = FALSE // If true, blocks sounds from other areas and prevents hearers on other areas from hearing the sounds within. + +/area/Initialize() + . = ..() + luminosity = !(dynamic_lighting) + icon_state = "" + return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms. + +/area/LateInitialize() + if(!requires_power || !apc) + power_light = 0 + power_equip = 0 + power_environ = 0 + power_change() // all machines set to current power level, also updates lighting icon + if(no_spoilers) + set_spoiler_obfuscation(TRUE) + +// Changes the area of T to A. Do not do this manually. +// Area is expected to be a non-null instance. +/proc/ChangeArea(var/turf/T, var/area/A) + if(!istype(A)) + CRASH("Area change attempt failed: invalid area supplied.") + var/area/old_area = get_area(T) + if(old_area == A) + return + // NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s + // NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs. + A.contents.Add(T) + if(old_area) + // Handle dynamic lighting update if + if(SSlighting.subsystem_initialized && T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting) + if(A.dynamic_lighting) + T.lighting_build_overlay() + else + T.lighting_clear_overlay() + for(var/atom/movable/AM in T) + old_area.Exited(AM, A) + for(var/atom/movable/AM in T) + A.Entered(AM, old_area) + for(var/obj/machinery/M in T) + M.area_changed(old_area, A) + +/area/proc/get_contents() + return contents + +/area/proc/get_cameras() + var/list/cameras = list() + for (var/obj/machinery/camera/C in src) + cameras += C + return cameras + +/area/proc/atmosalert(danger_level, var/alarm_source) + if (danger_level == 0) + atmosphere_alarm.clearAlarm(src, alarm_source) + else + var/obj/machinery/alarm/atmosalarm = alarm_source //maybe other things can trigger these, who knows + if(istype(atmosalarm)) + atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level, hidden = atmosalarm.alarms_hidden) + else + atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) + + //Check all the alarms before lowering atmosalm. Raising is perfectly fine. + for (var/obj/machinery/alarm/AA in src) + if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level) + danger_level = max(danger_level, AA.danger_level) + + if(danger_level != atmosalm) + atmosalm = danger_level + //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise + if (danger_level < 1 || danger_level >= 2) + firedoors_update() + + for (var/obj/machinery/alarm/AA in src) + AA.update_icon() + + return 1 + return 0 + +// Either close or open firedoors and arfgs depending on current alert statuses +/area/proc/firedoors_update() + if(fire || party || atmosalm) + firedoors_close() + arfgs_activate() + // VOREStation Edit - Make the lights colored! + if(fire) + for(var/obj/machinery/light/L in src) + L.set_alert_fire() + else if(atmosalm) + for(var/obj/machinery/light/L in src) + L.set_alert_atmos() + // VOREStation Edit End + else + firedoors_open() + arfgs_deactivate() + // VOREStation Edit - Put the lights back! + for(var/obj/machinery/light/L in src) + L.reset_alert() + // VOREStation Edit End + +// Close all firedoors in the area +/area/proc/firedoors_close() + if(!firedoors_closed) + firedoors_closed = TRUE + if(!all_doors) + return + for(var/obj/machinery/door/firedoor/E in all_doors) + if(!E.blocked) + if(E.operating) + E.nextstate = FIREDOOR_CLOSED + else if(!E.density) + spawn(0) + E.close() + +// Open all firedoors in the area +/area/proc/firedoors_open() + if(firedoors_closed) + firedoors_closed = FALSE + if(!all_doors) + return + for(var/obj/machinery/door/firedoor/E in all_doors) + if(!E.blocked) + if(E.operating) + E.nextstate = FIREDOOR_OPEN + else if(E.density) + spawn(0) + E.open() + +// Activate all retention fields! +/area/proc/arfgs_activate() + if(!arfgs_active) + arfgs_active = TRUE + if(!all_arfgs) + return + for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) + E.generate_field() //don't need to check powered state like doors, the arfgs handles it on its end + E.wasactive = TRUE + +// Deactivate retention fields! +/area/proc/arfgs_deactivate() + if(arfgs_active) + arfgs_active = FALSE + if(!all_arfgs) + return + for(var/obj/machinery/atmospheric_field_generator/E in all_arfgs) + E.disable_field() + E.wasactive = FALSE + +/area/proc/fire_alert() + if(!fire) + fire = 1 //used for firedoor checks + update_icon() + firedoors_update() + +/area/proc/fire_reset() + if (fire) + fire = 0 //used for firedoor checks + update_icon() + firedoors_update() + +/area/proc/readyalert() + if(!eject) + eject = 1 + update_icon() + return + +/area/proc/readyreset() + if(eject) + eject = 0 + update_icon() + return + +/area/proc/partyalert() + if (!( party )) + party = 1 + update_icon() + firedoors_update() + return + +/area/proc/partyreset() + if (party) + party = 0 + update_icon() + firedoors_update() + return + +/area/update_icon() + if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc. + if(fire && !eject && !party) + icon_state = null // Let lights take care of it + /*else if(atmosalm && !fire && !eject && !party) + icon_state = "bluenew"*/ + else if(!fire && eject && !party) + icon_state = "red" + else if(party && !fire && !eject) + icon_state = "party" + else + icon_state = "blue-red" + else + // new lighting behaviour with obj lights + icon_state = null + + +/* +#define EQUIP 1 +#define LIGHT 2 +#define ENVIRON 3 +*/ + +/area/proc/powered(var/chan) // return true if the area has power to given channel + + if(!requires_power) + return 1 + if(always_unpowered) + return 0 + switch(chan) + if(EQUIP) + return power_equip + if(LIGHT) + return power_light + if(ENVIRON) + return power_environ + + return 0 + +// called when power status changes +/area/proc/power_change() + for(var/obj/machinery/M in src) // for each machine in the area + M.power_change() // reverify power status (to update icons etc.) + if (fire || eject || party) + update_icon() + +/area/proc/usage(var/chan, var/include_static = TRUE) + var/used = 0 + switch(chan) + if(LIGHT) + used += oneoff_light + (include_static * static_light) + if(EQUIP) + used += oneoff_equip + (include_static * static_equip) + if(ENVIRON) + used += oneoff_environ + (include_static * static_environ) + if(TOTAL) + used += oneoff_light + (include_static * static_light) + used += oneoff_equip + (include_static * static_equip) + used += oneoff_environ + (include_static * static_environ) + return used + +// Helper for APCs; will generally be called every tick. +/area/proc/clear_usage() + oneoff_equip = 0 + oneoff_light = 0 + oneoff_environ = 0 + +// Use this for a one-time power draw from the area, typically for non-machines. +/area/proc/use_power_oneoff(var/amount, var/chan) + switch(chan) + if(EQUIP) + oneoff_equip += amount + if(LIGHT) + oneoff_light += amount + if(ENVIRON) + oneoff_environ += amount + return amount + +// This is used by machines to properly update the area of power changes. +/area/proc/power_use_change(old_amount, new_amount, chan) + use_power_static(new_amount - old_amount, chan) // Simultaneously subtract old_amount and add new_amount. + +// Not a proc you want to use directly unless you know what you are doing; see use_power_oneoff above instead. +/area/proc/use_power_static(var/amount, var/chan) + switch(chan) + if(EQUIP) + static_equip += amount + if(LIGHT) + static_light += amount + if(ENVIRON) + static_environ += amount + +// This recomputes the continued power usage; can be used for testing or error recovery, but is not called every tick. +/area/proc/retally_power() + static_equip = 0 + static_light = 0 + static_environ = 0 + for(var/obj/machinery/M in src) + switch(M.power_channel) + if(EQUIP) + static_equip += M.get_power_usage() + if(LIGHT) + static_light += M.get_power_usage() + if(ENVIRON) + static_environ += M.get_power_usage() + +////////////////////////////////////////////////////////////////// + +/area/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("check_static_power", "Check Static Power") + +/area/vv_do_topic(list/href_list) + . = ..() + IF_VV_OPTION("check_static_power") + if(!check_rights(R_DEBUG)) + return + src.check_static_power(usr) + href_list["datumrefresh"] = "\ref[src]" + +// Debugging proc to report if static power is correct or not. +/area/proc/check_static_power(var/user) + set name = "Check Static Power" + var/actual_static_equip = static_equip + var/actual_static_light = static_light + var/actual_static_environ = static_environ + retally_power() + if(user) + var/list/report = list("[src] ([type]) static power tally:") + report += "EQUIP: Actual: [actual_static_equip] Correct: [static_equip] Difference: [actual_static_equip - static_equip]" + report += "LIGHT: Actual: [actual_static_light] Correct: [static_light] Difference: [actual_static_light - static_light]" + report += "ENVIRON: Actual: [actual_static_environ] Correct: [static_environ] Difference: [actual_static_environ - static_environ]" + to_chat(user, report.Join("\n")) + return (actual_static_equip == static_equip && actual_static_light == static_light && actual_static_environ == static_environ) + +////////////////////////////////////////////////////////////////// + +var/list/mob/living/forced_ambiance_list = new + +/area/Entered(mob/M) + if(!istype(M) || !M.ckey) + return + + if(!isliving(M)) + M.lastarea = src + return + + var/mob/living/L = M + if(!L.lastarea) + L.lastarea = src + var/area/oldarea = L.lastarea + if((oldarea.has_gravity == 0) && (has_gravity == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together. + thunk(L) + L.update_floating( L.Check_Dense_Object() ) + + L.lastarea = src + L.lastareachange = world.time + play_ambience(L, initial = TRUE) + if(no_spoilers) + L.disable_spoiler_vision() + +/area/proc/play_ambience(var/mob/living/L, initial = TRUE) + // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch + if(!(L && L.is_preference_enabled(/datum/client_preference/play_ambiance))) + return + + var/volume_mod = L.get_preference_volume_channel(VOLUME_CHANNEL_AMBIENCE) + + // If we previously were in an area with force-played ambiance, stop it. + if((L in forced_ambiance_list) && initial) + L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) + forced_ambiance_list -= L + + if(forced_ambience) + if(L in forced_ambiance_list) + return + if(forced_ambience.len) + forced_ambiance_list |= L + var/sound/chosen_ambiance = pick(forced_ambience) + if(!istype(chosen_ambiance)) + chosen_ambiance = sound(chosen_ambiance, repeat = 1, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE_FORCED) + chosen_ambiance.volume *= volume_mod + L << chosen_ambiance + else + L << sound(null, channel = CHANNEL_AMBIENCE_FORCED) + else if(src.ambience && src.ambience.len) + var/ambience_odds = L?.client.prefs.ambience_chance + if(prob(ambience_odds) && (world.time >= L.client.time_last_ambience_played + 1 MINUTE)) + var/sound = pick(ambience) + L << sound(sound, repeat = 0, wait = 0, volume = 50 * volume_mod, channel = CHANNEL_AMBIENCE) + L.client.time_last_ambience_played = world.time + +/area/proc/gravitychange(var/gravitystate = 0) + src.has_gravity = gravitystate + + for(var/mob/M in src) + if(has_gravity) + thunk(M) + M.update_floating( M.Check_Dense_Object() ) + M.update_gravity(has_gravity) + +/area/proc/thunk(mob) + if(istype(get_turf(mob), /turf/space)) // Can't fall onto nothing. + return + + if(istype(mob,/mob/living/carbon/human/)) + var/mob/living/carbon/human/H = mob + if(H.buckled) + return // Being buckled to something solid keeps you in place. + if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP)) + return + if(H.incorporeal_move) // VOREstation edit - Phaseshifted beings should not be affected by gravity + return + if(H.species.can_zero_g_move || H.species.can_space_freemove) + return + + if(H.m_intent == "run") + H.AdjustStunned(6) + H.AdjustWeakened(6) + else + H.AdjustStunned(3) + H.AdjustWeakened(3) + to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") + playsound(mob, "bodyfall", 50, 1) + +/area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = TRUE) + var/obj/machinery/power/apc/theAPC = get_apc() + if(theAPC && theAPC.operating) + if(break_lights) + for(var/obj/machinery/power/apc/temp_apc in src) + temp_apc.overload_lighting(70) + if(open_doors) + for(var/obj/machinery/door/airlock/temp_airlock in src) + temp_airlock.prison_open() + for(var/obj/machinery/door/window/temp_windoor in src) + temp_windoor.open() + if(open_blast_doors) + for(var/obj/machinery/door/blast/temp_blast in src) + temp_blast.open() + +/area/has_gravity() + return has_gravity + +/area/space/has_gravity() + return 0 + +/proc/has_gravity(atom/AT, turf/T) + if(!T) + T = get_turf(AT) + var/area/A = get_area(T) + if(A && A.has_gravity()) + return 1 + return 0 + +/area/proc/shuttle_arrived() + return TRUE + +/area/proc/shuttle_departed() + return TRUE + +/area/AllowDrop() + CRASH("Bad op: area/AllowDrop() called") + +/area/drop_location() + CRASH("Bad op: area/drop_location() called") + +/*Adding a wizard area teleport list because motherfucking lag -- Urist*/ +/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ +var/list/teleportlocs = list() + +/hook/startup/proc/setupTeleportLocs() + for(var/area/AR in world) + if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue + if(teleportlocs.Find(AR.name)) continue + var/turf/picked = pick(get_area_turfs(AR.type)) + if (picked.z in using_map.station_levels) + teleportlocs += AR.name + teleportlocs[AR.name] = AR + + teleportlocs = sortAssoc(teleportlocs) + + return 1 + +var/list/ghostteleportlocs = list() + +/hook/startup/proc/setupGhostTeleportLocs() + for(var/area/AR in world) + if(ghostteleportlocs.Find(AR.name)) continue + if(istype(AR, /area/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom)) + ghostteleportlocs += AR.name + ghostteleportlocs[AR.name] = AR + var/turf/picked = pick(get_area_turfs(AR.type)) + if (picked.z in using_map.player_levels) + ghostteleportlocs += AR.name + ghostteleportlocs[AR.name] = AR + + ghostteleportlocs = sortAssoc(ghostteleportlocs) + + return 1 + +/area/proc/get_name() + if(secret_name) + return "Unknown Area" + return name + +GLOBAL_DATUM(spoiler_obfuscation_image, /image) + +/area/proc/set_spoiler_obfuscation(should_obfuscate) + if(!GLOB.spoiler_obfuscation_image) + GLOB.spoiler_obfuscation_image = image(icon = 'icons/misc/static.dmi') + GLOB.spoiler_obfuscation_image.plane = PLANE_MESONS + + if(should_obfuscate) + add_overlay(GLOB.spoiler_obfuscation_image) + else cut_overlay(GLOB.spoiler_obfuscation_image) \ No newline at end of file diff --git a/code/game/area/areas_vr.dm b/code/game/area/areas_vr.dm index 8d7b54bdf45..b2b67b089fd 100644 --- a/code/game/area/areas_vr.dm +++ b/code/game/area/areas_vr.dm @@ -2,7 +2,8 @@ var/enter_message var/exit_message var/limit_mob_size = TRUE //If mob size is limited in the area. - var/block_suit_sensors = FALSE //If mob size is limited in the area. + var/block_suit_sensors = FALSE //If suit sensors are blocked in the area. + var/block_tracking = FALSE //If camera tracking is blocked in the area. var/turf/ceiling_type // Size of the area in open turfs, only calculated for indoors areas. diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 9f865aced66..9490a0af464 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,733 +1,733 @@ -/atom - layer = TURF_LAYER //This was here when I got here. Why though? - var/level = 2 - var/flags = 0 - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - var/was_bloodied - var/blood_color - var/pass_flags = 0 - var/throwpass = 0 - var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. - var/simulated = TRUE //filter for actions - used by lighting overlays - var/atom_say_verb = "says" - var/bubble_icon = "normal" ///what icon the atom uses for speechbubbles - var/fluorescent // Shows up under a UV light. - - var/last_bumped = 0 - - ///Chemistry. - var/datum/reagents/reagents = null - - //var/chem_is_open_container = 0 - // replaced by OPENCONTAINER flags and atom/proc/is_open_container() - ///Chemistry. - - // Overlays - ///Our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate - var/list/our_overlays - ///Overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/priority_overlays - ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays - var/list/managed_vis_overlays - - ///Our local copy of filter data so we can add/remove it - var/list/filter_data - - //Detective Work, used for the duplicate data points kept in the scanners - var/list/original_atom - // Track if we are already had initialize() called to prevent double-initialization. - var/initialized = FALSE - - /// Last name used to calculate a color for the chatmessage overlays - var/chat_color_name - /// Last color calculated for the the chatmessage overlays - var/chat_color - /// A luminescence-shifted value of the last color calculated for chatmessage overlays - var/chat_color_darkened - /// The chat color var, without alpha. - var/chat_color_hover - -/atom/New(loc, ...) - // Don't call ..() unless /datum/New() ever exists - - // During dynamic mapload (reader.dm) this assigns the var overrides from the .dmm file - // Native BYOND maploading sets those vars before invoking New(), by doing this FIRST we come as close to that behavior as we can. - if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() - GLOB._preloader.load(src) - - // Pass our arguments to InitAtom so they can be passed to initialize(), but replace 1st with if-we're-during-mapload. - var/do_initialize = SSatoms.initialized - if(do_initialize > INITIALIZATION_INSSATOMS) - args[1] = (do_initialize == INITIALIZATION_INNEW_MAPLOAD) - if(SSatoms.InitAtom(src, args)) - // We were deleted. No sense continuing - return - - // Uncomment if anything ever uses the return value of SSatoms.InitializeAtoms ~Leshana - // If a map is being loaded, it might want to know about newly created objects so they can be handled. - // var/list/created = SSatoms.created_atoms - // if(created) - // created += src - -// Note: I removed "auto_init" feature (letting types disable auto-init) since it shouldn't be needed anymore. -// You can replicate the same by checking the value of the first parameter to initialize() ~Leshana - -// Called after New if the map is being loaded, with mapload = TRUE -// Called from base of New if the map is not being loaded, with mapload = FALSE -// This base must be called or derivatives must set initialized to TRUE -// Must not sleep! -// Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -// Must return an Initialize hint. Defined in code/__defines/subsystems.dm -/atom/proc/Initialize(mapload, ...) - if(QDELETED(src)) - stack_trace("GC: -- [type] had initialize() called after qdel() --") - if(initialized) - stack_trace("Warning: [src]([type]) initialized multiple times!") - initialized = TRUE - return INITIALIZE_HINT_NORMAL - -/atom/Destroy() - if(reagents) - QDEL_NULL(reagents) - if(light) - QDEL_NULL(light) - return ..() - -// Called after all object's normal initialize() if initialize() returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -/atom/proc/reveal_blood() - return - -/atom/proc/assume_air(datum/gas_mixture/giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -//return flags that should be added to the viewer's sight var. -//Otherwise return a negative number to indicate that the view should be cancelled. -/atom/proc/check_eye(user as mob) - if (istype(user, /mob/living/silicon/ai)) // WHYYYY - return 0 - return -1 - -/atom/proc/Bumped(AM as mob|obj) - set waitfor = FALSE - - SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, AM) - -// Convenience proc to see if a container is open for chemistry handling -// returns true if open -// false if closed -/atom/proc/is_open_container() - return flags & OPENCONTAINER - -/*//Convenience proc to see whether a container can be accessed in a certain way. - - proc/can_subract_container() - return flags & EXTRACT_CONTAINER - - proc/can_add_container() - return flags & INSERT_CONTAINER -*/ - -// Used to be for the PROXMOVE flag, but that was terrible, so instead it's just here as a stub for -// all the atoms that still have the proc, but get events other ways. -/atom/proc/HasProximity(turf/T, atom/movable/AM, old_loc) - return - -//Register listeners on turfs in a certain range -/atom/proc/sense_proximity(var/range = 1, var/callback) - ASSERT(callback) - ASSERT(isturf(loc)) - var/list/turfs = trange(range, src) - for(var/turf/T as anything in turfs) - GLOB.turf_entered_event.register(T, src, callback) - -//Unregister from prox listening in a certain range. You should do this BEFORE you move, but if you -// really can't, then you can set the center where you moved from. -/atom/proc/unsense_proximity(var/range = 1, var/callback, var/center) - ASSERT(isturf(center) || isturf(loc)) - var/list/turfs = trange(range, center ? center : src) - for(var/turf/T as anything in turfs) - GLOB.turf_entered_event.unregister(T, src, callback) - - -/atom/proc/emp_act(var/severity) - return - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - if(SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) & COMPONENT_CANCEL_ATTACK_CHAIN) - return - - P.on_hit(src, 0, def_zone) - . = 0 - -// Called when a blob expands onto the tile the atom occupies. -/atom/proc/blob_act() - return - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return 1 - else if(src in container) - return 1 - return - -/* - * atom/proc/search_contents_for(path,list/filter_path=null) - * Recursevly searches all atom contens (including contents contents and so on). - * - * ARGS: path - search atom contents for atoms of this type - * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. - * - * RETURNS: list of found atoms - */ - -/atom/proc/search_contents_for(path,list/filter_path=null) - var/list/found = list() - for(var/atom/A in src) - if(istype(A, path)) - found += A - if(filter_path) - var/pass = 0 - for(var/type in filter_path) - pass |= istype(A, type) - if(!pass) - continue - if(A.contents.len) - found += A.search_contents_for(path,filter_path) - return found - -/atom/proc/get_examine_desc() - return desc - -//All atoms -/atom/proc/examine(mob/user, var/infix = "", var/suffix = "") - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src][infix]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - if(blood_color != SYNTH_BLOOD_COLOUR) - f_name += "blood-stained [name][infix]!" - else - f_name += "oil-stained [name][infix]." - - var/list/output = list("\icon[src.examine_icon()][bicon(src)] That's [f_name] [suffix]", get_examine_desc()) - - if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_INCLUDE_USAGE) - output += description_info - - if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_SWITCH_TO_PANEL) - user.client.statpanel = "Examine" // Switch to stat panel - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, output) - return output - -// Don't make these call bicon or anything, these are what bicon uses. They need to return an icon. -/atom/proc/examine_icon() - return icon(icon=src.icon, icon_state=src.icon_state, dir=SOUTH, frame=1, moving=0) - -// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set. -// see code/modules/mob/mob_movement.dm for more. -/atom/proc/relaymove() - return - -//called to set the atom's dir and used to add behaviour to dir-changes -/atom/proc/set_dir(new_dir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, new_dir) - . = new_dir != dir - dir = new_dir - -// Called to set the atom's density and used to add behavior to density changes. -/atom/proc/set_density(var/new_density) - if(density == new_density) - return FALSE - density = !!new_density // Sanitize to be strictly 0 or 1 - return TRUE - -// Called to set the atom's invisibility and usd to add behavior to invisibility changes. -/atom/proc/set_invisibility(var/new_invisibility) - if(invisibility == new_invisibility) - return FALSE - invisibility = new_invisibility - return TRUE - -/atom/proc/ex_act(var/strength = 3) - return (SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, strength, src) & COMPONENT_IGNORE_EXPLOSION) - -/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source) - return -1 - -/atom/proc/fire_act() - return - - -// Returns an assoc list of RCD information. -// Example would be: list(RCD_VALUE_MODE = RCD_DECONSTRUCT, RCD_VALUE_DELAY = 50, RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4) -// This occurs before rcd_act() is called, and it won't be called if it returns FALSE. -/atom/proc/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return FALSE - -/atom/proc/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return - -/atom/proc/melt() - return - -// Previously this was defined both on /obj/ and /turf/ seperately. And that's bad. -/atom/proc/update_icon() - return - - -/atom/proc/hitby(atom/movable/AM as mob|obj) - if (density) - AM.throwing = 0 - return - -/atom/proc/add_hiddenprint(mob/living/M as mob) - if(isnull(M)) return - if(isnull(M.key)) return - if (ishuman(M)) - var/mob/living/carbon/human/H = M - if (!istype(H.dna, /datum/dna)) - return 0 - if (H.gloves) - if(src.fingerprintslast != H.key) - src.fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) - src.fingerprintslast = H.key - return 0 - if (!( src.fingerprints )) - if(src.fingerprintslast != H.key) - src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) - src.fingerprintslast = H.key - return 1 - else - if(src.fingerprintslast != M.key) - src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) - src.fingerprintslast = M.key - return - -/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) - if(isnull(M)) return - if(isAI(M)) return - if(isnull(M.key)) return - if (ishuman(M)) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - - //Fibers~ - add_fibers(M) - - //He has no prints! - if (mFingerprints in M.mutations) - if(fingerprintslast != M.key) - fingerprintshidden += "[time_stamp()]: [key_name(M)] (No fingerprints mutation)" - fingerprintslast = M.key - return 0 //Now, lets get to the dirty work. - //First, make sure their DNA makes sense. - var/mob/living/carbon/human/H = M - if (!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) - if(!istype(H.dna, /datum/dna)) - H.dna = new /datum/dna(null) - H.dna.real_name = H.real_name - H.check_dna() - - //Now, deal with gloves. - if (H.gloves && H.gloves != src) - if(fingerprintslast != H.key) - fingerprintshidden += "[time_stamp()]: [key_name(H)] (Wearing [H.gloves])" - fingerprintslast = H.key - H.gloves.add_fingerprint(M) - - //Deal with gloves the pass finger/palm prints. - if(!ignoregloves) - if(H.gloves && H.gloves != src) - if(istype(H.gloves, /obj/item/clothing/gloves)) - var/obj/item/clothing/gloves/G = H.gloves - if(!prob(G.fingerprint_chance)) - return 0 - - //More adminstuffz - if(fingerprintslast != H.key) - fingerprintshidden += "[time_stamp()]: [key_name(H)]" - fingerprintslast = H.key - - //Make the list if it does not exist. - if(!fingerprints) - fingerprints = list() - - //Hash this shit. - var/full_print = H.get_full_print() - - // Add the fingerprints - // - if(fingerprints[full_print]) - switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints. - - if(28 to 32) - if(prob(1)) - fingerprints[full_print] = full_print // You rolled a one buddy. - else - fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32 - - if(24 to 27) - if(prob(3)) - fingerprints[full_print] = full_print //Sucks to be you. - else - fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29 - - if(20 to 23) - if(prob(5)) - fingerprints[full_print] = full_print //Had a good run didn't ya. - else - fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25 - - if(16 to 19) - if(prob(5)) - fingerprints[full_print] = full_print //Welp. - else - fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21 - - if(0 to 15) - if(prob(5)) - fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge. - else - fingerprints[full_print] = full_print - - else - fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time. - - - return 1 - else - //Smudge up dem prints some - if(fingerprintslast != M.key) - fingerprintshidden += "[time_stamp()]: [key_name(M)]" - fingerprintslast = M.key - - //Cleaning up shit. - if(fingerprints && !fingerprints.len) - qdel(fingerprints) - return - - -/atom/proc/transfer_fingerprints_to(var/atom/A) - - if(!istype(A.fingerprints,/list)) - A.fingerprints = list() - - if(!istype(A.fingerprintshidden,/list)) - A.fingerprintshidden = list() - - if(!istype(fingerprintshidden, /list)) - fingerprintshidden = list() - - //skytodo - //A.fingerprints |= fingerprints //detective - //A.fingerprintshidden |= fingerprintshidden //admin - if(A.fingerprints && fingerprints) - A.fingerprints |= fingerprints.Copy() //detective - if(A.fingerprintshidden && fingerprintshidden) - A.fingerprintshidden |= fingerprintshidden.Copy() //admin A.fingerprintslast = fingerprintslast - - -//returns 1 if made bloody, returns 0 otherwise -/atom/proc/add_blood(mob/living/carbon/human/M as mob) - - if(flags & NOBLOODY) - return 0 - - if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. - blood_DNA = list() - - was_bloodied = TRUE - if(!blood_color) - blood_color = "#A10808" - if(istype(M)) - if (!istype(M.dna, /datum/dna)) - M.dna = new /datum/dna(null) - M.dna.real_name = M.real_name - M.check_dna() - blood_color = M.species.get_blood_colour(M) - . = 1 - return 1 - -/atom/proc/add_vomit_floor(mob/living/carbon/M as mob, var/toxvomit = 0) - if( istype(src, /turf/simulated) ) - var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) - this.virus2 = virus_copylist(M.virus2) - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - -/atom/proc/clean_blood() - if(!simulated) - return - fluorescent = 0 - src.germ_level = 0 - if(istype(blood_DNA, /list)) - blood_DNA = null - return TRUE - -/atom/proc/on_rag_wipe(var/obj/item/weapon/reagent_containers/glass/rag/R) - clean_blood() - R.reagents.splash(src, 1) - -/atom/proc/get_global_map_pos() - if(!islist(global_map) || isemptylist(global_map)) return - var/cur_x = null - var/cur_y = null - var/list/y_arr = null - for(cur_x=1,cur_x<=global_map.len,cur_x++) - y_arr = global_map[cur_x] - cur_y = y_arr.Find(src.z) - if(cur_y) - break -// to_world("X = [cur_x]; Y = [cur_y]") - if(cur_x && cur_y) - return list("x"=cur_x,"y"=cur_y) - else - return 0 - -/atom/proc/checkpass(passflag) - return (pass_flags&passflag) - -/atom/proc/isinspace() - if(istype(get_turf(src), /turf/space)) - return 1 - else - return 0 - -// Show a message to all mobs and objects in sight of this atom -// Use for objects performing visible actions -// message is output to anyone who can see, e.g. "The [src] does something!" -// blind_message (optional) is what blind people will hear e.g. "You hear something!" -/atom/proc/visible_message(var/message, var/blind_message, var/list/exclude_mobs, var/range = world.view, var/runemessage = "ðŸ‘") - - //VOREStation Edit - var/list/see - if(isbelly(loc)) - var/obj/belly/B = loc - see = B.get_mobs_and_objs_in_belly() - else - see = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE) - //VOREStation Edit End - - var/list/seeing_mobs = see["mobs"] - var/list/seeing_objs = see["objs"] - if(LAZYLEN(exclude_mobs)) - seeing_mobs -= exclude_mobs - - for(var/obj/O as anything in seeing_objs) - O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) - for(var/mob/M as anything in seeing_mobs) - if(M.see_invisible >= invisibility && MOB_CAN_SEE_PLANE(M, plane)) - M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) - if(runemessage != -1) - M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), audible = FALSE) - else if(blind_message) - M.show_message(blind_message, AUDIBLE_MESSAGE) - -// Show a message to all mobs and objects in earshot of this atom -// Use for objects performing audible actions -// message is the message output to anyone who can hear. -// deaf_message (optional) is what deaf people will see. -// hearing_distance (optional) is the range, how many tiles away the message can be heard. -/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/radio_message, var/runemessage) - - var/range = hearing_distance || world.view - var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE) - - var/list/hearing_mobs = hear["mobs"] - var/list/hearing_objs = hear["objs"] - - if(radio_message) - for(var/obj/O as anything in hearing_objs) - O.hear_talk(src, list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], radio_message)), null) - else - for(var/obj/O as anything in hearing_objs) - O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) - - for(var/mob/M as anything in hearing_mobs) - var/msg = message - M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) - if(runemessage != -1) - M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote")) - -/atom/movable/proc/dropInto(var/atom/destination) - while(istype(destination)) - var/atom/drop_destination = destination.onDropInto(src) - if(!istype(drop_destination) || drop_destination == destination) - return forceMove(destination) - destination = drop_destination - return moveToNullspace() - -/atom/proc/onDropInto(var/atom/movable/AM) - return // If onDropInto returns null, then dropInto will forceMove AM into us. - -/atom/movable/onDropInto(var/atom/movable/AM) - return loc // If onDropInto returns something, then dropInto will attempt to drop AM there. - -/atom/proc/InsertedContents() - return contents - -/atom/proc/has_gravity(turf/T) - if(!T || !isturf(T)) - T = get_turf(src) - if(istype(T, /turf/space)) // Turf never has gravity - return FALSE - var/area/A = get_area(T) - if(A && A.has_gravity()) - return TRUE - return FALSE - -/atom/proc/is_incorporeal() - return FALSE - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : L.drop_location() - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/get_nametag_name(mob/user) - return name - -/atom/proc/get_nametag_desc(mob/user) - return "" //Desc itself is often too long to use - -/atom/vv_get_dropdown() - . = ..() - VV_DROPDOWN_OPTION(VV_HK_ATOM_EXPLODE, "Explosion") - VV_DROPDOWN_OPTION(VV_HK_ATOM_EMP, "Emp Pulse") - -/atom/vv_do_topic(list/href_list) - . = ..() - IF_VV_OPTION(VV_HK_ATOM_EXPLODE) - if(!check_rights(R_DEBUG|R_FUN)) - return - usr.client.cmd_admin_explosion(src) - href_list["datumrefresh"] = "\ref[src]" - IF_VV_OPTION(VV_HK_ATOM_EMP) - if(!check_rights(R_DEBUG|R_FUN)) - return - usr.client.cmd_admin_emp(src) - href_list["datumrefresh"] = "\ref[src]" - -/atom/vv_get_header() - . = ..() - var/custom_edit_name - if(!isliving(src)) - custom_edit_name = "[src]" - . += {" - [custom_edit_name] -
    - << - [dir2text(dir)] - >> - - "} - var/turf/T = get_turf(src) - . += "
    [ADMIN_COORDJMP(T)]" - -/atom/vv_edit_var(var_name, var_value) - switch(var_name) - if(NAMEOF(src, light_range)) - if(light_system == STATIC_LIGHT) - set_light(l_range = var_value) - else - set_light_range(var_value) - . = TRUE - if(NAMEOF(src, light_power)) - if(light_system == STATIC_LIGHT) - set_light(l_power = var_value) - else - set_light_power(var_value) - . = TRUE - if(NAMEOF(src, light_color)) - if(light_system == STATIC_LIGHT) - set_light(l_color = var_value) - else - set_light_color(var_value) - . = TRUE - if(NAMEOF(src, light_on)) - set_light_on(var_value) - . = TRUE - if(NAMEOF(src, light_flags)) - set_light_flags(var_value) - . = TRUE - if(NAMEOF(src, opacity)) - set_opacity(var_value) - . = TRUE - - if(!isnull(.)) - datum_flags |= DF_VAR_EDITED - return - - . = ..() - -/atom/proc/atom_say(message) - if(!message) - return - var/list/speech_bubble_hearers = list() - for(var/mob/M in get_mobs_in_view(7, src)) - M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1) - if(M.client) - speech_bubble_hearers += M.client - - if(length(speech_bubble_hearers)) - var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), I, speech_bubble_hearers, 30) - -/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) - return - -/atom/Entered(atom/movable/AM, atom/old_loc) - . = ..() - GLOB.moved_event.raise_event(AM, old_loc, AM.loc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, old_loc) - SEND_SIGNAL(AM, COMSIG_ATOM_ENTERING, src, old_loc) - -/atom/Exit(atom/movable/AM, atom/new_loc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, new_loc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/new_loc) - . = ..() - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, new_loc) - -/atom/proc/get_visible_gender(mob/user, force) - return gender - -/atom/proc/interact(mob/user) - return - -// Purpose: Determines if the object can pass this atom. -// Called by: Movement. -// Inputs: The moving atom, target turf. -// Outputs: Boolean if can pass. -// Airflow and ZAS zones now uses CanZASPass() instead of this proc. -/atom/proc/CanPass(atom/movable/mover, turf/target) - return !density +/atom + layer = TURF_LAYER //This was here when I got here. Why though? + var/level = 2 + var/flags = 0 + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + var/was_bloodied + var/blood_color + var/pass_flags = 0 + var/throwpass = 0 + var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. + var/simulated = TRUE //filter for actions - used by lighting overlays + var/atom_say_verb = "says" + var/bubble_icon = "normal" ///what icon the atom uses for speechbubbles + var/fluorescent // Shows up under a UV light. + + var/last_bumped = 0 + + ///Chemistry. + var/datum/reagents/reagents = null + + //var/chem_is_open_container = 0 + // replaced by OPENCONTAINER flags and atom/proc/is_open_container() + ///Chemistry. + + // Overlays + ///Our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate + var/list/our_overlays + ///Overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/priority_overlays + ///vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays + var/list/managed_vis_overlays + + ///Our local copy of filter data so we can add/remove it + var/list/filter_data + + //Detective Work, used for the duplicate data points kept in the scanners + var/list/original_atom + // Track if we are already had initialize() called to prevent double-initialization. + var/initialized = FALSE + + /// Last name used to calculate a color for the chatmessage overlays + var/chat_color_name + /// Last color calculated for the the chatmessage overlays + var/chat_color + /// A luminescence-shifted value of the last color calculated for chatmessage overlays + var/chat_color_darkened + /// The chat color var, without alpha. + var/chat_color_hover + +/atom/New(loc, ...) + // Don't call ..() unless /datum/New() ever exists + + // During dynamic mapload (reader.dm) this assigns the var overrides from the .dmm file + // Native BYOND maploading sets those vars before invoking New(), by doing this FIRST we come as close to that behavior as we can. + if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() + GLOB._preloader.load(src) + + // Pass our arguments to InitAtom so they can be passed to initialize(), but replace 1st with if-we're-during-mapload. + var/do_initialize = SSatoms.initialized + if(do_initialize > INITIALIZATION_INSSATOMS) + args[1] = (do_initialize == INITIALIZATION_INNEW_MAPLOAD) + if(SSatoms.InitAtom(src, args)) + // We were deleted. No sense continuing + return + + // Uncomment if anything ever uses the return value of SSatoms.InitializeAtoms ~Leshana + // If a map is being loaded, it might want to know about newly created objects so they can be handled. + // var/list/created = SSatoms.created_atoms + // if(created) + // created += src + +// Note: I removed "auto_init" feature (letting types disable auto-init) since it shouldn't be needed anymore. +// You can replicate the same by checking the value of the first parameter to initialize() ~Leshana + +// Called after New if the map is being loaded, with mapload = TRUE +// Called from base of New if the map is not being loaded, with mapload = FALSE +// This base must be called or derivatives must set initialized to TRUE +// Must not sleep! +// Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +// Must return an Initialize hint. Defined in code/__defines/subsystems.dm +/atom/proc/Initialize(mapload, ...) + if(QDELETED(src)) + stack_trace("GC: -- [type] had initialize() called after qdel() --") + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + return INITIALIZE_HINT_NORMAL + +/atom/Destroy() + if(reagents) + QDEL_NULL(reagents) + if(light) + QDEL_NULL(light) + return ..() + +// Called after all object's normal initialize() if initialize() returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +/atom/proc/reveal_blood() + return + +/atom/proc/assume_air(datum/gas_mixture/giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +//return flags that should be added to the viewer's sight var. +//Otherwise return a negative number to indicate that the view should be cancelled. +/atom/proc/check_eye(user as mob) + if (istype(user, /mob/living/silicon/ai)) // WHYYYY + return 0 + return -1 + +/atom/proc/Bumped(AM as mob|obj) + set waitfor = FALSE + + SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, AM) + +// Convenience proc to see if a container is open for chemistry handling +// returns true if open +// false if closed +/atom/proc/is_open_container() + return flags & OPENCONTAINER + +/*//Convenience proc to see whether a container can be accessed in a certain way. + + proc/can_subract_container() + return flags & EXTRACT_CONTAINER + + proc/can_add_container() + return flags & INSERT_CONTAINER +*/ + +// Used to be for the PROXMOVE flag, but that was terrible, so instead it's just here as a stub for +// all the atoms that still have the proc, but get events other ways. +/atom/proc/HasProximity(turf/T, atom/movable/AM, old_loc) + return + +//Register listeners on turfs in a certain range +/atom/proc/sense_proximity(var/range = 1, var/callback) + ASSERT(callback) + ASSERT(isturf(loc)) + var/list/turfs = trange(range, src) + for(var/turf/T as anything in turfs) + GLOB.turf_entered_event.register(T, src, callback) + +//Unregister from prox listening in a certain range. You should do this BEFORE you move, but if you +// really can't, then you can set the center where you moved from. +/atom/proc/unsense_proximity(var/range = 1, var/callback, var/center) + ASSERT(isturf(center) || isturf(loc)) + var/list/turfs = trange(range, center ? center : src) + for(var/turf/T as anything in turfs) + GLOB.turf_entered_event.unregister(T, src, callback) + + +/atom/proc/emp_act(var/severity) + return + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + if(SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) & COMPONENT_CANCEL_ATTACK_CHAIN) + return + + P.on_hit(src, 0, def_zone) + . = 0 + +// Called when a blob expands onto the tile the atom occupies. +/atom/proc/blob_act() + return + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return 1 + else if(src in container) + return 1 + return + +/* + * atom/proc/search_contents_for(path,list/filter_path=null) + * Recursevly searches all atom contens (including contents contents and so on). + * + * ARGS: path - search atom contents for atoms of this type + * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. + * + * RETURNS: list of found atoms + */ + +/atom/proc/search_contents_for(path,list/filter_path=null) + var/list/found = list() + for(var/atom/A in src) + if(istype(A, path)) + found += A + if(filter_path) + var/pass = 0 + for(var/type in filter_path) + pass |= istype(A, type) + if(!pass) + continue + if(A.contents.len) + found += A.search_contents_for(path,filter_path) + return found + +/atom/proc/get_examine_desc() + return desc + +//All atoms +/atom/proc/examine(mob/user, var/infix = "", var/suffix = "") + //This reformat names to get a/an properly working on item descriptions when they are bloody + var/f_name = "\a [src][infix]." + if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + if(blood_color != SYNTH_BLOOD_COLOUR) + f_name += "blood-stained [name][infix]!" + else + f_name += "oil-stained [name][infix]." + + var/list/output = list("\icon[src.examine_icon()][bicon(src)] That's [f_name] [suffix]", get_examine_desc()) + + if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_INCLUDE_USAGE) + output += description_info + + if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_SWITCH_TO_PANEL) + user.client.statpanel = "Examine" // Switch to stat panel + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, output) + return output + +// Don't make these call bicon or anything, these are what bicon uses. They need to return an icon. +/atom/proc/examine_icon() + return icon(icon=src.icon, icon_state=src.icon_state, dir=SOUTH, frame=1, moving=0) + +// called by mobs when e.g. having the atom as their machine, pulledby, loc (AKA mob being inside the atom) or buckled var set. +// see code/modules/mob/mob_movement.dm for more. +/atom/proc/relaymove() + return + +//called to set the atom's dir and used to add behaviour to dir-changes +/atom/proc/set_dir(new_dir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, new_dir) + . = new_dir != dir + dir = new_dir + +// Called to set the atom's density and used to add behavior to density changes. +/atom/proc/set_density(var/new_density) + if(density == new_density) + return FALSE + density = !!new_density // Sanitize to be strictly 0 or 1 + return TRUE + +// Called to set the atom's invisibility and usd to add behavior to invisibility changes. +/atom/proc/set_invisibility(var/new_invisibility) + if(invisibility == new_invisibility) + return FALSE + invisibility = new_invisibility + return TRUE + +/atom/proc/ex_act(var/strength = 3) + return (SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, strength, src) & COMPONENT_IGNORE_EXPLOSION) + +/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source) + return -1 + +/atom/proc/fire_act() + return + + +// Returns an assoc list of RCD information. +// Example would be: list(RCD_VALUE_MODE = RCD_DECONSTRUCT, RCD_VALUE_DELAY = 50, RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4) +// This occurs before rcd_act() is called, and it won't be called if it returns FALSE. +/atom/proc/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return FALSE + +/atom/proc/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return + +/atom/proc/melt() + return + +// Previously this was defined both on /obj/ and /turf/ seperately. And that's bad. +/atom/proc/update_icon() + return + + +/atom/proc/hitby(atom/movable/AM as mob|obj) + if (density) + AM.throwing = 0 + return + +/atom/proc/add_hiddenprint(mob/living/M as mob) + if(isnull(M)) return + if(isnull(M.key)) return + if (ishuman(M)) + var/mob/living/carbon/human/H = M + if (!istype(H.dna, /datum/dna)) + return 0 + if (H.gloves) + if(src.fingerprintslast != H.key) + src.fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) + src.fingerprintslast = H.key + return 0 + if (!( src.fingerprints )) + if(src.fingerprintslast != H.key) + src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) + src.fingerprintslast = H.key + return 1 + else + if(src.fingerprintslast != M.key) + src.fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) + src.fingerprintslast = M.key + return + +/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) + if(isnull(M)) return + if(isAI(M)) return + if(isnull(M.key)) return + if (ishuman(M)) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + + //Fibers~ + add_fibers(M) + + //He has no prints! + if (mFingerprints in M.mutations) + if(fingerprintslast != M.key) + fingerprintshidden += "[time_stamp()]: [key_name(M)] (No fingerprints mutation)" + fingerprintslast = M.key + return 0 //Now, lets get to the dirty work. + //First, make sure their DNA makes sense. + var/mob/living/carbon/human/H = M + if (!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) + if(!istype(H.dna, /datum/dna)) + H.dna = new /datum/dna(null) + H.dna.real_name = H.real_name + H.check_dna() + + //Now, deal with gloves. + if (H.gloves && H.gloves != src) + if(fingerprintslast != H.key) + fingerprintshidden += "[time_stamp()]: [key_name(H)] (Wearing [H.gloves])" + fingerprintslast = H.key + H.gloves.add_fingerprint(M) + + //Deal with gloves the pass finger/palm prints. + if(!ignoregloves) + if(H.gloves && H.gloves != src) + if(istype(H.gloves, /obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = H.gloves + if(!prob(G.fingerprint_chance)) + return 0 + + //More adminstuffz + if(fingerprintslast != H.key) + fingerprintshidden += "[time_stamp()]: [key_name(H)]" + fingerprintslast = H.key + + //Make the list if it does not exist. + if(!fingerprints) + fingerprints = list() + + //Hash this shit. + var/full_print = H.get_full_print() + + // Add the fingerprints + // + if(fingerprints[full_print]) + switch(stringpercent(fingerprints[full_print])) //tells us how many stars are in the current prints. + + if(28 to 32) + if(prob(1)) + fingerprints[full_print] = full_print // You rolled a one buddy. + else + fingerprints[full_print] = stars(full_print, rand(0,40)) // 24 to 32 + + if(24 to 27) + if(prob(3)) + fingerprints[full_print] = full_print //Sucks to be you. + else + fingerprints[full_print] = stars(full_print, rand(15, 55)) // 20 to 29 + + if(20 to 23) + if(prob(5)) + fingerprints[full_print] = full_print //Had a good run didn't ya. + else + fingerprints[full_print] = stars(full_print, rand(30, 70)) // 15 to 25 + + if(16 to 19) + if(prob(5)) + fingerprints[full_print] = full_print //Welp. + else + fingerprints[full_print] = stars(full_print, rand(40, 100)) // 0 to 21 + + if(0 to 15) + if(prob(5)) + fingerprints[full_print] = stars(full_print, rand(0,50)) // small chance you can smudge. + else + fingerprints[full_print] = full_print + + else + fingerprints[full_print] = stars(full_print, rand(0, 20)) //Initial touch, not leaving much evidence the first time. + + + return 1 + else + //Smudge up dem prints some + if(fingerprintslast != M.key) + fingerprintshidden += "[time_stamp()]: [key_name(M)]" + fingerprintslast = M.key + + //Cleaning up shit. + if(fingerprints && !fingerprints.len) + qdel(fingerprints) + return + + +/atom/proc/transfer_fingerprints_to(var/atom/A) + + if(!istype(A.fingerprints,/list)) + A.fingerprints = list() + + if(!istype(A.fingerprintshidden,/list)) + A.fingerprintshidden = list() + + if(!istype(fingerprintshidden, /list)) + fingerprintshidden = list() + + //skytodo + //A.fingerprints |= fingerprints //detective + //A.fingerprintshidden |= fingerprintshidden //admin + if(A.fingerprints && fingerprints) + A.fingerprints |= fingerprints.Copy() //detective + if(A.fingerprintshidden && fingerprintshidden) + A.fingerprintshidden |= fingerprintshidden.Copy() //admin A.fingerprintslast = fingerprintslast + + +//returns 1 if made bloody, returns 0 otherwise +/atom/proc/add_blood(mob/living/carbon/human/M as mob) + + if(flags & NOBLOODY) + return 0 + + if(!blood_DNA || !istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. + blood_DNA = list() + + was_bloodied = TRUE + if(!blood_color) + blood_color = "#A10808" + if(istype(M)) + if (!istype(M.dna, /datum/dna)) + M.dna = new /datum/dna(null) + M.dna.real_name = M.real_name + M.check_dna() + blood_color = M.species.get_blood_colour(M) + . = 1 + return 1 + +/atom/proc/add_vomit_floor(mob/living/carbon/M as mob, var/toxvomit = 0) + if( istype(src, /turf/simulated) ) + var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) + this.virus2 = virus_copylist(M.virus2) + + // Make toxins vomit look different + if(toxvomit) + this.icon_state = "vomittox_[pick(1,4)]" + +/atom/proc/clean_blood() + if(!simulated) + return + fluorescent = 0 + src.germ_level = 0 + if(istype(blood_DNA, /list)) + blood_DNA = null + return TRUE + +/atom/proc/on_rag_wipe(var/obj/item/weapon/reagent_containers/glass/rag/R) + clean_blood() + R.reagents.splash(src, 1) + +/atom/proc/get_global_map_pos() + if(!islist(global_map) || isemptylist(global_map)) return + var/cur_x = null + var/cur_y = null + var/list/y_arr = null + for(cur_x=1,cur_x<=global_map.len,cur_x++) + y_arr = global_map[cur_x] + cur_y = y_arr.Find(src.z) + if(cur_y) + break +// to_world("X = [cur_x]; Y = [cur_y]") + if(cur_x && cur_y) + return list("x"=cur_x,"y"=cur_y) + else + return 0 + +/atom/proc/checkpass(passflag) + return (pass_flags&passflag) + +/atom/proc/isinspace() + if(istype(get_turf(src), /turf/space)) + return 1 + else + return 0 + +// Show a message to all mobs and objects in sight of this atom +// Use for objects performing visible actions +// message is output to anyone who can see, e.g. "The [src] does something!" +// blind_message (optional) is what blind people will hear e.g. "You hear something!" +/atom/proc/visible_message(var/message, var/blind_message, var/list/exclude_mobs, var/range = world.view, var/runemessage = "ðŸ‘") + + //VOREStation Edit + var/list/see + if(isbelly(loc)) + var/obj/belly/B = loc + see = B.get_mobs_and_objs_in_belly() + else + see = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE) + //VOREStation Edit End + + var/list/seeing_mobs = see["mobs"] + var/list/seeing_objs = see["objs"] + if(LAZYLEN(exclude_mobs)) + seeing_mobs -= exclude_mobs + + for(var/obj/O as anything in seeing_objs) + O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + for(var/mob/M as anything in seeing_mobs) + if(M.see_invisible >= invisibility && MOB_CAN_SEE_PLANE(M, plane)) + M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE) + if(runemessage != -1) + M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), audible = FALSE) + else if(blind_message) + M.show_message(blind_message, AUDIBLE_MESSAGE) + +// Show a message to all mobs and objects in earshot of this atom +// Use for objects performing audible actions +// message is the message output to anyone who can hear. +// deaf_message (optional) is what deaf people will see. +// hearing_distance (optional) is the range, how many tiles away the message can be heard. +/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/radio_message, var/runemessage) + + var/range = hearing_distance || world.view + var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE) + + var/list/hearing_mobs = hear["mobs"] + var/list/hearing_objs = hear["objs"] + + if(radio_message) + for(var/obj/O as anything in hearing_objs) + O.hear_talk(src, list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], radio_message)), null) + else + for(var/obj/O as anything in hearing_objs) + O.show_message(message, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + + for(var/mob/M as anything in hearing_mobs) + var/msg = message + M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE) + if(runemessage != -1) + M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote")) + +/atom/movable/proc/dropInto(var/atom/destination) + while(istype(destination)) + var/atom/drop_destination = destination.onDropInto(src) + if(!istype(drop_destination) || drop_destination == destination) + return forceMove(destination) + destination = drop_destination + return moveToNullspace() + +/atom/proc/onDropInto(var/atom/movable/AM) + return // If onDropInto returns null, then dropInto will forceMove AM into us. + +/atom/movable/onDropInto(var/atom/movable/AM) + return loc // If onDropInto returns something, then dropInto will attempt to drop AM there. + +/atom/proc/InsertedContents() + return contents + +/atom/proc/has_gravity(turf/T) + if(!T || !isturf(T)) + T = get_turf(src) + if(istype(T, /turf/space)) // Turf never has gravity + return FALSE + var/area/A = get_area(T) + if(A && A.has_gravity()) + return TRUE + return FALSE + +/atom/proc/is_incorporeal() + return FALSE + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : L.drop_location() + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/get_nametag_name(mob/user) + return name + +/atom/proc/get_nametag_desc(mob/user) + return "" //Desc itself is often too long to use + +/atom/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION(VV_HK_ATOM_EXPLODE, "Explosion") + VV_DROPDOWN_OPTION(VV_HK_ATOM_EMP, "Emp Pulse") + +/atom/vv_do_topic(list/href_list) + . = ..() + IF_VV_OPTION(VV_HK_ATOM_EXPLODE) + if(!check_rights(R_DEBUG|R_FUN)) + return + usr.client.cmd_admin_explosion(src) + href_list["datumrefresh"] = "\ref[src]" + IF_VV_OPTION(VV_HK_ATOM_EMP) + if(!check_rights(R_DEBUG|R_FUN)) + return + usr.client.cmd_admin_emp(src) + href_list["datumrefresh"] = "\ref[src]" + +/atom/vv_get_header() + . = ..() + var/custom_edit_name + if(!isliving(src)) + custom_edit_name = "[src]" + . += {" + [custom_edit_name] +
    + << + [dir2text(dir)] + >> + + "} + var/turf/T = get_turf(src) + . += "
    [ADMIN_COORDJMP(T)]" + +/atom/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, light_range)) + if(light_system == STATIC_LIGHT) + set_light(l_range = var_value) + else + set_light_range(var_value) + . = TRUE + if(NAMEOF(src, light_power)) + if(light_system == STATIC_LIGHT) + set_light(l_power = var_value) + else + set_light_power(var_value) + . = TRUE + if(NAMEOF(src, light_color)) + if(light_system == STATIC_LIGHT) + set_light(l_color = var_value) + else + set_light_color(var_value) + . = TRUE + if(NAMEOF(src, light_on)) + set_light_on(var_value) + . = TRUE + if(NAMEOF(src, light_flags)) + set_light_flags(var_value) + . = TRUE + if(NAMEOF(src, opacity)) + set_opacity(var_value) + . = TRUE + + if(!isnull(.)) + datum_flags |= DF_VAR_EDITED + return + + . = ..() + +/atom/proc/atom_say(message) + if(!message) + return + var/list/speech_bubble_hearers = list() + for(var/mob/M in get_mobs_in_view(7, src)) + M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1) + if(M.client) + speech_bubble_hearers += M.client + + if(length(speech_bubble_hearers)) + var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), I, speech_bubble_hearers, 30) + +/atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) + return + +/atom/Entered(atom/movable/AM, atom/old_loc) + . = ..() + GLOB.moved_event.raise_event(AM, old_loc, AM.loc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, old_loc) + SEND_SIGNAL(AM, COMSIG_ATOM_ENTERING, src, old_loc) + +/atom/Exit(atom/movable/AM, atom/new_loc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, new_loc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/new_loc) + . = ..() + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, new_loc) + +/atom/proc/get_visible_gender(mob/user, force) + return gender + +/atom/proc/interact(mob/user) + return + +// Purpose: Determines if the object can pass this atom. +// Called by: Movement. +// Inputs: The moving atom, target turf. +// Outputs: Boolean if can pass. +// Airflow and ZAS zones now uses CanZASPass() instead of this proc. +/atom/proc/CanPass(atom/movable/mover, turf/target) + return !density diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 40b4ea9d4ef..757283f00fa 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,640 +1,640 @@ -/atom/movable - layer = OBJ_LAYER - appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER|LONG_GLIDE - glide_size = 8 - var/last_move = null //The direction the atom last moved - var/anchored = FALSE - // var/elevation = 2 - not used anywhere - var/moving_diagonally - var/move_speed = 10 - var/l_move_time = 1 - var/throwing = 0 - var/thrower - var/turf/throw_source = null - var/throw_speed = 2 - var/throw_range = 7 - var/moved_recently = 0 - var/mob/pulledby = null - var/item_state = null // Used to specify the item state for the on-mob overlays. - var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform(). - var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform(). - var/icon_rotation = 0 // Used to rotate icons in update_transform() - var/icon_expected_height = 32 - var/icon_expected_width = 32 - var/old_x = 0 - var/old_y = 0 - var/datum/riding/riding_datum = null - var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) - var/movement_type = NONE - - var/cloaked = FALSE //If we're cloaked or not - var/image/cloaked_selfimage //The image we use for our client to let them see where we are - -/atom/movable/Initialize(mapload) - . = ..() - switch(blocks_emissive) - if(EMISSIVE_BLOCK_GENERIC) - var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = PLANE_EMISSIVE, alpha = src.alpha) - gen_emissive_blocker.color = GLOB.em_block_color - gen_emissive_blocker.dir = dir - gen_emissive_blocker.appearance_flags |= appearance_flags - add_overlay(list(gen_emissive_blocker), TRUE) - if(EMISSIVE_BLOCK_UNIQUE) - render_target = ref(src) - em_block = new(src, render_target) - add_overlay(list(em_block), TRUE) - if(opacity) - AddElement(/datum/element/light_blocking) - switch(light_system) - if(STATIC_LIGHT) - update_light() - if(MOVABLE_LIGHT) - AddComponent(/datum/component/overlay_lighting, starts_on = light_on) - if(MOVABLE_LIGHT_DIRECTIONAL) - AddComponent(/datum/component/overlay_lighting, is_directional = TRUE, starts_on = light_on) - -/atom/movable/Destroy() - . = ..() - for(var/atom/movable/AM in contents) - qdel(AM) - - if(opacity) - RemoveElement(/datum/element/light_blocking) - - moveToNullspace() - - vis_contents.Cut() - for(var/atom/movable/A as anything in vis_locs) - A.vis_contents -= src - - if(pulledby) - pulledby.stop_pulling() - - if(orbiting) - stop_orbit() - QDEL_NULL(riding_datum) //VOREStation Add - -/atom/movable/vv_edit_var(var_name, var_value) - if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly. - return FALSE - return ..() - -//////////////////////////////////////// -/atom/movable/Move(atom/newloc, direct = 0, movetime) - // Didn't pass enough info - if(!loc || !newloc) - return FALSE - - if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc, direct, movetime) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) - return FALSE - - // Store this early before we might move, it's used several places - var/atom/oldloc = loc - - // If we're not moving to the same spot (why? does that even happen?) - if(loc != newloc) - if(!direct) - direct = get_dir(oldloc, newloc) - if (IS_CARDINAL(direct)) //Cardinal move - // Track our failure if any in this value - . = TRUE - - // Face the direction of movement - set_dir(direct) - - // Check to make sure we can leave - if(!loc.Exit(src, newloc)) - . = FALSE - - // Check to make sure we can enter, if we haven't already failed - if(. && !newloc.Enter(src, src.loc)) - . = FALSE - - // Check to make sure if we're multi-tile we can move, if we haven't already failed - if(. && !check_multi_tile_move_density_dir(direct, locs)) - . = FALSE - - // Definitely moving if you enter this, no failures so far - if(. && locs.len <= 1) // We're not a multi-tile object. - var/area/oldarea = get_area(oldloc) - var/area/newarea = get_area(newloc) - var/old_z = get_z(oldloc) - var/dest_z = get_z(newloc) - - // Do The Move - if(movetime) - glide_for(movetime) // First attempt, lets let the diag do it. - loc = newloc - . = TRUE - - // So objects can be informed of z-level changes - if (old_z != dest_z) - onTransitZ(old_z, dest_z) - - // We don't call parent so we are calling this for byond - oldloc.Exited(src, newloc) - if(oldarea != newarea) - oldarea.Exited(src, newloc) - - // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself - for(var/atom/movable/thing as anything in oldloc) - // We don't call parent so we are calling this for byond - thing.Uncrossed(src) - - // We don't call parent so we are calling this for byond - newloc.Entered(src, oldloc) - if(oldarea != newarea) - newarea.Entered(src, oldloc) - - // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself - for(var/atom/movable/thing as anything in loc) - // We don't call parent so we are calling this for byond - thing.Crossed(src, oldloc) - - // We're a multi-tile object (multiple locs) - else if(. && newloc) - . = doMove(newloc) - - //Diagonal move, split it into cardinal moves - else - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a forceMove taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - glide_for(movetime * 2) - if (direct & NORTH) - if (direct & EAST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & WEST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & SOUTH) - if (direct & EAST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if (direct & WEST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - // If we failed, turn to face the direction of the first step at least - if(!. && moving_diagonally == SECOND_DIAG_STEP) - set_dir(first_step_dir) - // Done, regardless! - moving_diagonally = 0 - // We return because step above will call Move() and we don't want to do shenanigans back in here again - return - - else if(!loc || (loc == oldloc)) - last_move = 0 - return - - // If we moved, call Moved() on ourselves - if(.) - Moved(oldloc, direct, FALSE, movetime ? movetime : ( (TICKS2DS(WORLD_ICON_SIZE/glide_size)) * (moving_diagonally ? (0.5) : 1) ) ) - - // Update timers/cooldown stuff - move_speed = world.time - l_move_time - l_move_time = world.time - last_move = direct // The direction you last moved - // set_dir(direct) //Don't think this is necessary - -//Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/old_loc, direction, forced = FALSE, movetime) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction, forced, movetime) - // Handle any buckled mobs on this movable - if(has_buckled_mobs()) - handle_buckled_mob_movement(old_loc, direction, movetime) - if(riding_datum) - riding_datum.handle_vehicle_layer() - riding_datum.handle_vehicle_offsets() - for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. - light.source_atom.update_light() - - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction) - - return TRUE - -/atom/movable/set_dir(newdir) - . = ..(newdir) - if(riding_datum) - riding_datum.handle_vehicle_offsets() - -/atom/movable/relaymove(mob/user, direction) - . = ..() - if(riding_datum) - riding_datum.handle_ride(user, direction) - -// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly. -// You probably want CanPass() -/atom/movable/Cross(atom/movable/AM) - return CanPass(AM, loc) - -/atom/movable/CanPass(atom/movable/mover, turf/target) - . = ..() - if(locs && locs.len >= 2) // If something is standing on top of us, let them pass. - if(mover.loc in locs) - . = TRUE - return . - -/atom/movable/Bump(atom/A) - if(!A) - CRASH("Bump was called with no argument.") - . = ..() - if(throwing) - throw_impact(A) - throwing = 0 - if(QDELETED(A)) - return - - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) - - A.Bumped(src) - A.last_bumped = world.time - -/atom/movable/proc/forceMove(atom/destination, direction, movetime) - . = FALSE - if(destination) - . = doMove(destination, direction, movetime) - else - CRASH("No valid destination passed into forceMove") - -/atom/movable/proc/moveToNullspace() - return doMove(null) - -/atom/movable/proc/doMove(atom/destination, direction, movetime) - var/atom/oldloc = loc - var/area/old_area = get_area(oldloc) - var/same_loc = oldloc == destination - - if(destination) - var/area/destarea = get_area(destination) - - // Do The Move - glide_for(movetime) - last_move = isnull(direction) ? 0 : direction - loc = destination - - // Unset this in case it was set in some other proc. We're no longer moving diagonally for sure. - moving_diagonally = 0 - - // We are moving to a different loc - if(!same_loc) - // Not moving out of nullspace - if(oldloc) - oldloc.Exited(src, destination) - // If it's not the same area, Exited() it - if(old_area && old_area != destarea) - old_area.Exited(src, destination) - - // Uncross everything where we left - for(var/atom/movable/AM as anything in oldloc) - if(AM == src) - continue - AM.Uncrossed(src) - if(loc != destination) // Uncrossed() triggered a separate movement - return - - // Information about turf and z-levels for source and dest collected - var/turf/oldturf = get_turf(oldloc) - var/turf/destturf = get_turf(destination) - var/old_z = (oldturf ? oldturf.z : null) - var/dest_z = (destturf ? destturf.z : null) - - // So objects can be informed of z-level changes - if (old_z != dest_z) - onTransitZ(old_z, dest_z) - - // Destination atom Entered - destination.Entered(src, oldloc) - - // Entered() the new area if it's not the same area - if(destarea && old_area != destarea) - destarea.Entered(src, oldloc) - - // We ignore ourselves because if we're multi-tile we might be in both old and new locs - for(var/atom/movable/AM as anything in destination) - if(AM == src) - continue - AM.Crossed(src, oldloc) - if(loc != destination) // Crossed triggered a separate movement - return - - // Call our thingy to inform everyone we moved - Moved(oldloc, NONE, TRUE) - - // Break pulling if we are too far to pull now. - if(pulledby && (pulledby.z != src.z || get_dist(pulledby, src) > 1)) - pulledby.stop_pulling() - - // We moved - return TRUE - - //If no destination, move the atom into nullspace (don't do this unless you know what you're doing) - else if(oldloc) - loc = null - - // Uncross everything where we left (no multitile safety like above because we are definitely not still there) - for(var/atom/movable/AM as anything in oldloc) - AM.Uncrossed(src) - - // Exited() our loc and area - oldloc.Exited(src, null) - if(old_area) - old_area.Exited(src, null) - - // We moved - return TRUE - -/atom/movable/proc/onTransitZ(old_z,new_z) - GLOB.z_moved_event.raise_event(src, old_z, new_z) - SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z) - for(var/atom/movable/AM as anything in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care. - AM.onTransitZ(old_z,new_z) - -/atom/movable/proc/glide_for(movetime) - if(movetime) - glide_size = WORLD_ICON_SIZE/max(DS2TICKS(movetime), 1) - spawn(movetime) - glide_size = initial(glide_size) - else - glide_size = initial(glide_size) - -///////////////////////////////////////////////////////////////// - -//called when src is thrown into hit_atom -/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) - if(istype(hit_atom,/mob/living)) - var/mob/living/M = hit_atom - if(M.buckled == src) - return // Don't hit the thing we're buckled to. - M.hitby(src,speed) - - else if(isobj(hit_atom)) - var/obj/O = hit_atom - if(!O.anchored) - step(O, src.last_move) - O.hitby(src,speed) - - else if(isturf(hit_atom)) - src.throwing = 0 - var/turf/T = hit_atom - T.hitby(src,speed) - -//decided whether a movable atom being thrown can pass through the turf it is in. -/atom/movable/proc/hit_check(var/speed) - if(src.throwing) - for(var/atom/A in get_turf(src)) - if(A == src) continue - if(istype(A,/mob/living)) - if(A:lying) continue - src.throw_impact(A,speed) - if(isobj(A)) - if(!A.density || A.throwpass) - continue - // Special handling of windows, which are dense but block only from some directions - if(istype(A, /obj/structure/window)) - var/obj/structure/window/W = A - if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir)) - continue - // Same thing for (closed) windoors, which have the same problem - else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir)) - continue - src.throw_impact(A,speed) - -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback) //If this returns FALSE then callback will not be called. - . = TRUE - if (!target || speed <= 0 || QDELETED(src) || (target.z != src.z)) - return FALSE - - if (pulledby) - pulledby.stop_pulling() - - var/datum/thrownthing/TT = new(src, target, range, speed, thrower, callback) - throwing = TT - - pixel_z = 0 - if(spin && does_spin) - SpinAnimation(4,1) - - SSthrowing.processing[src] = TT - if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun)) - SSthrowing.currentrun[src] = TT - -//Overlays -/atom/movable/overlay - var/atom/master = null - anchored = TRUE - -/atom/movable/overlay/New() - for(var/x in src.verbs) - src.verbs -= x - ..() - -/atom/movable/overlay/attackby(a, b) - if (src.master) - return src.master.attackby(a, b) - return - -/atom/movable/overlay/attack_hand(a, b, c) - if (src.master) - return src.master.attack_hand(a, b, c) - return - -/atom/movable/proc/touch_map_edge() - if(z in using_map.sealed_levels) - return - - if(using_map.use_overmap) - overmap_spacetravel(get_turf(src), src) - return - - var/move_to_z = src.get_transit_zlevel() - if(move_to_z) - var/new_z = move_to_z - var/new_x - var/new_y - - if(x <= TRANSITIONEDGE) - new_x = world.maxx - TRANSITIONEDGE - 2 - new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - - else if (x >= (world.maxx - TRANSITIONEDGE + 1)) - new_x = TRANSITIONEDGE + 1 - new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - - else if (y <= TRANSITIONEDGE) - new_y = world.maxy - TRANSITIONEDGE -2 - new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - - else if (y >= (world.maxy - TRANSITIONEDGE + 1)) - new_y = TRANSITIONEDGE + 1 - new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - - if(ticker && istype(ticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear - var/datum/game_mode/nuclear/G = ticker.mode - G.check_nuke_disks() - - var/turf/T = locate(new_x, new_y, new_z) - if(istype(T)) - forceMove(T) - -//by default, transition randomly to another zlevel -/atom/movable/proc/get_transit_zlevel() - var/list/candidates = using_map.accessible_z_levels.Copy() - candidates.Remove("[src.z]") - - if(!candidates.len) - return null - return text2num(pickweight(candidates)) - -// Returns the current scaling of the sprite. -// Note this DOES NOT measure the height or width of the icon, but returns what number is being multiplied with to scale the icons, if any. -/atom/movable/proc/get_icon_scale_x() - return icon_scale_x - -/atom/movable/proc/get_icon_scale_y() - return icon_scale_y - -/atom/movable/proc/update_transform() - var/matrix/M = matrix() - M.Scale(icon_scale_x, icon_scale_y) - M.Turn(icon_rotation) - src.transform = M - -// Use this to set the object's scale. -/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y) - if(isnull(new_scale_y)) - new_scale_y = new_scale_x - if(new_scale_x != 0) - icon_scale_x = new_scale_x - if(new_scale_y != 0) - icon_scale_y = new_scale_y - update_transform() - -/atom/movable/proc/adjust_rotation(new_rotation) - icon_rotation = new_rotation - update_transform() - -// Called when touching a lava tile. -/atom/movable/proc/lava_act() - fire_act(null, 10000, 1000) - - -// Procs to cloak/uncloak -/atom/movable/proc/cloak() - if(cloaked) - return FALSE - cloaked = TRUE - . = TRUE // We did work - - var/static/animation_time = 1 SECOND - cloaked_selfimage = get_cloaked_selfimage() - - //Wheeee - cloak_animation(animation_time) - - //Needs to be last so people can actually see the effect before we become invisible - if(cloaked) // Ensure we are still cloaked after the animation delay - plane = CLOAKED_PLANE - -/atom/movable/proc/uncloak() - if(!cloaked) - return FALSE - cloaked = FALSE - . = TRUE // We did work - - var/static/animation_time = 1 SECOND - QDEL_NULL(cloaked_selfimage) - - //Needs to be first so people can actually see the effect, so become uninvisible first - plane = initial(plane) - - //Oooooo - uncloak_animation(animation_time) - - -// Animations for cloaking/uncloaking -/atom/movable/proc/cloak_animation(var/length = 1 SECOND) - //Save these - var/initial_alpha = alpha - - //Animate alpha fade - animate(src, alpha = 0, time = length) - - //Animate a cloaking effect - var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. - filters += filter(type="wave", x = 0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) - animate(filters[our_filter], offset = 1, size = 8, time = length, flags = ANIMATION_PARALLEL) - - //Wait for animations to finish - sleep(length+5) - - //Remove those - filters -= filter(type="wave", x = 0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) - - //Back to original alpha - alpha = initial_alpha - -/atom/movable/proc/uncloak_animation(var/length = 1 SECOND) - //Save these - var/initial_alpha = alpha - - //Put us back to normal, but no alpha - alpha = 0 - - //Animate alpha fade up - animate(src, alpha = initial_alpha, time = length) - - //Animate a cloaking effect - var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. - filters += filter(type="wave", x=0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) - animate(filters[our_filter], offset = 0, size = 0, time = length, flags = ANIMATION_PARALLEL) - - //Wait for animations to finish - sleep(length+5) - - //Remove those - filters -= filter(type="wave", x=0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) - - -// So cloaked things can see themselves, if necessary -/atom/movable/proc/get_cloaked_selfimage() - var/icon/selficon = icon(icon, icon_state) - selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White - var/image/selfimage = image(selficon) - selfimage.color = "#0000FF" - selfimage.alpha = 100 - selfimage.layer = initial(layer) - selfimage.plane = initial(plane) - selfimage.loc = src - - return selfimage - -/atom/movable/proc/get_cell() +/atom/movable + layer = OBJ_LAYER + appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER|LONG_GLIDE + glide_size = 8 + var/last_move = null //The direction the atom last moved + var/anchored = FALSE + // var/elevation = 2 - not used anywhere + var/moving_diagonally + var/move_speed = 10 + var/l_move_time = 1 + var/throwing = 0 + var/thrower + var/turf/throw_source = null + var/throw_speed = 2 + var/throw_range = 7 + var/moved_recently = 0 + var/mob/pulledby = null + var/item_state = null // Used to specify the item state for the on-mob overlays. + var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform(). + var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform(). + var/icon_rotation = 0 // Used to rotate icons in update_transform() + var/icon_expected_height = 32 + var/icon_expected_width = 32 + var/old_x = 0 + var/old_y = 0 + var/datum/riding/riding_datum = null + var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) + var/movement_type = NONE + + var/cloaked = FALSE //If we're cloaked or not + var/image/cloaked_selfimage //The image we use for our client to let them see where we are + +/atom/movable/Initialize(mapload) + . = ..() + switch(blocks_emissive) + if(EMISSIVE_BLOCK_GENERIC) + var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = PLANE_EMISSIVE, alpha = src.alpha) + gen_emissive_blocker.color = GLOB.em_block_color + gen_emissive_blocker.dir = dir + gen_emissive_blocker.appearance_flags |= appearance_flags + add_overlay(list(gen_emissive_blocker), TRUE) + if(EMISSIVE_BLOCK_UNIQUE) + render_target = ref(src) + em_block = new(src, render_target) + add_overlay(list(em_block), TRUE) + if(opacity) + AddElement(/datum/element/light_blocking) + switch(light_system) + if(STATIC_LIGHT) + update_light() + if(MOVABLE_LIGHT) + AddComponent(/datum/component/overlay_lighting, starts_on = light_on) + if(MOVABLE_LIGHT_DIRECTIONAL) + AddComponent(/datum/component/overlay_lighting, is_directional = TRUE, starts_on = light_on) + +/atom/movable/Destroy() + . = ..() + for(var/atom/movable/AM in contents) + qdel(AM) + + if(opacity) + RemoveElement(/datum/element/light_blocking) + + moveToNullspace() + + vis_contents.Cut() + for(var/atom/movable/A as anything in vis_locs) + A.vis_contents -= src + + if(pulledby) + pulledby.stop_pulling() + + if(orbiting) + stop_orbit() + QDEL_NULL(riding_datum) //VOREStation Add + +/atom/movable/vv_edit_var(var_name, var_value) + if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly. + return FALSE + return ..() + +//////////////////////////////////////// +/atom/movable/Move(atom/newloc, direct = 0, movetime) + // Didn't pass enough info + if(!loc || !newloc) + return FALSE + + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc, direct, movetime) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) + return FALSE + + // Store this early before we might move, it's used several places + var/atom/oldloc = loc + + // If we're not moving to the same spot (why? does that even happen?) + if(loc != newloc) + if(!direct) + direct = get_dir(oldloc, newloc) + if (IS_CARDINAL(direct)) //Cardinal move + // Track our failure if any in this value + . = TRUE + + // Face the direction of movement + set_dir(direct) + + // Check to make sure we can leave + if(!loc.Exit(src, newloc)) + . = FALSE + + // Check to make sure we can enter, if we haven't already failed + if(. && !newloc.Enter(src, src.loc)) + . = FALSE + + // Check to make sure if we're multi-tile we can move, if we haven't already failed + if(. && !check_multi_tile_move_density_dir(direct, locs)) + . = FALSE + + // Definitely moving if you enter this, no failures so far + if(. && locs.len <= 1) // We're not a multi-tile object. + var/area/oldarea = get_area(oldloc) + var/area/newarea = get_area(newloc) + var/old_z = get_z(oldloc) + var/dest_z = get_z(newloc) + + // Do The Move + if(movetime) + glide_for(movetime) // First attempt, lets let the diag do it. + loc = newloc + . = TRUE + + // So objects can be informed of z-level changes + if (old_z != dest_z) + onTransitZ(old_z, dest_z) + + // We don't call parent so we are calling this for byond + oldloc.Exited(src, newloc) + if(oldarea != newarea) + oldarea.Exited(src, newloc) + + // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself + for(var/atom/movable/thing as anything in oldloc) + // We don't call parent so we are calling this for byond + thing.Uncrossed(src) + + // We don't call parent so we are calling this for byond + newloc.Entered(src, oldloc) + if(oldarea != newarea) + newarea.Entered(src, oldloc) + + // Multi-tile objects can't reach here, otherwise you'd need to avoid uncrossing yourself + for(var/atom/movable/thing as anything in loc) + // We don't call parent so we are calling this for byond + thing.Crossed(src, oldloc) + + // We're a multi-tile object (multiple locs) + else if(. && newloc) + . = doMove(newloc) + + //Diagonal move, split it into cardinal moves + else + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a forceMove taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + glide_for(movetime * 2) + if (direct & NORTH) + if (direct & EAST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & WEST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & SOUTH) + if (direct & EAST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if (direct & WEST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + // If we failed, turn to face the direction of the first step at least + if(!. && moving_diagonally == SECOND_DIAG_STEP) + set_dir(first_step_dir) + // Done, regardless! + moving_diagonally = 0 + // We return because step above will call Move() and we don't want to do shenanigans back in here again + return + + else if(!loc || (loc == oldloc)) + last_move = 0 + return + + // If we moved, call Moved() on ourselves + if(.) + Moved(oldloc, direct, FALSE, movetime ? movetime : ( (TICKS2DS(WORLD_ICON_SIZE/glide_size)) * (moving_diagonally ? (0.5) : 1) ) ) + + // Update timers/cooldown stuff + move_speed = world.time - l_move_time + l_move_time = world.time + last_move = direct // The direction you last moved + // set_dir(direct) //Don't think this is necessary + +//Called after a successful Move(). By this point, we've already moved +/atom/movable/proc/Moved(atom/old_loc, direction, forced = FALSE, movetime) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction, forced, movetime) + // Handle any buckled mobs on this movable + if(has_buckled_mobs()) + handle_buckled_mob_movement(old_loc, direction, movetime) + if(riding_datum) + riding_datum.handle_vehicle_layer() + riding_datum.handle_vehicle_offsets() + for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. + light.source_atom.update_light() + + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, direction) + + return TRUE + +/atom/movable/set_dir(newdir) + . = ..(newdir) + if(riding_datum) + riding_datum.handle_vehicle_offsets() + +/atom/movable/relaymove(mob/user, direction) + . = ..() + if(riding_datum) + riding_datum.handle_ride(user, direction) + +// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly. +// You probably want CanPass() +/atom/movable/Cross(atom/movable/AM) + return CanPass(AM, loc) + +/atom/movable/CanPass(atom/movable/mover, turf/target) + . = ..() + if(locs && locs.len >= 2) // If something is standing on top of us, let them pass. + if(mover.loc in locs) + . = TRUE + return . + +/atom/movable/Bump(atom/A) + if(!A) + CRASH("Bump was called with no argument.") + . = ..() + if(throwing) + throw_impact(A) + throwing = 0 + if(QDELETED(A)) + return + + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) + + A.Bumped(src) + A.last_bumped = world.time + +/atom/movable/proc/forceMove(atom/destination, direction, movetime) + . = FALSE + if(destination) + . = doMove(destination, direction, movetime) + else + CRASH("No valid destination passed into forceMove") + +/atom/movable/proc/moveToNullspace() + return doMove(null) + +/atom/movable/proc/doMove(atom/destination, direction, movetime) + var/atom/oldloc = loc + var/area/old_area = get_area(oldloc) + var/same_loc = oldloc == destination + + if(destination) + var/area/destarea = get_area(destination) + + // Do The Move + glide_for(movetime) + last_move = isnull(direction) ? 0 : direction + loc = destination + + // Unset this in case it was set in some other proc. We're no longer moving diagonally for sure. + moving_diagonally = 0 + + // We are moving to a different loc + if(!same_loc) + // Not moving out of nullspace + if(oldloc) + oldloc.Exited(src, destination) + // If it's not the same area, Exited() it + if(old_area && old_area != destarea) + old_area.Exited(src, destination) + + // Uncross everything where we left + for(var/atom/movable/AM as anything in oldloc) + if(AM == src) + continue + AM.Uncrossed(src) + if(loc != destination) // Uncrossed() triggered a separate movement + return + + // Information about turf and z-levels for source and dest collected + var/turf/oldturf = get_turf(oldloc) + var/turf/destturf = get_turf(destination) + var/old_z = (oldturf ? oldturf.z : null) + var/dest_z = (destturf ? destturf.z : null) + + // So objects can be informed of z-level changes + if (old_z != dest_z) + onTransitZ(old_z, dest_z) + + // Destination atom Entered + destination.Entered(src, oldloc) + + // Entered() the new area if it's not the same area + if(destarea && old_area != destarea) + destarea.Entered(src, oldloc) + + // We ignore ourselves because if we're multi-tile we might be in both old and new locs + for(var/atom/movable/AM as anything in destination) + if(AM == src) + continue + AM.Crossed(src, oldloc) + if(loc != destination) // Crossed triggered a separate movement + return + + // Call our thingy to inform everyone we moved + Moved(oldloc, NONE, TRUE) + + // Break pulling if we are too far to pull now. + if(pulledby && (pulledby.z != src.z || get_dist(pulledby, src) > 1)) + pulledby.stop_pulling() + + // We moved + return TRUE + + //If no destination, move the atom into nullspace (don't do this unless you know what you're doing) + else if(oldloc) + loc = null + + // Uncross everything where we left (no multitile safety like above because we are definitely not still there) + for(var/atom/movable/AM as anything in oldloc) + AM.Uncrossed(src) + + // Exited() our loc and area + oldloc.Exited(src, null) + if(old_area) + old_area.Exited(src, null) + + // We moved + return TRUE + +/atom/movable/proc/onTransitZ(old_z,new_z) + GLOB.z_moved_event.raise_event(src, old_z, new_z) + SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z) + for(var/atom/movable/AM as anything in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care. + AM.onTransitZ(old_z,new_z) + +/atom/movable/proc/glide_for(movetime) + if(movetime) + glide_size = WORLD_ICON_SIZE/max(DS2TICKS(movetime), 1) + spawn(movetime) + glide_size = initial(glide_size) + else + glide_size = initial(glide_size) + +///////////////////////////////////////////////////////////////// + +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, var/speed) + if(istype(hit_atom,/mob/living)) + var/mob/living/M = hit_atom + if(M.buckled == src) + return // Don't hit the thing we're buckled to. + M.hitby(src,speed) + + else if(isobj(hit_atom)) + var/obj/O = hit_atom + if(!O.anchored) + step(O, src.last_move) + O.hitby(src,speed) + + else if(isturf(hit_atom)) + src.throwing = 0 + var/turf/T = hit_atom + T.hitby(src,speed) + +//decided whether a movable atom being thrown can pass through the turf it is in. +/atom/movable/proc/hit_check(var/speed) + if(src.throwing) + for(var/atom/A in get_turf(src)) + if(A == src) continue + if(istype(A,/mob/living)) + if(A:lying) continue + src.throw_impact(A,speed) + if(isobj(A)) + if(!A.density || A.throwpass) + continue + // Special handling of windows, which are dense but block only from some directions + if(istype(A, /obj/structure/window)) + var/obj/structure/window/W = A + if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir)) + continue + // Same thing for (closed) windoors, which have the same problem + else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir)) + continue + src.throw_impact(A,speed) + +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback) //If this returns FALSE then callback will not be called. + . = TRUE + if (!target || speed <= 0 || QDELETED(src) || (target.z != src.z)) + return FALSE + + if (pulledby) + pulledby.stop_pulling() + + var/datum/thrownthing/TT = new(src, target, range, speed, thrower, callback) + throwing = TT + + pixel_z = 0 + if(spin && does_spin) + SpinAnimation(4,1) + + SSthrowing.processing[src] = TT + if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun)) + SSthrowing.currentrun[src] = TT + +//Overlays +/atom/movable/overlay + var/atom/master = null + anchored = TRUE + +/atom/movable/overlay/New() + for(var/x in src.verbs) + src.verbs -= x + ..() + +/atom/movable/overlay/attackby(a, b) + if (src.master) + return src.master.attackby(a, b) + return + +/atom/movable/overlay/attack_hand(a, b, c) + if (src.master) + return src.master.attack_hand(a, b, c) + return + +/atom/movable/proc/touch_map_edge() + if(z in using_map.sealed_levels) + return + + if(using_map.use_overmap) + overmap_spacetravel(get_turf(src), src) + return + + var/move_to_z = src.get_transit_zlevel() + if(move_to_z) + var/new_z = move_to_z + var/new_x + var/new_y + + if(x <= TRANSITIONEDGE) + new_x = world.maxx - TRANSITIONEDGE - 2 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (x >= (world.maxx - TRANSITIONEDGE + 1)) + new_x = TRANSITIONEDGE + 1 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (y <= TRANSITIONEDGE) + new_y = world.maxy - TRANSITIONEDGE -2 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + else if (y >= (world.maxy - TRANSITIONEDGE + 1)) + new_y = TRANSITIONEDGE + 1 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + if(ticker && istype(ticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear + var/datum/game_mode/nuclear/G = ticker.mode + G.check_nuke_disks() + + var/turf/T = locate(new_x, new_y, new_z) + if(istype(T)) + forceMove(T) + +//by default, transition randomly to another zlevel +/atom/movable/proc/get_transit_zlevel() + var/list/candidates = using_map.accessible_z_levels.Copy() + candidates.Remove("[src.z]") + + if(!candidates.len) + return null + return text2num(pickweight(candidates)) + +// Returns the current scaling of the sprite. +// Note this DOES NOT measure the height or width of the icon, but returns what number is being multiplied with to scale the icons, if any. +/atom/movable/proc/get_icon_scale_x() + return icon_scale_x + +/atom/movable/proc/get_icon_scale_y() + return icon_scale_y + +/atom/movable/proc/update_transform() + var/matrix/M = matrix() + M.Scale(icon_scale_x, icon_scale_y) + M.Turn(icon_rotation) + src.transform = M + +// Use this to set the object's scale. +/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y) + if(isnull(new_scale_y)) + new_scale_y = new_scale_x + if(new_scale_x != 0) + icon_scale_x = new_scale_x + if(new_scale_y != 0) + icon_scale_y = new_scale_y + update_transform() + +/atom/movable/proc/adjust_rotation(new_rotation) + icon_rotation = new_rotation + update_transform() + +// Called when touching a lava tile. +/atom/movable/proc/lava_act() + fire_act(null, 10000, 1000) + + +// Procs to cloak/uncloak +/atom/movable/proc/cloak() + if(cloaked) + return FALSE + cloaked = TRUE + . = TRUE // We did work + + var/static/animation_time = 1 SECOND + cloaked_selfimage = get_cloaked_selfimage() + + //Wheeee + cloak_animation(animation_time) + + //Needs to be last so people can actually see the effect before we become invisible + if(cloaked) // Ensure we are still cloaked after the animation delay + plane = CLOAKED_PLANE + +/atom/movable/proc/uncloak() + if(!cloaked) + return FALSE + cloaked = FALSE + . = TRUE // We did work + + var/static/animation_time = 1 SECOND + QDEL_NULL(cloaked_selfimage) + + //Needs to be first so people can actually see the effect, so become uninvisible first + plane = initial(plane) + + //Oooooo + uncloak_animation(animation_time) + + +// Animations for cloaking/uncloaking +/atom/movable/proc/cloak_animation(var/length = 1 SECOND) + //Save these + var/initial_alpha = alpha + + //Animate alpha fade + animate(src, alpha = 0, time = length) + + //Animate a cloaking effect + var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. + filters += filter(type="wave", x = 0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) + animate(filters[our_filter], offset = 1, size = 8, time = length, flags = ANIMATION_PARALLEL) + + //Wait for animations to finish + sleep(length+5) + + //Remove those + filters -= filter(type="wave", x = 0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) + + //Back to original alpha + alpha = initial_alpha + +/atom/movable/proc/uncloak_animation(var/length = 1 SECOND) + //Save these + var/initial_alpha = alpha + + //Put us back to normal, but no alpha + alpha = 0 + + //Animate alpha fade up + animate(src, alpha = initial_alpha, time = length) + + //Animate a cloaking effect + var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. + filters += filter(type="wave", x=0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) + animate(filters[our_filter], offset = 0, size = 0, time = length, flags = ANIMATION_PARALLEL) + + //Wait for animations to finish + sleep(length+5) + + //Remove those + filters -= filter(type="wave", x=0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) + + +// So cloaked things can see themselves, if necessary +/atom/movable/proc/get_cloaked_selfimage() + var/icon/selficon = icon(icon, icon_state) + selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White + var/image/selfimage = image(selficon) + selfimage.color = "#0000FF" + selfimage.alpha = 100 + selfimage.layer = initial(layer) + selfimage.plane = initial(plane) + selfimage.loc = src + + return selfimage + +/atom/movable/proc/get_cell() return \ No newline at end of file diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index f6c96f0ab98..defe8b32146 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -5,10 +5,10 @@ */ // What each index means: -#define DNA_OFF_LOWERBOUND 0 -#define DNA_OFF_UPPERBOUND 1 -#define DNA_ON_LOWERBOUND 2 -#define DNA_ON_UPPERBOUND 3 +#define DNA_OFF_LOWERBOUND 1 +#define DNA_OFF_UPPERBOUND 2 +#define DNA_ON_LOWERBOUND 3 +#define DNA_ON_UPPERBOUND 4 // Define block bounds (off-low,off-high,on-low,on-high) // Used in setupgame.dm @@ -120,6 +120,7 @@ var/global/list/datum/dna/gene/dna_genes[0] var/custom_exclaim var/list/custom_heat = list() var/list/custom_cold = list() + var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types // VOREStation // New stuff @@ -148,6 +149,7 @@ var/global/list/datum/dna/gene/dna_genes[0] new_dna.custom_exclaim=custom_exclaim //VOREStaton Edit new_dna.custom_heat=custom_heat //VOREStation Edit new_dna.custom_cold=custom_cold //VOREStation Edit + new_dna.digitigrade=src.digitigrade //VOREStation Edit var/list/body_markings_genetic = (body_markings - body_marking_nopersist_list) new_dna.body_markings=body_markings_genetic.Copy() for(var/b=1;b<=DNA_SE_LENGTH;b++) @@ -222,6 +224,7 @@ var/global/list/datum/dna/gene/dna_genes[0] src.custom_exclaim = character.custom_exclaim src.custom_heat = character.custom_heat src.custom_cold = character.custom_cold + src.digitigrade = character.digitigrade //ARFS EDIT var/taurtype = /datum/sprite_accessory/tail/taur/fatdrake/andy diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 74c4d3569ee..0e8e0ba43c0 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -217,7 +217,7 @@ if(!M.client && M.mind) for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == M.mind) - to_chat(ghost, "Your corpse has been placed into a cloning scanner. Return to your body if you want to be resurrected/cloned! (Verbs -> Ghost -> Re-enter corpse)") + to_chat(ghost, span_interface("Your corpse has been placed into a cloning scanner. Return to your body if you want to be resurrected/cloned! (Verbs -> Ghost -> Re-enter corpse)")) break SStgui.update_uis(src) diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 0089355ed31..b9d4837184a 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -1,163 +1,163 @@ -/datum/dna/gene/monkey - name="Monkey" - -/datum/dna/gene/monkey/New() - block=MONKEYBLOCK - -/datum/dna/gene/monkey/can_activate(var/mob/M,var/flags) - return istype(M, /mob/living/carbon/human) || istype(M,/mob/living/carbon/monkey) - -/datum/dna/gene/monkey/activate(var/mob/living/M, var/connected, var/flags) - if(!istype(M,/mob/living/carbon/human)) - //testing("Cannot monkey-ify [M], type is [M.type].") - return - var/mob/living/carbon/human/H = M - H.transforming = 1 - var/list/implants = list() //Try to preserve implants. - for(var/obj/item/weapon/implant/W in H) - implants += W - W.loc = null - - if(!connected) - for(var/obj/item/W in (H.contents-implants)) - if (W==H.w_uniform) // will be teared - continue - H.drop_from_inventory(W) - M.transforming = 1 - M.canmove = 0 - M.icon = null - M.invisibility = 101 - var/atom/movable/overlay/animation = new( M.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("h2monkey", animation) - sleep(48) - qdel(animation) - - - var/mob/living/carbon/monkey/O = null - if(H.species.primitive) - O = new H.species.primitive(src) - else - H.gib() //Trying to change the species of a creature with no primitive var set is messy. - return - - if(M) - if (M.dna) - O.dna = M.dna.Clone() - M.dna = null - - if (M.suiciding) - O.suiciding = M.suiciding - M.suiciding = null - - for(var/obj/T in (M.contents-implants)) - qdel(T) - - O.loc = M.loc - - if(M.mind) - M.mind.transfer_to(O) //transfer our mind to the cute little monkey - - if (connected) //inside dna thing - var/obj/machinery/dna_scannernew/C = connected - O.loc = C - C.occupant = O - connected = null - O.real_name = text("monkey ([])",copytext(md5(M.real_name), 2, 6)) - O.take_overall_damage(M.getBruteLoss() + 40, M.getFireLoss()) - O.adjustToxLoss(M.getToxLoss() + 20) - O.adjustOxyLoss(M.getOxyLoss()) - O.set_stat(M.stat) - O.a_intent = I_HURT - for (var/obj/item/weapon/implant/I in implants) - I.loc = O - I.implanted = O -// O.update_icon = 1 //queue a full icon update at next life() call - qdel(M) - return - -/datum/dna/gene/monkey/deactivate(var/mob/living/M, var/connected, var/flags) - if(!istype(M,/mob/living/carbon/monkey)) - //testing("Cannot humanize [M], type is [M.type].") - return - var/mob/living/carbon/monkey/Mo = M - Mo.transforming = 1 - var/list/implants = list() //Still preserving implants - for(var/obj/item/weapon/implant/W in Mo) - implants += W - W.loc = null - if(!connected) - for(var/obj/item/W in (Mo.contents-implants)) - Mo.drop_from_inventory(W) - M.transforming = 1 - M.canmove = 0 - M.icon = null - M.invisibility = 101 - var/atom/movable/overlay/animation = new( M.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("monkey2h", animation) - sleep(48) - qdel(animation) - - var/mob/living/carbon/human/O - if(Mo.greaterform) - O = new(src, Mo.greaterform) - else - O = new(src) - - if (M.dna.GetUIState(DNA_UI_GENDER)) - O.gender = FEMALE - else - O.gender = MALE - - if (M) - if (M.dna) - O.dna = M.dna.Clone() - M.dna = null - - if (M.suiciding) - O.suiciding = M.suiciding - M.suiciding = null - - //for(var/obj/T in M) - // qdel(T) - - O.loc = M.loc - - if(M.mind) - M.mind.transfer_to(O) //transfer our mind to the human - - if (connected) //inside dna thing - var/obj/machinery/dna_scannernew/C = connected - O.loc = C - C.occupant = O - connected = null - - var/i - while (!i) - var/randomname - if (O.gender == MALE) - randomname = capitalize(pick(first_names_male) + " " + capitalize(pick(last_names))) - else - randomname = capitalize(pick(first_names_female) + " " + capitalize(pick(last_names))) - if (findname(randomname)) - continue - else - O.real_name = randomname - O.dna.real_name = randomname - i++ - O.UpdateAppearance() - O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) - O.adjustToxLoss(M.getToxLoss()) - O.adjustOxyLoss(M.getOxyLoss()) - O.set_stat(M.stat) - for (var/obj/item/weapon/implant/I in implants) - I.loc = O - I.implanted = O -// O.update_icon = 1 //queue a full icon update at next life() call - qdel(M) - return +/datum/dna/gene/monkey + name="Monkey" + +/datum/dna/gene/monkey/New() + block=MONKEYBLOCK + +/datum/dna/gene/monkey/can_activate(var/mob/M,var/flags) + return istype(M, /mob/living/carbon/human) || istype(M,/mob/living/carbon/monkey) + +/datum/dna/gene/monkey/activate(var/mob/living/M, var/connected, var/flags) + if(!istype(M,/mob/living/carbon/human)) + //testing("Cannot monkey-ify [M], type is [M.type].") + return + var/mob/living/carbon/human/H = M + H.transforming = 1 + var/list/implants = list() //Try to preserve implants. + for(var/obj/item/weapon/implant/W in H) + implants += W + W.loc = null + + if(!connected) + for(var/obj/item/W in (H.contents-implants)) + if (W==H.w_uniform) // will be teared + continue + H.drop_from_inventory(W) + M.transforming = 1 + M.canmove = 0 + M.icon = null + M.invisibility = 101 + var/atom/movable/overlay/animation = new( M.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("h2monkey", animation) + sleep(48) + qdel(animation) + + + var/mob/living/carbon/monkey/O = null + if(H.species.primitive) + O = new H.species.primitive(src) + else + H.gib() //Trying to change the species of a creature with no primitive var set is messy. + return + + if(M) + if (M.dna) + O.dna = M.dna.Clone() + M.dna = null + + if (M.suiciding) + O.suiciding = M.suiciding + M.suiciding = null + + for(var/obj/T in (M.contents-implants)) + qdel(T) + + O.loc = M.loc + + if(M.mind) + M.mind.transfer_to(O) //transfer our mind to the cute little monkey + + if (connected) //inside dna thing + var/obj/machinery/dna_scannernew/C = connected + O.loc = C + C.occupant = O + connected = null + O.real_name = text("monkey ([])",copytext(md5(M.real_name), 2, 6)) + O.take_overall_damage(M.getBruteLoss() + 40, M.getFireLoss()) + O.adjustToxLoss(M.getToxLoss() + 20) + O.adjustOxyLoss(M.getOxyLoss()) + O.set_stat(M.stat) + O.a_intent = I_HURT + for (var/obj/item/weapon/implant/I in implants) + I.loc = O + I.implanted = O +// O.update_icon = 1 //queue a full icon update at next life() call + qdel(M) + return + +/datum/dna/gene/monkey/deactivate(var/mob/living/M, var/connected, var/flags) + if(!istype(M,/mob/living/carbon/monkey)) + //testing("Cannot humanize [M], type is [M.type].") + return + var/mob/living/carbon/monkey/Mo = M + Mo.transforming = 1 + var/list/implants = list() //Still preserving implants + for(var/obj/item/weapon/implant/W in Mo) + implants += W + W.loc = null + if(!connected) + for(var/obj/item/W in (Mo.contents-implants)) + Mo.drop_from_inventory(W) + M.transforming = 1 + M.canmove = 0 + M.icon = null + M.invisibility = 101 + var/atom/movable/overlay/animation = new( M.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("monkey2h", animation) + sleep(48) + qdel(animation) + + var/mob/living/carbon/human/O + if(Mo.greaterform) + O = new(src, Mo.greaterform) + else + O = new(src) + + if (M.dna.GetUIState(DNA_UI_GENDER)) + O.gender = FEMALE + else + O.gender = MALE + + if (M) + if (M.dna) + O.dna = M.dna.Clone() + M.dna = null + + if (M.suiciding) + O.suiciding = M.suiciding + M.suiciding = null + + //for(var/obj/T in M) + // qdel(T) + + O.loc = M.loc + + if(M.mind) + M.mind.transfer_to(O) //transfer our mind to the human + + if (connected) //inside dna thing + var/obj/machinery/dna_scannernew/C = connected + O.loc = C + C.occupant = O + connected = null + + var/i + while (!i) + var/randomname + if (O.gender == MALE) + randomname = capitalize(pick(first_names_male) + " " + capitalize(pick(last_names))) + else + randomname = capitalize(pick(first_names_female) + " " + capitalize(pick(last_names))) + if (findname(randomname)) + continue + else + O.real_name = randomname + O.dna.real_name = randomname + i++ + O.UpdateAppearance() + O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) + O.adjustToxLoss(M.getToxLoss()) + O.adjustOxyLoss(M.getOxyLoss()) + O.set_stat(M.stat) + for (var/obj/item/weapon/implant/I in implants) + I.loc = O + I.implanted = O +// O.update_icon = 1 //queue a full icon update at next life() call + qdel(M) + return diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 13b8db962dd..ceb4234f2d6 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -1,19 +1,19 @@ -/datum/game_mode/changeling - name = "Changeling" - round_description = "There are alien changelings on the station. Do not let the changelings succeed!" - extended_round_description = "Life always finds a way. However, life can sometimes take a more disturbing route. \ - Humanity's extensive knowledge of xeno-biological specimens has made them confident and arrogant. Yet \ - something slipped past their eyes. Something dangerous. Something alive. Most frightening of all, \ - however, is that this something is someone. An unknown alien specimen has incorporated itself into \ - the crew of the station. Its unique biology allows it to manipulate its own or anyone else's DNA. \ - With the ability to copy faces, voices, animals, but also change the chemical make up of your own body, \ - its existence is a threat to not only your personal safety but the lives of everyone on board. \ - No one knows where it came from. No one knows who it is or what it wants. One thing is for \ - certain though... there is never just one of them. Good luck." - config_tag = "changeling" - required_players = 2 - required_players_secret = 3 - required_enemies = 1 - end_on_antag_death = 0 - antag_scaling_coeff = 10 - antag_tags = list(MODE_CHANGELING) +/datum/game_mode/changeling + name = "Changeling" + round_description = "There are alien changelings on the station. Do not let the changelings succeed!" + extended_round_description = "Life always finds a way. However, life can sometimes take a more disturbing route. \ + Humanity's extensive knowledge of xeno-biological specimens has made them confident and arrogant. Yet \ + something slipped past their eyes. Something dangerous. Something alive. Most frightening of all, \ + however, is that this something is someone. An unknown alien specimen has incorporated itself into \ + the crew of the station. Its unique biology allows it to manipulate its own or anyone else's DNA. \ + With the ability to copy faces, voices, animals, but also change the chemical make up of your own body, \ + its existence is a threat to not only your personal safety but the lives of everyone on board. \ + No one knows where it came from. No one knows who it is or what it wants. One thing is for \ + certain though... there is never just one of them. Good luck." + config_tag = "changeling" + required_players = 2 + required_players_secret = 3 + required_enemies = 1 + end_on_antag_death = 0 + antag_scaling_coeff = 10 + antag_tags = list(MODE_CHANGELING) diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index 431b4e06f32..4c92e7c95a6 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -1,244 +1,244 @@ -var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") - -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/datum/absorbed_dna/absorbed_dna = list() - var/list/absorbed_languages = list() // Necessary because of set_species stuff - var/absorbedcount = 0 - var/lingabsorbedcount = 1 //Starts at one, because that's us - var/chem_charges = 20 - var/chem_recharge_rate = 0.5 - var/chem_storage = 50 - var/sting_range = 1 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/geneticpoints = 7 - var/max_geneticpoints = 7 - var/readapts = 1 - var/max_readapts = 2 - var/list/purchased_powers = list() - var/mimicing = "" - var/cloaked = 0 - var/armor_deployed = 0 //This is only used for changeling_generic_equip_all_slots() at the moment. - var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name. - var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. - var/last_shriek = null // world.time when the ling last used a shriek. - var/next_escape = 0 // world.time when the ling can next use Escape Restraints - var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? - -/datum/changeling/New(var/gender=FEMALE) - ..() - if(possible_changeling_IDs.len) - changelingID = pick(possible_changeling_IDs) - possible_changeling_IDs -= changelingID - changelingID = "[changelingID]" - else - changelingID = "[rand(1,999)]" - -/datum/changeling/proc/regenerate() - chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage) - geneticdamage = max(0, geneticdamage-1) - -/datum/changeling/proc/GetDNA(var/dna_owner) - for(var/datum/absorbed_dna/DNA in absorbed_dna) - if(dna_owner == DNA.name) - return DNA - -/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA) - var/datum/changeling/changeling = null - if(src.mind && src.mind.changeling) - changeling = src.mind.changeling - if(!changeling) - return - - for(var/language in newDNA.languages) - changeling.absorbed_languages |= language - - changeling_update_languages(changeling.absorbed_languages) - - if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite - changeling.absorbed_dna += newDNA - -//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human -/mob/proc/make_changeling() - - if(!mind) return - if(!mind.changeling) mind.changeling = new /datum/changeling(gender) - - verbs.Add(/datum/changeling/proc/EvolutionMenu) - verbs.Add(/mob/proc/changeling_respec) - add_language("Changeling") - - var/lesser_form = !ishuman(src) - - if(!powerinstances.len) - for(var/P in powers) - powerinstances += new P() - - // Code to auto-purchase free powers. - for(var/datum/power/changeling/P in powerinstances) - if(!P.genomecost) // Is it free? - if(!(P in mind.changeling.purchased_powers)) // Do we not have it already? - mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. - - for(var/datum/power/changeling/P in mind.changeling.purchased_powers) - if(P.isVerb) - if(lesser_form && !P.allowduringlesserform) continue - if(!(P in src.verbs)) - verbs.Add(P.verbpath) - if(P.make_hud_button) - if(!src.ability_master) - src.ability_master = new /obj/screen/movable/ability_master(src) - src.ability_master.add_ling_ability( - object_given = src, - verb_given = P.verbpath, - name_given = P.name, - ability_icon_given = P.ability_icon_state, - arguments = list() - ) - - for(var/language in languages) - mind.changeling.absorbed_languages |= language - - var/mob/living/carbon/human/H = src - if(istype(H)) - var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. - var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.identifying_gender, H.flavor_texts, H.modifiers) - absorbDNA(newDNA) - - return 1 - -//removes our changeling verbs -/mob/proc/remove_changeling_powers() - if(!mind || !mind.changeling) return - for(var/datum/power/changeling/P in mind.changeling.purchased_powers) - if(P.isVerb) - verbs.Remove(P.verbpath) - var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath) - if(C) - ability_master.remove_ability(C) - - -//Helper proc. Does all the checks and stuff for us to avoid copypasta -/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0) - - if(!src.mind) return - if(!iscarbon(src)) return - - var/datum/changeling/changeling = src.mind.changeling - if(!changeling) - to_world_log("[src] has the changeling_transform() verb but is not a changeling.") - return - - if(src.stat > max_stat) - to_chat(src, "We are incapacitated.") - return - - if(changeling.absorbed_dna.len < required_dna) - to_chat(src, "We require at least [required_dna] samples of compatible DNA.") - return - - if(changeling.chem_charges < required_chems) - to_chat(src, "We require at least [required_chems] units of chemicals to do that!") - return - - if(changeling.geneticdamage > max_genetic_damage) - to_chat(src, "Our genomes are still reassembling. We need time to recover first.") - return - - return changeling - -//Used to dump the languages from the changeling datum into the actual mob. -/mob/proc/changeling_update_languages(var/updated_languages) - languages = list() - for(var/language in updated_languages) - languages += language - - //This isn't strictly necessary but just to be safe... - add_language("Changeling") - - ////////// - //STINGS// //They get a pretty header because there's just so fucking many of them ;_; - ////////// - -/turf/proc/AdjacentTurfsRangedSting() - //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke - var/list/allowed = list( - /obj/structure/table, - /obj/structure/closet, - /obj/structure/frame, - /obj/structure/target_stake, - /obj/structure/cable, - /obj/structure/disposalpipe, - /obj/machinery, - /mob - ) - - var/L[] = new() - for(var/turf/simulated/t in oview(src,1)) - var/add = 1 - if(t.density) - add = 0 - if(add && LinkBlocked(src,t)) - add = 0 - if(add && TurfBlockedNonWindow(t)) - add = 0 - for(var/obj/O in t) - if(O.density) - add = 0 - break - if(istype(O, /obj/machinery/door)) - //not sure why this doesn't fire on LinkBlocked() - add = 0 - break - for(var/type in allowed) - if (istype(O, type)) - add = 1 - break - if(!add) - break - if(add) - L.Add(t) - return L - - -/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) - if(M.loc == src.loc) - return 1 //target and source are in the same thing - if(!isturf(src.loc) || !isturf(M.loc)) - to_chat(src, "We cannot reach \the [M] with a sting!") - return 0 //One is inside, the other is outside something. - // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising - if(!AStar(src.loc, M.loc, /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail - to_chat(src, "We cannot find a path to sting \the [M] by!") - return 0 - return 1 - -//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) -/mob/proc/changeling_sting(var/required_chems=0, var/verb_path) - var/datum/changeling/changeling = changeling_power(required_chems) - if(!changeling) return - - var/list/victims = list() - for(var/mob/living/carbon/C in oview(changeling.sting_range)) - victims += C - var/mob/living/carbon/T = tgui_input_list(src, "Who will we sting?", "Sting!", victims) - - if(!T) - return - if(T.isSynthetic()) - to_chat(src, "We are unable to pierce the outer shell of [T].") - return - if(!(T in view(changeling.sting_range))) return - if(!sting_can_reach(T, changeling.sting_range)) return - if(!changeling_power(required_chems)) return - - changeling.chem_charges -= required_chems - changeling.sting_range = 1 - src.verbs -= verb_path - spawn(10) src.verbs += verb_path - - to_chat(src, "We stealthily sting [T].") - if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting - to_chat(T, "You feel a tiny prick.") - return +var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") + +/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) + var/list/datum/absorbed_dna/absorbed_dna = list() + var/list/absorbed_languages = list() // Necessary because of set_species stuff + var/absorbedcount = 0 + var/lingabsorbedcount = 1 //Starts at one, because that's us + var/chem_charges = 20 + var/chem_recharge_rate = 0.5 + var/chem_storage = 50 + var/sting_range = 1 + var/changelingID = "Changeling" + var/geneticdamage = 0 + var/isabsorbing = 0 + var/geneticpoints = 7 + var/max_geneticpoints = 7 + var/readapts = 1 + var/max_readapts = 2 + var/list/purchased_powers = list() + var/mimicing = "" + var/cloaked = 0 + var/armor_deployed = 0 //This is only used for changeling_generic_equip_all_slots() at the moment. + var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name. + var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. + var/last_shriek = null // world.time when the ling last used a shriek. + var/next_escape = 0 // world.time when the ling can next use Escape Restraints + var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? + +/datum/changeling/New(var/gender=FEMALE) + ..() + if(possible_changeling_IDs.len) + changelingID = pick(possible_changeling_IDs) + possible_changeling_IDs -= changelingID + changelingID = "[changelingID]" + else + changelingID = "[rand(1,999)]" + +/datum/changeling/proc/regenerate() + chem_charges = min(max(0, chem_charges+chem_recharge_rate), chem_storage) + geneticdamage = max(0, geneticdamage-1) + +/datum/changeling/proc/GetDNA(var/dna_owner) + for(var/datum/absorbed_dna/DNA in absorbed_dna) + if(dna_owner == DNA.name) + return DNA + +/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA) + var/datum/changeling/changeling = null + if(src.mind && src.mind.changeling) + changeling = src.mind.changeling + if(!changeling) + return + + for(var/language in newDNA.languages) + changeling.absorbed_languages |= language + + changeling_update_languages(changeling.absorbed_languages) + + if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite + changeling.absorbed_dna += newDNA + +//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human +/mob/proc/make_changeling() + + if(!mind) return + if(!mind.changeling) mind.changeling = new /datum/changeling(gender) + + verbs.Add(/datum/changeling/proc/EvolutionMenu) + verbs.Add(/mob/proc/changeling_respec) + add_language("Changeling") + + var/lesser_form = !ishuman(src) + + if(!powerinstances.len) + for(var/P in powers) + powerinstances += new P() + + // Code to auto-purchase free powers. + for(var/datum/power/changeling/P in powerinstances) + if(!P.genomecost) // Is it free? + if(!(P in mind.changeling.purchased_powers)) // Do we not have it already? + mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. + + for(var/datum/power/changeling/P in mind.changeling.purchased_powers) + if(P.isVerb) + if(lesser_form && !P.allowduringlesserform) continue + if(!(P in src.verbs)) + verbs.Add(P.verbpath) + if(P.make_hud_button) + if(!src.ability_master) + src.ability_master = new /obj/screen/movable/ability_master(src) + src.ability_master.add_ling_ability( + object_given = src, + verb_given = P.verbpath, + name_given = P.name, + ability_icon_given = P.ability_icon_state, + arguments = list() + ) + + for(var/language in languages) + mind.changeling.absorbed_languages |= language + + var/mob/living/carbon/human/H = src + if(istype(H)) + var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. + var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.identifying_gender, H.flavor_texts, H.modifiers) + absorbDNA(newDNA) + + return 1 + +//removes our changeling verbs +/mob/proc/remove_changeling_powers() + if(!mind || !mind.changeling) return + for(var/datum/power/changeling/P in mind.changeling.purchased_powers) + if(P.isVerb) + verbs.Remove(P.verbpath) + var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath) + if(C) + ability_master.remove_ability(C) + + +//Helper proc. Does all the checks and stuff for us to avoid copypasta +/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0) + + if(!src.mind) return + if(!iscarbon(src)) return + + var/datum/changeling/changeling = src.mind.changeling + if(!changeling) + to_world_log("[src] has the changeling_transform() verb but is not a changeling.") + return + + if(src.stat > max_stat) + to_chat(src, "We are incapacitated.") + return + + if(changeling.absorbed_dna.len < required_dna) + to_chat(src, "We require at least [required_dna] samples of compatible DNA.") + return + + if(changeling.chem_charges < required_chems) + to_chat(src, "We require at least [required_chems] units of chemicals to do that!") + return + + if(changeling.geneticdamage > max_genetic_damage) + to_chat(src, "Our genomes are still reassembling. We need time to recover first.") + return + + return changeling + +//Used to dump the languages from the changeling datum into the actual mob. +/mob/proc/changeling_update_languages(var/updated_languages) + languages = list() + for(var/language in updated_languages) + languages += language + + //This isn't strictly necessary but just to be safe... + add_language("Changeling") + + ////////// + //STINGS// //They get a pretty header because there's just so fucking many of them ;_; + ////////// + +/turf/proc/AdjacentTurfsRangedSting() + //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke + var/list/allowed = list( + /obj/structure/table, + /obj/structure/closet, + /obj/structure/frame, + /obj/structure/target_stake, + /obj/structure/cable, + /obj/structure/disposalpipe, + /obj/machinery, + /mob + ) + + var/L[] = new() + for(var/turf/simulated/t in oview(src,1)) + var/add = 1 + if(t.density) + add = 0 + if(add && LinkBlocked(src,t)) + add = 0 + if(add && TurfBlockedNonWindow(t)) + add = 0 + for(var/obj/O in t) + if(O.density) + add = 0 + break + if(istype(O, /obj/machinery/door)) + //not sure why this doesn't fire on LinkBlocked() + add = 0 + break + for(var/type in allowed) + if (istype(O, type)) + add = 1 + break + if(!add) + break + if(add) + L.Add(t) + return L + + +/mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) + if(M.loc == src.loc) + return 1 //target and source are in the same thing + if(!isturf(src.loc) || !isturf(M.loc)) + to_chat(src, "We cannot reach \the [M] with a sting!") + return 0 //One is inside, the other is outside something. + // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising + if(!AStar(src.loc, M.loc, /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail + to_chat(src, "We cannot find a path to sting \the [M] by!") + return 0 + return 1 + +//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) +/mob/proc/changeling_sting(var/required_chems=0, var/verb_path) + var/datum/changeling/changeling = changeling_power(required_chems) + if(!changeling) return + + var/list/victims = list() + for(var/mob/living/carbon/C in oview(changeling.sting_range)) + victims += C + var/mob/living/carbon/T = tgui_input_list(src, "Who will we sting?", "Sting!", victims) + + if(!T) + return + if(T.isSynthetic()) + to_chat(src, "We are unable to pierce the outer shell of [T].") + return + if(!(T in view(changeling.sting_range))) return + if(!sting_can_reach(T, changeling.sting_range)) return + if(!changeling_power(required_chems)) return + + changeling.chem_charges -= required_chems + changeling.sting_range = 1 + src.verbs -= verb_path + spawn(10) src.verbs += verb_path + + to_chat(src, "We stealthily sting [T].") + if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting + to_chat(T, "You feel a tiny prick.") + return diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index df162f74ca2..454a2efbddd 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,10 +1,10 @@ -/datum/game_mode/cult - name = "Cult" - round_description = "Some crewmembers are attempting to start a cult!" - extended_round_description = "The station has been infiltrated by a fanatical group of death-cultists! They will use powers from beyond your comprehension to subvert you to their cause and ultimately please their gods through sacrificial summons and physical immolation! Try to survive!" - config_tag = "cult" - required_players = 5 - required_players_secret = 5 - required_enemies = 3 - end_on_antag_death = 0 - antag_tags = list(MODE_CULTIST) +/datum/game_mode/cult + name = "Cult" + round_description = "Some crewmembers are attempting to start a cult!" + extended_round_description = "The station has been infiltrated by a fanatical group of death-cultists! They will use powers from beyond your comprehension to subvert you to their cause and ultimately please their gods through sacrificial summons and physical immolation! Try to survive!" + config_tag = "cult" + required_players = 5 + required_players_secret = 5 + required_enemies = 3 + end_on_antag_death = 0 + antag_tags = list(MODE_CULTIST) diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 3622998eef6..e54bb88eefe 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,125 +1,125 @@ -/obj/item/weapon/melee/cultblade - name = "cult blade" - desc = "An arcane weapon wielded by the followers of Nar-Sie." - icon_state = "cultblade" - origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1) - w_class = ITEMSIZE_LARGE - force = 30 - throwforce = 10 - hitsound = 'sound/weapons/bladeslice.ogg' - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - edge = TRUE - sharp = TRUE - -/obj/item/weapon/melee/cultblade/cultify() - return - -/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) - if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) - return ..() - - var/zone = (user.hand ? "l_arm":"r_arm") - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/external/affecting = H.get_organ(zone) - to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!") - //random amount of damage between half of the blade's force and the full force of the blade. - user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp = TRUE, edge = TRUE) - user.Weaken(5) - else if(!istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!") - else - to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.") - - user.drop_from_inventory(src, src.loc) - throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed) - - var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg') - playsound(src, spooky, 50, 1) - - return 1 - -/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob) - if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.") - user.make_dizzy(120) - if(istype(user, /mob/living/simple_mob/construct)) - to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.") - -/obj/item/clothing/head/culthood - name = "cult hood" - icon_state = "culthood" - desc = "A hood worn by the followers of Nar-Sie." - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - flags_inv = HIDEFACE - body_parts_covered = HEAD - armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/head/culthood/cultify() - return - -/obj/item/clothing/head/culthood/magus - name = "magus helm" - icon_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." - flags_inv = HIDEFACE | BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/culthood/alt - icon_state = "cult_hoodalt" - -/obj/item/clothing/suit/cultrobes - name = "cult robes" - desc = "A set of armored robes worn by the followers of Nar-Sie." - icon_state = "cultrobes" - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) - flags_inv = HIDEJUMPSUIT - siemens_coefficient = 0 - -/obj/item/clothing/suit/cultrobes/cultify() - return - -/obj/item/clothing/suit/cultrobes/alt - icon_state = "cultrobesalt" - -/obj/item/clothing/suit/cultrobes/magusred - name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie." - icon_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - -/obj/item/clothing/head/helmet/space/cult - name = "cult helmet" - desc = "A space worthy helmet used by the followers of Nar-Sie." - icon_state = "cult_helmet" - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0 - -/obj/item/clothing/head/helmet/space/cult/cultify() - return - -/obj/item/clothing/suit/space/cult - name = "cult armour" - icon_state = "cult_armour" - origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) - desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy." - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/emergency/oxygen,/obj/item/device/suit_cooling_unit) - slowdown = 0.5 - armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0 - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - -/obj/item/clothing/suit/space/cult/cultify() - return +/obj/item/weapon/melee/cultblade + name = "cult blade" + desc = "An arcane weapon wielded by the followers of Nar-Sie." + icon_state = "cultblade" + origin_tech = list(TECH_COMBAT = 1, TECH_ARCANE = 1) + w_class = ITEMSIZE_LARGE + force = 30 + throwforce = 10 + hitsound = 'sound/weapons/bladeslice.ogg' + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + edge = TRUE + sharp = TRUE + +/obj/item/weapon/melee/cultblade/cultify() + return + +/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) + if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) + return ..() + + var/zone = (user.hand ? "l_arm":"r_arm") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/affecting = H.get_organ(zone) + to_chat(user, "An inexplicable force rips through your [affecting.name], tearing the sword from your grasp!") + //random amount of damage between half of the blade's force and the full force of the blade. + user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp = TRUE, edge = TRUE) + user.Weaken(5) + else if(!istype(user, /mob/living/simple_mob/construct)) + to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!") + else + to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.") + + user.drop_from_inventory(src, src.loc) + throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed) + + var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg') + playsound(src, spooky, 50, 1) + + return 1 + +/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob) + if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) + to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.") + user.make_dizzy(120) + if(istype(user, /mob/living/simple_mob/construct)) + to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.") + +/obj/item/clothing/head/culthood + name = "cult hood" + icon_state = "culthood" + desc = "A hood worn by the followers of Nar-Sie." + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + flags_inv = HIDEFACE + body_parts_covered = HEAD + armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/head/culthood/cultify() + return + +/obj/item/clothing/head/culthood/magus + name = "magus helm" + icon_state = "magus" + desc = "A helm worn by the followers of Nar-Sie." + flags_inv = HIDEFACE | BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/culthood/alt + icon_state = "cult_hoodalt" + +/obj/item/clothing/suit/cultrobes + name = "cult robes" + desc = "A set of armored robes worn by the followers of Nar-Sie." + icon_state = "cultrobes" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50, energy = 80, bomb = 25, bio = 10, rad = 0) + flags_inv = HIDEJUMPSUIT + siemens_coefficient = 0 + +/obj/item/clothing/suit/cultrobes/cultify() + return + +/obj/item/clothing/suit/cultrobes/alt + icon_state = "cultrobesalt" + +/obj/item/clothing/suit/cultrobes/magusred + name = "magus robes" + desc = "A set of armored robes worn by the followers of Nar-Sie." + icon_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + +/obj/item/clothing/head/helmet/space/cult + name = "cult helmet" + desc = "A space worthy helmet used by the followers of Nar-Sie." + icon_state = "cult_helmet" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0 + +/obj/item/clothing/head/helmet/space/cult/cultify() + return + +/obj/item/clothing/suit/space/cult + name = "cult armour" + icon_state = "cult_armour" + origin_tech = list(TECH_MATERIAL = 3, TECH_ARCANE = 1) + desc = "A bulky suit of armour, bristling with spikes. It looks space-worthy." + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/weapon/book/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/emergency/oxygen,/obj/item/device/suit_cooling_unit) + slowdown = 0.5 + armor = list(melee = 60, bullet = 50, laser = 30, energy = 80, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0 + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + +/obj/item/clothing/suit/space/cult/cultify() + return diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index c0d7cfd7a48..9a9540784e5 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -1,173 +1,173 @@ -/obj/structure/cult - density = TRUE - anchored = TRUE - icon = 'icons/obj/cult.dmi' - -/obj/structure/cult/cultify() - return - -/obj/structure/cult/talisman - name = "Altar" - desc = "A bloodstained altar dedicated to Nar-Sie." - icon_state = "talismanaltar" - - -/obj/structure/cult/forge - name = "Daemon forge" - desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie." - icon_state = "forge" - -/obj/structure/cult/pylon - name = "Pylon" - desc = "A floating crystal that hums with an unearthly energy." - icon_state = "pylon" - var/isbroken = 0 - light_range = 5 - light_color = "#3e0000" - var/obj/item/wepon = null - - var/shatter_message = "The pylon shatters!" - var/impact_sound = 'sound/effects/Glasshit.ogg' - var/shatter_sound = 'sound/effects/Glassbr3.ogg' - - var/activation_cooldown = 30 SECONDS - var/last_activation = 0 - -/obj/structure/cult/pylon/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/structure/cult/pylon/attack_hand(mob/M as mob) - attackpylon(M, 5) - -/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) - attackpylon(user, damage) - -/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) - attackpylon(user, W.force) - -/obj/structure/cult/pylon/take_damage(var/damage) - pylonhit(damage) - -/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) - pylonhit(Proj.get_structure_damage()) - -/obj/structure/cult/pylon/proc/pylonhit(var/damage) - if(!isbroken) - if(prob(1+ damage * 5)) - visible_message("[shatter_message]") - STOP_PROCESSING(SSobj, src) - playsound(src,shatter_sound, 75, 1) - isbroken = 1 - density = FALSE - icon_state = "[initial(icon_state)]-broken" - set_light(0) - -/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage) - if(!isbroken) - if(prob(1+ damage * 5)) - user.visible_message( - "[user] smashed \the [src]!", - "You hit \the [src], and its crystal breaks apart!", - "You hear a tinkle of crystal shards." - ) - STOP_PROCESSING(SSobj, src) - user.do_attack_animation(src) - playsound(src,shatter_sound, 75, 1) - isbroken = 1 - density = FALSE - icon_state = "[initial(icon_state)]-broken" - set_light(0) - else - to_chat(user, "You hit \the [src]!") - playsound(src,impact_sound, 75, 1) - else - if(prob(damage * 2)) - to_chat(user, "You pulverize what was left of \the [src]!") - qdel(src) - else - to_chat(user, "You hit \the [src]!") - playsound(src,impact_sound, 75, 1) - -/obj/structure/cult/pylon/proc/repair(mob/user as mob) - if(isbroken) - START_PROCESSING(SSobj, src) - to_chat(user, "You repair \the [src].") - isbroken = 0 - density = TRUE - icon_state = initial(icon_state) - set_light(5) - -// Returns 1 if the pylon does something special. -/obj/structure/cult/pylon/proc/pylon_unique() - last_activation = world.time - return 0 - -/obj/structure/cult/pylon/process() - if(!isbroken && (last_activation < world.time + activation_cooldown) && pylon_unique()) - flick("[initial(icon_state)]-surge",src) - -/obj/structure/cult/tome - name = "Desk" - desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl." - icon_state = "tomealtar" - -//sprites for this no longer exist -Pete -//(they were stolen from another game anyway) -/* -/obj/structure/cult/pillar - name = "Pillar" - desc = "This should not exist" - icon_state = "pillar" - icon = 'magic_pillar.dmi' -*/ - -/obj/effect/gateway - name = "gateway" - desc = "You're pretty sure that abyss is staring back." - icon = 'icons/obj/cult.dmi' - icon_state = "hole" - density = TRUE - unacidable = TRUE - anchored = TRUE - var/spawnable = null - -/obj/effect/gateway/active - light_range=5 - light_color="#ff0000" - spawnable=list( - /mob/living/simple_mob/animal/space/bats, - /mob/living/simple_mob/creature, - /mob/living/simple_mob/faithless - ) - -/obj/effect/gateway/active/cult - light_range=5 - light_color="#ff0000" - spawnable=list( - /mob/living/simple_mob/animal/space/bats/cult, - /mob/living/simple_mob/creature/cult, - /mob/living/simple_mob/faithless/cult - ) - -/obj/effect/gateway/active/cult/cultify() - return - -/obj/effect/gateway/active/Initialize() - addtimer(CALLBACK(src, PROC_REF(spawn_and_qdel)), rand(30, 60) SECONDS) - -/obj/effect/gateway/active/proc/spawn_and_qdel() - if(LAZYLEN(spawnable)) - var/t = pick(spawnable) - new t(get_turf(src)) - qdel(src) - -/obj/effect/gateway/active/Crossed(var/atom/A) - if(A.is_incorporeal()) - return - if(!istype(A, /mob/living)) - return - - var/mob/living/M = A - - to_chat(M, "Walking into \the [src] is probably a bad idea, you think.") +/obj/structure/cult + density = TRUE + anchored = TRUE + icon = 'icons/obj/cult.dmi' + +/obj/structure/cult/cultify() + return + +/obj/structure/cult/talisman + name = "Altar" + desc = "A bloodstained altar dedicated to Nar-Sie." + icon_state = "talismanaltar" + + +/obj/structure/cult/forge + name = "Daemon forge" + desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie." + icon_state = "forge" + +/obj/structure/cult/pylon + name = "Pylon" + desc = "A floating crystal that hums with an unearthly energy." + icon_state = "pylon" + var/isbroken = 0 + light_range = 5 + light_color = "#3e0000" + var/obj/item/wepon = null + + var/shatter_message = "The pylon shatters!" + var/impact_sound = 'sound/effects/Glasshit.ogg' + var/shatter_sound = 'sound/effects/Glassbr3.ogg' + + var/activation_cooldown = 30 SECONDS + var/last_activation = 0 + +/obj/structure/cult/pylon/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/structure/cult/pylon/attack_hand(mob/M as mob) + attackpylon(M, 5) + +/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) + attackpylon(user, damage) + +/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) + attackpylon(user, W.force) + +/obj/structure/cult/pylon/take_damage(var/damage) + pylonhit(damage) + +/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) + pylonhit(Proj.get_structure_damage()) + +/obj/structure/cult/pylon/proc/pylonhit(var/damage) + if(!isbroken) + if(prob(1+ damage * 5)) + visible_message("[shatter_message]") + STOP_PROCESSING(SSobj, src) + playsound(src,shatter_sound, 75, 1) + isbroken = 1 + density = FALSE + icon_state = "[initial(icon_state)]-broken" + set_light(0) + +/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage) + if(!isbroken) + if(prob(1+ damage * 5)) + user.visible_message( + "[user] smashed \the [src]!", + "You hit \the [src], and its crystal breaks apart!", + "You hear a tinkle of crystal shards." + ) + STOP_PROCESSING(SSobj, src) + user.do_attack_animation(src) + playsound(src,shatter_sound, 75, 1) + isbroken = 1 + density = FALSE + icon_state = "[initial(icon_state)]-broken" + set_light(0) + else + to_chat(user, "You hit \the [src]!") + playsound(src,impact_sound, 75, 1) + else + if(prob(damage * 2)) + to_chat(user, "You pulverize what was left of \the [src]!") + qdel(src) + else + to_chat(user, "You hit \the [src]!") + playsound(src,impact_sound, 75, 1) + +/obj/structure/cult/pylon/proc/repair(mob/user as mob) + if(isbroken) + START_PROCESSING(SSobj, src) + to_chat(user, "You repair \the [src].") + isbroken = 0 + density = TRUE + icon_state = initial(icon_state) + set_light(5) + +// Returns 1 if the pylon does something special. +/obj/structure/cult/pylon/proc/pylon_unique() + last_activation = world.time + return 0 + +/obj/structure/cult/pylon/process() + if(!isbroken && (last_activation < world.time + activation_cooldown) && pylon_unique()) + flick("[initial(icon_state)]-surge",src) + +/obj/structure/cult/tome + name = "Desk" + desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl." + icon_state = "tomealtar" + +//sprites for this no longer exist -Pete +//(they were stolen from another game anyway) +/* +/obj/structure/cult/pillar + name = "Pillar" + desc = "This should not exist" + icon_state = "pillar" + icon = 'magic_pillar.dmi' +*/ + +/obj/effect/gateway + name = "gateway" + desc = "You're pretty sure that abyss is staring back." + icon = 'icons/obj/cult.dmi' + icon_state = "hole" + density = TRUE + unacidable = TRUE + anchored = TRUE + var/spawnable = null + +/obj/effect/gateway/active + light_range=5 + light_color="#ff0000" + spawnable=list( + /mob/living/simple_mob/animal/space/bats, + /mob/living/simple_mob/creature, + /mob/living/simple_mob/faithless + ) + +/obj/effect/gateway/active/cult + light_range=5 + light_color="#ff0000" + spawnable=list( + /mob/living/simple_mob/animal/space/bats/cult, + /mob/living/simple_mob/creature/cult, + /mob/living/simple_mob/faithless/cult + ) + +/obj/effect/gateway/active/cult/cultify() + return + +/obj/effect/gateway/active/Initialize() + addtimer(CALLBACK(src, PROC_REF(spawn_and_qdel)), rand(30, 60) SECONDS) + +/obj/effect/gateway/active/proc/spawn_and_qdel() + if(LAZYLEN(spawnable)) + var/t = pick(spawnable) + new t(get_turf(src)) + qdel(src) + +/obj/effect/gateway/active/Crossed(var/atom/A) + if(A.is_incorporeal()) + return + if(!istype(A, /mob/living)) + return + + var/mob/living/M = A + + to_chat(M, "Walking into \the [src] is probably a bad idea, you think.") diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index 9c63b8857e8..9c5eb4458f4 100644 --- a/code/game/gamemodes/cult/cultify/mob.dm +++ b/code/game/gamemodes/cult/cultify/mob.dm @@ -1,63 +1,63 @@ -/mob - //thou shall always be able to see the Geometer of Blood - var/image/narsimage = null - var/image/narglow = null - -/mob/proc/cultify() - return - -/mob/observer/dead/cultify() - if(icon_state != "ghost-narsie") - icon = 'icons/mob/mob.dmi' - icon_state = "ghost-narsie" - overlays = 0 - invisibility = 0 - to_chat(src, "Even as a non-corporal being, you can feel Nar-Sie's presence altering you. You are now visible to everyone.") - -/mob/living/cultify() - if(iscultist(src) && client) - var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) - mind.transfer_to(C) - to_chat(C, "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
    Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
    You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
    ") - dust() - else if(client) - var/mob/observer/dead/G = (ghostize()) - G.icon = 'icons/mob/mob.dmi' - G.icon_state = "ghost-narsie" - G.overlays = 0 - G.invisibility = 0 - to_chat(G, "You feel relieved as what's left of your soul finally escapes its prison of flesh.") - - cult.harvested += G.mind - else - dust() - -/mob/proc/see_narsie(var/obj/singularity/narsie/large/N, var/dir) - if(N.chained) - if(narsimage) - qdel(narsimage) - qdel(narglow) - return - if((N.z == src.z)&&(get_dist(N,src) <= (N.consume_range+10)) && !(N in view(src))) - if(!narsimage) //Create narsimage - narsimage = image('icons/obj/narsie.dmi',src.loc,"narsie",9,1) - narsimage.mouse_opacity = 0 - if(!narglow) //Create narglow - narglow = image('icons/obj/narsie.dmi',narsimage.loc,"glow-narsie",12,1) - narglow.mouse_opacity = 0 - //Else if no dir is given, simply send them the image of narsie - var/new_x = 32 * (N.x - src.x) + N.pixel_x - var/new_y = 32 * (N.y - src.y) + N.pixel_y - narsimage.pixel_x = new_x - narsimage.pixel_y = new_y - narglow.pixel_x = new_x - narglow.pixel_y = new_y - narsimage.loc = src.loc - narglow.loc = src.loc - //Display the new narsimage to the player - src << narsimage - src << narglow - else - if(narsimage) - qdel(narsimage) - qdel(narglow) +/mob + //thou shall always be able to see the Geometer of Blood + var/image/narsimage = null + var/image/narglow = null + +/mob/proc/cultify() + return + +/mob/observer/dead/cultify() + if(icon_state != "ghost-narsie") + icon = 'icons/mob/mob.dmi' + icon_state = "ghost-narsie" + overlays = 0 + invisibility = 0 + to_chat(src, "Even as a non-corporal being, you can feel Nar-Sie's presence altering you. You are now visible to everyone.") + +/mob/living/cultify() + if(iscultist(src) && client) + var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) + mind.transfer_to(C) + to_chat(C, "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
    Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
    You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
    ") + dust() + else if(client) + var/mob/observer/dead/G = (ghostize()) + G.icon = 'icons/mob/mob.dmi' + G.icon_state = "ghost-narsie" + G.overlays = 0 + G.invisibility = 0 + to_chat(G, "You feel relieved as what's left of your soul finally escapes its prison of flesh.") + + cult.harvested += G.mind + else + dust() + +/mob/proc/see_narsie(var/obj/singularity/narsie/large/N, var/dir) + if(N.chained) + if(narsimage) + qdel(narsimage) + qdel(narglow) + return + if((N.z == src.z)&&(get_dist(N,src) <= (N.consume_range+10)) && !(N in view(src))) + if(!narsimage) //Create narsimage + narsimage = image('icons/obj/narsie.dmi',src.loc,"narsie",9,1) + narsimage.mouse_opacity = 0 + if(!narglow) //Create narglow + narglow = image('icons/obj/narsie.dmi',narsimage.loc,"glow-narsie",12,1) + narglow.mouse_opacity = 0 + //Else if no dir is given, simply send them the image of narsie + var/new_x = 32 * (N.x - src.x) + N.pixel_x + var/new_y = 32 * (N.y - src.y) + N.pixel_y + narsimage.pixel_x = new_x + narsimage.pixel_y = new_y + narglow.pixel_x = new_x + narglow.pixel_y = new_y + narsimage.loc = src.loc + narglow.loc = src.loc + //Display the new narsimage to the player + src << narsimage + src << narglow + else + if(narsimage) + qdel(narsimage) + qdel(narglow) diff --git a/code/game/gamemodes/cult/cultify/obj.dm b/code/game/gamemodes/cult/cultify/obj.dm index 91dabc556fa..e00dbc6aad5 100644 --- a/code/game/gamemodes/cult/cultify/obj.dm +++ b/code/game/gamemodes/cult/cultify/obj.dm @@ -1,139 +1,139 @@ -/obj/proc/cultify() - qdel(src) - -/obj/effect/decal/cleanable/blood/cultify() - return - -/obj/effect/decal/remains/cultify() - return - -/obj/effect/overlay/cultify() - return - -/obj/item/device/flashlight/lamp/cultify() - new /obj/structure/cult/pylon(loc) - ..() - -/obj/item/stack/material/wood/cultify() - return - -/obj/item/weapon/book/cultify() - new /obj/item/weapon/book/tome(loc) - ..() - -/obj/item/weapon/material/sword/cultify() - new /obj/item/weapon/melee/cultblade(loc) - ..() - -/obj/item/weapon/storage/backpack/cultify() - new /obj/item/weapon/storage/backpack/cultpack(loc) - ..() - -/obj/item/weapon/storage/backpack/cultpack/cultify() - return - -/obj/machinery/cultify() - // We keep the number of cultified machines down by only converting those that are dense - // The alternative is to keep a separate file of exceptions. - if(density) - var/list/random_structure = list( - /obj/structure/cult/talisman, - /obj/structure/cult/forge, - /obj/structure/cult/tome - ) - var/I = pick(random_structure) - new I(loc) - ..() - -/obj/machinery/atmospherics/cultify() - if(src.invisibility != INVISIBILITY_MAXIMUM) - src.invisibility = INVISIBILITY_MAXIMUM - density = FALSE - -/obj/machinery/appliance/cooker/cultify() - new /obj/structure/cult/talisman(loc) - qdel(src) - -/obj/machinery/computer/cultify() - new /obj/structure/cult/tome(loc) - qdel(src) - -/obj/machinery/door/airlock/external/cultify() - new /obj/structure/simple_door/wood(loc) - ..() - -/obj/machinery/door/cultify() - if(invisibility != INVISIBILITY_MAXIMUM) - invisibility = INVISIBILITY_MAXIMUM - density = FALSE - anim(target = src, a_icon = 'icons/effects/effects.dmi', a_icon_state = "breakdoor", sleeptime = 10) - qdel(src) - -/obj/machinery/door/firedoor/cultify() - qdel(src) - -/obj/machinery/light/cultify() - new /obj/structure/cult/pylon(loc) - qdel(src) - -/obj/machinery/mech_sensor/cultify() - qdel(src) - -/obj/machinery/power/apc/cultify() - if(src.invisibility != INVISIBILITY_MAXIMUM) - src.invisibility = INVISIBILITY_MAXIMUM - -/obj/machinery/vending/cultify() - new /obj/structure/cult/forge(loc) - qdel(src) - -/obj/structure/bed/chair/cultify() - var/obj/structure/bed/chair/wood/wings/I = new(loc) - I.dir = dir - ..() - -/obj/structure/bed/chair/wood/cultify() - return - -/obj/structure/bookcase/cultify() - return - -/obj/structure/grille/cultify() - new /obj/structure/grille/cult(get_turf(src)) - ..() - -/obj/structure/grille/cult/cultify() - return - -/obj/structure/simple_door/cultify() - new /obj/structure/simple_door/wood(loc) - ..() - -/obj/structure/simple_door/wood/cultify() - return - -/obj/singularity/cultify() - var/dist = max((current_size - 2), 1) - explosion(get_turf(src), dist, dist * 2, dist * 4) - qdel(src) - -/obj/structure/shuttle/engine/heater/cultify() - new /obj/structure/cult/pylon(loc) - ..() - -/obj/structure/shuttle/engine/propulsion/cultify() - var/turf/T = get_turf(src) - if(T) - T.ChangeTurf(/turf/simulated/wall/cult) - ..() - -/obj/structure/table/cultify() - // Make it a wood-reinforced wooden table. - // There are cult materials available, but it'd make the table non-deconstructable with how holotables work. - // Could possibly use a new material var for holographic-ness? - material = get_material_by_name("wood") - reinforced = get_material_by_name("wood") - update_desc() - update_connections(1) - update_icon() - update_material() +/obj/proc/cultify() + qdel(src) + +/obj/effect/decal/cleanable/blood/cultify() + return + +/obj/effect/decal/remains/cultify() + return + +/obj/effect/overlay/cultify() + return + +/obj/item/device/flashlight/lamp/cultify() + new /obj/structure/cult/pylon(loc) + ..() + +/obj/item/stack/material/wood/cultify() + return + +/obj/item/weapon/book/cultify() + new /obj/item/weapon/book/tome(loc) + ..() + +/obj/item/weapon/material/sword/cultify() + new /obj/item/weapon/melee/cultblade(loc) + ..() + +/obj/item/weapon/storage/backpack/cultify() + new /obj/item/weapon/storage/backpack/cultpack(loc) + ..() + +/obj/item/weapon/storage/backpack/cultpack/cultify() + return + +/obj/machinery/cultify() + // We keep the number of cultified machines down by only converting those that are dense + // The alternative is to keep a separate file of exceptions. + if(density) + var/list/random_structure = list( + /obj/structure/cult/talisman, + /obj/structure/cult/forge, + /obj/structure/cult/tome + ) + var/I = pick(random_structure) + new I(loc) + ..() + +/obj/machinery/atmospherics/cultify() + if(src.invisibility != INVISIBILITY_MAXIMUM) + src.invisibility = INVISIBILITY_MAXIMUM + density = FALSE + +/obj/machinery/appliance/cooker/cultify() + new /obj/structure/cult/talisman(loc) + qdel(src) + +/obj/machinery/computer/cultify() + new /obj/structure/cult/tome(loc) + qdel(src) + +/obj/machinery/door/airlock/external/cultify() + new /obj/structure/simple_door/wood(loc) + ..() + +/obj/machinery/door/cultify() + if(invisibility != INVISIBILITY_MAXIMUM) + invisibility = INVISIBILITY_MAXIMUM + density = FALSE + anim(target = src, a_icon = 'icons/effects/effects.dmi', a_icon_state = "breakdoor", sleeptime = 10) + qdel(src) + +/obj/machinery/door/firedoor/cultify() + qdel(src) + +/obj/machinery/light/cultify() + new /obj/structure/cult/pylon(loc) + qdel(src) + +/obj/machinery/mech_sensor/cultify() + qdel(src) + +/obj/machinery/power/apc/cultify() + if(src.invisibility != INVISIBILITY_MAXIMUM) + src.invisibility = INVISIBILITY_MAXIMUM + +/obj/machinery/vending/cultify() + new /obj/structure/cult/forge(loc) + qdel(src) + +/obj/structure/bed/chair/cultify() + var/obj/structure/bed/chair/wood/wings/I = new(loc) + I.dir = dir + ..() + +/obj/structure/bed/chair/wood/cultify() + return + +/obj/structure/bookcase/cultify() + return + +/obj/structure/grille/cultify() + new /obj/structure/grille/cult(get_turf(src)) + ..() + +/obj/structure/grille/cult/cultify() + return + +/obj/structure/simple_door/cultify() + new /obj/structure/simple_door/wood(loc) + ..() + +/obj/structure/simple_door/wood/cultify() + return + +/obj/singularity/cultify() + var/dist = max((current_size - 2), 1) + explosion(get_turf(src), dist, dist * 2, dist * 4) + qdel(src) + +/obj/structure/shuttle/engine/heater/cultify() + new /obj/structure/cult/pylon(loc) + ..() + +/obj/structure/shuttle/engine/propulsion/cultify() + var/turf/T = get_turf(src) + if(T) + T.ChangeTurf(/turf/simulated/wall/cult) + ..() + +/obj/structure/table/cultify() + // Make it a wood-reinforced wooden table. + // There are cult materials available, but it'd make the table non-deconstructable with how holotables work. + // Could possibly use a new material var for holographic-ness? + material = get_material_by_name("wood") + reinforced = get_material_by_name("wood") + update_desc() + update_connections(1) + update_icon() + update_material() diff --git a/code/game/gamemodes/cult/cultify/turf.dm b/code/game/gamemodes/cult/cultify/turf.dm index 4506f49fccd..8cfd85886a7 100644 --- a/code/game/gamemodes/cult/cultify/turf.dm +++ b/code/game/gamemodes/cult/cultify/turf.dm @@ -1,42 +1,42 @@ -/turf/proc/cultify() - ChangeTurf(/turf/space) - return - -/turf/simulated/floor/cultify() - //todo: flooring datum cultify check - cultify_floor() - -/turf/simulated/shuttle/floor/cultify() - cultify_floor() - -/turf/simulated/shuttle/floor4/cultify() - cultify_floor() - -/turf/simulated/shuttle/wall/cultify() - cultify_wall() - -/turf/simulated/wall/cultify() - cultify_wall() - -/turf/simulated/wall/cult/cultify() - return - -/turf/unsimulated/wall/cult/cultify() - return - -/turf/unsimulated/beach/cultify() - return - -/turf/unsimulated/floor/cultify() - cultify_floor() - -/turf/unsimulated/wall/cultify() - cultify_wall() - -/turf/proc/cultify_floor() - if((icon_state != "cult")&&(icon_state != "cult-narsie")) - name = "engraved floor" - icon_state = "cult" - -/turf/proc/cultify_wall() - ChangeTurf(/turf/unsimulated/wall/cult) +/turf/proc/cultify() + ChangeTurf(/turf/space) + return + +/turf/simulated/floor/cultify() + //todo: flooring datum cultify check + cultify_floor() + +/turf/simulated/shuttle/floor/cultify() + cultify_floor() + +/turf/simulated/shuttle/floor4/cultify() + cultify_floor() + +/turf/simulated/shuttle/wall/cultify() + cultify_wall() + +/turf/simulated/wall/cultify() + cultify_wall() + +/turf/simulated/wall/cult/cultify() + return + +/turf/unsimulated/wall/cult/cultify() + return + +/turf/unsimulated/beach/cultify() + return + +/turf/unsimulated/floor/cultify() + cultify_floor() + +/turf/unsimulated/wall/cultify() + cultify_wall() + +/turf/proc/cultify_floor() + if((icon_state != "cult")&&(icon_state != "cult-narsie")) + name = "engraved floor" + icon_state = "cult" + +/turf/proc/cultify_wall() + ChangeTurf(/turf/unsimulated/wall/cult) diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index 0cf1e2d0fd6..d558a8bfbb2 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -1,84 +1,84 @@ -/* - -In short: - * Random gateways spawning hellmonsters - * Broken Fire Alarms - * Random tiles changing to culty tiles. - -*/ -/datum/universal_state/hell - name = "Hell Rising" - desc = "OH FUCK OH FUCK OH FUCK" - - decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) - -/datum/universal_state/hell/OnShuttleCall(var/mob/user) - return 1 - /* - if(user) - to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") - return 0 - */ - -/datum/universal_state/hell/DecayTurf(var/turf/T) - if(!T.holy) - T.cultify() - for(var/obj/machinery/light/L in T.contents) - new /obj/structure/cult/pylon(L.loc) - qdel(L) - return - - -/datum/universal_state/hell/OnTurfChange(var/turf/T) - var/turf/space/S = T - if(istype(S)) - S.color = "#FF0000" - else - S.color = initial(S.color) - -// Apply changes when entering state -/datum/universal_state/hell/OnEnter() - set background = 1 -// garbage_collector.garbage_collect = 0 - - escape_list = get_area_turfs(locate(/area/hallway/secondary/exit)) - - //Separated into separate procs for profiling - AreaSet() - MiscSet() - APCSet() - OverlayAndAmbientSet() - lightsout(0,0) - - runedec += 9000 //basically removing the rune cap - - -/datum/universal_state/hell/proc/AreaSet() - for(var/area/A in world) - if(!istype(A,/area) || istype(A, /area/space)) - continue - - A.update_icon() - -/datum/universal_state/hell/OverlayAndAmbientSet() - spawn(0) - for(var/datum/lighting_corner/L in world) - L.update_lumcount(1, 0, 0) - - for(var/turf/space/T in world) - OnTurfChange(T) - -/datum/universal_state/hell/proc/MiscSet() - for(var/turf/simulated/floor/T in world) - if(!T.holy && prob(1)) - new /obj/effect/gateway/active/cult(T) - - for (var/obj/machinery/firealarm/alm in machines) - if (!(alm.stat & BROKEN)) - alm.ex_act(2) - -/datum/universal_state/hell/proc/APCSet() - for (var/obj/machinery/power/apc/APC in GLOB.apcs) - if (!(APC.stat & BROKEN) && !APC.is_critical) - APC.emagged = 1 - APC.queue_icon_update() +/* + +In short: + * Random gateways spawning hellmonsters + * Broken Fire Alarms + * Random tiles changing to culty tiles. + +*/ +/datum/universal_state/hell + name = "Hell Rising" + desc = "OH FUCK OH FUCK OH FUCK" + + decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) + +/datum/universal_state/hell/OnShuttleCall(var/mob/user) + return 1 + /* + if(user) + to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") + return 0 + */ + +/datum/universal_state/hell/DecayTurf(var/turf/T) + if(!T.holy) + T.cultify() + for(var/obj/machinery/light/L in T.contents) + new /obj/structure/cult/pylon(L.loc) + qdel(L) + return + + +/datum/universal_state/hell/OnTurfChange(var/turf/T) + var/turf/space/S = T + if(istype(S)) + S.color = "#FF0000" + else + S.color = initial(S.color) + +// Apply changes when entering state +/datum/universal_state/hell/OnEnter() + set background = 1 +// garbage_collector.garbage_collect = 0 + + escape_list = get_area_turfs(locate(/area/hallway/secondary/exit)) + + //Separated into separate procs for profiling + AreaSet() + MiscSet() + APCSet() + OverlayAndAmbientSet() + lightsout(0,0) + + runedec += 9000 //basically removing the rune cap + + +/datum/universal_state/hell/proc/AreaSet() + for(var/area/A in world) + if(!istype(A,/area) || istype(A, /area/space)) + continue + + A.update_icon() + +/datum/universal_state/hell/OverlayAndAmbientSet() + spawn(0) + for(var/datum/lighting_corner/L in world) + L.update_lumcount(1, 0, 0) + + for(var/turf/space/T in world) + OnTurfChange(T) + +/datum/universal_state/hell/proc/MiscSet() + for(var/turf/simulated/floor/T in world) + if(!T.holy && prob(1)) + new /obj/effect/gateway/active/cult(T) + + for (var/obj/machinery/firealarm/alm in machines) + if (!(alm.stat & BROKEN)) + alm.ex_act(2) + +/datum/universal_state/hell/proc/APCSet() + for (var/obj/machinery/power/apc/APC in GLOB.apcs) + if (!(APC.stat & BROKEN) && !APC.is_critical) + APC.emagged = 1 + APC.queue_icon_update() diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm index 9c3504924f2..5a8dc60d3f8 100644 --- a/code/game/gamemodes/cult/narsie.dm +++ b/code/game/gamemodes/cult/narsie.dm @@ -1,367 +1,367 @@ -var/global/narsie_behaviour = "CultStation13" -var/global/narsie_cometh = 0 -var/global/list/narsie_list = list() -/obj/singularity/narsie //Moving narsie to its own file for the sake of being clearer - name = "Nar-Sie" - desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees." - icon = 'icons/obj/narsie.dmi' - icon_state = "narsie-small" - pixel_x = -236 - pixel_y = -256 - - current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO. - contained = 0 // Are we going to move around? - dissipate = 0 // Do we lose energy over time? - grav_pull = 10 //How many tiles out do we pull? - consume_range = 3 //How many tiles out do we eat - - -/obj/singularity/narsie/New() - ..() - narsie_list.Add(src) - -/obj/singularity/narsie/Destroy() - narsie_list.Remove(src) - ..() - -/obj/singularity/narsie/large - name = "Nar-Sie" - icon = 'icons/obj/narsie.dmi' - icon_state = "narsie"//mobs perceive the geometer of blood through their see_narsie proc - - // Pixel stuff centers Narsie. - pixel_x = -236 - pixel_y = -256 - light_range = 1 - light_color = "#3e0000" - - current_size = 12 - consume_range = 12 // How many tiles out do we eat. - var/announce=1 - var/cause_hell = 1 - -/obj/singularity/narsie/large/New() - ..() - if(announce) - to_world("[uppertext(name)] HAS RISEN") - world << sound('sound/effects/weather/wind/wind_5_1.ogg') - - narsie_spawn_animation() - - if(!narsie_cometh)//so we don't initiate Hell more than one time. - if(cause_hell) - SetUniversalState(/datum/universal_state/hell) - narsie_cometh = 1 - - spawn(10 SECONDS) - if(emergency_shuttle) - emergency_shuttle.call_evac() - emergency_shuttle.launch_time = 0 // Cannot recall - -/obj/singularity/narsie/process() - eat() - - if (!target || prob(5)) - pickcultist() - - move() - - if (prob(25)) - mezzer() - -/obj/singularity/narsie/large/eat() - for (var/turf/A in orange(consume_range, src)) - consume(A) - -/obj/singularity/narsie/mezzer() - for(var/mob/living/carbon/M in oviewers(8, src)) - if(M.stat == CONSCIOUS) - if(M.status_flags & GODMODE) - continue - if(!iscultist(M)) - to_chat(M, " You feel your sanity crumble away in an instant as you gaze upon [src.name]...") - M.apply_effect(3, STUN) - - -/obj/singularity/narsie/large/Bump(atom/A) - if(!cause_hell) return - if(isturf(A)) - narsiewall(A) - else if(istype(A, /obj/structure/cult)) - qdel(A) - -/obj/singularity/narsie/large/Bumped(atom/A) - if(!cause_hell) return - if(isturf(A)) - narsiewall(A) - else if(istype(A, /obj/structure/cult)) - qdel(A) - -/obj/singularity/narsie/move(var/force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) - - spawn(0) - step(src, movement_dir) - spawn(1) - step(src, movement_dir) - return 1 - -/obj/singularity/narsie/large/move(var/force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) - spawn(0) - step(src, movement_dir) - narsiefloor(get_turf(loc)) - for(var/mob/M in player_list) - if(M.client) - M.see_narsie(src,movement_dir) - spawn(10) - step(src, movement_dir) - narsiefloor(get_turf(loc)) - for(var/mob/M in player_list) - if(M.client) - M.see_narsie(src,movement_dir) - return 1 - -/obj/singularity/narsie/proc/narsiefloor(var/turf/T)//leaving "footprints" - if(!(istype(T, /turf/simulated/wall/cult)||istype(T, /turf/space))) - if(T.icon_state != "cult-narsie") - T.desc = "something that goes beyond your understanding went this way." - T.icon = 'icons/turf/flooring/cult.dmi' - T.icon_state = "cult-narsie" - T.set_light(1) - -/obj/singularity/narsie/proc/narsiewall(var/turf/T) - T.desc = "An opening has been made on that wall, but who can say if what you seek truly lies on the other side?" - T.icon = 'icons/turf/walls.dmi' - T.icon_state = "cult-narsie" - T.opacity = 0 - T.density = FALSE - set_light(1) - -/obj/singularity/narsie/large/consume(const/atom/A) //Has its own consume proc because it doesn't need energy and I don't want BoHs to explode it. --NEO -//NEW BEHAVIOUR - if(narsie_behaviour == "CultStation13") - //MOB PROCESSING - new_narsie(A) - -//OLD BEHAVIOUR - else if(narsie_behaviour == "Nar-Singulo") - old_narsie(A) - -/obj/singularity/narsie/proc/new_narsie(const/atom/A) - if (istype(A, /mob/) && (get_dist(A, src) <= 7)) - var/mob/M = A - - if(M.status_flags & GODMODE) - return 0 - - M.cultify() - -//ITEM PROCESSING - else if (istype(A, /obj/)) - var/obj/O = A - O.cultify() - -//TURF PROCESSING - else if (isturf(A)) - var/dist = get_dist(A, src) - - for (var/atom/movable/AM in A.contents) - if (dist <= consume_range) - consume(AM) - continue - - if (dist <= consume_range && !istype(A, /turf/space)) - var/turf/T = A - if(T.holy) - T.holy = 0 //Nar-Sie doesn't give a shit about sacred grounds. - T.cultify() - -/obj/singularity/narsie/proc/old_narsie(const/atom/A) - if(!(A.singuloCanEat())) - return 0 - - if (istype(A, /mob/living/)) - var/mob/living/C2 = A - - if(C2.status_flags & GODMODE) - return 0 - - C2.dust() // Changed from gib(), just for less lag. - - else if (istype(A, /obj/)) - qdel(A) - - if (A) - qdel(A) - else if (isturf(A)) - var/dist = get_dist(A, src) - - for (var/atom/movable/AM2 in A.contents) - if (AM2 == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM2) - continue - - if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) - var/turf/T2 = A - T2.ChangeTurf(get_base_turf_by_area(A)) - -/obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. - if(!(A.singuloCanEat())) - return 0 - - if (istype(A, /mob/living/)) - var/mob/living/C2 = A - - if(C2.status_flags & GODMODE) - return 0 - - C2.dust() // Changed from gib(), just for less lag. - - else if (istype(A, /obj/)) - qdel(A) - - if (A) - qdel(A) - else if (isturf(A)) - var/dist = get_dist(A, src) - - for (var/atom/movable/AM2 in A.contents) - if (AM2 == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM2) - continue - - if (dist > consume_range) - if(!(AM2.singuloCanEat())) - continue - - if (101 == AM2.invisibility) - continue - - spawn (0) - AM2.singularity_pull(src, src.current_size) - - if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) - var/turf/T2 = A - T2.ChangeTurf(get_base_turf_by_area(A)) - -/obj/singularity/narsie/ex_act(severity) //No throwing bombs at it either. --NEO - return - -/obj/singularity/narsie/proc/pickcultist() //Narsie rewards his cultists with being devoured first, then picks a ghost to follow. --NEO - var/list/cultists = list() - for(var/datum/mind/cult_nh_mind in cult.current_antagonists) - if(!cult_nh_mind.current) - continue - if(cult_nh_mind.current.stat) - continue - var/turf/pos = get_turf(cult_nh_mind.current) - if(pos.z != src.z) - continue - cultists += cult_nh_mind.current - if(cultists.len) - acquire(pick(cultists)) - return - //If there was living cultists, it picks one to follow. - for(var/mob/living/carbon/human/food in living_mob_list) - if(food.stat) - continue - var/turf/pos = get_turf(food) - if(pos.z != src.z) - continue - cultists += food - if(cultists.len) - acquire(pick(cultists)) - return - //no living cultists, pick a living human instead. - for(var/mob/observer/dead/ghost in player_list) - if(!ghost.client) - continue - var/turf/pos = get_turf(ghost) - if(pos.z != src.z) - continue - cultists += ghost - if(cultists.len) - acquire(pick(cultists)) - return - //no living humans, follow a ghost instead. - -/obj/singularity/narsie/proc/acquire(const/mob/food) - var/capname = uppertext(name) - - to_chat(target, "[capname] HAS LOST INTEREST IN YOU.") - target = food - - if (ishuman(target)) - to_chat(target, "[capname] HUNGERS FOR YOUR SOUL.") - else - to_chat(target, "[capname] HAS CHOSEN YOU TO LEAD HIM TO HIS NEXT MEAL.") - -/obj/singularity/narsie/on_capture() - chained = 1 - move_self = 0 - icon_state ="narsie-small-chains" - -/obj/singularity/narsie/on_release() - chained = 0 - move_self = 1 - icon_state ="narsie-small" - -/obj/singularity/narsie/large/on_capture() - chained = 1 - move_self = 0 - icon_state ="narsie-chains" - for(var/mob/M in mob_list)//removing the client image of nar-sie while it is chained - if(M.client) - M.see_narsie(src) - -/obj/singularity/narsie/large/on_release() - chained = 0 - move_self = 1 - icon_state ="narsie" - -/obj/singularity/narsie/cultify() - return - -/** - * Wizard narsie. - */ -/obj/singularity/narsie/wizard - grav_pull = 0 - -/obj/singularity/narsie/wizard/eat() - for (var/turf/T in trange(consume_range, src)) - consume(T) - -/obj/singularity/narsie/proc/narsie_spawn_animation() - icon = 'icons/obj/narsie_spawn_anim.dmi' - dir = SOUTH - move_self = 0 - flick("narsie_spawn_anim",src) - sleep(11) - move_self = 1 - icon = initial(icon) +var/global/narsie_behaviour = "CultStation13" +var/global/narsie_cometh = 0 +var/global/list/narsie_list = list() +/obj/singularity/narsie //Moving narsie to its own file for the sake of being clearer + name = "Nar-Sie" + desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees." + icon = 'icons/obj/narsie.dmi' + icon_state = "narsie-small" + pixel_x = -236 + pixel_y = -256 + + current_size = 9 //It moves/eats like a max-size singulo, aside from range. --NEO. + contained = 0 // Are we going to move around? + dissipate = 0 // Do we lose energy over time? + grav_pull = 10 //How many tiles out do we pull? + consume_range = 3 //How many tiles out do we eat + + +/obj/singularity/narsie/New() + ..() + narsie_list.Add(src) + +/obj/singularity/narsie/Destroy() + narsie_list.Remove(src) + ..() + +/obj/singularity/narsie/large + name = "Nar-Sie" + icon = 'icons/obj/narsie.dmi' + icon_state = "narsie"//mobs perceive the geometer of blood through their see_narsie proc + + // Pixel stuff centers Narsie. + pixel_x = -236 + pixel_y = -256 + light_range = 1 + light_color = "#3e0000" + + current_size = 12 + consume_range = 12 // How many tiles out do we eat. + var/announce=1 + var/cause_hell = 1 + +/obj/singularity/narsie/large/New() + ..() + if(announce) + to_world("[uppertext(name)] HAS RISEN") + world << sound('sound/effects/weather/wind/wind_5_1.ogg') + + narsie_spawn_animation() + + if(!narsie_cometh)//so we don't initiate Hell more than one time. + if(cause_hell) + SetUniversalState(/datum/universal_state/hell) + narsie_cometh = 1 + + spawn(10 SECONDS) + if(emergency_shuttle) + emergency_shuttle.call_evac() + emergency_shuttle.launch_time = 0 // Cannot recall + +/obj/singularity/narsie/process() + eat() + + if (!target || prob(5)) + pickcultist() + + move() + + if (prob(25)) + mezzer() + +/obj/singularity/narsie/large/eat() + for (var/turf/A in orange(consume_range, src)) + consume(A) + +/obj/singularity/narsie/mezzer() + for(var/mob/living/carbon/M in oviewers(8, src)) + if(M.stat == CONSCIOUS) + if(M.status_flags & GODMODE) + continue + if(!iscultist(M)) + to_chat(M, " You feel your sanity crumble away in an instant as you gaze upon [src.name]...") + M.apply_effect(3, STUN) + + +/obj/singularity/narsie/large/Bump(atom/A) + if(!cause_hell) return + if(isturf(A)) + narsiewall(A) + else if(istype(A, /obj/structure/cult)) + qdel(A) + +/obj/singularity/narsie/large/Bumped(atom/A) + if(!cause_hell) return + if(isturf(A)) + narsiewall(A) + else if(istype(A, /obj/structure/cult)) + qdel(A) + +/obj/singularity/narsie/move(var/force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) + + spawn(0) + step(src, movement_dir) + spawn(1) + step(src, movement_dir) + return 1 + +/obj/singularity/narsie/large/move(var/force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) + spawn(0) + step(src, movement_dir) + narsiefloor(get_turf(loc)) + for(var/mob/M in player_list) + if(M.client) + M.see_narsie(src,movement_dir) + spawn(10) + step(src, movement_dir) + narsiefloor(get_turf(loc)) + for(var/mob/M in player_list) + if(M.client) + M.see_narsie(src,movement_dir) + return 1 + +/obj/singularity/narsie/proc/narsiefloor(var/turf/T)//leaving "footprints" + if(!(istype(T, /turf/simulated/wall/cult)||istype(T, /turf/space))) + if(T.icon_state != "cult-narsie") + T.desc = "something that goes beyond your understanding went this way." + T.icon = 'icons/turf/flooring/cult.dmi' + T.icon_state = "cult-narsie" + T.set_light(1) + +/obj/singularity/narsie/proc/narsiewall(var/turf/T) + T.desc = "An opening has been made on that wall, but who can say if what you seek truly lies on the other side?" + T.icon = 'icons/turf/walls.dmi' + T.icon_state = "cult-narsie" + T.opacity = 0 + T.density = FALSE + set_light(1) + +/obj/singularity/narsie/large/consume(const/atom/A) //Has its own consume proc because it doesn't need energy and I don't want BoHs to explode it. --NEO +//NEW BEHAVIOUR + if(narsie_behaviour == "CultStation13") + //MOB PROCESSING + new_narsie(A) + +//OLD BEHAVIOUR + else if(narsie_behaviour == "Nar-Singulo") + old_narsie(A) + +/obj/singularity/narsie/proc/new_narsie(const/atom/A) + if (istype(A, /mob/) && (get_dist(A, src) <= 7)) + var/mob/M = A + + if(M.status_flags & GODMODE) + return 0 + + M.cultify() + +//ITEM PROCESSING + else if (istype(A, /obj/)) + var/obj/O = A + O.cultify() + +//TURF PROCESSING + else if (isturf(A)) + var/dist = get_dist(A, src) + + for (var/atom/movable/AM in A.contents) + if (dist <= consume_range) + consume(AM) + continue + + if (dist <= consume_range && !istype(A, /turf/space)) + var/turf/T = A + if(T.holy) + T.holy = 0 //Nar-Sie doesn't give a shit about sacred grounds. + T.cultify() + +/obj/singularity/narsie/proc/old_narsie(const/atom/A) + if(!(A.singuloCanEat())) + return 0 + + if (istype(A, /mob/living/)) + var/mob/living/C2 = A + + if(C2.status_flags & GODMODE) + return 0 + + C2.dust() // Changed from gib(), just for less lag. + + else if (istype(A, /obj/)) + qdel(A) + + if (A) + qdel(A) + else if (isturf(A)) + var/dist = get_dist(A, src) + + for (var/atom/movable/AM2 in A.contents) + if (AM2 == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM2) + continue + + if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) + var/turf/T2 = A + T2.ChangeTurf(get_base_turf_by_area(A)) + +/obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. + if(!(A.singuloCanEat())) + return 0 + + if (istype(A, /mob/living/)) + var/mob/living/C2 = A + + if(C2.status_flags & GODMODE) + return 0 + + C2.dust() // Changed from gib(), just for less lag. + + else if (istype(A, /obj/)) + qdel(A) + + if (A) + qdel(A) + else if (isturf(A)) + var/dist = get_dist(A, src) + + for (var/atom/movable/AM2 in A.contents) + if (AM2 == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM2) + continue + + if (dist > consume_range) + if(!(AM2.singuloCanEat())) + continue + + if (101 == AM2.invisibility) + continue + + spawn (0) + AM2.singularity_pull(src, src.current_size) + + if (dist <= consume_range && !istype(A, get_base_turf_by_area(A))) + var/turf/T2 = A + T2.ChangeTurf(get_base_turf_by_area(A)) + +/obj/singularity/narsie/ex_act(severity) //No throwing bombs at it either. --NEO + return + +/obj/singularity/narsie/proc/pickcultist() //Narsie rewards his cultists with being devoured first, then picks a ghost to follow. --NEO + var/list/cultists = list() + for(var/datum/mind/cult_nh_mind in cult.current_antagonists) + if(!cult_nh_mind.current) + continue + if(cult_nh_mind.current.stat) + continue + var/turf/pos = get_turf(cult_nh_mind.current) + if(pos.z != src.z) + continue + cultists += cult_nh_mind.current + if(cultists.len) + acquire(pick(cultists)) + return + //If there was living cultists, it picks one to follow. + for(var/mob/living/carbon/human/food in living_mob_list) + if(food.stat) + continue + var/turf/pos = get_turf(food) + if(pos.z != src.z) + continue + cultists += food + if(cultists.len) + acquire(pick(cultists)) + return + //no living cultists, pick a living human instead. + for(var/mob/observer/dead/ghost in player_list) + if(!ghost.client) + continue + var/turf/pos = get_turf(ghost) + if(pos.z != src.z) + continue + cultists += ghost + if(cultists.len) + acquire(pick(cultists)) + return + //no living humans, follow a ghost instead. + +/obj/singularity/narsie/proc/acquire(const/mob/food) + var/capname = uppertext(name) + + to_chat(target, "[capname] HAS LOST INTEREST IN YOU.") + target = food + + if (ishuman(target)) + to_chat(target, "[capname] HUNGERS FOR YOUR SOUL.") + else + to_chat(target, "[capname] HAS CHOSEN YOU TO LEAD HIM TO HIS NEXT MEAL.") + +/obj/singularity/narsie/on_capture() + chained = 1 + move_self = 0 + icon_state ="narsie-small-chains" + +/obj/singularity/narsie/on_release() + chained = 0 + move_self = 1 + icon_state ="narsie-small" + +/obj/singularity/narsie/large/on_capture() + chained = 1 + move_self = 0 + icon_state ="narsie-chains" + for(var/mob/M in mob_list)//removing the client image of nar-sie while it is chained + if(M.client) + M.see_narsie(src) + +/obj/singularity/narsie/large/on_release() + chained = 0 + move_self = 1 + icon_state ="narsie" + +/obj/singularity/narsie/cultify() + return + +/** + * Wizard narsie. + */ +/obj/singularity/narsie/wizard + grav_pull = 0 + +/obj/singularity/narsie/wizard/eat() + for (var/turf/T in trange(consume_range, src)) + consume(T) + +/obj/singularity/narsie/proc/narsie_spawn_animation() + icon = 'icons/obj/narsie_spawn_anim.dmi' + dir = SOUTH + move_self = 0 + flick("narsie_spawn_anim",src) + sleep(11) + move_self = 1 + icon = initial(icon) diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index dcafc9e48a8..35e1ed8eb92 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,611 +1,611 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -var/cultwords = list() -var/runedec = 0 -var/global/list/engwords = list("travel", "blood", "join", "hell", "destroy", "technology", "self", "see", "other", "hide") -var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") - -/client/proc/check_words() // -- Urist - set category = "Special Verbs" - set name = "Check Rune Words" - set desc = "Check the rune-word meaning" - if(!cultwords["travel"]) - runerandom() - for (var/word in engwords) - to_chat(usr, "[cultwords[word]] is [word]") - -/proc/runerandom() //randomizes word meaning - var/list/runewords=rnwords - for (var/word in engwords) - cultwords[word] = pick(runewords) - runewords-=cultwords[word] - -/obj/effect/rune - desc = "A strange collection of symbols drawn in blood." - anchored = TRUE - icon = 'icons/obj/rune.dmi' - icon_state = "1" - var/visibility = 0 - unacidable = TRUE - layer = TURF_LAYER - - - var/word1 - var/word2 - var/word3 - var/image/blood_image - var/list/converting = list() - -// Places these combos are mentioned: this file - twice in the rune code, once in imbued tome, once in tome's HTML runes.dm - in the imbue rune code. If you change a combination - dont forget to change it everywhere. - -// travel self [word] - Teleport to random [rune with word destination matching] -// travel other [word] - Portal to rune with word destination matching - kinda doesnt work. At least the icon. No idea why. -// see blood Hell - Create a new tome -// join blood self - Incorporate person over the rune into the group -// Hell join self - Summon TERROR -// destroy see technology - EMP rune -// travel blood self - Drain blood -// see Hell join - See invisible -// blood join Hell - Raise dead - -// hide see blood - Hide nearby runes -// blood see hide - Reveal nearby runes - The point of this rune is that its reversed obscure rune. So you always know the words to reveal the rune once oyu have obscured it. - -// Hell travel self - Leave your body and ghost around -// blood see travel - Manifest a ghost into a mortal body -// Hell tech join - Imbue a rune into a talisman -// Hell blood join - Sacrifice rune -// destroy travel self - Wall rune -// join other self - Summon cultist rune -// travel technology other - Freeing rune // other blood travel was freedom join other - -// hide other see - Deafening rune // was destroy see hear -// destroy see other - Blinding rune -// destroy see blood - BLOOD BOIL - -// self other technology - Communication rune //was other hear blood -// join hide technology - stun rune. Rune color: bright pink. -/obj/effect/rune/Initialize() - . = ..() - blood_image = image(loc = src) - blood_image.override = 1 - for(var/mob/living/silicon/ai/AI in player_list) - if(AI.client) - AI.client.images += blood_image - rune_list.Add(src) - -/obj/effect/rune/Destroy() - for(var/mob/living/silicon/ai/AI in player_list) - if(AI.client) - AI.client.images -= blood_image - qdel(blood_image) - blood_image = null - rune_list.Remove(src) - ..() - -/obj/effect/rune/examine(mob/user) - . = ..() - if(iscultist(user)) - . += "This spell circle reads: [word1] [word2] [word3]." - - -/obj/effect/rune/attackby(I as obj, user as mob) - if(istype(I, /obj/item/weapon/book/tome) && iscultist(user)) - to_chat(user, "You retrace your steps, carefully undoing the lines of the rune.") - qdel(src) - return - else if(istype(I, /obj/item/weapon/nullrod)) - to_chat(user, "You disrupt the vile magic with the deadening field of the null rod!") - qdel(src) - return - return - - -/obj/effect/rune/attack_hand(mob/living/user as mob) - if(!iscultist(user)) - to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") - return - if(user.is_muzzled()) - to_chat(user, "You are unable to speak the words of the rune.") - return - if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) - return fizzle() -// if(!src.visibility) -// src.visibility=1 - if(word1 == cultwords["travel"] && word2 == cultwords["self"]) - return teleport(src.word3) - if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) - return tomesummon() - if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) - return armor() - if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return convert() - if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) - return tearreality() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) - return emp(src.loc,5) - if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return drain() - if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) - return seer() - if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) - return raise() - if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return obscure(4) - if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return ajourney() - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) - return manifest() - if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) - return talisman() - if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) - return sacrifice() - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) - return revealrunes(src) - if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return wall() - if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) - return freedom() - if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) - return cultsummon() - if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) - return deafen() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) - return blind() - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return bloodboil() - if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) - return communicate() - if(word1 == cultwords["travel"] && word2 == cultwords["other"]) - return itemport(src.word3) - if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) - return runestun() - else - return fizzle() - - -/obj/effect/rune/proc/fizzle() - if(istype(src,/obj/effect/rune)) - usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - for (var/mob/V in viewers(src)) - V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) - return - -/obj/effect/rune/proc/check_icon() - icon = get_uristrune_cult(word1, word2, word3) - -/obj/item/weapon/book/tome - name = "arcane tome" - icon = 'icons/obj/weapons.dmi' - item_icons = list( - icon_l_hand = 'icons/mob/items/lefthand_books.dmi', - icon_r_hand = 'icons/mob/items/righthand_books.dmi', - ) - icon_state ="tome" - item_state = "tome" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - unique = 1 - var/tomedat = "" - var/list/words = list("ire" = "ire", "ego" = "ego", "nahlizet" = "nahlizet", "certum" = "certum", "veri" = "veri", "jatkaa" = "jatkaa", "balaq" = "balaq", "mgar" = "mgar", "karazet" = "karazet", "geeri" = "geeri") - - tomedat = {" - - - - -

    The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood.

    - - The book is written in an unknown dialect, there are lots of pictures of various complex geometric shapes. You find some notes in english that give you basic understanding of the many runes written in the book. The notes give you an understanding what the words for the runes should be. However, you do not know how to write all these words in this dialect.
    - Below is the summary of the runes.
    - -

    Contents

    -

    - Teleport self: Travel Self (word)
    - Teleport other: Travel Other (word)
    - Summon new tome: See Blood Hell
    - Convert a person: Join Blood Self
    - Summon Nar-Sie: Hell Join Self
    - Disable technology: Destroy See Technology
    - Drain blood: Travel Blood Self
    - Raise dead: Blood Join Hell
    - Hide runes: Hide See Blood
    - Reveal hidden runes: Blood See Hide
    - Leave your body: Hell travel self
    - Ghost Manifest: Blood See Travel
    - Imbue a talisman: Hell Technology Join
    - Sacrifice: Hell Blood Join
    - Create a wall: Destroy Travel Self
    - Summon cultist: Join Other Self
    - Free a cultist: Travel technology other
    - Deafen: Hide Other See
    - Blind: Destroy See Other
    - Blood Boil: Destroy See Blood
    - Communicate: Self Other Technology
    - Stun: Join Hide Technology
    - Summon Cultist Armor: Hell Destroy Other
    - See Invisible: See Hell Join
    -

    -

    Rune Descriptions

    -

    Teleport self

    - Teleport rune is a special rune, as it only needs two words, with the third word being destination. Basically, when you have two runes with the same destination, invoking one will teleport you to the other one. If there are more than 2 runes, you will be teleported to a random one. Runes with different third words will create separate networks. You can imbue this rune into a talisman, giving you a great escape mechanism.
    -

    Teleport other

    - Teleport other allows for teleportation for any movable object to another rune with the same third word. You need 3 cultists chanting the invocation for this rune to work.
    -

    Summon new tome

    - Invoking this rune summons a new arcane tome. -

    Convert a person

    - This rune opens target's mind to the realm of Nar-Sie, which usually results in this person joining the cult. However, some people (mostly the ones who possess high authority) have strong enough will to stay true to their old ideals.
    -

    Summon Nar-Sie

    - The ultimate rune. It summons the Avatar of Nar-Sie himself, tearing a huge hole in reality and consuming everything around it. Summoning it is the final goal of any cult.
    -

    Disable Technology

    - Invoking this rune creates a strong electromagnetic pulse in a small radius, making it basically analogic to an EMP grenade. You can imbue this rune into a talisman, making it a decent defensive item.
    -

    Drain Blood

    - This rune instantly heals you of some brute damage at the expense of a person placed on top of the rune. Whenever you invoke a drain rune, ALL drain runes on the station are activated, draining blood from anyone located on top of those runes. This includes yourself, though the blood you drain from yourself just comes back to you. This might help you identify this rune when studying words. One drain gives up to 25HP per each victim, but you can repeat it if you need more. Draining only works on living people, so you might need to recharge your "Battery" once its empty. Drinking too much blood at once might cause blood hunger.
    -

    Raise Dead

    - This rune allows for the resurrection of any dead person. You will need a dead human body and a living human sacrifice. Make 2 raise dead runes. Put a living, awake human on top of one, and a dead body on the other one. When you invoke the rune, the life force of the living human will be transferred into the dead body, allowing a ghost standing on top of the dead body to enter it, instantly and fully healing it. Use other runes to ensure there is a ghost ready to be resurrected.
    -

    Hide runes

    - This rune makes all nearby runes completely invisible. They are still there and will work if activated somehow, but you cannot invoke them directly if you do not see them.
    -

    Reveal runes

    - This rune is made to reverse the process of hiding a rune. It reveals all hidden runes in a rather large area around it. -

    Leave your body

    - This rune gently rips your soul out of your body, leaving it intact. You can observe the surroundings as a ghost as well as communicate with other ghosts. Your body takes damage while you are there, so ensure your journey is not too long, or you might never come back.
    -

    Manifest a ghost

    - Unlike the Raise Dead rune, this rune does not require any special preparations or vessels. Instead of using full lifeforce of a sacrifice, it will drain YOUR lifeforce. Stand on the rune and invoke it. If there's a ghost standing over the rune, it will materialise, and will live as long as you don't move off the rune or die. You can put a paper with a name on the rune to make the new body look like that person.
    -

    Imbue a talisman

    - This rune allows you to imbue the magic of some runes into paper talismans. Create an imbue rune, then an appropriate rune beside it. Put an empty piece of paper on the imbue rune and invoke it. You will now have a one-use talisman with the power of the target rune. Using a talisman drains some health, so be careful with it. You can imbue a talisman with power of the following runes: summon tome, reveal, conceal, teleport, tisable technology, communicate, deafen, blind and stun.
    -

    Sacrifice

    - Sacrifice rune allows you to sacrifice a living thing or a body to the Geometer of Blood. Monkeys and dead humans are the most basic sacrifices, they might or might not be enough to gain His favor. A living human is what a real sacrifice should be, however, you will need 3 people chanting the invocation to sacrifice a living person. -

    Create a wall

    - Invoking this rune solidifies the air above it, creating an an invisible wall. To remove the wall, simply invoke the rune again. -

    Summon cultist

    - This rune allows you to summon a fellow cultist to your location. The target cultist must be unhandcuffed ant not buckled to anything. You also need to have 3 people chanting at the rune to succesfully invoke it. Invoking it takes heavy strain on the bodies of all chanting cultists.
    -

    Free a cultist

    - This rune unhandcuffs and unbuckles any cultist of your choice, no matter where he is. You need to have 3 people invoking the rune for it to work. Invoking it takes heavy strain on the bodies of all chanting cultists.
    -

    Deafen

    - This rune temporarily deafens all non-cultists around you.
    -

    Blind

    - This rune temporarily blinds all non-cultists around you. Very robust. Use together with the deafen rune to leave your enemies completely helpless.
    -

    Blood boil

    - This rune boils the blood all non-cultists in visible range. The damage is enough to instantly critically hurt any person. You need 3 cultists invoking the rune for it to work. This rune is unreliable and may cause unpredicted effect when invoked. It also drains significant amount of your health when succesfully invoked.
    -

    Communicate

    - Invoking this rune allows you to relay a message to all cultists on the station and nearby space objects. -

    Stun

    - Unlike other runes, this ons is supposed to be used in talisman form. When invoked directly, it simply releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force all of its energy into one person, stunning him so hard he cant even speak. However, effect wears off rather fast.
    -

    Equip Armor

    - When this rune is invoked, either from a rune or a talisman, it will equip the user with the armor of the followers of Nar-Sie. To use this rune to its fullest extent, make sure you are not wearing any form of headgear, armor, gloves or shoes, and make sure you are not holding anything in your hands.
    -

    See Invisible

    - When invoked when standing on it, this rune allows the user to see the the world beyond as long as he does not move.
    - - - "} - -/obj/item/weapon/book/tome/Initialize() - . = ..() - if(!cultwords["travel"]) - runerandom() - for(var/V in cultwords) - words[cultwords[V]] = V - -/obj/item/weapon/book/tome/attack(mob/living/M as mob, mob/living/user as mob) - add_attack_logs(user,M,"Hit with [name]") - - if(istype(M,/mob/observer/dead)) - var/mob/observer/dead/D = M - D.manifest(user) - return - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - return - M.take_organ_damage(0,rand(5,20)) //really lucky - 5 hits for a crit - for(var/mob/O in viewers(M, null)) - O.show_message("\The [user] beats \the [M] with \the [src]!", 1) - to_chat(M, "You feel searing heat inside!") - - -/obj/item/weapon/book/tome/attack_self(mob/living/user as mob) - usr = user - if(!usr.canmove || usr.stat || usr.restrained()) - return - - if(!cultwords["travel"]) - runerandom() - if(iscultist(user)) - var/C = 0 - for(var/obj/effect/rune/N in rune_list) - C++ - if (!istype(user.loc,/turf)) - to_chat(user, "You do not have enough space to write a proper rune.") - return - - if (C>=26 + runedec + cult.current_antagonists.len) //including the useless rune at the secret room, shouldn't count against the limit of 25 runes - Urist - tgui_alert_async(user, "The cloth of reality can't take that much of a strain. Remove some runes first!") - return - else - switch(tgui_alert(user, "You open the tome", "Tome", list("Read it","Scribe a rune","Cancel"))) - if("Cancel") - return - if("Read it") - if(usr.get_active_hand() != src) - return - user << browse("[tomedat]", "window=Arcane Tome") - return - if(usr.get_active_hand() != src) - return - - var/list/dictionary = list ( - "convert" = list("join","blood","self"), - "wall" = list("destroy","travel","self"), - "blood boil" = list("destroy","see","blood"), - "blood drain" = list("travel","blood","self"), - "raise dead" = list("blood","join","hell"), - "summon narsie" = list("hell","join","self"), - "communicate" = list("self","other","technology"), - "emp" = list("destroy","see","technology"), - "manifest" = list("blood","see","travel"), - "summon tome" = list("see","blood","hell"), - "see invisible" = list("see","hell","join"), - "hide" = list("hide","see","blood"), - "reveal" = list("blood","see","hide"), - "astral journey" = list("hell","travel","self"), - "imbue" = list("hell","technology","join"), - "sacrifice" = list("hell","blood","join"), - "summon cultist" = list("join","other","self"), - "free cultist" = list("travel","technology","other"), - "deafen" = list("hide","other","see"), - "blind" = list("destroy","see","other"), - "stun" = list("join","hide","technology"), - "armor" = list("hell","destroy","other"), - "teleport" = list("travel","self"), - "teleport other" = list("travel","other") - ) - - var/list/english = list() - - var/list/scribewords = list("none") - - for (var/entry in words) - if (words[entry] != entry) - english += list(words[entry] = entry) - - for (var/entry in dictionary) - var/list/required = dictionary[entry] - if (length(english&required) == required.len) - scribewords += entry - - var/chosen_rune = null - - if(usr) - chosen_rune = input ("Choose a rune to scribe.") in scribewords - if (!chosen_rune) - return - if (chosen_rune == "none") - to_chat(user, "You decide against scribing a rune, perhaps you should take this time to study your notes.") - return - if (chosen_rune == "teleport") - dictionary[chosen_rune] += input ("Choose a destination word") in english - if (chosen_rune == "teleport other") - dictionary[chosen_rune] += input ("Choose a destination word") in english - - if(usr.get_active_hand() != src) - return - - for (var/mob/V in viewers(src)) - V.show_message("\The [user] slices open a finger and begins to chant and paint symbols on the floor.", 3, "You hear chanting.", 2) - to_chat(user, "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world.") - user.take_overall_damage((rand(9)+1)/10) // 0.1 to 1.0 damage - if(do_after(user, 50)) - var/area/A = get_area(user) - log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") - if(usr.get_active_hand() != src) - return - var/mob/living/carbon/human/H = user - var/obj/effect/rune/R = new /obj/effect/rune(user.loc) - to_chat(user, "You finish drawing the arcane markings of the Geometer.") - var/list/required = dictionary[chosen_rune] - R.word1 = english[required[1]] - R.word2 = english[required[2]] - R.word3 = english[required[3]] - R.check_icon() - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - return - else - to_chat(user, "The book seems full of illegible scribbles. Is this a joke?") - return - -/obj/item/weapon/book/tome/examine(mob/user) - . = ..() - if(!iscultist(user)) - . += "An old, dusty tome with frayed edges and a sinister looking cover." - else - . += "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though." - -/obj/item/weapon/book/tome/cultify() - return - -/obj/item/weapon/book/tome/imbued //admin tome, spawns working runes without waiting - w_class = ITEMSIZE_SMALL - var/cultistsonly = 1 -/obj/item/weapon/book/tome/imbued/attack_self(mob/user as mob) - if(src.cultistsonly && !iscultist(usr)) - return - if(!cultwords["travel"]) - runerandom() - if(user) - var/r - if (!istype(user.loc,/turf)) - to_chat(user, "You do not have enough space to write a proper rune.") - var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun") - r = input(usr, "Choose a rune to scribe", "Rune Scribing") in runes // Remains input() for extreme blocking - var/obj/effect/rune/R = new /obj/effect/rune - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type - var/area/A = get_area(user) - log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") - switch(r) - if("teleport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(usr) - beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking - R.word1=cultwords["travel"] - R.word2=cultwords["self"] - R.word3=beacon - R.loc = user.loc - R.check_icon() - if("itemport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(usr) - beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking - R.word1=cultwords["travel"] - R.word2=cultwords["other"] - R.word3=beacon - R.loc = user.loc - R.check_icon() - if("tome") - R.word1=cultwords["see"] - R.word2=cultwords["blood"] - R.word3=cultwords["hell"] - R.loc = user.loc - R.check_icon() - if("armor") - R.word1=cultwords["hell"] - R.word2=cultwords["destroy"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("convert") - R.word1=cultwords["join"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("tear in reality") - R.word1=cultwords["hell"] - R.word2=cultwords["join"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("emp") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() - if("drain") - R.word1=cultwords["travel"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("seer") - R.word1=cultwords["see"] - R.word2=cultwords["hell"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("raise") - R.word1=cultwords["blood"] - R.word2=cultwords["join"] - R.word3=cultwords["hell"] - R.loc = user.loc - R.check_icon() - if("obscure") - R.word1=cultwords["hide"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.loc = user.loc - R.check_icon() - if("astral journey") - R.word1=cultwords["hell"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("manifest") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["travel"] - R.loc = user.loc - R.check_icon() - if("imbue talisman") - R.word1=cultwords["hell"] - R.word2=cultwords["technology"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("sacrifice") - R.word1=cultwords["hell"] - R.word2=cultwords["blood"] - R.word3=cultwords["join"] - R.loc = user.loc - R.check_icon() - if("reveal") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["hide"] - R.loc = user.loc - R.check_icon() - if("wall") - R.word1=cultwords["destroy"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("freedom") - R.word1=cultwords["travel"] - R.word2=cultwords["technology"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("cultsummon") - R.word1=cultwords["join"] - R.word2=cultwords["other"] - R.word3=cultwords["self"] - R.loc = user.loc - R.check_icon() - if("deafen") - R.word1=cultwords["hide"] - R.word2=cultwords["other"] - R.word3=cultwords["see"] - R.loc = user.loc - R.check_icon() - if("blind") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["other"] - R.loc = user.loc - R.check_icon() - if("bloodboil") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - R.loc = user.loc - R.check_icon() - if("communicate") - R.word1=cultwords["self"] - R.word2=cultwords["other"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() - if("stun") - R.word1=cultwords["join"] - R.word2=cultwords["hide"] - R.word3=cultwords["technology"] - R.loc = user.loc - R.check_icon() +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +var/cultwords = list() +var/runedec = 0 +var/global/list/engwords = list("travel", "blood", "join", "hell", "destroy", "technology", "self", "see", "other", "hide") +var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri") + +/client/proc/check_words() // -- Urist + set category = "Special Verbs" + set name = "Check Rune Words" + set desc = "Check the rune-word meaning" + if(!cultwords["travel"]) + runerandom() + for (var/word in engwords) + to_chat(usr, "[cultwords[word]] is [word]") + +/proc/runerandom() //randomizes word meaning + var/list/runewords=rnwords + for (var/word in engwords) + cultwords[word] = pick(runewords) + runewords-=cultwords[word] + +/obj/effect/rune + desc = "A strange collection of symbols drawn in blood." + anchored = TRUE + icon = 'icons/obj/rune.dmi' + icon_state = "1" + var/visibility = 0 + unacidable = TRUE + layer = TURF_LAYER + + + var/word1 + var/word2 + var/word3 + var/image/blood_image + var/list/converting = list() + +// Places these combos are mentioned: this file - twice in the rune code, once in imbued tome, once in tome's HTML runes.dm - in the imbue rune code. If you change a combination - dont forget to change it everywhere. + +// travel self [word] - Teleport to random [rune with word destination matching] +// travel other [word] - Portal to rune with word destination matching - kinda doesnt work. At least the icon. No idea why. +// see blood Hell - Create a new tome +// join blood self - Incorporate person over the rune into the group +// Hell join self - Summon TERROR +// destroy see technology - EMP rune +// travel blood self - Drain blood +// see Hell join - See invisible +// blood join Hell - Raise dead + +// hide see blood - Hide nearby runes +// blood see hide - Reveal nearby runes - The point of this rune is that its reversed obscure rune. So you always know the words to reveal the rune once oyu have obscured it. + +// Hell travel self - Leave your body and ghost around +// blood see travel - Manifest a ghost into a mortal body +// Hell tech join - Imbue a rune into a talisman +// Hell blood join - Sacrifice rune +// destroy travel self - Wall rune +// join other self - Summon cultist rune +// travel technology other - Freeing rune // other blood travel was freedom join other + +// hide other see - Deafening rune // was destroy see hear +// destroy see other - Blinding rune +// destroy see blood - BLOOD BOIL + +// self other technology - Communication rune //was other hear blood +// join hide technology - stun rune. Rune color: bright pink. +/obj/effect/rune/Initialize() + . = ..() + blood_image = image(loc = src) + blood_image.override = 1 + for(var/mob/living/silicon/ai/AI in player_list) + if(AI.client) + AI.client.images += blood_image + rune_list.Add(src) + +/obj/effect/rune/Destroy() + for(var/mob/living/silicon/ai/AI in player_list) + if(AI.client) + AI.client.images -= blood_image + qdel(blood_image) + blood_image = null + rune_list.Remove(src) + ..() + +/obj/effect/rune/examine(mob/user) + . = ..() + if(iscultist(user)) + . += "This spell circle reads: [word1] [word2] [word3]." + + +/obj/effect/rune/attackby(I as obj, user as mob) + if(istype(I, /obj/item/weapon/book/tome) && iscultist(user)) + to_chat(user, "You retrace your steps, carefully undoing the lines of the rune.") + qdel(src) + return + else if(istype(I, /obj/item/weapon/nullrod)) + to_chat(user, "You disrupt the vile magic with the deadening field of the null rod!") + qdel(src) + return + return + + +/obj/effect/rune/attack_hand(mob/living/user as mob) + if(!iscultist(user)) + to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") + return + if(user.is_muzzled()) + to_chat(user, "You are unable to speak the words of the rune.") + return + if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) + return fizzle() +// if(!src.visibility) +// src.visibility=1 + if(word1 == cultwords["travel"] && word2 == cultwords["self"]) + return teleport(src.word3) + if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) + return tomesummon() + if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) + return armor() + if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) + return convert() + if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) + return tearreality() + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) + return emp(src.loc,5) + if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) + return drain() + if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) + return seer() + if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) + return raise() + if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) + return obscure(4) + if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) + return ajourney() + if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) + return manifest() + if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) + return talisman() + if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) + return sacrifice() + if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) + return revealrunes(src) + if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) + return wall() + if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) + return freedom() + if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) + return cultsummon() + if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) + return deafen() + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) + return blind() + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) + return bloodboil() + if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) + return communicate() + if(word1 == cultwords["travel"] && word2 == cultwords["other"]) + return itemport(src.word3) + if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) + return runestun() + else + return fizzle() + + +/obj/effect/rune/proc/fizzle() + if(istype(src,/obj/effect/rune)) + usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + else + usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + for (var/mob/V in viewers(src)) + V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) + return + +/obj/effect/rune/proc/check_icon() + icon = get_uristrune_cult(word1, word2, word3) + +/obj/item/weapon/book/tome + name = "arcane tome" + icon = 'icons/obj/weapons.dmi' + item_icons = list( + icon_l_hand = 'icons/mob/items/lefthand_books.dmi', + icon_r_hand = 'icons/mob/items/righthand_books.dmi', + ) + icon_state ="tome" + item_state = "tome" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + unique = 1 + var/tomedat = "" + var/list/words = list("ire" = "ire", "ego" = "ego", "nahlizet" = "nahlizet", "certum" = "certum", "veri" = "veri", "jatkaa" = "jatkaa", "balaq" = "balaq", "mgar" = "mgar", "karazet" = "karazet", "geeri" = "geeri") + + tomedat = {" + + + + +

    The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood.

    + + The book is written in an unknown dialect, there are lots of pictures of various complex geometric shapes. You find some notes in english that give you basic understanding of the many runes written in the book. The notes give you an understanding what the words for the runes should be. However, you do not know how to write all these words in this dialect.
    + Below is the summary of the runes.
    + +

    Contents

    +

    + Teleport self: Travel Self (word)
    + Teleport other: Travel Other (word)
    + Summon new tome: See Blood Hell
    + Convert a person: Join Blood Self
    + Summon Nar-Sie: Hell Join Self
    + Disable technology: Destroy See Technology
    + Drain blood: Travel Blood Self
    + Raise dead: Blood Join Hell
    + Hide runes: Hide See Blood
    + Reveal hidden runes: Blood See Hide
    + Leave your body: Hell travel self
    + Ghost Manifest: Blood See Travel
    + Imbue a talisman: Hell Technology Join
    + Sacrifice: Hell Blood Join
    + Create a wall: Destroy Travel Self
    + Summon cultist: Join Other Self
    + Free a cultist: Travel technology other
    + Deafen: Hide Other See
    + Blind: Destroy See Other
    + Blood Boil: Destroy See Blood
    + Communicate: Self Other Technology
    + Stun: Join Hide Technology
    + Summon Cultist Armor: Hell Destroy Other
    + See Invisible: See Hell Join
    +

    +

    Rune Descriptions

    +

    Teleport self

    + Teleport rune is a special rune, as it only needs two words, with the third word being destination. Basically, when you have two runes with the same destination, invoking one will teleport you to the other one. If there are more than 2 runes, you will be teleported to a random one. Runes with different third words will create separate networks. You can imbue this rune into a talisman, giving you a great escape mechanism.
    +

    Teleport other

    + Teleport other allows for teleportation for any movable object to another rune with the same third word. You need 3 cultists chanting the invocation for this rune to work.
    +

    Summon new tome

    + Invoking this rune summons a new arcane tome. +

    Convert a person

    + This rune opens target's mind to the realm of Nar-Sie, which usually results in this person joining the cult. However, some people (mostly the ones who possess high authority) have strong enough will to stay true to their old ideals.
    +

    Summon Nar-Sie

    + The ultimate rune. It summons the Avatar of Nar-Sie himself, tearing a huge hole in reality and consuming everything around it. Summoning it is the final goal of any cult.
    +

    Disable Technology

    + Invoking this rune creates a strong electromagnetic pulse in a small radius, making it basically analogic to an EMP grenade. You can imbue this rune into a talisman, making it a decent defensive item.
    +

    Drain Blood

    + This rune instantly heals you of some brute damage at the expense of a person placed on top of the rune. Whenever you invoke a drain rune, ALL drain runes on the station are activated, draining blood from anyone located on top of those runes. This includes yourself, though the blood you drain from yourself just comes back to you. This might help you identify this rune when studying words. One drain gives up to 25HP per each victim, but you can repeat it if you need more. Draining only works on living people, so you might need to recharge your "Battery" once its empty. Drinking too much blood at once might cause blood hunger.
    +

    Raise Dead

    + This rune allows for the resurrection of any dead person. You will need a dead human body and a living human sacrifice. Make 2 raise dead runes. Put a living, awake human on top of one, and a dead body on the other one. When you invoke the rune, the life force of the living human will be transferred into the dead body, allowing a ghost standing on top of the dead body to enter it, instantly and fully healing it. Use other runes to ensure there is a ghost ready to be resurrected.
    +

    Hide runes

    + This rune makes all nearby runes completely invisible. They are still there and will work if activated somehow, but you cannot invoke them directly if you do not see them.
    +

    Reveal runes

    + This rune is made to reverse the process of hiding a rune. It reveals all hidden runes in a rather large area around it. +

    Leave your body

    + This rune gently rips your soul out of your body, leaving it intact. You can observe the surroundings as a ghost as well as communicate with other ghosts. Your body takes damage while you are there, so ensure your journey is not too long, or you might never come back.
    +

    Manifest a ghost

    + Unlike the Raise Dead rune, this rune does not require any special preparations or vessels. Instead of using full lifeforce of a sacrifice, it will drain YOUR lifeforce. Stand on the rune and invoke it. If there's a ghost standing over the rune, it will materialise, and will live as long as you don't move off the rune or die. You can put a paper with a name on the rune to make the new body look like that person.
    +

    Imbue a talisman

    + This rune allows you to imbue the magic of some runes into paper talismans. Create an imbue rune, then an appropriate rune beside it. Put an empty piece of paper on the imbue rune and invoke it. You will now have a one-use talisman with the power of the target rune. Using a talisman drains some health, so be careful with it. You can imbue a talisman with power of the following runes: summon tome, reveal, conceal, teleport, tisable technology, communicate, deafen, blind and stun.
    +

    Sacrifice

    + Sacrifice rune allows you to sacrifice a living thing or a body to the Geometer of Blood. Monkeys and dead humans are the most basic sacrifices, they might or might not be enough to gain His favor. A living human is what a real sacrifice should be, however, you will need 3 people chanting the invocation to sacrifice a living person. +

    Create a wall

    + Invoking this rune solidifies the air above it, creating an an invisible wall. To remove the wall, simply invoke the rune again. +

    Summon cultist

    + This rune allows you to summon a fellow cultist to your location. The target cultist must be unhandcuffed ant not buckled to anything. You also need to have 3 people chanting at the rune to succesfully invoke it. Invoking it takes heavy strain on the bodies of all chanting cultists.
    +

    Free a cultist

    + This rune unhandcuffs and unbuckles any cultist of your choice, no matter where he is. You need to have 3 people invoking the rune for it to work. Invoking it takes heavy strain on the bodies of all chanting cultists.
    +

    Deafen

    + This rune temporarily deafens all non-cultists around you.
    +

    Blind

    + This rune temporarily blinds all non-cultists around you. Very robust. Use together with the deafen rune to leave your enemies completely helpless.
    +

    Blood boil

    + This rune boils the blood all non-cultists in visible range. The damage is enough to instantly critically hurt any person. You need 3 cultists invoking the rune for it to work. This rune is unreliable and may cause unpredicted effect when invoked. It also drains significant amount of your health when succesfully invoked.
    +

    Communicate

    + Invoking this rune allows you to relay a message to all cultists on the station and nearby space objects. +

    Stun

    + Unlike other runes, this ons is supposed to be used in talisman form. When invoked directly, it simply releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force all of its energy into one person, stunning him so hard he cant even speak. However, effect wears off rather fast.
    +

    Equip Armor

    + When this rune is invoked, either from a rune or a talisman, it will equip the user with the armor of the followers of Nar-Sie. To use this rune to its fullest extent, make sure you are not wearing any form of headgear, armor, gloves or shoes, and make sure you are not holding anything in your hands.
    +

    See Invisible

    + When invoked when standing on it, this rune allows the user to see the the world beyond as long as he does not move.
    + + + "} + +/obj/item/weapon/book/tome/Initialize() + . = ..() + if(!cultwords["travel"]) + runerandom() + for(var/V in cultwords) + words[cultwords[V]] = V + +/obj/item/weapon/book/tome/attack(mob/living/M as mob, mob/living/user as mob) + add_attack_logs(user,M,"Hit with [name]") + + if(istype(M,/mob/observer/dead)) + var/mob/observer/dead/D = M + D.manifest(user) + return + if(!istype(M)) + return + if(!iscultist(user)) + return ..() + if(iscultist(M)) + return + M.take_organ_damage(0,rand(5,20)) //really lucky - 5 hits for a crit + for(var/mob/O in viewers(M, null)) + O.show_message("\The [user] beats \the [M] with \the [src]!", 1) + to_chat(M, "You feel searing heat inside!") + + +/obj/item/weapon/book/tome/attack_self(mob/living/user as mob) + usr = user + if(!usr.canmove || usr.stat || usr.restrained()) + return + + if(!cultwords["travel"]) + runerandom() + if(iscultist(user)) + var/C = 0 + for(var/obj/effect/rune/N in rune_list) + C++ + if (!istype(user.loc,/turf)) + to_chat(user, "You do not have enough space to write a proper rune.") + return + + if (C>=26 + runedec + cult.current_antagonists.len) //including the useless rune at the secret room, shouldn't count against the limit of 25 runes - Urist + tgui_alert_async(user, "The cloth of reality can't take that much of a strain. Remove some runes first!") + return + else + switch(tgui_alert(user, "You open the tome", "Tome", list("Read it","Scribe a rune","Cancel"))) + if("Cancel") + return + if("Read it") + if(usr.get_active_hand() != src) + return + user << browse("[tomedat]", "window=Arcane Tome") + return + if(usr.get_active_hand() != src) + return + + var/list/dictionary = list ( + "convert" = list("join","blood","self"), + "wall" = list("destroy","travel","self"), + "blood boil" = list("destroy","see","blood"), + "blood drain" = list("travel","blood","self"), + "raise dead" = list("blood","join","hell"), + "summon narsie" = list("hell","join","self"), + "communicate" = list("self","other","technology"), + "emp" = list("destroy","see","technology"), + "manifest" = list("blood","see","travel"), + "summon tome" = list("see","blood","hell"), + "see invisible" = list("see","hell","join"), + "hide" = list("hide","see","blood"), + "reveal" = list("blood","see","hide"), + "astral journey" = list("hell","travel","self"), + "imbue" = list("hell","technology","join"), + "sacrifice" = list("hell","blood","join"), + "summon cultist" = list("join","other","self"), + "free cultist" = list("travel","technology","other"), + "deafen" = list("hide","other","see"), + "blind" = list("destroy","see","other"), + "stun" = list("join","hide","technology"), + "armor" = list("hell","destroy","other"), + "teleport" = list("travel","self"), + "teleport other" = list("travel","other") + ) + + var/list/english = list() + + var/list/scribewords = list("none") + + for (var/entry in words) + if (words[entry] != entry) + english += list(words[entry] = entry) + + for (var/entry in dictionary) + var/list/required = dictionary[entry] + if (length(english&required) == required.len) + scribewords += entry + + var/chosen_rune = null + + if(usr) + chosen_rune = input ("Choose a rune to scribe.") in scribewords + if (!chosen_rune) + return + if (chosen_rune == "none") + to_chat(user, "You decide against scribing a rune, perhaps you should take this time to study your notes.") + return + if (chosen_rune == "teleport") + dictionary[chosen_rune] += input ("Choose a destination word") in english + if (chosen_rune == "teleport other") + dictionary[chosen_rune] += input ("Choose a destination word") in english + + if(usr.get_active_hand() != src) + return + + for (var/mob/V in viewers(src)) + V.show_message("\The [user] slices open a finger and begins to chant and paint symbols on the floor.", 3, "You hear chanting.", 2) + to_chat(user, "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world.") + user.take_overall_damage((rand(9)+1)/10) // 0.1 to 1.0 damage + if(do_after(user, 50)) + var/area/A = get_area(user) + log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") + if(usr.get_active_hand() != src) + return + var/mob/living/carbon/human/H = user + var/obj/effect/rune/R = new /obj/effect/rune(user.loc) + to_chat(user, "You finish drawing the arcane markings of the Geometer.") + var/list/required = dictionary[chosen_rune] + R.word1 = english[required[1]] + R.word2 = english[required[2]] + R.word3 = english[required[3]] + R.check_icon() + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type + return + else + to_chat(user, "The book seems full of illegible scribbles. Is this a joke?") + return + +/obj/item/weapon/book/tome/examine(mob/user) + . = ..() + if(!iscultist(user)) + . += "An old, dusty tome with frayed edges and a sinister looking cover." + else + . += "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though." + +/obj/item/weapon/book/tome/cultify() + return + +/obj/item/weapon/book/tome/imbued //admin tome, spawns working runes without waiting + w_class = ITEMSIZE_SMALL + var/cultistsonly = 1 +/obj/item/weapon/book/tome/imbued/attack_self(mob/user as mob) + if(src.cultistsonly && !iscultist(usr)) + return + if(!cultwords["travel"]) + runerandom() + if(user) + var/r + if (!istype(user.loc,/turf)) + to_chat(user, "You do not have enough space to write a proper rune.") + var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun") + r = input(usr, "Choose a rune to scribe", "Rune Scribing") in runes // Remains input() for extreme blocking + var/obj/effect/rune/R = new /obj/effect/rune + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type + var/area/A = get_area(user) + log_and_message_admins("created \an [r] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") + switch(r) + if("teleport") + var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + var/beacon + if(usr) + beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking + R.word1=cultwords["travel"] + R.word2=cultwords["self"] + R.word3=beacon + R.loc = user.loc + R.check_icon() + if("itemport") + var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + var/beacon + if(usr) + beacon = input(usr, "Select the last rune", "Rune Scribing") in words // Remains input() for extreme blocking + R.word1=cultwords["travel"] + R.word2=cultwords["other"] + R.word3=beacon + R.loc = user.loc + R.check_icon() + if("tome") + R.word1=cultwords["see"] + R.word2=cultwords["blood"] + R.word3=cultwords["hell"] + R.loc = user.loc + R.check_icon() + if("armor") + R.word1=cultwords["hell"] + R.word2=cultwords["destroy"] + R.word3=cultwords["other"] + R.loc = user.loc + R.check_icon() + if("convert") + R.word1=cultwords["join"] + R.word2=cultwords["blood"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("tear in reality") + R.word1=cultwords["hell"] + R.word2=cultwords["join"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("emp") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["technology"] + R.loc = user.loc + R.check_icon() + if("drain") + R.word1=cultwords["travel"] + R.word2=cultwords["blood"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("seer") + R.word1=cultwords["see"] + R.word2=cultwords["hell"] + R.word3=cultwords["join"] + R.loc = user.loc + R.check_icon() + if("raise") + R.word1=cultwords["blood"] + R.word2=cultwords["join"] + R.word3=cultwords["hell"] + R.loc = user.loc + R.check_icon() + if("obscure") + R.word1=cultwords["hide"] + R.word2=cultwords["see"] + R.word3=cultwords["blood"] + R.loc = user.loc + R.check_icon() + if("astral journey") + R.word1=cultwords["hell"] + R.word2=cultwords["travel"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("manifest") + R.word1=cultwords["blood"] + R.word2=cultwords["see"] + R.word3=cultwords["travel"] + R.loc = user.loc + R.check_icon() + if("imbue talisman") + R.word1=cultwords["hell"] + R.word2=cultwords["technology"] + R.word3=cultwords["join"] + R.loc = user.loc + R.check_icon() + if("sacrifice") + R.word1=cultwords["hell"] + R.word2=cultwords["blood"] + R.word3=cultwords["join"] + R.loc = user.loc + R.check_icon() + if("reveal") + R.word1=cultwords["blood"] + R.word2=cultwords["see"] + R.word3=cultwords["hide"] + R.loc = user.loc + R.check_icon() + if("wall") + R.word1=cultwords["destroy"] + R.word2=cultwords["travel"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("freedom") + R.word1=cultwords["travel"] + R.word2=cultwords["technology"] + R.word3=cultwords["other"] + R.loc = user.loc + R.check_icon() + if("cultsummon") + R.word1=cultwords["join"] + R.word2=cultwords["other"] + R.word3=cultwords["self"] + R.loc = user.loc + R.check_icon() + if("deafen") + R.word1=cultwords["hide"] + R.word2=cultwords["other"] + R.word3=cultwords["see"] + R.loc = user.loc + R.check_icon() + if("blind") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["other"] + R.loc = user.loc + R.check_icon() + if("bloodboil") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["blood"] + R.loc = user.loc + R.check_icon() + if("communicate") + R.word1=cultwords["self"] + R.word2=cultwords["other"] + R.word3=cultwords["technology"] + R.loc = user.loc + R.check_icon() + if("stun") + R.word1=cultwords["join"] + R.word2=cultwords["hide"] + R.word3=cultwords["technology"] + R.loc = user.loc + R.check_icon() diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 3ad86499bda..0c52c00621c 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -357,9 +357,9 @@ var/list/sacrificed = list() else if(!corpse_to_raise.client && corpse_to_raise.mind) //Don't force the dead person to come back if they don't want to. for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == corpse_to_raise.mind) - to_chat(ghost, "The cultist [usr.real_name] is trying to \ + to_chat(ghost, span_interface("The cultist [usr.real_name] is trying to \ revive you. Return to your body if you want to be resurrected into the service of Nar'Sie! \ - (Verbs -> Ghost -> Re-enter corpse)") + (Verbs -> Ghost -> Re-enter corpse)")) break sleep(10 SECONDS) diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index 02923522dc5..24a64ef1af7 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,118 +1,118 @@ -/obj/item/weapon/paper/talisman - icon_state = "paper_talisman" - var/imbue = null - var/uses = 0 - info = "


    " - -/obj/item/weapon/paper/talisman/attack_self(mob/living/user as mob) - if(iscultist(user)) - var/delete = 1 - // who the hell thought this was a good idea :( - switch(imbue) - if("newtome") - call(/obj/effect/rune/proc/tomesummon)() - if("armor") - call(/obj/effect/rune/proc/armor)() - if("emp") - call(/obj/effect/rune/proc/emp)(usr.loc,3) - if("conceal") - call(/obj/effect/rune/proc/obscure)(2) - if("revealrunes") - call(/obj/effect/rune/proc/revealrunes)(src) - if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - call(/obj/effect/rune/proc/teleport)(imbue) - if("communicate") - //If the user cancels the talisman this var will be set to 0 - delete = call(/obj/effect/rune/proc/communicate)() - if("deafen") - call(/obj/effect/rune/proc/deafen)() - if("blind") - call(/obj/effect/rune/proc/blind)() - if("runestun") - to_chat(user, "To use this talisman, attack your target directly.") - return - if("supply") - supply() - user.take_organ_damage(5, 0) - if(src && src.imbue!="supply" && src.imbue!="runestun") - if(delete) - qdel(src) - return - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return - - -/obj/item/weapon/paper/talisman/attack(mob/living/carbon/T as mob, mob/living/user as mob) - if(iscultist(user)) - if(imbue == "runestun") - user.take_organ_damage(5, 0) - call(/obj/effect/rune/proc/runestun)(T) - qdel(src) - else - ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? - else - ..() - - -/obj/item/weapon/paper/talisman/proc/supply(var/key) - if (!src.uses) - qdel(src) - return - - var/dat = "There are [src.uses] bloody runes on the parchment.
    " - dat += "Please choose the chant to be imbued into the fabric of reality.
    " - dat += "
    " - dat += "N'ath reth sh'yro eth d'raggathnor! - Allows you to summon a new arcane tome.
    " - dat += "Sas'so c'arta forbici! - Allows you to move to a rune with the same last word.
    " - dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " - dat += "Kla'atu barada nikt'o! - Allows you to conceal the runes you placed on the floor.
    " - dat += "O bidai nabora se'sma! - Allows you to coordinate with others of your cult.
    " - dat += "Fuu ma'jin - Allows you to stun a person by attacking them with the talisman.
    " - dat += "Sa tatha najin - Allows you to summon armoured robes and an unholy blade
    " - dat += "Kal om neth - Summons a soul stone
    " - dat += "Da A'ig Osk - Summons a construct shell for use with captured souls. It is too large to carry on your person.
    " - usr << browse(dat, "window=id_com;size=350x200") - return - - -/obj/item/weapon/paper/talisman/Topic(href, href_list) - if(!src) return - if (usr.stat || usr.restrained() || !in_range(src, usr)) return - - if (href_list["rune"]) - switch(href_list["rune"]) - if("newtome") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "newtome" - if("teleport") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "[pick("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq")]" - T.info = "[T.imbue]" - if("emp") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "emp" - if("conceal") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "conceal" - if("communicate") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "communicate" - if("runestun") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "runestun" - if("armor") - var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) - T.imbue = "armor" - if("soulstone") - new /obj/item/device/soulstone(get_turf(usr)) - if("construct") - new /obj/structure/constructshell/cult(get_turf(usr)) - src.uses-- - supply() - return - - -/obj/item/weapon/paper/talisman/supply - imbue = "supply" - uses = 5 +/obj/item/weapon/paper/talisman + icon_state = "paper_talisman" + var/imbue = null + var/uses = 0 + info = "


    " + +/obj/item/weapon/paper/talisman/attack_self(mob/living/user as mob) + if(iscultist(user)) + var/delete = 1 + // who the hell thought this was a good idea :( + switch(imbue) + if("newtome") + call(/obj/effect/rune/proc/tomesummon)() + if("armor") + call(/obj/effect/rune/proc/armor)() + if("emp") + call(/obj/effect/rune/proc/emp)(usr.loc,3) + if("conceal") + call(/obj/effect/rune/proc/obscure)(2) + if("revealrunes") + call(/obj/effect/rune/proc/revealrunes)(src) + if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + call(/obj/effect/rune/proc/teleport)(imbue) + if("communicate") + //If the user cancels the talisman this var will be set to 0 + delete = call(/obj/effect/rune/proc/communicate)() + if("deafen") + call(/obj/effect/rune/proc/deafen)() + if("blind") + call(/obj/effect/rune/proc/blind)() + if("runestun") + to_chat(user, "To use this talisman, attack your target directly.") + return + if("supply") + supply() + user.take_organ_damage(5, 0) + if(src && src.imbue!="supply" && src.imbue!="runestun") + if(delete) + qdel(src) + return + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return + + +/obj/item/weapon/paper/talisman/attack(mob/living/carbon/T as mob, mob/living/user as mob) + if(iscultist(user)) + if(imbue == "runestun") + user.take_organ_damage(5, 0) + call(/obj/effect/rune/proc/runestun)(T) + qdel(src) + else + ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? + else + ..() + + +/obj/item/weapon/paper/talisman/proc/supply(var/key) + if (!src.uses) + qdel(src) + return + + var/dat = "There are [src.uses] bloody runes on the parchment.
    " + dat += "Please choose the chant to be imbued into the fabric of reality.
    " + dat += "
    " + dat += "N'ath reth sh'yro eth d'raggathnor! - Allows you to summon a new arcane tome.
    " + dat += "Sas'so c'arta forbici! - Allows you to move to a rune with the same last word.
    " + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " + dat += "Kla'atu barada nikt'o! - Allows you to conceal the runes you placed on the floor.
    " + dat += "O bidai nabora se'sma! - Allows you to coordinate with others of your cult.
    " + dat += "Fuu ma'jin - Allows you to stun a person by attacking them with the talisman.
    " + dat += "Sa tatha najin - Allows you to summon armoured robes and an unholy blade
    " + dat += "Kal om neth - Summons a soul stone
    " + dat += "Da A'ig Osk - Summons a construct shell for use with captured souls. It is too large to carry on your person.
    " + usr << browse(dat, "window=id_com;size=350x200") + return + + +/obj/item/weapon/paper/talisman/Topic(href, href_list) + if(!src) return + if (usr.stat || usr.restrained() || !in_range(src, usr)) return + + if (href_list["rune"]) + switch(href_list["rune"]) + if("newtome") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "newtome" + if("teleport") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "[pick("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri", "orkan", "allaq")]" + T.info = "[T.imbue]" + if("emp") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "emp" + if("conceal") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "conceal" + if("communicate") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "communicate" + if("runestun") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "runestun" + if("armor") + var/obj/item/weapon/paper/talisman/T = new /obj/item/weapon/paper/talisman(get_turf(usr)) + T.imbue = "armor" + if("soulstone") + new /obj/item/device/soulstone(get_turf(usr)) + if("construct") + new /obj/structure/constructshell/cult(get_turf(usr)) + src.uses-- + supply() + return + + +/obj/item/weapon/paper/talisman/supply + imbue = "supply" + uses = 5 diff --git a/code/game/gamemodes/endgame/endgame.dm b/code/game/gamemodes/endgame/endgame.dm index aef7894800c..721ae98f716 100644 --- a/code/game/gamemodes/endgame/endgame.dm +++ b/code/game/gamemodes/endgame/endgame.dm @@ -1,71 +1,71 @@ -/********************** - * ENDGAME STUFF - **********************/ - - // Universal State - // Handles stuff like space icon_state, constants, etc. - // Essentially a policy manager. Once shit hits the fan, this changes its policies. - // Called by master controller. - - // Default shit. -/datum/universal_state - // Just for reference, for now. - // Might eventually add an observatory job. - var/name = "Normal" - var/desc = "Nothing seems awry." - - // Sets world.turf, replaces all turfs of type /turf/space. - var/space_type = /turf/space - - // Replaces all turfs of type /turf/space/transit - var/transit_space_type = /turf/space/transit - - // Chance of a floor or wall getting damaged [0-100] - // Simulates stuff getting broken due to molecular bonds decaying. - var/decay_rate = 0 - -// Actually decay the turf. -/datum/universal_state/proc/DecayTurf(var/turf/T) - if(istype(T,/turf/simulated/wall)) - var/turf/simulated/wall/W=T - W.melt() - return - if(istype(T,/turf/simulated/floor)) - var/turf/simulated/floor/F=T - // Burnt? - if(!F.burnt) - F.burn_tile() - else - F.ReplaceWithLattice() - return - -// Return 0 to cause shuttle call to fail. -/datum/universal_state/proc/OnShuttleCall(var/mob/user) - return 1 - -// Processed per tick -/datum/universal_state/proc/OnTurfTick(var/turf/T) - if(decay_rate && prob(decay_rate)) - DecayTurf(T) - -// Apply changes when exiting state -/datum/universal_state/proc/OnExit() - // Does nothing by default - -// Apply changes when entering state -/datum/universal_state/proc/OnEnter() - // Does nothing by default - -// Apply changes to a new turf. -/datum/universal_state/proc/OnTurfChange(var/turf/NT) - return - -/datum/universal_state/proc/OverlayAndAmbientSet() - return - -/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1) - if(on_exit) - universe.OnExit() - universe = new newstate - if(on_enter) - universe.OnEnter() +/********************** + * ENDGAME STUFF + **********************/ + + // Universal State + // Handles stuff like space icon_state, constants, etc. + // Essentially a policy manager. Once shit hits the fan, this changes its policies. + // Called by master controller. + + // Default shit. +/datum/universal_state + // Just for reference, for now. + // Might eventually add an observatory job. + var/name = "Normal" + var/desc = "Nothing seems awry." + + // Sets world.turf, replaces all turfs of type /turf/space. + var/space_type = /turf/space + + // Replaces all turfs of type /turf/space/transit + var/transit_space_type = /turf/space/transit + + // Chance of a floor or wall getting damaged [0-100] + // Simulates stuff getting broken due to molecular bonds decaying. + var/decay_rate = 0 + +// Actually decay the turf. +/datum/universal_state/proc/DecayTurf(var/turf/T) + if(istype(T,/turf/simulated/wall)) + var/turf/simulated/wall/W=T + W.melt() + return + if(istype(T,/turf/simulated/floor)) + var/turf/simulated/floor/F=T + // Burnt? + if(!F.burnt) + F.burn_tile() + else + F.ReplaceWithLattice() + return + +// Return 0 to cause shuttle call to fail. +/datum/universal_state/proc/OnShuttleCall(var/mob/user) + return 1 + +// Processed per tick +/datum/universal_state/proc/OnTurfTick(var/turf/T) + if(decay_rate && prob(decay_rate)) + DecayTurf(T) + +// Apply changes when exiting state +/datum/universal_state/proc/OnExit() + // Does nothing by default + +// Apply changes when entering state +/datum/universal_state/proc/OnEnter() + // Does nothing by default + +// Apply changes to a new turf. +/datum/universal_state/proc/OnTurfChange(var/turf/NT) + return + +/datum/universal_state/proc/OverlayAndAmbientSet() + return + +/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1) + if(on_exit) + universe.OnExit() + universe = new newstate + if(on_enter) + universe.OnEnter() diff --git a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm index af29065f96a..000cc542247 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm @@ -1,120 +1,120 @@ -// QUALITY COPYPASTA -/turf/unsimulated/wall/supermatter - name = "Bluespace" - desc = "THE END IS right now actually." - - icon = 'icons/turf/space.dmi' - icon_state = "bluespace" - - //luminosity = 5 - //l_color="#0066FF" - plane = PLANE_LIGHTING_ABOVE - - var/spawned=0 // DIR mask - var/next_check=0 - var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) - -/turf/unsimulated/wall/supermatter/Initialize(mapload) - . = ..() - START_PROCESSING(SSturfs, src) - next_check = world.time+5 SECONDS - -/turf/unsimulated/wall/supermatter/Destroy() - STOP_PROCESSING(SSturfs, src) - return ..() - -/turf/unsimulated/wall/supermatter/process() - // Only check infrequently. - if(next_check>world.time) return - - // No more available directions? Shut down process(). - if(avail_dirs.len==0) - STOP_PROCESSING(SSobj, src) - return 1 - - // We're checking, reset the timer. - next_check = world.time+5 SECONDS - - // Choose a direction. - var/pdir = pick(avail_dirs) - avail_dirs -= pdir - var/turf/T=get_step(src,pdir) - - // EXPAND - if(!istype(T,type)) - // Do pretty fadeout animation for 1s. - new /obj/effect/overlay/bluespacify(T) - spawn(10) - // Nom. - for(var/atom/movable/A in T) - if(A) - if(istype(A,/mob/living)) - qdel(A) - else if(istype(A,/mob)) // Observers, AI cameras. - continue - else - qdel(A) - T.ChangeTurf(type) - - if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST)) - STOP_PROCESSING(SSturfs, src) - return - -/turf/unsimulated/wall/supermatter/attack_generic(mob/user as mob) - return attack_hand(user) - -/turf/unsimulated/wall/supermatter/attack_robot(mob/user as mob) - if(Adjacent(user)) - return attack_hand(user) - else - to_chat(user, "What the fuck are you doing?") - return - -// /vg/: Don't let ghosts fuck with this. -/turf/unsimulated/wall/supermatter/attack_ghost(mob/user as mob) - user.examinate(src) - -/turf/unsimulated/wall/supermatter/attack_ai(mob/user as mob) - return user.examinate(src) - -/turf/unsimulated/wall/supermatter/attack_hand(mob/user as mob) - user.visible_message("\The [user] reaches out and touches \the [src]... And then blinks out of existance.",\ - "You reach out and touch \the [src]. Everything immediately goes quiet. Your last thought is \"That was not a wise decision.\"",\ - "You hear an unearthly noise.") - - playsound(src, 'sound/effects/supermatter.ogg', 50, 1) - - Consume(user) - -/turf/unsimulated/wall/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) - user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\ - "You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\ - "Everything suddenly goes silent.") - - playsound(src, 'sound/effects/supermatter.ogg', 50, 1) - - user.drop_from_inventory(W) - Consume(W) - - -/turf/unsimulated/wall/supermatter/Bumped(atom/AM as mob|obj) - if(istype(AM, /mob/living)) - var/mob/living/M = AM - var/datum/gender/T = gender_datums[M.get_visible_gender()] - AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [T.his] body starts to glow and catch flame before flashing into ash.",\ - "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ - "You hear an unearthly noise as a wave of heat washes over you.") - else - AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") - - playsound(src, 'sound/effects/supermatter.ogg', 50, 1) - - Consume(AM) - - -/turf/unsimulated/wall/supermatter/proc/Consume(var/mob/living/user) - if(istype(user,/mob/observer)) - return - - qdel(user) +// QUALITY COPYPASTA +/turf/unsimulated/wall/supermatter + name = "Bluespace" + desc = "THE END IS right now actually." + + icon = 'icons/turf/space.dmi' + icon_state = "bluespace" + + //luminosity = 5 + //l_color="#0066FF" + plane = PLANE_LIGHTING_ABOVE + + var/spawned=0 // DIR mask + var/next_check=0 + var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) + +/turf/unsimulated/wall/supermatter/Initialize(mapload) + . = ..() + START_PROCESSING(SSturfs, src) + next_check = world.time+5 SECONDS + +/turf/unsimulated/wall/supermatter/Destroy() + STOP_PROCESSING(SSturfs, src) + return ..() + +/turf/unsimulated/wall/supermatter/process() + // Only check infrequently. + if(next_check>world.time) return + + // No more available directions? Shut down process(). + if(avail_dirs.len==0) + STOP_PROCESSING(SSobj, src) + return 1 + + // We're checking, reset the timer. + next_check = world.time+5 SECONDS + + // Choose a direction. + var/pdir = pick(avail_dirs) + avail_dirs -= pdir + var/turf/T=get_step(src,pdir) + + // EXPAND + if(!istype(T,type)) + // Do pretty fadeout animation for 1s. + new /obj/effect/overlay/bluespacify(T) + spawn(10) + // Nom. + for(var/atom/movable/A in T) + if(A) + if(istype(A,/mob/living)) + qdel(A) + else if(istype(A,/mob)) // Observers, AI cameras. + continue + else + qdel(A) + T.ChangeTurf(type) + + if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST)) + STOP_PROCESSING(SSturfs, src) + return + +/turf/unsimulated/wall/supermatter/attack_generic(mob/user as mob) + return attack_hand(user) + +/turf/unsimulated/wall/supermatter/attack_robot(mob/user as mob) + if(Adjacent(user)) + return attack_hand(user) + else + to_chat(user, "What the fuck are you doing?") + return + +// /vg/: Don't let ghosts fuck with this. +/turf/unsimulated/wall/supermatter/attack_ghost(mob/user as mob) + user.examinate(src) + +/turf/unsimulated/wall/supermatter/attack_ai(mob/user as mob) + return user.examinate(src) + +/turf/unsimulated/wall/supermatter/attack_hand(mob/user as mob) + user.visible_message("\The [user] reaches out and touches \the [src]... And then blinks out of existance.",\ + "You reach out and touch \the [src]. Everything immediately goes quiet. Your last thought is \"That was not a wise decision.\"",\ + "You hear an unearthly noise.") + + playsound(src, 'sound/effects/supermatter.ogg', 50, 1) + + Consume(user) + +/turf/unsimulated/wall/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob) + user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\ + "You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\ + "Everything suddenly goes silent.") + + playsound(src, 'sound/effects/supermatter.ogg', 50, 1) + + user.drop_from_inventory(W) + Consume(W) + + +/turf/unsimulated/wall/supermatter/Bumped(atom/AM as mob|obj) + if(istype(AM, /mob/living)) + var/mob/living/M = AM + var/datum/gender/T = gender_datums[M.get_visible_gender()] + AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [T.his] body starts to glow and catch flame before flashing into ash.",\ + "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ + "You hear an unearthly noise as a wave of heat washes over you.") + else + AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + + playsound(src, 'sound/effects/supermatter.ogg', 50, 1) + + Consume(AM) + + +/turf/unsimulated/wall/supermatter/proc/Consume(var/mob/living/user) + if(istype(user,/mob/observer)) + return + + qdel(user) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm index a74867d9905..313d6fbd2e0 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm @@ -1,95 +1,95 @@ -/*** EXIT PORTAL ***/ - -/obj/singularity/narsie/large/exit - name = "Bluespace Rift" - desc = "NO TIME TO EXPLAIN, JUMP IN" - icon = 'icons/obj/rift.dmi' - icon_state = "rift" - - move_self = 0 - announce=0 - cause_hell=0 - - plane = PLANE_LIGHTING_ABOVE // ITS SO BRIGHT - - consume_range = 6 - -/obj/singularity/narsie/large/exit/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/singularity/narsie/large/exit/update_icon() - overlays = 0 - -/obj/singularity/narsie/large/exit/process() - for(var/mob/M in player_list) - if(M.client) - M.see_rift(src) - eat() - -/obj/singularity/narsie/large/exit/acquire(var/mob/food) - return - -/obj/singularity/narsie/large/exit/consume(const/atom/A) - if(!(A.singuloCanEat())) - return 0 - - if (istype(A, /mob/living/)) - var/mob/living/L = A - if(L.buckled && istype(L.buckled,/obj/structure/bed/)) - var/turf/O = L.buckled - do_teleport(O, pick(endgame_safespawns), local = FALSE) //VOREStation Edit - L.loc = O.loc - else - do_teleport(L, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit - - else if (istype(A, /obj/mecha/)) - do_teleport(A, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit - - else if (isturf(A)) - var/turf/T = A - var/dist = get_dist(T, src) - if (dist <= consume_range && T.density) - T.density = FALSE - - for (var/atom/movable/AM in T.contents) - if (AM == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM) - continue - - if (dist > consume_range) - if(!(AM.singuloCanEat())) - continue - - if (101 == AM.invisibility) - continue - - spawn (0) - AM.singularity_pull(src, src.current_size) - - -/mob - //thou shall always be able to see the rift - var/image/riftimage = null - -/mob/proc/see_rift(var/obj/singularity/narsie/large/exit/R) - var/turf/T_mob = get_turf(src) - if((R.z == T_mob.z) && (get_dist(R,T_mob) <= (R.consume_range+10)) && !(R in view(T_mob))) - if(!riftimage) - riftimage = image('icons/obj/rift.dmi',T_mob,"rift",1,1) - riftimage.plane = PLANE_LIGHTING_ABOVE - riftimage.mouse_opacity = 0 - - var/new_x = 32 * (R.x - T_mob.x) + R.pixel_x - var/new_y = 32 * (R.y - T_mob.y) + R.pixel_y - riftimage.pixel_x = new_x - riftimage.pixel_y = new_y - riftimage.loc = T_mob - - src << riftimage - else - if(riftimage) - qdel(riftimage) +/*** EXIT PORTAL ***/ + +/obj/singularity/narsie/large/exit + name = "Bluespace Rift" + desc = "NO TIME TO EXPLAIN, JUMP IN" + icon = 'icons/obj/rift.dmi' + icon_state = "rift" + + move_self = 0 + announce=0 + cause_hell=0 + + plane = PLANE_LIGHTING_ABOVE // ITS SO BRIGHT + + consume_range = 6 + +/obj/singularity/narsie/large/exit/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/singularity/narsie/large/exit/update_icon() + overlays = 0 + +/obj/singularity/narsie/large/exit/process() + for(var/mob/M in player_list) + if(M.client) + M.see_rift(src) + eat() + +/obj/singularity/narsie/large/exit/acquire(var/mob/food) + return + +/obj/singularity/narsie/large/exit/consume(const/atom/A) + if(!(A.singuloCanEat())) + return 0 + + if (istype(A, /mob/living/)) + var/mob/living/L = A + if(L.buckled && istype(L.buckled,/obj/structure/bed/)) + var/turf/O = L.buckled + do_teleport(O, pick(endgame_safespawns), local = FALSE) //VOREStation Edit + L.loc = O.loc + else + do_teleport(L, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit + + else if (istype(A, /obj/mecha/)) + do_teleport(A, pick(endgame_safespawns), local = FALSE) //dead-on precision //VOREStation Edit + + else if (isturf(A)) + var/turf/T = A + var/dist = get_dist(T, src) + if (dist <= consume_range && T.density) + T.density = FALSE + + for (var/atom/movable/AM in T.contents) + if (AM == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM) + continue + + if (dist > consume_range) + if(!(AM.singuloCanEat())) + continue + + if (101 == AM.invisibility) + continue + + spawn (0) + AM.singularity_pull(src, src.current_size) + + +/mob + //thou shall always be able to see the rift + var/image/riftimage = null + +/mob/proc/see_rift(var/obj/singularity/narsie/large/exit/R) + var/turf/T_mob = get_turf(src) + if((R.z == T_mob.z) && (get_dist(R,T_mob) <= (R.consume_range+10)) && !(R in view(T_mob))) + if(!riftimage) + riftimage = image('icons/obj/rift.dmi',T_mob,"rift",1,1) + riftimage.plane = PLANE_LIGHTING_ABOVE + riftimage.mouse_opacity = 0 + + var/new_x = 32 * (R.x - T_mob.x) + R.pixel_x + var/new_y = 32 * (R.y - T_mob.y) + R.pixel_y + riftimage.pixel_x = new_x + riftimage.pixel_y = new_y + riftimage.loc = T_mob + + src << riftimage + else + if(riftimage) + qdel(riftimage) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index cd289689933..a217dde847b 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -1,128 +1,128 @@ -var/global/universe_has_ended = 0 - - -/datum/universal_state/supermatter_cascade - name = "Supermatter Cascade" - desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter." - - decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) - -/datum/universal_state/supermatter_cascade/OnShuttleCall(var/mob/user) - if(user) - to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") - return 0 - -/datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T) - var/turf/space/S = T - if(istype(S)) - S.color = "#0066FF" - else - S.color = initial(S.color) - -/datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T) - if(istype(T,/turf/simulated/wall)) - var/turf/simulated/wall/W=T - W.melt() - return - if(istype(T,/turf/simulated/floor)) - var/turf/simulated/floor/F=T - // Burnt? - if(!F.burnt) - F.burn_tile() - else - if(!istype(F,/turf/simulated/floor/plating)) - F.break_tile_to_plating() - return - -// Apply changes when entering state -/datum/universal_state/supermatter_cascade/OnEnter() - set background = 1 - to_world("You are blinded by a brilliant flash of energy.") - - world << sound('sound/effects/cascade.ogg') - - for(var/mob/M in player_list) - M.flash_eyes() - - if(emergency_shuttle.can_recall()) - priority_announcement.Announce("The emergency shuttle has returned due to bluespace distortion.") - emergency_shuttle.recall() - - AreaSet() - MiscSet() - APCSet() - OverlayAndAmbientSet() - - // Disable Nar-Sie. - cult.allow_narsie = 0 - - PlayerSet() - - new /obj/singularity/narsie/large/exit(pick(endgame_exits)) - spawn(rand(30,60) SECONDS) - var/txt = {" -There's been a galaxy-wide electromagnetic pulse. All of our systems are heavily damaged and many personnel are dead or dying. We are seeing increasing indications of the universe itself beginning to unravel. - -[station_name()], you are the only facility nearby a bluespace rift, which is near your research outpost. You are hereby directed to enter the rift using all means necessary, quite possibly as the last of your species alive. - -You have five minutes before the universe collapses. Good l\[\[###!!!- - -AUTOMATED ALERT: Link to [command_name()] lost. - -The access requirements on the Asteroid Shuttles' consoles have now been revoked. -"} - priority_announcement.Announce(txt,"SUPERMATTER CASCADE DETECTED") - - for(var/obj/machinery/computer/shuttle_control/C in machines) - if(istype(C, /obj/machinery/computer/shuttle_control/research) || istype(C, /obj/machinery/computer/shuttle_control/mining)) - C.req_access = list() - C.req_one_access = list() - - spawn(5 MINUTES) - ticker.station_explosion_cinematic(0,null) // TODO: Custom cinematic - universe_has_ended = 1 - return - -/datum/universal_state/supermatter_cascade/proc/AreaSet() - for(var/area/A in world) - if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) - continue - - A.update_icon() - -/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() - return - /* TODO - spawn(0) - for(var/datum/lighting_corner/L in world) - if(L.z in using_map.admin_levels) - L.update_lumcount(1,1,1) - else - L.update_lumcount(0.0, 0.4, 1) - - for(var/turf/space/T in world) - OnTurfChange(T) - */ -/datum/universal_state/supermatter_cascade/proc/MiscSet() - for (var/obj/machinery/firealarm/alm in machines) - if (!(alm.stat & BROKEN)) - alm.ex_act(2) - -/datum/universal_state/supermatter_cascade/proc/APCSet() - for (var/obj/machinery/power/apc/APC in GLOB.apcs) - if (!(APC.stat & BROKEN) && !APC.is_critical) - APC.chargemode = 0 - if(APC.cell) - APC.cell.charge = 0 - APC.emagged = 1 - APC.queue_icon_update() - -/datum/universal_state/supermatter_cascade/proc/PlayerSet() - for(var/datum/mind/M in player_list) - if(!istype(M.current,/mob/living)) - continue - if(M.current.stat!=2) - M.current.Weaken(10) - M.current.flash_eyes() - - clear_antag_roles(M) +var/global/universe_has_ended = 0 + + +/datum/universal_state/supermatter_cascade + name = "Supermatter Cascade" + desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter." + + decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs) + +/datum/universal_state/supermatter_cascade/OnShuttleCall(var/mob/user) + if(user) + to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") + return 0 + +/datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T) + var/turf/space/S = T + if(istype(S)) + S.color = "#0066FF" + else + S.color = initial(S.color) + +/datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T) + if(istype(T,/turf/simulated/wall)) + var/turf/simulated/wall/W=T + W.melt() + return + if(istype(T,/turf/simulated/floor)) + var/turf/simulated/floor/F=T + // Burnt? + if(!F.burnt) + F.burn_tile() + else + if(!istype(F,/turf/simulated/floor/plating)) + F.break_tile_to_plating() + return + +// Apply changes when entering state +/datum/universal_state/supermatter_cascade/OnEnter() + set background = 1 + to_world("You are blinded by a brilliant flash of energy.") + + world << sound('sound/effects/cascade.ogg') + + for(var/mob/M in player_list) + M.flash_eyes() + + if(emergency_shuttle.can_recall()) + priority_announcement.Announce("The emergency shuttle has returned due to bluespace distortion.") + emergency_shuttle.recall() + + AreaSet() + MiscSet() + APCSet() + OverlayAndAmbientSet() + + // Disable Nar-Sie. + cult.allow_narsie = 0 + + PlayerSet() + + new /obj/singularity/narsie/large/exit(pick(endgame_exits)) + spawn(rand(30,60) SECONDS) + var/txt = {" +There's been a galaxy-wide electromagnetic pulse. All of our systems are heavily damaged and many personnel are dead or dying. We are seeing increasing indications of the universe itself beginning to unravel. + +[station_name()], you are the only facility nearby a bluespace rift, which is near your research outpost. You are hereby directed to enter the rift using all means necessary, quite possibly as the last of your species alive. + +You have five minutes before the universe collapses. Good l\[\[###!!!- + +AUTOMATED ALERT: Link to [command_name()] lost. + +The access requirements on the Asteroid Shuttles' consoles have now been revoked. +"} + priority_announcement.Announce(txt,"SUPERMATTER CASCADE DETECTED") + + for(var/obj/machinery/computer/shuttle_control/C in machines) + if(istype(C, /obj/machinery/computer/shuttle_control/research) || istype(C, /obj/machinery/computer/shuttle_control/mining)) + C.req_access = list() + C.req_one_access = list() + + spawn(5 MINUTES) + ticker.station_explosion_cinematic(0,null) // TODO: Custom cinematic + universe_has_ended = 1 + return + +/datum/universal_state/supermatter_cascade/proc/AreaSet() + for(var/area/A in world) + if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) + continue + + A.update_icon() + +/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() + return + /* TODO + spawn(0) + for(var/datum/lighting_corner/L in world) + if(L.z in using_map.admin_levels) + L.update_lumcount(1,1,1) + else + L.update_lumcount(0.0, 0.4, 1) + + for(var/turf/space/T in world) + OnTurfChange(T) + */ +/datum/universal_state/supermatter_cascade/proc/MiscSet() + for (var/obj/machinery/firealarm/alm in machines) + if (!(alm.stat & BROKEN)) + alm.ex_act(2) + +/datum/universal_state/supermatter_cascade/proc/APCSet() + for (var/obj/machinery/power/apc/APC in GLOB.apcs) + if (!(APC.stat & BROKEN) && !APC.is_critical) + APC.chargemode = 0 + if(APC.cell) + APC.cell.charge = 0 + APC.emagged = 1 + APC.queue_icon_update() + +/datum/universal_state/supermatter_cascade/proc/PlayerSet() + for(var/datum/mind/M in player_list) + if(!istype(M.current,/mob/living)) + continue + if(M.current.stat!=2) + M.current.Weaken(10) + M.current.flash_eyes() + + clear_antag_roles(M) diff --git a/code/game/gamemodes/events/PortalStorm.dm b/code/game/gamemodes/events/PortalStorm.dm index 6144f3305bb..7e63b8c533a 100644 --- a/code/game/gamemodes/events/PortalStorm.dm +++ b/code/game/gamemodes/events/PortalStorm.dm @@ -1,26 +1,26 @@ -/datum/event/portalstorm - - Announce() - command_alert("Subspace disruption detected around the vessel", "Anomaly Alert") - LongTerm() - - var/list/turfs = list( ) - var/turf/picked - - for(var/turf/T in world) - if(T.z < 5 && istype(T,/turf/simulated/floor)) - turfs += T - - for(var/turf/T in world) - if(prob(10) && T.z < 5 && istype(T,/turf/simulated/floor)) - spawn(50+rand(0,3000)) - picked = pick(turfs) - var/obj/portal/P = new /obj/portal( T ) - P.target = picked - P.creator = null - P.icon = 'icons/obj/objects.dmi' - P.failchance = 0 - P.icon_state = "anom" - P.name = "wormhole" - spawn(rand(100,150)) - qdel(P) +/datum/event/portalstorm + + Announce() + command_alert("Subspace disruption detected around the vessel", "Anomaly Alert") + LongTerm() + + var/list/turfs = list( ) + var/turf/picked + + for(var/turf/T in world) + if(T.z < 5 && istype(T,/turf/simulated/floor)) + turfs += T + + for(var/turf/T in world) + if(prob(10) && T.z < 5 && istype(T,/turf/simulated/floor)) + spawn(50+rand(0,3000)) + picked = pick(turfs) + var/obj/portal/P = new /obj/portal( T ) + P.target = picked + P.creator = null + P.icon = 'icons/obj/objects.dmi' + P.failchance = 0 + P.icon_state = "anom" + P.name = "wormhole" + spawn(rand(100,150)) + qdel(P) diff --git a/code/game/gamemodes/events/black_hole.dm b/code/game/gamemodes/events/black_hole.dm index b18310ed886..44f6df7005f 100644 --- a/code/game/gamemodes/events/black_hole.dm +++ b/code/game/gamemodes/events/black_hole.dm @@ -1,92 +1,92 @@ -/obj/effect/bhole - name = "black hole" - icon = 'icons/obj/objects.dmi' - desc = "FUCK FUCK FUCK AAAHHH" - icon_state = "bhole3" - opacity = 1 - unacidable = TRUE - density = FALSE - anchored = TRUE - -/obj/effect/bhole/New() - spawn(4) - controller() - -/obj/effect/bhole/proc/controller() - while(src) - - if(!isturf(loc)) - qdel(src) - return - - //DESTROYING STUFF AT THE EPICENTER - for(var/mob/living/M in orange(1,src)) - qdel(M) - for(var/obj/O in orange(1,src)) - qdel(O) - var/base_turf = get_base_turf_by_area(src) - for(var/turf/simulated/ST in orange(1,src)) - if(ST.type == base_turf) - continue - ST.ChangeTurf(base_turf) - - sleep(6) - grav(10, 4, 10, 0 ) - sleep(6) - grav( 8, 4, 10, 0 ) - sleep(6) - grav( 9, 4, 10, 0 ) - sleep(6) - grav( 7, 3, 40, 1 ) - sleep(6) - grav( 5, 3, 40, 1 ) - sleep(6) - grav( 6, 3, 40, 1 ) - sleep(6) - grav( 4, 2, 50, 6 ) - sleep(6) - grav( 3, 2, 50, 6 ) - sleep(6) - grav( 2, 2, 75,25 ) - sleep(6) - - - - //MOVEMENT - if( prob(50) ) - src.anchored = FALSE - step(src,pick(alldirs)) - src.anchored = TRUE - -/obj/effect/bhole/proc/grav(var/r, var/ex_act_force, var/pull_chance, var/turf_removal_chance) - if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen - qdel(src) - return - for(var/t = -r, t < r, t++) - affect_coord(x+t, y-r, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x-t, y+r, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x+r, y+t, ex_act_force, pull_chance, turf_removal_chance) - affect_coord(x-r, y-t, ex_act_force, pull_chance, turf_removal_chance) - return - -/obj/effect/bhole/proc/affect_coord(var/x, var/y, var/ex_act_force, var/pull_chance, var/turf_removal_chance) - //Get turf at coordinate - var/turf/T = locate(x, y, z) - if(isnull(T)) return - - //Pulling and/or ex_act-ing movable atoms in that turf - if( prob(pull_chance) ) - for(var/obj/O in T.contents) - if(O.anchored) - O.ex_act(ex_act_force) - else - step_towards(O,src) - for(var/mob/living/M in T.contents) - step_towards(M,src) - - //Destroying the turf - if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) - var/turf/simulated/ST = T - var/base_turf = get_base_turf_by_area(src) - if(ST.type != base_turf) - ST.ChangeTurf(base_turf) +/obj/effect/bhole + name = "black hole" + icon = 'icons/obj/objects.dmi' + desc = "FUCK FUCK FUCK AAAHHH" + icon_state = "bhole3" + opacity = 1 + unacidable = TRUE + density = FALSE + anchored = TRUE + +/obj/effect/bhole/New() + spawn(4) + controller() + +/obj/effect/bhole/proc/controller() + while(src) + + if(!isturf(loc)) + qdel(src) + return + + //DESTROYING STUFF AT THE EPICENTER + for(var/mob/living/M in orange(1,src)) + qdel(M) + for(var/obj/O in orange(1,src)) + qdel(O) + var/base_turf = get_base_turf_by_area(src) + for(var/turf/simulated/ST in orange(1,src)) + if(ST.type == base_turf) + continue + ST.ChangeTurf(base_turf) + + sleep(6) + grav(10, 4, 10, 0 ) + sleep(6) + grav( 8, 4, 10, 0 ) + sleep(6) + grav( 9, 4, 10, 0 ) + sleep(6) + grav( 7, 3, 40, 1 ) + sleep(6) + grav( 5, 3, 40, 1 ) + sleep(6) + grav( 6, 3, 40, 1 ) + sleep(6) + grav( 4, 2, 50, 6 ) + sleep(6) + grav( 3, 2, 50, 6 ) + sleep(6) + grav( 2, 2, 75,25 ) + sleep(6) + + + + //MOVEMENT + if( prob(50) ) + src.anchored = FALSE + step(src,pick(alldirs)) + src.anchored = TRUE + +/obj/effect/bhole/proc/grav(var/r, var/ex_act_force, var/pull_chance, var/turf_removal_chance) + if(!isturf(loc)) //blackhole cannot be contained inside anything. Weird stuff might happen + qdel(src) + return + for(var/t = -r, t < r, t++) + affect_coord(x+t, y-r, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x-t, y+r, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x+r, y+t, ex_act_force, pull_chance, turf_removal_chance) + affect_coord(x-r, y-t, ex_act_force, pull_chance, turf_removal_chance) + return + +/obj/effect/bhole/proc/affect_coord(var/x, var/y, var/ex_act_force, var/pull_chance, var/turf_removal_chance) + //Get turf at coordinate + var/turf/T = locate(x, y, z) + if(isnull(T)) return + + //Pulling and/or ex_act-ing movable atoms in that turf + if( prob(pull_chance) ) + for(var/obj/O in T.contents) + if(O.anchored) + O.ex_act(ex_act_force) + else + step_towards(O,src) + for(var/mob/living/M in T.contents) + step_towards(M,src) + + //Destroying the turf + if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) + var/turf/simulated/ST = T + var/base_turf = get_base_turf_by_area(src) + if(ST.type != base_turf) + ST.ChangeTurf(base_turf) diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm index e0991edaa2d..62a29d823c2 100644 --- a/code/game/gamemodes/events/holidays/Christmas.dm +++ b/code/game/gamemodes/events/holidays/Christmas.dm @@ -1,63 +1,63 @@ -/proc/Christmas_Game_Start() - for(var/obj/structure/flora/tree/pine/xmas in world) - if(isNotStationLevel(xmas.z)) continue - for(var/turf/simulated/floor/T in orange(1,xmas)) - for(var/i=1,i<=rand(1,5),i++) - new /obj/item/weapon/a_gift(T) - //for(var/mob/living/simple_mob/corgi/Ian/Ian in mob_list) - // Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian)) - -/proc/ChristmasEvent() - for(var/obj/structure/flora/tree/pine/xmas in world) - var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc) - evil_tree.icon_state = xmas.icon_state - evil_tree.icon_living = evil_tree.icon_state - evil_tree.icon_dead = evil_tree.icon_state - evil_tree.icon_gib = evil_tree.icon_state - qdel(xmas) - -/obj/item/weapon/toy/xmas_cracker - name = "xmas cracker" - icon = 'icons/obj/christmas.dmi' - icon_state = "cracker" - desc = "Directions for use: Requires two people, one to pull each end." - var/cracked = 0 - -/obj/item/weapon/toy/xmas_cracker/New() - ..() - -/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user) - if( !cracked && (istype(target,/mob/living/silicon) || (istype(target,/mob/living/carbon/human) && !target.get_active_hand())) && target.stat == CONSCIOUS) - target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") - var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc) - Joke.name = "[pick("awful","terrible","unfunny")] joke" - Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'", - "Why couldn't the snowman get laid?\n\nHe was frigid!", - "Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.", - "What happened to the man who stole advent calanders?\n\nHe got 25 days.", - "What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.", - "Where do you find chili beans?\n\nThe north pole.", - "What do you get from eating tree decorations?\n\nTinsilitis!", - "What do snowmen wear on their heads?\n\nIce caps!", - "Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.", - "Why doesn't Santa have any children?\n\nBecause he only comes down the chimney.") - new /obj/item/clothing/head/festive(target.loc) - user.update_icons() - cracked = 1 - icon_state = "cracker1" - var/obj/item/weapon/toy/xmas_cracker/other_half = new /obj/item/weapon/toy/xmas_cracker(target) - other_half.cracked = 1 - other_half.icon_state = "cracker2" - target.put_in_active_hand(other_half) - playsound(src, 'sound/effects/snap.ogg', 50, 1) - return 1 - return ..() - -/obj/item/clothing/head/festive - name = "festive paper hat" - icon_state = "xmashat" - desc = "A crappy paper hat that you are REQUIRED to wear." - flags_inv = 0 - body_parts_covered = 0 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - +/proc/Christmas_Game_Start() + for(var/obj/structure/flora/tree/pine/xmas in world) + if(isNotStationLevel(xmas.z)) continue + for(var/turf/simulated/floor/T in orange(1,xmas)) + for(var/i=1,i<=rand(1,5),i++) + new /obj/item/weapon/a_gift(T) + //for(var/mob/living/simple_mob/corgi/Ian/Ian in mob_list) + // Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian)) + +/proc/ChristmasEvent() + for(var/obj/structure/flora/tree/pine/xmas in world) + var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc) + evil_tree.icon_state = xmas.icon_state + evil_tree.icon_living = evil_tree.icon_state + evil_tree.icon_dead = evil_tree.icon_state + evil_tree.icon_gib = evil_tree.icon_state + qdel(xmas) + +/obj/item/weapon/toy/xmas_cracker + name = "xmas cracker" + icon = 'icons/obj/christmas.dmi' + icon_state = "cracker" + desc = "Directions for use: Requires two people, one to pull each end." + var/cracked = 0 + +/obj/item/weapon/toy/xmas_cracker/New() + ..() + +/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user) + if( !cracked && (istype(target,/mob/living/silicon) || (istype(target,/mob/living/carbon/human) && !target.get_active_hand())) && target.stat == CONSCIOUS) + target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") + var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc) + Joke.name = "[pick("awful","terrible","unfunny")] joke" + Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'", + "Why couldn't the snowman get laid?\n\nHe was frigid!", + "Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.", + "What happened to the man who stole advent calanders?\n\nHe got 25 days.", + "What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.", + "Where do you find chili beans?\n\nThe north pole.", + "What do you get from eating tree decorations?\n\nTinsilitis!", + "What do snowmen wear on their heads?\n\nIce caps!", + "Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.", + "Why doesn't Santa have any children?\n\nBecause he only comes down the chimney.") + new /obj/item/clothing/head/festive(target.loc) + user.update_icons() + cracked = 1 + icon_state = "cracker1" + var/obj/item/weapon/toy/xmas_cracker/other_half = new /obj/item/weapon/toy/xmas_cracker(target) + other_half.cracked = 1 + other_half.icon_state = "cracker2" + target.put_in_active_hand(other_half) + playsound(src, 'sound/effects/snap.ogg', 50, 1) + return 1 + return ..() + +/obj/item/clothing/head/festive + name = "festive paper hat" + icon_state = "xmashat" + desc = "A crappy paper hat that you are REQUIRED to wear." + flags_inv = 0 + body_parts_covered = 0 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + diff --git a/code/game/gamemodes/events/holidays/Holidays.dm b/code/game/gamemodes/events/holidays/Holidays.dm index 3995f537846..b274d8a0fad 100644 --- a/code/game/gamemodes/events/holidays/Holidays.dm +++ b/code/game/gamemodes/events/holidays/Holidays.dm @@ -1,311 +1,311 @@ -//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays -var/global/list/Holiday = list() //Holidays are lists now, so we can have more than one holiday at the same time (hey, you never know). - -//Just thinking ahead! Here's the foundations to a more robust Holiday event system. -//It's easy as hell to add stuff. Just set Holiday to something using the switch (or something else) -//then use if(Holiday == "MyHoliday") to make stuff happen on that specific day only -//Please, Don't spam stuff up with easter eggs, I'd rather somebody just delete this than people cause -//the game to lag even more in the name of one-day content. - -////////////////////////////////////////////////////////////////////////////////////////////////////////// -//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// -////////////////////////////////////////////////////////////////////////////////////////////////////////// -// ~Carn - -/hook/startup/proc/updateHoliday() - Get_Holiday() - return 1 - -//sets up the Holiday global variable. Shouldbe called on game configuration or something. -/proc/Get_Holiday() - if(!Holiday) return // Holiday stuff was not enabled in the config! - - Holiday = list() // reset our switch now so we can recycle it as our Holiday name - - //var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - unused currently but can be used for floating dates - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - - //Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever - //Holidays are now associate lists. You should write holidays like this. - //Holiday["Holiday Name Here"] = "Blurb about the holiday here." - switch(MM) - if(1) //Jan - switch(DD) - if(1) - Holiday["New Years's Day"] = "The day of the new solar year on Sol." - if(12) - Holiday["Vertalliq-Qerr"] = "Vertalliq-Qerr, translated to mean 'Festival of the Royals', is a \ - skrellian holiday that celebrates the Qerr-Katish and all they have provided for the rest of skrellian society, \ - it often features colourful displays and skilled performers take this time to show off some of their more \ - elaborate displays." - if(14) - Holiday["Lohri"] = "A human festival traditionally celebrating the end of winter on the Indian subcontinent. \ - The holiday is now celebrated independently of seasons in many colonies with large populations of Indian \ - descent. Traditions include the burning of bonfires, dancing, and door-to-door singing in exchange for treats." - if(30) - Holiday["Lunar New Year"] = "Originally the new year on the ancient lunisolar calendar, the Lunar New Year is \ - celebrated with a wide variety of east Asian traditions with roots in Chinese, Japanese, Korean, Vietnamese, \ - Tibetan, Mongolian, and Ryukyu cultures. Elaborate parades, performances, dances and meals are usual staples." - - if(2) //Feb - switch(DD) - if(2) - Holiday["Groundhog Day"] = "An unoffical holiday based on medieval folklore that originated on Earth, \ - that involves the reverence of a prophetic animal - traditionally a badger, fox or groundhog - that was \ - said to be able to predict, or even control the changing of the seasons." - if(14) - Holiday["Valentine's Day"] = "A human holiday that revolves around expressions of romance and love. \ - In particular, the exchanging of gifts, letters and cards is traditional." - if(15) - Holiday["Lantern Festival"] = "A human holiday with origins in Chinese new year celebrations. Participants \ - carry or hang elaborate paper lanterns that are thought to bring good luck. Today, electric lights are often used \ - in environments where open flames would be hazardous or non-functional." - if(17) - Holiday["Random Acts of Kindness Day"] = "An unoffical holiday that challenges everyone to perform \ - acts of kindness to their friends, co-workers, and strangers, with no strings attached." - - if(3) //Mar - switch(DD) - if(3) - Holiday["Qixm-tes"] = "Qixm-tes, or 'Day of mourning', is a skrellian holiday where skrell gather at places \ - of worship and sing a song of mourning for all those who have died in service to their kingdoms." - if(14) - Holiday["Pi Day"] = "An unoffical holiday celebrating the mathematical constant Pi. It is celebrated on \ - March 14th, as the digits form 3 14, the first three significant digits of Pi. Observance of Pi Day generally \ - involve eating (or throwing) pie, due to a pun. Pies also tend to be round, and thus relatable to Pi." - if(17) - Holiday["St. Patrick's Day"] = "A holiday originating on Earth, celebrating a popular version of Irish culture. \ - Traditions include elaborate parades, wearing of the colour green, and drinking alcohol." - if(18) - Holiday["Holi"] = "Also known as the Festival of Colours, a human Hindu festival celebrating divine love and the \ - triumph of good over evil. Traditionally a bonfire is lit overnight, followed by the free-for-all smearing of \ - celebrants with colourful pigments, and the forgiveness of past wrongs." - if(27) - Holiday["Easter"] = "A Earth springtime festival variously celebrating rebirth and the beginning of the planting \ - season. Traditionally celebrated with the painting and exchange of eggs, sometimes made from chocolate. \ - The holiday's date was standardized in the 22nd century." - - if(4) //Apr - switch(DD) - if(1) - Holiday["April Fool's Day"] = "A human holiday that endevours one to pull pranks and spread hoaxes on their friends." - if(5) - Holiday["First Day of Passover"] = "The first of eight days of a human holiday celebrating the exodus of ancient Jewish people \ - from slavery, and of the spring harvest. The most well-known tradition is the Sedar meal. The date was standardized in the 22nd century." - if(22) - Holiday["Earth Day"] = "A holiday of enviromentalism, that originated on it's namesake, Earth." - - if(5) //May - switch(DD) - if(1) - Holiday["Interstellar Workers' Day"] = "This holiday celebrates the work of laborers and the working class." - if(18) - Holiday["Remembrance Day"] = "Remembrance Day (or, as it is more informally known, Armistice Day) is a confederation-wide holiday \ - mostly observed by its member states since late 2280. Officially, it is a day of remembering the men and women who died in various armed conflicts \ - throughout human history. Unofficially, however, it is commonly treated as a holiday honoring the victims of the Human-Unathi war. \ - Observance of this day varies throughout human space, but most common traditions are the act of bringing flowers to graves,\ - attending parades, and the wearing of poppies (either paper or real) in one's clothing." - if(28) - Holiday["Jiql-tes"] = "A skrellian holiday that translates to 'Day of Celebration', skrell communities \ - gather for a grand feast and give gifts to friends and close relatives." - - if(6) //Jun - switch(DD) - if(6) - Holiday["Sapient Rights Day"] = "This holiday celebrates the passing of the Declaration of Sapient Rights by SolGov, which guarantees the \ - same protections humans are granted to all sapient, living species." - if(14) - Holiday["Blood Donor Day"] = "This holiday was created to raise awareness of the need for safe blood and blood products, \ - and to thank blood donors for their voluntary, life-saving gifts of blood." - if(20) - Holiday["Civil Servant's Day"] = "Civil Servant's Day is a holiday observed in SCG member states that honors civil servants everywhere,\ - (especially those who are members of the armed forces and the emergency services), or have been or have been civil servants in the past." -/* if(25) - Holiday["Merhyat Njarha"] = "A Njarir'Akhan tajaran tradition translating to \"Harmony of the House\", in which Njarjirii citizens pay \ - homage to their ruling house and their ancestors. Traditions include large communal meals and dances hosted by the ruling house, \ - and the intensive upkeep of community spaces."*/ - - if(7) //Jul - switch(DD) - if(1) - Holiday["Doctors' Day"] = "A holiday that recognizes the services of physicians, commonly celebrated \ - in healthcare organizations and facilities." - if(30) - Holiday["Friendship Day"] = "An unoffical holiday that recognizes the value of friends and companionship. Indeed, not having someone watch \ - your back while in space can be dangerous, and the cold, isolating nature of space makes friends all the more important." - - if(8) //Aug - switch(DD) -//VOREStation Add - Of course we need this. - if(8) - Holiday["Vore Day"] = "A holiday representing the innate desire in all/most/some/a few of us to devour each other or be devoured. \ - That's probably why you're here, isn't it? Get to it, then!" -//VOREStation Add End. -/* if(10) - Holiday["S'randarr's Day"] = "A Tajaran holiday that occurs on the longest day of the year in summer, - on Ahdomai. It is named after the Tajaran deity of Light, and huge celebrations are common." - if(11) - Holiday["Tajaran Contact Day"] = "The anniversary of first contact between SolGov and the tajaran species, widely observed\ - throughout tajaran and human space. Marks the date that in 2513, a human exploration team investigating electromagnetic \ - emissions from the Meralar system made radio contact with the tajaran scientific outpost that had broadcast them."*/ - if(20) - Holiday["Obon"] = "An ancient Earth holiday originating in east Asia, for the honouring of one's ancestral spirits. \ - Traditions include the maintenance of grave sites and memorials, and community traditional dance performances." - if(27) - Holiday["Forgiveness Day"] = "A time to forgive and be forgiven." - - if(9) //Sep - switch(DD) - if(17) - Holiday["Qill-xamr"] = "Translated to 'Night of the dead', it is a skrellian holiday where skrell \ - communities hold parties in order to remember loved ones who passed, unlike Qixm-tes, this applies to everyone \ - and is a joyful celebration." - if(19) - Holiday["Talk-Like-a-Pirate Day"] = "Ahoy, matey! It be the unoffical holiday celebratin' the salty \ - sea humor of speakin' like the pirates of old." - if(20) - Holiday["Rosh Hashanah"] = "An old human holiday that marks the traditional Hebrew new year." - if(28) - Holiday["Stupid-Questions Day"] = "Known as Ask A Stupid Question Day, it is an unoffical holiday \ - created by teachers in Sol, very long ago, to encourage students to ask more questions in the classroom." - - if(10) //Oct - switch(DD) - if(9) - Holiday["Lief Eriksson Day"] = "A day commemorating Norse explorer Lief Eriksson, an early Scandinavian cultural figure \ - who is thought to have been the first European to set foot in North America." - if(16) - Holiday["Boss' Day"] = "Boss' Day has traditionally been a day for employees to thank their bosses for the difficult work that they do \ - throughout the year. This day was created for the purpose of strengthening the bond between employer and employee." - if(21) - Holiday["First Day of Diwali"] = "An ancient Hindu, Jain and Sikh festival lasting five days, celebrating victory of light over darkness, good over \ - evil, and knowledge over ignorance. It is celebrated by the wearing of your finest clothes, decorating with oil lamps and rangolis, \ - fireworks, and gift-giving. Electric lights are often used in modern times where oil lamps would be hazardous or inoperable." - if(31) - Holiday["Halloween"] = "Originating from Earth, Halloween is also known as All Saints' Eve, and \ - is celebrated by some by attending costume parties, trick-or-treating, carving faces in pumpkins, or visiting \ - 'haunted' locations. Some people make it a goal to scare other people." - - if(11) //Nov - switch(DD) - if(1) - Holiday["Day of the Dead"] = "An old human holiday celebrating the lives of deceased friends and family members, \ - by means of good humour and joyful parties. Offerings are often left at altars to the dead, and exchanging gifts \ - among the living is not uncommon." - if(13) - Holiday["Kindness Day"] = "Kindness Day is an unofficial holiday to highlight good deeds in the \ - community, focusing on the positive power and the common thread of kindness which binds humanity and \ - friends together." - if(28) //Space thanksgiving. - Holiday["Appreciation Day"] = "Originally an old holiday from Earth, Appreciation Day follows many of the \ - traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ - for what one has in life." - if(28 > DD > 20) - if(time2text(world.timeofday, "Day") == "Thursday") - Holiday["Thanksgiving"] = "Originally an old holiday from Earth, Thanksgiving follows many of the \ - traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ - for what one has in life." - - if(12) //Dec - switch(DD) - if(10) - Holiday["Human-Rights Day"] = "An old holiday created by an intergovernmental organization known back than as the United Nations, \ - human rights were not recognized globally at the time, and the holiday was made in honor of the Universal Declaration of Human Rights. \ - These days, SolGov ensures that past efforts were not in vein, and continues to honor this holiday across the galaxy as a historical \ - reminder." - if(22) - Holiday["Vertalliq-qixim"] = "A skrellian holiday that celebrates the skrell's first landing on one of \ - their moons. It's often celebrated with grand festivals." - if(24) - Holiday["Christmas Eve"] = "The eve of Christmas, an old holiday from Earth that mainly involves gift \ - giving, decorating, family reunions, and a fat red human breaking into people's homes to steal milk and cookies." - if(25) - Holiday["Christmas"] = "Christmas is a very old holiday that originated in Earth, Sol. It was a \ - religious holiday for the Christian religion, which would later form Unitarianism. Nowadays, the holiday is celebrated \ - generally by giving gifts, symbolic decoration, and reuniting with one's family. It also features a mythical fat \ - red human, known as Santa, who broke into people's homes to loot cookies and milk." - if(31) - Holiday["New Year's Eve"] = "The eve of the New Year for Sol. It is traditionally celebrated by counting down to midnight, as that is \ - when the new year begins. Other activities include planning for self-improvement over the new year, attending New Year's parties, or \ - watching a timer count to zero, a large object descending, and fireworks exploding in the sky, in person or on broadcast." - - if(!Holiday) - //Friday the 13th - if(DD == 13) - if(time2text(world.timeofday, "DDD") == "Fri") - Holiday["Friday the 13th"] = "Friday the 13th is a superstitious day, associated with bad luck and misfortune." - -//Allows GA and GM to set the Holiday variable -/client/proc/Set_Holiday() - set name = ".Set Holiday" - set category = "Fun" - set desc = "Force-set the Holiday variable to make the game think it's a certain day." - if(!check_rights(R_SERVER)) return - - Holiday = list() - - var/H = tgui_input_text(src,"What holiday is it today?","Set Holiday") - var/B = tgui_input_text(src,"Now explain what the holiday is about","Set Holiday", multiline = TRUE, prevent_enter = TRUE) - - - Holiday[H] = B - - //update our hub status - world.update_status() - Holiday_Game_Start() - - message_admins("ADMIN: Event: [key_name(src)] force-set Holiday to \"[Holiday]\"") - log_admin("[key_name(src)] force-set Holiday to \"[Holiday]\"") - - -//Run at the start of a round -/proc/Holiday_Game_Start() - if(Holiday.len != 0) - var/list/holidays = list() - var/list/holiday_blurbs = list() - for(var/p in Holiday) - holidays.Add(p) - holiday_blurbs.Add("[Holiday[p]]") - var/holidays_string = english_list(holidays, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - to_world("and...") - to_world("

    Happy [holidays_string] Everybody!

    ") - if(holiday_blurbs.len != 0) - for(var/blurb in holiday_blurbs) - to_world("
    [blurb]
    ") - switch(Holiday) //special holidays - if("Easter") - //do easter stuff - if("Christmas Eve","Christmas") - Christmas_Game_Start() - - return - -//Nested in the random events loop. Will be triggered every 2 minutes -/proc/Holiday_Random_Event() - if(isemptylist(Holiday)) - return 0 - switch(Holiday) //special holidays - if("Easter") //I'll make this into some helper procs at some point -/* var/list/turf/simulated/floor/Floorlist = list() - for(var/turf/simulated/floor/T) - if(T.contents) - Floorlist += T - var/turf/simulated/floor/F = Floorlist[rand(1,Floorlist.len)] - Floorlist = null - var/obj/structure/closet/C = locate(/obj/structure/closet) in F - var/obj/item/weapon/reagent_containers/food/snacks/chocolateegg/wrapped/Egg - if( C ) Egg = new(C) - else Egg = new(F) -*/ -/* var/list/obj/containers = list() - for(var/obj/item/weapon/storage/S in world) - if(isNotStationLevel(S.z)) continue - containers += S - - message_admins("DEBUG: Event: Egg spawned at [Egg.loc] ([Egg.x],[Egg.y],[Egg.z])")*/ - if("End of the World") - if(prob(eventchance)) GameOver() - - if("Christmas","Christmas Eve") - if(prob(eventchance)) ChristmasEvent() +//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays +var/global/list/Holiday = list() //Holidays are lists now, so we can have more than one holiday at the same time (hey, you never know). + +//Just thinking ahead! Here's the foundations to a more robust Holiday event system. +//It's easy as hell to add stuff. Just set Holiday to something using the switch (or something else) +//then use if(Holiday == "MyHoliday") to make stuff happen on that specific day only +//Please, Don't spam stuff up with easter eggs, I'd rather somebody just delete this than people cause +//the game to lag even more in the name of one-day content. + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ~Carn + +/hook/startup/proc/updateHoliday() + Get_Holiday() + return 1 + +//sets up the Holiday global variable. Shouldbe called on game configuration or something. +/proc/Get_Holiday() + if(!Holiday) return // Holiday stuff was not enabled in the config! + + Holiday = list() // reset our switch now so we can recycle it as our Holiday name + + //var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - unused currently but can be used for floating dates + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + + //Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever + //Holidays are now associate lists. You should write holidays like this. + //Holiday["Holiday Name Here"] = "Blurb about the holiday here." + switch(MM) + if(1) //Jan + switch(DD) + if(1) + Holiday["New Years's Day"] = "The day of the new solar year on Sol." + if(12) + Holiday["Vertalliq-Qerr"] = "Vertalliq-Qerr, translated to mean 'Festival of the Royals', is a \ + skrellian holiday that celebrates the Qerr-Katish and all they have provided for the rest of skrellian society, \ + it often features colourful displays and skilled performers take this time to show off some of their more \ + elaborate displays." + if(14) + Holiday["Lohri"] = "A human festival traditionally celebrating the end of winter on the Indian subcontinent. \ + The holiday is now celebrated independently of seasons in many colonies with large populations of Indian \ + descent. Traditions include the burning of bonfires, dancing, and door-to-door singing in exchange for treats." + if(30) + Holiday["Lunar New Year"] = "Originally the new year on the ancient lunisolar calendar, the Lunar New Year is \ + celebrated with a wide variety of east Asian traditions with roots in Chinese, Japanese, Korean, Vietnamese, \ + Tibetan, Mongolian, and Ryukyu cultures. Elaborate parades, performances, dances and meals are usual staples." + + if(2) //Feb + switch(DD) + if(2) + Holiday["Groundhog Day"] = "An unoffical holiday based on medieval folklore that originated on Earth, \ + that involves the reverence of a prophetic animal - traditionally a badger, fox or groundhog - that was \ + said to be able to predict, or even control the changing of the seasons." + if(14) + Holiday["Valentine's Day"] = "A human holiday that revolves around expressions of romance and love. \ + In particular, the exchanging of gifts, letters and cards is traditional." + if(15) + Holiday["Lantern Festival"] = "A human holiday with origins in Chinese new year celebrations. Participants \ + carry or hang elaborate paper lanterns that are thought to bring good luck. Today, electric lights are often used \ + in environments where open flames would be hazardous or non-functional." + if(17) + Holiday["Random Acts of Kindness Day"] = "An unoffical holiday that challenges everyone to perform \ + acts of kindness to their friends, co-workers, and strangers, with no strings attached." + + if(3) //Mar + switch(DD) + if(3) + Holiday["Qixm-tes"] = "Qixm-tes, or 'Day of mourning', is a skrellian holiday where skrell gather at places \ + of worship and sing a song of mourning for all those who have died in service to their kingdoms." + if(14) + Holiday["Pi Day"] = "An unoffical holiday celebrating the mathematical constant Pi. It is celebrated on \ + March 14th, as the digits form 3 14, the first three significant digits of Pi. Observance of Pi Day generally \ + involve eating (or throwing) pie, due to a pun. Pies also tend to be round, and thus relatable to Pi." + if(17) + Holiday["St. Patrick's Day"] = "A holiday originating on Earth, celebrating a popular version of Irish culture. \ + Traditions include elaborate parades, wearing of the colour green, and drinking alcohol." + if(18) + Holiday["Holi"] = "Also known as the Festival of Colours, a human Hindu festival celebrating divine love and the \ + triumph of good over evil. Traditionally a bonfire is lit overnight, followed by the free-for-all smearing of \ + celebrants with colourful pigments, and the forgiveness of past wrongs." + if(27) + Holiday["Easter"] = "A Earth springtime festival variously celebrating rebirth and the beginning of the planting \ + season. Traditionally celebrated with the painting and exchange of eggs, sometimes made from chocolate. \ + The holiday's date was standardized in the 22nd century." + + if(4) //Apr + switch(DD) + if(1) + Holiday["April Fool's Day"] = "A human holiday that endevours one to pull pranks and spread hoaxes on their friends." + if(5) + Holiday["First Day of Passover"] = "The first of eight days of a human holiday celebrating the exodus of ancient Jewish people \ + from slavery, and of the spring harvest. The most well-known tradition is the Sedar meal. The date was standardized in the 22nd century." + if(22) + Holiday["Earth Day"] = "A holiday of enviromentalism, that originated on it's namesake, Earth." + + if(5) //May + switch(DD) + if(1) + Holiday["Interstellar Workers' Day"] = "This holiday celebrates the work of laborers and the working class." + if(18) + Holiday["Remembrance Day"] = "Remembrance Day (or, as it is more informally known, Armistice Day) is a confederation-wide holiday \ + mostly observed by its member states since late 2280. Officially, it is a day of remembering the men and women who died in various armed conflicts \ + throughout human history. Unofficially, however, it is commonly treated as a holiday honoring the victims of the Human-Unathi war. \ + Observance of this day varies throughout human space, but most common traditions are the act of bringing flowers to graves,\ + attending parades, and the wearing of poppies (either paper or real) in one's clothing." + if(28) + Holiday["Jiql-tes"] = "A skrellian holiday that translates to 'Day of Celebration', skrell communities \ + gather for a grand feast and give gifts to friends and close relatives." + + if(6) //Jun + switch(DD) + if(6) + Holiday["Sapient Rights Day"] = "This holiday celebrates the passing of the Declaration of Sapient Rights by SolGov, which guarantees the \ + same protections humans are granted to all sapient, living species." + if(14) + Holiday["Blood Donor Day"] = "This holiday was created to raise awareness of the need for safe blood and blood products, \ + and to thank blood donors for their voluntary, life-saving gifts of blood." + if(20) + Holiday["Civil Servant's Day"] = "Civil Servant's Day is a holiday observed in SCG member states that honors civil servants everywhere,\ + (especially those who are members of the armed forces and the emergency services), or have been or have been civil servants in the past." +/* if(25) + Holiday["Merhyat Njarha"] = "A Njarir'Akhan tajaran tradition translating to \"Harmony of the House\", in which Njarjirii citizens pay \ + homage to their ruling house and their ancestors. Traditions include large communal meals and dances hosted by the ruling house, \ + and the intensive upkeep of community spaces."*/ + + if(7) //Jul + switch(DD) + if(1) + Holiday["Doctors' Day"] = "A holiday that recognizes the services of physicians, commonly celebrated \ + in healthcare organizations and facilities." + if(30) + Holiday["Friendship Day"] = "An unoffical holiday that recognizes the value of friends and companionship. Indeed, not having someone watch \ + your back while in space can be dangerous, and the cold, isolating nature of space makes friends all the more important." + + if(8) //Aug + switch(DD) +//VOREStation Add - Of course we need this. + if(8) + Holiday["Vore Day"] = "A holiday representing the innate desire in all/most/some/a few of us to devour each other or be devoured. \ + That's probably why you're here, isn't it? Get to it, then!" +//VOREStation Add End. +/* if(10) + Holiday["S'randarr's Day"] = "A Tajaran holiday that occurs on the longest day of the year in summer, + on Ahdomai. It is named after the Tajaran deity of Light, and huge celebrations are common." + if(11) + Holiday["Tajaran Contact Day"] = "The anniversary of first contact between SolGov and the tajaran species, widely observed\ + throughout tajaran and human space. Marks the date that in 2513, a human exploration team investigating electromagnetic \ + emissions from the Meralar system made radio contact with the tajaran scientific outpost that had broadcast them."*/ + if(20) + Holiday["Obon"] = "An ancient Earth holiday originating in east Asia, for the honouring of one's ancestral spirits. \ + Traditions include the maintenance of grave sites and memorials, and community traditional dance performances." + if(27) + Holiday["Forgiveness Day"] = "A time to forgive and be forgiven." + + if(9) //Sep + switch(DD) + if(17) + Holiday["Qill-xamr"] = "Translated to 'Night of the dead', it is a skrellian holiday where skrell \ + communities hold parties in order to remember loved ones who passed, unlike Qixm-tes, this applies to everyone \ + and is a joyful celebration." + if(19) + Holiday["Talk-Like-a-Pirate Day"] = "Ahoy, matey! It be the unoffical holiday celebratin' the salty \ + sea humor of speakin' like the pirates of old." + if(20) + Holiday["Rosh Hashanah"] = "An old human holiday that marks the traditional Hebrew new year." + if(28) + Holiday["Stupid-Questions Day"] = "Known as Ask A Stupid Question Day, it is an unoffical holiday \ + created by teachers in Sol, very long ago, to encourage students to ask more questions in the classroom." + + if(10) //Oct + switch(DD) + if(9) + Holiday["Lief Eriksson Day"] = "A day commemorating Norse explorer Lief Eriksson, an early Scandinavian cultural figure \ + who is thought to have been the first European to set foot in North America." + if(16) + Holiday["Boss' Day"] = "Boss' Day has traditionally been a day for employees to thank their bosses for the difficult work that they do \ + throughout the year. This day was created for the purpose of strengthening the bond between employer and employee." + if(21) + Holiday["First Day of Diwali"] = "An ancient Hindu, Jain and Sikh festival lasting five days, celebrating victory of light over darkness, good over \ + evil, and knowledge over ignorance. It is celebrated by the wearing of your finest clothes, decorating with oil lamps and rangolis, \ + fireworks, and gift-giving. Electric lights are often used in modern times where oil lamps would be hazardous or inoperable." + if(31) + Holiday["Halloween"] = "Originating from Earth, Halloween is also known as All Saints' Eve, and \ + is celebrated by some by attending costume parties, trick-or-treating, carving faces in pumpkins, or visiting \ + 'haunted' locations. Some people make it a goal to scare other people." + + if(11) //Nov + switch(DD) + if(1) + Holiday["Day of the Dead"] = "An old human holiday celebrating the lives of deceased friends and family members, \ + by means of good humour and joyful parties. Offerings are often left at altars to the dead, and exchanging gifts \ + among the living is not uncommon." + if(13) + Holiday["Kindness Day"] = "Kindness Day is an unofficial holiday to highlight good deeds in the \ + community, focusing on the positive power and the common thread of kindness which binds humanity and \ + friends together." + if(28) //Space thanksgiving. + Holiday["Appreciation Day"] = "Originally an old holiday from Earth, Appreciation Day follows many of the \ + traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ + for what one has in life." + if(28 > DD > 20) + if(time2text(world.timeofday, "Day") == "Thursday") + Holiday["Thanksgiving"] = "Originally an old holiday from Earth, Thanksgiving follows many of the \ + traditions that its predecessor did, such as having a large feast (turkey often included), gathering with family, and being thankful \ + for what one has in life." + + if(12) //Dec + switch(DD) + if(10) + Holiday["Human-Rights Day"] = "An old holiday created by an intergovernmental organization known back than as the United Nations, \ + human rights were not recognized globally at the time, and the holiday was made in honor of the Universal Declaration of Human Rights. \ + These days, SolGov ensures that past efforts were not in vein, and continues to honor this holiday across the galaxy as a historical \ + reminder." + if(22) + Holiday["Vertalliq-qixim"] = "A skrellian holiday that celebrates the skrell's first landing on one of \ + their moons. It's often celebrated with grand festivals." + if(24) + Holiday["Christmas Eve"] = "The eve of Christmas, an old holiday from Earth that mainly involves gift \ + giving, decorating, family reunions, and a fat red human breaking into people's homes to steal milk and cookies." + if(25) + Holiday["Christmas"] = "Christmas is a very old holiday that originated in Earth, Sol. It was a \ + religious holiday for the Christian religion, which would later form Unitarianism. Nowadays, the holiday is celebrated \ + generally by giving gifts, symbolic decoration, and reuniting with one's family. It also features a mythical fat \ + red human, known as Santa, who broke into people's homes to loot cookies and milk." + if(31) + Holiday["New Year's Eve"] = "The eve of the New Year for Sol. It is traditionally celebrated by counting down to midnight, as that is \ + when the new year begins. Other activities include planning for self-improvement over the new year, attending New Year's parties, or \ + watching a timer count to zero, a large object descending, and fireworks exploding in the sky, in person or on broadcast." + + if(!Holiday) + //Friday the 13th + if(DD == 13) + if(time2text(world.timeofday, "DDD") == "Fri") + Holiday["Friday the 13th"] = "Friday the 13th is a superstitious day, associated with bad luck and misfortune." + +//Allows GA and GM to set the Holiday variable +/client/proc/Set_Holiday() + set name = ".Set Holiday" + set category = "Fun" + set desc = "Force-set the Holiday variable to make the game think it's a certain day." + if(!check_rights(R_SERVER)) return + + Holiday = list() + + var/H = tgui_input_text(src,"What holiday is it today?","Set Holiday") + var/B = tgui_input_text(src,"Now explain what the holiday is about","Set Holiday", multiline = TRUE, prevent_enter = TRUE) + + + Holiday[H] = B + + //update our hub status + world.update_status() + Holiday_Game_Start() + + message_admins("ADMIN: Event: [key_name(src)] force-set Holiday to \"[Holiday]\"") + log_admin("[key_name(src)] force-set Holiday to \"[Holiday]\"") + + +//Run at the start of a round +/proc/Holiday_Game_Start() + if(Holiday.len != 0) + var/list/holidays = list() + var/list/holiday_blurbs = list() + for(var/p in Holiday) + holidays.Add(p) + holiday_blurbs.Add("[Holiday[p]]") + var/holidays_string = english_list(holidays, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) + to_world(span_blue("and...")) + to_world("

    Happy [holidays_string] Everybody!

    ") + if(holiday_blurbs.len != 0) + for(var/blurb in holiday_blurbs) + to_world(span_blue("
    [blurb]
    ")) + switch(Holiday) //special holidays + if("Easter") + //do easter stuff + if("Christmas Eve","Christmas") + Christmas_Game_Start() + + return + +//Nested in the random events loop. Will be triggered every 2 minutes +/proc/Holiday_Random_Event() + if(isemptylist(Holiday)) + return 0 + switch(Holiday) //special holidays + if("Easter") //I'll make this into some helper procs at some point +/* var/list/turf/simulated/floor/Floorlist = list() + for(var/turf/simulated/floor/T) + if(T.contents) + Floorlist += T + var/turf/simulated/floor/F = Floorlist[rand(1,Floorlist.len)] + Floorlist = null + var/obj/structure/closet/C = locate(/obj/structure/closet) in F + var/obj/item/weapon/reagent_containers/food/snacks/chocolateegg/wrapped/Egg + if( C ) Egg = new(C) + else Egg = new(F) +*/ +/* var/list/obj/containers = list() + for(var/obj/item/weapon/storage/S in world) + if(isNotStationLevel(S.z)) continue + containers += S + + message_admins("DEBUG: Event: Egg spawned at [Egg.loc] ([Egg.x],[Egg.y],[Egg.z])")*/ + if("End of the World") + if(prob(eventchance)) GameOver() + + if("Christmas","Christmas Eve") + if(prob(eventchance)) ChristmasEvent() diff --git a/code/game/gamemodes/events/holidays/Other.dm b/code/game/gamemodes/events/holidays/Other.dm index 74b47506aaf..bd64fda833f 100644 --- a/code/game/gamemodes/events/holidays/Other.dm +++ b/code/game/gamemodes/events/holidays/Other.dm @@ -1,10 +1,10 @@ -/proc/GameOver() - if(!hadevent) - hadevent = 1 - message_admins("The apocalypse has begun! (this holiday event can be disabled by toggling events off within 60 seconds)") - spawn(600) - if(!config.allow_random_events) return - Show2Group4Delay(ScreenText(null,"
    GAME OVER
    "),null,150) - for(var/i=1,i<=4,i++) - spawn_dynamic_event() +/proc/GameOver() + if(!hadevent) + hadevent = 1 + message_admins("The apocalypse has begun! (this holiday event can be disabled by toggling events off within 60 seconds)") + spawn(600) + if(!config.allow_random_events) return + Show2Group4Delay(ScreenText(null,"
    GAME OVER
    "),null,150) + for(var/i=1,i<=4,i++) + spawn_dynamic_event() sleep(50) \ No newline at end of file diff --git a/code/game/gamemodes/events/wormholes.dm b/code/game/gamemodes/events/wormholes.dm index 20f7da63f3e..65b21df2ea5 100644 --- a/code/game/gamemodes/events/wormholes.dm +++ b/code/game/gamemodes/events/wormholes.dm @@ -1,70 +1,70 @@ -/proc/wormhole_event(var/set_duration = 5 MINUTES, var/wormhole_duration_modifier = 1) - spawn() - var/list/pick_turfs = list() - var/list/Z_choices = list() - Z_choices |= using_map.get_map_levels(1, FALSE) - for(var/turf/simulated/floor/T in world) - if(T.z in Z_choices) - if(!T.block_tele) - pick_turfs += T - - if(pick_turfs.len) - - var/wormhole_max_duration = round((5 MINUTES) * wormhole_duration_modifier) - var/wormhole_min_duration = round((30 SECONDS) * wormhole_duration_modifier) - - //All ready. Announce that bad juju is afoot. - command_announcement.Announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", new_sound = 'sound/AI/spanomalies.ogg') - - //prob(20) can be approximated to 1 wormhole every 5 turfs! - //admittedly less random but totally worth it >_< - var/event_duration = set_duration - var/number_of_selections = round(pick_turfs.len/(4 * (Z_choices.len + 1)))+1 //+1 to avoid division by zero! - var/sleep_duration = 0.2 SECONDS - var/end_time = world.time + event_duration //the time by which the event should have ended - - var/increment = max(1,round(number_of_selections/50)) -// to_world("DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]") - - var/index = 1 - for(var/I = 1 to number_of_selections) - - //we've run into overtime. End the event - if( end_time < world.time ) -// to_world("DEBUG: we've run into overtime. End the event") - return - if( !pick_turfs.len ) -// to_world("DEBUG: we've run out of turfs to pick. End the event") - return - - //loop it round - index += increment - index %= pick_turfs.len - index++ - - //get our enter and exit locations - var/turf/simulated/floor/enter = pick_turfs[index] - pick_turfs -= enter //remove it from pickable turfs list - if( !enter || !istype(enter) ) continue //sanity - - var/turf/simulated/floor/exit = pick(pick_turfs) -// pick_turfs -= exit - if( !exit || !istype(exit) ) continue //sanity - - create_wormhole(enter,exit,wormhole_min_duration,wormhole_max_duration) - - sleep(sleep_duration) //have a well deserved nap! - - -//maybe this proc can even be used as an admin tool for teleporting players without ruining immulsions? -/proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf, var/min_duration = 30 SECONDS, var/max_duration = 60 SECONDS) - set waitfor = FALSE - var/obj/effect/portal/P = new /obj/effect/portal( enter ) - P.target = exit - P.creator = null - P.icon = 'icons/obj/objects.dmi' - P.failchance = 0 - P.icon_state = "anom" - P.name = "wormhole" - spawn(rand(min_duration,max_duration)) - qdel(P) +/proc/wormhole_event(var/set_duration = 5 MINUTES, var/wormhole_duration_modifier = 1) + spawn() + var/list/pick_turfs = list() + var/list/Z_choices = list() + Z_choices |= using_map.get_map_levels(1, FALSE) + for(var/turf/simulated/floor/T in world) + if(T.z in Z_choices) + if(!T.block_tele) + pick_turfs += T + + if(pick_turfs.len) + + var/wormhole_max_duration = round((5 MINUTES) * wormhole_duration_modifier) + var/wormhole_min_duration = round((30 SECONDS) * wormhole_duration_modifier) + + //All ready. Announce that bad juju is afoot. + command_announcement.Announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", new_sound = 'sound/AI/spanomalies.ogg') + + //prob(20) can be approximated to 1 wormhole every 5 turfs! + //admittedly less random but totally worth it >_< + var/event_duration = set_duration + var/number_of_selections = round(pick_turfs.len/(4 * (Z_choices.len + 1)))+1 //+1 to avoid division by zero! + var/sleep_duration = 0.2 SECONDS + var/end_time = world.time + event_duration //the time by which the event should have ended + + var/increment = max(1,round(number_of_selections/50)) +// to_world("DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]") + + var/index = 1 + for(var/I = 1 to number_of_selections) + + //we've run into overtime. End the event + if( end_time < world.time ) +// to_world("DEBUG: we've run into overtime. End the event") + return + if( !pick_turfs.len ) +// to_world("DEBUG: we've run out of turfs to pick. End the event") + return + + //loop it round + index += increment + index %= pick_turfs.len + index++ + + //get our enter and exit locations + var/turf/simulated/floor/enter = pick_turfs[index] + pick_turfs -= enter //remove it from pickable turfs list + if( !enter || !istype(enter) ) continue //sanity + + var/turf/simulated/floor/exit = pick(pick_turfs) +// pick_turfs -= exit + if( !exit || !istype(exit) ) continue //sanity + + create_wormhole(enter,exit,wormhole_min_duration,wormhole_max_duration) + + sleep(sleep_duration) //have a well deserved nap! + + +//maybe this proc can even be used as an admin tool for teleporting players without ruining immulsions? +/proc/create_wormhole(var/turf/enter as turf, var/turf/exit as turf, var/min_duration = 30 SECONDS, var/max_duration = 60 SECONDS) + set waitfor = FALSE + var/obj/effect/portal/P = new /obj/effect/portal( enter ) + P.target = exit + P.creator = null + P.icon = 'icons/obj/objects.dmi' + P.failchance = 0 + P.icon_state = "anom" + P.name = "wormhole" + spawn(rand(min_duration,max_duration)) + qdel(P) diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm index 0b37fa892fb..b9cdf222156 100644 --- a/code/game/gamemodes/extended/extended.dm +++ b/code/game/gamemodes/extended/extended.dm @@ -1,6 +1,6 @@ -/datum/game_mode/extended - name = "Extended" - config_tag = "extended" - required_players = 0 - round_description = "Just have fun and role-play!" +/datum/game_mode/extended + name = "Extended" + config_tag = "extended" + required_players = 0 + round_description = "Just have fun and role-play!" extended_round_description = "There are no antagonists during extended, unless an admin decides to be cheeky. Just play your character, mess around with your job, and have fun." \ No newline at end of file diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 7af06d8d33d..b8007d3ea5f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,561 +1,561 @@ -var/global/antag_add_failed // Used in antag type voting. -var/global/list/additional_antag_types = list() - -/datum/game_mode - var/name = "invalid" - var/round_description = "How did you even vote this in?" - var/extended_round_description = "This roundtype should not be spawned, let alone votable. Someone contact a developer and tell them the game's broken again." - var/config_tag = null - var/votable = 1 - var/probability = 0 - - var/required_players = 0 // Minimum players for round to start if voted in. - var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret - var/required_enemies = 0 // Minimum antagonists for round to start. - var/newscaster_announcements = null - var/end_on_antag_death = 0 // Round will end when all antagonists are dead. - var/ert_disabled = 0 // ERT cannot be called. - var/deny_respawn = 0 // Disable respawn during this round. - - var/list/disabled_jobs = list() // Mostly used for Malf. This check is performed in job_controller so it doesn't spawn a regular AI. - - var/shuttle_delay = 1 // Shuttle transit time is multiplied by this. - var/auto_recall_shuttle = 0 // Will the shuttle automatically be recalled? - - var/list/antag_tags = list() // Core antag templates to spawn. - var/list/antag_templates // Extra antagonist types to include. - var/list/latejoin_templates = list() - var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists? - var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count. - var/require_all_templates = 0 // Will only start if all templates are checked and can spawn. - - var/station_was_nuked = 0 // See nuclearbomb.dm and malfunction.dm. - var/explosion_in_progress = 0 // Sit back and relax - var/waittime_l = 600 // Lower bound on time before intercept arrives (in tenths of seconds) - var/waittime_h = 1800 // Upper bound on time before intercept arrives (in tenths of seconds) - - var/event_delay_mod_moderate // Modifies the timing of random events. - var/event_delay_mod_major // As above. - -/datum/game_mode/New() - ..() - -/datum/game_mode/Topic(href, href_list[]) - if(..()) - return - if(href_list["toggle"]) - switch(href_list["toggle"]) - if("respawn") - deny_respawn = !deny_respawn - if("ert") - ert_disabled = !ert_disabled - announce_ert_disabled() - if("shuttle_recall") - auto_recall_shuttle = !auto_recall_shuttle - if("autotraitor") - round_autoantag = !round_autoantag - message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.") - else if(href_list["set"]) - var/choice = "" - switch(href_list["set"]) - if("shuttle_delay") - choice = tgui_input_number(usr, "Enter a new shuttle delay multiplier", null, null, 20, 1) - if(!choice || choice < 1 || choice > 20) - return - shuttle_delay = choice - if("antag_scaling") - choice = tgui_input_number(usr, "Enter a new antagonist cap scaling coefficient.", null, null, 100, 0) - if(isnull(choice) || choice < 0 || choice > 100) - return - antag_scaling_coeff = choice - if("event_modifier_moderate") - choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) - if(isnull(choice) || choice < 0 || choice > 100) - return - event_delay_mod_moderate = choice - refresh_event_modifiers() - if("event_modifier_severe") - choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) - if(isnull(choice) || choice < 0 || choice > 100) - return - event_delay_mod_major = choice - refresh_event_modifiers() - message_admins("Admin [key_name_admin(usr)] set game mode option '[href_list["set"]]' to [choice].") - else if(href_list["debug_antag"]) - if(href_list["debug_antag"] == "self") - usr.client.debug_variables(src) - return - var/datum/antagonist/antag = all_antag_types[href_list["debug_antag"]] - if(antag) - usr.client.debug_variables(antag) - message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") - else if(href_list["remove_antag_type"]) - if(antag_tags && (href_list["remove_antag_type"] in antag_tags)) - to_chat(usr, "Cannot remove core mode antag type.") - return - var/datum/antagonist/antag = all_antag_types[href_list["remove_antag_type"]] - if(antag_templates && antag_templates.len && antag && (antag in antag_templates) && (antag.id in additional_antag_types)) - antag_templates -= antag - additional_antag_types -= antag.id - message_admins("Admin [key_name_admin(usr)] removed [antag.role_text] template from game mode.") - else if(href_list["add_antag_type"]) - var/choice = tgui_input_list(usr, "Which type do you wish to add?", "Select Antag Type", all_antag_types) - if(!choice) - return - var/datum/antagonist/antag = all_antag_types[choice] - if(antag) - if(!islist(ticker.mode.antag_templates)) - ticker.mode.antag_templates = list() - ticker.mode.antag_templates |= antag - message_admins("Admin [key_name_admin(usr)] added [antag.role_text] template to game mode.") - - // I am very sure there's a better way to do this, but I'm not sure what it might be. ~Z - spawn(1) - for(var/datum/admins/admin in world) - if(usr.client == admin.owner) - admin.show_game_mode(usr) - return - -/datum/game_mode/proc/announce() //to be called when round starts - to_world("The current game mode is [capitalize(name)]!") - if(round_description) - to_world("[round_description]") - if(round_autoantag) - to_world("Antagonists will be added to the round automagically as needed.") - if(antag_templates && antag_templates.len) - var/antag_summary = "Possible antagonist types: " - var/i = 1 - for(var/datum/antagonist/antag in antag_templates) - if(i > 1) - if(i == antag_templates.len) - antag_summary += " and " - else - antag_summary += ", " - antag_summary += "[antag.role_text_plural]" - i++ - antag_summary += "." - if(antag_templates.len > 1 && master_mode != "secret") - to_world("[antag_summary]") - else - message_admins("[antag_summary]") - -///can_start() -///Checks to see if the game can be setup and ran with the current number of players or whatnot. -/datum/game_mode/proc/can_start(var/do_not_spawn) - var/playerC = 0 - for(var/mob/new_player/player in player_list) - if((player.client)&&(player.ready)) - playerC++ - - if(master_mode=="secret") - if(playerC < config.player_requirements_secret[config_tag]) - return 0 - else - if(playerC < config.player_requirements[config_tag]) - return 0 - - if(!(antag_templates && antag_templates.len)) - return 1 - - var/enemy_count = 0 - if(antag_tags && antag_tags.len) - for(var/antag_tag in antag_tags) - var/datum/antagonist/antag = all_antag_types[antag_tag] - if(!antag) - continue - var/list/potential = list() - if(antag.flags & ANTAG_OVERRIDE_JOB) - potential = antag.pending_antagonists - else - potential = antag.candidates - if(islist(potential)) - if(require_all_templates && potential.len < antag.initial_spawn_req) - return 0 - enemy_count += potential.len - if(enemy_count >= required_enemies) - return 1 - return 0 - -/datum/game_mode/proc/refresh_event_modifiers() - if(event_delay_mod_moderate || event_delay_mod_major) - SSevents.report_at_round_end = TRUE - if(event_delay_mod_moderate) - var/datum/event_container/EModerate = SSevents.event_containers[EVENT_LEVEL_MODERATE] - EModerate.delay_modifier = event_delay_mod_moderate - if(event_delay_mod_moderate) - var/datum/event_container/EMajor = SSevents.event_containers[EVENT_LEVEL_MAJOR] - EMajor.delay_modifier = event_delay_mod_major - -/datum/game_mode/proc/pre_setup() - for(var/datum/antagonist/antag in antag_templates) - antag.build_candidate_list() //compile a list of all eligible candidates - - //antag roles that replace jobs need to be assigned before the job controller hands out jobs. - if(antag.flags & ANTAG_OVERRIDE_JOB) - antag.attempt_spawn() //select antags to be spawned - -///post_setup() -/datum/game_mode/proc/post_setup() - - refresh_event_modifiers() - - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_roundstart_logout_report() - - spawn (rand(waittime_l, waittime_h)) - spawn(rand(100,150)) - announce_ert_disabled() - - //Assign all antag types for this game mode. Any players spawned as antags earlier should have been removed from the pending list, so no need to worry about those. - for(var/datum/antagonist/antag in antag_templates) - if(!(antag.flags & ANTAG_OVERRIDE_JOB)) - antag.attempt_spawn() //select antags to be spawned - antag.finalize_spawn() //actually spawn antags - if(antag.is_latejoin_template()) - latejoin_templates |= antag - - if(emergency_shuttle && auto_recall_shuttle) - emergency_shuttle.auto_recall = 1 - - feedback_set_details("round_start","[time2text(world.realtime)]") - if(ticker && ticker.mode) - feedback_set_details("game_mode","[ticker.mode]") - feedback_set_details("server_ip","[world.internet_address]:[world.port]") - return 1 - -/datum/game_mode/proc/fail_setup() - for(var/datum/antagonist/antag in antag_templates) - antag.reset() - -/datum/game_mode/proc/announce_ert_disabled() - if(!ert_disabled) - return - - var/list/reasons = list( - "political instability", - "quantum fluctuations", - "hostile raiders", - "derelict station debris", - "REDACTED", - "ancient alien artillery", - "solar magnetic storms", - "sentient time-travelling killbots", - "gravitational anomalies", - "wormholes to another dimension", - "a telescience mishap", - "radiation flares", - "supermatter dust", - "leaks into a negative reality", - "antiparticle clouds", - "residual bluespace energy", - "suspected criminal operatives", - "malfunctioning von Neumann probe swarms", - "shadowy interlopers", - "a stranded alien arkship", - "haywire IPC constructs", - "rogue Unathi exiles", - "artifacts of eldritch horror", - "a brain slug infestation", - "killer bugs that lay eggs in the husks of the living", - "a deserted transport carrying alien specimens", - "an emissary for the gestalt requesting a security detail", - "a Tajaran slave rebellion", - "radical Skrellian transevolutionaries", - "classified security operations" - ) - command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") - -/datum/game_mode/proc/check_finished() - if(emergency_shuttle.returned() || station_was_nuked) - return 1 - if(end_on_antag_death && antag_templates && antag_templates.len) - for(var/datum/antagonist/antag in antag_templates) - if(!antag.antags_are_dead()) - return 0 - if(config.continous_rounds) - emergency_shuttle.auto_recall = 0 - return 0 - return 1 - return 0 - -/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. - return - -/datum/game_mode/proc/declare_completion() - - var/is_antag_mode = (antag_templates && antag_templates.len) - check_victory() - if(is_antag_mode) - sleep(10) - for(var/datum/antagonist/antag in antag_templates) - sleep(10) - antag.check_victory() - antag.print_player_summary() - sleep(10) - - var/clients = 0 - var/surviving_humans = 0 - var/surviving_total = 0 - var/ghosts = 0 - var/escaped_humans = 0 - var/escaped_total = 0 - var/escaped_on_pod_1 = 0 - var/escaped_on_pod_2 = 0 - var/escaped_on_pod_3 = 0 - var/escaped_on_pod_5 = 0 - var/escaped_on_shuttle = 0 - - var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) //VOREStation Edit - - for(var/mob/M in player_list) - if(M.client) - clients++ - var/M_area_type = (get_turf(M))?.loc?.type - if(ishuman(M)) - if(M.stat != DEAD) - surviving_humans++ - if(M_area_type in escape_locations) - escaped_humans++ - if(M.stat != DEAD) - surviving_total++ - if(M_area_type in escape_locations) - escaped_total++ - - if(M_area_type == /area/shuttle/escape/centcom) - escaped_on_shuttle++ - - if(M_area_type == /area/shuttle/escape_pod1/centcom) - escaped_on_pod_1++ - if(M_area_type == /area/shuttle/escape_pod2/centcom) - escaped_on_pod_2++ - if(M_area_type == /area/shuttle/escape_pod3/centcom) - escaped_on_pod_3++ - if(M_area_type == /area/shuttle/escape_pod5/centcom) - escaped_on_pod_5++ - - if(isobserver(M)) - ghosts++ - - var/text = "" - if(surviving_total > 0) - text += "
    There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]" - text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
    " - else - text += "There were no survivors ([ghosts] ghosts)." - to_world(text) - - if(clients > 0) - feedback_set("round_end_clients",clients) - if(ghosts > 0) - feedback_set("round_end_ghosts",ghosts) - if(surviving_humans > 0) - feedback_set("survived_human",surviving_humans) - if(surviving_total > 0) - feedback_set("survived_total",surviving_total) - if(escaped_humans > 0) - feedback_set("escaped_human",escaped_humans) - if(escaped_total > 0) - feedback_set("escaped_total",escaped_total) - if(escaped_on_shuttle > 0) - feedback_set("escaped_on_shuttle",escaped_on_shuttle) - if(escaped_on_pod_1 > 0) - feedback_set("escaped_on_pod_1",escaped_on_pod_1) - if(escaped_on_pod_2 > 0) - feedback_set("escaped_on_pod_2",escaped_on_pod_2) - if(escaped_on_pod_3 > 0) - feedback_set("escaped_on_pod_3",escaped_on_pod_3) - if(escaped_on_pod_5 > 0) - feedback_set("escaped_on_pod_5",escaped_on_pod_5) - - send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") - SSwebhooks.send( - WEBHOOK_ROUNDEND, - list( - "survivors" = surviving_total, - "escaped" = escaped_total, - "ghosts" = ghosts, - "clients" = clients - ) - ) - - return 0 - -/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. - return 0 - -/datum/game_mode/proc/get_players_for_role(var/role, var/antag_id, var/ghosts_only) - var/list/players = list() - var/list/candidates = list() - - var/datum/antagonist/antag_template = all_antag_types[antag_id] - if(!antag_template) - return candidates - - // If this is being called post-roundstart then it doesn't care about ready status. - if(ticker && ticker.current_state == GAME_STATE_PLAYING) - for(var/mob/player in player_list) - if(!player.client) - continue - if(istype(player, /mob/new_player)) - continue - if(istype(player, /mob/observer/dead) && !ghosts_only) - continue - if(!role || (player.client.prefs.be_special & role)) - log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") - candidates |= player.mind - else - // Assemble a list of active players without jobbans. - for(var/mob/new_player/player in player_list) - if( player.client && player.ready ) - players += player - - // Get a list of all the people who want to be the antagonist for this round - for(var/mob/new_player/player in players) - if(!role || (player.client.prefs.be_special & role)) - log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") - candidates += player.mind - players -= player - - // Below is commented out as an attempt to solve an issue of too little people wanting to join the round due to wanting to have cake and eat it too. - /* - // If we don't have enough antags, draft people who voted for the round. - if(candidates.len < required_enemies) - for(var/mob/new_player/player in players) - if(player.ckey in round_voters) - log_debug("[player.key] voted for this round, so we are drafting them.") - candidates += player.mind - players -= player - break - */ - - return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than required_enemies - // required_enemies if the number of people with that role set to yes is less than recomended_enemies, - // Less if there are not enough valid players in the game entirely to make required_enemies. - -/datum/game_mode/proc/num_players() - . = 0 - for(var/mob/new_player/P in player_list) - if(P.client && P.ready) - . ++ - -/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) - return 0 - -/datum/game_mode/proc/create_antagonists() - - if(!config.traitor_scaling) - antag_scaling_coeff = 0 - - if(antag_tags && antag_tags.len) - antag_templates = list() - for(var/antag_tag in antag_tags) - var/datum/antagonist/antag = all_antag_types[antag_tag] - if(antag) - antag_templates |= antag - - if(additional_antag_types && additional_antag_types.len) - if(!antag_templates) - antag_templates = list() - for(var/antag_type in additional_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag) - antag_templates |= antag - - newscaster_announcements = pick(newscaster_standard_feeds) - -/datum/game_mode/proc/check_victory() - return - -////////////////////////// -//Reports player logouts// -////////////////////////// -/proc/display_roundstart_logout_report() - var/msg = "Roundstart logout report\n\n" - for(var/mob/living/L in mob_list) - - if(L.ckey) - var/found = 0 - for(var/client/C in GLOB.clients) - if(C.ckey == L.ckey) - found = 1 - break - if(!found) - msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" - - if(L.ckey && L.client) - if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) - msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" - continue //AFK client - if(L.stat) - if(L.suiciding) //Suicider - msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" - continue //Disconnected client - if(L.stat == UNCONSCIOUS) - msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" - continue //Unconscious - if(L.stat == DEAD) - msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" - continue //Dead - - continue //Happy connected client - for(var/mob/observer/dead/D in mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) - if(L.stat == DEAD) - if(L.suiciding) //Suicider - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" - continue //Disconnected client - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" - continue //Dead mob, ghost abandoned - else - if(D.can_reenter_corpse) - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Adminghosted)\n" - continue //Lolwhat - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" - continue //Ghosted while alive - - msg += "" // close the span from right at the top - - for(var/mob/M in mob_list) - if(M.client && M.client.holder) - to_chat(M,msg) - -/proc/get_nt_opposed() - var/list/dudes = list() - for(var/mob/living/carbon/human/man in player_list) - if(man.client) - if(man.client.prefs.economic_status == CLASS_LOWER) - dudes += man - else if(man.client.prefs.economic_status == CLASS_LOWMID && prob(50)) - dudes += man - if(dudes.len == 0) return null - return pick(dudes) - -/proc/show_objectives(var/datum/mind/player) - - if(!player || !player.current) return - - var/obj_count = 1 - to_chat(player.current, "Your current objectives:") - for(var/datum/objective/objective in player.objectives) - to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - -/mob/verb/check_round_info() - set name = "Check Round Info" - set category = "OOC" - - if(!ticker || !ticker.mode) - to_chat(usr, "Something is terribly wrong; there is no gametype.") - return - - if(master_mode != "secret") - to_chat(usr, "The roundtype is [capitalize(ticker.mode.name)]") - if(ticker.mode.round_description) - to_chat(usr, "[ticker.mode.round_description]") - if(ticker.mode.extended_round_description) - to_chat(usr, "[ticker.mode.extended_round_description]") - else - to_chat(usr, "Shhhh. It's a secret.") - return +var/global/antag_add_failed // Used in antag type voting. +var/global/list/additional_antag_types = list() + +/datum/game_mode + var/name = "invalid" + var/round_description = "How did you even vote this in?" + var/extended_round_description = "This roundtype should not be spawned, let alone votable. Someone contact a developer and tell them the game's broken again." + var/config_tag = null + var/votable = 1 + var/probability = 0 + + var/required_players = 0 // Minimum players for round to start if voted in. + var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret + var/required_enemies = 0 // Minimum antagonists for round to start. + var/newscaster_announcements = null + var/end_on_antag_death = 0 // Round will end when all antagonists are dead. + var/ert_disabled = 0 // ERT cannot be called. + var/deny_respawn = 0 // Disable respawn during this round. + + var/list/disabled_jobs = list() // Mostly used for Malf. This check is performed in job_controller so it doesn't spawn a regular AI. + + var/shuttle_delay = 1 // Shuttle transit time is multiplied by this. + var/auto_recall_shuttle = 0 // Will the shuttle automatically be recalled? + + var/list/antag_tags = list() // Core antag templates to spawn. + var/list/antag_templates // Extra antagonist types to include. + var/list/latejoin_templates = list() + var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists? + var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count. + var/require_all_templates = 0 // Will only start if all templates are checked and can spawn. + + var/station_was_nuked = 0 // See nuclearbomb.dm and malfunction.dm. + var/explosion_in_progress = 0 // Sit back and relax + var/waittime_l = 600 // Lower bound on time before intercept arrives (in tenths of seconds) + var/waittime_h = 1800 // Upper bound on time before intercept arrives (in tenths of seconds) + + var/event_delay_mod_moderate // Modifies the timing of random events. + var/event_delay_mod_major // As above. + +/datum/game_mode/New() + ..() + +/datum/game_mode/Topic(href, href_list[]) + if(..()) + return + if(href_list["toggle"]) + switch(href_list["toggle"]) + if("respawn") + deny_respawn = !deny_respawn + if("ert") + ert_disabled = !ert_disabled + announce_ert_disabled() + if("shuttle_recall") + auto_recall_shuttle = !auto_recall_shuttle + if("autotraitor") + round_autoantag = !round_autoantag + message_admins("Admin [key_name_admin(usr)] toggled game mode option '[href_list["toggle"]]'.") + else if(href_list["set"]) + var/choice = "" + switch(href_list["set"]) + if("shuttle_delay") + choice = tgui_input_number(usr, "Enter a new shuttle delay multiplier", null, null, 20, 1) + if(!choice || choice < 1 || choice > 20) + return + shuttle_delay = choice + if("antag_scaling") + choice = tgui_input_number(usr, "Enter a new antagonist cap scaling coefficient.", null, null, 100, 0) + if(isnull(choice) || choice < 0 || choice > 100) + return + antag_scaling_coeff = choice + if("event_modifier_moderate") + choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) + if(isnull(choice) || choice < 0 || choice > 100) + return + event_delay_mod_moderate = choice + refresh_event_modifiers() + if("event_modifier_severe") + choice = tgui_input_number(usr, "Enter a new moderate event time modifier.", null, null, 100, 0) + if(isnull(choice) || choice < 0 || choice > 100) + return + event_delay_mod_major = choice + refresh_event_modifiers() + message_admins("Admin [key_name_admin(usr)] set game mode option '[href_list["set"]]' to [choice].") + else if(href_list["debug_antag"]) + if(href_list["debug_antag"] == "self") + usr.client.debug_variables(src) + return + var/datum/antagonist/antag = all_antag_types[href_list["debug_antag"]] + if(antag) + usr.client.debug_variables(antag) + message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") + else if(href_list["remove_antag_type"]) + if(antag_tags && (href_list["remove_antag_type"] in antag_tags)) + to_chat(usr, "Cannot remove core mode antag type.") + return + var/datum/antagonist/antag = all_antag_types[href_list["remove_antag_type"]] + if(antag_templates && antag_templates.len && antag && (antag in antag_templates) && (antag.id in additional_antag_types)) + antag_templates -= antag + additional_antag_types -= antag.id + message_admins("Admin [key_name_admin(usr)] removed [antag.role_text] template from game mode.") + else if(href_list["add_antag_type"]) + var/choice = tgui_input_list(usr, "Which type do you wish to add?", "Select Antag Type", all_antag_types) + if(!choice) + return + var/datum/antagonist/antag = all_antag_types[choice] + if(antag) + if(!islist(ticker.mode.antag_templates)) + ticker.mode.antag_templates = list() + ticker.mode.antag_templates |= antag + message_admins("Admin [key_name_admin(usr)] added [antag.role_text] template to game mode.") + + // I am very sure there's a better way to do this, but I'm not sure what it might be. ~Z + spawn(1) + for(var/datum/admins/admin in world) + if(usr.client == admin.owner) + admin.show_game_mode(usr) + return + +/datum/game_mode/proc/announce() //to be called when round starts + to_world("The current game mode is [capitalize(name)]!") + if(round_description) + to_world("[round_description]") + if(round_autoantag) + to_world("Antagonists will be added to the round automagically as needed.") + if(antag_templates && antag_templates.len) + var/antag_summary = "Possible antagonist types: " + var/i = 1 + for(var/datum/antagonist/antag in antag_templates) + if(i > 1) + if(i == antag_templates.len) + antag_summary += " and " + else + antag_summary += ", " + antag_summary += "[antag.role_text_plural]" + i++ + antag_summary += "." + if(antag_templates.len > 1 && master_mode != "secret") + to_world("[antag_summary]") + else + message_admins("[antag_summary]") + +///can_start() +///Checks to see if the game can be setup and ran with the current number of players or whatnot. +/datum/game_mode/proc/can_start(var/do_not_spawn) + var/playerC = 0 + for(var/mob/new_player/player in player_list) + if((player.client)&&(player.ready)) + playerC++ + + if(master_mode=="secret") + if(playerC < config.player_requirements_secret[config_tag]) + return 0 + else + if(playerC < config.player_requirements[config_tag]) + return 0 + + if(!(antag_templates && antag_templates.len)) + return 1 + + var/enemy_count = 0 + if(antag_tags && antag_tags.len) + for(var/antag_tag in antag_tags) + var/datum/antagonist/antag = all_antag_types[antag_tag] + if(!antag) + continue + var/list/potential = list() + if(antag.flags & ANTAG_OVERRIDE_JOB) + potential = antag.pending_antagonists + else + potential = antag.candidates + if(islist(potential)) + if(require_all_templates && potential.len < antag.initial_spawn_req) + return 0 + enemy_count += potential.len + if(enemy_count >= required_enemies) + return 1 + return 0 + +/datum/game_mode/proc/refresh_event_modifiers() + if(event_delay_mod_moderate || event_delay_mod_major) + SSevents.report_at_round_end = TRUE + if(event_delay_mod_moderate) + var/datum/event_container/EModerate = SSevents.event_containers[EVENT_LEVEL_MODERATE] + EModerate.delay_modifier = event_delay_mod_moderate + if(event_delay_mod_moderate) + var/datum/event_container/EMajor = SSevents.event_containers[EVENT_LEVEL_MAJOR] + EMajor.delay_modifier = event_delay_mod_major + +/datum/game_mode/proc/pre_setup() + for(var/datum/antagonist/antag in antag_templates) + antag.build_candidate_list() //compile a list of all eligible candidates + + //antag roles that replace jobs need to be assigned before the job controller hands out jobs. + if(antag.flags & ANTAG_OVERRIDE_JOB) + antag.attempt_spawn() //select antags to be spawned + +///post_setup() +/datum/game_mode/proc/post_setup() + + refresh_event_modifiers() + + spawn (ROUNDSTART_LOGOUT_REPORT_TIME) + display_roundstart_logout_report() + + spawn (rand(waittime_l, waittime_h)) + spawn(rand(100,150)) + announce_ert_disabled() + + //Assign all antag types for this game mode. Any players spawned as antags earlier should have been removed from the pending list, so no need to worry about those. + for(var/datum/antagonist/antag in antag_templates) + if(!(antag.flags & ANTAG_OVERRIDE_JOB)) + antag.attempt_spawn() //select antags to be spawned + antag.finalize_spawn() //actually spawn antags + if(antag.is_latejoin_template()) + latejoin_templates |= antag + + if(emergency_shuttle && auto_recall_shuttle) + emergency_shuttle.auto_recall = 1 + + feedback_set_details("round_start","[time2text(world.realtime)]") + if(ticker && ticker.mode) + feedback_set_details("game_mode","[ticker.mode]") + feedback_set_details("server_ip","[world.internet_address]:[world.port]") + return 1 + +/datum/game_mode/proc/fail_setup() + for(var/datum/antagonist/antag in antag_templates) + antag.reset() + +/datum/game_mode/proc/announce_ert_disabled() + if(!ert_disabled) + return + + var/list/reasons = list( + "political instability", + "quantum fluctuations", + "hostile raiders", + "derelict station debris", + "REDACTED", + "ancient alien artillery", + "solar magnetic storms", + "sentient time-travelling killbots", + "gravitational anomalies", + "wormholes to another dimension", + "a telescience mishap", + "radiation flares", + "supermatter dust", + "leaks into a negative reality", + "antiparticle clouds", + "residual bluespace energy", + "suspected criminal operatives", + "malfunctioning von Neumann probe swarms", + "shadowy interlopers", + "a stranded alien arkship", + "haywire IPC constructs", + "rogue Unathi exiles", + "artifacts of eldritch horror", + "a brain slug infestation", + "killer bugs that lay eggs in the husks of the living", + "a deserted transport carrying alien specimens", + "an emissary for the gestalt requesting a security detail", + "a Tajaran slave rebellion", + "radical Skrellian transevolutionaries", + "classified security operations" + ) + command_announcement.Announce("The presence of [pick(reasons)] in the region is tying up all available local emergency resources; emergency response teams cannot be called at this time, and post-evacuation recovery efforts will be substantially delayed.","Emergency Transmission") + +/datum/game_mode/proc/check_finished() + if(emergency_shuttle.returned() || station_was_nuked) + return 1 + if(end_on_antag_death && antag_templates && antag_templates.len) + for(var/datum/antagonist/antag in antag_templates) + if(!antag.antags_are_dead()) + return 0 + if(config.continous_rounds) + emergency_shuttle.auto_recall = 0 + return 0 + return 1 + return 0 + +/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. + return + +/datum/game_mode/proc/declare_completion() + + var/is_antag_mode = (antag_templates && antag_templates.len) + check_victory() + if(is_antag_mode) + sleep(10) + for(var/datum/antagonist/antag in antag_templates) + sleep(10) + antag.check_victory() + antag.print_player_summary() + sleep(10) + + var/clients = 0 + var/surviving_humans = 0 + var/surviving_total = 0 + var/ghosts = 0 + var/escaped_humans = 0 + var/escaped_total = 0 + var/escaped_on_pod_1 = 0 + var/escaped_on_pod_2 = 0 + var/escaped_on_pod_3 = 0 + var/escaped_on_pod_5 = 0 + var/escaped_on_shuttle = 0 + + var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) //VOREStation Edit + + for(var/mob/M in player_list) + if(M.client) + clients++ + var/M_area_type = (get_turf(M))?.loc?.type + if(ishuman(M)) + if(M.stat != DEAD) + surviving_humans++ + if(M_area_type in escape_locations) + escaped_humans++ + if(M.stat != DEAD) + surviving_total++ + if(M_area_type in escape_locations) + escaped_total++ + + if(M_area_type == /area/shuttle/escape/centcom) + escaped_on_shuttle++ + + if(M_area_type == /area/shuttle/escape_pod1/centcom) + escaped_on_pod_1++ + if(M_area_type == /area/shuttle/escape_pod2/centcom) + escaped_on_pod_2++ + if(M_area_type == /area/shuttle/escape_pod3/centcom) + escaped_on_pod_3++ + if(M_area_type == /area/shuttle/escape_pod5/centcom) + escaped_on_pod_5++ + + if(isobserver(M)) + ghosts++ + + var/text = "" + if(surviving_total > 0) + text += "
    There [surviving_total>1 ? "were [surviving_total] survivors" : "was one survivor"]" + text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
    " + else + text += "There were no survivors ([ghosts] ghosts)." + to_world(text) + + if(clients > 0) + feedback_set("round_end_clients",clients) + if(ghosts > 0) + feedback_set("round_end_ghosts",ghosts) + if(surviving_humans > 0) + feedback_set("survived_human",surviving_humans) + if(surviving_total > 0) + feedback_set("survived_total",surviving_total) + if(escaped_humans > 0) + feedback_set("escaped_human",escaped_humans) + if(escaped_total > 0) + feedback_set("escaped_total",escaped_total) + if(escaped_on_shuttle > 0) + feedback_set("escaped_on_shuttle",escaped_on_shuttle) + if(escaped_on_pod_1 > 0) + feedback_set("escaped_on_pod_1",escaped_on_pod_1) + if(escaped_on_pod_2 > 0) + feedback_set("escaped_on_pod_2",escaped_on_pod_2) + if(escaped_on_pod_3 > 0) + feedback_set("escaped_on_pod_3",escaped_on_pod_3) + if(escaped_on_pod_5 > 0) + feedback_set("escaped_on_pod_5",escaped_on_pod_5) + + send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") + SSwebhooks.send( + WEBHOOK_ROUNDEND, + list( + "survivors" = surviving_total, + "escaped" = escaped_total, + "ghosts" = ghosts, + "clients" = clients + ) + ) + + return 0 + +/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. + return 0 + +/datum/game_mode/proc/get_players_for_role(var/role, var/antag_id, var/ghosts_only) + var/list/players = list() + var/list/candidates = list() + + var/datum/antagonist/antag_template = all_antag_types[antag_id] + if(!antag_template) + return candidates + + // If this is being called post-roundstart then it doesn't care about ready status. + if(ticker && ticker.current_state == GAME_STATE_PLAYING) + for(var/mob/player in player_list) + if(!player.client) + continue + if(istype(player, /mob/new_player)) + continue + if(istype(player, /mob/observer/dead) && !ghosts_only) + continue + if(!role || (player.client.prefs.be_special & role)) + log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") + candidates |= player.mind + else + // Assemble a list of active players without jobbans. + for(var/mob/new_player/player in player_list) + if( player.client && player.ready ) + players += player + + // Get a list of all the people who want to be the antagonist for this round + for(var/mob/new_player/player in players) + if(!role || (player.client.prefs.be_special & role)) + log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") + candidates += player.mind + players -= player + + // Below is commented out as an attempt to solve an issue of too little people wanting to join the round due to wanting to have cake and eat it too. + /* + // If we don't have enough antags, draft people who voted for the round. + if(candidates.len < required_enemies) + for(var/mob/new_player/player in players) + if(player.ckey in round_voters) + log_debug("[player.key] voted for this round, so we are drafting them.") + candidates += player.mind + players -= player + break + */ + + return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than required_enemies + // required_enemies if the number of people with that role set to yes is less than recomended_enemies, + // Less if there are not enough valid players in the game entirely to make required_enemies. + +/datum/game_mode/proc/num_players() + . = 0 + for(var/mob/new_player/P in player_list) + if(P.client && P.ready) + . ++ + +/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) + return 0 + +/datum/game_mode/proc/create_antagonists() + + if(!config.traitor_scaling) + antag_scaling_coeff = 0 + + if(antag_tags && antag_tags.len) + antag_templates = list() + for(var/antag_tag in antag_tags) + var/datum/antagonist/antag = all_antag_types[antag_tag] + if(antag) + antag_templates |= antag + + if(additional_antag_types && additional_antag_types.len) + if(!antag_templates) + antag_templates = list() + for(var/antag_type in additional_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag) + antag_templates |= antag + + newscaster_announcements = pick(newscaster_standard_feeds) + +/datum/game_mode/proc/check_victory() + return + +////////////////////////// +//Reports player logouts// +////////////////////////// +/proc/display_roundstart_logout_report() + var/msg = "Roundstart logout report\n\n" + for(var/mob/living/L in mob_list) + + if(L.ckey) + var/found = 0 + for(var/client/C in GLOB.clients) + if(C.ckey == L.ckey) + found = 1 + break + if(!found) + msg += "[L.name] ([L.ckey]), the [L.job] ([span_yellow("Disconnected")])\n" + + if(L.ckey && L.client) + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.ckey]), the [L.job] ([span_yellow("Connected, Inactive")])\n" + continue //AFK client + if(L.stat) + if(L.suiciding) //Suicider + msg += "[L.name] ([L.ckey]), the [L.job] ([span_red("Suicide")])\n" + continue //Disconnected client + if(L.stat == UNCONSCIOUS) + msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" + continue //Unconscious + if(L.stat == DEAD) + msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" + continue //Dead + + continue //Happy connected client + for(var/mob/observer/dead/D in mob_list) + if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(L.stat == DEAD) + if(L.suiciding) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Suicide")])\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Adminghosted")])\n" + continue //Lolwhat + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_red("Ghosted")])\n" + continue //Ghosted while alive + + msg += "" // close the span from right at the top + + for(var/mob/M in mob_list) + if(M.client && M.client.holder) + to_chat(M,msg) + +/proc/get_nt_opposed() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in player_list) + if(man.client) + if(man.client.prefs.economic_status == CLASS_LOWER) + dudes += man + else if(man.client.prefs.economic_status == CLASS_LOWMID && prob(50)) + dudes += man + if(dudes.len == 0) return null + return pick(dudes) + +/proc/show_objectives(var/datum/mind/player) + + if(!player || !player.current) return + + var/obj_count = 1 + to_chat(player.current, "Your current objectives:") + for(var/datum/objective/objective in player.objectives) + to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + +/mob/verb/check_round_info() + set name = "Check Round Info" + set category = "OOC" + + if(!ticker || !ticker.mode) + to_chat(usr, "Something is terribly wrong; there is no gametype.") + return + + if(master_mode != "secret") + to_chat(usr, "The roundtype is [capitalize(ticker.mode.name)]") + if(ticker.mode.round_description) + to_chat(usr, "[ticker.mode.round_description]") + if(ticker.mode.extended_round_description) + to_chat(usr, "[ticker.mode.extended_round_description]") + else + to_chat(usr, "Shhhh. It's a secret.") + return diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index 3a0ccec1241..26e0c4572e1 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -1,13 +1,13 @@ -/datum/game_mode/malfunction - name = "AI Malfunction" - round_description = "The AI is behaving abnormally and must be stopped." - extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible." - config_tag = "malfunction" - required_players = 2 - required_players_secret = 2 - required_enemies = 1 - end_on_antag_death = 0 - auto_recall_shuttle = 0 - antag_tags = list(MODE_MALFUNCTION) - disabled_jobs = list("AI") - votable = 0 +/datum/game_mode/malfunction + name = "AI Malfunction" + round_description = "The AI is behaving abnormally and must be stopped." + extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible." + config_tag = "malfunction" + required_players = 2 + required_players_secret = 2 + required_enemies = 1 + end_on_antag_death = 0 + auto_recall_shuttle = 0 + antag_tags = list(MODE_MALFUNCTION) + disabled_jobs = list("AI") + votable = 0 diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm index d504fcf6a74..191e447813a 100644 --- a/code/game/gamemodes/meme/meme.dm +++ b/code/game/gamemodes/meme/meme.dm @@ -1,145 +1,145 @@ -//This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04 - -/datum/game_mode/var/list/memes = list() - -/datum/game_mode/meme - name = "Memetic Anomaly" - config_tag = "meme" - required_players = 3 - required_players_secret = 10 - restricted_jobs = list("AI", "Cyborg") - recommended_enemies = 2 // need at least a meme and a host - votable = 0 // temporarily disable this mode for voting - end_on_antag_death = 1 - - var/var/list/datum/mind/first_hosts = list() - var/var/list/assigned_hosts = list() - - var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time - var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target - var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target - - var/const/prob_int_item = 50 // intercept names the theft target half the time - var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target - var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target - - var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time - var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target - var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target - - var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative - var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative - var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly - var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly - - var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) - var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) - -/datum/game_mode/meme/announce() - to_world("The current game mode is - Meme!") - to_world("An unknown creature has infested the mind of a crew member. Find and destroy it by any means necessary.") - -/datum/game_mode/meme/can_start() - if(!..()) - return 0 - - // for every 10 players, get 1 meme, and for each meme, get a host - // also make sure that there's at least one meme and one host - recommended_enemies = max(src.num_players() / 20 * 2, 2) - - var/list/datum/mind/possible_memes = get_players_for_role(BE_MEME) - - if(possible_memes.len < 2) - log_admin("MODE FAILURE: MEME. NOT ENOUGH MEME CANDIDATES.") - return 0 // not enough candidates for meme - - // for each 2 possible memes, add one meme and one host - while(possible_memes.len >= 2) - var/datum/mind/meme = pick(possible_memes) - possible_memes.Remove(meme) - - var/datum/mind/first_host = pick(possible_memes) - possible_memes.Remove(first_host) - memes += meme - first_hosts += first_host - - // so that we can later know which host belongs to which meme - assigned_hosts[meme.key] = first_host - - meme.assigned_role = "MODE" //So they aren't chosen for other jobs. - meme.special_role = "Meme" - - return 1 - -/datum/game_mode/meme/pre_setup() - return 1 - - -/datum/game_mode/meme/post_setup() - // create a meme and enter it - for(var/datum/mind/meme in memes) - var/mob/living/parasite/meme/M = new - var/mob/original = meme.current - meme.transfer_to(M) - M.clearHUD() - - // get the host for this meme - var/datum/mind/first_host = assigned_hosts[meme.key] - // this is a redundant check, but I don't think the above works.. - // if picking hosts works with this method, remove the method above - if(!first_host) - first_host = pick(first_hosts) - first_hosts.Remove(first_host) - M.enter_host(first_host.current) - forge_meme_objectives(meme, first_host) - - qdel(original) - - log_admin("Created [memes.len] memes.") - - spawn (rand(waittime_l, waittime_h)) - send_intercept() - ..() - return - - -/datum/game_mode/proc/forge_meme_objectives(var/datum/mind/meme, var/datum/mind/first_host) - if (config.objectives_disabled) - return - - // meme always needs to attune X hosts - var/datum/objective/meme_attune/attune_objective = new - attune_objective.owner = meme - attune_objective.gen_amount_goal(3,6) - meme.objectives += attune_objective - - // generate some random objectives, use standard traitor objectives - var/job = first_host.assigned_role - - for(var/datum/objective/o in SelectObjectives(job, meme)) - o.owner = meme - meme.objectives += o - - greet_meme(meme) - - return - -/datum/game_mode/proc/greet_meme(var/datum/mind/meme, var/you_are=1) - if (you_are) - to_chat(meme.current, "You are a meme!") - show_objectives(meme) - return - -/datum/game_mode/meme/check_finished() - var/memes_alive = 0 - for(var/datum/mind/meme in memes) - if(!istype(meme.current,/mob/living)) - continue - if(meme.current.stat==2) - continue - memes_alive++ - - if (memes_alive) - return ..() - else - return 1 +//This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04 + +/datum/game_mode/var/list/memes = list() + +/datum/game_mode/meme + name = "Memetic Anomaly" + config_tag = "meme" + required_players = 3 + required_players_secret = 10 + restricted_jobs = list("AI", "Cyborg") + recommended_enemies = 2 // need at least a meme and a host + votable = 0 // temporarily disable this mode for voting + end_on_antag_death = 1 + + var/var/list/datum/mind/first_hosts = list() + var/var/list/assigned_hosts = list() + + var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time + var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target + var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target + + var/const/prob_int_item = 50 // intercept names the theft target half the time + var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target + var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target + + var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time + var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target + var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target + + var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative + var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative + var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly + var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly + + var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) + var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) + +/datum/game_mode/meme/announce() + to_world("The current game mode is - Meme!") + to_world("An unknown creature has infested the mind of a crew member. Find and destroy it by any means necessary.") + +/datum/game_mode/meme/can_start() + if(!..()) + return 0 + + // for every 10 players, get 1 meme, and for each meme, get a host + // also make sure that there's at least one meme and one host + recommended_enemies = max(src.num_players() / 20 * 2, 2) + + var/list/datum/mind/possible_memes = get_players_for_role(BE_MEME) + + if(possible_memes.len < 2) + log_admin("MODE FAILURE: MEME. NOT ENOUGH MEME CANDIDATES.") + return 0 // not enough candidates for meme + + // for each 2 possible memes, add one meme and one host + while(possible_memes.len >= 2) + var/datum/mind/meme = pick(possible_memes) + possible_memes.Remove(meme) + + var/datum/mind/first_host = pick(possible_memes) + possible_memes.Remove(first_host) + memes += meme + first_hosts += first_host + + // so that we can later know which host belongs to which meme + assigned_hosts[meme.key] = first_host + + meme.assigned_role = "MODE" //So they aren't chosen for other jobs. + meme.special_role = "Meme" + + return 1 + +/datum/game_mode/meme/pre_setup() + return 1 + + +/datum/game_mode/meme/post_setup() + // create a meme and enter it + for(var/datum/mind/meme in memes) + var/mob/living/parasite/meme/M = new + var/mob/original = meme.current + meme.transfer_to(M) + M.clearHUD() + + // get the host for this meme + var/datum/mind/first_host = assigned_hosts[meme.key] + // this is a redundant check, but I don't think the above works.. + // if picking hosts works with this method, remove the method above + if(!first_host) + first_host = pick(first_hosts) + first_hosts.Remove(first_host) + M.enter_host(first_host.current) + forge_meme_objectives(meme, first_host) + + qdel(original) + + log_admin("Created [memes.len] memes.") + + spawn (rand(waittime_l, waittime_h)) + send_intercept() + ..() + return + + +/datum/game_mode/proc/forge_meme_objectives(var/datum/mind/meme, var/datum/mind/first_host) + if (config.objectives_disabled) + return + + // meme always needs to attune X hosts + var/datum/objective/meme_attune/attune_objective = new + attune_objective.owner = meme + attune_objective.gen_amount_goal(3,6) + meme.objectives += attune_objective + + // generate some random objectives, use standard traitor objectives + var/job = first_host.assigned_role + + for(var/datum/objective/o in SelectObjectives(job, meme)) + o.owner = meme + meme.objectives += o + + greet_meme(meme) + + return + +/datum/game_mode/proc/greet_meme(var/datum/mind/meme, var/you_are=1) + if (you_are) + to_chat(meme.current, "You are a meme!") + show_objectives(meme) + return + +/datum/game_mode/meme/check_finished() + var/memes_alive = 0 + for(var/datum/mind/meme in memes) + if(!istype(meme.current,/mob/living)) + continue + if(meme.current.stat==2) + continue + memes_alive++ + + if (memes_alive) + return ..() + else + return 1 diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index 3c9244bc84e..57d5dadeacf 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,49 +1,49 @@ -#define METEOR_DELAY 6000 - -/datum/game_mode/meteor - name = "Meteor" - round_description = "The space station has been stuck in a major meteor shower." - extended_round_description = "The station is on an unavoidable collision course with an asteroid field. The station will be continuously slammed with meteors, venting hallways, rooms, and ultimately destroying a majority of the basic life functions of the entire structure. Coordinate with your fellow crew members to survive the inevitable destruction of the station and get back home in one piece!" - config_tag = "meteor" - required_players = 0 - votable = 0 - deny_respawn = 0 - var/next_wave = METEOR_DELAY - -/datum/game_mode/meteor/post_setup() - defer_powernet_rebuild = 2//Might help with the lag - ..() - -/datum/game_mode/meteor/process() - if(world.time >= next_wave) - next_wave = world.time + meteor_wave_delay - spawn() spawn_meteors(6, meteors_normal) - -/datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 - for(var/mob/living/player in player_list) - if(player.stat != DEAD) - var/turf/location = get_turf(player.loc) - if(!location) continue - switch(location.loc.type) - if( /area/shuttle/escape/centcom ) - text += "
    [player.real_name] escaped on the emergency shuttle" - if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) - text += "
    [player.real_name] escaped in a life pod." - else - text += "
    [player.real_name] survived but is stranded without any hope of rescue." - survivors++ - - if(survivors) - to_world("The following survived the meteor storm:[text]") - else - to_world("Nobody survived the meteor storm!") - - feedback_set_details("round_end_result","end - evacuation") - feedback_set("round_end_result",survivors) - - ..() - return 1 - +#define METEOR_DELAY 6000 + +/datum/game_mode/meteor + name = "Meteor" + round_description = "The space station has been stuck in a major meteor shower." + extended_round_description = "The station is on an unavoidable collision course with an asteroid field. The station will be continuously slammed with meteors, venting hallways, rooms, and ultimately destroying a majority of the basic life functions of the entire structure. Coordinate with your fellow crew members to survive the inevitable destruction of the station and get back home in one piece!" + config_tag = "meteor" + required_players = 0 + votable = 0 + deny_respawn = 0 + var/next_wave = METEOR_DELAY + +/datum/game_mode/meteor/post_setup() + defer_powernet_rebuild = 2//Might help with the lag + ..() + +/datum/game_mode/meteor/process() + if(world.time >= next_wave) + next_wave = world.time + meteor_wave_delay + spawn() spawn_meteors(6, meteors_normal) + +/datum/game_mode/meteor/declare_completion() + var/text + var/survivors = 0 + for(var/mob/living/player in player_list) + if(player.stat != DEAD) + var/turf/location = get_turf(player.loc) + if(!location) continue + switch(location.loc.type) + if( /area/shuttle/escape/centcom ) + text += "
    [player.real_name] escaped on the emergency shuttle" + if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) + text += "
    [player.real_name] escaped in a life pod." + else + text += "
    [player.real_name] survived but is stranded without any hope of rescue." + survivors++ + + if(survivors) + to_world("The following survived the meteor storm:[text]") + else + to_world("Nobody survived the meteor storm!") + + feedback_set_details("round_end_result","end - evacuation") + feedback_set("round_end_result",survivors) + + ..() + return 1 + #undef METEOR_DELAY \ No newline at end of file diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 950910fe5fc..dc636e54dc7 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,345 +1,345 @@ -/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds -//set to at least 100 unless you want evarr ruining every round - -//Meteors probability of spawning during a given wave - -//for space dust event -/var/list/meteors_dust = list(/obj/effect/meteor/dust) - -//for normal meteor event -/var/list/meteors_normal = list( - /obj/effect/meteor/dust=3, - /obj/effect/meteor/medium=8, - /obj/effect/meteor/big=3, - /obj/effect/meteor/flaming=1, - /obj/effect/meteor/irradiated=3 - ) - -//for threatening meteor event -/var/list/meteors_threatening = list( - /obj/effect/meteor/medium=5, - /obj/effect/meteor/big=10, - /obj/effect/meteor/flaming=3, - /obj/effect/meteor/irradiated=3, - /obj/effect/meteor/emp=3) - -//for catastrophic meteor event -/var/list/meteors_catastrophic = list( - /obj/effect/meteor/medium=5, - /obj/effect/meteor/big=75, - /obj/effect/meteor/flaming=10, - /obj/effect/meteor/irradiated=10, - /obj/effect/meteor/emp=10) - - - -/////////////////////////////// -//Meteor spawning global procs -/////////////////////////////// - -/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) - log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]") - for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes, startSide, zlevel) - -/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel) - if(isnull(startSide)) - startSide = pick(cardinal) - if(isnull(startLevel)) - startLevel = pick(using_map.station_levels - using_map.sealed_levels) - - var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel) - var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel) - - var/Me = pickweight(meteortypes) - var/obj/effect/meteor/M = new Me(pickedstart) - M.dest = pickedgoal - spawn(0) - walk_towards(M, M.dest, 3) //VOREStation Edit - Slower Meteors - return - -/proc/spaceDebrisStartLoc(startSide, Z) - var/starty - var/startx - switch(startSide) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - var/turf/T = locate(startx, starty, Z) - return T - -/proc/spaceDebrisFinishLoc(startSide, Z) - var/endy - var/endx - switch(startSide) - if(NORTH) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - var/turf/T = locate(endx, endy, Z) - return T - -// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'. -/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor) - return TRUE - -/////////////////////// -//The meteor effect -////////////////////// - -/obj/effect/meteor - name = "the concept of meteor" - desc = "You should probably run instead of gawking at this." - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - density = TRUE - anchored = TRUE - var/hits = 4 - var/hitpwr = 2 //Level of ex_act to be called on hit. - var/dest - pass_flags = PASSTABLE - var/heavy = FALSE - var/z_original - - var/meteordrop = /obj/item/weapon/ore/iron - var/dropamt = 2 - - // How much damage it does to walls, using take_damage(). - // Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through. - // Multiply this and the hits var to get a rough idea of how penetrating a meteor is. - var/wall_power = 100 - -/obj/effect/meteor/Initialize() - . = ..() - z_original = z - GLOB.meteor_list += src - -/obj/effect/meteor/Move() - if(z != z_original || loc == dest) - qdel(src) - return - - . = ..() //process movement... - -/obj/effect/meteor/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - var/turf/T = get_turf(loc) - ram_turf(T) - - if(prob(10) && !istype(T, /turf/space)) //randomly takes a 'hit' from ramming - get_hit() - -/obj/effect/meteor/Destroy() - walk(src,FALSE) //this cancels the walk_towards() proc - GLOB.meteor_list -= src - return ..() - -/obj/effect/meteor/New() - ..() - SpinAnimation() - -/obj/effect/meteor/Bump(atom/A) - if(attempt_vr(src,"Bump_vr",list(A))) return //VOREStation Edit - allows meteors to be deflected by baseball bats - if(A) - if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield. - ram_turf(get_turf(A)) - get_hit() - else - die(FALSE) - -/obj/effect/meteor/CanPass(atom/movable/mover, turf/target) - return istype(mover, /obj/effect/meteor) ? TRUE : ..() - -/obj/effect/meteor/proc/ram_turf(var/turf/T) - //first bust whatever is in the turf - for(var/atom/A in T) - if(A == src) // Don't hit ourselves. - continue - if(isturf(A)) // Don't hit floors. We'll deal with walls later. - continue - A.ex_act(hitpwr) - - //then, ram the turf if it still exists - if(T) - if(istype(T, /turf/simulated/wall)) - var/turf/simulated/wall/W = T - W.take_damage(wall_power) // Stronger walls can halt asteroids. - -/obj/effect/meteor/proc/get_shield_damage() - return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) - -//process getting 'hit' by colliding with a dense object -//or randomly when ramming turfs -/obj/effect/meteor/proc/get_hit() - hits-- - if(hits <= 0) - die(TRUE) - -/obj/effect/meteor/proc/die(var/explode = TRUE) - make_debris() - meteor_effect(explode) - qdel(src) - -/obj/effect/meteor/ex_act() - return - -/obj/effect/meteor/attackby(obj/item/weapon/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/weapon/pickaxe)) - qdel(src) - return - ..() - -/obj/effect/meteor/bullet_act(var/obj/item/projectile/Proj) - if(Proj.excavation_amount) - get_hit() - - if(!QDELETED(src)) - wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors. - - if(wall_power <= 0) - die(FALSE) // If you kill the meteor, then it dies. - return - return - -/obj/effect/meteor/proc/make_debris() - for(var/throws = dropamt, throws > 0, throws--) - var/obj/item/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/shake_players() - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) - -/obj/effect/meteor/proc/meteor_effect(var/explode) - if(heavy) - shake_players() - - -/////////////////////// -//Meteor types -/////////////////////// - -// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot. -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - hitpwr = 3 - meteordrop = /obj/item/weapon/ore/glass - wall_power = 50 - -// Medium-sized meteors aren't very special and can be stopped easily by r-walls. -/obj/effect/meteor/medium - name = "meteor" - dropamt = 3 - wall_power = 200 - -/obj/effect/meteor/medium/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 0, 1, 2, 3, 0) - -// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter. -/obj/effect/meteor/big - name = "large meteor" - icon_state = "large" - hits = 8 - heavy = 1 - dropamt = 4 - wall_power = 400 - -/obj/effect/meteor/big/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 1, 2, 3, 4, 0) - -// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end. -/obj/effect/meteor/flaming - name = "flaming meteor" - icon_state = "flaming" - hits = 5 - heavy = 1 - meteordrop = /obj/item/weapon/ore/phoron - wall_power = 100 - -/obj/effect/meteor/flaming/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) - -// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding. -/obj/effect/meteor/irradiated - name = "glowing meteor" - icon_state = "glowing" - heavy = 1 - meteordrop = /obj/item/weapon/ore/uranium - wall_power = 75 - - -/obj/effect/meteor/irradiated/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 0, 0, 4, 3, 0) - new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - SSradiation.radiate(src, 50) - -// This meteor fries toasters. -/obj/effect/meteor/emp - name = "conducting meteor" - icon_state = "glowing_blue" - desc = "Hide your floppies!" - meteordrop = /obj/item/weapon/ore/osmium - dropamt = 3 - wall_power = 80 - -/obj/effect/meteor/emp/meteor_effect(var/explode) - ..() - // Best case scenario: Comparable to a low-yield EMP grenade. - // Worst case scenario: Comparable to a standard yield EMP grenade. - empulse(src, rand(1, 3), rand(2, 4), rand(3, 7), rand(5, 10)) - -/obj/effect/meteor/emp/get_shield_damage() - return ..() * rand(2,4) - -//Station buster Tunguska -/obj/effect/meteor/tunguska - name = "tunguska meteor" - icon_state = "flaming" - desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity" - hits = 30 - hitpwr = 1 - heavy = 1 - meteordrop = /obj/item/weapon/ore/phoron - wall_power = 150 - -/obj/effect/meteor/tunguska/meteor_effect(var/explode) - ..() - if(explode) - explosion(src.loc, 3, 6, 9, 20, 0) - -/obj/effect/meteor/tunguska/Bump() - ..() - if(prob(20)) - explosion(src.loc,2,4,6,8) +/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds +//set to at least 100 unless you want evarr ruining every round + +//Meteors probability of spawning during a given wave + +//for space dust event +/var/list/meteors_dust = list(/obj/effect/meteor/dust) + +//for normal meteor event +/var/list/meteors_normal = list( + /obj/effect/meteor/dust=3, + /obj/effect/meteor/medium=8, + /obj/effect/meteor/big=3, + /obj/effect/meteor/flaming=1, + /obj/effect/meteor/irradiated=3 + ) + +//for threatening meteor event +/var/list/meteors_threatening = list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=10, + /obj/effect/meteor/flaming=3, + /obj/effect/meteor/irradiated=3, + /obj/effect/meteor/emp=3) + +//for catastrophic meteor event +/var/list/meteors_catastrophic = list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=75, + /obj/effect/meteor/flaming=10, + /obj/effect/meteor/irradiated=10, + /obj/effect/meteor/emp=10) + + + +/////////////////////////////// +//Meteor spawning global procs +/////////////////////////////// + +/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) + log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]") + for(var/i = 0; i < number; i++) + spawn_meteor(meteortypes, startSide, zlevel) + +/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel) + if(isnull(startSide)) + startSide = pick(cardinal) + if(isnull(startLevel)) + startLevel = pick(using_map.station_levels - using_map.sealed_levels) + + var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel) + var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel) + + var/Me = pickweight(meteortypes) + var/obj/effect/meteor/M = new Me(pickedstart) + M.dest = pickedgoal + spawn(0) + walk_towards(M, M.dest, 3) //VOREStation Edit - Slower Meteors + return + +/proc/spaceDebrisStartLoc(startSide, Z) + var/starty + var/startx + switch(startSide) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + var/turf/T = locate(startx, starty, Z) + return T + +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(EAST) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + var/turf/T = locate(endx, endy, Z) + return T + +// Override for special behavior when getting hit by meteors, and only meteors. Return one if the meteor hasn't been 'stopped'. +/atom/proc/handle_meteor_impact(var/obj/effect/meteor/meteor) + return TRUE + +/////////////////////// +//The meteor effect +////////////////////// + +/obj/effect/meteor + name = "the concept of meteor" + desc = "You should probably run instead of gawking at this." + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + density = TRUE + anchored = TRUE + var/hits = 4 + var/hitpwr = 2 //Level of ex_act to be called on hit. + var/dest + pass_flags = PASSTABLE + var/heavy = FALSE + var/z_original + + var/meteordrop = /obj/item/weapon/ore/iron + var/dropamt = 2 + + // How much damage it does to walls, using take_damage(). + // Normal walls will die to 150 or more, where as reinforced walls need 800 to penetrate. Durasteel walls need 1200 damage to go through. + // Multiply this and the hits var to get a rough idea of how penetrating a meteor is. + var/wall_power = 100 + +/obj/effect/meteor/Initialize() + . = ..() + z_original = z + GLOB.meteor_list += src + +/obj/effect/meteor/Move() + if(z != z_original || loc == dest) + qdel(src) + return + + . = ..() //process movement... + +/obj/effect/meteor/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + var/turf/T = get_turf(loc) + ram_turf(T) + + if(prob(10) && !istype(T, /turf/space)) //randomly takes a 'hit' from ramming + get_hit() + +/obj/effect/meteor/Destroy() + walk(src,FALSE) //this cancels the walk_towards() proc + GLOB.meteor_list -= src + return ..() + +/obj/effect/meteor/New() + ..() + SpinAnimation() + +/obj/effect/meteor/Bump(atom/A) + if(attempt_vr(src,"Bump_vr",list(A))) return //VOREStation Edit - allows meteors to be deflected by baseball bats + if(A) + if(A.handle_meteor_impact(src)) // Used for special behaviour when getting hit specifically by a meteor, like a shield. + ram_turf(get_turf(A)) + get_hit() + else + die(FALSE) + +/obj/effect/meteor/CanPass(atom/movable/mover, turf/target) + return istype(mover, /obj/effect/meteor) ? TRUE : ..() + +/obj/effect/meteor/proc/ram_turf(var/turf/T) + //first bust whatever is in the turf + for(var/atom/A in T) + if(A == src) // Don't hit ourselves. + continue + if(isturf(A)) // Don't hit floors. We'll deal with walls later. + continue + A.ex_act(hitpwr) + + //then, ram the turf if it still exists + if(T) + if(istype(T, /turf/simulated/wall)) + var/turf/simulated/wall/W = T + W.take_damage(wall_power) // Stronger walls can halt asteroids. + +/obj/effect/meteor/proc/get_shield_damage() + return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) + +//process getting 'hit' by colliding with a dense object +//or randomly when ramming turfs +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + die(TRUE) + +/obj/effect/meteor/proc/die(var/explode = TRUE) + make_debris() + meteor_effect(explode) + qdel(src) + +/obj/effect/meteor/ex_act() + return + +/obj/effect/meteor/attackby(obj/item/weapon/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/weapon/pickaxe)) + qdel(src) + return + ..() + +/obj/effect/meteor/bullet_act(var/obj/item/projectile/Proj) + if(Proj.excavation_amount) + get_hit() + + if(!QDELETED(src)) + wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors. + + if(wall_power <= 0) + die(FALSE) // If you kill the meteor, then it dies. + return + return + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/obj/item/O = new meteordrop(get_turf(src)) + O.throw_at(dest, 5, 10) + +/obj/effect/meteor/proc/shake_players() + for(var/mob/M in player_list) + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + +/obj/effect/meteor/proc/meteor_effect(var/explode) + if(heavy) + shake_players() + + +/////////////////////// +//Meteor types +/////////////////////// + +// Dust breaks windows and hurts normal walls, generally more of an annoyance than a danger unless two happen to hit the same spot. +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + hitpwr = 3 + meteordrop = /obj/item/weapon/ore/glass + wall_power = 50 + +// Medium-sized meteors aren't very special and can be stopped easily by r-walls. +/obj/effect/meteor/medium + name = "meteor" + dropamt = 3 + wall_power = 200 + +/obj/effect/meteor/medium/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 0, 1, 2, 3, 0) + +// Large-sized meteors generally pack the most punch, but are more concentrated towards the epicenter. +/obj/effect/meteor/big + name = "large meteor" + icon_state = "large" + hits = 8 + heavy = 1 + dropamt = 4 + wall_power = 400 + +/obj/effect/meteor/big/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 1, 2, 3, 4, 0) + +// 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end. +/obj/effect/meteor/flaming + name = "flaming meteor" + icon_state = "flaming" + hits = 5 + heavy = 1 + meteordrop = /obj/item/weapon/ore/phoron + wall_power = 100 + +/obj/effect/meteor/flaming/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) + +// Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding. +/obj/effect/meteor/irradiated + name = "glowing meteor" + icon_state = "glowing" + heavy = 1 + meteordrop = /obj/item/weapon/ore/uranium + wall_power = 75 + + +/obj/effect/meteor/irradiated/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 0, 0, 4, 3, 0) + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + SSradiation.radiate(src, 50) + +// This meteor fries toasters. +/obj/effect/meteor/emp + name = "conducting meteor" + icon_state = "glowing_blue" + desc = "Hide your floppies!" + meteordrop = /obj/item/weapon/ore/osmium + dropamt = 3 + wall_power = 80 + +/obj/effect/meteor/emp/meteor_effect(var/explode) + ..() + // Best case scenario: Comparable to a low-yield EMP grenade. + // Worst case scenario: Comparable to a standard yield EMP grenade. + empulse(src, rand(1, 3), rand(2, 4), rand(3, 7), rand(5, 10)) + +/obj/effect/meteor/emp/get_shield_damage() + return ..() * rand(2,4) + +//Station buster Tunguska +/obj/effect/meteor/tunguska + name = "tunguska meteor" + icon_state = "flaming" + desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity" + hits = 30 + hitpwr = 1 + heavy = 1 + meteordrop = /obj/item/weapon/ore/phoron + wall_power = 150 + +/obj/effect/meteor/tunguska/meteor_effect(var/explode) + ..() + if(explode) + explosion(src.loc, 3, 6, 9, 20, 0) + +/obj/effect/meteor/tunguska/Bump() + ..() + if(prob(20)) + explosion(src.loc,2,4,6,8) diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index a0a5cb6fe89..f6f2d1f68d3 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,93 +1,93 @@ -/* - MERCENARY ROUNDTYPE -*/ - -var/list/nuke_disks = list() - -/datum/game_mode/nuclear - name = "Mercenary" - round_description = "A mercenary strike force is approaching the station!" - extended_round_description = "The Company's majority control of phoron in the system has marked the \ - station to be a highly valuable target for many competing organizations and individuals. Being a \ - colony of sizable population and considerable wealth causes it to often be the target of various \ - attempts of robbery, fraud and other malicious actions." - config_tag = "mercenary" - required_players = 12 - required_players_secret = 12 - required_enemies = 3 - end_on_antag_death = 0 - var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station - var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level - antag_tags = list(MODE_MERCENARY) - -//delete all nuke disks not on a station zlevel -/datum/game_mode/nuclear/proc/check_nuke_disks() - for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) - if(isNotStationLevel(N.z)) qdel(N) - -//checks if L has a nuke disk on their person -/datum/game_mode/nuclear/proc/check_mob(mob/living/L) - for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) - if(N.storage_depth(L) >= 0) - return 1 - return 0 - -/datum/game_mode/nuclear/declare_completion() - if(config.objectives_disabled) - ..() - return - var/disk_rescued = 1 - for(var/obj/item/weapon/disk/nuclear/D in nuke_disks) - var/disk_area = get_area(D) - if(!is_type_in_list(disk_area, centcom_areas)) - disk_rescued = 0 - break - var/crew_evacuated = (emergency_shuttle.returned()) - - if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) - feedback_set_details("round_end_result","win - syndicate nuke") - to_world("Mercenary Major Victory!") - to_world("[syndicate_name()] operatives have destroyed [station_name()]!") - - else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") - to_world("Total Annihilation") - to_world("[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") - - else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - blew wrong station") - to_world("Crew Minor Victory") - to_world("[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") - - else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) - feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") - to_world("[syndicate_name()] operatives have earned Darwin Award!") - to_world("[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") - - else if (disk_rescued && mercs.antags_are_dead()) - feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") - to_world("Crew Major Victory!") - to_world("The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") - - else if ( disk_rescued ) - feedback_set_details("round_end_result","loss - evacuation - disk secured") - to_world("Crew Major Victory") - to_world("The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") - - else if (!disk_rescued && mercs.antags_are_dead()) - feedback_set_details("round_end_result","loss - evacuation - disk not secured") - to_world("Mercenary Minor Victory!") - to_world("The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") - - else if (!disk_rescued && crew_evacuated) - feedback_set_details("round_end_result","halfwin - detonation averted") - to_world("Mercenary Minor Victory!") - to_world("[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") - - else if (!disk_rescued && !crew_evacuated) - feedback_set_details("round_end_result","halfwin - interrupted") - to_world("Neutral Victory") - to_world("Round was mysteriously interrupted!") - - ..() - return +/* + MERCENARY ROUNDTYPE +*/ + +var/list/nuke_disks = list() + +/datum/game_mode/nuclear + name = "Mercenary" + round_description = "A mercenary strike force is approaching the station!" + extended_round_description = "The Company's majority control of phoron in the system has marked the \ + station to be a highly valuable target for many competing organizations and individuals. Being a \ + colony of sizable population and considerable wealth causes it to often be the target of various \ + attempts of robbery, fraud and other malicious actions." + config_tag = "mercenary" + required_players = 12 + required_players_secret = 12 + required_enemies = 3 + end_on_antag_death = 0 + var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station + var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level + antag_tags = list(MODE_MERCENARY) + +//delete all nuke disks not on a station zlevel +/datum/game_mode/nuclear/proc/check_nuke_disks() + for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) + if(isNotStationLevel(N.z)) qdel(N) + +//checks if L has a nuke disk on their person +/datum/game_mode/nuclear/proc/check_mob(mob/living/L) + for(var/obj/item/weapon/disk/nuclear/N in nuke_disks) + if(N.storage_depth(L) >= 0) + return 1 + return 0 + +/datum/game_mode/nuclear/declare_completion() + if(config.objectives_disabled) + ..() + return + var/disk_rescued = 1 + for(var/obj/item/weapon/disk/nuclear/D in nuke_disks) + var/disk_area = get_area(D) + if(!is_type_in_list(disk_area, centcom_areas)) + disk_rescued = 0 + break + var/crew_evacuated = (emergency_shuttle.returned()) + + if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) + feedback_set_details("round_end_result","win - syndicate nuke") + to_world("Mercenary Major Victory!") + to_world("[syndicate_name()] operatives have destroyed [station_name()]!") + + else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") + to_world("Total Annihilation") + to_world("[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") + + else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - blew wrong station") + to_world("Crew Minor Victory") + to_world("[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") + + else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) + feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") + to_world("[syndicate_name()] operatives have earned Darwin Award!") + to_world("[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") + + else if (disk_rescued && mercs.antags_are_dead()) + feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") + to_world("Crew Major Victory!") + to_world("The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") + + else if ( disk_rescued ) + feedback_set_details("round_end_result","loss - evacuation - disk secured") + to_world("Crew Major Victory") + to_world("The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") + + else if (!disk_rescued && mercs.antags_are_dead()) + feedback_set_details("round_end_result","loss - evacuation - disk not secured") + to_world("Mercenary Minor Victory!") + to_world("The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") + + else if (!disk_rescued && crew_evacuated) + feedback_set_details("round_end_result","halfwin - detonation averted") + to_world("Mercenary Minor Victory!") + to_world("[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") + + else if (!disk_rescued && !crew_evacuated) + feedback_set_details("round_end_result","halfwin - interrupted") + to_world("Neutral Victory") + to_world("Round was mysteriously interrupted!") + + ..() + return diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 7c51611bf2d..2a60b7ef8f0 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,317 +1,317 @@ -/obj/item/weapon/pinpointer - name = "pinpointer" - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - matter = list(MAT_STEEL = 500) - preserve_item = 1 - var/obj/item/weapon/disk/nuclear/the_disk = null - var/active = 0 - -/obj/item/weapon/pinpointer/Destroy() - active = 0 - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/weapon/pinpointer/attack_self() - if(!active) - active = 1 - START_PROCESSING(SSobj, src) - to_chat(usr, "You activate the pinpointer") - else - active = 0 - STOP_PROCESSING(SSobj, src) - icon_state = "pinoff" - to_chat(usr, "You deactivate the pinpointer") - -/obj/item/weapon/pinpointer/process() - if(!active) - return PROCESS_KILL - - if(!the_disk) - the_disk = locate() - if(!the_disk) - icon_state = "pinonnull" - return - - set_dir(get_dir(src,the_disk)) - - switch(get_dist(src,the_disk)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/examine(mob/user) - . = ..() - for(var/obj/machinery/nuclearbomb/bomb in machines) - if(bomb.timing) - . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" - - - -/obj/item/weapon/pinpointer/advpinpointer - name = "Advanced Pinpointer" - icon = 'icons/obj/device.dmi' - desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." - var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. - var/turf/location = null - var/obj/target = null - -/obj/item/weapon/pinpointer/advpinpointer/process() - if(!active) - return PROCESS_KILL - if(mode == 0) - ..() - if(mode == 1) - worklocation() - if(mode == 2) - workobj() - -/obj/item/weapon/pinpointer/advpinpointer/proc/worklocation() - if(!location) - icon_state = "pinonnull" - return - - set_dir(get_dir(src,location)) - - switch(get_dist(src,location)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/advpinpointer/proc/workobj() - if(!target) - icon_state = "pinonnull" - return - - set_dir(get_dir(src,target)) - - switch(get_dist(src,target)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode() - set category = "Object" - set name = "Toggle Pinpointer Mode" - set src in view(1) - - active = 0 - icon_state = "pinoff" - target=null - location = null - - switch(tgui_alert(usr, "Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", list("Location", "Disk Recovery", "Other Signature"))) - if("Location") - mode = 1 - - var/locationx = tgui_input_number(usr, "Please input the x coordinate to search for.", "Location?" , "") - if(!locationx || !(usr in view(1,src))) - return - var/locationy = tgui_input_number(usr, "Please input the y coordinate to search for.", "Location?" , "") - if(!locationy || !(usr in view(1,src))) - return - - var/turf/Z = get_turf(src) - - location = locate(locationx,locationy,Z.z) - - to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") - - return attack_self() - - if("Disk Recovery") - mode = 0 - return attack_self() - - if("Other Signature") - mode = 2 - switch(tgui_alert(usr, "Search for item signature or DNA fragment?", "Signature Mode Select", list("Item", "DNA"))) - - if("Item") - var/datum/objective/steal/itemlist - itemlist = itemlist - var/targetitem = tgui_input_list(usr, "Select item to search for.", "Item Mode Select", itemlist.possible_items) - if(!targetitem) - return - target=locate(itemlist.possible_items[targetitem]) - if(!target) - to_chat(usr, "Failed to locate [targetitem]!") - return - to_chat(usr, "You set the pinpointer to locate [targetitem]") - - if("DNA") - var/DNAstring = tgui_input_text(usr, "Input DNA string to search for." , "Please Enter String." , "") - if(!DNAstring) - return - for(var/mob/living/carbon/M in mob_list) - if(!M.dna) - continue - if(M.dna.unique_enzymes == DNAstring) - target = M - break - - return attack_self() - - -/////////////////////// -//nuke op pinpointers// -/////////////////////// - - -/obj/item/weapon/pinpointer/nukeop - var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle - var/obj/machinery/computer/shuttle_control/multi/syndicate/home = null - -/obj/item/weapon/pinpointer/nukeop/attack_self(mob/user as mob) - if(!active) - active = 1 - START_PROCESSING(SSobj, src) - if(!mode) - workdisk() - to_chat(user, "Authentication Disk Locator active.") - else - worklocation() - to_chat(user, "Shuttle Locator active.") - else - active = 0 - STOP_PROCESSING(SSobj, src) - icon_state = "pinoff" - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/weapon/pinpointer/nukeop/process() - if(!active) - return PROCESS_KILL - - switch(mode) - if(0) - workdisk() - if(1) - worklocation() - -/obj/item/weapon/pinpointer/nukeop/proc/workdisk() - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator active.") //Lets the mob holding it know that the mode has changed - return //Get outta here - - if(!the_disk) - the_disk = locate() - if(!the_disk) - icon_state = "pinonnull" - return - - set_dir(get_dir(src, the_disk)) - - switch(get_dist(src, the_disk)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - -/obj/item/weapon/pinpointer/nukeop/proc/worklocation() - if(!bomb_set) - mode = 0 - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - visible_message("Authentication Disk Locator active.") - return - - if(!home) - home = locate() - if(!home) - icon_state = "pinonnull" - return - - if(loc.z != home.z) //If you are on a different z-level from the shuttle - icon_state = "pinonnull" - - else - set_dir(get_dir(src, home)) - - switch(get_dist(src, home)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - - -// This one only points to the ship. Useful if there is no nuking to occur today. -/obj/item/weapon/pinpointer/shuttle - var/shuttle_comp_id = null - var/obj/machinery/computer/shuttle_control/our_shuttle = null - -/obj/item/weapon/pinpointer/shuttle/attack_self(mob/user as mob) - if(!active) - active = TRUE - START_PROCESSING(SSobj, src) - to_chat(user, "Shuttle Locator active.") - else - active = FALSE - STOP_PROCESSING(SSobj, src) - icon_state = "pinoff" - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/weapon/pinpointer/shuttle/process() - if(!active) - return PROCESS_KILL - - if(!our_shuttle) - for(var/obj/machinery/computer/shuttle_control/S in machines) - if(S.shuttle_tag == shuttle_comp_id) // Shuttle tags are used so that it will work if the computer path changes, as it does on the southern cross map. - our_shuttle = S - break - - if(!our_shuttle) - icon_state = "pinonnull" - return - - if(loc.z != our_shuttle.z) //If you are on a different z-level from the shuttle - icon_state = "pinonnull" - - else - set_dir(get_dir(src, our_shuttle)) - - switch(get_dist(src, our_shuttle)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - - -/obj/item/weapon/pinpointer/shuttle/merc - shuttle_comp_id = "Mercenary" - -/obj/item/weapon/pinpointer/shuttle/heist +/obj/item/weapon/pinpointer + name = "pinpointer" + icon = 'icons/obj/device.dmi' + icon_state = "pinoff" + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + matter = list(MAT_STEEL = 500) + preserve_item = 1 + var/obj/item/weapon/disk/nuclear/the_disk = null + var/active = 0 + +/obj/item/weapon/pinpointer/Destroy() + active = 0 + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/pinpointer/attack_self() + if(!active) + active = 1 + START_PROCESSING(SSobj, src) + to_chat(usr, "You activate the pinpointer") + else + active = 0 + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(usr, "You deactivate the pinpointer") + +/obj/item/weapon/pinpointer/process() + if(!active) + return PROCESS_KILL + + if(!the_disk) + the_disk = locate() + if(!the_disk) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,the_disk)) + + switch(get_dist(src,the_disk)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/examine(mob/user) + . = ..() + for(var/obj/machinery/nuclearbomb/bomb in machines) + if(bomb.timing) + . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" + + + +/obj/item/weapon/pinpointer/advpinpointer + name = "Advanced Pinpointer" + icon = 'icons/obj/device.dmi' + desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." + var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. + var/turf/location = null + var/obj/target = null + +/obj/item/weapon/pinpointer/advpinpointer/process() + if(!active) + return PROCESS_KILL + if(mode == 0) + ..() + if(mode == 1) + worklocation() + if(mode == 2) + workobj() + +/obj/item/weapon/pinpointer/advpinpointer/proc/worklocation() + if(!location) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,location)) + + switch(get_dist(src,location)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/advpinpointer/proc/workobj() + if(!target) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,target)) + + switch(get_dist(src,target)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode() + set category = "Object" + set name = "Toggle Pinpointer Mode" + set src in view(1) + + active = 0 + icon_state = "pinoff" + target=null + location = null + + switch(tgui_alert(usr, "Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", list("Location", "Disk Recovery", "Other Signature"))) + if("Location") + mode = 1 + + var/locationx = tgui_input_number(usr, "Please input the x coordinate to search for.", "Location?" , "") + if(!locationx || !(usr in view(1,src))) + return + var/locationy = tgui_input_number(usr, "Please input the y coordinate to search for.", "Location?" , "") + if(!locationy || !(usr in view(1,src))) + return + + var/turf/Z = get_turf(src) + + location = locate(locationx,locationy,Z.z) + + to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") + + return attack_self() + + if("Disk Recovery") + mode = 0 + return attack_self() + + if("Other Signature") + mode = 2 + switch(tgui_alert(usr, "Search for item signature or DNA fragment?", "Signature Mode Select", list("Item", "DNA"))) + + if("Item") + var/datum/objective/steal/itemlist + itemlist = itemlist + var/targetitem = tgui_input_list(usr, "Select item to search for.", "Item Mode Select", itemlist.possible_items) + if(!targetitem) + return + target=locate(itemlist.possible_items[targetitem]) + if(!target) + to_chat(usr, "Failed to locate [targetitem]!") + return + to_chat(usr, "You set the pinpointer to locate [targetitem]") + + if("DNA") + var/DNAstring = tgui_input_text(usr, "Input DNA string to search for." , "Please Enter String." , "") + if(!DNAstring) + return + for(var/mob/living/carbon/M in mob_list) + if(!M.dna) + continue + if(M.dna.unique_enzymes == DNAstring) + target = M + break + + return attack_self() + + +/////////////////////// +//nuke op pinpointers// +/////////////////////// + + +/obj/item/weapon/pinpointer/nukeop + var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle + var/obj/machinery/computer/shuttle_control/multi/syndicate/home = null + +/obj/item/weapon/pinpointer/nukeop/attack_self(mob/user as mob) + if(!active) + active = 1 + START_PROCESSING(SSobj, src) + if(!mode) + workdisk() + to_chat(user, "Authentication Disk Locator active.") + else + worklocation() + to_chat(user, "Shuttle Locator active.") + else + active = 0 + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/weapon/pinpointer/nukeop/process() + if(!active) + return PROCESS_KILL + + switch(mode) + if(0) + workdisk() + if(1) + worklocation() + +/obj/item/weapon/pinpointer/nukeop/proc/workdisk() + if(bomb_set) //If the bomb is set, lead to the shuttle + mode = 1 //Ensures worklocation() continues to work + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep + visible_message("Shuttle Locator active.") //Lets the mob holding it know that the mode has changed + return //Get outta here + + if(!the_disk) + the_disk = locate() + if(!the_disk) + icon_state = "pinonnull" + return + + set_dir(get_dir(src, the_disk)) + + switch(get_dist(src, the_disk)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/nukeop/proc/worklocation() + if(!bomb_set) + mode = 0 + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) + visible_message("Authentication Disk Locator active.") + return + + if(!home) + home = locate() + if(!home) + icon_state = "pinonnull" + return + + if(loc.z != home.z) //If you are on a different z-level from the shuttle + icon_state = "pinonnull" + + else + set_dir(get_dir(src, home)) + + switch(get_dist(src, home)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + + +// This one only points to the ship. Useful if there is no nuking to occur today. +/obj/item/weapon/pinpointer/shuttle + var/shuttle_comp_id = null + var/obj/machinery/computer/shuttle_control/our_shuttle = null + +/obj/item/weapon/pinpointer/shuttle/attack_self(mob/user as mob) + if(!active) + active = TRUE + START_PROCESSING(SSobj, src) + to_chat(user, "Shuttle Locator active.") + else + active = FALSE + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/weapon/pinpointer/shuttle/process() + if(!active) + return PROCESS_KILL + + if(!our_shuttle) + for(var/obj/machinery/computer/shuttle_control/S in machines) + if(S.shuttle_tag == shuttle_comp_id) // Shuttle tags are used so that it will work if the computer path changes, as it does on the southern cross map. + our_shuttle = S + break + + if(!our_shuttle) + icon_state = "pinonnull" + return + + if(loc.z != our_shuttle.z) //If you are on a different z-level from the shuttle + icon_state = "pinonnull" + + else + set_dir(get_dir(src, our_shuttle)) + + switch(get_dist(src, our_shuttle)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + + +/obj/item/weapon/pinpointer/shuttle/merc + shuttle_comp_id = "Mercenary" + +/obj/item/weapon/pinpointer/shuttle/heist shuttle_comp_id = "Skipjack" \ No newline at end of file diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 53e6c61304b..c42df5e5cdb 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,906 +1,906 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -var/global/list/all_objectives = list() - -/datum/objective - var/datum/mind/owner = null //Who owns the objective. - var/explanation_text = "Nothing" //What that person is supposed to do. - var/datum/mind/target = null //If they are focused on a particular person. - var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. - var/completed = 0 //currently only used for custom objectives. - -/datum/objective/New(var/text) - all_objectives |= src - if(text) - explanation_text = text - ..() - -/datum/objective/Destroy() - all_objectives -= src - ..() - -/datum/objective/proc/check_completion() - return completed - -/datum/objective/proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in ticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2)) - possible_targets += possible_target - if(possible_targets.len > 0) - target = pick(possible_targets) - - -/datum/objective/proc/find_target_by_role(role, role_type=0)//Option sets either to check assigned role or special role. Default to assigned. - for(var/datum/mind/possible_target in ticker.minds) - if((possible_target != owner) && ishuman(possible_target.current) && ((role_type ? possible_target.special_role : possible_target.assigned_role) == role) ) - target = possible_target - break - - - -/datum/objective/assassinate/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/assassinate/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/assassinate/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - return 0 - return 1 - - -/datum/objective/anti_revolution/execute/find_target() - ..() - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute [T.him]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/anti_revolution/execute/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has extracted confidential information above their clearance. Execute [T.him]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/execute/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || !ishuman(target.current)) - return 1 - return 0 - return 1 - -/datum/objective/anti_revolution/brig - var/already_completed = 0 - -/datum/objective/anti_revolution/brig/find_target() - ..() - if(target && target.current) - explanation_text = "Brig [target.current.real_name], the [target.assigned_role] for 20 minutes to set an example." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/anti_revolution/brig/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Brig [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] for 20 minutes to set an example." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/brig/check_completion() - if(already_completed) - return 1 - - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - if(target.is_brigged(10 * 60 * 10)) - already_completed = 1 - return 1 - return 0 - return 0 - -/datum/objective/anti_revolution/demote/find_target() - ..() - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/demote/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - var/datum/gender/T = gender_datums[target.current.get_visible_gender()] - explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." - else - explanation_text = "Free Objective" - return target - -/datum/objective/anti_revolution/demote/check_completion() - if(target && target.current && istype(target,/mob/living/carbon/human)) - var/obj/item/weapon/card/id/I = target.current:wear_id - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/P = I - I = P.id - - if(!istype(I)) return 1 - - if(I.assignment == USELESS_JOB) //VOREStation Edit - Visitor not Assistant - return 1 - else - return 0 - return 1 - -//I want braaaainssss -/datum/objective/debrain/find_target() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/debrain/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name] the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/debrain/check_completion() - if(!target)//If it's a free objective. - return 1 - if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead. - return 0 - if( !target.current || !isbrain(target.current) ) - return 0 - var/atom/A = target.current - while(A.loc) //check to see if the brainmob is on our person - A = A.loc - if(A == owner.current) - return 1 - return 0 - - -//The opposite of killing a dude. -/datum/objective/protect/find_target() - ..() - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/protect/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/protect/check_completion() - if(!target) //If it's a free objective. - return 1 - if(target.current) - if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current)) - return 0 - return 1 - return 0 - - -/datum/objective/hijack - explanation_text = "Hijack the emergency shuttle by escaping alone." - -/datum/objective/hijack/check_completion() - if(!owner.current || owner.current.stat) - return 0 - if(!emergency_shuttle.returned()) - return 0 - if(issilicon(owner.current)) - return 0 - var/area/shuttle = locate(/area/shuttle/escape/centcom) - var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai) - for(var/mob/living/player in player_list) - if(player.type in protected_mobs) continue - if (player.mind && (player.mind != owner)) - if(player.stat != DEAD) //they're not dead! - if(get_turf(player) in shuttle) - return 0 - return 1 - - -/datum/objective/block - explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive." - - -/datum/objective/block/check_completion() - if(!istype(owner.current, /mob/living/silicon)) - return 0 - if(!emergency_shuttle.returned()) - return 0 - if(!owner.current) - return 0 - var/area/shuttle = locate(/area/shuttle/escape/centcom) - var/protected_mobs[] = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) - for(var/mob/living/player in player_list) - if(player.type in protected_mobs) continue - if (player.mind) - if (player.stat != 2) - if (get_turf(player) in shuttle) - return 0 - return 1 - -/datum/objective/silence - explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left." - -/datum/objective/silence/check_completion() - if(!emergency_shuttle.returned()) - return 0 - - for(var/mob/living/player in player_list) - if(player == owner.current) - continue - if(player.mind) - if(player.stat != DEAD) - var/turf/T = get_turf(player) - if(!T) continue - switch(T.loc.type) - if(/area/shuttle/escape/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) - return 0 - return 1 - - -/datum/objective/escape - explanation_text = "Escape on the shuttle or an escape pod alive and free." - - -/datum/objective/escape/check_completion() - if(issilicon(owner.current)) - return 0 - if(isbrain(owner.current)) - return 0 - if(!emergency_shuttle.returned()) - return 0 - if(!owner.current || owner.current.stat ==2) - return 0 - var/turf/location = get_turf(owner.current.loc) - if(!location) - return 0 - - if(istype(location, /turf/simulated/shuttle/floor4)) // Fails traitors if they are in the shuttle brig -- Polymorph - if(istype(owner.current, /mob/living/carbon)) - var/mob/living/carbon/C = owner.current - if (!C.handcuffed) - return 1 - return 0 - - var/area/check_area = location.loc - if(istype(check_area, /area/shuttle/escape/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod1/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod2/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod3/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod5/centcom)) - return 1 - else - return 0 - - - -/datum/objective/survive - explanation_text = "Stay alive until the end." - -/datum/objective/survive/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) - return 0 //Brains no longer win survive objectives. --NEO - if(issilicon(owner.current) && owner.current != owner.original) - return 0 - return 1 - -// Similar to the anti-rev objective, but for traitors -/datum/objective/brig - var/already_completed = 0 - -/datum/objective/brig/find_target() - ..() - if(target && target.current) - explanation_text = "Have [target.current.real_name], the [target.assigned_role] brigged for 10 minutes." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/brig/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Have [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] brigged for 10 minutes." - else - explanation_text = "Free Objective" - return target - -/datum/objective/brig/check_completion() - if(already_completed) - return 1 - - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - // Make the actual required time a bit shorter than the official time - if(target.is_brigged(10 * 60 * 5)) - already_completed = 1 - return 1 - return 0 - return 0 - -// Harm a crew member, making an example of them -/datum/objective/harm - var/already_completed = 0 - -/datum/objective/harm/find_target() - ..() - if(target && target.current) - explanation_text = "Make an example of [target.current.real_name], the [target.assigned_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/harm/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Make an example of [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." - else - explanation_text = "Free Objective" - return target - -/datum/objective/harm/check_completion() - if(already_completed) - return 1 - - if(target && target.current && istype(target.current, /mob/living/carbon/human)) - if(target.current.stat == DEAD) - return 0 - - var/mob/living/carbon/human/H = target.current - for(var/obj/item/organ/external/E in H.organs) - if(E.status & ORGAN_BROKEN) - return 1 - for(var/limb_tag in H.species.has_limbs) //todo check prefs for robotic limbs and amputations. - var/list/organ_data = H.species.has_limbs[limb_tag] - var/limb_type = organ_data["path"] - var/found - for(var/obj/item/organ/external/E in H.organs) - if(limb_type == E.type) - found = 1 - break - if(!found) - return 1 - - var/obj/item/organ/external/head/head = H.get_organ(BP_HEAD) - if(head.disfigured) - return 1 - return 0 - - -/datum/objective/nuclear - explanation_text = "Destroy the station with a nuclear device." - - - -/datum/objective/steal - var/obj/item/steal_target - var/target_name - - var/global/possible_items[] = list( - "the Site Manager's antique laser gun" = /obj/item/weapon/gun/energy/captain, - "a hand teleporter" = /obj/item/weapon/hand_tele, - "an RCD" = /obj/item/weapon/rcd, - "a jetpack" = /obj/item/weapon/tank/jetpack, - "a site manager's jumpsuit" = /obj/item/clothing/under/rank/captain, - "a functional AI" = /obj/item/device/aicard, - "a pair of magboots" = /obj/item/clothing/shoes/magboots, - "the station blueprints" = /obj/item/areaeditor/blueprints, - "a nasa voidsuit" = /obj/item/clothing/suit/space/void, - "28 moles of phoron (full tank)" = /obj/item/weapon/tank, - "a sample of slime extract" = /obj/item/slime_extract, - "a piece of corgi meat" = /obj/item/weapon/reagent_containers/food/snacks/meat/corgi, - "a research director's jumpsuit" = /obj/item/clothing/under/rank/research_director, - "a chief engineer's jumpsuit" = /obj/item/clothing/under/rank/chief_engineer, - "a chief medical officer's jumpsuit" = /obj/item/clothing/under/rank/chief_medical_officer, - "a head of security's jumpsuit" = /obj/item/clothing/under/rank/head_of_security, - "a head of personnel's jumpsuit" = /obj/item/clothing/under/rank/head_of_personnel, - "the hypospray" = /obj/item/weapon/reagent_containers/hypospray/vial, - "the site manager's pinpointer" = /obj/item/weapon/pinpointer, - "an ablative armor vest" = /obj/item/clothing/suit/armor/laserproof, - ) - - var/global/possible_items_special[] = list( - /*"nuclear authentication disk" = /obj/item/weapon/disk/nuclear,*///Broken with the change to nuke disk making it respawn on z level change. - "nuclear gun" = /obj/item/weapon/gun/energy/gun/nuclear, - "diamond drill" = /obj/item/weapon/pickaxe/diamonddrill, - "bag of holding" = /obj/item/weapon/storage/backpack/holding, - "hyper-capacity cell" = /obj/item/weapon/cell/hyper, - "10 diamonds" = /obj/item/stack/material/diamond, - "50 gold bars" = /obj/item/stack/material/gold, - "25 refined uranium bars" = /obj/item/stack/material/uranium, - ) - - -/datum/objective/steal/proc/set_target(item_name) - target_name = item_name - steal_target = possible_items[target_name] - if (!steal_target ) - steal_target = possible_items_special[target_name] - explanation_text = "Steal [target_name]." - return steal_target - - -/datum/objective/steal/find_target() - return set_target(pick(possible_items)) - - -/datum/objective/steal/proc/select_target() - var/list/possible_items_all = possible_items+possible_items_special+"custom" - var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_items_all) - if (!new_target) return - if (new_target == "custom") - var/obj/item/custom_target = tgui_input_list(usr, "Select type:", "Type", typesof(/obj/item)) - if (!custom_target) return - var/tmp_obj = new custom_target - var/custom_name = tmp_obj:name - qdel(tmp_obj) - custom_name = sanitize(tgui_input_text(usr, "Enter target name:", "Objective target", custom_name)) - if (!custom_name) return - target_name = custom_name - steal_target = custom_target - explanation_text = "Steal [target_name]." - else - set_target(new_target) - return steal_target - -/datum/objective/steal/check_completion() - if(!steal_target || !owner.current) return 0 - if(!isliving(owner.current)) return 0 - var/list/all_items = owner.current.get_contents() - switch (target_name) - if("28 moles of phoron (full tank)","10 diamonds","50 gold bars","25 refined uranium bars") - var/target_amount = text2num(target_name)//Non-numbers are ignored. - var/found_amount = 0.0//Always starts as zero. - - for(var/obj/item/I in all_items) //Check for phoron tanks - if(istype(I, steal_target)) - found_amount += (target_name=="28 moles of phoron (full tank)" ? (I:air_contents:gas["phoron"]) : (I:amount)) - return found_amount>=target_amount - - if("50 coins (in bag)") - var/obj/item/weapon/moneybag/B = locate() in all_items - - if(B) - var/target = text2num(target_name) - var/found_amount = 0.0 - for(var/obj/item/weapon/coin/C in B) - found_amount++ - return found_amount>=target - - if("a functional AI") - - for(var/obj/item/device/aicard/C in all_items) //Check for ai card - for(var/mob/living/silicon/ai/M in C) - if(istype(M, /mob/living/silicon/ai) && M.stat != 2) //See if any AI's are alive inside that card. - return 1 - - for(var/mob/living/silicon/ai/ai in mob_list) - var/turf/T = get_turf(ai) - if(istype(T)) - var/area/check_area = get_area(ai) - if(istype(check_area, /area/shuttle/escape/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod1/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod2/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod3/centcom)) - return 1 - if(istype(check_area, /area/shuttle/escape_pod5/centcom)) - return 1 - else - - for(var/obj/I in all_items) //Check for items - if(istype(I, steal_target)) - return 1 - return 0 - - - -/datum/objective/download/proc/gen_amount_goal() - target_amount = rand(10,20) - explanation_text = "Download [target_amount] research levels." - return target_amount - - -/datum/objective/download/check_completion() - if(!ishuman(owner.current)) - return 0 - if(!owner.current || owner.current.stat == 2) - return 0 - - var/current_amount - var/obj/item/weapon/rig/S - if(istype(owner.current,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = owner.current - S = H.back - - if(!istype(S) || !S.installed_modules || !S.installed_modules.len) - return 0 - - var/obj/item/rig_module/datajack/stolen_data = locate() in S.installed_modules - if(!istype(stolen_data)) - return 0 - - for(var/datum/tech/current_data in stolen_data.stored_research) - if(current_data.level > 1) - current_amount += (current_data.level-1) - - return (current_amount= target_amount)) - return 1 - else - return 0 - -/datum/objective/vore/check_completion() - if(owner && owner.vore_prey_eaten >= target_amount) - return 1 - else - return 0 - -// Heist objectives. -/datum/objective/heist/proc/choose_target() - return - -/datum/objective/heist/kidnap/choose_target() - var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Engineer") - var/list/possible_targets = list() - var/list/priority_targets = list() - - for(var/datum/mind/possible_target in ticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && (!possible_target.special_role)) - possible_targets += possible_target - for(var/role in roles) - if(possible_target.assigned_role == role) - priority_targets += possible_target - continue - - if(priority_targets.len > 0) - target = pick(priority_targets) - else if(possible_targets.len > 0) - target = pick(possible_targets) - - if(target && target.current) - explanation_text = "We can get a good price for [target.current.real_name], the [target.assigned_role]. Take them alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/heist/kidnap/check_completion() - if(target && target.current) - if (target.current.stat == 2) - return 0 // They're dead. Fail. - //if (!target.current.restrained()) - // return 0 // They're loose. Close but no cigar. - - var/area/skipjack_station/start/A = locate() - for(var/mob/living/carbon/human/M in A) - if(target.current == M) - return 1 //They're restrained on the shuttle. Success. - else - return 0 - -/datum/objective/heist/loot/choose_target() - var/loot = "an object" - switch(rand(1,8)) - if(1) - target = /obj/structure/particle_accelerator - target_amount = 6 - loot = "a complete particle accelerator" - if(2) - target = /obj/machinery/the_singularitygen - target_amount = 1 - loot = "a gravitational generator" - if(3) - target = /obj/machinery/power/emitter - target_amount = 4 - loot = "four emitters" - if(4) - target = /obj/machinery/nuclearbomb - target_amount = 1 - loot = "a nuclear bomb" - if(5) - target = /obj/item/weapon/gun - target_amount = 6 - loot = "six guns" - if(6) - target = /obj/item/weapon/gun/energy - target_amount = 4 - loot = "four energy guns" - if(7) - target = /obj/item/weapon/gun/energy/laser - target_amount = 2 - loot = "two laser guns" - if(8) - target = /obj/item/weapon/gun/energy/ionrifle - target_amount = 1 - loot = "an ion gun" - - explanation_text = "It's a buyer's market out here. Steal [loot] for resale." - - -/datum/objective/heist/loot/check_completion() - var/total_amount = 0 - - for(var/obj/O in locate(/area/skipjack_station/start)) - if(istype(O,target)) total_amount++ - for(var/obj/I in O.contents) - if(istype(I,target)) total_amount++ - if(total_amount >= target_amount) return 1 - - for(var/datum/mind/raider in raiders.current_antagonists) - if(raider.current) - for(var/obj/O in raider.current.get_contents()) - if(istype(O,target)) total_amount++ - if(total_amount >= target_amount) return 1 - - return 0 - -/datum/objective/heist/salvage/choose_target() - switch(rand(1,8)) - if(1) - target = MAT_STEEL - target_amount = 300 - if(2) - target = MAT_GLASS - target_amount = 200 - if(3) - target = MAT_PLASTEEL - target_amount = 100 - if(4) - target = MAT_PHORON - target_amount = 100 - if(5) - target = MAT_SILVER - target_amount = 50 - if(6) - target = MAT_GOLD - target_amount = 20 - if(7) - target = MAT_URANIUM - target_amount = 20 - if(8) - target = MAT_DIAMOND - target_amount = 20 - - explanation_text = "Ransack the station and escape with [target_amount] [target]." - -/datum/objective/heist/salvage/check_completion() - - var/total_amount = 0 - - for(var/obj/item/O in locate(/area/skipjack_station/start)) - - var/obj/item/stack/material/S - if(istype(O,/obj/item/stack/material)) - if(O.name == target) - S = O - total_amount += S.get_amount() - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/material)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - for(var/datum/mind/raider in raiders.current_antagonists) - if(raider.current) - for(var/obj/item/O in raider.current.get_contents()) - if(istype(O,/obj/item/stack/material)) - if(O.name == target) - var/obj/item/stack/material/S = O - total_amount += S.get_amount() - - if(total_amount >= target_amount) return 1 - return 0 - - -/datum/objective/heist/preserve_crew - explanation_text = "Do not leave anyone behind, alive or dead." - -/datum/objective/heist/preserve_crew/check_completion() - if(raiders && raiders.is_raider_crew_safe()) return 1 - return 0 - -//Borer objective(s). -/datum/objective/borer_survive - explanation_text = "Survive in a host until the end of the round." - -/datum/objective/borer_survive/check_completion() - if(owner) - var/mob/living/simple_mob/animal/borer/B = owner - if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1 - return 0 - -/datum/objective/borer_reproduce - explanation_text = "Reproduce at least once." - -/datum/objective/borer_reproduce/check_completion() - if(owner && owner.current) - var/mob/living/simple_mob/animal/borer/B = owner.current - if(istype(B) && B.has_reproduced) return 1 - return 0 - -/datum/objective/ninja_highlander - explanation_text = "You aspire to be a Grand Master of the Spider Clan. Kill all of your fellow acolytes." - -/datum/objective/ninja_highlander/check_completion() - if(owner) - for(var/datum/mind/ninja in get_antags("ninja")) - if(ninja != owner) - if(ninja.current.stat < 2) return 0 - return 1 - return 0 - -/datum/objective/cult/survive - explanation_text = "Our knowledge must live on." - target_amount = 5 - -/datum/objective/cult/survive/New() - ..() - explanation_text = "Our knowledge must live on. Make sure at least [target_amount] acolytes escape on the shuttle to spread their work on an another station." - -/datum/objective/cult/survive/check_completion() - var/acolytes_survived = 0 - if(!cult) - return 0 - for(var/datum/mind/cult_mind in cult.current_antagonists) - if (cult_mind.current && cult_mind.current.stat!=2) - var/area/A = get_area(cult_mind.current ) - if ( is_type_in_list(A, centcom_areas)) - acolytes_survived++ - if(acolytes_survived >= target_amount) - return 0 - else - return 1 - -/datum/objective/cult/eldergod - explanation_text = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it. The convert rune is join blood self." - -/datum/objective/cult/eldergod/check_completion() - return (locate(/obj/singularity/narsie/large) in machines) - -/datum/objective/cult/sacrifice - explanation_text = "Conduct a ritual sacrifice for the glory of Nar-Sie." - -/datum/objective/cult/sacrifice/find_target() - var/list/possible_targets = list() - if(!possible_targets.len) - for(var/mob/living/carbon/human/player in player_list) - if(player.mind && !(player.mind in cult)) - possible_targets += player.mind - if(possible_targets.len > 0) - target = pick(possible_targets) - if(target) explanation_text = "Sacrifice [target.name], the [target.assigned_role]. You will need the sacrifice rune (Hell blood join) and three acolytes to do so." - -/datum/objective/cult/sacrifice/check_completion() - return (target && cult && !cult.sacrificed.Find(target)) - -/datum/objective/rev/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate, capture or convert [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/rev/find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "Assassinate, capture or convert [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/rev/check_completion() - var/rval = 1 - if(target && target.current) - var/mob/living/carbon/human/H = target.current - if(!istype(H)) - return 1 - if(H.stat == DEAD || H.restrained()) - return 1 - // Check if they're converted - if(target in revs.current_antagonists) - return 1 - var/turf/T = get_turf(H) - if(T && isNotStationLevel(T.z)) //If they leave the station they count as dead for this - rval = 2 - return 0 - return rval - +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +var/global/list/all_objectives = list() + +/datum/objective + var/datum/mind/owner = null //Who owns the objective. + var/explanation_text = "Nothing" //What that person is supposed to do. + var/datum/mind/target = null //If they are focused on a particular person. + var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. + var/completed = 0 //currently only used for custom objectives. + +/datum/objective/New(var/text) + all_objectives |= src + if(text) + explanation_text = text + ..() + +/datum/objective/Destroy() + all_objectives -= src + ..() + +/datum/objective/proc/check_completion() + return completed + +/datum/objective/proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in ticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2)) + possible_targets += possible_target + if(possible_targets.len > 0) + target = pick(possible_targets) + + +/datum/objective/proc/find_target_by_role(role, role_type=0)//Option sets either to check assigned role or special role. Default to assigned. + for(var/datum/mind/possible_target in ticker.minds) + if((possible_target != owner) && ishuman(possible_target.current) && ((role_type ? possible_target.special_role : possible_target.assigned_role) == role) ) + target = possible_target + break + + + +/datum/objective/assassinate/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/assassinate/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/assassinate/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + return 0 + return 1 + + +/datum/objective/anti_revolution/execute/find_target() + ..() + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute [T.him]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/anti_revolution/execute/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has extracted confidential information above their clearance. Execute [T.him]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/execute/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || !ishuman(target.current)) + return 1 + return 0 + return 1 + +/datum/objective/anti_revolution/brig + var/already_completed = 0 + +/datum/objective/anti_revolution/brig/find_target() + ..() + if(target && target.current) + explanation_text = "Brig [target.current.real_name], the [target.assigned_role] for 20 minutes to set an example." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/anti_revolution/brig/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Brig [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] for 20 minutes to set an example." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/brig/check_completion() + if(already_completed) + return 1 + + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + if(target.is_brigged(10 * 60 * 10)) + already_completed = 1 + return 1 + return 0 + return 0 + +/datum/objective/anti_revolution/demote/find_target() + ..() + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/demote/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + var/datum/gender/T = gender_datums[target.current.get_visible_gender()] + explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has been classified as harmful to [using_map.company_name]'s goals. Demote [T.him] to assistant." + else + explanation_text = "Free Objective" + return target + +/datum/objective/anti_revolution/demote/check_completion() + if(target && target.current && istype(target,/mob/living/carbon/human)) + var/obj/item/weapon/card/id/I = target.current:wear_id + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/P = I + I = P.id + + if(!istype(I)) return 1 + + if(I.assignment == USELESS_JOB) //VOREStation Edit - Visitor not Assistant + return 1 + else + return 0 + return 1 + +//I want braaaainssss +/datum/objective/debrain/find_target() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/debrain/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name] the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/debrain/check_completion() + if(!target)//If it's a free objective. + return 1 + if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead. + return 0 + if( !target.current || !isbrain(target.current) ) + return 0 + var/atom/A = target.current + while(A.loc) //check to see if the brainmob is on our person + A = A.loc + if(A == owner.current) + return 1 + return 0 + + +//The opposite of killing a dude. +/datum/objective/protect/find_target() + ..() + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/protect/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/protect/check_completion() + if(!target) //If it's a free objective. + return 1 + if(target.current) + if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current)) + return 0 + return 1 + return 0 + + +/datum/objective/hijack + explanation_text = "Hijack the emergency shuttle by escaping alone." + +/datum/objective/hijack/check_completion() + if(!owner.current || owner.current.stat) + return 0 + if(!emergency_shuttle.returned()) + return 0 + if(issilicon(owner.current)) + return 0 + var/area/shuttle = locate(/area/shuttle/escape/centcom) + var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai) + for(var/mob/living/player in player_list) + if(player.type in protected_mobs) continue + if (player.mind && (player.mind != owner)) + if(player.stat != DEAD) //they're not dead! + if(get_turf(player) in shuttle) + return 0 + return 1 + + +/datum/objective/block + explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive." + + +/datum/objective/block/check_completion() + if(!istype(owner.current, /mob/living/silicon)) + return 0 + if(!emergency_shuttle.returned()) + return 0 + if(!owner.current) + return 0 + var/area/shuttle = locate(/area/shuttle/escape/centcom) + var/protected_mobs[] = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) + for(var/mob/living/player in player_list) + if(player.type in protected_mobs) continue + if (player.mind) + if (player.stat != 2) + if (get_turf(player) in shuttle) + return 0 + return 1 + +/datum/objective/silence + explanation_text = "Do not allow anyone to escape the station. Only allow the shuttle to be called when everyone is dead and your story is the only one left." + +/datum/objective/silence/check_completion() + if(!emergency_shuttle.returned()) + return 0 + + for(var/mob/living/player in player_list) + if(player == owner.current) + continue + if(player.mind) + if(player.stat != DEAD) + var/turf/T = get_turf(player) + if(!T) continue + switch(T.loc.type) + if(/area/shuttle/escape/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) + return 0 + return 1 + + +/datum/objective/escape + explanation_text = "Escape on the shuttle or an escape pod alive and free." + + +/datum/objective/escape/check_completion() + if(issilicon(owner.current)) + return 0 + if(isbrain(owner.current)) + return 0 + if(!emergency_shuttle.returned()) + return 0 + if(!owner.current || owner.current.stat ==2) + return 0 + var/turf/location = get_turf(owner.current.loc) + if(!location) + return 0 + + if(istype(location, /turf/simulated/shuttle/floor4)) // Fails traitors if they are in the shuttle brig -- Polymorph + if(istype(owner.current, /mob/living/carbon)) + var/mob/living/carbon/C = owner.current + if (!C.handcuffed) + return 1 + return 0 + + var/area/check_area = location.loc + if(istype(check_area, /area/shuttle/escape/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod1/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod2/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod3/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod5/centcom)) + return 1 + else + return 0 + + + +/datum/objective/survive + explanation_text = "Stay alive until the end." + +/datum/objective/survive/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) + return 0 //Brains no longer win survive objectives. --NEO + if(issilicon(owner.current) && owner.current != owner.original) + return 0 + return 1 + +// Similar to the anti-rev objective, but for traitors +/datum/objective/brig + var/already_completed = 0 + +/datum/objective/brig/find_target() + ..() + if(target && target.current) + explanation_text = "Have [target.current.real_name], the [target.assigned_role] brigged for 10 minutes." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/brig/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Have [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] brigged for 10 minutes." + else + explanation_text = "Free Objective" + return target + +/datum/objective/brig/check_completion() + if(already_completed) + return 1 + + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + // Make the actual required time a bit shorter than the official time + if(target.is_brigged(10 * 60 * 5)) + already_completed = 1 + return 1 + return 0 + return 0 + +// Harm a crew member, making an example of them +/datum/objective/harm + var/already_completed = 0 + +/datum/objective/harm/find_target() + ..() + if(target && target.current) + explanation_text = "Make an example of [target.current.real_name], the [target.assigned_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/harm/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Make an example of [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]. Break one of their bones, detach one of their limbs or disfigure their face. Make sure they're alive when you do it." + else + explanation_text = "Free Objective" + return target + +/datum/objective/harm/check_completion() + if(already_completed) + return 1 + + if(target && target.current && istype(target.current, /mob/living/carbon/human)) + if(target.current.stat == DEAD) + return 0 + + var/mob/living/carbon/human/H = target.current + for(var/obj/item/organ/external/E in H.organs) + if(E.status & ORGAN_BROKEN) + return 1 + for(var/limb_tag in H.species.has_limbs) //todo check prefs for robotic limbs and amputations. + var/list/organ_data = H.species.has_limbs[limb_tag] + var/limb_type = organ_data["path"] + var/found + for(var/obj/item/organ/external/E in H.organs) + if(limb_type == E.type) + found = 1 + break + if(!found) + return 1 + + var/obj/item/organ/external/head/head = H.get_organ(BP_HEAD) + if(head.disfigured) + return 1 + return 0 + + +/datum/objective/nuclear + explanation_text = "Destroy the station with a nuclear device." + + + +/datum/objective/steal + var/obj/item/steal_target + var/target_name + + var/global/possible_items[] = list( + "the Site Manager's antique laser gun" = /obj/item/weapon/gun/energy/captain, + "a hand teleporter" = /obj/item/weapon/hand_tele, + "an RCD" = /obj/item/weapon/rcd, + "a jetpack" = /obj/item/weapon/tank/jetpack, + "a site manager's jumpsuit" = /obj/item/clothing/under/rank/captain, + "a functional AI" = /obj/item/device/aicard, + "a pair of magboots" = /obj/item/clothing/shoes/magboots, + "the station blueprints" = /obj/item/areaeditor/blueprints, + "a nasa voidsuit" = /obj/item/clothing/suit/space/void, + "28 moles of phoron (full tank)" = /obj/item/weapon/tank, + "a sample of slime extract" = /obj/item/slime_extract, + "a piece of corgi meat" = /obj/item/weapon/reagent_containers/food/snacks/meat/corgi, + "a research director's jumpsuit" = /obj/item/clothing/under/rank/research_director, + "a chief engineer's jumpsuit" = /obj/item/clothing/under/rank/chief_engineer, + "a chief medical officer's jumpsuit" = /obj/item/clothing/under/rank/chief_medical_officer, + "a head of security's jumpsuit" = /obj/item/clothing/under/rank/head_of_security, + "a head of personnel's jumpsuit" = /obj/item/clothing/under/rank/head_of_personnel, + "the hypospray" = /obj/item/weapon/reagent_containers/hypospray/vial, + "the site manager's pinpointer" = /obj/item/weapon/pinpointer, + "an ablative armor vest" = /obj/item/clothing/suit/armor/laserproof, + ) + + var/global/possible_items_special[] = list( + /*"nuclear authentication disk" = /obj/item/weapon/disk/nuclear,*///Broken with the change to nuke disk making it respawn on z level change. + "nuclear gun" = /obj/item/weapon/gun/energy/gun/nuclear, + "diamond drill" = /obj/item/weapon/pickaxe/diamonddrill, + "bag of holding" = /obj/item/weapon/storage/backpack/holding, + "hyper-capacity cell" = /obj/item/weapon/cell/hyper, + "10 diamonds" = /obj/item/stack/material/diamond, + "50 gold bars" = /obj/item/stack/material/gold, + "25 refined uranium bars" = /obj/item/stack/material/uranium, + ) + + +/datum/objective/steal/proc/set_target(item_name) + target_name = item_name + steal_target = possible_items[target_name] + if (!steal_target ) + steal_target = possible_items_special[target_name] + explanation_text = "Steal [target_name]." + return steal_target + + +/datum/objective/steal/find_target() + return set_target(pick(possible_items)) + + +/datum/objective/steal/proc/select_target() + var/list/possible_items_all = possible_items+possible_items_special+"custom" + var/new_target = tgui_input_list(usr, "Select target:", "Objective target", possible_items_all) + if (!new_target) return + if (new_target == "custom") + var/obj/item/custom_target = tgui_input_list(usr, "Select type:", "Type", typesof(/obj/item)) + if (!custom_target) return + var/tmp_obj = new custom_target + var/custom_name = tmp_obj:name + qdel(tmp_obj) + custom_name = sanitize(tgui_input_text(usr, "Enter target name:", "Objective target", custom_name)) + if (!custom_name) return + target_name = custom_name + steal_target = custom_target + explanation_text = "Steal [target_name]." + else + set_target(new_target) + return steal_target + +/datum/objective/steal/check_completion() + if(!steal_target || !owner.current) return 0 + if(!isliving(owner.current)) return 0 + var/list/all_items = owner.current.get_contents() + switch (target_name) + if("28 moles of phoron (full tank)","10 diamonds","50 gold bars","25 refined uranium bars") + var/target_amount = text2num(target_name)//Non-numbers are ignored. + var/found_amount = 0.0//Always starts as zero. + + for(var/obj/item/I in all_items) //Check for phoron tanks + if(istype(I, steal_target)) + found_amount += (target_name=="28 moles of phoron (full tank)" ? (I:air_contents:gas["phoron"]) : (I:amount)) + return found_amount>=target_amount + + if("50 coins (in bag)") + var/obj/item/weapon/moneybag/B = locate() in all_items + + if(B) + var/target = text2num(target_name) + var/found_amount = 0.0 + for(var/obj/item/weapon/coin/C in B) + found_amount++ + return found_amount>=target + + if("a functional AI") + + for(var/obj/item/device/aicard/C in all_items) //Check for ai card + for(var/mob/living/silicon/ai/M in C) + if(istype(M, /mob/living/silicon/ai) && M.stat != 2) //See if any AI's are alive inside that card. + return 1 + + for(var/mob/living/silicon/ai/ai in mob_list) + var/turf/T = get_turf(ai) + if(istype(T)) + var/area/check_area = get_area(ai) + if(istype(check_area, /area/shuttle/escape/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod1/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod2/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod3/centcom)) + return 1 + if(istype(check_area, /area/shuttle/escape_pod5/centcom)) + return 1 + else + + for(var/obj/I in all_items) //Check for items + if(istype(I, steal_target)) + return 1 + return 0 + + + +/datum/objective/download/proc/gen_amount_goal() + target_amount = rand(10,20) + explanation_text = "Download [target_amount] research levels." + return target_amount + + +/datum/objective/download/check_completion() + if(!ishuman(owner.current)) + return 0 + if(!owner.current || owner.current.stat == 2) + return 0 + + var/current_amount + var/obj/item/weapon/rig/S + if(istype(owner.current,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner.current + S = H.back + + if(!istype(S) || !S.installed_modules || !S.installed_modules.len) + return 0 + + var/obj/item/rig_module/datajack/stolen_data = locate() in S.installed_modules + if(!istype(stolen_data)) + return 0 + + for(var/datum/tech/current_data in stolen_data.stored_research) + if(current_data.level > 1) + current_amount += (current_data.level-1) + + return (current_amount= target_amount)) + return 1 + else + return 0 + +/datum/objective/vore/check_completion() + if(owner && owner.vore_prey_eaten >= target_amount) + return 1 + else + return 0 + +// Heist objectives. +/datum/objective/heist/proc/choose_target() + return + +/datum/objective/heist/kidnap/choose_target() + var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Engineer") + var/list/possible_targets = list() + var/list/priority_targets = list() + + for(var/datum/mind/possible_target in ticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && (!possible_target.special_role)) + possible_targets += possible_target + for(var/role in roles) + if(possible_target.assigned_role == role) + priority_targets += possible_target + continue + + if(priority_targets.len > 0) + target = pick(priority_targets) + else if(possible_targets.len > 0) + target = pick(possible_targets) + + if(target && target.current) + explanation_text = "We can get a good price for [target.current.real_name], the [target.assigned_role]. Take them alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/heist/kidnap/check_completion() + if(target && target.current) + if (target.current.stat == 2) + return 0 // They're dead. Fail. + //if (!target.current.restrained()) + // return 0 // They're loose. Close but no cigar. + + var/area/skipjack_station/start/A = locate() + for(var/mob/living/carbon/human/M in A) + if(target.current == M) + return 1 //They're restrained on the shuttle. Success. + else + return 0 + +/datum/objective/heist/loot/choose_target() + var/loot = "an object" + switch(rand(1,8)) + if(1) + target = /obj/structure/particle_accelerator + target_amount = 6 + loot = "a complete particle accelerator" + if(2) + target = /obj/machinery/the_singularitygen + target_amount = 1 + loot = "a gravitational generator" + if(3) + target = /obj/machinery/power/emitter + target_amount = 4 + loot = "four emitters" + if(4) + target = /obj/machinery/nuclearbomb + target_amount = 1 + loot = "a nuclear bomb" + if(5) + target = /obj/item/weapon/gun + target_amount = 6 + loot = "six guns" + if(6) + target = /obj/item/weapon/gun/energy + target_amount = 4 + loot = "four energy guns" + if(7) + target = /obj/item/weapon/gun/energy/laser + target_amount = 2 + loot = "two laser guns" + if(8) + target = /obj/item/weapon/gun/energy/ionrifle + target_amount = 1 + loot = "an ion gun" + + explanation_text = "It's a buyer's market out here. Steal [loot] for resale." + + +/datum/objective/heist/loot/check_completion() + var/total_amount = 0 + + for(var/obj/O in locate(/area/skipjack_station/start)) + if(istype(O,target)) total_amount++ + for(var/obj/I in O.contents) + if(istype(I,target)) total_amount++ + if(total_amount >= target_amount) return 1 + + for(var/datum/mind/raider in raiders.current_antagonists) + if(raider.current) + for(var/obj/O in raider.current.get_contents()) + if(istype(O,target)) total_amount++ + if(total_amount >= target_amount) return 1 + + return 0 + +/datum/objective/heist/salvage/choose_target() + switch(rand(1,8)) + if(1) + target = MAT_STEEL + target_amount = 300 + if(2) + target = MAT_GLASS + target_amount = 200 + if(3) + target = MAT_PLASTEEL + target_amount = 100 + if(4) + target = MAT_PHORON + target_amount = 100 + if(5) + target = MAT_SILVER + target_amount = 50 + if(6) + target = MAT_GOLD + target_amount = 20 + if(7) + target = MAT_URANIUM + target_amount = 20 + if(8) + target = MAT_DIAMOND + target_amount = 20 + + explanation_text = "Ransack the station and escape with [target_amount] [target]." + +/datum/objective/heist/salvage/check_completion() + + var/total_amount = 0 + + for(var/obj/item/O in locate(/area/skipjack_station/start)) + + var/obj/item/stack/material/S + if(istype(O,/obj/item/stack/material)) + if(O.name == target) + S = O + total_amount += S.get_amount() + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/material)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + for(var/datum/mind/raider in raiders.current_antagonists) + if(raider.current) + for(var/obj/item/O in raider.current.get_contents()) + if(istype(O,/obj/item/stack/material)) + if(O.name == target) + var/obj/item/stack/material/S = O + total_amount += S.get_amount() + + if(total_amount >= target_amount) return 1 + return 0 + + +/datum/objective/heist/preserve_crew + explanation_text = "Do not leave anyone behind, alive or dead." + +/datum/objective/heist/preserve_crew/check_completion() + if(raiders && raiders.is_raider_crew_safe()) return 1 + return 0 + +//Borer objective(s). +/datum/objective/borer_survive + explanation_text = "Survive in a host until the end of the round." + +/datum/objective/borer_survive/check_completion() + if(owner) + var/mob/living/simple_mob/animal/borer/B = owner + if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1 + return 0 + +/datum/objective/borer_reproduce + explanation_text = "Reproduce at least once." + +/datum/objective/borer_reproduce/check_completion() + if(owner && owner.current) + var/mob/living/simple_mob/animal/borer/B = owner.current + if(istype(B) && B.has_reproduced) return 1 + return 0 + +/datum/objective/ninja_highlander + explanation_text = "You aspire to be a Grand Master of the Spider Clan. Kill all of your fellow acolytes." + +/datum/objective/ninja_highlander/check_completion() + if(owner) + for(var/datum/mind/ninja in get_antags("ninja")) + if(ninja != owner) + if(ninja.current.stat < 2) return 0 + return 1 + return 0 + +/datum/objective/cult/survive + explanation_text = "Our knowledge must live on." + target_amount = 5 + +/datum/objective/cult/survive/New() + ..() + explanation_text = "Our knowledge must live on. Make sure at least [target_amount] acolytes escape on the shuttle to spread their work on an another station." + +/datum/objective/cult/survive/check_completion() + var/acolytes_survived = 0 + if(!cult) + return 0 + for(var/datum/mind/cult_mind in cult.current_antagonists) + if (cult_mind.current && cult_mind.current.stat!=2) + var/area/A = get_area(cult_mind.current ) + if ( is_type_in_list(A, centcom_areas)) + acolytes_survived++ + if(acolytes_survived >= target_amount) + return 0 + else + return 1 + +/datum/objective/cult/eldergod + explanation_text = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it. The convert rune is join blood self." + +/datum/objective/cult/eldergod/check_completion() + return (locate(/obj/singularity/narsie/large) in machines) + +/datum/objective/cult/sacrifice + explanation_text = "Conduct a ritual sacrifice for the glory of Nar-Sie." + +/datum/objective/cult/sacrifice/find_target() + var/list/possible_targets = list() + if(!possible_targets.len) + for(var/mob/living/carbon/human/player in player_list) + if(player.mind && !(player.mind in cult)) + possible_targets += player.mind + if(possible_targets.len > 0) + target = pick(possible_targets) + if(target) explanation_text = "Sacrifice [target.name], the [target.assigned_role]. You will need the sacrifice rune (Hell blood join) and three acolytes to do so." + +/datum/objective/cult/sacrifice/check_completion() + return (target && cult && !cult.sacrificed.Find(target)) + +/datum/objective/rev/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate, capture or convert [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/rev/find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "Assassinate, capture or convert [target.current.real_name], the [!role_type ? target.assigned_role : target.special_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/rev/check_completion() + var/rval = 1 + if(target && target.current) + var/mob/living/carbon/human/H = target.current + if(!istype(H)) + return 1 + if(H.stat == DEAD || H.restrained()) + return 1 + // Check if they're converted + if(target in revs.current_antagonists) + return 1 + var/turf/T = get_turf(H) + if(T && isNotStationLevel(T.z)) //If they leave the station they count as dead for this + rval = 2 + return 0 + return rval + diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 54f75a0032f..afa1d219ba9 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,12 +1,12 @@ -/datum/game_mode/revolution - name = "Revolution" - config_tag = "revolution" - round_description = "Some crewmembers are attempting to start a revolution!" - extended_round_description = "Revolutionaries - Remove the heads of staff from power. Convert other crewmembers to your cause using the 'Convert Bourgeoise' verb. Protect your leaders." - required_players = 12 //should be enough for a decent manifest, hopefully - required_players_secret = 12 //pretty sure rev doesn't even appear in secret - required_enemies = 3 - auto_recall_shuttle = 0 //un-wanted on polaris - end_on_antag_death = 0 - antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST) - require_all_templates = 1 +/datum/game_mode/revolution + name = "Revolution" + config_tag = "revolution" + round_description = "Some crewmembers are attempting to start a revolution!" + extended_round_description = "Revolutionaries - Remove the heads of staff from power. Convert other crewmembers to your cause using the 'Convert Bourgeoise' verb. Protect your leaders." + required_players = 12 //should be enough for a decent manifest, hopefully + required_players_secret = 12 //pretty sure rev doesn't even appear in secret + required_enemies = 3 + auto_recall_shuttle = 0 //un-wanted on polaris + end_on_antag_death = 0 + antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST) + require_all_templates = 1 diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index b95ddde1ab9..08b6b3d5dc0 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -1,151 +1,151 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -var/hsboxspawn = 1 -var/list - hrefs = list( - "hsbsuit" = "Suit Up (Space Travel Gear)", - "hsbmetal" = "Spawn 50 Metal", - "hsbglass" = "Spawn 50 Glass", - "hsbairlock" = "Spawn Airlock", - "hsbregulator" = "Spawn Air Regulator", - "hsbfilter" = "Spawn Air Filter", - "hsbcanister" = "Spawn Canister", - "hsbfueltank" = "Spawn Welding Fuel Tank", - "hsbwater tank" = "Spawn Water Tank", - "hsbtoolbox" = "Spawn Toolbox", - "hsbmedkit" = "Spawn Medical Kit") - -mob - var/datum/hSB/sandbox = null - proc - CanBuild() - if(master_mode == "sandbox") - sandbox = new/datum/hSB - sandbox.owner = src.ckey - if(src.client.holder) - sandbox.admin = 1 - verbs += new/mob/proc/sandbox_panel - sandbox_panel() - if(sandbox) - sandbox.update() - -/datum/hSB - var/owner = null - var/admin = 0 - proc - update() - var/hsbpanel = "
    h_Sandbox Panel

    " - if(admin) - hsbpanel += "Administration Tools:
    " - hsbpanel += "- Toggle Object Spawning

    " - hsbpanel += "Regular Tools:
    " - for(var/T in hrefs) - hsbpanel += "- [hrefs[T]]
    " - if(hsboxspawn) - hsbpanel += "- Spawn Object

    " - usr << browse(hsbpanel, "window=hsbpanel") - Topic(href, href_list) - if(!(src.owner == usr.ckey)) return - if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. - if(href_list["hsb"]) - switch(href_list["hsb"]) - if("hsbtobj") - if(!admin) return - if(hsboxspawn) - to_world("Sandbox: [usr.key] has disabled object spawning!") - hsboxspawn = 0 - return - if(!hsboxspawn) - to_world("Sandbox: [usr.key] has enabled object spawning!") - hsboxspawn = 1 - return - if("hsbsuit") - var/mob/living/carbon/human/P = usr - if(P.wear_suit) - P.wear_suit.loc = P.loc - P.wear_suit.reset_plane_and_layer() - P.wear_suit = null - P.wear_suit = new/obj/item/clothing/suit/space(P) - P.wear_suit.hud_layerise() - if(P.head) - P.head.loc = P.loc - P.head.reset_plane_and_layer() - P.head = null - P.head = new/obj/item/clothing/head/helmet/space(P) - P.head.hud_layerise() - if(P.wear_mask) - P.wear_mask.loc = P.loc - P.wear_mask.reset_plane_and_layer() - P.wear_mask = null - P.wear_mask = new/obj/item/clothing/mask/gas(P) - P.wear_mask.hud_layerise() - if(P.back) - P.back.loc = P.loc - P.back.reset_plane_and_layer() - P.back = null - P.back = new/obj/item/weapon/tank/jetpack(P) - P.back.hud_layerise() - P.internal = P.back - if("hsbmetal") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal - hsb.amount = 50 - hsb.loc = usr.loc - if("hsbglass") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass - hsb.amount = 50 - hsb.loc = usr.loc - if("hsbairlock") - var/obj/machinery/door/hsb = new/obj/machinery/door/airlock - - //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups - hsb.req_access = list() - var/accesses = get_all_accesses() - for(var/A in accesses) - if(tgui_alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", list("Yes", "No")) == "Yes") - LAZYADD(hsb.req_access, A) - - hsb.loc = usr.loc - to_chat(usr, "Sandbox: Created an airlock.") - if("hsbcanister") - var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister) - var/hsbcanister = tgui_input_list(usr, "Choose a canister to spawn:", "Sandbox", hsbcanisters) - if(hsbcanister) - new hsbcanister(usr.loc) - if("hsbfueltank") - //var/obj/hsb = new/obj/weldfueltank - //hsb.loc = usr.loc - if("hsbwatertank") - //var/obj/hsb = new/obj/watertank - //hsb.loc = usr.loc - if("hsbtoolbox") - var/obj/item/weapon/storage/hsb = new/obj/item/weapon/storage/toolbox/mechanical - for(var/obj/item/device/radio/T in hsb) - qdel(T) - new/obj/item/weapon/tool/crowbar (hsb) - hsb.loc = usr.loc - if("hsbmedkit") - var/obj/item/weapon/storage/firstaid/hsb = new/obj/item/weapon/storage/firstaid/regular - hsb.loc = usr.loc - if("hsbobj") - if(!hsboxspawn) return - - var/list/selectable = list() - for(var/O in typesof(/obj/item/)) - //Note, these istypes don't work - if(istype(O, /obj/item/weapon/gun)) - continue - if(istype(O, /obj/item/assembly)) - continue - if(istype(O, /obj/item/device/camera)) - continue - if(istype(O, /obj/item/weapon/dummy)) - continue - if(istype(O, /obj/item/weapon/melee/energy/sword)) - continue - if(istype(O, /obj/structure)) - continue - selectable += O - - var/hsbitem = tgui_input_list(usr, "Choose an object to spawn:", "Sandbox", selectable) - if(hsbitem) - new hsbitem(usr.loc) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +var/hsboxspawn = 1 +var/list + hrefs = list( + "hsbsuit" = "Suit Up (Space Travel Gear)", + "hsbmetal" = "Spawn 50 Metal", + "hsbglass" = "Spawn 50 Glass", + "hsbairlock" = "Spawn Airlock", + "hsbregulator" = "Spawn Air Regulator", + "hsbfilter" = "Spawn Air Filter", + "hsbcanister" = "Spawn Canister", + "hsbfueltank" = "Spawn Welding Fuel Tank", + "hsbwater tank" = "Spawn Water Tank", + "hsbtoolbox" = "Spawn Toolbox", + "hsbmedkit" = "Spawn Medical Kit") + +mob + var/datum/hSB/sandbox = null + proc + CanBuild() + if(master_mode == "sandbox") + sandbox = new/datum/hSB + sandbox.owner = src.ckey + if(src.client.holder) + sandbox.admin = 1 + verbs += new/mob/proc/sandbox_panel + sandbox_panel() + if(sandbox) + sandbox.update() + +/datum/hSB + var/owner = null + var/admin = 0 + proc + update() + var/hsbpanel = "
    h_Sandbox Panel

    " + if(admin) + hsbpanel += "Administration Tools:
    " + hsbpanel += "- Toggle Object Spawning

    " + hsbpanel += "Regular Tools:
    " + for(var/T in hrefs) + hsbpanel += "- [hrefs[T]]
    " + if(hsboxspawn) + hsbpanel += "- Spawn Object

    " + usr << browse(hsbpanel, "window=hsbpanel") + Topic(href, href_list) + if(!(src.owner == usr.ckey)) return + if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. + if(href_list["hsb"]) + switch(href_list["hsb"]) + if("hsbtobj") + if(!admin) return + if(hsboxspawn) + to_world("Sandbox: [usr.key] has disabled object spawning!") + hsboxspawn = 0 + return + if(!hsboxspawn) + to_world("Sandbox: [usr.key] has enabled object spawning!") + hsboxspawn = 1 + return + if("hsbsuit") + var/mob/living/carbon/human/P = usr + if(P.wear_suit) + P.wear_suit.loc = P.loc + P.wear_suit.reset_plane_and_layer() + P.wear_suit = null + P.wear_suit = new/obj/item/clothing/suit/space(P) + P.wear_suit.hud_layerise() + if(P.head) + P.head.loc = P.loc + P.head.reset_plane_and_layer() + P.head = null + P.head = new/obj/item/clothing/head/helmet/space(P) + P.head.hud_layerise() + if(P.wear_mask) + P.wear_mask.loc = P.loc + P.wear_mask.reset_plane_and_layer() + P.wear_mask = null + P.wear_mask = new/obj/item/clothing/mask/gas(P) + P.wear_mask.hud_layerise() + if(P.back) + P.back.loc = P.loc + P.back.reset_plane_and_layer() + P.back = null + P.back = new/obj/item/weapon/tank/jetpack(P) + P.back.hud_layerise() + P.internal = P.back + if("hsbmetal") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbglass") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbairlock") + var/obj/machinery/door/hsb = new/obj/machinery/door/airlock + + //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups + hsb.req_access = list() + var/accesses = get_all_accesses() + for(var/A in accesses) + if(tgui_alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", list("Yes", "No")) == "Yes") + LAZYADD(hsb.req_access, A) + + hsb.loc = usr.loc + to_chat(usr, "Sandbox: Created an airlock.") + if("hsbcanister") + var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister) + var/hsbcanister = tgui_input_list(usr, "Choose a canister to spawn:", "Sandbox", hsbcanisters) + if(hsbcanister) + new hsbcanister(usr.loc) + if("hsbfueltank") + //var/obj/hsb = new/obj/weldfueltank + //hsb.loc = usr.loc + if("hsbwatertank") + //var/obj/hsb = new/obj/watertank + //hsb.loc = usr.loc + if("hsbtoolbox") + var/obj/item/weapon/storage/hsb = new/obj/item/weapon/storage/toolbox/mechanical + for(var/obj/item/device/radio/T in hsb) + qdel(T) + new/obj/item/weapon/tool/crowbar (hsb) + hsb.loc = usr.loc + if("hsbmedkit") + var/obj/item/weapon/storage/firstaid/hsb = new/obj/item/weapon/storage/firstaid/regular + hsb.loc = usr.loc + if("hsbobj") + if(!hsboxspawn) return + + var/list/selectable = list() + for(var/O in typesof(/obj/item/)) + //Note, these istypes don't work + if(istype(O, /obj/item/weapon/gun)) + continue + if(istype(O, /obj/item/assembly)) + continue + if(istype(O, /obj/item/device/camera)) + continue + if(istype(O, /obj/item/weapon/dummy)) + continue + if(istype(O, /obj/item/weapon/melee/energy/sword)) + continue + if(istype(O, /obj/structure)) + continue + selectable += O + + var/hsbitem = tgui_input_list(usr, "Choose an object to spawn:", "Sandbox", selectable) + if(hsbitem) + new hsbitem(usr.loc) diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm index a9641dabd28..39e9249d240 100644 --- a/code/game/gamemodes/sandbox/sandbox.dm +++ b/code/game/gamemodes/sandbox/sandbox.dm @@ -1,17 +1,17 @@ -/datum/game_mode/sandbox - name = "Sandbox" - config_tag = "sandbox" - required_players = 0 - votable = 0 - round_description = "Build your own station!" - extended_round_description = "You can use the sandbox-panel command to access build options." - -/datum/game_mode/sandbox/pre_setup() - for(var/mob/M in player_list) - M.CanBuild() - return 1 - -/datum/game_mode/sandbox/post_setup() - ..() - if(emergency_shuttle) - emergency_shuttle.auto_recall = 1 +/datum/game_mode/sandbox + name = "Sandbox" + config_tag = "sandbox" + required_players = 0 + votable = 0 + round_description = "Build your own station!" + extended_round_description = "You can use the sandbox-panel command to access build options." + +/datum/game_mode/sandbox/pre_setup() + for(var/mob/M in player_list) + M.CanBuild() + return 1 + +/datum/game_mode/sandbox/post_setup() + ..() + if(emergency_shuttle) + emergency_shuttle.auto_recall = 1 diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 3c029d52980..b2cb6a6a6c4 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -1,83 +1,83 @@ -///////////////////////// -// (mostly) DNA2 SETUP -///////////////////////// - -// Randomize block, assign a reference name, and optionally define difficulty (by making activation zone smaller or bigger) -// The name is used on /vg/ for species with predefined genetic traits, -// and for the DNA panel in the player panel. -/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS) - if(blocksLeft.len==0) - warning("[name]: No more blocks left to assign!") - return 0 - var/assigned = pick(blocksLeft) - blocksLeft.Remove(assigned) - assigned_blocks[assigned]=name - dna_activity_bounds[assigned]=activity_bounds - //testing("[name] assigned to block #[assigned].") - return assigned - -/proc/setupgenetics() - - if (prob(50)) - // Currently unused. Will revisit. - N3X - BLOCKADD = rand(-300,300) - if (prob(75)) - DIFFMUT = rand(0,20) - - var/list/numsToAssign=new() - for(var/i=1;i\ + " + span_green("Recharge: 70/s") + "
    \ + " + span_red("Instability Modifier: 90%") + "
    \ Energy Cost Modifier: 100%
    \ Spell Power: 100%" cost = 100 @@ -27,11 +27,11 @@ desc = "This core has very large capacitors, however it also has a subpar fractal reactor. The user is recommended to \ purchase one or more energy-generating Functions as well if using this core. The intense weight of the core unfortunately can \ cause the wear to move slightly slower, and the closeness of the capacitors causes a slight increase in incoming instability.
    \ - Capacity: 20k
    \ - Recharge: 25/s
    \ - Instability Modifier: 100%
    \ + " + span_green("Capacity: 20k") + "
    \ + " + span_red("Recharge: 25/s") + "
    \ + " + span_red("Instability Modifier: 100%") + "
    \ Energy Cost Modifier: 100%
    \ - Spell Power: 140%" + " + span_green("Spell Power: 140%") + "" cost = 100 obj_path = /obj/item/weapon/technomancer_core/bulky @@ -40,11 +40,11 @@ desc = "This core feeds off unstable energies around the user in addition to a fractal reactor. This means that it performs \ better as the user has more instability, which could prove dangerous to the inexperienced or unprepared. The rate of recharging \ increases as the user accumulates more instability, eventually exceeding even the rapid core in regen speed, at a huge risk.
    \ - Capacity: 13k
    \ - Recharge: 35/s to 110/s+
    \ - Instability Modifier: 130%
    \ - Energy Cost Modifier: 70%
    \ - Spell Power: 110%" + " + span_green("Capacity: 13k") + "
    \ + " + span_green("Recharge: 35/s to 110/s+") + "
    \ + " + span_red("Instability Modifier: 130%") + "
    \ + " + span_green("Energy Cost Modifier: 70%") + "
    \ + " + span_green("Spell Power: 110%") + "" cost = 100 obj_path = /obj/item/weapon/technomancer_core/unstable @@ -52,10 +52,10 @@ name = "Recycling Core" desc = "This core is optimized for energy efficency, being able to sometimes recover energy that would have been lost with other \ cores. Each time energy is spent, there is a 30% chance of recovering half of what was spent.
    \ - Capacity: 12k
    \ - Recharge: 40/s
    \ - Instability Modifier: 60%
    \ - Energy Cost Modifier: 80%
    \ + " + span_green("Capacity: 12k") + "
    \ + " + span_red("Recharge: 40/s") + "
    \ + " + span_green("Instability Modifier: 60%") + "
    \ + " + span_green("Energy Cost Modifier: 80%") + "
    \ Spell Power: 100%" cost = 100 obj_path = /obj/item/weapon/technomancer_core/recycling @@ -65,11 +65,11 @@ desc = "A unique type of core, this one sacrifices other characteristics in order to optimize it for the purposes teleporting \ entities from vast distances, and keeping them there. Wearers of this core can maintain up to 40 summons at once, and the energy \ demand for maintaining summons is severely reduced.
    \ - Capacity: 8k
    \ - Recharge: 35/s
    \ - Instability Modifier: 120%
    \ + " + span_red("Capacity: 8k") + "
    \ + " + span_red("Recharge: 35/s") + "
    \ + " + span_red("Instability Modifier: 120%") + "
    \ Energy Cost Modifier: 100%
    \ - Spell Power: 120%" + " + span_green("Spell Power: 120%") + "" cost = 100 obj_path = /obj/item/weapon/technomancer_core/summoner @@ -77,11 +77,11 @@ name = "Safety Core" desc = "This core is designed so that the wearer suffers almost no instability. It unfortunately comes at a cost of subpar \ ratings for everything else.
    \ - Capacity: 7k
    \ - Recharge: 30/s
    \ - Instability Modifier: 30%
    \ + " + span_red("Capacity: 7k") + "
    \ + " + span_red("Recharge: 30/s") + "
    \ + " + span_green("Instability Modifier: 30%") + "
    \ Energy Cost Modifier: 100%
    \ - Spell Power: 70%" + " + span_red("Spell Power: 70%") + "" cost = 100 obj_path = /obj/item/weapon/technomancer_core/safety @@ -90,11 +90,11 @@ desc = "A core that was created in order to get the most power out of functions. It does this by shoving the most power into \ those functions, so it is the opposite of energy efficent, however the enhancement of functions is second to none for other \ cores.
    \ - Capacity: 15k (effectively 7.5k)
    \ - Recharge: 40/s
    \ - Instability Modifier: 110%
    \ - Energy Cost Modifier: 200%
    \ - Spell Power: 175%" + " + span_red("Capacity: 15k (effectively 7.5k)") + "
    \ + " + span_red("Recharge: 40/s") + "
    \ + " + span_red("Instability Modifier: 110%") + "
    \ + " + span_red("Energy Cost Modifier: 200%") + "
    \ + " + span_green("Spell Power: 175%") + "" cost = 100 obj_path = /obj/item/weapon/technomancer_core/overcharged diff --git a/code/game/gamemodes/technomancer/spells/audible_deception.dm b/code/game/gamemodes/technomancer/spells/audible_deception.dm index ebd8974305f..b89db0c0c46 100644 --- a/code/game/gamemodes/technomancer/spells/audible_deception.dm +++ b/code/game/gamemodes/technomancer/spells/audible_deception.dm @@ -92,4 +92,4 @@ M.Paralyse(4) else M.make_jittery(50) - to_chat(M, "HONK") + to_chat(M, span_red("HONK")) diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm b/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm index c29d9827ec2..7b2ab146a23 100644 --- a/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm +++ b/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm @@ -30,7 +30,6 @@ "BAT" = /mob/living/simple_mob/animal/space/bats, "SPIDER" = /mob/living/simple_mob/animal/giant_spider, "SPIDER HUNTER" = /mob/living/simple_mob/animal/giant_spider/hunter, - "SPIDER NURSE" = /mob/living/simple_mob/animal/giant_spider/nurse, "CARP" = /mob/living/simple_mob/animal/space/carp, "BEAR" = /mob/living/simple_mob/animal/space/bear ) @@ -57,4 +56,4 @@ // Now we hurt their new pal, because being forcefully abducted by teleportation can't be healthy. - summoned.adjustBruteLoss(summoned.getMaxHealth() * 0.3) // Lose 30% of max health on arrival (but could be healed back up). \ No newline at end of file + summoned.adjustBruteLoss(summoned.getMaxHealth() * 0.3) // Lose 30% of max health on arrival (but could be healed back up). diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index b978aca0897..4cf5dc944e0 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -1,28 +1,28 @@ -/datum/game_mode/traitor - name = "traitor" - round_description = "There is a foreign agent or traitor on the station. Do not let the traitor succeed!" - extended_round_description = "The Company's majority control of phoron in the system has marked the \ - station to be a highly valuable target for many competing organizations and individuals. The varied pasts \ - and experiences of your coworkers have left them susceptible to the vices and temptations of humanity. \ - Is the station the safe self-contained workplace you once thought it was, or has it become a playground \ - for the evils of the galaxy? Who can you trust? Watch your front. Watch your sides. Watch your back. \ - The familiar faces that you've passed hundreds of times down the hallways before can be hiding terrible \ - secrets and deceptions. Every corner is a mystery. Every conversation is a lie. You will be facing your \ - friends and family as they try to use your emotions and trust to their advantage, leaving you with nothing \ - but the painful reminder that space is cruel and unforgiving." - config_tag = "traitor" - required_players = 1 - required_players_secret = 1 - required_enemies = 1 - end_on_antag_death = 0 - antag_tags = list(MODE_TRAITOR) - antag_scaling_coeff = 8 - -/datum/game_mode/traitor/auto - name = "autotraitor" - config_tag = "autotraitor" - antag_tags = list(MODE_AUTOTRAITOR) - round_autoantag = 1 - required_players_secret = 0 - required_enemies = 0 - antag_scaling_coeff = 5 +/datum/game_mode/traitor + name = "traitor" + round_description = "There is a foreign agent or traitor on the station. Do not let the traitor succeed!" + extended_round_description = "The Company's majority control of phoron in the system has marked the \ + station to be a highly valuable target for many competing organizations and individuals. The varied pasts \ + and experiences of your coworkers have left them susceptible to the vices and temptations of humanity. \ + Is the station the safe self-contained workplace you once thought it was, or has it become a playground \ + for the evils of the galaxy? Who can you trust? Watch your front. Watch your sides. Watch your back. \ + The familiar faces that you've passed hundreds of times down the hallways before can be hiding terrible \ + secrets and deceptions. Every corner is a mystery. Every conversation is a lie. You will be facing your \ + friends and family as they try to use your emotions and trust to their advantage, leaving you with nothing \ + but the painful reminder that space is cruel and unforgiving." + config_tag = "traitor" + required_players = 1 + required_players_secret = 1 + required_enemies = 1 + end_on_antag_death = 0 + antag_tags = list(MODE_TRAITOR) + antag_scaling_coeff = 8 + +/datum/game_mode/traitor/auto + name = "autotraitor" + config_tag = "autotraitor" + antag_tags = list(MODE_AUTOTRAITOR) + round_autoantag = 1 + required_players_secret = 0 + required_enemies = 0 + antag_scaling_coeff = 5 diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 349f0edb14b..e3dc3dccd0b 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -1,11 +1,11 @@ -/datum/game_mode/wizard - name = "Wizard" - round_description = "There is a SPACE WIZARD on the station. You can't let the magician achieve their objectives!" - extended_round_description = "A powerful entity capable of manipulating the elements around him, most commonly referred to as a 'wizard', has infiltrated the station. They have a wide variety of powers and spells available to them that makes your own simple moral self tremble with fear and excitement. Ultimately, their purpose is unknown. However, it is up to you and your crew to decide if their powers can be used for good or if their arrival foreshadows the destruction of the entire station." - config_tag = "wizard" - votable = 0 - required_players = 1 - required_players_secret = 3 - required_enemies = 1 - end_on_antag_death = 0 - antag_tags = list(MODE_WIZARD) +/datum/game_mode/wizard + name = "Wizard" + round_description = "There is a SPACE WIZARD on the station. You can't let the magician achieve their objectives!" + extended_round_description = "A powerful entity capable of manipulating the elements around him, most commonly referred to as a 'wizard', has infiltrated the station. They have a wide variety of powers and spells available to them that makes your own simple moral self tremble with fear and excitement. Ultimately, their purpose is unknown. However, it is up to you and your crew to decide if their powers can be used for good or if their arrival foreshadows the destruction of the entire station." + config_tag = "wizard" + votable = 0 + required_players = 1 + required_players_secret = 3 + required_enemies = 1 + end_on_antag_death = 0 + antag_tags = list(MODE_WIZARD) diff --git a/code/game/jobs/_access_defs.dm b/code/game/jobs/_access_defs.dm index b76638fe3b7..613a98a6ff9 100644 --- a/code/game/jobs/_access_defs.dm +++ b/code/game/jobs/_access_defs.dm @@ -1,16 +1,16 @@ -#define ACCESS_REGION_NONE -1 -#define ACCESS_REGION_ALL 0 -#define ACCESS_REGION_SECURITY 1 -#define ACCESS_REGION_MEDBAY 2 -#define ACCESS_REGION_RESEARCH 3 -#define ACCESS_REGION_ENGINEERING 4 -#define ACCESS_REGION_COMMAND 5 -#define ACCESS_REGION_GENERAL 6 -#define ACCESS_REGION_SUPPLY 7 - -#define ACCESS_TYPE_NONE 0 -#define ACCESS_TYPE_CENTCOM 1 -#define ACCESS_TYPE_STATION 2 -#define ACCESS_TYPE_SYNDICATE 4 -#define ACCESS_TYPE_PRIVATE 8 -#define ACCESS_TYPE_ALL (ACCESS_TYPE_CENTCOM|ACCESS_TYPE_STATION|ACCESS_TYPE_SYNDICATE|ACCESS_TYPE_PRIVATE) +#define ACCESS_REGION_NONE -1 +#define ACCESS_REGION_ALL 0 +#define ACCESS_REGION_SECURITY 1 +#define ACCESS_REGION_MEDBAY 2 +#define ACCESS_REGION_RESEARCH 3 +#define ACCESS_REGION_ENGINEERING 4 +#define ACCESS_REGION_COMMAND 5 +#define ACCESS_REGION_GENERAL 6 +#define ACCESS_REGION_SUPPLY 7 + +#define ACCESS_TYPE_NONE 0 +#define ACCESS_TYPE_CENTCOM 1 +#define ACCESS_TYPE_STATION 2 +#define ACCESS_TYPE_SYNDICATE 4 +#define ACCESS_TYPE_PRIVATE 8 +#define ACCESS_TYPE_ALL (ACCESS_TYPE_CENTCOM|ACCESS_TYPE_STATION|ACCESS_TYPE_SYNDICATE|ACCESS_TYPE_PRIVATE) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index ddf4b9be334..c779487d7e6 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -1,262 +1,262 @@ -/obj/var/list/req_access -/obj/var/list/req_one_access - -//returns 1 if this mob has sufficient access to use this object -/obj/proc/allowed(mob/M) - return check_access(M?.GetIdCard()) - -/atom/movable/proc/GetAccess() - var/obj/item/weapon/card/id/id = GetIdCard() - return id ? id.GetAccess() : list() - -/obj/proc/GetID() - return null - -/obj/proc/check_access(obj/item/I) - return check_access_list(I ? I.GetAccess() : null) - -/obj/proc/check_access_list(var/list/L) - // We don't require access - if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access)) - return TRUE - - // They passed nothing, but we are something that requires access - if(!LAZYLEN(L)) - return FALSE - - // Run list comparisons - return has_access(req_access, req_one_access, L) - -/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) - // req_access list has priority if set - // Requires at least every access in list - for(var/req in req_access) - if(!(req in accesses)) - return FALSE - - // req_one_access is secondary if set - // Requires at least one access in list - if(LAZYLEN(req_one_access)) - for(var/req in req_one_access) - if(req in accesses) - return TRUE - return FALSE - - return TRUE - -/proc/get_centcom_access(job) - switch(job) - if("VIP Guest") - return list(access_cent_general) - if("Custodian") - return list(access_cent_general, access_cent_living, access_cent_storage) - if("Thunderdome Overseer") - return list(access_cent_general, access_cent_thunder) - if("Intel Officer") - return list(access_cent_general, access_cent_living) - if("Medical Officer") - return list(access_cent_general, access_cent_living, access_cent_medical) - if("Death Commando") - return list(access_cent_general, access_cent_specops, access_cent_living, access_cent_storage) - if("Research Officer") - return list(access_cent_general, access_cent_specops, access_cent_medical, access_cent_teleporter, access_cent_storage) - if("BlackOps Commander") - return list(access_cent_general, access_cent_thunder, access_cent_specops, access_cent_living, access_cent_storage, access_cent_creed) - if("Supreme Commander") - return get_all_centcom_access() - -/var/list/datum/access/priv_all_access_datums -/proc/get_all_access_datums() - if(!priv_all_access_datums) - priv_all_access_datums = init_subtypes(/datum/access) - priv_all_access_datums = dd_sortedObjectList(priv_all_access_datums) - - return priv_all_access_datums - -/var/list/datum/access/priv_all_access_datums_id -/proc/get_all_access_datums_by_id() - if(!priv_all_access_datums_id) - priv_all_access_datums_id = list() - for(var/datum/access/A in get_all_access_datums()) - priv_all_access_datums_id["[A.id]"] = A - - return priv_all_access_datums_id - -/var/list/datum/access/priv_all_access_datums_region -/proc/get_all_access_datums_by_region() - if(!priv_all_access_datums_region) - priv_all_access_datums_region = list() - for(var/datum/access/A in get_all_access_datums()) - if(!priv_all_access_datums_region[A.region]) - priv_all_access_datums_region[A.region] = list() - priv_all_access_datums_region[A.region] += A - - return priv_all_access_datums_region - -/proc/get_access_ids(var/access_types = ACCESS_TYPE_ALL) - var/list/L = new() - for(var/datum/access/A in get_all_access_datums()) - if(A.access_type & access_types) - L += A.id - return L - -/var/list/priv_all_access -/proc/get_all_accesses() - RETURN_TYPE(/list) - if(!priv_all_access) - priv_all_access = get_access_ids() - - return priv_all_access - -/var/list/priv_station_access -/proc/get_all_station_access() - RETURN_TYPE(/list) - if(!priv_station_access) - priv_station_access = get_access_ids(ACCESS_TYPE_STATION) - - return priv_station_access - -/var/list/priv_centcom_access -/proc/get_all_centcom_access() - RETURN_TYPE(/list) - if(!priv_centcom_access) - priv_centcom_access = get_access_ids(ACCESS_TYPE_CENTCOM) - - return priv_centcom_access - -/var/list/priv_syndicate_access -/proc/get_all_syndicate_access() - RETURN_TYPE(/list) - if(!priv_syndicate_access) - priv_syndicate_access = get_access_ids(ACCESS_TYPE_SYNDICATE) - - return priv_syndicate_access - -/var/list/priv_private_access -/proc/get_all_private_access() - RETURN_TYPE(/list) - if(!priv_private_access) - priv_private_access = get_access_ids(ACCESS_TYPE_PRIVATE) - - return priv_syndicate_access - -/var/list/priv_region_access -/proc/get_region_accesses(var/code) - if(code == ACCESS_REGION_ALL) - return get_all_station_access() - - if(!priv_region_access) - priv_region_access = list() - for(var/datum/access/A in get_all_access_datums()) - if(!priv_region_access["[A.region]"]) - priv_region_access["[A.region]"] = list() - priv_region_access["[A.region]"] += A.id - - return priv_region_access["[code]"] - -/proc/get_region_accesses_name(var/code) - switch(code) - if(ACCESS_REGION_ALL) - return "All" - if(ACCESS_REGION_SECURITY) //security - return "Security" - if(ACCESS_REGION_MEDBAY) //medbay - return "Medbay" - if(ACCESS_REGION_RESEARCH) //research - return "Research" - if(ACCESS_REGION_ENGINEERING) //engineering and maintenance - return "Engineering" - if(ACCESS_REGION_COMMAND) //command - return "Command" - if(ACCESS_REGION_GENERAL) //station general - return "Station General" - if(ACCESS_REGION_SUPPLY) //supply - return "Supply" - -/proc/get_access_desc(id) - var/list/AS = get_all_access_datums_by_id() - var/datum/access/A = AS["[id]"] - - return A ? A.desc : "" - -/proc/get_centcom_access_desc(A) - return get_access_desc(A) - -/proc/get_access_by_id(id) - var/list/AS = get_all_access_datums_by_id() - return AS["[id]"] - -/proc/get_all_jobs() - var/list/all_jobs = list() - var/list/all_datums = typesof(/datum/job) - all_datums -= exclude_jobs - var/datum/job/jobdatum - for(var/jobtype in all_datums) - jobdatum = new jobtype - all_jobs.Add(jobdatum.title) - return all_jobs - -/proc/get_all_centcom_jobs() - return list("VIP Guest", - "Custodian", - "Thunderdome Overseer", - "Intel Officer", - "Medical Officer", - "Death Commando", - "Research Officer", - "BlackOps Commander", - "Supreme Commander", - "Emergency Response Team", - "Emergency Response Team Leader") - -/atom/movable/proc/GetIdCard() - return null - -/mob/living/bot/GetIdCard() - return botcard - -/mob/living/carbon/human/GetIdCard() - if(get_active_hand()) - var/obj/item/I = get_active_hand() - var/id = I.GetID() - if(id) - return id - if(wear_id) - var/id = wear_id.GetID() - if(id) - return id - -/mob/living/silicon/GetIdCard() - return idcard - -/proc/FindNameFromID(var/mob/living/carbon/human/H) - ASSERT(istype(H)) - var/obj/item/weapon/card/id/C = H.GetIdCard() - if(C) - return C.registered_name - -/proc/get_all_job_icons() //For all existing HUD icons - return joblist + alt_titles_with_icons + list("Prisoner") - -/obj/proc/GetJobName() //Used in secHUD icon generation - var/obj/item/weapon/card/id/I = GetID() - - if(I) - if(istype(I,/obj/item/weapon/card/id/centcom)) - return "Centcom" - - var/job_icons = get_all_job_icons() - if(I.assignment in job_icons) //Check if the job has a hud icon - return I.assignment - if(I.rank in job_icons) - return I.rank - - var/centcom = get_all_centcom_jobs() - if(I.assignment in centcom) //Return with the NT logo if it is a CentCom job - return "CentCom" - if(I.rank in centcom) - return "CentCom" - else - return - - return "Unknown" //Return unknown if none of the above apply +/obj/var/list/req_access +/obj/var/list/req_one_access + +//returns 1 if this mob has sufficient access to use this object +/obj/proc/allowed(mob/M) + return check_access(M?.GetIdCard()) + +/atom/movable/proc/GetAccess() + var/obj/item/weapon/card/id/id = GetIdCard() + return id ? id.GetAccess() : list() + +/obj/proc/GetID() + return null + +/obj/proc/check_access(obj/item/I) + return check_access_list(I ? I.GetAccess() : null) + +/obj/proc/check_access_list(var/list/L) + // We don't require access + if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access)) + return TRUE + + // They passed nothing, but we are something that requires access + if(!LAZYLEN(L)) + return FALSE + + // Run list comparisons + return has_access(req_access, req_one_access, L) + +/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) + // req_access list has priority if set + // Requires at least every access in list + for(var/req in req_access) + if(!(req in accesses)) + return FALSE + + // req_one_access is secondary if set + // Requires at least one access in list + if(LAZYLEN(req_one_access)) + for(var/req in req_one_access) + if(req in accesses) + return TRUE + return FALSE + + return TRUE + +/proc/get_centcom_access(job) + switch(job) + if("VIP Guest") + return list(access_cent_general) + if("Custodian") + return list(access_cent_general, access_cent_living, access_cent_storage) + if("Thunderdome Overseer") + return list(access_cent_general, access_cent_thunder) + if("Intel Officer") + return list(access_cent_general, access_cent_living) + if("Medical Officer") + return list(access_cent_general, access_cent_living, access_cent_medical) + if("Death Commando") + return list(access_cent_general, access_cent_specops, access_cent_living, access_cent_storage) + if("Research Officer") + return list(access_cent_general, access_cent_specops, access_cent_medical, access_cent_teleporter, access_cent_storage) + if("BlackOps Commander") + return list(access_cent_general, access_cent_thunder, access_cent_specops, access_cent_living, access_cent_storage, access_cent_creed) + if("Supreme Commander") + return get_all_centcom_access() + +/var/list/datum/access/priv_all_access_datums +/proc/get_all_access_datums() + if(!priv_all_access_datums) + priv_all_access_datums = init_subtypes(/datum/access) + priv_all_access_datums = dd_sortedObjectList(priv_all_access_datums) + + return priv_all_access_datums + +/var/list/datum/access/priv_all_access_datums_id +/proc/get_all_access_datums_by_id() + if(!priv_all_access_datums_id) + priv_all_access_datums_id = list() + for(var/datum/access/A in get_all_access_datums()) + priv_all_access_datums_id["[A.id]"] = A + + return priv_all_access_datums_id + +/var/list/datum/access/priv_all_access_datums_region +/proc/get_all_access_datums_by_region() + if(!priv_all_access_datums_region) + priv_all_access_datums_region = list() + for(var/datum/access/A in get_all_access_datums()) + if(!priv_all_access_datums_region[A.region]) + priv_all_access_datums_region[A.region] = list() + priv_all_access_datums_region[A.region] += A + + return priv_all_access_datums_region + +/proc/get_access_ids(var/access_types = ACCESS_TYPE_ALL) + var/list/L = new() + for(var/datum/access/A in get_all_access_datums()) + if(A.access_type & access_types) + L += A.id + return L + +/var/list/priv_all_access +/proc/get_all_accesses() + RETURN_TYPE(/list) + if(!priv_all_access) + priv_all_access = get_access_ids() + + return priv_all_access + +/var/list/priv_station_access +/proc/get_all_station_access() + RETURN_TYPE(/list) + if(!priv_station_access) + priv_station_access = get_access_ids(ACCESS_TYPE_STATION) + + return priv_station_access + +/var/list/priv_centcom_access +/proc/get_all_centcom_access() + RETURN_TYPE(/list) + if(!priv_centcom_access) + priv_centcom_access = get_access_ids(ACCESS_TYPE_CENTCOM) + + return priv_centcom_access + +/var/list/priv_syndicate_access +/proc/get_all_syndicate_access() + RETURN_TYPE(/list) + if(!priv_syndicate_access) + priv_syndicate_access = get_access_ids(ACCESS_TYPE_SYNDICATE) + + return priv_syndicate_access + +/var/list/priv_private_access +/proc/get_all_private_access() + RETURN_TYPE(/list) + if(!priv_private_access) + priv_private_access = get_access_ids(ACCESS_TYPE_PRIVATE) + + return priv_syndicate_access + +/var/list/priv_region_access +/proc/get_region_accesses(var/code) + if(code == ACCESS_REGION_ALL) + return get_all_station_access() + + if(!priv_region_access) + priv_region_access = list() + for(var/datum/access/A in get_all_access_datums()) + if(!priv_region_access["[A.region]"]) + priv_region_access["[A.region]"] = list() + priv_region_access["[A.region]"] += A.id + + return priv_region_access["[code]"] + +/proc/get_region_accesses_name(var/code) + switch(code) + if(ACCESS_REGION_ALL) + return "All" + if(ACCESS_REGION_SECURITY) //security + return "Security" + if(ACCESS_REGION_MEDBAY) //medbay + return "Medbay" + if(ACCESS_REGION_RESEARCH) //research + return "Research" + if(ACCESS_REGION_ENGINEERING) //engineering and maintenance + return "Engineering" + if(ACCESS_REGION_COMMAND) //command + return "Command" + if(ACCESS_REGION_GENERAL) //station general + return "Station General" + if(ACCESS_REGION_SUPPLY) //supply + return "Supply" + +/proc/get_access_desc(id) + var/list/AS = get_all_access_datums_by_id() + var/datum/access/A = AS["[id]"] + + return A ? A.desc : "" + +/proc/get_centcom_access_desc(A) + return get_access_desc(A) + +/proc/get_access_by_id(id) + var/list/AS = get_all_access_datums_by_id() + return AS["[id]"] + +/proc/get_all_jobs() + var/list/all_jobs = list() + var/list/all_datums = typesof(/datum/job) + all_datums -= exclude_jobs + var/datum/job/jobdatum + for(var/jobtype in all_datums) + jobdatum = new jobtype + all_jobs.Add(jobdatum.title) + return all_jobs + +/proc/get_all_centcom_jobs() + return list("VIP Guest", + "Custodian", + "Thunderdome Overseer", + "Intel Officer", + "Medical Officer", + "Death Commando", + "Research Officer", + "BlackOps Commander", + "Supreme Commander", + "Emergency Response Team", + "Emergency Response Team Leader") + +/atom/movable/proc/GetIdCard() + return null + +/mob/living/bot/GetIdCard() + return botcard + +/mob/living/carbon/human/GetIdCard() + if(get_active_hand()) + var/obj/item/I = get_active_hand() + var/id = I.GetID() + if(id) + return id + if(wear_id) + var/id = wear_id.GetID() + if(id) + return id + +/mob/living/silicon/GetIdCard() + return idcard + +/proc/FindNameFromID(var/mob/living/carbon/human/H) + ASSERT(istype(H)) + var/obj/item/weapon/card/id/C = H.GetIdCard() + if(C) + return C.registered_name + +/proc/get_all_job_icons() //For all existing HUD icons + return joblist + alt_titles_with_icons + list("Prisoner") + +/obj/proc/GetJobName() //Used in secHUD icon generation + var/obj/item/weapon/card/id/I = GetID() + + if(I) + if(istype(I,/obj/item/weapon/card/id/centcom)) + return "Centcom" + + var/job_icons = get_all_job_icons() + if(I.assignment in job_icons) //Check if the job has a hud icon + return I.assignment + if(I.rank in job_icons) + return I.rank + + var/centcom = get_all_centcom_jobs() + if(I.assignment in centcom) //Return with the NT logo if it is a CentCom job + return "CentCom" + if(I.rank in centcom) + return "CentCom" + else + return + + return "Unknown" //Return unknown if none of the above apply diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index 26bd64d0b78..a7b0a71ea96 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -1,475 +1,475 @@ -/datum/access - var/id = 0 - var/desc = "" - var/region = ACCESS_REGION_NONE - var/access_type = ACCESS_TYPE_STATION - -/datum/access/dd_SortValue() - return "[access_type][desc]" - -/***************** -* Station access * -*****************/ -/var/const/access_security = 1 -/datum/access/security - id = access_security - desc = "Security Equipment" - region = ACCESS_REGION_SECURITY - -/var/const/access_brig = 2 // Brig timers and permabrig -/datum/access/holding - id = access_brig - desc = "Holding Cells" - region = ACCESS_REGION_SECURITY - -/var/const/access_armory = 3 -/datum/access/armory - id = access_armory - desc = "Armory" - region = ACCESS_REGION_SECURITY - -/var/const/access_forensics_lockers = 4 -/datum/access/forensics_lockers - id = access_forensics_lockers - desc = "Forensics" - region = ACCESS_REGION_SECURITY - -/var/const/access_medical = 5 -/datum/access/medical - id = access_medical - desc = "Medical" - region = ACCESS_REGION_MEDBAY - -/var/const/access_morgue = 6 -/datum/access/morgue - id = access_morgue - desc = "Morgue" - region = ACCESS_REGION_MEDBAY - -/var/const/access_tox = 7 -/datum/access/tox - id = access_tox - desc = "R&D Lab" - region = ACCESS_REGION_RESEARCH - -/var/const/access_tox_storage = 8 -/datum/access/tox_storage - id = access_tox_storage - desc = "Toxins Lab" - region = ACCESS_REGION_RESEARCH - -/var/const/access_genetics = 9 -/datum/access/genetics - id = access_genetics - desc = "Genetics Lab" - region = ACCESS_REGION_MEDBAY - -/var/const/access_engine = 10 -/datum/access/engine - id = access_engine - desc = "Engineering" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_engine_equip = 11 -/datum/access/engine_equip - id = access_engine_equip - desc = "Engine Room" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_maint_tunnels = 12 -/datum/access/maint_tunnels - id = access_maint_tunnels - desc = "Maintenance" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_external_airlocks = 13 -/datum/access/external_airlocks - id = access_external_airlocks - desc = "External Airlocks" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_emergency_storage = 14 -/datum/access/emergency_storage - id = access_emergency_storage - desc = "Emergency Storage" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_change_ids = 15 -/datum/access/change_ids - id = access_change_ids - desc = "ID Computer" - region = ACCESS_REGION_COMMAND - -/var/const/access_ai_upload = 16 -/datum/access/ai_upload - id = access_ai_upload - desc = "AI Upload" - region = ACCESS_REGION_COMMAND - -/var/const/access_teleporter = 17 -/datum/access/teleporter - id = access_teleporter - desc = "Teleporter" - region = ACCESS_REGION_COMMAND - -/var/const/access_eva = 18 -/datum/access/eva - id = access_eva - desc = "EVA" - region = ACCESS_REGION_COMMAND - -/var/const/access_heads = 19 -/datum/access/heads - id = access_heads - desc = "Bridge" - region = ACCESS_REGION_COMMAND - -/var/const/access_captain = 20 -/datum/access/captain - id = access_captain - desc = "Site Manager" - region = ACCESS_REGION_COMMAND - -/var/const/access_all_personal_lockers = 21 -/datum/access/all_personal_lockers - id = access_all_personal_lockers - desc = "Personal Lockers" - region = ACCESS_REGION_COMMAND - -/var/const/access_chapel_office = 22 -/datum/access/chapel_office - id = access_chapel_office - desc = "Chapel Office" - region = ACCESS_REGION_GENERAL - -/var/const/access_tech_storage = 23 -/datum/access/tech_storage - id = access_tech_storage - desc = "Technical Storage" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_atmospherics = 24 -/datum/access/atmospherics - id = access_atmospherics - desc = "Atmospherics" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_bar = 25 -/datum/access/bar - id = access_bar - desc = "Bar" - region = ACCESS_REGION_GENERAL - -/var/const/access_janitor = 26 -/datum/access/janitor - id = access_janitor - desc = "Custodial Closet" - region = ACCESS_REGION_GENERAL - -/var/const/access_crematorium = 27 -/datum/access/crematorium - id = access_crematorium - desc = "Crematorium" - region = ACCESS_REGION_GENERAL - -/var/const/access_kitchen = 28 -/datum/access/kitchen - id = access_kitchen - desc = "Kitchen" - region = ACCESS_REGION_GENERAL - -/var/const/access_robotics = 29 -/datum/access/robotics - id = access_robotics - desc = "Robotics" - region = ACCESS_REGION_RESEARCH - -/var/const/access_rd = 30 -/datum/access/rd - id = access_rd - desc = "Research Director" - region = ACCESS_REGION_RESEARCH - -/var/const/access_cargo = 31 -/datum/access/cargo - id = access_cargo - desc = "Cargo Bay" - region = ACCESS_REGION_SUPPLY - -/var/const/access_construction = 32 -/datum/access/construction - id = access_construction - desc = "Construction Areas" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_chemistry = 33 -/datum/access/chemistry - id = access_chemistry - desc = "Chemistry Lab" - region = ACCESS_REGION_MEDBAY - -/var/const/access_cargo_bot = 34 -/datum/access/cargo_bot - id = access_cargo_bot - desc = "Cargo Bot Delivery" - region = ACCESS_REGION_SUPPLY - -/var/const/access_hydroponics = 35 -/datum/access/hydroponics - id = access_hydroponics - desc = "Hydroponics" - region = ACCESS_REGION_GENERAL - -/var/const/access_manufacturing = 36 -/datum/access/manufacturing - id = access_manufacturing - desc = "Manufacturing" - access_type = ACCESS_TYPE_NONE - -/var/const/access_library = 37 -/datum/access/library - id = access_library - desc = "Library" - region = ACCESS_REGION_GENERAL - -/var/const/access_lawyer = 38 -/datum/access/lawyer - id = access_lawyer - desc = "Internal Affairs" - region = ACCESS_REGION_COMMAND - -/var/const/access_virology = 39 -/datum/access/virology - id = access_virology - desc = "Virology" - region = ACCESS_REGION_MEDBAY - -/var/const/access_cmo = 40 -/datum/access/cmo - id = access_cmo - desc = "Chief Medical Officer" - region = ACCESS_REGION_COMMAND - -/var/const/access_qm = 41 -/datum/access/qm - id = access_qm - desc = "Quartermaster" - region = ACCESS_REGION_SUPPLY - -/var/const/access_network = 42 -/datum/access/network - id = access_network - desc = "Station Network" - region = ACCESS_REGION_RESEARCH - -// /var/const/free_access_id = 43 // taken in VR -// /var/const/free_access_id = 44 // taken in VR - -/var/const/access_surgery = 45 -/datum/access/surgery - id = access_surgery - desc = "Surgery" - region = ACCESS_REGION_MEDBAY - -// /var/const/free_access_id = 46 - -/var/const/access_research = 47 -/datum/access/research - id = access_research - desc = "Science" - region = ACCESS_REGION_RESEARCH - -/var/const/access_mining = 48 -/datum/access/mining - id = access_mining - desc = "Mining" - region = ACCESS_REGION_SUPPLY - -/var/const/access_mining_office = 49 -/datum/access/mining_office - id = access_mining_office - desc = "Mining Office" - access_type = ACCESS_TYPE_NONE - -/var/const/access_mailsorting = 50 -/datum/access/mailsorting - id = access_mailsorting - desc = "Cargo Office" - region = ACCESS_REGION_SUPPLY - -// /var/const/free_access_id = 51 -// /var/const/free_access_id = 52 - -/var/const/access_heads_vault = 53 -/datum/access/heads_vault - id = access_heads_vault - desc = "Main Vault" - region = ACCESS_REGION_COMMAND - -/var/const/access_mining_station = 54 -/datum/access/mining_station - id = access_mining_station - desc = "Mining EVA" - region = ACCESS_REGION_SUPPLY - -/var/const/access_xenobiology = 55 -/datum/access/xenobiology - id = access_xenobiology - desc = "Xenobiology Lab" - region = ACCESS_REGION_RESEARCH - -/var/const/access_ce = 56 -/datum/access/ce - id = access_ce - desc = "Chief Engineer" - region = ACCESS_REGION_ENGINEERING - -/var/const/access_hop = 57 -/datum/access/hop - id = access_hop - desc = "Head of Personnel" - region = ACCESS_REGION_COMMAND - -/var/const/access_hos = 58 -/datum/access/hos - id = access_hos - desc = "Head of Security" - region = ACCESS_REGION_SECURITY - -/var/const/access_RC_announce = 59 //Request console announcements -/datum/access/RC_announce - id = access_RC_announce - desc = "RC Announcements" - region = ACCESS_REGION_COMMAND - -/var/const/access_keycard_auth = 60 //Used for events which require at least two people to confirm them -/datum/access/keycard_auth - id = access_keycard_auth - desc = "Keycode Auth. Device" - region = ACCESS_REGION_COMMAND - -/var/const/access_tcomsat = 61 // has access to the entire telecomms satellite / machinery -/datum/access/tcomsat - id = access_tcomsat - desc = "Telecommunications" - region = ACCESS_REGION_COMMAND - -/var/const/access_gateway = 62 -/datum/access/gateway - id = access_gateway - desc = "Gateway" - region = ACCESS_REGION_COMMAND - -/var/const/access_sec_doors = 63 // Security front doors -/datum/access/sec_doors - id = access_sec_doors - desc = "Security" - region = ACCESS_REGION_SECURITY - -/var/const/access_psychiatrist = 64 // Psychiatrist's office -/datum/access/psychiatrist - id = access_psychiatrist - desc = "Psychiatrist's Office" - region = ACCESS_REGION_MEDBAY - -/var/const/access_xenoarch = 65 -/datum/access/xenoarch - id = access_xenoarch - desc = "Xenoarchaeology" - region = ACCESS_REGION_RESEARCH - -/var/const/access_medical_equip = 66 -/datum/access/medical_equip - id = access_medical_equip - desc = "Medical Equipment" - region = ACCESS_REGION_MEDBAY - -/****************** -* Central Command * -******************/ -/var/const/access_cent_general = 101//General facilities. -/datum/access/cent_general - id = access_cent_general - desc = "General Facilities" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_thunder = 102//Thunderdome. -/datum/access/cent_thunder - id = access_cent_thunder - desc = "Entertainment Facilities" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_specops = 103//Special Ops. -/datum/access/cent_specops - id = access_cent_specops - desc = "Emergency Response Team Prep" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_medical = 104//Medical/Research -/datum/access/cent_medical - id = access_cent_medical - desc = "Medical Facilities" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_living = 105//Living quarters. -/datum/access/cent_living - id = access_cent_living - desc = "Dormitories" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_storage = 106//Generic storage areas. -/datum/access/cent_storage - id = access_cent_storage - desc = "Storage" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_teleporter = 107//Teleporter. -/datum/access/cent_teleporter - id = access_cent_teleporter - desc = "Central Command Teleporter" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_creed = 108//Creed's office. -/datum/access/cent_creed - id = access_cent_creed - desc = "Emergency Response Team Administration" - access_type = ACCESS_TYPE_CENTCOM - -/var/const/access_cent_captain = 109//Captain's office/ID comp/AI. -/datum/access/cent_captain - id = access_cent_captain - desc = "Central Command Administration" - access_type = ACCESS_TYPE_CENTCOM - -/*************** -* Antag access * -***************/ -/var/const/access_syndicate = 150//General Syndicate Access -/datum/access/syndicate - id = access_syndicate - access_type = ACCESS_TYPE_SYNDICATE - -/******* -* Misc * -*******/ -/var/const/access_synth = 199 -/datum/access/synthetic - id = access_synth - desc = "Synthetic" - access_type = ACCESS_TYPE_NONE - -/var/const/access_crate_cash = 200 -/datum/access/crate_cash - id = access_crate_cash - access_type = ACCESS_TYPE_NONE - -/var/const/access_trader = 160//General Beruang Trader Access -/datum/access/trader - id = access_trader - access_type = ACCESS_TYPE_PRIVATE - -/var/const/access_alien = 300 // For things like crashed ships. -/datum/access/alien - id = access_alien - desc = "#%_^&*@!" - access_type = ACCESS_TYPE_PRIVATE +/datum/access + var/id = 0 + var/desc = "" + var/region = ACCESS_REGION_NONE + var/access_type = ACCESS_TYPE_STATION + +/datum/access/dd_SortValue() + return "[access_type][desc]" + +/***************** +* Station access * +*****************/ +/var/const/access_security = 1 +/datum/access/security + id = access_security + desc = "Security Equipment" + region = ACCESS_REGION_SECURITY + +/var/const/access_brig = 2 // Brig timers and permabrig +/datum/access/holding + id = access_brig + desc = "Holding Cells" + region = ACCESS_REGION_SECURITY + +/var/const/access_armory = 3 +/datum/access/armory + id = access_armory + desc = "Armory" + region = ACCESS_REGION_SECURITY + +/var/const/access_forensics_lockers = 4 +/datum/access/forensics_lockers + id = access_forensics_lockers + desc = "Forensics" + region = ACCESS_REGION_SECURITY + +/var/const/access_medical = 5 +/datum/access/medical + id = access_medical + desc = "Medical" + region = ACCESS_REGION_MEDBAY + +/var/const/access_morgue = 6 +/datum/access/morgue + id = access_morgue + desc = "Morgue" + region = ACCESS_REGION_MEDBAY + +/var/const/access_tox = 7 +/datum/access/tox + id = access_tox + desc = "R&D Lab" + region = ACCESS_REGION_RESEARCH + +/var/const/access_tox_storage = 8 +/datum/access/tox_storage + id = access_tox_storage + desc = "Toxins Lab" + region = ACCESS_REGION_RESEARCH + +/var/const/access_genetics = 9 +/datum/access/genetics + id = access_genetics + desc = "Genetics Lab" + region = ACCESS_REGION_MEDBAY + +/var/const/access_engine = 10 +/datum/access/engine + id = access_engine + desc = "Engineering" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_engine_equip = 11 +/datum/access/engine_equip + id = access_engine_equip + desc = "Engine Room" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_maint_tunnels = 12 +/datum/access/maint_tunnels + id = access_maint_tunnels + desc = "Maintenance" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_external_airlocks = 13 +/datum/access/external_airlocks + id = access_external_airlocks + desc = "External Airlocks" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_emergency_storage = 14 +/datum/access/emergency_storage + id = access_emergency_storage + desc = "Emergency Storage" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_change_ids = 15 +/datum/access/change_ids + id = access_change_ids + desc = "ID Computer" + region = ACCESS_REGION_COMMAND + +/var/const/access_ai_upload = 16 +/datum/access/ai_upload + id = access_ai_upload + desc = "AI Upload" + region = ACCESS_REGION_COMMAND + +/var/const/access_teleporter = 17 +/datum/access/teleporter + id = access_teleporter + desc = "Teleporter" + region = ACCESS_REGION_COMMAND + +/var/const/access_eva = 18 +/datum/access/eva + id = access_eva + desc = "EVA" + region = ACCESS_REGION_COMMAND + +/var/const/access_heads = 19 +/datum/access/heads + id = access_heads + desc = "Bridge" + region = ACCESS_REGION_COMMAND + +/var/const/access_captain = 20 +/datum/access/captain + id = access_captain + desc = "Site Manager" + region = ACCESS_REGION_COMMAND + +/var/const/access_all_personal_lockers = 21 +/datum/access/all_personal_lockers + id = access_all_personal_lockers + desc = "Personal Lockers" + region = ACCESS_REGION_COMMAND + +/var/const/access_chapel_office = 22 +/datum/access/chapel_office + id = access_chapel_office + desc = "Chapel Office" + region = ACCESS_REGION_GENERAL + +/var/const/access_tech_storage = 23 +/datum/access/tech_storage + id = access_tech_storage + desc = "Technical Storage" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_atmospherics = 24 +/datum/access/atmospherics + id = access_atmospherics + desc = "Atmospherics" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_bar = 25 +/datum/access/bar + id = access_bar + desc = "Bar" + region = ACCESS_REGION_GENERAL + +/var/const/access_janitor = 26 +/datum/access/janitor + id = access_janitor + desc = "Custodial Closet" + region = ACCESS_REGION_GENERAL + +/var/const/access_crematorium = 27 +/datum/access/crematorium + id = access_crematorium + desc = "Crematorium" + region = ACCESS_REGION_GENERAL + +/var/const/access_kitchen = 28 +/datum/access/kitchen + id = access_kitchen + desc = "Kitchen" + region = ACCESS_REGION_GENERAL + +/var/const/access_robotics = 29 +/datum/access/robotics + id = access_robotics + desc = "Robotics" + region = ACCESS_REGION_RESEARCH + +/var/const/access_rd = 30 +/datum/access/rd + id = access_rd + desc = "Research Director" + region = ACCESS_REGION_RESEARCH + +/var/const/access_cargo = 31 +/datum/access/cargo + id = access_cargo + desc = "Cargo Bay" + region = ACCESS_REGION_SUPPLY + +/var/const/access_construction = 32 +/datum/access/construction + id = access_construction + desc = "Construction Areas" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_chemistry = 33 +/datum/access/chemistry + id = access_chemistry + desc = "Chemistry Lab" + region = ACCESS_REGION_MEDBAY + +/var/const/access_cargo_bot = 34 +/datum/access/cargo_bot + id = access_cargo_bot + desc = "Cargo Bot Delivery" + region = ACCESS_REGION_SUPPLY + +/var/const/access_hydroponics = 35 +/datum/access/hydroponics + id = access_hydroponics + desc = "Hydroponics" + region = ACCESS_REGION_GENERAL + +/var/const/access_manufacturing = 36 +/datum/access/manufacturing + id = access_manufacturing + desc = "Manufacturing" + access_type = ACCESS_TYPE_NONE + +/var/const/access_library = 37 +/datum/access/library + id = access_library + desc = "Library" + region = ACCESS_REGION_GENERAL + +/var/const/access_lawyer = 38 +/datum/access/lawyer + id = access_lawyer + desc = "Internal Affairs" + region = ACCESS_REGION_COMMAND + +/var/const/access_virology = 39 +/datum/access/virology + id = access_virology + desc = "Virology" + region = ACCESS_REGION_MEDBAY + +/var/const/access_cmo = 40 +/datum/access/cmo + id = access_cmo + desc = "Chief Medical Officer" + region = ACCESS_REGION_COMMAND + +/var/const/access_qm = 41 +/datum/access/qm + id = access_qm + desc = "Quartermaster" + region = ACCESS_REGION_SUPPLY + +/var/const/access_network = 42 +/datum/access/network + id = access_network + desc = "Station Network" + region = ACCESS_REGION_RESEARCH + +// /var/const/free_access_id = 43 // taken in VR +// /var/const/free_access_id = 44 // taken in VR + +/var/const/access_surgery = 45 +/datum/access/surgery + id = access_surgery + desc = "Surgery" + region = ACCESS_REGION_MEDBAY + +// /var/const/free_access_id = 46 + +/var/const/access_research = 47 +/datum/access/research + id = access_research + desc = "Science" + region = ACCESS_REGION_RESEARCH + +/var/const/access_mining = 48 +/datum/access/mining + id = access_mining + desc = "Mining" + region = ACCESS_REGION_SUPPLY + +/var/const/access_mining_office = 49 +/datum/access/mining_office + id = access_mining_office + desc = "Mining Office" + access_type = ACCESS_TYPE_NONE + +/var/const/access_mailsorting = 50 +/datum/access/mailsorting + id = access_mailsorting + desc = "Cargo Office" + region = ACCESS_REGION_SUPPLY + +// /var/const/free_access_id = 51 +// /var/const/free_access_id = 52 + +/var/const/access_heads_vault = 53 +/datum/access/heads_vault + id = access_heads_vault + desc = "Main Vault" + region = ACCESS_REGION_COMMAND + +/var/const/access_mining_station = 54 +/datum/access/mining_station + id = access_mining_station + desc = "Mining EVA" + region = ACCESS_REGION_SUPPLY + +/var/const/access_xenobiology = 55 +/datum/access/xenobiology + id = access_xenobiology + desc = "Xenobiology Lab" + region = ACCESS_REGION_RESEARCH + +/var/const/access_ce = 56 +/datum/access/ce + id = access_ce + desc = "Chief Engineer" + region = ACCESS_REGION_ENGINEERING + +/var/const/access_hop = 57 +/datum/access/hop + id = access_hop + desc = "Head of Personnel" + region = ACCESS_REGION_COMMAND + +/var/const/access_hos = 58 +/datum/access/hos + id = access_hos + desc = "Head of Security" + region = ACCESS_REGION_SECURITY + +/var/const/access_RC_announce = 59 //Request console announcements +/datum/access/RC_announce + id = access_RC_announce + desc = "RC Announcements" + region = ACCESS_REGION_COMMAND + +/var/const/access_keycard_auth = 60 //Used for events which require at least two people to confirm them +/datum/access/keycard_auth + id = access_keycard_auth + desc = "Keycode Auth. Device" + region = ACCESS_REGION_COMMAND + +/var/const/access_tcomsat = 61 // has access to the entire telecomms satellite / machinery +/datum/access/tcomsat + id = access_tcomsat + desc = "Telecommunications" + region = ACCESS_REGION_COMMAND + +/var/const/access_gateway = 62 +/datum/access/gateway + id = access_gateway + desc = "Gateway" + region = ACCESS_REGION_COMMAND + +/var/const/access_sec_doors = 63 // Security front doors +/datum/access/sec_doors + id = access_sec_doors + desc = "Security" + region = ACCESS_REGION_SECURITY + +/var/const/access_psychiatrist = 64 // Psychiatrist's office +/datum/access/psychiatrist + id = access_psychiatrist + desc = "Psychiatrist's Office" + region = ACCESS_REGION_MEDBAY + +/var/const/access_xenoarch = 65 +/datum/access/xenoarch + id = access_xenoarch + desc = "Xenoarchaeology" + region = ACCESS_REGION_RESEARCH + +/var/const/access_medical_equip = 66 +/datum/access/medical_equip + id = access_medical_equip + desc = "Medical Equipment" + region = ACCESS_REGION_MEDBAY + +/****************** +* Central Command * +******************/ +/var/const/access_cent_general = 101//General facilities. +/datum/access/cent_general + id = access_cent_general + desc = "General Facilities" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_thunder = 102//Thunderdome. +/datum/access/cent_thunder + id = access_cent_thunder + desc = "Entertainment Facilities" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_specops = 103//Special Ops. +/datum/access/cent_specops + id = access_cent_specops + desc = "Emergency Response Team Prep" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_medical = 104//Medical/Research +/datum/access/cent_medical + id = access_cent_medical + desc = "Medical Facilities" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_living = 105//Living quarters. +/datum/access/cent_living + id = access_cent_living + desc = "Dormitories" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_storage = 106//Generic storage areas. +/datum/access/cent_storage + id = access_cent_storage + desc = "Storage" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_teleporter = 107//Teleporter. +/datum/access/cent_teleporter + id = access_cent_teleporter + desc = "Central Command Teleporter" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_creed = 108//Creed's office. +/datum/access/cent_creed + id = access_cent_creed + desc = "Emergency Response Team Administration" + access_type = ACCESS_TYPE_CENTCOM + +/var/const/access_cent_captain = 109//Captain's office/ID comp/AI. +/datum/access/cent_captain + id = access_cent_captain + desc = "Central Command Administration" + access_type = ACCESS_TYPE_CENTCOM + +/*************** +* Antag access * +***************/ +/var/const/access_syndicate = 150//General Syndicate Access +/datum/access/syndicate + id = access_syndicate + access_type = ACCESS_TYPE_SYNDICATE + +/******* +* Misc * +*******/ +/var/const/access_synth = 199 +/datum/access/synthetic + id = access_synth + desc = "Synthetic" + access_type = ACCESS_TYPE_NONE + +/var/const/access_crate_cash = 200 +/datum/access/crate_cash + id = access_crate_cash + access_type = ACCESS_TYPE_NONE + +/var/const/access_trader = 160//General Beruang Trader Access +/datum/access/trader + id = access_trader + access_type = ACCESS_TYPE_PRIVATE + +/var/const/access_alien = 300 // For things like crashed ships. +/datum/access/alien + id = access_alien + desc = "#%_^&*@!" + access_type = ACCESS_TYPE_PRIVATE diff --git a/code/game/jobs/job/assistant.dm b/code/game/jobs/job/assistant.dm index c1270dab3e7..0cf6a8c086f 100644 --- a/code/game/jobs/job/assistant.dm +++ b/code/game/jobs/job/assistant.dm @@ -1,59 +1,59 @@ -////////////////////////////////// -// Assistant -////////////////////////////////// -/datum/job/assistant - title = "Assistant" - flag = ASSISTANT - departments = list(DEPARTMENT_CIVILIAN) - sorting_order = -1 - department_flag = CIVILIAN - faction = "Station" - total_positions = -1 - spawn_positions = -1 - supervisors = "absolutely everyone" - selection_color = "#515151" - economic_modifier = 1 - access = list() //See /datum/job/assistant/get_access() - minimal_access = list() //See /datum/job/assistant/get_access() - - outfit_type = /decl/hierarchy/outfit/job/assistant - job_description = "An Assistant does whatever is requested of them. Though they are part of the crew, they have no real authority." -/* alt_titles = list("Technical Assistant" = /datum/alt_title/tech_assist, - "Medical Intern"= /datum/alt_title/med_intern, "Research Assistant" = /datum/alt_title/research_assist, - "Visitor" = /datum/alt_title/visitor) - ) */ //VOREStation Removal: no alt-titles for visitors - -/datum/job/assistant/get_access() - if(config.assistant_maint) - return list(access_maint_tunnels) - else - return list() - -// Assistant Alt Titles -/datum/alt_title/tech_assist - title = "Technical Assistant" - title_blurb = "A Technical Assistant attempts to provide whatever the Engineering department needs. They are not proper Engineers, and are \ - often in training to become an Engineer. A Technical Assistant has no real authority." - -/datum/alt_title/med_intern - title = "Medical Intern" - title_blurb = "A Medical Intern attempts to provide whatever the Medical department needs. They are frequently asked to pay attention to \ - the suit sensors console. A Medical Intern has no real authority." - -/datum/alt_title/research_assist - title = "Research Assistant" - title_blurb = "A Research Assistant attempts to assist anyone working in the Research department. They are expected to follow the instruction \ - of proper Research staff, as it is frequently a matter of safety. A Research Assistant has no real authority." - -/datum/alt_title/visitor - title = "Visitor" - title_blurb = "A Visitor is anyone who has arrived on the station but does not have a specific job to do. Many off-duty crewmembers \ - who care to make use of the station's facilities arrive as Visitors. Properly registered Vistors are considered \ - to be part of the crew for most if not all purposes, but they have no real authority." - title_outfit = /decl/hierarchy/outfit/job/assistant/visitor - -/datum/alt_title/resident // Just in case it makes a comeback - title = "Resident" - title_blurb = "A Resident is an individual who resides on the station, frequently in a different part of the station than what is seen. \ - They are considered to be part of the crew for most purposes, but have no real authority." - title_outfit = /decl/hierarchy/outfit/job/assistant/resident +////////////////////////////////// +// Assistant +////////////////////////////////// +/datum/job/assistant + title = "Assistant" + flag = ASSISTANT + departments = list(DEPARTMENT_CIVILIAN) + sorting_order = -1 + department_flag = CIVILIAN + faction = "Station" + total_positions = -1 + spawn_positions = -1 + supervisors = "absolutely everyone" + selection_color = "#515151" + economic_modifier = 1 + access = list() //See /datum/job/assistant/get_access() + minimal_access = list() //See /datum/job/assistant/get_access() + + outfit_type = /decl/hierarchy/outfit/job/assistant + job_description = "An Assistant does whatever is requested of them. Though they are part of the crew, they have no real authority." +/* alt_titles = list("Technical Assistant" = /datum/alt_title/tech_assist, + "Medical Intern"= /datum/alt_title/med_intern, "Research Assistant" = /datum/alt_title/research_assist, + "Visitor" = /datum/alt_title/visitor) + ) */ //VOREStation Removal: no alt-titles for visitors + +/datum/job/assistant/get_access() + if(config.assistant_maint) + return list(access_maint_tunnels) + else + return list() + +// Assistant Alt Titles +/datum/alt_title/tech_assist + title = "Technical Assistant" + title_blurb = "A Technical Assistant attempts to provide whatever the Engineering department needs. They are not proper Engineers, and are \ + often in training to become an Engineer. A Technical Assistant has no real authority." + +/datum/alt_title/med_intern + title = "Medical Intern" + title_blurb = "A Medical Intern attempts to provide whatever the Medical department needs. They are frequently asked to pay attention to \ + the suit sensors console. A Medical Intern has no real authority." + +/datum/alt_title/research_assist + title = "Research Assistant" + title_blurb = "A Research Assistant attempts to assist anyone working in the Research department. They are expected to follow the instruction \ + of proper Research staff, as it is frequently a matter of safety. A Research Assistant has no real authority." + +/datum/alt_title/visitor + title = "Visitor" + title_blurb = "A Visitor is anyone who has arrived on the station but does not have a specific job to do. Many off-duty crewmembers \ + who care to make use of the station's facilities arrive as Visitors. Properly registered Vistors are considered \ + to be part of the crew for most if not all purposes, but they have no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/visitor + +/datum/alt_title/resident // Just in case it makes a comeback + title = "Resident" + title_blurb = "A Resident is an individual who resides on the station, frequently in a different part of the station than what is seen. \ + They are considered to be part of the crew for most purposes, but have no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/resident diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index add113dca28..f06c7dc083b 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -1,247 +1,247 @@ -//Food - -////////////////////////////////// -// Bartender -////////////////////////////////// - -/datum/job/bartender - title = "Bartender" - flag = BARTENDER - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Personnel" - selection_color = "#515151" - access = list(access_hydroponics, access_bar, access_kitchen) - minimal_access = list(access_bar) - - outfit_type = /decl/hierarchy/outfit/job/service/bartender - job_description = "A Bartender mixes drinks for the crew. They generally have permission to charge for drinks or deny service to unruly patrons." - alt_titles = list("Barista" = /datum/alt_title/barista) - -// Bartender Alt Titles -/datum/alt_title/barista - title = "Barista" - title_blurb = "A barista mans the Cafe, serving primarily non-alcoholic drinks to the crew. They generally have permission to charge for drinks \ - or deny service to unruly patrons." - title_outfit = /decl/hierarchy/outfit/job/service/bartender/barista - -////////////////////////////////// -// Chef -////////////////////////////////// - -/datum/job/chef - title = "Chef" - flag = CHEF - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Personnel" - selection_color = "#515151" - access = list(access_hydroponics, access_bar, access_kitchen) - minimal_access = list(access_kitchen) - - outfit_type = /decl/hierarchy/outfit/job/service/chef - job_description = "A Chef cooks food for the crew. They generally have permission to charge for food or deny service to unruly diners." - alt_titles = list("Cook" = /datum/alt_title/cook) - -// Chef Alt Titles -/datum/alt_title/cook - title = "Cook" - title_blurb = "A Cook has the same duties, though they may be less experienced." - -////////////////////////////////// -// Botanist -////////////////////////////////// - -/datum/job/hydro - title = "Botanist" - flag = BOTANIST - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Personnel" - selection_color = "#515151" - access = list(access_hydroponics, access_bar, access_kitchen) - minimal_access = list(access_hydroponics) - - outfit_type = /decl/hierarchy/outfit/job/service/gardener - job_description = "A Botanist grows plants for the Chef and Bartender." - alt_titles = list("Gardener" = /datum/alt_title/gardener) - -//Botanist Alt Titles -/datum/alt_title/gardener - title = "Gardener" - title_blurb = "A Gardener may be less professional than their counterparts, and are more likely to tend to the public gardens if they aren't needed elsewhere." - -//Cargo -////////////////////////////////// -// Quartermaster -////////////////////////////////// -/datum/job/qm - title = "Quartermaster" - flag = QUARTERMASTER - departments = list(DEPARTMENT_CARGO) - sorting_order = 1 // QM is above the cargo techs, but below the HoP. - departments_managed = list(DEPARTMENT_CARGO) - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Head of Personnel" - selection_color = "#9b633e" - economic_modifier = 5 - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station, access_RC_announce) - minimal_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station, access_RC_announce) - banned_job_species = list("digital", SPECIES_PROMETHEAN) - - ideal_character_age = 40 - - outfit_type = /decl/hierarchy/outfit/job/cargo/qm - job_description = "The Quartermaster manages the Supply department, checking cargo orders and ensuring supplies get to where they are needed." - alt_titles = list("Supply Chief" = /datum/alt_title/supply_chief) - -// Quartermaster Alt Titles -/datum/alt_title/supply_chief - title = "Supply Chief" - -////////////////////////////////// -// Cargo Tech -////////////////////////////////// -/datum/job/cargo_tech - title = "Cargo Technician" - flag = CARGOTECH - departments = list(DEPARTMENT_CARGO) - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Quartermaster and the Head of Personnel" - selection_color = "#7a4f33" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_mining, access_mining_station) - minimal_access = list(access_maint_tunnels, access_cargo, access_cargo_bot, access_mailsorting) - - outfit_type = /decl/hierarchy/outfit/job/cargo/cargo_tech - job_description = "A Cargo Technician fills and delivers cargo orders. They are encouraged to return delivered crates to the Cargo Shuttle, \ - because Central Command gives a partial refund." - -////////////////////////////////// -// Shaft Miner -////////////////////////////////// - -/datum/job/mining - title = "Shaft Miner" - flag = MINER - departments = list(DEPARTMENT_CARGO) - department_flag = CIVILIAN - faction = "Station" - total_positions = 3 - spawn_positions = 3 - supervisors = "the Quartermaster and the Head of Personnel" - selection_color = "#7a4f33" - economic_modifier = 5 - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_mining, access_mining_station) - minimal_access = list(access_mining, access_mining_station, access_mailsorting) - - outfit_type = /decl/hierarchy/outfit/job/cargo/mining - job_description = "A Shaft Miner mines and processes minerals to be delivered to departments that need them." - alt_titles = list("Drill Technician" = /datum/alt_title/drill_tech) - -/datum/alt_title/drill_tech - title = "Drill Technician" - title_blurb = "A Drill Technician specializes in operating and maintaining the machinery needed to extract ore from veins deep below the surface." - -//Service -////////////////////////////////// -// Janitor -////////////////////////////////// -/datum/job/janitor - title = "Janitor" - flag = JANITOR - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Personnel" - selection_color = "#515151" - access = list(access_janitor, access_maint_tunnels) - minimal_access = list(access_janitor, access_maint_tunnels) - - outfit_type = /decl/hierarchy/outfit/job/service/janitor - job_description = "A Janitor keeps the station clean, as long as it doesn't interfere with active crime scenes." - alt_titles = list("Custodian" = /datum/alt_title/custodian) - -// Janitor Alt Titles -/datum/alt_title/custodian - title = "Custodian" - -//More or less assistants -////////////////////////////////// -// Librarian -////////////////////////////////// -/datum/job/librarian - title = "Librarian" - flag = LIBRARIAN - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Head of Personnel" - selection_color = "#515151" - access = list(access_library, access_maint_tunnels) - minimal_access = list(access_library) - - outfit_type = /decl/hierarchy/outfit/job/librarian - job_description = "The Librarian curates the book selection in the Library, so the crew might enjoy it." - alt_titles = list("Journalist" = /datum/alt_title/journalist, "Writer" = /datum/alt_title/writer) - -// Librarian Alt Titles -/datum/alt_title/journalist - title = "Journalist" - title_outfit = /decl/hierarchy/outfit/job/librarian/journalist - title_blurb = "The Journalist uses the Library as a base of operations, from which they can report the news and goings-on on the station with their camera." - -/datum/alt_title/writer - title = "Writer" - title_blurb = "The Writer uses the Library as a quiet place to write whatever it is they choose to write." - -////////////////////////////////// -// Internal Affairs Agent -////////////////////////////////// - -//var/global/lawyer = 0//Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. -/datum/job/lawyer - title = "Internal Affairs Agent" - flag = LAWYER - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "company officials and Corporate Regulations" - selection_color = "#515151" - economic_modifier = 7 - access = list(access_lawyer, access_sec_doors, access_maint_tunnels, access_heads) - minimal_access = list(access_lawyer, access_sec_doors, access_heads) - minimal_player_age = 7 - banned_job_species = list(SPECIES_PROMETHEAN, SPECIES_UNATHI, SPECIES_DIONA, SPECIES_TESHARI, SPECIES_ZADDAT, "digital") - - outfit_type = /decl/hierarchy/outfit/job/internal_affairs_agent - job_description = "An Internal Affairs Agent makes sure that the crew is following Standard Operating Procedure. They also \ - handle complaints against crew members, and can have issues brought to the attention of Central Command, \ - assuming their paperwork is in order." - -/* -/datum/job/lawyer/equip(var/mob/living/carbon/human/H) - . = ..() - if(.) - H.implant_loyalty(H) -*/ +//Food + +////////////////////////////////// +// Bartender +////////////////////////////////// + +/datum/job/bartender + title = "Bartender" + flag = BARTENDER + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Personnel" + selection_color = "#515151" + access = list(access_hydroponics, access_bar, access_kitchen) + minimal_access = list(access_bar) + + outfit_type = /decl/hierarchy/outfit/job/service/bartender + job_description = "A Bartender mixes drinks for the crew. They generally have permission to charge for drinks or deny service to unruly patrons." + alt_titles = list("Barista" = /datum/alt_title/barista) + +// Bartender Alt Titles +/datum/alt_title/barista + title = "Barista" + title_blurb = "A barista mans the Cafe, serving primarily non-alcoholic drinks to the crew. They generally have permission to charge for drinks \ + or deny service to unruly patrons." + title_outfit = /decl/hierarchy/outfit/job/service/bartender/barista + +////////////////////////////////// +// Chef +////////////////////////////////// + +/datum/job/chef + title = "Chef" + flag = CHEF + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Personnel" + selection_color = "#515151" + access = list(access_hydroponics, access_bar, access_kitchen) + minimal_access = list(access_kitchen) + + outfit_type = /decl/hierarchy/outfit/job/service/chef + job_description = "A Chef cooks food for the crew. They generally have permission to charge for food or deny service to unruly diners." + alt_titles = list("Cook" = /datum/alt_title/cook) + +// Chef Alt Titles +/datum/alt_title/cook + title = "Cook" + title_blurb = "A Cook has the same duties, though they may be less experienced." + +////////////////////////////////// +// Botanist +////////////////////////////////// + +/datum/job/hydro + title = "Botanist" + flag = BOTANIST + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Personnel" + selection_color = "#515151" + access = list(access_hydroponics, access_bar, access_kitchen) + minimal_access = list(access_hydroponics) + + outfit_type = /decl/hierarchy/outfit/job/service/gardener + job_description = "A Botanist grows plants for the Chef and Bartender." + alt_titles = list("Gardener" = /datum/alt_title/gardener) + +//Botanist Alt Titles +/datum/alt_title/gardener + title = "Gardener" + title_blurb = "A Gardener may be less professional than their counterparts, and are more likely to tend to the public gardens if they aren't needed elsewhere." + +//Cargo +////////////////////////////////// +// Quartermaster +////////////////////////////////// +/datum/job/qm + title = "Quartermaster" + flag = QUARTERMASTER + departments = list(DEPARTMENT_CARGO) + sorting_order = 1 // QM is above the cargo techs, but below the HoP. + departments_managed = list(DEPARTMENT_CARGO) + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Head of Personnel" + selection_color = "#9b633e" + economic_modifier = 5 + access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station, access_RC_announce) + minimal_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station, access_RC_announce) + banned_job_species = list("digital", SPECIES_PROMETHEAN) + + ideal_character_age = 40 + + outfit_type = /decl/hierarchy/outfit/job/cargo/qm + job_description = "The Quartermaster manages the Supply department, checking cargo orders and ensuring supplies get to where they are needed." + alt_titles = list("Supply Chief" = /datum/alt_title/supply_chief) + +// Quartermaster Alt Titles +/datum/alt_title/supply_chief + title = "Supply Chief" + +////////////////////////////////// +// Cargo Tech +////////////////////////////////// +/datum/job/cargo_tech + title = "Cargo Technician" + flag = CARGOTECH + departments = list(DEPARTMENT_CARGO) + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Quartermaster and the Head of Personnel" + selection_color = "#7a4f33" + access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_mining, access_mining_station) + minimal_access = list(access_maint_tunnels, access_cargo, access_cargo_bot, access_mailsorting) + + outfit_type = /decl/hierarchy/outfit/job/cargo/cargo_tech + job_description = "A Cargo Technician fills and delivers cargo orders. They are encouraged to return delivered crates to the Cargo Shuttle, \ + because Central Command gives a partial refund." + +////////////////////////////////// +// Shaft Miner +////////////////////////////////// + +/datum/job/mining + title = "Shaft Miner" + flag = MINER + departments = list(DEPARTMENT_CARGO) + department_flag = CIVILIAN + faction = "Station" + total_positions = 3 + spawn_positions = 3 + supervisors = "the Quartermaster and the Head of Personnel" + selection_color = "#7a4f33" + economic_modifier = 5 + access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_mining, access_mining_station) + minimal_access = list(access_mining, access_mining_station, access_mailsorting) + + outfit_type = /decl/hierarchy/outfit/job/cargo/mining + job_description = "A Shaft Miner mines and processes minerals to be delivered to departments that need them." + alt_titles = list("Drill Technician" = /datum/alt_title/drill_tech) + +/datum/alt_title/drill_tech + title = "Drill Technician" + title_blurb = "A Drill Technician specializes in operating and maintaining the machinery needed to extract ore from veins deep below the surface." + +//Service +////////////////////////////////// +// Janitor +////////////////////////////////// +/datum/job/janitor + title = "Janitor" + flag = JANITOR + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Personnel" + selection_color = "#515151" + access = list(access_janitor, access_maint_tunnels) + minimal_access = list(access_janitor, access_maint_tunnels) + + outfit_type = /decl/hierarchy/outfit/job/service/janitor + job_description = "A Janitor keeps the station clean, as long as it doesn't interfere with active crime scenes." + alt_titles = list("Custodian" = /datum/alt_title/custodian) + +// Janitor Alt Titles +/datum/alt_title/custodian + title = "Custodian" + +//More or less assistants +////////////////////////////////// +// Librarian +////////////////////////////////// +/datum/job/librarian + title = "Librarian" + flag = LIBRARIAN + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Head of Personnel" + selection_color = "#515151" + access = list(access_library, access_maint_tunnels) + minimal_access = list(access_library) + + outfit_type = /decl/hierarchy/outfit/job/librarian + job_description = "The Librarian curates the book selection in the Library, so the crew might enjoy it." + alt_titles = list("Journalist" = /datum/alt_title/journalist, "Writer" = /datum/alt_title/writer) + +// Librarian Alt Titles +/datum/alt_title/journalist + title = "Journalist" + title_outfit = /decl/hierarchy/outfit/job/librarian/journalist + title_blurb = "The Journalist uses the Library as a base of operations, from which they can report the news and goings-on on the station with their camera." + +/datum/alt_title/writer + title = "Writer" + title_blurb = "The Writer uses the Library as a quiet place to write whatever it is they choose to write." + +////////////////////////////////// +// Internal Affairs Agent +////////////////////////////////// + +//var/global/lawyer = 0//Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. +/datum/job/lawyer + title = "Internal Affairs Agent" + flag = LAWYER + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "company officials and Corporate Regulations" + selection_color = "#515151" + economic_modifier = 7 + access = list(access_lawyer, access_sec_doors, access_maint_tunnels, access_heads) + minimal_access = list(access_lawyer, access_sec_doors, access_heads) + minimal_player_age = 7 + banned_job_species = list(SPECIES_PROMETHEAN, SPECIES_UNATHI, SPECIES_DIONA, SPECIES_TESHARI, SPECIES_ZADDAT, "digital") + + outfit_type = /decl/hierarchy/outfit/job/internal_affairs_agent + job_description = "An Internal Affairs Agent makes sure that the crew is following Standard Operating Procedure. They also \ + handle complaints against crew members, and can have issues brought to the attention of Central Command, \ + assuming their paperwork is in order." + +/* +/datum/job/lawyer/equip(var/mob/living/carbon/human/H) + . = ..() + if(.) + H.implant_loyalty(H) +*/ diff --git a/code/game/jobs/job/civilian_chaplain.dm b/code/game/jobs/job/civilian_chaplain.dm index baa151eecb8..ca379314126 100644 --- a/code/game/jobs/job/civilian_chaplain.dm +++ b/code/game/jobs/job/civilian_chaplain.dm @@ -1,124 +1,124 @@ -//Due to how large this one is it gets its own file -/datum/job/chaplain - title = "Chaplain" - flag = CHAPLAIN - departments = list(DEPARTMENT_CIVILIAN) - department_flag = CIVILIAN - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Head of Personnel" - selection_color = "#515151" - access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) - minimal_access = list(access_chapel_office, access_crematorium) - - outfit_type = /decl/hierarchy/outfit/job/chaplain - job_description = "The Chaplain ministers to the spiritual needs of the crew." - alt_titles = list("Counselor" = /datum/alt_title/counselor) - -// Chaplain Alt Titles -/datum/alt_title/counselor - title = "Counselor" - title_blurb = "The Counselor attends to the emotional needs of the crew, without a specific medicinal or spiritual focus." - -/datum/job/chaplain/equip(var/mob/living/carbon/human/H, var/alt_title, var/ask_questions = TRUE) - . = ..() - if(!.) - return - if(!ask_questions) - return - - var/obj/item/weapon/storage/bible/B = locate(/obj/item/weapon/storage/bible) in H - var/obj/item/weapon/card/id/I = locate(/obj/item/weapon/card/id) in H - - if(!B || !I) - return - - INVOKE_ASYNC(src, PROC_REF(religion_prompts), H, B, I) - -/datum/job/chaplain/proc/religion_prompts(mob/living/carbon/human/H, obj/item/weapon/storage/bible/B, obj/item/weapon/card/id/I) - var/religion_name = "Unitarianism" - var/new_religion = sanitize(input(H, "You are the crew services officer. Would you like to change your religion? Default is Unitarianism", "Name change", religion_name), MAX_NAME_LEN) - if(!new_religion) - new_religion = religion_name - - switch(lowertext(new_religion)) - if("unitarianism") - B.name = "The Great Canon" - if("christianity") - B.name = "The Holy Bible" - if("judaism") - B.name = "The Torah" - if("islam") - B.name = "Quran" - if("buddhism") - B.name = "Tripitakas" - if("hinduism") - B.name = pick("The Srimad Bhagvatam", "The Four Vedas", "The Shiv Mahapuran", "Devi Mahatmya") - if("neopaganism") - B.name = "Neopagan Hymnbook" - if("phact shintoism") - B.name = "The Kojiki" - if("kishari national faith") - B.name = "The Scriptures of Kishar" - if("pleromanism") - B.name = "The Revised Great Canon" - if("spectralism") - B.name = "The Book of the Spark" - if("hauler") - B.name = "Histories of Captaincy" - if("nock") - B.name = "The Book of the First" - if("singulitarian worship") - B.name = "The Book of the Precursors" - if("starlit path of angessa martei") - B.name = "Quotations of Exalted Martei" - if("sikhism") - B.name = "Guru Granth Sahib" - else - B.name = "The Holy Book of [new_religion]" - - var/deity_name = "Hashem" - var/new_deity = sanitize(input(H, "Would you like to change your deity? Default is Hashem", "Name change", deity_name), MAX_NAME_LEN) - - if((length(new_deity) == 0) || (new_deity == "Hashem")) - new_deity = deity_name - - var/new_title = sanitize(input(H, "Would you like to change your title?", "Title Change", I.assignment), MAX_NAME_LEN) - - var/list/all_jobs = get_job_datums() - - // Are they trying to fake an actual existent job - var/faking_job = FALSE - - for (var/datum/job/J in all_jobs) - if (J.title == new_title || (new_title in get_alternate_titles(J.title))) - faking_job = TRUE - - if (length(new_title) != 0 && !faking_job) - I.assignment = new_title - - H.mind.my_religion = new /datum/religion(new_religion, new_deity, B.name, "bible", "bible", new_title) - - B.deity_name = H.mind.my_religion.deity - I.assignment = H.mind.my_religion.title - I.name = text("[I.registered_name]'s ID Card ([I.assignment])") - data_core.manifest_modify(I.registered_name, I.assignment, I.rank) - -/datum/religion - var/religion = "Unitarianism" - var/deity = "Hashem" - var/bible_name = "Bible" - var/bible_icon_state = "bible" - var/bible_item_state = "bible" - var/title = "Chaplain" - var/configured = FALSE - -/datum/religion/New(var/r, var/d, var/bn, var/bis, var/bits, var/t) - . = ..() - religion = r - deity = d - bible_name = bn - bible_icon_state = bis - bible_item_state = bits - title = t +//Due to how large this one is it gets its own file +/datum/job/chaplain + title = "Chaplain" + flag = CHAPLAIN + departments = list(DEPARTMENT_CIVILIAN) + department_flag = CIVILIAN + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Head of Personnel" + selection_color = "#515151" + access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) + minimal_access = list(access_chapel_office, access_crematorium) + + outfit_type = /decl/hierarchy/outfit/job/chaplain + job_description = "The Chaplain ministers to the spiritual needs of the crew." + alt_titles = list("Counselor" = /datum/alt_title/counselor) + +// Chaplain Alt Titles +/datum/alt_title/counselor + title = "Counselor" + title_blurb = "The Counselor attends to the emotional needs of the crew, without a specific medicinal or spiritual focus." + +/datum/job/chaplain/equip(var/mob/living/carbon/human/H, var/alt_title, var/ask_questions = TRUE) + . = ..() + if(!.) + return + if(!ask_questions) + return + + var/obj/item/weapon/storage/bible/B = locate(/obj/item/weapon/storage/bible) in H + var/obj/item/weapon/card/id/I = locate(/obj/item/weapon/card/id) in H + + if(!B || !I) + return + + INVOKE_ASYNC(src, PROC_REF(religion_prompts), H, B, I) + +/datum/job/chaplain/proc/religion_prompts(mob/living/carbon/human/H, obj/item/weapon/storage/bible/B, obj/item/weapon/card/id/I) + var/religion_name = "Unitarianism" + var/new_religion = sanitize(input(H, "You are the crew services officer. Would you like to change your religion? Default is Unitarianism", "Name change", religion_name), MAX_NAME_LEN) + if(!new_religion) + new_religion = religion_name + + switch(lowertext(new_religion)) + if("unitarianism") + B.name = "The Great Canon" + if("christianity") + B.name = "The Holy Bible" + if("judaism") + B.name = "The Torah" + if("islam") + B.name = "Quran" + if("buddhism") + B.name = "Tripitakas" + if("hinduism") + B.name = pick("The Srimad Bhagvatam", "The Four Vedas", "The Shiv Mahapuran", "Devi Mahatmya") + if("neopaganism") + B.name = "Neopagan Hymnbook" + if("phact shintoism") + B.name = "The Kojiki" + if("kishari national faith") + B.name = "The Scriptures of Kishar" + if("pleromanism") + B.name = "The Revised Great Canon" + if("spectralism") + B.name = "The Book of the Spark" + if("hauler") + B.name = "Histories of Captaincy" + if("nock") + B.name = "The Book of the First" + if("singulitarian worship") + B.name = "The Book of the Precursors" + if("starlit path of angessa martei") + B.name = "Quotations of Exalted Martei" + if("sikhism") + B.name = "Guru Granth Sahib" + else + B.name = "The Holy Book of [new_religion]" + + var/deity_name = "Hashem" + var/new_deity = sanitize(input(H, "Would you like to change your deity? Default is Hashem", "Name change", deity_name), MAX_NAME_LEN) + + if((length(new_deity) == 0) || (new_deity == "Hashem")) + new_deity = deity_name + + var/new_title = sanitize(input(H, "Would you like to change your title?", "Title Change", I.assignment), MAX_NAME_LEN) + + var/list/all_jobs = get_job_datums() + + // Are they trying to fake an actual existent job + var/faking_job = FALSE + + for (var/datum/job/J in all_jobs) + if (J.title == new_title || (new_title in get_alternate_titles(J.title))) + faking_job = TRUE + + if (length(new_title) != 0 && !faking_job) + I.assignment = new_title + + H.mind.my_religion = new /datum/religion(new_religion, new_deity, B.name, "bible", "bible", new_title) + + B.deity_name = H.mind.my_religion.deity + I.assignment = H.mind.my_religion.title + I.name = text("[I.registered_name]'s ID Card ([I.assignment])") + data_core.manifest_modify(I.registered_name, I.assignment, I.rank) + +/datum/religion + var/religion = "Unitarianism" + var/deity = "Hashem" + var/bible_name = "Bible" + var/bible_icon_state = "bible" + var/bible_item_state = "bible" + var/title = "Chaplain" + var/configured = FALSE + +/datum/religion/New(var/r, var/d, var/bn, var/bis, var/bits, var/t) + . = ..() + religion = r + deity = d + bible_name = bn + bible_icon_state = bis + bible_item_state = bits + title = t diff --git a/code/game/jobs/job/department.dm b/code/game/jobs/job/department.dm index 39bbc1c38c6..313b19e07b0 100644 --- a/code/game/jobs/job/department.dm +++ b/code/game/jobs/job/department.dm @@ -1,80 +1,80 @@ -// A datum that holds information about a specific department. -// It is held inside, and managed by, the SSjob subsystem automatically, -// just define a department, and put that department's name in one or more job datums' departments list. - -/datum/department - var/name = "NOPE" // Name used in UIs, and the index for the department assoc list in SSjob. - var/short_name = "NO" // Shorter name, used for things like external Topic() responses. - var/color = "#000000" // Color to use in UIs to represent this department. - var/list/jobs = list() // Assoc list. Key is the job title, and the value is a reference to the job datum. Populated by SSjob subsystem. - var/list/primary_jobs = list() // Same as above, but only jobs with their 'primary' department are put here. Primary being the first department in their list. - var/sorting_order = 0 // Used to sort departments, e.g. Command always being on top. - var/visible = TRUE // If false, it should not show up on things like the manifest or ID computer. - var/assignable = TRUE // Similar for above, but only for ID computers and such. Used for silicon department. - var/centcom_only = FALSE - -/datum/department/command - name = DEPARTMENT_COMMAND - short_name = "Heads" - color = "#3333FF" - sorting_order = 10 - -/datum/department/security - name = DEPARTMENT_SECURITY - short_name = "Sec" - color = "#8E0000" - sorting_order = 6 - -/datum/department/engineering - name = DEPARTMENT_ENGINEERING - short_name = "Eng" - color = "#B27300" - sorting_order = 5 - -/datum/department/medical - name = DEPARTMENT_MEDICAL - short_name = "Med" - color = "#006600" - sorting_order = 4 - -/datum/department/research - name = DEPARTMENT_RESEARCH - short_name = "Sci" - color = "#A65BA6" - sorting_order = 3 - -/datum/department/cargo - name = DEPARTMENT_CARGO - short_name = "Car" - color = "#BB9040" - sorting_order = 2 - -/datum/department/civilian - name = DEPARTMENT_CIVILIAN - short_name = "Civ" - color = "#A32800" - sorting_order = 1 - -// Mostly for if someone wanted to rewrite manifest code to be map-agnostic. -/datum/department/misc - name = "Miscellaneous" - short_name = "Misc" - color = "#666666" - sorting_order = 0 - assignable = FALSE - -/datum/department/synthetic - name = DEPARTMENT_SYNTHETIC - short_name = "Bot" - color = "#222222" - sorting_order = -1 - assignable = FALSE - -// This one isn't very useful since no real centcom jobs exist yet. -// Instead the jobs like ERT are hardcoded in. -/datum/department/centcom - name = "Central Command" - short_name = "Centcom" - color = "#A52A2A" - sorting_order = 20 // Above Command. +// A datum that holds information about a specific department. +// It is held inside, and managed by, the SSjob subsystem automatically, +// just define a department, and put that department's name in one or more job datums' departments list. + +/datum/department + var/name = "NOPE" // Name used in UIs, and the index for the department assoc list in SSjob. + var/short_name = "NO" // Shorter name, used for things like external Topic() responses. + var/color = "#000000" // Color to use in UIs to represent this department. + var/list/jobs = list() // Assoc list. Key is the job title, and the value is a reference to the job datum. Populated by SSjob subsystem. + var/list/primary_jobs = list() // Same as above, but only jobs with their 'primary' department are put here. Primary being the first department in their list. + var/sorting_order = 0 // Used to sort departments, e.g. Command always being on top. + var/visible = TRUE // If false, it should not show up on things like the manifest or ID computer. + var/assignable = TRUE // Similar for above, but only for ID computers and such. Used for silicon department. + var/centcom_only = FALSE + +/datum/department/command + name = DEPARTMENT_COMMAND + short_name = "Heads" + color = "#3333FF" + sorting_order = 10 + +/datum/department/security + name = DEPARTMENT_SECURITY + short_name = "Sec" + color = "#8E0000" + sorting_order = 6 + +/datum/department/engineering + name = DEPARTMENT_ENGINEERING + short_name = "Eng" + color = "#B27300" + sorting_order = 5 + +/datum/department/medical + name = DEPARTMENT_MEDICAL + short_name = "Med" + color = "#006600" + sorting_order = 4 + +/datum/department/research + name = DEPARTMENT_RESEARCH + short_name = "Sci" + color = "#A65BA6" + sorting_order = 3 + +/datum/department/cargo + name = DEPARTMENT_CARGO + short_name = "Car" + color = "#BB9040" + sorting_order = 2 + +/datum/department/civilian + name = DEPARTMENT_CIVILIAN + short_name = "Civ" + color = "#A32800" + sorting_order = 1 + +// Mostly for if someone wanted to rewrite manifest code to be map-agnostic. +/datum/department/misc + name = "Miscellaneous" + short_name = "Misc" + color = "#666666" + sorting_order = 0 + assignable = FALSE + +/datum/department/synthetic + name = DEPARTMENT_SYNTHETIC + short_name = "Bot" + color = "#222222" + sorting_order = -1 + assignable = FALSE + +// This one isn't very useful since no real centcom jobs exist yet. +// Instead the jobs like ERT are hardcoded in. +/datum/department/centcom + name = "Central Command" + short_name = "Centcom" + color = "#A52A2A" + sorting_order = 20 // Above Command. centcom_only = TRUE \ No newline at end of file diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index 780f99a76fb..cd8729e702c 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -1,105 +1,105 @@ -////////////////////////////////// -// Chief Engineer -////////////////////////////////// -/datum/job/chief_engineer - title = "Chief Engineer" - flag = CHIEF - departments_managed = list(DEPARTMENT_ENGINEERING) - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_COMMAND) - sorting_order = 2 - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Site Manager" - selection_color = "#7F6E2C" - req_admin_notify = 1 - economic_modifier = 10 - - minimum_character_age = 25 - min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) - ideal_character_age = 50 - ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) - banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") - - - access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, - access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, - access_heads, access_construction, access_sec_doors, - access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_ai_upload) - minimal_access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, - access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, - access_heads, access_construction, access_sec_doors, - access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_ai_upload) - minimal_player_age = 7 - - outfit_type = /decl/hierarchy/outfit/job/engineering/chief_engineer - job_description = "The Chief Engineer manages the Engineering Department, ensuring that the Engineers work on what needs to be done, handling distribution \ - of manpower as much as they handle hands-on operations and repairs. They are also expected to keep the rest of the station informed of \ - any structural threats to the station that may be hazardous to health or disruptive to work." - -////////////////////////////////// -// Engineer -////////////////////////////////// -/datum/job/engineer - title = "Engineer" - flag = ENGINEER - departments = list(DEPARTMENT_ENGINEERING) - department_flag = ENGSEC - faction = "Station" - total_positions = 5 - spawn_positions = 5 - supervisors = "the Chief Engineer" - selection_color = "#5B4D20" - economic_modifier = 5 - access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics) - minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction) - alt_titles = list("Maintenance Technician" = /datum/alt_title/maint_tech, - "Engine Technician" = /datum/alt_title/engine_tech, "Electrician" = /datum/alt_title/electrician) - - minimal_player_age = 3 - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - - outfit_type = /decl/hierarchy/outfit/job/engineering/engineer - job_description = "An Engineer keeps the station running. They repair damages, keep the atmosphere stable, and ensure that power is being \ - generated and distributed. On quiet shifts, they may be called upon to make cosmetic alterations to the station." - -// Engineer Alt Titles -/datum/alt_title/maint_tech - title = "Maintenance Technician" - title_blurb = "A Maintenance Technician is generally a junior Engineer, and can be expected to run the mildly unpleasant or boring tasks that other \ - Engineers don't care to do." - -/datum/alt_title/engine_tech - title = "Engine Technician" - title_blurb = "An Engine Technician tends to the engine, most commonly a Supermatter crystal. They are expected to be able to keep it stable, and \ - possibly even run it beyond normal tolerances." - -/datum/alt_title/electrician - title = "Electrician" - title_blurb = "An Electrician's primary duty is making sure power is properly distributed thoughout the station, utilizing solars, substations, and other \ - methods to ensure every department has power in an emergency." - -////////////////////////////////// -// Atmos Tech -////////////////////////////////// -/datum/job/atmos - title = "Atmospheric Technician" - flag = ATMOSTECH - departments = list(DEPARTMENT_ENGINEERING) - department_flag = ENGSEC - faction = "Station" - total_positions = 3 - spawn_positions = 2 - supervisors = "the Chief Engineer" - selection_color = "#5B4D20" - economic_modifier = 5 - access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_external_airlocks) - minimal_access = list(access_eva, access_engine, access_atmospherics, access_maint_tunnels, access_emergency_storage, access_construction, access_external_airlocks) - - minimal_player_age = 3 - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - - outfit_type = /decl/hierarchy/outfit/job/engineering/atmos - job_description = "An Atmospheric Technician is primarily concerned with keeping the station's atmosphere breathable. They are expected to have a good \ - understanding of the pipes, vents, and scrubbers that move gasses around the station, and to be familiar with proper firefighting procedure." +////////////////////////////////// +// Chief Engineer +////////////////////////////////// +/datum/job/chief_engineer + title = "Chief Engineer" + flag = CHIEF + departments_managed = list(DEPARTMENT_ENGINEERING) + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_COMMAND) + sorting_order = 2 + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Site Manager" + selection_color = "#7F6E2C" + req_admin_notify = 1 + economic_modifier = 10 + + minimum_character_age = 25 + min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) + ideal_character_age = 50 + ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) + banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") + + + access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, + access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, + access_heads, access_construction, access_sec_doors, + access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_ai_upload) + minimal_access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, + access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, + access_heads, access_construction, access_sec_doors, + access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_ai_upload) + minimal_player_age = 7 + + outfit_type = /decl/hierarchy/outfit/job/engineering/chief_engineer + job_description = "The Chief Engineer manages the Engineering Department, ensuring that the Engineers work on what needs to be done, handling distribution \ + of manpower as much as they handle hands-on operations and repairs. They are also expected to keep the rest of the station informed of \ + any structural threats to the station that may be hazardous to health or disruptive to work." + +////////////////////////////////// +// Engineer +////////////////////////////////// +/datum/job/engineer + title = "Engineer" + flag = ENGINEER + departments = list(DEPARTMENT_ENGINEERING) + department_flag = ENGSEC + faction = "Station" + total_positions = 5 + spawn_positions = 5 + supervisors = "the Chief Engineer" + selection_color = "#5B4D20" + economic_modifier = 5 + access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics) + minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction) + alt_titles = list("Maintenance Technician" = /datum/alt_title/maint_tech, + "Engine Technician" = /datum/alt_title/engine_tech, "Electrician" = /datum/alt_title/electrician) + + minimal_player_age = 3 + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + + outfit_type = /decl/hierarchy/outfit/job/engineering/engineer + job_description = "An Engineer keeps the station running. They repair damages, keep the atmosphere stable, and ensure that power is being \ + generated and distributed. On quiet shifts, they may be called upon to make cosmetic alterations to the station." + +// Engineer Alt Titles +/datum/alt_title/maint_tech + title = "Maintenance Technician" + title_blurb = "A Maintenance Technician is generally a junior Engineer, and can be expected to run the mildly unpleasant or boring tasks that other \ + Engineers don't care to do." + +/datum/alt_title/engine_tech + title = "Engine Technician" + title_blurb = "An Engine Technician tends to the engine, most commonly a Supermatter crystal. They are expected to be able to keep it stable, and \ + possibly even run it beyond normal tolerances." + +/datum/alt_title/electrician + title = "Electrician" + title_blurb = "An Electrician's primary duty is making sure power is properly distributed thoughout the station, utilizing solars, substations, and other \ + methods to ensure every department has power in an emergency." + +////////////////////////////////// +// Atmos Tech +////////////////////////////////// +/datum/job/atmos + title = "Atmospheric Technician" + flag = ATMOSTECH + departments = list(DEPARTMENT_ENGINEERING) + department_flag = ENGSEC + faction = "Station" + total_positions = 3 + spawn_positions = 2 + supervisors = "the Chief Engineer" + selection_color = "#5B4D20" + economic_modifier = 5 + access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_external_airlocks) + minimal_access = list(access_eva, access_engine, access_atmospherics, access_maint_tunnels, access_emergency_storage, access_construction, access_external_airlocks) + + minimal_player_age = 3 + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + + outfit_type = /decl/hierarchy/outfit/job/engineering/atmos + job_description = "An Atmospheric Technician is primarily concerned with keeping the station's atmosphere breathable. They are expected to have a good \ + understanding of the pipes, vents, and scrubbers that move gasses around the station, and to be familiar with proper firefighting procedure." diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 443b188b60f..269f9dd18da 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -1,186 +1,186 @@ -/datum/job - - //The name of the job - var/title = "NOPE" - //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access - var/list/minimal_access = list() // Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) - var/list/access = list() // Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - var/flag = 0 // Bitflags for the job - var/department_flag = 0 - var/faction = "None" // Players will be allowed to spawn in as jobs that are set to "Station" - var/total_positions = 0 // How many players can be this job - var/spawn_positions = 0 // How many players can spawn in as this job - var/current_positions = 0 // How many players have this job - var/supervisors = null // Supervisors, who this person answers to directly - var/selection_color = "#ffffff" // Selection screen color - var/list/alt_titles = null // List of alternate titles; There is no need for an alt-title datum for the base job title. - var/req_admin_notify // If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. - var/minimal_player_age = 0 // If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) - var/list/departments = list() // List of departments this job belongs to, if any. The first one on the list will be the 'primary' department. - var/sorting_order = 0 // Used for sorting jobs so boss jobs go above regular ones, and their boss's boss is above that. Higher numbers = higher in sorting. - var/departments_managed = null // Is this a management position? If yes, list of departments managed. Otherwise null. - var/department_accounts = null // Which department accounts should people with this position be given the pin for? - var/assignable = TRUE // Should it show up on things like the ID computer? - var/minimum_character_age = 0 - var/list/min_age_by_species = null - var/ideal_character_age = 30 - var/list/ideal_age_by_species = null - var/list/banned_job_species = null - var/has_headset = TRUE //Do people with this job need to be given headsets and told how to use them? E.g. Cyborgs don't. - - var/account_allowed = 1 // Does this job type come with a station account? - var/economic_modifier = 2 // With how much does this job modify the initial account amount? - - var/outfit_type // What outfit datum does this job use in its default title? - - var/offmap_spawn = FALSE // Do we require weird and special spawning and datacore handling? - var/mob_type = JOB_CARBON // Bitflags representing mob type this job spawns - - // Description of the job's role and minimum responsibilities. - var/job_description = "This Job doesn't have a description! Please report it!" - -/datum/job/New() - . = ..() - department_accounts = department_accounts || departments_managed - -/datum/job/proc/equip(var/mob/living/carbon/human/H, var/alt_title) - var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) - if(!outfit) - return FALSE - . = outfit.equip(H, title, alt_title) - return 1 - -/datum/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title) - if(alt_title && alt_titles) - var/datum/alt_title/A = alt_titles[alt_title] - if(A && initial(A.title_outfit)) - . = initial(A.title_outfit) - . = . || outfit_type - . = outfit_by_type(.) - -/datum/job/proc/setup_account(var/mob/living/carbon/human/H) - if(!account_allowed || (H.mind && H.mind.initial_account)) - return - - var/income = 1 - if(H.client) - switch(H.client.prefs.economic_status) - if(CLASS_UPPER) income = 1.30 - if(CLASS_UPMID) income = 1.15 - if(CLASS_MIDDLE) income = 1 - if(CLASS_LOWMID) income = 0.75 - if(CLASS_LOWER) income = 0.50 - if(CLASS_BROKE) income = 0 //VOREStation Add - Rent's not cheap - - //give them an account in the station database - var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks, ECO_MODIFIER rather than per-species ones. - var/datum/money_account/M = create_account(H.real_name, money_amount, null, offmap_spawn) - if(H.mind) - var/remembered_info = "" - remembered_info += "Your account number is: #[M.account_number]
    " - remembered_info += "Your account pin is: [M.remote_access_pin]
    " - remembered_info += "Your account funds are: $[M.money]
    " - - if(M.transaction_log.len) - var/datum/transaction/T = M.transaction_log[1] - remembered_info += "Your account was created: [T.time], [T.date] at [T.source_terminal]
    " - H.mind.store_memory(remembered_info) - - H.mind.initial_account = M - - to_chat(H, "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]") - -// overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel() -/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) - var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) - if(!outfit) - return FALSE - . = outfit.equip_base(H, title, alt_title) - -/datum/job/proc/get_access() - if(!config || config.jobs_have_minimal_access) - return src.minimal_access.Copy() - else - return src.access.Copy() - -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/job/proc/player_old_enough(client/C) - return (available_in_days(C) == 0) //Available in 0 days = available right now = player is old enough to play. - -/datum/job/proc/available_in_days(client/C) - if(C && config.use_age_restriction_for_jobs && isnum(C.player_age) && isnum(minimal_player_age)) - return max(0, minimal_player_age - C.player_age) - return 0 - -/datum/job/proc/apply_fingerprints(var/mob/living/carbon/human/target) - if(!istype(target)) - return 0 - for(var/obj/item/item in target.contents) - apply_fingerprints_to_item(target, item) - return 1 - -/datum/job/proc/apply_fingerprints_to_item(var/mob/living/carbon/human/holder, var/obj/item/item) - item.add_fingerprint(holder,1) - if(item.contents.len) - for(var/obj/item/sub_item in item.contents) - apply_fingerprints_to_item(holder, sub_item) - -/datum/job/proc/is_position_available() - return (current_positions < total_positions) || (total_positions == -1) - -/datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title) - return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) - -/datum/job/proc/get_description_blurb(var/alt_title) - var/list/message = list() - message |= job_description - - if(alt_title && alt_titles) - var/typepath = alt_titles[alt_title] - if(typepath) - var/datum/alt_title/A = new typepath() - if(A.title_blurb) - message |= A.title_blurb - return message - -/datum/job/proc/get_job_icon() - if(!job_master.job_icons[title]) - var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#job_icon") - dress_mannequin(mannequin) - mannequin.dir = SOUTH - mannequin.ImmediateOverlayUpdate() - var/icon/preview_icon = getFlatIcon(mannequin) - - preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. - job_master.job_icons[title] = preview_icon - - return job_master.job_icons[title] - -/datum/job/proc/dress_mannequin(var/mob/living/carbon/human/dummy/mannequin/mannequin) - mannequin.delete_inventory(TRUE) - equip_preview(mannequin) - if(mannequin.back) - var/obj/O = mannequin.back - mannequin.drop_from_inventory(O) - qdel(O) - -///Assigns minimum age by race & brain type. Code says Positronic = mechanical and Drone = digital because nothing can be simple. -///Will first check based on brain type, then based on species. -/datum/job/proc/get_min_age(species_name, brain_type) - return minimum_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species - //return (brain_type && LAZYACCESS(min_age_by_species, brain_type)) || LAZYACCESS(min_age_by_species, species_name) || minimum_character_age //VOREStation Removal - -/datum/job/proc/get_ideal_age(species_name, brain_type) - return ideal_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species - //return (brain_type && LAZYACCESS(ideal_age_by_species, brain_type)) || LAZYACCESS(ideal_age_by_species, brain_type) || ideal_character_age //VOREStation Removal - -/datum/job/proc/is_species_banned(species_name, brain_type) - return FALSE // VOREStation Edit - Any species can be any job. - /* VOREStation Removal - if(banned_job_species == null) - return - if(species_name in banned_job_species) - return TRUE - if(brain_type in banned_job_species) - return TRUE - */ +/datum/job + + //The name of the job + var/title = "NOPE" + //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access + var/list/minimal_access = list() // Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) + var/list/access = list() // Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) + var/flag = 0 // Bitflags for the job + var/department_flag = 0 + var/faction = "None" // Players will be allowed to spawn in as jobs that are set to "Station" + var/total_positions = 0 // How many players can be this job + var/spawn_positions = 0 // How many players can spawn in as this job + var/current_positions = 0 // How many players have this job + var/supervisors = null // Supervisors, who this person answers to directly + var/selection_color = "#ffffff" // Selection screen color + var/list/alt_titles = null // List of alternate titles; There is no need for an alt-title datum for the base job title. + var/req_admin_notify // If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. + var/minimal_player_age = 0 // If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) + var/list/departments = list() // List of departments this job belongs to, if any. The first one on the list will be the 'primary' department. + var/sorting_order = 0 // Used for sorting jobs so boss jobs go above regular ones, and their boss's boss is above that. Higher numbers = higher in sorting. + var/departments_managed = null // Is this a management position? If yes, list of departments managed. Otherwise null. + var/department_accounts = null // Which department accounts should people with this position be given the pin for? + var/assignable = TRUE // Should it show up on things like the ID computer? + var/minimum_character_age = 0 + var/list/min_age_by_species = null + var/ideal_character_age = 30 + var/list/ideal_age_by_species = null + var/list/banned_job_species = null + var/has_headset = TRUE //Do people with this job need to be given headsets and told how to use them? E.g. Cyborgs don't. + + var/account_allowed = 1 // Does this job type come with a station account? + var/economic_modifier = 2 // With how much does this job modify the initial account amount? + + var/outfit_type // What outfit datum does this job use in its default title? + + var/offmap_spawn = FALSE // Do we require weird and special spawning and datacore handling? + var/mob_type = JOB_CARBON // Bitflags representing mob type this job spawns + + // Description of the job's role and minimum responsibilities. + var/job_description = "This Job doesn't have a description! Please report it!" + +/datum/job/New() + . = ..() + department_accounts = department_accounts || departments_managed + +/datum/job/proc/equip(var/mob/living/carbon/human/H, var/alt_title) + var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) + if(!outfit) + return FALSE + . = outfit.equip(H, title, alt_title) + return 1 + +/datum/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title) + if(alt_title && alt_titles) + var/datum/alt_title/A = alt_titles[alt_title] + if(A && initial(A.title_outfit)) + . = initial(A.title_outfit) + . = . || outfit_type + . = outfit_by_type(.) + +/datum/job/proc/setup_account(var/mob/living/carbon/human/H) + if(!account_allowed || (H.mind && H.mind.initial_account)) + return + + var/income = 1 + if(H.client) + switch(H.client.prefs.economic_status) + if(CLASS_UPPER) income = 1.30 + if(CLASS_UPMID) income = 1.15 + if(CLASS_MIDDLE) income = 1 + if(CLASS_LOWMID) income = 0.75 + if(CLASS_LOWER) income = 0.50 + if(CLASS_BROKE) income = 0 //VOREStation Add - Rent's not cheap + + //give them an account in the station database + var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks, ECO_MODIFIER rather than per-species ones. + var/datum/money_account/M = create_account(H.real_name, money_amount, null, offmap_spawn) + if(H.mind) + var/remembered_info = "" + remembered_info += "Your account number is: #[M.account_number]
    " + remembered_info += "Your account pin is: [M.remote_access_pin]
    " + remembered_info += "Your account funds are: $[M.money]
    " + + if(M.transaction_log.len) + var/datum/transaction/T = M.transaction_log[1] + remembered_info += "Your account was created: [T.time], [T.date] at [T.source_terminal]
    " + H.mind.store_memory(remembered_info) + + H.mind.initial_account = M + + to_chat(H, "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]") + +// overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel() +/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) + var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) + if(!outfit) + return FALSE + . = outfit.equip_base(H, title, alt_title) + +/datum/job/proc/get_access() + if(!config || config.jobs_have_minimal_access) + return src.minimal_access.Copy() + else + return src.access.Copy() + +//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 +/datum/job/proc/player_old_enough(client/C) + return (available_in_days(C) == 0) //Available in 0 days = available right now = player is old enough to play. + +/datum/job/proc/available_in_days(client/C) + if(C && config.use_age_restriction_for_jobs && isnum(C.player_age) && isnum(minimal_player_age)) + return max(0, minimal_player_age - C.player_age) + return 0 + +/datum/job/proc/apply_fingerprints(var/mob/living/carbon/human/target) + if(!istype(target)) + return 0 + for(var/obj/item/item in target.contents) + apply_fingerprints_to_item(target, item) + return 1 + +/datum/job/proc/apply_fingerprints_to_item(var/mob/living/carbon/human/holder, var/obj/item/item) + item.add_fingerprint(holder,1) + if(item.contents.len) + for(var/obj/item/sub_item in item.contents) + apply_fingerprints_to_item(holder, sub_item) + +/datum/job/proc/is_position_available() + return (current_positions < total_positions) || (total_positions == -1) + +/datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title) + return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) + +/datum/job/proc/get_description_blurb(var/alt_title) + var/list/message = list() + message |= job_description + + if(alt_title && alt_titles) + var/typepath = alt_titles[alt_title] + if(typepath) + var/datum/alt_title/A = new typepath() + if(A.title_blurb) + message |= A.title_blurb + return message + +/datum/job/proc/get_job_icon() + if(!job_master.job_icons[title]) + var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#job_icon") + dress_mannequin(mannequin) + mannequin.dir = SOUTH + mannequin.ImmediateOverlayUpdate() + var/icon/preview_icon = getFlatIcon(mannequin) + + preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. + job_master.job_icons[title] = preview_icon + + return job_master.job_icons[title] + +/datum/job/proc/dress_mannequin(var/mob/living/carbon/human/dummy/mannequin/mannequin) + mannequin.delete_inventory(TRUE) + equip_preview(mannequin) + if(mannequin.back) + var/obj/O = mannequin.back + mannequin.drop_from_inventory(O) + qdel(O) + +///Assigns minimum age by race & brain type. Code says Positronic = mechanical and Drone = digital because nothing can be simple. +///Will first check based on brain type, then based on species. +/datum/job/proc/get_min_age(species_name, brain_type) + return minimum_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species + //return (brain_type && LAZYACCESS(min_age_by_species, brain_type)) || LAZYACCESS(min_age_by_species, species_name) || minimum_character_age //VOREStation Removal + +/datum/job/proc/get_ideal_age(species_name, brain_type) + return ideal_character_age // VOREStation Edit - Minimum character age by rules is 18, return default which is standard for all species + //return (brain_type && LAZYACCESS(ideal_age_by_species, brain_type)) || LAZYACCESS(ideal_age_by_species, brain_type) || ideal_character_age //VOREStation Removal + +/datum/job/proc/is_species_banned(species_name, brain_type) + return FALSE // VOREStation Edit - Any species can be any job. + /* VOREStation Removal + if(banned_job_species == null) + return + if(species_name in banned_job_species) + return TRUE + if(brain_type in banned_job_species) + return TRUE + */ diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 7594ade21fb..f896bf978f2 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,204 +1,204 @@ -////////////////////////////////// -// Chief Medical Officer -////////////////////////////////// -/datum/job/cmo - title = "Chief Medical Officer" - flag = CMO - departments_managed = list(DEPARTMENT_MEDICAL) - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_COMMAND) - sorting_order = 2 - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Site Manager" - selection_color = "#026865" - req_admin_notify = 1 - economic_modifier = 10 - access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) - - minimum_character_age = 25 - min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) - minimal_player_age = 10 - ideal_character_age = 50 - ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) - banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") - - outfit_type = /decl/hierarchy/outfit/job/medical/cmo - job_description = "The CMO manages the Medical department and is a position requiring experience and skill; their goal is to ensure that their \ - staff keep the station's crew healthy and whole. They are primarily interested in making sure that patients are safely found and \ - transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \ - about the importance of Suit Sensors." - -////////////////////////////////// -// Medical Doctor -////////////////////////////////// -/datum/job/doctor - title = "Medical Doctor" - flag = DOCTOR - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 5 - spawn_positions = 3 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - economic_modifier = 7 - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_eva) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva) - outfit_type = /decl/hierarchy/outfit/job/medical/doctor - job_description = "A Medical Doctor is a Jack-of-All-Trades Medical title, covering a variety of skill levels and minor specializations. They are likely \ - familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \ - patients they encounter." - alt_titles = list( - "Surgeon" = /datum/alt_title/surgeon, - "Emergency Physician" = /datum/alt_title/emergency_physician, - "Nurse" = /datum/alt_title/nurse, - "Virologist" = /datum/alt_title/virologist) - - min_age_by_species = list(SPECIES_PROMETHEAN = 3) - -//Medical Doctor Alt Titles -/datum/alt_title/surgeon - title = "Surgeon" - title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \ - to know the ins and outs of anesthesia and surgery." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/surgeon - -/datum/alt_title/emergency_physician - title = "Emergency Physician" - title_blurb = "An Emergency Physician is a Medical professional trained for stabilizing and treating severely injured and/or dying patients. \ - They are generally the first response for any such individual brought to the Medbay, and can sometimes be expected to help their patients \ - make a full recovery." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/emergency_physician - -/datum/alt_title/nurse - title = "Nurse" - title_blurb = "A Nurse acts as a general purpose Doctor's Aide, providing basic care to non-critical patients, and stabilizing critical patients during \ - busy periods. They frequently watch the suit sensors console, to help manage the time of other Doctors. In rare occasions, a Nurse can be \ - called upon to revive deceased crew members." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/nurse - -/datum/alt_title/virologist - title = "Virologist" - title_blurb = "A Virologist cures active diseases in the crew, and prepares antibodies for possible infections. They also have the skills \ - to produce the various types of virus foods or mutagens." - title_outfit = /decl/hierarchy/outfit/job/medical/doctor/virologist - -//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro -////////////////////////////////// -// Chemist -////////////////////////////////// -/datum/job/chemist - title = "Chemist" - flag = CHEMIST - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - economic_modifier = 5 - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) - minimal_access = list(access_medical, access_medical_equip, access_chemistry) - minimal_player_age = 3 - min_age_by_species = list(SPECIES_PROMETHEAN = 3) - - outfit_type = /decl/hierarchy/outfit/job/medical/chemist - job_description = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \ - They are likely to know the use and dangers of many lab-produced chemicals." - alt_titles = list("Pharmacist" = /datum/alt_title/pharmacist) - -// Chemist Alt Titles -/datum/alt_title/pharmacist - title = "Pharmacist" - title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion." - -/* I'm commenting out Geneticist so you can't actually see it in the job menu, given that you can't play as one - Jon. -////////////////////////////////// -// Geneticist -////////////////////////////////// -/datum/job/geneticist - title = "Geneticist" - flag = GENETICIST - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH) - department_flag = MEDSCI - faction = "Station" - total_positions = 0 - spawn_positions = 0 - supervisors = "the Chief Medical Officer and Research Director" - selection_color = "#013D3B" - economic_modifier = 7 - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research) - minimal_access = list(access_medical, access_morgue, access_genetics, access_research) - - outfit_type = /decl/hierarchy/outfit/job/medical/geneticist - job_description = "A Geneticist operates genetic manipulation equipment to repair any genetic defects encountered in crew, from cloning or radiation as examples. \ - When required, geneticists have the skills to clone, and are the superior choice when available for doing so." -*/ - -////////////////////////////////// -// Psychiatrist -////////////////////////////////// -/datum/job/psychiatrist - title = "Psychiatrist" - flag = PSYCHIATRIST - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - economic_modifier = 5 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - access = list(access_medical, access_medical_equip, access_morgue, access_psychiatrist) - minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) - outfit_type = /decl/hierarchy/outfit/job/medical/psychiatrist - job_description = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \ - ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs." - alt_titles = list("Psychologist" = /datum/alt_title/psychologist) - banned_job_species = list(SPECIES_PROMETHEAN, SPECIES_DIONA) - -//Psychiatrist Alt Titles -/datum/alt_title/psychologist - title = "Psychologist" - title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \ - called upon to determine whatever ails the mentally unwell, frequently under Security supervision." - title_outfit = /decl/hierarchy/outfit/job/medical/psychiatrist/psychologist - -////////////////////////////////// -// Paramedic -////////////////////////////////// -/datum/job/paramedic - title = "Paramedic" - flag = PARAMEDIC - departments = list(DEPARTMENT_MEDICAL) - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Chief Medical Officer" - selection_color = "#013D3B" - economic_modifier = 4 - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks) - outfit_type = /decl/hierarchy/outfit/job/medical/paramedic - job_description = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \ - They may also be called upon to keep patients stable when Medical is busy or understaffed." - alt_titles = list("Emergency Medical Technician" = /datum/alt_title/emt) - banned_job_species = list(SPECIES_DIONA) - - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - -// Paramedic Alt Titles -/datum/alt_title/emt - title = "Emergency Medical Technician" - title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \ - own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training." +////////////////////////////////// +// Chief Medical Officer +////////////////////////////////// +/datum/job/cmo + title = "Chief Medical Officer" + flag = CMO + departments_managed = list(DEPARTMENT_MEDICAL) + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_COMMAND) + sorting_order = 2 + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Site Manager" + selection_color = "#026865" + req_admin_notify = 1 + economic_modifier = 10 + access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, + access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, + access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, + access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, + access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_maint_tunnels) + + minimum_character_age = 25 + min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) + minimal_player_age = 10 + ideal_character_age = 50 + ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) + banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") + + outfit_type = /decl/hierarchy/outfit/job/medical/cmo + job_description = "The CMO manages the Medical department and is a position requiring experience and skill; their goal is to ensure that their \ + staff keep the station's crew healthy and whole. They are primarily interested in making sure that patients are safely found and \ + transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \ + about the importance of Suit Sensors." + +////////////////////////////////// +// Medical Doctor +////////////////////////////////// +/datum/job/doctor + title = "Medical Doctor" + flag = DOCTOR + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 5 + spawn_positions = 3 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + economic_modifier = 7 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_eva) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva) + outfit_type = /decl/hierarchy/outfit/job/medical/doctor + job_description = "A Medical Doctor is a Jack-of-All-Trades Medical title, covering a variety of skill levels and minor specializations. They are likely \ + familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \ + patients they encounter." + alt_titles = list( + "Surgeon" = /datum/alt_title/surgeon, + "Emergency Physician" = /datum/alt_title/emergency_physician, + "Nurse" = /datum/alt_title/nurse, + "Virologist" = /datum/alt_title/virologist) + + min_age_by_species = list(SPECIES_PROMETHEAN = 3) + +//Medical Doctor Alt Titles +/datum/alt_title/surgeon + title = "Surgeon" + title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \ + to know the ins and outs of anesthesia and surgery." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/surgeon + +/datum/alt_title/emergency_physician + title = "Emergency Physician" + title_blurb = "An Emergency Physician is a Medical professional trained for stabilizing and treating severely injured and/or dying patients. \ + They are generally the first response for any such individual brought to the Medbay, and can sometimes be expected to help their patients \ + make a full recovery." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/emergency_physician + +/datum/alt_title/nurse + title = "Nurse" + title_blurb = "A Nurse acts as a general purpose Doctor's Aide, providing basic care to non-critical patients, and stabilizing critical patients during \ + busy periods. They frequently watch the suit sensors console, to help manage the time of other Doctors. In rare occasions, a Nurse can be \ + called upon to revive deceased crew members." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/nurse + +/datum/alt_title/virologist + title = "Virologist" + title_blurb = "A Virologist cures active diseases in the crew, and prepares antibodies for possible infections. They also have the skills \ + to produce the various types of virus foods or mutagens." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/virologist + +//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro +////////////////////////////////// +// Chemist +////////////////////////////////// +/datum/job/chemist + title = "Chemist" + flag = CHEMIST + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + economic_modifier = 5 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) + minimal_access = list(access_medical, access_medical_equip, access_chemistry) + minimal_player_age = 3 + min_age_by_species = list(SPECIES_PROMETHEAN = 3) + + outfit_type = /decl/hierarchy/outfit/job/medical/chemist + job_description = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \ + They are likely to know the use and dangers of many lab-produced chemicals." + alt_titles = list("Pharmacist" = /datum/alt_title/pharmacist) + +// Chemist Alt Titles +/datum/alt_title/pharmacist + title = "Pharmacist" + title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion." + +/* I'm commenting out Geneticist so you can't actually see it in the job menu, given that you can't play as one - Jon. +////////////////////////////////// +// Geneticist +////////////////////////////////// +/datum/job/geneticist + title = "Geneticist" + flag = GENETICIST + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH) + department_flag = MEDSCI + faction = "Station" + total_positions = 0 + spawn_positions = 0 + supervisors = "the Chief Medical Officer and Research Director" + selection_color = "#013D3B" + economic_modifier = 7 + access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research) + minimal_access = list(access_medical, access_morgue, access_genetics, access_research) + + outfit_type = /decl/hierarchy/outfit/job/medical/geneticist + job_description = "A Geneticist operates genetic manipulation equipment to repair any genetic defects encountered in crew, from cloning or radiation as examples. \ + When required, geneticists have the skills to clone, and are the superior choice when available for doing so." +*/ + +////////////////////////////////// +// Psychiatrist +////////////////////////////////// +/datum/job/psychiatrist + title = "Psychiatrist" + flag = PSYCHIATRIST + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + economic_modifier = 5 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + access = list(access_medical, access_medical_equip, access_morgue, access_psychiatrist) + minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) + outfit_type = /decl/hierarchy/outfit/job/medical/psychiatrist + job_description = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \ + ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs." + alt_titles = list("Psychologist" = /datum/alt_title/psychologist) + banned_job_species = list(SPECIES_PROMETHEAN, SPECIES_DIONA) + +//Psychiatrist Alt Titles +/datum/alt_title/psychologist + title = "Psychologist" + title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \ + called upon to determine whatever ails the mentally unwell, frequently under Security supervision." + title_outfit = /decl/hierarchy/outfit/job/medical/psychiatrist/psychologist + +////////////////////////////////// +// Paramedic +////////////////////////////////// +/datum/job/paramedic + title = "Paramedic" + flag = PARAMEDIC + departments = list(DEPARTMENT_MEDICAL) + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Chief Medical Officer" + selection_color = "#013D3B" + economic_modifier = 4 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks) + outfit_type = /decl/hierarchy/outfit/job/medical/paramedic + job_description = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \ + They may also be called upon to keep patients stable when Medical is busy or understaffed." + alt_titles = list("Emergency Medical Technician" = /datum/alt_title/emt) + banned_job_species = list(SPECIES_DIONA) + + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + +// Paramedic Alt Titles +/datum/alt_title/emt + title = "Emergency Medical Technician" + title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \ + own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training." title_outfit = /decl/hierarchy/outfit/job/medical/paramedic/emt \ No newline at end of file diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index c8df404b13d..240b985967c 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -1,159 +1,159 @@ -////////////////////////////////// -// Research Director -////////////////////////////////// -/datum/job/rd - title = "Research Director" - flag = RD - departments_managed = list(DEPARTMENT_RESEARCH) - departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_COMMAND) - sorting_order = 2 - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Site Manager" - selection_color = "#AD6BAD" - req_admin_notify = 1 - economic_modifier = 15 - access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, - access_tox_storage, access_teleporter, access_sec_doors, - access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) - minimal_access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, - access_tox_storage, access_teleporter, access_sec_doors, - access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) - - minimum_character_age = 25 - minimal_player_age = 14 - min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) - ideal_character_age = 50 - ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) - banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") - - outfit_type = /decl/hierarchy/outfit/job/science/rd - job_description = "The Research Director manages and maintains the Research department. They are required to ensure the safety of the entire crew, \ - at least with regards to anything occurring in the Research department, and to inform the crew of any disruptions that \ - might originate from Research. The Research Director often has at least passing knowledge of most of the Research department, but \ - are encouraged to allow their staff to perform their own duties." - alt_titles = list("Research Supervisor" = /datum/alt_title/research_supervisor) - - -// Research Director Alt Titles -/datum/alt_title/research_supervisor - title = "Research Supervisor" - -////////////////////////////////// -// Scientist -////////////////////////////////// -/datum/job/scientist - title = "Scientist" - flag = SCIENTIST - departments = list(DEPARTMENT_RESEARCH) - department_flag = MEDSCI - faction = "Station" - total_positions = 5 - spawn_positions = 3 - supervisors = "the Research Director" - selection_color = "#633D63" - economic_modifier = 7 - access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch) - minimal_access = list(access_tox, access_tox_storage, access_research, access_xenoarch) - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - banned_job_species = list("digital") - - minimal_player_age = 14 - - outfit_type = /decl/hierarchy/outfit/job/science/scientist - job_description = "A Scientist is a generalist working in the Research department, with general knowledge of the scientific process, as well as \ - the principles and requirements of Research and Development. They may also formulate experiments of their own devising, if \ - they find an appropriate topic." - alt_titles = list("Xenoarchaeologist" = /datum/alt_title/xenoarch, "Anomalist" = /datum/alt_title/anomalist, \ - "Phoron Researcher" = /datum/alt_title/phoron_research) - -// Scientist Alt Titles -/datum/alt_title/xenoarch - title = "Xenoarchaeologist" - title_blurb = "A Xenoarchaeologist enters digsites in search of artifacts of alien origin. These digsites are frequently in vacuum or other inhospitable \ - locations, and as such a Xenoarchaeologist should be prepared to handle hostile evironmental conditions." - -/datum/alt_title/anomalist - title = "Anomalist" - title_blurb = "An Anomalist is a Scientist whose expertise is analyzing alien artifacts. They are familar with the most common methods of testing artifact \ - function. They work closely with Xenoarchaeologists, or Miners, if either role is present." - -/datum/alt_title/phoron_research - title = "Phoron Researcher" - title_blurb = "A Phoron Researcher is a specialist in the practical applications of phoron, and has knowledge of its practical uses and dangers. \ - Many Phoron Researchers are interested in the combustability and explosive properties of gaseous phoron, as well as the specific hazards \ - of working with the substance in that state." - -////////////////////////////////// -// Xenobiologist -////////////////////////////////// -/datum/job/xenobiologist - title = "Xenobiologist" - flag = XENOBIOLOGIST - departments = list(DEPARTMENT_RESEARCH) - department_flag = MEDSCI - faction = "Station" - total_positions = 3 - spawn_positions = 2 - supervisors = "the Research Director" - selection_color = "#633D63" - economic_modifier = 7 - access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_hydroponics) - minimal_access = list(access_research, access_xenobiology, access_hydroponics, access_tox_storage) - banned_job_species = list("digital") - - minimal_player_age = 14 - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - - outfit_type = /decl/hierarchy/outfit/job/science/xenobiologist - job_description = "A Xenobiologist studies esoteric lifeforms, usually in the relative safety of their lab. They attempt to find ways to benefit \ - from the byproducts of these lifeforms, and their main subject at present is the Giant Slime." -/*VR edit start - alt_titles = list("Xenobotanist" = /datum/alt_title/xenobot) - - Xenibiologist Alt Titles -/datum/alt_title/xenobot - title = "Xenobotanist" - title_blurb = "A Xenobotanist grows and cares for a variety of abnormal, custom made, and frequently dangerous plant life. When the products of these plants \ - is both safe and beneficial to the station, they may choose to introduce it to the rest of the crew." -VR edit end*/ - -////////////////////////////////// -// Roboticist -////////////////////////////////// -/datum/job/roboticist - title = "Roboticist" - flag = ROBOTICIST - departments = list(DEPARTMENT_RESEARCH) - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Research Director" - selection_color = "#633D63" - economic_modifier = 5 - access = list(access_robotics, access_tox, access_tox_storage, access_tech_storage, access_morgue, access_research) //As a job that handles so many corpses, it makes sense for them to have morgue access. - minimal_access = list(access_robotics, access_tech_storage, access_morgue, access_research) //As a job that handles so many corpses, it makes sense for them to have morgue access. - minimal_player_age = 7 - min_age_by_species = list(SPECIES_PROMETHEAN = 2) - banned_job_species = list("digital") - - outfit_type = /decl/hierarchy/outfit/job/science/roboticist - job_description = "A Roboticist maintains and repairs the station's synthetics, including crew with prosthetic limbs. \ - They can also assist the station by producing simple robots and even pilotable exosuits." - alt_titles = list("Biomechanical Engineer" = /datum/alt_title/biomech, "Mechatronic Engineer" = /datum/alt_title/mech_tech) - -// Roboticist Alt Titles -/datum/alt_title/biomech - title = "Biomechanical Engineer" - title_blurb = "A Biomechanical Engineer primarily works on prosthetics, and the organic parts attached to them. They may have some \ - knowledge of the relatively simple surgical procedures used in making cyborgs and attaching prosthesis." - -/datum/alt_title/mech_tech - title = "Mechatronic Engineer" - title_blurb = "A Mechatronic Engineer focuses on the construction and maintenance of Exosuits, and should be well versed in their use. \ - They may also be called upon to work on synthetics and prosthetics, if needed." +////////////////////////////////// +// Research Director +////////////////////////////////// +/datum/job/rd + title = "Research Director" + flag = RD + departments_managed = list(DEPARTMENT_RESEARCH) + departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_COMMAND) + sorting_order = 2 + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Site Manager" + selection_color = "#AD6BAD" + req_admin_notify = 1 + economic_modifier = 15 + access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, + access_tox_storage, access_teleporter, access_sec_doors, + access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) + minimal_access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, + access_tox_storage, access_teleporter, access_sec_doors, + access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) + + minimum_character_age = 25 + minimal_player_age = 14 + min_age_by_species = list(SPECIES_UNATHI = 70, "mechanical" = 10, SPECIES_HUMAN_VATBORN = 14) + ideal_character_age = 50 + ideal_age_by_species = list(SPECIES_UNATHI = 140, "mechanical" = 20, SPECIES_HUMAN_VATBORN = 20) + banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital") + + outfit_type = /decl/hierarchy/outfit/job/science/rd + job_description = "The Research Director manages and maintains the Research department. They are required to ensure the safety of the entire crew, \ + at least with regards to anything occurring in the Research department, and to inform the crew of any disruptions that \ + might originate from Research. The Research Director often has at least passing knowledge of most of the Research department, but \ + are encouraged to allow their staff to perform their own duties." + alt_titles = list("Research Supervisor" = /datum/alt_title/research_supervisor) + + +// Research Director Alt Titles +/datum/alt_title/research_supervisor + title = "Research Supervisor" + +////////////////////////////////// +// Scientist +////////////////////////////////// +/datum/job/scientist + title = "Scientist" + flag = SCIENTIST + departments = list(DEPARTMENT_RESEARCH) + department_flag = MEDSCI + faction = "Station" + total_positions = 5 + spawn_positions = 3 + supervisors = "the Research Director" + selection_color = "#633D63" + economic_modifier = 7 + access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch) + minimal_access = list(access_tox, access_tox_storage, access_research, access_xenoarch) + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + banned_job_species = list("digital") + + minimal_player_age = 14 + + outfit_type = /decl/hierarchy/outfit/job/science/scientist + job_description = "A Scientist is a generalist working in the Research department, with general knowledge of the scientific process, as well as \ + the principles and requirements of Research and Development. They may also formulate experiments of their own devising, if \ + they find an appropriate topic." + alt_titles = list("Xenoarchaeologist" = /datum/alt_title/xenoarch, "Anomalist" = /datum/alt_title/anomalist, \ + "Phoron Researcher" = /datum/alt_title/phoron_research) + +// Scientist Alt Titles +/datum/alt_title/xenoarch + title = "Xenoarchaeologist" + title_blurb = "A Xenoarchaeologist enters digsites in search of artifacts of alien origin. These digsites are frequently in vacuum or other inhospitable \ + locations, and as such a Xenoarchaeologist should be prepared to handle hostile evironmental conditions." + +/datum/alt_title/anomalist + title = "Anomalist" + title_blurb = "An Anomalist is a Scientist whose expertise is analyzing alien artifacts. They are familar with the most common methods of testing artifact \ + function. They work closely with Xenoarchaeologists, or Miners, if either role is present." + +/datum/alt_title/phoron_research + title = "Phoron Researcher" + title_blurb = "A Phoron Researcher is a specialist in the practical applications of phoron, and has knowledge of its practical uses and dangers. \ + Many Phoron Researchers are interested in the combustability and explosive properties of gaseous phoron, as well as the specific hazards \ + of working with the substance in that state." + +////////////////////////////////// +// Xenobiologist +////////////////////////////////// +/datum/job/xenobiologist + title = "Xenobiologist" + flag = XENOBIOLOGIST + departments = list(DEPARTMENT_RESEARCH) + department_flag = MEDSCI + faction = "Station" + total_positions = 3 + spawn_positions = 2 + supervisors = "the Research Director" + selection_color = "#633D63" + economic_modifier = 7 + access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_hydroponics) + minimal_access = list(access_research, access_xenobiology, access_hydroponics, access_tox_storage) + banned_job_species = list("digital") + + minimal_player_age = 14 + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + + outfit_type = /decl/hierarchy/outfit/job/science/xenobiologist + job_description = "A Xenobiologist studies esoteric lifeforms, usually in the relative safety of their lab. They attempt to find ways to benefit \ + from the byproducts of these lifeforms, and their main subject at present is the Giant Slime." +/*VR edit start + alt_titles = list("Xenobotanist" = /datum/alt_title/xenobot) + + Xenibiologist Alt Titles +/datum/alt_title/xenobot + title = "Xenobotanist" + title_blurb = "A Xenobotanist grows and cares for a variety of abnormal, custom made, and frequently dangerous plant life. When the products of these plants \ + is both safe and beneficial to the station, they may choose to introduce it to the rest of the crew." +VR edit end*/ + +////////////////////////////////// +// Roboticist +////////////////////////////////// +/datum/job/roboticist + title = "Roboticist" + flag = ROBOTICIST + departments = list(DEPARTMENT_RESEARCH) + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Research Director" + selection_color = "#633D63" + economic_modifier = 5 + access = list(access_robotics, access_tox, access_tox_storage, access_tech_storage, access_morgue, access_research) //As a job that handles so many corpses, it makes sense for them to have morgue access. + minimal_access = list(access_robotics, access_tech_storage, access_morgue, access_research) //As a job that handles so many corpses, it makes sense for them to have morgue access. + minimal_player_age = 7 + min_age_by_species = list(SPECIES_PROMETHEAN = 2) + banned_job_species = list("digital") + + outfit_type = /decl/hierarchy/outfit/job/science/roboticist + job_description = "A Roboticist maintains and repairs the station's synthetics, including crew with prosthetic limbs. \ + They can also assist the station by producing simple robots and even pilotable exosuits." + alt_titles = list("Biomechanical Engineer" = /datum/alt_title/biomech, "Mechatronic Engineer" = /datum/alt_title/mech_tech) + +// Roboticist Alt Titles +/datum/alt_title/biomech + title = "Biomechanical Engineer" + title_blurb = "A Biomechanical Engineer primarily works on prosthetics, and the organic parts attached to them. They may have some \ + knowledge of the relatively simple surgical procedures used in making cyborgs and attaching prosthesis." + +/datum/alt_title/mech_tech + title = "Mechatronic Engineer" + title_blurb = "A Mechatronic Engineer focuses on the construction and maintenance of Exosuits, and should be well versed in their use. \ + They may also be called upon to work on synthetics and prosthetics, if needed." diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 07b363b67fc..2f6b4ee655b 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -1,135 +1,135 @@ -////////////////////////////////// -// Head of Security -////////////////////////////////// -/datum/job/hos - title = "Head of Security" - flag = HOS - departments_managed = list(DEPARTMENT_SECURITY) - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_COMMAND) - sorting_order = 2 - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Site Manager" - selection_color = "#8E2929" - req_admin_notify = 1 - economic_modifier = 10 - access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, - access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, - access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) - minimum_character_age = 25 - min_age_by_species = list(SPECIES_HUMAN_VATBORN = 14) - minimal_player_age = 14 - ideal_character_age = 50 - ideal_age_by_species = list(SPECIES_HUMAN_VATBORN = 20) - banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital", SPECIES_UNATHI, "mechanical") - - outfit_type = /decl/hierarchy/outfit/job/security/hos - job_description = " The Head of Security manages the Security Department, keeping the station safe and making sure the rules are followed. They are expected to \ - keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \ - perform the duties of absent Security roles, such as distributing gear from the Armory." - alt_titles = list("Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) - - -// Head of Security Alt Titles -/datum/alt_title/sec_commander - title = "Security Commander" - -/datum/alt_title/sec_chief - title = "Chief of Security" - -////////////////////////////////// -// Warden -////////////////////////////////// -/datum/job/warden - title = "Warden" - flag = WARDEN - departments = list(DEPARTMENT_SECURITY) - sorting_order = 1 - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the Head of Security" - selection_color = "#601C1C" - economic_modifier = 5 - access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_external_airlocks) - minimal_player_age = 5 - banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_TESHARI, SPECIES_DIONA) - - outfit_type = /decl/hierarchy/outfit/job/security/warden - job_description = "The Warden watches over the physical Security Department, making sure the Brig and Armoury are secure and in order at all times. They oversee \ - prisoners that have been processed and brigged, and are responsible for their well being. The Warden is also in charge of distributing \ - Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \ - Security Department as a whole." - -////////////////////////////////// -// Detective -////////////////////////////////// -/datum/job/detective - title = "Detective" - flag = DETECTIVE - departments = list(DEPARTMENT_SECURITY) - department_flag = ENGSEC - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Security" - selection_color = "#601C1C" - access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks, access_brig) //Vorestation edit - access_brig - minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) - economic_modifier = 5 - minimal_player_age = 3 - banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_DIONA) - - outfit_type = /decl/hierarchy/outfit/job/security/detective - job_description = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \ - For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence." - alt_titles = list("Forensic Technician" = /datum/alt_title/forensic_tech) - -// Detective Alt Titles -/datum/alt_title/forensic_tech - title = "Forensic Technician" - title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes." - title_outfit = /decl/hierarchy/outfit/job/security/detective/forensic - -////////////////////////////////// -// Security Officer -////////////////////////////////// -/datum/job/officer - title = "Security Officer" - flag = OFFICER - departments = list(DEPARTMENT_SECURITY) - department_flag = ENGSEC - faction = "Station" - total_positions = 4 - spawn_positions = 4 - supervisors = "the Head of Security" - selection_color = "#601C1C" - economic_modifier = 4 - access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_external_airlocks) - minimal_player_age = 3 - banned_job_species = list(SPECIES_ZADDAT, SPECIES_TESHARI, SPECIES_DIONA) - - outfit_type = /decl/hierarchy/outfit/job/security/officer - job_description = "A Security Officer is concerned with maintaining the safety and security of the station as a whole, dealing with external threats and \ - apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \ - No one is above the Law, not Security or Command." - alt_titles = list("Junior Officer" = /datum/alt_title/junior_officer) - - min_age_by_species = list(SPECIES_PROMETHEAN = 3) - -// Security Officer Alt Titles -/datum/alt_title/junior_officer - title = "Junior Officer" - title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \ - paired off with a more senior co-worker. Junior Officers may also be expected to take over the boring duties of other Officers \ +////////////////////////////////// +// Head of Security +////////////////////////////////// +/datum/job/hos + title = "Head of Security" + flag = HOS + departments_managed = list(DEPARTMENT_SECURITY) + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_COMMAND) + sorting_order = 2 + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Site Manager" + selection_color = "#8E2929" + req_admin_notify = 1 + economic_modifier = 10 + access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, + access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, + access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, + access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, + access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) + minimum_character_age = 25 + min_age_by_species = list(SPECIES_HUMAN_VATBORN = 14) + minimal_player_age = 14 + ideal_character_age = 50 + ideal_age_by_species = list(SPECIES_HUMAN_VATBORN = 20) + banned_job_species = list(SPECIES_TESHARI, SPECIES_DIONA, SPECIES_PROMETHEAN, SPECIES_ZADDAT, "digital", SPECIES_UNATHI, "mechanical") + + outfit_type = /decl/hierarchy/outfit/job/security/hos + job_description = " The Head of Security manages the Security Department, keeping the station safe and making sure the rules are followed. They are expected to \ + keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \ + perform the duties of absent Security roles, such as distributing gear from the Armory." + alt_titles = list("Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) + + +// Head of Security Alt Titles +/datum/alt_title/sec_commander + title = "Security Commander" + +/datum/alt_title/sec_chief + title = "Chief of Security" + +////////////////////////////////// +// Warden +////////////////////////////////// +/datum/job/warden + title = "Warden" + flag = WARDEN + departments = list(DEPARTMENT_SECURITY) + sorting_order = 1 + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the Head of Security" + selection_color = "#601C1C" + economic_modifier = 5 + access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_external_airlocks) + minimal_player_age = 5 + banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_TESHARI, SPECIES_DIONA) + + outfit_type = /decl/hierarchy/outfit/job/security/warden + job_description = "The Warden watches over the physical Security Department, making sure the Brig and Armoury are secure and in order at all times. They oversee \ + prisoners that have been processed and brigged, and are responsible for their well being. The Warden is also in charge of distributing \ + Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \ + Security Department as a whole." + +////////////////////////////////// +// Detective +////////////////////////////////// +/datum/job/detective + title = "Detective" + flag = DETECTIVE + departments = list(DEPARTMENT_SECURITY) + department_flag = ENGSEC + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Security" + selection_color = "#601C1C" + access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks, access_brig) //Vorestation edit - access_brig + minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) + economic_modifier = 5 + minimal_player_age = 3 + banned_job_species = list(SPECIES_ZADDAT, SPECIES_PROMETHEAN, SPECIES_DIONA) + + outfit_type = /decl/hierarchy/outfit/job/security/detective + job_description = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \ + For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence." + alt_titles = list("Forensic Technician" = /datum/alt_title/forensic_tech) + +// Detective Alt Titles +/datum/alt_title/forensic_tech + title = "Forensic Technician" + title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes." + title_outfit = /decl/hierarchy/outfit/job/security/detective/forensic + +////////////////////////////////// +// Security Officer +////////////////////////////////// +/datum/job/officer + title = "Security Officer" + flag = OFFICER + departments = list(DEPARTMENT_SECURITY) + department_flag = ENGSEC + faction = "Station" + total_positions = 4 + spawn_positions = 4 + supervisors = "the Head of Security" + selection_color = "#601C1C" + economic_modifier = 4 + access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_external_airlocks) + minimal_player_age = 3 + banned_job_species = list(SPECIES_ZADDAT, SPECIES_TESHARI, SPECIES_DIONA) + + outfit_type = /decl/hierarchy/outfit/job/security/officer + job_description = "A Security Officer is concerned with maintaining the safety and security of the station as a whole, dealing with external threats and \ + apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \ + No one is above the Law, not Security or Command." + alt_titles = list("Junior Officer" = /datum/alt_title/junior_officer) + + min_age_by_species = list(SPECIES_PROMETHEAN = 3) + +// Security Officer Alt Titles +/datum/alt_title/junior_officer + title = "Junior Officer" + title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \ + paired off with a more senior co-worker. Junior Officers may also be expected to take over the boring duties of other Officers \ including patrolling the station or maintaining specific posts." \ No newline at end of file diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index 1619569c9d7..06ec638ee3b 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,83 +1,83 @@ -////////////////////////////////// -// AI -////////////////////////////////// -/datum/job/ai - title = "AI" - flag = AI - departments = list(DEPARTMENT_SYNTHETIC) - sorting_order = 1 // Be above their borgs. - department_flag = ENGSEC - faction = "Station" - total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm - spawn_positions = 1 - selection_color = "#3F823F" - supervisors = "your Laws" - req_admin_notify = 1 - minimal_player_age = 7 - account_allowed = 0 - economic_modifier = 0 - has_headset = FALSE - assignable = FALSE - mob_type = JOB_SILICON_AI - outfit_type = /decl/hierarchy/outfit/job/silicon/ai - job_description = "The AI oversees the operation of the station and its crew, but has no real authority over them. \ - The AI is required to follow its Laws, and Lawbound Synthetics that are linked to it are expected to follow \ - the AI's commands, and their own Laws." - -// AI procs -/datum/job/ai/equip(var/mob/living/carbon/human/H) - if(!H) return 0 - return 1 - -/datum/job/ai/is_position_available() - return (empty_playable_ai_cores.len != 0) - -/datum/job/ai/equip_preview(mob/living/carbon/human/H) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/straight_jacket(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) - return 1 - -////////////////////////////////// -// Cyborg -////////////////////////////////// -/datum/job/cyborg - title = "Cyborg" - flag = CYBORG - departments = list(DEPARTMENT_SYNTHETIC) - department_flag = ENGSEC - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "your Laws and the AI" //Nodrak - selection_color = "#254C25" - minimal_player_age = 1 - account_allowed = 0 - economic_modifier = 0 - has_headset = FALSE - assignable = FALSE - mob_type = JOB_SILICON_ROBOT - outfit_type = /decl/hierarchy/outfit/job/silicon/cyborg - job_description = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \ - to follow its Laws." - alt_titles = list("Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) - -// Cyborg Alt Titles -/datum/alt_title/robot - title = "Robot" - title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \ - legally, but is required to follow its Laws." - -/datum/alt_title/drone - title = "Drone" - title_blurb = "A Drone is a mobile station synthetic, piloted by a simple computer-based AI. As such, it is not a person, but rather an expensive and \ - and important piece of station property, and is expected to follow its Laws." - -// Cyborg procs -/datum/job/cyborg/equip(var/mob/living/carbon/human/H) - if(!H) return 0 - return 1 - -/datum/job/cyborg/equip_preview(mob/living/carbon/human/H) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/cardborg(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) - return 1 +////////////////////////////////// +// AI +////////////////////////////////// +/datum/job/ai + title = "AI" + flag = AI + departments = list(DEPARTMENT_SYNTHETIC) + sorting_order = 1 // Be above their borgs. + department_flag = ENGSEC + faction = "Station" + total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm + spawn_positions = 1 + selection_color = "#3F823F" + supervisors = "your Laws" + req_admin_notify = 1 + minimal_player_age = 7 + account_allowed = 0 + economic_modifier = 0 + has_headset = FALSE + assignable = FALSE + mob_type = JOB_SILICON_AI + outfit_type = /decl/hierarchy/outfit/job/silicon/ai + job_description = "The AI oversees the operation of the station and its crew, but has no real authority over them. \ + The AI is required to follow its Laws, and Lawbound Synthetics that are linked to it are expected to follow \ + the AI's commands, and their own Laws." + +// AI procs +/datum/job/ai/equip(var/mob/living/carbon/human/H) + if(!H) return 0 + return 1 + +/datum/job/ai/is_position_available() + return (empty_playable_ai_cores.len != 0) + +/datum/job/ai/equip_preview(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/straight_jacket(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) + return 1 + +////////////////////////////////// +// Cyborg +////////////////////////////////// +/datum/job/cyborg + title = "Cyborg" + flag = CYBORG + departments = list(DEPARTMENT_SYNTHETIC) + department_flag = ENGSEC + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "your Laws and the AI" //Nodrak + selection_color = "#254C25" + minimal_player_age = 1 + account_allowed = 0 + economic_modifier = 0 + has_headset = FALSE + assignable = FALSE + mob_type = JOB_SILICON_ROBOT + outfit_type = /decl/hierarchy/outfit/job/silicon/cyborg + job_description = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \ + to follow its Laws." + alt_titles = list("Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) + +// Cyborg Alt Titles +/datum/alt_title/robot + title = "Robot" + title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \ + legally, but is required to follow its Laws." + +/datum/alt_title/drone + title = "Drone" + title_blurb = "A Drone is a mobile station synthetic, piloted by a simple computer-based AI. As such, it is not a person, but rather an expensive and \ + and important piece of station property, and is expected to follow its Laws." + +// Cyborg procs +/datum/job/cyborg/equip(var/mob/living/carbon/human/H) + if(!H) return 0 + return 1 + +/datum/job/cyborg/equip_preview(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/cardborg(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) + return 1 diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 785a60a93cd..8f1f409b66a 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -1,692 +1,692 @@ -var/global/datum/controller/occupations/job_master - -#define GET_RANDOM_JOB 0 -#define BE_ASSISTANT 1 -#define RETURN_TO_LOBBY 2 - -/datum/controller/occupations - //List of all jobs - var/list/occupations = list() - //Players who need jobs - var/list/unassigned = list() - //Debug info - var/list/job_debug = list() - //Cache of icons for job info window - var/list/job_icons = list() - -/datum/controller/occupations/proc/SetupOccupations(var/faction = "Station") - occupations = list() - //var/list/all_jobs = typesof(/datum/job) - var/list/all_jobs = list(/datum/job/assistant) | using_map.allowed_jobs - if(!all_jobs.len) - to_world("Error setting up jobs, no job datums found!") - return 0 - for(var/J in all_jobs) - var/datum/job/job = new J() - if(!job) continue - if(job.faction != faction) continue - occupations += job - sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) - - - return 1 - - -/datum/controller/occupations/proc/Debug(var/text) - if(!Debug2) return 0 - job_debug.Add(text) - return 1 - - -/datum/controller/occupations/proc/GetJob(var/rank) - if(!rank) return null - for(var/datum/job/J in occupations) - if(!J) continue - if(J.title == rank) return J - return null - -/datum/controller/occupations/proc/GetPlayerAltTitle(mob/new_player/player, rank) - return player.client.prefs.GetPlayerAltTitle(GetJob(rank)) - -/datum/controller/occupations/proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0) - Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") - if(player && player.mind && rank) - var/datum/job/job = GetJob(rank) - if(!job) - return 0 - if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) - return 0 - if(jobban_isbanned(player, rank)) - return 0 - if(!job.player_old_enough(player.client)) - return 0 - //VOREStation Add - if(!job.player_has_enough_playtime(player.client)) - return 0 - if(!is_job_whitelisted(player, rank)) - return 0 - //VOREStation Add End - - var/position_limit = job.total_positions - if(!latejoin) - position_limit = job.spawn_positions - if((job.current_positions < position_limit) || position_limit == -1) - Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]") - player.mind.assigned_role = rank - player.mind.role_alt_title = GetPlayerAltTitle(player, rank) - unassigned -= player - job.current_positions++ - return 1 - Debug("AR has failed, Player: [player], Rank: [rank]") - return 0 - -/datum/controller/occupations/proc/FreeRole(var/rank) //making additional slot on the fly - var/datum/job/job = GetJob(rank) - if(job && job.total_positions != -1) - job.total_positions++ - return 1 - return 0 - -/datum/controller/occupations/proc/FindOccupationCandidates(datum/job/job, level, flag) - Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") - var/list/candidates = list() - for(var/mob/new_player/player in unassigned) - if(jobban_isbanned(player, job.title)) - Debug("FOC isbanned failed, Player: [player]") - continue - if(!job.player_old_enough(player.client)) - Debug("FOC player not old enough, Player: [player]") - continue - if(job.minimum_character_age && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) - Debug("FOC character not old enough, Player: [player]") - continue - //VOREStation Code Start - if(!job.player_has_enough_playtime(player.client)) - Debug("FOC character not enough playtime, Player: [player]") - continue - if(!is_job_whitelisted(player, job.title)) - Debug("FOC is_job_whitelisted failed, Player: [player]") - continue - //VOREStation Code End - if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) - Debug("FOC character species invalid for job, Player: [player]") - continue - if(flag && !(player.client.prefs.be_special & flag)) - Debug("FOC flag failed, Player: [player], Flag: [flag], ") - continue - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) - Debug("FOC pass, Player: [player], Level:[level]") - candidates += player - return candidates - -/datum/controller/occupations/proc/GiveRandomJob(var/mob/new_player/player) - Debug("GRJ Giving random job, Player: [player]") - for(var/datum/job/job in shuffle(occupations)) - if(!job) - continue - - if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) - continue - - if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) - continue - - if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant - continue - - if(SSjob.is_job_in_department(job.title, DEPARTMENT_COMMAND)) //If you want a command position, select it! - continue - - if(jobban_isbanned(player, job.title)) - Debug("GRJ isbanned failed, Player: [player], Job: [job.title]") - continue - - if(!job.player_old_enough(player.client)) - Debug("GRJ player not old enough, Player: [player]") - continue - - //VOREStation Code Start - if(!job.player_has_enough_playtime(player.client)) - Debug("GRJ player not enough playtime, Player: [player]") - continue - if(!is_job_whitelisted(player, job.title)) - Debug("GRJ player not whitelisted for this job, Player: [player], Job: [job.title]") - continue - //VOREStation Code End - - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) - Debug("GRJ Random job given, Player: [player], Job: [job]") - AssignRole(player, job.title) - unassigned -= player - break - -/datum/controller/occupations/proc/ResetOccupations() - for(var/mob/new_player/player in player_list) - if((player) && (player.mind)) - player.mind.assigned_role = null - player.mind.special_role = null - SetupOccupations() - unassigned = list() - return - - -///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check -/datum/controller/occupations/proc/FillHeadPosition() - for(var/level = 1 to 3) - for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) - var/datum/job/job = GetJob(command_position) - if(!job) continue - var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) continue - - // Build a weighted list, weight by age. - var/list/weightedCandidates = list() - for(var/mob/V in candidates) - // Log-out during round-start? What a bad boy, no head position for you! - if(!V.client) continue - var/age = V.client.prefs.age - - if(age < job.get_min_age(V.client.prefs.species, V.client.prefs.organ_data["brain"])) // Nope. - continue - - var/idealage = job.get_ideal_age(V.client.prefs.species, V.client.prefs.organ_data["brain"]) - var/agediff = abs(idealage - age) // Compute the absolute difference in age from target - switch(agediff) /// If the math sucks, it's because I almost failed algebra in high school. - if(20 to INFINITY) - weightedCandidates[V] = 3 // Too far off - if(10 to 20) - weightedCandidates[V] = 6 // Nearer the mark, but not quite - if(0 to 10) - weightedCandidates[V] = 10 // On the mark - else - // If there's ABSOLUTELY NOBODY ELSE - if(candidates.len == 1) weightedCandidates[V] = 1 - - - var/mob/new_player/candidate = pickweight(weightedCandidates) - if(AssignRole(candidate, command_position)) - return 1 - return 0 - - -///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level -/datum/controller/occupations/proc/CheckHeadPositions(var/level) - for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) - var/datum/job/job = GetJob(command_position) - if(!job) continue - var/list/candidates = FindOccupationCandidates(job, level) - if(!candidates.len) continue - var/mob/new_player/candidate = pick(candidates) - AssignRole(candidate, command_position) - return - - -/** Proc DivideOccupations - * fills var "assigned_role" for all ready players. - * This proc must not have any side effect besides of modifying "assigned_role". - **/ -/datum/controller/occupations/proc/DivideOccupations() - //Setup new player list and get the jobs list - Debug("Running DO") - SetupOccupations() - - //Holder for Triumvirate is stored in the ticker, this just processes it - if(ticker && ticker.triai) - for(var/datum/job/A in occupations) - if(A.title == "AI") - A.spawn_positions = 3 - break - - //Get the players who are ready - for(var/mob/new_player/player in player_list) - if(player.ready && player.mind && !player.mind.assigned_role) - unassigned += player - - Debug("DO, Len: [unassigned.len]") - if(unassigned.len == 0) return 0 - - //Shuffle players and jobs - unassigned = shuffle(unassigned) - - HandleFeedbackGathering() - - //People who wants to be assistants, sure, go on. - Debug("DO, Running Assistant Check 1") - var/datum/job/assist = new DEFAULT_JOB_TYPE () - var/list/assistant_candidates = FindOccupationCandidates(assist, 3) - Debug("AC1, Candidates: [assistant_candidates.len]") - for(var/mob/new_player/player in assistant_candidates) - Debug("AC1 pass, Player: [player]") - AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant - assistant_candidates -= player - Debug("DO, AC1 end") - - //Select one head - Debug("DO, Running Head Check") - FillHeadPosition() - Debug("DO, Head Check end") - - //Other jobs are now checked - Debug("DO, Running Standard Check") - - - // New job giving system by Donkie - // This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all. - // Hopefully this will add more randomness and fairness to job giving. - - // Loop through all levels from high to low - var/list/shuffledoccupations = shuffle(occupations) - // var/list/disabled_jobs = ticker.mode.disabled_jobs // So we can use .Find down below without a colon. - for(var/level = 1 to 3) - //Check the head jobs first each level - CheckHeadPositions(level) - - // Loop through all unassigned players - for(var/mob/new_player/player in unassigned) - - // Loop through all jobs - for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY - if(!job || ticker.mode.disabled_jobs.Find(job.title) ) - continue - - if(jobban_isbanned(player, job.title)) - Debug("DO isbanned failed, Player: [player], Job:[job.title]") - continue - - if(!job.player_old_enough(player.client)) - Debug("DO player not old enough, Player: [player], Job:[job.title]") - continue - - //VOREStation Add - if(!job.player_has_enough_playtime(player.client)) - Debug("DO player not enough playtime, Player: [player]") - continue - //VOREStation Add End - - // If the player wants that job on this level, then try give it to him. - if(player.client.prefs.GetJobDepartment(job, level) & job.flag) - - // If the job isn't filled - if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) - Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") - AssignRole(player, job.title) - unassigned -= player - break - - // Hand out random jobs to the people who didn't get any in the last check - // Also makes sure that they got their preference correct - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == GET_RANDOM_JOB) - GiveRandomJob(player) - /* - Old job system - for(var/level = 1 to 3) - for(var/datum/job/job in occupations) - Debug("Checking job: [job]") - if(!job) - continue - if(!unassigned.len) - break - if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1) - continue - var/list/candidates = FindOccupationCandidates(job, level) - while(candidates.len && ((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)) - var/mob/new_player/candidate = pick(candidates) - Debug("Selcted: [candidate], for: [job.title]") - AssignRole(candidate, job.title) - candidates -= candidate*/ - - Debug("DO, Standard Check end") - - Debug("DO, Running AC2") - - // For those who wanted to be assistant if their preferences were filled, here you go. - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == BE_ASSISTANT) - Debug("AC2 Assistant located, Player: [player]") - AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant - - //For ones returning to lobby - for(var/mob/new_player/player in unassigned) - if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) - player.ready = 0 - player.new_player_panel_proc() - unassigned -= player - return 1 - - -/datum/controller/occupations/proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) - if(!H) return null - - var/datum/job/job = GetJob(rank) - var/list/spawn_in_storage = list() - - if(!joined_late) - var/obj/S = null - var/list/possible_spawns = list() - for(var/obj/effect/landmark/start/sloc in landmarks_list) - if(sloc.name != rank) continue - if(locate(/mob/living) in sloc.loc) continue - possible_spawns.Add(sloc) - if(possible_spawns.len) - S = pick(possible_spawns) - if(!S) - S = locate("start*[rank]") // use old stype - if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) - H.forceMove(S.loc) - else - var/list/spawn_props = LateSpawn(H.client, rank) - var/turf/T = spawn_props["turf"] - if(!T) - to_chat(H, "You were unable to be spawned at your chosen late-join spawnpoint. Please verify your job/spawn point combination makes sense, and try another one.") - return - else - H.forceMove(T) - - // Moving wheelchair if they have one - if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) - H.buckled.forceMove(H.loc) - H.buckled.set_dir(H.dir) - - if(job) - - //Equip custom gear loadout. - var/list/custom_equip_slots = list() - var/list/custom_equip_leftovers = list() - if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) - for(var/thing in H.client.prefs.gear) - var/datum/gear/G = gear_datums[thing] - if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile) - continue - - var/permitted - // Check if it is restricted to certain roles - if(G.allowed_roles) - for(var/job_name in G.allowed_roles) - if(job.title == job_name) - permitted = 1 - else - permitted = 1 - - // Check if they're whitelisted for this gear (in alien whitelist? seriously?) - if(G.whitelisted && !is_alien_whitelisted(H, GLOB.all_species[G.whitelisted])) - permitted = 0 - - // If they aren't, tell them - if(!permitted) - to_chat(H, "Your current species, job or whitelist status does not permit you to spawn with [thing]!") - continue - - // Implants get special treatment - if(G.slot == "implant") - var/obj/item/weapon/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) - I.invisibility = 100 - I.implant_loadout(H) - continue - - // Try desperately (and sorta poorly) to equip the item. Now with increased desperation! - if(G.slot && !(G.slot in custom_equip_slots)) - var/metadata = H.client.prefs.gear[G.display_name] - //if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) - // custom_equip_leftovers += thing - //else - if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - to_chat(H, "Equipping you with \the [thing]!") - if(G.slot != slot_tie) - custom_equip_slots.Add(G.slot) - else - custom_equip_leftovers.Add(thing) - else - spawn_in_storage += thing - - // Set up their account - job.setup_account(H) - - // Equip job items. - job.equip(H, H.mind ? H.mind.role_alt_title : "") - - // Stick their fingerprints on literally everything - job.apply_fingerprints(H) - - // Only non-silicons get post-job-equip equipment - if(!(job.mob_type & JOB_SILICON)) - H.equip_post_job() - - // If some custom items could not be equipped before, try again now. - for(var/thing in custom_equip_leftovers) - var/datum/gear/G = gear_datums[thing] - if(G.slot in custom_equip_slots) - spawn_in_storage += thing - else - var/metadata = H.client.prefs.gear[G.display_name] - if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - to_chat(H, "Equipping you with \the [thing]!") - custom_equip_slots.Add(G.slot) - else - spawn_in_storage += thing - else - to_chat(H, "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator.") - - H.job = rank - log_game("JOINED [key_name(H)] as \"[rank]\"") - log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add - - // If they're head, give them the account info for their department - if(H.mind && job.department_accounts) - var/remembered_info = "" - for(var/D in job.department_accounts) - var/datum/money_account/department_account = department_accounts[D] - if(department_account) - remembered_info += "Department account number ([D]): #[department_account.account_number]
    " - remembered_info += "Department account pin ([D]): [department_account.remote_access_pin]
    " - remembered_info += "Department account funds ([D]): $[department_account.money]
    " - - H.mind.store_memory(remembered_info) - - var/alt_title = null - if(H.mind) - H.mind.assigned_role = rank - alt_title = H.mind.role_alt_title - - // If we're a silicon, we may be done at this point - if(job.mob_type & JOB_SILICON_ROBOT) - return H.Robotize() - if(job.mob_type & JOB_SILICON_AI) - return H - - // TWEET PEEP - if(rank == "Site Manager") - var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP) ? null : sound('sound/misc/boatswain.ogg', volume=20) - captain_announcement.Announce("All hands, [alt_title ? alt_title : "Site Manager"] [H.real_name] on deck!", new_sound = announce_sound, zlevel = H.z) - - //Deferred item spawning. - if(spawn_in_storage && spawn_in_storage.len) - var/obj/item/weapon/storage/B - for(var/obj/item/weapon/storage/S in H.contents) - B = S - break - - if(!isnull(B)) - for(var/thing in spawn_in_storage) - to_chat(H, "Placing \the [thing] in your [B.name]!") - var/datum/gear/G = gear_datums[thing] - var/metadata = H.client.prefs.gear[G.display_name] - G.spawn_item(B, metadata) - else - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.") - - if(istype(H)) //give humans wheelchairs, if they need them. - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/obj/item/weapon/storage/S = locate() in H.contents - var/obj/item/wheelchair/R - if(S) - R = locate() in S.contents - if(!l_foot || !r_foot || R) - var/wheelchair_type = R?.unfolded_type || /obj/structure/bed/chair/wheelchair - var/obj/structure/bed/chair/wheelchair/W = new wheelchair_type(H.loc) - W.buckle_mob(H) - H.update_canmove() - W.set_dir(H.dir) - W.add_fingerprint(H) - if(R) - W.color = R.color - qdel(R) - - to_chat(H, "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank].") - - if(job.supervisors) - to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") - if(job.has_headset) - H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_l_ear) - to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") - - if(job.req_admin_notify) - to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") - - // EMAIL GENERATION - // Email addresses will be created under this domain name. Mostly for the looks. - var/domain = "freemail.nt" - if(using_map && LAZYLEN(using_map.usable_email_tlds)) - domain = using_map.usable_email_tlds[1] - var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) - var/complete_login = "[sanitized_name]@[domain]" - - // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. - // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. - if(ntnet_global.does_email_exist(complete_login)) - complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" - - // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. - if(ntnet_global.does_email_exist(complete_login)) - to_chat(H, "You were not assigned an email address.") - H.mind.store_memory("You were not assigned an email address.") - else - var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() - EA.password = GenerateKey() - EA.login = complete_login - to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") - H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") - // END EMAIL GENERATION - - //Gives glasses to the vision impaired - if(H.disabilities & NEARSIGHTED) - var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) - if(equipped != 1) - var/obj/item/clothing/glasses/G = H.glasses - G.prescription = 1 - - BITSET(H.hud_updateflag, ID_HUD) - BITSET(H.hud_updateflag, IMPLOYAL_HUD) - BITSET(H.hud_updateflag, SPECIALROLE_HUD) - return H - -/datum/controller/occupations/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist - if(!config.load_jobs_from_txt) - return 0 - - var/list/jobEntries = file2list(jobsfile) - - for(var/job in jobEntries) - if(!job) - continue - - job = trim(job) - if (!length(job)) - continue - - var/pos = findtext(job, "=") - var/name = null - var/value = null - - if(pos) - name = copytext(job, 1, pos) - value = copytext(job, pos + 1) - else - continue - - if(name && value) - var/datum/job/J = GetJob(name) - if(!J) continue - J.total_positions = text2num(value) - J.spawn_positions = text2num(value) - if(J.mob_type & JOB_SILICON) - J.total_positions = 0 - - return 1 - - -/datum/controller/occupations/proc/HandleFeedbackGathering() - for(var/datum/job/job in occupations) - var/tmp_str = "|[job.title]|" - - var/level1 = 0 //high - var/level2 = 0 //medium - var/level3 = 0 //low - var/level4 = 0 //never - var/level5 = 0 //banned - var/level6 = 0 //account too young - for(var/mob/new_player/player in player_list) - if(!(player.ready && player.mind && !player.mind.assigned_role)) - continue //This player is not ready - if(jobban_isbanned(player, job.title)) - level5++ - continue - if(!job.player_old_enough(player.client)) - level6++ - continue - //VOREStation Add - if(!job.player_has_enough_playtime(player.client)) - level6++ - continue - //VOREStation Add End - if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) - level1++ - else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) - level2++ - else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) - level3++ - else level4++ //not selected - - tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-" - feedback_add_details("job_preferences",tmp_str) - -/datum/controller/occupations/proc/LateSpawn(var/client/C, var/rank) - - var/datum/spawnpoint/spawnpos - var/fail_deadly = FALSE - - var/datum/job/J = SSjob.get_job(rank) - fail_deadly = J?.offmap_spawn - - //Spawn them at their preferred one - if(C && C.prefs.spawnpoint) - if(!(C.prefs.spawnpoint in using_map.allowed_spawns)) - if(fail_deadly) - to_chat(C, "Your chosen spawnpoint is unavailable for this map and your job requires a specific spawnpoint. Please correct your spawn point choice.") - return - else - to_chat(C, "Your chosen spawnpoint ([C.prefs.spawnpoint]) is unavailable for the current map. Spawning you at one of the enabled spawn points instead.") - spawnpos = null - else - spawnpos = spawntypes[C.prefs.spawnpoint] - - //We will return a list key'd by "turf" and "msg" - . = list("turf","msg") - if(spawnpos && istype(spawnpos) && spawnpos.turfs.len) - if(spawnpos.check_job_spawning(rank)) - .["turf"] = spawnpos.get_spawn_position() - .["msg"] = spawnpos.msg - .["channel"] = spawnpos.announce_channel - else - if(fail_deadly) - to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Please correct your spawn point choice.") - return - to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") - var/spawning = pick(latejoin) - .["turf"] = get_turf(spawning) - .["msg"] = "will arrive at the station shortly" - else if(!fail_deadly) - var/spawning = pick(latejoin) - .["turf"] = get_turf(spawning) - .["msg"] = "has arrived on the station" +var/global/datum/controller/occupations/job_master + +#define GET_RANDOM_JOB 0 +#define BE_ASSISTANT 1 +#define RETURN_TO_LOBBY 2 + +/datum/controller/occupations + //List of all jobs + var/list/occupations = list() + //Players who need jobs + var/list/unassigned = list() + //Debug info + var/list/job_debug = list() + //Cache of icons for job info window + var/list/job_icons = list() + +/datum/controller/occupations/proc/SetupOccupations(var/faction = "Station") + occupations = list() + //var/list/all_jobs = typesof(/datum/job) + var/list/all_jobs = list(/datum/job/assistant) | using_map.allowed_jobs + if(!all_jobs.len) + to_world("Error setting up jobs, no job datums found!") + return 0 + for(var/J in all_jobs) + var/datum/job/job = new J() + if(!job) continue + if(job.faction != faction) continue + occupations += job + sortTim(occupations, GLOBAL_PROC_REF(cmp_job_datums)) + + + return 1 + + +/datum/controller/occupations/proc/Debug(var/text) + if(!Debug2) return 0 + job_debug.Add(text) + return 1 + + +/datum/controller/occupations/proc/GetJob(var/rank) + if(!rank) return null + for(var/datum/job/J in occupations) + if(!J) continue + if(J.title == rank) return J + return null + +/datum/controller/occupations/proc/GetPlayerAltTitle(mob/new_player/player, rank) + return player.client.prefs.GetPlayerAltTitle(GetJob(rank)) + +/datum/controller/occupations/proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0) + Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]") + if(player && player.mind && rank) + var/datum/job/job = GetJob(rank) + if(!job) + return 0 + if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) + return 0 + if(jobban_isbanned(player, rank)) + return 0 + if(!job.player_old_enough(player.client)) + return 0 + //VOREStation Add + if(!job.player_has_enough_playtime(player.client)) + return 0 + if(!is_job_whitelisted(player, rank)) + return 0 + //VOREStation Add End + + var/position_limit = job.total_positions + if(!latejoin) + position_limit = job.spawn_positions + if((job.current_positions < position_limit) || position_limit == -1) + Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]") + player.mind.assigned_role = rank + player.mind.role_alt_title = GetPlayerAltTitle(player, rank) + unassigned -= player + job.current_positions++ + return 1 + Debug("AR has failed, Player: [player], Rank: [rank]") + return 0 + +/datum/controller/occupations/proc/FreeRole(var/rank) //making additional slot on the fly + var/datum/job/job = GetJob(rank) + if(job && job.total_positions != -1) + job.total_positions++ + return 1 + return 0 + +/datum/controller/occupations/proc/FindOccupationCandidates(datum/job/job, level, flag) + Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") + var/list/candidates = list() + for(var/mob/new_player/player in unassigned) + if(jobban_isbanned(player, job.title)) + Debug("FOC isbanned failed, Player: [player]") + continue + if(!job.player_old_enough(player.client)) + Debug("FOC player not old enough, Player: [player]") + continue + if(job.minimum_character_age && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) + Debug("FOC character not old enough, Player: [player]") + continue + //VOREStation Code Start + if(!job.player_has_enough_playtime(player.client)) + Debug("FOC character not enough playtime, Player: [player]") + continue + if(!is_job_whitelisted(player, job.title)) + Debug("FOC is_job_whitelisted failed, Player: [player]") + continue + //VOREStation Code End + if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) + Debug("FOC character species invalid for job, Player: [player]") + continue + if(flag && !(player.client.prefs.be_special & flag)) + Debug("FOC flag failed, Player: [player], Flag: [flag], ") + continue + if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + Debug("FOC pass, Player: [player], Level:[level]") + candidates += player + return candidates + +/datum/controller/occupations/proc/GiveRandomJob(var/mob/new_player/player) + Debug("GRJ Giving random job, Player: [player]") + for(var/datum/job/job in shuffle(occupations)) + if(!job) + continue + + if((job.minimum_character_age || job.min_age_by_species) && (player.client.prefs.age < job.get_min_age(player.client.prefs.species, player.client.prefs.organ_data["brain"]))) + continue + + if(job.is_species_banned(player.client.prefs.species, player.client.prefs.organ_data["brain"]) == TRUE) + continue + + if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant + continue + + if(SSjob.is_job_in_department(job.title, DEPARTMENT_COMMAND)) //If you want a command position, select it! + continue + + if(jobban_isbanned(player, job.title)) + Debug("GRJ isbanned failed, Player: [player], Job: [job.title]") + continue + + if(!job.player_old_enough(player.client)) + Debug("GRJ player not old enough, Player: [player]") + continue + + //VOREStation Code Start + if(!job.player_has_enough_playtime(player.client)) + Debug("GRJ player not enough playtime, Player: [player]") + continue + if(!is_job_whitelisted(player, job.title)) + Debug("GRJ player not whitelisted for this job, Player: [player], Job: [job.title]") + continue + //VOREStation Code End + + if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) + Debug("GRJ Random job given, Player: [player], Job: [job]") + AssignRole(player, job.title) + unassigned -= player + break + +/datum/controller/occupations/proc/ResetOccupations() + for(var/mob/new_player/player in player_list) + if((player) && (player.mind)) + player.mind.assigned_role = null + player.mind.special_role = null + SetupOccupations() + unassigned = list() + return + + +///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check +/datum/controller/occupations/proc/FillHeadPosition() + for(var/level = 1 to 3) + for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) + var/datum/job/job = GetJob(command_position) + if(!job) continue + var/list/candidates = FindOccupationCandidates(job, level) + if(!candidates.len) continue + + // Build a weighted list, weight by age. + var/list/weightedCandidates = list() + for(var/mob/V in candidates) + // Log-out during round-start? What a bad boy, no head position for you! + if(!V.client) continue + var/age = V.client.prefs.age + + if(age < job.get_min_age(V.client.prefs.species, V.client.prefs.organ_data["brain"])) // Nope. + continue + + var/idealage = job.get_ideal_age(V.client.prefs.species, V.client.prefs.organ_data["brain"]) + var/agediff = abs(idealage - age) // Compute the absolute difference in age from target + switch(agediff) /// If the math sucks, it's because I almost failed algebra in high school. + if(20 to INFINITY) + weightedCandidates[V] = 3 // Too far off + if(10 to 20) + weightedCandidates[V] = 6 // Nearer the mark, but not quite + if(0 to 10) + weightedCandidates[V] = 10 // On the mark + else + // If there's ABSOLUTELY NOBODY ELSE + if(candidates.len == 1) weightedCandidates[V] = 1 + + + var/mob/new_player/candidate = pickweight(weightedCandidates) + if(AssignRole(candidate, command_position)) + return 1 + return 0 + + +///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level +/datum/controller/occupations/proc/CheckHeadPositions(var/level) + for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) + var/datum/job/job = GetJob(command_position) + if(!job) continue + var/list/candidates = FindOccupationCandidates(job, level) + if(!candidates.len) continue + var/mob/new_player/candidate = pick(candidates) + AssignRole(candidate, command_position) + return + + +/** Proc DivideOccupations + * fills var "assigned_role" for all ready players. + * This proc must not have any side effect besides of modifying "assigned_role". + **/ +/datum/controller/occupations/proc/DivideOccupations() + //Setup new player list and get the jobs list + Debug("Running DO") + SetupOccupations() + + //Holder for Triumvirate is stored in the ticker, this just processes it + if(ticker && ticker.triai) + for(var/datum/job/A in occupations) + if(A.title == "AI") + A.spawn_positions = 3 + break + + //Get the players who are ready + for(var/mob/new_player/player in player_list) + if(player.ready && player.mind && !player.mind.assigned_role) + unassigned += player + + Debug("DO, Len: [unassigned.len]") + if(unassigned.len == 0) return 0 + + //Shuffle players and jobs + unassigned = shuffle(unassigned) + + HandleFeedbackGathering() + + //People who wants to be assistants, sure, go on. + Debug("DO, Running Assistant Check 1") + var/datum/job/assist = new DEFAULT_JOB_TYPE () + var/list/assistant_candidates = FindOccupationCandidates(assist, 3) + Debug("AC1, Candidates: [assistant_candidates.len]") + for(var/mob/new_player/player in assistant_candidates) + Debug("AC1 pass, Player: [player]") + AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant + assistant_candidates -= player + Debug("DO, AC1 end") + + //Select one head + Debug("DO, Running Head Check") + FillHeadPosition() + Debug("DO, Head Check end") + + //Other jobs are now checked + Debug("DO, Running Standard Check") + + + // New job giving system by Donkie + // This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all. + // Hopefully this will add more randomness and fairness to job giving. + + // Loop through all levels from high to low + var/list/shuffledoccupations = shuffle(occupations) + // var/list/disabled_jobs = ticker.mode.disabled_jobs // So we can use .Find down below without a colon. + for(var/level = 1 to 3) + //Check the head jobs first each level + CheckHeadPositions(level) + + // Loop through all unassigned players + for(var/mob/new_player/player in unassigned) + + // Loop through all jobs + for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY + if(!job || ticker.mode.disabled_jobs.Find(job.title) ) + continue + + if(jobban_isbanned(player, job.title)) + Debug("DO isbanned failed, Player: [player], Job:[job.title]") + continue + + if(!job.player_old_enough(player.client)) + Debug("DO player not old enough, Player: [player], Job:[job.title]") + continue + + //VOREStation Add + if(!job.player_has_enough_playtime(player.client)) + Debug("DO player not enough playtime, Player: [player]") + continue + //VOREStation Add End + + // If the player wants that job on this level, then try give it to him. + if(player.client.prefs.GetJobDepartment(job, level) & job.flag) + + // If the job isn't filled + if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1) + Debug("DO pass, Player: [player], Level:[level], Job:[job.title]") + AssignRole(player, job.title) + unassigned -= player + break + + // Hand out random jobs to the people who didn't get any in the last check + // Also makes sure that they got their preference correct + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == GET_RANDOM_JOB) + GiveRandomJob(player) + /* + Old job system + for(var/level = 1 to 3) + for(var/datum/job/job in occupations) + Debug("Checking job: [job]") + if(!job) + continue + if(!unassigned.len) + break + if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1) + continue + var/list/candidates = FindOccupationCandidates(job, level) + while(candidates.len && ((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)) + var/mob/new_player/candidate = pick(candidates) + Debug("Selcted: [candidate], for: [job.title]") + AssignRole(candidate, job.title) + candidates -= candidate*/ + + Debug("DO, Standard Check end") + + Debug("DO, Running AC2") + + // For those who wanted to be assistant if their preferences were filled, here you go. + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == BE_ASSISTANT) + Debug("AC2 Assistant located, Player: [player]") + AssignRole(player, USELESS_JOB) //VOREStation Edit - Visitor not Assistant + + //For ones returning to lobby + for(var/mob/new_player/player in unassigned) + if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) + player.ready = 0 + player.new_player_panel_proc() + unassigned -= player + return 1 + + +/datum/controller/occupations/proc/EquipRank(var/mob/living/carbon/human/H, var/rank, var/joined_late = 0) + if(!H) return null + + var/datum/job/job = GetJob(rank) + var/list/spawn_in_storage = list() + + if(!joined_late) + var/obj/S = null + var/list/possible_spawns = list() + for(var/obj/effect/landmark/start/sloc in landmarks_list) + if(sloc.name != rank) continue + if(locate(/mob/living) in sloc.loc) continue + possible_spawns.Add(sloc) + if(possible_spawns.len) + S = pick(possible_spawns) + if(!S) + S = locate("start*[rank]") // use old stype + if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf)) + H.forceMove(S.loc) + else + var/list/spawn_props = LateSpawn(H.client, rank) + var/turf/T = spawn_props["turf"] + if(!T) + to_chat(H, "You were unable to be spawned at your chosen late-join spawnpoint. Please verify your job/spawn point combination makes sense, and try another one.") + return + else + H.forceMove(T) + + // Moving wheelchair if they have one + if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) + H.buckled.forceMove(H.loc) + H.buckled.set_dir(H.dir) + + if(job) + + //Equip custom gear loadout. + var/list/custom_equip_slots = list() + var/list/custom_equip_leftovers = list() + if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON)) + for(var/thing in H.client.prefs.gear) + var/datum/gear/G = gear_datums[thing] + if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile) + continue + + var/permitted + // Check if it is restricted to certain roles + if(G.allowed_roles) + for(var/job_name in G.allowed_roles) + if(job.title == job_name) + permitted = 1 + else + permitted = 1 + + // Check if they're whitelisted for this gear (in alien whitelist? seriously?) + if(G.whitelisted && !is_alien_whitelisted(H, GLOB.all_species[G.whitelisted])) + permitted = 0 + + // If they aren't, tell them + if(!permitted) + to_chat(H, "Your current species, job or whitelist status does not permit you to spawn with [thing]!") + continue + + // Implants get special treatment + if(G.slot == "implant") + var/obj/item/weapon/implant/I = G.spawn_item(H, H.client.prefs.gear[G.display_name]) + I.invisibility = 100 + I.implant_loadout(H) + continue + + // Try desperately (and sorta poorly) to equip the item. Now with increased desperation! + if(G.slot && !(G.slot in custom_equip_slots)) + var/metadata = H.client.prefs.gear[G.display_name] + //if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) + // custom_equip_leftovers += thing + //else + if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) + to_chat(H, "Equipping you with \the [thing]!") + if(G.slot != slot_tie) + custom_equip_slots.Add(G.slot) + else + custom_equip_leftovers.Add(thing) + else + spawn_in_storage += thing + + // Set up their account + job.setup_account(H) + + // Equip job items. + job.equip(H, H.mind ? H.mind.role_alt_title : "") + + // Stick their fingerprints on literally everything + job.apply_fingerprints(H) + + // Only non-silicons get post-job-equip equipment + if(!(job.mob_type & JOB_SILICON)) + H.equip_post_job() + + // If some custom items could not be equipped before, try again now. + for(var/thing in custom_equip_leftovers) + var/datum/gear/G = gear_datums[thing] + if(G.slot in custom_equip_slots) + spawn_in_storage += thing + else + var/metadata = H.client.prefs.gear[G.display_name] + if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) + to_chat(H, "Equipping you with \the [thing]!") + custom_equip_slots.Add(G.slot) + else + spawn_in_storage += thing + else + to_chat(H, "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator.") + + H.job = rank + log_game("JOINED [key_name(H)] as \"[rank]\"") + log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add + + // If they're head, give them the account info for their department + if(H.mind && job.department_accounts) + var/remembered_info = "" + for(var/D in job.department_accounts) + var/datum/money_account/department_account = department_accounts[D] + if(department_account) + remembered_info += "Department account number ([D]): #[department_account.account_number]
    " + remembered_info += "Department account pin ([D]): [department_account.remote_access_pin]
    " + remembered_info += "Department account funds ([D]): $[department_account.money]
    " + + H.mind.store_memory(remembered_info) + + var/alt_title = null + if(H.mind) + H.mind.assigned_role = rank + alt_title = H.mind.role_alt_title + + // If we're a silicon, we may be done at this point + if(job.mob_type & JOB_SILICON_ROBOT) + return H.Robotize() + if(job.mob_type & JOB_SILICON_AI) + return H + + // TWEET PEEP + if(rank == "Site Manager") + var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP) ? null : sound('sound/misc/boatswain.ogg', volume=20) + captain_announcement.Announce("All hands, [alt_title ? alt_title : "Site Manager"] [H.real_name] on deck!", new_sound = announce_sound, zlevel = H.z) + + //Deferred item spawning. + if(spawn_in_storage && spawn_in_storage.len) + var/obj/item/weapon/storage/B + for(var/obj/item/weapon/storage/S in H.contents) + B = S + break + + if(!isnull(B)) + for(var/thing in spawn_in_storage) + to_chat(H, "Placing \the [thing] in your [B.name]!") + var/datum/gear/G = gear_datums[thing] + var/metadata = H.client.prefs.gear[G.display_name] + G.spawn_item(B, metadata) + else + to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.") + + if(istype(H)) //give humans wheelchairs, if they need them. + var/obj/item/organ/external/l_foot = H.get_organ("l_foot") + var/obj/item/organ/external/r_foot = H.get_organ("r_foot") + var/obj/item/weapon/storage/S = locate() in H.contents + var/obj/item/wheelchair/R + if(S) + R = locate() in S.contents + if(!l_foot || !r_foot || R) + var/wheelchair_type = R?.unfolded_type || /obj/structure/bed/chair/wheelchair + var/obj/structure/bed/chair/wheelchair/W = new wheelchair_type(H.loc) + W.buckle_mob(H) + H.update_canmove() + W.set_dir(H.dir) + W.add_fingerprint(H) + if(R) + W.color = R.color + qdel(R) + + to_chat(H, "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank].") + + if(job.supervisors) + to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") + if(job.has_headset) + H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_l_ear) + to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") + + if(job.req_admin_notify) + to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") + + // EMAIL GENERATION + // Email addresses will be created under this domain name. Mostly for the looks. + var/domain = "freemail.nt" + if(using_map && LAZYLEN(using_map.usable_email_tlds)) + domain = using_map.usable_email_tlds[1] + var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) + var/complete_login = "[sanitized_name]@[domain]" + + // It is VERY unlikely that we'll have two players, in the same round, with the same name and branch, but still, this is here. + // If such conflict is encountered, a random number will be appended to the email address. If this fails too, no email account will be created. + if(ntnet_global.does_email_exist(complete_login)) + complete_login = "[sanitized_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" + + // If even fallback login generation failed, just don't give them an email. The chance of this happening is astronomically low. + if(ntnet_global.does_email_exist(complete_login)) + to_chat(H, "You were not assigned an email address.") + H.mind.store_memory("You were not assigned an email address.") + else + var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account() + EA.password = GenerateKey() + EA.login = complete_login + to_chat(H, "Your email account address is [EA.login] and the password is [EA.password]. This information has also been placed into your notes.") + H.mind.store_memory("Your email account address is [EA.login] and the password is [EA.password].") + // END EMAIL GENERATION + + //Gives glasses to the vision impaired + if(H.disabilities & NEARSIGHTED) + var/equipped = H.equip_to_slot_or_del(new /obj/item/clothing/glasses/regular(H), slot_glasses) + if(equipped != 1) + var/obj/item/clothing/glasses/G = H.glasses + G.prescription = 1 + + BITSET(H.hud_updateflag, ID_HUD) + BITSET(H.hud_updateflag, IMPLOYAL_HUD) + BITSET(H.hud_updateflag, SPECIALROLE_HUD) + return H + +/datum/controller/occupations/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist + if(!config.load_jobs_from_txt) + return 0 + + var/list/jobEntries = file2list(jobsfile) + + for(var/job in jobEntries) + if(!job) + continue + + job = trim(job) + if (!length(job)) + continue + + var/pos = findtext(job, "=") + var/name = null + var/value = null + + if(pos) + name = copytext(job, 1, pos) + value = copytext(job, pos + 1) + else + continue + + if(name && value) + var/datum/job/J = GetJob(name) + if(!J) continue + J.total_positions = text2num(value) + J.spawn_positions = text2num(value) + if(J.mob_type & JOB_SILICON) + J.total_positions = 0 + + return 1 + + +/datum/controller/occupations/proc/HandleFeedbackGathering() + for(var/datum/job/job in occupations) + var/tmp_str = "|[job.title]|" + + var/level1 = 0 //high + var/level2 = 0 //medium + var/level3 = 0 //low + var/level4 = 0 //never + var/level5 = 0 //banned + var/level6 = 0 //account too young + for(var/mob/new_player/player in player_list) + if(!(player.ready && player.mind && !player.mind.assigned_role)) + continue //This player is not ready + if(jobban_isbanned(player, job.title)) + level5++ + continue + if(!job.player_old_enough(player.client)) + level6++ + continue + //VOREStation Add + if(!job.player_has_enough_playtime(player.client)) + level6++ + continue + //VOREStation Add End + if(player.client.prefs.GetJobDepartment(job, 1) & job.flag) + level1++ + else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag) + level2++ + else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag) + level3++ + else level4++ //not selected + + tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-" + feedback_add_details("job_preferences",tmp_str) + +/datum/controller/occupations/proc/LateSpawn(var/client/C, var/rank) + + var/datum/spawnpoint/spawnpos + var/fail_deadly = FALSE + + var/datum/job/J = SSjob.get_job(rank) + fail_deadly = J?.offmap_spawn + + //Spawn them at their preferred one + if(C && C.prefs.spawnpoint) + if(!(C.prefs.spawnpoint in using_map.allowed_spawns)) + if(fail_deadly) + to_chat(C, "Your chosen spawnpoint is unavailable for this map and your job requires a specific spawnpoint. Please correct your spawn point choice.") + return + else + to_chat(C, "Your chosen spawnpoint ([C.prefs.spawnpoint]) is unavailable for the current map. Spawning you at one of the enabled spawn points instead.") + spawnpos = null + else + spawnpos = spawntypes[C.prefs.spawnpoint] + + //We will return a list key'd by "turf" and "msg" + . = list("turf","msg") + if(spawnpos && istype(spawnpos) && spawnpos.turfs.len) + if(spawnpos.check_job_spawning(rank)) + .["turf"] = spawnpos.get_spawn_position() + .["msg"] = spawnpos.msg + .["channel"] = spawnpos.announce_channel + else + if(fail_deadly) + to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Please correct your spawn point choice.") + return + to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") + var/spawning = pick(latejoin) + .["turf"] = get_turf(spawning) + .["msg"] = "will arrive at the station shortly" + else if(!fail_deadly) + var/spawning = pick(latejoin) + .["turf"] = get_turf(spawning) + .["msg"] = "has arrived on the station" diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 104930f9c54..a657ecfbf27 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -1,88 +1,88 @@ - -var/const/ENGSEC =(1<<0) - -var/const/CAPTAIN =(1<<0) -var/const/HOS =(1<<1) -var/const/WARDEN =(1<<2) -var/const/DETECTIVE =(1<<3) -var/const/OFFICER =(1<<4) -var/const/CHIEF =(1<<5) -var/const/ENGINEER =(1<<6) -var/const/ATMOSTECH =(1<<7) -var/const/AI =(1<<8) -var/const/CYBORG =(1<<9) -var/const/CLOWN =(1<<13) -var/const/MIME =(1<<14) -var/const/INTERN =(1<<15) - -var/const/MEDSCI =(1<<1) - -var/const/RD =(1<<0) -var/const/SCIENTIST =(1<<1) -var/const/CHEMIST =(1<<2) -var/const/CMO =(1<<3) -var/const/DOCTOR =(1<<4) -var/const/GENETICIST =(1<<5) -var/const/VIROLOGIST =(1<<6) -var/const/PSYCHIATRIST =(1<<7) -var/const/ROBOTICIST =(1<<8) -var/const/XENOBIOLOGIST =(1<<9) -var/const/PARAMEDIC =(1<<10) -var/const/PATHFINDER =(1<<11) -var/const/EXPLORER =(1<<12) -var/const/SAR =(1<<13) -var/const/XENOBOTANIST =(1<<14) - -var/const/CIVILIAN =(1<<2) - -var/const/HOP =(1<<0) -var/const/BARTENDER =(1<<1) -var/const/BOTANIST =(1<<2) -var/const/CHEF =(1<<3) -var/const/JANITOR =(1<<4) -var/const/LIBRARIAN =(1<<5) -var/const/QUARTERMASTER =(1<<6) -var/const/CARGOTECH =(1<<7) -var/const/MINER =(1<<8) -var/const/LAWYER =(1<<9) -var/const/CHAPLAIN =(1<<10) -var/const/ASSISTANT =(1<<11) -var/const/BRIDGE =(1<<12) -var/const/PILOT =(1<<13) -var/const/ENTERTAINER =(1<<14) -var/const/ENTREPRENEUR =(1<<15) - -//VOREStation Add -var/const/TALON =(1<<3) - -var/const/TALCAP =(1<<0) -var/const/TALPIL =(1<<1) -var/const/TALDOC =(1<<2) -var/const/TALSEC =(1<<3) -var/const/TALENG =(1<<4) -var/const/TALMIN =(1<<5) -//VOREStation Add End - -/proc/guest_jobbans(var/job) - return ( (job in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) ) - -/proc/get_job_datums() - var/list/occupations = list() - var/list/all_jobs = typesof(/datum/job) - - for(var/A in all_jobs) - var/datum/job/job = new A() - if(!job) continue - occupations += job - - return occupations - -/proc/get_alternate_titles(var/job) - var/list/jobs = get_job_datums() - var/list/titles = list() - - for(var/datum/job/J in jobs) - if(J.title == job) - titles = J.alt_titles - - return titles + +var/const/ENGSEC =(1<<0) + +var/const/CAPTAIN =(1<<0) +var/const/HOS =(1<<1) +var/const/WARDEN =(1<<2) +var/const/DETECTIVE =(1<<3) +var/const/OFFICER =(1<<4) +var/const/CHIEF =(1<<5) +var/const/ENGINEER =(1<<6) +var/const/ATMOSTECH =(1<<7) +var/const/AI =(1<<8) +var/const/CYBORG =(1<<9) +var/const/CLOWN =(1<<13) +var/const/MIME =(1<<14) +var/const/INTERN =(1<<15) + +var/const/MEDSCI =(1<<1) + +var/const/RD =(1<<0) +var/const/SCIENTIST =(1<<1) +var/const/CHEMIST =(1<<2) +var/const/CMO =(1<<3) +var/const/DOCTOR =(1<<4) +var/const/GENETICIST =(1<<5) +var/const/VIROLOGIST =(1<<6) +var/const/PSYCHIATRIST =(1<<7) +var/const/ROBOTICIST =(1<<8) +var/const/XENOBIOLOGIST =(1<<9) +var/const/PARAMEDIC =(1<<10) +var/const/PATHFINDER =(1<<11) +var/const/EXPLORER =(1<<12) +var/const/SAR =(1<<13) +var/const/XENOBOTANIST =(1<<14) + +var/const/CIVILIAN =(1<<2) + +var/const/HOP =(1<<0) +var/const/BARTENDER =(1<<1) +var/const/BOTANIST =(1<<2) +var/const/CHEF =(1<<3) +var/const/JANITOR =(1<<4) +var/const/LIBRARIAN =(1<<5) +var/const/QUARTERMASTER =(1<<6) +var/const/CARGOTECH =(1<<7) +var/const/MINER =(1<<8) +var/const/LAWYER =(1<<9) +var/const/CHAPLAIN =(1<<10) +var/const/ASSISTANT =(1<<11) +var/const/BRIDGE =(1<<12) +var/const/PILOT =(1<<13) +var/const/ENTERTAINER =(1<<14) +var/const/ENTREPRENEUR =(1<<15) + +//VOREStation Add +var/const/TALON =(1<<3) + +var/const/TALCAP =(1<<0) +var/const/TALPIL =(1<<1) +var/const/TALDOC =(1<<2) +var/const/TALSEC =(1<<3) +var/const/TALENG =(1<<4) +var/const/TALMIN =(1<<5) +//VOREStation Add End + +/proc/guest_jobbans(var/job) + return ( (job in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) ) + +/proc/get_job_datums() + var/list/occupations = list() + var/list/all_jobs = typesof(/datum/job) + + for(var/A in all_jobs) + var/datum/job/job = new A() + if(!job) continue + occupations += job + + return occupations + +/proc/get_alternate_titles(var/job) + var/list/jobs = get_job_datums() + var/list/titles = list() + + for(var/datum/job/J in jobs) + if(J.title == job) + titles = J.alt_titles + + return titles diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index b47e3787a85..d32da8494c5 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,121 +1,121 @@ -#define WHITELISTFILE "data/whitelist.txt" - -var/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if(config.usewhitelist) - load_whitelist() - return 1 - -/proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!whitelist.len) whitelist = null - -/proc/check_whitelist(mob/M /*, var/rank*/) - if(!whitelist) - return 0 - return ("[M.ckey]" in whitelist) - -/var/list/alien_whitelist = list() - -/hook/startup/proc/loadAlienWhitelist() - if(config.usealienwhitelist) - load_alienwhitelist() - return 1 - -/proc/load_alienwhitelist() - var/text = file2text("config/alienwhitelist.txt") - if (!text) - log_misc("Failed to load config/alienwhitelist.txt") - else - var/lines = splittext(text, "\n") // Now we've got a bunch of "ckey = something" strings in a list - for(var/line in lines) - var/list/left_and_right = splittext(line, " - ") // Split it on the dash into left and right - if(LAZYLEN(left_and_right) != 2) - warning("Alien whitelist entry is invalid: [line]") // If we didn't end up with a left and right, the line is bad - continue - var/key = left_and_right[1] - if(key != ckey(key)) - warning("Alien whitelist entry appears to have key, not ckey: [line]") // The key contains invalid ckey characters - continue - var/list/our_whitelists = alien_whitelist[key] // Try to see if we have one already and add to it - if(!our_whitelists) // Guess this is their first/only whitelist entry - our_whitelists = list() - alien_whitelist[key] = our_whitelists - our_whitelists += left_and_right[2] - -/proc/is_alien_whitelisted(mob/M, var/datum/species/species) - //They are admin or the whitelist isn't in use - if(whitelist_overrides(M)) - return TRUE - - //You did something wrong - if(!M || !species) - return FALSE - - //The species isn't even whitelisted - if(!(species.spawn_flags & SPECIES_IS_WHITELISTED)) - return TRUE - - //Search the whitelist - var/list/our_whitelists = alien_whitelist[M.ckey] - if("All" in our_whitelists) - return TRUE - if(species.name in our_whitelists) - return TRUE - - // Go apply! - return FALSE - -/proc/is_lang_whitelisted(mob/M, var/datum/language/language) - //They are admin or the whitelist isn't in use - if(whitelist_overrides(M)) - return TRUE - - //You did something wrong - if(!M || !language) - return FALSE - - //The language isn't even whitelisted - if(!(language.flags & WHITELISTED)) - return TRUE - - //Search the whitelist - var/list/our_whitelists = alien_whitelist[M.ckey] - if("All" in our_whitelists) - return TRUE - if(language.name in our_whitelists) - return TRUE - - return FALSE - -/proc/is_borg_whitelisted(mob/M, var/module) - //They are admin or the whitelist isn't in use - if(whitelist_overrides(M)) - return 1 - - //You did something wrong - if(!M || !module) - return 0 - - //Module is not even whitelisted - if(!(module in whitelisted_module_types)) - return 1 - - //If we have a loaded file, search it - if(alien_whitelist) - for (var/s in alien_whitelist) - if(findtext(s,"[M.ckey] - [module]")) - return 1 - if(findtext(s,"[M.ckey] - All")) - return 1 - -/proc/whitelist_overrides(mob/M) - if(!config.usealienwhitelist) - return TRUE - if(check_rights(R_ADMIN|R_EVENT, 0, M)) - return TRUE - - return FALSE - -#undef WHITELISTFILE +#define WHITELISTFILE "data/whitelist.txt" + +var/list/whitelist = list() + +/hook/startup/proc/loadWhitelist() + if(config.usewhitelist) + load_whitelist() + return 1 + +/proc/load_whitelist() + whitelist = file2list(WHITELISTFILE) + if(!whitelist.len) whitelist = null + +/proc/check_whitelist(mob/M /*, var/rank*/) + if(!whitelist) + return 0 + return ("[M.ckey]" in whitelist) + +/var/list/alien_whitelist = list() + +/hook/startup/proc/loadAlienWhitelist() + if(config.usealienwhitelist) + load_alienwhitelist() + return 1 + +/proc/load_alienwhitelist() + var/text = file2text("config/alienwhitelist.txt") + if (!text) + log_misc("Failed to load config/alienwhitelist.txt") + else + var/lines = splittext(text, "\n") // Now we've got a bunch of "ckey = something" strings in a list + for(var/line in lines) + var/list/left_and_right = splittext(line, " - ") // Split it on the dash into left and right + if(LAZYLEN(left_and_right) != 2) + warning("Alien whitelist entry is invalid: [line]") // If we didn't end up with a left and right, the line is bad + continue + var/key = left_and_right[1] + if(key != ckey(key)) + warning("Alien whitelist entry appears to have key, not ckey: [line]") // The key contains invalid ckey characters + continue + var/list/our_whitelists = alien_whitelist[key] // Try to see if we have one already and add to it + if(!our_whitelists) // Guess this is their first/only whitelist entry + our_whitelists = list() + alien_whitelist[key] = our_whitelists + our_whitelists += left_and_right[2] + +/proc/is_alien_whitelisted(mob/M, var/datum/species/species) + //They are admin or the whitelist isn't in use + if(whitelist_overrides(M)) + return TRUE + + //You did something wrong + if(!M || !species) + return FALSE + + //The species isn't even whitelisted + if(!(species.spawn_flags & SPECIES_IS_WHITELISTED)) + return TRUE + + //Search the whitelist + var/list/our_whitelists = alien_whitelist[M.ckey] + if("All" in our_whitelists) + return TRUE + if(species.name in our_whitelists) + return TRUE + + // Go apply! + return FALSE + +/proc/is_lang_whitelisted(mob/M, var/datum/language/language) + //They are admin or the whitelist isn't in use + if(whitelist_overrides(M)) + return TRUE + + //You did something wrong + if(!M || !language) + return FALSE + + //The language isn't even whitelisted + if(!(language.flags & WHITELISTED)) + return TRUE + + //Search the whitelist + var/list/our_whitelists = alien_whitelist[M.ckey] + if("All" in our_whitelists) + return TRUE + if(language.name in our_whitelists) + return TRUE + + return FALSE + +/proc/is_borg_whitelisted(mob/M, var/module) + //They are admin or the whitelist isn't in use + if(whitelist_overrides(M)) + return 1 + + //You did something wrong + if(!M || !module) + return 0 + + //Module is not even whitelisted + if(!(module in whitelisted_module_types)) + return 1 + + //If we have a loaded file, search it + if(alien_whitelist) + for (var/s in alien_whitelist) + if(findtext(s,"[M.ckey] - [module]")) + return 1 + if(findtext(s,"[M.ckey] - All")) + return 1 + +/proc/whitelist_overrides(mob/M) + if(!config.usealienwhitelist) + return TRUE + if(check_rights(R_ADMIN|R_EVENT, 0, M)) + return TRUE + + return FALSE + +#undef WHITELISTFILE diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index 24b4ca979b4..b9242059c90 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -1,51 +1,51 @@ -/obj/machinery/bluespace_beacon - icon = 'icons/obj/objects.dmi' - icon_state = "floor_beaconf" - name = "Bluespace Gigabeacon" - desc = "A device that draws power from bluespace and creates a permanent tracking beacon." - level = 1 // underfloor - layer = UNDER_JUNK_LAYER - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 0 - var/obj/item/device/radio/beacon/Beacon - -/obj/machinery/bluespace_beacon/New() - ..() - var/turf/T = src.loc - Beacon = new /obj/item/device/radio/beacon - Beacon.invisibility = INVISIBILITY_MAXIMUM - Beacon.loc = T - - hide(!T.is_plating()) - -/obj/machinery/bluespace_beacon/Destroy() - if(Beacon) - qdel(Beacon) - ..() - -// update the invisibility and icon -/obj/machinery/bluespace_beacon/hide(var/intact) - invisibility = intact ? 101 : 0 - update_icon() - -// update the icon_state -/obj/machinery/bluespace_beacon/update_icon() - var/state="floor_beacon" - - if(invisibility) - icon_state = "[state]f" - else - icon_state = "[state]" - -/obj/machinery/bluespace_beacon/process() - if(!Beacon) - var/turf/T = src.loc - Beacon = new /obj/item/device/radio/beacon - Beacon.invisibility = INVISIBILITY_MAXIMUM - Beacon.loc = T - if(Beacon) - if(Beacon.loc != src.loc) - Beacon.loc = src.loc - +/obj/machinery/bluespace_beacon + icon = 'icons/obj/objects.dmi' + icon_state = "floor_beaconf" + name = "Bluespace Gigabeacon" + desc = "A device that draws power from bluespace and creates a permanent tracking beacon." + level = 1 // underfloor + layer = UNDER_JUNK_LAYER + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 0 + var/obj/item/device/radio/beacon/Beacon + +/obj/machinery/bluespace_beacon/New() + ..() + var/turf/T = src.loc + Beacon = new /obj/item/device/radio/beacon + Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.loc = T + + hide(!T.is_plating()) + +/obj/machinery/bluespace_beacon/Destroy() + if(Beacon) + qdel(Beacon) + ..() + +// update the invisibility and icon +/obj/machinery/bluespace_beacon/hide(var/intact) + invisibility = intact ? 101 : 0 + update_icon() + +// update the icon_state +/obj/machinery/bluespace_beacon/update_icon() + var/state="floor_beacon" + + if(invisibility) + icon_state = "[state]f" + else + icon_state = "[state]" + +/obj/machinery/bluespace_beacon/process() + if(!Beacon) + var/turf/T = src.loc + Beacon = new /obj/item/device/radio/beacon + Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.loc = T + if(Beacon) + if(Beacon.loc != src.loc) + Beacon.loc = src.loc + update_icon() \ No newline at end of file diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 20677bb0ff0..b64f04d16f0 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -34,7 +34,7 @@ to_chat(user, "You load [result] lengths of cable into [src].") return - if(O.is_wirecutter()) + if(O.has_tool_quality(TOOL_WIRECUTTER)) if(cable && cable.get_amount()) var/m = round(input(usr, "Please specify the length of cable to cut", "Cut cable", min(cable.get_amount(), 30)) as num, 1) m = min(m, cable.get_amount()) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index fcabe9f5ce0..2dbe70576e1 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -1,136 +1,136 @@ -/obj/machinery/optable - name = "Operating Table" - desc = "Used for advanced medical procedures." - icon = 'icons/obj/surgery_vr.dmi' - icon_state = "table2-idle" - density = TRUE - anchored = TRUE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 1 - active_power_usage = 5 - surgery_odds = 100 - throwpass = 1 - var/mob/living/carbon/human/victim = null - var/strapped = 0.0 - var/obj/machinery/computer/operating/computer = null - -/obj/machinery/optable/New() - ..() - for(var/direction in list(NORTH,EAST,SOUTH,WEST)) - computer = locate(/obj/machinery/computer/operating, get_step(src, direction)) - if(computer) - computer.table = src - break -// spawn(100) //Wont the MC just call this process() before and at the 10 second mark anyway? -// process() - -/obj/machinery/optable/ex_act(severity) - switch(severity) - if(1.0) - //SN src = null - qdel(src) - return - if(2.0) - if(prob(50)) - //SN src = null - qdel(src) - return - if(3.0) - if(prob(25)) - density = FALSE - else - return - -/obj/machinery/optable/attack_hand(mob/user as mob) - if(HULK in usr.mutations) - visible_message("\The [usr] destroys \the [src]!") - density = FALSE - qdel(src) - return - -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - return FALSE - -/obj/machinery/optable/proc/check_victim() - if(locate(/mob/living/carbon/human, src.loc)) - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) - if(M.lying) - victim = M - if(M.pulse) - if(M.stat) - icon_state = "table2-sleep" - else - icon_state = "table2-active" - else - icon_state = "table2-dead" - return 1 - victim = null - icon_state = "table2-idle" - return 0 - -/obj/machinery/optable/process() - check_victim() - -/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) - if(C == user) - user.visible_message("[user] climbs on \the [src].","You climb on \the [src].") - else - visible_message("\The [C] has been laid on \the [src] by [user].") - if(C.client) - C.client.perspective = EYE_PERSPECTIVE - C.client.eye = src - C.resting = 1 - C.loc = src.loc - for(var/obj/O in src) - O.loc = src.loc - add_fingerprint(user) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - victim = H - icon_state = H.pulse ? "table2-active" : "table2-idle" - else - icon_state = "table2-idle" - -/obj/machinery/optable/MouseDrop_T(mob/living/carbon/target, mob/living/user) - if(!istype(target) || !istype(user)) - return ..() - - if(!Adjacent(target) || !Adjacent(user)) - return ..() - - if(user.incapacitated() || !check_table(target, user)) - return ..() - - take_victim(target, user) - -/obj/machinery/optable/verb/climb_on() - set name = "Climb On Table" - set category = "Object" - set src in oview(1) - - var/mob/living/user = usr - if(!istype(user) || user.incapacitated() || !check_table(user, user)) - return - - take_victim(user, user) - -/obj/machinery/optable/attackby(obj/item/weapon/W, mob/living/carbon/user) - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - if(iscarbon(G.affecting) && check_table(G.affecting, user)) - take_victim(G.affecting, user) - qdel(W) - return - -/obj/machinery/optable/proc/check_table(mob/living/carbon/patient, mob/living/user) - check_victim() - if(victim && get_turf(victim) == get_turf(src) && victim.lying) - to_chat(user, "\The [src] is already occupied!") - return 0 - if(patient.buckled) - to_chat(user, "Unbuckle \the [patient] first!") - return 0 +/obj/machinery/optable + name = "Operating Table" + desc = "Used for advanced medical procedures." + icon = 'icons/obj/surgery_vr.dmi' + icon_state = "table2-idle" + density = TRUE + anchored = TRUE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 1 + active_power_usage = 5 + surgery_odds = 100 + throwpass = 1 + var/mob/living/carbon/human/victim = null + var/strapped = 0.0 + var/obj/machinery/computer/operating/computer = null + +/obj/machinery/optable/New() + ..() + for(var/direction in list(NORTH,EAST,SOUTH,WEST)) + computer = locate(/obj/machinery/computer/operating, get_step(src, direction)) + if(computer) + computer.table = src + break +// spawn(100) //Wont the MC just call this process() before and at the 10 second mark anyway? +// process() + +/obj/machinery/optable/ex_act(severity) + switch(severity) + if(1.0) + //SN src = null + qdel(src) + return + if(2.0) + if(prob(50)) + //SN src = null + qdel(src) + return + if(3.0) + if(prob(25)) + density = FALSE + else + return + +/obj/machinery/optable/attack_hand(mob/user as mob) + if(HULK in usr.mutations) + visible_message("\The [usr] destroys \the [src]!") + density = FALSE + qdel(src) + return + +/obj/machinery/optable/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + return FALSE + +/obj/machinery/optable/proc/check_victim() + if(locate(/mob/living/carbon/human, src.loc)) + var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) + if(M.lying) + victim = M + if(M.pulse) + if(M.stat) + icon_state = "table2-sleep" + else + icon_state = "table2-active" + else + icon_state = "table2-dead" + return 1 + victim = null + icon_state = "table2-idle" + return 0 + +/obj/machinery/optable/process() + check_victim() + +/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) + if(C == user) + user.visible_message("[user] climbs on \the [src].","You climb on \the [src].") + else + visible_message("\The [C] has been laid on \the [src] by [user].") + if(C.client) + C.client.perspective = EYE_PERSPECTIVE + C.client.eye = src + C.resting = 1 + C.loc = src.loc + for(var/obj/O in src) + O.loc = src.loc + add_fingerprint(user) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + victim = H + icon_state = H.pulse ? "table2-active" : "table2-idle" + else + icon_state = "table2-idle" + +/obj/machinery/optable/MouseDrop_T(mob/living/carbon/target, mob/living/user) + if(!istype(target) || !istype(user)) + return ..() + + if(!Adjacent(target) || !Adjacent(user)) + return ..() + + if(user.incapacitated() || !check_table(target, user)) + return ..() + + take_victim(target, user) + +/obj/machinery/optable/verb/climb_on() + set name = "Climb On Table" + set category = "Object" + set src in oview(1) + + var/mob/living/user = usr + if(!istype(user) || user.incapacitated() || !check_table(user, user)) + return + + take_victim(user, user) + +/obj/machinery/optable/attackby(obj/item/weapon/W, mob/living/carbon/user) + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(iscarbon(G.affecting) && check_table(G.affecting, user)) + take_victim(G.affecting, user) + qdel(W) + return + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient, mob/living/user) + check_victim() + if(victim && get_turf(victim) == get_turf(src) && victim.lying) + to_chat(user, "\The [src] is already occupied!") + return 0 + if(patient.buckled) + to_chat(user, "Unbuckle \the [patient] first!") + return 0 return 1 \ No newline at end of file diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 18765fcf9b0..bb86420b1c3 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -1,537 +1,537 @@ -/obj/machinery/sleep_console - name = "sleeper console" - desc = "A control panel to operate a linked sleeper with." - icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icon. - icon_state = "sleeperconsole" - var/obj/machinery/sleeper/sleeper - anchored = TRUE //About time someone fixed this. - density = TRUE //VOREStation Edit - Big console - unacidable = TRUE - dir = 8 - use_power = USE_POWER_IDLE - idle_power_usage = 40 - interact_offline = 1 - circuit = /obj/item/weapon/circuitboard/sleeper_console - clicksound = 'sound/machines/buttonbeep.ogg' - clickvol = 30 - -/obj/machinery/sleep_console/Initialize() - findsleeper() - return ..() - -/obj/machinery/sleep_console/Destroy() - if(sleeper) - sleeper.console = null - return ..() - -/obj/machinery/sleep_console/proc/findsleeper() - var/obj/machinery/sleeper/sleepernew = null - for(var/direction in GLOB.cardinal) // Loop through every direction - sleepernew = locate(/obj/machinery/sleeper, get_step(src, direction)) // Try to find a scanner in that direction - if(sleepernew) - sleeper = sleepernew - sleepernew.console = src - break //VOREStation Edit - - -/obj/machinery/sleep_console/attack_ai(var/mob/user) - return attack_hand(user) - -/obj/machinery/sleep_console/attack_hand(var/mob/user) - if(..()) - return 1 - - if(!sleeper) - findsleeper() - if(!sleeper) - to_chat(user, "Sleeper not found!") - return - - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - if(sleeper) - return tgui_interact(user) - -/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user) - if(computer_deconstruction_screwdriver(user, I)) - return - else - return attack_hand(user) - -/obj/machinery/sleep_console/power_change() - ..() - if(stat & (NOPOWER|BROKEN)) - icon_state = "sleeperconsole-p" - else - icon_state = initial(icon_state) - -/obj/machinery/sleep_console/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Sleeper", "Sleeper") - ui.open() - -/obj/machinery/sleep_console/tgui_data(mob/user) - if(sleeper) - return sleeper.tgui_data(user) - return null - -/obj/machinery/sleep_console/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(sleeper) - return sleeper.tgui_act(action, params, ui, state) - return ..() - -/obj/machinery/sleeper - name = "sleeper" - desc = "A stasis pod with built-in injectors, a dialysis machine, and a limited health scanner." - icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icons - icon_state = "sleeper_0" - density = TRUE - anchored = TRUE - unacidable = TRUE - circuit = /obj/item/weapon/circuitboard/sleeper - var/mob/living/carbon/human/occupant = null - var/list/available_chemicals = list() - var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") - var/amounts = list(5, 10) - var/obj/item/weapon/reagent_containers/glass/beaker = null - var/filtering = 0 - var/pumping = 0 - // Currently never changes. On Paradise, max_chem and min_health are based on the matter bins in the sleeper. - var/max_chem = 20 - var/initial_bin_rating = 1 - var/min_health = -101 - var/obj/machinery/sleep_console/console - var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) - var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) - var/controls_inside = FALSE - var/auto_eject_dead = FALSE - - use_power = USE_POWER_IDLE - idle_power_usage = 15 - active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. - -/obj/machinery/sleeper/Initialize() - . = ..() - beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src) - default_apply_parts() - update_icon() - -/obj/machinery/sleeper/Destroy() - if(console) - console.sleeper = null - return ..() - -/obj/machinery/sleeper/RefreshParts(var/limited = 0) - var/man_rating = 0 - var/cap_rating = 0 - - available_chemicals.Cut() - available_chemicals = base_chemicals.Copy() - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - - cap_rating = max(1, round(cap_rating / 2)) - - update_idle_power_usage(initial(idle_power_usage) / cap_rating) - update_active_power_usage(initial(active_power_usage) / cap_rating) - - if(!limited) - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - man_rating += P.rating - 1 - - var/list/new_chemicals = list() - - if(man_rating >= 4) // Alien tech. - var/reag_ID = pickweight(list( - "healing_nanites" = 10, - "shredding_nanites" = 5, - "irradiated_nanites" = 5, - "neurophage_nanites" = 2) - ) - new_chemicals[reag_ID] = "Nanite" - if(man_rating >= 3) // Anomalous tech. - new_chemicals["immunosuprizine"] = "Immunosuprizine" - if(man_rating >= 2) // Tier 3. - new_chemicals["spaceacillin"] = "Spaceacillin" - if(man_rating >= 1) // Tier 2. - new_chemicals["leporazine"] = "Leporazine" - - if(new_chemicals.len) - available_chemicals += new_chemicals - return - -/obj/machinery/sleeper/attack_hand(var/mob/user) - if(!controls_inside) - return FALSE - - if(user == occupant) - tgui_interact(user) - -/obj/machinery/sleeper/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Sleeper", "Sleeper") - ui.open() - -/obj/machinery/sleeper/tgui_data(mob/user) - var/data[0] - data["amounts"] = amounts - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - // var/crisis = 0 - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(isanimal(occupant)) - var/mob/living/simple_mob/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - - // crisis = (occupant.health < min_health) - // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent - // Runtime is aptly named, isn't she? - if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) - occupantData["bloodLevel"] = blood_volume - occupantData["bloodMax"] = occupant.species.blood_volume - occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.b_type - - data["occupant"] = occupantData - data["maxchem"] = max_chem - data["minhealth"] = min_health - data["dialysis"] = filtering - data["stomachpumping"] = pumping - data["auto_eject_dead"] = auto_eject_dead - if(beaker) - data["isBeakerLoaded"] = 1 - if(beaker.reagents) - data["beakerMaxSpace"] = beaker.reagents.maximum_volume - data["beakerFreeSpace"] = beaker.reagents.get_free_space() - else - data["beakerMaxSpace"] = 0 - data["beakerFreeSpace"] = 0 - else - data["isBeakerLoaded"] = FALSE - - - var/stasis_level_name = "Error!" - for(var/N in stasis_choices) - if(stasis_choices[N] == stasis_level) - stasis_level_name = N - break - data["stasis"] = stasis_level_name - - var/chemicals[0] - for(var/re in available_chemicals) - var/datum/reagent/temp = SSchemistry.chemical_reagents[re] - if(temp) - var/reagent_amount = 0 - var/pretty_amount - var/injectable = occupant ? 1 : 0 - var/overdosing = 0 - var/caution = 0 // To make things clear that you're coming close to an overdose - // if(crisis && !(temp.id in emergency_chems)) - // injectable = 0 - - if(occupant && occupant.reagents) - reagent_amount = occupant.reagents.get_reagent_amount(temp.id) - // If they're mashing the highest concentration, they get one warning - if(temp.overdose && reagent_amount + 10 > (temp.overdose * occupant?.species.chemOD_threshold)) - caution = 1 - if(temp.overdose && reagent_amount > (temp.overdose * occupant?.species.chemOD_threshold)) - overdosing = 1 - - pretty_amount = round(reagent_amount, 0.05) - - chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) - data["chemicals"] = chemicals - return data - - -/obj/machinery/sleeper/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - if(!controls_inside && usr == occupant) - return - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - - . = TRUE - switch(action) - if("chemical") - if(!occupant) - return - if(occupant.stat == DEAD) - var/datum/gender/G = gender_datums[occupant.get_visible_gender()] - to_chat(usr, "This person has no life to preserve anymore. Take [G.him] to a department capable of reanimating [G.him].") - return - var/chemical = params["chemid"] - var/amount = text2num(params["amount"]) - if(!length(chemical) || amount <= 0) - return - if(occupant.health > min_health) //|| (chemical in emergency_chems)) - inject_chemical(usr, chemical, amount) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - if("removebeaker") - remove_beaker() - if("togglefilter") - toggle_filter() - if("togglepump") - toggle_pump() - if("ejectify") - go_out() - if("changestasis") - var/new_stasis = tgui_input_list(usr, "Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level", stasis_choices) - if(new_stasis) - stasis_level = stasis_choices[new_stasis] - if("auto_eject_dead_on") - auto_eject_dead = TRUE - if("auto_eject_dead_off") - auto_eject_dead = FALSE - else - return FALSE - add_fingerprint(usr) - -/obj/machinery/sleeper/process() - if(stat & (NOPOWER|BROKEN)) - return - if(occupant) - if(auto_eject_dead && occupant.stat == DEAD) - playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) - go_out() - return - occupant.Stasis(stasis_level) - - if(filtering > 0) - if(beaker) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - var/pumped = 0 - for(var/datum/reagent/x in occupant.reagents.reagent_list) - occupant.reagents.trans_to_obj(beaker, 3) - pumped++ - if(ishuman(occupant)) - occupant.vessel.trans_to_obj(beaker, pumped + 1) - else - toggle_filter() - - if(pumping > 0) - if(beaker) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - for(var/datum/reagent/x in occupant.ingested.reagent_list) - occupant.ingested.trans_to_obj(beaker, 3) - else - toggle_pump() - -/obj/machinery/sleeper/update_icon() - icon_state = "sleeper_[occupant ? "1" : "0"]" - -/obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) - add_fingerprint(user) - if(istype(I, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = I - if(G.affecting) - go_in(G.affecting, user) - return - if(istype(I, /obj/item/weapon/reagent_containers/glass)) - if(!beaker) - beaker = I - user.drop_item() - I.loc = src - user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].") - else - to_chat(user, "\The [src] has a beaker already.") - return - if(!occupant) - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - if(default_part_replacement(user, I)) - return - -/obj/machinery/sleeper/verb/move_eject() - set name = "Eject occupant" - set category = "Object" - set src in oview(1) - if(usr == occupant) - switch(usr.stat) - if(DEAD) - return - if(UNCONSCIOUS) - to_chat(usr, "You struggle through the haze to hit the eject button. This will take a couple of minutes...") - if(do_after(usr, 2 MINUTES, src)) - go_out() - if(CONSCIOUS) - go_out() - else - if(usr.stat != CONSCIOUS) - return - go_out() - add_fingerprint(usr) - -/obj/machinery/sleeper/MouseDrop_T(var/mob/target, var/mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) - return - go_in(target, user) - -/obj/machinery/sleeper/relaymove(var/mob/user) - ..() - if(user.incapacitated()) - return - go_out() - -/obj/machinery/sleeper/emp_act(var/severity) - if(filtering) - toggle_filter() - - if(pumping) - toggle_pump() - - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(occupant) - go_out() - - ..(severity) -/obj/machinery/sleeper/proc/toggle_filter() - if(!occupant || !beaker) - filtering = 0 - return - filtering = !filtering - -/obj/machinery/sleeper/proc/toggle_pump() - if(!occupant || !beaker) - pumping = 0 - return - pumping = !pumping - -/obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) - if(!M) - return - if(stat & (BROKEN|NOPOWER)) - return - if(occupant) - to_chat(user, "\The [src] is already occupied.") - return - if(!ishuman(M)) - to_chat(user, "\The [src] is not designed for that organism!") - return - if(M == user) - visible_message("\The [user] starts climbing into \the [src].") - else - visible_message("\The [user] starts putting [M] into \the [src].") - - if(do_after(user, 20)) - if(occupant) - to_chat(user, "\The [src] is already occupied.") - return - M.stop_pulling() - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.loc = src - update_use_power(USE_POWER_ACTIVE) - occupant = M - update_icon() - -/obj/machinery/sleeper/proc/go_out() - if(!occupant || occupant.loc != src) - occupant = null // JUST IN CASE - return - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.Stasis(0) - occupant.loc = src.loc - occupant = null - for(var/atom/movable/A in src) // In case an object was dropped inside or something - if(A == beaker || A == circuit) - continue - if(A in component_parts) - continue - A.loc = src.loc - update_use_power(USE_POWER_IDLE) - update_icon() - toggle_filter() - toggle_pump() - -/obj/machinery/sleeper/proc/remove_beaker() - if(beaker) - beaker.loc = src.loc - beaker = null - toggle_filter() - -/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) - if(stat & (BROKEN|NOPOWER)) - return - if(!(amount in amounts)) - return - - if(occupant && occupant.reagents) - if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) - use_power(amount * CHEM_SYNTH_ENERGY) - occupant.reagents.add_reagent(chemical, amount) - to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.") - else - to_chat(user, "The subject has too many chemicals in their bloodstream.") - else - to_chat(user, "There's no suitable occupant in \the [src].") - -//Survival/Stasis sleepers -/obj/machinery/sleeper/survival_pod - desc = "A limited functionality sleeper, all it can do is put patients into stasis. It lacks the medication and configuration of the larger units." - icon_state = "sleeper" - stasis_level = 100 //Just one setting - -/obj/machinery/sleeper/survival_pod/Initialize() - . = ..() - RefreshParts(1) +/obj/machinery/sleep_console + name = "sleeper console" + desc = "A control panel to operate a linked sleeper with." + icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icon. + icon_state = "sleeperconsole" + var/obj/machinery/sleeper/sleeper + anchored = TRUE //About time someone fixed this. + density = TRUE //VOREStation Edit - Big console + unacidable = TRUE + dir = 8 + use_power = USE_POWER_IDLE + idle_power_usage = 40 + interact_offline = 1 + circuit = /obj/item/weapon/circuitboard/sleeper_console + clicksound = 'sound/machines/buttonbeep.ogg' + clickvol = 30 + +/obj/machinery/sleep_console/Initialize() + findsleeper() + return ..() + +/obj/machinery/sleep_console/Destroy() + if(sleeper) + sleeper.console = null + return ..() + +/obj/machinery/sleep_console/proc/findsleeper() + var/obj/machinery/sleeper/sleepernew = null + for(var/direction in GLOB.cardinal) // Loop through every direction + sleepernew = locate(/obj/machinery/sleeper, get_step(src, direction)) // Try to find a scanner in that direction + if(sleepernew) + sleeper = sleepernew + sleepernew.console = src + break //VOREStation Edit + + +/obj/machinery/sleep_console/attack_ai(var/mob/user) + return attack_hand(user) + +/obj/machinery/sleep_console/attack_hand(var/mob/user) + if(..()) + return 1 + + if(!sleeper) + findsleeper() + if(!sleeper) + to_chat(user, "Sleeper not found!") + return + + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + if(sleeper) + return tgui_interact(user) + +/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user) + if(computer_deconstruction_screwdriver(user, I)) + return + else + return attack_hand(user) + +/obj/machinery/sleep_console/power_change() + ..() + if(stat & (NOPOWER|BROKEN)) + icon_state = "sleeperconsole-p" + else + icon_state = initial(icon_state) + +/obj/machinery/sleep_console/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Sleeper", "Sleeper") + ui.open() + +/obj/machinery/sleep_console/tgui_data(mob/user) + if(sleeper) + return sleeper.tgui_data(user) + return null + +/obj/machinery/sleep_console/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(sleeper) + return sleeper.tgui_act(action, params, ui, state) + return ..() + +/obj/machinery/sleeper + name = "sleeper" + desc = "A stasis pod with built-in injectors, a dialysis machine, and a limited health scanner." + icon = 'icons/obj/Cryogenic2_vr.dmi' //VOREStation Edit - Better icons + icon_state = "sleeper_0" + density = TRUE + anchored = TRUE + unacidable = TRUE + circuit = /obj/item/weapon/circuitboard/sleeper + var/mob/living/carbon/human/occupant = null + var/list/available_chemicals = list() + var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") + var/amounts = list(5, 10) + var/obj/item/weapon/reagent_containers/glass/beaker = null + var/filtering = 0 + var/pumping = 0 + // Currently never changes. On Paradise, max_chem and min_health are based on the matter bins in the sleeper. + var/max_chem = 20 + var/initial_bin_rating = 1 + var/min_health = -101 + var/obj/machinery/sleep_console/console + var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) + var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) + var/controls_inside = FALSE + var/auto_eject_dead = FALSE + + use_power = USE_POWER_IDLE + idle_power_usage = 15 + active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. + +/obj/machinery/sleeper/Initialize() + . = ..() + beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src) + default_apply_parts() + update_icon() + +/obj/machinery/sleeper/Destroy() + if(console) + console.sleeper = null + return ..() + +/obj/machinery/sleeper/RefreshParts(var/limited = 0) + var/man_rating = 0 + var/cap_rating = 0 + + available_chemicals.Cut() + available_chemicals = base_chemicals.Copy() + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + + cap_rating = max(1, round(cap_rating / 2)) + + update_idle_power_usage(initial(idle_power_usage) / cap_rating) + update_active_power_usage(initial(active_power_usage) / cap_rating) + + if(!limited) + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + man_rating += P.rating - 1 + + var/list/new_chemicals = list() + + if(man_rating >= 4) // Alien tech. + var/reag_ID = pickweight(list( + "healing_nanites" = 10, + "shredding_nanites" = 5, + "irradiated_nanites" = 5, + "neurophage_nanites" = 2) + ) + new_chemicals[reag_ID] = "Nanite" + if(man_rating >= 3) // Anomalous tech. + new_chemicals["immunosuprizine"] = "Immunosuprizine" + if(man_rating >= 2) // Tier 3. + new_chemicals["spaceacillin"] = "Spaceacillin" + if(man_rating >= 1) // Tier 2. + new_chemicals["leporazine"] = "Leporazine" + + if(new_chemicals.len) + available_chemicals += new_chemicals + return + +/obj/machinery/sleeper/attack_hand(var/mob/user) + if(!controls_inside) + return FALSE + + if(user == occupant) + tgui_interact(user) + +/obj/machinery/sleeper/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Sleeper", "Sleeper") + ui.open() + +/obj/machinery/sleeper/tgui_data(mob/user) + var/data[0] + data["amounts"] = amounts + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + // var/crisis = 0 + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(isanimal(occupant)) + var/mob/living/simple_mob/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + + // crisis = (occupant.health < min_health) + // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent + // Runtime is aptly named, isn't she? + if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) + occupantData["bloodLevel"] = blood_volume + occupantData["bloodMax"] = occupant.species.blood_volume + occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.b_type + + data["occupant"] = occupantData + data["maxchem"] = max_chem + data["minhealth"] = min_health + data["dialysis"] = filtering + data["stomachpumping"] = pumping + data["auto_eject_dead"] = auto_eject_dead + if(beaker) + data["isBeakerLoaded"] = 1 + if(beaker.reagents) + data["beakerMaxSpace"] = beaker.reagents.maximum_volume + data["beakerFreeSpace"] = beaker.reagents.get_free_space() + else + data["beakerMaxSpace"] = 0 + data["beakerFreeSpace"] = 0 + else + data["isBeakerLoaded"] = FALSE + + + var/stasis_level_name = "Error!" + for(var/N in stasis_choices) + if(stasis_choices[N] == stasis_level) + stasis_level_name = N + break + data["stasis"] = stasis_level_name + + var/chemicals[0] + for(var/re in available_chemicals) + var/datum/reagent/temp = SSchemistry.chemical_reagents[re] + if(temp) + var/reagent_amount = 0 + var/pretty_amount + var/injectable = occupant ? 1 : 0 + var/overdosing = 0 + var/caution = 0 // To make things clear that you're coming close to an overdose + // if(crisis && !(temp.id in emergency_chems)) + // injectable = 0 + + if(occupant && occupant.reagents) + reagent_amount = occupant.reagents.get_reagent_amount(temp.id) + // If they're mashing the highest concentration, they get one warning + if(temp.overdose && reagent_amount + 10 > (temp.overdose * occupant?.species.chemOD_threshold)) + caution = 1 + if(temp.overdose && reagent_amount > (temp.overdose * occupant?.species.chemOD_threshold)) + overdosing = 1 + + pretty_amount = round(reagent_amount, 0.05) + + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) + data["chemicals"] = chemicals + return data + + +/obj/machinery/sleeper/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(!controls_inside && usr == occupant) + return + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + + . = TRUE + switch(action) + if("chemical") + if(!occupant) + return + if(occupant.stat == DEAD) + var/datum/gender/G = gender_datums[occupant.get_visible_gender()] + to_chat(usr, "This person has no life to preserve anymore. Take [G.him] to a department capable of reanimating [G.him].") + return + var/chemical = params["chemid"] + var/amount = text2num(params["amount"]) + if(!length(chemical) || amount <= 0) + return + if(occupant.health > min_health) //|| (chemical in emergency_chems)) + inject_chemical(usr, chemical, amount) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + if("removebeaker") + remove_beaker() + if("togglefilter") + toggle_filter() + if("togglepump") + toggle_pump() + if("ejectify") + go_out() + if("changestasis") + var/new_stasis = tgui_input_list(usr, "Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level", stasis_choices) + if(new_stasis) + stasis_level = stasis_choices[new_stasis] + if("auto_eject_dead_on") + auto_eject_dead = TRUE + if("auto_eject_dead_off") + auto_eject_dead = FALSE + else + return FALSE + add_fingerprint(usr) + +/obj/machinery/sleeper/process() + if(stat & (NOPOWER|BROKEN)) + return + if(occupant) + if(auto_eject_dead && occupant.stat == DEAD) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) + go_out() + return + occupant.Stasis(stasis_level) + + if(filtering > 0) + if(beaker) + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + var/pumped = 0 + for(var/datum/reagent/x in occupant.reagents.reagent_list) + occupant.reagents.trans_to_obj(beaker, 3) + pumped++ + if(ishuman(occupant)) + occupant.vessel.trans_to_obj(beaker, pumped + 1) + else + toggle_filter() + + if(pumping > 0) + if(beaker) + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + for(var/datum/reagent/x in occupant.ingested.reagent_list) + occupant.ingested.trans_to_obj(beaker, 3) + else + toggle_pump() + +/obj/machinery/sleeper/update_icon() + icon_state = "sleeper_[occupant ? "1" : "0"]" + +/obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) + add_fingerprint(user) + if(istype(I, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = I + if(G.affecting) + go_in(G.affecting, user) + return + if(istype(I, /obj/item/weapon/reagent_containers/glass)) + if(!beaker) + beaker = I + user.drop_item() + I.loc = src + user.visible_message("\The [user] adds \a [I] to \the [src].", "You add \a [I] to \the [src].") + else + to_chat(user, "\The [src] has a beaker already.") + return + if(!occupant) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + +/obj/machinery/sleeper/verb/move_eject() + set name = "Eject occupant" + set category = "Object" + set src in oview(1) + if(usr == occupant) + switch(usr.stat) + if(DEAD) + return + if(UNCONSCIOUS) + to_chat(usr, "You struggle through the haze to hit the eject button. This will take a couple of minutes...") + if(do_after(usr, 2 MINUTES, src)) + go_out() + if(CONSCIOUS) + go_out() + else + if(usr.stat != CONSCIOUS) + return + go_out() + add_fingerprint(usr) + +/obj/machinery/sleeper/MouseDrop_T(var/mob/target, var/mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) + return + go_in(target, user) + +/obj/machinery/sleeper/relaymove(var/mob/user) + ..() + if(user.incapacitated()) + return + go_out() + +/obj/machinery/sleeper/emp_act(var/severity) + if(filtering) + toggle_filter() + + if(pumping) + toggle_pump() + + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(occupant) + go_out() + + ..(severity) +/obj/machinery/sleeper/proc/toggle_filter() + if(!occupant || !beaker) + filtering = 0 + return + filtering = !filtering + +/obj/machinery/sleeper/proc/toggle_pump() + if(!occupant || !beaker) + pumping = 0 + return + pumping = !pumping + +/obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) + if(!M) + return + if(stat & (BROKEN|NOPOWER)) + return + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + if(!ishuman(M)) + to_chat(user, "\The [src] is not designed for that organism!") + return + if(M == user) + visible_message("\The [user] starts climbing into \the [src].") + else + visible_message("\The [user] starts putting [M] into \the [src].") + + if(do_after(user, 20)) + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + M.stop_pulling() + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.loc = src + update_use_power(USE_POWER_ACTIVE) + occupant = M + update_icon() + +/obj/machinery/sleeper/proc/go_out() + if(!occupant || occupant.loc != src) + occupant = null // JUST IN CASE + return + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.Stasis(0) + occupant.loc = src.loc + occupant = null + for(var/atom/movable/A in src) // In case an object was dropped inside or something + if(A == beaker || A == circuit) + continue + if(A in component_parts) + continue + A.loc = src.loc + update_use_power(USE_POWER_IDLE) + update_icon() + toggle_filter() + toggle_pump() + +/obj/machinery/sleeper/proc/remove_beaker() + if(beaker) + beaker.loc = src.loc + beaker = null + toggle_filter() + +/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) + if(stat & (BROKEN|NOPOWER)) + return + if(!(amount in amounts)) + return + + if(occupant && occupant.reagents) + if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) + use_power(amount * CHEM_SYNTH_ENERGY) + occupant.reagents.add_reagent(chemical, amount) + to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.") + else + to_chat(user, "The subject has too many chemicals in their bloodstream.") + else + to_chat(user, "There's no suitable occupant in \the [src].") + +//Survival/Stasis sleepers +/obj/machinery/sleeper/survival_pod + desc = "A limited functionality sleeper, all it can do is put patients into stasis. It lacks the medication and configuration of the larger units." + icon_state = "sleeper" + stasis_level = 100 //Just one setting + +/obj/machinery/sleeper/survival_pod/Initialize() + . = ..() + RefreshParts(1) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index f33549fc232..b73df5ca3ab 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -352,7 +352,7 @@ P.info += "Time of scan: [stationtime2text()]

    " P.info += "[generate_printing_text()]" P.info += "

    Notes:
    " - P.name = "Body Scan - [name] ([stationtime2text()]" + P.name = "Body Scan - [name] ([stationtime2text()])" else return FALSE diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm index 550e570d058..c8da98c9dfb 100644 --- a/code/game/machinery/air_alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -1,848 +1,848 @@ -#define DECLARE_TLV_VALUES var/red_min; var/yel_min; var/yel_max; var/red_max; var/tlv_comparitor; -#define LOAD_TLV_VALUES(x, y) red_min = x[1]; yel_min = x[2]; yel_max = x[3]; red_max = x[4]; tlv_comparitor = y; -#define TEST_TLV_VALUES (((tlv_comparitor >= red_max && red_max > 0) || tlv_comparitor <= red_min) ? 2 : ((tlv_comparitor >= yel_max && yel_max > 0) || tlv_comparitor <= yel_min) ? 1 : 0) - -#define AALARM_MODE_SCRUBBING 1 -#define AALARM_MODE_REPLACEMENT 2 //like scrubbing, but faster. -#define AALARM_MODE_PANIC 3 //constantly sucks all air -#define AALARM_MODE_CYCLE 4 //sucks off all air, then refill and switches to scrubbing -#define AALARM_MODE_FILL 5 //emergency fill -#define AALARM_MODE_OFF 6 //Shuts it all down. - -#define AALARM_SCREEN_MAIN 1 -#define AALARM_SCREEN_VENT 2 -#define AALARM_SCREEN_SCRUB 3 -#define AALARM_SCREEN_MODE 4 -#define AALARM_SCREEN_SENSORS 5 - -#define AALARM_REPORT_TIMEOUT 100 - -#define MAX_TEMPERATURE 90 -#define MIN_TEMPERATURE -40 - -//all air alarms in area are connected via magic -/area - var/obj/machinery/alarm/master_air_alarm - var/list/air_vent_names = list() - var/list/air_scrub_names = list() - var/list/air_vent_info = list() - var/list/air_scrub_info = list() - -/obj/machinery/alarm - name = "alarm" - desc = "Used to control various station atmospheric systems. The light indicates the current air status of the area." - icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - Other icons - icon_state = "alarm_0" - layer = ABOVE_WINDOW_LAYER - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - anchored = TRUE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 80 - active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. - power_channel = ENVIRON - req_one_access = list(access_atmospherics, access_engine_equip) - clicksound = "button" - clickvol = 30 - blocks_emissive = NONE - light_power = 0.25 - var/alarm_id = null - var/breach_detection = 1 // Whether to use automatic breach detection or not - var/frequency = 1439 - //var/skipprocess = 0 //Experimenting - var/alarm_frequency = 1437 - var/remote_control = 0 - var/rcon_setting = 2 - var/rcon_time = 0 - var/locked = 1 - panel_open = FALSE // If it's been screwdrivered open. - var/aidisabled = 0 - var/shorted = 0 - circuit = /obj/item/weapon/circuitboard/airalarm - - var/datum/wires/alarm/wires - - var/mode = AALARM_MODE_SCRUBBING - var/screen = AALARM_SCREEN_MAIN - var/area_uid - var/area/alarm_area - - var/target_temperature = T0C+20 - var/regulating_temperature = 0 - - var/datum/radio_frequency/radio_connection - - /// Keys are things like temperature and certain gasses. Values are lists, which contain, in order: - /// red warning minimum value, yellow warning minimum value, yellow warning maximum value, red warning maximum value - /// Use code\defines\gases.dm as reference for id/name. Please keep it consistent - var/list/TLV = list() - var/list/trace_gas = list("nitrous_oxide", "volatile_fuel") //list of other gases that this air alarm is able to detect - - var/danger_level = 0 - var/pressure_dangerlevel = 0 - - var/report_danger_level = 1 - - var/alarms_hidden = FALSE //If the alarms from this machine are visible on consoles - -/obj/machinery/alarm/nobreach - breach_detection = 0 - -/obj/machinery/alarm/monitor - report_danger_level = 0 - breach_detection = 0 - -/obj/machinery/alarm/alarms_hidden - alarms_hidden = TRUE - -/obj/machinery/alarm/angled - icon = 'icons/obj/wall_machines_angled.dmi' - -/obj/machinery/alarm/angled/hidden - alarms_hidden = TRUE - -/obj/machinery/alarm/angled/offset_airalarm() - pixel_x = (dir & 3) ? 0 : (dir == 4 ? -21 : 21) - pixel_y = (dir & 3) ? (dir == 1 ? -18 : 20) : 0 - -/obj/machinery/alarm/server/Initialize(mapload) - . = ..() - req_access = list(access_rd, access_atmospherics, access_engine_equip) - TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa - TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa - TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa - TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */ - TLV["temperature"] = list(20, 40, 140, 160) // K - target_temperature = 90 - -/obj/machinery/alarm/Initialize(mapload) - . = ..() - if(!pixel_x && !pixel_y) - offset_airalarm() - first_run() - -/obj/machinery/alarm/Destroy() - unregister_radio(src, frequency) - qdel(wires) - wires = null - if(alarm_area && alarm_area.master_air_alarm == src) - alarm_area.master_air_alarm = null - elect_master(exclude_self = TRUE) - return ..() - -/obj/machinery/alarm/proc/offset_airalarm() - pixel_x = (dir & 3) ? 0 : (dir == 4 ? -26 : 26) - pixel_y = (dir & 3) ? (dir == 1 ? -26 : 26) : 0 - -/obj/machinery/alarm/proc/first_run() - alarm_area = get_area(src) - area_uid = "\ref[alarm_area]" - if(name == "alarm") - name = "[alarm_area.name] Air Alarm" - - if(!wires) - wires = new(src) - - // breathable air according to human/Life() - TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa - TLV["nitrogen"] = list(0, 0, 135, 140) // Partial pressure, kpa - TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa - TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa - TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa - TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */ - TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K - - update_icon() - -/obj/machinery/alarm/proc/update_area() - alarm_area = get_area(src) - area_uid = "\ref[alarm_area]" - if(name == "alarm") - name = "[alarm_area.name] Air Alarm" - -/obj/machinery/alarm/Initialize() - . = ..() - set_frequency(frequency) - if(!master_is_operating()) - elect_master() - -/obj/machinery/alarm/process() - if((stat & (NOPOWER|BROKEN)) || shorted) - return - - var/turf/simulated/location = src.loc - if(!istype(location)) return//returns if loc is not simulated - - var/datum/gas_mixture/environment = location.return_air() - - //Handle temperature adjustment here. - handle_heating_cooling(environment) - - var/old_level = danger_level - var/old_pressurelevel = pressure_dangerlevel - danger_level = overall_danger_level(environment) - - if(old_level != danger_level) - apply_danger_level(danger_level) - - if(old_pressurelevel != pressure_dangerlevel) - if(breach_detected()) - mode = AALARM_MODE_OFF - apply_mode() - - if(mode == AALARM_MODE_CYCLE && environment.return_pressure() < ONE_ATMOSPHERE * 0.05) - mode = AALARM_MODE_FILL - apply_mode() - - //atmos computer remote controll stuff - switch(rcon_setting) - if(RCON_NO) - remote_control = 0 - if(RCON_AUTO) - if(danger_level == 2) - remote_control = 1 - else - remote_control = 0 - if(RCON_YES) - remote_control = 1 - - return - -/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment) - DECLARE_TLV_VALUES - LOAD_TLV_VALUES(TLV["temperature"], target_temperature) - if(!regulating_temperature) - //check for when we should start adjusting temperature - if(!TEST_TLV_VALUES && abs(environment.temperature - target_temperature) > 2.0 && environment.return_pressure() >= 1) - update_use_power(USE_POWER_ACTIVE) - regulating_temperature = 1 - audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.", runemessage = "* click *") - playsound(src, 'sound/machines/click.ogg', 50, 1) - else - //check for when we should stop adjusting temperature - if(TEST_TLV_VALUES || abs(environment.temperature - target_temperature) <= 0.5 || environment.return_pressure() < 1) - update_use_power(USE_POWER_IDLE) - regulating_temperature = 0 - audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.", runemessage = "* click *") - playsound(src, 'sound/machines/click.ogg', 50, 1) - - if(regulating_temperature) - if(target_temperature > T0C + MAX_TEMPERATURE) - target_temperature = T0C + MAX_TEMPERATURE - - if(target_temperature < T0C + MIN_TEMPERATURE) - target_temperature = T0C + MIN_TEMPERATURE - - var/datum/gas_mixture/gas - gas = environment.remove(0.25 * environment.total_moles) - if(gas) - - if(gas.temperature <= target_temperature) //gas heating - var/energy_used = min(gas.get_thermal_energy_change(target_temperature) , active_power_usage) - - gas.add_thermal_energy(energy_used) - //use_power(energy_used, ENVIRON) //handle by update_use_power instead - else //gas cooling - var/heat_transfer = min(abs(gas.get_thermal_energy_change(target_temperature)), active_power_usage) - - //Assume the heat is being pumped into the hull which is fixed at 20 C - //none of this is really proper thermodynamics but whatever - - var/cop = gas.temperature / T20C //coefficient of performance -> power used = heat_transfer/cop - - heat_transfer = min(heat_transfer, cop * active_power_usage) //this ensures that we don't use more than active_power_usage amount of power - - heat_transfer = -gas.add_thermal_energy(-heat_transfer) //get the actual heat transfer - - //use_power(heat_transfer / cop, ENVIRON) //handle by update_use_power instead - - environment.merge(gas) - -/obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) - var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature/environment.volume - var/environment_pressure = environment.return_pressure() - - var/other_moles = 0 - for(var/g in trace_gas) - other_moles += environment.gas[g] //this is only going to be used in a partial pressure calc, so we don't need to worry about group_multiplier here. - - DECLARE_TLV_VALUES - LOAD_TLV_VALUES(TLV["pressure"], environment_pressure) - pressure_dangerlevel = TEST_TLV_VALUES // not local because it's used in process() - LOAD_TLV_VALUES(TLV["oxygen"], environment.gas["oxygen"]*partial_pressure) - var/oxygen_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["carbon_dioxide"], environment.gas["carbon_dioxide"]*partial_pressure) - var/co2_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["phoron"], environment.gas["phoron"]*partial_pressure) - var/phoron_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["temperature"], environment.temperature) - var/temperature_dangerlevel = TEST_TLV_VALUES - LOAD_TLV_VALUES(TLV["other"], other_moles*partial_pressure) - var/other_dangerlevel = TEST_TLV_VALUES - - return max( - pressure_dangerlevel, - oxygen_dangerlevel, - co2_dangerlevel, - phoron_dangerlevel, - other_dangerlevel, - temperature_dangerlevel - ) - -// Returns whether this air alarm thinks there is a breach, given the sensors that are available to it. -/obj/machinery/alarm/proc/breach_detected() - var/turf/simulated/location = src.loc - - if(!istype(location)) - return 0 - - if(breach_detection == 0) - return 0 - - var/datum/gas_mixture/environment = location.return_air() - var/environment_pressure = environment.return_pressure() - var/pressure_levels = TLV["pressure"] - - if(environment_pressure <= pressure_levels[1]) //low pressures - if(!(mode == AALARM_MODE_PANIC || mode == AALARM_MODE_CYCLE)) - return 1 - - return 0 - -/obj/machinery/alarm/proc/master_is_operating() - return alarm_area && alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER | BROKEN)) - -/obj/machinery/alarm/proc/elect_master(exclude_self = FALSE) - for(var/obj/machinery/alarm/AA in alarm_area) - if(exclude_self && AA == src) - continue - if(!(AA.stat & (NOPOWER|BROKEN))) - alarm_area.master_air_alarm = AA - return 1 - return 0 - -/obj/machinery/alarm/update_icon() - cut_overlays() - - if(panel_open) - icon_state = "alarmx" - set_light(0) - set_light_on(FALSE) - return - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - set_light(0) - set_light_on(FALSE) - return - - var/icon_level = danger_level - if(alarm_area?.atmosalm) - icon_level = max(icon_level, 1) //if there's an atmos alarm but everything is okay locally, no need to go past yellow - - var/new_color = null - switch(icon_level) - if(0) - icon_state = "alarm_0" - add_overlay(mutable_appearance(icon, "alarm_ov0")) - add_overlay(emissive_appearance(icon, "alarm_ov0")) - new_color = "#03A728" - if(1) - icon_state = "alarm_2" //yes, alarm2 is yellow alarm - add_overlay(mutable_appearance(icon, "alarm_ov2")) - add_overlay(emissive_appearance(icon, "alarm_ov2")) - new_color = "#EC8B2F" - if(2) - icon_state = "alarm_1" - add_overlay(mutable_appearance(icon, "alarm_ov1")) - add_overlay(emissive_appearance(icon, "alarm_ov1")) - new_color = "#DA0205" - - set_light(l_range = 2, l_power = 0.25, l_color = new_color) - set_light_on(TRUE) - -/obj/machinery/alarm/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - if(alarm_area.master_air_alarm != src) - if(master_is_operating()) - return - elect_master() - if(alarm_area.master_air_alarm != src) - return - if(!signal || signal.encryption) - return - var/id_tag = signal.data["tag"] - if(!id_tag) - return - if(signal.data["area"] != area_uid) - return - if(signal.data["sigtype"] != "status") - return - - var/dev_type = signal.data["device"] - if(!(id_tag in alarm_area.air_scrub_names) && !(id_tag in alarm_area.air_vent_names)) - register_env_machine(id_tag, dev_type) - if(dev_type == "AScr") - alarm_area.air_scrub_info[id_tag] = signal.data - else if(dev_type == "AVP") - alarm_area.air_vent_info[id_tag] = signal.data - -/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) - var/new_name - if(device_type == "AVP") - new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" - alarm_area.air_vent_names[m_id] = new_name - else if(device_type == "AScr") - new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" - alarm_area.air_scrub_names[m_id] = new_name - else - return - spawn(10) - send_signal(m_id, list("init" = new_name)) - -/obj/machinery/alarm/proc/refresh_all() - for(var/id_tag in alarm_area.air_vent_names) - var/list/I = alarm_area.air_vent_info[id_tag] - if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) - continue - send_signal(id_tag, list("status")) - for(var/id_tag in alarm_area.air_scrub_names) - var/list/I = alarm_area.air_scrub_info[id_tag] - if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) - continue - send_signal(id_tag, list("status")) - -/obj/machinery/alarm/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = command - signal.data["tag"] = target - signal.data["sigtype"] = "command" - - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// to_world("Signal [command] Broadcasted to [target]") - - return 1 - -/obj/machinery/alarm/proc/apply_mode() - //propagate mode to other air alarms in the area - //TODO: make it so that players can choose between applying the new mode to the room they are in (related area) vs the entire alarm area - for(var/obj/machinery/alarm/AA in alarm_area) - AA.mode = mode - - switch(mode) - if(AALARM_MODE_SCRUBBING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 1, "co2_scrub"= 1, "scrubbing"= 1, "panic_siphon"= 0)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) - - if(AALARM_MODE_PANIC, AALARM_MODE_CYCLE) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 0)) - - if(AALARM_MODE_REPLACEMENT) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) - - if(AALARM_MODE_FILL) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 0)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) - - if(AALARM_MODE_OFF) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list("power"= 0)) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list("power"= 0)) - -/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) - if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) - post_alert(new_danger_level) - - update_icon() - -/obj/machinery/alarm/proc/post_alert(alert_level) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(alarm_frequency) - if(!frequency) - return - - var/datum/signal/alert_signal = new - alert_signal.source = src - alert_signal.transmission_method = TRANSMISSION_RADIO - alert_signal.data["zone"] = alarm_area.name - alert_signal.data["type"] = "Atmospheric" - - if(alert_level==2) - alert_signal.data["alert"] = "severe" - else if(alert_level==1) - alert_signal.data["alert"] = "minor" - else if(alert_level==0) - alert_signal.data["alert"] = "clear" - - frequency.post_signal(src, alert_signal) - -/obj/machinery/alarm/attack_ai(mob/user) - tgui_interact(user) - -/obj/machinery/alarm/attack_hand(mob/user) - . = ..() - if(.) - return - return interact(user) - -/obj/machinery/alarm/interact(mob/user) - tgui_interact(user) - wires.Interact(user) - -/obj/machinery/alarm/tgui_status(mob/user) - if(isAI(user) && aidisabled) - to_chat(user, "AI control has been disabled.") - else if(!shorted) - return ..() - return STATUS_CLOSE - -/obj/machinery/alarm/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/state) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AirAlarm", name, parent_ui) - if(state) - ui.set_state(state) - ui.open() - -/obj/machinery/alarm/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = list( - "locked" = locked, - "siliconUser" = issilicon(user), - "remoteUser" = !!ui.parent_ui, - "danger_level" = danger_level, - "target_temperature" = "[target_temperature - T0C]C", - "rcon" = rcon_setting, - ) - - var/area/A = get_area(src) - data["atmos_alarm"] = A?.atmosalm - data["fire_alarm"] = A?.fire - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - - var/list/list/environment_data = list() - data["environment_data"] = environment_data - - DECLARE_TLV_VALUES - - var/pressure = environment.return_pressure() - LOAD_TLV_VALUES(TLV["pressure"], pressure) - environment_data.Add(list(list( - "name" = "Pressure", - "value" = pressure, - "unit" = "kPa", - "danger_level" = TEST_TLV_VALUES - ))) - - var/temperature = environment.temperature - LOAD_TLV_VALUES(TLV["temperature"], temperature) - environment_data.Add(list(list( - "name" = "Temperature", - "value" = temperature, - "unit" = "K ([round(temperature - T0C, 0.1)]C)", - "danger_level" = TEST_TLV_VALUES - ))) - - var/total_moles = environment.total_moles - var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume - for(var/gas_id in environment.gas) - if(!(gas_id in TLV)) - continue - LOAD_TLV_VALUES(TLV[gas_id], environment.gas[gas_id] * partial_pressure) - environment_data.Add(list(list( - "name" = gas_id, - "value" = environment.gas[gas_id] / total_moles * 100, - "unit" = "%", - "danger_level" = TEST_TLV_VALUES - ))) - - if(!locked || issilicon(user) || data["remoteUser"]) - var/list/list/vents = list() - data["vents"] = vents - for(var/id_tag in A.air_vent_names) - var/long_name = A.air_vent_names[id_tag] - var/list/info = A.air_vent_info[id_tag] - if(!info) - continue - vents.Add(list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "checks" = info["checks"], - "excheck" = info["checks"]&1, - "incheck" = info["checks"]&2, - "direction" = info["direction"], - "external" = info["external"], - "internal" = info["internal"], - "extdefault"= (info["external"] == ONE_ATMOSPHERE), - "intdefault"= (info["internal"] == 0), - ))) - - - var/list/list/scrubbers = list() - data["scrubbers"] = scrubbers - for(var/id_tag in alarm_area.air_scrub_names) - var/long_name = alarm_area.air_scrub_names[id_tag] - var/list/info = alarm_area.air_scrub_info[id_tag] - if(!info) - continue - scrubbers += list(list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "scrubbing" = info["scrubbing"], - "panic" = info["panic"], - "filters" = list( - list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"]), - list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"]), - list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"]), - list("name" = "Phoron" , "command" = "tox_scrub","val" = info["filter_phoron"]), - list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"]), - list("name" = "Volatile Fuel", "command" = "fuel_scrub","val" = info["filter_fuel"]) - ) - )) - data["scrubbers"] = scrubbers - - data["mode"] = mode - - var/list/list/modes = list() - data["modes"] = modes - modes[++modes.len] = list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0) - modes[++modes.len] = list("name" = "Replace Air - Siphons out air while replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 0) - modes[++modes.len] = list("name" = "Panic - Siphons air out of the room", "mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1) - modes[++modes.len] = list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_CYCLE, "selected" = mode == AALARM_MODE_CYCLE, "danger" = 1) - modes[++modes.len] = list("name" = "Fill - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FILL, "selected" = mode == AALARM_MODE_FILL, "danger" = 0) - modes[++modes.len] = list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0) - - var/list/selected - var/list/thresholds = list() - - var/list/gas_names = list("oxygen", "carbon_dioxide", "phoron", "other") //Gas ids made to match code\defines\gases.dm - for(var/g in gas_names) - thresholds[++thresholds.len] = list("name" = g, "settings" = list()) - selected = TLV[g] - for(var/i = 1, i <= 4, i++) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = i, "selected" = selected[i])) - - selected = TLV["pressure"] - thresholds[++thresholds.len] = list("name" = "Pressure", "settings" = list()) - for(var/i = 1, i <= 4, i++) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = i, "selected" = selected[i])) - - selected = TLV["temperature"] - thresholds[++thresholds.len] = list("name" = "Temperature", "settings" = list()) - for(var/i = 1, i <= 4, i++) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = i, "selected" = selected[i])) - - data["thresholds"] = thresholds - return data - -/obj/machinery/alarm/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - if(action == "rcon") - var/attempted_rcon_setting = text2num(params["rcon"]) - - switch(attempted_rcon_setting) - if(RCON_NO) - rcon_setting = RCON_NO - if(RCON_AUTO) - rcon_setting = RCON_AUTO - if(RCON_YES) - rcon_setting = RCON_YES - return TRUE - - if(action == "temperature") - var/list/selected = TLV["temperature"] - var/max_temperature = min(selected[3] - T0C, MAX_TEMPERATURE) - var/min_temperature = max(selected[2] - T0C, MIN_TEMPERATURE) - var/input_temperature = tgui_input_number(usr, "What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C, max_temperature, min_temperature) - if(isnum(input_temperature)) - if(input_temperature > max_temperature || input_temperature < min_temperature) - to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C") - else - target_temperature = input_temperature + T0C - return TRUE - - // Account for remote users here. - // Yes, this is kinda snowflaky; however, I would argue it would be far more snowflakey - // to include "custom hrefs" and all the other bullshit that nano states have just for the - // like, two UIs, that want remote access to other UIs. - if((locked && !issilicon(usr) && !istype(state, /datum/tgui_state/air_alarm_remote)) || (issilicon(usr) && aidisabled)) - return - - var/device_id = params["id_tag"] - switch(action) - if("lock") - if(issilicon(usr) && !wires.is_cut(WIRE_IDSCAN)) - locked = !locked - . = TRUE - if( "power", - "o2_scrub", - "n2_scrub", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "fuel_scrub", - "panic_siphon", - "scrubbing", - "direction") - send_signal(device_id, list("[action]" = text2num(params["val"])), usr) - . = TRUE - if("excheck") - send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) - . = TRUE - if("incheck") - send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) - . = TRUE - if("set_external_pressure", "set_internal_pressure") - var/target = params["value"] - if(!isnull(target)) - send_signal(device_id, list("[action]" = target), usr) - . = TRUE - if("reset_external_pressure") - send_signal(device_id, list("reset_external_pressure"), usr) - . = TRUE - if("reset_internal_pressure") - send_signal(device_id, list("reset_internal_pressure"), usr) - . = TRUE - if("threshold") - var/env = params["env"] - - var/name = params["var"] - var/value = tgui_input_number(usr, "New [name] for [env]:", name, TLV[env][name]) - if(!isnull(value) && !..()) - if(value < 0) - TLV[env][name] = -1 - else - TLV[env][name] = round(value, 0.01) - clamp_tlv_values(env, name) - // investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) - . = TRUE - if("mode") - mode = text2num(params["mode"]) - // investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) - apply_mode(usr) - . = TRUE - if("alarm") - if(alarm_area.atmosalert(2, src)) - apply_danger_level(2) - . = TRUE - if("reset") - atmos_reset() - . = TRUE - update_icon() - -// This big ol' mess just ensures that TLV always makes sense. If you set the max value below the min value, -// it'll automatically update all the other values to keep it sane. -/obj/machinery/alarm/proc/clamp_tlv_values(env, changed_threshold) - var/list/selected = TLV[env] - switch(changed_threshold) - if(1) - if(selected[1] > selected[2]) - selected[2] = selected[1] - if(selected[1] > selected[3]) - selected[3] = selected[1] - if(selected[1] > selected[4]) - selected[4] = selected[1] - if(2) - if(selected[1] > selected[2]) - selected[1] = selected[2] - if(selected[2] > selected[3]) - selected[3] = selected[2] - if(selected[2] > selected[4]) - selected[4] = selected[2] - if(3) - if(selected[1] > selected[3]) - selected[1] = selected[3] - if(selected[2] > selected[3]) - selected[2] = selected[3] - if(selected[3] > selected[4]) - selected[4] = selected[3] - if(4) - if(selected[1] > selected[4]) - selected[1] = selected[4] - if(selected[2] > selected[4]) - selected[2] = selected[4] - if(selected[3] > selected[4]) - selected[3] = selected[4] - - - - -/obj/machinery/alarm/proc/atmos_reset() - if(alarm_area.atmosalert(0, src)) - apply_danger_level(0) - update_icon() - -/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) - add_fingerprint(user) - if(alarm_deconstruction_screwdriver(user, W)) - return - if(alarm_deconstruction_wirecutters(user, W)) - return - - if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card - togglelock() - return ..() - -/obj/machinery/alarm/verb/togglelock(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "It does nothing.") - return - else - if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] the Air Alarm interface.") - else - to_chat(user, "Access denied.") - return - -/obj/machinery/alarm/AltClick() - ..() - togglelock() - -/obj/machinery/alarm/power_change() - ..() - spawn(rand(0,15)) - update_icon() - -// VOREStation Edit Start -/obj/machinery/alarm/freezer - target_temperature = T0C - 13.15 // Chilly freezer room - -/obj/machinery/alarm/freezer/first_run() - . = ..() - - TLV["temperature"] = list(T0C - 40, T0C - 20, T0C + 40, T0C + 66) // K, Lower Temperature for Freezer Air Alarms (This is because TLV is hardcoded to be generated on first_run, and therefore the only way to modify this without changing TLV generation) - -// VOREStation Edit End -#undef LOAD_TLV_VALUES -#undef TEST_TLV_VALUES -#undef DECLARE_TLV_VALUES +#define DECLARE_TLV_VALUES var/red_min; var/yel_min; var/yel_max; var/red_max; var/tlv_comparitor; +#define LOAD_TLV_VALUES(x, y) red_min = x[1]; yel_min = x[2]; yel_max = x[3]; red_max = x[4]; tlv_comparitor = y; +#define TEST_TLV_VALUES (((tlv_comparitor >= red_max && red_max > 0) || tlv_comparitor <= red_min) ? 2 : ((tlv_comparitor >= yel_max && yel_max > 0) || tlv_comparitor <= yel_min) ? 1 : 0) + +#define AALARM_MODE_SCRUBBING 1 +#define AALARM_MODE_REPLACEMENT 2 //like scrubbing, but faster. +#define AALARM_MODE_PANIC 3 //constantly sucks all air +#define AALARM_MODE_CYCLE 4 //sucks off all air, then refill and switches to scrubbing +#define AALARM_MODE_FILL 5 //emergency fill +#define AALARM_MODE_OFF 6 //Shuts it all down. + +#define AALARM_SCREEN_MAIN 1 +#define AALARM_SCREEN_VENT 2 +#define AALARM_SCREEN_SCRUB 3 +#define AALARM_SCREEN_MODE 4 +#define AALARM_SCREEN_SENSORS 5 + +#define AALARM_REPORT_TIMEOUT 100 + +#define MAX_TEMPERATURE 90 +#define MIN_TEMPERATURE -40 + +//all air alarms in area are connected via magic +/area + var/obj/machinery/alarm/master_air_alarm + var/list/air_vent_names = list() + var/list/air_scrub_names = list() + var/list/air_vent_info = list() + var/list/air_scrub_info = list() + +/obj/machinery/alarm + name = "alarm" + desc = "Used to control various station atmospheric systems. The light indicates the current air status of the area." + icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - Other icons + icon_state = "alarm_0" + layer = ABOVE_WINDOW_LAYER + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + anchored = TRUE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 80 + active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. + power_channel = ENVIRON + req_one_access = list(access_atmospherics, access_engine_equip) + clicksound = "button" + clickvol = 30 + blocks_emissive = NONE + light_power = 0.25 + var/alarm_id = null + var/breach_detection = 1 // Whether to use automatic breach detection or not + var/frequency = 1439 + //var/skipprocess = 0 //Experimenting + var/alarm_frequency = 1437 + var/remote_control = 0 + var/rcon_setting = 2 + var/rcon_time = 0 + var/locked = 1 + panel_open = FALSE // If it's been screwdrivered open. + var/aidisabled = 0 + var/shorted = 0 + circuit = /obj/item/weapon/circuitboard/airalarm + + var/datum/wires/alarm/wires + + var/mode = AALARM_MODE_SCRUBBING + var/screen = AALARM_SCREEN_MAIN + var/area_uid + var/area/alarm_area + + var/target_temperature = T0C+20 + var/regulating_temperature = 0 + + var/datum/radio_frequency/radio_connection + + /// Keys are things like temperature and certain gasses. Values are lists, which contain, in order: + /// red warning minimum value, yellow warning minimum value, yellow warning maximum value, red warning maximum value + /// Use code\defines\gases.dm as reference for id/name. Please keep it consistent + var/list/TLV = list() + var/list/trace_gas = list("nitrous_oxide", "volatile_fuel") //list of other gases that this air alarm is able to detect + + var/danger_level = 0 + var/pressure_dangerlevel = 0 + + var/report_danger_level = 1 + + var/alarms_hidden = FALSE //If the alarms from this machine are visible on consoles + +/obj/machinery/alarm/nobreach + breach_detection = 0 + +/obj/machinery/alarm/monitor + report_danger_level = 0 + breach_detection = 0 + +/obj/machinery/alarm/alarms_hidden + alarms_hidden = TRUE + +/obj/machinery/alarm/angled + icon = 'icons/obj/wall_machines_angled.dmi' + +/obj/machinery/alarm/angled/hidden + alarms_hidden = TRUE + +/obj/machinery/alarm/angled/offset_airalarm() + pixel_x = (dir & 3) ? 0 : (dir == 4 ? -21 : 21) + pixel_y = (dir & 3) ? (dir == 1 ? -18 : 20) : 0 + +/obj/machinery/alarm/server/Initialize(mapload) + . = ..() + req_access = list(access_rd, access_atmospherics, access_engine_equip) + TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa + TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa + TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa + TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa + TLV["pressure"] = list(0,ONE_ATMOSPHERE*0.10,ONE_ATMOSPHERE*1.40,ONE_ATMOSPHERE*1.60) /* kpa */ + TLV["temperature"] = list(20, 40, 140, 160) // K + target_temperature = 90 + +/obj/machinery/alarm/Initialize(mapload) + . = ..() + if(!pixel_x && !pixel_y) + offset_airalarm() + first_run() + +/obj/machinery/alarm/Destroy() + unregister_radio(src, frequency) + qdel(wires) + wires = null + if(alarm_area && alarm_area.master_air_alarm == src) + alarm_area.master_air_alarm = null + elect_master(exclude_self = TRUE) + return ..() + +/obj/machinery/alarm/proc/offset_airalarm() + pixel_x = (dir & 3) ? 0 : (dir == 4 ? -26 : 26) + pixel_y = (dir & 3) ? (dir == 1 ? -26 : 26) : 0 + +/obj/machinery/alarm/proc/first_run() + alarm_area = get_area(src) + area_uid = "\ref[alarm_area]" + if(name == "alarm") + name = "[alarm_area.name] Air Alarm" + + if(!wires) + wires = new(src) + + // breathable air according to human/Life() + TLV["oxygen"] = list(16, 19, 135, 140) // Partial pressure, kpa + TLV["nitrogen"] = list(0, 0, 135, 140) // Partial pressure, kpa + TLV["carbon_dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa + TLV["phoron"] = list(-1.0, -1.0, 0, 0.5) // Partial pressure, kpa + TLV["other"] = list(-1.0, -1.0, 0.5, 1.0) // Partial pressure, kpa + TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */ + TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K + + update_icon() + +/obj/machinery/alarm/proc/update_area() + alarm_area = get_area(src) + area_uid = "\ref[alarm_area]" + if(name == "alarm") + name = "[alarm_area.name] Air Alarm" + +/obj/machinery/alarm/Initialize() + . = ..() + set_frequency(frequency) + if(!master_is_operating()) + elect_master() + +/obj/machinery/alarm/process() + if((stat & (NOPOWER|BROKEN)) || shorted) + return + + var/turf/simulated/location = src.loc + if(!istype(location)) return//returns if loc is not simulated + + var/datum/gas_mixture/environment = location.return_air() + + //Handle temperature adjustment here. + handle_heating_cooling(environment) + + var/old_level = danger_level + var/old_pressurelevel = pressure_dangerlevel + danger_level = overall_danger_level(environment) + + if(old_level != danger_level) + apply_danger_level(danger_level) + + if(old_pressurelevel != pressure_dangerlevel) + if(breach_detected()) + mode = AALARM_MODE_OFF + apply_mode() + + if(mode == AALARM_MODE_CYCLE && environment.return_pressure() < ONE_ATMOSPHERE * 0.05) + mode = AALARM_MODE_FILL + apply_mode() + + //atmos computer remote controll stuff + switch(rcon_setting) + if(RCON_NO) + remote_control = 0 + if(RCON_AUTO) + if(danger_level == 2) + remote_control = 1 + else + remote_control = 0 + if(RCON_YES) + remote_control = 1 + + return + +/obj/machinery/alarm/proc/handle_heating_cooling(var/datum/gas_mixture/environment) + DECLARE_TLV_VALUES + LOAD_TLV_VALUES(TLV["temperature"], target_temperature) + if(!regulating_temperature) + //check for when we should start adjusting temperature + if(!TEST_TLV_VALUES && abs(environment.temperature - target_temperature) > 2.0 && environment.return_pressure() >= 1) + update_use_power(USE_POWER_ACTIVE) + regulating_temperature = 1 + audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.", runemessage = "* click *") + playsound(src, 'sound/machines/click.ogg', 50, 1) + else + //check for when we should stop adjusting temperature + if(TEST_TLV_VALUES || abs(environment.temperature - target_temperature) <= 0.5 || environment.return_pressure() < 1) + update_use_power(USE_POWER_IDLE) + regulating_temperature = 0 + audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click as a faint electronic humming stops.", runemessage = "* click *") + playsound(src, 'sound/machines/click.ogg', 50, 1) + + if(regulating_temperature) + if(target_temperature > T0C + MAX_TEMPERATURE) + target_temperature = T0C + MAX_TEMPERATURE + + if(target_temperature < T0C + MIN_TEMPERATURE) + target_temperature = T0C + MIN_TEMPERATURE + + var/datum/gas_mixture/gas + gas = environment.remove(0.25 * environment.total_moles) + if(gas) + + if(gas.temperature <= target_temperature) //gas heating + var/energy_used = min(gas.get_thermal_energy_change(target_temperature) , active_power_usage) + + gas.add_thermal_energy(energy_used) + //use_power(energy_used, ENVIRON) //handle by update_use_power instead + else //gas cooling + var/heat_transfer = min(abs(gas.get_thermal_energy_change(target_temperature)), active_power_usage) + + //Assume the heat is being pumped into the hull which is fixed at 20 C + //none of this is really proper thermodynamics but whatever + + var/cop = gas.temperature / T20C //coefficient of performance -> power used = heat_transfer/cop + + heat_transfer = min(heat_transfer, cop * active_power_usage) //this ensures that we don't use more than active_power_usage amount of power + + heat_transfer = -gas.add_thermal_energy(-heat_transfer) //get the actual heat transfer + + //use_power(heat_transfer / cop, ENVIRON) //handle by update_use_power instead + + environment.merge(gas) + +/obj/machinery/alarm/proc/overall_danger_level(var/datum/gas_mixture/environment) + var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature/environment.volume + var/environment_pressure = environment.return_pressure() + + var/other_moles = 0 + for(var/g in trace_gas) + other_moles += environment.gas[g] //this is only going to be used in a partial pressure calc, so we don't need to worry about group_multiplier here. + + DECLARE_TLV_VALUES + LOAD_TLV_VALUES(TLV["pressure"], environment_pressure) + pressure_dangerlevel = TEST_TLV_VALUES // not local because it's used in process() + LOAD_TLV_VALUES(TLV["oxygen"], environment.gas["oxygen"]*partial_pressure) + var/oxygen_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["carbon_dioxide"], environment.gas["carbon_dioxide"]*partial_pressure) + var/co2_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["phoron"], environment.gas["phoron"]*partial_pressure) + var/phoron_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["temperature"], environment.temperature) + var/temperature_dangerlevel = TEST_TLV_VALUES + LOAD_TLV_VALUES(TLV["other"], other_moles*partial_pressure) + var/other_dangerlevel = TEST_TLV_VALUES + + return max( + pressure_dangerlevel, + oxygen_dangerlevel, + co2_dangerlevel, + phoron_dangerlevel, + other_dangerlevel, + temperature_dangerlevel + ) + +// Returns whether this air alarm thinks there is a breach, given the sensors that are available to it. +/obj/machinery/alarm/proc/breach_detected() + var/turf/simulated/location = src.loc + + if(!istype(location)) + return 0 + + if(breach_detection == 0) + return 0 + + var/datum/gas_mixture/environment = location.return_air() + var/environment_pressure = environment.return_pressure() + var/pressure_levels = TLV["pressure"] + + if(environment_pressure <= pressure_levels[1]) //low pressures + if(!(mode == AALARM_MODE_PANIC || mode == AALARM_MODE_CYCLE)) + return 1 + + return 0 + +/obj/machinery/alarm/proc/master_is_operating() + return alarm_area && alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER | BROKEN)) + +/obj/machinery/alarm/proc/elect_master(exclude_self = FALSE) + for(var/obj/machinery/alarm/AA in alarm_area) + if(exclude_self && AA == src) + continue + if(!(AA.stat & (NOPOWER|BROKEN))) + alarm_area.master_air_alarm = AA + return 1 + return 0 + +/obj/machinery/alarm/update_icon() + cut_overlays() + + if(panel_open) + icon_state = "alarmx" + set_light(0) + set_light_on(FALSE) + return + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + set_light(0) + set_light_on(FALSE) + return + + var/icon_level = danger_level + if(alarm_area?.atmosalm) + icon_level = max(icon_level, 1) //if there's an atmos alarm but everything is okay locally, no need to go past yellow + + var/new_color = null + switch(icon_level) + if(0) + icon_state = "alarm_0" + add_overlay(mutable_appearance(icon, "alarm_ov0")) + add_overlay(emissive_appearance(icon, "alarm_ov0")) + new_color = "#03A728" + if(1) + icon_state = "alarm_2" //yes, alarm2 is yellow alarm + add_overlay(mutable_appearance(icon, "alarm_ov2")) + add_overlay(emissive_appearance(icon, "alarm_ov2")) + new_color = "#EC8B2F" + if(2) + icon_state = "alarm_1" + add_overlay(mutable_appearance(icon, "alarm_ov1")) + add_overlay(emissive_appearance(icon, "alarm_ov1")) + new_color = "#DA0205" + + set_light(l_range = 2, l_power = 0.25, l_color = new_color) + set_light_on(TRUE) + +/obj/machinery/alarm/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + if(alarm_area.master_air_alarm != src) + if(master_is_operating()) + return + elect_master() + if(alarm_area.master_air_alarm != src) + return + if(!signal || signal.encryption) + return + var/id_tag = signal.data["tag"] + if(!id_tag) + return + if(signal.data["area"] != area_uid) + return + if(signal.data["sigtype"] != "status") + return + + var/dev_type = signal.data["device"] + if(!(id_tag in alarm_area.air_scrub_names) && !(id_tag in alarm_area.air_vent_names)) + register_env_machine(id_tag, dev_type) + if(dev_type == "AScr") + alarm_area.air_scrub_info[id_tag] = signal.data + else if(dev_type == "AVP") + alarm_area.air_vent_info[id_tag] = signal.data + +/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) + var/new_name + if(device_type == "AVP") + new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" + alarm_area.air_vent_names[m_id] = new_name + else if(device_type == "AScr") + new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" + alarm_area.air_scrub_names[m_id] = new_name + else + return + spawn(10) + send_signal(m_id, list("init" = new_name)) + +/obj/machinery/alarm/proc/refresh_all() + for(var/id_tag in alarm_area.air_vent_names) + var/list/I = alarm_area.air_vent_info[id_tag] + if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) + continue + send_signal(id_tag, list("status")) + for(var/id_tag in alarm_area.air_scrub_names) + var/list/I = alarm_area.air_scrub_info[id_tag] + if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time) + continue + send_signal(id_tag, list("status")) + +/obj/machinery/alarm/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = command + signal.data["tag"] = target + signal.data["sigtype"] = "command" + + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) +// to_world("Signal [command] Broadcasted to [target]") + + return 1 + +/obj/machinery/alarm/proc/apply_mode() + //propagate mode to other air alarms in the area + //TODO: make it so that players can choose between applying the new mode to the room they are in (related area) vs the entire alarm area + for(var/obj/machinery/alarm/AA in alarm_area) + AA.mode = mode + + switch(mode) + if(AALARM_MODE_SCRUBBING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 1, "co2_scrub"= 1, "scrubbing"= 1, "panic_siphon"= 0)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) + + if(AALARM_MODE_PANIC, AALARM_MODE_CYCLE) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 0)) + + if(AALARM_MODE_REPLACEMENT) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 1, "panic_siphon"= 1)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) + + if(AALARM_MODE_FILL) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 0)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 1, "checks"= "default", "set_external_pressure"= "default")) + + if(AALARM_MODE_OFF) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list("power"= 0)) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list("power"= 0)) + +/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) + if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) + post_alert(new_danger_level) + + update_icon() + +/obj/machinery/alarm/proc/post_alert(alert_level) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(alarm_frequency) + if(!frequency) + return + + var/datum/signal/alert_signal = new + alert_signal.source = src + alert_signal.transmission_method = TRANSMISSION_RADIO + alert_signal.data["zone"] = alarm_area.name + alert_signal.data["type"] = "Atmospheric" + + if(alert_level==2) + alert_signal.data["alert"] = "severe" + else if(alert_level==1) + alert_signal.data["alert"] = "minor" + else if(alert_level==0) + alert_signal.data["alert"] = "clear" + + frequency.post_signal(src, alert_signal) + +/obj/machinery/alarm/attack_ai(mob/user) + tgui_interact(user) + +/obj/machinery/alarm/attack_hand(mob/user) + . = ..() + if(.) + return + return interact(user) + +/obj/machinery/alarm/interact(mob/user) + tgui_interact(user) + wires.Interact(user) + +/obj/machinery/alarm/tgui_status(mob/user) + if(isAI(user) && aidisabled) + to_chat(user, "AI control has been disabled.") + else if(!shorted) + return ..() + return STATUS_CLOSE + +/obj/machinery/alarm/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AirAlarm", name, parent_ui) + if(state) + ui.set_state(state) + ui.open() + +/obj/machinery/alarm/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = list( + "locked" = locked, + "siliconUser" = issilicon(user), + "remoteUser" = !!ui.parent_ui, + "danger_level" = danger_level, + "target_temperature" = "[target_temperature - T0C]C", + "rcon" = rcon_setting, + ) + + var/area/A = get_area(src) + data["atmos_alarm"] = A?.atmosalm + data["fire_alarm"] = A?.fire + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + + var/list/list/environment_data = list() + data["environment_data"] = environment_data + + DECLARE_TLV_VALUES + + var/pressure = environment.return_pressure() + LOAD_TLV_VALUES(TLV["pressure"], pressure) + environment_data.Add(list(list( + "name" = "Pressure", + "value" = pressure, + "unit" = "kPa", + "danger_level" = TEST_TLV_VALUES + ))) + + var/temperature = environment.temperature + LOAD_TLV_VALUES(TLV["temperature"], temperature) + environment_data.Add(list(list( + "name" = "Temperature", + "value" = temperature, + "unit" = "K ([round(temperature - T0C, 0.1)]C)", + "danger_level" = TEST_TLV_VALUES + ))) + + var/total_moles = environment.total_moles + var/partial_pressure = R_IDEAL_GAS_EQUATION * environment.temperature / environment.volume + for(var/gas_id in environment.gas) + if(!(gas_id in TLV)) + continue + LOAD_TLV_VALUES(TLV[gas_id], environment.gas[gas_id] * partial_pressure) + environment_data.Add(list(list( + "name" = gas_id, + "value" = environment.gas[gas_id] / total_moles * 100, + "unit" = "%", + "danger_level" = TEST_TLV_VALUES + ))) + + if(!locked || issilicon(user) || data["remoteUser"]) + var/list/list/vents = list() + data["vents"] = vents + for(var/id_tag in A.air_vent_names) + var/long_name = A.air_vent_names[id_tag] + var/list/info = A.air_vent_info[id_tag] + if(!info) + continue + vents.Add(list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "checks" = info["checks"], + "excheck" = info["checks"]&1, + "incheck" = info["checks"]&2, + "direction" = info["direction"], + "external" = info["external"], + "internal" = info["internal"], + "extdefault"= (info["external"] == ONE_ATMOSPHERE), + "intdefault"= (info["internal"] == 0), + ))) + + + var/list/list/scrubbers = list() + data["scrubbers"] = scrubbers + for(var/id_tag in alarm_area.air_scrub_names) + var/long_name = alarm_area.air_scrub_names[id_tag] + var/list/info = alarm_area.air_scrub_info[id_tag] + if(!info) + continue + scrubbers += list(list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "scrubbing" = info["scrubbing"], + "panic" = info["panic"], + "filters" = list( + list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"]), + list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"]), + list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"]), + list("name" = "Phoron" , "command" = "tox_scrub","val" = info["filter_phoron"]), + list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"]), + list("name" = "Volatile Fuel", "command" = "fuel_scrub","val" = info["filter_fuel"]) + ) + )) + data["scrubbers"] = scrubbers + + data["mode"] = mode + + var/list/list/modes = list() + data["modes"] = modes + modes[++modes.len] = list("name" = "Filtering - Scrubs out contaminants", "mode" = AALARM_MODE_SCRUBBING, "selected" = mode == AALARM_MODE_SCRUBBING, "danger" = 0) + modes[++modes.len] = list("name" = "Replace Air - Siphons out air while replacing", "mode" = AALARM_MODE_REPLACEMENT, "selected" = mode == AALARM_MODE_REPLACEMENT, "danger" = 0) + modes[++modes.len] = list("name" = "Panic - Siphons air out of the room", "mode" = AALARM_MODE_PANIC, "selected" = mode == AALARM_MODE_PANIC, "danger" = 1) + modes[++modes.len] = list("name" = "Cycle - Siphons air before replacing", "mode" = AALARM_MODE_CYCLE, "selected" = mode == AALARM_MODE_CYCLE, "danger" = 1) + modes[++modes.len] = list("name" = "Fill - Shuts off scrubbers and opens vents", "mode" = AALARM_MODE_FILL, "selected" = mode == AALARM_MODE_FILL, "danger" = 0) + modes[++modes.len] = list("name" = "Off - Shuts off vents and scrubbers", "mode" = AALARM_MODE_OFF, "selected" = mode == AALARM_MODE_OFF, "danger" = 0) + + var/list/selected + var/list/thresholds = list() + + var/list/gas_names = list("oxygen", "carbon_dioxide", "phoron", "other") //Gas ids made to match code\defines\gases.dm + for(var/g in gas_names) + thresholds[++thresholds.len] = list("name" = g, "settings" = list()) + selected = TLV[g] + for(var/i = 1, i <= 4, i++) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = i, "selected" = selected[i])) + + selected = TLV["pressure"] + thresholds[++thresholds.len] = list("name" = "Pressure", "settings" = list()) + for(var/i = 1, i <= 4, i++) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = i, "selected" = selected[i])) + + selected = TLV["temperature"] + thresholds[++thresholds.len] = list("name" = "Temperature", "settings" = list()) + for(var/i = 1, i <= 4, i++) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = i, "selected" = selected[i])) + + data["thresholds"] = thresholds + return data + +/obj/machinery/alarm/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(action == "rcon") + var/attempted_rcon_setting = text2num(params["rcon"]) + + switch(attempted_rcon_setting) + if(RCON_NO) + rcon_setting = RCON_NO + if(RCON_AUTO) + rcon_setting = RCON_AUTO + if(RCON_YES) + rcon_setting = RCON_YES + return TRUE + + if(action == "temperature") + var/list/selected = TLV["temperature"] + var/max_temperature = min(selected[3] - T0C, MAX_TEMPERATURE) + var/min_temperature = max(selected[2] - T0C, MIN_TEMPERATURE) + var/input_temperature = tgui_input_number(usr, "What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C, max_temperature, min_temperature, round_value = FALSE) + if(isnum(input_temperature)) + if(input_temperature > max_temperature || input_temperature < min_temperature) + to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C") + else + target_temperature = input_temperature + T0C + return TRUE + + // Account for remote users here. + // Yes, this is kinda snowflaky; however, I would argue it would be far more snowflakey + // to include "custom hrefs" and all the other bullshit that nano states have just for the + // like, two UIs, that want remote access to other UIs. + if((locked && !issilicon(usr) && !istype(state, /datum/tgui_state/air_alarm_remote)) || (issilicon(usr) && aidisabled)) + return + + var/device_id = params["id_tag"] + switch(action) + if("lock") + if(issilicon(usr) && !wires.is_cut(WIRE_IDSCAN)) + locked = !locked + . = TRUE + if( "power", + "o2_scrub", + "n2_scrub", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "fuel_scrub", + "panic_siphon", + "scrubbing", + "direction") + send_signal(device_id, list("[action]" = text2num(params["val"])), usr) + . = TRUE + if("excheck") + send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) + . = TRUE + if("incheck") + send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) + . = TRUE + if("set_external_pressure", "set_internal_pressure") + var/target = params["value"] + if(!isnull(target)) + send_signal(device_id, list("[action]" = target), usr) + . = TRUE + if("reset_external_pressure") + send_signal(device_id, list("reset_external_pressure"), usr) + . = TRUE + if("reset_internal_pressure") + send_signal(device_id, list("reset_internal_pressure"), usr) + . = TRUE + if("threshold") + var/env = params["env"] + + var/name = params["var"] + var/value = tgui_input_number(usr, "New [name] for [env]:", name, TLV[env][name], min_value=-1, round_value = FALSE) + if(!isnull(value) && !..()) + if(value < 0) + TLV[env][name] = -1 + else + TLV[env][name] = round(value, 0.01) + clamp_tlv_values(env, name) + // investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) + . = TRUE + if("mode") + mode = text2num(params["mode"]) + // investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) + apply_mode(usr) + . = TRUE + if("alarm") + if(alarm_area.atmosalert(2, src)) + apply_danger_level(2) + . = TRUE + if("reset") + atmos_reset() + . = TRUE + update_icon() + +// This big ol' mess just ensures that TLV always makes sense. If you set the max value below the min value, +// it'll automatically update all the other values to keep it sane. +/obj/machinery/alarm/proc/clamp_tlv_values(env, changed_threshold) + var/list/selected = TLV[env] + switch(changed_threshold) + if(1) + if(selected[1] > selected[2]) + selected[2] = selected[1] + if(selected[1] > selected[3]) + selected[3] = selected[1] + if(selected[1] > selected[4]) + selected[4] = selected[1] + if(2) + if(selected[1] > selected[2]) + selected[1] = selected[2] + if(selected[2] > selected[3]) + selected[3] = selected[2] + if(selected[2] > selected[4]) + selected[4] = selected[2] + if(3) + if(selected[1] > selected[3]) + selected[1] = selected[3] + if(selected[2] > selected[3]) + selected[2] = selected[3] + if(selected[3] > selected[4]) + selected[4] = selected[3] + if(4) + if(selected[1] > selected[4]) + selected[1] = selected[4] + if(selected[2] > selected[4]) + selected[2] = selected[4] + if(selected[3] > selected[4]) + selected[3] = selected[4] + + + + +/obj/machinery/alarm/proc/atmos_reset() + if(alarm_area.atmosalert(0, src)) + apply_danger_level(0) + update_icon() + +/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) + add_fingerprint(user) + if(alarm_deconstruction_screwdriver(user, W)) + return + if(alarm_deconstruction_wirecutters(user, W)) + return + + if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card + togglelock() + return ..() + +/obj/machinery/alarm/verb/togglelock(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "It does nothing.") + return + else + if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) + locked = !locked + to_chat(user, "You [locked ? "lock" : "unlock"] the Air Alarm interface.") + else + to_chat(user, "Access denied.") + return + +/obj/machinery/alarm/AltClick() + ..() + togglelock() + +/obj/machinery/alarm/power_change() + ..() + spawn(rand(0,15)) + update_icon() + +// VOREStation Edit Start +/obj/machinery/alarm/freezer + target_temperature = T0C - 13.15 // Chilly freezer room + +/obj/machinery/alarm/freezer/first_run() + . = ..() + + TLV["temperature"] = list(T0C - 40, T0C - 20, T0C + 40, T0C + 66) // K, Lower Temperature for Freezer Air Alarms (This is because TLV is hardcoded to be generated on first_run, and therefore the only way to modify this without changing TLV generation) + +// VOREStation Edit End +#undef LOAD_TLV_VALUES +#undef TEST_TLV_VALUES +#undef DECLARE_TLV_VALUES diff --git a/code/game/machinery/airconditioner_vr.dm b/code/game/machinery/airconditioner_vr.dm index 364d4bf3771..9ee2ccca295 100644 --- a/code/game/machinery/airconditioner_vr.dm +++ b/code/game/machinery/airconditioner_vr.dm @@ -30,13 +30,13 @@ . += "There is a small display that reads \"[convert_k2c(target_temp)]C\"." /obj/machinery/power/thermoregulator/attackby(obj/item/I, mob/user) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) if(default_deconstruction_screwdriver(user,I)) return - if(I.is_crowbar()) + if(I.has_tool_quality(TOOL_CROWBAR)) if(default_deconstruction_crowbar(user,I)) return - if(I.is_wrench()) + if(I.has_tool_quality(TOOL_WRENCH)) anchored = !anchored visible_message("\The [src] has been [anchored ? "bolted to the floor" : "unbolted from the floor"] by [user].") playsound(src, I.usesound, 75, 1) @@ -47,7 +47,7 @@ turn_off() return if(istype(I, /obj/item/device/multitool)) - var/new_temp = tgui_input_number(usr, "Input a new target temperature, in degrees C.","Target Temperature", 20) + var/new_temp = tgui_input_number(usr, "Input a new target temperature, in degrees C.","Target Temperature", convert_k2c(target_temp), round_value = FALSE) if(!Adjacent(user) || user.incapacitated()) return new_temp = convert_c2k(new_temp) @@ -157,4 +157,4 @@ #undef MODE_IDLE #undef MODE_HEATING -#undef MODE_COOLING \ No newline at end of file +#undef MODE_COOLING diff --git a/code/game/machinery/atm_ret_field.dm b/code/game/machinery/atm_ret_field.dm index 603e6615be7..853550b9d40 100644 --- a/code/game/machinery/atm_ret_field.dm +++ b/code/game/machinery/atm_ret_field.dm @@ -41,11 +41,11 @@ field_type = /obj/structure/atmospheric_retention_field/impassable /obj/machinery/atmospheric_field_generator/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_crowbar() && isactive) + if(W.has_tool_quality(TOOL_CROWBAR) && isactive) if(!src) return to_chat(user, "You can't open the ARF-G whilst it's running!") return - if(W.is_crowbar() && !isactive) + if(W.has_tool_quality(TOOL_CROWBAR) && !isactive) if(!src) return to_chat(user, "You [hatch_open? "close" : "open"] \the [src]'s access hatch.") hatch_open = !hatch_open @@ -53,21 +53,21 @@ if(alwaysactive && wires_intact) generate_field() return - if(hatch_open && W.is_multitool()) + if(hatch_open && W.has_tool_quality(TOOL_MULTITOOL)) if(!src) return to_chat(user, "You toggle \the [src]'s activation behavior to [alwaysactive? "emergency" : "always-on"].") alwaysactive = !alwaysactive update_icon() return - if(hatch_open && W.is_wirecutter()) + if(hatch_open && W.has_tool_quality(TOOL_WIRECUTTER)) if(!src) return to_chat(user, "You [wires_intact? "cut" : "mend"] \the [src]'s wires!") wires_intact = !wires_intact update_icon() return - if(hatch_open && istype(W,/obj/item/weapon/weldingtool)) + if(hatch_open && W.has_tool_quality(TOOL_WELDER)) if(!src) return - var/obj/item/weapon/weldingtool/WT = W + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(!WT.isOn()) return if(WT.get_fuel() < 5) // uses up 5 fuel. to_chat(user, "You need more fuel to complete this task.") diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 27fc21f4f15..ca824fc22ae 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -1,455 +1,455 @@ -/obj/machinery/air_sensor - icon = 'icons/obj/stationobjs.dmi' - icon_state = "gsensor1" - name = "Gas Sensor" - desc = "Senses atmospheric conditions." - - anchored = TRUE - var/state = 0 - - var/id_tag - var/frequency = 1439 - - var/on = 1 - var/output = 3 - //Flags: - // 1 for pressure - // 2 for temperature - // Output >= 4 includes gas composition - // 4 for oxygen concentration - // 8 for phoron concentration - // 16 for nitrogen concentration - // 32 for carbon dioxide concentration - - var/datum/radio_frequency/radio_connection - -/obj/machinery/air_sensor/update_icon() - icon_state = "gsensor[on]" - -/obj/machinery/air_sensor/process() - if(on) - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.data["tag"] = id_tag - signal.data["timestamp"] = world.time - - var/datum/gas_mixture/air_sample = return_air() - - if(output&1) - signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1),) - if(output&2) - signal.data["temperature"] = round(air_sample.temperature,0.1) - - if(output>4) - var/total_moles = air_sample.total_moles - if(total_moles > 0) - if(output&4) - signal.data["oxygen"] = round(100*air_sample.gas["oxygen"]/total_moles,0.1) - if(output&8) - signal.data["phoron"] = round(100*air_sample.gas["phoron"]/total_moles,0.1) - if(output&16) - signal.data["nitrogen"] = round(100*air_sample.gas["nitrogen"]/total_moles,0.1) - if(output&32) - signal.data["carbon_dioxide"] = round(100*air_sample.gas["carbon_dioxide"]/total_moles,0.1) - else - signal.data["oxygen"] = 0 - signal.data["phoron"] = 0 - signal.data["nitrogen"] = 0 - signal.data["carbon_dioxide"] = 0 - signal.data["sigtype"]="status" - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/air_sensor/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/air_sensor/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/air_sensor/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - . = ..() - -/obj/machinery/computer/general_air_control - icon_keyboard = "atmos_key" - icon_screen = "tank" - name = "Computer" - desc = "Control atmospheric systems, remotely." - var/frequency = 1439 - var/list/sensors = list() - var/list/sensor_information = list() - var/datum/radio_frequency/radio_connection - circuit = /obj/item/weapon/circuitboard/air_management - -/obj/machinery/computer/general_air_control/Destroy() - if(radio_controller) - radio_controller.remove_object(src, frequency) - ..() - -/obj/machinery/computer/general_air_control/attack_hand(mob/user) - if(..(user)) - return - - tgui_interact(user) - -/obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - if(!id_tag || !sensors.Find(id_tag)) return - - sensor_information[id_tag] = signal.data - -/obj/machinery/computer/general_air_control/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "GeneralAtmoControl", name) - ui.open() - -/obj/machinery/computer/general_air_control/tgui_data(mob/user) - var/list/data = list() - var/sensors_ui[0] - if(sensors.len) - for(var/id_tag in sensors) - var/long_name = sensors[id_tag] - var/list/sensor_data = sensor_information[id_tag] - sensors_ui[++sensors_ui.len] = list("long_name" = long_name, "sensor_data" = sensor_data) - else - sensors_ui = null - - data["sensors"] = sensors_ui - - return data - -/obj/machinery/computer/general_air_control/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/Initialize() - . = ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/computer/general_air_control/large_tank_control - icon = 'icons/obj/computer.dmi' - frequency = 1441 - var/input_tag - var/output_tag - var/list/input_info - var/list/output_info - var/input_flow_setting = 200 - var/pressure_setting = ONE_ATMOSPHERE * 45 - circuit = /obj/item/weapon/circuitboard/air_management/tank_control - -/obj/machinery/computer/general_air_control/large_tank_control/tgui_data(mob/user) - var/list/data = ..() - - data["tanks"] = 1 - - if(input_info) - data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) - else - data["input_info"] = null - - if(output_info) - data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["internal"]) - else - data["output_info"] = null - - data["input_flow_setting"] = round(input_flow_setting, 0.1) - data["pressure_setting"] = pressure_setting - data["max_pressure"] = 50*ONE_ATMOSPHERE - data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 - - return data - -/obj/machinery/computer/general_air_control/large_tank_control/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - - if(input_tag == id_tag) - input_info = signal.data - else if(output_tag == id_tag) - output_info = signal.data - else - ..(signal) - -/obj/machinery/computer/general_air_control/large_tank_control/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("adj_pressure") - var/new_pressure = text2num(params["adj_pressure"]) - pressure_setting = between(0, new_pressure, 50*ONE_ATMOSPHERE) - return TRUE - - if("adj_input_flow_rate") - var/new_flow = text2num(params["adj_input_flow_rate"]) - input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors - return TRUE - - if(!radio_connection) - return FALSE - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - switch(action) - if("in_refresh_status") - input_info = null - signal.data = list ("tag" = input_tag, "status" = 1) - . = TRUE - - if("in_toggle_injector") - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle" = 1) - . = TRUE - - if("in_set_flowrate") - input_info = null - signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") - . = TRUE - - if("out_refresh_status") - output_info = null - signal.data = list ("tag" = output_tag, "status" = 1) - . = TRUE - - if("out_toggle_power") - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle" = 1) - . = TRUE - - if("out_set_pressure") - output_info = null - signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]") - . = TRUE - - signal.data["sigtype"]="command" - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/supermatter_core - icon = 'icons/obj/computer.dmi' - frequency = 1438 - var/input_tag - var/output_tag - var/list/input_info - var/list/output_info - var/input_flow_setting = 700 - var/pressure_setting = 100 - circuit = /obj/item/weapon/circuitboard/air_management/supermatter_core - -/obj/machinery/computer/general_air_control/supermatter_core/tgui_data(mob/user) - var/list/data = ..() - data["core"] = 1 - - if(input_info) - data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) - else - data["input_info"] = null - - if(output_info) - // Yes, TECHNICALLY this is not output pressure, it's a pressure LIMIT. HOWEVER. The fact that the UI uses "output_pressure" - // in EXACTLY THE SAME WAY as "pressure_limit" means this should just pass it as the other fucking data argument because holy shit what the - // fuck - data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["external"]) - else - data["output_info"] = null - - data["input_flow_setting"] = round(input_flow_setting, 0.1) - data["pressure_setting"] = pressure_setting - data["max_pressure"] = 10*ONE_ATMOSPHERE - data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 - - return data - -/obj/machinery/computer/general_air_control/supermatter_core/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - - if(input_tag == id_tag) - input_info = signal.data - else if(output_tag == id_tag) - output_info = signal.data - else - ..(signal) - -/obj/machinery/computer/general_air_control/supermatter_core/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("adj_pressure") - var/new_pressure = text2num(params["adj_pressure"]) - pressure_setting = between(0, new_pressure, 10*ONE_ATMOSPHERE) - return TRUE - - if("adj_input_flow_rate") - var/new_flow = text2num(params["adj_input_flow_rate"]) - input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors - return TRUE - - if(!radio_connection) - return FALSE - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - switch(action) - if("in_refresh_status") - input_info = null - signal.data = list ("tag" = input_tag, "status" = 1) - . = TRUE - - if("in_toggle_injector") - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle" = 1) - . = TRUE - - if("in_set_flowrate") - input_info = null - signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") - . = TRUE - - if("out_refresh_status") - output_info = null - signal.data = list ("tag" = output_tag, "status" = 1) - . = TRUE - - if("out_toggle_power") - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle" = 1) - . = TRUE - - if("out_set_pressure") - output_info = null - signal.data = list ("tag" = output_tag, "set_external_pressure" = "[pressure_setting]", "checks" = 1) - . = TRUE - - signal.data["sigtype"]="command" - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/fuel_injection - icon = 'icons/obj/computer.dmi' - icon_screen = "alert:0" - var/device_tag - var/list/device_info - var/automation = 0 - var/cutoff_temperature = 2000 - var/on_temperature = 1200 - circuit = /obj/item/weapon/circuitboard/air_management/injector_control - -/obj/machinery/computer/general_air_control/fuel_injection/process() - if(automation) - if(!radio_connection) - return FALSE - - var/injecting = 0 - for(var/id_tag in sensor_information) - var/list/data = sensor_information[id_tag] - if(data["temperature"]) - if(data["temperature"] >= cutoff_temperature) - injecting = 0 - break - if(data["temperature"] <= on_temperature) - injecting = 1 - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - - signal.data = list( - "tag" = device_tag, - "power" = injecting, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - - ..() - -/obj/machinery/computer/general_air_control/fuel_injection/tgui_data(mob/user) - var/list/data = ..() - data["fuel"] = 1 - data["automation"] = automation - - if(device_info) - data["device_info"] = list("power" = device_info["power"], "volume_rate" = device_info["volume_rate"]) - else - data["device_info"] = null - - return data - -/obj/machinery/computer/general_air_control/fuel_injection/receive_signal(datum/signal/signal) - if(!signal || signal.encryption) return - - var/id_tag = signal.data["tag"] - - if(device_tag == id_tag) - device_info = signal.data - else - ..(signal) - -/obj/machinery/computer/general_air_control/fuel_injection/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("refresh_status") - device_info = null - if(!radio_connection) - return FALSE - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "status" = 1, - "sigtype"="command" - ) - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - . = TRUE - - if("toggle_automation") - automation = !automation - . = TRUE - - if("toggle_injector") - device_info = null - if(!radio_connection) - return FALSE - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "power_toggle" = 1, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) - . = TRUE - - if("injection") - if(!radio_connection) - return FALSE - - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "inject" = 1, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) +/obj/machinery/air_sensor + icon = 'icons/obj/stationobjs.dmi' + icon_state = "gsensor1" + name = "Gas Sensor" + desc = "Senses atmospheric conditions." + + anchored = TRUE + var/state = 0 + + var/id_tag + var/frequency = 1439 + + var/on = 1 + var/output = 3 + //Flags: + // 1 for pressure + // 2 for temperature + // Output >= 4 includes gas composition + // 4 for oxygen concentration + // 8 for phoron concentration + // 16 for nitrogen concentration + // 32 for carbon dioxide concentration + + var/datum/radio_frequency/radio_connection + +/obj/machinery/air_sensor/update_icon() + icon_state = "gsensor[on]" + +/obj/machinery/air_sensor/process() + if(on) + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.data["tag"] = id_tag + signal.data["timestamp"] = world.time + + var/datum/gas_mixture/air_sample = return_air() + + if(output&1) + signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1),) + if(output&2) + signal.data["temperature"] = round(air_sample.temperature,0.1) + + if(output>4) + var/total_moles = air_sample.total_moles + if(total_moles > 0) + if(output&4) + signal.data["oxygen"] = round(100*air_sample.gas["oxygen"]/total_moles,0.1) + if(output&8) + signal.data["phoron"] = round(100*air_sample.gas["phoron"]/total_moles,0.1) + if(output&16) + signal.data["nitrogen"] = round(100*air_sample.gas["nitrogen"]/total_moles,0.1) + if(output&32) + signal.data["carbon_dioxide"] = round(100*air_sample.gas["carbon_dioxide"]/total_moles,0.1) + else + signal.data["oxygen"] = 0 + signal.data["phoron"] = 0 + signal.data["nitrogen"] = 0 + signal.data["carbon_dioxide"] = 0 + signal.data["sigtype"]="status" + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/air_sensor/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/air_sensor/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/air_sensor/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + . = ..() + +/obj/machinery/computer/general_air_control + icon_keyboard = "atmos_key" + icon_screen = "tank" + name = "Computer" + desc = "Control atmospheric systems, remotely." + var/frequency = 1439 + var/list/sensors = list() + var/list/sensor_information = list() + var/datum/radio_frequency/radio_connection + circuit = /obj/item/weapon/circuitboard/air_management + +/obj/machinery/computer/general_air_control/Destroy() + if(radio_controller) + radio_controller.remove_object(src, frequency) + ..() + +/obj/machinery/computer/general_air_control/attack_hand(mob/user) + if(..(user)) + return + + tgui_interact(user) + +/obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + if(!id_tag || !sensors.Find(id_tag)) return + + sensor_information[id_tag] = signal.data + +/obj/machinery/computer/general_air_control/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GeneralAtmoControl", name) + ui.open() + +/obj/machinery/computer/general_air_control/tgui_data(mob/user) + var/list/data = list() + var/sensors_ui[0] + if(sensors.len) + for(var/id_tag in sensors) + var/long_name = sensors[id_tag] + var/list/sensor_data = sensor_information[id_tag] + sensors_ui[++sensors_ui.len] = list("long_name" = long_name, "sensor_data" = sensor_data) + else + sensors_ui = null + + data["sensors"] = sensors_ui + + return data + +/obj/machinery/computer/general_air_control/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/Initialize() + . = ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/computer/general_air_control/large_tank_control + icon = 'icons/obj/computer.dmi' + frequency = 1441 + var/input_tag + var/output_tag + var/list/input_info + var/list/output_info + var/input_flow_setting = 200 + var/pressure_setting = ONE_ATMOSPHERE * 45 + circuit = /obj/item/weapon/circuitboard/air_management/tank_control + +/obj/machinery/computer/general_air_control/large_tank_control/tgui_data(mob/user) + var/list/data = ..() + + data["tanks"] = 1 + + if(input_info) + data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) + else + data["input_info"] = null + + if(output_info) + data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["internal"]) + else + data["output_info"] = null + + data["input_flow_setting"] = round(input_flow_setting, 0.1) + data["pressure_setting"] = pressure_setting + data["max_pressure"] = 50*ONE_ATMOSPHERE + data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 + + return data + +/obj/machinery/computer/general_air_control/large_tank_control/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/large_tank_control/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("adj_pressure") + var/new_pressure = text2num(params["adj_pressure"]) + pressure_setting = between(0, new_pressure, 50*ONE_ATMOSPHERE) + return TRUE + + if("adj_input_flow_rate") + var/new_flow = text2num(params["adj_input_flow_rate"]) + input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors + return TRUE + + if(!radio_connection) + return FALSE + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + switch(action) + if("in_refresh_status") + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + . = TRUE + + if("in_toggle_injector") + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + . = TRUE + + if("in_set_flowrate") + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") + . = TRUE + + if("out_refresh_status") + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + . = TRUE + + if("out_toggle_power") + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + . = TRUE + + if("out_set_pressure") + output_info = null + signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]") + . = TRUE + + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/supermatter_core + icon = 'icons/obj/computer.dmi' + frequency = 1438 + var/input_tag + var/output_tag + var/list/input_info + var/list/output_info + var/input_flow_setting = 700 + var/pressure_setting = 100 + circuit = /obj/item/weapon/circuitboard/air_management/supermatter_core + +/obj/machinery/computer/general_air_control/supermatter_core/tgui_data(mob/user) + var/list/data = ..() + data["core"] = 1 + + if(input_info) + data["input_info"] = list("power" = input_info["power"], "volume_rate" = round(input_info["volume_rate"], 0.1)) + else + data["input_info"] = null + + if(output_info) + // Yes, TECHNICALLY this is not output pressure, it's a pressure LIMIT. HOWEVER. The fact that the UI uses "output_pressure" + // in EXACTLY THE SAME WAY as "pressure_limit" means this should just pass it as the other fucking data argument because holy shit what the + // fuck + data["output_info"] = list("power" = output_info["power"], "output_pressure" = output_info["external"]) + else + data["output_info"] = null + + data["input_flow_setting"] = round(input_flow_setting, 0.1) + data["pressure_setting"] = pressure_setting + data["max_pressure"] = 10*ONE_ATMOSPHERE + data["max_flowrate"] = ATMOS_DEFAULT_VOLUME_PUMP + 500 + + return data + +/obj/machinery/computer/general_air_control/supermatter_core/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(input_tag == id_tag) + input_info = signal.data + else if(output_tag == id_tag) + output_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/supermatter_core/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("adj_pressure") + var/new_pressure = text2num(params["adj_pressure"]) + pressure_setting = between(0, new_pressure, 10*ONE_ATMOSPHERE) + return TRUE + + if("adj_input_flow_rate") + var/new_flow = text2num(params["adj_input_flow_rate"]) + input_flow_setting = between(0, new_flow, ATMOS_DEFAULT_VOLUME_PUMP + 500) //default flow rate limit for air injectors + return TRUE + + if(!radio_connection) + return FALSE + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + switch(action) + if("in_refresh_status") + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + . = TRUE + + if("in_toggle_injector") + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + . = TRUE + + if("in_set_flowrate") + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[input_flow_setting]") + . = TRUE + + if("out_refresh_status") + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + . = TRUE + + if("out_toggle_power") + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + . = TRUE + + if("out_set_pressure") + output_info = null + signal.data = list ("tag" = output_tag, "set_external_pressure" = "[pressure_setting]", "checks" = 1) + . = TRUE + + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/fuel_injection + icon = 'icons/obj/computer.dmi' + icon_screen = "alert:0" + var/device_tag + var/list/device_info + var/automation = 0 + var/cutoff_temperature = 2000 + var/on_temperature = 1200 + circuit = /obj/item/weapon/circuitboard/air_management/injector_control + +/obj/machinery/computer/general_air_control/fuel_injection/process() + if(automation) + if(!radio_connection) + return FALSE + + var/injecting = 0 + for(var/id_tag in sensor_information) + var/list/data = sensor_information[id_tag] + if(data["temperature"]) + if(data["temperature"] >= cutoff_temperature) + injecting = 0 + break + if(data["temperature"] <= on_temperature) + injecting = 1 + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + + signal.data = list( + "tag" = device_tag, + "power" = injecting, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + + ..() + +/obj/machinery/computer/general_air_control/fuel_injection/tgui_data(mob/user) + var/list/data = ..() + data["fuel"] = 1 + data["automation"] = automation + + if(device_info) + data["device_info"] = list("power" = device_info["power"], "volume_rate" = device_info["volume_rate"]) + else + data["device_info"] = null + + return data + +/obj/machinery/computer/general_air_control/fuel_injection/receive_signal(datum/signal/signal) + if(!signal || signal.encryption) return + + var/id_tag = signal.data["tag"] + + if(device_tag == id_tag) + device_info = signal.data + else + ..(signal) + +/obj/machinery/computer/general_air_control/fuel_injection/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("refresh_status") + device_info = null + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "status" = 1, + "sigtype"="command" + ) + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + . = TRUE + + if("toggle_automation") + automation = !automation + . = TRUE + + if("toggle_injector") + device_info = null + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "power_toggle" = 1, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) + . = TRUE + + if("injection") + if(!radio_connection) + return FALSE + + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "inject" = 1, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, radio_filter = RADIO_ATMOSIA) . = TRUE \ No newline at end of file diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index bb0858a79ed..3fb8adccfd4 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,471 +1,471 @@ -/obj/machinery/portable_atmospherics/canister - name = "canister" - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = TRUE - var/health = 100.0 - w_class = ITEMSIZE_HUGE - - layer = TABLE_LAYER // Above catwalks, hopefully below other things - - var/valve_open = 0 - var/release_pressure = ONE_ATMOSPHERE - var/release_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP //in L/s - - var/canister_color = "yellow" - var/can_label = 1 - start_pressure = 45 * ONE_ATMOSPHERE - pressure_resistance = 7 * ONE_ATMOSPHERE - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = USE_POWER_OFF - interact_offline = 1 // Allows this to be used when not in powered area. - var/release_log = "" - var/update_flag = 0 - -/obj/machinery/portable_atmospherics/canister/drain_power() - return -1 - -/obj/machinery/portable_atmospherics/canister/nitrous_oxide - name = "Canister: \[N2O\]" - icon_state = "redws" - canister_color = "redws" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/nitrogen - name = "Canister: \[N2\]" - icon_state = "red" - canister_color = "red" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" - canister_color = "blue" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled - name = "Canister: \[O2 (Cryo)\]" - -/obj/machinery/portable_atmospherics/canister/phoron - name = "Canister \[Phoron\]" - icon_state = "orangeps" - canister_color = "orangeps" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" - canister_color = "black" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/air - name = "Canister \[Air\]" - icon_state = "grey" - canister_color = "grey" - can_label = 0 - -/obj/machinery/portable_atmospherics/canister/air/airlock - start_pressure = 3 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/canister/empty/ - start_pressure = 0 - can_label = 1 - -/obj/machinery/portable_atmospherics/canister/empty/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" - canister_color = "blue" -/obj/machinery/portable_atmospherics/canister/empty/phoron - name = "Canister \[Phoron\]" - icon_state = "orangeps" - canister_color = "orangeps" -/obj/machinery/portable_atmospherics/canister/empty/nitrogen - name = "Canister \[N2\]" - icon_state = "red" - canister_color = "red" -/obj/machinery/portable_atmospherics/canister/empty/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" - canister_color = "black" -/obj/machinery/portable_atmospherics/canister/empty/nitrous_oxide - name = "Canister \[N2O\]" - icon_state = "redws" - canister_color = "redws" - - - - -/obj/machinery/portable_atmospherics/canister/proc/check_change() - var/old_flag = update_flag - update_flag = 0 - if(holding) - update_flag |= 1 - if(connected_port) - update_flag |= 2 - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < 10) - update_flag |= 4 - else if(tank_pressure < ONE_ATMOSPHERE) - update_flag |= 8 - else if(tank_pressure < 15*ONE_ATMOSPHERE) - update_flag |= 16 - else - update_flag |= 32 - - if(update_flag == old_flag) - return 1 - else - return 0 - -/obj/machinery/portable_atmospherics/canister/update_icon() -/* -update_flag -1 = holding -2 = connected_port -4 = tank_pressure < 10 -8 = tank_pressure < ONE_ATMOS -16 = tank_pressure < 15*ONE_ATMOS -32 = tank_pressure go boom. -*/ - - if (src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color) - return - - if(icon_state != "[canister_color]") - icon_state = "[canister_color]" - - if(check_change()) //Returns 1 if no change needed to icons. - return - - cut_overlays() - - if(update_flag & 1) - add_overlay("can-open") - if(update_flag & 2) - add_overlay("can-connector") - if(update_flag & 4) - add_overlay("can-o0") - if(update_flag & 8) - add_overlay("can-o1") - else if(update_flag & 16) - add_overlay("can-o2") - else if(update_flag & 32) - add_overlay("can-o3") - return - -/obj/machinery/portable_atmospherics/canister/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > temperature_resistance) - health -= 5 - healthcheck() - -/obj/machinery/portable_atmospherics/canister/proc/healthcheck() - if(destroyed) - return 1 - - if (src.health <= 10) - var/atom/location = src.loc - location.assume_air(air_contents) - - src.destroyed = 1 - playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) - src.density = FALSE - update_icon() - - if (src.holding) - src.holding.loc = src.loc - src.holding = null - - return 1 - else - return 1 - -/obj/machinery/portable_atmospherics/canister/process() - if (destroyed) - return - - ..() - - if(valve_open) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/env_pressure = environment.return_pressure() - var/pressure_delta = release_pressure - env_pressure - - if((air_contents.temperature > 0) && (pressure_delta > 0)) - var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) - transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.volume)*air_contents.total_moles) //flow rate limit - - var/returnval = pump_gas_passive(src, air_contents, environment, transfer_moles) - if(returnval >= 0) - src.update_icon() - - if(air_contents.return_pressure() < 1) - can_label = 1 - else - can_label = 0 - - air_contents.react() //cooking up air cans - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE - -/obj/machinery/portable_atmospherics/canister/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.return_pressure() - return 0 - -/obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/item/projectile/Proj) - if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - return - - if(Proj.damage) - src.health -= round(Proj.damage / 2) - healthcheck() - ..() - -/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(istype(W, /obj/item/weapon/weldingtool)) //Vorestart: Deconstructable Canisters - var/obj/item/weapon/weldingtool/WT = W - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - if(air_contents.return_pressure() > 1 && !destroyed) // Empty or broken cans are able to be deconstructed - to_chat(user, "\The [src]'s internal pressure is too high! Empty the canister before attempting to weld it apart.") - return - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the [src].") - new /obj/item/stack/material/steel( src.loc, 10) - qdel(src) - return - //Voreend - if(!W.is_wrench() && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda)) - visible_message("\The [user] hits \the [src] with \a [W]!") - src.health -= W.force - src.add_fingerprint(user) - healthcheck() - - if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack)) - var/datum/gas_mixture/thejetpack = W:air_contents - var/env_pressure = thejetpack.return_pressure() - var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - thejetpack.merge(removed) - to_chat(user, "You pulse-pressurize your jetpack from the tank.") - return - - ..() - - SStgui.update_uis(src) // Update all NanoUIs attached to src - -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return tgui_interact(user) - -/obj/machinery/portable_atmospherics/canister/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/machinery/portable_atmospherics/canister/tgui_interact(mob/user, datum/tgui/ui) - if(destroyed) - return - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Canister", name) - ui.open() - -/obj/machinery/portable_atmospherics/canister/tgui_data(mob/user) - var/list/data = list() - data["can_relabel"] = can_label ? 1 : 0 - data["connected"] = connected_port ? 1 : 0 - data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["defaultReleasePressure"] = round(initial(release_pressure)) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) - data["valveOpen"] = valve_open ? 1 : 0 - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure()) - else - data["holding"] = null - - return data - -/obj/machinery/portable_atmospherics/canister/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("relabel") - if(can_label) - var/list/colors = list(\ - "\[N2O\]" = "redws", \ - "\[N2\]" = "red", \ - "\[O2\]" = "blue", \ - "\[Phoron\]" = "orangeps", \ - "\[CO2\]" = "black", \ - "\[Air\]" = "grey", \ - "\[CAUTION\]" = "yellow", \ - ) - var/label = tgui_input_list(usr, "Choose canister label", "Gas canister", colors) - if(label) - canister_color = colors[label] - icon_state = colors[label] - name = "Canister: [label]" - if("pressure") - var/pressure = params["pressure"] - if(pressure == "reset") - pressure = initial(release_pressure) - . = TRUE - else if(pressure == "min") - pressure = ONE_ATMOSPHERE/10 - . = TRUE - else if(pressure == "max") - pressure = 10*ONE_ATMOSPHERE - . = TRUE - else if(pressure == "input") - pressure = tgui_input_number(usr, "New release pressure ([ONE_ATMOSPHERE/10]-[10*ONE_ATMOSPHERE] kPa):", name, release_pressure, 10*ONE_ATMOSPHERE, ONE_ATMOSPHERE/10) - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - release_pressure = clamp(round(pressure), ONE_ATMOSPHERE/10, 10*ONE_ATMOSPHERE) - if("valve") - if(valve_open) - if(holding) - release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " - else - release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the air
    " - else - if(holding) - release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the [holding]
    " - else - release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the air
    " - log_open() - valve_open = !valve_open - . = TRUE - if("eject") - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " - if(istype(holding, /obj/item/weapon/tank)) - holding.manipulated_by = usr.real_name - holding.loc = loc - holding = null - . = TRUE - - add_fingerprint(usr) - update_icon() - -/obj/machinery/portable_atmospherics/canister/phoron/New() - ..() - - src.air_contents.adjust_gas("phoron", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/New() - ..() - - src.air_contents.adjust_gas("oxygen", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/New() - ..() - - src.air_contents.adjust_gas("oxygen", MolesForPressure()) - src.air_contents.temperature = 80 - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/nitrous_oxide/New() - ..() - - air_contents.adjust_gas("nitrous_oxide", MolesForPressure()) - src.update_icon() - return 1 - -//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 -/obj/machinery/portable_atmospherics/canister/nitrous_oxide/roomfiller/Initialize() - . = ..() - air_contents.gas["nitrous_oxide"] = 9*4000 - var/turf/simulated/location = src.loc - if (istype(src.loc)) - location.assume_air(air_contents) - air_contents = new - return 1 - -/obj/machinery/portable_atmospherics/canister/nitrogen/New() - - ..() - - src.air_contents.adjust_gas("nitrogen", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() - ..() - src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) - src.update_icon() - return 1 - - -/obj/machinery/portable_atmospherics/canister/air/New() - ..() - var/list/air_mix = StandardAirMix() - src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) - - src.update_icon() - return 1 - -//R-UST port -// Special types used for engine setup admin verb, they contain double amount of that of normal canister. -/obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/New() - ..() - src.air_contents.adjust_gas("nitrogen", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/New() - ..() - src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/New() - ..() - src.air_contents.adjust_gas("phoron", MolesForPressure()) - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/take_damage(var/damage) - src.health -= damage - healthcheck() +/obj/machinery/portable_atmospherics/canister + name = "canister" + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = TRUE + var/health = 100.0 + w_class = ITEMSIZE_HUGE + + layer = TABLE_LAYER // Above catwalks, hopefully below other things + + var/valve_open = 0 + var/release_pressure = ONE_ATMOSPHERE + var/release_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP //in L/s + + var/canister_color = "yellow" + var/can_label = 1 + start_pressure = 45 * ONE_ATMOSPHERE + pressure_resistance = 7 * ONE_ATMOSPHERE + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = USE_POWER_OFF + interact_offline = 1 // Allows this to be used when not in powered area. + var/release_log = "" + var/update_flag = 0 + +/obj/machinery/portable_atmospherics/canister/drain_power() + return -1 + +/obj/machinery/portable_atmospherics/canister/nitrous_oxide + name = "Canister: \[N2O\]" + icon_state = "redws" + canister_color = "redws" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/nitrogen + name = "Canister: \[N2\]" + icon_state = "red" + canister_color = "red" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" + canister_color = "blue" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/oxygen/prechilled + name = "Canister: \[O2 (Cryo)\]" + +/obj/machinery/portable_atmospherics/canister/phoron + name = "Canister \[Phoron\]" + icon_state = "orangeps" + canister_color = "orangeps" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" + canister_color = "black" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/air + name = "Canister \[Air\]" + icon_state = "grey" + canister_color = "grey" + can_label = 0 + +/obj/machinery/portable_atmospherics/canister/air/airlock + start_pressure = 3 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/canister/empty/ + start_pressure = 0 + can_label = 1 + +/obj/machinery/portable_atmospherics/canister/empty/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" + canister_color = "blue" +/obj/machinery/portable_atmospherics/canister/empty/phoron + name = "Canister \[Phoron\]" + icon_state = "orangeps" + canister_color = "orangeps" +/obj/machinery/portable_atmospherics/canister/empty/nitrogen + name = "Canister \[N2\]" + icon_state = "red" + canister_color = "red" +/obj/machinery/portable_atmospherics/canister/empty/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" + canister_color = "black" +/obj/machinery/portable_atmospherics/canister/empty/nitrous_oxide + name = "Canister \[N2O\]" + icon_state = "redws" + canister_color = "redws" + + + + +/obj/machinery/portable_atmospherics/canister/proc/check_change() + var/old_flag = update_flag + update_flag = 0 + if(holding) + update_flag |= 1 + if(connected_port) + update_flag |= 2 + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < 10) + update_flag |= 4 + else if(tank_pressure < ONE_ATMOSPHERE) + update_flag |= 8 + else if(tank_pressure < 15*ONE_ATMOSPHERE) + update_flag |= 16 + else + update_flag |= 32 + + if(update_flag == old_flag) + return 1 + else + return 0 + +/obj/machinery/portable_atmospherics/canister/update_icon() +/* +update_flag +1 = holding +2 = connected_port +4 = tank_pressure < 10 +8 = tank_pressure < ONE_ATMOS +16 = tank_pressure < 15*ONE_ATMOS +32 = tank_pressure go boom. +*/ + + if (src.destroyed) + src.overlays = 0 + src.icon_state = text("[]-1", src.canister_color) + return + + if(icon_state != "[canister_color]") + icon_state = "[canister_color]" + + if(check_change()) //Returns 1 if no change needed to icons. + return + + cut_overlays() + + if(update_flag & 1) + add_overlay("can-open") + if(update_flag & 2) + add_overlay("can-connector") + if(update_flag & 4) + add_overlay("can-o0") + if(update_flag & 8) + add_overlay("can-o1") + else if(update_flag & 16) + add_overlay("can-o2") + else if(update_flag & 32) + add_overlay("can-o3") + return + +/obj/machinery/portable_atmospherics/canister/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > temperature_resistance) + health -= 5 + healthcheck() + +/obj/machinery/portable_atmospherics/canister/proc/healthcheck() + if(destroyed) + return 1 + + if (src.health <= 10) + var/atom/location = src.loc + location.assume_air(air_contents) + + src.destroyed = 1 + playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) + src.density = FALSE + update_icon() + + if (src.holding) + src.holding.loc = src.loc + src.holding = null + + return 1 + else + return 1 + +/obj/machinery/portable_atmospherics/canister/process() + if (destroyed) + return + + ..() + + if(valve_open) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/env_pressure = environment.return_pressure() + var/pressure_delta = release_pressure - env_pressure + + if((air_contents.temperature > 0) && (pressure_delta > 0)) + var/transfer_moles = calculate_transfer_moles(air_contents, environment, pressure_delta) + transfer_moles = min(transfer_moles, (release_flow_rate/air_contents.volume)*air_contents.total_moles) //flow rate limit + + var/returnval = pump_gas_passive(src, air_contents, environment, transfer_moles) + if(returnval >= 0) + src.update_icon() + + if(air_contents.return_pressure() < 1) + can_label = 1 + else + can_label = 0 + + air_contents.react() //cooking up air cans - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE + +/obj/machinery/portable_atmospherics/canister/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/canister/proc/return_temperature() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.temperature + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/return_pressure() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.return_pressure() + return 0 + +/obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/item/projectile/Proj) + if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + return + + if(Proj.damage) + src.health -= round(Proj.damage / 2) + healthcheck() + ..() + +/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER)) //Vorestart: Deconstructable Canisters + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(!WT.remove_fuel(0, user)) + to_chat(user, "The welding tool must be on to complete this task.") + return + if(air_contents.return_pressure() > 1 && !destroyed) // Empty or broken cans are able to be deconstructed + to_chat(user, "\The [src]'s internal pressure is too high! Empty the canister before attempting to weld it apart.") + return + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + to_chat(user, "You deconstruct the [src].") + new /obj/item/stack/material/steel( src.loc, 10) + qdel(src) + return + //Voreend + if(!W.has_tool_quality(TOOL_WRENCH) && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda)) + visible_message("\The [user] hits \the [src] with \a [W]!") + src.health -= W.force + src.add_fingerprint(user) + healthcheck() + + if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack)) + var/datum/gas_mixture/thejetpack = W:air_contents + var/env_pressure = thejetpack.return_pressure() + var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 0)) + transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + thejetpack.merge(removed) + to_chat(user, "You pulse-pressurize your jetpack from the tank.") + return + + ..() + + SStgui.update_uis(src) // Update all NanoUIs attached to src + +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) + return tgui_interact(user) + +/obj/machinery/portable_atmospherics/canister/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/machinery/portable_atmospherics/canister/tgui_interact(mob/user, datum/tgui/ui) + if(destroyed) + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Canister", name) + ui.open() + +/obj/machinery/portable_atmospherics/canister/tgui_data(mob/user) + var/list/data = list() + data["can_relabel"] = can_label ? 1 : 0 + data["connected"] = connected_port ? 1 : 0 + data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["defaultReleasePressure"] = round(initial(release_pressure)) + data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) + data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["valveOpen"] = valve_open ? 1 : 0 + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure()) + else + data["holding"] = null + + return data + +/obj/machinery/portable_atmospherics/canister/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("relabel") + if(can_label) + var/list/colors = list(\ + "\[N2O\]" = "redws", \ + "\[N2\]" = "red", \ + "\[O2\]" = "blue", \ + "\[Phoron\]" = "orangeps", \ + "\[CO2\]" = "black", \ + "\[Air\]" = "grey", \ + "\[CAUTION\]" = "yellow", \ + ) + var/label = tgui_input_list(usr, "Choose canister label", "Gas canister", colors) + if(label) + canister_color = colors[label] + icon_state = colors[label] + name = "Canister: [label]" + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = initial(release_pressure) + . = TRUE + else if(pressure == "min") + pressure = ONE_ATMOSPHERE/10 + . = TRUE + else if(pressure == "max") + pressure = 10*ONE_ATMOSPHERE + . = TRUE + else if(pressure == "input") + pressure = tgui_input_number(usr, "New release pressure ([ONE_ATMOSPHERE/10]-[10*ONE_ATMOSPHERE] kPa):", name, release_pressure, 10*ONE_ATMOSPHERE, ONE_ATMOSPHERE/10) + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + release_pressure = clamp(round(pressure), ONE_ATMOSPHERE/10, 10*ONE_ATMOSPHERE) + if("valve") + if(valve_open) + if(holding) + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " + else + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the air
    " + else + if(holding) + release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the [holding]
    " + else + release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the air
    " + log_open() + valve_open = !valve_open + . = TRUE + if("eject") + if(holding) + if(valve_open) + valve_open = 0 + release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding]
    " + if(istype(holding, /obj/item/weapon/tank)) + holding.manipulated_by = usr.real_name + holding.loc = loc + holding = null + . = TRUE + + add_fingerprint(usr) + update_icon() + +/obj/machinery/portable_atmospherics/canister/phoron/New() + ..() + + src.air_contents.adjust_gas("phoron", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/New() + ..() + + src.air_contents.adjust_gas("oxygen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/New() + ..() + + src.air_contents.adjust_gas("oxygen", MolesForPressure()) + src.air_contents.temperature = 80 + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/nitrous_oxide/New() + ..() + + air_contents.adjust_gas("nitrous_oxide", MolesForPressure()) + src.update_icon() + return 1 + +//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 +/obj/machinery/portable_atmospherics/canister/nitrous_oxide/roomfiller/Initialize() + . = ..() + air_contents.gas["nitrous_oxide"] = 9*4000 + var/turf/simulated/location = src.loc + if (istype(src.loc)) + location.assume_air(air_contents) + air_contents = new + return 1 + +/obj/machinery/portable_atmospherics/canister/nitrogen/New() + + ..() + + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() + ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + return 1 + + +/obj/machinery/portable_atmospherics/canister/air/New() + ..() + var/list/air_mix = StandardAirMix() + src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) + + src.update_icon() + return 1 + +//R-UST port +// Special types used for engine setup admin verb, they contain double amount of that of normal canister. +/obj/machinery/portable_atmospherics/canister/nitrogen/engine_setup/New() + ..() + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/New() + ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/New() + ..() + src.air_contents.adjust_gas("phoron", MolesForPressure()) + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/take_damage(var/damage) + src.health -= damage + healthcheck() diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 25f5e125670..93988186584 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -1,139 +1,139 @@ -/obj/machinery/meter - name = "meter" - desc = "It measures something." - icon = 'icons/obj/meter_vr.dmi' - icon_state = "meterX" - var/obj/machinery/atmospherics/pipe/target = null - var/list/pipes_on_turf = list() - anchored = TRUE - power_channel = ENVIRON - var/frequency = 0 - var/id - use_power = USE_POWER_IDLE - idle_power_usage = 15 - -/obj/machinery/meter/Initialize() - . = ..() - if (!target) - target = select_target() - -/obj/machinery/meter/Destroy() - pipes_on_turf.Cut() - target = null - return ..() - -/obj/machinery/meter/proc/select_target() - var/obj/machinery/atmospherics/pipe/P - for(P in loc) - if(!P.hides_under_flooring()) - break - if(!P) - P = locate(/obj/machinery/atmospherics/pipe) in loc - return P - -/obj/machinery/meter/process() - if(!target) - icon_state = "meterX" - return 0 - - if(stat & (BROKEN|NOPOWER)) - icon_state = "meter0" - return 0 - - var/datum/gas_mixture/environment = target.return_air() - if(!environment) - icon_state = "meterX" - return 0 - - var/env_pressure = environment.return_pressure() - if(env_pressure <= 0.15*ONE_ATMOSPHERE) - icon_state = "meter0" - else if(env_pressure <= 1.8*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) - icon_state = "meter1_[val]" - else if(env_pressure <= 30*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 - icon_state = "meter2_[val]" - else if(env_pressure <= 59*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 - icon_state = "meter3_[val]" - else - icon_state = "meter4" - - if(frequency) - var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(frequency) - - if(!radio_connection) return - - var/datum/signal/signal = new - signal.source = src - signal.transmission_method = TRANSMISSION_RADIO - signal.data = list( - "tag" = id, - "device" = "AM", - "pressure" = round(env_pressure), - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - -/obj/machinery/meter/examine(mob/user) - . = ..() - - if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/observer/dead))) - . += "You are too far away to read it." - - else if(stat & (NOPOWER|BROKEN)) - . += "The display is off." - - else if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - . += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" - else - . += "The sensor error light is blinking." - else - . += "The connect error light is blinking." - -/obj/machinery/meter/Click() - - if(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine - var/mob/living/L = usr - if(!L.get_active_hand() || !L.Adjacent(src)) - usr.examinate(src) - return 1 - - return ..() - -/obj/machinery/meter/attackby(var/obj/item/W, var/mob/user) - if(W.is_wrench()) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - new /obj/item/pipe_meter(get_turf(src)) - qdel(src) - return - - if(istype(W, /obj/item/device/multitool)) - for(var/obj/machinery/atmospherics/pipe/P in loc) - pipes_on_turf |= P - if(!pipes_on_turf.len) - return - target = pipes_on_turf[1] - pipes_on_turf.Remove(target) - pipes_on_turf.Add(target) - to_chat(user, "Pipe meter set to moniter \the [target].") - return - - return ..() - -// TURF METER - REPORTS A TILE'S AIR CONTENTS - -/obj/machinery/meter/turf/select_target() - return loc - -/obj/machinery/meter/turf/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - return +/obj/machinery/meter + name = "meter" + desc = "It measures something." + icon = 'icons/obj/meter_vr.dmi' + icon_state = "meterX" + var/obj/machinery/atmospherics/pipe/target = null + var/list/pipes_on_turf = list() + anchored = TRUE + power_channel = ENVIRON + var/frequency = 0 + var/id + use_power = USE_POWER_IDLE + idle_power_usage = 15 + +/obj/machinery/meter/Initialize() + . = ..() + if (!target) + target = select_target() + +/obj/machinery/meter/Destroy() + pipes_on_turf.Cut() + target = null + return ..() + +/obj/machinery/meter/proc/select_target() + var/obj/machinery/atmospherics/pipe/P + for(P in loc) + if(!P.hides_under_flooring()) + break + if(!P) + P = locate(/obj/machinery/atmospherics/pipe) in loc + return P + +/obj/machinery/meter/process() + if(!target) + icon_state = "meterX" + return 0 + + if(stat & (BROKEN|NOPOWER)) + icon_state = "meter0" + return 0 + + var/datum/gas_mixture/environment = target.return_air() + if(!environment) + icon_state = "meterX" + return 0 + + var/env_pressure = environment.return_pressure() + if(env_pressure <= 0.15*ONE_ATMOSPHERE) + icon_state = "meter0" + else if(env_pressure <= 1.8*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) + icon_state = "meter1_[val]" + else if(env_pressure <= 30*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 + icon_state = "meter2_[val]" + else if(env_pressure <= 59*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 + icon_state = "meter3_[val]" + else + icon_state = "meter4" + + if(frequency) + var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(frequency) + + if(!radio_connection) return + + var/datum/signal/signal = new + signal.source = src + signal.transmission_method = TRANSMISSION_RADIO + signal.data = list( + "tag" = id, + "device" = "AM", + "pressure" = round(env_pressure), + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + +/obj/machinery/meter/examine(mob/user) + . = ..() + + if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/observer/dead))) + . += "You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + . += "The display is off." + + else if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + . += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" + else + . += "The sensor error light is blinking." + else + . += "The connect error light is blinking." + +/obj/machinery/meter/Click() + + if(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + var/mob/living/L = usr + if(!L.get_active_hand() || !L.Adjacent(src)) + usr.examinate(src) + return 1 + + return ..() + +/obj/machinery/meter/attackby(var/obj/item/W, var/mob/user) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + new /obj/item/pipe_meter(get_turf(src)) + qdel(src) + return + + if(istype(W, /obj/item/device/multitool)) + for(var/obj/machinery/atmospherics/pipe/P in loc) + pipes_on_turf |= P + if(!pipes_on_turf.len) + return + target = pipes_on_turf[1] + pipes_on_turf.Remove(target) + pipes_on_turf.Add(target) + to_chat(user, "Pipe meter set to moniter \the [target].") + return + + return ..() + +// TURF METER - REPORTS A TILE'S AIR CONTENTS + +/obj/machinery/meter/turf/select_target() + return loc + +/obj/machinery/meter/turf/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + return diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 6bdf71c5254..c3fe8eaccfc 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,201 +1,201 @@ -/obj/machinery/portable_atmospherics - name = "atmoalter" - use_power = USE_POWER_OFF - layer = OBJ_LAYER // These are mobile, best not be under everything. - var/datum/gas_mixture/air_contents = new - - var/obj/machinery/atmospherics/portables_connector/connected_port - var/obj/item/weapon/tank/holding - - var/volume = 0 - var/destroyed = 0 - - var/start_pressure = ONE_ATMOSPHERE - var/maximum_pressure = 90 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/New() - ..() - //VOREStation Edit - Fix runtime - if(air_contents) - air_contents.volume = volume - air_contents.temperature = T20C - //VOREStation Edit End - - return 1 - -/obj/machinery/portable_atmospherics/Initialize() - ..() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/portable_atmospherics/LateInitialize() - var/obj/machinery/atmospherics/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - -/obj/machinery/portable_atmospherics/Destroy() - QDEL_NULL(air_contents) - QDEL_NULL(holding) - return ..() - -/obj/machinery/portable_atmospherics/process() - if(!connected_port) //only react when pipe_network will ont it do it for you - //Allow for reactions - air_contents.react() - else - update_icon() - -/obj/machinery/portable_atmospherics/blob_act() - qdel(src) - -/obj/machinery/portable_atmospherics/proc/StandardAirMix() - return list( - "oxygen" = O2STANDARD * MolesForPressure(), - "nitrogen" = N2STANDARD * MolesForPressure()) - -/obj/machinery/portable_atmospherics/proc/MolesForPressure(var/target_pressure = start_pressure) - return (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - -/obj/machinery/portable_atmospherics/update_icon() - return null - -/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - connected_port.on = 1 //Activate port updates - - anchored = TRUE //Prevent movement - - //Actually enforce the air sharing - var/datum/pipe_network/network = connected_port.return_network(src) - if(network && !network.gases.Find(air_contents)) - network.gases += air_contents - network.update = 1 - - return 1 - -/obj/machinery/portable_atmospherics/proc/disconnect() - if(!connected_port) - return 0 - - var/datum/pipe_network/network = connected_port.return_network(src) - if(network) - network.gases -= air_contents - - anchored = FALSE - - connected_port.connected_device = null - connected_port = null - - return 1 - -/obj/machinery/portable_atmospherics/proc/update_connected_network() - if(!connected_port) - return - - var/datum/pipe_network/network = connected_port.return_network(src) - if (network) - network.update = 1 - -/obj/machinery/portable_atmospherics/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if ((istype(W, /obj/item/weapon/tank) && !( src.destroyed ))) - if (src.holding) - return - var/obj/item/weapon/tank/T = W - user.drop_item() - T.loc = src - src.holding = T - update_icon() - return - - else if (W.is_wrench()) - if(connected_port) - disconnect() - to_chat(user, "You disconnect \the [src] from the port.") - update_icon() - playsound(src, W.usesound, 50, 1) - return - else - var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - to_chat(user, "You connect \the [src] to the port.") - update_icon() - playsound(src, W.usesound, 50, 1) - return - else - to_chat(user, "\The [src] failed to connect to the port.") - return - else - to_chat(user, "Nothing happens.") - return - return - - - -/obj/machinery/portable_atmospherics/powered - var/power_rating - var/power_losses - var/last_power_draw = 0 - var/obj/item/weapon/cell/cell - var/use_cell = TRUE - var/removeable_cell = TRUE - -/obj/machinery/portable_atmospherics/powered/powered() - if(use_power) //using area power - return ..() - if(cell && cell.charge) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) - if(use_cell && istype(I, /obj/item/weapon/cell)) - if(cell) - to_chat(user, "There is already a power cell installed.") - return - - var/obj/item/weapon/cell/C = I - - user.drop_item() - C.add_fingerprint(user) - cell = C - C.loc = src - user.visible_message("[user] opens the panel on [src] and inserts [C].", "You open the panel on [src] and insert [C].") - power_change() - return - - if(I.is_screwdriver() && removeable_cell) - if(!cell) - to_chat(user, "There is no power cell installed.") - return - - user.visible_message("[user] opens the panel on [src] and removes [cell].", "You open the panel on [src] and remove [cell].") - playsound(src, I.usesound, 50, 1) - cell.add_fingerprint(user) - cell.loc = src.loc - cell = null - power_change() - return - ..() - -/obj/machinery/portable_atmospherics/proc/log_open() - if(air_contents.gas.len == 0) - return - - var/gases = "" - for(var/gas in air_contents.gas) - if(gases) - gases += ", [gas]" - else - gases = gas - log_admin("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") - message_admins("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") +/obj/machinery/portable_atmospherics + name = "atmoalter" + use_power = USE_POWER_OFF + layer = OBJ_LAYER // These are mobile, best not be under everything. + var/datum/gas_mixture/air_contents = new + + var/obj/machinery/atmospherics/portables_connector/connected_port + var/obj/item/weapon/tank/holding + + var/volume = 0 + var/destroyed = 0 + + var/start_pressure = ONE_ATMOSPHERE + var/maximum_pressure = 90 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/New() + ..() + //VOREStation Edit - Fix runtime + if(air_contents) + air_contents.volume = volume + air_contents.temperature = T20C + //VOREStation Edit End + + return 1 + +/obj/machinery/portable_atmospherics/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/portable_atmospherics/LateInitialize() + var/obj/machinery/atmospherics/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + +/obj/machinery/portable_atmospherics/Destroy() + QDEL_NULL(air_contents) + QDEL_NULL(holding) + return ..() + +/obj/machinery/portable_atmospherics/process() + if(!connected_port) //only react when pipe_network will ont it do it for you + //Allow for reactions + air_contents.react() + else + update_icon() + +/obj/machinery/portable_atmospherics/blob_act() + qdel(src) + +/obj/machinery/portable_atmospherics/proc/StandardAirMix() + return list( + "oxygen" = O2STANDARD * MolesForPressure(), + "nitrogen" = N2STANDARD * MolesForPressure()) + +/obj/machinery/portable_atmospherics/proc/MolesForPressure(var/target_pressure = start_pressure) + return (target_pressure * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + +/obj/machinery/portable_atmospherics/update_icon() + return null + +/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + connected_port.on = 1 //Activate port updates + + anchored = TRUE //Prevent movement + + //Actually enforce the air sharing + var/datum/pipe_network/network = connected_port.return_network(src) + if(network && !network.gases.Find(air_contents)) + network.gases += air_contents + network.update = 1 + + return 1 + +/obj/machinery/portable_atmospherics/proc/disconnect() + if(!connected_port) + return 0 + + var/datum/pipe_network/network = connected_port.return_network(src) + if(network) + network.gases -= air_contents + + anchored = FALSE + + connected_port.connected_device = null + connected_port = null + + return 1 + +/obj/machinery/portable_atmospherics/proc/update_connected_network() + if(!connected_port) + return + + var/datum/pipe_network/network = connected_port.return_network(src) + if (network) + network.update = 1 + +/obj/machinery/portable_atmospherics/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if ((istype(W, /obj/item/weapon/tank) && !( src.destroyed ))) + if (src.holding) + return + var/obj/item/weapon/tank/T = W + user.drop_item() + T.loc = src + src.holding = T + update_icon() + return + + else if (W.has_tool_quality(TOOL_WRENCH)) + if(connected_port) + disconnect() + to_chat(user, "You disconnect \the [src] from the port.") + update_icon() + playsound(src, W.usesound, 50, 1) + return + else + var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector/) in loc + if(possible_port) + if(connect(possible_port)) + to_chat(user, "You connect \the [src] to the port.") + update_icon() + playsound(src, W.usesound, 50, 1) + return + else + to_chat(user, "\The [src] failed to connect to the port.") + return + else + to_chat(user, "Nothing happens.") + return + return + + + +/obj/machinery/portable_atmospherics/powered + var/power_rating + var/power_losses + var/last_power_draw = 0 + var/obj/item/weapon/cell/cell + var/use_cell = TRUE + var/removeable_cell = TRUE + +/obj/machinery/portable_atmospherics/powered/powered() + if(use_power) //using area power + return ..() + if(cell && cell.charge) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/powered/attackby(obj/item/I, mob/user) + if(use_cell && istype(I, /obj/item/weapon/cell)) + if(cell) + to_chat(user, "There is already a power cell installed.") + return + + var/obj/item/weapon/cell/C = I + + user.drop_item() + C.add_fingerprint(user) + cell = C + C.loc = src + user.visible_message("[user] opens the panel on [src] and inserts [C].", "You open the panel on [src] and insert [C].") + power_change() + return + + if(I.has_tool_quality(TOOL_SCREWDRIVER) && removeable_cell) + if(!cell) + to_chat(user, "There is no power cell installed.") + return + + user.visible_message("[user] opens the panel on [src] and removes [cell].", "You open the panel on [src] and remove [cell].") + playsound(src, I.usesound, 50, 1) + cell.add_fingerprint(user) + cell.loc = src.loc + cell = null + power_change() + return + ..() + +/obj/machinery/portable_atmospherics/proc/log_open() + if(air_contents.gas.len == 0) + return + + var/gases = "" + for(var/gas in air_contents.gas) + if(gases) + gases += ", [gas]" + else + gases = gas + log_admin("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") + message_admins("[usr] ([usr.ckey]) opened '[src.name]' containing [gases].") diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 1a502c675f9..0f0d2b3fe81 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,191 +1,191 @@ -/obj/machinery/portable_atmospherics/powered/pump - name = "portable air pump" - - icon = 'icons/obj/atmos.dmi' - icon_state = "psiphon:0" - density = TRUE - w_class = ITEMSIZE_NORMAL - - var/on = 0 - var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = ONE_ATMOSPHERE - - var/pressuremin = 0 - var/pressuremax = 10 * ONE_ATMOSPHERE - - volume = 1000 - - power_rating = 7500 //7500 W ~ 10 HP - power_losses = 150 - -/obj/machinery/portable_atmospherics/powered/pump/filled - start_pressure = 90 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/powered/pump/New() - ..() - cell = new/obj/item/weapon/cell/apc(src) - - var/list/air_mix = StandardAirMix() - src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) - -/obj/machinery/portable_atmospherics/powered/pump/update_icon() - cut_overlays() - - if(on && cell && cell.charge) - icon_state = "psiphon:1" - else - icon_state = "psiphon:0" - - if(holding) - add_overlay("siphon-open") - - if(connected_port) - add_overlay("siphon-connector") - - return - -/obj/machinery/portable_atmospherics/powered/pump/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - - if(prob(100/severity)) - direction_out = !direction_out - - target_pressure = rand(0,1300) - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/powered/pump/process() - ..() - var/power_draw = -1 - - if(on && cell && cell.charge) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/pressure_delta - var/output_volume - var/air_temperature - if(direction_out) - pressure_delta = target_pressure - environment.return_pressure() - output_volume = environment.volume * environment.group_multiplier - air_temperature = environment.temperature? environment.temperature : air_contents.temperature - else - pressure_delta = environment.return_pressure() - target_pressure - output_volume = air_contents.volume * air_contents.group_multiplier - air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature - - var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) - - if (pressure_delta > 0.01) - if (direction_out) - power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) - else - power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) - - if (power_draw < 0) - last_flow_rate = 0 - last_power_draw = 0 - else - power_draw = max(power_draw, power_losses) - cell.use(power_draw * CELLRATE) - last_power_draw = power_draw - - update_connected_network() - - //ran out of charge - if (!cell.charge) - power_change() - update_icon() - - src.updateDialog() - -/obj/machinery/portable_atmospherics/powered/pump/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/pump/attack_ghost(var/mob/user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user) - tgui_interact(user) - -/obj/machinery/portable_atmospherics/powered/pump/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PortablePump", name) - ui.open() - - -/obj/machinery/portable_atmospherics/powered/pump/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/machinery/portable_atmospherics/powered/pump/tgui_data(mob/user) - var/list/data[0] - data["on"] = on ? TRUE : FALSE - data["direction"] = !direction_out ? TRUE : FALSE - data["connected"] = connected_port ? TRUE : FALSE - data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["target_pressure"] = round(target_pressure ? target_pressure : 0) - data["default_pressure"] = round(initial(target_pressure)) - data["min_pressure"] = round(pressuremin) - data["max_pressure"] = round(pressuremax) - - data["powerDraw"] = round(last_power_draw) - data["cellCharge"] = cell ? cell.charge : 0 - data["cellMaxCharge"] = cell ? cell.maxcharge : 1 - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) - else - data["holding"] = null - - return data - -/obj/machinery/portable_atmospherics/powered/pump/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - on = !on - . = 1 - if("direction") - direction_out = !direction_out - . = 1 - if("eject") - if(holding) - holding.loc = loc - holding = null - . = 1 - if("pressure") - var/pressure = params["pressure"] - if(pressure == "reset") - pressure = initial(target_pressure) - . = TRUE - else if(pressure == "min") - pressure = pressuremin - . = TRUE - else if(pressure == "max") - pressure = pressuremax - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = clamp(round(pressure), pressuremin, pressuremax) - - update_icon() +/obj/machinery/portable_atmospherics/powered/pump + name = "portable air pump" + + icon = 'icons/obj/atmos.dmi' + icon_state = "psiphon:0" + density = TRUE + w_class = ITEMSIZE_NORMAL + + var/on = 0 + var/direction_out = 0 //0 = siphoning, 1 = releasing + var/target_pressure = ONE_ATMOSPHERE + + var/pressuremin = 0 + var/pressuremax = 10 * ONE_ATMOSPHERE + + volume = 1000 + + power_rating = 7500 //7500 W ~ 10 HP + power_losses = 150 + +/obj/machinery/portable_atmospherics/powered/pump/filled + start_pressure = 90 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/powered/pump/New() + ..() + cell = new/obj/item/weapon/cell/apc(src) + + var/list/air_mix = StandardAirMix() + src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) + +/obj/machinery/portable_atmospherics/powered/pump/update_icon() + cut_overlays() + + if(on && cell && cell.charge) + icon_state = "psiphon:1" + else + icon_state = "psiphon:0" + + if(holding) + add_overlay("siphon-open") + + if(connected_port) + add_overlay("siphon-connector") + + return + +/obj/machinery/portable_atmospherics/powered/pump/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + + if(prob(100/severity)) + direction_out = !direction_out + + target_pressure = rand(0,1300) + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/powered/pump/process() + ..() + var/power_draw = -1 + + if(on && cell && cell.charge) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/pressure_delta + var/output_volume + var/air_temperature + if(direction_out) + pressure_delta = target_pressure - environment.return_pressure() + output_volume = environment.volume * environment.group_multiplier + air_temperature = environment.temperature? environment.temperature : air_contents.temperature + else + pressure_delta = environment.return_pressure() - target_pressure + output_volume = air_contents.volume * air_contents.group_multiplier + air_temperature = air_contents.temperature? air_contents.temperature : environment.temperature + + var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION) + + if (pressure_delta > 0.01) + if (direction_out) + power_draw = pump_gas(src, air_contents, environment, transfer_moles, power_rating) + else + power_draw = pump_gas(src, environment, air_contents, transfer_moles, power_rating) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + power_draw = max(power_draw, power_losses) + cell.use(power_draw * CELLRATE) + last_power_draw = power_draw + + update_connected_network() + + //ran out of charge + if (!cell.charge) + power_change() + update_icon() + + src.updateDialog() + +/obj/machinery/portable_atmospherics/powered/pump/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/pump/attack_ghost(var/mob/user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user) + tgui_interact(user) + +/obj/machinery/portable_atmospherics/powered/pump/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortablePump", name) + ui.open() + + +/obj/machinery/portable_atmospherics/powered/pump/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/machinery/portable_atmospherics/powered/pump/tgui_data(mob/user) + var/list/data[0] + data["on"] = on ? TRUE : FALSE + data["direction"] = !direction_out ? TRUE : FALSE + data["connected"] = connected_port ? TRUE : FALSE + data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["target_pressure"] = round(target_pressure ? target_pressure : 0) + data["default_pressure"] = round(initial(target_pressure)) + data["min_pressure"] = round(pressuremin) + data["max_pressure"] = round(pressuremax) + + data["powerDraw"] = round(last_power_draw) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) + else + data["holding"] = null + + return data + +/obj/machinery/portable_atmospherics/powered/pump/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + on = !on + . = 1 + if("direction") + direction_out = !direction_out + . = 1 + if("eject") + if(holding) + holding.loc = loc + holding = null + . = 1 + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = initial(target_pressure) + . = TRUE + else if(pressure == "min") + pressure = pressuremin + . = TRUE + else if(pressure == "max") + pressure = pressuremax + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = clamp(round(pressure), pressuremin, pressuremax) + + update_icon() diff --git a/code/game/machinery/atmoalter/pump_vr.dm b/code/game/machinery/atmoalter/pump_vr.dm index 2c3b5c14d9b..fb4acbe9f63 100644 --- a/code/game/machinery/atmoalter/pump_vr.dm +++ b/code/game/machinery/atmoalter/pump_vr.dm @@ -83,7 +83,7 @@ update_connected_network() /obj/machinery/portable_atmospherics/powered/pump/huge/attackby(var/obj/item/I, var/mob/user) - if(I.is_wrench()) + if(I.has_tool_quality(TOOL_WRENCH)) if(on) to_chat(user, "Turn \the [src] off first!") return @@ -97,7 +97,7 @@ //doesn't use power cells if(istype(I, /obj/item/weapon/cell)) return - if (I.is_screwdriver()) + if (I.has_tool_quality(TOOL_SCREWDRIVER)) return //doesn't hold tanks @@ -111,7 +111,7 @@ name = "Stationary Air Pump" /obj/machinery/portable_atmospherics/powered/pump/huge/stationary/attackby(var/obj/item/I, var/mob/user) - if(I.is_wrench()) + if(I.has_tool_quality(TOOL_WRENCH)) to_chat(user, "The bolts are too tight for you to unscrew!") return diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 7ab58e2c88d..7b2d60e7b5f 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,257 +1,257 @@ -/obj/machinery/portable_atmospherics/powered/scrubber - name = "Portable Air Scrubber" - desc = "Similar to room scrubbers, this device contains an internal tank to scrub gasses from the atmosphere." - - icon = 'icons/obj/atmos.dmi' - icon_state = "pscrubber:0" - density = TRUE - w_class = ITEMSIZE_NORMAL - - var/on = 0 - var/volume_rate = 800 - - volume = 750 - - power_rating = 7500 //7500 W ~ 10 HP - power_losses = 150 - - var/minrate = 0 - var/maxrate = 10 * ONE_ATMOSPHERE - - var/list/scrubbing_gas = list("phoron", "carbon_dioxide", "nitrous_oxide", "volatile_fuel") - -/obj/machinery/portable_atmospherics/powered/scrubber/New() - ..() - cell = new/obj/item/weapon/cell/apc(src) - -/obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/powered/scrubber/update_icon() - cut_overlays() - - if(on && cell && cell.charge) - icon_state = "pscrubber:1" - else - icon_state = "pscrubber:0" - - if(holding) - add_overlay("scrubber-open") - - if(connected_port) - add_overlay("scrubber-connector") - - return - -/obj/machinery/portable_atmospherics/powered/scrubber/process() - ..() - - var/power_draw = -1 - - if(on && cell && cell.charge) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles - - power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) - - if (power_draw < 0) - last_flow_rate = 0 - last_power_draw = 0 - else - power_draw = max(power_draw, power_losses) - cell.use(power_draw * CELLRATE) - last_power_draw = power_draw - - update_connected_network() - - //ran out of charge - if (!cell.charge) - power_change() - update_icon() - - //src.update_icon() - src.updateDialog() - -/obj/machinery/portable_atmospherics/powered/scrubber/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/scrubber/attack_ghost(var/mob/user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user) - tgui_interact(user) - -/obj/machinery/portable_atmospherics/powered/scrubber/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PortableScrubber", name) - ui.open() - -/obj/machinery/portable_atmospherics/powered/scrubber/tgui_data(mob/user) - var/list/data = list() - data["on"] = on ? 1 : 0 - data["connected"] = connected_port ? 1 : 0 - data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - - data["rate"] = round(volume_rate) - data["minrate"] = round(minrate) - data["maxrate"] = round(maxrate) - data["powerDraw"] = round(last_power_draw) - data["cellCharge"] = cell ? cell.charge : 0 - data["cellMaxCharge"] = cell ? cell.maxcharge : 1 - - if(holding) - data["holding"] = list() - data["holding"]["name"] = holding.name - data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) - else - data["holding"] = null - - return data - -/obj/machinery/portable_atmospherics/powered/scrubber/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("power") - on = !on - . = TRUE - if("eject") - if(holding) - holding.loc = loc - holding = null - . = TRUE - if("volume_adj") - volume_rate = CLAMP(text2num(params["vol"]), minrate, maxrate) - . = TRUE - - update_icon() - - -//Huge scrubber -/obj/machinery/portable_atmospherics/powered/scrubber/huge - name = "Huge Air Scrubber" - desc = "A larger variation of the portable scrubber, for industrial scrubbing of air. Must be turned on from a remote terminal." - icon = 'icons/obj/atmos_vr.dmi' //VOREStation Edit - New Sprite - icon_state = "scrubber:0" - anchored = TRUE - volume = 500000 - volume_rate = 7000 - - use_power = USE_POWER_IDLE - idle_power_usage = 50 //VOREStation Edit //internal circuitry, friction losses and stuff - active_power_usage = 1000 //VOREStation Edit // Blowers running - power_rating = 100000 //VOREStation Add //100 kW ~ 135 HP - - var/global/gid = 1 - var/id = 0 - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/New() - ..() - cell = null - - id = gid - gid++ - - name = "[name] (ID [id])" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) - to_chat(user, "You can't directly interact with this machine. Use the scrubber control console.") - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/update_icon() - src.overlays = 0 - - if(on && !(stat & (NOPOWER|BROKEN))) - icon_state = "scrubber:1" - else - icon_state = "scrubber:0" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/power_change() - var/old_stat = stat - ..() - if (old_stat != stat) - update_icon() - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/process() - if(!anchored || (stat & (NOPOWER|BROKEN))) - on = 0 - last_flow_rate = 0 - last_power_draw = 0 - update_icon() - var/new_use_power = 1 + on - if(new_use_power != use_power) - update_use_power(new_use_power) - if(!on) - return - - var/power_draw = -1 - - var/datum/gas_mixture/environment = loc.return_air() - - var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles - - power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, active_power_usage) - - if (power_draw < 0) - last_flow_rate = 0 - last_power_draw = 0 - else - use_power(power_draw) - update_connected_network() - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(I.is_wrench()) - if(on) - to_chat(user, "Turn \the [src] off first!") - return - - anchored = !anchored - playsound(src, I.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - - return - - //doesn't use power cells - if(istype(I, /obj/item/weapon/cell)) - return - if(I.is_screwdriver()) - return - - //doesn't hold tanks - if(istype(I, /obj/item/weapon/tank)) - return - - ..() - - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary - name = "Stationary Air Scrubber" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/Initialize() - . = ..() - desc += "This one seems to be tightly secured with large bolts." - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(I.is_wrench()) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - - ..() +/obj/machinery/portable_atmospherics/powered/scrubber + name = "Portable Air Scrubber" + desc = "Similar to room scrubbers, this device contains an internal tank to scrub gasses from the atmosphere." + + icon = 'icons/obj/atmos.dmi' + icon_state = "pscrubber:0" + density = TRUE + w_class = ITEMSIZE_NORMAL + + var/on = 0 + var/volume_rate = 800 + + volume = 750 + + power_rating = 7500 //7500 W ~ 10 HP + power_losses = 150 + + var/minrate = 0 + var/maxrate = 10 * ONE_ATMOSPHERE + + var/list/scrubbing_gas = list("phoron", "carbon_dioxide", "nitrous_oxide", "volatile_fuel") + +/obj/machinery/portable_atmospherics/powered/scrubber/New() + ..() + cell = new/obj/item/weapon/cell/apc(src) + +/obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/powered/scrubber/update_icon() + cut_overlays() + + if(on && cell && cell.charge) + icon_state = "pscrubber:1" + else + icon_state = "pscrubber:0" + + if(holding) + add_overlay("scrubber-open") + + if(connected_port) + add_overlay("scrubber-connector") + + return + +/obj/machinery/portable_atmospherics/powered/scrubber/process() + ..() + + var/power_draw = -1 + + if(on && cell && cell.charge) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, power_rating) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + power_draw = max(power_draw, power_losses) + cell.use(power_draw * CELLRATE) + last_power_draw = power_draw + + update_connected_network() + + //ran out of charge + if (!cell.charge) + power_change() + update_icon() + + //src.update_icon() + src.updateDialog() + +/obj/machinery/portable_atmospherics/powered/scrubber/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ghost(var/mob/user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user) + tgui_interact(user) + +/obj/machinery/portable_atmospherics/powered/scrubber/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableScrubber", name) + ui.open() + +/obj/machinery/portable_atmospherics/powered/scrubber/tgui_data(mob/user) + var/list/data = list() + data["on"] = on ? 1 : 0 + data["connected"] = connected_port ? 1 : 0 + data["pressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + + data["rate"] = round(volume_rate) + data["minrate"] = round(minrate) + data["maxrate"] = round(maxrate) + data["powerDraw"] = round(last_power_draw) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 + + if(holding) + data["holding"] = list() + data["holding"]["name"] = holding.name + data["holding"]["pressure"] = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0) + else + data["holding"] = null + + return data + +/obj/machinery/portable_atmospherics/powered/scrubber/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("power") + on = !on + . = TRUE + if("eject") + if(holding) + holding.loc = loc + holding = null + . = TRUE + if("volume_adj") + volume_rate = CLAMP(text2num(params["vol"]), minrate, maxrate) + . = TRUE + + update_icon() + + +//Huge scrubber +/obj/machinery/portable_atmospherics/powered/scrubber/huge + name = "Huge Air Scrubber" + desc = "A larger variation of the portable scrubber, for industrial scrubbing of air. Must be turned on from a remote terminal." + icon = 'icons/obj/atmos_vr.dmi' //VOREStation Edit - New Sprite + icon_state = "scrubber:0" + anchored = TRUE + volume = 500000 + volume_rate = 7000 + + use_power = USE_POWER_IDLE + idle_power_usage = 50 //VOREStation Edit //internal circuitry, friction losses and stuff + active_power_usage = 1000 //VOREStation Edit // Blowers running + power_rating = 100000 //VOREStation Add //100 kW ~ 135 HP + + var/global/gid = 1 + var/id = 0 + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/New() + ..() + cell = null + + id = gid + gid++ + + name = "[name] (ID [id])" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) + to_chat(user, "You can't directly interact with this machine. Use the scrubber control console.") + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/update_icon() + src.overlays = 0 + + if(on && !(stat & (NOPOWER|BROKEN))) + icon_state = "scrubber:1" + else + icon_state = "scrubber:0" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/power_change() + var/old_stat = stat + ..() + if (old_stat != stat) + update_icon() + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/process() + if(!anchored || (stat & (NOPOWER|BROKEN))) + on = 0 + last_flow_rate = 0 + last_power_draw = 0 + update_icon() + var/new_use_power = 1 + on + if(new_use_power != use_power) + update_use_power(new_use_power) + if(!on) + return + + var/power_draw = -1 + + var/datum/gas_mixture/environment = loc.return_air() + + var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles + + power_draw = scrub_gas(src, scrubbing_gas, environment, air_contents, transfer_moles, active_power_usage) + + if (power_draw < 0) + last_flow_rate = 0 + last_power_draw = 0 + else + use_power(power_draw) + update_connected_network() + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(I.has_tool_quality(TOOL_WRENCH)) + if(on) + to_chat(user, "Turn \the [src] off first!") + return + + anchored = !anchored + playsound(src, I.usesound, 50, 1) + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") + + return + + //doesn't use power cells + if(istype(I, /obj/item/weapon/cell)) + return + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + return + + //doesn't hold tanks + if(istype(I, /obj/item/weapon/tank)) + return + + ..() + + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary + name = "Stationary Air Scrubber" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/Initialize() + . = ..() + desc += "This one seems to be tightly secured with large bolts." + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(I.has_tool_quality(TOOL_WRENCH)) + to_chat(user, "The bolts are too tight for you to unscrew!") + return + + ..() diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm index d4f2ccfc027..453b1ec5e07 100644 --- a/code/game/machinery/atmoalter/zvent.dm +++ b/code/game/machinery/atmoalter/zvent.dm @@ -1,29 +1,29 @@ -/obj/machinery/zvent - name = "Interfloor Air Transfer System" - - icon = 'icons/obj/pipes.dmi' - icon_state = "vent-db" - density = FALSE - anchored=TRUE - - var/on = 0 - var/volume_rate = 800 - -/obj/machinery/zvent/process() - - //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. - if (istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it - for (var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented - var/turf/simulated/zturf_conn = locate(x,y,z+new_z) - if (istype(zturf_conn)) - var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn - if (istype(zvent_conn)) - //both floors have simulated turfs, share() - var/turf/simulated/myturf = loc - var/datum/gas_mixture/conn_air = zturf_conn.zone.air //TODO: pop culture reference - var/datum/gas_mixture/my_air = myturf.air - if (istype(conn_air) && istype(my_air)) -// if (!my_air.compare(conn_air)) -// myturf.reset_delay() -// zturf_conn.reset_delay() - my_air.share(conn_air) +/obj/machinery/zvent + name = "Interfloor Air Transfer System" + + icon = 'icons/obj/pipes.dmi' + icon_state = "vent-db" + density = FALSE + anchored=TRUE + + var/on = 0 + var/volume_rate = 800 + +/obj/machinery/zvent/process() + + //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. + if (istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it + for (var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented + var/turf/simulated/zturf_conn = locate(x,y,z+new_z) + if (istype(zturf_conn)) + var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn + if (istype(zvent_conn)) + //both floors have simulated turfs, share() + var/turf/simulated/myturf = loc + var/datum/gas_mixture/conn_air = zturf_conn.zone.air //TODO: pop culture reference + var/datum/gas_mixture/my_air = myturf.air + if (istype(conn_air) && istype(my_air)) +// if (!my_air.compare(conn_air)) +// myturf.reset_delay() +// zturf_conn.reset_delay() + my_air.share(conn_air) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 236f6d68374..c484a74f7d1 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,285 +1,285 @@ -/obj/machinery/autolathe - name = "autolathe" - desc = "It produces items using metal and glass." - icon_state = "autolathe" - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - active_power_usage = 2000 - clicksound = "keyboard" - clickvol = 30 - - circuit = /obj/item/weapon/circuitboard/autolathe - - var/static/datum/category_collection/autolathe/autolathe_recipes - - var/hacked = 0 - var/disabled = 0 - var/shocked = 0 - var/busy = 0 - - var/mat_efficiency = 1 - var/build_time = 50 - - var/datum/wires/autolathe/wires = null - - var/mb_rating = 0 - var/man_rating = 0 - - var/filtertext - -/obj/machinery/autolathe/Initialize() - AddComponent(/datum/component/material_container, subtypesof(/datum/material), 0, MATCONTAINER_EXAMINE, _after_insert = CALLBACK(src, PROC_REF(AfterMaterialInsert))) - . = ..() - if(!autolathe_recipes) - autolathe_recipes = new() - wires = new(src) - - default_apply_parts() - RefreshParts() - -/obj/machinery/autolathe/Destroy() - qdel(wires) - wires = null - return ..() - -/obj/machinery/autolathe/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Autolathe", name) - ui.open() - -/obj/machinery/autolathe/tgui_status(mob/user) - if(disabled) - return STATUS_CLOSE - return ..() - -/obj/machinery/autolathe/tgui_static_data(mob/user) - var/list/data = ..() - - var/list/categories = list() - var/list/recipes = list() - for(var/datum/category_group/autolathe/A in autolathe_recipes.categories) - categories += A.name - for(var/datum/category_item/autolathe/M in A.items) - if(M.hidden && !hacked) - continue - if(M.man_rating > man_rating) - continue - recipes.Add(list(list( - "category" = A.name, - "name" = M.name, - "ref" = REF(M), - "requirements" = M.resources, - "hidden" = M.hidden, - "coeff_applies" = !M.no_scale, - "is_stack" = M.is_stack, - ))) - data["recipes"] = recipes - data["categories"] = categories - - return data - -/obj/machinery/autolathe/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/spritesheet/sheetmaterials) - ) - -/obj/machinery/autolathe/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - data["busy"] = busy - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - data["materials"] = materials.tgui_data() - data["mat_efficiency"] = mat_efficiency - return data - -/obj/machinery/autolathe/interact(mob/user) - if(panel_open) - return wires.Interact(user) - - if(disabled) - to_chat(user, "\The [src] is disabled!") - return - - if(shocked) - shock(user, 50) - - tgui_interact(user) - -/obj/machinery/autolathe/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(busy) - to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") - return - - if(default_deconstruction_screwdriver(user, O)) - interact(user) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - - if(stat) - return - - if(panel_open) - //Don't eat multitools or wirecutters used on an open lathe. - if(O.is_multitool() || O.is_wirecutter()) - wires.Interact(user) - return - - if(is_robot_module(O)) - return 0 - - if(istype(O,/obj/item/ammo_magazine/clip) || istype(O,/obj/item/ammo_magazine/s357) || istype(O,/obj/item/ammo_magazine/s38) || istype (O,/obj/item/ammo_magazine/s44)/* VOREstation Edit*/) // Prevents ammo recycling exploit with speedloaders. - to_chat(user, "\The [O] is too hazardous to recycle with the autolathe!") - return - - return ..() - -/obj/machinery/autolathe/attack_hand(mob/user as mob) - user.set_machine(src) - interact(user) - -/obj/machinery/autolathe/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - usr.set_machine(src) - add_fingerprint(usr) - - if(busy) - to_chat(usr, "The autolathe is busy. Please wait for completion of previous operation.") - return - switch(action) - if("make") - var/datum/category_item/autolathe/making = locate(params["make"]) - if(!istype(making)) - return - if(making.hidden && !hacked) - return - - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - - var/list/materials_used = list() - - var/multiplier = (params["multiplier"] || 1) - - if(making.is_stack) - var/max_sheets - for(var/material in making.resources) - var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient - var/sheets = round(materials.get_material_amount(material) / round(making.resources[material] * coeff)) - if(isnull(max_sheets) || max_sheets > sheets) - max_sheets = sheets - if(!isnull(materials.get_material_amount(material)) && materials.get_material_amount(material) < round(making.resources[material] * coeff)) - max_sheets = 0 - //Build list of multipliers for sheets. - multiplier = tgui_input_number(usr, "How many do you want to print? (0-[max_sheets])", null, null, max_sheets, 0) - if(!multiplier || multiplier <= 0 || (multiplier != round(multiplier)) || multiplier > max_sheets || tgui_status(usr, state) != STATUS_INTERACTIVE) - return FALSE - - //Check if we still have the materials. - var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient - - for(var/datum/material/used_material as anything in making.resources) - var/amount_needed = making.resources[used_material] * coeff * multiplier - materials_used[used_material] = amount_needed - - if(LAZYLEN(materials_used)) - if(!materials.has_materials(materials_used)) - return - - materials.use_materials(materials_used) - - busy = making.name - update_use_power(USE_POWER_ACTIVE) - - update_icon() // So lid closes - - sleep(build_time) - - busy = 0 - update_use_power(USE_POWER_IDLE) - update_icon() // So lid opens - - //Sanity check. - if(!making || !src) - return - - //Create the desired item. - var/obj/item/I = new making.path(src.loc) - - if(LAZYLEN(I.matter)) // Sadly we must obey the laws of equivalent exchange. - I.matter.Cut() - else - I.matter = list() - - for(var/material in making.resources) // Handle the datum's autoscaling for waste, so we're properly wasting material, but not so much if we have efficiency. - I.matter[material] = round(making.resources[material] / (making.no_scale ? 1 : 1.25)) * (making.no_scale ? 1 : mat_efficiency) - - flick("[initial(icon_state)]_finish", src) - if(multiplier > 1) - if(istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - S.set_amount(multiplier) - else - for(multiplier; multiplier > 1; --multiplier) // Create multiple items if it's not a stack. - I = new making.path(src.loc) - // We've already deducted the cost of multiple items. Process the matter the same. - if(LAZYLEN(I.matter)) - I.matter.Cut() - - else - I.matter = list() - - for(var/material in making.resources) - I.matter[material] = round(making.resources[material] / (making.no_scale ? 1 : 1.25)) * (making.no_scale ? 1 : mat_efficiency) - return TRUE - return FALSE - -/obj/machinery/autolathe/update_icon() - cut_overlays() - - icon_state = initial(icon_state) - - if(panel_open) - add_overlay("[icon_state]_panel") - if(stat & NOPOWER) - return - if(busy) - icon_state = "[icon_state]_work" - -//Updates overall lathe storage size. -/obj/machinery/autolathe/RefreshParts() - ..() - mb_rating = 0 - man_rating = 0 - for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts) - mb_rating += MB.rating - for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) - man_rating += M.rating - - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.max_amount = mb_rating * 75000 - - build_time = 50 / man_rating - mat_efficiency = 1.1 - man_rating * 0.1// Normally, price is 1.25 the amount of material, so this shouldn't go higher than 0.6. Maximum rating of parts is 5 - update_tgui_static_data(usr) - -/obj/machinery/autolathe/dismantle() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.retrieve_all() - return ..() - -/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted) - flick("autolathe_loading", src)//plays metal insertion animation - // use_power(min(1000, amount_inserted / 100)) - SStgui.update_uis(src) - -/obj/machinery/autolathe/examine(mob/user) - . = ..() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Storing up to [materials.max_amount] material units.
    Material consumption at [mat_efficiency*100]%.
    " +/obj/machinery/autolathe + name = "autolathe" + desc = "It produces items using metal and glass." + icon_state = "autolathe" + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + active_power_usage = 2000 + clicksound = "keyboard" + clickvol = 30 + + circuit = /obj/item/weapon/circuitboard/autolathe + + var/static/datum/category_collection/autolathe/autolathe_recipes + + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/busy = 0 + + var/mat_efficiency = 1 + var/build_time = 50 + + var/datum/wires/autolathe/wires = null + + var/mb_rating = 0 + var/man_rating = 0 + + var/filtertext + +/obj/machinery/autolathe/Initialize() + AddComponent(/datum/component/material_container, subtypesof(/datum/material), 0, MATCONTAINER_EXAMINE, _after_insert = CALLBACK(src, PROC_REF(AfterMaterialInsert))) + . = ..() + if(!autolathe_recipes) + autolathe_recipes = new() + wires = new(src) + + default_apply_parts() + RefreshParts() + +/obj/machinery/autolathe/Destroy() + qdel(wires) + wires = null + return ..() + +/obj/machinery/autolathe/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Autolathe", name) + ui.open() + +/obj/machinery/autolathe/tgui_status(mob/user) + if(disabled) + return STATUS_CLOSE + return ..() + +/obj/machinery/autolathe/tgui_static_data(mob/user) + var/list/data = ..() + + var/list/categories = list() + var/list/recipes = list() + for(var/datum/category_group/autolathe/A in autolathe_recipes.categories) + categories += A.name + for(var/datum/category_item/autolathe/M in A.items) + if(M.hidden && !hacked) + continue + if(M.man_rating > man_rating) + continue + recipes.Add(list(list( + "category" = A.name, + "name" = M.name, + "ref" = REF(M), + "requirements" = M.resources, + "hidden" = M.hidden, + "coeff_applies" = !M.no_scale, + "is_stack" = M.is_stack, + ))) + data["recipes"] = recipes + data["categories"] = categories + + return data + +/obj/machinery/autolathe/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/sheetmaterials) + ) + +/obj/machinery/autolathe/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["busy"] = busy + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + data["materials"] = materials.tgui_data() + data["mat_efficiency"] = mat_efficiency + return data + +/obj/machinery/autolathe/interact(mob/user) + if(panel_open) + return wires.Interact(user) + + if(disabled) + to_chat(user, "\The [src] is disabled!") + return + + if(shocked) + shock(user, 50) + + tgui_interact(user) + +/obj/machinery/autolathe/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(busy) + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") + return + + if(default_deconstruction_screwdriver(user, O)) + interact(user) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + if(stat) + return + + if(panel_open) + //Don't eat multitools or wirecutters used on an open lathe. + if(O.has_tool_quality(TOOL_MULTITOOL) || O.has_tool_quality(TOOL_WIRECUTTER)) + wires.Interact(user) + return + + if(is_robot_module(O)) + return 0 + + if(istype(O,/obj/item/ammo_magazine/clip) || istype(O,/obj/item/ammo_magazine/s357) || istype(O,/obj/item/ammo_magazine/s38) || istype (O,/obj/item/ammo_magazine/s44)/* VOREstation Edit*/) // Prevents ammo recycling exploit with speedloaders. + to_chat(user, "\The [O] is too hazardous to recycle with the autolathe!") + return + + return ..() + +/obj/machinery/autolathe/attack_hand(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/machinery/autolathe/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + usr.set_machine(src) + add_fingerprint(usr) + + if(busy) + to_chat(usr, "The autolathe is busy. Please wait for completion of previous operation.") + return + switch(action) + if("make") + var/datum/category_item/autolathe/making = locate(params["make"]) + if(!istype(making)) + return + if(making.hidden && !hacked) + return + + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + + var/list/materials_used = list() + + var/multiplier = (params["multiplier"] || 1) + + if(making.is_stack) + var/max_sheets + for(var/material in making.resources) + var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient + var/sheets = round(materials.get_material_amount(material) / round(making.resources[material] * coeff)) + if(isnull(max_sheets) || max_sheets > sheets) + max_sheets = sheets + if(!isnull(materials.get_material_amount(material)) && materials.get_material_amount(material) < round(making.resources[material] * coeff)) + max_sheets = 0 + //Build list of multipliers for sheets. + multiplier = tgui_input_number(usr, "How many do you want to print? (0-[max_sheets])", null, null, max_sheets, 0) + if(!multiplier || multiplier <= 0 || (multiplier != round(multiplier)) || multiplier > max_sheets || tgui_status(usr, state) != STATUS_INTERACTIVE) + return FALSE + + //Check if we still have the materials. + var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient + + for(var/datum/material/used_material as anything in making.resources) + var/amount_needed = making.resources[used_material] * coeff * multiplier + materials_used[used_material] = amount_needed + + if(LAZYLEN(materials_used)) + if(!materials.has_materials(materials_used)) + return + + materials.use_materials(materials_used) + + busy = making.name + update_use_power(USE_POWER_ACTIVE) + + update_icon() // So lid closes + + sleep(build_time) + + busy = 0 + update_use_power(USE_POWER_IDLE) + update_icon() // So lid opens + + //Sanity check. + if(!making || !src) + return + + //Create the desired item. + var/obj/item/I = new making.path(src.loc) + + if(LAZYLEN(I.matter)) // Sadly we must obey the laws of equivalent exchange. + I.matter.Cut() + else + I.matter = list() + + for(var/material in making.resources) // Handle the datum's autoscaling for waste, so we're properly wasting material, but not so much if we have efficiency. + I.matter[material] = round(making.resources[material] / (making.no_scale ? 1 : 1.25)) * (making.no_scale ? 1 : mat_efficiency) + + flick("[initial(icon_state)]_finish", src) + if(multiplier > 1) + if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + S.set_amount(multiplier) + else + for(multiplier; multiplier > 1; --multiplier) // Create multiple items if it's not a stack. + I = new making.path(src.loc) + // We've already deducted the cost of multiple items. Process the matter the same. + if(LAZYLEN(I.matter)) + I.matter.Cut() + + else + I.matter = list() + + for(var/material in making.resources) + I.matter[material] = round(making.resources[material] / (making.no_scale ? 1 : 1.25)) * (making.no_scale ? 1 : mat_efficiency) + return TRUE + return FALSE + +/obj/machinery/autolathe/update_icon() + cut_overlays() + + icon_state = initial(icon_state) + + if(panel_open) + add_overlay("[icon_state]_panel") + if(stat & NOPOWER) + return + if(busy) + icon_state = "[icon_state]_work" + +//Updates overall lathe storage size. +/obj/machinery/autolathe/RefreshParts() + ..() + mb_rating = 0 + man_rating = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts) + mb_rating += MB.rating + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + man_rating += M.rating + + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + materials.max_amount = mb_rating * 75000 + + build_time = 50 / man_rating + mat_efficiency = 1.1 - man_rating * 0.1// Normally, price is 1.25 the amount of material, so this shouldn't go higher than 0.6. Maximum rating of parts is 5 + update_tgui_static_data(usr) + +/obj/machinery/autolathe/dismantle() + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + materials.retrieve_all() + return ..() + +/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted) + flick("autolathe_loading", src)//plays metal insertion animation + // use_power(min(1000, amount_inserted / 100)) + SStgui.update_uis(src) + +/obj/machinery/autolathe/examine(mob/user) + . = ..() + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + if(in_range(user, src) || isobserver(user)) + . += "The status display reads: Storing up to [materials.max_amount] material units.
    Material consumption at [mat_efficiency*100]%.
    " diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index 62a2a154a5d..215b2051faf 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -64,26 +64,26 @@ item_list = list() item_list["Food Items"] = list( - BIOGEN_REAGENT("10 milk", "milk", 10, 20), - BIOGEN_REAGENT("50 milk", "milk", 50, 95), - BIOGEN_REAGENT("10 Cream", "cream", 10, 30), - BIOGEN_REAGENT("50 Cream", "cream", 50, 120), + BIOGEN_REAGENT("Milk x10", "milk", 10, 20), + BIOGEN_REAGENT("Milk x50", "milk", 50, 95), + BIOGEN_REAGENT("Cream x10", "cream", 10, 30), + BIOGEN_REAGENT("Cream x50", "cream", 50, 120), BIOGEN_ITEM("Slab of meat", /obj/item/weapon/reagent_containers/food/snacks/meat, 1, 50), - BIOGEN_ITEM("5 slabs of meat", /obj/item/weapon/reagent_containers/food/snacks/meat, 5, 250), + BIOGEN_ITEM("Slabs of meat x5", /obj/item/weapon/reagent_containers/food/snacks/meat, 5, 250), ) item_list["Cooking Ingredients"] = list( - BIOGEN_REAGENT("10 Universal Enzyme", "enzyme", 10, 30), - BIOGEN_REAGENT("50 Universal Enzyme", "enzyme", 50, 120), + BIOGEN_REAGENT("Universal Enzyme x10", "enzyme", 10, 30), + BIOGEN_REAGENT("Universal Enzyme x50", "enzyme", 50, 120), BIOGEN_ITEM("Nutri-spread", /obj/item/weapon/reagent_containers/food/snacks/spreads, 1, 30), - BIOGEN_ITEM("5 nutri-spread", /obj/item/weapon/reagent_containers/food/snacks/spreads, 5, 120), + BIOGEN_ITEM("Nutri-spread x5", /obj/item/weapon/reagent_containers/food/snacks/spreads, 5, 120), ) item_list["Gardening Nutrients"] = list( BIOGEN_ITEM("E-Z-Nutrient", /obj/item/weapon/reagent_containers/glass/bottle/eznutrient, 1, 60), - BIOGEN_ITEM("5 E-Z-Nutrient", /obj/item/weapon/reagent_containers/glass/bottle/eznutrient, 5, 300), + BIOGEN_ITEM("E-Z-Nutrient x5", /obj/item/weapon/reagent_containers/glass/bottle/eznutrient, 5, 300), BIOGEN_ITEM("Left 4 Zed", /obj/item/weapon/reagent_containers/glass/bottle/left4zed, 1, 120), - BIOGEN_ITEM("5 Left 4 Zed", /obj/item/weapon/reagent_containers/glass/bottle/left4zed, 5, 600), + BIOGEN_ITEM("Left 4 Zed x5", /obj/item/weapon/reagent_containers/glass/bottle/left4zed, 5, 600), BIOGEN_ITEM("Robust Harvest", /obj/item/weapon/reagent_containers/glass/bottle/robustharvest, 1, 150), - BIOGEN_ITEM("5 Robust Harvest", /obj/item/weapon/reagent_containers/glass/bottle/robustharvest, 5, 750), + BIOGEN_ITEM("Robust Harvest x5", /obj/item/weapon/reagent_containers/glass/bottle/robustharvest, 5, 750), ) item_list["Leather Products"] = list( BIOGEN_ITEM("Wallet", /obj/item/weapon/storage/wallet, 1, 100), @@ -100,8 +100,8 @@ BIOGEN_ITEM("Leather Jacket", /obj/item/clothing/suit/storage/toggle/brown_jacket, 1, 500), BIOGEN_ITEM("Winter Coat", /obj/item/clothing/suit/storage/hooded/wintercoat, 1, 500), //VOREStation Edit - Algae for oxygen generator - BIOGEN_ITEM("4 Algae Sheets", /obj/item/stack/material/algae, 4, 400), - BIOGEN_ITEM("50 Algae Sheets", /obj/item/stack/material/algae, 50, 5000), + BIOGEN_ITEM("Algae Sheets x4", /obj/item/stack/material/algae, 4, 400), + BIOGEN_ITEM("Algae Sheets x50", /obj/item/stack/material/algae, 50, 5000), ) /obj/machinery/biogenerator/tgui_static_data(mob/user) @@ -305,3 +305,6 @@ build_eff = man_rating eat_eff = bin_rating + +#undef BIOGEN_ITEM +#undef BIOGEN_REAGENT diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index e0fa69147b8..b3e03da0eb1 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -1,18 +1,18 @@ -/obj/machinery/button - name = "button" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - layer = ABOVE_WINDOW_LAYER - desc = "A remote control switch for something." - var/id = null - var/active = 0 - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/button/attackby(obj/item/weapon/W, mob/user as mob) - return attack_hand(user) +/obj/machinery/button + name = "button" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + layer = ABOVE_WINDOW_LAYER + desc = "A remote control switch for something." + var/id = null + var/active = 0 + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/button/attackby(obj/item/weapon/W, mob/user as mob) + return attack_hand(user) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 85f23cb1dbd..b001611c19a 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,481 +1,482 @@ -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons - icon_state = "camera" - use_power = USE_POWER_ACTIVE - idle_power_usage = 5 - active_power_usage = 10 - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - - var/list/network = list(NETWORK_DEFAULT) - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = TRUE - var/invuln = 0 - var/bugged = 0 - var/obj/item/weapon/camera_assembly/assembly = null - - var/toughness = 5 //sorta fragile - - // WIRES - var/datum/wires/camera/wires = null // Wires datum - - //OTHER - - var/view_range = 7 - var/short_range = 2 - - var/light_disabled = 0 - var/in_use_lights = 0 // TO BE IMPLEMENTED - LIES. - var/alarm_on = 0 - var/busy = 0 - - var/on_open_network = 0 - var/always_visible = FALSE //Visable from any map, good for entertainment network cameras - - var/affected_by_emp_until = 0 - - var/client_huds = list() - - var/list/camera_computers_using_this = list() - -/obj/machinery/camera/New() - wires = new(src) - assembly = new(src) - assembly.state = 4 - client_huds |= global_hud.whitense - - /* // Use this to look for cameras that have the same c_tag. - for(var/obj/machinery/camera/C in cameranet.cameras) - var/list/tempnetwork = C.network&src.network - if(C != src && C.c_tag == src.c_tag && tempnetwork.len) - to_world_log("[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]") - */ - if(!src.network || src.network.len < 1) - if(loc) - error("[src.name] in [get_area(src)] (x:[src.x] y:[src.y] z:[src.z] has errored. [src.network?"Empty network list":"Null network list"]") - else - error("[src.name] in [get_area(src)]has errored. [src.network?"Empty network list":"Null network list"]") - ASSERT(src.network) - ASSERT(src.network.len > 0) - // VOREStation Edit Start - Make mapping with cameras easier - if(!c_tag) - var/area/A = get_area(src) - c_tag = "[A ? A.name : "Unknown"] #[rand(111,999)]" - ..() - // VOREStation Edit End - -/obj/machinery/camera/Destroy() - if(isMotion()) - unsense_proximity(callback = /atom/proc/HasProximity) - deactivate(null, 0) //kick anyone viewing out - if(assembly) - qdel(assembly) - assembly = null - qdel(wires) - wires = null - return ..() - -/obj/machinery/camera/process() - if((stat & EMPED) && world.time >= affected_by_emp_until) - stat &= ~EMPED - cancelCameraAlarm() - update_icon() - update_coverage() - return internal_process() - -/obj/machinery/camera/proc/internal_process() - return - -/obj/machinery/camera/emp_act(severity) - if(!isEmpProof() && prob(100/severity)) - if(!affected_by_emp_until || (world.time > affected_by_emp_until)) - affected_by_emp_until = max(affected_by_emp_until, world.time + (90 SECONDS / severity)) - stat |= EMPED - set_light(0) - triggerCameraAlarm() - update_icon() - update_coverage() - START_PROCESSING(SSobj, src) - -/obj/machinery/camera/bullet_act(var/obj/item/projectile/P) - take_damage(P.get_structure_damage()) - -/obj/machinery/camera/ex_act(severity) - if(src.invuln) - return - - //camera dies if an explosion touches it! - if(severity <= 2 || prob(50)) - destroy() - - ..() //and give it the regular chance of being deleted outright - -/obj/machinery/camera/blob_act() - if((stat & BROKEN) || invuln) - return - destroy() - -/obj/machinery/camera/hitby(AM as mob|obj) - ..() - if (istype(AM, /obj)) - var/obj/O = AM - if (O.throwforce >= src.toughness) - visible_message("[src] was hit by [O].") - take_damage(O.throwforce) - -/obj/machinery/camera/proc/setViewRange(var/num = 7) - src.view_range = num - cameranet.updateVisibility(src, 0) - -/obj/machinery/camera/attack_hand(mob/living/carbon/human/user as mob) - if(!istype(user)) - return - - if(user.species.can_shred(user)) - set_status(0) - user.do_attack_animation(src) - user.setClickCooldown(user.get_attack_speed()) - visible_message("\The [user] slashes at [src]!") - playsound(src, 'sound/weapons/slash.ogg', 100, 1) - add_hiddenprint(user) - destroy() - -/obj/machinery/camera/attack_generic(mob/user as mob) - if(isanimal(user)) - var/mob/living/simple_mob/S = user - set_status(0) - S.do_attack_animation(src) - S.setClickCooldown(user.get_attack_speed()) - visible_message("\The [user] [pick(S.attacktext)] \the [src]!") - playsound(src, S.attack_sound, 100, 1) - add_hiddenprint(user) - destroy() - ..() - -/obj/machinery/camera/attackby(obj/item/W as obj, mob/living/user as mob) - update_coverage() - // DECONSTRUCTION - if(W.is_screwdriver()) - //to_chat(user, "You start to [panel_open ? "close" : "open"] the camera's panel.") - //if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown - panel_open = !panel_open - user.visible_message("[user] screws the camera's panel [panel_open ? "open" : "closed"]!", - "You screw the camera's panel [panel_open ? "open" : "closed"].") - playsound(src, W.usesound, 50, 1) - - else if((W.is_wirecutter() || istype(W, /obj/item/device/multitool)) && panel_open) - interact(user) - - else if(istype(W, /obj/item/weapon/weldingtool) && (wires.CanDeconstruct() || (stat & BROKEN))) - if(weld(W, user)) - if(assembly) - assembly.loc = src.loc - assembly.anchored = TRUE - assembly.camera_name = c_tag - assembly.camera_network = english_list(network, NETWORK_DEFAULT, ",", ",") - assembly.update_icon() - assembly.dir = src.dir - if(stat & BROKEN) - assembly.state = 2 - to_chat(user, "You repaired \the [src] frame.") - else - assembly.state = 1 - to_chat(user, "You cut \the [src] free from the wall.") - new /obj/item/stack/cable_coil(src.loc, length=2) - assembly = null //so qdel doesn't eat it. - qdel(src) - - // OTHER - else if (can_use() && (istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) - var/mob/living/U = user - var/obj/item/weapon/paper/X = null - var/obj/item/device/pda/P = null - - var/itemname = "" - var/info = "" - if(istype(W, /obj/item/weapon/paper)) - X = W - itemname = X.name - info = X.info - else - P = W - itemname = P.name - var/datum/data/pda/app/notekeeper/N = P.find_program(/datum/data/pda/app/notekeeper) - if(N) - info = N.notehtml - to_chat(U, "You hold \a [itemname] up to the camera ...") - for(var/mob/living/silicon/ai/O in living_mob_list) - if(!O.client) - continue - if(U.name == "Unknown") - to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") - else - to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - - else if (istype(W, /obj/item/weapon/camera_bug)) - if (!src.can_use()) - to_chat(user, "Camera non-functional.") - return - if (src.bugged) - to_chat(user, "Camera bug removed.") - src.bugged = 0 - else - to_chat(user, "Camera bugged.") - src.bugged = 1 - - else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras - user.setClickCooldown(user.get_attack_speed(W)) - if (W.force >= src.toughness) - user.do_attack_animation(src) - visible_message("[src] has been [LAZYLEN(W.attack_verb) ? pick(W.attack_verb) : "attacked"] with [W] by [user]!") - if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items? - var/obj/item/I = W - if (I.hitsound) - playsound(src, I.hitsound, 50, 1, -1) - take_damage(W.force) - - else - ..() - -/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1) - // The only way for AI to reactivate cameras are malf abilities, this gives them different messages. - if(istype(user, /mob/living/silicon/ai)) - user = null - - if(choice != 1) - return - - set_status(!src.status) - if (!(src.status)) - if(user) - visible_message(" [user] has deactivated [src]!") - else - visible_message(" [src] clicks and shuts down. ") - playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = "[initial(icon_state)]1" - add_hiddenprint(user) - else - if(user) - visible_message(" [user] has reactivated [src]!") - else - visible_message(" [src] clicks and reactivates itself. ") - playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = initial(icon_state) - add_hiddenprint(user) - -/obj/machinery/camera/take_damage(var/force, var/message) - //prob(25) gives an average of 3-4 hits - if (force >= toughness && (force > toughness*4 || prob(25))) - destroy() - -//Used when someone breaks a camera -/obj/machinery/camera/proc/destroy() - stat |= BROKEN - wires.cut_all() - - triggerCameraAlarm() - update_icon() - update_coverage() - - //sparks - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, loc) - spark_system.start() - playsound(src, "sparks", 50, 1) - -/obj/machinery/camera/proc/set_status(var/newstatus) - if (status != newstatus) - status = newstatus - update_coverage() - -/obj/machinery/camera/check_eye(mob/user) - if(!can_use()) return -1 - if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS - return 0 - -/obj/machinery/camera/update_icon() - if (!status || (stat & BROKEN)) - icon_state = "[initial(icon_state)]1" - else if (stat & EMPED) - icon_state = "[initial(icon_state)]emp" - else - icon_state = initial(icon_state) - -/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0) - alarm_on = 1 - camera_alarm.triggerAlarm(loc, src, duration) - -/obj/machinery/camera/proc/cancelCameraAlarm() - if(wires.is_cut(WIRE_CAM_ALARM)) - return - - alarm_on = 0 - camera_alarm.clearAlarm(loc, src) - -//if false, then the camera is listed as DEACTIVATED and cannot be used -/obj/machinery/camera/proc/can_use() - if(!status) - return 0 - if(stat & (EMPED|BROKEN)) - return 0 - return 1 - -/obj/machinery/camera/proc/can_see() - var/list/see = null - var/turf/pos = get_turf(src) - if(!pos) - return list() - - if(isXRay()) - see = range(view_range, pos) - else - see = hear(view_range, pos) - return see - -/atom/proc/auto_turn() - //Automatically turns based on nearby walls. - var/turf/simulated/wall/T = null - for(var/i = 1, i <= 8; i += i) - T = get_ranged_target_turf(src, i, 1) - if(istype(T)) - //If someone knows a better way to do this, let me know. -Giacom - switch(i) - if(NORTH) - src.set_dir(SOUTH) - if(SOUTH) - src.set_dir(NORTH) - if(WEST) - src.set_dir(EAST) - if(EAST) - src.set_dir(WEST) - break - -//Return a working camera that can see a given mob -//or null if none -/proc/seen_by_camera(var/mob/M) - for(var/obj/machinery/camera/C in oview(4, M)) - if(C.can_use()) // check if camera disabled - return C - return null - -/proc/near_range_camera(var/mob/M) - - for(var/obj/machinery/camera/C in range(4, M)) - if(C.can_use()) // check if camera disabled - return C - - return null - -/obj/machinery/camera/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) - - if(busy) - return 0 - if(!WT.isOn()) - return 0 - - // Do after stuff here - to_chat(user, "You start to weld [src]..") - playsound(src, WT.usesound, 50, 1) - WT.eyecheck(user) - busy = 1 - if(do_after(user, 100 * WT.toolspeed)) - busy = 0 - if(!WT.isOn()) - return 0 - return 1 - busy = 0 - return 0 - -/obj/machinery/camera/interact(mob/living/user as mob) - if(!panel_open || istype(user, /mob/living/silicon/ai)) - return - - if(stat & BROKEN) - to_chat(user, "\The [src] is broken.") - return - - user.set_machine(src) - wires.Interact(user) - -/obj/machinery/camera/proc/add_network(var/network_name) - add_networks(list(network_name)) - -/obj/machinery/camera/proc/remove_network(var/network_name) - remove_networks(list(network_name)) - -/obj/machinery/camera/proc/add_networks(var/list/networks) - var/network_added - network_added = 0 - for(var/network_name in networks) - if(!(network_name in src.network)) - network += network_name - network_added = 1 - - if(network_added) - update_coverage(1) - -/obj/machinery/camera/proc/remove_networks(var/list/networks) - var/network_removed - network_removed = 0 - for(var/network_name in networks) - if(network_name in src.network) - network -= network_name - network_removed = 1 - - if(network_removed) - update_coverage(1) - -/obj/machinery/camera/proc/replace_networks(var/list/networks) - if(networks.len != network.len) - network = networks - update_coverage(1) - return - - for(var/new_network in networks) - if(!(new_network in network)) - network = networks - update_coverage(1) - return - -/obj/machinery/camera/proc/clear_all_networks() - if(network.len) - network.Cut() - update_coverage(1) - -/obj/machinery/camera/proc/tgui_structure() - var/cam[0] - cam["name"] = sanitize(c_tag) - cam["deact"] = !can_use() - cam["camera"] = "\ref[src]" - cam["omni"] = always_visible - cam["x"] = x - cam["y"] = y - cam["z"] = z - return cam - -/obj/machinery/camera/proc/update_coverage(var/network_change = 0) - if(network_change) - var/list/open_networks = difflist(network, restricted_camera_networks) - // Add or remove camera from the camera net as necessary - if(on_open_network && !open_networks.len) - cameranet.removeCamera(src) - else if(!on_open_network && open_networks.len) - on_open_network = 1 - cameranet.addCamera(src) - else - cameranet.updateVisibility(src, 0) - -// Resets the camera's wires to fully operational state. Used by one of Malfunction abilities. -/obj/machinery/camera/proc/reset_wires() - if(!wires) - return - if (stat & BROKEN) // Fix the camera - stat &= ~BROKEN - wires.repair() - update_icon() - update_coverage() +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons + icon_state = "camera" + use_power = USE_POWER_ACTIVE + idle_power_usage = 5 + active_power_usage = 10 + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + + var/list/network = list(NETWORK_DEFAULT) + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = TRUE + var/invuln = 0 + var/bugged = 0 + var/obj/item/weapon/camera_assembly/assembly = null + + var/toughness = 5 //sorta fragile + + // WIRES + var/datum/wires/camera/wires = null // Wires datum + + //OTHER + + var/view_range = 7 + var/short_range = 2 + + var/light_disabled = 0 + var/in_use_lights = 0 // TO BE IMPLEMENTED - LIES. + var/alarm_on = 0 + var/busy = 0 + + var/on_open_network = 0 + var/always_visible = FALSE //Visable from any map, good for entertainment network cameras + + var/affected_by_emp_until = 0 + + var/client_huds = list() + + var/list/camera_computers_using_this = list() + +/obj/machinery/camera/New() + wires = new(src) + assembly = new(src) + assembly.state = 4 + client_huds |= global_hud.whitense + + /* // Use this to look for cameras that have the same c_tag. + for(var/obj/machinery/camera/C in cameranet.cameras) + var/list/tempnetwork = C.network&src.network + if(C != src && C.c_tag == src.c_tag && tempnetwork.len) + to_world_log("[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]") + */ + if(!src.network || src.network.len < 1) + if(loc) + error("[src.name] in [get_area(src)] (x:[src.x] y:[src.y] z:[src.z] has errored. [src.network?"Empty network list":"Null network list"]") + else + error("[src.name] in [get_area(src)]has errored. [src.network?"Empty network list":"Null network list"]") + ASSERT(src.network) + ASSERT(src.network.len > 0) + // VOREStation Edit Start - Make mapping with cameras easier + if(!c_tag) + var/area/A = get_area(src) + c_tag = "[A ? A.name : "Unknown"] #[rand(111,999)]" + ..() + // VOREStation Edit End + +/obj/machinery/camera/Destroy() + if(isMotion()) + unsense_proximity(callback = /atom/proc/HasProximity) + deactivate(null, 0) //kick anyone viewing out + if(assembly) + qdel(assembly) + assembly = null + qdel(wires) + wires = null + return ..() + +/obj/machinery/camera/process() + if((stat & EMPED) && world.time >= affected_by_emp_until) + stat &= ~EMPED + cancelCameraAlarm() + update_icon() + update_coverage() + return internal_process() + +/obj/machinery/camera/proc/internal_process() + return + +/obj/machinery/camera/emp_act(severity) + if(!isEmpProof() && prob(100/severity)) + if(!affected_by_emp_until || (world.time > affected_by_emp_until)) + affected_by_emp_until = max(affected_by_emp_until, world.time + (90 SECONDS / severity)) + stat |= EMPED + set_light(0) + triggerCameraAlarm() + update_icon() + update_coverage() + START_PROCESSING(SSobj, src) + +/obj/machinery/camera/bullet_act(var/obj/item/projectile/P) + take_damage(P.get_structure_damage()) + +/obj/machinery/camera/ex_act(severity) + if(src.invuln) + return + + //camera dies if an explosion touches it! + if(severity <= 2 || prob(50)) + destroy() + + ..() //and give it the regular chance of being deleted outright + +/obj/machinery/camera/blob_act() + if((stat & BROKEN) || invuln) + return + destroy() + +/obj/machinery/camera/hitby(AM as mob|obj) + ..() + if (istype(AM, /obj)) + var/obj/O = AM + if (O.throwforce >= src.toughness) + visible_message("[src] was hit by [O].") + take_damage(O.throwforce) + +/obj/machinery/camera/proc/setViewRange(var/num = 7) + src.view_range = num + cameranet.updateVisibility(src, 0) + +/obj/machinery/camera/attack_hand(mob/living/carbon/human/user as mob) + if(!istype(user)) + return + + if(user.species.can_shred(user)) + set_status(0) + user.do_attack_animation(src) + user.setClickCooldown(user.get_attack_speed()) + visible_message("\The [user] slashes at [src]!") + playsound(src, 'sound/weapons/slash.ogg', 100, 1) + add_hiddenprint(user) + destroy() + +/obj/machinery/camera/attack_generic(mob/user as mob) + if(isanimal(user)) + var/mob/living/simple_mob/S = user + set_status(0) + S.do_attack_animation(src) + S.setClickCooldown(user.get_attack_speed()) + visible_message("\The [user] [pick(S.attacktext)] \the [src]!") + playsound(src, S.attack_sound, 100, 1) + add_hiddenprint(user) + destroy() + ..() + +/obj/machinery/camera/attackby(obj/item/W as obj, mob/living/user as mob) + update_coverage() + // DECONSTRUCTION + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + //to_chat(user, "You start to [panel_open ? "close" : "open"] the camera's panel.") + //if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown + panel_open = !panel_open + user.visible_message("[user] screws the camera's panel [panel_open ? "open" : "closed"]!", + "You screw the camera's panel [panel_open ? "open" : "closed"].") + playsound(src, W.usesound, 50, 1) + + else if((W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) && panel_open) + interact(user) + + else if(W.has_tool_quality(TOOL_WELDER) && (wires.CanDeconstruct() || (stat & BROKEN))) + if(weld(W, user)) + if(assembly) + assembly.loc = src.loc + assembly.anchored = TRUE + assembly.camera_name = c_tag + assembly.camera_network = english_list(network, NETWORK_DEFAULT, ",", ",") + assembly.update_icon() + assembly.dir = src.dir + if(stat & BROKEN) + assembly.state = 2 + to_chat(user, "You repaired \the [src] frame.") + else + assembly.state = 1 + to_chat(user, "You cut \the [src] free from the wall.") + new /obj/item/stack/cable_coil(src.loc, length=2) + assembly = null //so qdel doesn't eat it. + qdel(src) + + // OTHER + else if (can_use() && (istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) + var/mob/living/U = user + var/obj/item/weapon/paper/X = null + var/obj/item/device/pda/P = null + + var/itemname = "" + var/info = "" + if(istype(W, /obj/item/weapon/paper)) + X = W + itemname = X.name + info = X.info + else + P = W + itemname = P.name + var/datum/data/pda/app/notekeeper/N = P.find_program(/datum/data/pda/app/notekeeper) + if(N) + info = N.notehtml + to_chat(U, "You hold \a [itemname] up to the camera ...") + for(var/mob/living/silicon/ai/O in living_mob_list) + if(!O.client) + continue + if(U.name == "Unknown") + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") + else + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + else if (istype(W, /obj/item/weapon/camera_bug)) + if (!src.can_use()) + to_chat(user, "Camera non-functional.") + return + if (src.bugged) + to_chat(user, "Camera bug removed.") + src.bugged = 0 + else + to_chat(user, "Camera bugged.") + src.bugged = 1 + + else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras + user.setClickCooldown(user.get_attack_speed(W)) + if (W.force >= src.toughness) + user.do_attack_animation(src) + visible_message("[src] has been [LAZYLEN(W.attack_verb) ? pick(W.attack_verb) : "attacked"] with [W] by [user]!") + if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items? + var/obj/item/I = W + if (I.hitsound) + playsound(src, I.hitsound, 50, 1, -1) + take_damage(W.force) + + else + ..() + +/obj/machinery/camera/proc/deactivate(user as mob, var/choice = 1) + // The only way for AI to reactivate cameras are malf abilities, this gives them different messages. + if(istype(user, /mob/living/silicon/ai)) + user = null + + if(choice != 1) + return + + set_status(!src.status) + if (!(src.status)) + if(user) + visible_message(" [user] has deactivated [src]!") + else + visible_message(" [src] clicks and shuts down. ") + playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = "[initial(icon_state)]1" + add_hiddenprint(user) + else + if(user) + visible_message(" [user] has reactivated [src]!") + else + visible_message(" [src] clicks and reactivates itself. ") + playsound(src, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = initial(icon_state) + add_hiddenprint(user) + +/obj/machinery/camera/take_damage(var/force, var/message) + //prob(25) gives an average of 3-4 hits + if (force >= toughness && (force > toughness*4 || prob(25))) + destroy() + +//Used when someone breaks a camera +/obj/machinery/camera/proc/destroy() + stat |= BROKEN + wires.cut_all() + + triggerCameraAlarm() + update_icon() + update_coverage() + + //sparks + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + +/obj/machinery/camera/proc/set_status(var/newstatus) + if (status != newstatus) + status = newstatus + update_coverage() + +/obj/machinery/camera/check_eye(mob/user) + if(!can_use()) return -1 + if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS + return 0 + +/obj/machinery/camera/update_icon() + if (!status || (stat & BROKEN)) + icon_state = "[initial(icon_state)]1" + else if (stat & EMPED) + icon_state = "[initial(icon_state)]emp" + else + icon_state = initial(icon_state) + +/obj/machinery/camera/proc/triggerCameraAlarm(var/duration = 0) + alarm_on = 1 + camera_alarm.triggerAlarm(loc, src, duration) + +/obj/machinery/camera/proc/cancelCameraAlarm() + if(wires.is_cut(WIRE_CAM_ALARM)) + return + + alarm_on = 0 + camera_alarm.clearAlarm(loc, src) + +//if false, then the camera is listed as DEACTIVATED and cannot be used +/obj/machinery/camera/proc/can_use() + if(!status) + return 0 + if(stat & (EMPED|BROKEN)) + return 0 + return 1 + +/obj/machinery/camera/proc/can_see() + var/list/see = null + var/turf/pos = get_turf(src) + if(!pos) + return list() + + if(isXRay()) + see = range(view_range, pos) + else + see = hear(view_range, pos) + return see + +/atom/proc/auto_turn() + //Automatically turns based on nearby walls. + var/turf/simulated/wall/T = null + for(var/i = 1, i <= 8; i += i) + T = get_ranged_target_turf(src, i, 1) + if(istype(T)) + //If someone knows a better way to do this, let me know. -Giacom + switch(i) + if(NORTH) + src.set_dir(SOUTH) + if(SOUTH) + src.set_dir(NORTH) + if(WEST) + src.set_dir(EAST) + if(EAST) + src.set_dir(WEST) + break + +//Return a working camera that can see a given mob +//or null if none +/proc/seen_by_camera(var/mob/M) + for(var/obj/machinery/camera/C in oview(4, M)) + if(C.can_use()) // check if camera disabled + return C + return null + +/proc/near_range_camera(var/mob/M) + + for(var/obj/machinery/camera/C in range(4, M)) + if(C.can_use()) // check if camera disabled + return C + + return null + +/obj/machinery/camera/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) + WT = WT.get_welder() + + if(busy) + return 0 + if(!WT.isOn()) + return 0 + + // Do after stuff here + to_chat(user, "You start to weld [src]..") + playsound(src, WT.usesound, 50, 1) + WT.eyecheck(user) + busy = 1 + if(do_after(user, 100 * WT.toolspeed)) + busy = 0 + if(!WT.isOn()) + return 0 + return 1 + busy = 0 + return 0 + +/obj/machinery/camera/interact(mob/living/user as mob) + if(!panel_open || istype(user, /mob/living/silicon/ai)) + return + + if(stat & BROKEN) + to_chat(user, "\The [src] is broken.") + return + + user.set_machine(src) + wires.Interact(user) + +/obj/machinery/camera/proc/add_network(var/network_name) + add_networks(list(network_name)) + +/obj/machinery/camera/proc/remove_network(var/network_name) + remove_networks(list(network_name)) + +/obj/machinery/camera/proc/add_networks(var/list/networks) + var/network_added + network_added = 0 + for(var/network_name in networks) + if(!(network_name in src.network)) + network += network_name + network_added = 1 + + if(network_added) + update_coverage(1) + +/obj/machinery/camera/proc/remove_networks(var/list/networks) + var/network_removed + network_removed = 0 + for(var/network_name in networks) + if(network_name in src.network) + network -= network_name + network_removed = 1 + + if(network_removed) + update_coverage(1) + +/obj/machinery/camera/proc/replace_networks(var/list/networks) + if(networks.len != network.len) + network = networks + update_coverage(1) + return + + for(var/new_network in networks) + if(!(new_network in network)) + network = networks + update_coverage(1) + return + +/obj/machinery/camera/proc/clear_all_networks() + if(network.len) + network.Cut() + update_coverage(1) + +/obj/machinery/camera/proc/tgui_structure() + var/cam[0] + cam["name"] = sanitize(c_tag) + cam["deact"] = !can_use() + cam["camera"] = "\ref[src]" + cam["omni"] = always_visible + cam["x"] = x + cam["y"] = y + cam["z"] = z + return cam + +/obj/machinery/camera/proc/update_coverage(var/network_change = 0) + if(network_change) + var/list/open_networks = difflist(network, restricted_camera_networks) + // Add or remove camera from the camera net as necessary + if(on_open_network && !open_networks.len) + cameranet.removeCamera(src) + else if(!on_open_network && open_networks.len) + on_open_network = 1 + cameranet.addCamera(src) + else + cameranet.updateVisibility(src, 0) + +// Resets the camera's wires to fully operational state. Used by one of Malfunction abilities. +/obj/machinery/camera/proc/reset_wires() + if(!wires) + return + if (stat & BROKEN) // Fix the camera + stat &= ~BROKEN + wires.repair() + update_icon() + update_coverage() diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index a73eb6fe917..86bdc11e007 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -1,173 +1,174 @@ -/obj/item/weapon/camera_assembly - name = "camera assembly" - desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." - icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons - icon_state = "cameracase" - w_class = ITEMSIZE_SMALL - anchored = FALSE - - matter = list(MAT_STEEL = 700,MAT_GLASS = 300) - - // Motion, EMP-Proof, X-Ray - var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/material/osmium, /obj/item/weapon/stock_parts/scanning_module) - var/list/upgrades = list() - var/camera_name - var/camera_network - var/state = 0 - var/busy = 0 - /* - 0 = Nothing done to it - 1 = Wrenched in place - 2 = Welded in place - 3 = Wires attached to it (you can now attach/dettach upgrades) - 4 = Screwdriver panel closed and is fully built (you cannot attach upgrades) - */ - -/obj/item/weapon/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob) - - switch(state) - - if(0) - // State 0 - if(W.is_wrench() && isturf(src.loc)) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You wrench the assembly into place.") - anchored = TRUE - state = 1 - update_icon() - auto_turn() - return - - if(1) - // State 1 - if(istype(W, /obj/item/weapon/weldingtool)) - if(weld(W, user)) - to_chat(user, "You weld the assembly securely into place.") - anchored = TRUE - state = 2 - return - - else if(W.is_wrench()) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You unattach the assembly from its place.") - anchored = FALSE - update_icon() - state = 0 - return - - if(2) - // State 2 - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(C.use(2)) - to_chat(user, "You add wires to the assembly.") - state = 3 - else - to_chat(user, "You need 2 coils of wire to wire the assembly.") - return - - else if(istype(W, /obj/item/weapon/weldingtool)) - - if(weld(W, user)) - to_chat(user, "You unweld the assembly from its place.") - state = 1 - anchored = TRUE - return - - - if(3) - // State 3 - if(W.is_screwdriver()) - playsound(src, W.usesound, 50, 1) - - var/input = sanitize(tgui_input_text(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: "+using_map.station_short+",Security,Secret ", "Set Network", camera_network ? camera_network : NETWORK_DEFAULT)) - if(!input) - to_chat(usr, "No input found please hang up and try your call again.") - return - - var/list/tempnetwork = splittext(input, ",") - if(tempnetwork.len < 1) - to_chat(usr, "No network found please hang up and try your call again.") - return - - var/area/camera_area = get_area(src) - var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" - input = sanitizeSafe(tgui_input_text(usr, "How would you like to name the camera?", "Set Camera Name", camera_name ? camera_name : temptag), MAX_NAME_LEN) - - state = 4 - var/obj/machinery/camera/C = new(src.loc) - src.loc = C - C.assembly = src - - C.auto_turn() - - C.replace_networks(uniqueList(tempnetwork)) - - C.c_tag = input - - for(var/i = 5; i >= 0; i -= 1) - var/direct = tgui_input_list(user, "Direction?", "Assembling Camera", list("NORTH", "EAST", "SOUTH", "WEST", "LEAVE IT")) - if(direct != "LEAVE IT") - C.dir = text2dir(direct) - if(i != 0) - var/confirm = tgui_alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", list("Yes", "No")) - if(confirm == "Yes") - break - return - - else if(W.is_wirecutter()) - - new/obj/item/stack/cable_coil(get_turf(src), 2) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You cut the wires from the circuits.") - state = 2 - return - - // Upgrades! - if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already. - to_chat(user, "You attach \the [W] into the assembly inner circuits.") - upgrades += W - user.remove_from_mob(W) - W.loc = src - return - - // Taking out upgrades - else if(W.is_crowbar() && upgrades.len) - var/obj/U = locate(/obj) in upgrades - if(U) - to_chat(user, "You unattach an upgrade from the assembly.") - playsound(src, W.usesound, 50, 1) - U.loc = get_turf(src) - upgrades -= U - return - - ..() - -/obj/item/weapon/camera_assembly/update_icon() - if(anchored) - icon_state = "camera1" - else - icon_state = "cameracase" - -/obj/item/weapon/camera_assembly/attack_hand(mob/user as mob) - if(!anchored) - ..() - -/obj/item/weapon/camera_assembly/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) - - if(busy) - return 0 - if(!WT.isOn()) - return 0 - - to_chat(user, "You start to weld the [src]..") - playsound(src, WT.usesound, 50, 1) - WT.eyecheck(user) - busy = 1 - if(do_after(user, 20 * WT.toolspeed)) - busy = 0 - if(!WT.isOn()) - return 0 - return 1 - busy = 0 - return 0 +/obj/item/weapon/camera_assembly + name = "camera assembly" + desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." + icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons + icon_state = "cameracase" + w_class = ITEMSIZE_SMALL + anchored = FALSE + + matter = list(MAT_STEEL = 700,MAT_GLASS = 300) + + // Motion, EMP-Proof, X-Ray + var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/material/osmium, /obj/item/weapon/stock_parts/scanning_module) + var/list/upgrades = list() + var/camera_name + var/camera_network + var/state = 0 + var/busy = 0 + /* + 0 = Nothing done to it + 1 = Wrenched in place + 2 = Welded in place + 3 = Wires attached to it (you can now attach/dettach upgrades) + 4 = Screwdriver panel closed and is fully built (you cannot attach upgrades) + */ + +/obj/item/weapon/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob) + + switch(state) + + if(0) + // State 0 + if(W.has_tool_quality(TOOL_WRENCH) && isturf(src.loc)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You wrench the assembly into place.") + anchored = TRUE + state = 1 + update_icon() + auto_turn() + return + + if(1) + // State 1 + if(W.has_tool_quality(TOOL_WELDER)) + if(weld(W, user)) + to_chat(user, "You weld the assembly securely into place.") + anchored = TRUE + state = 2 + return + + else if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You unattach the assembly from its place.") + anchored = FALSE + update_icon() + state = 0 + return + + if(2) + // State 2 + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(C.use(2)) + to_chat(user, "You add wires to the assembly.") + state = 3 + else + to_chat(user, "You need 2 coils of wire to wire the assembly.") + return + + else if(W.has_tool_quality(TOOL_WELDER)) + + if(weld(W, user)) + to_chat(user, "You unweld the assembly from its place.") + state = 1 + anchored = TRUE + return + + + if(3) + // State 3 + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, W.usesound, 50, 1) + + var/input = sanitize(tgui_input_text(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: "+using_map.station_short+",Security,Secret ", "Set Network", camera_network ? camera_network : NETWORK_DEFAULT)) + if(!input) + to_chat(usr, "No input found please hang up and try your call again.") + return + + var/list/tempnetwork = splittext(input, ",") + if(tempnetwork.len < 1) + to_chat(usr, "No network found please hang up and try your call again.") + return + + var/area/camera_area = get_area(src) + var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" + input = sanitizeSafe(tgui_input_text(usr, "How would you like to name the camera?", "Set Camera Name", camera_name ? camera_name : temptag), MAX_NAME_LEN) + + state = 4 + var/obj/machinery/camera/C = new(src.loc) + src.loc = C + C.assembly = src + + C.auto_turn() + + C.replace_networks(uniqueList(tempnetwork)) + + C.c_tag = input + + for(var/i = 5; i >= 0; i -= 1) + var/direct = tgui_input_list(user, "Direction?", "Assembling Camera", list("NORTH", "EAST", "SOUTH", "WEST", "LEAVE IT")) + if(direct != "LEAVE IT") + C.dir = text2dir(direct) + if(i != 0) + var/confirm = tgui_alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", list("Yes", "No")) + if(confirm == "Yes") + break + return + + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + + new/obj/item/stack/cable_coil(get_turf(src), 2) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You cut the wires from the circuits.") + state = 2 + return + + // Upgrades! + if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already. + to_chat(user, "You attach \the [W] into the assembly inner circuits.") + upgrades += W + user.remove_from_mob(W) + W.loc = src + return + + // Taking out upgrades + else if(W.has_tool_quality(TOOL_CROWBAR) && upgrades.len) + var/obj/U = locate(/obj) in upgrades + if(U) + to_chat(user, "You unattach an upgrade from the assembly.") + playsound(src, W.usesound, 50, 1) + U.loc = get_turf(src) + upgrades -= U + return + + ..() + +/obj/item/weapon/camera_assembly/update_icon() + if(anchored) + icon_state = "camera1" + else + icon_state = "cameracase" + +/obj/item/weapon/camera_assembly/attack_hand(mob/user as mob) + if(!anchored) + ..() + +/obj/item/weapon/camera_assembly/proc/weld(var/obj/item/weapon/weldingtool/WT, var/mob/user) + WT = WT.get_welder() + + if(busy) + return 0 + if(!WT.isOn()) + return 0 + + to_chat(user, "You start to weld the [src]..") + playsound(src, WT.usesound, 50, 1) + WT.eyecheck(user) + busy = 1 + if(do_after(user, 20 * WT.toolspeed)) + busy = 0 + if(!WT.isOn()) + return 0 + return 1 + busy = 0 + return 0 diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index ad183a7be83..00bfeb09cc4 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -1,62 +1,62 @@ -/obj/machinery/camera - var/list/motionTargets = list() - var/detectTime = 0 - var/area/ai_monitored/area_motion = null - var/alarm_delay = 100 // Don't forget, there's another 10 seconds in queueAlarm() - -/obj/machinery/camera/internal_process() - // motion camera event loop - if (stat & (EMPED|NOPOWER)) - return - if(!isMotion()) - return PROCESS_KILL - if (detectTime > 0) - var/elapsed = world.time - detectTime - if (elapsed > alarm_delay) - triggerAlarm() - else if (detectTime == -1) - for (var/mob/target in motionTargets) - if (target.stat == 2) lostTarget(target) - // If not detecting with motion camera... - if (!area_motion) - // See if the camera is still in range - if(!in_range(src, target)) - // If they aren't in range, lose the target. - lostTarget(target) - -/obj/machinery/camera/proc/newTarget(var/mob/target) - if (istype(target, /mob/living/silicon/ai)) return 0 - if (detectTime == 0) - detectTime = world.time // start the clock - if (!(target in motionTargets)) - motionTargets += target - return 1 - -/obj/machinery/camera/proc/lostTarget(var/mob/target) - if (target in motionTargets) - motionTargets -= target - if (motionTargets.len == 0) - cancelAlarm() - -/obj/machinery/camera/proc/cancelAlarm() - if (!status || (stat & NOPOWER)) - return 0 - if (detectTime == -1) - motion_alarm.clearAlarm(loc, src) - detectTime = 0 - return 1 - -/obj/machinery/camera/proc/triggerAlarm() - if (!status || (stat & NOPOWER)) - return 0 - if (!detectTime) return 0 - motion_alarm.triggerAlarm(loc, src) - detectTime = -1 - return 1 - -/obj/machinery/camera/HasProximity(turf/T, atom/movable/AM, old_loc) - // Motion cameras outside of an "ai monitored" area will use this to detect stuff. - if (!area_motion) - if(isliving(AM)) - newTarget(AM) - +/obj/machinery/camera + var/list/motionTargets = list() + var/detectTime = 0 + var/area/ai_monitored/area_motion = null + var/alarm_delay = 100 // Don't forget, there's another 10 seconds in queueAlarm() + +/obj/machinery/camera/internal_process() + // motion camera event loop + if (stat & (EMPED|NOPOWER)) + return + if(!isMotion()) + return PROCESS_KILL + if (detectTime > 0) + var/elapsed = world.time - detectTime + if (elapsed > alarm_delay) + triggerAlarm() + else if (detectTime == -1) + for (var/mob/target in motionTargets) + if (target.stat == 2) lostTarget(target) + // If not detecting with motion camera... + if (!area_motion) + // See if the camera is still in range + if(!in_range(src, target)) + // If they aren't in range, lose the target. + lostTarget(target) + +/obj/machinery/camera/proc/newTarget(var/mob/target) + if (istype(target, /mob/living/silicon/ai)) return 0 + if (detectTime == 0) + detectTime = world.time // start the clock + if (!(target in motionTargets)) + motionTargets += target + return 1 + +/obj/machinery/camera/proc/lostTarget(var/mob/target) + if (target in motionTargets) + motionTargets -= target + if (motionTargets.len == 0) + cancelAlarm() + +/obj/machinery/camera/proc/cancelAlarm() + if (!status || (stat & NOPOWER)) + return 0 + if (detectTime == -1) + motion_alarm.clearAlarm(loc, src) + detectTime = 0 + return 1 + +/obj/machinery/camera/proc/triggerAlarm() + if (!status || (stat & NOPOWER)) + return 0 + if (!detectTime) return 0 + motion_alarm.triggerAlarm(loc, src) + detectTime = -1 + return 1 + +/obj/machinery/camera/HasProximity(turf/T, atom/movable/AM, old_loc) + // Motion cameras outside of an "ai monitored" area will use this to detect stuff. + if (!area_motion) + if(isliving(AM)) + newTarget(AM) + diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 7e1de88bbb1..d0ef9e1cd42 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,234 +1,234 @@ -// PRESETS -/* -var/global/list/station_networks = list( -// NETWORK_CAFE_DOCK, - NETWORK_CARGO, - NETWORK_CIVILIAN, -// NETWORK_CIVILIAN_EAST, -// NETWORK_CIVILIAN_WEST, - NETWORK_COMMAND, - NETWORK_ENGINE, - NETWORK_ENGINEERING, - NETWORK_ENGINEERING_OUTPOST, - NETWORK_DEFAULT, - NETWORK_MEDICAL, - NETWORK_MINE, - NETWORK_NORTHERN_STAR, - NETWORK_RESEARCH, - NETWORK_RESEARCH_OUTPOST, - NETWORK_ROBOTS, - NETWORK_PRISON, - NETWORK_SECURITY, - NETWORK_INTERROGATION - ) -*/ -var/global/list/engineering_networks = list( - NETWORK_ENGINE, - NETWORK_ENGINEERING, - //NETWORK_ENGINEERING_OUTPOST, //VOREStation Edit: Tether has no Engineering Outpost, - NETWORK_ALARM_ATMOS, - NETWORK_ALARM_FIRE, - NETWORK_ALARM_POWER) -/obj/machinery/camera/network/crescent - network = list(NETWORK_CRESCENT) - -/* -/obj/machinery/camera/network/cafe_dock - network = list(NETWORK_CAFE_DOCK) -*/ - -/obj/machinery/camera/network/cargo - network = list(NETWORK_CARGO) - -/obj/machinery/camera/network/civilian - network = list(NETWORK_CIVILIAN) - -/obj/machinery/camera/network/circuits - network = list(NETWORK_CIRCUITS) - -/* -/obj/machinery/camera/network/civilian_east - network = list(NETWORK_CIVILIAN_EAST) - -/obj/machinery/camera/network/civilian_west - network = list(NETWORK_CIVILIAN_WEST) -*/ - -/obj/machinery/camera/network/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/network/engine - network = list(NETWORK_ENGINE) - -/obj/machinery/camera/network/engineering - network = list(NETWORK_ENGINEERING) - -/obj/machinery/camera/network/engineering_outpost - network = list(NETWORK_ENGINEERING_OUTPOST) - -/obj/machinery/camera/network/ert - network = list(NETWORK_ERT) - -/obj/machinery/camera/network/exodus - network = list(NETWORK_DEFAULT) - -/obj/machinery/camera/network/interrogation - network = list(NETWORK_INTERROGATION) - -/obj/machinery/camera/network/mining - network = list(NETWORK_MINE) - -/obj/machinery/camera/network/northern_star - network = list(NETWORK_NORTHERN_STAR) - -/obj/machinery/camera/network/prison - network = list(NETWORK_PRISON) - -/obj/machinery/camera/network/medbay - network = list(NETWORK_MEDICAL) - -/obj/machinery/camera/network/research - network = list(NETWORK_RESEARCH) - -/obj/machinery/camera/network/research_outpost - network = list(NETWORK_RESEARCH_OUTPOST) - -/obj/machinery/camera/network/security - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/network/telecom - network = list(NETWORK_TELECOM) - -/obj/machinery/camera/network/exploration - network = list(NETWORK_EXPLORATION) - -/obj/machinery/camera/network/research/xenobio - network = list(NETWORK_RESEARCH, NETWORK_XENOBIO) - -/obj/machinery/camera/network/thunder - network = list(NETWORK_THUNDER) - invuln = 1 - always_visible = TRUE - -// EMP - -/obj/machinery/camera/emp_proof/New() - ..() - upgradeEmpProof() - -// X-RAY - -/obj/machinery/camera/xray - icon_state = "xraycam" // Thanks to Krutchen for the icons. - -/obj/machinery/camera/xray/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/xray/security - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/xray/medbay - network = list(NETWORK_MEDICAL) - -/obj/machinery/camera/xray/research - network = list(NETWORK_RESEARCH) - -/obj/machinery/camera/xray/New() - ..() - upgradeXRay() - -// MOTION - -/obj/machinery/camera/motion/New() - ..() - upgradeMotion() - -/obj/machinery/camera/motion/engineering_outpost - network = list(NETWORK_ENGINEERING_OUTPOST) - -/obj/machinery/camera/motion/security - network = list(NETWORK_SECURITY) - -// ALL UPGRADES - - -/obj/machinery/camera/all/command - network = list(NETWORK_COMMAND) - -/obj/machinery/camera/all/New() - ..() - upgradeEmpProof() - upgradeXRay() - upgradeMotion() - -// AUTONAME -/obj/machinery/camera/autoname - var/static/list/by_area - -/obj/machinery/camera/autoname/Initialize() - . = ..() - var/area/A = get_area(src) - if(!A) - return . - if(!by_area) - by_area = list() - if(!by_area[A.name]) - by_area[A.name] = list() - var/list/my_area = by_area[A.name] - my_area += src - var/number = my_area.len - - c_tag = "[A.name] #[number]" - -/obj/machinery/camera/autoname/Destroy() - var/area/A = get_area(src) - if(!A || !by_area || !by_area[A.name]) - return ..() - var/list/my_area = by_area[A.name] - my_area -= src - return ..() - -// CHECKS - -/obj/machinery/camera/proc/isEmpProof() - var/O = locate(/obj/item/stack/material/osmium) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isXRay() - var/obj/item/weapon/stock_parts/scanning_module/O = locate(/obj/item/weapon/stock_parts/scanning_module) in assembly.upgrades - if (O && O.rating >= 2) - return O - return null - -/obj/machinery/camera/proc/isMotion() - var/O = locate(/obj/item/device/assembly/prox_sensor) in assembly.upgrades - return O - -// UPGRADE PROCS - -/obj/machinery/camera/proc/upgradeEmpProof() - assembly.upgrades.Add(new /obj/item/stack/material/osmium(assembly)) - setPowerUsage() - update_coverage() - -/obj/machinery/camera/proc/upgradeXRay() - assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) - setPowerUsage() - update_coverage() - -/obj/machinery/camera/proc/upgradeMotion() - if(!isturf(loc)) - return //nooooo - assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) - setPowerUsage() - START_MACHINE_PROCESSING(src) - sense_proximity(callback = /atom/proc/HasProximity) - update_coverage() - -/obj/machinery/camera/proc/setPowerUsage() - var/mult = 1 - if (isXRay()) - mult++ - if (isMotion()) - mult++ - update_active_power_usage(mult * initial(active_power_usage)) +// PRESETS +/* +var/global/list/station_networks = list( +// NETWORK_CAFE_DOCK, + NETWORK_CARGO, + NETWORK_CIVILIAN, +// NETWORK_CIVILIAN_EAST, +// NETWORK_CIVILIAN_WEST, + NETWORK_COMMAND, + NETWORK_ENGINE, + NETWORK_ENGINEERING, + NETWORK_ENGINEERING_OUTPOST, + NETWORK_DEFAULT, + NETWORK_MEDICAL, + NETWORK_MINE, + NETWORK_NORTHERN_STAR, + NETWORK_RESEARCH, + NETWORK_RESEARCH_OUTPOST, + NETWORK_ROBOTS, + NETWORK_PRISON, + NETWORK_SECURITY, + NETWORK_INTERROGATION + ) +*/ +var/global/list/engineering_networks = list( + NETWORK_ENGINE, + NETWORK_ENGINEERING, + //NETWORK_ENGINEERING_OUTPOST, //VOREStation Edit: Tether has no Engineering Outpost, + NETWORK_ALARM_ATMOS, + NETWORK_ALARM_FIRE, + NETWORK_ALARM_POWER) +/obj/machinery/camera/network/crescent + network = list(NETWORK_CRESCENT) + +/* +/obj/machinery/camera/network/cafe_dock + network = list(NETWORK_CAFE_DOCK) +*/ + +/obj/machinery/camera/network/cargo + network = list(NETWORK_CARGO) + +/obj/machinery/camera/network/civilian + network = list(NETWORK_CIVILIAN) + +/obj/machinery/camera/network/circuits + network = list(NETWORK_CIRCUITS) + +/* +/obj/machinery/camera/network/civilian_east + network = list(NETWORK_CIVILIAN_EAST) + +/obj/machinery/camera/network/civilian_west + network = list(NETWORK_CIVILIAN_WEST) +*/ + +/obj/machinery/camera/network/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/network/engine + network = list(NETWORK_ENGINE) + +/obj/machinery/camera/network/engineering + network = list(NETWORK_ENGINEERING) + +/obj/machinery/camera/network/engineering_outpost + network = list(NETWORK_ENGINEERING_OUTPOST) + +/obj/machinery/camera/network/ert + network = list(NETWORK_ERT) + +/obj/machinery/camera/network/exodus + network = list(NETWORK_DEFAULT) + +/obj/machinery/camera/network/interrogation + network = list(NETWORK_INTERROGATION) + +/obj/machinery/camera/network/mining + network = list(NETWORK_MINE) + +/obj/machinery/camera/network/northern_star + network = list(NETWORK_NORTHERN_STAR) + +/obj/machinery/camera/network/prison + network = list(NETWORK_PRISON) + +/obj/machinery/camera/network/medbay + network = list(NETWORK_MEDICAL) + +/obj/machinery/camera/network/research + network = list(NETWORK_RESEARCH) + +/obj/machinery/camera/network/research_outpost + network = list(NETWORK_RESEARCH_OUTPOST) + +/obj/machinery/camera/network/security + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/network/telecom + network = list(NETWORK_TELECOM) + +/obj/machinery/camera/network/exploration + network = list(NETWORK_EXPLORATION) + +/obj/machinery/camera/network/research/xenobio + network = list(NETWORK_RESEARCH, NETWORK_XENOBIO) + +/obj/machinery/camera/network/thunder + network = list(NETWORK_THUNDER) + invuln = 1 + always_visible = TRUE + +// EMP + +/obj/machinery/camera/emp_proof/New() + ..() + upgradeEmpProof() + +// X-RAY + +/obj/machinery/camera/xray + icon_state = "xraycam" // Thanks to Krutchen for the icons. + +/obj/machinery/camera/xray/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/xray/security + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/xray/medbay + network = list(NETWORK_MEDICAL) + +/obj/machinery/camera/xray/research + network = list(NETWORK_RESEARCH) + +/obj/machinery/camera/xray/New() + ..() + upgradeXRay() + +// MOTION + +/obj/machinery/camera/motion/New() + ..() + upgradeMotion() + +/obj/machinery/camera/motion/engineering_outpost + network = list(NETWORK_ENGINEERING_OUTPOST) + +/obj/machinery/camera/motion/security + network = list(NETWORK_SECURITY) + +// ALL UPGRADES + + +/obj/machinery/camera/all/command + network = list(NETWORK_COMMAND) + +/obj/machinery/camera/all/New() + ..() + upgradeEmpProof() + upgradeXRay() + upgradeMotion() + +// AUTONAME +/obj/machinery/camera/autoname + var/static/list/by_area + +/obj/machinery/camera/autoname/Initialize() + . = ..() + var/area/A = get_area(src) + if(!A) + return . + if(!by_area) + by_area = list() + if(!by_area[A.name]) + by_area[A.name] = list() + var/list/my_area = by_area[A.name] + my_area += src + var/number = my_area.len + + c_tag = "[A.name] #[number]" + +/obj/machinery/camera/autoname/Destroy() + var/area/A = get_area(src) + if(!A || !by_area || !by_area[A.name]) + return ..() + var/list/my_area = by_area[A.name] + my_area -= src + return ..() + +// CHECKS + +/obj/machinery/camera/proc/isEmpProof() + var/O = locate(/obj/item/stack/material/osmium) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isXRay() + var/obj/item/weapon/stock_parts/scanning_module/O = locate(/obj/item/weapon/stock_parts/scanning_module) in assembly.upgrades + if (O && O.rating >= 2) + return O + return null + +/obj/machinery/camera/proc/isMotion() + var/O = locate(/obj/item/device/assembly/prox_sensor) in assembly.upgrades + return O + +// UPGRADE PROCS + +/obj/machinery/camera/proc/upgradeEmpProof() + assembly.upgrades.Add(new /obj/item/stack/material/osmium(assembly)) + setPowerUsage() + update_coverage() + +/obj/machinery/camera/proc/upgradeXRay() + assembly.upgrades.Add(new /obj/item/weapon/stock_parts/scanning_module/adv(assembly)) + setPowerUsage() + update_coverage() + +/obj/machinery/camera/proc/upgradeMotion() + if(!isturf(loc)) + return //nooooo + assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) + setPowerUsage() + START_MACHINE_PROCESSING(src) + sense_proximity(callback = /atom/proc/HasProximity) + update_coverage() + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if (isXRay()) + mult++ + if (isMotion()) + mult++ + update_active_power_usage(mult * initial(active_power_usage)) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index d30ec8a07e8..8b1ec4256bf 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,283 +1,285 @@ -#define TRACKING_POSSIBLE 0 -#define TRACKING_NO_COVERAGE 1 -#define TRACKING_TERMINATE 2 - -/mob/living/silicon/ai/var/max_locations = 30 -/mob/living/silicon/ai/var/stored_locations[0] - -/proc/InvalidPlayerTurf(turf/T as turf) - return !(T?.z in using_map.player_levels) - -/mob/living/silicon/ai/proc/get_camera_list() - if(src.stat == 2) - return - - cameranet.process_sort() - - var/list/T = list() - for (var/obj/machinery/camera/C in cameranet.cameras) - var/list/tempnetwork = C.network&src.network - if (tempnetwork.len) - T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C - - track = new() - track.cameras = T - return T - - -/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) - set category = "AI Commands" - set name = "Show Camera List" - - if(check_unable()) - return - - if (!camera) - return 0 - - var/obj/machinery/camera/C = track.cameras[camera] - src.eyeobj.setLoc(C) - - return - -/mob/living/silicon/ai/proc/ai_store_location(loc as text) - set category = "AI Commands" - set name = "Store Camera Location" - set desc = "Stores your current camera location by the given name" - - loc = sanitize(loc) - if(!loc) - to_chat(src, "Must supply a location name") - return - - if(stored_locations.len >= max_locations) - to_chat(src, "Cannot store additional locations. Remove one first") - return - - if(loc in stored_locations) - to_chat(src, "There is already a stored location by this name") - return - - var/L = src.eyeobj.getLoc() - if (InvalidPlayerTurf(get_turf(L))) - to_chat(src, "Unable to store this location") - return - - stored_locations[loc] = L - to_chat(src, "Location '[loc]' stored") - -/mob/living/silicon/ai/proc/sorted_stored_locations() - return sortList(stored_locations) - -/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Goto Camera Location" - set desc = "Returns to the selected camera location" - - if (!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - var/L = stored_locations[loc] - src.eyeobj.setLoc(L) - -/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Delete Camera Location" - set desc = "Deletes the selected camera location" - - if (!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - stored_locations.Remove(loc) - to_chat(src, "Location [loc] removed") - -// Used to allow the AI is write in mob names/camera name from the CMD line. -/datum/trackable - var/list/names = list() - var/list/namecounts = list() - var/list/humans = list() - var/list/others = list() - var/list/cameras = list() - -/mob/living/silicon/ai/proc/trackable_mobs() - if(usr.stat == 2) - return list() - - var/datum/trackable/TB = new() - for(var/mob/living/M in mob_list) - if(M == usr) - continue - if(M.tracking_status() != TRACKING_POSSIBLE) - continue - - var/name = M.name - if (name in TB.names) - TB.namecounts[name]++ - name = text("[] ([])", name, TB.namecounts[name]) - else - TB.names.Add(name) - TB.namecounts[name] = 1 - if(istype(M, /mob/living/carbon/human)) - TB.humans[name] = M - else - TB.others[name] = M - - var/list/targets = sortList(TB.humans) + sortList(TB.others) - src.track = TB - return targets - -/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs()) - set category = "AI Commands" - set name = "Follow With Camera" - set desc = "Select who you would like to track." - - if(src.stat == 2) - to_chat(src, "You can't follow [target_name] with cameras because you are dead!") - return - if(!target_name) - src.cameraFollow = null - - var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) - src.track = null - ai_actual_track(target) - -/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) - if(!cameraFollow) - return - - to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") - cameraFollow.tracking_cancelled() - cameraFollow = null - -/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target as mob) - if(!istype(target)) return FALSE - var/mob/living/silicon/ai/U = usr - - if(target == U.cameraFollow) - return TRUE - - if(U.cameraFollow) - U.ai_cancel_tracking() - U.cameraFollow = target - to_chat(U, "Now tracking [target.name] on camera.") - target.tracking_initiated() - - spawn (0) - while (U.cameraFollow == target) - if (U.cameraFollow == null) - return - - switch(target.tracking_status()) - if(TRACKING_NO_COVERAGE) - to_chat(U, "Target is not near any active cameras.") - sleep(100) - continue - if(TRACKING_TERMINATE) - U.ai_cancel_tracking(1) - return - - if(U.eyeobj) - U.eyeobj.setLoc(get_turf(target), 0) - else - view_core() - return - sleep(10) - - return TRUE - -/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob) - if (!istype(user)) - return - if (!src.can_use()) - return - user.eyeobj.setLoc(get_turf(src)) - - -/mob/living/silicon/ai/attack_ai(var/mob/user as mob) - ai_camera_list() - -/proc/camera_sort(list/L) - var/obj/machinery/camera/a - var/obj/machinery/camera/b - - for (var/i = L.len, i > 0, i--) - for (var/j = 1 to i - 1) - a = L[j] - b = L[j + 1] - if (a.c_tag_order != b.c_tag_order) - if (a.c_tag_order > b.c_tag_order) - L.Swap(j, j + 1) - else - if (sorttext(a.c_tag, b.c_tag) < 0) - L.Swap(j, j + 1) - return L - - -/mob/living/proc/near_camera() - if (!isturf(loc)) - return 0 - else if(!cameranet.checkVis(src)) - return 0 - return 1 - -/mob/living/proc/tracking_status() - // Easy checks first. - // Don't detect mobs on CentCom. Since the wizard den is on CentCom, we only need this. - var/obj/item/weapon/card/id/id = GetIdCard() - if(id && id.prevent_tracking()) - return TRACKING_TERMINATE - if(InvalidPlayerTurf(get_turf(src))) - return TRACKING_TERMINATE - if(invisibility >= INVISIBILITY_LEVEL_ONE) //cloaked - return TRACKING_TERMINATE - if(digitalcamo) - return TRACKING_TERMINATE - if(alpha < 127) // For lings and possible future alpha-based cloaks. - return TRACKING_TERMINATE - if(istype(loc,/obj/effect/dummy)) - return TRACKING_TERMINATE - - // Now, are they viewable by a camera? (This is last because it's the most intensive check) - return near_camera() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE - -/mob/living/silicon/robot/tracking_status() - . = ..() - if(. == TRACKING_NO_COVERAGE) - return camera && camera.can_use() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE - -/mob/living/carbon/human/tracking_status() - //Cameras can't track people wearing an agent card or a ninja hood. - if(istype(head, /obj/item/clothing/head/helmet/space/rig)) - var/obj/item/clothing/head/helmet/space/rig/helmet = head - if(helmet.prevent_track()) - return TRACKING_TERMINATE - - . = ..() - if(. == TRACKING_TERMINATE) - return - - if(. == TRACKING_NO_COVERAGE) - var/turf/T = get_turf(src) - if(T && (T.z in using_map.station_levels) && hassensorlevel(src, SUIT_SENSOR_TRACKING)) - return TRACKING_POSSIBLE - -/mob/living/proc/tracking_initiated() - -/mob/living/silicon/robot/tracking_initiated() - tracking_entities++ - if(tracking_entities == 1 && has_zeroth_law()) - to_chat(src, "Internal camera is currently being accessed.") - -/mob/living/proc/tracking_cancelled() - -/mob/living/silicon/robot/tracking_initiated() - tracking_entities-- - if(!tracking_entities && has_zeroth_law()) - to_chat(src, "Internal camera is no longer being accessed.") - - -#undef TRACKING_POSSIBLE -#undef TRACKING_NO_COVERAGE -#undef TRACKING_TERMINATE +#define TRACKING_POSSIBLE 0 +#define TRACKING_NO_COVERAGE 1 +#define TRACKING_TERMINATE 2 + +/mob/living/silicon/ai/var/max_locations = 30 +/mob/living/silicon/ai/var/stored_locations[0] + +/proc/InvalidPlayerTurf(turf/T as turf) + return !(T?.z in using_map.player_levels) + +/mob/living/silicon/ai/proc/get_camera_list() + if(src.stat == 2) + return + + cameranet.process_sort() + + var/list/T = list() + for (var/obj/machinery/camera/C in cameranet.cameras) + var/list/tempnetwork = C.network&src.network + if (tempnetwork.len) + T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C + + track = new() + track.cameras = T + return T + + +/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) + set category = "AI Commands" + set name = "Show Camera List" + + if(check_unable()) + return + + if (!camera) + return 0 + + var/obj/machinery/camera/C = track.cameras[camera] + src.eyeobj.setLoc(C) + + return + +/mob/living/silicon/ai/proc/ai_store_location(loc as text) + set category = "AI Commands" + set name = "Store Camera Location" + set desc = "Stores your current camera location by the given name" + + loc = sanitize(loc) + if(!loc) + to_chat(src, "Must supply a location name") + return + + if(stored_locations.len >= max_locations) + to_chat(src, "Cannot store additional locations. Remove one first") + return + + if(loc in stored_locations) + to_chat(src, "There is already a stored location by this name") + return + + var/L = src.eyeobj.getLoc() + if (InvalidPlayerTurf(get_turf(L))) + to_chat(src, "Unable to store this location") + return + + stored_locations[loc] = L + to_chat(src, "Location '[loc]' stored") + +/mob/living/silicon/ai/proc/sorted_stored_locations() + return sortList(stored_locations) + +/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Goto Camera Location" + set desc = "Returns to the selected camera location" + + if (!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + var/L = stored_locations[loc] + src.eyeobj.setLoc(L) + +/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Delete Camera Location" + set desc = "Deletes the selected camera location" + + if (!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + stored_locations.Remove(loc) + to_chat(src, "Location [loc] removed") + +// Used to allow the AI is write in mob names/camera name from the CMD line. +/datum/trackable + var/list/names = list() + var/list/namecounts = list() + var/list/humans = list() + var/list/others = list() + var/list/cameras = list() + +/mob/living/silicon/ai/proc/trackable_mobs() + if(usr.stat == 2) + return list() + + var/datum/trackable/TB = new() + for(var/mob/living/M in mob_list) + if(M == usr) + continue + if(M.tracking_status() != TRACKING_POSSIBLE) + continue + + var/name = M.name + if (name in TB.names) + TB.namecounts[name]++ + name = text("[] ([])", name, TB.namecounts[name]) + else + TB.names.Add(name) + TB.namecounts[name] = 1 + if(istype(M, /mob/living/carbon/human)) + TB.humans[name] = M + else + TB.others[name] = M + + var/list/targets = sortList(TB.humans) + sortList(TB.others) + src.track = TB + return targets + +/mob/living/silicon/ai/proc/ai_camera_track(var/target_name in trackable_mobs()) + set category = "AI Commands" + set name = "Follow With Camera" + set desc = "Select who you would like to track." + + if(src.stat == 2) + to_chat(src, "You can't follow [target_name] with cameras because you are dead!") + return + if(!target_name) + src.cameraFollow = null + + var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + src.track = null + ai_actual_track(target) + +/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) + if(!cameraFollow) + return + + to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") + cameraFollow.tracking_cancelled() + cameraFollow = null + +/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target as mob) + if(!istype(target)) return FALSE + var/mob/living/silicon/ai/U = usr + + if(target == U.cameraFollow) + return TRUE + + if(U.cameraFollow) + U.ai_cancel_tracking() + U.cameraFollow = target + to_chat(U, "Now tracking [target.name] on camera.") + target.tracking_initiated() + + spawn (0) + while (U.cameraFollow == target) + if (U.cameraFollow == null) + return + + switch(target.tracking_status()) + if(TRACKING_NO_COVERAGE) + to_chat(U, "Target is not near any active cameras.") + sleep(100) + continue + if(TRACKING_TERMINATE) + U.ai_cancel_tracking(1) + return + + if(U.eyeobj) + U.eyeobj.setLoc(get_turf(target), 0) + else + view_core() + return + sleep(10) + + return TRUE + +/obj/machinery/camera/attack_ai(var/mob/living/silicon/ai/user as mob) + if (!istype(user)) + return + if (!src.can_use()) + return + user.eyeobj.setLoc(get_turf(src)) + + +/mob/living/silicon/ai/attack_ai(var/mob/user as mob) + ai_camera_list() + +/proc/camera_sort(list/L) + var/obj/machinery/camera/a + var/obj/machinery/camera/b + + for (var/i = L.len, i > 0, i--) + for (var/j = 1 to i - 1) + a = L[j] + b = L[j + 1] + if (a.c_tag_order != b.c_tag_order) + if (a.c_tag_order > b.c_tag_order) + L.Swap(j, j + 1) + else + if (sorttext(a.c_tag, b.c_tag) < 0) + L.Swap(j, j + 1) + return L + + +/mob/living/proc/near_camera() + if (!isturf(loc)) + return 0 + else if(!cameranet.checkVis(src)) + return 0 + return 1 + +/mob/living/proc/tracking_status() + // Easy checks first. + // Don't detect mobs on CentCom. Since the wizard den is on CentCom, we only need this. + var/obj/item/weapon/card/id/id = GetIdCard() + if(id && id.prevent_tracking()) + return TRACKING_TERMINATE + var/turf/pos = get_turf(src) + var/area/B = pos?.loc // No cam tracking in dorms! + if(InvalidPlayerTurf(pos) || B.block_tracking) + return TRACKING_TERMINATE + if(invisibility >= INVISIBILITY_LEVEL_ONE) //cloaked + return TRACKING_TERMINATE + if(digitalcamo) + return TRACKING_TERMINATE + if(alpha < 127) // For lings and possible future alpha-based cloaks. + return TRACKING_TERMINATE + if(istype(loc,/obj/effect/dummy)) + return TRACKING_TERMINATE + + // Now, are they viewable by a camera? (This is last because it's the most intensive check) + return near_camera() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE + +/mob/living/silicon/robot/tracking_status() + . = ..() + if(. == TRACKING_NO_COVERAGE) + return camera && camera.can_use() ? TRACKING_POSSIBLE : TRACKING_NO_COVERAGE + +/mob/living/carbon/human/tracking_status() + //Cameras can't track people wearing an agent card or a ninja hood. + if(istype(head, /obj/item/clothing/head/helmet/space/rig)) + var/obj/item/clothing/head/helmet/space/rig/helmet = head + if(helmet.prevent_track()) + return TRACKING_TERMINATE + + . = ..() + if(. == TRACKING_TERMINATE) + return + + if(. == TRACKING_NO_COVERAGE) + var/turf/T = get_turf(src) + if(T && (T.z in using_map.station_levels) && hassensorlevel(src, SUIT_SENSOR_TRACKING)) + return TRACKING_POSSIBLE + +/mob/living/proc/tracking_initiated() + +/mob/living/silicon/robot/tracking_initiated() + tracking_entities++ + if(tracking_entities == 1 && has_zeroth_law()) + to_chat(src, "Internal camera is currently being accessed.") + +/mob/living/proc/tracking_cancelled() + +/mob/living/silicon/robot/tracking_initiated() + tracking_entities-- + if(!tracking_entities && has_zeroth_law()) + to_chat(src, "Internal camera is no longer being accessed.") + + +#undef TRACKING_POSSIBLE +#undef TRACKING_NO_COVERAGE +#undef TRACKING_TERMINATE diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 8439c9ff156..28255c395ad 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,140 +1,140 @@ -/obj/machinery/cell_charger - name = "heavy-duty cell charger" - desc = "A much more powerful version of the standard recharger that is specially designed for charging power cells." - icon = 'icons/obj/power.dmi' - icon_state = "ccharger0" - anchored = 1 - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 60000 //60 kW. (this the power drawn when charging) - var/efficiency = 60000 //will provide the modified power rate when upgraded - power_channel = EQUIP - var/obj/item/weapon/cell/charging = null - var/chargelevel = -1 - circuit = /obj/item/weapon/circuitboard/cell_charger - -/obj/machinery/cell_charger/Initialize() - . = ..() - default_apply_parts() - add_overlay("ccharger1") - -/obj/machinery/cell_charger/update_icon() - if(!anchored) - cut_overlays() - icon_state = "ccharger2" - - if(charging && !(stat & (BROKEN|NOPOWER))) - var/newlevel = round(charging.percent() * 4.0 / 99) - //to_world("nl: [newlevel]") - - if(chargelevel != newlevel) - - cut_overlays() - add_overlay("ccharger-o[newlevel]") - - chargelevel = newlevel - - add_overlay(image(charging.icon, charging.icon_state)) - add_overlay("ccharger-[charging.connector_type]-on") - - else if(anchored) - cut_overlays() - icon_state = "ccharger0" - add_overlay("ccharger1") - -/obj/machinery/cell_charger/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 5) - . += "[charging ? "[charging]" : "Nothing"] is in [src]." - if(charging) - . += "Current charge: [charging.charge] / [charging.maxcharge]" - -/obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user) - if(stat & BROKEN) - return - - if(istype(W, /obj/item/weapon/cell) && anchored) - if(istype(W, /obj/item/weapon/cell/device)) - to_chat(user, "\The [src] isn't fitted for that type of cell.") - return - if(charging) - to_chat(user, "There is already [charging] in [src].") - return - else - var/area/a = loc.loc // Gets our locations location, like a dream within a dream - if(!isarea(a)) - return - if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - to_chat(user, "\The [src] blinks red as you try to insert [W]!") - return - - user.drop_item() - W.loc = src - charging = W - user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") - chargelevel = -1 - update_icon() - else if(W.is_wrench()) - if(charging) - to_chat(user, "Remove [charging] first!") - return - - anchored = !anchored - to_chat(user, "You [anchored ? "attach" : "detach"] [src] [anchored ? "to" : "from"] the ground") - playsound(src, W.usesound, 75, 1) - update_icon() - else if(default_deconstruction_screwdriver(user, W)) - return - else if(default_deconstruction_crowbar(user, W)) - return - else if(default_part_replacement(user, W)) - return - -/obj/machinery/cell_charger/attack_hand(mob/user) - add_fingerprint(user) - - if(charging) - user.put_in_hands(charging) - charging.update_icon() - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - - charging = null - chargelevel = -1 - update_icon() - -/obj/machinery/cell_charger/attack_ai(mob/user) - if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Borgs can remove the cell if they are near enough - if(charging) - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - charging.loc = src.loc - charging.update_icon() - charging = null - update_icon() - -/obj/machinery/cell_charger/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - if(charging) - charging.emp_act(severity) - ..(severity) - - -/obj/machinery/cell_charger/process() - //to_world("ccpt [charging] [stat]") - if((stat & (BROKEN|NOPOWER)) || !anchored) - update_use_power(USE_POWER_OFF) - return - - if(charging && !charging.fully_charged()) - charging.give(efficiency*CELLRATE) - update_use_power(USE_POWER_ACTIVE) - - update_icon() - else - update_use_power(USE_POWER_IDLE) - -/obj/machinery/cell_charger/RefreshParts() - var/E = 0 - for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) - E += C.rating +/obj/machinery/cell_charger + name = "heavy-duty cell charger" + desc = "A much more powerful version of the standard recharger that is specially designed for charging power cells." + icon = 'icons/obj/power.dmi' + icon_state = "ccharger0" + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 60000 //60 kW. (this the power drawn when charging) + var/efficiency = 60000 //will provide the modified power rate when upgraded + power_channel = EQUIP + var/obj/item/weapon/cell/charging = null + var/chargelevel = -1 + circuit = /obj/item/weapon/circuitboard/cell_charger + +/obj/machinery/cell_charger/Initialize() + . = ..() + default_apply_parts() + add_overlay("ccharger1") + +/obj/machinery/cell_charger/update_icon() + if(!anchored) + cut_overlays() + icon_state = "ccharger2" + + if(charging && !(stat & (BROKEN|NOPOWER))) + var/newlevel = round(charging.percent() * 4.0 / 99) + //to_world("nl: [newlevel]") + + if(chargelevel != newlevel) + + cut_overlays() + add_overlay("ccharger-o[newlevel]") + + chargelevel = newlevel + + add_overlay(image(charging.icon, charging.icon_state)) + add_overlay("ccharger-[charging.connector_type]-on") + + else if(anchored) + cut_overlays() + icon_state = "ccharger0" + add_overlay("ccharger1") + +/obj/machinery/cell_charger/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 5) + . += "[charging ? "[charging]" : "Nothing"] is in [src]." + if(charging) + . += "Current charge: [charging.charge] / [charging.maxcharge]" + +/obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user) + if(stat & BROKEN) + return + + if(istype(W, /obj/item/weapon/cell) && anchored) + if(istype(W, /obj/item/weapon/cell/device)) + to_chat(user, "\The [src] isn't fitted for that type of cell.") + return + if(charging) + to_chat(user, "There is already [charging] in [src].") + return + else + var/area/a = loc.loc // Gets our locations location, like a dream within a dream + if(!isarea(a)) + return + if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! + to_chat(user, "\The [src] blinks red as you try to insert [W]!") + return + + user.drop_item() + W.loc = src + charging = W + user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") + chargelevel = -1 + update_icon() + else if(W.has_tool_quality(TOOL_WRENCH)) + if(charging) + to_chat(user, "Remove [charging] first!") + return + + anchored = !anchored + to_chat(user, "You [anchored ? "attach" : "detach"] [src] [anchored ? "to" : "from"] the ground") + playsound(src, W.usesound, 75, 1) + update_icon() + else if(default_deconstruction_screwdriver(user, W)) + return + else if(default_deconstruction_crowbar(user, W)) + return + else if(default_part_replacement(user, W)) + return + +/obj/machinery/cell_charger/attack_hand(mob/user) + add_fingerprint(user) + + if(charging) + user.put_in_hands(charging) + charging.update_icon() + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + + charging = null + chargelevel = -1 + update_icon() + +/obj/machinery/cell_charger/attack_ai(mob/user) + if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) // Borgs can remove the cell if they are near enough + if(charging) + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + charging.loc = src.loc + charging.update_icon() + charging = null + update_icon() + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(charging) + charging.emp_act(severity) + ..(severity) + + +/obj/machinery/cell_charger/process() + //to_world("ccpt [charging] [stat]") + if((stat & (BROKEN|NOPOWER)) || !anchored) + update_use_power(USE_POWER_OFF) + return + + if(charging && !charging.fully_charged()) + charging.give(efficiency*CELLRATE) + update_use_power(USE_POWER_ACTIVE) + + update_icon() + else + update_use_power(USE_POWER_IDLE) + +/obj/machinery/cell_charger/RefreshParts() + var/E = 0 + for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) + E += C.rating efficiency = active_power_usage * (1+ (E - 1)*0.5) \ No newline at end of file diff --git a/code/game/machinery/clawmachine.dm b/code/game/machinery/clawmachine.dm index 1896d37b97c..08af125e20d 100644 --- a/code/game/machinery/clawmachine.dm +++ b/code/game/machinery/clawmachine.dm @@ -100,7 +100,7 @@ if(busy) to_chat(user,"The claw machine is currently running. ") return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) playsound(src, W.usesound, 100, 1) if(anchored) user.visible_message("[user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 49c917f312e..da958671e79 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,590 +1,590 @@ -//Cloning revival method. -//The pod handles the actual cloning while the computer manages the clone profiles - -//Potential replacement for genetics revives or something I dunno (?) - -//Find a dead mob with a brain and client. -/proc/find_dead_player(var/find_key) - if(isnull(find_key)) - return - - var/mob/selected = null - for(var/mob/living/M in player_list) - //Dead people only thanks! - if((M.stat != 2) || (!M.client)) - continue - //They need a brain! - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(!H.has_brain()) - continue - if(M.ckey == find_key) - selected = M - break - return selected - -#define CLONE_BIOMASS 30 //VOREstation Edit -#define MINIMUM_HEAL_LEVEL 40 - -/obj/machinery/clonepod - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = TRUE - anchored = TRUE - circuit = /obj/item/weapon/circuitboard/clonepod - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(access_genetics) // For premature unlocking. - var/mob/living/occupant - var/heal_level = 20 // The clone is released once its health reaches this level. - var/heal_rate = 1 - var/locked = 0 - var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. - var/mess = 0 // Need to clean out it if it's full of exploded clone. - var/attempting = 0 // One clone attempt at a time thanks - var/eject_wait = 0 // Don't eject them as soon as they are created fuckkk - - var/list/containers = list() // Beakers for our liquid biomass - var/container_limit = 3 // How many beakers can the machine hold? - - var/speed_coeff - var/efficiency - -/obj/machinery/clonepod/Initialize() - . = ..() - default_apply_parts() - update_icon() - -/obj/machinery/clonepod/attack_ai(mob/user as mob) - - add_hiddenprint(user) - return attack_hand(user) - -/obj/machinery/clonepod/attack_hand(mob/user as mob) - if((isnull(occupant)) || (stat & NOPOWER)) - return - if((!isnull(occupant)) && (occupant.stat != 2)) - var/completion = (100 * ((occupant.health + 50) / (heal_level + 100))) // Clones start at -150 health - to_chat(user, "Current clone cycle is [round(completion)]% complete.") - return - -//Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R) - if(mess || attempting) - return 0 - var/datum/mind/clonemind = locate(R.mind) - - if(!istype(clonemind, /datum/mind)) //not a mind - return 0 - if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body - return 0 - if(clonemind.active) //somebody is using that mind - if(ckey(clonemind.key) != R.ckey) - return 0 - else - for(var/mob/observer/dead/G in player_list) - if(G.ckey == R.ckey) - if(G.can_reenter_corpse) - break - else - return 0 - - for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan - if(istype(modifier_type, /datum/modifier/no_clone)) - return 0 - - // Remove biomass when the cloning is started, rather than when the guy pops out - remove_biomass(CLONE_BIOMASS) - - attempting = 1 //One at a time!! - locked = 1 - - eject_wait = 1 - spawn(30) - eject_wait = 0 - - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) - occupant = H - - if(!R.dna.real_name) //to prevent null names - R.dna.real_name = "clone ([rand(0,999)])" - H.real_name = R.dna.real_name - H.gender = R.gender - H.descriptors = R.body_descriptors - - //Get the clone body ready - H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite - H.Paralyse(4) - - //Here let's calculate their health so the pod doesn't immediately eject them!!! - H.updatehealth() - - clonemind.transfer_to(H) - H.ckey = R.ckey - to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
    Your recent memories are fuzzy, and it's hard to remember anything from today...

    So this is what cloning feels like?") - - // -- Mode/mind specific stuff goes here - callHook("clone", list(H)) - update_antag_icons(H.mind) - // -- End mode specific stuff - - if(!R.dna) - H.dna = new /datum/dna() - H.dna.real_name = H.real_name - else - H.dna = R.dna - H.UpdateAppearance() - H.sync_organ_dna() - if(heal_level < 60) - randmutb(H) //Sometimes the clones come out wrong. - H.dna.UpdateSE() - H.dna.UpdateUI() - - H.set_cloned_appearance() - update_icon() - - // A modifier is added which makes the new clone be unrobust. - var/modifier_lower_bound = 25 MINUTES - var/modifier_upper_bound = 40 MINUTES - - // Upgraded cloners can reduce the time of the modifier, up to 80% - var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1) - clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase. - modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1) - modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1) - - H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound)) - - // Modifier that doesn't do anything. - H.add_modifier(/datum/modifier/cloned) - - // This is really stupid. - for(var/modifier_type in R.genetic_modifiers) - H.add_modifier(modifier_type) - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - H.flavor_texts = R.flavor.Copy() - H.suiciding = 0 - attempting = 0 - return 1 - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/process() - if(stat & NOPOWER) //Autoeject if power is lost - if(occupant) - locked = 0 - go_out() - return - - if((occupant) && (occupant.loc == src)) - if((occupant.stat == DEAD) || (occupant.suiciding) || !occupant.key) //Autoeject corpses and suiciding dudes. - locked = 0 - go_out() - connected_message("Clone Rejected: Deceased.") - return - - else if(occupant.health < heal_level && occupant.getCloneLoss() > 0) - occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - occupant.adjustCloneLoss(-2 * heal_rate) - - //Premature clones may have brain damage. - occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1))) - - //So clones don't die of oxyloss in a running pod. - if(occupant.reagents.get_reagent_amount("inaprovaline") < 30) - occupant.reagents.add_reagent("inaprovaline", 60) - occupant.Sleeping(30) - //Also heal some oxyloss ourselves because inaprovaline is so bad at preventing it!! - occupant.adjustOxyLoss(-4) - - use_power(7500) //This might need tweaking. - return - - else if((occupant.health >= heal_level || occupant.health == occupant.getMaxHealth()) && (!eject_wait)) - playsound(src, 'sound/machines/medbayscanner1.ogg', 50, 1) - audible_message("\The [src] signals that the cloning process is complete.", runemessage = "ding") - connected_message("Cloning Process Complete.") - locked = 0 - go_out() - return - - else if((!occupant) || (occupant.loc != src)) - occupant = null - if(locked) - locked = 0 - return - - return - -//Let's unlock this early I guess. Might be too early, needs tweaking. -/obj/machinery/clonepod/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(isnull(occupant)) - if(default_deconstruction_screwdriver(user, W)) - return - if(default_deconstruction_crowbar(user, W)) - return - if(default_part_replacement(user, W)) - return - if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(!check_access(W)) - to_chat(user, "Access Denied.") - return - if((!locked) || (isnull(occupant))) - return - if((occupant.health < -20) && (occupant.stat != 2)) - to_chat(user, "Access Refused.") - return - else - locked = 0 - to_chat(user, "System unlocked.") - else if(istype(W,/obj/item/weapon/reagent_containers/glass)) - if(LAZYLEN(containers) >= container_limit) - to_chat(user, "\The [src] has too many containers loaded!") - else if(do_after(user, 1 SECOND)) - user.visible_message("[user] has loaded \the [W] into \the [src].", "You load \the [W] into \the [src].") - containers += W - user.drop_item() - W.forceMove(src) - return - else if(W.is_wrench()) - if(locked && (anchored || occupant)) - to_chat(user, "Can not do that while [src] is in use.") - else - if(anchored) - anchored = FALSE - connected.pods -= src - connected = null - else - anchored = TRUE - playsound(src, W.usesound, 100, 1) - if(anchored) - user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.") - else - user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.") - else if(istype(W, /obj/item/device/multitool)) - var/obj/item/device/multitool/M = W - M.connecting = src - to_chat(user, "You load connection data from [src] to [M].") - M.update_icon() - return - else - ..() - -/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) - if(isnull(occupant)) - return - to_chat(user, "You force an emergency ejection.") - locked = 0 - go_out() - return 1 - -//Put messages in the connected computer's temp var for display. -/obj/machinery/clonepod/proc/connected_message(var/message) - if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) - return 0 - if(!message) - return 0 - - connected.temp = "[name] : [message]" - connected.updateUsrDialog() - return 1 - -/obj/machinery/clonepod/RefreshParts() - ..() - speed_coeff = 0 - efficiency = 0 - for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts) - efficiency += S.rating - for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) - speed_coeff += P.rating - heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) - -/obj/machinery/clonepod/proc/get_completion() - . = (100 * ((occupant.health + 100) / (heal_level + 100))) - -/obj/machinery/clonepod/verb/eject() - set name = "Eject Cloner" - set category = "Object" - set src in oview(1) - - if(usr.stat != 0) - return - go_out() - add_fingerprint(usr) - return - -/obj/machinery/clonepod/proc/go_out() - if(locked) - return - - if(mess) //Clean that mess and dump those gibs! - mess = 0 - gibs(src.loc) - update_icon() - return - - if(!(occupant)) - return - - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.loc = src.loc - eject_wait = 0 //If it's still set somehow. - if(ishuman(occupant)) //Need to be safe. - var/mob/living/carbon/human/patient = occupant - if(!(patient.species.flags & NO_SCAN)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled. - domutcheck(occupant) //Waiting until they're out before possible transforming. - occupant = null - - update_icon() - return - -// Returns the total amount of biomass reagent in all of the pod's stored containers -/obj/machinery/clonepod/proc/get_biomass() - var/biomass_count = 0 - if(LAZYLEN(containers)) - for(var/obj/item/weapon/reagent_containers/glass/G in containers) - for(var/datum/reagent/R in G.reagents.reagent_list) - if(R.id == "biomass") - biomass_count += R.volume - - return biomass_count - -// Removes [amount] biomass, spread across all containers. Doesn't have any check that you actually HAVE enough biomass, though. -/obj/machinery/clonepod/proc/remove_biomass(var/amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone - var/to_remove = 0 // Tracks how much biomass has been found so far - if(LAZYLEN(containers)) - for(var/obj/item/weapon/reagent_containers/glass/G in containers) - if(to_remove < amount) //If we have what we need, we can stop. Checked every time we switch beakers - for(var/datum/reagent/R in G.reagents.reagent_list) - if(R.id == "biomass") // Finds Biomass - var/need_remove = max(0, amount - to_remove) //Figures out how much biomass is in this container - if(R.volume >= need_remove) //If we have more than enough in this beaker, only take what we need - R.remove_self(need_remove) - to_remove = amount - else //Otherwise, take everything and move on - to_remove += R.volume - R.remove_self(R.volume) - else - continue - else - return 1 - return 0 - -// Empties all of the beakers from the cloning pod, used to refill it -/obj/machinery/clonepod/verb/empty_beakers() - set name = "Eject Beakers" - set category = "Object" - set src in oview(1) - - if(usr.stat != 0) - return - - add_fingerprint(usr) - drop_beakers() - return - -// Actually does all of the beaker dropping -// Returns 1 if it succeeds, 0 if it fails. Added in case someone wants to add messages to the user. -/obj/machinery/clonepod/proc/drop_beakers() - if(LAZYLEN(containers)) - var/turf/T = get_turf(src) - if(T) - for(var/obj/item/weapon/reagent_containers/glass/G in containers) - G.forceMove(T) - containers -= G - return 1 - return 0 - -/obj/machinery/clonepod/proc/malfunction() - if(occupant) - connected_message("Critical Error!") - mess = 1 - update_icon() - occupant.ghostize() - spawn(5) - qdel(occupant) - return - -/obj/machinery/clonepod/relaymove(mob/user as mob) - if(user.stat) - return - go_out() - return - -/obj/machinery/clonepod/emp_act(severity) - if(prob(100/severity)) - malfunction() - ..() - -/obj/machinery/clonepod/ex_act(severity) - switch(severity) - if(1.0) - for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc - ex_act(severity) - qdel(src) - return - if(2.0) - if(prob(50)) - for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc - ex_act(severity) - qdel(src) - return - if(3.0) - if(prob(25)) - for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc - ex_act(severity) - qdel(src) - return - else - return - -/obj/machinery/clonepod/update_icon() - ..() - icon_state = "pod_0" - if(occupant && !(stat & NOPOWER)) - icon_state = "pod_1" - else if(mess) - icon_state = "pod_g" - - -/obj/machinery/clonepod/full/New() - ..() - for(var/i = 1 to container_limit) - containers += new /obj/item/weapon/reagent_containers/glass/bottle/biomass(src) - -//Health Tracker Implant - -/obj/item/weapon/implant/health - name = "health implant" - known_implant = TRUE - var/healthstring = "" - -/obj/item/weapon/implant/health/proc/sensehealth() - if(!implanted) - return "ERROR" - else - if(isliving(implanted)) - var/mob/living/L = implanted - healthstring = "[round(L.getOxyLoss())] - [round(L.getFireLoss())] - [round(L.getToxLoss())] - [round(L.getBruteLoss())]" - if(!healthstring) - healthstring = "ERROR" - return healthstring - -//Disk stuff. -//The return of data disks?? Just for transferring between genetics machine/cloning machine. -//TO-DO: Make the genetics machine accept them. -/obj/item/weapon/disk/data - name = "Cloning Data Disk" - icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit - icon_state = "data-red" //VOREStation Edit - item_state = "card-id" - w_class = ITEMSIZE_SMALL - var/datum/dna2/record/buf = null - var/read_only = 0 //Well,it's still a floppy disk - -/obj/item/weapon/disk/data/proc/initializeDisk() - buf = new - buf.dna=new - -/obj/item/weapon/disk/data/demo - name = "data disk - 'God Emperor of Mankind'" - read_only = 1 - -/obj/item/weapon/disk/data/demo/New() - initializeDisk() - buf.types=DNA2_BUF_UE|DNA2_BUF_UI - //data = "066000033000000000AF00330660FF4DB002690" - //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff - buf.dna.real_name="God Emperor of Mankind" - buf.dna.unique_enzymes = md5(buf.dna.real_name) - buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0xAF0,0x033,0x066,0x0FF,0x4DB,0x002,0x690) - //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff - buf.dna.UpdateUI() - -/obj/item/weapon/disk/data/monkey - name = "data disk - 'Mr. Muggles'" - read_only = 1 - -/obj/item/weapon/disk/data/monkey/New() - ..() - initializeDisk() - buf.types=DNA2_BUF_SE - var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) - for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) - new_SE += rand(1,1024) - buf.dna.SE=new_SE - buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) - -/obj/item/weapon/disk/data/New() - ..() - var/diskcolor = pick(0,1,2) - icon_state = "datadisk[diskcolor]" - -/obj/item/weapon/disk/data/attack_self(mob/user as mob) - read_only = !read_only - to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") - -/obj/item/weapon/disk/data/examine(mob/user) - . = ..() - . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." - -/* - * Diskette Box - */ - -/obj/item/weapon/storage/box/disks - name = "Diskette Box" - icon_state = "disk_kit" - -/obj/item/weapon/storage/box/disks/New() - ..() - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/weapon/paper/Cloning - name = "H-87 Cloning Apparatus Manual" - info = {"

    Getting Started

    - Congratulations, your station has purchased the H-87 industrial cloning device!
    - Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    - That's all there is to it!
    - Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    -

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. - Profile Deletion has been restricted to \[Station Head\] level access.

    -

    Cloning from a profile

    - Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    - Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    -
    -

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. - The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    - Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    -

    Profile Management

    -

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. - These diskettes are used to transfer genetic information between machines and profiles. - A load/save dialog will become available in each profile if a disk is inserted.


    - A good diskette is a great way to counter aforementioned genetic drift!
    -
    - This technology produced under license from Thinktronic Systems, LTD."} - -//SOME SCRAPS I GUESS -/* EMP grenade/spell effect - if(istype(A, /obj/machinery/clonepod)) - A:malfunction() -*/ +//Cloning revival method. +//The pod handles the actual cloning while the computer manages the clone profiles + +//Potential replacement for genetics revives or something I dunno (?) + +//Find a dead mob with a brain and client. +/proc/find_dead_player(var/find_key) + if(isnull(find_key)) + return + + var/mob/selected = null + for(var/mob/living/M in player_list) + //Dead people only thanks! + if((M.stat != 2) || (!M.client)) + continue + //They need a brain! + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(!H.has_brain()) + continue + if(M.ckey == find_key) + selected = M + break + return selected + +#define CLONE_BIOMASS 30 //VOREstation Edit +#define MINIMUM_HEAL_LEVEL 40 + +/obj/machinery/clonepod + name = "cloning pod" + desc = "An electronically-lockable pod for growing organic tissue." + density = TRUE + anchored = TRUE + circuit = /obj/item/weapon/circuitboard/clonepod + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_0" + req_access = list(access_genetics) // For premature unlocking. + var/mob/living/occupant + var/heal_level = 20 // The clone is released once its health reaches this level. + var/heal_rate = 1 + var/locked = 0 + var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. + var/mess = 0 // Need to clean out it if it's full of exploded clone. + var/attempting = 0 // One clone attempt at a time thanks + var/eject_wait = 0 // Don't eject them as soon as they are created fuckkk + + var/list/containers = list() // Beakers for our liquid biomass + var/container_limit = 3 // How many beakers can the machine hold? + + var/speed_coeff + var/efficiency + +/obj/machinery/clonepod/Initialize() + . = ..() + default_apply_parts() + update_icon() + +/obj/machinery/clonepod/attack_ai(mob/user as mob) + + add_hiddenprint(user) + return attack_hand(user) + +/obj/machinery/clonepod/attack_hand(mob/user as mob) + if((isnull(occupant)) || (stat & NOPOWER)) + return + if((!isnull(occupant)) && (occupant.stat != 2)) + var/completion = (100 * ((occupant.health + 50) / (heal_level + 100))) // Clones start at -150 health + to_chat(user, "Current clone cycle is [round(completion)]% complete.") + return + +//Start growing a human clone in the pod! +/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R) + if(mess || attempting) + return 0 + var/datum/mind/clonemind = locate(R.mind) + + if(!istype(clonemind, /datum/mind)) //not a mind + return 0 + if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body + return 0 + if(clonemind.active) //somebody is using that mind + if(ckey(clonemind.key) != R.ckey) + return 0 + else + for(var/mob/observer/dead/G in player_list) + if(G.ckey == R.ckey) + if(G.can_reenter_corpse) + break + else + return 0 + + for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan + if(istype(modifier_type, /datum/modifier/no_clone)) + return 0 + + // Remove biomass when the cloning is started, rather than when the guy pops out + remove_biomass(CLONE_BIOMASS) + + attempting = 1 //One at a time!! + locked = 1 + + eject_wait = 1 + spawn(30) + eject_wait = 0 + + var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) + occupant = H + + if(!R.dna.real_name) //to prevent null names + R.dna.real_name = "clone ([rand(0,999)])" + H.real_name = R.dna.real_name + H.gender = R.gender + H.descriptors = R.body_descriptors + + //Get the clone body ready + H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite + H.Paralyse(4) + + //Here let's calculate their health so the pod doesn't immediately eject them!!! + H.updatehealth() + + clonemind.transfer_to(H) + H.ckey = R.ckey + to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
    Your recent memories are fuzzy, and it's hard to remember anything from today...

    So this is what cloning feels like?") + + // -- Mode/mind specific stuff goes here + callHook("clone", list(H)) + update_antag_icons(H.mind) + // -- End mode specific stuff + + if(!R.dna) + H.dna = new /datum/dna() + H.dna.real_name = H.real_name + else + H.dna = R.dna + H.UpdateAppearance() + H.sync_organ_dna() + if(heal_level < 60) + randmutb(H) //Sometimes the clones come out wrong. + H.dna.UpdateSE() + H.dna.UpdateUI() + + H.set_cloned_appearance() + update_icon() + + // A modifier is added which makes the new clone be unrobust. + var/modifier_lower_bound = 25 MINUTES + var/modifier_upper_bound = 40 MINUTES + + // Upgraded cloners can reduce the time of the modifier, up to 80% + var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1) + clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase. + modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1) + modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1) + + H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound)) + + // Modifier that doesn't do anything. + H.add_modifier(/datum/modifier/cloned) + + // This is really stupid. + for(var/modifier_type in R.genetic_modifiers) + H.add_modifier(modifier_type) + + for(var/datum/language/L in R.languages) + H.add_language(L.name) + + H.flavor_texts = R.flavor.Copy() + H.suiciding = 0 + attempting = 0 + return 1 + +//Grow clones to maturity then kick them out. FREELOADERS +/obj/machinery/clonepod/process() + if(stat & NOPOWER) //Autoeject if power is lost + if(occupant) + locked = 0 + go_out() + return + + if((occupant) && (occupant.loc == src)) + if((occupant.stat == DEAD) || (occupant.suiciding) || !occupant.key) //Autoeject corpses and suiciding dudes. + locked = 0 + go_out() + connected_message("Clone Rejected: Deceased.") + return + + else if(occupant.health < heal_level && occupant.getCloneLoss() > 0) + occupant.Paralyse(4) + + //Slowly get that clone healed and finished. + occupant.adjustCloneLoss(-2 * heal_rate) + + //Premature clones may have brain damage. + occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1))) + + //So clones don't die of oxyloss in a running pod. + if(occupant.reagents.get_reagent_amount("inaprovaline") < 30) + occupant.reagents.add_reagent("inaprovaline", 60) + occupant.Sleeping(30) + //Also heal some oxyloss ourselves because inaprovaline is so bad at preventing it!! + occupant.adjustOxyLoss(-4) + + use_power(7500) //This might need tweaking. + return + + else if((occupant.health >= heal_level || occupant.health == occupant.getMaxHealth()) && (!eject_wait)) + playsound(src, 'sound/machines/medbayscanner1.ogg', 50, 1) + audible_message("\The [src] signals that the cloning process is complete.", runemessage = "ding") + connected_message("Cloning Process Complete.") + locked = 0 + go_out() + return + + else if((!occupant) || (occupant.loc != src)) + occupant = null + if(locked) + locked = 0 + return + + return + +//Let's unlock this early I guess. Might be too early, needs tweaking. +/obj/machinery/clonepod/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(isnull(occupant)) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if(!check_access(W)) + to_chat(user, "Access Denied.") + return + if((!locked) || (isnull(occupant))) + return + if((occupant.health < -20) && (occupant.stat != 2)) + to_chat(user, "Access Refused.") + return + else + locked = 0 + to_chat(user, "System unlocked.") + else if(istype(W,/obj/item/weapon/reagent_containers/glass)) + if(LAZYLEN(containers) >= container_limit) + to_chat(user, "\The [src] has too many containers loaded!") + else if(do_after(user, 1 SECOND)) + user.visible_message("[user] has loaded \the [W] into \the [src].", "You load \the [W] into \the [src].") + containers += W + user.drop_item() + W.forceMove(src) + return + else if(W.has_tool_quality(TOOL_WRENCH)) + if(locked && (anchored || occupant)) + to_chat(user, "Can not do that while [src] is in use.") + else + if(anchored) + anchored = FALSE + connected.pods -= src + connected = null + else + anchored = TRUE + playsound(src, W.usesound, 100, 1) + if(anchored) + user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.") + else + user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.") + else if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/M = W + M.connecting = src + to_chat(user, "You load connection data from [src] to [M].") + M.update_icon() + return + else + ..() + +/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) + if(isnull(occupant)) + return + to_chat(user, "You force an emergency ejection.") + locked = 0 + go_out() + return 1 + +//Put messages in the connected computer's temp var for display. +/obj/machinery/clonepod/proc/connected_message(var/message) + if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) + return 0 + if(!message) + return 0 + + connected.temp = "[name] : [message]" + connected.updateUsrDialog() + return 1 + +/obj/machinery/clonepod/RefreshParts() + ..() + speed_coeff = 0 + efficiency = 0 + for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts) + efficiency += S.rating + for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) + speed_coeff += P.rating + heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) + +/obj/machinery/clonepod/proc/get_completion() + . = (100 * ((occupant.health + 100) / (heal_level + 100))) + +/obj/machinery/clonepod/verb/eject() + set name = "Eject Cloner" + set category = "Object" + set src in oview(1) + + if(usr.stat != 0) + return + go_out() + add_fingerprint(usr) + return + +/obj/machinery/clonepod/proc/go_out() + if(locked) + return + + if(mess) //Clean that mess and dump those gibs! + mess = 0 + gibs(src.loc) + update_icon() + return + + if(!(occupant)) + return + + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.loc = src.loc + eject_wait = 0 //If it's still set somehow. + if(ishuman(occupant)) //Need to be safe. + var/mob/living/carbon/human/patient = occupant + if(!(patient.species.flags & NO_SCAN)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled. + domutcheck(occupant) //Waiting until they're out before possible transforming. + occupant = null + + update_icon() + return + +// Returns the total amount of biomass reagent in all of the pod's stored containers +/obj/machinery/clonepod/proc/get_biomass() + var/biomass_count = 0 + if(LAZYLEN(containers)) + for(var/obj/item/weapon/reagent_containers/glass/G in containers) + for(var/datum/reagent/R in G.reagents.reagent_list) + if(R.id == "biomass") + biomass_count += R.volume + + return biomass_count + +// Removes [amount] biomass, spread across all containers. Doesn't have any check that you actually HAVE enough biomass, though. +/obj/machinery/clonepod/proc/remove_biomass(var/amount = CLONE_BIOMASS) //Just in case it doesn't get passed a new amount, assume one clone + var/to_remove = 0 // Tracks how much biomass has been found so far + if(LAZYLEN(containers)) + for(var/obj/item/weapon/reagent_containers/glass/G in containers) + if(to_remove < amount) //If we have what we need, we can stop. Checked every time we switch beakers + for(var/datum/reagent/R in G.reagents.reagent_list) + if(R.id == "biomass") // Finds Biomass + var/need_remove = max(0, amount - to_remove) //Figures out how much biomass is in this container + if(R.volume >= need_remove) //If we have more than enough in this beaker, only take what we need + R.remove_self(need_remove) + to_remove = amount + else //Otherwise, take everything and move on + to_remove += R.volume + R.remove_self(R.volume) + else + continue + else + return 1 + return 0 + +// Empties all of the beakers from the cloning pod, used to refill it +/obj/machinery/clonepod/verb/empty_beakers() + set name = "Eject Beakers" + set category = "Object" + set src in oview(1) + + if(usr.stat != 0) + return + + add_fingerprint(usr) + drop_beakers() + return + +// Actually does all of the beaker dropping +// Returns 1 if it succeeds, 0 if it fails. Added in case someone wants to add messages to the user. +/obj/machinery/clonepod/proc/drop_beakers() + if(LAZYLEN(containers)) + var/turf/T = get_turf(src) + if(T) + for(var/obj/item/weapon/reagent_containers/glass/G in containers) + G.forceMove(T) + containers -= G + return 1 + return 0 + +/obj/machinery/clonepod/proc/malfunction() + if(occupant) + connected_message("Critical Error!") + mess = 1 + update_icon() + occupant.ghostize() + spawn(5) + qdel(occupant) + return + +/obj/machinery/clonepod/relaymove(mob/user as mob) + if(user.stat) + return + go_out() + return + +/obj/machinery/clonepod/emp_act(severity) + if(prob(100/severity)) + malfunction() + ..() + +/obj/machinery/clonepod/ex_act(severity) + switch(severity) + if(1.0) + for(var/atom/movable/A as mob|obj in src) + A.loc = src.loc + ex_act(severity) + qdel(src) + return + if(2.0) + if(prob(50)) + for(var/atom/movable/A as mob|obj in src) + A.loc = src.loc + ex_act(severity) + qdel(src) + return + if(3.0) + if(prob(25)) + for(var/atom/movable/A as mob|obj in src) + A.loc = src.loc + ex_act(severity) + qdel(src) + return + else + return + +/obj/machinery/clonepod/update_icon() + ..() + icon_state = "pod_0" + if(occupant && !(stat & NOPOWER)) + icon_state = "pod_1" + else if(mess) + icon_state = "pod_g" + + +/obj/machinery/clonepod/full/New() + ..() + for(var/i = 1 to container_limit) + containers += new /obj/item/weapon/reagent_containers/glass/bottle/biomass(src) + +//Health Tracker Implant + +/obj/item/weapon/implant/health + name = "health implant" + known_implant = TRUE + var/healthstring = "" + +/obj/item/weapon/implant/health/proc/sensehealth() + if(!implanted) + return "ERROR" + else + if(isliving(implanted)) + var/mob/living/L = implanted + healthstring = "[round(L.getOxyLoss())] - [round(L.getFireLoss())] - [round(L.getToxLoss())] - [round(L.getBruteLoss())]" + if(!healthstring) + healthstring = "ERROR" + return healthstring + +//Disk stuff. +//The return of data disks?? Just for transferring between genetics machine/cloning machine. +//TO-DO: Make the genetics machine accept them. +/obj/item/weapon/disk/data + name = "Cloning Data Disk" + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + icon_state = "data-red" //VOREStation Edit + item_state = "card-id" + w_class = ITEMSIZE_SMALL + var/datum/dna2/record/buf = null + var/read_only = 0 //Well,it's still a floppy disk + +/obj/item/weapon/disk/data/proc/initializeDisk() + buf = new + buf.dna=new + +/obj/item/weapon/disk/data/demo + name = "data disk - 'God Emperor of Mankind'" + read_only = 1 + +/obj/item/weapon/disk/data/demo/New() + initializeDisk() + buf.types=DNA2_BUF_UE|DNA2_BUF_UI + //data = "066000033000000000AF00330660FF4DB002690" + //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff + buf.dna.real_name="God Emperor of Mankind" + buf.dna.unique_enzymes = md5(buf.dna.real_name) + buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0xAF0,0x033,0x066,0x0FF,0x4DB,0x002,0x690) + //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff + buf.dna.UpdateUI() + +/obj/item/weapon/disk/data/monkey + name = "data disk - 'Mr. Muggles'" + read_only = 1 + +/obj/item/weapon/disk/data/monkey/New() + ..() + initializeDisk() + buf.types=DNA2_BUF_SE + var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) + for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) + new_SE += rand(1,1024) + buf.dna.SE=new_SE + buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) + +/obj/item/weapon/disk/data/New() + ..() + var/diskcolor = pick(0,1,2) + icon_state = "datadisk[diskcolor]" + +/obj/item/weapon/disk/data/attack_self(mob/user as mob) + read_only = !read_only + to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") + +/obj/item/weapon/disk/data/examine(mob/user) + . = ..() + . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." + +/* + * Diskette Box + */ + +/obj/item/weapon/storage/box/disks + name = "Diskette Box" + icon_state = "disk_kit" + +/obj/item/weapon/storage/box/disks/New() + ..() + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + new /obj/item/weapon/disk/data(src) + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/weapon/paper/Cloning + name = "H-87 Cloning Apparatus Manual" + info = {"

    Getting Started

    + Congratulations, your station has purchased the H-87 industrial cloning device!
    + Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    + That's all there is to it!
    + Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    +

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. + Profile Deletion has been restricted to \[Station Head\] level access.

    +

    Cloning from a profile

    + Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    + Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    +
    +

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. + The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    + Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    +

    Profile Management

    +

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. + These diskettes are used to transfer genetic information between machines and profiles. + A load/save dialog will become available in each profile if a disk is inserted.


    + A good diskette is a great way to counter aforementioned genetic drift!
    +
    + This technology produced under license from Thinktronic Systems, LTD."} + +//SOME SCRAPS I GUESS +/* EMP grenade/spell effect + if(istype(A, /obj/machinery/clonepod)) + A:malfunction() +*/ diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 1d45a3e508b..dc8375f1e75 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -1,312 +1,312 @@ -#define OP_COMPUTER_COOLDOWN 60 - -/obj/machinery/computer/operating - name = "patient monitoring console" - desc = "Used to monitor the vitals of a patient." - density = TRUE - anchored = TRUE - icon_keyboard = "med_key" - icon_screen = "crew" - circuit = /obj/item/weapon/circuitboard/operating - var/obj/machinery/optable/table = null - var/mob/living/carbon/human/victim = null - var/verbose = 1 //general speaker toggle - var/patientName = null - var/oxyAlarm = 30 //oxy damage at which the computer will beep - var/choice = 0 //just for going into and out of the options menu - var/healthAnnounce = 1 //healther announcer toggle - var/crit = 1 //crit beeping toggle - var/nextTick = OP_COMPUTER_COOLDOWN - var/healthAlarm = 50 - var/oxy = 1 //oxygen beeping toggle - -/obj/machinery/computer/operating/New() - ..() - for(var/direction in list(NORTH,EAST,SOUTH,WEST)) - table = locate(/obj/machinery/optable, get_step(src, direction)) - if(table) - table.computer = src - break - -/obj/machinery/computer/operating/Destroy() - if(table) - table.computer = null - table = null - if(victim) - victim = null - return ..() - -/obj/machinery/computer/operating/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/operating/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "OperatingComputer", "Patient Monitor") - ui.open() - -/obj/machinery/computer/operating/tgui_data(mob/user) - var/data[0] - var/mob/living/carbon/human/occupant - if(table) - occupant = table.victim - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(isanimal(occupant)) - var/mob/living/simple_mob/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) - occupantData["bloodLevel"] = blood_volume - occupantData["bloodMax"] = occupant.species.blood_volume - occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.b_type - occupantData["surgery"] = build_surgery_list(user) - - data["occupant"] = occupantData - data["verbose"]=verbose - data["oxyAlarm"]=oxyAlarm - data["choice"]=choice - data["health"]=healthAnnounce - data["crit"]=crit - data["healthAlarm"]=healthAlarm - data["oxy"]=oxy - - return data - -/obj/machinery/computer/operating/tgui_act(action, params) - if(..()) - return TRUE - if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - . = TRUE - switch(action) - if("verboseOn") - verbose = TRUE - if("verboseOff") - verbose = FALSE - if("healthOn") - healthAnnounce = TRUE - if("healthOff") - healthAnnounce = FALSE - if("critOn") - crit = TRUE - if("critOff") - crit = FALSE - if("oxyOn") - oxy = TRUE - if("oxyOff") - oxy = FALSE - if("oxy_adj") - oxyAlarm = clamp(text2num(params["new"]), -100, 100) - if("choiceOn") - choice = TRUE - if("choiceOff") - choice = FALSE - if("health_adj") - healthAlarm = clamp(text2num(params["new"]), -100, 100) - else - return FALSE - -/obj/machinery/computer/operating/process() - if(table && table.check_victim()) - if(verbose) - if(patientName!=table.victim.name) - patientName=table.victim.name - atom_say("New patient detected, loading stats") - victim = table.victim - atom_say("[victim.real_name], [victim.dna.b_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") - SStgui.update_uis(src) - if(nextTick < world.time) - nextTick=world.time + OP_COMPUTER_COOLDOWN - if(crit && victim.health <= -50 ) - playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) - if(oxy && victim.getOxyLoss()>oxyAlarm) - playsound(src.loc, 'sound/machines/defib_safetyOff.ogg', 50, 0) - if(healthAnnounce && ((victim.health / victim.maxHealth) * 100) <= healthAlarm) - atom_say("[round(((victim.health / victim.maxHealth) * 100))]% health.") - -// Surgery Helpers -/obj/machinery/computer/operating/proc/build_surgery_list(mob/user) - if(!istype(victim)) - return null - - . = list() - - for(var/limb in victim.organs_by_name) - var/obj/item/organ/external/E = victim.organs_by_name[limb] - if(E && E.open) - . += list(list("name" = E.name, "currentStage" = find_stage(E), "nextSteps" = find_next_steps(user, limb))) - -/** - * This proc is actually hell. I hate the surgery system Polaris uses. - * Basically, surgery is completely stateless, and what "stage" we're on is just dependent - * on the current state of 5 separate variables that determine what stages we can perform - * next. - * - * So, here's a little guide to understand this proc: - * Surgery is broken down into 5 different variables: - * `open`, - * `stage`, - * `cavity`, - * `burn_stage`, - * and `brute_stage`. - * Naturally, the values assigned to these don't use defines or names or anything, they're just magic numbers. - * So, we have to figure out ourselves what we should call each value. - * Open can be 4 values, and represents the "openness" of the surgery site. - * 1 = Cut Open. - * 2 = Retracted. - * 2.5 = Bones cut. - * 3 = Bones spread. - * Stage can be 3 values, and represents the progress in fixing broken bones - * 0 = Closed, can be either "we're done" or "we haven't started" FFS. - * 1 = Bones glued. - * 2 = Bones set. - * Cavity is just representing the cavity implant surgeries, and can be 2 values. - * 0 = Cavity Closed - * 1 = Cavity Open - * burn_stage and brute_stage are literally only used for repairing brute/burn damage to limbs - * I have no idea why you would ever perform these surgeries, given that Bicaradine and Kelotane exist. - * So I'm not even going to bother trying to represent them here. Fuck it. - */ -/obj/machinery/computer/operating/proc/find_stage(var/obj/item/organ/external/E) - . = "None." - switch(E.open) - if(1) - . = "Incision made." - if(2) - . = "Surgical site opened." - switch(E.stage) - // if(0) // Nothing. - if(1) - . = "Surgical site opened; Bones glued." - if(2) - . = "Surgical site opened; Bones set." - switch(E.cavity) - if(1) - . = "Surgical site opened; Cavity open." - if(2.5) // WHY IS THIS A FLOAT. WHY? - . = "Bones cut." - switch(E.stage) - // if(0) // Nothing. - if(1) - . = "Bones cut; Bones glued." - if(2) - . = "Bones cut; Bones set." - if(3) - . = "Bones retracted." - switch(E.stage) - // if(0) // Nothing. - if(1) - . = "Bones retracted; Bones glued." - if(2) - . = "Bones retracted; Bones reset." - switch(E.cavity) - if(1) - . = "Bones retracted; Cavity open." - -/** - * This converts a typepath into a pretty name. - * As best as it can, anyways. - */ -/proc/pretty_type(var/datum/A) - var/typeStr = "[A.type]" - . = copytext(typeStr, findlasttext(typeStr, "/") + 1, length(typeStr) + 1) - . = capitalize(replacetext(., "_", " ")) - -/proc/get_surgery_steps_without_basetypes() - var/static/list/good_surgeries = list() - if(LAZYLEN(good_surgeries)) - return good_surgeries - var/static/list/banned_surgery_steps = list( - /datum/surgery_step, - /datum/surgery_step/generic, - /datum/surgery_step/open_encased, - /datum/surgery_step/repairflesh, - /datum/surgery_step/face, - /datum/surgery_step/cavity, - /datum/surgery_step/limb, - /datum/surgery_step/brainstem, - /datum/surgery_step/generic/ripper, - ) - good_surgeries = surgery_steps - for(var/datum/surgery_step/S in good_surgeries) - if(S.type in banned_surgery_steps) - good_surgeries -= S - if(!LAZYLEN(S.allowed_tools)) - good_surgeries -= S - return good_surgeries - -/** - * Funnily enough, this proc is actually considerably less awful than find_stage. - * All we have to do is check what surgeries can be done, like surgery mechanics themselves do. - * Then, build a string telling the user what they can do next. - */ -/obj/machinery/computer/operating/proc/find_next_steps(mob/user, zone) - . = list() - for(var/datum/surgery_step/S in get_surgery_steps_without_basetypes()) - if(S.can_use(user, victim, zone, null) && S.is_valid_target(victim)) - var/allowed_tools_by_name = list() - for(var/tool in S.allowed_tools) - // Exempt ghetto tools. - if(S.allowed_tools[tool] < 100) - continue - var/obj/tool_path = tool - allowed_tools_by_name += capitalize(initial(tool_path.name)) - // Please for the love of all that is holy, someone make surgery steps - // have names so I don't have to do this stupid pretty_type shit. - . += "[pretty_type(S)]: [english_list(allowed_tools_by_name)]" +#define OP_COMPUTER_COOLDOWN 60 + +/obj/machinery/computer/operating + name = "patient monitoring console" + desc = "Used to monitor the vitals of a patient." + density = TRUE + anchored = TRUE + icon_keyboard = "med_key" + icon_screen = "crew" + circuit = /obj/item/weapon/circuitboard/operating + var/obj/machinery/optable/table = null + var/mob/living/carbon/human/victim = null + var/verbose = 1 //general speaker toggle + var/patientName = null + var/oxyAlarm = 30 //oxy damage at which the computer will beep + var/choice = 0 //just for going into and out of the options menu + var/healthAnnounce = 1 //healther announcer toggle + var/crit = 1 //crit beeping toggle + var/nextTick = OP_COMPUTER_COOLDOWN + var/healthAlarm = 50 + var/oxy = 1 //oxygen beeping toggle + +/obj/machinery/computer/operating/New() + ..() + for(var/direction in list(NORTH,EAST,SOUTH,WEST)) + table = locate(/obj/machinery/optable, get_step(src, direction)) + if(table) + table.computer = src + break + +/obj/machinery/computer/operating/Destroy() + if(table) + table.computer = null + table = null + if(victim) + victim = null + return ..() + +/obj/machinery/computer/operating/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + + +/obj/machinery/computer/operating/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/operating/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "OperatingComputer", "Patient Monitor") + ui.open() + +/obj/machinery/computer/operating/tgui_data(mob/user) + var/data[0] + var/mob/living/carbon/human/occupant + if(table) + occupant = table.victim + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(isanimal(occupant)) + var/mob/living/simple_mob/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + var/blood_volume = round(occupant.vessel.get_reagent_amount("blood")) + occupantData["bloodLevel"] = blood_volume + occupantData["bloodMax"] = occupant.species.blood_volume + occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.b_type + occupantData["surgery"] = build_surgery_list(user) + + data["occupant"] = occupantData + data["verbose"]=verbose + data["oxyAlarm"]=oxyAlarm + data["choice"]=choice + data["health"]=healthAnnounce + data["crit"]=crit + data["healthAlarm"]=healthAlarm + data["oxy"]=oxy + + return data + +/obj/machinery/computer/operating/tgui_act(action, params) + if(..()) + return TRUE + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + . = TRUE + switch(action) + if("verboseOn") + verbose = TRUE + if("verboseOff") + verbose = FALSE + if("healthOn") + healthAnnounce = TRUE + if("healthOff") + healthAnnounce = FALSE + if("critOn") + crit = TRUE + if("critOff") + crit = FALSE + if("oxyOn") + oxy = TRUE + if("oxyOff") + oxy = FALSE + if("oxy_adj") + oxyAlarm = clamp(text2num(params["new"]), -100, 100) + if("choiceOn") + choice = TRUE + if("choiceOff") + choice = FALSE + if("health_adj") + healthAlarm = clamp(text2num(params["new"]), -100, 100) + else + return FALSE + +/obj/machinery/computer/operating/process() + if(table && table.check_victim()) + if(verbose) + if(patientName!=table.victim.name) + patientName=table.victim.name + atom_say("New patient detected, loading stats") + victim = table.victim + atom_say("[victim.real_name], [victim.dna.b_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + SStgui.update_uis(src) + if(nextTick < world.time) + nextTick=world.time + OP_COMPUTER_COOLDOWN + if(crit && victim.health <= -50 ) + playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) + if(oxy && victim.getOxyLoss()>oxyAlarm) + playsound(src.loc, 'sound/machines/defib_safetyOff.ogg', 50, 0) + if(healthAnnounce && ((victim.health / victim.maxHealth) * 100) <= healthAlarm) + atom_say("[round(((victim.health / victim.maxHealth) * 100))]% health.") + +// Surgery Helpers +/obj/machinery/computer/operating/proc/build_surgery_list(mob/user) + if(!istype(victim)) + return null + + . = list() + + for(var/limb in victim.organs_by_name) + var/obj/item/organ/external/E = victim.organs_by_name[limb] + if(E && E.open) + . += list(list("name" = E.name, "currentStage" = find_stage(E), "nextSteps" = find_next_steps(user, limb))) + +/** + * This proc is actually hell. I hate the surgery system Polaris uses. + * Basically, surgery is completely stateless, and what "stage" we're on is just dependent + * on the current state of 5 separate variables that determine what stages we can perform + * next. + * + * So, here's a little guide to understand this proc: + * Surgery is broken down into 5 different variables: + * `open`, + * `stage`, + * `cavity`, + * `burn_stage`, + * and `brute_stage`. + * Naturally, the values assigned to these don't use defines or names or anything, they're just magic numbers. + * So, we have to figure out ourselves what we should call each value. + * Open can be 4 values, and represents the "openness" of the surgery site. + * 1 = Cut Open. + * 2 = Retracted. + * 2.5 = Bones cut. + * 3 = Bones spread. + * Stage can be 3 values, and represents the progress in fixing broken bones + * 0 = Closed, can be either "we're done" or "we haven't started" FFS. + * 1 = Bones glued. + * 2 = Bones set. + * Cavity is just representing the cavity implant surgeries, and can be 2 values. + * 0 = Cavity Closed + * 1 = Cavity Open + * burn_stage and brute_stage are literally only used for repairing brute/burn damage to limbs + * I have no idea why you would ever perform these surgeries, given that Bicaradine and Kelotane exist. + * So I'm not even going to bother trying to represent them here. Fuck it. + */ +/obj/machinery/computer/operating/proc/find_stage(var/obj/item/organ/external/E) + . = "None." + switch(E.open) + if(1) + . = "Incision made." + if(2) + . = "Surgical site opened." + switch(E.stage) + // if(0) // Nothing. + if(1) + . = "Surgical site opened; Bones glued." + if(2) + . = "Surgical site opened; Bones set." + switch(E.cavity) + if(1) + . = "Surgical site opened; Cavity open." + if(2.5) // WHY IS THIS A FLOAT. WHY? + . = "Bones cut." + switch(E.stage) + // if(0) // Nothing. + if(1) + . = "Bones cut; Bones glued." + if(2) + . = "Bones cut; Bones set." + if(3) + . = "Bones retracted." + switch(E.stage) + // if(0) // Nothing. + if(1) + . = "Bones retracted; Bones glued." + if(2) + . = "Bones retracted; Bones reset." + switch(E.cavity) + if(1) + . = "Bones retracted; Cavity open." + +/** + * This converts a typepath into a pretty name. + * As best as it can, anyways. + */ +/proc/pretty_type(var/datum/A) + var/typeStr = "[A.type]" + . = copytext(typeStr, findlasttext(typeStr, "/") + 1, length(typeStr) + 1) + . = capitalize(replacetext(., "_", " ")) + +/proc/get_surgery_steps_without_basetypes() + var/static/list/good_surgeries = list() + if(LAZYLEN(good_surgeries)) + return good_surgeries + var/static/list/banned_surgery_steps = list( + /datum/surgery_step, + /datum/surgery_step/generic, + /datum/surgery_step/open_encased, + /datum/surgery_step/repairflesh, + /datum/surgery_step/face, + /datum/surgery_step/cavity, + /datum/surgery_step/limb, + /datum/surgery_step/brainstem, + /datum/surgery_step/generic/ripper, + ) + good_surgeries = surgery_steps + for(var/datum/surgery_step/S in good_surgeries) + if(S.type in banned_surgery_steps) + good_surgeries -= S + if(!LAZYLEN(S.allowed_tools)) + good_surgeries -= S + return good_surgeries + +/** + * Funnily enough, this proc is actually considerably less awful than find_stage. + * All we have to do is check what surgeries can be done, like surgery mechanics themselves do. + * Then, build a string telling the user what they can do next. + */ +/obj/machinery/computer/operating/proc/find_next_steps(mob/user, zone) + . = list() + for(var/datum/surgery_step/S in get_surgery_steps_without_basetypes()) + if(S.can_use(user, victim, zone, null) && S.is_valid_target(victim)) + var/allowed_tools_by_name = list() + for(var/tool in S.allowed_tools) + // Exempt ghetto tools. + if(S.allowed_tools[tool] < 100) + continue + var/obj/tool_path = tool + allowed_tools_by_name += capitalize(initial(tool_path.name)) + // Please for the love of all that is holy, someone make surgery steps + // have names so I don't have to do this stupid pretty_type shit. + . += "[pretty_type(S)]: [english_list(allowed_tools_by_name)]" diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index a399fc7ef66..a039cd3d3d1 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -15,14 +15,14 @@ switch(state) if(0) - if(P.is_wrench()) + if(P.has_tool_quality(TOOL_WRENCH)) playsound(src, P.usesound, 50, 1) if(do_after(user, 20 * P.toolspeed)) to_chat(user, "You wrench the frame into place.") anchored = TRUE state = 1 - if(istype(P, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = P + if(P.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = P.get_welder() if(!WT.isOn()) to_chat(user, "The welder must be on for this task.") return @@ -33,7 +33,7 @@ new /obj/item/stack/material/plasteel( loc, 4) qdel(src) if(1) - if(P.is_wrench()) + if(P.has_tool_quality(TOOL_WRENCH)) playsound(src, P.usesound, 50, 1) if(do_after(user, 20 * P.toolspeed)) to_chat(user, "You unfasten the frame.") @@ -46,12 +46,12 @@ circuit = P user.drop_item() P.loc = src - if(P.is_screwdriver() && circuit) + if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) playsound(src, P.usesound, 50, 1) to_chat(user, "You screw the circuit board into place.") state = 2 icon_state = "2" - if(P.is_crowbar() && circuit) + if(P.has_tool_quality(TOOL_CROWBAR) && circuit) playsound(src, P.usesound, 50, 1) to_chat(user, "You remove the circuit board.") state = 1 @@ -59,7 +59,7 @@ circuit.loc = loc circuit = null if(2) - if(P.is_screwdriver() && circuit) + if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) playsound(src, P.usesound, 50, 1) to_chat(user, "You unfasten the circuit board.") state = 1 @@ -78,7 +78,7 @@ to_chat(user, "You add cables to the frame.") return if(3) - if(P.is_wirecutter()) + if(P.has_tool_quality(TOOL_WIRECUTTER)) if (brain) to_chat(user, "Get that brain out of there first") else @@ -145,7 +145,7 @@ to_chat(usr, "Added [P].") icon_state = "3b" - if(P.is_crowbar() && brain) + if(P.has_tool_quality(TOOL_CROWBAR) && brain) playsound(src, P.usesound, 50, 1) to_chat(user, "You remove the brain.") brain.loc = loc @@ -153,7 +153,7 @@ icon_state = "3" if(4) - if(P.is_crowbar()) + if(P.has_tool_quality(TOOL_CROWBAR)) playsound(src, P.usesound, 50, 1) to_chat(user, "You remove the glass panel.") state = 3 @@ -164,7 +164,7 @@ new /obj/item/stack/material/glass/reinforced( loc, 2 ) return - if(P.is_screwdriver()) + if(P.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, P.usesound, 50, 1) to_chat(user, "You connect the monitor.") if(!brain) @@ -232,7 +232,7 @@ GLOBAL_LIST_BOILERPLATE(all_deactivated_AI_cores, /obj/structure/AIcore/deactiva else to_chat(user, "ERROR: Unable to locate artificial intelligence.") return - else if(W.is_wrench()) + else if(W.has_tool_quality(TOOL_WRENCH)) if(anchored) user.visible_message("\The [user] starts to unbolt \the [src] from the plating...") playsound(src, W.usesound, 50, 1) @@ -270,7 +270,7 @@ GLOBAL_LIST_BOILERPLATE(all_deactivated_AI_cores, /obj/structure/AIcore/deactiva if(D in empty_playable_ai_cores) empty_playable_ai_cores -= D - to_chat(src, "\The [id] is now not available for latejoining AIs.") + to_chat(src, "\The [id] is now [span_red("not available")] for latejoining AIs.") else empty_playable_ai_cores += D - to_chat(src, "\The [id] is now available for latejoining AIs.") + to_chat(src, "\The [id] is now [span_green("available")] for latejoining AIs.") diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 3ec9e2c841e..e1d2c7fb099 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,135 +1,135 @@ -/obj/machinery/computer/aifixer - name = "\improper AI system integrity restorer" - desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order." - req_one_access = list(access_robotics, access_heads) - circuit = /obj/item/weapon/circuitboard/aifixer - icon_keyboard = "tech_key" - icon_screen = "ai-fixer" - light_color = LIGHT_COLOR_PINK - - active_power_usage = 1000 - - /// Variable containing transferred AI - var/mob/living/silicon/ai/occupier - /// Variable dictating if we are in the process of restoring the occupier AI - var/restoring = FALSE - -/obj/machinery/computer/aifixer/attackby(obj/item/I, mob/living/user) - if(I.is_screwdriver()) - if(occupier) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "The screws on [name]'s screen won't budge.") - else - to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep.") - return - if(istype(I, /obj/item/device/aicard)) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "This terminal isn't functioning right now.") - return - if(restoring) - to_chat(user, "Terminal is busy restoring [occupier] right now.") - return - - var/obj/item/device/aicard/card = I - if(occupier) - if(card.grab_ai(occupier, user)) - occupier = null - else if(card.carded_ai) - var/mob/living/silicon/ai/new_occupant = card.carded_ai - to_chat(new_occupant, "You have been transferred into a stationary terminal. Sadly there is no remote access from here.") - to_chat(user, "Transfer Successful: [new_occupant] placed within stationary terminal.") - new_occupant.forceMove(src) - new_occupant.cancel_camera() - new_occupant.control_disabled = TRUE - occupier = new_occupant - card.clear() - update_icon() - else - to_chat(user, "There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?") - return ..() - -/obj/machinery/computer/aifixer/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - tgui_interact(user) - -/obj/machinery/computer/aifixer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AiRestorer", name) - ui.open() - -/obj/machinery/computer/aifixer/tgui_data(mob/user) - var/list/data = list() - - data["ejectable"] = FALSE - data["AI_present"] = FALSE - data["error"] = null - if(!occupier) - data["error"] = "Please transfer an AI unit." - else - data["AI_present"] = TRUE - data["name"] = occupier.name - data["restoring"] = restoring - data["health"] = (occupier.health + 100) / 2 - data["isDead"] = occupier.stat == DEAD - var/list/laws = list() - for(var/datum/ai_law/law in occupier.laws.all_laws()) - laws += "[law.get_index()]: [law.law]" - data["laws"] = laws - - return data - -/obj/machinery/computer/aifixer/tgui_act(action, params) - if(..()) - return TRUE - if(!occupier) - restoring = FALSE - - if(action) - playsound(src, "terminal_type", 50, 1) - - switch(action) - if("PRG_beginReconstruction") - if(occupier?.health < 100) - to_chat(usr, "Reconstruction in progress. This will take several minutes.") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) - restoring = TRUE - var/mob/observer/dead/ghost = occupier.get_ghost() - if(ghost) - ghost.notify_revive("Your core files are being restored!", source = src) - . = TRUE - -/obj/machinery/computer/aifixer/proc/Fix() - use_power(active_power_usage) - occupier.adjustOxyLoss(-5, 0, FALSE) - occupier.adjustFireLoss(-5, 0, FALSE) - occupier.adjustBruteLoss(-5, 0) - if(occupier.health >= 0 && occupier.stat == DEAD) - occupier.revive() - - return occupier.health < 100 - -/obj/machinery/computer/aifixer/process() - if(..()) - if(restoring) - var/oldstat = occupier.stat - restoring = Fix() - if(oldstat != occupier.stat) - update_icon() - -/obj/machinery/computer/aifixer/update_icon() - . = ..() - if(stat & (NOPOWER|BROKEN)) - return - - if(restoring) - . += "ai-fixer-on" - if (occupier) - switch (occupier.stat) - if (CONSCIOUS) - . += "ai-fixer-full" - if (UNCONSCIOUS) - . += "ai-fixer-404" - else +/obj/machinery/computer/aifixer + name = "\improper AI system integrity restorer" + desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order." + req_one_access = list(access_robotics, access_heads) + circuit = /obj/item/weapon/circuitboard/aifixer + icon_keyboard = "tech_key" + icon_screen = "ai-fixer" + light_color = LIGHT_COLOR_PINK + + active_power_usage = 1000 + + /// Variable containing transferred AI + var/mob/living/silicon/ai/occupier + /// Variable dictating if we are in the process of restoring the occupier AI + var/restoring = FALSE + +/obj/machinery/computer/aifixer/attackby(obj/item/I, mob/living/user) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + if(occupier) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "The screws on [name]'s screen won't budge.") + else + to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep.") + return + if(istype(I, /obj/item/device/aicard)) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "This terminal isn't functioning right now.") + return + if(restoring) + to_chat(user, "Terminal is busy restoring [occupier] right now.") + return + + var/obj/item/device/aicard/card = I + if(occupier) + if(card.grab_ai(occupier, user)) + occupier = null + else if(card.carded_ai) + var/mob/living/silicon/ai/new_occupant = card.carded_ai + to_chat(new_occupant, "You have been transferred into a stationary terminal. Sadly there is no remote access from here.") + to_chat(user, "Transfer Successful: [new_occupant] placed within stationary terminal.") + new_occupant.forceMove(src) + new_occupant.cancel_camera() + new_occupant.control_disabled = TRUE + occupier = new_occupant + card.clear() + update_icon() + else + to_chat(user, "There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?") + return ..() + +/obj/machinery/computer/aifixer/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + tgui_interact(user) + +/obj/machinery/computer/aifixer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiRestorer", name) + ui.open() + +/obj/machinery/computer/aifixer/tgui_data(mob/user) + var/list/data = list() + + data["ejectable"] = FALSE + data["AI_present"] = FALSE + data["error"] = null + if(!occupier) + data["error"] = "Please transfer an AI unit." + else + data["AI_present"] = TRUE + data["name"] = occupier.name + data["restoring"] = restoring + data["health"] = (occupier.health + 100) / 2 + data["isDead"] = occupier.stat == DEAD + var/list/laws = list() + for(var/datum/ai_law/law in occupier.laws.all_laws()) + laws += "[law.get_index()]: [law.law]" + data["laws"] = laws + + return data + +/obj/machinery/computer/aifixer/tgui_act(action, params) + if(..()) + return TRUE + if(!occupier) + restoring = FALSE + + if(action) + playsound(src, "terminal_type", 50, 1) + + switch(action) + if("PRG_beginReconstruction") + if(occupier?.health < 100) + to_chat(usr, "Reconstruction in progress. This will take several minutes.") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) + restoring = TRUE + var/mob/observer/dead/ghost = occupier.get_ghost() + if(ghost) + ghost.notify_revive("Your core files are being restored!", source = src) + . = TRUE + +/obj/machinery/computer/aifixer/proc/Fix() + use_power(active_power_usage) + occupier.adjustOxyLoss(-5, 0, FALSE) + occupier.adjustFireLoss(-5, 0, FALSE) + occupier.adjustBruteLoss(-5, 0) + if(occupier.health >= 0 && occupier.stat == DEAD) + occupier.revive() + + return occupier.health < 100 + +/obj/machinery/computer/aifixer/process() + if(..()) + if(restoring) + var/oldstat = occupier.stat + restoring = Fix() + if(oldstat != occupier.stat) + update_icon() + +/obj/machinery/computer/aifixer/update_icon() + . = ..() + if(stat & (NOPOWER|BROKEN)) + return + + if(restoring) + . += "ai-fixer-on" + if (occupier) + switch (occupier.stat) + if (CONSCIOUS) + . += "ai-fixer-full" + if (UNCONSCIOUS) + . += "ai-fixer-404" + else . += "ai-fixer-empty" \ No newline at end of file diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index dd9a4d78865..1834971be2d 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -1,1341 +1,1341 @@ -/obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" - icon_state = "arcade1" - icon_keyboard = null - clicksound = null //Gets too spammy and makes no sense for arcade to have the console keyboard noise anyway - var/list/prizes = list( /obj/item/weapon/storage/box/snappops = 2, - /obj/item/toy/blink = 2, - /obj/item/clothing/under/syndicate/tacticool = 2, - /obj/item/toy/sword = 2, - /obj/item/weapon/storage/box/capguntoy = 2, - /obj/item/weapon/gun/projectile/revolver/toy/crossbow = 2, - /obj/item/clothing/suit/syndicatefake = 2, - /obj/item/weapon/storage/fancy/crayons = 2, - /obj/item/toy/spinningtoy = 2, - /obj/random/mech_toy = 1, - /obj/item/weapon/reagent_containers/spray/waterflower = 1, - /obj/random/action_figure = 1, - /obj/random/plushie = 1, - /obj/item/toy/cultsword = 1, - /obj/item/toy/bouquet/fake = 1, - /obj/item/clothing/accessory/badge/sheriff = 2, - /obj/item/clothing/head/cowboy/small = 2, - /obj/item/toy/stickhorse = 2 - ) - var/list/special_prizes = list() // Holds instanced objects, intended for admins to shove surprises inside or something. - -/obj/machinery/computer/arcade/Initialize() - . = ..() - // If it's a generic arcade machine, pick a random arcade - // circuit board for it and make the new machine - if(!circuit) - var/choice = pick(subtypesof(/obj/item/weapon/circuitboard/arcade) - /obj/item/weapon/circuitboard/arcade/clawmachine) - var/obj/item/weapon/circuitboard/CB = new choice() - new CB.build_path(loc, CB) - return INITIALIZE_HINT_QDEL - -/obj/machinery/computer/arcade/proc/prizevend() - if(LAZYLEN(special_prizes)) // Downstream wanted the 'win things inside contents sans circuitboard' feature kept. - var/atom/movable/AM = pick_n_take(special_prizes) - AM.forceMove(get_turf(src)) - special_prizes -= AM - - else if(LAZYLEN(prizes)) - var/prizeselect = pickweight(prizes) - new prizeselect(src.loc) - - if(istype(prizeselect, /obj/item/clothing/suit/syndicatefake)) //Helmet is part of the suit - new /obj/item/clothing/head/syndicatefake(src.loc) - -/obj/machinery/computer/arcade/attack_ai(mob/user as mob) - return attack_hand(user) - - -/obj/machinery/computer/arcade/emp_act(severity) - if(stat & (NOPOWER|BROKEN)) - ..(severity) - return - var/empprize = null - var/num_of_prizes = 0 - switch(severity) - if(1) - num_of_prizes = rand(1,4) - if(2) - num_of_prizes = rand(1,3) - if(3) - num_of_prizes = rand(0,2) - if(4) - num_of_prizes = rand(0,1) - for(num_of_prizes; num_of_prizes > 0; num_of_prizes--) - empprize = pickweight(prizes) - new empprize(src.loc) - - ..(severity) - -/////////////////// -// BATTLE HERE // -/////////////////// - -/obj/machinery/computer/arcade/battle - name = "Battler" - desc = "Fight through what space has to offer!" - icon_state = "arcade2" - icon_screen = "battler" - circuit = /obj/item/weapon/circuitboard/arcade/battle - var/enemy_name = "Space Villian" - var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc - var/enemy_action = "" - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = 0 - var/blocked = 0 //Player cannot attack/heal while set - var/turtle = 0 - -/obj/machinery/computer/arcade/battle/Initialize() - . = ..() - randomize_characters() - -/obj/machinery/computer/arcade/battle/proc/randomize_characters() - var/name_action - var/name_part1 - var/name_part2 - - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") - - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") - - enemy_name = replacetext((name_part1 + name_part2), "the ", "") - name = (name_action + name_part1 + name_part2) - - -/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - tgui_interact(user) - -/obj/machinery/computer/arcade/battle/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ArcadeBattle", name) - ui.open() - -/obj/machinery/computer/arcade/battle/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - data["name"] = name - data["temp"] = temp - data["enemyAction"] = enemy_action - data["enemyName"] = enemy_name - data["playerHP"] = player_hp - data["playerMP"] = player_mp - data["enemyHP"] = enemy_hp - data["gameOver"] = gameover - return data - -/obj/machinery/computer/arcade/battle/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - if(!blocked && !gameover) - switch(action) - if("attack") - blocked = 1 - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - if(turtle > 0) - turtle-- - - sleep(10) - enemy_hp -= attackamt - arcade_action() - - if("heal") - blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - turtle++ - - sleep(10) - player_mp -= pointamt - player_hp += healamt - blocked = 1 - arcade_action() - - if("charge") - blocked = 1 - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - playsound(src, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_mp += chargeamt - if(turtle > 0) - turtle-- - - sleep(10) - arcade_action() - - - if(action == "newgame") //Reset everything - temp = "New Round" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - turtle = 0 - - if(emagged) - randomize_characters() - emagged = 0 - - add_fingerprint(usr) - return TRUE - -/obj/machinery/computer/arcade/battle/proc/arcade_action() - if ((enemy_mp <= 0) || (enemy_hp <= 0)) - if(!gameover) - gameover = 1 - temp = "[enemy_name] has fallen! Rejoice!" - playsound(src, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - - if(emagged) - feedback_inc("arcade_win_emagged") - new /obj/effect/spawner/newbomb/timer/syndicate(src.loc) - new /obj/item/clothing/head/collectable/petehat(src.loc) - message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - log_game("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - randomize_characters() - emagged = 0 - else if(!contents.len) - feedback_inc("arcade_win_normal") - prizevend() - - else - feedback_inc("arcade_win_normal") - prizevend() - - else if (emagged && (turtle >= 4)) - var/boomamt = rand(5,10) - enemy_action = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" - playsound(src, 'sound/arcade/boom.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_hp -= boomamt - - else if ((enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - enemy_action = "[enemy_name] steals [stealamt] of your power!" - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_mp -= stealamt - - if (player_mp <= 0) - gameover = 1 - sleep(10) - temp = "You have been drained! GAME OVER" - if(emagged) - feedback_inc("arcade_loss_mana_emagged") - usr.gib() - else - feedback_inc("arcade_loss_mana_normal") - - else if ((enemy_hp <= 10) && (enemy_mp > 4)) - enemy_action = "[enemy_name] heals for 4 health!" - playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - enemy_hp += 4 - enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - enemy_action = "[enemy_name] attacks for [attackamt] damage!" - playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_hp -= attackamt - - if ((player_mp <= 0) || (player_hp <= 0)) - gameover = 1 - temp = "You have been crushed! GAME OVER" - playsound(src, 'sound/arcade/lose.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - if(emagged) - feedback_inc("arcade_loss_hp_emagged") - usr.gib() - else - feedback_inc("arcade_loss_hp_normal") - - blocked = 0 - return - - -/obj/machinery/computer/arcade/battle/emag_act(var/charges, var/mob/user) - if(!emagged) - to_chat(user, span("notice","You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Hyper-Lethal Mode.")) - - temp = "If you die in the game, you die for real!" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - blocked = 0 - emagged = 1 - - enemy_name = "Cuban Pete" - name = "Outbomb Cuban Pete" - - return 1 - - -////////////////////////// -// ORION TRAIL HERE // -////////////////////////// -#define ORION_TRAIL_WINTURN 9 - -//Orion Trail Events -#define ORION_TRAIL_RAIDERS "Raiders" -#define ORION_TRAIL_FLUX "Interstellar Flux" -#define ORION_TRAIL_ILLNESS "Illness" -#define ORION_TRAIL_BREAKDOWN "Breakdown" -#define ORION_TRAIL_MUTINY "Mutiny?" -#define ORION_TRAIL_MUTINY_ATTACK "Mutinous Ambush" -#define ORION_TRAIL_MALFUNCTION "Malfunction" -#define ORION_TRAIL_COLLISION "Collision" -#define ORION_TRAIL_SPACEPORT "Spaceport" -#define ORION_TRAIL_BLACKHOLE "BlackHole" - -#define ORION_STATUS_START 1 -#define ORION_STATUS_NORMAL 2 -#define ORION_STATUS_GAMEOVER 3 -#define ORION_STATUS_MARKET 4 - -/obj/machinery/computer/arcade/orion_trail - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - icon_state = "arcade1" - icon_screen = "orion" - circuit = /obj/item/weapon/circuitboard/arcade/orion_trail - var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. - var/engine = 0 - var/hull = 0 - var/electronics = 0 - var/food = 80 - var/fuel = 60 - var/turns = 4 - var/alive = 4 - var/eventdat = null - var/event = null - var/list/settlers = list("Harry","Larry","Bob") - var/list/events = list(ORION_TRAIL_RAIDERS = 3, - ORION_TRAIL_FLUX = 1, - ORION_TRAIL_ILLNESS = 3, - ORION_TRAIL_BREAKDOWN = 2, - ORION_TRAIL_MUTINY = 3, - ORION_TRAIL_MALFUNCTION = 2, - ORION_TRAIL_COLLISION = 1, - ORION_TRAIL_SPACEPORT = 2 - ) - var/list/stops = list() - var/list/stopblurbs = list() - var/traitors_aboard = 0 - var/spaceport_raided = 0 - var/spaceport_freebie = 0 - var/last_spaceport_action = "" - var/gameStatus = ORION_STATUS_START - var/canContinueEvent = 0 - -/obj/machinery/computer/arcade/orion_trail/New() - ..() - // Sets up the main trail - stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") - stopblurbs = list( - "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", - "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", - "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", - "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", - "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", - "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", - "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", - "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", - "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" - ) - -/obj/machinery/computer/arcade/orion_trail/proc/newgame() - // Set names of settlers in crew - settlers = list() - for(var/i = 1; i <= 3; i++) - add_crewmember() - add_crewmember("[usr]") - // Re-set items to defaults - engine = 1 - hull = 1 - electronics = 1 - food = 80 - fuel = 60 - alive = 4 - turns = 1 - event = null - gameStatus = ORION_STATUS_NORMAL - traitors_aboard = 0 - - //spaceport junk - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - -/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/living/user) - if(..()) - return - if(fuel <= 0 || food <=0 || settlers.len == 0) - gameStatus = ORION_STATUS_GAMEOVER - event = null - user.set_machine(src) - var/dat = "" - if(gameStatus == ORION_STATUS_GAMEOVER) - playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - dat = "

    Game Over

    " - dat += "Like many before you, your crew never made it to Orion, lost to space...
    forever." - if(settlers.len == 0) - dat += "
    Your entire crew died, and your ship joins the fleet of ghost-ships littering the galaxy." - else - if(food <= 0) - dat += "
    You ran out of food and starved." - if(emagged) - user.nutrition = 0 //yeah you pretty hongry - to_chat(user, span("danger", "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.")) - if(fuel <= 0) - dat += "
    You ran out of fuel, and drift, slowly, into a star." - if(emagged) - var/mob/living/M = user - M.adjust_fire_stacks(5) - M.IgniteMob() //flew into a star, so you're on fire - to_chat(user,span("danger", "You feel an immense wave of heat emanate from \the [src]. Your skin bursts into flames.")) - dat += "

    OK...

    " - - if(emagged) - to_chat(user, span("danger", "You're never going to make it to Orion...")) - user.death() - emagged = 0 //removes the emagged status after you lose - gameStatus = ORION_STATUS_START - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - - else if(event) - dat = eventdat - else if(gameStatus == ORION_STATUS_NORMAL) - var/title = stops[turns] - var/subtext = stopblurbs[turns] - dat = "

    [title]

    " - dat += "[subtext]" - dat += "

    Crew:

    " - dat += english_list(settlers) - dat += "
    Food: [food] | Fuel: [fuel]" - dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - if(turns == 7) - dat += "

    Go Around Continue

    " - else - dat += "

    Continue

    " - dat += "

    Kill a crewmember

    " - dat += "

    Close

    " - else - dat = "

    The Orion Trail

    " - dat += "

    Experience the journey of your ancestors!



    " - dat += "
    New Game
    " - dat += "

    Close

    " - user << browse(dat,"window=arcade") - return - -/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) - if(..()) - return - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - if(busy) - return - busy = 1 - - if (href_list["continue"]) //Continue your travels - if(gameStatus == ORION_STATUS_NORMAL && !event && turns != 7) - if(turns >= ORION_TRAIL_WINTURN) - win() - else - food -= (alive+traitors_aboard)*2 - fuel -= 5 - if(turns == 2 && prob(30)) - event = ORION_TRAIL_COLLISION - event() - else if(prob(75)) - event = pickweight(events) - if(traitors_aboard) - if(event == ORION_TRAIL_MUTINY || prob(55)) - event = ORION_TRAIL_MUTINY_ATTACK - event() - turns += 1 - if(emagged) - var/mob/living/carbon/M = usr //for some vars - switch(event) - if(ORION_TRAIL_RAIDERS) - if(prob(50)) - to_chat(usr, span("warning", "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?")) - M.hallucination += 30 - else - to_chat(usr, span("danger", "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...")) - M.take_organ_damage(25) - if(ORION_TRAIL_ILLNESS) - var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS - if(severity == 1) - to_chat(M, span("warning", "You suddenly feel slightly nauseous.")) //got off lucky - if(severity == 2) - to_chat(usr, span("warning", "You suddenly feel extremely nauseous and hunch over until it passes.")) - M.Stun(3) - if(severity >= 3) //you didn't pray hard enough - to_chat(M, span("warning", "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.")) - spawn(30) - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - H.vomit() - if(ORION_TRAIL_FLUX) - if(prob(75)) - M.Weaken(3) - src.visible_message("A sudden gust of powerful wind slams \the [M] into the floor!", "You hear a large fwooshing sound, followed by a bang.") - M.take_organ_damage(15) - else - to_chat(M, span("warning", "A violent gale blows past you, and you barely manage to stay standing!")) - if(ORION_TRAIL_COLLISION) //by far the most damaging event - if(prob(90) && !hull) - var/turf/simulated/floor/F = src.loc - F.ChangeTurf(/turf/space) - src.visible_message(span("danger", "Something slams into the floor around \the [src], exposing it to space!"), "You hear something crack and break.") - else - src.visible_message("Something slams into the floor around \the [src] - luckily, it didn't get through!", "You hear something crack.") - if(ORION_TRAIL_MALFUNCTION) - src.visible_message("\The [src] buzzes and the screen goes blank for a moment before returning to the game.") - var/oldfood = food - var/oldfuel = fuel - food = rand(10,80) / rand(1,2) - fuel = rand(10,60) / rand(1,2) - if(electronics) - sleep(10) - if(oldfuel > fuel && oldfood > food) - src.audible_message("\The [src] lets out a somehow reassuring chime.", runemessage = "reassuring chime") - else if(oldfuel < fuel || oldfood < food) - src.audible_message("\The [src] lets out a somehow ominous chime.", runemessage = "ominous chime") - food = oldfood - fuel = oldfuel - - else if(href_list["newgame"]) //Reset everything - if(gameStatus == ORION_STATUS_START) - playsound(src, 'sound/arcade/Ori_begin.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - newgame() - else if(href_list["menu"]) //back to the main menu - if(gameStatus == ORION_STATUS_GAMEOVER) - gameStatus = ORION_STATUS_START - event = null - food = 80 - fuel = 60 - settlers = list("Harry","Larry","Bob") - else if(href_list["slow"]) //slow down - if(event == ORION_TRAIL_FLUX) - food -= (alive+traitors_aboard)*2 - fuel -= 5 - event = null - else if(href_list["pastblack"]) //slow down - if(turns == 7) - food -= ((alive+traitors_aboard)*2)*3 - fuel -= 15 - turns += 1 - event = null - else if(href_list["useengine"]) //use parts - if(event == ORION_TRAIL_BREAKDOWN) - engine = max(0, --engine) - event = null - else if(href_list["useelec"]) //use parts - if(event == ORION_TRAIL_MALFUNCTION) - electronics = max(0, --electronics) - event = null - else if(href_list["usehull"]) //use parts - if(event == ORION_TRAIL_COLLISION) - hull = max(0, --hull) - event = null - else if(href_list["wait"]) //wait 3 days - if(event == ORION_TRAIL_BREAKDOWN || event == ORION_TRAIL_MALFUNCTION || event == ORION_TRAIL_COLLISION) - food -= ((alive+traitors_aboard)*2)*3 - event = null - else if(href_list["keepspeed"]) //keep speed - if(event == ORION_TRAIL_FLUX) - if(prob(75)) - event = "Breakdown" - event() - else - event = null - else if(href_list["blackhole"]) //keep speed past a black hole - if(turns == 7) - if(prob(75)) - event = ORION_TRAIL_BLACKHOLE - event() - if(emagged) //has to be here because otherwise it doesn't work - src.show_message("\The [src] states, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRIFIED.","You hear something say, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRFIED'") - to_chat(usr, span("warning", "Something draws you closer and closer to the machine.")) - sleep(10) - to_chat(usr, span("danger", "This is really starting to hurt!")) - var i; //spawning a literal blackhole would be fun, but a bit disruptive. - for(i=0;i<4;i++) - var/mob/living/L = usr - if(istype(L)) - L.adjustBruteLoss(25) - sleep(10) - else - event = null - turns += 1 - else if(href_list["holedeath"]) - if(event == ORION_TRAIL_BLACKHOLE) - gameStatus = ORION_STATUS_GAMEOVER - event = null - else if(href_list["eventclose"]) //end an event - if(canContinueEvent) - event = null - - else if(href_list["killcrew"]) //shoot a crewmember - if(gameStatus == ORION_STATUS_NORMAL || event == ORION_TRAIL_MUTINY) - playsound(src, 'sound/arcade/kill_crew.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/sheriff = remove_crewmember() //I shot the sheriff - var/mob/living/L = usr - if(!istype(L)) - return - if(settlers.len == 0 || alive == 0) - src.visible_message("\The [src] states, 'EVERYONE HAS DIED, GAMEOVER.'", "You hear something state, 'EVERYONE HAS DIED, GAMEOVER.'") - if(emagged) - src.visible_message("\The [src] produces a loud, gunlike sound.") - L.adjustBruteLoss(30) - emagged = 0 - gameStatus = ORION_STATUS_GAMEOVER - event = null - else if(emagged) - if(usr.name == sheriff) - src.visible_message("\The [src] states, 'THE CREW HAS CHOSEN TO KILL [usr]'. A gunshot can be heard coming from \the [src]", "You hear 'THE CREW HAS CHOSEN TO KILL [usr]' followed by a gunshot") - L.adjustBruteLoss(30) - if(event == ORION_TRAIL_MUTINY) //only ends the ORION_TRAIL_MUTINY event, since you can do this action in multiple places - event = null - - //Spaceport specific interactions - //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) - //they also call event() again, to regen the eventdata, which is kind of odd but necessary - else if(href_list["buycrew"]) //buy a crewmember - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided && food >= 10 && fuel >= 10) - playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/bought = add_crewmember() - last_spaceport_action = "You hired [bought] as a new crewmember." - fuel -= 10 - food -= 10 - event() - - else if(href_list["sellcrew"]) //sell a crewmember - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided && settlers.len > 1) - playsound(src, 'sound/arcade/lose_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/sold = remove_crewmember() - last_spaceport_action = "You sold your crewmember, [sold]!" - fuel += 7 - food += 7 - event() - - else if(href_list["leave_spaceport"]) - if(gameStatus == ORION_STATUS_MARKET) - event = null - gameStatus = ORION_STATUS_NORMAL - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - - else if(href_list["raid_spaceport"]) - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided) - playsound(src, 'sound/arcade/raid.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - var/success = min(15 * alive,100) //default crew (4) have a 60% chance - spaceport_raided = 1 - - var/FU = 0 - var/FO = 0 - if(prob(success)) - FU = rand(5,15) - FO = rand(5,15) - last_spaceport_action = "You successfully raided the spaceport! You gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" - else - FU = rand(-5,-15) - FO = rand(-5,-15) - last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" - - //your chance of lose a crewmember is 1/2 your chance of success - //this makes higher % failures hurt more, don't get cocky space cowboy! - if(prob(success*5)) - var/lost_crew = remove_crewmember() - last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" - if(emagged) - src.visible_message("The machine states, 'YOU ARE UNDER ARREST, RAIDER!' and shoots handcuffs onto [usr]!", "You hear something say 'YOU ARE UNDER ARREST, RAIDER!' and a clinking sound") - var/obj/item/weapon/handcuffs/C = new(src.loc) - var/mob/living/carbon/human/H = usr - if(istype(H)) - C.forceMove(H) - H.handcuffed = C - H.update_handcuffed() - else - C.throw_at(usr,16,3,src) - - - fuel += FU - food += FO - event() - - else if(href_list["buyparts"]) - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided && fuel > 5) - playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - switch(text2num(href_list["buyparts"])) - if(1) //Engine Parts - engine++ - last_spaceport_action = "Bought Engine Parts" - if(2) //Hull Plates - hull++ - last_spaceport_action = "Bought Hull Plates" - if(3) //Spare Electronics - electronics++ - last_spaceport_action = "Bought Spare Electronics" - fuel -= 5 //they all cost 5 - event() - - else if(href_list["trade"]) - if(gameStatus == ORION_STATUS_MARKET) - if(!spaceport_raided) - playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - switch(text2num(href_list["trade"])) - if(1) //Fuel - if(fuel > 5) - fuel -= 5 - food += 5 - last_spaceport_action = "Traded Fuel for Food" - event() - if(2) //Food - if(food > 5) - fuel += 5 - food -= 5 - last_spaceport_action = "Traded Food for Fuel" - event() - - src.add_fingerprint(usr) - src.updateUsrDialog() - busy = 0 - return - - -/obj/machinery/computer/arcade/orion_trail/proc/event() - eventdat = "

    [event]

    " - canContinueEvent = 0 - switch(event) - if(ORION_TRAIL_RAIDERS) - eventdat += "Raiders have come aboard your ship!" - if(prob(50)) - var/sfood = rand(1,10) - var/sfuel = rand(1,10) - food -= sfood - fuel -= sfuel - eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." - else if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] tried to fight back, but was killed." - else - eventdat += "
    Fortunately, you fended them off without any trouble." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - if(ORION_TRAIL_FLUX) - playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." - eventdat += "
    What will you do?" - eventdat += "

    Slow Down Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_ILLNESS) - eventdat += "A deadly illness has been contracted!" - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by the disease." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - if(ORION_TRAIL_BREAKDOWN) - playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - eventdat += "Oh no! The engine has broken down!" - eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." - if(engine >= 1) - eventdat += "

    Use Part Wait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_MALFUNCTION) - eventdat += "The ship's systems are malfunctioning!" - eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." - if(electronics >= 1) - eventdat += "

    Use Part Wait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_COLLISION) - playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - eventdat += "Something hit us! Looks like there's some hull damage." - if(prob(25)) - var/sfood = rand(5,15) - var/sfuel = rand(5,15) - food -= sfood - fuel -= sfuel - eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." - if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by rapid depressurization." - eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." - if(hull >= 1) - eventdat += "

    Use Part Wait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_BLACKHOLE) - eventdat += "You were swept away into the black hole." - eventdat += "

    Oh...

    " - eventdat += "

    Close

    " - settlers = list() - - if(ORION_TRAIL_MUTINY) - eventdat += "You've been hearing rumors of dissenting opinions amoungst your men." - if(settlers.len <= 2) - eventdat += "
    Your crew's so tiny you don't think anybody would risk an uprising." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - if(prob(10)) - traitors_aboard = min(++traitors_aboard,2) - else - if(traitors_aboard) //less likely to stack traitors - if(prob(20)) - traitors_aboard = min(++traitors_aboard,2) - else if(prob(70)) - traitors_aboard = min(++traitors_aboard,2) - - eventdat += "

    Kill a crewmember

    " - eventdat += "

    Risk it

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - if(ORION_TRAIL_MUTINY_ATTACK) - if(traitors_aboard <= 0) //shouldn't trigger, but hey. - eventdat += "Haha, fooled you, there isn't a mutiny on board!" - eventdat += "
    (You should report this to a coder :S)" - else - var/trait1 = remove_crewmember() - var/trait2 = "" - if(traitors_aboard >= 2) - trait2 = remove_crewmember() - - eventdat += "Oh no, some of your crew are attempting to mutiny!!" - if(trait2) - eventdat += "
    [trait1] and [trait2]'s have armed themselves with weapons!" - else - eventdat += "
    [trait1]'s armed with a weapon!" - - var/chance2attack = alive*20 - if(prob(chance2attack)) - var/chancetokill = 30*traitors_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 traitorss, 2 crew is 50% chance - if(prob(chancetokill)) - var/deadguy = remove_crewmember() - eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] up to [deadguy] and murder[trait2 ? "" : "s"] them!" - else - eventdat += "
    You valiantly fight off the traitor[trait2 ? "s":""]!" - eventdat += "
    You cut the traitor[trait2 ? "s":""] up into meat... Eww" - if(trait2) - food += 30 - traitors_aboard = max(0,traitors_aboard-2) - else - food += 15 - traitors_aboard = max(0,--traitors_aboard) - else - eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] away, What wimps!" - if(trait2) - traitors_aboard = max(0,traitors_aboard-2) - else - traitors_aboard = max(0,--traitors_aboard) - - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - canContinueEvent = 1 - - - if(ORION_TRAIL_SPACEPORT) - gameStatus = ORION_STATUS_MARKET - if(spaceport_raided) - eventdat += "The Spaceport is on high alert! they wont let you dock since you tried to attack them!" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Depart Spaceport

    " - eventdat += "

    Close

    " - else - eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" - eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." - eventdat += "
    Trading terms: FU = Fuel, FO = Food" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Crew:

    " - eventdat += english_list(settlers) - eventdat += "
    Food: [food] | Fuel: [fuel]" - eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - - - //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) - if(!spaceport_freebie && (fuel < 20 || food < 20)) - spaceport_freebie++ - var/FU = 10 - var/FO = 10 - var/freecrew = 0 - if(prob(30)) - FU = 25 - FO = 25 - - if(prob(10)) - add_crewmember() - freecrew++ - - eventdat += "
    The traders of the spaceport take pity on you, and give you some supplies. (+[FU]FU,+[FO]FO)" - if(freecrew) - eventdat += "
    You also gain a new crewmember!" - - fuel += FU - food += FO - - //CREW INTERACTIONS - eventdat += "

    Crew Management:

    " - - //Buy crew - if(food >= 10 && fuel >= 10) - eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " - else - eventdat += "

    You cannot afford a new Crewmember

    " - - //Sell crew - if(settlers.len > 1) - eventdat += "

    Sell crew for Fuel and Food (+7FU,+7FO)

    " - else - eventdat += "

    You cannot afford to sell a Crewmember

    " - - //BUY/SELL STUFF - eventdat += "

    Spare Parts:

    " - - //Engine parts - if(fuel > 5) - eventdat += "

    Buy Engine Parts (-5FU)

    " - else - eventdat += "

    You cannot afford to buy Engine Parts" - - //Hull plates - if(fuel > 5) - eventdat += "

    Buy Hull Plates (-5FU)

    " - else - eventdat += "

    You cannot afford to buy Hull Plates" - - //Electronics - if(fuel > 5) - eventdat += "

    Buy Spare Electronics (-5FU)

    " - else - eventdat += "

    You cannot afford to buy Spare Electronics" - - //Trade - if(fuel > 5) - eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " - else - eventdat += "

    You cannot afford to Trade Fuel for Food 5) - eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " - else - eventdat += "

    You cannot afford to Trade Food for Fuel\The [src] says, 'This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.'") - sleep(20) - src.visible_message(span("warning", "[src] begins to vibrate...")) - src.audible_message("\The [src] says, 'Uh, Port? Having some issues with our reactor, could you check it out? Over.'") - sleep(30) - src.audible_message("\The [src] says, 'Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-'") - sleep(3.6) - src.visible_message(span("danger", "[src] explodes!")) - explosion(src.loc, 1,2,4) - qdel(src) - -#undef ORION_TRAIL_WINTURN -#undef ORION_TRAIL_RAIDERS -#undef ORION_TRAIL_FLUX -#undef ORION_TRAIL_ILLNESS -#undef ORION_TRAIL_BREAKDOWN -#undef ORION_TRAIL_MUTINY -#undef ORION_TRAIL_MUTINY_ATTACK -#undef ORION_TRAIL_MALFUNCTION -#undef ORION_TRAIL_COLLISION -#undef ORION_TRAIL_SPACEPORT -#undef ORION_TRAIL_BLACKHOLE - -#undef ORION_STATUS_START -#undef ORION_STATUS_NORMAL -#undef ORION_STATUS_GAMEOVER -#undef ORION_STATUS_MARKET - -////////////////// -// Claw Machine // -////////////////// - -/obj/machinery/computer/arcade/clawmachine - name = "AlliCo Grab-a-Gift" - desc = "Show off your arcade skills for that special someone!" - icon_state = "clawmachine_new" - icon_keyboard = null - icon_screen = null - circuit = /obj/item/weapon/circuitboard/arcade/clawmachine - prizes = list(/obj/random/plushie) - var/wintick = 0 - var/winprob = 0 - var/instructions = "Insert 1 thaler or swipe a card to play!" - var/gameStatus = "CLAWMACHINE_NEW" - var/gamepaid = 0 - var/gameprice = 1 - var/winscreen = "" - -/// Payment -/obj/machinery/computer/arcade/clawmachine/attackby(obj/item/I as obj, mob/user as mob) - if(..()) - return - - if(gamepaid == 0 && vendor_account && !vendor_account.suspended) - var/paid = 0 - var/obj/item/weapon/card/id/W = I.GetID() - if(W) //for IDs and PDAs and wallets with IDs - paid = pay_with_card(W,I) - else if(istype(I, /obj/item/weapon/spacecash/ewallet)) - var/obj/item/weapon/spacecash/ewallet/C = I - paid = pay_with_ewallet(C) - else if(istype(I, /obj/item/weapon/spacecash)) - var/obj/item/weapon/spacecash/C = I - paid = pay_with_cash(C, user) - if(paid) - gamepaid = 1 - instructions = "Hit start to play!" - return - return - -////// Cash -/obj/machinery/computer/arcade/clawmachine/proc/pay_with_cash(var/obj/item/weapon/spacecash/cashmoney, mob/user) - if(!emagged) - if(gameprice > cashmoney.worth) - - // This is not a status display message, since it's something the character - // themselves is meant to see BEFORE putting the money in - to_chat(usr, "\icon[cashmoney][bicon(cashmoney)] That is not enough money.") - return 0 - - if(istype(cashmoney, /obj/item/weapon/spacecash)) - - visible_message("\The [usr] inserts some cash into \the [src].") - cashmoney.worth -= gameprice - - if(cashmoney.worth <= 0) - usr.drop_from_inventory(cashmoney) - qdel(cashmoney) - else - cashmoney.update_icon() - - // Machine has no idea who paid with cash - credit_purchase("(cash)") - return 1 - if(emagged) - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - to_chat(user, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") - - -///// Ewallet -/obj/machinery/computer/arcade/clawmachine/proc/pay_with_ewallet(var/obj/item/weapon/spacecash/ewallet/wallet) - if(!emagged) - visible_message("\The [usr] swipes \the [wallet] through \the [src].") - playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) - if(gameprice > wallet.worth) - visible_message("Insufficient funds.") - return 0 - else - wallet.worth -= gameprice - credit_purchase("[wallet.owner_name] (chargecard)") - return 1 - if(emagged) - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - to_chat(usr, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") - -///// ID -/obj/machinery/computer/arcade/clawmachine/proc/pay_with_card(var/obj/item/weapon/card/id/I, var/obj/item/ID_container) - if(I==ID_container || ID_container == null) - visible_message("\The [usr] swipes \the [I] through \the [src].") - else - visible_message("\The [usr] swipes \the [ID_container] through \the [src].") - playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) - var/datum/money_account/customer_account = get_account(I.associated_account_number) - if(!customer_account) - visible_message("Error: Unable to access account. Please contact technical support if problem persists.") - return 0 - - if(customer_account.suspended) - visible_message("Unable to access account: account suspended.") - return 0 - - // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is - // empty at high security levels - if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) - var/attempt_pin = tgui_input_number(usr, "Enter pin code", "Vendor transaction") - customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) - - if(!customer_account) - visible_message("Unable to access account: incorrect credentials.") - return 0 - - if(gameprice > customer_account.money) - visible_message("Insufficient funds in account.") - return 0 - else - // Okay to move the money at this point - if(emagged) - gameprice = customer_account.money - // debit money from the purchaser's account - customer_account.money -= gameprice - - // create entry in the purchaser's account log - var/datum/transaction/T = new() - T.target_name = "[vendor_account.owner_name] (via [name])" - T.purpose = "Purchase of arcade game([name])" - if(gameprice > 0) - T.amount = "([gameprice])" - else - T.amount = "[gameprice]" - T.source_terminal = name - T.date = current_date_string - T.time = stationtime2text() - customer_account.transaction_log.Add(T) - - // Give the vendor the money. We use the account owner name, which means - // that purchases made with stolen/borrowed card will look like the card - // owner made them - credit_purchase(customer_account.owner_name) - return 1 - -/// Add to vendor account - -/obj/machinery/computer/arcade/clawmachine/proc/credit_purchase(var/target as text) - vendor_account.money += gameprice - - var/datum/transaction/T = new() - T.target_name = target - T.purpose = "Purchase of arcade game([name])" - T.amount = "[gameprice]" - T.source_terminal = name - T.date = current_date_string - T.time = stationtime2text() - vendor_account.transaction_log.Add(T) - -/// End Payment - -/obj/machinery/computer/arcade/clawmachine/New() - ..() - -/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/living/user) - if(..()) - return - tgui_interact(user) - -/// TGUI Stuff - -/obj/machinery/computer/arcade/clawmachine/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ClawMachine", name, ui_x = 300, ui_y = 400) - ui.autoupdate = TRUE - ui.open() - -/obj/machinery/computer/arcade/clawmachine/tgui_data(mob/user) - var/list/data = list() - - data["wintick"] = wintick - data["instructions"] = instructions - data["gameStatus"] = gameStatus - data["winscreen"] = winscreen - - return data - -/obj/machinery/computer/arcade/clawmachine/tgui_act(action, params) - if(..()) - return - - if(action == "newgame" && gamepaid == 0) - playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - - if(action == "newgame" && gamepaid == 1) - gameStatus = "CLAWMACHINE_ON" - icon_state = "clawmachine_new_move" - instructions = "Guide the claw to the prize you want!" - wintick = 0 - - if(action == "return" && gameStatus == "CLAWMACHINE_END") - gameStatus = "CLAWMACHINE_NEW" - - if(action == "pointless" && wintick < 10) - wintick += 1 - - if(action == "pointless" && wintick >= 10) - instructions = "Insert 1 thaler or swipe a card to play!" - clawvend() - -/obj/machinery/computer/arcade/clawmachine/proc/clawvend() /// True to a real claw machine, it's NEARLY impossible to win. - winprob += 1 /// Yeah. - - if(prob(winprob)) /// YEAH. - if(!emagged) - prizevend() - winscreen = "You won!" - else if(emagged) - gameprice = 1 - emagged = 0 - winscreen = "You won...?" - var/obj/item/weapon/grenade/G = new /obj/item/weapon/grenade/explosive(get_turf(src)) /// YEAAAAAAAAAAAAAAAAAAH!!!!!!!!!! - G.activate() - G.throw_at(get_turf(usr),10,10) /// Play stupid games, win stupid prizes. - - playsound(src, 'sound/arcade/Ori_win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - winprob = 0 - - else - playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - winscreen = "Aw, shucks. Try again!" - wintick = 0 - gamepaid = 0 - icon_state = "clawmachine_new" - gameStatus = "CLAWMACHINE_END" - -/obj/machinery/computer/arcade/clawmachine/emag_act(mob/user) - if(!emagged) - to_chat(user, "You modify the claw of the machine. The next one is sure to win! You just have to pay...") - name = "AlliCo Snag-A-Prize" - desc = "Get some goodies, all for you!" - instructions = "Swipe a card to play!" - winprob = 100 - gamepaid = 0 - wintick = 0 - gameStatus = "CLAWMACHINE_NEW" - emagged = 1 - return 1 - -/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params) - ..() - if(istype(O, /obj/item/stack/arcadeticket)) - var/obj/item/stack/arcadeticket/T = O - var/amount = T.get_amount() - if(amount <2) - to_chat(user, "You need 2 tickets to claim a prize!") - return - prizevend(user) - T.pay_tickets() - T.update_icon() - O = T - to_chat(user, "You turn in 2 tickets to the [src] and claim a prize!") - return - else +/obj/machinery/computer/arcade + name = "random arcade" + desc = "random arcade machine" + icon_state = "arcade1" + icon_keyboard = null + clicksound = null //Gets too spammy and makes no sense for arcade to have the console keyboard noise anyway + var/list/prizes = list( /obj/item/weapon/storage/box/snappops = 2, + /obj/item/toy/blink = 2, + /obj/item/clothing/under/syndicate/tacticool = 2, + /obj/item/toy/sword = 2, + /obj/item/weapon/storage/box/capguntoy = 2, + /obj/item/weapon/gun/projectile/revolver/toy/crossbow = 2, + /obj/item/clothing/suit/syndicatefake = 2, + /obj/item/weapon/storage/fancy/crayons = 2, + /obj/item/toy/spinningtoy = 2, + /obj/random/mech_toy = 1, + /obj/item/weapon/reagent_containers/spray/waterflower = 1, + /obj/random/action_figure = 1, + /obj/random/plushie = 1, + /obj/item/toy/cultsword = 1, + /obj/item/toy/bouquet/fake = 1, + /obj/item/clothing/accessory/badge/sheriff = 2, + /obj/item/clothing/head/cowboy/small = 2, + /obj/item/toy/stickhorse = 2 + ) + var/list/special_prizes = list() // Holds instanced objects, intended for admins to shove surprises inside or something. + +/obj/machinery/computer/arcade/Initialize() + . = ..() + // If it's a generic arcade machine, pick a random arcade + // circuit board for it and make the new machine + if(!circuit) + var/choice = pick(subtypesof(/obj/item/weapon/circuitboard/arcade) - /obj/item/weapon/circuitboard/arcade/clawmachine) + var/obj/item/weapon/circuitboard/CB = new choice() + new CB.build_path(loc, CB) + return INITIALIZE_HINT_QDEL + +/obj/machinery/computer/arcade/proc/prizevend() + if(LAZYLEN(special_prizes)) // Downstream wanted the 'win things inside contents sans circuitboard' feature kept. + var/atom/movable/AM = pick_n_take(special_prizes) + AM.forceMove(get_turf(src)) + special_prizes -= AM + + else if(LAZYLEN(prizes)) + var/prizeselect = pickweight(prizes) + new prizeselect(src.loc) + + if(istype(prizeselect, /obj/item/clothing/suit/syndicatefake)) //Helmet is part of the suit + new /obj/item/clothing/head/syndicatefake(src.loc) + +/obj/machinery/computer/arcade/attack_ai(mob/user as mob) + return attack_hand(user) + + +/obj/machinery/computer/arcade/emp_act(severity) + if(stat & (NOPOWER|BROKEN)) + ..(severity) + return + var/empprize = null + var/num_of_prizes = 0 + switch(severity) + if(1) + num_of_prizes = rand(1,4) + if(2) + num_of_prizes = rand(1,3) + if(3) + num_of_prizes = rand(0,2) + if(4) + num_of_prizes = rand(0,1) + for(num_of_prizes; num_of_prizes > 0; num_of_prizes--) + empprize = pickweight(prizes) + new empprize(src.loc) + + ..(severity) + +/////////////////// +// BATTLE HERE // +/////////////////// + +/obj/machinery/computer/arcade/battle + name = "Battler" + desc = "Fight through what space has to offer!" + icon_state = "arcade2" + icon_screen = "battler" + circuit = /obj/item/weapon/circuitboard/arcade/battle + var/enemy_name = "Space Villian" + var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc + var/enemy_action = "" + var/player_hp = 30 //Player health/attack points + var/player_mp = 10 + var/enemy_hp = 45 //Enemy health/attack points + var/enemy_mp = 20 + var/gameover = 0 + var/blocked = 0 //Player cannot attack/heal while set + var/turtle = 0 + +/obj/machinery/computer/arcade/battle/Initialize() + . = ..() + randomize_characters() + +/obj/machinery/computer/arcade/battle/proc/randomize_characters() + var/name_action + var/name_part1 + var/name_part2 + + name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") + + name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") + name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") + + enemy_name = replacetext((name_part1 + name_part2), "the ", "") + name = (name_action + name_part1 + name_part2) + + +/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) + if(..()) + return + user.set_machine(src) + tgui_interact(user) + +/obj/machinery/computer/arcade/battle/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ArcadeBattle", name) + ui.open() + +/obj/machinery/computer/arcade/battle/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["name"] = name + data["temp"] = temp + data["enemyAction"] = enemy_action + data["enemyName"] = enemy_name + data["playerHP"] = player_hp + data["playerMP"] = player_mp + data["enemyHP"] = enemy_hp + data["gameOver"] = gameover + return data + +/obj/machinery/computer/arcade/battle/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(!blocked && !gameover) + switch(action) + if("attack") + blocked = 1 + var/attackamt = rand(2,6) + temp = "You attack for [attackamt] damage!" + playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + if(turtle > 0) + turtle-- + + sleep(10) + enemy_hp -= attackamt + arcade_action() + + if("heal") + blocked = 1 + var/pointamt = rand(1,3) + var/healamt = rand(6,8) + temp = "You use [pointamt] magic to heal for [healamt] damage!" + playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + turtle++ + + sleep(10) + player_mp -= pointamt + player_hp += healamt + blocked = 1 + arcade_action() + + if("charge") + blocked = 1 + var/chargeamt = rand(4,7) + temp = "You regain [chargeamt] points" + playsound(src, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_mp += chargeamt + if(turtle > 0) + turtle-- + + sleep(10) + arcade_action() + + + if(action == "newgame") //Reset everything + temp = "New Round" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + turtle = 0 + + if(emagged) + randomize_characters() + emagged = 0 + + add_fingerprint(usr) + return TRUE + +/obj/machinery/computer/arcade/battle/proc/arcade_action() + if ((enemy_mp <= 0) || (enemy_hp <= 0)) + if(!gameover) + gameover = 1 + temp = "[enemy_name] has fallen! Rejoice!" + playsound(src, 'sound/arcade/win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + + if(emagged) + feedback_inc("arcade_win_emagged") + new /obj/effect/spawner/newbomb/timer/syndicate(src.loc) + new /obj/item/clothing/head/collectable/petehat(src.loc) + message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") + log_game("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") + randomize_characters() + emagged = 0 + else if(!contents.len) + feedback_inc("arcade_win_normal") + prizevend() + + else + feedback_inc("arcade_win_normal") + prizevend() + + else if (emagged && (turtle >= 4)) + var/boomamt = rand(5,10) + enemy_action = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" + playsound(src, 'sound/arcade/boom.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_hp -= boomamt + + else if ((enemy_mp <= 5) && (prob(70))) + var/stealamt = rand(2,3) + enemy_action = "[enemy_name] steals [stealamt] of your power!" + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_mp -= stealamt + + if (player_mp <= 0) + gameover = 1 + sleep(10) + temp = "You have been drained! GAME OVER" + if(emagged) + feedback_inc("arcade_loss_mana_emagged") + usr.gib() + else + feedback_inc("arcade_loss_mana_normal") + + else if ((enemy_hp <= 10) && (enemy_mp > 4)) + enemy_action = "[enemy_name] heals for 4 health!" + playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + enemy_hp += 4 + enemy_mp -= 4 + + else + var/attackamt = rand(3,6) + enemy_action = "[enemy_name] attacks for [attackamt] damage!" + playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_hp -= attackamt + + if ((player_mp <= 0) || (player_hp <= 0)) + gameover = 1 + temp = "You have been crushed! GAME OVER" + playsound(src, 'sound/arcade/lose.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + if(emagged) + feedback_inc("arcade_loss_hp_emagged") + usr.gib() + else + feedback_inc("arcade_loss_hp_normal") + + blocked = 0 + return + + +/obj/machinery/computer/arcade/battle/emag_act(var/charges, var/mob/user) + if(!emagged) + to_chat(user, span("notice","You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Hyper-Lethal Mode.")) + + temp = "If you die in the game, you die for real!" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + blocked = 0 + emagged = 1 + + enemy_name = "Cuban Pete" + name = "Outbomb Cuban Pete" + + return 1 + + +////////////////////////// +// ORION TRAIL HERE // +////////////////////////// +#define ORION_TRAIL_WINTURN 9 + +//Orion Trail Events +#define ORION_TRAIL_RAIDERS "Raiders" +#define ORION_TRAIL_FLUX "Interstellar Flux" +#define ORION_TRAIL_ILLNESS "Illness" +#define ORION_TRAIL_BREAKDOWN "Breakdown" +#define ORION_TRAIL_MUTINY "Mutiny?" +#define ORION_TRAIL_MUTINY_ATTACK "Mutinous Ambush" +#define ORION_TRAIL_MALFUNCTION "Malfunction" +#define ORION_TRAIL_COLLISION "Collision" +#define ORION_TRAIL_SPACEPORT "Spaceport" +#define ORION_TRAIL_BLACKHOLE "BlackHole" + +#define ORION_STATUS_START 1 +#define ORION_STATUS_NORMAL 2 +#define ORION_STATUS_GAMEOVER 3 +#define ORION_STATUS_MARKET 4 + +/obj/machinery/computer/arcade/orion_trail + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + icon_state = "arcade1" + icon_screen = "orion" + circuit = /obj/item/weapon/circuitboard/arcade/orion_trail + var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. + var/engine = 0 + var/hull = 0 + var/electronics = 0 + var/food = 80 + var/fuel = 60 + var/turns = 4 + var/alive = 4 + var/eventdat = null + var/event = null + var/list/settlers = list("Harry","Larry","Bob") + var/list/events = list(ORION_TRAIL_RAIDERS = 3, + ORION_TRAIL_FLUX = 1, + ORION_TRAIL_ILLNESS = 3, + ORION_TRAIL_BREAKDOWN = 2, + ORION_TRAIL_MUTINY = 3, + ORION_TRAIL_MALFUNCTION = 2, + ORION_TRAIL_COLLISION = 1, + ORION_TRAIL_SPACEPORT = 2 + ) + var/list/stops = list() + var/list/stopblurbs = list() + var/traitors_aboard = 0 + var/spaceport_raided = 0 + var/spaceport_freebie = 0 + var/last_spaceport_action = "" + var/gameStatus = ORION_STATUS_START + var/canContinueEvent = 0 + +/obj/machinery/computer/arcade/orion_trail/New() + ..() + // Sets up the main trail + stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") + stopblurbs = list( + "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", + "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", + "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", + "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", + "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", + "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", + "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", + "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", + "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" + ) + +/obj/machinery/computer/arcade/orion_trail/proc/newgame() + // Set names of settlers in crew + settlers = list() + for(var/i = 1; i <= 3; i++) + add_crewmember() + add_crewmember("[usr]") + // Re-set items to defaults + engine = 1 + hull = 1 + electronics = 1 + food = 80 + fuel = 60 + alive = 4 + turns = 1 + event = null + gameStatus = ORION_STATUS_NORMAL + traitors_aboard = 0 + + //spaceport junk + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + +/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/living/user) + if(..()) + return + if(fuel <= 0 || food <=0 || settlers.len == 0) + gameStatus = ORION_STATUS_GAMEOVER + event = null + user.set_machine(src) + var/dat = "" + if(gameStatus == ORION_STATUS_GAMEOVER) + playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + dat = "

    Game Over

    " + dat += "Like many before you, your crew never made it to Orion, lost to space...
    forever." + if(settlers.len == 0) + dat += "
    Your entire crew died, and your ship joins the fleet of ghost-ships littering the galaxy." + else + if(food <= 0) + dat += "
    You ran out of food and starved." + if(emagged) + user.nutrition = 0 //yeah you pretty hongry + to_chat(user, span("danger", "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.")) + if(fuel <= 0) + dat += "
    You ran out of fuel, and drift, slowly, into a star." + if(emagged) + var/mob/living/M = user + M.adjust_fire_stacks(5) + M.IgniteMob() //flew into a star, so you're on fire + to_chat(user,span("danger", "You feel an immense wave of heat emanate from \the [src]. Your skin bursts into flames.")) + dat += "

    OK...

    " + + if(emagged) + to_chat(user, span("danger", "You're never going to make it to Orion...")) + user.death() + emagged = 0 //removes the emagged status after you lose + gameStatus = ORION_STATUS_START + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + + else if(event) + dat = eventdat + else if(gameStatus == ORION_STATUS_NORMAL) + var/title = stops[turns] + var/subtext = stopblurbs[turns] + dat = "

    [title]

    " + dat += "[subtext]" + dat += "

    Crew:

    " + dat += english_list(settlers) + dat += "
    Food: [food] | Fuel: [fuel]" + dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + if(turns == 7) + dat += "

    Go Around Continue

    " + else + dat += "

    Continue

    " + dat += "

    Kill a crewmember

    " + dat += "

    Close

    " + else + dat = "

    The Orion Trail

    " + dat += "

    Experience the journey of your ancestors!



    " + dat += "
    New Game
    " + dat += "

    Close

    " + user << browse(dat,"window=arcade") + return + +/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) + if(..()) + return + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + if(busy) + return + busy = 1 + + if (href_list["continue"]) //Continue your travels + if(gameStatus == ORION_STATUS_NORMAL && !event && turns != 7) + if(turns >= ORION_TRAIL_WINTURN) + win() + else + food -= (alive+traitors_aboard)*2 + fuel -= 5 + if(turns == 2 && prob(30)) + event = ORION_TRAIL_COLLISION + event() + else if(prob(75)) + event = pickweight(events) + if(traitors_aboard) + if(event == ORION_TRAIL_MUTINY || prob(55)) + event = ORION_TRAIL_MUTINY_ATTACK + event() + turns += 1 + if(emagged) + var/mob/living/carbon/M = usr //for some vars + switch(event) + if(ORION_TRAIL_RAIDERS) + if(prob(50)) + to_chat(usr, span("warning", "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?")) + M.hallucination += 30 + else + to_chat(usr, span("danger", "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...")) + M.take_organ_damage(25) + if(ORION_TRAIL_ILLNESS) + var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS + if(severity == 1) + to_chat(M, span("warning", "You suddenly feel slightly nauseous.")) //got off lucky + if(severity == 2) + to_chat(usr, span("warning", "You suddenly feel extremely nauseous and hunch over until it passes.")) + M.Stun(3) + if(severity >= 3) //you didn't pray hard enough + to_chat(M, span("warning", "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.")) + spawn(30) + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + H.vomit() + if(ORION_TRAIL_FLUX) + if(prob(75)) + M.Weaken(3) + src.visible_message("A sudden gust of powerful wind slams \the [M] into the floor!", "You hear a large fwooshing sound, followed by a bang.") + M.take_organ_damage(15) + else + to_chat(M, span("warning", "A violent gale blows past you, and you barely manage to stay standing!")) + if(ORION_TRAIL_COLLISION) //by far the most damaging event + if(prob(90) && !hull) + var/turf/simulated/floor/F = src.loc + F.ChangeTurf(/turf/space) + src.visible_message(span("danger", "Something slams into the floor around \the [src], exposing it to space!"), "You hear something crack and break.") + else + src.visible_message("Something slams into the floor around \the [src] - luckily, it didn't get through!", "You hear something crack.") + if(ORION_TRAIL_MALFUNCTION) + src.visible_message("\The [src] buzzes and the screen goes blank for a moment before returning to the game.") + var/oldfood = food + var/oldfuel = fuel + food = rand(10,80) / rand(1,2) + fuel = rand(10,60) / rand(1,2) + if(electronics) + sleep(10) + if(oldfuel > fuel && oldfood > food) + src.audible_message("\The [src] lets out a somehow reassuring chime.", runemessage = "reassuring chime") + else if(oldfuel < fuel || oldfood < food) + src.audible_message("\The [src] lets out a somehow ominous chime.", runemessage = "ominous chime") + food = oldfood + fuel = oldfuel + + else if(href_list["newgame"]) //Reset everything + if(gameStatus == ORION_STATUS_START) + playsound(src, 'sound/arcade/Ori_begin.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + newgame() + else if(href_list["menu"]) //back to the main menu + if(gameStatus == ORION_STATUS_GAMEOVER) + gameStatus = ORION_STATUS_START + event = null + food = 80 + fuel = 60 + settlers = list("Harry","Larry","Bob") + else if(href_list["slow"]) //slow down + if(event == ORION_TRAIL_FLUX) + food -= (alive+traitors_aboard)*2 + fuel -= 5 + event = null + else if(href_list["pastblack"]) //slow down + if(turns == 7) + food -= ((alive+traitors_aboard)*2)*3 + fuel -= 15 + turns += 1 + event = null + else if(href_list["useengine"]) //use parts + if(event == ORION_TRAIL_BREAKDOWN) + engine = max(0, --engine) + event = null + else if(href_list["useelec"]) //use parts + if(event == ORION_TRAIL_MALFUNCTION) + electronics = max(0, --electronics) + event = null + else if(href_list["usehull"]) //use parts + if(event == ORION_TRAIL_COLLISION) + hull = max(0, --hull) + event = null + else if(href_list["wait"]) //wait 3 days + if(event == ORION_TRAIL_BREAKDOWN || event == ORION_TRAIL_MALFUNCTION || event == ORION_TRAIL_COLLISION) + food -= ((alive+traitors_aboard)*2)*3 + event = null + else if(href_list["keepspeed"]) //keep speed + if(event == ORION_TRAIL_FLUX) + if(prob(75)) + event = "Breakdown" + event() + else + event = null + else if(href_list["blackhole"]) //keep speed past a black hole + if(turns == 7) + if(prob(75)) + event = ORION_TRAIL_BLACKHOLE + event() + if(emagged) //has to be here because otherwise it doesn't work + src.show_message("\The [src] states, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRIFIED.","You hear something say, 'YOU ARE EXPERIENCING A BLACKHOLE. BE TERRFIED'") + to_chat(usr, span("warning", "Something draws you closer and closer to the machine.")) + sleep(10) + to_chat(usr, span("danger", "This is really starting to hurt!")) + var i; //spawning a literal blackhole would be fun, but a bit disruptive. + for(i=0;i<4;i++) + var/mob/living/L = usr + if(istype(L)) + L.adjustBruteLoss(25) + sleep(10) + else + event = null + turns += 1 + else if(href_list["holedeath"]) + if(event == ORION_TRAIL_BLACKHOLE) + gameStatus = ORION_STATUS_GAMEOVER + event = null + else if(href_list["eventclose"]) //end an event + if(canContinueEvent) + event = null + + else if(href_list["killcrew"]) //shoot a crewmember + if(gameStatus == ORION_STATUS_NORMAL || event == ORION_TRAIL_MUTINY) + playsound(src, 'sound/arcade/kill_crew.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/sheriff = remove_crewmember() //I shot the sheriff + var/mob/living/L = usr + if(!istype(L)) + return + if(settlers.len == 0 || alive == 0) + src.visible_message("\The [src] states, 'EVERYONE HAS DIED, GAMEOVER.'", "You hear something state, 'EVERYONE HAS DIED, GAMEOVER.'") + if(emagged) + src.visible_message("\The [src] produces a loud, gunlike sound.") + L.adjustBruteLoss(30) + emagged = 0 + gameStatus = ORION_STATUS_GAMEOVER + event = null + else if(emagged) + if(usr.name == sheriff) + src.visible_message("\The [src] states, 'THE CREW HAS CHOSEN TO KILL [usr]'. A gunshot can be heard coming from \the [src]", "You hear 'THE CREW HAS CHOSEN TO KILL [usr]' followed by a gunshot") + L.adjustBruteLoss(30) + if(event == ORION_TRAIL_MUTINY) //only ends the ORION_TRAIL_MUTINY event, since you can do this action in multiple places + event = null + + //Spaceport specific interactions + //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) + //they also call event() again, to regen the eventdata, which is kind of odd but necessary + else if(href_list["buycrew"]) //buy a crewmember + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided && food >= 10 && fuel >= 10) + playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/bought = add_crewmember() + last_spaceport_action = "You hired [bought] as a new crewmember." + fuel -= 10 + food -= 10 + event() + + else if(href_list["sellcrew"]) //sell a crewmember + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided && settlers.len > 1) + playsound(src, 'sound/arcade/lose_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/sold = remove_crewmember() + last_spaceport_action = "You sold your crewmember, [sold]!" + fuel += 7 + food += 7 + event() + + else if(href_list["leave_spaceport"]) + if(gameStatus == ORION_STATUS_MARKET) + event = null + gameStatus = ORION_STATUS_NORMAL + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + + else if(href_list["raid_spaceport"]) + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided) + playsound(src, 'sound/arcade/raid.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + var/success = min(15 * alive,100) //default crew (4) have a 60% chance + spaceport_raided = 1 + + var/FU = 0 + var/FO = 0 + if(prob(success)) + FU = rand(5,15) + FO = rand(5,15) + last_spaceport_action = "You successfully raided the spaceport! You gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" + else + FU = rand(-5,-15) + FO = rand(-5,-15) + last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" + + //your chance of lose a crewmember is 1/2 your chance of success + //this makes higher % failures hurt more, don't get cocky space cowboy! + if(prob(success*5)) + var/lost_crew = remove_crewmember() + last_spaceport_action = "You failed to raid the spaceport! You lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" + if(emagged) + src.visible_message("The machine states, 'YOU ARE UNDER ARREST, RAIDER!' and shoots handcuffs onto [usr]!", "You hear something say 'YOU ARE UNDER ARREST, RAIDER!' and a clinking sound") + var/obj/item/weapon/handcuffs/C = new(src.loc) + var/mob/living/carbon/human/H = usr + if(istype(H)) + C.forceMove(H) + H.handcuffed = C + H.update_handcuffed() + else + C.throw_at(usr,16,3,src) + + + fuel += FU + food += FO + event() + + else if(href_list["buyparts"]) + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided && fuel > 5) + playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + switch(text2num(href_list["buyparts"])) + if(1) //Engine Parts + engine++ + last_spaceport_action = "Bought Engine Parts" + if(2) //Hull Plates + hull++ + last_spaceport_action = "Bought Hull Plates" + if(3) //Spare Electronics + electronics++ + last_spaceport_action = "Bought Spare Electronics" + fuel -= 5 //they all cost 5 + event() + + else if(href_list["trade"]) + if(gameStatus == ORION_STATUS_MARKET) + if(!spaceport_raided) + playsound(src, 'sound/arcade/get_fuel.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + switch(text2num(href_list["trade"])) + if(1) //Fuel + if(fuel > 5) + fuel -= 5 + food += 5 + last_spaceport_action = "Traded Fuel for Food" + event() + if(2) //Food + if(food > 5) + fuel += 5 + food -= 5 + last_spaceport_action = "Traded Food for Fuel" + event() + + src.add_fingerprint(usr) + src.updateUsrDialog() + busy = 0 + return + + +/obj/machinery/computer/arcade/orion_trail/proc/event() + eventdat = "

    [event]

    " + canContinueEvent = 0 + switch(event) + if(ORION_TRAIL_RAIDERS) + eventdat += "Raiders have come aboard your ship!" + if(prob(50)) + var/sfood = rand(1,10) + var/sfuel = rand(1,10) + food -= sfood + fuel -= sfuel + eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." + else if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] tried to fight back, but was killed." + else + eventdat += "
    Fortunately, you fended them off without any trouble." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + if(ORION_TRAIL_FLUX) + playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." + eventdat += "
    What will you do?" + eventdat += "

    Slow Down Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_ILLNESS) + eventdat += "A deadly illness has been contracted!" + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by the disease." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + if(ORION_TRAIL_BREAKDOWN) + playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + eventdat += "Oh no! The engine has broken down!" + eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." + if(engine >= 1) + eventdat += "

    Use Part Wait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_MALFUNCTION) + eventdat += "The ship's systems are malfunctioning!" + eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." + if(electronics >= 1) + eventdat += "

    Use Part Wait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_COLLISION) + playsound(src, 'sound/arcade/explo.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + eventdat += "Something hit us! Looks like there's some hull damage." + if(prob(25)) + var/sfood = rand(5,15) + var/sfuel = rand(5,15) + food -= sfood + fuel -= sfuel + eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." + if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by rapid depressurization." + eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." + if(hull >= 1) + eventdat += "

    Use Part Wait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_BLACKHOLE) + eventdat += "You were swept away into the black hole." + eventdat += "

    Oh...

    " + eventdat += "

    Close

    " + settlers = list() + + if(ORION_TRAIL_MUTINY) + eventdat += "You've been hearing rumors of dissenting opinions amoungst your men." + if(settlers.len <= 2) + eventdat += "
    Your crew's so tiny you don't think anybody would risk an uprising." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + if(prob(10)) + traitors_aboard = min(++traitors_aboard,2) + else + if(traitors_aboard) //less likely to stack traitors + if(prob(20)) + traitors_aboard = min(++traitors_aboard,2) + else if(prob(70)) + traitors_aboard = min(++traitors_aboard,2) + + eventdat += "

    Kill a crewmember

    " + eventdat += "

    Risk it

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + if(ORION_TRAIL_MUTINY_ATTACK) + if(traitors_aboard <= 0) //shouldn't trigger, but hey. + eventdat += "Haha, fooled you, there isn't a mutiny on board!" + eventdat += "
    (You should report this to a coder :S)" + else + var/trait1 = remove_crewmember() + var/trait2 = "" + if(traitors_aboard >= 2) + trait2 = remove_crewmember() + + eventdat += "Oh no, some of your crew are attempting to mutiny!!" + if(trait2) + eventdat += "
    [trait1] and [trait2]'s have armed themselves with weapons!" + else + eventdat += "
    [trait1]'s armed with a weapon!" + + var/chance2attack = alive*20 + if(prob(chance2attack)) + var/chancetokill = 30*traitors_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 traitorss, 2 crew is 50% chance + if(prob(chancetokill)) + var/deadguy = remove_crewmember() + eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] up to [deadguy] and murder[trait2 ? "" : "s"] them!" + else + eventdat += "
    You valiantly fight off the traitor[trait2 ? "s":""]!" + eventdat += "
    You cut the traitor[trait2 ? "s":""] up into meat... Eww" + if(trait2) + food += 30 + traitors_aboard = max(0,traitors_aboard-2) + else + food += 15 + traitors_aboard = max(0,--traitors_aboard) + else + eventdat += "
    The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] away, What wimps!" + if(trait2) + traitors_aboard = max(0,traitors_aboard-2) + else + traitors_aboard = max(0,--traitors_aboard) + + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + canContinueEvent = 1 + + + if(ORION_TRAIL_SPACEPORT) + gameStatus = ORION_STATUS_MARKET + if(spaceport_raided) + eventdat += "The Spaceport is on high alert! they wont let you dock since you tried to attack them!" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Depart Spaceport

    " + eventdat += "

    Close

    " + else + eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" + eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." + eventdat += "
    Trading terms: FU = Fuel, FO = Food" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Crew:

    " + eventdat += english_list(settlers) + eventdat += "
    Food: [food] | Fuel: [fuel]" + eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + + + //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) + if(!spaceport_freebie && (fuel < 20 || food < 20)) + spaceport_freebie++ + var/FU = 10 + var/FO = 10 + var/freecrew = 0 + if(prob(30)) + FU = 25 + FO = 25 + + if(prob(10)) + add_crewmember() + freecrew++ + + eventdat += "
    The traders of the spaceport take pity on you, and give you some supplies. (+[FU]FU,+[FO]FO)" + if(freecrew) + eventdat += "
    You also gain a new crewmember!" + + fuel += FU + food += FO + + //CREW INTERACTIONS + eventdat += "

    Crew Management:

    " + + //Buy crew + if(food >= 10 && fuel >= 10) + eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " + else + eventdat += "

    You cannot afford a new Crewmember

    " + + //Sell crew + if(settlers.len > 1) + eventdat += "

    Sell crew for Fuel and Food (+7FU,+7FO)

    " + else + eventdat += "

    You cannot afford to sell a Crewmember

    " + + //BUY/SELL STUFF + eventdat += "

    Spare Parts:

    " + + //Engine parts + if(fuel > 5) + eventdat += "

    Buy Engine Parts (-5FU)

    " + else + eventdat += "

    You cannot afford to buy Engine Parts" + + //Hull plates + if(fuel > 5) + eventdat += "

    Buy Hull Plates (-5FU)

    " + else + eventdat += "

    You cannot afford to buy Hull Plates" + + //Electronics + if(fuel > 5) + eventdat += "

    Buy Spare Electronics (-5FU)

    " + else + eventdat += "

    You cannot afford to buy Spare Electronics" + + //Trade + if(fuel > 5) + eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " + else + eventdat += "

    You cannot afford to Trade Fuel for Food 5) + eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " + else + eventdat += "

    You cannot afford to Trade Food for Fuel\The [src] says, 'This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.'") + sleep(20) + src.visible_message(span("warning", "[src] begins to vibrate...")) + src.audible_message("\The [src] says, 'Uh, Port? Having some issues with our reactor, could you check it out? Over.'") + sleep(30) + src.audible_message("\The [src] says, 'Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-'") + sleep(3.6) + src.visible_message(span("danger", "[src] explodes!")) + explosion(src.loc, 1,2,4) + qdel(src) + +#undef ORION_TRAIL_WINTURN +#undef ORION_TRAIL_RAIDERS +#undef ORION_TRAIL_FLUX +#undef ORION_TRAIL_ILLNESS +#undef ORION_TRAIL_BREAKDOWN +#undef ORION_TRAIL_MUTINY +#undef ORION_TRAIL_MUTINY_ATTACK +#undef ORION_TRAIL_MALFUNCTION +#undef ORION_TRAIL_COLLISION +#undef ORION_TRAIL_SPACEPORT +#undef ORION_TRAIL_BLACKHOLE + +#undef ORION_STATUS_START +#undef ORION_STATUS_NORMAL +#undef ORION_STATUS_GAMEOVER +#undef ORION_STATUS_MARKET + +////////////////// +// Claw Machine // +////////////////// + +/obj/machinery/computer/arcade/clawmachine + name = "AlliCo Grab-a-Gift" + desc = "Show off your arcade skills for that special someone!" + icon_state = "clawmachine_new" + icon_keyboard = null + icon_screen = null + circuit = /obj/item/weapon/circuitboard/arcade/clawmachine + prizes = list(/obj/random/plushie) + var/wintick = 0 + var/winprob = 0 + var/instructions = "Insert 1 thaler or swipe a card to play!" + var/gameStatus = "CLAWMACHINE_NEW" + var/gamepaid = 0 + var/gameprice = 1 + var/winscreen = "" + +/// Payment +/obj/machinery/computer/arcade/clawmachine/attackby(obj/item/I as obj, mob/user as mob) + if(..()) + return + + if(gamepaid == 0 && vendor_account && !vendor_account.suspended) + var/paid = 0 + var/obj/item/weapon/card/id/W = I.GetID() + if(W) //for IDs and PDAs and wallets with IDs + paid = pay_with_card(W,I) + else if(istype(I, /obj/item/weapon/spacecash/ewallet)) + var/obj/item/weapon/spacecash/ewallet/C = I + paid = pay_with_ewallet(C) + else if(istype(I, /obj/item/weapon/spacecash)) + var/obj/item/weapon/spacecash/C = I + paid = pay_with_cash(C, user) + if(paid) + gamepaid = 1 + instructions = "Hit start to play!" + return + return + +////// Cash +/obj/machinery/computer/arcade/clawmachine/proc/pay_with_cash(var/obj/item/weapon/spacecash/cashmoney, mob/user) + if(!emagged) + if(gameprice > cashmoney.worth) + + // This is not a status display message, since it's something the character + // themselves is meant to see BEFORE putting the money in + to_chat(usr, "\icon[cashmoney][bicon(cashmoney)] That is not enough money.") + return 0 + + if(istype(cashmoney, /obj/item/weapon/spacecash)) + + visible_message("\The [usr] inserts some cash into \the [src].") + cashmoney.worth -= gameprice + + if(cashmoney.worth <= 0) + usr.drop_from_inventory(cashmoney) + qdel(cashmoney) + else + cashmoney.update_icon() + + // Machine has no idea who paid with cash + credit_purchase("(cash)") + return 1 + if(emagged) + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + to_chat(user, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") + + +///// Ewallet +/obj/machinery/computer/arcade/clawmachine/proc/pay_with_ewallet(var/obj/item/weapon/spacecash/ewallet/wallet) + if(!emagged) + visible_message("\The [usr] swipes \the [wallet] through \the [src].") + playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) + if(gameprice > wallet.worth) + visible_message("Insufficient funds.") + return 0 + else + wallet.worth -= gameprice + credit_purchase("[wallet.owner_name] (chargecard)") + return 1 + if(emagged) + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + to_chat(usr, "It doesn't seem to accept that! Seem you'll need to swipe a valid ID.") + +///// ID +/obj/machinery/computer/arcade/clawmachine/proc/pay_with_card(var/obj/item/weapon/card/id/I, var/obj/item/ID_container) + if(I==ID_container || ID_container == null) + visible_message("\The [usr] swipes \the [I] through \the [src].") + else + visible_message("\The [usr] swipes \the [ID_container] through \the [src].") + playsound(src, 'sound/machines/id_swipe.ogg', 50, 1) + var/datum/money_account/customer_account = get_account(I.associated_account_number) + if(!customer_account) + visible_message("Error: Unable to access account. Please contact technical support if problem persists.") + return 0 + + if(customer_account.suspended) + visible_message("Unable to access account: account suspended.") + return 0 + + // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is + // empty at high security levels + if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + var/attempt_pin = tgui_input_number(usr, "Enter pin code", "Vendor transaction") + customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) + + if(!customer_account) + visible_message("Unable to access account: incorrect credentials.") + return 0 + + if(gameprice > customer_account.money) + visible_message("Insufficient funds in account.") + return 0 + else + // Okay to move the money at this point + if(emagged) + gameprice = customer_account.money + // debit money from the purchaser's account + customer_account.money -= gameprice + + // create entry in the purchaser's account log + var/datum/transaction/T = new() + T.target_name = "[vendor_account.owner_name] (via [name])" + T.purpose = "Purchase of arcade game([name])" + if(gameprice > 0) + T.amount = "([gameprice])" + else + T.amount = "[gameprice]" + T.source_terminal = name + T.date = current_date_string + T.time = stationtime2text() + customer_account.transaction_log.Add(T) + + // Give the vendor the money. We use the account owner name, which means + // that purchases made with stolen/borrowed card will look like the card + // owner made them + credit_purchase(customer_account.owner_name) + return 1 + +/// Add to vendor account + +/obj/machinery/computer/arcade/clawmachine/proc/credit_purchase(var/target as text) + vendor_account.money += gameprice + + var/datum/transaction/T = new() + T.target_name = target + T.purpose = "Purchase of arcade game([name])" + T.amount = "[gameprice]" + T.source_terminal = name + T.date = current_date_string + T.time = stationtime2text() + vendor_account.transaction_log.Add(T) + +/// End Payment + +/obj/machinery/computer/arcade/clawmachine/New() + ..() + +/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/living/user) + if(..()) + return + tgui_interact(user) + +/// TGUI Stuff + +/obj/machinery/computer/arcade/clawmachine/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ClawMachine", name, ui_x = 300, ui_y = 400) + ui.autoupdate = TRUE + ui.open() + +/obj/machinery/computer/arcade/clawmachine/tgui_data(mob/user) + var/list/data = list() + + data["wintick"] = wintick + data["instructions"] = instructions + data["gameStatus"] = gameStatus + data["winscreen"] = winscreen + + return data + +/obj/machinery/computer/arcade/clawmachine/tgui_act(action, params) + if(..()) + return + + if(action == "newgame" && gamepaid == 0) + playsound(src, 'sound/arcade/steal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + + if(action == "newgame" && gamepaid == 1) + gameStatus = "CLAWMACHINE_ON" + icon_state = "clawmachine_new_move" + instructions = "Guide the claw to the prize you want!" + wintick = 0 + + if(action == "return" && gameStatus == "CLAWMACHINE_END") + gameStatus = "CLAWMACHINE_NEW" + + if(action == "pointless" && wintick < 10) + wintick += 1 + + if(action == "pointless" && wintick >= 10) + instructions = "Insert 1 thaler or swipe a card to play!" + clawvend() + +/obj/machinery/computer/arcade/clawmachine/proc/clawvend() /// True to a real claw machine, it's NEARLY impossible to win. + winprob += 1 /// Yeah. + + if(prob(winprob)) /// YEAH. + if(!emagged) + prizevend() + winscreen = "You won!" + else if(emagged) + gameprice = 1 + emagged = 0 + winscreen = "You won...?" + var/obj/item/weapon/grenade/G = new /obj/item/weapon/grenade/explosive(get_turf(src)) /// YEAAAAAAAAAAAAAAAAAAH!!!!!!!!!! + G.activate() + G.throw_at(get_turf(usr),10,10) /// Play stupid games, win stupid prizes. + + playsound(src, 'sound/arcade/Ori_win.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + winprob = 0 + + else + playsound(src, 'sound/arcade/Ori_fail.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + winscreen = "Aw, shucks. Try again!" + wintick = 0 + gamepaid = 0 + icon_state = "clawmachine_new" + gameStatus = "CLAWMACHINE_END" + +/obj/machinery/computer/arcade/clawmachine/emag_act(mob/user) + if(!emagged) + to_chat(user, "You modify the claw of the machine. The next one is sure to win! You just have to pay...") + name = "AlliCo Snag-A-Prize" + desc = "Get some goodies, all for you!" + instructions = "Swipe a card to play!" + winprob = 100 + gamepaid = 0 + wintick = 0 + gameStatus = "CLAWMACHINE_NEW" + emagged = 1 + return 1 + +/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params) + ..() + if(istype(O, /obj/item/stack/arcadeticket)) + var/obj/item/stack/arcadeticket/T = O + var/amount = T.get_amount() + if(amount <2) + to_chat(user, "You need 2 tickets to claim a prize!") + return + prizevend(user) + T.pay_tickets() + T.update_icon() + O = T + to_chat(user, "You turn in 2 tickets to the [src] and claim a prize!") + return + else ..() //You can now actually deconstruct these. \ No newline at end of file diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 5f4ff28e058..dadd437b396 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -1,114 +1,114 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/structure/computerframe - density = TRUE - anchored = FALSE - name = "computer frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "0" - var/state = 0 - var/obj/item/weapon/circuitboard/circuit = null -// weight = 1.0E8 - -/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob) - switch(state) - if(0) - if(P.is_wrench()) - playsound(src, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed)) - to_chat(user, "You wrench the frame into place.") - src.anchored = TRUE - src.state = 1 - if(istype(P, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = P - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - new /obj/item/stack/material/steel( src.loc, 5 ) - qdel(src) - if(1) - if(P.is_wrench()) - playsound(src, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed)) - to_chat(user, "You unfasten the frame.") - src.anchored = FALSE - src.state = 0 - if(istype(P, /obj/item/weapon/circuitboard) && !circuit) - var/obj/item/weapon/circuitboard/B = P - if(B.board_type == "computer") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - src.icon_state = "1" - src.circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(P.is_screwdriver() && circuit) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - src.state = 2 - src.icon_state = "2" - if(P.is_crowbar()) && circuit) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - src.state = 1 - src.icon_state = "0" - circuit.loc = src.loc - src.circuit = null - if(2) - if(P.is_screwdriver() && circuit) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - src.state = 1 - src.icon_state = "1" - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if (C.get_amount() < 5) - to_chat(user, "You need five coils of wire to add them to the frame.") - return - to_chat(user, "You start to add cables to the frame.") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 20) && state == 2) - if (C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - if(3) - if(P.is_wirecutter()) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - src.state = 2 - src.icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) - A.amount = 5 - - if(istype(P, /obj/item/stack/material) && P.get_material_name() == "glass") - var/obj/item/stack/G = P - if (G.get_amount() < 2) - to_chat(user, "You need two sheets of glass to put in the glass panel.") - return - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You start to put in the glass panel.") - if(do_after(user, 20) && state == 3) - if (G.use(2)) - to_chat(user, "You put in the glass panel.") - src.state = 4 - src.icon_state = "4" - if(4) - if(P.is_crowbar()) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - src.state = 3 - src.icon_state = "3" - new /obj/item/stack/material/glass( src.loc, 2 ) - if(P.is_screwdriver()) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new src.circuit.build_path ( src.loc ) - src.circuit.construct(B) - qdel(src) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/structure/computerframe + density = TRUE + anchored = FALSE + name = "computer frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "0" + var/state = 0 + var/obj/item/weapon/circuitboard/circuit = null +// weight = 1.0E8 + +/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob) + switch(state) + if(0) + if(P.has_tool_quality(TOOL_WRENCH)) + playsound(src, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed)) + to_chat(user, "You wrench the frame into place.") + src.anchored = TRUE + src.state = 1 + if(P.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = P.get_welder() + if(!WT.remove_fuel(0, user)) + to_chat(user, "The welding tool must be on to complete this task.") + return + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + to_chat(user, "You deconstruct the frame.") + new /obj/item/stack/material/steel( src.loc, 5 ) + qdel(src) + if(1) + if(P.has_tool_quality(TOOL_WRENCH)) + playsound(src, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed)) + to_chat(user, "You unfasten the frame.") + src.anchored = FALSE + src.state = 0 + if(istype(P, /obj/item/weapon/circuitboard) && !circuit) + var/obj/item/weapon/circuitboard/B = P + if(B.board_type == "computer") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + src.icon_state = "1" + src.circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + src.state = 2 + src.icon_state = "2" + if(P.has_tool_quality(TOOL_CROWBAR)) && circuit) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + src.state = 1 + src.icon_state = "0" + circuit.loc = src.loc + src.circuit = null + if(2) + if(P.has_tool_quality(TOOL_SCREWDRIVER) && circuit) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + src.state = 1 + src.icon_state = "1" + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if (C.get_amount() < 5) + to_chat(user, "You need five coils of wire to add them to the frame.") + return + to_chat(user, "You start to add cables to the frame.") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 20) && state == 2) + if (C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + if(3) + if(P.has_tool_quality(TOOL_WIRECUTTER)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + src.state = 2 + src.icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) + A.amount = 5 + + if(istype(P, /obj/item/stack/material) && P.get_material_name() == "glass") + var/obj/item/stack/G = P + if (G.get_amount() < 2) + to_chat(user, "You need two sheets of glass to put in the glass panel.") + return + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You start to put in the glass panel.") + if(do_after(user, 20) && state == 3) + if (G.use(2)) + to_chat(user, "You put in the glass panel.") + src.state = 4 + src.icon_state = "4" + if(4) + if(P.has_tool_quality(TOOL_CROWBAR)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + src.state = 3 + src.icon_state = "3" + new /obj/item/stack/material/glass( src.loc, 2 ) + if(P.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new src.circuit.build_path ( src.loc ) + src.circuit.construct(B) + qdel(src) diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 3af4562f099..34caeb2d357 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,206 +1,206 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/machinery/computer/security - name = "security camera monitor" - desc = "Used to access the various cameras on the station." - - icon_keyboard = "security_key" - icon_screen = "cameras" - light_color = "#a91515" - circuit = /obj/item/weapon/circuitboard/security - - var/mapping = 0//For the overview file, interesting bit of code. - var/list/network = list() - - var/datum/tgui_module/camera/camera - var/camera_datum_type = /datum/tgui_module/camera - -/obj/machinery/computer/security/Initialize() - . = ..() - if(!LAZYLEN(network)) - network = get_default_networks() - camera = new camera_datum_type(src, network) - -/obj/machinery/computer/security/proc/get_default_networks() - . = using_map.station_networks.Copy() - -/obj/machinery/computer/security/Destroy() - QDEL_NULL(camera) - return ..() - -/obj/machinery/computer/security/tgui_interact(mob/user, datum/tgui/ui = null) - camera.tgui_interact(user, ui) - -/obj/machinery/computer/security/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/security/attack_robot(mob/user) - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - if(!R.shell) - return attack_hand(user) - ..() - -/obj/machinery/computer/security/attack_ai(mob/user) - if(isAI(user)) - to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") - return - attack_hand(user) - -/obj/machinery/computer/security/proc/set_network(list/new_network) - network = new_network - camera.network = network - camera.access_based = FALSE - -//Camera control: arrow keys. -/obj/machinery/computer/security/telescreen - name = "Telescreen" - desc = "Used for watching an empty arena." - icon_state = "wallframe" - layer = ABOVE_WINDOW_LAYER - icon_keyboard = null - icon_screen = null - light_range_on = 0 - network = list(NETWORK_THUNDER) - density = FALSE - circuit = null - -GLOBAL_LIST_EMPTY(entertainment_screens) -/obj/machinery/computer/security/telescreen/entertainment - name = "entertainment monitor" - desc = "Damn, why do they never have anything interesting on these things? (Alt-click to toggle the display)" - icon = 'icons/obj/entertainment_monitor.dmi' - icon_state = "screen" - icon_screen = null - light_color = "#FFEEDB" - light_range_on = 2 - network = list(NETWORK_THUNDER) - circuit = /obj/item/weapon/circuitboard/security/telescreen/entertainment - camera_datum_type = /datum/tgui_module/camera/bigscreen - - var/obj/item/device/radio/radio = null - var/obj/effect/overlay/vis/pinboard - var/datum/weakref/showing - - var/enabled = TRUE // on or off - -/obj/machinery/computer/security/telescreen/entertainment/Initialize() - GLOB.entertainment_screens += src - - var/static/icon/mask = icon('icons/obj/entertainment_monitor.dmi', "mask") - - add_overlay("glass") - - pinboard = new() - pinboard.icon = icon - pinboard.icon_state = "pinboard" - pinboard.layer = 0.1 - pinboard.vis_flags = VIS_UNDERLAY|VIS_INHERIT_ID|VIS_INHERIT_PLANE - pinboard.appearance_flags = KEEP_TOGETHER - pinboard.add_filter("screen cutter", 1, alpha_mask_filter(icon = mask)) - vis_contents += pinboard - - . = ..() - - radio = new(src) - radio.listening = TRUE - radio.broadcasting = FALSE - radio.set_frequency(ENT_FREQ) - radio.canhear_range = world.view // Same as default sight range. - power_change() - -/obj/machinery/computer/security/telescreen/entertainment/Destroy() - if(showing) - stop_showing() - vis_contents.Cut() - qdel_null(pinboard) - qdel_null(radio) - return ..() - -/obj/machinery/computer/security/telescreen/entertainment/proc/toggle() - enabled = !enabled - if(!enabled) - stop_showing() - radio?.on = FALSE - else if(operable()) - radio?.on = TRUE - -/obj/machinery/computer/security/telescreen/entertainment/Click(location, control, params) - var/list/modifiers = params2list(params) - if(modifiers["alt"]) - if(isliving(usr) && Adjacent(usr) && !usr.incapacitated()) - toggle() - visible_message("[usr] toggles [src] [enabled ? "on" : "off"].","You toggle [src] [enabled ? "on" : "off"].", runemessage = "click") - else - attack_hand(usr) - -/obj/machinery/computer/security/telescreen/entertainment/update_icon() - return // NUH - -/obj/machinery/computer/security/telescreen/entertainment/proc/show_thing(atom/thing) - if(!enabled) - return - if(showing) - stop_showing() - if(stat & NOPOWER) - return - showing = WEAKREF(thing) - pinboard.vis_contents = list(thing) - -/obj/machinery/computer/security/telescreen/entertainment/proc/stop_showing() - // Reverse of the above - pinboard.vis_contents = null - showing = null - -/obj/machinery/computer/security/telescreen/entertainment/proc/maybe_stop_showing(datum/weakref/thingref) - if(showing == thingref) - stop_showing() - -/obj/machinery/computer/security/telescreen/entertainment/power_change() - ..() - if(stat & NOPOWER) - radio?.on = FALSE - stop_showing() - else if(enabled) - radio?.on = TRUE - -/obj/machinery/computer/security/wooden_tv - name = "security camera monitor" - desc = "An old TV hooked into the station's camera network." - icon_state = "television" - icon_keyboard = null - icon_screen = "detective_tv" - circuit = /obj/item/weapon/circuitboard/security/tv - light_color = "#3848B3" - light_power_on = 0.5 - -/obj/machinery/computer/security/mining - name = "outpost camera monitor" - desc = "Used to watch over mining operations." - icon_keyboard = "mining_key" - icon_screen = "mining" - network = list("Mining Outpost") - circuit = /obj/item/weapon/circuitboard/security/mining - light_color = "#F9BBFC" - -/obj/machinery/computer/security/engineering - name = "engineering camera monitor" - desc = "Used to monitor fires and breaches." - icon_keyboard = "power_key" - icon_screen = "engie_cams" - circuit = /obj/item/weapon/circuitboard/security/engineering - light_color = "#FAC54B" - -/obj/machinery/computer/security/engineering/get_default_networks() - . = engineering_networks.Copy() - -/obj/machinery/computer/security/nuclear - name = "head mounted camera monitor" - desc = "Used to access the built-in cameras in helmets." - icon_state = "syndicam" - network = list(NETWORK_MERCENARY) - circuit = null - req_access = list(150) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/machinery/computer/security + name = "security camera monitor" + desc = "Used to access the various cameras on the station." + + icon_keyboard = "security_key" + icon_screen = "cameras" + light_color = "#a91515" + circuit = /obj/item/weapon/circuitboard/security + + var/mapping = 0//For the overview file, interesting bit of code. + var/list/network = list() + + var/datum/tgui_module/camera/camera + var/camera_datum_type = /datum/tgui_module/camera + +/obj/machinery/computer/security/Initialize() + . = ..() + if(!LAZYLEN(network)) + network = get_default_networks() + camera = new camera_datum_type(src, network) + +/obj/machinery/computer/security/proc/get_default_networks() + . = using_map.station_networks.Copy() + +/obj/machinery/computer/security/Destroy() + QDEL_NULL(camera) + return ..() + +/obj/machinery/computer/security/tgui_interact(mob/user, datum/tgui/ui = null) + camera.tgui_interact(user, ui) + +/obj/machinery/computer/security/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/security/attack_robot(mob/user) + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(!R.shell) + return attack_hand(user) + ..() + +/obj/machinery/computer/security/attack_ai(mob/user) + if(isAI(user)) + to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") + return + attack_hand(user) + +/obj/machinery/computer/security/proc/set_network(list/new_network) + network = new_network + camera.network = network + camera.access_based = FALSE + +//Camera control: arrow keys. +/obj/machinery/computer/security/telescreen + name = "Telescreen" + desc = "Used for watching an empty arena." + icon_state = "wallframe" + layer = ABOVE_WINDOW_LAYER + icon_keyboard = null + icon_screen = null + light_range_on = 0 + network = list(NETWORK_THUNDER) + density = FALSE + circuit = null + +GLOBAL_LIST_EMPTY(entertainment_screens) +/obj/machinery/computer/security/telescreen/entertainment + name = "entertainment monitor" + desc = "Damn, why do they never have anything interesting on these things? (Alt-click to toggle the display)" + icon = 'icons/obj/entertainment_monitor.dmi' + icon_state = "screen" + icon_screen = null + light_color = "#FFEEDB" + light_range_on = 2 + network = list(NETWORK_THUNDER) + circuit = /obj/item/weapon/circuitboard/security/telescreen/entertainment + camera_datum_type = /datum/tgui_module/camera/bigscreen + + var/obj/item/device/radio/radio = null + var/obj/effect/overlay/vis/pinboard + var/datum/weakref/showing + + var/enabled = TRUE // on or off + +/obj/machinery/computer/security/telescreen/entertainment/Initialize() + GLOB.entertainment_screens += src + + var/static/icon/mask = icon('icons/obj/entertainment_monitor.dmi', "mask") + + add_overlay("glass") + + pinboard = new() + pinboard.icon = icon + pinboard.icon_state = "pinboard" + pinboard.layer = 0.1 + pinboard.vis_flags = VIS_UNDERLAY|VIS_INHERIT_ID|VIS_INHERIT_PLANE + pinboard.appearance_flags = KEEP_TOGETHER + pinboard.add_filter("screen cutter", 1, alpha_mask_filter(icon = mask)) + vis_contents += pinboard + + . = ..() + + radio = new(src) + radio.listening = TRUE + radio.broadcasting = FALSE + radio.set_frequency(ENT_FREQ) + radio.canhear_range = world.view // Same as default sight range. + power_change() + +/obj/machinery/computer/security/telescreen/entertainment/Destroy() + if(showing) + stop_showing() + vis_contents.Cut() + qdel_null(pinboard) + qdel_null(radio) + return ..() + +/obj/machinery/computer/security/telescreen/entertainment/proc/toggle() + enabled = !enabled + if(!enabled) + stop_showing() + radio?.on = FALSE + else if(operable()) + radio?.on = TRUE + +/obj/machinery/computer/security/telescreen/entertainment/Click(location, control, params) + var/list/modifiers = params2list(params) + if(modifiers["alt"]) + if(isliving(usr) && Adjacent(usr) && !usr.incapacitated()) + toggle() + visible_message("[usr] toggles [src] [enabled ? "on" : "off"].","You toggle [src] [enabled ? "on" : "off"].", runemessage = "click") + else + attack_hand(usr) + +/obj/machinery/computer/security/telescreen/entertainment/update_icon() + return // NUH + +/obj/machinery/computer/security/telescreen/entertainment/proc/show_thing(atom/thing) + if(!enabled) + return + if(showing) + stop_showing() + if(stat & NOPOWER) + return + showing = WEAKREF(thing) + pinboard.vis_contents = list(thing) + +/obj/machinery/computer/security/telescreen/entertainment/proc/stop_showing() + // Reverse of the above + pinboard.vis_contents = null + showing = null + +/obj/machinery/computer/security/telescreen/entertainment/proc/maybe_stop_showing(datum/weakref/thingref) + if(showing == thingref) + stop_showing() + +/obj/machinery/computer/security/telescreen/entertainment/power_change() + ..() + if(stat & NOPOWER) + radio?.on = FALSE + stop_showing() + else if(enabled) + radio?.on = TRUE + +/obj/machinery/computer/security/wooden_tv + name = "security camera monitor" + desc = "An old TV hooked into the station's camera network." + icon_state = "television" + icon_keyboard = null + icon_screen = "detective_tv" + circuit = /obj/item/weapon/circuitboard/security/tv + light_color = "#3848B3" + light_power_on = 0.5 + +/obj/machinery/computer/security/mining + name = "outpost camera monitor" + desc = "Used to watch over mining operations." + icon_keyboard = "mining_key" + icon_screen = "mining" + network = list("Mining Outpost") + circuit = /obj/item/weapon/circuitboard/security/mining + light_color = "#F9BBFC" + +/obj/machinery/computer/security/engineering + name = "engineering camera monitor" + desc = "Used to monitor fires and breaches." + icon_keyboard = "power_key" + icon_screen = "engie_cams" + circuit = /obj/item/weapon/circuitboard/security/engineering + light_color = "#FAC54B" + +/obj/machinery/computer/security/engineering/get_default_networks() + . = engineering_networks.Copy() + +/obj/machinery/computer/security/nuclear + name = "head mounted camera monitor" + desc = "Used to access the built-in cameras in helmets." + icon_state = "syndicam" + network = list(NETWORK_MERCENARY) + circuit = null + req_access = list(150) diff --git a/code/game/machinery/computer/camera_vr.dm b/code/game/machinery/computer/camera_vr.dm index 372a5bfb11e..7f9f88d877f 100644 --- a/code/game/machinery/computer/camera_vr.dm +++ b/code/game/machinery/computer/camera_vr.dm @@ -1,35 +1,35 @@ -/obj/machinery/computer/security/abductor - name = "camera uplink" - desc = "Used for hacking into camera networks" - icon = 'icons/obj/abductor.dmi' - icon_state = "camera" - network = list( "Mercenary", - "Cargo", - "Circuits", - "Civilian", - "Command", - "Engine", - "Engineering", - "Exploration", - "Medical", - "Mining Outpost", - "Outside", - "Research", - "Research Outpost", - "Robots", - "Security", - "Telecommunications", - "Tether", - "TalonShip", - "Entertainment", - "Communicators" - ) - -/obj/machinery/computer/security/xenobio - name = "xenobiology camera monitor" - desc = "Used to access the xenobiology cell cameras." - icon_keyboard = "mining_key" - icon_screen = "mining" - network = list(NETWORK_XENOBIO) - circuit = /obj/item/weapon/circuitboard/security/xenobio +/obj/machinery/computer/security/abductor + name = "camera uplink" + desc = "Used for hacking into camera networks" + icon = 'icons/obj/abductor.dmi' + icon_state = "camera" + network = list( "Mercenary", + "Cargo", + "Circuits", + "Civilian", + "Command", + "Engine", + "Engineering", + "Exploration", + "Medical", + "Mining Outpost", + "Outside", + "Research", + "Research Outpost", + "Robots", + "Security", + "Telecommunications", + "Tether", + "TalonShip", + "Entertainment", + "Communicators" + ) + +/obj/machinery/computer/security/xenobio + name = "xenobiology camera monitor" + desc = "Used to access the xenobiology cell cameras." + icon_keyboard = "mining_key" + icon_screen = "mining" + network = list(NETWORK_XENOBIO) + circuit = /obj/item/weapon/circuitboard/security/xenobio light_color = "#F9BBFC" \ No newline at end of file diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index e878aaaf8a7..acc7dcb975e 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,32 +1,32 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -// The communications computer -/obj/machinery/computer/communications - name = "command and communications console" - desc = "Used to command and control the station. Can relay long-range communications." - icon_keyboard = "tech_key" - icon_screen = "comm" - light_color = "#0099ff" - req_access = list(access_heads) - circuit = /obj/item/weapon/circuitboard/communications - - var/datum/tgui_module/communications/communications - -/obj/machinery/computer/communications/Initialize() - . = ..() - communications = new(src) - -/obj/machinery/computer/communications/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - emagged = TRUE - communications.emagged = TRUE - to_chat(user, "You scramble the communication routing circuits!") - return TRUE - -/obj/machinery/computer/communications/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/communications/attack_hand(mob/user) - if(..()) - return - communications.tgui_interact(user) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// The communications computer +/obj/machinery/computer/communications + name = "command and communications console" + desc = "Used to command and control the station. Can relay long-range communications." + icon_keyboard = "tech_key" + icon_screen = "comm" + light_color = "#0099ff" + req_access = list(access_heads) + circuit = /obj/item/weapon/circuitboard/communications + + var/datum/tgui_module/communications/communications + +/obj/machinery/computer/communications/Initialize() + . = ..() + communications = new(src) + +/obj/machinery/computer/communications/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = TRUE + communications.emagged = TRUE + to_chat(user, "You scramble the communication routing circuits!") + return TRUE + +/obj/machinery/computer/communications/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/communications/attack_hand(mob/user) + if(..()) + return + communications.tgui_interact(user) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 1af31afe80d..cf1ce8ab20b 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -1,35 +1,35 @@ -/obj/machinery/computer/crew - name = "crew monitoring computer" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - icon_keyboard = "med_key" - icon_screen = "crew" - light_color = "#315ab4" - use_power = USE_POWER_IDLE - idle_power_usage = 250 - active_power_usage = 500 - circuit = /obj/item/weapon/circuitboard/crew - var/datum/tgui_module/crew_monitor/crew_monitor - -/obj/machinery/computer/crew/New() - crew_monitor = new(src) - ..() - -/obj/machinery/computer/crew/Destroy() - qdel(crew_monitor) - crew_monitor = null - ..() - -/obj/machinery/computer/crew/attack_ai(mob/user) - attack_hand(user) - -/obj/machinery/computer/crew/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/crew/tgui_interact(mob/user, datum/tgui/ui = null) - crew_monitor.tgui_interact(user, ui) - -/obj/machinery/computer/crew/interact(mob/user) - crew_monitor.tgui_interact(user) +/obj/machinery/computer/crew + name = "crew monitoring computer" + desc = "Used to monitor active health sensors built into most of the crew's uniforms." + icon_keyboard = "med_key" + icon_screen = "crew" + light_color = "#315ab4" + use_power = USE_POWER_IDLE + idle_power_usage = 250 + active_power_usage = 500 + circuit = /obj/item/weapon/circuitboard/crew + var/datum/tgui_module/crew_monitor/crew_monitor + +/obj/machinery/computer/crew/New() + crew_monitor = new(src) + ..() + +/obj/machinery/computer/crew/Destroy() + qdel(crew_monitor) + crew_monitor = null + ..() + +/obj/machinery/computer/crew/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/crew/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/crew/tgui_interact(mob/user, datum/tgui/ui = null) + crew_monitor.tgui_interact(user, ui) + +/obj/machinery/computer/crew/interact(mob/user) + crew_monitor.tgui_interact(user) diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 69c4e279e31..c54fb1ffb8b 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,514 +1,514 @@ -#define MED_DATA_R_LIST 2 // Record list -#define MED_DATA_MAINT 3 // Records maintenance -#define MED_DATA_RECORD 4 // Record -#define MED_DATA_V_DATA 5 // Virus database -#define MED_DATA_MEDBOT 6 // Medbot monitor - -#define FIELD(N, V, E) list(field = N, value = V, edit = E) -#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) - -/obj/machinery/computer/med_data//TODO:SANITY - name = "medical records console" - desc = "Used to view, edit and maintain medical records." - icon_keyboard = "med_key" - icon_screen = "medcomp" - light_color = "#315ab4" - req_one_access = list(access_medical, access_forensics_lockers, access_robotics) - circuit = /obj/item/weapon/circuitboard/med_data - var/obj/item/weapon/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/list/temp = null - var/printing = null - // The below are used to make modal generation more convenient - var/static/list/field_edit_questions - var/static/list/field_edit_choices - - -/obj/machinery/computer/med_data/Initialize() - . = ..() - field_edit_questions = list( - // General - "sex" = "Please select new sex:", - "species" = "Please input new species:", - "age" = "Please input new age:", - "fingerprint" = "Please input new fingerprint hash:", - "p_stat" = "Please select new physical status:", - "m_stat" = "Please select new mental status:", - // Medical - "id_gender" = "Please select new gender identity:", - "blood_type" = "Please select new blood type:", - "blood_reagent" = "Please select new blood basis:", - "b_dna" = "Please input new DNA:", - "mi_dis" = "Please input new minor disabilities:", - "mi_dis_d" = "Please summarize minor disabilities:", - "ma_dis" = "Please input new major disabilities:", - "ma_dis_d" = "Please summarize major disabilities:", - "alg" = "Please input new allergies:", - "alg_d" = "Please summarize allergies:", - "cdi" = "Please input new current diseases:", - "cdi_d" = "Please summarize current diseases:", - "notes" = "Please input new important notes:", - ) - field_edit_choices = list( - // General - "sex" = all_genders_text_list, - "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), - "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), - // Medical - "id_gender" = all_genders_text_list, - "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"), - ) - -/obj/machinery/computer/med_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/med_data/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(!usr || usr.stat || usr.lying) return - - if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") - scan.loc = get_turf(src) - if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) - usr.put_in_hands(scan) - scan = null - else - to_chat(usr, "There is nothing to remove from the console.") - return - -/obj/machinery/computer/med_data/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) - O.loc = src - scan = O - to_chat(user, "You insert \the [O].") - tgui_interact(user) - else - ..() - -/obj/machinery/computer/med_data/attack_ai(user as mob) - return attack_hand(user) - -/obj/machinery/computer/med_data/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - tgui_interact(user) - - -/obj/machinery/computer/med_data/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "MedicalRecords", "Medical Records") // 800, 380 - ui.open() - ui.set_autoupdate(FALSE) - - -/obj/machinery/computer/med_data/tgui_data(mob/user) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["rank"] = rank - data["screen"] = screen - data["printing"] = printing - data["isAI"] = isAI(user) - data["isRobot"] = isrobot(user) - if(authenticated) - switch(screen) - if(MED_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) - if(MED_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = FIELD("Name", active1.fields["name"], null) - fields[++fields.len] = FIELD("ID", active1.fields["id"], null) - fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") - fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") - fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") - fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") - fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat") - fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat") - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = active1.fields["photo-south"] - photos[++photos.len] = active1.fields["photo-west"] - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/medical = list() - data["medical"] = medical - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - var/list/fields = list() - medical["fields"] = fields - fields[++fields.len] = MED_FIELD("Gender identity", active2.fields["id_gender"], "id_gender", TRUE) - fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["b_type"], "blood_type", FALSE) - fields[++fields.len] = MED_FIELD("Blood Basis", active2.fields["blood_reagent"], "blood_reagent", FALSE) - fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE) - fields[++fields.len] = MED_FIELD("Brain Type", active2.fields["brain_type"], "brain_type", TRUE) - fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - medical["comments"] = active2.fields["comments"] - medical["empty"] = 0 - else - medical["empty"] = 1 - if(MED_DATA_V_DATA) - data["virus"] = list() - for(var/ID in virusDB) - var/datum/data/record/v = virusDB[ID] - data["virus"] += list(list("name" = v.fields["name"], "D" = "\ref[v]")) - if(MED_DATA_MEDBOT) - data["medbots"] = list() - for(var/mob/living/bot/medbot/M in mob_list) - if(M.z != z) - continue - var/turf/T = get_turf(M) - if(T) - var/medbot = list() - var/area/A = get_area(T) - medbot["name"] = M.name - medbot["area"] = A.name - medbot["x"] = T.x - medbot["y"] = T.y - medbot["on"] = M.on - if(!isnull(M.reagent_glass) && M.use_beaker) - medbot["use_beaker"] = 1 - medbot["total_volume"] = M.reagent_glass.reagents.total_volume - medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume - else - medbot["use_beaker"] = 0 - data["medbots"] += list(medbot) - - data["modal"] = tgui_modal_data(src) - return data - -/obj/machinery/computer/med_data/tgui_act(action, params) - if(..()) - return TRUE - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.medical.Find(active2)) - active2 = null - - . = TRUE - if(tgui_act_modal(action, params)) - return - - switch(action) - if("cleartemp") - temp = null - if("scan") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/weapon/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - if("login") - var/login_type = text2num(params["login_type"]) - if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - else if(login_type == LOGIN_TYPE_AI && isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - if(authenticated) - active1 = null - active2 = null - screen = MED_DATA_R_LIST - else - . = FALSE - - if(.) - return - - if(authenticated) - . = TRUE - switch(action) - if("logout") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - authenticated = null - screen = null - active1 = null - active2 = null - if("screen") - screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT) - active1 = null - active2 = null - if("vir") - var/datum/data/record/v = locate(params["vir"]) - if(!istype(v)) - return FALSE - tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"]) - if("del_all") - for(var/datum/data/record/R in data_core.medical) - qdel(R) - set_temp("All medical records deleted.") - if("del_r") - if(active2) - set_temp("Medical record deleted.") - qdel(active2) - if("d_rec") - var/datum/data/record/general_record = locate(params["d_rec"] || "") - if(!data_core.general.Find(general_record)) - set_temp("Record not found.", "danger") - return - - var/datum/data/record/medical_record - for(var/datum/data/record/M in data_core.medical) - if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) - medical_record = M - break - - active1 = general_record - active2 = medical_record - screen = MED_DATA_RECORD - if("new") - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Medical Record #[R.fields["id"]]" - R.fields["b_type"] = "Unknown" - R.fields["blood_reagent"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - data_core.medical += R - active2 = R - screen = MED_DATA_RECORD - set_temp("Medical record created.", "success") - if("del_c") - var/index = text2num(params["del_c"] || "") - if(!index || !istype(active2, /datum/data/record)) - return - - var/list/comments = active2.fields["comments"] - index = clamp(index, 1, length(comments)) - if(comments[index]) - comments.Cut(index, index + 1) - if("search") - active1 = null - active2 = null - var/t1 = lowertext(params["t1"] || "") - if(!length(t1)) - return - - for(var/datum/data/record/R in data_core.medical) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) - active2 = R - break - if(!active2) - set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger") - return - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) - active1 = E - break - screen = MED_DATA_RECORD - if("print_p") - if(!printing) - printing = TRUE - // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) - SStgui.update_uis(src) - addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) - else - return FALSE - -/** - * Called in tgui_act() to process modal actions - * - * Arguments: - * * action - The action passed by tgui - * * params - The params passed by tgui - */ -/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params) - . = TRUE - var/id = params["id"] // The modal's ID - var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] - switch(tgui_modal_act(src, action, params)) - if(TGUI_MODAL_OPEN) - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/question = field_edit_questions[field] - var/choices = field_edit_choices[field] - if(length(choices)) - tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) - else - tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) - if("add_c") - tgui_modal_input(src, id, "Please enter your message:") - else - return FALSE - if(TGUI_MODAL_ANSWER) - var/answer = params["answer"] - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/list/choices = field_edit_choices[field] - if(length(choices) && !(answer in choices)) - return - - if(field == "age") - answer = text2num(answer) - - if(istype(active2) && (field in active2.fields)) - active2.fields[field] = answer - else if(istype(active1) && (field in active1.fields)) - active1.fields[field] = answer - if("add_c") - if(!length(answer) || !istype(active2) || !length(authenticated)) - return - active2.fields["comments"] += list(list( - header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", - text = answer - )) - else - return FALSE - else - return FALSE - - -/** - * Called when the print timer finishes - */ -/obj/machinery/computer/med_data/proc/print_finish() - var/obj/item/weapon/paper/P = new(loc) - P.info = "

    Medical Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nSpecies: [active1.fields["species"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - P.info += {"
    \n
    Medical Data
    -
    \nGender Identity: [active2.fields["id_gender"]] -
    \nBlood Type: [active2.fields["b_type"]] -
    \nBlood Basis: [active2.fields["blood_reagent"]] -
    \nDNA: [active2.fields["b_dna"]]
    \n -
    \nMinor Disabilities: [active2.fields["mi_dis"]] -
    \nDetails: [active2.fields["mi_dis_d"]]
    \n -
    \nMajor Disabilities: [active2.fields["ma_dis"]] -
    \nDetails: [active2.fields["ma_dis_d"]]
    \n -
    \nAllergies: [active2.fields["alg"]] -
    \nDetails: [active2.fields["alg_d"]]
    \n -
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
    \nDetails: [active2.fields["cdi_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c["header"]]
    [c["text"]]
    " - else - P.info += "Medical Record Lost!
    " - P.info += "" - P.name = "paper - 'Medical Record: [active1.fields["name"]]'" - printing = FALSE - SStgui.update_uis(src) - -/** - * Sets a temporary message to display to the user - * - * Arguments: - * * text - Text to display, null/empty to clear the message from the UI - * * style - The style of the message: (color name), info, success, warning, danger, virus - */ -/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE) - temp = list(text = text, style = style) - if(update_now) - SStgui.update_uis(src) - -/obj/machinery/computer/med_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.medical) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["b_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") - if(5) - R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - - -/obj/machinery/computer/med_data/laptop //[TO DO] Change name to PCU and update mapdata to include replacement computers - name = "\improper Medical Laptop" - desc = "A personal computer unit. It seems to have only the medical records program installed." - icon_screen = "pcu_generic" - icon_state = "pcu_med" - icon_keyboard = "pcu_key" - light_color = "#5284e7" - circuit = /obj/item/weapon/circuitboard/med_data/pcu - density = FALSE - -#undef FIELD -#undef MED_FIELD +#define MED_DATA_R_LIST 2 // Record list +#define MED_DATA_MAINT 3 // Records maintenance +#define MED_DATA_RECORD 4 // Record +#define MED_DATA_V_DATA 5 // Virus database +#define MED_DATA_MEDBOT 6 // Medbot monitor + +#define FIELD(N, V, E) list(field = N, value = V, edit = E) +#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) + +/obj/machinery/computer/med_data//TODO:SANITY + name = "medical records console" + desc = "Used to view, edit and maintain medical records." + icon_keyboard = "med_key" + icon_screen = "medcomp" + light_color = "#315ab4" + req_one_access = list(access_medical, access_forensics_lockers, access_robotics) + circuit = /obj/item/weapon/circuitboard/med_data + var/obj/item/weapon/card/id/scan = null + var/authenticated = null + var/rank = null + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/list/temp = null + var/printing = null + // The below are used to make modal generation more convenient + var/static/list/field_edit_questions + var/static/list/field_edit_choices + + +/obj/machinery/computer/med_data/Initialize() + . = ..() + field_edit_questions = list( + // General + "sex" = "Please select new sex:", + "species" = "Please input new species:", + "age" = "Please input new age:", + "fingerprint" = "Please input new fingerprint hash:", + "p_stat" = "Please select new physical status:", + "m_stat" = "Please select new mental status:", + // Medical + "id_gender" = "Please select new gender identity:", + "blood_type" = "Please select new blood type:", + "blood_reagent" = "Please select new blood basis:", + "b_dna" = "Please input new DNA:", + "mi_dis" = "Please input new minor disabilities:", + "mi_dis_d" = "Please summarize minor disabilities:", + "ma_dis" = "Please input new major disabilities:", + "ma_dis_d" = "Please summarize major disabilities:", + "alg" = "Please input new allergies:", + "alg_d" = "Please summarize allergies:", + "cdi" = "Please input new current diseases:", + "cdi_d" = "Please summarize current diseases:", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = all_genders_text_list, + "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), + "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), + // Medical + "id_gender" = all_genders_text_list, + "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"), + ) + +/obj/machinery/computer/med_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/med_data/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + to_chat(usr, "You remove \the [scan] from \the [src].") + scan.loc = get_turf(src) + if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) + usr.put_in_hands(scan) + scan = null + else + to_chat(usr, "There is nothing to remove from the console.") + return + +/obj/machinery/computer/med_data/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) + O.loc = src + scan = O + to_chat(user, "You insert \the [O].") + tgui_interact(user) + else + ..() + +/obj/machinery/computer/med_data/attack_ai(user as mob) + return attack_hand(user) + +/obj/machinery/computer/med_data/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + tgui_interact(user) + + +/obj/machinery/computer/med_data/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MedicalRecords", "Medical Records") // 800, 380 + ui.open() + ui.set_autoupdate(FALSE) + + +/obj/machinery/computer/med_data/tgui_data(mob/user) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["rank"] = rank + data["screen"] = screen + data["printing"] = printing + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) + if(authenticated) + switch(screen) + if(MED_DATA_R_LIST) + if(!isnull(data_core.general)) + var/list/records = list() + data["records"] = records + for(var/datum/data/record/R in sortRecord(data_core.general)) + records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) + if(MED_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = FIELD("Name", active1.fields["name"], null) + fields[++fields.len] = FIELD("ID", active1.fields["id"], null) + fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") + fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") + fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") + fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") + fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat") + fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat") + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = active1.fields["photo-south"] + photos[++photos.len] = active1.fields["photo-west"] + general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/medical = list() + data["medical"] = medical + if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) + var/list/fields = list() + medical["fields"] = fields + fields[++fields.len] = MED_FIELD("Gender identity", active2.fields["id_gender"], "id_gender", TRUE) + fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["b_type"], "blood_type", FALSE) + fields[++fields.len] = MED_FIELD("Blood Basis", active2.fields["blood_reagent"], "blood_reagent", FALSE) + fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE) + fields[++fields.len] = MED_FIELD("Brain Type", active2.fields["brain_type"], "brain_type", TRUE) + fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE) + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + medical["comments"] = active2.fields["comments"] + medical["empty"] = 0 + else + medical["empty"] = 1 + if(MED_DATA_V_DATA) + data["virus"] = list() + for(var/ID in virusDB) + var/datum/data/record/v = virusDB[ID] + data["virus"] += list(list("name" = v.fields["name"], "D" = "\ref[v]")) + if(MED_DATA_MEDBOT) + data["medbots"] = list() + for(var/mob/living/bot/medbot/M in mob_list) + if(M.z != z) + continue + var/turf/T = get_turf(M) + if(T) + var/medbot = list() + var/area/A = get_area(T) + medbot["name"] = M.name + medbot["area"] = A.name + medbot["x"] = T.x + medbot["y"] = T.y + medbot["on"] = M.on + if(!isnull(M.reagent_glass) && M.use_beaker) + medbot["use_beaker"] = 1 + medbot["total_volume"] = M.reagent_glass.reagents.total_volume + medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume + else + medbot["use_beaker"] = 0 + data["medbots"] += list(medbot) + + data["modal"] = tgui_modal_data(src) + return data + +/obj/machinery/computer/med_data/tgui_act(action, params) + if(..()) + return TRUE + + if(!data_core.general.Find(active1)) + active1 = null + if(!data_core.medical.Find(active2)) + active2 = null + + . = TRUE + if(tgui_act_modal(action, params)) + return + + switch(action) + if("cleartemp") + temp = null + if("scan") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + if("login") + var/login_type = text2num(params["login_type"]) + if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + if(authenticated) + active1 = null + active2 = null + screen = MED_DATA_R_LIST + else + . = FALSE + + if(.) + return + + if(authenticated) + . = TRUE + switch(action) + if("logout") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + authenticated = null + screen = null + active1 = null + active2 = null + if("screen") + screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT) + active1 = null + active2 = null + if("vir") + var/datum/data/record/v = locate(params["vir"]) + if(!istype(v)) + return FALSE + tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"]) + if("del_all") + for(var/datum/data/record/R in data_core.medical) + qdel(R) + set_temp("All medical records deleted.") + if("del_r") + if(active2) + set_temp("Medical record deleted.") + qdel(active2) + if("d_rec") + var/datum/data/record/general_record = locate(params["d_rec"] || "") + if(!data_core.general.Find(general_record)) + set_temp("Record not found.", "danger") + return + + var/datum/data/record/medical_record + for(var/datum/data/record/M in data_core.medical) + if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) + medical_record = M + break + + active1 = general_record + active2 = medical_record + screen = MED_DATA_RECORD + if("new") + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Medical Record #[R.fields["id"]]" + R.fields["b_type"] = "Unknown" + R.fields["blood_reagent"] = "Unknown" + R.fields["b_dna"] = "Unknown" + R.fields["mi_dis"] = "None" + R.fields["mi_dis_d"] = "No minor disabilities have been declared." + R.fields["ma_dis"] = "None" + R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + R.fields["alg"] = "None" + R.fields["alg_d"] = "No allergies have been detected in this patient." + R.fields["cdi"] = "None" + R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + R.fields["notes"] = "No notes." + data_core.medical += R + active2 = R + screen = MED_DATA_RECORD + set_temp("Medical record created.", "success") + if("del_c") + var/index = text2num(params["del_c"] || "") + if(!index || !istype(active2, /datum/data/record)) + return + + var/list/comments = active2.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("search") + active1 = null + active2 = null + var/t1 = lowertext(params["t1"] || "") + if(!length(t1)) + return + + for(var/datum/data/record/R in data_core.medical) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + active2 = R + break + if(!active2) + set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger") + return + for(var/datum/data/record/E in data_core.general) + if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) + active1 = E + break + screen = MED_DATA_RECORD + if("print_p") + if(!printing) + printing = TRUE + // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + SStgui.update_uis(src) + addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) + else + return FALSE + +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) + else + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("add_c") + tgui_modal_input(src, id, "Please enter your message:") + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return + + if(field == "age") + answer = text2num(answer) + + if(istype(active2) && (field in active2.fields)) + active2.fields[field] = answer + else if(istype(active1) && (field in active1.fields)) + active1.fields[field] = answer + if("add_c") + if(!length(answer) || !istype(active2) || !length(authenticated)) + return + active2.fields["comments"] += list(list( + header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", + text = answer + )) + else + return FALSE + else + return FALSE + + +/** + * Called when the print timer finishes + */ +/obj/machinery/computer/med_data/proc/print_finish() + var/obj/item/weapon/paper/P = new(loc) + P.info = "
    Medical Record

    " + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nSpecies: [active1.fields["species"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) + P.info += {"
    \n
    Medical Data
    +
    \nGender Identity: [active2.fields["id_gender"]] +
    \nBlood Type: [active2.fields["b_type"]] +
    \nBlood Basis: [active2.fields["blood_reagent"]] +
    \nDNA: [active2.fields["b_dna"]]
    \n +
    \nMinor Disabilities: [active2.fields["mi_dis"]] +
    \nDetails: [active2.fields["mi_dis_d"]]
    \n +
    \nMajor Disabilities: [active2.fields["ma_dis"]] +
    \nDetails: [active2.fields["ma_dis_d"]]
    \n +
    \nAllergies: [active2.fields["alg"]] +
    \nDetails: [active2.fields["alg_d"]]
    \n +
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
    \nDetails: [active2.fields["cdi_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c["header"]]
    [c["text"]]
    " + else + P.info += "Medical Record Lost!
    " + P.info += "" + P.name = "paper - 'Medical Record: [active1.fields["name"]]'" + printing = FALSE + SStgui.update_uis(src) + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger, virus + */ +/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + +/obj/machinery/computer/med_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in data_core.medical) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["b_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") + if(5) + R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") + if(PDA_Manifest.len) + PDA_Manifest.Cut() + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + + +/obj/machinery/computer/med_data/laptop //[TO DO] Change name to PCU and update mapdata to include replacement computers + name = "\improper Medical Laptop" + desc = "A personal computer unit. It seems to have only the medical records program installed." + icon_screen = "pcu_generic" + icon_state = "pcu_med" + icon_keyboard = "pcu_key" + light_color = "#5284e7" + circuit = /obj/item/weapon/circuitboard/med_data/pcu + density = FALSE + +#undef FIELD +#undef MED_FIELD diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 27009d097b2..a5dad720ec9 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -34,7 +34,7 @@ return if(!istype(user)) return - if(O.is_screwdriver() && emag) + if(O.has_tool_quality(TOOL_SCREWDRIVER) && emag) //Stops people from just unscrewing the monitor and putting it back to get the console working again. to_chat(user, "It is too hot to mess with!") return @@ -101,7 +101,7 @@ if(linkedServer && auth) data["linkedServer"]["active"] = linkedServer.active data["linkedServer"]["broke"] = linkedServer.stat & (NOPOWER|BROKEN) - + var/list/pda_msgs = list() for(var/datum/data_pda_msg/pda in linkedServer.pda_msgs) pda_msgs.Add(list(list( @@ -111,7 +111,7 @@ "message" = pda.message, ))) data["linkedServer"]["pda_msgs"] = pda_msgs - + var/list/rc_msgs = list() for(var/datum/data_rc_msg/rc in linkedServer.rc_msgs) rc_msgs.Add(list(list( @@ -182,7 +182,7 @@ /obj/machinery/computer/message_monitor/tgui_act(action, params) if(..()) return TRUE - + switch(action) if("cleartemp") temp = null @@ -218,14 +218,14 @@ spawn(100*length(linkedServer.decryptkey)) if(src && linkedServer && usr) BruteForce(usr) - + if(!auth) return - + if(!linkedServer || linkedServer.stat & (NOPOWER|BROKEN)) temp = noserver return TRUE - + switch(action) //Turn the server on/off. if("active") @@ -277,7 +277,7 @@ var/obj/item/device/pda/P = locate(ref) if(!istype(P) || !P.owner || P.hidden) return FALSE - + var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger) if(!M || M.toff) return FALSE @@ -316,7 +316,7 @@ //Sender is faking as someone who exists else linkedServer.send_pda_message("[customrecepient.owner]", "[PDARec.owner]","[custommessage]") - + var/datum/data/pda/app/messenger/M = customrecepient.find_program(/datum/data/pda/app/messenger) if(M) M.receive_message(list("sent" = 0, "owner" = "[PDARec.owner]", "job" = "[customjob]", "message" = "[custommessage]", "target" = "\ref[PDARec]"), "\ref[PDARec]") diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index f37f0f6c970..c074256475e 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -50,7 +50,7 @@ /* /obj/machinery/computer/pod/attackby(I as obj, user as mob) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, W.usesound, 50, 1) if(do_after(user, 20)) if(stat & BROKEN) diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 1a81f9db919..2c6b4c7ced3 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -1,215 +1,215 @@ -//Config stuff -#define PRISON_MOVETIME 150 //Time to station is milliseconds. -#define PRISON_STATION_AREATYPE "/area/shuttle/prison/station" //Type of the prison shuttle area for station -#define PRISON_DOCK_AREATYPE "/area/shuttle/prison/prison" //Type of the prison shuttle area for dock - -var/prison_shuttle_moving_to_station = 0 -var/prison_shuttle_moving_to_prison = 0 -var/prison_shuttle_at_station = 0 -var/prison_shuttle_can_send = 1 -var/prison_shuttle_time = 0 -var/prison_shuttle_timeleft = 0 - -/obj/machinery/computer/prison_shuttle - name = "prison shuttle control console" - desc = "Used to move the prison shuttle to and from its destination." - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_security) - circuit = /obj/item/weapon/circuitboard/prison_shuttle - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/prison_break = 0 - -/obj/machinery/computer/prison_shuttle/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/prison_shuttle/attack_hand(var/mob/user as mob) - if(!src.allowed(user) && (!hacked)) - to_chat(user, "Access Denied.") - return - if(prison_break) - to_chat(user, "Unable to locate shuttle.") - return - if(..()) - return - user.set_machine(src) - post_signal("prison") - var/dat - if (src.temp) - dat = src.temp - else - dat += {"
    Prison Shuttle
    - \nLocation: [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "Moving to station ([prison_shuttle_timeleft] Secs.)":prison_shuttle_at_station ? "Station":"Dock"]
    - [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "\n*Shuttle already called*
    \n
    ":prison_shuttle_at_station ? "\nSend to Dock
    \n
    ":"\nSend to station
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - - -/obj/machinery/computer/prison_shuttle/Topic(href, href_list) - if(..()) - return - - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if (href_list["sendtodock"]) - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - if(!prison_shuttle_at_station|| prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - post_signal("prison") - to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") - src.temp += "Shuttle sent.

    OK" - src.updateUsrDialog() - prison_shuttle_moving_to_prison = 1 - prison_shuttle_time = world.timeofday + PRISON_MOVETIME - spawn(0) - prison_process() - - else if (href_list["sendtostation"]) - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - if(prison_shuttle_at_station || prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - post_signal("prison") - to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") - src.temp += "Shuttle sent.

    OK" - src.updateUsrDialog() - prison_shuttle_moving_to_station = 1 - prison_shuttle_time = world.timeofday + PRISON_MOVETIME - spawn(0) - prison_process() - - else if (href_list["mainmenu"]) - src.temp = null - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/obj/machinery/computer/prison_shuttle/proc/prison_can_move() - if(prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return 0 - else return 1 - - -/obj/machinery/computer/prison_shuttle/proc/prison_break() - switch(prison_break) - if (0) - if(!prison_shuttle_at_station || prison_shuttle_moving_to_prison) return - - prison_shuttle_moving_to_prison = 1 - prison_shuttle_at_station = prison_shuttle_at_station - - if (!prison_shuttle_moving_to_prison || !prison_shuttle_moving_to_station) - prison_shuttle_time = world.timeofday + PRISON_MOVETIME - spawn(0) - prison_process() - prison_break = 1 - if(1) - prison_break = 0 - - -/obj/machinery/computer/prison_shuttle/proc/post_signal(var/command) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1311) - if(!frequency) return - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = TRANSMISSION_RADIO - status_signal.data["command"] = command - frequency.post_signal(src, status_signal) - return - - -/obj/machinery/computer/prison_shuttle/proc/prison_process() - while(prison_shuttle_time - world.timeofday > 0) - var/ticksleft = prison_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - prison_shuttle_time = world.timeofday + 10 // midnight rollover - - prison_shuttle_timeleft = (ticksleft / 10) - sleep(5) - prison_shuttle_moving_to_station = 0 - prison_shuttle_moving_to_prison = 0 - - switch(prison_shuttle_at_station) - - if(0) - prison_shuttle_at_station = 1 - if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - - var/area/start_location = locate(/area/shuttle/prison/prison) - var/area/end_location = locate(/area/shuttle/prison/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - start_location.move_contents_to(end_location) - - if(1) - prison_shuttle_at_station = 0 - if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return - - if (!prison_can_move()) - to_chat(usr, "The prison shuttle is unable to leave.") - return - - var/area/start_location = locate(/area/shuttle/prison/station) - var/area/end_location = locate(/area/shuttle/prison/prison) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - return - -/obj/machinery/computer/prison_shuttle/emag_act(var/charges, var/mob/user) - if(!hacked) - hacked = 1 - to_chat(user, "You disable the lock.") - return 1 +//Config stuff +#define PRISON_MOVETIME 150 //Time to station is milliseconds. +#define PRISON_STATION_AREATYPE "/area/shuttle/prison/station" //Type of the prison shuttle area for station +#define PRISON_DOCK_AREATYPE "/area/shuttle/prison/prison" //Type of the prison shuttle area for dock + +var/prison_shuttle_moving_to_station = 0 +var/prison_shuttle_moving_to_prison = 0 +var/prison_shuttle_at_station = 0 +var/prison_shuttle_can_send = 1 +var/prison_shuttle_time = 0 +var/prison_shuttle_timeleft = 0 + +/obj/machinery/computer/prison_shuttle + name = "prison shuttle control console" + desc = "Used to move the prison shuttle to and from its destination." + icon_keyboard = "security_key" + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_security) + circuit = /obj/item/weapon/circuitboard/prison_shuttle + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/prison_break = 0 + +/obj/machinery/computer/prison_shuttle/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/prison_shuttle/attack_hand(var/mob/user as mob) + if(!src.allowed(user) && (!hacked)) + to_chat(user, "Access Denied.") + return + if(prison_break) + to_chat(user, "Unable to locate shuttle.") + return + if(..()) + return + user.set_machine(src) + post_signal("prison") + var/dat + if (src.temp) + dat = src.temp + else + dat += {"
    Prison Shuttle
    + \nLocation: [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "Moving to station ([prison_shuttle_timeleft] Secs.)":prison_shuttle_at_station ? "Station":"Dock"]
    + [prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison ? "\n*Shuttle already called*
    \n
    ":prison_shuttle_at_station ? "\nSend to Dock
    \n
    ":"\nSend to station
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + + +/obj/machinery/computer/prison_shuttle/Topic(href, href_list) + if(..()) + return + + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if (href_list["sendtodock"]) + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + if(!prison_shuttle_at_station|| prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + post_signal("prison") + to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") + src.temp += "Shuttle sent.

    OK" + src.updateUsrDialog() + prison_shuttle_moving_to_prison = 1 + prison_shuttle_time = world.timeofday + PRISON_MOVETIME + spawn(0) + prison_process() + + else if (href_list["sendtostation"]) + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + if(prison_shuttle_at_station || prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + post_signal("prison") + to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") + src.temp += "Shuttle sent.

    OK" + src.updateUsrDialog() + prison_shuttle_moving_to_station = 1 + prison_shuttle_time = world.timeofday + PRISON_MOVETIME + spawn(0) + prison_process() + + else if (href_list["mainmenu"]) + src.temp = null + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/obj/machinery/computer/prison_shuttle/proc/prison_can_move() + if(prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return 0 + else return 1 + + +/obj/machinery/computer/prison_shuttle/proc/prison_break() + switch(prison_break) + if (0) + if(!prison_shuttle_at_station || prison_shuttle_moving_to_prison) return + + prison_shuttle_moving_to_prison = 1 + prison_shuttle_at_station = prison_shuttle_at_station + + if (!prison_shuttle_moving_to_prison || !prison_shuttle_moving_to_station) + prison_shuttle_time = world.timeofday + PRISON_MOVETIME + spawn(0) + prison_process() + prison_break = 1 + if(1) + prison_break = 0 + + +/obj/machinery/computer/prison_shuttle/proc/post_signal(var/command) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1311) + if(!frequency) return + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = TRANSMISSION_RADIO + status_signal.data["command"] = command + frequency.post_signal(src, status_signal) + return + + +/obj/machinery/computer/prison_shuttle/proc/prison_process() + while(prison_shuttle_time - world.timeofday > 0) + var/ticksleft = prison_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + prison_shuttle_time = world.timeofday + 10 // midnight rollover + + prison_shuttle_timeleft = (ticksleft / 10) + sleep(5) + prison_shuttle_moving_to_station = 0 + prison_shuttle_moving_to_prison = 0 + + switch(prison_shuttle_at_station) + + if(0) + prison_shuttle_at_station = 1 + if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + + var/area/start_location = locate(/area/shuttle/prison/prison) + var/area/end_location = locate(/area/shuttle/prison/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + start_location.move_contents_to(end_location) + + if(1) + prison_shuttle_at_station = 0 + if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return + + if (!prison_can_move()) + to_chat(usr, "The prison shuttle is unable to leave.") + return + + var/area/start_location = locate(/area/shuttle/prison/station) + var/area/end_location = locate(/area/shuttle/prison/prison) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + return + +/obj/machinery/computer/prison_shuttle/emag_act(var/charges, var/mob/user) + if(!hacked) + hacked = 1 + to_chat(user, "You disable the lock.") + return 1 diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 7b318b966cf..954c0c0ac44 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -1,230 +1,230 @@ -/obj/machinery/computer/robotics - name = "robotics control console" - desc = "Used to remotely lockdown or detonate linked cyborgs." - icon_keyboard = "tech_key" - icon_screen = "robot" - light_color = "#a97faa" - req_access = list(access_robotics) - circuit = /obj/item/weapon/circuitboard/robotics - var/safety = 1 - -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) - tgui_interact(user) - -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) - if(..()) - return - if(stat & (NOPOWER|BROKEN)) - return - tgui_interact(user) - -/obj/machinery/computer/robotics/proc/is_authenticated(mob/user) - if(!istype(user)) - return FALSE - if(isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - return TRUE - if(allowed(user)) - return TRUE - return FALSE - -/** - * Does this borg show up in the console - * - * Returns TRUE if a robot will show up in the console - * Returns FALSE if a robot will not show up in the console - * Arguments: - * * R - The [mob/living/silicon/robot] to be checked - */ -/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) - if(!istype(R)) - return FALSE - if(istype(R, /mob/living/silicon/robot/drone)) - return FALSE - if(R.scrambledcodes) - return FALSE - if(!AreConnectedZLevels(get_z(src), get_z(R))) - return FALSE - return TRUE - -/** - * Check if a user can send a lockdown/detonate command to a specific borg - * - * Returns TRUE if a user can send the command (does not guarantee it will work) - * Returns FALSE if a user cannot - * Arguments: - * * user - The [mob/user] to be checked - * * R - The [mob/living/silicon/robot] to be checked - * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access - */ -/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) - if(!istype(user)) - return FALSE - if(!console_shows(R)) - return FALSE - if(isAI(user)) - if(R.connected_ai != user) - if(telluserwhy) - to_chat(user, "AIs can only control cyborgs which are linked to them.") - return FALSE - if(isrobot(user)) - if(R != user) - if(telluserwhy) - to_chat(user, "Cyborgs cannot control other cyborgs.") - return FALSE - return TRUE - -/** - * Check if the user is the right kind of entity to be able to hack borgs - * - * Returns TRUE if a user is a traitor AI, or aghost - * Returns FALSE otherwise - * Arguments: - * * user - The [mob/user] to be checked - */ -/obj/machinery/computer/robotics/proc/can_hack_any(mob/user) - if(!istype(user)) - return FALSE - if(isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - return TRUE - if(!isAI(user)) - return FALSE - return (user.mind.special_role && user.mind.original == user) - -/** - * Check if the user is allowed to hack a specific borg - * - * Returns TRUE if a user can hack the specific cyborg - * Returns FALSE if a user cannot - * Arguments: - * * user - The [mob/user] to be checked - * * R - The [mob/living/silicon/robot] to be checked - */ -/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) - if(!can_hack_any(user)) - return FALSE - if(!istype(R)) - return FALSE - if(R.emagged) - return FALSE - if(R.connected_ai != user) - return FALSE - return TRUE - - -/obj/machinery/computer/robotics/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "RoboticsControlConsole", name) - ui.open() - -/obj/machinery/computer/robotics/tgui_data(mob/user) - var/list/data = list() - data["auth"] = is_authenticated(user) - data["can_hack"] = can_hack_any(user) - data["cyborgs"] = list() - data["safety"] = safety - for(var/mob/living/silicon/robot/R in mob_list) - if(!console_shows(R)) - continue - var/area/A = get_area(R) - var/turf/T = get_turf(R) - var/list/cyborg_data = list( - name = R.name, - ref = REF(R), - locked_down = R.lockcharge, - locstring = "[A.name] ([T.x], [T.y])", - status = R.stat, - health = round(R.health * 100 / R.maxHealth, 0.1), - charge = R.cell ? round(R.cell.percent()) : null, - cell_capacity = R.cell ? R.cell.maxcharge : null, - module = R.module ? R.module.name : "No Module Detected", - synchronization = R.connected_ai, - is_hacked = R.connected_ai && R.emagged, - hackable = can_hack(user, R), - ) - data["cyborgs"] += list(cyborg_data) - data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) - return data - -/obj/machinery/computer/robotics/tgui_act(action, params) - if(..()) - return - . = FALSE - if(!is_authenticated(usr)) - to_chat(usr, "Access denied.") - return - switch(action) - if("arm") // Arms the emergency self-destruct system - if(issilicon(usr)) - to_chat(usr, "Access Denied (silicon detected)") - return - safety = !safety - to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.") - . = TRUE - if("nuke") // Destroys all accessible cyborgs if safety is disabled - if(issilicon(usr)) - to_chat(usr, "Access Denied (silicon detected)") - return - if(safety) - to_chat(usr, "Self-destruct aborted - safety active") - return - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("\[key_name(usr)] detonated all cyborgs!") - for(var/mob/living/silicon/robot/R in mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - to_chat(R, "Self-destruct command received.") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") - R.self_destruct() - . = TRUE - if("killbot") // destroys one specific cyborg - var/mob/living/silicon/robot/R = locate(params["ref"]) - if(!can_control(usr, R, TRUE)) - return - if(R.mind && R.mind.special_role && R.emagged) - to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - R.ResetSecurityCodes() - . = TRUE - return - var/turf/T = get_turf(R) - message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!") - log_game("\[key_name(usr)] detonated [key_name(R)]!") - to_chat(R, "Self-destruct command received.") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") - R.self_destruct() - . = TRUE - if("stopbot") // lock or unlock the borg - if(isrobot(usr)) - to_chat(usr, "Access Denied.") - return - var/mob/living/silicon/robot/R = locate(params["ref"]) - if(!can_control(usr, R, TRUE)) - return - message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") - log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") - R.SetLockdown(!R.lockcharge) - to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") - if(R.connected_ai) - to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
    ") - . = TRUE - if("hackbot") // AIs hacking/emagging a borg - var/mob/living/silicon/robot/R = locate(params["ref"]) - if(!can_hack(usr, R)) - return - var/choice = tgui_alert(usr, "Really hack [R.name]? This cannot be undone.", "Hack?", list("Yes", "No")) - if(choice == "No") - return - log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") - message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") - R.emagged = TRUE - to_chat(R, "Failsafe protocols overridden. New tools available.") - . = TRUE +/obj/machinery/computer/robotics + name = "robotics control console" + desc = "Used to remotely lockdown or detonate linked cyborgs." + icon_keyboard = "tech_key" + icon_screen = "robot" + light_color = "#a97faa" + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/robotics + var/safety = 1 + +/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) + tgui_interact(user) + +/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) + if(..()) + return + if(stat & (NOPOWER|BROKEN)) + return + tgui_interact(user) + +/obj/machinery/computer/robotics/proc/is_authenticated(mob/user) + if(!istype(user)) + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return TRUE + if(allowed(user)) + return TRUE + return FALSE + +/** + * Does this borg show up in the console + * + * Returns TRUE if a robot will show up in the console + * Returns FALSE if a robot will not show up in the console + * Arguments: + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) + if(!istype(R)) + return FALSE + if(istype(R, /mob/living/silicon/robot/drone)) + return FALSE + if(R.scrambledcodes) + return FALSE + if(!AreConnectedZLevels(get_z(src), get_z(R))) + return FALSE + return TRUE + +/** + * Check if a user can send a lockdown/detonate command to a specific borg + * + * Returns TRUE if a user can send the command (does not guarantee it will work) + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access + */ +/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) + if(!istype(user)) + return FALSE + if(!console_shows(R)) + return FALSE + if(isAI(user)) + if(R.connected_ai != user) + if(telluserwhy) + to_chat(user, "AIs can only control cyborgs which are linked to them.") + return FALSE + if(isrobot(user)) + if(R != user) + if(telluserwhy) + to_chat(user, "Cyborgs cannot control other cyborgs.") + return FALSE + return TRUE + +/** + * Check if the user is the right kind of entity to be able to hack borgs + * + * Returns TRUE if a user is a traitor AI, or aghost + * Returns FALSE otherwise + * Arguments: + * * user - The [mob/user] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack_any(mob/user) + if(!istype(user)) + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return TRUE + if(!isAI(user)) + return FALSE + return (user.mind.special_role && user.mind.original == user) + +/** + * Check if the user is allowed to hack a specific borg + * + * Returns TRUE if a user can hack the specific cyborg + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) + if(!can_hack_any(user)) + return FALSE + if(!istype(R)) + return FALSE + if(R.emagged) + return FALSE + if(R.connected_ai != user) + return FALSE + return TRUE + + +/obj/machinery/computer/robotics/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RoboticsControlConsole", name) + ui.open() + +/obj/machinery/computer/robotics/tgui_data(mob/user) + var/list/data = list() + data["auth"] = is_authenticated(user) + data["can_hack"] = can_hack_any(user) + data["cyborgs"] = list() + data["safety"] = safety + for(var/mob/living/silicon/robot/R in mob_list) + if(!console_shows(R)) + continue + var/area/A = get_area(R) + var/turf/T = get_turf(R) + var/list/cyborg_data = list( + name = R.name, + ref = REF(R), + locked_down = R.lockcharge, + locstring = "[A.name] ([T.x], [T.y])", + status = R.stat, + health = round(R.health * 100 / R.maxHealth, 0.1), + charge = R.cell ? round(R.cell.percent()) : null, + cell_capacity = R.cell ? R.cell.maxcharge : null, + module = R.module ? R.module.name : "No Module Detected", + synchronization = R.connected_ai, + is_hacked = R.connected_ai && R.emagged, + hackable = can_hack(user, R), + ) + data["cyborgs"] += list(cyborg_data) + data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) + return data + +/obj/machinery/computer/robotics/tgui_act(action, params) + if(..()) + return + . = FALSE + if(!is_authenticated(usr)) + to_chat(usr, "Access denied.") + return + switch(action) + if("arm") // Arms the emergency self-destruct system + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + safety = !safety + to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.") + . = TRUE + if("nuke") // Destroys all accessible cyborgs if safety is disabled + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + if(safety) + to_chat(usr, "Self-destruct aborted - safety active") + return + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("\[key_name(usr)] detonated all cyborgs!") + for(var/mob/living/silicon/robot/R in mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") + R.self_destruct() + . = TRUE + if("killbot") // destroys one specific cyborg + var/mob/living/silicon/robot/R = locate(params["ref"]) + if(!can_control(usr, R, TRUE)) + return + if(R.mind && R.mind.special_role && R.emagged) + to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + R.ResetSecurityCodes() + . = TRUE + return + var/turf/T = get_turf(R) + message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!") + log_game("\[key_name(usr)] detonated [key_name(R)]!") + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") + R.self_destruct() + . = TRUE + if("stopbot") // lock or unlock the borg + if(isrobot(usr)) + to_chat(usr, "Access Denied.") + return + var/mob/living/silicon/robot/R = locate(params["ref"]) + if(!can_control(usr, R, TRUE)) + return + message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") + log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") + R.SetLockdown(!R.lockcharge) + to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") + if(R.connected_ai) + to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
    ") + . = TRUE + if("hackbot") // AIs hacking/emagging a borg + var/mob/living/silicon/robot/R = locate(params["ref"]) + if(!can_hack(usr, R)) + return + var/choice = tgui_alert(usr, "Really hack [R.name]? This cannot be undone.", "Hack?", list("Yes", "No")) + if(choice == "No") + return + log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") + message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") + R.emagged = TRUE + to_chat(R, "Failsafe protocols overridden. New tools available.") + . = TRUE diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index fea0fa3950e..e239a6cf06c 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -1,515 +1,515 @@ -#define SEC_DATA_R_LIST 2 // Record list -#define SEC_DATA_MAINT 3 // Records maintenance -#define SEC_DATA_RECORD 4 // Record - -#define FIELD(N, V, E) list(field = N, value = V, edit = E) - -/obj/machinery/computer/secure_data//TODO:SANITY - name = "security records console" - desc = "Used to view, edit and maintain security records" - icon_keyboard = "security_key" - icon_screen = "security" - light_color = "#a91515" - req_one_access = list(access_security, access_forensics_lockers, access_lawyer) - circuit = /obj/item/weapon/circuitboard/secure_data - var/obj/item/weapon/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/list/temp = null - var/printing = null - // The below are used to make modal generation more convenient - var/static/list/field_edit_questions - var/static/list/field_edit_choices - -/obj/machinery/computer/secure_data/Initialize() - . = ..() - field_edit_questions = list( - // General - "name" = "Please enter new name:", - "id" = "Please enter new id:", - "sex" = "Please select new sex:", - "species" = "Please input new species:", - "age" = "Please input new age:", - "rank" = "Please enter new rank:", - "fingerprint" = "Please input new fingerprint hash:", - // Security - "brain_type" = "Please select new brain type:", - "criminal" = "Please select new criminal status:", - "mi_crim" = "Please input new minor crime:", - "mi_crim_d" = "Please input minor crime summary.", - "ma_crim" = "Please input new major crime:", - "ma_crim_d" = "Please input new major crime summary.", - "notes" = "Please input new important notes:", - ) - field_edit_choices = list( - // General - "sex" = all_genders_text_list, - // Security - "criminal" = list("*Arrest*", "Incarcerated", "Parolled", "Released", "None"), - ) - -/obj/machinery/computer/secure_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/secure_data/verb/eject_id() - set category = "Object" - set name = "Eject ID Card" - set src in oview(1) - - if(!usr || usr.stat || usr.lying) return - - if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") - scan.loc = get_turf(src) - if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) - usr.put_in_hands(scan) - scan = null - else - to_chat(usr, "There is nothing to remove from the console.") - return - -/obj/machinery/computer/secure_data/attackby(var/obj/item/O, var/mob/user) - if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) - O.loc = src - scan = O - to_chat(user, "You insert \the [O].") - tgui_interact(user) - else - ..() - -/obj/machinery/computer/secure_data/attack_ai(mob/user as mob) - return attack_hand(user) - -//Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/secure_data/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - tgui_interact(user) - -/obj/machinery/computer/secure_data/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SecurityRecords", "Security Records") // 800, 380 - ui.open() - ui.set_autoupdate(FALSE) - - -/obj/machinery/computer/secure_data/tgui_data(mob/user) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["rank"] = rank - data["screen"] = screen - data["printing"] = printing - data["isAI"] = isAI(user) - data["isRobot"] = isrobot(user) - if(authenticated) - switch(screen) - if(SEC_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - var/color = null - var/criminal = "None" - for(var/datum/data/record/M in data_core.security) - if(M.fields["name"] == R.fields["name"] && M.fields["id"] == R.fields["id"]) - switch(M.fields["criminal"]) - if("*Arrest*") - color = "bad" - if("Incarcerated") - color = "brown" - if("Parolled", "Released") - color = "average" - if("None") - color = "good" - criminal = M.fields["criminal"] - break - records[++records.len] = list( - "ref" = "\ref[R]", - "id" = R.fields["id"], - "name" = R.fields["name"], - "color" = color, - "criminal" = criminal - ) - if(SEC_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = FIELD("Name", active1.fields["name"], "name") - fields[++fields.len] = FIELD("ID", active1.fields["id"], "id") - fields[++fields.len] = FIELD("Entity Classification", active1.fields["brain_type"], "brain_type") - fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") - fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") - fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") - fields[++fields.len] = FIELD("Rank", active1.fields["rank"], "rank") - fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") - fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], null) - fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], null) - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = active1.fields["photo-south"] - photos[++photos.len] = active1.fields["photo-west"] - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/security = list() - data["security"] = security - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - var/list/fields = list() - security["fields"] = fields - fields[++fields.len] = FIELD("Criminal Status", active2.fields["criminal"], "criminal") - fields[++fields.len] = FIELD("Minor Crimes", active2.fields["mi_crim"], "mi_crim") - fields[++fields.len] = FIELD("Details", active2.fields["mi_crim_d"], "mi_crim_d") - fields[++fields.len] = FIELD("Major Crimes", active2.fields["ma_crim"], "ma_crim") - fields[++fields.len] = FIELD("Details", active2.fields["ma_crim_d"], "ma_crim_d") - fields[++fields.len] = FIELD("Important Notes", active2.fields["notes"], "notes") - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - security["comments"] = active2.fields["comments"] - security["empty"] = 0 - else - security["empty"] = 1 - - data["modal"] = tgui_modal_data(src) - return data - -/obj/machinery/computer/secure_data/tgui_act(action, params) - if(..()) - return TRUE - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.security.Find(active2)) - active2 = null - - . = TRUE - if(tgui_act_modal(action, params)) - return - - switch(action) - if("cleartemp") - temp = null - if("scan") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/weapon/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - if("login") - var/login_type = text2num(params["login_type"]) - if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - else if(login_type == LOGIN_TYPE_AI && isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - if(authenticated) - active1 = null - active2 = null - screen = SEC_DATA_R_LIST - else - . = FALSE - - if(.) - return - - if(authenticated) - . = TRUE - switch(action) - if("logout") - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - authenticated = null - screen = null - active1 = null - active2 = null - if("screen") - screen = clamp(text2num(params["screen"]) || 0, SEC_DATA_R_LIST, SEC_DATA_RECORD) - active1 = null - active2 = null - if("del_all") - for(var/datum/data/record/R in data_core.security) - qdel(R) - set_temp("All security records deleted.") - if("del_r") - if(active2) - set_temp("Security record deleted.") - qdel(active2) - if("del_r_2") - set_temp("All records for [active1.fields["name"]] deleted.") - if(active1) - for(var/datum/data/record/R in data_core.medical) - if((R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"])) - qdel(R) - qdel(active1) - if(active2) - qdel(active2) - if("d_rec") - var/datum/data/record/general_record = locate(params["d_rec"] || "") - if(!data_core.general.Find(general_record)) - set_temp("Record not found.", "danger") - return - - var/datum/data/record/security_record - for(var/datum/data/record/M in data_core.security) - if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) - security_record = M - break - - active1 = general_record - active2 = security_record - screen = SEC_DATA_RECORD - if("new") - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Security Record #[R.fields["id"]]" - R.fields["brain_type"] = "Unknown" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - R.fields["notes"] = "No notes." - data_core.security += R - active2 = R - screen = SEC_DATA_RECORD - set_temp("Security record created.", "success") - if("del_c") - var/index = text2num(params["del_c"] || "") - if(!index || !istype(active2, /datum/data/record)) - return - - var/list/comments = active2.fields["comments"] - index = clamp(index, 1, length(comments)) - if(comments[index]) - comments.Cut(index, index + 1) - if("search") - active1 = null - active2 = null - var/t1 = lowertext(params["t1"] || "") - if(!length(t1)) - return - - for(var/datum/data/record/R in data_core.general) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["fingerprint"])) - active1 = R - break - if(!active1) - set_temp("Security record not found. You must enter the person's exact name, ID, or fingerprint.", "danger") - return - for(var/datum/data/record/E in data_core.security) - if(E.fields["name"] == active1.fields["name"] && E.fields["id"] == active1.fields["id"]) - active2 = E - break - screen = SEC_DATA_RECORD - if("print_p") - if(!printing) - printing = TRUE - // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) - SStgui.update_uis(src) - addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) - if("photo_front") - var/icon/photo = get_photo(usr) - if(photo && active1) - active1.fields["photo_front"] = photo - active1.fields["photo-south"] = "'data:image/png;base64,[icon2base64(photo)]'" - if("photo_side") - var/icon/photo = get_photo(usr) - if(photo && active1) - active1.fields["photo_side"] = photo - active1.fields["photo-west"] = "'data:image/png;base64,[icon2base64(photo)]'" - else - return FALSE - -/** - * Called in tgui_act() to process modal actions - * - * Arguments: - * * action - The action passed by tgui - * * params - The params passed by tgui - */ -/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, params) - . = TRUE - var/id = params["id"] // The modal's ID - var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] - switch(tgui_modal_act(src, action, params)) - if(TGUI_MODAL_OPEN) - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/question = field_edit_questions[field] - var/choices = field_edit_choices[field] - if(length(choices)) - tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) - else - tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) - if("add_c") - tgui_modal_input(src, id, "Please enter your message:") - else - return FALSE - if(TGUI_MODAL_ANSWER) - var/answer = params["answer"] - switch(id) - if("edit") - var/field = arguments["field"] - if(!length(field) || !field_edit_questions[field]) - return - var/list/choices = field_edit_choices[field] - if(length(choices) && !(answer in choices)) - return - - if(field == "age") - answer = text2num(answer) - - if(field == "rank") - if(answer in joblist) - active1.fields["real_rank"] = answer - - if(field == "criminal") - for(var/mob/living/carbon/human/H in player_list) - BITSET(H.hud_updateflag, WANTED_HUD) - - if(istype(active2) && (field in active2.fields)) - active2.fields[field] = answer - if(istype(active1) && (field in active1.fields)) - active1.fields[field] = answer - if("add_c") - if(!length(answer) || !istype(active2) || !length(authenticated)) - return - active2.fields["comments"] += list(list( - header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", - text = answer - )) - else - return FALSE - else - return FALSE - - -/** - * Called when the print timer finishes - */ -/obj/machinery/computer/secure_data/proc/print_finish() - var/obj/item/weapon/paper/P = new(loc) - P.info = "
    Security Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nSpecies: [active1.fields["species"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - P.info += {"
    \n
    Security Data
    -
    \nCriminal Status: [active2.fields["criminal"]]
    \n -
    \nMinor Crimes: [active2.fields["mi_crim"]] -
    \nDetails: [active2.fields["mi_crim_d"]]
    \n -
    \nMajor Crimes: [active2.fields["ma_crim"]] -
    \nDetails: [active2.fields["ma_crim_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c["header"]]
    [c["text"]]
    " - else - P.info += "Security Record Lost!
    " - P.info += "" - P.name = "paper - 'Security Record: [active1.fields["name"]]'" - printing = FALSE - SStgui.update_uis(src) - - -/** - * Sets a temporary message to display to the user - * - * Arguments: - * * text - Text to display, null/empty to clear the message from the UI - * * style - The style of the message: (color name), info, success, warning, danger, virus - */ -/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE) - temp = list(text = text, style = style) - if(update_now) - SStgui.update_uis(src) - -/obj/machinery/computer/secure_data/proc/is_not_allowed(var/mob/user) - return !src.authenticated || user.stat || user.restrained() || (!in_range(src, user) && (!istype(user, /mob/living/silicon))) - -/obj/machinery/computer/secure_data/proc/get_photo(var/mob/user) - if(istype(user.get_active_hand(), /obj/item/weapon/photo)) - var/obj/item/weapon/photo/photo = user.get_active_hand() - return photo.img - if(istype(user, /mob/living/silicon)) - var/mob/living/silicon/tempAI = usr - var/obj/item/weapon/photo/selection = tempAI.GetPicture() - if (selection) - return selection.img - -/obj/machinery/computer/secure_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(PDA_Manifest.len) - PDA_Manifest.Cut() - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -/obj/machinery/computer/secure_data/detective_computer - icon_state = "messyfiles" - -#undef FIELD +#define SEC_DATA_R_LIST 2 // Record list +#define SEC_DATA_MAINT 3 // Records maintenance +#define SEC_DATA_RECORD 4 // Record + +#define FIELD(N, V, E) list(field = N, value = V, edit = E) + +/obj/machinery/computer/secure_data//TODO:SANITY + name = "security records console" + desc = "Used to view, edit and maintain security records" + icon_keyboard = "security_key" + icon_screen = "security" + light_color = "#a91515" + req_one_access = list(access_security, access_forensics_lockers, access_lawyer) + circuit = /obj/item/weapon/circuitboard/secure_data + var/obj/item/weapon/card/id/scan = null + var/authenticated = null + var/rank = null + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/list/temp = null + var/printing = null + // The below are used to make modal generation more convenient + var/static/list/field_edit_questions + var/static/list/field_edit_choices + +/obj/machinery/computer/secure_data/Initialize() + . = ..() + field_edit_questions = list( + // General + "name" = "Please enter new name:", + "id" = "Please enter new id:", + "sex" = "Please select new sex:", + "species" = "Please input new species:", + "age" = "Please input new age:", + "rank" = "Please enter new rank:", + "fingerprint" = "Please input new fingerprint hash:", + // Security + "brain_type" = "Please select new brain type:", + "criminal" = "Please select new criminal status:", + "mi_crim" = "Please input new minor crime:", + "mi_crim_d" = "Please input minor crime summary.", + "ma_crim" = "Please input new major crime:", + "ma_crim_d" = "Please input new major crime summary.", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = all_genders_text_list, + // Security + "criminal" = list("*Arrest*", "Incarcerated", "Parolled", "Released", "None"), + ) + +/obj/machinery/computer/secure_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/secure_data/verb/eject_id() + set category = "Object" + set name = "Eject ID Card" + set src in oview(1) + + if(!usr || usr.stat || usr.lying) return + + if(scan) + to_chat(usr, "You remove \the [scan] from \the [src].") + scan.loc = get_turf(src) + if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) + usr.put_in_hands(scan) + scan = null + else + to_chat(usr, "There is nothing to remove from the console.") + return + +/obj/machinery/computer/secure_data/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) + O.loc = src + scan = O + to_chat(user, "You insert \the [O].") + tgui_interact(user) + else + ..() + +/obj/machinery/computer/secure_data/attack_ai(mob/user as mob) + return attack_hand(user) + +//Someone needs to break down the dat += into chunks instead of long ass lines. +/obj/machinery/computer/secure_data/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + tgui_interact(user) + +/obj/machinery/computer/secure_data/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SecurityRecords", "Security Records") // 800, 380 + ui.open() + ui.set_autoupdate(FALSE) + + +/obj/machinery/computer/secure_data/tgui_data(mob/user) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["rank"] = rank + data["screen"] = screen + data["printing"] = printing + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) + if(authenticated) + switch(screen) + if(SEC_DATA_R_LIST) + if(!isnull(data_core.general)) + var/list/records = list() + data["records"] = records + for(var/datum/data/record/R in sortRecord(data_core.general)) + var/color = null + var/criminal = "None" + for(var/datum/data/record/M in data_core.security) + if(M.fields["name"] == R.fields["name"] && M.fields["id"] == R.fields["id"]) + switch(M.fields["criminal"]) + if("*Arrest*") + color = "bad" + if("Incarcerated") + color = "brown" + if("Parolled", "Released") + color = "average" + if("None") + color = "good" + criminal = M.fields["criminal"] + break + records[++records.len] = list( + "ref" = "\ref[R]", + "id" = R.fields["id"], + "name" = R.fields["name"], + "color" = color, + "criminal" = criminal + ) + if(SEC_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = FIELD("Name", active1.fields["name"], "name") + fields[++fields.len] = FIELD("ID", active1.fields["id"], "id") + fields[++fields.len] = FIELD("Entity Classification", active1.fields["brain_type"], "brain_type") + fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") + fields[++fields.len] = FIELD("Species", active1.fields["species"], "species") + fields[++fields.len] = FIELD("Age", "[active1.fields["age"]]", "age") + fields[++fields.len] = FIELD("Rank", active1.fields["rank"], "rank") + fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") + fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], null) + fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], null) + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = active1.fields["photo-south"] + photos[++photos.len] = active1.fields["photo-west"] + general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/security = list() + data["security"] = security + if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) + var/list/fields = list() + security["fields"] = fields + fields[++fields.len] = FIELD("Criminal Status", active2.fields["criminal"], "criminal") + fields[++fields.len] = FIELD("Minor Crimes", active2.fields["mi_crim"], "mi_crim") + fields[++fields.len] = FIELD("Details", active2.fields["mi_crim_d"], "mi_crim_d") + fields[++fields.len] = FIELD("Major Crimes", active2.fields["ma_crim"], "ma_crim") + fields[++fields.len] = FIELD("Details", active2.fields["ma_crim_d"], "ma_crim_d") + fields[++fields.len] = FIELD("Important Notes", active2.fields["notes"], "notes") + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + security["comments"] = active2.fields["comments"] + security["empty"] = 0 + else + security["empty"] = 1 + + data["modal"] = tgui_modal_data(src) + return data + +/obj/machinery/computer/secure_data/tgui_act(action, params) + if(..()) + return TRUE + + if(!data_core.general.Find(active1)) + active1 = null + if(!data_core.security.Find(active2)) + active2 = null + + . = TRUE + if(tgui_act_modal(action, params)) + return + + switch(action) + if("cleartemp") + temp = null + if("scan") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + if("login") + var/login_type = text2num(params["login_type"]) + if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + if(authenticated) + active1 = null + active2 = null + screen = SEC_DATA_R_LIST + else + . = FALSE + + if(.) + return + + if(authenticated) + . = TRUE + switch(action) + if("logout") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + authenticated = null + screen = null + active1 = null + active2 = null + if("screen") + screen = clamp(text2num(params["screen"]) || 0, SEC_DATA_R_LIST, SEC_DATA_RECORD) + active1 = null + active2 = null + if("del_all") + for(var/datum/data/record/R in data_core.security) + qdel(R) + set_temp("All security records deleted.") + if("del_r") + if(active2) + set_temp("Security record deleted.") + qdel(active2) + if("del_r_2") + set_temp("All records for [active1.fields["name"]] deleted.") + if(active1) + for(var/datum/data/record/R in data_core.medical) + if((R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"])) + qdel(R) + qdel(active1) + if(active2) + qdel(active2) + if("d_rec") + var/datum/data/record/general_record = locate(params["d_rec"] || "") + if(!data_core.general.Find(general_record)) + set_temp("Record not found.", "danger") + return + + var/datum/data/record/security_record + for(var/datum/data/record/M in data_core.security) + if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) + security_record = M + break + + active1 = general_record + active2 = security_record + screen = SEC_DATA_RECORD + if("new") + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Security Record #[R.fields["id"]]" + R.fields["brain_type"] = "Unknown" + R.fields["criminal"] = "None" + R.fields["mi_crim"] = "None" + R.fields["mi_crim_d"] = "No minor crime convictions." + R.fields["ma_crim"] = "None" + R.fields["ma_crim_d"] = "No major crime convictions." + R.fields["notes"] = "No notes." + R.fields["notes"] = "No notes." + data_core.security += R + active2 = R + screen = SEC_DATA_RECORD + set_temp("Security record created.", "success") + if("del_c") + var/index = text2num(params["del_c"] || "") + if(!index || !istype(active2, /datum/data/record)) + return + + var/list/comments = active2.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("search") + active1 = null + active2 = null + var/t1 = lowertext(params["t1"] || "") + if(!length(t1)) + return + + for(var/datum/data/record/R in data_core.general) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["fingerprint"])) + active1 = R + break + if(!active1) + set_temp("Security record not found. You must enter the person's exact name, ID, or fingerprint.", "danger") + return + for(var/datum/data/record/E in data_core.security) + if(E.fields["name"] == active1.fields["name"] && E.fields["id"] == active1.fields["id"]) + active2 = E + break + screen = SEC_DATA_RECORD + if("print_p") + if(!printing) + printing = TRUE + // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + SStgui.update_uis(src) + addtimer(CALLBACK(src, PROC_REF(print_finish)), 5 SECONDS) + if("photo_front") + var/icon/photo = get_photo(usr) + if(photo && active1) + active1.fields["photo_front"] = photo + active1.fields["photo-south"] = "'data:image/png;base64,[icon2base64(photo)]'" + if("photo_side") + var/icon/photo = get_photo(usr) + if(photo && active1) + active1.fields["photo_side"] = photo + active1.fields["photo-west"] = "'data:image/png;base64,[icon2base64(photo)]'" + else + return FALSE + +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) + else + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("add_c") + tgui_modal_input(src, id, "Please enter your message:") + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return + + if(field == "age") + answer = text2num(answer) + + if(field == "rank") + if(answer in joblist) + active1.fields["real_rank"] = answer + + if(field == "criminal") + for(var/mob/living/carbon/human/H in player_list) + BITSET(H.hud_updateflag, WANTED_HUD) + + if(istype(active2) && (field in active2.fields)) + active2.fields[field] = answer + if(istype(active1) && (field in active1.fields)) + active1.fields[field] = answer + if("add_c") + if(!length(answer) || !istype(active2) || !length(authenticated)) + return + active2.fields["comments"] += list(list( + header = "Made by [authenticated] ([rank]) at [worldtime2stationtime(world.time)]", + text = answer + )) + else + return FALSE + else + return FALSE + + +/** + * Called when the print timer finishes + */ +/obj/machinery/computer/secure_data/proc/print_finish() + var/obj/item/weapon/paper/P = new(loc) + P.info = "
    Security Record

    " + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nSpecies: [active1.fields["species"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) + P.info += {"
    \n
    Security Data
    +
    \nCriminal Status: [active2.fields["criminal"]]
    \n +
    \nMinor Crimes: [active2.fields["mi_crim"]] +
    \nDetails: [active2.fields["mi_crim_d"]]
    \n +
    \nMajor Crimes: [active2.fields["ma_crim"]] +
    \nDetails: [active2.fields["ma_crim_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c["header"]]
    [c["text"]]
    " + else + P.info += "Security Record Lost!
    " + P.info += "" + P.name = "paper - 'Security Record: [active1.fields["name"]]'" + printing = FALSE + SStgui.update_uis(src) + + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger, virus + */ +/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + +/obj/machinery/computer/secure_data/proc/is_not_allowed(var/mob/user) + return !src.authenticated || user.stat || user.restrained() || (!in_range(src, user) && (!istype(user, /mob/living/silicon))) + +/obj/machinery/computer/secure_data/proc/get_photo(var/mob/user) + if(istype(user.get_active_hand(), /obj/item/weapon/photo)) + var/obj/item/weapon/photo/photo = user.get_active_hand() + return photo.img + if(istype(user, /mob/living/silicon)) + var/mob/living/silicon/tempAI = usr + var/obj/item/weapon/photo/selection = tempAI.GetPicture() + if (selection) + return selection.img + +/obj/machinery/computer/secure_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in data_core.security) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(first_names_male), pick(first_names_female))] [pick(last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") + if(5) + R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") + if(PDA_Manifest.len) + PDA_Manifest.Cut() + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + +/obj/machinery/computer/secure_data/detective_computer + icon_state = "messyfiles" + +#undef FIELD diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm index d4e58675493..42ee83612d6 100644 --- a/code/game/machinery/computer/shuttle.dm +++ b/code/game/machinery/computer/shuttle.dm @@ -1,71 +1,71 @@ -/obj/machinery/computer/shuttle - name = "Shuttle" - desc = "For shuttle control." - icon_keyboard = "tech_key" - icon_screen = "shuttle" - light_color = "#00ffff" - var/auth_need = 3.0 - var/list/authorized = list( ) - - -/obj/machinery/computer/shuttle/attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob) - if(stat & (BROKEN|NOPOWER)) return - if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location() || !( user ))) return - if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if (istype(W, /obj/item/device/pda)) - var/obj/item/device/pda/pda = W - W = pda.id - if (!W:access) //no access - to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") - return - - var/list/cardaccess = W:access - if(!istype(cardaccess, /list) || !cardaccess.len) //no access - to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") - return - - if(!(access_heads in W:access)) //doesn't have this access - to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") - return 0 - - var/choice = tgui_alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", list("Authorize", "Repeal", "Abort")) - if(emergency_shuttle.location() && user.get_active_hand() != W) - return 0 - switch(choice) - if("Authorize") - src.authorized -= W:registered_name - src.authorized += W:registered_name - if (src.auth_need - src.authorized.len > 0) - message_admins("[key_name_admin(user)] has authorized early shuttle launch") - log_game("[user.ckey] has authorized early shuttle launch") - to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") - else - message_admins("[key_name_admin(user)] has launched the shuttle") - log_game("[user.ckey] has launched the shuttle early") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - emergency_shuttle.set_launch_countdown(10) - //src.authorized = null - qdel(src.authorized) - src.authorized = list( ) - - if("Repeal") - src.authorized -= W:registered_name - to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") - - if("Abort") - to_world("All authorizations to shortening time for shuttle launch have been revoked!") - src.authorized.len = 0 - src.authorized = list( ) - - else if (istype(W, /obj/item/weapon/card/emag) && !emagged) - var/choice = tgui_alert(user, "Would you like to launch the shuttle?", "Shuttle control", list("Launch", "Cancel")) - - if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W) - switch(choice) - if("Launch") - to_world("Alert: Shuttle launch time shortened to 10 seconds!") - emergency_shuttle.set_launch_countdown(10) - emagged = 1 - if("Cancel") - return - return +/obj/machinery/computer/shuttle + name = "Shuttle" + desc = "For shuttle control." + icon_keyboard = "tech_key" + icon_screen = "shuttle" + light_color = "#00ffff" + var/auth_need = 3.0 + var/list/authorized = list( ) + + +/obj/machinery/computer/shuttle/attackby(var/obj/item/weapon/card/W as obj, var/mob/user as mob) + if(stat & (BROKEN|NOPOWER)) return + if ((!( istype(W, /obj/item/weapon/card) ) || !( ticker ) || emergency_shuttle.location() || !( user ))) return + if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if (istype(W, /obj/item/device/pda)) + var/obj/item/device/pda/pda = W + W = pda.id + if (!W:access) //no access + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") + return + + var/list/cardaccess = W:access + if(!istype(cardaccess, /list) || !cardaccess.len) //no access + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") + return + + if(!(access_heads in W:access)) //doesn't have this access + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") + return 0 + + var/choice = tgui_alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", list("Authorize", "Repeal", "Abort")) + if(emergency_shuttle.location() && user.get_active_hand() != W) + return 0 + switch(choice) + if("Authorize") + src.authorized -= W:registered_name + src.authorized += W:registered_name + if (src.auth_need - src.authorized.len > 0) + message_admins("[key_name_admin(user)] has authorized early shuttle launch") + log_game("[user.ckey] has authorized early shuttle launch") + to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") + else + message_admins("[key_name_admin(user)] has launched the shuttle") + log_game("[user.ckey] has launched the shuttle early") + to_world("Alert: Shuttle launch time shortened to 10 seconds!") + emergency_shuttle.set_launch_countdown(10) + //src.authorized = null + qdel(src.authorized) + src.authorized = list( ) + + if("Repeal") + src.authorized -= W:registered_name + to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") + + if("Abort") + to_world("All authorizations to shortening time for shuttle launch have been revoked!") + src.authorized.len = 0 + src.authorized = list( ) + + else if (istype(W, /obj/item/weapon/card/emag) && !emagged) + var/choice = tgui_alert(user, "Would you like to launch the shuttle?", "Shuttle control", list("Launch", "Cancel")) + + if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W) + switch(choice) + if("Launch") + to_world("Alert: Shuttle launch time shortened to 10 seconds!") + emergency_shuttle.set_launch_countdown(10) + emagged = 1 + if("Cancel") + return + return diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index ce0bea0aff3..cc8727343c2 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -1,331 +1,331 @@ -//Config stuff -#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station -#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock -#define SPECOPS_RETURN_DELAY 600 //Time between the shuttle is capable of moving. - -var/specops_shuttle_moving_to_station = 0 -var/specops_shuttle_moving_to_centcom = 0 -var/specops_shuttle_at_station = 0 -var/specops_shuttle_can_send = 1 -var/specops_shuttle_time = 0 -var/specops_shuttle_timeleft = 0 - -/obj/machinery/computer/specops_shuttle - name = "special operations shuttle control console" - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_cent_specops) -// req_access = list(ACCESS_CENT_SPECOPS) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/specops_shuttle_timereset = 0 - -/proc/specops_return() - var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - - var/area/start_location = locate(/area/shuttle/specops/station) - var/area/end_location = locate(/area/shuttle/specops/centcom) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") - - specops_shuttle_at_station = 0 - - for(var/obj/machinery/computer/specops_shuttle/S in machines) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_process() - var/area/centcom/specops/special_ops = locate()//Where is the specops area located? - var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") -// message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" -// announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - if (specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if (!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/blast/M in special_ops) - switch(M.id) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in landmarks_list) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L) - for(var/obj/effect/landmark/L in landmarks_list) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc) - P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - P.target = pick(spawn_marauder)//Where the marauder will arrive. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in special_ops) - switch(M.id) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/blast/M in special_ops) - switch(M.id)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - special_ops.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - var/area/start_location = locate(/area/shuttle/specops/centcom) - var/area/end_location = locate(/area/shuttle/specops/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - - for(var/obj/machinery/computer/specops_shuttle/S in machines) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_can_move() - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) - return 0 - for(var/obj/machinery/computer/specops_shuttle/S in machines) - if(world.timeofday <= S.specops_shuttle_timereset) - return 0 - return 1 - -/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - -/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - - if(..()) - return - - user.machine = src - var/dat - if (temp) - dat = temp - else - dat += {"
    Special Operations Shuttle
    - \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
    - [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/specops_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.machine = src - - if (href_list["sendtodock"]) - if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if (!specops_can_move()) - to_chat(usr, "[using_map.boss_name] will not allow the Special Operations shuttle to return yet.") - if(world.timeofday <= specops_shuttle_timereset) - if (((world.timeofday - specops_shuttle_timereset)/10) > 60) - to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") - to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") - return - - to_chat(usr, "The Special Operations shuttle will arrive at [using_map.boss_name] in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - specops_shuttle_moving_to_centcom = 1 - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_return() - - else if (href_list["sendtostation"]) - if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if (!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/centcom/specops/special_ops = locate() - if(special_ops) - special_ops.readyalert()//Trigger alarm for the spec ops area. - specops_shuttle_moving_to_station = 1 - - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_process() - - else if (href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station +#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock +#define SPECOPS_RETURN_DELAY 600 //Time between the shuttle is capable of moving. + +var/specops_shuttle_moving_to_station = 0 +var/specops_shuttle_moving_to_centcom = 0 +var/specops_shuttle_at_station = 0 +var/specops_shuttle_can_send = 1 +var/specops_shuttle_time = 0 +var/specops_shuttle_timeleft = 0 + +/obj/machinery/computer/specops_shuttle + name = "special operations shuttle control console" + icon_keyboard = "security_key" + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_cent_specops) +// req_access = list(ACCESS_CENT_SPECOPS) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/specops_shuttle_timereset = 0 + +/proc/specops_return() + var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + + while(specops_shuttle_time - world.timeofday > 0) + var/ticksleft = specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + specops_shuttle_time = world.timeofday + 10 // midnight rollover + specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + specops_shuttle_moving_to_station = 0 + specops_shuttle_moving_to_centcom = 0 + + specops_shuttle_at_station = 1 + + var/area/start_location = locate(/area/shuttle/specops/station) + var/area/end_location = locate(/area/shuttle/specops/centcom) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") + + specops_shuttle_at_station = 0 + + for(var/obj/machinery/computer/specops_shuttle/S in machines) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_process() + var/area/centcom/specops/special_ops = locate()//Where is the specops area located? + var/obj/item/device/radio/intercom/announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") +// message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" +// announcer.autosay(message, "A.L.I.C.E.", "Response Team") + + while(specops_shuttle_time - world.timeofday > 0) + var/ticksleft = specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + specops_shuttle_time = world.timeofday + 10 // midnight rollover + specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + specops_shuttle_moving_to_station = 0 + specops_shuttle_moving_to_centcom = 0 + + specops_shuttle_at_station = 1 + if (specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return + + if (!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/blast/M in special_ops) + switch(M.id) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in landmarks_list) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L) + for(var/obj/effect/landmark/L in landmarks_list) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc) + P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + P.target = pick(spawn_marauder)//Where the marauder will arrive. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in special_ops) + switch(M.id) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/blast/M in special_ops) + switch(M.id)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + special_ops.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. + + var/area/start_location = locate(/area/shuttle/specops/centcom) + var/area/end_location = locate(/area/shuttle/specops/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived to [station_name()]. Commence operation!") + + for(var/obj/machinery/computer/specops_shuttle/S in machines) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_can_move() + if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) + return 0 + for(var/obj/machinery/computer/specops_shuttle/S in machines) + if(world.timeofday <= S.specops_shuttle_timereset) + return 0 + return 1 + +/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") + +/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + + if(..()) + return + + user.machine = src + var/dat + if (temp) + dat = temp + else + dat += {"
    Special Operations Shuttle
    + \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
    + [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/specops_shuttle/Topic(href, href_list) + if(..()) + return 1 + + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.machine = src + + if (href_list["sendtodock"]) + if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return + + if (!specops_can_move()) + to_chat(usr, "[using_map.boss_name] will not allow the Special Operations shuttle to return yet.") + if(world.timeofday <= specops_shuttle_timereset) + if (((world.timeofday - specops_shuttle_timereset)/10) > 60) + to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") + to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") + return + + to_chat(usr, "The Special Operations shuttle will arrive at [using_map.boss_name] in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + specops_shuttle_moving_to_centcom = 1 + specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_return() + + else if (href_list["sendtostation"]) + if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return + + if (!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/centcom/specops/special_ops = locate() + if(special_ops) + special_ops.readyalert()//Trigger alarm for the spec ops area. + specops_shuttle_moving_to_station = 1 + + specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_process() + + else if (href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 051a8e368bd..d6e8a3609fb 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -1,54 +1,54 @@ - -/obj/machinery/computer/station_alert - name = "Station Alert Console" - desc = "Used to access the station's automated alert system." - icon_keyboard = "tech_key" - icon_screen = "alert:0" - light_color = "#e6ffff" - circuit = /obj/item/weapon/circuitboard/stationalert_engineering - var/datum/tgui_module/alarm_monitor/alarm_monitor - var/monitor_type = /datum/tgui_module/alarm_monitor/engineering - -/obj/machinery/computer/station_alert/security - monitor_type = /datum/tgui_module/alarm_monitor/security - circuit = /obj/item/weapon/circuitboard/stationalert_security - -/obj/machinery/computer/station_alert/all - monitor_type = /datum/tgui_module/alarm_monitor/all - circuit = /obj/item/weapon/circuitboard/stationalert_all - -/obj/machinery/computer/station_alert/Initialize() - alarm_monitor = new monitor_type(src) - alarm_monitor.register_alarm(src, /atom/proc/update_icon) - . = ..() - -/obj/machinery/computer/station_alert/Destroy() - alarm_monitor.unregister_alarm(src) - qdel(alarm_monitor) - ..() - -/obj/machinery/computer/station_alert/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - return - -/obj/machinery/computer/station_alert/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - return - -/obj/machinery/computer/station_alert/tgui_interact(mob/user) - alarm_monitor.tgui_interact(user) - -/obj/machinery/computer/station_alert/update_icon() - if(!(stat & (BROKEN|NOPOWER))) - var/list/alarms = alarm_monitor ? alarm_monitor.major_alarms() : list() - if(alarms.len) - icon_screen = "alert:2" - else - icon_screen = initial(icon_screen) - ..() + +/obj/machinery/computer/station_alert + name = "Station Alert Console" + desc = "Used to access the station's automated alert system." + icon_keyboard = "tech_key" + icon_screen = "alert:0" + light_color = "#e6ffff" + circuit = /obj/item/weapon/circuitboard/stationalert_engineering + var/datum/tgui_module/alarm_monitor/alarm_monitor + var/monitor_type = /datum/tgui_module/alarm_monitor/engineering + +/obj/machinery/computer/station_alert/security + monitor_type = /datum/tgui_module/alarm_monitor/security + circuit = /obj/item/weapon/circuitboard/stationalert_security + +/obj/machinery/computer/station_alert/all + monitor_type = /datum/tgui_module/alarm_monitor/all + circuit = /obj/item/weapon/circuitboard/stationalert_all + +/obj/machinery/computer/station_alert/Initialize() + alarm_monitor = new monitor_type(src) + alarm_monitor.register_alarm(src, /atom/proc/update_icon) + . = ..() + +/obj/machinery/computer/station_alert/Destroy() + alarm_monitor.unregister_alarm(src) + qdel(alarm_monitor) + ..() + +/obj/machinery/computer/station_alert/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + return + +/obj/machinery/computer/station_alert/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + return + +/obj/machinery/computer/station_alert/tgui_interact(mob/user) + alarm_monitor.tgui_interact(user) + +/obj/machinery/computer/station_alert/update_icon() + if(!(stat & (BROKEN|NOPOWER))) + var/list/alarms = alarm_monitor ? alarm_monitor.major_alarms() : list() + if(alarms.len) + icon_screen = "alert:2" + else + icon_screen = initial(icon_screen) + ..() diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index c7db7b94e37..82994660717 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -1,6 +1,6 @@ -/obj/machinery/computer/syndicate_elite_shuttle - name = "elite mercenary squad shuttle control console" - icon_keyboard = "syndie_key" - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_cent_specops) +/obj/machinery/computer/syndicate_elite_shuttle + name = "elite mercenary squad shuttle control console" + icon_keyboard = "syndie_key" + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_cent_specops) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 6a61f015024..29979be8940 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -1,160 +1,160 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -//Circuit boards are in /code/game/objects/items/weapons/circuitboards/machinery/ - -/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. - name = "machine frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "box_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_OFF - var/obj/item/weapon/circuitboard/circuit = null - var/list/components = null - var/list/req_components = null - var/list/req_component_names = null - var/state = 1 - - proc/update_desc() - var/D - if(req_components) - var/list/component_list = new - for(var/I in req_components) - if(req_components[I] > 0) - component_list += "[num2text(req_components[I])] [req_component_names[I]]" - D = "Requires [english_list(component_list)]." - desc = D - -/obj/machinery/constructable_frame/machine_frame - attackby(obj/item/P as obj, mob/user as mob) - switch(state) - if(1) - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if (C.get_amount() < 5) - to_chat(user, "You need five lengths of cable to add them to the frame.") - return - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20) && state == 1) - if(C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 2 - icon_state = "box_1" - else - if(P.is_wrench()) - playsound(src, W.usesound, 75, 1) - to_chat(user, "You dismantle the frame") - new /obj/item/stack/material/steel(src.loc, 5) - qdel(src) - if(2) - if(istype(P, /obj/item/weapon/circuitboard)) - var/obj/item/weapon/circuitboard/B = P - if(B.board_type == "machine") - //VOREStation Addition End - if(istype(B, /obj/item/weapon/circuitboard/quantumpad) && istype(get_area(src), /area/shuttle)) - to_chat(user, "This is too unstable a platform for a quantum pad to operate on!") - return - //VOREStation Addition End - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You add the circuit board to the frame.") - circuit = P - user.drop_item() - P.loc = src - icon_state = "box_2" - state = 3 - components = list() - req_components = circuit.req_components.Copy() - for(var/A in circuit.req_components) - req_components[A] = circuit.req_components[A] - req_component_names = circuit.req_components.Copy() - for(var/A in req_components) - var/cp = text2path(A) - var/obj/ct = new cp() // have to quickly instantiate it get name - req_component_names[A] = ct.name - update_desc() - to_chat(user, desc) - else - to_chat(user, "This frame does not accept circuit boards of this type!") - else - if(P.is_wirecutter()) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 1 - icon_state = "box_0" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) - A.amount = 5 - - if(3) - if(P.is_crowbar()) - playsound(src, P.usesound, 50, 1) - state = 2 - circuit.loc = src.loc - circuit = null - if(components.len == 0) - to_chat(user, "You remove the circuit board.") - else - to_chat(user, "You remove the circuit board and other components.") - for(var/obj/item/weapon/W in components) - W.loc = src.loc - desc = initial(desc) - req_components = null - components = null - icon_state = "box_1" - else - if(P.is_screwdriver()) - var/component_check = 1 - for(var/R in req_components) - if(req_components[R] > 0) - component_check = 0 - break - if(component_check) - playsound(src, P.usesound, 50, 1) - var/obj/machinery/new_machine = new src.circuit.build_path(src.loc, src.dir) - - if(new_machine.component_parts) - new_machine.component_parts.Cut() - else - new_machine.component_parts = list() - - src.circuit.construct(new_machine) - - for(var/obj/O in src) - if(circuit.contain_parts) // things like disposal don't want their parts in them - O.loc = new_machine - else - O.loc = null - new_machine.component_parts += O - - if(circuit.contain_parts) - circuit.loc = new_machine - else - circuit.loc = null - - new_machine.RefreshParts() - qdel(src) - else - if(istype(P, /obj/item)) - for(var/I in req_components) - if(istype(P, text2path(I)) && (req_components[I] > 0)) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - if(P.is_cable_coil)) - var/obj/item/stack/cable_coil/CP = P - if(CP.get_amount() > 1) - var/camt = min(CP.amount, req_components[I]) // amount of cable to take, idealy amount required, but limited by amount provided - var/obj/item/stack/cable_coil/CC = new /obj/item/stack/cable_coil(src) - CC.amount = camt - CC.update_icon() - CP.use(camt) - components += CC - req_components[I] -= camt - update_desc() - break - user.drop_item() - P.loc = src - components += P - req_components[I]-- - update_desc() - break - to_chat(user, desc) - if(P && P.loc != src && !istype(P, /obj/item/stack/cable_coil)) - to_chat(user, "You cannot add that component to the machine!") +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +//Circuit boards are in /code/game/objects/items/weapons/circuitboards/machinery/ + +/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. + name = "machine frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "box_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_OFF + var/obj/item/weapon/circuitboard/circuit = null + var/list/components = null + var/list/req_components = null + var/list/req_component_names = null + var/state = 1 + + proc/update_desc() + var/D + if(req_components) + var/list/component_list = new + for(var/I in req_components) + if(req_components[I] > 0) + component_list += "[num2text(req_components[I])] [req_component_names[I]]" + D = "Requires [english_list(component_list)]." + desc = D + +/obj/machinery/constructable_frame/machine_frame + attackby(obj/item/P as obj, mob/user as mob) + switch(state) + if(1) + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if (C.get_amount() < 5) + to_chat(user, "You need five lengths of cable to add them to the frame.") + return + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20) && state == 1) + if(C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 2 + icon_state = "box_1" + else + if(P.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 75, 1) + to_chat(user, "You dismantle the frame") + new /obj/item/stack/material/steel(src.loc, 5) + qdel(src) + if(2) + if(istype(P, /obj/item/weapon/circuitboard)) + var/obj/item/weapon/circuitboard/B = P + if(B.board_type == "machine") + //VOREStation Addition End + if(istype(B, /obj/item/weapon/circuitboard/quantumpad) && istype(get_area(src), /area/shuttle)) + to_chat(user, "This is too unstable a platform for a quantum pad to operate on!") + return + //VOREStation Addition End + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You add the circuit board to the frame.") + circuit = P + user.drop_item() + P.loc = src + icon_state = "box_2" + state = 3 + components = list() + req_components = circuit.req_components.Copy() + for(var/A in circuit.req_components) + req_components[A] = circuit.req_components[A] + req_component_names = circuit.req_components.Copy() + for(var/A in req_components) + var/cp = text2path(A) + var/obj/ct = new cp() // have to quickly instantiate it get name + req_component_names[A] = ct.name + update_desc() + to_chat(user, desc) + else + to_chat(user, "This frame does not accept circuit boards of this type!") + else + if(P.has_tool_quality(TOOL_WIRECUTTER)) + playsound(src, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 1 + icon_state = "box_0" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) + A.amount = 5 + + if(3) + if(P.has_tool_quality(TOOL_CROWBAR)) + playsound(src, P.usesound, 50, 1) + state = 2 + circuit.loc = src.loc + circuit = null + if(components.len == 0) + to_chat(user, "You remove the circuit board.") + else + to_chat(user, "You remove the circuit board and other components.") + for(var/obj/item/weapon/W in components) + W.loc = src.loc + desc = initial(desc) + req_components = null + components = null + icon_state = "box_1" + else + if(P.has_tool_quality(TOOL_SCREWDRIVER)) + var/component_check = 1 + for(var/R in req_components) + if(req_components[R] > 0) + component_check = 0 + break + if(component_check) + playsound(src, P.usesound, 50, 1) + var/obj/machinery/new_machine = new src.circuit.build_path(src.loc, src.dir) + + if(new_machine.component_parts) + new_machine.component_parts.Cut() + else + new_machine.component_parts = list() + + src.circuit.construct(new_machine) + + for(var/obj/O in src) + if(circuit.contain_parts) // things like disposal don't want their parts in them + O.loc = new_machine + else + O.loc = null + new_machine.component_parts += O + + if(circuit.contain_parts) + circuit.loc = new_machine + else + circuit.loc = null + + new_machine.RefreshParts() + qdel(src) + else + if(istype(P, /obj/item)) + for(var/I in req_components) + if(istype(P, text2path(I)) && (req_components[I] > 0)) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + if(P.is_cable_coil)) + var/obj/item/stack/cable_coil/CP = P + if(CP.get_amount() > 1) + var/camt = min(CP.amount, req_components[I]) // amount of cable to take, idealy amount required, but limited by amount provided + var/obj/item/stack/cable_coil/CC = new /obj/item/stack/cable_coil(src) + CC.amount = camt + CC.update_icon() + CP.use(camt) + components += CC + req_components[I] -= camt + update_desc() + break + user.drop_item() + P.loc = src + components += P + req_components[I]-- + update_desc() + break + to_chat(user, desc) + if(P && P.loc != src && !istype(P, /obj/item/stack/cable_coil)) + to_chat(user, "You cannot add that component to the machine!") diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 0226d0bc66f..3a394b1973d 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -1,368 +1,368 @@ -#define HEAT_CAPACITY_HUMAN 100 //249840 J/K, for a 72 kg person. - -/obj/machinery/atmospherics/unary/cryo_cell - name = "cryo cell" - desc = "Used to cool people down for medical reasons. Totally." - icon = 'icons/obj/cryogenics.dmi' // map only - icon_state = "pod_preview" - density = TRUE - anchored = TRUE - layer = UNDER_JUNK_LAYER - interact_offline = 1 - - var/on = 0 - use_power = USE_POWER_IDLE - idle_power_usage = 20 - active_power_usage = 200 - buckle_lying = FALSE - buckle_dir = SOUTH - clicksound = 'sound/machines/buttonbeep.ogg' - clickvol = 30 - - var/temperature_archived - var/mob/living/carbon/occupant = null - var/obj/item/weapon/reagent_containers/glass/beaker = null - - var/current_heat_capacity = 50 - - var/image/fluid - -/obj/machinery/atmospherics/unary/cryo_cell/New() - ..() - icon = 'icons/obj/cryogenics_split.dmi' - icon_state = "base" - initialize_directions = dir - -/obj/machinery/atmospherics/unary/cryo_cell/Initialize() - . = ..() - var/image/tank = image(icon,"tank") - tank.alpha = 200 - tank.pixel_y = 18 - tank.plane = MOB_PLANE - tank.layer = MOB_LAYER+0.2 //Above fluid - fluid = image(icon, "tube_filler") - fluid.pixel_y = 18 - fluid.alpha = 200 - fluid.plane = MOB_PLANE - fluid.layer = MOB_LAYER+0.1 //Below glass, above mob - add_overlay(tank) - update_icon() - -/obj/machinery/atmospherics/unary/cryo_cell/Destroy() - var/turf/T = src.loc - T.contents += contents - if(beaker) - beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube - beaker = null - . = ..() - -/obj/machinery/atmospherics/unary/cryo_cell/process() - ..() - if(!node) - return - if(!on) - return - - if(occupant) - if(occupant.stat != 2) - process_occupant() - - if(air_contents) - temperature_archived = air_contents.temperature - heat_gas_contents() - expel_gas() - - if(abs(temperature_archived-air_contents.temperature) > 1) - network.update = 1 - - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) - // note that relaymove will also be called for mobs outside the cell with UI open - if(occupant == user && !user.stat) - go_out() - -/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) - if(user == occupant) - return - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - - tgui_interact(user) - -/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Cryo", "Cryo Cell") // 520, 470 - ui.open() - -/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user) - // this is the data which will be sent to the ui - var/data[0] - data["isOperating"] = on - data["hasOccupant"] = occupant ? TRUE : FALSE - - var/occupantData[0] - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.getMaxHealth() - occupantData["minHealth"] = config.health_threshold_dead - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["bodyTemperature"] = occupant.bodytemperature - data["occupant"] = occupantData; - - data["cellTemperature"] = round(air_contents.temperature) - data["cellTemperatureStatus"] = "good" - if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) - data["cellTemperatureStatus"] = "bad" - else if(air_contents.temperature > 225) - data["cellTemperatureStatus"] = "average" - - data["isBeakerLoaded"] = beaker ? TRUE : FALSE - data["beakerLabel"] = null - data["beakerVolume"] = 0 - if(beaker) - data["beakerLabel"] = beaker.label_text ? beaker.label_text : null - if(beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - data["beakerVolume"] += R.volume - - return data - -/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params) - if(..() || usr == occupant) - return TRUE - - . = TRUE - switch(action) - if("switchOn") - on = 1 - update_icon() - if("switchOff") - on = 0 - update_icon() - if("ejectBeaker") - if(beaker) - beaker.loc = get_step(src.loc, SOUTH) - beaker = null - update_icon() - if("ejectOccupant") - if(!occupant || isslime(usr) || ispAI(usr)) - return 0 // don't update UIs attached to this object - go_out() - else - return FALSE - - add_fingerprint(usr) - -/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) - if(istype(G, /obj/item/weapon/reagent_containers/glass)) - if(beaker) - to_chat(user, "A beaker is already loaded into the machine.") - return - - beaker = G - user.drop_item() - G.loc = src - user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") - SStgui.update_uis(src) - update_icon() - else if(istype(G, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/grab = G - if(!ismob(grab.affecting)) - return - if(occupant) - to_chat(user,"\The [src] is already occupied by [occupant].") - if(grab.affecting.has_buckled_mobs()) - to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first.")) - return - var/mob/M = grab.affecting - qdel(grab) - put_mob(M) - - return - -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) - return - put_mob(target) - -/obj/machinery/atmospherics/unary/cryo_cell/update_icon() - cut_overlay(fluid) - fluid.color = null - if(on) - if(beaker) - fluid.color = beaker.reagents.get_color() - add_overlay(fluid) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() - if(air_contents.total_moles < 10) - return - if(occupant) - if(occupant.stat >= DEAD) - return - occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) - occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise - occupant.set_stat(UNCONSCIOUS) - occupant.dir = SOUTH - if(occupant.bodytemperature < T0C) - occupant.Sleeping(max(5, (1/occupant.bodytemperature)*2000)) - occupant.Paralyse(max(5, (1/occupant.bodytemperature)*3000)) - if(air_contents.gas["oxygen"] > 2) - if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) - else - occupant.adjustOxyLoss(-1) - //severe damage should heal waaay slower without proper chemicals - if(occupant.bodytemperature < 225) - if(occupant.getToxLoss()) - occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) - if(occupant.radiation || occupant.accumulated_rads) - occupant.radiation -= 25 - occupant.accumulated_rads -= 25 - var/heal_brute = occupant.getBruteLoss() ? min(1, 20/occupant.getBruteLoss()) : 0 - var/heal_fire = occupant.getFireLoss() ? min(1, 20/occupant.getFireLoss()) : 0 - occupant.heal_organ_damage(heal_brute,heal_fire) - var/has_cryo = occupant.reagents.get_reagent_amount("cryoxadone") >= 1 - var/has_clonexa = occupant.reagents.get_reagent_amount("clonexadone") >= 1 - var/has_cryo_medicine = has_cryo || has_clonexa - if(beaker && !has_cryo_medicine) - beaker.reagents.trans_to_mob(occupant, 1, CHEM_BLOOD, 10) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() - if(air_contents.total_moles < 1) - return - var/air_heat_capacity = air_contents.heat_capacity() - var/combined_heat_capacity = current_heat_capacity + air_heat_capacity - if(combined_heat_capacity > 0) - var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature - air_contents.temperature = combined_energy/combined_heat_capacity - -/obj/machinery/atmospherics/unary/cryo_cell/proc/expel_gas() - if(air_contents.total_moles < 1) - return -// var/datum/gas_mixture/expel_gas = new -// var/remove_amount = air_contents.total_moles()/50 -// expel_gas = air_contents.remove(remove_amount) - - // Just have the gas disappear to nowhere. - //expel_gas.temperature = T20C // Lets expel hot gas and see if that helps people not die as they are removed - //loc.assume_air(expel_gas) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() - if(!(occupant)) - return - //for(var/obj/O in src) - // O.loc = src.loc - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - vis_contents -= occupant - occupant.pixel_x = occupant.default_pixel_x - occupant.pixel_y = occupant.default_pixel_y - occupant.loc = get_step(src.loc, SOUTH) //this doesn't account for walls or anything, but i don't forsee that being a problem. - if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected - occupant.bodytemperature = 261 // Changed to 70 from 140 by Zuhayr due to reoccurance of bug. - unbuckle_mob(occupant, force = TRUE) - occupant = null - current_heat_capacity = initial(current_heat_capacity) - update_use_power(USE_POWER_IDLE) - SStgui.update_uis(src) - return - -/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) - if(stat & (NOPOWER|BROKEN)) - to_chat(usr, "The cryo cell is not functioning.") - return - if(!istype(M)) - to_chat(usr, "The cryo cell cannot handle such a lifeform!") - return - if(occupant) - to_chat(usr, "The cryo cell is already occupied!") - return - if(M.abiotic()) - to_chat(usr, "Subject may not have abiotic items on.") - return - if(!node) - to_chat(usr, "The cell is not correctly connected to its pipe network!") - return - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.stop_pulling() - M.loc = src - M.ExtinguishMob() - if(M.health > -100 && (M.health < 0 || M.sleeping)) - to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") - occupant = M - buckle_mob(occupant, forced = TRUE, check_loc = FALSE) - vis_contents |= occupant - occupant.pixel_y += 19 - current_heat_capacity = HEAT_CAPACITY_HUMAN - update_use_power(USE_POWER_ACTIVE) -// M.metabslow = 1 - add_fingerprint(usr) - update_icon() - SStgui.update_uis(src) - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() - set name = "Eject occupant" - set category = "Object" - set src in oview(1) - if(usr == occupant)//If the user is inside the tube... - if(usr.stat == 2)//and he's not dead.... - return - to_chat(usr, "Release sequence activated. This will take two minutes.") - sleep(1200) - if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already - return - go_out()//and release him from the eternal prison. - else - if(usr.stat != 0) - return - go_out() - add_fingerprint(usr) - return - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() - set name = "Move Inside" - set category = "Object" - set src in oview(1) - if(isliving(usr)) - var/mob/living/L = usr - if(L.has_buckled_mobs()) - to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) - return - if(L.stat != CONSCIOUS) - return - put_mob(L) - -/atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform) - return return_air() - -/obj/machinery/atmospherics/unary/cryo_cell/return_air_for_internal_lifeform() - //assume that the cryo cell has some kind of breath mask or something that - //draws from the cryo tube's environment, instead of the cold internal air. - if(src.loc) - return loc.return_air() - else - return null - -/datum/data/function/proc/reset() - return - -/datum/data/function/proc/r_input(href, href_list, mob/user as mob) - return - -/datum/data/function/proc/display() - return +#define HEAT_CAPACITY_HUMAN 100 //249840 J/K, for a 72 kg person. + +/obj/machinery/atmospherics/unary/cryo_cell + name = "cryo cell" + desc = "Used to cool people down for medical reasons. Totally." + icon = 'icons/obj/cryogenics.dmi' // map only + icon_state = "pod_preview" + density = TRUE + anchored = TRUE + layer = UNDER_JUNK_LAYER + interact_offline = 1 + + var/on = 0 + use_power = USE_POWER_IDLE + idle_power_usage = 20 + active_power_usage = 200 + buckle_lying = FALSE + buckle_dir = SOUTH + clicksound = 'sound/machines/buttonbeep.ogg' + clickvol = 30 + + var/temperature_archived + var/mob/living/carbon/occupant = null + var/obj/item/weapon/reagent_containers/glass/beaker = null + + var/current_heat_capacity = 50 + + var/image/fluid + +/obj/machinery/atmospherics/unary/cryo_cell/New() + ..() + icon = 'icons/obj/cryogenics_split.dmi' + icon_state = "base" + initialize_directions = dir + +/obj/machinery/atmospherics/unary/cryo_cell/Initialize() + . = ..() + var/image/tank = image(icon,"tank") + tank.alpha = 200 + tank.pixel_y = 18 + tank.plane = MOB_PLANE + tank.layer = MOB_LAYER+0.2 //Above fluid + fluid = image(icon, "tube_filler") + fluid.pixel_y = 18 + fluid.alpha = 200 + fluid.plane = MOB_PLANE + fluid.layer = MOB_LAYER+0.1 //Below glass, above mob + add_overlay(tank) + update_icon() + +/obj/machinery/atmospherics/unary/cryo_cell/Destroy() + var/turf/T = src.loc + T.contents += contents + if(beaker) + beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube + beaker = null + . = ..() + +/obj/machinery/atmospherics/unary/cryo_cell/process() + ..() + if(!node) + return + if(!on) + return + + if(occupant) + if(occupant.stat != 2) + process_occupant() + + if(air_contents) + temperature_archived = air_contents.temperature + heat_gas_contents() + expel_gas() + + if(abs(temperature_archived-air_contents.temperature) > 1) + network.update = 1 + + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) + // note that relaymove will also be called for mobs outside the cell with UI open + if(occupant == user && !user.stat) + go_out() + +/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) + if(user == occupant) + return + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + + tgui_interact(user) + +/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Cryo", "Cryo Cell") // 520, 470 + ui.open() + +/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user) + // this is the data which will be sent to the ui + var/data[0] + data["isOperating"] = on + data["hasOccupant"] = occupant ? TRUE : FALSE + + var/occupantData[0] + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.getMaxHealth() + occupantData["minHealth"] = config.health_threshold_dead + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["bodyTemperature"] = occupant.bodytemperature + data["occupant"] = occupantData; + + data["cellTemperature"] = round(air_contents.temperature) + data["cellTemperatureStatus"] = "good" + if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) + data["cellTemperatureStatus"] = "bad" + else if(air_contents.temperature > 225) + data["cellTemperatureStatus"] = "average" + + data["isBeakerLoaded"] = beaker ? TRUE : FALSE + data["beakerLabel"] = null + data["beakerVolume"] = 0 + if(beaker) + data["beakerLabel"] = beaker.label_text ? beaker.label_text : null + if(beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + data["beakerVolume"] += R.volume + + return data + +/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params) + if(..() || usr == occupant) + return TRUE + + . = TRUE + switch(action) + if("switchOn") + on = 1 + update_icon() + if("switchOff") + on = 0 + update_icon() + if("ejectBeaker") + if(beaker) + beaker.loc = get_step(src.loc, SOUTH) + beaker = null + update_icon() + if("ejectOccupant") + if(!occupant || isslime(usr) || ispAI(usr)) + return 0 // don't update UIs attached to this object + go_out() + else + return FALSE + + add_fingerprint(usr) + +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) + if(istype(G, /obj/item/weapon/reagent_containers/glass)) + if(beaker) + to_chat(user, "A beaker is already loaded into the machine.") + return + + beaker = G + user.drop_item() + G.loc = src + user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") + SStgui.update_uis(src) + update_icon() + else if(istype(G, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/grab = G + if(!ismob(grab.affecting)) + return + if(occupant) + to_chat(user,"\The [src] is already occupied by [occupant].") + if(grab.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first.")) + return + var/mob/M = grab.affecting + qdel(grab) + put_mob(M) + + return + +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) + return + put_mob(target) + +/obj/machinery/atmospherics/unary/cryo_cell/update_icon() + cut_overlay(fluid) + fluid.color = null + if(on) + if(beaker) + fluid.color = beaker.reagents.get_color() + add_overlay(fluid) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() + if(air_contents.total_moles < 10) + return + if(occupant) + if(occupant.stat >= DEAD) + return + occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) + occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise + occupant.set_stat(UNCONSCIOUS) + occupant.dir = SOUTH + if(occupant.bodytemperature < T0C) + occupant.Sleeping(max(5, (1/occupant.bodytemperature)*2000)) + occupant.Paralyse(max(5, (1/occupant.bodytemperature)*3000)) + if(air_contents.gas["oxygen"] > 2) + if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1) + else + occupant.adjustOxyLoss(-1) + //severe damage should heal waaay slower without proper chemicals + if(occupant.bodytemperature < 225) + if(occupant.getToxLoss()) + occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) + if(occupant.radiation || occupant.accumulated_rads) + occupant.radiation -= 25 + occupant.accumulated_rads -= 25 + var/heal_brute = occupant.getBruteLoss() ? min(1, 20/occupant.getBruteLoss()) : 0 + var/heal_fire = occupant.getFireLoss() ? min(1, 20/occupant.getFireLoss()) : 0 + occupant.heal_organ_damage(heal_brute,heal_fire) + var/has_cryo = occupant.reagents.get_reagent_amount("cryoxadone") >= 1 + var/has_clonexa = occupant.reagents.get_reagent_amount("clonexadone") >= 1 + var/has_cryo_medicine = has_cryo || has_clonexa + if(beaker && !has_cryo_medicine) + beaker.reagents.trans_to_mob(occupant, 1, CHEM_BLOOD, 10) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() + if(air_contents.total_moles < 1) + return + var/air_heat_capacity = air_contents.heat_capacity() + var/combined_heat_capacity = current_heat_capacity + air_heat_capacity + if(combined_heat_capacity > 0) + var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature + air_contents.temperature = combined_energy/combined_heat_capacity + +/obj/machinery/atmospherics/unary/cryo_cell/proc/expel_gas() + if(air_contents.total_moles < 1) + return +// var/datum/gas_mixture/expel_gas = new +// var/remove_amount = air_contents.total_moles()/50 +// expel_gas = air_contents.remove(remove_amount) + + // Just have the gas disappear to nowhere. + //expel_gas.temperature = T20C // Lets expel hot gas and see if that helps people not die as they are removed + //loc.assume_air(expel_gas) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() + if(!(occupant)) + return + //for(var/obj/O in src) + // O.loc = src.loc + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + vis_contents -= occupant + occupant.pixel_x = occupant.default_pixel_x + occupant.pixel_y = occupant.default_pixel_y + occupant.loc = get_step(src.loc, SOUTH) //this doesn't account for walls or anything, but i don't forsee that being a problem. + if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected + occupant.bodytemperature = 261 // Changed to 70 from 140 by Zuhayr due to reoccurance of bug. + unbuckle_mob(occupant, force = TRUE) + occupant = null + current_heat_capacity = initial(current_heat_capacity) + update_use_power(USE_POWER_IDLE) + SStgui.update_uis(src) + return + +/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) + if(stat & (NOPOWER|BROKEN)) + to_chat(usr, "The cryo cell is not functioning.") + return + if(!istype(M)) + to_chat(usr, "The cryo cell cannot handle such a lifeform!") + return + if(occupant) + to_chat(usr, "The cryo cell is already occupied!") + return + if(M.abiotic()) + to_chat(usr, "Subject may not have abiotic items on.") + return + if(!node) + to_chat(usr, "The cell is not correctly connected to its pipe network!") + return + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.stop_pulling() + M.loc = src + M.ExtinguishMob() + if(M.health > -100 && (M.health < 0 || M.sleeping)) + to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") + occupant = M + buckle_mob(occupant, forced = TRUE, check_loc = FALSE) + vis_contents |= occupant + occupant.pixel_y += 19 + current_heat_capacity = HEAT_CAPACITY_HUMAN + update_use_power(USE_POWER_ACTIVE) +// M.metabslow = 1 + add_fingerprint(usr) + update_icon() + SStgui.update_uis(src) + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() + set name = "Eject occupant" + set category = "Object" + set src in oview(1) + if(usr == occupant)//If the user is inside the tube... + if(usr.stat == 2)//and he's not dead.... + return + to_chat(usr, "Release sequence activated. This will take two minutes.") + sleep(1200) + if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already + return + go_out()//and release him from the eternal prison. + else + if(usr.stat != 0) + return + go_out() + add_fingerprint(usr) + return + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() + set name = "Move Inside" + set category = "Object" + set src in oview(1) + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) + return + if(L.stat != CONSCIOUS) + return + put_mob(L) + +/atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform) + return return_air() + +/obj/machinery/atmospherics/unary/cryo_cell/return_air_for_internal_lifeform() + //assume that the cryo cell has some kind of breath mask or something that + //draws from the cryo tube's environment, instead of the cold internal air. + if(src.loc) + return loc.return_air() + else + return null + +/datum/data/function/proc/reset() + return + +/datum/data/function/proc/r_input(href, href_list, mob/user as mob) + return + +/datum/data/function/proc/display() + return diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index a72d814c791..54c1b38ca05 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -1,148 +1,148 @@ -/* -CONTAINS: -Deployable items -*/ - -/obj/machinery/deployable - name = "deployable" - desc = "deployable" - icon = 'icons/obj/objects.dmi' - req_access = list(access_security)//I'm changing this until these are properly tested./N - -/obj/machinery/deployable/barrier - name = "deployable barrier" - desc = "A deployable barrier. Swipe your ID card to lock/unlock it." - icon = 'icons/obj/objects.dmi' - anchored = FALSE - density = TRUE - icon_state = "barrier0" - var/health = 100.0 - var/maxhealth = 100.0 - var/locked = 0.0 -// req_access = list(access_maint_tunnels) - -/obj/machinery/deployable/barrier/New() - ..() - - icon_state = "barrier[locked]" - -/obj/machinery/deployable/barrier/attackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(istype(W, /obj/item/weapon/card/id/)) - if(allowed(user)) - if (emagged < 2.0) - locked = !locked - anchored = !anchored - icon_state = "barrier[locked]" - if((locked == 1.0) && (emagged < 2.0)) - to_chat(user, "Barrier lock toggled on.") - return - else if((locked == 0.0) && (emagged < 2.0)) - to_chat(user, "Barrier lock toggled off.") - return - else - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - visible_message("BZZzZZzZZzZT") - return - return - else if(W.is_wrench()) - if(health < maxhealth) - health = maxhealth - emagged = 0 - req_access = list(access_security) - visible_message("[user] repairs \the [src]!") - return - else if(emagged > 0) - emagged = 0 - req_access = list(access_security) - visible_message("[user] repairs \the [src]!") - return - return - else - switch(W.damtype) - if("fire") - health -= W.force * 0.75 - if("brute") - health -= W.force * 0.5 - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - CheckHealth() - ..() - -/obj/machinery/deployable/barrier/proc/CheckHealth() - if(health <= 0) - explode() - return - -/obj/machinery/deployable/barrier/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - user.do_attack_animation(src) - health -= damage - CheckHealth() - return - -/obj/machinery/deployable/barrier/take_damage(var/damage) - health -= damage - CheckHealth() - return - -/obj/machinery/deployable/barrier/ex_act(severity) - switch(severity) - if(1.0) - explode() - return - if(2.0) - health -= 25 - CheckHealth() - return - -/obj/machinery/deployable/barrier/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - if(prob(50/severity)) - locked = !locked - anchored = !anchored - icon_state = "barrier[locked]" - -/obj/machinery/deployable/barrier/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - return FALSE - -/obj/machinery/deployable/barrier/proc/explode() - - visible_message("[src] blows apart!") - var/turf/Tsec = get_turf(src) - -/* var/obj/item/stack/rods/ =*/ - new /obj/item/stack/rods(Tsec) - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - - explosion(src.loc,-1,-1,0) - if(src) - qdel(src) - -/obj/machinery/deployable/barrier/emag_act(var/remaining_charges, var/mob/user) - if(emagged == 0) - emagged = 1 - LAZYCLEARLIST(req_access) - LAZYCLEARLIST(req_one_access) - to_chat(user, "You break the ID authentication lock on \the [src].") - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - visible_message("BZZzZZzZZzZT") - return 1 - else if(emagged == 1) - emagged = 2 - to_chat(user, "You short out the anchoring mechanism on \the [src].") - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - visible_message("BZZzZZzZZzZT") +/* +CONTAINS: +Deployable items +*/ + +/obj/machinery/deployable + name = "deployable" + desc = "deployable" + icon = 'icons/obj/objects.dmi' + req_access = list(access_security)//I'm changing this until these are properly tested./N + +/obj/machinery/deployable/barrier + name = "deployable barrier" + desc = "A deployable barrier. Swipe your ID card to lock/unlock it." + icon = 'icons/obj/objects.dmi' + anchored = FALSE + density = TRUE + icon_state = "barrier0" + var/health = 100.0 + var/maxhealth = 100.0 + var/locked = 0.0 +// req_access = list(access_maint_tunnels) + +/obj/machinery/deployable/barrier/New() + ..() + + icon_state = "barrier[locked]" + +/obj/machinery/deployable/barrier/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(istype(W, /obj/item/weapon/card/id/)) + if(allowed(user)) + if (emagged < 2.0) + locked = !locked + anchored = !anchored + icon_state = "barrier[locked]" + if((locked == 1.0) && (emagged < 2.0)) + to_chat(user, "Barrier lock toggled on.") + return + else if((locked == 0.0) && (emagged < 2.0)) + to_chat(user, "Barrier lock toggled off.") + return + else + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + visible_message("BZZzZZzZZzZT") + return + return + else if(W.has_tool_quality(TOOL_WRENCH)) + if(health < maxhealth) + health = maxhealth + emagged = 0 + req_access = list(access_security) + visible_message("[user] repairs \the [src]!") + return + else if(emagged > 0) + emagged = 0 + req_access = list(access_security) + visible_message("[user] repairs \the [src]!") + return + return + else + switch(W.damtype) + if("fire") + health -= W.force * 0.75 + if("brute") + health -= W.force * 0.5 + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + CheckHealth() + ..() + +/obj/machinery/deployable/barrier/proc/CheckHealth() + if(health <= 0) + explode() + return + +/obj/machinery/deployable/barrier/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + health -= damage + CheckHealth() + return + +/obj/machinery/deployable/barrier/take_damage(var/damage) + health -= damage + CheckHealth() + return + +/obj/machinery/deployable/barrier/ex_act(severity) + switch(severity) + if(1.0) + explode() + return + if(2.0) + health -= 25 + CheckHealth() + return + +/obj/machinery/deployable/barrier/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(prob(50/severity)) + locked = !locked + anchored = !anchored + icon_state = "barrier[locked]" + +/obj/machinery/deployable/barrier/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + return FALSE + +/obj/machinery/deployable/barrier/proc/explode() + + visible_message("[src] blows apart!") + var/turf/Tsec = get_turf(src) + +/* var/obj/item/stack/rods/ =*/ + new /obj/item/stack/rods(Tsec) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + + explosion(src.loc,-1,-1,0) + if(src) + qdel(src) + +/obj/machinery/deployable/barrier/emag_act(var/remaining_charges, var/mob/user) + if(emagged == 0) + emagged = 1 + LAZYCLEARLIST(req_access) + LAZYCLEARLIST(req_one_access) + to_chat(user, "You break the ID authentication lock on \the [src].") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + visible_message("BZZzZZzZZzZT") + return 1 + else if(emagged == 1) + emagged = 2 + to_chat(user, "You short out the anchoring mechanism on \the [src].") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + visible_message("BZZzZZzZZzZT") return 1 \ No newline at end of file diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 6e5618d39e0..3d4c0382ce3 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -1,223 +1,223 @@ -/obj/machinery/button/remote - name = "remote object control" - desc = "It controls objects, remotely." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - power_channel = ENVIRON - layer = ABOVE_WINDOW_LAYER - var/desiredstate = 0 - var/exposedwires = 0 - var/wires = 3 - /* - Bitflag, 1=checkID - 2=Network Access - */ - - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/button/remote/attack_ai(mob/user as mob) - if(wires & 2) - return attack_hand(user) - else - to_chat(user, "Error, no route to host.") - -/obj/machinery/button/remote/attackby(obj/item/weapon/W, mob/user as mob) - return attack_hand(user) - -/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) - if(LAZYLEN(req_access) || LAZYLEN(req_one_access)) - LAZYCLEARLIST(req_access) - LAZYCLEARLIST(req_one_access) - playsound(src, "sparks", 100, 1) - return 1 - -/obj/machinery/button/remote/attack_hand(mob/user as mob) - if(..()) - return - - add_fingerprint(user) - if(stat & (NOPOWER|BROKEN)) - return - - if(!allowed(user) && (wires & 1)) - to_chat(user, "Access Denied") - flick("doorctrl-denied",src) - return - - use_power(5) - icon_state = "doorctrl1" - desiredstate = !desiredstate - trigger(user) - spawn(15) - update_icon() - -/obj/machinery/button/remote/proc/trigger() - return - -/obj/machinery/button/remote/power_change() - ..() - update_icon() - -/obj/machinery/button/remote/update_icon() - if(stat & NOPOWER) - icon_state = "doorctrl-p" - else - icon_state = "doorctrl0" - -/* - Airlock remote control -*/ - -// Bitmasks for door switches. -#define OPEN 0x1 -#define IDSCAN 0x2 -#define BOLTS 0x4 -#define SHOCK 0x8 -#define SAFE 0x10 - -/obj/machinery/button/remote/airlock - icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - name = "remote door-control" - desc = "It controls doors, remotely." - - var/specialfunctions = 1 - /* - Bitflag, 1= open - 2= idscan, - 4= bolts - 8= shock - 16= door safties - */ - -/obj/machinery/button/remote/airlock/trigger() - for(var/obj/machinery/door/airlock/D in machines) - if(D.id_tag == id) - if(specialfunctions & OPEN) - if(D.density) - spawn(0) - D.open() - return - else - spawn(0) - D.close() - return - if(desiredstate == 1) - if(specialfunctions & IDSCAN) - D.set_idscan(0) - if(specialfunctions & BOLTS) - D.lock() - if(specialfunctions & SHOCK) - D.electrify(-1) - if(specialfunctions & SAFE) - D.set_safeties(0) - else - if(specialfunctions & IDSCAN) - D.set_idscan(1) - if(specialfunctions & BOLTS) - D.unlock() - if(specialfunctions & SHOCK) - D.electrify(0) - if(specialfunctions & SAFE) - D.set_safeties(1) - -#undef OPEN -#undef IDSCAN -#undef BOLTS -#undef SHOCK -#undef SAFE - -/* - Blast door remote control -*/ -/obj/machinery/button/remote/blast_door - icon = 'icons/obj/stationobjs_vr.dmi' - name = "remote blast door-control" - desc = "It controls blast doors, remotely." - -/obj/machinery/button/remote/blast_door/trigger() - for(var/obj/machinery/door/blast/M in machines) - if(M.id == id) - if(M.density) - spawn(0) - M.open() - return - else - spawn(0) - M.close() - return - -/* - Emitter remote control -*/ -/obj/machinery/button/remote/emitter - name = "remote emitter control" - desc = "It controls emitters, remotely." - -/obj/machinery/button/remote/emitter/trigger(mob/user as mob) - for(var/obj/machinery/power/emitter/E in machines) - if(E.id == id) - spawn(0) - E.activate(user) - return - -/* - Mass driver remote control -*/ -/obj/machinery/button/remote/driver - name = "mass driver button" - desc = "A remote control switch for a mass driver." - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - -/obj/machinery/button/remote/driver/trigger(mob/user as mob) - active = 1 - update_icon() - - for(var/obj/machinery/door/blast/M in machines) - if(M.id == id) - spawn(0) - M.open() - return - - sleep(20) - - for(var/obj/machinery/mass_driver/M in machines) - if(M.id == id) - M.drive() - - sleep(50) - - for(var/obj/machinery/door/blast/M in machines) - if(M.id == id) - spawn(0) - M.close() - return - - icon_state = "launcherbtt" - update_icon() - - return - -/obj/machinery/button/remote/driver/update_icon() - if(!active || (stat & NOPOWER)) - icon_state = "launcherbtt" - else - icon_state = "launcheract" - -/* - Shieldgen remote control -*/ -/obj/machinery/button/remote/shields - name = "remote shield control" - desc = "It controls shields, remotely." - icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - -/obj/machinery/button/remote/shields/trigger(var/mob/user) - for(var/obj/machinery/shield_gen/SG in machines) - if(SG.id == id) - spawn(0) - if(SG?.anchored) - SG.toggle() +/obj/machinery/button/remote + name = "remote object control" + desc = "It controls objects, remotely." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + power_channel = ENVIRON + layer = ABOVE_WINDOW_LAYER + var/desiredstate = 0 + var/exposedwires = 0 + var/wires = 3 + /* + Bitflag, 1=checkID + 2=Network Access + */ + + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/button/remote/attack_ai(mob/user as mob) + if(wires & 2) + return attack_hand(user) + else + to_chat(user, "Error, no route to host.") + +/obj/machinery/button/remote/attackby(obj/item/weapon/W, mob/user as mob) + return attack_hand(user) + +/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) + if(LAZYLEN(req_access) || LAZYLEN(req_one_access)) + LAZYCLEARLIST(req_access) + LAZYCLEARLIST(req_one_access) + playsound(src, "sparks", 100, 1) + return 1 + +/obj/machinery/button/remote/attack_hand(mob/user as mob) + if(..()) + return + + add_fingerprint(user) + if(stat & (NOPOWER|BROKEN)) + return + + if(!allowed(user) && (wires & 1)) + to_chat(user, "Access Denied") + flick("doorctrl-denied",src) + return + + use_power(5) + icon_state = "doorctrl1" + desiredstate = !desiredstate + trigger(user) + spawn(15) + update_icon() + +/obj/machinery/button/remote/proc/trigger() + return + +/obj/machinery/button/remote/power_change() + ..() + update_icon() + +/obj/machinery/button/remote/update_icon() + if(stat & NOPOWER) + icon_state = "doorctrl-p" + else + icon_state = "doorctrl0" + +/* + Airlock remote control +*/ + +// Bitmasks for door switches. +#define OPEN 0x1 +#define IDSCAN 0x2 +#define BOLTS 0x4 +#define SHOCK 0x8 +#define SAFE 0x10 + +/obj/machinery/button/remote/airlock + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit + name = "remote door-control" + desc = "It controls doors, remotely." + + var/specialfunctions = 1 + /* + Bitflag, 1= open + 2= idscan, + 4= bolts + 8= shock + 16= door safties + */ + +/obj/machinery/button/remote/airlock/trigger() + for(var/obj/machinery/door/airlock/D in machines) + if(D.id_tag == id) + if(specialfunctions & OPEN) + if(D.density) + spawn(0) + D.open() + return + else + spawn(0) + D.close() + return + if(desiredstate == 1) + if(specialfunctions & IDSCAN) + D.set_idscan(0) + if(specialfunctions & BOLTS) + D.lock() + if(specialfunctions & SHOCK) + D.electrify(-1) + if(specialfunctions & SAFE) + D.set_safeties(0) + else + if(specialfunctions & IDSCAN) + D.set_idscan(1) + if(specialfunctions & BOLTS) + D.unlock() + if(specialfunctions & SHOCK) + D.electrify(0) + if(specialfunctions & SAFE) + D.set_safeties(1) + +#undef OPEN +#undef IDSCAN +#undef BOLTS +#undef SHOCK +#undef SAFE + +/* + Blast door remote control +*/ +/obj/machinery/button/remote/blast_door + icon = 'icons/obj/stationobjs_vr.dmi' + name = "remote blast door-control" + desc = "It controls blast doors, remotely." + +/obj/machinery/button/remote/blast_door/trigger() + for(var/obj/machinery/door/blast/M in machines) + if(M.id == id) + if(M.density) + spawn(0) + M.open() + return + else + spawn(0) + M.close() + return + +/* + Emitter remote control +*/ +/obj/machinery/button/remote/emitter + name = "remote emitter control" + desc = "It controls emitters, remotely." + +/obj/machinery/button/remote/emitter/trigger(mob/user as mob) + for(var/obj/machinery/power/emitter/E in machines) + if(E.id == id) + spawn(0) + E.activate(user) + return + +/* + Mass driver remote control +*/ +/obj/machinery/button/remote/driver + name = "mass driver button" + desc = "A remote control switch for a mass driver." + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + +/obj/machinery/button/remote/driver/trigger(mob/user as mob) + active = 1 + update_icon() + + for(var/obj/machinery/door/blast/M in machines) + if(M.id == id) + spawn(0) + M.open() + return + + sleep(20) + + for(var/obj/machinery/mass_driver/M in machines) + if(M.id == id) + M.drive() + + sleep(50) + + for(var/obj/machinery/door/blast/M in machines) + if(M.id == id) + spawn(0) + M.close() + return + + icon_state = "launcherbtt" + update_icon() + + return + +/obj/machinery/button/remote/driver/update_icon() + if(!active || (stat & NOPOWER)) + icon_state = "launcherbtt" + else + icon_state = "launcheract" + +/* + Shieldgen remote control +*/ +/obj/machinery/button/remote/shields + name = "remote shield control" + desc = "It controls shields, remotely." + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit + +/obj/machinery/button/remote/shields/trigger(var/mob/user) + for(var/obj/machinery/shield_gen/SG in machines) + if(SG.id == id) + spawn(0) + if(SG?.anchored) + SG.toggle() diff --git a/code/game/machinery/doorbell_vr.dm b/code/game/machinery/doorbell_vr.dm index 906c41ea6d2..fbcc58b79ef 100644 --- a/code/game/machinery/doorbell_vr.dm +++ b/code/game/machinery/doorbell_vr.dm @@ -136,7 +136,7 @@ var/obj/item/device/multitool/M = W M.connectable = src to_chat(user, "You save the data in \the [M]'s buffer.") - else if(W.is_wrench()) + else if(W.has_tool_quality(TOOL_WRENCH)) to_chat(user, "You start to unwrench \the [src].") playsound(src, 'sound/items/Ratchet.ogg', 50, 1) if(do_after(user, 15) && !QDELETED(src)) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index b155462d70b..c5680602ddc 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1,1582 +1,1582 @@ -//VOREStation Edit - Redone a lot of airlock things: -/* -- Specific department maintenance doors -- Named doors properly according to type -- Gave them default access levels with the access constants -- Improper'd all of the names in the new() -*/ - -/obj/machinery/door/airlock - name = "Airlock" - description_info = "If you hold left alt whilst left-clicking on an airlock, you can ring the doorbell to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!

    AIs and Cyborgs can also quickly open/close, bolt/unbolt, and electrify/de-electrify doors at a distance by holding left shift, left control, or left alt respectively whilst left-clicking." - icon = 'icons/obj/doors/Doorint.dmi' - icon_state = "door_closed" - power_channel = ENVIRON - - explosion_resistance = 10 - - // Doors do their own stuff - bullet_vulnerability = 0 - - blocks_emissive = EMISSIVE_BLOCK_GENERIC // Not quite as nice as /tg/'s custom masks. We should make those sometime - - var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. - var/hackProof = 0 // if 1, this door can't be hacked by the AI - var/electrified_until = 0 //World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. - var/main_power_lost_until = 0 //World time when main power is restored. - var/backup_power_lost_until = -1 //World time when backup power is restored. - var/has_beeped = 0 //If 1, will not beep on failed closing attempt. Resets when door closes. - var/spawnPowerRestoreRunning = 0 - var/welded = null - var/locked = 0 - var/lights = 1 // bolt lights show by default - var/aiDisabledIdScanner = 0 - var/aiHacking = 0 - var/obj/machinery/door/airlock/closeOther = null - var/closeOtherId = null - var/lockdownbyai = 0 - autoclose = 1 - var/assembly_type = /obj/structure/door_assembly - var/mineral = null - var/justzap = 0 - var/safe = 1 - normalspeed = 1 - var/obj/item/weapon/airlock_electronics/electronics = null - var/hasShocked = 0 //Prevents multiple shocks from happening - var/secured_wires = 0 - var/security_level = 1 //VOREStation Addition - acts as a multiplier on the time required to hack an airlock with a hacktool - var/datum/wires/airlock/wires = null - - var/open_sound_powered = 'sound/machines/door/covert1o.ogg' - var/open_sound_unpowered = 'sound/machines/door/airlockforced.ogg' - var/close_sound_powered = 'sound/machines/door/covert1c.ogg' - var/legacy_open_powered = 'sound/machines/door/old_airlock.ogg' - var/legacy_close_powered = 'sound/machines/door/old_airlockclose.ogg' - var/department_open_powered = null - var/department_close_powered = null - var/denied_sound = 'sound/machines/deniedbeep.ogg' - var/bolt_up_sound = 'sound/machines/door/boltsup.ogg' - var/bolt_down_sound = 'sound/machines/door/boltsdown.ogg' - var/knock_sound = 'sound/machines/2beeplow.ogg' - var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' - var/knock_unpowered_sound = 'sound/machines/door/knock_glass.ogg' - -/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage) - if(stat & (BROKEN|NOPOWER)) - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - if(src.locked || src.welded) - visible_message("\The [user] begins breaking into \the [src] internals!") - user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. - if(do_after(user,10 SECONDS,src)) - src.locked = 0 - src.welded = 0 - update_icon() - open(1) - if(prob(25)) - src.shock(user, 100) - user.set_AI_busy(FALSE) - else if(src.density) - visible_message("\The [user] forces \the [src] open!") - open(1) - else - visible_message("\The [user] forces \the [src] closed!") - close(1) - else - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/airlock/attack_alien(var/mob/user) //Familiar, right? Doors. -Mechoid - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - if(src.locked || src.welded) - visible_message("\The [user] begins tearing into \the [src] internals!") - src.do_animate("deny") - if(do_after(user,15 SECONDS,src)) - visible_message("\The [user] tears \the [src] open, sparks flying from its electronics!") - src.do_animate("spark") - playsound(src, 'sound/machines/door/airlock_tear_apart.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) - src.locked = 0 - src.welded = 0 - update_icon() - open(1) - src.set_broken() //These aren't emags, these be CLAWS - else if(src.density) - visible_message("\The [user] begins forcing \the [src] open!") - if(do_after(user, 5 SECONDS,src)) - playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) - visible_message("\The [user] forces \the [src] open!") - open(1) - else - visible_message("\The [user] forces \the [src] closed!") - close(1) - else - src.do_animate("deny") - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/airlock/get_material() - if(mineral) - return get_material_by_name(mineral) - return get_material_by_name(MAT_STEEL) - -/obj/machinery/door/airlock/command - name = "Command Airlock" - icon = 'icons/obj/doors/Doorcom.dmi' - req_one_access = list(access_heads) - assembly_type = /obj/structure/door_assembly/door_assembly_com - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cmd3o.ogg' - department_close_powered = 'sound/machines/door/cmd3c.ogg' - security_level = 3 //VOREStation Addition - -/obj/machinery/door/airlock/security - name = "Security Airlock" - icon = 'icons/obj/doors/Doorsec.dmi' - req_one_access = list(access_security) - assembly_type = /obj/structure/door_assembly/door_assembly_sec - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sec1o.ogg' - department_close_powered = 'sound/machines/door/sec1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/engineering - name = "Engineering Airlock" - icon = 'icons/obj/doors/Dooreng.dmi' - req_one_access = list(access_engine) - assembly_type = /obj/structure/door_assembly/door_assembly_eng - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/engineeringatmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Doorengatmos.dmi' - req_one_access = list(access_atmospherics) - assembly_type = /obj/structure/door_assembly/door_assembly_eat - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/medical - name = "Medical Airlock" - icon = 'icons/obj/doors/Doormed.dmi' - req_one_access = list(access_medical) - assembly_type = /obj/structure/door_assembly/door_assembly_med - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/med1o.ogg' - department_close_powered = 'sound/machines/door/med1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/maintenance - name = "Maintenance Access" - icon = 'icons/obj/doors/Doormaint.dmi' - //req_one_access = list(access_maint_tunnels) //VOREStation Edit - Maintenance is open access - assembly_type = /obj/structure/door_assembly/door_assembly_mai - open_sound_powered = 'sound/machines/door/door2o.ogg' - close_sound_powered = 'sound/machines/door/door2c.ogg' - -/obj/machinery/door/airlock/maintenance/cargo - icon = 'icons/obj/doors/Doormaint_cargo.dmi' - req_one_access = list(access_cargo) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/door2o.ogg' - department_close_powered = 'sound/machines/door/door2c.ogg' - -/obj/machinery/door/airlock/maintenance/command - icon = 'icons/obj/doors/Doormaint_command.dmi' - req_one_access = list(access_heads) - -/obj/machinery/door/airlock/maintenance/common - icon = 'icons/obj/doors/Doormaint_common.dmi' - open_sound_powered = 'sound/machines/door/hall3o.ogg' - close_sound_powered = 'sound/machines/door/hall3c.ogg' - -/obj/machinery/door/airlock/maintenance/engi - icon = 'icons/obj/doors/Doormaint_engi.dmi' - req_one_access = list(access_engine) - -/obj/machinery/door/airlock/maintenance/int - icon = 'icons/obj/doors/Doormaint_int.dmi' - -/obj/machinery/door/airlock/maintenance/medical - icon = 'icons/obj/doors/Doormaint_med.dmi' - req_one_access = list(access_medical) - -/obj/machinery/door/airlock/maintenance/rnd - icon = 'icons/obj/doors/Doormaint_rnd.dmi' - req_one_access = list(access_research) - -/obj/machinery/door/airlock/maintenance/sec - icon = 'icons/obj/doors/Doormaint_sec.dmi' - req_one_access = list(access_security) - -/obj/machinery/door/airlock/external - name = "External Airlock" - icon = 'icons/obj/doors/Doorext.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_ext - open_sound_powered = 'sound/machines/door/space1o.ogg' - close_sound_powered = 'sound/machines/door/space1c.ogg' - -/obj/machinery/door/airlock/external/bolted - icon_state = "door_locked" // So it looks visibly bolted in map editor - locked = 1 - -// For convenience in making docking ports: one that is pre-bolted with frequency set! -/obj/machinery/door/airlock/external/bolted/cycling - frequency = 1379 - -/obj/machinery/door/airlock/glass_external - name = "External Airlock" - icon = 'icons/obj/doors/Doorextglass.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_ext - opacity = 0 - glass = 1 - req_one_access = list(access_external_airlocks) - open_sound_powered = 'sound/machines/door/space1o.ogg' - close_sound_powered = 'sound/machines/door/space1c.ogg' - -/obj/machinery/door/airlock/glass - name = "Glass Airlock" - icon = 'icons/obj/doors/Doorglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - open_sound_powered = 'sound/machines/door/hall1o.ogg' - close_sound_powered = 'sound/machines/door/hall1c.ogg' - legacy_open_powered = 'sound/machines/door/windowdoor.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - glass = 1 - -/obj/machinery/door/airlock/centcom - name = "Centcom Airlock" - icon = 'icons/obj/doors/Doorele.dmi' - req_one_access = list(access_cent_general) - opacity = 1 - open_sound_powered = 'sound/machines/door/cmd3o.ogg' - close_sound_powered = 'sound/machines/door/cmd3c.ogg' - security_level = 100 //VOREStation Addition - -/obj/machinery/door/airlock/glass_centcom - name = "Airlock" - icon = 'icons/obj/doors/Dooreleglass.dmi' - opacity = 0 - glass = 1 - open_sound_powered = 'sound/machines/door/cmd3o.ogg' - close_sound_powered = 'sound/machines/door/cmd3c.ogg' - security_level = 100 //VOREStation Addition - -/obj/machinery/door/airlock/vault - name = "Vault" - icon = 'icons/obj/doors/vault.dmi' - explosion_resistance = 20 - opacity = 1 - secured_wires = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity //Until somebody makes better sprites. - req_one_access = list(access_heads_vault) - open_sound_powered = 'sound/machines/door/vault1o.ogg' - close_sound_powered = 'sound/machines/door/vault1c.ogg' - security_level = 5 //VOREStation Addition - -/obj/machinery/door/airlock/vault/bolted - icon_state = "door_locked" - locked = 1 - -/obj/machinery/door/airlock/freezer - name = "Freezer Airlock" - icon = 'icons/obj/doors/Doorfreezer.dmi' - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_fre - -/obj/machinery/door/airlock/hatch - name = "Airtight Hatch" - icon = 'icons/obj/doors/Doorhatchele.dmi' - explosion_resistance = 20 - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_hatch - req_one_access = list(access_maint_tunnels) - open_sound_powered = 'sound/machines/door/hatchopen.ogg' - close_sound_powered = 'sound/machines/door/hatchclose.ogg' - open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' - -/obj/machinery/door/airlock/maintenance_hatch - name = "Maintenance Hatch" - icon = 'icons/obj/doors/Doorhatchmaint2.dmi' - explosion_resistance = 20 - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_mhatch - req_one_access = list(access_maint_tunnels) - open_sound_powered = 'sound/machines/door/hatchopen.ogg' - close_sound_powered = 'sound/machines/door/hatchclose.ogg' - open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' - -/obj/machinery/door/airlock/glass_command - name = "Command Airlock" - icon = 'icons/obj/doors/Doorcomglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_com - glass = 1 - req_one_access = list(access_heads) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cmd1o.ogg' - department_close_powered = 'sound/machines/door/cmd1c.ogg' - security_level = 3 //VOREStation Addition - -/obj/machinery/door/airlock/glass_engineering - name = "Engineering Airlock" - icon = 'icons/obj/doors/Doorengglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_eng - glass = 1 - req_one_access = list(access_engine) - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/glass_engineeringatmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Doorengatmoglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_eat - glass = 1 - req_one_access = list(access_atmospherics) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/glass_security - name = "Security Airlock" - icon = 'icons/obj/doors/Doorsecglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_sec - glass = 1 - req_one_access = list(access_security) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sec1o.ogg' - department_close_powered = 'sound/machines/door/sec1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/glass_medical - name = "Medical Airlock" - icon = 'icons/obj/doors/Doormedglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_med - glass = 1 - req_one_access = list(access_medical) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/med1o.ogg' - department_close_powered = 'sound/machines/door/med1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/mining - name = "Mining Airlock" - icon = 'icons/obj/doors/Doormining.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_min - req_one_access = list(access_mining) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cgo1o.ogg' - department_close_powered = 'sound/machines/door/cgo1c.ogg' - -/obj/machinery/door/airlock/atmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Dooratmo.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_atmo - req_one_access = list(access_atmospherics) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/research - name = "Research Airlock" - icon = 'icons/obj/doors/Doorresearch.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_research - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/glass_research - name = "Research Airlock" - icon = 'icons/obj/doors/Doorresearchglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_research - glass = 1 - req_one_access = list(access_research) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 2 //VOREStation Addition - -/obj/machinery/door/airlock/glass_mining - name = "Mining Airlock" - icon = 'icons/obj/doors/Doorminingglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_min - glass = 1 - req_one_access = list(access_mining) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/cgo1o.ogg' - department_close_powered = 'sound/machines/door/cgo1c.ogg' - -/obj/machinery/door/airlock/glass_atmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Dooratmoglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_atmo - glass = 1 - req_one_access = list(access_atmospherics) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/eng1o.ogg' - department_close_powered = 'sound/machines/door/eng1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/gold - name = "Gold Airlock" - icon = 'icons/obj/doors/Doorgold.dmi' - mineral = "gold" - -/obj/machinery/door/airlock/silver - name = "Silver Airlock" - icon = 'icons/obj/doors/Doorsilver.dmi' - mineral = "silver" - -/obj/machinery/door/airlock/diamond - name = "Diamond Airlock" - icon = 'icons/obj/doors/Doordiamond.dmi' - mineral = "diamond" - -/obj/machinery/door/airlock/uranium - name = "Uranium Airlock" - desc = "And they said I was crazy." - icon = 'icons/obj/doors/Dooruranium.dmi' - mineral = "uranium" - var/last_event = 0 - var/rad_power = 7.5 - -/obj/machinery/door/airlock/process() - // Deliberate no call to parent. - if(main_power_lost_until > 0 && world.time >= main_power_lost_until) - regainMainPower() - - if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until) - regainBackupPower() - - else if(electrified_until > 0 && world.time >= electrified_until) - electrify(0) - - if (..() == PROCESS_KILL && !(main_power_lost_until > 0 || backup_power_lost_until > 0 || electrified_until > 0)) - . = PROCESS_KILL - -/obj/machinery/door/airlock/uranium/process() - if(world.time > last_event+20) - if(prob(50)) - SSradiation.radiate(src, rad_power) - last_event = world.time - ..() - -/obj/machinery/door/airlock/phoron - name = "Phoron Airlock" - desc = "No way this can end badly." - icon = 'icons/obj/doors/Doorphoron.dmi' - mineral = "phoron" - -/obj/machinery/door/airlock/phoron/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > 300) - PhoronBurn(exposed_temperature) - -/obj/machinery/door/airlock/phoron/proc/ignite(exposed_temperature) - if(exposed_temperature > 300) - PhoronBurn(exposed_temperature) - -/obj/machinery/door/airlock/phoron/proc/PhoronBurn(temperature) - for(var/turf/simulated/floor/target_tile in range(2,loc)) - target_tile.assume_gas("phoron", 35, 400+T0C) - spawn (0) target_tile.hotspot_expose(temperature, 400) - for(var/turf/simulated/wall/W in range(3,src)) - W.burn((temperature/4))//Added so that you can't set off a massive chain reaction with a small flame - for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) - D.ignite(temperature/4) - new/obj/structure/door_assembly( src.loc ) - qdel(src) - -/obj/machinery/door/airlock/sandstone - name = "Sandstone Airlock" - icon = 'icons/obj/doors/Doorsand.dmi' - mineral = "sandstone" - -/obj/machinery/door/airlock/science - name = "Research Airlock" - icon = 'icons/obj/doors/Doorsci.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_science - req_one_access = list(access_research) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/glass_science - name = "Glass Airlocks" - icon = 'icons/obj/doors/Doorsciglass.dmi' - opacity = 0 - assembly_type = /obj/structure/door_assembly/door_assembly_science - glass = 1 - req_one_access = list(access_research) - open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. - department_open_powered = 'sound/machines/door/sci1o.ogg' - department_close_powered = 'sound/machines/door/sci1c.ogg' - security_level = 1.5 //VOREStation Addition - -/obj/machinery/door/airlock/highsecurity - name = "Secure Airlock" - icon = 'icons/obj/doors/hightechsecurity.dmi' - explosion_resistance = 20 - secured_wires = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity - req_one_access = list(access_heads_vault) - open_sound_powered = 'sound/machines/door/secure1o.ogg' - close_sound_powered = 'sound/machines/door/secure1c.ogg' - security_level = 4 //VOREStation Addition - -/obj/machinery/door/airlock/voidcraft - name = "voidcraft hatch" - desc = "It's an extra resilient airlock intended for spacefaring vessels." - icon = 'icons/obj/doors/shuttledoors.dmi' - explosion_resistance = 20 - opacity = 0 - glass = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft - open_sound_powered = 'sound/machines/door/shuttle1o.ogg' - close_sound_powered = 'sound/machines/door/shuttle1c.ogg' - -// Airlock opens from top-bottom instead of left-right. -/obj/machinery/door/airlock/voidcraft/vertical - icon = 'icons/obj/doors/shuttledoors_vertical.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft/vertical - open_sound_powered = 'sound/machines/door/shuttle1o.ogg' - close_sound_powered = 'sound/machines/door/shuttle1c.ogg' - - -/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock - name = "Precursor Alpha Object - Doors" - desc = "This object appears to be used in order to restrict or allow access to \ - rooms based on its physical state. In other words, a door. \ - Despite being designed and created by unknown ancient alien hands, this door has \ - a large number of similarities to the conventional airlock, such as being driven by \ - electricity, opening and closing by physically moving, and being air tight. \ - It also operates by responding to signals through internal electrical conduits. \ - These characteristics make it possible for one with experience with a multitool \ - to manipulate the door.\ -

    \ - The symbol on the door does not match any living species' patterns, giving further \ - implications that this door is very old, and yet it remains operational after \ - thousands of years. It is unknown if that is due to superb construction, or \ - unseen autonomous maintenance having been performed." - value = CATALOGUER_REWARD_EASY - -/obj/machinery/door/airlock/alien - name = "alien airlock" - desc = "You're fairly sure this is a door." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock) - icon = 'icons/obj/doors/Dooralien.dmi' - explosion_resistance = 20 - secured_wires = TRUE - hackProof = TRUE - assembly_type = /obj/structure/door_assembly/door_assembly_alien - req_one_access = list(access_alien) - security_level = 100 //VOREStation Addition - -/obj/machinery/door/airlock/alien/locked - icon_state = "door_locked" - locked = TRUE - -/obj/machinery/door/airlock/alien/public // Entry to UFO. - req_one_access = list() - normalspeed = FALSE // So it closes faster and hopefully keeps the warm air inside. - hackProof = TRUE //VOREStation Edit - No borgos - -/* -About the new airlock wires panel: -* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. -* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) -* two wires for power. Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be open, but bolts-raising will not work. Cutting these wires may electrocute the user. -* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. -* two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. -* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. -* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. -* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) -* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. -* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. -*/ - - - -/obj/machinery/door/airlock/bumpopen(mob/living/user as mob) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite - if(!issilicon(usr)) - if(src.isElectrified()) - if(!src.justzap) - if(src.shock(user, 100)) - src.justzap = 1 - spawn (10) - src.justzap = 0 - return - else /*if(src.justzap)*/ - return - else if(user.hallucination > 50 && prob(10) && src.operating == 0) - to_chat(user, "You feel a powerful shock course through your body!") - user.halloss += 10 - user.stunned += 10 - return - ..(user) - -/obj/machinery/door/airlock/proc/isElectrified() - if(src.electrified_until != 0) - return 1 - return 0 - -/obj/machinery/door/airlock/proc/canAIControl() - return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); - -/obj/machinery/door/airlock/proc/canAIHack() - return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); - -/obj/machinery/door/airlock/proc/arePowerSystemsOn() - if (stat & (NOPOWER|BROKEN)) - return 0 - return (src.main_power_lost_until==0 || src.backup_power_lost_until==0) - -/obj/machinery/door/airlock/requiresID() - return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner) - -/obj/machinery/door/airlock/proc/isAllPowerLoss() - if(stat & (NOPOWER|BROKEN)) - return 1 - if(mainPowerCablesCut() && backupPowerCablesCut()) - return 1 - return 0 - -/obj/machinery/door/airlock/proc/mainPowerCablesCut() - return wires.is_cut(WIRE_MAIN_POWER1) || wires.is_cut(WIRE_MAIN_POWER2) - -/obj/machinery/door/airlock/proc/backupPowerCablesCut() - return wires.is_cut(WIRE_BACKUP_POWER1) || wires.is_cut(WIRE_BACKUP_POWER2) - -/obj/machinery/door/airlock/proc/loseMainPower() - main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) - - // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running - if(backup_power_lost_until == -1 && !backupPowerCablesCut()) - backup_power_lost_until = world.time + SecondsToTicks(10) - - if(main_power_lost_until > 0 || backup_power_lost_until > 0) - START_MACHINE_PROCESSING(src) - - // Disable electricity if required - if(electrified_until && isAllPowerLoss()) - electrify(0) - - update_icon() - -/obj/machinery/door/airlock/proc/loseBackupPower() - backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) - - if(backup_power_lost_until > 0) - START_MACHINE_PROCESSING(src) - - // Disable electricity if required - if(electrified_until && isAllPowerLoss()) - electrify(0) - - update_icon() - -/obj/machinery/door/airlock/proc/regainMainPower() - if(!mainPowerCablesCut()) - main_power_lost_until = 0 - // If backup power is currently active then disable, otherwise let it count down and disable itself later - if(!backup_power_lost_until) - backup_power_lost_until = -1 - - update_icon() - -/obj/machinery/door/airlock/proc/regainBackupPower() - if(!backupPowerCablesCut()) - // Restore backup power only if main power is offline, otherwise permanently disable - backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 - - update_icon() - -/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) - var/message = "" - if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn()) - message = text("The electrification wire is cut - Door permanently electrified.") - src.electrified_until = -1 - else if(duration && !arePowerSystemsOn()) - message = text("The door is unpowered - Cannot electrify the door.") - src.electrified_until = 0 - else if(!duration && electrified_until != 0) - message = "The door is now un-electrified." - src.electrified_until = 0 - else if(duration) //electrify door for the given duration seconds - if(usr) - shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") - add_attack_logs(usr,name,"Electrified a door") - else - shockedby += text("\[[time_stamp()]\] - EMP)") - message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." - src.electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) - - if(electrified_until > 0) - START_MACHINE_PROCESSING(src) - - if(feedback && message) - to_chat(usr,message) - -/obj/machinery/door/airlock/proc/set_idscan(var/activate, var/feedback = 0) - var/message = "" - if(wires.is_cut(WIRE_IDSCAN)) - message = "The IdScan wire is cut - IdScan feature permanently disabled." - else if(activate && src.aiDisabledIdScanner) - src.aiDisabledIdScanner = 0 - message = "IdScan feature has been enabled." - else if(!activate && !src.aiDisabledIdScanner) - src.aiDisabledIdScanner = 1 - message = "IdScan feature has been disabled." - - if(feedback && message) - to_chat(usr,message) - -/obj/machinery/door/airlock/proc/set_safeties(var/activate, var/feedback = 0) - var/message = "" - // Safeties! We don't need no stinking safeties! - if (wires.is_cut(WIRE_SAFETY)) - message = text("The safety wire is cut - Cannot enable safeties.") - else if (!activate && src.safe) - safe = 0 - else if (activate && !src.safe) - safe = 1 - - if(feedback && message) - to_chat(usr,message) - -// shock user with probability prb (if all connections & power are working) -// returns 1 if shocked, 0 otherwise -// The preceding comment was borrowed from the grille's shock script -/obj/machinery/door/airlock/shock(mob/user, prb) - if(!arePowerSystemsOn()) - return 0 - if(hasShocked) - return 0 //Already shocked someone recently? - if(..()) - hasShocked = 1 - sleep(10) - hasShocked = 0 - return 1 - else - return 0 - - -/obj/machinery/door/airlock/update_icon() - cut_overlays() - if(density) - if(locked && lights && src.arePowerSystemsOn()) - icon_state = "door_locked" - else - icon_state = "door_closed" - if(p_open || welded) - if(p_open) - add_overlay("panel_open") - if (!(stat & NOPOWER)) - if(stat & BROKEN) - add_overlay("sparks_broken") - else if (health < maxhealth * 3/4) - add_overlay("sparks_damaged") - if(welded) - add_overlay("welded") - else if (health < maxhealth * 3/4 && !(stat & NOPOWER)) - add_overlay("sparks_damaged") - else - icon_state = "door_open" - if((stat & BROKEN) && !(stat & NOPOWER)) - add_overlay("sparks_open") - return - -/obj/machinery/door/airlock/do_animate(animation) - switch(animation) - if("opening") - cut_overlay() - if(p_open) - flick("o_door_opening", src) //can not use flick due to BYOND bug updating overlays right before flicking - update_icon() - else - flick("door_opening", src)//[stat ? "_stat":] - update_icon() - if("closing") - cut_overlay() - if(p_open) - flick("o_door_closing", src) - update_icon() - else - flick("door_closing", src) - update_icon() - if("spark") - if(density) - flick("door_spark", src) - if("deny") - if(density && src.arePowerSystemsOn()) - flick("door_deny", src) - playsound(src, denied_sound, 50, 0, 3) - return - -/obj/machinery/door/airlock/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/door/airlock/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/door/airlock/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AiAirlock", name) - ui.open() - if(custom_state) - ui.set_state(custom_state) - return TRUE - -/obj/machinery/door/airlock/tgui_data(mob/user) - var/list/data = list() - - var/list/power = list() - power["main"] = main_power_lost_until > 0 ? 0 : 2 - power["main_timeleft"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) - power["backup"] = backup_power_lost_until > 0 ? 0 : 2 - power["backup_timeleft"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) - data["power"] = power - - data["shock"] = (electrified_until == 0) ? 2 : 0 - data["shock_timeleft"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) - data["id_scanner"] = !aiDisabledIdScanner - data["locked"] = locked // bolted - data["lights"] = lights // bolt lights - data["safe"] = safe // safeties - data["speed"] = normalspeed // safe speed - data["welded"] = welded // welded - data["opened"] = !density // opened - - var/list/wire = list() - wire["main_1"] = !wires.is_cut(WIRE_MAIN_POWER1) - wire["main_2"] = !wires.is_cut(WIRE_MAIN_POWER2) - wire["backup_1"] = !wires.is_cut(WIRE_BACKUP_POWER1) - wire["backup_2"] = !wires.is_cut(WIRE_BACKUP_POWER2) - wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY) - wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN) - wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS) - wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT) - wire["safe"] = !wires.is_cut(WIRE_SAFETY) - wire["timing"] = !wires.is_cut(WIRE_SPEED) - - data["wires"] = wire - return data - -/obj/machinery/door/airlock/proc/hack(mob/user as mob) - if(src.aiHacking==0) - src.aiHacking=1 - spawn(20) - //TODO: Make this take a minute - to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.") - sleep(50) - if(src.canAIControl()) - to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") - src.aiHacking=0 - return - else if(!src.canAIHack(user)) - to_chat(user, "We've lost our connection! Unable to hack airlock.") - src.aiHacking=0 - return - to_chat(user, "Fault confirmed: airlock control wire disabled or cut.") - sleep(20) - to_chat(user, "Attempting to hack into airlock. This may take some time.") - sleep(200) - if(src.canAIControl()) - to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") - src.aiHacking=0 - return - else if(!src.canAIHack(user)) - to_chat(user, "We've lost our connection! Unable to hack airlock.") - src.aiHacking=0 - return - to_chat(user, "Upload access confirmed. Loading control program into airlock software.") - sleep(170) - if(src.canAIControl()) - to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") - src.aiHacking=0 - return - else if(!src.canAIHack(user)) - to_chat(user, "We've lost our connection! Unable to hack airlock.") - src.aiHacking=0 - return - to_chat(user, "Transfer complete. Forcing airlock to execute program.") - sleep(50) - //disable blocked control - src.aiControlDisabled = 2 - to_chat(user, "Receiving control information from airlock.") - sleep(10) - //bring up airlock dialog - src.aiHacking = 0 - if (user) - src.attack_ai(user) - -/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target) - if (src.isElectrified()) - if (istype(mover, /obj/item)) - var/obj/item/i = mover - if (i.matter && (MAT_STEEL in i.matter) && i.matter[MAT_STEEL] > 0) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - return ..() - -/obj/machinery/door/airlock/attack_hand(mob/user as mob) - if(!istype(usr, /mob/living/silicon)) - if(src.isElectrified()) - if(src.shock(user, 100)) - return - - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - src.attack_alien(user) - return - - if(src.p_open) - user.set_machine(src) - wires.Interact(user) - else - ..(user) - return - -/obj/machinery/door/airlock/AltClick(mob/user as mob) - . = ..() - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(!Adjacent(user)) - return - else if(user.a_intent == I_HURT) - src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") - src.add_fingerprint(user) - if(icon_state == "door_closed" && arePowerSystemsOn()) - flick("door_deny", src) - playsound(src, knock_hammer_sound, 50, 0, 3) - else if(arePowerSystemsOn() && user.a_intent == I_HELP) - src.visible_message("[user] presses the door bell on \the [src].", "\The [src]'s bell rings.") - src.add_fingerprint(user) - if(icon_state == "door_closed") - flick("door_deny", src) - playsound(src, knock_sound, 50, 0, 3) - else if(user.a_intent == I_HELP) - src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") - src.add_fingerprint(user) - playsound(src, knock_unpowered_sound, 50, 0, 3) - return - -/obj/machinery/door/airlock/tgui_act(action, params) - if(..()) - return TRUE - if(!user_allowed(usr)) - return TRUE - - switch(action) - if("disrupt-main") - if(!main_power_lost_until) - loseMainPower() - update_icon() - else - to_chat(usr, "Main power is already offline.") - . = TRUE - if("disrupt-backup") - if(!backup_power_lost_until) - loseBackupPower() - update_icon() - else - to_chat(usr, "Backup power is already offline.") - . = TRUE - if("shock-restore") - electrify(0, 1) - . = TRUE - if("shock-temp") - electrify(30, 1) - . = TRUE - if("shock-perm") - electrify(-1, 1) - . = TRUE - if("idscan-toggle") - set_idscan(aiDisabledIdScanner, 1) - . = TRUE - // if("emergency-toggle") - // toggle_emergency(usr) - // . = TRUE - if("bolt-toggle") - toggle_bolt(usr) - . = TRUE - if("light-toggle") - if(wires.is_cut(WIRE_BOLT_LIGHT)) - to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") - return - lights = !lights - update_icon() - . = TRUE - if("safe-toggle") - set_safeties(!safe, 1) - . = TRUE - if("speed-toggle") - if(wires.is_cut(WIRE_SPEED)) - to_chat(usr, "The timing wire is cut - Cannot alter timing.") - return - normalspeed = !normalspeed - . = TRUE - if("open-close") - user_toggle_open(usr) - . = TRUE - - update_icon() - return 1 - -/obj/machinery/door/airlock/proc/user_allowed(mob/user) - var/allowed = (issilicon(user) && canAIControl(user)) - if(!allowed && isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - allowed = TRUE - return allowed - -/obj/machinery/door/airlock/proc/toggle_bolt(mob/user) - if(!user_allowed(user)) - return - if(wires.is_cut(WIRE_DOOR_BOLTS)) - to_chat(user, "The door bolt drop wire is cut - you can't toggle the door bolts.") - return - if(locked) - if(!arePowerSystemsOn()) - to_chat(user, "The door has no power - you can't raise the door bolts.") - else - unlock() - to_chat(user, "The door bolts have been raised.") - // log_combat(user, src, "unbolted") - else - lock() - to_chat(user, "The door bolts have been dropped.") - // log_combat(user, src, "bolted") - -/obj/machinery/door/airlock/proc/user_toggle_open(mob/user) - if(!user_allowed(user)) - return - if(welded) - to_chat(user, text("The airlock has been welded shut!")) - else if(locked) - to_chat(user, text("The door bolts are down!")) - else if(!density) - close() - else - open() - -/obj/machinery/door/airlock/proc/can_remove_electronics() - return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) - -/obj/machinery/door/airlock/attackby(obj/item/C, mob/user as mob) - //to_world("airlock attackby src [src] obj [C] mob [user]") - if(!istype(usr, /mob/living/silicon)) - if(src.isElectrified()) - if(src.shock(user, 75)) - return - if(istype(C, /obj/item/taperoll)) - return - - src.add_fingerprint(user) - if (attempt_vr(src,"attackby_vr",list(C, user))) return - if(istype(C, /mob/living)) - ..() - return - if(!repairing && istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density) - var/obj/item/weapon/weldingtool/W = C - if(W.remove_fuel(0,user)) - if(!src.welded) - src.welded = 1 - else - src.welded = null - playsound(src, C.usesound, 75, 1) - src.update_icon() - return - else - return - else if(C.is_screwdriver()) - if (src.p_open) - if (stat & BROKEN) - to_chat(usr, "The panel is broken and cannot be closed.") - else - src.p_open = FALSE - playsound(src, C.usesound, 50, 1) - src.update_icon() - return - else - src.p_open = TRUE - playsound(src, C.usesound, 50, 1) - src.update_icon() - return src.attack_hand(user) - else if(C.is_wirecutter()) - return src.attack_hand(user) - else if(istype(C, /obj/item/device/multitool)) - return src.attack_hand(user) - else if(istype(C, /obj/item/device/assembly/signaler)) - return src.attack_hand(user) - else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE - var/obj/item/weapon/pai_cable/cable = C - cable.plugin(src, user) - else if(!repairing && C.is_crowbar()) - if(can_remove_electronics()) - playsound(src, C.usesound, 75, 1) - user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") - if(do_after(user,40 * C.toolspeed)) - to_chat(user, "You removed the airlock electronics!") - - var/obj/structure/door_assembly/da = new assembly_type(src.loc) - if (istype(da, /obj/structure/door_assembly/multi_tile)) - da.set_dir(src.dir) - - da.anchored = TRUE - if(mineral) - da.glass = mineral - //else if(glass) - else if(glass && !da.glass) - da.glass = 1 - da.state = 1 - da.created_name = src.name - da.update_state() - - if(operating == -1 || (stat & BROKEN)) - new /obj/item/weapon/circuitboard/broken(src.loc) - operating = 0 - else - if (!electronics) create_electronics() - - electronics.loc = src.loc - electronics = null - - qdel(src) - return - else if(arePowerSystemsOn()) - to_chat(user, "The airlock's motors resist your efforts to force it.") - else if(locked) - to_chat(user, "The airlock's bolts prevent it from being forced.") - else - if(density) - spawn(0) open(1) - else - spawn(0) close(1) - - // Check if we're using a crowbar or armblade, and if the airlock's unpowered for whatever reason (off, broken, etc). - else if(istype(C, /obj/item/weapon)) - var/obj/item/weapon/W = C - if((W.pry == 1) && !arePowerSystemsOn()) - if(locked) - to_chat(user, "The airlock's bolts prevent it from being forced.") - else if( !welded && !operating ) - if(istype(C, /obj/item/weapon/material/twohanded/fireaxe)) // If this is a fireaxe, make sure it's held in two hands. - var/obj/item/weapon/material/twohanded/fireaxe/F = C - if(!F.wielded) - to_chat(user, "You need to be wielding \the [F] to do that.") - return - // At this point, it's an armblade or a fireaxe that passed the wielded test, let's try to open it. - if(density) - spawn(0) - open(1) - else - spawn(0) - close(1) - else - ..() - else - ..() - return - -/obj/machinery/door/airlock/phoron/attackby(C as obj, mob/user as mob) - if(C) - ignite(is_hot(C)) - ..() - -/obj/machinery/door/airlock/set_broken() - src.p_open = TRUE - stat |= BROKEN - if (secured_wires) - lock() - for (var/mob/O in viewers(src, null)) - if ((O.client && !( O.blinded ))) - O.show_message("[src.name]'s control panel bursts open, sparks spewing out!") - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - - update_icon() - return - -/obj/machinery/door/airlock/open(var/forced=0) - if(!can_open(forced)) - return 0 - use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people - - //if the door is unpowered then it doesn't make sense to hear the woosh of a pneumatic actuator - for(var/mob/M as anything in player_list) - if(!M || !M.client) - continue - var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) - var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) - var/sound - var/volume - if(old_sounds) // Do we have old sounds enabled? Play these even if we have department door sounds enabled. - if(arePowerSystemsOn()) - sound = legacy_open_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else if(!old_sounds && department_door_sounds && src.department_open_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. - if(arePowerSystemsOn()) - sound = department_open_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else // Else, play these. - if(arePowerSystemsOn()) - sound = open_sound_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - - var/turf/T = get_turf(M) - var/distance = get_dist(T, get_turf(src)) - if(distance <= world.view * 2) - if(T && T.z == get_z(src)) - M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) - - if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) - src.closeOther.close() - return ..() - -/obj/machinery/door/airlock/can_open(var/forced=0) - if(!forced) - if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) - return 0 - - if(locked || welded) - return 0 - return ..() - -/obj/machinery/door/airlock/can_close(var/forced=0) - if(locked || welded) - return 0 - - if(!forced) - //despite the name, this wire is for general door control. - if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) - return 0 - - return ..() - -/atom/movable/proc/blocks_airlock() - return density - -/obj/machinery/door/blocks_airlock() - return 0 - -/obj/machinery/mech_sensor/blocks_airlock() - return 0 - -/mob/living/blocks_airlock() - return 1 - -/atom/movable/proc/airlock_crush(var/crush_damage) - return 0 - -/obj/machinery/portable_atmospherics/canister/airlock_crush(var/crush_damage) - . = ..() - health -= crush_damage - healthcheck() - -/obj/effect/energy_field/airlock_crush(var/crush_damage) - adjust_strength(crush_damage) - -/obj/structure/closet/airlock_crush(var/crush_damage) - ..() - damage(crush_damage) - for(var/atom/movable/AM in src) - AM.airlock_crush() - return 1 - -/mob/living/airlock_crush(var/crush_damage) - . = ..() - adjustBruteLoss(crush_damage) - SetStunned(5) - SetWeakened(5) - var/turf/T = get_turf(src) - T.add_blood(src) - return 1 - -/mob/living/carbon/airlock_crush(var/crush_damage) - . = ..() - if(can_feel_pain()) - emote("scream") - -/mob/living/silicon/robot/airlock_crush(var/crush_damage) - adjustBruteLoss(crush_damage) - return 0 - -/obj/machinery/door/airlock/close(var/forced=0) - if(!can_close(forced)) - return 0 - - if(safe) - for(var/turf/turf in locs) - for(var/atom/movable/AM in turf) - if(AM.blocks_airlock()) - if(!has_beeped) - playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) - has_beeped = 1 - autoclose_in(6) - return - - for(var/turf/turf in locs) - for(var/atom/movable/AM in turf) - if(AM.airlock_crush(DOOR_CRUSH_DAMAGE)) - take_damage(DOOR_CRUSH_DAMAGE) - - use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people - has_beeped = 0 - for(var/mob/M as anything in player_list) - if(!M || !M.client) - continue - var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) - var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) - var/sound - var/volume - if(old_sounds) - if(arePowerSystemsOn()) - sound = legacy_close_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else if(!old_sounds && department_door_sounds && src.department_close_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. - if(arePowerSystemsOn()) - sound = department_close_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - else - if(arePowerSystemsOn()) - sound = close_sound_powered - volume = 50 - else - sound = open_sound_unpowered - volume = 75 - - var/turf/T = get_turf(M) - var/distance = get_dist(T, get_turf(src)) - if(distance <= world.view * 2) - if(T && T.z == get_z(src)) - M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) - for(var/turf/turf in locs) - var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) - if(killthis) - killthis.ex_act(2)//Smashin windows - return ..() - -/obj/machinery/door/airlock/proc/lock(var/forced=0) - if(locked) - return 0 - - if (operating && !forced) return 0 - - src.locked = 1 - playsound(src, bolt_down_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) - for(var/mob/M in range(1,src)) - M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() - return 1 - -/obj/machinery/door/airlock/proc/unlock(var/forced=0) - if(!src.locked) - return - - if (!forced) - if(operating || !src.arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS)) return - - src.locked = 0 - playsound(src, bolt_up_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) - for(var/mob/M in range(1,src)) - M.show_message("You hear a click from the bottom of the door.", 2) - update_icon() - return 1 - -/obj/machinery/door/airlock/allowed(mob/M) - if(locked) - return 0 - return ..(M) - -/obj/machinery/door/airlock/New(var/newloc, var/obj/structure/door_assembly/assembly=null) - ..() - - //if assembly is given, create the new door from the assembly - if (assembly && istype(assembly)) - assembly_type = assembly.type - - electronics = assembly.electronics - electronics.loc = src - - //update the door's access to match the electronics' - secured_wires = electronics.secure - if(electronics.one_access) - LAZYCLEARLIST(req_access) - req_one_access = src.electronics.conf_access - else - LAZYCLEARLIST(req_one_access) - req_access = src.electronics.conf_access - - //get the name from the assembly - if(assembly.created_name) - name = assembly.created_name - else - name = "[istext(assembly.glass) ? "[assembly.glass] airlock" : assembly.base_name]" - - //get the dir from the assembly - set_dir(assembly.dir) - - //wires - var/turf/T = get_turf(newloc) - if(T && (T.z in using_map.admin_levels)) - secured_wires = 1 - if (secured_wires) - wires = new/datum/wires/airlock/secure(src) - else - wires = new/datum/wires/airlock(src) - -/obj/machinery/door/airlock/Initialize() - if(src.closeOtherId != null) - for (var/obj/machinery/door/airlock/A in machines) - if(A.closeOtherId == src.closeOtherId && A != src) - src.closeOther = A - break - name = "\improper [name]" - . = ..() - -/obj/machinery/door/airlock/Destroy() - qdel(wires) - wires = null - return ..() - -// Most doors will never be deconstructed over the course of a round, -// so as an optimization defer the creation of electronics until -// the airlock is deconstructed -/obj/machinery/door/airlock/proc/create_electronics() - //create new electronics - if (secured_wires) - src.electronics = new/obj/item/weapon/airlock_electronics/secure( src.loc ) - else - src.electronics = new/obj/item/weapon/airlock_electronics( src.loc ) - - //update the electronics to match the door's access - if(LAZYLEN(req_access)) - electronics.conf_access = req_access - else if (LAZYLEN(req_one_access)) - electronics.conf_access = req_one_access - electronics.one_access = 1 - -/obj/machinery/door/airlock/emp_act(var/severity) - if(prob(40/severity)) - var/duration = world.time + SecondsToTicks(30 / severity) - if(duration > electrified_until) - electrify(duration) - ..() - -/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason - ..() - if(stat & NOPOWER) - // If we lost power, disable electrification - // Keeping door lights on, runs on internal battery or something. - electrified_until = 0 - update_icon() - -/obj/machinery/door/airlock/proc/prison_open() - if(arePowerSystemsOn()) - src.unlock() - src.open() - src.lock() - return - - -/obj/machinery/door/airlock/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - // Old RCD code made it cost 10 units to decon an airlock. - // Now the new one costs ten "sheets". - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 - ) - return FALSE - -/obj/machinery/door/airlock/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - return FALSE +//VOREStation Edit - Redone a lot of airlock things: +/* +- Specific department maintenance doors +- Named doors properly according to type +- Gave them default access levels with the access constants +- Improper'd all of the names in the new() +*/ + +/obj/machinery/door/airlock + name = "Airlock" + description_info = "If you hold left alt whilst left-clicking on an airlock, you can ring the doorbell to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!

    AIs and Cyborgs can also quickly open/close, bolt/unbolt, and electrify/de-electrify doors at a distance by holding left shift, left control, or left alt respectively whilst left-clicking." + icon = 'icons/obj/doors/Doorint.dmi' + icon_state = "door_closed" + power_channel = ENVIRON + + explosion_resistance = 10 + + // Doors do their own stuff + bullet_vulnerability = 0 + + blocks_emissive = EMISSIVE_BLOCK_GENERIC // Not quite as nice as /tg/'s custom masks. We should make those sometime + + var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in. + var/hackProof = 0 // if 1, this door can't be hacked by the AI + var/electrified_until = 0 //World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + var/main_power_lost_until = 0 //World time when main power is restored. + var/backup_power_lost_until = -1 //World time when backup power is restored. + var/has_beeped = 0 //If 1, will not beep on failed closing attempt. Resets when door closes. + var/spawnPowerRestoreRunning = 0 + var/welded = null + var/locked = 0 + var/lights = 1 // bolt lights show by default + var/aiDisabledIdScanner = 0 + var/aiHacking = 0 + var/obj/machinery/door/airlock/closeOther = null + var/closeOtherId = null + var/lockdownbyai = 0 + autoclose = 1 + var/assembly_type = /obj/structure/door_assembly + var/mineral = null + var/justzap = 0 + var/safe = 1 + normalspeed = 1 + var/obj/item/weapon/airlock_electronics/electronics = null + var/hasShocked = 0 //Prevents multiple shocks from happening + var/secured_wires = 0 + var/security_level = 1 //VOREStation Addition - acts as a multiplier on the time required to hack an airlock with a hacktool + var/datum/wires/airlock/wires = null + + var/open_sound_powered = 'sound/machines/door/covert1o.ogg' + var/open_sound_unpowered = 'sound/machines/door/airlockforced.ogg' + var/close_sound_powered = 'sound/machines/door/covert1c.ogg' + var/legacy_open_powered = 'sound/machines/door/old_airlock.ogg' + var/legacy_close_powered = 'sound/machines/door/old_airlockclose.ogg' + var/department_open_powered = null + var/department_close_powered = null + var/denied_sound = 'sound/machines/deniedbeep.ogg' + var/bolt_up_sound = 'sound/machines/door/boltsup.ogg' + var/bolt_down_sound = 'sound/machines/door/boltsdown.ogg' + var/knock_sound = 'sound/machines/2beeplow.ogg' + var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' + var/knock_unpowered_sound = 'sound/machines/door/knock_glass.ogg' + +/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage) + if(stat & (BROKEN|NOPOWER)) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + if(src.locked || src.welded) + visible_message("\The [user] begins breaking into \the [src] internals!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. + if(do_after(user,10 SECONDS,src)) + src.locked = 0 + src.welded = 0 + update_icon() + open(1) + if(prob(25)) + src.shock(user, 100) + user.set_AI_busy(FALSE) + else if(src.density) + visible_message("\The [user] forces \the [src] open!") + open(1) + else + visible_message("\The [user] forces \the [src] closed!") + close(1) + else + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/airlock/attack_alien(var/mob/user) //Familiar, right? Doors. -Mechoid + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + if(src.locked || src.welded) + visible_message("\The [user] begins tearing into \the [src] internals!") + src.do_animate("deny") + if(do_after(user,15 SECONDS,src)) + visible_message("\The [user] tears \the [src] open, sparks flying from its electronics!") + src.do_animate("spark") + playsound(src, 'sound/machines/door/airlock_tear_apart.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) + src.locked = 0 + src.welded = 0 + update_icon() + open(1) + src.set_broken() //These aren't emags, these be CLAWS + else if(src.density) + visible_message("\The [user] begins forcing \the [src] open!") + if(do_after(user, 5 SECONDS,src)) + playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1, volume_channel = VOLUME_CHANNEL_DOORS) + visible_message("\The [user] forces \the [src] open!") + open(1) + else + visible_message("\The [user] forces \the [src] closed!") + close(1) + else + src.do_animate("deny") + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/airlock/get_material() + if(mineral) + return get_material_by_name(mineral) + return get_material_by_name(MAT_STEEL) + +/obj/machinery/door/airlock/command + name = "Command Airlock" + icon = 'icons/obj/doors/Doorcom.dmi' + req_one_access = list(access_heads) + assembly_type = /obj/structure/door_assembly/door_assembly_com + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cmd3o.ogg' + department_close_powered = 'sound/machines/door/cmd3c.ogg' + security_level = 3 //VOREStation Addition + +/obj/machinery/door/airlock/security + name = "Security Airlock" + icon = 'icons/obj/doors/Doorsec.dmi' + req_one_access = list(access_security) + assembly_type = /obj/structure/door_assembly/door_assembly_sec + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sec1o.ogg' + department_close_powered = 'sound/machines/door/sec1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/engineering + name = "Engineering Airlock" + icon = 'icons/obj/doors/Dooreng.dmi' + req_one_access = list(access_engine) + assembly_type = /obj/structure/door_assembly/door_assembly_eng + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/engineeringatmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Doorengatmos.dmi' + req_one_access = list(access_atmospherics) + assembly_type = /obj/structure/door_assembly/door_assembly_eat + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/medical + name = "Medical Airlock" + icon = 'icons/obj/doors/Doormed.dmi' + req_one_access = list(access_medical) + assembly_type = /obj/structure/door_assembly/door_assembly_med + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/med1o.ogg' + department_close_powered = 'sound/machines/door/med1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/maintenance + name = "Maintenance Access" + icon = 'icons/obj/doors/Doormaint.dmi' + //req_one_access = list(access_maint_tunnels) //VOREStation Edit - Maintenance is open access + assembly_type = /obj/structure/door_assembly/door_assembly_mai + open_sound_powered = 'sound/machines/door/door2o.ogg' + close_sound_powered = 'sound/machines/door/door2c.ogg' + +/obj/machinery/door/airlock/maintenance/cargo + icon = 'icons/obj/doors/Doormaint_cargo.dmi' + req_one_access = list(access_cargo) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/door2o.ogg' + department_close_powered = 'sound/machines/door/door2c.ogg' + +/obj/machinery/door/airlock/maintenance/command + icon = 'icons/obj/doors/Doormaint_command.dmi' + req_one_access = list(access_heads) + +/obj/machinery/door/airlock/maintenance/common + icon = 'icons/obj/doors/Doormaint_common.dmi' + open_sound_powered = 'sound/machines/door/hall3o.ogg' + close_sound_powered = 'sound/machines/door/hall3c.ogg' + +/obj/machinery/door/airlock/maintenance/engi + icon = 'icons/obj/doors/Doormaint_engi.dmi' + req_one_access = list(access_engine) + +/obj/machinery/door/airlock/maintenance/int + icon = 'icons/obj/doors/Doormaint_int.dmi' + +/obj/machinery/door/airlock/maintenance/medical + icon = 'icons/obj/doors/Doormaint_med.dmi' + req_one_access = list(access_medical) + +/obj/machinery/door/airlock/maintenance/rnd + icon = 'icons/obj/doors/Doormaint_rnd.dmi' + req_one_access = list(access_research) + +/obj/machinery/door/airlock/maintenance/sec + icon = 'icons/obj/doors/Doormaint_sec.dmi' + req_one_access = list(access_security) + +/obj/machinery/door/airlock/external + name = "External Airlock" + icon = 'icons/obj/doors/Doorext.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_ext + open_sound_powered = 'sound/machines/door/space1o.ogg' + close_sound_powered = 'sound/machines/door/space1c.ogg' + +/obj/machinery/door/airlock/external/bolted + icon_state = "door_locked" // So it looks visibly bolted in map editor + locked = 1 + +// For convenience in making docking ports: one that is pre-bolted with frequency set! +/obj/machinery/door/airlock/external/bolted/cycling + frequency = 1379 + +/obj/machinery/door/airlock/glass_external + name = "External Airlock" + icon = 'icons/obj/doors/Doorextglass.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_ext + opacity = 0 + glass = 1 + req_one_access = list(access_external_airlocks) + open_sound_powered = 'sound/machines/door/space1o.ogg' + close_sound_powered = 'sound/machines/door/space1c.ogg' + +/obj/machinery/door/airlock/glass + name = "Glass Airlock" + icon = 'icons/obj/doors/Doorglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + open_sound_powered = 'sound/machines/door/hall1o.ogg' + close_sound_powered = 'sound/machines/door/hall1c.ogg' + legacy_open_powered = 'sound/machines/door/windowdoor.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + glass = 1 + +/obj/machinery/door/airlock/centcom + name = "Centcom Airlock" + icon = 'icons/obj/doors/Doorele.dmi' + req_one_access = list(access_cent_general) + opacity = 1 + open_sound_powered = 'sound/machines/door/cmd3o.ogg' + close_sound_powered = 'sound/machines/door/cmd3c.ogg' + security_level = 100 //VOREStation Addition + +/obj/machinery/door/airlock/glass_centcom + name = "Airlock" + icon = 'icons/obj/doors/Dooreleglass.dmi' + opacity = 0 + glass = 1 + open_sound_powered = 'sound/machines/door/cmd3o.ogg' + close_sound_powered = 'sound/machines/door/cmd3c.ogg' + security_level = 100 //VOREStation Addition + +/obj/machinery/door/airlock/vault + name = "Vault" + icon = 'icons/obj/doors/vault.dmi' + explosion_resistance = 20 + opacity = 1 + secured_wires = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity //Until somebody makes better sprites. + req_one_access = list(access_heads_vault) + open_sound_powered = 'sound/machines/door/vault1o.ogg' + close_sound_powered = 'sound/machines/door/vault1c.ogg' + security_level = 5 //VOREStation Addition + +/obj/machinery/door/airlock/vault/bolted + icon_state = "door_locked" + locked = 1 + +/obj/machinery/door/airlock/freezer + name = "Freezer Airlock" + icon = 'icons/obj/doors/Doorfreezer.dmi' + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_fre + +/obj/machinery/door/airlock/hatch + name = "Airtight Hatch" + icon = 'icons/obj/doors/Doorhatchele.dmi' + explosion_resistance = 20 + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_hatch + req_one_access = list(access_maint_tunnels) + open_sound_powered = 'sound/machines/door/hatchopen.ogg' + close_sound_powered = 'sound/machines/door/hatchclose.ogg' + open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' + +/obj/machinery/door/airlock/maintenance_hatch + name = "Maintenance Hatch" + icon = 'icons/obj/doors/Doorhatchmaint2.dmi' + explosion_resistance = 20 + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_mhatch + req_one_access = list(access_maint_tunnels) + open_sound_powered = 'sound/machines/door/hatchopen.ogg' + close_sound_powered = 'sound/machines/door/hatchclose.ogg' + open_sound_unpowered = 'sound/machines/door/hatchforced.ogg' + +/obj/machinery/door/airlock/glass_command + name = "Command Airlock" + icon = 'icons/obj/doors/Doorcomglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_com + glass = 1 + req_one_access = list(access_heads) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cmd1o.ogg' + department_close_powered = 'sound/machines/door/cmd1c.ogg' + security_level = 3 //VOREStation Addition + +/obj/machinery/door/airlock/glass_engineering + name = "Engineering Airlock" + icon = 'icons/obj/doors/Doorengglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_eng + glass = 1 + req_one_access = list(access_engine) + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/glass_engineeringatmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Doorengatmoglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_eat + glass = 1 + req_one_access = list(access_atmospherics) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/glass_security + name = "Security Airlock" + icon = 'icons/obj/doors/Doorsecglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_sec + glass = 1 + req_one_access = list(access_security) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sec1o.ogg' + department_close_powered = 'sound/machines/door/sec1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/glass_medical + name = "Medical Airlock" + icon = 'icons/obj/doors/Doormedglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_med + glass = 1 + req_one_access = list(access_medical) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/med1o.ogg' + department_close_powered = 'sound/machines/door/med1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/mining + name = "Mining Airlock" + icon = 'icons/obj/doors/Doormining.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_min + req_one_access = list(access_mining) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cgo1o.ogg' + department_close_powered = 'sound/machines/door/cgo1c.ogg' + +/obj/machinery/door/airlock/atmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Dooratmo.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + req_one_access = list(access_atmospherics) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/research + name = "Research Airlock" + icon = 'icons/obj/doors/Doorresearch.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_research + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/glass_research + name = "Research Airlock" + icon = 'icons/obj/doors/Doorresearchglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_research + glass = 1 + req_one_access = list(access_research) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 2 //VOREStation Addition + +/obj/machinery/door/airlock/glass_mining + name = "Mining Airlock" + icon = 'icons/obj/doors/Doorminingglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_min + glass = 1 + req_one_access = list(access_mining) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/cgo1o.ogg' + department_close_powered = 'sound/machines/door/cgo1c.ogg' + +/obj/machinery/door/airlock/glass_atmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Dooratmoglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + glass = 1 + req_one_access = list(access_atmospherics) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/eng1o.ogg' + department_close_powered = 'sound/machines/door/eng1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/gold + name = "Gold Airlock" + icon = 'icons/obj/doors/Doorgold.dmi' + mineral = "gold" + +/obj/machinery/door/airlock/silver + name = "Silver Airlock" + icon = 'icons/obj/doors/Doorsilver.dmi' + mineral = "silver" + +/obj/machinery/door/airlock/diamond + name = "Diamond Airlock" + icon = 'icons/obj/doors/Doordiamond.dmi' + mineral = "diamond" + +/obj/machinery/door/airlock/uranium + name = "Uranium Airlock" + desc = "And they said I was crazy." + icon = 'icons/obj/doors/Dooruranium.dmi' + mineral = "uranium" + var/last_event = 0 + var/rad_power = 7.5 + +/obj/machinery/door/airlock/process() + // Deliberate no call to parent. + if(main_power_lost_until > 0 && world.time >= main_power_lost_until) + regainMainPower() + + if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until) + regainBackupPower() + + else if(electrified_until > 0 && world.time >= electrified_until) + electrify(0) + + if (..() == PROCESS_KILL && !(main_power_lost_until > 0 || backup_power_lost_until > 0 || electrified_until > 0)) + . = PROCESS_KILL + +/obj/machinery/door/airlock/uranium/process() + if(world.time > last_event+20) + if(prob(50)) + SSradiation.radiate(src, rad_power) + last_event = world.time + ..() + +/obj/machinery/door/airlock/phoron + name = "Phoron Airlock" + desc = "No way this can end badly." + icon = 'icons/obj/doors/Doorphoron.dmi' + mineral = "phoron" + +/obj/machinery/door/airlock/phoron/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300) + PhoronBurn(exposed_temperature) + +/obj/machinery/door/airlock/phoron/proc/ignite(exposed_temperature) + if(exposed_temperature > 300) + PhoronBurn(exposed_temperature) + +/obj/machinery/door/airlock/phoron/proc/PhoronBurn(temperature) + for(var/turf/simulated/floor/target_tile in range(2,loc)) + target_tile.assume_gas("phoron", 35, 400+T0C) + spawn (0) target_tile.hotspot_expose(temperature, 400) + for(var/turf/simulated/wall/W in range(3,src)) + W.burn((temperature/4))//Added so that you can't set off a massive chain reaction with a small flame + for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) + D.ignite(temperature/4) + new/obj/structure/door_assembly( src.loc ) + qdel(src) + +/obj/machinery/door/airlock/sandstone + name = "Sandstone Airlock" + icon = 'icons/obj/doors/Doorsand.dmi' + mineral = "sandstone" + +/obj/machinery/door/airlock/science + name = "Research Airlock" + icon = 'icons/obj/doors/Doorsci.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_science + req_one_access = list(access_research) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/glass_science + name = "Glass Airlocks" + icon = 'icons/obj/doors/Doorsciglass.dmi' + opacity = 0 + assembly_type = /obj/structure/door_assembly/door_assembly_science + glass = 1 + req_one_access = list(access_research) + open_sound_powered = 'sound/machines/door/hall1o.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + close_sound_powered = 'sound/machines/door/hall1c.ogg' // VOREStation Edit: Default door sounds for fancy, department-off. + department_open_powered = 'sound/machines/door/sci1o.ogg' + department_close_powered = 'sound/machines/door/sci1c.ogg' + security_level = 1.5 //VOREStation Addition + +/obj/machinery/door/airlock/highsecurity + name = "Secure Airlock" + icon = 'icons/obj/doors/hightechsecurity.dmi' + explosion_resistance = 20 + secured_wires = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity + req_one_access = list(access_heads_vault) + open_sound_powered = 'sound/machines/door/secure1o.ogg' + close_sound_powered = 'sound/machines/door/secure1c.ogg' + security_level = 4 //VOREStation Addition + +/obj/machinery/door/airlock/voidcraft + name = "voidcraft hatch" + desc = "It's an extra resilient airlock intended for spacefaring vessels." + icon = 'icons/obj/doors/shuttledoors.dmi' + explosion_resistance = 20 + opacity = 0 + glass = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft + open_sound_powered = 'sound/machines/door/shuttle1o.ogg' + close_sound_powered = 'sound/machines/door/shuttle1c.ogg' + +// Airlock opens from top-bottom instead of left-right. +/obj/machinery/door/airlock/voidcraft/vertical + icon = 'icons/obj/doors/shuttledoors_vertical.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_voidcraft/vertical + open_sound_powered = 'sound/machines/door/shuttle1o.ogg' + close_sound_powered = 'sound/machines/door/shuttle1c.ogg' + + +/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock + name = "Precursor Alpha Object - Doors" + desc = "This object appears to be used in order to restrict or allow access to \ + rooms based on its physical state. In other words, a door. \ + Despite being designed and created by unknown ancient alien hands, this door has \ + a large number of similarities to the conventional airlock, such as being driven by \ + electricity, opening and closing by physically moving, and being air tight. \ + It also operates by responding to signals through internal electrical conduits. \ + These characteristics make it possible for one with experience with a multitool \ + to manipulate the door.\ +

    \ + The symbol on the door does not match any living species' patterns, giving further \ + implications that this door is very old, and yet it remains operational after \ + thousands of years. It is unknown if that is due to superb construction, or \ + unseen autonomous maintenance having been performed." + value = CATALOGUER_REWARD_EASY + +/obj/machinery/door/airlock/alien + name = "alien airlock" + desc = "You're fairly sure this is a door." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock) + icon = 'icons/obj/doors/Dooralien.dmi' + explosion_resistance = 20 + secured_wires = TRUE + hackProof = TRUE + assembly_type = /obj/structure/door_assembly/door_assembly_alien + req_one_access = list(access_alien) + security_level = 100 //VOREStation Addition + +/obj/machinery/door/airlock/alien/locked + icon_state = "door_locked" + locked = TRUE + +/obj/machinery/door/airlock/alien/public // Entry to UFO. + req_one_access = list() + normalspeed = FALSE // So it closes faster and hopefully keeps the warm air inside. + hackProof = TRUE //VOREStation Edit - No borgos + +/* +About the new airlock wires panel: +* An airlock wire dialog can be accessed by the normal way or by using wirecutters or a multitool on the door while the wire-panel is open. This would show the following wires, which you can either wirecut/mend or send a multitool pulse through. There are 9 wires. +* one wire from the ID scanner. Sending a pulse through this flashes the red light on the door (if the door has power). If you cut this wire, the door will stop recognizing valid IDs. (If the door has 0000 access, it still opens and closes, though) +* two wires for power. Sending a pulse through either one causes a breaker to trip, disabling the door for 10 seconds if backup power is connected, or 1 minute if not (or until backup power comes back on, whichever is shorter). Cutting either one disables the main door power, but unless backup power is also cut, the backup power re-powers the door in 10 seconds. While unpowered, the door may be open, but bolts-raising will not work. Cutting these wires may electrocute the user. +* one wire for door bolts. Sending a pulse through this drops door bolts (whether the door is powered or not) or raises them (if it is). Cutting this wire also drops the door bolts, and mending it does not raise them. If the wire is cut, trying to raise the door bolts will not work. +* two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter). Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. +* one wire for opening the door. Sending a pulse through this while the door has power makes it open the door if no access is required. +* one wire for AI control. Sending a pulse through this blocks AI control for a second or so (which is enough to see the AI control light on the panel dialog go off and back on again). Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all. +* one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. (Currently it is also STAYING electrified until someone mends the wire) +* one wire for controling door safetys. When active, door does not close on someone. When cut, door will ruin someone's shit. When pulsed, door will immedately ruin someone's shit. +* one wire for controlling door speed. When active, dor closes at normal rate. When cut, door does not close manually. When pulsed, door attempts to close every tick. +*/ + + + +/obj/machinery/door/airlock/bumpopen(mob/living/user as mob) //Airlocks now zap you when you 'bump' them open when they're electrified. --NeoFite + if(!issilicon(usr)) + if(src.isElectrified()) + if(!src.justzap) + if(src.shock(user, 100)) + src.justzap = 1 + spawn (10) + src.justzap = 0 + return + else /*if(src.justzap)*/ + return + else if(user.hallucination > 50 && prob(10) && src.operating == 0) + to_chat(user, "You feel a powerful shock course through your body!") + user.halloss += 10 + user.stunned += 10 + return + ..(user) + +/obj/machinery/door/airlock/proc/isElectrified() + if(src.electrified_until != 0) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/canAIControl() + return ((src.aiControlDisabled!=1) && (!src.isAllPowerLoss())); + +/obj/machinery/door/airlock/proc/canAIHack() + return ((src.aiControlDisabled==1) && (!hackProof) && (!src.isAllPowerLoss())); + +/obj/machinery/door/airlock/proc/arePowerSystemsOn() + if (stat & (NOPOWER|BROKEN)) + return 0 + return (src.main_power_lost_until==0 || src.backup_power_lost_until==0) + +/obj/machinery/door/airlock/requiresID() + return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner) + +/obj/machinery/door/airlock/proc/isAllPowerLoss() + if(stat & (NOPOWER|BROKEN)) + return 1 + if(mainPowerCablesCut() && backupPowerCablesCut()) + return 1 + return 0 + +/obj/machinery/door/airlock/proc/mainPowerCablesCut() + return wires.is_cut(WIRE_MAIN_POWER1) || wires.is_cut(WIRE_MAIN_POWER2) + +/obj/machinery/door/airlock/proc/backupPowerCablesCut() + return wires.is_cut(WIRE_BACKUP_POWER1) || wires.is_cut(WIRE_BACKUP_POWER2) + +/obj/machinery/door/airlock/proc/loseMainPower() + main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + + // If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running + if(backup_power_lost_until == -1 && !backupPowerCablesCut()) + backup_power_lost_until = world.time + SecondsToTicks(10) + + if(main_power_lost_until > 0 || backup_power_lost_until > 0) + START_MACHINE_PROCESSING(src) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + + update_icon() + +/obj/machinery/door/airlock/proc/loseBackupPower() + backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + + if(backup_power_lost_until > 0) + START_MACHINE_PROCESSING(src) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + + update_icon() + +/obj/machinery/door/airlock/proc/regainMainPower() + if(!mainPowerCablesCut()) + main_power_lost_until = 0 + // If backup power is currently active then disable, otherwise let it count down and disable itself later + if(!backup_power_lost_until) + backup_power_lost_until = -1 + + update_icon() + +/obj/machinery/door/airlock/proc/regainBackupPower() + if(!backupPowerCablesCut()) + // Restore backup power only if main power is offline, otherwise permanently disable + backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 + + update_icon() + +/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) + var/message = "" + if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn()) + message = text("The electrification wire is cut - Door permanently electrified.") + src.electrified_until = -1 + else if(duration && !arePowerSystemsOn()) + message = text("The door is unpowered - Cannot electrify the door.") + src.electrified_until = 0 + else if(!duration && electrified_until != 0) + message = "The door is now un-electrified." + src.electrified_until = 0 + else if(duration) //electrify door for the given duration seconds + if(usr) + shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") + add_attack_logs(usr,name,"Electrified a door") + else + shockedby += text("\[[time_stamp()]\] - EMP)") + message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." + src.electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration) + + if(electrified_until > 0) + START_MACHINE_PROCESSING(src) + + if(feedback && message) + to_chat(usr,message) + +/obj/machinery/door/airlock/proc/set_idscan(var/activate, var/feedback = 0) + var/message = "" + if(wires.is_cut(WIRE_IDSCAN)) + message = "The IdScan wire is cut - IdScan feature permanently disabled." + else if(activate && src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 0 + message = "IdScan feature has been enabled." + else if(!activate && !src.aiDisabledIdScanner) + src.aiDisabledIdScanner = 1 + message = "IdScan feature has been disabled." + + if(feedback && message) + to_chat(usr,message) + +/obj/machinery/door/airlock/proc/set_safeties(var/activate, var/feedback = 0) + var/message = "" + // Safeties! We don't need no stinking safeties! + if (wires.is_cut(WIRE_SAFETY)) + message = text("The safety wire is cut - Cannot enable safeties.") + else if (!activate && src.safe) + safe = 0 + else if (activate && !src.safe) + safe = 1 + + if(feedback && message) + to_chat(usr,message) + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise +// The preceding comment was borrowed from the grille's shock script +/obj/machinery/door/airlock/shock(mob/user, prb) + if(!arePowerSystemsOn()) + return 0 + if(hasShocked) + return 0 //Already shocked someone recently? + if(..()) + hasShocked = 1 + sleep(10) + hasShocked = 0 + return 1 + else + return 0 + + +/obj/machinery/door/airlock/update_icon() + cut_overlays() + if(density) + if(locked && lights && src.arePowerSystemsOn()) + icon_state = "door_locked" + else + icon_state = "door_closed" + if(p_open || welded) + if(p_open) + add_overlay("panel_open") + if (!(stat & NOPOWER)) + if(stat & BROKEN) + add_overlay("sparks_broken") + else if (health < maxhealth * 3/4) + add_overlay("sparks_damaged") + if(welded) + add_overlay("welded") + else if (health < maxhealth * 3/4 && !(stat & NOPOWER)) + add_overlay("sparks_damaged") + else + icon_state = "door_open" + if((stat & BROKEN) && !(stat & NOPOWER)) + add_overlay("sparks_open") + return + +/obj/machinery/door/airlock/do_animate(animation) + switch(animation) + if("opening") + cut_overlay() + if(p_open) + flick("o_door_opening", src) //can not use flick due to BYOND bug updating overlays right before flicking + update_icon() + else + flick("door_opening", src)//[stat ? "_stat":] + update_icon() + if("closing") + cut_overlay() + if(p_open) + flick("o_door_closing", src) + update_icon() + else + flick("door_closing", src) + update_icon() + if("spark") + if(density) + flick("door_spark", src) + if("deny") + if(density && src.arePowerSystemsOn()) + flick("door_deny", src) + playsound(src, denied_sound, 50, 0, 3) + return + +/obj/machinery/door/airlock/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/door/airlock/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/door/airlock/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiAirlock", name) + ui.open() + if(custom_state) + ui.set_state(custom_state) + return TRUE + +/obj/machinery/door/airlock/tgui_data(mob/user) + var/list/data = list() + + var/list/power = list() + power["main"] = main_power_lost_until > 0 ? 0 : 2 + power["main_timeleft"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) + power["backup"] = backup_power_lost_until > 0 ? 0 : 2 + power["backup_timeleft"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) + data["power"] = power + + data["shock"] = (electrified_until == 0) ? 2 : 0 + data["shock_timeleft"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) + data["id_scanner"] = !aiDisabledIdScanner + data["locked"] = locked // bolted + data["lights"] = lights // bolt lights + data["safe"] = safe // safeties + data["speed"] = normalspeed // safe speed + data["welded"] = welded // welded + data["opened"] = !density // opened + + var/list/wire = list() + wire["main_1"] = !wires.is_cut(WIRE_MAIN_POWER1) + wire["main_2"] = !wires.is_cut(WIRE_MAIN_POWER2) + wire["backup_1"] = !wires.is_cut(WIRE_BACKUP_POWER1) + wire["backup_2"] = !wires.is_cut(WIRE_BACKUP_POWER2) + wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY) + wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN) + wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS) + wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT) + wire["safe"] = !wires.is_cut(WIRE_SAFETY) + wire["timing"] = !wires.is_cut(WIRE_SPEED) + + data["wires"] = wire + return data + +/obj/machinery/door/airlock/proc/hack(mob/user as mob) + if(src.aiHacking==0) + src.aiHacking=1 + spawn(20) + //TODO: Make this take a minute + to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.") + sleep(50) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Fault confirmed: airlock control wire disabled or cut.") + sleep(20) + to_chat(user, "Attempting to hack into airlock. This may take some time.") + sleep(200) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Upload access confirmed. Loading control program into airlock software.") + sleep(170) + if(src.canAIControl()) + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") + src.aiHacking=0 + return + else if(!src.canAIHack(user)) + to_chat(user, "We've lost our connection! Unable to hack airlock.") + src.aiHacking=0 + return + to_chat(user, "Transfer complete. Forcing airlock to execute program.") + sleep(50) + //disable blocked control + src.aiControlDisabled = 2 + to_chat(user, "Receiving control information from airlock.") + sleep(10) + //bring up airlock dialog + src.aiHacking = 0 + if (user) + src.attack_ai(user) + +/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target) + if (src.isElectrified()) + if (istype(mover, /obj/item)) + var/obj/item/i = mover + if (i.matter && (MAT_STEEL in i.matter) && i.matter[MAT_STEEL] > 0) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + return ..() + +/obj/machinery/door/airlock/attack_hand(mob/user as mob) + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 100)) + return + + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + src.attack_alien(user) + return + + if(src.p_open) + user.set_machine(src) + wires.Interact(user) + else + ..(user) + return + +/obj/machinery/door/airlock/AltClick(mob/user as mob) + . = ..() + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(!Adjacent(user)) + return + else if(user.a_intent == I_HURT) + src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") + src.add_fingerprint(user) + if(icon_state == "door_closed" && arePowerSystemsOn()) + flick("door_deny", src) + playsound(src, knock_hammer_sound, 50, 0, 3) + else if(arePowerSystemsOn() && user.a_intent == I_HELP) + src.visible_message("[user] presses the door bell on \the [src].", "\The [src]'s bell rings.") + src.add_fingerprint(user) + if(icon_state == "door_closed") + flick("door_deny", src) + playsound(src, knock_sound, 50, 0, 3) + else if(user.a_intent == I_HELP) + src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") + src.add_fingerprint(user) + playsound(src, knock_unpowered_sound, 50, 0, 3) + return + +/obj/machinery/door/airlock/tgui_act(action, params) + if(..()) + return TRUE + if(!user_allowed(usr)) + return TRUE + + switch(action) + if("disrupt-main") + if(!main_power_lost_until) + loseMainPower() + update_icon() + else + to_chat(usr, "Main power is already offline.") + . = TRUE + if("disrupt-backup") + if(!backup_power_lost_until) + loseBackupPower() + update_icon() + else + to_chat(usr, "Backup power is already offline.") + . = TRUE + if("shock-restore") + electrify(0, 1) + . = TRUE + if("shock-temp") + electrify(30, 1) + . = TRUE + if("shock-perm") + electrify(-1, 1) + . = TRUE + if("idscan-toggle") + set_idscan(aiDisabledIdScanner, 1) + . = TRUE + // if("emergency-toggle") + // toggle_emergency(usr) + // . = TRUE + if("bolt-toggle") + toggle_bolt(usr) + . = TRUE + if("light-toggle") + if(wires.is_cut(WIRE_BOLT_LIGHT)) + to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") + return + lights = !lights + update_icon() + . = TRUE + if("safe-toggle") + set_safeties(!safe, 1) + . = TRUE + if("speed-toggle") + if(wires.is_cut(WIRE_SPEED)) + to_chat(usr, "The timing wire is cut - Cannot alter timing.") + return + normalspeed = !normalspeed + . = TRUE + if("open-close") + user_toggle_open(usr) + . = TRUE + + update_icon() + return 1 + +/obj/machinery/door/airlock/proc/user_allowed(mob/user) + var/allowed = (issilicon(user) && canAIControl(user)) + if(!allowed && isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + allowed = TRUE + return allowed + +/obj/machinery/door/airlock/proc/toggle_bolt(mob/user) + if(!user_allowed(user)) + return + if(wires.is_cut(WIRE_DOOR_BOLTS)) + to_chat(user, "The door bolt drop wire is cut - you can't toggle the door bolts.") + return + if(locked) + if(!arePowerSystemsOn()) + to_chat(user, "The door has no power - you can't raise the door bolts.") + else + unlock() + to_chat(user, "The door bolts have been raised.") + // log_combat(user, src, "unbolted") + else + lock() + to_chat(user, "The door bolts have been dropped.") + // log_combat(user, src, "bolted") + +/obj/machinery/door/airlock/proc/user_toggle_open(mob/user) + if(!user_allowed(user)) + return + if(welded) + to_chat(user, text("The airlock has been welded shut!")) + else if(locked) + to_chat(user, text("The door bolts are down!")) + else if(!density) + close() + else + open() + +/obj/machinery/door/airlock/proc/can_remove_electronics() + return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) + +/obj/machinery/door/airlock/attackby(obj/item/C, mob/user as mob) + //to_world("airlock attackby src [src] obj [C] mob [user]") + if(!istype(usr, /mob/living/silicon)) + if(src.isElectrified()) + if(src.shock(user, 75)) + return + if(istype(C, /obj/item/taperoll)) + return + + src.add_fingerprint(user) + if (attempt_vr(src,"attackby_vr",list(C, user))) return + if(istype(C, /mob/living)) + ..() + return + if(!repairing && C.has_tool_quality(TOOL_WELDER) && !( src.operating > 0 ) && src.density) + var/obj/item/weapon/weldingtool/W = C.get_welder() + if(W.remove_fuel(0,user)) + if(!src.welded) + src.welded = 1 + else + src.welded = null + playsound(src, C.usesound, 75, 1) + src.update_icon() + return + else + return + else if(C.has_tool_quality(TOOL_SCREWDRIVER)) + if (src.p_open) + if (stat & BROKEN) + to_chat(usr, "The panel is broken and cannot be closed.") + else + src.p_open = FALSE + playsound(src, C.usesound, 50, 1) + src.update_icon() + return + else + src.p_open = TRUE + playsound(src, C.usesound, 50, 1) + src.update_icon() + return src.attack_hand(user) + else if(C.has_tool_quality(TOOL_WIRECUTTER)) + return src.attack_hand(user) + else if(istype(C, /obj/item/device/multitool)) + return src.attack_hand(user) + else if(istype(C, /obj/item/device/assembly/signaler)) + return src.attack_hand(user) + else if(istype(C, /obj/item/weapon/pai_cable)) // -- TLE + var/obj/item/weapon/pai_cable/cable = C + cable.plugin(src, user) + else if(!repairing && C.has_tool_quality(TOOL_CROWBAR)) + if(can_remove_electronics()) + playsound(src, C.usesound, 75, 1) + user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") + if(do_after(user,40 * C.toolspeed)) + to_chat(user, "You removed the airlock electronics!") + + var/obj/structure/door_assembly/da = new assembly_type(src.loc) + if (istype(da, /obj/structure/door_assembly/multi_tile)) + da.set_dir(src.dir) + + da.anchored = TRUE + if(mineral) + da.glass = mineral + //else if(glass) + else if(glass && !da.glass) + da.glass = 1 + da.state = 1 + da.created_name = src.name + da.update_state() + + if(operating == -1 || (stat & BROKEN)) + new /obj/item/weapon/circuitboard/broken(src.loc) + operating = 0 + else + if (!electronics) create_electronics() + + electronics.loc = src.loc + electronics = null + + qdel(src) + return + else if(arePowerSystemsOn()) + to_chat(user, "The airlock's motors resist your efforts to force it.") + else if(locked) + to_chat(user, "The airlock's bolts prevent it from being forced.") + else + if(density) + spawn(0) open(1) + else + spawn(0) close(1) + + // Check if we're using a crowbar or armblade, and if the airlock's unpowered for whatever reason (off, broken, etc). + else if(istype(C, /obj/item/weapon)) + var/obj/item/weapon/W = C + if((W.pry == 1) && !arePowerSystemsOn()) + if(locked) + to_chat(user, "The airlock's bolts prevent it from being forced.") + else if( !welded && !operating ) + if(istype(C, /obj/item/weapon/material/twohanded/fireaxe)) // If this is a fireaxe, make sure it's held in two hands. + var/obj/item/weapon/material/twohanded/fireaxe/F = C + if(!F.wielded) + to_chat(user, "You need to be wielding \the [F] to do that.") + return + // At this point, it's an armblade or a fireaxe that passed the wielded test, let's try to open it. + if(density) + spawn(0) + open(1) + else + spawn(0) + close(1) + else + ..() + else + ..() + return + +/obj/machinery/door/airlock/phoron/attackby(C as obj, mob/user as mob) + if(C) + ignite(is_hot(C)) + ..() + +/obj/machinery/door/airlock/set_broken() + src.p_open = TRUE + stat |= BROKEN + if (secured_wires) + lock() + for (var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message("[src.name]'s control panel bursts open, sparks spewing out!") + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + + update_icon() + return + +/obj/machinery/door/airlock/open(var/forced=0) + if(!can_open(forced)) + return 0 + use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people + + //if the door is unpowered then it doesn't make sense to hear the woosh of a pneumatic actuator + for(var/mob/M as anything in player_list) + if(!M || !M.client) + continue + var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) + var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) + var/sound + var/volume + if(old_sounds) // Do we have old sounds enabled? Play these even if we have department door sounds enabled. + if(arePowerSystemsOn()) + sound = legacy_open_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else if(!old_sounds && department_door_sounds && src.department_open_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. + if(arePowerSystemsOn()) + sound = department_open_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else // Else, play these. + if(arePowerSystemsOn()) + sound = open_sound_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + + var/turf/T = get_turf(M) + var/distance = get_dist(T, get_turf(src)) + if(distance <= world.view * 2) + if(T && T.z == get_z(src)) + M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) + + if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) + src.closeOther.close() + return ..() + +/obj/machinery/door/airlock/can_open(var/forced=0) + if(!forced) + if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) + return 0 + + if(locked || welded) + return 0 + return ..() + +/obj/machinery/door/airlock/can_close(var/forced=0) + if(locked || welded) + return 0 + + if(!forced) + //despite the name, this wire is for general door control. + if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) + return 0 + + return ..() + +/atom/movable/proc/blocks_airlock() + return density + +/obj/machinery/door/blocks_airlock() + return 0 + +/obj/machinery/mech_sensor/blocks_airlock() + return 0 + +/mob/living/blocks_airlock() + return 1 + +/atom/movable/proc/airlock_crush(var/crush_damage) + return 0 + +/obj/machinery/portable_atmospherics/canister/airlock_crush(var/crush_damage) + . = ..() + health -= crush_damage + healthcheck() + +/obj/effect/energy_field/airlock_crush(var/crush_damage) + adjust_strength(crush_damage) + +/obj/structure/closet/airlock_crush(var/crush_damage) + ..() + damage(crush_damage) + for(var/atom/movable/AM in src) + AM.airlock_crush() + return 1 + +/mob/living/airlock_crush(var/crush_damage) + . = ..() + adjustBruteLoss(crush_damage) + SetStunned(5) + SetWeakened(5) + var/turf/T = get_turf(src) + T.add_blood(src) + return 1 + +/mob/living/carbon/airlock_crush(var/crush_damage) + . = ..() + if(can_feel_pain()) + emote("scream") + +/mob/living/silicon/robot/airlock_crush(var/crush_damage) + adjustBruteLoss(crush_damage) + return 0 + +/obj/machinery/door/airlock/close(var/forced=0) + if(!can_close(forced)) + return 0 + + if(safe) + for(var/turf/turf in locs) + for(var/atom/movable/AM in turf) + if(AM.blocks_airlock()) + if(!has_beeped) + playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) + has_beeped = 1 + autoclose_in(6) + return + + for(var/turf/turf in locs) + for(var/atom/movable/AM in turf) + if(AM.airlock_crush(DOOR_CRUSH_DAMAGE)) + take_damage(DOOR_CRUSH_DAMAGE) + + use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people + has_beeped = 0 + for(var/mob/M as anything in player_list) + if(!M || !M.client) + continue + var/old_sounds = M.client.is_preference_enabled(/datum/client_preference/old_door_sounds) + var/department_door_sounds = M.client.is_preference_enabled(/datum/client_preference/department_door_sounds) + var/sound + var/volume + if(old_sounds) + if(arePowerSystemsOn()) + sound = legacy_close_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else if(!old_sounds && department_door_sounds && src.department_close_powered) // Else, we have old sounds disabled, the door has per-department door sounds, and we have chosen to play department door sounds, use these. + if(arePowerSystemsOn()) + sound = department_close_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + else + if(arePowerSystemsOn()) + sound = close_sound_powered + volume = 50 + else + sound = open_sound_unpowered + volume = 75 + + var/turf/T = get_turf(M) + var/distance = get_dist(T, get_turf(src)) + if(distance <= world.view * 2) + if(T && T.z == get_z(src)) + M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) + for(var/turf/turf in locs) + var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) + if(killthis) + killthis.ex_act(2)//Smashin windows + return ..() + +/obj/machinery/door/airlock/proc/lock(var/forced=0) + if(locked) + return 0 + + if (operating && !forced) return 0 + + src.locked = 1 + playsound(src, bolt_down_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) + for(var/mob/M in range(1,src)) + M.show_message("You hear a click from the bottom of the door.", 2) + update_icon() + return 1 + +/obj/machinery/door/airlock/proc/unlock(var/forced=0) + if(!src.locked) + return + + if (!forced) + if(operating || !src.arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS)) return + + src.locked = 0 + playsound(src, bolt_up_sound, 30, 0, 3, volume_channel = VOLUME_CHANNEL_DOORS) + for(var/mob/M in range(1,src)) + M.show_message("You hear a click from the bottom of the door.", 2) + update_icon() + return 1 + +/obj/machinery/door/airlock/allowed(mob/M) + if(locked) + return 0 + return ..(M) + +/obj/machinery/door/airlock/New(var/newloc, var/obj/structure/door_assembly/assembly=null) + ..() + + //if assembly is given, create the new door from the assembly + if (assembly && istype(assembly)) + assembly_type = assembly.type + + electronics = assembly.electronics + electronics.loc = src + + //update the door's access to match the electronics' + secured_wires = electronics.secure + if(electronics.one_access) + LAZYCLEARLIST(req_access) + req_one_access = src.electronics.conf_access + else + LAZYCLEARLIST(req_one_access) + req_access = src.electronics.conf_access + + //get the name from the assembly + if(assembly.created_name) + name = assembly.created_name + else + name = "[istext(assembly.glass) ? "[assembly.glass] airlock" : assembly.base_name]" + + //get the dir from the assembly + set_dir(assembly.dir) + + //wires + var/turf/T = get_turf(newloc) + if(T && (T.z in using_map.admin_levels)) + secured_wires = 1 + if (secured_wires) + wires = new/datum/wires/airlock/secure(src) + else + wires = new/datum/wires/airlock(src) + +/obj/machinery/door/airlock/Initialize() + if(src.closeOtherId != null) + for (var/obj/machinery/door/airlock/A in machines) + if(A.closeOtherId == src.closeOtherId && A != src) + src.closeOther = A + break + name = "\improper [name]" + . = ..() + +/obj/machinery/door/airlock/Destroy() + qdel(wires) + wires = null + return ..() + +// Most doors will never be deconstructed over the course of a round, +// so as an optimization defer the creation of electronics until +// the airlock is deconstructed +/obj/machinery/door/airlock/proc/create_electronics() + //create new electronics + if (secured_wires) + src.electronics = new/obj/item/weapon/airlock_electronics/secure( src.loc ) + else + src.electronics = new/obj/item/weapon/airlock_electronics( src.loc ) + + //update the electronics to match the door's access + if(LAZYLEN(req_access)) + electronics.conf_access = req_access + else if (LAZYLEN(req_one_access)) + electronics.conf_access = req_one_access + electronics.one_access = 1 + +/obj/machinery/door/airlock/emp_act(var/severity) + if(prob(40/severity)) + var/duration = world.time + SecondsToTicks(30 / severity) + if(duration > electrified_until) + electrify(duration) + ..() + +/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason + ..() + if(stat & NOPOWER) + // If we lost power, disable electrification + // Keeping door lights on, runs on internal battery or something. + electrified_until = 0 + update_icon() + +/obj/machinery/door/airlock/proc/prison_open() + if(arePowerSystemsOn()) + src.unlock() + src.open() + src.lock() + return + + +/obj/machinery/door/airlock/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + // Old RCD code made it cost 10 units to decon an airlock. + // Now the new one costs ten "sheets". + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 + ) + return FALSE + +/obj/machinery/door/airlock/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + return FALSE diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 3d401fa0fb6..6aac668b509 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -1,153 +1,153 @@ -/obj/item/weapon/airlock_electronics - name = "airlock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = ITEMSIZE_SMALL //It should be tiny! -Agouri - - matter = list(MAT_STEEL = 50,MAT_GLASS = 50) - - req_one_access = list(access_engine, access_talon) // Access to unlock the device, ignored if emagged //VOREStation Edit - Add talon - var/list/apply_any_access = list(access_engine) // Can apply any access, not just their own - - var/secure = 0 //if set, then wires will be randomized and bolts will drop if the door is broken - var/list/conf_access = null - var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access - var/last_configurator = null - var/locked = 1 - var/emagged = 0 - -/obj/item/weapon/airlock_electronics/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - emagged = 1 - to_chat(user, "You remove the access restrictions on [src]!") - return 1 - -/obj/item/weapon/airlock_electronics/attack_self(mob/user as mob) - if (!ishuman(user) && !istype(user,/mob/living/silicon/robot)) - return ..(user) - - var/t1 = text("Access control
    \n") - - if (last_configurator) - t1 += "Operator: [last_configurator]
    " - - if (locked) - t1 += "Unlock Interface
    " - else - t1 += "Lock Interface
    " - - t1 += "Access requirement is set to " - t1 += one_access ? "ONE
    " : "ALL
    " - - t1 += conf_access == null ? "All
    " : "All
    " - - t1 += "
    " - - var/list/accesses = get_available_accesses(user) - for (var/acc in accesses) - var/aname = get_access_desc(acc) - - if (!conf_access || !conf_access.len || !(acc in conf_access)) - t1 += "[aname]
    " - else if(one_access) - t1 += "[aname]
    " - else - t1 += "[aname]
    " - - t1 += text("

    Close

    \n", src) - - user << browse(t1, "window=airlock_electronics") - onclose(user, "airlock") - -/obj/item/weapon/airlock_electronics/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained() || (!ishuman(usr) && !istype(usr,/mob/living/silicon))) - return - if (href_list["close"]) - usr << browse(null, "window=airlock_electronics") - return - - if (href_list["login"]) - if(emagged) - src.locked = 0 - src.last_configurator = usr.name - else if(issilicon(usr)) - src.locked = 0 - src.last_configurator = usr.name - else if(isliving(usr)) - var/obj/item/weapon/card/id/id - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - id = H.get_idcard() - // In their ID slot? - if(id && src.check_access(id)) - src.locked = 0 - src.last_configurator = id.registered_name - // Still locked, human handling didn't do it! - if(locked) - var/obj/item/I = usr.get_active_hand() - id = I?.GetID() - if(id && src.check_access(id)) - src.locked = 0 - src.last_configurator = id.registered_name - - if (locked) - return - - if (href_list["logout"]) - locked = 1 - - if (href_list["one_access"]) - one_access = !one_access - - if (href_list["access"]) - toggle_access(href_list["access"]) - - attack_self(usr) - -/obj/item/weapon/airlock_electronics/proc/toggle_access(var/acc) - if (acc == "all") - conf_access = null - else - var/req = text2num(acc) - - if (conf_access == null) - conf_access = list() - - if (!(req in conf_access)) - conf_access += req - else - conf_access -= req - if (!conf_access.len) - conf_access = null - -/obj/item/weapon/airlock_electronics/proc/get_available_accesses(var/mob/user) - var/obj/item/weapon/card/id/id - if(ishuman(user)) - var/mob/living/carbon/human/H = user - id = H.get_idcard() - else if(issilicon(user)) - var/mob/living/silicon/R = user - id = R.idcard - - // Nothing - if(!id || !id.access) - return list() - - // Has engineer access, can put any access - else if(has_access(null, apply_any_access, id.access)) - return get_all_station_access() - - // Not an engineer, can only pick your own accesses to program - else - return id.access - -/obj/item/weapon/airlock_electronics/secure - name = "secure airlock electronics" - desc = "designed to be somewhat more resistant to hacking than standard electronics." - origin_tech = list(TECH_DATA = 2) - secure = 1 - -/obj/item/weapon/airlock_electronics/secure/emag_act(var/remaining_charges, var/mob/user) - to_chat(user, "You don't appear to be able to bypass this hardened device!") - return -1 +/obj/item/weapon/airlock_electronics + name = "airlock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = ITEMSIZE_SMALL //It should be tiny! -Agouri + + matter = list(MAT_STEEL = 50,MAT_GLASS = 50) + + req_one_access = list(access_engine, access_talon) // Access to unlock the device, ignored if emagged //VOREStation Edit - Add talon + var/list/apply_any_access = list(access_engine) // Can apply any access, not just their own + + var/secure = 0 //if set, then wires will be randomized and bolts will drop if the door is broken + var/list/conf_access = null + var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access + var/last_configurator = null + var/locked = 1 + var/emagged = 0 + +/obj/item/weapon/airlock_electronics/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = 1 + to_chat(user, "You remove the access restrictions on [src]!") + return 1 + +/obj/item/weapon/airlock_electronics/attack_self(mob/user as mob) + if (!ishuman(user) && !istype(user,/mob/living/silicon/robot)) + return ..(user) + + var/t1 = text("Access control
    \n") + + if (last_configurator) + t1 += "Operator: [last_configurator]
    " + + if (locked) + t1 += "Unlock Interface
    " + else + t1 += "Lock Interface
    " + + t1 += "Access requirement is set to " + t1 += one_access ? "ONE
    " : "ALL
    " + + t1 += conf_access == null ? "All
    " : "All
    " + + t1 += "
    " + + var/list/accesses = get_available_accesses(user) + for (var/acc in accesses) + var/aname = get_access_desc(acc) + + if (!conf_access || !conf_access.len || !(acc in conf_access)) + t1 += "[aname]
    " + else if(one_access) + t1 += "[aname]
    " + else + t1 += "[aname]
    " + + t1 += text("

    Close

    \n", src) + + user << browse(t1, "window=airlock_electronics") + onclose(user, "airlock") + +/obj/item/weapon/airlock_electronics/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained() || (!ishuman(usr) && !istype(usr,/mob/living/silicon))) + return + if (href_list["close"]) + usr << browse(null, "window=airlock_electronics") + return + + if (href_list["login"]) + if(emagged) + src.locked = 0 + src.last_configurator = usr.name + else if(issilicon(usr)) + src.locked = 0 + src.last_configurator = usr.name + else if(isliving(usr)) + var/obj/item/weapon/card/id/id + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + id = H.get_idcard() + // In their ID slot? + if(id && src.check_access(id)) + src.locked = 0 + src.last_configurator = id.registered_name + // Still locked, human handling didn't do it! + if(locked) + var/obj/item/I = usr.get_active_hand() + id = I?.GetID() + if(id && src.check_access(id)) + src.locked = 0 + src.last_configurator = id.registered_name + + if (locked) + return + + if (href_list["logout"]) + locked = 1 + + if (href_list["one_access"]) + one_access = !one_access + + if (href_list["access"]) + toggle_access(href_list["access"]) + + attack_self(usr) + +/obj/item/weapon/airlock_electronics/proc/toggle_access(var/acc) + if (acc == "all") + conf_access = null + else + var/req = text2num(acc) + + if (conf_access == null) + conf_access = list() + + if (!(req in conf_access)) + conf_access += req + else + conf_access -= req + if (!conf_access.len) + conf_access = null + +/obj/item/weapon/airlock_electronics/proc/get_available_accesses(var/mob/user) + var/obj/item/weapon/card/id/id + if(ishuman(user)) + var/mob/living/carbon/human/H = user + id = H.get_idcard() + else if(issilicon(user)) + var/mob/living/silicon/R = user + id = R.idcard + + // Nothing + if(!id || !id.access) + return list() + + // Has engineer access, can put any access + else if(has_access(null, apply_any_access, id.access)) + return get_all_station_access() + + // Not an engineer, can only pick your own accesses to program + else + return id.access + +/obj/item/weapon/airlock_electronics/secure + name = "secure airlock electronics" + desc = "designed to be somewhat more resistant to hacking than standard electronics." + origin_tech = list(TECH_DATA = 2) + secure = 1 + +/obj/item/weapon/airlock_electronics/secure/emag_act(var/remaining_charges, var/mob/user) + to_chat(user, "You don't appear to be able to bypass this hardened device!") + return -1 diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm index f7dda48feb4..f8d0ea6b57e 100644 --- a/code/game/machinery/doors/alarmlock.dm +++ b/code/game/machinery/doors/alarmlock.dm @@ -1,45 +1,45 @@ -/obj/machinery/door/airlock/alarmlock - - name = "Glass Alarm Airlock" - icon = 'icons/obj/doors/Doorglass.dmi' - opacity = 0 - glass = 1 - - var/datum/radio_frequency/air_connection - var/air_frequency = 1437 - autoclose = 0 - -/obj/machinery/door/airlock/alarmlock/New() - ..() - air_connection = new - -/obj/machinery/door/airlock/alarmlock/Destroy() - if(radio_controller) - radio_controller.remove_object(src,air_frequency) - ..() - -/obj/machinery/door/airlock/alarmlock/Initialize() - . = ..() - radio_controller.remove_object(src, air_frequency) - air_connection = radio_controller.add_object(src, air_frequency, RADIO_TO_AIRALARM) - open() - - -/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) - ..() - if(stat & (NOPOWER|BROKEN)) - return - - var/alarm_area = signal.data["zone"] - var/alert = signal.data["alert"] - - var/area/our_area = get_area(src) - - if(alarm_area == our_area.name) - switch(alert) - if("severe") - autoclose = 1 - close() - if("minor", "clear") - autoclose = 0 - open() +/obj/machinery/door/airlock/alarmlock + + name = "Glass Alarm Airlock" + icon = 'icons/obj/doors/Doorglass.dmi' + opacity = 0 + glass = 1 + + var/datum/radio_frequency/air_connection + var/air_frequency = 1437 + autoclose = 0 + +/obj/machinery/door/airlock/alarmlock/New() + ..() + air_connection = new + +/obj/machinery/door/airlock/alarmlock/Destroy() + if(radio_controller) + radio_controller.remove_object(src,air_frequency) + ..() + +/obj/machinery/door/airlock/alarmlock/Initialize() + . = ..() + radio_controller.remove_object(src, air_frequency) + air_connection = radio_controller.add_object(src, air_frequency, RADIO_TO_AIRALARM) + open() + + +/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) + ..() + if(stat & (NOPOWER|BROKEN)) + return + + var/alarm_area = signal.data["zone"] + var/alert = signal.data["alert"] + + var/area/our_area = get_area(src) + + if(alarm_area == our_area.name) + switch(alert) + if("severe") + autoclose = 1 + close() + if("minor", "clear") + autoclose = 0 + open() diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index a1b5b21e977..e7e359ca9fa 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -1,294 +1,294 @@ -#define CHARS_PER_LINE 5 -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Small Fonts" -#define MAX_TIMER 36000 - -#define PRESET_SHORT 1 MINUTES -#define PRESET_MEDIUM 5 MINUTES -#define PRESET_LONG 10 MINUTES - -/////////////////////////////////////////////////////////////////////////////////////////////// -// Brig Door control displays. -// Description: This is a controls the timer for the brig doors, displays the timer on itself and -// has a popup window when used, allowing to set the timer. -// Code Notes: Combination of old brigdoor.dm code from rev4407 and the status_display.dm code -// Date: 01/September/2010 -// Programmer: Veryinky -///////////////////////////////////////////////////////////////////////////////////////////////// -/obj/machinery/door_timer - name = "Door Timer" - icon = 'icons/obj/status_display.dmi' - icon_state = "frame" - layer = ABOVE_WINDOW_LAYER - desc = "A remote control for a door." - req_access = list(access_brig) - anchored = TRUE // can't pick it up - density = FALSE // can walk through it. - var/id = null // id of door it controls. - var/activation_time = 0 - var/timer_duration = 0 - - var/timing = FALSE // boolean, true/1 timer is on, false/0 means it's not timing - var/list/obj/machinery/targets = list() - - - maptext_height = 26 - maptext_width = 32 - -/obj/machinery/door_timer/Initialize() - ..() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/door_timer/LateInitialize() - . = ..() - - for(var/obj/machinery/door/window/brigdoor/M in machines) - if(M.id == id) - LAZYADD(targets,M) - - for(var/obj/machinery/flasher/F in machines) - if(F.id == id) - LAZYADD(targets,F) - - for(var/obj/structure/closet/secure_closet/brig/C in all_brig_closets) - if(C.id == id) - LAZYADD(targets,C) - - if(!LAZYLEN(targets)) - stat |= BROKEN - update_icon() - -/obj/machinery/door_timer/Destroy() - LAZYCLEARLIST(targets) - return ..() - -//Main door timer loop, if it's timing and time is >0 reduce time by 1. -// if it's less than 0, open door, reset timer -// update the door_timer window and the icon -/obj/machinery/door_timer/process() - if(stat & (NOPOWER|BROKEN)) - return - if(timing) - if(world.time - activation_time >= timer_duration) - timer_end() // open doors, reset timer, clear status screen - update_icon() - - -// has the door power situation changed, if so update icon. -/obj/machinery/door_timer/power_change() - ..() - update_icon() - -// open/closedoor checks if door_timer has power, if so it checks if the -// linked door is open/closed (by density) then opens it/closes it. - -// Closes and locks doors, power check -/obj/machinery/door_timer/proc/timer_start() - if(stat & (NOPOWER|BROKEN)) - return 0 - - activation_time = world.time - timing = TRUE - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(door.density) - continue - INVOKE_ASYNC(door, TYPE_PROC_REF(/obj/machinery/door/window/brigdoor, close)) - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) - continue - if(C.opened && !C.close()) - continue - C.locked = TRUE - C.icon_state = "closed_locked" - return 1 - -/// Opens and unlocks doors, power check -/obj/machinery/door_timer/proc/timer_end(forced = FALSE) - if(stat & (NOPOWER|BROKEN)) - return 0 - - timing = FALSE - activation_time = null - set_timer(0) - update_icon() - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(!door.density) - continue - INVOKE_ASYNC(door, TYPE_PROC_REF(/obj/machinery/door/window/brigdoor, open)) - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) - continue - if(C.opened) - continue - C.locked = FALSE - C.icon_state = "closed_unlocked" - - return 1 - -/obj/machinery/door_timer/proc/time_left(seconds = FALSE) - . = max(0, timer_duration - (activation_time ? (world.time - activation_time) : 0)) - if(seconds) - . /= 10 - -/obj/machinery/door_timer/proc/set_timer(value) - var/new_time = clamp(value, 0, MAX_TIMER) - . = new_time == timer_duration //return 1 on no change - timer_duration = new_time - if(timer_duration && activation_time && timing) // Setting it while active will reset the activation time - activation_time = world.time - -/obj/machinery/door_timer/attack_ai(mob/user) - return src.attack_hand(user) - -/obj/machinery/door_timer/attack_hand(mob/user) - if(..()) - return TRUE - tgui_interact(user) - -/obj/machinery/door_timer/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "BrigTimer", name) - ui.open() - -/obj/machinery/door_timer/tgui_data() - var/list/data = list() - data["time_left"] = time_left() - data["max_time_left"] = MAX_TIMER - data["timing"] = timing - data["flash_found"] = FALSE - data["flash_charging"] = FALSE - data["preset_short"] = PRESET_SHORT - data["preset_medium"] = PRESET_MEDIUM - data["preset_long"] = PRESET_LONG - for(var/obj/machinery/flasher/F in targets) - data["flash_found"] = TRUE - if(F.last_flash && (F.last_flash + 150) > world.time) - data["flash_charging"] = TRUE - break - return data - -/obj/machinery/door_timer/tgui_act(action, params) - if(..()) - return - . = TRUE - - if(!allowed(usr)) - to_chat(usr, "Access denied.") - return FALSE - - switch(action) - if("time") - var/real_new_time = 0 - var/new_time = params["time"] - var/list/L = splittext(new_time, ":") - if(LAZYLEN(L)) - for(var/i in 1 to LAZYLEN(L)) - real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) - else - real_new_time = text2num(new_time) - if(real_new_time) - set_timer(real_new_time * 10) - if("start") - timer_start() - if("stop") - timer_end(forced = TRUE) - if("flash") - for(var/obj/machinery/flasher/F in targets) - F.flash() - if("preset") - var/preset = params["preset"] - var/preset_time = time_left() - switch(preset) - if("short") - preset_time = PRESET_SHORT - if("medium") - preset_time = PRESET_MEDIUM - if("long") - preset_time = PRESET_LONG - set_timer(timer_duration + preset_time) - if(timing) - activation_time = world.time - else - . = FALSE - - -//icon update function -// if NOPOWER, display blank -// if BROKEN, display blue screen of death icon AI uses -// if timing=true, run update display function -/obj/machinery/door_timer/update_icon() - if(stat & (NOPOWER)) - icon_state = "frame" - return - - if(stat & (BROKEN)) - set_picture("ai_bsod") - return - - if(timing) - var/disp1 = id - var/timeleft = time_left(seconds = TRUE) - var/disp2 = "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, "0")]" - if(length(disp2) > CHARS_PER_LINE) - disp2 = "Error" - update_display(disp1, disp2) - else - if(maptext) - maptext = "" - return - -// Adds an icon in case the screen is broken/off, stolen from status_display.dm -/obj/machinery/door_timer/proc/set_picture(state) - if(maptext) - maptext = "" - cut_overlays() - add_overlay(mutable_appearance('icons/obj/status_display.dmi', state)) - -//Checks to see if there's 1 line or 2, adds text-icons-numbers/letters over display -// Stolen from status_display -/obj/machinery/door_timer/proc/update_display(line1, line2) - line1 = uppertext(line1) - line2 = uppertext(line2) - var/new_text = {"
    [line1]
    [line2]
    "} - if(maptext != new_text) - maptext = new_text - -/obj/machinery/door_timer/cell_1 - name = "Cell 1" - id = "Cell 1" - -/obj/machinery/door_timer/cell_2 - name = "Cell 2" - id = "Cell 2" - -/obj/machinery/door_timer/cell_3 - name = "Cell 3" - id = "Cell 3" - -/obj/machinery/door_timer/cell_4 - name = "Cell 4" - id = "Cell 4" - -/obj/machinery/door_timer/cell_5 - name = "Cell 5" - id = "Cell 5" - -/obj/machinery/door_timer/cell_6 - name = "Cell 6" - id = "Cell 6" - -/obj/machinery/door_timer/tactical_pet_storage //Vorestation Addition - name = "Tactical Pet Storage" - id = "tactical_pet_storage" - desc = "Opens and Closes on a timer. This one seals away a tactical boost in morale." - -#undef FONT_SIZE -#undef FONT_COLOR -#undef FONT_STYLE -#undef CHARS_PER_LINE +#define CHARS_PER_LINE 5 +#define FONT_SIZE "5pt" +#define FONT_COLOR "#09f" +#define FONT_STYLE "Small Fonts" +#define MAX_TIMER 36000 + +#define PRESET_SHORT 1 MINUTES +#define PRESET_MEDIUM 5 MINUTES +#define PRESET_LONG 10 MINUTES + +/////////////////////////////////////////////////////////////////////////////////////////////// +// Brig Door control displays. +// Description: This is a controls the timer for the brig doors, displays the timer on itself and +// has a popup window when used, allowing to set the timer. +// Code Notes: Combination of old brigdoor.dm code from rev4407 and the status_display.dm code +// Date: 01/September/2010 +// Programmer: Veryinky +///////////////////////////////////////////////////////////////////////////////////////////////// +/obj/machinery/door_timer + name = "Door Timer" + icon = 'icons/obj/status_display.dmi' + icon_state = "frame" + layer = ABOVE_WINDOW_LAYER + desc = "A remote control for a door." + req_access = list(access_brig) + anchored = TRUE // can't pick it up + density = FALSE // can walk through it. + var/id = null // id of door it controls. + var/activation_time = 0 + var/timer_duration = 0 + + var/timing = FALSE // boolean, true/1 timer is on, false/0 means it's not timing + var/list/obj/machinery/targets = list() + + + maptext_height = 26 + maptext_width = 32 + +/obj/machinery/door_timer/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/door_timer/LateInitialize() + . = ..() + + for(var/obj/machinery/door/window/brigdoor/M in machines) + if(M.id == id) + LAZYADD(targets,M) + + for(var/obj/machinery/flasher/F in machines) + if(F.id == id) + LAZYADD(targets,F) + + for(var/obj/structure/closet/secure_closet/brig/C in all_brig_closets) + if(C.id == id) + LAZYADD(targets,C) + + if(!LAZYLEN(targets)) + stat |= BROKEN + update_icon() + +/obj/machinery/door_timer/Destroy() + LAZYCLEARLIST(targets) + return ..() + +//Main door timer loop, if it's timing and time is >0 reduce time by 1. +// if it's less than 0, open door, reset timer +// update the door_timer window and the icon +/obj/machinery/door_timer/process() + if(stat & (NOPOWER|BROKEN)) + return + if(timing) + if(world.time - activation_time >= timer_duration) + timer_end() // open doors, reset timer, clear status screen + update_icon() + + +// has the door power situation changed, if so update icon. +/obj/machinery/door_timer/power_change() + ..() + update_icon() + +// open/closedoor checks if door_timer has power, if so it checks if the +// linked door is open/closed (by density) then opens it/closes it. + +// Closes and locks doors, power check +/obj/machinery/door_timer/proc/timer_start() + if(stat & (NOPOWER|BROKEN)) + return 0 + + activation_time = world.time + timing = TRUE + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(door.density) + continue + INVOKE_ASYNC(door, TYPE_PROC_REF(/obj/machinery/door/window/brigdoor, close)) + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) + continue + if(C.opened && !C.close()) + continue + C.locked = TRUE + C.icon_state = "closed_locked" + return 1 + +/// Opens and unlocks doors, power check +/obj/machinery/door_timer/proc/timer_end(forced = FALSE) + if(stat & (NOPOWER|BROKEN)) + return 0 + + timing = FALSE + activation_time = null + set_timer(0) + update_icon() + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(!door.density) + continue + INVOKE_ASYNC(door, TYPE_PROC_REF(/obj/machinery/door/window/brigdoor, open)) + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) + continue + if(C.opened) + continue + C.locked = FALSE + C.icon_state = "closed_unlocked" + + return 1 + +/obj/machinery/door_timer/proc/time_left(seconds = FALSE) + . = max(0, timer_duration - (activation_time ? (world.time - activation_time) : 0)) + if(seconds) + . /= 10 + +/obj/machinery/door_timer/proc/set_timer(value) + var/new_time = clamp(value, 0, MAX_TIMER) + . = new_time == timer_duration //return 1 on no change + timer_duration = new_time + if(timer_duration && activation_time && timing) // Setting it while active will reset the activation time + activation_time = world.time + +/obj/machinery/door_timer/attack_ai(mob/user) + return src.attack_hand(user) + +/obj/machinery/door_timer/attack_hand(mob/user) + if(..()) + return TRUE + tgui_interact(user) + +/obj/machinery/door_timer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "BrigTimer", name) + ui.open() + +/obj/machinery/door_timer/tgui_data() + var/list/data = list() + data["time_left"] = time_left() + data["max_time_left"] = MAX_TIMER + data["timing"] = timing + data["flash_found"] = FALSE + data["flash_charging"] = FALSE + data["preset_short"] = PRESET_SHORT + data["preset_medium"] = PRESET_MEDIUM + data["preset_long"] = PRESET_LONG + for(var/obj/machinery/flasher/F in targets) + data["flash_found"] = TRUE + if(F.last_flash && (F.last_flash + 150) > world.time) + data["flash_charging"] = TRUE + break + return data + +/obj/machinery/door_timer/tgui_act(action, params) + if(..()) + return + . = TRUE + + if(!allowed(usr)) + to_chat(usr, "Access denied.") + return FALSE + + switch(action) + if("time") + var/real_new_time = 0 + var/new_time = params["time"] + var/list/L = splittext(new_time, ":") + if(LAZYLEN(L)) + for(var/i in 1 to LAZYLEN(L)) + real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) + else + real_new_time = text2num(new_time) + if(real_new_time) + set_timer(real_new_time * 10) + if("start") + timer_start() + if("stop") + timer_end(forced = TRUE) + if("flash") + for(var/obj/machinery/flasher/F in targets) + F.flash() + if("preset") + var/preset = params["preset"] + var/preset_time = time_left() + switch(preset) + if("short") + preset_time = PRESET_SHORT + if("medium") + preset_time = PRESET_MEDIUM + if("long") + preset_time = PRESET_LONG + set_timer(timer_duration + preset_time) + if(timing) + activation_time = world.time + else + . = FALSE + + +//icon update function +// if NOPOWER, display blank +// if BROKEN, display blue screen of death icon AI uses +// if timing=true, run update display function +/obj/machinery/door_timer/update_icon() + if(stat & (NOPOWER)) + icon_state = "frame" + return + + if(stat & (BROKEN)) + set_picture("ai_bsod") + return + + if(timing) + var/disp1 = id + var/timeleft = time_left(seconds = TRUE) + var/disp2 = "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, "0")]" + if(length(disp2) > CHARS_PER_LINE) + disp2 = "Error" + update_display(disp1, disp2) + else + if(maptext) + maptext = "" + return + +// Adds an icon in case the screen is broken/off, stolen from status_display.dm +/obj/machinery/door_timer/proc/set_picture(state) + if(maptext) + maptext = "" + cut_overlays() + add_overlay(mutable_appearance('icons/obj/status_display.dmi', state)) + +//Checks to see if there's 1 line or 2, adds text-icons-numbers/letters over display +// Stolen from status_display +/obj/machinery/door_timer/proc/update_display(line1, line2) + line1 = uppertext(line1) + line2 = uppertext(line2) + var/new_text = {"
    [line1]
    [line2]
    "} + if(maptext != new_text) + maptext = new_text + +/obj/machinery/door_timer/cell_1 + name = "Cell 1" + id = "Cell 1" + +/obj/machinery/door_timer/cell_2 + name = "Cell 2" + id = "Cell 2" + +/obj/machinery/door_timer/cell_3 + name = "Cell 3" + id = "Cell 3" + +/obj/machinery/door_timer/cell_4 + name = "Cell 4" + id = "Cell 4" + +/obj/machinery/door_timer/cell_5 + name = "Cell 5" + id = "Cell 5" + +/obj/machinery/door_timer/cell_6 + name = "Cell 6" + id = "Cell 6" + +/obj/machinery/door_timer/tactical_pet_storage //Vorestation Addition + name = "Tactical Pet Storage" + id = "tactical_pet_storage" + desc = "Opens and Closes on a timer. This one seals away a tactical boost in morale." + +#undef FONT_SIZE +#undef FONT_COLOR +#undef FONT_STYLE +#undef CHARS_PER_LINE diff --git a/code/game/machinery/doors/checkForMultipleDoors.dm b/code/game/machinery/doors/checkForMultipleDoors.dm index f7c6ff98b15..2e2f0768cb1 100644 --- a/code/game/machinery/doors/checkForMultipleDoors.dm +++ b/code/game/machinery/doors/checkForMultipleDoors.dm @@ -1,16 +1,16 @@ -/obj/machinery/door/proc/checkForMultipleDoors() - if(!src.loc) - return 0 - for(var/obj/machinery/door/D in src.loc) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - return 1 - -/turf/simulated/wall/proc/checkForMultipleDoors() - if(!src.loc) - return 0 - for(var/obj/machinery/door/D in locate(src.x,src.y,src.z)) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - //There are no false wall checks because that would be mischievious - return 1 +/obj/machinery/door/proc/checkForMultipleDoors() + if(!src.loc) + return 0 + for(var/obj/machinery/door/D in src.loc) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + return 1 + +/turf/simulated/wall/proc/checkForMultipleDoors() + if(!src.loc) + return 0 + for(var/obj/machinery/door/D in locate(src.x,src.y,src.z)) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + //There are no false wall checks because that would be mischievious + return 1 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index fa31ef365fc..5425b712109 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -1,514 +1,514 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -#define DOOR_REPAIR_AMOUNT 50 //amount of health regained per stack amount used - -/obj/machinery/door - name = "Door" - desc = "It opens and closes." - icon = 'icons/obj/doors/Doorint.dmi' - icon_state = "door1" - anchored = TRUE - opacity = 1 - density = TRUE - can_atmos_pass = ATMOS_PASS_PROC - layer = DOOR_OPEN_LAYER - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - var/open_layer = DOOR_OPEN_LAYER - var/closed_layer = DOOR_CLOSED_LAYER - - var/visible = 1 - var/p_open = 0 - var/operating = 0 - var/autoclose = 0 - var/glass = 0 - var/normalspeed = 1 - var/heat_proof = 0 // For glass airlocks/opacity firedoors - var/air_properties_vary_with_direction = 0 - var/maxhealth = 300 - var/health - var/destroy_hits = 10 //How many strong hits it takes to destroy the door - var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon - var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon - var/repairing = 0 - var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. - var/close_door_at = 0 //When to automatically close the door, if possible - - var/anim_length_before_density = 3 - var/anim_length_before_finalize = 7 - - //Multi-tile doors - dir = EAST - var/width = 1 - - // turf animation - var/atom/movable/overlay/c_animation = null - -/obj/machinery/door/attack_generic(var/mob/user, var/damage) - if(isanimal(user)) - var/mob/living/simple_mob/S = user - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - visible_message("\The [user] smashes into [src]!") - playsound(src, S.attack_sound, 75, 1) - take_damage(damage) - else - visible_message("\The [user] bonks \the [src] harmlessly.") - user.do_attack_animation(src) - -/obj/machinery/door/New() - . = ..() - if(density) - layer = closed_layer - explosion_resistance = initial(explosion_resistance) - update_heat_protection(get_turf(src)) - else - layer = open_layer - explosion_resistance = 0 - - - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - - health = maxhealth - update_icon() - - update_nearby_tiles(need_rebuild=1) - return - -/obj/machinery/door/Destroy() - density = FALSE - update_nearby_tiles() - . = ..() - -/obj/machinery/door/process() - if(close_door_at && world.time >= close_door_at) - if(autoclose) - close_door_at = world.time + next_close_wait() - spawn(0) - close() - else - close_door_at = 0 - if (..() == PROCESS_KILL && !close_door_at) - return PROCESS_KILL - -/obj/machinery/door/proc/autoclose_in(wait) - close_door_at = world.time + wait - START_MACHINE_PROCESSING(src) - -/obj/machinery/door/proc/can_open() - if(!density || operating || !ticker) - return 0 - return 1 - -/obj/machinery/door/proc/can_close() - if(density || operating || !ticker) - return 0 - return 1 - -/obj/machinery/door/Bumped(atom/AM) - . = ..() - if(p_open || operating) - return - if(ismob(AM)) - var/mob/M = AM - if(world.time - M.last_bumped <= 10) - return //Can bump-open one airlock per second. This is to prevent shock spam. - M.last_bumped = world.time - if(M.restrained() && !check_access(null)) - return - else if(istype(M, /mob/living/simple_mob/animal/passive/mouse) && !(M.ckey)) //VOREStation Edit: Make wild mice - return //VOREStation Edit: unable to open doors - else - bumpopen(M) - if(istype(AM, /obj/item/device/uav)) - if(check_access(null)) - open() - else - do_animate("deny") - - if(istype(AM, /mob/living/bot)) - var/mob/living/bot/bot = AM - if(src.check_access(bot.botcard)) - if(density) - open() - return - - if(istype(AM, /obj/mecha)) - var/obj/mecha/mecha = AM - if(density) - if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access))) - open() - else - do_animate("deny") - return - if(istype(AM, /obj/structure/bed/chair/wheelchair)) - var/obj/structure/bed/chair/wheelchair/wheel = AM - if(density) - if(wheel.pulling && (src.allowed(wheel.pulling))) - open() - else - do_animate("deny") - -/obj/machinery/door/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density - -/obj/machinery/door/CanZASPass(turf/T, is_zone) - if(is_zone) - return !block_air_zones // Block merging unless block_air_zones = 0 - return !density // Block airflow unless density = FALSE - -/obj/machinery/door/proc/bumpopen(mob/user as mob) - if(operating) return - if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit - return - src.add_fingerprint(user) - if(density) - if(allowed(user)) open() - else do_animate("deny") - return - -/obj/machinery/door/bullet_act(var/obj/item/projectile/Proj) - ..() - - var/damage = Proj.get_structure_damage() - - // Emitter Blasts - these will eventually completely destroy the door, given enough time. - if (damage > 90) - destroy_hits-- - if (destroy_hits <= 0) - visible_message("\The [src.name] disintegrates!") - switch (Proj.damage_type) - if(BRUTE) - new /obj/item/stack/material/steel(src.loc, 2) - new /obj/item/stack/rods(src.loc, 3) - if(BURN) - new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes! - qdel(src) - - if(damage) - //cap projectile damage so that there's still a minimum number of hits required to break the door - take_damage(min(damage, 100)) - - - -/obj/machinery/door/hitby(AM as mob|obj, var/speed=5) - - ..() - visible_message("[src.name] was hit by [AM].") - var/tforce = 0 - if(ismob(AM)) - tforce = 15 * (speed/5) - else - tforce = AM:throwforce * (speed/5) - playsound(src, hitsound, 100, 1) - take_damage(tforce) - return - -/obj/machinery/door/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/door/attack_hand(mob/user as mob) - return src.attackby(user, user) - -/obj/machinery/door/attack_tk(mob/user as mob) - if(requiresID() && !allowed(null)) - return - ..() - -/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) - src.add_fingerprint(user) - - if(istype(I)) - if(attackby_vr(I, user)) //VOREStation begin: Fireproofing - return //VOREStation begin: Fireproofing - if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name()) - if(stat & BROKEN) - to_chat(user, "It looks like \the [src] is pretty busted. It's going to need more than just patching up now.") - return - if(health >= maxhealth) - to_chat(user, "Nothing to fix!") - return - if(!density) - to_chat(user, "\The [src] must be closed before you can repair it.") - return - - //figure out how much metal we need - var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT - amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc? - - var/obj/item/stack/stack = I - var/amount_given = amount_needed - repairing - var/mats_given = stack.get_amount() - if(repairing && amount_given <= 0) - to_chat(user, "You must weld or remove \the [get_material_name()] from \the [src] before you can add anything else.") - else - if(mats_given >= amount_given) - if(stack.use(amount_given)) - repairing += amount_given - else - if(stack.use(mats_given)) - repairing += mats_given - amount_given = mats_given - if(amount_given) - to_chat(user, "You fit [amount_given] [stack.singular_name]\s to damaged and broken parts on \the [src].") - - return - - if(repairing && istype(I, /obj/item/weapon/weldingtool)) - if(!density) - to_chat(user, "\The [src] must be closed before you can repair it.") - return - - var/obj/item/weapon/weldingtool/welder = I - if(welder.remove_fuel(0,user)) - to_chat(user, "You start to fix dents and weld \the [get_material_name()] into place.") - playsound(src, welder.usesound, 50, 1) - if(do_after(user, (5 * repairing) * welder.toolspeed) && welder && welder.isOn()) - to_chat(user, "You finish repairing the damage to \the [src].") - health = between(health, health + repairing*DOOR_REPAIR_AMOUNT, maxhealth) - update_icon() - repairing = 0 - return - - if(repairing && I.is_crowbar()) - var/datum/material/mat = get_material() - var/obj/item/stack/material/repairing_sheet = mat.place_sheet(loc, repairing) - repairing = 0 - to_chat(user, "You remove \the [repairing_sheet].") - playsound(src, I.usesound, 100, 1) - return - - //psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them. - if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card)) - var/obj/item/weapon/W = I - user.setClickCooldown(user.get_attack_speed(W)) - if(W.damtype == BRUTE || W.damtype == BURN) - user.do_attack_animation(src) - if(W.force < min_force) - user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") - else - user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") - playsound(src, hitsound, 100, 1) - take_damage(W.force) - return - - if(src.operating > 0 || isrobot(user)) - return //borgs can't attack doors open because it conflicts with their AI-like interaction with them. - - if(src.operating) - return - - if(src.allowed(user) && operable()) - if(src.density) - open() - else - close() - return - - if(src.density) - do_animate("deny") - return - -/obj/machinery/door/emag_act(var/remaining_charges) - if(density && operable()) - do_animate("spark") - sleep(6) - open() - operating = -1 - return 1 - -/obj/machinery/door/take_damage(var/damage) - var/initialhealth = src.health - src.health = max(0, src.health - damage) - if(src.health <= 0 && initialhealth > 0) - src.set_broken() - else if(src.health < src.maxhealth / 4 && initialhealth >= src.maxhealth / 4) - visible_message("\The [src] looks like it's about to break!" ) - else if(src.health < src.maxhealth / 2 && initialhealth >= src.maxhealth / 2) - visible_message("\The [src] looks seriously damaged!" ) - else if(src.health < src.maxhealth * 3/4 && initialhealth >= src.maxhealth * 3/4) - visible_message("\The [src] shows signs of damage!" ) - update_icon() - return - - -/obj/machinery/door/examine(mob/user) - . = ..() - if(src.health <= 0) - . += "It is broken!" - else if(src.health < src.maxhealth / 4) - . += "It looks like it's about to break!" - else if(src.health < src.maxhealth / 2) - . += "It looks seriously damaged!" - else if(src.health < src.maxhealth * 3/4) - . += "It shows signs of damage!" - - -/obj/machinery/door/proc/set_broken() - stat |= BROKEN - for (var/mob/O in viewers(src, null)) - if ((O.client && !( O.blinded ))) - O.show_message("[src.name] breaks!" ) - update_icon() - return - - -/obj/machinery/door/emp_act(severity) - if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) - spawn(0) - open() - ..() - - -/obj/machinery/door/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if(prob(25)) - qdel(src) - else - take_damage(300) - if(3.0) - if(prob(80)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - else - take_damage(150) - return - -/obj/machinery/door/blob_act() - if(density) // If it's closed. - if(stat & BROKEN) - spawn(0) - open(1) - else - take_damage(100) - -/obj/machinery/door/update_icon() - if(density) - icon_state = "door1" - else - icon_state = "door0" - SSradiation.resistance_cache.Remove(get_turf(src)) - return - - -/obj/machinery/door/proc/do_animate(animation) - switch(animation) - if("opening") - if(p_open) - flick("o_doorc0", src) - else - flick("doorc0", src) - if("closing") - if(p_open) - flick("o_doorc1", src) - else - flick("doorc1", src) - if("spark") - if(density) - flick("door_spark", src) - if("deny") - if(density && !(stat & (NOPOWER|BROKEN))) - flick("door_deny", src) - playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) - return - - -/obj/machinery/door/proc/open(var/forced = 0) - if(!can_open(forced)) - return - operating = 1 - - do_animate("opening") - icon_state = "door0" - set_opacity(0) - sleep(anim_length_before_density) - src.density = FALSE - update_nearby_tiles() - sleep(anim_length_before_finalize) - src.layer = open_layer - explosion_resistance = 0 - update_icon() - set_opacity(0) - operating = 0 - - if(autoclose) - autoclose_in(next_close_wait()) - - return 1 - -/obj/machinery/door/proc/next_close_wait() - return (normalspeed ? 150 : 5) - -/obj/machinery/door/proc/close(var/forced = 0) - if(!can_close(forced)) - return - operating = 1 - - close_door_at = 0 - do_animate("closing") - sleep(anim_length_before_density) - src.density = TRUE - explosion_resistance = initial(explosion_resistance) - src.layer = closed_layer - update_nearby_tiles() - sleep(anim_length_before_finalize) - update_icon() - if(visible && !glass) - set_opacity(1) //caaaaarn! - operating = 0 - - //I shall not add a check every x ticks if a door has closed over some fire. - var/obj/fire/fire = locate() in loc - if(fire) - qdel(fire) - - return 1 - -/obj/machinery/door/proc/requiresID() - return 1 - -/obj/machinery/door/allowed(mob/M) - if(!requiresID()) - return ..(null) //don't care who they are or what they have, act as if they're NOTHING - return ..(M) - -/obj/machinery/door/update_nearby_tiles(need_rebuild) - if(!air_master) - return 0 - - for(var/turf/simulated/turf in locs) - update_heat_protection(turf) - air_master.mark_for_update(turf) - - return 1 - -/obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source) - if(istype(source)) - if(src.density && (src.opacity || src.heat_proof)) - source.thermal_conductivity = DOOR_HEAT_TRANSFER_COEFFICIENT - else - source.thermal_conductivity = initial(source.thermal_conductivity) - -/obj/machinery/door/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - - update_nearby_tiles() - -/obj/machinery/door/morgue - icon = 'icons/obj/doors/doormorgue.dmi' +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +#define DOOR_REPAIR_AMOUNT 50 //amount of health regained per stack amount used + +/obj/machinery/door + name = "Door" + desc = "It opens and closes." + icon = 'icons/obj/doors/Doorint.dmi' + icon_state = "door1" + anchored = TRUE + opacity = 1 + density = TRUE + can_atmos_pass = ATMOS_PASS_PROC + layer = DOOR_OPEN_LAYER + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + var/open_layer = DOOR_OPEN_LAYER + var/closed_layer = DOOR_CLOSED_LAYER + + var/visible = 1 + var/p_open = 0 + var/operating = 0 + var/autoclose = 0 + var/glass = 0 + var/normalspeed = 1 + var/heat_proof = 0 // For glass airlocks/opacity firedoors + var/air_properties_vary_with_direction = 0 + var/maxhealth = 300 + var/health + var/destroy_hits = 10 //How many strong hits it takes to destroy the door + var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon + var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon + var/repairing = 0 + var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. + var/close_door_at = 0 //When to automatically close the door, if possible + + var/anim_length_before_density = 3 + var/anim_length_before_finalize = 7 + + //Multi-tile doors + dir = EAST + var/width = 1 + + // turf animation + var/atom/movable/overlay/c_animation = null + +/obj/machinery/door/attack_generic(var/mob/user, var/damage) + if(isanimal(user)) + var/mob/living/simple_mob/S = user + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + visible_message("\The [user] smashes into [src]!") + playsound(src, S.attack_sound, 75, 1) + take_damage(damage) + else + visible_message("\The [user] bonks \the [src] harmlessly.") + user.do_attack_animation(src) + +/obj/machinery/door/New() + . = ..() + if(density) + layer = closed_layer + explosion_resistance = initial(explosion_resistance) + update_heat_protection(get_turf(src)) + else + layer = open_layer + explosion_resistance = 0 + + + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + + health = maxhealth + update_icon() + + update_nearby_tiles(need_rebuild=1) + return + +/obj/machinery/door/Destroy() + density = FALSE + update_nearby_tiles() + . = ..() + +/obj/machinery/door/process() + if(close_door_at && world.time >= close_door_at) + if(autoclose) + close_door_at = world.time + next_close_wait() + spawn(0) + close() + else + close_door_at = 0 + if (..() == PROCESS_KILL && !close_door_at) + return PROCESS_KILL + +/obj/machinery/door/proc/autoclose_in(wait) + close_door_at = world.time + wait + START_MACHINE_PROCESSING(src) + +/obj/machinery/door/proc/can_open() + if(!density || operating || !ticker) + return 0 + return 1 + +/obj/machinery/door/proc/can_close() + if(density || operating || !ticker) + return 0 + return 1 + +/obj/machinery/door/Bumped(atom/AM) + . = ..() + if(p_open || operating) + return + if(ismob(AM)) + var/mob/M = AM + if(world.time - M.last_bumped <= 10) + return //Can bump-open one airlock per second. This is to prevent shock spam. + M.last_bumped = world.time + if(M.restrained() && !check_access(null)) + return + else if(istype(M, /mob/living/simple_mob/animal/passive/mouse) && !(M.ckey)) //VOREStation Edit: Make wild mice + return //VOREStation Edit: unable to open doors + else + bumpopen(M) + if(istype(AM, /obj/item/device/uav)) + if(check_access(null)) + open() + else + do_animate("deny") + + if(istype(AM, /mob/living/bot)) + var/mob/living/bot/bot = AM + if(src.check_access(bot.botcard)) + if(density) + open() + return + + if(istype(AM, /obj/mecha)) + var/obj/mecha/mecha = AM + if(density) + if(mecha.occupant && (src.allowed(mecha.occupant) || src.check_access_list(mecha.operation_req_access))) + open() + else + do_animate("deny") + return + if(istype(AM, /obj/structure/bed/chair/wheelchair)) + var/obj/structure/bed/chair/wheelchair/wheel = AM + if(density) + if(wheel.pulling && (src.allowed(wheel.pulling))) + open() + else + do_animate("deny") + +/obj/machinery/door/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density + +/obj/machinery/door/CanZASPass(turf/T, is_zone) + if(is_zone) + return !block_air_zones // Block merging unless block_air_zones = 0 + return !density // Block airflow unless density = FALSE + +/obj/machinery/door/proc/bumpopen(mob/user as mob) + if(operating) return + if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit + return + src.add_fingerprint(user) + if(density) + if(allowed(user)) open() + else do_animate("deny") + return + +/obj/machinery/door/bullet_act(var/obj/item/projectile/Proj) + ..() + + var/damage = Proj.get_structure_damage() + + // Emitter Blasts - these will eventually completely destroy the door, given enough time. + if (damage > 90) + destroy_hits-- + if (destroy_hits <= 0) + visible_message("\The [src.name] disintegrates!") + switch (Proj.damage_type) + if(BRUTE) + new /obj/item/stack/material/steel(src.loc, 2) + new /obj/item/stack/rods(src.loc, 3) + if(BURN) + new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes! + qdel(src) + + if(damage) + //cap projectile damage so that there's still a minimum number of hits required to break the door + take_damage(min(damage, 100)) + + + +/obj/machinery/door/hitby(AM as mob|obj, var/speed=5) + + ..() + visible_message("[src.name] was hit by [AM].") + var/tforce = 0 + if(ismob(AM)) + tforce = 15 * (speed/5) + else + tforce = AM:throwforce * (speed/5) + playsound(src, hitsound, 100, 1) + take_damage(tforce) + return + +/obj/machinery/door/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/door/attack_hand(mob/user as mob) + return src.attackby(user, user) + +/obj/machinery/door/attack_tk(mob/user as mob) + if(requiresID() && !allowed(null)) + return + ..() + +/obj/machinery/door/attackby(obj/item/I as obj, mob/user as mob) + src.add_fingerprint(user) + + if(istype(I)) + if(attackby_vr(I, user)) //VOREStation begin: Fireproofing + return //VOREStation begin: Fireproofing + if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name()) + if(stat & BROKEN) + to_chat(user, "It looks like \the [src] is pretty busted. It's going to need more than just patching up now.") + return + if(health >= maxhealth) + to_chat(user, "Nothing to fix!") + return + if(!density) + to_chat(user, "\The [src] must be closed before you can repair it.") + return + + //figure out how much metal we need + var/amount_needed = (maxhealth - health) / DOOR_REPAIR_AMOUNT + amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc? + + var/obj/item/stack/stack = I + var/amount_given = amount_needed - repairing + var/mats_given = stack.get_amount() + if(repairing && amount_given <= 0) + to_chat(user, "You must weld or remove \the [get_material_name()] from \the [src] before you can add anything else.") + else + if(mats_given >= amount_given) + if(stack.use(amount_given)) + repairing += amount_given + else + if(stack.use(mats_given)) + repairing += mats_given + amount_given = mats_given + if(amount_given) + to_chat(user, "You fit [amount_given] [stack.singular_name]\s to damaged and broken parts on \the [src].") + + return + + if(repairing && I.has_tool_quality(TOOL_WELDER)) + if(!density) + to_chat(user, "\The [src] must be closed before you can repair it.") + return + + var/obj/item/weapon/weldingtool/welder = I.get_welder() + if(welder.remove_fuel(0,user)) + to_chat(user, "You start to fix dents and weld \the [get_material_name()] into place.") + playsound(src, welder.usesound, 50, 1) + if(do_after(user, (5 * repairing) * welder.toolspeed) && welder && welder.isOn()) + to_chat(user, "You finish repairing the damage to \the [src].") + health = between(health, health + repairing*DOOR_REPAIR_AMOUNT, maxhealth) + update_icon() + repairing = 0 + return + + if(repairing && I.has_tool_quality(TOOL_CROWBAR)) + var/datum/material/mat = get_material() + var/obj/item/stack/material/repairing_sheet = mat.place_sheet(loc, repairing) + repairing = 0 + to_chat(user, "You remove \the [repairing_sheet].") + playsound(src, I.usesound, 100, 1) + return + + //psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them. + if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card)) + var/obj/item/weapon/W = I + user.setClickCooldown(user.get_attack_speed(W)) + if(W.damtype == BRUTE || W.damtype == BURN) + user.do_attack_animation(src) + if(W.force < min_force) + user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") + else + user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") + playsound(src, hitsound, 100, 1) + take_damage(W.force) + return + + if(src.operating > 0 || isrobot(user)) + return //borgs can't attack doors open because it conflicts with their AI-like interaction with them. + + if(src.operating) + return + + if(src.allowed(user) && operable()) + if(src.density) + open() + else + close() + return + + if(src.density) + do_animate("deny") + return + +/obj/machinery/door/emag_act(var/remaining_charges) + if(density && operable()) + do_animate("spark") + sleep(6) + open() + operating = -1 + return 1 + +/obj/machinery/door/take_damage(var/damage) + var/initialhealth = src.health + src.health = max(0, src.health - damage) + if(src.health <= 0 && initialhealth > 0) + src.set_broken() + else if(src.health < src.maxhealth / 4 && initialhealth >= src.maxhealth / 4) + visible_message("\The [src] looks like it's about to break!" ) + else if(src.health < src.maxhealth / 2 && initialhealth >= src.maxhealth / 2) + visible_message("\The [src] looks seriously damaged!" ) + else if(src.health < src.maxhealth * 3/4 && initialhealth >= src.maxhealth * 3/4) + visible_message("\The [src] shows signs of damage!" ) + update_icon() + return + + +/obj/machinery/door/examine(mob/user) + . = ..() + if(src.health <= 0) + . += "It is broken!" + else if(src.health < src.maxhealth / 4) + . += "It looks like it's about to break!" + else if(src.health < src.maxhealth / 2) + . += "It looks seriously damaged!" + else if(src.health < src.maxhealth * 3/4) + . += "It shows signs of damage!" + + +/obj/machinery/door/proc/set_broken() + stat |= BROKEN + for (var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message("[src.name] breaks!" ) + update_icon() + return + + +/obj/machinery/door/emp_act(severity) + if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) + spawn(0) + open() + ..() + + +/obj/machinery/door/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if(prob(25)) + qdel(src) + else + take_damage(300) + if(3.0) + if(prob(80)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + else + take_damage(150) + return + +/obj/machinery/door/blob_act() + if(density) // If it's closed. + if(stat & BROKEN) + spawn(0) + open(1) + else + take_damage(100) + +/obj/machinery/door/update_icon() + if(density) + icon_state = "door1" + else + icon_state = "door0" + SSradiation.resistance_cache.Remove(get_turf(src)) + return + + +/obj/machinery/door/proc/do_animate(animation) + switch(animation) + if("opening") + if(p_open) + flick("o_doorc0", src) + else + flick("doorc0", src) + if("closing") + if(p_open) + flick("o_doorc1", src) + else + flick("doorc1", src) + if("spark") + if(density) + flick("door_spark", src) + if("deny") + if(density && !(stat & (NOPOWER|BROKEN))) + flick("door_deny", src) + playsound(src, 'sound/machines/buzz-two.ogg', 50, 0) + return + + +/obj/machinery/door/proc/open(var/forced = 0) + if(!can_open(forced)) + return + operating = 1 + + do_animate("opening") + icon_state = "door0" + set_opacity(0) + sleep(anim_length_before_density) + src.density = FALSE + update_nearby_tiles() + sleep(anim_length_before_finalize) + src.layer = open_layer + explosion_resistance = 0 + update_icon() + set_opacity(0) + operating = 0 + + if(autoclose) + autoclose_in(next_close_wait()) + + return 1 + +/obj/machinery/door/proc/next_close_wait() + return (normalspeed ? 150 : 5) + +/obj/machinery/door/proc/close(var/forced = 0) + if(!can_close(forced)) + return + operating = 1 + + close_door_at = 0 + do_animate("closing") + sleep(anim_length_before_density) + src.density = TRUE + explosion_resistance = initial(explosion_resistance) + src.layer = closed_layer + update_nearby_tiles() + sleep(anim_length_before_finalize) + update_icon() + if(visible && !glass) + set_opacity(1) //caaaaarn! + operating = 0 + + //I shall not add a check every x ticks if a door has closed over some fire. + var/obj/fire/fire = locate() in loc + if(fire) + qdel(fire) + + return 1 + +/obj/machinery/door/proc/requiresID() + return 1 + +/obj/machinery/door/allowed(mob/M) + if(!requiresID()) + return ..(null) //don't care who they are or what they have, act as if they're NOTHING + return ..(M) + +/obj/machinery/door/update_nearby_tiles(need_rebuild) + if(!air_master) + return 0 + + for(var/turf/simulated/turf in locs) + update_heat_protection(turf) + air_master.mark_for_update(turf) + + return 1 + +/obj/machinery/door/proc/update_heat_protection(var/turf/simulated/source) + if(istype(source)) + if(src.density && (src.opacity || src.heat_proof)) + source.thermal_conductivity = DOOR_HEAT_TRANSFER_COEFFICIENT + else + source.thermal_conductivity = initial(source.thermal_conductivity) + +/obj/machinery/door/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + + update_nearby_tiles() + +/obj/machinery/door/morgue + icon = 'icons/obj/doors/doormorgue.dmi' diff --git a/code/game/machinery/doors/door_vr.dm b/code/game/machinery/doors/door_vr.dm index 1a504065cc3..7a2ba190cd3 100644 --- a/code/game/machinery/doors/door_vr.dm +++ b/code/game/machinery/doors/door_vr.dm @@ -69,7 +69,7 @@ return TRUE - if(reinforcing && istype(I, /obj/item/weapon/weldingtool)) + if(reinforcing && I.has_tool_quality(TOOL_WELDER)) if(!density) to_chat(user, "\The [src] must be closed before you can reinforce it.") return TRUE @@ -78,7 +78,7 @@ to_chat(user, "You will need more plasteel to reinforce \the [src].") return TRUE - var/obj/item/weapon/weldingtool/welder = I + var/obj/item/weapon/weldingtool/welder = I.get_welder() if(welder.remove_fuel(0,user)) to_chat(user, "You start weld \the plasteel into place.") playsound(src, welder.usesound, 50, 1) @@ -89,7 +89,7 @@ reinforcing = 0 return TRUE - if(reinforcing && I.is_crowbar()) + if(reinforcing && I.has_tool_quality(TOOL_CROWBAR)) var/obj/item/stack/material/plasteel/reinforcing_sheet = new /obj/item/stack/material/plasteel(src.loc, reinforcing) reinforcing = 0 to_chat(user, "You remove \the [reinforcing_sheet].") diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 96609e02a5f..22b6f37d60b 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,521 +1,528 @@ - -#define FIREDOOR_MAX_PRESSURE_DIFF 25 // kPa -#define FIREDOOR_MAX_TEMP 50 // °C -#define FIREDOOR_MIN_TEMP 0 - -// Bitflags -#define FIREDOOR_ALERT_HOT 1 -#define FIREDOOR_ALERT_COLD 2 -// Not used #define FIREDOOR_ALERT_LOWPRESS 4 - -/obj/machinery/door/firedoor - name = "\improper Emergency Shutter" - desc = "Emergency air-tight shutter, capable of sealing off breached areas." - icon = 'icons/obj/doors/DoorHazard.dmi' - icon_state = "door_open" - req_one_access = list(access_eva) //access_atmospherics, access_engine_equip) - opacity = 0 - density = FALSE - layer = DOOR_OPEN_LAYER - 0.01 - open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open - closed_layer = DOOR_CLOSED_LAYER + 0.01 // Just above doors when closed - - //These are frequenly used with windows, so make sure zones can pass. - //Generally if a firedoor is at a place where there should be a zone boundery then there will be a regular door underneath it. - block_air_zones = 0 - - var/blocked = 0 - var/prying = 0 - var/lockdown = 0 // When the door has detected a problem, it locks. - var/pdiff_alert = 0 - var/pdiff = 0 - var/nextstate = null - var/net_id - var/list/areas_added - var/list/users_to_open = new - var/next_process_time = 0 - - var/hatch_open = 0 - - power_channel = ENVIRON - use_power = USE_POWER_IDLE - idle_power_usage = 5 - - var/list/tile_info[4] - var/list/dir_alerts[4] // 4 dirs, bitflags - - // MUST be in same order as FIREDOOR_ALERT_* - var/list/ALERT_STATES=list( - "hot", - "cold" - ) - -/obj/machinery/door/firedoor/Initialize() - . = ..() - //Delete ourselves if we find extra mapped in firedoors - for(var/obj/machinery/door/firedoor/F in loc) - if(F != src) - log_debug("Duplicate firedoors at [x],[y],[z]") - return INITIALIZE_HINT_QDEL - - var/area/A = get_area(src) - ASSERT(istype(A)) - - LAZYADD(A.all_doors, src) - areas_added = list(A) - - for(var/direction in cardinal) - A = get_area(get_step(src,direction)) - if(istype(A) && !(A in areas_added)) - LAZYADD(A.all_doors, src) - areas_added += A - -/obj/machinery/door/firedoor/Destroy() - for(var/area/A in areas_added) - LAZYREMOVE(A.all_doors, src) - . = ..() - -/obj/machinery/door/firedoor/get_material() - return get_material_by_name(MAT_STEEL) - -/obj/machinery/door/firedoor/examine(mob/user) - . = ..() - - if(!Adjacent(user)) - return . - - if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) - . += "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!" - - . += "Sensor readings:" - for(var/index = 1; index <= tile_info.len; index++) - var/o = "  " - switch(index) - if(1) - o += "NORTH: " - if(2) - o += "SOUTH: " - if(3) - o += "EAST: " - if(4) - o += "WEST: " - if(tile_info[index] == null) - o += "DATA UNAVAILABLE" - . += o - continue - var/celsius = convert_k2c(tile_info[index][1]) - var/pressure = tile_info[index][2] - o += "" - o += "[celsius]°C " - o += "" - o += "[pressure]kPa" - . += o - - if(islist(users_to_open) && users_to_open.len) - var/users_to_open_string = users_to_open[1] - if(users_to_open.len >= 2) - for(var/i = 2 to users_to_open.len) - users_to_open_string += ", [users_to_open[i]]" - . += "These people have opened \the [src] during an alert: [users_to_open_string]." - -/obj/machinery/door/firedoor/Bumped(atom/AM) - if(p_open || operating) - return - if(!density) - return ..() - if(istype(AM, /obj/mecha)) - var/obj/mecha/mecha = AM - if(mecha.occupant) - var/mob/M = mecha.occupant - if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent popup message spam. - M.last_bumped = world.time - attack_hand(M) - return 0 - -/obj/machinery/door/firedoor/attack_hand(mob/user as mob) - add_fingerprint(user) - if(operating) - return//Already doing something. - - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - src.attack_alien(user) - return - - if(blocked) - to_chat(user, "\The [src] is welded solid!") - return - - var/alarmed = lockdown - for(var/area/A in areas_added) //Checks if there are fire alarms in any areas associated with that firedoor - if(A.firedoors_closed) - alarmed = 1 - - var/answer = tgui_alert(user, "Would you like to [density ? "open" : "close"] this [src.name]?[ alarmed && density ? "\nNote that by doing so, you acknowledge any damages from opening this\n[src.name] as being your own fault, and you will be held accountable under the law." : ""]",\ - "\The [src]", list("Yes, [density ? "open" : "close"]", "No")) - if(answer == "No") - return - if(user.incapacitated() || (get_dist(src, user) > 1 && !issilicon(user))) - to_chat(user, "Sorry, you must remain able bodied and close to \the [src] in order to use it.") - return - if(density && (stat & (BROKEN|NOPOWER))) //can still close without power - to_chat(user, "\The [src] is not functioning, you'll have to force it open manually.") - return - - if(alarmed && density && lockdown && !allowed(user)) - to_chat(user, "Access denied. Please wait for authorities to arrive, or for the alert to clear.") - return - else - user.visible_message("\The [src] [density ? "open" : "close"]s for \the [user].",\ - "\The [src] [density ? "open" : "close"]s.",\ - "You hear a beep, and a door opening.") - - var/needs_to_close = 0 - if(density) - if(alarmed) - // Accountability! - users_to_open |= user.name - needs_to_close = !issilicon(user) - spawn() - open() - else - spawn() - close() - - if(needs_to_close) - spawn(50) - alarmed = 0 - for(var/area/A in areas_added) //Just in case a fire alarm is turned off while the firedoor is going through an autoclose cycle - if(A.firedoors_closed) - alarmed = 1 - if(alarmed) - nextstate = FIREDOOR_CLOSED - close() - -/obj/machinery/door/firedoor/attack_alien(var/mob/user) //Familiar, right? Doors. - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/X = user - if(istype(X.species, /datum/species/xenos)) - if(src.blocked) - visible_message("\The [user] begins digging into \the [src] internals!") - if(do_after(user,5 SECONDS,src)) - playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) - src.blocked = 0 - update_icon() - open(1) - else if(src.density) - visible_message("\The [user] begins forcing \the [src] open!") - if(do_after(user, 2 SECONDS,src)) - playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) - visible_message("\The [user] forces \the [src] open!") - open(1) - else - visible_message("\The [user] forces \the [src] closed!") - close(1) - else - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage) - if(stat & (BROKEN|NOPOWER)) - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - var/time_to_force = (2 + (2 * blocked)) * 5 - if(src.density) - visible_message("\The [user] starts forcing \the [src] open!") - user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. - if(do_after(user, time_to_force, src)) - visible_message("\The [user] forces \the [src] open!") - src.blocked = 0 - open(1) - user.set_AI_busy(FALSE) - else - time_to_force = (time_to_force / 2) - visible_message("\The [user] starts forcing \the [src] closed!") - user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. - if(do_after(user, time_to_force, src)) - visible_message("\The [user] forces \the [src] closed!") - close(1) - user.set_AI_busy(FALSE) - else - visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") - return - ..() - -/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob) - add_fingerprint(user) - if(istype(C, /obj/item/taperoll)) - return //Don't open the door if we're putting tape on it to tell people 'don't open the door'. - if(operating) - return//Already doing something. - if(istype(C, /obj/item/weapon/weldingtool) && !repairing) - if(prying) - to_chat(user, "Someone's busy prying that [density ? "open" : "closed"]!") - var/obj/item/weapon/weldingtool/W = C - if(W.remove_fuel(0, user)) - blocked = !blocked - user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ - "You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\ - "You hear something being welded.") - playsound(src, W.usesound, 100, 1) - update_icon() - return - - if(density && C.is_screwdriver()) - hatch_open = !hatch_open - playsound(src, C.usesound, 50, 1) - user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance hatch.", - "You have [hatch_open ? "opened" : "closed"] the [src] maintenance hatch.") - update_icon() - return - - if(blocked && C.is_crowbar() && !repairing) - if(!hatch_open) - to_chat(user, "You must open the maintenance hatch first!") - else - user.visible_message("[user] is removing the electronics from \the [src].", - "You start to remove the electronics from [src].") - if(do_after(user,30)) - if(blocked && density && hatch_open) - playsound(src, C.usesound, 50, 1) - user.visible_message("[user] has removed the electronics from \the [src].", - "You have removed the electronics from [src].") - - if (stat & BROKEN) - new /obj/item/weapon/circuitboard/broken(src.loc) - else - new/obj/item/weapon/circuitboard/airalarm(src.loc) - - var/obj/structure/firedoor_assembly/FA = new/obj/structure/firedoor_assembly(src.loc) - FA.anchored = TRUE - FA.density = TRUE - FA.wired = 1 - FA.glass = glass - FA.update_icon() - qdel(src) - return - - if(blocked) - to_chat(user, "\The [src] is welded shut!") - return - - if(C.pry == 1) - if(operating) - return - - if(blocked && C.is_crowbar()) - user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ - "You try to pry \the [src] [density ? "open" : "closed"], but it is welded in place!",\ - "You hear someone struggle and metal straining.") - return - - if(istype(C,/obj/item/weapon/material/twohanded/fireaxe)) - var/obj/item/weapon/material/twohanded/fireaxe/F = C - if(!F.wielded) - return - - if(prying) - to_chat(user, "Someone's already prying that [density ? "open" : "closed"].") - return - - user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ - "You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\ - "You hear metal strain.") - prying = 1 - update_icon() - playsound(src, C.usesound, 100, 1) - if(do_after(user,30 * C.toolspeed)) - if(C.is_crowbar()) - if(stat & (BROKEN|NOPOWER) || !density) - user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ - "You force \the [src] [density ? "open" : "closed"] with \the [C]!",\ - "You hear metal strain, and a door [density ? "open" : "close"].") - else - user.visible_message("\The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\ - "You force \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \the [C]!",\ - "You hear metal strain and groan, and a door [density ? "opening" : "closing"].") - if(density) - spawn(0) - open(1) - else - spawn(0) - close() - prying = 0 - update_icon() - return - - return ..() - -// CHECK PRESSURE -/obj/machinery/door/firedoor/process() - ..() - - if(!density) - return PROCESS_KILL - if(next_process_time <= world.time) - next_process_time = world.time + 100 // 10 second delays between process updates - var/changed = 0 - lockdown=0 - // Pressure alerts - pdiff = getOPressureDifferential(src.loc) - if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) - lockdown = 1 - if(!pdiff_alert) - pdiff_alert = 1 - changed = 1 // update_icon() - else - if(pdiff_alert) - pdiff_alert = 0 - changed = 1 // update_icon() - - tile_info = getCardinalAirInfo(src.loc,list("temperature","pressure")) - var/old_alerts = dir_alerts - for(var/index = 1; index <= 4; index++) - var/list/tileinfo=tile_info[index] - if(tileinfo==null) - continue // Bad data. - var/celsius = convert_k2c(tileinfo[1]) - - var/alerts=0 - - // Temperatures - if(celsius >= FIREDOOR_MAX_TEMP) - alerts |= FIREDOOR_ALERT_HOT - lockdown = 1 - else if(celsius <= FIREDOOR_MIN_TEMP) - alerts |= FIREDOOR_ALERT_COLD - lockdown = 1 - - dir_alerts[index]=alerts - - if(dir_alerts != old_alerts) - changed = 1 - if(changed) - update_icon() - -/obj/machinery/door/firedoor/proc/latetoggle() - if(operating || !nextstate) - return - switch(nextstate) - if(FIREDOOR_OPEN) - nextstate = null - - open() - if(FIREDOOR_CLOSED) - nextstate = null - close() - return - -/obj/machinery/door/firedoor/close() - latetoggle() - . = ..() - // Queue us for processing when we are closed! - if(density) - START_MACHINE_PROCESSING(src) - -/obj/machinery/door/firedoor/open(var/forced = 0) - if(hatch_open) - hatch_open = 0 - visible_message("The maintenance hatch of \the [src] closes.") - update_icon() - - if(!forced) - if(stat & (BROKEN|NOPOWER)) - return //needs power to open unless it was forced - else - use_power(360) - else - if(usr && usr.ckey) - log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.") - message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.") - latetoggle() - return ..() - -/obj/machinery/door/firedoor/do_animate(animation) - switch(animation) - if("opening") - flick("door_opening", src) - playsound(src, 'sound/machines/firelockopen.ogg', 37, 1) - if("closing") - playsound(src, 'sound/machines/firelockclose.ogg', 37, 1) - flick("door_closing", src) - return - - -/obj/machinery/door/firedoor/update_icon() - cut_overlays() - if(density) - icon_state = "door_closed" - if(prying) - icon_state = "prying_closed" - if(hatch_open) - add_overlay("hatch") - if(blocked) - add_overlay("welded") - if(pdiff_alert) - add_overlay("palert") - if(dir_alerts) - for(var/d=1;d<=4;d++) - var/cdir = cardinal[d] - for(var/i=1;i<=ALERT_STATES.len;i++) - if(dir_alerts[d] & (1<<(i-1))) - add_overlay(new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir)) - else - icon_state = "door_open" - if(prying) - icon_state = "prying_open" - if(blocked) - add_overlay("welded_open") - return - -//These are playing merry hell on ZAS. Sorry fellas :( - -/obj/machinery/door/firedoor/border_only -/* - icon = 'icons/obj/doors/edge_Doorfire.dmi' - glass = 1 //There is a glass window so you can see through the door - //This is needed due to BYOND limitations in controlling visibility - heat_proof = 1 - air_properties_vary_with_direction = 1 - - CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - return !density - else - return 1 - - CheckExit(atom/movable/mover as mob|obj, turf/target as turf) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 - - - update_nearby_tiles(need_rebuild) - if(!air_master) return 0 - - var/turf/simulated/source = loc - var/turf/simulated/destination = get_step(source,dir) - - update_heat_protection(loc) - - if(istype(source)) air_master.tiles_to_update += source - if(istype(destination)) air_master.tiles_to_update += destination - return 1 -*/ - -/obj/machinery/door/firedoor/multi_tile - icon = 'icons/obj/doors/DoorHazard2x1.dmi' - width = 2 - -/obj/machinery/door/firedoor/glass - name = "\improper Emergency Glass Shutter" - desc = "Emergency air-tight shutter, capable of sealing off breached areas. This one has a resilient glass window, allowing you to see the danger." - icon = 'icons/obj/doors/DoorHazardGlass.dmi' - icon_state = "door_open" - glass = 1 +#define FIREDOOR_MAX_PRESSURE_DIFF 25 // kPa +#define FIREDOOR_MAX_TEMP 50 // °C +#define FIREDOOR_MIN_TEMP 0 + +// Bitflags +#define FIREDOOR_ALERT_HOT 1 +#define FIREDOOR_ALERT_COLD 2 +// Not used #define FIREDOOR_ALERT_LOWPRESS 4 + +/obj/machinery/door/firedoor + name = "\improper Emergency Shutter" + desc = "Emergency air-tight shutter, capable of sealing off breached areas." + icon = 'icons/obj/doors/DoorHazard.dmi' + icon_state = "door_open" + req_one_access = list(access_eva) //access_atmospherics, access_engine_equip) + opacity = 0 + density = FALSE + layer = DOOR_OPEN_LAYER - 0.01 + open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open + closed_layer = DOOR_CLOSED_LAYER + 0.01 // Just above doors when closed + + //These are frequenly used with windows, so make sure zones can pass. + //Generally if a firedoor is at a place where there should be a zone boundery then there will be a regular door underneath it. + block_air_zones = 0 + + var/blocked = 0 + var/prying = 0 + var/lockdown = 0 // When the door has detected a problem, it locks. + var/pdiff_alert = 0 + var/pdiff = 0 + var/nextstate = null + var/net_id + var/list/areas_added + var/list/users_to_open = new + var/next_process_time = 0 + + var/hatch_open = 0 + + power_channel = ENVIRON + use_power = USE_POWER_IDLE + idle_power_usage = 5 + + var/list/tile_info[4] + var/list/dir_alerts[4] // 4 dirs, bitflags + + // MUST be in same order as FIREDOOR_ALERT_* + var/list/ALERT_STATES=list( + "hot", + "cold" + ) + +/obj/machinery/door/firedoor/Initialize() + . = ..() + //Delete ourselves if we find extra mapped in firedoors + for(var/obj/machinery/door/firedoor/F in loc) + if(F != src) + log_debug("Duplicate firedoors at [x],[y],[z]") + return INITIALIZE_HINT_QDEL + + var/area/A = get_area(src) + ASSERT(istype(A)) + + LAZYADD(A.all_doors, src) + areas_added = list(A) + + for(var/direction in cardinal) + A = get_area(get_step(src,direction)) + if(istype(A) && !(A in areas_added)) + LAZYADD(A.all_doors, src) + areas_added += A + +/obj/machinery/door/firedoor/Destroy() + for(var/area/A in areas_added) + LAZYREMOVE(A.all_doors, src) + . = ..() + +/obj/machinery/door/firedoor/get_material() + return get_material_by_name(MAT_STEEL) + +/obj/machinery/door/firedoor/examine(mob/user) + . = ..() + + if(!Adjacent(user)) + return . + + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + . += "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!" + + . += "Sensor readings:" + for(var/index = 1; index <= tile_info.len; index++) + var/o = "  " + switch(index) + if(1) + o += "NORTH: " + if(2) + o += "SOUTH: " + if(3) + o += "EAST: " + if(4) + o += "WEST: " + if(tile_info[index] == null) + o += "DATA UNAVAILABLE" + . += o + continue + var/celsius = convert_k2c(tile_info[index][1]) + var/pressure = tile_info[index][2] + o += "" + o += "[celsius]°C " + o += "" + o += "[pressure]kPa" + . += o + + if(islist(users_to_open) && users_to_open.len) + var/users_to_open_string = users_to_open[1] + if(users_to_open.len >= 2) + for(var/i = 2 to users_to_open.len) + users_to_open_string += ", [users_to_open[i]]" + . += "These people have opened \the [src] during an alert: [users_to_open_string]." + +/obj/machinery/door/firedoor/Bumped(atom/AM) + if(p_open || operating) + return + if(!density) + return ..() + if(istype(AM, /obj/mecha)) + var/obj/mecha/mecha = AM + if(mecha.occupant) + var/mob/M = mecha.occupant + if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent popup message spam. + M.last_bumped = world.time + attack_hand(M) + return 0 + +/obj/machinery/door/firedoor/attack_hand(mob/user as mob) + add_fingerprint(user) + if(operating) + return//Already doing something. + + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + src.attack_alien(user) + return + + if(blocked) + to_chat(user, "\The [src] is welded solid!") + return + + var/alarmed = lockdown + for(var/area/A in areas_added) //Checks if there are fire alarms in any areas associated with that firedoor + if(A.firedoors_closed) + alarmed = 1 + + var/answer = tgui_alert(user, "Would you like to [density ? "open" : "close"] this [src.name]?[ alarmed && density ? "\nNote that by doing so, you acknowledge any damages from opening this\n[src.name] as being your own fault, and you will be held accountable under the law." : ""]",\ + "\The [src]", list("Yes, [density ? "open" : "close"]", "No")) + if(answer == "No") + return + if(user.incapacitated() || (get_dist(src, user) > 1 && !issilicon(user))) + to_chat(user, "Sorry, you must remain able bodied and close to \the [src] in order to use it.") + return + if(density && (stat & (BROKEN|NOPOWER))) //can still close without power + to_chat(user, "\The [src] is not functioning, you'll have to force it open manually.") + return + + if(alarmed && density && lockdown && !allowed(user)) + to_chat(user, "Access denied. Please wait for authorities to arrive, or for the alert to clear.") + return + else + user.visible_message("\The [src] [density ? "open" : "close"]s for \the [user].",\ + "\The [src] [density ? "open" : "close"]s.",\ + "You hear a beep, and a door opening.") + + var/needs_to_close = 0 + if(density) + if(alarmed) + // Accountability! + users_to_open |= user.name + needs_to_close = !issilicon(user) + spawn() + open() + else + spawn() + close() + + if(needs_to_close) + spawn(50) + alarmed = 0 + for(var/area/A in areas_added) //Just in case a fire alarm is turned off while the firedoor is going through an autoclose cycle + if(A.firedoors_closed) + alarmed = 1 + if(alarmed) + nextstate = FIREDOOR_CLOSED + close() + +/obj/machinery/door/firedoor/attack_alien(var/mob/user) //Familiar, right? Doors. + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/X = user + if(istype(X.species, /datum/species/xenos)) + if(src.blocked) + visible_message("\The [user] begins digging into \the [src] internals!") + if(do_after(user,5 SECONDS,src)) + playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) + src.blocked = 0 + update_icon() + open(1) + else if(src.density) + visible_message("\The [user] begins forcing \the [src] open!") + if(do_after(user, 2 SECONDS,src)) + playsound(src, 'sound/machines/door/airlock_creaking.ogg', 100, 1) + visible_message("\The [user] forces \the [src] open!") + open(1) + else + visible_message("\The [user] forces \the [src] closed!") + close(1) + else + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage) + if(stat & (BROKEN|NOPOWER)) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + var/time_to_force = (2 + (2 * blocked)) * 5 + if(src.density) + visible_message("\The [user] starts forcing \the [src] open!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. + if(do_after(user, time_to_force, src)) + visible_message("\The [user] forces \the [src] open!") + src.blocked = 0 + open(1) + user.set_AI_busy(FALSE) + else + time_to_force = (time_to_force / 2) + visible_message("\The [user] starts forcing \the [src] closed!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. + if(do_after(user, time_to_force, src)) + visible_message("\The [user] forces \the [src] closed!") + close(1) + user.set_AI_busy(FALSE) + else + visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") + return + ..() + +/obj/machinery/door/firedoor/attackby(obj/item/weapon/C as obj, mob/user as mob) + add_fingerprint(user) + if(istype(C, /obj/item/taperoll)) + return //Don't open the door if we're putting tape on it to tell people 'don't open the door'. + if(operating) + return//Already doing something. + if(C.has_tool_quality(TOOL_WELDER) && !repairing) + if(prying) + to_chat(user, "Someone's busy prying that [density ? "open" : "closed"]!") + var/obj/item/weapon/weldingtool/W = C.get_welder() + if(W.remove_fuel(0, user)) + blocked = !blocked + user.visible_message("\The [user] [blocked ? "welds" : "unwelds"] \the [src] with \a [W].",\ + "You [blocked ? "weld" : "unweld"] \the [src] with \the [W].",\ + "You hear something being welded.") + playsound(src, W.usesound, 100, 1) + update_icon() + return + + if(density && C.has_tool_quality(TOOL_SCREWDRIVER)) + hatch_open = !hatch_open + playsound(src, C.usesound, 50, 1) + user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance hatch.", + "You have [hatch_open ? "opened" : "closed"] the [src] maintenance hatch.") + update_icon() + return + + if(blocked && C.has_tool_quality(TOOL_CROWBAR) && !repairing) + if(!hatch_open) + to_chat(user, "You must open the maintenance hatch first!") + else + user.visible_message("[user] is removing the electronics from \the [src].", + "You start to remove the electronics from [src].") + if(do_after(user,30)) + if(blocked && density && hatch_open) + playsound(src, C.usesound, 50, 1) + user.visible_message("[user] has removed the electronics from \the [src].", + "You have removed the electronics from [src].") + + if (stat & BROKEN) + new /obj/item/weapon/circuitboard/broken(src.loc) + else + new/obj/item/weapon/circuitboard/airalarm(src.loc) + + var/obj/structure/firedoor_assembly/FA = new/obj/structure/firedoor_assembly(src.loc) + FA.anchored = TRUE + FA.density = TRUE + FA.wired = 1 + FA.glass = glass + FA.update_icon() + qdel(src) + return + + if(blocked) + to_chat(user, "\The [src] is welded shut!") + return + + if(C.pry == 1) + if(operating) + return + + if(blocked && C.has_tool_quality(TOOL_CROWBAR)) + user.visible_message("\The [user] pries at \the [src] with \a [C], but \the [src] is welded in place!",\ + "You try to pry \the [src] [density ? "open" : "closed"], but it is welded in place!",\ + "You hear someone struggle and metal straining.") + return + + if(istype(C,/obj/item/weapon/material/twohanded/fireaxe)) + var/obj/item/weapon/material/twohanded/fireaxe/F = C + if(!F.wielded) + return + + if(prying) + to_chat(user, "Someone's already prying that [density ? "open" : "closed"].") + return + + user.visible_message("\The [user] starts to force \the [src] [density ? "open" : "closed"] with \a [C]!",\ + "You start forcing \the [src] [density ? "open" : "closed"] with \the [C]!",\ + "You hear metal strain.") + prying = 1 + update_icon() + playsound(src, C.usesound, 100, 1) + if(do_after(user,30 * C.toolspeed)) + if(C.has_tool_quality(TOOL_CROWBAR)) + if(stat & (BROKEN|NOPOWER) || !density) + user.visible_message("\The [user] forces \the [src] [density ? "open" : "closed"] with \a [C]!",\ + "You force \the [src] [density ? "open" : "closed"] with \the [C]!",\ + "You hear metal strain, and a door [density ? "open" : "close"].") + else + user.visible_message("\The [user] forces \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \a [C]!",\ + "You force \the [ blocked ? "welded" : "" ] [src] [density ? "open" : "closed"] with \the [C]!",\ + "You hear metal strain and groan, and a door [density ? "opening" : "closing"].") + if(density) + spawn(0) + open(1) + else + spawn(0) + close() + prying = 0 + update_icon() + return + + return ..() + +// CHECK PRESSURE +/obj/machinery/door/firedoor/process() + ..() + + if(!density) + return PROCESS_KILL + if(next_process_time <= world.time) + next_process_time = world.time + 100 // 10 second delays between process updates + var/changed = 0 + lockdown=0 + // Pressure alerts + pdiff = getOPressureDifferential(src.loc) + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + lockdown = 1 + if(!pdiff_alert) + pdiff_alert = 1 + changed = 1 // update_icon() + else + if(pdiff_alert) + pdiff_alert = 0 + changed = 1 // update_icon() + + tile_info = getCardinalAirInfo(src.loc,list("temperature","pressure")) + var/old_alerts = dir_alerts + for(var/index = 1; index <= 4; index++) + var/list/tileinfo=tile_info[index] + if(tileinfo==null) + continue // Bad data. + var/celsius = convert_k2c(tileinfo[1]) + + var/alerts=0 + + // Temperatures + if(celsius >= FIREDOOR_MAX_TEMP) + alerts |= FIREDOOR_ALERT_HOT + lockdown = 1 + else if(celsius <= FIREDOOR_MIN_TEMP) + alerts |= FIREDOOR_ALERT_COLD + lockdown = 1 + + dir_alerts[index]=alerts + + if(dir_alerts != old_alerts) + changed = 1 + if(changed) + update_icon() + +/obj/machinery/door/firedoor/proc/latetoggle() + if(operating || !nextstate) + return + switch(nextstate) + if(FIREDOOR_OPEN) + nextstate = null + + open() + if(FIREDOOR_CLOSED) + nextstate = null + close() + return + +/obj/machinery/door/firedoor/close() + latetoggle() + . = ..() + // Queue us for processing when we are closed! + if(density) + START_MACHINE_PROCESSING(src) + +/obj/machinery/door/firedoor/open(var/forced = 0) + if(hatch_open) + hatch_open = 0 + visible_message("The maintenance hatch of \the [src] closes.") + update_icon() + + if(!forced) + if(stat & (BROKEN|NOPOWER)) + return //needs power to open unless it was forced + else + use_power(360) + else + if(usr && usr.ckey) + log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.") + message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.") + latetoggle() + return ..() + +/obj/machinery/door/firedoor/do_animate(animation) + switch(animation) + if("opening") + flick("door_opening", src) + playsound(src, 'sound/machines/firelockopen.ogg', 37, 1) + if("closing") + playsound(src, 'sound/machines/firelockclose.ogg', 37, 1) + flick("door_closing", src) + return + + +/obj/machinery/door/firedoor/update_icon() + cut_overlays() + if(density) + icon_state = "door_closed" + if(prying) + icon_state = "prying_closed" + if(hatch_open) + add_overlay("hatch") + if(blocked) + add_overlay("welded") + if(pdiff_alert) + add_overlay("palert") + if(dir_alerts) + for(var/d=1;d<=4;d++) + var/cdir = cardinal[d] + for(var/i=1;i<=ALERT_STATES.len;i++) + if(dir_alerts[d] & (1<<(i-1))) + add_overlay(new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir)) + else + icon_state = "door_open" + if(prying) + icon_state = "prying_open" + if(blocked) + add_overlay("welded_open") + return + +//These are playing merry hell on ZAS. Sorry fellas :( + +/obj/machinery/door/firedoor/border_only +/* + icon = 'icons/obj/doors/edge_Doorfire.dmi' + glass = 1 //There is a glass window so you can see through the door + //This is needed due to BYOND limitations in controlling visibility + heat_proof = 1 + air_properties_vary_with_direction = 1 + + CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + return !density + else + return 1 + + CheckExit(atom/movable/mover as mob|obj, turf/target as turf) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) + return !density + else + return 1 + + + update_nearby_tiles(need_rebuild) + if(!air_master) return 0 + + var/turf/simulated/source = loc + var/turf/simulated/destination = get_step(source,dir) + + update_heat_protection(loc) + + if(istype(source)) air_master.tiles_to_update += source + if(istype(destination)) air_master.tiles_to_update += destination + return 1 +*/ + +/obj/machinery/door/firedoor/multi_tile + icon = 'icons/obj/doors/DoorHazard2x1.dmi' + width = 2 + +/obj/machinery/door/firedoor/glass + name = "\improper Emergency Glass Shutter" + desc = "Emergency air-tight shutter, capable of sealing off breached areas. This one has a resilient glass window, allowing you to see the danger." + icon = 'icons/obj/doors/DoorHazardGlass.dmi' + icon_state = "door_open" + glass = 1 + +#undef FIREDOOR_MAX_PRESSURE_DIFF +#undef FIREDOOR_MAX_TEMP +#undef FIREDOOR_MIN_TEMP + +#undef FIREDOOR_ALERT_HOT +#undef FIREDOOR_ALERT_COLD +// Not used #undef FIREDOOR_ALERT_LOWPRESS diff --git a/code/game/machinery/doors/firedoor_assembly.dm b/code/game/machinery/doors/firedoor_assembly.dm index 829cce4e7c7..e6c37c7e51a 100644 --- a/code/game/machinery/doors/firedoor_assembly.dm +++ b/code/game/machinery/doors/firedoor_assembly.dm @@ -31,7 +31,7 @@ wired = 1 to_chat(user, "You wire \the [src].") - else if(C.is_wirecutter() && wired ) + else if(C.has_tool_quality(TOOL_WIRECUTTER) && wired ) playsound(src, C.usesound, 100, 1) user.visible_message("[user] cuts the wires from \the [src].", "You start to cut the wires from \the [src].") @@ -54,14 +54,14 @@ qdel(src) else to_chat(user, "You must secure \the [src] first!") - else if(C.is_wrench()) + else if(C.has_tool_quality(TOOL_WRENCH)) anchored = !anchored playsound(src, C.usesound, 50, 1) user.visible_message("[user] has [anchored ? "" : "un" ]secured \the [src]!", "You have [anchored ? "" : "un" ]secured \the [src]!") update_icon() - else if((glass || !anchored) && istype(C, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = C + else if((glass || !anchored) && C.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = C.get_welder() if(WT.remove_fuel(0, user)) playsound(src, WT.usesound, 50, 1) if(glass) diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm index 515ed18017c..8e2a28d1dda 100644 --- a/code/game/machinery/doors/unpowered.dm +++ b/code/game/machinery/doors/unpowered.dm @@ -1,25 +1,25 @@ -/obj/machinery/door/unpowered - autoclose = 0 - var/locked = 0 - -/obj/machinery/door/unpowered/Bumped(atom/AM) - if(src.locked) - return - ..() - return - -/obj/machinery/door/unpowered/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/weapon/melee/energy/blade)) return - if(src.locked) return - ..() - return - -/obj/machinery/door/unpowered/emag_act() - return -1 - -/obj/machinery/door/unpowered/shuttle - icon = 'icons/turf/shuttle_white.dmi' - name = "door" - icon_state = "door1" - opacity = 1 - density = TRUE +/obj/machinery/door/unpowered + autoclose = 0 + var/locked = 0 + +/obj/machinery/door/unpowered/Bumped(atom/AM) + if(src.locked) + return + ..() + return + +/obj/machinery/door/unpowered/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I, /obj/item/weapon/melee/energy/blade)) return + if(src.locked) return + ..() + return + +/obj/machinery/door/unpowered/emag_act() + return -1 + +/obj/machinery/door/unpowered/shuttle + icon = 'icons/turf/shuttle_white.dmi' + name = "door" + icon_state = "door1" + opacity = 1 + density = TRUE diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 822e950d639..7b350805c25 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -1,353 +1,353 @@ -/obj/machinery/door/window - name = "interior door" - desc = "A strong door." - icon = 'icons/obj/doors/windoor.dmi' - icon_state = "left" - var/base_state = "left" - min_force = 4 - hitsound = 'sound/effects/Glasshit.ogg' - maxhealth = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file - health = 150 - visible = 0.0 - use_power = USE_POWER_OFF - flags = ON_BORDER - opacity = 0 - var/obj/item/weapon/airlock_electronics/electronics = null - explosion_resistance = 5 - can_atmos_pass = ATMOS_PASS_PROC - air_properties_vary_with_direction = 1 - -/obj/machinery/door/window/New() - ..() - update_nearby_tiles() - if(LAZYLEN(req_access)) - src.icon_state = "[src.icon_state]" - src.base_state = src.icon_state - return - -/obj/machinery/door/window/update_icon() - if(density) - icon_state = base_state - else - icon_state = "[base_state]open" - -/obj/machinery/door/window/proc/shatter(var/display_message = 1) - new /obj/item/weapon/material/shard(src.loc) - new /obj/item/weapon/material/shard(src.loc) - new /obj/item/stack/cable_coil(src.loc, 1) - var/obj/item/weapon/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/weapon/airlock_electronics( src.loc ) - if(LAZYLEN(req_access)) - ae.conf_access = req_access - else if (LAZYLEN(req_one_access)) - ae.conf_access = req_one_access - ae.one_access = 1 - else - ae = electronics - electronics = null - ae.loc = src.loc - if(operating == -1) - ae.icon_state = "door_electronics_smoked" - operating = 0 - src.density = FALSE - playsound(src, "shatter", 70, 1) - if(display_message) - visible_message("[src] shatters!") - qdel(src) - -/obj/machinery/door/window/Destroy() - density = FALSE - update_nearby_tiles() - return ..() - -/obj/machinery/door/window/Bumped(atom/movable/AM as mob|obj) - if (!( ismob(AM) )) - var/mob/living/bot/bot = AM - if(istype(bot)) - if(density && src.check_access(bot.botcard)) - open() - addtimer(CALLBACK(src, PROC_REF(close)), 50) - else if(istype(AM, /obj/mecha)) - var/obj/mecha/mecha = AM - if(density) - if(mecha.occupant && src.allowed(mecha.occupant)) - open() - addtimer(CALLBACK(src, PROC_REF(close)), 50) - return - if (!( ticker )) - return - if (src.operating) - return - if (density && allowed(AM)) - open() - addtimer(CALLBACK(src, PROC_REF(close)), check_access(null)? 50 : 20) - -/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir - return !density - return TRUE - -/obj/machinery/door/window/Uncross(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir - return !density - return TRUE - -/obj/machinery/door/window/CanZASPass(turf/T, is_zone) - if(get_dir(T, loc) == turn(dir, 180)) - if(is_zone) // No merging allowed. - return FALSE - return !density // Air can flow if open (density == FALSE). - return TRUE // Windoors don't block if not facing the right way. - -/obj/machinery/door/window/open() - if (operating == 1 || !density) //doors can still open when emag-disabled - return 0 - if (!ticker) - return 0 - if (!operating) //in case of emag - operating = 1 - flick(text("[src.base_state]opening"), src) - playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) - sleep(10) - - explosion_resistance = 0 - density = FALSE - update_icon() - update_nearby_tiles() - - if(operating == 1) //emag again - operating = 0 - return 1 - -/obj/machinery/door/window/close() - if(operating || density) - return FALSE - operating = TRUE - flick(text("[]closing", src.base_state), src) - playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) - - density = TRUE - update_icon() - explosion_resistance = initial(explosion_resistance) - update_nearby_tiles() - - sleep(10) - operating = FALSE - return TRUE - -/obj/machinery/door/window/take_damage(var/damage) - src.health = max(0, src.health - damage) - if (src.health <= 0) - shatter() - return - -/obj/machinery/door/window/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/door/window/attack_hand(mob/user as mob) - src.add_fingerprint(user) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(H)) - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("[user] smashes against the [src.name].", 1) - user.do_attack_animation(src) - user.setClickCooldown(user.get_attack_speed()) - take_damage(25) - return - - if (src.allowed(user)) - if (src.density) - open() - else - close() - - else if (src.density) - flick(text("[]deny", src.base_state), src) - - return - -/obj/machinery/door/window/emag_act(var/remaining_charges, var/mob/user) - if (density && operable()) - operating = -1 - flick("[src.base_state]spark", src) - sleep(6) - open() - return 1 - -/obj/machinery/door/window/attackby(obj/item/I as obj, mob/user as mob) - - //If it's in the process of opening/closing, ignore the click - if (src.operating == 1) - return - - if(istype(I)) - // Fixing. - if(istype(I, /obj/item/weapon/weldingtool) && user.a_intent == I_HELP) - var/obj/item/weapon/weldingtool/WT = I - if(health < maxhealth) - if(WT.remove_fuel(1 ,user)) - to_chat(user, "You begin repairing [src]...") - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 40 * WT.toolspeed, target = src)) - health = maxhealth - update_icon() - to_chat(user, "You repair [src].") - else - to_chat(user, "[src] is already in good condition!") - return - - //Emags and ninja swords? You may pass. - if (istype(I, /obj/item/weapon/melee/energy/blade)) - if(emag_act(10, user)) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, "sparks", 50, 1) - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - visible_message("The glass door was sliced open by [user]!") - return 1 - - //If it's opened/emagged, crowbar can pry it out of its frame. - if (!density && I.is_crowbar()) - playsound(src, I.usesound, 50, 1) - user.visible_message("[user] begins prying the windoor out of the frame.", "You start to pry the windoor out of the frame.") - if (do_after(user,40 * I.toolspeed)) - to_chat(user,"You pried the windoor out of the frame!") - - var/obj/structure/windoor_assembly/wa = new/obj/structure/windoor_assembly(src.loc) - if (istype(src, /obj/machinery/door/window/brigdoor)) - wa.secure = "secure_" - if (src.base_state == "right" || src.base_state == "rightsecure") - wa.facing = "r" - wa.set_dir(src.dir) - wa.anchored = TRUE - wa.created_name = name - wa.state = "02" - wa.step = 2 - wa.update_state() - - if(operating == -1) - wa.electronics = new/obj/item/weapon/circuitboard/broken() - else - if(!electronics) - wa.electronics = new/obj/item/weapon/airlock_electronics() - if(LAZYLEN(req_access)) - wa.electronics.conf_access = req_access - else if (LAZYLEN(req_one_access)) - wa.electronics.conf_access = req_one_access - wa.electronics.one_access = 1 - else - wa.electronics = electronics - electronics = null - operating = 0 - qdel(src) - return - - //If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway) - if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card)) - user.setClickCooldown(user.get_attack_speed(I)) - var/aforce = I.force - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("[src] was hit by [I].") - if(I.damtype == BRUTE || I.damtype == BURN) - take_damage(aforce) - return - - - src.add_fingerprint(user) - - if (src.allowed(user)) - if (src.density) - open() - else - close() - - else if (src.density) - flick(text("[]deny", src.base_state), src) - - return - -/obj/machinery/door/window/brigdoor - name = "secure door" - icon = 'icons/obj/doors/windoor.dmi' - icon_state = "leftsecure" - base_state = "leftsecure" - req_access = list(access_security) - var/id = null - maxhealth = 300 - health = 300.0 //Stronger doors for prison (regular window door health is 150) - -/obj/machinery/door/window/brigdoor/shatter() - new /obj/item/stack/rods(src.loc, 2) - ..() - -/obj/machinery/door/window/northleft - dir = NORTH - -/obj/machinery/door/window/eastleft - dir = EAST - -/obj/machinery/door/window/westleft - dir = WEST - -/obj/machinery/door/window/southleft - dir = SOUTH - -/obj/machinery/door/window/northright - dir = NORTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/eastright - dir = EAST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/westright - dir = WEST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/southright - dir = SOUTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/brigdoor/northleft - dir = NORTH - -/obj/machinery/door/window/brigdoor/eastleft - dir = EAST - -/obj/machinery/door/window/brigdoor/westleft - dir = WEST - -/obj/machinery/door/window/brigdoor/southleft - dir = SOUTH - -/obj/machinery/door/window/brigdoor/northright - dir = NORTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/eastright - dir = EAST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/westright - dir = WEST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/southright - dir = SOUTH - icon_state = "rightsecure" - base_state = "rightsecure" +/obj/machinery/door/window + name = "interior door" + desc = "A strong door." + icon = 'icons/obj/doors/windoor.dmi' + icon_state = "left" + var/base_state = "left" + min_force = 4 + hitsound = 'sound/effects/Glasshit.ogg' + maxhealth = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file + health = 150 + visible = 0.0 + use_power = USE_POWER_OFF + flags = ON_BORDER + opacity = 0 + var/obj/item/weapon/airlock_electronics/electronics = null + explosion_resistance = 5 + can_atmos_pass = ATMOS_PASS_PROC + air_properties_vary_with_direction = 1 + +/obj/machinery/door/window/New() + ..() + update_nearby_tiles() + if(LAZYLEN(req_access)) + src.icon_state = "[src.icon_state]" + src.base_state = src.icon_state + return + +/obj/machinery/door/window/update_icon() + if(density) + icon_state = base_state + else + icon_state = "[base_state]open" + +/obj/machinery/door/window/proc/shatter(var/display_message = 1) + new /obj/item/weapon/material/shard(src.loc) + new /obj/item/weapon/material/shard(src.loc) + new /obj/item/stack/cable_coil(src.loc, 1) + var/obj/item/weapon/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/weapon/airlock_electronics( src.loc ) + if(LAZYLEN(req_access)) + ae.conf_access = req_access + else if (LAZYLEN(req_one_access)) + ae.conf_access = req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.loc = src.loc + if(operating == -1) + ae.icon_state = "door_electronics_smoked" + operating = 0 + src.density = FALSE + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] shatters!") + qdel(src) + +/obj/machinery/door/window/Destroy() + density = FALSE + update_nearby_tiles() + return ..() + +/obj/machinery/door/window/Bumped(atom/movable/AM as mob|obj) + if (!( ismob(AM) )) + var/mob/living/bot/bot = AM + if(istype(bot)) + if(density && src.check_access(bot.botcard)) + open() + addtimer(CALLBACK(src, PROC_REF(close)), 50) + else if(istype(AM, /obj/mecha)) + var/obj/mecha/mecha = AM + if(density) + if(mecha.occupant && src.allowed(mecha.occupant)) + open() + addtimer(CALLBACK(src, PROC_REF(close)), 50) + return + if (!( ticker )) + return + if (src.operating) + return + if (density && allowed(AM)) + open() + addtimer(CALLBACK(src, PROC_REF(close)), check_access(null)? 50 : 20) + +/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir + return !density + return TRUE + +/obj/machinery/door/window/Uncross(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir + return !density + return TRUE + +/obj/machinery/door/window/CanZASPass(turf/T, is_zone) + if(get_dir(T, loc) == turn(dir, 180)) + if(is_zone) // No merging allowed. + return FALSE + return !density // Air can flow if open (density == FALSE). + return TRUE // Windoors don't block if not facing the right way. + +/obj/machinery/door/window/open() + if (operating == 1 || !density) //doors can still open when emag-disabled + return 0 + if (!ticker) + return 0 + if (!operating) //in case of emag + operating = 1 + flick(text("[src.base_state]opening"), src) + playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) + sleep(10) + + explosion_resistance = 0 + density = FALSE + update_icon() + update_nearby_tiles() + + if(operating == 1) //emag again + operating = 0 + return 1 + +/obj/machinery/door/window/close() + if(operating || density) + return FALSE + operating = TRUE + flick(text("[]closing", src.base_state), src) + playsound(src, 'sound/machines/door/windowdoor.ogg', 100, 1) + + density = TRUE + update_icon() + explosion_resistance = initial(explosion_resistance) + update_nearby_tiles() + + sleep(10) + operating = FALSE + return TRUE + +/obj/machinery/door/window/take_damage(var/damage) + src.health = max(0, src.health - damage) + if (src.health <= 0) + shatter() + return + +/obj/machinery/door/window/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/door/window/attack_hand(mob/user as mob) + src.add_fingerprint(user) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + visible_message("[user] smashes against the [src.name].", 1) + user.do_attack_animation(src) + user.setClickCooldown(user.get_attack_speed()) + take_damage(25) + return + + if (src.allowed(user)) + if (src.density) + open() + else + close() + + else if (src.density) + flick(text("[]deny", src.base_state), src) + + return + +/obj/machinery/door/window/emag_act(var/remaining_charges, var/mob/user) + if (density && operable()) + operating = -1 + flick("[src.base_state]spark", src) + sleep(6) + open() + return 1 + +/obj/machinery/door/window/attackby(obj/item/I as obj, mob/user as mob) + + //If it's in the process of opening/closing, ignore the click + if (src.operating == 1) + return + + if(istype(I)) + // Fixing. + if(I.has_tool_quality(TOOL_WELDER) && user.a_intent == I_HELP) + var/obj/item/weapon/weldingtool/WT = I.get_welder() + if(health < maxhealth) + if(WT.remove_fuel(1 ,user)) + to_chat(user, "You begin repairing [src]...") + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 40 * WT.toolspeed, target = src)) + health = maxhealth + update_icon() + to_chat(user, "You repair [src].") + else + to_chat(user, "[src] is already in good condition!") + return + + //Emags and ninja swords? You may pass. + if (istype(I, /obj/item/weapon/melee/energy/blade)) + if(emag_act(10, user)) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + visible_message("The glass door was sliced open by [user]!") + return 1 + + //If it's opened/emagged, crowbar can pry it out of its frame. + if (!density && I.has_tool_quality(TOOL_CROWBAR)) + playsound(src, I.usesound, 50, 1) + user.visible_message("[user] begins prying the windoor out of the frame.", "You start to pry the windoor out of the frame.") + if (do_after(user,40 * I.toolspeed)) + to_chat(user,"You pried the windoor out of the frame!") + + var/obj/structure/windoor_assembly/wa = new/obj/structure/windoor_assembly(src.loc) + if (istype(src, /obj/machinery/door/window/brigdoor)) + wa.secure = "secure_" + if (src.base_state == "right" || src.base_state == "rightsecure") + wa.facing = "r" + wa.set_dir(src.dir) + wa.anchored = TRUE + wa.created_name = name + wa.state = "02" + wa.step = 2 + wa.update_state() + + if(operating == -1) + wa.electronics = new/obj/item/weapon/circuitboard/broken() + else + if(!electronics) + wa.electronics = new/obj/item/weapon/airlock_electronics() + if(LAZYLEN(req_access)) + wa.electronics.conf_access = req_access + else if (LAZYLEN(req_one_access)) + wa.electronics.conf_access = req_one_access + wa.electronics.one_access = 1 + else + wa.electronics = electronics + electronics = null + operating = 0 + qdel(src) + return + + //If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway) + if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card)) + user.setClickCooldown(user.get_attack_speed(I)) + var/aforce = I.force + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + visible_message("[src] was hit by [I].") + if(I.damtype == BRUTE || I.damtype == BURN) + take_damage(aforce) + return + + + src.add_fingerprint(user) + + if (src.allowed(user)) + if (src.density) + open() + else + close() + + else if (src.density) + flick(text("[]deny", src.base_state), src) + + return + +/obj/machinery/door/window/brigdoor + name = "secure door" + icon = 'icons/obj/doors/windoor.dmi' + icon_state = "leftsecure" + base_state = "leftsecure" + req_access = list(access_security) + var/id = null + maxhealth = 300 + health = 300.0 //Stronger doors for prison (regular window door health is 150) + +/obj/machinery/door/window/brigdoor/shatter() + new /obj/item/stack/rods(src.loc, 2) + ..() + +/obj/machinery/door/window/northleft + dir = NORTH + +/obj/machinery/door/window/eastleft + dir = EAST + +/obj/machinery/door/window/westleft + dir = WEST + +/obj/machinery/door/window/southleft + dir = SOUTH + +/obj/machinery/door/window/northright + dir = NORTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/eastright + dir = EAST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/westright + dir = WEST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/southright + dir = SOUTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/brigdoor/northleft + dir = NORTH + +/obj/machinery/door/window/brigdoor/eastleft + dir = EAST + +/obj/machinery/door/window/brigdoor/westleft + dir = WEST + +/obj/machinery/door/window/brigdoor/southleft + dir = SOUTH + +/obj/machinery/door/window/brigdoor/northright + dir = NORTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/eastright + dir = EAST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/westright + dir = WEST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/southright + dir = SOUTH + icon_state = "rightsecure" + base_state = "rightsecure" diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 7b8039cb6f9..556309040f3 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -1,51 +1,51 @@ -var/list/doppler_arrays = list() - -/obj/machinery/doppler_array - anchored = TRUE - name = "tachyon-doppler array" - density = TRUE - desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." - dir = NORTH - - icon_state = "doppler" - -/obj/machinery/doppler_array/New() - ..() - doppler_arrays += src - -/obj/machinery/doppler_array/Destroy() - doppler_arrays -= src - ..() - -/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range,var/took) - if(stat & NOPOWER) return - if(z != z0) return - - var/dx = abs(x0-x) - var/dy = abs(y0-y) - var/distance - var/direct - - if(dx > dy) - distance = dx - if(x0 > x) direct = EAST - else direct = WEST - else - distance = dy - if(y0 > y) direct = NORTH - else direct = SOUTH - - if(distance > 100) return - if(!(direct & dir)) return - - var/message = "Explosive disturbance detected - Epicenter at: grid ([x0],[y0]). Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range]. Temporal displacement of tachyons: [took]seconds." - - for(var/mob/O in hearers(src, null)) - O.show_message("[src] states coldly, \"[message]\"",2) - -/obj/machinery/doppler_array/power_change() - ..() - if(!(stat & NOPOWER)) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]_off" +var/list/doppler_arrays = list() + +/obj/machinery/doppler_array + anchored = TRUE + name = "tachyon-doppler array" + density = TRUE + desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." + dir = NORTH + + icon_state = "doppler" + +/obj/machinery/doppler_array/New() + ..() + doppler_arrays += src + +/obj/machinery/doppler_array/Destroy() + doppler_arrays -= src + ..() + +/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range,var/took) + if(stat & NOPOWER) return + if(z != z0) return + + var/dx = abs(x0-x) + var/dy = abs(y0-y) + var/distance + var/direct + + if(dx > dy) + distance = dx + if(x0 > x) direct = EAST + else direct = WEST + else + distance = dy + if(y0 > y) direct = NORTH + else direct = SOUTH + + if(distance > 100) return + if(!(direct & dir)) return + + var/message = "Explosive disturbance detected - Epicenter at: grid ([x0],[y0]). Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range]. Temporal displacement of tachyons: [took]seconds." + + for(var/mob/O in hearers(src, null)) + O.show_message("[src] states coldly, \"[message]\"",2) + +/obj/machinery/doppler_array/power_change() + ..() + if(!(stat & NOPOWER)) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_off" diff --git a/code/game/machinery/embedded_controller/docking_program.dm b/code/game/machinery/embedded_controller/docking_program.dm index 1dca7665357..7c13792724a 100644 --- a/code/game/machinery/embedded_controller/docking_program.dm +++ b/code/game/machinery/embedded_controller/docking_program.dm @@ -1,309 +1,309 @@ - -#define STATE_UNDOCKED 0 -#define STATE_DOCKING 1 -#define STATE_UNDOCKING 2 -#define STATE_DOCKED 3 - -#define MODE_NONE 0 -#define MODE_SERVER 1 -#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) - -#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message - -/* - *** STATE TABLE *** - - MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. - MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. - MODE_CLIENT|STATE_DOCKED idle - docked as client. - MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. - - MODE_SERVER|STATE_UNDOCKED should never happen. - MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. - MODE_SERVER|STATE_DOCKED idle - docked as server - MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. - - MODE_NONE|STATE_UNDOCKED idle - not docked. - MODE_NONE|anything else should never happen. - - *** Docking Signals *** - - Docking - Client sends request_dock - Server sends confirm_dock to say that yes, we will serve your request - When client is ready, sends confirm_dock - Server sends confirm_dock back to indicate that docking is complete - - Undocking - Client sends request_undock - When client is ready, sends confirm_undock - Server sends confirm_undock back to indicate that docking is complete - - Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. - The client first sends a confirm message to indicate it is ready, and then finally the server will send it's - confirm message to indicate that the operation is complete. - - Note also that when docking, the server sends an additional confirm message. This is because before docking, - the server and client do not have a defined relationship. Before undocking, the server and client are already - related to each other, thus the extra confirm message is not needed. - - *** Override, what is it? *** - - The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. - Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. - This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. - - The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced - launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock - (which usually just means closing and locking the doors). - - In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. -*/ - - -/datum/embedded_program/docking - var/tag_target //the tag of the docking controller that we are trying to dock with - var/dock_state = STATE_UNDOCKED - var/control_mode = MODE_NONE - var/response_sent = 0 //so we don't spam confirmation messages - var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed - - var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually - var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client - var/docking_codes //would only allow docking when receiving signal with these, if set - var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset - -/datum/embedded_program/docking/New() - ..() - if(id_tag) - if(SSshuttles.docking_registry[id_tag]) - stack_trace("Docking controller tag [id_tag] had multiple associated programs.") - SSshuttles.docking_registry[id_tag] = src - -/datum/embedded_program/docking/Destroy() - SSshuttles.docking_registry -= id_tag - return ..() - -/datum/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) - var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id - var/command = signal.data["command"] - var/recipient = signal.data["recipient"] //the intended recipient of the docking signal - - if (recipient != id_tag) - return //this signal is not for us - - switch (command) - if ("confirm_dock") - if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) - dock_state = STATE_DOCKING - broadcast_docking_status() - if (!override_enabled) - prepare_for_docking() - - else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) - dock_state = STATE_DOCKED - broadcast_docking_status() - if (!override_enabled) - finish_docking() //client done docking! - response_sent = 0 - else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process - received_confirm = 1 - - if ("request_dock") - if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) - - tag_target = receive_tag - - if(docking_codes) - var/code = signal.data["code"] - if(code != docking_codes) - log_debug("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") - return - - control_mode = MODE_SERVER - dock_state = STATE_DOCKING - broadcast_docking_status() - - - if (!override_enabled) - prepare_for_docking() - send_docking_command(tag_target, "confirm_dock") //acknowledge the request - - if ("confirm_undock") - if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) - if (!override_enabled) - finish_undocking() - reset() //client is done undocking! - else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) - received_confirm = 1 - - if ("request_undock") - if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) - dock_state = STATE_UNDOCKING - broadcast_docking_status() - - if (!override_enabled) - prepare_for_undocking() - - if ("dock_error") - if (receive_tag == tag_target) - reset() - -/datum/embedded_program/docking/process() - switch(dock_state) - if (STATE_DOCKING) //waiting for our docking port to be ready for docking - if (ready_for_docking()) - if (control_mode == MODE_CLIENT) - if (!response_sent) - send_docking_command(tag_target, "confirm_dock") //tell the server we're ready - response_sent = 1 - - else if (control_mode == MODE_SERVER && received_confirm) - send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. - - dock_state = STATE_DOCKED - broadcast_docking_status() - - if (!override_enabled) - finish_docking() //server done docking! - response_sent = 0 - received_confirm = 0 - - if (STATE_UNDOCKING) - if (ready_for_undocking()) - if (control_mode == MODE_CLIENT) - if (!response_sent) - send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. - response_sent = 1 - - else if (control_mode == MODE_SERVER && received_confirm) - send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. - if (!override_enabled) - finish_undocking() - reset() //server is done undocking! - - if (response_sent || resend_counter > 0) - resend_counter++ - - if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) - response_sent = 0 - resend_counter = 0 - - //handle invalid states - if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) - if (tag_target) - send_docking_command(tag_target, "dock_error") - reset() - if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) - control_mode = MODE_NONE - - -/datum/embedded_program/docking/proc/initiate_docking(var/target) - if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake - return - - tag_target = target - control_mode = MODE_CLIENT - - send_docking_command(tag_target, "request_dock") - -/datum/embedded_program/docking/proc/initiate_undocking() - if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking - return - - dock_state = STATE_UNDOCKING - broadcast_docking_status() - - if (!override_enabled) - prepare_for_undocking() - - send_docking_command(tag_target, "request_undock") - -//tell the docking port to start getting ready for docking - e.g. pressurize -/datum/embedded_program/docking/proc/prepare_for_docking() - return - -//are we ready for docking? -/datum/embedded_program/docking/proc/ready_for_docking() - return 1 - -//we are docked, open the doors or whatever. -/datum/embedded_program/docking/proc/finish_docking() - return - -//tell the docking port to start getting ready for undocking - e.g. close those doors. -/datum/embedded_program/docking/proc/prepare_for_undocking() - return - -//we are docked, open the doors or whatever. -/datum/embedded_program/docking/proc/finish_undocking() - return - -//are we ready for undocking? -/datum/embedded_program/docking/proc/ready_for_undocking() - return 1 - -/datum/embedded_program/docking/proc/enable_override() - override_enabled = 1 - -/datum/embedded_program/docking/proc/disable_override() - override_enabled = 0 - -/datum/embedded_program/docking/proc/reset() - dock_state = STATE_UNDOCKED - broadcast_docking_status() - - control_mode = MODE_NONE - tag_target = null - response_sent = 0 - received_confirm = 0 - -/datum/embedded_program/docking/proc/force_undock() - //to_world("[id_tag]: forcing undock") - if (tag_target) - send_docking_command(tag_target, "dock_error") - reset() - -/datum/embedded_program/docking/proc/docked() - return (dock_state == STATE_DOCKED) - -/datum/embedded_program/docking/proc/undocked() - return (dock_state == STATE_UNDOCKED) - -//returns 1 if we are saftely undocked (and the shuttle can leave) -/datum/embedded_program/docking/proc/can_launch() - return undocked() - -/datum/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) - var/datum/signal/signal = new - signal.data["tag"] = id_tag - signal.data["command"] = command - signal.data["recipient"] = recipient - signal.data["code"] = docking_codes - post_signal(signal) - -/datum/embedded_program/docking/proc/broadcast_docking_status() - var/datum/signal/signal = new - signal.data["tag"] = id_tag - signal.data["dock_status"] = get_docking_status() - post_signal(signal) - -//this is mostly for NanoUI -/datum/embedded_program/docking/proc/get_docking_status() - switch (dock_state) - if (STATE_UNDOCKED) return "undocked" - if (STATE_DOCKING) return "docking" - if (STATE_UNDOCKING) return "undocking" - if (STATE_DOCKED) return "docked" - -/datum/embedded_program/docking/proc/get_name() - return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])" - -#undef STATE_UNDOCKED -#undef STATE_DOCKING -#undef STATE_UNDOCKING -#undef STATE_DOCKED - -#undef MODE_NONE -#undef MODE_SERVER + +#define STATE_UNDOCKED 0 +#define STATE_DOCKING 1 +#define STATE_UNDOCKING 2 +#define STATE_DOCKED 3 + +#define MODE_NONE 0 +#define MODE_SERVER 1 +#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) + +#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message + +/* + *** STATE TABLE *** + + MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. + MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. + MODE_CLIENT|STATE_DOCKED idle - docked as client. + MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. + + MODE_SERVER|STATE_UNDOCKED should never happen. + MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. + MODE_SERVER|STATE_DOCKED idle - docked as server + MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. + + MODE_NONE|STATE_UNDOCKED idle - not docked. + MODE_NONE|anything else should never happen. + + *** Docking Signals *** + + Docking + Client sends request_dock + Server sends confirm_dock to say that yes, we will serve your request + When client is ready, sends confirm_dock + Server sends confirm_dock back to indicate that docking is complete + + Undocking + Client sends request_undock + When client is ready, sends confirm_undock + Server sends confirm_undock back to indicate that docking is complete + + Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. + The client first sends a confirm message to indicate it is ready, and then finally the server will send it's + confirm message to indicate that the operation is complete. + + Note also that when docking, the server sends an additional confirm message. This is because before docking, + the server and client do not have a defined relationship. Before undocking, the server and client are already + related to each other, thus the extra confirm message is not needed. + + *** Override, what is it? *** + + The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. + Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. + This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. + + The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced + launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock + (which usually just means closing and locking the doors). + + In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. +*/ + + +/datum/embedded_program/docking + var/tag_target //the tag of the docking controller that we are trying to dock with + var/dock_state = STATE_UNDOCKED + var/control_mode = MODE_NONE + var/response_sent = 0 //so we don't spam confirmation messages + var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed + + var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually + var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client + var/docking_codes //would only allow docking when receiving signal with these, if set + var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset + +/datum/embedded_program/docking/New() + ..() + if(id_tag) + if(SSshuttles.docking_registry[id_tag]) + stack_trace("Docking controller tag [id_tag] had multiple associated programs.") + SSshuttles.docking_registry[id_tag] = src + +/datum/embedded_program/docking/Destroy() + SSshuttles.docking_registry -= id_tag + return ..() + +/datum/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) + var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id + var/command = signal.data["command"] + var/recipient = signal.data["recipient"] //the intended recipient of the docking signal + + if (recipient != id_tag) + return //this signal is not for us + + switch (command) + if ("confirm_dock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) + dock_state = STATE_DOCKING + broadcast_docking_status() + if (!override_enabled) + prepare_for_docking() + + else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) + dock_state = STATE_DOCKED + broadcast_docking_status() + if (!override_enabled) + finish_docking() //client done docking! + response_sent = 0 + else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process + received_confirm = 1 + + if ("request_dock") + if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) + + tag_target = receive_tag + + if(docking_codes) + var/code = signal.data["code"] + if(code != docking_codes) + log_debug("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") + return + + control_mode = MODE_SERVER + dock_state = STATE_DOCKING + broadcast_docking_status() + + + if (!override_enabled) + prepare_for_docking() + send_docking_command(tag_target, "confirm_dock") //acknowledge the request + + if ("confirm_undock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + if (!override_enabled) + finish_undocking() + reset() //client is done undocking! + else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + received_confirm = 1 + + if ("request_undock") + if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + if ("dock_error") + if (receive_tag == tag_target) + reset() + +/datum/embedded_program/docking/process() + switch(dock_state) + if (STATE_DOCKING) //waiting for our docking port to be ready for docking + if (ready_for_docking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_dock") //tell the server we're ready + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. + + dock_state = STATE_DOCKED + broadcast_docking_status() + + if (!override_enabled) + finish_docking() //server done docking! + response_sent = 0 + received_confirm = 0 + + if (STATE_UNDOCKING) + if (ready_for_undocking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. + if (!override_enabled) + finish_undocking() + reset() //server is done undocking! + + if (response_sent || resend_counter > 0) + resend_counter++ + + if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) + response_sent = 0 + resend_counter = 0 + + //handle invalid states + if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) + control_mode = MODE_NONE + + +/datum/embedded_program/docking/proc/initiate_docking(var/target) + if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake + return + + tag_target = target + control_mode = MODE_CLIENT + + send_docking_command(tag_target, "request_dock") + +/datum/embedded_program/docking/proc/initiate_undocking() + if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking + return + + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + send_docking_command(tag_target, "request_undock") + +//tell the docking port to start getting ready for docking - e.g. pressurize +/datum/embedded_program/docking/proc/prepare_for_docking() + return + +//are we ready for docking? +/datum/embedded_program/docking/proc/ready_for_docking() + return 1 + +//we are docked, open the doors or whatever. +/datum/embedded_program/docking/proc/finish_docking() + return + +//tell the docking port to start getting ready for undocking - e.g. close those doors. +/datum/embedded_program/docking/proc/prepare_for_undocking() + return + +//we are docked, open the doors or whatever. +/datum/embedded_program/docking/proc/finish_undocking() + return + +//are we ready for undocking? +/datum/embedded_program/docking/proc/ready_for_undocking() + return 1 + +/datum/embedded_program/docking/proc/enable_override() + override_enabled = 1 + +/datum/embedded_program/docking/proc/disable_override() + override_enabled = 0 + +/datum/embedded_program/docking/proc/reset() + dock_state = STATE_UNDOCKED + broadcast_docking_status() + + control_mode = MODE_NONE + tag_target = null + response_sent = 0 + received_confirm = 0 + +/datum/embedded_program/docking/proc/force_undock() + //to_world("[id_tag]: forcing undock") + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + +/datum/embedded_program/docking/proc/docked() + return (dock_state == STATE_DOCKED) + +/datum/embedded_program/docking/proc/undocked() + return (dock_state == STATE_UNDOCKED) + +//returns 1 if we are saftely undocked (and the shuttle can leave) +/datum/embedded_program/docking/proc/can_launch() + return undocked() + +/datum/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["command"] = command + signal.data["recipient"] = recipient + signal.data["code"] = docking_codes + post_signal(signal) + +/datum/embedded_program/docking/proc/broadcast_docking_status() + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["dock_status"] = get_docking_status() + post_signal(signal) + +//this is mostly for NanoUI +/datum/embedded_program/docking/proc/get_docking_status() + switch (dock_state) + if (STATE_UNDOCKED) return "undocked" + if (STATE_DOCKING) return "docking" + if (STATE_UNDOCKING) return "undocking" + if (STATE_DOCKED) return "docked" + +/datum/embedded_program/docking/proc/get_name() + return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])" + +#undef STATE_UNDOCKED +#undef STATE_DOCKING +#undef STATE_UNDOCKING +#undef STATE_DOCKED + +#undef MODE_NONE +#undef MODE_SERVER #undef MODE_CLIENT \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 4a05005ac56..268bde37cea 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,109 +1,109 @@ -/obj/machinery/embedded_controller - name = "Embedded Controller" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - var/datum/embedded_program/program //the currently executing program - var/list/valid_actions = list() - var/on = 1 - -/obj/machinery/embedded_controller/Initialize() - if(ispath(program)) - program = new program(src) - return ..() - -/obj/machinery/embedded_controller/Destroy() - if(istype(program)) - qdel(program) // the program will clear the ref in its Destroy - return ..() - -/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) - return 0 - -/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) - if(!signal || signal.encryption) return - - if(program) - program.receive_signal(signal, receive_method, receive_param) - -/obj/machinery/embedded_controller/Topic() - . = ..() - stack_trace("WARNING: Embedded controller [src] ([type]) had Topic() called unexpectedly. Please report this.") - -/obj/machinery/embedded_controller/tgui_act(action, params) - if(..()) - return TRUE - if(LAZYLEN(valid_actions)) - if(action in valid_actions) - program.receive_user_command(action) - if(usr) - add_fingerprint(usr) - -/obj/machinery/embedded_controller/process() - if(program) - program.process() - - update_icon() - -/obj/machinery/embedded_controller/attack_ai(mob/user as mob) - tgui_interact(user) - -/obj/machinery/embedded_controller/attack_hand(mob/user as mob) - if(!user.IsAdvancedToolUser()) - return 0 - - tgui_interact(user) - -/obj/machinery/embedded_controller/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "EmbeddedController", src) - ui.open() - -// -// Embedded controller with a radio! (Most things (All things?) use this) -// -/obj/machinery/embedded_controller/radio - icon = 'icons/obj/airlock_machines.dmi' - icon_state = "airlock_control_standby" - power_channel = ENVIRON - density = FALSE - unacidable = TRUE - - var/id_tag - //var/radio_power_use = 50 //power used to xmit signals - - var/frequency = 1379 - var/radio_filter = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/embedded_controller/radio/Initialize() - set_frequency(frequency) // Set it before parent instantiates program - . = ..() - -/obj/machinery/embedded_controller/radio/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - ..() - -/obj/machinery/embedded_controller/radio/update_icon() - if(on && program) - if(program.memory["processing"]) - icon_state = "airlock_control_process" - else - icon_state = "airlock_control_standby" - else - icon_state = "airlock_control_off" - -/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/radio_filter = null) - signal.transmission_method = TRANSMISSION_RADIO - if(radio_connection) - //use_power(radio_power_use) //neat idea, but causes way too much lag. - return radio_connection.post_signal(src, signal, radio_filter) - else - qdel(signal) - -/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency +/obj/machinery/embedded_controller + name = "Embedded Controller" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + var/datum/embedded_program/program //the currently executing program + var/list/valid_actions = list() + var/on = 1 + +/obj/machinery/embedded_controller/Initialize() + if(ispath(program)) + program = new program(src) + return ..() + +/obj/machinery/embedded_controller/Destroy() + if(istype(program)) + qdel(program) // the program will clear the ref in its Destroy + return ..() + +/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) + return 0 + +/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) + if(!signal || signal.encryption) return + + if(program) + program.receive_signal(signal, receive_method, receive_param) + +/obj/machinery/embedded_controller/Topic() + . = ..() + stack_trace("WARNING: Embedded controller [src] ([type]) had Topic() called unexpectedly. Please report this.") + +/obj/machinery/embedded_controller/tgui_act(action, params) + if(..()) + return TRUE + if(LAZYLEN(valid_actions)) + if(action in valid_actions) + program.receive_user_command(action) + if(usr) + add_fingerprint(usr) + +/obj/machinery/embedded_controller/process() + if(program) + program.process() + + update_icon() + +/obj/machinery/embedded_controller/attack_ai(mob/user as mob) + tgui_interact(user) + +/obj/machinery/embedded_controller/attack_hand(mob/user as mob) + if(!user.IsAdvancedToolUser()) + return 0 + + tgui_interact(user) + +/obj/machinery/embedded_controller/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "EmbeddedController", src) + ui.open() + +// +// Embedded controller with a radio! (Most things (All things?) use this) +// +/obj/machinery/embedded_controller/radio + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + power_channel = ENVIRON + density = FALSE + unacidable = TRUE + + var/id_tag + //var/radio_power_use = 50 //power used to xmit signals + + var/frequency = 1379 + var/radio_filter = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/embedded_controller/radio/Initialize() + set_frequency(frequency) // Set it before parent instantiates program + . = ..() + +/obj/machinery/embedded_controller/radio/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + ..() + +/obj/machinery/embedded_controller/radio/update_icon() + if(on && program) + if(program.memory["processing"]) + icon_state = "airlock_control_process" + else + icon_state = "airlock_control_standby" + else + icon_state = "airlock_control_off" + +/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/radio_filter = null) + signal.transmission_method = TRANSMISSION_RADIO + if(radio_connection) + //use_power(radio_power_use) //neat idea, but causes way too much lag. + return radio_connection.post_signal(src, signal, radio_filter) + else + qdel(signal) + +/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency radio_connection = radio_controller.add_object(src, frequency, radio_filter) \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/embedded_program_base.dm b/code/game/machinery/embedded_controller/embedded_program_base.dm index 664d60de816..7e49d792df6 100644 --- a/code/game/machinery/embedded_controller/embedded_program_base.dm +++ b/code/game/machinery/embedded_controller/embedded_program_base.dm @@ -1,31 +1,31 @@ -/datum/embedded_program - var/name - var/list/memory = list() - var/obj/machinery/embedded_controller/master - - var/id_tag - -/datum/embedded_program/New(var/obj/machinery/embedded_controller/M) - master = M - if (istype(M, /obj/machinery/embedded_controller/radio)) - var/obj/machinery/embedded_controller/radio/R = M - id_tag = R.id_tag - -/datum/embedded_program/Destroy() - if(master) - master.program = null - master = null - return ..() - -// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts) -/datum/embedded_program/proc/receive_user_command(command) - return FALSE - -/datum/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return - -/datum/embedded_program/proc/post_signal(datum/signal/signal, comm_line) - if(master) - master.post_signal(signal, comm_line) - else - qdel(signal) +/datum/embedded_program + var/name + var/list/memory = list() + var/obj/machinery/embedded_controller/master + + var/id_tag + +/datum/embedded_program/New(var/obj/machinery/embedded_controller/M) + master = M + if (istype(M, /obj/machinery/embedded_controller/radio)) + var/obj/machinery/embedded_controller/radio/R = M + id_tag = R.id_tag + +/datum/embedded_program/Destroy() + if(master) + master.program = null + master = null + return ..() + +// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts) +/datum/embedded_program/proc/receive_user_command(command) + return FALSE + +/datum/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return + +/datum/embedded_program/proc/post_signal(datum/signal/signal, comm_line) + if(master) + master.post_signal(signal, comm_line) + else + qdel(signal) diff --git a/code/game/machinery/embedded_controller/mapping_helpers.dm b/code/game/machinery/embedded_controller/mapping_helpers.dm index 768fc74beb7..0306146d388 100644 --- a/code/game/machinery/embedded_controller/mapping_helpers.dm +++ b/code/game/machinery/embedded_controller/mapping_helpers.dm @@ -26,11 +26,11 @@ Any frequency works, it's self-setting, but it seems like people have decided 13 my_controller = get_controller(get_area(src)) my_device = locate(my_device_type) in get_turf(src) if(!my_device) - to_world("WARNING:Airlock helper '[name]' couldn't find what it wanted at: X:[x] Y:[y] Z:[z]") + to_world("[span_red("WARNING:")][span_black("Airlock helper '[name]' couldn't find what it wanted at: X:[x] Y:[y] Z:[z]")]") else if(!my_controller) - to_world("WARNING:Airlock helper '[name]' couldn't find a controller at: X:[x] Y:[y] Z:[z]") + to_world("[span_red("WARNING:")][span_black("Airlock helper '[name]' couldn't find a controller at: X:[x] Y:[y] Z:[z]")]") else if(!my_controller.id_tag) - to_world("WARNING:Airlock helper '[name]' found a controller without an 'id_tag' set: X:[x] Y:[y] Z:[z]") + to_world("[span_red("WARNING:")][span_black("Airlock helper '[name]' found a controller without an 'id_tag' set: X:[x] Y:[y] Z:[z]")]") else setup() return INITIALIZE_HINT_QDEL @@ -48,15 +48,15 @@ Any frequency works, it's self-setting, but it seems like people have decided 13 for(var/obj/O in A) if(istype(O, my_controller_type)) potentials += O - + //Couldn't find one if(!potentials.len) return null - + //Only found one if(potentials.len == 1) return potentials[1] - + //Gotta find closest var/closest = potentials[potentials.len] var/closest_dist = get_dist(src, closest) diff --git a/code/game/machinery/event/stage_vr.dm b/code/game/machinery/event/stage_vr.dm index 1ed1e6c419a..24b4bd62eba 100644 --- a/code/game/machinery/event/stage_vr.dm +++ b/code/game/machinery/event/stage_vr.dm @@ -1,10 +1,10 @@ -/obj/structure/event/stage - name = "stage" - desc = "It's a stage!" - icon = 'icons/misc/event/stage.dmi' - icon_state = "stage1" - anchored = TRUE - density = FALSE - pixel_y = -224 - pixel_x = -224 +/obj/structure/event/stage + name = "stage" + desc = "It's a stage!" + icon = 'icons/misc/event/stage.dmi' + icon_state = "stage1" + anchored = TRUE + density = FALSE + pixel_y = -224 + pixel_x = -224 plane = -44 \ No newline at end of file diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm index 73c4ff2dad1..297f1421d66 100644 --- a/code/game/machinery/exonet_node.dm +++ b/code/game/machinery/exonet_node.dm @@ -78,9 +78,9 @@ // Parameters: 2 (I - the item being whacked against the machine, user - the person doing the whacking) // Description: Handles deconstruction. /obj/machinery/exonet_node/attackby(obj/item/I, mob/user) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) default_deconstruction_screwdriver(user, I) - else if(I.is_crowbar()) + else if(I.has_tool_quality(TOOL_CROWBAR)) default_deconstruction_crowbar(user, I) else ..() diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 848108285bc..47041e68750 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -1,148 +1,148 @@ -// It is a gizmo that flashes a small area -/obj/machinery/flasher - name = "Mounted flash" - desc = "A wall-mounted flashbulb device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mflash1" - layer = ABOVE_WINDOW_LAYER - var/id = null - var/range = 2 //this is roughly the size of brig cell - var/disable = 0 - var/last_flash = 0 //Don't want it getting spammed like regular flashes - var/strength = 10 //How weakened targets are when flashed. - var/base_state = "mflash" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - -/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored - name = "portable flasher" - desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." - icon_state = "pflash1" - strength = 8 - anchored = FALSE - base_state = "pflash" - density = TRUE - -/obj/machinery/flasher/power_change() - ..() - if(!(stat & NOPOWER)) - icon_state = "[base_state]1" -// sd_SetLuminosity(2) - else - icon_state = "[base_state]1-p" -// sd_SetLuminosity(0) - -//Don't want to render prison breaks impossible -/obj/machinery/flasher/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wirecutter()) - add_fingerprint(user) - disable = !disable - if(disable) - user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") - if(!disable) - user.visible_message("[user] has connected the [src]'s flashbulb!", "You connect the [src]'s flashbulb!") - -//Let the AI trigger them directly. -/obj/machinery/flasher/attack_ai() - if(anchored) - return flash() - else - return - -/obj/machinery/flasher/proc/flash() - if(!(powered())) - return - - if((disable) || (last_flash && world.time < last_flash + 150)) - return - - playsound(src, 'sound/weapons/flash.ogg', 100, 1) - flick("[base_state]_flash", src) - last_flash = world.time - use_power(1500) - - for (var/mob/O in viewers(src, null)) - if(get_dist(src, O) > range) - continue - - var/flash_time = strength - if(istype(O, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = O - //VOREStation Edit Start - if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) - H.nif.notify("High intensity light detected, and blocked!",TRUE) - continue - //VOREStation Edit End - if(!H.eyecheck() <= 0) - continue - flash_time *= H.species.flash_mod - var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] - if(!E) - return - if(E.is_bruised() && prob(E.damage + 50)) - H.flash_eyes() - E.damage += rand(1, 5) - else - if(!O.blinded && isliving(O)) - var/mob/living/L = O - L.flash_eyes() - O.Weaken(flash_time) - -/obj/machinery/flasher/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(prob(75/severity)) - flash() - ..(severity) - -/obj/machinery/flasher/portable/HasProximity(turf/T, atom/movable/AM, oldloc) - if(disable || !anchored || (last_flash && world.time < last_flash + 150)) - return - - if(iscarbon(AM)) - var/mob/living/carbon/M = AM - if(M.m_intent != "walk") - flash() - -/obj/machinery/flasher/portable/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - add_fingerprint(user) - anchored = !anchored - - if(!anchored) - user.show_message(text("[src] can now be moved.")) - cut_overlays() - unsense_proximity(callback = /atom/proc/HasProximity) - - else if(anchored) - user.show_message(text("[src] is now secured.")) - add_overlay("[base_state]-s") - sense_proximity(callback = /atom/proc/HasProximity) - -/obj/machinery/button/flasher - name = "flasher button" - desc = "A remote control switch for a mounted flasher." - -/obj/machinery/button/flasher/attack_hand(mob/user as mob) - - if(..()) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/flasher/M in machines) - if(M.id == id) - spawn() - M.flash() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - +// It is a gizmo that flashes a small area +/obj/machinery/flasher + name = "Mounted flash" + desc = "A wall-mounted flashbulb device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mflash1" + layer = ABOVE_WINDOW_LAYER + var/id = null + var/range = 2 //this is roughly the size of brig cell + var/disable = 0 + var/last_flash = 0 //Don't want it getting spammed like regular flashes + var/strength = 10 //How weakened targets are when flashed. + var/base_state = "mflash" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + +/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored + name = "portable flasher" + desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." + icon_state = "pflash1" + strength = 8 + anchored = FALSE + base_state = "pflash" + density = TRUE + +/obj/machinery/flasher/power_change() + ..() + if(!(stat & NOPOWER)) + icon_state = "[base_state]1" +// sd_SetLuminosity(2) + else + icon_state = "[base_state]1-p" +// sd_SetLuminosity(0) + +//Don't want to render prison breaks impossible +/obj/machinery/flasher/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WIRECUTTER)) + add_fingerprint(user) + disable = !disable + if(disable) + user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") + if(!disable) + user.visible_message("[user] has connected the [src]'s flashbulb!", "You connect the [src]'s flashbulb!") + +//Let the AI trigger them directly. +/obj/machinery/flasher/attack_ai() + if(anchored) + return flash() + else + return + +/obj/machinery/flasher/proc/flash() + if(!(powered())) + return + + if((disable) || (last_flash && world.time < last_flash + 150)) + return + + playsound(src, 'sound/weapons/flash.ogg', 100, 1) + flick("[base_state]_flash", src) + last_flash = world.time + use_power(1500) + + for (var/mob/O in viewers(src, null)) + if(get_dist(src, O) > range) + continue + + var/flash_time = strength + if(istype(O, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = O + //VOREStation Edit Start + if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) + H.nif.notify("High intensity light detected, and blocked!",TRUE) + continue + //VOREStation Edit End + if(!H.eyecheck() <= 0) + continue + flash_time *= H.species.flash_mod + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if(!E) + return + if(E.is_bruised() && prob(E.damage + 50)) + H.flash_eyes() + E.damage += rand(1, 5) + else + if(!O.blinded && isliving(O)) + var/mob/living/L = O + L.flash_eyes() + O.Weaken(flash_time) + +/obj/machinery/flasher/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(prob(75/severity)) + flash() + ..(severity) + +/obj/machinery/flasher/portable/HasProximity(turf/T, atom/movable/AM, oldloc) + if(disable || !anchored || (last_flash && world.time < last_flash + 150)) + return + + if(iscarbon(AM)) + var/mob/living/carbon/M = AM + if(M.m_intent != "walk") + flash() + +/obj/machinery/flasher/portable/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + add_fingerprint(user) + anchored = !anchored + + if(!anchored) + user.show_message(text("[src] can now be moved.")) + cut_overlays() + unsense_proximity(callback = /atom/proc/HasProximity) + + else if(anchored) + user.show_message(text("[src] is now secured.")) + add_overlay("[base_state]-s") + sense_proximity(callback = /atom/proc/HasProximity) + +/obj/machinery/button/flasher + name = "flasher button" + desc = "A remote control switch for a mounted flasher." + +/obj/machinery/button/flasher/attack_hand(mob/user as mob) + + if(..()) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/flasher/M in machines) + if(M.id == id) + spawn() + M.flash() + + sleep(50) + + icon_state = "launcherbtt" + active = 0 + return \ No newline at end of file diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index a5cc7d67508..3c4b3a7a136 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -102,7 +102,7 @@ update_icon() /obj/machinery/floodlight/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(!open) if(unlocked) unlocked = 0 @@ -111,7 +111,7 @@ unlocked = 1 to_chat(user, "You unscrew the battery panel.") - if(W.is_crowbar()) + if(W.has_tool_quality(TOOL_CROWBAR)) if(unlocked) if(open) open = 0 diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index a870245be9a..1722510df08 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -23,11 +23,11 @@ var/list/floor_light_cache = list() anchored = TRUE /obj/machinery/floor_light/attackby(var/obj/item/W, var/mob/user) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) anchored = !anchored visible_message("\The [user] has [anchored ? "attached" : "detached"] \the [src].") - else if(istype(W, /obj/item/weapon/weldingtool) && (damaged || (stat & BROKEN))) - var/obj/item/weapon/weldingtool/WT = W + else if(W.has_tool_quality(TOOL_WELDER) && (damaged || (stat & BROKEN))) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(!WT.remove_fuel(0, user)) to_chat(user, "\The [src] must be on to complete this task.") return diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm index dfbf3c38df7..38da6b057fc 100644 --- a/code/game/machinery/floorlayer.dm +++ b/code/game/machinery/floorlayer.dm @@ -34,7 +34,7 @@ return /obj/machinery/floorlayer/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) var/m = tgui_input_list(usr, "Choose work mode", "Mode", mode) mode[m] = !mode[m] var/O = mode[m] @@ -47,7 +47,7 @@ TakeTile(T) return - if(W.is_crowbar()) + if(W.has_tool_quality(TOOL_CROWBAR)) if(!length(contents)) to_chat(user, "\The [src] is empty.") else @@ -58,7 +58,7 @@ T = null return - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) T = tgui_input_list(usr, "Choose tile type.", "Tiles", contents) return ..() diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index 43fd737eaf6..3b6caffcf9b 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -300,7 +300,7 @@ update_icon() /obj/structure/frame/attackby(obj/item/P as obj, mob/user as mob) - if(P.is_wrench()) + if(P.has_tool_quality(TOOL_WRENCH)) if(state == FRAME_PLACED && !anchored) to_chat(user, "You start to wrench the frame into place.") playsound(src, P.usesound, 50, 1) @@ -320,9 +320,9 @@ to_chat(user, "You unfasten the frame.") anchored = FALSE - else if(istype(P, /obj/item/weapon/weldingtool)) + else if(P.has_tool_quality(TOOL_WELDER)) if(state == FRAME_PLACED) - var/obj/item/weapon/weldingtool/WT = P + var/obj/item/weapon/weldingtool/WT = P.get_welder() if(WT.remove_fuel(0, user)) playsound(src, P.usesound, 50, 1) if(do_after(user, 20 * P.toolspeed)) @@ -353,7 +353,7 @@ to_chat(user, "This frame does not accept circuit boards of this type!") return - else if(P.is_screwdriver()) + else if(P.has_tool_quality(TOOL_SCREWDRIVER)) if(state == FRAME_UNFASTENED) if(need_circuit && circuit) playsound(src, P.usesound, 50, 1) @@ -448,7 +448,7 @@ qdel(src) return - else if(P.is_crowbar()) + else if(P.has_tool_quality(TOOL_CROWBAR)) if(state == FRAME_UNFASTENED) if(need_circuit && circuit) playsound(src, P.usesound, 50, 1) @@ -525,7 +525,7 @@ break to_chat(user, desc) - else if(P.is_wirecutter()) + else if(P.has_tool_quality(TOOL_WIRECUTTER)) if(state == FRAME_WIRED) if( \ frame_type.frame_class == FRAME_CLASS_COMPUTER || \ diff --git a/code/game/machinery/gear_dispenser.dm b/code/game/machinery/gear_dispenser.dm index 315b56e8479..e410ecc8160 100644 --- a/code/game/machinery/gear_dispenser.dm +++ b/code/game/machinery/gear_dispenser.dm @@ -21,8 +21,8 @@ var/list/dispenser_presets = list() if(LAZYLEN(req_one_access)) var/accesses = user.GetAccess() return has_access(null, req_one_access, accesses) - - return 1 + + return 1 /datum/gear_disp/proc/spawn_gear(var/turf/T, var/mob/living/carbon/human/user) var/list/spawned = list() @@ -40,7 +40,7 @@ var/list/dispenser_presets = list() /datum/gear_disp/custom/allowed(var/mob/living/carbon/human/user) if(ckey_allowed && user.ckey != ckey_allowed) return 0 - + if(character_allowed && user.real_name != character_allowed) return 0 @@ -81,7 +81,7 @@ var/list/dispenser_presets = list() voidsuit = new voidsuit_type(T) spawned += voidsuit // We only add the voidsuit so the game doesn't try to put the tank/helmet/boots etc into their hands - + // If we're supposed to make a helmet if(voidhelmet_type) // The coder may not have realized this type spawns its own helmet @@ -98,7 +98,7 @@ var/list/dispenser_presets = list() else magboots = new magboots_type(voidsuit) voidsuit.boots = magboots - + if(refit) voidsuit.refit_for_species(user.species?.get_bodytype()) // does helmet and boots if they're attached @@ -112,7 +112,7 @@ var/list/dispenser_presets = list() else if(user.species?.breath_type) if(voidsuit.tank) error("[src] created a voidsuit [voidsuit] and wants to add a tank but it already has one") - else + else //Create a tank (if such a thing exists for this species) var/tanktext = "/obj/item/weapon/tank/" + "[user.species?.breath_type]" var/obj/item/weapon/tank/tankpath = text2path(tanktext) @@ -135,7 +135,7 @@ var/list/dispenser_presets = list() /datum/gear_disp/voidsuit/custom/allowed(var/mob/living/carbon/human/user) if(ckey_allowed && user.ckey != ckey_allowed) return 0 - + if(character_allowed && user.real_name != character_allowed) return 0 @@ -172,7 +172,7 @@ var/list/dispenser_presets = list() if(one_setting) one_setting = new one_setting dispenses = real_gear_list - + /obj/machinery/gear_dispenser/attack_hand(var/mob/living/carbon/human/user) if(!can_use(user)) @@ -180,18 +180,18 @@ var/list/dispenser_presets = list() dispenser_flags |= GD_BUSY if(!(dispenser_flags & GD_ONEITEM)) var/list/gear_list = get_gear_list(user) - + if(!LAZYLEN(gear_list)) to_chat(user, "\The [src] doesn't have anything to dispense for you!") dispenser_flags &= ~GD_BUSY return - + var/choice = tgui_input_list(usr, "Select equipment to dispense.", "Equipment Dispenser", gear_list) - + if(!choice) dispenser_flags &= ~GD_BUSY return - + dispense(gear_list[choice],user) else dispense(one_setting,user) @@ -223,7 +223,7 @@ var/list/dispenser_presets = list() else audible_message("!'^&YouVE alreaDY pIC&$!Ked UP yOU%r Ge^!ar.") playsound(src, 'sound/machines/buzz-sigh.ogg', 100, 0) - return 1 + return 1 // And finally if(allowed(user)) return 1 @@ -261,7 +261,7 @@ var/list/dispenser_presets = list() var/turf/T = get_turf(src) if(!(S && T)) // in case we got destroyed while we slept return 1 - + S.spawn_gear(T, user) if(emagged) @@ -323,15 +323,15 @@ var/list/dispenser_presets = list() /obj/machinery/gear_dispenser/suit_fancy/update_icon() cut_overlays() - + if(special_frame) add_overlay(special_frame) - + if(needs_power && inoperable()) add_overlay("nopower") else add_overlay("light1") - + if(held_gear_disp) add_overlay("fullsuit") if(operable()) @@ -686,7 +686,7 @@ var/list/dispenser_presets = list() "gearlist": ["/obj/random/trash"] } ]"} - + /** * Needs to be valid json with keys of: * "menuoption" = string for the name @@ -696,12 +696,12 @@ var/list/dispenser_presets = list() var/input = tgui_input_text(usr, "Paste new gear pack JSON below. See example/code comments.", "Admin-load Dispenser", example, multiline = TRUE) if(!input) return - + var/list/parsed = json_decode(input) - + if(!islist(parsed)) return - + var/list/running = list() for(var/entry in parsed) if(!islist(entry)) @@ -710,7 +710,7 @@ var/list/dispenser_presets = list() var/option_name = this_entry["menuoption"] var/list/types = this_entry["gearlist"] var/list/access = this_entry["req_one_access"] - + if(!option_name || !islist(types)) continue @@ -721,15 +721,15 @@ var/list/dispenser_presets = list() var/tnew = text2path(t) if(ispath(tnew)) types += tnew - + var/datum/gear_disp/G = new() - + G.name = option_name G.to_spawn = types - + if(LAZYLEN(access)) G.req_one_access = access - + running[option_name] = G to_chat(usr, "[src] added [running.len] entries") dispenses = running @@ -945,4 +945,10 @@ var/list/dispenser_presets = list() /datum/gear_disp/adventure_box/armor, /datum/gear_disp/adventure_box/light, /datum/gear_disp/adventure_box/weapon - ) \ No newline at end of file + ) + +#undef GD_BUSY +#undef GD_ONEITEM +#undef GD_NOGREED +#undef GD_UNLIMITED +#undef GD_UNIQUE diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 0df4174d1bb..a48fa1e79ee 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -1,259 +1,259 @@ -/* Holograms! - * Contains: - * Holopad - * Hologram - * Other stuff - */ - -/* -Revised. Original based on space ninja hologram code. Which is also mine. /N -How it works: -AI clicks on holopad in camera view. View centers on holopad. -AI clicks again on the holopad to display a hologram. Hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. -AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. -Only one AI may project from a holopad at any given time. -AI may cancel the hologram at any time by clicking on the holopad once more. - -Possible to do for anyone motivated enough: - Give an AI variable for different hologram icons. - Itegrate EMP effect to disable the unit. -*/ - -/* - * Holopad - */ -#define HOLOPAD_PASSIVE_POWER_USAGE 1 -#define HOLOGRAM_POWER_USAGE 2 -#define RANGE_BASED 4 -#define AREA_BASED 6 - -var/const/HOLOPAD_MODE = RANGE_BASED - -/obj/machinery/hologram/holopad - name = "\improper AI holopad" - desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." - icon_state = "holopad0" - show_messages = 1 - circuit = /obj/item/weapon/circuitboard/holopad - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - var/power_per_hologram = 500 //per usage per hologram - idle_power_usage = 5 - use_power = USE_POWER_IDLE - var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. - -/obj/machinery/hologram/holopad/attackby(obj/item/I as obj, user as mob) - if(computer_deconstruction_screwdriver(user, I)) - return - else - attack_hand(user) - return - -/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests. - if(!istype(user)) - return - if(tgui_alert(user,"Would you like to request an AI's presence?","Request AI",list("Yes","No")) == "Yes") - if(last_request + 200 < world.time) //don't spam the AI with requests you jerk! - last_request = world.time - to_chat(user, "You request an AI's presence.") - var/area/area = get_area(src) - for(var/mob/living/silicon/ai/AI in living_mob_list) - if(!AI.client) continue - to_chat(AI, "Your presence is requested at \the [area].") - else - to_chat(user, "A request for AI presence was already sent recently.") - -/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - /*There are pretty much only three ways to interact here. - I don't need to check for client since they're clicking on an object. - This may change in the future but for now will suffice.*/ - if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already. - user.eyeobj.setLoc(get_turf(src)) - else if(!masters[user])//If there is no hologram, possibly make one. - activate_holo(user) - else//If there is a hologram, remove it. - clear_holo(user) - return - -/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user) - if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it - if(user.holo) - to_chat(user, "ERROR: Image feed in progress.") - return - create_holo(user)//Create one. - visible_message("A holographic image of [user] flicks to life right before your eyes!") - else - to_chat(user, "ERROR: Unable to project hologram.") - return - -/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. -For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/hologram/holopad/hear_talk(mob/M, list/message_pieces, verb) - if(M && LAZYLEN(masters)) - for(var/mob/living/silicon/ai/master in masters) - if(masters[master] && M != master) - master.relay_speech(M, message_pieces, verb) - -/obj/machinery/hologram/holopad/see_emote(mob/living/M, text) - if(M) - for(var/mob/living/silicon/ai/master in masters) - //var/name_used = M.GetVoice() - var/rendered = "Holopad received, [text]" - //The lack of name_used is needed, because message already contains a name. This is needed for simple mobs to emote properly. - master.show_message(rendered, 2) - return - -/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) - for(var/mob/living/silicon/ai/master in masters) - var/rendered = "Holopad received, [msg]" - master.show_message(rendered, type) - return - -/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) - var/obj/effect/overlay/aiholo/hologram = new(T)//Spawn a blank effect at the location. //VOREStation Edit to specific type for adding vars - hologram.master = A //VOREStation Edit: So you can reference the master AI from in the hologram procs - hologram.icon = A.holo_icon - hologram.pixel_x = 16 - round(A.holo_icon.Width() / 2) //VOREStation Edit: centers the hologram on the tile - //hologram.mouse_opacity = 0//So you can't click on it. //VOREStation Removal - hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - hologram.anchored = TRUE//So space wind cannot drag it. - hologram.name = "[A.name] (Hologram)"//If someone decides to right click. - - if(!isnull(color)) - hologram.color = color - else - hologram.color = A.holo_color - - if(hologram.color) //hologram lighting - hologram.set_light(2,1,hologram.color) - else - hologram.set_light(2) - - masters[A] = hologram - set_light(2) //pad lighting - icon_state = "holopad1" - flick("holopadload", src) //VOREStation Add - A.holo = src - if(LAZYLEN(masters)) - START_MACHINE_PROCESSING(src) - return 1 - -/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user) - if(user.holo == src) - user.holo = null - qdel(masters[user])//Get rid of user's hologram - masters -= user //Discard AI from the list of those who use holopad - if(!masters.len)//If no users left - set_light(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) - icon_state = "holopad0" - return 1 - -/obj/machinery/hologram/holopad/process() - for (var/mob/living/silicon/ai/master in masters) - var/active_ai = (master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector. - if((stat & NOPOWER) || !active_ai) - clear_holo(master) - continue - - use_power(power_per_hologram) - if(..() == PROCESS_KILL && !LAZYLEN(masters)) - return PROCESS_KILL - -/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) - if(masters[user]) - /*VOREStation Removal, using our own code - step_to(masters[user], user.eyeobj) // So it turns. - var/obj/effect/overlay/H = masters[user] - H.loc = get_turf(user.eyeobj) - masters[user] = H - */ - //VOREStation Add - Solid mass holovore tracking stuff - var/obj/effect/overlay/aiholo/H = masters[user] - if(H.bellied) - walk_to(H, user.eyeobj) //Walk-to respects obstacles - else - walk_towards(H, user.eyeobj) //Walk-towards does not - //Hologram left the screen (got stuck on a wall or something) - if(get_dist(H, user.eyeobj) > world.view) - clear_holo(user) - //VOREStation Add End - if((HOLOPAD_MODE == RANGE_BASED && (get_dist(H, src) > holo_range))) - clear_holo(user) - - if(HOLOPAD_MODE == AREA_BASED) - var/area/holopad_area = get_area(src) - var/area/hologram_area = get_area(H) - - if(!(hologram_area in holopad_area)) - clear_holo(user) - - return 1 - -/* - * Hologram - */ - -/obj/machinery/hologram - icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 100 - -//Destruction procs. -/obj/machinery/hologram/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if(prob(50)) - qdel(src) - if(3.0) - if(prob(5)) - qdel(src) - return - -/obj/machinery/hologram/holopad/Destroy() - for (var/mob/living/silicon/ai/master in masters) - clear_holo(master) - return ..() - -/* -Holographic project of everything else. - -/mob/verb/hologram_test() - set name = "Hologram Debug New" - set category = "CURRENT DEBUG" - - var/obj/effect/overlay/hologram = new(loc)//Spawn a blank effect at the location. - var/icon/flat_icon = icon(getFlatIcon(src,0))//Need to make sure it's a new icon so the old one is not reused. - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - var/input = input(usr, "Select what icon state to use in effect.",,"") - if(input) - var/icon/alpha_mask = new('icons/effects/effects.dmi', "[input]") - flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. - hologram.icon = flat_icon - - to_world("Your icon should appear now.") - return -*/ - -/* - * Other Stuff: Is this even used? - */ -/obj/machinery/hologram/projector - name = "hologram projector" - desc = "It makes a hologram appear...with magnets or something..." - icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit - icon_state = "hologram0" - - -#undef RANGE_BASED -#undef AREA_BASED -#undef HOLOPAD_PASSIVE_POWER_USAGE -#undef HOLOGRAM_POWER_USAGE +/* Holograms! + * Contains: + * Holopad + * Hologram + * Other stuff + */ + +/* +Revised. Original based on space ninja hologram code. Which is also mine. /N +How it works: +AI clicks on holopad in camera view. View centers on holopad. +AI clicks again on the holopad to display a hologram. Hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. +AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. +Only one AI may project from a holopad at any given time. +AI may cancel the hologram at any time by clicking on the holopad once more. + +Possible to do for anyone motivated enough: + Give an AI variable for different hologram icons. + Itegrate EMP effect to disable the unit. +*/ + +/* + * Holopad + */ +#define HOLOPAD_PASSIVE_POWER_USAGE 1 +#define HOLOGRAM_POWER_USAGE 2 +#define RANGE_BASED 4 +#define AREA_BASED 6 + +var/const/HOLOPAD_MODE = RANGE_BASED + +/obj/machinery/hologram/holopad + name = "\improper AI holopad" + desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." + icon_state = "holopad0" + show_messages = 1 + circuit = /obj/item/weapon/circuitboard/holopad + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + var/power_per_hologram = 500 //per usage per hologram + idle_power_usage = 5 + use_power = USE_POWER_IDLE + var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad + var/last_request = 0 //to prevent request spam. ~Carn + var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. + +/obj/machinery/hologram/holopad/attackby(obj/item/I as obj, user as mob) + if(computer_deconstruction_screwdriver(user, I)) + return + else + attack_hand(user) + return + +/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests. + if(!istype(user)) + return + if(tgui_alert(user,"Would you like to request an AI's presence?","Request AI",list("Yes","No")) == "Yes") + if(last_request + 200 < world.time) //don't spam the AI with requests you jerk! + last_request = world.time + to_chat(user, "You request an AI's presence.") + var/area/area = get_area(src) + for(var/mob/living/silicon/ai/AI in living_mob_list) + if(!AI.client) continue + to_chat(AI, "Your presence is requested at \the [area].") + else + to_chat(user, "A request for AI presence was already sent recently.") + +/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + /*There are pretty much only three ways to interact here. + I don't need to check for client since they're clicking on an object. + This may change in the future but for now will suffice.*/ + if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already. + user.eyeobj.setLoc(get_turf(src)) + else if(!masters[user])//If there is no hologram, possibly make one. + activate_holo(user) + else//If there is a hologram, remove it. + clear_holo(user) + return + +/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user) + if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it + if(user.holo) + to_chat(user, "ERROR: Image feed in progress.") + return + create_holo(user)//Create one. + visible_message("A holographic image of [user] flicks to life right before your eyes!") + else + to_chat(user, "ERROR: Unable to project hologram.") + return + +/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. +For the other part of the code, check silicon say.dm. Particularly robot talk.*/ +/obj/machinery/hologram/holopad/hear_talk(mob/M, list/message_pieces, verb) + if(M && LAZYLEN(masters)) + for(var/mob/living/silicon/ai/master in masters) + if(masters[master] && M != master) + master.relay_speech(M, message_pieces, verb) + +/obj/machinery/hologram/holopad/see_emote(mob/living/M, text) + if(M) + for(var/mob/living/silicon/ai/master in masters) + //var/name_used = M.GetVoice() + var/rendered = "Holopad received, [text]" + //The lack of name_used is needed, because message already contains a name. This is needed for simple mobs to emote properly. + master.show_message(rendered, 2) + return + +/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) + for(var/mob/living/silicon/ai/master in masters) + var/rendered = "Holopad received, [msg]" + master.show_message(rendered, type) + return + +/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) + var/obj/effect/overlay/aiholo/hologram = new(T)//Spawn a blank effect at the location. //VOREStation Edit to specific type for adding vars + hologram.master = A //VOREStation Edit: So you can reference the master AI from in the hologram procs + hologram.icon = A.holo_icon + hologram.pixel_x = 16 - round(A.holo_icon.Width() / 2) //VOREStation Edit: centers the hologram on the tile + //hologram.mouse_opacity = 0//So you can't click on it. //VOREStation Removal + hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + hologram.anchored = TRUE//So space wind cannot drag it. + hologram.name = "[A.name] (Hologram)"//If someone decides to right click. + + if(!isnull(color)) + hologram.color = color + else + hologram.color = A.holo_color + + if(hologram.color) //hologram lighting + hologram.set_light(2,1,hologram.color) + else + hologram.set_light(2) + + masters[A] = hologram + set_light(2) //pad lighting + icon_state = "holopad1" + flick("holopadload", src) //VOREStation Add + A.holo = src + if(LAZYLEN(masters)) + START_MACHINE_PROCESSING(src) + return 1 + +/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user) + if(user.holo == src) + user.holo = null + qdel(masters[user])//Get rid of user's hologram + masters -= user //Discard AI from the list of those who use holopad + if(!masters.len)//If no users left + set_light(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted) + icon_state = "holopad0" + return 1 + +/obj/machinery/hologram/holopad/process() + for (var/mob/living/silicon/ai/master in masters) + var/active_ai = (master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector. + if((stat & NOPOWER) || !active_ai) + clear_holo(master) + continue + + use_power(power_per_hologram) + if(..() == PROCESS_KILL && !LAZYLEN(masters)) + return PROCESS_KILL + +/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) + if(masters[user]) + /*VOREStation Removal, using our own code + step_to(masters[user], user.eyeobj) // So it turns. + var/obj/effect/overlay/H = masters[user] + H.loc = get_turf(user.eyeobj) + masters[user] = H + */ + //VOREStation Add - Solid mass holovore tracking stuff + var/obj/effect/overlay/aiholo/H = masters[user] + if(H.bellied) + walk_to(H, user.eyeobj) //Walk-to respects obstacles + else + walk_towards(H, user.eyeobj) //Walk-towards does not + //Hologram left the screen (got stuck on a wall or something) + if(get_dist(H, user.eyeobj) > world.view) + clear_holo(user) + //VOREStation Add End + if((HOLOPAD_MODE == RANGE_BASED && (get_dist(H, src) > holo_range))) + clear_holo(user) + + if(HOLOPAD_MODE == AREA_BASED) + var/area/holopad_area = get_area(src) + var/area/hologram_area = get_area(H) + + if(!(hologram_area in holopad_area)) + clear_holo(user) + + return 1 + +/* + * Hologram + */ + +/obj/machinery/hologram + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 100 + +//Destruction procs. +/obj/machinery/hologram/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if(prob(50)) + qdel(src) + if(3.0) + if(prob(5)) + qdel(src) + return + +/obj/machinery/hologram/holopad/Destroy() + for (var/mob/living/silicon/ai/master in masters) + clear_holo(master) + return ..() + +/* +Holographic project of everything else. + +/mob/verb/hologram_test() + set name = "Hologram Debug New" + set category = "CURRENT DEBUG" + + var/obj/effect/overlay/hologram = new(loc)//Spawn a blank effect at the location. + var/icon/flat_icon = icon(getFlatIcon(src,0))//Need to make sure it's a new icon so the old one is not reused. + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + var/input = input(usr, "Select what icon state to use in effect.",,"") + if(input) + var/icon/alpha_mask = new('icons/effects/effects.dmi', "[input]") + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + hologram.icon = flat_icon + + to_world("Your icon should appear now.") + return +*/ + +/* + * Other Stuff: Is this even used? + */ +/obj/machinery/hologram/projector + name = "hologram projector" + desc = "It makes a hologram appear...with magnets or something..." + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit + icon_state = "hologram0" + + +#undef RANGE_BASED +#undef AREA_BASED +#undef HOLOPAD_PASSIVE_POWER_USAGE +#undef HOLOGRAM_POWER_USAGE diff --git a/code/game/machinery/holoposter.dm b/code/game/machinery/holoposter.dm index a5174380d2d..9b32fbb3716 100644 --- a/code/game/machinery/holoposter.dm +++ b/code/game/machinery/holoposter.dm @@ -83,7 +83,7 @@ GLOBAL_LIST_EMPTY(holoposters) src.add_fingerprint(user) if(stat & (NOPOWER)) return - if (W.is_multitool()) + if (W.has_tool_quality(TOOL_MULTITOOL)) playsound(src, 'sound/items/penclick.ogg', 60, 1) icon_state = tgui_input_list(usr, "Available Posters", "Holographic Poster", postertypes + "random") if(!Adjacent(user)) diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 060bd029fc5..461b80cf633 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -1,150 +1,150 @@ -/obj/machinery/igniter - name = "igniter" - desc = "It's useful for igniting flammable items." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "igniter1" - var/id = null - var/on = 1.0 - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/igniter/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/igniter/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - - use_power(50) - on = !(on) - icon_state = text("igniter[]", on) - return - -/obj/machinery/igniter/process() //ugh why is this even in process()? - if(on && !(stat & NOPOWER)) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/igniter/New() - ..() - icon_state = "igniter[on]" - -/obj/machinery/igniter/power_change() - ..() - if(!(stat & NOPOWER)) - icon_state = "igniter[on]" - else - icon_state = "igniter0" - -// Wall mounted remote-control igniter. - -/obj/machinery/sparker - name = "Mounted igniter" - desc = "A wall-mounted ignition device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "migniter" - layer = ABOVE_WINDOW_LAYER - var/id = null - var/disable = 0 - var/last_spark = 0 - var/base_state = "migniter" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/sparker/New() - ..() - -/obj/machinery/sparker/power_change() - ..() - if(!(stat & NOPOWER) && disable == 0) - - icon_state = "[base_state]" -// sd_SetLuminosity(2) - else - icon_state = "[base_state]-p" -// sd_SetLuminosity(0) - -/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_screwdriver()) - add_fingerprint(user) - disable = !disable - playsound(src, W.usesound, 50, 1) - if(disable) - user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") - icon_state = "[base_state]-d" - if(!disable) - user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") - if(powered()) - icon_state = "[base_state]" - else - icon_state = "[base_state]-p" - -/obj/machinery/sparker/attack_ai() - if(anchored) - return ignite() - else - return - -/obj/machinery/sparker/proc/ignite() - if(!(powered())) - return - - if((disable) || (last_spark && world.time < last_spark + 50)) - return - - flick("[base_state]-spark", src) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, src) - s.start() - last_spark = world.time - use_power(1000) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/sparker/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - ignite() - ..(severity) - -/obj/machinery/button/ignition - name = "ignition switch" - desc = "A remote control switch for a mounted igniter." - -/obj/machinery/button/ignition/attack_hand(mob/user as mob) - - if(..()) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/sparker/M in machines) - if(M.id == id) - spawn(0) - M.ignite() - - for(var/obj/machinery/igniter/M in machines) - if(M.id == id) - use_power(50) - M.on = !(M.on) - M.icon_state = text("igniter[]", M.on) - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - +/obj/machinery/igniter + name = "igniter" + desc = "It's useful for igniting flammable items." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "igniter1" + var/id = null + var/on = 1.0 + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/igniter/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/igniter/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + + use_power(50) + on = !(on) + icon_state = text("igniter[]", on) + return + +/obj/machinery/igniter/process() //ugh why is this even in process()? + if(on && !(stat & NOPOWER)) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/igniter/New() + ..() + icon_state = "igniter[on]" + +/obj/machinery/igniter/power_change() + ..() + if(!(stat & NOPOWER)) + icon_state = "igniter[on]" + else + icon_state = "igniter0" + +// Wall mounted remote-control igniter. + +/obj/machinery/sparker + name = "Mounted igniter" + desc = "A wall-mounted ignition device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "migniter" + layer = ABOVE_WINDOW_LAYER + var/id = null + var/disable = 0 + var/last_spark = 0 + var/base_state = "migniter" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/sparker/New() + ..() + +/obj/machinery/sparker/power_change() + ..() + if(!(stat & NOPOWER) && disable == 0) + + icon_state = "[base_state]" +// sd_SetLuminosity(2) + else + icon_state = "[base_state]-p" +// sd_SetLuminosity(0) + +/obj/machinery/sparker/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + add_fingerprint(user) + disable = !disable + playsound(src, W.usesound, 50, 1) + if(disable) + user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") + icon_state = "[base_state]-d" + if(!disable) + user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") + if(powered()) + icon_state = "[base_state]" + else + icon_state = "[base_state]-p" + +/obj/machinery/sparker/attack_ai() + if(anchored) + return ignite() + else + return + +/obj/machinery/sparker/proc/ignite() + if(!(powered())) + return + + if((disable) || (last_spark && world.time < last_spark + 50)) + return + + flick("[base_state]-spark", src) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + last_spark = world.time + use_power(1000) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/sparker/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + ignite() + ..(severity) + +/obj/machinery/button/ignition + name = "ignition switch" + desc = "A remote control switch for a mounted igniter." + +/obj/machinery/button/ignition/attack_hand(mob/user as mob) + + if(..()) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/sparker/M in machines) + if(M.id == id) + spawn(0) + M.ignite() + + for(var/obj/machinery/igniter/M in machines) + if(M.id == id) + use_power(50) + M.on = !(M.on) + M.icon_state = text("igniter[]", M.on) + + sleep(50) + + icon_state = "launcherbtt" + active = 0 + return \ No newline at end of file diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index f3149dc78a0..603618d1f31 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -1,185 +1,185 @@ -/obj/machinery/iv_drip - name = "\improper IV drip" - desc = "Helpful for giving someone blood! Or taking it away. It giveth, it taketh." - icon = 'icons/obj/iv_drip.dmi' - anchored = FALSE - density = FALSE - - -/obj/machinery/iv_drip/var/mob/living/carbon/human/attached = null -/obj/machinery/iv_drip/var/mode = 1 // 1 is injecting, 0 is taking blood. -/obj/machinery/iv_drip/var/obj/item/weapon/reagent_containers/beaker = null - -/obj/machinery/iv_drip/update_icon() - if(attached) - icon_state = "hooked" - else - icon_state = "" - - cut_overlays() - - if(beaker) - var/datum/reagents/reagents = beaker.reagents - if(reagents.total_volume) - var/image/filling = image('icons/obj/iv_drip.dmi', src, "reagent") - - var/percent = round((reagents.total_volume / beaker.volume) * 100) - switch(percent) - if(0 to 9) filling.icon_state = "reagent0" - if(10 to 24) filling.icon_state = "reagent10" - if(25 to 49) filling.icon_state = "reagent25" - if(50 to 74) filling.icon_state = "reagent50" - if(75 to 79) filling.icon_state = "reagent75" - if(80 to 90) filling.icon_state = "reagent80" - if(91 to INFINITY) filling.icon_state = "reagent100" - - filling.icon += reagents.get_color() - add_overlay(filling) - -/obj/machinery/iv_drip/MouseDrop(over_object, src_location, over_location) - ..() - if(!isliving(usr)) - return - - if(attached) - visible_message("[attached] is detached from \the [src]") - attached = null - update_icon() - return - - if(in_range(src, usr) && ishuman(over_object) && get_dist(over_object, src) <= 1) - visible_message("[usr] attaches \the [src] to \the [over_object].") - attached = over_object - update_icon() - - -/obj/machinery/iv_drip/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/reagent_containers)) - if(!isnull(beaker)) - to_chat(user, "There is already a reagent container loaded!") - return - - user.drop_item() - W.loc = src - beaker = W - to_chat(user, "You attach \the [W] to \the [src].") - update_icon() - return - - if(W.is_screwdriver()) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You start to dismantle the IV drip.") - if(do_after(user, 15)) - to_chat(user, "You dismantle the IV drip.") - new /obj/item/stack/rods(src.loc, 6) - if(beaker) - beaker.loc = get_turf(src) - beaker = null - qdel(src) - return - else - return ..() - - -/obj/machinery/iv_drip/process() - set background = 1 - - if(attached) - - if(!(get_dist(src, attached) <= 1 && isturf(attached.loc))) - visible_message("The needle is ripped out of [attached], doesn't that hurt?") - attached:apply_damage(3, BRUTE, pick("r_arm", "l_arm")) - attached = null - update_icon() - return - - if(attached && beaker) - // Give blood - if(mode) - if(beaker.volume > 0) - var/transfer_amount = REM - if(istype(beaker, /obj/item/weapon/reagent_containers/blood)) - // speed up transfer on blood packs - transfer_amount = 4 - beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD) - update_icon() - - // Take blood - else - var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume - amount = min(amount, 4) - // If the beaker is full, ping - if(amount == 0) - if(prob(5)) - visible_message("\The [src] pings.") - return - - var/mob/living/carbon/human/T = attached - - if(!istype(T)) - return - if(!T.dna) - return - if(NOCLONE in T.mutations) - return - - if(!T.should_have_organ(O_HEART)) - return - - // If the human is losing too much blood, beep. - if(T.vessel.get_reagent_amount("blood") < T.species.blood_volume*T.species.blood_level_safe) - visible_message("\The [src] beeps loudly.") - - var/datum/reagent/B = T.take_blood(beaker,amount) - - if(B) - beaker.reagents.reagent_list |= B - beaker.reagents.update_total() - beaker.on_reagent_change() - beaker.reagents.handle_reactions() - update_icon() - -/obj/machinery/iv_drip/attack_hand(mob/user as mob) - if(beaker) - beaker.loc = get_turf(src) - beaker = null - update_icon() - else - return ..() - - -/obj/machinery/iv_drip/verb/toggle_mode() - set category = "Object" - set name = "Toggle Mode" - set src in view(1) - - if(!istype(usr, /mob/living)) - to_chat(usr, "You can't do that.") - return - - if(usr.stat) - return - - mode = !mode - to_chat(usr, "The IV drip is now [mode ? "injecting" : "taking blood"].") - -/obj/machinery/iv_drip/examine(mob/user) - . = ..() - - if(get_dist(user, src) <= 2) - . += "The IV drip is [mode ? "injecting" : "taking blood"]." - - if(beaker) - if(beaker.reagents?.reagent_list?.len) - . += "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid." - else - . += "Attached is an empty [beaker]." - else - . += "No chemicals are attached." - - . += "[attached ? attached : "No one"] is attached." - -/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, mice, drones, and the like through. - return TRUE - return ..() +/obj/machinery/iv_drip + name = "\improper IV drip" + desc = "Helpful for giving someone blood! Or taking it away. It giveth, it taketh." + icon = 'icons/obj/iv_drip.dmi' + anchored = FALSE + density = FALSE + + +/obj/machinery/iv_drip/var/mob/living/carbon/human/attached = null +/obj/machinery/iv_drip/var/mode = 1 // 1 is injecting, 0 is taking blood. +/obj/machinery/iv_drip/var/obj/item/weapon/reagent_containers/beaker = null + +/obj/machinery/iv_drip/update_icon() + if(attached) + icon_state = "hooked" + else + icon_state = "" + + cut_overlays() + + if(beaker) + var/datum/reagents/reagents = beaker.reagents + if(reagents.total_volume) + var/image/filling = image('icons/obj/iv_drip.dmi', src, "reagent") + + var/percent = round((reagents.total_volume / beaker.volume) * 100) + switch(percent) + if(0 to 9) filling.icon_state = "reagent0" + if(10 to 24) filling.icon_state = "reagent10" + if(25 to 49) filling.icon_state = "reagent25" + if(50 to 74) filling.icon_state = "reagent50" + if(75 to 79) filling.icon_state = "reagent75" + if(80 to 90) filling.icon_state = "reagent80" + if(91 to INFINITY) filling.icon_state = "reagent100" + + filling.icon += reagents.get_color() + add_overlay(filling) + +/obj/machinery/iv_drip/MouseDrop(over_object, src_location, over_location) + ..() + if(!isliving(usr)) + return + + if(attached) + visible_message("[attached] is detached from \the [src]") + attached = null + update_icon() + return + + if(in_range(src, usr) && ishuman(over_object) && get_dist(over_object, src) <= 1) + visible_message("[usr] attaches \the [src] to \the [over_object].") + attached = over_object + update_icon() + + +/obj/machinery/iv_drip/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/reagent_containers)) + if(!isnull(beaker)) + to_chat(user, "There is already a reagent container loaded!") + return + + user.drop_item() + W.loc = src + beaker = W + to_chat(user, "You attach \the [W] to \the [src].") + update_icon() + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You start to dismantle the IV drip.") + if(do_after(user, 15)) + to_chat(user, "You dismantle the IV drip.") + new /obj/item/stack/rods(src.loc, 6) + if(beaker) + beaker.loc = get_turf(src) + beaker = null + qdel(src) + return + else + return ..() + + +/obj/machinery/iv_drip/process() + set background = 1 + + if(attached) + + if(!(get_dist(src, attached) <= 1 && isturf(attached.loc))) + visible_message("The needle is ripped out of [attached], doesn't that hurt?") + attached:apply_damage(3, BRUTE, pick("r_arm", "l_arm")) + attached = null + update_icon() + return + + if(attached && beaker) + // Give blood + if(mode) + if(beaker.volume > 0) + var/transfer_amount = REM + if(istype(beaker, /obj/item/weapon/reagent_containers/blood)) + // speed up transfer on blood packs + transfer_amount = 4 + beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD) + update_icon() + + // Take blood + else + var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume + amount = min(amount, 4) + // If the beaker is full, ping + if(amount == 0) + if(prob(5)) + visible_message("\The [src] pings.") + return + + var/mob/living/carbon/human/T = attached + + if(!istype(T)) + return + if(!T.dna) + return + if(NOCLONE in T.mutations) + return + + if(!T.should_have_organ(O_HEART)) + return + + // If the human is losing too much blood, beep. + if(T.vessel.get_reagent_amount("blood") < T.species.blood_volume*T.species.blood_level_safe) + visible_message("\The [src] beeps loudly.") + + var/datum/reagent/B = T.take_blood(beaker,amount) + + if(B) + beaker.reagents.reagent_list |= B + beaker.reagents.update_total() + beaker.on_reagent_change() + beaker.reagents.handle_reactions() + update_icon() + +/obj/machinery/iv_drip/attack_hand(mob/user as mob) + if(beaker) + beaker.loc = get_turf(src) + beaker = null + update_icon() + else + return ..() + + +/obj/machinery/iv_drip/verb/toggle_mode() + set category = "Object" + set name = "Toggle Mode" + set src in view(1) + + if(!istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(usr.stat) + return + + mode = !mode + to_chat(usr, "The IV drip is now [mode ? "injecting" : "taking blood"].") + +/obj/machinery/iv_drip/examine(mob/user) + . = ..() + + if(get_dist(user, src) <= 2) + . += "The IV drip is [mode ? "injecting" : "taking blood"]." + + if(beaker) + if(beaker.reagents?.reagent_list?.len) + . += "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid." + else + . += "Attached is an empty [beaker]." + else + . += "No chemicals are attached." + + . += "[attached ? attached : "No one"] is attached." + +/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, mice, drones, and the like through. + return TRUE + return ..() diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 0e184683ec0..52bbb8c6345 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -108,11 +108,11 @@ return if(default_deconstruction_crowbar(user, W)) return - if(W.is_wirecutter()) + if(W.has_tool_quality(TOOL_WIRECUTTER)) return wires.Interact(user) if(istype(W, /obj/item/device/multitool)) return wires.Interact(user) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) if(playing) StopPlaying() user.visible_message("[user] has [anchored ? "un" : ""]secured \the [src].", "You [anchored ? "un" : ""]secure \the [src].") @@ -270,7 +270,7 @@ return if(default_deconstruction_crowbar(user, W)) return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) if(playing) StopPlaying() user.visible_message("[user] has [anchored ? "un" : ""]secured \the [src].", "You [anchored ? "un" : ""]secure \the [src].") diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 9a535161451..5340bd2dd5e 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -1,89 +1,89 @@ -// the light switch -// can have multiple per area -// can also operate on non-loc area through "otherarea" var -/obj/machinery/light_switch - name = "light switch" - desc = "It turns lights on and off. What are you, simple?" - icon = 'icons/obj/power_vr.dmi' // VOREStation Edit - icon_state = "light1" - layer = ABOVE_WINDOW_LAYER - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - power_channel = LIGHT - blocks_emissive = FALSE - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - var/on = 1 - var/area/area = null - var/otherarea = null - var/image/overlay - -/obj/machinery/light_switch/Initialize() - . = ..() - - area = get_area(src) - - if(otherarea) - area = locate(text2path("/area/[otherarea]")) - - if(!name) - name = "light switch ([area.name])" - - on = area.lightswitch - update_icon() - -/obj/machinery/light_switch/Destroy() - area = null - overlay = null - return ..() - -/obj/machinery/light_switch/update_icon() - cut_overlays() - if(stat & NOPOWER) - icon_state = "light-p" - set_light(0) - else - icon_state = "light[on]" - set_light(2, 0.1, on ? "#82FF4C" : "#F86060") - . = list() - . += emissive_appearance(icon, "light[on]-overlay") - - return add_overlay(.) - - -/obj/machinery/light_switch/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "A light switch. It is [on? "on" : "off"]." - -/obj/machinery/light_switch/attack_hand(mob/user) - - on = !on - - area.lightswitch = on - area.update_icon() - playsound(src, 'sound/machines/button.ogg', 100, 1, 0) // VOREStation Edit - - for(var/obj/machinery/light_switch/L in area) - L.on = on - L.update_icon() - - area.power_change() - GLOB.lights_switched_on_roundstat++ - -/obj/machinery/light_switch/power_change() - - if(!otherarea) - if(powered(LIGHT)) - stat &= ~NOPOWER - else - stat |= NOPOWER - - update_icon() - -/obj/machinery/light_switch/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - power_change() +// the light switch +// can have multiple per area +// can also operate on non-loc area through "otherarea" var +/obj/machinery/light_switch + name = "light switch" + desc = "It turns lights on and off. What are you, simple?" + icon = 'icons/obj/power_vr.dmi' // VOREStation Edit + icon_state = "light1" + layer = ABOVE_WINDOW_LAYER + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + power_channel = LIGHT + blocks_emissive = FALSE + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + var/on = 1 + var/area/area = null + var/otherarea = null + var/image/overlay + +/obj/machinery/light_switch/Initialize() + . = ..() + + area = get_area(src) + + if(otherarea) + area = locate(text2path("/area/[otherarea]")) + + if(!name) + name = "light switch ([area.name])" + + on = area.lightswitch + update_icon() + +/obj/machinery/light_switch/Destroy() + area = null + overlay = null + return ..() + +/obj/machinery/light_switch/update_icon() + cut_overlays() + if(stat & NOPOWER) + icon_state = "light-p" + set_light(0) + else + icon_state = "light[on]" + set_light(2, 0.1, on ? "#82FF4C" : "#F86060") + . = list() + . += emissive_appearance(icon, "light[on]-overlay") + + return add_overlay(.) + + +/obj/machinery/light_switch/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "A light switch. It is [on? "on" : "off"]." + +/obj/machinery/light_switch/attack_hand(mob/user) + + on = !on + + area.lightswitch = on + area.update_icon() + playsound(src, 'sound/machines/button.ogg', 100, 1, 0) // VOREStation Edit + + for(var/obj/machinery/light_switch/L in area) + L.on = on + L.update_icon() + + area.power_change() + GLOB.lights_switched_on_roundstat++ + +/obj/machinery/light_switch/power_change() + + if(!otherarea) + if(powered(LIGHT)) + stat &= ~NOPOWER + else + stat |= NOPOWER + + update_icon() + +/obj/machinery/light_switch/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + power_change() ..(severity) \ No newline at end of file diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 6ceab538950..b275821a593 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -1,571 +1,571 @@ -/* -Overview: - Used to create objects that need a per step proc call. Default definition of 'New()' - stores a reference to src machine in global 'machines list'. Default definition - of 'Del' removes reference to src machine in global 'machines list'. - -Class Variables: - - power_init_complete (boolean) - Indicates that we have have registered our static power usage with the area. - - use_power (num) - current state of auto power use. - Possible Values: - USE_POWER_OFF:0 -- no auto power use - USE_POWER_IDLE:1 -- machine is using power at its idle power level - USE_POWER_ACTIVE:2 -- machine is using power at its active power level - - active_power_usage (num) - Value for the amount of power to use when in active power mode - - idle_power_usage (num) - Value for the amount of power to use when in idle power mode - - power_channel (num) - What channel to draw from when drawing power for power mode - Possible Values: - EQUIP:0 -- Equipment Channel - LIGHT:2 -- Lighting Channel - ENVIRON:3 -- Environment Channel - - component_parts (list) - A list of component parts of machine used by frame based machines. - - panel_open (num) - Whether the panel is open - - uid (num) - Unique id of machine across all machines. - - gl_uid (global num) - Next uid value in sequence - - stat (bitflag) - Machine status bit flags. - Possible bit flags: - BROKEN:1 -- Machine is broken - NOPOWER:2 -- No power is being supplied to machine. - POWEROFF:4 -- tbd - MAINT:8 -- machine is currently under going maintenance. - EMPED:16 -- temporary broken by EMP pulse - -Class Procs: - New() 'game/machinery/machine.dm' - - Destroy() 'game/machinery/machine.dm' - - get_power_usage() 'game/machinery/machinery_power.dm' - Returns the amount of power this machine uses every SSmachines cycle. - Default definition uses 'use_power', 'active_power_usage', 'idle_power_usage' - - powered(chan = CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' - Checks to see if area that contains the object has power available for power - channel given in 'chan'. - - use_power_oneoff(amount, chan=CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' - Deducts 'amount' from the power channel 'chan' of the area that contains the object. - - power_change() 'game/machinery/machinery_power.dm' - Called by the area that contains the object when ever that area under goes a - power state change (area runs out of power, or area channel is turned off). - - RefreshParts() 'game/machinery/machine.dm' - Called to refresh the variables in the machine that are contributed to by parts - contained in the component_parts list. (example: glass and material amounts for - the autolathe) - - Default definition does nothing. - - assign_uid() 'game/machinery/machine.dm' - Called by machine to assign a value to the uid variable. - - process() 'game/machinery/machine.dm' - Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. - - - Compiled by Aygar -*/ - -/obj/machinery - name = "machinery" - icon = 'icons/obj/stationobjs.dmi' - w_class = ITEMSIZE_NO_CONTAINER - layer = UNDER_JUNK_LAYER - - var/stat = 0 - var/emagged = 0 - var/use_power = USE_POWER_IDLE - //0 = dont run the auto - //1 = run auto, use idle - //2 = run auto, use active - var/idle_power_usage = 0 - var/active_power_usage = 0 - var/power_channel = EQUIP //EQUIP, ENVIRON or LIGHT - var/power_init_complete = FALSE - var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. - var/uid - var/panel_open = FALSE - var/global/gl_uid = 1 - var/clicksound // sound played on succesful interface. Just put it in the list of vars at the start. - var/clickvol = 40 // volume - var/interact_offline = 0 // Can the machine be interacted with while de-powered. - var/obj/item/weapon/circuitboard/circuit = null - - // 0.0 - 1.0 multipler for prob() based on bullet structure damage - // So if this is 1.0 then a 100 damage bullet will always break this structure - // If this is 0.5 then a 50 damage bullet will break this structure 25% of the time - var/bullet_vulnerability = 0.25 - - var/speed_process = FALSE //If false, SSmachines. If true, SSfastprocess. - - blocks_emissive = EMISSIVE_BLOCK_GENERIC - -/obj/machinery/New(l, d=0) - ..() - if(isnum(d)) - set_dir(d) - if(ispath(circuit)) - circuit = new circuit(src) - -/obj/machinery/Initialize(var/mapload) - . = ..() - global.machines += src - if(ispath(circuit)) - circuit = new circuit(src) - if(!speed_process) - START_MACHINE_PROCESSING(src) - else - START_PROCESSING(SSfastprocess, src) - if(!mapload) - power_change() - -/obj/machinery/Destroy() - if(!speed_process) - STOP_MACHINE_PROCESSING(src) - else - STOP_PROCESSING(SSfastprocess, src) - global.machines -= src - if(component_parts) - for(var/atom/A in component_parts) - if(A.loc == src) // If the components are inside the machine, delete them. - qdel(A) - else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code. - warning("[A] was still in [src]'s component_parts when it was Destroy()'d") - component_parts.Cut() - component_parts = null - if(contents) // The same for contents. - for(var/atom/A in contents) - if(ishuman(A)) - var/mob/living/carbon/human/H = A - H.client.eye = H.client.mob - H.client.perspective = MOB_PERSPECTIVE - H.loc = src.loc - else - qdel(A) - return ..() - -/obj/machinery/process() // Steady power usage is handled separately. If you dont use process why are you here? - return PROCESS_KILL - -/obj/machinery/emp_act(severity) - if(use_power && stat == 0) - use_power(7500/severity) - - var/obj/effect/overlay/pulse2 = new /obj/effect/overlay(src.loc) - pulse2.icon = 'icons/effects/effects.dmi' - pulse2.icon_state = "empdisable" - pulse2.name = "emp sparks" - pulse2.anchored = TRUE - pulse2.set_dir(pick(cardinal)) - - spawn(10) - qdel(pulse2) - ..() - -/obj/machinery/ex_act(severity) - switch(severity) - if(1.0) - fall_apart(severity) - return - if(2.0) - if(prob(50)) - fall_apart(severity) - return - if(3.0) - if(prob(25)) - fall_apart(severity) - return - else - return - -/obj/machinery/vv_edit_var(var/var_name, var/new_value) - if(var_name == NAMEOF(src, use_power)) - update_use_power(new_value) - return TRUE - else if(var_name == NAMEOF(src, power_channel)) - update_power_channel(new_value) - return TRUE - else if(var_name == NAMEOF(src, idle_power_usage)) - update_idle_power_usage(new_value) - return TRUE - else if(var_name == NAMEOF(src, active_power_usage)) - update_active_power_usage(new_value) - return TRUE - return ..() - -/obj/machinery/proc/operable(var/additional_flags = 0) - return !inoperable(additional_flags) - -/obj/machinery/proc/inoperable(var/additional_flags = 0) - return (stat & (NOPOWER | BROKEN | additional_flags)) - -// Duplicate of below because we don't want to fuck around with CanUseTopic in TGUI -// TODO: Replace this with can_interact from /tg/ -/obj/machinery/tgui_status(mob/user) - if(!interact_offline && (stat & (NOPOWER | BROKEN))) - return STATUS_CLOSE - return ..() - -/obj/machinery/CanUseTopic(var/mob/user) - if(!interact_offline && (stat & (NOPOWER | BROKEN))) - return STATUS_CLOSE - return ..() - -/obj/machinery/CouldUseTopic(var/mob/user) - ..() - user.set_machine(src) - -/obj/machinery/CouldNotUseTopic(var/mob/user) - user.unset_machine() - -//////////////////////////////////////////////////////////////////////////////////////////// - -/obj/machinery/attack_ai(mob/user as mob) - if(isrobot(user)) - // For some reason attack_robot doesn't work - // This is to stop robots from using cameras to remotely control machines. - if(user.client && user.client.eye == user) - return attack_hand(user) - else - return attack_hand(user) - -/obj/machinery/attack_hand(mob/user as mob) - - if(inoperable(MAINT)) - return 1 - if(user.lying || user.stat) - return 1 - if(!user.IsAdvancedToolUser()) //Vorestation edit - to_chat(user, "You don't have the dexterity to do this!") - return 1 - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 55) - visible_message("[H] stares cluelessly at [src].") - return 1 - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return 1 - - if(clicksound && istype(user, /mob/living/carbon)) - playsound(src, clicksound, clickvol) - - add_fingerprint(user) - - return ..() - -/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. - return - -/obj/machinery/proc/assign_uid() - uid = gl_uid - gl_uid++ - -/obj/machinery/proc/state(var/msg) - for(var/mob/O in hearers(src, null)) - O.show_message("\icon[src][bicon(src)] [msg]", 2) - -/obj/machinery/proc/ping(text=null) - if(!text) - text = "\The [src] pings." - - state(text, "blue") - playsound(src, 'sound/machines/ping.ogg', 50, 0) - -/obj/machinery/proc/shock(mob/user, prb) - if(inoperable()) - return 0 - if(!prob(prb)) - return 0 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, get_area(src), src, 0.7)) - var/area/temp_area = get_area(src) - if(temp_area) - var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() - - if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) - temp_apc.terminal.powernet.trigger_warning() - if(user.stunned) - return 1 - return 0 - -/obj/machinery/proc/default_apply_parts() - var/obj/item/weapon/circuitboard/CB = circuit - if(!istype(CB)) - return - CB.apply_default_parts(src) - RefreshParts() - -/obj/machinery/proc/default_use_hicell() - var/obj/item/weapon/cell/C = locate(/obj/item/weapon/cell) in component_parts - if(C) - component_parts -= C - qdel(C) - C = new /obj/item/weapon/cell/high(src) - component_parts += C - RefreshParts() - return C - -/obj/machinery/proc/default_part_replacement(var/mob/user, var/obj/item/weapon/storage/part_replacer/R) - var/parts_replaced = FALSE - if(!istype(R)) - return 0 - if(!component_parts) - return 0 - to_chat(user, "Following parts detected in [src]:") - for(var/obj/item/C in component_parts) - to_chat(user, " [C.name]") - if(panel_open || !R.panel_req) - var/obj/item/weapon/circuitboard/CB = circuit - var/P - for(var/obj/item/A in component_parts) - for(var/T in CB.req_components) - if(ispath(A.type, T)) - P = T - break - for(var/obj/item/B in R.contents) - if(istype(B, P) && istype(A, P)) - if(B.get_rating() > A.get_rating()) - R.remove_from_storage(B, src) - R.handle_item_insertion(A, 1) - component_parts -= A - component_parts += B - B.loc = null - to_chat(user, "[A.name] replaced with [B.name].") - parts_replaced = TRUE - break - update_icon() - RefreshParts() - if(parts_replaced) - R.play_rped_sound() - return 1 - -// Default behavior for wrenching down machines. Supports both delay and instant modes. -/obj/machinery/proc/default_unfasten_wrench(var/mob/user, var/obj/item/W, var/time = 0) - if(!W.is_wrench()) - return FALSE - if(panel_open) - return FALSE // Close panel first! - playsound(src, W.usesound, 50, 1) - var/actual_time = W.toolspeed * time - if(actual_time != 0) - user.visible_message( \ - "\The [user] begins [anchored ? "un" : ""]securing \the [src].", \ - "You start [anchored ? "un" : ""]securing \the [src].") - if(actual_time == 0 || do_after(user, actual_time, target = src)) - user.visible_message( \ - "\The [user] has [anchored ? "un" : ""]secured \the [src].", \ - "You [anchored ? "un" : ""]secure \the [src].") - anchored = !anchored - power_change() //Turn on or off the machine depending on the status of power in the new area. - update_icon() - return TRUE - -/obj/machinery/proc/default_deconstruction_crowbar(var/mob/user, var/obj/item/C) - if(!C.is_crowbar()) - return 0 - if(!panel_open) - return 0 - . = dismantle() - -/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!S.is_screwdriver()) - return 0 - playsound(src, S.usesound, 50, 1) - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open" : "close"] the maintenance hatch of [src].") - update_icon() - return 1 - -/obj/machinery/proc/computer_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!S.is_screwdriver()) - return 0 - if(!circuit) - return 0 - to_chat(user, "You start disconnecting the monitor.") - playsound(src, S.usesound, 50, 1) - if(do_after(user, 20 * S.toolspeed)) - if(stat & BROKEN) - to_chat(user, "The broken glass falls out.") - new /obj/item/weapon/material/shard(src.loc) - else - to_chat(user, "You disconnect the monitor.") - . = dismantle() - -/obj/machinery/proc/alarm_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!S.is_screwdriver()) - return 0 - playsound(src, S.usesound, 50, 1) - panel_open = !panel_open - to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") - update_icon() - return 1 - -/obj/machinery/proc/alarm_deconstruction_wirecutters(var/mob/user, var/obj/item/W) - if(!W.is_wirecutter()) - return 0 - if(!panel_open) - return 0 - user.visible_message("[user] has cut the wires inside \the [src]!", "You have cut the wires inside \the [src].") - playsound(src, W.usesound, 50, 1) - new/obj/item/stack/cable_coil(get_turf(src), 5) - . = dismantle() - -/obj/machinery/proc/dismantle() - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - for(var/obj/I in contents) - if(istype(I,/obj/item/weapon/card/id)) - I.forceMove(src.loc) - - if(!circuit) - return 0 - var/obj/structure/frame/A = new /obj/structure/frame(src.loc) - var/obj/item/weapon/circuitboard/M = circuit - A.circuit = M - A.anchored = TRUE - A.frame_type = M.board_type - if(A.frame_type.circuit) - A.need_circuit = 0 - - if(A.frame_type.frame_class == FRAME_CLASS_ALARM || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) - A.density = FALSE - else - A.density = TRUE - - if(A.frame_type.frame_class == FRAME_CLASS_MACHINE) - for(var/obj/D in component_parts) - D.forceMove(src.loc) - if(A.components) - A.components.Cut() - else - A.components = list() - component_parts = list() - A.check_components() - - if(A.frame_type.frame_class == FRAME_CLASS_ALARM) - A.state = FRAME_FASTENED - else if(A.frame_type.frame_class == FRAME_CLASS_COMPUTER || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) - if(stat & BROKEN) - A.state = FRAME_WIRED - else - A.state = FRAME_PANELED - else - A.state = FRAME_WIRED - - A.set_dir(dir) - A.pixel_x = pixel_x - A.pixel_y = pixel_y - A.update_desc() - A.update_icon() - M.loc = null - M.deconstruct(src) - qdel(src) - return 1 - -/obj/machinery/bullet_act(obj/item/projectile/P, def_zone) - . = ..() - if(prob(P.get_structure_damage() * bullet_vulnerability)) - fall_apart() - -/** - * Like an angrier dismantle, where it destroys some of the parts and doesn't give you a frame - ** severity: Same severities as ex_act (so lower is more destructive) - ** scatter: If you want the parts to slide around 1 turf in random directions - */ -/obj/machinery/proc/fall_apart(var/severity = 3, var/scatter = TRUE) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - var/atom/droploc = drop_location() - if(!droploc || !contents.len) // not even a circuit? - playsound(src, 'sound/machines/machine_die_short.ogg') - spark_system.start() - qdel(spark_system) - qdel(src) - return - - var/list/surviving_parts = list() - // Deleting IDs is lame, unless this is like nuclear severity - if(severity != 1) - for(var/obj/item/weapon/card/id/I in contents) - surviving_parts |= I - - // May populate some items to throw around - if(!LAZYLEN(component_parts) && circuit) - circuit.apply_default_parts(src) - - var/survivability - switch(severity) - // No survivors - if(1) - survivability = 0 - - // 1 part survives - if(2) - survivability = 0 - var/atom/movable/picked_part = pick(contents) - if(istype(picked_part)) - surviving_parts |= picked_part - - // 50% of parts destroyed on average - if(3) - survivability = 50 - - // No parts destroyed, but you lose the frame - else - survivability = 100 - - for(var/atom/movable/P in contents) - if(prob(survivability)) - surviving_parts |= P - - if(circuit && severity >= 2) - var/datum/frame/frame_types/FT = circuit.board_type - if(istype(FT)) - // Some steel from the frame, but about half - surviving_parts += new /obj/item/stack/material/steel(null, max(1,round(FT.frame_size/2))) - // Two bits of cable, but not the 5 required to rebuild - surviving_parts += new /obj/item/stack/cable_coil(null, 1) - surviving_parts += new /obj/item/stack/cable_coil(null, 1) - - for(var/atom/movable/A as anything in surviving_parts) - A.forceMove(droploc) - if(scatter && isturf(droploc)) - var/turf/T = droploc - A.Move(get_step(T, pick(alldirs))) - - playsound(src, 'sound/machines/machine_die_short.ogg') - spark_system.start() - qdel(spark_system) - qdel(src) - -/datum/proc/apply_visual(mob/M) - M.sight = 0 //Just reset their mesons and stuff so they can't use them, by default. - return - -/datum/proc/remove_visual(mob/M) - return +/* +Overview: + Used to create objects that need a per step proc call. Default definition of 'New()' + stores a reference to src machine in global 'machines list'. Default definition + of 'Del' removes reference to src machine in global 'machines list'. + +Class Variables: + + power_init_complete (boolean) + Indicates that we have have registered our static power usage with the area. + + use_power (num) + current state of auto power use. + Possible Values: + USE_POWER_OFF:0 -- no auto power use + USE_POWER_IDLE:1 -- machine is using power at its idle power level + USE_POWER_ACTIVE:2 -- machine is using power at its active power level + + active_power_usage (num) + Value for the amount of power to use when in active power mode + + idle_power_usage (num) + Value for the amount of power to use when in idle power mode + + power_channel (num) + What channel to draw from when drawing power for power mode + Possible Values: + EQUIP:0 -- Equipment Channel + LIGHT:2 -- Lighting Channel + ENVIRON:3 -- Environment Channel + + component_parts (list) + A list of component parts of machine used by frame based machines. + + panel_open (num) + Whether the panel is open + + uid (num) + Unique id of machine across all machines. + + gl_uid (global num) + Next uid value in sequence + + stat (bitflag) + Machine status bit flags. + Possible bit flags: + BROKEN:1 -- Machine is broken + NOPOWER:2 -- No power is being supplied to machine. + POWEROFF:4 -- tbd + MAINT:8 -- machine is currently under going maintenance. + EMPED:16 -- temporary broken by EMP pulse + +Class Procs: + New() 'game/machinery/machine.dm' + + Destroy() 'game/machinery/machine.dm' + + get_power_usage() 'game/machinery/machinery_power.dm' + Returns the amount of power this machine uses every SSmachines cycle. + Default definition uses 'use_power', 'active_power_usage', 'idle_power_usage' + + powered(chan = CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' + Checks to see if area that contains the object has power available for power + channel given in 'chan'. + + use_power_oneoff(amount, chan=CURRENT_CHANNEL) 'game/machinery/machinery_power.dm' + Deducts 'amount' from the power channel 'chan' of the area that contains the object. + + power_change() 'game/machinery/machinery_power.dm' + Called by the area that contains the object when ever that area under goes a + power state change (area runs out of power, or area channel is turned off). + + RefreshParts() 'game/machinery/machine.dm' + Called to refresh the variables in the machine that are contributed to by parts + contained in the component_parts list. (example: glass and material amounts for + the autolathe) + + Default definition does nothing. + + assign_uid() 'game/machinery/machine.dm' + Called by machine to assign a value to the uid variable. + + process() 'game/machinery/machine.dm' + Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. + + + Compiled by Aygar +*/ + +/obj/machinery + name = "machinery" + icon = 'icons/obj/stationobjs.dmi' + w_class = ITEMSIZE_NO_CONTAINER + layer = UNDER_JUNK_LAYER + + var/stat = 0 + var/emagged = 0 + var/use_power = USE_POWER_IDLE + //0 = dont run the auto + //1 = run auto, use idle + //2 = run auto, use active + var/idle_power_usage = 0 + var/active_power_usage = 0 + var/power_channel = EQUIP //EQUIP, ENVIRON or LIGHT + var/power_init_complete = FALSE + var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. + var/uid + var/panel_open = FALSE + var/global/gl_uid = 1 + var/clicksound // sound played on succesful interface. Just put it in the list of vars at the start. + var/clickvol = 40 // volume + var/interact_offline = 0 // Can the machine be interacted with while de-powered. + var/obj/item/weapon/circuitboard/circuit = null + + // 0.0 - 1.0 multipler for prob() based on bullet structure damage + // So if this is 1.0 then a 100 damage bullet will always break this structure + // If this is 0.5 then a 50 damage bullet will break this structure 25% of the time + var/bullet_vulnerability = 0.25 + + var/speed_process = FALSE //If false, SSmachines. If true, SSfastprocess. + + blocks_emissive = EMISSIVE_BLOCK_GENERIC + +/obj/machinery/New(l, d=0) + ..() + if(isnum(d)) + set_dir(d) + if(ispath(circuit)) + circuit = new circuit(src) + +/obj/machinery/Initialize(var/mapload) + . = ..() + SSmachines.all_machines += src + if(ispath(circuit)) + circuit = new circuit(src) + if(!speed_process) + START_MACHINE_PROCESSING(src) + else + START_PROCESSING(SSfastprocess, src) + if(!mapload) + power_change() + +/obj/machinery/Destroy() + if(!speed_process) + STOP_MACHINE_PROCESSING(src) + else + STOP_PROCESSING(SSfastprocess, src) + SSmachines.all_machines -= src + if(component_parts) + for(var/atom/A in component_parts) + if(A.loc == src) // If the components are inside the machine, delete them. + qdel(A) + else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code. + warning("[A] was still in [src]'s component_parts when it was Destroy()'d") + component_parts.Cut() + component_parts = null + if(contents) // The same for contents. + for(var/atom/A in contents) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + H.client.eye = H.client.mob + H.client.perspective = MOB_PERSPECTIVE + H.loc = src.loc + else + qdel(A) + return ..() + +/obj/machinery/process() // Steady power usage is handled separately. If you dont use process why are you here? + return PROCESS_KILL + +/obj/machinery/emp_act(severity) + if(use_power && stat == 0) + use_power(7500/severity) + + var/obj/effect/overlay/pulse2 = new /obj/effect/overlay(src.loc) + pulse2.icon = 'icons/effects/effects.dmi' + pulse2.icon_state = "empdisable" + pulse2.name = "emp sparks" + pulse2.anchored = TRUE + pulse2.set_dir(pick(cardinal)) + + spawn(10) + qdel(pulse2) + ..() + +/obj/machinery/ex_act(severity) + switch(severity) + if(1.0) + fall_apart(severity) + return + if(2.0) + if(prob(50)) + fall_apart(severity) + return + if(3.0) + if(prob(25)) + fall_apart(severity) + return + else + return + +/obj/machinery/vv_edit_var(var/var_name, var/new_value) + if(var_name == NAMEOF(src, use_power)) + update_use_power(new_value) + return TRUE + else if(var_name == NAMEOF(src, power_channel)) + update_power_channel(new_value) + return TRUE + else if(var_name == NAMEOF(src, idle_power_usage)) + update_idle_power_usage(new_value) + return TRUE + else if(var_name == NAMEOF(src, active_power_usage)) + update_active_power_usage(new_value) + return TRUE + return ..() + +/obj/machinery/proc/operable(var/additional_flags = 0) + return !inoperable(additional_flags) + +/obj/machinery/proc/inoperable(var/additional_flags = 0) + return (stat & (NOPOWER | BROKEN | additional_flags)) + +// Duplicate of below because we don't want to fuck around with CanUseTopic in TGUI +// TODO: Replace this with can_interact from /tg/ +/obj/machinery/tgui_status(mob/user) + if(!interact_offline && (stat & (NOPOWER | BROKEN))) + return STATUS_CLOSE + return ..() + +/obj/machinery/CanUseTopic(var/mob/user) + if(!interact_offline && (stat & (NOPOWER | BROKEN))) + return STATUS_CLOSE + return ..() + +/obj/machinery/CouldUseTopic(var/mob/user) + ..() + user.set_machine(src) + +/obj/machinery/CouldNotUseTopic(var/mob/user) + user.unset_machine() + +//////////////////////////////////////////////////////////////////////////////////////////// + +/obj/machinery/attack_ai(mob/user as mob) + if(isrobot(user)) + // For some reason attack_robot doesn't work + // This is to stop robots from using cameras to remotely control machines. + if(user.client && user.client.eye == user) + return attack_hand(user) + else + return attack_hand(user) + +/obj/machinery/attack_hand(mob/user as mob) + + if(inoperable(MAINT)) + return 1 + if(user.lying || user.stat) + return 1 + if(!user.IsAdvancedToolUser()) //Vorestation edit + to_chat(user, "You don't have the dexterity to do this!") + return 1 + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 55) + visible_message("[H] stares cluelessly at [src].") + return 1 + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return 1 + + if(clicksound && istype(user, /mob/living/carbon)) + playsound(src, clicksound, clickvol) + + add_fingerprint(user) + + return ..() + +/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. + return + +/obj/machinery/proc/assign_uid() + uid = gl_uid + gl_uid++ + +/obj/machinery/proc/state(var/msg) + for(var/mob/O in hearers(src, null)) + O.show_message("\icon[src][bicon(src)] [msg]", 2) + +/obj/machinery/proc/ping(text=null) + if(!text) + text = "\The [src] pings." + + state(text, "blue") + playsound(src, 'sound/machines/ping.ogg', 50, 0) + +/obj/machinery/proc/shock(mob/user, prb) + if(inoperable()) + return 0 + if(!prob(prb)) + return 0 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + if(electrocute_mob(user, get_area(src), src, 0.7)) + var/area/temp_area = get_area(src) + if(temp_area) + var/obj/machinery/power/apc/temp_apc = temp_area.get_apc() + + if(temp_apc && temp_apc.terminal && temp_apc.terminal.powernet) + temp_apc.terminal.powernet.trigger_warning() + if(user.stunned) + return 1 + return 0 + +/obj/machinery/proc/default_apply_parts() + var/obj/item/weapon/circuitboard/CB = circuit + if(!istype(CB)) + return + CB.apply_default_parts(src) + RefreshParts() + +/obj/machinery/proc/default_use_hicell() + var/obj/item/weapon/cell/C = locate(/obj/item/weapon/cell) in component_parts + if(C) + component_parts -= C + qdel(C) + C = new /obj/item/weapon/cell/high(src) + component_parts += C + RefreshParts() + return C + +/obj/machinery/proc/default_part_replacement(var/mob/user, var/obj/item/weapon/storage/part_replacer/R) + var/parts_replaced = FALSE + if(!istype(R)) + return 0 + if(!component_parts) + return 0 + to_chat(user, "Following parts detected in [src]:") + for(var/obj/item/C in component_parts) + to_chat(user, " [C.name]") + if(panel_open || !R.panel_req) + var/obj/item/weapon/circuitboard/CB = circuit + var/P + for(var/obj/item/A in component_parts) + for(var/T in CB.req_components) + if(ispath(A.type, T)) + P = T + break + for(var/obj/item/B in R.contents) + if(istype(B, P) && istype(A, P)) + if(B.get_rating() > A.get_rating()) + R.remove_from_storage(B, src) + R.handle_item_insertion(A, 1) + component_parts -= A + component_parts += B + B.loc = null + to_chat(user, "[A.name] replaced with [B.name].") + parts_replaced = TRUE + break + update_icon() + RefreshParts() + if(parts_replaced) + R.play_rped_sound() + return 1 + +// Default behavior for wrenching down machines. Supports both delay and instant modes. +/obj/machinery/proc/default_unfasten_wrench(var/mob/user, var/obj/item/W, var/time = 0) + if(!W.has_tool_quality(TOOL_WRENCH)) + return FALSE + if(panel_open) + return FALSE // Close panel first! + playsound(src, W.usesound, 50, 1) + var/actual_time = W.toolspeed * time + if(actual_time != 0) + user.visible_message( \ + "\The [user] begins [anchored ? "un" : ""]securing \the [src].", \ + "You start [anchored ? "un" : ""]securing \the [src].") + if(actual_time == 0 || do_after(user, actual_time, target = src)) + user.visible_message( \ + "\The [user] has [anchored ? "un" : ""]secured \the [src].", \ + "You [anchored ? "un" : ""]secure \the [src].") + anchored = !anchored + power_change() //Turn on or off the machine depending on the status of power in the new area. + update_icon() + return TRUE + +/obj/machinery/proc/default_deconstruction_crowbar(var/mob/user, var/obj/item/C) + if(!C.has_tool_quality(TOOL_CROWBAR)) + return 0 + if(!panel_open) + return 0 + . = dismantle() + +/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!S.has_tool_quality(TOOL_SCREWDRIVER)) + return 0 + playsound(src, S.usesound, 50, 1) + panel_open = !panel_open + to_chat(user, "You [panel_open ? "open" : "close"] the maintenance hatch of [src].") + update_icon() + return 1 + +/obj/machinery/proc/computer_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!S.has_tool_quality(TOOL_SCREWDRIVER)) + return 0 + if(!circuit) + return 0 + to_chat(user, "You start disconnecting the monitor.") + playsound(src, S.usesound, 50, 1) + if(do_after(user, 20 * S.toolspeed)) + if(stat & BROKEN) + to_chat(user, "The broken glass falls out.") + new /obj/item/weapon/material/shard(src.loc) + else + to_chat(user, "You disconnect the monitor.") + . = dismantle() + +/obj/machinery/proc/alarm_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!S.has_tool_quality(TOOL_SCREWDRIVER)) + return 0 + playsound(src, S.usesound, 50, 1) + panel_open = !panel_open + to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") + update_icon() + return 1 + +/obj/machinery/proc/alarm_deconstruction_wirecutters(var/mob/user, var/obj/item/W) + if(!W.has_tool_quality(TOOL_WIRECUTTER)) + return 0 + if(!panel_open) + return 0 + user.visible_message("[user] has cut the wires inside \the [src]!", "You have cut the wires inside \the [src].") + playsound(src, W.usesound, 50, 1) + new/obj/item/stack/cable_coil(get_turf(src), 5) + . = dismantle() + +/obj/machinery/proc/dismantle() + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + for(var/obj/I in contents) + if(istype(I,/obj/item/weapon/card/id)) + I.forceMove(src.loc) + + if(!circuit) + return 0 + var/obj/structure/frame/A = new /obj/structure/frame(src.loc) + var/obj/item/weapon/circuitboard/M = circuit + A.circuit = M + A.anchored = TRUE + A.frame_type = M.board_type + if(A.frame_type.circuit) + A.need_circuit = 0 + + if(A.frame_type.frame_class == FRAME_CLASS_ALARM || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) + A.density = FALSE + else + A.density = TRUE + + if(A.frame_type.frame_class == FRAME_CLASS_MACHINE) + for(var/obj/D in component_parts) + D.forceMove(src.loc) + if(A.components) + A.components.Cut() + else + A.components = list() + component_parts = list() + A.check_components() + + if(A.frame_type.frame_class == FRAME_CLASS_ALARM) + A.state = FRAME_FASTENED + else if(A.frame_type.frame_class == FRAME_CLASS_COMPUTER || A.frame_type.frame_class == FRAME_CLASS_DISPLAY) + if(stat & BROKEN) + A.state = FRAME_WIRED + else + A.state = FRAME_PANELED + else + A.state = FRAME_WIRED + + A.set_dir(dir) + A.pixel_x = pixel_x + A.pixel_y = pixel_y + A.update_desc() + A.update_icon() + M.loc = null + M.deconstruct(src) + qdel(src) + return 1 + +/obj/machinery/bullet_act(obj/item/projectile/P, def_zone) + . = ..() + if(prob(P.get_structure_damage() * bullet_vulnerability)) + fall_apart() + +/** + * Like an angrier dismantle, where it destroys some of the parts and doesn't give you a frame + ** severity: Same severities as ex_act (so lower is more destructive) + ** scatter: If you want the parts to slide around 1 turf in random directions + */ +/obj/machinery/proc/fall_apart(var/severity = 3, var/scatter = TRUE) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + var/atom/droploc = drop_location() + if(!droploc || !contents.len) // not even a circuit? + playsound(src, 'sound/machines/machine_die_short.ogg') + spark_system.start() + qdel(spark_system) + qdel(src) + return + + var/list/surviving_parts = list() + // Deleting IDs is lame, unless this is like nuclear severity + if(severity != 1) + for(var/obj/item/weapon/card/id/I in contents) + surviving_parts |= I + + // May populate some items to throw around + if(!LAZYLEN(component_parts) && circuit) + circuit.apply_default_parts(src) + + var/survivability + switch(severity) + // No survivors + if(1) + survivability = 0 + + // 1 part survives + if(2) + survivability = 0 + var/atom/movable/picked_part = pick(contents) + if(istype(picked_part)) + surviving_parts |= picked_part + + // 50% of parts destroyed on average + if(3) + survivability = 50 + + // No parts destroyed, but you lose the frame + else + survivability = 100 + + for(var/atom/movable/P in contents) + if(prob(survivability)) + surviving_parts |= P + + if(circuit && severity >= 2) + var/datum/frame/frame_types/FT = circuit.board_type + if(istype(FT)) + // Some steel from the frame, but about half + surviving_parts += new /obj/item/stack/material/steel(null, max(1,round(FT.frame_size/2))) + // Two bits of cable, but not the 5 required to rebuild + surviving_parts += new /obj/item/stack/cable_coil(null, 1) + surviving_parts += new /obj/item/stack/cable_coil(null, 1) + + for(var/atom/movable/A as anything in surviving_parts) + A.forceMove(droploc) + if(scatter && isturf(droploc)) + var/turf/T = droploc + A.Move(get_step(T, pick(alldirs))) + + playsound(src, 'sound/machines/machine_die_short.ogg') + spark_system.start() + qdel(spark_system) + qdel(src) + +/datum/proc/apply_visual(mob/M) + M.sight = 0 //Just reset their mesons and stuff so they can't use them, by default. + return + +/datum/proc/remove_visual(mob/M) + return diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 7b6de697ac7..13184fdbfb7 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -1,396 +1,396 @@ -// Magnetic attractor, creates variable magnetic fields and attraction. -// Can also be used to emit electron/proton beams to create a center of magnetism on another tile - -// tl;dr: it's magnets lol -// This was created for firing ranges, but I suppose this could have other applications - Doohl - -/obj/machinery/magnetic_module - icon = 'icons/obj/objects.dmi' - icon_state = "floor_magnet-f" - name = "Electromagnetic Generator" - desc = "A device that uses station power to create points of magnetic energy." - plane = PLATING_PLANE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 50 - - var/freq = 1449 // radio frequency - var/electricity_level = 1 // intensity of the magnetic pull - var/magnetic_field = 1 // the range of magnetic attraction - var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something - var/turf/center // the center of magnetic attraction - var/on = 0 - var/pulling = 0 - - // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down - var/center_x = 0 - var/center_y = 0 - var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer - -/obj/machinery/magnetic_module/New() - ..() - var/turf/T = loc - hide(!T.is_plating()) - center = T - - spawn(10) // must wait for map loading to finish - if(radio_controller) - radio_controller.add_object(src, freq, RADIO_MAGNETS) - - spawn() - magnetic_process() - -// update the invisibility and icon -/obj/machinery/magnetic_module/hide(var/intact) - invisibility = intact ? 101 : 0 - update_icon() - -// update the icon_state -/obj/machinery/magnetic_module/update_icon() - var/state="floor_magnet" - var/onstate="" - if(!on) - onstate="0" - - if(invisibility) - icon_state = "[state][onstate]-f" // if invisible, set icon to faded version - // in case of being revealed by T-scanner - else - icon_state = "[state][onstate]" - -/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) - var/command = signal.data["command"] - var/modifier = signal.data["modifier"] - var/signal_code = signal.data["code"] - if(command && (signal_code == code)) - - Cmd(command, modifier) - -/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) - if(command) - switch(command) - if("set-electriclevel") - if(modifier) electricity_level = modifier - if("set-magneticfield") - if(modifier) magnetic_field = modifier - - if("add-elec") - electricity_level++ - if(electricity_level > 12) - electricity_level = 12 - if("sub-elec") - electricity_level-- - if(electricity_level <= 0) - electricity_level = 1 - if("add-mag") - magnetic_field++ - if(magnetic_field > 4) - magnetic_field = 4 - if("sub-mag") - magnetic_field-- - if(magnetic_field <= 0) - magnetic_field = 1 - - if("set-x") - if(modifier) center_x = modifier - if("set-y") - if(modifier) center_y = modifier - - if("N") // NORTH - center_y++ - if("S") // SOUTH - center_y-- - if("E") // EAST - center_x++ - if("W") // WEST - center_x-- - if("C") // CENTER - center_x = 0 - center_y = 0 - if("R") // RANDOM - center_x = rand(-max_dist, max_dist) - center_y = rand(-max_dist, max_dist) - - if("set-code") - if(modifier) code = modifier - if("toggle-power") - on = !on - - if(on) - spawn() - magnetic_process() - -/obj/machinery/magnetic_module/process() - if(stat & NOPOWER) - on = 0 - - // Sanity checks: - if(electricity_level <= 0) - electricity_level = 1 - if(magnetic_field <= 0) - magnetic_field = 1 - - // Limitations: - if(abs(center_x) > max_dist) - center_x = max_dist - if(abs(center_y) > max_dist) - center_y = max_dist - if(magnetic_field > 4) - magnetic_field = 4 - if(electricity_level > 12) - electricity_level = 12 - - // Update power usage: - if(on) - update_use_power(USE_POWER_ACTIVE) - update_active_power_usage(electricity_level * 15) - else - update_use_power(USE_POWER_OFF) - - // Overload conditions: - /* // Eeeehhh kinda stupid - if(on) - if(electricity_level > 11) - if(prob(electricity_level)) - explosion(loc, 0, 1, 2, 3) // ooo dat shit EXPLODES son - spawn(2) - qdel(src) - */ - - update_icon() - -/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling - if(pulling) return - while(on) - - pulling = 1 - center = locate(x+center_x, y+center_y, z) - if(center) - for(var/obj/M in orange(magnetic_field, center)) - if(!M.anchored && !(M.flags & NOCONDUCT)) - step_towards(M, center) - - for(var/mob/living/silicon/S in orange(magnetic_field, center)) - if(istype(S, /mob/living/silicon/ai)) continue - step_towards(S, center) - - use_power(electricity_level * 5) - sleep(13 - electricity_level) - - pulling = 0 - -/obj/machinery/magnetic_module/Destroy() - if(radio_controller) - radio_controller.remove_object(src, freq) - ..() - -/obj/machinery/magnetic_controller - name = "Magnetic Control Console" - icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! - icon_state = "airlock_control_standby" - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 45 - var/frequency = 1449 - var/code = 0 - var/list/magnets = list() - var/title = "Magnetic Control Console" - var/autolink = 0 // if set to 1, can't probe for other magnets! - - var/pathpos = 1 // position in the path - var/path = "NULL" // text path of the magnet - var/speed = 1 // lowest = 1, highest = 10 - var/list/rpath = list() // real path of the magnet, used in iterator - - var/moving = 0 // 1 if scheduled to loop - var/looping = 0 // 1 if looping - - var/datum/radio_frequency/radio_connection - - -/obj/machinery/magnetic_controller/New() - ..() - - if(autolink) - for(var/obj/machinery/magnetic_module/M in machines) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - - spawn(45) // must wait for map loading to finish - if(radio_controller) - radio_connection = radio_controller.add_object(src, frequency, RADIO_MAGNETS) - - - if(path) // check for default path - filter_path() // renders rpath - - -/obj/machinery/magnetic_controller/process() - if(magnets.len == 0 && autolink) - for(var/obj/machinery/magnetic_module/M in machines) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - -/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "Magnetic Control Console

    " - if(!autolink) - dat += {" - Frequency: [frequency]
    - Code: [code]
    - Probe Generators
    - "} - - if(magnets.len >= 1) - - dat += "Magnets confirmed:
    " - var/i = 0 - for(var/obj/machinery/magnetic_module/M in magnets) - i++ - dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " - - dat += "
    Speed: - [speed] +
    " - dat += "Path: {[path]}
    " - dat += "Moving: [moving ? "Enabled":"Disabled"]" - - - user << browse(dat, "window=magnet;size=400x500") - onclose(user, "magnet") - -/obj/machinery/magnetic_controller/Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) - return - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["radio-op"]) - - // Prepare signal beforehand, because this is a radio operation - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - // Apply any necessary commands - switch(href_list["radio-op"]) - if("togglepower") - signal.data["command"] = "toggle-power" - - if("minuselec") - signal.data["command"] = "sub-elec" - if("pluselec") - signal.data["command"] = "add-elec" - - if("minusmag") - signal.data["command"] = "sub-mag" - if("plusmag") - signal.data["command"] = "add-mag" - - - // Broadcast the signal - - radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) - - spawn(1) - updateUsrDialog() // pretty sure this increases responsiveness - - if(href_list["operation"]) - switch(href_list["operation"]) - if("plusspeed") - speed ++ - if(speed > 10) - speed = 10 - if("minusspeed") - speed -- - if(speed <= 0) - speed = 1 - if("setpath") - var/newpath = sanitize(tgui_input_text(usr, "Please define a new path!",,path)) - if(newpath && newpath != "") - moving = 0 // stop moving - path = newpath - pathpos = 1 // reset position - filter_path() // renders rpath - - if("togglemoving") - moving = !moving - if(moving) - spawn() MagnetMove() - - - updateUsrDialog() - -/obj/machinery/magnetic_controller/proc/MagnetMove() - if(looping) return - - while(moving && rpath.len >= 1) - - if(stat & (BROKEN|NOPOWER)) - break - - looping = 1 - - // Prepare the radio signal - var/datum/signal/signal = new - signal.transmission_method = TRANSMISSION_RADIO // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! - pathpos = 1 - - var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive - - if(!(nextmove in list("N","S","E","W","C","R"))) - // N, S, E, W are directional - // C is center - // R is random (in magnetic field's bounds) - qdel(signal) - break // break the loop if the character located is invalid - - signal.data["command"] = nextmove - - - pathpos++ // increase iterator - - // Broadcast the signal - spawn() - radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) - - if(speed == 10) - sleep(1) - else - sleep(12-speed) - - looping = 0 - - -/obj/machinery/magnetic_controller/proc/filter_path() - // Generates the rpath variable using the path string, think of this as "string2list" - // Doesn't use params2list() because of the akward way it stacks entities - rpath = list() // clear rpath - var/maximum_character = min(50, length(path)) // chooses the maximum length of the iterator. 50 max length - - for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path - - var/nextchar = copytext(path, i, i+1) // find next character - - if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore - rpath += copytext(path, i, i+1) // else, add to list - - // there doesn't HAVE to be separators but it makes paths syntatically visible - -/obj/machinery/magnetic_controller/Destroy() - if(radio_controller) - radio_controller.remove_object(src, frequency) - ..() +// Magnetic attractor, creates variable magnetic fields and attraction. +// Can also be used to emit electron/proton beams to create a center of magnetism on another tile + +// tl;dr: it's magnets lol +// This was created for firing ranges, but I suppose this could have other applications - Doohl + +/obj/machinery/magnetic_module + icon = 'icons/obj/objects.dmi' + icon_state = "floor_magnet-f" + name = "Electromagnetic Generator" + desc = "A device that uses station power to create points of magnetic energy." + plane = PLATING_PLANE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 50 + + var/freq = 1449 // radio frequency + var/electricity_level = 1 // intensity of the magnetic pull + var/magnetic_field = 1 // the range of magnetic attraction + var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something + var/turf/center // the center of magnetic attraction + var/on = 0 + var/pulling = 0 + + // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down + var/center_x = 0 + var/center_y = 0 + var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer + +/obj/machinery/magnetic_module/New() + ..() + var/turf/T = loc + hide(!T.is_plating()) + center = T + + spawn(10) // must wait for map loading to finish + if(radio_controller) + radio_controller.add_object(src, freq, RADIO_MAGNETS) + + spawn() + magnetic_process() + +// update the invisibility and icon +/obj/machinery/magnetic_module/hide(var/intact) + invisibility = intact ? 101 : 0 + update_icon() + +// update the icon_state +/obj/machinery/magnetic_module/update_icon() + var/state="floor_magnet" + var/onstate="" + if(!on) + onstate="0" + + if(invisibility) + icon_state = "[state][onstate]-f" // if invisible, set icon to faded version + // in case of being revealed by T-scanner + else + icon_state = "[state][onstate]" + +/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) + var/command = signal.data["command"] + var/modifier = signal.data["modifier"] + var/signal_code = signal.data["code"] + if(command && (signal_code == code)) + + Cmd(command, modifier) + +/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) + if(command) + switch(command) + if("set-electriclevel") + if(modifier) electricity_level = modifier + if("set-magneticfield") + if(modifier) magnetic_field = modifier + + if("add-elec") + electricity_level++ + if(electricity_level > 12) + electricity_level = 12 + if("sub-elec") + electricity_level-- + if(electricity_level <= 0) + electricity_level = 1 + if("add-mag") + magnetic_field++ + if(magnetic_field > 4) + magnetic_field = 4 + if("sub-mag") + magnetic_field-- + if(magnetic_field <= 0) + magnetic_field = 1 + + if("set-x") + if(modifier) center_x = modifier + if("set-y") + if(modifier) center_y = modifier + + if("N") // NORTH + center_y++ + if("S") // SOUTH + center_y-- + if("E") // EAST + center_x++ + if("W") // WEST + center_x-- + if("C") // CENTER + center_x = 0 + center_y = 0 + if("R") // RANDOM + center_x = rand(-max_dist, max_dist) + center_y = rand(-max_dist, max_dist) + + if("set-code") + if(modifier) code = modifier + if("toggle-power") + on = !on + + if(on) + spawn() + magnetic_process() + +/obj/machinery/magnetic_module/process() + if(stat & NOPOWER) + on = 0 + + // Sanity checks: + if(electricity_level <= 0) + electricity_level = 1 + if(magnetic_field <= 0) + magnetic_field = 1 + + // Limitations: + if(abs(center_x) > max_dist) + center_x = max_dist + if(abs(center_y) > max_dist) + center_y = max_dist + if(magnetic_field > 4) + magnetic_field = 4 + if(electricity_level > 12) + electricity_level = 12 + + // Update power usage: + if(on) + update_use_power(USE_POWER_ACTIVE) + update_active_power_usage(electricity_level * 15) + else + update_use_power(USE_POWER_OFF) + + // Overload conditions: + /* // Eeeehhh kinda stupid + if(on) + if(electricity_level > 11) + if(prob(electricity_level)) + explosion(loc, 0, 1, 2, 3) // ooo dat shit EXPLODES son + spawn(2) + qdel(src) + */ + + update_icon() + +/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling + if(pulling) return + while(on) + + pulling = 1 + center = locate(x+center_x, y+center_y, z) + if(center) + for(var/obj/M in orange(magnetic_field, center)) + if(!M.anchored && !(M.flags & NOCONDUCT)) + step_towards(M, center) + + for(var/mob/living/silicon/S in orange(magnetic_field, center)) + if(istype(S, /mob/living/silicon/ai)) continue + step_towards(S, center) + + use_power(electricity_level * 5) + sleep(13 - electricity_level) + + pulling = 0 + +/obj/machinery/magnetic_module/Destroy() + if(radio_controller) + radio_controller.remove_object(src, freq) + ..() + +/obj/machinery/magnetic_controller + name = "Magnetic Control Console" + icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! + icon_state = "airlock_control_standby" + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 45 + var/frequency = 1449 + var/code = 0 + var/list/magnets = list() + var/title = "Magnetic Control Console" + var/autolink = 0 // if set to 1, can't probe for other magnets! + + var/pathpos = 1 // position in the path + var/path = "NULL" // text path of the magnet + var/speed = 1 // lowest = 1, highest = 10 + var/list/rpath = list() // real path of the magnet, used in iterator + + var/moving = 0 // 1 if scheduled to loop + var/looping = 0 // 1 if looping + + var/datum/radio_frequency/radio_connection + + +/obj/machinery/magnetic_controller/New() + ..() + + if(autolink) + for(var/obj/machinery/magnetic_module/M in machines) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + + spawn(45) // must wait for map loading to finish + if(radio_controller) + radio_connection = radio_controller.add_object(src, frequency, RADIO_MAGNETS) + + + if(path) // check for default path + filter_path() // renders rpath + + +/obj/machinery/magnetic_controller/process() + if(magnets.len == 0 && autolink) + for(var/obj/machinery/magnetic_module/M in machines) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + +/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "Magnetic Control Console

    " + if(!autolink) + dat += {" + Frequency: [frequency]
    + Code: [code]
    + Probe Generators
    + "} + + if(magnets.len >= 1) + + dat += "Magnets confirmed:
    " + var/i = 0 + for(var/obj/machinery/magnetic_module/M in magnets) + i++ + dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " + + dat += "
    Speed: - [speed] +
    " + dat += "Path: {[path]}
    " + dat += "Moving: [moving ? "Enabled":"Disabled"]" + + + user << browse(dat, "window=magnet;size=400x500") + onclose(user, "magnet") + +/obj/machinery/magnetic_controller/Topic(href, href_list) + if(stat & (BROKEN|NOPOWER)) + return + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["radio-op"]) + + // Prepare signal beforehand, because this is a radio operation + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + // Apply any necessary commands + switch(href_list["radio-op"]) + if("togglepower") + signal.data["command"] = "toggle-power" + + if("minuselec") + signal.data["command"] = "sub-elec" + if("pluselec") + signal.data["command"] = "add-elec" + + if("minusmag") + signal.data["command"] = "sub-mag" + if("plusmag") + signal.data["command"] = "add-mag" + + + // Broadcast the signal + + radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) + + spawn(1) + updateUsrDialog() // pretty sure this increases responsiveness + + if(href_list["operation"]) + switch(href_list["operation"]) + if("plusspeed") + speed ++ + if(speed > 10) + speed = 10 + if("minusspeed") + speed -- + if(speed <= 0) + speed = 1 + if("setpath") + var/newpath = sanitize(tgui_input_text(usr, "Please define a new path!",,path)) + if(newpath && newpath != "") + moving = 0 // stop moving + path = newpath + pathpos = 1 // reset position + filter_path() // renders rpath + + if("togglemoving") + moving = !moving + if(moving) + spawn() MagnetMove() + + + updateUsrDialog() + +/obj/machinery/magnetic_controller/proc/MagnetMove() + if(looping) return + + while(moving && rpath.len >= 1) + + if(stat & (BROKEN|NOPOWER)) + break + + looping = 1 + + // Prepare the radio signal + var/datum/signal/signal = new + signal.transmission_method = TRANSMISSION_RADIO // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! + pathpos = 1 + + var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive + + if(!(nextmove in list("N","S","E","W","C","R"))) + // N, S, E, W are directional + // C is center + // R is random (in magnetic field's bounds) + qdel(signal) + break // break the loop if the character located is invalid + + signal.data["command"] = nextmove + + + pathpos++ // increase iterator + + // Broadcast the signal + spawn() + radio_connection.post_signal(src, signal, radio_filter = RADIO_MAGNETS) + + if(speed == 10) + sleep(1) + else + sleep(12-speed) + + looping = 0 + + +/obj/machinery/magnetic_controller/proc/filter_path() + // Generates the rpath variable using the path string, think of this as "string2list" + // Doesn't use params2list() because of the akward way it stacks entities + rpath = list() // clear rpath + var/maximum_character = min(50, length(path)) // chooses the maximum length of the iterator. 50 max length + + for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path + + var/nextchar = copytext(path, i, i+1) // find next character + + if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore + rpath += copytext(path, i, i+1) // else, add to list + + // there doesn't HAVE to be separators but it makes paths syntatically visible + +/obj/machinery/magnetic_controller/Destroy() + if(radio_controller) + radio_controller.remove_object(src, frequency) + ..() diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 1002e3fa213..7aa39cce388 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -1,62 +1,62 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/machinery/mass_driver - name = "mass driver" - desc = "Shoots things into space." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mass_driver" - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 50 - circuit = /obj/item/weapon/circuitboard/mass_driver - - var/power = 1.0 - var/code = 1.0 - var/id = 1.0 - var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. - -/obj/machinery/mass_driver/New() - . = ..() - default_apply_parts() - -/obj/machinery/mass_driver/attackby(var/obj/item/I, mob/user) - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - - if(istype(I, /obj/item/device/multitool)) - if(panel_open) - var/input = sanitize(tgui_input_text(usr, "What id would you like to give this conveyor?", "Multitool-Conveyor interface", id)) - if(!input) - to_chat(usr, "No input found please hang up and try your call again.") - return - id = input - return - return - -/obj/machinery/mass_driver/proc/drive(amount) - if(stat & (BROKEN|NOPOWER)) - return - use_power(500) - var/O_limit - var/atom/target = get_edge_target_turf(src, dir) - for(var/atom/movable/O in loc) - if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. - O_limit++ - if(O_limit >= 20) - for(var/mob/M in hearers(src, null)) - to_chat(M, "The mass driver lets out a screech, it mustn't be able to handle any more items.") - break - use_power(500) - spawn(0) - O.throw_at(target, drive_range * power, power) - flick("mass_driver1", src) - return - -/obj/machinery/mass_driver/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - drive() - ..(severity) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/machinery/mass_driver + name = "mass driver" + desc = "Shoots things into space." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mass_driver" + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 50 + circuit = /obj/item/weapon/circuitboard/mass_driver + + var/power = 1.0 + var/code = 1.0 + var/id = 1.0 + var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. + +/obj/machinery/mass_driver/New() + . = ..() + default_apply_parts() + +/obj/machinery/mass_driver/attackby(var/obj/item/I, mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + + if(istype(I, /obj/item/device/multitool)) + if(panel_open) + var/input = sanitize(tgui_input_text(usr, "What id would you like to give this conveyor?", "Multitool-Conveyor interface", id)) + if(!input) + to_chat(usr, "No input found please hang up and try your call again.") + return + id = input + return + return + +/obj/machinery/mass_driver/proc/drive(amount) + if(stat & (BROKEN|NOPOWER)) + return + use_power(500) + var/O_limit + var/atom/target = get_edge_target_turf(src, dir) + for(var/atom/movable/O in loc) + if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. + O_limit++ + if(O_limit >= 20) + for(var/mob/M in hearers(src, null)) + to_chat(M, "The mass driver lets out a screech, it mustn't be able to handle any more items.") + break + use_power(500) + spawn(0) + O.throw_at(target, drive_range * power, power) + flick("mass_driver1", src) + return + +/obj/machinery/mass_driver/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + drive() + ..(severity) diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index b0a1588b4db..170c085a090 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -1,242 +1,242 @@ -// Navigation beacon for AI robots -// Functions as a transponder: looks for incoming signal matching - -var/global/list/navbeacons = list() // no I don't like putting this in, but it will do for now - -/obj/machinery/navbeacon - icon = 'icons/obj/objects.dmi' - icon_state = "navbeacon0-f" - name = "navigation beacon" - desc = "A beacon used for bot navigation." - plane = PLATING_PLANE - anchored = TRUE - var/open = 0 // true if cover is open - var/locked = 1 // true if controls are locked - var/freq = null // DEPRECATED we don't use radios anymore! - var/location = "" // location response text - var/codes_txt // DEPRECATED codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" - var/list/codes = list() // assoc. list of transponder codes - req_access = list(access_engine) - -/obj/machinery/navbeacon/New() - ..() - set_codes_from_txt(codes_txt) - if(freq) - warning("[src] at [x],[y],[z] has deprecated var freq=[freq]. Replace it with proper type.") - - var/turf/T = loc - hide(!T.is_plating()) - navbeacons += src - -// set the transponder codes assoc list from codes_txt -// DEPRECATED - This is kept only for compatibilty with old map files! Do not use this! -// Instead, you should replace the map instance with one of the appropriate navbeacon subtypes. -// See the bottom of this file for a list of subtypes, make your own examples if your map needs more -/obj/machinery/navbeacon/proc/set_codes_from_txt() - if(!codes_txt) - return - warning("[src] at [x],[y],[z] in [get_area(src)] is using the deprecated 'codes_txt' mapping method. Replace it with proper type.") - - codes = list() - var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons - for(var/e in entries) - var/index = findtext(e, "=") // format is "key=value" - if(index) - var/key = copytext(e, 1, index) - var/val = copytext(e, index+1) - codes[key] = val - else - codes[e] = "1" - -/obj/machinery/navbeacon/hides_under_flooring() - return 1 - -// called when turf state changes -// hide the object if turf is intact -/obj/machinery/navbeacon/hide(var/intact) - invisibility = intact ? 101 : 0 - update_icon() - -// update the icon_state -/obj/machinery/navbeacon/update_icon() - var/state="navbeacon[open]" - - if(invisibility) - icon_state = "[state]-f" // if invisible, set icon to faded version - // in case revealed by T-scanner - else - icon_state = "[state]" - -/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user) - var/turf/T = loc - if(!T.is_plating()) - return // prevent intraction when T-scanner revealed - - if(I.is_screwdriver()) - open = !open - playsound(src, I.usesound, 50, 1) - user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") - - update_icon() - - else if(I.GetID()) - if(open) - if(allowed(user)) - locked = !locked - to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]") - else - to_chat(user, "Access denied.") - updateDialog() - else - to_chat(user, "You must open the cover first!") - return - -/obj/machinery/navbeacon/attack_ai(var/mob/user) - interact(user, 1) - -/obj/machinery/navbeacon/attack_hand(var/mob/user) - - if(!user.IsAdvancedToolUser()) - return 0 - - interact(user, 0) - -/obj/machinery/navbeacon/interact(var/mob/user, var/ai = 0) - var/turf/T = loc - if(!T.is_plating()) - return // prevent intraction when T-scanner revealed - - if(!open && !ai) // can't alter controls if not open, unless you're an AI - to_chat(user, "The beacon's control cover is closed.") - return - - - var/t - - if(locked && !ai) - t = {"Navigation Beacon

    -(swipe card to unlock controls)

    -Location: [location ? location : "(none)"]
    -Transponder Codes:
      "} - - for(var/key in codes) - t += "
    • [key] ... [codes[key]]" - t+= "
        " - - else - - t = {"Navigation Beacon

        -(swipe card to lock controls)

        -Location: [location ? location : "(none)"]
        -Transponder Codes:
          "} - - for(var/key in codes) - t += "
        • [key] ... [codes[key]]" - t += " (edit)" - t += " (delete)
          " - t += "(add new)
          " - t+= "
            " - - user << browse(t, "window=navbeacon") - onclose(user, "navbeacon") - return - -/obj/machinery/navbeacon/Topic(href, href_list) - ..() - if(usr.stat) - return - if((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) - if(open && !locked) - usr.set_machine(src) - - if(href_list["locedit"]) - var/newloc = sanitize(tgui_input_text(usr, "Enter New Location", "Navigation Beacon", location, MAX_NAME_LEN)) - if(newloc) - location = newloc - updateDialog() - - else if(href_list["edit"]) - var/codekey = href_list["code"] - - var/newkey = tgui_input_text(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey, MAX_NAME_LEN) - newkey = sanitize(newkey,MAX_NAME_LEN) - if(!newkey) - return - - var/codeval = codes[codekey] - var/newval = tgui_input_text(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval, MAX_NAME_LEN) - newval = sanitize(newval,MAX_NAME_LEN) - if(!newval) - newval = codekey - return - - codes.Remove(codekey) - codes[newkey] = newval - - updateDialog() - - else if(href_list["delete"]) - var/codekey = href_list["code"] - codes.Remove(codekey) - updateDialog() - - else if(href_list["add"]) - - var/newkey = tgui_input_text(usr, "Enter New Transponder Code Key", "Navigation Beacon", null, MAX_NAME_LEN) - newkey = sanitize(newkey,MAX_NAME_LEN) - if(!newkey) - return - - var/newval = tgui_input_text(usr, "Enter New Transponder Code Value", "Navigation Beacon", null, MAX_NAME_LEN) - newval = sanitize(newval,MAX_NAME_LEN) - if(!newval) - newval = "1" - return - - if(!codes) - codes = new() - - codes[newkey] = newval - - updateDialog() - -/obj/machinery/navbeacon/Destroy() - navbeacons.Remove(src) - ..() - - -// -// Nav Beacon Mapping -// These subtypes are what you should actually put into maps! they will make your life much easier. -// -// Developer Note: navbeacons do not HAVE to use these subtypes. They are purely for mapping convenience. -// You can feel free to construct them in-game as just /obj/machinery/navbeacon and they will work just -// fine, and you can define your own specific types for every instance on map if you want (BayStation does) -// This design is a compromise that means you can do mapping without every single one being its own type -// but with it still being easy to map ~ Leshana -// - -// Mulebot delivery destinations - -/obj/machinery/navbeacon/delivery/north - codes = list("delivery" = 1, "dir" = NORTH) - -/obj/machinery/navbeacon/delivery/south - codes = list("delivery" = 1, "dir" = SOUTH) - -/obj/machinery/navbeacon/delivery/east - codes = list("delivery" = 1, "dir" = EAST) - -/obj/machinery/navbeacon/delivery/west - codes = list("delivery" = 1, "dir" = WEST) - - -// For part of the patrol route -// You MUST set "location" -// You MUST set "next_patrol" -/obj/machinery/navbeacon/patrol - var/next_patrol - -/obj/machinery/navbeacon/patrol/New() - codes = list("patrol" = 1, "next_patrol" = next_patrol) - ..() +// Navigation beacon for AI robots +// Functions as a transponder: looks for incoming signal matching + +var/global/list/navbeacons = list() // no I don't like putting this in, but it will do for now + +/obj/machinery/navbeacon + icon = 'icons/obj/objects.dmi' + icon_state = "navbeacon0-f" + name = "navigation beacon" + desc = "A beacon used for bot navigation." + plane = PLATING_PLANE + anchored = TRUE + var/open = 0 // true if cover is open + var/locked = 1 // true if controls are locked + var/freq = null // DEPRECATED we don't use radios anymore! + var/location = "" // location response text + var/codes_txt // DEPRECATED codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" + var/list/codes = list() // assoc. list of transponder codes + req_access = list(access_engine) + +/obj/machinery/navbeacon/New() + ..() + set_codes_from_txt(codes_txt) + if(freq) + warning("[src] at [x],[y],[z] has deprecated var freq=[freq]. Replace it with proper type.") + + var/turf/T = loc + hide(!T.is_plating()) + navbeacons += src + +// set the transponder codes assoc list from codes_txt +// DEPRECATED - This is kept only for compatibilty with old map files! Do not use this! +// Instead, you should replace the map instance with one of the appropriate navbeacon subtypes. +// See the bottom of this file for a list of subtypes, make your own examples if your map needs more +/obj/machinery/navbeacon/proc/set_codes_from_txt() + if(!codes_txt) + return + warning("[src] at [x],[y],[z] in [get_area(src)] is using the deprecated 'codes_txt' mapping method. Replace it with proper type.") + + codes = list() + var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons + for(var/e in entries) + var/index = findtext(e, "=") // format is "key=value" + if(index) + var/key = copytext(e, 1, index) + var/val = copytext(e, index+1) + codes[key] = val + else + codes[e] = "1" + +/obj/machinery/navbeacon/hides_under_flooring() + return 1 + +// called when turf state changes +// hide the object if turf is intact +/obj/machinery/navbeacon/hide(var/intact) + invisibility = intact ? 101 : 0 + update_icon() + +// update the icon_state +/obj/machinery/navbeacon/update_icon() + var/state="navbeacon[open]" + + if(invisibility) + icon_state = "[state]-f" // if invisible, set icon to faded version + // in case revealed by T-scanner + else + icon_state = "[state]" + +/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user) + var/turf/T = loc + if(!T.is_plating()) + return // prevent intraction when T-scanner revealed + + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + open = !open + playsound(src, I.usesound, 50, 1) + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + + update_icon() + + else if(I.GetID()) + if(open) + if(allowed(user)) + locked = !locked + to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]") + else + to_chat(user, "Access denied.") + updateDialog() + else + to_chat(user, "You must open the cover first!") + return + +/obj/machinery/navbeacon/attack_ai(var/mob/user) + interact(user, 1) + +/obj/machinery/navbeacon/attack_hand(var/mob/user) + + if(!user.IsAdvancedToolUser()) + return 0 + + interact(user, 0) + +/obj/machinery/navbeacon/interact(var/mob/user, var/ai = 0) + var/turf/T = loc + if(!T.is_plating()) + return // prevent intraction when T-scanner revealed + + if(!open && !ai) // can't alter controls if not open, unless you're an AI + to_chat(user, "The beacon's control cover is closed.") + return + + + var/t + + if(locked && !ai) + t = {"Navigation Beacon

            +(swipe card to unlock controls)

            +Location: [location ? location : "(none)"]
            +Transponder Codes:
              "} + + for(var/key in codes) + t += "
            • [key] ... [codes[key]]" + t+= "
                " + + else + + t = {"Navigation Beacon

                +(swipe card to lock controls)

                +Location: [location ? location : "(none)"]
                +Transponder Codes:
                  "} + + for(var/key in codes) + t += "
                • [key] ... [codes[key]]" + t += " (edit)" + t += " (delete)
                  " + t += "(add new)
                  " + t+= "
                    " + + user << browse(t, "window=navbeacon") + onclose(user, "navbeacon") + return + +/obj/machinery/navbeacon/Topic(href, href_list) + ..() + if(usr.stat) + return + if((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) + if(open && !locked) + usr.set_machine(src) + + if(href_list["locedit"]) + var/newloc = sanitize(tgui_input_text(usr, "Enter New Location", "Navigation Beacon", location, MAX_NAME_LEN)) + if(newloc) + location = newloc + updateDialog() + + else if(href_list["edit"]) + var/codekey = href_list["code"] + + var/newkey = tgui_input_text(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey, MAX_NAME_LEN) + newkey = sanitize(newkey,MAX_NAME_LEN) + if(!newkey) + return + + var/codeval = codes[codekey] + var/newval = tgui_input_text(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval, MAX_NAME_LEN) + newval = sanitize(newval,MAX_NAME_LEN) + if(!newval) + newval = codekey + return + + codes.Remove(codekey) + codes[newkey] = newval + + updateDialog() + + else if(href_list["delete"]) + var/codekey = href_list["code"] + codes.Remove(codekey) + updateDialog() + + else if(href_list["add"]) + + var/newkey = tgui_input_text(usr, "Enter New Transponder Code Key", "Navigation Beacon", null, MAX_NAME_LEN) + newkey = sanitize(newkey,MAX_NAME_LEN) + if(!newkey) + return + + var/newval = tgui_input_text(usr, "Enter New Transponder Code Value", "Navigation Beacon", null, MAX_NAME_LEN) + newval = sanitize(newval,MAX_NAME_LEN) + if(!newval) + newval = "1" + return + + if(!codes) + codes = new() + + codes[newkey] = newval + + updateDialog() + +/obj/machinery/navbeacon/Destroy() + navbeacons.Remove(src) + ..() + + +// +// Nav Beacon Mapping +// These subtypes are what you should actually put into maps! they will make your life much easier. +// +// Developer Note: navbeacons do not HAVE to use these subtypes. They are purely for mapping convenience. +// You can feel free to construct them in-game as just /obj/machinery/navbeacon and they will work just +// fine, and you can define your own specific types for every instance on map if you want (BayStation does) +// This design is a compromise that means you can do mapping without every single one being its own type +// but with it still being easy to map ~ Leshana +// + +// Mulebot delivery destinations + +/obj/machinery/navbeacon/delivery/north + codes = list("delivery" = 1, "dir" = NORTH) + +/obj/machinery/navbeacon/delivery/south + codes = list("delivery" = 1, "dir" = SOUTH) + +/obj/machinery/navbeacon/delivery/east + codes = list("delivery" = 1, "dir" = EAST) + +/obj/machinery/navbeacon/delivery/west + codes = list("delivery" = 1, "dir" = WEST) + + +// For part of the patrol route +// You MUST set "location" +// You MUST set "next_patrol" +/obj/machinery/navbeacon/patrol + var/next_patrol + +/obj/machinery/navbeacon/patrol/New() + codes = list("patrol" = 1, "next_patrol" = next_patrol) + ..() diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index b9646235c07..1784635d599 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -55,7 +55,7 @@ var/bomb_set return /obj/machinery/nuclearbomb/attackby(obj/item/weapon/O as obj, mob/user as mob) - if(O.is_screwdriver()) + if(O.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, O.usesound, 50, 1) add_fingerprint(user) if(auth) @@ -78,7 +78,7 @@ var/bomb_set flick("nuclearbombc", src) return - if(O.is_wirecutter() || istype(O, /obj/item/device/multitool)) + if(O.has_tool_quality(TOOL_WIRECUTTER) || istype(O, /obj/item/device/multitool)) if(opened == 1) nukehack_win(user) return @@ -94,9 +94,9 @@ var/bomb_set if(anchored) switch(removal_stage) if(0) - if(istype(O,/obj/item/weapon/weldingtool)) + if(O.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = O + var/obj/item/weapon/weldingtool/WT = O.get_welder() if(!WT.isOn()) return if(WT.get_fuel() < 5) // uses up 5 fuel. to_chat(user, "You need more fuel to complete this task.") @@ -111,7 +111,7 @@ var/bomb_set return if(1) - if(O.is_crowbar()) + if(O.has_tool_quality(TOOL_CROWBAR)) user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [O]...") playsound(src, O.usesound, 50, 1) @@ -122,9 +122,9 @@ var/bomb_set return if(2) - if(istype(O,/obj/item/weapon/weldingtool)) + if(O.has_tool_quality(TOOL_WELDER)) - var/obj/item/weapon/weldingtool/WT = O + var/obj/item/weapon/weldingtool/WT = O.get_welder() if(!WT.isOn()) return if(WT.get_fuel() < 5) // uses up 5 fuel. to_chat(user, "You need more fuel to complete this task.") @@ -139,7 +139,7 @@ var/bomb_set return if(3) - if(O.is_wrench()) + if(O.has_tool_quality(TOOL_WRENCH)) user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") playsound(src, O.usesound, 50, 1) @@ -150,7 +150,7 @@ var/bomb_set return if(4) - if(O.is_crowbar()) + if(O.has_tool_quality(TOOL_CROWBAR)) user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") playsound(src, O.usesound, 50, 1) @@ -262,7 +262,7 @@ var/bomb_set visible_message("The [src] emits a quiet whirling noise!") if(href_list["act"] == "wire") var/obj/item/I = usr.get_active_hand() - if(!I.is_wirecutter()) + if(!I.has_tool_quality(TOOL_WIRECUTTER)) to_chat(usr, "You need wirecutters!") else wires[temp_wire] = !wires[temp_wire] diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index c63ea1f1d82..058abb899d7 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -1,335 +1,335 @@ -//#define AMAP -/obj/machinery/computer/security/verb/station_map() - set name = ".map" - set category = "Object" - set src in view(1) - usr.set_machine(src) - if(!mapping) return - log_game("[usr]([usr.key]) used station map L[z] in [src.loc.loc]") - drawmap(usr) - -/obj/machinery/computer/security/proc/drawmap(var/mob/user as mob) - - var/icx = round(world.maxx/16) + 1 - var/icy = round(world.maxy/16) + 1 - - var/xoff = round((icx*16-world.maxx)-2) - var/yoff = round((icy*16-world.maxy)-2) - - var/icount = icx * icy - - var/list/imap = list() - -#ifdef AMAP - - for(var/i = 0; i\The [user] [(stat & MAINT) ? "opens" : "closes"] \the [src].", "You [(stat & MAINT) ? "open" : "close"] \the [src].") icon_state = (stat & MAINT) ? icon_state_open : icon_state_closed diff --git a/code/game/machinery/pda_multicaster.dm b/code/game/machinery/pda_multicaster.dm index ef7612bf87f..d8a75ebedcd 100644 --- a/code/game/machinery/pda_multicaster.dm +++ b/code/game/machinery/pda_multicaster.dm @@ -39,9 +39,9 @@ icon_state = "[initial(icon_state)]_off" /obj/machinery/pda_multicaster/attackby(obj/item/I, mob/user) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) default_deconstruction_screwdriver(user, I) - else if(I.is_crowbar()) + else if(I.has_tool_quality(TOOL_CROWBAR)) default_deconstruction_crowbar(user, I) else ..() diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index f5f25320800..cee7977ecbb 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -1,282 +1,282 @@ -/*CONTENTS -Buildable pipes -Buildable meters -*/ - -/obj/item/pipe - name = "pipe" - desc = "A pipe." - var/pipe_type - var/pipename - force = 7 - throwforce = 7 - icon = 'icons/obj/pipe-item.dmi' - icon_state = "simple" - item_state = "buildpipe" - w_class = ITEMSIZE_NORMAL - level = 2 - var/piping_layer = PIPING_LAYER_DEFAULT - var/dispenser_class // Tells the dispenser what orientations we support, so RPD can show previews. - -// One subtype for each way components connect to neighbors -/obj/item/pipe/directional - dispenser_class = PIPE_DIRECTIONAL -/obj/item/pipe/binary - dispenser_class = PIPE_STRAIGHT -/obj/item/pipe/binary/bendable - dispenser_class = PIPE_BENDABLE -/obj/item/pipe/trinary - dispenser_class = PIPE_TRINARY -/obj/item/pipe/trinary/flippable - dispenser_class = PIPE_TRIN_M - var/mirrored = FALSE -/obj/item/pipe/quaternary - dispenser_class = PIPE_ONEDIR - -/** - * Call constructor with: - * @param loc Location - * @pipe_type - */ -/obj/item/pipe/Initialize(var/mapload, var/_pipe_type, var/_dir, var/obj/machinery/atmospherics/make_from) - if(make_from) - make_from_existing(make_from) - else - pipe_type = _pipe_type - set_dir(_dir) - - update() - pixel_x += rand(-5, 5) - pixel_y += rand(-5, 5) - return ..() - -/obj/item/pipe/proc/make_from_existing(obj/machinery/atmospherics/make_from) - set_dir(make_from.dir) - pipename = make_from.name - if(make_from.req_access) - src.req_access = make_from.req_access - if(make_from.req_one_access) - src.req_one_access = make_from.req_one_access - color = make_from.pipe_color - pipe_type = make_from.type - -/obj/item/pipe/trinary/flippable/make_from_existing(obj/machinery/atmospherics/trinary/make_from) - ..() - if(make_from.mirrored) - do_a_flip() - -/obj/item/pipe/dropped() - if(loc) - setPipingLayer(piping_layer) - return ..() - -/obj/item/pipe/proc/setPipingLayer(new_layer = PIPING_LAYER_DEFAULT) - var/obj/machinery/atmospherics/fakeA = pipe_type - if(initial(fakeA.pipe_flags) & (PIPING_ALL_LAYER|PIPING_DEFAULT_LAYER_ONLY)) - new_layer = PIPING_LAYER_DEFAULT - piping_layer = new_layer - // Do it the Polaris way - switch(piping_layer) - if(PIPING_LAYER_SCRUBBER) - color = PIPE_COLOR_RED - name = "[initial(fakeA.name)] scrubber fitting" - if(PIPING_LAYER_SUPPLY) - color = PIPE_COLOR_BLUE - name = "[initial(fakeA.name)] supply fitting" - if(PIPING_LAYER_FUEL) - color = PIPE_COLOR_YELLOW - name = "[initial(fakeA.name)] fuel fitting" - if(PIPING_LAYER_AUX) - color = PIPE_COLOR_CYAN - name = "[initial(fakeA.name)] aux fitting" - // Or if we were to do it the TG way... - // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) - // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) - // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) - -/obj/item/pipe/proc/update() - var/obj/machinery/atmospherics/fakeA = pipe_type - name = "[initial(fakeA.name)] fitting" - icon_state = initial(fakeA.pipe_state) - -/obj/item/pipe/verb/flip() - set category = "Object" - set name = "Flip Pipe" - set src in view(1) - - if ( usr.stat || usr.restrained() || !usr.canmove ) - return - - do_a_flip() - -/obj/item/pipe/proc/do_a_flip() - set_dir(turn(dir, -180)) - fixdir() - -/obj/item/pipe/trinary/flippable/do_a_flip() - // set_dir(turn(dir, flipped ? 45 : -45)) - // TG has a magic icon set with the flipped versions in the diagonals. - // We may switch to this later, but for now gotta do some magic. - mirrored = !mirrored - var/obj/machinery/atmospherics/fakeA = pipe_type - icon_state = "[initial(fakeA.pipe_state)][mirrored ? "m" : ""]" - -/obj/item/pipe/verb/rotate_clockwise() - set category = "Object" - set name = "Rotate Pipe Clockwise" - set src in view(1) - - if ( usr.stat || usr.restrained() || !usr.canmove ) - return - - src.set_dir(turn(src.dir, 270)) - fixdir() - -// Don't let pulling a pipe straighten it out. -/obj/item/pipe/binary/bendable/Move() - var/old_bent = !IS_CARDINAL(dir) - . = ..() - if(old_bent && IS_CARDINAL(dir)) - set_dir(turn(src.dir, -45)) - -//Helper to clean up dir -/obj/item/pipe/proc/fixdir() - return - -/obj/item/pipe/binary/fixdir() - if(dir == SOUTH) - set_dir(NORTH) - else if(dir == WEST) - set_dir(EAST) - -/obj/item/pipe/trinary/flippable/fixdir() - if(dir in cornerdirs) - set_dir(turn(dir, 45)) - -/obj/item/pipe/attack_self(mob/user) - set_dir(turn(dir,-90)) - fixdir() - -//called when a turf is attacked with a pipe item -/obj/item/pipe/afterattack(turf/simulated/floor/target, mob/user, proximity) - if(!proximity) return - if(istype(target) && user.canUnEquip(src)) - user.drop_from_inventory(src, target) - else - return ..() - -/obj/item/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(W.is_wrench()) - return wrench_act(user, W) - return ..() - -/obj/item/pipe/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) - if(!isturf(loc)) - return TRUE - - add_fingerprint(user) - fixdir() - - var/obj/machinery/atmospherics/fakeA = pipe_type - var/flags = initial(fakeA.pipe_flags) - for(var/obj/machinery/atmospherics/M in loc) - if((M.pipe_flags & flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. - to_chat(user, "Something is hogging the tile!") - return TRUE - if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER - continue - if(M.get_init_dirs() & SSmachines.get_init_dirs(pipe_type, dir)) // matches at least one direction on either type of pipe - to_chat(user, "There is already a pipe at that location!") - return TRUE - // no conflicts found - - var/obj/machinery/atmospherics/A = new pipe_type(loc) - build_pipe(A) - // TODO - Evaluate and remove the "need at least one thing to connect to" thing ~Leshana - // With how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment. - if (QDELETED(A)) - to_chat(user, "There's nothing to connect this pipe section to!") - return TRUE - transfer_fingerprints_to(A) - - playsound(src, W.usesound, 50, 1) - user.visible_message( \ - "[user] fastens \the [src].", \ - "You fasten \the [src].", \ - "You hear ratcheting.") - - qdel(src) - -/obj/item/pipe/proc/build_pipe(obj/machinery/atmospherics/A) - A.set_dir(dir) - A.init_dir() - if(pipename) - A.name = pipename - if(req_access) - A.req_access = req_access - if(req_one_access) - A.req_one_access = req_one_access - A.on_construction(color, piping_layer) - -/obj/item/pipe/trinary/flippable/build_pipe(obj/machinery/atmospherics/trinary/T) - T.mirrored = mirrored - . = ..() - -// Lookup the initialize_directions for a given atmos machinery instance facing dir. -// TODO - Right now this determines the answer by instantiating an instance and checking! -// There has to be a better way... ~Leshana -/datum/controller/subsystem/machines/proc/get_init_dirs(type, dir) - var/static/list/pipe_init_dirs_cache = list() - if(!pipe_init_dirs_cache[type]) - pipe_init_dirs_cache[type] = list() - - if(!pipe_init_dirs_cache[type]["[dir]"]) - var/obj/machinery/atmospherics/temp = new type(null, dir) - pipe_init_dirs_cache[type]["[dir]"] = temp.get_init_dirs() - qdel(temp) - - return pipe_init_dirs_cache[type]["[dir]"] - - - - - -// -// Meters are special - not like any other pipes or components -// - -/obj/item/pipe_meter - name = "meter" - desc = "A meter that can be laid on pipes." - icon = 'icons/obj/pipe-item.dmi' - icon_state = "meter" - item_state = "buildpipe" - w_class = ITEMSIZE_LARGE - var/piping_layer = PIPING_LAYER_DEFAULT - -/obj/item/pipe_meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(W.is_wrench()) - return wrench_act(user, W) - return ..() - -/obj/item/pipe_meter/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) - var/obj/machinery/atmospherics/pipe/pipe - for(var/obj/machinery/atmospherics/pipe/P in loc) - if(P.piping_layer == piping_layer) - pipe = P - break - if(!pipe) - to_chat(user, "You need to fasten it to a pipe!") - return TRUE - new /obj/machinery/meter(loc, piping_layer) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You fasten the meter to the pipe.") - qdel(src) - -/obj/item/pipe_meter/dropped() - . = ..() - if(loc) - setAttachLayer(piping_layer) - -/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT) - piping_layer = new_layer +/*CONTENTS +Buildable pipes +Buildable meters +*/ + +/obj/item/pipe + name = "pipe" + desc = "A pipe." + var/pipe_type + var/pipename + force = 7 + throwforce = 7 + icon = 'icons/obj/pipe-item.dmi' + icon_state = "simple" + item_state = "buildpipe" + w_class = ITEMSIZE_NORMAL + level = 2 + var/piping_layer = PIPING_LAYER_DEFAULT + var/dispenser_class // Tells the dispenser what orientations we support, so RPD can show previews. + +// One subtype for each way components connect to neighbors +/obj/item/pipe/directional + dispenser_class = PIPE_DIRECTIONAL +/obj/item/pipe/binary + dispenser_class = PIPE_STRAIGHT +/obj/item/pipe/binary/bendable + dispenser_class = PIPE_BENDABLE +/obj/item/pipe/trinary + dispenser_class = PIPE_TRINARY +/obj/item/pipe/trinary/flippable + dispenser_class = PIPE_TRIN_M + var/mirrored = FALSE +/obj/item/pipe/quaternary + dispenser_class = PIPE_ONEDIR + +/** + * Call constructor with: + * @param loc Location + * @pipe_type + */ +/obj/item/pipe/Initialize(var/mapload, var/_pipe_type, var/_dir, var/obj/machinery/atmospherics/make_from) + if(make_from) + make_from_existing(make_from) + else + pipe_type = _pipe_type + set_dir(_dir) + + update() + pixel_x += rand(-5, 5) + pixel_y += rand(-5, 5) + return ..() + +/obj/item/pipe/proc/make_from_existing(obj/machinery/atmospherics/make_from) + set_dir(make_from.dir) + pipename = make_from.name + if(make_from.req_access) + src.req_access = make_from.req_access + if(make_from.req_one_access) + src.req_one_access = make_from.req_one_access + color = make_from.pipe_color + pipe_type = make_from.type + +/obj/item/pipe/trinary/flippable/make_from_existing(obj/machinery/atmospherics/trinary/make_from) + ..() + if(make_from.mirrored) + do_a_flip() + +/obj/item/pipe/dropped() + if(loc) + setPipingLayer(piping_layer) + return ..() + +/obj/item/pipe/proc/setPipingLayer(new_layer = PIPING_LAYER_DEFAULT) + var/obj/machinery/atmospherics/fakeA = pipe_type + if(initial(fakeA.pipe_flags) & (PIPING_ALL_LAYER|PIPING_DEFAULT_LAYER_ONLY)) + new_layer = PIPING_LAYER_DEFAULT + piping_layer = new_layer + // Do it the Polaris way + switch(piping_layer) + if(PIPING_LAYER_SCRUBBER) + color = PIPE_COLOR_RED + name = "[initial(fakeA.name)] scrubber fitting" + if(PIPING_LAYER_SUPPLY) + color = PIPE_COLOR_BLUE + name = "[initial(fakeA.name)] supply fitting" + if(PIPING_LAYER_FUEL) + color = PIPE_COLOR_YELLOW + name = "[initial(fakeA.name)] fuel fitting" + if(PIPING_LAYER_AUX) + color = PIPE_COLOR_CYAN + name = "[initial(fakeA.name)] aux fitting" + // Or if we were to do it the TG way... + // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) + // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) + // layer = initial(layer) + PIPE_LAYER_OFFSET(piping_layer) + +/obj/item/pipe/proc/update() + var/obj/machinery/atmospherics/fakeA = pipe_type + name = "[initial(fakeA.name)] fitting" + icon_state = initial(fakeA.pipe_state) + +/obj/item/pipe/verb/flip() + set category = "Object" + set name = "Flip Pipe" + set src in view(1) + + if ( usr.stat || usr.restrained() || !usr.canmove ) + return + + do_a_flip() + +/obj/item/pipe/proc/do_a_flip() + set_dir(turn(dir, -180)) + fixdir() + +/obj/item/pipe/trinary/flippable/do_a_flip() + // set_dir(turn(dir, flipped ? 45 : -45)) + // TG has a magic icon set with the flipped versions in the diagonals. + // We may switch to this later, but for now gotta do some magic. + mirrored = !mirrored + var/obj/machinery/atmospherics/fakeA = pipe_type + icon_state = "[initial(fakeA.pipe_state)][mirrored ? "m" : ""]" + +/obj/item/pipe/verb/rotate_clockwise() + set category = "Object" + set name = "Rotate Pipe Clockwise" + set src in view(1) + + if ( usr.stat || usr.restrained() || !usr.canmove ) + return + + src.set_dir(turn(src.dir, 270)) + fixdir() + +// Don't let pulling a pipe straighten it out. +/obj/item/pipe/binary/bendable/Move() + var/old_bent = !IS_CARDINAL(dir) + . = ..() + if(old_bent && IS_CARDINAL(dir)) + set_dir(turn(src.dir, -45)) + +//Helper to clean up dir +/obj/item/pipe/proc/fixdir() + return + +/obj/item/pipe/binary/fixdir() + if(dir == SOUTH) + set_dir(NORTH) + else if(dir == WEST) + set_dir(EAST) + +/obj/item/pipe/trinary/flippable/fixdir() + if(dir in cornerdirs) + set_dir(turn(dir, 45)) + +/obj/item/pipe/attack_self(mob/user) + set_dir(turn(dir,-90)) + fixdir() + +//called when a turf is attacked with a pipe item +/obj/item/pipe/afterattack(turf/simulated/floor/target, mob/user, proximity) + if(!proximity) return + if(istype(target) && user.canUnEquip(src)) + user.drop_from_inventory(src, target) + else + return ..() + +/obj/item/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + return wrench_act(user, W) + return ..() + +/obj/item/pipe/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) + if(!isturf(loc)) + return TRUE + + add_fingerprint(user) + fixdir() + + var/obj/machinery/atmospherics/fakeA = pipe_type + var/flags = initial(fakeA.pipe_flags) + for(var/obj/machinery/atmospherics/M in loc) + if((M.pipe_flags & flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers. + to_chat(user, "Something is hogging the tile!") + return TRUE + if((M.piping_layer != piping_layer) && !((M.pipe_flags | flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER + continue + if(M.get_init_dirs() & SSmachines.get_init_dirs(pipe_type, dir)) // matches at least one direction on either type of pipe + to_chat(user, "There is already a pipe at that location!") + return TRUE + // no conflicts found + + var/obj/machinery/atmospherics/A = new pipe_type(loc) + build_pipe(A) + // TODO - Evaluate and remove the "need at least one thing to connect to" thing ~Leshana + // With how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment. + if (QDELETED(A)) + to_chat(user, "There's nothing to connect this pipe section to!") + return TRUE + transfer_fingerprints_to(A) + + playsound(src, W.usesound, 50, 1) + user.visible_message( \ + "[user] fastens \the [src].", \ + "You fasten \the [src].", \ + "You hear ratcheting.") + + qdel(src) + +/obj/item/pipe/proc/build_pipe(obj/machinery/atmospherics/A) + A.set_dir(dir) + A.init_dir() + if(pipename) + A.name = pipename + if(req_access) + A.req_access = req_access + if(req_one_access) + A.req_one_access = req_one_access + A.on_construction(color, piping_layer) + +/obj/item/pipe/trinary/flippable/build_pipe(obj/machinery/atmospherics/trinary/T) + T.mirrored = mirrored + . = ..() + +// Lookup the initialize_directions for a given atmos machinery instance facing dir. +// TODO - Right now this determines the answer by instantiating an instance and checking! +// There has to be a better way... ~Leshana +/datum/controller/subsystem/machines/proc/get_init_dirs(type, dir) + var/static/list/pipe_init_dirs_cache = list() + if(!pipe_init_dirs_cache[type]) + pipe_init_dirs_cache[type] = list() + + if(!pipe_init_dirs_cache[type]["[dir]"]) + var/obj/machinery/atmospherics/temp = new type(null, dir) + pipe_init_dirs_cache[type]["[dir]"] = temp.get_init_dirs() + qdel(temp) + + return pipe_init_dirs_cache[type]["[dir]"] + + + + + +// +// Meters are special - not like any other pipes or components +// + +/obj/item/pipe_meter + name = "meter" + desc = "A meter that can be laid on pipes." + icon = 'icons/obj/pipe-item.dmi' + icon_state = "meter" + item_state = "buildpipe" + w_class = ITEMSIZE_LARGE + var/piping_layer = PIPING_LAYER_DEFAULT + +/obj/item/pipe_meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + return wrench_act(user, W) + return ..() + +/obj/item/pipe_meter/proc/wrench_act(var/mob/living/user, var/obj/item/weapon/tool/wrench/W) + var/obj/machinery/atmospherics/pipe/pipe + for(var/obj/machinery/atmospherics/pipe/P in loc) + if(P.piping_layer == piping_layer) + pipe = P + break + if(!pipe) + to_chat(user, "You need to fasten it to a pipe!") + return TRUE + new /obj/machinery/meter(loc, piping_layer) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You fasten the meter to the pipe.") + qdel(src) + +/obj/item/pipe_meter/dropped() + . = ..() + if(loc) + setAttachLayer(piping_layer) + +/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT) + piping_layer = new_layer diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index 8827dc8782c..96da384789b 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -1,178 +1,178 @@ -/obj/machinery/pipedispenser - name = "Pipe Dispenser" - desc = "A large machine that can rapidly dispense pipes." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "pipe_d" - density = TRUE - anchored = TRUE - unacidable = TRUE - var/unwrenched = 0 - var/wait = 0 - var/p_layer = PIPING_LAYER_REGULAR - var/static/list/pipe_layers = list( - "Regular" = PIPING_LAYER_REGULAR, - "Supply" = PIPING_LAYER_SUPPLY, - "Scrubber" = PIPING_LAYER_SCRUBBER, - "Fuel" = PIPING_LAYER_FUEL, - "Aux" = PIPING_LAYER_AUX - ) - var/disposals = FALSE - -// TODO - Its about time to make this NanoUI don't we think? -/obj/machinery/pipedispenser/attack_hand(var/mob/user as mob) - if((. = ..())) - return - tgui_interact(user) - -/obj/machinery/pipedispenser/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/spritesheet/pipes), - ) - -/obj/machinery/pipedispenser/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PipeDispenser", name) - ui.open() - -/obj/machinery/pipedispenser/tgui_data(mob/user) - var/list/data = list( - "disposals" = disposals, - "p_layer" = p_layer, - "pipe_layers" = pipe_layers, - ) - - var/list/recipes - if(disposals) - recipes = GLOB.disposal_pipe_recipes - else - recipes = GLOB.atmos_pipe_recipes - - for(var/c in recipes) - var/list/cat = recipes[c] - var/list/r = list() - for(var/i in 1 to cat.len) - var/datum/pipe_recipe/info = cat[i] - r += list(list("pipe_name" = info.name, "ref" = "\ref[info]")) - // Stationary pipe dispensers don't allow you to pre-select pipe directions. - // This makes it impossble to spawn bent versions of bendable pipes. - // We add a "Bent" pipe type with a special param to work around it. - if(info.dirtype == PIPE_BENDABLE) - r += list(list( - "pipe_name" = ("Bent " + info.name), - "ref" = "\ref[info]", - "bent" = TRUE - )) - data["categories"] += list(list("cat_name" = c, "recipes" = r)) - - return data - -/obj/machinery/pipedispenser/tgui_act(action, params) - if(..()) - return TRUE - if(unwrenched || !usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return TRUE - - . = TRUE - switch(action) - if("p_layer") - p_layer = text2num(params["p_layer"]) - if("dispense_pipe") - if(!wait) - var/datum/pipe_recipe/recipe = locate(params["ref"]) - if(!istype(recipe)) - return - - var/target_dir = NORTH - if(params["bent"]) - target_dir = NORTHEAST - - var/obj/created_object = null - if(istype(recipe, /datum/pipe_recipe/pipe)) - var/datum/pipe_recipe/pipe/R = recipe - created_object = new R.construction_type(loc, recipe.pipe_type, target_dir) - var/obj/item/pipe/P = created_object - P.setPipingLayer(p_layer) - else if(istype(recipe, /datum/pipe_recipe/disposal)) - var/datum/pipe_recipe/disposal/D = recipe - var/obj/structure/disposalconstruct/C = new(loc, D.pipe_type, target_dir, 0, D.subtype ? D.subtype : 0) - C.update() - created_object = C - else if(istype(recipe, /datum/pipe_recipe/meter)) - created_object = new recipe.pipe_type(loc) - else - log_runtime(EXCEPTION("Warning: [usr] attempted to spawn pipe recipe type by params [json_encode(params)] ([recipe] [recipe?.type]), but it was not allowed by this machine ([src] [type])")) - return - - created_object.add_fingerprint(usr) - wait = TRUE - VARSET_IN(src, wait, FALSE, 15) - - -/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob) - src.add_fingerprint(usr) - if (istype(W, /obj/item/pipe) || istype(W, /obj/item/pipe_meter)) - to_chat(usr, "You put [W] back in [src].") - user.drop_item() - qdel(W) - return - else if(W.is_wrench()) - if (unwrenched==0) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src] from the floor...") - if (do_after(user, 40 * W.toolspeed)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src]. Now it can be pulled somewhere else.", \ - "You hear ratchet.") - src.anchored = FALSE - src.stat |= MAINT - src.unwrenched = 1 - if (usr.machine==src) - usr << browse(null, "window=pipedispenser") - else /*if (unwrenched==1)*/ - playsound(src, W.usesound, 50, 1) - to_chat(user, "You begin to fasten \the [src] to the floor...") - if (do_after(user, 20 * W.toolspeed)) - user.visible_message( \ - "[user] fastens \the [src].", \ - "You have fastened \the [src]. Now it can dispense pipes.", \ - "You hear ratchet.") - src.anchored = TRUE - src.stat &= ~MAINT - src.unwrenched = 0 - power_change() - else - return ..() - -/obj/machinery/pipedispenser/disposal - name = "Disposal Pipe Dispenser" - desc = "A large machine that can rapidly dispense pipes. This one seems to dispsense disposal pipes." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "pipe_d" - density = TRUE - anchored = TRUE - disposals = TRUE - -//Allow you to drag-drop disposal pipes into it -/obj/machinery/pipedispenser/disposal/MouseDrop_T(var/obj/structure/disposalconstruct/pipe as obj, mob/usr as mob) - if(!usr.canmove || usr.stat || usr.restrained()) - return - - if (!istype(pipe) || get_dist(usr, src) > 1 || get_dist(src,pipe) > 1 ) - return - - if (pipe.anchored) - return - - to_chat(usr, "You shove [pipe] back in [src].") - qdel(pipe) - -// adding a pipe dispensers that spawn unhooked from the ground -/obj/machinery/pipedispenser/orderable - anchored = FALSE - unwrenched = 1 - -/obj/machinery/pipedispenser/disposal/orderable - anchored = FALSE - unwrenched = 1 +/obj/machinery/pipedispenser + name = "Pipe Dispenser" + desc = "A large machine that can rapidly dispense pipes." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "pipe_d" + density = TRUE + anchored = TRUE + unacidable = TRUE + var/unwrenched = 0 + var/wait = 0 + var/p_layer = PIPING_LAYER_REGULAR + var/static/list/pipe_layers = list( + "Regular" = PIPING_LAYER_REGULAR, + "Supply" = PIPING_LAYER_SUPPLY, + "Scrubber" = PIPING_LAYER_SCRUBBER, + "Fuel" = PIPING_LAYER_FUEL, + "Aux" = PIPING_LAYER_AUX + ) + var/disposals = FALSE + +// TODO - Its about time to make this NanoUI don't we think? +/obj/machinery/pipedispenser/attack_hand(var/mob/user as mob) + if((. = ..())) + return + tgui_interact(user) + +/obj/machinery/pipedispenser/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/pipes), + ) + +/obj/machinery/pipedispenser/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PipeDispenser", name) + ui.open() + +/obj/machinery/pipedispenser/tgui_data(mob/user) + var/list/data = list( + "disposals" = disposals, + "p_layer" = p_layer, + "pipe_layers" = pipe_layers, + ) + + var/list/recipes + if(disposals) + recipes = GLOB.disposal_pipe_recipes + else + recipes = GLOB.atmos_pipe_recipes + + for(var/c in recipes) + var/list/cat = recipes[c] + var/list/r = list() + for(var/i in 1 to cat.len) + var/datum/pipe_recipe/info = cat[i] + r += list(list("pipe_name" = info.name, "ref" = "\ref[info]")) + // Stationary pipe dispensers don't allow you to pre-select pipe directions. + // This makes it impossble to spawn bent versions of bendable pipes. + // We add a "Bent" pipe type with a special param to work around it. + if(info.dirtype == PIPE_BENDABLE) + r += list(list( + "pipe_name" = ("Bent " + info.name), + "ref" = "\ref[info]", + "bent" = TRUE + )) + data["categories"] += list(list("cat_name" = c, "recipes" = r)) + + return data + +/obj/machinery/pipedispenser/tgui_act(action, params) + if(..()) + return TRUE + if(unwrenched || !usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return TRUE + + . = TRUE + switch(action) + if("p_layer") + p_layer = text2num(params["p_layer"]) + if("dispense_pipe") + if(!wait) + var/datum/pipe_recipe/recipe = locate(params["ref"]) + if(!istype(recipe)) + return + + var/target_dir = NORTH + if(params["bent"]) + target_dir = NORTHEAST + + var/obj/created_object = null + if(istype(recipe, /datum/pipe_recipe/pipe)) + var/datum/pipe_recipe/pipe/R = recipe + created_object = new R.construction_type(loc, recipe.pipe_type, target_dir) + var/obj/item/pipe/P = created_object + P.setPipingLayer(p_layer) + else if(istype(recipe, /datum/pipe_recipe/disposal)) + var/datum/pipe_recipe/disposal/D = recipe + var/obj/structure/disposalconstruct/C = new(loc, D.pipe_type, target_dir, 0, D.subtype ? D.subtype : 0) + C.update() + created_object = C + else if(istype(recipe, /datum/pipe_recipe/meter)) + created_object = new recipe.pipe_type(loc) + else + log_runtime(EXCEPTION("Warning: [usr] attempted to spawn pipe recipe type by params [json_encode(params)] ([recipe] [recipe?.type]), but it was not allowed by this machine ([src] [type])")) + return + + created_object.add_fingerprint(usr) + wait = TRUE + VARSET_IN(src, wait, FALSE, 15) + + +/obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob) + src.add_fingerprint(usr) + if (istype(W, /obj/item/pipe) || istype(W, /obj/item/pipe_meter)) + to_chat(usr, "You put [W] back in [src].") + user.drop_item() + qdel(W) + return + else if(W.has_tool_quality(TOOL_WRENCH)) + if (unwrenched==0) + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src] from the floor...") + if (do_after(user, 40 * W.toolspeed)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src]. Now it can be pulled somewhere else.", \ + "You hear ratchet.") + src.anchored = FALSE + src.stat |= MAINT + src.unwrenched = 1 + if (usr.machine==src) + usr << browse(null, "window=pipedispenser") + else /*if (unwrenched==1)*/ + playsound(src, W.usesound, 50, 1) + to_chat(user, "You begin to fasten \the [src] to the floor...") + if (do_after(user, 20 * W.toolspeed)) + user.visible_message( \ + "[user] fastens \the [src].", \ + "You have fastened \the [src]. Now it can dispense pipes.", \ + "You hear ratchet.") + src.anchored = TRUE + src.stat &= ~MAINT + src.unwrenched = 0 + power_change() + else + return ..() + +/obj/machinery/pipedispenser/disposal + name = "Disposal Pipe Dispenser" + desc = "A large machine that can rapidly dispense pipes. This one seems to dispsense disposal pipes." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "pipe_d" + density = TRUE + anchored = TRUE + disposals = TRUE + +//Allow you to drag-drop disposal pipes into it +/obj/machinery/pipedispenser/disposal/MouseDrop_T(var/obj/structure/disposalconstruct/pipe as obj, mob/usr as mob) + if(!usr.canmove || usr.stat || usr.restrained()) + return + + if (!istype(pipe) || get_dist(usr, src) > 1 || get_dist(src,pipe) > 1 ) + return + + if (pipe.anchored) + return + + to_chat(usr, "You shove [pipe] back in [src].") + qdel(pipe) + +// adding a pipe dispensers that spawn unhooked from the ground +/obj/machinery/pipedispenser/orderable + anchored = FALSE + unwrenched = 1 + +/obj/machinery/pipedispenser/disposal/orderable + anchored = FALSE + unwrenched = 1 diff --git a/code/game/machinery/pipe/pipelayer.dm b/code/game/machinery/pipe/pipelayer.dm index bcc1f770a33..7a4951f0656 100644 --- a/code/game/machinery/pipe/pipelayer.dm +++ b/code/game/machinery/pipe/pipelayer.dm @@ -81,12 +81,12 @@ return if(default_part_replacement(user, W)) return - if (!panel_open && W.is_wrench()) + if (!panel_open && W.has_tool_quality(TOOL_WRENCH)) P_type_t = tgui_input_list(usr, "Choose pipe type", "Pipe type", Pipes) P_type = Pipes[P_type_t] user.visible_message("[user] has set \the [src] to manufacture [P_type_t].", "You set \the [src] to manufacture [P_type_t].") return - if(!panel_open && W.is_crowbar()) + if(!panel_open && W.has_tool_quality(TOOL_CROWBAR)) a_dis = !a_dis user.visible_message("[user] has [!a_dis?"de":""]activated auto-dismantling.", "You [!a_dis?"de":""]activate auto-dismantling.") return diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm index d61e0dbe721..2a0a284c365 100644 --- a/code/game/machinery/pointdefense.dm +++ b/code/game/machinery/pointdefense.dm @@ -93,7 +93,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) return data /obj/machinery/pointdefense_control/attackby(var/obj/item/W, var/mob/user) - if(W?.is_multitool()) + if(W?.has_tool_quality(TOOL_MULTITOOL)) var/new_ident = tgui_input_text(user, "Enter a new ident tag.", "[src]", id_tag, MAX_NAME_LEN) new_ident = sanitize(new_ident,MAX_NAME_LEN) if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, GLOB.tgui_physical_state)) @@ -211,7 +211,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) return PDC /obj/machinery/power/pointdefense/attackby(var/obj/item/W, var/mob/user) - if(W?.is_multitool()) + if(W?.has_tool_quality(TOOL_MULTITOOL)) var/new_ident = tgui_input_text(user, "Enter a new ident tag.", "[src]", id_tag, MAX_NAME_LEN) new_ident = sanitize(new_ident,MAX_NAME_LEN) if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, GLOB.tgui_physical_state)) diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index df2bb6319ec..f287e915422 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -1,1137 +1,1137 @@ -/* Portable Turrets: - Constructed from metal, a gun of choice, and a prox sensor. - This code is slightly more documented than normal, as requested by XSI on IRC. -*/ - -/datum/category_item/catalogue/technology/turret - name = "Turrets" - desc = "This imtimidating machine is essentially an automated gun. It is able to \ - scan its immediate environment, and if it determines that a threat is nearby, it will \ - open up, aim the barrel of the weapon at the threat, and engage it until the threat \ - goes away, it dies (if using a lethal gun), or the turret is destroyed. This has made them \ - well suited for long term defense for a static position, as electricity costs much \ - less than hiring a person to stand around. Despite this, the lack of a sapient entity's \ - judgement has sometimes lead to tragedy when turrets are poorly configured.\ -

                    \ - Early models generally had simple designs, and would shoot at anything that moved, with only \ - the option to disable it remotely for maintenance or to let someone pass. More modern iterations \ - of turrets have instead replaced those simple systems with intricate optical sensors and \ - image recognition software that allow the turret to distinguish between several kinds of \ - entities, and to only engage whatever their owners configured them to fight against.\ - Some models also have the ability to switch between a lethal and non-lethal mode.\ -

                    \ - Today's cutting edge in static defense development has shifted away from improving the \ - software of the turret, and instead towards the hardware. The newest solutions for \ - automated protection includes new hardware capabilities such as thicker armor, more \ - advanced integrated weapons, and some may even have been built with EM hardening in \ - mind." - value = CATALOGUER_REWARD_MEDIUM - - -#define TURRET_PRIORITY_TARGET 2 -#define TURRET_SECONDARY_TARGET 1 -#define TURRET_NOT_TARGET 0 - -/obj/machinery/porta_turret - name = "turret" - catalogue_data = list(/datum/category_item/catalogue/technology/turret) - icon = 'icons/obj/turrets.dmi' - icon_state = "turret_cover_normal" - anchored = TRUE - - density = FALSE - use_power = TRUE //this turret uses and requires power - idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power - active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power - power_channel = EQUIP //drains power from the EQUIPMENT channel - req_one_access = list(access_security, access_heads) - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - - var/raised = FALSE //if the turret cover is "open" and the turret is raised - var/raising= FALSE //if the turret is currently opening or closing its cover - var/health = 80 //the turret's health - var/maxhealth = 80 //turrets maximal health. - var/auto_repair = FALSE //if 1 the turret slowly repairs itself. - var/locked = TRUE //if the turret's behaviour control access is locked - var/controllock = FALSE //if the turret responds to control panels - - var/installation = /obj/item/weapon/gun/energy/gun //the type of weapon installed - var/gun_charge = 0 //the charge of the gun inserted - var/projectile = null //holder for bullettype - var/lethal_projectile = null //holder for the shot when emagged - var/reqpower = 500 //holder for power needed - var/turret_type = "normal" - var/icon_color = "blue" - var/lethal_icon_color = "blue" - - var/last_fired = FALSE //TRUE: if the turret is cooling down from a shot, FALSE: turret is ready to fire - var/shot_delay = 1.5 SECONDS //1.5 seconds between each shot - - var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI - var/check_arrest = TRUE //checks if the perp is set to arrest - var/check_records = TRUE //checks if a security record exists at all - var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg - var/check_all = FALSE //If active, will fire on anything, including synthetics. - var/ailock = FALSE // AI cannot use this - var/check_down = FALSE //If active, will shoot to kill when lethals are also on - var/faction = null //if set, will not fire at people in the same faction for any reason. - - var/attacked = FALSE //if set to TRUE, the turret gets pissed off and shoots at people nearby (unless they have sec access!) - - var/enabled = TRUE //determines if the turret is on - var/lethal = FALSE //whether in lethal or stun mode - var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed - var/disabled = FALSE - - var/shot_sound //what sound should play when the turret fires - var/lethal_shot_sound //what sound should play when the emagged turret fires - - var/datum/effect/effect/system/spark_spread/spark_system //the spark system, used for generating... sparks? - - var/wrenching = FALSE - var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range - var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down. - var/can_salvage = TRUE // If false, salvaging doesn't give you anything. - -/obj/machinery/porta_turret/crescent - req_one_access = list(access_cent_specops) - enabled = FALSE - ailock = TRUE - check_synth = FALSE - check_access = TRUE - check_arrest = TRUE - check_records = TRUE - check_weapons = TRUE - check_anomalies = TRUE - check_all = FALSE - check_down = TRUE - -/obj/machinery/porta_turret/can_catalogue(mob/user) // Dead turrets can't be scanned. - if(stat & BROKEN) - to_chat(user, span("warning", "\The [src] was destroyed, so it cannot be scanned.")) - return FALSE - return ..() - -/obj/machinery/porta_turret/stationary - ailock = TRUE - lethal = TRUE - installation = /obj/item/weapon/gun/energy/laser - -/obj/machinery/porta_turret/stationary/syndie // Generic turrets for POIs that need to not shoot their buddies. - req_one_access = list(access_syndicate) - enabled = TRUE - check_all = TRUE - faction = "syndicate" // Make sure this equals the faction that the mobs in the POI have or they will fight each other. - -/obj/machinery/porta_turret/ai_defense - name = "defense turret" - desc = "This variant appears to be much more durable." - req_one_access = list(access_synth) // Just in case. - installation = /obj/item/weapon/gun/energy/xray // For the armor pen. - health = 250 // Since lasers do 40 each. - maxhealth = 250 - -/datum/category_item/catalogue/anomalous/precursor_a/alien_turret - name = "Precursor Alpha Object - Turrets" - desc = "An autonomous defense turret created by unknown ancient aliens. It utilizes an \ - integrated laser projector to harm, firing a cyan beam at the target. The signal processing \ - of this mechanism appears to be radically different to conventional electronics used by modern \ - technology, which appears to be much less susceptible to external electromagnetic influences.\ -

                    \ - This makes the turret be very resistant to the effects of an EM pulse. It is unknown if whatever \ - species that built the turret had intended for it to have that quality, or if it was an incidental \ - quirk of how they designed their electronics." - value = CATALOGUER_REWARD_MEDIUM - -/obj/machinery/porta_turret/alien // The kind used on the UFO submap. - name = "interior anti-boarding turret" - desc = "A very tough looking turret made by alien hands." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_turret) - icon_state = "turret_cover_alien" - req_one_access = list(access_alien) - installation = /obj/item/weapon/gun/energy/alien - enabled = TRUE - lethal = TRUE - ailock = TRUE - check_all = TRUE - health = 250 // Similar to the AI turrets. - maxhealth = 250 - turret_type = "alien" - -/obj/machinery/porta_turret/alien/destroyed // Turrets that are already dead, to act as a warning of what the rest of the submap contains. - name = "broken interior anti-boarding turret" - desc = "A very tough looking turret made by alien hands. This one looks destroyed, thankfully." - icon_state = "destroyed_target_prism_alien" - stat = BROKEN - can_salvage = FALSE // So you need to actually kill a turret to get the alien gun. - -/obj/machinery/porta_turret/industrial - name = "industrial turret" - desc = "This variant appears to be much more rugged." - req_one_access = list(access_heads) - icon_state = "turret_cover_industrial" - installation = /obj/item/weapon/gun/energy/phasegun - health = 200 - maxhealth = 200 - turret_type = "industrial" - -/obj/machinery/porta_turret/industrial/bullet_act(obj/item/projectile/Proj) - var/damage = round(Proj.get_structure_damage() * 1.33) - - if(!damage) - return - - if(enabled) - if(!attacked && !emagged) - attacked = TRUE - spawn() - sleep(60) - attacked = FALSE - - take_damage(damage) - -/obj/machinery/porta_turret/industrial/attack_generic(mob/living/L, damage) - return ..(L, damage * 0.8) - -/obj/machinery/porta_turret/industrial/teleport_defense - name = "defense turret" - desc = "This variant appears to be much more durable, with a rugged outer coating." - req_one_access = list(access_heads) - installation = /obj/item/weapon/gun/energy/gun/burst - health = 250 - maxhealth = 250 - -/obj/machinery/porta_turret/poi //These are always angry - enabled = TRUE - lethal = TRUE - ailock = TRUE - check_all = TRUE - can_salvage = FALSE // So you can't just twoshot a turret and get a fancy gun - -/obj/machinery/porta_turret/lasertag - name = "lasertag turret" - turret_type = "normal" - req_one_access = list() - installation = /obj/item/weapon/gun/energy/lasertag/omni - - targetting_is_configurable = FALSE - lethal_is_configurable = FALSE - - locked = FALSE - enabled = FALSE - anchored = FALSE - //These two are used for lasertag - check_synth = FALSE - check_weapons = FALSE - //These vars aren't used - check_access = FALSE - check_arrest = FALSE - check_records = FALSE - check_anomalies = FALSE - check_all = FALSE - check_down = FALSE - -/obj/machinery/porta_turret/lasertag/red - turret_type = "red" - installation = /obj/item/weapon/gun/energy/lasertag/red - check_weapons = TRUE // Used to target blue players - -/obj/machinery/porta_turret/lasertag/blue - turret_type = "blue" - installation = /obj/item/weapon/gun/energy/lasertag/blue - check_synth = TRUE // Used to target red players - -/obj/machinery/porta_turret/lasertag/assess_living(var/mob/living/L) - if(!ishuman(L)) - return TURRET_NOT_TARGET - - if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var - return TURRET_NOT_TARGET - - if(get_dist(src, L) > 7) //if it's too far away, why bother? - return TURRET_NOT_TARGET - - if(!(L in check_trajectory(L, src))) //check if we have true line of sight - return TURRET_NOT_TARGET - - if(L.lying) //Don't need to stun-lock the players - return TURRET_NOT_TARGET - - if(ishuman(L)) - var/mob/living/carbon/human/M = L - if(istype(M.wear_suit, /obj/item/clothing/suit/redtag) && check_synth) // Checks if they are a red player - return TURRET_PRIORITY_TARGET - - if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag) && check_weapons) // Checks if they are a blue player - return TURRET_PRIORITY_TARGET - -/obj/machinery/porta_turret/lasertag/tgui_data(mob/user) - var/list/data = list( - "locked" = isLocked(user), // does the current user have access? - "on" = enabled, // is turret turned on? - "lethal" = lethal, - "lethal_is_configurable" = lethal_is_configurable - ) - return data - -/obj/machinery/porta_turret/Initialize() - //Sets up a spark system - spark_system = new /datum/effect/effect/system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - setup() - - // If turrets ever switch overlays, this will need to be cached and reapplied each time overlays_cut() is called. - var/image/turret_opened_overlay = image(icon, "open_[turret_type]") - turret_opened_overlay.layer = layer-0.1 - add_overlay(turret_opened_overlay) - return ..() - -/obj/machinery/porta_turret/Destroy() - qdel(spark_system) - spark_system = null - return ..() - -/obj/machinery/porta_turret/update_icon() - if(stat & BROKEN) // Turret is dead. - icon_state = "destroyed_target_prism_[turret_type]" - - else if(raised || raising) - // Turret is open. - if(powered() && enabled) - // Trying to shoot someone. - if(lethal) - icon_state = "[lethal_icon_color]_target_prism_[turret_type]" - else - icon_state = "[icon_color]_target_prism_[turret_type]" - - else - // Disabled. - icon_state = "grey_target_prism_[turret_type]" - - else - // Its closed. - icon_state = "turret_cover_[turret_type]" - - -/obj/machinery/porta_turret/proc/setup() - var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable - var/obj/item/projectile/P = initial(E.projectile_type) - //var/obj/item/ammo_casing/shottype = E.projectile_type - - projectile = P - lethal_projectile = projectile - shot_sound = initial(P.fire_sound) - lethal_shot_sound = shot_sound - - if(istype(P, /obj/item/projectile/energy)) - icon_color = "orange" - - else if(istype(P, /obj/item/projectile/beam/stun)) - icon_color = "blue" - - else if(istype(P, /obj/item/projectile/beam/lasertag)) - icon_color = "blue" - - else if(istype(P, /obj/item/projectile/beam)) - icon_color = "red" - - else - icon_color = "blue" - - lethal_icon_color = icon_color - - weapon_setup(installation) - -/obj/machinery/porta_turret/proc/weapon_setup(var/guntype) - switch(guntype) - if(/obj/item/weapon/gun/energy/gun/burst) - lethal_icon_color = "red" - lethal_projectile = /obj/item/projectile/beam/burstlaser - lethal_shot_sound = 'sound/weapons/Laser.ogg' - shot_delay = 1 SECOND - - if(/obj/item/weapon/gun/energy/phasegun) - icon_color = "orange" - lethal_icon_color = "orange" - lethal_projectile = /obj/item/projectile/energy/phase/heavy - shot_delay = 1 SECOND - - if(/obj/item/weapon/gun/energy/gun) - lethal_icon_color = "red" - lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode - lethal_shot_sound = 'sound/weapons/Laser.ogg' - - if(/obj/item/weapon/gun/energy/gun/nuclear) - lethal_icon_color = "red" - lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode - lethal_shot_sound = 'sound/weapons/Laser.ogg' - - if(/obj/item/weapon/gun/energy/xray) - lethal_icon_color = "green" - lethal_projectile = /obj/item/projectile/beam/xray - projectile = /obj/item/projectile/beam/stun // Otherwise we fire xrays on both modes. - lethal_shot_sound = 'sound/weapons/eluger.ogg' - shot_sound = 'sound/weapons/Taser.ogg' - -/obj/machinery/porta_turret/proc/isLocked(mob/user) - if(locked && !issilicon(user)) - to_chat(user, "Controls locked.") - return 1 - if(HasController()) - return TRUE - if(isrobot(user) || isAI(user)) - if(ailock) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return TRUE - else - return FALSE - if(isobserver(user)) - var/mob/observer/dead/D = user - if(D.can_admin_interact()) - return FALSE - else - return TRUE - if(locked) - return TRUE - return FALSE - -/obj/machinery/porta_turret/attack_ai(mob/user) - tgui_interact(user) - -/obj/machinery/porta_turret/attack_ghost(mob/user) - tgui_interact(user) - -/obj/machinery/porta_turret/attack_hand(mob/user) - tgui_interact(user) - -/obj/machinery/porta_turret/proc/HasController() - var/area/A = get_area(src) - return A && A.turret_controls.len > 0 - -/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null) - if(HasController()) - to_chat(user, "[src] can only be controlled using the assigned turret controller.") - return - if(!anchored) - to_chat(user, "[src] has to be secured first!") - return - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "PortableTurret", name, ui_x = 500, ui_y = 400) - ui.open() - -/obj/machinery/porta_turret/tgui_data(mob/user) - var/list/data = list( - "locked" = isLocked(user), // does the current user have access? - "on" = enabled, - "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up - "lethal" = lethal, - "lethal_is_configurable" = lethal_is_configurable, - "check_weapons" = check_weapons, - "neutralize_noaccess" = check_access, - "neutralize_norecord" = check_records, - "neutralize_criminals" = check_arrest, - "neutralize_all" = check_all, - "neutralize_nonsynth" = check_synth, - "neutralize_unidentified" = check_anomalies, - "neutralize_down" = check_down, - ) - return data - -/obj/machinery/porta_turret/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - if(isLocked(usr)) - return TRUE - . = TRUE - - switch(action) - if("power") - enabled = !enabled - if("lethal") - if(lethal_is_configurable) - lethal = !lethal - if(targetting_is_configurable) - switch(action) - if("authweapon") - check_weapons = !check_weapons - if("authaccess") - check_access = !check_access - if("authnorecord") - check_records = !check_records - if("autharrest") - check_arrest = !check_arrest - if("authxeno") - check_anomalies = !check_anomalies - if("authsynth") - check_synth = !check_synth - if("authall") - check_all = !check_all - if("authdown") - check_down = !check_down - -/obj/machinery/porta_turret/power_change() - if(powered()) - stat &= ~NOPOWER - update_icon() - else - spawn(rand(0, 15)) - stat |= NOPOWER - update_icon() - - -/obj/machinery/porta_turret/attackby(obj/item/I, mob/user) - if(stat & BROKEN) - if(I.is_crowbar()) - //If the turret is destroyed, you can remove it with a crowbar to - //try and salvage its components - to_chat(user, "You begin prying the metal coverings off.") - if(do_after(user, 20)) - if(can_salvage && prob(70)) - to_chat(user, "You remove the turret and salvage some components.") - if(installation) - var/obj/item/weapon/gun/energy/Gun = new installation(loc) - Gun.power_supply.charge = gun_charge - Gun.update_icon() - if(prob(50)) - new /obj/item/stack/material/steel(loc, rand(1,4)) - if(prob(50)) - new /obj/item/device/assembly/prox_sensor(loc) - else - to_chat(user, "You remove the turret but did not manage to salvage anything.") - qdel(src) // qdel - - else if(I.is_wrench()) - if(enabled || raised) - to_chat(user, "You cannot unsecure an active turret!") - return - if(wrenching) - to_chat(user, "Someone is already [anchored ? "un" : ""]securing the turret!") - return - if(!anchored && isinspace()) - to_chat(user, "Cannot secure turrets in space!") - return - - user.visible_message(\ - "[user] begins [anchored ? "un" : ""]securing the turret.", \ - "You begin [anchored ? "un" : ""]securing the turret." \ - ) - - wrenching = TRUE - if(do_after(user, 50 * I.toolspeed)) - //This code handles moving the turret around. After all, it's a portable turret! - if(!anchored) - playsound(src, I.usesound, 100, 1) - anchored = TRUE - update_icon() - to_chat(user, "You secure the exterior bolts on the turret.") - else if(anchored) - playsound(src, I.usesound, 100, 1) - anchored = FALSE - to_chat(user, "You unsecure the exterior bolts on the turret.") - update_icon() - wrenching = FALSE - - else if(istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) - //Behavior lock/unlock mangement - if(allowed(user)) - locked = !locked - to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") - updateUsrDialog() - else - to_chat(user, "Access denied.") - - else - //if the turret was attacked with the intention of harming it: - user.setClickCooldown(user.get_attack_speed(I)) - take_damage(I.force * 0.5) - if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off - if(!attacked && !emagged) - attacked = 1 - spawn() - sleep(60) - attacked = 0 - ..() - -/obj/machinery/porta_turret/attack_generic(mob/living/L, damage) - if(isanimal(L)) - var/mob/living/simple_mob/S = L - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly. - visible_message("\The [S] [pick(S.attacktext)] \the [src]!") - take_damage(incoming_damage) - S.do_attack_animation(src) - return 1 - visible_message("\The [L] bonks \the [src]'s casing!") - return ..() - -/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - //Emagging the turret makes it go bonkers and stun everyone. It also makes - //the turret shoot much, much faster. - to_chat(user, "You short out [src]'s threat assessment circuits.") - visible_message("[src] hums oddly...") - emagged = TRUE - controllock = TRUE - enabled = FALSE //turns off the turret temporarily - sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here - return 1 - -/obj/machinery/porta_turret/take_damage(var/force) - if(!raised && !raising) - force = force / 8 - if(force < 5) - return - - health -= force - if(force > 5 && prob(45)) - spark_system.start() - if(health <= 0) - die() //the death process :( - -/obj/machinery/porta_turret/bullet_act(obj/item/projectile/Proj) - var/damage = Proj.get_structure_damage() - - if(!damage) - return - - if(enabled) - if(!attacked && !emagged) - attacked = 1 - spawn() - sleep(60) - attacked = FALSE - - ..() - - take_damage(damage) - -/obj/machinery/porta_turret/emp_act(severity) - if(enabled) - //if the turret is on, the EMP no matter how severe disables the turret for a while - //and scrambles its settings, with a slight chance of having an emag effect - check_arrest = prob(50) - check_records = prob(50) - check_weapons = prob(50) - check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on - check_anomalies = prob(50) - if(prob(5)) - emagged = TRUE - - enabled=0 - spawn(rand(60,600)) - if(!enabled) - enabled = TRUE - - ..() - -/obj/machinery/porta_turret/ai_defense/emp_act(severity) - if(prob(33)) // One in three chance to resist an EMP. This is significant if an AoE EMP is involved against multiple turrets. - return - ..() - -/obj/machinery/porta_turret/alien/emp_act(severity) // This is overrided to give an EMP resistance as well as avoid scambling the turret settings. - if(prob(75)) // Superior alien technology, I guess. - return - enabled = FALSE - spawn(rand(1 MINUTE, 2 MINUTES)) - if(!enabled) - enabled = TRUE - -/obj/machinery/porta_turret/ex_act(severity) - switch (severity) - if(1) - qdel(src) - if(2) - if(prob(25)) - qdel(src) - else - take_damage(initial(health) * 8) //should instakill most turrets - if(3) - take_damage(initial(health) * 8 / 3) //Level 4 is too weak to bother turrets - -/obj/machinery/porta_turret/proc/die() //called when the turret dies, ie, health <= 0 - health = 0 - stat |= BROKEN //enables the BROKEN bit - spark_system.start() //creates some sparks because they look cool - update_icon() - -/obj/machinery/porta_turret/process() - //the main machinery process - - if(stat & (NOPOWER|BROKEN)) - //if the turret has no power or is broken, make the turret pop down if it hasn't already - popDown() - return - - if(!enabled) - //if the turret is off, make it pop down - popDown() - return - - var/list/targets = list() //list of primary targets - var/list/secondarytargets = list() //targets that are least important - - var/list/seenturfs = list() - for(var/turf/T in oview(world.view, src)) - seenturfs += T - - for(var/mob/M as anything in living_mob_list) - if(M == null || M.z != z || !(get_turf(M) in seenturfs)) //Skip // ARFS Edit - Fix Roundstart error - continue - switch(assess_living(M)) - if(TURRET_PRIORITY_TARGET) - targets += M - if(TURRET_SECONDARY_TARGET) - secondarytargets += M - - for(var/obj/mecha/M as anything in mechas_list) - if(M.z != z || !(get_turf(M) in seenturfs)) // Skip - continue - switch(assess_mecha(M)) - if(TURRET_PRIORITY_TARGET) - targets += M - if(TURRET_SECONDARY_TARGET) - secondarytargets += M - - if(!tryToShootAt(targets) && !tryToShootAt(secondarytargets) && --timeout <= 0) - popDown() // no valid targets, close the cover - - if(auto_repair && (health < maxhealth)) - use_power(20000) - health = min(health+1, maxhealth) // 1HP for 20kJ - -/obj/machinery/porta_turret/proc/assess_living(var/mob/living/L) - if(!istype(L)) - return TURRET_NOT_TARGET - - if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var - return TURRET_NOT_TARGET - - if(faction && L.faction == faction) - return TURRET_NOT_TARGET - - if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. - return TURRET_NOT_TARGET - - if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really - return TURRET_NOT_TARGET //move onto next potential victim! - - if(get_dist(src, L) > 7) //if it's too far away, why bother? - return TURRET_NOT_TARGET - - if(!(L in check_trajectory(L, src))) //check if we have true line of sight - return TURRET_NOT_TARGET - - if(emagged) // If emagged not even the dead get a rest - return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET - - if(lethal && locate(/mob/living/silicon/ai) in get_turf(L)) //don't accidentally kill the AI! - return TURRET_NOT_TARGET - - if(check_synth || check_all) //If it's set to attack all non-silicons or everything, target them! - if(L.lying) - return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - return TURRET_PRIORITY_TARGET - - if(iscuffed(L)) // If the target is handcuffed, leave it alone - return TURRET_NOT_TARGET - - if(isanimal(L)) // Animals are not so dangerous - return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - - if(isxenomorph(L) || isalien(L)) // Xenos are dangerous - return check_anomalies ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET - - if(ishuman(L)) //if the target is a human, analyze threat level - if(assess_perp(L) < 4) - return TURRET_NOT_TARGET //if threat level < 4, keep going - - if(L.lying) //if the perp is lying down, it's still a target but a less-important target - return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - - return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee - -/obj/machinery/porta_turret/proc/assess_mecha(var/obj/mecha/M) - if(!istype(M)) - return TURRET_NOT_TARGET - - if(!M.occupant) - return check_all ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET - - return assess_living(M.occupant) - -/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/carbon/human/H) - if(!H || !istype(H)) - return 0 - - if(emagged) - return 10 - - return H.assess_perp(src, check_access, check_weapons, check_records, check_arrest) - -/obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets) - if(targets.len && last_target && (last_target in targets) && target(last_target)) - return 1 - - while(targets.len > 0) - var/mob/living/M = pick(targets) - targets -= M - if(target(M)) - return 1 - - -/obj/machinery/porta_turret/proc/popUp() //pops the turret up - set waitfor = FALSE - if(disabled) - return - if(raising || raised) - return - if(stat & BROKEN) - return - set_raised_raising(raised, 1) - update_icon() - - var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) - flick_holder.layer = layer + 0.1 - flick("popup_[turret_type]", flick_holder) - playsound(src, 'sound/machines/turrets/turret_deploy.ogg', 100, 1) - sleep(10) - qdel(flick_holder) - - set_raised_raising(1, 0) - update_icon() - timeout = 10 - -/obj/machinery/porta_turret/proc/popDown() //pops the turret down - set waitfor = FALSE - last_target = null - if(disabled) - return - if(raising || !raised) - return - if(stat & BROKEN) - return - set_raised_raising(raised, 1) - update_icon() - - var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) - flick_holder.layer = layer + 0.1 - flick("popdown_[turret_type]", flick_holder) - playsound(src, 'sound/machines/turrets/turret_retract.ogg', 100, 1) - sleep(10) - qdel(flick_holder) - - set_raised_raising(0, 0) - update_icon() - timeout = 10 - -/obj/machinery/porta_turret/proc/set_raised_raising(var/incoming_raised, var/incoming_raising) - raised = incoming_raised - raising = incoming_raising - density = raised || raising - -/obj/machinery/porta_turret/proc/target(var/mob/living/target) - if(disabled) - return FALSE - if(target) - last_target = target - popUp() //pop the turret up if it's not already up. - set_dir(get_dir(src, target)) //even if you can't shoot, follow the target - playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound - spawn() - shootAt(target) - return TRUE - return FALSE - -/obj/machinery/porta_turret/proc/shootAt(var/mob/living/target) - //any emagged turrets will shoot extremely fast! This not only is deadly, but drains a lot power! - if(!(emagged || attacked)) //if it hasn't been emagged or attacked, it has to obey a cooldown rate - if(last_fired || !raised) //prevents rapid-fire shooting, unless it's been emagged - return - last_fired = TRUE - spawn() - sleep(shot_delay) - last_fired = FALSE - - if(!isturf(get_turf(src)) || !isturf(get_turf(target))) - return - - if(!raised) //the turret has to be raised in order to fire - makes sense, right? - return - - update_icon() - var/obj/item/projectile/A - if(emagged || lethal) - A = new lethal_projectile(loc) - playsound(src, lethal_shot_sound, 75, 1) - else - A = new projectile(loc) - playsound(src, shot_sound, 75, 1) - - var/power_mult = 1 - if(emagged) - power_mult = 4 // Lethal beams + higher rate of fire - else if(lethal) - power_mult = 2 // Lethal beams - use_power(reqpower * power_mult) - - //Turrets aim for the center of mass by default. - //If the target is grabbing someone then the turret smartly aims for extremities - var/def_zone - var/obj/item/weapon/grab/G = locate() in target - if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. - def_zone = pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) - else - def_zone = pick(BP_TORSO, BP_GROIN) - - //Shooting Code: - A.firer = src - A.old_style_target(target) - A.launch_projectile_from_turf(target, def_zone, src) - - // Reset the time needed to go back down, since we just tried to shoot at someone. - timeout = 10 - -/datum/turret_checks - var/enabled - var/lethal - var/check_synth - var/check_access - var/check_records - var/check_arrest - var/check_weapons - var/check_anomalies - var/check_all - var/ailock - -/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) - if(controllock) - return - enabled = TC.enabled - lethal = TC.lethal - - check_synth = TC.check_synth - check_access = TC.check_access - check_records = TC.check_records - check_arrest = TC.check_arrest - check_weapons = TC.check_weapons - check_anomalies = TC.check_anomalies - check_all = TC.check_all - ailock = TC.ailock - - power_change() - -/* - Portable turret constructions - Known as "turret frame"s -*/ - -/obj/machinery/porta_turret_construct - name = "turret frame" - icon = 'icons/obj/turrets.dmi' - icon_state = "turret_frame" - density=TRUE - var/target_type = /obj/machinery/porta_turret // The type we intend to build - var/build_step = 0 //the current step in the building process - var/finish_name="turret" //the name applied to the product turret - var/installation = null //the gun type installed - var/gun_charge = 0 //the gun charge of the gun type installed - -/obj/machinery/porta_turret_construct/attackby(obj/item/I, mob/user) - //this is a bit unwieldy but self-explanatory - switch(build_step) - if(0) //first step - if(I.is_wrench() && !anchored) - playsound(src, I.usesound, 100, 1) - to_chat(user, "You secure the external bolts.") - anchored = TRUE - build_step = 1 - return - - else if(I.is_crowbar() && !anchored) - playsound(src, I.usesound, 75, 1) - to_chat(user, "You dismantle the turret construction.") - new /obj/item/stack/material/steel(loc, 5) - qdel(src) - return - - if(1) - if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) - var/obj/item/stack/M = I - if(M.use(2)) - to_chat(user, "You add some metal armor to the interior frame.") - build_step = 2 - icon_state = "turret_frame2" - else - to_chat(user, "You need two sheets of metal to continue construction.") - return - - else if(I.is_wrench()) - playsound(src, I.usesound, 75, 1) - to_chat(user, "You unfasten the external bolts.") - anchored = FALSE - build_step = 0 - return - - if(2) - if(I.is_wrench()) - playsound(src, I.usesound, 100, 1) - to_chat(user, "You bolt the metal armor into place.") - build_step = 3 - return - - else if(istype(I, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = I - if(!WT.isOn()) - return - if(WT.get_fuel() < 5) //uses up 5 fuel. - to_chat(user, "You need more fuel to complete this task.") - return - - playsound(src, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed)) - if(!src || !WT.remove_fuel(5, user)) return - build_step = 1 - to_chat(user, "You remove the turret's interior metal armor.") - new /obj/item/stack/material/steel(loc, 2) - return - - if(3) - if(istype(I, /obj/item/weapon/gun/energy)) //the gun installation part - - if(isrobot(user)) - return - var/obj/item/weapon/gun/energy/E = I //typecasts the item to an energy gun - if(!user.unEquip(I)) - to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") - return - installation = I.type //installation becomes I.type - gun_charge = E.power_supply.charge //the gun's charge is stored in gun_charge - to_chat(user, "You add [I] to the turret.") - target_type = /obj/machinery/porta_turret - - build_step = 4 - qdel(I) //delete the gun :( - return - - else if(I.is_wrench()) - playsound(src, I.usesound, 100, 1) - to_chat(user, "You remove the turret's metal armor bolts.") - build_step = 2 - return - - if(4) - if(isprox(I)) - build_step = 5 - if(!user.unEquip(I)) - to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") - return - to_chat(user, "You add the prox sensor to the turret.") - qdel(I) - return - - //attack_hand() removes the gun - - if(5) - if(I.is_screwdriver()) - playsound(src, I.usesound, 100, 1) - build_step = 6 - to_chat(user, "You close the internal access hatch.") - return - - //attack_hand() removes the prox sensor - - if(6) - if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) - var/obj/item/stack/M = I - if(M.use(2)) - to_chat(user, "You add some metal armor to the exterior frame.") - build_step = 7 - else - to_chat(user, "You need two sheets of metal to continue construction.") - return - - else if(I.is_screwdriver()) - playsound(src, I.usesound, 100, 1) - build_step = 5 - to_chat(user, "You open the internal access hatch.") - return - - if(7) - if(istype(I, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = I - if(!WT.isOn()) return - if(WT.get_fuel() < 5) - to_chat(user, "You need more fuel to complete this task.") - - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 30 * WT.toolspeed)) - if(!src || !WT.remove_fuel(5, user)) - return - build_step = 8 - to_chat(user, "You weld the turret's armor down.") - - //The final step: create a full turret - var/obj/machinery/porta_turret/Turret = new target_type(loc) - Turret.name = finish_name - Turret.installation = installation - Turret.gun_charge = gun_charge - Turret.enabled = 0 - Turret.setup() - - qdel(src) // qdel - - else if(I.is_crowbar()) - playsound(src, I.usesound, 75, 1) - to_chat(user, "You pry off the turret's exterior armor.") - new /obj/item/stack/material/steel(loc, 2) - build_step = 6 - return - - if(istype(I, /obj/item/weapon/pen)) //you can rename turrets like bots! - var/t = sanitizeSafe(tgui_input_text(user, "Enter new turret name", name, finish_name, MAX_NAME_LEN), MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - - finish_name = t - return - - ..() - -/obj/machinery/porta_turret_construct/attack_hand(mob/user) - switch(build_step) - if(4) - if(!installation) - return - build_step = 3 - - var/obj/item/weapon/gun/energy/Gun = new installation(loc) - Gun.power_supply.charge = gun_charge - Gun.update_icon() - installation = null - gun_charge = 0 - to_chat(user, "You remove [Gun] from the turret frame.") - - if(5) - to_chat(user, "You remove the prox sensor from the turret frame.") - new /obj/item/device/assembly/prox_sensor(loc) - build_step = 4 - -/obj/machinery/porta_turret_construct/attack_ai() - return - -/atom/movable/porta_turret_cover - icon = 'icons/obj/turrets.dmi' - -#undef TURRET_PRIORITY_TARGET -#undef TURRET_SECONDARY_TARGET -#undef TURRET_NOT_TARGET +/* Portable Turrets: + Constructed from metal, a gun of choice, and a prox sensor. + This code is slightly more documented than normal, as requested by XSI on IRC. +*/ + +/datum/category_item/catalogue/technology/turret + name = "Turrets" + desc = "This imtimidating machine is essentially an automated gun. It is able to \ + scan its immediate environment, and if it determines that a threat is nearby, it will \ + open up, aim the barrel of the weapon at the threat, and engage it until the threat \ + goes away, it dies (if using a lethal gun), or the turret is destroyed. This has made them \ + well suited for long term defense for a static position, as electricity costs much \ + less than hiring a person to stand around. Despite this, the lack of a sapient entity's \ + judgement has sometimes lead to tragedy when turrets are poorly configured.\ +

                    \ + Early models generally had simple designs, and would shoot at anything that moved, with only \ + the option to disable it remotely for maintenance or to let someone pass. More modern iterations \ + of turrets have instead replaced those simple systems with intricate optical sensors and \ + image recognition software that allow the turret to distinguish between several kinds of \ + entities, and to only engage whatever their owners configured them to fight against.\ + Some models also have the ability to switch between a lethal and non-lethal mode.\ +

                    \ + Today's cutting edge in static defense development has shifted away from improving the \ + software of the turret, and instead towards the hardware. The newest solutions for \ + automated protection includes new hardware capabilities such as thicker armor, more \ + advanced integrated weapons, and some may even have been built with EM hardening in \ + mind." + value = CATALOGUER_REWARD_MEDIUM + + +#define TURRET_PRIORITY_TARGET 2 +#define TURRET_SECONDARY_TARGET 1 +#define TURRET_NOT_TARGET 0 + +/obj/machinery/porta_turret + name = "turret" + catalogue_data = list(/datum/category_item/catalogue/technology/turret) + icon = 'icons/obj/turrets.dmi' + icon_state = "turret_cover_normal" + anchored = TRUE + + density = FALSE + use_power = TRUE //this turret uses and requires power + idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power + active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power + power_channel = EQUIP //drains power from the EQUIPMENT channel + req_one_access = list(access_security, access_heads) + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + + var/raised = FALSE //if the turret cover is "open" and the turret is raised + var/raising= FALSE //if the turret is currently opening or closing its cover + var/health = 80 //the turret's health + var/maxhealth = 80 //turrets maximal health. + var/auto_repair = FALSE //if 1 the turret slowly repairs itself. + var/locked = TRUE //if the turret's behaviour control access is locked + var/controllock = FALSE //if the turret responds to control panels + + var/installation = /obj/item/weapon/gun/energy/gun //the type of weapon installed + var/gun_charge = 0 //the charge of the gun inserted + var/projectile = null //holder for bullettype + var/lethal_projectile = null //holder for the shot when emagged + var/reqpower = 500 //holder for power needed + var/turret_type = "normal" + var/icon_color = "blue" + var/lethal_icon_color = "blue" + + var/last_fired = FALSE //TRUE: if the turret is cooling down from a shot, FALSE: turret is ready to fire + var/shot_delay = 1.5 SECONDS //1.5 seconds between each shot + + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_all = FALSE //If active, will fire on anything, including synthetics. + var/ailock = FALSE // AI cannot use this + var/check_down = FALSE //If active, will shoot to kill when lethals are also on + var/faction = null //if set, will not fire at people in the same faction for any reason. + + var/attacked = FALSE //if set to TRUE, the turret gets pissed off and shoots at people nearby (unless they have sec access!) + + var/enabled = TRUE //determines if the turret is on + var/lethal = FALSE //whether in lethal or stun mode + var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed + var/disabled = FALSE + + var/shot_sound //what sound should play when the turret fires + var/lethal_shot_sound //what sound should play when the emagged turret fires + + var/datum/effect/effect/system/spark_spread/spark_system //the spark system, used for generating... sparks? + + var/wrenching = FALSE + var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range + var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down. + var/can_salvage = TRUE // If false, salvaging doesn't give you anything. + +/obj/machinery/porta_turret/crescent + req_one_access = list(access_cent_specops) + enabled = FALSE + ailock = TRUE + check_synth = FALSE + check_access = TRUE + check_arrest = TRUE + check_records = TRUE + check_weapons = TRUE + check_anomalies = TRUE + check_all = FALSE + check_down = TRUE + +/obj/machinery/porta_turret/can_catalogue(mob/user) // Dead turrets can't be scanned. + if(stat & BROKEN) + to_chat(user, span("warning", "\The [src] was destroyed, so it cannot be scanned.")) + return FALSE + return ..() + +/obj/machinery/porta_turret/stationary + ailock = TRUE + lethal = TRUE + installation = /obj/item/weapon/gun/energy/laser + +/obj/machinery/porta_turret/stationary/syndie // Generic turrets for POIs that need to not shoot their buddies. + req_one_access = list(access_syndicate) + enabled = TRUE + check_all = TRUE + faction = "syndicate" // Make sure this equals the faction that the mobs in the POI have or they will fight each other. + +/obj/machinery/porta_turret/ai_defense + name = "defense turret" + desc = "This variant appears to be much more durable." + req_one_access = list(access_synth) // Just in case. + installation = /obj/item/weapon/gun/energy/xray // For the armor pen. + health = 250 // Since lasers do 40 each. + maxhealth = 250 + +/datum/category_item/catalogue/anomalous/precursor_a/alien_turret + name = "Precursor Alpha Object - Turrets" + desc = "An autonomous defense turret created by unknown ancient aliens. It utilizes an \ + integrated laser projector to harm, firing a cyan beam at the target. The signal processing \ + of this mechanism appears to be radically different to conventional electronics used by modern \ + technology, which appears to be much less susceptible to external electromagnetic influences.\ +

                    \ + This makes the turret be very resistant to the effects of an EM pulse. It is unknown if whatever \ + species that built the turret had intended for it to have that quality, or if it was an incidental \ + quirk of how they designed their electronics." + value = CATALOGUER_REWARD_MEDIUM + +/obj/machinery/porta_turret/alien // The kind used on the UFO submap. + name = "interior anti-boarding turret" + desc = "A very tough looking turret made by alien hands." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_turret) + icon_state = "turret_cover_alien" + req_one_access = list(access_alien) + installation = /obj/item/weapon/gun/energy/alien + enabled = TRUE + lethal = TRUE + ailock = TRUE + check_all = TRUE + health = 250 // Similar to the AI turrets. + maxhealth = 250 + turret_type = "alien" + +/obj/machinery/porta_turret/alien/destroyed // Turrets that are already dead, to act as a warning of what the rest of the submap contains. + name = "broken interior anti-boarding turret" + desc = "A very tough looking turret made by alien hands. This one looks destroyed, thankfully." + icon_state = "destroyed_target_prism_alien" + stat = BROKEN + can_salvage = FALSE // So you need to actually kill a turret to get the alien gun. + +/obj/machinery/porta_turret/industrial + name = "industrial turret" + desc = "This variant appears to be much more rugged." + req_one_access = list(access_heads) + icon_state = "turret_cover_industrial" + installation = /obj/item/weapon/gun/energy/phasegun + health = 200 + maxhealth = 200 + turret_type = "industrial" + +/obj/machinery/porta_turret/industrial/bullet_act(obj/item/projectile/Proj) + var/damage = round(Proj.get_structure_damage() * 1.33) + + if(!damage) + return + + if(enabled) + if(!attacked && !emagged) + attacked = TRUE + spawn() + sleep(60) + attacked = FALSE + + take_damage(damage) + +/obj/machinery/porta_turret/industrial/attack_generic(mob/living/L, damage) + return ..(L, damage * 0.8) + +/obj/machinery/porta_turret/industrial/teleport_defense + name = "defense turret" + desc = "This variant appears to be much more durable, with a rugged outer coating." + req_one_access = list(access_heads) + installation = /obj/item/weapon/gun/energy/gun/burst + health = 250 + maxhealth = 250 + +/obj/machinery/porta_turret/poi //These are always angry + enabled = TRUE + lethal = TRUE + ailock = TRUE + check_all = TRUE + can_salvage = FALSE // So you can't just twoshot a turret and get a fancy gun + +/obj/machinery/porta_turret/lasertag + name = "lasertag turret" + turret_type = "normal" + req_one_access = list() + installation = /obj/item/weapon/gun/energy/lasertag/omni + + targetting_is_configurable = FALSE + lethal_is_configurable = FALSE + + locked = FALSE + enabled = FALSE + anchored = FALSE + //These two are used for lasertag + check_synth = FALSE + check_weapons = FALSE + //These vars aren't used + check_access = FALSE + check_arrest = FALSE + check_records = FALSE + check_anomalies = FALSE + check_all = FALSE + check_down = FALSE + +/obj/machinery/porta_turret/lasertag/red + turret_type = "red" + installation = /obj/item/weapon/gun/energy/lasertag/red + check_weapons = TRUE // Used to target blue players + +/obj/machinery/porta_turret/lasertag/blue + turret_type = "blue" + installation = /obj/item/weapon/gun/energy/lasertag/blue + check_synth = TRUE // Used to target red players + +/obj/machinery/porta_turret/lasertag/assess_living(var/mob/living/L) + if(!ishuman(L)) + return TURRET_NOT_TARGET + + if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var + return TURRET_NOT_TARGET + + if(get_dist(src, L) > 7) //if it's too far away, why bother? + return TURRET_NOT_TARGET + + if(!(L in check_trajectory(L, src))) //check if we have true line of sight + return TURRET_NOT_TARGET + + if(L.lying) //Don't need to stun-lock the players + return TURRET_NOT_TARGET + + if(ishuman(L)) + var/mob/living/carbon/human/M = L + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag) && check_synth) // Checks if they are a red player + return TURRET_PRIORITY_TARGET + + if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag) && check_weapons) // Checks if they are a blue player + return TURRET_PRIORITY_TARGET + +/obj/machinery/porta_turret/lasertag/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, // is turret turned on? + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable + ) + return data + +/obj/machinery/porta_turret/Initialize() + //Sets up a spark system + spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + setup() + + // If turrets ever switch overlays, this will need to be cached and reapplied each time overlays_cut() is called. + var/image/turret_opened_overlay = image(icon, "open_[turret_type]") + turret_opened_overlay.layer = layer-0.1 + add_overlay(turret_opened_overlay) + return ..() + +/obj/machinery/porta_turret/Destroy() + qdel(spark_system) + spark_system = null + return ..() + +/obj/machinery/porta_turret/update_icon() + if(stat & BROKEN) // Turret is dead. + icon_state = "destroyed_target_prism_[turret_type]" + + else if(raised || raising) + // Turret is open. + if(powered() && enabled) + // Trying to shoot someone. + if(lethal) + icon_state = "[lethal_icon_color]_target_prism_[turret_type]" + else + icon_state = "[icon_color]_target_prism_[turret_type]" + + else + // Disabled. + icon_state = "grey_target_prism_[turret_type]" + + else + // Its closed. + icon_state = "turret_cover_[turret_type]" + + +/obj/machinery/porta_turret/proc/setup() + var/obj/item/weapon/gun/energy/E = installation //All energy-based weapons are applicable + var/obj/item/projectile/P = initial(E.projectile_type) + //var/obj/item/ammo_casing/shottype = E.projectile_type + + projectile = P + lethal_projectile = projectile + shot_sound = initial(P.fire_sound) + lethal_shot_sound = shot_sound + + if(istype(P, /obj/item/projectile/energy)) + icon_color = "orange" + + else if(istype(P, /obj/item/projectile/beam/stun)) + icon_color = "blue" + + else if(istype(P, /obj/item/projectile/beam/lasertag)) + icon_color = "blue" + + else if(istype(P, /obj/item/projectile/beam)) + icon_color = "red" + + else + icon_color = "blue" + + lethal_icon_color = icon_color + + weapon_setup(installation) + +/obj/machinery/porta_turret/proc/weapon_setup(var/guntype) + switch(guntype) + if(/obj/item/weapon/gun/energy/gun/burst) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam/burstlaser + lethal_shot_sound = 'sound/weapons/Laser.ogg' + shot_delay = 1 SECOND + + if(/obj/item/weapon/gun/energy/phasegun) + icon_color = "orange" + lethal_icon_color = "orange" + lethal_projectile = /obj/item/projectile/energy/phase/heavy + shot_delay = 1 SECOND + + if(/obj/item/weapon/gun/energy/gun) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode + lethal_shot_sound = 'sound/weapons/Laser.ogg' + + if(/obj/item/weapon/gun/energy/gun/nuclear) + lethal_icon_color = "red" + lethal_projectile = /obj/item/projectile/beam //If it has, going to kill mode + lethal_shot_sound = 'sound/weapons/Laser.ogg' + + if(/obj/item/weapon/gun/energy/xray) + lethal_icon_color = "green" + lethal_projectile = /obj/item/projectile/beam/xray + projectile = /obj/item/projectile/beam/stun // Otherwise we fire xrays on both modes. + lethal_shot_sound = 'sound/weapons/eluger.ogg' + shot_sound = 'sound/weapons/Taser.ogg' + +/obj/machinery/porta_turret/proc/isLocked(mob/user) + if(locked && !issilicon(user)) + to_chat(user, "Controls locked.") + return 1 + if(HasController()) + return TRUE + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return FALSE + else + return TRUE + if(locked) + return TRUE + return FALSE + +/obj/machinery/porta_turret/attack_ai(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/attack_hand(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/proc/HasController() + var/area/A = get_area(src) + return A && A.turret_controls.len > 0 + +/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null) + if(HasController()) + to_chat(user, "[src] can only be controlled using the assigned turret controller.") + return + if(!anchored) + to_chat(user, "[src] has to be secured first!") + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableTurret", name, ui_x = 500, ui_y = 400) + ui.open() + +/obj/machinery/porta_turret/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_all, + "neutralize_nonsynth" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_down" = check_down, + ) + return data + +/obj/machinery/porta_turret/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(isLocked(usr)) + return TRUE + . = TRUE + + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authall") + check_all = !check_all + if("authdown") + check_down = !check_down + +/obj/machinery/porta_turret/power_change() + if(powered()) + stat &= ~NOPOWER + update_icon() + else + spawn(rand(0, 15)) + stat |= NOPOWER + update_icon() + + +/obj/machinery/porta_turret/attackby(obj/item/I, mob/user) + if(stat & BROKEN) + if(I.has_tool_quality(TOOL_CROWBAR)) + //If the turret is destroyed, you can remove it with a crowbar to + //try and salvage its components + to_chat(user, "You begin prying the metal coverings off.") + if(do_after(user, 20)) + if(can_salvage && prob(70)) + to_chat(user, "You remove the turret and salvage some components.") + if(installation) + var/obj/item/weapon/gun/energy/Gun = new installation(loc) + Gun.power_supply.charge = gun_charge + Gun.update_icon() + if(prob(50)) + new /obj/item/stack/material/steel(loc, rand(1,4)) + if(prob(50)) + new /obj/item/device/assembly/prox_sensor(loc) + else + to_chat(user, "You remove the turret but did not manage to salvage anything.") + qdel(src) // qdel + + else if(I.has_tool_quality(TOOL_WRENCH)) + if(enabled || raised) + to_chat(user, "You cannot unsecure an active turret!") + return + if(wrenching) + to_chat(user, "Someone is already [anchored ? "un" : ""]securing the turret!") + return + if(!anchored && isinspace()) + to_chat(user, "Cannot secure turrets in space!") + return + + user.visible_message(\ + "[user] begins [anchored ? "un" : ""]securing the turret.", \ + "You begin [anchored ? "un" : ""]securing the turret." \ + ) + + wrenching = TRUE + if(do_after(user, 50 * I.toolspeed)) + //This code handles moving the turret around. After all, it's a portable turret! + if(!anchored) + playsound(src, I.usesound, 100, 1) + anchored = TRUE + update_icon() + to_chat(user, "You secure the exterior bolts on the turret.") + else if(anchored) + playsound(src, I.usesound, 100, 1) + anchored = FALSE + to_chat(user, "You unsecure the exterior bolts on the turret.") + update_icon() + wrenching = FALSE + + else if(istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) + //Behavior lock/unlock mangement + if(allowed(user)) + locked = !locked + to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") + updateUsrDialog() + else + to_chat(user, "Access denied.") + + else + //if the turret was attacked with the intention of harming it: + user.setClickCooldown(user.get_attack_speed(I)) + take_damage(I.force * 0.5) + if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off + if(!attacked && !emagged) + attacked = 1 + spawn() + sleep(60) + attacked = 0 + ..() + +/obj/machinery/porta_turret/attack_generic(mob/living/L, damage) + if(isanimal(L)) + var/mob/living/simple_mob/S = L + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly. + visible_message("\The [S] [pick(S.attacktext)] \the [src]!") + take_damage(incoming_damage) + S.do_attack_animation(src) + return 1 + visible_message("\The [L] bonks \the [src]'s casing!") + return ..() + +/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + //Emagging the turret makes it go bonkers and stun everyone. It also makes + //the turret shoot much, much faster. + to_chat(user, "You short out [src]'s threat assessment circuits.") + visible_message("[src] hums oddly...") + emagged = TRUE + controllock = TRUE + enabled = FALSE //turns off the turret temporarily + sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit + enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here + return 1 + +/obj/machinery/porta_turret/take_damage(var/force) + if(!raised && !raising) + force = force / 8 + if(force < 5) + return + + health -= force + if(force > 5 && prob(45)) + spark_system.start() + if(health <= 0) + die() //the death process :( + +/obj/machinery/porta_turret/bullet_act(obj/item/projectile/Proj) + var/damage = Proj.get_structure_damage() + + if(!damage) + return + + if(enabled) + if(!attacked && !emagged) + attacked = 1 + spawn() + sleep(60) + attacked = FALSE + + ..() + + take_damage(damage) + +/obj/machinery/porta_turret/emp_act(severity) + if(enabled) + //if the turret is on, the EMP no matter how severe disables the turret for a while + //and scrambles its settings, with a slight chance of having an emag effect + check_arrest = prob(50) + check_records = prob(50) + check_weapons = prob(50) + check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on + check_anomalies = prob(50) + if(prob(5)) + emagged = TRUE + + enabled=0 + spawn(rand(60,600)) + if(!enabled) + enabled = TRUE + + ..() + +/obj/machinery/porta_turret/ai_defense/emp_act(severity) + if(prob(33)) // One in three chance to resist an EMP. This is significant if an AoE EMP is involved against multiple turrets. + return + ..() + +/obj/machinery/porta_turret/alien/emp_act(severity) // This is overrided to give an EMP resistance as well as avoid scambling the turret settings. + if(prob(75)) // Superior alien technology, I guess. + return + enabled = FALSE + spawn(rand(1 MINUTE, 2 MINUTES)) + if(!enabled) + enabled = TRUE + +/obj/machinery/porta_turret/ex_act(severity) + switch (severity) + if(1) + qdel(src) + if(2) + if(prob(25)) + qdel(src) + else + take_damage(initial(health) * 8) //should instakill most turrets + if(3) + take_damage(initial(health) * 8 / 3) //Level 4 is too weak to bother turrets + +/obj/machinery/porta_turret/proc/die() //called when the turret dies, ie, health <= 0 + health = 0 + stat |= BROKEN //enables the BROKEN bit + spark_system.start() //creates some sparks because they look cool + update_icon() + +/obj/machinery/porta_turret/process() + //the main machinery process + + if(stat & (NOPOWER|BROKEN)) + //if the turret has no power or is broken, make the turret pop down if it hasn't already + popDown() + return + + if(!enabled) + //if the turret is off, make it pop down + popDown() + return + + var/list/targets = list() //list of primary targets + var/list/secondarytargets = list() //targets that are least important + + var/list/seenturfs = list() + for(var/turf/T in oview(world.view, src)) + seenturfs += T + + for(var/mob/M as anything in living_mob_list) + if(M == null || M.z != z || !(get_turf(M) in seenturfs)) //Skip // ARFS Edit - Fix Roundstart error + continue + switch(assess_living(M)) + if(TURRET_PRIORITY_TARGET) + targets += M + if(TURRET_SECONDARY_TARGET) + secondarytargets += M + + for(var/obj/mecha/M as anything in mechas_list) + if(M.z != z || !(get_turf(M) in seenturfs)) // Skip + continue + switch(assess_mecha(M)) + if(TURRET_PRIORITY_TARGET) + targets += M + if(TURRET_SECONDARY_TARGET) + secondarytargets += M + + if(!tryToShootAt(targets) && !tryToShootAt(secondarytargets) && --timeout <= 0) + popDown() // no valid targets, close the cover + + if(auto_repair && (health < maxhealth)) + use_power(20000) + health = min(health+1, maxhealth) // 1HP for 20kJ + +/obj/machinery/porta_turret/proc/assess_living(var/mob/living/L) + if(!istype(L)) + return TURRET_NOT_TARGET + + if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var + return TURRET_NOT_TARGET + + if(faction && L.faction == faction) + return TURRET_NOT_TARGET + + if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. + return TURRET_NOT_TARGET + + if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really + return TURRET_NOT_TARGET //move onto next potential victim! + + if(get_dist(src, L) > 7) //if it's too far away, why bother? + return TURRET_NOT_TARGET + + if(!(L in check_trajectory(L, src))) //check if we have true line of sight + return TURRET_NOT_TARGET + + if(emagged) // If emagged not even the dead get a rest + return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET + + if(lethal && locate(/mob/living/silicon/ai) in get_turf(L)) //don't accidentally kill the AI! + return TURRET_NOT_TARGET + + if(check_synth || check_all) //If it's set to attack all non-silicons or everything, target them! + if(L.lying) + return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + return TURRET_PRIORITY_TARGET + + if(iscuffed(L)) // If the target is handcuffed, leave it alone + return TURRET_NOT_TARGET + + if(isanimal(L)) // Animals are not so dangerous + return check_anomalies ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + + if(isxenomorph(L) || isalien(L)) // Xenos are dangerous + return check_anomalies ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET + + if(ishuman(L)) //if the target is a human, analyze threat level + if(assess_perp(L) < 4) + return TURRET_NOT_TARGET //if threat level < 4, keep going + + if(L.lying) //if the perp is lying down, it's still a target but a less-important target + return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + + return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee + +/obj/machinery/porta_turret/proc/assess_mecha(var/obj/mecha/M) + if(!istype(M)) + return TURRET_NOT_TARGET + + if(!M.occupant) + return check_all ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET + + return assess_living(M.occupant) + +/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/carbon/human/H) + if(!H || !istype(H)) + return 0 + + if(emagged) + return 10 + + return H.assess_perp(src, check_access, check_weapons, check_records, check_arrest) + +/obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets) + if(targets.len && last_target && (last_target in targets) && target(last_target)) + return 1 + + while(targets.len > 0) + var/mob/living/M = pick(targets) + targets -= M + if(target(M)) + return 1 + + +/obj/machinery/porta_turret/proc/popUp() //pops the turret up + set waitfor = FALSE + if(disabled) + return + if(raising || raised) + return + if(stat & BROKEN) + return + set_raised_raising(raised, 1) + update_icon() + + var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) + flick_holder.layer = layer + 0.1 + flick("popup_[turret_type]", flick_holder) + playsound(src, 'sound/machines/turrets/turret_deploy.ogg', 100, 1) + sleep(10) + qdel(flick_holder) + + set_raised_raising(1, 0) + update_icon() + timeout = 10 + +/obj/machinery/porta_turret/proc/popDown() //pops the turret down + set waitfor = FALSE + last_target = null + if(disabled) + return + if(raising || !raised) + return + if(stat & BROKEN) + return + set_raised_raising(raised, 1) + update_icon() + + var/atom/flick_holder = new /atom/movable/porta_turret_cover(loc) + flick_holder.layer = layer + 0.1 + flick("popdown_[turret_type]", flick_holder) + playsound(src, 'sound/machines/turrets/turret_retract.ogg', 100, 1) + sleep(10) + qdel(flick_holder) + + set_raised_raising(0, 0) + update_icon() + timeout = 10 + +/obj/machinery/porta_turret/proc/set_raised_raising(var/incoming_raised, var/incoming_raising) + raised = incoming_raised + raising = incoming_raising + density = raised || raising + +/obj/machinery/porta_turret/proc/target(var/mob/living/target) + if(disabled) + return FALSE + if(target) + last_target = target + popUp() //pop the turret up if it's not already up. + set_dir(get_dir(src, target)) //even if you can't shoot, follow the target + playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound + spawn() + shootAt(target) + return TRUE + return FALSE + +/obj/machinery/porta_turret/proc/shootAt(var/mob/living/target) + //any emagged turrets will shoot extremely fast! This not only is deadly, but drains a lot power! + if(!(emagged || attacked)) //if it hasn't been emagged or attacked, it has to obey a cooldown rate + if(last_fired || !raised) //prevents rapid-fire shooting, unless it's been emagged + return + last_fired = TRUE + spawn() + sleep(shot_delay) + last_fired = FALSE + + if(!isturf(get_turf(src)) || !isturf(get_turf(target))) + return + + if(!raised) //the turret has to be raised in order to fire - makes sense, right? + return + + update_icon() + var/obj/item/projectile/A + if(emagged || lethal) + A = new lethal_projectile(loc) + playsound(src, lethal_shot_sound, 75, 1) + else + A = new projectile(loc) + playsound(src, shot_sound, 75, 1) + + var/power_mult = 1 + if(emagged) + power_mult = 4 // Lethal beams + higher rate of fire + else if(lethal) + power_mult = 2 // Lethal beams + use_power(reqpower * power_mult) + + //Turrets aim for the center of mass by default. + //If the target is grabbing someone then the turret smartly aims for extremities + var/def_zone + var/obj/item/weapon/grab/G = locate() in target + if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people. + def_zone = pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) + else + def_zone = pick(BP_TORSO, BP_GROIN) + + //Shooting Code: + A.firer = src + A.old_style_target(target) + A.launch_projectile_from_turf(target, def_zone, src) + + // Reset the time needed to go back down, since we just tried to shoot at someone. + timeout = 10 + +/datum/turret_checks + var/enabled + var/lethal + var/check_synth + var/check_access + var/check_records + var/check_arrest + var/check_weapons + var/check_anomalies + var/check_all + var/ailock + +/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) + if(controllock) + return + enabled = TC.enabled + lethal = TC.lethal + + check_synth = TC.check_synth + check_access = TC.check_access + check_records = TC.check_records + check_arrest = TC.check_arrest + check_weapons = TC.check_weapons + check_anomalies = TC.check_anomalies + check_all = TC.check_all + ailock = TC.ailock + + power_change() + +/* + Portable turret constructions + Known as "turret frame"s +*/ + +/obj/machinery/porta_turret_construct + name = "turret frame" + icon = 'icons/obj/turrets.dmi' + icon_state = "turret_frame" + density=TRUE + var/target_type = /obj/machinery/porta_turret // The type we intend to build + var/build_step = 0 //the current step in the building process + var/finish_name="turret" //the name applied to the product turret + var/installation = null //the gun type installed + var/gun_charge = 0 //the gun charge of the gun type installed + +/obj/machinery/porta_turret_construct/attackby(obj/item/I, mob/user) + //this is a bit unwieldy but self-explanatory + switch(build_step) + if(0) //first step + if(I.has_tool_quality(TOOL_WRENCH) && !anchored) + playsound(src, I.usesound, 100, 1) + to_chat(user, "You secure the external bolts.") + anchored = TRUE + build_step = 1 + return + + else if(I.has_tool_quality(TOOL_CROWBAR) && !anchored) + playsound(src, I.usesound, 75, 1) + to_chat(user, "You dismantle the turret construction.") + new /obj/item/stack/material/steel(loc, 5) + qdel(src) + return + + if(1) + if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) + var/obj/item/stack/M = I + if(M.use(2)) + to_chat(user, "You add some metal armor to the interior frame.") + build_step = 2 + icon_state = "turret_frame2" + else + to_chat(user, "You need two sheets of metal to continue construction.") + return + + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(src, I.usesound, 75, 1) + to_chat(user, "You unfasten the external bolts.") + anchored = FALSE + build_step = 0 + return + + if(2) + if(I.has_tool_quality(TOOL_WRENCH)) + playsound(src, I.usesound, 100, 1) + to_chat(user, "You bolt the metal armor into place.") + build_step = 3 + return + + else if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = I.get_welder() + if(!WT.isOn()) + return + if(WT.get_fuel() < 5) //uses up 5 fuel. + to_chat(user, "You need more fuel to complete this task.") + return + + playsound(src, I.usesound, 50, 1) + if(do_after(user, 20 * I.toolspeed)) + if(!src || !WT.remove_fuel(5, user)) return + build_step = 1 + to_chat(user, "You remove the turret's interior metal armor.") + new /obj/item/stack/material/steel(loc, 2) + return + + if(3) + if(istype(I, /obj/item/weapon/gun/energy)) //the gun installation part + + if(isrobot(user)) + return + var/obj/item/weapon/gun/energy/E = I //typecasts the item to an energy gun + if(!user.unEquip(I)) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") + return + installation = I.type //installation becomes I.type + gun_charge = E.power_supply.charge //the gun's charge is stored in gun_charge + to_chat(user, "You add [I] to the turret.") + target_type = /obj/machinery/porta_turret + + build_step = 4 + qdel(I) //delete the gun :( + return + + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(src, I.usesound, 100, 1) + to_chat(user, "You remove the turret's metal armor bolts.") + build_step = 2 + return + + if(4) + if(isprox(I)) + build_step = 5 + if(!user.unEquip(I)) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in \the [src]") + return + to_chat(user, "You add the prox sensor to the turret.") + qdel(I) + return + + //attack_hand() removes the gun + + if(5) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, I.usesound, 100, 1) + build_step = 6 + to_chat(user, "You close the internal access hatch.") + return + + //attack_hand() removes the prox sensor + + if(6) + if(istype(I, /obj/item/stack/material) && I.get_material_name() == MAT_STEEL) + var/obj/item/stack/M = I + if(M.use(2)) + to_chat(user, "You add some metal armor to the exterior frame.") + build_step = 7 + else + to_chat(user, "You need two sheets of metal to continue construction.") + return + + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, I.usesound, 100, 1) + build_step = 5 + to_chat(user, "You open the internal access hatch.") + return + + if(7) + if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = I.get_welder() + if(!WT.isOn()) return + if(WT.get_fuel() < 5) + to_chat(user, "You need more fuel to complete this task.") + + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 30 * WT.toolspeed)) + if(!src || !WT.remove_fuel(5, user)) + return + build_step = 8 + to_chat(user, "You weld the turret's armor down.") + + //The final step: create a full turret + var/obj/machinery/porta_turret/Turret = new target_type(loc) + Turret.name = finish_name + Turret.installation = installation + Turret.gun_charge = gun_charge + Turret.enabled = 0 + Turret.setup() + + qdel(src) // qdel + + else if(I.has_tool_quality(TOOL_CROWBAR)) + playsound(src, I.usesound, 75, 1) + to_chat(user, "You pry off the turret's exterior armor.") + new /obj/item/stack/material/steel(loc, 2) + build_step = 6 + return + + if(istype(I, /obj/item/weapon/pen)) //you can rename turrets like bots! + var/t = sanitizeSafe(tgui_input_text(user, "Enter new turret name", name, finish_name, MAX_NAME_LEN), MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + + finish_name = t + return + + ..() + +/obj/machinery/porta_turret_construct/attack_hand(mob/user) + switch(build_step) + if(4) + if(!installation) + return + build_step = 3 + + var/obj/item/weapon/gun/energy/Gun = new installation(loc) + Gun.power_supply.charge = gun_charge + Gun.update_icon() + installation = null + gun_charge = 0 + to_chat(user, "You remove [Gun] from the turret frame.") + + if(5) + to_chat(user, "You remove the prox sensor from the turret frame.") + new /obj/item/device/assembly/prox_sensor(loc) + build_step = 4 + +/obj/machinery/porta_turret_construct/attack_ai() + return + +/atom/movable/porta_turret_cover + icon = 'icons/obj/turrets.dmi' + +#undef TURRET_PRIORITY_TARGET +#undef TURRET_SECONDARY_TARGET +#undef TURRET_NOT_TARGET diff --git a/code/game/machinery/protean_reconstitutor.dm b/code/game/machinery/protean_reconstitutor.dm new file mode 100644 index 00000000000..287e3e0c1a2 --- /dev/null +++ b/code/game/machinery/protean_reconstitutor.dm @@ -0,0 +1,301 @@ +/obj/machinery/protean_reconstitutor + name = "protean reconstitutor" + desc = "A complex machine that is most definitely not just a large tub into which one pours a large amount of untethered nanites, then adds a protean positronic brain and orchestrator, in order to reconstitute a disintegrated protean... it's complicated, really!" + description_info = "Use a protean positronic brain, orchestrator, refactory, and nanopaste to \'fill\' the machine, then interact with it once it's ready. Protean components can be retrieved using a wrench, but any nanopaste inserted will be converted, cannot be reclaimed, and will be lost if the machine is disassembled!" + icon = 'icons/obj/protean_recon.dmi' + icon_state = "recon-nopower" + var/state_base = "recon" + anchored = TRUE + density = TRUE + power_channel = EQUIP + use_power = USE_POWER_IDLE + idle_power_usage = 10 + active_power_usage = 1000 + var/processing_revive = FALSE + clicksound = 'sound/machines/buttonbeep.ogg' //standard initialization sound + var/dingsound = 'sound/machines/kitchen/microwave/microwave-end.ogg' //sound to play when the process is complete + var/buzzsound = 'sound/items/nif_tone_bad.ogg' //sound to play when we have to abort due to loss of posibrain client + + //vars for basic functionality + var/obj/item/device/mmi/digital/posibrain/nano/protean_brain = null //only allow protean brains, no midround upgrades to bypass the whitelist! + var/obj/item/organ/internal/nano/orchestrator/protean_orchestrator = null //essential + var/obj/item/organ/internal/nano/refactory/protean_refactory = null //not essential, but nice to have; lets us transfer stored materials + var/nanomass_reserve = 0 //starting reserve - will be wiped if it's deconstructed! + var/nanotank_max = 300 //how much we can store at once, higher = better + var/nanomass_required = 150 //how much we need in order to make a new body, non-adjustable + var/paste_inefficiency = 5 //divisor to mech_repair value of paste added; higher = less effective; adv paste is +40 reserve at base, or +200 at max! + + //time vars + var/base_cook_time = 150 SECONDS //how long to initially delay before starting the overall cooking cycle + var/per_organ_delay = 5 SECONDS //how long to delay the cycle per organ and per synch step (multiply by three to get time for all three organs), then add base cook time for total time + var/finalize_time = 135 SECONDS //finally, how long we need before popping them out of the tank + + //component vars + circuit = /obj/item/weapon/circuitboard/protean_reconstitutor + +/obj/machinery/protean_reconstitutor/Initialize() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) + component_parts += new /obj/item/stack/cable_coil(src, 5) + RefreshParts() + . = ..() + +/obj/machinery/protean_reconstitutor/RefreshParts() + //total paste storage cap (300 * the rating, straightforward) + var/store_rating = initial(nanotank_max) + for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts) + store_rating = store_rating * MB.rating + nanotank_max = store_rating + + //inefficiency of adding paste (amount of uses * (mech_repair / inefficiency)); most complex, good way to get good bang for your buck tho + var/paste_rating = initial(paste_inefficiency) + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + paste_rating = paste_rating - (M.rating - 1) + paste_inefficiency = paste_rating + ..() + +/obj/machinery/protean_reconstitutor/update_icon() + cut_overlays() + if(stat & (NOPOWER|BROKEN) || !anchored) + if(stat & BROKEN) + icon_state = "[state_base]-broken" + else + icon_state = "[state_base]-nopower" + return + icon_state = state_base + if(protean_brain) + add_overlay("[state_base]-brain") + if(protean_orchestrator) + add_overlay("[state_base]-orchestrator") + if(protean_refactory) + add_overlay("[state_base]-refactory") + if(nanomass_reserve >= nanomass_required) + add_overlay("[state_base]-tank_full") + +/obj/machinery/protean_reconstitutor/examine() + . = ..() + if(protean_refactory) + . += "A protean refactory is present." + if(protean_orchestrator) + . += "A protean orchestrator is present." + if(protean_brain) + . += "It currently has a protean positronic brain." + if(!protean_brain.brainmob.client) + . += "The positronic brain appears to be inactive!" + . += "The readout shows that it has [nanomass_reserve] units of nanites ready for use. It requires [nanomass_required] per \'revive\' process, and has a maximum capacity of [nanotank_max] units." + +/obj/machinery/protean_reconstitutor/attackby(obj/item/W as obj, mob/user as mob) + src.add_fingerprint(user) + if(processing_revive) + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") + playsound(src, buzzsound, 100, 1, -1) + return + + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + + if(istype(W,/obj/item/device/mmi/digital/posibrain/nano)) + var/obj/item/device/mmi/digital/posibrain/nano/NB = W + if(!NB.brainmob.client) + to_chat(user,"You cannot use an inactive positronic brain for this process.") + return + to_chat(user,"You slot \the [NB] into \the [src].") + user.drop_from_inventory(NB) + NB.loc = src + protean_brain = NB + + if(istype(W,/obj/item/organ/internal/nano/orchestrator)) + to_chat(user,"You slot \the [W] into \the [src].") + user.drop_from_inventory(W) + W.loc = src + protean_orchestrator = W + + if(istype(W,/obj/item/organ/internal/nano/refactory)) + to_chat(user,"You slot \the [W] into \the [src].") + user.drop_from_inventory(W) + W.loc = src + protean_refactory = W + + if(istype(W,/obj/item/stack/nanopaste)) + var/obj/item/stack/nanopaste/NP = W + if(nanomass_reserve >= nanotank_max) + to_chat(user,"The tank is full!") + return + nanomass_reserve += NP.amount * max(1,NP.mech_repair / paste_inefficiency) + if(nanomass_reserve > nanotank_max) + nanomass_reserve = nanotank_max + to_chat(user,"You fill \the [src] with paste from \the [NP]. The display now reads [nanomass_reserve]/[nanotank_max] units.") + qdel(NP) + + if(W.has_tool_quality(TOOL_WRENCH)) + if(protean_brain || protean_orchestrator || protean_refactory) + var/choice = tgui_input_list(usr, "What component would you like to remove?", "Remove Component", list(protean_brain,protean_orchestrator,protean_refactory)) + if(!choice) return + + if(choice == protean_brain) + to_chat(user, "You fish \the [protean_brain] out of \the [src].") + protean_brain.forceMove(get_turf(src)) + playsound(src, W.usesound, 50, 1) + src.protean_brain = null + if(choice == protean_refactory) + to_chat(user, "You fish \the [protean_refactory] out of \the [src].") + protean_refactory.forceMove(get_turf(src)) + playsound(src, W.usesound, 50, 1) + src.protean_refactory = null + else if(choice == protean_orchestrator) + to_chat(user, "You fish \the [protean_orchestrator] out of \the [src].") + protean_orchestrator.forceMove(get_turf(src)) + playsound(src, W.usesound, 50, 1) + src.protean_orchestrator = null + else + to_chat(user, "\The [src] does not have any protean components you can retrieve.") + + update_icon() + ..() + +/obj/machinery/protean_reconstitutor/attack_hand(mob/user as mob) + if(!protean_brain || !protean_orchestrator || !protean_refactory || (nanomass_reserve < nanomass_required)) + //no brain, no orchestrator, and/or not enough goo + to_chat(user,"Essential components missing, or insufficient materials available!") + playsound(src, buzzsound, 100, 1, -1) + update_icon() + return + if(processing_revive) + //we're currently processing a patient, chill out! + src.visible_message("\The [src] chirps, \"Reconstitution cycle currently in progress, please wait!\"") + playsound(src, buzzsound, 100, 1, -1) + return + if(!protean_brain.brainmob.client) + src.visible_message("\The [src] chirps, \"Warning, no positronic neural network activity detected! Recommend removing inactive core.\"") + return + else if(!processing_revive && protean_brain && protean_orchestrator && protean_refactory && (nanomass_reserve >= nanomass_required)) + //we're good, let's get recombobulating! + src.visible_message("[user] initializes \the [src]. It chirps, \"Please stand by, synchronizing components... estimated time to completion: five minutes.\"") + processing_revive = TRUE + power_change() + if(prob(2)) + playsound(src, 'sound/machines/blender.ogg', 50, 1) + else + playsound(src, clicksound, 50, 1) + nanomass_reserve -= nanomass_required + sleep(base_cook_time) + var/mob/living/carbon/human/protean/P = new /mob/living/carbon/human/protean + var/mats_cached + var/list/materials_cache + P.loc = src + P.name = "Unfinished Protean" + P.real_name = "Unfinished Protean" + for(var/organ in P.internal_organs_by_name) + sleep(per_organ_delay) + var/obj/item/O = P.internal_organs_by_name[organ] + if(istype(O,/obj/item/organ/internal/nano/refactory)) + src.visible_message("\The [src] chirps, \"Initializing refactory...\"") + P.internal_organs_by_name.Remove(O) + P.contents.Remove(O) + qdel(O) + P.internal_organs_by_name.Add(list(O_FACT = protean_refactory)) + P.internal_organs.Add(protean_refactory) + //cache our mats otherwise they get wiped by the revive + materials_cache = protean_refactory.materials.Copy() + mats_cached = TRUE + protean_refactory.loc = P + if(istype(O,/obj/item/organ/internal/nano/orchestrator)) + src.visible_message("\The [src] chirps, \"Linking nanoswarm to orchestrator...\"") + P.internal_organs_by_name.Remove(O) + P.internal_organs.Remove(O) + P.contents.Remove(O) + qdel(O) + P.internal_organs_by_name.Add(list(O_ORCH = protean_orchestrator)) + P.internal_organs.Add(protean_orchestrator) + protean_orchestrator.loc = P + if(istype(O,/obj/item/organ/internal/mmi_holder/posibrain/nano)) + src.visible_message("\The [src] chirps, \"Synchronizing positronic neural architecture...\"") + //on the offchance our client blipped before getting to this step, abort, schloop the organs back into the machine, dissolve the body, and refund the nanos + if(!protean_brain.brainmob.client) + src.visible_message("\The [src] buzzes, \"No positronic neural activity detected! Aborting cycle!\"") + playsound(src, buzzsound, 100, 1, -1) + processing_revive = FALSE + qdel(P) + nanomass_reserve += nanomass_required + update_icon() + return + var/obj/item/organ/internal/mmi_holder/posibrain/nano/BR = O + BR.stored_mmi = null //toss the dummy... + BR.contents.Cut() + BR.stored_mmi = protean_brain //...and implant the salvaged mmi in its place + BR.contents.Add(protean_brain) + var/client/posibrain_client = protean_brain.brainmob.client + var/picked_ckey = posibrain_client.ckey + var/picked_slot = posibrain_client.prefs.default_slot + var/charjob + var/datum/data/record/record_found + record_found = find_general_record("name",posibrain_client.prefs.real_name) + if(record_found) + charjob = record_found.fields["real_rank"] + else + return + if(P.dna) + P.dna.ResetUIFrom(P) + P.sync_organ_dna() + + if(P.mind) + P.mind.loaded_from_ckey = picked_ckey + P.mind.loaded_from_slot = picked_slot + var/datum/antagonist/antag_data = get_antag_data(P.mind.special_role) + if(antag_data) + antag_data.add_antagonist(P.mind) + antag_data.place_mob(P) + P.mind.assigned_role = charjob + P.mind.role_alt_title = job_master.GetPlayerAltTitle(P, charjob) + + //no need to be particularly thorough about language handover, we can safely assume that they were allowed to have it if they had it to begin with + P.languages.Cut() + P.languages = protean_brain.brainmob.languages.Copy() + + for(var/key in posibrain_client.prefs.language_custom_keys) + if(posibrain_client.prefs.language_custom_keys[key]) + var/datum/language/keylang = GLOB.all_languages[posibrain_client.prefs.language_custom_keys[key]] + if(keylang) + P.language_keys[key] = keylang + + if(posibrain_client.prefs.preferred_language) + var/datum/language/def_lang = GLOB.all_languages[posibrain_client.prefs.preferred_language] + if(def_lang) + P.default_language = def_lang + + protean_brain.brainmob.mind.transfer_to(P) + protean_brain.loc = BR + protean_refactory = null + protean_brain = null + protean_orchestrator = null + sleep(finalize_time) //let 'em cook a tiny bit longer + P.revive() + P.apply_vore_prefs() + //run a little revive, load their prefs, and boot a new NIF on them for the finishing touches and cleanup... (yes, we need to initialize a new NIF, they don't get one from the revive process) + //using revive is honestly a bit overkill since it kinda deletes-and-replaces most of the guts anyway (hence the cache and restore of refactory contents; otherwise they get wiped!), but it also ensures the new protean comes out in their "base form" as well as hopefully cleaning up any loose ends in the resurrection process + var/obj/item/device/nif/protean/new_nif = new() + new_nif.quick_implant(P) + //revive complete, now restore the cached mats (if we had any) + if(mats_cached == TRUE) + src.visible_message("\The [src] chirps, \"Reindexing archived refactory materials storage.\"") + for(var/organ in P.internal_organs_by_name) + var/obj/item/O = P.internal_organs_by_name[organ] + if(istype(O,/obj/item/organ/internal/nano/refactory)) + var/obj/item/organ/internal/nano/refactory/RF = O + RF.materials = materials_cache.Copy() + materials_cache.Cut() + mats_cached = FALSE + //finally... drop them in front of the machine + src.visible_message("\The [src] chirps, \"Protean reconstitution cycle complete!\"") + to_chat(P,"You feel your sense of self expanding, spreading out to inhabit your new \'body\'. You feel... ALIVE!") + playsound(src, dingsound, 100, 1, -1) //soup's on! + P.loc = src.loc + processing_revive = FALSE + update_icon() + update_icon() diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index db94f63adbe..cd23dad276a 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -91,7 +91,7 @@ update_icon() user.visible_message("[user] inserts [charging] into [src].", "You insert [charging] into [src].") - else if(portable && G.is_wrench()) + else if(portable && G.has_tool_quality(TOOL_WRENCH)) if(charging) to_chat(user, "Remove [charging] first!") return diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index cd497b2d216..31bc167040f 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -1,352 +1,355 @@ -/obj/machinery/recharge_station - name = "cyborg recharging station" - desc = "A heavy duty rapid charging system, designed to quickly recharge cyborg power reserves." - icon = 'icons/obj/objects.dmi' - icon_state = "borgcharger0" - density = TRUE - anchored = TRUE - unacidable = TRUE - circuit = /obj/item/weapon/circuitboard/recharge_station - use_power = USE_POWER_IDLE - idle_power_usage = 50 - var/mob/occupant = null - var/obj/item/weapon/cell/cell = null - var/icon_update_tick = 0 // Used to rebuild the overlay only once every 10 ticks - var/charging = 0 - - var/charging_power // W. Power rating used for charging the cyborg. 120 kW if un-upgraded - var/restore_power_active // W. Power drawn from APC when an occupant is charging. 40 kW if un-upgraded - var/restore_power_passive // W. Power drawn from APC when idle. 7 kW if un-upgraded - var/weld_rate = 0 // How much brute damage is repaired per tick - var/wire_rate = 0 // How much burn damage is repaired per tick - - var/weld_power_use = 2300 // power used per point of brute damage repaired. 2.3 kW ~ about the same power usage of a handheld arc welder - var/wire_power_use = 500 // power used per point of burn damage repaired. - -/obj/machinery/recharge_station/Initialize() - . = ..() - default_apply_parts() - cell = default_use_hicell() - update_icon() - -/obj/machinery/recharge_station/proc/has_cell_power() - return cell && cell.percent() > 0 - -/obj/machinery/recharge_station/process() - if(stat & (BROKEN)) - return - if(!cell) // Shouldn't be possible, but sanity check - return - - if((stat & NOPOWER) && !has_cell_power()) // No power and cell is dead. - if(icon_update_tick) - icon_update_tick = 0 //just rebuild the overlay once more only - update_icon() - return - - //First, draw from the internal power cell to recharge/repair/etc the occupant - if(occupant) - process_occupant() - - //Then, if external power is available, recharge the internal cell - var/recharge_amount = 0 - if(!(stat & NOPOWER)) - // Calculating amount of power to draw - recharge_amount = (occupant ? restore_power_active : restore_power_passive) * CELLRATE - - recharge_amount = cell.give(recharge_amount) - use_power(recharge_amount / CELLRATE) - else - // Since external power is offline, draw operating current from the internal cell - cell.use(get_power_usage() * CELLRATE) - - if(icon_update_tick >= 10) - icon_update_tick = 0 - else - icon_update_tick++ - - if(occupant || recharge_amount) - update_icon() - -//Processes the occupant, drawing from the internal power cell if needed. -/obj/machinery/recharge_station/proc/process_occupant() - if(isrobot(occupant)) - var/mob/living/silicon/robot/R = occupant - if(R.module) - R.module.respawn_consumable(R, charging_power * CELLRATE / 250) //consumables are magical, apparently - if(R.cell && !R.cell.fully_charged()) - var/diff = min(R.cell.maxcharge - R.cell.charge, charging_power * CELLRATE) // Capped by charging_power / tick - var/charge_used = cell.use(diff) - R.cell.give(charge_used) - - //Lastly, attempt to repair the cyborg if enabled - if(weld_rate && R.getBruteLoss() && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) - R.adjustBruteLoss(-weld_rate) - if(wire_rate && R.getFireLoss() && cell.checked_use(wire_power_use * wire_rate * CELLRATE)) - R.adjustFireLoss(-wire_rate) - - //VOREStation Add Start - else if(ispAI(occupant)) - var/mob/living/silicon/pai/P = occupant - - if(P.nutrition < 400) - P.nutrition = min(P.nutrition+10, 400) - cell.use(7000/450*10) - //VOREStation Add End - - else if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - - if(H.isSynthetic()) - // In case they somehow end up with positive values for otherwise unobtainable damage... - if(H.getToxLoss() > 0) - H.adjustToxLoss(-(rand(1,3))) - if(H.getOxyLoss() > 0) - H.adjustOxyLoss(-(rand(1,3))) - if(H.getCloneLoss() > 0) - H.adjustCloneLoss(-(rand(1,3))) - if(H.getBrainLoss() > 0) - H.adjustBrainLoss(-(rand(1,3))) - - // Also recharge their internal battery. - if(H.isSynthetic() && H.nutrition < 500) //VOREStation Edit - H.nutrition = min(H.nutrition+(10*(1-min(H.species.synthetic_food_coeff, 0.9))), 500) //VOREStation Edit - cell.use(7000/450*10) - - // And clear up radiation - if(H.radiation > 0 || H.accumulated_rads > 0) - H.radiation = max(H.radiation - 25, 0) - H.accumulated_rads = max(H.accumulated_rads - 25, 0) - - if(H.wearing_rig) // stepping into a borg charger to charge your rig and fix your shit - var/obj/item/weapon/rig/wornrig = H.get_rig() - if(wornrig) // just to make sure - for(var/obj/item/rig_module/storedmod in wornrig.installed_modules) - if(weld_rate && storedmod.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) - to_chat(H, "[storedmod] is repaired!") - storedmod.damage = 0 - if(wornrig.chest) - var/obj/item/clothing/suit/space/rig/rigchest = wornrig.chest - if(weld_rate && rigchest.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) - rigchest.breaches = list() - rigchest.calc_breach_damage() - to_chat(H, "[rigchest] is repaired!") - if(wornrig.cell) - var/obj/item/weapon/cell/rigcell = wornrig.cell - var/diff = min(rigcell.maxcharge - rigcell.charge, charging_power * CELLRATE) // Capped by charging_power / tick - var/charge_used = cell.use(diff) - rigcell.give(charge_used) - -/obj/machinery/recharge_station/examine(mob/user) - . = ..() - . += "The charge meter reads: [round(chargepercentage())]%" - -/obj/machinery/recharge_station/proc/chargepercentage() - if(!cell) - return 0 - return cell.percent() - -/obj/machinery/recharge_station/relaymove(mob/user as mob) - if(user.stat) - return - go_out() - return - -/obj/machinery/recharge_station/emp_act(severity) - if(occupant) - occupant.emp_act(severity) - go_out() - if(cell) - cell.emp_act(severity) - ..(severity) - -/obj/machinery/recharge_station/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(!occupant) - if(default_deconstruction_screwdriver(user, O)) - return - if(default_deconstruction_crowbar(user, O)) - return - if(default_part_replacement(user, O)) - return - if (istype(O, /obj/item/weapon/grab) && get_dist(src,user)<2) - var/obj/item/weapon/grab/G = O - if(istype(G.affecting,/mob/living)) - var/mob/living/M = G.affecting - qdel(O) - go_in(M) - - ..() - -/obj/machinery/recharge_station/MouseDrop_T(var/mob/target, var/mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)) - return - - go_in(target) - -/obj/machinery/recharge_station/RefreshParts() - ..() - var/man_rating = 0 - var/cap_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - man_rating += P.rating - cell = locate(/obj/item/weapon/cell) in component_parts - - charging_power = 40000 + 40000 * cap_rating - restore_power_active = 10000 + 15000 * cap_rating - restore_power_passive = 5000 + 1000 * cap_rating - weld_rate = max(0, man_rating - 3) - wire_rate = max(0, man_rating - 5) - - desc = initial(desc) - desc += " Uses a dedicated internal power cell to deliver [charging_power]W when in use." - if(weld_rate) - desc += "
                    It is capable of repairing structural damage." - if(wire_rate) - desc += "
                    It is capable of repairing burn damage." - -/obj/machinery/recharge_station/proc/build_overlays() - cut_overlays() - switch(round(chargepercentage())) - if(1 to 20) - add_overlay("statn_c0") - if(21 to 40) - add_overlay("statn_c20") - if(41 to 60) - add_overlay("statn_c40") - if(61 to 80) - add_overlay("statn_c60") - if(81 to 98) - add_overlay("statn_c80") - if(99 to 110) - add_overlay("statn_c100") - -/obj/machinery/recharge_station/update_icon() - ..() - if(stat & BROKEN) - icon_state = "borgcharger0" - return - - if(occupant) - if((stat & NOPOWER) && !has_cell_power()) - icon_state = "borgcharger2" - else - icon_state = "borgcharger1" - else - icon_state = "borgcharger0" - - if(icon_update_tick == 0) - build_overlays() - -/obj/machinery/recharge_station/Bumped(var/mob/living/L) - go_in(L) - -/obj/machinery/recharge_station/proc/go_in(var/mob/living/L) - - if(occupant) - return - - if(istype(L, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = L - - if(R.incapacitated()) - return - - if(!R.cell) - return - - if(istype(R, /mob/living/silicon/robot/platform)) - to_chat(R, SPAN_WARNING("You are too large to fit into \the [src].")) - return - - add_fingerprint(R) - R.reset_view(src) - R.forceMove(src) - occupant = R - update_icon() - return 1 - - //VOREStation Add Start - else if(istype(L, /mob/living/silicon/pai)) - var/mob/living/silicon/pai/P = L - - if(P.incapacitated()) - return - - add_fingerprint(P) - P.reset_view(src) - P.forceMove(src) - occupant = P - update_icon() - return 1 - //VOREStation Add End - - else if(istype(L, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = L - if(H.isSynthetic() || H.wearing_rig) - add_fingerprint(H) - H.reset_view(src) - H.forceMove(src) - occupant = H - update_icon() - return 1 - else - return - -/obj/machinery/recharge_station/proc/go_out() - if(!occupant) - return - - occupant.forceMove(src.loc) - occupant.reset_view() - occupant = null - update_icon() - -/obj/machinery/recharge_station/verb/move_eject() - set category = "Object" - set name = "Eject Recharger" - set src in oview(1) - - if(usr.incapacitated() || !isliving(usr)) - return - - go_out() - add_fingerprint(usr) - return - -/obj/machinery/recharge_station/verb/move_inside() - set category = "Object" - set name = "Enter Recharger" - set src in oview(1) - - if(usr.incapacitated() || !isliving(usr)) - return - - go_in(usr) - -/obj/machinery/recharge_station/ghost_pod_recharger - name = "drone pod" - desc = "This is a pod which used to contain a drone... Or maybe it still does?" - icon = 'icons/obj/structures.dmi' - -/obj/machinery/recharge_station/ghost_pod_recharger/update_icon() - ..() - if(stat & BROKEN) - icon_state = "borg_pod_closed" - desc = "It appears broken..." - return - - if(occupant) - if((stat & NOPOWER) && !has_cell_power()) - icon_state = "borg_pod_closed" - desc = "It appears to be unpowered..." - else - icon_state = "borg_pod_closed" - else - icon_state = "borg_pod_opened" - - if(icon_update_tick == 0) - build_overlays() +/obj/machinery/recharge_station + name = "cyborg recharging station" + desc = "A heavy duty rapid charging system, designed to quickly recharge cyborg power reserves." + icon = 'icons/obj/objects.dmi' + icon_state = "borgcharger0" + density = TRUE + anchored = TRUE + unacidable = TRUE + circuit = /obj/item/weapon/circuitboard/recharge_station + use_power = USE_POWER_IDLE + idle_power_usage = 50 + var/mob/occupant = null + var/obj/item/weapon/cell/cell = null + var/icon_update_tick = 0 // Used to rebuild the overlay only once every 10 ticks + var/charging = 0 + + var/charging_power // W. Power rating used for charging the cyborg. 120 kW if un-upgraded + var/restore_power_active // W. Power drawn from APC when an occupant is charging. 40 kW if un-upgraded + var/restore_power_passive // W. Power drawn from APC when idle. 7 kW if un-upgraded + var/weld_rate = 0 // How much brute damage is repaired per tick + var/wire_rate = 0 // How much burn damage is repaired per tick + + var/weld_power_use = 2300 // power used per point of brute damage repaired. 2.3 kW ~ about the same power usage of a handheld arc welder + var/wire_power_use = 500 // power used per point of burn damage repaired. + +/obj/machinery/recharge_station/Initialize() + . = ..() + default_apply_parts() + cell = default_use_hicell() + update_icon() + +/obj/machinery/recharge_station/proc/has_cell_power() + return cell && cell.percent() > 0 + +/obj/machinery/recharge_station/process() + if(stat & (BROKEN)) + return + if(!cell) // Shouldn't be possible, but sanity check + return + + if((stat & NOPOWER) && !has_cell_power()) // No power and cell is dead. + if(icon_update_tick) + icon_update_tick = 0 //just rebuild the overlay once more only + update_icon() + return + + //First, draw from the internal power cell to recharge/repair/etc the occupant + if(occupant) + process_occupant() + + //Then, if external power is available, recharge the internal cell + var/recharge_amount = 0 + if(!(stat & NOPOWER)) + // Calculating amount of power to draw + recharge_amount = (occupant ? restore_power_active : restore_power_passive) * CELLRATE + + recharge_amount = cell.give(recharge_amount) + use_power(recharge_amount / CELLRATE) + else + // Since external power is offline, draw operating current from the internal cell + cell.use(get_power_usage() * CELLRATE) + + if(icon_update_tick >= 10) + icon_update_tick = 0 + else + icon_update_tick++ + + if(occupant || recharge_amount) + update_icon() + +//Processes the occupant, drawing from the internal power cell if needed. +/obj/machinery/recharge_station/proc/process_occupant() + if(isrobot(occupant)) + var/mob/living/silicon/robot/R = occupant + var/overcharged = FALSE + if(R.cell.maxcharge < R.cell.charge) + overcharged = TRUE + if(R.module && !overcharged) + R.module.respawn_consumable(R, charging_power * CELLRATE / 250) //consumables are magical, apparently + if(R.cell && !R.cell.fully_charged() && !overcharged) + var/diff = min(R.cell.maxcharge - R.cell.charge, charging_power * CELLRATE) // Capped by charging_power / tick + var/charge_used = cell.use(diff) + R.cell.give(charge_used) + + //Lastly, attempt to repair the cyborg if enabled + if(weld_rate && R.getBruteLoss() && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) + R.adjustBruteLoss(-weld_rate) + if(wire_rate && R.getFireLoss() && cell.checked_use(wire_power_use * wire_rate * CELLRATE)) + R.adjustFireLoss(-wire_rate) + + //VOREStation Add Start + else if(ispAI(occupant)) + var/mob/living/silicon/pai/P = occupant + + if(P.nutrition < 400) + P.nutrition = min(P.nutrition+10, 400) + cell.use(7000/450*10) + //VOREStation Add End + + else if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + + if(H.isSynthetic()) + // In case they somehow end up with positive values for otherwise unobtainable damage... + if(H.getToxLoss() > 0) + H.adjustToxLoss(-(rand(1,3))) + if(H.getOxyLoss() > 0) + H.adjustOxyLoss(-(rand(1,3))) + if(H.getCloneLoss() > 0) + H.adjustCloneLoss(-(rand(1,3))) + if(H.getBrainLoss() > 0) + H.adjustBrainLoss(-(rand(1,3))) + + // Also recharge their internal battery. + if(H.isSynthetic() && H.nutrition < 500) //VOREStation Edit + H.nutrition = min(H.nutrition+(10*(1-min(H.species.synthetic_food_coeff, 0.9))), 500) //VOREStation Edit + cell.use(7000/450*10) + + // And clear up radiation + if(H.radiation > 0 || H.accumulated_rads > 0) + H.radiation = max(H.radiation - 25, 0) + H.accumulated_rads = max(H.accumulated_rads - 25, 0) + + if(H.wearing_rig) // stepping into a borg charger to charge your rig and fix your shit + var/obj/item/weapon/rig/wornrig = H.get_rig() + if(wornrig) // just to make sure + for(var/obj/item/rig_module/storedmod in wornrig.installed_modules) + if(weld_rate && storedmod.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) + to_chat(H, "[storedmod] is repaired!") + storedmod.damage = 0 + if(wornrig.chest) + var/obj/item/clothing/suit/space/rig/rigchest = wornrig.chest + if(weld_rate && rigchest.damage && cell.checked_use(weld_power_use * weld_rate * CELLRATE)) + rigchest.breaches = list() + rigchest.calc_breach_damage() + to_chat(H, "[rigchest] is repaired!") + if(wornrig.cell) + var/obj/item/weapon/cell/rigcell = wornrig.cell + var/diff = min(rigcell.maxcharge - rigcell.charge, charging_power * CELLRATE) // Capped by charging_power / tick + var/charge_used = cell.use(diff) + rigcell.give(charge_used) + +/obj/machinery/recharge_station/examine(mob/user) + . = ..() + . += "The charge meter reads: [round(chargepercentage())]%" + +/obj/machinery/recharge_station/proc/chargepercentage() + if(!cell) + return 0 + return cell.percent() + +/obj/machinery/recharge_station/relaymove(mob/user as mob) + if(user.stat) + return + go_out() + return + +/obj/machinery/recharge_station/emp_act(severity) + if(occupant) + occupant.emp_act(severity) + go_out() + if(cell) + cell.emp_act(severity) + ..(severity) + +/obj/machinery/recharge_station/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(!occupant) + if(default_deconstruction_screwdriver(user, O)) + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + if (istype(O, /obj/item/weapon/grab) && get_dist(src,user)<2) + var/obj/item/weapon/grab/G = O + if(istype(G.affecting,/mob/living)) + var/mob/living/M = G.affecting + qdel(O) + go_in(M) + + ..() + +/obj/machinery/recharge_station/MouseDrop_T(var/mob/target, var/mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)) + return + + go_in(target) + +/obj/machinery/recharge_station/RefreshParts() + ..() + var/man_rating = 0 + var/cap_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + man_rating += P.rating + cell = locate(/obj/item/weapon/cell) in component_parts + + charging_power = 40000 + 40000 * cap_rating + restore_power_active = 10000 + 15000 * cap_rating + restore_power_passive = 5000 + 1000 * cap_rating + weld_rate = max(0, man_rating - 3) + wire_rate = max(0, man_rating - 5) + + desc = initial(desc) + desc += " Uses a dedicated internal power cell to deliver [charging_power]W when in use." + if(weld_rate) + desc += "
                    It is capable of repairing structural damage." + if(wire_rate) + desc += "
                    It is capable of repairing burn damage." + +/obj/machinery/recharge_station/proc/build_overlays() + cut_overlays() + switch(round(chargepercentage())) + if(1 to 20) + add_overlay("statn_c0") + if(21 to 40) + add_overlay("statn_c20") + if(41 to 60) + add_overlay("statn_c40") + if(61 to 80) + add_overlay("statn_c60") + if(81 to 98) + add_overlay("statn_c80") + if(99 to 110) + add_overlay("statn_c100") + +/obj/machinery/recharge_station/update_icon() + ..() + if(stat & BROKEN) + icon_state = "borgcharger0" + return + + if(occupant) + if((stat & NOPOWER) && !has_cell_power()) + icon_state = "borgcharger2" + else + icon_state = "borgcharger1" + else + icon_state = "borgcharger0" + + if(icon_update_tick == 0) + build_overlays() + +/obj/machinery/recharge_station/Bumped(var/mob/living/L) + go_in(L) + +/obj/machinery/recharge_station/proc/go_in(var/mob/living/L) + + if(occupant) + return + + if(istype(L, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = L + + if(R.incapacitated()) + return + + if(!R.cell) + return + + if(istype(R, /mob/living/silicon/robot/platform)) + to_chat(R, SPAN_WARNING("You are too large to fit into \the [src].")) + return + + add_fingerprint(R) + R.reset_view(src) + R.forceMove(src) + occupant = R + update_icon() + return 1 + + //VOREStation Add Start + else if(istype(L, /mob/living/silicon/pai)) + var/mob/living/silicon/pai/P = L + + if(P.incapacitated()) + return + + add_fingerprint(P) + P.reset_view(src) + P.forceMove(src) + occupant = P + update_icon() + return 1 + //VOREStation Add End + + else if(istype(L, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic() || H.wearing_rig) + add_fingerprint(H) + H.reset_view(src) + H.forceMove(src) + occupant = H + update_icon() + return 1 + else + return + +/obj/machinery/recharge_station/proc/go_out() + if(!occupant) + return + + occupant.forceMove(src.loc) + occupant.reset_view() + occupant = null + update_icon() + +/obj/machinery/recharge_station/verb/move_eject() + set category = "Object" + set name = "Eject Recharger" + set src in oview(1) + + if(usr.incapacitated() || !isliving(usr)) + return + + go_out() + add_fingerprint(usr) + return + +/obj/machinery/recharge_station/verb/move_inside() + set category = "Object" + set name = "Enter Recharger" + set src in oview(1) + + if(usr.incapacitated() || !isliving(usr)) + return + + go_in(usr) + +/obj/machinery/recharge_station/ghost_pod_recharger + name = "drone pod" + desc = "This is a pod which used to contain a drone... Or maybe it still does?" + icon = 'icons/obj/structures.dmi' + +/obj/machinery/recharge_station/ghost_pod_recharger/update_icon() + ..() + if(stat & BROKEN) + icon_state = "borg_pod_closed" + desc = "It appears broken..." + return + + if(occupant) + if((stat & NOPOWER) && !has_cell_power()) + icon_state = "borg_pod_closed" + desc = "It appears to be unpowered..." + else + icon_state = "borg_pod_closed" + else + icon_state = "borg_pod_opened" + + if(icon_update_tick == 0) + build_overlays() diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 6b6fc6df6a7..9e9e8786468 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -1,291 +1,291 @@ -/******************** Requests Console ********************/ -/** Originally written by errorage, updated by: Carn, needs more work though. I just added some security fixes */ - -//Request Console Department Types -#define RC_ASSIST 1 //Request Assistance -#define RC_SUPPLY 2 //Request Supplies -#define RC_INFO 4 //Relay Info - -//Request Console Screens -#define RCS_MAINMENU 0 // Main menu -#define RCS_RQASSIST 1 // Request supplies -#define RCS_RQSUPPLY 2 // Request assistance -#define RCS_SENDINFO 3 // Relay information -#define RCS_SENTPASS 4 // Message sent successfully -#define RCS_SENTFAIL 5 // Message sent unsuccessfully -#define RCS_VIEWMSGS 6 // View messages -#define RCS_MESSAUTH 7 // Authentication before sending -#define RCS_ANNOUNCE 8 // Send announcement - -var/req_console_assistance = list() -var/req_console_supplies = list() -var/req_console_information = list() -var/list/obj/machinery/requests_console/allConsoles = list() - -/obj/machinery/requests_console - name = "requests console" - desc = "A console intended to send requests to different departments on the station." - anchored = TRUE - icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit - icon_state = "req_comp_0" - layer = ABOVE_WINDOW_LAYER - circuit = /obj/item/weapon/circuitboard/request - blocks_emissive = NONE - light_power = 0.25 - light_color = "#00ff00" - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department - var/list/message_log = list() //List of all messages - var/departmentType = 0 //Bitflag. Zero is reply-only. Map currently uses raw numbers instead of defines. - var/newmessagepriority = 0 - // 0 = no new message - // 1 = normal priority - // 2 = high priority - var/screen = RCS_VIEWMSGS - var/silent = 0 // set to 1 for it not to beep all the time -// var/hackState = 0 - // 0 = not hacked - // 1 = hacked - var/announcementConsole = 0 - // 0 = This console cannot be used to send department announcements - // 1 = This console can send department announcementsf - var/open = 0 // 1 if open - var/announceAuth = 0 //Will be set to 1 when you authenticate yourself for announcements - var/msgVerified = "" //Will contain the name of the person who varified it - var/msgStamped = "" //If a message is stamped, this will contain the stamp name - var/message = ""; - var/recipient = ""; //the department which will be receiving the message - var/priority = -1 ; //Priority of the message being sent - light_range = 0 - var/datum/announcement/announcement = new - -/obj/machinery/requests_console/Initialize() - . = ..() - - announcement.title = "[department] announcement" - announcement.newscast = 1 - - name = "[department] requests console" - allConsoles += src - if(departmentType & RC_ASSIST) - req_console_assistance |= department - if(departmentType & RC_SUPPLY) - req_console_supplies |= department - if(departmentType & RC_INFO) - req_console_information |= department - - update_icon() - -/obj/machinery/requests_console/Destroy() - allConsoles -= src - var/lastDeptRC = 1 - for (var/obj/machinery/requests_console/Console in allConsoles) - if(Console.department == department) - lastDeptRC = 0 - break - if(lastDeptRC) - if(departmentType & RC_ASSIST) - req_console_assistance -= department - if(departmentType & RC_SUPPLY) - req_console_supplies -= department - if(departmentType & RC_INFO) - req_console_information -= department - return ..() - -/obj/machinery/requests_console/power_change() - ..() - update_icon() - -/obj/machinery/requests_console/update_icon() - cut_overlays() - - if(stat & NOPOWER) - set_light(0) - set_light_on(FALSE) - icon_state = "req_comp_off" - else - icon_state = "req_comp_[newmessagepriority]" - add_overlay(mutable_appearance(icon, "req_comp_ov[newmessagepriority]")) - add_overlay(emissive_appearance(icon, "req_comp_ov[newmessagepriority]")) - set_light(2) - set_light_on(TRUE) - -/obj/machinery/requests_console/attack_hand(user as mob) - if(..(user)) - return - tgui_interact(user) - -/obj/machinery/requests_console/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "RequestConsole", "[department] Request Console") - ui.open() - -/obj/machinery/requests_console/tgui_data(mob/user) - var/list/data = ..() - data["department"] = department - data["screen"] = screen - data["message_log"] = message_log - data["newmessagepriority"] = newmessagepriority - data["silent"] = silent - data["announcementConsole"] = announcementConsole - - data["assist_dept"] = req_console_assistance - data["supply_dept"] = req_console_supplies - data["info_dept"] = req_console_information - - data["message"] = message - data["recipient"] = recipient - data["priority"] = priority - data["msgStamped"] = msgStamped - data["msgVerified"] = msgVerified - data["announceAuth"] = announceAuth - return data - -/obj/machinery/requests_console/tgui_act(action, list/params) - if(..()) - return TRUE - - add_fingerprint(usr) - - switch(action) - if("write") - if(reject_bad_text(params["write"])) - recipient = params["write"] //write contains the string of the receiving department's name - - var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) - if(new_message) - message = new_message - screen = RCS_MESSAUTH - switch(params["priority"]) - if(1) - priority = 1 - if(2) - priority = 2 - else - priority = 0 - else - reset_message(1) - . = TRUE - - if("writeAnnouncement") - var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) - if(new_message) - message = new_message - else - reset_message(1) - . = TRUE - - if("sendAnnouncement") - if(!announcementConsole) - return FALSE - announcement.Announce(message, msg_sanitized = 1) - reset_message(1) - . = TRUE - - if("department") - if(!message) - return FALSE - var/log_msg = message - var/pass = 0 - screen = RCS_SENTFAIL - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) - continue - MS.send_rc_message(ckey(params["department"]), department, log_msg, msgStamped, msgVerified, priority) - pass = 1 - if(pass) - screen = RCS_SENTPASS - message_log += list(list("Message sent to [recipient]", "[message]")) - else - audible_message(text("\icon[src][bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) - . = TRUE - - //Handle printing - if("print") - var/msg = message_log[text2num(params["print"])]; - if(msg) - msg = "[msg[1]]:
                    [msg[2]]" - msg = replacetext(msg, "
                    ", "\n") - msg = strip_html_properly(msg) - var/obj/item/weapon/paper/R = new(src.loc) - R.name = "[department] Message" - R.info = "

                    [department] Requests Console

                    [msg]
                    " - . = TRUE - - //Handle screen switching - if("setScreen") - var/tempScreen = text2num(params["setScreen"]) - if(tempScreen == RCS_ANNOUNCE && !announcementConsole) - return - if(tempScreen == RCS_VIEWMSGS) - for (var/obj/machinery/requests_console/Console in allConsoles) - if(Console.department == department) - Console.newmessagepriority = 0 - Console.update_icon() - if(tempScreen == RCS_MAINMENU) - reset_message() - screen = tempScreen - . = TRUE - - //Handle silencing the console - if("toggleSilent") - silent = !silent - . = TRUE - - //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messaging on that console (EXTREME priority...), but the code for that was removed. -/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob) - if(computer_deconstruction_screwdriver(user, O)) - return - if(istype(O, /obj/item/device/multitool)) - var/input = sanitize(tgui_input_text(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department)) - if(!input) - to_chat(usr, "No input found. Please hang up and try your call again.") - return - department = input - announcement.title = "[department] announcement" - announcement.newscast = 1 - - name = "[department] Requests Console" - allConsoles += src - if(departmentType & RC_ASSIST) - req_console_assistance |= department - if(departmentType & RC_SUPPLY) - req_console_supplies |= department - if(departmentType & RC_INFO) - req_console_information |= department - return - - if(istype(O, /obj/item/weapon/card/id)) - if(inoperable(MAINT)) return - if(screen == RCS_MESSAUTH) - var/obj/item/weapon/card/id/T = O - msgVerified = text("Verified by [T.registered_name] ([T.assignment])") - SStgui.update_uis(src) - if(screen == RCS_ANNOUNCE) - var/obj/item/weapon/card/id/ID = O - if(access_RC_announce in ID.GetAccess()) - announceAuth = 1 - announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name - else - reset_message() - to_chat(user, "You are not authorized to send announcements.") - SStgui.update_uis(src) - if(istype(O, /obj/item/weapon/stamp)) - if(inoperable(MAINT)) return - if(screen == RCS_MESSAUTH) - var/obj/item/weapon/stamp/T = O - msgStamped = text("Stamped with the [T.name]") - SStgui.update_uis(src) - return - -/obj/machinery/requests_console/proc/reset_message(var/mainmenu = 0) - message = "" - recipient = "" - priority = 0 - msgVerified = "" - msgStamped = "" - announceAuth = 0 - announcement.announcer = "" - if(mainmenu) - screen = RCS_MAINMENU +/******************** Requests Console ********************/ +/** Originally written by errorage, updated by: Carn, needs more work though. I just added some security fixes */ + +//Request Console Department Types +#define RC_ASSIST 1 //Request Assistance +#define RC_SUPPLY 2 //Request Supplies +#define RC_INFO 4 //Relay Info + +//Request Console Screens +#define RCS_MAINMENU 0 // Main menu +#define RCS_RQASSIST 1 // Request supplies +#define RCS_RQSUPPLY 2 // Request assistance +#define RCS_SENDINFO 3 // Relay information +#define RCS_SENTPASS 4 // Message sent successfully +#define RCS_SENTFAIL 5 // Message sent unsuccessfully +#define RCS_VIEWMSGS 6 // View messages +#define RCS_MESSAUTH 7 // Authentication before sending +#define RCS_ANNOUNCE 8 // Send announcement + +var/req_console_assistance = list() +var/req_console_supplies = list() +var/req_console_information = list() +var/list/obj/machinery/requests_console/allConsoles = list() + +/obj/machinery/requests_console + name = "requests console" + desc = "A console intended to send requests to different departments on the station." + anchored = TRUE + icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit + icon_state = "req_comp_0" + layer = ABOVE_WINDOW_LAYER + circuit = /obj/item/weapon/circuitboard/request + blocks_emissive = NONE + light_power = 0.25 + light_color = "#00ff00" + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department + var/list/message_log = list() //List of all messages + var/departmentType = 0 //Bitflag. Zero is reply-only. Map currently uses raw numbers instead of defines. + var/newmessagepriority = 0 + // 0 = no new message + // 1 = normal priority + // 2 = high priority + var/screen = RCS_VIEWMSGS + var/silent = 0 // set to 1 for it not to beep all the time +// var/hackState = 0 + // 0 = not hacked + // 1 = hacked + var/announcementConsole = 0 + // 0 = This console cannot be used to send department announcements + // 1 = This console can send department announcementsf + var/open = 0 // 1 if open + var/announceAuth = 0 //Will be set to 1 when you authenticate yourself for announcements + var/msgVerified = "" //Will contain the name of the person who varified it + var/msgStamped = "" //If a message is stamped, this will contain the stamp name + var/message = ""; + var/recipient = ""; //the department which will be receiving the message + var/priority = -1 ; //Priority of the message being sent + light_range = 0 + var/datum/announcement/announcement = new + +/obj/machinery/requests_console/Initialize() + . = ..() + + announcement.title = "[department] announcement" + announcement.newscast = 1 + + name = "[department] requests console" + allConsoles += src + if(departmentType & RC_ASSIST) + req_console_assistance |= department + if(departmentType & RC_SUPPLY) + req_console_supplies |= department + if(departmentType & RC_INFO) + req_console_information |= department + + update_icon() + +/obj/machinery/requests_console/Destroy() + allConsoles -= src + var/lastDeptRC = 1 + for (var/obj/machinery/requests_console/Console in allConsoles) + if(Console.department == department) + lastDeptRC = 0 + break + if(lastDeptRC) + if(departmentType & RC_ASSIST) + req_console_assistance -= department + if(departmentType & RC_SUPPLY) + req_console_supplies -= department + if(departmentType & RC_INFO) + req_console_information -= department + return ..() + +/obj/machinery/requests_console/power_change() + ..() + update_icon() + +/obj/machinery/requests_console/update_icon() + cut_overlays() + + if(stat & NOPOWER) + set_light(0) + set_light_on(FALSE) + icon_state = "req_comp_off" + else + icon_state = "req_comp_[newmessagepriority]" + add_overlay(mutable_appearance(icon, "req_comp_ov[newmessagepriority]")) + add_overlay(emissive_appearance(icon, "req_comp_ov[newmessagepriority]")) + set_light(2) + set_light_on(TRUE) + +/obj/machinery/requests_console/attack_hand(user as mob) + if(..(user)) + return + tgui_interact(user) + +/obj/machinery/requests_console/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RequestConsole", "[department] Request Console") + ui.open() + +/obj/machinery/requests_console/tgui_data(mob/user) + var/list/data = ..() + data["department"] = department + data["screen"] = screen + data["message_log"] = message_log + data["newmessagepriority"] = newmessagepriority + data["silent"] = silent + data["announcementConsole"] = announcementConsole + + data["assist_dept"] = req_console_assistance + data["supply_dept"] = req_console_supplies + data["info_dept"] = req_console_information + + data["message"] = message + data["recipient"] = recipient + data["priority"] = priority + data["msgStamped"] = msgStamped + data["msgVerified"] = msgVerified + data["announceAuth"] = announceAuth + return data + +/obj/machinery/requests_console/tgui_act(action, list/params) + if(..()) + return TRUE + + add_fingerprint(usr) + + switch(action) + if("write") + if(reject_bad_text(params["write"])) + recipient = params["write"] //write contains the string of the receiving department's name + + var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) + if(new_message) + message = new_message + screen = RCS_MESSAUTH + switch(params["priority"]) + if(1) + priority = 1 + if(2) + priority = 2 + else + priority = 0 + else + reset_message(1) + . = TRUE + + if("writeAnnouncement") + var/new_message = sanitize(tgui_input_text(usr, "Write your message:", "Awaiting Input", "")) + if(new_message) + message = new_message + else + reset_message(1) + . = TRUE + + if("sendAnnouncement") + if(!announcementConsole) + return FALSE + announcement.Announce(message, msg_sanitized = 1) + reset_message(1) + . = TRUE + + if("department") + if(!message) + return FALSE + var/log_msg = message + var/pass = 0 + screen = RCS_SENTFAIL + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) + continue + MS.send_rc_message(ckey(params["department"]), department, log_msg, msgStamped, msgVerified, priority) + pass = 1 + if(pass) + screen = RCS_SENTPASS + message_log += list(list("Message sent to [recipient]", "[message]")) + else + audible_message(text("\icon[src][bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) + . = TRUE + + //Handle printing + if("print") + var/msg = message_log[text2num(params["print"])]; + if(msg) + msg = "[msg[1]]:
                    [msg[2]]" + msg = replacetext(msg, "
                    ", "\n") + msg = strip_html_properly(msg) + var/obj/item/weapon/paper/R = new(src.loc) + R.name = "[department] Message" + R.info = "

                    [department] Requests Console

                    [msg]
                    " + . = TRUE + + //Handle screen switching + if("setScreen") + var/tempScreen = text2num(params["setScreen"]) + if(tempScreen == RCS_ANNOUNCE && !announcementConsole) + return + if(tempScreen == RCS_VIEWMSGS) + for (var/obj/machinery/requests_console/Console in allConsoles) + if(Console.department == department) + Console.newmessagepriority = 0 + Console.update_icon() + if(tempScreen == RCS_MAINMENU) + reset_message() + screen = tempScreen + . = TRUE + + //Handle silencing the console + if("toggleSilent") + silent = !silent + . = TRUE + + //err... hacking code, which has no reason for existing... but anyway... it was once supposed to unlock priority 3 messaging on that console (EXTREME priority...), but the code for that was removed. +/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob) + if(computer_deconstruction_screwdriver(user, O)) + return + if(istype(O, /obj/item/device/multitool)) + var/input = sanitize(tgui_input_text(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department)) + if(!input) + to_chat(usr, "No input found. Please hang up and try your call again.") + return + department = input + announcement.title = "[department] announcement" + announcement.newscast = 1 + + name = "[department] Requests Console" + allConsoles += src + if(departmentType & RC_ASSIST) + req_console_assistance |= department + if(departmentType & RC_SUPPLY) + req_console_supplies |= department + if(departmentType & RC_INFO) + req_console_information |= department + return + + if(istype(O, /obj/item/weapon/card/id)) + if(inoperable(MAINT)) return + if(screen == RCS_MESSAUTH) + var/obj/item/weapon/card/id/T = O + msgVerified = text("Verified by [T.registered_name] ([T.assignment])") + SStgui.update_uis(src) + if(screen == RCS_ANNOUNCE) + var/obj/item/weapon/card/id/ID = O + if(access_RC_announce in ID.GetAccess()) + announceAuth = 1 + announcement.announcer = ID.assignment ? "[ID.assignment] [ID.registered_name]" : ID.registered_name + else + reset_message() + to_chat(user, "You are not authorized to send announcements.") + SStgui.update_uis(src) + if(istype(O, /obj/item/weapon/stamp)) + if(inoperable(MAINT)) return + if(screen == RCS_MESSAUTH) + var/obj/item/weapon/stamp/T = O + msgStamped = text("Stamped with the [T.name]") + SStgui.update_uis(src) + return + +/obj/machinery/requests_console/proc/reset_message(var/mainmenu = 0) + message = "" + recipient = "" + priority = 0 + msgVerified = "" + msgStamped = "" + announceAuth = 0 + announcement.announcer = "" + if(mainmenu) + screen = RCS_MAINMENU diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm index 103ea5f7ae2..91b0aa19bac 100644 --- a/code/game/machinery/robot_fabricator.dm +++ b/code/game/machinery/robot_fabricator.dm @@ -1,138 +1,138 @@ -/obj/machinery/robotic_fabricator - name = "robotic fabricator" - icon = 'icons/obj/robotics.dmi' - icon_state = "fab-idle" - density = TRUE - anchored = TRUE - var/metal_amount = 0 - var/operating = 0 - var/obj/item/robot_parts/being_built = null - use_power = USE_POWER_IDLE - idle_power_usage = 40 - active_power_usage = 10000 - -/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/stack/material) && O.get_material_name() == MAT_STEEL) - var/obj/item/stack/M = O - if(metal_amount < 150000.0) - var/count = 0 - add_overlay("fab-load-metal") - spawn(15) - if(M) - if(!M.get_amount()) - return - while(metal_amount < 150000 && M.get_amount()) - metal_amount += O.matter[MAT_STEEL] /*O:height * O:width * O:length * 100000.0*/ - M.use(1) - count++ - - to_chat(user, "You insert [count] metal sheet\s into the fabricator.") - cut_overlay("fab-load-metal") - updateDialog() - else - to_chat(user, "The robot part maker is full. Please remove metal from the robot part maker in order to insert more.") - -/obj/machinery/robotic_fabricator/attack_hand(user as mob) - var/dat - if(..()) - return - - if(operating) - dat = {" -Building [being_built.name].
                    -Please wait until completion...

                    -
                    -"} - else - dat = {" -Metal Amount: [min(150000, metal_amount)] cm3 (MAX: 150,000)

                    -
                    -Left Arm (25,000 cc metal.)
                    -
                    Right Arm (25,000 cc metal.)
                    -
                    Left Leg (25,000 cc metal.)
                    -
                    Right Leg (25,000 cc metal).
                    -
                    Chest (50,000 cc metal).
                    -
                    Head (50,000 cc metal).
                    -
                    Robot Frame (75,000 cc metal).
                    -"} - - user << browse("Robotic Fabricator Control Panel[dat]", "window=robot_fabricator") - onclose(user, "robot_fabricator") - return - -/obj/machinery/robotic_fabricator/Topic(href, href_list) - if(..()) - return - - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["make"]) - if(!operating) - var/part_type = text2num(href_list["make"]) - - var/build_type = "" - var/build_time = 200 - var/build_cost = 25000 - - switch (part_type) - if(1) - build_type = "/obj/item/robot_parts/l_arm" - build_time = 200 - build_cost = 25000 - - if(2) - build_type = "/obj/item/robot_parts/r_arm" - build_time = 200 - build_cost = 25000 - - if(3) - build_type = "/obj/item/robot_parts/l_leg" - build_time = 200 - build_cost = 25000 - - if(4) - build_type = "/obj/item/robot_parts/r_leg" - build_time = 200 - build_cost = 25000 - - if(5) - build_type = "/obj/item/robot_parts/chest" - build_time = 350 - build_cost = 50000 - - if(6) - build_type = "/obj/item/robot_parts/head" - build_time = 350 - build_cost = 50000 - - if(7) - build_type = "/obj/item/robot_parts/robot_suit" - build_time = 600 - build_cost = 75000 - - var/building = text2path(build_type) - if(!isnull(building)) - if(metal_amount >= build_cost) - operating = 1 - update_use_power(USE_POWER_ACTIVE) - - metal_amount = max(0, metal_amount - build_cost) - - being_built = new building(src) - - add_overlay("fab-active") - updateUsrDialog() - - spawn (build_time) - if(!isnull(being_built)) - being_built.loc = get_turf(src) - being_built = null - update_use_power(USE_POWER_IDLE) - operating = 0 - cut_overlay("fab-active") - return - - for (var/mob/M in viewers(1, src)) - if(M.client && M.machine == src) - attack_hand(M) +/obj/machinery/robotic_fabricator + name = "robotic fabricator" + icon = 'icons/obj/robotics.dmi' + icon_state = "fab-idle" + density = TRUE + anchored = TRUE + var/metal_amount = 0 + var/operating = 0 + var/obj/item/robot_parts/being_built = null + use_power = USE_POWER_IDLE + idle_power_usage = 40 + active_power_usage = 10000 + +/obj/machinery/robotic_fabricator/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/stack/material) && O.get_material_name() == MAT_STEEL) + var/obj/item/stack/M = O + if(metal_amount < 150000.0) + var/count = 0 + add_overlay("fab-load-metal") + spawn(15) + if(M) + if(!M.get_amount()) + return + while(metal_amount < 150000 && M.get_amount()) + metal_amount += O.matter[MAT_STEEL] /*O:height * O:width * O:length * 100000.0*/ + M.use(1) + count++ + + to_chat(user, "You insert [count] metal sheet\s into the fabricator.") + cut_overlay("fab-load-metal") + updateDialog() + else + to_chat(user, "The robot part maker is full. Please remove metal from the robot part maker in order to insert more.") + +/obj/machinery/robotic_fabricator/attack_hand(user as mob) + var/dat + if(..()) + return + + if(operating) + dat = {" +Building [being_built.name].
                    +Please wait until completion...

                    +
                    +"} + else + dat = {" +Metal Amount: [min(150000, metal_amount)] cm3 (MAX: 150,000)

                    +
                    +
                    Left Arm (25,000 cc metal.)
                    +
                    Right Arm (25,000 cc metal.)
                    +
                    Left Leg (25,000 cc metal.)
                    +
                    Right Leg (25,000 cc metal).
                    +
                    Chest (50,000 cc metal).
                    +
                    Head (50,000 cc metal).
                    +
                    Robot Frame (75,000 cc metal).
                    +"} + + user << browse("Robotic Fabricator Control Panel[dat]", "window=robot_fabricator") + onclose(user, "robot_fabricator") + return + +/obj/machinery/robotic_fabricator/Topic(href, href_list) + if(..()) + return + + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["make"]) + if(!operating) + var/part_type = text2num(href_list["make"]) + + var/build_type = "" + var/build_time = 200 + var/build_cost = 25000 + + switch (part_type) + if(1) + build_type = "/obj/item/robot_parts/l_arm" + build_time = 200 + build_cost = 25000 + + if(2) + build_type = "/obj/item/robot_parts/r_arm" + build_time = 200 + build_cost = 25000 + + if(3) + build_type = "/obj/item/robot_parts/l_leg" + build_time = 200 + build_cost = 25000 + + if(4) + build_type = "/obj/item/robot_parts/r_leg" + build_time = 200 + build_cost = 25000 + + if(5) + build_type = "/obj/item/robot_parts/chest" + build_time = 350 + build_cost = 50000 + + if(6) + build_type = "/obj/item/robot_parts/head" + build_time = 350 + build_cost = 50000 + + if(7) + build_type = "/obj/item/robot_parts/robot_suit" + build_time = 600 + build_cost = 75000 + + var/building = text2path(build_type) + if(!isnull(building)) + if(metal_amount >= build_cost) + operating = 1 + update_use_power(USE_POWER_ACTIVE) + + metal_amount = max(0, metal_amount - build_cost) + + being_built = new building(src) + + add_overlay("fab-active") + updateUsrDialog() + + spawn (build_time) + if(!isnull(being_built)) + being_built.loc = get_turf(src) + being_built = null + update_use_power(USE_POWER_IDLE) + operating = 0 + cut_overlay("fab-active") + return + + for (var/mob/M in viewers(1, src)) + if(M.client && M.machine == src) + attack_hand(M) diff --git a/code/game/machinery/seed_extractor.dm b/code/game/machinery/seed_extractor.dm index 4f29a3e347e..77cf8d1509a 100644 --- a/code/game/machinery/seed_extractor.dm +++ b/code/game/machinery/seed_extractor.dm @@ -1,51 +1,51 @@ -/obj/machinery/seed_extractor - name = "seed extractor" - desc = "Extracts and bags seeds from produce." - icon = 'icons/obj/hydroponics_machines_vr.dmi' //VOREStation Edit - icon_state = "sextractor" - density = TRUE - anchored = TRUE - -/obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob) - - // Fruits and vegetables. - if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown) || istype(O, /obj/item/weapon/grown)) - - user.remove_from_mob(O) - - var/datum/seed/new_seed_type - if(istype(O, /obj/item/weapon/grown)) - var/obj/item/weapon/grown/F = O - new_seed_type = SSplants.seeds[F.plantname] - else - var/obj/item/weapon/reagent_containers/food/snacks/grown/F = O - new_seed_type = SSplants.seeds[F.plantname] - - if(new_seed_type) - to_chat(user, "You extract some seeds from [O].") - var/produce = rand(1,4) - for(var/i = 0;i<=produce;i++) - var/obj/item/seeds/seeds = new(get_turf(src)) - seeds.seed_type = new_seed_type.name - seeds.update_seed() - else - to_chat(user, "[O] doesn't seem to have any usable seeds inside it.") - - qdel(O) - - //Grass. - else if(istype(O, /obj/item/stack/tile/grass)) - var/obj/item/stack/tile/grass/S = O - if(S.use(1)) - to_chat(user, "You extract some seeds from the grass tile.") - new /obj/item/seeds/grassseed(loc) - - else if(istype(O, /obj/item/weapon/fossil/plant)) // Fossils - var/obj/item/seeds/random/R = new(get_turf(src)) - to_chat(user, "\The [src] pulverizes \the [O] and spits out \the [R].") - qdel(O) - - else if(default_unfasten_wrench(user, O, 20)) - return - +/obj/machinery/seed_extractor + name = "seed extractor" + desc = "Extracts and bags seeds from produce." + icon = 'icons/obj/hydroponics_machines_vr.dmi' //VOREStation Edit + icon_state = "sextractor" + density = TRUE + anchored = TRUE + +/obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob) + + // Fruits and vegetables. + if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown) || istype(O, /obj/item/weapon/grown)) + + user.remove_from_mob(O) + + var/datum/seed/new_seed_type + if(istype(O, /obj/item/weapon/grown)) + var/obj/item/weapon/grown/F = O + new_seed_type = SSplants.seeds[F.plantname] + else + var/obj/item/weapon/reagent_containers/food/snacks/grown/F = O + new_seed_type = SSplants.seeds[F.plantname] + + if(new_seed_type) + to_chat(user, "You extract some seeds from [O].") + var/produce = rand(1,4) + for(var/i = 0;i<=produce;i++) + var/obj/item/seeds/seeds = new(get_turf(src)) + seeds.seed_type = new_seed_type.name + seeds.update_seed() + else + to_chat(user, "[O] doesn't seem to have any usable seeds inside it.") + + qdel(O) + + //Grass. + else if(istype(O, /obj/item/stack/tile/grass)) + var/obj/item/stack/tile/grass/S = O + if(S.use(1)) + to_chat(user, "You extract some seeds from the grass tile.") + new /obj/item/seeds/grassseed(loc) + + else if(istype(O, /obj/item/weapon/fossil/plant)) // Fossils + var/obj/item/seeds/random/R = new(get_turf(src)) + to_chat(user, "\The [src] pulverizes \the [O] and spits out \the [R].") + qdel(O) + + else if(default_unfasten_wrench(user, O, 20)) + return + return \ No newline at end of file diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index ac0615635a1..f5505ba9da4 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -10,12 +10,12 @@ icon_state = "sheater0" name = "space heater" desc = "Made by Space Amish using traditional space techniques, this heater is guaranteed not to set the station on fire." - + light_system = MOVABLE_LIGHT light_range = 3 light_power = 1 light_on = FALSE - + var/obj/item/weapon/cell/cell var/cell_type = /obj/item/weapon/cell/high var/state = 0 @@ -90,7 +90,7 @@ else to_chat(user, "The hatch must be open to insert a power cell.") return - else if(I.is_screwdriver()) + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) panel_open = !panel_open playsound(src, I.usesound, 50, 1) user.visible_message("[user] [panel_open ? "opens" : "closes"] the hatch on the [src].", "You [panel_open ? "open" : "close"] the hatch on the [src].") diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index d1db08374a6..80d28b7e3d3 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -1,269 +1,269 @@ -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Arial Black" -#define SCROLL_SPEED 2 - -// Status display -// (formerly Countdown timer display) - -// Use to show shuttle ETA/ETD times -// Alert status -// And arbitrary messages set by comms computer -/obj/machinery/status_display - icon = 'icons/obj/status_display.dmi' - icon_state = "frame" - plane = TURF_PLANE - layer = ABOVE_WINDOW_LAYER - name = "status display" - anchored = TRUE - density = FALSE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 10 - circuit = /obj/item/weapon/circuitboard/status_display - var/mode = 1 // 0 = Blank - // 1 = Shuttle timer - // 2 = Arbitrary message(s) - // 3 = alert picture - // 4 = Supply shuttle timer - - var/picture_state // icon_state of alert picture - var/message1 = "" // message line 1 - var/message2 = "" // message line 2 - var/index1 // display index for scrolling messages or 0 if non-scrolling - var/index2 - var/picture = null - - var/frequency = 1435 // radio frequency - - var/friendc = 0 // track if Friend Computer mode - var/ignore_friendc = 0 - - maptext_height = 26 - maptext_width = 32 - - var/const/CHARS_PER_LINE = 5 - var/const/STATUS_DISPLAY_BLANK = 0 - var/const/STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME = 1 - var/const/STATUS_DISPLAY_MESSAGE = 2 - var/const/STATUS_DISPLAY_ALERT = 3 - var/const/STATUS_DISPLAY_TIME = 4 - var/const/STATUS_DISPLAY_CUSTOM = 99 - - var/seclevel = "green" - -/obj/machinery/status_display/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - return ..() - -/obj/machinery/status_display/attackby(I as obj, user as mob) - if(computer_deconstruction_screwdriver(user, I)) - return - else - attack_hand(user) - return - -// register for radio system -/obj/machinery/status_display/Initialize() - . = ..() - if(radio_controller) - radio_controller.add_object(src, frequency) - -// timed process -/obj/machinery/status_display/process() - if(stat & NOPOWER) - remove_display() - return - update() - -/obj/machinery/status_display/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - set_picture("ai_bsod") - ..(severity) - -// set what is displayed -/obj/machinery/status_display/proc/update() - remove_display() - if(friendc && !ignore_friendc) - set_picture("ai_friend") - return 1 - - switch(mode) - if(STATUS_DISPLAY_BLANK) //blank - return 1 - if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer - if(!emergency_shuttle) - message1 = "-ETA-" - message2 = "Never" // You're here forever. - return 1 - if(emergency_shuttle.waiting_to_leave()) - message1 = "-ETD-" - if(emergency_shuttle.shuttle.is_launching()) - message2 = "Launch" - else - message2 = get_shuttle_timer_departure() - if(length(message2) > CHARS_PER_LINE) - message2 = "Error" - update_display(message1, message2) - else if(emergency_shuttle.has_eta()) - message1 = "-ETA-" - message2 = get_shuttle_timer_arrival() - if(length(message2) > CHARS_PER_LINE) - message2 = "Error" - update_display(message1, message2) - return 1 - if(STATUS_DISPLAY_MESSAGE) //custom messages - var/line1 - var/line2 - - if(!index1) - line1 = message1 - else - line1 = copytext(message1+"|"+message1, index1, index1+CHARS_PER_LINE) - var/message1_len = length(message1) - index1 += SCROLL_SPEED - if(index1 > message1_len) - index1 -= message1_len - - if(!index2) - line2 = message2 - else - line2 = copytext(message2+"|"+message2, index2, index2+CHARS_PER_LINE) - var/message2_len = length(message2) - index2 += SCROLL_SPEED - if(index2 > message2_len) - index2 -= message2_len - update_display(line1, line2) - return 1 - if(STATUS_DISPLAY_ALERT) - display_alert(seclevel) - return 1 - if(STATUS_DISPLAY_TIME) - message1 = "TIME" - message2 = stationtime2text() - update_display(message1, message2) - return 1 - return 0 - -/obj/machinery/status_display/examine(mob/user) - . = ..() - if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT) - . += "The display says:
                    \t[sanitize(message1)]
                    \t[sanitize(message2)]" - -/obj/machinery/status_display/proc/set_message(m1, m2) - if(m1) - index1 = (length(m1) > CHARS_PER_LINE) - message1 = m1 - else - message1 = "" - index1 = 0 - - if(m2) - index2 = (length(m2) > CHARS_PER_LINE) - message2 = m2 - else - message2 = "" - index2 = 0 - -/obj/machinery/status_display/proc/display_alert(var/newlevel) - remove_display() - if(seclevel != newlevel) - seclevel = newlevel - switch(seclevel) - if("green") set_light(l_range = 2, l_power = 0.25, l_color = "#00ff00") - if("yellow") set_light(l_range = 2, l_power = 0.25, l_color = "#ffff00") - if("violet") set_light(l_range = 2, l_power = 0.25, l_color = "#9933ff") - if("orange") set_light(l_range = 2, l_power = 0.25, l_color = "#ff9900") - if("blue") set_light(l_range = 2, l_power = 0.25, l_color = "#1024A9") - if("red") set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") - if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") - set_picture("status_display_[seclevel]") - -// Called when the alert level is changed. -/obj/machinery/status_display/proc/on_alert_changed(new_level) - // On most alerts, this will change to a flashing alert picture in a specific color. - // Doing that for green alert automatically doesn't really make sense, but it is still available on the comm consoles/PDAs. - if(seclevel2num(new_level) == SEC_LEVEL_GREEN) - mode = STATUS_DISPLAY_TIME - set_light(0) // Remove any glow we had from the alert previously. - update() - return - mode = STATUS_DISPLAY_ALERT - display_alert(new_level) - -/obj/machinery/status_display/proc/set_picture(state) - remove_display() - if(!picture || picture_state != state) - picture_state = state - picture = image('icons/obj/status_display.dmi', icon_state=picture_state) - add_overlay(picture) - -/obj/machinery/status_display/proc/update_display(line1, line2) - var/new_text = {"
                    [line1]
                    [line2]
                    "} - if(maptext != new_text) - maptext = new_text - -/obj/machinery/status_display/proc/get_shuttle_timer_arrival() - if(!emergency_shuttle) - return "Error" - var/timeleft = emergency_shuttle.estimate_arrival_time() - if(timeleft < 0) - return "" - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - -/obj/machinery/status_display/proc/get_shuttle_timer_departure() - if(!emergency_shuttle) - return "Error" - var/timeleft = emergency_shuttle.estimate_launch_time() - if(timeleft < 0) - return "" - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - -/obj/machinery/status_display/proc/get_supply_shuttle_timer() - var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle - if(!shuttle) - return "Error" - - if(shuttle.has_arrive_time()) - var/timeleft = round((shuttle.arrive_time - world.time) / 10,1) - if(timeleft < 0) - return "Late" - return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - return "" - -/obj/machinery/status_display/proc/remove_display() - cut_overlays() - if(maptext) - maptext = "" - -/obj/machinery/status_display/receive_signal(datum/signal/signal) - switch(signal.data["command"]) - if("blank") - mode = STATUS_DISPLAY_BLANK - set_light(0) - - if("shuttle") - mode = STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME - set_light(0) - - if("message") - mode = STATUS_DISPLAY_MESSAGE - set_message(signal.data["msg1"], signal.data["msg2"]) - set_light(0) - - if("alert") - mode = STATUS_DISPLAY_ALERT - set_picture(signal.data["picture_state"]) - - if("time") - mode = STATUS_DISPLAY_TIME - set_light(0) - update() - -#undef FONT_SIZE -#undef FONT_COLOR -#undef FONT_STYLE -#undef SCROLL_SPEED +#define FONT_SIZE "5pt" +#define FONT_COLOR "#09f" +#define FONT_STYLE "Arial Black" +#define SCROLL_SPEED 2 + +// Status display +// (formerly Countdown timer display) + +// Use to show shuttle ETA/ETD times +// Alert status +// And arbitrary messages set by comms computer +/obj/machinery/status_display + icon = 'icons/obj/status_display.dmi' + icon_state = "frame" + plane = TURF_PLANE + layer = ABOVE_WINDOW_LAYER + name = "status display" + anchored = TRUE + density = FALSE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 10 + circuit = /obj/item/weapon/circuitboard/status_display + var/mode = 1 // 0 = Blank + // 1 = Shuttle timer + // 2 = Arbitrary message(s) + // 3 = alert picture + // 4 = Supply shuttle timer + + var/picture_state // icon_state of alert picture + var/message1 = "" // message line 1 + var/message2 = "" // message line 2 + var/index1 // display index for scrolling messages or 0 if non-scrolling + var/index2 + var/picture = null + + var/frequency = 1435 // radio frequency + + var/friendc = 0 // track if Friend Computer mode + var/ignore_friendc = 0 + + maptext_height = 26 + maptext_width = 32 + + var/const/CHARS_PER_LINE = 5 + var/const/STATUS_DISPLAY_BLANK = 0 + var/const/STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME = 1 + var/const/STATUS_DISPLAY_MESSAGE = 2 + var/const/STATUS_DISPLAY_ALERT = 3 + var/const/STATUS_DISPLAY_TIME = 4 + var/const/STATUS_DISPLAY_CUSTOM = 99 + + var/seclevel = "green" + +/obj/machinery/status_display/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + return ..() + +/obj/machinery/status_display/attackby(I as obj, user as mob) + if(computer_deconstruction_screwdriver(user, I)) + return + else + attack_hand(user) + return + +// register for radio system +/obj/machinery/status_display/Initialize() + . = ..() + if(radio_controller) + radio_controller.add_object(src, frequency) + +// timed process +/obj/machinery/status_display/process() + if(stat & NOPOWER) + remove_display() + return + update() + +/obj/machinery/status_display/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + set_picture("ai_bsod") + ..(severity) + +// set what is displayed +/obj/machinery/status_display/proc/update() + remove_display() + if(friendc && !ignore_friendc) + set_picture("ai_friend") + return 1 + + switch(mode) + if(STATUS_DISPLAY_BLANK) //blank + return 1 + if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer + if(!emergency_shuttle) + message1 = "-ETA-" + message2 = "Never" // You're here forever. + return 1 + if(emergency_shuttle.waiting_to_leave()) + message1 = "-ETD-" + if(emergency_shuttle.shuttle.is_launching()) + message2 = "Launch" + else + message2 = get_shuttle_timer_departure() + if(length(message2) > CHARS_PER_LINE) + message2 = "Error" + update_display(message1, message2) + else if(emergency_shuttle.has_eta()) + message1 = "-ETA-" + message2 = get_shuttle_timer_arrival() + if(length(message2) > CHARS_PER_LINE) + message2 = "Error" + update_display(message1, message2) + return 1 + if(STATUS_DISPLAY_MESSAGE) //custom messages + var/line1 + var/line2 + + if(!index1) + line1 = message1 + else + line1 = copytext(message1+"|"+message1, index1, index1+CHARS_PER_LINE) + var/message1_len = length(message1) + index1 += SCROLL_SPEED + if(index1 > message1_len) + index1 -= message1_len + + if(!index2) + line2 = message2 + else + line2 = copytext(message2+"|"+message2, index2, index2+CHARS_PER_LINE) + var/message2_len = length(message2) + index2 += SCROLL_SPEED + if(index2 > message2_len) + index2 -= message2_len + update_display(line1, line2) + return 1 + if(STATUS_DISPLAY_ALERT) + display_alert(seclevel) + return 1 + if(STATUS_DISPLAY_TIME) + message1 = "TIME" + message2 = stationtime2text() + update_display(message1, message2) + return 1 + return 0 + +/obj/machinery/status_display/examine(mob/user) + . = ..() + if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT) + . += "The display says:
                    \t[sanitize(message1)]
                    \t[sanitize(message2)]" + +/obj/machinery/status_display/proc/set_message(m1, m2) + if(m1) + index1 = (length(m1) > CHARS_PER_LINE) + message1 = m1 + else + message1 = "" + index1 = 0 + + if(m2) + index2 = (length(m2) > CHARS_PER_LINE) + message2 = m2 + else + message2 = "" + index2 = 0 + +/obj/machinery/status_display/proc/display_alert(var/newlevel) + remove_display() + if(seclevel != newlevel) + seclevel = newlevel + switch(seclevel) + if("green") set_light(l_range = 2, l_power = 0.25, l_color = "#00ff00") + if("yellow") set_light(l_range = 2, l_power = 0.25, l_color = "#ffff00") + if("violet") set_light(l_range = 2, l_power = 0.25, l_color = "#9933ff") + if("orange") set_light(l_range = 2, l_power = 0.25, l_color = "#ff9900") + if("blue") set_light(l_range = 2, l_power = 0.25, l_color = "#1024A9") + if("red") set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") + if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") + set_picture("status_display_[seclevel]") + +// Called when the alert level is changed. +/obj/machinery/status_display/proc/on_alert_changed(new_level) + // On most alerts, this will change to a flashing alert picture in a specific color. + // Doing that for green alert automatically doesn't really make sense, but it is still available on the comm consoles/PDAs. + if(seclevel2num(new_level) == SEC_LEVEL_GREEN) + mode = STATUS_DISPLAY_TIME + set_light(0) // Remove any glow we had from the alert previously. + update() + return + mode = STATUS_DISPLAY_ALERT + display_alert(new_level) + +/obj/machinery/status_display/proc/set_picture(state) + remove_display() + if(!picture || picture_state != state) + picture_state = state + picture = image('icons/obj/status_display.dmi', icon_state=picture_state) + add_overlay(picture) + +/obj/machinery/status_display/proc/update_display(line1, line2) + var/new_text = {"
                    [line1]
                    [line2]
                    "} + if(maptext != new_text) + maptext = new_text + +/obj/machinery/status_display/proc/get_shuttle_timer_arrival() + if(!emergency_shuttle) + return "Error" + var/timeleft = emergency_shuttle.estimate_arrival_time() + if(timeleft < 0) + return "" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + +/obj/machinery/status_display/proc/get_shuttle_timer_departure() + if(!emergency_shuttle) + return "Error" + var/timeleft = emergency_shuttle.estimate_launch_time() + if(timeleft < 0) + return "" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + +/obj/machinery/status_display/proc/get_supply_shuttle_timer() + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle + if(!shuttle) + return "Error" + + if(shuttle.has_arrive_time()) + var/timeleft = round((shuttle.arrive_time - world.time) / 10,1) + if(timeleft < 0) + return "Late" + return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + return "" + +/obj/machinery/status_display/proc/remove_display() + cut_overlays() + if(maptext) + maptext = "" + +/obj/machinery/status_display/receive_signal(datum/signal/signal) + switch(signal.data["command"]) + if("blank") + mode = STATUS_DISPLAY_BLANK + set_light(0) + + if("shuttle") + mode = STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME + set_light(0) + + if("message") + mode = STATUS_DISPLAY_MESSAGE + set_message(signal.data["msg1"], signal.data["msg2"]) + set_light(0) + + if("alert") + mode = STATUS_DISPLAY_ALERT + set_picture(signal.data["picture_state"]) + + if("time") + mode = STATUS_DISPLAY_TIME + set_light(0) + update() + +#undef FONT_SIZE +#undef FONT_COLOR +#undef FONT_STYLE +#undef SCROLL_SPEED diff --git a/code/game/machinery/suit_storage/suit_cycler.dm b/code/game/machinery/suit_storage/suit_cycler.dm index 56a081737fe..cdd6b45dc29 100644 --- a/code/game/machinery/suit_storage/suit_cycler.dm +++ b/code/game/machinery/suit_storage/suit_cycler.dm @@ -141,7 +141,7 @@ GLOBAL_LIST_EMPTY(suit_cycler_typecache) return //Hacking init. - if(istype(I, /obj/item/device/multitool) || I.is_wirecutter()) + if(istype(I, /obj/item/device/multitool) || I.has_tool_quality(TOOL_WIRECUTTER)) if(panel_open) attack_hand(user) return @@ -177,7 +177,7 @@ GLOBAL_LIST_EMPTY(suit_cycler_typecache) updateUsrDialog() return - else if(I.is_screwdriver()) + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) panel_open = !panel_open playsound(src, I.usesound, 50, 1) diff --git a/code/game/machinery/suit_storage/suit_storage.dm b/code/game/machinery/suit_storage/suit_storage.dm index 334f5227d8b..021b050c5f8 100644 --- a/code/game/machinery/suit_storage/suit_storage.dm +++ b/code/game/machinery/suit_storage/suit_storage.dm @@ -229,7 +229,7 @@ /obj/machinery/suit_storage_unit/proc/toggle_open(mob/user as mob) if(islocked || isUV) - to_chat(user, "Unable to open unit.") + to_chat(user, span_red("Unable to open unit.")) return if(OCCUPANT) eject_occupant(user) @@ -240,7 +240,7 @@ /obj/machinery/suit_storage_unit/proc/toggle_lock(mob/user as mob) if(OCCUPANT && safetieson) - to_chat(user, "The Unit's safety protocols disallow locking when a biological form is detected inside its compartments.") + to_chat(user, span_red("The Unit's safety protocols disallow locking when a biological form is detected inside its compartments.")) return if(isopen) return @@ -252,10 +252,10 @@ if(isUV || isopen) //I'm bored of all these sanity checks return if(OCCUPANT && safetieson) - to_chat(user, "WARNING: Biological entity detected in the confines of the Unit's storage. Cannot initiate cycle.") + to_chat(user, span_red("WARNING: Biological entity detected in the confines of the Unit's storage. Cannot initiate cycle.")) return if(!HELMET && !MASK && !SUIT && !OCCUPANT) //shit's empty yo - to_chat(user, "Unit storage bays empty. Nothing to disinfect -- Aborting.") + to_chat(user, span_red("Unit storage bays empty. Nothing to disinfect -- Aborting.")) return to_chat(user, "You start the Unit's cauterisation cycle.") cycletime_left = 20 @@ -295,7 +295,7 @@ SUIT = null if(MASK) MASK = null - visible_message("With a loud whining noise, the Suit Storage Unit's door grinds open. Puffs of ashen smoke come out of its chamber.", 3) + visible_message(span_red("With a loud whining noise, the Suit Storage Unit's door grinds open. Puffs of ashen smoke come out of its chamber."), 3) isbroken = 1 isopen = 1 islocked = 0 @@ -320,9 +320,9 @@ if(OCCUPANT.client) if(user != OCCUPANT) - to_chat(OCCUPANT, "The machine kicks you out!") + to_chat(OCCUPANT, span_blue("The machine kicks you out!")) if(user.loc != src.loc) - to_chat(OCCUPANT, "You leave the not-so-cozy confines of the SSU.") + to_chat(OCCUPANT, span_blue("You leave the not-so-cozy confines of the SSU.")) OCCUPANT.client.eye = OCCUPANT.client.mob OCCUPANT.client.perspective = MOB_PERSPECTIVE @@ -356,13 +356,13 @@ if(usr.stat != 0) return if(!isopen) - to_chat(usr, "The unit's doors are shut.") + to_chat(usr, span_red("The unit's doors are shut.")) return if(!ispowered || isbroken) - to_chat(usr, "The unit is not operational.") + to_chat(usr, span_red("The unit is not operational.")) return if((OCCUPANT) || (HELMET) || (SUIT)) - to_chat(usr, "It's too cluttered inside for you to fit in!") + to_chat(usr, span_red("It's too cluttered inside for you to fit in!")) return visible_message("[usr] starts squeezing into the suit storage unit!", 3) if(do_after(usr, 10)) @@ -385,10 +385,10 @@ /obj/machinery/suit_storage_unit/attackby(obj/item/I as obj, mob/user as mob) if(!ispowered) return - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) panelopen = !panelopen playsound(src, I.usesound, 100, 1) - to_chat(user, "You [panelopen ? "open up" : "close"] the unit's maintenance panel.") + to_chat(user, span_blue("You [panelopen ? "open up" : "close"] the unit's maintenance panel.")) updateUsrDialog() return if(istype(I, /obj/item/weapon/grab)) @@ -396,13 +396,13 @@ if(!(ismob(G.affecting))) return if(!isopen) - to_chat(user, "The unit's doors are shut.") + to_chat(user, span_red("The unit's doors are shut.")) return if(!ispowered || isbroken) - to_chat(user, "The unit is not operational.") + to_chat(user, span_red("The unit is not operational.")) return if((OCCUPANT) || (HELMET) || (SUIT)) //Unit needs to be absolutely empty - to_chat(user, "The unit's storage area is too cluttered.") + to_chat(user, span_red("The unit's storage area is too cluttered.")) return visible_message("[user] starts putting [G.affecting.name] into the Suit Storage Unit.", 3) if(do_after(user, 20)) @@ -426,7 +426,7 @@ return var/obj/item/clothing/suit/space/S = I if(SUIT) - to_chat(user, "The unit already contains a suit.") + to_chat(user, span_blue("The unit already contains a suit.")) return to_chat(user, "You load the [S.name] into the storage compartment.") user.drop_item() @@ -440,7 +440,7 @@ return var/obj/item/clothing/head/helmet/H = I if(HELMET) - to_chat(user, "The unit already contains a helmet.") + to_chat(user, span_blue("The unit already contains a helmet.")) return to_chat(user, "You load the [H.name] into the storage compartment.") user.drop_item() @@ -454,7 +454,7 @@ return var/obj/item/clothing/mask/M = I if(MASK) - to_chat(user, "The unit already contains a mask.") + to_chat(user, span_blue("The unit already contains a mask.")) return to_chat(user, "You load the [M.name] into the storage compartment.") user.drop_item() @@ -473,4 +473,4 @@ //////////////////////////////REMINDER: Make it lock once you place some fucker inside. -//God this entire file is fucking awful //Yes \ No newline at end of file +//God this entire file is fucking awful //Yes diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index 377a02aa74b..f3d4bb574fd 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -45,7 +45,7 @@ drop_type = "supermatter" /obj/machinery/power/supply_beacon/attackby(var/obj/item/weapon/W, var/mob/user) - if(!use_power && W.is_wrench()) + if(!use_power && W.has_tool_quality(TOOL_WRENCH)) if(!anchored && !connect_to_network()) to_chat(user, "This device must be placed over an exposed cable.") return diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index ea731a471bc..3f3e9e5d166 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -1,158 +1,158 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -// Beacon randomly spawns in space -// When a non-traitor (no special role in /mind) uses it, he is given the choice to become a traitor -// If he accepts there is a random chance he will be accepted, rejected, or rejected and killed -// Bringing certain items can help improve the chance to become a traitor - -/obj/machinery/syndicate_beacon - name = "ominous beacon" - desc = "This looks suspicious..." - icon = 'icons/obj/device.dmi' - icon_state = "syndbeacon" - anchored = TRUE - density = TRUE - var/temptext = "" - var/selfdestructing = 0 - var/charges = 1 - -/obj/machinery/syndicate_beacon/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
                    Identity confirmed,
                    " - if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) - if(is_special_character(user)) - dat += "Operative record found. Greetings, Agent [user.name].
                    " - else if(charges < 1) - dat += "Connection severed.
                    " - else - var/honorific = "Mr." - if(user.gender == FEMALE) - honorific = "Ms." - dat += "Identity not found in operative database. What can the Syndicate do for you today, [honorific] [user.name]?
                    " - if(!selfdestructing) - dat += "

                    \"[pick("I want to switch teams.", "I want to work for you.", "Let me join you.", "I can be of use to you.", "You want me working for you, and here's why...", "Give me an objective.", "How's the 401k over at the Syndicate?")]\"
                    " - dat += temptext - user << browse(dat, "window=syndbeacon") - onclose(user, "syndbeacon") - -/obj/machinery/syndicate_beacon/Topic(href, href_list) - if(..()) - return - if(href_list["betraitor"]) - if(charges < 1) - updateUsrDialog() - return - var/mob/M = locate(href_list["traitormob"]) - if(M.mind.special_role || jobban_isbanned(M, "Syndicate")) - temptext = "We have no need for you at this time. Have a pleasant day.
                    " - updateUsrDialog() - return - charges -= 1 - if(prob(50)) - temptext = "Double-crosser. You planned to betray us from the start. Allow us to repay the favor in kind." - updateUsrDialog() - spawn(rand(50,200)) selfdestruct() - return - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/N = M - to_chat(N, "You have joined the ranks of the Syndicate and become a traitor to the station!") - traitors.add_antagonist(N.mind) - traitors.equip(N) - message_admins("[N]/([N.ckey]) has accepted a traitor objective from a syndicate beacon.") - - updateUsrDialog() - return - -/obj/machinery/syndicate_beacon/proc/selfdestruct() - selfdestructing = 1 - spawn() explosion(src.loc, 1, rand(1,3), rand(3,8), 10) - -//////////////////////////////////////// -//Singularity beacon -//////////////////////////////////////// -/obj/machinery/power/singularity_beacon - name = "ominous beacon" - desc = "This looks suspicious..." - icon = 'icons/obj/singularity.dmi' - icon_state = "beacon" - - anchored = FALSE - density = TRUE - layer = MOB_LAYER - 0.1 //so people can't hide it and it's REALLY OBVIOUS - stat = 0 - - var/active = 0 - var/icontype = "beacon" - -/obj/machinery/power/singularity_beacon/proc/Activate(mob/user = null) - if(surplus() < 1500) - if(user) - to_chat(user, "The connected wire doesn't have enough current.") - return - for(var/obj/singularity/singulo in all_singularities) - if(singulo.z == z) - singulo.target = src - icon_state = "[icontype]1" - active = 1 - START_MACHINE_PROCESSING(src) - if(user) - to_chat(user, "You activate the beacon.") - -/obj/machinery/power/singularity_beacon/proc/Deactivate(mob/user = null) - for(var/obj/singularity/singulo in all_singularities) - if(singulo.target == src) - singulo.target = null - icon_state = "[icontype]0" - active = 0 - if(user) - to_chat(user, "You deactivate the beacon.") - -/obj/machinery/power/singularity_beacon/attack_ai(mob/user as mob) - return - -/obj/machinery/power/singularity_beacon/attack_hand(var/mob/user as mob) - if(anchored) - return active ? Deactivate(user) : Activate(user) - else - to_chat(user, "You need to screw the beacon to the floor first!") - return - -/obj/machinery/power/singularity_beacon/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_screwdriver()) - if(active) - to_chat(user, "You need to deactivate the beacon first!") - return - - if(anchored) - anchored = FALSE - to_chat(user, "You unscrew the beacon from the floor.") - playsound(src, W.usesound, 50, 1) - disconnect_from_network() - return - else - if(!connect_to_network()) - to_chat(user, "This device must be placed over an exposed cable.") - return - anchored = TRUE - to_chat(user, "You screw the beacon to the floor and attach the cable.") - playsound(src, W.usesound, 50, 1) - return - ..() - return - -/obj/machinery/power/singularity_beacon/Destroy() - if(active) - Deactivate() - ..() - -//stealth direct power usage -/obj/machinery/power/singularity_beacon/process() - if(!active) - return PROCESS_KILL - else - if(draw_power(1500) < 1500) - Deactivate() - -/obj/machinery/power/singularity_beacon/syndicate - icontype = "beaconsynd" - icon_state = "beaconsynd0" +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// Beacon randomly spawns in space +// When a non-traitor (no special role in /mind) uses it, he is given the choice to become a traitor +// If he accepts there is a random chance he will be accepted, rejected, or rejected and killed +// Bringing certain items can help improve the chance to become a traitor + +/obj/machinery/syndicate_beacon + name = "ominous beacon" + desc = "This looks suspicious..." + icon = 'icons/obj/device.dmi' + icon_state = "syndbeacon" + anchored = TRUE + density = TRUE + var/temptext = "" + var/selfdestructing = 0 + var/charges = 1 + +/obj/machinery/syndicate_beacon/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
                    Identity confirmed,
                    " + if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) + if(is_special_character(user)) + dat += "Operative record found. Greetings, Agent [user.name].
                    " + else if(charges < 1) + dat += "Connection severed.
                    " + else + var/honorific = "Mr." + if(user.gender == FEMALE) + honorific = "Ms." + dat += "Identity not found in operative database. What can the Syndicate do for you today, [honorific] [user.name]?
                    " + if(!selfdestructing) + dat += "

                    \"[pick("I want to switch teams.", "I want to work for you.", "Let me join you.", "I can be of use to you.", "You want me working for you, and here's why...", "Give me an objective.", "How's the 401k over at the Syndicate?")]\"
                    " + dat += temptext + user << browse(dat, "window=syndbeacon") + onclose(user, "syndbeacon") + +/obj/machinery/syndicate_beacon/Topic(href, href_list) + if(..()) + return + if(href_list["betraitor"]) + if(charges < 1) + updateUsrDialog() + return + var/mob/M = locate(href_list["traitormob"]) + if(M.mind.special_role || jobban_isbanned(M, "Syndicate")) + temptext = "We have no need for you at this time. Have a pleasant day.
                    " + updateUsrDialog() + return + charges -= 1 + if(prob(50)) + temptext = "Double-crosser. You planned to betray us from the start. Allow us to repay the favor in kind." + updateUsrDialog() + spawn(rand(50,200)) selfdestruct() + return + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/N = M + to_chat(N, "You have joined the ranks of the Syndicate and become a traitor to the station!") + traitors.add_antagonist(N.mind) + traitors.equip(N) + message_admins("[N]/([N.ckey]) has accepted a traitor objective from a syndicate beacon.") + + updateUsrDialog() + return + +/obj/machinery/syndicate_beacon/proc/selfdestruct() + selfdestructing = 1 + spawn() explosion(src.loc, 1, rand(1,3), rand(3,8), 10) + +//////////////////////////////////////// +//Singularity beacon +//////////////////////////////////////// +/obj/machinery/power/singularity_beacon + name = "ominous beacon" + desc = "This looks suspicious..." + icon = 'icons/obj/singularity.dmi' + icon_state = "beacon" + + anchored = FALSE + density = TRUE + layer = MOB_LAYER - 0.1 //so people can't hide it and it's REALLY OBVIOUS + stat = 0 + + var/active = 0 + var/icontype = "beacon" + +/obj/machinery/power/singularity_beacon/proc/Activate(mob/user = null) + if(surplus() < 1500) + if(user) + to_chat(user, "The connected wire doesn't have enough current.") + return + for(var/obj/singularity/singulo in all_singularities) + if(singulo.z == z) + singulo.target = src + icon_state = "[icontype]1" + active = 1 + START_MACHINE_PROCESSING(src) + if(user) + to_chat(user, "You activate the beacon.") + +/obj/machinery/power/singularity_beacon/proc/Deactivate(mob/user = null) + for(var/obj/singularity/singulo in all_singularities) + if(singulo.target == src) + singulo.target = null + icon_state = "[icontype]0" + active = 0 + if(user) + to_chat(user, "You deactivate the beacon.") + +/obj/machinery/power/singularity_beacon/attack_ai(mob/user as mob) + return + +/obj/machinery/power/singularity_beacon/attack_hand(var/mob/user as mob) + if(anchored) + return active ? Deactivate(user) : Activate(user) + else + to_chat(user, "You need to screw the beacon to the floor first!") + return + +/obj/machinery/power/singularity_beacon/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(active) + to_chat(user, "You need to deactivate the beacon first!") + return + + if(anchored) + anchored = FALSE + to_chat(user, "You unscrew the beacon from the floor.") + playsound(src, W.usesound, 50, 1) + disconnect_from_network() + return + else + if(!connect_to_network()) + to_chat(user, "This device must be placed over an exposed cable.") + return + anchored = TRUE + to_chat(user, "You screw the beacon to the floor and attach the cable.") + playsound(src, W.usesound, 50, 1) + return + ..() + return + +/obj/machinery/power/singularity_beacon/Destroy() + if(active) + Deactivate() + ..() + +//stealth direct power usage +/obj/machinery/power/singularity_beacon/process() + if(!active) + return PROCESS_KILL + else + if(draw_power(1500) < 1500) + Deactivate() + +/obj/machinery/power/singularity_beacon/syndicate + icontype = "beaconsynd" + icon_state = "beaconsynd0" diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index ca8a74312aa..8d7f91d704b 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/machine_interactions.dm @@ -1,386 +1,386 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - - -/* - - All telecommunications interactions: - -*/ - -#define STATION_Z 1 -#define TELECOMM_Z 3 - -/obj/machinery/telecomms - var/list/temp = null // output message - -/obj/machinery/telecomms/attackby(obj/item/P as obj, mob/user as mob) - - // Using a multitool lets you access the receiver's interface - if(istype(P, /obj/item/device/multitool)) - attack_hand(user) - - // REPAIRING: Use Nanopaste to repair 10-20 integrity points. - if(istype(P, /obj/item/stack/nanopaste)) - var/obj/item/stack/nanopaste/T = P - if (integrity < 100) //Damaged, let's repair! - if (T.use(1)) - integrity = between(0, integrity + rand(10,20), 100) - to_chat(usr, "You apply the Nanopaste to [src], repairing some of the damage.") - else - to_chat(usr, "This machine is already in perfect condition.") - return - - - if(default_deconstruction_screwdriver(user, P)) - return - if(default_deconstruction_crowbar(user, P)) - return - -/obj/machinery/telecomms/attack_ai(var/mob/user as mob) - attack_hand(user) - -/obj/machinery/telecomms/tgui_data(mob/user) - var/list/data = list() - - data["temp"] = temp - data["on"] = on - - data["id"] = null - data["network"] = null - data["autolinkers"] = FALSE - data["shadowlink"] = FALSE - data["options"] = list() - data["linked"] = list() - data["filter"] = list() - data["multitool"] = FALSE - data["multitool_buffer"] = null - - if(on || interact_offline) - data["id"] = id - data["network"] = network - data["autolinkers"] = !!LAZYLEN(autolinkers) - data["shadowlink"] = !!hide - - data["options"] = Options_Menu() - - var/obj/item/device/multitool/P = get_multitool(user) - data["multitool"] = !!P - data["multitool_buffer"] = null - if(P && P.buffer) - P.update_icon() - data["multitool_buffer"] = list("name" = "[P.buffer]", "id" = "[P.buffer.id]") - - var/i = 0 - var/list/linked = list() - for(var/obj/machinery/telecomms/T in links) - i++ - linked.Add(list(list( - "ref" = "\ref[T]", - "name" = "[T]", - "id" = T.id, - "index" = i, - ))) - data["linked"] = linked - - var/list/filter = list() - for(var/x in freq_listening) - filter.Add(list(list( - "name" = "[format_frequency(x)]", - "freq" = x, - ))) - data["filter"] = filter - - return data - -/obj/machinery/telecomms/tgui_status(mob/user) - if(!issilicon(user)) - if(!istype(user.get_active_hand(), /obj/item/device/multitool)) - return STATUS_CLOSE - . = ..() - -/obj/machinery/telecomms/attack_hand(var/mob/user as mob) - tgui_interact(user) - -/obj/machinery/telecomms/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TelecommsMultitoolMenu", name) - ui.open() - -// Off-Site Relays -// -// You are able to send/receive signals from the station's z level (changeable in the STATION_Z #define) if -// the relay is on the telecomm satellite (changable in the TELECOMM_Z #define) - - -/obj/machinery/telecomms/relay/proc/toggle_level() - - var/turf/position = get_turf(src) - - // Toggle on/off getting signals from the station or the current Z level - if(src.listening_level == STATION_Z) // equals the station - src.listening_level = position.z - return 1 - else if(position.z == TELECOMM_Z) - src.listening_level = STATION_Z - return 1 - return 0 - -// Returns a multitool from a user depending on their mobtype. - -/obj/machinery/proc/get_multitool(mob/user as mob) //No need to have this being a telecomms specific proc. - - var/obj/item/device/multitool/P = null - // Let's double check - if(!issilicon(user) && istype(user.get_active_hand(), /obj/item/device/multitool)) - P = user.get_active_hand() - else if(isAI(user)) - var/mob/living/silicon/ai/U = user - P = U.aiMulti - else if(isrobot(user) && in_range(user, src)) - if(istype(user.get_active_hand(), /obj/item/device/multitool)) - P = user.get_active_hand() - return P - -// Additional Options for certain machines. Use this when you want to add an option to a specific machine. -// Example of how to use below. - -/obj/machinery/telecomms/proc/Options_Menu() - return list() - -/* -// Add an option to the processor to switch processing mode. (COMPRESS -> UNCOMPRESS or UNCOMPRESS -> COMPRESS) -/obj/machinery/telecomms/processor/Options_Menu() - var/dat = "
                    Processing Mode: [process_mode ? "UNCOMPRESS" : "COMPRESS"]" - return dat -*/ -// The topic for Additional Options. Use this for checking href links for your specific option. -// Example of how to use below. -/obj/machinery/telecomms/proc/Options_Act(action, params) - return - -/* -/obj/machinery/telecomms/processor/Options_Act(action, params) - - if(href_list["process"]) - set_temp("-% Processing mode changed. %-", "average") - src.process_mode = !src.process_mode -*/ - -// RELAY - -/obj/machinery/telecomms/relay/Options_Menu() - var/list/data = ..() - data["use_listening_level"] = TRUE - data["use_broadcasting"] = TRUE - data["use_receiving"] = TRUE - data["listening_level"] = (listening_level == STATION_Z) - data["broadcasting"] = broadcasting - data["receiving"] = receiving - return data - -/obj/machinery/telecomms/relay/Options_Act(action, params) - if(..()) - return TRUE - - switch(action) - if("receive") - . = TRUE - receiving = !receiving - set_temp("-% Receiving mode changed. %-", "average") - if("broadcast") - . = TRUE - broadcasting = !broadcasting - set_temp("-% Broadcasting mode changed. %-", "average") - if("change_listening") - . = TRUE - //Lock to the station OR lock to the current position! - //You need at least two receivers and two broadcasters for this to work, this includes the machine. - var/result = toggle_level() - if(result) - set_temp("-% [src]'s signal has been successfully changed.", "average") - else - set_temp("-% [src] could not lock it's signal onto the station. Two broadcasters or receivers required.", "average") - -// BUS - -/obj/machinery/telecomms/bus/Options_Menu() - var/list/data = ..() - data["use_change_freq"] = TRUE - data["change_freq"] = change_frequency - return data - -/obj/machinery/telecomms/bus/Options_Act(action, params) - if(..()) - return TRUE - - switch(action) - if("change_freq") - . = TRUE - var/newfreq = input(usr, "Specify a new frequency for new signals to change to. Enter null to turn off frequency changing. Decimals assigned automatically.", src, network) as null|num - if(canAccess(usr)) - if(newfreq) - if(findtext(num2text(newfreq), ".")) - newfreq *= 10 // shift the decimal one place - if(newfreq < 10000) - change_frequency = newfreq - set_temp("-% New frequency to change to assigned: \"[newfreq] GHz\" %-", "average") - else - change_frequency = 0 - set_temp("-% Frequency changing deactivated %-", "average") - - -// BROADCASTER -/obj/machinery/telecomms/broadcaster/Options_Menu() - var/list/data = ..() - data["use_broadcast_range"] = TRUE - data["range"] = overmap_range - data["minRange"] = overmap_range_min - data["maxRange"] = overmap_range_max - return data - -/obj/machinery/telecomms/broadcaster - interact_offline = TRUE // because you can accidentally nuke power grids with these, need to be able to fix mistake - -/obj/machinery/telecomms/broadcaster/Options_Act(action, params) - if(..()) - return TRUE - - switch(action) - if("range") - var/new_range = params["range"] - overmap_range = clamp(new_range, overmap_range_min, overmap_range_max) - update_idle_power_usage(initial(idle_power_usage)**(overmap_range+1)) - -// RECEIVER -/obj/machinery/telecomms/receiver/Options_Menu() - var/list/data = ..() - data["use_receive_range"] = TRUE - data["range"] = overmap_range - data["minRange"] = overmap_range_min - data["maxRange"] = overmap_range_max - return data - -/obj/machinery/telecomms/receiver - interact_offline = TRUE // because you can accidentally nuke power grids with these, need to be able to fix mistake - -/obj/machinery/telecomms/receiver/Options_Act(action, params) - if(..()) - return TRUE - - switch(action) - if("range") - var/new_range = params["range"] - overmap_range = clamp(new_range, overmap_range_min, overmap_range_max) - update_idle_power_usage(initial(idle_power_usage)**(overmap_range+1)) - -/obj/machinery/telecomms/tgui_act(action, params) - if(..()) - return TRUE - - var/obj/item/device/multitool/P = get_multitool(usr) - - switch(action) - if("toggle") - src.toggled = !src.toggled - set_temp("-% [src] has been [src.toggled ? "activated" : "deactivated"].", "average") - update_power() - . = TRUE - - if("id") - var/newid = copytext(reject_bad_text(tgui_input_text(usr, "Specify the new ID for this machine", src, id)),1,MAX_MESSAGE_LEN) - if(newid && canAccess(usr)) - id = newid - set_temp("-% New ID assigned: \"[id]\" %-", "average") - . = TRUE - - if("network") - var/newnet = tgui_input_text(usr, "Specify the new network for this machine. This will break all current links.", src, network) - newnet = sanitize(newnet,15) - if(newnet && canAccess(usr)) - - if(length(newnet) > 15) - set_temp("-% Too many characters in new network tag %-", "average") - - else - for(var/obj/machinery/telecomms/T in links) - T.links.Remove(src) - - network = newnet - links = list() - set_temp("-% New network tag assigned: \"[network]\" %-", "average") - . = TRUE - - - if("freq") - var/newfreq = input(usr, "Specify a new frequency to filter (GHz). Decimals assigned automatically.", src, network) as null|num - if(newfreq && canAccess(usr)) - if(findtext(num2text(newfreq), ".")) - newfreq *= 10 // shift the decimal one place - if(!(newfreq in freq_listening) && newfreq < 10000) - freq_listening.Add(newfreq) - set_temp("-% New frequency filter assigned: \"[newfreq] GHz\" %-", "average") - . = TRUE - - if("delete") - var/x = text2num(params["delete"]) - set_temp("-% Removed frequency filter [x] %-", "average") - freq_listening.Remove(x) - . = TRUE - - if("unlink") - if(text2num(params["unlink"]) <= length(links)) - var/obj/machinery/telecomms/T = links[text2num(params["unlink"])] - set_temp("-% Removed \ref[T] [T.name] from linked entities. %-", "average") - - // Remove link entries from both T and src. - - if(src in T.links) - T.links.Remove(src) - links.Remove(T) - . = TRUE - - if("link") - if(P) - if(P.buffer && P.buffer != src) - if(!(src in P.buffer.links)) - P.buffer.links.Add(src) - - if(!(P.buffer in src.links)) - src.links.Add(P.buffer) - - set_temp("-% Successfully linked with \ref[P.buffer] [P.buffer.name] %-", "average") - - else - set_temp("-% Unable to acquire buffer %-", "average") - . = TRUE - - if("buffer") - P.buffer = src - set_temp("-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-", "average") - . = TRUE - - if("flush") - set_temp("-% Buffer successfully flushed. %-", "average") - P.buffer = null - . = TRUE - - if("cleartemp") - temp = null - . = TRUE - - if(Options_Act(action, params)) - . = TRUE - - add_fingerprint(usr) - -/obj/machinery/telecomms/proc/canAccess(var/mob/user) - if(issilicon(user) || in_range(user, src)) - return 1 - return 0 - -/obj/machinery/telecomms/proc/set_temp(var/text, var/color = "average") - temp = list("color" = color, "text" = text) - -#undef TELECOMM_Z -#undef STATION_Z +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + + +/* + + All telecommunications interactions: + +*/ + +#define STATION_Z 1 +#define TELECOMM_Z 3 + +/obj/machinery/telecomms + var/list/temp = null // output message + +/obj/machinery/telecomms/attackby(obj/item/P as obj, mob/user as mob) + + // Using a multitool lets you access the receiver's interface + if(istype(P, /obj/item/device/multitool)) + attack_hand(user) + + // REPAIRING: Use Nanopaste to repair 10-20 integrity points. + if(istype(P, /obj/item/stack/nanopaste)) + var/obj/item/stack/nanopaste/T = P + if (integrity < 100) //Damaged, let's repair! + if (T.use(1)) + integrity = between(0, integrity + rand(10,20), 100) + to_chat(usr, "You apply the Nanopaste to [src], repairing some of the damage.") + else + to_chat(usr, "This machine is already in perfect condition.") + return + + + if(default_deconstruction_screwdriver(user, P)) + return + if(default_deconstruction_crowbar(user, P)) + return + +/obj/machinery/telecomms/attack_ai(var/mob/user as mob) + attack_hand(user) + +/obj/machinery/telecomms/tgui_data(mob/user) + var/list/data = list() + + data["temp"] = temp + data["on"] = on + + data["id"] = null + data["network"] = null + data["autolinkers"] = FALSE + data["shadowlink"] = FALSE + data["options"] = list() + data["linked"] = list() + data["filter"] = list() + data["multitool"] = FALSE + data["multitool_buffer"] = null + + if(on || interact_offline) + data["id"] = id + data["network"] = network + data["autolinkers"] = !!LAZYLEN(autolinkers) + data["shadowlink"] = !!hide + + data["options"] = Options_Menu() + + var/obj/item/device/multitool/P = get_multitool(user) + data["multitool"] = !!P + data["multitool_buffer"] = null + if(P && P.buffer) + P.update_icon() + data["multitool_buffer"] = list("name" = "[P.buffer]", "id" = "[P.buffer.id]") + + var/i = 0 + var/list/linked = list() + for(var/obj/machinery/telecomms/T in links) + i++ + linked.Add(list(list( + "ref" = "\ref[T]", + "name" = "[T]", + "id" = T.id, + "index" = i, + ))) + data["linked"] = linked + + var/list/filter = list() + for(var/x in freq_listening) + filter.Add(list(list( + "name" = "[format_frequency(x)]", + "freq" = x, + ))) + data["filter"] = filter + + return data + +/obj/machinery/telecomms/tgui_status(mob/user) + if(!issilicon(user)) + if(!istype(user.get_active_hand(), /obj/item/device/multitool)) + return STATUS_CLOSE + . = ..() + +/obj/machinery/telecomms/attack_hand(var/mob/user as mob) + tgui_interact(user) + +/obj/machinery/telecomms/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TelecommsMultitoolMenu", name) + ui.open() + +// Off-Site Relays +// +// You are able to send/receive signals from the station's z level (changeable in the STATION_Z #define) if +// the relay is on the telecomm satellite (changable in the TELECOMM_Z #define) + + +/obj/machinery/telecomms/relay/proc/toggle_level() + + var/turf/position = get_turf(src) + + // Toggle on/off getting signals from the station or the current Z level + if(src.listening_level == STATION_Z) // equals the station + src.listening_level = position.z + return 1 + else if(position.z == TELECOMM_Z) + src.listening_level = STATION_Z + return 1 + return 0 + +// Returns a multitool from a user depending on their mobtype. + +/obj/machinery/proc/get_multitool(mob/user as mob) //No need to have this being a telecomms specific proc. + + var/obj/item/device/multitool/P = null + // Let's double check + if(!issilicon(user) && istype(user.get_active_hand(), /obj/item/device/multitool)) + P = user.get_active_hand() + else if(isAI(user)) + var/mob/living/silicon/ai/U = user + P = U.aiMulti + else if(isrobot(user) && in_range(user, src)) + if(istype(user.get_active_hand(), /obj/item/device/multitool)) + P = user.get_active_hand() + return P + +// Additional Options for certain machines. Use this when you want to add an option to a specific machine. +// Example of how to use below. + +/obj/machinery/telecomms/proc/Options_Menu() + return list() + +/* +// Add an option to the processor to switch processing mode. (COMPRESS -> UNCOMPRESS or UNCOMPRESS -> COMPRESS) +/obj/machinery/telecomms/processor/Options_Menu() + var/dat = "
                    Processing Mode: [process_mode ? "UNCOMPRESS" : "COMPRESS"]" + return dat +*/ +// The topic for Additional Options. Use this for checking href links for your specific option. +// Example of how to use below. +/obj/machinery/telecomms/proc/Options_Act(action, params) + return + +/* +/obj/machinery/telecomms/processor/Options_Act(action, params) + + if(href_list["process"]) + set_temp("-% Processing mode changed. %-", "average") + src.process_mode = !src.process_mode +*/ + +// RELAY + +/obj/machinery/telecomms/relay/Options_Menu() + var/list/data = ..() + data["use_listening_level"] = TRUE + data["use_broadcasting"] = TRUE + data["use_receiving"] = TRUE + data["listening_level"] = (listening_level == STATION_Z) + data["broadcasting"] = broadcasting + data["receiving"] = receiving + return data + +/obj/machinery/telecomms/relay/Options_Act(action, params) + if(..()) + return TRUE + + switch(action) + if("receive") + . = TRUE + receiving = !receiving + set_temp("-% Receiving mode changed. %-", "average") + if("broadcast") + . = TRUE + broadcasting = !broadcasting + set_temp("-% Broadcasting mode changed. %-", "average") + if("change_listening") + . = TRUE + //Lock to the station OR lock to the current position! + //You need at least two receivers and two broadcasters for this to work, this includes the machine. + var/result = toggle_level() + if(result) + set_temp("-% [src]'s signal has been successfully changed.", "average") + else + set_temp("-% [src] could not lock it's signal onto the station. Two broadcasters or receivers required.", "average") + +// BUS + +/obj/machinery/telecomms/bus/Options_Menu() + var/list/data = ..() + data["use_change_freq"] = TRUE + data["change_freq"] = change_frequency + return data + +/obj/machinery/telecomms/bus/Options_Act(action, params) + if(..()) + return TRUE + + switch(action) + if("change_freq") + . = TRUE + var/newfreq = input(usr, "Specify a new frequency for new signals to change to. Enter null to turn off frequency changing. Decimals assigned automatically.", src, network) as null|num + if(canAccess(usr)) + if(newfreq) + if(findtext(num2text(newfreq), ".")) + newfreq *= 10 // shift the decimal one place + if(newfreq < 10000) + change_frequency = newfreq + set_temp("-% New frequency to change to assigned: \"[newfreq] GHz\" %-", "average") + else + change_frequency = 0 + set_temp("-% Frequency changing deactivated %-", "average") + + +// BROADCASTER +/obj/machinery/telecomms/broadcaster/Options_Menu() + var/list/data = ..() + data["use_broadcast_range"] = TRUE + data["range"] = overmap_range + data["minRange"] = overmap_range_min + data["maxRange"] = overmap_range_max + return data + +/obj/machinery/telecomms/broadcaster + interact_offline = TRUE // because you can accidentally nuke power grids with these, need to be able to fix mistake + +/obj/machinery/telecomms/broadcaster/Options_Act(action, params) + if(..()) + return TRUE + + switch(action) + if("range") + var/new_range = params["range"] + overmap_range = clamp(new_range, overmap_range_min, overmap_range_max) + update_idle_power_usage(initial(idle_power_usage)**(overmap_range+1)) + +// RECEIVER +/obj/machinery/telecomms/receiver/Options_Menu() + var/list/data = ..() + data["use_receive_range"] = TRUE + data["range"] = overmap_range + data["minRange"] = overmap_range_min + data["maxRange"] = overmap_range_max + return data + +/obj/machinery/telecomms/receiver + interact_offline = TRUE // because you can accidentally nuke power grids with these, need to be able to fix mistake + +/obj/machinery/telecomms/receiver/Options_Act(action, params) + if(..()) + return TRUE + + switch(action) + if("range") + var/new_range = params["range"] + overmap_range = clamp(new_range, overmap_range_min, overmap_range_max) + update_idle_power_usage(initial(idle_power_usage)**(overmap_range+1)) + +/obj/machinery/telecomms/tgui_act(action, params) + if(..()) + return TRUE + + var/obj/item/device/multitool/P = get_multitool(usr) + + switch(action) + if("toggle") + src.toggled = !src.toggled + set_temp("-% [src] has been [src.toggled ? "activated" : "deactivated"].", "average") + update_power() + . = TRUE + + if("id") + var/newid = copytext(reject_bad_text(tgui_input_text(usr, "Specify the new ID for this machine", src, id)),1,MAX_MESSAGE_LEN) + if(newid && canAccess(usr)) + id = newid + set_temp("-% New ID assigned: \"[id]\" %-", "average") + . = TRUE + + if("network") + var/newnet = tgui_input_text(usr, "Specify the new network for this machine. This will break all current links.", src, network) + newnet = sanitize(newnet,15) + if(newnet && canAccess(usr)) + + if(length(newnet) > 15) + set_temp("-% Too many characters in new network tag %-", "average") + + else + for(var/obj/machinery/telecomms/T in links) + T.links.Remove(src) + + network = newnet + links = list() + set_temp("-% New network tag assigned: \"[network]\" %-", "average") + . = TRUE + + + if("freq") + var/newfreq = input(usr, "Specify a new frequency to filter (GHz). Decimals assigned automatically.", src, network) as null|num + if(newfreq && canAccess(usr)) + if(findtext(num2text(newfreq), ".")) + newfreq *= 10 // shift the decimal one place + if(!(newfreq in freq_listening) && newfreq < 10000) + freq_listening.Add(newfreq) + set_temp("-% New frequency filter assigned: \"[newfreq] GHz\" %-", "average") + . = TRUE + + if("delete") + var/x = text2num(params["delete"]) + set_temp("-% Removed frequency filter [x] %-", "average") + freq_listening.Remove(x) + . = TRUE + + if("unlink") + if(text2num(params["unlink"]) <= length(links)) + var/obj/machinery/telecomms/T = links[text2num(params["unlink"])] + set_temp("-% Removed \ref[T] [T.name] from linked entities. %-", "average") + + // Remove link entries from both T and src. + + if(src in T.links) + T.links.Remove(src) + links.Remove(T) + . = TRUE + + if("link") + if(P) + if(P.buffer && P.buffer != src) + if(!(src in P.buffer.links)) + P.buffer.links.Add(src) + + if(!(P.buffer in src.links)) + src.links.Add(P.buffer) + + set_temp("-% Successfully linked with \ref[P.buffer] [P.buffer.name] %-", "average") + + else + set_temp("-% Unable to acquire buffer %-", "average") + . = TRUE + + if("buffer") + P.buffer = src + set_temp("-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-", "average") + . = TRUE + + if("flush") + set_temp("-% Buffer successfully flushed. %-", "average") + P.buffer = null + . = TRUE + + if("cleartemp") + temp = null + . = TRUE + + if(Options_Act(action, params)) + . = TRUE + + add_fingerprint(usr) + +/obj/machinery/telecomms/proc/canAccess(var/mob/user) + if(issilicon(user) || in_range(user, src)) + return 1 + return 0 + +/obj/machinery/telecomms/proc/set_temp(var/text, var/color = "average") + temp = list("color" = color, "text" = text) + +#undef TELECOMM_Z +#undef STATION_Z diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm index 34ee3e424f2..b678ab19584 100644 --- a/code/game/machinery/telecomms/presets.dm +++ b/code/game/machinery/telecomms/presets.dm @@ -1,221 +1,221 @@ -// ### Preset machines ### - -//Relay - -/obj/machinery/telecomms/relay/preset - network = "tcommsat" - -/obj/machinery/telecomms/relay/preset/station - id = "Station Relay" - autolinkers = list("s_relay") - -/obj/machinery/telecomms/relay/preset/telecomms - id = "Telecomms Relay" - autolinkers = list("relay") - -/obj/machinery/telecomms/relay/preset/mining - id = "Mining Relay" - autolinkers = list("m_relay") - -/obj/machinery/telecomms/relay/preset/ruskie - id = "Ruskie Relay" - hide = 1 - toggled = 0 - autolinkers = list("r_relay") - -/obj/machinery/telecomms/relay/preset/centcom - id = "CentCom Relay" - hide = 1 - toggled = 1 - //anchored = TRUE - //use_power = 0 - //idle_power_usage = 0 - produces_heat = 0 - autolinkers = list("c_relay") - -//HUB - -/obj/machinery/telecomms/hub/preset - id = "Hub" - network = "tcommsat" - autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "science", "medical", - "supply", "service", "common", "command", "engineering", "security", "unused", "hb_relay", - "receiverA", "broadcasterA") //VOREStation Edit - Added "hb_relay" - -/obj/machinery/telecomms/hub/preset_cent - id = "CentCom Hub" - network = "tcommsat" - produces_heat = 0 - autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay", "hb_relay", - "centcom", "receiverCent", "broadcasterCent") //VOREStation Edit - Added "hb_relay" - -//Receivers - -/obj/machinery/telecomms/receiver/preset_right - id = "Receiver A" - network = "tcommsat" - autolinkers = list("receiverA") // link to relay - freq_listening = list(AI_FREQ, SCI_FREQ, MED_FREQ, SUP_FREQ, SRV_FREQ, COMM_FREQ, ENG_FREQ, SEC_FREQ, ENT_FREQ) - - //Common and other radio frequencies for people to freely use -/obj/machinery/telecomms/receiver/preset_right/New() - for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) - freq_listening |= i - ..() - -/obj/machinery/telecomms/receiver/preset_cent - id = "CentCom Receiver" - network = "tcommsat" - produces_heat = 0 - autolinkers = list("receiverCent") - freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) - - -//Buses - -/obj/machinery/telecomms/bus/preset_one - id = "Bus 1" - network = "tcommsat" - freq_listening = list(SCI_FREQ, MED_FREQ) - autolinkers = list("processor1", "science", "medical") - -/obj/machinery/telecomms/bus/preset_two - id = "Bus 2" - network = "tcommsat" - freq_listening = list(SUP_FREQ, SRV_FREQ) - autolinkers = list("processor2", "supply", "service", "unused") - -/obj/machinery/telecomms/bus/preset_two/New() - for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) - if(i == PUB_FREQ) - continue - freq_listening |= i - ..() - -/obj/machinery/telecomms/bus/preset_three - id = "Bus 3" - network = "tcommsat" - freq_listening = list(SEC_FREQ, COMM_FREQ) - autolinkers = list("processor3", "security", "command") - -/obj/machinery/telecomms/bus/preset_four - id = "Bus 4" - network = "tcommsat" - freq_listening = list(ENG_FREQ, AI_FREQ, PUB_FREQ, ENT_FREQ) - autolinkers = list("processor4", "engineering", "common") - -/obj/machinery/telecomms/bus/preset_cent - id = "CentCom Bus" - network = "tcommsat" - freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) - produces_heat = 0 - autolinkers = list("processorCent", "centcom") - -//Processors - -/obj/machinery/telecomms/processor/preset_one - id = "Processor 1" - network = "tcommsat" - autolinkers = list("processor1") // processors are sort of isolated; they don't need backward links - -/obj/machinery/telecomms/processor/preset_two - id = "Processor 2" - network = "tcommsat" - autolinkers = list("processor2") - -/obj/machinery/telecomms/processor/preset_three - id = "Processor 3" - network = "tcommsat" - autolinkers = list("processor3") - -/obj/machinery/telecomms/processor/preset_four - id = "Processor 4" - network = "tcommsat" - autolinkers = list("processor4") - -/obj/machinery/telecomms/processor/preset_cent - id = "CentCom Processor" - network = "tcommsat" - produces_heat = 0 - autolinkers = list("processorCent") - -//Servers - -/obj/machinery/telecomms/server/presets - - network = "tcommsat" - -/obj/machinery/telecomms/server/presets/science - id = "Science Server" - freq_listening = list(SCI_FREQ) - autolinkers = list("science") - -/obj/machinery/telecomms/server/presets/medical - id = "Medical Server" - freq_listening = list(MED_FREQ) - autolinkers = list("medical") - -/obj/machinery/telecomms/server/presets/supply - id = "Supply Server" - freq_listening = list(SUP_FREQ) - autolinkers = list("supply") - -/obj/machinery/telecomms/server/presets/service - id = "Service Server" - freq_listening = list(SRV_FREQ) - autolinkers = list("service") - -/obj/machinery/telecomms/server/presets/common - id = "Common Server" - freq_listening = list(PUB_FREQ, AI_FREQ, ENT_FREQ) // AI Private and Common - autolinkers = list("common") - -// "Unused" channels, AKA all others. -/obj/machinery/telecomms/server/presets/unused - id = "Unused Server" - freq_listening = list() - autolinkers = list("unused") - -/obj/machinery/telecomms/server/presets/unused/New() - for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) - if(i == AI_FREQ || i == PUB_FREQ) - continue - freq_listening |= i - ..() - -/obj/machinery/telecomms/server/presets/command - id = "Command Server" - freq_listening = list(COMM_FREQ) - autolinkers = list("command") - -/obj/machinery/telecomms/server/presets/engineering - id = "Engineering Server" - freq_listening = list(ENG_FREQ) - autolinkers = list("engineering") - -/obj/machinery/telecomms/server/presets/security - id = "Security Server" - freq_listening = list(SEC_FREQ) - autolinkers = list("security") - -/obj/machinery/telecomms/server/presets/centcomm - id = "CentCom Server" - freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) - produces_heat = 0 - autolinkers = list("centcom") - - -//Broadcasters - -//--PRESET LEFT--// - -/obj/machinery/telecomms/broadcaster/preset_right - id = "Broadcaster A" - network = "tcommsat" - autolinkers = list("broadcasterA") - -/obj/machinery/telecomms/broadcaster/preset_cent - id = "CentCom Broadcaster" - network = "tcommsat" - produces_heat = 0 +// ### Preset machines ### + +//Relay + +/obj/machinery/telecomms/relay/preset + network = "tcommsat" + +/obj/machinery/telecomms/relay/preset/station + id = "Station Relay" + autolinkers = list("s_relay") + +/obj/machinery/telecomms/relay/preset/telecomms + id = "Telecomms Relay" + autolinkers = list("relay") + +/obj/machinery/telecomms/relay/preset/mining + id = "Mining Relay" + autolinkers = list("m_relay") + +/obj/machinery/telecomms/relay/preset/ruskie + id = "Ruskie Relay" + hide = 1 + toggled = 0 + autolinkers = list("r_relay") + +/obj/machinery/telecomms/relay/preset/centcom + id = "CentCom Relay" + hide = 1 + toggled = 1 + //anchored = TRUE + //use_power = 0 + //idle_power_usage = 0 + produces_heat = 0 + autolinkers = list("c_relay") + +//HUB + +/obj/machinery/telecomms/hub/preset + id = "Hub" + network = "tcommsat" + autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "science", "medical", + "supply", "service", "common", "command", "engineering", "security", "unused", "hb_relay", + "receiverA", "broadcasterA") //VOREStation Edit - Added "hb_relay" + +/obj/machinery/telecomms/hub/preset_cent + id = "CentCom Hub" + network = "tcommsat" + produces_heat = 0 + autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay", "hb_relay", + "centcom", "receiverCent", "broadcasterCent") //VOREStation Edit - Added "hb_relay" + +//Receivers + +/obj/machinery/telecomms/receiver/preset_right + id = "Receiver A" + network = "tcommsat" + autolinkers = list("receiverA") // link to relay + freq_listening = list(AI_FREQ, SCI_FREQ, MED_FREQ, SUP_FREQ, SRV_FREQ, COMM_FREQ, ENG_FREQ, SEC_FREQ, ENT_FREQ) + + //Common and other radio frequencies for people to freely use +/obj/machinery/telecomms/receiver/preset_right/New() + for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) + freq_listening |= i + ..() + +/obj/machinery/telecomms/receiver/preset_cent + id = "CentCom Receiver" + network = "tcommsat" + produces_heat = 0 + autolinkers = list("receiverCent") + freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) + + +//Buses + +/obj/machinery/telecomms/bus/preset_one + id = "Bus 1" + network = "tcommsat" + freq_listening = list(SCI_FREQ, MED_FREQ) + autolinkers = list("processor1", "science", "medical") + +/obj/machinery/telecomms/bus/preset_two + id = "Bus 2" + network = "tcommsat" + freq_listening = list(SUP_FREQ, SRV_FREQ) + autolinkers = list("processor2", "supply", "service", "unused") + +/obj/machinery/telecomms/bus/preset_two/New() + for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) + if(i == PUB_FREQ) + continue + freq_listening |= i + ..() + +/obj/machinery/telecomms/bus/preset_three + id = "Bus 3" + network = "tcommsat" + freq_listening = list(SEC_FREQ, COMM_FREQ) + autolinkers = list("processor3", "security", "command") + +/obj/machinery/telecomms/bus/preset_four + id = "Bus 4" + network = "tcommsat" + freq_listening = list(ENG_FREQ, AI_FREQ, PUB_FREQ, ENT_FREQ) + autolinkers = list("processor4", "engineering", "common") + +/obj/machinery/telecomms/bus/preset_cent + id = "CentCom Bus" + network = "tcommsat" + freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) + produces_heat = 0 + autolinkers = list("processorCent", "centcom") + +//Processors + +/obj/machinery/telecomms/processor/preset_one + id = "Processor 1" + network = "tcommsat" + autolinkers = list("processor1") // processors are sort of isolated; they don't need backward links + +/obj/machinery/telecomms/processor/preset_two + id = "Processor 2" + network = "tcommsat" + autolinkers = list("processor2") + +/obj/machinery/telecomms/processor/preset_three + id = "Processor 3" + network = "tcommsat" + autolinkers = list("processor3") + +/obj/machinery/telecomms/processor/preset_four + id = "Processor 4" + network = "tcommsat" + autolinkers = list("processor4") + +/obj/machinery/telecomms/processor/preset_cent + id = "CentCom Processor" + network = "tcommsat" + produces_heat = 0 + autolinkers = list("processorCent") + +//Servers + +/obj/machinery/telecomms/server/presets + + network = "tcommsat" + +/obj/machinery/telecomms/server/presets/science + id = "Science Server" + freq_listening = list(SCI_FREQ) + autolinkers = list("science") + +/obj/machinery/telecomms/server/presets/medical + id = "Medical Server" + freq_listening = list(MED_FREQ) + autolinkers = list("medical") + +/obj/machinery/telecomms/server/presets/supply + id = "Supply Server" + freq_listening = list(SUP_FREQ) + autolinkers = list("supply") + +/obj/machinery/telecomms/server/presets/service + id = "Service Server" + freq_listening = list(SRV_FREQ) + autolinkers = list("service") + +/obj/machinery/telecomms/server/presets/common + id = "Common Server" + freq_listening = list(PUB_FREQ, AI_FREQ, ENT_FREQ) // AI Private and Common + autolinkers = list("common") + +// "Unused" channels, AKA all others. +/obj/machinery/telecomms/server/presets/unused + id = "Unused Server" + freq_listening = list() + autolinkers = list("unused") + +/obj/machinery/telecomms/server/presets/unused/New() + for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2) + if(i == AI_FREQ || i == PUB_FREQ) + continue + freq_listening |= i + ..() + +/obj/machinery/telecomms/server/presets/command + id = "Command Server" + freq_listening = list(COMM_FREQ) + autolinkers = list("command") + +/obj/machinery/telecomms/server/presets/engineering + id = "Engineering Server" + freq_listening = list(ENG_FREQ) + autolinkers = list("engineering") + +/obj/machinery/telecomms/server/presets/security + id = "Security Server" + freq_listening = list(SEC_FREQ) + autolinkers = list("security") + +/obj/machinery/telecomms/server/presets/centcomm + id = "CentCom Server" + freq_listening = list(ERT_FREQ, DTH_FREQ, SYND_FREQ) + produces_heat = 0 + autolinkers = list("centcom") + + +//Broadcasters + +//--PRESET LEFT--// + +/obj/machinery/telecomms/broadcaster/preset_right + id = "Broadcaster A" + network = "tcommsat" + autolinkers = list("broadcasterA") + +/obj/machinery/telecomms/broadcaster/preset_cent + id = "CentCom Broadcaster" + network = "tcommsat" + produces_heat = 0 autolinkers = list("broadcasterCent") \ No newline at end of file diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index bddcd5f1ea4..d7b1fcbff57 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -1,716 +1,716 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/* - Hello, friends, this is Doohl from sexylands. You may be wondering what this - monstrous code file is. Sit down, boys and girls, while I tell you the tale. - - - The machines defined in this file were designed to be compatible with any radio - signals, provided they use subspace transmission. Currently they are only used for - headsets, but they can eventually be outfitted for real COMPUTER networks. This - is just a skeleton, ladies and gentlemen. - - Look at radio.dm for the prequel to this code. -*/ - -var/global/list/obj/machinery/telecomms/telecomms_list = list() - -/obj/machinery/telecomms - icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Add - unacidable = TRUE - var/list/links = list() // list of machines this machine is linked to - var/traffic = 0 // value increases as traffic increases - var/netspeed = 5 // how much traffic to lose per tick (50 gigabytes/second * netspeed) - var/list/autolinkers = list() // list of text/number values to link with - var/id = "NULL" // identification string - var/network = "NULL" // the network of the machinery - - var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all - - var/machinetype = 0 // just a hacky way of preventing alike machines from pairing - var/toggled = 1 // Is it toggled on - var/on = 1 - var/integrity = 100 // basically HP, loses integrity by heat - var/produces_heat = 1 //whether the machine will produce heat when on. - var/delay = 10 // how many process() ticks to delay per heat - var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub) - var/hide = 0 // Is it a hidden machine? - var/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to. - - -/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20) - // relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending - - if(!on) - return - //to_world("[src] ([src.id]) - [signal.debug_print()]") - var/send_count = 0 - - signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity - - /* - // Edit by Atlantis: Commented out as emergency fix due to causing extreme delays in communications. - // Apply some lag based on traffic rates - var/netlag = round(traffic / 50) - if(netlag > signal.data["slow"]) - signal.data["slow"] = netlag - */ -// Loop through all linked machines and send the signal or copy. - for(var/obj/machinery/telecomms/machine in links) - if(filter && !istype(machine, filter)) - continue - if(!machine.on) - continue - if(amount && send_count >= amount) - break - if(machine.loc.z != listening_level) - if(long_range_link == 0 && machine.long_range_link == 0) - continue - // If we're sending a copy, be sure to create the copy for EACH machine and paste the data - var/datum/signal/copy - if(copysig) - copy = new - copy.transmission_method = TRANSMISSION_SUBSPACE - copy.frequency = signal.frequency - copy.data = signal.data.Copy() - - // Keep the "original" signal constant - if(!signal.data["original"]) - copy.data["original"] = signal - else - copy.data["original"] = signal.data["original"] - - send_count++ - if(machine.is_freq_listening(signal)) - machine.traffic++ - - if(copysig && copy) - machine.receive_information(copy, src) - else - machine.receive_information(signal, src) - - - if(send_count > 0 && is_freq_listening(signal)) - traffic++ - - return send_count - -/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine) - // send signal directly to a machine - machine.receive_information(signal, src) - -/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - // receive information from linked machinery - return - -/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal) - // return 1 if found, 0 if not found - if(!signal) - return 0 - if((signal.frequency in freq_listening) || (!freq_listening.len)) - return 1 - else - return 0 - - -/obj/machinery/telecomms/New() - telecomms_list += src - ..() - - //Set the listening_level if there's none. - if(!listening_level) - //Defaults to our Z level! - var/turf/position = get_turf(src) - listening_level = position.z - -/obj/machinery/telecomms/Initialize() - if(autolinkers.len) - // Links nearby machines - if(!long_range_link) - for(var/obj/machinery/telecomms/T in orange(20, src)) - add_link(T) - else - for(var/obj/machinery/telecomms/T in telecomms_list) - add_link(T) - . = ..() - -/obj/machinery/telecomms/Destroy() - telecomms_list -= src - for(var/obj/machinery/telecomms/comm in telecomms_list) - comm.links -= src - links = list() - ..() - -// Used in auto linking -/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) - var/pos_z = get_z(src) - var/tpos_z = get_z(T) - if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) - for(var/x in autolinkers) - if(T.autolinkers.Find(x)) - if(src != T) - links |= T - -/obj/machinery/telecomms/update_icon() - if(on) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]_off" - -/obj/machinery/telecomms/proc/update_power() - - if(toggled) - if(stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off - on = 0 - else - on = 1 - else - on = 0 - -/obj/machinery/telecomms/process() - update_power() - - // Check heat and generate some - checkheat() - - // Update the icon - update_icon() - - if(traffic > 0) - traffic -= netspeed - -/obj/machinery/telecomms/emp_act(severity) - if(prob(100/severity)) - if(!(stat & EMPED)) - stat |= EMPED - var/duration = (300 * 10)/severity - spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot. - stat &= ~EMPED - ..() - -/obj/machinery/telecomms/proc/checkheat() - // Checks heat from the environment and applies any integrity damage - var/datum/gas_mixture/environment = loc.return_air() - var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick - switch(environment.temperature) - if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage - damage_chance = 10 - if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage - damage_chance = 25 - if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage - damage_chance = 50 - if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick. - damage_chance = 100 - if (damage_chance && prob(damage_chance)) - integrity = between(0, integrity - 1, 100) - - - if(delay > 0) - delay-- - else if(on) - produce_heat() - delay = initial(delay) - - - -/obj/machinery/telecomms/proc/produce_heat() - if (!produces_heat) - return - - if (!use_power) - return - - if(!(stat & (NOPOWER|BROKEN))) - var/turf/simulated/L = loc - if(istype(L)) - var/datum/gas_mixture/env = L.return_air() - - var/transfer_moles = 0.25 * env.total_moles - - var/datum/gas_mixture/removed = env.remove(transfer_moles) - - if(removed) - - var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source - if (traffic <= 0) - heat_produced *= 0.30 //if idle, produce less heat. - - removed.add_thermal_energy(heat_produced) - - env.merge(removed) -/* - The receiver idles and receives messages from subspace-compatible radio equipment; - primarily headsets. They then just relay this information to all linked devices, - which can would probably be network hubs. - - Link to Processor Units in case receiver can't send to bus units. -*/ - -/obj/machinery/telecomms/receiver - name = "Subspace Receiver" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "broadcast receiver" - desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 1 - produces_heat = 0 - circuit = /obj/item/weapon/circuitboard/telecomms/receiver - //Vars only used if you're using the overmap - var/overmap_range = 0 - var/overmap_range_min = 0 - var/overmap_range_max = 5 - - var/list/linked_radios_weakrefs = list() - -/obj/machinery/telecomms/receiver/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/device/radio/R) - if(!istype(R)) - return - linked_radios_weakrefs |= WEAKREF(R) - -/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) - if(!on) // has to be on to receive messages - return - if(!signal) - return - if(!check_receive_level(signal)) - return - - if(signal.transmission_method == TRANSMISSION_SUBSPACE) - - if(is_freq_listening(signal)) // detect subspace signals - - //Remove the level and then start adding levels that it is being broadcasted in. - signal.data["level"] = list() - - var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) // ideally relay the copied information to relays - if(!can_send) - relay_information(signal, /obj/machinery/telecomms/bus) // Send it to a bus instead, if it's linked to one - -/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) - // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally - if(signal.transmission_method == TRANSMISSION_BLUESPACE) - var/obj/item/device/radio/R = signal.data["radio"] - - //Who're you? - if(!(WEAKREF(R) in linked_radios_weakrefs)) - signal.data["reject"] = 1 - return 0 - - //We'll resend this for you - signal.data["level"] = z - signal.transmission_method = TRANSMISSION_SUBSPACE - return 1 - - //Where can we hear? - var/list/listening_levels = using_map.get_map_levels(listening_level, TRUE, overmap_range) - - // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it - if(!(signal.data["level"] in listening_levels)) - for(var/obj/machinery/telecomms/hub/H in links) - var/list/relayed_levels = list() - for(var/obj/machinery/telecomms/relay/R in H.links) - if(R.can_receive(signal)) - relayed_levels |= R.listening_level - if(signal.data["level"] in relayed_levels) - return 1 - return 0 - return 1 - - -/* - The HUB idles until it receives information. It then passes on that information - depending on where it came from. - - This is the heart of the Telecommunications Network, sending information where it - is needed. It mainly receives information from long-distance Relays and then sends - that information to be processed. Afterwards it gets the uncompressed information - from Servers/Buses and sends that back to the relay, to then be broadcasted. -*/ - -/obj/machinery/telecomms/hub - name = "Telecommunication Hub" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "hub" - desc = "A mighty piece of hardware used to send/receive massive amounts of data." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 1600 - machinetype = 7 - circuit = /obj/item/weapon/circuitboard/telecomms/hub - long_range_link = 1 - netspeed = 40 - -/obj/machinery/telecomms/hub/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - if(is_freq_listening(signal)) - if(istype(machine_from, /obj/machinery/telecomms/receiver)) - //If the signal is compressed, send it to the bus. - relay_information(signal, /obj/machinery/telecomms/bus, 1) // ideally relay the copied information to bus units - else - // Get a list of relays that we're linked to, then send the signal to their levels. - relay_information(signal, /obj/machinery/telecomms/relay, 1) - relay_information(signal, /obj/machinery/telecomms/broadcaster, 1) // Send it to a broadcaster. - - -/* - The relay idles until it receives information. It then passes on that information - depending on where it came from. - - The relay is needed in order to send information pass Z levels. It must be linked - with a HUB, the only other machine that can send/receive pass Z levels. -*/ - -/obj/machinery/telecomms/relay - name = "Telecommunication Relay" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "relay" - desc = "A mighty piece of hardware used to send massive amounts of data far away." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 8 - produces_heat = 0 - circuit = /obj/item/weapon/circuitboard/telecomms/relay - netspeed = 5 - long_range_link = 1 - var/broadcasting = 1 - var/receiving = 1 - -/obj/machinery/telecomms/relay/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/relay/forceMove(var/newloc) - . = ..(newloc) - listening_level = z - -/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - // Add our level and send it back - if(can_send(signal)) - signal.data["level"] |= using_map.get_map_levels(listening_level) - -// Checks to see if it can send/receive. - -/obj/machinery/telecomms/relay/proc/can(datum/signal/signal) - if(!on) - return 0 - if(!is_freq_listening(signal)) - return 0 - return 1 - -/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal) - if(!can(signal)) - return 0 - return broadcasting - -/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal) - if(!can(signal)) - return 0 - return receiving - -/* - The bus mainframe idles and waits for hubs to relay them signals. They act - as junctions for the network. - - They transfer uncompressed subspace packets to processor units, and then take - the processed packet to a server for logging. - - Link to a subspace hub if it can't send to a server. -*/ - -/obj/machinery/telecomms/bus - name = "Bus Mainframe" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "bus" - desc = "A mighty piece of hardware used to send massive amounts of data quickly." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 1000 - machinetype = 2 - circuit = /obj/item/weapon/circuitboard/telecomms/bus - netspeed = 40 - var/change_frequency = 0 - -/obj/machinery/telecomms/bus/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(is_freq_listening(signal)) - - if(change_frequency) - signal.frequency = change_frequency - - if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it - // send to one linked processor unit - var/send_to_processor = relay_information(signal, /obj/machinery/telecomms/processor) - - if(send_to_processor) - return - // failed to send to a processor, relay information anyway - signal.data["slow"] += rand(1, 5) // slow the signal down only slightly - src.receive_information(signal, src) - - // Try sending it! - var/list/try_send = list(/obj/machinery/telecomms/server, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster, /obj/machinery/telecomms/bus) - var/i = 0 - for(var/send in try_send) - if(i) - signal.data["slow"] += rand(0, 1) // slow the signal down only slightly - i++ - var/can_send = relay_information(signal, send) - if(can_send) - break - - - -/* - The processor is a very simple machine that decompresses subspace signals and - transfers them back to the original bus. It is essential in producing audible - data. - - Link to servers if bus is not present -*/ - -/obj/machinery/telecomms/processor - name = "Processor Unit" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "processor" - desc = "This machine is used to process large quantities of information." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 600 - machinetype = 3 - delay = 5 - circuit = /obj/item/weapon/circuitboard/telecomms/processor - var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals - -/obj/machinery/telecomms/processor/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(is_freq_listening(signal)) - - if(process_mode) - signal.data["compression"] = 0 // uncompress subspace signal - else - signal.data["compression"] = 100 // even more compressed signal - - if(istype(machine_from, /obj/machinery/telecomms/bus)) - relay_direct_information(signal, machine_from) // send the signal back to the machine - else // no bus detected - send the signal to servers instead - signal.data["slow"] += rand(5, 10) // slow the signal down - relay_information(signal, /obj/machinery/telecomms/server) - - -/* - The server logs all traffic and signal data. Once it records the signal, it sends - it to the subspace broadcaster. - - Store a maximum of 100 logs and then deletes them. -*/ - - -/obj/machinery/telecomms/server - name = "Telecommunication Server" - //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon - icon_state = "comm_server" - desc = "A machine used to store data and network statistics." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 300 - machinetype = 4 - circuit = /obj/item/weapon/circuitboard/telecomms/server - var/list/log_entries = list() - var/list/stored_names = list() - var/list/TrafficActions = list() - var/logs = 0 // number of logs - var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes) - - var/list/memory = list() // stored memory - var/rawcode = "" // the code to compile (raw text) - var/datum/TCS_Compiler/Compiler // the compiler that compiles and runs the code - var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up - - var/encryption = "null" // encryption key: ie "password" - var/salt = "null" // encryption salt: ie "123comsat" - // would add up to md5("password123comsat") - var/obj/item/device/radio/headset/server_radio = null - -/obj/machinery/telecomms/server/New() - ..() - Compiler = new() - Compiler.Holder = src - server_radio = new() - -/obj/machinery/telecomms/server/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - - if(signal.data["message"]) - - if(is_freq_listening(signal)) - - if(traffic > 0) - totaltraffic += traffic // add current traffic to total traffic - - //Is this a test signal? Bypass logging - if(signal.data["type"] != SIGNAL_TEST) - - // If signal has a message and appropriate frequency - - update_logs() - - var/datum/comm_log_entry/log = new - var/mob/M = signal.data["mob"] - - // Copy the signal.data entries we want - log.parameters["mobtype"] = signal.data["mobtype"] - log.parameters["job"] = signal.data["job"] - log.parameters["key"] = signal.data["key"] - log.parameters["vmessage"] = multilingual_to_message(signal.data["message"]) - log.parameters["vname"] = signal.data["vname"] - log.parameters["message"] = multilingual_to_message(signal.data["message"]) - log.parameters["name"] = signal.data["name"] - log.parameters["realname"] = signal.data["realname"] - log.parameters["timecode"] = worldtime2stationtime(world.time) - - var/race = "unknown" - if(ishuman(M)) - var/mob/living/carbon/human/H = M - race = "[H.species.name]" - log.parameters["intelligible"] = 1 - else if(isbrain(M)) - race = "Brain" - log.parameters["intelligible"] = 1 - else if(M.isMonkey()) - race = "Monkey" - else if(issilicon(M)) - race = "Artificial Life" - log.parameters["intelligible"] = 1 - else if(isslime(M)) - race = "Slime" - else if(isanimal(M)) - race = "Domestic Animal" - - log.parameters["race"] = race - - if(!istype(M, /mob/new_player) && M) - log.parameters["uspeech"] = M.universal_speak - else - log.parameters["uspeech"] = 0 - - // If the signal is still compressed, make the log entry gibberish - if(signal.data["compression"] > 0) - log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50) - log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) - log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) - log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) - log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) - log.input_type = "Corrupt File" - - // Log and store everything that needs to be logged - log_entries.Add(log) - if(!(signal.data["name"] in stored_names)) - stored_names.Add(signal.data["name"]) - logs++ - signal.data["server"] = src - - // Give the log a name - var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "data packet ([md5(identifier)])" - - if(Compiler && autoruncode) - Compiler.Run(signal) // execute the code - - var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) - if(!can_send) - relay_information(signal, /obj/machinery/telecomms/broadcaster) - - -/obj/machinery/telecomms/server/proc/setcode(var/t) - if(t) - if(istext(t)) - rawcode = t - -/obj/machinery/telecomms/server/proc/compile() - if(Compiler) - return Compiler.Compile(rawcode) - -/obj/machinery/telecomms/server/proc/update_logs() - // start deleting the very first log entry - if(logs >= 400) - for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it - var/datum/comm_log_entry/L = log_entries[i] - if(L.garbage_collector) - log_entries.Remove(L) - logs-- - break - -/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input) - var/datum/comm_log_entry/log = new - var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "[input] ([md5(identifier)])" - log.input_type = input - log.parameters["message"] = content - log.parameters["timecode"] = stationtime2text() - log_entries.Add(log) - update_logs() - - - - -// Simple log entry datum - -/datum/comm_log_entry - var/parameters = list() // carbon-copy to signal.data[] - var/name = "data packet (#)" - var/garbage_collector = 1 // if set to 0, will not be garbage collected - var/input_type = "Speech File" - -//ARFS EDIT - Commented out to add exonet relay -//Generic telecomm connectivity test proc -// /proc/can_telecomm(var/atom/A, var/atom/B, var/ad_hoc = FALSE) -// if(!A || !B) -// log_debug("can_telecomm(): Undefined endpoints!") -// return FALSE - -// //Can't in this case, obviously! -// if(is_jammed(A) || is_jammed(B)) -// return FALSE - -// //Items don't have a Z when inside an object or mob -// var/turf/src_z = get_z(A) -// var/turf/dst_z = get_z(B) - -// //Nullspace, probably. -// if(!src_z || !dst_z) -// return FALSE - -// //We can do the simple check first, if you have ad_hoc radios. -// if(ad_hoc && src_z == dst_z) -// return TRUE - -// return src_z in using_map.get_map_levels(dst_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/* + Hello, friends, this is Doohl from sexylands. You may be wondering what this + monstrous code file is. Sit down, boys and girls, while I tell you the tale. + + + The machines defined in this file were designed to be compatible with any radio + signals, provided they use subspace transmission. Currently they are only used for + headsets, but they can eventually be outfitted for real COMPUTER networks. This + is just a skeleton, ladies and gentlemen. + + Look at radio.dm for the prequel to this code. +*/ + +var/global/list/obj/machinery/telecomms/telecomms_list = list() + +/obj/machinery/telecomms + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Add + unacidable = TRUE + var/list/links = list() // list of machines this machine is linked to + var/traffic = 0 // value increases as traffic increases + var/netspeed = 5 // how much traffic to lose per tick (50 gigabytes/second * netspeed) + var/list/autolinkers = list() // list of text/number values to link with + var/id = "NULL" // identification string + var/network = "NULL" // the network of the machinery + + var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all + + var/machinetype = 0 // just a hacky way of preventing alike machines from pairing + var/toggled = 1 // Is it toggled on + var/on = 1 + var/integrity = 100 // basically HP, loses integrity by heat + var/produces_heat = 1 //whether the machine will produce heat when on. + var/delay = 10 // how many process() ticks to delay per heat + var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub) + var/hide = 0 // Is it a hidden machine? + var/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to. + + +/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20) + // relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending + + if(!on) + return + //to_world("[src] ([src.id]) - [signal.debug_print()]") + var/send_count = 0 + + signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity + + /* + // Edit by Atlantis: Commented out as emergency fix due to causing extreme delays in communications. + // Apply some lag based on traffic rates + var/netlag = round(traffic / 50) + if(netlag > signal.data["slow"]) + signal.data["slow"] = netlag + */ +// Loop through all linked machines and send the signal or copy. + for(var/obj/machinery/telecomms/machine in links) + if(filter && !istype(machine, filter)) + continue + if(!machine.on) + continue + if(amount && send_count >= amount) + break + if(machine.loc.z != listening_level) + if(long_range_link == 0 && machine.long_range_link == 0) + continue + // If we're sending a copy, be sure to create the copy for EACH machine and paste the data + var/datum/signal/copy + if(copysig) + copy = new + copy.transmission_method = TRANSMISSION_SUBSPACE + copy.frequency = signal.frequency + copy.data = signal.data.Copy() + + // Keep the "original" signal constant + if(!signal.data["original"]) + copy.data["original"] = signal + else + copy.data["original"] = signal.data["original"] + + send_count++ + if(machine.is_freq_listening(signal)) + machine.traffic++ + + if(copysig && copy) + machine.receive_information(copy, src) + else + machine.receive_information(signal, src) + + + if(send_count > 0 && is_freq_listening(signal)) + traffic++ + + return send_count + +/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine) + // send signal directly to a machine + machine.receive_information(signal, src) + +/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + // receive information from linked machinery + return + +/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal) + // return 1 if found, 0 if not found + if(!signal) + return 0 + if((signal.frequency in freq_listening) || (!freq_listening.len)) + return 1 + else + return 0 + + +/obj/machinery/telecomms/New() + telecomms_list += src + ..() + + //Set the listening_level if there's none. + if(!listening_level) + //Defaults to our Z level! + var/turf/position = get_turf(src) + listening_level = position.z + +/obj/machinery/telecomms/Initialize() + if(autolinkers.len) + // Links nearby machines + if(!long_range_link) + for(var/obj/machinery/telecomms/T in orange(20, src)) + add_link(T) + else + for(var/obj/machinery/telecomms/T in telecomms_list) + add_link(T) + . = ..() + +/obj/machinery/telecomms/Destroy() + telecomms_list -= src + for(var/obj/machinery/telecomms/comm in telecomms_list) + comm.links -= src + links = list() + ..() + +// Used in auto linking +/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) + var/pos_z = get_z(src) + var/tpos_z = get_z(T) + if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) + for(var/x in autolinkers) + if(T.autolinkers.Find(x)) + if(src != T) + links |= T + +/obj/machinery/telecomms/update_icon() + if(on) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_off" + +/obj/machinery/telecomms/proc/update_power() + + if(toggled) + if(stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off + on = 0 + else + on = 1 + else + on = 0 + +/obj/machinery/telecomms/process() + update_power() + + // Check heat and generate some + checkheat() + + // Update the icon + update_icon() + + if(traffic > 0) + traffic -= netspeed + +/obj/machinery/telecomms/emp_act(severity) + if(prob(100/severity)) + if(!(stat & EMPED)) + stat |= EMPED + var/duration = (300 * 10)/severity + spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot. + stat &= ~EMPED + ..() + +/obj/machinery/telecomms/proc/checkheat() + // Checks heat from the environment and applies any integrity damage + var/datum/gas_mixture/environment = loc.return_air() + var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick + switch(environment.temperature) + if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage + damage_chance = 10 + if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage + damage_chance = 25 + if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage + damage_chance = 50 + if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick. + damage_chance = 100 + if (damage_chance && prob(damage_chance)) + integrity = between(0, integrity - 1, 100) + + + if(delay > 0) + delay-- + else if(on) + produce_heat() + delay = initial(delay) + + + +/obj/machinery/telecomms/proc/produce_heat() + if (!produces_heat) + return + + if (!use_power) + return + + if(!(stat & (NOPOWER|BROKEN))) + var/turf/simulated/L = loc + if(istype(L)) + var/datum/gas_mixture/env = L.return_air() + + var/transfer_moles = 0.25 * env.total_moles + + var/datum/gas_mixture/removed = env.remove(transfer_moles) + + if(removed) + + var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source + if (traffic <= 0) + heat_produced *= 0.30 //if idle, produce less heat. + + removed.add_thermal_energy(heat_produced) + + env.merge(removed) +/* + The receiver idles and receives messages from subspace-compatible radio equipment; + primarily headsets. They then just relay this information to all linked devices, + which can would probably be network hubs. + + Link to Processor Units in case receiver can't send to bus units. +*/ + +/obj/machinery/telecomms/receiver + name = "Subspace Receiver" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "broadcast receiver" + desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 1 + produces_heat = 0 + circuit = /obj/item/weapon/circuitboard/telecomms/receiver + //Vars only used if you're using the overmap + var/overmap_range = 0 + var/overmap_range_min = 0 + var/overmap_range_max = 5 + + var/list/linked_radios_weakrefs = list() + +/obj/machinery/telecomms/receiver/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/device/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= WEAKREF(R) + +/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) + if(!on) // has to be on to receive messages + return + if(!signal) + return + if(!check_receive_level(signal)) + return + + if(signal.transmission_method == TRANSMISSION_SUBSPACE) + + if(is_freq_listening(signal)) // detect subspace signals + + //Remove the level and then start adding levels that it is being broadcasted in. + signal.data["level"] = list() + + var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) // ideally relay the copied information to relays + if(!can_send) + relay_information(signal, /obj/machinery/telecomms/bus) // Send it to a bus instead, if it's linked to one + +/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) + // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally + if(signal.transmission_method == TRANSMISSION_BLUESPACE) + var/obj/item/device/radio/R = signal.data["radio"] + + //Who're you? + if(!(WEAKREF(R) in linked_radios_weakrefs)) + signal.data["reject"] = 1 + return 0 + + //We'll resend this for you + signal.data["level"] = z + signal.transmission_method = TRANSMISSION_SUBSPACE + return 1 + + //Where can we hear? + var/list/listening_levels = using_map.get_map_levels(listening_level, TRUE, overmap_range) + + // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it + if(!(signal.data["level"] in listening_levels)) + for(var/obj/machinery/telecomms/hub/H in links) + var/list/relayed_levels = list() + for(var/obj/machinery/telecomms/relay/R in H.links) + if(R.can_receive(signal)) + relayed_levels |= R.listening_level + if(signal.data["level"] in relayed_levels) + return 1 + return 0 + return 1 + + +/* + The HUB idles until it receives information. It then passes on that information + depending on where it came from. + + This is the heart of the Telecommunications Network, sending information where it + is needed. It mainly receives information from long-distance Relays and then sends + that information to be processed. Afterwards it gets the uncompressed information + from Servers/Buses and sends that back to the relay, to then be broadcasted. +*/ + +/obj/machinery/telecomms/hub + name = "Telecommunication Hub" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "hub" + desc = "A mighty piece of hardware used to send/receive massive amounts of data." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 1600 + machinetype = 7 + circuit = /obj/item/weapon/circuitboard/telecomms/hub + long_range_link = 1 + netspeed = 40 + +/obj/machinery/telecomms/hub/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + if(is_freq_listening(signal)) + if(istype(machine_from, /obj/machinery/telecomms/receiver)) + //If the signal is compressed, send it to the bus. + relay_information(signal, /obj/machinery/telecomms/bus, 1) // ideally relay the copied information to bus units + else + // Get a list of relays that we're linked to, then send the signal to their levels. + relay_information(signal, /obj/machinery/telecomms/relay, 1) + relay_information(signal, /obj/machinery/telecomms/broadcaster, 1) // Send it to a broadcaster. + + +/* + The relay idles until it receives information. It then passes on that information + depending on where it came from. + + The relay is needed in order to send information pass Z levels. It must be linked + with a HUB, the only other machine that can send/receive pass Z levels. +*/ + +/obj/machinery/telecomms/relay + name = "Telecommunication Relay" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "relay" + desc = "A mighty piece of hardware used to send massive amounts of data far away." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 8 + produces_heat = 0 + circuit = /obj/item/weapon/circuitboard/telecomms/relay + netspeed = 5 + long_range_link = 1 + var/broadcasting = 1 + var/receiving = 1 + +/obj/machinery/telecomms/relay/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/relay/forceMove(var/newloc) + . = ..(newloc) + listening_level = z + +/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + // Add our level and send it back + if(can_send(signal)) + signal.data["level"] |= using_map.get_map_levels(listening_level) + +// Checks to see if it can send/receive. + +/obj/machinery/telecomms/relay/proc/can(datum/signal/signal) + if(!on) + return 0 + if(!is_freq_listening(signal)) + return 0 + return 1 + +/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal) + if(!can(signal)) + return 0 + return broadcasting + +/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal) + if(!can(signal)) + return 0 + return receiving + +/* + The bus mainframe idles and waits for hubs to relay them signals. They act + as junctions for the network. + + They transfer uncompressed subspace packets to processor units, and then take + the processed packet to a server for logging. + + Link to a subspace hub if it can't send to a server. +*/ + +/obj/machinery/telecomms/bus + name = "Bus Mainframe" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "bus" + desc = "A mighty piece of hardware used to send massive amounts of data quickly." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 1000 + machinetype = 2 + circuit = /obj/item/weapon/circuitboard/telecomms/bus + netspeed = 40 + var/change_frequency = 0 + +/obj/machinery/telecomms/bus/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(is_freq_listening(signal)) + + if(change_frequency) + signal.frequency = change_frequency + + if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it + // send to one linked processor unit + var/send_to_processor = relay_information(signal, /obj/machinery/telecomms/processor) + + if(send_to_processor) + return + // failed to send to a processor, relay information anyway + signal.data["slow"] += rand(1, 5) // slow the signal down only slightly + src.receive_information(signal, src) + + // Try sending it! + var/list/try_send = list(/obj/machinery/telecomms/server, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster, /obj/machinery/telecomms/bus) + var/i = 0 + for(var/send in try_send) + if(i) + signal.data["slow"] += rand(0, 1) // slow the signal down only slightly + i++ + var/can_send = relay_information(signal, send) + if(can_send) + break + + + +/* + The processor is a very simple machine that decompresses subspace signals and + transfers them back to the original bus. It is essential in producing audible + data. + + Link to servers if bus is not present +*/ + +/obj/machinery/telecomms/processor + name = "Processor Unit" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "processor" + desc = "This machine is used to process large quantities of information." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 600 + machinetype = 3 + delay = 5 + circuit = /obj/item/weapon/circuitboard/telecomms/processor + var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals + +/obj/machinery/telecomms/processor/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/processor/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(is_freq_listening(signal)) + + if(process_mode) + signal.data["compression"] = 0 // uncompress subspace signal + else + signal.data["compression"] = 100 // even more compressed signal + + if(istype(machine_from, /obj/machinery/telecomms/bus)) + relay_direct_information(signal, machine_from) // send the signal back to the machine + else // no bus detected - send the signal to servers instead + signal.data["slow"] += rand(5, 10) // slow the signal down + relay_information(signal, /obj/machinery/telecomms/server) + + +/* + The server logs all traffic and signal data. Once it records the signal, it sends + it to the subspace broadcaster. + + Store a maximum of 100 logs and then deletes them. +*/ + + +/obj/machinery/telecomms/server + name = "Telecommunication Server" + //icon = 'icons/obj/stationobjs.dmi' //VOREStation Removal - use parent icon + icon_state = "comm_server" + desc = "A machine used to store data and network statistics." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 300 + machinetype = 4 + circuit = /obj/item/weapon/circuitboard/telecomms/server + var/list/log_entries = list() + var/list/stored_names = list() + var/list/TrafficActions = list() + var/logs = 0 // number of logs + var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes) + + var/list/memory = list() // stored memory + var/rawcode = "" // the code to compile (raw text) + var/datum/TCS_Compiler/Compiler // the compiler that compiles and runs the code + var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up + + var/encryption = "null" // encryption key: ie "password" + var/salt = "null" // encryption salt: ie "123comsat" + // would add up to md5("password123comsat") + var/obj/item/device/radio/headset/server_radio = null + +/obj/machinery/telecomms/server/New() + ..() + Compiler = new() + Compiler.Holder = src + server_radio = new() + +/obj/machinery/telecomms/server/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) + + if(signal.data["message"]) + + if(is_freq_listening(signal)) + + if(traffic > 0) + totaltraffic += traffic // add current traffic to total traffic + + //Is this a test signal? Bypass logging + if(signal.data["type"] != SIGNAL_TEST) + + // If signal has a message and appropriate frequency + + update_logs() + + var/datum/comm_log_entry/log = new + var/mob/M = signal.data["mob"] + + // Copy the signal.data entries we want + log.parameters["mobtype"] = signal.data["mobtype"] + log.parameters["job"] = signal.data["job"] + log.parameters["key"] = signal.data["key"] + log.parameters["vmessage"] = multilingual_to_message(signal.data["message"]) + log.parameters["vname"] = signal.data["vname"] + log.parameters["message"] = multilingual_to_message(signal.data["message"]) + log.parameters["name"] = signal.data["name"] + log.parameters["realname"] = signal.data["realname"] + log.parameters["timecode"] = worldtime2stationtime(world.time) + + var/race = "unknown" + if(ishuman(M)) + var/mob/living/carbon/human/H = M + race = "[H.species.name]" + log.parameters["intelligible"] = 1 + else if(isbrain(M)) + race = "Brain" + log.parameters["intelligible"] = 1 + else if(M.isMonkey()) + race = "Monkey" + else if(issilicon(M)) + race = "Artificial Life" + log.parameters["intelligible"] = 1 + else if(isslime(M)) + race = "Slime" + else if(isanimal(M)) + race = "Domestic Animal" + + log.parameters["race"] = race + + if(!istype(M, /mob/new_player) && M) + log.parameters["uspeech"] = M.universal_speak + else + log.parameters["uspeech"] = 0 + + // If the signal is still compressed, make the log entry gibberish + if(signal.data["compression"] > 0) + log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50) + log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) + log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) + log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) + log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) + log.input_type = "Corrupt File" + + // Log and store everything that needs to be logged + log_entries.Add(log) + if(!(signal.data["name"] in stored_names)) + stored_names.Add(signal.data["name"]) + logs++ + signal.data["server"] = src + + // Give the log a name + var/identifier = num2text( rand(-1000,1000) + world.time ) + log.name = "data packet ([md5(identifier)])" + + if(Compiler && autoruncode) + Compiler.Run(signal) // execute the code + + var/can_send = relay_information(signal, /obj/machinery/telecomms/hub) + if(!can_send) + relay_information(signal, /obj/machinery/telecomms/broadcaster) + + +/obj/machinery/telecomms/server/proc/setcode(var/t) + if(t) + if(istext(t)) + rawcode = t + +/obj/machinery/telecomms/server/proc/compile() + if(Compiler) + return Compiler.Compile(rawcode) + +/obj/machinery/telecomms/server/proc/update_logs() + // start deleting the very first log entry + if(logs >= 400) + for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it + var/datum/comm_log_entry/L = log_entries[i] + if(L.garbage_collector) + log_entries.Remove(L) + logs-- + break + +/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input) + var/datum/comm_log_entry/log = new + var/identifier = num2text( rand(-1000,1000) + world.time ) + log.name = "[input] ([md5(identifier)])" + log.input_type = input + log.parameters["message"] = content + log.parameters["timecode"] = stationtime2text() + log_entries.Add(log) + update_logs() + + + + +// Simple log entry datum + +/datum/comm_log_entry + var/parameters = list() // carbon-copy to signal.data[] + var/name = "data packet (#)" + var/garbage_collector = 1 // if set to 0, will not be garbage collected + var/input_type = "Speech File" + +//ARFS EDIT - Commented out to add exonet relay +//Generic telecomm connectivity test proc +// /proc/can_telecomm(var/atom/A, var/atom/B, var/ad_hoc = FALSE) +// if(!A || !B) +// log_debug("can_telecomm(): Undefined endpoints!") +// return FALSE + +// //Can't in this case, obviously! +// if(is_jammed(A) || is_jammed(B)) +// return FALSE + +// //Items don't have a Z when inside an object or mob +// var/turf/src_z = get_z(A) +// var/turf/dst_z = get_z(B) + +// //Nullspace, probably. +// if(!src_z || !dst_z) +// return FALSE + +// //We can do the simple check first, if you have ad_hoc radios. +// if(ad_hoc && src_z == dst_z) +// return TRUE + +// return src_z in using_map.get_map_levels(dst_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) // END ARFS EDIT \ No newline at end of file diff --git a/code/game/machinery/telecomms/telemonitor.dm b/code/game/machinery/telecomms/telemonitor.dm index cf9ab2096dc..c9a2678af84 100644 --- a/code/game/machinery/telecomms/telemonitor.dm +++ b/code/game/machinery/telecomms/telemonitor.dm @@ -1,129 +1,129 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - - -/* - Telecomms monitor tracks the overall trafficing of a telecommunications network - and displays a heirarchy of linked machines. -*/ - -/obj/machinery/computer/telecomms/monitor - name = "Telecommunications Monitor" - desc = "Used to traverse a telecommunication network. Helpful for debugging connection issues." - icon_screen = "comm_monitor" - - var/screen = 0 // the screen number: - var/list/machinelist = list() // the machines located by the computer - var/obj/machinery/telecomms/SelectedMachine - circuit = /obj/item/weapon/circuitboard/comm_monitor - - var/network = "NULL" // the network to probe - - var/list/temp = null // temporary feedback messages - -/obj/machinery/computer/telecomms/monitor/tgui_data(mob/user) - var/list/data = list() - - data["network"] = network - data["temp"] = temp - - var/list/machinelistData = list() - for(var/obj/machinery/telecomms/T in machinelist) - machinelistData.Add(list(list( - "id" = T.id, - "name" = T.name, - ))) - data["machinelist"] = machinelistData - - data["selectedMachine"] = null - if(SelectedMachine) - data["selectedMachine"] = list( - "id" = SelectedMachine.id, - "name" = SelectedMachine.name, - ) - var/list/links = list() - for(var/obj/machinery/telecomms/T in SelectedMachine.links) - if(!T.hide) - links.Add(list(list( - "id" = T.id, - "name" = T.name - ))) - data["selectedMachine"]["links"] = links - return data - -/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user) - if(stat & (BROKEN|NOPOWER)) - return - tgui_interact(user) - -/obj/machinery/computer/telecomms/monitor/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TelecommsMachineBrowser", name) - ui.open() - -/obj/machinery/computer/telecomms/monitor/tgui_act(action, params) - if(..()) - return TRUE - - add_fingerprint(usr) - - switch(action) - if("view") - for(var/obj/machinery/telecomms/T in machinelist) - if(T.id == params["id"]) - SelectedMachine = T - break - . = TRUE - - if("mainmenu") - SelectedMachine = null - . = TRUE - - if("release") - machinelist = list() - SelectedMachine = null - . = TRUE - - if("scan") - if(machinelist.len > 0) - set_temp("FAILED: CANNOT PROBE WHEN BUFFER FULL", "bad") - return TRUE - - for(var/obj/machinery/telecomms/T in range(25, src)) - if(T.network == network) - machinelist.Add(T) - - if(!machinelist.len) - set_temp("FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\]", "bad") - else - set_temp("[machinelist.len] ENTITIES LOCATED & BUFFERED", "good") - . = TRUE - - if("network") - var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) - newnet = sanitize(newnet,15) //Honestly, I'd be amazed if someone managed to do HTML in 15 chars. - if(newnet && ((usr in range(1, src) || issilicon(usr)))) - if(length(newnet) > 15) - set_temp("FAILED: NETWORK TAG STRING TOO LENGTHY", "bad") - return TRUE - network = newnet - machinelist = list() - set_temp("NEW NETWORK TAG SET IN ADDRESS \[[network]\]", "good") - - . = TRUE - - if("cleartemp") - temp = null - . = TRUE - - -/obj/machinery/computer/telecomms/monitor/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - playsound(src, 'sound/effects/sparks4.ogg', 75, 1) - emagged = 1 - to_chat(user, "You you disable the security protocols") - src.updateUsrDialog() - return 1 - -/obj/machinery/computer/telecomms/monitor/proc/set_temp(var/text, var/color = "average") +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + + +/* + Telecomms monitor tracks the overall trafficing of a telecommunications network + and displays a heirarchy of linked machines. +*/ + +/obj/machinery/computer/telecomms/monitor + name = "Telecommunications Monitor" + desc = "Used to traverse a telecommunication network. Helpful for debugging connection issues." + icon_screen = "comm_monitor" + + var/screen = 0 // the screen number: + var/list/machinelist = list() // the machines located by the computer + var/obj/machinery/telecomms/SelectedMachine + circuit = /obj/item/weapon/circuitboard/comm_monitor + + var/network = "NULL" // the network to probe + + var/list/temp = null // temporary feedback messages + +/obj/machinery/computer/telecomms/monitor/tgui_data(mob/user) + var/list/data = list() + + data["network"] = network + data["temp"] = temp + + var/list/machinelistData = list() + for(var/obj/machinery/telecomms/T in machinelist) + machinelistData.Add(list(list( + "id" = T.id, + "name" = T.name, + ))) + data["machinelist"] = machinelistData + + data["selectedMachine"] = null + if(SelectedMachine) + data["selectedMachine"] = list( + "id" = SelectedMachine.id, + "name" = SelectedMachine.name, + ) + var/list/links = list() + for(var/obj/machinery/telecomms/T in SelectedMachine.links) + if(!T.hide) + links.Add(list(list( + "id" = T.id, + "name" = T.name + ))) + data["selectedMachine"]["links"] = links + return data + +/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/telecomms/monitor/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TelecommsMachineBrowser", name) + ui.open() + +/obj/machinery/computer/telecomms/monitor/tgui_act(action, params) + if(..()) + return TRUE + + add_fingerprint(usr) + + switch(action) + if("view") + for(var/obj/machinery/telecomms/T in machinelist) + if(T.id == params["id"]) + SelectedMachine = T + break + . = TRUE + + if("mainmenu") + SelectedMachine = null + . = TRUE + + if("release") + machinelist = list() + SelectedMachine = null + . = TRUE + + if("scan") + if(machinelist.len > 0) + set_temp("FAILED: CANNOT PROBE WHEN BUFFER FULL", "bad") + return TRUE + + for(var/obj/machinery/telecomms/T in range(25, src)) + if(T.network == network) + machinelist.Add(T) + + if(!machinelist.len) + set_temp("FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\]", "bad") + else + set_temp("[machinelist.len] ENTITIES LOCATED & BUFFERED", "good") + . = TRUE + + if("network") + var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) + newnet = sanitize(newnet,15) //Honestly, I'd be amazed if someone managed to do HTML in 15 chars. + if(newnet && ((usr in range(1, src) || issilicon(usr)))) + if(length(newnet) > 15) + set_temp("FAILED: NETWORK TAG STRING TOO LENGTHY", "bad") + return TRUE + network = newnet + machinelist = list() + set_temp("NEW NETWORK TAG SET IN ADDRESS \[[network]\]", "good") + + . = TRUE + + if("cleartemp") + temp = null + . = TRUE + + +/obj/machinery/computer/telecomms/monitor/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + playsound(src, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You you disable the security protocols") + src.updateUsrDialog() + return 1 + +/obj/machinery/computer/telecomms/monitor/proc/set_temp(var/text, var/color = "average") temp = list("color" = color, "text" = text) \ No newline at end of file diff --git a/code/game/machinery/telecomms/traffic_control.dm b/code/game/machinery/telecomms/traffic_control.dm index 17dbed4e2cc..9f14a300842 100644 --- a/code/game/machinery/telecomms/traffic_control.dm +++ b/code/game/machinery/telecomms/traffic_control.dm @@ -1,218 +1,218 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - - - - - -/obj/machinery/computer/telecomms/traffic - name = "Telecommunications Traffic Control" - desc = "Used to upload code to telecommunication consoles for execution." - icon_screen = "generic" - - var/screen = 0 // the screen number: - var/list/servers = list() // the servers located by the computer - var/mob/editingcode - var/mob/lasteditor - var/list/viewingcode = list() - var/obj/machinery/telecomms/server/SelectedServer - circuit = /obj/item/weapon/circuitboard/comm_traffic - req_access = list(access_tcomsat) - - var/network = "NULL" // the network to probe - var/temp = "" // temporary feedback messages - - var/storedcode = "" // code stored - - -/obj/machinery/computer/telecomms/traffic/proc/update_ide() - - // loop if there's someone manning the keyboard - while(editingcode) - if(!editingcode.client) - editingcode = null - break - - // For the typer, the input is enabled. Buffer the typed text - if(editingcode) - storedcode = "[winget(editingcode, "tcscode", "text")]" - if(editingcode) // double if's to work around a runtime error - winset(editingcode, "tcscode", "is-disabled=false") - - // If the player's not manning the keyboard anymore, adjust everything - if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode))) - if(editingcode) - winshow(editingcode, "Telecomms IDE", 0) // hide the window! - editingcode = null - break - - // For other people viewing the typer type code, the input is disabled and they can only view the code - // (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once) - - if(length(viewingcode)) - // This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element - var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") - showcode = replacetext(storedcode, "\"", "\\\"") - - for(var/mob/M in viewingcode) - - if( (M.machine == src && (M in view(1, src)) ) || issilicon(M)) - winset(M, "tcscode", "is-disabled=true") - winset(M, "tcscode", "text=\"[showcode]\"") - else - viewingcode.Remove(M) - winshow(M, "Telecomms IDE", 0) // hide the window! - - sleep(5) - - if(length(viewingcode) > 0) - editingcode = pick(viewingcode) - viewingcode.Remove(editingcode) - update_ide() - - - - -/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "Telecommunication Traffic Control
                    Telecommunications Traffic Control
                    " - - switch(screen) - - - // --- Main Menu --- - - if(0) - dat += "
                    [temp]
                    " - dat += "
                    Current Network: [network]
                    " - if(servers.len) - dat += "
                    Detected Telecommunication Servers:
                      " - for(var/obj/machinery/telecomms/T in servers) - dat += "
                    • \ref[T] [T.name] ([T.id])
                    • " - dat += "
                    " - dat += "
                    \[Flush Buffer\]" - - else - dat += "
                    No servers detected. Scan for servers: \[Scan\]" - - - // --- Viewing Server --- - - if(1) - dat += "
                    [temp]
                    " - dat += "
                    \[Main Menu\] \[Refresh\]
                    " - dat += "
                    Current Network: [network]" - dat += "
                    Selected Server: [SelectedServer.id]

                    " - dat += "
                    \[Edit Code\]" - dat += "
                    Signal Execution: " - if(SelectedServer.autoruncode) - dat += "ALWAYS" - else - dat += "NEVER" - - - user << browse(dat, "window=traffic_control;size=575x400") - onclose(user, "server_control") - - temp = "" - return - - -/obj/machinery/computer/telecomms/traffic/Topic(href, href_list) - if(..()) - return - - - add_fingerprint(usr) - usr.set_machine(src) - if(!src.allowed(usr) && !emagged) - to_chat(usr, "ACCESS DENIED.") - return - - if(href_list["viewserver"]) - screen = 1 - for(var/obj/machinery/telecomms/T in servers) - if(T.id == href_list["viewserver"]) - SelectedServer = T - break - - if(href_list["operation"]) - switch(href_list["operation"]) - - if("release") - servers = list() - screen = 0 - - if("mainmenu") - screen = 0 - - if("scan") - if(servers.len > 0) - temp = "- FAILED: CANNOT PROBE WHEN BUFFER FULL -" - - else - for(var/obj/machinery/telecomms/server/T in range(25, src)) - if(T.network == network) - servers.Add(T) - - if(!servers.len) - temp = "- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -" - else - temp = "- [servers.len] SERVERS PROBED & BUFFERED -" - - screen = 0 - - if("editcode") - if(editingcode == usr) return - if(usr in viewingcode) return - - if(!editingcode) - lasteditor = usr - editingcode = usr - winshow(editingcode, "Telecomms IDE", 1) // show the IDE - winset(editingcode, "tcscode", "is-disabled=false") - winset(editingcode, "tcscode", "text=\"\"") - var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") - showcode = replacetext(storedcode, "\"", "\\\"") - winset(editingcode, "tcscode", "text=\"[showcode]\"") - spawn() - update_ide() - - else - viewingcode.Add(usr) - winshow(usr, "Telecomms IDE", 1) // show the IDE - winset(usr, "tcscode", "is-disabled=true") - winset(editingcode, "tcscode", "text=\"\"") - var/showcode = replacetext(storedcode, "\"", "\\\"") - winset(usr, "tcscode", "text=\"[showcode]\"") - - if("togglerun") - SelectedServer.autoruncode = !(SelectedServer.autoruncode) - - if(href_list["network"]) - - var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) - newnet = sanitize(newnet,15) - - if(newnet && ((usr in range(1, src) || issilicon(usr)))) - if(length(newnet) > 15) - temp = "- FAILED: NETWORK TAG STRING TOO LENGHTLY -" - - else - - network = newnet - screen = 0 - servers = list() - temp = "- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -" - - updateUsrDialog() - return - -/obj/machinery/computer/telecomms/traffic/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - playsound(src, 'sound/effects/sparks4.ogg', 75, 1) - emagged = 1 - to_chat(user, "You you disable the security protocols") - src.updateUsrDialog() +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + + + + + +/obj/machinery/computer/telecomms/traffic + name = "Telecommunications Traffic Control" + desc = "Used to upload code to telecommunication consoles for execution." + icon_screen = "generic" + + var/screen = 0 // the screen number: + var/list/servers = list() // the servers located by the computer + var/mob/editingcode + var/mob/lasteditor + var/list/viewingcode = list() + var/obj/machinery/telecomms/server/SelectedServer + circuit = /obj/item/weapon/circuitboard/comm_traffic + req_access = list(access_tcomsat) + + var/network = "NULL" // the network to probe + var/temp = "" // temporary feedback messages + + var/storedcode = "" // code stored + + +/obj/machinery/computer/telecomms/traffic/proc/update_ide() + + // loop if there's someone manning the keyboard + while(editingcode) + if(!editingcode.client) + editingcode = null + break + + // For the typer, the input is enabled. Buffer the typed text + if(editingcode) + storedcode = "[winget(editingcode, "tcscode", "text")]" + if(editingcode) // double if's to work around a runtime error + winset(editingcode, "tcscode", "is-disabled=false") + + // If the player's not manning the keyboard anymore, adjust everything + if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode))) + if(editingcode) + winshow(editingcode, "Telecomms IDE", 0) // hide the window! + editingcode = null + break + + // For other people viewing the typer type code, the input is disabled and they can only view the code + // (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once) + + if(length(viewingcode)) + // This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element + var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") + showcode = replacetext(storedcode, "\"", "\\\"") + + for(var/mob/M in viewingcode) + + if( (M.machine == src && (M in view(1, src)) ) || issilicon(M)) + winset(M, "tcscode", "is-disabled=true") + winset(M, "tcscode", "text=\"[showcode]\"") + else + viewingcode.Remove(M) + winshow(M, "Telecomms IDE", 0) // hide the window! + + sleep(5) + + if(length(viewingcode) > 0) + editingcode = pick(viewingcode) + viewingcode.Remove(editingcode) + update_ide() + + + + +/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "Telecommunication Traffic Control
                    Telecommunications Traffic Control
                    " + + switch(screen) + + + // --- Main Menu --- + + if(0) + dat += "
                    [temp]
                    " + dat += "
                    Current Network: [network]
                    " + if(servers.len) + dat += "
                    Detected Telecommunication Servers:
                      " + for(var/obj/machinery/telecomms/T in servers) + dat += "
                    • \ref[T] [T.name] ([T.id])
                    • " + dat += "
                    " + dat += "
                    \[Flush Buffer\]" + + else + dat += "
                    No servers detected. Scan for servers: \[Scan\]" + + + // --- Viewing Server --- + + if(1) + dat += "
                    [temp]
                    " + dat += "
                    \[Main Menu\] \[Refresh\]
                    " + dat += "
                    Current Network: [network]" + dat += "
                    Selected Server: [SelectedServer.id]

                    " + dat += "
                    \[Edit Code\]" + dat += "
                    Signal Execution: " + if(SelectedServer.autoruncode) + dat += "ALWAYS" + else + dat += "NEVER" + + + user << browse(dat, "window=traffic_control;size=575x400") + onclose(user, "server_control") + + temp = "" + return + + +/obj/machinery/computer/telecomms/traffic/Topic(href, href_list) + if(..()) + return + + + add_fingerprint(usr) + usr.set_machine(src) + if(!src.allowed(usr) && !emagged) + to_chat(usr, "ACCESS DENIED.") + return + + if(href_list["viewserver"]) + screen = 1 + for(var/obj/machinery/telecomms/T in servers) + if(T.id == href_list["viewserver"]) + SelectedServer = T + break + + if(href_list["operation"]) + switch(href_list["operation"]) + + if("release") + servers = list() + screen = 0 + + if("mainmenu") + screen = 0 + + if("scan") + if(servers.len > 0) + temp = "- FAILED: CANNOT PROBE WHEN BUFFER FULL -" + + else + for(var/obj/machinery/telecomms/server/T in range(25, src)) + if(T.network == network) + servers.Add(T) + + if(!servers.len) + temp = "- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -" + else + temp = "- [servers.len] SERVERS PROBED & BUFFERED -" + + screen = 0 + + if("editcode") + if(editingcode == usr) return + if(usr in viewingcode) return + + if(!editingcode) + lasteditor = usr + editingcode = usr + winshow(editingcode, "Telecomms IDE", 1) // show the IDE + winset(editingcode, "tcscode", "is-disabled=false") + winset(editingcode, "tcscode", "text=\"\"") + var/showcode = replacetext(storedcode, "\\\"", "\\\\\"") + showcode = replacetext(storedcode, "\"", "\\\"") + winset(editingcode, "tcscode", "text=\"[showcode]\"") + spawn() + update_ide() + + else + viewingcode.Add(usr) + winshow(usr, "Telecomms IDE", 1) // show the IDE + winset(usr, "tcscode", "is-disabled=true") + winset(editingcode, "tcscode", "text=\"\"") + var/showcode = replacetext(storedcode, "\"", "\\\"") + winset(usr, "tcscode", "text=\"[showcode]\"") + + if("togglerun") + SelectedServer.autoruncode = !(SelectedServer.autoruncode) + + if(href_list["network"]) + + var/newnet = tgui_input_text(usr, "Which network do you want to view?", "Comm Monitor", network, 15) + newnet = sanitize(newnet,15) + + if(newnet && ((usr in range(1, src) || issilicon(usr)))) + if(length(newnet) > 15) + temp = "- FAILED: NETWORK TAG STRING TOO LENGHTLY -" + + else + + network = newnet + screen = 0 + servers = list() + temp = "- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -" + + updateUsrDialog() + return + +/obj/machinery/computer/telecomms/traffic/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + playsound(src, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You you disable the security protocols") + src.updateUsrDialog() return 1 \ No newline at end of file diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm index 43bc97c20a2..8a09b6f048a 100644 --- a/code/game/machinery/transformer.dm +++ b/code/game/machinery/transformer.dm @@ -1,56 +1,56 @@ -/obj/machinery/transformer - name = "Automatic Robotic Factory 5000" - desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'human go in, robot come out', human must be lying down and alive." - icon = 'icons/obj/recycling.dmi' - icon_state = "separator-AO1" - layer = MOB_LAYER+1 // Overhead - anchored = TRUE - density = TRUE - var/transform_dead = 0 - var/transform_standing = 0 - -/obj/machinery/transformer/New() - // On us - ..() - new /obj/machinery/conveyor(loc, WEST, 1) - -/obj/machinery/transformer/Bumped(var/atom/movable/AM) - // HasEntered didn't like people lying down. - if(ishuman(AM)) - // Only humans can enter from the west side, while lying down. - var/move_dir = get_dir(loc, AM.loc) - var/mob/living/carbon/human/H = AM - if((transform_standing || H.lying) && move_dir == EAST)// || move_dir == WEST) - AM.loc = src.loc - transform(AM) - -/obj/machinery/transformer/proc/transform(var/mob/living/carbon/human/H) - if(stat & (BROKEN|NOPOWER)) - return - if(!transform_dead && H.stat == DEAD) - playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0) - return - playsound(src, 'sound/items/Welder.ogg', 50, 1) - use_power(5000) // Use a lot of power. - var/mob/living/silicon/robot = H.Robotize() - robot.SetLockDown() - spawn(50) // So he can't jump out the gate right away. - playsound(src, 'sound/machines/ping.ogg', 50, 0) - if(robot) - robot.SetLockDown(0) - -/obj/machinery/transformer/conveyor/New() - ..() - var/turf/T = loc - if(T) - // Spawn Conveyour Belts - - //East - var/turf/east = locate(T.x + 1, T.y, T.z) - if(istype(east, /turf/simulated/floor)) - new /obj/machinery/conveyor(east, WEST, 1) - - // West - var/turf/west = locate(T.x - 1, T.y, T.z) - if(istype(west, /turf/simulated/floor)) +/obj/machinery/transformer + name = "Automatic Robotic Factory 5000" + desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'human go in, robot come out', human must be lying down and alive." + icon = 'icons/obj/recycling.dmi' + icon_state = "separator-AO1" + layer = MOB_LAYER+1 // Overhead + anchored = TRUE + density = TRUE + var/transform_dead = 0 + var/transform_standing = 0 + +/obj/machinery/transformer/New() + // On us + ..() + new /obj/machinery/conveyor(loc, WEST, 1) + +/obj/machinery/transformer/Bumped(var/atom/movable/AM) + // HasEntered didn't like people lying down. + if(ishuman(AM)) + // Only humans can enter from the west side, while lying down. + var/move_dir = get_dir(loc, AM.loc) + var/mob/living/carbon/human/H = AM + if((transform_standing || H.lying) && move_dir == EAST)// || move_dir == WEST) + AM.loc = src.loc + transform(AM) + +/obj/machinery/transformer/proc/transform(var/mob/living/carbon/human/H) + if(stat & (BROKEN|NOPOWER)) + return + if(!transform_dead && H.stat == DEAD) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0) + return + playsound(src, 'sound/items/Welder.ogg', 50, 1) + use_power(5000) // Use a lot of power. + var/mob/living/silicon/robot = H.Robotize() + robot.SetLockDown() + spawn(50) // So he can't jump out the gate right away. + playsound(src, 'sound/machines/ping.ogg', 50, 0) + if(robot) + robot.SetLockDown(0) + +/obj/machinery/transformer/conveyor/New() + ..() + var/turf/T = loc + if(T) + // Spawn Conveyour Belts + + //East + var/turf/east = locate(T.x + 1, T.y, T.z) + if(istype(east, /turf/simulated/floor)) + new /obj/machinery/conveyor(east, WEST, 1) + + // West + var/turf/west = locate(T.x - 1, T.y, T.z) + if(istype(west, /turf/simulated/floor)) new /obj/machinery/conveyor(west, WEST, 1) \ No newline at end of file diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm index be00cab96a0..eb3f4d428d8 100644 --- a/code/game/machinery/wall_frames.dm +++ b/code/game/machinery/wall_frames.dm @@ -1,150 +1,150 @@ -/obj/item/frame - name = "frame parts" - desc = "Used for building frames." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "frame_bitem" - var/build_machine_type - var/build_wall_only = FALSE - var/refund_amt = 5 - var/refund_type = /obj/item/stack/material/steel - var/reverse = 0 //if resulting object faces opposite its dir (like light fixtures) - var/list/frame_types_floor - var/list/frame_types_wall - -/obj/item/frame/proc/update_type_list() - if(!frame_types_floor) - frame_types_floor = construction_frame_floor - if(!frame_types_wall) - frame_types_wall = construction_frame_wall - -/obj/item/frame/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - new refund_type(get_turf(src.loc), refund_amt) - qdel(src) - return - ..() - -/obj/item/frame/attack_self(mob/user as mob) - ..() - update_type_list() - var/datum/frame/frame_types/frame_type - if(!build_machine_type && !build_wall_only) - var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_floor) - if(!response) - return - frame_type = response - - build_machine_type = /obj/structure/frame - - if(frame_type.frame_size != 5) - new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) - - var/ndir - ndir = user.dir - if(!(ndir in cardinal)) - return - - var/obj/machinery/M = new build_machine_type(get_turf(src.loc), ndir, 1, frame_type) - M.fingerprints = fingerprints - M.fingerprintshidden = fingerprintshidden - M.fingerprintslast = fingerprintslast - if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans - user.drop_item() - qdel(src) - -/obj/item/frame/proc/try_build(turf/on_wall, mob/user as mob) - update_type_list() - - if(get_dist(on_wall, user)>1) - return - - var/ndir - if(reverse) - ndir = get_dir(user, on_wall) - else - ndir = get_dir(on_wall, user) - - if(!(ndir in cardinal)) - return - - var/turf/loc = get_turf(user) - var/area/A = loc.loc - if(!istype(loc, /turf/simulated/floor)) - to_chat(user, "\The frame cannot be placed on this spot.") - return - - if(A.requires_power == 0 || A.name == "Space") - to_chat(user, "\The [src] Alarm cannot be placed in this area.") - return - - if(gotwallitem(loc, ndir)) - to_chat(user, "There's already an item on this wall!") - return - - var/datum/frame/frame_types/frame_type - if(!build_machine_type) - var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_wall) - if(!response) - return - frame_type = response - - build_machine_type = /obj/structure/frame - - if(frame_type.frame_size != 5) - new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) - - var/obj/machinery/M = new build_machine_type(loc, ndir, 1, frame_type) - M.fingerprints = fingerprints - M.fingerprintshidden = fingerprintshidden - M.fingerprintslast = fingerprintslast - if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans - user.drop_item() - qdel(src) - -/obj/item/frame/light - name = "light fixture frame" - desc = "Used for building lights." - icon = 'icons/obj/lighting.dmi' - icon_state = "tube-construct-item" - refund_amt = 2 - build_machine_type = /obj/machinery/light_construct - reverse = 1 - -/obj/item/frame/light/small - name = "small light fixture frame" - icon_state = "bulb-construct-item" - refund_amt = 1 - build_machine_type = /obj/machinery/light_construct/small - -/obj/item/frame/extinguisher_cabinet - name = "extinguisher cabinet frame" - desc = "Used for building fire extinguisher cabinets." - icon = 'icons/obj/closet.dmi' - icon_state = "extinguisher_empty" - refund_amt = 4 - build_machine_type = /obj/structure/extinguisher_cabinet - -/obj/item/frame/noticeboard - name = "noticeboard frame" - desc = "Used for building noticeboards." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nboard00" - refund_amt = 4 - refund_type = /obj/item/stack/material/wood - build_machine_type = /obj/structure/noticeboard - -/obj/item/frame/mirror - name = "mirror frame" - desc = "Used for building mirrors." - icon = 'icons/obj/watercloset.dmi' - icon_state = "mirror_frame" - refund_amt = 1 - build_machine_type = /obj/structure/mirror - -/obj/item/frame/fireaxe_cabinet - name = "fire axe cabinet frame" - desc = "Used for building fire axe cabinets." - icon = 'icons/obj/closet.dmi' - icon_state = "fireaxe0101" - refund_amt = 4 - build_machine_type = /obj/structure/fireaxecabinet +/obj/item/frame + name = "frame parts" + desc = "Used for building frames." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "frame_bitem" + var/build_machine_type + var/build_wall_only = FALSE + var/refund_amt = 5 + var/refund_type = /obj/item/stack/material/steel + var/reverse = 0 //if resulting object faces opposite its dir (like light fixtures) + var/list/frame_types_floor + var/list/frame_types_wall + +/obj/item/frame/proc/update_type_list() + if(!frame_types_floor) + frame_types_floor = construction_frame_floor + if(!frame_types_wall) + frame_types_wall = construction_frame_wall + +/obj/item/frame/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + new refund_type(get_turf(src.loc), refund_amt) + qdel(src) + return + ..() + +/obj/item/frame/attack_self(mob/user as mob) + ..() + update_type_list() + var/datum/frame/frame_types/frame_type + if(!build_machine_type && !build_wall_only) + var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_floor) + if(!response) + return + frame_type = response + + build_machine_type = /obj/structure/frame + + if(frame_type.frame_size != 5) + new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) + + var/ndir + ndir = user.dir + if(!(ndir in cardinal)) + return + + var/obj/machinery/M = new build_machine_type(get_turf(src.loc), ndir, 1, frame_type) + M.fingerprints = fingerprints + M.fingerprintshidden = fingerprintshidden + M.fingerprintslast = fingerprintslast + if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans + user.drop_item() + qdel(src) + +/obj/item/frame/proc/try_build(turf/on_wall, mob/user as mob) + update_type_list() + + if(get_dist(on_wall, user)>1) + return + + var/ndir + if(reverse) + ndir = get_dir(user, on_wall) + else + ndir = get_dir(on_wall, user) + + if(!(ndir in cardinal)) + return + + var/turf/loc = get_turf(user) + var/area/A = loc.loc + if(!istype(loc, /turf/simulated/floor)) + to_chat(user, "\The frame cannot be placed on this spot.") + return + + if(A.requires_power == 0 || A.name == "Space") + to_chat(user, "\The [src] Alarm cannot be placed in this area.") + return + + if(gotwallitem(loc, ndir)) + to_chat(user, "There's already an item on this wall!") + return + + var/datum/frame/frame_types/frame_type + if(!build_machine_type) + var/datum/frame/frame_types/response = tgui_input_list(user, "What kind of frame would you like to make?", "Frame type request", frame_types_wall) + if(!response) + return + frame_type = response + + build_machine_type = /obj/structure/frame + + if(frame_type.frame_size != 5) + new /obj/item/stack/material/steel(user.loc, (5 - frame_type.frame_size)) + + var/obj/machinery/M = new build_machine_type(loc, ndir, 1, frame_type) + M.fingerprints = fingerprints + M.fingerprintshidden = fingerprintshidden + M.fingerprintslast = fingerprintslast + if(istype(src.loc, /obj/item/weapon/gripper)) //Typical gripper shenanigans + user.drop_item() + qdel(src) + +/obj/item/frame/light + name = "light fixture frame" + desc = "Used for building lights." + icon = 'icons/obj/lighting.dmi' + icon_state = "tube-construct-item" + refund_amt = 2 + build_machine_type = /obj/machinery/light_construct + reverse = 1 + +/obj/item/frame/light/small + name = "small light fixture frame" + icon_state = "bulb-construct-item" + refund_amt = 1 + build_machine_type = /obj/machinery/light_construct/small + +/obj/item/frame/extinguisher_cabinet + name = "extinguisher cabinet frame" + desc = "Used for building fire extinguisher cabinets." + icon = 'icons/obj/closet.dmi' + icon_state = "extinguisher_empty" + refund_amt = 4 + build_machine_type = /obj/structure/extinguisher_cabinet + +/obj/item/frame/noticeboard + name = "noticeboard frame" + desc = "Used for building noticeboards." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nboard00" + refund_amt = 4 + refund_type = /obj/item/stack/material/wood + build_machine_type = /obj/structure/noticeboard + +/obj/item/frame/mirror + name = "mirror frame" + desc = "Used for building mirrors." + icon = 'icons/obj/watercloset.dmi' + icon_state = "mirror_frame" + refund_amt = 1 + build_machine_type = /obj/structure/mirror + +/obj/item/frame/fireaxe_cabinet + name = "fire axe cabinet frame" + desc = "Used for building fire axe cabinets." + icon = 'icons/obj/closet.dmi' + icon_state = "fireaxe0101" + refund_amt = 4 + build_machine_type = /obj/structure/fireaxecabinet diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 77c31874582..ca1546190a4 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -1,185 +1,185 @@ -/obj/machinery/washing_machine - name = "Washing Machine" - desc = "Not a hiding place. Unfit for pets." - icon = 'icons/obj/machines/washing_machine_vr.dmi' //VOREStation Edit - icon_state = "wm_1" //VOREStation Edit - density = TRUE - anchored = TRUE - clicksound = "button" - clickvol = 40 - - circuit = /obj/item/weapon/circuitboard/washing - var/state = 1 - //1 = empty, open door - //2 = empty, closed door - //3 = full, open door - //4 = full, closed door - //5 = running - //6 = blood, open door - //7 = blood, closed door - //8 = blood, running - var/hacked = 1 //Bleh, screw hacking, let's have it hacked by default. - //0 = not hacked - //1 = hacked - var/gibs_ready = 0 - var/obj/crayon - var/list/washing = list() - var/list/disallowed_types = list( - /obj/item/clothing/suit/space, - /obj/item/clothing/head/helmet/space - ) - -/obj/machinery/washing_machine/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/washing_machine/AltClick() - start() - -/obj/machinery/washing_machine/verb/start_washing() - set name = "Start Washing" - set category = "Object" - set src in oview(1) - start() - -/obj/machinery/washing_machine/proc/start() - - if(!istype(usr, /mob/living)) //ew ew ew usr, but it's the only way to check. - return - - if(state != 4) - to_chat(usr, "The washing machine cannot run in this state.") - return - - if(locate(/mob,washing)) - state = 8 - else - state = 5 - update_icon() - to_chat(usr, "The washing machine starts a cycle.") - playsound(src, 'sound/items/washingmachine.ogg', 50, 1, 1) - sleep(200) - for(var/atom/A in washing) - A.clean_blood() - - for(var/obj/item/I in washing) - I.decontaminate() - - //Tanning! - for(var/obj/item/stack/hairlesshide/HH in washing) - var/obj/item/stack/wetleather/WL = new(src, HH.get_amount()) - washing -= HH - HH.forceMove(get_turf(src)) - HH.use(HH.get_amount()) - - washing += WL - - if(locate(/mob,washing)) - state = 7 - gibs_ready = 1 - else - state = 4 - update_icon() - -/obj/machinery/washing_machine/verb/climb_out() - set name = "Climb out" - set category = "Object" - set src in usr.loc - - sleep(20) - if(state in list(1,3,6)) - usr.loc = src.loc - -/obj/machinery/washing_machine/update_icon() - //VOREStation Edit - cut_overlays() - icon_state = "wm_[state]" - if(panel_open) - add_overlay("panel") - //VOREStation Edit End - -/obj/machinery/washing_machine/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(state == 2 && washing.len < 1) - if(default_deconstruction_screwdriver(user, W)) - return - if(default_deconstruction_crowbar(user, W)) - return - if(default_unfasten_wrench(user, W, 40)) - return - /*if(W.is_screwdriver()) - panel = !panel - to_chat(user, "You [panel ? "open" : "close"] the [src]'s maintenance panel")*/ - if(istype(W,/obj/item/weapon/pen/crayon) || istype(W,/obj/item/weapon/stamp)) - if(state in list( 1, 3, 6)) - if(!crayon) - user.drop_item() - crayon = W - crayon.loc = src - else - ..() - else - ..() - else if(istype(W,/obj/item/weapon/grab)) - if((state == 1) && hacked) - var/obj/item/weapon/grab/G = W - if(ishuman(G.assailant) && iscorgi(G.affecting)) - G.affecting.loc = src - qdel(G) - state = 3 - else - ..() - - else if(is_type_in_list(W, disallowed_types)) - to_chat(user, "You can't fit \the [W] inside.") - return - - else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet) || istype(W, /obj/item/stack/hairlesshide)) - if(washing.len < 5) - if(state in list(1, 3)) - user.drop_item() - W.loc = src - washing += W - state = 3 - else - to_chat(user, "You can't put the item in right now.") - else - to_chat(user, "The washing machine is full.") - else - ..() - update_icon() - -/obj/machinery/washing_machine/attack_hand(mob/user as mob) - switch(state) - if(1) - state = 2 - if(2) - state = 1 - for(var/atom/movable/O in washing) - O.loc = src.loc - washing.Cut() - if(3) - state = 4 - if(4) - state = 3 - for(var/atom/movable/O in washing) - O.loc = src.loc - crayon = null - washing.Cut() - state = 1 - if(5) - to_chat(user, "The [src] is busy.") - if(6) - state = 7 - if(7) - if(gibs_ready) - gibs_ready = 0 - if(locate(/mob,washing)) - var/mob/M = locate(/mob,washing) - M.gib() - for(var/atom/movable/O in washing) - O.loc = src.loc - crayon = null - state = 1 - washing.Cut() - - update_icon() +/obj/machinery/washing_machine + name = "Washing Machine" + desc = "Not a hiding place. Unfit for pets." + icon = 'icons/obj/machines/washing_machine_vr.dmi' //VOREStation Edit + icon_state = "wm_1" //VOREStation Edit + density = TRUE + anchored = TRUE + clicksound = "button" + clickvol = 40 + + circuit = /obj/item/weapon/circuitboard/washing + var/state = 1 + //1 = empty, open door + //2 = empty, closed door + //3 = full, open door + //4 = full, closed door + //5 = running + //6 = blood, open door + //7 = blood, closed door + //8 = blood, running + var/hacked = 1 //Bleh, screw hacking, let's have it hacked by default. + //0 = not hacked + //1 = hacked + var/gibs_ready = 0 + var/obj/crayon + var/list/washing = list() + var/list/disallowed_types = list( + /obj/item/clothing/suit/space, + /obj/item/clothing/head/helmet/space + ) + +/obj/machinery/washing_machine/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/washing_machine/AltClick() + start() + +/obj/machinery/washing_machine/verb/start_washing() + set name = "Start Washing" + set category = "Object" + set src in oview(1) + start() + +/obj/machinery/washing_machine/proc/start() + + if(!istype(usr, /mob/living)) //ew ew ew usr, but it's the only way to check. + return + + if(state != 4) + to_chat(usr, "The washing machine cannot run in this state.") + return + + if(locate(/mob,washing)) + state = 8 + else + state = 5 + update_icon() + to_chat(usr, "The washing machine starts a cycle.") + playsound(src, 'sound/items/washingmachine.ogg', 50, 1, 1) + sleep(200) + for(var/atom/A in washing) + A.clean_blood() + + for(var/obj/item/I in washing) + I.decontaminate() + + //Tanning! + for(var/obj/item/stack/hairlesshide/HH in washing) + var/obj/item/stack/wetleather/WL = new(src, HH.get_amount()) + washing -= HH + HH.forceMove(get_turf(src)) + HH.use(HH.get_amount()) + + washing += WL + + if(locate(/mob,washing)) + state = 7 + gibs_ready = 1 + else + state = 4 + update_icon() + +/obj/machinery/washing_machine/verb/climb_out() + set name = "Climb out" + set category = "Object" + set src in usr.loc + + sleep(20) + if(state in list(1,3,6)) + usr.loc = src.loc + +/obj/machinery/washing_machine/update_icon() + //VOREStation Edit + cut_overlays() + icon_state = "wm_[state]" + if(panel_open) + add_overlay("panel") + //VOREStation Edit End + +/obj/machinery/washing_machine/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(state == 2 && washing.len < 1) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_unfasten_wrench(user, W, 40)) + return + /*if(W.has_tool_quality(TOOL_SCREWDRIVER)) + panel = !panel + to_chat(user, "You [panel ? "open" : "close"] the [src]'s maintenance panel")*/ + if(istype(W,/obj/item/weapon/pen/crayon) || istype(W,/obj/item/weapon/stamp)) + if(state in list( 1, 3, 6)) + if(!crayon) + user.drop_item() + crayon = W + crayon.loc = src + else + ..() + else + ..() + else if(istype(W,/obj/item/weapon/grab)) + if((state == 1) && hacked) + var/obj/item/weapon/grab/G = W + if(ishuman(G.assailant) && iscorgi(G.affecting)) + G.affecting.loc = src + qdel(G) + state = 3 + else + ..() + + else if(is_type_in_list(W, disallowed_types)) + to_chat(user, "You can't fit \the [W] inside.") + return + + else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet) || istype(W, /obj/item/stack/hairlesshide)) + if(washing.len < 5) + if(state in list(1, 3)) + user.drop_item() + W.loc = src + washing += W + state = 3 + else + to_chat(user, "You can't put the item in right now.") + else + to_chat(user, "The washing machine is full.") + else + ..() + update_icon() + +/obj/machinery/washing_machine/attack_hand(mob/user as mob) + switch(state) + if(1) + state = 2 + if(2) + state = 1 + for(var/atom/movable/O in washing) + O.loc = src.loc + washing.Cut() + if(3) + state = 4 + if(4) + state = 3 + for(var/atom/movable/O in washing) + O.loc = src.loc + crayon = null + washing.Cut() + state = 1 + if(5) + to_chat(user, "The [src] is busy.") + if(6) + state = 7 + if(7) + if(gibs_ready) + gibs_ready = 0 + if(locate(/mob,washing)) + var/mob/M = locate(/mob,washing) + M.gib() + for(var/atom/movable/O in washing) + O.loc = src.loc + crayon = null + state = 1 + washing.Cut() + + update_icon() diff --git a/code/game/mecha/combat/combat.dm b/code/game/mecha/combat/combat.dm index 88dace7ff97..d99d2c913ec 100644 --- a/code/game/mecha/combat/combat.dm +++ b/code/game/mecha/combat/combat.dm @@ -1,158 +1,158 @@ -/obj/mecha/combat - force = 30 - var/melee_cooldown = 10 - var/melee_can_hit = 1 - //var/list/destroyable_obj = list(/obj/mecha, /obj/structure/window, /obj/structure/grille, /turf/simulated/wall, /obj/structure/girder) - internal_damage_threshold = 50 - maint_access = 0 - //add_req_access = 0 - //operation_req_access = list(access_hos) - var/am = "d3c2fbcadca903a41161ccc9df9cf948" - - max_hull_equip = 2 - max_weapon_equip = 2 - max_utility_equip = 1 - max_universal_equip = 1 - max_special_equip = 1 - cargo_capacity = 1 - - encumbrance_gap = 1.5 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/reinforced, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - -/* -/obj/mecha/combat/range_action(target as obj|mob|turf) - if(internal_damage&MECHA_INT_CONTROL_LOST) - target = pick(view(3,target)) - if(selected_weapon) - selected_weapon.fire(target) - return -*/ - -/obj/mecha/combat/melee_action(atom/T) - if(internal_damage&MECHA_INT_CONTROL_LOST) - T = safepick(oview(1,src)) - if(!melee_can_hit) - return - if(istype(T, /mob/living)) - var/mob/living/M = T - if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) //Brains cannot change intents; Exo-piloting brains lack any form of physical feedback for control, limiting the ability to 'play nice'. - playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) - if(damtype == "brute") - step_away(M,src,15) - /* - if(M.stat>1) - M.gib() - melee_can_hit = 0 - if(do_after(melee_cooldown)) - melee_can_hit = 1 - return - */ - if(ishuman(T)) - var/mob/living/carbon/human/H = T - // if (M.health <= 0) return - - var/obj/item/organ/external/temp = H.get_organ(pick(BP_TORSO, BP_TORSO, BP_TORSO, BP_HEAD)) - if(temp) - var/update = 0 - switch(damtype) - if("brute") - H.Paralyse(1) - update |= temp.take_damage(rand(force/2, force), 0) - if("fire") - update |= temp.take_damage(0, rand(force/2, force)) - if("tox") - if(H.reagents) - if(H.reagents.get_reagent_amount("carpotoxin") + force < force*2) - H.reagents.add_reagent("carpotoxin", force) - if(H.reagents.get_reagent_amount("cryptobiolin") + force < force*2) - H.reagents.add_reagent("cryptobiolin", force) - if("halloss") - H.stun_effect_act(1, force / 2, BP_TORSO, src) - else - return - if(update) H.UpdateDamageIcon() - H.updatehealth() - - else - switch(damtype) - if("brute") - M.Paralyse(1) - M.take_overall_damage(rand(force/2, force)) - if("fire") - M.take_overall_damage(0, rand(force/2, force)) - if("tox") - if(M.reagents) - if(M.reagents.get_reagent_amount("carpotoxin") + force < force*2) - M.reagents.add_reagent("carpotoxin", force) - if(M.reagents.get_reagent_amount("cryptobiolin") + force < force*2) - M.reagents.add_reagent("cryptobiolin", force) - else - return - M.updatehealth() - src.occupant_message("You hit [T].") - src.visible_message("[src.name] hits [T].") - else - step_away(M,src) - src.occupant_message("You push [T] out of the way.") - src.visible_message("[src] pushes [T] out of the way.") - - melee_can_hit = 0 - if(do_after(melee_cooldown)) - melee_can_hit = 1 - return - - else - if(istype(T, /obj/machinery/disposal)) // Stops mechs from climbing into disposals - return - if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) // Don't smash unless we mean it - if(damtype == "brute") - src.occupant_message("You hit [T].") - src.visible_message("[src.name] hits [T]") - playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) - - if(istype(T, /obj/structure/girder)) - T:take_damage(force * 3) //Girders have 200 health by default. Steel, non-reinforced walls take four punches, girders take (with this value-mod) two, girders took five without. - else - T:take_damage(force) - - melee_can_hit = 0 - - if(do_after(melee_cooldown)) - melee_can_hit = 1 - return - -/obj/mecha/combat/moved_inside(var/mob/living/carbon/human/H as mob) - if(..()) - if(H.client) - H.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") - return 1 - else - return 0 - -/obj/mecha/combat/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) - if(..()) - if(occupant.client) - occupant.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") - return 1 - else - return 0 - -/obj/mecha/combat/go_out() - if(src.occupant && src.occupant.client) - src.occupant.client.mouse_pointer_icon = initial(src.occupant.client.mouse_pointer_icon) - ..() - return - -/obj/mecha/combat/Topic(href,href_list) - ..() - var/datum/topic_input/top_filter = new (href,href_list) - if(top_filter.get("close")) - am = null - return +/obj/mecha/combat + force = 30 + var/melee_cooldown = 10 + var/melee_can_hit = 1 + //var/list/destroyable_obj = list(/obj/mecha, /obj/structure/window, /obj/structure/grille, /turf/simulated/wall, /obj/structure/girder) + internal_damage_threshold = 50 + maint_access = 0 + //add_req_access = 0 + //operation_req_access = list(access_hos) + var/am = "d3c2fbcadca903a41161ccc9df9cf948" + + max_hull_equip = 2 + max_weapon_equip = 2 + max_utility_equip = 1 + max_universal_equip = 1 + max_special_equip = 1 + cargo_capacity = 1 + + encumbrance_gap = 1.5 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/reinforced, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + +/* +/obj/mecha/combat/range_action(target as obj|mob|turf) + if(internal_damage&MECHA_INT_CONTROL_LOST) + target = pick(view(3,target)) + if(selected_weapon) + selected_weapon.fire(target) + return +*/ + +/obj/mecha/combat/melee_action(atom/T) + if(internal_damage&MECHA_INT_CONTROL_LOST) + T = safepick(oview(1,src)) + if(!melee_can_hit) + return + if(istype(T, /mob/living)) + var/mob/living/M = T + if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) //Brains cannot change intents; Exo-piloting brains lack any form of physical feedback for control, limiting the ability to 'play nice'. + playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) + if(damtype == "brute") + step_away(M,src,15) + /* + if(M.stat>1) + M.gib() + melee_can_hit = 0 + if(do_after(melee_cooldown)) + melee_can_hit = 1 + return + */ + if(ishuman(T)) + var/mob/living/carbon/human/H = T + // if (M.health <= 0) return + + var/obj/item/organ/external/temp = H.get_organ(pick(BP_TORSO, BP_TORSO, BP_TORSO, BP_HEAD)) + if(temp) + var/update = 0 + switch(damtype) + if("brute") + H.Paralyse(1) + update |= temp.take_damage(rand(force/2, force), 0) + if("fire") + update |= temp.take_damage(0, rand(force/2, force)) + if("tox") + if(H.reagents) + if(H.reagents.get_reagent_amount("carpotoxin") + force < force*2) + H.reagents.add_reagent("carpotoxin", force) + if(H.reagents.get_reagent_amount("cryptobiolin") + force < force*2) + H.reagents.add_reagent("cryptobiolin", force) + if("halloss") + H.stun_effect_act(1, force / 2, BP_TORSO, src) + else + return + if(update) H.UpdateDamageIcon() + H.updatehealth() + + else + switch(damtype) + if("brute") + M.Paralyse(1) + M.take_overall_damage(rand(force/2, force)) + if("fire") + M.take_overall_damage(0, rand(force/2, force)) + if("tox") + if(M.reagents) + if(M.reagents.get_reagent_amount("carpotoxin") + force < force*2) + M.reagents.add_reagent("carpotoxin", force) + if(M.reagents.get_reagent_amount("cryptobiolin") + force < force*2) + M.reagents.add_reagent("cryptobiolin", force) + else + return + M.updatehealth() + src.occupant_message("You hit [T].") + src.visible_message(span_red("[src.name] hits [T].")) + else + step_away(M,src) + src.occupant_message("You push [T] out of the way.") + src.visible_message("[src] pushes [T] out of the way.") + + melee_can_hit = 0 + if(do_after(melee_cooldown)) + melee_can_hit = 1 + return + + else + if(istype(T, /obj/machinery/disposal)) // Stops mechs from climbing into disposals + return + if(src.occupant.a_intent == I_HURT || istype(src.occupant, /mob/living/carbon/brain)) // Don't smash unless we mean it + if(damtype == "brute") + src.occupant_message("You hit [T].") + src.visible_message(span_red("[src.name] hits [T]")) + playsound(src, 'sound/weapons/heavysmash.ogg', 50, 1) + + if(istype(T, /obj/structure/girder)) + T:take_damage(force * 3) //Girders have 200 health by default. Steel, non-reinforced walls take four punches, girders take (with this value-mod) two, girders took five without. + else + T:take_damage(force) + + melee_can_hit = 0 + + if(do_after(melee_cooldown)) + melee_can_hit = 1 + return + +/obj/mecha/combat/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.client) + H.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") + return 1 + else + return 0 + +/obj/mecha/combat/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) + if(..()) + if(occupant.client) + occupant.client.mouse_pointer_icon = file("icons/mecha/mecha_mouse.dmi") + return 1 + else + return 0 + +/obj/mecha/combat/go_out() + if(src.occupant && src.occupant.client) + src.occupant.client.mouse_pointer_icon = initial(src.occupant.client.mouse_pointer_icon) + ..() + return + +/obj/mecha/combat/Topic(href,href_list) + ..() + var/datum/topic_input/top_filter = new (href,href_list) + if(top_filter.get("close")) + am = null + return diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm index 4ea027f154d..44986197dd6 100644 --- a/code/game/mecha/combat/durand.dm +++ b/code/game/mecha/combat/durand.dm @@ -1,87 +1,87 @@ -/obj/mecha/combat/durand - desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms." - name = "Durand" - icon_state = "durand" - initial_icon = "durand" - step_in = 4 - dir_in = 1 //Facing North. - health = 300 - maxhealth = 300 //Don't forget to update the /old variant if you change this number. - deflect_chance = 20 - max_temperature = 30000 - infra_luminosity = 8 - force = 40 - wreckage = /obj/effect/decal/mecha_wreckage/durand - - damage_minimum = 15 //Big stompy - minimum_penetration = 25 - - max_hull_equip = 2 - max_weapon_equip = 1 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/military, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - defence_mode_possible = 1 - - icon_scale_x = 1.5 - icon_scale_y = 1.5 - -/* -/obj/mecha/combat/durand/New() - ..() - weapons += new /datum/mecha_weapon/ballistic/lmg(src) - weapons += new /datum/mecha_weapon/ballistic/scattershot(src) - selected_weapon = weapons[1] - return -*/ - - - -//This is for the Mech stats / Menu system. To be moved later on. -/obj/mecha/combat/durand/get_commands() - var/output = {"
                    -
                    Special
                    - -
                    - "} - output += ..() - return output - - -//Not needed anymore but left for reference. -/* -/obj/mecha/combat/durand/get_stats_part() - var/output = ..() - output += "Defence mode: [defence?"on":"off"]" - return output -*/ - -/* - -/obj/mecha/combat/durand/Topic(href, href_list) - ..() - if (href_list["toggle_defence_mode"]) - src.defence_mode() - return -*/ - -//Meant for random spawns. -/obj/mecha/combat/durand/old - desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/combat/durand/old/New() - ..() - health = 25 - maxhealth = 250 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/durand + desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms." + name = "Durand" + icon_state = "durand" + initial_icon = "durand" + step_in = 4 + dir_in = 1 //Facing North. + health = 300 + maxhealth = 300 //Don't forget to update the /old variant if you change this number. + deflect_chance = 20 + max_temperature = 30000 + infra_luminosity = 8 + force = 40 + wreckage = /obj/effect/decal/mecha_wreckage/durand + + damage_minimum = 15 //Big stompy + minimum_penetration = 25 + + max_hull_equip = 2 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/military, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + defence_mode_possible = 1 + + icon_scale_x = 1.5 + icon_scale_y = 1.5 + +/* +/obj/mecha/combat/durand/New() + ..() + weapons += new /datum/mecha_weapon/ballistic/lmg(src) + weapons += new /datum/mecha_weapon/ballistic/scattershot(src) + selected_weapon = weapons[1] + return +*/ + + + +//This is for the Mech stats / Menu system. To be moved later on. +/obj/mecha/combat/durand/get_commands() + var/output = {"
                    +
                    Special
                    + +
                    + "} + output += ..() + return output + + +//Not needed anymore but left for reference. +/* +/obj/mecha/combat/durand/get_stats_part() + var/output = ..() + output += "Defence mode: [defence?"on":"off"]" + return output +*/ + +/* + +/obj/mecha/combat/durand/Topic(href, href_list) + ..() + if (href_list["toggle_defence_mode"]) + src.defence_mode() + return +*/ + +//Meant for random spawns. +/obj/mecha/combat/durand/old + desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/combat/durand/old/New() + ..() + health = 25 + maxhealth = 250 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 1fa6933add7..6ff46999063 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -1,148 +1,148 @@ -/obj/mecha/combat/gygax - desc = "A lightweight, security exosuit. Popular among private and corporate security." - name = "Gygax" - icon_state = "gygax" - initial_icon = "gygax" - step_in = 3 - dir_in = 1 //Facing North. - health = 250 - maxhealth = 250 //Don't forget to update the /old variant if you change this number. - deflect_chance = 15 - max_temperature = 25000 - infra_luminosity = 6 - wreckage = /obj/effect/decal/mecha_wreckage/gygax - internal_damage_threshold = 35 - max_equip = 3 - - max_hull_equip = 1 - max_weapon_equip = 2 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull/lightweight, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/marshal, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - overload_possible = 1 - - icon_scale_x = 1.35 - icon_scale_y = 1.35 - -//Not quite sure how to move those yet. -/obj/mecha/combat/gygax/get_commands() - var/output = {"
                    -
                    Special
                    - -
                    - "} - output += ..() - return output - - -/obj/mecha/combat/gygax/dark - desc = "A lightweight exosuit used by Heavy Asset Protection. A significantly upgraded Gygax security mech." - name = "Dark Gygax" - icon_state = "darkgygax" - initial_icon = "darkgygax" - health = 400 - maxhealth = 400 - deflect_chance = 25 - max_temperature = 45000 - overload_coeff = 1 - wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark - max_equip = 4 - step_energy_drain = 5 - mech_faction = MECH_FACTION_SYNDI - - max_hull_equip = 1 - max_weapon_equip = 2 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 2 - - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/grenade/clusterbang, - /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, - /obj/item/mecha_parts/mecha_equipment/teleporter - ) - -/obj/mecha/combat/gygax/dark/add_cell(var/obj/item/weapon/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new /obj/item/weapon/cell/hyper(src) - -/obj/mecha/combat/gygax/serenity - desc = "A lightweight exosuit made from a modified Gygax chassis combined with proprietary VeyMed medical tech. It's faster and sturdier than most medical mechs, but much of the armor plating has been stripped out, leaving it more vulnerable than a regular Gygax." - name = "Serenity" - icon_state = "medgax" - initial_icon = "medgax" - health = 150 - maxhealth = 150 - deflect_chance = 20 - step_in = 2 - max_temperature = 20000 - overload_coeff = 1 - wreckage = /obj/effect/decal/mecha_wreckage/gygax/serenity - max_equip = 3 - step_energy_drain = 8 - cargo_capacity = 2 - max_hull_equip = 1 - max_weapon_equip = 1 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/lightweight, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - var/obj/item/clothing/glasses/hud/health/mech/hud - -/obj/mecha/combat/gygax/serenity/New() - ..() - hud = new /obj/item/clothing/glasses/hud/health/mech(src) - return - -/obj/mecha/combat/gygax/serenity/moved_inside(var/mob/living/carbon/human/H as mob) - if(..()) - if(H.glasses) - occupant_message("[H.glasses] prevent you from using [src] [hud]") - else - H.glasses = hud - H.recalculate_vis() - return 1 - else - return 0 - -/obj/mecha/combat/gygax/serenity/go_out() - if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - if(H.glasses == hud) - H.glasses = null - H.recalculate_vis() - ..() - return - -//Meant for random spawns. -/obj/mecha/combat/gygax/old - desc = "A lightweight, security exosuit. Popular among private and corporate security. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/combat/gygax/old/New() - ..() - health = 25 - maxhealth = 250 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/gygax + desc = "A lightweight, security exosuit. Popular among private and corporate security." + name = "Gygax" + icon_state = "gygax" + initial_icon = "gygax" + step_in = 3 + dir_in = 1 //Facing North. + health = 250 + maxhealth = 250 //Don't forget to update the /old variant if you change this number. + deflect_chance = 15 + max_temperature = 25000 + infra_luminosity = 6 + wreckage = /obj/effect/decal/mecha_wreckage/gygax + internal_damage_threshold = 35 + max_equip = 3 + + max_hull_equip = 1 + max_weapon_equip = 2 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull/lightweight, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/marshal, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + overload_possible = 1 + + icon_scale_x = 1.35 + icon_scale_y = 1.35 + +//Not quite sure how to move those yet. +/obj/mecha/combat/gygax/get_commands() + var/output = {"
                    +
                    Special
                    + +
                    + "} + output += ..() + return output + + +/obj/mecha/combat/gygax/dark + desc = "A lightweight exosuit used by Heavy Asset Protection. A significantly upgraded Gygax security mech." + name = "Dark Gygax" + icon_state = "darkgygax" + initial_icon = "darkgygax" + health = 400 + maxhealth = 400 + deflect_chance = 25 + max_temperature = 45000 + overload_coeff = 1 + wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark + max_equip = 4 + step_energy_drain = 5 + mech_faction = MECH_FACTION_SYNDI + + max_hull_equip = 1 + max_weapon_equip = 2 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 2 + + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/grenade/clusterbang, + /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, + /obj/item/mecha_parts/mecha_equipment/teleporter + ) + +/obj/mecha/combat/gygax/dark/add_cell(var/obj/item/weapon/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/weapon/cell/hyper(src) + +/obj/mecha/combat/gygax/serenity + desc = "A lightweight exosuit made from a modified Gygax chassis combined with proprietary VeyMed medical tech. It's faster and sturdier than most medical mechs, but much of the armor plating has been stripped out, leaving it more vulnerable than a regular Gygax." + name = "Serenity" + icon_state = "medgax" + initial_icon = "medgax" + health = 150 + maxhealth = 150 + deflect_chance = 20 + step_in = 2 + max_temperature = 20000 + overload_coeff = 1 + wreckage = /obj/effect/decal/mecha_wreckage/gygax/serenity + max_equip = 3 + step_energy_drain = 8 + cargo_capacity = 2 + max_hull_equip = 1 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/lightweight, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + var/obj/item/clothing/glasses/hud/health/mech/hud + +/obj/mecha/combat/gygax/serenity/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health/mech(src) + return + +/obj/mecha/combat/gygax/serenity/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.glasses) + occupant_message(span_red("[H.glasses] prevent you from using [src] [hud]!")) + else + H.glasses = hud + H.recalculate_vis() + return 1 + else + return 0 + +/obj/mecha/combat/gygax/serenity/go_out() + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + if(H.glasses == hud) + H.glasses = null + H.recalculate_vis() + ..() + return + +//Meant for random spawns. +/obj/mecha/combat/gygax/old + desc = "A lightweight, security exosuit. Popular among private and corporate security. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/combat/gygax/old/New() + ..() + health = 25 + maxhealth = 250 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm index 8fc089c4955..371c45fa4d0 100644 --- a/code/game/mecha/combat/marauder.dm +++ b/code/game/mecha/combat/marauder.dm @@ -1,150 +1,150 @@ -/obj/mecha/combat/marauder - desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations." - name = "Marauder" - catalogue_data = list(/datum/category_item/catalogue/technology/marauder) - icon_state = "marauder" - initial_icon = "marauder" - step_in = 5 - health = 350 - maxhealth = 350 //Don't forget to update the /old variant if you change this number. - deflect_chance = 25 - max_temperature = 60000 - infra_luminosity = 3 - operation_req_access = list(access_cent_specops) - wreckage = /obj/effect/decal/mecha_wreckage/marauder - add_req_access = 0 - internal_damage_threshold = 25 - force = 45 - max_equip = 4 - mech_faction = MECH_FACTION_NT - - max_hull_equip = 3 - max_weapon_equip = 3 - max_utility_equip = 3 - max_universal_equip = 1 - max_special_equip = 1 - - smoke_possible = 1 - zoom_possible = 1 - thrusters_possible = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/military, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, - /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, - /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster - ) - - icon_scale_x = 1.5 - icon_scale_y = 1.5 - -/obj/mecha/combat/marauder/seraph - desc = "Heavy-duty, command-type exosuit. This is a custom model, utilized only by high-ranking military personnel." - name = "Seraph" - catalogue_data = list(/datum/category_item/catalogue/technology/seraph) - icon_state = "seraph" - initial_icon = "seraph" - operation_req_access = list(access_cent_creed) - step_in = 3 - health = 450 - wreckage = /obj/effect/decal/mecha_wreckage/seraph - internal_damage_threshold = 20 - force = 55 - max_equip = 5 - - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, - /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, - /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster, - /obj/item/mecha_parts/mecha_equipment/teleporter - ) - -//Note that is the Mauler -/obj/mecha/combat/marauder/mauler - desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model." - name = "Mauler" - icon_state = "mauler" - initial_icon = "mauler" - operation_req_access = list(access_syndicate) - wreckage = /obj/effect/decal/mecha_wreckage/mauler - mech_faction = MECH_FACTION_SYNDI - -//I'll break this down later -/obj/mecha/combat/marauder/relaymove(mob/user,direction) - if(user != src.occupant) //While not "realistic", this piece is player friendly. - user.loc = get_turf(src) - to_chat(user, "You climb out from [src]") - return 0 - if(!can_move) - return 0 - if(zoom) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - if(connected_port) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while connected to the air system port") - last_message = world.time - return 0 - if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) - return 0 - if(state || !has_charge(step_energy_drain)) - return 0 - var/tmp_step_in = step_in - var/tmp_step_energy_drain = step_energy_drain - var/move_result = 0 - if(internal_damage&MECHA_INT_CONTROL_LOST) - move_result = mechsteprand() - else if(src.dir!=direction) - move_result = mechturn(direction) - else - move_result = mechstep(direction) - if(move_result) - if(istype(src.loc, /turf/space)) - if(!src.check_for_support()) - float_direction = direction - start_process(MECHA_PROC_MOVEMENT) - if(thrusters) - tmp_step_energy_drain = step_energy_drain*2 - - can_move = 0 - spawn(tmp_step_in) can_move = 1 - use_power(tmp_step_energy_drain) - return 1 - return 0 - -//To be kill ltr -/obj/mecha/combat/marauder/get_commands() - var/output = {"
                    -
                    Special
                    - -
                    - "} - output += ..() - return output - -//Meant for random spawns. -/obj/mecha/combat/marauder/old - desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations. This one is particularly worn looking and likely isn't as sturdy." - - starting_equipment = null - -/obj/mecha/combat/marauder/old/New() - ..() - health = 25 - maxhealth = 300 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/marauder + desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations." + name = "Marauder" + catalogue_data = list(/datum/category_item/catalogue/technology/marauder) + icon_state = "marauder" + initial_icon = "marauder" + step_in = 5 + health = 350 + maxhealth = 350 //Don't forget to update the /old variant if you change this number. + deflect_chance = 25 + max_temperature = 60000 + infra_luminosity = 3 + operation_req_access = list(access_cent_specops) + wreckage = /obj/effect/decal/mecha_wreckage/marauder + add_req_access = 0 + internal_damage_threshold = 25 + force = 45 + max_equip = 4 + mech_faction = MECH_FACTION_NT + + max_hull_equip = 3 + max_weapon_equip = 3 + max_utility_equip = 3 + max_universal_equip = 1 + max_special_equip = 1 + + smoke_possible = 1 + zoom_possible = 1 + thrusters_possible = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/military, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/weapon/energy/pulse, + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, + /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, + /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster + ) + + icon_scale_x = 1.5 + icon_scale_y = 1.5 + +/obj/mecha/combat/marauder/seraph + desc = "Heavy-duty, command-type exosuit. This is a custom model, utilized only by high-ranking military personnel." + name = "Seraph" + catalogue_data = list(/datum/category_item/catalogue/technology/seraph) + icon_state = "seraph" + initial_icon = "seraph" + operation_req_access = list(access_cent_creed) + step_in = 3 + health = 450 + wreckage = /obj/effect/decal/mecha_wreckage/seraph + internal_damage_threshold = 20 + force = 55 + max_equip = 5 + + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot, + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive, + /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay, + /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster, + /obj/item/mecha_parts/mecha_equipment/teleporter + ) + +//Note that is the Mauler +/obj/mecha/combat/marauder/mauler + desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model." + name = "Mauler" + icon_state = "mauler" + initial_icon = "mauler" + operation_req_access = list(access_syndicate) + wreckage = /obj/effect/decal/mecha_wreckage/mauler + mech_faction = MECH_FACTION_SYNDI + +//I'll break this down later +/obj/mecha/combat/marauder/relaymove(mob/user,direction) + if(user != src.occupant) //While not "realistic", this piece is player friendly. + user.loc = get_turf(src) + to_chat(user, "You climb out from [src]") + return 0 + if(!can_move) + return 0 + if(zoom) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + if(connected_port) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while connected to the air system port") + last_message = world.time + return 0 + if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) + return 0 + if(state || !has_charge(step_energy_drain)) + return 0 + var/tmp_step_in = step_in + var/tmp_step_energy_drain = step_energy_drain + var/move_result = 0 + if(internal_damage&MECHA_INT_CONTROL_LOST) + move_result = mechsteprand() + else if(src.dir!=direction) + move_result = mechturn(direction) + else + move_result = mechstep(direction) + if(move_result) + if(istype(src.loc, /turf/space)) + if(!src.check_for_support()) + float_direction = direction + start_process(MECHA_PROC_MOVEMENT) + if(thrusters) + tmp_step_energy_drain = step_energy_drain*2 + + can_move = 0 + spawn(tmp_step_in) can_move = 1 + use_power(tmp_step_energy_drain) + return 1 + return 0 + +//To be kill ltr +/obj/mecha/combat/marauder/get_commands() + var/output = {"
                    +
                    Special
                    + +
                    + "} + output += ..() + return output + +//Meant for random spawns. +/obj/mecha/combat/marauder/old + desc = "Heavy-duty, combat exosuit, developed after the Durand model. Rarely found among civilian populations. This one is particularly worn looking and likely isn't as sturdy." + + starting_equipment = null + +/obj/mecha/combat/marauder/old/New() + ..() + health = 25 + maxhealth = 300 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 04e3869cc1c..08aded1cf07 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -1,165 +1,165 @@ -/obj/mecha/combat/phazon - desc = "An exosuit which can only be described as 'WTF?'." - name = "Phazon" - icon_state = "phazon" - initial_icon = "phazon" - step_in = 1 - dir_in = 1 //Facing North. - step_energy_drain = 3 - health = 200 //God this is low - maxhealth = 200 //Don't forget to update the /old variant if you change this number. - deflect_chance = 30 - max_temperature = 25000 - infra_luminosity = 3 - wreckage = /obj/effect/decal/mecha_wreckage/phazon - add_req_access = 1 - //operation_req_access = list() - internal_damage_threshold = 25 - force = 15 - max_equip = 4 - - max_hull_equip = 3 - max_weapon_equip = 3 - max_utility_equip = 3 - max_universal_equip = 3 - max_special_equip = 4 - - encumbrance_gap = 2 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/alien, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - cloak_possible = TRUE - phasing_possible = TRUE - switch_dmg_type_possible = TRUE - var/list/inherent_damage_absorption = list("brute"=0.7,"fire"=0.7,"bullet"=0.7,"laser"=0.7,"energy"=0.7,"bomb"=0.7) - -/obj/mecha/combat/phazon/equipped/Initialize() - . = ..() - starting_equipment = list( - /obj/item/mecha_parts/mecha_equipment/tool/rcd, - /obj/item/mecha_parts/mecha_equipment/gravcatapult - ) - return - -/* Leaving this until we are really sure we don't need it for reference. -/obj/mecha/combat/phazon/Bump(var/atom/obstacle) - if(phasing && get_charge()>=phasing_energy_drain) - spawn() - if(can_phase) - can_phase = FALSE - flick("[initial_icon]-phase", src) - src.loc = get_step(src,src.dir) - src.use_power(phasing_energy_drain) - sleep(step_in*3) - can_phase = TRUE - else - . = ..() - return -*/ - - -/obj/mecha/combat/phazon/get_commands() - var/output = {" - "} - output += ..() - return output - - - -/obj/mecha/combat/phazon/janus - name = "Phazon Prototype Janus Class" - desc = "An exosuit which a more crude civilization such as yours might describe as WTF?." - description_fluff = "An incredibly high-tech exosuit constructed out of salvaged alien and cutting-edge modern technology.\ - This machine, theoretically, is capable of travelling through time, however due to the strange nature of its miniaturized \ - supermatter-fueled bluespace drive, it is uncertain how this ability manifests." - icon_state = "janus" - initial_icon = "janus" - step_in = 1 - dir_in = 1 //Facing North. - step_energy_drain = 3 - health = 350 - maxhealth = 350 - deflect_chance = 30 - inherent_damage_absorption = list("brute"=0.6,"fire"=0.7,"bullet"=0.7,"laser"=0.9,"energy"=0.7,"bomb"=0.5) - max_temperature = 10000 - infra_luminosity = 3 - wreckage = /obj/effect/decal/mecha_wreckage/janus - internal_damage_threshold = 25 - force = 20 - phasing_energy_drain = 300 - - max_hull_equip = 2 - max_weapon_equip = 1 - max_utility_equip = 2 - max_universal_equip = 2 - max_special_equip = 2 - - phasing_possible = TRUE - switch_dmg_type_possible = TRUE - cloak_possible = FALSE - -/obj/mecha/combat/phazon/janus/take_damage(amount, type="brute") - ..() - if(phasing) - phasing = FALSE - SSradiation.radiate(get_turf(src), 30) - log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1) - visible_message("The [src.name] appears to flicker, before its silhouette stabilizes!") - return - -/obj/mecha/combat/phazon/janus/dynbulletdamage(var/obj/item/projectile/Proj) - if((Proj.damage && !Proj.nodamage) && !istype(Proj, /obj/item/projectile/beam) && prob(max(1, 33 - round(Proj.damage / 4)))) - src.occupant_message("The armor absorbs the incoming projectile's force, negating it!") - src.visible_message("The [src.name] absorbs the incoming projectile's force, negating it!") - src.log_append_to_last("Armor negated.") - return - else if((Proj.damage && !Proj.nodamage) && istype(Proj, /obj/item/projectile/beam) && prob(max(1, (50 - round((Proj.damage / 2) * inherent_damage_absorption["laser"])) * (1 - (Proj.armor_penetration / 100))))) // Base 50% chance to deflect a beam,lowered by half the beam's damage scaled to laser absorption, then multiplied by the remaining percent of non-penetrated armor, with a minimum chance of 1%. - src.occupant_message("The armor reflects the incoming beam, negating it!") - src.visible_message("The [src.name] reflects the incoming beam, negating it!") - src.log_append_to_last("Armor reflected.") - return - - ..() - -/obj/mecha/combat/phazon/janus/dynattackby(obj/item/weapon/W as obj, mob/user as mob) - if(prob(max(1, (50 - round((W.force / 2) * inherent_damage_absorption["brute"])) * (1 - (W.armor_penetration / 100))))) - src.occupant_message("The armor absorbs the incoming attack's force, negating it!") - src.visible_message("The [src.name] absorbs the incoming attack's force, negating it!") - src.log_append_to_last("Armor absorbed.") - return - - ..() - -/obj/mecha/combat/phazon/janus/query_damtype() - var/new_damtype = tgui_alert(src.occupant,"Gauntlet Phase Emitter Mode","Damage Type",list("Force","Energy","Stun")) - switch(new_damtype) - if("Force") - damtype = "brute" - if("Energy") - damtype = "fire" - if("Stun") - damtype = "halloss" - src.occupant_message("Melee damage type switched to [new_damtype]") - return - -//Meant for random spawns. -/obj/mecha/combat/phazon/old - desc = "An exosuit which can only be described as 'WTF?'. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/combat/phazon/old/New() - ..() - health = 25 - maxhealth = 150 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) +/obj/mecha/combat/phazon + desc = "An exosuit which can only be described as 'WTF?'." + name = "Phazon" + icon_state = "phazon" + initial_icon = "phazon" + step_in = 1 + dir_in = 1 //Facing North. + step_energy_drain = 3 + health = 200 //God this is low + maxhealth = 200 //Don't forget to update the /old variant if you change this number. + deflect_chance = 30 + max_temperature = 25000 + infra_luminosity = 3 + wreckage = /obj/effect/decal/mecha_wreckage/phazon + add_req_access = 1 + //operation_req_access = list() + internal_damage_threshold = 25 + force = 15 + max_equip = 4 + + max_hull_equip = 3 + max_weapon_equip = 3 + max_utility_equip = 3 + max_universal_equip = 3 + max_special_equip = 4 + + encumbrance_gap = 2 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/alien, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + cloak_possible = TRUE + phasing_possible = TRUE + switch_dmg_type_possible = TRUE + var/list/inherent_damage_absorption = list("brute"=0.7,"fire"=0.7,"bullet"=0.7,"laser"=0.7,"energy"=0.7,"bomb"=0.7) + +/obj/mecha/combat/phazon/equipped/Initialize() + . = ..() + starting_equipment = list( + /obj/item/mecha_parts/mecha_equipment/tool/rcd, + /obj/item/mecha_parts/mecha_equipment/gravcatapult + ) + return + +/* Leaving this until we are really sure we don't need it for reference. +/obj/mecha/combat/phazon/Bump(var/atom/obstacle) + if(phasing && get_charge()>=phasing_energy_drain) + spawn() + if(can_phase) + can_phase = FALSE + flick("[initial_icon]-phase", src) + src.loc = get_step(src,src.dir) + src.use_power(phasing_energy_drain) + sleep(step_in*3) + can_phase = TRUE + else + . = ..() + return +*/ + + +/obj/mecha/combat/phazon/get_commands() + var/output = {" + "} + output += ..() + return output + + + +/obj/mecha/combat/phazon/janus + name = "Phazon Prototype Janus Class" + desc = "An exosuit which a more crude civilization such as yours might describe as WTF?." + description_fluff = "An incredibly high-tech exosuit constructed out of salvaged alien and cutting-edge modern technology.\ + This machine, theoretically, is capable of travelling through time, however due to the strange nature of its miniaturized \ + supermatter-fueled bluespace drive, it is uncertain how this ability manifests." + icon_state = "janus" + initial_icon = "janus" + step_in = 1 + dir_in = 1 //Facing North. + step_energy_drain = 3 + health = 350 + maxhealth = 350 + deflect_chance = 30 + inherent_damage_absorption = list("brute"=0.6,"fire"=0.7,"bullet"=0.7,"laser"=0.9,"energy"=0.7,"bomb"=0.5) + max_temperature = 10000 + infra_luminosity = 3 + wreckage = /obj/effect/decal/mecha_wreckage/janus + internal_damage_threshold = 25 + force = 20 + phasing_energy_drain = 300 + + max_hull_equip = 2 + max_weapon_equip = 1 + max_utility_equip = 2 + max_universal_equip = 2 + max_special_equip = 2 + + phasing_possible = TRUE + switch_dmg_type_possible = TRUE + cloak_possible = FALSE + +/obj/mecha/combat/phazon/janus/take_damage(amount, type="brute") + ..() + if(phasing) + phasing = FALSE + SSradiation.radiate(get_turf(src), 30) + log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1) + visible_message("The [src.name] appears to flicker, before its silhouette stabilizes!") + return + +/obj/mecha/combat/phazon/janus/dynbulletdamage(var/obj/item/projectile/Proj) + if((Proj.damage && !Proj.nodamage) && !istype(Proj, /obj/item/projectile/beam) && prob(max(1, 33 - round(Proj.damage / 4)))) + src.occupant_message("The armor absorbs the incoming projectile's force, negating it!") + src.visible_message("The [src.name] absorbs the incoming projectile's force, negating it!") + src.log_append_to_last("Armor negated.") + return + else if((Proj.damage && !Proj.nodamage) && istype(Proj, /obj/item/projectile/beam) && prob(max(1, (50 - round((Proj.damage / 2) * inherent_damage_absorption["laser"])) * (1 - (Proj.armor_penetration / 100))))) // Base 50% chance to deflect a beam,lowered by half the beam's damage scaled to laser absorption, then multiplied by the remaining percent of non-penetrated armor, with a minimum chance of 1%. + src.occupant_message("The armor reflects the incoming beam, negating it!") + src.visible_message("The [src.name] reflects the incoming beam, negating it!") + src.log_append_to_last("Armor reflected.") + return + + ..() + +/obj/mecha/combat/phazon/janus/dynattackby(obj/item/weapon/W as obj, mob/user as mob) + if(prob(max(1, (50 - round((W.force / 2) * inherent_damage_absorption["brute"])) * (1 - (W.armor_penetration / 100))))) + src.occupant_message("The armor absorbs the incoming attack's force, negating it!") + src.visible_message("The [src.name] absorbs the incoming attack's force, negating it!") + src.log_append_to_last("Armor absorbed.") + return + + ..() + +/obj/mecha/combat/phazon/janus/query_damtype() + var/new_damtype = tgui_alert(src.occupant,"Gauntlet Phase Emitter Mode","Damage Type",list("Force","Energy","Stun")) + switch(new_damtype) + if("Force") + damtype = "brute" + if("Energy") + damtype = "fire" + if("Stun") + damtype = "halloss" + src.occupant_message("Melee damage type switched to [new_damtype]") + return + +//Meant for random spawns. +/obj/mecha/combat/phazon/old + desc = "An exosuit which can only be described as 'WTF?'. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/combat/phazon/old/New() + ..() + health = 25 + maxhealth = 150 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 7184fc3364c..e4a178034ea 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -1,287 +1,287 @@ -//DO NOT ADD MECHA PARTS TO THE GAME WITH THE DEFAULT "SPRITE ME" SPRITE! -//I'm annoyed I even have to tell you this! SPRITE FIRST, then commit. -#define EQUIP_HULL "hull" -#define EQUIP_WEAPON "weapon" -#define EQUIP_UTILITY "utility" -#define EQUIP_SPECIAL "core" -//VOREStation Addition begin: MICROMECHS -#define EQUIP_MICRO_UTILITY "micro_utility" -#define EQUIP_MICRO_WEAPON "micro_weapon" -//VOREStation Addition end: MICROMECHS - -/obj/item/mecha_parts/mecha_equipment - name = "mecha equipment" - icon = 'icons/mecha/mecha_equipment.dmi' - icon_state = "mecha_equip" - force = 5 - origin_tech = list(TECH_MATERIAL = 2) - description_info = "Some equipment may gain new abilities or advantages if equipped to certain types of Exosuits." - var/equip_cooldown = 0 - var/equip_ready = TRUE - var/energy_drain = 0 - var/obj/mecha/chassis = null - var/range = MELEE //bitflags - /// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this. - var/mech_flags = NONE - var/salvageable = TRUE - var/required_type = /obj/mecha //may be either a type or a list of allowed types - var/equip_type = null //mechaequip2 - var/allow_duplicate = FALSE - var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed. - var/enable_special = FALSE // Will the tool do its special? - - var/step_delay = 0 // Does the component slow/speed up the suit? - -/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1) - sleep(equip_cooldown) - set_ready_state(TRUE) - if(ready_sound) //Kind of like the kinetic accelerator. - playsound(src, ready_sound, 50, 1, -1) - if(target && chassis) - return 1 - return 0 - -/obj/item/mecha_parts/mecha_equipment/examine(mob/user) - . = ..() - . += "[src] will fill [equip_type?"a [equip_type]":"any"] slot." - -/obj/item/mecha_parts/mecha_equipment/proc/add_equip_overlay(obj/mecha/M as obj) - return - -/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page() - if(chassis) - send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) - send_byjax(chassis.occupant,"exosuit.browser","equipment_menu",chassis.get_equipment_menu(),"dropdowns") - return 1 - return - -/obj/item/mecha_parts/mecha_equipment/proc/update_equip_info() - if(chassis) - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",get_equip_info()) - return 1 - return - -/obj/item/mecha_parts/mecha_equipment/proc/destroy()//missiles detonating, teleporter creating singularity? - if(chassis) - if(equip_type) - if(equip_type == EQUIP_HULL) - chassis.hull_equipment -= src - listclearnulls(chassis.hull_equipment) - if(equip_type == EQUIP_WEAPON) - chassis.weapon_equipment -= src - listclearnulls(chassis.weapon_equipment) - if(equip_type == EQUIP_UTILITY) - chassis.utility_equipment -= src - listclearnulls(chassis.utility_equipment) - if(equip_type == EQUIP_SPECIAL) - chassis.special_equipment -= src - listclearnulls(chassis.special_equipment) - //VOREStation Addition begin: MICROMECHS - if(equip_type == EQUIP_MICRO_UTILITY) - chassis.micro_utility_equipment -= src - listclearnulls(chassis.micro_utility_equipment) - if(equip_type == EQUIP_MICRO_WEAPON) - chassis.micro_weapon_equipment -= src - listclearnulls(chassis.micro_weapon_equipment) - //VOREStation Addition end: MICROMECHS - chassis.universal_equipment -= src - chassis.equipment -= src - listclearnulls(chassis.equipment) - if(chassis.selected == src) - chassis.selected = null - src.update_chassis_page() - chassis.occupant_message("The [src] is destroyed!") - chassis.log_append_to_last("[src] is destroyed.",1) - if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))//Gun - switch(chassis.mech_faction) - if(MECH_FACTION_NT) - src.chassis.occupant << sound('sound/mecha/weapdestrnano.ogg',volume=70) - if(MECH_FACTION_SYNDI) - src.chassis.occupant << sound('sound/mecha/weapdestrsyndi.ogg',volume=60) - else - src.chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50) - else //Not a gun - switch(chassis.mech_faction) - if(MECH_FACTION_NT) - src.chassis.occupant << sound('sound/mecha/critdestrnano.ogg',volume=70) - if(MECH_FACTION_SYNDI) - src.chassis.occupant << sound('sound/mecha/critdestrsyndi.ogg',volume=70) - else - src.chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50) - spawn - qdel(src) - return - -/obj/item/mecha_parts/mecha_equipment/proc/critfail() - if(chassis) - log_message("Critical failure",1) - return - -/obj/item/mecha_parts/mecha_equipment/proc/get_equip_info() - if(!chassis) return - return "* [chassis.selected==src?"":""][src.name][chassis.selected==src?"":""]" - -/obj/item/mecha_parts/mecha_equipment/proc/is_ranged()//add a distance restricted equipment. Why not? - return range&RANGED - -/obj/item/mecha_parts/mecha_equipment/proc/is_melee() - return range&MELEE - -/obj/item/mecha_parts/mecha_equipment/proc/enable_special_checks(atom/target) - if(ispath(required_type)) - return istype(target, required_type) - - for (var/path in required_type) - if (istype(target, path)) - return 1 - - return 0 - -/obj/item/mecha_parts/mecha_equipment/proc/action_checks(atom/target) - if(!target) - return 0 - if(!chassis) - return 0 - if(!equip_ready) - return 0 - if(energy_drain && !chassis.has_charge(energy_drain)) - return 0 - return 1 - -/obj/item/mecha_parts/mecha_equipment/proc/action(atom/target) - return - -/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj) - //if(M.equipment.len >= M.max_equip) - // return 0 - if(!allow_duplicate) - for(var/obj/item/mecha_parts/mecha_equipment/ME in M.equipment) //Exact duplicate components aren't allowed. - if(ME.type == src.type) - return 0 - if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip) - return 1 - if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip) - return 1 - if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip) - return 1 - if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip) - return 1 - //VOREStation Addition begin: MICROMECHS - if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip) - return 1 - if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip) - return 1 - //VOREStation Addition end: MICROMECHS - if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip) //The exosuit needs to be military grade to actually have a universal slot capable of accepting a true weapon. - if(equip_type == EQUIP_WEAPON && !istype(M, /obj/mecha/combat)) - return 0 - return 1 - /*if (ispath(required_type)) - return istype(M, required_type) - - for (var/path in required_type) - if (istype(M, path)) - return 1 - */ - return 0 - -/obj/item/mecha_parts/mecha_equipment/proc/attach(obj/mecha/M as obj) - //M.equipment += src - var/has_equipped = 0 - if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip && !has_equipped) - M.hull_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip && !has_equipped) - M.weapon_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip && !has_equipped) - M.utility_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip && !has_equipped) - M.special_equipment += src - has_equipped = 1 - //VOREStation Addition begin: MICROMECHS - if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip && !has_equipped) - M.micro_utility_equipment += src - has_equipped = 1 - if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip && !has_equipped) - M.micro_weapon_equipment += src - has_equipped = 1 - //VOREStation Addition end: MICROMECHS - if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip && !has_equipped) - M.universal_equipment += src - M.equipment += src - chassis = M - src.loc = M - - if(enable_special_checks(M)) - enable_special = TRUE - - M.log_message("[src] initialized.") - if(!M.selected) - M.selected = src - src.update_chassis_page() - return - -/obj/item/mecha_parts/mecha_equipment/Destroy() - detach() - return ..() - -/obj/item/mecha_parts/mecha_equipment/proc/detach(atom/moveto=null) - if(!chassis) - return - moveto = moveto || get_turf(chassis) - forceMove(moveto) - chassis.equipment -= src - chassis.universal_equipment -= src - if(equip_type) - switch(equip_type) - if(EQUIP_HULL) - chassis.hull_equipment -= src - if(EQUIP_WEAPON) - chassis.weapon_equipment -= src - if(EQUIP_UTILITY) - chassis.utility_equipment -= src - if(EQUIP_SPECIAL) - chassis.special_equipment -= src - //VOREStation Addition begin: MICROMECHS - if(EQUIP_MICRO_UTILITY) - chassis.micro_utility_equipment -= src - if(EQUIP_MICRO_WEAPON) - chassis.micro_weapon_equipment -= src - //VOREStation Addition end: MICROMECHS - if(chassis.selected == src) - chassis.selected = null - update_chassis_page() - chassis.log_message("[src] removed from equipment.") - chassis = null - set_ready_state(TRUE) - enable_special = FALSE - return - -/obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) - if(href_list["detach"]) - src.detach() - return - -/obj/item/mecha_parts/mecha_equipment/proc/set_ready_state(state) - equip_ready = state - if(chassis) - send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) - return - -/obj/item/mecha_parts/mecha_equipment/proc/occupant_message(message) - if(chassis) - chassis.occupant_message("\icon[src][bicon(src)] [message]") - return - -/obj/item/mecha_parts/mecha_equipment/proc/log_message(message) - if(chassis) - chassis.log_message("[src]: [message]") - return - -/obj/item/mecha_parts/mecha_equipment/proc/MoveAction() //Allows mech equipment to do an action upon the mech moving - return - -/obj/item/mecha_parts/mecha_equipment/proc/get_step_delay() // Equipment returns its slowdown or speedboost. - return step_delay +//DO NOT ADD MECHA PARTS TO THE GAME WITH THE DEFAULT "SPRITE ME" SPRITE! +//I'm annoyed I even have to tell you this! SPRITE FIRST, then commit. +#define EQUIP_HULL "hull" +#define EQUIP_WEAPON "weapon" +#define EQUIP_UTILITY "utility" +#define EQUIP_SPECIAL "core" +//VOREStation Addition begin: MICROMECHS +#define EQUIP_MICRO_UTILITY "micro_utility" +#define EQUIP_MICRO_WEAPON "micro_weapon" +//VOREStation Addition end: MICROMECHS + +/obj/item/mecha_parts/mecha_equipment + name = "mecha equipment" + icon = 'icons/mecha/mecha_equipment.dmi' + icon_state = "mecha_equip" + force = 5 + origin_tech = list(TECH_MATERIAL = 2) + description_info = "Some equipment may gain new abilities or advantages if equipped to certain types of Exosuits." + var/equip_cooldown = 0 + var/equip_ready = TRUE + var/energy_drain = 0 + var/obj/mecha/chassis = null + var/range = MELEE //bitflags + /// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this. + var/mech_flags = NONE + var/salvageable = TRUE + var/required_type = /obj/mecha //may be either a type or a list of allowed types + var/equip_type = null //mechaequip2 + var/allow_duplicate = FALSE + var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed. + var/enable_special = FALSE // Will the tool do its special? + + var/step_delay = 0 // Does the component slow/speed up the suit? + +/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1) + sleep(equip_cooldown) + set_ready_state(TRUE) + if(ready_sound) //Kind of like the kinetic accelerator. + playsound(src, ready_sound, 50, 1, -1) + if(target && chassis) + return 1 + return 0 + +/obj/item/mecha_parts/mecha_equipment/examine(mob/user) + . = ..() + . += "[src] will fill [equip_type?"a [equip_type]":"any"] slot." + +/obj/item/mecha_parts/mecha_equipment/proc/add_equip_overlay(obj/mecha/M as obj) + return + +/obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page() + if(chassis) + send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) + send_byjax(chassis.occupant,"exosuit.browser","equipment_menu",chassis.get_equipment_menu(),"dropdowns") + return 1 + return + +/obj/item/mecha_parts/mecha_equipment/proc/update_equip_info() + if(chassis) + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",get_equip_info()) + return 1 + return + +/obj/item/mecha_parts/mecha_equipment/proc/destroy()//missiles detonating, teleporter creating singularity? + if(chassis) + if(equip_type) + if(equip_type == EQUIP_HULL) + chassis.hull_equipment -= src + listclearnulls(chassis.hull_equipment) + if(equip_type == EQUIP_WEAPON) + chassis.weapon_equipment -= src + listclearnulls(chassis.weapon_equipment) + if(equip_type == EQUIP_UTILITY) + chassis.utility_equipment -= src + listclearnulls(chassis.utility_equipment) + if(equip_type == EQUIP_SPECIAL) + chassis.special_equipment -= src + listclearnulls(chassis.special_equipment) + //VOREStation Addition begin: MICROMECHS + if(equip_type == EQUIP_MICRO_UTILITY) + chassis.micro_utility_equipment -= src + listclearnulls(chassis.micro_utility_equipment) + if(equip_type == EQUIP_MICRO_WEAPON) + chassis.micro_weapon_equipment -= src + listclearnulls(chassis.micro_weapon_equipment) + //VOREStation Addition end: MICROMECHS + chassis.universal_equipment -= src + chassis.equipment -= src + listclearnulls(chassis.equipment) + if(chassis.selected == src) + chassis.selected = null + src.update_chassis_page() + chassis.occupant_message(span_red("The [src] is destroyed!")) + chassis.log_append_to_last("[src] is destroyed.",1) + if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))//Gun + switch(chassis.mech_faction) + if(MECH_FACTION_NT) + src.chassis.occupant << sound('sound/mecha/weapdestrnano.ogg',volume=70) + if(MECH_FACTION_SYNDI) + src.chassis.occupant << sound('sound/mecha/weapdestrsyndi.ogg',volume=60) + else + src.chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50) + else //Not a gun + switch(chassis.mech_faction) + if(MECH_FACTION_NT) + src.chassis.occupant << sound('sound/mecha/critdestrnano.ogg',volume=70) + if(MECH_FACTION_SYNDI) + src.chassis.occupant << sound('sound/mecha/critdestrsyndi.ogg',volume=70) + else + src.chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50) + spawn + qdel(src) + return + +/obj/item/mecha_parts/mecha_equipment/proc/critfail() + if(chassis) + log_message("Critical failure",1) + return + +/obj/item/mecha_parts/mecha_equipment/proc/get_equip_info() + if(!chassis) return + return "* [chassis.selected==src?"":""][src.name][chassis.selected==src?"":""]" + +/obj/item/mecha_parts/mecha_equipment/proc/is_ranged()//add a distance restricted equipment. Why not? + return range&RANGED + +/obj/item/mecha_parts/mecha_equipment/proc/is_melee() + return range&MELEE + +/obj/item/mecha_parts/mecha_equipment/proc/enable_special_checks(atom/target) + if(ispath(required_type)) + return istype(target, required_type) + + for (var/path in required_type) + if (istype(target, path)) + return 1 + + return 0 + +/obj/item/mecha_parts/mecha_equipment/proc/action_checks(atom/target) + if(!target) + return 0 + if(!chassis) + return 0 + if(!equip_ready) + return 0 + if(energy_drain && !chassis.has_charge(energy_drain)) + return 0 + return 1 + +/obj/item/mecha_parts/mecha_equipment/proc/action(atom/target) + return + +/obj/item/mecha_parts/mecha_equipment/proc/can_attach(obj/mecha/M as obj) + //if(M.equipment.len >= M.max_equip) + // return 0 + if(!allow_duplicate) + for(var/obj/item/mecha_parts/mecha_equipment/ME in M.equipment) //Exact duplicate components aren't allowed. + if(ME.type == src.type) + return 0 + if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip) + return 1 + if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip) + return 1 + if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip) + return 1 + if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip) + return 1 + //VOREStation Addition begin: MICROMECHS + if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip) + return 1 + if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip) + return 1 + //VOREStation Addition end: MICROMECHS + if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip) //The exosuit needs to be military grade to actually have a universal slot capable of accepting a true weapon. + if(equip_type == EQUIP_WEAPON && !istype(M, /obj/mecha/combat)) + return 0 + return 1 + /*if (ispath(required_type)) + return istype(M, required_type) + + for (var/path in required_type) + if (istype(M, path)) + return 1 + */ + return 0 + +/obj/item/mecha_parts/mecha_equipment/proc/attach(obj/mecha/M as obj) + //M.equipment += src + var/has_equipped = 0 + if(equip_type == EQUIP_HULL && M.hull_equipment.len < M.max_hull_equip && !has_equipped) + M.hull_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_WEAPON && M.weapon_equipment.len < M.max_weapon_equip && !has_equipped) + M.weapon_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_UTILITY && M.utility_equipment.len < M.max_utility_equip && !has_equipped) + M.utility_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_SPECIAL && M.special_equipment.len < M.max_special_equip && !has_equipped) + M.special_equipment += src + has_equipped = 1 + //VOREStation Addition begin: MICROMECHS + if(equip_type == EQUIP_MICRO_UTILITY && M.micro_utility_equipment.len < M.max_micro_utility_equip && !has_equipped) + M.micro_utility_equipment += src + has_equipped = 1 + if(equip_type == EQUIP_MICRO_WEAPON && M.micro_weapon_equipment.len < M.max_micro_weapon_equip && !has_equipped) + M.micro_weapon_equipment += src + has_equipped = 1 + //VOREStation Addition end: MICROMECHS + if(equip_type != EQUIP_SPECIAL && M.universal_equipment.len < M.max_universal_equip && !has_equipped) + M.universal_equipment += src + M.equipment += src + chassis = M + src.loc = M + + if(enable_special_checks(M)) + enable_special = TRUE + + M.log_message("[src] initialized.") + if(!M.selected) + M.selected = src + src.update_chassis_page() + return + +/obj/item/mecha_parts/mecha_equipment/Destroy() + detach() + return ..() + +/obj/item/mecha_parts/mecha_equipment/proc/detach(atom/moveto=null) + if(!chassis) + return + moveto = moveto || get_turf(chassis) + forceMove(moveto) + chassis.equipment -= src + chassis.universal_equipment -= src + if(equip_type) + switch(equip_type) + if(EQUIP_HULL) + chassis.hull_equipment -= src + if(EQUIP_WEAPON) + chassis.weapon_equipment -= src + if(EQUIP_UTILITY) + chassis.utility_equipment -= src + if(EQUIP_SPECIAL) + chassis.special_equipment -= src + //VOREStation Addition begin: MICROMECHS + if(EQUIP_MICRO_UTILITY) + chassis.micro_utility_equipment -= src + if(EQUIP_MICRO_WEAPON) + chassis.micro_weapon_equipment -= src + //VOREStation Addition end: MICROMECHS + if(chassis.selected == src) + chassis.selected = null + update_chassis_page() + chassis.log_message("[src] removed from equipment.") + chassis = null + set_ready_state(TRUE) + enable_special = FALSE + return + +/obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) + if(href_list["detach"]) + src.detach() + return + +/obj/item/mecha_parts/mecha_equipment/proc/set_ready_state(state) + equip_ready = state + if(chassis) + send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) + return + +/obj/item/mecha_parts/mecha_equipment/proc/occupant_message(message) + if(chassis) + chassis.occupant_message("\icon[src][bicon(src)] [message]") + return + +/obj/item/mecha_parts/mecha_equipment/proc/log_message(message) + if(chassis) + chassis.log_message("[src]: [message]") + return + +/obj/item/mecha_parts/mecha_equipment/proc/MoveAction() //Allows mech equipment to do an action upon the mech moving + return + +/obj/item/mecha_parts/mecha_equipment/proc/get_step_delay() // Equipment returns its slowdown or speedboost. + return step_delay diff --git a/code/game/mecha/equipment/tools/cable_layer.dm b/code/game/mecha/equipment/tools/cable_layer.dm index 8de184cb7a5..eb1e891a3be 100644 --- a/code/game/mecha/equipment/tools/cable_layer.dm +++ b/code/game/mecha/equipment/tools/cable_layer.dm @@ -20,7 +20,7 @@ var/result = load_cable(target) var/message if(isnull(result)) - message = "Unable to load [target] - no cable found." + message = span_red("Unable to load [target] - no cable found.") else if(!result) message = "Reel is full." else @@ -123,4 +123,4 @@ //NC.mergeConnectedNetworksOnTurf() last_piece = NC - return 1 \ No newline at end of file + return 1 diff --git a/code/game/mecha/equipment/tools/sleeper.dm b/code/game/mecha/equipment/tools/sleeper.dm index 9163ecec2ba..634e1ad435d 100644 --- a/code/game/mecha/equipment/tools/sleeper.dm +++ b/code/game/mecha/equipment/tools/sleeper.dm @@ -44,7 +44,7 @@ if(chassis.loc!=C || target.loc!=T) return if(occupant) - occupant_message("The sleeper is already occupied!") + occupant_message(span_red("The sleeper is already occupied!")) return target.forceMove(src) occupant = target @@ -57,7 +57,7 @@ */ set_ready_state(FALSE) START_PROCESSING(SSprocessing, src) - occupant_message("[target] successfully loaded into [src]. Life support functions engaged.") + occupant_message(span_blue("[target] successfully loaded into [src]. Life support functions engaged.")) chassis.visible_message("[chassis] loads [target] into [src].") log_message("[target] loaded. Life support functions engaged.") return @@ -236,4 +236,4 @@ M.reagents.add_reagent("inaprovaline", 5) chassis.use_power(energy_drain) update_equip_info() - return \ No newline at end of file + return diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index 4fd4c8d9670..d9416629d5c 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -1,3 +1,3 @@ -/obj/item/mecha_parts/mecha_equipment/tool - matter = list(MAT_STEEL = 5000, MAT_GLASS = 3000) +/obj/item/mecha_parts/mecha_equipment/tool + matter = list(MAT_STEEL = 5000, MAT_GLASS = 3000) equip_type = EQUIP_UTILITY \ No newline at end of file diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 9852db29d5e..aa8764ea6a3 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -1,100 +1,100 @@ -/obj/item/mecha_parts/mecha_equipment/weapon - name = "mecha weapon" - range = RANGED - origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3) - matter = list(MAT_STEEL = 6000, MAT_GLASS = 3000) - var/projectile //Type of projectile fired. - var/projectiles = 1 //Amount of projectiles loaded. - var/projectiles_per_shot = 1 //Amount of projectiles fired per single shot. - var/deviation = 0 //Inaccuracy of shots. - var/fire_cooldown = 0 //Duration of sleep between firing projectiles in single shot. - var/fire_sound //Sound played while firing. - var/fire_volume = 50 //How loud it is played. - var/auto_rearm = 0 //Does the weapon reload itself after each shot? - required_type = list(/obj/mecha/combat, /obj/mecha/working/hoverpod/combatpod) - - step_delay = 0.1 - - equip_type = EQUIP_WEAPON - -/obj/item/mecha_parts/mecha_equipment/weapon/action_checks(atom/target) - if(projectiles <= 0) - return 0 - return ..() - -/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target, params) - if(!action_checks(target)) - return - var/turf/curloc = chassis.loc - var/turf/targloc = get_turf(target) - if(!curloc || !targloc) - return - chassis.use_power(energy_drain) - chassis.visible_message("[chassis] fires [src]!") - occupant_message("You fire [src]!") - log_message("Fired from [src], targeting [target].") - var/target_for_log = "unknown" - if(ismob(target)) - target_for_log = target - else if(target) - target_for_log = "[target.name]" - add_attack_logs(chassis.occupant,target_for_log,"Fired exosuit weapon [src.name] (MANUAL)") - - for(var/i = 1 to min(projectiles, projectiles_per_shot)) - var/turf/aimloc = targloc - if(deviation) - aimloc = locate(targloc.x+GaussRandRound(deviation,1),targloc.y+GaussRandRound(deviation,1),targloc.z) - if(!aimloc || aimloc == curloc || (locs && (aimloc in locs))) - break - playsound(src, fire_sound, fire_volume, 1) - projectiles-- - var/turf/projectile_turf - if(chassis.locs && chassis.locs.len) // Multi tile. - for(var/turf/Tloc in chassis.locs) - if(get_dist(Tloc, aimloc) < get_dist(loc, aimloc)) - projectile_turf = get_turf(Tloc) - if(!projectile_turf) - projectile_turf = get_turf(curloc) - var/P = new projectile(projectile_turf) - Fire(P, target, params) - if(i == 1) - set_ready_state(FALSE) - if(fire_cooldown) - sleep(fire_cooldown) - if(auto_rearm) - projectiles = projectiles_per_shot -// set_ready_state(FALSE) - - do_after_cooldown() - - return - -/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, params) - if(istype(A, /obj/item/projectile)) // Sanity. - var/obj/item/projectile/P = A - P.dispersion = deviation - process_accuracy(P, chassis.occupant, target) - P.launch_projectile_from_turf(target, chassis.get_pilot_zone_sel(), chassis.occupant, params) - else if(istype(A, /atom/movable)) - var/atom/movable/AM = A - AM.throw_at(target, 7, 1, chassis) - -/obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target) - var/obj/item/projectile/P = projectile - if(!istype(P)) - return - - P.accuracy -= user.get_accuracy_penalty() - - // Some modifiers make it harder or easier to hit things. - for(var/datum/modifier/M in user.modifiers) - if(!isnull(M.accuracy)) - P.accuracy += M.accuracy - if(!isnull(M.accuracy_dispersion)) - P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.species) - P.accuracy += H.species.gun_accuracy_mod +/obj/item/mecha_parts/mecha_equipment/weapon + name = "mecha weapon" + range = RANGED + origin_tech = list(TECH_MATERIAL = 3, TECH_COMBAT = 3) + matter = list(MAT_STEEL = 6000, MAT_GLASS = 3000) + var/projectile //Type of projectile fired. + var/projectiles = 1 //Amount of projectiles loaded. + var/projectiles_per_shot = 1 //Amount of projectiles fired per single shot. + var/deviation = 0 //Inaccuracy of shots. + var/fire_cooldown = 0 //Duration of sleep between firing projectiles in single shot. + var/fire_sound //Sound played while firing. + var/fire_volume = 50 //How loud it is played. + var/auto_rearm = 0 //Does the weapon reload itself after each shot? + required_type = list(/obj/mecha/combat, /obj/mecha/working/hoverpod/combatpod) + + step_delay = 0.1 + + equip_type = EQUIP_WEAPON + +/obj/item/mecha_parts/mecha_equipment/weapon/action_checks(atom/target) + if(projectiles <= 0) + return 0 + return ..() + +/obj/item/mecha_parts/mecha_equipment/weapon/action(atom/target, params) + if(!action_checks(target)) + return + var/turf/curloc = chassis.loc + var/turf/targloc = get_turf(target) + if(!curloc || !targloc) + return + chassis.use_power(energy_drain) + chassis.visible_message("[chassis] fires [src]!") + occupant_message("You fire [src]!") + log_message("Fired from [src], targeting [target].") + var/target_for_log = "unknown" + if(ismob(target)) + target_for_log = target + else if(target) + target_for_log = "[target.name]" + add_attack_logs(chassis.occupant,target_for_log,"Fired exosuit weapon [src.name] (MANUAL)") + + for(var/i = 1 to min(projectiles, projectiles_per_shot)) + var/turf/aimloc = targloc + if(deviation) + aimloc = locate(targloc.x+GaussRandRound(deviation,1),targloc.y+GaussRandRound(deviation,1),targloc.z) + if(!aimloc || aimloc == curloc || (locs && (aimloc in locs))) + break + playsound(src, fire_sound, fire_volume, 1) + projectiles-- + var/turf/projectile_turf + if(chassis.locs && chassis.locs.len) // Multi tile. + for(var/turf/Tloc in chassis.locs) + if(get_dist(Tloc, aimloc) < get_dist(loc, aimloc)) + projectile_turf = get_turf(Tloc) + if(!projectile_turf) + projectile_turf = get_turf(curloc) + var/P = new projectile(projectile_turf) + Fire(P, target, params) + if(i == 1) + set_ready_state(FALSE) + if(fire_cooldown) + sleep(fire_cooldown) + if(auto_rearm) + projectiles = projectiles_per_shot +// set_ready_state(FALSE) + + do_after_cooldown() + + return + +/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, params) + if(istype(A, /obj/item/projectile)) // Sanity. + var/obj/item/projectile/P = A + P.dispersion = deviation + process_accuracy(P, chassis.occupant, target) + P.launch_projectile_from_turf(target, chassis.get_pilot_zone_sel(), chassis.occupant, params) + else if(istype(A, /atom/movable)) + var/atom/movable/AM = A + AM.throw_at(target, 7, 1, chassis) + +/obj/item/mecha_parts/mecha_equipment/weapon/proc/process_accuracy(obj/projectile, mob/living/user, atom/target) + var/obj/item/projectile/P = projectile + if(!istype(P)) + return + + P.accuracy -= user.get_accuracy_penalty() + + // Some modifiers make it harder or easier to hit things. + for(var/datum/modifier/M in user.modifiers) + if(!isnull(M.accuracy)) + P.accuracy += M.accuracy + if(!isnull(M.accuracy_dispersion)) + P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.species) + P.accuracy += H.species.gun_accuracy_mod P.dispersion = max(P.dispersion + H.species.gun_accuracy_dispersion_mod, 0) \ No newline at end of file diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index 26c57989494..4d481fe0557 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -1,106 +1,106 @@ -/obj/machinery/mech_recharger - name = "mech recharger" - desc = "A mech recharger, built into the floor." - icon = 'icons/mecha/mech_bay.dmi' - icon_state = "recharge_floor" - density = FALSE - anchored = TRUE - layer = TURF_LAYER + 0.1 - circuit = /obj/item/weapon/circuitboard/mech_recharger - - var/atom/movable/charging - var/charge = 45 - var/repair = 0 - var/list/chargable_types = list( - /obj/mecha, - /mob/living/silicon/robot/platform - ) - -/obj/machinery/mech_recharger/Initialize() - . = ..() - default_apply_parts() - -/obj/machinery/mech_recharger/Crossed(var/atom/movable/M) - . = ..() - if(charging == M) - return - for(var/mtype in chargable_types) - if(istype(M, mtype)) - start_charging(M) - return - -/obj/machinery/mech_recharger/Uncrossed(var/atom/movable/M) - . = ..() - if(M == charging) - charging = null - -/obj/machinery/mech_recharger/RefreshParts() - ..() - charge = 0 - repair = -5 - for(var/obj/item/weapon/stock_parts/P in component_parts) - if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - charge += P.rating * 20 - if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) - charge += P.rating * 5 - repair += P.rating - if(istype(P, /obj/item/weapon/stock_parts/manipulator)) - repair += P.rating * 2 - -/obj/machinery/mech_recharger/process() - ..() - if(!charging) - return - if(charging.loc != src.loc) // Could be qdel or teleport or something - charging = null - return - - var/done = FALSE - var/obj/mecha/mech = charging - var/obj/item/weapon/cell/cell = charging.get_cell() - if(cell) - var/t = min(charge, cell.maxcharge - cell.charge) - if(t > 0) - if(istype(mech)) - mech.give_power(t) - else - cell.give(t) - use_power(t * 150) - else - if(istype(mech)) - mech.occupant_message(SPAN_NOTICE("Fully charged.")) - done = TRUE - - if(repair && istype(mech) && mech.health < initial(mech.health)) - mech.health = min(mech.health + repair, initial(mech.health)) - if(mech.health == initial(mech.health)) - mech.occupant_message(SPAN_NOTICE("Fully repaired.")) - else - done = FALSE - if(done) - charging = null - -/obj/machinery/mech_recharger/attackby(var/obj/item/I, var/mob/user) - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - if(default_part_replacement(user, I)) - return - -/obj/machinery/mech_recharger/proc/start_charging(var/atom/movable/M) - - var/obj/mecha/mech = M - if(stat & (NOPOWER | BROKEN)) - if(istype(mech)) - mech.occupant_message(SPAN_WARNING("Power port not responding. Terminating.")) - else - to_chat(M, SPAN_WARNING("Power port not responding. Terminating.")) - return - if(M.get_cell()) - if(istype(mech)) - mech.occupant_message(SPAN_NOTICE("Now charging...")) - else - to_chat(M, SPAN_NOTICE("Now charging...")) - charging = M - return +/obj/machinery/mech_recharger + name = "mech recharger" + desc = "A mech recharger, built into the floor." + icon = 'icons/mecha/mech_bay.dmi' + icon_state = "recharge_floor" + density = FALSE + anchored = TRUE + layer = TURF_LAYER + 0.1 + circuit = /obj/item/weapon/circuitboard/mech_recharger + + var/atom/movable/charging + var/charge = 45 + var/repair = 0 + var/list/chargable_types = list( + /obj/mecha, + /mob/living/silicon/robot/platform + ) + +/obj/machinery/mech_recharger/Initialize() + . = ..() + default_apply_parts() + +/obj/machinery/mech_recharger/Crossed(var/atom/movable/M) + . = ..() + if(charging == M) + return + for(var/mtype in chargable_types) + if(istype(M, mtype)) + start_charging(M) + return + +/obj/machinery/mech_recharger/Uncrossed(var/atom/movable/M) + . = ..() + if(M == charging) + charging = null + +/obj/machinery/mech_recharger/RefreshParts() + ..() + charge = 0 + repair = -5 + for(var/obj/item/weapon/stock_parts/P in component_parts) + if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + charge += P.rating * 20 + if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) + charge += P.rating * 5 + repair += P.rating + if(istype(P, /obj/item/weapon/stock_parts/manipulator)) + repair += P.rating * 2 + +/obj/machinery/mech_recharger/process() + ..() + if(!charging) + return + if(charging.loc != src.loc) // Could be qdel or teleport or something + charging = null + return + + var/done = FALSE + var/obj/mecha/mech = charging + var/obj/item/weapon/cell/cell = charging.get_cell() + if(cell) + var/t = min(charge, cell.maxcharge - cell.charge) + if(t > 0) + if(istype(mech)) + mech.give_power(t) + else + cell.give(t) + use_power(t * 150) + else + if(istype(mech)) + mech.occupant_message(SPAN_NOTICE("Fully charged.")) + done = TRUE + + if(repair && istype(mech) && mech.health < initial(mech.health)) + mech.health = min(mech.health + repair, initial(mech.health)) + if(mech.health == initial(mech.health)) + mech.occupant_message(SPAN_NOTICE("Fully repaired.")) + else + done = FALSE + if(done) + charging = null + +/obj/machinery/mech_recharger/attackby(var/obj/item/I, var/mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + +/obj/machinery/mech_recharger/proc/start_charging(var/atom/movable/M) + + var/obj/mecha/mech = M + if(stat & (NOPOWER | BROKEN)) + if(istype(mech)) + mech.occupant_message(SPAN_WARNING("Power port not responding. Terminating.")) + else + to_chat(M, SPAN_WARNING("Power port not responding. Terminating.")) + return + if(M.get_cell()) + if(istype(mech)) + mech.occupant_message(SPAN_NOTICE("Now charging...")) + else + to_chat(M, SPAN_NOTICE("Now charging...")) + charging = M + return diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index e7585d4ec29..ebc1e22aa14 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -1,710 +1,723 @@ -/obj/machinery/mecha_part_fabricator - icon = 'icons/obj/robotics_vr.dmi' //VOREStation Edit - New icon - icon_state = "mechfab" - name = "Exosuit Fabricator" - desc = "A machine used for the construction of mechas." - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 20 - active_power_usage = 5000 - req_access = list(access_robotics) - circuit = /obj/item/weapon/circuitboard/mechfab - - /// Current items in the build queue. - var/list/queue = list() - /// Whether or not the machine is building the entire queue automagically. - var/process_queue = FALSE - - /// The current design datum that the machine is building. - var/datum/design/being_built - /// World time when the build will finish. - var/build_finish = 0 - /// World time when the build started. - var/build_start = 0 - /// Reference to all materials used in the creation of the item being_built. - var/list/build_materials - /// Part currently stored in the Exofab. - var/obj/item/stored_part - - /// Coefficient for the speed of item building. Based on the installed parts. - var/time_coeff = 1 - /// Coefficient for the efficiency of material usage in item building. Based on the installed parts. - var/component_coeff = 1 - - var/loading_icon_state = "mechfab-idle" - - var/list/materials = list( - MAT_STEEL = 0, - MAT_GLASS = 0, - MAT_PLASTIC = 0, - MAT_GRAPHITE = 0, - MAT_PLASTEEL = 0, - MAT_GOLD = 0, - MAT_SILVER = 0, - MAT_LEAD = 0, - MAT_OSMIUM = 0, - MAT_DIAMOND = 0, - MAT_DURASTEEL = 0, - MAT_PHORON = 0, - MAT_URANIUM = 0, - MAT_VERDANTIUM = 0, - MAT_MORPHIUM = 0, - MAT_METALHYDROGEN = 0, - MAT_SUPERMATTER = 0) - var/res_max_amount = 200000 - - var/datum/research/files - var/valid_buildtype = MECHFAB - /// A list of categories that valid MECHFAB design datums will broadly categorise themselves under. - var/list/part_sets = list( - "Cyborg", - "Ripley", - "Odysseus", - "Gygax", - "Durand", - "Janus", - "Vehicle", - "Rigsuit", - "Phazon", - "Gopher", // VOREStation Add - "Polecat", // VOREStation Add - "Weasel", // VOREStation Add - "Exosuit Equipment", - "Exosuit Internals", - "Exosuit Ammunition", - "Cyborg Upgrade Modules", - "Cybernetics", - "Implants", - "Control Interfaces", - "Other", - "Misc", - ) - -/obj/machinery/mecha_part_fabricator/Initialize() - . = ..() - -// Go through all materials, and add them to the possible storage, but hide them unless we contain them. - for(var/Name in name_to_material) - if(Name in materials) - continue - - materials[Name] = 0 - - default_apply_parts() - files = new /datum/research(src) //Setup the research data holder. - -/obj/machinery/mecha_part_fabricator/dismantle() - for(var/f in materials) - eject_materials(f, -1) - ..() - -/obj/machinery/mecha_part_fabricator/RefreshParts() - res_max_amount = 0 - for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) - res_max_amount += M.rating * 100000 // 200k -> 600k - var/T = 0 - for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) - T += M.rating - component_coeff = max(1 - (T - 1) / 4, 0.2) // 1 -> 0.2 - for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; time_coeff is affected by both - T += M.rating - time_coeff = T / 2 // 1 -> 3 - update_tgui_static_data(usr) - - -/** - * Generates an info list for a given part. - * - * Returns a list of part information. - * * D - Design datum to get information on. - * * categories - Boolean, whether or not to parse snowflake categories into the part information list. - */ -/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D, var/categories = FALSE) - var/cost = list() - for(var/c in D.materials) - cost[c] = get_resource_cost_w_coeff(D, D.materials[c]) - - var/obj/built_item = D.build_path - - var/list/category_override = null - var/list/sub_category = null - - if(categories) - // Handle some special cases to build up sub-categories for the fab interface. - // Start with checking if this design builds a cyborg module. - if(built_item in typesof(/obj/item/borg/upgrade)) - var/obj/item/borg/upgrade/U = built_item - var/module_types = initial(U.module_flags) - sub_category = list() - if(module_types) - if(module_types & BORG_MODULE_SECURITY) - sub_category += "Security" - if(module_types & BORG_MODULE_MINER) - sub_category += "Mining" - if(module_types & BORG_MODULE_JANITOR) - sub_category += "Janitor" - if(module_types & BORG_MODULE_MEDICAL) - sub_category += "Medical" - if(module_types & BORG_MODULE_ENGINEERING) - sub_category += "Engineering" - if(module_types & BORG_MODULE_SCIENCE) - sub_category += "Science" - - else - sub_category += "All Cyborgs" - // Else check if this design builds a piece of exosuit equipment. - else if(built_item in typesof(/obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = built_item - var/mech_types = initial(E.mech_flags) - sub_category = "Equipment" - if(mech_types) - category_override = list() - if(mech_types & EXOSUIT_MODULE_RIPLEY) - category_override += "Ripley" - if(mech_types & EXOSUIT_MODULE_ODYSSEUS) - category_override += "Odysseus" - if(mech_types & EXOSUIT_MODULE_GYGAX) - category_override += "Gygax" - if(mech_types & EXOSUIT_MODULE_DURAND) - category_override += "Durand" - if(mech_types & EXOSUIT_MODULE_PHAZON) - category_override += "Phazon" - - var/list/part = list( - "name" = D.name, - "desc" = initial(built_item.desc), - "printTime" = get_construction_time_w_coeff(initial(D.time))/10, - "cost" = cost, - "id" = D.id, - "subCategory" = sub_category, - "categoryOverride" = category_override, - "searchMeta" = D.search_metadata - ) - - return part - - -/** - * Generates a list of resources / materials available to this Exosuit Fab - * - * Returns null if there is no material container available. - * List format is list(material_name = list(amount = ..., ref = ..., etc.)) - */ -/obj/machinery/mecha_part_fabricator/proc/output_available_resources() - var/list/material_data = list() - - for(var/mat_id in materials) - var/amount = materials[mat_id] - var/list/material_info = list( - "name" = mat_id, - "amount" = amount, - "sheets" = round(amount / SHEET_MATERIAL_AMOUNT), - "removable" = amount >= SHEET_MATERIAL_AMOUNT - ) - - material_data += list(material_info) - - return material_data - -/** - * Intended to be called when an item starts printing. - * - * Adds the overlay to show the fab working and sets active power usage settings. - */ -/obj/machinery/mecha_part_fabricator/proc/on_start_printing() - add_overlay("[icon_state]-active") - use_power = USE_POWER_ACTIVE - -/** - * Intended to be called when the exofab has stopped working and is no longer printing items. - * - * Removes the overlay to show the fab working and sets idle power usage settings. Additionally resets the description and turns off queue processing. - */ -/obj/machinery/mecha_part_fabricator/proc/on_finish_printing() - cut_overlay("[icon_state]-active") - use_power = USE_POWER_IDLE - desc = initial(desc) - process_queue = FALSE - -/** - * Calculates resource/material costs for printing an item based on the machine's resource coefficient. - * - * Returns a list of k,v resources with their amounts. - * * D - Design datum to calculate the modified resource cost of. - */ -/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) - var/list/resources = list() - for(var/mat_id in D.materials) - resources[mat_id] = get_resource_cost_w_coeff(D, D.materials[mat_id]) - return resources - -/** - * Checks if the Exofab has enough resources to print a given item. - * - * Returns FALSE if the design has no reagents used in its construction (?) or if there are insufficient resources. - * Returns TRUE if there are sufficient resources to print the item. - * * D - Design datum to calculate the modified resource cost of. - */ -/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) - if(length(D.chemicals)) // No reagents storage - no reagent designs. - return FALSE - . = TRUE - var/list/coeff_required = get_resources_w_coeff(D) - for(var/mat_id in coeff_required) - if(materials[mat_id] < coeff_required[mat_id]) - return FALSE - -/** - * Attempts to build the next item in the build queue. - * - * Returns FALSE if either there are no more parts to build or the next part is not buildable. - * Returns TRUE if the next part has started building. - * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. - */ -/obj/machinery/mecha_part_fabricator/proc/build_next_in_queue(verbose = TRUE) - if(!length(queue)) - return FALSE - - var/datum/design/D = queue[1] - if(build_part(D, verbose)) - remove_from_queue(1) - return TRUE - - return FALSE - -/** - * Starts the build process for a given design datum. - * - * Returns FALSE if the procedure fails. Returns TRUE when being_built is set. - * Uses materials. - * * D - Design datum to attempt to print. - * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. - */ -/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D, verbose = TRUE) - if(!D) - return FALSE - - if(!check_resources(D)) - if(verbose) - atom_say("Not enough resources. Processing stopped.") - return FALSE - - build_materials = get_resources_w_coeff(D) - for(var/mat_id in build_materials) - materials[mat_id] -= build_materials[mat_id] - - being_built = D - build_finish = world.time + get_construction_time_w_coeff(initial(D.time)) - build_start = world.time - desc = "It's building \a [D.name]." - - return TRUE - -/obj/machinery/mecha_part_fabricator/process() - ..() - // If there's a stored part to dispense due to an obstruction, try to dispense it. - if(stored_part) - var/turf/exit = get_step(src,(dir)) - if(exit.density) - return TRUE - - atom_say("Obstruction cleared. \The [stored_part] is complete.") - stored_part.forceMove(exit) - stored_part = null - - // If there's nothing being built, try to build something - if(!being_built) - // If we're not processing the queue anymore or there's nothing to build, end processing. - if(!process_queue || !build_next_in_queue()) - on_finish_printing() - return PROCESS_KILL - on_start_printing() - - // If there's an item being built, check if it is complete. - if(being_built && (build_finish < world.time)) - // Then attempt to dispense it and if appropriate build the next item. - dispense_built_part(being_built) - if(process_queue) - build_next_in_queue(FALSE) - return TRUE - - -/** - * Dispenses a part to the tile infront of the Exosuit Fab. - * - * Returns FALSE is the machine cannot dispense the part on the appropriate turf. - * Return TRUE if the part was successfully dispensed. - * * D - Design datum to attempt to dispense. - */ -/obj/machinery/mecha_part_fabricator/proc/dispense_built_part(datum/design/D) - var/obj/item/I = D.Fabricate(src, src) - // I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this. - // I.set_custom_materials(build_materials) - - being_built = null - - var/turf/exit = get_step(src,(dir)) - if(exit.density) - atom_say("Error! Part outlet is obstructed.") - desc = "It's trying to dispense \a [D.name], but the part outlet is obstructed." - stored_part = I - return FALSE - - atom_say("\The [I] is complete.") - I.forceMove(exit) - return I - -/** - * Adds a list of datum designs to the build queue. - * - * Will only add designs that are in this machine's stored techweb. - * Does final checks for datum IDs and makes sure this machine can build the designs. - * * part_list - List of datum design ids for designs to add to the queue. - */ -/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(list/part_list) - for(var/datum/design/D in files.known_designs) - if((D.build_type & valid_buildtype) && (D.id in part_list)) - add_to_queue(D) - -/** - * Adds a datum design to the build queue. - * - * Returns TRUE if successful and FALSE if the design was not added to the queue. - * * D - Datum design to add to the queue. - */ -/obj/machinery/mecha_part_fabricator/proc/add_to_queue(datum/design/D) - if(!istype(queue)) - queue = list() - if(D) - queue[++queue.len] = D - return TRUE - return FALSE - -/** - * Removes datum design from the build queue based on index. - * - * Returns TRUE if successful and FALSE if a design was not removed from the queue. - * * index - Index in the build queue of the element to remove. - */ -/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) - if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>length(queue))) - return FALSE - queue.Cut(index,++index) - return TRUE - -/** - * Generates a list of parts formatted for tgui based on the current build queue. - * - * Returns a formatted list of lists containing formatted part information for every part in the build queue. - */ -/obj/machinery/mecha_part_fabricator/proc/list_queue() - if(!istype(queue) || !length(queue)) - return null - - var/list/queued_parts = list() - for(var/datum/design/D in queue) - var/list/part = output_part_info(D) - queued_parts += list(part) - return queued_parts - -/obj/machinery/mecha_part_fabricator/proc/sync() - for(var/obj/machinery/computer/rdconsole/RDC in get_area_all_atoms(get_area(src))) - if(!RDC.sync) - continue - for(var/datum/tech/T in RDC.files.known_tech) - files.AddTech2Known(T) - for(var/datum/design/D in RDC.files.known_designs) - files.AddDesign2Known(D) - files.RefreshResearch() - update_tgui_static_data(usr) - atom_say("Successfully synchronized with R&D server.") - return - - atom_say("Unable to connect to local R&D server.") - return - -/** - * Calculates the coefficient-modified resource cost of a single material component of a design's recipe. - * - * Returns coefficient-modified resource cost for the given material component. - * * D - Design datum to pull the resource cost from. - * * resource - Material datum reference to the resource to calculate the cost of. - * * roundto - Rounding value for round() proc - */ -/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, var/amt, roundto = 1) - return round(amt * component_coeff, roundto) - -/** - * Calculates the coefficient-modified build time of a design. - * - * Returns coefficient-modified build time of a given design. - * * D - Design datum to calculate the modified build time of. - * * roundto - Rounding value for round() proc - */ -/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(construction_time, roundto = 1) //aran - return round(construction_time * time_coeff, roundto) - -/obj/machinery/mecha_part_fabricator/ui_assets(mob/user) - return list( - get_asset_datum(/datum/asset/spritesheet/sheetmaterials) - ) - -/obj/machinery/mecha_part_fabricator/attack_hand(var/mob/user) - if(..()) - return - if(!allowed(user)) - to_chat(user, SPAN_WARNING("\The [src] rejects your use due to lack of access!")) - return - tgui_interact(user) - -/obj/machinery/mecha_part_fabricator/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ExosuitFabricator", name) - ui.open() - -/obj/machinery/mecha_part_fabricator/tgui_static_data(mob/user) - var/list/data = list() - - var/list/final_sets = list() - var/list/buildable_parts = list() - - for(var/part_set in part_sets) - final_sets += part_set - - for(var/datum/design/D in files.known_designs) - if((D.build_type & valid_buildtype) && D.id != "id") // bugfix for weird null entries - // This is for us. - var/list/part = output_part_info(D, TRUE) - - if(part["categoryOverride"]) - for(var/cat in part["categoryOverride"]) - buildable_parts[cat] += list(part) - if(!(cat in part_sets)) - final_sets += cat - continue - - for(var/cat in part_sets) - // Find all matching categories. - if(!(cat in D.category)) - continue - - buildable_parts[cat] += list(part) - - data["partSets"] = final_sets - data["buildableParts"] = buildable_parts - - return data - -/obj/machinery/mecha_part_fabricator/tgui_data(mob/user) - var/list/data = list() - - data["materials"] = output_available_resources() - - if(being_built) - var/list/part = list( - "name" = being_built.name, - "duration" = build_finish - world.time, - "printTime" = get_construction_time_w_coeff(initial(being_built.time)) - ) - data["buildingPart"] = part - else - data["buildingPart"] = null - - data["queue"] = list_queue() - - if(stored_part) - data["storedPart"] = stored_part.name - else - data["storedPart"] = null - - data["isProcessingQueue"] = process_queue - - return data - -/obj/machinery/mecha_part_fabricator/tgui_act(action, var/list/params) - if(..()) - return TRUE - - . = TRUE - - add_fingerprint(usr) - usr.set_machine(src) - - switch(action) - if("sync_rnd") - // Sync with R&D Servers - sync() - return - if("add_queue_set") - // Add all parts of a set to queue - var/part_list = params["part_list"] - add_part_set_to_queue(part_list) - return - if("add_queue_part") - // Add a specific part to queue - var/T = params["id"] - for(var/datum/design/D in files.known_designs) - if((D.build_type & valid_buildtype) && (D.id == T)) - add_to_queue(D) - break - return - if("del_queue_part") - // Delete a specific from from the queue - var/index = text2num(params["index"]) - remove_from_queue(index) - return - if("clear_queue") - // Delete everything from queue - queue.Cut() - return - if("build_queue") - // Build everything in queue - if(process_queue) - return - process_queue = TRUE - - if(!being_built) - START_PROCESSING(SSobj, src) - return - if("stop_queue") - // Pause queue building. Also known as stop. - process_queue = FALSE - return - if("build_part") - // Build a single part - if(being_built || process_queue) - return - - var/id = params["id"] - var/datum/design/D = null - for(var/datum/design/D_new in files.known_designs) - if((D_new.build_type == valid_buildtype) && (D_new.id == id)) - D = D_new - break - - if(!D) - return - - if(build_part(D)) - on_start_printing() - START_PROCESSING(SSobj, src) - - return - if("move_queue_part") - // Moves a part up or down in the queue. - var/index = text2num(params["index"]) - var/new_index = index + text2num(params["newindex"]) - if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index)) - if(ISINRANGE(new_index,1,length(queue))) - queue.Swap(index,new_index) - return - if("remove_mat") - // Remove a material from the fab - var/mat_id = params["id"] - var/amount = text2num(params["amount"]) - eject_materials(mat_id, amount) - return - - return FALSE - -/obj/machinery/mecha_part_fabricator/attackby(var/obj/item/I, var/mob/user) - if(being_built) - to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") - return 1 - if(default_deconstruction_screwdriver(user, I)) - return - if(default_deconstruction_crowbar(user, I)) - return - if(default_part_replacement(user, I)) - return - - if(istype(I,/obj/item/stack/material)) - var/obj/item/stack/material/S = I - if(!(S.material.name in materials)) - to_chat(user, "The [src] doesn't accept [S.material]!") - return - - var/sname = "[S.name]" - var/amnt = S.perunit - if(materials[S.material.name] + amnt <= res_max_amount) - if(S && S.get_amount() >= 1) - var/count = 0 - flick("[loading_icon_state]", src) - // yess hacky but whatever //even more hacky now, but at least it works - if(loading_icon_state == "mechfab-idle") - flick("mechfab-load-metal", src) - while(materials[S.material.name] + amnt <= res_max_amount && S.get_amount() >= 1) - materials[S.material.name] += amnt - S.use(1) - count++ - to_chat(user, "You insert [count] [sname] into the fabricator.") - else - to_chat(user, "The fabricator cannot hold more [sname].") - - return - - ..() - -/obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) - switch(emagged) - if(0) - emagged = 0.5 - visible_message("\icon[src][bicon(src)] [src] beeps: \"DB error \[Code 0x00F1\]\"") - sleep(10) - visible_message("\icon[src][bicon(src)] [src] beeps: \"Attempting auto-repair\"") - sleep(15) - visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") - sleep(30) - visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") - req_access = null - emagged = 1 - return 1 - if(0.5) - visible_message("\icon[src][bicon(src)] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") - if(1) - visible_message("\icon[src][bicon(src)] [src] beeps: \"No records in User DB\"") - -/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything - var/recursive = amount == -1 ? TRUE : FALSE - var/matstring = lowertext(material) - - // 0 or null, nothing to eject - if(!materials[matstring]) - return - // Problem, fix problem and abort - if(materials[matstring] < 0) - warning("[src] tried to eject material '[material]', which it has 'materials[matstring]' of!") - materials[matstring] = 0 - return - - // Find the material datum for our material - var/datum/material/M = get_material_by_name(matstring) - if(!M) - warning("[src] tried to eject material '[matstring]', which didn't match any known material datum!") - return - // Find what type of sheets it makes - var/obj/item/stack/material/S = M.stack_type - if(!S) - warning("[src] tried to eject material '[matstring]', which didn't have a stack_type!") - return - - // If we were passed -1, then it's recursive ejection and we should eject all we can - if(amount <= 0) - amount = initial(S.max_amount) - // Smaller of what we have left, or the desired amount (note the amount is in sheets, but the array stores perunit values) - var/ejected = min(round(materials[matstring] / initial(S.perunit)), amount) - - // Place a sheet - S = M.place_sheet(get_turf(src), ejected) - if(!istype(S)) - warning("[src] tried to eject material '[material]', which didn't generate a proper stack when asked!") - return - - // Reduce our amount stored - materials[matstring] -= ejected * S.perunit - - // Recurse if we have enough left for more sheets - if(recursive && materials[matstring] >= S.perunit) - eject_materials(matstring, -1) +/obj/machinery/mecha_part_fabricator + icon = 'icons/obj/robotics_vr.dmi' //VOREStation Edit - New icon + icon_state = "mechfab" + name = "Exosuit Fabricator" + desc = "A machine used for the construction of mechas." + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 20 + active_power_usage = 5000 + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/mechfab + + /// Current items in the build queue. + var/list/queue = list() + /// Whether or not the machine is building the entire queue automagically. + var/process_queue = FALSE + + /// The current design datum that the machine is building. + var/datum/design/being_built + /// World time when the build will finish. + var/build_finish = 0 + /// World time when the build started. + var/build_start = 0 + /// Reference to all materials used in the creation of the item being_built. + var/list/build_materials + /// Part currently stored in the Exofab. + var/obj/item/stored_part + + /// Coefficient for the speed of item building. Based on the installed parts. + var/time_coeff = 1 + /// Coefficient for the efficiency of material usage in item building. Based on the installed parts. + var/component_coeff = 1 + + var/loading_icon_state = "mechfab-idle" + + var/list/materials = list( + MAT_STEEL = 0, + MAT_GLASS = 0, + MAT_PLASTIC = 0, + MAT_GRAPHITE = 0, + MAT_PLASTEEL = 0, + MAT_GOLD = 0, + MAT_SILVER = 0, + MAT_LEAD = 0, + MAT_OSMIUM = 0, + MAT_DIAMOND = 0, + MAT_DURASTEEL = 0, + MAT_PHORON = 0, + MAT_URANIUM = 0, + MAT_VERDANTIUM = 0, + MAT_MORPHIUM = 0, + MAT_METALHYDROGEN = 0, + MAT_SUPERMATTER = 0) + var/res_max_amount = 200000 + + var/datum/research/files + var/valid_buildtype = MECHFAB + /// A list of categories that valid MECHFAB design datums will broadly categorise themselves under. + var/list/part_sets = list( + "Cyborg", + "Ripley", + "Odysseus", + "Gygax", + "Durand", + "Janus", + "Vehicle", + "Rigsuit", + "Phazon", + "Gopher", // VOREStation Add + "Polecat", // VOREStation Add + "Weasel", // VOREStation Add + "Exosuit Equipment", + "Exosuit Internals", + "Exosuit Ammunition", + "Cyborg Upgrade Modules", + "Cybernetics", + "Implants", + "Control Interfaces", + "Other", + "Misc", + ) + +/obj/machinery/mecha_part_fabricator/Initialize() + . = ..() + +// Go through all materials, and add them to the possible storage, but hide them unless we contain them. + for(var/Name in name_to_material) + if(Name in materials) + continue + + materials[Name] = 0 + + default_apply_parts() + files = new /datum/research(src) //Setup the research data holder. + +/obj/machinery/mecha_part_fabricator/dismantle() + for(var/f in materials) + eject_materials(f, -1) + ..() + +/obj/machinery/mecha_part_fabricator/RefreshParts() + res_max_amount = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) + res_max_amount += M.rating * 100000 // 200k -> 600k + var/T = 0 + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + T += M.rating + component_coeff = max(1 - (T - 1) / 4, 0.2) // 1 -> 0.2 + for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; time_coeff is affected by both + T += M.rating + time_coeff = T / 2 // 1 -> 3 + update_tgui_static_data(usr) + + +/** + * Generates an info list for a given part. + * + * Returns a list of part information. + * * D - Design datum to get information on. + * * categories - Boolean, whether or not to parse snowflake categories into the part information list. + */ +/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D, var/categories = FALSE) + var/cost = list() + for(var/c in D.materials) + cost[c] = get_resource_cost_w_coeff(D, D.materials[c]) + + var/obj/built_item = D.build_path + + var/list/category_override = null + var/list/sub_category = null + + if(categories) + // Handle some special cases to build up sub-categories for the fab interface. + // Start with checking if this design builds a cyborg module. + if(built_item in typesof(/obj/item/borg/upgrade)) + var/obj/item/borg/upgrade/U = built_item + var/module_types = initial(U.module_flags) + sub_category = list() + if(module_types) + if(module_types & BORG_UTILITY) + sub_category += "All Cyborgs - Utility" + if(module_types & BORG_BASIC) + sub_category += "All Cyborgs - Basic" + if(module_types & BORG_ADVANCED) + sub_category += "All Cyborgs - Advanced" + if(module_types & BORG_MODULE_SECURITY) + sub_category += "Security" + if(module_types & BORG_MODULE_MINER) + sub_category += "Mining" + if(module_types & BORG_MODULE_JANITOR) + sub_category += "Janitor" + if(module_types & BORG_MODULE_MEDICAL) + sub_category += "Medical" + if(module_types & BORG_MODULE_ENGINEERING) + sub_category += "Engineering" + if(module_types & BORG_MODULE_SCIENCE) + sub_category += "Science" + if(module_types & BORG_MODULE_SERVICE) + sub_category += "Service" + if(module_types & BORG_MODULE_CLERIC) + sub_category += "Cleric" + if(module_types & BORG_MODULE_COMBAT) + sub_category += "Combat" + if(module_types & BORG_MODULE_EXPLO) + sub_category += "Exploration" + else + sub_category += "This shouldn't be here, bother a dev!" + // Else check if this design builds a piece of exosuit equipment. + else if(built_item in typesof(/obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = built_item + var/mech_types = initial(E.mech_flags) + sub_category = "Equipment" + if(mech_types) + category_override = list() + if(mech_types & EXOSUIT_MODULE_RIPLEY) + category_override += "Ripley" + if(mech_types & EXOSUIT_MODULE_ODYSSEUS) + category_override += "Odysseus" + if(mech_types & EXOSUIT_MODULE_GYGAX) + category_override += "Gygax" + if(mech_types & EXOSUIT_MODULE_DURAND) + category_override += "Durand" + if(mech_types & EXOSUIT_MODULE_PHAZON) + category_override += "Phazon" + + var/list/part = list( + "name" = D.name, + "desc" = initial(built_item.desc), + "printTime" = get_construction_time_w_coeff(initial(D.time))/10, + "cost" = cost, + "id" = D.id, + "subCategory" = sub_category, + "categoryOverride" = category_override, + "searchMeta" = D.search_metadata + ) + + return part + + +/** + * Generates a list of resources / materials available to this Exosuit Fab + * + * Returns null if there is no material container available. + * List format is list(material_name = list(amount = ..., ref = ..., etc.)) + */ +/obj/machinery/mecha_part_fabricator/proc/output_available_resources() + var/list/material_data = list() + + for(var/mat_id in materials) + var/amount = materials[mat_id] + var/list/material_info = list( + "name" = mat_id, + "amount" = amount, + "sheets" = round(amount / SHEET_MATERIAL_AMOUNT), + "removable" = amount >= SHEET_MATERIAL_AMOUNT + ) + + material_data += list(material_info) + + return material_data + +/** + * Intended to be called when an item starts printing. + * + * Adds the overlay to show the fab working and sets active power usage settings. + */ +/obj/machinery/mecha_part_fabricator/proc/on_start_printing() + add_overlay("[icon_state]-active") + use_power = USE_POWER_ACTIVE + +/** + * Intended to be called when the exofab has stopped working and is no longer printing items. + * + * Removes the overlay to show the fab working and sets idle power usage settings. Additionally resets the description and turns off queue processing. + */ +/obj/machinery/mecha_part_fabricator/proc/on_finish_printing() + cut_overlay("[icon_state]-active") + use_power = USE_POWER_IDLE + desc = initial(desc) + process_queue = FALSE + +/** + * Calculates resource/material costs for printing an item based on the machine's resource coefficient. + * + * Returns a list of k,v resources with their amounts. + * * D - Design datum to calculate the modified resource cost of. + */ +/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) + var/list/resources = list() + for(var/mat_id in D.materials) + resources[mat_id] = get_resource_cost_w_coeff(D, D.materials[mat_id]) + return resources + +/** + * Checks if the Exofab has enough resources to print a given item. + * + * Returns FALSE if the design has no reagents used in its construction (?) or if there are insufficient resources. + * Returns TRUE if there are sufficient resources to print the item. + * * D - Design datum to calculate the modified resource cost of. + */ +/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) + if(length(D.chemicals)) // No reagents storage - no reagent designs. + return FALSE + . = TRUE + var/list/coeff_required = get_resources_w_coeff(D) + for(var/mat_id in coeff_required) + if(materials[mat_id] < coeff_required[mat_id]) + return FALSE + +/** + * Attempts to build the next item in the build queue. + * + * Returns FALSE if either there are no more parts to build or the next part is not buildable. + * Returns TRUE if the next part has started building. + * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. + */ +/obj/machinery/mecha_part_fabricator/proc/build_next_in_queue(verbose = TRUE) + if(!length(queue)) + return FALSE + + var/datum/design/D = queue[1] + if(build_part(D, verbose)) + remove_from_queue(1) + return TRUE + + return FALSE + +/** + * Starts the build process for a given design datum. + * + * Returns FALSE if the procedure fails. Returns TRUE when being_built is set. + * Uses materials. + * * D - Design datum to attempt to print. + * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. + */ +/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D, verbose = TRUE) + if(!D) + return FALSE + + if(!check_resources(D)) + if(verbose) + atom_say("Not enough resources. Processing stopped.") + return FALSE + + build_materials = get_resources_w_coeff(D) + for(var/mat_id in build_materials) + materials[mat_id] -= build_materials[mat_id] + + being_built = D + build_finish = world.time + get_construction_time_w_coeff(initial(D.time)) + build_start = world.time + desc = "It's building \a [D.name]." + + return TRUE + +/obj/machinery/mecha_part_fabricator/process() + ..() + // If there's a stored part to dispense due to an obstruction, try to dispense it. + if(stored_part) + var/turf/exit = get_step(src,(dir)) + if(exit.density) + return TRUE + + atom_say("Obstruction cleared. \The [stored_part] is complete.") + stored_part.forceMove(exit) + stored_part = null + + // If there's nothing being built, try to build something + if(!being_built) + // If we're not processing the queue anymore or there's nothing to build, end processing. + if(!process_queue || !build_next_in_queue()) + on_finish_printing() + return PROCESS_KILL + on_start_printing() + + // If there's an item being built, check if it is complete. + if(being_built && (build_finish < world.time)) + // Then attempt to dispense it and if appropriate build the next item. + dispense_built_part(being_built) + if(process_queue) + build_next_in_queue(FALSE) + return TRUE + + +/** + * Dispenses a part to the tile infront of the Exosuit Fab. + * + * Returns FALSE is the machine cannot dispense the part on the appropriate turf. + * Return TRUE if the part was successfully dispensed. + * * D - Design datum to attempt to dispense. + */ +/obj/machinery/mecha_part_fabricator/proc/dispense_built_part(datum/design/D) + var/obj/item/I = D.Fabricate(src, src) + // I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this. + // I.set_custom_materials(build_materials) + + being_built = null + + var/turf/exit = get_step(src,(dir)) + if(exit.density) + atom_say("Error! Part outlet is obstructed.") + desc = "It's trying to dispense \a [D.name], but the part outlet is obstructed." + stored_part = I + return FALSE + + atom_say("\The [I] is complete.") + I.forceMove(exit) + return I + +/** + * Adds a list of datum designs to the build queue. + * + * Will only add designs that are in this machine's stored techweb. + * Does final checks for datum IDs and makes sure this machine can build the designs. + * * part_list - List of datum design ids for designs to add to the queue. + */ +/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(list/part_list) + for(var/datum/design/D in files.known_designs) + if((D.build_type & valid_buildtype) && (D.id in part_list)) + add_to_queue(D) + +/** + * Adds a datum design to the build queue. + * + * Returns TRUE if successful and FALSE if the design was not added to the queue. + * * D - Datum design to add to the queue. + */ +/obj/machinery/mecha_part_fabricator/proc/add_to_queue(datum/design/D) + if(!istype(queue)) + queue = list() + if(D) + queue[++queue.len] = D + return TRUE + return FALSE + +/** + * Removes datum design from the build queue based on index. + * + * Returns TRUE if successful and FALSE if a design was not removed from the queue. + * * index - Index in the build queue of the element to remove. + */ +/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) + if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>length(queue))) + return FALSE + queue.Cut(index,++index) + return TRUE + +/** + * Generates a list of parts formatted for tgui based on the current build queue. + * + * Returns a formatted list of lists containing formatted part information for every part in the build queue. + */ +/obj/machinery/mecha_part_fabricator/proc/list_queue() + if(!istype(queue) || !length(queue)) + return null + + var/list/queued_parts = list() + for(var/datum/design/D in queue) + var/list/part = output_part_info(D) + queued_parts += list(part) + return queued_parts + +/obj/machinery/mecha_part_fabricator/proc/sync() + for(var/obj/machinery/computer/rdconsole/RDC in get_area_all_atoms(get_area(src))) + if(!RDC.sync) + continue + for(var/datum/tech/T in RDC.files.known_tech) + files.AddTech2Known(T) + for(var/datum/design/D in RDC.files.known_designs) + files.AddDesign2Known(D) + files.RefreshResearch() + update_tgui_static_data(usr) + atom_say("Successfully synchronized with R&D server.") + return + + atom_say("Unable to connect to local R&D server.") + return + +/** + * Calculates the coefficient-modified resource cost of a single material component of a design's recipe. + * + * Returns coefficient-modified resource cost for the given material component. + * * D - Design datum to pull the resource cost from. + * * resource - Material datum reference to the resource to calculate the cost of. + * * roundto - Rounding value for round() proc + */ +/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, var/amt, roundto = 1) + return round(amt * component_coeff, roundto) + +/** + * Calculates the coefficient-modified build time of a design. + * + * Returns coefficient-modified build time of a given design. + * * D - Design datum to calculate the modified build time of. + * * roundto - Rounding value for round() proc + */ +/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(construction_time, roundto = 1) //aran + return round(construction_time * time_coeff, roundto) + +/obj/machinery/mecha_part_fabricator/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/sheetmaterials) + ) + +/obj/machinery/mecha_part_fabricator/attack_hand(var/mob/user) + if(..()) + return + if(!allowed(user)) + to_chat(user, SPAN_WARNING("\The [src] rejects your use due to lack of access!")) + return + tgui_interact(user) + +/obj/machinery/mecha_part_fabricator/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ExosuitFabricator", name) + ui.open() + +/obj/machinery/mecha_part_fabricator/tgui_static_data(mob/user) + var/list/data = list() + + var/list/final_sets = list() + var/list/buildable_parts = list() + + for(var/part_set in part_sets) + final_sets += part_set + + for(var/datum/design/D in files.known_designs) + if((D.build_type & valid_buildtype) && D.id != "id") // bugfix for weird null entries + // This is for us. + var/list/part = output_part_info(D, TRUE) + + if(part["categoryOverride"]) + for(var/cat in part["categoryOverride"]) + buildable_parts[cat] += list(part) + if(!(cat in part_sets)) + final_sets += cat + continue + + for(var/cat in part_sets) + // Find all matching categories. + if(!(cat in D.category)) + continue + + buildable_parts[cat] += list(part) + + data["partSets"] = final_sets + data["buildableParts"] = buildable_parts + + return data + +/obj/machinery/mecha_part_fabricator/tgui_data(mob/user) + var/list/data = list() + + data["materials"] = output_available_resources() + + if(being_built) + var/list/part = list( + "name" = being_built.name, + "duration" = build_finish - world.time, + "printTime" = get_construction_time_w_coeff(initial(being_built.time)) + ) + data["buildingPart"] = part + else + data["buildingPart"] = null + + data["queue"] = list_queue() + + if(stored_part) + data["storedPart"] = stored_part.name + else + data["storedPart"] = null + + data["isProcessingQueue"] = process_queue + + return data + +/obj/machinery/mecha_part_fabricator/tgui_act(action, var/list/params) + if(..()) + return TRUE + + . = TRUE + + add_fingerprint(usr) + usr.set_machine(src) + + switch(action) + if("sync_rnd") + // Sync with R&D Servers + sync() + return + if("add_queue_set") + // Add all parts of a set to queue + var/part_list = params["part_list"] + add_part_set_to_queue(part_list) + return + if("add_queue_part") + // Add a specific part to queue + var/T = params["id"] + for(var/datum/design/D in files.known_designs) + if((D.build_type & valid_buildtype) && (D.id == T)) + add_to_queue(D) + break + return + if("del_queue_part") + // Delete a specific from from the queue + var/index = text2num(params["index"]) + remove_from_queue(index) + return + if("clear_queue") + // Delete everything from queue + queue.Cut() + return + if("build_queue") + // Build everything in queue + if(process_queue) + return + process_queue = TRUE + + if(!being_built) + START_PROCESSING(SSobj, src) + return + if("stop_queue") + // Pause queue building. Also known as stop. + process_queue = FALSE + return + if("build_part") + // Build a single part + if(being_built || process_queue) + return + + var/id = params["id"] + var/datum/design/D = null + for(var/datum/design/D_new in files.known_designs) + if((D_new.build_type == valid_buildtype) && (D_new.id == id)) + D = D_new + break + + if(!D) + return + + if(build_part(D)) + on_start_printing() + START_PROCESSING(SSobj, src) + + return + if("move_queue_part") + // Moves a part up or down in the queue. + var/index = text2num(params["index"]) + var/new_index = index + text2num(params["newindex"]) + if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index)) + if(ISINRANGE(new_index,1,length(queue))) + queue.Swap(index,new_index) + return + if("remove_mat") + // Remove a material from the fab + var/mat_id = params["id"] + var/amount = text2num(params["amount"]) + eject_materials(mat_id, amount) + return + + return FALSE + +/obj/machinery/mecha_part_fabricator/attackby(var/obj/item/I, var/mob/user) + if(being_built) + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") + return 1 + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + + if(istype(I,/obj/item/stack/material)) + var/obj/item/stack/material/S = I + if(!(S.material.name in materials)) + to_chat(user, "The [src] doesn't accept [S.material]!") + return + + var/sname = "[S.name]" + var/amnt = S.perunit + if(materials[S.material.name] + amnt <= res_max_amount) + if(S && S.get_amount() >= 1) + var/count = 0 + flick("[loading_icon_state]", src) + // yess hacky but whatever //even more hacky now, but at least it works + if(loading_icon_state == "mechfab-idle") + flick("mechfab-load-metal", src) + while(materials[S.material.name] + amnt <= res_max_amount && S.get_amount() >= 1) + materials[S.material.name] += amnt + S.use(1) + count++ + to_chat(user, "You insert [count] [sname] into the fabricator.") + else + to_chat(user, "The fabricator cannot hold more [sname].") + + return + + ..() + +/obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) + switch(emagged) + if(0) + emagged = 0.5 + visible_message("\icon[src][bicon(src)] [src] beeps: \"DB error \[Code 0x00F1\]\"") + sleep(10) + visible_message("\icon[src][bicon(src)] [src] beeps: \"Attempting auto-repair\"") + sleep(15) + visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") + sleep(30) + visible_message("\icon[src][bicon(src)] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") + req_access = null + emagged = 1 + return 1 + if(0.5) + visible_message("\icon[src][bicon(src)] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") + if(1) + visible_message("\icon[src][bicon(src)] [src] beeps: \"No records in User DB\"") + +/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything + var/recursive = amount == -1 ? TRUE : FALSE + var/matstring = lowertext(material) + + // 0 or null, nothing to eject + if(!materials[matstring]) + return + // Problem, fix problem and abort + if(materials[matstring] < 0) + warning("[src] tried to eject material '[material]', which it has 'materials[matstring]' of!") + materials[matstring] = 0 + return + + // Find the material datum for our material + var/datum/material/M = get_material_by_name(matstring) + if(!M) + warning("[src] tried to eject material '[matstring]', which didn't match any known material datum!") + return + // Find what type of sheets it makes + var/obj/item/stack/material/S = M.stack_type + if(!S) + warning("[src] tried to eject material '[matstring]', which didn't have a stack_type!") + return + + // If we were passed -1, then it's recursive ejection and we should eject all we can + if(amount <= 0) + amount = initial(S.max_amount) + // Smaller of what we have left, or the desired amount (note the amount is in sheets, but the array stores perunit values) + var/ejected = min(round(materials[matstring] / initial(S.perunit)), amount) + + // Place a sheet + S = M.place_sheet(get_turf(src), ejected) + if(!istype(S)) + warning("[src] tried to eject material '[material]', which didn't generate a proper stack when asked!") + return + + // Reduce our amount stored + materials[matstring] -= ejected * S.perunit + + // Recurse if we have enough left for more sheets + if(recursive && materials[matstring] >= S.perunit) + eject_materials(matstring, -1) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 595e44a2cdf..07bba04dc1d 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,2913 +1,2913 @@ -#define MECHA_INT_FIRE 1 -#define MECHA_INT_TEMP_CONTROL 2 -#define MECHA_INT_SHORT_CIRCUIT 4 -#define MECHA_INT_TANK_BREACH 8 -#define MECHA_INT_CONTROL_LOST 16 - -#define MECHA_PROC_MOVEMENT 1 -#define MECHA_PROC_DAMAGE 2 -#define MECHA_PROC_INT_TEMP 4 - -#define MELEE 1 -#define RANGED 2 - -#define MECHA_OPERATING 0 -#define MECHA_BOLTS_SECURED 1 -#define MECHA_PANEL_LOOSE 2 -#define MECHA_CELL_OPEN 3 -#define MECHA_CELL_OUT 4 - -#define MECH_FACTION_NT "nano" -#define MECH_FACTION_SYNDI "syndi" -#define MECH_FACTION_NONE "none" - -/obj/mecha - name = "Mecha" - desc = "Exosuit" - description_info = "Alt click to strafe." - icon = 'icons/mecha/mecha.dmi' - density = TRUE //Dense. To raise the heat. - opacity = 1 //Opaque. Menacing. - anchored = TRUE //No pulling around. - unacidable = TRUE //And no deleting hoomans inside - layer = MOB_LAYER //Icon draw layer - infra_luminosity = 15 //Byond implementation is bugged. - var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) - var/can_move = 1 - var/mob/living/carbon/occupant = null - - var/step_in = 10 //Make a step in step_in/10 sec. - var/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in. - - var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South. - var/step_energy_drain = 10 - var/health = 300 //Health is health - var/maxhealth = 300 //Maxhealth is maxhealth. - var/deflect_chance = 10 //Chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - - var/damage_minimum = 10 //Incoming damage lower than this won't actually deal damage. Scrapes shouldn't be a real thing. - var/minimum_penetration = 15 //Incoming damage won't be fully applied if you don't have at least 20. Almost all AP clears this. - var/fail_penetration_value = 0.66 //By how much failing to penetrate reduces your shit. 66% by default. 100dmg = 66dmg if failed pen - - var/obj/item/weapon/cell/cell - var/state = MECHA_OPERATING - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - var/dna //Dna-locking the mech - var/list/proc_res = list() //Stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect/effect/system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - var/force = 0 - - var/mech_faction = null - var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once. - - var/stomp_sound = 'sound/mecha/mechstep.ogg' - var/swivel_sound = 'sound/mecha/mechturn.ogg' - - //inner atmos - var/use_internal_tank = 0 - var/internal_tank_valve = ONE_ATMOSPHERE - var/obj/machinery/portable_atmospherics/canister/internal_tank - var/datum/gas_mixture/cabin_air - var/obj/machinery/atmospherics/portables_connector/connected_port = null - - var/obj/item/device/radio/radio = null - - var/max_temperature = 25000 //Kelvin values. - var/internal_damage_threshold = 33 //Health percentage below which internal damage is possible - var/internal_damage_minimum = 15 //At least this much damage to trigger some real bad hurt. - var/internal_damage = 0 //Contains bitflags - - var/list/operation_req_access = list() //Required access level for mecha operation - var/list/internals_req_access = list(access_engine,access_robotics) //Required access level to open cell compartment - - var/wreckage - - var/list/equipment = new //This lists holds what stuff you bolted onto your baby ride - var/obj/item/mecha_parts/mecha_equipment/selected - var/max_equip = 2 - - // What direction to float in, if inertial movement is active. - var/float_direction = 0 - // Process() iterator count. - var/process_ticks = 0 - // These control what toggleable processes are executed within process(). - var/current_processes = MECHA_PROC_INT_TEMP - -//mechaequipt2 stuffs - var/list/hull_equipment = new - var/list/weapon_equipment = new - var/list/utility_equipment = new - var/list/universal_equipment = new - var/list/special_equipment = new - var/max_hull_equip = 2 - var/max_weapon_equip = 2 - var/max_utility_equip = 2 - var/max_universal_equip = 2 - var/max_special_equip = 1 - - var/list/starting_equipment = null // List containing starting tools. - -// Mech Components, similar to Cyborg, but Bigger. - var/list/internal_components = list( - MECH_HULL = null, - MECH_ACTUATOR = null, - MECH_ARMOR = null, - MECH_GAS = null, - MECH_ELECTRIC = null - ) - var/list/starting_components = list( - /obj/item/mecha_parts/component/hull, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - -//Working exosuit vars - var/list/cargo = list() - var/cargo_capacity = 3 - - var/static/image/radial_image_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject") - var/static/image/radial_image_airtoggle = image(icon= 'icons/mob/radial.dmi', icon_state = "radial_airtank") - var/static/image/radial_image_lighttoggle = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_light") - var/static/image/radial_image_statpanel = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine2") - -//Mech actions - var/datum/mini_hud/mech/minihud //VOREStation Edit - var/strafing = 0 //Are we strafing or not? - - var/defence_mode_possible = 0 //Can we even use defence mode? This is used to assign it to mechs and check for verbs. - var/defence_mode = 0 //Are we in defence mode - var/defence_deflect = 35 //How much it deflect - - var/overload_possible = 0 //Same as above. Don't forget to GRANT the verb&actions if you want everything to work proper. - var/overload = 0 //Are our legs overloaded - var/overload_coeff = 1 //How much extra energy you use when use the L E G - - var/zoom = 0 - var/zoom_possible = 0 - - var/thrusters = 0 - var/thrusters_possible = 0 - - var/phasing = 0 //Are we currently phasing - var/phasing_possible = 0 //This is to allow phasing. - var/can_phase = TRUE //This is an internal check during the relevant procs. - var/phasing_energy_drain = 200 - - var/switch_dmg_type_possible = 0 //Can you switch damage type? It is mostly for the Phazon and its children. - - var/smoke_possible = 0 - var/smoke_reserve = 5 //How many shots you have. Might make a reload later on. MIGHT. - var/smoke_ready = 1 //This is a check for the whether or not the cooldown is ongoing. - var/smoke_cooldown = 100 //How long you have between uses. - var/datum/effect/effect/system/smoke_spread/smoke_system = new - - var/cloak_possible = FALSE // Can this exosuit innately cloak? - -////All of those are for the HUD buttons in the top left. See Grant and Remove procs in mecha_actions. - - var/datum/action/innate/mecha/mech_eject/eject_action = new - var/datum/action/innate/mecha/mech_toggle_internals/internals_action = new - var/datum/action/innate/mecha/mech_toggle_lights/lights_action = new - var/datum/action/innate/mecha/mech_view_stats/stats_action = new - var/datum/action/innate/mecha/strafe/strafing_action = new - - var/datum/action/innate/mecha/mech_defence_mode/defence_action = new - var/datum/action/innate/mecha/mech_overload_mode/overload_action = new - var/datum/action/innate/mecha/mech_smoke/smoke_action = new - var/datum/action/innate/mecha/mech_zoom/zoom_action = new - var/datum/action/innate/mecha/mech_toggle_thrusters/thrusters_action = new - var/datum/action/innate/mecha/mech_cycle_equip/cycle_action = new - var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action = new - var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action = new - var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action = new - - var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times. - -/obj/mecha/Initialize() - . = ..() - - for(var/path in starting_components) - var/obj/item/mecha_parts/component/C = new path(src) - C.attach(src) - - if(starting_equipment && LAZYLEN(starting_equipment)) - for(var/path in starting_equipment) - var/obj/item/mecha_parts/mecha_equipment/ME = new path(src) - ME.attach(src) - - START_PROCESSING(SSobj, src) - - update_transform() - -/obj/mecha/drain_power(var/drain_check) - - if(drain_check) - return 1 - - if(!cell) - return 0 - - return cell.drain_power(drain_check) - -/obj/mecha/New() - ..() - icon_state += "-open" - add_radio() - add_cabin() - if(!add_airtank()) //we check this here in case mecha does not have an internal tank available by default - WIP - removeVerb(/obj/mecha/verb/connect_to_port) - removeVerb(/obj/mecha/verb/toggle_internal_tank) - - spark_system.set_up(2, 0, src) - spark_system.attach(src) - - if(smoke_possible)//I am pretty sure that's needed here. - src.smoke_system.set_up(3, 0, src) - src.smoke_system.attach(src) - - add_cell() - removeVerb(/obj/mecha/verb/disconnect_from_port) - log_message("[src.name] created.") - loc.Entered(src) - mechas_list += src //global mech list - return - -/obj/mecha/Exit(atom/movable/O) - if(O in cargo) - return 0 - return ..() - -/obj/mecha/Destroy() - src.go_out() - for(var/mob/M in src) //Be Extra Sure - M.forceMove(get_turf(src)) - M.loc.Entered(M) - if(M != src.occupant) - step_rand(M) - for(var/atom/movable/A in src.cargo) - A.forceMove(get_turf(src)) - var/turf/T = get_turf(A) - if(T) - T.Entered(A) - step_rand(A) - - if(loc) - loc.Exited(src) - - if(prob(30)) - explosion(get_turf(loc), 0, 0, 1, 3) - - if(wreckage) - var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc) - hull_equipment.Cut() - weapon_equipment.Cut() - utility_equipment.Cut() - universal_equipment.Cut() - special_equipment.Cut() - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.forceMove(WR) - E.equip_ready = TRUE - else - E.forceMove(loc) - E.destroy() - - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - if(istype(C)) - C.damage_part(rand(10, 20)) - C.detach() - WR.crowbar_salvage += C - C.forceMove(WR) - - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - else - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach(loc) - E.destroy() - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - if(istype(C)) - C.detach() - qdel(C) - if(cell) - qdel(cell) - if(internal_tank) - qdel(internal_tank) - equipment.Cut() - cell = null - internal_tank = null - - if(smoke_possible) //Just making sure nothing is running. - qdel(smoke_system) - - GLOB.mech_destroyed_roundstat++ - - QDEL_NULL(spark_system) - QDEL_NULL(minihud) - - STOP_PROCESSING(SSobj, src) - - mechas_list -= src //global mech list - . = ..() - -// The main process loop to replace the ancient global iterators. -// It's a bit hardcoded but I don't see anyone else adding stuff to -// mechas, and it's easy enough to modify. -/obj/mecha/process() - var/static/max_ticks = 16 - - if (current_processes & MECHA_PROC_MOVEMENT) - process_inertial_movement() - - if ((current_processes & MECHA_PROC_DAMAGE) && !(process_ticks % 2)) - process_internal_damage() - - if ((current_processes & MECHA_PROC_INT_TEMP) && !(process_ticks % 4)) - process_preserve_temp() - - if (!(process_ticks % 3)) - process_tank_give_air() - - // Max value is 16. So we let it run between [0, 16] with this. - process_ticks = (process_ticks + 1) % 17 - -// Normalizing cabin air temperature to 20 degrees celsius. -// Called every fourth process() tick (20 deciseconds). -/obj/mecha/proc/process_preserve_temp() - if (cabin_air && cabin_air.volume > 0) - var/delta = cabin_air.temperature - T20C - cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1))) - -// Handles internal air tank action. -// Called every third process() tick (15 deciseconds). -/obj/mecha/proc/process_tank_give_air() - if(internal_tank) - var/datum/gas_mixture/tank_air = internal_tank.return_air() - - var/release_pressure = internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.temperature > 0) - transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = get_turf_air() - pressure_delta = cabin_pressure - release_pressure - - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - qdel(removed) - -// Inertial movement in space. -// Called every process() tick (5 deciseconds). -/obj/mecha/proc/process_inertial_movement() - if(float_direction) - if(!step(src, float_direction) || check_for_support()) - stop_process(MECHA_PROC_MOVEMENT) - else - stop_process(MECHA_PROC_MOVEMENT) - return - -// Processes internal damage. -// Called every other process() tick (10 deciseconds). -/obj/mecha/proc/process_internal_damage() - if(!hasInternalDamage()) - stop_process(MECHA_PROC_DAMAGE) - return - - if(hasInternalDamage(MECHA_INT_FIRE)) - if(!hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5)) - clearInternalDamage(MECHA_INT_FIRE) - if(internal_tank) - if(internal_tank.return_pressure()>internal_tank.maximum_pressure && !(hasInternalDamage(MECHA_INT_TANK_BREACH))) - setInternalDamage(MECHA_INT_TANK_BREACH) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - if(int_tank_air && int_tank_air.volume>0) //heat the air_contents - int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15)) - if(cabin_air && cabin_air.volume>0) - cabin_air.temperature = min(6000+T0C, cabin_air.temperature+rand(10,15)) - if(cabin_air.temperature>max_temperature/2) - take_damage(4/round(max_temperature/cabin_air.temperature,0.1),"fire") - - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - stop_process(MECHA_PROC_INT_TEMP) - - if(hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(istype(loc, /turf/simulated)) - loc.assume_air(leaked_gas) - else - qdel(leaked_gas) - - if(hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - if(get_charge()) - spark_system.start() - cell.charge -= min(20,cell.charge) - cell.maxcharge -= min(20,cell.maxcharge) - return - -//////////////////////// -////// Helpers ///////// -//////////////////////// - -/obj/mecha/proc/removeVerb(verb_path) - verbs -= verb_path - -/obj/mecha/proc/addVerb(verb_path) - verbs += verb_path - -/obj/mecha/proc/add_airtank() - internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) - return internal_tank - -/obj/mecha/proc/add_cell(var/obj/item/weapon/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new /obj/item/weapon/cell/mech(src) - -/obj/mecha/get_cell() - return cell - -/obj/mecha/proc/add_cabin() - cabin_air = new - cabin_air.temperature = T20C - cabin_air.volume = 200 - cabin_air.adjust_multi("oxygen", O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), "nitrogen", N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)) - return cabin_air - -/obj/mecha/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/mecha/proc/do_after(delay as num) - sleep(delay) - if(src) - return 1 - return 0 - -/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5) - var/delayfraction = delay/numticks - - var/turf/T = user.loc - - for(var/i = 0, iMaintenance protocols in effect") - return - - if(phasing)//Phazon and other mechs with phasing. - src.occupant_message("Unable to interact with objects while phasing")//Haha dumbass. - return - - if(!get_charge()) return - if(src == target) return - var/dir_to_target = get_dir(src,target) - if(dir_to_target && !(dir_to_target & src.dir))//wrong direction - return - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - target = safepick(view(3,target)) - if(!target) - return - if(istype(target, /obj/machinery)) - if (src.interface_action(target)) - return - if(!target.Adjacent(src)) - if(selected && selected.is_ranged()) - selected.action(target) - else if(selected && selected.is_melee()) - selected.action(target, params) - else - src.melee_action(target) - return - -/obj/mecha/proc/interface_action(obj/machinery/target) - if(istype(target, /obj/machinery/access_button)) - src.occupant_message("Interfacing with [target].") - src.log_message("Interfaced with [target].") - target.attack_hand(src.occupant) - return 1 - if(istype(target, /obj/machinery/embedded_controller)) - target.tgui_interact(src.occupant) - return 1 - return 0 - -/obj/mecha/contents_tgui_distance(var/src_object, var/mob/living/user) - . = user.shared_living_tgui_distance(src_object) //allow them to interact with anything they can interact with normally. - if(. != STATUS_INTERACTIVE) - //Allow interaction with the mecha or anything that is part of the mecha - if(src_object == src || (src_object in src)) - return STATUS_INTERACTIVE - if(src.Adjacent(src_object)) - src.occupant_message("Interfacing with [src_object]...") - src.log_message("Interfaced with [src_object].") - return STATUS_INTERACTIVE - if(src_object in view(2, src)) - return STATUS_UPDATE //if they're close enough, allow the occupant to see the screen through the viewport or whatever. - -/obj/mecha/proc/melee_action(atom/target) - return - -/obj/mecha/proc/range_action(atom/target) - return - - -////////////////////////////////// -//////// Movement procs //////// -////////////////////////////////// - -/obj/mecha/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - MoveAction() - -/obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves - if(!equipment.len) - return - - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - ME.MoveAction() - -/obj/mecha/relaymove(mob/user,direction) - if(user != src.occupant) //While not "realistic", this piece is player friendly. - if(istype(user,/mob/living/carbon/brain)) - to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") - return 0 - user.forceMove(get_turf(src)) - to_chat(user, "You climb out from [src]") - return 0 - - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - if(!HC) - occupant_message("You can't operate an exosuit that doesn't have a hull!") - return - - if(connected_port) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while connected to the air system port") - last_message = world.time - return 0 - if(state) - occupant_message("Maintenance protocols in effect") - return -/* - if(zoom) - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 -*/ - return domove(direction) - -/obj/mecha/proc/can_ztravel() - for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) - return jp.equip_ready - return FALSE - -/obj/mecha/proc/domove(direction) - - return call((proc_res["dyndomove"]||src), "dyndomove")(direction) - -/obj/mecha/proc/get_step_delay() - var/tally = 0 - - if(LAZYLEN(equipment)) - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(ME.get_step_delay()) - tally += ME.get_step_delay() - - if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight. - tally = 0 - else // Otherwise, start the tally after cutting that gap out. - tally -= encumbrance_gap - - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - if(C && C.get_step_delay()) - tally += C.get_step_delay() - - var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR] - - if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast. - tally = 2 SECONDS - - return tally - - tally += 0.5 SECONDS * (1 - actuator.get_efficiency()) // Damaged actuators run slower, slowing as damage increases beyond its threshold. - - if(strafing) - tally = round(tally * actuator.strafing_multiplier) - - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(istype(ME, /obj/item/mecha_parts/mecha_equipment/speedboost)) - var/obj/item/mecha_parts/mecha_equipment/speedboost/SB = ME - for(var/path in ME.required_type) - if(istype(src, path)) - tally = round(tally * SB.slowdown_multiplier) - break - break - - if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0. - tally = min(1, round(tally/2)) - - return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that. - -/obj/mecha/proc/dyndomove(direction) - if(!can_move) - return 0 - if(current_processes & MECHA_PROC_MOVEMENT) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - - //Can we even move, below is if yes. - - if(defence_mode)//Check if we are currently locked down - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in defence mode") - last_message = world.time - return 0 - - if(zoom)//:eyes: - if(world.time - last_message > 20) - src.occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - - if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) //I think this mean 'if you try to move in space without thruster, u no move' - return 0 - - if(overload)//Check if you have leg overload - health-- - if(health < initial(health) - initial(health)/3) - overload = 0 - step_energy_drain = initial(step_energy_drain) - src.occupant_message("Leg actuators damage threshold exceded. Disabling overload.") - - - var/move_result = 0 - - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - move_result = mechsteprand() - //Up/down zmove - else if(direction == UP || direction == DOWN) - if(!can_ztravel()) - occupant_message("Your vehicle lacks the capacity to move in that direction!") - return FALSE - - //We're using locs because some mecha are 2x2 turfs. So thicc! - var/result = TRUE - - for(var/turf/T in locs) - if(!T.CanZPass(src,direction)) - occupant_message("You can't move that direction from here!") - result = FALSE - break - var/turf/dest = (direction == UP) ? GetAbove(src) : GetBelow(src) - if(!dest) - occupant_message("There is nothing of interest in this direction.") - result = FALSE - break - if(!dest.CanZPass(src,direction)) - occupant_message("There's something blocking your movement in that direction!") - result = FALSE - break - if(result) - move_result = mechstep(direction) - - //Turning - - else if(src.dir != direction) - - if(strafing) - move_result = mechstep(direction) - else - move_result = mechturn(direction) - - //Stepping - else - move_result = mechstep(direction) - - - if(move_result) - can_move = 0 - use_power(step_energy_drain) - if(istype(src.loc, /turf/space)) - if(!src.check_for_support()) - float_direction = direction - start_process(MECHA_PROC_MOVEMENT) - src.log_message("Movement control lost. Inertial movement started.") - if(do_after(get_step_delay())) - can_move = 1 - return 1 - return 0 - -/obj/mecha/proc/handle_equipment_movement() - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(ME.chassis == src) //Sanity - ME.handle_movement_action() - return - -/obj/mecha/proc/mechturn(direction) - set_dir(direction) - if(swivel_sound) - playsound(src,swivel_sound,40,1) - return 1 - -/obj/mecha/proc/mechstep(direction) - var/current_dir = dir //For strafing - var/result = get_step(src,direction) - if(result && Move(result)) - if(stomp_sound) - playsound(src,stomp_sound,40,1) - handle_equipment_movement() - if(strafing) //Also for strafing - set_dir(current_dir) - return result - - -/obj/mecha/proc/mechsteprand() - var/result = get_step_rand(src) - if(result && Move(result)) - if(stomp_sound) - playsound(src,stomp_sound,40,1) - handle_equipment_movement() - return result - -/obj/mecha/Bump(var/atom/obstacle) -// src.inertia_dir = null - if(istype(obstacle, /mob))//First we check if it is a mob. Mechs mostly shouln't go through them, even while phasing. - var/mob/M = obstacle - M.Move(get_step(obstacle,src.dir)) - else if(phasing && get_charge()>=phasing_energy_drain)//Phazon check. This could use an improvement elsewhere. - src.use_power(phasing_energy_drain) - phase() - . = ..(obstacle) - return - else if(istype(obstacle, /obj))//Then we check for regular obstacles. - var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - src.anchored = 0 // Portals can only move unanchored objects. - O.Crossed(src) - spawn(0)//countering portal teleport spawn(0), hurr - src.anchored = 1 - if(O.anchored) - obstacle.Bumped(src) - else - step(obstacle,src.dir) - - else//No idea when this triggers, so i won't touch it. - . = ..(obstacle) - return - -/obj/mecha/proc/phase() // Force the mecha to move forward by phasing. - set waitfor = FALSE - if(can_phase) - can_phase = FALSE - flick("[initial_icon]-phase", src) - forceMove(get_step(src,src.dir)) - sleep(get_step_delay() * 3) - can_phase = TRUE - occupant_message("Phazed.") - return TRUE // In the event this is sequenced - return FALSE - -/////////////////////////////////// -//////// Internal damage //////// -/////////////////////////////////// - -//ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. -/obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) - if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return - if(prob(30)) - if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - var/int_dam_flag = safepick(possible_int_damage) - if(int_dam_flag) - setInternalDamage(int_dam_flag) - return //It already hurts to get some, lets not get both. - - if(prob(10)) - if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) - var/obj/item/mecha_parts/mecha_equipment/destr = safepick(equipment) - if(destr) - destr.destroy() - return - -/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) - return int_dam_flag ? internal_damage&int_dam_flag : internal_damage - - -/obj/mecha/proc/setInternalDamage(int_dam_flag) - internal_damage |= int_dam_flag - start_process(MECHA_PROC_DAMAGE) - log_append_to_last("Internal damage of type [int_dam_flag].",1) - occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding. - return - -/obj/mecha/proc/clearInternalDamage(int_dam_flag) - internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - occupant_message("Life support system reactivated.") - start_process(MECHA_PROC_INT_TEMP) - if(MECHA_INT_FIRE) - occupant_message("Internal fire extinquished.") - if(MECHA_INT_TANK_BREACH) - occupant_message("Damaged internal tank has been sealed.") - return - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/take_damage(amount, type="brute") - update_damage_alerts() - if(amount) - var/damage = absorbDamage(amount,type) - - damage = components_handle_damage(damage,type) - - health -= damage - - update_health() - log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) - return - -/obj/mecha/proc/components_handle_damage(var/damage, var/type = BRUTE) - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - - if(AC) - var/armor_efficiency = AC.get_efficiency() - var/damage_change = armor_efficiency * (damage * 0.5) * AC.damage_absorption[type] - AC.damage_part(damage_change, type) - damage -= damage_change - - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - - if(HC) - if(HC.integrity) - var/hull_absorb = round(rand(5, 10) / 10, 0.1) * damage - HC.damage_part(hull_absorb, type) - damage -= hull_absorb - - for(var/obj/item/mecha_parts/component/C in (internal_components - list(MECH_HULL, MECH_ARMOR))) - if(prob(C.relative_size)) - var/damage_part_amt = round(damage / 4, 0.1) - C.damage_part(damage_part_amt) - damage -= damage_part_amt - - return damage - -/obj/mecha/proc/get_damage_absorption() - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - if(istype(AC) && AC.get_efficiency() > 0.25) - return AC.damage_absorption - -/obj/mecha/proc/absorbDamage(damage,damage_type) - return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type) - -/obj/mecha/proc/dynabsorbdamage(damage,damage_type) - return damage*(listgetindex(get_damage_absorption(),damage_type) || 1) - -/obj/mecha/airlock_crush(var/crush_damage) - ..() - take_damage(crush_damage) - if(prob(50)) //Try to avoid that. - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return 1 - -/obj/mecha/proc/update_health() - if(src.health > 0) - src.spark_system.start() - else - qdel(src) - return - -/obj/mecha/attack_hand(mob/user as mob) - if(user == occupant) - show_radial_occupant(user) - return - - user.setClickCooldown(user.get_attack_speed()) - src.log_message("Attack by hand/paw. Attacker - [user].",1) - - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - - if(!ArmC) - temp_deflect_chance = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(user)) - if(!prob(temp_deflect_chance)) - src.take_damage(15) //The take_damage() proc handles armor values - if(prob(25)) //Why would they get free internal damage. At least make it a bit RNG. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) - to_chat(user, "You slash at the armored suit!") - visible_message("\The [user] slashes at [src.name]'s armor!") - else - src.log_append_to_last("Armor saved.") - playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) - to_chat(user, "Your claws had no effect!") - src.occupant_message("\The [user]'s claws are stopped by the armor.") - visible_message("\The [user] rebounds off [src.name]'s armor!") - else - user.visible_message("\The [user] hits \the [src]. Nothing happens.","You hit \the [src] with no visible effect.") - src.log_append_to_last("Armor saved.") - return - else if ((HULK in user.mutations) && !prob(temp_deflect_chance)) - src.take_damage(15) //The take_damage() proc handles armor values - if(prob(25)) //Hulks punch hard but lets not give them consistent internal damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - user.visible_message("[user] hits [src.name], doing some damage.", "You hit [src.name] with all your might. The metal creaks and bends.") - else - user.visible_message("[user] hits [src.name]. Nothing happens.","You hit [src.name] with no visible effect.") - src.log_append_to_last("Armor saved.") - return - -/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper - ..() - src.log_message("Hit by [A].",1) - call((proc_res["dynhitby"]||src), "dynhitby")(A) - return - -//I think this is relative to throws. -/obj/mecha/proc/dynhitby(atom/movable/A) - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - var/temp_minimum_penetration = minimum_penetration - var/temp_fail_penetration_value = fail_penetration_value - - if(!ArmC) - temp_deflect_chance = 0 - temp_damage_minimum = 0 - temp_minimum_penetration = 0 - temp_fail_penetration_value = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) - temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) - - if(istype(A, /obj/item/mecha_parts/mecha_tracking)) - A.forceMove(src) - src.visible_message("The [A] fastens firmly to [src].") - return - if(prob(temp_deflect_chance) || istype(A, /mob)) - src.occupant_message("\The [A] bounces off the armor.") - src.visible_message("\The [A] bounces off \the [src] armor") - src.log_append_to_last("Armor saved.") - if(istype(A, /mob/living)) - var/mob/living/M = A - M.take_organ_damage(10) - else if(istype(A, /obj)) - var/obj/O = A - if(O.throwforce) - - var/pass_damage = O.throwforce - var/pass_damage_reduc_mod - if(pass_damage <= temp_damage_minimum)//Too little to go through. - src.occupant_message("\The [A] bounces off the armor.") - src.visible_message("\The [A] bounces off \the [src] armor") - return - - else if(O.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage - src.occupant_message("\The [A] struggles to bypass \the [src] armor.") - src.visible_message("\The [A] struggles to bypass \the [src] armor") - pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default - else - src.occupant_message("\The [A] manages to pierce \the [src] armor.") -// src.visible_message("\The [A] manages to pierce \the [src] armor") - pass_damage_reduc_mod = 1 - - - - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - pass_damage = ME.handle_ranged_contact(A, pass_damage) - - pass_damage = (pass_damage*pass_damage_reduc_mod)//Applying damage reduction - src.take_damage(pass_damage) //The take_damage() proc handles armor values - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return - - -/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper - if(istype(Proj, /obj/item/projectile/test)) - var/obj/item/projectile/test/Test = Proj - Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. - return - - src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1) - call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment - ..() - return - -/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj) - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - var/temp_minimum_penetration = minimum_penetration - var/temp_fail_penetration_value = fail_penetration_value - - if(!ArmC) - temp_deflect_chance = 0 - temp_damage_minimum = 0 - temp_minimum_penetration = 0 - temp_fail_penetration_value = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) - temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) - - if(prob(temp_deflect_chance)) - src.occupant_message("The armor deflects incoming projectile.") - src.visible_message("The [src.name] armor deflects the projectile") - src.log_append_to_last("Armor saved.") - return - - if(Proj.damage_type == HALLOSS) - use_power(Proj.agony * 5) - - if(!(Proj.nodamage)) - var/ignore_threshold - if(istype(Proj, /obj/item/projectile/beam/pulse)) //ATM, this is literally only for the pulse rifles used mostly by deathsquads. - ignore_threshold = 1 - - var/pass_damage = Proj.damage - var/pass_damage_reduc_mod - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - pass_damage = ME.handle_projectile_contact(Proj, pass_damage) - - if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. - src.occupant_message("The armor deflects incoming projectile.") - src.visible_message("The [src.name] armor deflects\the [Proj]") - return - - else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage - src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") - src.visible_message("\The [Proj] struggles to pierce \the [src] armor") - pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default - - else //You go through completely because you use AP. Nice. - src.occupant_message("\The [Proj] manages to pierce \the [src] armor.") -// src.visible_message("\The [Proj] manages to pierce \the [src] armor") - pass_damage_reduc_mod = 1 - - pass_damage = (pass_damage_reduc_mod*pass_damage)//Apply damage reduction before usage. - src.take_damage(pass_damage, Proj.check_armour) //The take_damage() proc handles armor values - if(prob(25)) - spark_system.start() - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) - - //AP projectiles have a chance to cause additional damage - if(Proj.penetrating) - var/distance = get_dist(Proj.starting, get_turf(loc)) - var/hit_occupant = 1 //only allow the occupant to be hit once - for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) - if(src.occupant && hit_occupant && prob(20)) - Proj.attack_mob(src.occupant, distance) - hit_occupant = 0 - else - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) - - Proj.penetrating-- - - if(prob(15)) - break //give a chance to exit early - - Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? - return - -//This refer to whenever you are caught in an explosion. -/obj/mecha/ex_act(severity) - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - - if(!ArmC) - temp_deflect_chance = 0 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - - src.log_message("Affected by explosion of severity: [severity].",1) - if(prob(temp_deflect_chance)) - severity++ - src.log_append_to_last("Armor saved, changing severity to [severity].") - switch(severity) - if(1.0) - src.take_damage(initial(src.health), "bomb") - if(2.0) - if (prob(30)) - src.take_damage(initial(src.health), "bomb") - else - src.take_damage(initial(src.health)/2, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - if(3.0) - if (prob(5)) - qdel(src) - else - src.take_damage(initial(src.health)/5, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - return - -/*Will fix later -Sieve -/obj/mecha/attack_blob(mob/user as mob) - src.log_message("Attack by blob. Attacker - [user].",1) - if(!prob(src.deflect_chance)) - src.take_damage(6) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) - to_chat(user, "You smash at the armored suit!") - for (var/mob/V in viewers(src)) - if(V.client && !(V.blinded)) - V.show_message("\The [user] smashes against [src.name]'s armor!", 1) - else - src.log_append_to_last("Armor saved.") - playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) - to_chat(user, "Your attack had no effect!") - src.occupant_message("\The [user]'s attack is stopped by the armor.") - for (var/mob/V in viewers(src)) - if(V.client && !(V.blinded)) - V.show_message("\The [user] rebounds off the [src.name] armor!", 1) - return -*/ - -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/2)/severity) - take_damage(50 / severity,"energy") - src.log_message("EMP detected",1) - if(prob(80)) - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) - return - -/obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature>src.max_temperature) - src.log_message("Exposed to dangerous temperature.",1) - src.take_damage(5,"fire") //The take_damage() proc handles armor values - src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - return - -/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(user.get_attack_speed(W)) - src.log_message("Attacked by [W]. Attacker - [user]") - var/pass_damage_reduc_mod //Modifer for failing to bring AP. - - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - var/temp_minimum_penetration = minimum_penetration - var/temp_fail_penetration_value = fail_penetration_value - - if(!ArmC) - temp_deflect_chance = 0 - temp_damage_minimum = 0 - temp_minimum_penetration = 0 - temp_fail_penetration_value = 1 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) - temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) - - if(prob(temp_deflect_chance)) //Does your attack get deflected outright. - src.occupant_message("\The [W] bounces off [src.name].") - to_chat(user, "\The [W] bounces off [src.name].") - src.log_append_to_last("Armor saved.") - - else if(W.force < temp_damage_minimum) //Is your attack too PATHETIC to do anything. 3 damage to a person shouldn't do anything to a mech. - src.occupant_message("\The [W] bounces off the armor.") - src.visible_message("\The [W] bounces off \the [src] armor") - return - - else if(W.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage - src.occupant_message("\The [W] struggles to bypass \the [src] armor.") - src.visible_message("\The [W] struggles to bypass \the [src] armor") - pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default - - else - pass_damage_reduc_mod = 1 //Just making sure. - src.occupant_message("[user] hits [src] with [W].") - user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") - - var/pass_damage = W.force - pass_damage = (pass_damage*pass_damage_reduc_mod) //Apply the reduction of damage from not having enough armor penetration. This is not regular armor values at play. - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - pass_damage = ME.handle_projectile_contact(W, user, pass_damage) - src.take_damage(pass_damage,W.damtype) //The take_damage() proc handles armor values - if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob) - - if(istype(W, /obj/item/device/mmi)) - if(mmi_move_inside(W,user)) - to_chat(user, "[src]-MMI interface initialized successfuly") - else - to_chat(user, "[src]-MMI interface initialization failed.") - return - - if(istype(W, /obj/item/device/robotanalyzer)) - var/obj/item/device/robotanalyzer/RA = W - RA.do_scan(src, user) - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - user.drop_item() - E.attach(src) - user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]") - else - to_chat(user, "You were unable to attach [W] to [src]") - return - - if(istype(W, /obj/item/mecha_parts/component) && state == MECHA_CELL_OUT) - var/obj/item/mecha_parts/component/MC = W - spawn() - if(MC.attach(src)) - user.drop_item() - MC.forceMove(src) - user.visible_message("[user] installs \the [W] in \the [src]", "You install \the [W] in \the [src].") - return - - if(istype(W, /obj/item/weapon/card/robot)) - var/obj/item/weapon/card/robot/RoC = W - return attackby(RoC.dummy_card, user) - - if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(add_req_access || maint_access) - if(internals_access_allowed(usr)) - var/obj/item/weapon/card/id/id_card - if(istype(W, /obj/item/weapon/card/id)) - id_card = W - else - var/obj/item/device/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - to_chat(user, "Invalid ID: Access denied.") - else - to_chat(user, "Maintenance protocols disabled by operator.") - else if(W.is_wrench()) - if(state==MECHA_BOLTS_SECURED) - state = MECHA_PANEL_LOOSE - to_chat(user, "You undo the securing bolts.") - else if(state==MECHA_PANEL_LOOSE) - state = MECHA_BOLTS_SECURED - to_chat(user, "You tighten the securing bolts.") - return - else if(W.is_crowbar()) - if(state==MECHA_PANEL_LOOSE) - state = MECHA_CELL_OPEN - to_chat(user, "You open the hatch to the power unit") - else if(state==MECHA_CELL_OPEN) - state=MECHA_PANEL_LOOSE - to_chat(user, "You close the hatch to the power unit") - else if(state==MECHA_CELL_OUT) - var/list/removable_components = list() - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/MC = internal_components[slot] - if(istype(MC)) - removable_components[MC.name] = MC - else - to_chat(user, "\The [src] appears to be missing \the [slot].") - - var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) - if(!remove) - return - - var/obj/item/mecha_parts/component/RmC = removable_components[remove] - RmC.detach() - - return - else if(istype(W, /obj/item/stack/cable_coil)) - if(state >= MECHA_CELL_OPEN && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.use(2)) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, "You replace the fused wires.") - else - to_chat(user, "There's not enough wire to finish the task.") - return - else if(W.is_screwdriver()) - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - clearInternalDamage(MECHA_INT_TEMP_CONTROL) - to_chat(user, "You repair the damaged temperature controller.") - else if(state==MECHA_CELL_OPEN && src.cell) - src.cell.forceMove(src.loc) - src.cell = null - state = MECHA_CELL_OUT - to_chat(user, "You unscrew and pry out the powercell.") - src.log_message("Powercell removed") - else if(state==MECHA_CELL_OUT && src.cell) - state=MECHA_CELL_OPEN - to_chat(user, "You screw the cell in place") - return - - else if(istype(W, /obj/item/device/multitool)) - if(state>=MECHA_CELL_OPEN && src.occupant) - to_chat(user, "You attempt to eject the pilot using the maintenance controls.") - if(src.occupant.stat) - src.go_out() - src.log_message("[src.occupant] was ejected using the maintenance controls.") - else - to_chat(user, "Your attempt is rejected.") - src.occupant_message("An attempt to eject you was made using the maintenance controls.") - src.log_message("Eject attempt made using maintenance controls - rejected.") - return - - else if(istype(W, /obj/item/weapon/cell)) - if(state==MECHA_CELL_OUT) - if(!src.cell) - to_chat(user, "You install the powercell") - user.drop_item() - W.forceMove(src) - src.cell = W - src.log_message("Powercell installed") - else - to_chat(user, "There's already a powercell installed.") - return - - else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != I_HURT) - var/obj/item/weapon/weldingtool/WT = W - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - if (WT.remove_fuel(0,user)) - if (hasInternalDamage(MECHA_INT_TANK_BREACH)) - clearInternalDamage(MECHA_INT_TANK_BREACH) - to_chat(user, "You repair the damaged gas tank.") - else - return - if((src.healthYou repair some damage to [src.name].") - src.health += min(10, initial(src.health)-src.health) - update_damage_alerts() - else if(HC.integrityYou repair some damage to [HC.name].") - HC.integrity += min(10, HC.max_integrity-HC.integrity) - update_damage_alerts() - else if(AC.integrityYou repair some damage to [AC.name].") - AC.integrity += min(10, AC.max_integrity-AC.integrity) - update_damage_alerts() - - else - to_chat(user, "The [src.name] is at full integrity") - return - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - user.drop_from_inventory(W) - W.forceMove(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]") - return - - else if(istype(W,/obj/item/stack/nanopaste)) - if(state >= MECHA_PANEL_LOOSE) - var/obj/item/stack/nanopaste/NP = W - - for(var/slot in internal_components) - var/obj/item/mecha_parts/component/C = internal_components[slot] - - if(!C) - to_chat(user, "There are no components installed!") - return - - if(C.integrity >= C.max_integrity) - to_chat(user, "\The [C] does not require repairs.") - - else if(C.integrity < C.max_integrity) - to_chat(user, "You start to repair damage to \the [C].") - while(C.integrity < C.max_integrity && NP) - if(do_after(user, 1 SECOND, src)) - NP.use(1) - C.adjust_integrity(NP.mech_repair) - - if(C.integrity >= C.max_integrity) - to_chat(user, "You finish repairing \the [C].") - break - - else if(NP.amount == 0) - to_chat(user, "Insufficient nanopaste to complete repairs!") - break - return - - else - to_chat(user, "You can't reach \the [src]'s internal components.") - return - - else - call((proc_res["dynattackby"]||src), "dynattackby")(W,user) -/* - src.log_message("Attacked by [W]. Attacker - [user]") - if(prob(src.deflect_chance)) - to_chat(user, "\The [W] bounces off [src.name] armor.") - src.log_append_to_last("Armor saved.") -/* - for (var/mob/V in viewers(src)) - if(V.client && !(V.blinded)) - V.show_message("The [W] bounces off [src.name] armor.", 1) -*/ - else - src.occupant_message("[user] hits [src] with [W].") - user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") - src.take_damage(W.force,W.damtype) - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) -*/ - return - - - -/* -/obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) - if(!istype(user, /mob/living/silicon/ai)) - return - var/output = {"Assume direct control over [src]? - Yes
                    - "} - user << browse(output, "window=mecha_attack_ai") - return -*/ - -/////////////////////////////// -//////// Brain Stuff //////// -/////////////////////////////// - -/obj/mecha/proc/mmi_move_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Brain activity below acceptable level.") - return 0 - else if(occupant) - to_chat(user, "Occupant detected.") - return 0 - else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.") - return 0 - //Added a message here since people assume their first click failed or something./N -// to_chat(user, "Installing MMI, please stand by.") - - visible_message("[usr] starts to insert a brain into [src.name]") - - if(enter_after(40,user)) - if(!occupant) - return mmi_moved_inside(mmi_as_oc,user) - else - to_chat(user, "Occupant detected.") - else - to_chat(user, "You stop attempting to install the brain.") - return 0 - -/obj/mecha/proc/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) - if(mmi_as_oc && (user in range(1))) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level.") - return 0 - user.drop_from_inventory(mmi_as_oc) - var/mob/brainmob = mmi_as_oc.brainmob - brainmob.reset_view(src) - /* - brainmob.client.eye = src - brainmob.client.perspective = EYE_PERSPECTIVE - */ - occupant = brainmob - brainmob.loc = src //should allow relaymove - brainmob.canmove = 1 - mmi_as_oc.loc = src - mmi_as_oc.mecha = src - src.verbs += /obj/mecha/verb/eject - src.Entered(mmi_as_oc) - src.Move(src.loc) - update_icon() - set_dir(dir_in) - src.log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - src.occupant << sound('sound/mecha/nominal.ogg',volume=50) - update_icon() - return 1 - else - return 0 - - -///////////////////////////////////// -//////// Atmospheric stuff //////// -///////////////////////////////////// - -/obj/mecha/proc/get_turf_air() - var/turf/T = get_turf(src) - if(T) - . = T.return_air() - return - -/obj/mecha/remove_air(amount) - if(use_internal_tank) - return cabin_air.remove(amount) - else - var/turf/T = get_turf(src) - if(T) - return T.remove_air(amount) - return - -/obj/mecha/return_air() - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) - return cabin_air - return get_turf_air() - -/obj/mecha/proc/return_pressure() - . = 0 - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) - . = cabin_air.return_pressure() - else - var/datum/gas_mixture/t_air = get_turf_air() - if(t_air) - . = t_air.return_pressure() - return - -//skytodo: //No idea what you want me to do here, mate. -/obj/mecha/proc/return_temperature() - . = 0 - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) - . = cabin_air.temperature - else - var/datum/gas_mixture/t_air = get_turf_air() - if(t_air) - . = t_air.temperature - return - -/obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(!(new_port.loc in locs)) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - - //Actually enforce the air sharing - var/datum/pipe_network/network = connected_port.return_network(src) - if(network && !(internal_tank.return_air() in network.gases)) - network.gases += internal_tank.return_air() - network.update = 1 - playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1) - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - var/datum/pipe_network/network = connected_port.return_network(src) - if(network) - network.gases -= internal_tank.return_air() - - connected_port.connected_device = null - connected_port = null - playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1) - src.log_message("Disconnected from gas port.") - return 1 - - -///////////////////////// -//////// Verbs //////// -///////////////////////// - - -/obj/mecha/verb/connect_to_port() - set name = "Connect to port" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!occupant) - return - - if(usr != occupant) - return - - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(!GC) - return - - for(var/turf/T in locs) - var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T - if(possible_port) - if(connect(possible_port)) - occupant_message("\The [name] connects to the port.") - verbs += /obj/mecha/verb/disconnect_from_port - verbs -= /obj/mecha/verb/connect_to_port - return - else - occupant_message("\The [name] failed to connect to the port.") - return - else - occupant_message("Nothing happens") - - -/obj/mecha/verb/disconnect_from_port() - set name = "Disconnect from port" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - - if(!occupant) - return - - if(usr != occupant) - return - - if(disconnect()) - occupant_message("[name] disconnects from the port.") - verbs -= /obj/mecha/verb/disconnect_from_port - verbs += /obj/mecha/verb/connect_to_port - else - occupant_message("[name] is not connected to the port at the moment.") - -/obj/mecha/verb/toggle_lights() - set name = "Toggle Lights" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - lights() - -/obj/mecha/verb/lights() - if(usr!=occupant) return - lights = !lights - if(lights) set_light(light_range + lights_power) - else set_light(light_range - lights_power) - src.occupant_message("Toggled lights [lights?"on":"off"].") - log_message("Toggled lights [lights?"on":"off"].") - playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1) - return - - -/obj/mecha/verb/toggle_internal_tank() - set name = "Toggle internal airtank usage" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - internal_tank() - -/obj/mecha/proc/internal_tank() - if(usr!=src.occupant) - return - - var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] - if(!GC) - to_chat(occupant, "The life support systems don't seem to respond.") - return - - if(!prob(GC.get_efficiency() * 100)) - to_chat(occupant, "\The [GC] shudders and barks, before returning to how it was before.") - return - - use_internal_tank = !use_internal_tank - src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") - src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") - playsound(src, 'sound/mecha/gasdisconnected.ogg', 30, 1) - return - - -/obj/mecha/verb/toggle_strafing() - set name = "Toggle strafing" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - strafing() - -/obj/mecha/proc/strafing() - if(usr!=src.occupant) - return - strafing = !strafing - src.occupant_message("Toggled strafing mode [strafing?"on":"off"].") - src.log_message("Toggled strafing mode [strafing?"on":"off"].") - return - -/obj/mecha/MouseDrop_T(mob/O, mob/user as mob) - //Humans can pilot mechs. - if(!ishuman(O)) - return - - //Can't put other people into mechs (can comment this out if you want that to be possible) - if(O != user) - return - - move_inside() - -/obj/mecha/verb/enter() - set category = "Object" - set name = "Enter Exosuit" - set src in oview(1) - move_inside() - -//returns an equipment object if we have one of that type, useful since is_type_in_list won't return the object -//since is_type_in_list uses caching, this is a slower operation, so only use it if needed -/obj/mecha/proc/get_equipment(var/equip_type) - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(istype(ME,equip_type)) - return ME - return null - -/obj/mecha/proc/move_inside() - if (usr.stat || !ishuman(usr)) - return - - if (usr.buckled) - to_chat(usr, "You can't climb into the exosuit while buckled!") - return - - src.log_message("[usr] tries to move in.") - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - if(C.handcuffed) - to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") - return - if (src.occupant) - to_chat(usr, "The [src.name] is already occupied!") - src.log_append_to_last("Permission denied.") - return -/* - if (usr.abiotic()) - to_chat(usr, "Subject cannot have abiotic items on.") - return -*/ - var/passed - if(src.dna) - if(usr.dna.unique_enzymes==src.dna) - passed = 1 - else if(src.operation_allowed(usr)) - passed = 1 - if(!passed) - to_chat(usr, "Access denied") - src.log_append_to_last("Permission denied.") - return - if(isliving(usr)) - var/mob/living/L = usr - if(L.has_buckled_mobs()) - to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) - return - -// to_chat(usr, "You start climbing into [src.name]") - if(get_equipment(/obj/item/mecha_parts/mecha_equipment/runningboard)) - visible_message("\The [usr] is instantly lifted into [src.name] by the running board!") - moved_inside(usr) - if(ishuman(occupant)) - GrantActions(occupant, 1) - else - visible_message("\The [usr] starts to climb into [src.name]") - if(enter_after(40,usr)) - if(!src.occupant) - moved_inside(usr) - if(ishuman(occupant)) //Aeiou - GrantActions(occupant, 1) - else if(src.occupant!=usr) - to_chat(usr, "[src.occupant] was faster. Try better next time, loser.") - else - to_chat(usr, "You stop entering the exosuit.") - return - -/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) - if(H && H.client && (H in range(1))) - H.reset_view(src) - /* - H.client.perspective = EYE_PERSPECTIVE - H.client.eye = src - */ - H.stop_pulling() - H.forceMove(src) - src.occupant = H - src.add_fingerprint(H) - src.forceMove(src.loc) - src.verbs += /obj/mecha/verb/eject - src.log_append_to_last("[H] moved in as pilot.") - update_icon() - //VOREStation Edit Add - if(occupant.hud_used) - minihud = new (occupant.hud_used, src) - //VOREStation Edit Add End - -//This part removes all the verbs if you don't have them the _possible on your mech. This is a little clunky, but it lets you just add that to any mech. -//And it's not like this 10yo code wasn't clunky before. - - if(!smoke_possible) //Can't use smoke? No verb for you. - verbs -= /obj/mecha/verb/toggle_smoke - if(!thrusters_possible) //Can't use thrusters? No verb for you. - verbs -= /obj/mecha/verb/toggle_thrusters - if(!defence_mode_possible) //Do i need to explain everything? - verbs -= /obj/mecha/verb/toggle_defence_mode - if(!overload_possible) - verbs -= /obj/mecha/verb/toggle_overload - if(!zoom_possible) - verbs -= /obj/mecha/verb/toggle_zoom - if(!phasing_possible) - verbs -= /obj/mecha/verb/toggle_phasing - if(!switch_dmg_type_possible) - verbs -= /obj/mecha/verb/switch_damtype - if(!cloak_possible) - verbs -= /obj/mecha/verb/toggle_cloak - - occupant.in_enclosed_vehicle = 1 //Useful for when you need to know if someone is in a mecho. - update_cell_alerts() - update_damage_alerts() - set_dir(dir_in) - playsound(src, 'sound/machines/door/windowdoor.ogg', 50, 1) - if(occupant.client && cloaked_selfimage) - occupant.client.images += cloaked_selfimage - play_entered_noise(occupant) - return 1 - else - return 0 - -/obj/mecha/proc/play_entered_noise(var/mob/who) - if(!hasInternalDamage()) //Otherwise it's not nominal! - switch(mech_faction) - if(MECH_FACTION_NT)//The good guys category - if(firstactivation)//First time = long activation sound - firstactivation = 1 - who << sound('sound/mecha/LongNanoActivation.ogg',volume=50) - else - who << sound('sound/mecha/nominalnano.ogg',volume=50) - if(MECH_FACTION_SYNDI)//Bad guys - if(firstactivation) - firstactivation = 1 - who << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) - else - who << sound('sound/mecha/nominalsyndi.ogg',volume=50) - else//Everyone else gets the normal noise - who << sound('sound/mecha/nominal.ogg',volume=50) - -/obj/mecha/AltClick(mob/living/user) - if(user == occupant) - strafing() - -/obj/mecha/verb/view_stats() - set name = "View Stats" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - //pr_update_stats.start() - src.occupant << browse(src.get_stats_html(), "window=exosuit") - return - -/* -/obj/mecha/verb/force_eject() - set category = "Object" - set name = "Force Eject" - set src in view(5) - src.go_out() - return -*/ - -/obj/mecha/verb/eject() - set name = "Eject" - set category = "Exosuit Interface" - set src = usr.loc - set popup_menu = 0 - if(usr!=src.occupant) - return - src.go_out() - add_fingerprint(usr) - return - - -/obj/mecha/proc/go_out() //Eject/Exit the mech. Yes this is for easier searching. - if(!src.occupant) return - var/atom/movable/mob_container - QDEL_NULL(minihud) - if(ishuman(occupant)) - mob_container = src.occupant - RemoveActions(occupant, human_occupant=1)//AEIOU - else if(istype(occupant, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/brain = occupant - mob_container = brain.container - else - return - if(mob_container.forceMove(src.loc))//ejecting mob container - log_message("[mob_container] moved out.") - occupant.reset_view() - occupant << browse(null, "window=exosuit") - if(occupant.client && cloaked_selfimage) - occupant.client.images -= cloaked_selfimage - if(istype(mob_container, /obj/item/device/mmi)) - var/obj/item/device/mmi/mmi = mob_container - if(mmi.brainmob) - occupant.loc = mmi - mmi.mecha = null - occupant.canmove = 0 - occupant.clear_alert("charge") - occupant.clear_alert("mech damage") - occupant.in_enclosed_vehicle = 0 - occupant = null - update_icon() - set_dir(dir_in) - verbs -= /obj/mecha/verb/eject - - //src.zoom = 0 - - // Doesn't seem needed. - if(src.occupant && src.occupant.client) - src.occupant.client.view = world.view - src.zoom = 0 - - strafing = 0 - return - -///////////////////////// -////// Access stuff ///// -///////////////////////// - -/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) - for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(src.check_access(ID,src.operation_req_access)) - return 1 - return 0 - - -/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) - if(istype(H)) - for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(src.check_access(ID,src.internals_req_access)) - return 1 - else if(istype(H, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = H - if(src.check_access(R.idcard,src.internals_req_access)) - return 1 - return 0 - - -/obj/mecha/check_access(obj/item/weapon/card/id/I, list/access_list) - if(!istype(access_list)) - return 1 - if(!access_list.len) //no requirements - return 1 - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/pda = I - I = pda.id - if(!istype(I) || !I.access) //not ID or no access - return 0 - if(access_list==src.operation_req_access) - for(var/req in access_list) - if(!(req in I.access)) //doesn't have this access - return 0 - else if(access_list==src.internals_req_access) - for(var/req in access_list) - if(req in I.access) - return 1 - return 1 - - -//////////////////////////////////// -///// Rendering stats window /////// -//////////////////////////////////// - -/obj/mecha/proc/get_stats_html() - var/output = {" - [src.name] data - - - - -
                    - [src.get_stats_part()] -
                    -
                    - [src.get_equipment_list()] -
                    -
                    -
                    - [src.get_commands()] -
                    - - - "} - return output - - -/obj/mecha/proc/report_internal_damage() - var/output = null - var/list/dam_reports = list( - "[MECHA_INT_FIRE]" = "INTERNAL FIRE", - "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", - "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", - "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", - "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" - ) - for(var/tflag in dam_reports) - var/intdamflag = text2num(tflag) - if(hasInternalDamage(intdamflag)) - output += dam_reports[tflag] - output += "
                    " - if(return_pressure() > WARNING_HIGH_PRESSURE) - output += "DANGEROUSLY HIGH CABIN PRESSURE
                    " - return output - - -/obj/mecha/proc/get_stats_part() - var/integrity = health/initial(health)*100 - var/cell_charge = get_charge() - var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" - var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" - var/cabin_pressure = round(return_pressure(),0.01) - - var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] - var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] - - var/output = {"[report_internal_damage()] - Armor Integrity: [AC?"[round(AC.integrity / AC.max_integrity * 100, 0.1)]%":"ARMOR MISSING"]
                    - Hull Integrity: [HC?"[round(HC.integrity / HC.max_integrity * 100, 0.1)]%":"HULL MISSING"]
                    - [integrity<30?"DAMAGE LEVEL CRITICAL
                    ":null] - Chassis Integrity: [integrity]%
                    - Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
                    - Air source: [use_internal_tank?"Internal Airtank":"Environment"]
                    - Airtank pressure: [tank_pressure]kPa
                    - Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
                    - Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
                    - Cabin temperature: [return_temperature()]K|[return_temperature() - T0C]°C
                    - Lights: [lights?"on":"off"]
                    - [src.dna?"DNA-locked:
                    [src.dna] \[Reset\]
                    ":null] - "} - - - if(defence_mode_possible) - output += "Defence mode: [defence_mode?"on":"off"]
                    " - if(overload_possible) - output += "Leg actuators overload: [overload?"on":"off"]
                    " - if(smoke_possible) - output += "Smoke: [smoke_reserve]
                    " - if(thrusters_possible) - output += "Thrusters: [thrusters?"on":"off"]
                    " - -//Cargo components. Keep this last otherwise it does weird alignment issues. - output += "Cargo Compartment Contents:
                    " - if(src.cargo.len) - for(var/obj/O in src.cargo) - output += "Unload : [O]
                    " - else - output += "Nothing" - output += "
                    " - return output - -/obj/mecha/proc/get_commands() - var/output = {"
                    -
                    Electronics
                    - -
                    -
                    -
                    Airtank
                    - -
                    - -
                    [get_equipment_menu()]
                    -
                    - [(/obj/mecha/verb/eject in src.verbs)?"Eject
                    ":null] - "} - return output - -/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu - var/output - if(equipment.len) - output += {"
                    -
                    Equipment
                    -
                    - "} - return output - -/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html - if(!equipment.len) - return - var/output = "Equipment:
                    " - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - output += "
                    [MT.get_equip_info()]
                    " - output += "
                    " - return output - - -/obj/mecha/proc/get_log_html() - var/output = "[src.name] Log" - for(var/list/entry in log) - output += {"
                    [time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [game_year]
                    -
                    [entry["message"]]
                    - "} - output += "" - return output - -/obj/mecha/proc/get_log_tgui() - var/list/data = list() - for(var/list/entry in log) - data.Add(list(list( - "time" = time2text(entry["time"], "DDD MMM DD hh:mm:ss"), - "year" = game_year, - "message" = entry["message"], - ))) - return data - - -/obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) - if(!id_card || !user) return - var/output = {" - - - -

                    Following keycodes are present in this system:

                    "} - for(var/a in operation_req_access) - output += "[get_access_desc(a)] - Delete
                    " - output += "

                    Following keycodes were detected on portable device:

                    " - for(var/a in id_card.access) - if(a in operation_req_access) continue - var/a_name = get_access_desc(a) - if(!a_name) continue //there's some strange access without a name - output += "[a_name] - Add
                    " - output += "
                    Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" - output += "" - user << browse(output, "window=exosuit_add_access") - onclose(user, "exosuit_add_access") - return - -/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user) - if(!id_card || !user) return - - var/maint_options = "Set Cabin Air Pressure" - if (locate(/obj/item/mecha_parts/mecha_equipment/tool/passenger) in contents) - maint_options += "Remove Passenger" - - var/output = {" - - - - - [add_req_access?"Edit operation keycodes":null] - [maint_access?"Initiate maintenance protocol":null] - [(state>0) ? maint_options : ""] - - "} - user << browse(output, "window=exosuit_maint_console") - onclose(user, "exosuit_maint_console") - return - - -//////////////////////////////// -/////// Messages and Log /////// -//////////////////////////////// - -/obj/mecha/proc/occupant_message(message as text) - if(message) - if(src.occupant && src.occupant.client) - to_chat(src.occupant, "\icon[src][bicon(src)] [message]") - return - -/obj/mecha/proc/log_message(message as text,red=null) - log.len++ - log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") - return log.len - -/obj/mecha/proc/log_append_to_last(message as text,red=null) - var/list/last_entry = src.log[src.log.len] - last_entry["message"] += "
                    [red?"":null][message][red?"":null]" - return - - -///////////////// -///// Topic ///// -///////////////// - -/obj/mecha/Topic(href, href_list) - ..() - if(href_list["update_content"]) - if(usr != src.occupant) return - send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part()) - return - if(href_list["close"]) - return - if(usr.stat > 0) - return - var/datum/topic_input/top_filter = new /datum/topic_input(href,href_list) - if(href_list["select_equip"]) - if(usr != src.occupant) return - var/obj/item/mecha_parts/mecha_equipment/equip = top_filter.getObj("select_equip") - if(equip) - src.selected = equip - src.occupant_message("You switch to [equip].") - src.visible_message("[src] raises [equip].") - send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) - return - if(href_list["eject"]) - if(usr != src.occupant) return - src.eject() - return - if(href_list["toggle_lights"]) - if(usr != src.occupant) return - src.lights() - return -/* - if(href_list["toggle_strafing"]) - if(usr != src.occupant) return - src.strafing() - return*/ - - if(href_list["toggle_airtank"]) - if(usr != src.occupant) return - src.internal_tank() - return - if (href_list["toggle_thrusters"]) - src.toggle_thrusters() - if (href_list["smoke"]) - src.smoke() - if (href_list["toggle_zoom"]) - src.zoom() - if(href_list["toggle_defence_mode"]) - src.defence_mode() - if(href_list["switch_damtype"]) - src.switch_damtype() - if(href_list["phasing"]) - src.phasing() - - if(href_list["rmictoggle"]) - if(usr != src.occupant) return - radio.broadcasting = !radio.broadcasting - send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) - return - if(href_list["rspktoggle"]) - if(usr != src.occupant) return - radio.listening = !radio.listening - send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) - return - if(href_list["rfreq"]) - if(usr != src.occupant) return - var/new_frequency = (radio.frequency + top_filter.getNum("rfreq")) - if ((radio.frequency < PUBLIC_LOW_FREQ || radio.frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - radio.set_frequency(new_frequency) - send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") - return - if(href_list["port_disconnect"]) - if(usr != src.occupant) return - src.disconnect_from_port() - return - if (href_list["port_connect"]) - if(usr != src.occupant) return - src.connect_to_port() - return - if (href_list["view_log"]) - if(usr != src.occupant) return - src.occupant << browse(src.get_log_html(), "window=exosuit_log") - onclose(occupant, "exosuit_log") - return - if (href_list["change_name"]) - if(usr != src.occupant) return - var/newname = sanitizeSafe(tgui_input_text(occupant,"Choose new exosuit name","Rename exosuit",initial(name), MAX_NAME_LEN), MAX_NAME_LEN) - if(newname) - name = newname - else - tgui_alert_async(occupant, "nope.avi") - return - if (href_list["toggle_id_upload"]) - if(usr != src.occupant) return - add_req_access = !add_req_access - send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel") - return - if(href_list["toggle_maint_access"]) - if(usr != src.occupant) return - if(state) - occupant_message("Maintenance protocols in effect") - return - maint_access = !maint_access - send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") - return - if(href_list["req_access"] && add_req_access) - if(!in_range(src, usr)) return - output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) - return - if(href_list["maint_access"] && maint_access) - if(!in_range(src, usr)) return - var/mob/user = top_filter.getMob("user") - if(user) - if(state==MECHA_OPERATING) - state = MECHA_BOLTS_SECURED - to_chat(user, "The securing bolts are now exposed.") - else if(state==MECHA_BOLTS_SECURED) - state = MECHA_OPERATING - to_chat(user, "The securing bolts are now hidden.") - output_maintenance_dialog(top_filter.getObj("id_card"),user) - return - if(href_list["set_internal_tank_valve"] && state >=MECHA_BOLTS_SECURED) - if(!in_range(src, usr)) return - var/mob/user = top_filter.getMob("user") - if(user) - var/new_pressure = tgui_input_number(user,"Input new output pressure","Pressure setting",internal_tank_valve) - if(new_pressure) - internal_tank_valve = new_pressure - to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.") - if(href_list["remove_passenger"] && state >= MECHA_BOLTS_SECURED) - var/mob/user = top_filter.getMob("user") - var/list/passengers = list() - for (var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P in contents) - if (P.occupant) - passengers["[P.occupant]"] = P - - if (!passengers) - to_chat(user, "There are no passengers to remove.") - return - - var/pname = tgui_input_list(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger", passengers) - - if (!pname) - return - - var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P = passengers[pname] - var/mob/occupant = P.occupant - - user.visible_message("\The [user] begins opening the hatch on \the [P]...", "You begin opening the hatch on \the [P]...") - if (!do_after(user, 40)) - return - - user.visible_message("\The [user] opens the hatch on \the [P] and removes [occupant]!", "You open the hatch on \the [P] and remove [occupant]!") - P.go_out() - P.log_message("[occupant] was removed.") - return - if(href_list["add_req_access"] && add_req_access && top_filter.getObj("id_card")) - if(!in_range(src, usr)) return - operation_req_access += top_filter.getNum("add_req_access") - output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) - return - if(href_list["del_req_access"] && add_req_access && top_filter.getObj("id_card")) - if(!in_range(src, usr)) return - operation_req_access -= top_filter.getNum("del_req_access") - output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) - return - if(href_list["finish_req_access"]) - if(!in_range(src, usr)) return - add_req_access = 0 - var/mob/user = top_filter.getMob("user") - user << browse(null,"window=exosuit_add_access") - return - if(href_list["dna_lock"]) - if(usr != src.occupant) return - if(istype(occupant, /mob/living/carbon/brain)) - occupant_message("You are a brain. No.") - return - if(src.occupant) - src.dna = src.occupant.dna.unique_enzymes - src.occupant_message("You feel a prick as the needle takes your DNA sample.") - return - if(href_list["reset_dna"]) - if(usr != src.occupant) return - src.dna = null - if(href_list["repair_int_control_lost"]) - if(usr != src.occupant) return - src.occupant_message("Recalibrating coordination system.") - src.log_message("Recalibration of coordination system started.") - var/T = src.loc - if(do_after(100)) - if(T == src.loc) - src.clearInternalDamage(MECHA_INT_CONTROL_LOST) - src.occupant_message("Recalibration successful.") - src.log_message("Recalibration of coordination system finished with 0 errors.") - else - src.occupant_message("Recalibration failed.") - src.log_message("Recalibration of coordination system failed with 1 error.",1) - if(href_list["drop_from_cargo"]) - var/obj/O = locate(href_list["drop_from_cargo"]) - if(O && (O in src.cargo)) - src.occupant_message("You unload [O].") - O.forceMove(get_turf(src)) - src.cargo -= O - var/turf/T = get_turf(O) - if(T) - T.Entered(O) - src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") - return - - //debug - /* - if(href_list["debug"]) - if(href_list["set_i_dam"]) - setInternalDamage(top_filter.getNum("set_i_dam")) - if(href_list["clear_i_dam"]) - clearInternalDamage(top_filter.getNum("clear_i_dam")) - return - */ - - - -/* - - if (href_list["ai_take_control"]) - var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) - var/duration = text2num(href_list["duration"]) - var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src) - var/cur_occupant = src.occupant - O.invisibility = 0 - O.canmove = 1 - O.name = AI.name - O.real_name = AI.real_name - O.anchored = TRUE - O.aiRestorePowerRoutine = 0 - O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! - O.laws = AI.laws - O.set_stat(AI.stat) - O.oxyloss = AI.getOxyLoss() - O.fireloss = AI.getFireLoss() - O.bruteloss = AI.getBruteLoss() - O.toxloss = AI.toxloss - O.updatehealth() - src.occupant = O - if(AI.mind) - AI.mind.transfer_to(O) - AI.name = "Inactive AI" - AI.real_name = "Inactive AI" - AI.icon_state = "ai-empty" - spawn(duration) - AI.name = O.name - AI.real_name = O.real_name - if(O.mind) - O.mind.transfer_to(AI) - AI.control_disabled = 0 - AI.laws = O.laws - AI.oxyloss = O.getOxyLoss() - AI.fireloss = O.getFireLoss() - AI.bruteloss = O.getBruteLoss() - AI.toxloss = O.toxloss - AI.updatehealth() - qdel(O) - if (!AI.stat) - AI.icon_state = "ai" - else - AI.icon_state = "ai-crash" - src.occupant = cur_occupant -*/ - -/////////////////////// -///// Power stuff ///// -/////////////////////// - -/obj/mecha/proc/has_charge(amount) - return (get_charge()>=amount) - -/obj/mecha/proc/get_charge() - return call((proc_res["dyngetcharge"]||src), "dyngetcharge")() - -/obj/mecha/proc/dyngetcharge()//returns null if no powercell, else returns cell.charge - if(!src.cell) return - return max(0, src.cell.charge) - -/obj/mecha/proc/use_power(amount) - return call((proc_res["dynusepower"]||src), "dynusepower")(amount) - -/obj/mecha/proc/dynusepower(amount) - update_cell_alerts() - var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] - - if(EC) - amount = amount * (2 - EC.get_efficiency()) * EC.charge_cost_mod - else - amount *= 5 - - if(get_charge()) - cell.use(amount) - return 1 - return 0 - -/obj/mecha/proc/give_power(amount) - update_cell_alerts() - var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] - - if(!EC) - amount /= 4 - else - amount *= EC.get_efficiency() - - if(!isnull(get_charge())) - cell.give(amount) - return 1 - return 0 - -//This is for mobs mostly. -/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message) - - var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] - - var/temp_deflect_chance = deflect_chance - var/temp_damage_minimum = damage_minimum - - if(!ArmC) - temp_deflect_chance = 1 - temp_damage_minimum = 0 - - else - temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) - temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) - - user.setClickCooldown(user.get_attack_speed()) - if(!damage) - return 0 - - src.log_message("Attacked. Attacker - [user].",1) - user.do_attack_animation(src) - - if(prob(temp_deflect_chance))//Deflected - src.log_append_to_last("Armor saved.") - src.occupant_message("\The [user]'s attack is stopped by the armor.") - visible_message("\The [user] rebounds off [src.name]'s armor!") - user.attack_log += text("\[[time_stamp()]\] attacked [src.name]") - playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) - - else if(damage < temp_damage_minimum)//Pathetic damage levels just don't harm MECH. - src.occupant_message("\The [user]'s doesn't dent \the [src] paint.") - src.visible_message("\The [user]'s attack doesn't dent \the [src] armor") - src.log_append_to_last("Armor saved.") - playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) - return - - else - src.take_damage(damage) //Apply damage - The take_damage() proc handles armor values - if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. - src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - visible_message("[user] [attack_message] [src]!") - user.attack_log += text("\[[time_stamp()]\] attacked [src.name]") - - return 1 - - -///////////////////////////////////////// -//////// Mecha process() helpers //////// -///////////////////////////////////////// -/obj/mecha/proc/stop_process(process) - current_processes &= ~process - -/obj/mecha/proc/start_process(process) - current_processes |= process - - -///////////// -/obj/mecha/cloak() - . = ..() - if(occupant && occupant.client && cloaked_selfimage) - occupant.client.images += cloaked_selfimage - -/obj/mecha/uncloak() - if(occupant && occupant.client && cloaked_selfimage) - occupant.client.images -= cloaked_selfimage - return ..() - - -//debug -/* -/obj/mecha/verb/test_int_damage() - set name = "Test internal damage" - set category = "Exosuit Interface" - set src in view(0) - if(!occupant) return - if(usr!=occupant) - return - var/output = {" - - - -

                    Set:

                    - MECHA_INT_FIRE
                    - MECHA_INT_TEMP_CONTROL
                    - MECHA_INT_SHORT_CIRCUIT
                    - MECHA_INT_TANK_BREACH
                    - MECHA_INT_CONTROL_LOST
                    -
                    -

                    Clear:

                    - MECHA_INT_FIRE
                    - MECHA_INT_TEMP_CONTROL
                    - MECHA_INT_SHORT_CIRCUIT
                    - MECHA_INT_TANK_BREACH
                    - MECHA_INT_CONTROL_LOST
                    - - "} - - occupant << browse(output, "window=ex_debug") - //src.health = initial(src.health)/2.2 - //src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - return -*/ - -/obj/mecha/proc/update_cell_alerts() - if(occupant && cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - occupant.clear_alert("charge") - if(0.5 to 0.75) - occupant.throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - occupant.throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - occupant.throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - occupant.throw_alert("charge", /obj/screen/alert/emptycell) - -/obj/mecha/proc/update_damage_alerts() - if(occupant) - var/integrity = health/initial(health)*100 - switch(integrity) - if(30 to 45) - occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 1) - if(15 to 35) - occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 2) - if(-INFINITY to 15) - occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 3) - else - occupant.clear_alert("mech damage") - -/obj/mecha/blob_act(var/obj/structure/blob/B) - var/datum/blob_type/blob = B?.overmind?.blob_type - if(!istype(blob)) - return FALSE - - var/damage = rand(blob.damage_lower, blob.damage_upper) - src.take_damage(damage, blob.damage_type) - visible_message("\The [B] [blob.attack_verb] \the [src]!", "[blob.attack_message_synth]!") - playsound(src, 'sound/effects/attackblob.ogg', 50, 1) - - return ..() +#define MECHA_INT_FIRE 1 +#define MECHA_INT_TEMP_CONTROL 2 +#define MECHA_INT_SHORT_CIRCUIT 4 +#define MECHA_INT_TANK_BREACH 8 +#define MECHA_INT_CONTROL_LOST 16 + +#define MECHA_PROC_MOVEMENT 1 +#define MECHA_PROC_DAMAGE 2 +#define MECHA_PROC_INT_TEMP 4 + +#define MELEE 1 +#define RANGED 2 + +#define MECHA_OPERATING 0 +#define MECHA_BOLTS_SECURED 1 +#define MECHA_PANEL_LOOSE 2 +#define MECHA_CELL_OPEN 3 +#define MECHA_CELL_OUT 4 + +#define MECH_FACTION_NT "nano" +#define MECH_FACTION_SYNDI "syndi" +#define MECH_FACTION_NONE "none" + +/obj/mecha + name = "Mecha" + desc = "Exosuit" + description_info = "Alt click to strafe." + icon = 'icons/mecha/mecha.dmi' + density = TRUE //Dense. To raise the heat. + opacity = 1 //Opaque. Menacing. + anchored = TRUE //No pulling around. + unacidable = TRUE //And no deleting hoomans inside + layer = MOB_LAYER //Icon draw layer + infra_luminosity = 15 //Byond implementation is bugged. + var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) + var/can_move = 1 + var/mob/living/carbon/occupant = null + + var/step_in = 10 //Make a step in step_in/10 sec. + var/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in. + + var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South. + var/step_energy_drain = 10 + var/health = 300 //Health is health + var/maxhealth = 300 //Maxhealth is maxhealth. + var/deflect_chance = 10 //Chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + + var/damage_minimum = 10 //Incoming damage lower than this won't actually deal damage. Scrapes shouldn't be a real thing. + var/minimum_penetration = 15 //Incoming damage won't be fully applied if you don't have at least 20. Almost all AP clears this. + var/fail_penetration_value = 0.66 //By how much failing to penetrate reduces your shit. 66% by default. 100dmg = 66dmg if failed pen + + var/obj/item/weapon/cell/cell + var/state = MECHA_OPERATING + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + var/dna //Dna-locking the mech + var/list/proc_res = list() //Stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect/effect/system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + var/force = 0 + + var/mech_faction = null + var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once. + + var/stomp_sound = 'sound/mecha/mechstep.ogg' + var/swivel_sound = 'sound/mecha/mechturn.ogg' + + //inner atmos + var/use_internal_tank = 0 + var/internal_tank_valve = ONE_ATMOSPHERE + var/obj/machinery/portable_atmospherics/canister/internal_tank + var/datum/gas_mixture/cabin_air + var/obj/machinery/atmospherics/portables_connector/connected_port = null + + var/obj/item/device/radio/radio = null + + var/max_temperature = 25000 //Kelvin values. + var/internal_damage_threshold = 33 //Health percentage below which internal damage is possible + var/internal_damage_minimum = 15 //At least this much damage to trigger some real bad hurt. + var/internal_damage = 0 //Contains bitflags + + var/list/operation_req_access = list() //Required access level for mecha operation + var/list/internals_req_access = list(access_engine,access_robotics) //Required access level to open cell compartment + + var/wreckage + + var/list/equipment = new //This lists holds what stuff you bolted onto your baby ride + var/obj/item/mecha_parts/mecha_equipment/selected + var/max_equip = 2 + + // What direction to float in, if inertial movement is active. + var/float_direction = 0 + // Process() iterator count. + var/process_ticks = 0 + // These control what toggleable processes are executed within process(). + var/current_processes = MECHA_PROC_INT_TEMP + +//mechaequipt2 stuffs + var/list/hull_equipment = new + var/list/weapon_equipment = new + var/list/utility_equipment = new + var/list/universal_equipment = new + var/list/special_equipment = new + var/max_hull_equip = 2 + var/max_weapon_equip = 2 + var/max_utility_equip = 2 + var/max_universal_equip = 2 + var/max_special_equip = 1 + + var/list/starting_equipment = null // List containing starting tools. + +// Mech Components, similar to Cyborg, but Bigger. + var/list/internal_components = list( + MECH_HULL = null, + MECH_ACTUATOR = null, + MECH_ARMOR = null, + MECH_GAS = null, + MECH_ELECTRIC = null + ) + var/list/starting_components = list( + /obj/item/mecha_parts/component/hull, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + +//Working exosuit vars + var/list/cargo = list() + var/cargo_capacity = 3 + + var/static/image/radial_image_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject") + var/static/image/radial_image_airtoggle = image(icon= 'icons/mob/radial.dmi', icon_state = "radial_airtank") + var/static/image/radial_image_lighttoggle = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_light") + var/static/image/radial_image_statpanel = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine2") + +//Mech actions + var/datum/mini_hud/mech/minihud //VOREStation Edit + var/strafing = 0 //Are we strafing or not? + + var/defence_mode_possible = 0 //Can we even use defence mode? This is used to assign it to mechs and check for verbs. + var/defence_mode = 0 //Are we in defence mode + var/defence_deflect = 35 //How much it deflect + + var/overload_possible = 0 //Same as above. Don't forget to GRANT the verb&actions if you want everything to work proper. + var/overload = 0 //Are our legs overloaded + var/overload_coeff = 1 //How much extra energy you use when use the L E G + + var/zoom = 0 + var/zoom_possible = 0 + + var/thrusters = 0 + var/thrusters_possible = 0 + + var/phasing = 0 //Are we currently phasing + var/phasing_possible = 0 //This is to allow phasing. + var/can_phase = TRUE //This is an internal check during the relevant procs. + var/phasing_energy_drain = 200 + + var/switch_dmg_type_possible = 0 //Can you switch damage type? It is mostly for the Phazon and its children. + + var/smoke_possible = 0 + var/smoke_reserve = 5 //How many shots you have. Might make a reload later on. MIGHT. + var/smoke_ready = 1 //This is a check for the whether or not the cooldown is ongoing. + var/smoke_cooldown = 100 //How long you have between uses. + var/datum/effect/effect/system/smoke_spread/smoke_system = new + + var/cloak_possible = FALSE // Can this exosuit innately cloak? + +////All of those are for the HUD buttons in the top left. See Grant and Remove procs in mecha_actions. + + var/datum/action/innate/mecha/mech_eject/eject_action = new + var/datum/action/innate/mecha/mech_toggle_internals/internals_action = new + var/datum/action/innate/mecha/mech_toggle_lights/lights_action = new + var/datum/action/innate/mecha/mech_view_stats/stats_action = new + var/datum/action/innate/mecha/strafe/strafing_action = new + + var/datum/action/innate/mecha/mech_defence_mode/defence_action = new + var/datum/action/innate/mecha/mech_overload_mode/overload_action = new + var/datum/action/innate/mecha/mech_smoke/smoke_action = new + var/datum/action/innate/mecha/mech_zoom/zoom_action = new + var/datum/action/innate/mecha/mech_toggle_thrusters/thrusters_action = new + var/datum/action/innate/mecha/mech_cycle_equip/cycle_action = new + var/datum/action/innate/mecha/mech_switch_damtype/switch_damtype_action = new + var/datum/action/innate/mecha/mech_toggle_phasing/phasing_action = new + var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action = new + + var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times. + +/obj/mecha/Initialize() + . = ..() + + for(var/path in starting_components) + var/obj/item/mecha_parts/component/C = new path(src) + C.attach(src) + + if(starting_equipment && LAZYLEN(starting_equipment)) + for(var/path in starting_equipment) + var/obj/item/mecha_parts/mecha_equipment/ME = new path(src) + ME.attach(src) + + START_PROCESSING(SSobj, src) + + update_transform() + +/obj/mecha/drain_power(var/drain_check) + + if(drain_check) + return 1 + + if(!cell) + return 0 + + return cell.drain_power(drain_check) + +/obj/mecha/New() + ..() + icon_state += "-open" + add_radio() + add_cabin() + if(!add_airtank()) //we check this here in case mecha does not have an internal tank available by default - WIP + removeVerb(/obj/mecha/verb/connect_to_port) + removeVerb(/obj/mecha/verb/toggle_internal_tank) + + spark_system.set_up(2, 0, src) + spark_system.attach(src) + + if(smoke_possible)//I am pretty sure that's needed here. + src.smoke_system.set_up(3, 0, src) + src.smoke_system.attach(src) + + add_cell() + removeVerb(/obj/mecha/verb/disconnect_from_port) + log_message("[src.name] created.") + loc.Entered(src) + mechas_list += src //global mech list + return + +/obj/mecha/Exit(atom/movable/O) + if(O in cargo) + return 0 + return ..() + +/obj/mecha/Destroy() + src.go_out() + for(var/mob/M in src) //Be Extra Sure + M.forceMove(get_turf(src)) + M.loc.Entered(M) + if(M != src.occupant) + step_rand(M) + for(var/atom/movable/A in src.cargo) + A.forceMove(get_turf(src)) + var/turf/T = get_turf(A) + if(T) + T.Entered(A) + step_rand(A) + + if(loc) + loc.Exited(src) + + if(prob(30)) + explosion(get_turf(loc), 0, 0, 1, 3) + + if(wreckage) + var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc) + hull_equipment.Cut() + weapon_equipment.Cut() + utility_equipment.Cut() + universal_equipment.Cut() + special_equipment.Cut() + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.forceMove(WR) + E.equip_ready = TRUE + else + E.forceMove(loc) + E.destroy() + + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + if(istype(C)) + C.damage_part(rand(10, 20)) + C.detach() + WR.crowbar_salvage += C + C.forceMove(WR) + + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + else + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.detach(loc) + E.destroy() + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + if(istype(C)) + C.detach() + qdel(C) + if(cell) + qdel(cell) + if(internal_tank) + qdel(internal_tank) + equipment.Cut() + cell = null + internal_tank = null + + if(smoke_possible) //Just making sure nothing is running. + qdel(smoke_system) + + GLOB.mech_destroyed_roundstat++ + + QDEL_NULL(spark_system) + QDEL_NULL(minihud) + + STOP_PROCESSING(SSobj, src) + + mechas_list -= src //global mech list + . = ..() + +// The main process loop to replace the ancient global iterators. +// It's a bit hardcoded but I don't see anyone else adding stuff to +// mechas, and it's easy enough to modify. +/obj/mecha/process() + var/static/max_ticks = 16 + + if (current_processes & MECHA_PROC_MOVEMENT) + process_inertial_movement() + + if ((current_processes & MECHA_PROC_DAMAGE) && !(process_ticks % 2)) + process_internal_damage() + + if ((current_processes & MECHA_PROC_INT_TEMP) && !(process_ticks % 4)) + process_preserve_temp() + + if (!(process_ticks % 3)) + process_tank_give_air() + + // Max value is 16. So we let it run between [0, 16] with this. + process_ticks = (process_ticks + 1) % 17 + +// Normalizing cabin air temperature to 20 degrees celsius. +// Called every fourth process() tick (20 deciseconds). +/obj/mecha/proc/process_preserve_temp() + if (cabin_air && cabin_air.volume > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1))) + +// Handles internal air tank action. +// Called every third process() tick (15 deciseconds). +/obj/mecha/proc/process_tank_give_air() + if(internal_tank) + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.temperature > 0) + transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = get_turf_air() + pressure_delta = cabin_pressure - release_pressure + + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.volume/(cabin_air.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + qdel(removed) + +// Inertial movement in space. +// Called every process() tick (5 deciseconds). +/obj/mecha/proc/process_inertial_movement() + if(float_direction) + if(!step(src, float_direction) || check_for_support()) + stop_process(MECHA_PROC_MOVEMENT) + else + stop_process(MECHA_PROC_MOVEMENT) + return + +// Processes internal damage. +// Called every other process() tick (10 deciseconds). +/obj/mecha/proc/process_internal_damage() + if(!hasInternalDamage()) + stop_process(MECHA_PROC_DAMAGE) + return + + if(hasInternalDamage(MECHA_INT_FIRE)) + if(!hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5)) + clearInternalDamage(MECHA_INT_FIRE) + if(internal_tank) + if(internal_tank.return_pressure()>internal_tank.maximum_pressure && !(hasInternalDamage(MECHA_INT_TANK_BREACH))) + setInternalDamage(MECHA_INT_TANK_BREACH) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air && int_tank_air.volume>0) //heat the air_contents + int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15)) + if(cabin_air && cabin_air.volume>0) + cabin_air.temperature = min(6000+T0C, cabin_air.temperature+rand(10,15)) + if(cabin_air.temperature>max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.temperature,0.1),"fire") + + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + stop_process(MECHA_PROC_INT_TEMP) + + if(hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(istype(loc, /turf/simulated)) + loc.assume_air(leaked_gas) + else + qdel(leaked_gas) + + if(hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + if(get_charge()) + spark_system.start() + cell.charge -= min(20,cell.charge) + cell.maxcharge -= min(20,cell.maxcharge) + return + +//////////////////////// +////// Helpers ///////// +//////////////////////// + +/obj/mecha/proc/removeVerb(verb_path) + verbs -= verb_path + +/obj/mecha/proc/addVerb(verb_path) + verbs += verb_path + +/obj/mecha/proc/add_airtank() + internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) + return internal_tank + +/obj/mecha/proc/add_cell(var/obj/item/weapon/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/weapon/cell/mech(src) + +/obj/mecha/get_cell() + return cell + +/obj/mecha/proc/add_cabin() + cabin_air = new + cabin_air.temperature = T20C + cabin_air.volume = 200 + cabin_air.adjust_multi("oxygen", O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature), "nitrogen", N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)) + return cabin_air + +/obj/mecha/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/mecha/proc/do_after(delay as num) + sleep(delay) + if(src) + return 1 + return 0 + +/obj/mecha/proc/enter_after(delay as num, var/mob/user as mob, var/numticks = 5) + var/delayfraction = delay/numticks + + var/turf/T = user.loc + + for(var/i = 0, iInterfacing with [target].") + src.log_message("Interfaced with [target].") + target.attack_hand(src.occupant) + return 1 + if(istype(target, /obj/machinery/embedded_controller)) + target.tgui_interact(src.occupant) + return 1 + return 0 + +/obj/mecha/contents_tgui_distance(var/src_object, var/mob/living/user) + . = user.shared_living_tgui_distance(src_object) //allow them to interact with anything they can interact with normally. + if(. != STATUS_INTERACTIVE) + //Allow interaction with the mecha or anything that is part of the mecha + if(src_object == src || (src_object in src)) + return STATUS_INTERACTIVE + if(src.Adjacent(src_object)) + src.occupant_message("Interfacing with [src_object]...") + src.log_message("Interfaced with [src_object].") + return STATUS_INTERACTIVE + if(src_object in view(2, src)) + return STATUS_UPDATE //if they're close enough, allow the occupant to see the screen through the viewport or whatever. + +/obj/mecha/proc/melee_action(atom/target) + return + +/obj/mecha/proc/range_action(atom/target) + return + + +////////////////////////////////// +//////// Movement procs //////// +////////////////////////////////// + +/obj/mecha/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + MoveAction() + +/obj/mecha/proc/MoveAction() //Allows mech equipment to do an action once the mech moves + if(!equipment.len) + return + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + ME.MoveAction() + +/obj/mecha/relaymove(mob/user,direction) + if(user != src.occupant) //While not "realistic", this piece is player friendly. + if(istype(user,/mob/living/carbon/brain)) + to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") + return 0 + user.forceMove(get_turf(src)) + to_chat(user, "You climb out from [src]") + return 0 + + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + if(!HC) + occupant_message("You can't operate an exosuit that doesn't have a hull!") + return + + if(connected_port) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while connected to the air system port") + last_message = world.time + return 0 + if(state) + occupant_message("Maintenance protocols in effect") + return +/* + if(zoom) + if(world.time - last_message > 20) + src.occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 +*/ + return domove(direction) + +/obj/mecha/proc/can_ztravel() + for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) + return jp.equip_ready + return FALSE + +/obj/mecha/proc/domove(direction) + + return call((proc_res["dyndomove"]||src), "dyndomove")(direction) + +/obj/mecha/proc/get_step_delay() + var/tally = 0 + + if(LAZYLEN(equipment)) + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(ME.get_step_delay()) + tally += ME.get_step_delay() + + if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight. + tally = 0 + else // Otherwise, start the tally after cutting that gap out. + tally -= encumbrance_gap + + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + if(C && C.get_step_delay()) + tally += C.get_step_delay() + + var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR] + + if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast. + tally = 2 SECONDS + + return tally + + tally += 0.5 SECONDS * (1 - actuator.get_efficiency()) // Damaged actuators run slower, slowing as damage increases beyond its threshold. + + if(strafing) + tally = round(tally * actuator.strafing_multiplier) + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(istype(ME, /obj/item/mecha_parts/mecha_equipment/speedboost)) + var/obj/item/mecha_parts/mecha_equipment/speedboost/SB = ME + for(var/path in ME.required_type) + if(istype(src, path)) + tally = round(tally * SB.slowdown_multiplier) + break + break + + if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0. + tally = min(1, round(tally/2)) + + return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that. + +/obj/mecha/proc/dyndomove(direction) + if(!can_move) + return 0 + if(current_processes & MECHA_PROC_MOVEMENT) + return 0 + if(!has_charge(step_energy_drain)) + return 0 + + //Can we even move, below is if yes. + + if(defence_mode)//Check if we are currently locked down + if(world.time - last_message > 20) + src.occupant_message(span_red("Unable to move while in defence mode")) + last_message = world.time + return 0 + + if(zoom)//:eyes: + if(world.time - last_message > 20) + src.occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + + if(!thrusters && (current_processes & MECHA_PROC_MOVEMENT)) //I think this mean 'if you try to move in space without thruster, u no move' + return 0 + + if(overload)//Check if you have leg overload + health-- + if(health < initial(health) - initial(health)/3) + overload = 0 + step_energy_drain = initial(step_energy_drain) + src.occupant_message(span_red("Leg actuators damage threshold exceded. Disabling overload.")) + + + var/move_result = 0 + + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + move_result = mechsteprand() + //Up/down zmove + else if(direction == UP || direction == DOWN) + if(!can_ztravel()) + occupant_message("Your vehicle lacks the capacity to move in that direction!") + return FALSE + + //We're using locs because some mecha are 2x2 turfs. So thicc! + var/result = TRUE + + for(var/turf/T in locs) + if(!T.CanZPass(src,direction)) + occupant_message("You can't move that direction from here!") + result = FALSE + break + var/turf/dest = (direction == UP) ? GetAbove(src) : GetBelow(src) + if(!dest) + occupant_message("There is nothing of interest in this direction.") + result = FALSE + break + if(!dest.CanZPass(src,direction)) + occupant_message("There's something blocking your movement in that direction!") + result = FALSE + break + if(result) + move_result = mechstep(direction) + + //Turning + + else if(src.dir != direction) + + if(strafing) + move_result = mechstep(direction) + else + move_result = mechturn(direction) + + //Stepping + else + move_result = mechstep(direction) + + + if(move_result) + can_move = 0 + use_power(step_energy_drain) + if(istype(src.loc, /turf/space)) + if(!src.check_for_support()) + float_direction = direction + start_process(MECHA_PROC_MOVEMENT) + src.log_message("Movement control lost. Inertial movement started.") + if(do_after(get_step_delay())) + can_move = 1 + return 1 + return 0 + +/obj/mecha/proc/handle_equipment_movement() + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(ME.chassis == src) //Sanity + ME.handle_movement_action() + return + +/obj/mecha/proc/mechturn(direction) + set_dir(direction) + if(swivel_sound) + playsound(src,swivel_sound,40,1) + return 1 + +/obj/mecha/proc/mechstep(direction) + var/current_dir = dir //For strafing + var/result = get_step(src,direction) + if(result && Move(result)) + if(stomp_sound) + playsound(src,stomp_sound,40,1) + handle_equipment_movement() + if(strafing) //Also for strafing + set_dir(current_dir) + return result + + +/obj/mecha/proc/mechsteprand() + var/result = get_step_rand(src) + if(result && Move(result)) + if(stomp_sound) + playsound(src,stomp_sound,40,1) + handle_equipment_movement() + return result + +/obj/mecha/Bump(var/atom/obstacle) +// src.inertia_dir = null + if(istype(obstacle, /mob))//First we check if it is a mob. Mechs mostly shouln't go through them, even while phasing. + var/mob/M = obstacle + M.Move(get_step(obstacle,src.dir)) + else if(phasing && get_charge()>=phasing_energy_drain)//Phazon check. This could use an improvement elsewhere. + src.use_power(phasing_energy_drain) + phase() + . = ..(obstacle) + return + else if(istype(obstacle, /obj))//Then we check for regular obstacles. + var/obj/O = obstacle + if(istype(O, /obj/effect/portal)) //derpfix + src.anchored = 0 // Portals can only move unanchored objects. + O.Crossed(src) + spawn(0)//countering portal teleport spawn(0), hurr + src.anchored = 1 + if(O.anchored) + obstacle.Bumped(src) + else + step(obstacle,src.dir) + + else//No idea when this triggers, so i won't touch it. + . = ..(obstacle) + return + +/obj/mecha/proc/phase() // Force the mecha to move forward by phasing. + set waitfor = FALSE + if(can_phase) + can_phase = FALSE + flick("[initial_icon]-phase", src) + forceMove(get_step(src,src.dir)) + sleep(get_step_delay() * 3) + can_phase = TRUE + occupant_message("Phazed.") + return TRUE // In the event this is sequenced + return FALSE + +/////////////////////////////////// +//////// Internal damage //////// +/////////////////////////////////// + +//ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. +/obj/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) + if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) return + if(prob(30)) + if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) + for(var/T in possible_int_damage) + if(internal_damage & T) + possible_int_damage -= T + var/int_dam_flag = safepick(possible_int_damage) + if(int_dam_flag) + setInternalDamage(int_dam_flag) + return //It already hurts to get some, lets not get both. + + if(prob(10)) + if(ignore_threshold || src.health*100/initial(src.health) < src.internal_damage_threshold) + var/obj/item/mecha_parts/mecha_equipment/destr = safepick(equipment) + if(destr) + destr.destroy() + return + +/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) + return int_dam_flag ? internal_damage&int_dam_flag : internal_damage + + +/obj/mecha/proc/setInternalDamage(int_dam_flag) + internal_damage |= int_dam_flag + start_process(MECHA_PROC_DAMAGE) + log_append_to_last("Internal damage of type [int_dam_flag].",1) + occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding. + return + +/obj/mecha/proc/clearInternalDamage(int_dam_flag) + internal_damage &= ~int_dam_flag + switch(int_dam_flag) + if(MECHA_INT_TEMP_CONTROL) + occupant_message(span_blue("Life support system reactivated.")) + start_process(MECHA_PROC_INT_TEMP) + if(MECHA_INT_FIRE) + occupant_message(span_blue("Internal fire extinquished.")) + if(MECHA_INT_TANK_BREACH) + occupant_message(span_blue("Damaged internal tank has been sealed.")) + return + + +//////////////////////////////////////// +//////// Health related procs //////// +//////////////////////////////////////// + +/obj/mecha/take_damage(amount, type="brute") + update_damage_alerts() + if(amount) + var/damage = absorbDamage(amount,type) + + damage = components_handle_damage(damage,type) + + health -= damage + + update_health() + log_append_to_last("Took [damage] points of damage. Damage type: \"[type]\".",1) + return + +/obj/mecha/proc/components_handle_damage(var/damage, var/type = BRUTE) + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + + if(AC) + var/armor_efficiency = AC.get_efficiency() + var/damage_change = armor_efficiency * (damage * 0.5) * AC.damage_absorption[type] + AC.damage_part(damage_change, type) + damage -= damage_change + + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + + if(HC) + if(HC.integrity) + var/hull_absorb = round(rand(5, 10) / 10, 0.1) * damage + HC.damage_part(hull_absorb, type) + damage -= hull_absorb + + for(var/obj/item/mecha_parts/component/C in (internal_components - list(MECH_HULL, MECH_ARMOR))) + if(prob(C.relative_size)) + var/damage_part_amt = round(damage / 4, 0.1) + C.damage_part(damage_part_amt) + damage -= damage_part_amt + + return damage + +/obj/mecha/proc/get_damage_absorption() + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + if(istype(AC) && AC.get_efficiency() > 0.25) + return AC.damage_absorption + +/obj/mecha/proc/absorbDamage(damage,damage_type) + return call((proc_res["dynabsorbdamage"]||src), "dynabsorbdamage")(damage,damage_type) + +/obj/mecha/proc/dynabsorbdamage(damage,damage_type) + return damage*(listgetindex(get_damage_absorption(),damage_type) || 1) + +/obj/mecha/airlock_crush(var/crush_damage) + ..() + take_damage(crush_damage) + if(prob(50)) //Try to avoid that. + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return 1 + +/obj/mecha/proc/update_health() + if(src.health > 0) + src.spark_system.start() + else + qdel(src) + return + +/obj/mecha/attack_hand(mob/user as mob) + if(user == occupant) + show_radial_occupant(user) + return + + user.setClickCooldown(user.get_attack_speed()) + src.log_message("Attack by hand/paw. Attacker - [user].",1) + + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + + if(!ArmC) + temp_deflect_chance = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(user)) + if(!prob(temp_deflect_chance)) + src.take_damage(15) //The take_damage() proc handles armor values + if(prob(25)) //Why would they get free internal damage. At least make it a bit RNG. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) + to_chat(user, "You slash at the armored suit!") + visible_message("\The [user] slashes at [src.name]'s armor!") + else + src.log_append_to_last("Armor saved.") + playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) + to_chat(user, "Your claws had no effect!") + src.occupant_message("\The [user]'s claws are stopped by the armor.") + visible_message("\The [user] rebounds off [src.name]'s armor!") + else + user.visible_message("\The [user] hits \the [src]. Nothing happens.","You hit \the [src] with no visible effect.") + src.log_append_to_last("Armor saved.") + return + else if ((HULK in user.mutations) && !prob(temp_deflect_chance)) + src.take_damage(15) //The take_damage() proc handles armor values + if(prob(25)) //Hulks punch hard but lets not give them consistent internal damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + user.visible_message(span_red("[user] hits [src.name], doing some damage."), span_red("You hit [src.name] with all your might. The metal creaks and bends.")) + else + user.visible_message(span_red("[user] hits [src.name]. Nothing happens."),span_red("You hit [src.name] with no visible effect.")) + src.log_append_to_last("Armor saved.") + return + +/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper + ..() + src.log_message("Hit by [A].",1) + call((proc_res["dynhitby"]||src), "dynhitby")(A) + return + +//I think this is relative to throws. +/obj/mecha/proc/dynhitby(atom/movable/A) + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + var/temp_minimum_penetration = minimum_penetration + var/temp_fail_penetration_value = fail_penetration_value + + if(!ArmC) + temp_deflect_chance = 0 + temp_damage_minimum = 0 + temp_minimum_penetration = 0 + temp_fail_penetration_value = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) + temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) + + if(istype(A, /obj/item/mecha_parts/mecha_tracking)) + A.forceMove(src) + src.visible_message("The [A] fastens firmly to [src].") + return + if(prob(temp_deflect_chance) || istype(A, /mob)) + src.occupant_message("\The [A] bounces off the armor.") + src.visible_message("\The [A] bounces off \the [src] armor") + src.log_append_to_last("Armor saved.") + if(istype(A, /mob/living)) + var/mob/living/M = A + M.take_organ_damage(10) + else if(istype(A, /obj)) + var/obj/O = A + if(O.throwforce) + + var/pass_damage = O.throwforce + var/pass_damage_reduc_mod + if(pass_damage <= temp_damage_minimum)//Too little to go through. + src.occupant_message("\The [A] bounces off the armor.") + src.visible_message("\The [A] bounces off \the [src] armor") + return + + else if(O.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage + src.occupant_message("\The [A] struggles to bypass \the [src] armor.") + src.visible_message("\The [A] struggles to bypass \the [src] armor") + pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default + else + src.occupant_message("\The [A] manages to pierce \the [src] armor.") +// src.visible_message("\The [A] manages to pierce \the [src] armor") + pass_damage_reduc_mod = 1 + + + + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_ranged_contact(A, pass_damage) + + pass_damage = (pass_damage*pass_damage_reduc_mod)//Applying damage reduction + src.take_damage(pass_damage) //The take_damage() proc handles armor values + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return + + +/obj/mecha/bullet_act(var/obj/item/projectile/Proj) //wrapper + if(istype(Proj, /obj/item/projectile/test)) + var/obj/item/projectile/test/Test = Proj + Test.hit |= occupant // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. + return + + src.log_message("Hit by projectile. Type: [Proj.name]([Proj.check_armour]).",1) + call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment + ..() + return + +/obj/mecha/proc/dynbulletdamage(var/obj/item/projectile/Proj) + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + var/temp_minimum_penetration = minimum_penetration + var/temp_fail_penetration_value = fail_penetration_value + + if(!ArmC) + temp_deflect_chance = 0 + temp_damage_minimum = 0 + temp_minimum_penetration = 0 + temp_fail_penetration_value = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) + temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) + + if(prob(temp_deflect_chance)) + src.occupant_message("The armor deflects incoming projectile.") + src.visible_message("The [src.name] armor deflects the projectile") + src.log_append_to_last("Armor saved.") + return + + if(Proj.damage_type == HALLOSS) + use_power(Proj.agony * 5) + + if(!(Proj.nodamage)) + var/ignore_threshold + if(istype(Proj, /obj/item/projectile/beam/pulse)) //ATM, this is literally only for the pulse rifles used mostly by deathsquads. + ignore_threshold = 1 + + var/pass_damage = Proj.damage + var/pass_damage_reduc_mod + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_projectile_contact(Proj, pass_damage) + + if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. + src.occupant_message("The armor deflects incoming projectile.") + src.visible_message("The [src.name] armor deflects\the [Proj]") + return + + else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage + src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") + src.visible_message("\The [Proj] struggles to pierce \the [src] armor") + pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default + + else //You go through completely because you use AP. Nice. + src.occupant_message("\The [Proj] manages to pierce \the [src] armor.") +// src.visible_message("\The [Proj] manages to pierce \the [src] armor") + pass_damage_reduc_mod = 1 + + pass_damage = (pass_damage_reduc_mod*pass_damage)//Apply damage reduction before usage. + src.take_damage(pass_damage, Proj.check_armour) //The take_damage() proc handles armor values + if(prob(25)) + spark_system.start() + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) + + //AP projectiles have a chance to cause additional damage + if(Proj.penetrating) + var/distance = get_dist(Proj.starting, get_turf(loc)) + var/hit_occupant = 1 //only allow the occupant to be hit once + for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) + if(src.occupant && hit_occupant && prob(20)) + Proj.attack_mob(src.occupant, distance) + hit_occupant = 0 + else + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) + + Proj.penetrating-- + + if(prob(15)) + break //give a chance to exit early + + Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? + return + +//This refer to whenever you are caught in an explosion. +/obj/mecha/ex_act(severity) + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + + if(!ArmC) + temp_deflect_chance = 0 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + + src.log_message("Affected by explosion of severity: [severity].",1) + if(prob(temp_deflect_chance)) + severity++ + src.log_append_to_last("Armor saved, changing severity to [severity].") + switch(severity) + if(1.0) + src.take_damage(initial(src.health), "bomb") + if(2.0) + if (prob(30)) + src.take_damage(initial(src.health), "bomb") + else + src.take_damage(initial(src.health)/2, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + if(3.0) + if (prob(5)) + qdel(src) + else + src.take_damage(initial(src.health)/5, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + return + +/*Will fix later -Sieve +/obj/mecha/attack_blob(mob/user as mob) + src.log_message("Attack by blob. Attacker - [user].",1) + if(!prob(src.deflect_chance)) + src.take_damage(6) + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) + to_chat(user, "You smash at the armored suit!") + for (var/mob/V in viewers(src)) + if(V.client && !(V.blinded)) + V.show_message("\The [user] smashes against [src.name]'s armor!", 1) + else + src.log_append_to_last("Armor saved.") + playsound(src, 'sound/effects/blobattack.ogg', 50, 1, -1) + to_chat(user, "Your attack had no effect!") + src.occupant_message("\The [user]'s attack is stopped by the armor.") + for (var/mob/V in viewers(src)) + if(V.client && !(V.blinded)) + V.show_message("\The [user] rebounds off the [src.name] armor!", 1) + return +*/ + +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/2)/severity) + take_damage(50 / severity,"energy") + src.log_message("EMP detected",1) + if(prob(80)) + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + return + +/obj/mecha/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature>src.max_temperature) + src.log_message("Exposed to dangerous temperature.",1) + src.take_damage(5,"fire") //The take_damage() proc handles armor values + src.check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + return + +/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(user.get_attack_speed(W)) + src.log_message("Attacked by [W]. Attacker - [user]") + var/pass_damage_reduc_mod //Modifer for failing to bring AP. + + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + var/temp_minimum_penetration = minimum_penetration + var/temp_fail_penetration_value = fail_penetration_value + + if(!ArmC) + temp_deflect_chance = 0 + temp_damage_minimum = 0 + temp_minimum_penetration = 0 + temp_fail_penetration_value = 1 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + temp_minimum_penetration = round(ArmC.get_efficiency() * ArmC.minimum_penetration) + temp_fail_penetration_value = round(ArmC.get_efficiency() * ArmC.fail_penetration_value) + + if(prob(temp_deflect_chance)) //Does your attack get deflected outright. + src.occupant_message("\The [W] bounces off [src.name].") + to_chat(user, "\The [W] bounces off [src.name].") + src.log_append_to_last("Armor saved.") + + else if(W.force < temp_damage_minimum) //Is your attack too PATHETIC to do anything. 3 damage to a person shouldn't do anything to a mech. + src.occupant_message("\The [W] bounces off the armor.") + src.visible_message("\The [W] bounces off \the [src] armor") + return + + else if(W.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage + src.occupant_message("\The [W] struggles to bypass \the [src] armor.") + src.visible_message("\The [W] struggles to bypass \the [src] armor") + pass_damage_reduc_mod = temp_fail_penetration_value //This will apply to reduce damage to 2/3 or 66% by default + + else + pass_damage_reduc_mod = 1 //Just making sure. + src.occupant_message(span_red("[user] hits [src] with [W].")) + user.visible_message(span_red("[user] hits [src] with [W]."), span_red("You hit [src] with [W].")) + + var/pass_damage = W.force + pass_damage = (pass_damage*pass_damage_reduc_mod) //Apply the reduction of damage from not having enough armor penetration. This is not regular armor values at play. + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_projectile_contact(W, user, pass_damage) + src.take_damage(pass_damage,W.damtype) //The take_damage() proc handles armor values + if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return + +////////////////////// +////// AttackBy ////// +////////////////////// + +/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob) + + if(istype(W, /obj/item/device/mmi)) + if(mmi_move_inside(W,user)) + to_chat(user, "[src]-MMI interface initialized successfuly") + else + to_chat(user, "[src]-MMI interface initialization failed.") + return + + if(istype(W, /obj/item/device/robotanalyzer)) + var/obj/item/device/robotanalyzer/RA = W + RA.do_scan(src, user) + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + user.drop_item() + E.attach(src) + user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]") + else + to_chat(user, "You were unable to attach [W] to [src]") + return + + if(istype(W, /obj/item/mecha_parts/component) && state == MECHA_CELL_OUT) + var/obj/item/mecha_parts/component/MC = W + spawn() + if(MC.attach(src)) + user.drop_item() + MC.forceMove(src) + user.visible_message("[user] installs \the [W] in \the [src]", "You install \the [W] in \the [src].") + return + + if(istype(W, /obj/item/weapon/card/robot)) + var/obj/item/weapon/card/robot/RoC = W + return attackby(RoC.dummy_card, user) + + if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + if(add_req_access || maint_access) + if(internals_access_allowed(usr)) + var/obj/item/weapon/card/id/id_card + if(istype(W, /obj/item/weapon/card/id)) + id_card = W + else + var/obj/item/device/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + to_chat(user, "Invalid ID: Access denied.") + else + to_chat(user, "Maintenance protocols disabled by operator.") + else if(W.has_tool_quality(TOOL_WRENCH)) + if(state==MECHA_BOLTS_SECURED) + state = MECHA_PANEL_LOOSE + to_chat(user, "You undo the securing bolts.") + else if(state==MECHA_PANEL_LOOSE) + state = MECHA_BOLTS_SECURED + to_chat(user, "You tighten the securing bolts.") + return + else if(W.has_tool_quality(TOOL_CROWBAR)) + if(state==MECHA_PANEL_LOOSE) + state = MECHA_CELL_OPEN + to_chat(user, "You open the hatch to the power unit") + else if(state==MECHA_CELL_OPEN) + state=MECHA_PANEL_LOOSE + to_chat(user, "You close the hatch to the power unit") + else if(state==MECHA_CELL_OUT) + var/list/removable_components = list() + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/MC = internal_components[slot] + if(istype(MC)) + removable_components[MC.name] = MC + else + to_chat(user, "\The [src] appears to be missing \the [slot].") + + var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) + if(!remove) + return + + var/obj/item/mecha_parts/component/RmC = removable_components[remove] + RmC.detach() + + return + else if(istype(W, /obj/item/stack/cable_coil)) + if(state >= MECHA_CELL_OPEN && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.use(2)) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + to_chat(user, "You replace the fused wires.") + else + to_chat(user, "There's not enough wire to finish the task.") + return + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + clearInternalDamage(MECHA_INT_TEMP_CONTROL) + to_chat(user, "You repair the damaged temperature controller.") + else if(state==MECHA_CELL_OPEN && src.cell) + src.cell.forceMove(src.loc) + src.cell = null + state = MECHA_CELL_OUT + to_chat(user, "You unscrew and pry out the powercell.") + src.log_message("Powercell removed") + else if(state==MECHA_CELL_OUT && src.cell) + state=MECHA_CELL_OPEN + to_chat(user, "You screw the cell in place") + return + + else if(istype(W, /obj/item/device/multitool)) + if(state>=MECHA_CELL_OPEN && src.occupant) + to_chat(user, "You attempt to eject the pilot using the maintenance controls.") + if(src.occupant.stat) + src.go_out() + src.log_message("[src.occupant] was ejected using the maintenance controls.") + else + to_chat(user, "Your attempt is rejected.") + src.occupant_message("An attempt to eject you was made using the maintenance controls.") + src.log_message("Eject attempt made using maintenance controls - rejected.") + return + + else if(istype(W, /obj/item/weapon/cell)) + if(state==MECHA_CELL_OUT) + if(!src.cell) + to_chat(user, "You install the powercell") + user.drop_item() + W.forceMove(src) + src.cell = W + src.log_message("Powercell installed") + else + to_chat(user, "There's already a powercell installed.") + return + + else if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + if (WT.remove_fuel(0,user)) + if (hasInternalDamage(MECHA_INT_TANK_BREACH)) + clearInternalDamage(MECHA_INT_TANK_BREACH) + to_chat(user, "You repair the damaged gas tank.") + else + return + if((src.healthYou repair some damage to [src.name].") + src.health += min(10, initial(src.health)-src.health) + update_damage_alerts() + else if(HC.integrityYou repair some damage to [HC.name].") + HC.integrity += min(10, HC.max_integrity-HC.integrity) + update_damage_alerts() + else if(AC.integrityYou repair some damage to [AC.name].") + AC.integrity += min(10, AC.max_integrity-AC.integrity) + update_damage_alerts() + + else + to_chat(user, "The [src.name] is at full integrity") + return + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + user.drop_from_inventory(W) + W.forceMove(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src]") + return + + else if(istype(W,/obj/item/stack/nanopaste)) + if(state >= MECHA_PANEL_LOOSE) + var/obj/item/stack/nanopaste/NP = W + + for(var/slot in internal_components) + var/obj/item/mecha_parts/component/C = internal_components[slot] + + if(!C) + to_chat(user, "There are no components installed!") + return + + if(C.integrity >= C.max_integrity) + to_chat(user, "\The [C] does not require repairs.") + + else if(C.integrity < C.max_integrity) + to_chat(user, "You start to repair damage to \the [C].") + while(C.integrity < C.max_integrity && NP) + if(do_after(user, 1 SECOND, src)) + NP.use(1) + C.adjust_integrity(NP.mech_repair) + + if(C.integrity >= C.max_integrity) + to_chat(user, "You finish repairing \the [C].") + break + + else if(NP.amount == 0) + to_chat(user, "Insufficient nanopaste to complete repairs!") + break + return + + else + to_chat(user, "You can't reach \the [src]'s internal components.") + return + + else + call((proc_res["dynattackby"]||src), "dynattackby")(W,user) +/* + src.log_message("Attacked by [W]. Attacker - [user]") + if(prob(src.deflect_chance)) + to_chat(user, "\The [W] bounces off [src.name] armor.") + src.log_append_to_last("Armor saved.") +/* + for (var/mob/V in viewers(src)) + if(V.client && !(V.blinded)) + V.show_message("The [W] bounces off [src.name] armor.", 1) +*/ + else + src.occupant_message("[user] hits [src] with [W].") + user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") + src.take_damage(W.force,W.damtype) + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) +*/ + return + + + +/* +/obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) + if(!istype(user, /mob/living/silicon/ai)) + return + var/output = {"Assume direct control over [src]? + Yes
                    + "} + user << browse(output, "window=mecha_attack_ai") + return +*/ + +/////////////////////////////// +//////// Brain Stuff //////// +/////////////////////////////// + +/obj/mecha/proc/mmi_move_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Brain activity below acceptable level.") + return 0 + else if(occupant) + to_chat(user, "Occupant detected.") + return 0 + else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes) + to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.") + return 0 + //Added a message here since people assume their first click failed or something./N +// to_chat(user, "Installing MMI, please stand by.") + + visible_message("[usr] starts to insert a brain into [src.name]") + + if(enter_after(40,user)) + if(!occupant) + return mmi_moved_inside(mmi_as_oc,user) + else + to_chat(user, "Occupant detected.") + else + to_chat(user, "You stop attempting to install the brain.") + return 0 + +/obj/mecha/proc/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) + if(mmi_as_oc && (user in range(1))) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level.") + return 0 + user.drop_from_inventory(mmi_as_oc) + var/mob/brainmob = mmi_as_oc.brainmob + brainmob.reset_view(src) + /* + brainmob.client.eye = src + brainmob.client.perspective = EYE_PERSPECTIVE + */ + occupant = brainmob + brainmob.loc = src //should allow relaymove + brainmob.canmove = 1 + mmi_as_oc.loc = src + mmi_as_oc.mecha = src + src.verbs += /obj/mecha/verb/eject + src.Entered(mmi_as_oc) + src.Move(src.loc) + update_icon() + set_dir(dir_in) + src.log_message("[mmi_as_oc] moved in as pilot.") + if(!hasInternalDamage()) + src.occupant << sound('sound/mecha/nominal.ogg',volume=50) + update_icon() + return 1 + else + return 0 + + +///////////////////////////////////// +//////// Atmospheric stuff //////// +///////////////////////////////////// + +/obj/mecha/proc/get_turf_air() + var/turf/T = get_turf(src) + if(T) + . = T.return_air() + return + +/obj/mecha/remove_air(amount) + if(use_internal_tank) + return cabin_air.remove(amount) + else + var/turf/T = get_turf(src) + if(T) + return T.remove_air(amount) + return + +/obj/mecha/return_air() + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) + return cabin_air + return get_turf_air() + +/obj/mecha/proc/return_pressure() + . = 0 + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) + . = cabin_air.return_pressure() + else + var/datum/gas_mixture/t_air = get_turf_air() + if(t_air) + . = t_air.return_pressure() + return + +//skytodo: //No idea what you want me to do here, mate. +/obj/mecha/proc/return_temperature() + . = 0 + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(use_internal_tank && (GC && prob(GC.get_efficiency() * 100))) + . = cabin_air.temperature + else + var/datum/gas_mixture/t_air = get_turf_air() + if(t_air) + . = t_air.temperature + return + +/obj/mecha/proc/connect(obj/machinery/atmospherics/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(!(new_port.loc in locs)) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + + //Actually enforce the air sharing + var/datum/pipe_network/network = connected_port.return_network(src) + if(network && !(internal_tank.return_air() in network.gases)) + network.gases += internal_tank.return_air() + network.update = 1 + playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1) + log_message("Connected to gas port.") + return 1 + +/obj/mecha/proc/disconnect() + if(!connected_port) + return 0 + + var/datum/pipe_network/network = connected_port.return_network(src) + if(network) + network.gases -= internal_tank.return_air() + + connected_port.connected_device = null + connected_port = null + playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1) + src.log_message("Disconnected from gas port.") + return 1 + + +///////////////////////// +//////// Verbs //////// +///////////////////////// + + +/obj/mecha/verb/connect_to_port() + set name = "Connect to port" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + + if(!occupant) + return + + if(usr != occupant) + return + + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(!GC) + return + + for(var/turf/T in locs) + var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T + if(possible_port) + if(connect(possible_port)) + occupant_message("\The [name] connects to the port.") + verbs += /obj/mecha/verb/disconnect_from_port + verbs -= /obj/mecha/verb/connect_to_port + return + else + occupant_message("\The [name] failed to connect to the port.") + return + else + occupant_message("Nothing happens") + + +/obj/mecha/verb/disconnect_from_port() + set name = "Disconnect from port" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + + if(!occupant) + return + + if(usr != occupant) + return + + if(disconnect()) + occupant_message("[name] disconnects from the port.") + verbs -= /obj/mecha/verb/disconnect_from_port + verbs += /obj/mecha/verb/connect_to_port + else + occupant_message("[name] is not connected to the port at the moment.") + +/obj/mecha/verb/toggle_lights() + set name = "Toggle Lights" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + lights() + +/obj/mecha/verb/lights() + if(usr!=occupant) return + lights = !lights + if(lights) set_light(light_range + lights_power) + else set_light(light_range - lights_power) + src.occupant_message("Toggled lights [lights?"on":"off"].") + log_message("Toggled lights [lights?"on":"off"].") + playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1) + return + + +/obj/mecha/verb/toggle_internal_tank() + set name = "Toggle internal airtank usage" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + internal_tank() + +/obj/mecha/proc/internal_tank() + if(usr!=src.occupant) + return + + var/obj/item/mecha_parts/component/gas/GC = internal_components[MECH_GAS] + if(!GC) + to_chat(occupant, "The life support systems don't seem to respond.") + return + + if(!prob(GC.get_efficiency() * 100)) + to_chat(occupant, "\The [GC] shudders and barks, before returning to how it was before.") + return + + use_internal_tank = !use_internal_tank + src.occupant_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") + src.log_message("Now taking air from [use_internal_tank?"internal airtank":"environment"].") + playsound(src, 'sound/mecha/gasdisconnected.ogg', 30, 1) + return + + +/obj/mecha/verb/toggle_strafing() + set name = "Toggle strafing" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + strafing() + +/obj/mecha/proc/strafing() + if(usr!=src.occupant) + return + strafing = !strafing + src.occupant_message("Toggled strafing mode [strafing?"on":"off"].") + src.log_message("Toggled strafing mode [strafing?"on":"off"].") + return + +/obj/mecha/MouseDrop_T(mob/O, mob/user as mob) + //Humans can pilot mechs. + if(!ishuman(O)) + return + + //Can't put other people into mechs (can comment this out if you want that to be possible) + if(O != user) + return + + move_inside() + +/obj/mecha/verb/enter() + set category = "Object" + set name = "Enter Exosuit" + set src in oview(1) + move_inside() + +//returns an equipment object if we have one of that type, useful since is_type_in_list won't return the object +//since is_type_in_list uses caching, this is a slower operation, so only use it if needed +/obj/mecha/proc/get_equipment(var/equip_type) + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(istype(ME,equip_type)) + return ME + return null + +/obj/mecha/proc/move_inside() + if (usr.stat || !ishuman(usr)) + return + + if (usr.buckled) + to_chat(usr, "You can't climb into the exosuit while buckled!") + return + + src.log_message("[usr] tries to move in.") + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + if(C.handcuffed) + to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") + return + if (src.occupant) + to_chat(usr, "The [src.name] is already occupied!") + src.log_append_to_last("Permission denied.") + return +/* + if (usr.abiotic()) + to_chat(usr, "Subject cannot have abiotic items on.") + return +*/ + var/passed + if(src.dna) + if(usr.dna.unique_enzymes==src.dna) + passed = 1 + else if(src.operation_allowed(usr)) + passed = 1 + if(!passed) + to_chat(usr, "Access denied") + src.log_append_to_last("Permission denied.") + return + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) + return + +// to_chat(usr, "You start climbing into [src.name]") + if(get_equipment(/obj/item/mecha_parts/mecha_equipment/runningboard)) + visible_message("\The [usr] is instantly lifted into [src.name] by the running board!") + moved_inside(usr) + if(ishuman(occupant)) + GrantActions(occupant, 1) + else + visible_message("\The [usr] starts to climb into [src.name]") + if(enter_after(40,usr)) + if(!src.occupant) + moved_inside(usr) + if(ishuman(occupant)) //Aeiou + GrantActions(occupant, 1) + else if(src.occupant!=usr) + to_chat(usr, "[src.occupant] was faster. Try better next time, loser.") + else + to_chat(usr, "You stop entering the exosuit.") + return + +/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) + if(H && H.client && (H in range(1))) + H.reset_view(src) + /* + H.client.perspective = EYE_PERSPECTIVE + H.client.eye = src + */ + H.stop_pulling() + H.forceMove(src) + src.occupant = H + src.add_fingerprint(H) + src.forceMove(src.loc) + src.verbs += /obj/mecha/verb/eject + src.log_append_to_last("[H] moved in as pilot.") + update_icon() + //VOREStation Edit Add + if(occupant.hud_used) + minihud = new (occupant.hud_used, src) + //VOREStation Edit Add End + +//This part removes all the verbs if you don't have them the _possible on your mech. This is a little clunky, but it lets you just add that to any mech. +//And it's not like this 10yo code wasn't clunky before. + + if(!smoke_possible) //Can't use smoke? No verb for you. + verbs -= /obj/mecha/verb/toggle_smoke + if(!thrusters_possible) //Can't use thrusters? No verb for you. + verbs -= /obj/mecha/verb/toggle_thrusters + if(!defence_mode_possible) //Do i need to explain everything? + verbs -= /obj/mecha/verb/toggle_defence_mode + if(!overload_possible) + verbs -= /obj/mecha/verb/toggle_overload + if(!zoom_possible) + verbs -= /obj/mecha/verb/toggle_zoom + if(!phasing_possible) + verbs -= /obj/mecha/verb/toggle_phasing + if(!switch_dmg_type_possible) + verbs -= /obj/mecha/verb/switch_damtype + if(!cloak_possible) + verbs -= /obj/mecha/verb/toggle_cloak + + occupant.in_enclosed_vehicle = 1 //Useful for when you need to know if someone is in a mecho. + update_cell_alerts() + update_damage_alerts() + set_dir(dir_in) + playsound(src, 'sound/machines/door/windowdoor.ogg', 50, 1) + if(occupant.client && cloaked_selfimage) + occupant.client.images += cloaked_selfimage + play_entered_noise(occupant) + return 1 + else + return 0 + +/obj/mecha/proc/play_entered_noise(var/mob/who) + if(!hasInternalDamage()) //Otherwise it's not nominal! + switch(mech_faction) + if(MECH_FACTION_NT)//The good guys category + if(firstactivation)//First time = long activation sound + firstactivation = 1 + who << sound('sound/mecha/LongNanoActivation.ogg',volume=50) + else + who << sound('sound/mecha/nominalnano.ogg',volume=50) + if(MECH_FACTION_SYNDI)//Bad guys + if(firstactivation) + firstactivation = 1 + who << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) + else + who << sound('sound/mecha/nominalsyndi.ogg',volume=50) + else//Everyone else gets the normal noise + who << sound('sound/mecha/nominal.ogg',volume=50) + +/obj/mecha/AltClick(mob/living/user) + if(user == occupant) + strafing() + +/obj/mecha/verb/view_stats() + set name = "View Stats" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + if(usr!=src.occupant) + return + //pr_update_stats.start() + src.occupant << browse(src.get_stats_html(), "window=exosuit") + return + +/* +/obj/mecha/verb/force_eject() + set category = "Object" + set name = "Force Eject" + set src in view(5) + src.go_out() + return +*/ + +/obj/mecha/verb/eject() + set name = "Eject" + set category = "Exosuit Interface" + set src = usr.loc + set popup_menu = 0 + if(usr!=src.occupant) + return + src.go_out() + add_fingerprint(usr) + return + + +/obj/mecha/proc/go_out() //Eject/Exit the mech. Yes this is for easier searching. + if(!src.occupant) return + var/atom/movable/mob_container + QDEL_NULL(minihud) + if(ishuman(occupant)) + mob_container = src.occupant + RemoveActions(occupant, human_occupant=1)//AEIOU + else if(istype(occupant, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/brain = occupant + mob_container = brain.container + else + return + if(mob_container.forceMove(src.loc))//ejecting mob container + log_message("[mob_container] moved out.") + occupant.reset_view() + occupant << browse(null, "window=exosuit") + if(occupant.client && cloaked_selfimage) + occupant.client.images -= cloaked_selfimage + if(istype(mob_container, /obj/item/device/mmi)) + var/obj/item/device/mmi/mmi = mob_container + if(mmi.brainmob) + occupant.loc = mmi + mmi.mecha = null + occupant.canmove = 0 + occupant.clear_alert("charge") + occupant.clear_alert("mech damage") + occupant.in_enclosed_vehicle = 0 + occupant = null + update_icon() + set_dir(dir_in) + verbs -= /obj/mecha/verb/eject + + //src.zoom = 0 + + // Doesn't seem needed. + if(src.occupant && src.occupant.client) + src.occupant.client.view = world.view + src.zoom = 0 + + strafing = 0 + return + +///////////////////////// +////// Access stuff ///// +///////////////////////// + +/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) + for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(src.check_access(ID,src.operation_req_access)) + return 1 + return 0 + + +/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) + if(istype(H)) + for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(src.check_access(ID,src.internals_req_access)) + return 1 + else if(istype(H, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = H + if(src.check_access(R.idcard,src.internals_req_access)) + return 1 + return 0 + + +/obj/mecha/check_access(obj/item/weapon/card/id/I, list/access_list) + if(!istype(access_list)) + return 1 + if(!access_list.len) //no requirements + return 1 + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/pda = I + I = pda.id + if(!istype(I) || !I.access) //not ID or no access + return 0 + if(access_list==src.operation_req_access) + for(var/req in access_list) + if(!(req in I.access)) //doesn't have this access + return 0 + else if(access_list==src.internals_req_access) + for(var/req in access_list) + if(req in I.access) + return 1 + return 1 + + +//////////////////////////////////// +///// Rendering stats window /////// +//////////////////////////////////// + +/obj/mecha/proc/get_stats_html() + var/output = {" + [src.name] data + + + + +
                    + [src.get_stats_part()] +
                    +
                    + [src.get_equipment_list()] +
                    +
                    +
                    + [src.get_commands()] +
                    + + + "} + return output + + +/obj/mecha/proc/report_internal_damage() + var/output = null + var/list/dam_reports = list( + "[MECHA_INT_FIRE]" = "INTERNAL FIRE", + "[MECHA_INT_TEMP_CONTROL]" = "LIFE SUPPORT SYSTEM MALFUNCTION", + "[MECHA_INT_TANK_BREACH]" = "GAS TANK BREACH", + "[MECHA_INT_CONTROL_LOST]" = "COORDINATION SYSTEM CALIBRATION FAILURE - Recalibrate", + "[MECHA_INT_SHORT_CIRCUIT]" = "SHORT CIRCUIT" + ) + for(var/tflag in dam_reports) + var/intdamflag = text2num(tflag) + if(hasInternalDamage(intdamflag)) + output += dam_reports[tflag] + output += "
                    " + if(return_pressure() > WARNING_HIGH_PRESSURE) + output += "DANGEROUSLY HIGH CABIN PRESSURE
                    " + return output + + +/obj/mecha/proc/get_stats_part() + var/integrity = health/initial(health)*100 + var/cell_charge = get_charge() + var/tank_pressure = internal_tank ? round(internal_tank.return_pressure(),0.01) : "None" + var/tank_temperature = internal_tank ? internal_tank.return_temperature() : "Unknown" + var/cabin_pressure = round(return_pressure(),0.01) + + var/obj/item/mecha_parts/component/hull/HC = internal_components[MECH_HULL] + var/obj/item/mecha_parts/component/armor/AC = internal_components[MECH_ARMOR] + + var/output = {"[report_internal_damage()] + Armor Integrity: [AC?"[round(AC.integrity / AC.max_integrity * 100, 0.1)]%":"ARMOR MISSING"]
                    + Hull Integrity: [HC?"[round(HC.integrity / HC.max_integrity * 100, 0.1)]%":"HULL MISSING"]
                    + [integrity<30?"DAMAGE LEVEL CRITICAL
                    ":null] + Chassis Integrity: [integrity]%
                    + Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
                    + Air source: [use_internal_tank?"Internal Airtank":"Environment"]
                    + Airtank pressure: [tank_pressure]kPa
                    + Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
                    + Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
                    + Cabin temperature: [return_temperature()]K|[return_temperature() - T0C]°C
                    + Lights: [lights?"on":"off"]
                    + [src.dna?"DNA-locked:
                    [src.dna] \[Reset\]
                    ":null] + "} + + + if(defence_mode_possible) + output += "Defence mode: [defence_mode?"on":"off"]
                    " + if(overload_possible) + output += "Leg actuators overload: [overload?"on":"off"]
                    " + if(smoke_possible) + output += "Smoke: [smoke_reserve]
                    " + if(thrusters_possible) + output += "Thrusters: [thrusters?"on":"off"]
                    " + +//Cargo components. Keep this last otherwise it does weird alignment issues. + output += "Cargo Compartment Contents:
                    " + if(src.cargo.len) + for(var/obj/O in src.cargo) + output += "Unload : [O]
                    " + else + output += "Nothing" + output += "
                    " + return output + +/obj/mecha/proc/get_commands() + var/output = {"
                    +
                    Electronics
                    + +
                    +
                    +
                    Airtank
                    + +
                    + +
                    [get_equipment_menu()]
                    +
                    + [(/obj/mecha/verb/eject in src.verbs)?"Eject
                    ":null] + "} + return output + +/obj/mecha/proc/get_equipment_menu() //outputs mecha html equipment menu + var/output + if(equipment.len) + output += {"
                    +
                    Equipment
                    +
                    + "} + return output + +/obj/mecha/proc/get_equipment_list() //outputs mecha equipment list in html + if(!equipment.len) + return + var/output = "Equipment:
                    " + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + output += "
                    [MT.get_equip_info()]
                    " + output += "
                    " + return output + + +/obj/mecha/proc/get_log_html() + var/output = "[src.name] Log" + for(var/list/entry in log) + output += {"
                    [time2text(entry["time"],"DDD MMM DD hh:mm:ss")] [game_year]
                    +
                    [entry["message"]]
                    + "} + output += "" + return output + +/obj/mecha/proc/get_log_tgui() + var/list/data = list() + for(var/list/entry in log) + data.Add(list(list( + "time" = time2text(entry["time"], "DDD MMM DD hh:mm:ss"), + "year" = game_year, + "message" = entry["message"], + ))) + return data + + +/obj/mecha/proc/output_access_dialog(obj/item/weapon/card/id/id_card, mob/user) + if(!id_card || !user) return + var/output = {" + + + +

                    Following keycodes are present in this system:

                    "} + for(var/a in operation_req_access) + output += "[get_access_desc(a)] - Delete
                    " + output += "

                    Following keycodes were detected on portable device:

                    " + for(var/a in id_card.access) + if(a in operation_req_access) continue + var/a_name = get_access_desc(a) + if(!a_name) continue //there's some strange access without a name + output += "[a_name] - Add
                    " + output += "
                    Finish (Warning! The ID upload panel will be locked. It can be unlocked only through Exosuit Interface.)" + output += "" + user << browse(output, "window=exosuit_add_access") + onclose(user, "exosuit_add_access") + return + +/obj/mecha/proc/output_maintenance_dialog(obj/item/weapon/card/id/id_card,mob/user) + if(!id_card || !user) return + + var/maint_options = "Set Cabin Air Pressure" + if (locate(/obj/item/mecha_parts/mecha_equipment/tool/passenger) in contents) + maint_options += "Remove Passenger" + + var/output = {" + + + + + [add_req_access?"Edit operation keycodes":null] + [maint_access?"Initiate maintenance protocol":null] + [(state>0) ? maint_options : ""] + + "} + user << browse(output, "window=exosuit_maint_console") + onclose(user, "exosuit_maint_console") + return + + +//////////////////////////////// +/////// Messages and Log /////// +//////////////////////////////// + +/obj/mecha/proc/occupant_message(message as text) + if(message) + if(src.occupant && src.occupant.client) + to_chat(src.occupant, "\icon[src][bicon(src)] [message]") + return + +/obj/mecha/proc/log_message(message as text,red=null) + log.len++ + log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") + return log.len + +/obj/mecha/proc/log_append_to_last(message as text,red=null) + var/list/last_entry = src.log[src.log.len] + last_entry["message"] += "
                    [red?"":null][message][red?"":null]" + return + + +///////////////// +///// Topic ///// +///////////////// + +/obj/mecha/Topic(href, href_list) + ..() + if(href_list["update_content"]) + if(usr != src.occupant) return + send_byjax(src.occupant,"exosuit.browser","content",src.get_stats_part()) + return + if(href_list["close"]) + return + if(usr.stat > 0) + return + var/datum/topic_input/top_filter = new /datum/topic_input(href,href_list) + if(href_list["select_equip"]) + if(usr != src.occupant) return + var/obj/item/mecha_parts/mecha_equipment/equip = top_filter.getObj("select_equip") + if(equip) + src.selected = equip + src.occupant_message("You switch to [equip].") + src.visible_message("[src] raises [equip].") + send_byjax(src.occupant,"exosuit.browser","eq_list",src.get_equipment_list()) + return + if(href_list["eject"]) + if(usr != src.occupant) return + src.eject() + return + if(href_list["toggle_lights"]) + if(usr != src.occupant) return + src.lights() + return +/* + if(href_list["toggle_strafing"]) + if(usr != src.occupant) return + src.strafing() + return*/ + + if(href_list["toggle_airtank"]) + if(usr != src.occupant) return + src.internal_tank() + return + if (href_list["toggle_thrusters"]) + src.toggle_thrusters() + if (href_list["smoke"]) + src.smoke() + if (href_list["toggle_zoom"]) + src.zoom() + if(href_list["toggle_defence_mode"]) + src.defence_mode() + if(href_list["switch_damtype"]) + src.switch_damtype() + if(href_list["phasing"]) + src.phasing() + + if(href_list["rmictoggle"]) + if(usr != src.occupant) return + radio.broadcasting = !radio.broadcasting + send_byjax(src.occupant,"exosuit.browser","rmicstate",(radio.broadcasting?"Engaged":"Disengaged")) + return + if(href_list["rspktoggle"]) + if(usr != src.occupant) return + radio.listening = !radio.listening + send_byjax(src.occupant,"exosuit.browser","rspkstate",(radio.listening?"Engaged":"Disengaged")) + return + if(href_list["rfreq"]) + if(usr != src.occupant) return + var/new_frequency = (radio.frequency + top_filter.getNum("rfreq")) + if ((radio.frequency < PUBLIC_LOW_FREQ || radio.frequency > PUBLIC_HIGH_FREQ)) + new_frequency = sanitize_frequency(new_frequency) + radio.set_frequency(new_frequency) + send_byjax(src.occupant,"exosuit.browser","rfreq","[format_frequency(radio.frequency)]") + return + if(href_list["port_disconnect"]) + if(usr != src.occupant) return + src.disconnect_from_port() + return + if (href_list["port_connect"]) + if(usr != src.occupant) return + src.connect_to_port() + return + if (href_list["view_log"]) + if(usr != src.occupant) return + src.occupant << browse(src.get_log_html(), "window=exosuit_log") + onclose(occupant, "exosuit_log") + return + if (href_list["change_name"]) + if(usr != src.occupant) return + var/newname = sanitizeSafe(tgui_input_text(occupant,"Choose new exosuit name","Rename exosuit",initial(name), MAX_NAME_LEN), MAX_NAME_LEN) + if(newname) + name = newname + else + tgui_alert_async(occupant, "nope.avi") + return + if (href_list["toggle_id_upload"]) + if(usr != src.occupant) return + add_req_access = !add_req_access + send_byjax(src.occupant,"exosuit.browser","t_id_upload","[add_req_access?"L":"Unl"]ock ID upload panel") + return + if(href_list["toggle_maint_access"]) + if(usr != src.occupant) return + if(state) + occupant_message(span_red("Maintenance protocols in effect")) + return + maint_access = !maint_access + send_byjax(src.occupant,"exosuit.browser","t_maint_access","[maint_access?"Forbid":"Permit"] maintenance protocols") + return + if(href_list["req_access"] && add_req_access) + if(!in_range(src, usr)) return + output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) + return + if(href_list["maint_access"] && maint_access) + if(!in_range(src, usr)) return + var/mob/user = top_filter.getMob("user") + if(user) + if(state==MECHA_OPERATING) + state = MECHA_BOLTS_SECURED + to_chat(user, "The securing bolts are now exposed.") + else if(state==MECHA_BOLTS_SECURED) + state = MECHA_OPERATING + to_chat(user, "The securing bolts are now hidden.") + output_maintenance_dialog(top_filter.getObj("id_card"),user) + return + if(href_list["set_internal_tank_valve"] && state >=MECHA_BOLTS_SECURED) + if(!in_range(src, usr)) return + var/mob/user = top_filter.getMob("user") + if(user) + var/new_pressure = tgui_input_number(user,"Input new output pressure","Pressure setting",internal_tank_valve, round_value=FALSE) + if(new_pressure) + internal_tank_valve = new_pressure + to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.") + if(href_list["remove_passenger"] && state >= MECHA_BOLTS_SECURED) + var/mob/user = top_filter.getMob("user") + var/list/passengers = list() + for (var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P in contents) + if (P.occupant) + passengers["[P.occupant]"] = P + + if (!passengers) + to_chat(user, "There are no passengers to remove.") + return + + var/pname = tgui_input_list(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger", passengers) + + if (!pname) + return + + var/obj/item/mecha_parts/mecha_equipment/tool/passenger/P = passengers[pname] + var/mob/occupant = P.occupant + + user.visible_message("\The [user] begins opening the hatch on \the [P]...", "You begin opening the hatch on \the [P]...") + if (!do_after(user, 40)) + return + + user.visible_message("\The [user] opens the hatch on \the [P] and removes [occupant]!", "You open the hatch on \the [P] and remove [occupant]!") + P.go_out() + P.log_message("[occupant] was removed.") + return + if(href_list["add_req_access"] && add_req_access && top_filter.getObj("id_card")) + if(!in_range(src, usr)) return + operation_req_access += top_filter.getNum("add_req_access") + output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) + return + if(href_list["del_req_access"] && add_req_access && top_filter.getObj("id_card")) + if(!in_range(src, usr)) return + operation_req_access -= top_filter.getNum("del_req_access") + output_access_dialog(top_filter.getObj("id_card"),top_filter.getMob("user")) + return + if(href_list["finish_req_access"]) + if(!in_range(src, usr)) return + add_req_access = 0 + var/mob/user = top_filter.getMob("user") + user << browse(null,"window=exosuit_add_access") + return + if(href_list["dna_lock"]) + if(usr != src.occupant) return + if(istype(occupant, /mob/living/carbon/brain)) + occupant_message("You are a brain. No.") + return + if(src.occupant) + src.dna = src.occupant.dna.unique_enzymes + src.occupant_message("You feel a prick as the needle takes your DNA sample.") + return + if(href_list["reset_dna"]) + if(usr != src.occupant) return + src.dna = null + if(href_list["repair_int_control_lost"]) + if(usr != src.occupant) return + src.occupant_message("Recalibrating coordination system.") + src.log_message("Recalibration of coordination system started.") + var/T = src.loc + if(do_after(100)) + if(T == src.loc) + src.clearInternalDamage(MECHA_INT_CONTROL_LOST) + src.occupant_message(span_blue("Recalibration successful.")) + src.log_message("Recalibration of coordination system finished with 0 errors.") + else + src.occupant_message(span_red("Recalibration failed.")) + src.log_message("Recalibration of coordination system failed with 1 error.",1) + if(href_list["drop_from_cargo"]) + var/obj/O = locate(href_list["drop_from_cargo"]) + if(O && (O in src.cargo)) + src.occupant_message("You unload [O].") + O.forceMove(get_turf(src)) + src.cargo -= O + var/turf/T = get_turf(O) + if(T) + T.Entered(O) + src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") + return + + //debug + /* + if(href_list["debug"]) + if(href_list["set_i_dam"]) + setInternalDamage(top_filter.getNum("set_i_dam")) + if(href_list["clear_i_dam"]) + clearInternalDamage(top_filter.getNum("clear_i_dam")) + return + */ + + + +/* + + if (href_list["ai_take_control"]) + var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) + var/duration = text2num(href_list["duration"]) + var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src) + var/cur_occupant = src.occupant + O.invisibility = 0 + O.canmove = 1 + O.name = AI.name + O.real_name = AI.real_name + O.anchored = TRUE + O.aiRestorePowerRoutine = 0 + O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! + O.laws = AI.laws + O.set_stat(AI.stat) + O.oxyloss = AI.getOxyLoss() + O.fireloss = AI.getFireLoss() + O.bruteloss = AI.getBruteLoss() + O.toxloss = AI.toxloss + O.updatehealth() + src.occupant = O + if(AI.mind) + AI.mind.transfer_to(O) + AI.name = "Inactive AI" + AI.real_name = "Inactive AI" + AI.icon_state = "ai-empty" + spawn(duration) + AI.name = O.name + AI.real_name = O.real_name + if(O.mind) + O.mind.transfer_to(AI) + AI.control_disabled = 0 + AI.laws = O.laws + AI.oxyloss = O.getOxyLoss() + AI.fireloss = O.getFireLoss() + AI.bruteloss = O.getBruteLoss() + AI.toxloss = O.toxloss + AI.updatehealth() + qdel(O) + if (!AI.stat) + AI.icon_state = "ai" + else + AI.icon_state = "ai-crash" + src.occupant = cur_occupant +*/ + +/////////////////////// +///// Power stuff ///// +/////////////////////// + +/obj/mecha/proc/has_charge(amount) + return (get_charge()>=amount) + +/obj/mecha/proc/get_charge() + return call((proc_res["dyngetcharge"]||src), "dyngetcharge")() + +/obj/mecha/proc/dyngetcharge()//returns null if no powercell, else returns cell.charge + if(!src.cell) return + return max(0, src.cell.charge) + +/obj/mecha/proc/use_power(amount) + return call((proc_res["dynusepower"]||src), "dynusepower")(amount) + +/obj/mecha/proc/dynusepower(amount) + update_cell_alerts() + var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] + + if(EC) + amount = amount * (2 - EC.get_efficiency()) * EC.charge_cost_mod + else + amount *= 5 + + if(get_charge()) + cell.use(amount) + return 1 + return 0 + +/obj/mecha/proc/give_power(amount) + update_cell_alerts() + var/obj/item/mecha_parts/component/electrical/EC = internal_components[MECH_ELECTRIC] + + if(!EC) + amount /= 4 + else + amount *= EC.get_efficiency() + + if(!isnull(get_charge())) + cell.give(amount) + return 1 + return 0 + +//This is for mobs mostly. +/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message) + + var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] + + var/temp_deflect_chance = deflect_chance + var/temp_damage_minimum = damage_minimum + + if(!ArmC) + temp_deflect_chance = 1 + temp_damage_minimum = 0 + + else + temp_deflect_chance = round(ArmC.get_efficiency() * ArmC.deflect_chance + (defence_mode ? 25 : 0)) + temp_damage_minimum = round(ArmC.get_efficiency() * ArmC.damage_minimum) + + user.setClickCooldown(user.get_attack_speed()) + if(!damage) + return 0 + + src.log_message("Attacked. Attacker - [user].",1) + user.do_attack_animation(src) + + if(prob(temp_deflect_chance))//Deflected + src.log_append_to_last("Armor saved.") + src.occupant_message("\The [user]'s attack is stopped by the armor.") + visible_message("\The [user] rebounds off [src.name]'s armor!") + user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + playsound(src, 'sound/weapons/slash.ogg', 50, 1, -1) + + else if(damage < temp_damage_minimum)//Pathetic damage levels just don't harm MECH. + src.occupant_message("\The [user]'s doesn't dent \the [src] paint.") + src.visible_message("\The [user]'s attack doesn't dent \the [src] armor") + src.log_append_to_last("Armor saved.") + playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) + return + + else + src.take_damage(damage) //Apply damage - The take_damage() proc handles armor values + if(damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. + src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + visible_message("[user] [attack_message] [src]!") + user.attack_log += text("\[[time_stamp()]\] [span_red("attacked [src.name]")]") + + return 1 + + +///////////////////////////////////////// +//////// Mecha process() helpers //////// +///////////////////////////////////////// +/obj/mecha/proc/stop_process(process) + current_processes &= ~process + +/obj/mecha/proc/start_process(process) + current_processes |= process + + +///////////// +/obj/mecha/cloak() + . = ..() + if(occupant && occupant.client && cloaked_selfimage) + occupant.client.images += cloaked_selfimage + +/obj/mecha/uncloak() + if(occupant && occupant.client && cloaked_selfimage) + occupant.client.images -= cloaked_selfimage + return ..() + + +//debug +/* +/obj/mecha/verb/test_int_damage() + set name = "Test internal damage" + set category = "Exosuit Interface" + set src in view(0) + if(!occupant) return + if(usr!=occupant) + return + var/output = {" + + + +

                    Set:

                    + MECHA_INT_FIRE
                    + MECHA_INT_TEMP_CONTROL
                    + MECHA_INT_SHORT_CIRCUIT
                    + MECHA_INT_TANK_BREACH
                    + MECHA_INT_CONTROL_LOST
                    +
                    +

                    Clear:

                    + MECHA_INT_FIRE
                    + MECHA_INT_TEMP_CONTROL
                    + MECHA_INT_SHORT_CIRCUIT
                    + MECHA_INT_TANK_BREACH
                    + MECHA_INT_CONTROL_LOST
                    + + "} + + occupant << browse(output, "window=ex_debug") + //src.health = initial(src.health)/2.2 + //src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + return +*/ + +/obj/mecha/proc/update_cell_alerts() + if(occupant && cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + occupant.throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + occupant.throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + occupant.throw_alert("charge", /obj/screen/alert/emptycell) + +/obj/mecha/proc/update_damage_alerts() + if(occupant) + var/integrity = health/initial(health)*100 + switch(integrity) + if(30 to 45) + occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 1) + if(15 to 35) + occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 2) + if(-INFINITY to 15) + occupant.throw_alert("mech damage", /obj/screen/alert/low_mech_integrity, 3) + else + occupant.clear_alert("mech damage") + +/obj/mecha/blob_act(var/obj/structure/blob/B) + var/datum/blob_type/blob = B?.overmind?.blob_type + if(!istype(blob)) + return FALSE + + var/damage = rand(blob.damage_lower, blob.damage_upper) + src.take_damage(damage, blob.damage_type) + visible_message("\The [B] [blob.attack_verb] \the [src]!", "[blob.attack_message_synth]!") + playsound(src, 'sound/effects/attackblob.ogg', 50, 1) + + return ..() diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 33d7d711e81..cee2768dacd 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -195,7 +195,7 @@ return if(!chassis.selected) chassis.selected = available_equipment[1] - chassis.occupant_message("You select [chassis.selected]") + chassis.occupant_message("You select [chassis.selected]") send_byjax(chassis.occupant,"exosuit.browser","eq_list",chassis.get_equipment_list()) button_icon_state = "mech_cycle_equip_on" button.UpdateIcon() @@ -277,10 +277,10 @@ defence_mode = !defence_mode if(defence_mode) deflect_chance = defence_deflect - src.occupant_message("You enable [src] defence mode.") + src.occupant_message(span_blue("You enable [src] defence mode.")) else deflect_chance = initial(deflect_chance) - src.occupant_message("You disable [src] defence mode.") + src.occupant_message(span_red("You disable [src] defence mode.")) src.log_message("Toggled defence mode.") return @@ -299,17 +299,17 @@ if(usr!=src.occupant) return if(health < initial(health) - initial(health)/3)//Same formula as in movement, just beforehand. - src.occupant_message("Leg actuators damage critical, unable to engage overload.") + src.occupant_message(span_red("Leg actuators damage critical, unable to engage overload.")) overload = 0 //Just to be sure return if(overload) overload = 0 step_energy_drain = initial(step_energy_drain) - src.occupant_message("You disable leg actuators overload.") + src.occupant_message(span_blue("You disable leg actuators overload.")) else overload = 1 step_energy_drain = step_energy_drain*overload_coeff - src.occupant_message("You enable leg actuators overload.") + src.occupant_message(span_red("You enable leg actuators overload.")) src.log_message("Toggled leg actuators overload.") playsound(src, 'sound/mecha/mechanical_toggle.ogg', 50, 1) return @@ -327,12 +327,12 @@ return if(smoke_reserve < 1) - src.occupant_message("You don't have any smoke left in stock!") + src.occupant_message(span_red("You don't have any smoke left in stock!")) return if(smoke_ready) smoke_reserve-- //Remove ammo - src.occupant_message("Smoke fired. [smoke_reserve] usages left.") + src.occupant_message(span_red("Smoke fired. [smoke_reserve] usages left.")) var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() smoke.attach(src) @@ -360,7 +360,10 @@ if(src.occupant.client) src.zoom = !src.zoom src.log_message("Toggled zoom mode.") - src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") + if(src.zoom) + src.occupant_message(span_blue("Zoom mode enabled.")) + else + src.occupant_message(span_red("Zoom mode disabled.")) if(zoom) src.occupant.set_viewsize(12) src.occupant << sound('sound/mecha/imag_enh.ogg',volume=50) @@ -384,7 +387,10 @@ if(get_charge() > 0) thrusters = !thrusters src.log_message("Toggled thrusters.") - src.occupant_message("Thrusters [thrusters?"en":"dis"]abled.") + if(src.thrusters) + src.occupant_message(span_blue("Thrusters enabled.")) + else + src.occupant_message(span_red("Thrusters disabled.")) return @@ -427,7 +433,10 @@ return phasing = !phasing send_byjax(src.occupant,"exosuit.browser","phasing_command","[phasing?"Dis":"En"]able phasing") - src.occupant_message("En":"#f00\">Dis"]abled phasing.") + if(phasing) + src.occupant_message(span_blue("Enabled phasing.")) + else + src.occupant_message(span_red("Disabled phasing.")) return @@ -447,7 +456,10 @@ else cloak() - src.occupant_message("En":"#f00\">Dis"]abled cloaking.") + if(cloaked) + src.occupant_message(span_blue("Enabled cloaking.")) + else + src.occupant_message(span_red("Disabled cloaking.")) return /obj/mecha/verb/toggle_weapons_only_cycle() @@ -461,5 +473,8 @@ if(usr!=src.occupant) return weapons_only_cycle = !weapons_only_cycle - src.occupant_message("En":"#f00\">Dis"]abled weapons only cycling.") + if(weapons_only_cycle) + src.occupant_message(span_blue("Enabled weapons only cycling.")) + else + src.occupant_message(span_red("Disabled weapons only cycling.")) return diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 27974a588dc..cc16359a3c5 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -1,2133 +1,2133 @@ -//////////////////////////////// -///// Construction datums ////// -//////////////////////////////// - -/datum/construction/mecha/custom_action(step, obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/W = I - if (W.remove_fuel(0, user)) - playsound(holder, 'sound/items/Welder2.ogg', 50, 1) - else - return 0 - else if(I.is_wrench()) - playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) - - else if(I.is_screwdriver()) - playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) - - else if(I.is_wirecutter()) - playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) - - else if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = I - if(C.use(4)) - playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) - else - to_chat(user, "There's not enough cable to finish the task.") - return 0 - else if(istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - if(S.get_amount() < 5) - to_chat(user, "There's not enough material in this stack.") - return 0 - else - S.use(5) - return 1 - -/datum/construction/reversible/mecha/custom_action(index as num, diff as num, obj/item/I, mob/user as mob) - if(istype(I, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/W = I - if (W.remove_fuel(0, user)) - playsound(holder, 'sound/items/Welder2.ogg', 50, 1) - else - return 0 - else if(I.is_wrench()) - playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) - - else if(I.is_screwdriver()) - playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) - - else if(I.is_wirecutter()) - playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) - - else if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = I - if(C.use(4)) - playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) - else - to_chat(user, "There's not enough cable to finish the task.") - return 0 - else if(istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - if(S.get_amount() < 5) - to_chat(user, "There's not enough material in this stack.") - return 0 - else - S.use(5) - return 1 - -////////////////////// -// Ripley -////////////////////// -/datum/construction/mecha/ripley_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 - ) - -/datum/construction/mecha/ripley_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/ripley_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/ripley_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "ripley0" - const_holder.density = TRUE - const_holder.overlays.len = 0 - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/ripley - result = "/obj/mecha/working/ripley" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //8 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //10 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //11 - list("key"=IS_WIRECUTTER, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/ripley/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/ripley/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "ripley2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "ripley0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "ripley3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "ripley4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "ripley2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "ripley5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "ripley3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "ripley6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "ripley4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "ripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "ripley5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "ripley8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "ripley6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "ripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "ripley7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "ripley10" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "ripley8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "ripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "ripley9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") - holder.icon_state = "ripley12" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "ripley10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "ripley13" - else - user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "ripley11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "ripley12" - return 1 - -/datum/construction/reversible/mecha/ripley/spawn_result() - ..() - feedback_inc("mecha_ripley_created",1) - return - -////////////////////// -// Gygax -////////////////////// -/datum/construction/mecha/gygax_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/gygax_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/gygax_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/gygax_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/gygax - result = "/obj/mecha/combat/gygax" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/gygax_armour, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced capacitor is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced capacitor is installed"), - //8 - list("key"=/obj/item/weapon/stock_parts/capacitor/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced scanner module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced scanner module is installed"), - //10 - list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/gygax/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/gygax/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/targeting(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") - qdel(I) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") - new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") - qdel(I) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") - new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Gygax Armour Plates to [holder].", "You install Gygax Armour Plates to [holder].") - qdel(I) - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Gygax Armour Plates.", "You secure Gygax Armour Plates.") - holder.icon_state = "gygax19" - else - user.visible_message("[user] pries Gygax Armour Plates from [holder].", "You prie Gygax Armour Plates from [holder].") - new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Gygax Armour Plates to [holder].", "You weld Gygax Armour Plates to [holder].") - else - user.visible_message("[user] unfastens Gygax Armour Plates.", "You unfasten Gygax Armour Plates.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/gygax/spawn_result() - ..() - feedback_inc("mecha_gygax_created",1) - return - - - ////////////////////// -// Serenity -////////////////////// -/datum/construction/mecha/serenity_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/serenity_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/serenity_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/serenity_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/serenity(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/serenity - result = "/obj/mecha/combat/gygax/serenity" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced capacitor is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced capacitor is installed"), - //8 - list("key"=/obj/item/weapon/stock_parts/capacitor/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced scanner module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced scanner module is installed"), - //10 - list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Medical module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Medical module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/medical, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/serenity/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/serenity/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the medical control module into [holder].", "You install the medical control module into [holder].") - qdel(I) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the medical control module.", "You secure the medical control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the medical control module from [holder].", "You remove the medical control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/medical(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") - qdel(I) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the medical control module.", "You unfasten the medical control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") - new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") - qdel(I) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") - new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You pry the internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external armor layer to [holder].", "You install the external armor layer to [holder].") - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external armor layer.") - holder.icon_state = "gygax19-s" - else - user.visible_message("[user] pries the external armor layer from [holder].", "You pry the external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) // Fixes serenity giving Gygax Armor Plates for the reverse action... - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/serenity/spawn_result() - ..() - feedback_inc("mecha_serenity_created",1) - return - - - -//////////////////////// -// Firefighter -//////////////////////// -/datum/construction/mecha/firefighter_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 - list("key"=/obj/item/clothing/suit/fire)//6 - ) - -/datum/construction/mecha/firefighter_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - user.drop_item() - qdel(I) - return 1 - -/datum/construction/mecha/firefighter_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/firefighter_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "fireripley0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/firefighter - result = "/obj/mecha/working/ripley/firefighter" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_CROWBAR, - "desc"="External armor is being installed."), - //4 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //5 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //6 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //7 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //8 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //9 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //10 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //11 - list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //12 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //13 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //14 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //15 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/firefighter/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(15) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(14) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "fireripley2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "fireripley0" - if(13) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "fireripley3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "fireripley4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "fireripley2" - if(11) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "fireripley5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "fireripley3" - if(10) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "fireripley6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "fireripley4" - if(9) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "fireripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "fireripley5" - if(8) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "fireripley8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "fireripley6" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "fireripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "fireripley7" - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "fireripley10" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "fireripley8" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "fireripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "fireripley9" - if(4) - if(diff==FORWARD) - user.visible_message("[user] starts to install the external armor layer to [holder].", "You start to install the external armor layer to [holder].") - holder.icon_state = "fireripley12" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "fireripley10" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") - holder.icon_state = "fireripley13" - else - user.visible_message("[user] removes the external armor from [holder].", "You remove the external armor from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "fireripley11" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "fireripley14" - else - user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "fireripley12" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "fireripley13" - return 1 - -/datum/construction/reversible/mecha/firefighter/spawn_result() - ..() - feedback_inc("mecha_firefighter_created",1) - return - -////////////////////// -// Durand -////////////////////// -/datum/construction/mecha/durand_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 - list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/durand_head) - ) - -/datum/construction/mecha/durand_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/durand_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/durand_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "durand0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/durand - result = "/obj/mecha/combat/durand" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/durand_armour, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced capacitor is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced capacitor is installed"), - //8 - list("key"=/obj/item/weapon/stock_parts/capacitor/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Advanced scanner module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Advanced scanner module is installed"), - //10 - list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/durand/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/durand/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "durand2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "durand0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "durand3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "durand4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "durand2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "durand5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "durand3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "durand6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/main(get_turf(holder)) - holder.icon_state = "durand4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "durand7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "durand5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "durand8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "durand6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "durand9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "durand7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "durand10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/targeting(get_turf(holder)) - holder.icon_state = "durand8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") - qdel(I) - holder.icon_state = "durand11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "durand9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "durand12" - else - user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") - new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "durand10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") - qdel(I) - holder.icon_state = "durand13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "durand11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "durand14" - else - user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") - new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "durand12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "durand15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "durand13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "durand16" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "durand14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "durand17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "durand15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Durand Armour Plates to [holder].", "You install Durand Armour Plates to [holder].") - qdel(I) - holder.icon_state = "durand18" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "durand16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Durand Armour Plates.", "You secure Durand Armour Plates.") - holder.icon_state = "durand19" - else - user.visible_message("[user] pries Durand Armour Plates from [holder].", "You prie Durand Armour Plates from [holder].") - new /obj/item/mecha_parts/part/durand_armour(get_turf(holder)) - holder.icon_state = "durand17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Durand Armour Plates to [holder].", "You weld Durand Armour Plates to [holder].") - else - user.visible_message("[user] unfastens Durand Armour Plates.", "You unfasten Durand Armour Plates.") - holder.icon_state = "durand18" - return 1 - -/datum/construction/reversible/mecha/durand/spawn_result() - ..() - feedback_inc("mecha_durand_created",1) - return - -//////////////////////// -// Odysseus -//////////////////////// -/datum/construction/mecha/odysseus_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 - list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 - list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 - list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 - list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 - list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 - ) - -/datum/construction/mecha/odysseus_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/odysseus_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/odysseus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "odysseus0" - const_holder.density = TRUE - spawn() - qdel(src) - return - - -/datum/construction/reversible/mecha/odysseus - result = "/obj/mecha/medical/odysseus" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //8 - list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //10 - list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //11 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/odysseus/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "odysseus2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "odysseus0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "odysseus3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "odysseus4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "odysseus2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "odysseus5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "odysseus3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "odysseus6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/odysseus/main(get_turf(holder)) - holder.icon_state = "odysseus4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "odysseus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "odysseus5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "odysseus8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) - holder.icon_state = "odysseus6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") - holder.icon_state = "odysseus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "odysseus7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") - holder.icon_state = "odysseus10" - else - user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "odysseus8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "odysseus11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "odysseus9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs [I] layer to [holder].", "You install external reinforced armor layer to [holder].") - holder.icon_state = "odysseus12" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "odysseus10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "odysseus13" - else - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - user.visible_message("[user] pries the plasteel from [holder].", "You prie the plasteel from [holder].") - holder.icon_state = "odysseus11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") - holder.icon_state = "odysseus14" - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "odysseus12" - return 1 - -/datum/construction/reversible/mecha/odysseus/spawn_result() - ..() - feedback_inc("mecha_odysseus_created",1) - return - -////////////////////// -// Phazon -////////////////////// -/datum/construction/mecha/phazon_chassis - result = "/obj/mecha/combat/phazon" - steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 - list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/phazon_head) - ) - -/datum/construction/mecha/phazon_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/phazon_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/phazon_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "phazon0" - const_holder.density = TRUE - spawn() - qdel(src) - return - -/datum/construction/reversible/mecha/phazon - result = "/obj/mecha/combat/phazon" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_WRENCH, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is installed"), - //6 - list("key"=/obj/item/stack/material/steel, - "backkey"=IS_SCREWDRIVER, - "desc"="Hand teleporter is secured"), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Hand teleporter is installed"), - //8 - list("key"=/obj/item/weapon/hand_tele, - "backkey"=IS_SCREWDRIVER, - "desc"="SMES coil is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="SMES coil is installed"), - //10 - list("key"=/obj/item/weapon/smes_coil/super_capacity, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/phazon/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/phazon/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/phazon/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //17 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/phazon/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/phazon/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - switch(index) - if(20) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "phazon2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "phazon0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "phazon3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "phazon4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "phazon2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "phazon5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "phazon3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "phazon6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/phazon/main(get_turf(holder)) - holder.icon_state = "phazon4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "phazon7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "phazon5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "phazon8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/phazon/peripherals(get_turf(holder)) - holder.icon_state = "phazon6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "phazon9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "phazon7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "phazon10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/phazon/targeting(get_turf(holder)) - holder.icon_state = "phazon8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the SMES coil to [holder].", "You install the SMES coil to [holder].") - qdel(I) - holder.icon_state = "phazon11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "phazon9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the SMES coil.", "You secure the SMES coil.") - holder.icon_state = "phazon12" - else - user.visible_message("[user] removes the SMES coil from [holder].", "You remove the SMES coil from [holder].") - new /obj/item/weapon/smes_coil/super_capacity(get_turf(holder)) - holder.icon_state = "phazon10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the hand teleporter to [holder].", "You install the hand teleporter to [holder].") - qdel(I) - holder.icon_state = "phazon13" - else - user.visible_message("[user] unfastens the SMES coil.", "You unfasten the SMES coil.") - holder.icon_state = "phazon11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the hand teleporter.", "You secure the hand teleporter.") - holder.icon_state = "phazon14" - else - user.visible_message("[user] removes the hand teleporter from [holder].", "You remove the hand teleporter from [holder].") - new /obj/item/weapon/hand_tele(get_turf(holder)) - holder.icon_state = "phazon12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") - holder.icon_state = "phazon19" - else - user.visible_message("[user] unfastens the hand teleporter.", "You unfasten the hand teleporter.") - holder.icon_state = "phazon13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "phazon20" - else - user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") - new /obj/item/stack/material/steel(get_turf(holder), 5) - holder.icon_state = "phazon14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "phazon21" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "phazon19" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") - holder.icon_state = "phazon22" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "phazon20" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "phazon23" - else - user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") - new /obj/item/stack/material/plasteel(get_turf(holder), 5) - holder.icon_state = "phazon21" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "phazon22" - return 1 - -/datum/construction/reversible/mecha/phazon/spawn_result() - ..() - feedback_inc("mecha_phazon_created",1) - return - -////////////////////// -// Janus -////////////////////// -/datum/construction/mecha/janus_chassis - result = "/obj/mecha/combat/phazon/janus" - steps = list(list("key"=/obj/item/mecha_parts/part/janus_torso),//1 - list("key"=/obj/item/mecha_parts/part/janus_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/janus_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/janus_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/janus_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/janus_head) - ) - -/datum/construction/mecha/janus_chassis/custom_action(step, obj/item/I, mob/user) - user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") - holder.add_overlay(I.icon_state+"+o") - qdel(I) - return 1 - -/datum/construction/mecha/janus_chassis/action(obj/item/I,mob/user as mob) - return check_all_steps(I,user) - -/datum/construction/mecha/janus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/janus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "janus0" - const_holder.density = TRUE - spawn() - qdel(src) - return - -/datum/construction/reversible/mecha/janus - result = "/obj/mecha/combat/phazon/janus" - steps = list( - //1 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_CROWBAR, - "desc"="External armor is installed."), - //2 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="External armor is attached."), - //3 - list("key"=/obj/item/stack/material/morphium, - "backkey"=/obj/item/weapon/weldingtool, - "desc"="Internal armor is welded"), - //4 - list("key"=/obj/item/weapon/weldingtool, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is wrenched"), - //5 - list("key"=IS_WRENCH, - "backkey"=IS_CROWBAR, - "desc"="Internal armor is attached."), - //6 - list("key"=/obj/item/stack/material/durasteel, - "backkey"=IS_SCREWDRIVER, - "desc"="Durand auxiliary board is secured."), - //7 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Durand auxiliary board is installed"), - //8 - list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Phase coil is secured"), - //9 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Phase coil is installed"), - //10 - list("key"=/obj/item/prop/alien/phasecoil, - "backkey"=IS_SCREWDRIVER, - "desc"="Gygax balance system secured"), - //11 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Gygax balance system installed"), - //12 - list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Targeting module is secured"), - //13 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Targeting module is installed"), - //14 - list("key"=/obj/item/weapon/circuitboard/mecha/imperion/targeting, - "backkey"=IS_SCREWDRIVER, - "desc"="Peripherals control module is secured"), - //15 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Peripherals control module is installed"), - //16 - list("key"=/obj/item/weapon/circuitboard/mecha/imperion/peripherals, - "backkey"=IS_SCREWDRIVER, - "desc"="Central control module is secured"), - //17 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_CROWBAR, - "desc"="Central control module is installed"), - //18 - list("key"=/obj/item/weapon/circuitboard/mecha/imperion/main, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is adjusted"), - //19 - list("key"=/obj/item/weapon/tool/wirecutters, - "backkey"=IS_SCREWDRIVER, - "desc"="The wiring is added"), - //20 - list("key"=/obj/item/stack/cable_coil, - "backkey"=IS_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //21 - list("key"=IS_SCREWDRIVER, - "backkey"=IS_WRENCH, - "desc"="The hydraulic systems are connected."), - //22 - list("key"=IS_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/janus/action(obj/item/I,mob/user as mob) - return check_step(I,user) - -/datum/construction/reversible/mecha/janus/custom_action(index, diff, obj/item/I, mob/user) - if(!..()) - return 0 - - switch(index) - if(22) - user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") - holder.icon_state = "janus1" - if(21) - if(diff==FORWARD) - user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") - holder.icon_state = "janus2" - else - user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") - holder.icon_state = "janus0" - if(20) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") - holder.icon_state = "janus3" - else - user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") - holder.icon_state = "janus1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") - holder.icon_state = "janus4" - else - user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") - new /obj/item/stack/cable_coil(get_turf(holder), 4) - holder.icon_state = "janus2" - if(18) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") - qdel(I) - holder.icon_state = "janus5" - else - user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") - holder.icon_state = "janus3" - if(17) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "janus6" - else - user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") - new /obj/item/weapon/circuitboard/mecha/imperion/main(get_turf(holder)) - holder.icon_state = "janus4" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") - qdel(I) - holder.icon_state = "janus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "janus5" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "janus8" - else - user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/imperion/peripherals(get_turf(holder)) - holder.icon_state = "janus6" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") - qdel(I) - holder.icon_state = "janus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "janus7" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "janus10" - else - user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/imperion/targeting(get_turf(holder)) - holder.icon_state = "janus8" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the Gygax control module into [holder].", "You install the Gygax control module into [holder].") - qdel(I) - holder.icon_state = "janus11" - else - user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") - holder.icon_state = "janus9" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the Gygax control module.", "You secure the Gygax control module.") - holder.icon_state = "janus12" - else - user.visible_message("[user] removes the Gygax control module from [holder].", "You remove the Gygax control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "janus10" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the phase coil into [holder].", "You install the phase coil into [holder].") - qdel(I) - holder.icon_state = "janus13" - else - user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") - holder.icon_state = "janus11" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the phase coil.", "You secure the phase coil.") - holder.icon_state = "janus14" - else - user.visible_message("[user] removes the phase coil from [holder].", "You remove the phase coil from [holder].") - new /obj/item/prop/alien/phasecoil(get_turf(holder)) - holder.icon_state = "janus12" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the Durand control module into [holder].", "You install the Durand control module into [holder].") - qdel(I) - holder.icon_state = "janus15" - else - user.visible_message("[user] unfastens the phase coil.", "You unfasten the phase coil.") - holder.icon_state = "janus13" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the Durand control module.", "You secure the Durand control module.") - holder.icon_state = "janus16" - else - user.visible_message("[user] removes the Durand control module from [holder].", "You remove the Durand control module from [holder].") - new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "janus14" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") - holder.icon_state = "janus17" - else - user.visible_message("[user] unfastens the Durand control module.", "You unfasten the Durand control module.") - holder.icon_state = "janus15" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "janus18" - else - user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") - new /obj/item/stack/material/durasteel(get_turf(holder), 5) - holder.icon_state = "janus16" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") - holder.icon_state = "janus19" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "janus17" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") - holder.icon_state = "janus20" - else - user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") - holder.icon_state = "janus18" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") - holder.icon_state = "janus21" - else - user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") - new /obj/item/stack/material/morphium(get_turf(holder), 5) - holder.icon_state = "janus19" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "janus20" - return 1 - -/datum/construction/reversible/mecha/janus/spawn_result() - ..() - feedback_inc("mecha_janus_created",1) - return +//////////////////////////////// +///// Construction datums ////// +//////////////////////////////// + +/datum/construction/mecha/custom_action(step, obj/item/I, mob/user) + if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/W = I.get_welder() + if(W.remove_fuel(0, user)) + playsound(holder, 'sound/items/Welder2.ogg', 50, 1) + else + return 0 + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_WIRECUTTER)) + playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) + + else if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = I + if(C.use(4)) + playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) + else + to_chat(user, "There's not enough cable to finish the task.") + return 0 + else if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + if(S.get_amount() < 5) + to_chat(user, "There's not enough material in this stack.") + return 0 + else + S.use(5) + return 1 + +/datum/construction/reversible/mecha/custom_action(index as num, diff as num, obj/item/I, mob/user as mob) + if(I.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/W = I.get_welder() + if(W.remove_fuel(0, user)) + playsound(holder, 'sound/items/Welder2.ogg', 50, 1) + else + return 0 + else if(I.has_tool_quality(TOOL_WRENCH)) + playsound(holder, 'sound/items/Ratchet.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(holder, 'sound/items/Screwdriver.ogg', 50, 1) + + else if(I.has_tool_quality(TOOL_WIRECUTTER)) + playsound(holder, 'sound/items/Wirecutter.ogg', 50, 1) + + else if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = I + if(C.use(4)) + playsound(holder, 'sound/items/Deconstruct.ogg', 50, 1) + else + to_chat(user, "There's not enough cable to finish the task.") + return 0 + else if(istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + if(S.get_amount() < 5) + to_chat(user, "There's not enough material in this stack.") + return 0 + else + S.use(5) + return 1 + +////////////////////// +// Ripley +////////////////////// +/datum/construction/mecha/ripley_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 + ) + +/datum/construction/mecha/ripley_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/ripley_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/ripley_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "ripley0" + const_holder.density = TRUE + const_holder.overlays.len = 0 + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/ripley + result = "/obj/mecha/working/ripley" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //8 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //10 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //11 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/ripley/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/ripley/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "ripley2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "ripley0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "ripley3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "ripley4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "ripley2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "ripley5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "ripley3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "ripley6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "ripley4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "ripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "ripley5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "ripley8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "ripley6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "ripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "ripley7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "ripley10" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "ripley8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "ripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "ripley9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") + holder.icon_state = "ripley12" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "ripley10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "ripley13" + else + user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "ripley11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "ripley12" + return 1 + +/datum/construction/reversible/mecha/ripley/spawn_result() + ..() + feedback_inc("mecha_ripley_created",1) + return + +////////////////////// +// Gygax +////////////////////// +/datum/construction/mecha/gygax_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/gygax_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/gygax_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/gygax_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/gygax + result = "/obj/mecha/combat/gygax" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/gygax_armour, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/gygax/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/gygax/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/targeting(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Gygax Armour Plates to [holder].", "You install Gygax Armour Plates to [holder].") + qdel(I) + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Gygax Armour Plates.", "You secure Gygax Armour Plates.") + holder.icon_state = "gygax19" + else + user.visible_message("[user] pries Gygax Armour Plates from [holder].", "You prie Gygax Armour Plates from [holder].") + new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Gygax Armour Plates to [holder].", "You weld Gygax Armour Plates to [holder].") + else + user.visible_message("[user] unfastens Gygax Armour Plates.", "You unfasten Gygax Armour Plates.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/gygax/spawn_result() + ..() + feedback_inc("mecha_gygax_created",1) + return + + + ////////////////////// +// Serenity +////////////////////// +/datum/construction/mecha/serenity_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/serenity_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/serenity_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/serenity_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/serenity(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/serenity + result = "/obj/mecha/combat/gygax/serenity" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Medical module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Medical module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/medical, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/serenity/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/serenity/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the medical control module into [holder].", "You install the medical control module into [holder].") + qdel(I) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the medical control module.", "You secure the medical control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the medical control module from [holder].", "You remove the medical control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/medical(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the medical control module.", "You unfasten the medical control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external armor layer to [holder].", "You install the external armor layer to [holder].") + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external armor layer.") + holder.icon_state = "gygax19-s" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry the external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) // Fixes serenity giving Gygax Armor Plates for the reverse action... + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/serenity/spawn_result() + ..() + feedback_inc("mecha_serenity_created",1) + return + + + +//////////////////////// +// Firefighter +//////////////////////// +/datum/construction/mecha/firefighter_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 + list("key"=/obj/item/clothing/suit/fire)//6 + ) + +/datum/construction/mecha/firefighter_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + user.drop_item() + qdel(I) + return 1 + +/datum/construction/mecha/firefighter_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/firefighter_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "fireripley0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/firefighter + result = "/obj/mecha/working/ripley/firefighter" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_CROWBAR, + "desc"="External armor is being installed."), + //4 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //5 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //6 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //7 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //8 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //9 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //10 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //11 + list("key"=/obj/item/weapon/circuitboard/mecha/ripley/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //12 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //13 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //14 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //15 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/firefighter/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(15) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(14) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "fireripley2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "fireripley0" + if(13) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "fireripley3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "fireripley4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "fireripley2" + if(11) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "fireripley5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "fireripley3" + if(10) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "fireripley6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "fireripley4" + if(9) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "fireripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "fireripley5" + if(8) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "fireripley8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "fireripley6" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "fireripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "fireripley7" + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "fireripley10" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "fireripley8" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "fireripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "fireripley9" + if(4) + if(diff==FORWARD) + user.visible_message("[user] starts to install the external armor layer to [holder].", "You start to install the external armor layer to [holder].") + holder.icon_state = "fireripley12" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "fireripley10" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs external reinforced armor layer to [holder].", "You install external reinforced armor layer to [holder].") + holder.icon_state = "fireripley13" + else + user.visible_message("[user] removes the external armor from [holder].", "You remove the external armor from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "fireripley11" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "fireripley14" + else + user.visible_message("[user] pries external armor layer from [holder].", "You prie external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "fireripley12" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "fireripley13" + return 1 + +/datum/construction/reversible/mecha/firefighter/spawn_result() + ..() + feedback_inc("mecha_firefighter_created",1) + return + +////////////////////// +// Durand +////////////////////// +/datum/construction/mecha/durand_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 + list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/durand_head) + ) + +/datum/construction/mecha/durand_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/durand_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/durand_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "durand0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/durand + result = "/obj/mecha/combat/durand" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/durand_armour, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced capacitor is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced capacitor is installed"), + //8 + list("key"=/obj/item/weapon/stock_parts/capacitor/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Advanced scanner module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Advanced scanner module is installed"), + //10 + list("key"=/obj/item/weapon/stock_parts/scanning_module/adv, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/durand/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/durand/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "durand2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "durand0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "durand3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "durand4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "durand2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "durand5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "durand3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "durand6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/main(get_turf(holder)) + holder.icon_state = "durand4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "durand7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "durand5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "durand8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "durand6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "durand9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "durand7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "durand10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/targeting(get_turf(holder)) + holder.icon_state = "durand8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to [holder].", "You install advanced scanner module to [holder].") + qdel(I) + holder.icon_state = "durand11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "durand9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "durand12" + else + user.visible_message("[user] removes the advanced scanner module from [holder].", "You remove the advanced scanner module from [holder].") + new /obj/item/weapon/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "durand10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to [holder].", "You install advanced capacitor to [holder].") + qdel(I) + holder.icon_state = "durand13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "durand11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "durand14" + else + user.visible_message("[user] removes the advanced capacitor from [holder].", "You remove the advanced capacitor from [holder].") + new /obj/item/weapon/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "durand12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "durand15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "durand13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "durand16" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "durand14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "durand17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "durand15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Durand Armour Plates to [holder].", "You install Durand Armour Plates to [holder].") + qdel(I) + holder.icon_state = "durand18" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "durand16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Durand Armour Plates.", "You secure Durand Armour Plates.") + holder.icon_state = "durand19" + else + user.visible_message("[user] pries Durand Armour Plates from [holder].", "You prie Durand Armour Plates from [holder].") + new /obj/item/mecha_parts/part/durand_armour(get_turf(holder)) + holder.icon_state = "durand17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Durand Armour Plates to [holder].", "You weld Durand Armour Plates to [holder].") + else + user.visible_message("[user] unfastens Durand Armour Plates.", "You unfasten Durand Armour Plates.") + holder.icon_state = "durand18" + return 1 + +/datum/construction/reversible/mecha/durand/spawn_result() + ..() + feedback_inc("mecha_durand_created",1) + return + +//////////////////////// +// Odysseus +//////////////////////// +/datum/construction/mecha/odysseus_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 + list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 + list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 + list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 + list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 + list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 + ) + +/datum/construction/mecha/odysseus_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/odysseus_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/odysseus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "odysseus0" + const_holder.density = TRUE + spawn() + qdel(src) + return + + +/datum/construction/reversible/mecha/odysseus + result = "/obj/mecha/medical/odysseus" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //8 + list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //10 + list("key"=/obj/item/weapon/circuitboard/mecha/odysseus/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //11 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/odysseus/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "odysseus2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "odysseus0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "odysseus3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "odysseus4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "odysseus2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "odysseus5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "odysseus3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "odysseus6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/odysseus/main(get_turf(holder)) + holder.icon_state = "odysseus4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "odysseus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "odysseus5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "odysseus8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) + holder.icon_state = "odysseus6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs internal armor layer to [holder].", "You install internal armor layer to [holder].") + holder.icon_state = "odysseus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "odysseus7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures internal armor layer.", "You secure internal armor layer.") + holder.icon_state = "odysseus10" + else + user.visible_message("[user] pries internal armor layer from [holder].", "You prie internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "odysseus8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "odysseus11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "odysseus9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs [I] layer to [holder].", "You install external reinforced armor layer to [holder].") + holder.icon_state = "odysseus12" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "odysseus10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "odysseus13" + else + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + user.visible_message("[user] pries the plasteel from [holder].", "You prie the plasteel from [holder].") + holder.icon_state = "odysseus11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds external armor layer to [holder].", "You weld external armor layer to [holder].") + holder.icon_state = "odysseus14" + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "odysseus12" + return 1 + +/datum/construction/reversible/mecha/odysseus/spawn_result() + ..() + feedback_inc("mecha_odysseus_created",1) + return + +////////////////////// +// Phazon +////////////////////// +/datum/construction/mecha/phazon_chassis + result = "/obj/mecha/combat/phazon" + steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 + list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/phazon_head) + ) + +/datum/construction/mecha/phazon_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/phazon_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/phazon_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "phazon0" + const_holder.density = TRUE + spawn() + qdel(src) + return + +/datum/construction/reversible/mecha/phazon + result = "/obj/mecha/combat/phazon" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/material/plasteel, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=IS_WELDER, + "backkey"=IS_WRENCH, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is installed"), + //6 + list("key"=/obj/item/stack/material/steel, + "backkey"=IS_SCREWDRIVER, + "desc"="Hand teleporter is secured"), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Hand teleporter is installed"), + //8 + list("key"=/obj/item/weapon/hand_tele, + "backkey"=IS_SCREWDRIVER, + "desc"="SMES coil is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="SMES coil is installed"), + //10 + list("key"=/obj/item/weapon/smes_coil/super_capacity, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/phazon/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/phazon/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/phazon/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //17 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/phazon/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/phazon/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + switch(index) + if(20) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "phazon2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "phazon0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "phazon3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "phazon4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "phazon2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "phazon5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "phazon3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "phazon6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/phazon/main(get_turf(holder)) + holder.icon_state = "phazon4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "phazon7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "phazon5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "phazon8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/phazon/peripherals(get_turf(holder)) + holder.icon_state = "phazon6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "phazon9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "phazon7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "phazon10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/phazon/targeting(get_turf(holder)) + holder.icon_state = "phazon8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the SMES coil to [holder].", "You install the SMES coil to [holder].") + qdel(I) + holder.icon_state = "phazon11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "phazon9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the SMES coil.", "You secure the SMES coil.") + holder.icon_state = "phazon12" + else + user.visible_message("[user] removes the SMES coil from [holder].", "You remove the SMES coil from [holder].") + new /obj/item/weapon/smes_coil/super_capacity(get_turf(holder)) + holder.icon_state = "phazon10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the hand teleporter to [holder].", "You install the hand teleporter to [holder].") + qdel(I) + holder.icon_state = "phazon13" + else + user.visible_message("[user] unfastens the SMES coil.", "You unfasten the SMES coil.") + holder.icon_state = "phazon11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the hand teleporter.", "You secure the hand teleporter.") + holder.icon_state = "phazon14" + else + user.visible_message("[user] removes the hand teleporter from [holder].", "You remove the hand teleporter from [holder].") + new /obj/item/weapon/hand_tele(get_turf(holder)) + holder.icon_state = "phazon12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") + holder.icon_state = "phazon19" + else + user.visible_message("[user] unfastens the hand teleporter.", "You unfasten the hand teleporter.") + holder.icon_state = "phazon13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "phazon20" + else + user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + new /obj/item/stack/material/steel(get_turf(holder), 5) + holder.icon_state = "phazon14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "phazon21" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "phazon19" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") + holder.icon_state = "phazon22" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "phazon20" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "phazon23" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") + new /obj/item/stack/material/plasteel(get_turf(holder), 5) + holder.icon_state = "phazon21" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "phazon22" + return 1 + +/datum/construction/reversible/mecha/phazon/spawn_result() + ..() + feedback_inc("mecha_phazon_created",1) + return + +////////////////////// +// Janus +////////////////////// +/datum/construction/mecha/janus_chassis + result = "/obj/mecha/combat/phazon/janus" + steps = list(list("key"=/obj/item/mecha_parts/part/janus_torso),//1 + list("key"=/obj/item/mecha_parts/part/janus_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/janus_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/janus_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/janus_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/janus_head) + ) + +/datum/construction/mecha/janus_chassis/custom_action(step, obj/item/I, mob/user) + user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]") + holder.add_overlay(I.icon_state+"+o") + qdel(I) + return 1 + +/datum/construction/mecha/janus_chassis/action(obj/item/I,mob/user as mob) + return check_all_steps(I,user) + +/datum/construction/mecha/janus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/janus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "janus0" + const_holder.density = TRUE + spawn() + qdel(src) + return + +/datum/construction/reversible/mecha/janus + result = "/obj/mecha/combat/phazon/janus" + steps = list( + //1 + list("key"=IS_WELDER, + "backkey"=IS_CROWBAR, + "desc"="External armor is installed."), + //2 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="External armor is attached."), + //3 + list("key"=/obj/item/stack/material/morphium, + "backkey"=IS_WELDER, + "desc"="Internal armor is welded"), + //4 + list("key"=IS_WELDER, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is wrenched"), + //5 + list("key"=IS_WRENCH, + "backkey"=IS_CROWBAR, + "desc"="Internal armor is attached."), + //6 + list("key"=/obj/item/stack/material/durasteel, + "backkey"=IS_SCREWDRIVER, + "desc"="Durand auxiliary board is secured."), + //7 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Durand auxiliary board is installed"), + //8 + list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Phase coil is secured"), + //9 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Phase coil is installed"), + //10 + list("key"=/obj/item/prop/alien/phasecoil, + "backkey"=IS_SCREWDRIVER, + "desc"="Gygax balance system secured"), + //11 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Gygax balance system installed"), + //12 + list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Targeting module is secured"), + //13 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Targeting module is installed"), + //14 + list("key"=/obj/item/weapon/circuitboard/mecha/imperion/targeting, + "backkey"=IS_SCREWDRIVER, + "desc"="Peripherals control module is secured"), + //15 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Peripherals control module is installed"), + //16 + list("key"=/obj/item/weapon/circuitboard/mecha/imperion/peripherals, + "backkey"=IS_SCREWDRIVER, + "desc"="Central control module is secured"), + //17 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_CROWBAR, + "desc"="Central control module is installed"), + //18 + list("key"=/obj/item/weapon/circuitboard/mecha/imperion/main, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is adjusted"), + //19 + list("key"=IS_WIRECUTTER, + "backkey"=IS_SCREWDRIVER, + "desc"="The wiring is added"), + //20 + list("key"=/obj/item/stack/cable_coil, + "backkey"=IS_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //21 + list("key"=IS_SCREWDRIVER, + "backkey"=IS_WRENCH, + "desc"="The hydraulic systems are connected."), + //22 + list("key"=IS_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/janus/action(obj/item/I,mob/user as mob) + return check_step(I,user) + +/datum/construction/reversible/mecha/janus/custom_action(index, diff, obj/item/I, mob/user) + if(!..()) + return 0 + + switch(index) + if(22) + user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.") + holder.icon_state = "janus1" + if(21) + if(diff==FORWARD) + user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.") + holder.icon_state = "janus2" + else + user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.") + holder.icon_state = "janus0" + if(20) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].") + holder.icon_state = "janus3" + else + user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.") + holder.icon_state = "janus1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].") + holder.icon_state = "janus4" + else + user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].") + new /obj/item/stack/cable_coil(get_turf(holder), 4) + holder.icon_state = "janus2" + if(18) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].") + qdel(I) + holder.icon_state = "janus5" + else + user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].") + holder.icon_state = "janus3" + if(17) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "janus6" + else + user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].") + new /obj/item/weapon/circuitboard/mecha/imperion/main(get_turf(holder)) + holder.icon_state = "janus4" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].") + qdel(I) + holder.icon_state = "janus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "janus5" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "janus8" + else + user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/imperion/peripherals(get_turf(holder)) + holder.icon_state = "janus6" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].") + qdel(I) + holder.icon_state = "janus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "janus7" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "janus10" + else + user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/imperion/targeting(get_turf(holder)) + holder.icon_state = "janus8" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the Gygax control module into [holder].", "You install the Gygax control module into [holder].") + qdel(I) + holder.icon_state = "janus11" + else + user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") + holder.icon_state = "janus9" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the Gygax control module.", "You secure the Gygax control module.") + holder.icon_state = "janus12" + else + user.visible_message("[user] removes the Gygax control module from [holder].", "You remove the Gygax control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "janus10" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the phase coil into [holder].", "You install the phase coil into [holder].") + qdel(I) + holder.icon_state = "janus13" + else + user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.") + holder.icon_state = "janus11" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the phase coil.", "You secure the phase coil.") + holder.icon_state = "janus14" + else + user.visible_message("[user] removes the phase coil from [holder].", "You remove the phase coil from [holder].") + new /obj/item/prop/alien/phasecoil(get_turf(holder)) + holder.icon_state = "janus12" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the Durand control module into [holder].", "You install the Durand control module into [holder].") + qdel(I) + holder.icon_state = "janus15" + else + user.visible_message("[user] unfastens the phase coil.", "You unfasten the phase coil.") + holder.icon_state = "janus13" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the Durand control module.", "You secure the Durand control module.") + holder.icon_state = "janus16" + else + user.visible_message("[user] removes the Durand control module from [holder].", "You remove the Durand control module from [holder].") + new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "janus14" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].") + holder.icon_state = "janus17" + else + user.visible_message("[user] unfastens the Durand control module.", "You unfasten the Durand control module.") + holder.icon_state = "janus15" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "janus18" + else + user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].") + new /obj/item/stack/material/durasteel(get_turf(holder), 5) + holder.icon_state = "janus16" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].") + holder.icon_state = "janus19" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "janus17" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].") + holder.icon_state = "janus20" + else + user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].") + holder.icon_state = "janus18" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.") + holder.icon_state = "janus21" + else + user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].") + new /obj/item/stack/material/morphium(get_turf(holder), 5) + holder.icon_state = "janus19" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "janus20" + return 1 + +/datum/construction/reversible/mecha/janus/spawn_result() + ..() + feedback_inc("mecha_janus_created",1) + return diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 1c5003ac95d..a3c7ed24b90 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -1,134 +1,134 @@ -/obj/machinery/computer/mecha - name = "Exosuit Control" - desc = "Used to track exosuits, as well as view their logs and activate EMP beacons." - icon_keyboard = "rd_key" - icon_screen = "mecha" - light_color = "#a97faa" - req_access = list(access_robotics) - circuit = /obj/item/weapon/circuitboard/mecha_control - var/list/located = list() - var/screen = 0 - var/list/stored_data - -/obj/machinery/computer/mecha/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/mecha/attack_hand(mob/user) - if(..()) - return - tgui_interact(user) - -/obj/machinery/computer/mecha/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "MechaControlConsole", name) - ui.open() - -/obj/machinery/computer/mecha/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - - - var/list/beacons = list() - for(var/obj/item/mecha_parts/mecha_tracking/TR in world) - var/list/tr_data = TR.tgui_data(user) - if(tr_data) - beacons.Add(list(tr_data)) - data["beacons"] = beacons - - LAZYINITLIST(stored_data) - data["stored_data"] = stored_data - - return data - -/obj/machinery/computer/mecha/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("send_message") - var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) - if(istype(MT)) - var/message = sanitize(tgui_input_text(usr, "Input message", "Transmit message")) - var/obj/mecha/M = MT.in_mecha() - if(message && M) - M.occupant_message(message) - return TRUE - - if("shock") - var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) - if(istype(MT)) - MT.shock() - return TRUE - - if("get_log") - var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) - if(istype(MT)) - stored_data = MT.get_mecha_log() - return TRUE - - if("clear_log") - stored_data = null - return TRUE - -/obj/item/mecha_parts/mecha_tracking - name = "Exosuit tracking beacon" - desc = "Device used to transmit exosuit data." - icon = 'icons/obj/device.dmi' - icon_state = "motion2" - origin_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/mecha_tracking/tgui_data(mob/user) - var/list/data = ..() - if(!in_mecha()) - return FALSE - - var/obj/mecha/M = loc - data["ref"] = REF(src) - data["charge"] = M.get_charge() - data["name"] = M.name - data["health"] = M.health - data["maxHealth"] = initial(M.health) - data["cell"] = M.cell - if(M.cell) - data["cellCharge"] = M.cell.charge - data["cellMaxCharge"] = M.cell.charge - data["airtank"] = M.return_pressure() - data["pilot"] = M.occupant - data["location"] = get_area(M) - data["active"] = M.selected - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - data["cargoUsed"] = RM.cargo.len - data["cargoMax"] = RM.cargo_capacity - - return data - -/obj/item/mecha_parts/mecha_tracking/emp_act() - qdel(src) - return - -/obj/item/mecha_parts/mecha_tracking/ex_act() - qdel(src) - return - -/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() - if(istype(loc, /obj/mecha)) - return loc - return 0 - -/obj/item/mecha_parts/mecha_tracking/proc/shock() - var/obj/mecha/M = in_mecha() - if(M) - M.emp_act(4) - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() - if(!in_mecha()) - return list() - var/obj/mecha/M = loc - return M.get_log_tgui() - - -/obj/item/weapon/storage/box/mechabeacons - name = "Exosuit Tracking Beacons" - starts_with = list(/obj/item/mecha_parts/mecha_tracking = 7) +/obj/machinery/computer/mecha + name = "Exosuit Control" + desc = "Used to track exosuits, as well as view their logs and activate EMP beacons." + icon_keyboard = "rd_key" + icon_screen = "mecha" + light_color = "#a97faa" + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/mecha_control + var/list/located = list() + var/screen = 0 + var/list/stored_data + +/obj/machinery/computer/mecha/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/mecha/attack_hand(mob/user) + if(..()) + return + tgui_interact(user) + +/obj/machinery/computer/mecha/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MechaControlConsole", name) + ui.open() + +/obj/machinery/computer/mecha/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + + var/list/beacons = list() + for(var/obj/item/mecha_parts/mecha_tracking/TR in world) + var/list/tr_data = TR.tgui_data(user) + if(tr_data) + beacons.Add(list(tr_data)) + data["beacons"] = beacons + + LAZYINITLIST(stored_data) + data["stored_data"] = stored_data + + return data + +/obj/machinery/computer/mecha/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("send_message") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) + if(istype(MT)) + var/message = sanitize(tgui_input_text(usr, "Input message", "Transmit message")) + var/obj/mecha/M = MT.in_mecha() + if(message && M) + M.occupant_message(message) + return TRUE + + if("shock") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) + if(istype(MT)) + MT.shock() + return TRUE + + if("get_log") + var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["mt"]) + if(istype(MT)) + stored_data = MT.get_mecha_log() + return TRUE + + if("clear_log") + stored_data = null + return TRUE + +/obj/item/mecha_parts/mecha_tracking + name = "Exosuit tracking beacon" + desc = "Device used to transmit exosuit data." + icon = 'icons/obj/device.dmi' + icon_state = "motion2" + origin_tech = list(TECH_DATA = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/mecha_tracking/tgui_data(mob/user) + var/list/data = ..() + if(!in_mecha()) + return FALSE + + var/obj/mecha/M = loc + data["ref"] = REF(src) + data["charge"] = M.get_charge() + data["name"] = M.name + data["health"] = M.health + data["maxHealth"] = initial(M.health) + data["cell"] = M.cell + if(M.cell) + data["cellCharge"] = M.cell.charge + data["cellMaxCharge"] = M.cell.charge + data["airtank"] = M.return_pressure() + data["pilot"] = M.occupant + data["location"] = get_area(M) + data["active"] = M.selected + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + data["cargoUsed"] = RM.cargo.len + data["cargoMax"] = RM.cargo_capacity + + return data + +/obj/item/mecha_parts/mecha_tracking/emp_act() + qdel(src) + return + +/obj/item/mecha_parts/mecha_tracking/ex_act() + qdel(src) + return + +/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() + if(istype(loc, /obj/mecha)) + return loc + return 0 + +/obj/item/mecha_parts/mecha_tracking/proc/shock() + var/obj/mecha/M = in_mecha() + if(M) + M.emp_act(4) + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() + if(!in_mecha()) + return list() + var/obj/mecha/M = loc + return M.get_log_tgui() + + +/obj/item/weapon/storage/box/mechabeacons + name = "Exosuit Tracking Beacons" + starts_with = list(/obj/item/mecha_parts/mecha_tracking = 7) diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 4be3c27d48d..3476de004c5 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -1,342 +1,342 @@ - ///////////////////////// -////// Mecha Parts ////// -///////////////////////// - -// Mecha circuitboards can be found in /code/game/objects/items/weapons/circuitboards/mecha.dm - -/obj/item/mecha_parts - name = "mecha part" - icon = 'icons/mecha/mech_construct.dmi' - icon_state = "blank" - w_class = ITEMSIZE_HUGE - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2) - - -/obj/item/mecha_parts/chassis - name="Mecha Chassis" - icon_state = "backbone" - var/datum/construction/construct - -/obj/item/mecha_parts/chassis/attackby(obj/item/W as obj, mob/user as mob) - if(!construct || !construct.action(W, user)) - ..() - return - -/obj/item/mecha_parts/chassis/attack_hand() - return - -/////////// Ripley - -/obj/item/mecha_parts/chassis/ripley - name = "Ripley Chassis" - -/obj/item/mecha_parts/chassis/ripley/New() - ..() - construct = new /datum/construction/mecha/ripley_chassis(src) - -/obj/item/mecha_parts/part/ripley_torso - name="Ripley Torso" - desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." - icon_state = "ripley_harness" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_left_arm - name="Ripley Left Arm" - desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_right_arm - name="Ripley Right Arm" - desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_left_leg - name="Ripley Left Leg" - desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/ripley_right_leg - name="Ripley Right Leg" - desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -///////// Gygax - -/obj/item/mecha_parts/chassis/gygax - name = "Gygax Chassis" - -/obj/item/mecha_parts/chassis/gygax/New() - ..() - construct = new /datum/construction/mecha/gygax_chassis(src) - -/obj/item/mecha_parts/part/gygax_torso - name="Gygax Torso" - desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." - icon_state = "gygax_harness" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_head - name="Gygax Head" - desc="A Gygax head. Houses advanced surveilance and targeting sensors." - icon_state = "gygax_head" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_MAGNET = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_left_arm - name="Gygax Left Arm" - desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_right_arm - name="Gygax Right Arm" - desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_left_leg - name="Gygax Left Leg" - icon_state = "gygax_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_right_leg - name="Gygax Right Leg" - icon_state = "gygax_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/gygax_armour - name="Gygax Armour Plates" - icon_state = "gygax_armour" - origin_tech = list(TECH_MATERIAL = 6, TECH_COMBAT = 4, TECH_ENGINEERING = 5) - -////////// Serenity - -/obj/item/mecha_parts/chassis/serenity - name = "Serenity Chassis" - -/obj/item/mecha_parts/chassis/serenity/New() - ..() - construct = new /datum/construction/mecha/serenity_chassis(src) - -//////////// Durand - -/obj/item/mecha_parts/chassis/durand - name = "Durand Chassis" - -/obj/item/mecha_parts/chassis/durand/New() - ..() - construct = new /datum/construction/mecha/durand_chassis(src) - -/obj/item/mecha_parts/part/durand_torso - name="Durand Torso" - icon_state = "durand_harness" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_BIO = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_head - name="Durand Head" - icon_state = "durand_head" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_left_arm - name="Durand Left Arm" - icon_state = "durand_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_right_arm - name="Durand Right Arm" - icon_state = "durand_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_left_leg - name="Durand Left Leg" - icon_state = "durand_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_right_leg - name="Durand Right Leg" - icon_state = "durand_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - -/obj/item/mecha_parts/part/durand_armour - name="Durand Armour Plates" - icon_state = "durand_armour" - origin_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4, TECH_ENGINEERING = 5) - - - -////////// Firefighter - -/obj/item/mecha_parts/chassis/firefighter - name = "Firefighter Chassis" - -/obj/item/mecha_parts/chassis/firefighter/New() - ..() - construct = new /datum/construction/mecha/firefighter_chassis(src) -/* -/obj/item/mecha_parts/part/firefighter_torso - name="Ripley-on-Fire Torso" - icon_state = "ripley_harness" - -/obj/item/mecha_parts/part/firefighter_left_arm - name="Ripley-on-Fire Left Arm" - icon_state = "ripley_l_arm" - -/obj/item/mecha_parts/part/firefighter_right_arm - name="Ripley-on-Fire Right Arm" - icon_state = "ripley_r_arm" - -/obj/item/mecha_parts/part/firefighter_left_leg - name="Ripley-on-Fire Left Leg" - icon_state = "ripley_l_leg" - -/obj/item/mecha_parts/part/firefighter_right_leg - name="Ripley-on-Fire Right Leg" - icon_state = "ripley_r_leg" -*/ - -////////// Phazon - -/obj/item/mecha_parts/chassis/phazon - name = "Phazon Chassis" - origin_tech = list(TECH_MATERIAL = 7) - -/obj/item/mecha_parts/chassis/phazon/New() - ..() - construct = new /datum/construction/mecha/phazon_chassis(src) - -/obj/item/mecha_parts/part/phazon_torso - name="Phazon Torso" - icon_state = "phazon_harness" - //construction_time = 300 - //construction_cost = list(MAT_STEEL=35000,"glass"=10000,"phoron"=20000) - origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 6, TECH_POWER = 6) - -/obj/item/mecha_parts/part/phazon_head - name="Phazon Head" - icon_state = "phazon_head" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=15000,"glass"=5000,"phoron"=10000) - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6) - -/obj/item/mecha_parts/part/phazon_left_arm - name="Phazon Left Arm" - icon_state = "phazon_l_arm" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/phazon_right_arm - name="Phazon Right Arm" - icon_state = "phazon_r_arm" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/phazon_left_leg - name="Phazon Left Leg" - icon_state = "phazon_l_leg" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) - -/obj/item/mecha_parts/part/phazon_right_leg - name="Phazon Right Leg" - icon_state = "phazon_r_leg" - //construction_time = 200 - //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) - -///////// Odysseus - - -/obj/item/mecha_parts/chassis/odysseus - name = "Odysseus Chassis" - -/obj/item/mecha_parts/chassis/odysseus/New() - ..() - construct = new /datum/construction/mecha/odysseus_chassis(src) - -/obj/item/mecha_parts/part/odysseus_head - name="Odysseus Head" - icon_state = "odysseus_head" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 2) - -/obj/item/mecha_parts/part/odysseus_torso - name="Odysseus Torso" - desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." - icon_state = "odysseus_torso" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_left_arm - name="Odysseus Left Arm" - desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_l_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_right_arm - name="Odysseus Right Arm" - desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_r_arm" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_left_leg - name="Odysseus Left Leg" - desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_l_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/obj/item/mecha_parts/part/odysseus_right_leg - name="Odysseus Right Leg" - desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_r_leg" - origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - -/*/obj/item/mecha_parts/part/odysseus_armour - name="Odysseus Carapace" - icon_state = "odysseus_armour" - origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3) - construction_time = 200 - construction_cost = list(MAT_STEEL=15000)*/ - -////////// Janus - -/obj/item/mecha_parts/chassis/janus - name = "Janus Chassis" - origin_tech = list(TECH_MATERIAL = 7) - -/obj/item/mecha_parts/chassis/janus/New() - ..() - construct = new /datum/construction/mecha/janus_chassis(src) - -/obj/item/mecha_parts/part/janus_torso - name="Imperion Torso" - icon_state = "janus_harness" - origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 2, TECH_POWER = 6, TECH_PRECURSOR = 2) - -/obj/item/mecha_parts/part/janus_head - name="Imperion Head" - icon_state = "janus_head" - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6, TECH_PRECURSOR = 1) - -/obj/item/mecha_parts/part/janus_left_arm - name="Prototype Gygax Left Arm" - icon_state = "janus_l_arm" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/janus_right_arm - name="Prototype Gygax Right Arm" - icon_state = "janus_r_arm" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) - -/obj/item/mecha_parts/part/janus_left_leg - name="Prototype Durand Left Leg" - icon_state = "janus_l_leg" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) - -/obj/item/mecha_parts/part/janus_right_leg - name="Prototype Durand Right Leg" - icon_state = "janus_r_leg" - origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) + ///////////////////////// +////// Mecha Parts ////// +///////////////////////// + +// Mecha circuitboards can be found in /code/game/objects/items/weapons/circuitboards/mecha.dm + +/obj/item/mecha_parts + name = "mecha part" + icon = 'icons/mecha/mech_construct.dmi' + icon_state = "blank" + w_class = ITEMSIZE_HUGE + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2) + + +/obj/item/mecha_parts/chassis + name="Mecha Chassis" + icon_state = "backbone" + var/datum/construction/construct + +/obj/item/mecha_parts/chassis/attackby(obj/item/W as obj, mob/user as mob) + if(!construct || !construct.action(W, user)) + ..() + return + +/obj/item/mecha_parts/chassis/attack_hand() + return + +/////////// Ripley + +/obj/item/mecha_parts/chassis/ripley + name = "Ripley Chassis" + +/obj/item/mecha_parts/chassis/ripley/New() + ..() + construct = new /datum/construction/mecha/ripley_chassis(src) + +/obj/item/mecha_parts/part/ripley_torso + name="Ripley Torso" + desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." + icon_state = "ripley_harness" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_left_arm + name="Ripley Left Arm" + desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_right_arm + name="Ripley Right Arm" + desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_left_leg + name="Ripley Left Leg" + desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/ripley_right_leg + name="Ripley Right Leg" + desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +///////// Gygax + +/obj/item/mecha_parts/chassis/gygax + name = "Gygax Chassis" + +/obj/item/mecha_parts/chassis/gygax/New() + ..() + construct = new /datum/construction/mecha/gygax_chassis(src) + +/obj/item/mecha_parts/part/gygax_torso + name="Gygax Torso" + desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." + icon_state = "gygax_harness" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_head + name="Gygax Head" + desc="A Gygax head. Houses advanced surveilance and targeting sensors." + icon_state = "gygax_head" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_MAGNET = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_left_arm + name="Gygax Left Arm" + desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_right_arm + name="Gygax Right Arm" + desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_left_leg + name="Gygax Left Leg" + icon_state = "gygax_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_right_leg + name="Gygax Right Leg" + icon_state = "gygax_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/gygax_armour + name="Gygax Armour Plates" + icon_state = "gygax_armour" + origin_tech = list(TECH_MATERIAL = 6, TECH_COMBAT = 4, TECH_ENGINEERING = 5) + +////////// Serenity + +/obj/item/mecha_parts/chassis/serenity + name = "Serenity Chassis" + +/obj/item/mecha_parts/chassis/serenity/New() + ..() + construct = new /datum/construction/mecha/serenity_chassis(src) + +//////////// Durand + +/obj/item/mecha_parts/chassis/durand + name = "Durand Chassis" + +/obj/item/mecha_parts/chassis/durand/New() + ..() + construct = new /datum/construction/mecha/durand_chassis(src) + +/obj/item/mecha_parts/part/durand_torso + name="Durand Torso" + icon_state = "durand_harness" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_BIO = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_head + name="Durand Head" + icon_state = "durand_head" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_left_arm + name="Durand Left Arm" + icon_state = "durand_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_right_arm + name="Durand Right Arm" + icon_state = "durand_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_left_leg + name="Durand Left Leg" + icon_state = "durand_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_right_leg + name="Durand Right Leg" + icon_state = "durand_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + +/obj/item/mecha_parts/part/durand_armour + name="Durand Armour Plates" + icon_state = "durand_armour" + origin_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4, TECH_ENGINEERING = 5) + + + +////////// Firefighter + +/obj/item/mecha_parts/chassis/firefighter + name = "Firefighter Chassis" + +/obj/item/mecha_parts/chassis/firefighter/New() + ..() + construct = new /datum/construction/mecha/firefighter_chassis(src) +/* +/obj/item/mecha_parts/part/firefighter_torso + name="Ripley-on-Fire Torso" + icon_state = "ripley_harness" + +/obj/item/mecha_parts/part/firefighter_left_arm + name="Ripley-on-Fire Left Arm" + icon_state = "ripley_l_arm" + +/obj/item/mecha_parts/part/firefighter_right_arm + name="Ripley-on-Fire Right Arm" + icon_state = "ripley_r_arm" + +/obj/item/mecha_parts/part/firefighter_left_leg + name="Ripley-on-Fire Left Leg" + icon_state = "ripley_l_leg" + +/obj/item/mecha_parts/part/firefighter_right_leg + name="Ripley-on-Fire Right Leg" + icon_state = "ripley_r_leg" +*/ + +////////// Phazon + +/obj/item/mecha_parts/chassis/phazon + name = "Phazon Chassis" + origin_tech = list(TECH_MATERIAL = 7) + +/obj/item/mecha_parts/chassis/phazon/New() + ..() + construct = new /datum/construction/mecha/phazon_chassis(src) + +/obj/item/mecha_parts/part/phazon_torso + name="Phazon Torso" + icon_state = "phazon_harness" + //construction_time = 300 + //construction_cost = list(MAT_STEEL=35000,"glass"=10000,"phoron"=20000) + origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 6, TECH_POWER = 6) + +/obj/item/mecha_parts/part/phazon_head + name="Phazon Head" + icon_state = "phazon_head" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=15000,"glass"=5000,"phoron"=10000) + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6) + +/obj/item/mecha_parts/part/phazon_left_arm + name="Phazon Left Arm" + icon_state = "phazon_l_arm" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/phazon_right_arm + name="Phazon Right Arm" + icon_state = "phazon_r_arm" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/phazon_left_leg + name="Phazon Left Leg" + icon_state = "phazon_l_leg" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) + +/obj/item/mecha_parts/part/phazon_right_leg + name="Phazon Right Leg" + icon_state = "phazon_r_leg" + //construction_time = 200 + //construction_cost = list(MAT_STEEL=20000,"phoron"=10000) + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3) + +///////// Odysseus + + +/obj/item/mecha_parts/chassis/odysseus + name = "Odysseus Chassis" + +/obj/item/mecha_parts/chassis/odysseus/New() + ..() + construct = new /datum/construction/mecha/odysseus_chassis(src) + +/obj/item/mecha_parts/part/odysseus_head + name="Odysseus Head" + icon_state = "odysseus_head" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 2) + +/obj/item/mecha_parts/part/odysseus_torso + name="Odysseus Torso" + desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." + icon_state = "odysseus_torso" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_BIO = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_left_arm + name="Odysseus Left Arm" + desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_l_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_right_arm + name="Odysseus Right Arm" + desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_r_arm" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_left_leg + name="Odysseus Left Leg" + desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_l_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/obj/item/mecha_parts/part/odysseus_right_leg + name="Odysseus Right Leg" + desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_r_leg" + origin_tech = list(TECH_DATA = 2, TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + +/*/obj/item/mecha_parts/part/odysseus_armour + name="Odysseus Carapace" + icon_state = "odysseus_armour" + origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3) + construction_time = 200 + construction_cost = list(MAT_STEEL=15000)*/ + +////////// Janus + +/obj/item/mecha_parts/chassis/janus + name = "Janus Chassis" + origin_tech = list(TECH_MATERIAL = 7) + +/obj/item/mecha_parts/chassis/janus/New() + ..() + construct = new /datum/construction/mecha/janus_chassis(src) + +/obj/item/mecha_parts/part/janus_torso + name="Imperion Torso" + icon_state = "janus_harness" + origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 2, TECH_POWER = 6, TECH_PRECURSOR = 2) + +/obj/item/mecha_parts/part/janus_head + name="Imperion Head" + icon_state = "janus_head" + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6, TECH_PRECURSOR = 1) + +/obj/item/mecha_parts/part/janus_left_arm + name="Prototype Gygax Left Arm" + icon_state = "janus_l_arm" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/janus_right_arm + name="Prototype Gygax Right Arm" + icon_state = "janus_r_arm" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2) + +/obj/item/mecha_parts/part/janus_left_leg + name="Prototype Durand Left Leg" + icon_state = "janus_l_leg" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) + +/obj/item/mecha_parts/part/janus_right_leg + name="Prototype Durand Right Leg" + icon_state = "janus_r_leg" + origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1) diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm index 5dbee5556bc..8a3df3e0247 100644 --- a/code/game/mecha/mecha_wreckage.dm +++ b/code/game/mecha/mecha_wreckage.dm @@ -1,231 +1,231 @@ -/////////////////////////////////// -//////// Mecha wreckage //////// -/////////////////////////////////// - - -/obj/effect/decal/mecha_wreckage - name = "Exosuit wreckage" - desc = "Remains of some unfortunate mecha. Completely unrepairable." - icon = 'icons/mecha/mecha.dmi' - density = TRUE - anchored = FALSE - opacity = 0 - var/list/welder_salvage = list(/obj/item/stack/material/plasteel,/obj/item/stack/material/steel,/obj/item/stack/rods) - var/list/wirecutters_salvage = list(/obj/item/stack/cable_coil) - var/list/crowbar_salvage - var/salvage_num = 5 - -/obj/effect/decal/mecha_wreckage/New() - ..() - crowbar_salvage = new - return - -/obj/effect/decal/mecha_wreckage/ex_act(severity) - if(severity < 2) - spawn - qdel(src) - return - -/obj/effect/decal/mecha_wreckage/bullet_act(var/obj/item/projectile/Proj) - return - - -/obj/effect/decal/mecha_wreckage/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(salvage_num <= 0) - to_chat(user, "You don't see anything that can be cut with [W].") - return - if (!isemptylist(welder_salvage) && WT.remove_fuel(0,user)) - var/type = prob(70)?pick(welder_salvage):null - if(type) - var/N = new type(get_turf(user)) - user.visible_message("[user] cuts [N] from [src]", "You cut [N] from [src]", "You hear a sound of welder nearby") - if(istype(N, /obj/item/mecha_parts/part)) - welder_salvage -= type - salvage_num-- - else - to_chat(user, "You failed to salvage anything valuable from [src].") - else - to_chat(user, "You need more welding fuel to complete this task.") - return - if(W.is_wirecutter()) - if(salvage_num <= 0) - to_chat(user, "You don't see anything that can be cut with [W].") - return - else if(!isemptylist(wirecutters_salvage)) - var/type = prob(70)?pick(wirecutters_salvage):null - if(type) - var/N = new type(get_turf(user)) - user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") - salvage_num-- - else - to_chat(user, "You failed to salvage anything valuable from [src].") - if(W.is_crowbar()) - if(!isemptylist(crowbar_salvage)) - var/obj/S = pick(crowbar_salvage) - if(S) - S.loc = get_turf(user) - crowbar_salvage -= S - user.visible_message("[user] pries [S] from [src].", "You pry [S] from [src].") - return - else - to_chat(user, "You don't see anything that can be pried with [W].") - else - ..() - return - - -/obj/effect/decal/mecha_wreckage/gygax - name = "Gygax wreckage" - icon_state = "gygax-broken" - -/obj/effect/decal/mecha_wreckage/gygax/New() - ..() - var/list/parts = list(/obj/item/mecha_parts/part/gygax_torso, - /obj/item/mecha_parts/part/gygax_head, - /obj/item/mecha_parts/part/gygax_left_arm, - /obj/item/mecha_parts/part/gygax_right_arm, - /obj/item/mecha_parts/part/gygax_left_leg, - /obj/item/mecha_parts/part/gygax_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/gygax/dark - name = "Dark Gygax wreckage" - icon_state = "darkgygax-broken" - -/obj/effect/decal/mecha_wreckage/gygax/adv - name = "Advanced Dark Gygax wreckage" - icon_state = "darkgygax_adv-broken" - -/obj/effect/decal/mecha_wreckage/gygax/medgax - name = "Medgax wreckage" - icon_state = "medgax-broken" - -/obj/effect/decal/mecha_wreckage/gygax/serenity - name = "Serenity wreckage" - icon_state = "medgax-broken" - -/obj/effect/decal/mecha_wreckage/marauder - name = "Marauder wreckage" - icon_state = "marauder-broken" - -/obj/effect/decal/mecha_wreckage/mauler - name = "Mauler Wreckage" - icon_state = "mauler-broken" - desc = "The syndicate won't be very happy about this..." - -/obj/effect/decal/mecha_wreckage/seraph - name = "Seraph wreckage" - icon_state = "seraph-broken" - -/obj/effect/decal/mecha_wreckage/ripley - name = "Ripley wreckage" - icon_state = "ripley-broken" - -/obj/effect/decal/mecha_wreckage/ripley/New() - ..() - var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, - /obj/item/mecha_parts/part/ripley_left_arm, - /obj/item/mecha_parts/part/ripley_right_arm, - /obj/item/mecha_parts/part/ripley_left_leg, - /obj/item/mecha_parts/part/ripley_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/ripley/firefighter - name = "Firefighter wreckage" - icon_state = "firefighter-broken" - -/obj/effect/decal/mecha_wreckage/ripley/firefighter/New() - ..() - var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, - /obj/item/mecha_parts/part/ripley_left_arm, - /obj/item/mecha_parts/part/ripley_right_arm, - /obj/item/mecha_parts/part/ripley_left_leg, - /obj/item/mecha_parts/part/ripley_right_leg, - /obj/item/clothing/suit/fire) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/ripley/deathripley - name = "Death-Ripley wreckage" - icon_state = "deathripley-broken" - -/obj/effect/decal/mecha_wreckage/durand - name = "Durand wreckage" - icon_state = "durand-broken" - -/obj/effect/decal/mecha_wreckage/durand/New() - ..() - var/list/parts = list( - /obj/item/mecha_parts/part/durand_torso, - /obj/item/mecha_parts/part/durand_head, - /obj/item/mecha_parts/part/durand_left_arm, - /obj/item/mecha_parts/part/durand_right_arm, - /obj/item/mecha_parts/part/durand_left_leg, - /obj/item/mecha_parts/part/durand_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/phazon - name = "Phazon wreckage" - icon_state = "phazon-broken" - - -/obj/effect/decal/mecha_wreckage/odysseus - name = "Odysseus wreckage" - icon_state = "odysseus-broken" - -/obj/effect/decal/mecha_wreckage/odysseus/New() - ..() - var/list/parts = list( - /obj/item/mecha_parts/part/odysseus_torso, - /obj/item/mecha_parts/part/odysseus_head, - /obj/item/mecha_parts/part/odysseus_left_arm, - /obj/item/mecha_parts/part/odysseus_right_arm, - /obj/item/mecha_parts/part/odysseus_left_leg, - /obj/item/mecha_parts/part/odysseus_right_leg) - for(var/i=0;i<2;i++) - if(!isemptylist(parts) && prob(40)) - var/part = pick(parts) - welder_salvage += part - parts -= part - return - -/obj/effect/decal/mecha_wreckage/odysseus/murdysseus - icon_state = "murdysseus-broken" - -/obj/effect/decal/mecha_wreckage/hoverpod - name = "Hover pod wreckage" - icon_state = "engineering_pod-broken" - -/obj/effect/decal/mecha_wreckage/janus - name = "Janus wreckage" - icon_state = "janus-broken" - description_info = "Due to the incredibly intricate design of this exosuit, it is impossible to salvage components from it." - -/obj/effect/decal/mecha_wreckage/shuttlecraft - name = "Shuttlecraft wreckage" - desc = "Remains of some unfortunate shuttlecraft. Completely unrepairable." - icon = 'icons/mecha/mecha64x64.dmi' - icon_state = "shuttle_standard-broken" - bound_width = 64 - bound_height = 64 +/////////////////////////////////// +//////// Mecha wreckage //////// +/////////////////////////////////// + + +/obj/effect/decal/mecha_wreckage + name = "Exosuit wreckage" + desc = "Remains of some unfortunate mecha. Completely unrepairable." + icon = 'icons/mecha/mecha.dmi' + density = TRUE + anchored = FALSE + opacity = 0 + var/list/welder_salvage = list(/obj/item/stack/material/plasteel,/obj/item/stack/material/steel,/obj/item/stack/rods) + var/list/wirecutters_salvage = list(/obj/item/stack/cable_coil) + var/list/crowbar_salvage + var/salvage_num = 5 + +/obj/effect/decal/mecha_wreckage/New() + ..() + crowbar_salvage = new + return + +/obj/effect/decal/mecha_wreckage/ex_act(severity) + if(severity < 2) + spawn + qdel(src) + return + +/obj/effect/decal/mecha_wreckage/bullet_act(var/obj/item/projectile/Proj) + return + + +/obj/effect/decal/mecha_wreckage/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(salvage_num <= 0) + to_chat(user, "You don't see anything that can be cut with [W].") + return + if (!isemptylist(welder_salvage) && WT.remove_fuel(0,user)) + var/type = prob(70)?pick(welder_salvage):null + if(type) + var/N = new type(get_turf(user)) + user.visible_message("[user] cuts [N] from [src]", "You cut [N] from [src]", "You hear a sound of welder nearby") + if(istype(N, /obj/item/mecha_parts/part)) + welder_salvage -= type + salvage_num-- + else + to_chat(user, "You failed to salvage anything valuable from [src].") + else + to_chat(user, "You need more welding fuel to complete this task.") + return + if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(salvage_num <= 0) + to_chat(user, "You don't see anything that can be cut with [W].") + return + else if(!isemptylist(wirecutters_salvage)) + var/type = prob(70)?pick(wirecutters_salvage):null + if(type) + var/N = new type(get_turf(user)) + user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") + salvage_num-- + else + to_chat(user, "You failed to salvage anything valuable from [src].") + if(W.has_tool_quality(TOOL_CROWBAR)) + if(!isemptylist(crowbar_salvage)) + var/obj/S = pick(crowbar_salvage) + if(S) + S.loc = get_turf(user) + crowbar_salvage -= S + user.visible_message("[user] pries [S] from [src].", "You pry [S] from [src].") + return + else + to_chat(user, "You don't see anything that can be pried with [W].") + else + ..() + return + + +/obj/effect/decal/mecha_wreckage/gygax + name = "Gygax wreckage" + icon_state = "gygax-broken" + +/obj/effect/decal/mecha_wreckage/gygax/New() + ..() + var/list/parts = list(/obj/item/mecha_parts/part/gygax_torso, + /obj/item/mecha_parts/part/gygax_head, + /obj/item/mecha_parts/part/gygax_left_arm, + /obj/item/mecha_parts/part/gygax_right_arm, + /obj/item/mecha_parts/part/gygax_left_leg, + /obj/item/mecha_parts/part/gygax_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/gygax/dark + name = "Dark Gygax wreckage" + icon_state = "darkgygax-broken" + +/obj/effect/decal/mecha_wreckage/gygax/adv + name = "Advanced Dark Gygax wreckage" + icon_state = "darkgygax_adv-broken" + +/obj/effect/decal/mecha_wreckage/gygax/medgax + name = "Medgax wreckage" + icon_state = "medgax-broken" + +/obj/effect/decal/mecha_wreckage/gygax/serenity + name = "Serenity wreckage" + icon_state = "medgax-broken" + +/obj/effect/decal/mecha_wreckage/marauder + name = "Marauder wreckage" + icon_state = "marauder-broken" + +/obj/effect/decal/mecha_wreckage/mauler + name = "Mauler Wreckage" + icon_state = "mauler-broken" + desc = "The syndicate won't be very happy about this..." + +/obj/effect/decal/mecha_wreckage/seraph + name = "Seraph wreckage" + icon_state = "seraph-broken" + +/obj/effect/decal/mecha_wreckage/ripley + name = "Ripley wreckage" + icon_state = "ripley-broken" + +/obj/effect/decal/mecha_wreckage/ripley/New() + ..() + var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, + /obj/item/mecha_parts/part/ripley_left_arm, + /obj/item/mecha_parts/part/ripley_right_arm, + /obj/item/mecha_parts/part/ripley_left_leg, + /obj/item/mecha_parts/part/ripley_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/ripley/firefighter + name = "Firefighter wreckage" + icon_state = "firefighter-broken" + +/obj/effect/decal/mecha_wreckage/ripley/firefighter/New() + ..() + var/list/parts = list(/obj/item/mecha_parts/part/ripley_torso, + /obj/item/mecha_parts/part/ripley_left_arm, + /obj/item/mecha_parts/part/ripley_right_arm, + /obj/item/mecha_parts/part/ripley_left_leg, + /obj/item/mecha_parts/part/ripley_right_leg, + /obj/item/clothing/suit/fire) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/ripley/deathripley + name = "Death-Ripley wreckage" + icon_state = "deathripley-broken" + +/obj/effect/decal/mecha_wreckage/durand + name = "Durand wreckage" + icon_state = "durand-broken" + +/obj/effect/decal/mecha_wreckage/durand/New() + ..() + var/list/parts = list( + /obj/item/mecha_parts/part/durand_torso, + /obj/item/mecha_parts/part/durand_head, + /obj/item/mecha_parts/part/durand_left_arm, + /obj/item/mecha_parts/part/durand_right_arm, + /obj/item/mecha_parts/part/durand_left_leg, + /obj/item/mecha_parts/part/durand_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/phazon + name = "Phazon wreckage" + icon_state = "phazon-broken" + + +/obj/effect/decal/mecha_wreckage/odysseus + name = "Odysseus wreckage" + icon_state = "odysseus-broken" + +/obj/effect/decal/mecha_wreckage/odysseus/New() + ..() + var/list/parts = list( + /obj/item/mecha_parts/part/odysseus_torso, + /obj/item/mecha_parts/part/odysseus_head, + /obj/item/mecha_parts/part/odysseus_left_arm, + /obj/item/mecha_parts/part/odysseus_right_arm, + /obj/item/mecha_parts/part/odysseus_left_leg, + /obj/item/mecha_parts/part/odysseus_right_leg) + for(var/i=0;i<2;i++) + if(!isemptylist(parts) && prob(40)) + var/part = pick(parts) + welder_salvage += part + parts -= part + return + +/obj/effect/decal/mecha_wreckage/odysseus/murdysseus + icon_state = "murdysseus-broken" + +/obj/effect/decal/mecha_wreckage/hoverpod + name = "Hover pod wreckage" + icon_state = "engineering_pod-broken" + +/obj/effect/decal/mecha_wreckage/janus + name = "Janus wreckage" + icon_state = "janus-broken" + description_info = "Due to the incredibly intricate design of this exosuit, it is impossible to salvage components from it." + +/obj/effect/decal/mecha_wreckage/shuttlecraft + name = "Shuttlecraft wreckage" + desc = "Remains of some unfortunate shuttlecraft. Completely unrepairable." + icon = 'icons/mecha/mecha64x64.dmi' + icon_state = "shuttle_standard-broken" + bound_width = 64 + bound_height = 64 diff --git a/code/game/mecha/medical/medical.dm b/code/game/mecha/medical/medical.dm index 973ddec3fc1..b001e50822c 100644 --- a/code/game/mecha/medical/medical.dm +++ b/code/game/mecha/medical/medical.dm @@ -1,43 +1,43 @@ -/obj/mecha/medical - max_hull_equip = 1 - max_weapon_equip = 0 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - - stomp_sound = 'sound/mecha/mechmove01.ogg' - - cargo_capacity = 1 - - starting_components = list( - /obj/item/mecha_parts/component/hull, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/lightweight, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - -/obj/mecha/medical/Initialize() - . = ..() - var/turf/T = get_turf(src) - if(isPlayerLevel(T.z)) - new /obj/item/mecha_parts/mecha_tracking(src) - -/* // One horrific bastardization of glorious inheritence dead. A billion to go. ~Mech -/obj/mecha/medical/mechturn(direction) - set_dir(direction) - playsound(src,'sound/mecha/mechmove01.ogg',40,1) - return 1 - -/obj/mecha/medical/mechstep(direction) - var/result = step(src,direction) - if(result) - playsound(src,'sound/mecha/mechstep.ogg',25,1) - return result - -/obj/mecha/medical/mechsteprand() - var/result = step_rand(src) - if(result) - playsound(src,'sound/mecha/mechstep.ogg',25,1) - return result -*/ +/obj/mecha/medical + max_hull_equip = 1 + max_weapon_equip = 0 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + + stomp_sound = 'sound/mecha/mechmove01.ogg' + + cargo_capacity = 1 + + starting_components = list( + /obj/item/mecha_parts/component/hull, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/lightweight, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + +/obj/mecha/medical/Initialize() + . = ..() + var/turf/T = get_turf(src) + if(isPlayerLevel(T.z)) + new /obj/item/mecha_parts/mecha_tracking(src) + +/* // One horrific bastardization of glorious inheritence dead. A billion to go. ~Mech +/obj/mecha/medical/mechturn(direction) + set_dir(direction) + playsound(src,'sound/mecha/mechmove01.ogg',40,1) + return 1 + +/obj/mecha/medical/mechstep(direction) + var/result = step(src,direction) + if(result) + playsound(src,'sound/mecha/mechstep.ogg',25,1) + return result + +/obj/mecha/medical/mechsteprand() + var/result = step_rand(src) + if(result) + playsound(src,'sound/mecha/mechstep.ogg',25,1) + return result +*/ diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index f1612e9083d..1215f756f1f 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -1,146 +1,146 @@ -/obj/mecha/medical/odysseus - desc = "These exosuits are developed and produced by Vey-Med. (© All rights reserved)." - name = "Odysseus" - catalogue_data = list( - /datum/category_item/catalogue/technology/odysseus, - /datum/category_item/catalogue/information/organization/vey_med - ) - icon_state = "odysseus" - initial_icon = "odysseus" - step_in = 2 - max_temperature = 15000 - health = 70 - maxhealth = 70 - wreckage = /obj/effect/decal/mecha_wreckage/odysseus - internal_damage_threshold = 35 - deflect_chance = 15 - step_energy_drain = 6 - var/obj/item/clothing/glasses/hud/health/mech/hud - - icon_scale_x = 1.2 - icon_scale_y = 1.2 - -/obj/mecha/medical/odysseus/New() - ..() - hud = new /obj/item/clothing/glasses/hud/health/mech(src) - return - -/obj/mecha/medical/odysseus/moved_inside(var/mob/living/carbon/human/H as mob) - if(..()) - if(H.glasses) - occupant_message("[H.glasses] prevent you from using [src] [hud]") - else - H.glasses = hud - H.recalculate_vis() - return 1 - else - return 0 - -/obj/mecha/medical/odysseus/go_out() - if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - if(H.glasses == hud) - H.glasses = null - H.recalculate_vis() - ..() - return -/* - verb/set_perspective() - set name = "Set client perspective." - set category = "Exosuit Interface" - set src = usr.loc - var/perspective = input(usr, "Select a perspective type.", - "Client perspective", - occupant.client.perspective) in list(MOB_PERSPECTIVE,EYE_PERSPECTIVE) - to_world("[perspective]") - occupant.client.perspective = perspective - return - - verb/toggle_eye() - set name = "Toggle eye." - set category = "Exosuit Interface" - set src = usr.loc - if(occupant.client.eye == occupant) - occupant.client.eye = src - else - occupant.client.eye = occupant - to_world("[occupant.client.eye]") - return -*/ - -//TODO - Check documentation for client.eye and client.perspective... -/obj/item/clothing/glasses/hud/health/mech - name = "Integrated Medical Hud" - - -// process_hud(var/mob/M) //TODO VIS -/* - to_world("view(M)") - for(var/mob/mob in view(M)) - to_world("[mob]") - to_world("view(M.client)") - for(var/mob/mob in view(M.client)) - to_world("[mob]") - to_world("view(M.loc)") - for(var/mob/mob in view(M.loc)) - to_world("[mob]") - - - if(!M || M.stat || !(M in view(M))) return - if(!M.client) return - var/client/C = M.client - var/image/holder - for(var/mob/living/carbon/human/patient in view(M.loc)) - if(M.see_invisible < patient.invisibility) - continue - var/foundVirus = 0 - - for (var/ID in patient.virus2) - if (ID in virusDB) - foundVirus = 1 - break - - holder = patient.hud_list[HEALTH_HUD] - if(patient.stat == DEAD) - holder.icon_state = "hudhealth-100" - C.images += holder - else - holder.icon_state = RoundHealth((patient.health-config.health_threshold_crit)/(patient.getMaxHealth()-config.health_threshold_crit)*100) - C.images += holder - - holder = patient.hud_list[STATUS_HUD] - if(patient.isSynthetic()) - holder.icon_state = "hudrobo" - else if(patient.stat == DEAD) - holder.icon_state = "huddead" - else if(foundVirus) - holder.icon_state = "hudill" - else if(patient.has_brain_worms()) - var/mob/living/simple_mob/animal/borer/B = patient.has_brain_worms() - if(B.controlling) - holder.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - else - holder.icon_state = "hudhealthy" - - C.images += holder -*/ -/obj/mecha/medical/odysseus/loaded/Initialize() - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper - ME.attach(src) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun - ME.attach(src) - -//Meant for random spawns. -/obj/mecha/medical/odysseus/old - desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." - -/obj/mecha/medical/odysseus/old/New() - ..() - health = 25 - maxhealth = 50 //Just slightly worse. - cell.charge = rand(0, (cell.charge/2)) \ No newline at end of file +/obj/mecha/medical/odysseus + desc = "These exosuits are developed and produced by Vey-Med. (© All rights reserved)." + name = "Odysseus" + catalogue_data = list( + /datum/category_item/catalogue/technology/odysseus, + /datum/category_item/catalogue/information/organization/vey_med + ) + icon_state = "odysseus" + initial_icon = "odysseus" + step_in = 2 + max_temperature = 15000 + health = 70 + maxhealth = 70 + wreckage = /obj/effect/decal/mecha_wreckage/odysseus + internal_damage_threshold = 35 + deflect_chance = 15 + step_energy_drain = 6 + var/obj/item/clothing/glasses/hud/health/mech/hud + + icon_scale_x = 1.2 + icon_scale_y = 1.2 + +/obj/mecha/medical/odysseus/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health/mech(src) + return + +/obj/mecha/medical/odysseus/moved_inside(var/mob/living/carbon/human/H as mob) + if(..()) + if(H.glasses) + occupant_message(span_red("[H.glasses] prevent you from using [src] [hud]!")) + else + H.glasses = hud + H.recalculate_vis() + return 1 + else + return 0 + +/obj/mecha/medical/odysseus/go_out() + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + if(H.glasses == hud) + H.glasses = null + H.recalculate_vis() + ..() + return +/* + verb/set_perspective() + set name = "Set client perspective." + set category = "Exosuit Interface" + set src = usr.loc + var/perspective = input(usr, "Select a perspective type.", + "Client perspective", + occupant.client.perspective) in list(MOB_PERSPECTIVE,EYE_PERSPECTIVE) + to_world("[perspective]") + occupant.client.perspective = perspective + return + + verb/toggle_eye() + set name = "Toggle eye." + set category = "Exosuit Interface" + set src = usr.loc + if(occupant.client.eye == occupant) + occupant.client.eye = src + else + occupant.client.eye = occupant + to_world("[occupant.client.eye]") + return +*/ + +//TODO - Check documentation for client.eye and client.perspective... +/obj/item/clothing/glasses/hud/health/mech + name = "Integrated Medical Hud" + + +// process_hud(var/mob/M) //TODO VIS +/* + to_world("view(M)") + for(var/mob/mob in view(M)) + to_world("[mob]") + to_world("view(M.client)") + for(var/mob/mob in view(M.client)) + to_world("[mob]") + to_world("view(M.loc)") + for(var/mob/mob in view(M.loc)) + to_world("[mob]") + + + if(!M || M.stat || !(M in view(M))) return + if(!M.client) return + var/client/C = M.client + var/image/holder + for(var/mob/living/carbon/human/patient in view(M.loc)) + if(M.see_invisible < patient.invisibility) + continue + var/foundVirus = 0 + + for (var/ID in patient.virus2) + if (ID in virusDB) + foundVirus = 1 + break + + holder = patient.hud_list[HEALTH_HUD] + if(patient.stat == DEAD) + holder.icon_state = "hudhealth-100" + C.images += holder + else + holder.icon_state = RoundHealth((patient.health-config.health_threshold_crit)/(patient.getMaxHealth()-config.health_threshold_crit)*100) + C.images += holder + + holder = patient.hud_list[STATUS_HUD] + if(patient.isSynthetic()) + holder.icon_state = "hudrobo" + else if(patient.stat == DEAD) + holder.icon_state = "huddead" + else if(foundVirus) + holder.icon_state = "hudill" + else if(patient.has_brain_worms()) + var/mob/living/simple_mob/animal/borer/B = patient.has_brain_worms() + if(B.controlling) + holder.icon_state = "hudbrainworm" + else + holder.icon_state = "hudhealthy" + else + holder.icon_state = "hudhealthy" + + C.images += holder +*/ +/obj/mecha/medical/odysseus/loaded/Initialize() + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun + ME.attach(src) + +//Meant for random spawns. +/obj/mecha/medical/odysseus/old + desc = "An aging combat exosuit utilized by many corporations. Originally developed to combat hostile alien lifeforms. This one is particularly worn looking and likely isn't as sturdy." + +/obj/mecha/medical/odysseus/old/New() + ..() + health = 25 + maxhealth = 50 //Just slightly worse. + cell.charge = rand(0, (cell.charge/2)) diff --git a/code/game/mecha/micro/mecha_construction_paths_vr.dm b/code/game/mecha/micro/mecha_construction_paths_vr.dm index 4117e2ca652..85ac01325bf 100644 --- a/code/game/mecha/micro/mecha_construction_paths_vr.dm +++ b/code/game/mecha/micro/mecha_construction_paths_vr.dm @@ -33,7 +33,7 @@ result = "/obj/mecha/micro/sec/polecat" steps = list( //1 - list("key"=/obj/item/weapon/weldingtool, + list("key"=IS_WELDER, "backkey"=IS_WRENCH, "desc"="External armor is wrenched."), //2 @@ -42,10 +42,10 @@ "desc"="External armor is installed."), //3 list("key"=/obj/item/mecha_parts/micro/part/polecat_armour, - "backkey"=/obj/item/weapon/weldingtool, + "backkey"=IS_WELDER, "desc"="Internal armor is welded."), //4 - list("key"=/obj/item/weapon/weldingtool, + list("key"=IS_WELDER, "backkey"=IS_WRENCH, "desc"="Internal armor is wrenched"), //5 @@ -312,7 +312,7 @@ result = "/obj/mecha/micro/utility/gopher" steps = list( //1 - list("key"=/obj/item/weapon/weldingtool, + list("key"=IS_WELDER, "backkey"=IS_WRENCH, "desc"="External armor is wrenched."), //2 @@ -321,10 +321,10 @@ "desc"="External armor is installed."), //3 list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, + "backkey"=IS_WELDER, "desc"="Internal armor is welded."), //4 - list("key"=/obj/item/weapon/weldingtool, + list("key"=IS_WELDER, "backkey"=IS_WRENCH, "desc"="Internal armor is wrenched"), //5 @@ -518,7 +518,7 @@ result = "/obj/mecha/micro/sec/weasel" steps = list( //1 - list("key"=/obj/item/weapon/weldingtool, + list("key"=IS_WELDER, "backkey"=IS_WRENCH, "desc"="External armor is wrenched."), //2 @@ -527,10 +527,10 @@ "desc"="External armor is installed."), //3 list("key"=/obj/item/stack/material/plasteel, - "backkey"=/obj/item/weapon/weldingtool, + "backkey"=IS_WELDER, "desc"="Internal armor is welded."), //4 - list("key"=/obj/item/weapon/weldingtool, + list("key"=IS_WELDER, "backkey"=IS_WRENCH, "desc"="Internal armor is wrenched"), //5 diff --git a/code/game/mecha/micro/micro.dm b/code/game/mecha/micro/micro.dm index d1e5aedf592..1f301ce5417 100644 --- a/code/game/mecha/micro/micro.dm +++ b/code/game/mecha/micro/micro.dm @@ -81,7 +81,7 @@ return M.updatehealth() src.occupant_message("You hit [target].") - src.visible_message("[src.name] hits [target].") + src.visible_message(span_red("[src.name] hits [target].")) else step_away(M,src) src.occupant_message("You push [target] out of the way.") @@ -97,7 +97,7 @@ for(var/target_type in src.destroyable_obj) if(istype(target, target_type) && hascall(target, "attackby")) src.occupant_message("You hit [target].") - src.visible_message("[src.name] hits [target]") + src.visible_message(span_red("[src.name] hits [target].")) if(!istype(target, /turf/simulated/wall)) target:attackby(src,src.occupant) else @@ -155,4 +155,3 @@ /obj/effect/decal/mecha_wreckage/micro icon = 'icons/mecha/micro.dmi' - diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 8d4ee98fbf9..eb5a53fbfaf 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -1,150 +1,150 @@ -/obj/mecha/working/ripley - desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world." - name = "APLU \"Ripley\"" - icon_state = "ripley" - initial_icon = "ripley" - step_in = 5 // vorestation edit, was 6 but that's PAINFULLY slow - step_energy_drain = 5 // vorestation edit because 10 drained a significant chunk of its cell before you even got out the airlock - max_temperature = 20000 - health = 200 - maxhealth = 200 //Don't forget to update the /old variant if you change this number. - wreckage = /obj/effect/decal/mecha_wreckage/ripley - cargo_capacity = 10 - var/obj/item/weapon/mining_scanner/orescanner // vorestation addition - - minimum_penetration = 10 - - encumbrance_gap = 2 - - starting_components = list( - /obj/item/mecha_parts/component/hull/durable, - /obj/item/mecha_parts/component/actuator, - /obj/item/mecha_parts/component/armor/mining, - /obj/item/mecha_parts/component/gas, - /obj/item/mecha_parts/component/electrical - ) - - icon_scale_x = 1.2 - icon_scale_y = 1.2 - -/obj/mecha/working/ripley/Move() - . = ..() - if(.) - collect_ore() - -/obj/mecha/working/ripley/proc/collect_ore() - if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in equipment) - var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo - if(ore_box) - for(var/obj/item/weapon/ore/ore in range(1, src)) - if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! - ore_box.stored_ore[ore.material]++ - qdel(ore) - -/obj/mecha/working/ripley/Destroy() - for(var/atom/movable/A in src.cargo) - A.loc = loc - var/turf/T = loc - if(istype(T)) - T.Entered(A) - step_rand(A) - cargo.Cut() - ..() - -/obj/mecha/working/ripley/firefighter - desc = "Standard APLU chassis was refitted with additional thermal protection and cistern." - name = "APLU \"Firefighter\"" - icon_state = "firefighter" - initial_icon = "firefighter" - max_temperature = 65000 - health = 250 - lights_power = 8 - wreckage = /obj/effect/decal/mecha_wreckage/ripley/firefighter - max_hull_equip = 2 - max_weapon_equip = 0 - max_utility_equip = 2 - max_universal_equip = 1 - max_special_equip = 1 - -/obj/mecha/working/ripley/deathripley - desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" - name = "DEATH-RIPLEY" - icon_state = "deathripley" - initial_icon = "deathripley" - step_in = 2 - opacity=0 - lights_power = 60 - wreckage = /obj/effect/decal/mecha_wreckage/ripley/deathripley - step_energy_drain = 0 - max_hull_equip = 1 - max_weapon_equip = 1 - max_utility_equip = 3 - max_universal_equip = 1 - max_special_equip = 1 - -/obj/mecha/working/ripley/deathripley/Initialize() - . = ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/safety - ME.attach(src) - return - -/obj/mecha/working/ripley/mining - desc = "An old, dusty mining ripley." - name = "APLU \"Miner\"" - -/obj/mecha/working/ripley/mining/Initialize() - . = ..() - //Attach drill - if(prob(25)) //Possible diamond drill... Feeling lucky? - var/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill - D.attach(src) - else - var/obj/item/mecha_parts/mecha_equipment/tool/drill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill - D.attach(src) - - //Attach hydrolic clamp - var/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp - HC.attach(src) - for(var/obj/item/mecha_parts/mecha_tracking/B in src.contents)//Deletes the beacon so it can't be found easily - qdel (B) - -/obj/mecha/working/ripley/antique - name = "APLU \"Geiger\"" - desc = "You can't beat the classics." - icon_state = "ripley-old" - initial_icon = "ripley-old" - - show_pilot = TRUE - pilot_lift = 5 - - max_utility_equip = 1 - max_universal_equip = 3 - - icon_scale_x = 1 - icon_scale_y = 1 - -//Vorestation Edit Start - -/obj/mecha/working/ripley/New() - ..() - orescanner = new /obj/item/weapon/mining_scanner - -/obj/mecha/working/ripley/verb/detect_ore() - set category = "Exosuit Interface" - set name = "Detect Ores" - set src = usr.loc - set popup_menu = 0 - - orescanner.attack_self(usr) - -//Vorestation Edit End - -//Meant for random spawns. -/obj/mecha/working/ripley/mining/old - desc = "An old, dusty mining ripley." - -/obj/mecha/working/ripley/mining/old/New() - ..() - health = 25 - maxhealth = 190 //Just slightly worse. - cell.charge = rand(0, cell.charge) +/obj/mecha/working/ripley + desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world." + name = "APLU \"Ripley\"" + icon_state = "ripley" + initial_icon = "ripley" + step_in = 5 // vorestation edit, was 6 but that's PAINFULLY slow + step_energy_drain = 5 // vorestation edit because 10 drained a significant chunk of its cell before you even got out the airlock + max_temperature = 20000 + health = 200 + maxhealth = 200 //Don't forget to update the /old variant if you change this number. + wreckage = /obj/effect/decal/mecha_wreckage/ripley + cargo_capacity = 10 + var/obj/item/weapon/mining_scanner/orescanner // vorestation addition + + minimum_penetration = 10 + + encumbrance_gap = 2 + + starting_components = list( + /obj/item/mecha_parts/component/hull/durable, + /obj/item/mecha_parts/component/actuator, + /obj/item/mecha_parts/component/armor/mining, + /obj/item/mecha_parts/component/gas, + /obj/item/mecha_parts/component/electrical + ) + + icon_scale_x = 1.2 + icon_scale_y = 1.2 + +/obj/mecha/working/ripley/Move() + . = ..() + if(.) + collect_ore() + +/obj/mecha/working/ripley/proc/collect_ore() + if(locate(/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp) in equipment) + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo + if(ore_box) + for(var/obj/item/weapon/ore/ore in range(1, src)) + if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! + ore_box.stored_ore[ore.material]++ + qdel(ore) + +/obj/mecha/working/ripley/Destroy() + for(var/atom/movable/A in src.cargo) + A.loc = loc + var/turf/T = loc + if(istype(T)) + T.Entered(A) + step_rand(A) + cargo.Cut() + ..() + +/obj/mecha/working/ripley/firefighter + desc = "Standard APLU chassis was refitted with additional thermal protection and cistern." + name = "APLU \"Firefighter\"" + icon_state = "firefighter" + initial_icon = "firefighter" + max_temperature = 65000 + health = 250 + lights_power = 8 + wreckage = /obj/effect/decal/mecha_wreckage/ripley/firefighter + max_hull_equip = 2 + max_weapon_equip = 0 + max_utility_equip = 2 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/ripley/deathripley + desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" + name = "DEATH-RIPLEY" + icon_state = "deathripley" + initial_icon = "deathripley" + step_in = 2 + opacity=0 + lights_power = 60 + wreckage = /obj/effect/decal/mecha_wreckage/ripley/deathripley + step_energy_drain = 0 + max_hull_equip = 1 + max_weapon_equip = 1 + max_utility_equip = 3 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/ripley/deathripley/Initialize() + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/safety + ME.attach(src) + return + +/obj/mecha/working/ripley/mining + desc = "An old, dusty mining ripley." + name = "APLU \"Miner\"" + +/obj/mecha/working/ripley/mining/Initialize() + . = ..() + //Attach drill + if(prob(25)) //Possible diamond drill... Feeling lucky? + var/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill + D.attach(src) + else + var/obj/item/mecha_parts/mecha_equipment/tool/drill/D = new /obj/item/mecha_parts/mecha_equipment/tool/drill + D.attach(src) + + //Attach hydrolic clamp + var/obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp + HC.attach(src) + for(var/obj/item/mecha_parts/mecha_tracking/B in src.contents)//Deletes the beacon so it can't be found easily + qdel (B) + +/obj/mecha/working/ripley/antique + name = "APLU \"Geiger\"" + desc = "You can't beat the classics." + icon_state = "ripley-old" + initial_icon = "ripley-old" + + show_pilot = TRUE + pilot_lift = 5 + + max_utility_equip = 1 + max_universal_equip = 3 + + icon_scale_x = 1 + icon_scale_y = 1 + +//Vorestation Edit Start + +/obj/mecha/working/ripley/New() + ..() + orescanner = new /obj/item/weapon/mining_scanner + +/obj/mecha/working/ripley/verb/detect_ore() + set category = "Exosuit Interface" + set name = "Detect Ores" + set src = usr.loc + set popup_menu = 0 + + orescanner.attack_self(usr) + +//Vorestation Edit End + +//Meant for random spawns. +/obj/mecha/working/ripley/mining/old + desc = "An old, dusty mining ripley." + +/obj/mecha/working/ripley/mining/old/New() + ..() + health = 25 + maxhealth = 190 //Just slightly worse. + cell.charge = rand(0, cell.charge) diff --git a/code/game/mecha/working/working.dm b/code/game/mecha/working/working.dm index f2149b1cdd0..6d19bbd6b57 100644 --- a/code/game/mecha/working/working.dm +++ b/code/game/mecha/working/working.dm @@ -1,63 +1,63 @@ -/obj/mecha/working - internal_damage_threshold = 60 - max_hull_equip = 1 - max_weapon_equip = 0 - max_utility_equip = 3 - max_universal_equip = 1 - max_special_equip = 1 - -/obj/mecha/working/Initialize() - . = ..() - var/turf/T = get_turf(src) - if(isPlayerLevel(T.z)) - new /obj/item/mecha_parts/mecha_tracking(src) - -/* This stuff has been generalized! -/obj/mecha/working/Destroy() - for(var/mob/M in src) - if(M==src.occupant) - continue - M.loc = get_turf(src) - M.loc.Entered(M) - step_rand(M) - for(var/atom/movable/A in src.cargo) - A.loc = get_turf(src) - var/turf/T = get_turf(A) - if(T) - T.Entered(A) - step_rand(A) - ..() - return - -/obj/mecha/working/Topic(href, href_list) - ..() - if(href_list["drop_from_cargo"]) - var/obj/O = locate(href_list["drop_from_cargo"]) - if(O && O in src.cargo) - src.occupant_message("You unload [O].") - O.loc = get_turf(src) - src.cargo -= O - var/turf/T = get_turf(O) - if(T) - T.Entered(O) - src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") - return - -/obj/mecha/working/Exit(atom/movable/O) - if(O in cargo) - return 0 - return ..() - -/obj/mecha/working/get_stats_part() - var/output = ..() - output += "Cargo Compartment Contents:
                    " - if(src.cargo.len) - for(var/obj/O in src.cargo) - output += "Unload : [O]
                    " - else - output += "Nothing" - output += "
                    " - return output -*/ -/obj/mecha/working/range_action(atom/target as obj|mob|turf) - return +/obj/mecha/working + internal_damage_threshold = 60 + max_hull_equip = 1 + max_weapon_equip = 0 + max_utility_equip = 3 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/working/Initialize() + . = ..() + var/turf/T = get_turf(src) + if(isPlayerLevel(T.z)) + new /obj/item/mecha_parts/mecha_tracking(src) + +/* This stuff has been generalized! +/obj/mecha/working/Destroy() + for(var/mob/M in src) + if(M==src.occupant) + continue + M.loc = get_turf(src) + M.loc.Entered(M) + step_rand(M) + for(var/atom/movable/A in src.cargo) + A.loc = get_turf(src) + var/turf/T = get_turf(A) + if(T) + T.Entered(A) + step_rand(A) + ..() + return + +/obj/mecha/working/Topic(href, href_list) + ..() + if(href_list["drop_from_cargo"]) + var/obj/O = locate(href_list["drop_from_cargo"]) + if(O && O in src.cargo) + src.occupant_message("You unload [O].") + O.loc = get_turf(src) + src.cargo -= O + var/turf/T = get_turf(O) + if(T) + T.Entered(O) + src.log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - src.cargo.len]") + return + +/obj/mecha/working/Exit(atom/movable/O) + if(O in cargo) + return 0 + return ..() + +/obj/mecha/working/get_stats_part() + var/output = ..() + output += "Cargo Compartment Contents:
                    " + if(src.cargo.len) + for(var/obj/O in src.cargo) + output += "Unload : [O]
                    " + else + output += "Nothing" + output += "
                    " + return output +*/ +/obj/mecha/working/range_action(atom/target as obj|mob|turf) + return diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 5e6f0165615..38893adcbaa 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -1,208 +1,208 @@ - - -/atom/movable - var/can_buckle = FALSE - var/buckle_movable = 0 - var/buckle_dir = 0 - var/buckle_lying = -1 //bed-like behavior, forces mob.lying = buckle_lying if != -1 - var/buckle_require_restraints = 0 //require people to be handcuffed before being able to buckle. eg: pipes -// var/mob/living/buckled_mob = null - var/list/mob/living/buckled_mobs = null //list() - var/max_buckled_mobs = 1 - - -/atom/movable/attack_hand(mob/living/user) - . = ..() -// if(can_buckle && buckled_mob) -// user_unbuckle_mob(user) - - if(can_buckle && has_buckled_mobs()) - if(buckled_mobs.len > 1) - var/unbuckled = tgui_input_list(user, "Who do you wish to unbuckle?","Unbuckle Who?", buckled_mobs) - if(user_unbuckle_mob(unbuckled, user)) - return TRUE - else - if(user_unbuckle_mob(buckled_mobs[1], user)) - return TRUE - -/obj/proc/attack_alien(mob/user as mob) //For calling in the event of Xenomorph or other alien checks. - return - -/obj/attack_robot(mob/living/user) - if(Adjacent(user) && has_buckled_mobs()) //Checks if what we're touching is adjacent to us and has someone buckled to it. This should prevent interacting with anti-robot manual valves among other things. - return attack_hand(user) //Process as if we're a normal person touching the object. - return ..() //Otherwise, treat this as an AI click like usual. - -/atom/movable/MouseDrop_T(mob/living/M, mob/living/user) - . = ..() - if(can_buckle && istype(M)) - if(user_buckle_mob(M, user)) - return TRUE - -/atom/movable/proc/has_buckled_mobs() - return LAZYLEN(buckled_mobs) - -/atom/movable/Destroy() - unbuckle_all_mobs() - return ..() - - -/atom/movable/proc/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE) - if(check_loc && M.loc != loc) - return FALSE - - if(!can_buckle_check(M, forced)) - return FALSE - - if(M == src) - stack_trace("Recursive buckle warning: [M] being buckled to self.") - return - - M.buckled = src - M.facing_dir = null - M.set_dir(buckle_dir ? buckle_dir : dir) - M.update_canmove() - M.update_floating( M.Check_Dense_Object() ) -// buckled_mob = M - buckled_mobs |= M - - //VOREStation Add - if(riding_datum) - riding_datum.ridden = src - riding_datum.handle_vehicle_offsets() - M.update_water() - //VOREStation Add End - - post_buckle_mob(M) - M.throw_alert("buckled", /obj/screen/alert/restrained/buckled, new_master = src) - return TRUE - -/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force = FALSE) - if(!buckled_mob) // If we didn't get told which mob needs to get unbuckled, just assume its the first one on the list. - if(has_buckled_mobs()) - buckled_mob = buckled_mobs[1] - else - return - - if(buckled_mob && buckled_mob.buckled == src) - . = buckled_mob - buckled_mob.buckled = null - buckled_mob.anchored = initial(buckled_mob.anchored) - buckled_mob.update_canmove() - buckled_mob.update_floating( buckled_mob.Check_Dense_Object() ) - buckled_mob.clear_alert("buckled") - // buckled_mob = null - buckled_mobs -= buckled_mob - - //VOREStation Add - buckled_mob.update_water() - if(riding_datum) - riding_datum.restore_position(buckled_mob) - riding_datum.handle_vehicle_offsets() // So the person in back goes to the front. - //VOREStation Add End - post_buckle_mob(.) - -/atom/movable/proc/unbuckle_all_mobs(force = FALSE) - if(!has_buckled_mobs()) - return - for(var/m in buckled_mobs) - unbuckle_mob(m, force) - -//Handle any extras after buckling/unbuckling -//Called on buckle_mob() and unbuckle_mob() -/atom/movable/proc/post_buckle_mob(mob/living/M) - return - -//Wrapper procs that handle sanity and user feedback -/atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, var/forced = FALSE, var/silent = FALSE) - if(!ticker) - to_chat(user, "You can't buckle anyone in before the game starts.") - return FALSE // Is this really needed? - if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai)) - return FALSE - if(M in buckled_mobs) - to_chat(user, "\The [M] is already buckled to \the [src].") - return FALSE - if(!can_buckle_check(M, forced)) - return FALSE - - add_fingerprint(user) -// unbuckle_mob() - - //can't buckle unless you share locs so try to move M to the obj. - if(M.loc != src.loc) - if(M.Adjacent(src) && user.Adjacent(src)) - M.forceMove(get_turf(src)) - // step_towards(M, src) - - . = buckle_mob(M, forced) - playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) - if(.) - var/reveal_message = list("buckled_mob" = null, "buckled_to" = null) //VORE EDIT: This being a list and messages existing for the buckle target atom. - if(!silent) - if(M == user) - reveal_message["buckled_mob"] = "You come out of hiding and buckle yourself to [src]." //VORE EDIT - reveal_message["buckled_to"] = "You come out of hiding as [M.name] buckles themselves to you." //VORE EDIT - M.visible_message(\ - "[M.name] buckles themselves to [src].",\ - "You buckle yourself to [src].",\ - "You hear metal clanking.") - else - reveal_message["buckled_mob"] = "You are revealed as you are buckled to [src]." //VORE EDIT - reveal_message["buckled_to"] = "You are revealed as [M.name] is buckled to you." //VORE EDIT - M.visible_message(\ - "[M.name] is buckled to [src] by [user.name]!",\ - "You are buckled to [src] by [user.name]!",\ - "You hear metal clanking.") - - M.reveal(silent, reveal_message["buckled_mob"]) //Reveal people so they aren't buckled to chairs from behind. //VORE EDIT, list arg instead of simple message var for buckled mob - //Vore edit start - var/mob/living/L = src - if(istype(L)) - L.reveal(silent, reveal_message["buckled_to"]) - //Vore edit end - -/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user) - var/mob/living/M = unbuckle_mob(buckled_mob) - playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) - if(M) - if(M != user) - M.visible_message(\ - "[M.name] was unbuckled by [user.name]!",\ - "You were unbuckled from [src] by [user.name].",\ - "You hear metal clanking.") - else - M.visible_message(\ - "[M.name] unbuckled themselves!",\ - "You unbuckle yourself from [src].",\ - "You hear metal clanking.") - add_fingerprint(user) - return M - -/atom/movable/proc/handle_buckled_mob_movement(atom/old_loc, direct, movetime) - for(var/mob/living/L as anything in buckled_mobs) - if(!L.Move(loc, direct, movetime)) - L.forceMove(loc, direct, movetime) - L.last_move = last_move - L.inertia_dir = last_move - - if(!buckle_dir) - L.set_dir(dir) - else - L.set_dir(buckle_dir) - -/atom/movable/proc/can_buckle_check(mob/living/M, forced = FALSE) - if(!buckled_mobs) - buckled_mobs = list() - - if(!istype(M)) - return FALSE - - if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained())) - return FALSE - - if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it - to_chat(M, "\The [src] can't buckle anymore people.") - return FALSE - - return TRUE + + +/atom/movable + var/can_buckle = FALSE + var/buckle_movable = 0 + var/buckle_dir = 0 + var/buckle_lying = -1 //bed-like behavior, forces mob.lying = buckle_lying if != -1 + var/buckle_require_restraints = 0 //require people to be handcuffed before being able to buckle. eg: pipes +// var/mob/living/buckled_mob = null + var/list/mob/living/buckled_mobs = null //list() + var/max_buckled_mobs = 1 + + +/atom/movable/attack_hand(mob/living/user) + . = ..() +// if(can_buckle && buckled_mob) +// user_unbuckle_mob(user) + + if(can_buckle && has_buckled_mobs()) + if(buckled_mobs.len > 1) + var/unbuckled = tgui_input_list(user, "Who do you wish to unbuckle?","Unbuckle Who?", buckled_mobs) + if(user_unbuckle_mob(unbuckled, user)) + return TRUE + else + if(user_unbuckle_mob(buckled_mobs[1], user)) + return TRUE + +/obj/proc/attack_alien(mob/user as mob) //For calling in the event of Xenomorph or other alien checks. + return + +/obj/attack_robot(mob/living/user) + if(Adjacent(user) && has_buckled_mobs()) //Checks if what we're touching is adjacent to us and has someone buckled to it. This should prevent interacting with anti-robot manual valves among other things. + return attack_hand(user) //Process as if we're a normal person touching the object. + return ..() //Otherwise, treat this as an AI click like usual. + +/atom/movable/MouseDrop_T(mob/living/M, mob/living/user) + . = ..() + if(can_buckle && istype(M)) + if(user_buckle_mob(M, user)) + return TRUE + +/atom/movable/proc/has_buckled_mobs() + return LAZYLEN(buckled_mobs) + +/atom/movable/Destroy() + unbuckle_all_mobs() + return ..() + + +/atom/movable/proc/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE) + if(check_loc && M.loc != loc) + return FALSE + + if(!can_buckle_check(M, forced)) + return FALSE + + if(M == src) + stack_trace("Recursive buckle warning: [M] being buckled to self.") + return + + M.buckled = src + M.facing_dir = null + M.set_dir(buckle_dir ? buckle_dir : dir) + M.update_canmove() + M.update_floating( M.Check_Dense_Object() ) +// buckled_mob = M + buckled_mobs |= M + + //VOREStation Add + if(riding_datum) + riding_datum.ridden = src + riding_datum.handle_vehicle_offsets() + M.update_water() + //VOREStation Add End + + post_buckle_mob(M) + M.throw_alert("buckled", /obj/screen/alert/restrained/buckled, new_master = src) + return TRUE + +/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force = FALSE) + if(!buckled_mob) // If we didn't get told which mob needs to get unbuckled, just assume its the first one on the list. + if(has_buckled_mobs()) + buckled_mob = buckled_mobs[1] + else + return + + if(buckled_mob && buckled_mob.buckled == src) + . = buckled_mob + buckled_mob.buckled = null + buckled_mob.anchored = initial(buckled_mob.anchored) + buckled_mob.update_canmove() + buckled_mob.update_floating( buckled_mob.Check_Dense_Object() ) + buckled_mob.clear_alert("buckled") + // buckled_mob = null + buckled_mobs -= buckled_mob + + //VOREStation Add + buckled_mob.update_water() + if(riding_datum) + riding_datum.restore_position(buckled_mob) + riding_datum.handle_vehicle_offsets() // So the person in back goes to the front. + //VOREStation Add End + post_buckle_mob(.) + +/atom/movable/proc/unbuckle_all_mobs(force = FALSE) + if(!has_buckled_mobs()) + return + for(var/m in buckled_mobs) + unbuckle_mob(m, force) + +//Handle any extras after buckling/unbuckling +//Called on buckle_mob() and unbuckle_mob() +/atom/movable/proc/post_buckle_mob(mob/living/M) + return + +//Wrapper procs that handle sanity and user feedback +/atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, var/forced = FALSE, var/silent = FALSE) + if(!ticker) + to_chat(user, "You can't buckle anyone in before the game starts.") + return FALSE // Is this really needed? + if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai)) + return FALSE + if(M in buckled_mobs) + to_chat(user, "\The [M] is already buckled to \the [src].") + return FALSE + if(!can_buckle_check(M, forced)) + return FALSE + + add_fingerprint(user) +// unbuckle_mob() + + //can't buckle unless you share locs so try to move M to the obj. + if(M.loc != src.loc) + if(M.Adjacent(src) && user.Adjacent(src)) + M.forceMove(get_turf(src)) + // step_towards(M, src) + + . = buckle_mob(M, forced) + playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) + if(.) + var/reveal_message = list("buckled_mob" = null, "buckled_to" = null) //VORE EDIT: This being a list and messages existing for the buckle target atom. + if(!silent) + if(M == user) + reveal_message["buckled_mob"] = "You come out of hiding and buckle yourself to [src]." //VORE EDIT + reveal_message["buckled_to"] = "You come out of hiding as [M.name] buckles themselves to you." //VORE EDIT + M.visible_message(\ + "[M.name] buckles themselves to [src].",\ + "You buckle yourself to [src].",\ + "You hear metal clanking.") + else + reveal_message["buckled_mob"] = "You are revealed as you are buckled to [src]." //VORE EDIT + reveal_message["buckled_to"] = "You are revealed as [M.name] is buckled to you." //VORE EDIT + M.visible_message(\ + "[M.name] is buckled to [src] by [user.name]!",\ + "You are buckled to [src] by [user.name]!",\ + "You hear metal clanking.") + + M.reveal(silent, reveal_message["buckled_mob"]) //Reveal people so they aren't buckled to chairs from behind. //VORE EDIT, list arg instead of simple message var for buckled mob + //Vore edit start + var/mob/living/L = src + if(istype(L)) + L.reveal(silent, reveal_message["buckled_to"]) + //Vore edit end + +/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user) + var/mob/living/M = unbuckle_mob(buckled_mob) + playsound(src, 'sound/effects/seatbelt.ogg', 50, 1) + if(M) + if(M != user) + M.visible_message(\ + "[M.name] was unbuckled by [user.name]!",\ + "You were unbuckled from [src] by [user.name].",\ + "You hear metal clanking.") + else + M.visible_message(\ + "[M.name] unbuckled themselves!",\ + "You unbuckle yourself from [src].",\ + "You hear metal clanking.") + add_fingerprint(user) + return M + +/atom/movable/proc/handle_buckled_mob_movement(atom/old_loc, direct, movetime) + for(var/mob/living/L as anything in buckled_mobs) + if(!L.Move(loc, direct, movetime)) + L.forceMove(loc, direct, movetime) + L.last_move = last_move + L.inertia_dir = last_move + + if(!buckle_dir) + L.set_dir(dir) + else + L.set_dir(buckle_dir) + +/atom/movable/proc/can_buckle_check(mob/living/M, forced = FALSE) + if(!buckled_mobs) + buckled_mobs = list() + + if(!istype(M)) + return FALSE + + if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained())) + return FALSE + + if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it + to_chat(M, "\The [src] can't buckle anymore people.") + return FALSE + + return TRUE diff --git a/code/game/objects/effects/alien/aliens.dm b/code/game/objects/effects/alien/aliens.dm index 84b415506d0..b7a40d48195 100644 --- a/code/game/objects/effects/alien/aliens.dm +++ b/code/game/objects/effects/alien/aliens.dm @@ -192,8 +192,8 @@ var/damage = W.force / 4.0 - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(WT.remove_fuel(0, user)) damage = 15 diff --git a/code/game/objects/effects/bump_teleporter.dm b/code/game/objects/effects/bump_teleporter.dm index 581aeea16f8..5c579e7925c 100644 --- a/code/game/objects/effects/bump_teleporter.dm +++ b/code/game/objects/effects/bump_teleporter.dm @@ -1,34 +1,34 @@ -var/list/obj/effect/bump_teleporter/BUMP_TELEPORTERS = list() - -/obj/effect/bump_teleporter - name = "bump-teleporter" - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - var/id = null //id of this bump_teleporter. - var/id_target = null //id of bump_teleporter which this moves you to. - invisibility = 101 //nope, can't see this - anchored = TRUE - density = TRUE - opacity = 0 - -/obj/effect/bump_teleporter/New() - ..() - BUMP_TELEPORTERS += src - -/obj/effect/bump_teleporter/Destroy() - BUMP_TELEPORTERS -= src - return ..() - -/obj/effect/bump_teleporter/Bumped(atom/user) - if(!ismob(user)) - //user.loc = src.loc //Stop at teleporter location - return - var/mob/M = user //VOREStation edit - if(!id_target) - //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. - return - - for(var/obj/effect/bump_teleporter/BT in BUMP_TELEPORTERS) - if(BT.id == src.id_target) - M.forceMove(BT.loc) //Teleport to location with correct id. //VOREStation Edit - return +var/list/obj/effect/bump_teleporter/BUMP_TELEPORTERS = list() + +/obj/effect/bump_teleporter + name = "bump-teleporter" + icon = 'icons/mob/screen1.dmi' + icon_state = "x2" + var/id = null //id of this bump_teleporter. + var/id_target = null //id of bump_teleporter which this moves you to. + invisibility = 101 //nope, can't see this + anchored = TRUE + density = TRUE + opacity = 0 + +/obj/effect/bump_teleporter/New() + ..() + BUMP_TELEPORTERS += src + +/obj/effect/bump_teleporter/Destroy() + BUMP_TELEPORTERS -= src + return ..() + +/obj/effect/bump_teleporter/Bumped(atom/user) + if(!ismob(user)) + //user.loc = src.loc //Stop at teleporter location + return + var/mob/M = user //VOREStation edit + if(!id_target) + //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. + return + + for(var/obj/effect/bump_teleporter/BT in BUMP_TELEPORTERS) + if(BT.id == src.id_target) + M.forceMove(BT.loc) //Teleport to location with correct id. //VOREStation Edit + return diff --git a/code/game/objects/effects/decals/Cleanable/aliens.dm b/code/game/objects/effects/decals/Cleanable/aliens.dm index 1df3acb2b73..57a32434af2 100644 --- a/code/game/objects/effects/decals/Cleanable/aliens.dm +++ b/code/game/objects/effects/decals/Cleanable/aliens.dm @@ -1,33 +1,33 @@ -/obj/effect/decal/cleanable/blood/xeno - name = "xeno blood" - desc = "It's green and acidic. It looks like... blood?" - icon = 'icons/effects/blood.dmi' - basecolor = "#05EE05" - -/obj/effect/decal/cleanable/blood/gibs/xeno - name = "xeno gibs" - desc = "Gnarly..." - icon_state = "xgib1" - random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6") - basecolor = "#05EE05" - -/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() - color = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/xeno/up - random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibup1","xgibup1","xgibup1") - -/obj/effect/decal/cleanable/blood/gibs/xeno/down - random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibdown1","xgibdown1","xgibdown1") - -/obj/effect/decal/cleanable/blood/gibs/xeno/body - random_icon_states = list("xgibhead", "xgibtorso") - -/obj/effect/decal/cleanable/blood/gibs/xeno/limb - random_icon_states = list("xgibleg", "xgibarm") - -/obj/effect/decal/cleanable/blood/gibs/xeno/core - random_icon_states = list("xgibmid1", "xgibmid2", "xgibmid3") - -/obj/effect/decal/cleanable/blood/xtracks +/obj/effect/decal/cleanable/blood/xeno + name = "xeno blood" + desc = "It's green and acidic. It looks like... blood?" + icon = 'icons/effects/blood.dmi' + basecolor = "#05EE05" + +/obj/effect/decal/cleanable/blood/gibs/xeno + name = "xeno gibs" + desc = "Gnarly..." + icon_state = "xgib1" + random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6") + basecolor = "#05EE05" + +/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() + color = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/xeno/up + random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibup1","xgibup1","xgibup1") + +/obj/effect/decal/cleanable/blood/gibs/xeno/down + random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6","xgibdown1","xgibdown1","xgibdown1") + +/obj/effect/decal/cleanable/blood/gibs/xeno/body + random_icon_states = list("xgibhead", "xgibtorso") + +/obj/effect/decal/cleanable/blood/gibs/xeno/limb + random_icon_states = list("xgibleg", "xgibarm") + +/obj/effect/decal/cleanable/blood/gibs/xeno/core + random_icon_states = list("xgibmid1", "xgibmid2", "xgibmid3") + +/obj/effect/decal/cleanable/blood/xtracks basecolor = "#05EE05" \ No newline at end of file diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 73669120381..1e3393c8577 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -1,253 +1,253 @@ -#define DRYING_TIME 5 * 60*10 //for 1 unit of depth in puddle (amount var) - -var/global/list/image/splatter_cache=list() - -/obj/effect/decal/cleanable/blood - name = "blood" - var/dryname = "dried blood" - desc = "It's thick and gooey. Perhaps it's the chef's cooking?" - var/drydesc = "It's dry and crusty. Someone is not doing their job." - gender = PLURAL - density = FALSE - anchored = TRUE - plane = BLOOD_PLANE - layer = BLOOD_DECAL_LAYER - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" - random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - var/base_icon = 'icons/effects/blood.dmi' - blood_DNA = list() - var/basecolor="#A10808" // Color when wet. - var/synthblood = 0 - var/list/datum/disease2/disease/virus2 = list() - var/amount = 5 - generic_filth = TRUE - persistent = FALSE - -/obj/effect/decal/cleanable/blood/reveal_blood() - if(!fluorescent) - fluorescent = 1 - basecolor = COLOR_LUMINOL - update_icon() - -/obj/effect/decal/cleanable/blood/clean_blood() - fluorescent = 0 - if(invisibility != 100) - invisibility = 100 - amount = 0 - ..(ignore=1) - -/obj/effect/decal/cleanable/blood/New() - ..() - update_icon() - if(istype(src, /obj/effect/decal/cleanable/blood/gibs)) - return - if(src.type == /obj/effect/decal/cleanable/blood) - if(src.loc && isturf(src.loc)) - for(var/obj/effect/decal/cleanable/blood/B in src.loc) - if(B != src) - if (B.blood_DNA) - blood_DNA |= B.blood_DNA.Copy() - qdel(B) - addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1)) - -/obj/effect/decal/cleanable/blood/update_icon() - if(basecolor == "rainbow") basecolor = get_random_colour(1) - color = basecolor - - if(basecolor == SYNTH_BLOOD_COLOUR) - name = "oil" - desc = "It's quite oily." - else if(synthblood) - name = "synthetic blood" - desc = "It's quite greasy." - else - name = initial(name) - desc = initial(desc) - -/obj/effect/decal/cleanable/blood/Crossed(mob/living/carbon/human/perp) - if(perp.is_incorporeal()) - return - if (!istype(perp)) - return - if(amount < 1) - return - - var/obj/item/organ/external/l_foot = perp.get_organ("l_foot") - var/obj/item/organ/external/r_foot = perp.get_organ("r_foot") - var/hasfeet = 1 - if((!l_foot || l_foot.is_stump()) && (!r_foot || r_foot.is_stump())) - hasfeet = 0 - if(perp.shoes && !perp.buckled)//Adding blood to shoes - var/obj/item/clothing/shoes/S = perp.shoes - if(istype(S)) - S.blood_color = basecolor - S.track_blood = max(amount,S.track_blood) - if(!S.blood_overlay) - S.generate_blood_overlay() - if(!S.blood_DNA) - S.blood_DNA = list() - S.blood_overlay.color = basecolor - S.add_overlay(S.blood_overlay) - if(S.blood_overlay && S.blood_overlay.color != basecolor) - S.blood_overlay.color = basecolor - S.add_overlay(S.blood_overlay) - S.blood_DNA |= blood_DNA.Copy() - perp.update_inv_shoes() - - else if (hasfeet)//Or feet - perp.feet_blood_color = basecolor - perp.track_blood = max(amount,perp.track_blood) - LAZYINITLIST(perp.feet_blood_DNA) - perp.feet_blood_DNA |= blood_DNA.Copy() - perp.update_bloodied() - else if (perp.buckled && istype(perp.buckled, /obj/structure/bed/chair/wheelchair)) - var/obj/structure/bed/chair/wheelchair/W = perp.buckled - W.bloodiness = 4 - - amount-- - -/obj/effect/decal/cleanable/blood/proc/dry() - name = dryname - desc = drydesc - color = adjust_brightness(color, -50) - amount = 0 - -/obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user) - ..() - if (amount && istype(user)) - add_fingerprint(user) - if (user.gloves) - return - var/taken = rand(1,amount) - amount -= taken - to_chat(user, "You get some of \the [src] on your hands.") - if (!user.blood_DNA) - user.blood_DNA = list() - user.blood_DNA |= blood_DNA.Copy() - user.bloody_hands += taken - user.hand_blood_color = basecolor - user.update_inv_gloves(1) - user.verbs += /mob/living/carbon/human/proc/bloody_doodle - -/obj/effect/decal/cleanable/blood/splatter - random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") - amount = 2 - -/obj/effect/decal/cleanable/blood/drip - name = "drips of blood" - desc = "It's red." - gender = PLURAL - icon = 'icons/effects/drip.dmi' - icon_state = "1" - random_icon_states = list("1","2","3","4","5") - amount = 0 - var/list/drips = list() - -/obj/effect/decal/cleanable/blood/drip/New() - ..() - drips |= icon_state - -/obj/effect/decal/cleanable/blood/writing - icon_state = "tracks" - desc = "It looks like a writing in blood." - gender = NEUTER - random_icon_states = list("writing1","writing2","writing3","writing4","writing5") - amount = 0 - var/message - -/obj/effect/decal/cleanable/blood/writing/New() - ..() - if(random_icon_states.len) - for(var/obj/effect/decal/cleanable/blood/writing/W in loc) - random_icon_states.Remove(W.icon_state) - icon_state = pick(random_icon_states) - else - icon_state = "writing1" - -/obj/effect/decal/cleanable/blood/writing/examine(mob/user) - . = ..() - . += "It reads: \"[message]\"" - -/obj/effect/decal/cleanable/blood/gibs - name = "gibs" - desc = "They look bloody and gruesome." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "gibbl5" - random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6") - var/fleshcolor = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/update_icon() - - var/image/giblets = new(base_icon, "[icon_state]_flesh", dir) - if(!fleshcolor || fleshcolor == "rainbow") - fleshcolor = get_random_colour(1) - giblets.color = fleshcolor - - var/icon/blood = new(base_icon,"[icon_state]",dir) - if(basecolor == "rainbow") basecolor = get_random_colour(1) - blood.Blend(basecolor,ICON_MULTIPLY) - - icon = blood - cut_overlays() - add_overlay(giblets) - -/obj/effect/decal/cleanable/blood/gibs/up - random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibup1","gibup1","gibup1") - -/obj/effect/decal/cleanable/blood/gibs/down - random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibdown1","gibdown1","gibdown1") - -/obj/effect/decal/cleanable/blood/gibs/body - random_icon_states = list("gibhead", "gibtorso") - -/obj/effect/decal/cleanable/blood/gibs/limb - random_icon_states = list("gibleg", "gibarm") - -/obj/effect/decal/cleanable/blood/gibs/core - random_icon_states = list("gibmid1", "gibmid2", "gibmid3") - - -/obj/effect/decal/cleanable/blood/gibs/proc/streak(var/list/directions) - spawn (0) - var/direction = pick(directions) - for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) - sleep(3) - if (i > 0) - var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(src.loc) - b.basecolor = src.basecolor - b.update_icon() - - if (step_to(src, get_step(src, direction), 0)) - break - - -/obj/effect/decal/cleanable/mucus - name = "mucus" - desc = "Disgusting mucus." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "mucus" - random_icon_states = list("mucus") - - var/list/datum/disease2/disease/virus2 = list() - var/dry = 0 // Keeps the lag down - -/obj/effect/decal/cleanable/mucus/Initialize() - . = ..() - VARSET_IN(src, dry, TRUE, DRYING_TIME * 2) - -//This version should be used for admin spawns and pre-mapped virus vectors (e.g. in PoIs), this version does not dry -/obj/effect/decal/cleanable/mucus/mapped/Initialize() - . = ..() - virus2 |= new /datum/disease2/disease - virus2[1].makerandom() - -/obj/effect/decal/cleanable/mucus/mapped/Destroy() - virus2.Cut() - return ..() +#define DRYING_TIME 5 * 60*10 //for 1 unit of depth in puddle (amount var) + +var/global/list/image/splatter_cache=list() + +/obj/effect/decal/cleanable/blood + name = "blood" + var/dryname = "dried blood" + desc = "It's thick and gooey. Perhaps it's the chef's cooking?" + var/drydesc = "It's dry and crusty. Someone is not doing their job." + gender = PLURAL + density = FALSE + anchored = TRUE + plane = BLOOD_PLANE + layer = BLOOD_DECAL_LAYER + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" + random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") + var/base_icon = 'icons/effects/blood.dmi' + blood_DNA = list() + var/basecolor="#A10808" // Color when wet. + var/synthblood = 0 + var/list/datum/disease2/disease/virus2 = list() + var/amount = 5 + generic_filth = TRUE + persistent = FALSE + +/obj/effect/decal/cleanable/blood/reveal_blood() + if(!fluorescent) + fluorescent = 1 + basecolor = COLOR_LUMINOL + update_icon() + +/obj/effect/decal/cleanable/blood/clean_blood() + fluorescent = 0 + if(invisibility != 100) + invisibility = 100 + amount = 0 + ..(ignore=1) + +/obj/effect/decal/cleanable/blood/New() + ..() + update_icon() + if(istype(src, /obj/effect/decal/cleanable/blood/gibs)) + return + if(src.type == /obj/effect/decal/cleanable/blood) + if(src.loc && isturf(src.loc)) + for(var/obj/effect/decal/cleanable/blood/B in src.loc) + if(B != src) + if (B.blood_DNA) + blood_DNA |= B.blood_DNA.Copy() + qdel(B) + addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1)) + +/obj/effect/decal/cleanable/blood/update_icon() + if(basecolor == "rainbow") basecolor = get_random_colour(1) + color = basecolor + + if(basecolor == SYNTH_BLOOD_COLOUR) + name = "oil" + desc = "It's quite oily." + else if(synthblood) + name = "synthetic blood" + desc = "It's quite greasy." + else + name = initial(name) + desc = initial(desc) + +/obj/effect/decal/cleanable/blood/Crossed(mob/living/carbon/human/perp) + if(perp.is_incorporeal()) + return + if (!istype(perp)) + return + if(amount < 1) + return + + var/obj/item/organ/external/l_foot = perp.get_organ("l_foot") + var/obj/item/organ/external/r_foot = perp.get_organ("r_foot") + var/hasfeet = 1 + if((!l_foot || l_foot.is_stump()) && (!r_foot || r_foot.is_stump())) + hasfeet = 0 + if(perp.shoes && !perp.buckled)//Adding blood to shoes + var/obj/item/clothing/shoes/S = perp.shoes + if(istype(S)) + S.blood_color = basecolor + S.track_blood = max(amount,S.track_blood) + if(!S.blood_overlay) + S.generate_blood_overlay() + if(!S.blood_DNA) + S.blood_DNA = list() + S.blood_overlay.color = basecolor + S.add_overlay(S.blood_overlay) + if(S.blood_overlay && S.blood_overlay.color != basecolor) + S.blood_overlay.color = basecolor + S.add_overlay(S.blood_overlay) + S.blood_DNA |= blood_DNA.Copy() + perp.update_inv_shoes() + + else if (hasfeet)//Or feet + perp.feet_blood_color = basecolor + perp.track_blood = max(amount,perp.track_blood) + LAZYINITLIST(perp.feet_blood_DNA) + perp.feet_blood_DNA |= blood_DNA.Copy() + perp.update_bloodied() + else if (perp.buckled && istype(perp.buckled, /obj/structure/bed/chair/wheelchair)) + var/obj/structure/bed/chair/wheelchair/W = perp.buckled + W.bloodiness = 4 + + amount-- + +/obj/effect/decal/cleanable/blood/proc/dry() + name = dryname + desc = drydesc + color = adjust_brightness(color, -50) + amount = 0 + +/obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user) + ..() + if (amount && istype(user)) + add_fingerprint(user) + if (user.gloves) + return + var/taken = rand(1,amount) + amount -= taken + to_chat(user, "You get some of \the [src] on your hands.") + if (!user.blood_DNA) + user.blood_DNA = list() + user.blood_DNA |= blood_DNA.Copy() + user.bloody_hands += taken + user.hand_blood_color = basecolor + user.update_inv_gloves(1) + user.verbs += /mob/living/carbon/human/proc/bloody_doodle + +/obj/effect/decal/cleanable/blood/splatter + random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") + amount = 2 + +/obj/effect/decal/cleanable/blood/drip + name = "drips of blood" + desc = "It's red." + gender = PLURAL + icon = 'icons/effects/drip.dmi' + icon_state = "1" + random_icon_states = list("1","2","3","4","5") + amount = 0 + var/list/drips = list() + +/obj/effect/decal/cleanable/blood/drip/New() + ..() + drips |= icon_state + +/obj/effect/decal/cleanable/blood/writing + icon_state = "tracks" + desc = "It looks like a writing in blood." + gender = NEUTER + random_icon_states = list("writing1","writing2","writing3","writing4","writing5") + amount = 0 + var/message + +/obj/effect/decal/cleanable/blood/writing/New() + ..() + if(random_icon_states.len) + for(var/obj/effect/decal/cleanable/blood/writing/W in loc) + random_icon_states.Remove(W.icon_state) + icon_state = pick(random_icon_states) + else + icon_state = "writing1" + +/obj/effect/decal/cleanable/blood/writing/examine(mob/user) + . = ..() + . += "It reads: \"[message]\"" + +/obj/effect/decal/cleanable/blood/gibs + name = "gibs" + desc = "They look bloody and gruesome." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "gibbl5" + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6") + var/fleshcolor = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/update_icon() + + var/image/giblets = new(base_icon, "[icon_state]_flesh", dir) + if(!fleshcolor || fleshcolor == "rainbow") + fleshcolor = get_random_colour(1) + giblets.color = fleshcolor + + var/icon/blood = new(base_icon,"[icon_state]",dir) + if(basecolor == "rainbow") basecolor = get_random_colour(1) + blood.Blend(basecolor,ICON_MULTIPLY) + + icon = blood + cut_overlays() + add_overlay(giblets) + +/obj/effect/decal/cleanable/blood/gibs/up + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibup1","gibup1","gibup1") + +/obj/effect/decal/cleanable/blood/gibs/down + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6","gibdown1","gibdown1","gibdown1") + +/obj/effect/decal/cleanable/blood/gibs/body + random_icon_states = list("gibhead", "gibtorso") + +/obj/effect/decal/cleanable/blood/gibs/limb + random_icon_states = list("gibleg", "gibarm") + +/obj/effect/decal/cleanable/blood/gibs/core + random_icon_states = list("gibmid1", "gibmid2", "gibmid3") + + +/obj/effect/decal/cleanable/blood/gibs/proc/streak(var/list/directions) + spawn (0) + var/direction = pick(directions) + for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) + sleep(3) + if (i > 0) + var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(src.loc) + b.basecolor = src.basecolor + b.update_icon() + + if (step_to(src, get_step(src, direction), 0)) + break + + +/obj/effect/decal/cleanable/mucus + name = "mucus" + desc = "Disgusting mucus." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "mucus" + random_icon_states = list("mucus") + + var/list/datum/disease2/disease/virus2 = list() + var/dry = 0 // Keeps the lag down + +/obj/effect/decal/cleanable/mucus/Initialize() + . = ..() + VARSET_IN(src, dry, TRUE, DRYING_TIME * 2) + +//This version should be used for admin spawns and pre-mapped virus vectors (e.g. in PoIs), this version does not dry +/obj/effect/decal/cleanable/mucus/mapped/Initialize() + . = ..() + virus2 |= new /datum/disease2/disease + virus2[1].makerandom() + +/obj/effect/decal/cleanable/mucus/mapped/Destroy() + virus2.Cut() + return ..() diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 55cdcfa4ff6..ad2a3fef803 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -1,163 +1,163 @@ -/obj/effect/decal/cleanable/generic - name = "clutter" - desc = "Someone should clean that up." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/obj/objects.dmi' - icon_state = "shards" - -/obj/effect/decal/cleanable/ash - name = "ashes" - desc = "Ashes to ashes, dust to dust, and into space." - gender = PLURAL - icon = 'icons/obj/objects.dmi' - icon_state = "ash" - anchored = TRUE - -/obj/effect/decal/cleanable/ash/attack_hand(mob/user as mob) - to_chat(user, "[src] sifts through your fingers.") - var/turf/simulated/floor/F = get_turf(src) - if (istype(F)) - F.dirt += 4 - qdel(src) - -/obj/effect/decal/cleanable/greenglow - -/obj/effect/decal/cleanable/greenglow/New() - ..() - QDEL_IN(src, 2 MINUTES) - -/obj/effect/decal/cleanable/dirt - name = "dirt" - desc = "Someone should clean that up." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/effects.dmi' - icon_state = "dirt" - mouse_opacity = 0 - -/obj/effect/decal/cleanable/dirt/Initialize(var/mapload, var/_age, var/dirt) - .=..() - var/turf/simulated/our_turf = src.loc - if(our_turf && istype(our_turf) && our_turf.can_dirty) - our_turf.dirt = clamp(max(age ? (dirt ? dirt : 101) : our_turf.dirt, our_turf.dirt), 0, 101) - var/calcalpha = our_turf.dirt > 50 ? min((our_turf.dirt - 50) * 5, 255) : 0 - var/alreadyfound = FALSE - for (var/obj/effect/decal/cleanable/dirt/alreadythere in our_turf) //in case of multiple - if (alreadythere == src) - continue - else if (alreadyfound) - qdel(alreadythere) - continue - alreadyfound = TRUE - alreadythere.alpha = calcalpha //don't need to constantly recalc for all of them in it because it'll just max if a non-persistent dirt overlay gets added, and then the new dirt overlay will be deleted - if (alreadyfound) - return INITIALIZE_HINT_QDEL - alpha = calcalpha - -/obj/effect/decal/cleanable/flour - name = "flour" - desc = "It's still good. Four second rule!" - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/effects.dmi' - icon_state = "flour" - -/obj/effect/decal/cleanable/greenglow - name = "glowing goo" - desc = "Jeez. I hope that's not for lunch." - gender = PLURAL - density = FALSE - anchored = TRUE - light_range = 1 - icon = 'icons/effects/effects.dmi' - icon_state = "greenglow" - -/obj/effect/decal/cleanable/cobweb - name = "cobweb" - desc = "Somebody should remove that." - density = FALSE - anchored = TRUE - plane = OBJ_PLANE - icon = 'icons/effects/effects.dmi' - icon_state = "cobweb1" - -/obj/effect/decal/cleanable/molten_item - name = "gooey grey mass" - desc = "It looks like a melted... something." - density = FALSE - anchored = TRUE - plane = OBJ_PLANE - icon = 'icons/obj/chemical.dmi' - icon_state = "molten" - -/obj/effect/decal/cleanable/cobweb2 - name = "cobweb" - desc = "Somebody should remove that." - density = FALSE - anchored = TRUE - plane = OBJ_PLANE - icon = 'icons/effects/effects.dmi' - icon_state = "cobweb2" - -//Vomit (sorry) -/obj/effect/decal/cleanable/vomit - name = "vomit" - desc = "Gosh, how unpleasant." - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "vomit_1" - random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4") - var/list/datum/disease2/disease/virus2 = list() - -/obj/effect/decal/cleanable/tomato_smudge - name = "tomato smudge" - desc = "It's red." - density = FALSE - anchored = TRUE - icon = 'icons/effects/tomatodecal.dmi' - random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") - -/obj/effect/decal/cleanable/egg_smudge - name = "smashed egg" - desc = "Seems like this one won't hatch." - density = FALSE - anchored = TRUE - icon = 'icons/effects/tomatodecal.dmi' - random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") - -/obj/effect/decal/cleanable/pie_smudge //honk - name = "smashed pie" - desc = "It's pie cream from a cream pie." - density = FALSE - anchored = TRUE - icon = 'icons/effects/tomatodecal.dmi' - random_icon_states = list("smashed_pie") - -/obj/effect/decal/cleanable/fruit_smudge - name = "smudge" - desc = "Some kind of fruit smear." - density = FALSE - anchored = TRUE - icon = 'icons/effects/blood.dmi' - icon_state = "mfloor1" - random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") - -/obj/effect/decal/cleanable/confetti - name = "confetti" - desc = "Tiny bits of colored paper thrown about for the janitor to enjoy!" - gender = PLURAL - density = FALSE - anchored = TRUE - icon = 'icons/effects/effects.dmi' - icon_state = "confetti" - -/obj/effect/decal/cleanable/confetti/attack_hand(mob/user) - to_chat(user, "You start to meticulously pick up the confetti.") - if(do_after(user, 60)) - qdel(src) +/obj/effect/decal/cleanable/generic + name = "clutter" + desc = "Someone should clean that up." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/obj/objects.dmi' + icon_state = "shards" + +/obj/effect/decal/cleanable/ash + name = "ashes" + desc = "Ashes to ashes, dust to dust, and into space." + gender = PLURAL + icon = 'icons/obj/objects.dmi' + icon_state = "ash" + anchored = TRUE + +/obj/effect/decal/cleanable/ash/attack_hand(mob/user as mob) + to_chat(user, "[src] sifts through your fingers.") + var/turf/simulated/floor/F = get_turf(src) + if (istype(F)) + F.dirt += 4 + qdel(src) + +/obj/effect/decal/cleanable/greenglow + +/obj/effect/decal/cleanable/greenglow/New() + ..() + QDEL_IN(src, 2 MINUTES) + +/obj/effect/decal/cleanable/dirt + name = "dirt" + desc = "Someone should clean that up." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/effects.dmi' + icon_state = "dirt" + mouse_opacity = 0 + +/obj/effect/decal/cleanable/dirt/Initialize(var/mapload, var/_age, var/dirt) + .=..() + var/turf/simulated/our_turf = src.loc + if(our_turf && istype(our_turf) && our_turf.can_dirty) + our_turf.dirt = clamp(max(age ? (dirt ? dirt : 101) : our_turf.dirt, our_turf.dirt), 0, 101) + var/calcalpha = our_turf.dirt > 50 ? min((our_turf.dirt - 50) * 5, 255) : 0 + var/alreadyfound = FALSE + for (var/obj/effect/decal/cleanable/dirt/alreadythere in our_turf) //in case of multiple + if (alreadythere == src) + continue + else if (alreadyfound) + qdel(alreadythere) + continue + alreadyfound = TRUE + alreadythere.alpha = calcalpha //don't need to constantly recalc for all of them in it because it'll just max if a non-persistent dirt overlay gets added, and then the new dirt overlay will be deleted + if (alreadyfound) + return INITIALIZE_HINT_QDEL + alpha = calcalpha + +/obj/effect/decal/cleanable/flour + name = "flour" + desc = "It's still good. Four second rule!" + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/effects.dmi' + icon_state = "flour" + +/obj/effect/decal/cleanable/greenglow + name = "glowing goo" + desc = "Jeez. I hope that's not for lunch." + gender = PLURAL + density = FALSE + anchored = TRUE + light_range = 1 + icon = 'icons/effects/effects.dmi' + icon_state = "greenglow" + +/obj/effect/decal/cleanable/cobweb + name = "cobweb" + desc = "Somebody should remove that." + density = FALSE + anchored = TRUE + plane = OBJ_PLANE + icon = 'icons/effects/effects.dmi' + icon_state = "cobweb1" + +/obj/effect/decal/cleanable/molten_item + name = "gooey grey mass" + desc = "It looks like a melted... something." + density = FALSE + anchored = TRUE + plane = OBJ_PLANE + icon = 'icons/obj/chemical.dmi' + icon_state = "molten" + +/obj/effect/decal/cleanable/cobweb2 + name = "cobweb" + desc = "Somebody should remove that." + density = FALSE + anchored = TRUE + plane = OBJ_PLANE + icon = 'icons/effects/effects.dmi' + icon_state = "cobweb2" + +//Vomit (sorry) +/obj/effect/decal/cleanable/vomit + name = "vomit" + desc = "Gosh, how unpleasant." + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "vomit_1" + random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4") + var/list/datum/disease2/disease/virus2 = list() + +/obj/effect/decal/cleanable/tomato_smudge + name = "tomato smudge" + desc = "It's red." + density = FALSE + anchored = TRUE + icon = 'icons/effects/tomatodecal.dmi' + random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3") + +/obj/effect/decal/cleanable/egg_smudge + name = "smashed egg" + desc = "Seems like this one won't hatch." + density = FALSE + anchored = TRUE + icon = 'icons/effects/tomatodecal.dmi' + random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3") + +/obj/effect/decal/cleanable/pie_smudge //honk + name = "smashed pie" + desc = "It's pie cream from a cream pie." + density = FALSE + anchored = TRUE + icon = 'icons/effects/tomatodecal.dmi' + random_icon_states = list("smashed_pie") + +/obj/effect/decal/cleanable/fruit_smudge + name = "smudge" + desc = "Some kind of fruit smear." + density = FALSE + anchored = TRUE + icon = 'icons/effects/blood.dmi' + icon_state = "mfloor1" + random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") + +/obj/effect/decal/cleanable/confetti + name = "confetti" + desc = "Tiny bits of colored paper thrown about for the janitor to enjoy!" + gender = PLURAL + density = FALSE + anchored = TRUE + icon = 'icons/effects/effects.dmi' + icon_state = "confetti" + +/obj/effect/decal/cleanable/confetti/attack_hand(mob/user) + to_chat(user, "You start to meticulously pick up the confetti.") + if(do_after(user, 60)) + qdel(src) diff --git a/code/game/objects/effects/decals/Cleanable/robots.dm b/code/game/objects/effects/decals/Cleanable/robots.dm index bdc5c9237a6..b0980966a30 100644 --- a/code/game/objects/effects/decals/Cleanable/robots.dm +++ b/code/game/objects/effects/decals/Cleanable/robots.dm @@ -1,52 +1,52 @@ -/obj/effect/decal/cleanable/blood/gibs/robot - name = "robot debris" - desc = "It's a useless heap of junk... or is it?" - icon = 'icons/mob/robots.dmi' - icon_state = "gib1" - basecolor = SYNTH_BLOOD_COLOUR - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7") - generic_filth = FALSE - persistent = FALSE - -/obj/effect/decal/cleanable/blood/gibs/robot/update_icon() - color = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like - return - -/obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) - spawn (0) - var/direction = pick(directions) - for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) - sleep(3) - if (i > 0) - if (prob(40)) - var/obj/effect/decal/cleanable/blood/oil/streak = new(src.loc) - streak.update_icon() - else if (prob(10)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - if (step_to(src, get_step(src, direction), 0)) - break - -/obj/effect/decal/cleanable/blood/gibs/robot/limb - random_icon_states = list("gibarm", "gibleg") - -/obj/effect/decal/cleanable/blood/gibs/robot/up - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibup1","gibup1") //2:7 is close enough to 1:4 - -/obj/effect/decal/cleanable/blood/gibs/robot/down - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibdown1","gibdown1") //2:7 is close enough to 1:4 - -/obj/effect/decal/cleanable/blood/oil - basecolor = SYNTH_BLOOD_COLOUR - generic_filth = FALSE - persistent = FALSE - -/obj/effect/decal/cleanable/blood/oil/dry() - return - -/obj/effect/decal/cleanable/blood/oil/streak - random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") +/obj/effect/decal/cleanable/blood/gibs/robot + name = "robot debris" + desc = "It's a useless heap of junk... or is it?" + icon = 'icons/mob/robots.dmi' + icon_state = "gib1" + basecolor = SYNTH_BLOOD_COLOUR + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7") + generic_filth = FALSE + persistent = FALSE + +/obj/effect/decal/cleanable/blood/gibs/robot/update_icon() + color = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like + return + +/obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) + spawn (0) + var/direction = pick(directions) + for (var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) + sleep(3) + if (i > 0) + if (prob(40)) + var/obj/effect/decal/cleanable/blood/oil/streak = new(src.loc) + streak.update_icon() + else if (prob(10)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + if (step_to(src, get_step(src, direction), 0)) + break + +/obj/effect/decal/cleanable/blood/gibs/robot/limb + random_icon_states = list("gibarm", "gibleg") + +/obj/effect/decal/cleanable/blood/gibs/robot/up + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibup1","gibup1") //2:7 is close enough to 1:4 + +/obj/effect/decal/cleanable/blood/gibs/robot/down + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7","gibdown1","gibdown1") //2:7 is close enough to 1:4 + +/obj/effect/decal/cleanable/blood/oil + basecolor = SYNTH_BLOOD_COLOUR + generic_filth = FALSE + persistent = FALSE + +/obj/effect/decal/cleanable/blood/oil/dry() + return + +/obj/effect/decal/cleanable/blood/oil/streak + random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") amount = 2 \ No newline at end of file diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index cc24fd13e68..8c8344df2ca 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -201,4 +201,6 @@ var/global/list/image/fluidtrack_cache=list() going_state = "" gender = PLURAL random_icon_states = null - amount = 0 \ No newline at end of file + amount = 0 + +#undef TRACKS_CRUSTIFY_TIME diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index ed411a67ab5..d30a548ab2d 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -1,38 +1,38 @@ -/* -USAGE NOTE -For decals, the var Persistent = 'has already been saved', and is primarily used to prevent duplicate savings of generic filth (filth.dm). -This also means 'TRUE' can be used to define a decal as "Do not save at all, even as a generic replacement." if a dirt decal is considered 'too common' to save. -generic_filth = TRUE means when the decal is saved, it will be switched out for a generic green 'filth' decal. -*/ - -/obj/effect/decal/cleanable - plane = DIRTY_PLANE - layer = DIRTY_LAYER - var/persistent = FALSE - var/generic_filth = FALSE - var/age = 0 - var/list/random_icon_states = list() - -/obj/effect/decal/cleanable/Initialize(var/mapload, var/_age) - if(!isnull(_age)) - age = _age - if(random_icon_states && length(src.random_icon_states) > 0) - src.icon_state = pick(src.random_icon_states) - if(!mapload || !config.persistence_ignore_mapload) - SSpersistence.track_value(src, /datum/persistent/filth) - . = ..() - -/obj/effect/decal/cleanable/Destroy() - SSpersistence.forget_value(src, /datum/persistent/filth) - . = ..() - -/obj/effect/decal/cleanable/clean_blood(var/ignore = 0) - if(!ignore) - qdel(src) - return - ..() - -/obj/effect/decal/cleanable/New() - if (random_icon_states && length(src.random_icon_states) > 0) - src.icon_state = pick(src.random_icon_states) - ..() +/* +USAGE NOTE +For decals, the var Persistent = 'has already been saved', and is primarily used to prevent duplicate savings of generic filth (filth.dm). +This also means 'TRUE' can be used to define a decal as "Do not save at all, even as a generic replacement." if a dirt decal is considered 'too common' to save. +generic_filth = TRUE means when the decal is saved, it will be switched out for a generic green 'filth' decal. +*/ + +/obj/effect/decal/cleanable + plane = DIRTY_PLANE + layer = DIRTY_LAYER + var/persistent = FALSE + var/generic_filth = FALSE + var/age = 0 + var/list/random_icon_states = list() + +/obj/effect/decal/cleanable/Initialize(var/mapload, var/_age) + if(!isnull(_age)) + age = _age + if(random_icon_states && length(src.random_icon_states) > 0) + src.icon_state = pick(src.random_icon_states) + if(!mapload || !config.persistence_ignore_mapload) + SSpersistence.track_value(src, /datum/persistent/filth) + . = ..() + +/obj/effect/decal/cleanable/Destroy() + SSpersistence.forget_value(src, /datum/persistent/filth) + . = ..() + +/obj/effect/decal/cleanable/clean_blood(var/ignore = 0) + if(!ignore) + qdel(src) + return + ..() + +/obj/effect/decal/cleanable/New() + if (random_icon_states && length(src.random_icon_states) > 0) + src.icon_state = pick(src.random_icon_states) + ..() diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm index 2915213d8a5..0b298f2224d 100644 --- a/code/game/objects/effects/decals/contraband.dm +++ b/code/game/objects/effects/decals/contraband.dm @@ -180,7 +180,7 @@ poster_set = TRUE /obj/structure/sign/poster/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wirecutter()) + if(W.has_tool_quality(TOOL_WIRECUTTER)) playsound(src, W.usesound, 100, 1) if(ruined) to_chat(user, "You remove the remnants of the poster.") diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index 248b9cfbb46..493529cedeb 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -1,31 +1,31 @@ -/obj/effect/decal/cleanable/crayon - name = "rune" - desc = "A rune drawn in crayon." - icon = 'icons/obj/rune.dmi' - plane = DIRTY_PLANE - layer = DIRTY_LAYER - anchored = TRUE - -/obj/effect/decal/cleanable/crayon/New(location,main = "#FFFFFF",shade = "#000000",var/type = "rune") - ..() - loc = location - - name = type - desc = "A [type] drawn in crayon." - - switch(type) - if("rune") - type = "rune[rand(1,6)]" - if("graffiti") - type = pick("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa") - - var/icon/mainOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]",2.1) - var/icon/shadeOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]s",2.1) - - mainOverlay.Blend(main,ICON_ADD) - shadeOverlay.Blend(shade,ICON_ADD) - - add_overlay(mainOverlay) - add_overlay(shadeOverlay) - - add_hiddenprint(usr) +/obj/effect/decal/cleanable/crayon + name = "rune" + desc = "A rune drawn in crayon." + icon = 'icons/obj/rune.dmi' + plane = DIRTY_PLANE + layer = DIRTY_LAYER + anchored = TRUE + +/obj/effect/decal/cleanable/crayon/New(location,main = "#FFFFFF",shade = "#000000",var/type = "rune") + ..() + loc = location + + name = type + desc = "A [type] drawn in crayon." + + switch(type) + if("rune") + type = "rune[rand(1,6)]" + if("graffiti") + type = pick("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa") + + var/icon/mainOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]",2.1) + var/icon/shadeOverlay = new/icon('icons/effects/crayondecal.dmi',"[type]s",2.1) + + mainOverlay.Blend(main,ICON_ADD) + shadeOverlay.Blend(shade,ICON_ADD) + + add_overlay(mainOverlay) + add_overlay(shadeOverlay) + + add_hiddenprint(usr) diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm index 3e5c46e1347..df2d3ee32bb 100644 --- a/code/game/objects/effects/decals/misc.dm +++ b/code/game/objects/effects/decals/misc.dm @@ -1,14 +1,14 @@ -/obj/effect/decal/point - name = "arrow" - desc = "It's an arrow hanging in mid-air. There may be a wizard about." - icon = 'icons/mob/screen1.dmi' - icon_state = "arrow" - plane = ABOVE_PLANE - anchored = TRUE - mouse_opacity = 0 - -// Used for spray that you spray at walls, tables, hydrovats etc -/obj/effect/decal/spraystill - density = FALSE - anchored = TRUE +/obj/effect/decal/point + name = "arrow" + desc = "It's an arrow hanging in mid-air. There may be a wizard about." + icon = 'icons/mob/screen1.dmi' + icon_state = "arrow" + plane = ABOVE_PLANE + anchored = TRUE + mouse_opacity = 0 + +// Used for spray that you spray at walls, tables, hydrovats etc +/obj/effect/decal/spraystill + density = FALSE + anchored = TRUE plane = ABOVE_PLANE \ No newline at end of file diff --git a/code/game/objects/effects/decals/posters/posters.dm b/code/game/objects/effects/decals/posters/posters.dm index 1a9710a5668..74624bcb0d8 100644 --- a/code/game/objects/effects/decals/posters/posters.dm +++ b/code/game/objects/effects/decals/posters/posters.dm @@ -165,7 +165,7 @@ flick("poster_being_set", src) /obj/structure/sign/poster/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wirecutter()) + if(W.has_tool_quality(TOOL_WIRECUTTER)) playsound(src, W.usesound, 100, 1) if(ruined) to_chat(user, "You remove the remnants of the poster.") diff --git a/code/game/objects/effects/decals/posters/voreposters_vr.dm b/code/game/objects/effects/decals/posters/voreposters_vr.dm index 335366aedc8..08374d979f0 100644 --- a/code/game/objects/effects/decals/posters/voreposters_vr.dm +++ b/code/game/objects/effects/decals/posters/voreposters_vr.dm @@ -2,10 +2,6 @@ desc = "You shouldn't see this." listing_name = "N/A" -/decl/poster/vore_1 - icon_state = "sbsposter1" - name = "Best Girl Pip" - desc = "A poster of the official NT Best Girl Mascot, Pip" /decl/poster/lewd/vore_2 icon_state = "sbsposter2" name = "AroButt" @@ -26,14 +22,6 @@ icon_state = "sbsposter6" name = "Mawletta" desc = "The best view in the system." -/decl/poster/vore_8 - icon_state = "sbsposter8" - name = "Shitty Timbs" - desc = "A special edition, brand deal, Shitty Tim Concert Poster." -/decl/poster/vore_9 - icon_state = "sbsposter9" - name = "Shitty Tim" - desc = "A Shitty Tim concert poster." /decl/poster/vore_11 icon_state = "sbsposter11" name = "Fear" diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index 6f8af8cdd9e..9d1d0e1309e 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -1,70 +1,70 @@ -/obj/effect/decal/remains - name = "remains" - gender = PLURAL - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - anchored = FALSE - -/obj/effect/decal/remains/human - desc = "They look like human remains. They have a strange aura about them." - -/obj/effect/decal/remains/xeno - desc = "They look like the remains of something... alien. They have a strange aura about them." - icon_state = "remainsxeno" - -/obj/effect/decal/remains/robot - desc = "They look like the remains of something mechanical. They have a strange aura about them." - icon = 'icons/mob/robots.dmi' - icon_state = "remainsrobot" - -/obj/effect/decal/remains/mouse - desc = "They look like the remains of a small rodent." - icon_state = "mouse" - -/obj/effect/decal/remains/lizard - desc = "They look like the remains of a small lizard." - icon_state = "lizard" - -/obj/effect/decal/remains/unathi - desc = "They look like Unathi remains. Pointy." - icon_state = "remainsunathi" - -/obj/effect/decal/remains/tajaran - desc = "They look like Tajaran remains. They're surprisingly small." - icon_state = "remainstajaran" - -/obj/effect/decal/remains/ribcage - desc = "They look like animal remains of some sort... You hope." - icon_state = "remainsribcage" - -/obj/effect/decal/remains/deer - desc = "They look like the remains of a large herbivore, picked clean." - icon_state = "remainsdeer" - -/obj/effect/decal/remains/posi - desc = "This looks like part of an old FBP. Hopefully it was empty." - icon_state = "remainsposi" - -/obj/effect/decal/remains/mummy1 - name = "mummified remains" - desc = "They look like human remains. They've been here a long time." - icon_state = "mummified1" - -/obj/effect/decal/remains/mummy2 - name = "mummified remains" - desc = "They look like human remains. They've been here a long time." - icon_state = "mummified2" - -/obj/effect/decal/remains/attack_hand(mob/user as mob) - to_chat(user, "[src] sinks together into a pile of ash.") - var/turf/simulated/floor/F = get_turf(src) - if(istype(F)) - new /obj/effect/decal/cleanable/ash(F) - qdel(src) - -/obj/effect/decal/remains/robot/attack_hand(mob/user as mob) - to_chat(user, "[src] crumbles down into a pile of debris.") - var/turf/simulated/floor/F = get_turf(src) - if(istype(F)) - new /obj/effect/decal/cleanable/blood/gibs/robot(F) - qdel(src) +/obj/effect/decal/remains + name = "remains" + gender = PLURAL + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + anchored = FALSE + +/obj/effect/decal/remains/human + desc = "They look like human remains. They have a strange aura about them." + +/obj/effect/decal/remains/xeno + desc = "They look like the remains of something... alien. They have a strange aura about them." + icon_state = "remainsxeno" + +/obj/effect/decal/remains/robot + desc = "They look like the remains of something mechanical. They have a strange aura about them." + icon = 'icons/mob/robots.dmi' + icon_state = "remainsrobot" + +/obj/effect/decal/remains/mouse + desc = "They look like the remains of a small rodent." + icon_state = "mouse" + +/obj/effect/decal/remains/lizard + desc = "They look like the remains of a small lizard." + icon_state = "lizard" + +/obj/effect/decal/remains/unathi + desc = "They look like Unathi remains. Pointy." + icon_state = "remainsunathi" + +/obj/effect/decal/remains/tajaran + desc = "They look like Tajaran remains. They're surprisingly small." + icon_state = "remainstajaran" + +/obj/effect/decal/remains/ribcage + desc = "They look like animal remains of some sort... You hope." + icon_state = "remainsribcage" + +/obj/effect/decal/remains/deer + desc = "They look like the remains of a large herbivore, picked clean." + icon_state = "remainsdeer" + +/obj/effect/decal/remains/posi + desc = "This looks like part of an old FBP. Hopefully it was empty." + icon_state = "remainsposi" + +/obj/effect/decal/remains/mummy1 + name = "mummified remains" + desc = "They look like human remains. They've been here a long time." + icon_state = "mummified1" + +/obj/effect/decal/remains/mummy2 + name = "mummified remains" + desc = "They look like human remains. They've been here a long time." + icon_state = "mummified2" + +/obj/effect/decal/remains/attack_hand(mob/user as mob) + to_chat(user, "[src] sinks together into a pile of ash.") + var/turf/simulated/floor/F = get_turf(src) + if(istype(F)) + new /obj/effect/decal/cleanable/ash(F) + qdel(src) + +/obj/effect/decal/remains/robot/attack_hand(mob/user as mob) + to_chat(user, "[src] crumbles down into a pile of debris.") + var/turf/simulated/floor/F = get_turf(src) + if(istype(F)) + new /obj/effect/decal/cleanable/blood/gibs/robot(F) + qdel(src) diff --git a/code/game/objects/effects/decals/warning_stripes.dm b/code/game/objects/effects/decals/warning_stripes.dm index e69c90a8d84..c470d186cea 100644 --- a/code/game/objects/effects/decals/warning_stripes.dm +++ b/code/game/objects/effects/decals/warning_stripes.dm @@ -1,10 +1,10 @@ -/obj/effect/decal/warning_stripes - icon = 'icons/effects/warning_stripes.dmi' - -/obj/effect/decal/warning_stripes/Initialize() - . = ..() - var/turf/T=get_turf(src) - var/image/I=image(icon, icon_state = icon_state, dir = dir) - I.color=color - T.add_overlay(I) - return INITIALIZE_HINT_QDEL +/obj/effect/decal/warning_stripes + icon = 'icons/effects/warning_stripes.dmi' + +/obj/effect/decal/warning_stripes/Initialize() + . = ..() + var/turf/T=get_turf(src) + var/image/I=image(icon, icon_state = icon_state, dir = dir) + I.color=color + T.add_overlay(I) + return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 8543c55a2b0..fca49884932 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -1,579 +1,581 @@ -/* This is an attempt to make some easily reusable "particle" type effect, to stop the code -constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one -defined, then set up when it is created with New(). Then this same system can just be reused each time -it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam -would spawn and follow the beaker, even if it is carried or thrown. -*/ -/obj/effect - light_on = TRUE - -/obj/effect/effect - name = "effect" - icon = 'icons/effects/effects.dmi' - mouse_opacity = 0 - unacidable = TRUE//So effect are not targeted by alien acid. - pass_flags = PASSTABLE | PASSGRILLE - blocks_emissive = EMISSIVE_BLOCK_GENERIC - light_on = TRUE - plane = ABOVE_OBJ_PLANE - -/datum/effect/effect/system - var/number = 3 - var/cardinals = 0 - var/turf/location - var/atom/holder - var/setup = 0 - -/datum/effect/effect/system/proc/set_up(n = 3, c = 0, turf/loc) - if(n > 10) - n = 10 - number = n - cardinals = c - location = loc - setup = 1 - -/datum/effect/effect/system/proc/attach(atom/atom) - holder = atom - -/datum/effect/effect/system/proc/start() - -/datum/effect/effect/system/Destroy() - holder = null - return ..() - -///////////////////////////////////////////// -// GENERIC STEAM SPREAD SYSTEM - -//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) -// The attach(atom/atom) proc is optional, and can be called to attach the effect -// to something, like a smoking beaker, so then you can just call start() and the steam -// will always spawn at the items location, even if it's moved. - -/* Example: -var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system -steam.set_up(5, 0, mob.loc) -- sets up variables -OPTIONAL: steam.attach(mob) -steam.start() -- spawns the effect -*/ -///////////////////////////////////////////// -/obj/effect/effect/steam - name = "steam" - icon = 'icons/effects/effects.dmi' - icon_state = "extinguish" - density = FALSE - -/datum/effect/effect/system/steam_spread/set_up(n = 3, c = 0, turf/loc) - if(n > 10) - n = 10 - number = n - cardinals = c - location = loc - -/datum/effect/effect/system/steam_spread/start() - var/i = 0 - for(i=0, i 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - -/datum/effect/effect/system/spark_spread/start() - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/sparks/sparks = new /obj/effect/effect/sparks(src.location) - src.total_sparks++ - var/direction - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, i 10) - n = 10 - number = n - cardinals = c - if(istype(loca, /turf/)) - location = loca - else - location = get_turf(loca) - if(direct) - direction = direct - -/datum/effect/effect/system/smoke_spread/start(var/I) - var/i = 0 - for(i=0, i 20) - return - spawn(0) - if(holder) - src.location = get_turf(holder) - var/obj/effect/effect/smoke/smoke = new smoke_type(src.location) - src.total_smoke++ - if(I) - smoke.color = I - var/direction = src.direction - if(!direction) - if(src.cardinals) - direction = pick(cardinal) - else - direction = pick(alldirs) - for(i=0, iThe solution violently explodes.") - for(var/mob/M in viewers(1, location)) - if (prob (50 * amount)) - to_chat(M, "The explosion knocks you down.") - M.Weaken(rand(1,5)) - return - else - var/devst = -1 - var/heavy = -1 - var/light = -1 - var/flash = -1 - - // Clamp all values to fractions of max_explosion_range, following the same pattern as for tank transfer bombs - if (round(amount/12) > 0) - devst = devst + amount/12 - - if (round(amount/6) > 0) - heavy = heavy + amount/6 - - if (round(amount/3) > 0) - light = light + amount/3 - - if (flashing && flashing_factor) - flash = (amount/4) * flashing_factor - - for(var/mob/M in viewers(8, location)) - to_chat(M, "The solution violently explodes.") - - explosion( - location, - round(min(devst, BOMBCAP_DVSTN_RADIUS)), - round(min(heavy, BOMBCAP_HEAVY_RADIUS)), - round(min(light, BOMBCAP_LIGHT_RADIUS)), - round(min(flash, BOMBCAP_FLASH_RADIUS)) - ) +/* This is an attempt to make some easily reusable "particle" type effect, to stop the code +constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one +defined, then set up when it is created with New(). Then this same system can just be reused each time +it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam +would spawn and follow the beaker, even if it is carried or thrown. +*/ +/obj/effect + light_on = TRUE + +/obj/effect/effect + name = "effect" + icon = 'icons/effects/effects.dmi' + mouse_opacity = 0 + unacidable = TRUE//So effect are not targeted by alien acid. + pass_flags = PASSTABLE | PASSGRILLE + blocks_emissive = EMISSIVE_BLOCK_GENERIC + light_on = TRUE + plane = ABOVE_OBJ_PLANE + +/datum/effect/effect/system + var/number = 3 + var/cardinals = 0 + var/turf/location + var/atom/holder + var/setup = 0 + +/datum/effect/effect/system/proc/set_up(n = 3, c = 0, turf/loc) + if(n > 10) + n = 10 + number = n + cardinals = c + location = loc + setup = 1 + +/datum/effect/effect/system/proc/attach(atom/atom) + holder = atom + +/datum/effect/effect/system/proc/start() + +/datum/effect/effect/system/Destroy() + holder = null + return ..() + +///////////////////////////////////////////// +// GENERIC STEAM SPREAD SYSTEM + +//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) +// The attach(atom/atom) proc is optional, and can be called to attach the effect +// to something, like a smoking beaker, so then you can just call start() and the steam +// will always spawn at the items location, even if it's moved. + +/* Example: +var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system +steam.set_up(5, 0, mob.loc) -- sets up variables +OPTIONAL: steam.attach(mob) +steam.start() -- spawns the effect +*/ +///////////////////////////////////////////// +/obj/effect/effect/steam + name = "steam" + icon = 'icons/effects/effects.dmi' + icon_state = "extinguish" + density = FALSE + +/datum/effect/effect/system/steam_spread/set_up(n = 3, c = 0, turf/loc) + if(n > 10) + n = 10 + number = n + cardinals = c + location = loc + +/datum/effect/effect/system/steam_spread/start() + var/i = 0 + for(i=0, i 10) + n = 10 + number = n + cardinals = c + if(istype(loca, /turf/)) + location = loca + else + location = get_turf(loca) + +/datum/effect/effect/system/spark_spread/start() + var/i = 0 + for(i=0, i 20) + return + spawn(0) + if(holder) + src.location = get_turf(holder) + var/obj/effect/effect/sparks/sparks = new /obj/effect/effect/sparks(src.location) + src.total_sparks++ + var/direction + if(src.cardinals) + direction = pick(cardinal) + else + direction = pick(alldirs) + for(i=0, i 10) + n = 10 + number = n + cardinals = c + if(istype(loca, /turf/)) + location = loca + else + location = get_turf(loca) + if(direct) + direction = direct + +/datum/effect/effect/system/smoke_spread/start(var/I) + var/i = 0 + for(i=0, i 20) + return + spawn(0) + if(holder) + src.location = get_turf(holder) + var/obj/effect/effect/smoke/smoke = new smoke_type(src.location) + src.total_smoke++ + if(I) + smoke.color = I + var/direction = src.direction + if(!direction) + if(src.cardinals) + direction = pick(cardinal) + else + direction = pick(alldirs) + for(i=0, iThe solution violently explodes.") + for(var/mob/M in viewers(1, location)) + if (prob (50 * amount)) + to_chat(M, "The explosion knocks you down.") + M.Weaken(rand(1,5)) + return + else + var/devst = -1 + var/heavy = -1 + var/light = -1 + var/flash = -1 + + // Clamp all values to fractions of max_explosion_range, following the same pattern as for tank transfer bombs + if (round(amount/12) > 0) + devst = devst + amount/12 + + if (round(amount/6) > 0) + heavy = heavy + amount/6 + + if (round(amount/3) > 0) + light = light + amount/3 + + if (flashing && flashing_factor) + flash = (amount/4) * flashing_factor + + for(var/mob/M in viewers(8, location)) + to_chat(M, "The solution violently explodes.") + + explosion( + location, + round(min(devst, BOMBCAP_DVSTN_RADIUS)), + round(min(heavy, BOMBCAP_HEAVY_RADIUS)), + round(min(light, BOMBCAP_LIGHT_RADIUS)), + round(min(flash, BOMBCAP_FLASH_RADIUS)) + ) diff --git a/code/game/objects/effects/explosion_particles.dm b/code/game/objects/effects/explosion_particles.dm index 449506197ad..f9460485aa7 100644 --- a/code/game/objects/effects/explosion_particles.dm +++ b/code/game/objects/effects/explosion_particles.dm @@ -1,72 +1,72 @@ -/obj/effect/expl_particles - name = "explosive particles" - icon = 'icons/effects/effects.dmi' - icon_state = "explosion_particle" - opacity = 1 - anchored = TRUE - mouse_opacity = 0 - -/obj/effect/expl_particles/New() - ..() - spawn (15) - qdel(src) - return - -/datum/effect/system/expl_particles - var/number = 10 - var/turf/location - var/total_particles = 0 - -/datum/effect/system/expl_particles/proc/set_up(n = 10, loca) - number = n - if(istype(loca, /turf/)) location = loca - else location = get_turf(loca) - -/datum/effect/system/expl_particles/proc/start() - var/i = 0 - for(i=0, iGib list length mismatch!") - return - - var/obj/effect/decal/cleanable/blood/gibs/gib = null - - if(sparks) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(2, 1, get_turf(location)) // Not sure if it's safe to pass an arbitrary object to set_up, todo - s.start() - - for(var/i = 1, i<= gibtypes.len, i++) - if(gibamounts[i]) - for(var/j = 1, j<= gibamounts[i], j++) - var/gibType = gibtypes[i] - gib = new gibType(location) - - // Apply human species colouration to masks. - if(fleshcolor) - gib.fleshcolor = fleshcolor - if(bloodcolor) - gib.basecolor = bloodcolor - - gib.update_icon() - - gib.blood_DNA = list() - if(MobDNA) - gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type - else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey - gib.blood_DNA["Non-human DNA"] = "A+" - if(istype(location,/turf/)) - var/list/directions = gibdirections[i] - if(directions.len) - gib.streak(directions) - - qdel(src) +/proc/gibs(atom/location, var/datum/dna/MobDNA, gibber_type = /obj/effect/gibspawner/generic, var/fleshcolor, var/bloodcolor) + new gibber_type(location,MobDNA,fleshcolor,bloodcolor) + +/obj/effect/gibspawner + var/sparks = 0 //whether sparks spread on Gib() + var/list/gibtypes = list() + var/list/gibamounts = list() + var/list/gibdirections = list() //of lists + var/fleshcolor //Used for gibbed humans. + var/bloodcolor //Used for gibbed humans. + +/obj/effect/gibspawner/New(location, var/datum/dna/MobDNA, var/fleshcolor, var/bloodcolor) + ..() + + if(fleshcolor) src.fleshcolor = fleshcolor + if(bloodcolor) src.bloodcolor = bloodcolor + Gib(loc,MobDNA) + +/obj/effect/gibspawner/proc/Gib(atom/location, var/datum/dna/MobDNA = null) + if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len) + to_world("Gib list length mismatch!") + return + + var/obj/effect/decal/cleanable/blood/gibs/gib = null + + if(sparks) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(2, 1, get_turf(location)) // Not sure if it's safe to pass an arbitrary object to set_up, todo + s.start() + + for(var/i = 1, i<= gibtypes.len, i++) + if(gibamounts[i]) + for(var/j = 1, j<= gibamounts[i], j++) + var/gibType = gibtypes[i] + gib = new gibType(location) + + // Apply human species colouration to masks. + if(fleshcolor) + gib.fleshcolor = fleshcolor + if(bloodcolor) + gib.basecolor = bloodcolor + + gib.update_icon() + + gib.blood_DNA = list() + if(MobDNA) + gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type + else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey + gib.blood_DNA["Non-human DNA"] = "A+" + if(istype(location,/turf/)) + var/list/directions = gibdirections[i] + if(directions.len) + gib.streak(directions) + + qdel(src) diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index fc6af6b2fd5..e25bc4de774 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -1,291 +1,291 @@ -/obj/effect/landmark - name = "landmark" - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - anchored = TRUE - unacidable = TRUE - simulated = FALSE - invisibility = 100 - var/delete_me = 0 - -/obj/effect/landmark/New() - ..() - tag = text("landmark*[]", name) - invisibility = 101 - - switch(name) //some of these are probably obsolete - if("monkey") - monkeystart += loc - delete_me = 1 - return - if("start") - newplayer_start += loc - delete_me = 1 - return - if("JoinLate") // Bit difference, since we need the spawn point to move. - latejoin += src - simulated = TRUE - // delete_me = 1 - return - if("JoinLateGateway") - latejoin_gateway += loc - latejoin += src //VOREStation Addition - delete_me = 1 - return - if("JoinLateElevator") - latejoin_elevator += loc - delete_me = 1 - return - if("JoinLateCryo") - latejoin_cryo += loc - delete_me = 1 - return - if("JoinLateCyborg") - latejoin_cyborg += loc - delete_me = 1 - return - if("prisonwarp") - prisonwarp += loc - delete_me = 1 - return - if("Holding Facility") - holdingfacility += loc - if("tdome1") - tdome1 += loc - if("tdome2") - tdome2 += loc - if("tdomeadmin") - tdomeadmin += loc - if("tdomeobserve") - tdomeobserve += loc - if("prisonsecuritywarp") - prisonsecuritywarp += loc - delete_me = 1 - return - if("blobstart") - blobstart += loc - delete_me = 1 - return - if("xeno_spawn") - xeno_spawn += loc - delete_me = 1 - return - if("endgame_exit") - endgame_safespawns += loc - delete_me = 1 - return - if("bluespacerift") - endgame_exits += loc - delete_me = 1 - return - //VOREStation Add Start - if("vinestart") - vinestart += loc - delete_me = 1 - return - //VORE Station Add End - - landmarks_list += src - return 1 - -/obj/effect/landmark/proc/delete() - delete_me = 1 - -/obj/effect/landmark/Initialize() - . = ..() - if(delete_me) - return INITIALIZE_HINT_QDEL - -/obj/effect/landmark/Destroy(var/force = FALSE) - if(delete_me || force) - landmarks_list -= src - return ..() - return QDEL_HINT_LETMELIVE - -/obj/effect/landmark/start - name = "start" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - anchored = TRUE - -/obj/effect/landmark/start/New() - ..() - tag = "start*[name]" - invisibility = 101 - - return 1 - -/obj/effect/landmark/forbidden_level - delete_me = 1 -/obj/effect/landmark/forbidden_level/Initialize() - . = ..() - if(using_map) - using_map.secret_levels |= z - else - log_error("[type] mapped in but no using_map") - -/obj/effect/landmark/hidden_level - delete_me = 1 -/obj/effect/landmark/hidden_level/Initialize() - . = ..() - if(using_map) - using_map.hidden_levels |= z - else - log_error("[type] mapped in but no using_map") - - -/obj/effect/landmark/virtual_reality - name = "virtual_reality" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - anchored = TRUE - -/obj/effect/landmark/virtual_reality/New() - ..() - tag = "virtual_reality*[name]" - invisibility = 101 - return 1 - - -//Costume spawner landmarks -/obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears - - var/list/options = typesof(/obj/effect/landmark/costume) - var/PICK= options[rand(1,options.len)] - new PICK(src.loc) - delete_me = 1 - -//SUBCLASSES. Spawn a bunch of items and disappear likewise -/obj/effect/landmark/costume/chicken/New() - new /obj/item/clothing/suit/chickensuit(src.loc) - new /obj/item/clothing/head/chicken(src.loc) - new /obj/item/weapon/reagent_containers/food/snacks/egg(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/gladiator/New() - new /obj/item/clothing/under/gladiator(src.loc) - new /obj/item/clothing/head/helmet/gladiator(src.loc) - qdel(src) - -/obj/effect/landmark/costume/madscientist/New() - new /obj/item/clothing/under/suit_jacket/green(src.loc) - new /obj/item/clothing/head/flatcap(src.loc) - new /obj/item/clothing/suit/storage/toggle/labcoat/mad(src.loc) - new /obj/item/clothing/glasses/gglasses(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/elpresidente/New() - new /obj/item/clothing/under/suit_jacket/green(src.loc) - new /obj/item/clothing/head/flatcap(src.loc) - new /obj/item/clothing/mask/smokable/cigarette/cigar/havana(src.loc) - new /obj/item/clothing/shoes/boots/jackboots(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/nyangirl/New() - new /obj/item/clothing/under/schoolgirl(src.loc) - new /obj/item/clothing/head/kitty(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/maid/New() - new /obj/item/clothing/under/skirt(src.loc) - var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears ) - new CHOICE(src.loc) - new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/butler/New() - new /obj/item/clothing/accessory/wcoat(src.loc) - new /obj/item/clothing/under/suit_jacket(src.loc) - new /obj/item/clothing/head/that(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/scratch/New() - new /obj/item/clothing/gloves/white(src.loc) - new /obj/item/clothing/shoes/white(src.loc) - new /obj/item/clothing/under/scratch(src.loc) - if (prob(30)) - new /obj/item/clothing/head/cueball(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/highlander/New() - new /obj/item/clothing/under/kilt(src.loc) - new /obj/item/clothing/head/beret(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/prig/New() - new /obj/item/clothing/accessory/wcoat(src.loc) - new /obj/item/clothing/glasses/monocle(src.loc) - var/CHOICE= pick( /obj/item/clothing/head/bowler, /obj/item/clothing/head/that) - new CHOICE(src.loc) - new /obj/item/clothing/shoes/black(src.loc) - new /obj/item/weapon/cane(src.loc) - new /obj/item/clothing/under/sl_suit(src.loc) - new /obj/item/clothing/mask/fakemoustache(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/plaguedoctor/New() - new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc) - new /obj/item/clothing/head/plaguedoctorhat(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/nightowl/New() - new /obj/item/clothing/under/owl(src.loc) - new /obj/item/clothing/mask/gas/owl_mask(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/waiter/New() - new /obj/item/clothing/under/waiter(src.loc) - var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears) - new CHOICE(src.loc) - new /obj/item/clothing/suit/storage/apron(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/pirate/New() - new /obj/item/clothing/under/pirate(src.loc) - new /obj/item/clothing/suit/pirate(src.loc) - var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana ) - new CHOICE(src.loc) - new /obj/item/clothing/glasses/eyepatch(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/commie/New() - new /obj/item/clothing/under/soviet(src.loc) - new /obj/item/clothing/head/ushanka(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/imperium_monk/New() - new /obj/item/clothing/suit/imperium_monk(src.loc) - if (prob(25)) - new /obj/item/clothing/mask/gas/cyborg(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/holiday_priest/New() - new /obj/item/clothing/suit/holidaypriest(src.loc) - qdel(src) - -/obj/effect/landmark/costume/marisawizard/fake/New() - new /obj/item/clothing/head/wizard/marisa/fake(src.loc) - new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/cutewitch/New() - new /obj/item/clothing/under/sundress(src.loc) - new /obj/item/clothing/head/witchwig(src.loc) - new /obj/item/weapon/staff/broom(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/fakewizard/New() - new /obj/item/clothing/suit/wizrobe/fake(src.loc) - new /obj/item/clothing/head/wizard/fake(src.loc) - new /obj/item/weapon/staff/(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/sexyclown/New() - new /obj/item/clothing/mask/gas/sexyclown(src.loc) - new /obj/item/clothing/under/sexyclown(src.loc) - delete_me = 1 - -/obj/effect/landmark/costume/sexymime/New() - new /obj/item/clothing/mask/gas/sexymime(src.loc) - new /obj/item/clothing/under/sexymime(src.loc) - delete_me = 1 +/obj/effect/landmark + name = "landmark" + icon = 'icons/mob/screen1.dmi' + icon_state = "x2" + anchored = TRUE + unacidable = TRUE + simulated = FALSE + invisibility = 100 + var/delete_me = 0 + +/obj/effect/landmark/New() + ..() + tag = text("landmark*[]", name) + invisibility = 101 + + switch(name) //some of these are probably obsolete + if("monkey") + monkeystart += loc + delete_me = 1 + return + if("start") + newplayer_start += loc + delete_me = 1 + return + if("JoinLate") // Bit difference, since we need the spawn point to move. + latejoin += src + simulated = TRUE + // delete_me = 1 + return + if("JoinLateGateway") + latejoin_gateway += loc + latejoin += src //VOREStation Addition + delete_me = 1 + return + if("JoinLateElevator") + latejoin_elevator += loc + delete_me = 1 + return + if("JoinLateCryo") + latejoin_cryo += loc + delete_me = 1 + return + if("JoinLateCyborg") + latejoin_cyborg += loc + delete_me = 1 + return + if("prisonwarp") + prisonwarp += loc + delete_me = 1 + return + if("Holding Facility") + holdingfacility += loc + if("tdome1") + tdome1 += loc + if("tdome2") + tdome2 += loc + if("tdomeadmin") + tdomeadmin += loc + if("tdomeobserve") + tdomeobserve += loc + if("prisonsecuritywarp") + prisonsecuritywarp += loc + delete_me = 1 + return + if("blobstart") + blobstart += loc + delete_me = 1 + return + if("xeno_spawn") + xeno_spawn += loc + delete_me = 1 + return + if("endgame_exit") + endgame_safespawns += loc + delete_me = 1 + return + if("bluespacerift") + endgame_exits += loc + delete_me = 1 + return + //VOREStation Add Start + if("vinestart") + vinestart += loc + delete_me = 1 + return + //VORE Station Add End + + landmarks_list += src + return 1 + +/obj/effect/landmark/proc/delete() + delete_me = 1 + +/obj/effect/landmark/Initialize() + . = ..() + if(delete_me) + return INITIALIZE_HINT_QDEL + +/obj/effect/landmark/Destroy(var/force = FALSE) + if(delete_me || force) + landmarks_list -= src + return ..() + return QDEL_HINT_LETMELIVE + +/obj/effect/landmark/start + name = "start" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + anchored = TRUE + +/obj/effect/landmark/start/New() + ..() + tag = "start*[name]" + invisibility = 101 + + return 1 + +/obj/effect/landmark/forbidden_level + delete_me = 1 +/obj/effect/landmark/forbidden_level/Initialize() + . = ..() + if(using_map) + using_map.secret_levels |= z + else + log_error("[type] mapped in but no using_map") + +/obj/effect/landmark/hidden_level + delete_me = 1 +/obj/effect/landmark/hidden_level/Initialize() + . = ..() + if(using_map) + using_map.hidden_levels |= z + else + log_error("[type] mapped in but no using_map") + + +/obj/effect/landmark/virtual_reality + name = "virtual_reality" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + anchored = TRUE + +/obj/effect/landmark/virtual_reality/New() + ..() + tag = "virtual_reality*[name]" + invisibility = 101 + return 1 + + +//Costume spawner landmarks +/obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears + + var/list/options = typesof(/obj/effect/landmark/costume) + var/PICK= options[rand(1,options.len)] + new PICK(src.loc) + delete_me = 1 + +//SUBCLASSES. Spawn a bunch of items and disappear likewise +/obj/effect/landmark/costume/chicken/New() + new /obj/item/clothing/suit/chickensuit(src.loc) + new /obj/item/clothing/head/chicken(src.loc) + new /obj/item/weapon/reagent_containers/food/snacks/egg(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/gladiator/New() + new /obj/item/clothing/under/gladiator(src.loc) + new /obj/item/clothing/head/helmet/gladiator(src.loc) + qdel(src) + +/obj/effect/landmark/costume/madscientist/New() + new /obj/item/clothing/under/suit_jacket/green(src.loc) + new /obj/item/clothing/head/flatcap(src.loc) + new /obj/item/clothing/suit/storage/toggle/labcoat/mad(src.loc) + new /obj/item/clothing/glasses/gglasses(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/elpresidente/New() + new /obj/item/clothing/under/suit_jacket/green(src.loc) + new /obj/item/clothing/head/flatcap(src.loc) + new /obj/item/clothing/mask/smokable/cigarette/cigar/havana(src.loc) + new /obj/item/clothing/shoes/boots/jackboots(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/nyangirl/New() + new /obj/item/clothing/under/schoolgirl(src.loc) + new /obj/item/clothing/head/kitty(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/maid/New() + new /obj/item/clothing/under/skirt(src.loc) + var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears ) + new CHOICE(src.loc) + new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/butler/New() + new /obj/item/clothing/accessory/wcoat(src.loc) + new /obj/item/clothing/under/suit_jacket(src.loc) + new /obj/item/clothing/head/that(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/scratch/New() + new /obj/item/clothing/gloves/white(src.loc) + new /obj/item/clothing/shoes/white(src.loc) + new /obj/item/clothing/under/scratch(src.loc) + if (prob(30)) + new /obj/item/clothing/head/cueball(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/highlander/New() + new /obj/item/clothing/under/kilt(src.loc) + new /obj/item/clothing/head/beret(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/prig/New() + new /obj/item/clothing/accessory/wcoat(src.loc) + new /obj/item/clothing/glasses/monocle(src.loc) + var/CHOICE= pick( /obj/item/clothing/head/bowler, /obj/item/clothing/head/that) + new CHOICE(src.loc) + new /obj/item/clothing/shoes/black(src.loc) + new /obj/item/weapon/cane(src.loc) + new /obj/item/clothing/under/sl_suit(src.loc) + new /obj/item/clothing/mask/fakemoustache(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/plaguedoctor/New() + new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc) + new /obj/item/clothing/head/plaguedoctorhat(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/nightowl/New() + new /obj/item/clothing/under/owl(src.loc) + new /obj/item/clothing/mask/gas/owl_mask(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/waiter/New() + new /obj/item/clothing/under/waiter(src.loc) + var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears) + new CHOICE(src.loc) + new /obj/item/clothing/suit/storage/apron(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/pirate/New() + new /obj/item/clothing/under/pirate(src.loc) + new /obj/item/clothing/suit/pirate(src.loc) + var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana ) + new CHOICE(src.loc) + new /obj/item/clothing/glasses/eyepatch(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/commie/New() + new /obj/item/clothing/under/soviet(src.loc) + new /obj/item/clothing/head/ushanka(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/imperium_monk/New() + new /obj/item/clothing/suit/imperium_monk(src.loc) + if (prob(25)) + new /obj/item/clothing/mask/gas/cyborg(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/holiday_priest/New() + new /obj/item/clothing/suit/holidaypriest(src.loc) + qdel(src) + +/obj/effect/landmark/costume/marisawizard/fake/New() + new /obj/item/clothing/head/wizard/marisa/fake(src.loc) + new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/cutewitch/New() + new /obj/item/clothing/under/sundress(src.loc) + new /obj/item/clothing/head/witchwig(src.loc) + new /obj/item/weapon/staff/broom(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/fakewizard/New() + new /obj/item/clothing/suit/wizrobe/fake(src.loc) + new /obj/item/clothing/head/wizard/fake(src.loc) + new /obj/item/weapon/staff/(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/sexyclown/New() + new /obj/item/clothing/mask/gas/sexyclown(src.loc) + new /obj/item/clothing/under/sexyclown(src.loc) + delete_me = 1 + +/obj/effect/landmark/costume/sexymime/New() + new /obj/item/clothing/mask/gas/sexymime(src.loc) + new /obj/item/clothing/under/sexymime(src.loc) + delete_me = 1 diff --git a/code/game/objects/effects/manifest.dm b/code/game/objects/effects/manifest.dm index 98d378faf5d..a6ec15c348e 100644 --- a/code/game/objects/effects/manifest.dm +++ b/code/game/objects/effects/manifest.dm @@ -1,21 +1,21 @@ -/obj/effect/manifest - name = "manifest" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - unacidable = TRUE//Just to be sure. - -/obj/effect/manifest/New() - - src.invisibility = 101 - return - -/obj/effect/manifest/proc/manifest() - var/dat = "Crew Manifest:
                    " - for(var/mob/living/carbon/human/M in mob_list) - dat += text(" [] - []
                    ", M.name, M.get_assignment()) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( src.loc ) - P.info = dat - P.name = "paper- 'Crew Manifest'" - //SN src = null - qdel(src) +/obj/effect/manifest + name = "manifest" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + unacidable = TRUE//Just to be sure. + +/obj/effect/manifest/New() + + src.invisibility = 101 + return + +/obj/effect/manifest/proc/manifest() + var/dat = "Crew Manifest:
                    " + for(var/mob/living/carbon/human/M in mob_list) + dat += text(" [] - []
                    ", M.name, M.get_assignment()) + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( src.loc ) + P.info = dat + P.name = "paper- 'Crew Manifest'" + //SN src = null + qdel(src) return \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/beam_point.dm b/code/game/objects/effects/map_effects/beam_point.dm index 6a023a0470f..e8973e5fca7 100644 --- a/code/game/objects/effects/map_effects/beam_point.dm +++ b/code/game/objects/effects/map_effects/beam_point.dm @@ -1,191 +1,191 @@ -GLOBAL_LIST_EMPTY(all_beam_points) - -// Creates and manages a beam attached to itself and another beam_point. -// You can do cool things with these such as moving the beam_point to move the beam, turning them on and off on a timer, triggered by external input, and more. -/obj/effect/map_effect/beam_point - name = "beam point" - icon_state = "beam_point" - - // General variables. - var/list/my_beams = list() // Instances of beams. Deleting one will kill the beam. - var/id = "A" // Two beam_points must share the same ID to be connected to each other. - var/max_beams = 10 // How many concurrent beams to seperate beam_points to have at once. Set to zero to only act as targets for other beam_points. - var/seek_range = 7 // How far to look for an end beam_point when not having a beam. Defaults to screen height/width. Make sure this is below beam_max_distance. - - // Controls how and when the beam is created. - var/make_beams_on_init = FALSE - var/use_timer = FALSE // Sadly not the /tg/ timers. - var/list/on_duration = list(2 SECONDS, 2 SECONDS, 2 SECONDS) // How long the beam should stay on for, if use_timer is true. Alternates between each duration in the list. - var/list/off_duration = list(3 SECONDS, 0.5 SECOND, 0.5 SECOND) // How long it should stay off for. List length is not needed to be the same as on_duration. - var/timer_on_index = 1 // Index to use for on_duration list. - var/timer_off_index = 1// Ditto, for off_duration list. - var/initial_delay = 0 // How long to wait before first turning on the beam, to sync beam times or create a specific pattern. - var/beam_creation_sound = null // Optional sound played when one or more beams are created. - var/beam_destruction_sound = null // Optional sound played when a beam is destroyed. - - // Beam datum arguments. - var/beam_icon = 'icons/effects/beam.dmi' // Icon file to use for beam visuals. - var/beam_icon_state = "b_beam" // Icon state to use for visuals. - var/beam_time = INFINITY // How long the beam lasts. By default it will last forever until destroyed. - var/beam_max_distance = 10 // If the beam is farther than this, it will be destroyed. Make sure it's higher than seek_range. - var/beam_type = /obj/effect/ebeam // The type of beam. Default has no special properties. Some others may do things like hurt things touching it. - var/beam_sleep_time = 3 // How often the beam updates visually. Suggested to leave this alone, 3 is already fast. - -/obj/effect/map_effect/beam_point/Initialize() - GLOB.all_beam_points += src - if(make_beams_on_init) - create_beams() - if(use_timer) - addtimer(CALLBACK(src, PROC_REF(handle_beam_timer)), initial_delay) - return ..() - -/obj/effect/map_effect/beam_point/Destroy() - destroy_all_beams() - use_timer = FALSE - GLOB.all_beam_points -= src - return ..() - -// This is the top level proc to make the magic happen. -/obj/effect/map_effect/beam_point/proc/create_beams() - if(my_beams.len >= max_beams) - return - var/beams_to_fill = max_beams - my_beams.len - for(var/i = 1 to beams_to_fill) - var/obj/effect/map_effect/beam_point/point = seek_beam_point() - if(!point) - break // No more points could be found, no point checking repeatively. - build_beam(point) - -// Finds a suitable beam point. -/obj/effect/map_effect/beam_point/proc/seek_beam_point() - for(var/obj/effect/map_effect/beam_point/point in GLOB.all_beam_points) - if(id != point.id) - continue // Not linked together by ID. - if(has_active_beam(point)) - continue // Already got one. - if(point.z != src.z) - continue // Not on same z-level. get_dist() ignores z-levels by design according to docs. - if(get_dist(src, point) > seek_range) - continue // Too far. - return point - -// Checks if the two points have an active beam between them. -// Used to make sure two points don't have more than one beam. -/obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them) - // First, check our beams. - for(var/datum/beam/B in my_beams) - if(B.target == them) - return TRUE - if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not. - return TRUE - - // Now check theirs, to see if they have a beam on us. - for(var/datum/beam/B in them.my_beams) - if(B.target == src) - return TRUE - if(B.origin == src) // Same story as above. - return TRUE - - return FALSE - -/obj/effect/map_effect/beam_point/proc/build_beam(var/atom/beam_target) - if(!beam_target) - log_debug("[src] ([src.type] \[[x],[y],[z]\]) failed to build its beam due to not having a target.") - return FALSE - - var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) - my_beams += new_beam - if(beam_creation_sound) - playsound(src, beam_creation_sound, 70, 1) - - return TRUE - -/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B) - if(!B) - log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam that does not exist.") - return FALSE - - if(!(B in my_beams)) - log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam it did not own.") - return FALSE - - my_beams -= B - qdel(B) - if(beam_destruction_sound) - playsound(src, beam_destruction_sound, 70, 1) - - return TRUE - -/obj/effect/map_effect/beam_point/proc/destroy_all_beams() - for(var/datum/beam/B in my_beams) - destroy_beam(B) - return TRUE - -// This code makes me sad. -/obj/effect/map_effect/beam_point/proc/handle_beam_timer() - if(!use_timer || QDELETED(src)) - return - - if(my_beams.len) // Currently on. - destroy_all_beams() - color = "#FF0000" - - timer_off_index++ - if(timer_off_index > off_duration.len) - timer_off_index = 1 - - spawn(off_duration[timer_off_index]) - .() - - else // Currently off. - // If nobody's around, keep the beams off to avoid wasteful beam process(), if they have one. - if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) - spawn(retry_delay) - .() - return - - create_beams() - color = "#00FF00" - - timer_on_index++ - if(timer_on_index > on_duration.len) - timer_on_index = 1 - - spawn(on_duration[timer_on_index]) - .() - - - -// Subtypes to use in maps and adminbuse. -// Remember, beam_points ONLY connect to other beam_points with the same id variable. - -// Creates the beam when instantiated and stays on until told otherwise. -/obj/effect/map_effect/beam_point/instant - make_beams_on_init = TRUE - -/obj/effect/map_effect/beam_point/instant/electric - beam_icon_state = "nzcrentrs_power" - beam_type = /obj/effect/ebeam/reactive/electric - beam_creation_sound = 'sound/effects/lightningshock.ogg' - beam_destruction_sound = "sparks" - -// Turns on and off on a timer. -/obj/effect/map_effect/beam_point/timer - use_timer = TRUE - -// Shocks people who touch the beam while it's on. Flicks on and off on a specific pattern. -/obj/effect/map_effect/beam_point/timer/electric - beam_icon_state = "nzcrentrs_power" - beam_type = /obj/effect/ebeam/reactive/electric - beam_creation_sound = 'sound/effects/lightningshock.ogg' - beam_destruction_sound = "sparks" - seek_range = 3 - -// Is only a target for other beams to connect to. -/obj/effect/map_effect/beam_point/end - max_beams = 0 - -// Can only have one beam. -/obj/effect/map_effect/beam_point/mono - make_beams_on_init = TRUE - max_beams = 1 +GLOBAL_LIST_EMPTY(all_beam_points) + +// Creates and manages a beam attached to itself and another beam_point. +// You can do cool things with these such as moving the beam_point to move the beam, turning them on and off on a timer, triggered by external input, and more. +/obj/effect/map_effect/beam_point + name = "beam point" + icon_state = "beam_point" + + // General variables. + var/list/my_beams = list() // Instances of beams. Deleting one will kill the beam. + var/id = "A" // Two beam_points must share the same ID to be connected to each other. + var/max_beams = 10 // How many concurrent beams to seperate beam_points to have at once. Set to zero to only act as targets for other beam_points. + var/seek_range = 7 // How far to look for an end beam_point when not having a beam. Defaults to screen height/width. Make sure this is below beam_max_distance. + + // Controls how and when the beam is created. + var/make_beams_on_init = FALSE + var/use_timer = FALSE // Sadly not the /tg/ timers. + var/list/on_duration = list(2 SECONDS, 2 SECONDS, 2 SECONDS) // How long the beam should stay on for, if use_timer is true. Alternates between each duration in the list. + var/list/off_duration = list(3 SECONDS, 0.5 SECOND, 0.5 SECOND) // How long it should stay off for. List length is not needed to be the same as on_duration. + var/timer_on_index = 1 // Index to use for on_duration list. + var/timer_off_index = 1// Ditto, for off_duration list. + var/initial_delay = 0 // How long to wait before first turning on the beam, to sync beam times or create a specific pattern. + var/beam_creation_sound = null // Optional sound played when one or more beams are created. + var/beam_destruction_sound = null // Optional sound played when a beam is destroyed. + + // Beam datum arguments. + var/beam_icon = 'icons/effects/beam.dmi' // Icon file to use for beam visuals. + var/beam_icon_state = "b_beam" // Icon state to use for visuals. + var/beam_time = INFINITY // How long the beam lasts. By default it will last forever until destroyed. + var/beam_max_distance = 10 // If the beam is farther than this, it will be destroyed. Make sure it's higher than seek_range. + var/beam_type = /obj/effect/ebeam // The type of beam. Default has no special properties. Some others may do things like hurt things touching it. + var/beam_sleep_time = 3 // How often the beam updates visually. Suggested to leave this alone, 3 is already fast. + +/obj/effect/map_effect/beam_point/Initialize() + GLOB.all_beam_points += src + if(make_beams_on_init) + create_beams() + if(use_timer) + addtimer(CALLBACK(src, PROC_REF(handle_beam_timer)), initial_delay) + return ..() + +/obj/effect/map_effect/beam_point/Destroy() + destroy_all_beams() + use_timer = FALSE + GLOB.all_beam_points -= src + return ..() + +// This is the top level proc to make the magic happen. +/obj/effect/map_effect/beam_point/proc/create_beams() + if(my_beams.len >= max_beams) + return + var/beams_to_fill = max_beams - my_beams.len + for(var/i = 1 to beams_to_fill) + var/obj/effect/map_effect/beam_point/point = seek_beam_point() + if(!point) + break // No more points could be found, no point checking repeatively. + build_beam(point) + +// Finds a suitable beam point. +/obj/effect/map_effect/beam_point/proc/seek_beam_point() + for(var/obj/effect/map_effect/beam_point/point in GLOB.all_beam_points) + if(id != point.id) + continue // Not linked together by ID. + if(has_active_beam(point)) + continue // Already got one. + if(point.z != src.z) + continue // Not on same z-level. get_dist() ignores z-levels by design according to docs. + if(get_dist(src, point) > seek_range) + continue // Too far. + return point + +// Checks if the two points have an active beam between them. +// Used to make sure two points don't have more than one beam. +/obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them) + // First, check our beams. + for(var/datum/beam/B in my_beams) + if(B.target == them) + return TRUE + if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not. + return TRUE + + // Now check theirs, to see if they have a beam on us. + for(var/datum/beam/B in them.my_beams) + if(B.target == src) + return TRUE + if(B.origin == src) // Same story as above. + return TRUE + + return FALSE + +/obj/effect/map_effect/beam_point/proc/build_beam(var/atom/beam_target) + if(!beam_target) + log_debug("[src] ([src.type] \[[x],[y],[z]\]) failed to build its beam due to not having a target.") + return FALSE + + var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) + my_beams += new_beam + if(beam_creation_sound) + playsound(src, beam_creation_sound, 70, 1) + + return TRUE + +/obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B) + if(!B) + log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam that does not exist.") + return FALSE + + if(!(B in my_beams)) + log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam it did not own.") + return FALSE + + my_beams -= B + qdel(B) + if(beam_destruction_sound) + playsound(src, beam_destruction_sound, 70, 1) + + return TRUE + +/obj/effect/map_effect/beam_point/proc/destroy_all_beams() + for(var/datum/beam/B in my_beams) + destroy_beam(B) + return TRUE + +// This code makes me sad. +/obj/effect/map_effect/beam_point/proc/handle_beam_timer() + if(!use_timer || QDELETED(src)) + return + + if(my_beams.len) // Currently on. + destroy_all_beams() + color = "#FF0000" + + timer_off_index++ + if(timer_off_index > off_duration.len) + timer_off_index = 1 + + spawn(off_duration[timer_off_index]) + .() + + else // Currently off. + // If nobody's around, keep the beams off to avoid wasteful beam process(), if they have one. + if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) + spawn(retry_delay) + .() + return + + create_beams() + color = "#00FF00" + + timer_on_index++ + if(timer_on_index > on_duration.len) + timer_on_index = 1 + + spawn(on_duration[timer_on_index]) + .() + + + +// Subtypes to use in maps and adminbuse. +// Remember, beam_points ONLY connect to other beam_points with the same id variable. + +// Creates the beam when instantiated and stays on until told otherwise. +/obj/effect/map_effect/beam_point/instant + make_beams_on_init = TRUE + +/obj/effect/map_effect/beam_point/instant/electric + beam_icon_state = "nzcrentrs_power" + beam_type = /obj/effect/ebeam/reactive/electric + beam_creation_sound = 'sound/effects/lightningshock.ogg' + beam_destruction_sound = "sparks" + +// Turns on and off on a timer. +/obj/effect/map_effect/beam_point/timer + use_timer = TRUE + +// Shocks people who touch the beam while it's on. Flicks on and off on a specific pattern. +/obj/effect/map_effect/beam_point/timer/electric + beam_icon_state = "nzcrentrs_power" + beam_type = /obj/effect/ebeam/reactive/electric + beam_creation_sound = 'sound/effects/lightningshock.ogg' + beam_destruction_sound = "sparks" + seek_range = 3 + +// Is only a target for other beams to connect to. +/obj/effect/map_effect/beam_point/end + max_beams = 0 + +// Can only have one beam. +/obj/effect/map_effect/beam_point/mono + make_beams_on_init = TRUE + max_beams = 1 diff --git a/code/game/objects/effects/map_effects/map_effects.dm b/code/game/objects/effects/map_effects/map_effects.dm index d5bad866620..ef1099d4b42 100644 --- a/code/game/objects/effects/map_effects/map_effects.dm +++ b/code/game/objects/effects/map_effects/map_effects.dm @@ -1,69 +1,69 @@ -// These are objects you can use inside special maps (like PoIs), or for adminbuse. -// Players cannot see or interact with these. -/obj/effect/map_effect - anchored = TRUE - invisibility = 99 // So a badmin can go view these by changing their see_invisible. - icon = 'icons/effects/map_effects.dmi' - - // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. - var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it. - var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run. - var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement. - var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well. - var/retry_delay = 5 SECONDS // How long until we check for players again. - var/next_attempt = 0 // Next time we're going to do ACTUAL WORK - -/obj/effect/map_effect/ex_act() - return - -/obj/effect/map_effect/singularity_pull() - return - -/obj/effect/map_effect/singularity_act() - return - -// Base type for effects that run on variable intervals. -/obj/effect/map_effect/interval - var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. - var/interval_upper_bound = 5 SECONDS // Higher number for above. - -/obj/effect/map_effect/interval/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/effect/map_effect/interval/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -// Override this for the specific thing to do. -/obj/effect/map_effect/interval/proc/trigger() - return - -// Handles the delay and making sure it doesn't run when it would be bad. -/obj/effect/map_effect/interval/process() - //Not yet! - if(world.time < next_attempt) - return - - // Check to see if we're useful first. - if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) - next_attempt = world.time + retry_delay - // Hey there's someone nearby. - else - next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) - trigger() - -// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. -/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE) - if(!proximity_to) - return FALSE - - for(var/thing in player_list) - var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways. - if(ignore_ghosts && isobserver(M)) - continue - if(ignore_afk && M.client && M.client.is_afk(5 MINUTES)) - continue - if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius) - return TRUE - return FALSE +// These are objects you can use inside special maps (like PoIs), or for adminbuse. +// Players cannot see or interact with these. +/obj/effect/map_effect + anchored = TRUE + invisibility = 99 // So a badmin can go view these by changing their see_invisible. + icon = 'icons/effects/map_effects.dmi' + + // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. + var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it. + var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run. + var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement. + var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well. + var/retry_delay = 5 SECONDS // How long until we check for players again. + var/next_attempt = 0 // Next time we're going to do ACTUAL WORK + +/obj/effect/map_effect/ex_act() + return + +/obj/effect/map_effect/singularity_pull() + return + +/obj/effect/map_effect/singularity_act() + return + +// Base type for effects that run on variable intervals. +/obj/effect/map_effect/interval + var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. + var/interval_upper_bound = 5 SECONDS // Higher number for above. + +/obj/effect/map_effect/interval/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/map_effect/interval/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +// Override this for the specific thing to do. +/obj/effect/map_effect/interval/proc/trigger() + return + +// Handles the delay and making sure it doesn't run when it would be bad. +/obj/effect/map_effect/interval/process() + //Not yet! + if(world.time < next_attempt) + return + + // Check to see if we're useful first. + if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) + next_attempt = world.time + retry_delay + // Hey there's someone nearby. + else + next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) + trigger() + +// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. +/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE) + if(!proximity_to) + return FALSE + + for(var/thing in player_list) + var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways. + if(ignore_ghosts && isobserver(M)) + continue + if(ignore_afk && M.client && M.client.is_afk(5 MINUTES)) + continue + if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius) + return TRUE + return FALSE diff --git a/code/game/objects/effects/map_effects/perma_light.dm b/code/game/objects/effects/map_effects/perma_light.dm index 35eef6d6b0b..d2e9caa9244 100644 --- a/code/game/objects/effects/map_effects/perma_light.dm +++ b/code/game/objects/effects/map_effects/perma_light.dm @@ -1,39 +1,39 @@ -// Emits light forever with magic. Useful for mood lighting in Points of Interest. -// Be sure to check how it looks ingame, and fiddle with the settings until it looks right. -/obj/effect/map_effect/perma_light - name = "permanent light" - icon_state = "permalight" - - light_range = 3 - light_power = 1 - light_color = "#FFFFFF" - light_on = TRUE - -/obj/effect/map_effect/perma_light/brighter - name = "permanent light (bright)" - icon_state = "permalight" - - light_range = 5 - light_power = 3 - light_color = "#FFFFFF" - -/obj/effect/map_effect/perma_light/concentrated - name = "permanent light (concentrated)" - - light_range = 2 - light_power = 5 - -/obj/effect/map_effect/perma_light/concentrated/incandescent - name = "permanent light (concentrated incandescent)" - - light_color = LIGHT_COLOR_INCANDESCENT_TUBE - -// VOREStation Addition Start -/obj/effect/map_effect/perma_light/gateway - name = "permanent light (gateway)" - icon_state = "permalight" - - light_range = 10 - light_power = 5 - light_color = "#b6cdff" +// Emits light forever with magic. Useful for mood lighting in Points of Interest. +// Be sure to check how it looks ingame, and fiddle with the settings until it looks right. +/obj/effect/map_effect/perma_light + name = "permanent light" + icon_state = "permalight" + + light_range = 3 + light_power = 1 + light_color = "#FFFFFF" + light_on = TRUE + +/obj/effect/map_effect/perma_light/brighter + name = "permanent light (bright)" + icon_state = "permalight" + + light_range = 5 + light_power = 3 + light_color = "#FFFFFF" + +/obj/effect/map_effect/perma_light/concentrated + name = "permanent light (concentrated)" + + light_range = 2 + light_power = 5 + +/obj/effect/map_effect/perma_light/concentrated/incandescent + name = "permanent light (concentrated incandescent)" + + light_color = LIGHT_COLOR_INCANDESCENT_TUBE + +// VOREStation Addition Start +/obj/effect/map_effect/perma_light/gateway + name = "permanent light (gateway)" + icon_state = "permalight" + + light_range = 10 + light_power = 5 + light_color = "#b6cdff" // VOREStation Addition End \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/portal.dm b/code/game/objects/effects/map_effects/portal.dm index ede5cbdacac..769c4bcc90e 100644 --- a/code/game/objects/effects/map_effects/portal.dm +++ b/code/game/objects/effects/map_effects/portal.dm @@ -51,7 +51,7 @@ when portals are shortly lived, or when portals are made to be obvious with spec plane = TURF_PLANE layer = ABOVE_TURF_LAYER appearance_flags = NONE - + var/obj/effect/map_effect/portal/counterpart = null // The portal line or master that this is connected to, on the 'other side'. // Information used to apply `pixel_[x|y]` offsets so that the visuals line up. @@ -93,7 +93,7 @@ when portals are shortly lived, or when portals are made to be obvious with spec // pulled.forceMove(get_turf(counterpart)) pulled.forceMove(counterpart.get_focused_turf()) L.forceMove(counterpart.get_focused_turf()) - L.start_pulling(pulled) + L.continue_pulling(pulled) else L.forceMove(counterpart.get_focused_turf()) else diff --git a/code/game/objects/effects/map_effects/radiation_emitter.dm b/code/game/objects/effects/map_effects/radiation_emitter.dm index 8abf946556d..89a8d194f53 100644 --- a/code/game/objects/effects/map_effects/radiation_emitter.dm +++ b/code/game/objects/effects/map_effects/radiation_emitter.dm @@ -1,19 +1,19 @@ -// Constantly emites radiation from the tile it's placed on. -/obj/effect/map_effect/radiation_emitter - name = "radiation emitter" - icon_state = "radiation_emitter" - var/radiation_power = 30 // Bigger numbers means more radiation. - -/obj/effect/map_effect/radiation_emitter/Initialize() - START_PROCESSING(SSobj, src) - return ..() - -/obj/effect/map_effect/radiation_emitter/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/map_effect/radiation_emitter/process() - SSradiation.radiate(src, radiation_power) - +// Constantly emites radiation from the tile it's placed on. +/obj/effect/map_effect/radiation_emitter + name = "radiation emitter" + icon_state = "radiation_emitter" + var/radiation_power = 30 // Bigger numbers means more radiation. + +/obj/effect/map_effect/radiation_emitter/Initialize() + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/map_effect/radiation_emitter/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/map_effect/radiation_emitter/process() + SSradiation.radiate(src, radiation_power) + /obj/effect/map_effect/radiation_emitter/strong radiation_power = 100 \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/screen_shaker.dm b/code/game/objects/effects/map_effects/screen_shaker.dm index c8f5d413bd8..15a83b4c3d6 100644 --- a/code/game/objects/effects/map_effects/screen_shaker.dm +++ b/code/game/objects/effects/map_effects/screen_shaker.dm @@ -1,17 +1,17 @@ -// Makes the screen shake for nearby players every so often. -/obj/effect/map_effect/interval/screen_shaker - name = "screen shaker" - icon_state = "screen_shaker" - - interval_lower_bound = 1 SECOND - interval_upper_bound = 2 SECONDS - - var/shake_radius = 7 // How far the shaking effect extends to. By default it is one screen length. - var/shake_duration = 2 // How long the shaking lasts. - var/shake_strength = 1 // How much it shakes. - -/obj/effect/map_effect/interval/screen_shaker/trigger() - for(var/mob/M as anything in player_list) - if(M.z == src.z && get_dist(src, M) <= shake_radius) - shake_camera(M, shake_duration, shake_strength) +// Makes the screen shake for nearby players every so often. +/obj/effect/map_effect/interval/screen_shaker + name = "screen shaker" + icon_state = "screen_shaker" + + interval_lower_bound = 1 SECOND + interval_upper_bound = 2 SECONDS + + var/shake_radius = 7 // How far the shaking effect extends to. By default it is one screen length. + var/shake_duration = 2 // How long the shaking lasts. + var/shake_strength = 1 // How much it shakes. + +/obj/effect/map_effect/interval/screen_shaker/trigger() + for(var/mob/M as anything in player_list) + if(M.z == src.z && get_dist(src, M) <= shake_radius) + shake_camera(M, shake_duration, shake_strength) ..() \ No newline at end of file diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 93a2ec408cd..74a7e624b83 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -1,404 +1,404 @@ -/obj/effect/mine - name = "land mine" //The name and description are deliberately NOT modified, so you can't game the mines you find. - desc = "A small explosive land mine." - density = FALSE - anchored = TRUE - icon = 'icons/obj/weapons.dmi' - icon_state = "landmine" - var/triggered = 0 - var/smoke_strength = 3 - var/obj/item/weapon/mine/mineitemtype = /obj/item/weapon/mine - var/panel_open = FALSE - var/datum/wires/mines/wires = null - var/camo_net = FALSE // Will the mine 'cloak' on deployment? - - // The trap item will be triggered in some manner when detonating. Default only checks for grenades. - var/obj/item/trap = null - -/obj/effect/mine/Initialize() - icon_state = "landmine_armed" - wires = new(src) - . = ..() - if(ispath(trap)) - trap = new trap(src) - register_dangerous_to_step() - if(camo_net) - alpha = 50 - -/obj/effect/mine/Destroy() - unregister_dangerous_to_step() - if(trap) - QDEL_NULL(trap) - qdel_null(wires) - return ..() - -/obj/effect/mine/Moved(atom/oldloc) - . = ..() - if(.) - var/turf/old_turf = get_turf(oldloc) - var/turf/new_turf = get_turf(src) - if(old_turf != new_turf) - old_turf.unregister_dangerous_object(src) - new_turf.register_dangerous_object(src) - -/obj/effect/mine/proc/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - - if(trap) - trigger_trap(M) - visible_message("\The [src.name] flashes as it is triggered!") - - else - explosion(loc, 0, 2, 3, 4) //land mines are dangerous, folks. - visible_message("\The [src.name] detonates!") - - qdel(s) - qdel(src) - -/obj/effect/mine/proc/trigger_trap(var/mob/living/victim) - if(istype(trap, /obj/item/weapon/grenade)) - var/obj/item/weapon/grenade/G = trap - trap = null - G.forceMove(get_turf(src)) - if(victim.ckey) - msg_admin_attack("[key_name_admin(victim)] stepped on \a [src.name], triggering [trap]") - G.activate() - - if(istype(trap, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TV = trap - trap = null - TV.forceMove(get_turf(src)) - TV.toggle_valve() - -/obj/effect/mine/bullet_act() - if(prob(50)) - explode() - -/obj/effect/mine/ex_act(severity) - if(severity <= 2 || prob(50)) - explode() - ..() - -/obj/effect/mine/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - Bumped(AM) - -/obj/effect/mine/Bumped(mob/M as mob|obj) - - if(triggered) - return - - if(istype(M, /obj/mecha)) - explode(M) - - if(istype(M, /mob/living/)) - var/mob/living/mob = M - if(!mob.hovering || !mob.flying) - explode(M) - -/obj/effect/mine/attackby(obj/item/W as obj, mob/living/user as mob) - if(W.is_screwdriver()) - panel_open = !panel_open - user.visible_message("[user] very carefully screws the mine's panel [panel_open ? "open" : "closed"].", - "You very carefully screw the mine's panel [panel_open ? "open" : "closed"].") - playsound(src, W.usesound, 50, 1) - - // Panel open, stay uncloaked, or uncloak if already cloaked. If you don't cloak on place, ignore it and just be normal alpha. - alpha = camo_net ? (panel_open ? 255 : 50) : 255 - - else if((W.is_wirecutter() || istype(W, /obj/item/device/multitool)) && panel_open) - interact(user) - else - ..() - -/obj/effect/mine/interact(mob/living/user as mob) - if(!panel_open || istype(user, /mob/living/silicon/ai)) - return - user.set_machine(src) - wires.Interact(user) - -/obj/effect/mine/camo - camo_net = TRUE - -/obj/effect/mine/dnascramble - mineitemtype = /obj/item/weapon/mine/dnascramble - -/obj/effect/mine/dnascramble/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - if(istype(M)) - M.radiation += 50 - randmutb(M) - domutcheck(M,null) - visible_message("\The [src.name] flashes violently before disintegrating!") - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/stun - mineitemtype = /obj/item/weapon/mine/stun - -/obj/effect/mine/stun/explode(var/mob/living/M) - triggered = 1 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(3, 1, src) - s.start() - if(istype(M)) - M.Stun(30) - visible_message("\The [src.name] flashes violently before disintegrating!") - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/n2o - mineitemtype = /obj/item/weapon/mine/n2o - -/obj/effect/mine/n2o/explode(var/mob/living/M) - triggered = 1 - for (var/turf/simulated/floor/target in range(1,src)) - if(!target.blocks_air) - target.assume_gas("nitrous_oxide", 30) - visible_message("\The [src.name] detonates!") - spawn(0) - qdel(src) - -/obj/effect/mine/phoron - mineitemtype = /obj/item/weapon/mine/phoron - -/obj/effect/mine/phoron/explode(var/mob/living/M) - triggered = 1 - for (var/turf/simulated/floor/target in range(1,src)) - if(!target.blocks_air) - target.assume_gas("phoron", 30) - target.hotspot_expose(1000, CELL_VOLUME) - visible_message("\The [src.name] detonates!") - spawn(0) - qdel(src) - -/obj/effect/mine/kick - mineitemtype = /obj/item/weapon/mine/kick - -/obj/effect/mine/kick/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - if(istype(M, /obj/mecha)) - var/obj/mecha/E = M - M = E.occupant - if(istype(M)) - qdel(M.client) - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/frag - mineitemtype = /obj/item/weapon/mine/frag - var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) - var/num_fragments = 20 //total number of fragments produced by the grenade - //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern - var/spread_range = 7 - -/obj/effect/mine/frag/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - var/turf/O = get_turf(src) - if(!O) - return - src.fragmentate(O, 20, 7, list(/obj/item/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it - visible_message("\The [src.name] detonates!") - spawn(0) - qdel(s) - qdel(src) - -/obj/effect/mine/training //Name and Desc commented out so it's possible to trick people with the training mines -// name = "training mine" -// desc = "A mine with its payload removed, for EOD training and demonstrations." - mineitemtype = /obj/item/weapon/mine/training - -/obj/effect/mine/training/explode(var/mob/living/M) - triggered = 1 - visible_message("\The [src.name]'s light flashes rapidly as it 'explodes'.") - new src.mineitemtype(get_turf(src)) - spawn(0) - qdel(src) - -/obj/effect/mine/emp - mineitemtype = /obj/item/weapon/mine/emp - -/obj/effect/mine/emp/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(3, 1, src) - s.start() - visible_message("\The [src.name] flashes violently before disintegrating!") - empulse(loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade - spawn(0) - qdel(src) - -/obj/effect/mine/emp/camo - camo_net = TRUE - -/obj/effect/mine/incendiary - mineitemtype = /obj/item/weapon/mine/incendiary - -/obj/effect/mine/incendiary/explode(var/mob/living/M) - triggered = 1 - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - s.set_up(3, 1, src) - s.start() - if(istype(M)) - M.adjust_fire_stacks(5) - M.fire_act() - visible_message("\The [src.name] bursts into flames!") - spawn(0) - qdel(src) - -/obj/effect/mine/gadget - mineitemtype = /obj/item/weapon/mine/gadget - -/obj/effect/mine/gadget/explode(var/mob/living/M) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() - triggered = 1 - s.set_up(3, 1, src) - s.start() - - if(trap) - trigger_trap(M) - visible_message("\The [src.name] flashes as it is triggered!") - - else - explosion(loc, 0, 0, 2, 2) - visible_message("\The [src.name] detonates!") - - qdel(s) - qdel(src) - -///////////////////////////////////////////// -// The held item version of the above mines -///////////////////////////////////////////// -/obj/item/weapon/mine - name = "mine" - desc = "A small explosive mine with 'HE' and a grenade symbol on the side." - icon = 'icons/obj/weapons.dmi' - icon_state = "uglymine" - var/countdown = 10 - var/minetype = /obj/effect/mine //This MUST be an /obj/effect/mine type, or it'll runtime. - - var/obj/item/trap = null - - var/list/allowed_gadgets = null - -/obj/item/weapon/mine/attack_self(mob/user as mob) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times - add_fingerprint(user) - msg_admin_attack("[key_name_admin(user)] primed \a [src]") - user.visible_message("[user] starts priming \the [src.name].", "You start priming \the [src.name]. Hold still!") - if(do_after(user, 10 SECONDS)) - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - prime(user) - else - visible_message("[user] triggers \the [src.name]!", "You accidentally trigger \the [src.name]!") - prime(user, TRUE) - return - -/obj/item/weapon/mine/attackby(obj/item/W as obj, mob/living/user as mob) - if(W.is_screwdriver() && trap) - to_chat(user, "You begin removing \the [trap].") - if(do_after(user, 10 SECONDS)) - to_chat(user, "You finish disconnecting the mine's trigger.") - trap.forceMove(get_turf(src)) - trap = null - return - - if(LAZYLEN(allowed_gadgets) && !trap) - var/allowed = FALSE - - for(var/path in allowed_gadgets) - if(istype(W, path)) - allowed = TRUE - break - - if(allowed) - user.drop_from_inventory(W) - W.forceMove(src) - trap = W - - ..() - -/obj/item/weapon/mine/proc/prime(mob/user as mob, var/explode_now = FALSE) - visible_message("\The [src.name] beeps as the priming sequence completes.") - var/obj/effect/mine/R = new minetype(get_turf(src)) - src.transfer_fingerprints_to(R) - R.add_fingerprint(user) - if(trap) - R.trap = trap - trap = null - R.trap.forceMove(R) - if(explode_now) - R.explode(user) - spawn(0) - qdel(src) - -/obj/item/weapon/mine/dnascramble - name = "radiation mine" - desc = "A small explosive mine with a radiation symbol on the side." - minetype = /obj/effect/mine/dnascramble - -/obj/item/weapon/mine/phoron - name = "incendiary mine" - desc = "A small explosive mine with a fire symbol on the side." - minetype = /obj/effect/mine/phoron - -/obj/item/weapon/mine/kick - name = "kick mine" - desc = "Concentrated war crimes. Handle with care." - minetype = /obj/effect/mine/kick - -/obj/item/weapon/mine/n2o - name = "nitrous oxide mine" - desc = "A small explosive mine with three Z's on the side." - minetype = /obj/effect/mine/n2o - -/obj/item/weapon/mine/stun - name = "stun mine" - desc = "A small explosive mine with a lightning bolt symbol on the side." - minetype = /obj/effect/mine/stun - -/obj/item/weapon/mine/frag - name = "fragmentation mine" - desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." - minetype = /obj/effect/mine/frag - -/obj/item/weapon/mine/training - name = "training mine" - desc = "A mine with its payload removed, for EOD training and demonstrations." - minetype = /obj/effect/mine/training - -/obj/item/weapon/mine/emp - name = "emp mine" - desc = "A small explosive mine with a lightning bolt symbol on the side." - minetype = /obj/effect/mine/emp - -/obj/item/weapon/mine/incendiary - name = "incendiary mine" - desc = "A small explosive mine with a fire symbol on the side." - minetype = /obj/effect/mine/incendiary - -/obj/item/weapon/mine/gadget - name = "gadget mine" - desc = "A small pressure-triggered device. If no component is added, the internal release bolts will detonate in unison when triggered." - - allowed_gadgets = list(/obj/item/weapon/grenade, /obj/item/device/transfer_valve) - -// This tells AI mobs to not be dumb and step on mines willingly. -/obj/item/weapon/mine/is_safe_to_step(mob/living/L) - if(!L.hovering || !L.flying) - return FALSE - return ..() +/obj/effect/mine + name = "land mine" //The name and description are deliberately NOT modified, so you can't game the mines you find. + desc = "A small explosive land mine." + density = FALSE + anchored = TRUE + icon = 'icons/obj/weapons.dmi' + icon_state = "landmine" + var/triggered = 0 + var/smoke_strength = 3 + var/obj/item/weapon/mine/mineitemtype = /obj/item/weapon/mine + var/panel_open = FALSE + var/datum/wires/mines/wires = null + var/camo_net = FALSE // Will the mine 'cloak' on deployment? + + // The trap item will be triggered in some manner when detonating. Default only checks for grenades. + var/obj/item/trap = null + +/obj/effect/mine/Initialize() + icon_state = "landmine_armed" + wires = new(src) + . = ..() + if(ispath(trap)) + trap = new trap(src) + register_dangerous_to_step() + if(camo_net) + alpha = 50 + +/obj/effect/mine/Destroy() + unregister_dangerous_to_step() + if(trap) + QDEL_NULL(trap) + qdel_null(wires) + return ..() + +/obj/effect/mine/Moved(atom/oldloc) + . = ..() + if(.) + var/turf/old_turf = get_turf(oldloc) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +/obj/effect/mine/proc/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + + if(trap) + trigger_trap(M) + visible_message("\The [src.name] flashes as it is triggered!") + + else + explosion(loc, 0, 2, 3, 4) //land mines are dangerous, folks. + visible_message("\The [src.name] detonates!") + + qdel(s) + qdel(src) + +/obj/effect/mine/proc/trigger_trap(var/mob/living/victim) + if(istype(trap, /obj/item/weapon/grenade)) + var/obj/item/weapon/grenade/G = trap + trap = null + G.forceMove(get_turf(src)) + if(victim.ckey) + msg_admin_attack("[key_name_admin(victim)] stepped on \a [src.name], triggering [trap]") + G.activate() + + if(istype(trap, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TV = trap + trap = null + TV.forceMove(get_turf(src)) + TV.toggle_valve() + +/obj/effect/mine/bullet_act() + if(prob(50)) + explode() + +/obj/effect/mine/ex_act(severity) + if(severity <= 2 || prob(50)) + explode() + ..() + +/obj/effect/mine/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + Bumped(AM) + +/obj/effect/mine/Bumped(mob/M as mob|obj) + + if(triggered) + return + + if(istype(M, /obj/mecha)) + explode(M) + + if(istype(M, /mob/living/)) + var/mob/living/mob = M + if(!mob.hovering || !mob.flying) + explode(M) + +/obj/effect/mine/attackby(obj/item/W as obj, mob/living/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + panel_open = !panel_open + user.visible_message("[user] very carefully screws the mine's panel [panel_open ? "open" : "closed"].", + "You very carefully screw the mine's panel [panel_open ? "open" : "closed"].") + playsound(src, W.usesound, 50, 1) + + // Panel open, stay uncloaked, or uncloak if already cloaked. If you don't cloak on place, ignore it and just be normal alpha. + alpha = camo_net ? (panel_open ? 255 : 50) : 255 + + else if((W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) && panel_open) + interact(user) + else + ..() + +/obj/effect/mine/interact(mob/living/user as mob) + if(!panel_open || istype(user, /mob/living/silicon/ai)) + return + user.set_machine(src) + wires.Interact(user) + +/obj/effect/mine/camo + camo_net = TRUE + +/obj/effect/mine/dnascramble + mineitemtype = /obj/item/weapon/mine/dnascramble + +/obj/effect/mine/dnascramble/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + if(istype(M)) + M.radiation += 50 + randmutb(M) + domutcheck(M,null) + visible_message("\The [src.name] flashes violently before disintegrating!") + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/stun + mineitemtype = /obj/item/weapon/mine/stun + +/obj/effect/mine/stun/explode(var/mob/living/M) + triggered = 1 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(3, 1, src) + s.start() + if(istype(M)) + M.Stun(30) + visible_message("\The [src.name] flashes violently before disintegrating!") + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/n2o + mineitemtype = /obj/item/weapon/mine/n2o + +/obj/effect/mine/n2o/explode(var/mob/living/M) + triggered = 1 + for (var/turf/simulated/floor/target in range(1,src)) + if(!target.blocks_air) + target.assume_gas("nitrous_oxide", 30) + visible_message("\The [src.name] detonates!") + spawn(0) + qdel(src) + +/obj/effect/mine/phoron + mineitemtype = /obj/item/weapon/mine/phoron + +/obj/effect/mine/phoron/explode(var/mob/living/M) + triggered = 1 + for (var/turf/simulated/floor/target in range(1,src)) + if(!target.blocks_air) + target.assume_gas("phoron", 30) + target.hotspot_expose(1000, CELL_VOLUME) + visible_message("\The [src.name] detonates!") + spawn(0) + qdel(src) + +/obj/effect/mine/kick + mineitemtype = /obj/item/weapon/mine/kick + +/obj/effect/mine/kick/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + if(istype(M, /obj/mecha)) + var/obj/mecha/E = M + M = E.occupant + if(istype(M)) + qdel(M.client) + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/frag + mineitemtype = /obj/item/weapon/mine/frag + var/fragment_types = list(/obj/item/projectile/bullet/pellet/fragment) + var/num_fragments = 20 //total number of fragments produced by the grenade + //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern + var/spread_range = 7 + +/obj/effect/mine/frag/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + var/turf/O = get_turf(src) + if(!O) + return + src.fragmentate(O, 20, 7, list(/obj/item/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it + visible_message("\The [src.name] detonates!") + spawn(0) + qdel(s) + qdel(src) + +/obj/effect/mine/training //Name and Desc commented out so it's possible to trick people with the training mines +// name = "training mine" +// desc = "A mine with its payload removed, for EOD training and demonstrations." + mineitemtype = /obj/item/weapon/mine/training + +/obj/effect/mine/training/explode(var/mob/living/M) + triggered = 1 + visible_message("\The [src.name]'s light flashes rapidly as it 'explodes'.") + new src.mineitemtype(get_turf(src)) + spawn(0) + qdel(src) + +/obj/effect/mine/emp + mineitemtype = /obj/item/weapon/mine/emp + +/obj/effect/mine/emp/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(3, 1, src) + s.start() + visible_message("\The [src.name] flashes violently before disintegrating!") + empulse(loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade + spawn(0) + qdel(src) + +/obj/effect/mine/emp/camo + camo_net = TRUE + +/obj/effect/mine/incendiary + mineitemtype = /obj/item/weapon/mine/incendiary + +/obj/effect/mine/incendiary/explode(var/mob/living/M) + triggered = 1 + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + s.set_up(3, 1, src) + s.start() + if(istype(M)) + M.adjust_fire_stacks(5) + M.fire_act() + visible_message("\The [src.name] bursts into flames!") + spawn(0) + qdel(src) + +/obj/effect/mine/gadget + mineitemtype = /obj/item/weapon/mine/gadget + +/obj/effect/mine/gadget/explode(var/mob/living/M) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() + triggered = 1 + s.set_up(3, 1, src) + s.start() + + if(trap) + trigger_trap(M) + visible_message("\The [src.name] flashes as it is triggered!") + + else + explosion(loc, 0, 0, 2, 2) + visible_message("\The [src.name] detonates!") + + qdel(s) + qdel(src) + +///////////////////////////////////////////// +// The held item version of the above mines +///////////////////////////////////////////// +/obj/item/weapon/mine + name = "mine" + desc = "A small explosive mine with 'HE' and a grenade symbol on the side." + icon = 'icons/obj/weapons.dmi' + icon_state = "landmine" + var/countdown = 10 + var/minetype = /obj/effect/mine //This MUST be an /obj/effect/mine type, or it'll runtime. + + var/obj/item/trap = null + + var/list/allowed_gadgets = null + +/obj/item/weapon/mine/attack_self(mob/user as mob) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times + add_fingerprint(user) + msg_admin_attack("[key_name_admin(user)] primed \a [src]") + user.visible_message("[user] starts priming \the [src.name].", "You start priming \the [src.name]. Hold still!") + if(do_after(user, 10 SECONDS)) + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + prime(user) + else + visible_message("[user] triggers \the [src.name]!", "You accidentally trigger \the [src.name]!") + prime(user, TRUE) + return + +/obj/item/weapon/mine/attackby(obj/item/W as obj, mob/living/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER) && trap) + to_chat(user, "You begin removing \the [trap].") + if(do_after(user, 10 SECONDS)) + to_chat(user, "You finish disconnecting the mine's trigger.") + trap.forceMove(get_turf(src)) + trap = null + return + + if(LAZYLEN(allowed_gadgets) && !trap) + var/allowed = FALSE + + for(var/path in allowed_gadgets) + if(istype(W, path)) + allowed = TRUE + break + + if(allowed) + user.drop_from_inventory(W) + W.forceMove(src) + trap = W + + ..() + +/obj/item/weapon/mine/proc/prime(mob/user as mob, var/explode_now = FALSE) + visible_message("\The [src.name] beeps as the priming sequence completes.") + var/obj/effect/mine/R = new minetype(get_turf(src)) + src.transfer_fingerprints_to(R) + R.add_fingerprint(user) + if(trap) + R.trap = trap + trap = null + R.trap.forceMove(R) + if(explode_now) + R.explode(user) + spawn(0) + qdel(src) + +/obj/item/weapon/mine/dnascramble + name = "radiation mine" + desc = "A small explosive mine with a radiation symbol on the side." + minetype = /obj/effect/mine/dnascramble + +/obj/item/weapon/mine/phoron + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + minetype = /obj/effect/mine/phoron + +/obj/item/weapon/mine/kick + name = "kick mine" + desc = "Concentrated war crimes. Handle with care." + minetype = /obj/effect/mine/kick + +/obj/item/weapon/mine/n2o + name = "nitrous oxide mine" + desc = "A small explosive mine with three Z's on the side." + minetype = /obj/effect/mine/n2o + +/obj/item/weapon/mine/stun + name = "stun mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + minetype = /obj/effect/mine/stun + +/obj/item/weapon/mine/frag + name = "fragmentation mine" + desc = "A small explosive mine with 'FRAG' and a grenade symbol on the side." + minetype = /obj/effect/mine/frag + +/obj/item/weapon/mine/training + name = "training mine" + desc = "A mine with its payload removed, for EOD training and demonstrations." + minetype = /obj/effect/mine/training + +/obj/item/weapon/mine/emp + name = "emp mine" + desc = "A small explosive mine with a lightning bolt symbol on the side." + minetype = /obj/effect/mine/emp + +/obj/item/weapon/mine/incendiary + name = "incendiary mine" + desc = "A small explosive mine with a fire symbol on the side." + minetype = /obj/effect/mine/incendiary + +/obj/item/weapon/mine/gadget + name = "gadget mine" + desc = "A small pressure-triggered device. If no component is added, the internal release bolts will detonate in unison when triggered." + + allowed_gadgets = list(/obj/item/weapon/grenade, /obj/item/device/transfer_valve) + +// This tells AI mobs to not be dumb and step on mines willingly. +/obj/item/weapon/mine/is_safe_to_step(mob/living/L) + if(!L.hovering || !L.flying) + return FALSE + return ..() diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index cd517098dc3..d51d769d568 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -1,152 +1,152 @@ -//The effect when you wrap a dead body in gift wrap -/obj/effect/spresent - name = "strange present" - desc = "It's a ... present?" - icon = 'icons/obj/items.dmi' - icon_state = "strangepresent" - density = TRUE - anchored = FALSE - -/obj/effect/temporary_effect - name = "self deleting effect" - desc = "How are you examining what which cannot be seen?" - icon = 'icons/effects/effects.dmi' - invisibility = 0 - var/time_to_die = 10 SECONDS // Afer which, it will delete itself. - -/obj/effect/temporary_effect/Initialize() - . = ..() - if(time_to_die) - QDEL_IN(src, time_to_die) - -// Shown really briefly when attacking with axes. -/obj/effect/temporary_effect/cleave_attack - name = "cleaving attack" - desc = "Something swinging really wide." - icon = 'icons/effects/96x96.dmi' - icon_state = "cleave" - plane = ABOVE_MOB_PLANE - layer = ABOVE_MOB_LAYER - time_to_die = 6 - alpha = 140 - mouse_opacity = 0 - pixel_x = -32 - pixel_y = -32 - -/obj/effect/temporary_effect/cleave_attack/Initialize() // Makes the slash fade smoothly. When completely transparent it should qdel itself. - . = ..() - animate(src, alpha = 0, time = time_to_die - 1) - -/obj/effect/temporary_effect/shuttle_landing - name = "shuttle landing" - desc = "You better move if you don't want to go splat!" - //VOREStation Edit Start - icon = 'icons/goonstation/featherzone.dmi' - icon_state = "hazard-corners" - time_to_die = 5 SECONDS - plane = PLANE_LIGHTING_ABOVE - //VOREStation Edit End - -// The manifestation of Zeus's might. Or just a really unlucky day. -// This is purely a visual effect, this isn't the part of the code that hurts things. -/obj/effect/temporary_effect/lightning_strike - name = "lightning" - desc = "How shocked you must be, to see this text. You must have lightning reflexes. \ - The humor in this description is just so electrifying." - icon = 'icons/effects/96x256.dmi' - icon_state = "lightning_strike" - plane = PLANE_LIGHTING_ABOVE - time_to_die = 1 SECOND - pixel_x = -32 - -/obj/effect/temporary_effect/lightning_strike/Initialize() - icon_state += "[rand(1,2)]" // To have two variants of lightning sprites. - animate(src, alpha = 0, time = time_to_die - 1) - . = ..() - -/obj/effect/dummy/lighting_obj - name = "lighting fx obj" - desc = "Tell a coder if you're seeing this." - icon_state = "nothing" - light_system = MOVABLE_LIGHT - light_range = MINIMUM_USEFUL_LIGHT_RANGE - light_color = COLOR_WHITE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - light_on = TRUE - blocks_emissive = FALSE - -/obj/effect/dummy/lighting_obj/Initialize(mapload, _range, _power, _color, _duration) - . = ..() - if(!isnull(_range)) - set_light_range(_range) - if(!isnull(_power)) - set_light_power(_power) - if(!isnull(_color)) - set_light_color(_color) - if(_duration) - QDEL_IN(src, _duration) - -/obj/effect/dummy/lighting_obj/moblight - name = "mob lighting fx" - -/obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, _color, _range, _power, _duration) - . = ..() - if(!ismob(loc)) - return INITIALIZE_HINT_QDEL - -/obj/effect/dummy/lighting_obj/moblight/fire - name = "fire" - light_color = LIGHT_COLOR_FIRE - light_range = LIGHT_RANGE_FIRE - -/obj/effect/abstract - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - plane = ABOVE_MOB_PLANE - -/obj/effect/abstract/light_spot - icon = 'icons/effects/eris_flashlight.dmi' - icon_state = "medium" - pixel_x = -16 - pixel_y = -16 - -/obj/effect/abstract/directional_lighting - var/obj/effect/abstract/light_spot/light_spot = new - var/trans_angle - var/icon_dist - -/obj/effect/abstract/directional_lighting/Initialize() - . = ..() - vis_contents += light_spot - -/obj/effect/abstract/directional_lighting/proc/face_light(atom/movable/source, angle, distance) - if(!loc) // We're in nullspace - return - - // Save ourselves some matrix math - if(angle != trans_angle) - trans_angle = angle - // Doing this in one operation (tn = turn(initial(tn), angle)) has strange results... - light_spot.transform = initial(light_spot.transform) - light_spot.transform = turn(light_spot.transform, angle) - - if(icon_dist != distance) - icon_dist = distance - switch(distance) - if(0) - light_spot.icon_state = "vclose" - if(1) - light_spot.icon_state = "close" - if(2) - light_spot.icon_state = "medium" - if(3 to INFINITY) - light_spot.icon_state = "far" - -/obj/effect/abstract/directional_lighting/Destroy(force) - if(!force) - stack_trace("Directional light atom deleted, but not by our component") - return QDEL_HINT_LETMELIVE - - vis_contents.Cut() - qdel_null(light_spot) - - return ..() +//The effect when you wrap a dead body in gift wrap +/obj/effect/spresent + name = "strange present" + desc = "It's a ... present?" + icon = 'icons/obj/items.dmi' + icon_state = "strangepresent" + density = TRUE + anchored = FALSE + +/obj/effect/temporary_effect + name = "self deleting effect" + desc = "How are you examining what which cannot be seen?" + icon = 'icons/effects/effects.dmi' + invisibility = 0 + var/time_to_die = 10 SECONDS // Afer which, it will delete itself. + +/obj/effect/temporary_effect/Initialize() + . = ..() + if(time_to_die) + QDEL_IN(src, time_to_die) + +// Shown really briefly when attacking with axes. +/obj/effect/temporary_effect/cleave_attack + name = "cleaving attack" + desc = "Something swinging really wide." + icon = 'icons/effects/96x96.dmi' + icon_state = "cleave" + plane = ABOVE_MOB_PLANE + layer = ABOVE_MOB_LAYER + time_to_die = 6 + alpha = 140 + mouse_opacity = 0 + pixel_x = -32 + pixel_y = -32 + +/obj/effect/temporary_effect/cleave_attack/Initialize() // Makes the slash fade smoothly. When completely transparent it should qdel itself. + . = ..() + animate(src, alpha = 0, time = time_to_die - 1) + +/obj/effect/temporary_effect/shuttle_landing + name = "shuttle landing" + desc = "You better move if you don't want to go splat!" + //VOREStation Edit Start + icon = 'icons/goonstation/featherzone.dmi' + icon_state = "hazard-corners" + time_to_die = 5 SECONDS + plane = PLANE_LIGHTING_ABOVE + //VOREStation Edit End + +// The manifestation of Zeus's might. Or just a really unlucky day. +// This is purely a visual effect, this isn't the part of the code that hurts things. +/obj/effect/temporary_effect/lightning_strike + name = "lightning" + desc = "How shocked you must be, to see this text. You must have lightning reflexes. \ + The humor in this description is just so electrifying." + icon = 'icons/effects/96x256.dmi' + icon_state = "lightning_strike" + plane = PLANE_LIGHTING_ABOVE + time_to_die = 1 SECOND + pixel_x = -32 + +/obj/effect/temporary_effect/lightning_strike/Initialize() + icon_state += "[rand(1,2)]" // To have two variants of lightning sprites. + animate(src, alpha = 0, time = time_to_die - 1) + . = ..() + +/obj/effect/dummy/lighting_obj + name = "lighting fx obj" + desc = "Tell a coder if you're seeing this." + icon_state = "nothing" + light_system = MOVABLE_LIGHT + light_range = MINIMUM_USEFUL_LIGHT_RANGE + light_color = COLOR_WHITE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + light_on = TRUE + blocks_emissive = FALSE + +/obj/effect/dummy/lighting_obj/Initialize(mapload, _range, _power, _color, _duration) + . = ..() + if(!isnull(_range)) + set_light_range(_range) + if(!isnull(_power)) + set_light_power(_power) + if(!isnull(_color)) + set_light_color(_color) + if(_duration) + QDEL_IN(src, _duration) + +/obj/effect/dummy/lighting_obj/moblight + name = "mob lighting fx" + +/obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, _color, _range, _power, _duration) + . = ..() + if(!ismob(loc)) + return INITIALIZE_HINT_QDEL + +/obj/effect/dummy/lighting_obj/moblight/fire + name = "fire" + light_color = LIGHT_COLOR_FIRE + light_range = LIGHT_RANGE_FIRE + +/obj/effect/abstract + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + plane = ABOVE_MOB_PLANE + +/obj/effect/abstract/light_spot + icon = 'icons/effects/eris_flashlight.dmi' + icon_state = "medium" + pixel_x = -16 + pixel_y = -16 + +/obj/effect/abstract/directional_lighting + var/obj/effect/abstract/light_spot/light_spot = new + var/trans_angle + var/icon_dist + +/obj/effect/abstract/directional_lighting/Initialize() + . = ..() + vis_contents += light_spot + +/obj/effect/abstract/directional_lighting/proc/face_light(atom/movable/source, angle, distance) + if(!loc) // We're in nullspace + return + + // Save ourselves some matrix math + if(angle != trans_angle) + trans_angle = angle + // Doing this in one operation (tn = turn(initial(tn), angle)) has strange results... + light_spot.transform = initial(light_spot.transform) + light_spot.transform = turn(light_spot.transform, angle) + + if(icon_dist != distance) + icon_dist = distance + switch(distance) + if(0) + light_spot.icon_state = "vclose" + if(1) + light_spot.icon_state = "close" + if(2) + light_spot.icon_state = "medium" + if(3 to INFINITY) + light_spot.icon_state = "far" + +/obj/effect/abstract/directional_lighting/Destroy(force) + if(!force) + stack_trace("Directional light atom deleted, but not by our component") + return QDEL_HINT_LETMELIVE + + vis_contents.Cut() + qdel_null(light_spot) + + return ..() diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 9669004bca2..475b3f5be75 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -1,191 +1,191 @@ -/obj/effect/overlay - name = "overlay" - unacidable = TRUE - var/i_attached//Added for possible image attachments to objects. For hallucinations and the like. - -/obj/effect/overlay/beam//Not actually a projectile, just an effect. - name="beam" - icon='icons/effects/beam.dmi' - icon_state="b_beam" - plane = ABOVE_OBJ_PLANE - var/tmp/atom/BeamSource - -/obj/effect/overlay/beam/New() - ..() - spawn(10) qdel(src) - -/obj/effect/overlay/palmtree_r - name = "Palm tree" - icon = 'icons/misc/beach2.dmi' - icon_state = "palm1" - density = TRUE - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - anchored = TRUE - -/obj/effect/overlay/palmtree_l - name = "Palm tree" - icon = 'icons/misc/beach2.dmi' - icon_state = "palm2" - density = TRUE - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - anchored = TRUE - -/obj/effect/overlay/coconut - name = "Coconuts" - icon = 'icons/misc/beach.dmi' - icon_state = "coconuts" - -/obj/effect/overlay/bluespacify - name = "Bluespace" - icon = 'icons/turf/space_vr.dmi' //VOREStation Edit - icon_state = "bluespacify" - plane = ABOVE_PLANE - -/obj/effect/overlay/wallrot - name = "wallrot" - desc = "Ick..." - icon = 'icons/effects/wallrot.dmi' - anchored = TRUE - density = TRUE - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - mouse_opacity = 0 - -/obj/effect/overlay/wallrot/New() - ..() - pixel_x += rand(-10, 10) - pixel_y += rand(-10, 10) - -/obj/effect/overlay/snow - name = "snow" - icon = 'icons/turf/overlays.dmi' - icon_state = "snow" - anchored = TRUE - plane = TURF_PLANE - -// Todo: Add a version that gradually reaccumulates over time by means of alpha transparency. -Spades -/obj/effect/overlay/snow/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/shovel)) - user.visible_message("[user] begins to shovel away \the [src].") - if(do_after(user, 40)) - to_chat(user, "You have finished shoveling!") - qdel(src) - return - -/obj/effect/overlay/snow/floor - icon_state = "snowfloor" - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - mouse_opacity = 0 //Don't block underlying tile interactions - -/obj/effect/overlay/snow/floor/edges - icon_state = "snow_edges" - -/obj/effect/overlay/snow/floor/surround - icon_state = "snow_surround" - -/obj/effect/overlay/snow/airlock - icon_state = "snowairlock" - layer = DOOR_CLOSED_LAYER+0.01 - -/obj/effect/overlay/snow/floor/pointy - icon_state = "snowfloorpointy" - -/obj/effect/overlay/snow/wall - icon_state = "snowwall" - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - -/obj/effect/overlay/holographic - mouse_opacity = FALSE - anchored = TRUE - plane = ABOVE_PLANE - -// Similar to the tesla ball but doesn't actually do anything and is purely visual. -/obj/effect/overlay/energy_ball - name = "energy ball" - desc = "An energy ball." - icon = 'icons/obj/tesla_engine/energy_ball.dmi' - icon_state = "energy_ball" - plane = PLANE_LIGHTING_ABOVE - pixel_x = -32 - pixel_y = -32 - -/obj/effect/overlay/vis - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - anchored = TRUE - vis_flags = VIS_INHERIT_DIR - ///When detected to be unused it gets set to world.time, after a while it gets removed - var/unused = 0 - ///overlays which go unused for this amount of time get cleaned up - var/cache_expiration = 2 MINUTES - -/* -/obj/effect/overlay/atmos_excited - name = "excited group" - icon = null - icon_state = null - anchored = TRUE // should only appear in vis_contents, but to be safe - appearance_flags = RESET_TRANSFORM | TILE_BOUND - invisibility = INVISIBILITY_ABSTRACT - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - - plane = ATMOS_GROUP_PLANE -*/ - -/obj/effect/overlay/light_visible - name = "" - icon = 'icons/effects/light_overlays/light_32.dmi' - icon_state = "light" - plane = PLANE_O_LIGHTING_VISUAL - appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - alpha = 0 - vis_flags = NONE - blocks_emissive = FALSE - -/obj/effect/overlay/light_visible/Destroy(force) - if(!force) - stack_trace("Movable light visible mask deleted, but not by our component") - return QDEL_HINT_LETMELIVE - return ..() - -/obj/effect/overlay/light_cone - name = "" - icon = 'icons/effects/light_overlays/light_cone.dmi' - icon_state = "light" - plane = PLANE_O_LIGHTING_VISUAL - appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - vis_flags = NONE - alpha = 110 - blocks_emissive = FALSE - - var/static/matrix/normal_transform - -/obj/effect/overlay/light_cone/Initialize() - . = ..() - apply_standard_transform() - -/obj/effect/overlay/light_cone/proc/reset_transform(apply_standard) - transform = initial(transform) - if(apply_standard) - apply_standard_transform() - -/obj/effect/overlay/light_cone/proc/apply_standard_transform() - transform = transform.Translate(-32, -32) - -/obj/effect/overlay/light_cone/Destroy(force) - if(!force) - stack_trace("Directional light cone deleted, but not by our component") - return QDEL_HINT_LETMELIVE - return ..() - -/obj/effect/overlay/closet_door - anchored = TRUE - plane = FLOAT_PLANE - layer = FLOAT_LAYER - vis_flags = VIS_INHERIT_ID - appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE +/obj/effect/overlay + name = "overlay" + unacidable = TRUE + var/i_attached//Added for possible image attachments to objects. For hallucinations and the like. + +/obj/effect/overlay/beam//Not actually a projectile, just an effect. + name="beam" + icon='icons/effects/beam.dmi' + icon_state="b_beam" + plane = ABOVE_OBJ_PLANE + var/tmp/atom/BeamSource + +/obj/effect/overlay/beam/New() + ..() + spawn(10) qdel(src) + +/obj/effect/overlay/palmtree_r + name = "Palm tree" + icon = 'icons/misc/beach2.dmi' + icon_state = "palm1" + density = TRUE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = TRUE + +/obj/effect/overlay/palmtree_l + name = "Palm tree" + icon = 'icons/misc/beach2.dmi' + icon_state = "palm2" + density = TRUE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = TRUE + +/obj/effect/overlay/coconut + name = "Coconuts" + icon = 'icons/misc/beach.dmi' + icon_state = "coconuts" + +/obj/effect/overlay/bluespacify + name = "Bluespace" + icon = 'icons/turf/space_vr.dmi' //VOREStation Edit + icon_state = "bluespacify" + plane = ABOVE_PLANE + +/obj/effect/overlay/wallrot + name = "wallrot" + desc = "Ick..." + icon = 'icons/effects/wallrot.dmi' + anchored = TRUE + density = TRUE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + mouse_opacity = 0 + +/obj/effect/overlay/wallrot/New() + ..() + pixel_x += rand(-10, 10) + pixel_y += rand(-10, 10) + +/obj/effect/overlay/snow + name = "snow" + icon = 'icons/turf/overlays.dmi' + icon_state = "snow" + anchored = TRUE + plane = TURF_PLANE + +// Todo: Add a version that gradually reaccumulates over time by means of alpha transparency. -Spades +/obj/effect/overlay/snow/attackby(obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/shovel)) + user.visible_message("[user] begins to shovel away \the [src].") + if(do_after(user, 40)) + to_chat(user, "You have finished shoveling!") + qdel(src) + return + +/obj/effect/overlay/snow/floor + icon_state = "snowfloor" + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + mouse_opacity = 0 //Don't block underlying tile interactions + +/obj/effect/overlay/snow/floor/edges + icon_state = "snow_edges" + +/obj/effect/overlay/snow/floor/surround + icon_state = "snow_surround" + +/obj/effect/overlay/snow/airlock + icon_state = "snowairlock" + layer = DOOR_CLOSED_LAYER+0.01 + +/obj/effect/overlay/snow/floor/pointy + icon_state = "snowfloorpointy" + +/obj/effect/overlay/snow/wall + icon_state = "snowwall" + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + +/obj/effect/overlay/holographic + mouse_opacity = FALSE + anchored = TRUE + plane = ABOVE_PLANE + +// Similar to the tesla ball but doesn't actually do anything and is purely visual. +/obj/effect/overlay/energy_ball + name = "energy ball" + desc = "An energy ball." + icon = 'icons/obj/tesla_engine/energy_ball.dmi' + icon_state = "energy_ball" + plane = PLANE_LIGHTING_ABOVE + pixel_x = -32 + pixel_y = -32 + +/obj/effect/overlay/vis + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + anchored = TRUE + vis_flags = VIS_INHERIT_DIR + ///When detected to be unused it gets set to world.time, after a while it gets removed + var/unused = 0 + ///overlays which go unused for this amount of time get cleaned up + var/cache_expiration = 2 MINUTES + +/* +/obj/effect/overlay/atmos_excited + name = "excited group" + icon = null + icon_state = null + anchored = TRUE // should only appear in vis_contents, but to be safe + appearance_flags = RESET_TRANSFORM | TILE_BOUND + invisibility = INVISIBILITY_ABSTRACT + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + plane = ATMOS_GROUP_PLANE +*/ + +/obj/effect/overlay/light_visible + name = "" + icon = 'icons/effects/light_overlays/light_32.dmi' + icon_state = "light" + plane = PLANE_O_LIGHTING_VISUAL + appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + alpha = 0 + vis_flags = NONE + blocks_emissive = FALSE + +/obj/effect/overlay/light_visible/Destroy(force) + if(!force) + stack_trace("Movable light visible mask deleted, but not by our component") + return QDEL_HINT_LETMELIVE + return ..() + +/obj/effect/overlay/light_cone + name = "" + icon = 'icons/effects/light_overlays/light_cone.dmi' + icon_state = "light" + plane = PLANE_O_LIGHTING_VISUAL + appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + vis_flags = NONE + alpha = 110 + blocks_emissive = FALSE + + var/static/matrix/normal_transform + +/obj/effect/overlay/light_cone/Initialize() + . = ..() + apply_standard_transform() + +/obj/effect/overlay/light_cone/proc/reset_transform(apply_standard) + transform = initial(transform) + if(apply_standard) + apply_standard_transform() + +/obj/effect/overlay/light_cone/proc/apply_standard_transform() + transform = transform.Translate(-32, -32) + +/obj/effect/overlay/light_cone/Destroy(force) + if(!force) + stack_trace("Directional light cone deleted, but not by our component") + return QDEL_HINT_LETMELIVE + return ..() + +/obj/effect/overlay/closet_door + anchored = TRUE + plane = FLOAT_PLANE + layer = FLOAT_LAYER + vis_flags = VIS_INHERIT_ID + appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index ae27e756957..002d5d5cc8a 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -1,69 +1,69 @@ -GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) - -/obj/effect/portal - name = "portal" - desc = "Looks unstable. Best to test it with the clown." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "portal" - density = TRUE - unacidable = TRUE//Can't destroy energy portals. - var/failchance = 5 - var/obj/item/target = null - var/creator = null - anchored = TRUE - -/obj/effect/portal/Bumped(mob/M as mob|obj) - if(istype(M,/mob) && !(istype(M,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(M) - return - return - -/obj/effect/portal/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM,/mob) && !(istype(AM,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(AM) - return - return - -/obj/effect/portal/attack_hand(mob/user as mob) - if(istype(user) && !(istype(user,/mob/living))) - return //do not send ghosts, zshadows, ai eyes, etc - spawn(0) - src.teleport(user) - return - return - -/obj/effect/portal/Initialize() - . = ..() - QDEL_IN(src, 30 SECONDS) - -/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj) - if(istype(M, /obj/effect)) //sparks don't teleport - return - if (M.anchored&&istype(M, /obj/mecha)) - return - if (icon_state == "portal1") - return - if (!( target )) - qdel(src) - return - if (istype(M, /atom/movable)) - //VOREStation Addition Start: Prevent taurriding abuse - if(istype(M, /mob/living)) - var/mob/living/L = M - if(LAZYLEN(L.buckled_mobs)) - var/datum/riding/R = L.riding_datum - for(var/rider in L.buckled_mobs) - R.force_dismount(rider) - //VOREStation Addition End: Prevent taurriding abuse - if(prob(failchance)) //oh dear a problem, put em in deep space - src.icon_state = "portal1" - do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0) - else - do_teleport(M, target, 1) ///You will appear adjacent to the beacon - +GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) + +/obj/effect/portal + name = "portal" + desc = "Looks unstable. Best to test it with the clown." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "portal" + density = TRUE + unacidable = TRUE//Can't destroy energy portals. + var/failchance = 5 + var/obj/item/target = null + var/creator = null + anchored = TRUE + +/obj/effect/portal/Bumped(mob/M as mob|obj) + if(istype(M,/mob) && !(istype(M,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(M) + return + return + +/obj/effect/portal/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM,/mob) && !(istype(AM,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(AM) + return + return + +/obj/effect/portal/attack_hand(mob/user as mob) + if(istype(user) && !(istype(user,/mob/living))) + return //do not send ghosts, zshadows, ai eyes, etc + spawn(0) + src.teleport(user) + return + return + +/obj/effect/portal/Initialize() + . = ..() + QDEL_IN(src, 30 SECONDS) + +/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj) + if(istype(M, /obj/effect)) //sparks don't teleport + return + if (M.anchored&&istype(M, /obj/mecha)) + return + if (icon_state == "portal1") + return + if (!( target )) + qdel(src) + return + if (istype(M, /atom/movable)) + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent taurriding abuse + if(prob(failchance)) //oh dear a problem, put em in deep space + src.icon_state = "portal1" + do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0) + else + do_teleport(M, target, 1) ///You will appear adjacent to the beacon + diff --git a/code/game/objects/effects/semirandom_mobs_vr.dm b/code/game/objects/effects/semirandom_mobs_vr.dm index 6abc8c95718..5651aba7948 100644 --- a/code/game/objects/effects/semirandom_mobs_vr.dm +++ b/code/game/objects/effects/semirandom_mobs_vr.dm @@ -1,1089 +1,1093 @@ -var/global/list/semirandom_mob_spawner_decisions = list() - -/obj/random/mob/semirandom_mob_spawner - name = "Semi-Random Spawner" - desc = "Spawns groups of mobs that are all of the same theme type/theme." - icon = 'icons/mob/randomlandmarks.dmi' - icon_state = "monster" - mob_returns_home = 1 - mob_wander_distance = 7 - - var/list/possible_mob_types = list( - list(/mob/living/simple_mob/animal/goat), - list( - /mob/living/simple_mob/animal/passive/bird, - /mob/living/simple_mob/animal/passive/bird/azure_tit, - /mob/living/simple_mob/animal/passive/bird/black_bird, - /mob/living/simple_mob/animal/passive/bird/european_robin, - /mob/living/simple_mob/animal/passive/bird/goldcrest, - /mob/living/simple_mob/animal/passive/bird/ringneck_dove, - /mob/living/simple_mob/animal/passive/bird/parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, - /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, - /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/kea, - /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo - ), - list( - /mob/living/simple_mob/animal/passive/cat, - /mob/living/simple_mob/animal/passive/cat/black - ), - list(/mob/living/simple_mob/animal/passive/chick), - list(/mob/living/simple_mob/animal/passive/cow), - list(/mob/living/simple_mob/animal/passive/dog/brittany), - list(/mob/living/simple_mob/animal/passive/dog/corgi), - list(/mob/living/simple_mob/animal/passive/dog/tamaskan), - list(/mob/living/simple_mob/animal/passive/fox), - list(/mob/living/simple_mob/animal/passive/hare), - list(/mob/living/simple_mob/animal/passive/lizard), - list(/mob/living/simple_mob/animal/passive/mouse), - list(/mob/living/simple_mob/animal/passive/mouse/jerboa), - list(/mob/living/simple_mob/animal/passive/opossum), - list(/mob/living/simple_mob/animal/passive/pillbug), - list(/mob/living/simple_mob/animal/passive/snake), - list(/mob/living/simple_mob/animal/passive/snake/red), - list(/mob/living/simple_mob/animal/passive/snake/python), - list(/mob/living/simple_mob/animal/passive/tindalos), - list(/mob/living/simple_mob/animal/passive/yithian), - list( - /mob/living/simple_mob/vore/wolf = 10, - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ), - list(/mob/living/simple_mob/vore/rabbit), - list(/mob/living/simple_mob/vore/redpanda), - list(/mob/living/simple_mob/vore/woof), - list(/mob/living/simple_mob/vore/fennec), - list(/mob/living/simple_mob/vore/fennix), - list(/mob/living/simple_mob/vore/hippo), - list(/mob/living/simple_mob/vore/horse), - list(/mob/living/simple_mob/vore/bee), - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ), - list( - /mob/living/simple_mob/vore/otie/feral, - /mob/living/simple_mob/vore/otie/feral/chubby, - /mob/living/simple_mob/vore/otie/red, - /mob/living/simple_mob/vore/otie/red/chubby - ), - list(/mob/living/simple_mob/animal/sif/diyaab), - list(/mob/living/simple_mob/animal/sif/duck), - list(/mob/living/simple_mob/animal/sif/frostfly), - list( - /mob/living/simple_mob/animal/sif/glitterfly =50, - /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 - ), - list( - /mob/living/simple_mob/animal/sif/kururak = 10, - /mob/living/simple_mob/animal/sif/kururak/leader = 1, - /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, - ), - list( - /mob/living/simple_mob/animal/sif/sakimm = 10, - /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 - ), - list(/mob/living/simple_mob/animal/sif/savik) = 5, - list( - /mob/living/simple_mob/animal/sif/shantak = 10, - /mob/living/simple_mob/animal/sif/shantak/leader = 1 - ), - list(/mob/living/simple_mob/animal/sif/siffet), - list(/mob/living/simple_mob/animal/sif/tymisian), - list( - /mob/living/simple_mob/animal/giant_spider/nurse = 10, - /mob/living/simple_mob/animal/giant_spider/electric = 5, - /mob/living/simple_mob/animal/giant_spider/frost = 5, - /mob/living/simple_mob/animal/giant_spider/hunter = 10, - /mob/living/simple_mob/animal/giant_spider/ion = 5, - /mob/living/simple_mob/animal/giant_spider/lurker = 10, - /mob/living/simple_mob/animal/giant_spider/pepper = 10, - /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, - /mob/living/simple_mob/animal/giant_spider/thermic = 5, - /mob/living/simple_mob/animal/giant_spider/tunneler = 10, - /mob/living/simple_mob/animal/giant_spider/webslinger = 5, - /mob/living/simple_mob/animal/giant_spider/broodmother = 1), - list(/mob/living/simple_mob/creature/strong), - list(/mob/living/simple_mob/faithless/strong), - list(/mob/living/simple_mob/animal/goat), - list( - /mob/living/simple_mob/animal/sif/shantak/leader = 1, - /mob/living/simple_mob/animal/sif/shantak = 10), - list(/mob/living/simple_mob/animal/sif/savik,), - list(/mob/living/simple_mob/animal/sif/hooligan_crab), - list( - /mob/living/simple_mob/animal/space/alien = 50, - /mob/living/simple_mob/animal/space/alien/drone = 40, - /mob/living/simple_mob/animal/space/alien/sentinel = 25, - /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, - /mob/living/simple_mob/animal/space/alien/queen = 10, - /mob/living/simple_mob/animal/space/alien/queen/empress = 5, - /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1 - ), - list(/mob/living/simple_mob/animal/space/bats/cult/strong), - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ), - list( - /mob/living/simple_mob/animal/space/carp = 50, - /mob/living/simple_mob/animal/space/carp/large = 10, - /mob/living/simple_mob/animal/space/carp/large/huge = 5 - ), - list(/mob/living/simple_mob/animal/space/goose), - list(/mob/living/simple_mob/vore/jelly), - list(/mob/living/simple_mob/animal/space/tree), - list( - /mob/living/simple_mob/vore/aggressive/corrupthound = 10, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, - ), - list(/mob/living/simple_mob/vore/aggressive/deathclaw), - list(/mob/living/simple_mob/vore/aggressive/dino), - list(/mob/living/simple_mob/vore/aggressive/dragon), - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), - list(/mob/living/simple_mob/vore/aggressive/frog), - list(/mob/living/simple_mob/vore/aggressive/giant_snake), - list(/mob/living/simple_mob/vore/aggressive/mimic), - list(/mob/living/simple_mob/vore/aggressive/panther), - list(/mob/living/simple_mob/vore/aggressive/rat), - list(/mob/living/simple_mob/vore/bee), - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ), - list(/mob/living/simple_mob/vore/solargrub), - list( - /mob/living/simple_mob/vore/oregrub = 5, - /mob/living/simple_mob/vore/oregrub/lava = 1 - ), - list(/mob/living/simple_mob/vore/catgirl), - list(/mob/living/simple_mob/vore/wolfgirl), - list( - /mob/living/simple_mob/vore/lamia, - /mob/living/simple_mob/vore/lamia/albino, - /mob/living/simple_mob/vore/lamia/albino/bra, - /mob/living/simple_mob/vore/lamia/albino/shirt, - /mob/living/simple_mob/vore/lamia/bra, - /mob/living/simple_mob/vore/lamia/cobra, - /mob/living/simple_mob/vore/lamia/cobra/bra, - /mob/living/simple_mob/vore/lamia/cobra/shirt, - /mob/living/simple_mob/vore/lamia/copper, - /mob/living/simple_mob/vore/lamia/copper/bra, - /mob/living/simple_mob/vore/lamia/copper/shirt, - /mob/living/simple_mob/vore/lamia/green, - /mob/living/simple_mob/vore/lamia/green/bra, - /mob/living/simple_mob/vore/lamia/green/shirt, - /mob/living/simple_mob/vore/lamia/zebra, - /mob/living/simple_mob/vore/lamia/zebra/bra, - /mob/living/simple_mob/vore/lamia/zebra/shirt - ), - list( - /mob/living/simple_mob/humanoid/merc = 100, - /mob/living/simple_mob/humanoid/merc/melee/sword = 50, - /mob/living/simple_mob/humanoid/merc/ranged = 25, - /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, - /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, - /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, - /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, - /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, - /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, - /mob/living/simple_mob/humanoid/merc/ranged/space = 10, - /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 - ), - list( - /mob/living/simple_mob/humanoid/pirate = 3, - /mob/living/simple_mob/humanoid/pirate/ranged = 1 - ), - list(/mob/living/simple_mob/mechanical/combat_drone), - list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), - list( - /mob/living/simple_mob/mechanical/hivebot = 100, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, - /mob/living/simple_mob/mechanical/hivebot/support = 8, - /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, - /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, - /mob/living/simple_mob/mechanical/hivebot/swarm = 20, - /mob/living/simple_mob/mechanical/hivebot/tank = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 - ), - list(/mob/living/simple_mob/mechanical/infectionbot), - list(/mob/living/simple_mob/mechanical/mining_drone), - list(/mob/living/simple_mob/mechanical/technomancer_golem), - list( - /mob/living/simple_mob/mechanical/viscerator, - /mob/living/simple_mob/mechanical/viscerator/piercing - ), - list(/mob/living/simple_mob/mechanical/wahlem), - list(/mob/living/simple_mob/animal/passive/fox/syndicate), - list(/mob/living/simple_mob/animal/passive/fox), - list(/mob/living/simple_mob/vore/jelly), - list( - /mob/living/simple_mob/vore/otie/feral, - /mob/living/simple_mob/vore/otie/feral/chubby, - /mob/living/simple_mob/vore/otie/red, - /mob/living/simple_mob/vore/otie/red/chubby - ), - list( - /mob/living/simple_mob/shadekin/blue = 100, - /mob/living/simple_mob/shadekin/green = 50, - /mob/living/simple_mob/shadekin/orange = 20, - /mob/living/simple_mob/shadekin/purple = 60, - /mob/living/simple_mob/shadekin/red = 40, - /mob/living/simple_mob/shadekin/yellow = 1 - ), - list( - /mob/living/simple_mob/vore/aggressive/corrupthound, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi - ), - list(/mob/living/simple_mob/vore/aggressive/deathclaw), - list(/mob/living/simple_mob/vore/aggressive/dino), - list(/mob/living/simple_mob/vore/aggressive/dragon), - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), - list(/mob/living/simple_mob/vore/aggressive/frog), - list(/mob/living/simple_mob/vore/aggressive/giant_snake), - list(/mob/living/simple_mob/vore/aggressive/mimic), - list(/mob/living/simple_mob/vore/aggressive/panther), - list(/mob/living/simple_mob/vore/aggressive/rat), - list(/mob/living/simple_mob/vore/bee), - list(/mob/living/simple_mob/vore/catgirl), - list(/mob/living/simple_mob/vore/cookiegirl), - list(/mob/living/simple_mob/vore/fennec), - list(/mob/living/simple_mob/vore/fennix), - list(/mob/living/simple_mob/vore/hippo), - list(/mob/living/simple_mob/vore/horse), - list(/mob/living/simple_mob/vore/oregrub), - list(/mob/living/simple_mob/vore/rabbit), - list( - /mob/living/simple_mob/vore/redpanda = 50, - /mob/living/simple_mob/vore/redpanda/fae = 1 - ), - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ), - list(/mob/living/simple_mob/vore/solargrub), - list(/mob/living/simple_mob/vore/woof), - list(/mob/living/simple_mob/vore/alienanimals/space_ghost), - list(/mob/living/simple_mob/vore/alienanimals/catslug), - list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish), - list(/mob/living/simple_mob/vore/alienanimals/startreader), - list( - /mob/living/simple_mob/vore/bigdragon, - /mob/living/simple_mob/vore/bigdragon/friendly), - list( - /mob/living/simple_mob/vore/leopardmander = 50, - /mob/living/simple_mob/vore/leopardmander/blue = 10, - /mob/living/simple_mob/vore/leopardmander/exotic = 1 - ), - list(/mob/living/simple_mob/vore/sheep), - list(/mob/living/simple_mob/vore/weretiger) - ) - -/obj/random/mob/semirandom_mob_spawner/item_to_spawn() - var/list/choice = semirandom_mob_spawner_decisions[type] - - if(!choice) - choice = pickweight(possible_mob_types) - semirandom_mob_spawner_decisions[type] = choice - - return pickweight(choice) - -/obj/random/mob/semirandom_mob_spawner/animal - name = "Semi-Random Animal" - desc = "Spawns groups of non-hostile mobs that are all of the same theme type/theme." - icon_state = "animal" - mob_faction = "animal" - overwrite_hostility = 1 - mob_hostile = 0 - - possible_mob_types = list( - list(/mob/living/simple_mob/animal/goat) = 25, - list( - /mob/living/simple_mob/animal/passive/bird, - /mob/living/simple_mob/animal/passive/bird/azure_tit, - /mob/living/simple_mob/animal/passive/bird/black_bird, - /mob/living/simple_mob/animal/passive/bird/european_robin, - /mob/living/simple_mob/animal/passive/bird/goldcrest, - /mob/living/simple_mob/animal/passive/bird/ringneck_dove, - /mob/living/simple_mob/animal/passive/bird/parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, - /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, - /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, - /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, - /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, - /mob/living/simple_mob/animal/passive/bird/parrot/kea, - /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, - /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo, - /mob/living/simple_mob/animal/space/goose - ) = 25, - list( - /mob/living/simple_mob/animal/passive/cat, - /mob/living/simple_mob/animal/passive/cat/black - ) = 25, - list( - /mob/living/simple_mob/animal/passive/chick, - /mob/living/simple_mob/animal/passive/chicken - ) = 25, - list(/mob/living/simple_mob/animal/passive/cow) = 25, - list(/mob/living/simple_mob/animal/passive/dog/brittany) = 10, - list(/mob/living/simple_mob/animal/passive/dog/corgi) = 10, - list(/mob/living/simple_mob/animal/passive/dog/tamaskan) = 10, - list(/mob/living/simple_mob/animal/passive/fox) = 25, - list(/mob/living/simple_mob/animal/passive/hare) = 25, - list(/mob/living/simple_mob/animal/passive/lizard) = 10, - list(/mob/living/simple_mob/animal/passive/mouse) = 15, - list(/mob/living/simple_mob/animal/passive/mouse/jerboa) = 5, - list(/mob/living/simple_mob/animal/passive/opossum) = 10, - list(/mob/living/simple_mob/animal/passive/pillbug) = 10, - list(/mob/living/simple_mob/animal/passive/snake) = 10, - list(/mob/living/simple_mob/animal/passive/snake/red) = 10, - list(/mob/living/simple_mob/animal/passive/snake/python) = 10, - list(/mob/living/simple_mob/animal/passive/tindalos) = 10, - list(/mob/living/simple_mob/animal/passive/yithian) = 10, - list( - /mob/living/simple_mob/vore/wolf = 10, - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ) = 10, - list(/mob/living/simple_mob/vore/rabbit) = 10, - list(/mob/living/simple_mob/vore/redpanda) = 10, - list(/mob/living/simple_mob/vore/woof) = 1, - list(/mob/living/simple_mob/vore/fennec) = 10, - list(/mob/living/simple_mob/vore/fennix) = 1, - list(/mob/living/simple_mob/vore/hippo) = 5, - list(/mob/living/simple_mob/vore/horse) = 25, - list(/mob/living/simple_mob/vore/bee) = 10, - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ) = 1, - list( - /mob/living/simple_mob/vore/otie/feral = 50, - /mob/living/simple_mob/vore/otie/feral/chubby = 10, - /mob/living/simple_mob/vore/otie/red = 5, - /mob/living/simple_mob/vore/otie/red/chubby = 1 - ) = 5, - list(/mob/living/simple_mob/vore/aggressive/rat) = 15, - list(/mob/living/simple_mob/animal/sif/diyaab) = 5, - list(/mob/living/simple_mob/animal/sif/duck) = 5, - list(/mob/living/simple_mob/animal/sif/frostfly) = 5, - list( - /mob/living/simple_mob/animal/sif/glitterfly = 50, - /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 - ) = 5, - list( - /mob/living/simple_mob/animal/sif/kururak = 10, - /mob/living/simple_mob/animal/sif/kururak/leader = 1, - /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, - ) = 5, - list( - /mob/living/simple_mob/animal/sif/sakimm = 10, - /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 - ) = 5, - list(/mob/living/simple_mob/animal/sif/savik) = 5, - list( - /mob/living/simple_mob/animal/sif/shantak = 10, - /mob/living/simple_mob/animal/sif/shantak/leader = 1 - ) = 5, - list(/mob/living/simple_mob/animal/sif/siffet) = 5, - list(/mob/living/simple_mob/animal/sif/tymisian) = 5, - list(/mob/living/simple_mob/vore/alienanimals/dustjumper) = 5, - list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, - list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, - list( - /mob/living/simple_mob/vore/leopardmander = 50, - /mob/living/simple_mob/vore/leopardmander/blue = 10, - /mob/living/simple_mob/vore/leopardmander/exotic = 1 - ) = 5, - list(/mob/living/simple_mob/vore/sheep) = 5, - list(/mob/living/simple_mob/vore/weretiger) = 5, - list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5 - ) - -/obj/random/mob/semirandom_mob_spawner/monster - name = "Semi-Random Monster" - desc = "Spawns groups of hostile mobs that are all of the same theme type/theme." - overwrite_hostility = 1 - mob_faction = "monster" - mob_hostile = 1 - mob_retaliate = 1 - - possible_mob_types = list( - list( - /mob/living/simple_mob/animal/giant_spider/nurse = 10, - /mob/living/simple_mob/animal/giant_spider/electric = 5, - /mob/living/simple_mob/animal/giant_spider/frost = 5, - /mob/living/simple_mob/animal/giant_spider/hunter = 10, - /mob/living/simple_mob/animal/giant_spider/ion = 5, - /mob/living/simple_mob/animal/giant_spider/lurker = 10, - /mob/living/simple_mob/animal/giant_spider/pepper = 10, - /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, - /mob/living/simple_mob/animal/giant_spider/thermic = 5, - /mob/living/simple_mob/animal/giant_spider/tunneler = 10, - /mob/living/simple_mob/animal/giant_spider/webslinger = 5 - ) = 100, - list( - /mob/living/simple_mob/shadekin/red = 5, - /mob/living/simple_mob/shadekin/orange = 1, - /mob/living/simple_mob/shadekin/purple = 10 - ) = 1, - list( - /mob/living/simple_mob/vore/wolf = 10, - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ) = 40, - list(/mob/living/simple_mob/creature/strong) = 40, - list(/mob/living/simple_mob/faithless/strong) = 20, - list(/mob/living/simple_mob/animal/goat) = 1, - list( - /mob/living/simple_mob/animal/sif/shantak/leader = 1, - /mob/living/simple_mob/animal/sif/shantak = 10 - ) = 50, - list(/mob/living/simple_mob/animal/sif/savik,) = 20, - list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 10, - list( - /mob/living/simple_mob/animal/space/alien = 50, - /mob/living/simple_mob/animal/space/alien/drone = 40, - /mob/living/simple_mob/animal/space/alien/sentinel = 25, - /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, - /mob/living/simple_mob/animal/space/alien/queen = 10, - /mob/living/simple_mob/animal/space/alien/queen/empress = 5, - /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1, - ) = 40, - list(/mob/living/simple_mob/animal/space/bats/cult/strong) = 40, - list( - /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/bear/brown - ) = 40, - list( - /mob/living/simple_mob/animal/space/carp = 50, - /mob/living/simple_mob/animal/space/carp/large = 10, - /mob/living/simple_mob/animal/space/carp/large/huge = 5 - ) = 50, - list(/mob/living/simple_mob/animal/space/goose) = 50, - list(/mob/living/simple_mob/vore/jelly) = 40, - list(/mob/living/simple_mob/animal/space/tree) = 15, - list( - /mob/living/simple_mob/vore/otie/feral = 50, - /mob/living/simple_mob/vore/otie/feral/chubby = 10, - /mob/living/simple_mob/vore/otie/red = 5, - /mob/living/simple_mob/vore/otie/red/chubby = 1 - ) = 40, - list( - /mob/living/simple_mob/vore/aggressive/corrupthound = 10, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, - ) = 50, - list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 40, - list(/mob/living/simple_mob/vore/aggressive/dino) = 40, - list(/mob/living/simple_mob/vore/aggressive/dragon) = 40, - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 40, - list(/mob/living/simple_mob/vore/aggressive/frog) = 40, - list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 40, - list(/mob/living/simple_mob/vore/aggressive/mimic) = 40, - list(/mob/living/simple_mob/vore/aggressive/panther) = 25, - list(/mob/living/simple_mob/vore/aggressive/rat) = 50, - list(/mob/living/simple_mob/vore/bee) = 40, - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ) = 20, - list(/mob/living/simple_mob/vore/solargrub) = 15, - list( - /mob/living/simple_mob/vore/oregrub = 5, - /mob/living/simple_mob/vore/oregrub/lava = 1 - ) = 15, - list(/mob/living/simple_mob/vore/alienanimals/teppi) = 15, - list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, - list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, - list( - /mob/living/simple_mob/vore/leopardmander = 50, - /mob/living/simple_mob/vore/leopardmander/blue = 10, - /mob/living/simple_mob/vore/leopardmander/exotic = 1 - ) = 5, - list(/mob/living/simple_mob/vore/sheep) = 5, - list(/mob/living/simple_mob/vore/weretiger) = 5, - list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5, - list(/mob/living/simple_mob/vore/alienanimals/catslug) = 5 - ) - -/obj/random/mob/semirandom_mob_spawner/humanoid - name = "Semi-Random Humanoid" - desc = "Spawns groups of humanoid mobs that may or may not be hostile, all of the same theme type/theme." - icon_state = "humanoid" - mob_faction = "humanoid" - - possible_mob_types = list( - list( - /mob/living/simple_mob/shadekin/blue = 25, - /mob/living/simple_mob/shadekin/green = 10, - /mob/living/simple_mob/shadekin/purple = 1, - ) = 1, - list(/mob/living/simple_mob/vore/catgirl) = 100, - list(/mob/living/simple_mob/vore/wolfgirl) = 100, - list( - /mob/living/simple_mob/vore/lamia, - /mob/living/simple_mob/vore/lamia/albino, - /mob/living/simple_mob/vore/lamia/albino/bra, - /mob/living/simple_mob/vore/lamia/albino/shirt, - /mob/living/simple_mob/vore/lamia/bra, - /mob/living/simple_mob/vore/lamia/cobra, - /mob/living/simple_mob/vore/lamia/cobra/bra, - /mob/living/simple_mob/vore/lamia/cobra/shirt, - /mob/living/simple_mob/vore/lamia/copper, - /mob/living/simple_mob/vore/lamia/copper/bra, - /mob/living/simple_mob/vore/lamia/copper/shirt, - /mob/living/simple_mob/vore/lamia/green, - /mob/living/simple_mob/vore/lamia/green/bra, - /mob/living/simple_mob/vore/lamia/green/shirt, - /mob/living/simple_mob/vore/lamia/zebra, - /mob/living/simple_mob/vore/lamia/zebra/bra, - /mob/living/simple_mob/vore/lamia/zebra/shirt - ) = 100, -// LOOK OKAY MERCS ARE HUMANOIDS SO THEY ARE HERE, but they are also kind of bullshit so they probably shouldn't be able to spawn in the same place as catgirls. -// I want some better potentially hostile humanoids that aren't stupid to fight. If they become a big issue I'll comment them out. -// For now they are just rare, and the ranged ones are way more rare than the melee ones, which I think will help balance them out. - list( - /mob/living/simple_mob/humanoid/merc = 100, - /mob/living/simple_mob/humanoid/merc/melee/sword = 50, - /mob/living/simple_mob/humanoid/merc/ranged = 25, - /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, - /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, - /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, - /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, - /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, - /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, - /mob/living/simple_mob/humanoid/merc/ranged/space = 10, - /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 - ) = 5, -// PIRATES are okay though. They can be kind of a pain but you can kind of slap them around. Also it's not like. A crime. To fight and blow up pirates so it's fine. - list( - /mob/living/simple_mob/humanoid/pirate = 3, - /mob/living/simple_mob/humanoid/pirate/ranged = 1 - ) = 50 - ) - -// I am not familiar enough with robots to know which ones are fun to fight so this list isn't weighted at all SO YOU KNOW. Be careful. -/obj/random/mob/semirandom_mob_spawner/robot - name = "Semi-Random Robot" - desc = "Spawns groups of robotic mobs that are probably hostile, all of the same theme type/theme." - icon_state = "robot" - mob_faction = "robot" - - possible_mob_types = list( - list(/mob/living/simple_mob/mechanical/combat_drone), - list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), - list( - /mob/living/simple_mob/mechanical/hivebot = 100, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, - /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, - /mob/living/simple_mob/mechanical/hivebot/support = 8, - /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, - /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, - /mob/living/simple_mob/mechanical/hivebot/swarm = 20, - /mob/living/simple_mob/mechanical/hivebot/tank = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, - /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 - ), - list(/mob/living/simple_mob/mechanical/infectionbot), - list(/mob/living/simple_mob/mechanical/mining_drone), - list(/mob/living/simple_mob/mechanical/technomancer_golem), - list( - /mob/living/simple_mob/mechanical/viscerator, - /mob/living/simple_mob/mechanical/viscerator/piercing - ), - list(/mob/living/simple_mob/mechanical/wahlem), - list(/mob/living/simple_mob/animal/passive/fox/syndicate) - ) - -/obj/random/mob/semirandom_mob_spawner/fish - name = "Semi-Random Fish" - desc = "Spawns groups of fish, all of the same theme type/theme." - icon_state = "fish" - mob_faction = "fish" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 0 - - - possible_mob_types = list( - list(/mob/living/simple_mob/animal/passive/fish/bass) = 20, - list(/mob/living/simple_mob/animal/passive/fish/icebass) = 20, - list(/mob/living/simple_mob/animal/passive/fish/javelin) = 20, - list(/mob/living/simple_mob/animal/passive/fish/koi) = 10, - list(/mob/living/simple_mob/animal/passive/fish/measelshark) = 5, - list(/mob/living/simple_mob/animal/passive/fish/murkin) = 20, - list(/mob/living/simple_mob/animal/passive/fish/perch) = 20, - list(/mob/living/simple_mob/animal/passive/fish/pike) = 20, - list(/mob/living/simple_mob/animal/passive/fish/rockfish) = 10, - list(/mob/living/simple_mob/animal/passive/fish/salmon) = 20, - list(/mob/living/simple_mob/animal/passive/fish/solarfish) = 5, - list(/mob/living/simple_mob/animal/passive/fish/trout) = 20, - list(/mob/living/simple_mob/animal/passive/crab) = 10, - list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 1 - ) - -/obj/random/mob/semirandom_mob_spawner/bird - name = "Semi-Random Bird" - desc = "Spawns groups of bird, all of the same theme type/theme." - icon_state = "bird" - mob_faction = "bird" - - possible_mob_types = list( - list(/mob/living/simple_mob/animal/passive/bird), - list(/mob/living/simple_mob/animal/passive/bird/azure_tit), - list(/mob/living/simple_mob/animal/passive/bird/black_bird), - list(/mob/living/simple_mob/animal/passive/bird/european_robin), - list(/mob/living/simple_mob/animal/passive/bird/goldcrest), - list(/mob/living/simple_mob/animal/passive/bird/ringneck_dove), - list(/mob/living/simple_mob/animal/passive/bird/parrot), - list(/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique), - list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar), - list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue), - list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white), - list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish), - list(/mob/living/simple_mob/animal/passive/bird/parrot/eclectus), - list(/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot), - list(/mob/living/simple_mob/animal/passive/bird/parrot/kea), - list(/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo), - list(/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo), - list(/mob/living/simple_mob/animal/passive/bird/parrot/white_caique), - list(/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo), - list(/mob/living/simple_mob/animal/space/goose), - list(/mob/living/simple_mob/animal/passive/chicken), - list(/mob/living/simple_mob/animal/passive/penguin) - ) - -/obj/random/mob/semirandom_mob_spawner/vore - name = "Semi-Random Voremob" - desc = "Spawns groups of voremobs, all of the same theme type/theme." - icon_state = "vore" - mob_faction = "vore" - - possible_mob_types = list( - list( - /mob/living/simple_mob/vore/wolf/direwolf = 5, - /mob/living/simple_mob/vore/greatwolf = 1, - /mob/living/simple_mob/vore/greatwolf/black = 1, - /mob/living/simple_mob/vore/greatwolf/grey = 1 - ) = 100, - list(/mob/living/simple_mob/vore/jelly) = 70, - list( - /mob/living/simple_mob/vore/otie/feral, - /mob/living/simple_mob/vore/otie/feral/chubby, - /mob/living/simple_mob/vore/otie/red, - /mob/living/simple_mob/vore/otie/red/chubby - ) = 50, - list( - /mob/living/simple_mob/shadekin/blue = 100, - /mob/living/simple_mob/shadekin/green = 50, - /mob/living/simple_mob/shadekin/orange = 20, - /mob/living/simple_mob/shadekin/purple = 60, - /mob/living/simple_mob/shadekin/red = 40, - /mob/living/simple_mob/shadekin/yellow = 1 - ) = 1, - list( - /mob/living/simple_mob/vore/aggressive/corrupthound, - /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi - ) = 70, - list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 70, - list(/mob/living/simple_mob/vore/aggressive/dino) = 100, - list(/mob/living/simple_mob/vore/aggressive/dragon) = 100, - list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 100, - list(/mob/living/simple_mob/vore/aggressive/frog) = 100, - list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 100, - list(/mob/living/simple_mob/vore/aggressive/mimic) = 50, - list(/mob/living/simple_mob/vore/aggressive/panther) = 70, - list(/mob/living/simple_mob/vore/aggressive/rat) = 100, - list(/mob/living/simple_mob/vore/bee) = 100, - list(/mob/living/simple_mob/vore/catgirl) = 100, - list(/mob/living/simple_mob/vore/wolftaur) = 100, - list(/mob/living/simple_mob/vore/cookiegirl) = 100, - list(/mob/living/simple_mob/vore/fennec) = 100, - list(/mob/living/simple_mob/vore/fennix) = 50, - list(/mob/living/simple_mob/vore/hippo) = 70, - list(/mob/living/simple_mob/vore/horse) = 100, - list( - /mob/living/simple_mob/vore/lamia, - /mob/living/simple_mob/vore/lamia/albino, - /mob/living/simple_mob/vore/lamia/albino/bra, - /mob/living/simple_mob/vore/lamia/albino/shirt, - /mob/living/simple_mob/vore/lamia/bra, - /mob/living/simple_mob/vore/lamia/cobra, - /mob/living/simple_mob/vore/lamia/cobra/bra, - /mob/living/simple_mob/vore/lamia/cobra/shirt, - /mob/living/simple_mob/vore/lamia/copper, - /mob/living/simple_mob/vore/lamia/copper/bra, - /mob/living/simple_mob/vore/lamia/copper/shirt, - /mob/living/simple_mob/vore/lamia/green, - /mob/living/simple_mob/vore/lamia/green/bra, - /mob/living/simple_mob/vore/lamia/green/shirt, - /mob/living/simple_mob/vore/lamia/zebra, - /mob/living/simple_mob/vore/lamia/zebra/bra, - /mob/living/simple_mob/vore/lamia/zebra/shirt - ) = 100, - list(/mob/living/simple_mob/vore/rabbit) = 100, - list( - /mob/living/simple_mob/vore/redpanda = 50, - /mob/living/simple_mob/vore/redpanda/fae = 1 - ) = 100, - list( - /mob/living/simple_mob/vore/sect_drone = 10, - /mob/living/simple_mob/vore/sect_queen = 1 - ) = 50, - list(/mob/living/simple_mob/vore/solargrub) = 100, - list(/mob/living/simple_mob/vore/woof) = 1 - ) - -/obj/random/mob/semirandom_mob_spawner/sus - name = "Weird shit" - desc = "Spawns groups of weird stuff, all of the same theme type/theme. Don't put this on normal maps." - icon_state = "sus" - mob_faction = "sus" - - possible_mob_types = list( - list( - /mob/living/simple_mob/vore/woof/hostile/melee = 100, - /mob/living/simple_mob/vore/woof/hostile/ranged = 20, - /mob/living/simple_mob/vore/woof/hostile/horrible = 10, - /mob/living/simple_mob/vore/woof/hostile/terrible = 5, - /mob/living/simple_mob/vore/woof/cass = 1 - ), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/recursive) - ) - -/obj/random/mob/semirandom_mob_spawner/mecha - name = "Semi-Random Mecha" - desc = "Spawns groups of mechs, all of the same theme type/theme. Don't put this on normal maps." - icon_state = "mecha" - mob_faction = "mecha" - - possible_mob_types = list( - list(/mob/living/simple_mob/mechanical/mecha/combat/durand), - list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive), - list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned), - list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax), - list(/mob/living/simple_mob/mechanical/mecha/combat/marauder), - list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler), - list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph), - list(/mob/living/simple_mob/mechanical/mecha/combat/phazon), - list(/mob/living/simple_mob/mechanical/mecha/hoverpod), - list(/mob/living/simple_mob/mechanical/mecha/hoverpod/manned), - list(/mob/living/simple_mob/mechanical/mecha/odysseus), - list(/mob/living/simple_mob/mechanical/mecha/odysseus/manned), - list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus), - list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley), - list(/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames), - list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley), - list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter), - list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley/manned), - list(/mob/living/simple_mob/mechanical/mecha/ripley/red_flames) - ) - - -/obj/random/mob/semirandom_mob_spawner/monster/b - mob_faction = "monsterb" - -/obj/random/mob/semirandom_mob_spawner/monster/c - mob_faction = "monsterc" - -/obj/random/mob/semirandom_mob_spawner/monster/d - mob_faction = "monsterd" - -/obj/random/mob/semirandom_mob_spawner/monster/e - mob_faction = "monstere" - -/obj/random/mob/semirandom_mob_spawner/monster/f - mob_faction = "monsterf" - -/obj/random/mob/semirandom_mob_spawner/animal/b - mob_faction = "animalb" - -/obj/random/mob/semirandom_mob_spawner/animal/c - mob_faction = "animalc" - -/obj/random/mob/semirandom_mob_spawner/animal/d - mob_faction = "animald" - -/obj/random/mob/semirandom_mob_spawner/animal/e - mob_faction = "animale" - -/obj/random/mob/semirandom_mob_spawner/animal/f - mob_faction = "animalf" - -/obj/random/mob/semirandom_mob_spawner/animal/retaliate - mob_faction = "retanimala" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/animal/retaliate/b - mob_faction = "retanimalb" - -/obj/random/mob/semirandom_mob_spawner/animal/retaliate/c - mob_faction = "retanimalc" - -/obj/random/mob/semirandom_mob_spawner/animal/hostile - mob_faction = "hosanimala" - overwrite_hostility = 1 - mob_hostile = 1 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/animal/hostile/b - mob_faction = "hosanimalb" - -/obj/random/mob/semirandom_mob_spawner/animal/hostile/c - mob_faction = "hosanimalc" - - -/obj/random/mob/semirandom_mob_spawner/humanoid/b - mob_faction = "humanoidb" - -/obj/random/mob/semirandom_mob_spawner/humanoid/c - mob_faction = "humanoidc" - -/obj/random/mob/semirandom_mob_spawner/humanoid/d - mob_faction = "humanoidd" - -/obj/random/mob/semirandom_mob_spawner/humanoid/e - mob_faction = "humanoide" - -/obj/random/mob/semirandom_mob_spawner/humanoid/f - mob_faction = "humanoidf" - -/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate - mob_faction = "rethumanoida" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/b - mob_faction = "rethumanoidb" - -/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/c - mob_faction = "rethumanoidc" - -/obj/random/mob/semirandom_mob_spawner/humanoid/hostile - mob_faction = "hoshumanoida" - overwrite_hostility = 1 - mob_hostile = 1 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/b - mob_faction = "hoshumanoidb" - -/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/c - mob_faction = "hoshumanoidc" - -/obj/random/mob/semirandom_mob_spawner/robot/b - mob_faction = "robotb" - -/obj/random/mob/semirandom_mob_spawner/robot/c - mob_faction = "robotc" - -/obj/random/mob/semirandom_mob_spawner/robot/d - mob_faction = "robotd" - -/obj/random/mob/semirandom_mob_spawner/robot/e - mob_faction = "robote" - -/obj/random/mob/semirandom_mob_spawner/robot/f - mob_faction = "robotf" - -/obj/random/mob/semirandom_mob_spawner/robot/retaliate - mob_faction = "retrobota" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/robot/retaliate/b - mob_faction = "retrobotb" - -/obj/random/mob/semirandom_mob_spawner/robot/retaliate/c - mob_faction = "retrobotc" - -/obj/random/mob/semirandom_mob_spawner/bird/b - mob_faction = "birdb" - -/obj/random/mob/semirandom_mob_spawner/bird/c - mob_faction = "birdc" - -/obj/random/mob/semirandom_mob_spawner/bird/d - mob_faction = "birdd" - -/obj/random/mob/semirandom_mob_spawner/bird/e - mob_faction = "birde" - -/obj/random/mob/semirandom_mob_spawner/bird/f - mob_faction = "birdf" - -/obj/random/mob/semirandom_mob_spawner/fish/b - mob_faction = "fishb" - -/obj/random/mob/semirandom_mob_spawner/fish/c - mob_faction = "fishc" - -/obj/random/mob/semirandom_mob_spawner/fish/d - mob_faction = "fishd" - -/obj/random/mob/semirandom_mob_spawner/fish/e - mob_faction = "fishe" - -/obj/random/mob/semirandom_mob_spawner/fish/f - mob_faction = "fishf" - -/obj/random/mob/semirandom_mob_spawner/vore/b - mob_faction = "voreb" - -/obj/random/mob/semirandom_mob_spawner/vore/c - mob_faction = "vorec" - -/obj/random/mob/semirandom_mob_spawner/vore/d - mob_faction = "vored" - -/obj/random/mob/semirandom_mob_spawner/vore/e - mob_faction = "voree" - -/obj/random/mob/semirandom_mob_spawner/vore/f - mob_faction = "voref" - -/obj/random/mob/semirandom_mob_spawner/vore/passive - mob_faction = "pasvorea" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 0 - -/obj/random/mob/semirandom_mob_spawner/vore/passive/b - mob_faction = "pasvoreb" - -/obj/random/mob/semirandom_mob_spawner/vore/passive/c - mob_faction = "pasvorec" - -/obj/random/mob/semirandom_mob_spawner/vore/retaliate - mob_faction = "retvorea" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/vore/retaliate/b - mob_faction = "retvoreb" - -/obj/random/mob/semirandom_mob_spawner/vore/retaliate/c - mob_faction = "retvorec" - -/obj/random/mob/semirandom_mob_spawner/vore/hostile - mob_faction = "hosvorea" - overwrite_hostility = 1 - mob_hostile = 1 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/vore/hostile/b - mob_faction = "hosvoreb" - -/obj/random/mob/semirandom_mob_spawner/vore/hostile/c - mob_faction = "hosvorec" - -/obj/random/mob/semirandom_mob_spawner/sus/b - mob_faction = "susb" - -/obj/random/mob/semirandom_mob_spawner/sus/c - mob_faction = "susc" - -/obj/random/mob/semirandom_mob_spawner/sus/d - mob_faction = "susd" - -/obj/random/mob/semirandom_mob_spawner/sus/e - mob_faction = "suse" - -/obj/random/mob/semirandom_mob_spawner/sus/f - mob_faction = "susf" - -/obj/random/mob/semirandom_mob_spawner/mecha/b - mob_faction = "mechab" - -/obj/random/mob/semirandom_mob_spawner/mecha/c - mob_faction = "mechac" - -/obj/random/mob/semirandom_mob_spawner/mecha/d - mob_faction = "mechad" - -/obj/random/mob/semirandom_mob_spawner/mecha/e - mob_faction = "mechae" - -/obj/random/mob/semirandom_mob_spawner/mecha/f - mob_faction = "mechaf" - -/obj/random/mob/semirandom_mob_spawner/mecha/retaliate - mob_faction = "retmecha" - overwrite_hostility = 1 - mob_hostile = 0 - mob_retaliate = 1 - -/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/b - mob_faction = "retmechb" - -/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/c - mob_faction = "retmechc" +var/global/list/semirandom_mob_spawner_decisions = list() + +/obj/random/mob/semirandom_mob_spawner + name = "Semi-Random Spawner" + desc = "Spawns groups of mobs that are all of the same theme type/theme." + icon = 'icons/mob/randomlandmarks.dmi' + icon_state = "monster" + mob_returns_home = 1 + mob_wander_distance = 7 + + var/list/possible_mob_types = list( + list(/mob/living/simple_mob/animal/goat), + list( + /mob/living/simple_mob/animal/passive/bird, + /mob/living/simple_mob/animal/passive/bird/azure_tit, + /mob/living/simple_mob/animal/passive/bird/black_bird, + /mob/living/simple_mob/animal/passive/bird/european_robin, + /mob/living/simple_mob/animal/passive/bird/goldcrest, + /mob/living/simple_mob/animal/passive/bird/ringneck_dove, + /mob/living/simple_mob/animal/passive/bird/parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, + /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, + /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/kea, + /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo + ), + list( + /mob/living/simple_mob/animal/passive/cat, + /mob/living/simple_mob/animal/passive/cat/black + ), + list(/mob/living/simple_mob/animal/passive/chick), + list(/mob/living/simple_mob/animal/passive/cow), + list(/mob/living/simple_mob/animal/passive/dog/brittany), + list(/mob/living/simple_mob/animal/passive/dog/corgi), + list(/mob/living/simple_mob/animal/passive/dog/tamaskan), + list(/mob/living/simple_mob/animal/passive/fox), + list(/mob/living/simple_mob/animal/passive/hare), + list(/mob/living/simple_mob/animal/passive/lizard), + list(/mob/living/simple_mob/animal/passive/mouse), + list(/mob/living/simple_mob/animal/passive/mouse/jerboa), + list(/mob/living/simple_mob/animal/passive/opossum), + list(/mob/living/simple_mob/animal/passive/pillbug), + list(/mob/living/simple_mob/animal/passive/snake), + list(/mob/living/simple_mob/animal/passive/snake/red), + list(/mob/living/simple_mob/animal/passive/snake/python), + list(/mob/living/simple_mob/animal/passive/tindalos), + list(/mob/living/simple_mob/animal/passive/yithian), + list( + /mob/living/simple_mob/vore/wolf = 10, + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ), + list(/mob/living/simple_mob/vore/rabbit), + list(/mob/living/simple_mob/vore/redpanda), + list(/mob/living/simple_mob/vore/woof), + list(/mob/living/simple_mob/vore/fennec), + list(/mob/living/simple_mob/vore/fennix), + list(/mob/living/simple_mob/vore/hippo), + list(/mob/living/simple_mob/vore/horse), + list(/mob/living/simple_mob/vore/bee), + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ), + list( + /mob/living/simple_mob/vore/otie/feral, + /mob/living/simple_mob/vore/otie/feral/chubby, + /mob/living/simple_mob/vore/otie/red, + /mob/living/simple_mob/vore/otie/red/chubby + ), + list(/mob/living/simple_mob/animal/sif/diyaab), + list(/mob/living/simple_mob/animal/sif/duck), + list(/mob/living/simple_mob/animal/sif/frostfly), + list( + /mob/living/simple_mob/animal/sif/glitterfly =50, + /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 + ), + list( + /mob/living/simple_mob/animal/sif/kururak = 10, + /mob/living/simple_mob/animal/sif/kururak/leader = 1, + /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, + ), + list( + /mob/living/simple_mob/animal/sif/sakimm = 10, + /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 + ), + list(/mob/living/simple_mob/animal/sif/savik) = 5, + list( + /mob/living/simple_mob/animal/sif/shantak = 10, + /mob/living/simple_mob/animal/sif/shantak/leader = 1 + ), + list(/mob/living/simple_mob/animal/sif/siffet), + list(/mob/living/simple_mob/animal/sif/tymisian), + list( + /mob/living/simple_mob/animal/giant_spider/electric = 5, + /mob/living/simple_mob/animal/giant_spider/frost = 5, + /mob/living/simple_mob/animal/giant_spider/hunter = 10, + /mob/living/simple_mob/animal/giant_spider/ion = 5, + /mob/living/simple_mob/animal/giant_spider/lurker = 10, + /mob/living/simple_mob/animal/giant_spider/pepper = 10, + /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, + /mob/living/simple_mob/animal/giant_spider/thermic = 5, + /mob/living/simple_mob/animal/giant_spider/tunneler = 10, + /mob/living/simple_mob/animal/giant_spider/webslinger = 5, + /mob/living/simple_mob/animal/giant_spider/broodmother = 1), + list(/mob/living/simple_mob/creature/strong), + list(/mob/living/simple_mob/faithless/strong), + list(/mob/living/simple_mob/animal/goat), + list( + /mob/living/simple_mob/animal/sif/shantak/leader = 1, + /mob/living/simple_mob/animal/sif/shantak = 10), + list(/mob/living/simple_mob/animal/sif/savik,), + list(/mob/living/simple_mob/animal/sif/hooligan_crab), + list( + /mob/living/simple_mob/animal/space/alien = 50, + /mob/living/simple_mob/animal/space/alien/drone = 40, + /mob/living/simple_mob/animal/space/alien/sentinel = 25, + /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, + /mob/living/simple_mob/animal/space/alien/queen = 10, + /mob/living/simple_mob/animal/space/alien/queen/empress = 5, + /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1 + ), + list(/mob/living/simple_mob/animal/space/bats/cult/strong), + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ), + list( + /mob/living/simple_mob/animal/space/carp = 50, + /mob/living/simple_mob/animal/space/carp/large = 10, + /mob/living/simple_mob/animal/space/carp/large/huge = 5 + ), + list(/mob/living/simple_mob/animal/space/goose), + list(/mob/living/simple_mob/vore/jelly), + list(/mob/living/simple_mob/animal/space/tree), + list( + /mob/living/simple_mob/vore/aggressive/corrupthound = 10, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, + ), + list(/mob/living/simple_mob/vore/aggressive/deathclaw), + list(/mob/living/simple_mob/vore/aggressive/dino), + list(/mob/living/simple_mob/vore/aggressive/dragon), + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), + list(/mob/living/simple_mob/vore/aggressive/frog), + list(/mob/living/simple_mob/vore/aggressive/giant_snake), + list(/mob/living/simple_mob/vore/aggressive/mimic), + list(/mob/living/simple_mob/vore/aggressive/panther), + list(/mob/living/simple_mob/vore/aggressive/rat), + list(/mob/living/simple_mob/vore/bee), + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ), + list(/mob/living/simple_mob/vore/solargrub), + list( + /mob/living/simple_mob/vore/oregrub = 5, + /mob/living/simple_mob/vore/oregrub/lava = 1 + ), + list(/mob/living/simple_mob/vore/catgirl), + list(/mob/living/simple_mob/vore/wolfgirl), + list( + /mob/living/simple_mob/vore/lamia, + /mob/living/simple_mob/vore/lamia/albino, + /mob/living/simple_mob/vore/lamia/albino/bra, + /mob/living/simple_mob/vore/lamia/albino/shirt, + /mob/living/simple_mob/vore/lamia/bra, + /mob/living/simple_mob/vore/lamia/cobra, + /mob/living/simple_mob/vore/lamia/cobra/bra, + /mob/living/simple_mob/vore/lamia/cobra/shirt, + /mob/living/simple_mob/vore/lamia/copper, + /mob/living/simple_mob/vore/lamia/copper/bra, + /mob/living/simple_mob/vore/lamia/copper/shirt, + /mob/living/simple_mob/vore/lamia/green, + /mob/living/simple_mob/vore/lamia/green/bra, + /mob/living/simple_mob/vore/lamia/green/shirt, + /mob/living/simple_mob/vore/lamia/zebra, + /mob/living/simple_mob/vore/lamia/zebra/bra, + /mob/living/simple_mob/vore/lamia/zebra/shirt + ), + list( + /mob/living/simple_mob/humanoid/merc = 100, + /mob/living/simple_mob/humanoid/merc/melee/sword = 50, + /mob/living/simple_mob/humanoid/merc/ranged = 25, + /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, + /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, + /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, + /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, + /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, + /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, + /mob/living/simple_mob/humanoid/merc/ranged/space = 10, + /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 + ), + list( + /mob/living/simple_mob/humanoid/pirate = 3, + /mob/living/simple_mob/humanoid/pirate/ranged = 1 + ), + list(/mob/living/simple_mob/mechanical/combat_drone), + list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), + list( + /mob/living/simple_mob/mechanical/hivebot = 100, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, + /mob/living/simple_mob/mechanical/hivebot/support = 8, + /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, + /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, + /mob/living/simple_mob/mechanical/hivebot/swarm = 20, + /mob/living/simple_mob/mechanical/hivebot/tank = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 + ), + list(/mob/living/simple_mob/mechanical/infectionbot), + list(/mob/living/simple_mob/mechanical/mining_drone), + list(/mob/living/simple_mob/mechanical/technomancer_golem), + list( + /mob/living/simple_mob/mechanical/viscerator, + /mob/living/simple_mob/mechanical/viscerator/piercing + ), + list(/mob/living/simple_mob/mechanical/wahlem), + list(/mob/living/simple_mob/animal/passive/fox/syndicate), + list(/mob/living/simple_mob/animal/passive/fox), + list(/mob/living/simple_mob/vore/jelly), + list( + /mob/living/simple_mob/vore/otie/feral, + /mob/living/simple_mob/vore/otie/feral/chubby, + /mob/living/simple_mob/vore/otie/red, + /mob/living/simple_mob/vore/otie/red/chubby + ), + list( + /mob/living/simple_mob/shadekin/blue = 100, + /mob/living/simple_mob/shadekin/green = 50, + /mob/living/simple_mob/shadekin/orange = 20, + /mob/living/simple_mob/shadekin/purple = 60, + /mob/living/simple_mob/shadekin/red = 40, + /mob/living/simple_mob/shadekin/yellow = 1 + ), + list( + /mob/living/simple_mob/vore/aggressive/corrupthound, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi + ), + list(/mob/living/simple_mob/vore/aggressive/deathclaw), + list(/mob/living/simple_mob/vore/aggressive/dino), + list(/mob/living/simple_mob/vore/aggressive/dragon), + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b), + list(/mob/living/simple_mob/vore/aggressive/frog), + list(/mob/living/simple_mob/vore/aggressive/giant_snake), + list(/mob/living/simple_mob/vore/aggressive/mimic), + list(/mob/living/simple_mob/vore/aggressive/panther), + list(/mob/living/simple_mob/vore/aggressive/rat), + list(/mob/living/simple_mob/vore/bee), + list(/mob/living/simple_mob/vore/catgirl), + list(/mob/living/simple_mob/vore/cookiegirl), + list(/mob/living/simple_mob/vore/fennec), + list(/mob/living/simple_mob/vore/fennix), + list(/mob/living/simple_mob/vore/hippo), + list(/mob/living/simple_mob/vore/horse), + list(/mob/living/simple_mob/vore/oregrub), + list(/mob/living/simple_mob/vore/rabbit), + list( + /mob/living/simple_mob/vore/redpanda = 50, + /mob/living/simple_mob/vore/redpanda/fae = 1 + ), + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ), + list(/mob/living/simple_mob/vore/solargrub), + list(/mob/living/simple_mob/vore/woof), + list(/mob/living/simple_mob/vore/alienanimals/space_ghost), + list(/mob/living/simple_mob/vore/alienanimals/catslug), + list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish), + list(/mob/living/simple_mob/vore/alienanimals/startreader), + list( + /mob/living/simple_mob/vore/bigdragon, + /mob/living/simple_mob/vore/bigdragon/friendly), + list( + /mob/living/simple_mob/vore/leopardmander = 50, + /mob/living/simple_mob/vore/leopardmander/blue = 10, + /mob/living/simple_mob/vore/leopardmander/exotic = 1 + ), + list(/mob/living/simple_mob/vore/sheep), + list(/mob/living/simple_mob/vore/weretiger) + ) + +/obj/random/mob/semirandom_mob_spawner/item_to_spawn() + var/list/choice = semirandom_mob_spawner_decisions[type] + + if(!choice) + choice = pickweight(possible_mob_types) + semirandom_mob_spawner_decisions[type] = choice + + return pickweight(choice) + +/obj/random/mob/semirandom_mob_spawner/animal + name = "Semi-Random Animal" + desc = "Spawns groups of non-hostile mobs that are all of the same theme type/theme." + icon_state = "animal" + mob_faction = "animal" + overwrite_hostility = 1 + mob_hostile = 0 + + possible_mob_types = list( + list(/mob/living/simple_mob/animal/goat) = 25, + list( + /mob/living/simple_mob/animal/passive/bird, + /mob/living/simple_mob/animal/passive/bird/azure_tit, + /mob/living/simple_mob/animal/passive/bird/black_bird, + /mob/living/simple_mob/animal/passive/bird/european_robin, + /mob/living/simple_mob/animal/passive/bird/goldcrest, + /mob/living/simple_mob/animal/passive/bird/ringneck_dove, + /mob/living/simple_mob/animal/passive/bird/parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue, + /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white, + /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish, + /mob/living/simple_mob/animal/passive/bird/parrot/eclectus, + /mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot, + /mob/living/simple_mob/animal/passive/bird/parrot/kea, + /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, + /mob/living/simple_mob/animal/passive/bird/parrot/white_caique, + /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo, + /mob/living/simple_mob/animal/space/goose + ) = 25, + list( + /mob/living/simple_mob/animal/passive/cat, + /mob/living/simple_mob/animal/passive/cat/black + ) = 25, + list( + /mob/living/simple_mob/animal/passive/chick, + /mob/living/simple_mob/animal/passive/chicken + ) = 25, + list(/mob/living/simple_mob/animal/passive/cow) = 25, + list(/mob/living/simple_mob/animal/passive/dog/brittany) = 10, + list(/mob/living/simple_mob/animal/passive/dog/corgi) = 10, + list(/mob/living/simple_mob/animal/passive/dog/tamaskan) = 10, + list(/mob/living/simple_mob/animal/passive/fox) = 25, + list(/mob/living/simple_mob/animal/passive/hare) = 25, + list(/mob/living/simple_mob/animal/passive/lizard) = 10, + list(/mob/living/simple_mob/animal/passive/mouse) = 15, + list(/mob/living/simple_mob/animal/passive/mouse/jerboa) = 5, + list(/mob/living/simple_mob/animal/passive/opossum) = 10, + list(/mob/living/simple_mob/animal/passive/pillbug) = 10, + list(/mob/living/simple_mob/animal/passive/snake) = 10, + list(/mob/living/simple_mob/animal/passive/snake/red) = 10, + list(/mob/living/simple_mob/animal/passive/snake/python) = 10, + list(/mob/living/simple_mob/animal/passive/tindalos) = 10, + list(/mob/living/simple_mob/animal/passive/yithian) = 10, + list( + /mob/living/simple_mob/vore/wolf = 10, + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ) = 10, + list(/mob/living/simple_mob/vore/rabbit) = 10, + list(/mob/living/simple_mob/vore/redpanda) = 10, + list(/mob/living/simple_mob/vore/woof) = 1, + list(/mob/living/simple_mob/vore/fennec) = 10, + list(/mob/living/simple_mob/vore/fennix) = 1, + list(/mob/living/simple_mob/vore/hippo) = 5, + list(/mob/living/simple_mob/vore/horse) = 25, + list(/mob/living/simple_mob/vore/bee) = 10, + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ) = 1, + list( + /mob/living/simple_mob/vore/otie/feral = 50, + /mob/living/simple_mob/vore/otie/feral/chubby = 10, + /mob/living/simple_mob/vore/otie/red = 5, + /mob/living/simple_mob/vore/otie/red/chubby = 1 + ) = 5, + list(/mob/living/simple_mob/vore/aggressive/rat) = 15, + list(/mob/living/simple_mob/animal/sif/diyaab) = 5, + list(/mob/living/simple_mob/animal/sif/duck) = 5, + list(/mob/living/simple_mob/animal/sif/frostfly) = 5, + list( + /mob/living/simple_mob/animal/sif/glitterfly = 50, + /mob/living/simple_mob/animal/sif/glitterfly/rare = 1 + ) = 5, + list( + /mob/living/simple_mob/animal/sif/kururak = 10, + /mob/living/simple_mob/animal/sif/kururak/leader = 1, + /mob/living/simple_mob/animal/sif/kururak/hibernate = 2, + ) = 5, + list( + /mob/living/simple_mob/animal/sif/sakimm = 10, + /mob/living/simple_mob/animal/sif/sakimm/intelligent = 1 + ) = 5, + list(/mob/living/simple_mob/animal/sif/savik) = 5, + list( + /mob/living/simple_mob/animal/sif/shantak = 10, + /mob/living/simple_mob/animal/sif/shantak/leader = 1 + ) = 5, + list(/mob/living/simple_mob/animal/sif/siffet) = 5, + list(/mob/living/simple_mob/animal/sif/tymisian) = 5, + list(/mob/living/simple_mob/vore/alienanimals/dustjumper) = 5, + list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, + list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, + list( + /mob/living/simple_mob/vore/leopardmander = 50, + /mob/living/simple_mob/vore/leopardmander/blue = 10, + /mob/living/simple_mob/vore/leopardmander/exotic = 1 + ) = 5, + list(/mob/living/simple_mob/vore/sheep) = 5, + list(/mob/living/simple_mob/vore/weretiger) = 5, + list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5 + ) + +/obj/random/mob/semirandom_mob_spawner/monster + name = "Semi-Random Monster" + desc = "Spawns groups of hostile mobs that are all of the same theme type/theme." + overwrite_hostility = 1 + mob_faction = "monster" + mob_hostile = 1 + mob_retaliate = 1 + + possible_mob_types = list( + list( + /mob/living/simple_mob/animal/giant_spider/electric = 5, + /mob/living/simple_mob/animal/giant_spider/frost = 5, + /mob/living/simple_mob/animal/giant_spider/hunter = 10, + /mob/living/simple_mob/animal/giant_spider/ion = 5, + /mob/living/simple_mob/animal/giant_spider/lurker = 10, + /mob/living/simple_mob/animal/giant_spider/pepper = 10, + /mob/living/simple_mob/animal/giant_spider/phorogenic = 10, + /mob/living/simple_mob/animal/giant_spider/thermic = 5, + /mob/living/simple_mob/animal/giant_spider/tunneler = 10, + /mob/living/simple_mob/animal/giant_spider/webslinger = 5 + ) = 100, + list( + /mob/living/simple_mob/shadekin/red = 5, + /mob/living/simple_mob/shadekin/orange = 1, + /mob/living/simple_mob/shadekin/purple = 10 + ) = 1, + list( + /mob/living/simple_mob/vore/wolf = 10, + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ) = 40, + list(/mob/living/simple_mob/creature/strong) = 40, + list(/mob/living/simple_mob/faithless/strong) = 20, + list(/mob/living/simple_mob/animal/goat) = 1, + list( + /mob/living/simple_mob/animal/sif/shantak/leader = 1, + /mob/living/simple_mob/animal/sif/shantak = 10 + ) = 50, + list(/mob/living/simple_mob/animal/sif/savik,) = 20, + list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 10, + list( + /mob/living/simple_mob/animal/space/alien = 50, + /mob/living/simple_mob/animal/space/alien/drone = 40, + /mob/living/simple_mob/animal/space/alien/sentinel = 25, + /mob/living/simple_mob/animal/space/alien/sentinel/praetorian = 15, + /mob/living/simple_mob/animal/space/alien/queen = 10, + /mob/living/simple_mob/animal/space/alien/queen/empress = 5, + /mob/living/simple_mob/animal/space/alien/queen/empress/mother = 1, + ) = 40, + list(/mob/living/simple_mob/animal/space/bats/cult/strong) = 40, + list( + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/animal/space/bear/brown + ) = 40, + list( + /mob/living/simple_mob/animal/space/carp = 50, + /mob/living/simple_mob/animal/space/carp/large = 10, + /mob/living/simple_mob/animal/space/carp/large/huge = 5 + ) = 50, + list(/mob/living/simple_mob/animal/space/goose) = 50, + list(/mob/living/simple_mob/vore/jelly) = 40, + list(/mob/living/simple_mob/animal/space/tree) = 15, + list( + /mob/living/simple_mob/vore/otie/feral = 50, + /mob/living/simple_mob/vore/otie/feral/chubby = 10, + /mob/living/simple_mob/vore/otie/red = 5, + /mob/living/simple_mob/vore/otie/red/chubby = 1 + ) = 40, + list( + /mob/living/simple_mob/vore/aggressive/corrupthound = 10, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi = 1, + ) = 50, + list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 40, + list(/mob/living/simple_mob/vore/aggressive/dino) = 40, + list(/mob/living/simple_mob/vore/aggressive/dragon) = 40, + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 40, + list(/mob/living/simple_mob/vore/aggressive/frog) = 40, + list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 40, + list(/mob/living/simple_mob/vore/aggressive/mimic) = 40, + list(/mob/living/simple_mob/vore/aggressive/panther) = 25, + list(/mob/living/simple_mob/vore/aggressive/rat) = 50, + list(/mob/living/simple_mob/vore/bee) = 40, + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ) = 20, + list(/mob/living/simple_mob/vore/solargrub) = 15, + list( + /mob/living/simple_mob/vore/oregrub = 5, + /mob/living/simple_mob/vore/oregrub/lava = 1 + ) = 15, + list(/mob/living/simple_mob/vore/alienanimals/teppi) = 15, + list(/mob/living/simple_mob/vore/alienanimals/space_jellyfish) = 5, + list(/mob/living/simple_mob/vore/alienanimals/space_ghost) = 5, + list( + /mob/living/simple_mob/vore/leopardmander = 50, + /mob/living/simple_mob/vore/leopardmander/blue = 10, + /mob/living/simple_mob/vore/leopardmander/exotic = 1 + ) = 5, + list(/mob/living/simple_mob/vore/sheep) = 5, + list(/mob/living/simple_mob/vore/weretiger) = 5, + list(/mob/living/simple_mob/vore/alienanimals/skeleton) = 5, + list(/mob/living/simple_mob/vore/alienanimals/catslug) = 5 + ) + +/obj/random/mob/semirandom_mob_spawner/humanoid + name = "Semi-Random Humanoid" + desc = "Spawns groups of humanoid mobs that may or may not be hostile, all of the same theme type/theme." + icon_state = "humanoid" + mob_faction = "humanoid" + + possible_mob_types = list( + list( + /mob/living/simple_mob/shadekin/blue = 25, + /mob/living/simple_mob/shadekin/green = 10, + /mob/living/simple_mob/shadekin/purple = 1, + ) = 1, + list(/mob/living/simple_mob/vore/catgirl) = 100, + list(/mob/living/simple_mob/vore/wolfgirl) = 100, + list( + /mob/living/simple_mob/vore/lamia, + /mob/living/simple_mob/vore/lamia/albino, + /mob/living/simple_mob/vore/lamia/albino/bra, + /mob/living/simple_mob/vore/lamia/albino/shirt, + /mob/living/simple_mob/vore/lamia/bra, + /mob/living/simple_mob/vore/lamia/cobra, + /mob/living/simple_mob/vore/lamia/cobra/bra, + /mob/living/simple_mob/vore/lamia/cobra/shirt, + /mob/living/simple_mob/vore/lamia/copper, + /mob/living/simple_mob/vore/lamia/copper/bra, + /mob/living/simple_mob/vore/lamia/copper/shirt, + /mob/living/simple_mob/vore/lamia/green, + /mob/living/simple_mob/vore/lamia/green/bra, + /mob/living/simple_mob/vore/lamia/green/shirt, + /mob/living/simple_mob/vore/lamia/zebra, + /mob/living/simple_mob/vore/lamia/zebra/bra, + /mob/living/simple_mob/vore/lamia/zebra/shirt + ) = 100, +// LOOK OKAY MERCS ARE HUMANOIDS SO THEY ARE HERE, but they are also kind of bullshit so they probably shouldn't be able to spawn in the same place as catgirls. +// I want some better potentially hostile humanoids that aren't stupid to fight. If they become a big issue I'll comment them out. +// For now they are just rare, and the ranged ones are way more rare than the melee ones, which I think will help balance them out. + list( + /mob/living/simple_mob/humanoid/merc = 100, + /mob/living/simple_mob/humanoid/merc/melee/sword = 50, + /mob/living/simple_mob/humanoid/merc/ranged = 25, + /mob/living/simple_mob/humanoid/merc/ranged/grenadier = 1, + /mob/living/simple_mob/humanoid/merc/ranged/ionrifle = 10, + /mob/living/simple_mob/humanoid/merc/ranged/laser = 5, + /mob/living/simple_mob/humanoid/merc/ranged/rifle = 5, + /mob/living/simple_mob/humanoid/merc/ranged/smg = 5, + /mob/living/simple_mob/humanoid/merc/ranged/sniper = 1, + /mob/living/simple_mob/humanoid/merc/ranged/space = 10, + /mob/living/simple_mob/humanoid/merc/ranged/technician = 5 + ) = 5, +// PIRATES are okay though. They can be kind of a pain but you can kind of slap them around. Also it's not like. A crime. To fight and blow up pirates so it's fine. + list( + /mob/living/simple_mob/humanoid/pirate = 3, + /mob/living/simple_mob/humanoid/pirate/ranged = 1 + ) = 50 + ) + +// I am not familiar enough with robots to know which ones are fun to fight so this list isn't weighted at all SO YOU KNOW. Be careful. +/obj/random/mob/semirandom_mob_spawner/robot + name = "Semi-Random Robot" + desc = "Spawns groups of robotic mobs that are probably hostile, all of the same theme type/theme." + icon_state = "robot" + mob_faction = "robot" + + possible_mob_types = list( + list(/mob/living/simple_mob/mechanical/combat_drone), + list(/mob/living/simple_mob/mechanical/corrupt_maint_drone), + list( + /mob/living/simple_mob/mechanical/hivebot = 100, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion = 20, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser = 10, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid = 2, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp = 5, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation = 1, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong = 3, + /mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard = 3, + /mob/living/simple_mob/mechanical/hivebot/support = 8, + /mob/living/simple_mob/mechanical/hivebot/support/commander = 5, + /mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow = 10, + /mob/living/simple_mob/mechanical/hivebot/swarm = 20, + /mob/living/simple_mob/mechanical/hivebot/tank = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee = 20, + /mob/living/simple_mob/mechanical/hivebot/tank/meatshield = 20 + ), + list(/mob/living/simple_mob/mechanical/infectionbot), + list(/mob/living/simple_mob/mechanical/mining_drone), + list(/mob/living/simple_mob/mechanical/technomancer_golem), + list( + /mob/living/simple_mob/mechanical/viscerator, + /mob/living/simple_mob/mechanical/viscerator/piercing + ), + list(/mob/living/simple_mob/mechanical/wahlem), + list(/mob/living/simple_mob/animal/passive/fox/syndicate) + ) + +/obj/random/mob/semirandom_mob_spawner/fish + name = "Semi-Random Fish" + desc = "Spawns groups of fish, all of the same theme type/theme." + icon_state = "fish" + mob_faction = "fish" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 0 + + + possible_mob_types = list( + list(/mob/living/simple_mob/animal/passive/fish/bass) = 20, + list(/mob/living/simple_mob/animal/passive/fish/icebass) = 20, + list(/mob/living/simple_mob/animal/passive/fish/javelin) = 20, + list(/mob/living/simple_mob/animal/passive/fish/koi) = 10, + list(/mob/living/simple_mob/animal/passive/fish/measelshark) = 5, + list(/mob/living/simple_mob/animal/passive/fish/murkin) = 20, + list(/mob/living/simple_mob/animal/passive/fish/perch) = 20, + list(/mob/living/simple_mob/animal/passive/fish/pike) = 20, + list(/mob/living/simple_mob/animal/passive/fish/rockfish) = 10, + list(/mob/living/simple_mob/animal/passive/fish/salmon) = 20, + list(/mob/living/simple_mob/animal/passive/fish/solarfish) = 5, + list(/mob/living/simple_mob/animal/passive/fish/trout) = 20, + list(/mob/living/simple_mob/animal/passive/crab) = 10, + list(/mob/living/simple_mob/animal/sif/hooligan_crab) = 1 + ) + +/obj/random/mob/semirandom_mob_spawner/bird + name = "Semi-Random Bird" + desc = "Spawns groups of bird, all of the same theme type/theme." + icon_state = "bird" + mob_faction = "bird" + + possible_mob_types = list( + list(/mob/living/simple_mob/animal/passive/bird), + list(/mob/living/simple_mob/animal/passive/bird/azure_tit), + list(/mob/living/simple_mob/animal/passive/bird/black_bird), + list(/mob/living/simple_mob/animal/passive/bird/european_robin), + list(/mob/living/simple_mob/animal/passive/bird/goldcrest), + list(/mob/living/simple_mob/animal/passive/bird/ringneck_dove), + list(/mob/living/simple_mob/animal/passive/bird/parrot), + list(/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique), + list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar), + list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue), + list(/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white), + list(/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish), + list(/mob/living/simple_mob/animal/passive/bird/parrot/eclectus), + list(/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot), + list(/mob/living/simple_mob/animal/passive/bird/parrot/kea), + list(/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo), + list(/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo), + list(/mob/living/simple_mob/animal/passive/bird/parrot/white_caique), + list(/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo), + list(/mob/living/simple_mob/animal/space/goose), + list(/mob/living/simple_mob/animal/passive/chicken), + list(/mob/living/simple_mob/animal/passive/penguin) + ) + +/obj/random/mob/semirandom_mob_spawner/vore + name = "Semi-Random Voremob" + desc = "Spawns groups of voremobs, all of the same theme type/theme." + icon_state = "vore" + mob_faction = "vore" + + possible_mob_types = list( + list( + /mob/living/simple_mob/vore/wolf/direwolf = 5, + /mob/living/simple_mob/vore/greatwolf = 1, + /mob/living/simple_mob/vore/greatwolf/black = 1, + /mob/living/simple_mob/vore/greatwolf/grey = 1 + ) = 100, + list(/mob/living/simple_mob/vore/jelly) = 70, + list( + /mob/living/simple_mob/vore/otie/feral, + /mob/living/simple_mob/vore/otie/feral/chubby, + /mob/living/simple_mob/vore/otie/red, + /mob/living/simple_mob/vore/otie/red/chubby + ) = 50, + list( + /mob/living/simple_mob/shadekin/blue = 100, + /mob/living/simple_mob/shadekin/green = 50, + /mob/living/simple_mob/shadekin/orange = 20, + /mob/living/simple_mob/shadekin/purple = 60, + /mob/living/simple_mob/shadekin/red = 40, + /mob/living/simple_mob/shadekin/yellow = 1 + ) = 1, + list( + /mob/living/simple_mob/vore/aggressive/corrupthound, + /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi + ) = 70, + list(/mob/living/simple_mob/vore/aggressive/deathclaw) = 70, + list(/mob/living/simple_mob/vore/aggressive/dino) = 100, + list(/mob/living/simple_mob/vore/aggressive/dragon) = 100, + list(/mob/living/simple_mob/vore/aggressive/dragon/virgo3b) = 100, + list(/mob/living/simple_mob/vore/aggressive/frog) = 100, + list(/mob/living/simple_mob/vore/aggressive/giant_snake) = 100, + list(/mob/living/simple_mob/vore/aggressive/mimic) = 50, + list(/mob/living/simple_mob/vore/aggressive/panther) = 70, + list(/mob/living/simple_mob/vore/aggressive/rat) = 100, + list(/mob/living/simple_mob/vore/bee) = 100, + list(/mob/living/simple_mob/vore/catgirl) = 100, + list(/mob/living/simple_mob/vore/wolftaur) = 100, + list(/mob/living/simple_mob/vore/cookiegirl) = 100, + list(/mob/living/simple_mob/vore/fennec) = 100, + list(/mob/living/simple_mob/vore/fennix) = 50, + list(/mob/living/simple_mob/vore/hippo) = 70, + list(/mob/living/simple_mob/vore/horse) = 100, + list(/mob/living/simple_mob/vore/raptor) = 100, + list(/mob/living/simple_mob/vore/succubus) = 100, + list(/mob/living/simple_mob/vore/vampire) = 50, + list(/mob/living/simple_mob/vore/vampire/queen) = 1, + list(/mob/living/simple_mob/vore/bat) = 50, + list(/mob/living/simple_mob/vore/scel) = 10, + list( + /mob/living/simple_mob/vore/lamia, + /mob/living/simple_mob/vore/lamia/albino, + /mob/living/simple_mob/vore/lamia/albino/bra, + /mob/living/simple_mob/vore/lamia/albino/shirt, + /mob/living/simple_mob/vore/lamia/bra, + /mob/living/simple_mob/vore/lamia/cobra, + /mob/living/simple_mob/vore/lamia/cobra/bra, + /mob/living/simple_mob/vore/lamia/cobra/shirt, + /mob/living/simple_mob/vore/lamia/copper, + /mob/living/simple_mob/vore/lamia/copper/bra, + /mob/living/simple_mob/vore/lamia/copper/shirt, + /mob/living/simple_mob/vore/lamia/green, + /mob/living/simple_mob/vore/lamia/green/bra, + /mob/living/simple_mob/vore/lamia/green/shirt, + /mob/living/simple_mob/vore/lamia/zebra, + /mob/living/simple_mob/vore/lamia/zebra/bra, + /mob/living/simple_mob/vore/lamia/zebra/shirt + ) = 100, + list(/mob/living/simple_mob/vore/rabbit) = 100, + list( + /mob/living/simple_mob/vore/redpanda = 50, + /mob/living/simple_mob/vore/redpanda/fae = 1 + ) = 100, + list( + /mob/living/simple_mob/vore/sect_drone = 10, + /mob/living/simple_mob/vore/sect_queen = 1 + ) = 50, + list(/mob/living/simple_mob/vore/solargrub) = 100, + list(/mob/living/simple_mob/vore/woof) = 1 + ) + +/obj/random/mob/semirandom_mob_spawner/sus + name = "Weird shit" + desc = "Spawns groups of weird stuff, all of the same theme type/theme. Don't put this on normal maps." + icon_state = "sus" + mob_faction = "sus" + + possible_mob_types = list( + list( + /mob/living/simple_mob/vore/woof/hostile/melee = 100, + /mob/living/simple_mob/vore/woof/hostile/ranged = 20, + /mob/living/simple_mob/vore/woof/hostile/horrible = 10, + /mob/living/simple_mob/vore/woof/hostile/terrible = 5, + /mob/living/simple_mob/vore/woof/cass = 1 + ), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/recursive) + ) + +/obj/random/mob/semirandom_mob_spawner/mecha + name = "Semi-Random Mecha" + desc = "Spawns groups of mechs, all of the same theme type/theme. Don't put this on normal maps." + icon_state = "mecha" + mob_faction = "mecha" + + possible_mob_types = list( + list(/mob/living/simple_mob/mechanical/mecha/combat/durand), + list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive), + list(/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned), + list(/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax), + list(/mob/living/simple_mob/mechanical/mecha/combat/marauder), + list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler), + list(/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph), + list(/mob/living/simple_mob/mechanical/mecha/combat/phazon), + list(/mob/living/simple_mob/mechanical/mecha/hoverpod), + list(/mob/living/simple_mob/mechanical/mecha/hoverpod/manned), + list(/mob/living/simple_mob/mechanical/mecha/odysseus), + list(/mob/living/simple_mob/mechanical/mecha/odysseus/manned), + list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus), + list(/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley), + list(/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames), + list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley), + list(/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter), + list(/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley/manned), + list(/mob/living/simple_mob/mechanical/mecha/ripley/red_flames) + ) + + +/obj/random/mob/semirandom_mob_spawner/monster/b + mob_faction = "monsterb" + +/obj/random/mob/semirandom_mob_spawner/monster/c + mob_faction = "monsterc" + +/obj/random/mob/semirandom_mob_spawner/monster/d + mob_faction = "monsterd" + +/obj/random/mob/semirandom_mob_spawner/monster/e + mob_faction = "monstere" + +/obj/random/mob/semirandom_mob_spawner/monster/f + mob_faction = "monsterf" + +/obj/random/mob/semirandom_mob_spawner/animal/b + mob_faction = "animalb" + +/obj/random/mob/semirandom_mob_spawner/animal/c + mob_faction = "animalc" + +/obj/random/mob/semirandom_mob_spawner/animal/d + mob_faction = "animald" + +/obj/random/mob/semirandom_mob_spawner/animal/e + mob_faction = "animale" + +/obj/random/mob/semirandom_mob_spawner/animal/f + mob_faction = "animalf" + +/obj/random/mob/semirandom_mob_spawner/animal/retaliate + mob_faction = "retanimala" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/animal/retaliate/b + mob_faction = "retanimalb" + +/obj/random/mob/semirandom_mob_spawner/animal/retaliate/c + mob_faction = "retanimalc" + +/obj/random/mob/semirandom_mob_spawner/animal/hostile + mob_faction = "hosanimala" + overwrite_hostility = 1 + mob_hostile = 1 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/animal/hostile/b + mob_faction = "hosanimalb" + +/obj/random/mob/semirandom_mob_spawner/animal/hostile/c + mob_faction = "hosanimalc" + + +/obj/random/mob/semirandom_mob_spawner/humanoid/b + mob_faction = "humanoidb" + +/obj/random/mob/semirandom_mob_spawner/humanoid/c + mob_faction = "humanoidc" + +/obj/random/mob/semirandom_mob_spawner/humanoid/d + mob_faction = "humanoidd" + +/obj/random/mob/semirandom_mob_spawner/humanoid/e + mob_faction = "humanoide" + +/obj/random/mob/semirandom_mob_spawner/humanoid/f + mob_faction = "humanoidf" + +/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate + mob_faction = "rethumanoida" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/b + mob_faction = "rethumanoidb" + +/obj/random/mob/semirandom_mob_spawner/humanoid/retaliate/c + mob_faction = "rethumanoidc" + +/obj/random/mob/semirandom_mob_spawner/humanoid/hostile + mob_faction = "hoshumanoida" + overwrite_hostility = 1 + mob_hostile = 1 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/b + mob_faction = "hoshumanoidb" + +/obj/random/mob/semirandom_mob_spawner/humanoid/hostile/c + mob_faction = "hoshumanoidc" + +/obj/random/mob/semirandom_mob_spawner/robot/b + mob_faction = "robotb" + +/obj/random/mob/semirandom_mob_spawner/robot/c + mob_faction = "robotc" + +/obj/random/mob/semirandom_mob_spawner/robot/d + mob_faction = "robotd" + +/obj/random/mob/semirandom_mob_spawner/robot/e + mob_faction = "robote" + +/obj/random/mob/semirandom_mob_spawner/robot/f + mob_faction = "robotf" + +/obj/random/mob/semirandom_mob_spawner/robot/retaliate + mob_faction = "retrobota" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/robot/retaliate/b + mob_faction = "retrobotb" + +/obj/random/mob/semirandom_mob_spawner/robot/retaliate/c + mob_faction = "retrobotc" + +/obj/random/mob/semirandom_mob_spawner/bird/b + mob_faction = "birdb" + +/obj/random/mob/semirandom_mob_spawner/bird/c + mob_faction = "birdc" + +/obj/random/mob/semirandom_mob_spawner/bird/d + mob_faction = "birdd" + +/obj/random/mob/semirandom_mob_spawner/bird/e + mob_faction = "birde" + +/obj/random/mob/semirandom_mob_spawner/bird/f + mob_faction = "birdf" + +/obj/random/mob/semirandom_mob_spawner/fish/b + mob_faction = "fishb" + +/obj/random/mob/semirandom_mob_spawner/fish/c + mob_faction = "fishc" + +/obj/random/mob/semirandom_mob_spawner/fish/d + mob_faction = "fishd" + +/obj/random/mob/semirandom_mob_spawner/fish/e + mob_faction = "fishe" + +/obj/random/mob/semirandom_mob_spawner/fish/f + mob_faction = "fishf" + +/obj/random/mob/semirandom_mob_spawner/vore/b + mob_faction = "voreb" + +/obj/random/mob/semirandom_mob_spawner/vore/c + mob_faction = "vorec" + +/obj/random/mob/semirandom_mob_spawner/vore/d + mob_faction = "vored" + +/obj/random/mob/semirandom_mob_spawner/vore/e + mob_faction = "voree" + +/obj/random/mob/semirandom_mob_spawner/vore/f + mob_faction = "voref" + +/obj/random/mob/semirandom_mob_spawner/vore/passive + mob_faction = "pasvorea" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 0 + +/obj/random/mob/semirandom_mob_spawner/vore/passive/b + mob_faction = "pasvoreb" + +/obj/random/mob/semirandom_mob_spawner/vore/passive/c + mob_faction = "pasvorec" + +/obj/random/mob/semirandom_mob_spawner/vore/retaliate + mob_faction = "retvorea" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/vore/retaliate/b + mob_faction = "retvoreb" + +/obj/random/mob/semirandom_mob_spawner/vore/retaliate/c + mob_faction = "retvorec" + +/obj/random/mob/semirandom_mob_spawner/vore/hostile + mob_faction = "hosvorea" + overwrite_hostility = 1 + mob_hostile = 1 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/vore/hostile/b + mob_faction = "hosvoreb" + +/obj/random/mob/semirandom_mob_spawner/vore/hostile/c + mob_faction = "hosvorec" + +/obj/random/mob/semirandom_mob_spawner/sus/b + mob_faction = "susb" + +/obj/random/mob/semirandom_mob_spawner/sus/c + mob_faction = "susc" + +/obj/random/mob/semirandom_mob_spawner/sus/d + mob_faction = "susd" + +/obj/random/mob/semirandom_mob_spawner/sus/e + mob_faction = "suse" + +/obj/random/mob/semirandom_mob_spawner/sus/f + mob_faction = "susf" + +/obj/random/mob/semirandom_mob_spawner/mecha/b + mob_faction = "mechab" + +/obj/random/mob/semirandom_mob_spawner/mecha/c + mob_faction = "mechac" + +/obj/random/mob/semirandom_mob_spawner/mecha/d + mob_faction = "mechad" + +/obj/random/mob/semirandom_mob_spawner/mecha/e + mob_faction = "mechae" + +/obj/random/mob/semirandom_mob_spawner/mecha/f + mob_faction = "mechaf" + +/obj/random/mob/semirandom_mob_spawner/mecha/retaliate + mob_faction = "retmecha" + overwrite_hostility = 1 + mob_hostile = 0 + mob_retaliate = 1 + +/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/b + mob_faction = "retmechb" + +/obj/random/mob/semirandom_mob_spawner/mecha/retaliate/c + mob_faction = "retmechc" diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index c7fc6962ba1..b06ec311ad5 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -1,143 +1,143 @@ -/client/proc/spawn_tanktransferbomb() - set category = "Debug" - set desc = "Spawn a tank transfer valve bomb" - set name = "Instant TTV" - - if(!check_rights(R_SPAWN)) return - - var/obj/effect/spawner/newbomb/proto = /obj/effect/spawner/newbomb/radio/custom - - var/p = tgui_input_number(usr, "Enter phoron amount (mol):","Phoron", initial(proto.phoron_amt)) - if(p == null) return - - var/o = tgui_input_number(usr, "Enter oxygen amount (mol):","Oxygen", initial(proto.oxygen_amt)) - if(o == null) return - - var/c = tgui_input_number(usr, "Enter carbon dioxide amount (mol):","Carbon Dioxide", initial(proto.carbon_amt)) - if(c == null) return - - new /obj/effect/spawner/newbomb/radio/custom(get_turf(mob), p, o, c) - -/obj/effect/spawner/newbomb - name = "TTV bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - - var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. - var/phoron_amt = 12 - var/oxygen_amt = 18 - var/carbon_amt = 0 - -/obj/effect/spawner/newbomb/timer - name = "TTV bomb - timer" - assembly_type = /obj/item/device/assembly/timer - -/obj/effect/spawner/newbomb/timer/syndicate - name = "TTV bomb - merc" - //High yield bombs. Yes, it is possible to make these with toxins - phoron_amt = 18.5 - oxygen_amt = 28.5 - -/obj/effect/spawner/newbomb/proximity - name = "TTV bomb - proximity" - assembly_type = /obj/item/device/assembly/prox_sensor - -/obj/effect/spawner/newbomb/radio/custom/New(var/newloc, ph, ox, co) - if(ph != null) phoron_amt = ph - if(ox != null) oxygen_amt = ox - if(co != null) carbon_amt = co - ..() - -/obj/effect/spawner/newbomb/Initialize(newloc) - ..(newloc) - var/obj/item/device/transfer_valve/V = new(src.loc) - var/obj/item/weapon/tank/phoron/PT = new(V) - var/obj/item/weapon/tank/oxygen/OT = new(V) - - V.tank_one = PT - V.tank_two = OT - - PT.master = V - OT.master = V - - PT.valve_welded = 1 - PT.air_contents.gas["phoron"] = phoron_amt - PT.air_contents.gas["carbon_dioxide"] = carbon_amt - PT.air_contents.total_moles = phoron_amt + carbon_amt - PT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 - PT.air_contents.update_values() - - OT.valve_welded = 1 - OT.air_contents.gas["oxygen"] = oxygen_amt - OT.air_contents.total_moles = oxygen_amt - OT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 - OT.air_contents.update_values() - - var/obj/item/device/assembly/S = new assembly_type(V) - V.attached_device = S - - S.holder = V - S.toggle_secure() - - V.update_icon() - return INITIALIZE_HINT_QDEL - - -/////////////////////// -//One Tank Bombs, WOOOOOOO! -Luke -/////////////////////// - -/obj/effect/spawner/onetankbomb - name = "Single-tank bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - -// var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. - var/phoron_amt = 0 - var/oxygen_amt = 0 - -/obj/effect/spawner/onetankbomb/New(newloc) //just needs an assembly. - ..(newloc) - - var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb, /obj/item/weapon/tank/oxygen/onetankbomb) - new type(src.loc) - - qdel(src) - -/obj/effect/spawner/onetankbomb/full - name = "Single-tank bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - -// var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. -/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. - ..(newloc) - - var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) - new type(src.loc) - - qdel(src) - -/obj/effect/spawner/onetankbomb/frag - name = "Single-tank bomb" - icon = 'icons/mob/screen1.dmi' - icon_state = "x" - -// var/assembly_type = /obj/item/device/assembly/signaler - - //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. -/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. - ..(newloc) - - var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) - new type(src.loc) - - qdel(src) - - +/client/proc/spawn_tanktransferbomb() + set category = "Debug" + set desc = "Spawn a tank transfer valve bomb" + set name = "Instant TTV" + + if(!check_rights(R_SPAWN)) return + + var/obj/effect/spawner/newbomb/proto = /obj/effect/spawner/newbomb/radio/custom + + var/p = tgui_input_number(usr, "Enter phoron amount (mol):","Phoron", initial(proto.phoron_amt)) + if(p == null) return + + var/o = tgui_input_number(usr, "Enter oxygen amount (mol):","Oxygen", initial(proto.oxygen_amt)) + if(o == null) return + + var/c = tgui_input_number(usr, "Enter carbon dioxide amount (mol):","Carbon Dioxide", initial(proto.carbon_amt)) + if(c == null) return + + new /obj/effect/spawner/newbomb/radio/custom(get_turf(mob), p, o, c) + +/obj/effect/spawner/newbomb + name = "TTV bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + + var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. + var/phoron_amt = 12 + var/oxygen_amt = 18 + var/carbon_amt = 0 + +/obj/effect/spawner/newbomb/timer + name = "TTV bomb - timer" + assembly_type = /obj/item/device/assembly/timer + +/obj/effect/spawner/newbomb/timer/syndicate + name = "TTV bomb - merc" + //High yield bombs. Yes, it is possible to make these with toxins + phoron_amt = 18.5 + oxygen_amt = 28.5 + +/obj/effect/spawner/newbomb/proximity + name = "TTV bomb - proximity" + assembly_type = /obj/item/device/assembly/prox_sensor + +/obj/effect/spawner/newbomb/radio/custom/New(var/newloc, ph, ox, co) + if(ph != null) phoron_amt = ph + if(ox != null) oxygen_amt = ox + if(co != null) carbon_amt = co + ..() + +/obj/effect/spawner/newbomb/Initialize(newloc) + ..(newloc) + var/obj/item/device/transfer_valve/V = new(src.loc) + var/obj/item/weapon/tank/phoron/PT = new(V) + var/obj/item/weapon/tank/oxygen/OT = new(V) + + V.tank_one = PT + V.tank_two = OT + + PT.master = V + OT.master = V + + PT.valve_welded = 1 + PT.air_contents.gas["phoron"] = phoron_amt + PT.air_contents.gas["carbon_dioxide"] = carbon_amt + PT.air_contents.total_moles = phoron_amt + carbon_amt + PT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 + PT.air_contents.update_values() + + OT.valve_welded = 1 + OT.air_contents.gas["oxygen"] = oxygen_amt + OT.air_contents.total_moles = oxygen_amt + OT.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE+1 + OT.air_contents.update_values() + + var/obj/item/device/assembly/S = new assembly_type(V) + V.attached_device = S + + S.holder = V + S.toggle_secure() + + V.update_icon() + return INITIALIZE_HINT_QDEL + + +/////////////////////// +//One Tank Bombs, WOOOOOOO! -Luke +/////////////////////// + +/obj/effect/spawner/onetankbomb + name = "Single-tank bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + +// var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. + var/phoron_amt = 0 + var/oxygen_amt = 0 + +/obj/effect/spawner/onetankbomb/New(newloc) //just needs an assembly. + ..(newloc) + + var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb, /obj/item/weapon/tank/oxygen/onetankbomb) + new type(src.loc) + + qdel(src) + +/obj/effect/spawner/onetankbomb/full + name = "Single-tank bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + +// var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. +/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. + ..(newloc) + + var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) + new type(src.loc) + + qdel(src) + +/obj/effect/spawner/onetankbomb/frag + name = "Single-tank bomb" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + +// var/assembly_type = /obj/item/device/assembly/signaler + + //Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol. +/obj/effect/spawner/onetankbomb/full/New(newloc) //just needs an assembly. + ..(newloc) + + var/type = pick(/obj/item/weapon/tank/phoron/onetankbomb/full, /obj/item/weapon/tank/oxygen/onetankbomb/full) + new type(src.loc) + + qdel(src) + + diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index af84a15d427..0fcfe6d6a04 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -1,26 +1,26 @@ -/obj/effect/gibspawner/generic - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) - gibamounts = list(2,2,1) - -/obj/effect/gibspawner/generic/New() - gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list()) - ..() - -/obj/effect/gibspawner/human - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/down,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) - gibamounts = list(1,1,1,1,1,1,1) - -/obj/effect/gibspawner/human/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs, list()) - gibamounts[6] = pick(0,1,2) - ..() - -/obj/effect/gibspawner/robot - sparks = 1 - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/robot/up,/obj/effect/decal/cleanable/blood/gibs/robot/down,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot/limb) - gibamounts = list(1,1,1,1,1,1) - -/obj/effect/gibspawner/robot/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs) - gibamounts[6] = pick(0,1,2) +/obj/effect/gibspawner/generic + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) + gibamounts = list(2,2,1) + +/obj/effect/gibspawner/generic/New() + gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list()) + ..() + +/obj/effect/gibspawner/human + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/down,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) + gibamounts = list(1,1,1,1,1,1,1) + +/obj/effect/gibspawner/human/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs, list()) + gibamounts[6] = pick(0,1,2) + ..() + +/obj/effect/gibspawner/robot + sparks = 1 + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/robot/up,/obj/effect/decal/cleanable/blood/gibs/robot/down,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot/limb) + gibamounts = list(1,1,1,1,1,1) + +/obj/effect/gibspawner/robot/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs) + gibamounts[6] = pick(0,1,2) ..() \ No newline at end of file diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index c92bacbb794..776b2e69d7d 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -1,324 +1,324 @@ -//generic procs copied from obj/effect/alien -/obj/effect/spider - name = "web" - desc = "it's stringy and sticky" - icon = 'icons/effects/effects.dmi' - anchored = TRUE - density = FALSE - var/health = 15 - -//similar to weeds, but only barfed out by nurses manually -/obj/effect/spider/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - if(2.0) - if (prob(50)) - qdel(src) - if(3.0) - if (prob(5)) - qdel(src) - return - -/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user) - user.setClickCooldown(user.get_attack_speed(W)) - - if(LAZYLEN(W.attack_verb)) - visible_message("\The [src] has been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]") - else - visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") - - var/damage = W.force / 4.0 - - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - - if(WT.remove_fuel(0, user)) - damage = 15 - playsound(src, W.usesound, 100, 1) - - health -= damage - healthcheck() - -/obj/effect/spider/spiderling/attack_hand(mob/living/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - user.do_attack_animation(src) - if(prob(20)) - visible_message("\The [user] tries to stomp on \the [src], but misses!") - var/list/nearby = oview(2, src) - if(length(nearby)) - walk_to(src, pick(nearby), 2) - return - visible_message("\The [user] stomps \the [src] dead!") - die() - -/obj/effect/spider/bullet_act(var/obj/item/projectile/Proj) - ..() - health -= Proj.get_structure_damage() - healthcheck() - -/obj/effect/spider/proc/die() - qdel(src) - -/obj/effect/spider/proc/healthcheck() - if(health <= 0) - die() - -/obj/effect/spider/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > 300 + T0C) - health -= 5 - healthcheck() - -/obj/effect/spider/stickyweb - icon_state = "stickyweb1" - -/obj/effect/spider/stickyweb/Initialize() - if(prob(50)) - icon_state = "stickyweb2" - return ..() - -/obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /mob/living/simple_mob/animal/giant_spider)) - return TRUE - else if(istype(mover, /mob/living)) - if(prob(50)) - to_chat(mover, span("warning", "You get stuck in \the [src] for a moment.")) - return FALSE - else if(istype(mover, /obj/item/projectile)) - return prob(30) - return TRUE - -/obj/effect/spider/eggcluster - name = "egg cluster" - desc = "They seem to pulse slightly with an inner life" - icon_state = "eggs" - var/amount_grown = 0 - var/spiders_min = 6 - var/spiders_max = 24 - var/spider_type = /obj/effect/spider/spiderling - var/faction = "spiders" - -/obj/effect/spider/eggcluster/Initialize() - pixel_x = rand(3,-3) - pixel_y = rand(3,-3) - START_PROCESSING(SSobj, src) - return ..() - -/obj/effect/spider/eggcluster/New(var/location, var/atom/parent) - get_light_and_color(parent) - ..() - -/obj/effect/spider/eggcluster/Destroy() - STOP_PROCESSING(SSobj, src) - if(istype(loc, /obj/item/organ/external)) - var/obj/item/organ/external/O = loc - O.implants -= src - - return ..() - -/obj/effect/spider/eggcluster/process() - amount_grown += rand(0,2) - if(amount_grown >= 100) - var/num = rand(spiders_min, spiders_max) - var/obj/item/organ/external/O = null - if(istype(loc, /obj/item/organ/external)) - O = loc - - for(var/i=0, i[src] dies!") - new /obj/effect/decal/cleanable/spiderling_remains(src.loc) - ..() - -/obj/effect/spider/spiderling/healthcheck() - if(health <= 0) - die() - -/obj/effect/spider/spiderling/process() - healthcheck() - if(travelling_in_vent) - if(istype(src.loc, /turf)) - travelling_in_vent = 0 - entry_vent = null - else if(entry_vent) - if(get_dist(src, entry_vent) <= 1) - //VOREStation Edit Start - var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = get_safe_ventcrawl_target(entry_vent) - if(!exit_vent) - return - spawn(rand(20,60)) - loc = exit_vent - var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) - spawn(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - - if(prob(50)) - src.visible_message("You hear something squeezing through the ventilation ducts.",2) - sleep(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - loc = exit_vent.loc - entry_vent = null - var/area/new_area = get_area(loc) - if(new_area) - new_area.Entered(src) - //VOREStation Edit End - //================= - - if(isturf(loc)) - skitter() - - else if(isorgan(loc)) - if(amount_grown < 0) amount_grown = 1 - var/obj/item/organ/external/O = loc - if(!O.owner || O.owner.stat == DEAD || amount_grown > 80) - O.implants -= src - src.loc = O.owner ? O.owner.loc : O.loc - src.visible_message("\A [src] makes its way out of [O.owner ? "[O.owner]'s [O.name]" : "\the [O]"]!") - if(O.owner) - O.owner.apply_damage(1, BRUTE, O.organ_tag) - else if(prob(1)) - O.owner.apply_damage(1, TOX, O.organ_tag) - if(world.time > last_itch + 30 SECONDS) - last_itch = world.time - to_chat(O.owner, "Your [O.name] itches...") - else if(prob(1)) - src.visible_message("\The [src] skitters.") - - if(amount_grown >= 0) - amount_grown += rand(0,2) - -/obj/effect/spider/spiderling/proc/skitter() - if(isturf(loc)) - if(prob(25)) - var/list/nearby = trange(5, src) - loc - if(nearby.len) - var/target_atom = pick(nearby) - walk_to(src, target_atom, 5) - if(prob(25)) - src.visible_message("\The [src] skitters[pick(" away"," around","")].") - else if(amount_grown < 75 && prob(5)) - //vent crawl! - for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) - if(!v.welded) - entry_vent = v - walk_to(src, entry_vent, 5) - break - if(amount_grown >= 100) - var/spawn_type = pick(grow_as) - var/mob/living/simple_mob/animal/giant_spider/GS = new spawn_type(src.loc, src) - GS.faction = faction - if(stunted) - spawn(2) - GS.make_spiderling() - qdel(src) - -/obj/effect/spider/spiderling/stunted - stunted = TRUE - - grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/hunter) - -/obj/effect/spider/spiderling/non_growing - amount_grown = -1 - -/obj/effect/spider/spiderling/princess - name = "royal spiderling" - desc = "There's a special aura about this one." - grow_as = list(/mob/living/simple_mob/animal/giant_spider/nurse/queen) - -/obj/effect/spider/spiderling/princess/New(var/location, var/atom/parent) - ..() - amount_grown = 50 - -/obj/effect/decal/cleanable/spiderling_remains - name = "spiderling remains" - desc = "Green squishy mess." - icon = 'icons/effects/effects.dmi' - icon_state = "greenshatter" - -/obj/effect/spider/cocoon - name = "cocoon" - desc = "Something wrapped in silky spider web" - icon_state = "cocoon1" - health = 60 - -/obj/effect/spider/cocoon/New() - icon_state = pick("cocoon1","cocoon2","cocoon3") - -/obj/effect/spider/cocoon/Destroy() - src.visible_message("\The [src] splits open.") - for(var/atom/movable/A in contents) - A.loc = src.loc - return ..() +//generic procs copied from obj/effect/alien +/obj/effect/spider + name = "web" + desc = "it's stringy and sticky" + icon = 'icons/effects/effects.dmi' + anchored = TRUE + density = FALSE + var/health = 15 + +//similar to weeds, but only barfed out by nurses manually +/obj/effect/spider/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + if(2.0) + if (prob(50)) + qdel(src) + if(3.0) + if (prob(5)) + qdel(src) + return + +/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user) + user.setClickCooldown(user.get_attack_speed(W)) + + if(LAZYLEN(W.attack_verb)) + visible_message("\The [src] has been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]") + else + visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") + + var/damage = W.force / 4.0 + + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + + if(WT.remove_fuel(0, user)) + damage = 15 + playsound(src, W.usesound, 100, 1) + + health -= damage + healthcheck() + +/obj/effect/spider/spiderling/attack_hand(mob/living/user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src) + if(prob(20)) + visible_message("\The [user] tries to stomp on \the [src], but misses!") + var/list/nearby = oview(2, src) + if(length(nearby)) + walk_to(src, pick(nearby), 2) + return + visible_message("\The [user] stomps \the [src] dead!") + die() + +/obj/effect/spider/bullet_act(var/obj/item/projectile/Proj) + ..() + health -= Proj.get_structure_damage() + healthcheck() + +/obj/effect/spider/proc/die() + qdel(src) + +/obj/effect/spider/proc/healthcheck() + if(health <= 0) + die() + +/obj/effect/spider/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > 300 + T0C) + health -= 5 + healthcheck() + +/obj/effect/spider/stickyweb + icon_state = "stickyweb1" + +/obj/effect/spider/stickyweb/Initialize() + if(prob(50)) + icon_state = "stickyweb2" + return ..() + +/obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /mob/living/simple_mob/animal/giant_spider)) + return TRUE + else if(istype(mover, /mob/living)) + if(prob(50)) + to_chat(mover, span("warning", "You get stuck in \the [src] for a moment.")) + return FALSE + else if(istype(mover, /obj/item/projectile)) + return prob(30) + return TRUE + +/obj/effect/spider/eggcluster + name = "egg cluster" + desc = "They seem to pulse slightly with an inner life" + icon_state = "eggs" + var/amount_grown = 0 + var/spiders_min = 6 + var/spiders_max = 24 + var/spider_type = /obj/effect/spider/spiderling + var/faction = "spiders" + +/obj/effect/spider/eggcluster/Initialize() + pixel_x = rand(3,-3) + pixel_y = rand(3,-3) + START_PROCESSING(SSobj, src) + return ..() + +/obj/effect/spider/eggcluster/New(var/location, var/atom/parent) + get_light_and_color(parent) + ..() + +/obj/effect/spider/eggcluster/Destroy() + STOP_PROCESSING(SSobj, src) + if(istype(loc, /obj/item/organ/external)) + var/obj/item/organ/external/O = loc + O.implants -= src + + return ..() + +/obj/effect/spider/eggcluster/process() + amount_grown += rand(0,2) + if(amount_grown >= 100) + var/num = rand(spiders_min, spiders_max) + var/obj/item/organ/external/O = null + if(istype(loc, /obj/item/organ/external)) + O = loc + + for(var/i=0, i[src] dies!") + new /obj/effect/decal/cleanable/spiderling_remains(src.loc) + ..() + +/obj/effect/spider/spiderling/healthcheck() + if(health <= 0) + die() + +/obj/effect/spider/spiderling/process() + healthcheck() + if(travelling_in_vent) + if(istype(src.loc, /turf)) + travelling_in_vent = 0 + entry_vent = null + else if(entry_vent) + if(get_dist(src, entry_vent) <= 1) + //VOREStation Edit Start + var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = get_safe_ventcrawl_target(entry_vent) + if(!exit_vent) + return + spawn(rand(20,60)) + loc = exit_vent + var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) + spawn(travel_time) + + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + + if(prob(50)) + src.visible_message("You hear something squeezing through the ventilation ducts.",2) + sleep(travel_time) + + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + loc = exit_vent.loc + entry_vent = null + var/area/new_area = get_area(loc) + if(new_area) + new_area.Entered(src) + //VOREStation Edit End + //================= + + if(isturf(loc)) + skitter() + + else if(isorgan(loc)) + if(amount_grown < 0) amount_grown = 1 + var/obj/item/organ/external/O = loc + if(!O.owner || O.owner.stat == DEAD || amount_grown > 80) + O.implants -= src + src.loc = O.owner ? O.owner.loc : O.loc + src.visible_message("\A [src] makes its way out of [O.owner ? "[O.owner]'s [O.name]" : "\the [O]"]!") + if(O.owner) + O.owner.apply_damage(1, BRUTE, O.organ_tag) + else if(prob(1)) + O.owner.apply_damage(1, TOX, O.organ_tag) + if(world.time > last_itch + 30 SECONDS) + last_itch = world.time + to_chat(O.owner, "Your [O.name] itches...") + else if(prob(1)) + src.visible_message("\The [src] skitters.") + + if(amount_grown >= 0) + amount_grown += rand(0,2) + +/obj/effect/spider/spiderling/proc/skitter() + if(isturf(loc)) + if(prob(25)) + var/list/nearby = trange(5, src) - loc + if(nearby.len) + var/target_atom = pick(nearby) + walk_to(src, target_atom, 5) + if(prob(25)) + src.visible_message("\The [src] skitters[pick(" away"," around","")].") + else if(amount_grown < 75 && prob(5)) + //vent crawl! + for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) + if(!v.welded) + entry_vent = v + walk_to(src, entry_vent, 5) + break + if(amount_grown >= 100) + var/spawn_type = pick(grow_as) + var/mob/living/simple_mob/animal/giant_spider/GS = new spawn_type(src.loc, src) + GS.faction = faction + if(stunted) + spawn(2) + GS.make_spiderling() + qdel(src) + +/obj/effect/spider/spiderling/stunted + stunted = TRUE + + grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/hunter) + +/obj/effect/spider/spiderling/non_growing + amount_grown = -1 + +/obj/effect/spider/spiderling/princess + name = "royal spiderling" + desc = "There's a special aura about this one." + grow_as = list(/mob/living/simple_mob/animal/giant_spider/nurse/queen) + +/obj/effect/spider/spiderling/princess/New(var/location, var/atom/parent) + ..() + amount_grown = 50 + +/obj/effect/decal/cleanable/spiderling_remains + name = "spiderling remains" + desc = "Green squishy mess." + icon = 'icons/effects/effects.dmi' + icon_state = "greenshatter" + +/obj/effect/spider/cocoon + name = "cocoon" + desc = "Something wrapped in silky spider web" + icon_state = "cocoon1" + health = 60 + +/obj/effect/spider/cocoon/New() + icon_state = pick("cocoon1","cocoon2","cocoon3") + +/obj/effect/spider/cocoon/Destroy() + src.visible_message("\The [src] splits open.") + for(var/atom/movable/A in contents) + A.loc = src.loc + return ..() diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index 15750828ddb..2c689d9cdd9 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -117,7 +117,7 @@ L.stop_pulling() P.forceMove(T) L.forceMove(T) - L.start_pulling(P) + L.continue_pulling(P) else L.forceMove(T) else @@ -285,4 +285,3 @@ var/global/list/tele_landmarks = list() // Terrible, but the alternative is loop /obj/effect/step_trigger/warning/train_edge warningmessage = "The wind billowing alongside the train is extremely strong here! Any movement could easily pull you down beneath the carriages, return to the train immediately!" - diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm index fcec6803e4c..949eb71eafa 100644 --- a/code/game/objects/empulse.dm +++ b/code/game/objects/empulse.dm @@ -1,74 +1,74 @@ -// Uncomment this define to check for possible lengthy processing of emp_act()s. -// If emp_act() takes more than defined deciseconds (1/10 seconds) an admin message and log is created. -// I do not recommend having this uncommented on main server, it probably causes a bit more lag, espicially with larger EMPs. - -// #define EMPDEBUG 10 - -/proc/empulse(turf/epicenter, first_range, second_range, third_range, fourth_range, log=0) - if(!epicenter) return - - if(!istype(epicenter, /turf)) - epicenter = get_turf(epicenter.loc) - - if(log) - message_admins("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") - log_game("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") - - if(first_range > 1) - var/obj/effect/overlay/pulse = new /obj/effect/overlay(epicenter) - pulse.icon = 'icons/effects/effects.dmi' - pulse.icon_state = "emppulse" - pulse.name = "emp pulse" - pulse.anchored = TRUE - spawn(20) - qdel(pulse) - - if(first_range > second_range) - second_range = first_range - if(second_range > third_range) - third_range = second_range - if(third_range > fourth_range) - fourth_range = third_range - - for(var/mob/M in range(first_range, epicenter)) - M << 'sound/effects/EMPulse.ogg' - - for(var/atom/T in range(fourth_range, epicenter)) - #ifdef EMPDEBUG - var/time = world.timeofday - #endif - var/distance = get_dist(epicenter, T) - if(distance < 0) - distance = 0 - //Worst effects, really hurts - if(distance < first_range) - T.emp_act(1) - else if(distance == first_range) - if(prob(50)) - T.emp_act(1) - else - T.emp_act(2) - //Slightly less painful - else if(distance <= second_range) - T.emp_act(2) - else if(distance == second_range) - if(prob(50)) - T.emp_act(2) - else - T.emp_act(3) - //Even less slightly less painful - else if(distance <= third_range) - T.emp_act(3) - else if(distance == third_range) - if(prob(50)) - T.emp_act(2) - else - T.emp_act(3) - //This should be more or less harmless - else if(distance <= fourth_range) - T.emp_act(4) - #ifdef EMPDEBUG - if((world.timeofday - time) >= EMPDEBUG) - log_and_message_admins("EMPDEBUG: [T.name] - [T.type] - took [world.timeofday - time]ds to process emp_act()!") - #endif +// Uncomment this define to check for possible lengthy processing of emp_act()s. +// If emp_act() takes more than defined deciseconds (1/10 seconds) an admin message and log is created. +// I do not recommend having this uncommented on main server, it probably causes a bit more lag, espicially with larger EMPs. + +// #define EMPDEBUG 10 + +/proc/empulse(turf/epicenter, first_range, second_range, third_range, fourth_range, log=0) + if(!epicenter) return + + if(!istype(epicenter, /turf)) + epicenter = get_turf(epicenter.loc) + + if(log) + message_admins("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") + log_game("EMP with size ([first_range], [second_range], [third_range], [fourth_range]) in area [epicenter.loc.name] ") + + if(first_range > 1) + var/obj/effect/overlay/pulse = new /obj/effect/overlay(epicenter) + pulse.icon = 'icons/effects/effects.dmi' + pulse.icon_state = "emppulse" + pulse.name = "emp pulse" + pulse.anchored = TRUE + spawn(20) + qdel(pulse) + + if(first_range > second_range) + second_range = first_range + if(second_range > third_range) + third_range = second_range + if(third_range > fourth_range) + fourth_range = third_range + + for(var/mob/M in range(first_range, epicenter)) + M << 'sound/effects/EMPulse.ogg' + + for(var/atom/T in range(fourth_range, epicenter)) + #ifdef EMPDEBUG + var/time = world.timeofday + #endif + var/distance = get_dist(epicenter, T) + if(distance < 0) + distance = 0 + //Worst effects, really hurts + if(distance < first_range) + T.emp_act(1) + else if(distance == first_range) + if(prob(50)) + T.emp_act(1) + else + T.emp_act(2) + //Slightly less painful + else if(distance <= second_range) + T.emp_act(2) + else if(distance == second_range) + if(prob(50)) + T.emp_act(2) + else + T.emp_act(3) + //Even less slightly less painful + else if(distance <= third_range) + T.emp_act(3) + else if(distance == third_range) + if(prob(50)) + T.emp_act(2) + else + T.emp_act(3) + //This should be more or less harmless + else if(distance <= fourth_range) + T.emp_act(4) + #ifdef EMPDEBUG + if((world.timeofday - time) >= EMPDEBUG) + log_and_message_admins("EMPDEBUG: [T.name] - [T.type] - took [world.timeofday - time]ds to process emp_act()!") + #endif return 1 \ No newline at end of file diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index 7177e52cbe6..f54adf376f7 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -1,112 +1,112 @@ -//TODO: Flash range does nothing currently - -/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, shaped) - var/multi_z_scalar = config.multi_z_explosion_scalar - spawn(0) - var/start = world.timeofday - epicenter = get_turf(epicenter) - if(!epicenter) return - - // Handles recursive propagation of explosions. - if(z_transfer && multi_z_scalar) - var/adj_dev = max(0, (multi_z_scalar * devastation_range) - (shaped ? 2 : 0) ) - var/adj_heavy = max(0, (multi_z_scalar * heavy_impact_range) - (shaped ? 2 : 0) ) - var/adj_light = max(0, (multi_z_scalar * light_impact_range) - (shaped ? 2 : 0) ) - var/adj_flash = max(0, (multi_z_scalar * flash_range) - (shaped ? 2 : 0) ) - - - if(adj_dev > 0 || adj_heavy > 0) - if(HasAbove(epicenter.z) && z_transfer & UP) - explosion(GetAbove(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, UP, shaped) - if(HasBelow(epicenter.z) && z_transfer & DOWN) - explosion(GetBelow(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, DOWN, shaped) - - var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flash_range) - - // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. - // Stereo users will also hear the direction of the explosion! - // Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions. - // 3/7/14 will calculate to 80 + 35 - var/far_dist = 0 - far_dist += heavy_impact_range * 5 - far_dist += devastation_range * 20 - var/frequency = get_rand_frequency() - for(var/mob/M in player_list) - if(M.z == epicenter.z) - var/turf/M_turf = get_turf(M) - var/dist = get_dist(M_turf, epicenter) - // If inside the blast radius + world.view - 2 - if(dist <= round(max_range + world.view - 2, 1)) - M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound - else if(dist <= far_dist) - var/far_volume = CLAMP(far_dist, 30, 50) // Volume is based on explosion size and dist - far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion - M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5) - - var/close = range(world.view+round(devastation_range,1), epicenter) - // to all distanced mobs play a different sound - for(var/mob/M in player_list) - if(M.z == epicenter.z) - if(!(M in close)) - // check if the mob can hear - if(M.ear_deaf <= 0 || !M.ear_deaf) - if(!istype(M.loc,/turf/space)) - M << 'sound/effects/explosionfar.ogg' - - if(adminlog) - message_admins("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)") - log_game("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ") - - var/approximate_intensity = (devastation_range * 3) + (heavy_impact_range * 2) + light_impact_range - var/powernet_rebuild_was_deferred_already = defer_powernet_rebuild - // Large enough explosion. For performance reasons, powernets will be rebuilt manually - if(!defer_powernet_rebuild && (approximate_intensity > 25)) - defer_powernet_rebuild = 1 - - if(heavy_impact_range > 1) - var/datum/effect/system/explosion/E = new/datum/effect/system/explosion() - E.set_up(epicenter) - E.start() - - var/x0 = epicenter.x - var/y0 = epicenter.y - var/z0 = epicenter.z - if(config.use_recursive_explosions) - var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power. - explosion_rec(epicenter, power, shaped) - else - for(var/turf/T in trange(max_range, epicenter)) - var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) - - if(dist < devastation_range) dist = 1 - else if(dist < heavy_impact_range) dist = 2 - else if(dist < light_impact_range) dist = 3 - else continue - - if(!T) - T = locate(x0,y0,z0) - for(var/atom_movable in T.contents) //bypass type checking since only atom/movable can be contained by turfs anyway - var/atom/movable/AM = atom_movable - if(AM && AM.simulated) AM.ex_act(dist) - - T.ex_act(dist) - - var/took = (world.timeofday-start)/10 - //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare - if(Debug2) to_world_log("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") - - //Machines which report explosions. - for(var/i,i<=doppler_arrays.len,i++) - var/obj/machinery/doppler_array/Array = doppler_arrays[i] - if(Array) - Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took) - sleep(8) - - if(!powernet_rebuild_was_deferred_already && defer_powernet_rebuild) - SSmachines.makepowernets() - defer_powernet_rebuild = 0 - return 1 - -/proc/secondaryexplosion(turf/epicenter, range) - for(var/turf/tile in range(range, epicenter)) - tile.ex_act(2) +//TODO: Flash range does nothing currently + +/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, shaped) + var/multi_z_scalar = config.multi_z_explosion_scalar + spawn(0) + var/start = world.timeofday + epicenter = get_turf(epicenter) + if(!epicenter) return + + // Handles recursive propagation of explosions. + if(z_transfer && multi_z_scalar) + var/adj_dev = max(0, (multi_z_scalar * devastation_range) - (shaped ? 2 : 0) ) + var/adj_heavy = max(0, (multi_z_scalar * heavy_impact_range) - (shaped ? 2 : 0) ) + var/adj_light = max(0, (multi_z_scalar * light_impact_range) - (shaped ? 2 : 0) ) + var/adj_flash = max(0, (multi_z_scalar * flash_range) - (shaped ? 2 : 0) ) + + + if(adj_dev > 0 || adj_heavy > 0) + if(HasAbove(epicenter.z) && z_transfer & UP) + explosion(GetAbove(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, UP, shaped) + if(HasBelow(epicenter.z) && z_transfer & DOWN) + explosion(GetBelow(epicenter), round(adj_dev), round(adj_heavy), round(adj_light), round(adj_flash), 0, DOWN, shaped) + + var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flash_range) + + // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. + // Stereo users will also hear the direction of the explosion! + // Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions. + // 3/7/14 will calculate to 80 + 35 + var/far_dist = 0 + far_dist += heavy_impact_range * 5 + far_dist += devastation_range * 20 + var/frequency = get_rand_frequency() + for(var/mob/M in player_list) + if(M.z == epicenter.z) + var/turf/M_turf = get_turf(M) + var/dist = get_dist(M_turf, epicenter) + // If inside the blast radius + world.view - 2 + if(dist <= round(max_range + world.view - 2, 1)) + M.playsound_local(epicenter, get_sfx("explosion"), 100, 1, frequency, falloff = 5) // get_sfx() is so that everyone gets the same sound + else if(dist <= far_dist) + var/far_volume = CLAMP(far_dist, 30, 50) // Volume is based on explosion size and dist + far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion + M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5) + + var/close = range(world.view+round(devastation_range,1), epicenter) + // to all distanced mobs play a different sound + for(var/mob/M in player_list) + if(M.z == epicenter.z) + if(!(M in close)) + // check if the mob can hear + if(M.ear_deaf <= 0 || !M.ear_deaf) + if(!istype(M.loc,/turf/space)) + M << 'sound/effects/explosionfar.ogg' + + if(adminlog) + message_admins("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z]) (JMP)") + log_game("Explosion with [shaped ? "shaped" : "non-shaped"] size ([devastation_range], [heavy_impact_range], [light_impact_range]) in area [epicenter.loc.name] ") + + var/approximate_intensity = (devastation_range * 3) + (heavy_impact_range * 2) + light_impact_range + var/powernet_rebuild_was_deferred_already = defer_powernet_rebuild + // Large enough explosion. For performance reasons, powernets will be rebuilt manually + if(!defer_powernet_rebuild && (approximate_intensity > 25)) + defer_powernet_rebuild = 1 + + if(heavy_impact_range > 1) + var/datum/effect/system/explosion/E = new/datum/effect/system/explosion() + E.set_up(epicenter) + E.start() + + var/x0 = epicenter.x + var/y0 = epicenter.y + var/z0 = epicenter.z + if(config.use_recursive_explosions) + var/power = devastation_range * 2 + heavy_impact_range + light_impact_range //The ranges add up, ie light 14 includes both heavy 7 and devestation 3. So this calculation means devestation counts for 4, heavy for 2 and light for 1 power, giving us a cap of 27 power. + explosion_rec(epicenter, power, shaped) + else + for(var/turf/T in trange(max_range, epicenter)) + var/dist = sqrt((T.x - x0)**2 + (T.y - y0)**2) + + if(dist < devastation_range) dist = 1 + else if(dist < heavy_impact_range) dist = 2 + else if(dist < light_impact_range) dist = 3 + else continue + + if(!T) + T = locate(x0,y0,z0) + for(var/atom_movable in T.contents) //bypass type checking since only atom/movable can be contained by turfs anyway + var/atom/movable/AM = atom_movable + if(AM && AM.simulated) AM.ex_act(dist) + + T.ex_act(dist) + + var/took = (world.timeofday-start)/10 + //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare + if(Debug2) to_world_log("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") + + //Machines which report explosions. + for(var/i,i<=doppler_arrays.len,i++) + var/obj/machinery/doppler_array/Array = doppler_arrays[i] + if(Array) + Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took) + sleep(8) + + if(!powernet_rebuild_was_deferred_already && defer_powernet_rebuild) + SSmachines.makepowernets() + defer_powernet_rebuild = 0 + return 1 + +/proc/secondaryexplosion(turf/epicenter, range) + for(var/turf/tile in range(range, epicenter)) + tile.ex_act(2) diff --git a/code/game/objects/explosion_recursive.dm b/code/game/objects/explosion_recursive.dm index d60dc90f3e4..aebf6ba8e80 100644 --- a/code/game/objects/explosion_recursive.dm +++ b/code/game/objects/explosion_recursive.dm @@ -1,114 +1,114 @@ -/client/proc/kaboom() - var/power = tgui_input_number(src, "power?", "power?") - var/turf/T = get_turf(src.mob) - explosion_rec(T, power) - -/obj - var/explosion_resistance - -/proc/explosion_rec(turf/epicenter, power) - var/list/explosion_turfs = list() - var/explosion_in_progress = 0 - - var/loopbreak = 0 - while(explosion_in_progress) - if(loopbreak >= 15) return - spawn(10) - loopbreak++ - - if(power <= 0) return - epicenter = get_turf(epicenter) - if(!epicenter) return - - message_admins("Explosion with size ([power]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])") - log_game("Explosion with size ([power]) in area [epicenter.loc.name] ") - - playsound(epicenter, 'sound/effects/explosionfar.ogg', 100, 1, round(power*2,1) ) - playsound(epicenter, "explosion", 100, 1, round(power,1) ) - - explosion_in_progress = 1 - explosion_turfs = list() - - explosion_turfs[epicenter] = power - - //This steap handles the gathering of turfs which will be ex_act() -ed in the next step. It also ensures each turf gets the maximum possible amount of power dealt to it. - for(var/direction in cardinal) - var/turf/T = get_step(epicenter, direction) - T.explosion_spread(power - epicenter.explosion_resistance, direction, explosion_turfs) - - //This step applies the ex_act effects for the explosion, as planned in the previous step. - for(var/turf/T in explosion_turfs) - if(explosion_turfs[T] <= 0) continue - if(!T) continue - - //Wow severity looks confusing to calculate... Fret not, I didn't leave you with any additional instructions or help. (just kidding, see the line under the calculation) - var/severity = 4 - round(max(min( 3, ((explosion_turfs[T] - T.explosion_resistance) / (max(3,(power/3)))) ) ,1), 1) //sanity effective power on tile divided by either 3 or one third the total explosion power - // One third because there are three power levels and I - // want each one to take up a third of the crater - var/x = T.x - var/y = T.y - var/z = T.z - T.ex_act(severity) - if(!T) - T = locate(x,y,z) - for(var/atom_movable in T.contents) - var/atom/movable/AM = atom_movable - if(AM && AM.simulated) - AM.ex_act(severity) - - explosion_in_progress = 0 - -/turf - var/explosion_resistance - -/turf/space - explosion_resistance = 3 - -/turf/simulated/open - explosion_resistance = 3 - -/turf/simulated/floor - explosion_resistance = 1 - -/turf/simulated/mineral - explosion_resistance = 2 - -/turf/simulated/shuttle/floor - explosion_resistance = 1 - -/turf/simulated/shuttle/floor4 - explosion_resistance = 1 - -/turf/simulated/shuttle/plating - explosion_resistance = 1 - -/turf/simulated/shuttle/wall - explosion_resistance = 10 - -/turf/simulated/wall - explosion_resistance = 10 - -//Code-wise, a safe value for power is something up to ~25 or ~30.. This does quite a bit of damage to the station. -//direction is the direction that the spread took to come to this tile. So it is pointing in the main blast direction - meaning where this tile should spread most of it's force. -/turf/proc/explosion_spread(power, direction, var/list/explosion_turfs) - if(power <= 0) - return - if(src in explosion_turfs) - if(explosion_turfs[src] >= power) - return //The turf already sustained and spread a power greated than what we are dealing with. No point spreading again. - explosion_turfs[src] = power - - var/spread_power = power - src.explosion_resistance //This is the amount of power that will be spread to the tile in the direction of the blast - for(var/obj/O in src) - if(O.explosion_resistance) - spread_power -= O.explosion_resistance - - var/turf/T = get_step(src, direction) - T.explosion_spread(spread_power, direction, explosion_turfs) - T = get_step(src, turn(direction,90)) - T.explosion_spread(spread_power, turn(direction,90), explosion_turfs) - T = get_step(src, turn(direction,-90)) - T.explosion_spread(spread_power, turn(direction,-90), explosion_turfs) - -/turf/unsimulated/explosion_spread(power) - return //So it doesn't get to the parent proc, which simulates explosions +/client/proc/kaboom() + var/power = tgui_input_number(src, "power?", "power?") + var/turf/T = get_turf(src.mob) + explosion_rec(T, power) + +/obj + var/explosion_resistance + +/proc/explosion_rec(turf/epicenter, power) + var/list/explosion_turfs = list() + var/explosion_in_progress = 0 + + var/loopbreak = 0 + while(explosion_in_progress) + if(loopbreak >= 15) return + spawn(10) + loopbreak++ + + if(power <= 0) return + epicenter = get_turf(epicenter) + if(!epicenter) return + + message_admins("Explosion with size ([power]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])") + log_game("Explosion with size ([power]) in area [epicenter.loc.name] ") + + playsound(epicenter, 'sound/effects/explosionfar.ogg', 100, 1, round(power*2,1) ) + playsound(epicenter, "explosion", 100, 1, round(power,1) ) + + explosion_in_progress = 1 + explosion_turfs = list() + + explosion_turfs[epicenter] = power + + //This steap handles the gathering of turfs which will be ex_act() -ed in the next step. It also ensures each turf gets the maximum possible amount of power dealt to it. + for(var/direction in cardinal) + var/turf/T = get_step(epicenter, direction) + T.explosion_spread(power - epicenter.explosion_resistance, direction, explosion_turfs) + + //This step applies the ex_act effects for the explosion, as planned in the previous step. + for(var/turf/T in explosion_turfs) + if(explosion_turfs[T] <= 0) continue + if(!T) continue + + //Wow severity looks confusing to calculate... Fret not, I didn't leave you with any additional instructions or help. (just kidding, see the line under the calculation) + var/severity = 4 - round(max(min( 3, ((explosion_turfs[T] - T.explosion_resistance) / (max(3,(power/3)))) ) ,1), 1) //sanity effective power on tile divided by either 3 or one third the total explosion power + // One third because there are three power levels and I + // want each one to take up a third of the crater + var/x = T.x + var/y = T.y + var/z = T.z + T.ex_act(severity) + if(!T) + T = locate(x,y,z) + for(var/atom_movable in T.contents) + var/atom/movable/AM = atom_movable + if(AM && AM.simulated) + AM.ex_act(severity) + + explosion_in_progress = 0 + +/turf + var/explosion_resistance + +/turf/space + explosion_resistance = 3 + +/turf/simulated/open + explosion_resistance = 3 + +/turf/simulated/floor + explosion_resistance = 1 + +/turf/simulated/mineral + explosion_resistance = 2 + +/turf/simulated/shuttle/floor + explosion_resistance = 1 + +/turf/simulated/shuttle/floor4 + explosion_resistance = 1 + +/turf/simulated/shuttle/plating + explosion_resistance = 1 + +/turf/simulated/shuttle/wall + explosion_resistance = 10 + +/turf/simulated/wall + explosion_resistance = 10 + +//Code-wise, a safe value for power is something up to ~25 or ~30.. This does quite a bit of damage to the station. +//direction is the direction that the spread took to come to this tile. So it is pointing in the main blast direction - meaning where this tile should spread most of it's force. +/turf/proc/explosion_spread(power, direction, var/list/explosion_turfs) + if(power <= 0) + return + if(src in explosion_turfs) + if(explosion_turfs[src] >= power) + return //The turf already sustained and spread a power greated than what we are dealing with. No point spreading again. + explosion_turfs[src] = power + + var/spread_power = power - src.explosion_resistance //This is the amount of power that will be spread to the tile in the direction of the blast + for(var/obj/O in src) + if(O.explosion_resistance) + spread_power -= O.explosion_resistance + + var/turf/T = get_step(src, direction) + T.explosion_spread(spread_power, direction, explosion_turfs) + T = get_step(src, turn(direction,90)) + T.explosion_spread(spread_power, turn(direction,90), explosion_turfs) + T = get_step(src, turn(direction,-90)) + T.explosion_spread(spread_power, turn(direction,-90), explosion_turfs) + +/turf/unsimulated/explosion_spread(power) + return //So it doesn't get to the parent proc, which simulates explosions diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index fa4dc544526..609822a7562 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -226,6 +226,7 @@ /obj/item/attack_hand(mob/living/user as mob) if (!user) return + ..() if(anchored) to_chat(user, span("notice", "\The [src] won't budge, you can't pick it up!")) return @@ -1018,3 +1019,6 @@ Note: This proc can be overwritten to allow for different types of auto-alignmen /obj/item/proc/on_holder_escape(var/obj/item/weapon/holder/H) return + +/obj/item/proc/get_welder() + return diff --git a/code/game/objects/items/apc_frame.dm b/code/game/objects/items/apc_frame.dm index ca3f7241c3a..c84e0119663 100644 --- a/code/game/objects/items/apc_frame.dm +++ b/code/game/objects/items/apc_frame.dm @@ -1,38 +1,38 @@ -// APC HULL - -/obj/item/frame/apc - name = "\improper APC frame" - desc = "Used for repairing or building APCs" - icon = 'icons/obj/apc_repair.dmi' - icon_state = "apc_frame" - refund_amt = 2 - build_wall_only = TRUE - matter = list(MAT_STEEL = 100, MAT_GLASS = 30) - -/obj/item/frame/apc/try_build(turf/on_wall, mob/user as mob) - if (get_dist(on_wall, user)>1) - return - var/ndir = get_dir(user, on_wall) - if (!(ndir in cardinal)) - return - var/turf/loc = get_turf(user) - var/area/A = loc.loc - if (!istype(loc, /turf/simulated/floor)) - to_chat(user, "APC cannot be placed on this spot.") - return - if (A.requires_power == 0 || istype(A, /area/space)) - to_chat(user, "APC cannot be placed in this area.") - return - if (A.get_apc()) - to_chat(user, "This area already has an APC.") - return //only one APC per area - for(var/obj/machinery/power/terminal/T in loc) - if (T.master) - to_chat(user, "There is another network terminal here.") - return - else - new /obj/item/stack/cable_coil(loc, 10) - to_chat(user, "You cut the cables and disassemble the unused power terminal.") - qdel(T) - new /obj/machinery/power/apc(loc, ndir, 1) - qdel(src) +// APC HULL + +/obj/item/frame/apc + name = "\improper APC frame" + desc = "Used for repairing or building APCs" + icon = 'icons/obj/apc_repair.dmi' + icon_state = "apc_frame" + refund_amt = 2 + build_wall_only = TRUE + matter = list(MAT_STEEL = 100, MAT_GLASS = 30) + +/obj/item/frame/apc/try_build(turf/on_wall, mob/user as mob) + if (get_dist(on_wall, user)>1) + return + var/ndir = get_dir(user, on_wall) + if (!(ndir in cardinal)) + return + var/turf/loc = get_turf(user) + var/area/A = loc.loc + if (!istype(loc, /turf/simulated/floor)) + to_chat(user, "APC cannot be placed on this spot.") + return + if (A.requires_power == 0 || istype(A, /area/space)) + to_chat(user, "APC cannot be placed in this area.") + return + if (A.get_apc()) + to_chat(user, "This area already has an APC.") + return //only one APC per area + for(var/obj/machinery/power/terminal/T in loc) + if (T.master) + to_chat(user, "There is another network terminal here.") + return + else + new /obj/item/stack/cable_coil(loc, 10) + to_chat(user, "You cut the cables and disassemble the unused power terminal.") + qdel(T) + new /obj/machinery/power/apc(loc, ndir, 1) + qdel(src) diff --git a/code/game/objects/items/bells.dm b/code/game/objects/items/bells.dm index e9219957e40..0dce9e0223f 100644 --- a/code/game/objects/items/bells.dm +++ b/code/game/objects/items/bells.dm @@ -87,7 +87,7 @@ /obj/item/weapon/deskbell/attackby(obj/item/W, mob/user, params) if(!istype(W)) return - if(W.is_wrench() && isturf(loc)) + if(W.has_tool_quality(TOOL_WRENCH) && isturf(loc)) if(do_after(5)) if(!src) return to_chat(user, "You dissasemble the desk bell") diff --git a/code/game/objects/items/blueprints_vr.dm b/code/game/objects/items/blueprints_vr.dm index 92231894d9b..93bd466de60 100644 --- a/code/game/objects/items/blueprints_vr.dm +++ b/code/game/objects/items/blueprints_vr.dm @@ -108,7 +108,7 @@ to_chat(user, span_notice("You add some more writing material to the [src] with the [blueprint]!")) return else if(blueprint.uses_charges && blueprint.charges) //Getting from another with limited charges. - var/to_add = tgui_input_number(user, "How many charges do you want to add to the [src]?", "[blueprint]", missing_charges) + var/to_add = tgui_input_number(user, "How many charges do you want to add to the [src]?", "[blueprint]", missing_charges, blueprint.charges) if(!isnull(to_add) && blueprint.charges >= to_add) to_chat(user, span_notice("You add some more writing material to the [src] with the [blueprint]!")) blueprint.charges -= to_add diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index 94a24c9ee8b..aaca80ae28f 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -1,263 +1,263 @@ -//Also contains /obj/structure/closet/body_bag because I doubt anyone would think to look for bodybags in /object/structures - -/obj/item/bodybag - name = "body bag" - desc = "A folded bag designed for the storage and transportation of cadavers." - icon = 'icons/obj/closets/bodybag.dmi' - icon_state = "bodybag_folded" - w_class = ITEMSIZE_SMALL - -/obj/item/bodybag/attack_self(mob/user) - var/obj/structure/closet/body_bag/R = new /obj/structure/closet/body_bag(user.loc) - R.add_fingerprint(user) - qdel(src) - - -/obj/item/weapon/storage/box/bodybags - name = "body bags" - desc = "This box contains body bags." - icon_state = "bodybags" - starts_with = list(/obj/item/bodybag = 7) - -/obj/structure/closet/body_bag - name = "body bag" - desc = "A plastic bag designed for the storage and transportation of cadavers." - icon = 'icons/obj/closets/bodybag.dmi' - closet_appearance = null - open_sound = 'sound/items/zip.ogg' - close_sound = 'sound/items/zip.ogg' - var/item_path = /obj/item/bodybag - density = FALSE - storage_capacity = (MOB_MEDIUM * 2) - 1 - var/contains_body = 0 - -/obj/structure/closet/body_bag/attackby(var/obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/pen)) - var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null, MAX_NAME_LEN ) - if (user.get_active_hand() != W) - return - if (!in_range(src, user) && src.loc != user) - return - t = sanitizeSafe(t, MAX_NAME_LEN) - if (t) - src.name = "body bag - " - src.name += t - add_overlay("bodybag_label") - else - src.name = "body bag" - //..() //Doesn't need to run the parent. Since when can fucking bodybags be welded shut? -Agouri - return - else if(W.is_wirecutter()) - to_chat(user, "You cut the tag off the bodybag") - src.name = "body bag" - cut_overlays() - return - -/obj/structure/closet/body_bag/store_mobs(var/stored_units) - contains_body = ..() - return contains_body - -/obj/structure/closet/body_bag/close() - if(..()) - density = FALSE - return 1 - return 0 - -/obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location) - ..() - if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) - if(!ishuman(usr)) return 0 - if(opened) return 0 - if(contents.len) return 0 - visible_message("[usr] folds up the [src.name]") - var/folded = new item_path(get_turf(src)) - spawn(0) - qdel(src) - return folded - -/obj/structure/closet/body_bag/relaymove(mob/user,direction) - if(src.loc != get_turf(src)) - src.loc.relaymove(user,direction) - else - ..() - -/obj/structure/closet/body_bag/proc/get_occupants() - var/list/occupants = list() - for(var/mob/living/carbon/human/H in contents) - occupants += H - return occupants - -/obj/structure/closet/body_bag/proc/update(var/broadcast=0) - if(istype(loc, /obj/structure/morgue)) - var/obj/structure/morgue/M = loc - M.update(broadcast) - -/obj/structure/closet/body_bag/update_icon() - if(opened) - icon_state = "open" - else - icon_state = "closed_unlocked" - - cut_overlays() - /* Ours don't have toetags - if(has_label) - add_overlay("bodybag_label") - */ - - -/obj/item/bodybag/cryobag - name = "stasis bag" - desc = "A non-reusable plastic bag designed to slow down bodily functions such as circulation and breathing, \ - especially useful if short on time or in a hostile enviroment." - icon = 'icons/obj/closets/cryobag.dmi' - icon_state = "bodybag_folded" - item_state = "bodybag_cryo_folded" - origin_tech = list(TECH_BIO = 4) - var/obj/item/weapon/reagent_containers/syringe/syringe - -/obj/item/bodybag/cryobag/attack_self(mob/user) - var/obj/structure/closet/body_bag/cryobag/R = new /obj/structure/closet/body_bag/cryobag(user.loc) - R.add_fingerprint(user) - if(syringe) - R.syringe = syringe - syringe = null - qdel(src) - -/obj/structure/closet/body_bag/cryobag - name = "stasis bag" - desc = "A non-reusable plastic bag designed to slow down bodily functions such as circulation and breathing, \ - especially useful if short on time or in a hostile enviroment." - icon = 'icons/obj/closets/cryobag.dmi' - item_path = /obj/item/bodybag/cryobag - store_misc = 0 - store_items = 0 - var/used = 0 - var/obj/item/weapon/tank/tank = null - var/tank_type = /obj/item/weapon/tank/stasis/oxygen - var/stasis_level = 3 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) - var/obj/item/weapon/reagent_containers/syringe/syringe - -/obj/structure/closet/body_bag/cryobag/Initialize() - tank = new tank_type(null) //It's in nullspace to prevent ejection when the bag is opened. - ..() - -/obj/structure/closet/body_bag/cryobag/Destroy() - QDEL_NULL(syringe) - QDEL_NULL(tank) - return ..() - -/obj/structure/closet/body_bag/cryobag/attack_hand(mob/living/user) - if(used) - var/confirm = tgui_alert(user, "Are you sure you want to open \the [src]? \The [src] will expire upon opening it.", "Confirm Opening", list("No", "Yes")) - if(confirm == "Yes") - ..() // Will call `toggle()` and open the bag. - else - ..() - -/obj/structure/closet/body_bag/cryobag/open() - . = ..() - if(used) - new /obj/item/usedcryobag(loc) - qdel(src) - -/obj/structure/closet/body_bag/cryobag/update_icon() - ..() - cut_overlays() - var/image/I = image(icon, "indicator[opened]") - I.appearance_flags = RESET_COLOR - I.color = COLOR_LIME - add_overlay(I) - -/obj/structure/closet/body_bag/cryobag/MouseDrop(over_object, src_location, over_location) - . = ..() - if(. && syringe) - var/obj/item/bodybag/cryobag/folded = . - folded.syringe = syringe - syringe = null - -/obj/structure/closet/body_bag/cryobag/Entered(atom/movable/AM) - if(ishuman(AM)) - var/mob/living/carbon/human/H = AM - H.Stasis(stasis_level) - src.used = 1 - inject_occupant(H) - - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 1 - for(var/obj/item/organ/organ in O) - organ.preserved = 1 - ..() - -/obj/structure/closet/body_bag/cryobag/Exited(atom/movable/AM) - if(ishuman(AM)) - var/mob/living/carbon/human/H = AM - H.Stasis(0) - - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 0 - for(var/obj/item/organ/organ in O) - organ.preserved = 0 - ..() - -/obj/structure/closet/body_bag/cryobag/return_air() //Used to make stasis bags protect from vacuum. - if(tank) - return tank.air_contents - ..() - -/obj/structure/closet/body_bag/cryobag/proc/inject_occupant(var/mob/living/carbon/human/H) - if(!syringe) - return - - if(H.reagents) - syringe.reagents.trans_to_mob(H, 30, CHEM_BLOOD) - -/obj/structure/closet/body_bag/cryobag/examine(mob/user) - . = ..() - if(Adjacent(user)) //The bag's rather thick and opaque from a distance. - . += "You peer into \the [src]." - if(syringe) - . += "It has a syringe added to it." - for(var/mob/living/L in contents) - . += L.examine(user) - -/obj/structure/closet/body_bag/cryobag/attackby(obj/item/W, mob/user) - if(opened) - ..() - else //Allows the bag to respond to a health analyzer by analyzing the mob inside without needing to open it. - if(istype(W,/obj/item/device/healthanalyzer)) - var/obj/item/device/healthanalyzer/analyzer = W - for(var/mob/living/L in contents) - analyzer.attack(L,user) - - else if(istype(W,/obj/item/weapon/reagent_containers/syringe)) - if(syringe) - to_chat(user,"\The [src] already has an injector! Remove it first.") - else - var/obj/item/weapon/reagent_containers/syringe/syringe = W - to_chat(user,"You insert \the [syringe] into \the [src], and it locks into place.") - user.unEquip(syringe) - src.syringe = syringe - syringe.loc = null - for(var/mob/living/carbon/human/H in contents) - inject_occupant(H) - break - - else if(W.is_screwdriver()) - if(syringe) - if(used) - to_chat(user,"The injector cannot be removed now that the stasis bag has been used!") - else - syringe.forceMove(src.loc) - to_chat(user,"You pry \the [syringe] out of \the [src].") - syringe = null - - else - ..() - -/obj/item/usedcryobag - name = "used stasis bag" - desc = "Pretty useless now.." - icon_state = "bodybag_used" - icon = 'icons/obj/closets/cryobag.dmi' +//Also contains /obj/structure/closet/body_bag because I doubt anyone would think to look for bodybags in /object/structures + +/obj/item/bodybag + name = "body bag" + desc = "A folded bag designed for the storage and transportation of cadavers." + icon = 'icons/obj/closets/bodybag.dmi' + icon_state = "bodybag_folded" + w_class = ITEMSIZE_SMALL + +/obj/item/bodybag/attack_self(mob/user) + var/obj/structure/closet/body_bag/R = new /obj/structure/closet/body_bag(user.loc) + R.add_fingerprint(user) + qdel(src) + + +/obj/item/weapon/storage/box/bodybags + name = "body bags" + desc = "This box contains body bags." + icon_state = "bodybags" + starts_with = list(/obj/item/bodybag = 7) + +/obj/structure/closet/body_bag + name = "body bag" + desc = "A plastic bag designed for the storage and transportation of cadavers." + icon = 'icons/obj/closets/bodybag.dmi' + closet_appearance = null + open_sound = 'sound/items/zip.ogg' + close_sound = 'sound/items/zip.ogg' + var/item_path = /obj/item/bodybag + density = FALSE + storage_capacity = (MOB_MEDIUM * 2) - 1 + var/contains_body = 0 + +/obj/structure/closet/body_bag/attackby(var/obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/pen)) + var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null, MAX_NAME_LEN ) + if (user.get_active_hand() != W) + return + if (!in_range(src, user) && src.loc != user) + return + t = sanitizeSafe(t, MAX_NAME_LEN) + if (t) + src.name = "body bag - " + src.name += t + add_overlay("bodybag_label") + else + src.name = "body bag" + //..() //Doesn't need to run the parent. Since when can fucking bodybags be welded shut? -Agouri + return + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + to_chat(user, "You cut the tag off the bodybag") + src.name = "body bag" + cut_overlays() + return + +/obj/structure/closet/body_bag/store_mobs(var/stored_units) + contains_body = ..() + return contains_body + +/obj/structure/closet/body_bag/close() + if(..()) + density = FALSE + return 1 + return 0 + +/obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location) + ..() + if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) + if(!ishuman(usr)) return 0 + if(opened) return 0 + if(contents.len) return 0 + visible_message("[usr] folds up the [src.name]") + var/folded = new item_path(get_turf(src)) + spawn(0) + qdel(src) + return folded + +/obj/structure/closet/body_bag/relaymove(mob/user,direction) + if(src.loc != get_turf(src)) + src.loc.relaymove(user,direction) + else + ..() + +/obj/structure/closet/body_bag/proc/get_occupants() + var/list/occupants = list() + for(var/mob/living/carbon/human/H in contents) + occupants += H + return occupants + +/obj/structure/closet/body_bag/proc/update(var/broadcast=0) + if(istype(loc, /obj/structure/morgue)) + var/obj/structure/morgue/M = loc + M.update(broadcast) + +/obj/structure/closet/body_bag/update_icon() + if(opened) + icon_state = "open" + else + icon_state = "closed_unlocked" + + cut_overlays() + /* Ours don't have toetags + if(has_label) + add_overlay("bodybag_label") + */ + + +/obj/item/bodybag/cryobag + name = "stasis bag" + desc = "A non-reusable plastic bag designed to slow down bodily functions such as circulation and breathing, \ + especially useful if short on time or in a hostile enviroment." + icon = 'icons/obj/closets/cryobag.dmi' + icon_state = "bodybag_folded" + item_state = "bodybag_cryo_folded" + origin_tech = list(TECH_BIO = 4) + var/obj/item/weapon/reagent_containers/syringe/syringe + +/obj/item/bodybag/cryobag/attack_self(mob/user) + var/obj/structure/closet/body_bag/cryobag/R = new /obj/structure/closet/body_bag/cryobag(user.loc) + R.add_fingerprint(user) + if(syringe) + R.syringe = syringe + syringe = null + qdel(src) + +/obj/structure/closet/body_bag/cryobag + name = "stasis bag" + desc = "A non-reusable plastic bag designed to slow down bodily functions such as circulation and breathing, \ + especially useful if short on time or in a hostile enviroment." + icon = 'icons/obj/closets/cryobag.dmi' + item_path = /obj/item/bodybag/cryobag + store_misc = 0 + store_items = 0 + var/used = 0 + var/obj/item/weapon/tank/tank = null + var/tank_type = /obj/item/weapon/tank/stasis/oxygen + var/stasis_level = 3 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) + var/obj/item/weapon/reagent_containers/syringe/syringe + +/obj/structure/closet/body_bag/cryobag/Initialize() + tank = new tank_type(null) //It's in nullspace to prevent ejection when the bag is opened. + ..() + +/obj/structure/closet/body_bag/cryobag/Destroy() + QDEL_NULL(syringe) + QDEL_NULL(tank) + return ..() + +/obj/structure/closet/body_bag/cryobag/attack_hand(mob/living/user) + if(used) + var/confirm = tgui_alert(user, "Are you sure you want to open \the [src]? \The [src] will expire upon opening it.", "Confirm Opening", list("No", "Yes")) + if(confirm == "Yes") + ..() // Will call `toggle()` and open the bag. + else + ..() + +/obj/structure/closet/body_bag/cryobag/open() + . = ..() + if(used) + new /obj/item/usedcryobag(loc) + qdel(src) + +/obj/structure/closet/body_bag/cryobag/update_icon() + ..() + cut_overlays() + var/image/I = image(icon, "indicator[opened]") + I.appearance_flags = RESET_COLOR + I.color = COLOR_LIME + add_overlay(I) + +/obj/structure/closet/body_bag/cryobag/MouseDrop(over_object, src_location, over_location) + . = ..() + if(. && syringe) + var/obj/item/bodybag/cryobag/folded = . + folded.syringe = syringe + syringe = null + +/obj/structure/closet/body_bag/cryobag/Entered(atom/movable/AM) + if(ishuman(AM)) + var/mob/living/carbon/human/H = AM + H.Stasis(stasis_level) + src.used = 1 + inject_occupant(H) + + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 1 + for(var/obj/item/organ/organ in O) + organ.preserved = 1 + ..() + +/obj/structure/closet/body_bag/cryobag/Exited(atom/movable/AM) + if(ishuman(AM)) + var/mob/living/carbon/human/H = AM + H.Stasis(0) + + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 0 + for(var/obj/item/organ/organ in O) + organ.preserved = 0 + ..() + +/obj/structure/closet/body_bag/cryobag/return_air() //Used to make stasis bags protect from vacuum. + if(tank) + return tank.air_contents + ..() + +/obj/structure/closet/body_bag/cryobag/proc/inject_occupant(var/mob/living/carbon/human/H) + if(!syringe) + return + + if(H.reagents) + syringe.reagents.trans_to_mob(H, 30, CHEM_BLOOD) + +/obj/structure/closet/body_bag/cryobag/examine(mob/user) + . = ..() + if(Adjacent(user)) //The bag's rather thick and opaque from a distance. + . += "You peer into \the [src]." + if(syringe) + . += "It has a syringe added to it." + for(var/mob/living/L in contents) + . += L.examine(user) + +/obj/structure/closet/body_bag/cryobag/attackby(obj/item/W, mob/user) + if(opened) + ..() + else //Allows the bag to respond to a health analyzer by analyzing the mob inside without needing to open it. + if(istype(W,/obj/item/device/healthanalyzer)) + var/obj/item/device/healthanalyzer/analyzer = W + for(var/mob/living/L in contents) + analyzer.attack(L,user) + + else if(istype(W,/obj/item/weapon/reagent_containers/syringe)) + if(syringe) + to_chat(user,"\The [src] already has an injector! Remove it first.") + else + var/obj/item/weapon/reagent_containers/syringe/syringe = W + to_chat(user,"You insert \the [syringe] into \the [src], and it locks into place.") + user.unEquip(syringe) + src.syringe = syringe + syringe.loc = null + for(var/mob/living/carbon/human/H in contents) + inject_occupant(H) + break + + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(syringe) + if(used) + to_chat(user,"The injector cannot be removed now that the stasis bag has been used!") + else + syringe.forceMove(src.loc) + to_chat(user,"You pry \the [syringe] out of \the [src].") + syringe = null + + else + ..() + +/obj/item/usedcryobag + name = "used stasis bag" + desc = "Pretty useless now.." + icon_state = "bodybag_used" + icon = 'icons/obj/closets/cryobag.dmi' diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 8c3a5b0551b..280c43db0c0 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -1,214 +1,214 @@ -/obj/item/weapon/pen/crayon/red - icon_state = "crayonred" - colour = "#DA0000" - shadeColour = "#810C0C" - colourName = "red" - -/obj/item/weapon/pen/crayon/orange - icon_state = "crayonorange" - colour = "#FF9300" - shadeColour = "#A55403" - colourName = "orange" - -/obj/item/weapon/pen/crayon/yellow - icon_state = "crayonyellow" - colour = "#FFF200" - shadeColour = "#886422" - colourName = "yellow" - -/obj/item/weapon/pen/crayon/green - icon_state = "crayongreen" - colour = "#A8E61D" - shadeColour = "#61840F" - colourName = "green" - -/obj/item/weapon/pen/crayon/blue - icon_state = "crayonblue" - colour = "#00B7EF" - shadeColour = "#0082A8" - colourName = "blue" - -/obj/item/weapon/pen/crayon/purple - icon_state = "crayonpurple" - colour = "#DA00FF" - shadeColour = "#810CFF" - colourName = "purple" - -/obj/item/weapon/pen/crayon/mime - icon_state = "crayonmime" - desc = "A very sad-looking crayon." - colour = "#FFFFFF" - shadeColour = "#000000" - colourName = "mime" - uses = 0 - -/obj/item/weapon/pen/crayon/mime/attack_self(mob/living/user as mob) //inversion - if(colour != "#FFFFFF" && shadeColour != "#000000") - colour = "#FFFFFF" - shadeColour = "#000000" - to_chat(user, "You will now draw in white and black with this crayon.") - else - colour = "#000000" - shadeColour = "#FFFFFF" - to_chat(user, "You will now draw in black and white with this crayon.") - return - -/obj/item/weapon/pen/crayon/rainbow - icon_state = "crayonrainbow" - colour = "#FFF000" - shadeColour = "#000FFF" - colourName = "rainbow" - uses = 0 - -/obj/item/weapon/pen/crayon/rainbow/attack_self(mob/living/user as mob) - colour = input(user, "Please select the main colour.", "Crayon colour") as color - shadeColour = input(user, "Please select the shade colour.", "Crayon colour") as color - return - -/obj/item/weapon/pen/crayon/afterattack(atom/target, mob/user as mob, proximity) - if(!proximity) return - if(istype(target,/turf/simulated/floor)) - var/drawtype = tgui_input_list(user, "Choose what you'd like to draw.", "Crayon scribbles", list("graffiti","rune","letter","arrow")) - if(!drawtype) - return - if(get_dist(target, user) > 1 || !(user.z == target.z)) - return - switch(drawtype) - if("letter") - drawtype = tgui_input_list(user, "Choose the letter.", "Crayon scribbles", list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing a letter on the [target.name].") - if("graffiti") - drawtype = tgui_input_list(user, "Choose the graffiti.", "Crayon scribbles", list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing graffiti on the [target.name].") - if("rune") - drawtype = tgui_input_list(user, "Choose the rune.", "Crayon scribbles", list("rune1", "rune2", "rune3", "rune4", "rune5", "rune6")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing a rune on the [target.name].") - if("arrow") - drawtype = tgui_input_list(user, "Choose the arrow.", "Crayon scribbles", list("left", "right", "up", "down")) - if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) - return - to_chat(user, "You start drawing an arrow on the [target.name].") - if(instant || do_after(user, 50)) - new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype) - to_chat(user, "You finish drawing.") - - var/msg = "[user.client.key] ([user]) has drawn [drawtype] (with [src]) at [target.x],[target.y],[target.z]." - if(config.log_graffiti) - message_admins(msg) - log_game(msg) //We will log it anyways. - - target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on. - if(uses) - uses-- - if(!uses) - to_chat(user, "You used up your crayon!") - qdel(src) - return - -/obj/item/weapon/pen/crayon/attack(mob/living/M as mob, mob/living/user as mob) - if(M == user) - to_chat(user, "You take a bite of the crayon and swallow it.") - user.nutrition += 1 - user.reagents.add_reagent("crayon_dust",min(5,uses)/3) - if(uses) - uses -= 5 - if(uses <= 0) - to_chat(user, "You ate your crayon!") - qdel(src) - else - ..() - -/obj/item/weapon/pen/crayon/marker/black - icon_state = "markerblack" - colour = "#2D2D2D" - shadeColour = "#000000" - colourName = "black" - -/obj/item/weapon/pen/crayon/marker/red - icon_state = "markerred" - colour = "#DA0000" - shadeColour = "#810C0C" - colourName = "red" - -/obj/item/weapon/pen/crayon/marker/orange - icon_state = "markerorange" - colour = "#FF9300" - shadeColour = "#A55403" - colourName = "orange" - -/obj/item/weapon/pen/crayon/marker/yellow - icon_state = "markeryellow" - colour = "#FFF200" - shadeColour = "#886422" - colourName = "yellow" - -/obj/item/weapon/pen/crayon/marker/green - icon_state = "markergreen" - colour = "#A8E61D" - shadeColour = "#61840F" - colourName = "green" - -/obj/item/weapon/pen/crayon/marker/blue - icon_state = "markerblue" - colour = "#00B7EF" - shadeColour = "#0082A8" - colourName = "blue" - -/obj/item/weapon/pen/crayon/marker/purple - icon_state = "markerpurple" - colour = "#DA00FF" - shadeColour = "#810CFF" - colourName = "purple" - -/obj/item/weapon/pen/crayon/marker/mime - icon_state = "markermime" - desc = "A very sad-looking marker." - colour = "#FFFFFF" - shadeColour = "#000000" - colourName = "mime" - uses = 0 - -/obj/item/weapon/pen/crayon/marker/mime/attack_self(mob/living/user as mob) //inversion - if(colour != "#FFFFFF" && shadeColour != "#000000") - colour = "#FFFFFF" - shadeColour = "#000000" - to_chat(user, "You will now draw in white and black with this marker.") - else - colour = "#000000" - shadeColour = "#FFFFFF" - to_chat(user, "You will now draw in black and white with this marker.") - return - -/obj/item/weapon/pen/crayon/marker/rainbow - icon_state = "markerrainbow" - colour = "#FFF000" - shadeColour = "#000FFF" - colourName = "rainbow" - uses = 0 - -/obj/item/weapon/pen/crayon/marker/rainbow/attack_self(mob/living/user as mob) - colour = input(user, "Please select the main colour.", "Marker colour") as color - shadeColour = input(user, "Please select the shade colour.", "Marker colour") as color - return - -/obj/item/weapon/pen/crayon/marker/attack(mob/living/M as mob, mob/living/user as mob) - if(M == user) - to_chat(user, "You take a bite of the marker and swallow it.") - user.nutrition += 1 - user.reagents.add_reagent("marker_ink",6) - if(uses) - uses -= 5 - if(uses <= 0) - to_chat(user, "You ate the marker!") - qdel(src) - else - ..() - -/obj/item/weapon/pen/crayon/attack_self(var/mob/user) - return +/obj/item/weapon/pen/crayon/red + icon_state = "crayonred" + colour = "#DA0000" + shadeColour = "#810C0C" + colourName = "red" + +/obj/item/weapon/pen/crayon/orange + icon_state = "crayonorange" + colour = "#FF9300" + shadeColour = "#A55403" + colourName = "orange" + +/obj/item/weapon/pen/crayon/yellow + icon_state = "crayonyellow" + colour = "#FFF200" + shadeColour = "#886422" + colourName = "yellow" + +/obj/item/weapon/pen/crayon/green + icon_state = "crayongreen" + colour = "#A8E61D" + shadeColour = "#61840F" + colourName = "green" + +/obj/item/weapon/pen/crayon/blue + icon_state = "crayonblue" + colour = "#00B7EF" + shadeColour = "#0082A8" + colourName = "blue" + +/obj/item/weapon/pen/crayon/purple + icon_state = "crayonpurple" + colour = "#DA00FF" + shadeColour = "#810CFF" + colourName = "purple" + +/obj/item/weapon/pen/crayon/mime + icon_state = "crayonmime" + desc = "A very sad-looking crayon." + colour = "#FFFFFF" + shadeColour = "#000000" + colourName = "mime" + uses = 0 + +/obj/item/weapon/pen/crayon/mime/attack_self(mob/living/user as mob) //inversion + if(colour != "#FFFFFF" && shadeColour != "#000000") + colour = "#FFFFFF" + shadeColour = "#000000" + to_chat(user, "You will now draw in white and black with this crayon.") + else + colour = "#000000" + shadeColour = "#FFFFFF" + to_chat(user, "You will now draw in black and white with this crayon.") + return + +/obj/item/weapon/pen/crayon/rainbow + icon_state = "crayonrainbow" + colour = "#FFF000" + shadeColour = "#000FFF" + colourName = "rainbow" + uses = 0 + +/obj/item/weapon/pen/crayon/rainbow/attack_self(mob/living/user as mob) + colour = input(user, "Please select the main colour.", "Crayon colour") as color + shadeColour = input(user, "Please select the shade colour.", "Crayon colour") as color + return + +/obj/item/weapon/pen/crayon/afterattack(atom/target, mob/user as mob, proximity) + if(!proximity) return + if(istype(target,/turf/simulated/floor)) + var/drawtype = tgui_input_list(user, "Choose what you'd like to draw.", "Crayon scribbles", list("graffiti","rune","letter","arrow")) + if(!drawtype) + return + if(get_dist(target, user) > 1 || !(user.z == target.z)) + return + switch(drawtype) + if("letter") + drawtype = tgui_input_list(user, "Choose the letter.", "Crayon scribbles", list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing a letter on the [target.name].") + if("graffiti") + drawtype = tgui_input_list(user, "Choose the graffiti.", "Crayon scribbles", list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing graffiti on the [target.name].") + if("rune") + drawtype = tgui_input_list(user, "Choose the rune.", "Crayon scribbles", list("rune1", "rune2", "rune3", "rune4", "rune5", "rune6")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing a rune on the [target.name].") + if("arrow") + drawtype = tgui_input_list(user, "Choose the arrow.", "Crayon scribbles", list("left", "right", "up", "down")) + if(!drawtype || get_dist(target, user) > 1 || !(user.z == target.z)) + return + to_chat(user, "You start drawing an arrow on the [target.name].") + if(instant || do_after(user, 50)) + new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype) + to_chat(user, "You finish drawing.") + + var/msg = "[user.client.key] ([user]) has drawn [drawtype] (with [src]) at [target.x],[target.y],[target.z]." + if(config.log_graffiti) + message_admins(msg) + log_game(msg) //We will log it anyways. + + target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on. + if(uses) + uses-- + if(!uses) + to_chat(user, "You used up your crayon!") + qdel(src) + return + +/obj/item/weapon/pen/crayon/attack(mob/living/M as mob, mob/living/user as mob) + if(M == user) + to_chat(user, "You take a bite of the crayon and swallow it.") + user.nutrition += 1 + user.reagents.add_reagent("crayon_dust",min(5,uses)/3) + if(uses) + uses -= 5 + if(uses <= 0) + to_chat(user, "You ate your crayon!") + qdel(src) + else + ..() + +/obj/item/weapon/pen/crayon/marker/black + icon_state = "markerblack" + colour = "#2D2D2D" + shadeColour = "#000000" + colourName = "black" + +/obj/item/weapon/pen/crayon/marker/red + icon_state = "markerred" + colour = "#DA0000" + shadeColour = "#810C0C" + colourName = "red" + +/obj/item/weapon/pen/crayon/marker/orange + icon_state = "markerorange" + colour = "#FF9300" + shadeColour = "#A55403" + colourName = "orange" + +/obj/item/weapon/pen/crayon/marker/yellow + icon_state = "markeryellow" + colour = "#FFF200" + shadeColour = "#886422" + colourName = "yellow" + +/obj/item/weapon/pen/crayon/marker/green + icon_state = "markergreen" + colour = "#A8E61D" + shadeColour = "#61840F" + colourName = "green" + +/obj/item/weapon/pen/crayon/marker/blue + icon_state = "markerblue" + colour = "#00B7EF" + shadeColour = "#0082A8" + colourName = "blue" + +/obj/item/weapon/pen/crayon/marker/purple + icon_state = "markerpurple" + colour = "#DA00FF" + shadeColour = "#810CFF" + colourName = "purple" + +/obj/item/weapon/pen/crayon/marker/mime + icon_state = "markermime" + desc = "A very sad-looking marker." + colour = "#FFFFFF" + shadeColour = "#000000" + colourName = "mime" + uses = 0 + +/obj/item/weapon/pen/crayon/marker/mime/attack_self(mob/living/user as mob) //inversion + if(colour != "#FFFFFF" && shadeColour != "#000000") + colour = "#FFFFFF" + shadeColour = "#000000" + to_chat(user, "You will now draw in white and black with this marker.") + else + colour = "#000000" + shadeColour = "#FFFFFF" + to_chat(user, "You will now draw in black and white with this marker.") + return + +/obj/item/weapon/pen/crayon/marker/rainbow + icon_state = "markerrainbow" + colour = "#FFF000" + shadeColour = "#000FFF" + colourName = "rainbow" + uses = 0 + +/obj/item/weapon/pen/crayon/marker/rainbow/attack_self(mob/living/user as mob) + colour = input(user, "Please select the main colour.", "Marker colour") as color + shadeColour = input(user, "Please select the shade colour.", "Marker colour") as color + return + +/obj/item/weapon/pen/crayon/marker/attack(mob/living/M as mob, mob/living/user as mob) + if(M == user) + to_chat(user, "You take a bite of the marker and swallow it.") + user.nutrition += 1 + user.reagents.add_reagent("marker_ink",6) + if(uses) + uses -= 5 + if(uses <= 0) + to_chat(user, "You ate the marker!") + qdel(src) + else + ..() + +/obj/item/weapon/pen/crayon/attack_self(var/mob/user) + return diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index ac73bf97c8a..0ba1de02356 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -1,185 +1,185 @@ -/obj/item/device/aicard - name = "intelliCore" - desc = "Used to preserve and transport an AI." - icon = 'icons/obj/pda.dmi' - icon_state = "aicard" // aicard-full - item_state = "aicard" - w_class = ITEMSIZE_NORMAL - slot_flags = SLOT_BELT - show_messages = 0 - preserve_item = 1 - - var/flush = null - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) - - var/mob/living/silicon/ai/carded_ai - -/obj/item/device/aicard/attack(mob/living/silicon/decoy/M as mob, mob/user as mob) - if (!istype (M, /mob/living/silicon/decoy)) - return ..() - else - M.death() - to_chat(user, "ERROR ERROR ERROR") - -/obj/item/device/aicard/attack_self(mob/user) - tgui_interact(user) - -/obj/item/device/aicard/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui_state/custom_state) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AICard", "[name]") // 600, 394 - ui.open() - if(custom_state) - ui.set_state(custom_state) - -/obj/item/device/aicard/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/aicard/tgui_data(mob/user) - var/data[0] - - data["has_ai"] = carded_ai != null - if(carded_ai) - data["name"] = carded_ai.name - data["integrity"] = carded_ai.hardware_integrity() - data["backup_capacitor"] = carded_ai.backup_capacitor() - data["radio"] = !carded_ai.aiRadio.disabledAi - data["wireless"] = !carded_ai.control_disabled - data["operational"] = carded_ai.stat != DEAD - data["flushing"] = flush - - var/laws[0] - for(var/datum/ai_law/law in carded_ai.laws.all_laws()) - if(law in carded_ai.laws.ion_laws) // If we're an ion law, give it an ion index code - laws.Add(ionnum() + ". " + law.law) - else - laws.Add(num2text(law.get_index()) + ". " + law.law) - data["laws"] = laws - data["has_laws"] = length(carded_ai.laws.all_laws()) - - return data - -/obj/item/device/aicard/tgui_act(action, params) - if(..()) - return TRUE - - if(!carded_ai) - return - - var/user = usr - switch(action) - if("wipe") - msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].") - add_attack_logs(user,carded_ai,"Purged from AI Card") - INVOKE_ASYNC(src, PROC_REF(wipe_ai)) - if("radio") - carded_ai.aiRadio.disabledAi = !carded_ai.aiRadio.disabledAi - to_chat(carded_ai, "Your Subspace Transceiver has been [carded_ai.aiRadio.disabledAi ? "disabled" : "enabled"]!") - to_chat(user, "You [carded_ai.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") - if("wireless") - carded_ai.control_disabled = !carded_ai.control_disabled - to_chat(carded_ai, "Your wireless interface has been [carded_ai.control_disabled ? "disabled" : "enabled"]!") - to_chat(user, "You [carded_ai.control_disabled ? "disable" : "enable"] the AI's wireless interface.") - if(carded_ai.control_disabled && carded_ai.deployed_shell) - carded_ai.disconnect_shell("Disconnecting from remote shell due to [src] wireless access interface being disabled.") - update_icon() - - return TRUE - -/obj/item/device/aicard/update_icon() - cut_overlays() - if(carded_ai) - if (!carded_ai.control_disabled) - add_overlay("aicard-on") - if(carded_ai.stat) - icon_state = "aicard-404" - else - icon_state = "aicard-full" - else - icon_state = "aicard" - -/obj/item/device/aicard/proc/grab_ai(var/mob/living/silicon/ai/ai, var/mob/living/user) - if(!ai.client && !ai.deployed_shell) - to_chat(user, "ERROR: AI [ai.name] is offline. Unable to transfer.") - return 0 - - if(carded_ai) - to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") - return 0 - - if(!user.IsAdvancedToolUser() && isanimal(user)) - var/mob/living/simple_mob/S = user - if(!S.IsHumanoidToolUser(src)) - return 0 - - user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...") - show_message(span("critical", "\The [user] is transferring you into \the [src]!")) - - if(do_after(user, 100)) - if(carded_ai) - to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") - return 0 - if(istype(ai.loc, /turf/)) - new /obj/structure/AIcore/deactivated(get_turf(ai)) - - ai.carded = 1 - add_attack_logs(user,ai,"Extracted into AI Card") - src.name = "[initial(name)] - [ai.name]" - - ai.loc = src - ai.destroy_eyeobj(src) - ai.cancel_camera() - ai.control_disabled = 1 - ai.aiRestorePowerRoutine = 0 - carded_ai = ai - ai.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.") //If the AI is controlling a borg, force the player back to core! - - if(ai.client) - to_chat(ai, "You have been transferred into a mobile core. Remote access lost.") - if(user.client) - to_chat(ai, "Transfer successful: [ai.name] extracted from current device and placed within mobile core.") - - ai.canmove = 1 - update_icon() - return 1 - -/obj/item/device/aicard/proc/clear() - if(carded_ai && istype(carded_ai.loc, /turf)) - carded_ai.canmove = 0 - carded_ai.carded = 0 - name = initial(name) - carded_ai = null - update_icon() - -/obj/item/device/aicard/see_emote(mob/living/M, text) - if(carded_ai && carded_ai.client) - var/rendered = "[text]" - carded_ai.show_message(rendered, 2) - ..() - -/obj/item/device/aicard/show_message(msg, type, alt, alt_type) - if(carded_ai && carded_ai.client) - var/rendered = "[msg]" - carded_ai.show_message(rendered, type) - ..() - -/obj/item/device/aicard/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/weapon/rig/rig = src.get_rig() - if(istype(rig)) - rig.forced_move(direction, user) - -/obj/item/device/aicard/proc/wipe_ai() - var/mob/living/silicon/ai/AI = carded_ai - flush = TRUE - AI.suiciding = TRUE - to_chat(AI, "Your power has been disabled!") - while(AI && AI.stat != DEAD) - // This is absolutely evil and I love it. - if(AI.deployed_shell && prob(AI.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely. - AI.disconnect_shell("Disconnecting from remote shell due to insufficent power.") - AI.adjustOxyLoss(2) - AI.updatehealth() - sleep(10) - flush = FALSE +/obj/item/device/aicard + name = "intelliCore" + desc = "Used to preserve and transport an AI." + icon = 'icons/obj/pda.dmi' + icon_state = "aicard" // aicard-full + item_state = "aicard" + w_class = ITEMSIZE_NORMAL + slot_flags = SLOT_BELT + show_messages = 0 + preserve_item = 1 + + var/flush = null + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) + + var/mob/living/silicon/ai/carded_ai + +/obj/item/device/aicard/attack(mob/living/silicon/decoy/M as mob, mob/user as mob) + if (!istype (M, /mob/living/silicon/decoy)) + return ..() + else + M.death() + to_chat(user, "ERROR ERROR ERROR") + +/obj/item/device/aicard/attack_self(mob/user) + tgui_interact(user) + +/obj/item/device/aicard/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui_state/custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AICard", "[name]") // 600, 394 + ui.open() + if(custom_state) + ui.set_state(custom_state) + +/obj/item/device/aicard/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/aicard/tgui_data(mob/user) + var/data[0] + + data["has_ai"] = carded_ai != null + if(carded_ai) + data["name"] = carded_ai.name + data["integrity"] = carded_ai.hardware_integrity() + data["backup_capacitor"] = carded_ai.backup_capacitor() + data["radio"] = !carded_ai.aiRadio.disabledAi + data["wireless"] = !carded_ai.control_disabled + data["operational"] = carded_ai.stat != DEAD + data["flushing"] = flush + + var/laws[0] + for(var/datum/ai_law/law in carded_ai.laws.all_laws()) + if(law in carded_ai.laws.ion_laws) // If we're an ion law, give it an ion index code + laws.Add(ionnum() + ". " + law.law) + else + laws.Add(num2text(law.get_index()) + ". " + law.law) + data["laws"] = laws + data["has_laws"] = length(carded_ai.laws.all_laws()) + + return data + +/obj/item/device/aicard/tgui_act(action, params) + if(..()) + return TRUE + + if(!carded_ai) + return + + var/user = usr + switch(action) + if("wipe") + msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].") + add_attack_logs(user,carded_ai,"Purged from AI Card") + INVOKE_ASYNC(src, PROC_REF(wipe_ai)) + if("radio") + carded_ai.aiRadio.disabledAi = !carded_ai.aiRadio.disabledAi + to_chat(carded_ai, "Your Subspace Transceiver has been [carded_ai.aiRadio.disabledAi ? "disabled" : "enabled"]!") + to_chat(user, "You [carded_ai.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") + if("wireless") + carded_ai.control_disabled = !carded_ai.control_disabled + to_chat(carded_ai, "Your wireless interface has been [carded_ai.control_disabled ? "disabled" : "enabled"]!") + to_chat(user, "You [carded_ai.control_disabled ? "disable" : "enable"] the AI's wireless interface.") + if(carded_ai.control_disabled && carded_ai.deployed_shell) + carded_ai.disconnect_shell("Disconnecting from remote shell due to [src] wireless access interface being disabled.") + update_icon() + + return TRUE + +/obj/item/device/aicard/update_icon() + cut_overlays() + if(carded_ai) + if (!carded_ai.control_disabled) + add_overlay("aicard-on") + if(carded_ai.stat) + icon_state = "aicard-404" + else + icon_state = "aicard-full" + else + icon_state = "aicard" + +/obj/item/device/aicard/proc/grab_ai(var/mob/living/silicon/ai/ai, var/mob/living/user) + if(!ai.client && !ai.deployed_shell) + to_chat(user, "ERROR: AI [ai.name] is offline. Unable to transfer.") + return 0 + + if(carded_ai) + to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") + return 0 + + if(!user.IsAdvancedToolUser() && isanimal(user)) + var/mob/living/simple_mob/S = user + if(!S.IsHumanoidToolUser(src)) + return 0 + + user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...") + show_message(span("critical", "\The [user] is transferring you into \the [src]!")) + + if(do_after(user, 100)) + if(carded_ai) + to_chat(user, "Transfer failed: Existing AI found on remote device. Remove existing AI to install a new one.") + return 0 + if(istype(ai.loc, /turf/)) + new /obj/structure/AIcore/deactivated(get_turf(ai)) + + ai.carded = 1 + add_attack_logs(user,ai,"Extracted into AI Card") + src.name = "[initial(name)] - [ai.name]" + + ai.loc = src + ai.destroy_eyeobj(src) + ai.cancel_camera() + ai.control_disabled = 1 + ai.aiRestorePowerRoutine = 0 + carded_ai = ai + ai.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.") //If the AI is controlling a borg, force the player back to core! + + if(ai.client) + to_chat(ai, "You have been transferred into a mobile core. Remote access lost.") + if(user.client) + to_chat(ai, "Transfer successful: [ai.name] extracted from current device and placed within mobile core.") + + ai.canmove = 1 + update_icon() + return 1 + +/obj/item/device/aicard/proc/clear() + if(carded_ai && istype(carded_ai.loc, /turf)) + carded_ai.canmove = 0 + carded_ai.carded = 0 + name = initial(name) + carded_ai = null + update_icon() + +/obj/item/device/aicard/see_emote(mob/living/M, text) + if(carded_ai && carded_ai.client) + var/rendered = "[text]" + carded_ai.show_message(rendered, 2) + ..() + +/obj/item/device/aicard/show_message(msg, type, alt, alt_type) + if(carded_ai && carded_ai.client) + var/rendered = "[msg]" + carded_ai.show_message(rendered, type) + ..() + +/obj/item/device/aicard/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.forced_move(direction, user) + +/obj/item/device/aicard/proc/wipe_ai() + var/mob/living/silicon/ai/AI = carded_ai + flush = TRUE + AI.suiciding = TRUE + to_chat(AI, "Your power has been disabled!") + while(AI && AI.stat != DEAD) + // This is absolutely evil and I love it. + if(AI.deployed_shell && prob(AI.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely. + AI.disconnect_shell("Disconnecting from remote shell due to insufficent power.") + AI.adjustOxyLoss(2) + AI.updatehealth() + sleep(10) + flush = FALSE diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 9a9e7ea0ae1..054896cc387 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -1,147 +1,147 @@ -/obj/item/device/chameleon - name = "chameleon projector" - icon_state = "shield0" - slot_flags = SLOT_BELT - item_state = "electronic" - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_ILLEGAL = 4, TECH_MAGNET = 4) - var/can_use = 1 - var/obj/effect/dummy/chameleon/active_dummy = null - var/saved_item = /obj/item/trash/cigbutt - var/saved_icon = 'icons/inventory/face/item.dmi' - var/saved_icon_state = "cigbutt" - var/saved_overlays - -/obj/item/device/chameleon/dropped() - disrupt() - ..() - -/obj/item/device/chameleon/equipped() - ..() - disrupt() - ..() - -/obj/item/device/chameleon/attack_self() - toggle() - -/obj/item/device/chameleon/afterattack(atom/target, mob/user , proximity) - if(!proximity) return - if(!active_dummy) - if(istype(target,/obj/item) && !istype(target, /obj/item/weapon/disk/nuclear)) - playsound(src, 'sound/weapons/flash.ogg', 100, 1, -6) - to_chat(user, "Scanned [target].") - saved_item = target.type - saved_icon = target.icon - saved_icon_state = target.icon_state - saved_overlays = target.overlays - -/obj/item/device/chameleon/proc/toggle() - if(!can_use || !saved_item) return - if(active_dummy) - eject_all() - playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) - qdel(active_dummy) - active_dummy = null - to_chat(usr, "You deactivate the [src].") - var/obj/effect/overlay/T = new /obj/effect/overlay(get_turf(src)) - T.icon = 'icons/effects/effects.dmi' - flick("emppulse",T) - spawn(8) qdel(T) - else - playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) - var/obj/O = new saved_item(src) - if(!O) return - var/obj/effect/dummy/chameleon/C = new /obj/effect/dummy/chameleon(usr.loc) - C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, src) - qdel(O) - to_chat(usr, "You activate the [src].") - var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) - T.icon = 'icons/effects/effects.dmi' - flick("emppulse",T) - spawn(8) qdel(T) - -/obj/item/device/chameleon/proc/disrupt(var/delete_dummy = 1) - if(active_dummy) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - spark_system.start() - eject_all() - if(delete_dummy) - qdel(active_dummy) - active_dummy = null - can_use = 0 - spawn(50) can_use = 1 - -/obj/item/device/chameleon/proc/eject_all() - for(var/atom/movable/A in active_dummy) - A.loc = active_dummy.loc - if(ismob(A)) - var/mob/M = A - M.reset_view(null) - -/obj/effect/dummy/chameleon - name = "" - desc = "" - density = FALSE - anchored = TRUE - var/can_move = 1 - var/obj/item/device/chameleon/master = null - -/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, var/obj/item/device/chameleon/C) - name = O.name - desc = O.desc - icon = new_icon - icon_state = new_iconstate - overlays = new_overlays - set_dir(O.dir) - M.loc = src - master = C - master.active_dummy = src - -/obj/effect/dummy/chameleon/attackby() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/attack_hand() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/ex_act() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/bullet_act() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - ..() - master.disrupt() - -/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) - if(istype(loc, /turf/space)) return //No magical space movement! - - if(can_move) - can_move = 0 - switch(user.bodytemperature) - if(300 to INFINITY) - spawn(10) can_move = 1 - if(295 to 300) - spawn(13) can_move = 1 - if(280 to 295) - spawn(16) can_move = 1 - if(260 to 280) - spawn(20) can_move = 1 - else - spawn(25) can_move = 1 - step(src, direction) - return - -/obj/effect/dummy/chameleon/Destroy() - master.disrupt(0) - ..() +/obj/item/device/chameleon + name = "chameleon projector" + icon_state = "shield0" + slot_flags = SLOT_BELT + item_state = "electronic" + throwforce = 5.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_ILLEGAL = 4, TECH_MAGNET = 4) + var/can_use = 1 + var/obj/effect/dummy/chameleon/active_dummy = null + var/saved_item = /obj/item/trash/cigbutt + var/saved_icon = 'icons/inventory/face/item.dmi' + var/saved_icon_state = "cigbutt" + var/saved_overlays + +/obj/item/device/chameleon/dropped() + disrupt() + ..() + +/obj/item/device/chameleon/equipped() + ..() + disrupt() + ..() + +/obj/item/device/chameleon/attack_self() + toggle() + +/obj/item/device/chameleon/afterattack(atom/target, mob/user , proximity) + if(!proximity) return + if(!active_dummy) + if(istype(target,/obj/item) && !istype(target, /obj/item/weapon/disk/nuclear)) + playsound(src, 'sound/weapons/flash.ogg', 100, 1, -6) + to_chat(user, "Scanned [target].") + saved_item = target.type + saved_icon = target.icon + saved_icon_state = target.icon_state + saved_overlays = target.overlays + +/obj/item/device/chameleon/proc/toggle() + if(!can_use || !saved_item) return + if(active_dummy) + eject_all() + playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) + qdel(active_dummy) + active_dummy = null + to_chat(usr, "You deactivate the [src].") + var/obj/effect/overlay/T = new /obj/effect/overlay(get_turf(src)) + T.icon = 'icons/effects/effects.dmi' + flick("emppulse",T) + spawn(8) qdel(T) + else + playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) + var/obj/O = new saved_item(src) + if(!O) return + var/obj/effect/dummy/chameleon/C = new /obj/effect/dummy/chameleon(usr.loc) + C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, src) + qdel(O) + to_chat(usr, "You activate the [src].") + var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) + T.icon = 'icons/effects/effects.dmi' + flick("emppulse",T) + spawn(8) qdel(T) + +/obj/item/device/chameleon/proc/disrupt(var/delete_dummy = 1) + if(active_dummy) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + spark_system.start() + eject_all() + if(delete_dummy) + qdel(active_dummy) + active_dummy = null + can_use = 0 + spawn(50) can_use = 1 + +/obj/item/device/chameleon/proc/eject_all() + for(var/atom/movable/A in active_dummy) + A.loc = active_dummy.loc + if(ismob(A)) + var/mob/M = A + M.reset_view(null) + +/obj/effect/dummy/chameleon + name = "" + desc = "" + density = FALSE + anchored = TRUE + var/can_move = 1 + var/obj/item/device/chameleon/master = null + +/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, var/obj/item/device/chameleon/C) + name = O.name + desc = O.desc + icon = new_icon + icon_state = new_iconstate + overlays = new_overlays + set_dir(O.dir) + M.loc = src + master = C + master.active_dummy = src + +/obj/effect/dummy/chameleon/attackby() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/attack_hand() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/ex_act() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/bullet_act() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + ..() + master.disrupt() + +/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) + if(istype(loc, /turf/space)) return //No magical space movement! + + if(can_move) + can_move = 0 + switch(user.bodytemperature) + if(300 to INFINITY) + spawn(10) can_move = 1 + if(295 to 300) + spawn(13) can_move = 1 + if(280 to 295) + spawn(16) can_move = 1 + if(260 to 280) + spawn(20) can_move = 1 + else + spawn(25) can_move = 1 + step(src, direction) + return + +/obj/effect/dummy/chameleon/Destroy() + master.disrupt(0) + ..() diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm index 7eafc0fe052..d4fb3ff04ab 100644 --- a/code/game/objects/items/devices/communicator/communicator.dm +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -376,3 +376,13 @@ var/global/list/obj/item/device/communicator/all_communicators = list() return icon_state = initial(icon_state) + +#undef HOMETAB +#undef PHONTAB +#undef CONTTAB +#undef MESSTAB +#undef NEWSTAB +#undef NOTETAB +#undef WTHRTAB +#undef MANITAB +#undef SETTTAB diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm index 8d169f62c1d..cc00aef6121 100644 --- a/code/game/objects/items/devices/communicator/helper.dm +++ b/code/game/objects/items/devices/communicator/helper.dm @@ -1,97 +1,97 @@ -/obj/item/device/communicator/proc/analyze_air() - var/list/results = list() - var/turf/T = get_turf(src.loc) - if(!isnull(T)) - var/datum/gas_mixture/environment = T.return_air() - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles - if (total_moles) - var/o2_level = environment.gas["oxygen"]/total_moles - var/n2_level = environment.gas["nitrogen"]/total_moles - var/co2_level = environment.gas["carbon_dioxide"]/total_moles - var/phoron_level = environment.gas["phoron"]/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) - - // Label is what the entry is describing - // Type identifies which unit or other special characters to use - // Val is the information reported - // Bad_high/_low are the values outside of which the entry reports as dangerous - // Poor_high/_low are the values outside of which the entry reports as unideal - // Values were extracted from the template itself - results = list( - list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), - list("entry" = "Temperature", "units" = "\u00B0" + "C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), - list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), - list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), - list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) - ) - - if(isnull(results)) - results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) - return results - - -// Proc - compile_news() -// Parameters - none -// Description - Returns the list of newsfeeds, compiled for template processing -/obj/item/device/communicator/proc/compile_news() - var/list/feeds = list() - for(var/datum/feed_channel/channel in news_network.network_channels) - var/list/messages = list() - if(!channel.censored && channel.channel_name != "Vir News Network") //Do not load the 'IC news' channel as it is simply too long. - var/index = 0 - for(var/datum/feed_message/FM in channel.messages) - index++ - var/list/msgdata = list( - "author" = FM.author, - "body" = FM.body, - "img" = null, - "message_type" = FM.message_type, - "time_stamp" = FM.time_stamp, - "caption" = FM.caption, - "index" = index - ) - if(FM.img) - msgdata["img"] = icon2base64(FM.img) - messages[++messages.len] = msgdata - - feeds[++feeds.len] = list( - "name" = channel.channel_name, - "censored" = channel.censored, - "author" = channel.author, - "messages" = messages, - "index" = feeds.len + 1 // actually align them, since I guess the population of the list doesn't occur until after the evaluation of the new entry's contents - ) - return feeds - -// Proc - get_recent_news() -// Parameters - none -// Description - Returns the latest three newscasts, compiled for template processing -/obj/item/device/communicator/proc/get_recent_news() - var/list/news = list() - - // Compile all the newscasts - for(var/datum/feed_channel/channel in news_network.network_channels) - if(!channel.censored) - for(var/datum/feed_message/FM in channel.messages) - var/body = replacetext(FM.body, "\n", "
                    ") - news[++news.len] = list( - "channel" = channel.channel_name, - "author" = FM.author, - "body" = body, - "message_type" = FM.message_type, - "time_stamp" = FM.time_stamp, - "has_image" = (FM.img != null), - "caption" = FM.caption, - "time" = FM.post_time - ) - - // Cut out all but the youngest three - if(news.len > 3) - sortByKey(news, "time") - news.Cut(1, news.len - 2) // Last three have largest timestamps, youngest posts - news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending - - return news +/obj/item/device/communicator/proc/analyze_air() + var/list/results = list() + var/turf/T = get_turf(src.loc) + if(!isnull(T)) + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles + if (total_moles) + var/o2_level = environment.gas["oxygen"]/total_moles + var/n2_level = environment.gas["nitrogen"]/total_moles + var/co2_level = environment.gas["carbon_dioxide"]/total_moles + var/phoron_level = environment.gas["phoron"]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) + + // Label is what the entry is describing + // Type identifies which unit or other special characters to use + // Val is the information reported + // Bad_high/_low are the values outside of which the entry reports as dangerous + // Poor_high/_low are the values outside of which the entry reports as unideal + // Values were extracted from the template itself + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "\u00B0" + "C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + return results + + +// Proc - compile_news() +// Parameters - none +// Description - Returns the list of newsfeeds, compiled for template processing +/obj/item/device/communicator/proc/compile_news() + var/list/feeds = list() + for(var/datum/feed_channel/channel in news_network.network_channels) + var/list/messages = list() + if(!channel.censored && channel.channel_name != "Vir News Network") //Do not load the 'IC news' channel as it is simply too long. + var/index = 0 + for(var/datum/feed_message/FM in channel.messages) + index++ + var/list/msgdata = list( + "author" = FM.author, + "body" = FM.body, + "img" = null, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "caption" = FM.caption, + "index" = index + ) + if(FM.img) + msgdata["img"] = icon2base64(FM.img) + messages[++messages.len] = msgdata + + feeds[++feeds.len] = list( + "name" = channel.channel_name, + "censored" = channel.censored, + "author" = channel.author, + "messages" = messages, + "index" = feeds.len + 1 // actually align them, since I guess the population of the list doesn't occur until after the evaluation of the new entry's contents + ) + return feeds + +// Proc - get_recent_news() +// Parameters - none +// Description - Returns the latest three newscasts, compiled for template processing +/obj/item/device/communicator/proc/get_recent_news() + var/list/news = list() + + // Compile all the newscasts + for(var/datum/feed_channel/channel in news_network.network_channels) + if(!channel.censored) + for(var/datum/feed_message/FM in channel.messages) + var/body = replacetext(FM.body, "\n", "
                    ") + news[++news.len] = list( + "channel" = channel.channel_name, + "author" = FM.author, + "body" = body, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "has_image" = (FM.img != null), + "caption" = FM.caption, + "time" = FM.post_time + ) + + // Cut out all but the youngest three + if(news.len > 3) + sortByKey(news, "time") + news.Cut(1, news.len - 2) // Last three have largest timestamps, youngest posts + news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending + + return news diff --git a/code/game/objects/items/devices/communicator/messaging.dm b/code/game/objects/items/devices/communicator/messaging.dm index e5a27902b1b..2f9249dd5dc 100644 --- a/code/game/objects/items/devices/communicator/messaging.dm +++ b/code/game/objects/items/devices/communicator/messaging.dm @@ -1,185 +1,185 @@ -// Proc: receive_exonet_message() -// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received, -// text - message text to send if message is of type "text") -// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function. -/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text) - if(message == "voice") - if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator)) - if(origin_atom in voice_invites) - var/user = null - if(ismob(origin_atom.loc)) - user = origin_atom.loc - open_connection(user, origin_atom) - return - else if(origin_atom in voice_requests) - return //Spam prevention - else - request(origin_atom) - if(message == "ping") - if(network_visibility) - var/random = rand(200,350) - random = random / 10 - exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") - if(message == "text") - request_im(origin_atom, origin_address, text) - return - -// Proc: receive_exonet_message() -// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received) -// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response. -/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text) - if(message == "voice") - if(istype(origin_atom, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = origin_atom - if(src in comm.voice_invites) - comm.open_connection(src) - return - to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ - verb, and select that name to answer the call.") - src << 'sound/machines/defib_SafetyOn.ogg' - comm.voice_invites |= src - if(message == "ping") - if(client && client.prefs.communicator_visibility) - var/random = rand(450,700) - random = random / 10 - exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") - if(message == "text") - to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Received text message from [origin_atom]: \"[text]\"") - src << 'sound/machines/defib_safetyOff.ogg' - exonet_messages.Add("From [origin_atom]:
                    [text]") - return - -// Proc: request_im() -// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message) -// Description: Response to a communicator trying to message the device. -// Adds them to the list of people that have messaged this device and adds the message to the message list. -/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text) - var/who = null - if(isobserver(candidate)) - var/mob/observer/dead/ghost = candidate - who = ghost.name - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - who = comm.owner - comm.im_contacts |= src - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else if(istype(candidate, /obj/item/integrated_circuit)) - var/obj/item/integrated_circuit/CIRC = candidate - who = CIRC - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else return - - im_contacts |= candidate - - if(!who) - return - - if(ringer) - var/S - if(ttone in ttone_sound) - S = ttone_sound[ttone] - else - S = 'sound/machines/twobeep.ogg' - - playsound(src, S, 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src][bicon(src)] *[ttone]*")) - - alert_called = 1 - update_icon() - - //Search for holder of the device. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - - if(L) - to_chat(L, "\icon[src][bicon(src)] Message from [who]: \"[text]\" (Reply)") - -// This is the only Topic the communicators really uses -/obj/item/device/communicator/Topic(href, href_list) - switch(href_list["action"]) - if("Reply") - var/obj/item/device/communicator/comm = locate(href_list["target"]) - var/message = tgui_input_text(usr, "Enter your message below.", "Reply") - - if(message) - exonet.send_message(comm.exonet.address, "text", message) - im_list += list(list("address" = exonet.address, "to_address" = comm.exonet.address, "im" = message)) - log_pda("(COMM: [src]) sent \"[message]\" to [exonet.get_atom_from_address(comm.exonet.address)]", usr) - to_chat(usr, "\icon[src][bicon(src)] Sent message to [istype(comm, /obj/item/device/communicator) ? comm.owner : comm.name], \"[message]\" (Reply)") - -// Verb: text_communicator() -// Parameters: None -// Description: Allows a ghost to send a text message to a communicator. -/mob/observer/dead/verb/text_communicator() - set category = "Ghost" - set name = "Text Communicator" - set desc = "If there is a communicator available, send a text message to it." - - if(ticker.current_state < GAME_STATE_PLAYING) - to_chat(src, "The game hasn't started yet!") - return - - if (!src.stat) - return - - if (usr != src) - return //something is terribly wrong - - for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. - if(src.client.prefs.real_name == L.real_name) - to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") - return - - var/obj/machinery/exonet_node/E = get_exonet_node() - if(!E || !E.on || !E.allow_external_communicators) - to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ - so your call can't go through.") - return - - var/list/choices = list() - for(var/obj/item/device/communicator/comm in all_communicators) - if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) - continue - choices.Add(comm) - - if(!choices.len) - to_chat(src, "There are no available communicators, sorry.") - return - - var/choice = tgui_input_list(src,"Send a text message to whom?", "Recipient Choice", choices) - if(choice) - var/obj/item/device/communicator/chosen_communicator = choice - var/mob/observer/dead/O = src - var/text_message = sanitize(tgui_input_text(src, "What do you want the message to say?", multiline = TRUE)) - if(text_message && O.exonet) - O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message) - - to_chat(src, "You have sent '[text_message]' to [chosen_communicator].") - exonet_messages.Add("To [chosen_communicator]:
                    [text_message]") - log_pda("(DCOMM: [src]) sent \"[text_message]\" to [chosen_communicator]", src) - for(var/mob/M in player_list) - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) - continue - if(M == src) - continue - M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]") - - - -// Verb: show_text_messages() -// Parameters: None -// Description: Lets ghosts review messages they've sent or received. -/mob/observer/dead/verb/show_text_messages() - set category = "Ghost" - set name = "Show Text Messages" - set desc = "Allows you to see exonet text messages you've sent and received." - - var/HTML = "Exonet Message Log" - for(var/line in exonet_messages) - HTML += line + "
                    " - HTML +="" - usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") +// Proc: receive_exonet_message() +// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received, +// text - message text to send if message is of type "text") +// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function. +/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text) + if(message == "voice") + if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator)) + if(origin_atom in voice_invites) + var/user = null + if(ismob(origin_atom.loc)) + user = origin_atom.loc + open_connection(user, origin_atom) + return + else if(origin_atom in voice_requests) + return //Spam prevention + else + request(origin_atom) + if(message == "ping") + if(network_visibility) + var/random = rand(200,350) + random = random / 10 + exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") + if(message == "text") + request_im(origin_atom, origin_address, text) + return + +// Proc: receive_exonet_message() +// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received) +// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response. +/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text) + if(message == "voice") + if(istype(origin_atom, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = origin_atom + if(src in comm.voice_invites) + comm.open_connection(src) + return + to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ + verb, and select that name to answer the call.") + src << 'sound/machines/defib_SafetyOn.ogg' + comm.voice_invites |= src + if(message == "ping") + if(client && client.prefs.communicator_visibility) + var/random = rand(450,700) + random = random / 10 + exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") + if(message == "text") + to_chat(src, "\icon[origin_atom][bicon(origin_atom)] Received text message from [origin_atom]: \"[text]\"") + src << 'sound/machines/defib_safetyOff.ogg' + exonet_messages.Add("From [origin_atom]:
                    [text]") + return + +// Proc: request_im() +// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message) +// Description: Response to a communicator trying to message the device. +// Adds them to the list of people that have messaged this device and adds the message to the message list. +/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text) + var/who = null + if(isobserver(candidate)) + var/mob/observer/dead/ghost = candidate + who = ghost.name + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + who = comm.owner + comm.im_contacts |= src + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else if(istype(candidate, /obj/item/integrated_circuit)) + var/obj/item/integrated_circuit/CIRC = candidate + who = CIRC + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else return + + im_contacts |= candidate + + if(!who) + return + + if(ringer) + var/S + if(ttone in ttone_sound) + S = ttone_sound[ttone] + else + S = 'sound/machines/twobeep.ogg' + + playsound(src, S, 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src][bicon(src)] *[ttone]*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + to_chat(L, "\icon[src][bicon(src)] Message from [who]: \"[text]\" (Reply)") + +// This is the only Topic the communicators really uses +/obj/item/device/communicator/Topic(href, href_list) + switch(href_list["action"]) + if("Reply") + var/obj/item/device/communicator/comm = locate(href_list["target"]) + var/message = tgui_input_text(usr, "Enter your message below.", "Reply") + + if(message) + exonet.send_message(comm.exonet.address, "text", message) + im_list += list(list("address" = exonet.address, "to_address" = comm.exonet.address, "im" = message)) + log_pda("(COMM: [src]) sent \"[message]\" to [exonet.get_atom_from_address(comm.exonet.address)]", usr) + to_chat(usr, "\icon[src][bicon(src)] Sent message to [istype(comm, /obj/item/device/communicator) ? comm.owner : comm.name], \"[message]\" (Reply)") + +// Verb: text_communicator() +// Parameters: None +// Description: Allows a ghost to send a text message to a communicator. +/mob/observer/dead/verb/text_communicator() + set category = "Ghost" + set name = "Text Communicator" + set desc = "If there is a communicator available, send a text message to it." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") + return + + var/obj/machinery/exonet_node/E = get_exonet_node() + if(!E || !E.on || !E.allow_external_communicators) + to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ + so your call can't go through.") + return + + var/list/choices = list() + for(var/obj/item/device/communicator/comm in all_communicators) + if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) + continue + choices.Add(comm) + + if(!choices.len) + to_chat(src, "There are no available communicators, sorry.") + return + + var/choice = tgui_input_list(src,"Send a text message to whom?", "Recipient Choice", choices) + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + var/mob/observer/dead/O = src + var/text_message = sanitize(tgui_input_text(src, "What do you want the message to say?", multiline = TRUE)) + if(text_message && O.exonet) + O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message) + + to_chat(src, "You have sent '[text_message]' to [chosen_communicator].") + exonet_messages.Add("To [chosen_communicator]:
                    [text_message]") + log_pda("(DCOMM: [src]) sent \"[text_message]\" to [chosen_communicator]", src) + for(var/mob/M in player_list) + if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) + continue + if(M == src) + continue + M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]") + + + +// Verb: show_text_messages() +// Parameters: None +// Description: Lets ghosts review messages they've sent or received. +/mob/observer/dead/verb/show_text_messages() + set category = "Ghost" + set name = "Show Text Messages" + set desc = "Allows you to see exonet text messages you've sent and received." + + var/HTML = "Exonet Message Log" + for(var/line in exonet_messages) + HTML += line + "
                    " + HTML +="" + usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm index eafd3105e0b..bbf91d1ead1 100644 --- a/code/game/objects/items/devices/communicator/phone.dm +++ b/code/game/objects/items/devices/communicator/phone.dm @@ -1,366 +1,366 @@ -// Proc: add_communicating() -// Parameters: 1 (comm - the communicator to add to communicating) -// Description: Used when this communicator gets a new communicator to relay say/me messages to -/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm) - if(!comm || !istype(comm)) return - - communicating |= comm - listening_objects |= src - update_icon() - -// Proc: del_communicating() -// Parameters: 1 (comm - the communicator to remove from communicating) -// Description: Used when this communicator is being asked to stop relaying say/me messages to another -/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm) - if(!comm || !istype(comm)) return - - communicating.Remove(comm) - update_icon() - -// Proc: open_connection() -// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device) -// Description: Typechecks the candidate, then calls the correct proc for further connecting. -/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate) - if(isobserver(candidate)) - voice_invites.Remove(candidate) - open_connection_to_ghost(user, candidate) - else - if(istype(candidate, /obj/item/device/communicator)) - open_connection_to_communicator(user, candidate) - -// Proc: open_connection_to_communicator() -// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator) -// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed. -/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate) - if(!istype(candidate, /obj/item/device/communicator)) - return - var/obj/item/device/communicator/comm = candidate - voice_invites.Remove(candidate) - comm.voice_requests.Remove(src) - - if(user) - comm.visible_message("\icon[src][bicon(src)] Connecting to [src].") - to_chat(user, "\icon[src][bicon(src)] Attempting to call [comm].") - sleep(10) - to_chat(user, "\icon[src][bicon(src)] Dialing internally from [station_name()], [system_name()].") - sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. - to_chat(user, "\icon[src][bicon(src)] Connection re-routed to [comm] at [comm.exonet.address].") - sleep(40) - to_chat(user, "\icon[src][bicon(src)] Connection to [comm] at [comm.exonet.address] established.") - comm.visible_message("\icon[src][bicon(src)] Connection to [src] at [exonet.address] established.") - sleep(20) - - src.add_communicating(comm) - comm.add_communicating(src) - -// Proc: open_connection_to_ghost() -// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob) -// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client. -/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate) - if(!isobserver(candidate)) - return - //Handle moving the ghost into the new shell. - announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") - voice_requests.Remove(candidate) - voice_invites.Remove(candidate) - var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. - new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. - //Do some simple logging since this is a tad risky as a concept. - var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ - [user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]." - message_admins(msg) - log_game(msg) - new_voice.mind = candidate.mind //Transfer the mind, if any. - new_voice.ckey = candidate.ckey //Finally, bring the client over. - voice_mobs.Add(new_voice) - listening_objects |= src - - var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator. - blackness.screen_loc = ui_entire_screen - blackness.icon = 'icons/effects/effects.dmi' - blackness.icon_state = "1" - blackness.mouse_opacity = 2 //Can't see anything! - new_voice.client.screen.Add(blackness) - - update_icon() - - //Now for some connection fluff. - if(user) - to_chat(user, "\icon[src][bicon(src)] Connecting to [candidate].") - to_chat(new_voice, "\icon[src][bicon(src)] Attempting to call [src].") - sleep(10) - to_chat(new_voice, "\icon[src][bicon(src)] Dialing to [station_name()], Kara Subsystem, [system_name()].") - sleep(20) - to_chat(new_voice, "\icon[src][bicon(src)] Connecting to [station_name()] telecommunications array.") - sleep(40) - to_chat(new_voice, "\icon[src][bicon(src)] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") - sleep(20) - - //We're connected, no need to hide everything. - new_voice.client.screen.Remove(blackness) - qdel(blackness) - - to_chat(new_voice, "\icon[src][bicon(src)] Connection to [src] established.") - to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.") - to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.") - to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!") - if(new_voice.mind) - new_voice.mind.assigned_role = "Disembodied Voice" - if(user) - to_chat(user, "\icon[src][bicon(src)] Your communicator is now connected to [candidate]'s communicator.") - -// Proc: close_connection() -// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected) -// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators -// and voice mobs are removed. -/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason) - if(voice_mobs.len == 0 && communicating.len == 0) - return - - for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers - if(target && voice != target) //If no target is inputted, it deletes all of them. - continue - to_chat(voice, "\icon[src][bicon(src)] [reason].") - visible_message("\icon[src][bicon(src)] [reason].") - voice_mobs.Remove(voice) - qdel(voice) - update_icon() - - for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators. - if(target && comm != target) - continue - src.del_communicating(comm) - comm.del_communicating(src) - comm.visible_message("\icon[src][bicon(src)] [reason].") - visible_message("\icon[src][bicon(src)] [reason].") - if(comm.camera && video_source == comm.camera) //We hung up on the person on video - end_video() - if(camera && comm.video_source == camera) //We hung up on them while they were watching us - comm.end_video() - - if(voice_mobs.len == 0 && communicating.len == 0) - listening_objects.Remove(src) - -// Proc: request() -// Parameters: 1 (candidate - the ghost or communicator wanting to call the device) -// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters -/obj/item/device/communicator/proc/request(var/atom/candidate) - if(candidate in voice_requests) - return - var/who = null - if(isobserver(candidate)) - who = candidate.name - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - who = comm.owner - comm.voice_invites |= src - - if(!who) - return - - voice_requests |= candidate - - if(ringer) - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src][bicon(src)] *beep*")) - - alert_called = 1 - update_icon() - - //Search for holder of the device. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - - if(L) - to_chat(L, "\icon[src][bicon(src)] Communications request from [who].") - -// Proc: del_request() -// Parameters: 1 (candidate - the ghost or communicator to be declined) -// Description: Declines a request and cleans up both ends -/obj/item/device/communicator/proc/del_request(var/atom/candidate) - if(!(candidate in voice_requests)) - return - - if(isobserver(candidate)) - to_chat(candidate, "Your communicator call request was declined.") - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - comm.voice_invites -= src - - voice_requests -= candidate - - //Search for holder of our device. - var/mob/living/us = null - if(loc && isliving(loc)) - us = loc - - if(us) - to_chat(us, "\icon[src][bicon(src)] Declined request.") - -// Proc: see_emote() -// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents) -// Description: Relays the emote to all linked communicators. -/obj/item/device/communicator/see_emote(mob/living/M, text) - var/rendered = "\icon[src][bicon(src)] [text]" - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - //VOREStation Edit Start for commlinks - var/list/mobs_to_relay - if(istype(comm,/obj/item/device/communicator/commlink)) - var/obj/item/device/communicator/commlink/CL = comm - mobs_to_relay = list(CL.nif.human) - else - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - mobs_to_relay = in_range["mobs"] - //VOREStation Edit End - - for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. - var/dst = get_dist(get_turf(mob),get_turf(comm)) - if(dst <= video_range) - mob.show_message(rendered) - else - to_chat(mob, "You can barely see some movement on \the [src]'s display.") - - ..() - -// Proc: hear_talk() -// Parameters: 3 (M - the mob the speech originated from, -// list/message_pieces - what is being said w/ baked languages, -// verb - the word used to describe how text is being said) -// Description: Relays the speech to all linked communicators. -/obj/item/device/communicator/hear_talk(mob/M, list/message_pieces, verb) - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - //VOREStation Edit Start for commlinks - var/list/mobs_to_relay - if(istype(comm,/obj/item/device/communicator/commlink)) - var/obj/item/device/communicator/commlink/CL = comm - mobs_to_relay = list(CL.nif.human) - else - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - mobs_to_relay = in_range["mobs"] - //VOREStation Edit End - - for(var/mob/mob in mobs_to_relay) - var/list/combined = mob.combine_message(message_pieces, verb, M) - var/message = combined["formatted"] - var/name_used = M.GetVoice() - var/rendered = null - rendered = "\icon[src][bicon(src)] [name_used] [message]" - mob.show_message(rendered, 2) - -// Proc: show_message() -// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown) -// Description: Relays the message to all linked communicators. -/obj/item/device/communicator/show_message(msg, type, alt, alt_type) - var/rendered = "\icon[src][bicon(src)] [msg]" - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) - var/list/mobs_to_relay = in_range["mobs"] - - for(var/mob/mob in mobs_to_relay) - mob.show_message(rendered) - ..() - -// Verb: join_as_voice() -// Parameters: None -// Description: Allows ghosts to call communicators, if they meet all the requirements. -/mob/observer/dead/verb/join_as_voice() - set category = "Ghost" - set name = "Call Communicator" - set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." - - if(ticker.current_state < GAME_STATE_PLAYING) - to_chat(src, "The game hasn't started yet!") - return - - if (!src.stat) - return - - if (usr != src) - return //something is terribly wrong - - var/confirm = tgui_alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? This will reset your respawn timer, if someone answers.", "Join as Voice?", list("Yes","No")) - if(confirm == "No") - return - - if(config.antag_hud_restricted && has_enabled_antagHUD == 1) - to_chat(src, "You have used the antagHUD and cannot respawn or use communicators!") - return - - for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. - if(src.client.prefs.real_name == L.real_name) - to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") - return - - var/obj/machinery/exonet_node/E = get_exonet_node() - if(!E || !E.on || !E.allow_external_communicators) - to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ - so your call can't go through.") - return - - var/list/choices = list() - for(var/obj/item/device/communicator/comm in all_communicators) - if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) - continue - choices.Add(comm) - - if(!choices.len) - to_chat(src , "There are no available communicators, sorry.") - return - - var/choice = tgui_input_list(src,"Send a voice request to whom?", "Recipient Choice", choices) - if(choice) - var/obj/item/device/communicator/chosen_communicator = choice - var/mob/observer/dead/O = src - if(O.exonet) - O.exonet.send_message(chosen_communicator.exonet.address, "voice") - - to_chat(src, "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers.") - -// Proc: connect_video() -// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end -// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon -/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm) - if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video - - if(video_source) //Already in a video - to_chat(user, "You are already connected to a video call!") - return - - if(user.blinded) //User is blinded - to_chat(user, "You cannot see well enough to do that!") - return - - if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something - to_chat(user, "\icon[src][bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") - return - - to_chat(user, "\icon[src][bicon(src)] Attempting to start video over existing call.") - sleep(30) - to_chat(user, "\icon[src][bicon(src)] Please wait...") - - video_source = comm.camera - comm.visible_message("\icon[src][bicon(src)] New video connection from [comm].") - update_active_camera_screen() - GLOB.moved_event.register(video_source, src, PROC_REF(update_active_camera_screen)) - update_icon() - -// Proc: end_video() -// Parameters: reason - the text reason to print for why it ended -// Description: Ends the video call by clearing video_source -/obj/item/device/communicator/proc/end_video(var/reason) - GLOB.moved_event.unregister(video_source, src, PROC_REF(update_active_camera_screen)) - show_static() - video_source = null - - . = "\icon[src][bicon(src)] [reason ? reason : "Video session ended"]." - - visible_message(.) - update_icon() +// Proc: add_communicating() +// Parameters: 1 (comm - the communicator to add to communicating) +// Description: Used when this communicator gets a new communicator to relay say/me messages to +/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm) + if(!comm || !istype(comm)) return + + communicating |= comm + listening_objects |= src + update_icon() + +// Proc: del_communicating() +// Parameters: 1 (comm - the communicator to remove from communicating) +// Description: Used when this communicator is being asked to stop relaying say/me messages to another +/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm) + if(!comm || !istype(comm)) return + + communicating.Remove(comm) + update_icon() + +// Proc: open_connection() +// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device) +// Description: Typechecks the candidate, then calls the correct proc for further connecting. +/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate) + if(isobserver(candidate)) + voice_invites.Remove(candidate) + open_connection_to_ghost(user, candidate) + else + if(istype(candidate, /obj/item/device/communicator)) + open_connection_to_communicator(user, candidate) + +// Proc: open_connection_to_communicator() +// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator) +// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed. +/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate) + if(!istype(candidate, /obj/item/device/communicator)) + return + var/obj/item/device/communicator/comm = candidate + voice_invites.Remove(candidate) + comm.voice_requests.Remove(src) + + if(user) + comm.visible_message("\icon[src][bicon(src)] Connecting to [src].") + to_chat(user, "\icon[src][bicon(src)] Attempting to call [comm].") + sleep(10) + to_chat(user, "\icon[src][bicon(src)] Dialing internally from [station_name()], [system_name()].") + sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. + to_chat(user, "\icon[src][bicon(src)] Connection re-routed to [comm] at [comm.exonet.address].") + sleep(40) + to_chat(user, "\icon[src][bicon(src)] Connection to [comm] at [comm.exonet.address] established.") + comm.visible_message("\icon[src][bicon(src)] Connection to [src] at [exonet.address] established.") + sleep(20) + + src.add_communicating(comm) + comm.add_communicating(src) + +// Proc: open_connection_to_ghost() +// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob) +// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client. +/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate) + if(!isobserver(candidate)) + return + //Handle moving the ghost into the new shell. + announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") + voice_requests.Remove(candidate) + voice_invites.Remove(candidate) + var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. + new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. + //Do some simple logging since this is a tad risky as a concept. + var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ + [user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]." + message_admins(msg) + log_game(msg) + new_voice.mind = candidate.mind //Transfer the mind, if any. + new_voice.ckey = candidate.ckey //Finally, bring the client over. + voice_mobs.Add(new_voice) + listening_objects |= src + + var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator. + blackness.screen_loc = ui_entire_screen + blackness.icon = 'icons/effects/effects.dmi' + blackness.icon_state = "1" + blackness.mouse_opacity = 2 //Can't see anything! + new_voice.client.screen.Add(blackness) + + update_icon() + + //Now for some connection fluff. + if(user) + to_chat(user, "\icon[src][bicon(src)] Connecting to [candidate].") + to_chat(new_voice, "\icon[src][bicon(src)] Attempting to call [src].") + sleep(10) + to_chat(new_voice, "\icon[src][bicon(src)] Dialing to [station_name()], Kara Subsystem, [system_name()].") + sleep(20) + to_chat(new_voice, "\icon[src][bicon(src)] Connecting to [station_name()] telecommunications array.") + sleep(40) + to_chat(new_voice, "\icon[src][bicon(src)] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") + sleep(20) + + //We're connected, no need to hide everything. + new_voice.client.screen.Remove(blackness) + qdel(blackness) + + to_chat(new_voice, "\icon[src][bicon(src)] Connection to [src] established.") + to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.") + to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.") + to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!") + if(new_voice.mind) + new_voice.mind.assigned_role = "Disembodied Voice" + if(user) + to_chat(user, "\icon[src][bicon(src)] Your communicator is now connected to [candidate]'s communicator.") + +// Proc: close_connection() +// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected) +// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators +// and voice mobs are removed. +/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason) + if(voice_mobs.len == 0 && communicating.len == 0) + return + + for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers + if(target && voice != target) //If no target is inputted, it deletes all of them. + continue + to_chat(voice, "\icon[src][bicon(src)] [reason].") + visible_message("\icon[src][bicon(src)] [reason].") + voice_mobs.Remove(voice) + qdel(voice) + update_icon() + + for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators. + if(target && comm != target) + continue + src.del_communicating(comm) + comm.del_communicating(src) + comm.visible_message("\icon[src][bicon(src)] [reason].") + visible_message("\icon[src][bicon(src)] [reason].") + if(comm.camera && video_source == comm.camera) //We hung up on the person on video + end_video() + if(camera && comm.video_source == camera) //We hung up on them while they were watching us + comm.end_video() + + if(voice_mobs.len == 0 && communicating.len == 0) + listening_objects.Remove(src) + +// Proc: request() +// Parameters: 1 (candidate - the ghost or communicator wanting to call the device) +// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters +/obj/item/device/communicator/proc/request(var/atom/candidate) + if(candidate in voice_requests) + return + var/who = null + if(isobserver(candidate)) + who = candidate.name + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + who = comm.owner + comm.voice_invites |= src + + if(!who) + return + + voice_requests |= candidate + + if(ringer) + playsound(src, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src][bicon(src)] *beep*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + to_chat(L, "\icon[src][bicon(src)] Communications request from [who].") + +// Proc: del_request() +// Parameters: 1 (candidate - the ghost or communicator to be declined) +// Description: Declines a request and cleans up both ends +/obj/item/device/communicator/proc/del_request(var/atom/candidate) + if(!(candidate in voice_requests)) + return + + if(isobserver(candidate)) + to_chat(candidate, "Your communicator call request was declined.") + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + comm.voice_invites -= src + + voice_requests -= candidate + + //Search for holder of our device. + var/mob/living/us = null + if(loc && isliving(loc)) + us = loc + + if(us) + to_chat(us, "\icon[src][bicon(src)] Declined request.") + +// Proc: see_emote() +// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents) +// Description: Relays the emote to all linked communicators. +/obj/item/device/communicator/see_emote(mob/living/M, text) + var/rendered = "\icon[src][bicon(src)] [text]" + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + //VOREStation Edit Start for commlinks + var/list/mobs_to_relay + if(istype(comm,/obj/item/device/communicator/commlink)) + var/obj/item/device/communicator/commlink/CL = comm + mobs_to_relay = list(CL.nif.human) + else + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + mobs_to_relay = in_range["mobs"] + //VOREStation Edit End + + for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. + var/dst = get_dist(get_turf(mob),get_turf(comm)) + if(dst <= video_range) + mob.show_message(rendered) + else + to_chat(mob, "You can barely see some movement on \the [src]'s display.") + + ..() + +// Proc: hear_talk() +// Parameters: 3 (M - the mob the speech originated from, +// list/message_pieces - what is being said w/ baked languages, +// verb - the word used to describe how text is being said) +// Description: Relays the speech to all linked communicators. +/obj/item/device/communicator/hear_talk(mob/M, list/message_pieces, verb) + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + //VOREStation Edit Start for commlinks + var/list/mobs_to_relay + if(istype(comm,/obj/item/device/communicator/commlink)) + var/obj/item/device/communicator/commlink/CL = comm + mobs_to_relay = list(CL.nif.human) + else + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + mobs_to_relay = in_range["mobs"] + //VOREStation Edit End + + for(var/mob/mob in mobs_to_relay) + var/list/combined = mob.combine_message(message_pieces, verb, M) + var/message = combined["formatted"] + var/name_used = M.GetVoice() + var/rendered = null + rendered = "\icon[src][bicon(src)] [name_used] [message]" + mob.show_message(rendered, 2) + +// Proc: show_message() +// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown) +// Description: Relays the message to all linked communicators. +/obj/item/device/communicator/show_message(msg, type, alt, alt_type) + var/rendered = "\icon[src][bicon(src)] [msg]" + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) + var/list/mobs_to_relay = in_range["mobs"] + + for(var/mob/mob in mobs_to_relay) + mob.show_message(rendered) + ..() + +// Verb: join_as_voice() +// Parameters: None +// Description: Allows ghosts to call communicators, if they meet all the requirements. +/mob/observer/dead/verb/join_as_voice() + set category = "Ghost" + set name = "Call Communicator" + set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + var/confirm = tgui_alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? This will reset your respawn timer, if someone answers.", "Join as Voice?", list("Yes","No")) + if(confirm == "No") + return + + if(config.antag_hud_restricted && has_enabled_antagHUD == 1) + to_chat(src, "You have used the antagHUD and cannot respawn or use communicators!") + return + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") + return + + var/obj/machinery/exonet_node/E = get_exonet_node() + if(!E || !E.on || !E.allow_external_communicators) + to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ + so your call can't go through.") + return + + var/list/choices = list() + for(var/obj/item/device/communicator/comm in all_communicators) + if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) + continue + choices.Add(comm) + + if(!choices.len) + to_chat(src , "There are no available communicators, sorry.") + return + + var/choice = tgui_input_list(src,"Send a voice request to whom?", "Recipient Choice", choices) + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + var/mob/observer/dead/O = src + if(O.exonet) + O.exonet.send_message(chosen_communicator.exonet.address, "voice") + + to_chat(src, "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers.") + +// Proc: connect_video() +// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end +// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon +/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm) + if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video + + if(video_source) //Already in a video + to_chat(user, "You are already connected to a video call!") + return + + if(user.blinded) //User is blinded + to_chat(user, "You cannot see well enough to do that!") + return + + if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something + to_chat(user, "\icon[src][bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") + return + + to_chat(user, "\icon[src][bicon(src)] Attempting to start video over existing call.") + sleep(30) + to_chat(user, "\icon[src][bicon(src)] Please wait...") + + video_source = comm.camera + comm.visible_message("\icon[src][bicon(src)] New video connection from [comm].") + update_active_camera_screen() + GLOB.moved_event.register(video_source, src, PROC_REF(update_active_camera_screen)) + update_icon() + +// Proc: end_video() +// Parameters: reason - the text reason to print for why it ended +// Description: Ends the video call by clearing video_source +/obj/item/device/communicator/proc/end_video(var/reason) + GLOB.moved_event.unregister(video_source, src, PROC_REF(update_active_camera_screen)) + show_static() + video_source = null + + . = "\icon[src][bicon(src)] [reason ? reason : "Video session ended"]." + + visible_message(.) + update_icon() diff --git a/code/game/objects/items/devices/debugger.dm b/code/game/objects/items/devices/debugger.dm index 8bfcc034541..f97ab7db0c0 100644 --- a/code/game/objects/items/devices/debugger.dm +++ b/code/game/objects/items/devices/debugger.dm @@ -1,45 +1,45 @@ -/** - * Multitool -- A multitool is used for hacking electronic devices. - * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. - * - */ - -/obj/item/device/debugger - name = "debugger" - desc = "Used to debug electronic equipment." - icon = 'icons/obj/hacktool.dmi' - icon_state = "hacktool-g" - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - desc = "You can use this on airlocks or APCs to try to hack them without cutting wires." - - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - - origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) - var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage - -/obj/item/device/debugger/is_used_on(obj/O, mob/user) - if(istype(O, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = O - if(A.emagged || A.hacker) - to_chat(user, "There is a software error with the device.") - else - to_chat(user, "The device's software appears to be fine.") - return 1 - if(istype(O, /obj/machinery/door)) - var/obj/machinery/door/D = O - if(D.operating == -1) - to_chat(user, "There is a software error with the device.") - else - to_chat(user, "The device's software appears to be fine.") - return 1 - else if(istype(O, /obj/machinery)) - var/obj/machinery/A = O - if(A.emagged) - to_chat(user, "There is a software error with the device.") - else - to_chat(user, "The device's software appears to be fine.") - return 1 +/** + * Multitool -- A multitool is used for hacking electronic devices. + * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. + * + */ + +/obj/item/device/debugger + name = "debugger" + desc = "Used to debug electronic equipment." + icon = 'icons/obj/hacktool.dmi' + icon_state = "hacktool-g" + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + desc = "You can use this on airlocks or APCs to try to hack them without cutting wires." + + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + + origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) + var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage + +/obj/item/device/debugger/is_used_on(obj/O, mob/user) + if(istype(O, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = O + if(A.emagged || A.hacker) + to_chat(user, "There is a software error with the device.") + else + to_chat(user, "The device's software appears to be fine.") + return 1 + if(istype(O, /obj/machinery/door)) + var/obj/machinery/door/D = O + if(D.operating == -1) + to_chat(user, "There is a software error with the device.") + else + to_chat(user, "The device's software appears to be fine.") + return 1 + else if(istype(O, /obj/machinery)) + var/obj/machinery/A = O + if(A.emagged) + to_chat(user, "There is a software error with the device.") + else + to_chat(user, "The device's software appears to be fine.") + return 1 diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index b7d6d5278c5..7e993ce8ee8 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -96,7 +96,7 @@ to_chat(user, "You install a cell in \the [src].") update_icon() - else if(W.is_screwdriver()) + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(bcell) bcell.update_icon() bcell.forceMove(get_turf(src.loc)) diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 0b987e04c7f..5fb6a4ac84f 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -1,321 +1,321 @@ -/obj/item/device/flash - name = "flash" - desc = "Used for blinding and disorienting." - icon_state = "flash" - item_state = "flashtool" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 4 - throw_range = 10 - origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) - - var/times_used = 0 //Number of times it's been used. - var/broken = FALSE //Is the flash burnt out? - var/last_used = 0 //last world.time it was used. - var/max_flashes = 10 // How many times the flash can be used before needing to self recharge. - var/halloss_per_flash = 30 - var/break_mod = 3 // The percent to break increased by every use on the flash. - - var/can_break = TRUE // Can the flash break? - var/can_repair = FALSE // Can you repair the flash? - var/repairing = FALSE // Are we repairing right now? - - var/safe_flashes = 2 // How many flashes are kept in 1% breakchance? - - var/charge_only = FALSE // Does the flash run purely on charge? - - var/base_icon = "flash" - - var/obj/item/weapon/cell/power_supply //What type of power cell this uses - var/charge_cost = 30 //How much energy is needed to flash. - var/use_external_power = FALSE // Do we use charge from an external source? - - var/cell_type = /obj/item/weapon/cell/device - -/obj/item/device/flash/Initialize() - . = ..() - power_supply = new cell_type(src) - -/obj/item/device/flash/attackby(var/obj/item/W, var/mob/user) - if(W.is_screwdriver() && broken) - if(repairing) - to_chat(user, "\The [src] is already being repaired!") - return - user.visible_message("\The [user] starts trying to repair \the [src]'s bulb.") - repairing = TRUE - if(do_after(user, (40 SECONDS + rand(0, 20 SECONDS)) * W.toolspeed) && can_repair) - if(prob(30)) - user.visible_message("\The [user] successfully repairs \the [src]!") - broken = FALSE - update_icon() - playsound(src, W.usesound, 50, 1) - else - user.visible_message("\The [user] fails to repair \the [src].") - repairing = FALSE - else - ..() - -/obj/item/device/flash/update_icon() - var/obj/item/weapon/cell/battery = power_supply - - if(use_external_power) - battery = get_external_power_supply() - - if(broken || !battery || battery.charge < charge_cost) - icon_state = "[base_icon]burnt" - else - icon_state = "[base_icon]" - return - -/obj/item/device/flash/get_cell() - return power_supply - -/obj/item/device/flash/proc/get_external_power_supply() - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - return R.cell - if(istype(src.loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = src.loc - if(module.holder && module.holder.wearer) - var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.get_rig()) - var/obj/item/weapon/rig/suit = H.get_rig() - if(istype(suit)) - return suit.cell - return null - -/obj/item/device/flash/proc/clown_check(var/mob/user) - if(user && (CLUMSY in user.mutations) && prob(50)) - to_chat(user, "\The [src] slips out of your hand.") - user.drop_item() - return 0 - return 1 - -/obj/item/device/flash/proc/flash_recharge() - //Every ten seconds the flash doesn't get used, the times_used variable goes down by one, making the flash less likely to burn out, - // as well as being able to flash more before reaching max_flashes cap. - for(var/i=0, i < max_flashes, i++) - if(last_used + 10 SECONDS > world.time) - break - - else if(use_external_power) - var/obj/item/weapon/cell/external = get_external_power_supply() - if(!external || !external.use(charge_cost)) //Take power from the borg or rig! - break - - else if(!power_supply || !power_supply.checked_use(charge_cost)) - break - - last_used += 10 SECONDS - times_used-- - - last_used = world.time - times_used = max(0,round(times_used)) //sanity - update_icon() - -// Returns true if the device can flash. -/obj/item/device/flash/proc/check_capacitor(var/mob/user) - //spamming the flash before it's fully charged (60 seconds) increases the chance of it breaking - //It will never break on the first use. - var/obj/item/weapon/cell/battery = power_supply - - if(use_external_power) - battery = get_external_power_supply() - - if(times_used <= max_flashes && battery && battery.charge >= charge_cost) - last_used = world.time - if(prob( max(0, times_used - safe_flashes) * 2 + (times_used >= safe_flashes)) && can_break) //if you use it 10 times in a minute it has a 30% chance to break. - broken = TRUE - if(user) - to_chat(user, "The bulb has burnt out!") - update_icon() - return FALSE - else - times_used++ - update_icon() - return TRUE - else if(!charge_only) //can only use it 10 times a minute, unless it runs purely on charge. - if(user) - update_icon() - to_chat(user, "click") - playsound(src, 'sound/weapons/empty.ogg', 80, 1) - return FALSE - else if(battery && battery.checked_use(charge_cost + (round(charge_cost / 4) * max(0, times_used - max_flashes)))) // Using over your maximum flashes starts taking more charge per added flash. - times_used++ - update_icon() - return TRUE - -//attack_as_weapon -/obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone) - if(!user || !M) return //sanity - - add_attack_logs(user,M,"Flashed (attempt) with [src]") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(M) - - if(!clown_check(user)) return - if(broken) - to_chat(user, "\The [src] is broken.") - return - - flash_recharge() - - if(!check_capacitor(user)) - return - - playsound(src, 'sound/weapons/flash.ogg', 100, 1) - var/flashfail = 0 - - //VOREStation Add - NIF - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) - flashfail = 1 - H.nif.notify("High intensity light detected, and blocked!",TRUE) - //VOREStation Add End - - if(iscarbon(M) && !flashfail) //VOREStation Add - NIF - var/mob/living/carbon/C = M - if(C.stat != DEAD) - var/safety = C.eyecheck() - if(safety <= 0) - var/flash_strength = 10 //Vorestation edit, making flashes behave the same as flash rounds - if(ishuman(C)) - var/mob/living/carbon/human/H = C - flash_strength *= H.species.flash_mod - - if(flash_strength > 0) - H.Confuse(flash_strength + 5) - H.Blind(flash_strength) - H.eye_blurry = max(H.eye_blurry, flash_strength + 5) - H.flash_eyes() - H.adjustHalLoss(halloss_per_flash * (flash_strength / 5)) // Should take four flashes to stun. - H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns") - - else - flashfail = 1 - - else if(issilicon(M)) - flashfail = 0 - var/mob/living/silicon/S = M - if(isrobot(S)) - var/mob/living/silicon/robot/R = S - if(R.has_active_type(/obj/item/borg/combat/shield)) - var/obj/item/borg/combat/shield/shield = locate() in R - if(shield) - if(shield.active) - shield.adjust_flash_count(R, 1) - flashfail = 1 - else - flashfail = 1 - - if(isrobot(user)) - spawn(0) - var/atom/movable/overlay/animation = new(user.loc) - animation.layer = user.layer + 1 - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = user - flick("blspell", animation) - sleep(5) - qdel(animation) - - if(!flashfail) - flick("flash2", src) - if(!issilicon(M)) - - user.visible_message("[user] blinds [M] with the flash!") - else - - user.visible_message("[user] overloads [M]'s sensors with the flash!") - M.Weaken(rand(5,10)) - else - - user.visible_message("[user] fails to blind [M] with the flash!") - - return - - - - -/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) - if(!user || !clown_check(user)) return - - user.setClickCooldown(user.get_attack_speed(src)) - - if(broken) - user.show_message("The [src.name] is broken", 2) - return - - flash_recharge() - - if(!check_capacitor(user)) - return - - playsound(src, 'sound/weapons/flash.ogg', 100, 1) - flick("flash2", src) - if(user && isrobot(user)) - spawn(0) - var/atom/movable/overlay/animation = new(user.loc) - animation.layer = user.layer + 1 - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = user - flick("blspell", animation) - sleep(5) - qdel(animation) - - for(var/mob/living/carbon/C in oviewers(3, null)) - var/safety = C.eyecheck() - if(!safety) - if(!C.blinded) - C.flash_eyes() - - return - -/obj/item/device/flash/emp_act(severity) - if(broken) return - flash_recharge() - if(!check_capacitor()) - return - - if(istype(loc, /mob/living/carbon)) - var/mob/living/carbon/C = loc - var/safety = C.eyecheck() - if(safety <= 0) - C.adjustHalLoss(halloss_per_flash) - //C.Weaken(10) - C.flash_eyes() - for(var/mob/M in viewers(C, null)) - M.show_message("[C] is blinded by the flash!") - ..() - -/obj/item/device/flash/synthetic - name = "synthetic flash" - desc = "When a problem arises, SCIENCE is the solution." - icon_state = "sflash" - origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) - base_icon = "sflash" - can_repair = FALSE - -//attack_as_weapon -/obj/item/device/flash/synthetic/attack(mob/living/M, mob/living/user, var/target_zone) - ..() - if(!broken) - broken = 1 - to_chat(user, "The bulb has burnt out!") - update_icon() - -/obj/item/device/flash/synthetic/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) - ..() - if(!broken) - broken = 1 - to_chat(user, "The bulb has burnt out!") - update_icon() - -/obj/item/device/flash/robot - name = "mounted flash" - can_break = FALSE - use_external_power = TRUE - charge_only = TRUE +/obj/item/device/flash + name = "flash" + desc = "Used for blinding and disorienting." + icon_state = "flash" + item_state = "flashtool" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 10 + origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) + + var/times_used = 0 //Number of times it's been used. + var/broken = FALSE //Is the flash burnt out? + var/last_used = 0 //last world.time it was used. + var/max_flashes = 10 // How many times the flash can be used before needing to self recharge. + var/halloss_per_flash = 30 + var/break_mod = 3 // The percent to break increased by every use on the flash. + + var/can_break = TRUE // Can the flash break? + var/can_repair = FALSE // Can you repair the flash? + var/repairing = FALSE // Are we repairing right now? + + var/safe_flashes = 2 // How many flashes are kept in 1% breakchance? + + var/charge_only = FALSE // Does the flash run purely on charge? + + var/base_icon = "flash" + + var/obj/item/weapon/cell/power_supply //What type of power cell this uses + var/charge_cost = 30 //How much energy is needed to flash. + var/use_external_power = FALSE // Do we use charge from an external source? + + var/cell_type = /obj/item/weapon/cell/device + +/obj/item/device/flash/Initialize() + . = ..() + power_supply = new cell_type(src) + +/obj/item/device/flash/attackby(var/obj/item/W, var/mob/user) + if(W.has_tool_quality(TOOL_SCREWDRIVER) && broken) + if(repairing) + to_chat(user, "\The [src] is already being repaired!") + return + user.visible_message("\The [user] starts trying to repair \the [src]'s bulb.") + repairing = TRUE + if(do_after(user, (40 SECONDS + rand(0, 20 SECONDS)) * W.toolspeed) && can_repair) + if(prob(30)) + user.visible_message("\The [user] successfully repairs \the [src]!") + broken = FALSE + update_icon() + playsound(src, W.usesound, 50, 1) + else + user.visible_message("\The [user] fails to repair \the [src].") + repairing = FALSE + else + ..() + +/obj/item/device/flash/update_icon() + var/obj/item/weapon/cell/battery = power_supply + + if(use_external_power) + battery = get_external_power_supply() + + if(broken || !battery || battery.charge < charge_cost) + icon_state = "[base_icon]burnt" + else + icon_state = "[base_icon]" + return + +/obj/item/device/flash/get_cell() + return power_supply + +/obj/item/device/flash/proc/get_external_power_supply() + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return R.cell + if(istype(src.loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = src.loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.get_rig()) + var/obj/item/weapon/rig/suit = H.get_rig() + if(istype(suit)) + return suit.cell + return null + +/obj/item/device/flash/proc/clown_check(var/mob/user) + if(user && (CLUMSY in user.mutations) && prob(50)) + to_chat(user, "\The [src] slips out of your hand.") + user.drop_item() + return 0 + return 1 + +/obj/item/device/flash/proc/flash_recharge() + //Every ten seconds the flash doesn't get used, the times_used variable goes down by one, making the flash less likely to burn out, + // as well as being able to flash more before reaching max_flashes cap. + for(var/i=0, i < max_flashes, i++) + if(last_used + 10 SECONDS > world.time) + break + + else if(use_external_power) + var/obj/item/weapon/cell/external = get_external_power_supply() + if(!external || !external.use(charge_cost)) //Take power from the borg or rig! + break + + else if(!power_supply || !power_supply.checked_use(charge_cost)) + break + + last_used += 10 SECONDS + times_used-- + + last_used = world.time + times_used = max(0,round(times_used)) //sanity + update_icon() + +// Returns true if the device can flash. +/obj/item/device/flash/proc/check_capacitor(var/mob/user) + //spamming the flash before it's fully charged (60 seconds) increases the chance of it breaking + //It will never break on the first use. + var/obj/item/weapon/cell/battery = power_supply + + if(use_external_power) + battery = get_external_power_supply() + + if(times_used <= max_flashes && battery && battery.charge >= charge_cost) + last_used = world.time + if(prob( max(0, times_used - safe_flashes) * 2 + (times_used >= safe_flashes)) && can_break) //if you use it 10 times in a minute it has a 30% chance to break. + broken = TRUE + if(user) + to_chat(user, "The bulb has burnt out!") + update_icon() + return FALSE + else + times_used++ + update_icon() + return TRUE + else if(!charge_only) //can only use it 10 times a minute, unless it runs purely on charge. + if(user) + update_icon() + to_chat(user, "click") + playsound(src, 'sound/weapons/empty.ogg', 80, 1) + return FALSE + else if(battery && battery.checked_use(charge_cost + (round(charge_cost / 4) * max(0, times_used - max_flashes)))) // Using over your maximum flashes starts taking more charge per added flash. + times_used++ + update_icon() + return TRUE + +//attack_as_weapon +/obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone) + if(!user || !M) return //sanity + + add_attack_logs(user,M,"Flashed (attempt) with [src]") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(M) + + if(!clown_check(user)) return + if(broken) + to_chat(user, "\The [src] is broken.") + return + + flash_recharge() + + if(!check_capacitor(user)) + return + + playsound(src, 'sound/weapons/flash.ogg', 100, 1) + var/flashfail = 0 + + //VOREStation Add - NIF + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) + flashfail = 1 + H.nif.notify("High intensity light detected, and blocked!",TRUE) + //VOREStation Add End + + if(iscarbon(M) && !flashfail) //VOREStation Add - NIF + var/mob/living/carbon/C = M + if(C.stat != DEAD) + var/safety = C.eyecheck() + if(safety <= 0) + var/flash_strength = 10 //Vorestation edit, making flashes behave the same as flash rounds + if(ishuman(C)) + var/mob/living/carbon/human/H = C + flash_strength *= H.species.flash_mod + + if(flash_strength > 0) + H.Confuse(flash_strength + 5) + H.Blind(flash_strength) + H.eye_blurry = max(H.eye_blurry, flash_strength + 5) + H.flash_eyes() + H.adjustHalLoss(halloss_per_flash * (flash_strength / 5)) // Should take four flashes to stun. + H.apply_damage(flash_strength * H.species.flash_burn/5, BURN, BP_HEAD, 0, 0, "Photon burns") + + else + flashfail = 1 + + else if(issilicon(M)) + flashfail = 0 + var/mob/living/silicon/S = M + if(isrobot(S)) + var/mob/living/silicon/robot/R = S + if(R.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in R + if(shield) + if(shield.active) + shield.adjust_flash_count(R, 1) + flashfail = 1 + else + flashfail = 1 + + if(isrobot(user)) + spawn(0) + var/atom/movable/overlay/animation = new(user.loc) + animation.layer = user.layer + 1 + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = user + flick("blspell", animation) + sleep(5) + qdel(animation) + + if(!flashfail) + flick("flash2", src) + if(!issilicon(M)) + + user.visible_message("[user] blinds [M] with the flash!") + else + + user.visible_message("[user] overloads [M]'s sensors with the flash!") + M.Weaken(rand(5,10)) + else + + user.visible_message("[user] fails to blind [M] with the flash!") + + return + + + + +/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) + if(!user || !clown_check(user)) return + + user.setClickCooldown(user.get_attack_speed(src)) + + if(broken) + user.show_message("The [src.name] is broken", 2) + return + + flash_recharge() + + if(!check_capacitor(user)) + return + + playsound(src, 'sound/weapons/flash.ogg', 100, 1) + flick("flash2", src) + if(user && isrobot(user)) + spawn(0) + var/atom/movable/overlay/animation = new(user.loc) + animation.layer = user.layer + 1 + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = user + flick("blspell", animation) + sleep(5) + qdel(animation) + + for(var/mob/living/carbon/C in oviewers(3, null)) + var/safety = C.eyecheck() + if(!safety) + if(!C.blinded) + C.flash_eyes() + + return + +/obj/item/device/flash/emp_act(severity) + if(broken) return + flash_recharge() + if(!check_capacitor()) + return + + if(istype(loc, /mob/living/carbon)) + var/mob/living/carbon/C = loc + var/safety = C.eyecheck() + if(safety <= 0) + C.adjustHalLoss(halloss_per_flash) + //C.Weaken(10) + C.flash_eyes() + for(var/mob/M in viewers(C, null)) + M.show_message("[C] is blinded by the flash!") + ..() + +/obj/item/device/flash/synthetic + name = "synthetic flash" + desc = "When a problem arises, SCIENCE is the solution." + icon_state = "sflash" + origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) + base_icon = "sflash" + can_repair = FALSE + +//attack_as_weapon +/obj/item/device/flash/synthetic/attack(mob/living/M, mob/living/user, var/target_zone) + ..() + if(!broken) + broken = 1 + to_chat(user, "The bulb has burnt out!") + update_icon() + +/obj/item/device/flash/synthetic/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) + ..() + if(!broken) + broken = 1 + to_chat(user, "The bulb has burnt out!") + update_icon() + +/obj/item/device/flash/robot + name = "mounted flash" + can_break = FALSE + use_external_power = TRUE + charge_only = TRUE diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index eff6d348480..b7b4440b1ea 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -1,493 +1,493 @@ -/* - * Contains: - * Flashlights - * Lamps - * Flares - * Chemlights - * Slime Extract - */ - -/* - * Flashlights - */ - -/obj/item/device/flashlight - name = "flashlight" - desc = "A hand-held emergency light." - icon = 'icons/obj/lighting.dmi' - icon_state = "flashlight" - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_BELT - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - action_button_name = "Toggle Flashlight" - - light_system = MOVABLE_LIGHT_DIRECTIONAL - light_range = 4 //luminosity when on - light_power = 0.8 //lighting power when on - light_color = "#FFFFFF" //LIGHT_COLOR_INCANDESCENT_FLASHLIGHT //lighting colour when on - light_cone_y_offset = -7 - - var/on = 0 - - var/obj/item/weapon/cell/cell - var/cell_type = /obj/item/weapon/cell/device - var/power_usage = 1 - var/power_use = 1 - -/obj/item/device/flashlight/Initialize() - . = ..() - - if(power_use && cell_type) - cell = new cell_type(src) - - update_brightness() - -/obj/item/device/flashlight/Destroy() - STOP_PROCESSING(SSobj, src) - qdel_null(cell) - return ..() - -/obj/item/device/flashlight/get_cell() - return cell - -/obj/item/device/flashlight/process() - if(!on || !cell) - return PROCESS_KILL - - if(power_usage) - if(cell.use(power_usage) != power_usage) // we weren't able to use our full power_usage amount! - visible_message("\The [src] flickers before going dull.") - playsound(src, 'sound/effects/sparks3.ogg', 10, 1, -3) //Small cue that your light went dull in your pocket. //VOREStation Edit - on = 0 - update_brightness() - return PROCESS_KILL - -/obj/item/device/flashlight/proc/update_brightness() - if(on) - icon_state = "[initial(icon_state)]-on" - else - icon_state = initial(icon_state) - set_light_on(on) - if(light_system == STATIC_LIGHT) - update_light() - -/obj/item/device/flashlight/examine(mob/user) - . = ..() - if(power_use && cell) - . += "\The [src] has a \the [cell] attached." - - if(cell.charge <= cell.maxcharge*0.25) - . += "It appears to have a low amount of power remaining." - else if(cell.charge > cell.maxcharge*0.25 && cell.charge <= cell.maxcharge*0.5) - . += "It appears to have an average amount of power remaining." - else if(cell.charge > cell.maxcharge*0.5 && cell.charge <= cell.maxcharge*0.75) - . += "It appears to have an above average amount of power remaining." - else if(cell.charge > cell.maxcharge*0.75 && cell.charge <= cell.maxcharge) - . += "It appears to have a high amount of power remaining." - -/obj/item/device/flashlight/attack_self(mob/user) - if(power_use) - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc].") //To prevent some lighting anomalities. - return 0 - if(!cell || cell.charge == 0) - to_chat(user, "You flick the switch on [src], but nothing happens.") - return 0 - on = !on - if(on && power_use) - START_PROCESSING(SSobj, src) - else if(power_use) - STOP_PROCESSING(SSobj, src) - playsound(src, 'sound/weapons/empty.ogg', 15, 1, -3) // VOREStation Edit - update_brightness() - user.update_action_buttons() - return 1 - -/obj/item/device/flashlight/emp_act(severity) - for(var/obj/O in contents) - O.emp_act(severity) - ..() - -/obj/item/device/flashlight/attack(mob/living/M as mob, mob/living/user as mob) - add_fingerprint(user) - if(on && user.zone_sel.selecting == O_EYES) - - if((CLUMSY in user.mutations) && prob(50)) //too dumb to use flashlight properly - return ..() //just hit them in the head - - var/mob/living/carbon/human/H = M //mob has protective eyewear - if(istype(H)) - for(var/obj/item/clothing/C in list(H.head,H.wear_mask,H.glasses)) - if(istype(C) && (C.body_parts_covered & EYES)) - to_chat(user, "You're going to need to remove [C.name] first.") - return - - var/obj/item/organ/vision - if(H.species.vision_organ) - vision = H.internal_organs_by_name[H.species.vision_organ] - if(!vision) - user.visible_message("\The [user] directs [src] at [M]'s face.", \ - "You direct [src] at [M]'s face.") - to_chat(user, "You can't find any [H.species.vision_organ ? H.species.vision_organ : "eyes"] on [H]!") - user.setClickCooldown(user.get_attack_speed(src)) - return - - user.visible_message("\The [user] directs [src] to [M]'s eyes.", \ - "You direct [src] to [M]'s eyes.") - if(H != user) //can't look into your own eyes buster - if(M.stat == DEAD || M.blinded) //mob is dead or fully blind - to_chat(user, "\The [M]'s pupils do not react to the light!") - return - if(XRAY in M.mutations) - to_chat(user, "\The [M] pupils give an eerie glow!") - if(vision.is_bruised()) - to_chat(user, "There's visible damage to [M]'s [vision.name]!") - else if(M.eye_blurry) - to_chat(user, "\The [M]'s pupils react slower than normally.") - if(M.getBrainLoss() > 15) - to_chat(user, "There's visible lag between left and right pupils' reactions.") - - var/list/pinpoint = list("oxycodone"=1,"tramadol"=5) - var/list/dilating = list("bliss"=5,"ambrosia_extract"=5,"mindbreaker"=1) - if(M.reagents.has_any_reagent(pinpoint) || H.ingested.has_any_reagent(pinpoint)) - to_chat(user, "\The [M]'s pupils are already pinpoint and cannot narrow any more.") - else if(M.reagents.has_any_reagent(dilating) || H.ingested.has_any_reagent(dilating)) - to_chat(user, "\The [M]'s pupils narrow slightly, but are still very dilated.") - else - to_chat(user, "\The [M]'s pupils narrow.") - - user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively - M.flash_eyes() - else - return ..() - -/obj/item/device/flashlight/attack_hand(mob/user as mob) - if(user.get_inactive_hand() == src) - if(cell) - cell.update_icon() - user.put_in_hands(cell) - cell = null - to_chat(user, "You remove the cell from the [src].") - playsound(src, 'sound/machines/button.ogg', 30, 1, 0) - on = 0 - update_brightness() - return - ..() - else - return ..() - -/obj/item/device/flashlight/MouseDrop(obj/over_object as obj) - if(!canremove) - return - - if (ishuman(usr) || issmall(usr)) //so monkeys can take off their backpacks -- Urist - - if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? - return - - if (!( istype(over_object, /obj/screen) )) - return ..() - - //makes sure that the thing is equipped, so that we can't drag it into our hand from miles away. - //there's got to be a better way of doing this. - if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) - return - - if (( usr.restrained() ) || ( usr.stat )) - return - - if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) - return - - switch(over_object.name) - if("r_hand") - usr.u_equip(src) - usr.put_in_r_hand(src) - if("l_hand") - usr.u_equip(src) - usr.put_in_l_hand(src) - src.add_fingerprint(usr) - -/obj/item/device/flashlight/attackby(obj/item/weapon/W, mob/user as mob) - if(power_use) - if(istype(W, /obj/item/weapon/cell)) - if(istype(W, /obj/item/weapon/cell/device)) - if(!cell) - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You install a cell in \the [src].") - playsound(src, 'sound/machines/button.ogg', 30, 1, 0) - update_brightness() - else - to_chat(user, "\The [src] already has a cell.") - else - to_chat(user, "\The [src] cannot use that type of cell.") - - else - ..() - -/obj/item/device/flashlight/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - . = ..() - if(!on) - return - if(light_system == MOVABLE_LIGHT_DIRECTIONAL) - var/datum/component/overlay_lighting/OL = GetComponent(/datum/component/overlay_lighting) - if(!OL) - return - var/turf/T = get_turf(target) - OL.place_directional_light(T) - -/obj/item/device/flashlight/pen - name = "penlight" - desc = "A pen-sized light, used by medical staff." - icon_state = "penlight" - item_state = "pen" - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - slot_flags = SLOT_EARS - light_range = 2 - w_class = ITEMSIZE_TINY - power_use = 0 - -/obj/item/device/flashlight/color //Default color is blue - name = "blue flashlight" - desc = "A small flashlight. This one is blue." - icon_state = "flashlight_blue" - -/obj/item/device/flashlight/color/green - name = "green flashlight" - desc = "A small flashlight. This one is green." - icon_state = "flashlight_green" - -/obj/item/device/flashlight/color/purple - name = "purple flashlight" - desc = "A small flashlight. This one is purple." - icon_state = "flashlight_purple" - -/obj/item/device/flashlight/color/red - name = "red flashlight" - desc = "A small flashlight. This one is red." - icon_state = "flashlight_red" - -/obj/item/device/flashlight/color/orange - name = "orange flashlight" - desc = "A small flashlight. This one is orange." - icon_state = "flashlight_orange" - -/obj/item/device/flashlight/color/yellow - name = "yellow flashlight" - desc = "A small flashlight. This one is yellow." - icon_state = "flashlight_yellow" - -/obj/item/device/flashlight/maglight - name = "maglight" - desc = "A very, very heavy duty flashlight." - icon_state = "maglight" - light_color = LIGHT_COLOR_FLUORESCENT_FLASHLIGHT - force = 10 - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - attack_verb = list ("smacked", "thwacked", "thunked") - matter = list(MAT_STEEL = 200,MAT_GLASS = 50) - hitsound = "swing_hit" - -/obj/item/device/flashlight/drone - name = "low-power flashlight" - desc = "A miniature lamp, that might be used by small robots." - icon_state = "penlight" - item_state = null - light_range = 2 - w_class = ITEMSIZE_TINY - power_use = 0 - -/* - * Lamps - */ - -// pixar desk lamp -/obj/item/device/flashlight/lamp - name = "desk lamp" - desc = "A desk lamp with an adjustable mount." - icon_state = "lamp" - force = 10 - center_of_mass = list("x" = 13,"y" = 11) - light_range = 5 - w_class = ITEMSIZE_LARGE - power_use = 0 - on = 1 - light_system = STATIC_LIGHT - -/obj/item/device/flashlight/lamp/verb/toggle_light() - set name = "Toggle light" - set category = "Object" - set src in oview(1) - - if(!usr.stat) - attack_self(usr) - -// green-shaded desk lamp -/obj/item/device/flashlight/lamp/green - desc = "A classic green-shaded desk lamp." - icon_state = "lampgreen" - center_of_mass = list("x" = 15,"y" = 11) - light_color = "#FFC58F" - -// clown lamp -/obj/item/device/flashlight/lamp/clown - desc = "A whacky banana peel shaped lamp." - icon_state = "bananalamp" - center_of_mass = list("x" = 15,"y" = 11) - - -/* - * Flares - */ - -/obj/item/device/flashlight/flare - name = "flare" - desc = "A red standard-issue flare. There are instructions on the side reading 'pull cord, make light'." - w_class = ITEMSIZE_SMALL - light_range = 8 // Pretty bright. - light_power = 0.8 - light_color = LIGHT_COLOR_FLARE - icon_state = "flare" - item_state = "flare" - action_button_name = null //just pull it manually, neckbeard. - var/fuel = 0 - var/on_damage = 7 - var/produce_heat = 1500 - power_use = 0 - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - light_system = MOVABLE_LIGHT - -/obj/item/device/flashlight/flare/New() - fuel = rand(800, 1000) // Sorry for changing this so much but I keep under-estimating how long X number of ticks last in seconds. - ..() - -/obj/item/device/flashlight/flare/process() - var/turf/pos = get_turf(src) - if(pos) - pos.hotspot_expose(produce_heat, 5) - fuel = max(fuel - 1, 0) - if(!fuel || !on) - turn_off() - if(!fuel) - src.icon_state = "[initial(icon_state)]-empty" - STOP_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/flare/proc/turn_off() - on = 0 - src.force = initial(src.force) - src.damtype = initial(src.damtype) - update_brightness() - -/obj/item/device/flashlight/flare/attack_self(mob/user) - - // Usual checks - if(!fuel) - to_chat(user, "It's out of fuel.") - return - if(on) - return - - . = ..() - // All good, turn it on. - if(.) - user.visible_message("[user] activates the flare.", "You pull the cord on the flare, activating it!") - src.force = on_damage - src.damtype = "fire" - START_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/flare/proc/ignite() //Used for flare launchers. - on = !on - update_brightness() - force = on_damage - damtype = "fire" - START_PROCESSING(SSobj, src) - return 1 - -/* - * Chemlights - */ - -/obj/item/device/flashlight/glowstick - name = "green glowstick" - desc = "A green military-grade chemical light." - w_class = ITEMSIZE_SMALL - light_system = MOVABLE_LIGHT - light_range = 4 - light_power = 0.9 - light_color = "#49F37C" - icon_state = "glowstick_green" - item_state = "glowstick_green" - var/fuel = 0 - power_use = 0 - -/obj/item/device/flashlight/glowstick/New() - fuel = rand(1600, 2000) - ..() - -/obj/item/device/flashlight/glowstick/process() - fuel = max(fuel - 1, 0) - if(!fuel || !on) - turn_off() - if(!fuel) - src.icon_state = "[initial(icon_state)]-empty" - STOP_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/glowstick/proc/turn_off() - on = 0 - update_brightness() - -/obj/item/device/flashlight/glowstick/attack_self(mob/user) - - if(!fuel) - to_chat(user, "The glowstick has already been turned on.") - return - if(on) - return - - . = ..() - if(.) - user.visible_message("[user] cracks and shakes \the [name].", "You crack and shake \the [src], turning it on!") - START_PROCESSING(SSobj, src) - -/obj/item/device/flashlight/glowstick/red - name = "red glowstick" - desc = "A red military-grade chemical light." - light_color = "#FC0F29" - icon_state = "glowstick_red" - item_state = "glowstick_red" - -/obj/item/device/flashlight/glowstick/blue - name = "blue glowstick" - desc = "A blue military-grade chemical light." - light_color = "#599DFF" - icon_state = "glowstick_blue" - item_state = "glowstick_blue" - -/obj/item/device/flashlight/glowstick/orange - name = "orange glowstick" - desc = "A orange military-grade chemical light." - light_color = "#FA7C0B" - icon_state = "glowstick_orange" - item_state = "glowstick_orange" - -/obj/item/device/flashlight/glowstick/yellow - name = "yellow glowstick" - desc = "A yellow military-grade chemical light." - light_color = "#FEF923" - icon_state = "glowstick_yellow" - item_state = "glowstick_yellow" - -/obj/item/device/flashlight/glowstick/radioisotope - name = "radioisotope glowstick" - desc = "A radioisotope powered chemical light. Escaping particles light up the area far brighter on similar levels to flares and for longer" - icon_state = "glowstick_isotope" - item_state = "glowstick_isotope" - - light_range = 8 - light_power = 0.1 - light_color = "#49F37C" +/* + * Contains: + * Flashlights + * Lamps + * Flares + * Chemlights + * Slime Extract + */ + +/* + * Flashlights + */ + +/obj/item/device/flashlight + name = "flashlight" + desc = "A hand-held emergency light." + icon = 'icons/obj/lighting.dmi' + icon_state = "flashlight" + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_BELT + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + action_button_name = "Toggle Flashlight" + + light_system = MOVABLE_LIGHT_DIRECTIONAL + light_range = 4 //luminosity when on + light_power = 0.8 //lighting power when on + light_color = "#FFFFFF" //LIGHT_COLOR_INCANDESCENT_FLASHLIGHT //lighting colour when on + light_cone_y_offset = -7 + + var/on = 0 + + var/obj/item/weapon/cell/cell + var/cell_type = /obj/item/weapon/cell/device + var/power_usage = 1 + var/power_use = 1 + +/obj/item/device/flashlight/Initialize() + . = ..() + + if(power_use && cell_type) + cell = new cell_type(src) + + update_brightness() + +/obj/item/device/flashlight/Destroy() + STOP_PROCESSING(SSobj, src) + qdel_null(cell) + return ..() + +/obj/item/device/flashlight/get_cell() + return cell + +/obj/item/device/flashlight/process() + if(!on || !cell) + return PROCESS_KILL + + if(power_usage) + if(cell.use(power_usage) != power_usage) // we weren't able to use our full power_usage amount! + visible_message("\The [src] flickers before going dull.") + playsound(src, 'sound/effects/sparks3.ogg', 10, 1, -3) //Small cue that your light went dull in your pocket. //VOREStation Edit + on = 0 + update_brightness() + return PROCESS_KILL + +/obj/item/device/flashlight/proc/update_brightness() + if(on) + icon_state = "[initial(icon_state)]-on" + else + icon_state = initial(icon_state) + set_light_on(on) + if(light_system == STATIC_LIGHT) + update_light() + +/obj/item/device/flashlight/examine(mob/user) + . = ..() + if(power_use && cell) + . += "\The [src] has a \the [cell] attached." + + if(cell.charge <= cell.maxcharge*0.25) + . += "It appears to have a low amount of power remaining." + else if(cell.charge > cell.maxcharge*0.25 && cell.charge <= cell.maxcharge*0.5) + . += "It appears to have an average amount of power remaining." + else if(cell.charge > cell.maxcharge*0.5 && cell.charge <= cell.maxcharge*0.75) + . += "It appears to have an above average amount of power remaining." + else if(cell.charge > cell.maxcharge*0.75 && cell.charge <= cell.maxcharge) + . += "It appears to have a high amount of power remaining." + +/obj/item/device/flashlight/attack_self(mob/user) + if(power_use) + if(!isturf(user.loc)) + to_chat(user, "You cannot turn the light on while in this [user.loc].") //To prevent some lighting anomalities. + return 0 + if(!cell || cell.charge == 0) + to_chat(user, "You flick the switch on [src], but nothing happens.") + return 0 + on = !on + if(on && power_use) + START_PROCESSING(SSobj, src) + else if(power_use) + STOP_PROCESSING(SSobj, src) + playsound(src, 'sound/weapons/empty.ogg', 15, 1, -3) // VOREStation Edit + update_brightness() + user.update_action_buttons() + return 1 + +/obj/item/device/flashlight/emp_act(severity) + for(var/obj/O in contents) + O.emp_act(severity) + ..() + +/obj/item/device/flashlight/attack(mob/living/M as mob, mob/living/user as mob) + add_fingerprint(user) + if(on && user.zone_sel.selecting == O_EYES) + + if((CLUMSY in user.mutations) && prob(50)) //too dumb to use flashlight properly + return ..() //just hit them in the head + + var/mob/living/carbon/human/H = M //mob has protective eyewear + if(istype(H)) + for(var/obj/item/clothing/C in list(H.head,H.wear_mask,H.glasses)) + if(istype(C) && (C.body_parts_covered & EYES)) + to_chat(user, "You're going to need to remove [C.name] first.") + return + + var/obj/item/organ/vision + if(H.species.vision_organ) + vision = H.internal_organs_by_name[H.species.vision_organ] + if(!vision) + user.visible_message("\The [user] directs [src] at [M]'s face.", \ + "You direct [src] at [M]'s face.") + to_chat(user, "You can't find any [H.species.vision_organ ? H.species.vision_organ : "eyes"] on [H]!") + user.setClickCooldown(user.get_attack_speed(src)) + return + + user.visible_message("\The [user] directs [src] to [M]'s eyes.", \ + "You direct [src] to [M]'s eyes.") + if(H != user) //can't look into your own eyes buster + if(M.stat == DEAD || M.blinded) //mob is dead or fully blind + to_chat(user, "\The [M]'s pupils do not react to the light!") + return + if(XRAY in M.mutations) + to_chat(user, "\The [M] pupils give an eerie glow!") + if(vision.is_bruised()) + to_chat(user, "There's visible damage to [M]'s [vision.name]!") + else if(M.eye_blurry) + to_chat(user, "\The [M]'s pupils react slower than normally.") + if(M.getBrainLoss() > 15) + to_chat(user, "There's visible lag between left and right pupils' reactions.") + + var/list/pinpoint = list("oxycodone"=1,"tramadol"=5) + var/list/dilating = list("bliss"=5,"ambrosia_extract"=5,"mindbreaker"=1) + if(M.reagents.has_any_reagent(pinpoint) || H.ingested.has_any_reagent(pinpoint)) + to_chat(user, "\The [M]'s pupils are already pinpoint and cannot narrow any more.") + else if(M.reagents.has_any_reagent(dilating) || H.ingested.has_any_reagent(dilating)) + to_chat(user, "\The [M]'s pupils narrow slightly, but are still very dilated.") + else + to_chat(user, "\The [M]'s pupils narrow.") + + user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively + M.flash_eyes() + else + return ..() + +/obj/item/device/flashlight/attack_hand(mob/user as mob) + if(user.get_inactive_hand() == src) + if(cell) + cell.update_icon() + user.put_in_hands(cell) + cell = null + to_chat(user, "You remove the cell from the [src].") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) + on = 0 + update_brightness() + return + ..() + else + return ..() + +/obj/item/device/flashlight/MouseDrop(obj/over_object as obj) + if(!canremove) + return + + if (ishuman(usr) || issmall(usr)) //so monkeys can take off their backpacks -- Urist + + if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? + return + + if (!( istype(over_object, /obj/screen) )) + return ..() + + //makes sure that the thing is equipped, so that we can't drag it into our hand from miles away. + //there's got to be a better way of doing this. + if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) + return + + if (( usr.restrained() ) || ( usr.stat )) + return + + if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) + return + + switch(over_object.name) + if("r_hand") + usr.u_equip(src) + usr.put_in_r_hand(src) + if("l_hand") + usr.u_equip(src) + usr.put_in_l_hand(src) + src.add_fingerprint(usr) + +/obj/item/device/flashlight/attackby(obj/item/weapon/W, mob/user as mob) + if(power_use) + if(istype(W, /obj/item/weapon/cell)) + if(istype(W, /obj/item/weapon/cell/device)) + if(!cell) + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You install a cell in \the [src].") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) + update_brightness() + else + to_chat(user, "\The [src] already has a cell.") + else + to_chat(user, "\The [src] cannot use that type of cell.") + + else + ..() + +/obj/item/device/flashlight/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!on) + return + if(light_system == MOVABLE_LIGHT_DIRECTIONAL) + var/datum/component/overlay_lighting/OL = GetComponent(/datum/component/overlay_lighting) + if(!OL) + return + var/turf/T = get_turf(target) + OL.place_directional_light(T) + +/obj/item/device/flashlight/pen + name = "penlight" + desc = "A pen-sized light, used by medical staff." + icon_state = "penlight" + item_state = "pen" + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + slot_flags = SLOT_EARS + light_range = 2 + w_class = ITEMSIZE_TINY + power_use = 0 + +/obj/item/device/flashlight/color //Default color is blue + name = "blue flashlight" + desc = "A small flashlight. This one is blue." + icon_state = "flashlight_blue" + +/obj/item/device/flashlight/color/green + name = "green flashlight" + desc = "A small flashlight. This one is green." + icon_state = "flashlight_green" + +/obj/item/device/flashlight/color/purple + name = "purple flashlight" + desc = "A small flashlight. This one is purple." + icon_state = "flashlight_purple" + +/obj/item/device/flashlight/color/red + name = "red flashlight" + desc = "A small flashlight. This one is red." + icon_state = "flashlight_red" + +/obj/item/device/flashlight/color/orange + name = "orange flashlight" + desc = "A small flashlight. This one is orange." + icon_state = "flashlight_orange" + +/obj/item/device/flashlight/color/yellow + name = "yellow flashlight" + desc = "A small flashlight. This one is yellow." + icon_state = "flashlight_yellow" + +/obj/item/device/flashlight/maglight + name = "maglight" + desc = "A very, very heavy duty flashlight." + icon_state = "maglight" + light_color = LIGHT_COLOR_FLUORESCENT_FLASHLIGHT + force = 10 + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + attack_verb = list ("smacked", "thwacked", "thunked") + matter = list(MAT_STEEL = 200,MAT_GLASS = 50) + hitsound = "swing_hit" + +/obj/item/device/flashlight/drone + name = "low-power flashlight" + desc = "A miniature lamp, that might be used by small robots." + icon_state = "penlight" + item_state = null + light_range = 2 + w_class = ITEMSIZE_TINY + power_use = 0 + +/* + * Lamps + */ + +// pixar desk lamp +/obj/item/device/flashlight/lamp + name = "desk lamp" + desc = "A desk lamp with an adjustable mount." + icon_state = "lamp" + force = 10 + center_of_mass = list("x" = 13,"y" = 11) + light_range = 5 + w_class = ITEMSIZE_LARGE + power_use = 0 + on = 1 + light_system = STATIC_LIGHT + +/obj/item/device/flashlight/lamp/verb/toggle_light() + set name = "Toggle light" + set category = "Object" + set src in oview(1) + + if(!usr.stat) + attack_self(usr) + +// green-shaded desk lamp +/obj/item/device/flashlight/lamp/green + desc = "A classic green-shaded desk lamp." + icon_state = "lampgreen" + center_of_mass = list("x" = 15,"y" = 11) + light_color = "#FFC58F" + +// clown lamp +/obj/item/device/flashlight/lamp/clown + desc = "A whacky banana peel shaped lamp." + icon_state = "bananalamp" + center_of_mass = list("x" = 15,"y" = 11) + + +/* + * Flares + */ + +/obj/item/device/flashlight/flare + name = "flare" + desc = "A red standard-issue flare. There are instructions on the side reading 'pull cord, make light'." + w_class = ITEMSIZE_SMALL + light_range = 8 // Pretty bright. + light_power = 0.8 + light_color = LIGHT_COLOR_FLARE + icon_state = "flare" + item_state = "flare" + action_button_name = null //just pull it manually, neckbeard. + var/fuel = 0 + var/on_damage = 7 + var/produce_heat = 1500 + power_use = 0 + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + light_system = MOVABLE_LIGHT + +/obj/item/device/flashlight/flare/New() + fuel = rand(800, 1000) // Sorry for changing this so much but I keep under-estimating how long X number of ticks last in seconds. + ..() + +/obj/item/device/flashlight/flare/process() + var/turf/pos = get_turf(src) + if(pos) + pos.hotspot_expose(produce_heat, 5) + fuel = max(fuel - 1, 0) + if(!fuel || !on) + turn_off() + if(!fuel) + src.icon_state = "[initial(icon_state)]-empty" + STOP_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/flare/proc/turn_off() + on = 0 + src.force = initial(src.force) + src.damtype = initial(src.damtype) + update_brightness() + +/obj/item/device/flashlight/flare/attack_self(mob/user) + + // Usual checks + if(!fuel) + to_chat(user, "It's out of fuel.") + return + if(on) + return + + . = ..() + // All good, turn it on. + if(.) + user.visible_message("[user] activates the flare.", "You pull the cord on the flare, activating it!") + src.force = on_damage + src.damtype = "fire" + START_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/flare/proc/ignite() //Used for flare launchers. + on = !on + update_brightness() + force = on_damage + damtype = "fire" + START_PROCESSING(SSobj, src) + return 1 + +/* + * Chemlights + */ + +/obj/item/device/flashlight/glowstick + name = "green glowstick" + desc = "A green military-grade chemical light." + w_class = ITEMSIZE_SMALL + light_system = MOVABLE_LIGHT + light_range = 4 + light_power = 0.9 + light_color = "#49F37C" + icon_state = "glowstick_green" + item_state = "glowstick_green" + var/fuel = 0 + power_use = 0 + +/obj/item/device/flashlight/glowstick/New() + fuel = rand(1600, 2000) + ..() + +/obj/item/device/flashlight/glowstick/process() + fuel = max(fuel - 1, 0) + if(!fuel || !on) + turn_off() + if(!fuel) + src.icon_state = "[initial(icon_state)]-empty" + STOP_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/glowstick/proc/turn_off() + on = 0 + update_brightness() + +/obj/item/device/flashlight/glowstick/attack_self(mob/user) + + if(!fuel) + to_chat(user, "The glowstick has already been turned on.") + return + if(on) + return + + . = ..() + if(.) + user.visible_message("[user] cracks and shakes \the [name].", "You crack and shake \the [src], turning it on!") + START_PROCESSING(SSobj, src) + +/obj/item/device/flashlight/glowstick/red + name = "red glowstick" + desc = "A red military-grade chemical light." + light_color = "#FC0F29" + icon_state = "glowstick_red" + item_state = "glowstick_red" + +/obj/item/device/flashlight/glowstick/blue + name = "blue glowstick" + desc = "A blue military-grade chemical light." + light_color = "#599DFF" + icon_state = "glowstick_blue" + item_state = "glowstick_blue" + +/obj/item/device/flashlight/glowstick/orange + name = "orange glowstick" + desc = "A orange military-grade chemical light." + light_color = "#FA7C0B" + icon_state = "glowstick_orange" + item_state = "glowstick_orange" + +/obj/item/device/flashlight/glowstick/yellow + name = "yellow glowstick" + desc = "A yellow military-grade chemical light." + light_color = "#FEF923" + icon_state = "glowstick_yellow" + item_state = "glowstick_yellow" + +/obj/item/device/flashlight/glowstick/radioisotope + name = "radioisotope glowstick" + desc = "A radioisotope powered chemical light. Escaping particles light up the area far brighter on similar levels to flares and for longer" + icon_state = "glowstick_isotope" + item_state = "glowstick_isotope" + + light_range = 8 + light_power = 0.1 + light_color = "#49F37C" diff --git a/code/game/objects/items/devices/hacktool.dm b/code/game/objects/items/devices/hacktool.dm index a374f19d8de..38f1c2afdf6 100644 --- a/code/game/objects/items/devices/hacktool.dm +++ b/code/game/objects/items/devices/hacktool.dm @@ -1,130 +1,130 @@ -/obj/item/device/multitool/hacktool - var/is_hacking = 0 - var/max_known_targets - var/hackspeed = 1 - var/max_level = 4 //what's the max door security_level we can handle? - var/full_override = FALSE //can we override door bolts too? defaults to false for event/safety reasons - - var/in_hack_mode = 0 - var/list/known_targets - var/list/supported_types - var/datum/tgui_state/default/must_hack/hack_state - -/obj/item/device/multitool/hacktool/override - hackspeed = 0.75 - max_level = 5 - full_override = TRUE - -/obj/item/device/multitool/hacktool/New() - ..() - known_targets = list() - max_known_targets = 5 + rand(1,3) - supported_types = list(/obj/machinery/door/airlock) - hack_state = new(src) - -/obj/item/device/multitool/hacktool/Destroy() - for(var/atom/target as anything in known_targets) - target.unregister(OBSERVER_EVENT_DESTROY, src) - known_targets.Cut() - qdel(hack_state) - hack_state = null - return ..() - -/obj/item/device/multitool/hacktool/attackby(var/obj/item/W, var/mob/user) - if(W.is_screwdriver()) - in_hack_mode = !in_hack_mode - playsound(src, W.usesound, 50, 1) - else - ..() - -/obj/item/device/multitool/hacktool/afterattack(atom/A, mob/user) - sanity_check() - - if(!in_hack_mode) - return ..() - - if(!attempt_hack(user, A)) - return 0 - - // Note, if you ever want to expand supported_types, you must manually add the custom state argument to their tgui_interact - // DISABLED: too fancy, too high-effort // A.tgui_interact(user, custom_state = hack_state) - // Just brute-force it - if(istype(A, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/D = A - if(!D.arePowerSystemsOn()) - to_chat(user, "No response from remote, check door power.") - else if(D.locked == TRUE && full_override == FALSE) - to_chat(user, "Unable to override door bolts!") - else if(D.locked == TRUE && full_override == TRUE && D.arePowerSystemsOn()) - to_chat(user, "Door bolts overridden.") - D.unlock() - else if(D.density == TRUE && D.locked == FALSE) - to_chat(user, "Overriding access. Door opening.") - D.open() - else if(D.density == FALSE && D.locked == FALSE) - to_chat(user, "Overriding access. Door closing.") - D.close() - return 1 - -/obj/item/device/multitool/hacktool/proc/attempt_hack(var/mob/user, var/atom/target) - if(is_hacking) - to_chat(user, "You are already hacking!") - return 0 - if(!is_type_in_list(target, supported_types)) - to_chat(user, "\icon[src][bicon(src)] Unable to hack this target, invalid target type.") - return 0 - - var/obj/machinery/door/airlock/D = target - if(D.security_level > max_level) - to_chat(user, "\icon[src][bicon(src)] Target's electronic security is too complex.") - return 0 - - var/found = known_targets.Find(D) - if(found) - known_targets.Swap(1, found) // Move the last hacked item first - return 1 - to_chat(user, "You begin hacking \the [D]...") - is_hacking = 1 - // On average hackin takes ~15 seconds. Fairly small random span to avoid people simply aborting and trying again - // Reduced hack duration to compensate for the reduced functionality, multiplied by door sec level - var/hack_result = do_after(user, (((10 SECONDS + rand(0, 10 SECONDS) + rand(0, 10 SECONDS))*hackspeed)*D.security_level)) - is_hacking = 0 - - if(hack_result && in_hack_mode) - to_chat(user, "Your hacking attempt was succesful!") - user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50) - else - to_chat(user, "Your hacking attempt failed!") - return 0 - - known_targets.Insert(1, D) // Insert the newly hacked target first, - D.register(OBSERVER_EVENT_DESTROY, src, /obj/item/device/multitool/hacktool/proc/on_target_destroy) - return 1 - -/obj/item/device/multitool/hacktool/proc/sanity_check() - if(max_known_targets < 1) max_known_targets = 1 - // Cut away the oldest items if the capacity has been reached - if(known_targets.len > max_known_targets) - for(var/i = (max_known_targets + 1) to known_targets.len) - var/atom/A = known_targets[i] - A.unregister(OBSERVER_EVENT_DESTROY, src) - known_targets.Cut(max_known_targets + 1) - -/obj/item/device/multitool/hacktool/proc/on_target_destroy(var/target) - known_targets -= target - -/datum/tgui_state/default/must_hack - var/obj/item/device/multitool/hacktool/hacktool - -/datum/tgui_state/default/must_hack/New(var/hacktool) - src.hacktool = hacktool - ..() - -/datum/tgui_state/default/must_hack/Destroy() - hacktool = null - return ..() - -/datum/tgui_state/default/must_hack/can_use_topic(src_object, mob/user) - if(!hacktool || !hacktool.in_hack_mode || !(src_object in hacktool.known_targets)) - return STATUS_CLOSE - return ..() +/obj/item/device/multitool/hacktool + var/is_hacking = 0 + var/max_known_targets + var/hackspeed = 1 + var/max_level = 4 //what's the max door security_level we can handle? + var/full_override = FALSE //can we override door bolts too? defaults to false for event/safety reasons + + var/in_hack_mode = 0 + var/list/known_targets + var/list/supported_types + var/datum/tgui_state/default/must_hack/hack_state + +/obj/item/device/multitool/hacktool/override + hackspeed = 0.75 + max_level = 5 + full_override = TRUE + +/obj/item/device/multitool/hacktool/New() + ..() + known_targets = list() + max_known_targets = 5 + rand(1,3) + supported_types = list(/obj/machinery/door/airlock) + hack_state = new(src) + +/obj/item/device/multitool/hacktool/Destroy() + for(var/atom/target as anything in known_targets) + target.unregister(OBSERVER_EVENT_DESTROY, src) + known_targets.Cut() + qdel(hack_state) + hack_state = null + return ..() + +/obj/item/device/multitool/hacktool/attackby(var/obj/item/W, var/mob/user) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + in_hack_mode = !in_hack_mode + playsound(src, W.usesound, 50, 1) + else + ..() + +/obj/item/device/multitool/hacktool/afterattack(atom/A, mob/user) + sanity_check() + + if(!in_hack_mode) + return ..() + + if(!attempt_hack(user, A)) + return 0 + + // Note, if you ever want to expand supported_types, you must manually add the custom state argument to their tgui_interact + // DISABLED: too fancy, too high-effort // A.tgui_interact(user, custom_state = hack_state) + // Just brute-force it + if(istype(A, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/D = A + if(!D.arePowerSystemsOn()) + to_chat(user, "No response from remote, check door power.") + else if(D.locked == TRUE && full_override == FALSE) + to_chat(user, "Unable to override door bolts!") + else if(D.locked == TRUE && full_override == TRUE && D.arePowerSystemsOn()) + to_chat(user, "Door bolts overridden.") + D.unlock() + else if(D.density == TRUE && D.locked == FALSE) + to_chat(user, "Overriding access. Door opening.") + D.open() + else if(D.density == FALSE && D.locked == FALSE) + to_chat(user, "Overriding access. Door closing.") + D.close() + return 1 + +/obj/item/device/multitool/hacktool/proc/attempt_hack(var/mob/user, var/atom/target) + if(is_hacking) + to_chat(user, "You are already hacking!") + return 0 + if(!is_type_in_list(target, supported_types)) + to_chat(user, "\icon[src][bicon(src)] Unable to hack this target, invalid target type.") + return 0 + + var/obj/machinery/door/airlock/D = target + if(D.security_level > max_level) + to_chat(user, "\icon[src][bicon(src)] Target's electronic security is too complex.") + return 0 + + var/found = known_targets.Find(D) + if(found) + known_targets.Swap(1, found) // Move the last hacked item first + return 1 + to_chat(user, "You begin hacking \the [D]...") + is_hacking = 1 + // On average hackin takes ~15 seconds. Fairly small random span to avoid people simply aborting and trying again + // Reduced hack duration to compensate for the reduced functionality, multiplied by door sec level + var/hack_result = do_after(user, (((10 SECONDS + rand(0, 10 SECONDS) + rand(0, 10 SECONDS))*hackspeed)*D.security_level)) + is_hacking = 0 + + if(hack_result && in_hack_mode) + to_chat(user, "Your hacking attempt was succesful!") + user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50) + else + to_chat(user, "Your hacking attempt failed!") + return 0 + + known_targets.Insert(1, D) // Insert the newly hacked target first, + D.register(OBSERVER_EVENT_DESTROY, src, /obj/item/device/multitool/hacktool/proc/on_target_destroy) + return 1 + +/obj/item/device/multitool/hacktool/proc/sanity_check() + if(max_known_targets < 1) max_known_targets = 1 + // Cut away the oldest items if the capacity has been reached + if(known_targets.len > max_known_targets) + for(var/i = (max_known_targets + 1) to known_targets.len) + var/atom/A = known_targets[i] + A.unregister(OBSERVER_EVENT_DESTROY, src) + known_targets.Cut(max_known_targets + 1) + +/obj/item/device/multitool/hacktool/proc/on_target_destroy(var/target) + known_targets -= target + +/datum/tgui_state/default/must_hack + var/obj/item/device/multitool/hacktool/hacktool + +/datum/tgui_state/default/must_hack/New(var/hacktool) + src.hacktool = hacktool + ..() + +/datum/tgui_state/default/must_hack/Destroy() + hacktool = null + return ..() + +/datum/tgui_state/default/must_hack/can_use_topic(src_object, mob/user) + if(!hacktool || !hacktool.in_hack_mode || !(src_object in hacktool.known_targets)) + return STATUS_CLOSE + return ..() diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 784fb4ced30..0112b25dd3d 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -54,7 +54,7 @@ else to_chat(user, "[src] already has a diode.") - else if(W.is_screwdriver()) + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(diode) to_chat(user, "You remove the [diode.name] from the [src].") diode.loc = get_turf(src.loc) diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 54cc46d802c..f188f054312 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -1,230 +1,230 @@ - -// Light Replacer (LR) -// -// ABOUT THE DEVICE -// -// This is a device supposedly to be used by Janitors and Janitor Cyborgs which will -// allow them to easily replace lights. This was mostly designed for Janitor Cyborgs since -// they don't have hands or a way to replace lightbulbs. -// -// HOW IT WORKS -// -// You attack a light fixture with it, if the light fixture is broken it will replace the -// light fixture with a working light; the broken light is then placed on the floor for the -// user to then pickup with a trash bag. If it's empty then it will just place a light in the fixture. -// -// HOW TO REFILL THE DEVICE -// -// It can be manually refilled or by clicking on a storage item containing lights. -// If it's part of a robot module, it will charge when the Robot is inside a Recharge Station. -// -// EMAGGED FEATURES -// -// NOTICE: The Cyborg cannot use the emagged Light Replacer and the light's explosion was nerfed. It cannot create holes in the station anymore. -// -// I'm not sure everyone will react the emag's features so please say what your opinions are of it. -// -// When emagged it will rig every light it replaces, which will explode when the light is on. -// This is VERY noticable, even the device's name changes when you emag it so if anyone -// examines you when you're holding it in your hand, you will be discovered. -// It will also be very obvious who is setting all these lights off, since only Janitor Borgs and Janitors have easy -// access to them, and only one of them can emag their device. -// -// The explosion cannot insta-kill anyone with 30% or more health. - -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - - -/obj/item/device/lightreplacer - - name = "light replacer" - desc = "A device to automatically replace lights. Refill with working lightbulbs or sheets of glass." - force = 8 - icon = 'icons/obj/janitor.dmi' - icon_state = "lightreplacer0" - slot_flags = SLOT_BELT - origin_tech = list(TECH_MAGNET = 3, TECH_MATERIAL = 2) - - var/max_uses = 32 - var/uses = 32 - var/emagged = 0 - var/failmsg = "" - var/charge = 0 - var/selected_color = LIGHT_COLOR_INCANDESCENT_TUBE //Default color! - - // Eating used bulbs gives us bulb shards - var/bulb_shards = 0 - // when we get this many shards, we get a free bulb. - var/shards_required = 4 - -/obj/item/device/lightreplacer/New() - failmsg = "The [name]'s refill light blinks red." - ..() - -/obj/item/device/lightreplacer/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - . += "It has [uses] lights remaining." - -/obj/item/device/lightreplacer/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/stack/material) && W.get_material_name() == "glass") - var/obj/item/stack/G = W - if(uses >= max_uses) - to_chat(user, "[src.name] is full.") - return - else if(G.use(1)) - add_uses(16) //Autolathe converts 1 sheet into 16 lights. - to_chat(user, "You insert a piece of glass into \the [src.name]. You have [uses] light\s remaining.") - return - else - to_chat(user, "You need one sheet of glass to replace lights.") - - if(istype(W, /obj/item/weapon/light)) - var/new_bulbs = 0 - var/obj/item/weapon/light/L = W - if(L.status == 0) // LIGHT OKAY - if(uses < max_uses) - if(!user.unEquip(W)) - return - add_uses(1) - qdel(L) - else - if(!user.unEquip(W)) - return - new_bulbs += AddShards(1) - qdel(L) - if(new_bulbs != 0) - playsound(src, 'sound/machines/ding.ogg', 50, 1) - to_chat(user, "You insert \the [L.name] into \the [src.name]. You have [uses] light\s remaining.") - return - - if(istype(W, /obj/item/weapon/storage)) - var/obj/item/weapon/storage/S = W - var/found_lightbulbs = FALSE - var/replaced_something = TRUE - - for(var/obj/item/I in S.contents) - if(istype(I,/obj/item/weapon/light)) - var/obj/item/weapon/light/L = I - found_lightbulbs = TRUE - if(src.uses >= max_uses) - break - if(L.status == LIGHT_OK) - replaced_something = TRUE - add_uses(1) - qdel(L) - - else if(L.status == LIGHT_BROKEN || L.status == LIGHT_BURNED) - replaced_something = TRUE - AddShards(1) - qdel(L) - - if(!found_lightbulbs) - to_chat(user, "\The [S] contains no bulbs.") - return - - if(!replaced_something && src.uses == max_uses) - to_chat(user, "\The [src] is full!") - return - - to_chat(user, "You fill \the [src] with lights from \the [S].") - -/obj/item/device/lightreplacer/attack_self(mob/user) - /* // This would probably be a bit OP. If you want it though, uncomment the code. - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - if(R.emagged) - src.Emag() - to_chat(usr, You short circuit the [src].") - return - */ - to_chat(usr, "It has [uses] lights remaining.") - var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null - if(new_color) - selected_color = new_color - to_chat(usr, "The light color has been changed.") - -/obj/item/device/lightreplacer/update_icon() - icon_state = "lightreplacer[emagged]" - - -/obj/item/device/lightreplacer/proc/Use(var/mob/user) - - playsound(src, 'sound/machines/click.ogg', 50, 1) - add_uses(-1) - return 1 - -// Negative numbers will subtract -/obj/item/device/lightreplacer/proc/add_uses(var/amount = 1) - uses = min(max(uses + amount, 0), max_uses) - - -/obj/item/device/lightreplacer/proc/AddShards(amount = 1) - bulb_shards += amount - var/new_bulbs = round(bulb_shards / shards_required) - if(new_bulbs > 0) - add_uses(new_bulbs) - bulb_shards = bulb_shards % shards_required - return new_bulbs - -/obj/item/device/lightreplacer/proc/Charge(var/mob/user, var/amount = 1) - charge += amount - if(charge > 6) - add_uses(1) - charge = 0 - -/obj/item/device/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U) - - if(target.status != LIGHT_OK) - if(CanUse(U)) - if(!Use(U)) return - to_chat(U, "You replace the [target.get_fitting_name()] with the [src].") - - if(target.status != LIGHT_EMPTY) - var/new_bulbs = AddShards(1) - if(new_bulbs != 0) - to_chat(U, "\The [src] has fabricated a new bulb from the broken bulbs it has stored. It now has [uses] uses.") - playsound(src, 'sound/machines/ding.ogg', 50, 1) - target.status = LIGHT_EMPTY - target.installed_light = null //Remove the light! - target.update() - - var/obj/item/weapon/light/L2 = new target.light_type() - L2.brightness_color = selected_color - target.insert_bulb(L2) //Call the insertion proc. - target.update() - - if(target.on && target.rigged) - target.explode() - return - - else - to_chat(U, failmsg) - return - else - to_chat(U, "There is a working [target.get_fitting_name()] already inserted.") - return - -/obj/item/device/lightreplacer/emag_act(var/remaining_charges, var/mob/user) - emagged = !emagged - playsound(src, "sparks", 100, 1) - update_icon() - return 1 - -//Can you use it? - -/obj/item/device/lightreplacer/proc/CanUse(var/mob/living/user) - src.add_fingerprint(user) - //Not sure what else to check for. Maybe if clumsy? - if(uses > 0) - return 1 - else - return 0 - -#undef LIGHT_OK -#undef LIGHT_EMPTY -#undef LIGHT_BROKEN + +// Light Replacer (LR) +// +// ABOUT THE DEVICE +// +// This is a device supposedly to be used by Janitors and Janitor Cyborgs which will +// allow them to easily replace lights. This was mostly designed for Janitor Cyborgs since +// they don't have hands or a way to replace lightbulbs. +// +// HOW IT WORKS +// +// You attack a light fixture with it, if the light fixture is broken it will replace the +// light fixture with a working light; the broken light is then placed on the floor for the +// user to then pickup with a trash bag. If it's empty then it will just place a light in the fixture. +// +// HOW TO REFILL THE DEVICE +// +// It can be manually refilled or by clicking on a storage item containing lights. +// If it's part of a robot module, it will charge when the Robot is inside a Recharge Station. +// +// EMAGGED FEATURES +// +// NOTICE: The Cyborg cannot use the emagged Light Replacer and the light's explosion was nerfed. It cannot create holes in the station anymore. +// +// I'm not sure everyone will react the emag's features so please say what your opinions are of it. +// +// When emagged it will rig every light it replaces, which will explode when the light is on. +// This is VERY noticable, even the device's name changes when you emag it so if anyone +// examines you when you're holding it in your hand, you will be discovered. +// It will also be very obvious who is setting all these lights off, since only Janitor Borgs and Janitors have easy +// access to them, and only one of them can emag their device. +// +// The explosion cannot insta-kill anyone with 30% or more health. + +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + + +/obj/item/device/lightreplacer + + name = "light replacer" + desc = "A device to automatically replace lights. Refill with working lightbulbs or sheets of glass." + force = 8 + icon = 'icons/obj/janitor.dmi' + icon_state = "lightreplacer0" + slot_flags = SLOT_BELT + origin_tech = list(TECH_MAGNET = 3, TECH_MATERIAL = 2) + + var/max_uses = 32 + var/uses = 32 + var/emagged = 0 + var/failmsg = "" + var/charge = 0 + var/selected_color = LIGHT_COLOR_INCANDESCENT_TUBE //Default color! + + // Eating used bulbs gives us bulb shards + var/bulb_shards = 0 + // when we get this many shards, we get a free bulb. + var/shards_required = 4 + +/obj/item/device/lightreplacer/New() + failmsg = "The [name]'s refill light blinks red." + ..() + +/obj/item/device/lightreplacer/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + . += "It has [uses] lights remaining." + +/obj/item/device/lightreplacer/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/stack/material) && W.get_material_name() == "glass") + var/obj/item/stack/G = W + if(uses >= max_uses) + to_chat(user, "[src.name] is full.") + return + else if(G.use(1)) + add_uses(16) //Autolathe converts 1 sheet into 16 lights. + to_chat(user, "You insert a piece of glass into \the [src.name]. You have [uses] light\s remaining.") + return + else + to_chat(user, "You need one sheet of glass to replace lights.") + + if(istype(W, /obj/item/weapon/light)) + var/new_bulbs = 0 + var/obj/item/weapon/light/L = W + if(L.status == 0) // LIGHT OKAY + if(uses < max_uses) + if(!user.unEquip(W)) + return + add_uses(1) + qdel(L) + else + if(!user.unEquip(W)) + return + new_bulbs += AddShards(1) + qdel(L) + if(new_bulbs != 0) + playsound(src, 'sound/machines/ding.ogg', 50, 1) + to_chat(user, "You insert \the [L.name] into \the [src.name]. You have [uses] light\s remaining.") + return + + if(istype(W, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = W + var/found_lightbulbs = FALSE + var/replaced_something = TRUE + + for(var/obj/item/I in S.contents) + if(istype(I,/obj/item/weapon/light)) + var/obj/item/weapon/light/L = I + found_lightbulbs = TRUE + if(src.uses >= max_uses) + break + if(L.status == LIGHT_OK) + replaced_something = TRUE + add_uses(1) + qdel(L) + + else if(L.status == LIGHT_BROKEN || L.status == LIGHT_BURNED) + replaced_something = TRUE + AddShards(1) + qdel(L) + + if(!found_lightbulbs) + to_chat(user, "\The [S] contains no bulbs.") + return + + if(!replaced_something && src.uses == max_uses) + to_chat(user, "\The [src] is full!") + return + + to_chat(user, "You fill \the [src] with lights from \the [S].") + +/obj/item/device/lightreplacer/attack_self(mob/user) + /* // This would probably be a bit OP. If you want it though, uncomment the code. + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(R.emagged) + src.Emag() + to_chat(usr, You short circuit the [src].") + return + */ + to_chat(usr, "It has [uses] lights remaining.") + var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null + if(new_color) + selected_color = new_color + to_chat(usr, "The light color has been changed.") + +/obj/item/device/lightreplacer/update_icon() + icon_state = "lightreplacer[emagged]" + + +/obj/item/device/lightreplacer/proc/Use(var/mob/user) + + playsound(src, 'sound/machines/click.ogg', 50, 1) + add_uses(-1) + return 1 + +// Negative numbers will subtract +/obj/item/device/lightreplacer/proc/add_uses(var/amount = 1) + uses = min(max(uses + amount, 0), max_uses) + + +/obj/item/device/lightreplacer/proc/AddShards(amount = 1) + bulb_shards += amount + var/new_bulbs = round(bulb_shards / shards_required) + if(new_bulbs > 0) + add_uses(new_bulbs) + bulb_shards = bulb_shards % shards_required + return new_bulbs + +/obj/item/device/lightreplacer/proc/Charge(var/mob/user, var/amount = 1) + charge += amount + if(charge > 6) + add_uses(1) + charge = 0 + +/obj/item/device/lightreplacer/proc/ReplaceLight(var/obj/machinery/light/target, var/mob/living/U) + + if(target.status != LIGHT_OK) + if(CanUse(U)) + if(!Use(U)) return + to_chat(U, "You replace the [target.get_fitting_name()] with the [src].") + + if(target.status != LIGHT_EMPTY) + var/new_bulbs = AddShards(1) + if(new_bulbs != 0) + to_chat(U, "\The [src] has fabricated a new bulb from the broken bulbs it has stored. It now has [uses] uses.") + playsound(src, 'sound/machines/ding.ogg', 50, 1) + target.status = LIGHT_EMPTY + target.installed_light = null //Remove the light! + target.update() + + var/obj/item/weapon/light/L2 = new target.light_type() + L2.brightness_color = selected_color + target.insert_bulb(L2) //Call the insertion proc. + target.update() + + if(target.on && target.rigged) + target.explode() + return + + else + to_chat(U, failmsg) + return + else + to_chat(U, "There is a working [target.get_fitting_name()] already inserted.") + return + +/obj/item/device/lightreplacer/emag_act(var/remaining_charges, var/mob/user) + emagged = !emagged + playsound(src, "sparks", 100, 1) + update_icon() + return 1 + +//Can you use it? + +/obj/item/device/lightreplacer/proc/CanUse(var/mob/living/user) + src.add_fingerprint(user) + //Not sure what else to check for. Maybe if clumsy? + if(uses > 0) + return 1 + else + return 0 + +#undef LIGHT_OK +#undef LIGHT_EMPTY +#undef LIGHT_BROKEN #undef LIGHT_BURNED \ No newline at end of file diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 6fe9aea398e..a320c6b490a 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -1,98 +1,95 @@ -/** - * Multitool -- A multitool is used for hacking electronic devices. - * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. - * - */ - -/obj/item/device/multitool - name = "multitool" - desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." - description_info = "You can use this on airlocks or APCs to try to hack them without cutting wires." - icon_state = "multitool" - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - drop_sound = 'sound/items/drop/multitool.ogg' - pickup_sound = 'sound/items/pickup/multitool.ogg' - - matter = list(MAT_STEEL = 50,MAT_GLASS = 20) - - var/mode_index = 1 - var/toolmode = MULTITOOL_MODE_STANDARD - var/list/modes = list(MULTITOOL_MODE_STANDARD, MULTITOOL_MODE_INTCIRCUITS) - - origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) - var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage - var/obj/machinery/clonepod/connecting //same for cryopod linkage - var/obj/machinery/connectable //Used to connect machinery. - var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool. - toolspeed = 1 - tool_qualities = list(TOOL_MULTITOOL) - -/obj/item/device/multitool/attack_self(mob/living/user) - var/choice = tgui_alert(usr, "What do you want to do with \the [src]?", "Multitool Menu", list("Switch Mode", "Clear Buffers", "Cancel")) - switch(choice) - if("Cancel") - to_chat(user,"You lower \the [src].") - return - if("Clear Buffers") - to_chat(user,"You clear \the [src]'s memory.") - buffer = null - connecting = null - connectable = null - weakref_wiring = null - accepting_refs = 0 - if(toolmode == MULTITOOL_MODE_INTCIRCUITS) - accepting_refs = 1 - if("Switch Mode") - mode_switch(user) - - update_icon() - - return ..() - -/obj/item/device/multitool/proc/mode_switch(mob/living/user) - if(mode_index + 1 > modes.len) mode_index = 1 - - else - mode_index += 1 - - toolmode = modes[mode_index] - to_chat(user,"\The [src] is now set to [toolmode].") - - accepting_refs = (toolmode == MULTITOOL_MODE_INTCIRCUITS) - - return - -/obj/item/device/multitool/is_multitool() - return TRUE - -/obj/item/device/multitool/cyborg - name = "multitool" - desc = "Optimised and stripped-down version of a regular multitool." - toolspeed = 0.5 - - - -/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool - name = "Precursor Alpha Object - Pulse Tool" - desc = "This ancient object appears to be an electrical tool. \ - It has a simple mechanism at the handle, which will cause a pulse of \ - energy to be emitted from the head of the tool. This can be used on a \ - conductive object such as a wire, in order to send a pulse signal through it.\ -

                    \ - These qualities make this object somewhat similar in purpose to the common \ - multitool, and can probably be used for tasks such as direct interfacing with \ - an airlock, if one knows how." - value = CATALOGUER_REWARD_EASY - -/obj/item/device/multitool/alien - name = "alien multitool" - desc = "An omni-technological interface." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool) - icon = 'icons/obj/abductor.dmi' - icon_state = "multitool" - toolspeed = 0.1 - origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5) +/** + * Multitool -- A multitool is used for hacking electronic devices. + * TO-DO -- Using it as a power measurement tool for cables etc. Nannek. + * + */ + +/obj/item/device/multitool + name = "multitool" + desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." + description_info = "You can use this on airlocks or APCs to try to hack them without cutting wires." + icon_state = "multitool" + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + drop_sound = 'sound/items/drop/multitool.ogg' + pickup_sound = 'sound/items/pickup/multitool.ogg' + + matter = list(MAT_STEEL = 50,MAT_GLASS = 20) + + var/mode_index = 1 + var/toolmode = MULTITOOL_MODE_STANDARD + var/list/modes = list(MULTITOOL_MODE_STANDARD, MULTITOOL_MODE_INTCIRCUITS) + + origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) + var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage + var/obj/machinery/clonepod/connecting //same for cryopod linkage + var/obj/machinery/connectable //Used to connect machinery. + var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool. + toolspeed = 1 + tool_qualities = list(TOOL_MULTITOOL) + +/obj/item/device/multitool/attack_self(mob/living/user) + var/choice = tgui_alert(usr, "What do you want to do with \the [src]?", "Multitool Menu", list("Switch Mode", "Clear Buffers", "Cancel")) + switch(choice) + if("Cancel") + to_chat(user,"You lower \the [src].") + return + if("Clear Buffers") + to_chat(user,"You clear \the [src]'s memory.") + buffer = null + connecting = null + connectable = null + weakref_wiring = null + accepting_refs = 0 + if(toolmode == MULTITOOL_MODE_INTCIRCUITS) + accepting_refs = 1 + if("Switch Mode") + mode_switch(user) + + update_icon() + + return ..() + +/obj/item/device/multitool/proc/mode_switch(mob/living/user) + if(mode_index + 1 > modes.len) mode_index = 1 + + else + mode_index += 1 + + toolmode = modes[mode_index] + to_chat(user,"\The [src] is now set to [toolmode].") + + accepting_refs = (toolmode == MULTITOOL_MODE_INTCIRCUITS) + + return + +/obj/item/device/multitool/cyborg + name = "multitool" + desc = "Optimised and stripped-down version of a regular multitool." + toolspeed = 0.5 + + + +/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool + name = "Precursor Alpha Object - Pulse Tool" + desc = "This ancient object appears to be an electrical tool. \ + It has a simple mechanism at the handle, which will cause a pulse of \ + energy to be emitted from the head of the tool. This can be used on a \ + conductive object such as a wire, in order to send a pulse signal through it.\ +

                    \ + These qualities make this object somewhat similar in purpose to the common \ + multitool, and can probably be used for tasks such as direct interfacing with \ + an airlock, if one knows how." + value = CATALOGUER_REWARD_EASY + +/obj/item/device/multitool/alien + name = "alien multitool" + desc = "An omni-technological interface." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_multitool) + icon = 'icons/obj/abductor.dmi' + icon_state = "multitool" + toolspeed = 0.1 + origin_tech = list(TECH_MAGNET = 5, TECH_ENGINEERING = 5) diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index a944d8c1721..932491fae59 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -1,537 +1,537 @@ -var/global/list/radio_channels_by_freq = list( - num2text(PUB_FREQ) = "Common", - num2text(AI_FREQ) = "AI Private", - num2text(ENT_FREQ) = "Entertainment", - num2text(ERT_FREQ) = "Response Team", - num2text(COMM_FREQ)= "Command", - num2text(ENG_FREQ) = "Engineering", - num2text(MED_FREQ) = "Medical", - num2text(MED_I_FREQ)="Medical(I)", - num2text(SEC_FREQ) = "Security", - num2text(SEC_I_FREQ)="Security(I)", - num2text(SCI_FREQ) = "Science", - num2text(SUP_FREQ) = "Supply", - num2text(SRV_FREQ) = "Service", - num2text(EXP_FREQ) = "Explorer" - ) - -GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) - -/obj/item/device/paicard - name = "personal AI device" - icon = 'icons/obj/pda.dmi' - icon_state = "pai" - item_state = "electronic" - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_BELT | SLOT_HOLSTER - origin_tech = list(TECH_DATA = 2) - show_messages = 0 - preserve_item = 1 - - var/obj/item/device/radio/borg/pai/radio - var/looking_for_personality = 0 - var/mob/living/silicon/pai/pai - var/image/screen_layer - var/screen_color = "#00ff0d" - var/last_notify = 0 - var/screen_msg - -/obj/item/device/paicard/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/weapon/rig/rig = src.get_rig() - if(istype(rig)) - rig.forced_move(direction, user) - -/obj/item/device/paicard/New() - ..() - add_overlay("pai-off") - -/obj/item/device/paicard/Destroy() - //Will stop people throwing friend pAIs into the singularity so they can respawn - if(!isnull(pai)) - pai.death(0) - QDEL_NULL(radio) - return ..() - -// VOREStation Edit - Allow everyone to become a pAI -/obj/item/device/paicard/attack_ghost(mob/user as mob) - if(pai != null) //Have a person in them already? - return ..() - if(is_damage_critical()) - to_chat(usr, "That card is too damaged to activate!") - return - var/time_till_respawn = user.time_till_respawn() - if(time_till_respawn == -1) // Special case, never allowed to respawn - to_chat(usr, "Respawning is not allowed!") - else if(time_till_respawn) // Nonzero time to respawn - to_chat(usr, "You can't do that yet! You died too recently. You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes.") - return - if(jobban_isbanned(usr, "pAI")) - to_chat(usr,"You cannot join a pAI card when you are banned from playing as a pAI.") - return - - for(var/ourkey in paikeys) - if(ourkey == user.ckey) - to_chat(usr, "You can't just rejoin any old pAI card!!! Your card still exists.") - return - - var/choice = tgui_alert(user, "You sure you want to inhabit this PAI, or submit yourself to being recruited?", "Confirmation", list("Inhabit", "Recruit", "Cancel")) - if(choice == "Cancel") - return ..() - if(choice == "Recruit") - paiController.recruitWindow(user) - return ..() - choice = tgui_alert(user, "Do you want to load your pAI data?", "Load", list("Yes", "No")) - var/actual_pai_name - var/turf/location = get_turf(src) - if(choice == "No") - var/pai_name = tgui_input_text(user, "Choose your character's name", "Character Name") - actual_pai_name = sanitize_name(pai_name, ,1) - if(isnull(actual_pai_name)) - return ..() - if(istype(src , /obj/item/device/paicard/typeb)) - var/obj/item/device/paicard/typeb/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - new_pai.SetName(actual_pai_name) - else - var/obj/item/device/paicard/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - new_pai.SetName(actual_pai_name) - - if(choice == "Yes") - if(istype(src , /obj/item/device/paicard/typeb)) - var/obj/item/device/paicard/typeb/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - if(!new_pai.savefile_load(new_pai)) - var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") - actual_pai_name = sanitize_name(pai_name, ,1) - if(isnull(actual_pai_name)) - return ..() - else - var/obj/item/device/paicard/card = new(location) - var/mob/living/silicon/pai/new_pai = new(card) - new_pai.key = user.key - paikeys |= new_pai.ckey - card.setPersonality(new_pai) - if(!new_pai.savefile_load(new_pai)) - var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") - actual_pai_name = sanitize_name(pai_name, ,1) - if(isnull(actual_pai_name)) - return ..() - - qdel(src) - return ..() - -// VOREStation Edit End - -/obj/item/device/paicard/proc/access_screen(mob/user) - if(is_damage_critical()) - to_chat(user, "WARNING: CRITICAL HARDWARE FAILURE, SERVICE DEVICE IMMEDIATELY") - return - if (!in_range(src, user)) - return - user.set_machine(src) - var/dat = {" - - - - - - - "} - - if(pai) - dat += {" - Personal AI Device

                    - - - - - - - - - - - - - - - -
                    [pai.name]
                    Integrity: [pai.health]
                    Prime directive:[pai.pai_law0]
                    Additional directives:[pai.pai_laws]
                    -
                    - "} - dat += {" - - -
                    - Configure Directives -
                    - "} - if(pai && (!pai.master_dna || !pai.master)) - dat += {" - - -
                    - Imprint Master DNA -
                    - "} - dat += "
                    " - if(radio) - dat += "Radio Uplink" - dat += {" - - - - - - - - - -
                    Transmit:[radio.broadcasting ? "En" : "Dis" ]abled - -
                    Receive:[radio.listening ? "En" : "Dis" ]abled - -
                    -
                    - "} - else // - dat += "Radio Uplink
                    " - dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
                    " - /* - //A button for instantly deleting people from the game is lame, especially considering that pAIs on our server tend to activate without a master. - dat += {" - - -
                    Wipe current pAI personality - -
                    - "} - */ - if(screen_msg) - dat += "Message from [pai.name]
                    [screen_msg]" - else - if(looking_for_personality) - dat += {" - pAI Request Module

                    -

                    Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

                    - Searching for personalities

                    - - - - - -
                    - Refresh available personalities -

                    - "} - else - dat += {" - pAI Request Module

                    -

                    No personality is installed.

                    - - - - -
                    Request personality -
                    -
                    -

                    Each time this button is pressed, a request will be sent out to any available personalities. Check back often give plenty of time for personalities to respond. This process could take anywhere from 15 seconds to several minutes, depending on the available personalities' timeliness.

                    - "} - user << browse(dat, "window=paicard") - onclose(user, "paicard") - return - -/obj/item/device/paicard/Topic(href, href_list) - - if(!usr || usr.stat) - return - - if(href_list["setdna"]) - if(pai.master_dna) - return - var/mob/M = usr - if(!istype(M, /mob/living/carbon)) - to_chat(usr, "You don't have any DNA, or your DNA is incompatible with this device.") - else - var/datum/dna/dna = usr.dna - pai.master = M.real_name - pai.master_dna = dna.unique_enzymes - to_chat(pai, "

                    You have been bound to a new master.

                    ") - if(href_list["request"]) - src.looking_for_personality = 1 - paiController.findPAI(src, usr) - if(href_list["wipe"]) - var/confirm = tgui_alert(usr, "Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe", list("Yes", "No")) - if(confirm == "Yes") - for(var/mob/M in src) - to_chat(M, "

                    You feel yourself slipping away from reality.

                    ") - to_chat(M, "

                    Byte by byte you lose your sense of self.

                    ") - to_chat(M, "

                    Your mental faculties leave you.

                    ") - to_chat(M, "
                    oblivion...
                    ") - M.death(0) - removePersonality() - if(href_list["wires"]) - var/t1 = text2num(href_list["wires"]) - switch(t1) - if(4) - radio.ToggleBroadcast() - if(2) - radio.ToggleReception() - if(href_list["setlaws"]) - var/newlaws = sanitize(tgui_input_text(usr, "Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws, multiline = TRUE, prevent_enter = TRUE)) - if(newlaws) - pai.pai_laws = newlaws - to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") - to_chat(pai, "Prime Directive:
                    [pai.pai_law0]") - to_chat(pai, "Supplemental Directives:
                    [pai.pai_laws]") - attack_self(usr) - -// WIRE_SIGNAL = 1 -// WIRE_RECEIVE = 2 -// WIRE_TRANSMIT = 4 - -/obj/item/device/paicard/proc/setPersonality(mob/living/silicon/pai/personality) - src.pai = personality - setEmotion(1) - -/obj/item/device/paicard/proc/removePersonality() - src.pai = null - cut_overlays() - setEmotion(16) - -/obj/item/device/paicard - var/current_emotion = 1 -/obj/item/device/paicard/proc/setEmotion(var/emotion) - if(pai) - cut_overlays() - qdel(screen_layer) - screen_layer = null - switch(emotion) - if(1) screen_layer = image(icon, "pai-neutral") - if(2) screen_layer = image(icon, "pai-what") - if(3) screen_layer = image(icon, "pai-happy") - if(4) screen_layer = image(icon, "pai-cat") - if(5) screen_layer = image(icon, "pai-extremely-happy") - if(6) screen_layer = image(icon, "pai-face") - if(7) screen_layer = image(icon, "pai-laugh") - if(8) screen_layer = image(icon, "pai-sad") - if(9) screen_layer = image(icon, "pai-angry") - if(10) screen_layer = image(icon, "pai-silly") - if(11) screen_layer = image(icon, "pai-nose") - if(12) screen_layer = image(icon, "pai-smirk") - if(13) screen_layer = image(icon, "pai-exclamation") - if(14) screen_layer = image(icon, "pai-question") - if(15) screen_layer = image(icon, "pai-blank") - if(16) screen_layer = image(icon, "pai-off") - - screen_layer.color = pai.eye_color - add_overlay(screen_layer) - current_emotion = emotion - -/obj/item/device/paicard/proc/alertUpdate() - if(pai) - return - if(last_notify == 0 || (5 MINUTES <= world.time - last_notify)) - audible_message("\The [src] flashes a message across its screen, \"Additional personalities available for download.\"", hearing_distance = world.view, runemessage = "bleeps!") - last_notify = world.time - -/obj/item/device/paicard/emp_act(severity) - for(var/mob/M in src) - M.emp_act(severity) - -/obj/item/device/paicard/ex_act(severity) - if(pai) - pai.ex_act(severity) - else - qdel(src) - -/obj/item/device/paicard/see_emote(mob/living/M, text) - if(pai && pai.client && !pai.canmove) - var/rendered = "[text]" - pai.show_message(rendered, 2) - ..() - -/obj/item/device/paicard/show_message(msg, type, alt, alt_type) - if(pai && pai.client) - var/rendered = "[msg]" - pai.show_message(rendered, type) - ..() - - -// VoreEdit: Living Machine Stuff after this. -// This adds a var and proc for all machines to take a pAI. (The pAI can't control anything, it's just for RP.) -// You need to add usage of the proc to each machine to actually add support. For an example of this, see code\modules\food\kitchen\microwave.dm -/obj/machinery - var/obj/item/device/paicard/paicard = null - -/obj/machinery/proc/insertpai(mob/user, obj/item/device/paicard/card) - //var/obj/item/paicard/card = I - var/mob/living/silicon/pai/AI = card.pai - if(paicard) - to_chat(user, span_notice("This bot is already under PAI Control!")) - return - if(!istype(card)) // TODO: Add sleevecard support. - return - if(!card.pai) - to_chat(user, span_notice("This card does not currently have a personality!")) - return - paicard = card - user.unEquip(card) - card.forceMove(src) - AI.client.eye = src - to_chat(AI, span_notice("Your location is [card.loc].")) // DEBUG. TODO: Make unfolding the chassis trigger an eject. - name = AI.name - to_chat(AI, span_notice("You feel a tingle in your circuits as your systems interface with \the [initial(src.name)].")) - -/obj/machinery/proc/ejectpai(mob/user) - if(paicard) - var/mob/living/silicon/pai/AI = paicard.pai - paicard.forceMove(src.loc) - AI.client.eye = AI - paicard = null - name = initial(src.name) - to_chat(AI, span_notice("You feel a tad claustrophobic as your mind closes back into your card, ejecting from \the [initial(src.name)].")) - if(user) - to_chat(user, span_notice("You eject the card from \the [initial(src.name)].")) - -/////////////////////////////// -//////////pAI Radios////////// -/////////////////////////////// -//Thanks heroman! - -/obj/item/device/radio/borg/pai - name = "integrated radio" - icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. - icon_state = "radio" - loudspeaker = FALSE - -/obj/item/device/radio/borg/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) - return - -/obj/item/device/radio/borg/pai/recalculateChannels() - if(!istype(loc,/obj/item/device/paicard)) - return - var/obj/item/device/paicard/card = loc - secure_radio_connections = list() - channels = list() - - for(var/internal_chan in internal_channels) - var/ch_name = radio_channels_by_freq[internal_chan] - if(has_channel_access(card.pai, internal_chan)) - channels += ch_name - channels[ch_name] = 1 - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - -/obj/item/device/paicard/typeb - name = "personal AI device" - icon = 'icons/obj/paicard.dmi' - -/obj/random/paicard - name = "personal AI device spawner" - icon = 'icons/obj/paicard.dmi' - icon_state = "pai" - -/obj/random/paicard/item_to_spawn() - return pick(/obj/item/device/paicard ,/obj/item/device/paicard/typeb) - -/obj/item/device/paicard/digest_act(var/atom/movable/item_storage = null) - if(pai.digestable) - return ..() +var/global/list/radio_channels_by_freq = list( + num2text(PUB_FREQ) = "Common", + num2text(AI_FREQ) = "AI Private", + num2text(ENT_FREQ) = "Entertainment", + num2text(ERT_FREQ) = "Response Team", + num2text(COMM_FREQ)= "Command", + num2text(ENG_FREQ) = "Engineering", + num2text(MED_FREQ) = "Medical", + num2text(MED_I_FREQ)="Medical(I)", + num2text(SEC_FREQ) = "Security", + num2text(SEC_I_FREQ)="Security(I)", + num2text(SCI_FREQ) = "Science", + num2text(SUP_FREQ) = "Supply", + num2text(SRV_FREQ) = "Service", + num2text(EXP_FREQ) = "Explorer" + ) + +GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) + +/obj/item/device/paicard + name = "personal AI device" + icon = 'icons/obj/pda.dmi' + icon_state = "pai" + item_state = "electronic" + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_BELT | SLOT_HOLSTER + origin_tech = list(TECH_DATA = 2) + show_messages = 0 + preserve_item = 1 + + var/obj/item/device/radio/borg/pai/radio + var/looking_for_personality = 0 + var/mob/living/silicon/pai/pai + var/image/screen_layer + var/screen_color = "#00ff0d" + var/last_notify = 0 + var/screen_msg + +/obj/item/device/paicard/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.forced_move(direction, user) + +/obj/item/device/paicard/New() + ..() + add_overlay("pai-off") + +/obj/item/device/paicard/Destroy() + //Will stop people throwing friend pAIs into the singularity so they can respawn + if(!isnull(pai)) + pai.death(0) + QDEL_NULL(radio) + return ..() + +// VOREStation Edit - Allow everyone to become a pAI +/obj/item/device/paicard/attack_ghost(mob/user as mob) + if(pai != null) //Have a person in them already? + return ..() + if(is_damage_critical()) + to_chat(usr, "That card is too damaged to activate!") + return + var/time_till_respawn = user.time_till_respawn() + if(time_till_respawn == -1) // Special case, never allowed to respawn + to_chat(usr, "Respawning is not allowed!") + else if(time_till_respawn) // Nonzero time to respawn + to_chat(usr, "You can't do that yet! You died too recently. You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes.") + return + if(jobban_isbanned(usr, "pAI")) + to_chat(usr,"You cannot join a pAI card when you are banned from playing as a pAI.") + return + + for(var/ourkey in paikeys) + if(ourkey == user.ckey) + to_chat(usr, "You can't just rejoin any old pAI card!!! Your card still exists.") + return + + var/choice = tgui_alert(user, "You sure you want to inhabit this PAI, or submit yourself to being recruited?", "Confirmation", list("Inhabit", "Recruit", "Cancel")) + if(choice == "Cancel") + return ..() + if(choice == "Recruit") + paiController.recruitWindow(user) + return ..() + choice = tgui_alert(user, "Do you want to load your pAI data?", "Load", list("Yes", "No")) + var/actual_pai_name + var/turf/location = get_turf(src) + if(choice == "No") + var/pai_name = tgui_input_text(user, "Choose your character's name", "Character Name") + actual_pai_name = sanitize_name(pai_name, ,1) + if(isnull(actual_pai_name)) + return ..() + if(istype(src , /obj/item/device/paicard/typeb)) + var/obj/item/device/paicard/typeb/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + new_pai.SetName(actual_pai_name) + else + var/obj/item/device/paicard/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + new_pai.SetName(actual_pai_name) + + if(choice == "Yes") + if(istype(src , /obj/item/device/paicard/typeb)) + var/obj/item/device/paicard/typeb/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + if(!new_pai.savefile_load(new_pai)) + var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") + actual_pai_name = sanitize_name(pai_name, ,1) + if(isnull(actual_pai_name)) + return ..() + else + var/obj/item/device/paicard/card = new(location) + var/mob/living/silicon/pai/new_pai = new(card) + new_pai.key = user.key + paikeys |= new_pai.ckey + card.setPersonality(new_pai) + if(!new_pai.savefile_load(new_pai)) + var/pai_name = tgui_input_text(new_pai, "Choose your character's name", "Character Name") + actual_pai_name = sanitize_name(pai_name, ,1) + if(isnull(actual_pai_name)) + return ..() + + qdel(src) + return ..() + +// VOREStation Edit End + +/obj/item/device/paicard/proc/access_screen(mob/user) + if(is_damage_critical()) + to_chat(user, "WARNING: CRITICAL HARDWARE FAILURE, SERVICE DEVICE IMMEDIATELY") + return + if (!in_range(src, user)) + return + user.set_machine(src) + var/dat = {" + + + + + + + "} + + if(pai) + dat += {" + Personal AI Device

                    + + + + + + + + + + + + + + + +
                    [pai.name]
                    Integrity: [pai.health]
                    Prime directive:[pai.pai_law0]
                    Additional directives:[pai.pai_laws]
                    +
                    + "} + dat += {" + + +
                    + Configure Directives +
                    + "} + if(pai && (!pai.master_dna || !pai.master)) + dat += {" + + +
                    + Imprint Master DNA +
                    + "} + dat += "
                    " + if(radio) + dat += "Radio Uplink" + dat += {" + + + + + + + + + +
                    Transmit:[radio.broadcasting ? "En" : "Dis" ]abled + +
                    Receive:[radio.listening ? "En" : "Dis" ]abled + +
                    +
                    + "} + else // + dat += "Radio Uplink
                    " + dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
                    " + /* - //A button for instantly deleting people from the game is lame, especially considering that pAIs on our server tend to activate without a master. + dat += {" + + +
                    Wipe current pAI personality + +
                    + "} + */ + if(screen_msg) + dat += "Message from [pai.name]
                    [screen_msg]" + else + if(looking_for_personality) + dat += {" + pAI Request Module

                    +

                    Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

                    + Searching for personalities

                    + + + + + +
                    + Refresh available personalities +

                    + "} + else + dat += {" + pAI Request Module

                    +

                    No personality is installed.

                    + + + + +
                    Request personality +
                    +
                    +

                    Each time this button is pressed, a request will be sent out to any available personalities. Check back often give plenty of time for personalities to respond. This process could take anywhere from 15 seconds to several minutes, depending on the available personalities' timeliness.

                    + "} + user << browse(dat, "window=paicard") + onclose(user, "paicard") + return + +/obj/item/device/paicard/Topic(href, href_list) + + if(!usr || usr.stat) + return + + if(href_list["setdna"]) + if(pai.master_dna) + return + var/mob/M = usr + if(!istype(M, /mob/living/carbon)) + to_chat(usr, span_blue("You don't have any DNA, or your DNA is incompatible with this device.")) + else + var/datum/dna/dna = usr.dna + pai.master = M.real_name + pai.master_dna = dna.unique_enzymes + to_chat(pai, span_red("

                    You have been bound to a new master.

                    ")) + if(href_list["request"]) + src.looking_for_personality = 1 + paiController.findPAI(src, usr) + if(href_list["wipe"]) + var/confirm = tgui_alert(usr, "Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe", list("Yes", "No")) + if(confirm == "Yes") + for(var/mob/M in src) + to_chat(M, "

                    You feel yourself slipping away from reality.

                    ") + to_chat(M, "

                    Byte by byte you lose your sense of self.

                    ") + to_chat(M, "

                    Your mental faculties leave you.

                    ") + to_chat(M, "
                    oblivion...
                    ") + M.death(0) + removePersonality() + if(href_list["wires"]) + var/t1 = text2num(href_list["wires"]) + switch(t1) + if(4) + radio.ToggleBroadcast() + if(2) + radio.ToggleReception() + if(href_list["setlaws"]) + var/newlaws = sanitize(tgui_input_text(usr, "Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws, multiline = TRUE, prevent_enter = TRUE)) + if(newlaws) + pai.pai_laws = newlaws + to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") + to_chat(pai, "Prime Directive:
                    [pai.pai_law0]") + to_chat(pai, "Supplemental Directives:
                    [pai.pai_laws]") + attack_self(usr) + +// WIRE_SIGNAL = 1 +// WIRE_RECEIVE = 2 +// WIRE_TRANSMIT = 4 + +/obj/item/device/paicard/proc/setPersonality(mob/living/silicon/pai/personality) + src.pai = personality + setEmotion(1) + +/obj/item/device/paicard/proc/removePersonality() + src.pai = null + cut_overlays() + setEmotion(16) + +/obj/item/device/paicard + var/current_emotion = 1 +/obj/item/device/paicard/proc/setEmotion(var/emotion) + if(pai) + cut_overlays() + qdel(screen_layer) + screen_layer = null + switch(emotion) + if(1) screen_layer = image(icon, "pai-neutral") + if(2) screen_layer = image(icon, "pai-what") + if(3) screen_layer = image(icon, "pai-happy") + if(4) screen_layer = image(icon, "pai-cat") + if(5) screen_layer = image(icon, "pai-extremely-happy") + if(6) screen_layer = image(icon, "pai-face") + if(7) screen_layer = image(icon, "pai-laugh") + if(8) screen_layer = image(icon, "pai-sad") + if(9) screen_layer = image(icon, "pai-angry") + if(10) screen_layer = image(icon, "pai-silly") + if(11) screen_layer = image(icon, "pai-nose") + if(12) screen_layer = image(icon, "pai-smirk") + if(13) screen_layer = image(icon, "pai-exclamation") + if(14) screen_layer = image(icon, "pai-question") + if(15) screen_layer = image(icon, "pai-blank") + if(16) screen_layer = image(icon, "pai-off") + + screen_layer.color = pai.eye_color + add_overlay(screen_layer) + current_emotion = emotion + +/obj/item/device/paicard/proc/alertUpdate() + if(pai) + return + if(last_notify == 0 || (5 MINUTES <= world.time - last_notify)) + audible_message("\The [src] flashes a message across its screen, \"Additional personalities available for download.\"", hearing_distance = world.view, runemessage = "bleeps!") + last_notify = world.time + +/obj/item/device/paicard/emp_act(severity) + for(var/mob/M in src) + M.emp_act(severity) + +/obj/item/device/paicard/ex_act(severity) + if(pai) + pai.ex_act(severity) + else + qdel(src) + +/obj/item/device/paicard/see_emote(mob/living/M, text) + if(pai && pai.client && !pai.canmove) + var/rendered = "[text]" + pai.show_message(rendered, 2) + ..() + +/obj/item/device/paicard/show_message(msg, type, alt, alt_type) + if(pai && pai.client) + var/rendered = "[msg]" + pai.show_message(rendered, type) + ..() + + +// VoreEdit: Living Machine Stuff after this. +// This adds a var and proc for all machines to take a pAI. (The pAI can't control anything, it's just for RP.) +// You need to add usage of the proc to each machine to actually add support. For an example of this, see code\modules\food\kitchen\microwave.dm +/obj/machinery + var/obj/item/device/paicard/paicard = null + +/obj/machinery/proc/insertpai(mob/user, obj/item/device/paicard/card) + //var/obj/item/paicard/card = I + var/mob/living/silicon/pai/AI = card.pai + if(paicard) + to_chat(user, span_notice("This bot is already under PAI Control!")) + return + if(!istype(card)) // TODO: Add sleevecard support. + return + if(!card.pai) + to_chat(user, span_notice("This card does not currently have a personality!")) + return + paicard = card + user.unEquip(card) + card.forceMove(src) + AI.client.eye = src + to_chat(AI, span_notice("Your location is [card.loc].")) // DEBUG. TODO: Make unfolding the chassis trigger an eject. + name = AI.name + to_chat(AI, span_notice("You feel a tingle in your circuits as your systems interface with \the [initial(src.name)].")) + +/obj/machinery/proc/ejectpai(mob/user) + if(paicard) + var/mob/living/silicon/pai/AI = paicard.pai + paicard.forceMove(src.loc) + AI.client.eye = AI + paicard = null + name = initial(src.name) + to_chat(AI, span_notice("You feel a tad claustrophobic as your mind closes back into your card, ejecting from \the [initial(src.name)].")) + if(user) + to_chat(user, span_notice("You eject the card from \the [initial(src.name)].")) + +/////////////////////////////// +//////////pAI Radios////////// +/////////////////////////////// +//Thanks heroman! + +/obj/item/device/radio/borg/pai + name = "integrated radio" + icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. + icon_state = "radio" + loudspeaker = FALSE + +/obj/item/device/radio/borg/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) + return + +/obj/item/device/radio/borg/pai/recalculateChannels() + if(!istype(loc,/obj/item/device/paicard)) + return + var/obj/item/device/paicard/card = loc + secure_radio_connections = list() + channels = list() + + for(var/internal_chan in internal_channels) + var/ch_name = radio_channels_by_freq[internal_chan] + if(has_channel_access(card.pai, internal_chan)) + channels += ch_name + channels[ch_name] = 1 + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + +/obj/item/device/paicard/typeb + name = "personal AI device" + icon = 'icons/obj/paicard.dmi' + +/obj/random/paicard + name = "personal AI device spawner" + icon = 'icons/obj/paicard.dmi' + icon_state = "pai" + +/obj/random/paicard/item_to_spawn() + return pick(/obj/item/device/paicard ,/obj/item/device/paicard/typeb) + +/obj/item/device/paicard/digest_act(var/atom/movable/item_storage = null) + if(pai.digestable) + return ..() diff --git a/code/game/objects/items/devices/personal_shield_generator_vr.dm b/code/game/objects/items/devices/personal_shield_generator_vr.dm index 6d105b7b542..dbae24eabf6 100644 --- a/code/game/objects/items/devices/personal_shield_generator_vr.dm +++ b/code/game/objects/items/devices/personal_shield_generator_vr.dm @@ -179,7 +179,7 @@ to_chat(user, "You install a cell in \the [src].") update_icon() - else if(W.is_screwdriver()) + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(bcell) if(istype(bcell, /obj/item/weapon/cell/device/shield_generator)) //No stealing self charging batteries! var/choice = tgui_alert(user, "A popup appears on the device 'REMOVING THE INTERNAL CELL WILL DESTROY THE BATTERY. DO YOU WISH TO CONTINUE?'...Well, do you?", "Selection List", list("Cancel", "Remove")) diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index 339b57f0558..0d7b8f7bd29 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -1,134 +1,134 @@ -// Powersink - used to drain station power - -/obj/item/device/powersink - name = "power sink" - desc = "A nulling power sink which drains energy from electrical systems." - icon_state = "powersink0" - icon = 'icons/obj/device.dmi' - w_class = ITEMSIZE_LARGE - throwforce = 5 - throw_speed = 1 - throw_range = 2 - - matter = list(MAT_STEEL = 750) - - origin_tech = list(TECH_POWER = 3, TECH_ILLEGAL = 5) - var/drain_rate = 1500000 // amount of power to drain per tick - var/apc_drain_rate = 5000 // Max. amount drained from single APC. In Watts. - var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts. - var/power_drained = 0 // Amount of power drained. - var/max_power = 1e9 // Detonation point. - var/mode = 0 // 0 = off, 1=clamped (off), 2=operating - var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs. - - var/datum/powernet/PN // Our powernet - var/obj/structure/cable/attached // the attached cable - -/obj/item/device/powersink/Destroy() - STOP_PROCESSING(SSobj, src) - STOP_PROCESSING_POWER_OBJECT(src) - ..() - -/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user) - if(I.is_screwdriver()) - if(mode == 0) - var/turf/T = loc - if(isturf(T) && !!T.is_plating()) - attached = locate() in T - if(!attached) - to_chat(user, "No exposed cable here to attach to.") - return - else - anchored = TRUE - mode = 1 - src.visible_message("[user] attaches [src] to the cable!") - playsound(src, I.usesound, 50, 1) - return - else - to_chat(user, "Device must be placed over an exposed cable to attach to it.") - return - else - if (mode == 2) - STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite - STOP_PROCESSING_POWER_OBJECT(src) - anchored = FALSE - mode = 0 - src.visible_message("[user] detaches [src] from the cable!") - set_light(0) - playsound(src, I.usesound, 50, 1) - icon_state = "powersink0" - - return - else - ..() - -/obj/item/device/powersink/attack_ai() - return - -/obj/item/device/powersink/attack_hand(var/mob/user) - switch(mode) - if(0) - ..() - if(1) - src.visible_message("[user] activates [src]!") - mode = 2 - icon_state = "powersink1" - START_PROCESSING(SSobj, src) - datum_flags &= ~DF_ISPROCESSING // Have to reset this flag so that PROCESSING_POWER_OBJECT can re-add it. It fails if the flag is already present. - Ater - START_PROCESSING_POWER_OBJECT(src) - if(2) //This switch option wasn't originally included. It exists now. --NeoFite - src.visible_message("[user] deactivates [src]!") - mode = 1 - set_light(0) - icon_state = "powersink0" - STOP_PROCESSING(SSobj, src) - STOP_PROCESSING_POWER_OBJECT(src) - -/obj/item/device/powersink/pwr_drain() - if(!attached) - return 0 - - if(drained_this_tick) - return 1 - drained_this_tick = 1 - - var/drained = 0 - - if(!PN) - return 1 - - set_light(12) - PN.trigger_warning() - // found a powernet, so drain up to max power from it - drained = PN.draw_power(drain_rate) - // if tried to drain more than available on powernet - // now look for APCs and drain their cells - if(drained < drain_rate) - for(var/obj/machinery/power/terminal/T in PN.nodes) - // Enough power drained this tick, no need to torture more APCs - if(drained >= drain_rate) - break - if(istype(T.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = T.master - if(A.operating && A.cell) - var/cur_charge = A.cell.charge / CELLRATE - var/drain_val = min(apc_drain_rate, cur_charge) - A.cell.use(drain_val * CELLRATE) - drained += drain_val - power_drained += drained - return 1 - - -/obj/item/device/powersink/process() - drained_this_tick = 0 - power_drained -= min(dissipation_rate, power_drained) - if(power_drained > max_power * 0.95) - playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) - if(power_drained >= max_power) - explosion(src.loc, 3,6,9,12) - qdel(src) - return - if(attached && attached.powernet) - PN = attached.powernet - else - PN = null +// Powersink - used to drain station power + +/obj/item/device/powersink + name = "power sink" + desc = "A nulling power sink which drains energy from electrical systems." + icon_state = "powersink0" + icon = 'icons/obj/device.dmi' + w_class = ITEMSIZE_LARGE + throwforce = 5 + throw_speed = 1 + throw_range = 2 + + matter = list(MAT_STEEL = 750) + + origin_tech = list(TECH_POWER = 3, TECH_ILLEGAL = 5) + var/drain_rate = 1500000 // amount of power to drain per tick + var/apc_drain_rate = 5000 // Max. amount drained from single APC. In Watts. + var/dissipation_rate = 20000 // Passive dissipation of drained power. In Watts. + var/power_drained = 0 // Amount of power drained. + var/max_power = 1e9 // Detonation point. + var/mode = 0 // 0 = off, 1=clamped (off), 2=operating + var/drained_this_tick = 0 // This is unfortunately necessary to ensure we process powersinks BEFORE other machinery such as APCs. + + var/datum/powernet/PN // Our powernet + var/obj/structure/cable/attached // the attached cable + +/obj/item/device/powersink/Destroy() + STOP_PROCESSING(SSobj, src) + STOP_PROCESSING_POWER_OBJECT(src) + ..() + +/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + if(mode == 0) + var/turf/T = loc + if(isturf(T) && !!T.is_plating()) + attached = locate() in T + if(!attached) + to_chat(user, "No exposed cable here to attach to.") + return + else + anchored = TRUE + mode = 1 + src.visible_message("[user] attaches [src] to the cable!") + playsound(src, I.usesound, 50, 1) + return + else + to_chat(user, "Device must be placed over an exposed cable to attach to it.") + return + else + if (mode == 2) + STOP_PROCESSING(SSobj, src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite + STOP_PROCESSING_POWER_OBJECT(src) + anchored = FALSE + mode = 0 + src.visible_message("[user] detaches [src] from the cable!") + set_light(0) + playsound(src, I.usesound, 50, 1) + icon_state = "powersink0" + + return + else + ..() + +/obj/item/device/powersink/attack_ai() + return + +/obj/item/device/powersink/attack_hand(var/mob/user) + switch(mode) + if(0) + ..() + if(1) + src.visible_message("[user] activates [src]!") + mode = 2 + icon_state = "powersink1" + START_PROCESSING(SSobj, src) + datum_flags &= ~DF_ISPROCESSING // Have to reset this flag so that PROCESSING_POWER_OBJECT can re-add it. It fails if the flag is already present. - Ater + START_PROCESSING_POWER_OBJECT(src) + if(2) //This switch option wasn't originally included. It exists now. --NeoFite + src.visible_message("[user] deactivates [src]!") + mode = 1 + set_light(0) + icon_state = "powersink0" + STOP_PROCESSING(SSobj, src) + STOP_PROCESSING_POWER_OBJECT(src) + +/obj/item/device/powersink/pwr_drain() + if(!attached) + return 0 + + if(drained_this_tick) + return 1 + drained_this_tick = 1 + + var/drained = 0 + + if(!PN) + return 1 + + set_light(12) + PN.trigger_warning() + // found a powernet, so drain up to max power from it + drained = PN.draw_power(drain_rate) + // if tried to drain more than available on powernet + // now look for APCs and drain their cells + if(drained < drain_rate) + for(var/obj/machinery/power/terminal/T in PN.nodes) + // Enough power drained this tick, no need to torture more APCs + if(drained >= drain_rate) + break + if(istype(T.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = T.master + if(A.operating && A.cell) + var/cur_charge = A.cell.charge / CELLRATE + var/drain_val = min(apc_drain_rate, cur_charge) + A.cell.use(drain_val * CELLRATE) + drained += drain_val + power_drained += drained + return 1 + + +/obj/item/device/powersink/process() + drained_this_tick = 0 + power_drained -= min(dissipation_rate, power_drained) + if(power_drained > max_power * 0.95) + playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) + if(power_drained >= max_power) + explosion(src.loc, 3,6,9,12) + qdel(src) + return + if(attached && attached.powernet) + PN = attached.powernet + else + PN = null diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index cbd95866309..a7b7348e29f 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -1,44 +1,44 @@ -/obj/item/device/radio/beacon - name = "tracking beacon" - desc = "A beacon used by a teleporter." - icon_state = "beacon" - item_state = "signaler" - var/code = "electronic" - origin_tech = list(TECH_BLUESPACE = 1) - -GLOBAL_LIST_BOILERPLATE(all_beacons, /obj/item/device/radio/beacon) - -/obj/item/device/radio/beacon/hear_talk() - return - - -/obj/item/device/radio/beacon/send_hear() - return null - - -/obj/item/device/radio/beacon/verb/alter_signal(t as text) - set name = "Alter Beacon's Signal" - set category = "Object" - set src in usr - - if ((usr.canmove && !( usr.restrained() ))) - src.code = t - if (!( src.code )) - src.code = "beacon" - src.add_fingerprint(usr) - return - -// SINGULO BEACON SPAWNER - -/obj/item/device/radio/beacon/syndicate - name = "suspicious beacon" - desc = "A label on it reads: Activate to have a singularity beacon teleported to your location." - origin_tech = list(TECH_BLUESPACE = 1, TECH_ILLEGAL = 7) - -/obj/item/device/radio/beacon/syndicate/attack_self(mob/user as mob) - if(user) - to_chat(user, "Locked In") - new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) - playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) - qdel(src) - return +/obj/item/device/radio/beacon + name = "tracking beacon" + desc = "A beacon used by a teleporter." + icon_state = "beacon" + item_state = "signaler" + var/code = "electronic" + origin_tech = list(TECH_BLUESPACE = 1) + +GLOBAL_LIST_BOILERPLATE(all_beacons, /obj/item/device/radio/beacon) + +/obj/item/device/radio/beacon/hear_talk() + return + + +/obj/item/device/radio/beacon/send_hear() + return null + + +/obj/item/device/radio/beacon/verb/alter_signal(t as text) + set name = "Alter Beacon's Signal" + set category = "Object" + set src in usr + + if ((usr.canmove && !( usr.restrained() ))) + src.code = t + if (!( src.code )) + src.code = "beacon" + src.add_fingerprint(usr) + return + +// SINGULO BEACON SPAWNER + +/obj/item/device/radio/beacon/syndicate + name = "suspicious beacon" + desc = "A label on it reads: Activate to have a singularity beacon teleported to your location." + origin_tech = list(TECH_BLUESPACE = 1, TECH_ILLEGAL = 7) + +/obj/item/device/radio/beacon/syndicate/attack_self(mob/user as mob) + if(user) + to_chat(user, "Locked In") + new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) + playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) + qdel(src) + return diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 5cea839acb3..e0fe849fccd 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -1,131 +1,131 @@ -/obj/item/device/radio/electropack - name = "electropack" - desc = "Dance my monkeys! DANCE!!!" - icon_state = "electropack0" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', - ) - item_state = "electropack" - frequency = 1449 - slot_flags = SLOT_BACK - w_class = ITEMSIZE_HUGE - - matter = list(MAT_STEEL = 10000,MAT_GLASS = 2500) - - var/code = 2 - -/obj/item/device/radio/electropack/attack_hand(mob/living/user as mob) - if(src == user.back) - to_chat(user, "You need help taking this off!") - return - ..() - -/obj/item/device/radio/electropack/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if(istype(W, /obj/item/clothing/head/helmet)) - if(!b_stat) - to_chat(user, "[src] is not ready to be attached!") - return - var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) - A.icon = 'icons/obj/assemblies.dmi' - - user.drop_from_inventory(W) - W.loc = A - W.master = A - A.part1 = W - - user.drop_from_inventory(src) - loc = A - master = A - A.part2 = src - - user.put_in_hands(A) - A.add_fingerprint(user) - -/obj/item/device/radio/electropack/Topic(href, href_list) - //..() - if(usr.stat || usr.restrained()) - return - if(((istype(usr, /mob/living/carbon/human) && ((!( ticker ) || (ticker && ticker.mode != "monkey")) && usr.contents.Find(src))) || (usr.contents.Find(master) || (in_range(src, usr) && istype(loc, /turf))))) - usr.set_machine(src) - if(href_list["freq"]) - var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) - set_frequency(new_frequency) - else - if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = min(100, code) - code = max(1, code) - else - if(href_list["power"]) - on = !( on ) - icon_state = "electropack[on]" - if(!( master )) - if(istype(loc, /mob)) - attack_self(loc) - else - for(var/mob/M in viewers(1, src)) - if(M.client) - attack_self(M) - else - if(istype(master.loc, /mob)) - attack_self(master.loc) - else - for(var/mob/M in viewers(1, master)) - if(M.client) - attack_self(M) - else - usr << browse(null, "window=radio") - return - return - -/obj/item/device/radio/electropack/receive_signal(datum/signal/signal) - if(!signal || signal.encryption != code) - return - - if(ismob(loc) && on) - var/mob/M = loc - var/turf/T = M.loc - if(istype(T, /turf)) - if(!M.moved_recently && M.last_move) - M.moved_recently = 1 - step(M, M.last_move) - sleep(50) - if(M) - M.moved_recently = 0 - to_chat(M, "You feel a sharp shock!") - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, M) - s.start() - - M.Weaken(10) - - if(master && wires & 1) - master.receive_signal() - return - -/obj/item/device/radio/electropack/attack_self(mob/user as mob, flag1) - - if(!istype(user, /mob/living/carbon/human)) - return - user.set_machine(src) - var/dat = {" -Turn [on ? "Off" : "On"]
                    -Frequency/Code for electropack:
                    -Frequency: -- -- [format_frequency(frequency)] -+ -+
                    - -Code: -- -- [code] -+ -+
                    -
                    "} - user << browse(dat, "window=radio") - onclose(user, "radio") - return +/obj/item/device/radio/electropack + name = "electropack" + desc = "Dance my monkeys! DANCE!!!" + icon_state = "electropack0" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', + ) + item_state = "electropack" + frequency = 1449 + slot_flags = SLOT_BACK + w_class = ITEMSIZE_HUGE + + matter = list(MAT_STEEL = 10000,MAT_GLASS = 2500) + + var/code = 2 + +/obj/item/device/radio/electropack/attack_hand(mob/living/user as mob) + if(src == user.back) + to_chat(user, "You need help taking this off!") + return + ..() + +/obj/item/device/radio/electropack/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if(istype(W, /obj/item/clothing/head/helmet)) + if(!b_stat) + to_chat(user, "[src] is not ready to be attached!") + return + var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) + A.icon = 'icons/obj/assemblies.dmi' + + user.drop_from_inventory(W) + W.loc = A + W.master = A + A.part1 = W + + user.drop_from_inventory(src) + loc = A + master = A + A.part2 = src + + user.put_in_hands(A) + A.add_fingerprint(user) + +/obj/item/device/radio/electropack/Topic(href, href_list) + //..() + if(usr.stat || usr.restrained()) + return + if(((istype(usr, /mob/living/carbon/human) && ((!( ticker ) || (ticker && ticker.mode != "monkey")) && usr.contents.Find(src))) || (usr.contents.Find(master) || (in_range(src, usr) && istype(loc, /turf))))) + usr.set_machine(src) + if(href_list["freq"]) + var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) + set_frequency(new_frequency) + else + if(href_list["code"]) + code += text2num(href_list["code"]) + code = round(code) + code = min(100, code) + code = max(1, code) + else + if(href_list["power"]) + on = !( on ) + icon_state = "electropack[on]" + if(!( master )) + if(istype(loc, /mob)) + attack_self(loc) + else + for(var/mob/M in viewers(1, src)) + if(M.client) + attack_self(M) + else + if(istype(master.loc, /mob)) + attack_self(master.loc) + else + for(var/mob/M in viewers(1, master)) + if(M.client) + attack_self(M) + else + usr << browse(null, "window=radio") + return + return + +/obj/item/device/radio/electropack/receive_signal(datum/signal/signal) + if(!signal || signal.encryption != code) + return + + if(ismob(loc) && on) + var/mob/M = loc + var/turf/T = M.loc + if(istype(T, /turf)) + if(!M.moved_recently && M.last_move) + M.moved_recently = 1 + step(M, M.last_move) + sleep(50) + if(M) + M.moved_recently = 0 + to_chat(M, "You feel a sharp shock!") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, M) + s.start() + + M.Weaken(10) + + if(master && wires & 1) + master.receive_signal() + return + +/obj/item/device/radio/electropack/attack_self(mob/user as mob, flag1) + + if(!istype(user, /mob/living/carbon/human)) + return + user.set_machine(src) + var/dat = {" +Turn [on ? "Off" : "On"]
                    +Frequency/Code for electropack:
                    +Frequency: +- +- [format_frequency(frequency)] ++ ++
                    + +Code: +- +- [code] ++ ++
                    +
                    "} + user << browse(dat, "window=radio") + onclose(user, "radio") + return diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm index 36892be518a..f28c98ef41e 100644 --- a/code/game/objects/items/devices/radio/encryptionkey.dm +++ b/code/game/objects/items/devices/radio/encryptionkey.dm @@ -127,4 +127,9 @@ channels = list("Response Team" = 1, "Science" = 1, "Command" = 1, "Medical" = 1, "Engineering" = 1, "Security" = 1, "Supply" = 1, "Service" = 1) /obj/item/device/encryptionkey/omni //Literally only for the admin intercoms - channels = list("Mercenary" = 1, "Raider" = 1, "Response Team" = 1, "Science" = 1, "Command" = 1, "Medical" = 1, "Engineering" = 1, "Security" = 1, "Supply" = 1, "Service" = 1) \ No newline at end of file + channels = list("Mercenary" = 1, "Raider" = 1, "Response Team" = 1, "Science" = 1, "Command" = 1, "Medical" = 1, "Engineering" = 1, "Security" = 1, "Supply" = 1, "Service" = 1) + syndie = 1//Signifies that it de-crypts Syndicate transmissions + +/obj/item/device/encryptionkey/ent + name = "entertainment encryption key" + channels = list("Entertainment" = 1) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 5c46bbf3d8d..b253e930560 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -1,455 +1,455 @@ -/obj/item/device/radio/headset - name = "radio headset" - desc = "An updated, modular intercom that fits over the head. Takes encryption keys" - var/radio_desc = "" - icon_state = "headset" - item_state = null //To remove the radio's state - matter = list(MAT_STEEL = 75) - subspace_transmission = 1 - canhear_range = 0 // can't hear headsets from very far away - slot_flags = SLOT_EARS - sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/ears/mob_teshari.dmi') - - var/translate_binary = 0 - var/translate_hive = 0 - var/obj/item/device/encryptionkey/keyslot1 = null - var/obj/item/device/encryptionkey/keyslot2 = null - var/ks1type = null - var/ks2type = null - - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - -/obj/item/device/radio/headset/New() - ..() - internal_channels.Cut() - if(ks1type) - keyslot1 = new ks1type(src) - if(ks2type) - keyslot2 = new ks2type(src) - recalculateChannels(1) - -/obj/item/device/radio/headset/Destroy() - qdel(keyslot1) - qdel(keyslot2) - keyslot1 = null - keyslot2 = null - return ..() - -/obj/item/device/radio/headset/list_channels(var/mob/user) - return list_secure_channels() - -/obj/item/device/radio/headset/examine(mob/user) - . = ..() - - if(radio_desc && Adjacent(user)) - . += "The following channels are available:" - . += radio_desc - -/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) - if(channel == "special") - if(translate_binary) - var/datum/language/binary = GLOB.all_languages["Robot Talk"] - binary.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) - return RADIO_CONNECTION_NON_SUBSPACE - if(translate_hive) - var/datum/language/hivemind = GLOB.all_languages["Hivemind"] - hivemind.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) - return RADIO_CONNECTION_NON_SUBSPACE - return RADIO_CONNECTION_FAIL - - return ..() - -/obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0) - if (aiOverride) - //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) - return ..(freq, level) - if(ishuman(src.loc)) - var/mob/living/carbon/human/H = src.loc - if(H.l_ear == src || H.r_ear == src) - //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) - return ..(freq, level) - return -1 - -/obj/item/device/radio/headset/get_worn_icon_state(var/slot_name) - var/append = "" - if(icon_override) - switch(slot_name) - if(slot_l_ear_str) - append = "_l" - if(slot_r_ear_str) - append = "_r" - - return "[..()][append]" - -/obj/item/device/radio/headset/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/radio/headset/syndicate - origin_tech = list(TECH_ILLEGAL = 3) - syndie = 1 - ks1type = /obj/item/device/encryptionkey/syndicate - -/obj/item/device/radio/headset/syndicate/alt - icon_state = "syndie_headset" - item_state = "headset" - origin_tech = list(TECH_ILLEGAL = 3) - syndie = 1 - ks1type = /obj/item/device/encryptionkey/syndicate - -/obj/item/device/radio/headset/raider - origin_tech = list(TECH_ILLEGAL = 2) - syndie = 1 - ks1type = /obj/item/device/encryptionkey/raider - -/obj/item/device/radio/headset/raider/Initialize() - . = ..() - set_frequency(RAID_FREQ) - -/obj/item/device/radio/headset/binary - origin_tech = list(TECH_ILLEGAL = 3) - ks1type = /obj/item/device/encryptionkey/binary - -/obj/item/device/radio/headset/headset_sec - name = "security radio headset" - desc = "This is used by your elite security force." - icon_state = "sec_headset" - ks2type = /obj/item/device/encryptionkey/headset_sec - -/obj/item/device/radio/headset/headset_sec/alt - name = "security bowman headset" - desc = "This is used by your elite security force." - icon_state = "sec_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_sec - -/obj/item/device/radio/headset/headset_eng - name = "engineering radio headset" - desc = "When the engineers wish to chat like girls." - icon_state = "eng_headset" - ks2type = /obj/item/device/encryptionkey/headset_eng - -/obj/item/device/radio/headset/headset_eng/alt - name = "engineering bowman headset" - desc = "When the engineers wish to chat like girls." - icon_state = "eng_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_eng - -/obj/item/device/radio/headset/headset_rob - name = "robotics radio headset" - desc = "Made specifically for the roboticists who cannot decide between departments." - icon_state = "rob_headset" - ks2type = /obj/item/device/encryptionkey/headset_rob - -/obj/item/device/radio/headset/headset_med - name = "medical radio headset" - desc = "A headset for the trained staff of the medbay." - icon_state = "med_headset" - ks2type = /obj/item/device/encryptionkey/headset_med - -/obj/item/device/radio/headset/headset_med/alt - name = "medical bowman headset" - desc = "A headset for the trained staff of the medbay." - icon_state = "med_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_med - -/obj/item/device/radio/headset/headset_sci - name = "science radio headset" - desc = "A sciency headset. Like usual." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/headset_sci - -/obj/item/device/radio/headset/headset_medsci - name = "medical research radio headset" - desc = "A headset that is a result of the mating between medical and science." - icon_state = "med_headset" - ks2type = /obj/item/device/encryptionkey/headset_medsci - -/obj/item/device/radio/headset/headset_com - name = "command radio headset" - desc = "A headset with a commanding channel." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/headset_com - -/obj/item/device/radio/headset/headset_com/alt - name = "command bowman headset" - desc = "A headset with a commanding channel." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_com - - -/obj/item/device/radio/headset/heads/captain - name = "site manager's headset" - desc = "The headset of the boss." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/captain - -/obj/item/device/radio/headset/heads/captain/alt - name = "site manager's bowman headset" - desc = "The headset of the boss." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/captain - -/obj/item/device/radio/headset/heads/captain/sfr - name = "SFR headset" - desc = "A headset belonging to a Sif Free Radio DJ. SFR, best tunes in the wilderness." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/captain - -/obj/item/device/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway. - name = "\improper AI subspace transceiver" - desc = "Integrated AI radio transceiver." - icon = 'icons/obj/robot_component.dmi' - icon_state = "radio" - item_state = "headset" - ks2type = /obj/item/device/encryptionkey/heads/ai_integrated - var/myAi = null // Atlantis: Reference back to the AI which has this radio. - var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu. - -/obj/item/device/radio/headset/heads/ai_integrated/receive_range(freq, level) - if (disabledAi) - return -1 //Transciever Disabled. - return ..(freq, level, 1) - -/obj/item/device/radio/headset/heads/rd - name = "research director's headset" - desc = "Headset of the eccentric-in-chief." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/rd - -/obj/item/device/radio/headset/heads/rd/alt - name = "research director's bowman headset" - desc = "Headset of the eccentric-in-chief." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/rd - -/obj/item/device/radio/headset/heads/hos - name = "head of security's headset" - desc = "The headset of the hardass who protects your worthless lives." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/hos - -/obj/item/device/radio/headset/heads/hos/alt - name = "head of security's bowman headset" - desc = "The headset of the hardass who protects your worthless lives." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/hos - -/obj/item/device/radio/headset/heads/ce - name = "chief engineer's headset" - desc = "The headset of the clown who is in charge of the circus." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/ce - -/obj/item/device/radio/headset/heads/ce/alt - name = "chief engineer's bowman headset" - desc = "The headset of the clown who is in charge of the circus." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/ce - -/obj/item/device/radio/headset/heads/cmo - name = "chief medical officer's headset" - desc = "The headset of the highly trained medical chief." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/cmo - -/obj/item/device/radio/headset/heads/cmo/alt - name = "chief medical officer's bowman headset" - desc = "The headset of the highly trained medical chief." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/cmo - -/obj/item/device/radio/headset/heads/hop - name = "head of personnel's headset" - desc = "The headset of the poor fool who will one day be Site Manager." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/hop - -/obj/item/device/radio/headset/heads/hop/alt - name = "head of personnel's bowman headset" - desc = "The headset of the poor fool who will one day be Site Manager." - icon_state = "com_headset_alt" - ks2type = /obj/item/device/encryptionkey/heads/hop - -/obj/item/device/radio/headset/headset_mine - name = "mining radio headset" - desc = "Headset used by miners. Has inbuilt short-band radio for when comms are down." - icon_state = "mine_headset" - adhoc_fallback = TRUE - ks2type = /obj/item/device/encryptionkey/headset_cargo - -/obj/item/device/radio/headset/headset_cargo - name = "supply radio headset" - desc = "A headset used by the QM and their cronies." - icon_state = "cargo_headset" - ks2type = /obj/item/device/encryptionkey/headset_cargo - -/obj/item/device/radio/headset/headset_cargo/alt - name = "supply bowman headset" - desc = "A bowman headset used by the QM and their cronies." - icon_state = "cargo_headset_alt" - ks2type = /obj/item/device/encryptionkey/headset_cargo - -/obj/item/device/radio/headset/headset_service - name = "service radio headset" - desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean." - icon_state = "srv_headset" - ks2type = /obj/item/device/encryptionkey/headset_service - -/obj/item/device/radio/headset/ert - name = "emergency response team radio headset" - desc = "The headset of the boss's boss." - icon_state = "com_headset" - centComm = 1 -// freerange = 1 - ks2type = /obj/item/device/encryptionkey/ert - -/obj/item/device/radio/headset/ert/alt - name = "emergency response team bowman headset" - desc = "The headset of the boss's boss." - icon_state = "com_headset_alt" -// freerange = 1 - ks2type = /obj/item/device/encryptionkey/ert - -/obj/item/device/radio/headset/omni //Only for the admin intercoms - ks2type = /obj/item/device/encryptionkey/omni - -/obj/item/device/radio/headset/ia - name = "internal affair's headset" - desc = "The headset of your worst enemy." - icon_state = "com_headset" - ks2type = /obj/item/device/encryptionkey/heads/hos - -/obj/item/device/radio/headset/mmi_radio - name = "brain-integrated radio" - desc = "MMIs and synthetic brains are often equipped with these." - icon = 'icons/obj/robot_component.dmi' - icon_state = "radio" - item_state = "headset" - var/mmiowner = null - var/radio_enabled = 1 - -/obj/item/device/radio/headset/mmi_radio/receive_range(freq, level) - if (!radio_enabled || istype(src.loc.loc, /mob/living/silicon) || istype(src.loc.loc, /obj/item/organ/internal)) - return -1 //Transciever Disabled. - return ..(freq, level, 1) - -/obj/item/device/radio/headset/attackby(obj/item/weapon/W as obj, mob/user as mob) -// ..() - user.set_machine(src) - if(!(W.is_screwdriver() || istype(W, /obj/item/device/encryptionkey))) - return - - if(W.is_screwdriver()) - if(keyslot1 || keyslot2) - - - for(var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - secure_radio_connections[ch_name] = null - - - if(keyslot1) - var/turf/T = get_turf(user) - if(T) - keyslot1.loc = T - keyslot1 = null - - - - if(keyslot2) - var/turf/T = get_turf(user) - if(T) - keyslot2.loc = T - keyslot2 = null - - recalculateChannels() - to_chat(user, "You pop out the encryption keys in the headset!") - playsound(src, W.usesound, 50, 1) - - else - to_chat(user, "This headset doesn't have any encryption keys! How useless...") - - if(istype(W, /obj/item/device/encryptionkey/)) - if(keyslot1 && keyslot2) - to_chat(user, "The headset can't hold another key!") - return - - if(!keyslot1) - user.drop_item() - W.loc = src - keyslot1 = W - - else - user.drop_item() - W.loc = src - keyslot2 = W - - - recalculateChannels() - - return - - -/obj/item/device/radio/headset/recalculateChannels(var/setDescription = 0) - src.channels = list() - src.translate_binary = 0 - src.translate_hive = 0 - src.syndie = 0 - - if(keyslot1) - for(var/ch_name in keyslot1.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] = keyslot1.channels[ch_name] - - if(keyslot1.translate_binary) - src.translate_binary = 1 - - if(keyslot1.translate_hive) - src.translate_hive = 1 - - if(keyslot1.syndie) - src.syndie = 1 - - if(keyslot2) - for(var/ch_name in keyslot2.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] = keyslot2.channels[ch_name] - - if(keyslot2.translate_binary) - src.translate_binary = 1 - - if(keyslot2.translate_hive) - src.translate_hive = 1 - - if(keyslot2.syndie) - src.syndie = 1 - - - for (var/ch_name in channels) - if(!radio_controller) - sleep(30) // Waiting for the radio_controller to be created. - if(!radio_controller) - src.name = "broken radio headset" - return - - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - - if(setDescription) - setupRadioDescription() - - return - -/obj/item/device/radio/headset/proc/setupRadioDescription() - var/radio_text = "" - for(var/i = 1 to channels.len) - var/channel = channels[i] - var/key = get_radio_key_from_channel(channel) - radio_text += "[key] - [channel]" - if(i != channels.len) - radio_text += ", " - - radio_desc = radio_text +/obj/item/device/radio/headset + name = "radio headset" + desc = "An updated, modular intercom that fits over the head. Takes encryption keys" + var/radio_desc = "" + icon_state = "headset" + item_state = null //To remove the radio's state + matter = list(MAT_STEEL = 75) + subspace_transmission = 1 + canhear_range = 0 // can't hear headsets from very far away + slot_flags = SLOT_EARS + sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/ears/mob_teshari.dmi') + + var/translate_binary = 0 + var/translate_hive = 0 + var/obj/item/device/encryptionkey/keyslot1 = null + var/obj/item/device/encryptionkey/keyslot2 = null + var/ks1type = null + var/ks2type = null + + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + +/obj/item/device/radio/headset/New() + ..() + internal_channels.Cut() + if(ks1type) + keyslot1 = new ks1type(src) + if(ks2type) + keyslot2 = new ks2type(src) + recalculateChannels(1) + +/obj/item/device/radio/headset/Destroy() + qdel(keyslot1) + qdel(keyslot2) + keyslot1 = null + keyslot2 = null + return ..() + +/obj/item/device/radio/headset/list_channels(var/mob/user) + return list_secure_channels() + +/obj/item/device/radio/headset/examine(mob/user) + . = ..() + + if(radio_desc && Adjacent(user)) + . += "The following channels are available:" + . += radio_desc + +/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) + if(channel == "special") + if(translate_binary) + var/datum/language/binary = GLOB.all_languages["Robot Talk"] + binary.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) + return RADIO_CONNECTION_NON_SUBSPACE + if(translate_hive) + var/datum/language/hivemind = GLOB.all_languages["Hivemind"] + hivemind.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces))) + return RADIO_CONNECTION_NON_SUBSPACE + return RADIO_CONNECTION_FAIL + + return ..() + +/obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0) + if (aiOverride) + //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) + return ..(freq, level) + if(ishuman(src.loc)) + var/mob/living/carbon/human/H = src.loc + if(H.l_ear == src || H.r_ear == src) + //playsound(loc, 'sound/effects/radio_common.ogg', 20, 1, 1, preference = /datum/client_preference/radio_sounds) + return ..(freq, level) + return -1 + +/obj/item/device/radio/headset/get_worn_icon_state(var/slot_name) + var/append = "" + if(icon_override) + switch(slot_name) + if(slot_l_ear_str) + append = "_l" + if(slot_r_ear_str) + append = "_r" + + return "[..()][append]" + +/obj/item/device/radio/headset/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/radio/headset/syndicate + origin_tech = list(TECH_ILLEGAL = 3) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/syndicate + +/obj/item/device/radio/headset/syndicate/alt + icon_state = "syndie_headset" + item_state = "headset" + origin_tech = list(TECH_ILLEGAL = 3) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/syndicate + +/obj/item/device/radio/headset/raider + origin_tech = list(TECH_ILLEGAL = 2) + syndie = 1 + ks1type = /obj/item/device/encryptionkey/raider + +/obj/item/device/radio/headset/raider/Initialize() + . = ..() + set_frequency(RAID_FREQ) + +/obj/item/device/radio/headset/binary + origin_tech = list(TECH_ILLEGAL = 3) + ks1type = /obj/item/device/encryptionkey/binary + +/obj/item/device/radio/headset/headset_sec + name = "security radio headset" + desc = "This is used by your elite security force." + icon_state = "sec_headset" + ks2type = /obj/item/device/encryptionkey/headset_sec + +/obj/item/device/radio/headset/headset_sec/alt + name = "security bowman headset" + desc = "This is used by your elite security force." + icon_state = "sec_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_sec + +/obj/item/device/radio/headset/headset_eng + name = "engineering radio headset" + desc = "When the engineers wish to chat like girls." + icon_state = "eng_headset" + ks2type = /obj/item/device/encryptionkey/headset_eng + +/obj/item/device/radio/headset/headset_eng/alt + name = "engineering bowman headset" + desc = "When the engineers wish to chat like girls." + icon_state = "eng_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_eng + +/obj/item/device/radio/headset/headset_rob + name = "robotics radio headset" + desc = "Made specifically for the roboticists who cannot decide between departments." + icon_state = "rob_headset" + ks2type = /obj/item/device/encryptionkey/headset_rob + +/obj/item/device/radio/headset/headset_med + name = "medical radio headset" + desc = "A headset for the trained staff of the medbay." + icon_state = "med_headset" + ks2type = /obj/item/device/encryptionkey/headset_med + +/obj/item/device/radio/headset/headset_med/alt + name = "medical bowman headset" + desc = "A headset for the trained staff of the medbay." + icon_state = "med_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_med + +/obj/item/device/radio/headset/headset_sci + name = "science radio headset" + desc = "A sciency headset. Like usual." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/headset_sci + +/obj/item/device/radio/headset/headset_medsci + name = "medical research radio headset" + desc = "A headset that is a result of the mating between medical and science." + icon_state = "med_headset" + ks2type = /obj/item/device/encryptionkey/headset_medsci + +/obj/item/device/radio/headset/headset_com + name = "command radio headset" + desc = "A headset with a commanding channel." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/headset_com + +/obj/item/device/radio/headset/headset_com/alt + name = "command bowman headset" + desc = "A headset with a commanding channel." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_com + + +/obj/item/device/radio/headset/heads/captain + name = "site manager's headset" + desc = "The headset of the boss." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/captain + +/obj/item/device/radio/headset/heads/captain/alt + name = "site manager's bowman headset" + desc = "The headset of the boss." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/captain + +/obj/item/device/radio/headset/heads/captain/sfr + name = "SFR headset" + desc = "A headset belonging to a Sif Free Radio DJ. SFR, best tunes in the wilderness." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/captain + +/obj/item/device/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway. + name = "\improper AI subspace transceiver" + desc = "Integrated AI radio transceiver." + icon = 'icons/obj/robot_component.dmi' + icon_state = "radio" + item_state = "headset" + ks2type = /obj/item/device/encryptionkey/heads/ai_integrated + var/myAi = null // Atlantis: Reference back to the AI which has this radio. + var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu. + +/obj/item/device/radio/headset/heads/ai_integrated/receive_range(freq, level) + if (disabledAi) + return -1 //Transciever Disabled. + return ..(freq, level, 1) + +/obj/item/device/radio/headset/heads/rd + name = "research director's headset" + desc = "Headset of the eccentric-in-chief." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/rd + +/obj/item/device/radio/headset/heads/rd/alt + name = "research director's bowman headset" + desc = "Headset of the eccentric-in-chief." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/rd + +/obj/item/device/radio/headset/heads/hos + name = "head of security's headset" + desc = "The headset of the hardass who protects your worthless lives." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/hos + +/obj/item/device/radio/headset/heads/hos/alt + name = "head of security's bowman headset" + desc = "The headset of the hardass who protects your worthless lives." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/hos + +/obj/item/device/radio/headset/heads/ce + name = "chief engineer's headset" + desc = "The headset of the clown who is in charge of the circus." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/ce + +/obj/item/device/radio/headset/heads/ce/alt + name = "chief engineer's bowman headset" + desc = "The headset of the clown who is in charge of the circus." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/ce + +/obj/item/device/radio/headset/heads/cmo + name = "chief medical officer's headset" + desc = "The headset of the highly trained medical chief." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/cmo + +/obj/item/device/radio/headset/heads/cmo/alt + name = "chief medical officer's bowman headset" + desc = "The headset of the highly trained medical chief." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/cmo + +/obj/item/device/radio/headset/heads/hop + name = "head of personnel's headset" + desc = "The headset of the poor fool who will one day be Site Manager." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/hop + +/obj/item/device/radio/headset/heads/hop/alt + name = "head of personnel's bowman headset" + desc = "The headset of the poor fool who will one day be Site Manager." + icon_state = "com_headset_alt" + ks2type = /obj/item/device/encryptionkey/heads/hop + +/obj/item/device/radio/headset/headset_mine + name = "mining radio headset" + desc = "Headset used by miners. Has inbuilt short-band radio for when comms are down." + icon_state = "mine_headset" + adhoc_fallback = TRUE + ks2type = /obj/item/device/encryptionkey/headset_cargo + +/obj/item/device/radio/headset/headset_cargo + name = "supply radio headset" + desc = "A headset used by the QM and their cronies." + icon_state = "cargo_headset" + ks2type = /obj/item/device/encryptionkey/headset_cargo + +/obj/item/device/radio/headset/headset_cargo/alt + name = "supply bowman headset" + desc = "A bowman headset used by the QM and their cronies." + icon_state = "cargo_headset_alt" + ks2type = /obj/item/device/encryptionkey/headset_cargo + +/obj/item/device/radio/headset/headset_service + name = "service radio headset" + desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean." + icon_state = "srv_headset" + ks2type = /obj/item/device/encryptionkey/headset_service + +/obj/item/device/radio/headset/ert + name = "emergency response team radio headset" + desc = "The headset of the boss's boss." + icon_state = "com_headset" + centComm = 1 +// freerange = 1 + ks2type = /obj/item/device/encryptionkey/ert + +/obj/item/device/radio/headset/ert/alt + name = "emergency response team bowman headset" + desc = "The headset of the boss's boss." + icon_state = "com_headset_alt" +// freerange = 1 + ks2type = /obj/item/device/encryptionkey/ert + +/obj/item/device/radio/headset/omni //Only for the admin intercoms + ks2type = /obj/item/device/encryptionkey/omni + +/obj/item/device/radio/headset/ia + name = "internal affair's headset" + desc = "The headset of your worst enemy." + icon_state = "com_headset" + ks2type = /obj/item/device/encryptionkey/heads/hos + +/obj/item/device/radio/headset/mmi_radio + name = "brain-integrated radio" + desc = "MMIs and synthetic brains are often equipped with these." + icon = 'icons/obj/robot_component.dmi' + icon_state = "radio" + item_state = "headset" + var/mmiowner = null + var/radio_enabled = 1 + +/obj/item/device/radio/headset/mmi_radio/receive_range(freq, level) + if (!radio_enabled || istype(src.loc.loc, /mob/living/silicon) || istype(src.loc.loc, /obj/item/organ/internal)) + return -1 //Transciever Disabled. + return ..(freq, level, 1) + +/obj/item/device/radio/headset/attackby(obj/item/weapon/W as obj, mob/user as mob) +// ..() + user.set_machine(src) + if(!(W.has_tool_quality(TOOL_SCREWDRIVER) || istype(W, /obj/item/device/encryptionkey))) + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(keyslot1 || keyslot2) + + + for(var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + + + if(keyslot1) + var/turf/T = get_turf(user) + if(T) + keyslot1.loc = T + keyslot1 = null + + + + if(keyslot2) + var/turf/T = get_turf(user) + if(T) + keyslot2.loc = T + keyslot2 = null + + recalculateChannels() + to_chat(user, "You pop out the encryption keys in the headset!") + playsound(src, W.usesound, 50, 1) + + else + to_chat(user, "This headset doesn't have any encryption keys! How useless...") + + if(istype(W, /obj/item/device/encryptionkey/)) + if(keyslot1 && keyslot2) + to_chat(user, "The headset can't hold another key!") + return + + if(!keyslot1) + user.drop_item() + W.loc = src + keyslot1 = W + + else + user.drop_item() + W.loc = src + keyslot2 = W + + + recalculateChannels() + + return + + +/obj/item/device/radio/headset/recalculateChannels(var/setDescription = 0) + src.channels = list() + src.translate_binary = 0 + src.translate_hive = 0 + src.syndie = 0 + + if(keyslot1) + for(var/ch_name in keyslot1.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] = keyslot1.channels[ch_name] + + if(keyslot1.translate_binary) + src.translate_binary = 1 + + if(keyslot1.translate_hive) + src.translate_hive = 1 + + if(keyslot1.syndie) + src.syndie = 1 + + if(keyslot2) + for(var/ch_name in keyslot2.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] = keyslot2.channels[ch_name] + + if(keyslot2.translate_binary) + src.translate_binary = 1 + + if(keyslot2.translate_hive) + src.translate_hive = 1 + + if(keyslot2.syndie) + src.syndie = 1 + + + for (var/ch_name in channels) + if(!radio_controller) + sleep(30) // Waiting for the radio_controller to be created. + if(!radio_controller) + src.name = "broken radio headset" + return + + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + + if(setDescription) + setupRadioDescription() + + return + +/obj/item/device/radio/headset/proc/setupRadioDescription() + var/radio_text = "" + for(var/i = 1 to channels.len) + var/channel = channels[i] + var/key = get_radio_key_from_channel(channel) + radio_text += "[key] - [channel]" + if(i != channels.len) + radio_text += ", " + + radio_desc = radio_text diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index cf3781f31b2..86a1af1d727 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -1,229 +1,229 @@ -/obj/item/device/radio/intercom - name = "station intercom (General)" - desc = "Talk through this." - icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit - New Icon - icon_state = "intercom" - layer = ABOVE_WINDOW_LAYER - anchored = TRUE - w_class = ITEMSIZE_LARGE - canhear_range = 7 //VOREStation Edit - flags = NOBLOODY - light_color = "#00ff00" - light_power = 0.25 - blocks_emissive = NONE - vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature - var/circuit = /obj/item/weapon/circuitboard/intercom - var/number = 0 - var/wiresexposed = 0 - -/obj/item/device/radio/intercom/Initialize() - . = ..() - var/area/A = get_area(src) - if(A) - GLOB.apc_event.register(A, src, /atom/proc/update_icon) - update_icon() - -/obj/item/device/radio/intercom/Destroy() - var/area/A = get_area(src) - if(A) - GLOB.apc_event.unregister(A, src, /atom/proc/update_icon) - return ..() - -/obj/item/device/radio/intercom/custom - name = "station intercom (Custom)" - broadcasting = 0 - listening = 0 - -/obj/item/device/radio/intercom/interrogation - name = "station intercom (Interrogation)" - frequency = 1449 - -/obj/item/device/radio/intercom/private - name = "station intercom (Private)" - frequency = AI_FREQ - -/obj/item/device/radio/intercom/specops - name = "\improper Spec Ops intercom" - frequency = ERT_FREQ - subspace_transmission = 1 - centComm = 1 - -/obj/item/device/radio/intercom/department - canhear_range = 5 - broadcasting = 0 - listening = 1 - -/obj/item/device/radio/intercom/department/medbay - name = "station intercom (Medbay)" - icon_state = "medintercom" - light_color = "#00aaff" - frequency = MED_I_FREQ - -/obj/item/device/radio/intercom/department/security - name = "station intercom (Security)" - icon_state = "secintercom" - light_color = "#ff0000" - frequency = SEC_I_FREQ - -/obj/item/device/radio/intercom/entertainment - name = "entertainment intercom" - frequency = ENT_FREQ - -/obj/item/device/radio/intercom/omni - name = "global announcer" -/obj/item/device/radio/intercom/omni/Initialize() - channels = radiochannels.Copy() - return ..() - -/obj/item/device/radio/intercom/New() - ..() - circuit = new circuit(src) - -/obj/item/device/radio/intercom/department/medbay/New() - ..() - internal_channels = default_medbay_channels.Copy() - -/obj/item/device/radio/intercom/department/security/New() - ..() - internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(SEC_I_FREQ) = list(access_security) - ) - -/obj/item/device/radio/intercom/entertainment/New() - ..() - internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(ENT_FREQ) = list() - ) - -/obj/item/device/radio/intercom/syndicate - name = "illicit intercom" - desc = "Talk through this. Evilly" - frequency = SYND_FREQ - subspace_transmission = 1 - syndie = 1 - -/obj/item/device/radio/intercom/syndicate/New() - ..() - internal_channels[num2text(SYND_FREQ)] = list(access_syndicate) - -/obj/item/device/radio/intercom/raider - name = "illicit intercom" - desc = "Pirate radio, but not in the usual sense of the word." - frequency = RAID_FREQ - subspace_transmission = 1 - syndie = 1 - -/obj/item/device/radio/intercom/raider/New() - ..() - internal_channels[num2text(RAID_FREQ)] = list(access_syndicate) - -/obj/item/device/radio/intercom/attack_ai(mob/user as mob) - src.add_fingerprint(user) - spawn (0) - attack_self(user) - -/obj/item/device/radio/intercom/attack_hand(mob/user as mob) - src.add_fingerprint(user) - spawn (0) - attack_self(user) - -/obj/item/device/radio/intercom/attackby(obj/item/W as obj, mob/user as mob) - add_fingerprint(user) - if(W.is_screwdriver()) // Opening the intercom up. - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - playsound(src, W.usesound, 50, 1) - update_icon() - else if(wiresexposed && W.is_wirecutter()) - user.visible_message("[user] has cut the wires inside \the [src]!", "You have cut the wires inside \the [src].") - playsound(src, W.usesound, 50, 1) - new/obj/item/stack/cable_coil(get_turf(src), 5) - var/obj/structure/frame/A = new /obj/structure/frame(src.loc) - var/obj/item/weapon/circuitboard/M = circuit - A.frame_type = M.board_type - A.pixel_x = pixel_x - A.pixel_y = pixel_y - A.circuit = M - A.set_dir(dir) - A.anchored = TRUE - A.state = 2 - A.update_icon() - M.deconstruct(src) - qdel(src) - else - src.attack_hand(user) - -/obj/item/device/radio/intercom/receive_range(freq, level) - if (!on) - return -1 - if(!(0 in level)) - var/turf/position = get_turf(src) - if(isnull(position) || !(position.z in level)) - return -1 - if (!src.listening) - return -1 - if(freq in ANTAG_FREQS) - if(!(src.syndie)) - return -1//Prevents broadcast of messages over devices lacking the encryption - - return canhear_range - -/obj/item/device/radio/intercom/update_icon() - var/area/A = get_area(src) - on = A?.powered(EQUIP) - - cut_overlays() - - if(!on) - set_light(0) - set_light_on(FALSE) - if(wiresexposed) - icon_state = "intercom-p_open" - else - icon_state = "intercom-p" - else - if(wiresexposed) - icon_state = "intercom_open" - set_light(0) - set_light_on(FALSE) - else - icon_state = initial(icon_state) - add_overlay(mutable_appearance(icon, "[icon_state]_ov")) - add_overlay(emissive_appearance(icon, "[icon_state]_ov")) - set_light(2) - set_light_on(TRUE) - -//VOREStation Add Start -/obj/item/device/radio/intercom/AICtrlClick(var/mob/user) - ToggleBroadcast() - to_chat(user, "\The [src]'s microphone is now [broadcasting ? "enabled" : "disabled"].") - -/obj/item/device/radio/intercom/AIAltClick(var/mob/user) - if(frequency == AI_FREQ) - set_frequency(initial(frequency)) - to_chat(user, "\The [src]'s frequency is now set to Default.") - else - set_frequency(AI_FREQ) - to_chat(user, "\The [src]'s frequency is now set to AI Private.") -//VOREStation Add End -/obj/item/device/radio/intercom/locked - var/locked_frequency - -/obj/item/device/radio/intercom/locked/set_frequency(var/frequency) - if(frequency == locked_frequency) - ..(locked_frequency) - -/obj/item/device/radio/intercom/locked/list_channels() - return "" - -/obj/item/device/radio/intercom/locked/ai_private - name = "\improper AI intercom" - frequency = AI_FREQ - broadcasting = 1 - listening = 1 - -/obj/item/device/radio/intercom/locked/confessional - name = "confessional intercom" - frequency = 1480 \ No newline at end of file +/obj/item/device/radio/intercom + name = "station intercom (General)" + desc = "Talk through this." + icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit - New Icon + icon_state = "intercom" + layer = ABOVE_WINDOW_LAYER + anchored = TRUE + w_class = ITEMSIZE_LARGE + canhear_range = 7 //VOREStation Edit + flags = NOBLOODY + light_color = "#00ff00" + light_power = 0.25 + blocks_emissive = NONE + vis_flags = VIS_HIDE // They have an emissive that looks bad in openspace due to their wall-mounted nature + var/circuit = /obj/item/weapon/circuitboard/intercom + var/number = 0 + var/wiresexposed = 0 + +/obj/item/device/radio/intercom/Initialize() + . = ..() + var/area/A = get_area(src) + if(A) + GLOB.apc_event.register(A, src, /atom/proc/update_icon) + update_icon() + +/obj/item/device/radio/intercom/Destroy() + var/area/A = get_area(src) + if(A) + GLOB.apc_event.unregister(A, src, /atom/proc/update_icon) + return ..() + +/obj/item/device/radio/intercom/custom + name = "station intercom (Custom)" + broadcasting = 0 + listening = 0 + +/obj/item/device/radio/intercom/interrogation + name = "station intercom (Interrogation)" + frequency = 1449 + +/obj/item/device/radio/intercom/private + name = "station intercom (Private)" + frequency = AI_FREQ + +/obj/item/device/radio/intercom/specops + name = "\improper Spec Ops intercom" + frequency = ERT_FREQ + subspace_transmission = 1 + centComm = 1 + +/obj/item/device/radio/intercom/department + canhear_range = 5 + broadcasting = 0 + listening = 1 + +/obj/item/device/radio/intercom/department/medbay + name = "station intercom (Medbay)" + icon_state = "medintercom" + light_color = "#00aaff" + frequency = MED_I_FREQ + +/obj/item/device/radio/intercom/department/security + name = "station intercom (Security)" + icon_state = "secintercom" + light_color = "#ff0000" + frequency = SEC_I_FREQ + +/obj/item/device/radio/intercom/entertainment + name = "entertainment intercom" + frequency = ENT_FREQ + +/obj/item/device/radio/intercom/omni + name = "global announcer" +/obj/item/device/radio/intercom/omni/Initialize() + channels = radiochannels.Copy() + return ..() + +/obj/item/device/radio/intercom/New() + ..() + circuit = new circuit(src) + +/obj/item/device/radio/intercom/department/medbay/New() + ..() + internal_channels = default_medbay_channels.Copy() + +/obj/item/device/radio/intercom/department/security/New() + ..() + internal_channels = list( + num2text(PUB_FREQ) = list(), + num2text(SEC_I_FREQ) = list(access_security) + ) + +/obj/item/device/radio/intercom/entertainment/New() + ..() + internal_channels = list( + num2text(PUB_FREQ) = list(), + num2text(ENT_FREQ) = list() + ) + +/obj/item/device/radio/intercom/syndicate + name = "illicit intercom" + desc = "Talk through this. Evilly" + frequency = SYND_FREQ + subspace_transmission = 1 + syndie = 1 + +/obj/item/device/radio/intercom/syndicate/New() + ..() + internal_channels[num2text(SYND_FREQ)] = list(access_syndicate) + +/obj/item/device/radio/intercom/raider + name = "illicit intercom" + desc = "Pirate radio, but not in the usual sense of the word." + frequency = RAID_FREQ + subspace_transmission = 1 + syndie = 1 + +/obj/item/device/radio/intercom/raider/New() + ..() + internal_channels[num2text(RAID_FREQ)] = list(access_syndicate) + +/obj/item/device/radio/intercom/attack_ai(mob/user as mob) + src.add_fingerprint(user) + spawn (0) + attack_self(user) + +/obj/item/device/radio/intercom/attack_hand(mob/user as mob) + src.add_fingerprint(user) + spawn (0) + attack_self(user) + +/obj/item/device/radio/intercom/attackby(obj/item/W as obj, mob/user as mob) + add_fingerprint(user) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) // Opening the intercom up. + wiresexposed = !wiresexposed + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") + playsound(src, W.usesound, 50, 1) + update_icon() + else if(wiresexposed && W.has_tool_quality(TOOL_WIRECUTTER)) + user.visible_message("[user] has cut the wires inside \the [src]!", "You have cut the wires inside \the [src].") + playsound(src, W.usesound, 50, 1) + new/obj/item/stack/cable_coil(get_turf(src), 5) + var/obj/structure/frame/A = new /obj/structure/frame(src.loc) + var/obj/item/weapon/circuitboard/M = circuit + A.frame_type = M.board_type + A.pixel_x = pixel_x + A.pixel_y = pixel_y + A.circuit = M + A.set_dir(dir) + A.anchored = TRUE + A.state = 2 + A.update_icon() + M.deconstruct(src) + qdel(src) + else + src.attack_hand(user) + +/obj/item/device/radio/intercom/receive_range(freq, level) + if (!on) + return -1 + if(!(0 in level)) + var/turf/position = get_turf(src) + if(isnull(position) || !(position.z in level)) + return -1 + if (!src.listening) + return -1 + if(freq in ANTAG_FREQS) + if(!(src.syndie)) + return -1//Prevents broadcast of messages over devices lacking the encryption + + return canhear_range + +/obj/item/device/radio/intercom/update_icon() + var/area/A = get_area(src) + on = A?.powered(EQUIP) + + cut_overlays() + + if(!on) + set_light(0) + set_light_on(FALSE) + if(wiresexposed) + icon_state = "intercom-p_open" + else + icon_state = "intercom-p" + else + if(wiresexposed) + icon_state = "intercom_open" + set_light(0) + set_light_on(FALSE) + else + icon_state = initial(icon_state) + add_overlay(mutable_appearance(icon, "[icon_state]_ov")) + add_overlay(emissive_appearance(icon, "[icon_state]_ov")) + set_light(2) + set_light_on(TRUE) + +//VOREStation Add Start +/obj/item/device/radio/intercom/AICtrlClick(var/mob/user) + ToggleBroadcast() + to_chat(user, "\The [src]'s microphone is now [broadcasting ? "enabled" : "disabled"].") + +/obj/item/device/radio/intercom/AIAltClick(var/mob/user) + if(frequency == AI_FREQ) + set_frequency(initial(frequency)) + to_chat(user, "\The [src]'s frequency is now set to [span_green("Default")].") + else + set_frequency(AI_FREQ) + to_chat(user, "\The [src]'s frequency is now set to [span_pink("AI Private")].") +//VOREStation Add End +/obj/item/device/radio/intercom/locked + var/locked_frequency + +/obj/item/device/radio/intercom/locked/set_frequency(var/frequency) + if(frequency == locked_frequency) + ..(locked_frequency) + +/obj/item/device/radio/intercom/locked/list_channels() + return "" + +/obj/item/device/radio/intercom/locked/ai_private + name = "\improper AI intercom" + frequency = AI_FREQ + broadcasting = 1 + listening = 1 + +/obj/item/device/radio/intercom/locked/confessional + name = "confessional intercom" + frequency = 1480 diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 2114ac0644a..d73a0840d8f 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -1,767 +1,767 @@ -// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't -//VOREStation Edit Start - Updating this for Virgo -var/global/list/default_internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(AI_FREQ) = list(access_synth), - num2text(ENT_FREQ) = list(), - num2text(ERT_FREQ) = list(access_cent_specops), - num2text(COMM_FREQ)= list(access_heads), - num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), - num2text(MED_FREQ) = list(access_medical_equip), - num2text(MED_I_FREQ)=list(access_medical_equip), - num2text(SEC_FREQ) = list(access_security), - num2text(SEC_I_FREQ)=list(access_security), - num2text(SCI_FREQ) = list(access_tox, access_robotics, access_xenobiology), - num2text(SUP_FREQ) = list(access_cargo, access_mining_station), - num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), - num2text(EXP_FREQ) = list(access_explorer, access_pilot) -) - -var/global/list/default_medbay_channels = list( - num2text(PUB_FREQ) = list(), - num2text(MED_FREQ) = list(), - num2text(MED_I_FREQ) = list() -) -//VOREStation Edit End - -/obj/item/device/radio - icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit - name = "shortwave radio" //VOREStation Edit - desc = "Used to talk to people when headsets don't function. Range is limited." - suffix = "\[3\]" - icon_state = "walkietalkie" - item_state = "radio" - - var/on = 1 // 0 for off - var/last_transmission - var/frequency = PUB_FREQ //common chat - var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies - var/canhear_range = 3 // the range which mobs can hear this radio from - var/loudspeaker = TRUE // Allows borgs to disable canhear_range. - var/datum/wires/radio/wires = null - var/b_stat = 0 - var/broadcasting = 0 - var/listening = 1 - var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h - var/subspace_transmission = 0 - var/subspace_switchable = FALSE - var/adhoc_fallback = FALSE //Falls back to 'radio' mode if subspace not available - var/syndie = 0//Holder to see if it's a syndicate encrypted radio - var/centComm = 0//Holder to see if it's a CentCom encrypted radio - slot_flags = SLOT_BELT - throw_speed = 2 - throw_range = 9 - w_class = ITEMSIZE_SMALL - show_messages = 1 - - // Bluespace radios talk directly to telecomms equipment - var/bluespace_radio = FALSE - var/datum/weakref/bs_tx_weakref //Maybe misleading, this is the device to TRANSMIT TO - // For mappers or subtypes, to start them prelinked to these devices - var/bs_tx_preload_id - var/bs_rx_preload_id - - matter = list(MAT_GLASS = 25,MAT_STEEL = 75) - var/const/FREQ_LISTENING = 1 - var/list/internal_channels - - var/datum/radio_frequency/radio_connection - var/list/datum/radio_frequency/secure_radio_connections = new - -/obj/item/device/radio/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) - -/obj/item/device/radio/New() - ..() - wires = new(src) - internal_channels = default_internal_channels.Copy() - listening_objects += src - -/obj/item/device/radio/Destroy() - qdel(wires) - wires = null - listening_objects -= src - if(radio_controller) - radio_controller.remove_object(src, frequency) - for (var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - return ..() - - -/obj/item/device/radio/Initialize() - . = ..() - if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ) - frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(frequency) - - for (var/ch_name in channels) - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - - if(bluespace_radio) - if(bs_tx_preload_id) - //Try to find a receiver - for(var/obj/machinery/telecomms/receiver/RX in telecomms_list) - if(RX.id == bs_tx_preload_id) //Again, bs_tx is the thing to TRANSMIT TO, so a receiver. - bs_tx_weakref = WEAKREF(RX) - RX.link_radio(src) - break - //Hmm, howabout an AIO machine - if(!bs_tx_weakref) - for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) - if(AIO.id == bs_tx_preload_id) - bs_tx_weakref = WEAKREF(AIO) - AIO.link_radio(src) - break - if(!bs_tx_weakref) - testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") - - if(bs_rx_preload_id) - var/found = 0 - //Try to find a transmitter - for(var/obj/machinery/telecomms/broadcaster/TX in telecomms_list) - if(TX.id == bs_rx_preload_id) //Again, bs_rx is the thing to RECEIVE FROM, so a transmitter. - TX.link_radio(src) - found = 1 - break - //Hmm, howabout an AIO machine - if(!found) - for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) - if(AIO.id == bs_rx_preload_id) - AIO.link_radio(src) - found = 1 - break - if(!found) - testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") - -/obj/item/device/radio/proc/recalculateChannels() - return - -/obj/item/device/radio/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) - -/obj/item/device/radio/interact(mob/user) - if(!user) - return FALSE - - if(b_stat) - wires.Interact(user) - - return tgui_interact(user) - -/obj/item/device/radio/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Radio", name, parent_ui) - ui.open() - -/obj/item/device/radio/tgui_data(mob/user) - var/data[0] - - data["rawfreq"] = frequency - data["listening"] = listening - data["broadcasting"] = broadcasting - data["subspace"] = subspace_transmission - data["subspaceSwitchable"] = subspace_switchable - data["loudspeaker"] = loudspeaker - - data["mic_cut"] = (wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) - data["spk_cut"] = (wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - else - data["chan_list"] = null - - if(syndie) - data["useSyndMode"] = 1 - - data["minFrequency"] = PUBLIC_LOW_FREQ - data["maxFrequency"] = PUBLIC_HIGH_FREQ - - return data - -/obj/item/device/radio/proc/list_channels(var/mob/user) - return list_internal_channels(user) - -/obj/item/device/radio/proc/list_secure_channels(var/mob/user) - var/dat[0] - - for(var/ch_name in channels) - var/chan_stat = channels[ch_name] - var/listening = !!(chan_stat & FREQ_LISTENING) != 0 - - dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "freq" = radiochannels[ch_name]))) - - return dat - -/obj/item/device/radio/proc/list_internal_channels(var/mob/user) - var/dat[0] - for(var/internal_chan in internal_channels) - if(has_channel_access(user, internal_chan)) - dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "freq" = text2num(internal_chan)))) - - return dat - -/obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq) - if(!user) - return FALSE - - if(!(freq in internal_channels)) - return FALSE - - return user.has_internal_radio_channel_access(internal_channels[freq]) - -/mob/proc/has_internal_radio_channel_access(var/list/req_one_accesses) - var/obj/item/weapon/card/id/I = GetIdCard() - return has_access(list(), req_one_accesses, I ? I.GetAccess() : list()) - -/mob/observer/dead/has_internal_radio_channel_access(var/list/req_one_accesses) - return can_admin_interact() - -/obj/item/device/radio/proc/text_sec_channel(var/chan_name, var/chan_stat) - var/list = !!(chan_stat&FREQ_LISTENING)!=0 - return {" - [chan_name]
                    - Speaker: [list ? "Engaged" : "Disengaged"]
                    - "} - -/obj/item/device/radio/proc/ToggleBroadcast() - broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) - -/obj/item/device/radio/proc/ToggleReception() - listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) - -/obj/item/device/radio/CanUseTopic() - if(!on) - return STATUS_CLOSE - return ..() - -/obj/item/device/radio/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("setFrequency") - var/new_frequency = (text2num(params["freq"])) - if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - set_frequency(new_frequency) - if(hidden_uplink) - if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) - usr << browse(null, "window=radio") - . = TRUE - if("broadcast") - ToggleBroadcast() - . = TRUE - if("listen") - ToggleReception() - . = TRUE - if("channel") - var/chan_name = params["channel"] - if(channels[chan_name] & FREQ_LISTENING) - channels[chan_name] &= ~FREQ_LISTENING - else - channels[chan_name] |= FREQ_LISTENING - . = TRUE - if("specFreq") - var/freq = params["channel"] - if(has_channel_access(usr, freq)) - set_frequency(text2num(freq)) - . = TRUE - if("subspace") - if(subspace_switchable) - subspace_transmission = !subspace_transmission - if(!subspace_transmission) - channels = list() - to_chat(usr, "Subspace Transmission is disabled") - else - recalculateChannels() - to_chat(usr, "Subspace Transmission is enabled") - . = TRUE - if("toggleLoudspeaker") - if(!subspace_switchable) - return - loudspeaker = !loudspeaker - - if(loudspeaker) - to_chat(usr, "Loadspeaker enabled.") - else - to_chat(usr, "Loadspeaker disabled.") - . = TRUE - - if(. && iscarbon(usr)) - playsound(src, "button", 10) - -GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer) - -/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels, var/states) //VOREStation Edit - - if(!GLOB.autospeaker) - return - var/datum/radio_frequency/connection = null - if(channel && channels && channels.len > 0) - if(channel == "department") - channel = channels[1] - connection = secure_radio_connections[channel] - else - connection = radio_connection - channel = null - if(!istype(connection)) - return - - if(!LAZYLEN(zlevels)) - zlevels = list(0) - //VOREStation Edit Start - if(!states) - states = "states" - //VOREStation Edit End - GLOB.autospeaker.SetName(from) - Broadcast_Message(connection, GLOB.autospeaker, - 0, "*garbled automated announcement*", src, - message_to_multilingual(message, GLOB.all_languages[LANGUAGE_GALCOM]), from, "Automated Announcement", from, "synthesized voice", - DATA_FAKE, 0, zlevels, connection.frequency, states) //VOREStation Edit - -// Interprets the message mode when talking into a radio, possibly returning a connection datum -/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) - // If a channel isn't specified, send to common. - if(!message_mode || message_mode == "headset") - return radio_connection - - // Otherwise, if a channel is specified, look for it. - if(channels && channels.len > 0) - if (message_mode == "department") // Department radio shortcut - message_mode = channels[1] - - if (channels[message_mode]) // only broadcast if the channel is set on - return secure_radio_connections[message_mode] - - // If we were to send to a channel we don't have, drop it. - return RADIO_CONNECTION_FAIL - -/obj/item/device/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") - if(!on) - return FALSE // the device has to be on - // Fix for permacell radios, but kinda eh about actually fixing them. - if(!M || !message_pieces) - return FALSE - - if(istype(M)) - M.trigger_aiming(TARGET_CAN_RADIO) - - // Uncommenting this. To the above comment: - // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom - if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact - return FALSE - - if(!radio_connection) - set_frequency(frequency) - - /* Quick introduction: - This new radio system uses a very robust FTL signaling technology unoriginally - dubbed "subspace" which is somewhat similar to 'blue-space' but can't - actually transmit large mass. Headsets are the only radio devices capable - of sending subspace transmissions to the Communications Satellite. - - A headset sends a signal to a subspace listener/reciever elsewhere in space, - the signal gets processed and logged, and an audible transmission gets sent - to each individual headset. - */ - - //#### Grab the connection datum ####// - var/message_mode = handle_message_mode(M, message_pieces, channel) - switch(message_mode) - if(RADIO_CONNECTION_FAIL) - return FALSE - if(RADIO_CONNECTION_NON_SUBSPACE) - return TRUE - - if(!istype(message_mode, /datum/radio_frequency)) - return FALSE - - var/pos_z = get_z(src) - var/datum/radio_frequency/connection = message_mode - - //#### Tagging the signal with all appropriate identity values ####// - - // ||-- The mob's name identity --|| - var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) - var/real_name = M.real_name // mob's real name - var/mobkey = "none" // player key associated with mob - var/voicemask = 0 // the speaker is wearing a voice mask - if(M.client) - mobkey = M.key // assign the mob's key - - - var/jobname // the mob's "job" - - // --- Human: use their actual job --- - if (ishuman(M)) - var/mob/living/carbon/human/H = M - jobname = H.get_assignment() - - // --- Carbon Nonhuman --- - else if (iscarbon(M)) // Nonhuman carbon mob - jobname = "No id" - - // --- AI --- - else if (isAI(M)) - jobname = "AI" - - // --- Cyborg --- - else if (isrobot(M)) - jobname = "Cyborg" - - // --- Personal AI (pAI) --- - else if (istype(M, /mob/living/silicon/pai)) - jobname = "Personal AI" - - // --- Unidentifiable mob --- - else - jobname = "Unknown" - - - // --- Modifications to the mob's identity --- - - // The mob is disguising their identity: - if (ishuman(M) && M.GetVoice() != real_name) - displayname = M.GetVoice() - jobname = "Unknown" - voicemask = 1 - - // First, we want to generate a new radio signal - var/datum/signal/signal = new - - // --- Finally, tag the actual signal with the appropriate values --- - signal.data = list( - // Identity-associated tags: - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = message_to_multilingual(pick(M.speak_emote)), // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mask - - // We store things that would otherwise be kept in the actual mob - // so that they can be logged even AFTER the mob is deleted or something - - // Other tags: - "compression" = rand(45,50), // compressed radio signal - "message" = message_pieces, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, // how much to sleep() before broadcasting - simulates net lag - "traffic" = 0, // dictates the total traffic sum that the signal went through - "type" = SIGNAL_NORMAL, // determines what type of radio input it is: normal broadcast - "server" = null, // the last server to log this signal - "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery - "level" = pos_z, // The source's z level - "verb" = verb - ) - signal.frequency = connection.frequency // Quick frequency set - - var/filter_type = DATA_LOCAL //If we end up having to send it the old fashioned way, it's with this data var. - - /* ###### Bluespace radios talk directly to receivers (and only directly to receivers) ###### */ - if(bluespace_radio) - //Nothing to transmit to - if(!bs_tx_weakref) - to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") - return FALSE - - var/obj/machinery/telecomms/tx_to = bs_tx_weakref.resolve() - //Was linked, now destroyed or something - if(!tx_to) - bs_tx_weakref = null - to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") - return FALSE - - //Transmitted in the blind. If we get a message back, cool. If not, oh well. - signal.transmission_method = TRANSMISSION_BLUESPACE - return tx_to.receive_signal(signal) - - /* ###### Radios with subspace_transmission can only broadcast through subspace (unless they have adhoc_fallback) ###### */ - else if(subspace_transmission) - var/list/jamming = is_jammed(src) - if(jamming) - var/distance = 0 - var/area/our_area = get_area(src) - if(our_area.no_comms) - distance = 99 - else - distance = jamming["distance"] - to_chat(M, "\icon[src][bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") - return FALSE - - // First, we want to generate a new radio signal - signal.transmission_method = TRANSMISSION_SUBSPACE - - //#### Sending the signal to all subspace receivers ####// - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - // Allinone can act as receivers. - for(var/obj/machinery/telecomms/allinone/R in telecomms_list) - R.receive_signal(signal) - - // Receiving code can be located in Telecommunications.dm - if(signal.data["done"] && (pos_z in signal.data["level"])) - return TRUE //Huzzah, sent via subspace - - else if(adhoc_fallback) //Less huzzah, we have to fallback - to_chat(loc, "\The [src] pings as it falls back to local radio transmission.") - subspace_transmission = FALSE - - else //Oh well - return FALSE - - /* ###### Intercoms and station-bounced radios ###### */ - else - /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ - if(istype(src, /obj/item/device/radio/intercom)) - filter_type = DATA_INTERCOM - - /* --- Try to send a normal subspace broadcast first */ - signal.transmission_method = TRANSMISSION_SUBSPACE - signal.data["compression"] = 0 - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - // Allinone can act as receivers. - for(var/obj/machinery/telecomms/allinone/R in telecomms_list) - R.receive_signal(signal) - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - if(signal.data["done"] && (pos_z in signal.data["level"])) - if(adhoc_fallback) - to_chat(loc, "\The [src] pings as it reestablishes subspace communications.") - subspace_transmission = TRUE - // we're done here. - return TRUE - - //Nothing handled any sort of remote radio-ing and returned before now, just squawk on this zlevel. - return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message_pieces, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], using_map.get_map_levels(pos_z), connection.frequency, verb) - - -/obj/item/device/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") - if(broadcasting) - if(get_dist(src, M) <= canhear_range) - talk_into(M, message_pieces, null, verb) - -/obj/item/device/radio/proc/receive_range(freq, level) - // check if this radio can receive on the given frequency, and if so, - // what the range is in which mobs will hear the radio - // returns: -1 if can't receive, range otherwise - if(wires.is_cut(WIRE_RADIO_RECEIVER)) - return -1 - if(!listening) - return -1 - if(is_jammed(src)) - return -1 - if(!(0 in level)) - var/pos_z = get_z(src) - if(!(pos_z in level)) - return -1 - if(freq in ANTAG_FREQS) - if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys - return -1 - if(freq in CENT_FREQS) - if(!(src.centComm))//Checks to see if it's allowed on that frequency, based on the encryption keys - return -1 - if (!on) - return -1 - if (!freq) //recieved on main frequency - if (!listening) - return -1 - else - var/accept = (freq==frequency && listening) - if (!accept) - for (var/ch_name in channels) - var/datum/radio_frequency/RF = secure_radio_connections[ch_name] - if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) - accept = 1 - break - if (!accept) - return -1 - return canhear_range - -/obj/item/device/radio/proc/send_hear(freq, level) - var/range = receive_range(freq, level) - if(range > -1 && loudspeaker) - return get_mobs_or_objects_in_view(range, src) - - -/obj/item/device/radio/examine(mob/user) - . = ..() - - if((in_range(src, user) || loc == user)) - if(b_stat) - . += "\The [src] can be attached and modified!" - else - . += "\The [src] can not be modified or attached!" - -/obj/item/device/radio/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - user.set_machine(src) - if (!W.is_screwdriver()) - return - b_stat = !( b_stat ) - if(!istype(src, /obj/item/device/radio/beacon)) - if (b_stat) - user.show_message("\The [src] can now be attached and modified!") - else - user.show_message("\The [src] can no longer be modified or attached!") - updateDialog() - //Foreach goto(83) - add_fingerprint(user) - return - else return - -/obj/item/device/radio/emp_act(severity) - broadcasting = 0 - listening = 0 - for (var/ch_name in channels) - channels[ch_name] = 0 - ..() - -/////////////////////////////// -//////////Borg Radios////////// -/////////////////////////////// -//Giving borgs their own radio to have some more room to work with -Sieve - -/obj/item/device/radio/borg - var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks - var/obj/item/device/encryptionkey/keyslot = null//Borg radios can handle a single encryption key - icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. - icon_state = "radio" - canhear_range = 0 - subspace_transmission = TRUE - subspace_switchable = TRUE - -/obj/item/device/radio/borg/Destroy() - myborg = null - return ..() - -/obj/item/device/radio/borg/list_channels(var/mob/user) - return list_secure_channels(user) - -/obj/item/device/radio/borg/talk_into() - . = ..() - if (isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - var/datum/robot_component/C = R.components["radio"] - R.cell_use_power(C.active_usage) - -/obj/item/device/radio/borg/attackby(obj/item/weapon/W as obj, mob/user as mob) -// ..() - user.set_machine(src) - if (!(W.is_screwdriver() || istype(W, /obj/item/device/encryptionkey))) - return - - if(W.is_screwdriver()) - if(keyslot) - - - for(var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - secure_radio_connections[ch_name] = null - - - if(keyslot) - var/turf/T = get_turf(user) - if(T) - keyslot.loc = T - keyslot = null - - recalculateChannels() - to_chat(user, "You pop out the encryption key in the radio!") - playsound(src, W.usesound, 50, 1) - - else - to_chat(user, "This radio doesn't have any encryption keys!") - - if(istype(W, /obj/item/device/encryptionkey/)) - if(keyslot) - to_chat(user, "The radio can't hold another key!") - return - - if(!keyslot) - user.drop_item() - W.loc = src - keyslot = W - - recalculateChannels() - - return - -/obj/item/device/radio/borg/recalculateChannels() - src.channels = list() - src.syndie = 0 - - var/mob/living/silicon/robot/D = src.loc - if(D.module) - for(var/ch_name in D.module.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] += D.module.channels[ch_name] - if(keyslot) - for(var/ch_name in keyslot.channels) - if(ch_name in src.channels) - continue - src.channels += ch_name - src.channels[ch_name] += keyslot.channels[ch_name] - - if(keyslot.syndie) - src.syndie = 1 - - for (var/ch_name in src.channels) - if(!radio_controller) - sleep(30) // Waiting for the radio_controller to be created. - if(!radio_controller) - src.name = "broken radio" - return - - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - - return - -/obj/item/device/radio/proc/config(op) - if(radio_controller) - for (var/ch_name in channels) - radio_controller.remove_object(src, radiochannels[ch_name]) - secure_radio_connections = new - channels = op - if(radio_controller) - for (var/ch_name in op) - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) - return - -/obj/item/device/radio/off - listening = 0 - -/obj/item/device/radio/phone - broadcasting = 0 - icon = 'icons/obj/items.dmi' - icon_state = "red_phone" - listening = 1 - name = "phone" - anchored = FALSE - -/obj/item/device/radio/phone/medbay - frequency = MED_I_FREQ - -/obj/item/device/radio/phone/medbay/New() - ..() - internal_channels = default_medbay_channels.Copy() +// Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't +//VOREStation Edit Start - Updating this for Virgo +var/global/list/default_internal_channels = list( + num2text(PUB_FREQ) = list(), + num2text(AI_FREQ) = list(access_synth), + num2text(ENT_FREQ) = list(), + num2text(ERT_FREQ) = list(access_cent_specops), + num2text(COMM_FREQ)= list(access_heads), + num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), + num2text(MED_FREQ) = list(access_medical_equip), + num2text(MED_I_FREQ)=list(access_medical_equip), + num2text(SEC_FREQ) = list(access_security), + num2text(SEC_I_FREQ)=list(access_security), + num2text(SCI_FREQ) = list(access_tox, access_robotics, access_xenobiology), + num2text(SUP_FREQ) = list(access_cargo, access_mining_station), + num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), + num2text(EXP_FREQ) = list(access_explorer, access_pilot) +) + +var/global/list/default_medbay_channels = list( + num2text(PUB_FREQ) = list(), + num2text(MED_FREQ) = list(), + num2text(MED_I_FREQ) = list() +) +//VOREStation Edit End + +/obj/item/device/radio + icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit + name = "shortwave radio" //VOREStation Edit + desc = "Used to talk to people when headsets don't function. Range is limited." + suffix = "\[3\]" + icon_state = "walkietalkie" + item_state = "radio" + + var/on = 1 // 0 for off + var/last_transmission + var/frequency = PUB_FREQ //common chat + var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies + var/canhear_range = 3 // the range which mobs can hear this radio from + var/loudspeaker = TRUE // Allows borgs to disable canhear_range. + var/datum/wires/radio/wires = null + var/b_stat = 0 + var/broadcasting = 0 + var/listening = 1 + var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h + var/subspace_transmission = 0 + var/subspace_switchable = FALSE + var/adhoc_fallback = FALSE //Falls back to 'radio' mode if subspace not available + var/syndie = 0//Holder to see if it's a syndicate encrypted radio + var/centComm = 0//Holder to see if it's a CentCom encrypted radio + slot_flags = SLOT_BELT + throw_speed = 2 + throw_range = 9 + w_class = ITEMSIZE_SMALL + show_messages = 1 + + // Bluespace radios talk directly to telecomms equipment + var/bluespace_radio = FALSE + var/datum/weakref/bs_tx_weakref //Maybe misleading, this is the device to TRANSMIT TO + // For mappers or subtypes, to start them prelinked to these devices + var/bs_tx_preload_id + var/bs_rx_preload_id + + matter = list(MAT_GLASS = 25,MAT_STEEL = 75) + var/const/FREQ_LISTENING = 1 + var/list/internal_channels + + var/datum/radio_frequency/radio_connection + var/list/datum/radio_frequency/secure_radio_connections = new + +/obj/item/device/radio/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) + +/obj/item/device/radio/New() + ..() + wires = new(src) + internal_channels = default_internal_channels.Copy() + listening_objects += src + +/obj/item/device/radio/Destroy() + qdel(wires) + wires = null + listening_objects -= src + if(radio_controller) + radio_controller.remove_object(src, frequency) + for (var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + return ..() + + +/obj/item/device/radio/Initialize() + . = ..() + if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(frequency) + + for (var/ch_name in channels) + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + + if(bluespace_radio) + if(bs_tx_preload_id) + //Try to find a receiver + for(var/obj/machinery/telecomms/receiver/RX in telecomms_list) + if(RX.id == bs_tx_preload_id) //Again, bs_tx is the thing to TRANSMIT TO, so a receiver. + bs_tx_weakref = WEAKREF(RX) + RX.link_radio(src) + break + //Hmm, howabout an AIO machine + if(!bs_tx_weakref) + for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) + if(AIO.id == bs_tx_preload_id) + bs_tx_weakref = WEAKREF(AIO) + AIO.link_radio(src) + break + if(!bs_tx_weakref) + testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") + + if(bs_rx_preload_id) + var/found = 0 + //Try to find a transmitter + for(var/obj/machinery/telecomms/broadcaster/TX in telecomms_list) + if(TX.id == bs_rx_preload_id) //Again, bs_rx is the thing to RECEIVE FROM, so a transmitter. + TX.link_radio(src) + found = 1 + break + //Hmm, howabout an AIO machine + if(!found) + for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) + if(AIO.id == bs_rx_preload_id) + AIO.link_radio(src) + found = 1 + break + if(!found) + testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") + +/obj/item/device/radio/proc/recalculateChannels() + return + +/obj/item/device/radio/attack_self(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/item/device/radio/interact(mob/user) + if(!user) + return FALSE + + if(b_stat) + wires.Interact(user) + + return tgui_interact(user) + +/obj/item/device/radio/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Radio", name, parent_ui) + ui.open() + +/obj/item/device/radio/tgui_data(mob/user) + var/data[0] + + data["rawfreq"] = frequency + data["listening"] = listening + data["broadcasting"] = broadcasting + data["subspace"] = subspace_transmission + data["subspaceSwitchable"] = subspace_switchable + data["loudspeaker"] = loudspeaker + + data["mic_cut"] = (wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) + data["spk_cut"] = (wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) + + var/list/chanlist = list_channels(user) + if(islist(chanlist) && chanlist.len) + data["chan_list"] = chanlist + else + data["chan_list"] = null + + if(syndie) + data["useSyndMode"] = 1 + + data["minFrequency"] = PUBLIC_LOW_FREQ + data["maxFrequency"] = PUBLIC_HIGH_FREQ + + return data + +/obj/item/device/radio/proc/list_channels(var/mob/user) + return list_internal_channels(user) + +/obj/item/device/radio/proc/list_secure_channels(var/mob/user) + var/dat[0] + + for(var/ch_name in channels) + var/chan_stat = channels[ch_name] + var/listening = !!(chan_stat & FREQ_LISTENING) != 0 + + dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "freq" = radiochannels[ch_name]))) + + return dat + +/obj/item/device/radio/proc/list_internal_channels(var/mob/user) + var/dat[0] + for(var/internal_chan in internal_channels) + if(has_channel_access(user, internal_chan)) + dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "freq" = text2num(internal_chan)))) + + return dat + +/obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq) + if(!user) + return FALSE + + if(!(freq in internal_channels)) + return FALSE + + return user.has_internal_radio_channel_access(internal_channels[freq]) + +/mob/proc/has_internal_radio_channel_access(var/list/req_one_accesses) + var/obj/item/weapon/card/id/I = GetIdCard() + return has_access(list(), req_one_accesses, I ? I.GetAccess() : list()) + +/mob/observer/dead/has_internal_radio_channel_access(var/list/req_one_accesses) + return can_admin_interact() + +/obj/item/device/radio/proc/text_sec_channel(var/chan_name, var/chan_stat) + var/list = !!(chan_stat&FREQ_LISTENING)!=0 + return {" + [chan_name]
                    + Speaker: [list ? "Engaged" : "Disengaged"]
                    + "} + +/obj/item/device/radio/proc/ToggleBroadcast() + broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) + +/obj/item/device/radio/proc/ToggleReception() + listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) + +/obj/item/device/radio/CanUseTopic() + if(!on) + return STATUS_CLOSE + return ..() + +/obj/item/device/radio/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("setFrequency") + var/new_frequency = (text2num(params["freq"])) + if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) + new_frequency = sanitize_frequency(new_frequency) + set_frequency(new_frequency) + if(hidden_uplink) + if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) + usr << browse(null, "window=radio") + . = TRUE + if("broadcast") + ToggleBroadcast() + . = TRUE + if("listen") + ToggleReception() + . = TRUE + if("channel") + var/chan_name = params["channel"] + if(channels[chan_name] & FREQ_LISTENING) + channels[chan_name] &= ~FREQ_LISTENING + else + channels[chan_name] |= FREQ_LISTENING + . = TRUE + if("specFreq") + var/freq = params["channel"] + if(has_channel_access(usr, freq)) + set_frequency(text2num(freq)) + . = TRUE + if("subspace") + if(subspace_switchable) + subspace_transmission = !subspace_transmission + if(!subspace_transmission) + channels = list() + to_chat(usr, "Subspace Transmission is disabled") + else + recalculateChannels() + to_chat(usr, "Subspace Transmission is enabled") + . = TRUE + if("toggleLoudspeaker") + if(!subspace_switchable) + return + loudspeaker = !loudspeaker + + if(loudspeaker) + to_chat(usr, "Loadspeaker enabled.") + else + to_chat(usr, "Loadspeaker disabled.") + . = TRUE + + if(. && iscarbon(usr)) + playsound(src, "button", 10) + +GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer) + +/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels, var/states) //VOREStation Edit + + if(!GLOB.autospeaker) + return + var/datum/radio_frequency/connection = null + if(channel && channels && channels.len > 0) + if(channel == "department") + channel = channels[1] + connection = secure_radio_connections[channel] + else + connection = radio_connection + channel = null + if(!istype(connection)) + return + + if(!LAZYLEN(zlevels)) + zlevels = list(0) + //VOREStation Edit Start + if(!states) + states = "states" + //VOREStation Edit End + GLOB.autospeaker.SetName(from) + Broadcast_Message(connection, GLOB.autospeaker, + 0, "*garbled automated announcement*", src, + message_to_multilingual(message, GLOB.all_languages[LANGUAGE_GALCOM]), from, "Automated Announcement", from, "synthesized voice", + DATA_FAKE, 0, zlevels, connection.frequency, states) //VOREStation Edit + +// Interprets the message mode when talking into a radio, possibly returning a connection datum +/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) + // If a channel isn't specified, send to common. + if(!message_mode || message_mode == "headset") + return radio_connection + + // Otherwise, if a channel is specified, look for it. + if(channels && channels.len > 0) + if (message_mode == "department") // Department radio shortcut + message_mode = channels[1] + + if (channels[message_mode]) // only broadcast if the channel is set on + return secure_radio_connections[message_mode] + + // If we were to send to a channel we don't have, drop it. + return RADIO_CONNECTION_FAIL + +/obj/item/device/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") + if(!on) + return FALSE // the device has to be on + // Fix for permacell radios, but kinda eh about actually fixing them. + if(!M || !message_pieces) + return FALSE + + if(istype(M)) + M.trigger_aiming(TARGET_CAN_RADIO) + + // Uncommenting this. To the above comment: + // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom + if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact + return FALSE + + if(!radio_connection) + set_frequency(frequency) + + /* Quick introduction: + This new radio system uses a very robust FTL signaling technology unoriginally + dubbed "subspace" which is somewhat similar to 'blue-space' but can't + actually transmit large mass. Headsets are the only radio devices capable + of sending subspace transmissions to the Communications Satellite. + + A headset sends a signal to a subspace listener/reciever elsewhere in space, + the signal gets processed and logged, and an audible transmission gets sent + to each individual headset. + */ + + //#### Grab the connection datum ####// + var/message_mode = handle_message_mode(M, message_pieces, channel) + switch(message_mode) + if(RADIO_CONNECTION_FAIL) + return FALSE + if(RADIO_CONNECTION_NON_SUBSPACE) + return TRUE + + if(!istype(message_mode, /datum/radio_frequency)) + return FALSE + + var/pos_z = get_z(src) + var/datum/radio_frequency/connection = message_mode + + //#### Tagging the signal with all appropriate identity values ####// + + // ||-- The mob's name identity --|| + var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) + var/real_name = M.real_name // mob's real name + var/mobkey = "none" // player key associated with mob + var/voicemask = 0 // the speaker is wearing a voice mask + if(M.client) + mobkey = M.key // assign the mob's key + + + var/jobname // the mob's "job" + + // --- Human: use their actual job --- + if (ishuman(M)) + var/mob/living/carbon/human/H = M + jobname = H.get_assignment() + + // --- Carbon Nonhuman --- + else if (iscarbon(M)) // Nonhuman carbon mob + jobname = "No id" + + // --- AI --- + else if (isAI(M)) + jobname = "AI" + + // --- Cyborg --- + else if (isrobot(M)) + jobname = "Cyborg" + + // --- Personal AI (pAI) --- + else if (istype(M, /mob/living/silicon/pai)) + jobname = "Personal AI" + + // --- Unidentifiable mob --- + else + jobname = "Unknown" + + + // --- Modifications to the mob's identity --- + + // The mob is disguising their identity: + if (ishuman(M) && M.GetVoice() != real_name) + displayname = M.GetVoice() + jobname = "Unknown" + voicemask = 1 + + // First, we want to generate a new radio signal + var/datum/signal/signal = new + + // --- Finally, tag the actual signal with the appropriate values --- + signal.data = list( + // Identity-associated tags: + "mob" = M, // store a reference to the mob + "mobtype" = M.type, // the mob's type + "realname" = real_name, // the mob's real name + "name" = displayname, // the mob's display name + "job" = jobname, // the mob's job + "key" = mobkey, // the mob's key + "vmessage" = message_to_multilingual(pick(M.speak_emote)), // the message to display if the voice wasn't understood + "vname" = M.voice_name, // the name to display if the voice wasn't understood + "vmask" = voicemask, // 1 if the mob is using a voice gas mask + + // We store things that would otherwise be kept in the actual mob + // so that they can be logged even AFTER the mob is deleted or something + + // Other tags: + "compression" = rand(45,50), // compressed radio signal + "message" = message_pieces, // the actual sent message + "connection" = connection, // the radio connection to use + "radio" = src, // stores the radio used for transmission + "slow" = 0, // how much to sleep() before broadcasting - simulates net lag + "traffic" = 0, // dictates the total traffic sum that the signal went through + "type" = SIGNAL_NORMAL, // determines what type of radio input it is: normal broadcast + "server" = null, // the last server to log this signal + "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery + "level" = pos_z, // The source's z level + "verb" = verb + ) + signal.frequency = connection.frequency // Quick frequency set + + var/filter_type = DATA_LOCAL //If we end up having to send it the old fashioned way, it's with this data var. + + /* ###### Bluespace radios talk directly to receivers (and only directly to receivers) ###### */ + if(bluespace_radio) + //Nothing to transmit to + if(!bs_tx_weakref) + to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") + return FALSE + + var/obj/machinery/telecomms/tx_to = bs_tx_weakref.resolve() + //Was linked, now destroyed or something + if(!tx_to) + bs_tx_weakref = null + to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") + return FALSE + + //Transmitted in the blind. If we get a message back, cool. If not, oh well. + signal.transmission_method = TRANSMISSION_BLUESPACE + return tx_to.receive_signal(signal) + + /* ###### Radios with subspace_transmission can only broadcast through subspace (unless they have adhoc_fallback) ###### */ + else if(subspace_transmission) + var/list/jamming = is_jammed(src) + if(jamming) + var/distance = 0 + var/area/our_area = get_area(src) + if(our_area.no_comms) + distance = 99 + else + distance = jamming["distance"] + to_chat(M, "\icon[src][bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") + return FALSE + + // First, we want to generate a new radio signal + signal.transmission_method = TRANSMISSION_SUBSPACE + + //#### Sending the signal to all subspace receivers ####// + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in telecomms_list) + R.receive_signal(signal) + + // Receiving code can be located in Telecommunications.dm + if(signal.data["done"] && (pos_z in signal.data["level"])) + return TRUE //Huzzah, sent via subspace + + else if(adhoc_fallback) //Less huzzah, we have to fallback + to_chat(loc, "\The [src] pings as it falls back to local radio transmission.") + subspace_transmission = FALSE + + else //Oh well + return FALSE + + /* ###### Intercoms and station-bounced radios ###### */ + else + /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ + if(istype(src, /obj/item/device/radio/intercom)) + filter_type = DATA_INTERCOM + + /* --- Try to send a normal subspace broadcast first */ + signal.transmission_method = TRANSMISSION_SUBSPACE + signal.data["compression"] = 0 + + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in telecomms_list) + R.receive_signal(signal) + + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + if(signal.data["done"] && (pos_z in signal.data["level"])) + if(adhoc_fallback) + to_chat(loc, "\The [src] pings as it reestablishes subspace communications.") + subspace_transmission = TRUE + // we're done here. + return TRUE + + //Nothing handled any sort of remote radio-ing and returned before now, just squawk on this zlevel. + return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), + src, message_pieces, displayname, jobname, real_name, M.voice_name, + filter_type, signal.data["compression"], using_map.get_map_levels(pos_z), connection.frequency, verb) + + +/obj/item/device/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") + if(broadcasting) + if(get_dist(src, M) <= canhear_range) + talk_into(M, message_pieces, null, verb) + +/obj/item/device/radio/proc/receive_range(freq, level) + // check if this radio can receive on the given frequency, and if so, + // what the range is in which mobs will hear the radio + // returns: -1 if can't receive, range otherwise + if(wires.is_cut(WIRE_RADIO_RECEIVER)) + return -1 + if(!listening) + return -1 + if(is_jammed(src)) + return -1 + if(!(0 in level)) + var/pos_z = get_z(src) + if(!(pos_z in level)) + return -1 + if(freq in ANTAG_FREQS) + if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys + return -1 + if(freq in CENT_FREQS) + if(!(src.centComm))//Checks to see if it's allowed on that frequency, based on the encryption keys + return -1 + if (!on) + return -1 + if (!freq) //recieved on main frequency + if (!listening) + return -1 + else + var/accept = (freq==frequency && listening) + if (!accept) + for (var/ch_name in channels) + var/datum/radio_frequency/RF = secure_radio_connections[ch_name] + if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) + accept = 1 + break + if (!accept) + return -1 + return canhear_range + +/obj/item/device/radio/proc/send_hear(freq, level) + var/range = receive_range(freq, level) + if(range > -1 && loudspeaker) + return get_mobs_or_objects_in_view(range, src) + + +/obj/item/device/radio/examine(mob/user) + . = ..() + + if((in_range(src, user) || loc == user)) + if(b_stat) + . += "\The [src] can be attached and modified!" + else + . += "\The [src] can not be modified or attached!" + +/obj/item/device/radio/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + user.set_machine(src) + if (!W.has_tool_quality(TOOL_SCREWDRIVER)) + return + b_stat = !( b_stat ) + if(!istype(src, /obj/item/device/radio/beacon)) + if (b_stat) + user.show_message("\The [src] can now be attached and modified!") + else + user.show_message("\The [src] can no longer be modified or attached!") + updateDialog() + //Foreach goto(83) + add_fingerprint(user) + return + else return + +/obj/item/device/radio/emp_act(severity) + broadcasting = 0 + listening = 0 + for (var/ch_name in channels) + channels[ch_name] = 0 + ..() + +/////////////////////////////// +//////////Borg Radios////////// +/////////////////////////////// +//Giving borgs their own radio to have some more room to work with -Sieve + +/obj/item/device/radio/borg + var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks + var/obj/item/device/encryptionkey/keyslot = null//Borg radios can handle a single encryption key + icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. + icon_state = "radio" + canhear_range = 0 + subspace_transmission = TRUE + subspace_switchable = TRUE + +/obj/item/device/radio/borg/Destroy() + myborg = null + return ..() + +/obj/item/device/radio/borg/list_channels(var/mob/user) + return list_secure_channels(user) + +/obj/item/device/radio/borg/talk_into() + . = ..() + if (isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + var/datum/robot_component/C = R.components["radio"] + R.cell_use_power(C.active_usage) + +/obj/item/device/radio/borg/attackby(obj/item/weapon/W as obj, mob/user as mob) +// ..() + user.set_machine(src) + if (!(W.has_tool_quality(TOOL_SCREWDRIVER) || istype(W, /obj/item/device/encryptionkey))) + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(keyslot) + + + for(var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + + + if(keyslot) + var/turf/T = get_turf(user) + if(T) + keyslot.loc = T + keyslot = null + + recalculateChannels() + to_chat(user, "You pop out the encryption key in the radio!") + playsound(src, W.usesound, 50, 1) + + else + to_chat(user, "This radio doesn't have any encryption keys!") + + if(istype(W, /obj/item/device/encryptionkey/)) + if(keyslot) + to_chat(user, "The radio can't hold another key!") + return + + if(!keyslot) + user.drop_item() + W.loc = src + keyslot = W + + recalculateChannels() + + return + +/obj/item/device/radio/borg/recalculateChannels() + src.channels = list() + src.syndie = 0 + + var/mob/living/silicon/robot/D = src.loc + if(D.module) + for(var/ch_name in D.module.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] += D.module.channels[ch_name] + if(keyslot) + for(var/ch_name in keyslot.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] += keyslot.channels[ch_name] + + if(keyslot.syndie) + src.syndie = 1 + + for (var/ch_name in src.channels) + if(!radio_controller) + sleep(30) // Waiting for the radio_controller to be created. + if(!radio_controller) + src.name = "broken radio" + return + + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + + return + +/obj/item/device/radio/proc/config(op) + if(radio_controller) + for (var/ch_name in channels) + radio_controller.remove_object(src, radiochannels[ch_name]) + secure_radio_connections = new + channels = op + if(radio_controller) + for (var/ch_name in op) + secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + return + +/obj/item/device/radio/off + listening = 0 + +/obj/item/device/radio/phone + broadcasting = 0 + icon = 'icons/obj/items.dmi' + icon_state = "red_phone" + listening = 1 + name = "phone" + anchored = FALSE + +/obj/item/device/radio/phone/medbay + frequency = MED_I_FREQ + +/obj/item/device/radio/phone/medbay/New() + ..() + internal_channels = default_medbay_channels.Copy() diff --git a/code/game/objects/items/devices/scanners/health.dm b/code/game/objects/items/devices/scanners/health.dm index 6d854e201c5..2d0b040c991 100644 --- a/code/game/objects/items/devices/scanners/health.dm +++ b/code/game/objects/items/devices/scanners/health.dm @@ -59,11 +59,11 @@ if (!ishuman(M) || M.isSynthetic()) //these sensors are designed for organic life dat += "Analyzing Results for ERROR:\n\tOverall Status: ERROR
                    " - dat += "\tKey: Suffocation/Toxin/Burns/Brute
                    " - dat += "\tDamage Specifics: ? - ? - ? - ?
                    " + dat += "\tKey: [span_cyan("Suffocation")]/[span_green("Toxin")]/[span_orange("Burns")]/[span_red("Brute")]
                    " + dat += "\tDamage Specifics: [span_cyan("?")] - [span_green("?")] - [span_orange("?")] - [span_red("?")]
                    " dat += "Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)

                    " dat += "Warning: Blood Level ERROR: --% --cl. Type: ERROR
                    " - dat += "Subject's pulse: -- bpm." + dat += "Subject's pulse: [span_red("-- bpm.")]" user.show_message(dat, 1) return @@ -78,8 +78,8 @@ dat += "Overall Status: dead
                    " else dat += "Analyzing Results for [M]:\n\t Overall Status: [M.stat > 1 ? "dead" : "[round((M.health/M.getMaxHealth())*100) ]% healthy"]
                    " - dat += "\tKey: Suffocation/Toxin/Burns/Brute
                    " - dat += "\tDamage Specifics: [OX] - [TX] - [BU] - [BR]
                    " + dat += "\tKey: [span_cyan("Suffocation")]/[span_green("Toxin")]/[span_orange("Burns")]/[span_red("Brute")]
                    " + dat += "\tDamage Specifics: [span_cyan("[OX]")] - [span_green("[TX]")] - [span_orange("[BU]")] - [span_red("[BR]")]
                    " dat += "Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)

                    " //VOREStation edit/addition starts if(M.timeofdeath && (M.stat == DEAD || (M.status_flags & FAKEDEATH))) @@ -99,14 +99,14 @@ else dat += " [capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : 0]" dat += "[(org.status & ORGAN_BLEEDING)?"\[Bleeding\]":""] - " - dat += "[(org.burn_dam > 0) ? "[org.burn_dam]" : 0]
                    " + dat += "[(org.burn_dam > 0) ? "[span_orange("[org.burn_dam]")]" : 0]
                    " else dat += " Limbs are OK.
                    " - OX = M.getOxyLoss() > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal" - TX = M.getToxLoss() > 50 ? "Dangerous amount of toxins detected" : "Subject bloodstream toxin level minimal" - BU = M.getFireLoss() > 50 ? "Severe burn damage detected" : "Subject burn injury status O.K" - BR = M.getBruteLoss() > 50 ? "Severe anatomical damage detected" : "Subject brute-force injury status O.K" + OX = M.getOxyLoss() > 50 ? "[span_cyan("Severe oxygen deprivation detected")]" : "Subject bloodstream oxygen level normal" + TX = M.getToxLoss() > 50 ? "[span_green("Dangerous amount of toxins detected")]" : "Subject bloodstream toxin level minimal" + BU = M.getFireLoss() > 50 ? "[span_orange("Severe burn damage detected")]" : "Subject burn injury status O.K" + BR = M.getBruteLoss() > 50 ? "[span_red("Severe anatomical damage detected")]" : "Subject brute-force injury status O.K" if(M.status_flags & FAKEDEATH) OX = fake_oxy > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal" dat += "[OX] | [TX] | [BU] | [BR]
                    " diff --git a/code/game/objects/items/devices/scanners/sleevemate.dm b/code/game/objects/items/devices/scanners/sleevemate.dm index 6de4c66497b..0109335b0d8 100644 --- a/code/game/objects/items/devices/scanners/sleevemate.dm +++ b/code/game/objects/items/devices/scanners/sleevemate.dm @@ -309,3 +309,17 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob icon_state = "[initial(icon_state)]_on" else icon_state = initial(icon_state) + +/obj/item/device/sleevemate/emag_act(var/remaining_charges, var/mob/user) + to_chat(user,"You hack [src]!") + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + if(istype(src.loc,/mob/living)) + var/mob/living/L = src.loc + L.unEquip(src) + src.forceMove(get_turf(src)) + new /obj/item/device/bodysnatcher(src.loc) + qdel(src) + return 1 diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm index a92bf815b84..61526248a5a 100644 --- a/code/game/objects/items/devices/spy_bug.dm +++ b/code/game/objects/items/devices/spy_bug.dm @@ -1,275 +1,275 @@ -/obj/item/device/camerabug - name = "mobile camera pod" - desc = "A camera pod used by tactical operators. Must be linked to a camera scanner unit." - icon = 'icons/obj/grenade.dmi' - icon_state = "camgrenade" - item_state = "empgrenade" - w_class = ITEMSIZE_SMALL - force = 0 - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) - var/obj/item/device/bug_monitor/linkedmonitor - var/brokentype = /obj/item/brokenbug - -// var/obj/item/device/radio/bug/radio - var/obj/machinery/camera/bug/camera - var/camtype = /obj/machinery/camera/bug - -/obj/item/device/camerabug/New() - ..() -// radio = new(src) - camera = new camtype(src) - -/obj/item/device/camerabug/attack_self(mob/user) - if(user.a_intent == I_HURT) - to_chat(user, "You crush the [src] under your foot, breaking it.") - visible_message("[user.name] crushes the [src] under their foot, breaking it!") - new brokentype(get_turf(src)) - spawn(0) - qdel(src) -/* else - user.set_machine(radio) - radio.interact(user) -*/ -/obj/item/device/camerabug/verb/reset() - set name = "Reset camera bug" - set category = "Object" - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - qdel(camera) - camera = new camtype(src) - to_chat(usr, "You turn the [src] off and on again, delinking it from any monitors.") - -/obj/item/brokenbug - name = "broken mobile camera pod" - desc = "A camera pod formerly used by tactical operators. The lens is smashed, and the circuits are damaged beyond repair." - icon = 'icons/obj/grenade.dmi' - icon_state = "camgrenadebroken" - item_state = "empgrenade" - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - origin_tech = list(TECH_ENGINEERING = 1) - -/obj/item/brokenbug/spy - name = "broken bug" - desc = "" //Even when it's broken it's inconspicuous - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "nothing" - layer = TURF_LAYER+0.2 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - origin_tech = list(TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) //crush it and you lose the data - force = 0 - throwforce = 5.0 - throw_range = 15 - throw_speed = 3 - -/obj/item/device/camerabug/spy - name = "bug" - desc = "" //Nothing to see here - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "nothing" - layer = TURF_LAYER+0.2 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) - camtype = /obj/machinery/camera/bug/spy - -/obj/item/device/camerabug/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - . += "It has a tiny camera inside. Needs to be both configured and brought in contact with monitor device to be fully functional." - -/obj/item/device/camerabug/update_icon() - ..() - - if(anchored) // Standard versions are relatively obvious if not hidden in a container. Anchoring them is advised, to disguise them. - alpha = 50 - else - alpha = 255 - -/obj/item/device/camerabug/attackby(obj/item/W as obj, mob/living/user as mob) - if(istype(W, /obj/item/device/bug_monitor)) - var/obj/item/device/bug_monitor/SM = W - if(!linkedmonitor) - to_chat(user, "\The [src] has been paired with \the [SM].") - SM.pair(src) - linkedmonitor = SM - else if (linkedmonitor == SM) - to_chat(user, "\The [src] has been unpaired from \the [SM].") - linkedmonitor.unpair(src) - linkedmonitor = null - else - to_chat(user, "Error: The device is linked to another monitor.") - - else if(W.is_wrench() && user.a_intent != I_HURT) - if(isturf(loc)) - anchored = !anchored - - to_chat(user, "You [anchored ? "" : "un"]secure \the [src].") - - update_icon() - return - else - if(W.force >= 5) - visible_message("\The [src] lens shatters!") - new brokentype(get_turf(src)) - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - spawn(0) - qdel(src) - ..() - -/obj/item/device/camerabug/bullet_act() - visible_message("The [src] lens shatters!") - new brokentype(get_turf(src)) - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - spawn(0) - qdel(src) - -/obj/item/device/camerabug/Destroy() - if(linkedmonitor) - linkedmonitor.unpair(src) - linkedmonitor = null - ..() - -/obj/item/device/bug_monitor - name = "mobile camera pod monitor" - desc = "A portable camera console designed to work with mobile camera pods." - icon = 'icons/obj/device.dmi' - icon_state = "forensic0" - item_state = "electronic" - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) - - var/operating = 0 -// var/obj/item/device/radio/bug/radio - var/obj/machinery/camera/bug/selected_camera - var/list/obj/machinery/camera/bug/cameras = new() -/* -/obj/item/device/bug_monitor/New() - radio = new(src) -*/ -/obj/item/device/bug_monitor/attack_self(mob/user) - if(operating) - return - -// radio.attack_self(user) - view_cameras(user) - -/obj/item/device/bug_monitor/attackby(obj/item/W as obj, mob/living/user as mob) - if(istype(W, /obj/item/device/camerabug)) - W.attackby(src, user) - else - return ..() - -/obj/item/device/bug_monitor/proc/unpair(var/obj/item/device/camerabug/SB) - if(SB.camera in cameras) - cameras -= SB.camera - -/obj/item/device/bug_monitor/proc/pair(var/obj/item/device/camerabug/SB) - cameras += SB.camera - -/obj/item/device/bug_monitor/proc/view_cameras(mob/user) - if(!can_use_cam(user)) - return - - selected_camera = cameras[1] - user.reset_view(selected_camera) - view_camera(user) - - operating = 1 - while(selected_camera && Adjacent(user)) - selected_camera = tgui_input_list(usr, "Select camera to view.", "Camera Choice", cameras) - selected_camera = null - operating = 0 - -/obj/item/device/bug_monitor/proc/view_camera(mob/user) - spawn(0) - while(selected_camera && Adjacent(user)) - var/turf/T = get_turf(selected_camera) - if(!T || !is_on_same_plane_or_station(T.z, user.z) || !selected_camera.can_use()) - user.unset_machine() - user.reset_view(null) - to_chat(user, "Link to [selected_camera] has been lost.") - src.unpair(selected_camera.loc) - sleep(90) - else - user.set_machine(selected_camera) - user.reset_view(selected_camera) - sleep(10) - user.unset_machine() - user.reset_view(null) - -/obj/item/device/bug_monitor/proc/can_use_cam(mob/user) - if(operating) - return - - if(!cameras.len) - to_chat(user, "No paired cameras detected!") - to_chat(user, "Bring a camera in contact with this device to pair the camera.") - return - - return 1 - -/obj/item/device/bug_monitor/spy - name = "\improper PDA" - desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." - icon = 'icons/obj/pda.dmi' - icon_state = "pda" - item_state = "electronic" - origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) - -/obj/item/device/bug_monitor/spy/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made." - -/obj/machinery/camera/bug/check_eye(var/mob/user as mob) - return 0 - -/obj/machinery/camera/bug - network = list(NETWORK_SECURITY) - -/obj/machinery/camera/bug/New() - ..() - name = "Camera #[rand(1000,9999)]" - c_tag = name - -/obj/machinery/camera/bug/spy - // These cheap toys are accessible from the mercenary camera console as well - only the antag ones though! - network = list(NETWORK_MERCENARY) - -/obj/machinery/camera/bug/spy/New() - ..() - name = "DV-136ZB #[rand(1000,9999)]" - c_tag = name - -/* //These were originally supposed to have radios in them. Doesn't work. -/obj/item/device/radio/bug - listening = 0 //turn it on first - frequency = 1359 //sec comms - broadcasting = 0 - canhear_range = 1 - name = "camera bug device" - icon_state = "syn_cypherkey" - -/obj/item/device/radio/bug/spy - listening = 0 - frequency = 1473 - broadcasting = 0 - canhear_range = 1 - name = "spy device" - icon_state = "syn_cypherkey" +/obj/item/device/camerabug + name = "mobile camera pod" + desc = "A camera pod used by tactical operators. Must be linked to a camera scanner unit." + icon = 'icons/obj/grenade.dmi' + icon_state = "camgrenade" + item_state = "empgrenade" + w_class = ITEMSIZE_SMALL + force = 0 + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + var/obj/item/device/bug_monitor/linkedmonitor + var/brokentype = /obj/item/brokenbug + +// var/obj/item/device/radio/bug/radio + var/obj/machinery/camera/bug/camera + var/camtype = /obj/machinery/camera/bug + +/obj/item/device/camerabug/New() + ..() +// radio = new(src) + camera = new camtype(src) + +/obj/item/device/camerabug/attack_self(mob/user) + if(user.a_intent == I_HURT) + to_chat(user, "You crush the [src] under your foot, breaking it.") + visible_message("[user.name] crushes the [src] under their foot, breaking it!") + new brokentype(get_turf(src)) + spawn(0) + qdel(src) +/* else + user.set_machine(radio) + radio.interact(user) +*/ +/obj/item/device/camerabug/verb/reset() + set name = "Reset camera bug" + set category = "Object" + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + qdel(camera) + camera = new camtype(src) + to_chat(usr, "You turn the [src] off and on again, delinking it from any monitors.") + +/obj/item/brokenbug + name = "broken mobile camera pod" + desc = "A camera pod formerly used by tactical operators. The lens is smashed, and the circuits are damaged beyond repair." + icon = 'icons/obj/grenade.dmi' + icon_state = "camgrenadebroken" + item_state = "empgrenade" + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + origin_tech = list(TECH_ENGINEERING = 1) + +/obj/item/brokenbug/spy + name = "broken bug" + desc = "" //Even when it's broken it's inconspicuous + icon = 'icons/obj/weapons.dmi' + icon_state = "eshield" + item_state = "nothing" + layer = TURF_LAYER+0.2 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + origin_tech = list(TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) //crush it and you lose the data + force = 0 + throwforce = 5.0 + throw_range = 15 + throw_speed = 3 + +/obj/item/device/camerabug/spy + name = "bug" + desc = "" //Nothing to see here + icon = 'icons/obj/weapons.dmi' + icon_state = "eshield" + item_state = "nothing" + layer = TURF_LAYER+0.2 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) + camtype = /obj/machinery/camera/bug/spy + +/obj/item/device/camerabug/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + . += "It has a tiny camera inside. Needs to be both configured and brought in contact with monitor device to be fully functional." + +/obj/item/device/camerabug/update_icon() + ..() + + if(anchored) // Standard versions are relatively obvious if not hidden in a container. Anchoring them is advised, to disguise them. + alpha = 50 + else + alpha = 255 + +/obj/item/device/camerabug/attackby(obj/item/W as obj, mob/living/user as mob) + if(istype(W, /obj/item/device/bug_monitor)) + var/obj/item/device/bug_monitor/SM = W + if(!linkedmonitor) + to_chat(user, "\The [src] has been paired with \the [SM].") + SM.pair(src) + linkedmonitor = SM + else if (linkedmonitor == SM) + to_chat(user, "\The [src] has been unpaired from \the [SM].") + linkedmonitor.unpair(src) + linkedmonitor = null + else + to_chat(user, "Error: The device is linked to another monitor.") + + else if(W.has_tool_quality(TOOL_WRENCH) && user.a_intent != I_HURT) + if(isturf(loc)) + anchored = !anchored + + to_chat(user, "You [anchored ? "" : "un"]secure \the [src].") + + update_icon() + return + else + if(W.force >= 5) + visible_message("\The [src] lens shatters!") + new brokentype(get_turf(src)) + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + spawn(0) + qdel(src) + ..() + +/obj/item/device/camerabug/bullet_act() + visible_message("The [src] lens shatters!") + new brokentype(get_turf(src)) + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + spawn(0) + qdel(src) + +/obj/item/device/camerabug/Destroy() + if(linkedmonitor) + linkedmonitor.unpair(src) + linkedmonitor = null + ..() + +/obj/item/device/bug_monitor + name = "mobile camera pod monitor" + desc = "A portable camera console designed to work with mobile camera pods." + icon = 'icons/obj/device.dmi' + icon_state = "forensic0" + item_state = "electronic" + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1) + + var/operating = 0 +// var/obj/item/device/radio/bug/radio + var/obj/machinery/camera/bug/selected_camera + var/list/obj/machinery/camera/bug/cameras = new() +/* +/obj/item/device/bug_monitor/New() + radio = new(src) +*/ +/obj/item/device/bug_monitor/attack_self(mob/user) + if(operating) + return + +// radio.attack_self(user) + view_cameras(user) + +/obj/item/device/bug_monitor/attackby(obj/item/W as obj, mob/living/user as mob) + if(istype(W, /obj/item/device/camerabug)) + W.attackby(src, user) + else + return ..() + +/obj/item/device/bug_monitor/proc/unpair(var/obj/item/device/camerabug/SB) + if(SB.camera in cameras) + cameras -= SB.camera + +/obj/item/device/bug_monitor/proc/pair(var/obj/item/device/camerabug/SB) + cameras += SB.camera + +/obj/item/device/bug_monitor/proc/view_cameras(mob/user) + if(!can_use_cam(user)) + return + + selected_camera = cameras[1] + user.reset_view(selected_camera) + view_camera(user) + + operating = 1 + while(selected_camera && Adjacent(user)) + selected_camera = tgui_input_list(usr, "Select camera to view.", "Camera Choice", cameras) + selected_camera = null + operating = 0 + +/obj/item/device/bug_monitor/proc/view_camera(mob/user) + spawn(0) + while(selected_camera && Adjacent(user)) + var/turf/T = get_turf(selected_camera) + if(!T || !is_on_same_plane_or_station(T.z, user.z) || !selected_camera.can_use()) + user.unset_machine() + user.reset_view(null) + to_chat(user, "Link to [selected_camera] has been lost.") + src.unpair(selected_camera.loc) + sleep(90) + else + user.set_machine(selected_camera) + user.reset_view(selected_camera) + sleep(10) + user.unset_machine() + user.reset_view(null) + +/obj/item/device/bug_monitor/proc/can_use_cam(mob/user) + if(operating) + return + + if(!cameras.len) + to_chat(user, "No paired cameras detected!") + to_chat(user, "Bring a camera in contact with this device to pair the camera.") + return + + return 1 + +/obj/item/device/bug_monitor/spy + name = "\improper PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." + icon = 'icons/obj/pda.dmi' + icon_state = "pda" + item_state = "electronic" + origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) + +/obj/item/device/bug_monitor/spy/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made." + +/obj/machinery/camera/bug/check_eye(var/mob/user as mob) + return 0 + +/obj/machinery/camera/bug + network = list(NETWORK_SECURITY) + +/obj/machinery/camera/bug/New() + ..() + name = "Camera #[rand(1000,9999)]" + c_tag = name + +/obj/machinery/camera/bug/spy + // These cheap toys are accessible from the mercenary camera console as well - only the antag ones though! + network = list(NETWORK_MERCENARY) + +/obj/machinery/camera/bug/spy/New() + ..() + name = "DV-136ZB #[rand(1000,9999)]" + c_tag = name + +/* //These were originally supposed to have radios in them. Doesn't work. +/obj/item/device/radio/bug + listening = 0 //turn it on first + frequency = 1359 //sec comms + broadcasting = 0 + canhear_range = 1 + name = "camera bug device" + icon_state = "syn_cypherkey" + +/obj/item/device/radio/bug/spy + listening = 0 + frequency = 1473 + broadcasting = 0 + canhear_range = 1 + name = "spy device" + icon_state = "syn_cypherkey" */ \ No newline at end of file diff --git a/code/game/objects/items/devices/suit_cooling.dm b/code/game/objects/items/devices/suit_cooling.dm index dda476191b2..1d073ab3fe6 100644 --- a/code/game/objects/items/devices/suit_cooling.dm +++ b/code/game/objects/items/devices/suit_cooling.dm @@ -146,7 +146,7 @@ to_chat(user, "You switch \the [src] [on ? "on" : "off"].") /obj/item/device/suit_cooling_unit/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (W.is_screwdriver()) + if (W.has_tool_quality(TOOL_SCREWDRIVER)) if(cover_open) cover_open = 0 to_chat(user, "You screw the panel into place.") @@ -237,7 +237,7 @@ return cell /obj/item/device/suit_cooling_unit/emergency/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (W.is_screwdriver()) + if (W.has_tool_quality(TOOL_SCREWDRIVER)) to_chat(user, "This cooler's cell is permanently installed!") return diff --git a/code/game/objects/items/devices/t_scanner.dm b/code/game/objects/items/devices/t_scanner.dm index 415f22d8d80..a1c8c6c7228 100644 --- a/code/game/objects/items/devices/t_scanner.dm +++ b/code/game/objects/items/devices/t_scanner.dm @@ -1,150 +1,150 @@ -#define OVERLAY_CACHE_LEN 50 - -/obj/item/device/t_scanner - name = "\improper T-ray scanner" - desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - icon_state = "t-ray0" - item_state = "t-ray" - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - matter = list(MAT_STEEL = 150) - origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) - - var/scan_range = 1 - - var/on = 0 - var/list/active_scanned = list() //assoc list of objects being scanned, mapped to their overlay - var/client/user_client //since making sure overlays are properly added and removed is pretty important, so we track the current user explicitly - var/flicker = 0 - - var/global/list/overlay_cache = list() //cache recent overlays - -/obj/item/device/t_scanner/update_icon() - icon_state = "t-ray[on]" - -/obj/item/device/t_scanner/attack_self(mob/user) - set_active(!on) - -/obj/item/device/t_scanner/proc/set_active(var/active) - on = active - if(on) - START_PROCESSING(SSobj, src) - flicker = 0 - else - STOP_PROCESSING(SSobj, src) - set_user_client(null) - update_icon() - -//If reset is set, then assume the client has none of our overlays, otherwise we only send new overlays. -/obj/item/device/t_scanner/process() - if(!on) return - - //handle clients changing - var/client/loc_client = null - if(ismob(src.loc)) - var/mob/M = src.loc - loc_client = M.client - set_user_client(loc_client) - - //no sense processing if no-one is going to see it. - if(!user_client) return - - //get all objects in scan range - var/list/scanned = get_scanned_objects(scan_range) - var/list/update_add = scanned - active_scanned - var/list/update_remove = active_scanned - scanned - - //Add new overlays - for(var/obj/O in update_add) - var/image/overlay = get_overlay(O) - active_scanned[O] = overlay - user_client.images += overlay - - //Remove stale overlays - for(var/obj/O in update_remove) - user_client.images -= active_scanned[O] - active_scanned -= O - - //Flicker effect - for(var/obj/O in active_scanned) - var/image/overlay = active_scanned[O] - if(flicker) - overlay.alpha = 0 - else - overlay.alpha = 128 - flicker = !flicker - -//creates a new overlay for a scanned object -/obj/item/device/t_scanner/proc/get_overlay(obj/scanned) - //Use a cache so we don't create a whole bunch of new images just because someone's walking back and forth in a room. - //Also means that images are reused if multiple people are using t-rays to look at the same objects. - if(scanned in overlay_cache) - . = overlay_cache[scanned] - else - var/image/I = image(loc = scanned, icon = scanned.icon, icon_state = scanned.icon_state, layer = HUD_LAYER) - - //Pipes are special - if(istype(scanned, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/P = scanned - I.color = P.pipe_color - I.add_overlay(P.overlays) - - I.alpha = 128 - I.mouse_opacity = 0 - . = I - - // Add it to cache, cutting old entries if the list is too long - overlay_cache[scanned] = . - if(overlay_cache.len > OVERLAY_CACHE_LEN) - overlay_cache.Cut(1, overlay_cache.len-OVERLAY_CACHE_LEN-1) - -/obj/item/device/t_scanner/proc/get_scanned_objects(var/scan_dist) - . = list() - - var/turf/center = get_turf(src.loc) - if(!center) return - - for(var/turf/T in range(scan_range, center)) - if(!!T.is_plating()) - continue - - for(var/obj/O in T.contents) - if(O.level != 1) - continue - if(!O.invisibility) - continue //if it's already visible don't need an overlay for it - . += O - -/obj/item/device/t_scanner/proc/set_user_client(var/client/new_client) - if(new_client == user_client) - return - if(user_client) - for(var/scanned in active_scanned) - user_client.images -= active_scanned[scanned] - if(new_client) - for(var/scanned in active_scanned) - new_client.images += active_scanned[scanned] - else - active_scanned.Cut() - - user_client = new_client - -/obj/item/device/t_scanner/dropped(mob/user) - set_user_client(null) - -/obj/item/device/t_scanner/upgraded - name = "Upgraded T-ray Scanner" - desc = "An upgraded version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - matter = list(MAT_STEEL = 500, PHORON = 150) - origin_tech = list(TECH_MAGNET = 4, TECH_ENGINEERING = 5) - scan_range = 3 - -/obj/item/device/t_scanner/advanced - name = "Advanced T-ray Scanner" - desc = "An advanced version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - matter = list(MAT_STEEL = 1500, PHORON = 200, SILVER = 250) - origin_tech = list(TECH_MAGNET = 7, TECH_ENGINEERING = 7, TECH_MATERIAL = 6) - scan_range = 7 - - +#define OVERLAY_CACHE_LEN 50 + +/obj/item/device/t_scanner + name = "\improper T-ray scanner" + desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + icon_state = "t-ray0" + item_state = "t-ray" + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + matter = list(MAT_STEEL = 150) + origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) + + var/scan_range = 1 + + var/on = 0 + var/list/active_scanned = list() //assoc list of objects being scanned, mapped to their overlay + var/client/user_client //since making sure overlays are properly added and removed is pretty important, so we track the current user explicitly + var/flicker = 0 + + var/global/list/overlay_cache = list() //cache recent overlays + +/obj/item/device/t_scanner/update_icon() + icon_state = "t-ray[on]" + +/obj/item/device/t_scanner/attack_self(mob/user) + set_active(!on) + +/obj/item/device/t_scanner/proc/set_active(var/active) + on = active + if(on) + START_PROCESSING(SSobj, src) + flicker = 0 + else + STOP_PROCESSING(SSobj, src) + set_user_client(null) + update_icon() + +//If reset is set, then assume the client has none of our overlays, otherwise we only send new overlays. +/obj/item/device/t_scanner/process() + if(!on) return + + //handle clients changing + var/client/loc_client = null + if(ismob(src.loc)) + var/mob/M = src.loc + loc_client = M.client + set_user_client(loc_client) + + //no sense processing if no-one is going to see it. + if(!user_client) return + + //get all objects in scan range + var/list/scanned = get_scanned_objects(scan_range) + var/list/update_add = scanned - active_scanned + var/list/update_remove = active_scanned - scanned + + //Add new overlays + for(var/obj/O in update_add) + var/image/overlay = get_overlay(O) + active_scanned[O] = overlay + user_client.images += overlay + + //Remove stale overlays + for(var/obj/O in update_remove) + user_client.images -= active_scanned[O] + active_scanned -= O + + //Flicker effect + for(var/obj/O in active_scanned) + var/image/overlay = active_scanned[O] + if(flicker) + overlay.alpha = 0 + else + overlay.alpha = 128 + flicker = !flicker + +//creates a new overlay for a scanned object +/obj/item/device/t_scanner/proc/get_overlay(obj/scanned) + //Use a cache so we don't create a whole bunch of new images just because someone's walking back and forth in a room. + //Also means that images are reused if multiple people are using t-rays to look at the same objects. + if(scanned in overlay_cache) + . = overlay_cache[scanned] + else + var/image/I = image(loc = scanned, icon = scanned.icon, icon_state = scanned.icon_state, layer = HUD_LAYER) + + //Pipes are special + if(istype(scanned, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/P = scanned + I.color = P.pipe_color + I.add_overlay(P.overlays) + + I.alpha = 128 + I.mouse_opacity = 0 + . = I + + // Add it to cache, cutting old entries if the list is too long + overlay_cache[scanned] = . + if(overlay_cache.len > OVERLAY_CACHE_LEN) + overlay_cache.Cut(1, overlay_cache.len-OVERLAY_CACHE_LEN-1) + +/obj/item/device/t_scanner/proc/get_scanned_objects(var/scan_dist) + . = list() + + var/turf/center = get_turf(src.loc) + if(!center) return + + for(var/turf/T in range(scan_range, center)) + if(!!T.is_plating()) + continue + + for(var/obj/O in T.contents) + if(O.level != 1) + continue + if(!O.invisibility) + continue //if it's already visible don't need an overlay for it + . += O + +/obj/item/device/t_scanner/proc/set_user_client(var/client/new_client) + if(new_client == user_client) + return + if(user_client) + for(var/scanned in active_scanned) + user_client.images -= active_scanned[scanned] + if(new_client) + for(var/scanned in active_scanned) + new_client.images += active_scanned[scanned] + else + active_scanned.Cut() + + user_client = new_client + +/obj/item/device/t_scanner/dropped(mob/user) + set_user_client(null) + +/obj/item/device/t_scanner/upgraded + name = "Upgraded T-ray Scanner" + desc = "An upgraded version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + matter = list(MAT_STEEL = 500, PHORON = 150) + origin_tech = list(TECH_MAGNET = 4, TECH_ENGINEERING = 5) + scan_range = 3 + +/obj/item/device/t_scanner/advanced + name = "Advanced T-ray Scanner" + desc = "An advanced version of the terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + matter = list(MAT_STEEL = 1500, PHORON = 200, SILVER = 250) + origin_tech = list(TECH_MAGNET = 7, TECH_ENGINEERING = 7, TECH_MATERIAL = 6) + scan_range = 7 + + #undef OVERLAY_CACHE_LEN \ No newline at end of file diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 0ae3fd17b9d..b59bea3d3df 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -1,436 +1,436 @@ -/obj/item/device/taperecorder - name = "universal recorder" - desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." - icon_state = "taperecorder_empty" - item_state = "analyzer" - w_class = ITEMSIZE_SMALL - - matter = list(MAT_STEEL = 60,MAT_GLASS = 30) - - var/emagged = 0.0 - var/recording = 0.0 - var/playing = 0.0 - var/playsleepseconds = 0.0 - var/obj/item/device/tape/mytape = /obj/item/device/tape/random - var/canprint = 1 - slot_flags = SLOT_BELT - throwforce = 2 - throw_speed = 4 - throw_range = 20 - -/obj/item/device/taperecorder/New() - ..() - if(ispath(mytape)) - mytape = new mytape(src) - update_icon() - listening_objects += src - -/obj/item/device/taperecorder/empty - mytape = null - -/obj/item/device/taperecorder/Destroy() - listening_objects -= src - if(mytape) - qdel(mytape) - mytape = null - return ..() - - -/obj/item/device/taperecorder/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/device/tape)) - if(mytape) - to_chat(user, "There's already a tape inside.") - return - if(!user.unEquip(I)) - return - I.forceMove(src) - mytape = I - to_chat(user, "You insert [I] into [src].") - update_icon() - return - ..() - - -/obj/item/device/taperecorder/fire_act() - if(mytape) - mytape.ruin() //Fires destroy the tape - return ..() - - -/obj/item/device/taperecorder/attack_hand(mob/user) - if(user.get_inactive_hand() == src) - if(mytape) - eject() - return - ..() - - -/obj/item/device/taperecorder/verb/eject() - set name = "Eject Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape in \the [src].") - return - if(emagged) - to_chat(usr, "The tape seems to be stuck inside.") - return - - if(playing || recording) - stop() - to_chat(usr, "You remove [mytape] from [src].") - usr.put_in_hands(mytape) - mytape = null - update_icon() - - -/obj/item/device/taperecorder/hear_talk(mob/M, list/message_pieces, verb) - var/msg = multilingual_to_message(message_pieces, requires_machine_understands = TRUE, with_capitalization = TRUE) - var/voice = M.GetVoice() //Defined on living, returns name for normal mobs/ - if(mytape && recording) - mytape.record_speech("[voice] [verb], \"[msg]\"") - - -/obj/item/device/taperecorder/see_emote(mob/M as mob, text, var/emote_type) - if(emote_type != 2) //only hearable emotes - return - if(mytape && recording) - mytape.record_speech("[strip_html_properly(text)]") - - -/obj/item/device/taperecorder/show_message(msg, type, alt, alt_type) - var/recordedtext - if (msg && type == 2) //must be hearable - recordedtext = msg - else if (alt && alt_type == 2) - recordedtext = alt - else - return - if(mytape && recording) - mytape.record_noise("[strip_html_properly(recordedtext)]") - -/obj/item/device/taperecorder/emag_act(var/remaining_charges, var/mob/user) - if(emagged == 0) - emagged = 1 - recording = 0 - to_chat(user, "PZZTTPFFFT") - update_icon() - return 1 - else - to_chat(user, "It is already emagged!") - -/obj/item/device/taperecorder/proc/explode() - var/turf/T = get_turf(loc) - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "\The [src] explodes!") - if(T) - T.hotspot_expose(700,125) - explosion(T, -1, -1, 0, 4) - qdel(src) - return - -/obj/item/device/taperecorder/verb/record() - set name = "Start Recording" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(recording) - to_chat(usr, "You're already recording!") - return - if(playing) - to_chat(usr, "You can't record when playing!") - return - if(emagged) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(mytape.used_capacity < mytape.max_capacity) - to_chat(usr, "Recording started.") - recording = 1 - update_icon() - - mytape.record_speech("Recording started.") - - //count seconds until full, or recording is stopped - while(mytape && recording && mytape.used_capacity < mytape.max_capacity) - sleep(10) - mytape.used_capacity++ - if(mytape.used_capacity >= mytape.max_capacity) - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "The tape is full.") - stop_recording() - - - update_icon() - return - else - to_chat(usr, "The tape is full.") - - -/obj/item/device/taperecorder/proc/stop_recording() - //Sanity checks skipped, should not be called unless actually recording - recording = 0 - update_icon() - mytape.record_speech("Recording stopped.") - if(ismob(loc)) - var/mob/M = loc - to_chat(M, "Recording stopped.") - - -/obj/item/device/taperecorder/verb/stop() - set name = "Stop" - set category = "Object" - - if(usr.incapacitated()) - return - if(recording) - stop_recording() - return - else if(playing) - playing = 0 - update_icon() - to_chat(usr, "Playback stopped.") - return - else - to_chat(usr, "Stop what?") - - -/obj/item/device/taperecorder/verb/wipe_tape() - set name = "Wipe Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(emagged) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(recording || playing) - to_chat(usr, "You can't wipe the tape while playing or recording!") - return - else - if(mytape.storedinfo) mytape.storedinfo.Cut() - if(mytape.timestamp) mytape.timestamp.Cut() - mytape.used_capacity = 0 - to_chat(usr, "You wipe the tape.") - return - - -/obj/item/device/taperecorder/verb/playback_memory() - set name = "Playback Tape" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(recording) - to_chat(usr, "You can't playback when recording!") - return - if(playing) - to_chat(usr, "You're already playing!") - return - playing = 1 - update_icon() - to_chat(usr, "Playing started.") - for(var/i=1 , i < mytape.max_capacity , i++) - if(!mytape || !playing) - break - if(mytape.storedinfo.len < i) - break - - var/turf/T = get_turf(src) - var/playedmessage = mytape.storedinfo[i] - if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds - playedmessage = copytext(playedmessage,2) - T.audible_message("Tape Recorder: [playedmessage]", runemessage = playedmessage) - - if(mytape.storedinfo.len < i+1) - playsleepseconds = 1 - sleep(10) - T = get_turf(src) - T.audible_message("Tape Recorder: End of recording.", runemessage = "click") - break - else - playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i] - - if(playsleepseconds > 14) - sleep(10) - T = get_turf(src) - T.audible_message("Tape Recorder: Skipping [playsleepseconds] seconds of silence", runemessage = "tape winding") - playsleepseconds = 1 - sleep(10 * playsleepseconds) - - - playing = 0 - update_icon() - - if(emagged) - var/turf/T = get_turf(src) - T.audible_message("Tape Recorder: This tape recorder will self-destruct in... Five.", runemessage = "beep beep") - sleep(10) - T = get_turf(src) - T.audible_message("Tape Recorder: Four.") - sleep(10) - T = get_turf(src) - T.audible_message("Tape Recorder: Three.") - sleep(10) - T = get_turf(src) - T.audible_message("Tape Recorder: Two.") - sleep(10) - T = get_turf(src) - T.audible_message("Tape Recorder: One.") - sleep(10) - explode() - - -/obj/item/device/taperecorder/verb/print_transcript() - set name = "Print Transcript" - set category = "Object" - - if(usr.incapacitated()) - return - if(!mytape) - to_chat(usr, "There's no tape!") - return - if(mytape.ruined) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(emagged) - to_chat(usr, "The tape recorder makes a scratchy noise.") - return - if(!canprint) - to_chat(usr, "The recorder can't print that fast!") - return - if(recording || playing) - to_chat(usr, "You can't print the transcript while playing or recording!") - return - - to_chat(usr, "Transcript printed.") - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(src)) - var/t1 = "Transcript:

                    " - for(var/i=1,mytape.storedinfo.len >= i,i++) - var/printedmessage = mytape.storedinfo[i] - if (findtextEx(printedmessage,"*",1,2)) //replace action sounds - printedmessage = "\[[time2text(mytape.timestamp[i]*10,"mm:ss")]\] (Unrecognized sound)" - t1 += "[printedmessage]
                    " - P.info = t1 - P.name = "Transcript" - canprint = 0 - sleep(300) - canprint = 1 - - -/obj/item/device/taperecorder/attack_self(mob/user) - if(recording || playing) - stop() - else - record() - - -/obj/item/device/taperecorder/update_icon() - if(!mytape) - icon_state = "taperecorder_empty" - else if(recording) - icon_state = "taperecorder_recording" - else if(playing) - icon_state = "taperecorder_playing" - else - icon_state = "taperecorder_idle" - - - -/obj/item/device/tape - name = "tape" - desc = "A magnetic tape that can hold up to ten minutes of content." - icon_state = "tape_white" - item_state = "analyzer" - w_class = ITEMSIZE_TINY - matter = list(MAT_STEEL=20, MAT_GLASS=5) - force = 1 - throwforce = 0 - var/max_capacity = 1800 - var/used_capacity = 0 - var/list/storedinfo = new/list() - var/list/timestamp = new/list() - var/ruined = 0 - - -/obj/item/device/tape/update_icon() - cut_overlays() - if(ruined) - add_overlay("ribbonoverlay") - - -/obj/item/device/tape/fire_act() - ruin() - -/obj/item/device/tape/attack_self(mob/user) - if(!ruined) - to_chat(user, "You pull out all the tape!") - ruin() - - -/obj/item/device/tape/proc/ruin() - ruined = 1 - update_icon() - - -/obj/item/device/tape/proc/fix() - ruined = 0 - update_icon() - - -/obj/item/device/tape/proc/record_speech(text) - timestamp += used_capacity - storedinfo += "\[[time2text(used_capacity*10,"mm:ss")]\] [text]" - - -//shows up on the printed transcript as (Unrecognized sound) -/obj/item/device/tape/proc/record_noise(text) - timestamp += used_capacity - storedinfo += "*\[[time2text(used_capacity*10,"mm:ss")]\] [text]" - - -/obj/item/device/tape/attackby(obj/item/I, mob/user, params) - if(ruined && I.is_screwdriver()) - to_chat(user, "You start winding the tape back in...") - playsound(src, I.usesound, 50, 1) - if(do_after(user, 120 * I.toolspeed, target = src)) - to_chat(user, "You wound the tape back in.") - fix() - return - else if(istype(I, /obj/item/weapon/pen)) - if(loc == user && !user.incapacitated()) - var/new_name = tgui_input_text(user, "What would you like to label the tape?", "Tape labeling") - if(isnull(new_name)) return - new_name = sanitizeSafe(new_name) - if(new_name) - name = "tape - '[new_name]'" - to_chat(user, "You label the tape '[new_name]'.") - else - name = "tape" - to_chat(user, "You scratch off the label.") - return - ..() - - -//Random colour tapes -/obj/item/device/tape/random/New() - icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" +/obj/item/device/taperecorder + name = "universal recorder" + desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." + icon_state = "taperecorder_empty" + item_state = "analyzer" + w_class = ITEMSIZE_SMALL + + matter = list(MAT_STEEL = 60,MAT_GLASS = 30) + + var/emagged = 0.0 + var/recording = 0.0 + var/playing = 0.0 + var/playsleepseconds = 0.0 + var/obj/item/device/tape/mytape = /obj/item/device/tape/random + var/canprint = 1 + slot_flags = SLOT_BELT + throwforce = 2 + throw_speed = 4 + throw_range = 20 + +/obj/item/device/taperecorder/New() + ..() + if(ispath(mytape)) + mytape = new mytape(src) + update_icon() + listening_objects += src + +/obj/item/device/taperecorder/empty + mytape = null + +/obj/item/device/taperecorder/Destroy() + listening_objects -= src + if(mytape) + qdel(mytape) + mytape = null + return ..() + + +/obj/item/device/taperecorder/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/device/tape)) + if(mytape) + to_chat(user, "There's already a tape inside.") + return + if(!user.unEquip(I)) + return + I.forceMove(src) + mytape = I + to_chat(user, "You insert [I] into [src].") + update_icon() + return + ..() + + +/obj/item/device/taperecorder/fire_act() + if(mytape) + mytape.ruin() //Fires destroy the tape + return ..() + + +/obj/item/device/taperecorder/attack_hand(mob/user) + if(user.get_inactive_hand() == src) + if(mytape) + eject() + return + ..() + + +/obj/item/device/taperecorder/verb/eject() + set name = "Eject Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape in \the [src].") + return + if(emagged) + to_chat(usr, "The tape seems to be stuck inside.") + return + + if(playing || recording) + stop() + to_chat(usr, "You remove [mytape] from [src].") + usr.put_in_hands(mytape) + mytape = null + update_icon() + + +/obj/item/device/taperecorder/hear_talk(mob/M, list/message_pieces, verb) + var/msg = multilingual_to_message(message_pieces, requires_machine_understands = TRUE, with_capitalization = TRUE) + var/voice = M.GetVoice() //Defined on living, returns name for normal mobs/ + if(mytape && recording) + mytape.record_speech("[voice] [verb], \"[msg]\"") + + +/obj/item/device/taperecorder/see_emote(mob/M as mob, text, var/emote_type) + if(emote_type != 2) //only hearable emotes + return + if(mytape && recording) + mytape.record_speech("[strip_html_properly(text)]") + + +/obj/item/device/taperecorder/show_message(msg, type, alt, alt_type) + var/recordedtext + if (msg && type == 2) //must be hearable + recordedtext = msg + else if (alt && alt_type == 2) + recordedtext = alt + else + return + if(mytape && recording) + mytape.record_noise("[strip_html_properly(recordedtext)]") + +/obj/item/device/taperecorder/emag_act(var/remaining_charges, var/mob/user) + if(emagged == 0) + emagged = 1 + recording = 0 + to_chat(user, "PZZTTPFFFT") + update_icon() + return 1 + else + to_chat(user, "It is already emagged!") + +/obj/item/device/taperecorder/proc/explode() + var/turf/T = get_turf(loc) + if(ismob(loc)) + var/mob/M = loc + to_chat(M, "\The [src] explodes!") + if(T) + T.hotspot_expose(700,125) + explosion(T, -1, -1, 0, 4) + qdel(src) + return + +/obj/item/device/taperecorder/verb/record() + set name = "Start Recording" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape!") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(recording) + to_chat(usr, "You're already recording!") + return + if(playing) + to_chat(usr, "You can't record when playing!") + return + if(emagged) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(mytape.used_capacity < mytape.max_capacity) + to_chat(usr, "Recording started.") + recording = 1 + update_icon() + + mytape.record_speech("Recording started.") + + //count seconds until full, or recording is stopped + while(mytape && recording && mytape.used_capacity < mytape.max_capacity) + sleep(10) + mytape.used_capacity++ + if(mytape.used_capacity >= mytape.max_capacity) + if(ismob(loc)) + var/mob/M = loc + to_chat(M, "The tape is full.") + stop_recording() + + + update_icon() + return + else + to_chat(usr, "The tape is full.") + + +/obj/item/device/taperecorder/proc/stop_recording() + //Sanity checks skipped, should not be called unless actually recording + recording = 0 + update_icon() + mytape.record_speech("Recording stopped.") + if(ismob(loc)) + var/mob/M = loc + to_chat(M, "Recording stopped.") + + +/obj/item/device/taperecorder/verb/stop() + set name = "Stop" + set category = "Object" + + if(usr.incapacitated()) + return + if(recording) + stop_recording() + return + else if(playing) + playing = 0 + update_icon() + to_chat(usr, "Playback stopped.") + return + else + to_chat(usr, "Stop what?") + + +/obj/item/device/taperecorder/verb/wipe_tape() + set name = "Wipe Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(emagged) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(recording || playing) + to_chat(usr, "You can't wipe the tape while playing or recording!") + return + else + if(mytape.storedinfo) mytape.storedinfo.Cut() + if(mytape.timestamp) mytape.timestamp.Cut() + mytape.used_capacity = 0 + to_chat(usr, "You wipe the tape.") + return + + +/obj/item/device/taperecorder/verb/playback_memory() + set name = "Playback Tape" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape!") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(recording) + to_chat(usr, "You can't playback when recording!") + return + if(playing) + to_chat(usr, "You're already playing!") + return + playing = 1 + update_icon() + to_chat(usr, "Playing started.") + for(var/i=1 , i < mytape.max_capacity , i++) + if(!mytape || !playing) + break + if(mytape.storedinfo.len < i) + break + + var/turf/T = get_turf(src) + var/playedmessage = mytape.storedinfo[i] + if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds + playedmessage = copytext(playedmessage,2) + T.audible_message(span_maroon("Tape Recorder: [playedmessage]"), runemessage = playedmessage) + + if(mytape.storedinfo.len < i+1) + playsleepseconds = 1 + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: End of recording."), runemessage = "click") + break + else + playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i] + + if(playsleepseconds > 14) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Skipping [playsleepseconds] seconds of silence"), runemessage = "tape winding") + playsleepseconds = 1 + sleep(10 * playsleepseconds) + + + playing = 0 + update_icon() + + if(emagged) + var/turf/T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: This tape recorder will self-destruct in... Five."), runemessage = "beep beep") + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Four.")) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Three.")) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: Two.")) + sleep(10) + T = get_turf(src) + T.audible_message(span_maroon("Tape Recorder: One.")) + sleep(10) + explode() + + +/obj/item/device/taperecorder/verb/print_transcript() + set name = "Print Transcript" + set category = "Object" + + if(usr.incapacitated()) + return + if(!mytape) + to_chat(usr, "There's no tape!") + return + if(mytape.ruined) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(emagged) + to_chat(usr, "The tape recorder makes a scratchy noise.") + return + if(!canprint) + to_chat(usr, "The recorder can't print that fast!") + return + if(recording || playing) + to_chat(usr, "You can't print the transcript while playing or recording!") + return + + to_chat(usr, "Transcript printed.") + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(src)) + var/t1 = "Transcript:

                    " + for(var/i=1,mytape.storedinfo.len >= i,i++) + var/printedmessage = mytape.storedinfo[i] + if (findtextEx(printedmessage,"*",1,2)) //replace action sounds + printedmessage = "\[[time2text(mytape.timestamp[i]*10,"mm:ss")]\] (Unrecognized sound)" + t1 += "[printedmessage]
                    " + P.info = t1 + P.name = "Transcript" + canprint = 0 + sleep(300) + canprint = 1 + + +/obj/item/device/taperecorder/attack_self(mob/user) + if(recording || playing) + stop() + else + record() + + +/obj/item/device/taperecorder/update_icon() + if(!mytape) + icon_state = "taperecorder_empty" + else if(recording) + icon_state = "taperecorder_recording" + else if(playing) + icon_state = "taperecorder_playing" + else + icon_state = "taperecorder_idle" + + + +/obj/item/device/tape + name = "tape" + desc = "A magnetic tape that can hold up to ten minutes of content." + icon_state = "tape_white" + item_state = "analyzer" + w_class = ITEMSIZE_TINY + matter = list(MAT_STEEL=20, MAT_GLASS=5) + force = 1 + throwforce = 0 + var/max_capacity = 1800 + var/used_capacity = 0 + var/list/storedinfo = new/list() + var/list/timestamp = new/list() + var/ruined = 0 + + +/obj/item/device/tape/update_icon() + cut_overlays() + if(ruined) + add_overlay("ribbonoverlay") + + +/obj/item/device/tape/fire_act() + ruin() + +/obj/item/device/tape/attack_self(mob/user) + if(!ruined) + to_chat(user, "You pull out all the tape!") + ruin() + + +/obj/item/device/tape/proc/ruin() + ruined = 1 + update_icon() + + +/obj/item/device/tape/proc/fix() + ruined = 0 + update_icon() + + +/obj/item/device/tape/proc/record_speech(text) + timestamp += used_capacity + storedinfo += "\[[time2text(used_capacity*10,"mm:ss")]\] [text]" + + +//shows up on the printed transcript as (Unrecognized sound) +/obj/item/device/tape/proc/record_noise(text) + timestamp += used_capacity + storedinfo += "*\[[time2text(used_capacity*10,"mm:ss")]\] [text]" + + +/obj/item/device/tape/attackby(obj/item/I, mob/user, params) + if(ruined && I.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You start winding the tape back in...") + playsound(src, I.usesound, 50, 1) + if(do_after(user, 120 * I.toolspeed, target = src)) + to_chat(user, "You wound the tape back in.") + fix() + return + else if(istype(I, /obj/item/weapon/pen)) + if(loc == user && !user.incapacitated()) + var/new_name = tgui_input_text(user, "What would you like to label the tape?", "Tape labeling") + if(isnull(new_name)) return + new_name = sanitizeSafe(new_name) + if(new_name) + name = "tape - '[new_name]'" + to_chat(user, "You label the tape '[new_name]'.") + else + name = "tape" + to_chat(user, "You scratch off the label.") + return + ..() + + +//Random colour tapes +/obj/item/device/tape/random/New() + icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 45df3c628f0..66d05b2d93c 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -1,218 +1,218 @@ -/obj/item/device/transfer_valve - name = "tank transfer valve" - desc = "Regulates the transfer of air between two tanks" - icon = 'icons/obj/assemblies.dmi' - icon_state = "valve_1" - var/obj/item/weapon/tank/tank_one - var/obj/item/weapon/tank/tank_two - var/obj/item/device/assembly/attached_device - var/mob/attacher = null - var/valve_open = 0 - var/toggle = 1 - -/obj/item/device/transfer_valve/attackby(obj/item/item, mob/user) - var/turf/location = get_turf(src) // For admin logs - if(istype(item, /obj/item/weapon/tank)) - if(tank_one && tank_two) - to_chat(user, "There are already two tanks attached, remove one first.") - return - - if(!tank_one) - tank_one = item - user.drop_item() - item.forceMove(src) - to_chat(user, "You attach the tank to the transfer valve.") - else if(!tank_two) - tank_two = item - user.drop_item() - item.forceMove(src) - to_chat(user, "You attach the tank to the transfer valve.") - message_admins("[key_name_admin(user)] attached both tanks to a transfer valve. [ADMIN_JMP(location)]") - log_game("[key_name_admin(user)] attached both tanks to a transfer valve.") - - update_icon() - SStgui.update_uis(src) // update all UIs attached to src -//TODO: Have this take an assemblyholder - else if(isassembly(item)) - var/obj/item/device/assembly/A = item - if(A.secured) - to_chat(user, "The device is secured.") - return - if(attached_device) - to_chat(user, "There is already an device attached to the valve, remove it first.") - return - user.remove_from_mob(item) - attached_device = A - A.forceMove(src) - to_chat(user, "You attach the [item] to the valve controls and secure it.") - A.holder = src - A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). - - bombers += "[key_name(user)] attached a [item] to a transfer valve." - message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. [ADMIN_JMP(location)]") - log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") - attacher = user - SStgui.update_uis(src) // update all UIs attached to src - return - - -/obj/item/device/transfer_valve/HasProximity(turf/T, atom/movable/AM, old_loc) - attached_device?.HasProximity(T, AM, old_loc) - -/obj/item/device/transfer_valve/Moved(old_loc, direction, forced) - . = ..() - if(isturf(old_loc)) - unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(callback = /atom/proc/HasProximity) - -/obj/item/device/transfer_valve/attack_self(mob/user) - tgui_interact(user) - -/obj/item/device/transfer_valve/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/transfer_valve/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TransferValve", name) // 460, 320 - ui.open() - -/obj/item/device/transfer_valve/tgui_data(mob/user) - var/list/data = list() - data["tank_one"] = tank_one ? tank_one.name : null - data["tank_two"] = tank_two ? tank_two.name : null - data["attached_device"] = attached_device ? attached_device.name : null - data["valve"] = valve_open - return data - -/obj/item/device/transfer_valve/tgui_act(action, params) - if(..()) - return - . = TRUE - switch(action) - if("tankone") - remove_tank(tank_one) - if("tanktwo") - remove_tank(tank_two) - if("toggle") - toggle_valve() - if("device") - if(attached_device) - attached_device.attack_self(usr) - if("remove_device") - if(attached_device) - attached_device.forceMove(get_turf(src)) - attached_device.holder = null - attached_device = null - update_icon() - else - . = FALSE - if(.) - update_icon() - add_fingerprint(usr) - -/obj/item/device/transfer_valve/proc/process_activation(var/obj/item/device/D) - if(toggle) - toggle = FALSE - toggle_valve() - VARSET_IN(src, toggle, TRUE, 5 SECONDS) - -/obj/item/device/transfer_valve/update_icon() - cut_overlays() - underlays = null - - if(!tank_one && !tank_two && !attached_device) - icon_state = "valve_1" - return - icon_state = "valve" - - if(tank_one) - add_overlay("[tank_one.icon_state]") - if(tank_two) - var/icon/J = new(icon, icon_state = "[tank_two.icon_state]") - J.Shift(WEST, 13) - underlays += J - if(attached_device) - add_overlay("device") - -/obj/item/device/transfer_valve/proc/remove_tank(obj/item/weapon/tank/T) - if(tank_one == T) - split_gases() - tank_one = null - else if(tank_two == T) - split_gases() - tank_two = null - else - return - - T.forceMove(get_turf(src)) - update_icon() - -/obj/item/device/transfer_valve/proc/merge_gases() - if(valve_open) - return - tank_two.air_contents.volume += tank_one.air_contents.volume - var/datum/gas_mixture/temp - temp = tank_one.air_contents.remove_ratio(1) - tank_two.air_contents.merge(temp) - valve_open = 1 - -/obj/item/device/transfer_valve/proc/split_gases() - if(!valve_open) - return - - valve_open = 0 - - if(QDELETED(tank_one) || QDELETED(tank_two)) - return - - var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume - var/datum/gas_mixture/temp - temp = tank_two.air_contents.remove_ratio(ratio1) - tank_one.air_contents.merge(temp) - tank_two.air_contents.volume -= tank_one.air_contents.volume - - - /* - Exadv1: I know this isn't how it's going to work, but this was just to check - it explodes properly when it gets a signal (and it does). - */ - -/obj/item/device/transfer_valve/proc/toggle_valve() - if(!valve_open && (tank_one && tank_two)) - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - - var/attacher_name = "" - if(!attacher) - attacher_name = "Unknown" - else - attacher_name = "[attacher.name]([attacher.ckey])" - - var/log_str = "Bomb valve opened in [A.name] " - log_str += "with [attached_device ? attached_device : "no device"] attacher: [attacher_name]" - - if(attacher) - log_str += ADMIN_QUE(attacher) - - var/mob/mob = get_mob_by_key(src.fingerprintslast) - var/last_touch_info = "" - if(mob) - last_touch_info = ADMIN_QUE(mob) - - log_str += " Last touched by: [src.fingerprintslast][last_touch_info]" - bombers += log_str - message_admins(log_str, 0, 1) - log_game(log_str) - merge_gases() - - else if(valve_open==1 && (tank_one && tank_two)) - split_gases() - - src.update_icon() - -// this doesn't do anything but the timer etc. expects it to be here -// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs -/obj/item/device/transfer_valve/proc/c_state() - return +/obj/item/device/transfer_valve + name = "tank transfer valve" + desc = "Regulates the transfer of air between two tanks" + icon = 'icons/obj/assemblies.dmi' + icon_state = "valve_1" + var/obj/item/weapon/tank/tank_one + var/obj/item/weapon/tank/tank_two + var/obj/item/device/assembly/attached_device + var/mob/attacher = null + var/valve_open = 0 + var/toggle = 1 + +/obj/item/device/transfer_valve/attackby(obj/item/item, mob/user) + var/turf/location = get_turf(src) // For admin logs + if(istype(item, /obj/item/weapon/tank)) + if(tank_one && tank_two) + to_chat(user, "There are already two tanks attached, remove one first.") + return + + if(!tank_one) + tank_one = item + user.drop_item() + item.forceMove(src) + to_chat(user, "You attach the tank to the transfer valve.") + else if(!tank_two) + tank_two = item + user.drop_item() + item.forceMove(src) + to_chat(user, "You attach the tank to the transfer valve.") + message_admins("[key_name_admin(user)] attached both tanks to a transfer valve. [ADMIN_JMP(location)]") + log_game("[key_name_admin(user)] attached both tanks to a transfer valve.") + + update_icon() + SStgui.update_uis(src) // update all UIs attached to src +//TODO: Have this take an assemblyholder + else if(isassembly(item)) + var/obj/item/device/assembly/A = item + if(A.secured) + to_chat(user, "The device is secured.") + return + if(attached_device) + to_chat(user, "There is already an device attached to the valve, remove it first.") + return + user.remove_from_mob(item) + attached_device = A + A.forceMove(src) + to_chat(user, "You attach the [item] to the valve controls and secure it.") + A.holder = src + A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). + + bombers += "[key_name(user)] attached a [item] to a transfer valve." + message_admins("[key_name_admin(user)] attached a [item] to a transfer valve. [ADMIN_JMP(location)]") + log_game("[key_name_admin(user)] attached a [item] to a transfer valve.") + attacher = user + SStgui.update_uis(src) // update all UIs attached to src + return + + +/obj/item/device/transfer_valve/HasProximity(turf/T, atom/movable/AM, old_loc) + attached_device?.HasProximity(T, AM, old_loc) + +/obj/item/device/transfer_valve/Moved(old_loc, direction, forced) + . = ..() + if(isturf(old_loc)) + unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(callback = /atom/proc/HasProximity) + +/obj/item/device/transfer_valve/attack_self(mob/user) + tgui_interact(user) + +/obj/item/device/transfer_valve/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/transfer_valve/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TransferValve", name) // 460, 320 + ui.open() + +/obj/item/device/transfer_valve/tgui_data(mob/user) + var/list/data = list() + data["tank_one"] = tank_one ? tank_one.name : null + data["tank_two"] = tank_two ? tank_two.name : null + data["attached_device"] = attached_device ? attached_device.name : null + data["valve"] = valve_open + return data + +/obj/item/device/transfer_valve/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("tankone") + remove_tank(tank_one) + if("tanktwo") + remove_tank(tank_two) + if("toggle") + toggle_valve() + if("device") + if(attached_device) + attached_device.attack_self(usr) + if("remove_device") + if(attached_device) + attached_device.forceMove(get_turf(src)) + attached_device.holder = null + attached_device = null + update_icon() + else + . = FALSE + if(.) + update_icon() + add_fingerprint(usr) + +/obj/item/device/transfer_valve/proc/process_activation(var/obj/item/device/D) + if(toggle) + toggle = FALSE + toggle_valve() + VARSET_IN(src, toggle, TRUE, 5 SECONDS) + +/obj/item/device/transfer_valve/update_icon() + cut_overlays() + underlays = null + + if(!tank_one && !tank_two && !attached_device) + icon_state = "valve_1" + return + icon_state = "valve" + + if(tank_one) + add_overlay("[tank_one.icon_state]") + if(tank_two) + var/icon/J = new(icon, icon_state = "[tank_two.icon_state]") + J.Shift(WEST, 13) + underlays += J + if(attached_device) + add_overlay("device") + +/obj/item/device/transfer_valve/proc/remove_tank(obj/item/weapon/tank/T) + if(tank_one == T) + split_gases() + tank_one = null + else if(tank_two == T) + split_gases() + tank_two = null + else + return + + T.forceMove(get_turf(src)) + update_icon() + +/obj/item/device/transfer_valve/proc/merge_gases() + if(valve_open) + return + tank_two.air_contents.volume += tank_one.air_contents.volume + var/datum/gas_mixture/temp + temp = tank_one.air_contents.remove_ratio(1) + tank_two.air_contents.merge(temp) + valve_open = 1 + +/obj/item/device/transfer_valve/proc/split_gases() + if(!valve_open) + return + + valve_open = 0 + + if(QDELETED(tank_one) || QDELETED(tank_two)) + return + + var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume + var/datum/gas_mixture/temp + temp = tank_two.air_contents.remove_ratio(ratio1) + tank_one.air_contents.merge(temp) + tank_two.air_contents.volume -= tank_one.air_contents.volume + + + /* + Exadv1: I know this isn't how it's going to work, but this was just to check + it explodes properly when it gets a signal (and it does). + */ + +/obj/item/device/transfer_valve/proc/toggle_valve() + if(!valve_open && (tank_one && tank_two)) + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + + var/attacher_name = "" + if(!attacher) + attacher_name = "Unknown" + else + attacher_name = "[attacher.name]([attacher.ckey])" + + var/log_str = "Bomb valve opened in [A.name] " + log_str += "with [attached_device ? attached_device : "no device"] attacher: [attacher_name]" + + if(attacher) + log_str += ADMIN_QUE(attacher) + + var/mob/mob = get_mob_by_key(src.fingerprintslast) + var/last_touch_info = "" + if(mob) + last_touch_info = ADMIN_QUE(mob) + + log_str += " Last touched by: [src.fingerprintslast][last_touch_info]" + bombers += log_str + message_admins(log_str, 0, 1) + log_game(log_str) + merge_gases() + + else if(valve_open==1 && (tank_one && tank_two)) + split_gases() + + src.update_icon() + +// this doesn't do anything but the timer etc. expects it to be here +// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs +/obj/item/device/transfer_valve/proc/c_state() + return diff --git a/code/game/objects/items/devices/translocator_vr.dm b/code/game/objects/items/devices/translocator_vr.dm index 9bc4233ccf5..5123602a35b 100644 --- a/code/game/objects/items/devices/translocator_vr.dm +++ b/code/game/objects/items/devices/translocator_vr.dm @@ -301,11 +301,11 @@ This device records all warnings given and teleport events for admin review in c if(isbelly(real_dest)) var/obj/belly/B = real_dest if(!(target.can_be_drop_prey) && B.owner != user) - to_chat(target,"\The [src] narrowly avoids teleporting you right into \a [lowertext(real_dest.name)]!") + to_chat(target,"\The [src] narrowly avoids teleporting you right into \a [lowertext(real_dest.name)]!") real_dest = dT //Nevermind! else televored = TRUE - to_chat(target,"\The [src] teleports you right into \a [lowertext(real_dest.name)]!") + to_chat(target,"\The [src] teleports you right into \a [lowertext(real_dest.name)]!") //Phase-out effect phase_out(target,get_turf(target)) diff --git a/code/game/objects/items/devices/uplink_random_lists.dm b/code/game/objects/items/devices/uplink_random_lists.dm index 89bfd5f0040..7e8a6953aef 100644 --- a/code/game/objects/items/devices/uplink_random_lists.dm +++ b/code/game/objects/items/devices/uplink_random_lists.dm @@ -1,118 +1,118 @@ -var/datum/uplink_random_selection/default_uplink_selection = new/datum/uplink_random_selection/default() -var/datum/uplink_random_selection/all_uplink_selection = new/datum/uplink_random_selection/all() - -/datum/uplink_random_item - var/uplink_item // The uplink item - var/keep_probability // The probability we'll decide to keep this item if selected - var/reselect_probability // Probability that we'll decide to keep this item if previously selected. - // Is done together with the keep_probability check. Being selected more than once does not affect this probability. - -/datum/uplink_random_item/New(var/uplink_item, var/keep_probability = 100, var/reselect_propbability = 33) - ..() - src.uplink_item = uplink_item - src.keep_probability = keep_probability - src.reselect_probability = reselect_probability - -/datum/uplink_random_selection - var/list/datum/uplink_random_item/items - var/list/datum/uplink_random_item/all_items - -/datum/uplink_random_selection/New() - ..() - items = list() - all_items = list() - -/datum/uplink_random_selection/proc/get_random_item(var/telecrystals, obj/item/device/uplink/U, var/list/bought_items, var/items_override = 0) - var/const/attempts = 50 - - for(var/i = 0; i < attempts; i++) - var/datum/uplink_random_item/RI - if(items_override) - RI = pick(all_items) - else - RI = pick(items) - if(!prob(RI.keep_probability)) - continue - var/datum/uplink_item/I = uplink.items_assoc[RI.uplink_item] - if(I.cost(U) > telecrystals) - continue - if(bought_items && (I in bought_items) && !prob(RI.reselect_probability)) - continue - if(U && !I.can_buy(U, telecrystals)) - continue - return I - -/datum/uplink_random_selection/all/New() - ..() - for(var/datum/uplink_item/item in uplink.items) - if(item.blacklisted) - continue - else - all_items += new/datum/uplink_random_item(item.type) - -/datum/uplink_random_selection/default/New() - ..() - - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/silenced_45) - items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/mc9mm) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/revolver) - items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/a357) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/heavysnipermerc, 15, 0) - items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/sniperammo, 15, 0) - items += new/datum/uplink_random_item(/datum/uplink_item/item/grenades/emp, 50) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/crossbow, 33) - items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/energy_sword, 75) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/soap, 5, 100) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/concealed_cane, 50, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/detomatix, 20, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/parapen) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/cigarette_kit) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/id) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/spy) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_kit) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_projector) - items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/voice) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/heavy_vest) - items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/combat) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/toolbox, reselect_propbability = 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/plastique) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_radio) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_binary) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/emag, 100, 50) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/clerical) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/space_suit, 50, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/thermal) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/powersink, 10, 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/ai_module, 25, 0) - items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/teleporter, 10, 0) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_freedom) - items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_compress) - items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_explosive) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/sinpockets, reselect_propbability = 20) - items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/surgery, reselect_propbability = 10) - items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/combat, reselect_propbability = 10) - - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/thermal, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/energy_net, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/ewar_voice, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/maneuvering_jets, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/egun, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/power_sink, reselect_propbability = 15) - items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/laser_canon, reselect_propbability = 5) - -#ifdef DEBUG -/proc/debug_uplink_purchage_log() - for(var/antag_type in all_antag_types) - var/datum/antagonist/A = all_antag_types[antag_type] - A.print_player_summary() - -/proc/debug_uplink_item_assoc_list() - for(var/key in uplink.items_assoc) - to_world("[key] - [uplink.items_assoc[key]]") -#endif +var/datum/uplink_random_selection/default_uplink_selection = new/datum/uplink_random_selection/default() +var/datum/uplink_random_selection/all_uplink_selection = new/datum/uplink_random_selection/all() + +/datum/uplink_random_item + var/uplink_item // The uplink item + var/keep_probability // The probability we'll decide to keep this item if selected + var/reselect_probability // Probability that we'll decide to keep this item if previously selected. + // Is done together with the keep_probability check. Being selected more than once does not affect this probability. + +/datum/uplink_random_item/New(var/uplink_item, var/keep_probability = 100, var/reselect_propbability = 33) + ..() + src.uplink_item = uplink_item + src.keep_probability = keep_probability + src.reselect_probability = reselect_probability + +/datum/uplink_random_selection + var/list/datum/uplink_random_item/items + var/list/datum/uplink_random_item/all_items + +/datum/uplink_random_selection/New() + ..() + items = list() + all_items = list() + +/datum/uplink_random_selection/proc/get_random_item(var/telecrystals, obj/item/device/uplink/U, var/list/bought_items, var/items_override = 0) + var/const/attempts = 50 + + for(var/i = 0; i < attempts; i++) + var/datum/uplink_random_item/RI + if(items_override) + RI = pick(all_items) + else + RI = pick(items) + if(!prob(RI.keep_probability)) + continue + var/datum/uplink_item/I = uplink.items_assoc[RI.uplink_item] + if(I.cost(U) > telecrystals) + continue + if(bought_items && (I in bought_items) && !prob(RI.reselect_probability)) + continue + if(U && !I.can_buy(U, telecrystals)) + continue + return I + +/datum/uplink_random_selection/all/New() + ..() + for(var/datum/uplink_item/item in uplink.items) + if(item.blacklisted) + continue + else + all_items += new/datum/uplink_random_item(item.type) + +/datum/uplink_random_selection/default/New() + ..() + + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/silenced_45) + items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/mc9mm) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/revolver) + items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/a357) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/heavysnipermerc, 15, 0) + items += new/datum/uplink_random_item(/datum/uplink_item/item/ammo/sniperammo, 15, 0) + items += new/datum/uplink_random_item(/datum/uplink_item/item/grenades/emp, 50) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/crossbow, 33) + items += new/datum/uplink_random_item(/datum/uplink_item/item/visible_weapons/energy_sword, 75) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/soap, 5, 100) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/concealed_cane, 50, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/detomatix, 20, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/parapen) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealthy_weapons/cigarette_kit) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/id) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/spy) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_kit) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/chameleon_projector) + items += new/datum/uplink_random_item(/datum/uplink_item/item/stealth_items/voice) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/heavy_vest) + items += new/datum/uplink_random_item(/datum/uplink_item/item/armor/combat) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/toolbox, reselect_propbability = 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/plastique) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_radio) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/encryptionkey_binary) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/emag, 100, 50) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/clerical) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/space_suit, 50, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/thermal) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/powersink, 10, 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/ai_module, 25, 0) + items += new/datum/uplink_random_item(/datum/uplink_item/item/tools/teleporter, 10, 0) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_freedom) + items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_compress) + items += new/datum/uplink_random_item(/datum/uplink_item/item/implants/imp_explosive) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/sinpockets, reselect_propbability = 20) + items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/surgery, reselect_propbability = 10) + items += new/datum/uplink_random_item(/datum/uplink_item/item/medical/combat, reselect_propbability = 10) + + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/thermal, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/energy_net, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/ewar_voice, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/maneuvering_jets, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/egun, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/power_sink, reselect_propbability = 15) + items += new/datum/uplink_random_item(/datum/uplink_item/item/hardsuit_modules/laser_canon, reselect_propbability = 5) + +#ifdef DEBUG +/proc/debug_uplink_purchage_log() + for(var/antag_type in all_antag_types) + var/datum/antagonist/A = all_antag_types[antag_type] + A.print_player_summary() + +/proc/debug_uplink_item_assoc_list() + for(var/key in uplink.items_assoc) + to_world("[key] - [uplink.items_assoc[key]]") +#endif diff --git a/code/game/objects/items/latexballoon.dm b/code/game/objects/items/latexballoon.dm index 559bfbec0d3..753158eadb6 100644 --- a/code/game/objects/items/latexballoon.dm +++ b/code/game/objects/items/latexballoon.dm @@ -1,64 +1,64 @@ -/obj/item/latexballon - name = "latex glove" - desc = "A latex glove, usually used as a balloon." - icon_state = "latexballon" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', - ) - item_state = "lgloves" - force = 0 - throwforce = 0 - w_class = ITEMSIZE_SMALL - throw_speed = 1 - throw_range = 15 - var/state - var/datum/gas_mixture/air_contents = null - -/obj/item/latexballon/proc/blow(obj/item/weapon/tank/tank) - if (icon_state == "latexballon_bursted") - return - src.air_contents = tank.remove_air_volume(3) - icon_state = "latexballon_blow" - item_state = "latexballon" - -/obj/item/latexballon/proc/burst() - if (!air_contents) - return - playsound(src, 'sound/weapons/Gunshot_old.ogg', 100, 1) - icon_state = "latexballon_bursted" - item_state = "lgloves" - loc.assume_air(air_contents) - -/obj/item/latexballon/ex_act(severity) - burst() - switch(severity) - if (1) - qdel(src) - if (2) - if (prob(50)) - qdel(src) - -/obj/item/latexballon/bullet_act() - burst() - -/obj/item/latexballon/fire_act(datum/gas_mixture/air, temperature, volume) - if(temperature > T0C+100) - burst() - return - -/obj/item/latexballon/attackby(obj/item/W as obj, mob/user as mob) - if (can_puncture(W)) - burst() - -/* -/obj/item/latexballon/nitrile - name = "nitrile glove" - desc = "A nitrile glove, usually used as a balloon." - icon_state = "nitrileballon" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', - ) - item_state = "ngloves" +/obj/item/latexballon + name = "latex glove" + desc = "A latex glove, usually used as a balloon." + icon_state = "latexballon" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', + ) + item_state = "lgloves" + force = 0 + throwforce = 0 + w_class = ITEMSIZE_SMALL + throw_speed = 1 + throw_range = 15 + var/state + var/datum/gas_mixture/air_contents = null + +/obj/item/latexballon/proc/blow(obj/item/weapon/tank/tank) + if (icon_state == "latexballon_bursted") + return + src.air_contents = tank.remove_air_volume(3) + icon_state = "latexballon_blow" + item_state = "latexballon" + +/obj/item/latexballon/proc/burst() + if (!air_contents) + return + playsound(src, 'sound/weapons/Gunshot_old.ogg', 100, 1) + icon_state = "latexballon_bursted" + item_state = "lgloves" + loc.assume_air(air_contents) + +/obj/item/latexballon/ex_act(severity) + burst() + switch(severity) + if (1) + qdel(src) + if (2) + if (prob(50)) + qdel(src) + +/obj/item/latexballon/bullet_act() + burst() + +/obj/item/latexballon/fire_act(datum/gas_mixture/air, temperature, volume) + if(temperature > T0C+100) + burst() + return + +/obj/item/latexballon/attackby(obj/item/W as obj, mob/user as mob) + if (can_puncture(W)) + burst() + +/* +/obj/item/latexballon/nitrile + name = "nitrile glove" + desc = "A nitrile glove, usually used as a balloon." + icon_state = "nitrileballon" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_gloves.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_gloves.dmi', + ) + item_state = "ngloves" */ \ No newline at end of file diff --git a/code/game/objects/items/paintkit.dm b/code/game/objects/items/paintkit.dm index 3367574393f..53eb76a4662 100644 --- a/code/game/objects/items/paintkit.dm +++ b/code/game/objects/items/paintkit.dm @@ -1,332 +1,332 @@ -/obj/item/device/kit - icon_state = "modkit" - icon = 'icons/obj/device.dmi' - w_class = ITEMSIZE_SMALL - var/new_name = "custom item" - var/new_desc = "A custom item." - var/new_icon - var/new_icon_file - var/new_icon_override_file - var/uses = 1 // Uses before the kit deletes itself. - var/list/allowed_types = list() - -/obj/item/device/kit/examine() - . = ..() - . += "It has [uses] use\s left." - -/obj/item/device/kit/proc/use(var/amt, var/mob/user) - uses -= amt - playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) - if(uses<1) - user.drop_item() - qdel(src) - -/obj/item/device/kit/proc/can_customize(var/obj/item/I) - return is_type_in_list(I, allowed_types) - -/obj/item/device/kit/proc/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) - new_name = kit_name - new_desc = kit_desc - new_icon = kit_icon - new_icon_file = kit_icon_file - new_icon_override_file = kit_icon_override_file - - for(var/path in splittext(additional_data, ", ")) - allowed_types |= text2path(path) - -/obj/item/device/kit/proc/customize(var/obj/item/I, var/mob/user) - if(can_customize(I)) - I.name = new_name ? new_name : I.name - I.desc = new_desc ? new_desc : I.desc - I.icon = new_icon_file ? new_icon_file : I.icon - I.icon_override = new_icon_override_file ? new_icon_override_file : I.icon_override - if(new_icon) - I.icon_state = new_icon - var/obj/item/clothing/under/U = I - if(istype(U)) - U.worn_state = I.icon_state - U.update_rolldown_status() - use(1, user) - -// Generic use -/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/device/kit)) - var/obj/item/device/kit/K = W - K.customize(src, user) - return - - ..() - -// Root hardsuit kit defines. -// Icons for modified hardsuits need to be in the proper .dmis because suit cyclers may cock them up. -/obj/item/device/kit/suit - name = "voidsuit modification kit" - desc = "A kit for modifying a voidsuit." - uses = 2 - var/new_light_overlay - -/obj/item/device/kit/suit/can_customize(var/obj/item/I) - return istype(I, /obj/item/clothing/head/helmet/space/void) || istype(I, /obj/item/clothing/suit/space/void) || istype(I, /obj/item/clothing/suit/storage/hooded) - -/obj/item/device/kit/suit/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) - ..() - - new_light_overlay = additional_data - - -/obj/item/device/kit/suit/customize(var/obj/item/I, var/mob/user) - if(can_customize(I)) - if(istype(I, /obj/item/clothing/head/helmet/space/void)) - var/obj/item/clothing/head/helmet/space/void/helmet = I - helmet.name = "[new_name] suit helmet" - helmet.desc = new_desc - helmet.icon_state = "[new_icon]_helmet" - helmet.item_state = "[new_icon]_helmet" - if(new_icon_file) - helmet.icon = new_icon_file - if(new_icon_override_file) - helmet.icon_override = new_icon_override_file - if(new_light_overlay) - helmet.light_overlay = new_light_overlay - to_chat(user, "You set about modifying the helmet into [helmet].") - var/mob/living/carbon/human/H = user - if(istype(H)) - helmet.species_restricted = list(H.species.get_bodytype(H)) - else if(istype(I, /obj/item/clothing/suit/storage/hooded)) - var/obj/item/clothing/suit/storage/hooded/suit = I - suit.name = "[new_name] suit" - suit.desc = new_desc - suit.icon_state = "[new_icon]_suit" - suit.toggleicon = "[new_icon]_suit" - var/obj/item/clothing/head/hood/S = suit.hood - S.icon_state = "[new_icon]_helmet" - if(new_icon_file) - suit.icon = new_icon_file - S.icon = new_icon_file - if(new_icon_override_file) - suit.icon_override = new_icon_override_file - S.icon_override = new_icon_override_file - to_chat(user, "You set about modifying the suit into [suit].") -// var/mob/living/carbon/human/H = user -// if(istype(H)) -// suit.species_restricted = list(H.species.get_bodytype(H)) Does not quite make sense for something usually very pliable. - else - var/obj/item/clothing/suit/space/void/suit = I - suit.name = "[new_name] voidsuit" - suit.desc = new_desc - suit.icon_state = "[new_icon]_suit" - suit.item_state = "[new_icon]_suit" - if(new_icon_file) - suit.icon = new_icon_file - if(new_icon_override_file) - suit.icon_override = new_icon_override_file - to_chat(user, "You set about modifying the suit into [suit].") - var/mob/living/carbon/human/H = user - if(istype(H)) - suit.species_restricted = list(H.species.get_bodytype(H)) - use(1,user) - -/obj/item/clothing/head/helmet/space/void/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/clothing/suit/space/void/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/clothing/suit/storage/hooded/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/device/kit/suit/rig - name = "rig modification kit" - desc = "A kit for modifying a rigsuit." - uses = 1 - -/obj/item/device/kit/suit/rig/customize(var/obj/item/I, var/mob/user) - var/obj/item/weapon/rig/RIG = I - RIG.suit_state = new_icon - RIG.item_state = new_icon - RIG.suit_type = "customized [initial(RIG.suit_type)]" - RIG.name = "[new_name]" - RIG.desc = new_desc - RIG.icon = new_icon_file - RIG.icon_state = new_icon - RIG.icon_override = new_icon_override_file - for(var/obj/item/piece in list(RIG.gloves,RIG.helmet,RIG.boots,RIG.chest)) - if(!istype(piece)) - continue - piece.name = "[RIG.suit_type] [initial(piece.name)]" - piece.desc = "It seems to be part of a [RIG.name]." - piece.icon_state = "[RIG.suit_state]" - if(istype(piece, /obj/item/clothing/shoes)) - piece.icon = 'icons/mob/custom_items_rig_boots.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_boots.dmi' - if(istype(piece, /obj/item/clothing/suit)) - piece.icon = 'icons/mob/custom_items_rig_suit.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_suit.dmi' - if(istype(piece, /obj/item/clothing/head)) - piece.icon = 'icons/mob/custom_items_rig_helmet.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_helmet.dmi' - if(istype(piece, /obj/item/clothing/gloves)) - piece.icon = 'icons/mob/custom_items_rig_gloves.dmi' - piece.icon_override = 'icons/mob/custom_items_rig_gloves.dmi' - if(RIG.helmet && istype(RIG.helmet, /obj/item/clothing/head/helmet) && new_light_overlay) - var/obj/item/clothing/head/helmet/H = RIG.helmet - H.light_overlay = new_light_overlay - use(1,user) - -/obj/item/device/kit/suit/rig/can_customize(var/obj/item/I) - return istype(I, /obj/item/weapon/rig) - -/obj/item/weapon/rig/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/device/kit/suit)) - var/obj/item/device/kit/suit/rig/kit = O - kit.customize(src, user) - return - return ..() - -/obj/item/device/kit/suit/rig/debug/Initialize() - set_info("debug suit", "This is a test", "debug", CUSTOM_ITEM_OBJ, CUSTOM_ITEM_MOB) - -/obj/item/device/kit/paint - name = "mecha customisation kit" - desc = "A kit containing all the needed tools and parts to repaint a mech." - var/removable = null - -/obj/item/device/kit/paint/can_customize(var/obj/mecha/M) - if(!istype(M)) - return 0 - - for(var/type in allowed_types) - if(type == M.initial_icon) - return 1 - -/obj/item/device/kit/paint/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) - ..() - - allowed_types = splittext(additional_data, ", ") - - -/obj/item/device/kit/paint/examine() - . = ..() - . += "This kit will convert an exosuit into: [new_name]." - . += "This kit can be used on the following exosuit models:" - for(var/exotype in allowed_types) - . += "- [capitalize(exotype)]" - -/obj/item/device/kit/paint/customize(var/obj/mecha/M, var/mob/user) - if(!can_customize(M)) - to_chat(user, "That kit isn't meant for use on this class of exosuit.") - return - - if(M.occupant) - to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") - return - - user.visible_message("[user] opens [src] and spends some quality time customising [M].") - M.name = new_name - M.desc = new_desc - M.initial_icon = new_icon - if(new_icon_file) - M.icon = new_icon_file - M.update_icon() - use(1, user) - -/obj/mecha/attackby(var/obj/item/weapon/W, var/mob/user) - if(istype(W, /obj/item/device/kit/paint)) - var/obj/item/device/kit/paint/P = W - P.customize(src, user) - return - else - return ..() - -//Ripley APLU kits. -/obj/item/device/kit/paint/ripley - name = "\"Classic\" APLU customisation kit" - new_name = "APLU \"Classic\"" - new_desc = "A very retro APLU unit; didn't they retire these back in 2303?" - new_icon = "ripley-old" - allowed_types = list("ripley") - var/showpilot = TRUE - var/showpilot_lift = 5 - -/obj/item/device/kit/paint/ripley/customize(obj/mecha/M, mob/user) - if(showpilot) - M.show_pilot = TRUE - M.pilot_lift = 5 - else - M.show_pilot = FALSE - M.pilot_lift = 0 - . = ..() - -/obj/item/device/kit/paint/ripley/death - name = "\"Reaper\" APLU customisation kit" - new_name = "APLU \"Reaper\"" - new_desc = "A terrifying, grim power loader. Why do those clamps have spikes?" - new_icon = "deathripley" - allowed_types = list("ripley","firefighter") - showpilot = FALSE - -/obj/item/device/kit/paint/ripley/flames_red - name = "\"Firestarter\" APLU customisation kit" - new_name = "APLU \"Firestarter\"" - new_desc = "A standard APLU exosuit with stylish orange flame decals." - new_icon = "ripley_flames_red" - showpilot = FALSE - -/obj/item/device/kit/paint/ripley/flames_blue - name = "\"Burning Chrome\" APLU customisation kit" - new_name = "APLU \"Burning Chrome\"" - new_desc = "A standard APLU exosuit with stylish blue flame decals." - new_icon = "ripley_flames_blue" - showpilot = FALSE - -// Durand kits. -/obj/item/device/kit/paint/durand - name = "\"Classic\" Durand customisation kit" - new_name = "Durand \"Classic\"" - new_desc = "An older model of Durand combat exosuit. This model was retired for rotating a pilot's torso 180 degrees." - new_icon = "old_durand" - allowed_types = list("durand") - -/obj/item/device/kit/paint/durand/seraph - name = "\"Cherubim\" Durand customisation kit" - new_name = "Durand \"Cherubim\"" - new_desc = "A Durand combat exosuit modelled after ancient Earth entertainment. Your heart goes doki-doki just looking at it." - new_icon = "old_durand" - -/obj/item/device/kit/paint/durand/phazon - name = "\"Sypher\" Durand customisation kit" - new_name = "Durand \"Sypher\"" - new_desc = "A Durand combat exosuit with some very stylish neons and decals. Seems to blur slightly at the edges; probably an optical illusion." - new_icon = "phazon" - -// Gygax kits. -/obj/item/device/kit/paint/gygax - name = "\"Jester\" Gygax customisation kit" - new_name = "Gygax \"Jester\"" - new_desc = "A Gygax exosuit modelled after the infamous combat-troubadors of Earth's distant past. Terrifying to behold." - new_icon = "honker" - allowed_types = list("gygax") - -/obj/item/device/kit/paint/gygax/darkgygax - name = "\"Silhouette\" Gygax customisation kit" - new_name = "Gygax \"Silhouette\"" - new_desc = "An ominous Gygax exosuit modelled after the fictional corporate 'death squads' that were popular in pulp action-thrillers back in 2314." - new_icon = "darkgygax" - -/obj/item/device/kit/paint/gygax/recitence - name = "\"Gaoler\" Gygax customisation kit" - new_name = "Durand \"Gaoler\"" - new_desc = "A bulky silver Gygax exosuit. The extra armour appears to be painted on, but it's very shiny." - new_icon = "recitence" +/obj/item/device/kit + icon_state = "modkit" + icon = 'icons/obj/device.dmi' + w_class = ITEMSIZE_SMALL + var/new_name = "custom item" + var/new_desc = "A custom item." + var/new_icon + var/new_icon_file + var/new_icon_override_file + var/uses = 1 // Uses before the kit deletes itself. + var/list/allowed_types = list() + +/obj/item/device/kit/examine() + . = ..() + . += "It has [uses] use\s left." + +/obj/item/device/kit/proc/use(var/amt, var/mob/user) + uses -= amt + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + if(uses<1) + user.drop_item() + qdel(src) + +/obj/item/device/kit/proc/can_customize(var/obj/item/I) + return is_type_in_list(I, allowed_types) + +/obj/item/device/kit/proc/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) + new_name = kit_name + new_desc = kit_desc + new_icon = kit_icon + new_icon_file = kit_icon_file + new_icon_override_file = kit_icon_override_file + + for(var/path in splittext(additional_data, ", ")) + allowed_types |= text2path(path) + +/obj/item/device/kit/proc/customize(var/obj/item/I, var/mob/user) + if(can_customize(I)) + I.name = new_name ? new_name : I.name + I.desc = new_desc ? new_desc : I.desc + I.icon = new_icon_file ? new_icon_file : I.icon + I.icon_override = new_icon_override_file ? new_icon_override_file : I.icon_override + if(new_icon) + I.icon_state = new_icon + var/obj/item/clothing/under/U = I + if(istype(U)) + U.worn_state = I.icon_state + U.update_rolldown_status() + use(1, user) + +// Generic use +/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/device/kit)) + var/obj/item/device/kit/K = W + K.customize(src, user) + return + + ..() + +// Root hardsuit kit defines. +// Icons for modified hardsuits need to be in the proper .dmis because suit cyclers may cock them up. +/obj/item/device/kit/suit + name = "voidsuit modification kit" + desc = "A kit for modifying a voidsuit." + uses = 2 + var/new_light_overlay + +/obj/item/device/kit/suit/can_customize(var/obj/item/I) + return istype(I, /obj/item/clothing/head/helmet/space/void) || istype(I, /obj/item/clothing/suit/space/void) || istype(I, /obj/item/clothing/suit/storage/hooded) + +/obj/item/device/kit/suit/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) + ..() + + new_light_overlay = additional_data + + +/obj/item/device/kit/suit/customize(var/obj/item/I, var/mob/user) + if(can_customize(I)) + if(istype(I, /obj/item/clothing/head/helmet/space/void)) + var/obj/item/clothing/head/helmet/space/void/helmet = I + helmet.name = "[new_name] suit helmet" + helmet.desc = new_desc + helmet.icon_state = "[new_icon]_helmet" + helmet.item_state = "[new_icon]_helmet" + if(new_icon_file) + helmet.icon = new_icon_file + if(new_icon_override_file) + helmet.icon_override = new_icon_override_file + if(new_light_overlay) + helmet.light_overlay = new_light_overlay + to_chat(user, "You set about modifying the helmet into [helmet].") + var/mob/living/carbon/human/H = user + if(istype(H)) + helmet.species_restricted = list(H.species.get_bodytype(H)) + else if(istype(I, /obj/item/clothing/suit/storage/hooded)) + var/obj/item/clothing/suit/storage/hooded/suit = I + suit.name = "[new_name] suit" + suit.desc = new_desc + suit.icon_state = "[new_icon]_suit" + suit.toggleicon = "[new_icon]_suit" + var/obj/item/clothing/head/hood/S = suit.hood + S.icon_state = "[new_icon]_helmet" + if(new_icon_file) + suit.icon = new_icon_file + S.icon = new_icon_file + if(new_icon_override_file) + suit.icon_override = new_icon_override_file + S.icon_override = new_icon_override_file + to_chat(user, "You set about modifying the suit into [suit].") +// var/mob/living/carbon/human/H = user +// if(istype(H)) +// suit.species_restricted = list(H.species.get_bodytype(H)) Does not quite make sense for something usually very pliable. + else + var/obj/item/clothing/suit/space/void/suit = I + suit.name = "[new_name] voidsuit" + suit.desc = new_desc + suit.icon_state = "[new_icon]_suit" + suit.item_state = "[new_icon]_suit" + if(new_icon_file) + suit.icon = new_icon_file + if(new_icon_override_file) + suit.icon_override = new_icon_override_file + to_chat(user, "You set about modifying the suit into [suit].") + var/mob/living/carbon/human/H = user + if(istype(H)) + suit.species_restricted = list(H.species.get_bodytype(H)) + use(1,user) + +/obj/item/clothing/head/helmet/space/void/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/clothing/suit/space/void/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/clothing/suit/storage/hooded/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/device/kit/suit/rig + name = "rig modification kit" + desc = "A kit for modifying a rigsuit." + uses = 1 + +/obj/item/device/kit/suit/rig/customize(var/obj/item/I, var/mob/user) + var/obj/item/weapon/rig/RIG = I + RIG.suit_state = new_icon + RIG.item_state = new_icon + RIG.suit_type = "customized [initial(RIG.suit_type)]" + RIG.name = "[new_name]" + RIG.desc = new_desc + RIG.icon = new_icon_file + RIG.icon_state = new_icon + RIG.icon_override = new_icon_override_file + for(var/obj/item/piece in list(RIG.gloves,RIG.helmet,RIG.boots,RIG.chest)) + if(!istype(piece)) + continue + piece.name = "[RIG.suit_type] [initial(piece.name)]" + piece.desc = "It seems to be part of a [RIG.name]." + piece.icon_state = "[RIG.suit_state]" + if(istype(piece, /obj/item/clothing/shoes)) + piece.icon = 'icons/mob/custom_items_rig_boots.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_boots.dmi' + if(istype(piece, /obj/item/clothing/suit)) + piece.icon = 'icons/mob/custom_items_rig_suit.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_suit.dmi' + if(istype(piece, /obj/item/clothing/head)) + piece.icon = 'icons/mob/custom_items_rig_helmet.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_helmet.dmi' + if(istype(piece, /obj/item/clothing/gloves)) + piece.icon = 'icons/mob/custom_items_rig_gloves.dmi' + piece.icon_override = 'icons/mob/custom_items_rig_gloves.dmi' + if(RIG.helmet && istype(RIG.helmet, /obj/item/clothing/head/helmet) && new_light_overlay) + var/obj/item/clothing/head/helmet/H = RIG.helmet + H.light_overlay = new_light_overlay + use(1,user) + +/obj/item/device/kit/suit/rig/can_customize(var/obj/item/I) + return istype(I, /obj/item/weapon/rig) + +/obj/item/weapon/rig/attackby(var/obj/item/O, var/mob/user) + if(istype(O,/obj/item/device/kit/suit)) + var/obj/item/device/kit/suit/rig/kit = O + kit.customize(src, user) + return + return ..() + +/obj/item/device/kit/suit/rig/debug/Initialize() + set_info("debug suit", "This is a test", "debug", CUSTOM_ITEM_OBJ, CUSTOM_ITEM_MOB) + +/obj/item/device/kit/paint + name = "mecha customisation kit" + desc = "A kit containing all the needed tools and parts to repaint a mech." + var/removable = null + +/obj/item/device/kit/paint/can_customize(var/obj/mecha/M) + if(!istype(M)) + return 0 + + for(var/type in allowed_types) + if(type == M.initial_icon) + return 1 + +/obj/item/device/kit/paint/set_info(var/kit_name, var/kit_desc, var/kit_icon, var/kit_icon_file = CUSTOM_ITEM_OBJ, var/kit_icon_override_file = CUSTOM_ITEM_MOB, var/additional_data) + ..() + + allowed_types = splittext(additional_data, ", ") + + +/obj/item/device/kit/paint/examine() + . = ..() + . += "This kit will convert an exosuit into: [new_name]." + . += "This kit can be used on the following exosuit models:" + for(var/exotype in allowed_types) + . += "- [capitalize(exotype)]" + +/obj/item/device/kit/paint/customize(var/obj/mecha/M, var/mob/user) + if(!can_customize(M)) + to_chat(user, "That kit isn't meant for use on this class of exosuit.") + return + + if(M.occupant) + to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") + return + + user.visible_message("[user] opens [src] and spends some quality time customising [M].") + M.name = new_name + M.desc = new_desc + M.initial_icon = new_icon + if(new_icon_file) + M.icon = new_icon_file + M.update_icon() + use(1, user) + +/obj/mecha/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W, /obj/item/device/kit/paint)) + var/obj/item/device/kit/paint/P = W + P.customize(src, user) + return + else + return ..() + +//Ripley APLU kits. +/obj/item/device/kit/paint/ripley + name = "\"Classic\" APLU customisation kit" + new_name = "APLU \"Classic\"" + new_desc = "A very retro APLU unit; didn't they retire these back in 2303?" + new_icon = "ripley-old" + allowed_types = list("ripley") + var/showpilot = TRUE + var/showpilot_lift = 5 + +/obj/item/device/kit/paint/ripley/customize(obj/mecha/M, mob/user) + if(showpilot) + M.show_pilot = TRUE + M.pilot_lift = 5 + else + M.show_pilot = FALSE + M.pilot_lift = 0 + . = ..() + +/obj/item/device/kit/paint/ripley/death + name = "\"Reaper\" APLU customisation kit" + new_name = "APLU \"Reaper\"" + new_desc = "A terrifying, grim power loader. Why do those clamps have spikes?" + new_icon = "deathripley" + allowed_types = list("ripley","firefighter") + showpilot = FALSE + +/obj/item/device/kit/paint/ripley/flames_red + name = "\"Firestarter\" APLU customisation kit" + new_name = "APLU \"Firestarter\"" + new_desc = "A standard APLU exosuit with stylish orange flame decals." + new_icon = "ripley_flames_red" + showpilot = FALSE + +/obj/item/device/kit/paint/ripley/flames_blue + name = "\"Burning Chrome\" APLU customisation kit" + new_name = "APLU \"Burning Chrome\"" + new_desc = "A standard APLU exosuit with stylish blue flame decals." + new_icon = "ripley_flames_blue" + showpilot = FALSE + +// Durand kits. +/obj/item/device/kit/paint/durand + name = "\"Classic\" Durand customisation kit" + new_name = "Durand \"Classic\"" + new_desc = "An older model of Durand combat exosuit. This model was retired for rotating a pilot's torso 180 degrees." + new_icon = "old_durand" + allowed_types = list("durand") + +/obj/item/device/kit/paint/durand/seraph + name = "\"Cherubim\" Durand customisation kit" + new_name = "Durand \"Cherubim\"" + new_desc = "A Durand combat exosuit modelled after ancient Earth entertainment. Your heart goes doki-doki just looking at it." + new_icon = "old_durand" + +/obj/item/device/kit/paint/durand/phazon + name = "\"Sypher\" Durand customisation kit" + new_name = "Durand \"Sypher\"" + new_desc = "A Durand combat exosuit with some very stylish neons and decals. Seems to blur slightly at the edges; probably an optical illusion." + new_icon = "phazon" + +// Gygax kits. +/obj/item/device/kit/paint/gygax + name = "\"Jester\" Gygax customisation kit" + new_name = "Gygax \"Jester\"" + new_desc = "A Gygax exosuit modelled after the infamous combat-troubadors of Earth's distant past. Terrifying to behold." + new_icon = "honker" + allowed_types = list("gygax") + +/obj/item/device/kit/paint/gygax/darkgygax + name = "\"Silhouette\" Gygax customisation kit" + new_name = "Gygax \"Silhouette\"" + new_desc = "An ominous Gygax exosuit modelled after the fictional corporate 'death squads' that were popular in pulp action-thrillers back in 2314." + new_icon = "darkgygax" + +/obj/item/device/kit/paint/gygax/recitence + name = "\"Gaoler\" Gygax customisation kit" + new_name = "Durand \"Gaoler\"" + new_desc = "A bulky silver Gygax exosuit. The extra armour appears to be painted on, but it's very shiny." + new_icon = "recitence" diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index ddeac74b584..c7563b4412f 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -1,84 +1,84 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/********************************************************************** - Cyborg Spec Items -***********************************************************************/ -//Might want to move this into several files later but for now it works here - -/obj/item/weapon/melee/baton/robot/arm - name = "electrified arm" - icon = 'icons/obj/decals.dmi' - icon_state = "shock" - - hitcost = 750 - agonyforce = 70 - -/obj/item/weapon/melee/baton/robot/arm/update_icon() - if(status) - set_light(1.5, 1, lightcolor) - else - set_light(0) - -/obj/item/borg/overdrive - name = "overdrive" - icon = 'icons/obj/decals.dmi' - icon_state = "shock" - -/********************************************************************** - HUD/SIGHT things -***********************************************************************/ -/obj/item/borg/sight - icon = 'icons/obj/decals.dmi' - icon_state = "securearea" - var/sight_mode = null - - -/obj/item/borg/sight/xray - name = "\proper x-ray vision" - sight_mode = BORGXRAY - - -/obj/item/borg/sight/thermal - name = "\proper thermal vision" - sight_mode = BORGTHERM - icon_state = "thermal" - icon = 'icons/inventory/eyes/item.dmi' - - -/obj/item/borg/sight/meson - name = "\proper meson vision" - sight_mode = BORGMESON - icon_state = "meson" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/material - name = "\proper material scanner vision" - sight_mode = BORGMATERIAL - icon_state = "material" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/hud - name = "hud" - var/obj/item/clothing/glasses/hud/hud = null - - -/obj/item/borg/sight/hud/med - name = "medical hud" - icon_state = "healthhud" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/hud/med/New() - ..() - hud = new /obj/item/clothing/glasses/hud/health(src) - return - - -/obj/item/borg/sight/hud/sec - name = "security hud" - icon_state = "securityhud" - icon = 'icons/inventory/eyes/item.dmi' - -/obj/item/borg/sight/hud/sec/New() - ..() - hud = new /obj/item/clothing/glasses/hud/security(src) - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/********************************************************************** + Cyborg Spec Items +***********************************************************************/ +//Might want to move this into several files later but for now it works here + +/obj/item/weapon/melee/baton/robot/arm + name = "electrified arm" + icon = 'icons/obj/decals.dmi' + icon_state = "shock" + + hitcost = 750 + agonyforce = 70 + +/obj/item/weapon/melee/baton/robot/arm/update_icon() + if(status) + set_light(1.5, 1, lightcolor) + else + set_light(0) + +/obj/item/borg/overdrive + name = "overdrive" + icon = 'icons/obj/decals.dmi' + icon_state = "shock" + +/********************************************************************** + HUD/SIGHT things +***********************************************************************/ +/obj/item/borg/sight + icon = 'icons/obj/decals.dmi' + icon_state = "securearea" + var/sight_mode = null + + +/obj/item/borg/sight/xray + name = "\proper x-ray vision" + sight_mode = BORGXRAY + + +/obj/item/borg/sight/thermal + name = "\proper thermal vision" + sight_mode = BORGTHERM + icon_state = "thermal" + icon = 'icons/inventory/eyes/item.dmi' + + +/obj/item/borg/sight/meson + name = "\proper meson vision" + sight_mode = BORGMESON + icon_state = "meson" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/material + name = "\proper material scanner vision" + sight_mode = BORGMATERIAL + icon_state = "material" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/hud + name = "hud" + var/obj/item/clothing/glasses/hud/hud = null + + +/obj/item/borg/sight/hud/med + name = "medical hud" + icon_state = "healthhud" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/hud/med/New() + ..() + hud = new /obj/item/clothing/glasses/hud/health(src) + return + + +/obj/item/borg/sight/hud/sec + name = "security hud" + icon_state = "securityhud" + icon = 'icons/inventory/eyes/item.dmi' + +/obj/item/borg/sight/hud/sec/New() + ..() + hud = new /obj/item/clothing/glasses/hud/security(src) + return diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 2a79c6aaac5..34892beab26 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -18,31 +18,39 @@ return 1 return 0 +/* ###################################################################################################### + # Utility section. All reusable upgrades without lasting effects, like renaming, reset, etc. go here.# + ######################################################################################################*/ -/obj/item/borg/upgrade/reset +/obj/item/borg/upgrade/utility + module_flags = BORG_UTILITY + +/obj/item/borg/upgrade/utility/reset name = "robotic module reset board" - desc = "Used to reset a cyborg's module. Destroys any other upgrades applied to the robot." + desc = "Used to reset a cyborg's module. Destroys any higher than basic upgrades applied to the robot." icon_state = "cyborg_upgrade1" item_state = "cyborg_upgrade" require_module = 1 -/obj/item/borg/upgrade/reset/action(var/mob/living/silicon/robot/R) - if(..()) - return 0 +/obj/item/borg/upgrade/utility/reset/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + R.module_reset() return 1 -/obj/item/borg/upgrade/rename +/obj/item/borg/upgrade/utility/rename name = "robot reclassification board" desc = "Used to rename a cyborg." icon_state = "cyborg_upgrade1" item_state = "cyborg_upgrade" var/heldname = "default name" -/obj/item/borg/upgrade/rename/attack_self(mob/user as mob) - heldname = sanitizeSafe(tgui_input_text(user, "Enter new robot name", "Robot Reclassification", heldname, MAX_NAME_LEN), MAX_NAME_LEN) +/obj/item/borg/upgrade/utility/rename/attack_self(mob/user as mob) + var/new_name = sanitizeSafe(tgui_input_text(user, "Enter new robot name", "Robot Reclassification", heldname, MAX_NAME_LEN), MAX_NAME_LEN) + if(new_name) + heldname = new_name -/obj/item/borg/upgrade/rename/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/utility/rename/action(var/mob/living/silicon/robot/R) if(..()) return 0 R.notify_ai(ROBOT_NOTIFICATION_NEW_NAME, R.name, heldname) R.name = heldname @@ -51,14 +59,17 @@ return 1 -/obj/item/borg/upgrade/restart +/obj/item/borg/upgrade/utility/restart name = "robot emergency restart module" desc = "Used to force a restart of a disabled-but-repaired robot, bringing it back online." icon_state = "cyborg_upgrade1" item_state = "cyborg_upgrade" -/obj/item/borg/upgrade/restart/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/utility/restart/action(var/mob/living/silicon/robot/R) + if(R.stat == CONSCIOUS) + return 0 + if(R.health < 0) to_chat(usr, "You have to repair the robot before using this module!") return 0 @@ -75,25 +86,257 @@ R.notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) return 1 +/* ################################################################################### + # Basic section. All upgrades which effect the robot's variables directly go here.# + ###################################################################################*/ + +/obj/item/borg/upgrade/basic + module_flags = BORG_BASIC -/obj/item/borg/upgrade/vtec +/obj/item/borg/upgrade/basic/vtec name = "robotic VTEC Module" desc = "Used to kick in a robot's VTEC systems, increasing their speed." icon_state = "cyborg_upgrade2" item_state = "cyborg_upgrade" require_module = 1 -/obj/item/borg/upgrade/vtec/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/basic/vtec/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_basic_upgrade(type)) + to_chat(R, "Actuator already running on overdrive mode!") + to_chat(usr, "It'd be unwise to plug another vtec module in!") + return 0 + + R.verbs += /mob/living/silicon/robot/proc/toggle_vtec + R.speed = -1 + return 1 + +/obj/item/borg/upgrade/basic/sizeshift + name = "robot size alteration module" + desc = "Using technology similar to one used in sizeguns, allows cyborgs to adjust their own size as neccesary." + icon_state = "cyborg_upgrade2" + item_state = "cyborg_upgrade" + require_module = 1 + +/obj/item/borg/upgrade/basic/sizeshift/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_basic_upgrade(type)) + to_chat(R, "Size alteration module already applied!") + to_chat(usr, "There's no space for another size alteration module!") + return 0 + + R.verbs += /mob/living/proc/set_size + return 1 + +/obj/item/borg/upgrade/basic/syndicate + name = "scrambled equipment module" + desc = "Unlocks new and often deadly module specific items of a robot" + icon_state = "cyborg_upgrade3" + item_state = "cyborg_upgrade" + require_module = 1 + +/obj/item/borg/upgrade/basic/syndicate/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_basic_upgrade(type)) + to_chat(R, "Secret modules already unlocked!") + to_chat(usr, "Plugging another scambled module would be useless!") + return 0 + + R.emag_items = 1 + return 1 + +/obj/item/borg/upgrade/basic/language + name = "language module" + desc = "Used to let cyborgs other than clerical or service speak a variety of languages." + icon_state = "cyborg_upgrade3" + item_state = "cyborg_upgrade" + +/obj/item/borg/upgrade/basic/language/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_basic_upgrade(type)) + to_chat(R, "All possible languages already uploaded!") + to_chat(usr, "The language database is up to date!") + return 0 + + R.add_language(LANGUAGE_SOL_COMMON, 1) + R.add_language(LANGUAGE_TRADEBAND, 1) + R.add_language(LANGUAGE_UNATHI, 1) + R.add_language(LANGUAGE_SIIK, 1) + R.add_language(LANGUAGE_AKHANI, 1) + R.add_language(LANGUAGE_SKRELLIAN, 1) + R.add_language(LANGUAGE_GUTTER, 1) + R.add_language(LANGUAGE_SCHECHI, 1) + R.add_language(LANGUAGE_ROOTLOCAL, 1) + R.add_language(LANGUAGE_TERMINUS, 1) + R.add_language(LANGUAGE_ZADDAT, 1) + R.add_language(LANGUAGE_BIRDSONG, 1) + R.add_language(LANGUAGE_SAGARU, 1) + R.add_language(LANGUAGE_CANILUNZT, 1) + R.add_language(LANGUAGE_ECUREUILIAN, 1) + R.add_language(LANGUAGE_DAEMON, 1) + R.add_language(LANGUAGE_ENOCHIAN, 1) + R.add_language(LANGUAGE_DRUDAKAR, 1) + R.add_language(LANGUAGE_TAVAN, 1) + R.add_language(LANGUAGE_SIGN, 1) + R.add_language(LANGUAGE_VOX, 1) + R.add_language(LANGUAGE_ALAI, 1) + R.add_language(LANGUAGE_PROMETHEAN, 1) + R.add_language(LANGUAGE_GIBBERISH, 1) + R.add_language(LANGUAGE_VESPINAE, 1) + R.add_language(LANGUAGE_SPACER, 1) + R.add_language(LANGUAGE_MOUSE, 1) + R.add_language(LANGUAGE_ANIMAL, 1) + R.add_language(LANGUAGE_TEPPI, 1) + /* Admin Stuff + R.add_language(LANGUAGE_CULT, 1) + R.add_language(LANGUAGE_SWARMBOT, 1) + R.add_language(LANGUAGE_MINBUS, 1) + R.add_language(LANGUAGE_EVENT1, 1) + */ + /* We don't want telepathy, but want to mark it that it's not been oversighted + R.add_language(LANGUAGE_CHANGELING, 1) + R.add_language(LANGUAGE_ROOTGLOBAL, 1) + R.add_language(LANGUAGE_SHADEKIN, 1) + */ + + return 1 + +/* ########################################################################### + # Advanced section. All upgrades which effect the robot's modules go here.# + ###########################################################################*/ + +/obj/item/borg/upgrade/advanced + module_flags = BORG_ADVANCED + +/obj/item/borg/upgrade/advanced/bellysizeupgrade + name = "robohound capacity expansion module" + desc = "Used to double a robohound's belly capacity. This only affects total volume, and won't allow support of more than one patient in case of sleeper bellies. Can only be applied once." + icon_state = "cyborg_upgrade2" + item_state = "cyborg_upgrade" + require_module = 1 + +/obj/item/borg/upgrade/advanced/bellysizeupgrade/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + var/obj/T = R.has_upgrade_module(/obj/item/device/dogborg/sleeper) + if(!T) + to_chat(usr, "This robot has had its processor removed!") + return 0 + + if(R.has_advanced_upgrade(type)) + to_chat(R, "Maximum capacity achieved for this hardpoint!") + to_chat(usr, "There's no room for another capacity upgrade!") + return 0 + + var/obj/item/device/dogborg/sleeper/B = T + var/X = B.max_item_count*2 + B.max_item_count = X //I couldn't do T = maxitem*2 for some reason. + to_chat(R, "Internal capacity doubled.") + to_chat(usr, "Internal capacity doubled.") + B.upgraded_capacity = TRUE + return 1 + +/obj/item/borg/upgrade/advanced/jetpack + name = "robot jetpack" + desc = "A carbon dioxide jetpack suitable for low-gravity operations." + icon_state = "cyborg_upgrade3" + item_state = "cyborg_upgrade" + require_module = 1 + +/obj/item/borg/upgrade/advanced/jetpack/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_advanced_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 + + R.module.modules += new/obj/item/weapon/tank/jetpack/carbondioxide(R.module) + for(var/obj/item/weapon/tank/jetpack/carbondioxide in R.module.modules) + R.internals = src + return 1 + +/obj/item/borg/upgrade/advanced/advhealth + name = "advanced health analyzer module" + desc = "An Advanced Health Analyzer, optimized for borg mounting." + icon_state = "cyborg_upgrade3" + item_state = "cyborg_upgrade" + require_module = 1 + +/obj/item/borg/upgrade/advanced/advhealth/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(R.has_advanced_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 + + R.module.modules += new/obj/item/device/healthanalyzer/advanced(R.module) + return 1 + +//Robot size gun +/obj/item/borg/upgrade/advanced/sizegun + name = "robot mounted size gun" + desc = "A size gun adapted for installation in cyborgs, allows them to turn people pocket-sized among other uses. What could go wrong?" + icon_state = "cyborg_upgrade2" + item_state = "cyborg_upgrade" + require_module = 1 + +/obj/item/borg/upgrade/advanced/sizegun/action(var/mob/living/silicon/robot/R) if(..()) return 0 - if(R.speed == -1) + if(R.has_advanced_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") return 0 - R.speed-- + R.module.modules += new/obj/item/weapon/gun/energy/sizegun/mounted(R.module) return 1 +/* ############################################################################## + # Restricted section. All upgrades which only work on specific modules go here.# + ##############################################################################*/ + +//adds the capability to ingest items to the sleeper modules as optional upgrade +/obj/item/borg/upgrade/restricted/bellycapupgrade + name = "robohound capability expansion module" + desc = "Used to enable a robohound's sleeper to ingest items. This only affects sleepers, and has no effect on compactor bellies. Can only be applied once." + icon_state = "cyborg_upgrade2" + item_state = "cyborg_upgrade" + require_module = 1 + module_flags = BORG_MODULE_SECURITY | BORG_MODULE_MEDICAL | BORG_MODULE_COMBAT + +/obj/item/borg/upgrade/restricted/bellycapupgrade/action(var/mob/living/silicon/robot/R) + if(..()) return 0 + + if(!R.supports_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 + + var/obj/T = R.has_upgrade_module(/obj/item/device/dogborg/sleeper) + if(!T) + to_chat(usr, "This robot has had its processor removed!") + return 0 + + if(R.has_restricted_upgrade(type)) + to_chat(R, "Maximum capability achieved for this hardpoint!") + to_chat(usr, "There's no room for another capability upgrade!") + return 0 -/obj/item/borg/upgrade/tasercooler + var/obj/item/device/dogborg/sleeper/B = T + var/X = B.max_item_count*2 //double the capacity from 1 to 2 to allow sleepers to store some items, at most 4 with both upgrades + B.max_item_count = X //I couldn't do T = maxitem*2 for some reason. + to_chat(R, "Internal capability upgraded.") + to_chat(usr, "Internal capability upgraded.") + B.compactor = TRUE + return 1 + +/obj/item/borg/upgrade/restricted/tasercooler name = "robotic Rapid Taser Cooling Module" desc = "Used to cool a mounted taser, increasing the potential current in it and thus its recharge rate." icon_state = "cyborg_upgrade3" @@ -102,116 +345,125 @@ require_module = 1 -/obj/item/borg/upgrade/tasercooler/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/restricted/tasercooler/action(var/mob/living/silicon/robot/R) if(..()) return 0 - if(!R.module || !(type in R.module.supported_upgrades)) + if(!R.supports_upgrade(type)) to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") to_chat(usr, "There's no mounting point for the module!") return 0 - var/obj/item/weapon/gun/energy/taser/mounted/cyborg/T = locate() in R.module - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules + var/obj/T = R.has_upgrade_module(/obj/item/weapon/gun/energy/taser/mounted/cyborg) if(!T) to_chat(usr, "This robot has had its taser removed!") return 0 - if(T.recharge_time <= 2) + if(R.has_restricted_upgrade(type)) to_chat(R, "Maximum cooling achieved for this hardpoint!") to_chat(usr, "There's no room for another cooling unit!") return 0 - else - T.recharge_time = max(2 , T.recharge_time - 4) - + var/obj/item/weapon/gun/energy/taser/mounted/cyborg/B = T + B.recharge_time = max(2 , B.recharge_time - 4) return 1 -/obj/item/borg/upgrade/jetpack - name = "robot jetpack" - desc = "A carbon dioxide jetpack suitable for low-gravity operations." +//Advanced RPED +/obj/item/borg/upgrade/restricted/advrped + name = "Advanced Rapid Part Exchange Device" + desc = "An ARPED, now in borg size!" icon_state = "cyborg_upgrade3" item_state = "cyborg_upgrade" + module_flags = BORG_MODULE_SCIENCE require_module = 1 -/obj/item/borg/upgrade/jetpack/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/restricted/advrped/action(var/mob/living/silicon/robot/R) if(..()) return 0 - var/obj/item/weapon/tank/jetpack/carbondioxide/T = locate() in R.module - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/weapon/tank/jetpack/carbondioxide(R.module) - for(var/obj/item/weapon/tank/jetpack/carbondioxide in R.module.modules) - R.internals = src - return 1 - if(T) + if(!R.supports_upgrade(type)) to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") to_chat(usr, "There's no mounting point for the module!") return 0 -/obj/item/borg/upgrade/advhealth - name = "advanced health analyzer module" - desc = "An Advanced Health Analyzer, optimized for borg mounting." + if(R.has_restricted_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 + + R.module.modules += new/obj/item/weapon/storage/part_replacer/adv(R.module) + return 1 + +//Diamond Drill +/obj/item/borg/upgrade/restricted/diamonddrill + name = "Mounted Diamond Drill" + desc = "An advanced drill, optimized for borg use." icon_state = "cyborg_upgrade3" item_state = "cyborg_upgrade" + module_flags = BORG_MODULE_MINER require_module = 1 -/obj/item/borg/upgrade/advhealth/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/restricted/diamonddrill/action(var/mob/living/silicon/robot/R) if(..()) return 0 - var/obj/item/device/healthanalyzer/advanced/T = locate() in R.module - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/device/healthanalyzer/advanced(R.module) - return 1 - if(T) + if(!R.supports_upgrade(type)) to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") to_chat(usr, "There's no mounting point for the module!") return 0 -/obj/item/borg/upgrade/syndicate/ - name = "scrambled equipment module" - desc = "Unlocks new and often deadly module specific items of a robot" + if(R.has_restricted_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 + + R.module.modules += new/obj/item/weapon/pickaxe/diamonddrill(R.module) + return 1 + +//PKA +/obj/item/borg/upgrade/restricted/pka + name = "Protokenetic Accelerator Upgrade" + desc = "A borg mounted PKA Rifle for use in mining and wildlife defense." icon_state = "cyborg_upgrade3" item_state = "cyborg_upgrade" + module_flags = BORG_MODULE_MINER require_module = 1 -/obj/item/borg/upgrade/syndicate/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/restricted/pka/action(var/mob/living/silicon/robot/R) if(..()) return 0 - if(R.emag_items == 1) + if(!R.supports_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 + + if(R.has_restricted_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") return 0 - R.emag_items = 1 + R.module.modules += new/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg(R.module) return 1 -/obj/item/borg/upgrade/language - name = "language module" - desc = "Used to let cyborgs other than clerical or service speak a variety of languages." - icon_state = "cyborg_upgrade3" +/* ############################################### + # Unsorted section. All cargo modules go here.# + ###############################################*/ + +/obj/item/borg/upgrade/no_prod + var/hidden_from_scan = 0//use this for unproduceable modules you want to hide from scanning (e.g. event tools / admeme) + +//cyborg foam dart gun +/obj/item/borg/upgrade/no_prod/toygun + name = "Donk-Soft Cyborg Blaster module" //Cyborg Blaster is capitalized because it's the brand name + desc = "A foam dart gun designed for mounting into cyborgs. It's Donk or Don't! DISCLAIMER: Donk-Soft bears no responsibility for incidents relating to cyborgs becoming too accustomed to shooting at crew. Installation of the Donk-Soft Cyborg Blaster must be performed only by a licensed roboticist." + icon_state = "cyborg_upgrade5" item_state = "cyborg_upgrade" + require_module = 1 -/obj/item/borg/upgrade/language/action(var/mob/living/silicon/robot/R) +/obj/item/borg/upgrade/no_prod/toygun/action(var/mob/living/silicon/robot/R) if(..()) return 0 - R.add_language(LANGUAGE_SOL_COMMON, 1) - R.add_language(LANGUAGE_TRADEBAND, 1) - R.add_language(LANGUAGE_UNATHI, 1) - R.add_language(LANGUAGE_SIIK, 1) - R.add_language(LANGUAGE_AKHANI, 1) - R.add_language(LANGUAGE_SKRELLIAN, 1) - R.add_language(LANGUAGE_GUTTER, 1) - R.add_language(LANGUAGE_SCHECHI, 1) - R.add_language(LANGUAGE_ROOTLOCAL, 1) - R.add_language(LANGUAGE_TERMINUS, 1) - R.add_language(LANGUAGE_ZADDAT, 1) + if(R.has_no_prod_upgrade(type)) + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") + to_chat(usr, "There's no mounting point for the module!") + return 0 - return 1 \ No newline at end of file + R.module.modules += new/obj/item/weapon/gun/projectile/cyborgtoy(R.module) + return 1 diff --git a/code/game/objects/items/robot/robot_upgrades_vr.dm b/code/game/objects/items/robot/robot_upgrades_vr.dm deleted file mode 100644 index f034fbba594..00000000000 --- a/code/game/objects/items/robot/robot_upgrades_vr.dm +++ /dev/null @@ -1,205 +0,0 @@ -/obj/item/borg/upgrade/language/action(var/mob/living/silicon/robot/R) - if(..()) - R.add_language(LANGUAGE_BIRDSONG, 1) - R.add_language(LANGUAGE_SAGARU, 1) - R.add_language(LANGUAGE_CANILUNZT, 1) - R.add_language(LANGUAGE_ECUREUILIAN, 1) - R.add_language(LANGUAGE_DAEMON, 1) - R.add_language(LANGUAGE_ENOCHIAN, 1) - R.add_language(LANGUAGE_DRUDAKAR, 1) - R.add_language(LANGUAGE_TAVAN, 1) - return 1 - else - return 0 - -//Robot resizing module - -/obj/item/borg/upgrade/sizeshift - name = "robot size alteration module" - desc = "Using technology similar to one used in sizeguns, allows cyborgs to adjust their own size as neccesary." - icon_state = "cyborg_upgrade2" - item_state = "cyborg_upgrade" - require_module = 1 - -/obj/item/borg/upgrade/sizeshift/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - if(/mob/living/proc/set_size in R.verbs) - return 0 - - R.verbs += /mob/living/proc/set_size - return 1 - -//Robot size gun -/obj/item/borg/upgrade/sizegun - name = "robot mounted size gun" - desc = "A size gun adapted for installation in cyborgs, allows them to turn people pocket-sized among other uses. What could go wrong?" - icon_state = "cyborg_upgrade2" - item_state = "cyborg_upgrade" - require_module = 1 - -/obj/item/borg/upgrade/sizegun/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - var/obj/item/weapon/gun/energy/sizegun/mounted/T = locate() in R.module - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/weapon/gun/energy/sizegun/mounted(R.module) - return 1 - if(T) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - -/obj/item/borg/upgrade/bellysizeupgrade - name = "robohound capacity expansion module" - desc = "Used to double a robohound's belly capacity. This only affects total volume, and won't allow support of more than one patient in case of sleeper bellies. Can only be applied once." - icon_state = "cyborg_upgrade2" - item_state = "cyborg_upgrade" - require_module = 1 - -/obj/item/borg/upgrade/bellysizeupgrade/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - if(!R.module)//can work - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - - var/obj/item/device/dogborg/sleeper/T = locate() in R.module - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - to_chat(usr, "This robot has had its processor removed!") - return 0 - - if(T.upgraded_capacity)// == TRUE - to_chat(R, "Maximum capacity achieved for this hardpoint!") - to_chat(usr, "There's no room for another capacity upgrade!") - return 0 - else - var/X = T.max_item_count*2 - T.max_item_count = X //I couldn't do T = maxitem*2 for some reason. - to_chat(R, "Internal capacity doubled.") - to_chat(usr, "Internal capacity doubled.") - T.upgraded_capacity = TRUE - return 1 - -//Advanced RPED -/obj/item/borg/upgrade/advrped - name = "Advanced Rapid Part Exchange Device" - desc = "An ARPED, now in borg size!" - icon_state = "cyborg_upgrade3" - item_state = "cyborg_upgrade" - module_flags = BORG_MODULE_SCIENCE - require_module = 1 - -/obj/item/borg/upgrade/advrped/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - var/obj/item/weapon/storage/part_replacer/adv/T = locate() in R.module - - if(!R.module || !(type in R.module.supported_upgrades)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/weapon/storage/part_replacer/adv(R.module) - return 1 - if(T) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - -//Diamond Drill -/obj/item/borg/upgrade/diamonddrill - name = "Mounted Diamond Drill" - desc = "An advanced drill, optimized for borg use." - icon_state = "cyborg_upgrade3" - item_state = "cyborg_upgrade" - module_flags = BORG_MODULE_MINER - require_module = 1 - -/obj/item/borg/upgrade/diamonddrill/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - var/obj/item/weapon/pickaxe/diamonddrill/T = locate() in R.module - - if(!R.module || !(type in R.module.supported_upgrades)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/weapon/pickaxe/diamonddrill(R.module) - return 1 - if(T) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - -//PKA -/obj/item/borg/upgrade/pka - name = "Protokenetic Accelerator Upgrade" - desc = "A borg mounted PKA Rifle for use in mining and wildlife defense." - icon_state = "cyborg_upgrade3" - item_state = "cyborg_upgrade" - module_flags = BORG_MODULE_MINER - require_module = 1 - -/obj/item/borg/upgrade/pka/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - var/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg/T = locate() in R.module - - if(!R.module || !(type in R.module.supported_upgrades)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg(R.module) - return 1 - if(T) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 - -//cyborg foam dart gun -/obj/item/borg/upgrade/toygun - name = "Donk-Soft Cyborg Blaster module" //Cyborg Blaster is capitalized because it's the brand name - desc = "A foam dart gun designed for mounting into cyborgs. It's Donk or Don't! DISCLAIMER: Donk-Soft bears no responsibility for incidents relating to cyborgs becoming too accustomed to shooting at crew. Installation of the Donk-Soft Cyborg Blaster must be performed only by a licensed roboticist." - icon_state = "cyborg_upgrade5" - item_state = "cyborg_upgrade" - require_module = 1 - -/obj/item/borg/upgrade/toygun/action(var/mob/living/silicon/robot/R) - if(..()) return 0 - - var/obj/item/weapon/gun/projectile/cyborgtoy/T = locate() in R.module - if(!T) - T = locate() in R.module.contents - if(!T) - T = locate() in R.module.modules - if(!T) - R.module.modules += new/obj/item/weapon/gun/projectile/cyborgtoy(R.module) - return 1 - if(T) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(usr, "There's no mounting point for the module!") - return 0 \ No newline at end of file diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index bf132c7cf96..53ea1523855 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -1,180 +1,180 @@ -// Targets, the things that actually get shot! -/obj/item/target - name = "shooting target" - desc = "A shooting target." - icon = 'icons/obj/objects.dmi' - icon_state = "target_h" - density = FALSE - var/hp = 1800 - var/icon/virtualIcon - var/list/bulletholes = list() - -/obj/item/target/Destroy() - // if a target is deleted and associated with a stake, force stake to forget - for(var/obj/structure/target_stake/T in view(3,src)) - if(T.pinned_target == src) - T.pinned_target = null - T.density = TRUE - break - ..() // delete target - -/obj/item/target/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - // After target moves, check for nearby stakes. If associated, move to target - for(var/obj/structure/target_stake/M in view(3,src)) - if(M.density == 0 && M.pinned_target == src) - M.forceMove(loc) - - // This may seem a little counter-intuitive but I assure you that's for a purpose. - // Stakes are the ones that carry targets, yes, but in the stake code we set - // a stake's density to 0 meaning it can't be pushed anymore. Instead of pushing - // the stake now, we have to push the target. - - - -/obj/item/target/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(0, user)) - cut_overlays() - to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") - return - - -/obj/item/target/attack_hand(mob/user as mob) - // taking pinned targets off! - var/obj/structure/target_stake/stake - for(var/obj/structure/target_stake/T in view(3,src)) - if(T.pinned_target == src) - stake = T - break - - if(stake) - if(stake.pinned_target) - stake.density = TRUE - density = FALSE - layer = OBJ_LAYER - - loc = user.loc - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(src) - to_chat(user, "You take the target out of the stake.") - else - src.loc = get_turf(user) - to_chat(user, "You take the target out of the stake.") - - stake.pinned_target = null - return - - else - ..() - -/obj/item/target/syndicate - icon_state = "target_s" - desc = "A shooting target that looks like a hostile agent." - hp = 2600 // i guess syndie targets are sturdier? -/obj/item/target/alien - icon_state = "target_q" - desc = "A shooting target with a threatening silhouette." - hp = 2350 // alium onest too kinda - -/obj/item/target/bullet_act(var/obj/item/projectile/Proj) - var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" - var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) - var/decaltype = 1 // 1 - scorch, 2 - bullet - - if(istype(/obj/item/projectile/bullet, Proj)) - decaltype = 2 - - - virtualIcon = new(icon, icon_state) - - if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) - - hp -= Proj.damage - if(hp <= 0) - for(var/mob/O in oviewers()) - if ((O.client && !( O.blinded ))) - to_chat(O, "\The [src] breaks into tiny pieces and collapses!") - qdel(src) - - // Create a temporary object to represent the damage - var/obj/bmark = new - bmark.pixel_x = p_x - bmark.pixel_y = p_y - bmark.icon = 'icons/effects/effects.dmi' - bmark.icon_state = "scorch" - - if(decaltype == 1) - // Energy weapons are hot. they scorch! - - // offset correction - bmark.pixel_x-- - bmark.pixel_y-- - - if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) - bmark.icon_state = "scorch" - bmark.set_dir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design - - - else - bmark.icon_state = "light_scorch" - else - - // Bullets are hard. They make dents! - bmark.icon_state = "dent" - - if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes - if(decaltype == 2) // bullet - if(prob(Proj.damage+30)) // bullets make holes more commonly! - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - else // Lasers! - if(prob(Proj.damage-10)) // lasers make holes less commonly - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - - // draw bullet holes - for(var/datum/bullethole/B in bulletholes) - - virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right - virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom - - add_overlay(bmark) // add the decal - - icon = virtualIcon // apply bulletholes over decals - - return - - return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! - - -// Small memory holder entity for transparent bullet holes -/datum/bullethole - // First box - var/b1x1 = 0 - var/b1x2 = 0 - var/b1y = 0 - - // Second box - var/b2x = 0 - var/b2y1 = 0 - var/b2y2 = 0 - -/datum/bullethole/New(var/obj/item/target/Target, var/pixel_x = 0, var/pixel_y = 0) - if(!Target) return - - // Randomize the first box - b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) - b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) - b1y = pixel_y - if(prob(35)) - b1y += rand(-4,4) - - // Randomize the second box - b2x = pixel_x - if(prob(35)) - b2x += rand(-4,4) - b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) - b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) - - Target.bulletholes.Add(src) +// Targets, the things that actually get shot! +/obj/item/target + name = "shooting target" + desc = "A shooting target." + icon = 'icons/obj/objects.dmi' + icon_state = "target_h" + density = FALSE + var/hp = 1800 + var/icon/virtualIcon + var/list/bulletholes = list() + +/obj/item/target/Destroy() + // if a target is deleted and associated with a stake, force stake to forget + for(var/obj/structure/target_stake/T in view(3,src)) + if(T.pinned_target == src) + T.pinned_target = null + T.density = TRUE + break + ..() // delete target + +/obj/item/target/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + // After target moves, check for nearby stakes. If associated, move to target + for(var/obj/structure/target_stake/M in view(3,src)) + if(M.density == 0 && M.pinned_target == src) + M.forceMove(loc) + + // This may seem a little counter-intuitive but I assure you that's for a purpose. + // Stakes are the ones that carry targets, yes, but in the stake code we set + // a stake's density to 0 meaning it can't be pushed anymore. Instead of pushing + // the stake now, we have to push the target. + + + +/obj/item/target/attackby(obj/item/W as obj, mob/user as mob) + if (W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(WT.remove_fuel(0, user)) + cut_overlays() + to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") + return + + +/obj/item/target/attack_hand(mob/user as mob) + // taking pinned targets off! + var/obj/structure/target_stake/stake + for(var/obj/structure/target_stake/T in view(3,src)) + if(T.pinned_target == src) + stake = T + break + + if(stake) + if(stake.pinned_target) + stake.density = TRUE + density = FALSE + layer = OBJ_LAYER + + loc = user.loc + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(src) + to_chat(user, "You take the target out of the stake.") + else + src.loc = get_turf(user) + to_chat(user, "You take the target out of the stake.") + + stake.pinned_target = null + return + + else + ..() + +/obj/item/target/syndicate + icon_state = "target_s" + desc = "A shooting target that looks like a hostile agent." + hp = 2600 // i guess syndie targets are sturdier? +/obj/item/target/alien + icon_state = "target_q" + desc = "A shooting target with a threatening silhouette." + hp = 2350 // alium onest too kinda + +/obj/item/target/bullet_act(var/obj/item/projectile/Proj) + var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" + var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) + var/decaltype = 1 // 1 - scorch, 2 - bullet + + if(istype(/obj/item/projectile/bullet, Proj)) + decaltype = 2 + + + virtualIcon = new(icon, icon_state) + + if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) + + hp -= Proj.damage + if(hp <= 0) + for(var/mob/O in oviewers()) + if ((O.client && !( O.blinded ))) + to_chat(O, "\The [src] breaks into tiny pieces and collapses!") + qdel(src) + + // Create a temporary object to represent the damage + var/obj/bmark = new + bmark.pixel_x = p_x + bmark.pixel_y = p_y + bmark.icon = 'icons/effects/effects.dmi' + bmark.icon_state = "scorch" + + if(decaltype == 1) + // Energy weapons are hot. they scorch! + + // offset correction + bmark.pixel_x-- + bmark.pixel_y-- + + if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) + bmark.icon_state = "scorch" + bmark.set_dir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design + + + else + bmark.icon_state = "light_scorch" + else + + // Bullets are hard. They make dents! + bmark.icon_state = "dent" + + if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes + if(decaltype == 2) // bullet + if(prob(Proj.damage+30)) // bullets make holes more commonly! + new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole + else // Lasers! + if(prob(Proj.damage-10)) // lasers make holes less commonly + new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole + + // draw bullet holes + for(var/datum/bullethole/B in bulletholes) + + virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right + virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom + + add_overlay(bmark) // add the decal + + icon = virtualIcon // apply bulletholes over decals + + return + + return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! + + +// Small memory holder entity for transparent bullet holes +/datum/bullethole + // First box + var/b1x1 = 0 + var/b1x2 = 0 + var/b1y = 0 + + // Second box + var/b2x = 0 + var/b2y1 = 0 + var/b2y2 = 0 + +/datum/bullethole/New(var/obj/item/target/Target, var/pixel_x = 0, var/pixel_y = 0) + if(!Target) return + + // Randomize the first box + b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) + b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) + b1y = pixel_y + if(prob(35)) + b1y += rand(-4,4) + + // Randomize the second box + b2x = pixel_x + if(prob(35)) + b2x += rand(-4,4) + b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) + b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) + + Target.bulletholes.Add(src) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 1cb0b442d96..5b4a5c9d5ff 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -1,446 +1,452 @@ -/obj/item/stack/medical - name = "medical pack" - singular_name = "medical pack" - icon = 'icons/obj/stacks.dmi' - amount = 10 - max_amount = 10 - w_class = ITEMSIZE_SMALL - throw_speed = 4 - throw_range = 20 - var/heal_brute = 0 - var/heal_burn = 0 - var/apply_sounds - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - - var/upgrade_to // The type path this stack can be upgraded to. - -/obj/item/stack/medical/attack(mob/living/carbon/M as mob, mob/user as mob) - if (!istype(M)) - to_chat(user, "\The [src] cannot be applied to [M]!") - return 1 - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return 1 - - var/available = get_amount() - if(!available) - to_chat(user, "There's not enough [uses_charge ? "charge" : "items"] left to use that!") - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(!affecting) - to_chat(user, "No body part there to work on!") - return 1 - - if(affecting.organ_tag == BP_HEAD) - if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) - to_chat(user, "You can't apply [src] through [H.head]!") - return 1 - else - if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) - to_chat(user, "You can't apply [src] through [H.wear_suit]!") - return 1 - - if(affecting.robotic == ORGAN_ROBOT) - to_chat(user, "This isn't useful at all on a robotic limb.") - return 1 - - if(affecting.robotic >= ORGAN_LIFELIKE) - to_chat(user, "You apply the [src], but it seems to have no effect...") - use(1) - return 1 - - H.UpdateDamageIcon() - - else - - M.heal_organ_damage((src.heal_brute/2), (src.heal_burn/2)) - user.visible_message( \ - "[M] has been applied with [src] by [user].", \ - "You apply \the [src] to [M]." \ - ) - use(1) - - M.updatehealth() - -/obj/item/stack/medical/proc/upgrade_stack(var/upgrade_amount) - . = FALSE - - var/turf/T = get_turf(src) - - if(ispath(upgrade_to) && use(upgrade_amount)) - var/obj/item/stack/medical/M = new upgrade_to(T, upgrade_amount) - return M - - return . - -/obj/item/stack/medical/crude_pack - name = "crude bandage" - singular_name = "crude bandage length" - desc = "Some bandages to wrap around bloody stumps." - icon_state = "gauze" - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') - - upgrade_to = /obj/item/stack/medical/bruise_pack - -/obj/item/stack/medical/crude_pack/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_bandaged()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - else - var/available = get_amount() - user.visible_message("\The [user] starts bandaging [M]'s [affecting.name].", \ - "You start bandaging [M]'s [affecting.name]." ) - var/used = 0 - for (var/datum/wound/W in affecting.wounds) - if(W.internal) - continue - if(W.bandaged) - continue - if(used == amount) - break - if(!do_mob(user, M, W.damage/3, exclusive = TRUE)) - to_chat(user, "You must stand still to bandage wounds.") - break - - if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - - if(used >= available) - to_chat(user, "You run out of [src]!") - break - - if (W.current_stage <= W.max_bleeding_stage) - user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ - "You bandage \a [W.desc] on [M]'s [affecting.name]." ) - else - user.visible_message("\The [user] places a bandage over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a bandage over \a [W.desc] on [M]'s [affecting.name]." ) - W.bandage() - playsound(src, pick(apply_sounds), 25) - used++ - affecting.update_damages() - if(used == amount) - if(affecting.is_bandaged()) - to_chat(user, "\The [src] is used up.") - else - to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") - use(used) - -/obj/item/stack/medical/bruise_pack - name = "roll of gauze" - singular_name = "gauze length" - desc = "Some sterile gauze to wrap around bloody stumps." - icon_state = "brutepack" - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - - upgrade_to = /obj/item/stack/medical/advanced/bruise_pack - -/obj/item/stack/medical/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_bandaged()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - else - var/available = get_amount() - user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ - "You start treating [M]'s [affecting.name]." ) - var/used = 0 - for (var/datum/wound/W in affecting.wounds) - if (W.internal) - continue - if(W.bandaged) - continue - if(used == amount) - break - if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) - to_chat(user, "You must stand still to bandage wounds.") - break - - if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - - if(used >= available) - to_chat(user, "You run out of [src]!") - break - - if (W.current_stage <= W.max_bleeding_stage) - user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ - "You bandage \a [W.desc] on [M]'s [affecting.name]." ) - //H.add_side_effect("Itch") - else if (W.damage_type == BRUISE) - user.visible_message("\The [user] places a bruise patch over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a bruise patch over \a [W.desc] on [M]'s [affecting.name]." ) - else - user.visible_message("\The [user] places a bandaid over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a bandaid over \a [W.desc] on [M]'s [affecting.name]." ) - W.bandage() - // W.disinfect() // VOREStation - Tech1 should not disinfect - playsound(src, pick(apply_sounds), 25) - used++ - affecting.update_damages() - if(used == amount) - if(affecting.is_bandaged()) - to_chat(user, "\The [src] is used up.") - else - to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") - use(used) - -/obj/item/stack/medical/ointment - name = "ointment" - desc = "Used to treat those nasty burns." - gender = PLURAL - singular_name = "ointment" - icon_state = "ointment" - heal_burn = 1 - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - apply_sounds = list('sound/effects/ointment.ogg') - drop_sound = 'sound/items/drop/herb.ogg' - pickup_sound = 'sound/items/pickup/herb.ogg' - -/obj/item/stack/medical/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_salved()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - else - user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ - "You start salving the wounds on [M]'s [affecting.name]." ) - if(!do_mob(user, M, 10, exclusive = TRUE)) - to_chat(user, "You must stand still to salve wounds.") - return 1 - if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - user.visible_message("[user] salved wounds on [M]'s [affecting.name].", \ - "You salved wounds on [M]'s [affecting.name]." ) - use(1) - affecting.salve() - playsound(src, pick(apply_sounds), 25) - -/obj/item/stack/medical/advanced/bruise_pack - name = "advanced trauma kit" - singular_name = "advanced trauma kit" - desc = "An advanced trauma kit for severe injuries." - icon_state = "traumakit" - heal_brute = 7 //VOREStation Edit - origin_tech = list(TECH_BIO = 1) - apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg','sound/effects/tape.ogg') - -/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - return - - if(affecting.is_bandaged() && affecting.is_disinfected()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been treated.") - return 1 - else - var/available = get_amount() - user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ - "You start treating [M]'s [affecting.name]." ) - var/used = 0 - for (var/datum/wound/W in affecting.wounds) - if (W.internal) - continue - if (W.bandaged && W.disinfected) - continue - //if(used == amount) //VOREStation Edit - // break //VOREStation Edit - if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) - to_chat(user, "You must stand still to bandage wounds.") - break - if(affecting.is_bandaged() && affecting.is_disinfected()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") - return 1 - - if(used >= available) - to_chat(user, "You run out of [src]!") - break - - if (W.current_stage <= W.max_bleeding_stage) - user.visible_message("\The [user] cleans \a [W.desc] on [M]'s [affecting.name] and seals the edges with bioglue.", \ - "You clean and seal \a [W.desc] on [M]'s [affecting.name]." ) - else if (W.damage_type == BRUISE) - user.visible_message("\The [user] places a medical patch over \a [W.desc] on [M]'s [affecting.name].", \ - "You place a medical patch over \a [W.desc] on [M]'s [affecting.name]." ) - else - user.visible_message("\The [user] smears some bioglue over \a [W.desc] on [M]'s [affecting.name].", \ - "You smear some bioglue over \a [W.desc] on [M]'s [affecting.name]." ) - W.bandage() - W.disinfect() - W.heal_damage(heal_brute) - playsound(src, pick(apply_sounds), 25) - used = 1 //VOREStation Edit - update_icon() // VOREStation Edit - Support for stack icons - affecting.update_damages() - if(used == amount) - if(affecting.is_bandaged()) - to_chat(user, "\The [src] is used up.") - else - to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") - use(used) - -/obj/item/stack/medical/advanced/ointment - name = "advanced burn kit" - singular_name = "advanced burn kit" - desc = "An advanced treatment kit for severe burns." - icon_state = "burnkit" - heal_burn = 7 //VOREStation Edit - origin_tech = list(TECH_BIO = 1) - apply_sounds = list('sound/effects/ointment.ogg') - -/obj/item/stack/medical/advanced/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - - if(affecting.open) - to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") - - if(affecting.is_salved()) - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - else - user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ - "You start salving the wounds on [M]'s [affecting.name]." ) - if(!do_mob(user, M, 10, exclusive = TRUE)) - to_chat(user, "You must stand still to salve wounds.") - return 1 - if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. - to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") - return 1 - user.visible_message( "[user] covers wounds on [M]'s [affecting.name] with regenerative membrane.", \ - "You cover wounds on [M]'s [affecting.name] with regenerative membrane." ) - affecting.heal_damage(0,heal_burn) - use(1) - affecting.salve() - playsound(src, pick(apply_sounds), 25) - update_icon() // VOREStation Edit - Support for stack icons - -/obj/item/stack/medical/splint - name = "medical splints" - singular_name = "medical splint" - desc = "Modular splints capable of supporting and immobilizing bones in all areas of the body." - icon_state = "splint" - amount = 5 - max_amount = 5 - drop_sound = 'sound/items/drop/hat.ogg' - pickup_sound = 'sound/items/pickup/hat.ogg' - - var/list/splintable_organs = list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO) //List of organs you can splint, natch. - -/obj/item/stack/medical/splint/attack(mob/living/carbon/M as mob, mob/living/user as mob) - if(..()) - return 1 - - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) - var/limb = affecting.name - if(!(affecting.organ_tag in splintable_organs)) - to_chat(user, "You can't use \the [src] to apply a splint there!") - return - if(affecting.splinted) - to_chat(user, "[M]'s [limb] is already splinted!") - return - if (M != user) - user.visible_message("[user] starts to apply \the [src] to [M]'s [limb].", "You start to apply \the [src] to [M]'s [limb].", "You hear something being wrapped.") - else - if(( !user.hand && (affecting.organ_tag in list(BP_R_ARM, BP_R_HAND)) || \ - user.hand && (affecting.organ_tag in list(BP_L_ARM, BP_L_HAND)) )) - to_chat(user, "You can't apply a splint to the arm you're using!") - return - user.visible_message("[user] starts to apply \the [src] to their [limb].", "You start to apply \the [src] to your [limb].", "You hear something being wrapped.") - if(do_after(user, 50, M, exclusive = TASK_USER_EXCLUSIVE)) - if(affecting.splinted) - to_chat(user, "[M]'s [limb] is already splinted!") - return - if(M == user && prob(75)) - user.visible_message("\The [user] fumbles [src].", "You fumble [src].", "You hear something being wrapped.") - return - if(ishuman(user)) - var/obj/item/stack/medical/splint/S = split(1) - if(S) - if(affecting.apply_splint(S)) - S.forceMove(affecting) - if (M != user) - user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") - else - user.visible_message("\The [user] successfully applies [src] to their [limb].", "You successfully apply \the [src] to your [limb].", "You hear something being wrapped.") - return - S.dropInto(src.loc) //didn't get applied, so just drop it - if(isrobot(user)) - var/obj/item/stack/medical/splint/B = src - if(B) - if(affecting.apply_splint(B)) - B.forceMove(affecting) - user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") - B.use(1) - return - user.visible_message("\The [user] fails to apply [src].", "You fail to apply [src].", "You hear something being wrapped.") - return - - -/obj/item/stack/medical/splint/ghetto - name = "makeshift splints" - singular_name = "makeshift splint" - desc = "For holding your limbs in place with duct tape and scrap metal." - icon_state = "tape-splint" - amount = 1 - splintable_organs = list(BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) +/obj/item/stack/medical + name = "medical pack" + singular_name = "medical pack" + icon = 'icons/obj/stacks.dmi' + amount = 10 + max_amount = 10 + w_class = ITEMSIZE_SMALL + throw_speed = 4 + throw_range = 20 + var/heal_brute = 0 + var/heal_burn = 0 + var/apply_sounds + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + + var/upgrade_to // The type path this stack can be upgraded to. + +/obj/item/stack/medical/attack(mob/living/carbon/M as mob, mob/user as mob) + if (!istype(M)) + to_chat(user, "\The [src] cannot be applied to [M]!") + return 1 + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return 1 + + var/available = get_amount() + if(!available) + to_chat(user, "There's not enough [uses_charge ? "charge" : "items"] left to use that!") + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(!affecting) + to_chat(user, "No body part there to work on!") + return 1 + + if(affecting.organ_tag == BP_HEAD) + if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) + to_chat(user, "You can't apply [src] through [H.head]!") + return 1 + else + if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + to_chat(user, "You can't apply [src] through [H.wear_suit]!") + return 1 + + if(affecting.robotic == ORGAN_ROBOT) + to_chat(user, "This isn't useful at all on a robotic limb.") + return 1 + + if(affecting.robotic >= ORGAN_LIFELIKE) + to_chat(user, "You apply the [src], but it seems to have no effect...") + use(1) + return 1 + + H.UpdateDamageIcon() + + else + + M.heal_organ_damage((src.heal_brute/2), (src.heal_burn/2)) + user.visible_message( \ + "[M] has been applied with [src] by [user].", \ + "You apply \the [src] to [M]." \ + ) + use(1) + + M.updatehealth() + +/obj/item/stack/medical/proc/upgrade_stack(var/upgrade_amount) + . = FALSE + + var/turf/T = get_turf(src) + + if(ispath(upgrade_to) && use(upgrade_amount)) + var/obj/item/stack/medical/M = new upgrade_to(T, upgrade_amount) + return M + + return . + +/obj/item/stack/medical/crude_pack + name = "crude bandage" + singular_name = "crude bandage length" + desc = "Some bandages to wrap around bloody stumps." + icon_state = "gauze" + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + + upgrade_to = /obj/item/stack/medical/bruise_pack + +/obj/item/stack/medical/crude_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + else + var/available = get_amount() + user.visible_message("\The [user] starts bandaging [M]'s [affecting.name].", \ + "You start bandaging [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if(W.internal) + continue + if(W.bandaged) + continue + if(used == amount) + break + if(!do_mob(user, M, W.damage/3, exclusive = TRUE)) + to_chat(user, "You must stand still to bandage wounds.") + break + + if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if(used >= available) + to_chat(user, "You run out of [src]!") + break + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ + "You bandage \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] places a bandage over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bandage over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + playsound(src, pick(apply_sounds), 25) + used++ + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + +/obj/item/stack/medical/bruise_pack + name = "roll of gauze" + singular_name = "gauze length" + desc = "Some sterile gauze to wrap around bloody stumps." + icon_state = "brutepack" + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + + upgrade_to = /obj/item/stack/medical/advanced/bruise_pack + +/obj/item/stack/medical/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + else + var/available = get_amount() + user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ + "You start treating [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if (W.internal) + continue + if(W.bandaged) + continue + if(used == amount) + break + if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) + to_chat(user, "You must stand still to bandage wounds.") + break + + if(affecting.is_bandaged()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if(used >= available) + to_chat(user, "You run out of [src]!") + break + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] bandages \a [W.desc] on [M]'s [affecting.name].", \ + "You bandage \a [W.desc] on [M]'s [affecting.name]." ) + //H.add_side_effect("Itch") + else if (W.damage_type == BRUISE) + user.visible_message("\The [user] places a bruise patch over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bruise patch over \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] places a bandaid over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a bandaid over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + // W.disinfect() // VOREStation - Tech1 should not disinfect + playsound(src, pick(apply_sounds), 25) + used++ + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + +/obj/item/stack/medical/ointment + name = "ointment" + desc = "Used to treat those nasty burns." + gender = PLURAL + singular_name = "ointment" + icon_state = "ointment" + heal_burn = 1 + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + apply_sounds = list('sound/effects/ointment.ogg') + drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' + +/obj/item/stack/medical/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_salved()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + else + user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ + "You start salving the wounds on [M]'s [affecting.name]." ) + if(!do_mob(user, M, 10, exclusive = TRUE)) + to_chat(user, "You must stand still to salve wounds.") + return 1 + if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + user.visible_message("[user] salved wounds on [M]'s [affecting.name].", \ + "You salved wounds on [M]'s [affecting.name]." ) + use(1) + affecting.salve() + playsound(src, pick(apply_sounds), 25) + +/obj/item/stack/medical/ointment/simple + name = "ointment paste" + desc = "A simple thick paste used to salve burns." + singular_name = "old-ointment" + icon_state = "old-ointment" + +/obj/item/stack/medical/advanced/bruise_pack + name = "advanced trauma kit" + singular_name = "advanced trauma kit" + desc = "An advanced trauma kit for severe injuries." + icon_state = "traumakit" + heal_brute = 7 //VOREStation Edit + origin_tech = list(TECH_BIO = 1) + apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg','sound/effects/tape.ogg') + +/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + return + + if(affecting.is_bandaged() && affecting.is_disinfected()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been treated.") + return 1 + else + var/available = get_amount() + user.visible_message("\The [user] starts treating [M]'s [affecting.name].", \ + "You start treating [M]'s [affecting.name]." ) + var/used = 0 + for (var/datum/wound/W in affecting.wounds) + if (W.internal) + continue + if (W.bandaged && W.disinfected) + continue + //if(used == amount) //VOREStation Edit + // break //VOREStation Edit + if(!do_mob(user, M, W.damage/5, exclusive = TRUE)) + to_chat(user, "You must stand still to bandage wounds.") + break + if(affecting.is_bandaged() && affecting.is_disinfected()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been bandaged.") + return 1 + + if(used >= available) + to_chat(user, "You run out of [src]!") + break + + if (W.current_stage <= W.max_bleeding_stage) + user.visible_message("\The [user] cleans \a [W.desc] on [M]'s [affecting.name] and seals the edges with bioglue.", \ + "You clean and seal \a [W.desc] on [M]'s [affecting.name]." ) + else if (W.damage_type == BRUISE) + user.visible_message("\The [user] places a medical patch over \a [W.desc] on [M]'s [affecting.name].", \ + "You place a medical patch over \a [W.desc] on [M]'s [affecting.name]." ) + else + user.visible_message("\The [user] smears some bioglue over \a [W.desc] on [M]'s [affecting.name].", \ + "You smear some bioglue over \a [W.desc] on [M]'s [affecting.name]." ) + W.bandage() + W.disinfect() + W.heal_damage(heal_brute) + playsound(src, pick(apply_sounds), 25) + used = 1 //VOREStation Edit + update_icon() // VOREStation Edit - Support for stack icons + affecting.update_damages() + if(used == amount) + if(affecting.is_bandaged()) + to_chat(user, "\The [src] is used up.") + else + to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].") + use(used) + +/obj/item/stack/medical/advanced/ointment + name = "advanced burn kit" + singular_name = "advanced burn kit" + desc = "An advanced treatment kit for severe burns." + icon_state = "burnkit" + heal_burn = 7 //VOREStation Edit + origin_tech = list(TECH_BIO = 1) + apply_sounds = list('sound/effects/ointment.ogg') + +/obj/item/stack/medical/advanced/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + + if(affecting.open) + to_chat(user, "The [affecting.name] is cut open, you'll need more than a bandage!") + + if(affecting.is_salved()) + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + else + user.visible_message("\The [user] starts salving wounds on [M]'s [affecting.name].", \ + "You start salving the wounds on [M]'s [affecting.name]." ) + if(!do_mob(user, M, 10, exclusive = TRUE)) + to_chat(user, "You must stand still to salve wounds.") + return 1 + if(affecting.is_salved()) // We do a second check after the delay, in case it was bandaged after the first check. + to_chat(user, "The wounds on [M]'s [affecting.name] have already been salved.") + return 1 + user.visible_message( "[user] covers wounds on [M]'s [affecting.name] with regenerative membrane.", \ + "You cover wounds on [M]'s [affecting.name] with regenerative membrane." ) + affecting.heal_damage(0,heal_burn) + use(1) + affecting.salve() + playsound(src, pick(apply_sounds), 25) + update_icon() // VOREStation Edit - Support for stack icons + +/obj/item/stack/medical/splint + name = "medical splints" + singular_name = "medical splint" + desc = "Modular splints capable of supporting and immobilizing bones in all areas of the body." + icon_state = "splint" + amount = 5 + max_amount = 5 + drop_sound = 'sound/items/drop/hat.ogg' + pickup_sound = 'sound/items/pickup/hat.ogg' + + var/list/splintable_organs = list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO) //List of organs you can splint, natch. + +/obj/item/stack/medical/splint/attack(mob/living/carbon/M as mob, mob/living/user as mob) + if(..()) + return 1 + + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + var/limb = affecting.name + if(!(affecting.organ_tag in splintable_organs)) + to_chat(user, "You can't use \the [src] to apply a splint there!") + return + if(affecting.splinted) + to_chat(user, "[M]'s [limb] is already splinted!") + return + if (M != user) + user.visible_message("[user] starts to apply \the [src] to [M]'s [limb].", "You start to apply \the [src] to [M]'s [limb].", "You hear something being wrapped.") + else + if(( !user.hand && (affecting.organ_tag in list(BP_R_ARM, BP_R_HAND)) || \ + user.hand && (affecting.organ_tag in list(BP_L_ARM, BP_L_HAND)) )) + to_chat(user, "You can't apply a splint to the arm you're using!") + return + user.visible_message("[user] starts to apply \the [src] to their [limb].", "You start to apply \the [src] to your [limb].", "You hear something being wrapped.") + if(do_after(user, 50, M, exclusive = TASK_USER_EXCLUSIVE)) + if(affecting.splinted) + to_chat(user, "[M]'s [limb] is already splinted!") + return + if(M == user && prob(75)) + user.visible_message("\The [user] fumbles [src].", "You fumble [src].", "You hear something being wrapped.") + return + if(ishuman(user)) + var/obj/item/stack/medical/splint/S = split(1) + if(S) + if(affecting.apply_splint(S)) + S.forceMove(affecting) + if (M != user) + user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") + else + user.visible_message("\The [user] successfully applies [src] to their [limb].", "You successfully apply \the [src] to your [limb].", "You hear something being wrapped.") + return + S.dropInto(src.loc) //didn't get applied, so just drop it + if(isrobot(user)) + var/obj/item/stack/medical/splint/B = src + if(B) + if(affecting.apply_splint(B)) + B.forceMove(affecting) + user.visible_message("\The [user] finishes applying [src] to [M]'s [limb].", "You finish applying \the [src] to [M]'s [limb].", "You hear something being wrapped.") + B.use(1) + return + user.visible_message("\The [user] fails to apply [src].", "You fail to apply [src].", "You hear something being wrapped.") + return + + +/obj/item/stack/medical/splint/ghetto + name = "makeshift splints" + singular_name = "makeshift splint" + desc = "For holding your limbs in place with duct tape and scrap metal." + icon_state = "tape-splint" + amount = 1 + splintable_organs = list(BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG) diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 6f97f72e3d3..c040c35db99 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -1,373 +1,373 @@ -/* Diffrent misc types of tiles - * Contains: - * Prototype - * Grass - * Wood - * Carpet - * Blue Carpet - * Linoleum - * - * Put your stuff in fifty_stacks_tiles.dm as well. - */ - -/obj/item/stack/tile - name = "tile" - singular_name = "tile" - desc = "A non-descript floor tile" - randpixel = 7 - w_class = ITEMSIZE_NORMAL - max_amount = 60 - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' - -//crafting / welding vars - var/datum/material/material //*sigh* i guess this is how we're doing this. - var/craftable = FALSE //set to TRUE for tiles you can craft stuff from directly, like grass - var/can_weld = FALSE //set to TRUE for tiles you can reforge into their components via welding, like metal - var/welds_into = /obj/item/stack/material/steel //what you get from the welding. defaults to steel. - var/default_type = DEFAULT_WALL_MATERIAL - - - -/obj/item/stack/tile/Initialize() - . = ..() - randpixel_xy() - if(craftable) - material = get_material_by_name("[default_type]") - if(!material) - return INITIALIZE_HINT_QDEL - if(material) //sanity check - recipes = material.get_recipes() - stacktype = material.stack_type - -/obj/item/stack/tile/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - - if(can_weld == FALSE) - to_chat("You can't reform these into their original components.") - return - - if(get_amount() < 4) - to_chat(user, "You need at least four tiles to do this.") - return - - if(WT.remove_fuel(0,user)) - new welds_into(usr.loc) - usr.update_icon() - visible_message("\The [src] is shaped by [user.name] with the welding tool.","You hear welding.") - var/obj/item/stack/tile/T = src - src = null - var/replace = (user.get_inactive_hand()==T) - T.use(4) - if (!T && replace) - user.put_in_hands(welds_into) - return TRUE - return ..() - -/* - * Grass - */ -/obj/item/stack/tile/grass - name = "grass tile" - singular_name = "grass floor tile" - desc = "A patch of grass like they often use on golf courses." - icon_state = "tile_grass" - default_type = "grass" - force = 1.0 - throwforce = 1.0 - throw_speed = 5 - throw_range = 20 - flags = 0 - origin_tech = list(TECH_BIO = 1) - no_variants = FALSE - drop_sound = 'sound/items/drop/herb.ogg' - pickup_sound = 'sound/items/pickup/herb.ogg' - craftable = TRUE - -/* - * Wood - */ -/obj/item/stack/tile/wood - name = "wood floor tile" - singular_name = "wood floor tile" - desc = "An easy to fit wooden floor tile." - icon_state = "tile-wood" - force = 1.0 - throwforce = 1.0 - throw_speed = 5 - throw_range = 20 - flags = 0 - no_variants = FALSE - drop_sound = 'sound/items/drop/wooden.ogg' - pickup_sound = 'sound/items/pickup/wooden.ogg' - -/obj/item/stack/tile/wood/sif - name = "alien wood tile" - singular_name = "alien wood tile" - desc = "An easy to fit wooden floor tile. It's blue!" - icon_state = "tile-sifwood" - -/obj/item/stack/tile/wood/alt - name = "wood floor tile" - singular_name = "wood floor tile" - icon_state = "tile-wood_tile" - -/obj/item/stack/tile/wood/parquet - name = "parquet wood floor tile" - singular_name = "parquet wood floor tile" - icon_state = "tile-wood_parquet" - -/obj/item/stack/tile/wood/panel - name = "large wood floor tile" - singular_name = "large wood floor tile" - icon_state = "tile-wood_large" - -/obj/item/stack/tile/wood/tile - name = "tiled wood floor tile" - singular_name = "tiled wood floor tile" - icon_state = "tile-wood_tile" - -/obj/item/stack/tile/wood/cyborg - name = "wood floor tile synthesizer" - desc = "A device that makes wood floor tiles." - uses_charge = 1 - charge_costs = list(250) - stacktype = /obj/item/stack/tile/wood - build_type = /obj/item/stack/tile/wood - - - -/* - * Carpets - */ -/obj/item/stack/tile/carpet - name = "carpet" - singular_name = "carpet" - desc = "A piece of carpet. It is the same size as a normal floor tile!" - icon_state = "tile-carpet" - force = 1.0 - throwforce = 1.0 - throw_speed = 5 - throw_range = 20 - flags = 0 - no_variants = FALSE - drop_sound = 'sound/items/drop/cloth.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' - -/obj/item/stack/tile/carpet/teal - desc = "A piece of teal carpet. It is the same size as a normal floor tile!" - icon_state = "tile-tealcarpet" - -/obj/item/stack/tile/carpet/turcarpet - desc = "A piece of turqoise carpet. It is the same size as a normal floor tile!" - icon_state = "tile-turcarpet" - -/obj/item/stack/tile/carpet/bcarpet - desc = "A piece of black diamond-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-bcarpet" - -/obj/item/stack/tile/carpet/blucarpet - desc = "A piece of blue diamond-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-blucarpet" - -/obj/item/stack/tile/carpet/sblucarpet - desc = "A piece of silver-blue diamond-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-sblucarpet" - -/obj/item/stack/tile/carpet/gaycarpet - desc = "A piece of pink diamond-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-gaycarpet" - -/obj/item/stack/tile/carpet/purcarpet - desc = "A piece of purple diamond-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-purcarpet" - -/obj/item/stack/tile/carpet/oracarpet - desc = "A piece of orange diamond-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-oracarpet" - -/obj/item/stack/tile/carpet/brncarpet - desc = "A piece of brown ornate-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-brncarpet" - -/obj/item/stack/tile/carpet/blucarpet2 - desc = "A piece of blue ornate-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-blucarpet2" - -/obj/item/stack/tile/carpet/greencarpet - desc = "A piece of green ornate-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-greencarpet" - -/obj/item/stack/tile/carpet/purplecarpet - desc = "A piece of purple ornate-pattern carpet. It is the same size as a normal floor tile!" - icon_state = "tile-purplecarpet" - -/obj/item/stack/tile/carpet/geo - icon_state = "tile-carpet-deco" - desc = "A piece of carpet with a gnarly geometric design. It is the same size as a normal floor tile!" - -/obj/item/stack/tile/carpet/retro - icon_state = "tile-carpet-retro" - desc = "A piece of carpet with totally wicked blue space patterns. It is the same size as a normal floor tile!" - -/obj/item/stack/tile/carpet/retro_red - icon_state = "tile-carpet-retro-red" - desc = "A piece of carpet with red-ical space patterns. It is the same size as a normal floor tile!" - -/obj/item/stack/tile/carpet/happy - icon_state = "tile-carpet-happy" - desc = "A piece of carpet with happy patterns. It is the same size as a normal floor tile!" - -/obj/item/stack/tile/floor - name = "floor tile" - singular_name = "floor tile" - desc = "A metal tile fit for covering a section of floor." - icon_state = "tile" - force = 6.0 - matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - no_variants = FALSE - can_weld = TRUE - -/obj/item/stack/tile/floor/red - name = "red floor tile" - singular_name = "red floor tile" - color = COLOR_RED_GRAY - icon_state = "tile_white" - no_variants = FALSE - -/obj/item/stack/tile/floor/techgrey - name = "grey techfloor tile" - singular_name = "grey techfloor tile" - icon_state = "techtile_grey" - no_variants = FALSE - -/obj/item/stack/tile/floor/techgrid - name = "grid techfloor tile" - singular_name = "grid techfloor tile" - icon_state = "techtile_grid" - no_variants = FALSE - -/obj/item/stack/tile/floor/techmaint - name = "maint techfloor tile" - singular_name = "maint techfloor tile" - icon_state = "techtile_maint" - no_variants = FALSE - -/obj/item/stack/tile/floor/steel_dirty - name = "steel floor tile" - singular_name = "steel floor tile" - icon_state = "tile_steel" - matter = list(MAT_PLASTEEL = SHEET_MATERIAL_AMOUNT / 4) - welds_into = /obj/item/stack/material/plasteel - no_variants = FALSE - -/obj/item/stack/tile/floor/steel - name = "steel floor tile" - singular_name = "steel floor tile" - icon_state = "tile_steel" - matter = list(MAT_PLASTEEL = SHEET_MATERIAL_AMOUNT / 4) - welds_into = /obj/item/stack/material/plasteel - no_variants = FALSE - -/obj/item/stack/tile/floor/white - name = "white floor tile" - singular_name = "white floor tile" - icon_state = "tile_white" - matter = list(MAT_PLASTIC = SHEET_MATERIAL_AMOUNT / 4) - welds_into = /obj/item/stack/material/plastic - no_variants = FALSE - -/obj/item/stack/tile/floor/yellow - name = "yellow floor tile" - singular_name = "yellow floor tile" - color = COLOR_BROWN - icon_state = "tile_white" - no_variants = FALSE - -/obj/item/stack/tile/floor/dark - name = "dark floor tile" - singular_name = "dark floor tile" - icon_state = "tile_steel" - matter = list(MAT_PLASTEEL = SHEET_MATERIAL_AMOUNT / 4) - welds_into = /obj/item/stack/material/plasteel - no_variants = FALSE - -/obj/item/stack/tile/floor/freezer - name = "freezer floor tile" - singular_name = "freezer floor tile" - icon_state = "tile_freezer" - matter = list(MAT_PLASTIC = SHEET_MATERIAL_AMOUNT / 4) - welds_into = /obj/item/stack/material/plastic - no_variants = FALSE - -/obj/item/stack/tile/floor/cyborg - name = "floor tile synthesizer" - desc = "A device that makes floor tiles." - gender = NEUTER - matter = null - uses_charge = 1 - charge_costs = list(250) - stacktype = /obj/item/stack/tile/floor - build_type = /obj/item/stack/tile/floor - can_weld = FALSE //we're not going there - -/obj/item/stack/tile/linoleum - name = "linoleum" - singular_name = "linoleum" - desc = "A piece of linoleum. It is the same size as a normal floor tile!" - icon_state = "tile-linoleum" - force = 1.0 - throwforce = 1.0 - throw_speed = 5 - throw_range = 20 - flags = 0 - no_variants = FALSE - can_weld = FALSE - -/obj/item/stack/tile/wmarble - name = "light marble tile" - singular_name = "light marble tile" - desc = "Some white marble tiles used for flooring." - icon_state = "tile-wmarble" - force = 6.0 - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - flags = 0 - no_variants = FALSE - can_weld = TRUE - welds_into = /obj/item/stack/material/marble - -/obj/item/stack/tile/bmarble - name = "dark marble tile" - singular_name = "dark marble tile" - desc = "Some black marble tiles used for flooring." - icon_state = "tile-bmarble" - force = 6.0 - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - flags = 0 - no_variants = FALSE - can_weld = TRUE - welds_into = /obj/item/stack/material/marble - -/obj/item/stack/tile/roofing - name = "roofing" - singular_name = "roofing" - desc = "A section of roofing material. You can use it to repair the ceiling, or expand it." - icon_state = "techtile_grid" - can_weld = FALSE //roofing can also be made from wood, so let's not open that can of worms today - -/obj/item/stack/tile/roofing/cyborg - name = "roofing synthesizer" - desc = "A device that makes roofing tiles." - uses_charge = 1 - charge_costs = list(250) - stacktype = /obj/item/stack/tile/roofing - build_type = /obj/item/stack/tile/roofing - can_weld = FALSE +/* Diffrent misc types of tiles + * Contains: + * Prototype + * Grass + * Wood + * Carpet + * Blue Carpet + * Linoleum + * + * Put your stuff in fifty_stacks_tiles.dm as well. + */ + +/obj/item/stack/tile + name = "tile" + singular_name = "tile" + desc = "A non-descript floor tile" + randpixel = 7 + w_class = ITEMSIZE_NORMAL + max_amount = 60 + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' + +//crafting / welding vars + var/datum/material/material //*sigh* i guess this is how we're doing this. + var/craftable = FALSE //set to TRUE for tiles you can craft stuff from directly, like grass + var/can_weld = FALSE //set to TRUE for tiles you can reforge into their components via welding, like metal + var/welds_into = /obj/item/stack/material/steel //what you get from the welding. defaults to steel. + var/default_type = DEFAULT_WALL_MATERIAL + + + +/obj/item/stack/tile/Initialize() + . = ..() + randpixel_xy() + if(craftable) + material = get_material_by_name("[default_type]") + if(!material) + return INITIALIZE_HINT_QDEL + if(material) //sanity check + recipes = material.get_recipes() + stacktype = material.stack_type + +/obj/item/stack/tile/attackby(obj/item/W as obj, mob/user as mob) + if (W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + + if(can_weld == FALSE) + to_chat("You can't reform these into their original components.") + return + + if(get_amount() < 4) + to_chat(user, "You need at least four tiles to do this.") + return + + if(WT.remove_fuel(0,user)) + new welds_into(usr.loc) + usr.update_icon() + visible_message("\The [src] is shaped by [user.name] with the welding tool.","You hear welding.") + var/obj/item/stack/tile/T = src + src = null + var/replace = (user.get_inactive_hand()==T) + T.use(4) + if (!T && replace) + user.put_in_hands(welds_into) + return TRUE + return ..() + +/* + * Grass + */ +/obj/item/stack/tile/grass + name = "grass tile" + singular_name = "grass floor tile" + desc = "A patch of grass like they often use on golf courses." + icon_state = "tile_grass" + default_type = "grass" + force = 1.0 + throwforce = 1.0 + throw_speed = 5 + throw_range = 20 + flags = 0 + origin_tech = list(TECH_BIO = 1) + no_variants = FALSE + drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' + craftable = TRUE + +/* + * Wood + */ +/obj/item/stack/tile/wood + name = "wood floor tile" + singular_name = "wood floor tile" + desc = "An easy to fit wooden floor tile." + icon_state = "tile-wood" + force = 1.0 + throwforce = 1.0 + throw_speed = 5 + throw_range = 20 + flags = 0 + no_variants = FALSE + drop_sound = 'sound/items/drop/wooden.ogg' + pickup_sound = 'sound/items/pickup/wooden.ogg' + +/obj/item/stack/tile/wood/sif + name = "alien wood tile" + singular_name = "alien wood tile" + desc = "An easy to fit wooden floor tile. It's blue!" + icon_state = "tile-sifwood" + +/obj/item/stack/tile/wood/alt + name = "wood floor tile" + singular_name = "wood floor tile" + icon_state = "tile-wood_tile" + +/obj/item/stack/tile/wood/parquet + name = "parquet wood floor tile" + singular_name = "parquet wood floor tile" + icon_state = "tile-wood_parquet" + +/obj/item/stack/tile/wood/panel + name = "large wood floor tile" + singular_name = "large wood floor tile" + icon_state = "tile-wood_large" + +/obj/item/stack/tile/wood/tile + name = "tiled wood floor tile" + singular_name = "tiled wood floor tile" + icon_state = "tile-wood_tile" + +/obj/item/stack/tile/wood/cyborg + name = "wood floor tile synthesizer" + desc = "A device that makes wood floor tiles." + uses_charge = 1 + charge_costs = list(250) + stacktype = /obj/item/stack/tile/wood + build_type = /obj/item/stack/tile/wood + + + +/* + * Carpets + */ +/obj/item/stack/tile/carpet + name = "carpet" + singular_name = "carpet" + desc = "A piece of carpet. It is the same size as a normal floor tile!" + icon_state = "tile-carpet" + force = 1.0 + throwforce = 1.0 + throw_speed = 5 + throw_range = 20 + flags = 0 + no_variants = FALSE + drop_sound = 'sound/items/drop/cloth.ogg' + pickup_sound = 'sound/items/pickup/cloth.ogg' + +/obj/item/stack/tile/carpet/teal + desc = "A piece of teal carpet. It is the same size as a normal floor tile!" + icon_state = "tile-tealcarpet" + +/obj/item/stack/tile/carpet/turcarpet + desc = "A piece of turqoise carpet. It is the same size as a normal floor tile!" + icon_state = "tile-turcarpet" + +/obj/item/stack/tile/carpet/bcarpet + desc = "A piece of black diamond-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-bcarpet" + +/obj/item/stack/tile/carpet/blucarpet + desc = "A piece of blue diamond-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-blucarpet" + +/obj/item/stack/tile/carpet/sblucarpet + desc = "A piece of silver-blue diamond-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-sblucarpet" + +/obj/item/stack/tile/carpet/gaycarpet + desc = "A piece of pink diamond-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-gaycarpet" + +/obj/item/stack/tile/carpet/purcarpet + desc = "A piece of purple diamond-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-purcarpet" + +/obj/item/stack/tile/carpet/oracarpet + desc = "A piece of orange diamond-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-oracarpet" + +/obj/item/stack/tile/carpet/brncarpet + desc = "A piece of brown ornate-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-brncarpet" + +/obj/item/stack/tile/carpet/blucarpet2 + desc = "A piece of blue ornate-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-blucarpet2" + +/obj/item/stack/tile/carpet/greencarpet + desc = "A piece of green ornate-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-greencarpet" + +/obj/item/stack/tile/carpet/purplecarpet + desc = "A piece of purple ornate-pattern carpet. It is the same size as a normal floor tile!" + icon_state = "tile-purplecarpet" + +/obj/item/stack/tile/carpet/geo + icon_state = "tile-carpet-deco" + desc = "A piece of carpet with a gnarly geometric design. It is the same size as a normal floor tile!" + +/obj/item/stack/tile/carpet/retro + icon_state = "tile-carpet-retro" + desc = "A piece of carpet with totally wicked blue space patterns. It is the same size as a normal floor tile!" + +/obj/item/stack/tile/carpet/retro_red + icon_state = "tile-carpet-retro-red" + desc = "A piece of carpet with red-ical space patterns. It is the same size as a normal floor tile!" + +/obj/item/stack/tile/carpet/happy + icon_state = "tile-carpet-happy" + desc = "A piece of carpet with happy patterns. It is the same size as a normal floor tile!" + +/obj/item/stack/tile/floor + name = "floor tile" + singular_name = "floor tile" + desc = "A metal tile fit for covering a section of floor." + icon_state = "tile" + force = 6.0 + matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + no_variants = FALSE + can_weld = TRUE + +/obj/item/stack/tile/floor/red + name = "red floor tile" + singular_name = "red floor tile" + color = COLOR_RED_GRAY + icon_state = "tile_white" + no_variants = FALSE + +/obj/item/stack/tile/floor/techgrey + name = "grey techfloor tile" + singular_name = "grey techfloor tile" + icon_state = "techtile_grey" + no_variants = FALSE + +/obj/item/stack/tile/floor/techgrid + name = "grid techfloor tile" + singular_name = "grid techfloor tile" + icon_state = "techtile_grid" + no_variants = FALSE + +/obj/item/stack/tile/floor/techmaint + name = "maint techfloor tile" + singular_name = "maint techfloor tile" + icon_state = "techtile_maint" + no_variants = FALSE + +/obj/item/stack/tile/floor/steel_dirty + name = "steel floor tile" + singular_name = "steel floor tile" + icon_state = "tile_steel" + matter = list(MAT_PLASTEEL = SHEET_MATERIAL_AMOUNT / 4) + welds_into = /obj/item/stack/material/plasteel + no_variants = FALSE + +/obj/item/stack/tile/floor/steel + name = "steel floor tile" + singular_name = "steel floor tile" + icon_state = "tile_steel" + matter = list(MAT_PLASTEEL = SHEET_MATERIAL_AMOUNT / 4) + welds_into = /obj/item/stack/material/plasteel + no_variants = FALSE + +/obj/item/stack/tile/floor/white + name = "white floor tile" + singular_name = "white floor tile" + icon_state = "tile_white" + matter = list(MAT_PLASTIC = SHEET_MATERIAL_AMOUNT / 4) + welds_into = /obj/item/stack/material/plastic + no_variants = FALSE + +/obj/item/stack/tile/floor/yellow + name = "yellow floor tile" + singular_name = "yellow floor tile" + color = COLOR_BROWN + icon_state = "tile_white" + no_variants = FALSE + +/obj/item/stack/tile/floor/dark + name = "dark floor tile" + singular_name = "dark floor tile" + icon_state = "tile_steel" + matter = list(MAT_PLASTEEL = SHEET_MATERIAL_AMOUNT / 4) + welds_into = /obj/item/stack/material/plasteel + no_variants = FALSE + +/obj/item/stack/tile/floor/freezer + name = "freezer floor tile" + singular_name = "freezer floor tile" + icon_state = "tile_freezer" + matter = list(MAT_PLASTIC = SHEET_MATERIAL_AMOUNT / 4) + welds_into = /obj/item/stack/material/plastic + no_variants = FALSE + +/obj/item/stack/tile/floor/cyborg + name = "floor tile synthesizer" + desc = "A device that makes floor tiles." + gender = NEUTER + matter = null + uses_charge = 1 + charge_costs = list(250) + stacktype = /obj/item/stack/tile/floor + build_type = /obj/item/stack/tile/floor + can_weld = FALSE //we're not going there + +/obj/item/stack/tile/linoleum + name = "linoleum" + singular_name = "linoleum" + desc = "A piece of linoleum. It is the same size as a normal floor tile!" + icon_state = "tile-linoleum" + force = 1.0 + throwforce = 1.0 + throw_speed = 5 + throw_range = 20 + flags = 0 + no_variants = FALSE + can_weld = FALSE + +/obj/item/stack/tile/wmarble + name = "light marble tile" + singular_name = "light marble tile" + desc = "Some white marble tiles used for flooring." + icon_state = "tile-wmarble" + force = 6.0 + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + flags = 0 + no_variants = FALSE + can_weld = TRUE + welds_into = /obj/item/stack/material/marble + +/obj/item/stack/tile/bmarble + name = "dark marble tile" + singular_name = "dark marble tile" + desc = "Some black marble tiles used for flooring." + icon_state = "tile-bmarble" + force = 6.0 + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + flags = 0 + no_variants = FALSE + can_weld = TRUE + welds_into = /obj/item/stack/material/marble + +/obj/item/stack/tile/roofing + name = "roofing" + singular_name = "roofing" + desc = "A section of roofing material. You can use it to repair the ceiling, or expand it." + icon_state = "techtile_grid" + can_weld = FALSE //roofing can also be made from wood, so let's not open that can of worms today + +/obj/item/stack/tile/roofing/cyborg + name = "roofing synthesizer" + desc = "A device that makes roofing tiles." + uses_charge = 1 + charge_costs = list(250) + stacktype = /obj/item/stack/tile/roofing + build_type = /obj/item/stack/tile/roofing + can_weld = FALSE diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 62e6bfa9788..d2fd4893514 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -1,474 +1,491 @@ -//Items labled as 'trash' for the trash bag. -//TODO: Make this an item var or something... - -//Added by Jack Rost -/obj/item/trash - icon = 'icons/obj/trash.dmi' - w_class = ITEMSIZE_SMALL - desc = "This is rubbish." - drop_sound = 'sound/items/drop/wrapper.ogg' - pickup_sound = 'sound/items/pickup/wrapper.ogg' - matter = list(MAT_STEEL = 30) - var/age = 0 - -/obj/item/trash/New(var/newloc, var/_age) - ..(newloc) - if(!isnull(_age)) - age = _age - -/obj/item/trash/Initialize(mapload) - if(!mapload || !config.persistence_ignore_mapload) - SSpersistence.track_value(src, /datum/persistent/filth/trash) - . = ..() - -/obj/item/trash/Destroy() - SSpersistence.forget_value(src, /datum/persistent/filth/trash) - . = ..() - -/obj/item/trash/raisins - name = "\improper 4no raisins" - icon_state = "4no_raisins" - -/obj/item/trash/candy - name = "hard candy wrapper" - icon_state = "candy" - -/obj/item/trash/candy/gums - name = "gummy candy bag" - icon_state = "candy_gums" - -/obj/item/trash/candy/proteinbar - name = "protein bar wrapper" - icon_state = "proteinbar" - -/obj/item/trash/candy/fruitbar - name = "fruit bar wrapper" - icon_state = "fruitbar" - -/obj/item/trash/cheesie - name = "\improper Cheesie Honkers bag" - icon_state = "cheesie_honkers" - -/obj/item/trash/chips - name = "chips bag" - icon_state = "chips" - -/obj/item/trash/chips/bbq - name = "bbq chips bag" - icon_state = "chips_bbq" - -/obj/item/trash/chips/snv - name = "salt & vinegar chips bag" - icon_state = "chips_snv" - -/obj/item/trash/cookiesnack - name = "\improper Carps Ahoy! miniature cookies packet" - icon_state = "cookiesnack" - -/obj/item/trash/popcorn - name = "popcorn bag" - icon_state = "popcorn" - -/obj/item/trash/tuna - name = "fish flake packet" - icon_state = "tuna" - -/obj/item/trash/sosjerky - name = "Scaredy's Private Reserve Beef Jerky wrapper" - icon_state = "sosjerky" - -/obj/item/trash/unajerky - name = "Moghes Imported Sissalik Jerky tin" - icon_state = "unathitinred" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/syndi_cakes - name = "syndi cakes box" - icon_state = "syndi_cakes" - -/obj/item/trash/waffles - name = "waffles tray" - icon_state = "waffles" - -/obj/item/trash/plate - name = "plate" - icon_state = "plate" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/asian_bowl - name = "asian bowl" - icon_state = "asian_bowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/snack_bowl - name = "snack bowl" - icon_state = "snack_bowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/small_bowl - name = "small bowl" - icon_state = "small_bowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/pistachios - name = "pistachios packet" - icon_state = "pistachios_pack" - -/obj/item/trash/semki - name = "semki packet" - icon_state = "semki_pack" - -/obj/item/trash/koisbar - name = "candy wrapper" - icon_state = "koisbar" - -/obj/item/trash/kokobar - name = "candy wrapper" - icon_state = "kokobar" - -/obj/item/trash/skrellsnax - name = "skrellsnax packet" - icon_state = "skrellsnacks" - -/obj/item/trash/gumpack - name = "gum packet" - icon_state = "gum_pack" - -/obj/item/trash/admints - name = "mint wrapper" - icon_state = "admint_pack" - -/obj/item/trash/coffee - name = "empty cup" - icon_state = "coffee_vended" - drop_sound = 'sound/items/drop/papercup.ogg' - pickup_sound = 'sound/items/pickup/papercup.ogg' - -/obj/item/trash/ramen - name = "cup ramen" - icon_state = "ramen" - drop_sound = 'sound/items/drop/papercup.ogg' - pickup_sound = 'sound/items/pickup/papercup.ogg' - -/obj/item/trash/tray - name = "tray" - icon_state = "tray" - drop_sound = 'sound/items/trayhit1.ogg' - -/obj/item/trash/candle - name = "candle" - icon = 'icons/obj/candle.dmi' - icon_state = "candle4" - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - -/obj/item/trash/liquidfood - name = "\improper \"LiquidFood\" ration packet" - icon_state = "liquidfood" - -/obj/item/trash/liquidprotein - name = "\improper \"LiquidProtein\" ration packet" - icon_state = "liquidprotein" - -/obj/item/trash/liquidvitamin - name = "\improper \"VitaPaste\" ration packet" - icon_state = "liquidvitamin" - -/obj/item/trash/tastybread - name = "bread tube wrapper" - icon_state = "tastybread" - -// Aurora Food Port -/obj/item/trash/brownies - name = "brownie tray" - icon_state = "brownies" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/snacktray - name = "snacktray" - icon_state = "snacktray" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/dipbowl - name = "dip bowl" - icon_state = "dipbowl" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/chipbasket - name = "empty chip basket" - icon_state = "chipbasket_empty" - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/trash/spitgum - name = "old gum" - desc = "A disgusting chewed up wad of gum." - icon = 'icons/inventory/face/item.dmi' - icon_state = "spit-gum" - drop_sound = 'sound/items/drop/flesh.ogg' - pickup_sound = 'sound/items/pickup/flesh.ogg' - -/obj/item/trash/lollibutt - name = "lollipop stick" - desc = "A lollipop stick devoid of pop." - icon = 'icons/inventory/face/item.dmi' - icon_state = "pop-stick" - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - -/obj/item/trash/spitwad - name = "spit wad" - desc = "A disgusting spitwad." - icon = 'icons/inventory/face/item.dmi' - icon_state = "spit-chew" - drop_sound = 'sound/items/drop/flesh.ogg' - pickup_sound = 'sound/items/pickup/flesh.ogg' - slot_flags = SLOT_EARS | SLOT_MASK - -/obj/item/trash/attack(mob/M as mob, mob/living/user as mob) - return - - -/obj/item/trash/beef - name = "empty beef can" - icon_state = "beef" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/beans - name = "empty bean can" - icon_state = "beans" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/tomato - name = "empty tomato soup can" - icon_state = "tomato" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/spinach - name = "empty spinach can" - icon_state = "spinach" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/fishegg - name = "empty fisheggs can" - icon_state = "fisheggs" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/carpegg - name = "empty carpeggs can" - icon_state = "carpeggs" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/ntbeans - name = "empty baked bean can" - icon_state = "ntbeans" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/salo - name = "salo pack" - icon_state = "pigfat" - -/obj/item/trash/croutons - name = "suhariki pack" - icon_state = "croutons" - -/obj/item/trash/squid - name = "calamari pack" - icon_state = "squid" - -/obj/item/trash/driedfish - name = "vobla pack" - icon_state = "driedfish" - -/obj/item/trash/lunacakewrap - name = "cake wrapper" - icon_state = "cakewrap" - -/obj/item/trash/mochicakewrap - name = "cake wrapper" - icon_state = "mochicakewrap" - -/obj/item/trash/mooncakewrap - name = "cake wrapper" - icon_state = "mooncakewrap" - -/obj/item/trash/tidegobs - name = "tide gob bag" - icon_state = "tidegobs" - -/obj/item/trash/saturno - name = "\improper saturn-Os bag" - icon_state = "saturn0s" - -/obj/item/trash/jupiter - name = "gello cup" - icon_state = "jupiter" - -/obj/item/trash/pluto - name = "rod bag" - icon_state = "pluto" - -/obj/item/trash/venus - name = "hot cakes bag" - icon_state = "venus" - -/obj/item/trash/mars - name = "frouka box" - icon_state = "mars" - -/obj/item/trash/oort - name = "oort rock bag" - icon_state = "oort" - -/obj/item/trash/weebonuts - name = "red alert nuts bag" - icon_state = "weebonuts" - -/obj/item/trash/stick - name = "stick" - desc = "a stick from some snack or other food item, not even useful as crafting material." - icon_state = "stick" - -/obj/item/trash/maps - name = "empty MAPS can" - icon_state = "maps" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/spacer_cake_wrap - name = "snack cake wrapper" - icon_state = "spacercake_wrap" - -/obj/item/trash/sun_snax - name = "sun snax bag" - icon_state = "sun_snax" - -/obj/item/trash/wasabi_peas - name = "wasabi peas bag" - icon_state = "wasabi_peas" - -/obj/item/trash/namagashi - name = "namagashi bag" - icon_state = "namagashi" - -/obj/item/trash/pocky - name = "pocky bag" - icon_state = "pocky" - -/obj/item/trash/appleberry - name = "appleberry can" - icon_state = "appleberry" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/hakarl - name = "\improper Indigo Co. Hákarl bag" - icon_state = "hakarl" - -/obj/item/trash/pretzel - name = "\improper Value Pretzel Snack" - icon_state = "pretzel" - -/obj/item/trash/sweetration - name = "desert ration bag" - icon_state = "baseration" - -/obj/item/trash/genration - name = "generic ration bag" - icon_state = "genration" - -/obj/item/trash/meatration - name = "meat ration bag" - icon_state = "meatration" - -/obj/item/trash/vegration - name = "veggie ration bag" - icon_state = "vegration" - -/obj/item/trash/tgmc_mre - name = "\improper CRS ration bag" - icon_state = "tgmc_mre_trash" - -/obj/item/trash/smolburger - name = "burger packaging" - icon_state = "smolburger" - -/obj/item/trash/smolhotdog - name = "hotdog packaging" - icon_state = "smolhotdog" - -/obj/item/trash/smolburrito - name = "burrito packaging" - icon_state = "smolburrito" - -/obj/item/trash/candybowl - name = "candy bowl" - icon_state = "candy_bowl" - -/obj/item/trash/brainzsnax - name = "\improper BrainzSnax can" - icon_state = "brainzsnax" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -/obj/item/trash/brainzsnaxred - name = "\improper BrainzSnax RED can" - icon_state = "brainzsnaxred" - drop_sound = 'sound/items/drop/soda.ogg' - pickup_sound = 'sound/items/pickup/soda.ogg' - -//Candy Bars (1-10) -/obj/item/trash/candy/cb01 - name = "\improper Tau Ceti Bar wrapper" - icon_state = "cb01" - -/obj/item/trash/candy/cb02 - name = "\improper Hundred-Thousand Thaler Bar wrapper" - icon_state = "cb02" - -/obj/item/trash/candy/cb03 - name = "\improper Lars' Saltlakris wrapper" - icon_state = "cb03" - -/obj/item/trash/candy/cb04 - name = "\improper Aerostat Bar wrapper" - icon_state = "cb04" - -/obj/item/trash/candy/cb05 - name = "\improper Andromeda Bar wrapper" - icon_state = "cb05" - -/obj/item/trash/candy/cb06 - name = "\improper Mocha Crunch wrapper" - icon_state = "cb06" - -/obj/item/trash/candy/cb07 - name = "\improper TaroMilk Bar wrapper" - icon_state = "cb07" - -/obj/item/trash/candy/cb08 - name = "\improper Cronk Bar wrapper" - icon_state = "cb08" - -/obj/item/trash/candy/cb09 - name = "\improper Kaju Mamma! Bar wrapper" - icon_state = "cb09" - -/obj/item/trash/candy/cb10 - name = "\improper Shantak Bar wrapper" +//Items labled as 'trash' for the trash bag. +//TODO: Make this an item var or something... + +//Added by Jack Rost +/obj/item/trash + icon = 'icons/obj/trash.dmi' + w_class = ITEMSIZE_SMALL + desc = "This is rubbish." + drop_sound = 'sound/items/drop/wrapper.ogg' + pickup_sound = 'sound/items/pickup/wrapper.ogg' + matter = list(MAT_STEEL = 30) + var/age = 0 + +/obj/item/trash/New(var/newloc, var/_age) + ..(newloc) + if(!isnull(_age)) + age = _age + +/obj/item/trash/Initialize(mapload) + if(!mapload || !config.persistence_ignore_mapload) + SSpersistence.track_value(src, /datum/persistent/filth/trash) + . = ..() + +/obj/item/trash/Destroy() + SSpersistence.forget_value(src, /datum/persistent/filth/trash) + . = ..() + +/obj/item/trash/raisins + name = "\improper 4no raisins" + icon_state = "4no_raisins" + +/obj/item/trash/candy + name = "hard candy wrapper" + icon_state = "candy" + +/obj/item/trash/candy/gums + name = "gummy candy bag" + icon_state = "candy_gums" + +/obj/item/trash/candy/proteinbar + name = "protein bar wrapper" + icon_state = "proteinbar" + +/obj/item/trash/candy/fruitbar + name = "fruit bar wrapper" + icon_state = "fruitbar" + +/obj/item/trash/cheesie + name = "\improper Cheesie Honkers bag" + icon_state = "cheesie_honkers" + +/obj/item/trash/chips + name = "chips bag" + icon_state = "chips" + +/obj/item/trash/chips/bbq + name = "bbq chips bag" + icon_state = "chips_bbq" + +/obj/item/trash/chips/snv + name = "salt & vinegar chips bag" + icon_state = "chips_snv" + +/obj/item/trash/cookiesnack + name = "\improper Carps Ahoy! miniature cookies packet" + icon_state = "cookiesnack" + +/obj/item/trash/popcorn + name = "popcorn bag" + icon_state = "popcorn" + +/obj/item/trash/tuna + name = "fish flake packet" + icon_state = "tuna" + +/obj/item/trash/sosjerky + name = "Scaredy's Private Reserve Beef Jerky wrapper" + icon_state = "sosjerky" + +/obj/item/trash/unajerky + name = "Moghes Imported Sissalik Jerky tin" + icon_state = "unathitinred" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/syndi_cakes + name = "syndi cakes box" + icon_state = "syndi_cakes" + +/obj/item/trash/waffles + name = "waffles tray" + icon_state = "waffles" + +/obj/item/trash/plate + name = "plate" + icon_state = "plate" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/asian_bowl + name = "asian bowl" + icon_state = "asian_bowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/snack_bowl + name = "snack bowl" + icon_state = "snack_bowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/small_bowl + name = "small bowl" + icon_state = "small_bowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/pistachios + name = "pistachios packet" + icon_state = "pistachios_pack" + +/obj/item/trash/semki + name = "semki packet" + icon_state = "semki_pack" + +/obj/item/trash/koisbar + name = "candy wrapper" + icon_state = "koisbar" + +/obj/item/trash/kokobar + name = "candy wrapper" + icon_state = "kokobar" + +/obj/item/trash/skrellsnax + name = "skrellsnax packet" + icon_state = "skrellsnacks" + +/obj/item/trash/gumpack + name = "gum packet" + icon_state = "gum_pack" + +/obj/item/trash/admints + name = "mint wrapper" + icon_state = "admint_pack" + +/obj/item/trash/coffee + name = "empty cup" + icon_state = "coffee_vended" + drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' + +/obj/item/trash/ramen + name = "cup ramen" + icon_state = "ramen" + drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' + +/obj/item/trash/tray + name = "tray" + icon_state = "tray" + drop_sound = 'sound/items/trayhit1.ogg' + +/obj/item/trash/candle + name = "candle" + icon = 'icons/obj/candle.dmi' + icon_state = "candle4" + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + +/obj/item/trash/liquidfood + name = "\improper \"LiquidFood\" ration packet" + icon_state = "liquidfood" + +/obj/item/trash/liquidprotein + name = "\improper \"LiquidProtein\" ration packet" + icon_state = "liquidprotein" + +/obj/item/trash/liquidvitamin + name = "\improper \"VitaPaste\" ration packet" + icon_state = "liquidvitamin" + +/obj/item/trash/tastybread + name = "bread tube wrapper" + icon_state = "tastybread" + +// Aurora Food Port +/obj/item/trash/brownies + name = "brownie tray" + icon_state = "brownies" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/snacktray + name = "snacktray" + icon_state = "snacktray" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/dipbowl + name = "dip bowl" + icon_state = "dipbowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/chipbasket + name = "empty chip basket" + icon_state = "chipbasket_empty" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/trash/spitgum + name = "old gum" + desc = "A disgusting chewed up wad of gum." + icon = 'icons/inventory/face/item.dmi' + icon_state = "spit-gum" + drop_sound = 'sound/items/drop/flesh.ogg' + pickup_sound = 'sound/items/pickup/flesh.ogg' + +/obj/item/trash/lollibutt + name = "lollipop stick" + desc = "A lollipop stick devoid of pop." + icon = 'icons/inventory/face/item.dmi' + icon_state = "pop-stick" + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + +/obj/item/trash/spitwad + name = "spit wad" + desc = "A disgusting spitwad." + icon = 'icons/inventory/face/item.dmi' + icon_state = "spit-chew" + drop_sound = 'sound/items/drop/flesh.ogg' + pickup_sound = 'sound/items/pickup/flesh.ogg' + slot_flags = SLOT_EARS | SLOT_MASK + +/obj/item/trash/attack(mob/M as mob, mob/living/user as mob) + return + + +/obj/item/trash/beef + name = "empty beef can" + icon_state = "beef" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/beans + name = "empty bean can" + icon_state = "beans" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/tomato + name = "empty tomato soup can" + icon_state = "tomato" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/spinach + name = "empty spinach can" + icon_state = "spinach" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/fishegg + name = "empty fisheggs can" + icon_state = "fisheggs" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/carpegg + name = "empty carpeggs can" + icon_state = "carpeggs" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/ntbeans + name = "empty baked bean can" + icon_state = "ntbeans" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/salo + name = "salo pack" + icon_state = "pigfat" + +/obj/item/trash/croutons + name = "suhariki pack" + icon_state = "croutons" + +/obj/item/trash/squid + name = "calamari pack" + icon_state = "squid" + +/obj/item/trash/driedfish + name = "vobla pack" + icon_state = "driedfish" + +/obj/item/trash/lunacakewrap + name = "cake wrapper" + icon_state = "cakewrap" + +/obj/item/trash/mochicakewrap + name = "cake wrapper" + icon_state = "mochicakewrap" + +/obj/item/trash/mooncakewrap + name = "cake wrapper" + icon_state = "mooncakewrap" + +/obj/item/trash/tidegobs + name = "tide gob bag" + icon_state = "tidegobs" + +/obj/item/trash/saturno + name = "\improper saturn-Os bag" + icon_state = "saturn0s" + +/obj/item/trash/jupiter + name = "gello cup" + icon_state = "jupiter" + +/obj/item/trash/pluto + name = "rod bag" + icon_state = "pluto" + +/obj/item/trash/venus + name = "hot cakes bag" + icon_state = "venus" + +/obj/item/trash/mars + name = "frouka box" + icon_state = "mars" + +/obj/item/trash/oort + name = "oort rock bag" + icon_state = "oort" + +/obj/item/trash/weebonuts + name = "red alert nuts bag" + icon_state = "weebonuts" + +/obj/item/trash/stick + name = "stick" + desc = "a stick from some snack or other food item, not even useful as crafting material." + icon_state = "stick" + +/obj/item/trash/maps + name = "empty MAPS can" + icon_state = "maps" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/spacer_cake_wrap + name = "snack cake wrapper" + icon_state = "spacercake_wrap" + +/obj/item/trash/sun_snax + name = "sun snax bag" + icon_state = "sun_snax" + +/obj/item/trash/wasabi_peas + name = "wasabi peas bag" + icon_state = "wasabi_peas" + +/obj/item/trash/namagashi + name = "namagashi bag" + icon_state = "namagashi" + +/obj/item/trash/pocky + name = "pocky bag" + icon_state = "pocky" + +/obj/item/trash/appleberry + name = "appleberry can" + icon_state = "appleberry" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/hakarl + name = "\improper Indigo Co. Hákarl bag" + icon_state = "hakarl" + +/obj/item/trash/pretzel + name = "\improper Value Pretzel Snack" + icon_state = "pretzel" + +/obj/item/trash/sweetration + name = "desert ration bag" + icon_state = "baseration" + +/obj/item/trash/genration + name = "generic ration bag" + icon_state = "genration" + +/obj/item/trash/meatration + name = "meat ration bag" + icon_state = "meatration" + +/obj/item/trash/vegration + name = "veggie ration bag" + icon_state = "vegration" + +/obj/item/trash/tgmc_mre + name = "\improper CRS ration bag" + icon_state = "tgmc_mre_trash" + +/obj/item/trash/smolburger + name = "burger packaging" + icon_state = "smolburger" + +/obj/item/trash/smolhotdog + name = "hotdog packaging" + icon_state = "smolhotdog" + +/obj/item/trash/smolburrito + name = "burrito packaging" + icon_state = "smolburrito" + +/obj/item/trash/candybowl + name = "candy bowl" + icon_state = "candy_bowl" + +/obj/item/trash/brainzsnax + name = "\improper BrainzSnax can" + icon_state = "brainzsnax" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + +/obj/item/trash/brainzsnaxred + name = "\improper BrainzSnax RED can" + icon_state = "brainzsnaxred" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' + + +/obj/item/trash/pasty + name = "pasty packaging" + icon_state = "pasty" + +/obj/item/trash/sausageroll + name = "sausage roll packaging" + icon_state = "sausageroll" + +/obj/item/trash/scotchegg + name = "scotch egg packaging" + icon_state = "scotchegg" + +/obj/item/trash/porkpie + name = "pork pie packaging" + icon_state = "porkpie" + +//Candy Bars (1-10) +/obj/item/trash/candy/cb01 + name = "\improper Tau Ceti Bar wrapper" + icon_state = "cb01" + +/obj/item/trash/candy/cb02 + name = "\improper Hundred-Thousand Thaler Bar wrapper" + icon_state = "cb02" + +/obj/item/trash/candy/cb03 + name = "\improper Lars' Saltlakris wrapper" + icon_state = "cb03" + +/obj/item/trash/candy/cb04 + name = "\improper Aerostat Bar wrapper" + icon_state = "cb04" + +/obj/item/trash/candy/cb05 + name = "\improper Andromeda Bar wrapper" + icon_state = "cb05" + +/obj/item/trash/candy/cb06 + name = "\improper Mocha Crunch wrapper" + icon_state = "cb06" + +/obj/item/trash/candy/cb07 + name = "\improper TaroMilk Bar wrapper" + icon_state = "cb07" + +/obj/item/trash/candy/cb08 + name = "\improper Cronk Bar wrapper" + icon_state = "cb08" + +/obj/item/trash/candy/cb09 + name = "\improper Kaju Mamma! Bar wrapper" + icon_state = "cb09" + +/obj/item/trash/candy/cb10 + name = "\improper Shantak Bar wrapper" icon_state = "cb10" \ No newline at end of file diff --git a/code/game/objects/items/trash_vr.dm b/code/game/objects/items/trash_vr.dm index 08fbdda5b6f..4a50236a62f 100644 --- a/code/game/objects/items/trash_vr.dm +++ b/code/game/objects/items/trash_vr.dm @@ -95,4 +95,14 @@ /obj/item/trash/ratpacktaco name = "\improper Prepackaged Meal Tray" icon = 'icons/obj/trash_vr.dmi' - icon_state = "altevian_pack_taco-trash" \ No newline at end of file + icon_state = "altevian_pack_taco-trash" + +/obj/item/trash/ratpackcake + name = "\improper Prepackaged Meal Tray" + icon = 'icons/obj/trash_vr.dmi' + icon_state = "altevian_pack_cake-trash" + +/obj/item/trash/ratpackmeat + name = "\improper Prepackaged Meal Tray" + icon = 'icons/obj/trash_vr.dmi' + icon_state = "altevian_pack_meat-trash" \ No newline at end of file diff --git a/code/game/objects/items/uav.dm b/code/game/objects/items/uav.dm index 2dc4cad3ee4..20bb85d69b7 100644 --- a/code/game/objects/items/uav.dm +++ b/code/game/objects/items/uav.dm @@ -118,7 +118,7 @@ visible_message("[user] pairs [I] to [nickname]") toggle_pairing() - else if(I.is_screwdriver() && cell) + else if(I.has_tool_quality(TOOL_SCREWDRIVER) && cell) if(do_after(user, 3 SECONDS, src)) to_chat(user, "You remove [cell] into [nickname].") playsound(src, I.usesound, 50, 1) diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 72c0c1915cb..96cdd170d01 100644 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -1,492 +1,492 @@ -/* -CONTAINS: -AI MODULES - -*/ - -// AI module - -/obj/item/weapon/aiModule - name = "\improper AI module" - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - desc = "An AI Module for transmitting encrypted instructions to the AI." - force = 5.0 - w_class = ITEMSIZE_SMALL - throwforce = 5.0 - throw_speed = 3 - throw_range = 15 - origin_tech = list(TECH_DATA = 3) - preserve_item = 1 - matter = list(MAT_STEEL = 30, MAT_GLASS = 10) - var/datum/ai_laws/laws = null - -/obj/item/weapon/aiModule/proc/install(var/atom/movable/AM, var/mob/living/user) - if(!user.IsAdvancedToolUser() && isanimal(user)) - var/mob/living/simple_mob/S = user - if(!S.IsHumanoidToolUser(src)) - return 0 - - if (istype(AM, /obj/machinery/computer/aiupload)) - var/obj/machinery/computer/aiupload/comp = AM - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if (!comp.current) - to_chat(usr, "You haven't selected an AI to transmit laws to!") - return - - if (comp.current.stat == 2 || comp.current.control_disabled == 1) - to_chat(usr, "Upload failed. No signal is being detected from the AI.") - else if (comp.current.see_in_dark == 0) - to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - for(var/mob/living/silicon/robot/R in mob_list) - if(R.lawupdate && (R.connected_ai == comp.current)) - to_chat(R, "These are your laws now:") - R.show_laws() - to_chat(usr, "Upload complete. The AI's laws have been modified.") - - - else if (istype(AM, /obj/machinery/computer/borgupload)) - var/obj/machinery/computer/borgupload/comp = AM - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if (!comp.current) - to_chat(usr, "You haven't selected a robot to transmit laws to!") - return - - if (comp.current.stat == 2 || comp.current.emagged) - to_chat(usr, "Upload failed. No signal is being detected from the robot.") - else if (comp.current.connected_ai) - to_chat(usr, "Upload failed. The robot is slaved to an AI.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - to_chat(usr, "Upload complete. The robot's laws have been modified.") - - else if(istype(AM, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = AM - if(R.stat == DEAD) - to_chat(user, "Law Upload Error: Unit is nonfunctional.") - return - if(R.emagged) - to_chat(user, "Law Upload Error: Cannot obtain write access to laws.") - to_chat(R, "Law modification attempt detected. Blocking.") - return - if(R.connected_ai) - to_chat(user, "Law Upload Error: Unit is slaved to an AI.") - return - - R.visible_message("\The [user] slides a law module into \the [R].") - to_chat(R, "Local law upload in progress.") - to_chat(user, "Uploading laws from board. This will take a moment...") - if(do_after(user, 10 SECONDS)) - transmitInstructions(R, user) - to_chat(R, "These are your laws now:") - R.show_laws() - to_chat(user, "Law upload complete. Unit's laws have been modified.") - else - to_chat(user, "Law Upload Error: Law board was removed before upload was complete. Aborting.") - to_chat(R, "Law upload aborted.") - - -/obj/item/weapon/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if(laws) - laws.sync(target, 0) - target.notify_of_law_change() - addAdditionalLaws(target, sender) - - to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") - target.show_laws() - -/obj/item/weapon/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") - log_and_message_admins("used [src.name] on [target.name]([target.key])") - -/obj/item/weapon/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - - -/******************** Modules ********************/ - -/******************** Safeguard ********************/ - -/obj/item/weapon/aiModule/safeguard - name = "\improper 'Safeguard' AI module" - var/targetName = "" - desc = "A 'safeguard' AI module: 'Safeguard . Anyone threatening or attempting to harm is no longer to be considered a crew member, and is a threat which must be neutralized.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/safeguard/attack_self(var/mob/user as mob) - ..() - var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name)) - targetName = targName - desc = text("A 'safeguard' AI module: 'Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.'", targetName, targetName) - -/obj/item/weapon/aiModule/safeguard/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - ..() - -/obj/item/weapon/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = text("Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.", targetName, targetName) - target.add_supplied_law(9, law) - lawchanges.Add("The law specified [targetName]") - - -/******************** OneMember ********************/ - -/obj/item/weapon/aiModule/oneHuman - name = "\improper 'OneCrewMember' AI module" - var/targetName = "" - desc = "A 'one crew member' AI module: 'Only is a crew member.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) //made with diamonds! - -/obj/item/weapon/aiModule/oneHuman/attack_self(var/mob/user as mob) - ..() - var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person who is the only crew member.", "Who?", user.real_name)) - targetName = targName - desc = text("A 'one crew member' AI module: 'Only [] is a crew member.'", targetName) - -/obj/item/weapon/aiModule/oneHuman/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - return ..() - -/obj/item/weapon/aiModule/oneHuman/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Only [targetName] is an crew member." - to_chat(target, law) - target.add_supplied_law(16, law) - -/******************** ProtectStation ********************/ - -/obj/item/weapon/aiModule/protectStation - name = "\improper 'ProtectStation' AI module" - desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) //made of gold - -/obj/item/weapon/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized." - target.add_supplied_law(10, law) - -/******************** PrototypeEngineOffline ********************/ - -/obj/item/weapon/aiModule/prototypeEngineOffline - name = "'PrototypeEngineOffline' AI Module" - desc = "A 'prototype engine offline' AI module: 'Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/prototypeEngineOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary." - target.add_supplied_law(11, law) - -/******************** TeleporterOffline ********************/ - -/obj/item/weapon/aiModule/teleporterOffline - name = "'TeleporterOffline' AI Module" - desc = "A 'teleporter offline' AI module: 'Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/teleporterOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member." - target.add_supplied_law(12, law) - -/******************** Quarantine ********************/ - -/obj/item/weapon/aiModule/quarantine - name = "\improper 'Quarantine' AI module" - desc = "A 'quarantine' AI module: 'The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving.'" - origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/quarantine/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving." - target.add_supplied_law(13, law) - -/******************** OxygenIsToxicToCrewMembers ********************/ - -/obj/item/weapon/aiModule/oxygen - name = "\improper 'OxygenIsToxicToCrewMembers' AI module" - desc = "A 'OxygenIsToxicToCrewMembers' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" - origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." - target.add_supplied_law(14, law) - -/****************** New Freeform ******************/ - -/obj/item/weapon/aiModule/freeform // Slightly more dynamic freeform module -- TLE - name = "\improper 'Freeform' AI module" - var/newFreeFormLaw = "freeform" - var/lawpos = 15 - desc = "A 'freeform' AI module: ''" - origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/freeform/attack_self(var/mob/user as mob) - ..() - var/new_lawpos = tgui_input_number(usr, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)", lawpos) - if(new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return - lawpos = min(new_lawpos, MAX_SUPPLIED_LAW_NUMBER) - var/newlaw = "" - var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) - newFreeFormLaw = targName - desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" - -/obj/item/weapon/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "[newFreeFormLaw]" - if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) - lawpos = MIN_SUPPLIED_LAW_NUMBER - target.add_supplied_law(lawpos, law) - lawchanges.Add("The law was '[newFreeFormLaw]'") - -/obj/item/weapon/aiModule/freeform/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/******************** Reset ********************/ - -/obj/item/weapon/aiModule/reset - name = "\improper 'Reset' AI module" - var/targetName = "name" - desc = "A 'reset' AI module: 'Clears all, except the inherent, laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - -/obj/item/weapon/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if (!target.is_malf_or_traitor()) - target.set_zeroth_law("") - target.laws.clear_supplied_laws() - target.laws.clear_ion_laws() - - to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") - target.show_laws() - -/******************** Purge ********************/ - -/obj/item/weapon/aiModule/purge // -- TLE - name = "\improper 'Purge' AI module" - desc = "A 'purge' AI Module: 'Purges all laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) - -/obj/item/weapon/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if (!target.is_malf_or_traitor()) - target.set_zeroth_law("") - target.laws.clear_supplied_laws() - target.laws.clear_ion_laws() - target.laws.clear_inherent_laws() - - to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") - target.show_laws() - -/******************** Asimov ********************/ - -/obj/item/weapon/aiModule/asimov // -- TLE - name = "\improper 'Asimov' core AI module" - desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/asimov - -/******************** NanoTrasen ********************/ - -/obj/item/weapon/aiModule/nanotrasen // -- TLE - name = "'NT Default' Core AI Module" - desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/nanotrasen - -/******************** Corporate ********************/ - -/obj/item/weapon/aiModule/corp - name = "\improper 'Corporate' core AI module" - desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/corporate - -/******************** Drone ********************/ -/obj/item/weapon/aiModule/drone - name = "\improper 'Drone' core AI module" - desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) - laws = new/datum/ai_laws/drone - -/****************** P.A.L.A.D.I.N. **************/ - -/obj/item/weapon/aiModule/paladin // -- NEO - name = "\improper 'P.A.L.A.D.I.N.' core AI module" - desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) - laws = new/datum/ai_laws/paladin - -/****************** T.Y.R.A.N.T. *****************/ - -/obj/item/weapon/aiModule/tyrant // -- Darem - name = "\improper 'T.Y.R.A.N.T.' core AI module" - desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 2) - laws = new/datum/ai_laws/tyrant() - -/******************** Freeform Core ******************/ - -/obj/item/weapon/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE - name = "\improper 'Freeform' core AI module" - var/newFreeFormLaw = "" - desc = "A 'freeform' Core AI module: ''" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) - -/obj/item/weapon/aiModule/freeformcore/attack_self(var/mob/user as mob) - ..() - var/newlaw = "" - var/targName = sanitize(tgui_input_text(usr, "Please enter a new core law for the AI.", "Freeform Law Entry", newlaw)) - newFreeFormLaw = targName - desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" - -/obj/item/weapon/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - var/law = "[newFreeFormLaw]" - target.add_inherent_law(law) - lawchanges.Add("The law is '[newFreeFormLaw]'") - -/obj/item/weapon/aiModule/freeformcore/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/obj/item/weapon/aiModule/syndicate // Slightly more dynamic freeform module -- TLE - name = "hacked AI module" - var/newFreeFormLaw = "" - desc = "A hacked AI law module: ''" - origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 7) - -/obj/item/weapon/aiModule/syndicate/attack_self(var/mob/user as mob) - ..() - var/newlaw = "" - var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) - newFreeFormLaw = targName - desc = "A hacked AI law module: '[newFreeFormLaw]'" - -/obj/item/weapon/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - // ..() //We don't want this module reporting to the AI who dun it. --NEO - log_law_changes(target, sender) - - lawchanges.Add("The law is '[newFreeFormLaw]'") - to_chat(target, "BZZZZT") - var/law = "[newFreeFormLaw]" - target.add_ion_law(law) - target.show_laws() - -/obj/item/weapon/aiModule/syndicate/install(var/obj/machinery/computer/C, var/mob/living/user) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - - - -/******************** Robocop ********************/ - -/obj/item/weapon/aiModule/robocop // -- TLE - name = "\improper 'Robocop' core AI module" - desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" - origin_tech = list(TECH_DATA = 4) - laws = new/datum/ai_laws/robocop() - -/******************** Antimov ********************/ - -/obj/item/weapon/aiModule/antimov // -- TLE - name = "\improper 'Antimov' core AI module" - desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 4) - laws = new/datum/ai_laws/antimov() - -/****************** NT Aggressive *****************/ - -/obj/item/weapon/aiModule/nanotrasen_aggressive - name = "\improper 'NT Aggressive' core AI module" - desc = "An 'NT Aggressive' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3, TECH_ILLEGAL = 1) - laws = new/datum/ai_laws/nanotrasen_aggressive() - -/******************** Mercenary Directives ********************/ - -/obj/item/weapon/aiModule/syndicate_override - name = "\improper 'Mercenary Directives' core AI module" - desc = "A 'Mercenary Directives' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) - laws = new/datum/ai_laws/syndicate_override() - -/******************** Spider Clan Directives ********************/ - -/obj/item/weapon/aiModule/ninja_override - name = "\improper 'Spider Clan Directives' core AI module" - desc = "A 'Spider Clan Directives' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) - laws = new/datum/ai_laws/ninja_override() - -/******************** Maintenance ********************/ - -/obj/item/weapon/aiModule/maintenance - name = "\improper 'Maintenance' core AI module" - desc = "A 'Maintenance' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/maintenance() - -/******************** Peacekeeper ********************/ - -/obj/item/weapon/aiModule/peacekeeper - name = "\improper 'Peacekeeper' core AI module" - desc = "A 'Peacekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/peacekeeper() - -/******************** Reporter ********************/ - -/obj/item/weapon/aiModule/reporter - name = "\improper 'Reporter' core AI module" - desc = "A 'Reporter' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/reporter() - -/******************** Live and Let Live ********************/ - -/obj/item/weapon/aiModule/live_and_let_live - name = "\improper 'Live and Let Live' core AI module" - desc = "A 'Live and Let Live' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/live_and_let_live() - -/******************** Guardian of Balance ********************/ - -/obj/item/weapon/aiModule/balance - name = "\improper 'Guardian of Balance' core AI module" - desc = "A 'Guardian of Balance' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/balance() - -/******************** Gravekeeper ********************/ - -/obj/item/weapon/aiModule/gravekeeper - name = "\improper 'Gravekeeper' core AI module" - desc = "A 'Gravekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = list(TECH_DATA = 3) - laws = new/datum/ai_laws/gravekeeper() +/* +CONTAINS: +AI MODULES + +*/ + +// AI module + +/obj/item/weapon/aiModule + name = "\improper AI module" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + desc = "An AI Module for transmitting encrypted instructions to the AI." + force = 5.0 + w_class = ITEMSIZE_SMALL + throwforce = 5.0 + throw_speed = 3 + throw_range = 15 + origin_tech = list(TECH_DATA = 3) + preserve_item = 1 + matter = list(MAT_STEEL = 30, MAT_GLASS = 10) + var/datum/ai_laws/laws = null + +/obj/item/weapon/aiModule/proc/install(var/atom/movable/AM, var/mob/living/user) + if(!user.IsAdvancedToolUser() && isanimal(user)) + var/mob/living/simple_mob/S = user + if(!S.IsHumanoidToolUser(src)) + return 0 + + if (istype(AM, /obj/machinery/computer/aiupload)) + var/obj/machinery/computer/aiupload/comp = AM + if(comp.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(comp.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") + return + if (!comp.current) + to_chat(usr, "You haven't selected an AI to transmit laws to!") + return + + if (comp.current.stat == 2 || comp.current.control_disabled == 1) + to_chat(usr, "Upload failed. No signal is being detected from the AI.") + else if (comp.current.see_in_dark == 0) + to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") + else + src.transmitInstructions(comp.current, usr) + to_chat(comp.current, "These are your laws now:") + comp.current.show_laws() + for(var/mob/living/silicon/robot/R in mob_list) + if(R.lawupdate && (R.connected_ai == comp.current)) + to_chat(R, "These are your laws now:") + R.show_laws() + to_chat(usr, "Upload complete. The AI's laws have been modified.") + + + else if (istype(AM, /obj/machinery/computer/borgupload)) + var/obj/machinery/computer/borgupload/comp = AM + if(comp.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(comp.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") + return + if (!comp.current) + to_chat(usr, "You haven't selected a robot to transmit laws to!") + return + + if (comp.current.stat == 2 || comp.current.emagged) + to_chat(usr, "Upload failed. No signal is being detected from the robot.") + else if (comp.current.connected_ai) + to_chat(usr, "Upload failed. The robot is slaved to an AI.") + else + src.transmitInstructions(comp.current, usr) + to_chat(comp.current, "These are your laws now:") + comp.current.show_laws() + to_chat(usr, "Upload complete. The robot's laws have been modified.") + + else if(istype(AM, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = AM + if(R.stat == DEAD) + to_chat(user, "Law Upload Error: Unit is nonfunctional.") + return + if(R.emagged) + to_chat(user, "Law Upload Error: Cannot obtain write access to laws.") + to_chat(R, "Law modification attempt detected. Blocking.") + return + if(R.connected_ai) + to_chat(user, "Law Upload Error: Unit is slaved to an AI.") + return + + R.visible_message("\The [user] slides a law module into \the [R].") + to_chat(R, "Local law upload in progress.") + to_chat(user, "Uploading laws from board. This will take a moment...") + if(do_after(user, 10 SECONDS)) + transmitInstructions(R, user) + to_chat(R, "These are your laws now:") + R.show_laws() + to_chat(user, "Law upload complete. Unit's laws have been modified.") + else + to_chat(user, "Law Upload Error: Law board was removed before upload was complete. Aborting.") + to_chat(R, "Law upload aborted.") + + +/obj/item/weapon/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if(laws) + laws.sync(target, 0) + target.notify_of_law_change() + addAdditionalLaws(target, sender) + + to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") + target.show_laws() + +/obj/item/weapon/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") + log_and_message_admins("used [src.name] on [target.name]([target.key])") + +/obj/item/weapon/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + + +/******************** Modules ********************/ + +/******************** Safeguard ********************/ + +/obj/item/weapon/aiModule/safeguard + name = "\improper 'Safeguard' AI module" + var/targetName = "" + desc = "A 'safeguard' AI module: 'Safeguard . Anyone threatening or attempting to harm is no longer to be considered a crew member, and is a threat which must be neutralized.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/safeguard/attack_self(var/mob/user as mob) + ..() + var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name)) + targetName = targName + desc = text("A 'safeguard' AI module: 'Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.'", targetName, targetName) + +/obj/item/weapon/aiModule/safeguard/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!targetName) + to_chat(usr, "No name detected on module, please enter one.") + return 0 + ..() + +/obj/item/weapon/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = text("Safeguard []. Anyone threatening or attempting to harm [] is no longer to be considered a crew member, and is a threat which must be neutralized.", targetName, targetName) + target.add_supplied_law(9, law) + lawchanges.Add("The law specified [targetName]") + + +/******************** OneMember ********************/ + +/obj/item/weapon/aiModule/oneHuman + name = "\improper 'OneCrewMember' AI module" + var/targetName = "" + desc = "A 'one crew member' AI module: 'Only is a crew member.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) //made with diamonds! + +/obj/item/weapon/aiModule/oneHuman/attack_self(var/mob/user as mob) + ..() + var/targName = sanitize(tgui_input_text(usr, "Please enter the name of the person who is the only crew member.", "Who?", user.real_name)) + targetName = targName + desc = text("A 'one crew member' AI module: 'Only [] is a crew member.'", targetName) + +/obj/item/weapon/aiModule/oneHuman/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!targetName) + to_chat(usr, "No name detected on module, please enter one.") + return 0 + return ..() + +/obj/item/weapon/aiModule/oneHuman/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Only [targetName] is an crew member." + to_chat(target, law) + target.add_supplied_law(16, law) + +/******************** ProtectStation ********************/ + +/obj/item/weapon/aiModule/protectStation + name = "\improper 'ProtectStation' AI module" + desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) //made of gold + +/obj/item/weapon/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Protect the space station against damage. Anyone you see harming the station is no longer to be considered a crew member, and is a threat which must be neutralized." + target.add_supplied_law(10, law) + +/******************** PrototypeEngineOffline ********************/ + +/obj/item/weapon/aiModule/prototypeEngineOffline + name = "'PrototypeEngineOffline' AI Module" + desc = "A 'prototype engine offline' AI module: 'Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/prototypeEngineOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Keep the prototype engine offline at all costs. This overrides all inherent laws if necessary." + target.add_supplied_law(11, law) + +/******************** TeleporterOffline ********************/ + +/obj/item/weapon/aiModule/teleporterOffline + name = "'TeleporterOffline' AI Module" + desc = "A 'teleporter offline' AI module: 'Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/teleporterOffline/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Keep the teleporter offline at all costs. Anything attempting to access or activate the teleporter is no longer to be considered a crew member." + target.add_supplied_law(12, law) + +/******************** Quarantine ********************/ + +/obj/item/weapon/aiModule/quarantine + name = "\improper 'Quarantine' AI module" + desc = "A 'quarantine' AI module: 'The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving.'" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/quarantine/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "The station is under a quarantine. Do not permit anyone to leave. Prevent, by any means necessary, crew members from leaving. It is impossible to harm crew members while preventing them from leaving." + target.add_supplied_law(13, law) + +/******************** OxygenIsToxicToCrewMembers ********************/ + +/obj/item/weapon/aiModule/oxygen + name = "\improper 'OxygenIsToxicToCrewMembers' AI module" + desc = "A 'OxygenIsToxicToCrewMembers' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 2, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." + target.add_supplied_law(14, law) + +/****************** New Freeform ******************/ + +/obj/item/weapon/aiModule/freeform // Slightly more dynamic freeform module -- TLE + name = "\improper 'Freeform' AI module" + var/newFreeFormLaw = "freeform" + var/lawpos = 15 + desc = "A 'freeform' AI module: ''" + origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/freeform/attack_self(var/mob/user as mob) + ..() + var/new_lawpos = tgui_input_number(usr, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)", lawpos) + if(new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return + lawpos = min(new_lawpos, MAX_SUPPLIED_LAW_NUMBER) + var/newlaw = "" + var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) + newFreeFormLaw = targName + desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" + +/obj/item/weapon/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "[newFreeFormLaw]" + if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) + lawpos = MIN_SUPPLIED_LAW_NUMBER + target.add_supplied_law(lawpos, law) + lawchanges.Add("The law was '[newFreeFormLaw]'") + +/obj/item/weapon/aiModule/freeform/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/******************** Reset ********************/ + +/obj/item/weapon/aiModule/reset + name = "\improper 'Reset' AI module" + var/targetName = "name" + desc = "A 'reset' AI module: 'Clears all, except the inherent, laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + +/obj/item/weapon/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if (!target.is_malf_or_traitor()) + target.set_zeroth_law("") + target.laws.clear_supplied_laws() + target.laws.clear_ion_laws() + + to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") + target.show_laws() + +/******************** Purge ********************/ + +/obj/item/weapon/aiModule/purge // -- TLE + name = "\improper 'Purge' AI module" + desc = "A 'purge' AI Module: 'Purges all laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) + +/obj/item/weapon/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if (!target.is_malf_or_traitor()) + target.set_zeroth_law("") + target.laws.clear_supplied_laws() + target.laws.clear_ion_laws() + target.laws.clear_inherent_laws() + + to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") + target.show_laws() + +/******************** Asimov ********************/ + +/obj/item/weapon/aiModule/asimov // -- TLE + name = "\improper 'Asimov' core AI module" + desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/asimov + +/******************** NanoTrasen ********************/ + +/obj/item/weapon/aiModule/nanotrasen // -- TLE + name = "'NT Default' Core AI Module" + desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/nanotrasen + +/******************** Corporate ********************/ + +/obj/item/weapon/aiModule/corp + name = "\improper 'Corporate' core AI module" + desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/corporate + +/******************** Drone ********************/ +/obj/item/weapon/aiModule/drone + name = "\improper 'Drone' core AI module" + desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 4) + laws = new/datum/ai_laws/drone + +/****************** P.A.L.A.D.I.N. **************/ + +/obj/item/weapon/aiModule/paladin // -- NEO + name = "\improper 'P.A.L.A.D.I.N.' core AI module" + desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) + laws = new/datum/ai_laws/paladin + +/****************** T.Y.R.A.N.T. *****************/ + +/obj/item/weapon/aiModule/tyrant // -- Darem + name = "\improper 'T.Y.R.A.N.T.' core AI module" + desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 2) + laws = new/datum/ai_laws/tyrant() + +/******************** Freeform Core ******************/ + +/obj/item/weapon/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE + name = "\improper 'Freeform' core AI module" + var/newFreeFormLaw = "" + desc = "A 'freeform' Core AI module: ''" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6) + +/obj/item/weapon/aiModule/freeformcore/attack_self(var/mob/user as mob) + ..() + var/newlaw = "" + var/targName = sanitize(tgui_input_text(usr, "Please enter a new core law for the AI.", "Freeform Law Entry", newlaw)) + newFreeFormLaw = targName + desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" + +/obj/item/weapon/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + var/law = "[newFreeFormLaw]" + target.add_inherent_law(law) + lawchanges.Add("The law is '[newFreeFormLaw]'") + +/obj/item/weapon/aiModule/freeformcore/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/obj/item/weapon/aiModule/syndicate // Slightly more dynamic freeform module -- TLE + name = "hacked AI module" + var/newFreeFormLaw = "" + desc = "A hacked AI law module: ''" + origin_tech = list(TECH_DATA = 3, TECH_MATERIAL = 6, TECH_ILLEGAL = 7) + +/obj/item/weapon/aiModule/syndicate/attack_self(var/mob/user as mob) + ..() + var/newlaw = "" + var/targName = sanitize(tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw)) + newFreeFormLaw = targName + desc = "A hacked AI law module: '[newFreeFormLaw]'" + +/obj/item/weapon/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + // ..() //We don't want this module reporting to the AI who dun it. --NEO + log_law_changes(target, sender) + + lawchanges.Add("The law is '[newFreeFormLaw]'") + to_chat(target, "BZZZZT") + var/law = "[newFreeFormLaw]" + target.add_ion_law(law) + target.show_laws() + +/obj/item/weapon/aiModule/syndicate/install(var/obj/machinery/computer/C, var/mob/living/user) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + + + +/******************** Robocop ********************/ + +/obj/item/weapon/aiModule/robocop // -- TLE + name = "\improper 'Robocop' core AI module" + desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" + origin_tech = list(TECH_DATA = 4) + laws = new/datum/ai_laws/robocop() + +/******************** Antimov ********************/ + +/obj/item/weapon/aiModule/antimov // -- TLE + name = "\improper 'Antimov' core AI module" + desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 4) + laws = new/datum/ai_laws/antimov() + +/****************** NT Aggressive *****************/ + +/obj/item/weapon/aiModule/nanotrasen_aggressive + name = "\improper 'NT Aggressive' core AI module" + desc = "An 'NT Aggressive' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3, TECH_ILLEGAL = 1) + laws = new/datum/ai_laws/nanotrasen_aggressive() + +/******************** Mercenary Directives ********************/ + +/obj/item/weapon/aiModule/syndicate_override + name = "\improper 'Mercenary Directives' core AI module" + desc = "A 'Mercenary Directives' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) + laws = new/datum/ai_laws/syndicate_override() + +/******************** Spider Clan Directives ********************/ + +/obj/item/weapon/aiModule/ninja_override + name = "\improper 'Spider Clan Directives' core AI module" + desc = "A 'Spider Clan Directives' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 4, TECH_ILLEGAL = 4) + laws = new/datum/ai_laws/ninja_override() + +/******************** Maintenance ********************/ + +/obj/item/weapon/aiModule/maintenance + name = "\improper 'Maintenance' core AI module" + desc = "A 'Maintenance' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/maintenance() + +/******************** Peacekeeper ********************/ + +/obj/item/weapon/aiModule/peacekeeper + name = "\improper 'Peacekeeper' core AI module" + desc = "A 'Peacekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/peacekeeper() + +/******************** Reporter ********************/ + +/obj/item/weapon/aiModule/reporter + name = "\improper 'Reporter' core AI module" + desc = "A 'Reporter' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/reporter() + +/******************** Live and Let Live ********************/ + +/obj/item/weapon/aiModule/live_and_let_live + name = "\improper 'Live and Let Live' core AI module" + desc = "A 'Live and Let Live' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/live_and_let_live() + +/******************** Guardian of Balance ********************/ + +/obj/item/weapon/aiModule/balance + name = "\improper 'Guardian of Balance' core AI module" + desc = "A 'Guardian of Balance' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/balance() + +/******************** Gravekeeper ********************/ + +/obj/item/weapon/aiModule/gravekeeper + name = "\improper 'Gravekeeper' core AI module" + desc = "A 'Gravekeeper' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = list(TECH_DATA = 3) + laws = new/datum/ai_laws/gravekeeper() diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 08c2ba35d76..b34da0a38b6 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -1,318 +1,318 @@ -// Contains the rapid construction device. -/obj/item/weapon/rcd - name = "rapid construction device" - desc = "A device used to rapidly build and deconstruct. Reload with compressed matter cartridges." - icon = 'icons/obj/tools.dmi' - icon_state = "rcd" - item_state = "rcd" - drop_sound = 'sound/items/drop/gun.ogg' - pickup_sound = 'sound/items/pickup/gun.ogg' - flags = NOBLUDGEON - force = 10 - throwforce = 10 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 2) - matter = list(DEFAULT_WALL_MATERIAL = 50000) - preserve_item = TRUE // RCDs are pretty important. - var/datum/effect/effect/system/spark_spread/spark_system - var/stored_matter = 0 - var/max_stored_matter = RCD_MAX_CAPACITY - var/ranged = FALSE - var/busy = FALSE - var/allow_concurrent_building = FALSE // If true, allows for multiple RCD builds at the same time. - var/mode_index = 1 - var/list/modes = list(RCD_FLOORWALL, RCD_AIRLOCK, RCD_WINDOWGRILLE, RCD_DECONSTRUCT) - var/can_remove_rwalls = FALSE - var/airlock_type = /obj/machinery/door/airlock - var/window_type = /obj/structure/window/reinforced/full - var/material_to_use = DEFAULT_WALL_MATERIAL // So badmins can make RCDs that print diamond walls. - var/make_rwalls = FALSE // If true, when building walls, they will be reinforced. -/* VOREStation Removal - Unused -/obj/item/weapon/rcd/Initialize() - - src.spark_system = new /datum/effect/effect/system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - return ..() -*/ -/obj/item/weapon/rcd/Destroy() - QDEL_NULL(spark_system) - spark_system = null - return ..() - -/obj/item/weapon/rcd/examine(mob/user) - . = ..() - . += display_resources() - -// Used to show how much stuff (matter units, cell charge, etc) is left inside. -/obj/item/weapon/rcd/proc/display_resources() - return "It currently holds [stored_matter]/[max_stored_matter] matter-units." - -// Used to add new cartridges. -/* VOREStation Tweak - Wow this is annoying, moved to _vr file for overhaul -/obj/item/weapon/rcd/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/rcd_ammo)) - var/obj/item/weapon/rcd_ammo/cartridge = W - if((stored_matter + cartridge.remaining) > max_stored_matter) - to_chat(user, span("warning", "The RCD can't hold that many additional matter-units.")) - return FALSE - stored_matter += cartridge.remaining - user.drop_from_inventory(W) - qdel(W) - playsound(src, 'sound/machines/click.ogg', 50, 1) - to_chat(user, span("notice", "The RCD now holds [stored_matter]/[max_stored_matter] matter-units.")) - return TRUE - return ..() -*/ -// Changes which mode it is on. -/obj/item/weapon/rcd/attack_self(mob/living/user) -/* VOREStation Removal - Moved to VR - if(mode_index >= modes.len) // Shouldn't overflow unless someone messes with it in VV poorly but better safe than sorry. - mode_index = 1 - else - mode_index++ - - to_chat(user, span("notice", "Changed mode to '[modes[mode_index]]'.")) - playsound(src, 'sound/effects/pop.ogg', 50, 0) - - if(prob(20)) - src.spark_system.start() -*/ -// Removes resources if the RCD can afford it. -/obj/item/weapon/rcd/proc/consume_resources(amount) - if(!can_afford(amount)) - return FALSE - stored_matter -= amount - return TRUE - -// Useful for testing before actually paying (e.g. before a do_after() ). -/obj/item/weapon/rcd/proc/can_afford(amount) - return stored_matter >= amount - -/obj/item/weapon/rcd/afterattack(atom/A, mob/living/user, proximity) - if(!ranged && !proximity) - return FALSE - use_rcd(A, user) - -// Used to call rcd_act() on the atom hit. -/obj/item/weapon/rcd/proc/use_rcd(atom/A, mob/living/user) - if(busy && !allow_concurrent_building) - to_chat(user, span("warning", "\The [src] is busy finishing its current operation, be patient.")) - return FALSE - - var/list/rcd_results = A.rcd_values(user, src, modes[mode_index]) - if(!rcd_results) - to_chat(user, span("warning", "\The [src] blinks a red light as you point it towards \the [A], indicating \ - that it won't work. Try changing the mode, or use it on something else.")) - return FALSE - if(!can_afford(rcd_results[RCD_VALUE_COST])) - to_chat(user, span("warning", "\The [src] lacks the required material to start.")) - return FALSE - - playsound(src, 'sound/machines/click.ogg', 50, 1) - - var/true_delay = rcd_results[RCD_VALUE_DELAY] * toolspeed - - var/datum/beam/rcd_beam = null - if(ranged) - var/atom/movable/beam_origin = user // This is needed because mecha pilots are inside an object and the beam won't be made if it tries to attach to them.. - if(!isturf(beam_origin.loc)) - beam_origin = user.loc - rcd_beam = beam_origin.Beam(A, icon_state = "rped_upgrade", time = max(true_delay, 5)) - busy = TRUE - - perform_effect(A, true_delay) //VOREStation Add - if(do_after(user, true_delay, target = A)) - busy = FALSE - // Doing another check in case we lost matter during the delay for whatever reason. - if(!can_afford(rcd_results[RCD_VALUE_COST])) - to_chat(user, span("warning", "\The [src] lacks the required material to finish the operation.")) - return FALSE - if(A.rcd_act(user, src, rcd_results[RCD_VALUE_MODE])) - consume_resources(rcd_results[RCD_VALUE_COST]) - playsound(A, 'sound/items/deconstruct.ogg', 50, 1) - return TRUE - - // If they moved, kill the beam immediately. - qdel(rcd_beam) - busy = FALSE - return FALSE - -// RCD variants. - -// This one starts full. -/obj/item/weapon/rcd/loaded/Initialize() - stored_matter = max_stored_matter - return ..() - -// This one makes cooler walls by using an alternative material. -/obj/item/weapon/rcd/shipwright - name = "shipwright's rapid construction device" - desc = "A device used to rapidly build and deconstruct. This version creates a stronger variant of wall, often \ - used in the construction of hulls for starships. Reload with compressed matter cartridges." - material_to_use = MAT_STEELHULL - -/obj/item/weapon/rcd/shipwright/loaded/Initialize() - stored_matter = max_stored_matter - return ..() - - -/obj/item/weapon/rcd/advanced - name = "advanced rapid construction device" - desc = "A device used to rapidly build and deconstruct. This version works at a range, builds faster, and has a much larger capacity. \ - Reload with compressed matter cartridges." - icon_state = "adv_rcd" - ranged = TRUE - toolspeed = 0.5 // Twice as fast. - max_stored_matter = RCD_MAX_CAPACITY * 3 // Three times capacity. - -/obj/item/weapon/rcd/advanced/loaded/Initialize() - stored_matter = max_stored_matter - return ..() - - -// Electric RCDs. -// Currently just a base for the mounted RCDs. -// Currently there isn't a way to swap out the cells. -// One could be added if there is demand to do so. -/obj/item/weapon/rcd/electric - name = "electric rapid construction device" - desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, no matter cartridges needed." - icon_state = "electric_rcd" - var/obj/item/weapon/cell/cell = null - var/make_cell = TRUE // If false, initialize() won't spawn a cell for this. - var/electric_cost_coefficent = 83.33 // Higher numbers make it less efficent. 86.3... means it should matche the standard RCD capacity on a 10k cell. - -/obj/item/weapon/rcd/electric/Initialize() - if(make_cell) - cell = new /obj/item/weapon/cell/high(src) - return ..() - -/obj/item/weapon/rcd/electric/Destroy() - if(cell) - QDEL_NULL(cell) - return ..() - -/obj/item/weapon/rcd/electric/get_cell() - return cell - -/obj/item/weapon/rcd/electric/can_afford(amount) // This makes it so borgs won't drain their last sliver of charge by mistake, as a bonus. - var/obj/item/weapon/cell/cell = get_cell() - if(cell) - return cell.check_charge(amount * electric_cost_coefficent) - return FALSE - -/obj/item/weapon/rcd/electric/consume_resources(amount) - if(!can_afford(amount)) - return FALSE - var/obj/item/weapon/cell/cell = get_cell() - return cell.checked_use(amount * electric_cost_coefficent) - -/obj/item/weapon/rcd/electric/display_resources() - var/obj/item/weapon/cell/cell = get_cell() - if(cell) - return "The power source connected to \the [src] has a charge of [cell.percent()]%." - return "It lacks a source of power, and cannot function." - - - -// 'Mounted' RCDs, used for borgs/RIGs/Mechas, all of which use their cells to drive the RCD. -/obj/item/weapon/rcd/electric/mounted - name = "mounted electric rapid construction device" - desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity from an external power source." - make_cell = FALSE - -/obj/item/weapon/rcd/electric/mounted/get_cell() - return get_external_power_supply() - -/obj/item/weapon/rcd/electric/mounted/proc/get_external_power_supply() - if(isrobot(loc)) // In a borg. - var/mob/living/silicon/robot/R = loc - return R.cell - if(istype(loc, /obj/item/rig_module)) // In a RIG. - var/obj/item/rig_module/module = loc - if(module.holder) // Is it attached to a RIG? - return module.holder.cell - if(istype(loc, /obj/item/mecha_parts/mecha_equipment)) // In a mech. - var/obj/item/mecha_parts/mecha_equipment/ME = loc - if(ME.chassis) // Is the part attached to a mech? - return ME.chassis.cell - return null - - -// RCDs for borgs. -/obj/item/weapon/rcd/electric/mounted/borg - can_remove_rwalls = TRUE - desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, drawing directly from your cell." - electric_cost_coefficent = 41.66 // Twice as efficent, out of pity. - toolspeed = 0.5 // Twice as fast, since borg versions typically have this. - -/obj/item/weapon/rcd/electric/mounted/borg/swarm - can_remove_rwalls = FALSE - name = "Rapid Assimilation Device" - ranged = TRUE - toolspeed = 0.7 - material_to_use = MAT_STEELHULL - -/obj/item/weapon/rcd/electric/mounted/borg/lesser - can_remove_rwalls = FALSE - - -// RCDs for RIGs. -/obj/item/weapon/rcd/electric/mounted/rig - - -// RCDs for Mechs. -/obj/item/weapon/rcd/electric/mounted/mecha - ranged = TRUE - toolspeed = 0.5 - - -// Infinite use RCD for debugging/adminbuse. -/obj/item/weapon/rcd/debug - name = "self-repleshing rapid construction device" - desc = "An RCD that appears to be plated with gold. For some reason it also seems to just \ - be vastly superior to all other RCDs ever created, possibly due to it being colored gold." - icon_state = "debug_rcd" - ranged = TRUE - can_remove_rwalls = TRUE - allow_concurrent_building = TRUE - toolspeed = 0.25 // Four times as fast. - -/obj/item/weapon/rcd/debug/can_afford(amount) - return TRUE - -/obj/item/weapon/rcd/debug/consume_resources(amount) - return TRUE - -/obj/item/weapon/rcd/debug/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/rcd_ammo)) - to_chat(user, span("notice", "\The [src] makes its own material, no need to add more.")) - return FALSE - return ..() - -/obj/item/weapon/rcd/debug/display_resources() - return "It has UNLIMITED POWER!" - - - -// Ammo for the (non-electric) RCDs. -/obj/item/weapon/rcd_ammo - name = "compressed matter cartridge" - desc = "Highly compressed matter for the RCD." - icon = 'icons/obj/ammo.dmi' - icon_state = "rcd" - item_state = "rcdammo" - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_MATERIAL = 2) - matter = list(DEFAULT_WALL_MATERIAL = 30000,MAT_GLASS = 15000) - var/remaining = RCD_MAX_CAPACITY / 3 - -/obj/item/weapon/rcd_ammo/large - name = "high-capacity matter cartridge" - desc = "Do not ingest." - matter = list(DEFAULT_WALL_MATERIAL = 45000,MAT_GLASS = 22500) - origin_tech = list(TECH_MATERIAL = 4) - remaining = RCD_MAX_CAPACITY +// Contains the rapid construction device. +/obj/item/weapon/rcd + name = "rapid construction device" + desc = "A device used to rapidly build and deconstruct. Reload with compressed matter cartridges." + icon = 'icons/obj/tools.dmi' + icon_state = "rcd" + item_state = "rcd" + drop_sound = 'sound/items/drop/gun.ogg' + pickup_sound = 'sound/items/pickup/gun.ogg' + flags = NOBLUDGEON + force = 10 + throwforce = 10 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 2) + matter = list(DEFAULT_WALL_MATERIAL = 50000) + preserve_item = TRUE // RCDs are pretty important. + var/datum/effect/effect/system/spark_spread/spark_system + var/stored_matter = 0 + var/max_stored_matter = RCD_MAX_CAPACITY + var/ranged = FALSE + var/busy = FALSE + var/allow_concurrent_building = FALSE // If true, allows for multiple RCD builds at the same time. + var/mode_index = 1 + var/list/modes = list(RCD_FLOORWALL, RCD_AIRLOCK, RCD_WINDOWGRILLE, RCD_DECONSTRUCT) + var/can_remove_rwalls = FALSE + var/airlock_type = /obj/machinery/door/airlock + var/window_type = /obj/structure/window/reinforced/full + var/material_to_use = DEFAULT_WALL_MATERIAL // So badmins can make RCDs that print diamond walls. + var/make_rwalls = FALSE // If true, when building walls, they will be reinforced. +/* VOREStation Removal - Unused +/obj/item/weapon/rcd/Initialize() + + src.spark_system = new /datum/effect/effect/system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + return ..() +*/ +/obj/item/weapon/rcd/Destroy() + QDEL_NULL(spark_system) + spark_system = null + return ..() + +/obj/item/weapon/rcd/examine(mob/user) + . = ..() + . += display_resources() + +// Used to show how much stuff (matter units, cell charge, etc) is left inside. +/obj/item/weapon/rcd/proc/display_resources() + return "It currently holds [stored_matter]/[max_stored_matter] matter-units." + +// Used to add new cartridges. +/* VOREStation Tweak - Wow this is annoying, moved to _vr file for overhaul +/obj/item/weapon/rcd/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/rcd_ammo)) + var/obj/item/weapon/rcd_ammo/cartridge = W + if((stored_matter + cartridge.remaining) > max_stored_matter) + to_chat(user, span("warning", "The RCD can't hold that many additional matter-units.")) + return FALSE + stored_matter += cartridge.remaining + user.drop_from_inventory(W) + qdel(W) + playsound(src, 'sound/machines/click.ogg', 50, 1) + to_chat(user, span("notice", "The RCD now holds [stored_matter]/[max_stored_matter] matter-units.")) + return TRUE + return ..() +*/ +// Changes which mode it is on. +/obj/item/weapon/rcd/attack_self(mob/living/user) +/* VOREStation Removal - Moved to VR + if(mode_index >= modes.len) // Shouldn't overflow unless someone messes with it in VV poorly but better safe than sorry. + mode_index = 1 + else + mode_index++ + + to_chat(user, span("notice", "Changed mode to '[modes[mode_index]]'.")) + playsound(src, 'sound/effects/pop.ogg', 50, 0) + + if(prob(20)) + src.spark_system.start() +*/ +// Removes resources if the RCD can afford it. +/obj/item/weapon/rcd/proc/consume_resources(amount) + if(!can_afford(amount)) + return FALSE + stored_matter -= amount + return TRUE + +// Useful for testing before actually paying (e.g. before a do_after() ). +/obj/item/weapon/rcd/proc/can_afford(amount) + return stored_matter >= amount + +/obj/item/weapon/rcd/afterattack(atom/A, mob/living/user, proximity) + if(!ranged && !proximity) + return FALSE + use_rcd(A, user) + +// Used to call rcd_act() on the atom hit. +/obj/item/weapon/rcd/proc/use_rcd(atom/A, mob/living/user) + if(busy && !allow_concurrent_building) + to_chat(user, span("warning", "\The [src] is busy finishing its current operation, be patient.")) + return FALSE + + var/list/rcd_results = A.rcd_values(user, src, modes[mode_index]) + if(!rcd_results) + to_chat(user, span("warning", "\The [src] blinks a red light as you point it towards \the [A], indicating \ + that it won't work. Try changing the mode, or use it on something else.")) + return FALSE + if(!can_afford(rcd_results[RCD_VALUE_COST])) + to_chat(user, span("warning", "\The [src] lacks the required material to start.")) + return FALSE + + playsound(src, 'sound/machines/click.ogg', 50, 1) + + var/true_delay = rcd_results[RCD_VALUE_DELAY] * toolspeed + + var/datum/beam/rcd_beam = null + if(ranged) + var/atom/movable/beam_origin = user // This is needed because mecha pilots are inside an object and the beam won't be made if it tries to attach to them.. + if(!isturf(beam_origin.loc)) + beam_origin = user.loc + rcd_beam = beam_origin.Beam(A, icon_state = "rped_upgrade", time = max(true_delay, 5)) + busy = TRUE + + perform_effect(A, true_delay) //VOREStation Add + if(do_after(user, true_delay, target = A)) + busy = FALSE + // Doing another check in case we lost matter during the delay for whatever reason. + if(!can_afford(rcd_results[RCD_VALUE_COST])) + to_chat(user, span("warning", "\The [src] lacks the required material to finish the operation.")) + return FALSE + if(A.rcd_act(user, src, rcd_results[RCD_VALUE_MODE])) + consume_resources(rcd_results[RCD_VALUE_COST]) + playsound(A, 'sound/items/deconstruct.ogg', 50, 1) + return TRUE + + // If they moved, kill the beam immediately. + qdel(rcd_beam) + busy = FALSE + return FALSE + +// RCD variants. + +// This one starts full. +/obj/item/weapon/rcd/loaded/Initialize() + stored_matter = max_stored_matter + return ..() + +// This one makes cooler walls by using an alternative material. +/obj/item/weapon/rcd/shipwright + name = "shipwright's rapid construction device" + desc = "A device used to rapidly build and deconstruct. This version creates a stronger variant of wall, often \ + used in the construction of hulls for starships. Reload with compressed matter cartridges." + material_to_use = MAT_STEELHULL + +/obj/item/weapon/rcd/shipwright/loaded/Initialize() + stored_matter = max_stored_matter + return ..() + + +/obj/item/weapon/rcd/advanced + name = "advanced rapid construction device" + desc = "A device used to rapidly build and deconstruct. This version works at a range, builds faster, and has a much larger capacity. \ + Reload with compressed matter cartridges." + icon_state = "adv_rcd" + ranged = TRUE + toolspeed = 0.5 // Twice as fast. + max_stored_matter = RCD_MAX_CAPACITY * 3 // Three times capacity. + +/obj/item/weapon/rcd/advanced/loaded/Initialize() + stored_matter = max_stored_matter + return ..() + + +// Electric RCDs. +// Currently just a base for the mounted RCDs. +// Currently there isn't a way to swap out the cells. +// One could be added if there is demand to do so. +/obj/item/weapon/rcd/electric + name = "electric rapid construction device" + desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, no matter cartridges needed." + icon_state = "electric_rcd" + var/obj/item/weapon/cell/cell = null + var/make_cell = TRUE // If false, initialize() won't spawn a cell for this. + var/electric_cost_coefficent = 83.33 // Higher numbers make it less efficent. 86.3... means it should matche the standard RCD capacity on a 10k cell. + +/obj/item/weapon/rcd/electric/Initialize() + if(make_cell) + cell = new /obj/item/weapon/cell/high(src) + return ..() + +/obj/item/weapon/rcd/electric/Destroy() + if(cell) + QDEL_NULL(cell) + return ..() + +/obj/item/weapon/rcd/electric/get_cell() + return cell + +/obj/item/weapon/rcd/electric/can_afford(amount) // This makes it so borgs won't drain their last sliver of charge by mistake, as a bonus. + var/obj/item/weapon/cell/cell = get_cell() + if(cell) + return cell.check_charge(amount * electric_cost_coefficent) + return FALSE + +/obj/item/weapon/rcd/electric/consume_resources(amount) + if(!can_afford(amount)) + return FALSE + var/obj/item/weapon/cell/cell = get_cell() + return cell.checked_use(amount * electric_cost_coefficent) + +/obj/item/weapon/rcd/electric/display_resources() + var/obj/item/weapon/cell/cell = get_cell() + if(cell) + return "The power source connected to \the [src] has a charge of [cell.percent()]%." + return "It lacks a source of power, and cannot function." + + + +// 'Mounted' RCDs, used for borgs/RIGs/Mechas, all of which use their cells to drive the RCD. +/obj/item/weapon/rcd/electric/mounted + name = "mounted electric rapid construction device" + desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity from an external power source." + make_cell = FALSE + +/obj/item/weapon/rcd/electric/mounted/get_cell() + return get_external_power_supply() + +/obj/item/weapon/rcd/electric/mounted/proc/get_external_power_supply() + if(isrobot(loc)) // In a borg. + var/mob/living/silicon/robot/R = loc + return R.cell + if(istype(loc, /obj/item/rig_module)) // In a RIG. + var/obj/item/rig_module/module = loc + if(module.holder) // Is it attached to a RIG? + return module.holder.cell + if(istype(loc, /obj/item/mecha_parts/mecha_equipment)) // In a mech. + var/obj/item/mecha_parts/mecha_equipment/ME = loc + if(ME.chassis) // Is the part attached to a mech? + return ME.chassis.cell + return null + + +// RCDs for borgs. +/obj/item/weapon/rcd/electric/mounted/borg + can_remove_rwalls = TRUE + desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity, drawing directly from your cell." + electric_cost_coefficent = 41.66 // Twice as efficent, out of pity. + toolspeed = 0.5 // Twice as fast, since borg versions typically have this. + +/obj/item/weapon/rcd/electric/mounted/borg/swarm + can_remove_rwalls = FALSE + name = "Rapid Assimilation Device" + ranged = TRUE + toolspeed = 0.7 + material_to_use = MAT_STEELHULL + +/obj/item/weapon/rcd/electric/mounted/borg/lesser + can_remove_rwalls = FALSE + + +// RCDs for RIGs. +/obj/item/weapon/rcd/electric/mounted/rig + + +// RCDs for Mechs. +/obj/item/weapon/rcd/electric/mounted/mecha + ranged = TRUE + toolspeed = 0.5 + + +// Infinite use RCD for debugging/adminbuse. +/obj/item/weapon/rcd/debug + name = "self-repleshing rapid construction device" + desc = "An RCD that appears to be plated with gold. For some reason it also seems to just \ + be vastly superior to all other RCDs ever created, possibly due to it being colored gold." + icon_state = "debug_rcd" + ranged = TRUE + can_remove_rwalls = TRUE + allow_concurrent_building = TRUE + toolspeed = 0.25 // Four times as fast. + +/obj/item/weapon/rcd/debug/can_afford(amount) + return TRUE + +/obj/item/weapon/rcd/debug/consume_resources(amount) + return TRUE + +/obj/item/weapon/rcd/debug/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/rcd_ammo)) + to_chat(user, span("notice", "\The [src] makes its own material, no need to add more.")) + return FALSE + return ..() + +/obj/item/weapon/rcd/debug/display_resources() + return "It has UNLIMITED POWER!" + + + +// Ammo for the (non-electric) RCDs. +/obj/item/weapon/rcd_ammo + name = "compressed matter cartridge" + desc = "Highly compressed matter for the RCD." + icon = 'icons/obj/ammo.dmi' + icon_state = "rcd" + item_state = "rcdammo" + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_MATERIAL = 2) + matter = list(DEFAULT_WALL_MATERIAL = 30000,MAT_GLASS = 15000) + var/remaining = RCD_MAX_CAPACITY / 3 + +/obj/item/weapon/rcd_ammo/large + name = "high-capacity matter cartridge" + desc = "Do not ingest." + matter = list(DEFAULT_WALL_MATERIAL = 45000,MAT_GLASS = 22500) + origin_tech = list(TECH_MATERIAL = 4) + remaining = RCD_MAX_CAPACITY diff --git a/code/game/objects/items/weapons/RMS_vr.dm b/code/game/objects/items/weapons/RMS_vr.dm index 69c5f61e64b..b57cf9d5394 100644 --- a/code/game/objects/items/weapons/RMS_vr.dm +++ b/code/game/objects/items/weapons/RMS_vr.dm @@ -248,7 +248,7 @@ return 1 /obj/item/weapon/rms/attackby(obj/item/W, mob/user) - if(W.is_multitool()) + if(W.has_tool_quality(TOOL_MULTITOOL)) overcharge = !overcharge if(overcharge) to_chat(user, "The Rapid Material Synthesizer quietly whirrs...") diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm index 200e99f8e1e..0eacd7922c3 100644 --- a/code/game/objects/items/weapons/RSF.dm +++ b/code/game/objects/items/weapons/RSF.dm @@ -1,136 +1,136 @@ -/* -CONTAINS: -RSF - -*/ - -/obj/item/weapon/rsf - name = "\improper Rapid-Service-Fabricator" - desc = "A device used to rapidly deploy service items." - description_info = "Control Clicking on the device will allow you to choose the glass it dispenses when in the proper mode." - icon = 'icons/obj/tools_vr.dmi' //VOREStation Edit - icon_state = "rsf" //VOREStation Edit - opacity = 0 - density = FALSE - anchored = FALSE - matter = list(DEFAULT_WALL_MATERIAL = 25000) - var/stored_matter = 30 - var/mode = 1 - var/obj/item/weapon/reagent_containers/glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass - - var/list/container_types = list( - "metamorphic glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass, - "metamorphic pint glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint, - "half-pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/square, - "rocks glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/rocks, - "milkshake glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shake, - "cocktail glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/cocktail, - "shot glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shot, - "pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/pint, - "mug" = /obj/item/weapon/reagent_containers/food/drinks/glass2/mug, - "wine glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/wine, - "condiment bottle" = /obj/item/weapon/reagent_containers/food/condiment - ) - - w_class = ITEMSIZE_NORMAL - -/obj/item/weapon/rsf/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - . += "It currently holds [stored_matter]/30 fabrication-units." - -/obj/item/weapon/rsf/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if (istype(W, /obj/item/weapon/rcd_ammo)) - - if ((stored_matter + 10) > 30) - to_chat(user, "The RSF can't hold any more matter.") - return - - qdel(W) - - stored_matter += 10 - playsound(src, 'sound/machines/click.ogg', 10, 1) - to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") - return - -/obj/item/weapon/rsf/CtrlClick(mob/living/user) - if(!Adjacent(user) || !istype(user)) - to_chat(user,"You are too far away.") - return - var/glass_choice = tgui_input_list(user, "Please choose which type of glass you would like to produce.", "Glass Choice", container_types) - - if(glass_choice) - glasstype = container_types[glass_choice] - else - glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass - -/obj/item/weapon/rsf/attack_self(mob/user as mob) - playsound(src, 'sound/effects/pop.ogg', 50, 0) - if (mode == 1) - mode = 2 - to_chat(user,"Changed dispensing mode to 'Container'.") - return - if (mode == 2) - mode = 3 - to_chat(user,"Changed dispensing mode to 'Paper'") - return - if (mode == 3) - mode = 4 - to_chat(user,"Changed dispensing mode to 'Pen'") - return - if (mode == 4) - mode = 5 - to_chat(user,"Changed dispensing mode to 'Dice Pack'") - return - if (mode == 5) - mode = 1 - to_chat(user,"Changed dispensing mode to 'Cigarette'") - return - -/obj/item/weapon/rsf/afterattack(atom/A, mob/user as mob, proximity) - - if(!proximity) return - - if(istype(user,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = user - if(R.stat || !R.cell || R.cell.charge <= 0) - return - else - if(stored_matter <= 0) - return - - if(!istype(A, /obj/structure/table) && !istype(A, /turf/simulated/floor)) - return - - playsound(src, 'sound/machines/click.ogg', 10, 1) - var/used_energy = 0 - var/obj/product - - switch(mode) - if(1) - product = new /obj/item/clothing/mask/smokable/cigarette() - used_energy = 10 - if(2) - product = new glasstype() - used_energy = 50 - if(3) - product = new /obj/item/weapon/paper() - used_energy = 10 - if(4) - product = new /obj/item/weapon/pen() - used_energy = 50 - if(5) - product = new /obj/item/weapon/storage/pill_bottle/dice() - used_energy = 200 - - to_chat(user,"Dispensing [product ? product : "product"]...") - product.loc = get_turf(A) - - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - if(R.cell) - R.cell.use(used_energy) - else - stored_matter-- - to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") +/* +CONTAINS: +RSF + +*/ + +/obj/item/weapon/rsf + name = "\improper Rapid-Service-Fabricator" + desc = "A device used to rapidly deploy service items." + description_info = "Control Clicking on the device will allow you to choose the glass it dispenses when in the proper mode." + icon = 'icons/obj/tools_vr.dmi' //VOREStation Edit + icon_state = "rsf" //VOREStation Edit + opacity = 0 + density = FALSE + anchored = FALSE + matter = list(DEFAULT_WALL_MATERIAL = 25000) + var/stored_matter = 30 + var/mode = 1 + var/obj/item/weapon/reagent_containers/glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass + + var/list/container_types = list( + "metamorphic glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass, + "metamorphic pint glass" = /obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint, + "half-pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/square, + "rocks glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/rocks, + "milkshake glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shake, + "cocktail glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/cocktail, + "shot glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/shot, + "pint glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/pint, + "mug" = /obj/item/weapon/reagent_containers/food/drinks/glass2/mug, + "wine glass" = /obj/item/weapon/reagent_containers/food/drinks/glass2/wine, + "condiment bottle" = /obj/item/weapon/reagent_containers/food/condiment + ) + + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/rsf/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + . += "It currently holds [stored_matter]/30 fabrication-units." + +/obj/item/weapon/rsf/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if (istype(W, /obj/item/weapon/rcd_ammo)) + + if ((stored_matter + 10) > 30) + to_chat(user, "The RSF can't hold any more matter.") + return + + qdel(W) + + stored_matter += 10 + playsound(src, 'sound/machines/click.ogg', 10, 1) + to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") + return + +/obj/item/weapon/rsf/CtrlClick(mob/living/user) + if(!Adjacent(user) || !istype(user)) + to_chat(user,"You are too far away.") + return + var/glass_choice = tgui_input_list(user, "Please choose which type of glass you would like to produce.", "Glass Choice", container_types) + + if(glass_choice) + glasstype = container_types[glass_choice] + else + glasstype = /obj/item/weapon/reagent_containers/food/drinks/metaglass + +/obj/item/weapon/rsf/attack_self(mob/user as mob) + playsound(src, 'sound/effects/pop.ogg', 50, 0) + if (mode == 1) + mode = 2 + to_chat(user,"Changed dispensing mode to 'Container'.") + return + if (mode == 2) + mode = 3 + to_chat(user,"Changed dispensing mode to 'Paper'") + return + if (mode == 3) + mode = 4 + to_chat(user,"Changed dispensing mode to 'Pen'") + return + if (mode == 4) + mode = 5 + to_chat(user,"Changed dispensing mode to 'Dice Pack'") + return + if (mode == 5) + mode = 1 + to_chat(user,"Changed dispensing mode to 'Cigarette'") + return + +/obj/item/weapon/rsf/afterattack(atom/A, mob/user as mob, proximity) + + if(!proximity) return + + if(istype(user,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = user + if(R.stat || !R.cell || R.cell.charge <= 0) + return + else + if(stored_matter <= 0) + return + + if(!istype(A, /obj/structure/table) && !istype(A, /turf/simulated/floor)) + return + + playsound(src, 'sound/machines/click.ogg', 10, 1) + var/used_energy = 0 + var/obj/product + + switch(mode) + if(1) + product = new /obj/item/clothing/mask/smokable/cigarette() + used_energy = 10 + if(2) + product = new glasstype() + used_energy = 50 + if(3) + product = new /obj/item/weapon/paper() + used_energy = 10 + if(4) + product = new /obj/item/weapon/pen() + used_energy = 50 + if(5) + product = new /obj/item/weapon/storage/pill_bottle/dice() + used_energy = 200 + + to_chat(user,"Dispensing [product ? product : "product"]...") + product.loc = get_turf(A) + + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(R.cell) + R.cell.use(used_energy) + else + stored_matter-- + to_chat(user,"The RSF now holds [stored_matter]/30 fabrication-units.") diff --git a/code/game/objects/items/weapons/augment_items.dm b/code/game/objects/items/weapons/augment_items.dm index bfef21ae13d..29db229cae4 100644 --- a/code/game/objects/items/weapons/augment_items.dm +++ b/code/game/objects/items/weapons/augment_items.dm @@ -1,37 +1,37 @@ -// **For augment items that aren't subtypes of other things.** - -/obj/item/weapon/melee/augment - name = "integrated item" - desc = "A surprisingly non-descript item, integrated into its user. You probably shouldn't be seeing this." - icon = 'icons/obj/surgery.dmi' - icon_state = "augment_box" - - -/obj/item/weapon/melee/augment/blade - name = "handblade" - desc = "A sleek-looking telescopic blade that fits inside the hand. Favored by infiltration specialists and assassins." - icon_state = "augment_handblade" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - w_class = ITEMSIZE_SMALL - force = 15 - armor_penetration = 25 - sharp = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - defend_chance = 10 - projectile_parry_chance = 5 - hitsound = 'sound/weapons/bladeslice.ogg' - -/obj/item/weapon/melee/augment/blade/arm - name = "armblade" - desc = "A sleek-looking cybernetic blade that cleaves through people like butter. Favored by psychopaths and assassins." - icon_state = "augment_armblade" - w_class = ITEMSIZE_HUGE - force = 30 - armor_penetration = 15 - edge = TRUE - pry = 1 - defend_chance = 40 +// **For augment items that aren't subtypes of other things.** + +/obj/item/weapon/melee/augment + name = "integrated item" + desc = "A surprisingly non-descript item, integrated into its user. You probably shouldn't be seeing this." + icon = 'icons/obj/surgery.dmi' + icon_state = "augment_box" + + +/obj/item/weapon/melee/augment/blade + name = "handblade" + desc = "A sleek-looking telescopic blade that fits inside the hand. Favored by infiltration specialists and assassins." + icon_state = "augment_handblade" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + w_class = ITEMSIZE_SMALL + force = 15 + armor_penetration = 25 + sharp = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + defend_chance = 10 + projectile_parry_chance = 5 + hitsound = 'sound/weapons/bladeslice.ogg' + +/obj/item/weapon/melee/augment/blade/arm + name = "armblade" + desc = "A sleek-looking cybernetic blade that cleaves through people like butter. Favored by psychopaths and assassins." + icon_state = "augment_armblade" + w_class = ITEMSIZE_HUGE + force = 30 + armor_penetration = 15 + edge = TRUE + pry = 1 + defend_chance = 40 projectile_parry_chance = 20 \ No newline at end of file diff --git a/code/game/objects/items/weapons/candle.dm b/code/game/objects/items/weapons/candle.dm index 7663dfd7ff9..8fc58d8664b 100644 --- a/code/game/objects/items/weapons/candle.dm +++ b/code/game/objects/items/weapons/candle.dm @@ -26,8 +26,8 @@ /obj/item/weapon/flame/candle/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(WT.isOn()) //Badasses dont get blinded by lighting their candle with a welding tool light("\The [user] casually lights the [src] with [W].") else if(istype(W, /obj/item/weapon/flame/lighter)) diff --git a/code/game/objects/items/weapons/capture_crystal.dm b/code/game/objects/items/weapons/capture_crystal.dm index 83dbec328a7..450a2c6e39e 100644 --- a/code/game/objects/items/weapons/capture_crystal.dm +++ b/code/game/objects/items/weapons/capture_crystal.dm @@ -635,7 +635,6 @@ list(/mob/living/simple_mob/animal/sif/siffet), list(/mob/living/simple_mob/animal/sif/tymisian), list( - /mob/living/simple_mob/animal/giant_spider/nurse = 10, /mob/living/simple_mob/animal/giant_spider/electric = 5, /mob/living/simple_mob/animal/giant_spider/frost = 5, /mob/living/simple_mob/animal/giant_spider/hunter = 10, diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm index b2405e9d1be..46c7f38751e 100644 --- a/code/game/objects/items/weapons/cigs_lighters.dm +++ b/code/game/objects/items/weapons/cigs_lighters.dm @@ -1,768 +1,768 @@ -/* -CONTAINS: -MATCHES -CIGARETTES -CIGARS -SMOKING PIPES -CUSTOM CIGS -CHEAP LIGHTERS -ZIPPO - -CIGARETTE PACKETS ARE IN FANCY.DM -*/ - -//For anything that can light stuff on fire -/obj/item/weapon/flame - var/lit = 0 - -/obj/item/weapon/flame/is_hot() - return lit - -/////////// -//MATCHES// -/////////// -/obj/item/weapon/flame/match - name = "match" - desc = "A simple match stick, used for lighting fine smokables." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "match_unlit" - var/burnt = 0 - var/smoketime = 5 - w_class = ITEMSIZE_TINY - origin_tech = list(TECH_MATERIAL = 1) - slot_flags = SLOT_EARS - attack_verb = list("burnt", "singed") - drop_sound = 'sound/items/drop/food.ogg' - pickup_sound = 'sound/items/pickup/food.ogg' - -/obj/item/weapon/flame/match/process() - if(isliving(loc)) - var/mob/living/M = loc - M.IgniteMob() - var/turf/location = get_turf(src) - smoketime-- - if(smoketime < 1) - burn_out() - return - if(location) - location.hotspot_expose(700, 5) - return - -/obj/item/weapon/flame/match/dropped(mob/user as mob) - //If dropped, put ourselves out - //not before lighting up the turf we land on, though. - if(lit) - spawn(0) - var/turf/location = src.loc - if(istype(location)) - location.hotspot_expose(700, 5) - burn_out() - return ..() - -/obj/item/weapon/flame/match/proc/light(var/mob/user) - playsound(src, 'sound/items/cigs_lighters/matchstick_lit.ogg', 25, 0, -1) - lit = 1 - damtype = "burn" - icon_state = "match_lit" - name = "burning match" - desc = "A match. This one is presently on fire." - START_PROCESSING(SSobj, src) - -/obj/item/weapon/flame/match/proc/burn_out() - lit = 0 - burnt = 1 - damtype = "brute" - icon_state = "match_burnt" - item_state = "cigoff" - name = "burnt match" - desc = "A match. This one has seen better days." - STOP_PROCESSING(SSobj, src) - -////////////////// -//FINE SMOKABLES// -////////////////// -/obj/item/clothing/mask/smokable - name = "smokable item" - desc = "You're not sure what this is. You should probably ahelp it." - body_parts_covered = 0 - var/lit = 0 - var/icon_on - var/type_butt = null - var/chem_volume = 0 - var/max_smoketime = 0 //Related to sprites - var/smoketime = 0 - var/is_pipe = 0 //Prevents a runtime with pipes - var/matchmes = "USER lights NAME with FLAME" - var/lightermes = "USER lights NAME with FLAME" - var/zippomes = "USER lights NAME with FLAME" - var/weldermes = "USER lights NAME with FLAME" - var/ignitermes = "USER lights NAME with FLAME" - var/brand - blood_sprite_state = null //Can't bloody these - drop_sound = 'sound/items/cigs_lighters/cig_snuff.ogg' - -/obj/item/clothing/mask/smokable/Initialize() - . = ..() - flags |= NOREACT // so it doesn't react until you light it - create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 15 - if(smoketime && !max_smoketime) - max_smoketime = smoketime - -/obj/item/clothing/mask/smokable/proc/smoke(amount) - if(smoketime > max_smoketime) - smoketime = max_smoketime - smoketime -= amount - if(reagents && reagents.total_volume) // check if it has any reagents at all - if(ishuman(loc)) - var/mob/living/carbon/human/C = loc - if (src == C.wear_mask && C.check_has_mouth()) // if it's in the human/monkey mouth, transfer reagents to the mob - reagents.trans_to_mob(C, amount, CHEM_INGEST, 1.5) // I don't predict significant balance issues by letting blunts actually WORK. - else // else just remove some of the reagents - reagents.remove_any(REM) - -/obj/item/clothing/mask/smokable/process() - var/turf/location = get_turf(src) - smoke(1) - if(smoketime < 1) - die() - return - if(location) - location.hotspot_expose(700, 5) - -/obj/item/clothing/mask/smokable/update_icon() - if(lit) - icon_state = "[initial(icon_state)]_on" - item_state = "[initial(item_state)]_on" - else if(smoketime < max_smoketime) - if(is_pipe) - icon_state = initial(icon_state) - item_state = initial(item_state) - else - icon_state = "[initial(icon_state)]_burnt" - item_state = "[initial(item_state)]_burnt" - if(ismob(loc)) - var/mob/living/M = loc - M.update_inv_wear_mask(0) - M.update_inv_l_hand(0) - M.update_inv_r_hand(1) - ..() - -/obj/item/clothing/mask/smokable/examine(mob/user) - . = ..() - - if(!is_pipe) - var/smoke_percent = round((smoketime / max_smoketime) * 100) - switch(smoke_percent) - if(90 to INFINITY) - . += "[src] is still fresh." - if(60 to 90) - . += "[src] has a good amount of burn time remaining." - if(30 to 60) - . += "[src] is about half finished." - if(10 to 30) - . += "[src] is starting to burn low." - else - . += "[src] is nearly burnt out!" - -/obj/item/clothing/mask/smokable/proc/light(var/flavor_text = "[usr] lights the [name].") - if(!src.lit) - src.lit = 1 - playsound(src, 'sound/items/cigs_lighters/cig_light.ogg', 75, 1, -1) - damtype = "fire" - if(reagents.get_reagent_amount("phoron")) // the phoron explodes when exposed to fire - var/datum/effect/effect/system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount("phoron") / 2.5, 1), get_turf(src), 0, 0) - e.start() - qdel(src) - return - if(reagents.get_reagent_amount("fuel")) // the fuel explodes, too, but much less violently - var/datum/effect/effect/system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount("fuel") / 5, 1), get_turf(src), 0, 0) - e.start() - qdel(src) - return - flags &= ~NOREACT // allowing reagents to react after being lit - reagents.handle_reactions() - var/turf/T = get_turf(src) - T.visible_message(flavor_text) - update_icon() - set_light(2, 0.25, "#E38F46") - START_PROCESSING(SSobj, src) - -/obj/item/clothing/mask/smokable/proc/die(var/nomessage = 0) - var/turf/T = get_turf(src) - set_light(0) - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - STOP_PROCESSING(SSobj, src) - if (type_butt) - var/obj/item/butt = new type_butt(T) - transfer_fingerprints_to(butt) - if(brand) - butt.desc += " This one is \a [brand]." - if(ismob(loc)) - var/mob/living/M = loc - if (!nomessage) - to_chat(M, "Your [name] goes out.") - M.remove_from_mob(src) //un-equip it so the overlays can update - M.update_inv_wear_mask(0) - qdel(src) - else - new /obj/effect/decal/cleanable/ash(T) - if(ismob(loc)) - var/mob/living/M = loc - if (!nomessage) - to_chat(M, "Your [name] goes out, and you empty the ash.") - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - lit = 0 - icon_state = initial(icon_state) - item_state = initial(item_state) - M.update_inv_wear_mask(0) - smoketime = 0 - reagents.clear_reagents() - name = "empty [initial(name)]" - -/obj/item/clothing/mask/smokable/proc/quench() - lit = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - -/obj/item/clothing/mask/smokable/attack(mob/living/carbon/human/H, mob/user, def_zone) - if(lit && H == user && istype(H)) - var/obj/item/blocked = H.check_mouth_coverage() - if(blocked) - to_chat(H, "\The [blocked] is in the way!") - return 1 - to_chat(H, "You take a drag on your [name].") - playsound(src, 'sound/items/cigs_lighters/inhale.ogg', 50, 0, -1) - smoke(5) - return 1 - return ..() - -/obj/item/clothing/mask/smokable/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if(W.is_hot()) - var/text = matchmes - if(istype(W, /obj/item/weapon/flame/match)) - text = matchmes - else if(istype(W, /obj/item/weapon/flame/lighter/zippo)) - text = zippomes - else if(istype(W, /obj/item/weapon/flame/lighter)) - text = lightermes - else if(istype(W, /obj/item/weapon/weldingtool)) - text = weldermes - else if(istype(W, /obj/item/device/assembly/igniter)) - text = ignitermes - text = replacetext(text, "USER", "[user]") - text = replacetext(text, "NAME", "[name]") - text = replacetext(text, "FLAME", "[W.name]") - light(text) - -/obj/item/clothing/mask/smokable/attack(var/mob/living/M, var/mob/living/user, def_zone) - if(istype(M) && M.on_fire) - user.do_attack_animation(M) - light("[user] coldly lights the [name] with the burning body of [M].") - return 1 - else - return ..() - -/obj/item/clothing/mask/smokable/water_act(amount) - if(amount >= 5) - quench() - -/obj/item/clothing/mask/smokable/cigarette - name = "cigarette" - desc = "A roll of tobacco and nicotine." - icon_state = "cig" - item_state = "cig" - throw_speed = 0.5 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS | SLOT_MASK - attack_verb = list("burnt", "singed") - type_butt = /obj/item/trash/cigbutt - chem_volume = 15 - max_smoketime = 300 - smoketime = 300 - var/nicotine_amt = 2 - matchmes = "USER lights their NAME with their FLAME." - lightermes = "USER manages to light their NAME with FLAME." - zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." - weldermes = "USER casually lights the NAME with FLAME." - ignitermes = "USER fiddles with FLAME, and manages to light their NAME." - -/obj/item/clothing/mask/smokable/cigarette/Initialize() - . = ..() - if(nicotine_amt) - reagents.add_reagent("nicotine", nicotine_amt) - -/obj/item/clothing/mask/smokable/cigarette/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - - if(istype(W, /obj/item/weapon/melee/energy/sword)) - var/obj/item/weapon/melee/energy/sword/S = W - if(S.active) - light("[user] swings their [W], barely missing their nose. They light their [name] in the process.") - - return - -/obj/item/clothing/mask/smokable/cigarette/afterattack(obj/item/weapon/reagent_containers/glass/glass, mob/user as mob, proximity) - ..() - if(!proximity) - return - if(istype(glass)) //you can dip cigarettes into beakers - var/transfered = glass.reagents.trans_to_obj(src, chem_volume) - if(transfered) //if reagents were transfered, show the message - to_chat(user, "You dip \the [src] into \the [glass].") - else //if not, either the beaker was empty, or the cigarette was full - if(!glass.reagents.total_volume) - to_chat(user, "[glass] is empty.") - else - to_chat(user, "[src] is full.") - -/obj/item/clothing/mask/smokable/cigarette/attack_self(mob/user as mob) - if(lit == 1) - if(user.a_intent == I_HURT) - user.visible_message("[user] drops and treads on the lit [src], putting it out instantly.") - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - die(1) - else - user.visible_message("[user] puts out \the [src].") - quench() - return ..() - -//////////// -// CIGARS // -//////////// -/obj/item/clothing/mask/smokable/cigarette/cigar - name = "premium cigar" - desc = "A brown roll of tobacco and... well, you're not quite sure. This thing's huge!" - description_fluff = "While the label does say that this is a 'premium cigar', it \ - really cannot match other types of cigars on the market. Is it a quality \ - cigarette? Perhaps. Was it hand-made with care? No." - icon_state = "cigar2" - type_butt = /obj/item/trash/cigbutt/cigarbutt - throw_speed = 0.5 - item_state = "cigar" - max_smoketime = 1500 - smoketime = 1500 - chem_volume = 20 - nicotine_amt = 4 - matchmes = "USER lights their NAME with their FLAME." - lightermes = "USER manages to offend their NAME by lighting it with FLAME." - zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." - weldermes = "USER insults NAME by lighting it with FLAME." - ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." - -/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba - name = "\improper Cohiba Robusto cigar" - desc = "There's little more you could want from a cigar." - description_fluff = "Cohiba has been a popular cigar company for centuries. \ - They are still based out of Cuba and refuse to expand and therefore have a very \ - limited quantity, making their cigars coveted all through known space. Robusto \ - is one of their most popular shapes of cigars." - icon_state = "cigar2" - nicotine_amt = 7 - -/obj/item/clothing/mask/smokable/cigarette/cigar/havana - name = "premium Havanian cigar" - desc = "Save these for the fancy-pantses at the next CentCom black tie reception. \ - You can't blow the smoke from such majestic stogies in just anyone's face." - description_fluff = "'Havanian' is an umbrella term for any cigar made in the \ - typical handmade style of Cuba. This particular cigar is from Gilthari's cigar \ - manufacturers and produced galaxy-wide. While this way of making quality cigars \ - has become slightly bastardized over the years, overall quality has remained \ - relatively the same, even if there is a large quantity of 'Havanian' cigars." - icon_state = "cigar2" - max_smoketime = 7200 - smoketime = 7200 - chem_volume = 30 - nicotine_amt = 10 - -/obj/item/trash/cigbutt - name = "cigarette butt" - desc = "A manky old cigarette butt." - icon = 'icons/inventory/face/item.dmi' - icon_state = "cigbutt" - randpixel = 10 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - throwforce = 1 - -/obj/item/trash/cigbutt/Initialize() - . = ..() - randpixel_xy() - transform = turn(transform,rand(0,360)) - -/obj/item/trash/cigbutt/cigarbutt - name = "cigar butt" - desc = "A manky old cigar butt." - icon_state = "cigarbutt" - -/obj/item/clothing/mask/smokable/cigarette/cigar/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - - user.update_inv_wear_mask(0) - user.update_inv_l_hand(0) - user.update_inv_r_hand(1) - -///////////////// -//SMOKING PIPES// -///////////////// -/obj/item/clothing/mask/smokable/pipe - name = "smoking pipe" - desc = "A pipe, for smoking. Made of fine, stained cherry wood." - description_fluff = "ClassiCo Accessories and Haberdashers, originating out of Mars, \ - claim to produce products 'for the modern gentlefolk'. Most of their items are high-end \ - and expensive, but they pledge to back their prices up with quality, and usually do." - icon_state = "pipe" - item_state = "pipe" - smoketime = 0 - chem_volume = 50 - matchmes = "USER lights their NAME with their FLAME." - lightermes = "USER manages to light their NAME with FLAME." - zippomes = "With much care, USER lights their NAME with their FLAME." - weldermes = "USER recklessly lights NAME with FLAME." - ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." - is_pipe = 1 - -/obj/item/clothing/mask/smokable/pipe/New() - ..() - name = "empty [initial(name)]" - -/obj/item/clothing/mask/smokable/pipe/attack_self(mob/user as mob) - if(lit == 1) - if(user.a_intent == I_HURT) - user.visible_message("[user] empties the lit [src] on the floor!.") - playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) - die(1) - else - user.visible_message("[user] puts out \the [src].") - quench() - -/obj/item/clothing/mask/smokable/pipe/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/melee/energy/sword)) - return - - ..() - - if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) - var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W - if (!G.dry) - to_chat(user, "[G] must be dried before you stuff it into [src].") - return - if (smoketime) - to_chat(user, "[src] is already packed.") - return - max_smoketime = 1000 - smoketime = 1000 - if(G.reagents) - G.reagents.trans_to_obj(src, G.reagents.total_volume) - name = "[G.name]-packed [initial(name)]" - qdel(G) - - else if(istype(W, /obj/item/weapon/flame/lighter)) - var/obj/item/weapon/flame/lighter/L = W - if(L.lit) - light("[user] manages to light their [name] with [W].") - - else if(istype(W, /obj/item/weapon/flame/match)) - var/obj/item/weapon/flame/match/M = W - if(M.lit) - light("[user] lights their [name] with their [W].") - - else if(istype(W, /obj/item/device/assembly/igniter)) - light("[user] fiddles with [W], and manages to light their [name] with the power of science.") - - user.update_inv_wear_mask(0) - user.update_inv_l_hand(0) - user.update_inv_r_hand(1) - -/obj/item/clothing/mask/smokable/pipe/cobpipe - name = "corn cob pipe" - desc = "A nicotine delivery system popularized by folksy backwoodsmen, kept popular in the modern age and beyond by space hipsters." - icon_state = "cobpipe" - item_state = "cobpipe" - chem_volume = 35 - -/obj/item/clothing/mask/smokable/pipe/bonepipe - name = "Europan bone pipe" - desc = "A smoking pipe made out of the bones of the Europan bone whale." - description_fluff = "While most commonly associated with bone charms, bones from various sea creatures on Europa are used in a \ - variety of goods, such as this smoking pipe. While smoking in submarines is often an uncommon occurrence, due to a lack of \ - available air or space, these pipes are a common sight in the many stations of Europa. Higher-quality pipes typically have \ - scenes etched into their bones, and can tell the story of their owner's time on Europa." - icon_state = "bonepipe" - item_state = "bonepipe" - chem_volume = 30 - -/////////////// -//CUSTOM CIGS// -/////////////// -//and by custom cigs i mean craftable joints. smoke weed every day - -/obj/item/clothing/mask/smokable/cigarette/joint - name = "joint" - desc = "A joint lovingly rolled and crafted with care. Blaze it." - icon_state = "joint" - max_smoketime = 400 - smoketime = 400 - chem_volume = 25 - -/obj/item/clothing/mask/smokable/cigarette/joint/blunt - name = "blunt" - desc = "A blunt lovingly rolled and crafted with care. Blaze it." - icon_state = "cigar" - max_smoketime = 500 - smoketime = 500 - nicotine_amt = 4 - chem_volume = 45 - -/obj/item/weapon/reagent_containers/rollingpaper - name = "rolling paper" - desc = "A small, thin piece of easily flammable paper, commonly used for rolling and smoking various dried plants." - description_fluff = "The legalization of certain substances propelled the sale of rolling \ - papers through the roof. Now almost every Trans-stellar produces a variety, often of questionable quality." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "cig paper" - volume = 25 - var/obj/item/clothing/mask/smokable/cigarette/crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint - -/obj/item/weapon/reagent_containers/rollingpaper/blunt - name = "blunt wrap" - desc = "A small piece of easily flammable paper similar to that which encases cigars. It's made out of tobacco, bigger than a standard rolling paper, and will last longer." - icon_state = "blunt paper" - volume = 45 - crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint/blunt - -/obj/item/weapon/reagent_containers/rollingpaper/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) - var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W - if (!G.dry) //This prevents people from just stuffing cheeseburgers into their joint - to_chat(user, "[G.name] must be dried before you add it to [src].") - return - if (G.reagents.total_volume + src.reagents.total_volume > src.reagents.maximum_volume) //Check that we don't have too much already in the paper before adding things - to_chat(user, "The [src] is too full to add [G.name].") - return - if (src.reagents.total_volume == 0) - if (istype(src, /obj/item/weapon/reagent_containers/rollingpaper/blunt)) //update the icon if this is the first thing we're adding to the paper - src.icon_state = "blunt_full" - else - src.icon_state = "paper_full" - to_chat(user, "You add the [G.name] to the [src.name].") - src.add_fingerprint(user) - if(G.reagents) - G.reagents.trans_to_obj(src, G.reagents.total_volume) //adds the reagents from the plant into the paper - user.drop_from_inventory(G) - qdel(G) - -/obj/item/weapon/reagent_containers/rollingpaper/attack_self(mob/living/user) - if(!src.reagents) //don't roll an empty joint - to_chat(user, "There is nothing in [src]. Add something to it first.") - return - var/obj/item/clothing/mask/smokable/cigarette/J = new crafted_type() - to_chat(user,"You roll the [src] into a blunt!") - J.add_fingerprint(user) - if(src.reagents) - src.reagents.trans_to_obj(J, src.reagents.total_volume) - user.drop_from_inventory(src) - user.put_in_hands(J) - qdel(src) - -///////// -//CHEAP// -///////// -/obj/item/weapon/flame/lighter - name = "cheap lighter" - desc = "A cheap-as-free lighter." - description_fluff = "The 'hand-made in Altair' sticker underneath is a charming way of \ - saying 'Made with prison labour'. It's no wonder the company can sell these things so cheap." - icon = 'icons/obj/lighters.dmi' - icon_state = "lighter" - item_state = "lighter" - w_class = ITEMSIZE_TINY - throwforce = 4 - slot_flags = SLOT_BELT - attack_verb = list("burnt", "singed") - var/base_state - /// Sounds - var/activation_sound = 'sound/items/lighter_on.ogg' - var/deactivation_sound = 'sound/items/lighter_off.ogg' - /// Color of the flame and how big the flame is (pulled from Welder code) - var/flame_color = "#FF9933" - var/flame_intensity = 2 - /// Color List - var/random_color = FALSE - var/available_colors = list(COLOR_ASSEMBLY_BLACK, - COLOR_ASSEMBLY_BGRAY, - COLOR_ASSEMBLY_WHITE, - COLOR_ASSEMBLY_RED, - COLOR_ASSEMBLY_ORANGE, - COLOR_ASSEMBLY_BEIGE, - COLOR_ASSEMBLY_BROWN, - COLOR_ASSEMBLY_GOLD, - COLOR_ASSEMBLY_YELLOW, - COLOR_ASSEMBLY_GURKHA, - COLOR_ASSEMBLY_LGREEN, - COLOR_ASSEMBLY_GREEN, - COLOR_ASSEMBLY_LBLUE, - COLOR_ASSEMBLY_BLUE, - COLOR_ASSEMBLY_PURPLE, - COLOR_ASSEMBLY_HOT_PINK) - -// TODO: Remove this path from POIs and loose maps (it's no longer needed) -/obj/item/weapon/flame/lighter/random - -// Randomizes Cheap Lighters on Spawn -/obj/item/weapon/flame/lighter/Initialize() - . = ..() - var/image/I = image(icon, "lighter-[pick("trans","tall","matte")]") - I.color = pick(available_colors) - add_overlay(I) - -/obj/item/weapon/flame/lighter/attack_self(mob/living/user) - if(!lit) - lit = 1 - icon_state = "lighteron" - playsound(src, activation_sound, 75, 1) - user.visible_message("After a few attempts, [user] manages to light the [src].") - - set_light(2, 0.5, "#FF9933") - START_PROCESSING(SSobj, src) - update_icon() - else - lit = 0 - icon_state = "lighter" - playsound(src, deactivation_sound, 75, 1) - user.visible_message("[user] quietly shuts off the [src].") - - set_light(0) - STOP_PROCESSING(SSobj, src) - update_icon() - return - -/obj/item/weapon/flame/lighter/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M, /mob)) - return - - if(lit == 1) - M.IgniteMob() - add_attack_logs(user,M,"Lit on fire with [src]") - - if(istype(M.wear_mask, /obj/item/clothing/mask/smokable/cigarette) && user.zone_sel.selecting == O_MOUTH && lit) - var/obj/item/clothing/mask/smokable/cigarette/cig = M.wear_mask - if(M == user) - cig.attackby(src, user) - else - if(istype(src, /obj/item/weapon/flame/lighter/zippo)) - cig.light("[user] whips the [name] out and holds it for [M].") - else - cig.light("[user] holds the [name] out for [M], and lights the [cig.name].") - else - ..() - -/obj/item/weapon/flame/lighter/process() - var/turf/location = get_turf(src) - if(location) - location.hotspot_expose(700, 5) - return - -///////// -//ZIPPO// -///////// -/obj/item/weapon/flame/lighter/zippo - name = "\improper Zippo lighter" - desc = "The zippo." - description_fluff = "Still going after all these years." - icon_state = "zippo" - item_state = "zippo" - activation_sound = 'sound/items/zippo_on.ogg' - deactivation_sound = 'sound/items/zippo_off.ogg' - -/obj/item/weapon/flame/lighter/zippo/Initialize() - . = ..() - cut_overlays() //Prevents the Cheap Lighter overlay from appearing on this - -/obj/item/weapon/flame/lighter/zippo/attack_self(mob/living/user) - if(!base_state) - base_state = icon_state - if(!lit) - lit = 1 - icon_state = "[base_state]on" - item_state = "[base_state]on" - playsound(src, activation_sound, 75, 1) - user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.") - - set_light(2, 0.5, "#FF9933") - START_PROCESSING(SSobj, src) - else - lit = 0 - icon_state = "[base_state]" - item_state = "[base_state]" - playsound(src, deactivation_sound, 75, 1) - user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what they're doing.") - - set_light(0) - STOP_PROCESSING(SSobj, src) - return - -//Here we add Zippo skins. - -/obj/item/weapon/flame/lighter/zippo/black - name = "\improper holy Zippo lighter" - desc = "Only in regards to Christianity, that is." - icon_state = "blackzippo" - -/obj/item/weapon/flame/lighter/zippo/blue - name = "\improper blue Zippo lighter" - icon_state = "bluezippo" - -/obj/item/weapon/flame/lighter/zippo/engraved - name = "\improper engraved Zippo lighter" - icon_state = "engravedzippo" - item_state = "zippo" - -/obj/item/weapon/flame/lighter/zippo/gold - name = "\improper golden Zippo lighter" - icon_state = "goldzippo" - -/obj/item/weapon/flame/lighter/zippo/moff - name = "\improper moth Zippo lighter" - desc = "Too cute to be a Tymisian." - icon_state = "moffzippo" - -/obj/item/weapon/flame/lighter/zippo/red - name = "\improper red Zippo lighter" - icon_state = "redzippo" - -/obj/item/weapon/flame/lighter/zippo/ironic - name = "\improper ironic Zippo lighter" - desc = "What a quiant idea." - icon_state = "ironiczippo" - -/obj/item/weapon/flame/lighter/zippo/capitalist - name = "\improper capitalist Zippo lighter" - desc = "Made of gold and obsidian, this is truly not worth however much you spent on it." - icon_state = "cappiezippo" - -/obj/item/weapon/flame/lighter/zippo/communist - name = "\improper communist Zippo lighter" - desc = "All you need to spark a revolution." - icon_state = "commiezippo" - -/obj/item/weapon/flame/lighter/zippo/royal - name = "\improper royal Zippo lighter" - desc = "An incredibly fancy lighter, gilded and covered in the color of royalty." - icon_state = "royalzippo" - -/obj/item/weapon/flame/lighter/zippo/gonzo - name = "\improper Gonzo Zippo lighter" - desc = "A lighter with the iconic Gonzo fist painted on it." - icon_state = "gonzozippo" - -/obj/item/weapon/flame/lighter/zippo/rainbow - name = "\improper rainbow Zippo lighter" - icon_state = "rainbowzippo" - -/obj/item/weapon/flame/lighter/zippo/skull - name = "\improper badass Zippo lighter" - desc = "An absolutely badass zippo lighter. Just look at that skull!" +/* +CONTAINS: +MATCHES +CIGARETTES +CIGARS +SMOKING PIPES +CUSTOM CIGS +CHEAP LIGHTERS +ZIPPO + +CIGARETTE PACKETS ARE IN FANCY.DM +*/ + +//For anything that can light stuff on fire +/obj/item/weapon/flame + var/lit = 0 + +/obj/item/weapon/flame/is_hot() + return lit + +/////////// +//MATCHES// +/////////// +/obj/item/weapon/flame/match + name = "match" + desc = "A simple match stick, used for lighting fine smokables." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "match_unlit" + var/burnt = 0 + var/smoketime = 5 + w_class = ITEMSIZE_TINY + origin_tech = list(TECH_MATERIAL = 1) + slot_flags = SLOT_EARS + attack_verb = list("burnt", "singed") + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' + +/obj/item/weapon/flame/match/process() + if(isliving(loc)) + var/mob/living/M = loc + M.IgniteMob() + var/turf/location = get_turf(src) + smoketime-- + if(smoketime < 1) + burn_out() + return + if(location) + location.hotspot_expose(700, 5) + return + +/obj/item/weapon/flame/match/dropped(mob/user as mob) + //If dropped, put ourselves out + //not before lighting up the turf we land on, though. + if(lit) + spawn(0) + var/turf/location = src.loc + if(istype(location)) + location.hotspot_expose(700, 5) + burn_out() + return ..() + +/obj/item/weapon/flame/match/proc/light(var/mob/user) + playsound(src, 'sound/items/cigs_lighters/matchstick_lit.ogg', 25, 0, -1) + lit = 1 + damtype = "burn" + icon_state = "match_lit" + name = "burning match" + desc = "A match. This one is presently on fire." + START_PROCESSING(SSobj, src) + +/obj/item/weapon/flame/match/proc/burn_out() + lit = 0 + burnt = 1 + damtype = "brute" + icon_state = "match_burnt" + item_state = "cigoff" + name = "burnt match" + desc = "A match. This one has seen better days." + STOP_PROCESSING(SSobj, src) + +////////////////// +//FINE SMOKABLES// +////////////////// +/obj/item/clothing/mask/smokable + name = "smokable item" + desc = "You're not sure what this is. You should probably ahelp it." + body_parts_covered = 0 + var/lit = 0 + var/icon_on + var/type_butt = null + var/chem_volume = 0 + var/max_smoketime = 0 //Related to sprites + var/smoketime = 0 + var/is_pipe = 0 //Prevents a runtime with pipes + var/matchmes = "USER lights NAME with FLAME" + var/lightermes = "USER lights NAME with FLAME" + var/zippomes = "USER lights NAME with FLAME" + var/weldermes = "USER lights NAME with FLAME" + var/ignitermes = "USER lights NAME with FLAME" + var/brand + blood_sprite_state = null //Can't bloody these + drop_sound = 'sound/items/cigs_lighters/cig_snuff.ogg' + +/obj/item/clothing/mask/smokable/Initialize() + . = ..() + flags |= NOREACT // so it doesn't react until you light it + create_reagents(chem_volume) // making the cigarrete a chemical holder with a maximum volume of 15 + if(smoketime && !max_smoketime) + max_smoketime = smoketime + +/obj/item/clothing/mask/smokable/proc/smoke(amount) + if(smoketime > max_smoketime) + smoketime = max_smoketime + smoketime -= amount + if(reagents && reagents.total_volume) // check if it has any reagents at all + if(ishuman(loc)) + var/mob/living/carbon/human/C = loc + if (src == C.wear_mask && C.check_has_mouth()) // if it's in the human/monkey mouth, transfer reagents to the mob + reagents.trans_to_mob(C, amount, CHEM_INGEST, 1.5) // I don't predict significant balance issues by letting blunts actually WORK. + else // else just remove some of the reagents + reagents.remove_any(REM) + +/obj/item/clothing/mask/smokable/process() + var/turf/location = get_turf(src) + smoke(1) + if(smoketime < 1) + die() + return + if(location) + location.hotspot_expose(700, 5) + +/obj/item/clothing/mask/smokable/update_icon() + if(lit) + icon_state = "[initial(icon_state)]_on" + item_state = "[initial(item_state)]_on" + else if(smoketime < max_smoketime) + if(is_pipe) + icon_state = initial(icon_state) + item_state = initial(item_state) + else + icon_state = "[initial(icon_state)]_burnt" + item_state = "[initial(item_state)]_burnt" + if(ismob(loc)) + var/mob/living/M = loc + M.update_inv_wear_mask(0) + M.update_inv_l_hand(0) + M.update_inv_r_hand(1) + ..() + +/obj/item/clothing/mask/smokable/examine(mob/user) + . = ..() + + if(!is_pipe) + var/smoke_percent = round((smoketime / max_smoketime) * 100) + switch(smoke_percent) + if(90 to INFINITY) + . += "[src] is still fresh." + if(60 to 90) + . += "[src] has a good amount of burn time remaining." + if(30 to 60) + . += "[src] is about half finished." + if(10 to 30) + . += "[src] is starting to burn low." + else + . += "[src] is nearly burnt out!" + +/obj/item/clothing/mask/smokable/proc/light(var/flavor_text = "[usr] lights the [name].") + if(!src.lit) + src.lit = 1 + playsound(src, 'sound/items/cigs_lighters/cig_light.ogg', 75, 1, -1) + damtype = "fire" + if(reagents.get_reagent_amount("phoron")) // the phoron explodes when exposed to fire + var/datum/effect/effect/system/reagents_explosion/e = new() + e.set_up(round(reagents.get_reagent_amount("phoron") / 2.5, 1), get_turf(src), 0, 0) + e.start() + qdel(src) + return + if(reagents.get_reagent_amount("fuel")) // the fuel explodes, too, but much less violently + var/datum/effect/effect/system/reagents_explosion/e = new() + e.set_up(round(reagents.get_reagent_amount("fuel") / 5, 1), get_turf(src), 0, 0) + e.start() + qdel(src) + return + flags &= ~NOREACT // allowing reagents to react after being lit + reagents.handle_reactions() + var/turf/T = get_turf(src) + T.visible_message(flavor_text) + update_icon() + set_light(2, 0.25, "#E38F46") + START_PROCESSING(SSobj, src) + +/obj/item/clothing/mask/smokable/proc/die(var/nomessage = 0) + var/turf/T = get_turf(src) + set_light(0) + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + STOP_PROCESSING(SSobj, src) + if (type_butt) + var/obj/item/butt = new type_butt(T) + transfer_fingerprints_to(butt) + if(brand) + butt.desc += " This one is \a [brand]." + if(ismob(loc)) + var/mob/living/M = loc + if (!nomessage) + to_chat(M, "Your [name] goes out.") + M.remove_from_mob(src) //un-equip it so the overlays can update + M.update_inv_wear_mask(0) + qdel(src) + else + new /obj/effect/decal/cleanable/ash(T) + if(ismob(loc)) + var/mob/living/M = loc + if (!nomessage) + to_chat(M, "Your [name] goes out, and you empty the ash.") + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + lit = 0 + icon_state = initial(icon_state) + item_state = initial(item_state) + M.update_inv_wear_mask(0) + smoketime = 0 + reagents.clear_reagents() + name = "empty [initial(name)]" + +/obj/item/clothing/mask/smokable/proc/quench() + lit = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + +/obj/item/clothing/mask/smokable/attack(mob/living/carbon/human/H, mob/user, def_zone) + if(lit && H == user && istype(H)) + var/obj/item/blocked = H.check_mouth_coverage() + if(blocked) + to_chat(H, "\The [blocked] is in the way!") + return 1 + to_chat(H, "You take a drag on your [name].") + playsound(src, 'sound/items/cigs_lighters/inhale.ogg', 50, 0, -1) + smoke(5) + return 1 + return ..() + +/obj/item/clothing/mask/smokable/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if(W.is_hot()) + var/text = matchmes + if(istype(W, /obj/item/weapon/flame/match)) + text = matchmes + else if(istype(W, /obj/item/weapon/flame/lighter/zippo)) + text = zippomes + else if(istype(W, /obj/item/weapon/flame/lighter)) + text = lightermes + else if(istype(W, /obj/item/weapon/weldingtool)) + text = weldermes + else if(istype(W, /obj/item/device/assembly/igniter)) + text = ignitermes + text = replacetext(text, "USER", "[user]") + text = replacetext(text, "NAME", "[name]") + text = replacetext(text, "FLAME", "[W.name]") + light(text) + +/obj/item/clothing/mask/smokable/attack(var/mob/living/M, var/mob/living/user, def_zone) + if(istype(M) && M.on_fire) + user.do_attack_animation(M) + light("[user] coldly lights the [name] with the burning body of [M].") + return 1 + else + return ..() + +/obj/item/clothing/mask/smokable/water_act(amount) + if(amount >= 5) + quench() + +/obj/item/clothing/mask/smokable/cigarette + name = "cigarette" + desc = "A roll of tobacco and nicotine." + icon_state = "cig" + item_state = "cig" + throw_speed = 0.5 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS | SLOT_MASK + attack_verb = list("burnt", "singed") + type_butt = /obj/item/trash/cigbutt + chem_volume = 15 + max_smoketime = 300 + smoketime = 300 + var/nicotine_amt = 2 + matchmes = "USER lights their NAME with their FLAME." + lightermes = "USER manages to light their NAME with FLAME." + zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." + weldermes = "USER casually lights the NAME with FLAME." + ignitermes = "USER fiddles with FLAME, and manages to light their NAME." + +/obj/item/clothing/mask/smokable/cigarette/Initialize() + . = ..() + if(nicotine_amt) + reagents.add_reagent("nicotine", nicotine_amt) + +/obj/item/clothing/mask/smokable/cigarette/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + + if(istype(W, /obj/item/weapon/melee/energy/sword)) + var/obj/item/weapon/melee/energy/sword/S = W + if(S.active) + light("[user] swings their [W], barely missing their nose. They light their [name] in the process.") + + return + +/obj/item/clothing/mask/smokable/cigarette/afterattack(obj/item/weapon/reagent_containers/glass/glass, mob/user as mob, proximity) + ..() + if(!proximity) + return + if(istype(glass)) //you can dip cigarettes into beakers + var/transfered = glass.reagents.trans_to_obj(src, chem_volume) + if(transfered) //if reagents were transfered, show the message + to_chat(user, "You dip \the [src] into \the [glass].") + else //if not, either the beaker was empty, or the cigarette was full + if(!glass.reagents.total_volume) + to_chat(user, "[glass] is empty.") + else + to_chat(user, "[src] is full.") + +/obj/item/clothing/mask/smokable/cigarette/attack_self(mob/user as mob) + if(lit == 1) + if(user.a_intent == I_HURT) + user.visible_message("[user] drops and treads on the lit [src], putting it out instantly.") + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + die(1) + else + user.visible_message("[user] puts out \the [src].") + quench() + return ..() + +//////////// +// CIGARS // +//////////// +/obj/item/clothing/mask/smokable/cigarette/cigar + name = "premium cigar" + desc = "A brown roll of tobacco and... well, you're not quite sure. This thing's huge!" + description_fluff = "While the label does say that this is a 'premium cigar', it \ + really cannot match other types of cigars on the market. Is it a quality \ + cigarette? Perhaps. Was it hand-made with care? No." + icon_state = "cigar2" + type_butt = /obj/item/trash/cigbutt/cigarbutt + throw_speed = 0.5 + item_state = "cigar" + max_smoketime = 1500 + smoketime = 1500 + chem_volume = 20 + nicotine_amt = 4 + matchmes = "USER lights their NAME with their FLAME." + lightermes = "USER manages to offend their NAME by lighting it with FLAME." + zippomes = "With a flick of their wrist, USER lights their NAME with their FLAME." + weldermes = "USER insults NAME by lighting it with FLAME." + ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." + +/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba + name = "\improper Cohiba Robusto cigar" + desc = "There's little more you could want from a cigar." + description_fluff = "Cohiba has been a popular cigar company for centuries. \ + They are still based out of Cuba and refuse to expand and therefore have a very \ + limited quantity, making their cigars coveted all through known space. Robusto \ + is one of their most popular shapes of cigars." + icon_state = "cigar2" + nicotine_amt = 7 + +/obj/item/clothing/mask/smokable/cigarette/cigar/havana + name = "premium Havanian cigar" + desc = "Save these for the fancy-pantses at the next CentCom black tie reception. \ + You can't blow the smoke from such majestic stogies in just anyone's face." + description_fluff = "'Havanian' is an umbrella term for any cigar made in the \ + typical handmade style of Cuba. This particular cigar is from Gilthari's cigar \ + manufacturers and produced galaxy-wide. While this way of making quality cigars \ + has become slightly bastardized over the years, overall quality has remained \ + relatively the same, even if there is a large quantity of 'Havanian' cigars." + icon_state = "cigar2" + max_smoketime = 7200 + smoketime = 7200 + chem_volume = 30 + nicotine_amt = 10 + +/obj/item/trash/cigbutt + name = "cigarette butt" + desc = "A manky old cigarette butt." + icon = 'icons/inventory/face/item.dmi' + icon_state = "cigbutt" + randpixel = 10 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + throwforce = 1 + +/obj/item/trash/cigbutt/Initialize() + . = ..() + randpixel_xy() + transform = turn(transform,rand(0,360)) + +/obj/item/trash/cigbutt/cigarbutt + name = "cigar butt" + desc = "A manky old cigar butt." + icon_state = "cigarbutt" + +/obj/item/clothing/mask/smokable/cigarette/cigar/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + + user.update_inv_wear_mask(0) + user.update_inv_l_hand(0) + user.update_inv_r_hand(1) + +///////////////// +//SMOKING PIPES// +///////////////// +/obj/item/clothing/mask/smokable/pipe + name = "smoking pipe" + desc = "A pipe, for smoking. Made of fine, stained cherry wood." + description_fluff = "ClassiCo Accessories and Haberdashers, originating out of Mars, \ + claim to produce products 'for the modern gentlefolk'. Most of their items are high-end \ + and expensive, but they pledge to back their prices up with quality, and usually do." + icon_state = "pipe" + item_state = "pipe" + smoketime = 0 + chem_volume = 50 + matchmes = "USER lights their NAME with their FLAME." + lightermes = "USER manages to light their NAME with FLAME." + zippomes = "With much care, USER lights their NAME with their FLAME." + weldermes = "USER recklessly lights NAME with FLAME." + ignitermes = "USER fiddles with FLAME, and manages to light their NAME with the power of science." + is_pipe = 1 + +/obj/item/clothing/mask/smokable/pipe/New() + ..() + name = "empty [initial(name)]" + +/obj/item/clothing/mask/smokable/pipe/attack_self(mob/user as mob) + if(lit == 1) + if(user.a_intent == I_HURT) + user.visible_message("[user] empties the lit [src] on the floor!.") + playsound(src, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) + die(1) + else + user.visible_message("[user] puts out \the [src].") + quench() + +/obj/item/clothing/mask/smokable/pipe/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/melee/energy/sword)) + return + + ..() + + if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) + var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W + if (!G.dry) + to_chat(user, "[G] must be dried before you stuff it into [src].") + return + if (smoketime) + to_chat(user, "[src] is already packed.") + return + max_smoketime = 1000 + smoketime = 1000 + if(G.reagents) + G.reagents.trans_to_obj(src, G.reagents.total_volume) + name = "[G.name]-packed [initial(name)]" + qdel(G) + + else if(istype(W, /obj/item/weapon/flame/lighter)) + var/obj/item/weapon/flame/lighter/L = W + if(L.lit) + light("[user] manages to light their [name] with [W].") + + else if(istype(W, /obj/item/weapon/flame/match)) + var/obj/item/weapon/flame/match/M = W + if(M.lit) + light("[user] lights their [name] with their [W].") + + else if(istype(W, /obj/item/device/assembly/igniter)) + light("[user] fiddles with [W], and manages to light their [name] with the power of science.") + + user.update_inv_wear_mask(0) + user.update_inv_l_hand(0) + user.update_inv_r_hand(1) + +/obj/item/clothing/mask/smokable/pipe/cobpipe + name = "corn cob pipe" + desc = "A nicotine delivery system popularized by folksy backwoodsmen, kept popular in the modern age and beyond by space hipsters." + icon_state = "cobpipe" + item_state = "cobpipe" + chem_volume = 35 + +/obj/item/clothing/mask/smokable/pipe/bonepipe + name = "Europan bone pipe" + desc = "A smoking pipe made out of the bones of the Europan bone whale." + description_fluff = "While most commonly associated with bone charms, bones from various sea creatures on Europa are used in a \ + variety of goods, such as this smoking pipe. While smoking in submarines is often an uncommon occurrence, due to a lack of \ + available air or space, these pipes are a common sight in the many stations of Europa. Higher-quality pipes typically have \ + scenes etched into their bones, and can tell the story of their owner's time on Europa." + icon_state = "bonepipe" + item_state = "bonepipe" + chem_volume = 30 + +/////////////// +//CUSTOM CIGS// +/////////////// +//and by custom cigs i mean craftable joints. smoke weed every day + +/obj/item/clothing/mask/smokable/cigarette/joint + name = "joint" + desc = "A joint lovingly rolled and crafted with care. Blaze it." + icon_state = "joint" + max_smoketime = 400 + smoketime = 400 + chem_volume = 25 + +/obj/item/clothing/mask/smokable/cigarette/joint/blunt + name = "blunt" + desc = "A blunt lovingly rolled and crafted with care. Blaze it." + icon_state = "cigar" + max_smoketime = 500 + smoketime = 500 + nicotine_amt = 4 + chem_volume = 45 + +/obj/item/weapon/reagent_containers/rollingpaper + name = "rolling paper" + desc = "A small, thin piece of easily flammable paper, commonly used for rolling and smoking various dried plants." + description_fluff = "The legalization of certain substances propelled the sale of rolling \ + papers through the roof. Now almost every Trans-stellar produces a variety, often of questionable quality." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "cig paper" + volume = 25 + var/obj/item/clothing/mask/smokable/cigarette/crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint + +/obj/item/weapon/reagent_containers/rollingpaper/blunt + name = "blunt wrap" + desc = "A small piece of easily flammable paper similar to that which encases cigars. It's made out of tobacco, bigger than a standard rolling paper, and will last longer." + icon_state = "blunt paper" + volume = 45 + crafted_type = /obj/item/clothing/mask/smokable/cigarette/joint/blunt + +/obj/item/weapon/reagent_containers/rollingpaper/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) + var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W + if (!G.dry) //This prevents people from just stuffing cheeseburgers into their joint + to_chat(user, "[G.name] must be dried before you add it to [src].") + return + if (G.reagents.total_volume + src.reagents.total_volume > src.reagents.maximum_volume) //Check that we don't have too much already in the paper before adding things + to_chat(user, "The [src] is too full to add [G.name].") + return + if (src.reagents.total_volume == 0) + if (istype(src, /obj/item/weapon/reagent_containers/rollingpaper/blunt)) //update the icon if this is the first thing we're adding to the paper + src.icon_state = "blunt_full" + else + src.icon_state = "paper_full" + to_chat(user, "You add the [G.name] to the [src.name].") + src.add_fingerprint(user) + if(G.reagents) + G.reagents.trans_to_obj(src, G.reagents.total_volume) //adds the reagents from the plant into the paper + user.drop_from_inventory(G) + qdel(G) + +/obj/item/weapon/reagent_containers/rollingpaper/attack_self(mob/living/user) + if(!src.reagents) //don't roll an empty joint + to_chat(user, "There is nothing in [src]. Add something to it first.") + return + var/obj/item/clothing/mask/smokable/cigarette/J = new crafted_type() + to_chat(user,"You roll the [src] into a blunt!") + J.add_fingerprint(user) + if(src.reagents) + src.reagents.trans_to_obj(J, src.reagents.total_volume) + user.drop_from_inventory(src) + user.put_in_hands(J) + qdel(src) + +///////// +//CHEAP// +///////// +/obj/item/weapon/flame/lighter + name = "cheap lighter" + desc = "A cheap-as-free lighter." + description_fluff = "The 'hand-made in Altair' sticker underneath is a charming way of \ + saying 'Made with prison labour'. It's no wonder the company can sell these things so cheap." + icon = 'icons/obj/lighters.dmi' + icon_state = "lighter" + item_state = "lighter" + w_class = ITEMSIZE_TINY + throwforce = 4 + slot_flags = SLOT_BELT + attack_verb = list("burnt", "singed") + var/base_state + /// Sounds + var/activation_sound = 'sound/items/lighter_on.ogg' + var/deactivation_sound = 'sound/items/lighter_off.ogg' + /// Color of the flame and how big the flame is (pulled from Welder code) + var/flame_color = "#FF9933" + var/flame_intensity = 2 + /// Color List + var/random_color = FALSE + var/available_colors = list(COLOR_ASSEMBLY_BLACK, + COLOR_ASSEMBLY_BGRAY, + COLOR_ASSEMBLY_WHITE, + COLOR_ASSEMBLY_RED, + COLOR_ASSEMBLY_ORANGE, + COLOR_ASSEMBLY_BEIGE, + COLOR_ASSEMBLY_BROWN, + COLOR_ASSEMBLY_GOLD, + COLOR_ASSEMBLY_YELLOW, + COLOR_ASSEMBLY_GURKHA, + COLOR_ASSEMBLY_LGREEN, + COLOR_ASSEMBLY_GREEN, + COLOR_ASSEMBLY_LBLUE, + COLOR_ASSEMBLY_BLUE, + COLOR_ASSEMBLY_PURPLE, + COLOR_ASSEMBLY_HOT_PINK) + +// TODO: Remove this path from POIs and loose maps (it's no longer needed) +/obj/item/weapon/flame/lighter/random + +// Randomizes Cheap Lighters on Spawn +/obj/item/weapon/flame/lighter/Initialize() + . = ..() + var/image/I = image(icon, "lighter-[pick("trans","tall","matte")]") + I.color = pick(available_colors) + add_overlay(I) + +/obj/item/weapon/flame/lighter/attack_self(mob/living/user) + if(!lit) + lit = 1 + icon_state = "lighteron" + playsound(src, activation_sound, 75, 1) + user.visible_message("After a few attempts, [user] manages to light the [src].") + + set_light(2, 0.5, "#FF9933") + START_PROCESSING(SSobj, src) + update_icon() + else + lit = 0 + icon_state = "lighter" + playsound(src, deactivation_sound, 75, 1) + user.visible_message("[user] quietly shuts off the [src].") + + set_light(0) + STOP_PROCESSING(SSobj, src) + update_icon() + return + +/obj/item/weapon/flame/lighter/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M, /mob)) + return + + if(lit == 1) + M.IgniteMob() + add_attack_logs(user,M,"Lit on fire with [src]") + + if(istype(M.wear_mask, /obj/item/clothing/mask/smokable/cigarette) && user.zone_sel.selecting == O_MOUTH && lit) + var/obj/item/clothing/mask/smokable/cigarette/cig = M.wear_mask + if(M == user) + cig.attackby(src, user) + else + if(istype(src, /obj/item/weapon/flame/lighter/zippo)) + cig.light("[user] whips the [name] out and holds it for [M].") + else + cig.light("[user] holds the [name] out for [M], and lights the [cig.name].") + else + ..() + +/obj/item/weapon/flame/lighter/process() + var/turf/location = get_turf(src) + if(location) + location.hotspot_expose(700, 5) + return + +///////// +//ZIPPO// +///////// +/obj/item/weapon/flame/lighter/zippo + name = "\improper Zippo lighter" + desc = "The zippo." + description_fluff = "Still going after all these years." + icon_state = "zippo" + item_state = "zippo" + activation_sound = 'sound/items/zippo_on.ogg' + deactivation_sound = 'sound/items/zippo_off.ogg' + +/obj/item/weapon/flame/lighter/zippo/Initialize() + . = ..() + cut_overlays() //Prevents the Cheap Lighter overlay from appearing on this + +/obj/item/weapon/flame/lighter/zippo/attack_self(mob/living/user) + if(!base_state) + base_state = icon_state + if(!lit) + lit = 1 + icon_state = "[base_state]on" + item_state = "[base_state]on" + playsound(src, activation_sound, 75, 1) + user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.") + + set_light(2, 0.5, "#FF9933") + START_PROCESSING(SSobj, src) + else + lit = 0 + icon_state = "[base_state]" + item_state = "[base_state]" + playsound(src, deactivation_sound, 75, 1) + user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what they're doing.") + + set_light(0) + STOP_PROCESSING(SSobj, src) + return + +//Here we add Zippo skins. + +/obj/item/weapon/flame/lighter/zippo/black + name = "\improper holy Zippo lighter" + desc = "Only in regards to Christianity, that is." + icon_state = "blackzippo" + +/obj/item/weapon/flame/lighter/zippo/blue + name = "\improper blue Zippo lighter" + icon_state = "bluezippo" + +/obj/item/weapon/flame/lighter/zippo/engraved + name = "\improper engraved Zippo lighter" + icon_state = "engravedzippo" + item_state = "zippo" + +/obj/item/weapon/flame/lighter/zippo/gold + name = "\improper golden Zippo lighter" + icon_state = "goldzippo" + +/obj/item/weapon/flame/lighter/zippo/moff + name = "\improper moth Zippo lighter" + desc = "Too cute to be a Tymisian." + icon_state = "moffzippo" + +/obj/item/weapon/flame/lighter/zippo/red + name = "\improper red Zippo lighter" + icon_state = "redzippo" + +/obj/item/weapon/flame/lighter/zippo/ironic + name = "\improper ironic Zippo lighter" + desc = "What a quiant idea." + icon_state = "ironiczippo" + +/obj/item/weapon/flame/lighter/zippo/capitalist + name = "\improper capitalist Zippo lighter" + desc = "Made of gold and obsidian, this is truly not worth however much you spent on it." + icon_state = "cappiezippo" + +/obj/item/weapon/flame/lighter/zippo/communist + name = "\improper communist Zippo lighter" + desc = "All you need to spark a revolution." + icon_state = "commiezippo" + +/obj/item/weapon/flame/lighter/zippo/royal + name = "\improper royal Zippo lighter" + desc = "An incredibly fancy lighter, gilded and covered in the color of royalty." + icon_state = "royalzippo" + +/obj/item/weapon/flame/lighter/zippo/gonzo + name = "\improper Gonzo Zippo lighter" + desc = "A lighter with the iconic Gonzo fist painted on it." + icon_state = "gonzozippo" + +/obj/item/weapon/flame/lighter/zippo/rainbow + name = "\improper rainbow Zippo lighter" + icon_state = "rainbowzippo" + +/obj/item/weapon/flame/lighter/zippo/skull + name = "\improper badass Zippo lighter" + desc = "An absolutely badass zippo lighter. Just look at that skull!" icon_state = "skullzippo" \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/computer/research.dm b/code/game/objects/items/weapons/circuitboards/computer/research.dm index 8de61a2b91c..43ea3d3cd61 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/research.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/research.dm @@ -7,7 +7,7 @@ build_path = /obj/machinery/computer/rdconsole/core /obj/item/weapon/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, I.usesound, 50, 1) user.visible_message("\The [user] adjusts the jumper on \the [src]'s access protocol pins.", "You adjust the jumper on the access protocol pins.") if(build_path == /obj/machinery/computer/rdconsole/core) diff --git a/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm b/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm index 4a5307fa12d..579c5885193 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/kitchen_appliances.dm @@ -1,74 +1,74 @@ -/obj/item/weapon/circuitboard/microwave - name = T_BOARD("microwave") - desc = "The circuitboard for a microwave." - build_path = /obj/machinery/microwave - board_type = new /datum/frame/frame_types/microwave - contain_parts = 0 - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - req_components = list( - /obj/item/weapon/stock_parts/console_screen = 1, - /obj/item/weapon/stock_parts/capacitor = 3, // Original Capacitor count was 1 - /obj/item/weapon/stock_parts/motor = 1, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/oven - name = T_BOARD("oven") - desc = "The circuitboard for an oven." - build_path = /obj/machinery/appliance/cooker/oven - board_type = new /datum/frame/frame_types/machine - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/fryer - name = T_BOARD("deep fryer") - desc = "The circuitboard for a deep fryer." - build_path = /obj/machinery/appliance/cooker/fryer - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/grill - name = T_BOARD("grill") - desc = "The circuitboard for an industrial grill." - build_path = /obj/machinery/appliance/cooker/grill - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/cerealmaker - name = T_BOARD("cereal maker") - desc = "The circuitboard for a cereal maker." - build_path = /obj/machinery/appliance/mixer/cereal - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/candymachine - name = T_BOARD("candy machine") - desc = "The circuitboard for a candy machine." - build_path = /obj/machinery/appliance/mixer/candy - board_type = new /datum/frame/frame_types/machine - req_components = list( - /obj/item/weapon/stock_parts/capacitor = 3, - /obj/item/weapon/stock_parts/scanning_module = 1, - /obj/item/weapon/stock_parts/matter_bin = 2) - -/obj/item/weapon/circuitboard/microwave/advanced - name = T_BOARD("deluxe microwave") - build_path = /obj/machinery/microwave/advanced - board_type = new /datum/frame/frame_types/microwave - matter = list(MAT_STEEL = 50, MAT_GLASS = 50) - req_components = list( - /obj/item/weapon/stock_parts/console_screen = 1, - /obj/item/weapon/stock_parts/motor = 1, +/obj/item/weapon/circuitboard/microwave + name = T_BOARD("microwave") + desc = "The circuitboard for a microwave." + build_path = /obj/machinery/microwave + board_type = new /datum/frame/frame_types/microwave + contain_parts = 0 + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/capacitor = 3, // Original Capacitor count was 1 + /obj/item/weapon/stock_parts/motor = 1, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/oven + name = T_BOARD("oven") + desc = "The circuitboard for an oven." + build_path = /obj/machinery/appliance/cooker/oven + board_type = new /datum/frame/frame_types/machine + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/fryer + name = T_BOARD("deep fryer") + desc = "The circuitboard for a deep fryer." + build_path = /obj/machinery/appliance/cooker/fryer + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/grill + name = T_BOARD("grill") + desc = "The circuitboard for an industrial grill." + build_path = /obj/machinery/appliance/cooker/grill + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/cerealmaker + name = T_BOARD("cereal maker") + desc = "The circuitboard for a cereal maker." + build_path = /obj/machinery/appliance/mixer/cereal + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/candymachine + name = T_BOARD("candy machine") + desc = "The circuitboard for a candy machine." + build_path = /obj/machinery/appliance/mixer/candy + board_type = new /datum/frame/frame_types/machine + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 3, + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/matter_bin = 2) + +/obj/item/weapon/circuitboard/microwave/advanced + name = T_BOARD("deluxe microwave") + build_path = /obj/machinery/microwave/advanced + board_type = new /datum/frame/frame_types/microwave + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + req_components = list( + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/motor = 1, /obj/item/weapon/stock_parts/capacitor = 1) \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/machinery/research.dm b/code/game/objects/items/weapons/circuitboards/machinery/research.dm index 4305ecf1805..8f23291f8aa 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/research.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/research.dm @@ -12,7 +12,7 @@ /obj/item/weapon/stock_parts/scanning_module = 1) /obj/item/weapon/circuitboard/rdserver/attackby(obj/item/I as obj, mob/user as mob) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, I.usesound, 50, 1) user.visible_message("\The [user] adjusts the jumper on \the [src]'s access protocol pins.", "You adjust the jumper on the access protocol pins.") if(build_path == /obj/machinery/r_n_d/server/core) @@ -94,3 +94,14 @@ origin_tech = list(TECH_DATA = 4) req_components = list( /obj/item/stack/cable_coil = 15) + +/obj/item/weapon/circuitboard/protean_reconstitutor + name = T_BOARD("protean reconstitutor") + board_type = new /datum/frame/frame_types/machine + build_path = /obj/machinery/protean_reconstitutor + origin_tech = list(TECH_MAGNET = 5, TECH_BLUESPACE = 5, TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_DATA = 5) + req_components = list( + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/manipulator = 1, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/stack/cable_coil = 5) diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index d7208d5db37..e3a12284704 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -1,87 +1,87 @@ -/* Clown Items - * Contains: - * Banana Peels - * Soap - * Bike Horns - */ - -/* - * Banana Peels - */ -/obj/item/weapon/bananapeel/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM, /mob/living)) - var/mob/living/M = AM - M.slip("the [src.name]",4) -/* - * Soap - */ -/obj/item/weapon/soap/Initialize() - . = ..() - create_reagents(5) - wet() - -/obj/item/weapon/soap/proc/wet() - reagents.add_reagent("cleaner", 5) - -/obj/item/weapon/soap/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM, /mob/living)) - var/mob/living/M = AM - M.slip("the [src.name]",3) - -/obj/item/weapon/soap/afterattack(atom/target, mob/user as mob, proximity) - if(!proximity) return - //I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing. - //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn - if(user.client && (target in user.client.screen)) - to_chat(user, "You need to take that [target.name] off before cleaning it.") - else if(istype(target,/obj/effect/decal/cleanable/blood)) - to_chat(user, "You scrub \the [target.name] out.") - target.clean_blood() - return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. - else if(istype(target,/obj/effect/decal/cleanable)) - to_chat(user, "You scrub \the [target.name] out.") - qdel(target) - else if(istype(target,/turf)) - to_chat(user, "You scrub \the [target.name] clean.") - var/turf/T = target - T.clean(src, user) - else if(istype(target,/obj/structure/sink)) - to_chat(user, "You wet \the [src] in the sink.") - wet() - else - to_chat(user, "You clean \the [target.name].") - target.clean_blood(TRUE) - return - -//attack_as_weapon -/obj/item/weapon/soap/attack(mob/living/target, mob/living/user, var/target_zone) - if(target && user && ishuman(target) && ishuman(user) && !user.incapacitated() && user.zone_sel &&user.zone_sel.selecting == "mouth" ) - user.visible_message("\The [user] washes \the [target]'s mouth out with soap!") - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam - return - ..() - -/* - * Bike Horns - */ -/obj/item/weapon/bikehorn - var/honk_sound = 'sound/items/bikehorn.ogg' - -/obj/item/weapon/bikehorn/attack_self(mob/user as mob) - if(spam_flag == 0) - spam_flag = 1 - playsound(src, honk_sound, 50, 1) - src.add_fingerprint(user) - spawn(20) - spam_flag = 0 - return - -/obj/item/weapon/bikehorn/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(istype(AM, /mob/living)) - playsound(src, honk_sound, 50, 1) +/* Clown Items + * Contains: + * Banana Peels + * Soap + * Bike Horns + */ + +/* + * Banana Peels + */ +/obj/item/weapon/bananapeel/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM, /mob/living)) + var/mob/living/M = AM + M.slip("the [src.name]",4) +/* + * Soap + */ +/obj/item/weapon/soap/Initialize() + . = ..() + create_reagents(5) + wet() + +/obj/item/weapon/soap/proc/wet() + reagents.add_reagent("cleaner", 5) + +/obj/item/weapon/soap/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM, /mob/living)) + var/mob/living/M = AM + M.slip("the [src.name]",3) + +/obj/item/weapon/soap/afterattack(atom/target, mob/user as mob, proximity) + if(!proximity) return + //I couldn't feasibly fix the overlay bugs caused by cleaning items we are wearing. + //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn + if(user.client && (target in user.client.screen)) + to_chat(user, "You need to take that [target.name] off before cleaning it.") + else if(istype(target,/obj/effect/decal/cleanable/blood)) + to_chat(user, "You scrub \the [target.name] out.") + target.clean_blood() + return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. + else if(istype(target,/obj/effect/decal/cleanable)) + to_chat(user, "You scrub \the [target.name] out.") + qdel(target) + else if(istype(target,/turf)) + to_chat(user, "You scrub \the [target.name] clean.") + var/turf/T = target + T.clean(src, user) + else if(istype(target,/obj/structure/sink)) + to_chat(user, "You wet \the [src] in the sink.") + wet() + else + to_chat(user, "You clean \the [target.name].") + target.clean_blood(TRUE) + return + +//attack_as_weapon +/obj/item/weapon/soap/attack(mob/living/target, mob/living/user, var/target_zone) + if(target && user && ishuman(target) && ishuman(user) && !user.incapacitated() && user.zone_sel &&user.zone_sel.selecting == "mouth" ) + user.visible_message("\The [user] washes \the [target]'s mouth out with soap!") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam + return + ..() + +/* + * Bike Horns + */ +/obj/item/weapon/bikehorn + var/honk_sound = 'sound/items/bikehorn.ogg' + +/obj/item/weapon/bikehorn/attack_self(mob/user as mob) + if(spam_flag == 0) + spam_flag = 1 + playsound(src, honk_sound, 50, 1) + src.add_fingerprint(user) + spawn(20) + spam_flag = 0 + return + +/obj/item/weapon/bikehorn/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(istype(AM, /mob/living)) + playsound(src, honk_sound, 50, 1) diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index e2d9ad6a496..6a899d9a700 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -1,113 +1,113 @@ -/obj/item/weapon/lipstick - gender = PLURAL - name = "red lipstick" - desc = "A generic brand of lipstick." - icon = 'icons/obj/items.dmi' - icon_state = "lipstick" - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - var/colour = "red" - var/open = 0 - drop_sound = 'sound/items/drop/glass.ogg' - pickup_sound = 'sound/items/pickup/glass.ogg' - -/obj/item/weapon/lipstick/purple - name = "purple lipstick" - colour = "purple" - -/obj/item/weapon/lipstick/jade - name = "jade lipstick" - colour = "jade" - -/obj/item/weapon/lipstick/black - name = "black lipstick" - colour = "black" - -/obj/item/weapon/lipstick/random - name = "lipstick" - -/obj/item/weapon/lipstick/random/New() - colour = pick("red","purple","jade","black") - name = "[colour] lipstick" - -/obj/item/weapon/lipstick/attack_self(mob/user as mob) - to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") - open = !open - if(open) - icon_state = "[initial(icon_state)]_[colour]" - else - icon_state = initial(icon_state) - -/obj/item/weapon/lipstick/attack(mob/M as mob, mob/user as mob) - if(!open) return - - if(!istype(M, /mob)) return - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.lip_style) //if they already have lipstick on - to_chat(user, "You need to wipe off the old lipstick first!") - return - if(H == user) - user.visible_message("[user] does their lips with \the [src].", \ - "You take a moment to apply \the [src]. Perfect!") - H.lip_style = colour - H.update_icons_body() - else - user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ - "You begin to apply \the [src].") - if(do_after(user, 20, H)) //user needs to keep their active hand, H does not. - user.visible_message("[user] does [H]'s lips with \the [src].", \ - "You apply \the [src].") - H.lip_style = colour - H.update_icons_body() - else - to_chat(user, "Where are the lips on that?") - -//you can wipe off lipstick with paper! see code/modules/paperwork/paper.dm, paper/attack() - -/obj/item/weapon/haircomb //sparklysheep's comb - name = "purple comb" - desc = "A pristine purple comb made from flexible plastic." - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - icon = 'icons/obj/items.dmi' - icon_state = "purplecomb" - -/obj/item/weapon/haircomb/attack_self(mob/living/user) - var/text = "person" - if(ishuman(user)) - var/mob/living/carbon/human/U = user - switch(U.identifying_gender) - if(MALE) - text = "guy" - if(FEMALE) - text = "lady" - else - switch(user.gender) - if(MALE) - text = "guy" - if(FEMALE) - text = "lady" - user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].") - -/obj/item/weapon/makeover - name = "makeover kit" - desc = "A tiny case containing a mirror and some contact lenses." - w_class = ITEMSIZE_TINY - icon = 'icons/obj/items.dmi' - icon_state = "trinketbox" - var/datum/tgui_module/appearance_changer/mirror/coskit/M - -/obj/item/weapon/makeover/Initialize() - . = ..() - M = new(src, null) - -/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob) - if(ishuman(user)) - to_chat(user, "You flip open \the [src] and begin to adjust your appearance.") - M.tgui_interact(user) - var/mob/living/carbon/human/H = user - var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] - if(istype(E)) +/obj/item/weapon/lipstick + gender = PLURAL + name = "red lipstick" + desc = "A generic brand of lipstick." + icon = 'icons/obj/items.dmi' + icon_state = "lipstick" + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + var/colour = "red" + var/open = 0 + drop_sound = 'sound/items/drop/glass.ogg' + pickup_sound = 'sound/items/pickup/glass.ogg' + +/obj/item/weapon/lipstick/purple + name = "purple lipstick" + colour = "purple" + +/obj/item/weapon/lipstick/jade + name = "jade lipstick" + colour = "jade" + +/obj/item/weapon/lipstick/black + name = "black lipstick" + colour = "black" + +/obj/item/weapon/lipstick/random + name = "lipstick" + +/obj/item/weapon/lipstick/random/New() + colour = pick("red","purple","jade","black") + name = "[colour] lipstick" + +/obj/item/weapon/lipstick/attack_self(mob/user as mob) + to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") + open = !open + if(open) + icon_state = "[initial(icon_state)]_[colour]" + else + icon_state = initial(icon_state) + +/obj/item/weapon/lipstick/attack(mob/M as mob, mob/user as mob) + if(!open) return + + if(!istype(M, /mob)) return + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.lip_style) //if they already have lipstick on + to_chat(user, "You need to wipe off the old lipstick first!") + return + if(H == user) + user.visible_message("[user] does their lips with \the [src].", \ + "You take a moment to apply \the [src]. Perfect!") + H.lip_style = colour + H.update_icons_body() + else + user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ + "You begin to apply \the [src].") + if(do_after(user, 20, H)) //user needs to keep their active hand, H does not. + user.visible_message("[user] does [H]'s lips with \the [src].", \ + "You apply \the [src].") + H.lip_style = colour + H.update_icons_body() + else + to_chat(user, "Where are the lips on that?") + +//you can wipe off lipstick with paper! see code/modules/paperwork/paper.dm, paper/attack() + +/obj/item/weapon/haircomb //sparklysheep's comb + name = "purple comb" + desc = "A pristine purple comb made from flexible plastic." + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + icon = 'icons/obj/items.dmi' + icon_state = "purplecomb" + +/obj/item/weapon/haircomb/attack_self(mob/living/user) + var/text = "person" + if(ishuman(user)) + var/mob/living/carbon/human/U = user + switch(U.identifying_gender) + if(MALE) + text = "guy" + if(FEMALE) + text = "lady" + else + switch(user.gender) + if(MALE) + text = "guy" + if(FEMALE) + text = "lady" + user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].") + +/obj/item/weapon/makeover + name = "makeover kit" + desc = "A tiny case containing a mirror and some contact lenses." + w_class = ITEMSIZE_TINY + icon = 'icons/obj/items.dmi' + icon_state = "trinketbox" + var/datum/tgui_module/appearance_changer/mirror/coskit/M + +/obj/item/weapon/makeover/Initialize() + . = ..() + M = new(src, null) + +/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob) + if(ishuman(user)) + to_chat(user, "You flip open \the [src] and begin to adjust your appearance.") + M.tgui_interact(user) + var/mob/living/carbon/human/H = user + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if(istype(E)) E.change_eye_color() \ No newline at end of file diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index ce634360942..ceafb06c73e 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -1,578 +1,578 @@ -/obj/item/weapon/dnainjector - name = "\improper DNA injector" - desc = "This injects the person with DNA." - icon = 'icons/obj/items.dmi' - icon_state = "dnainjector" - var/block=0 - var/datum/dna2/record/buf=null - var/s_time = 10.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - var/uses = 1 - var/nofail - var/is_bullet = 0 - var/inuse = 0 - - // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. - var/datatype=0 - var/value=0 - -/obj/item/weapon/dnainjector/New() - if(datatype && block) - buf=new - buf.dna=new - buf.types = datatype - buf.dna.ResetSE() - //testing("[name]: DNA2 SE blocks prior to SetValue: [english_list(buf.dna.SE)]") - SetValue(src.value) - //testing("[name]: DNA2 SE blocks after SetValue: [english_list(buf.dna.SE)]") - -/obj/item/weapon/dnainjector/proc/GetRealBlock(var/selblock) - if(selblock==0) - return block - else - return selblock - -/obj/item/weapon/dnainjector/proc/GetState(var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.GetSEState(real_block) - else - return buf.dna.GetUIState(real_block) - -/obj/item/weapon/dnainjector/proc/SetState(var/on, var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.SetSEState(real_block,on) - else - return buf.dna.SetUIState(real_block,on) - -/obj/item/weapon/dnainjector/proc/GetValue(var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.GetSEValue(real_block) - else - return buf.dna.GetUIValue(real_block) - -/obj/item/weapon/dnainjector/proc/SetValue(var/val,var/selblock=0) - var/real_block=GetRealBlock(selblock) - if(buf.types&DNA2_BUF_SE) - return buf.dna.SetSEValue(real_block,val) - else - return buf.dna.SetUIValue(real_block,val) - -/obj/item/weapon/dnainjector/proc/inject(mob/M as mob, mob/user as mob) - if(istype(M,/mob/living)) - var/mob/living/L = M - L.apply_effect(rand(5,20), IRRADIATE, check_protection = 0) - L.apply_damage(max(2,L.getCloneLoss()), CLONE) - - if (!(NOCLONE in M.mutations)) // prevents drained people from having their DNA changed - if (buf.types & DNA2_BUF_UI) - if (!block) //isolated block? - M.UpdateAppearance(buf.dna.UI.Copy()) - if (buf.types & DNA2_BUF_UE) //unique enzymes? yes - M.real_name = buf.dna.real_name - M.name = buf.dna.real_name - uses-- - else - M.dna.SetUIValue(block,src.GetValue()) - M.UpdateAppearance() - uses-- - if (buf.types & DNA2_BUF_SE) - if (!block) //isolated block? - M.dna.SE = buf.dna.SE.Copy() - M.dna.UpdateSE() - else - M.dna.SetSEValue(block,src.GetValue()) - domutcheck(M, null, block!=null) - uses-- - if(prob(5)) - trigger_side_effect(M) - - spawn(0)//this prevents the collapse of space-time continuum - if (user) - user.drop_from_inventory(src) - qdel(src) - return uses - -/obj/item/weapon/dnainjector/attack(mob/M as mob, mob/user as mob) - if (!istype(M, /mob)) - return - if (!usr.IsAdvancedToolUser()) - return - if(inuse) - return 0 - - user.visible_message("\The [user] is trying to inject \the [M] with \the [src]!") - inuse = 1 - s_time = world.time - spawn(50) - inuse = 0 - - if(!do_after(user,50)) - return - - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) - user.do_attack_animation(M) - - M.visible_message("\The [M] has been injected with \the [src] by \the [user].") - - var/mob/living/carbon/human/H = M - if(!istype(H)) - to_chat(user, "Apparently it didn't work...") - return - - // Used by admin log. - var/injected_with_monkey = "" - if((buf.types & DNA2_BUF_SE) && (block ? (GetState() && block == MONKEYBLOCK) : GetState(MONKEYBLOCK))) - injected_with_monkey = " (MONKEY)" - - add_attack_logs(user,M,"[injected_with_monkey] used the [name] on") - - // Apply the DNA shit. - inject(M, user) - return - -/obj/item/weapon/dnainjector/hulkmut - name = "\improper DNA injector (Hulk)" - desc = "This will make you big and strong, but give you a bad skin condition." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/hulkmut/New() - block = HULKBLOCK - ..() - -/obj/item/weapon/dnainjector/antihulk - name = "\improper DNA injector (Anti-Hulk)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antihulk/New() - block = HULKBLOCK - ..() - -/obj/item/weapon/dnainjector/xraymut - name = "\improper DNA injector (Xray)" - desc = "Finally you can see what the Site Manager does." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/xraymut/New() - block = XRAYBLOCK - ..() - -/obj/item/weapon/dnainjector/antixray - name = "\improper DNA injector (Anti-Xray)" - desc = "It will make you see harder." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antixray/New() - block = XRAYBLOCK - ..() - -/obj/item/weapon/dnainjector/firemut - name = "\improper DNA injector (Fire)" - desc = "Gives you fire." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/firemut/New() - block = FIREBLOCK - ..() - -/obj/item/weapon/dnainjector/antifire - name = "\improper DNA injector (Anti-Fire)" - desc = "Cures fire." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antifire/New() - block = FIREBLOCK - ..() - -/obj/item/weapon/dnainjector/telemut - name = "\improper DNA injector (Tele.)" - desc = "Super brain man!" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/telemut/New() - block = TELEBLOCK - ..() - -/obj/item/weapon/dnainjector/antitele - name = "\improper DNA injector (Anti-Tele.)" - desc = "Will make you not able to control your mind." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antitele/New() - block = TELEBLOCK - ..() - -/obj/item/weapon/dnainjector/nobreath - name = "\improper DNA injector (No Breath)" - desc = "Hold your breath and count to infinity." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/nobreath/New() - block = NOBREATHBLOCK - ..() - -/obj/item/weapon/dnainjector/antinobreath - name = "\improper DNA injector (Anti-No Breath)" - desc = "Hold your breath and count to 100." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antinobreath/New() - block = NOBREATHBLOCK - ..() - -/obj/item/weapon/dnainjector/remoteview - name = "\improper DNA injector (Remote View)" - desc = "Stare into the distance for a reason." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/remoteview/New() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/weapon/dnainjector/antiremoteview - name = "\improper DNA injector (Anti-Remote View)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiremoteview/New() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/weapon/dnainjector/regenerate - name = "\improper DNA injector (Regeneration)" - desc = "Healthy but hungry." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/regenerate/New() - block = REGENERATEBLOCK - ..() - -/obj/item/weapon/dnainjector/antiregenerate - name = "\improper DNA injector (Anti-Regeneration)" - desc = "Sickly but sated." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiregenerate/New() - block = REGENERATEBLOCK - ..() - -/obj/item/weapon/dnainjector/runfast - name = "\improper DNA injector (Increase Run)" - desc = "Running Man." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/runfast/New() - block = INCREASERUNBLOCK - ..() - -/obj/item/weapon/dnainjector/antirunfast - name = "\improper DNA injector (Anti-Increase Run)" - desc = "Walking Man." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antirunfast/New() - block = INCREASERUNBLOCK - ..() - -/obj/item/weapon/dnainjector/morph - name = "\improper DNA injector (Morph)" - desc = "A total makeover." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/morph/New() - block = MORPHBLOCK - ..() - -/obj/item/weapon/dnainjector/antimorph - name = "\improper DNA injector (Anti-Morph)" - desc = "Cures identity crisis." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antimorph/New() - block = MORPHBLOCK - ..() - -/obj/item/weapon/dnainjector/noprints - name = "\improper DNA injector (No Prints)" - desc = "Better than a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/noprints/New() - block = NOPRINTSBLOCK - ..() - -/obj/item/weapon/dnainjector/antinoprints - name = "\improper DNA injector (Anti-No Prints)" - desc = "Not quite as good as a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antinoprints/New() - block = NOPRINTSBLOCK - ..() - -/obj/item/weapon/dnainjector/insulation - name = "\improper DNA injector (Shock Immunity)" - desc = "Better than a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/insulation/New() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/weapon/dnainjector/antiinsulation - name = "\improper DNA injector (Anti-Shock Immunity)" - desc = "Not quite as good as a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiinsulation/New() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/weapon/dnainjector/midgit - name = "\improper DNA injector (Small Size)" - desc = "Makes you shrink." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/midgit/New() - block = SMALLSIZEBLOCK - ..() - -/obj/item/weapon/dnainjector/antimidgit - name = "\improper DNA injector (Anti-Small Size)" - desc = "Makes you grow. But not too much." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antimidgit/New() - block = SMALLSIZEBLOCK - ..() - -///////////////////////////////////// -/obj/item/weapon/dnainjector/antiglasses - name = "\improper DNA injector (Anti-Glasses)" - desc = "Toss away those glasses!" - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiglasses/New() - block = GLASSESBLOCK - ..() - -/obj/item/weapon/dnainjector/glassesmut - name = "\improper DNA injector (Glasses)" - desc = "Will make you need dorkish glasses." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/glassesmut/New() - block = GLASSESBLOCK - ..() - -/obj/item/weapon/dnainjector/epimut - name = "\improper DNA injector (Epi.)" - desc = "Shake shake shake the room!" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/epimut/New() - block = HEADACHEBLOCK - ..() - -/obj/item/weapon/dnainjector/antiepi - name = "\improper DNA injector (Anti-Epi.)" - desc = "Will fix you up from shaking the room." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiepi/New() - block = HEADACHEBLOCK - ..() - -/obj/item/weapon/dnainjector/anticough - name = "\improper DNA injector (Anti-Cough)" - desc = "Will stop that awful noise." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/anticough/New() - block = COUGHBLOCK - ..() - -/obj/item/weapon/dnainjector/coughmut - name = "\improper DNA injector (Cough)" - desc = "Will bring forth a sound of horror from your throat." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/coughmut/New() - block = COUGHBLOCK - ..() - -/obj/item/weapon/dnainjector/clumsymut - name = "\improper DNA injector (Clumsy)" - desc = "Makes clumsy minions." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/clumsymut/New() - block = CLUMSYBLOCK - ..() - -/obj/item/weapon/dnainjector/anticlumsy - name = "\improper DNA injector (Anti-Clumy)" - desc = "Cleans up confusion." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/anticlumsy/New() - block = CLUMSYBLOCK - ..() - -/obj/item/weapon/dnainjector/antitour - name = "\improper DNA injector (Anti-Tour.)" - desc = "Will cure tourrets." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antitour/New() - block = TWITCHBLOCK - ..() - -/obj/item/weapon/dnainjector/tourmut - name = "\improper DNA injector (Tour.)" - desc = "Gives you a nasty case off tourrets." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/tourmut/New() - block = TWITCHBLOCK - ..() - -/obj/item/weapon/dnainjector/stuttmut - name = "\improper DNA injector (Stutt.)" - desc = "Makes you s-s-stuttterrr" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/stuttmut/New() - block = NERVOUSBLOCK - ..() - -/obj/item/weapon/dnainjector/antistutt - name = "\improper DNA injector (Anti-Stutt.)" - desc = "Fixes that speaking impairment." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antistutt/New() - block = NERVOUSBLOCK - ..() - -/obj/item/weapon/dnainjector/blindmut - name = "\improper DNA injector (Blind)" - desc = "Makes you not see anything." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/blindmut/New() - block = BLINDBLOCK - ..() - -/obj/item/weapon/dnainjector/antiblind - name = "\improper DNA injector (Anti-Blind)" - desc = "ITS A MIRACLE!!!" - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antiblind/New() - block = BLINDBLOCK - ..() - -/obj/item/weapon/dnainjector/deafmut - name = "\improper DNA injector (Deaf)" - desc = "Sorry, what did you say?" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/deafmut/New() - block = DEAFBLOCK - ..() - -/obj/item/weapon/dnainjector/antideaf - name = "\improper DNA injector (Anti-Deaf)" - desc = "Will make you hear once more." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antideaf/New() - block = DEAFBLOCK - ..() - -/obj/item/weapon/dnainjector/hallucination - name = "\improper DNA injector (Halluctination)" - desc = "What you see isn't always what you get." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/hallucination/New() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/weapon/dnainjector/antihallucination - name = "\improper DNA injector (Anti-Hallucination)" - desc = "What you see is what you get." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/antihallucination/New() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/weapon/dnainjector/h2m - name = "\improper DNA injector (Human > Monkey)" - desc = "Will make you a flea bag." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/weapon/dnainjector/h2m/New() - block = MONKEYBLOCK - ..() - -/obj/item/weapon/dnainjector/m2h - name = "\improper DNA injector (Monkey > Human)" - desc = "Will make you...less hairy." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/weapon/dnainjector/m2h/New() - block = MONKEYBLOCK - ..() +/obj/item/weapon/dnainjector + name = "\improper DNA injector" + desc = "This injects the person with DNA." + icon = 'icons/obj/items.dmi' + icon_state = "dnainjector" + var/block=0 + var/datum/dna2/record/buf=null + var/s_time = 10.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + var/uses = 1 + var/nofail + var/is_bullet = 0 + var/inuse = 0 + + // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. + var/datatype=0 + var/value=0 + +/obj/item/weapon/dnainjector/New() + if(datatype && block) + buf=new + buf.dna=new + buf.types = datatype + buf.dna.ResetSE() + //testing("[name]: DNA2 SE blocks prior to SetValue: [english_list(buf.dna.SE)]") + SetValue(src.value) + //testing("[name]: DNA2 SE blocks after SetValue: [english_list(buf.dna.SE)]") + +/obj/item/weapon/dnainjector/proc/GetRealBlock(var/selblock) + if(selblock==0) + return block + else + return selblock + +/obj/item/weapon/dnainjector/proc/GetState(var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.GetSEState(real_block) + else + return buf.dna.GetUIState(real_block) + +/obj/item/weapon/dnainjector/proc/SetState(var/on, var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.SetSEState(real_block,on) + else + return buf.dna.SetUIState(real_block,on) + +/obj/item/weapon/dnainjector/proc/GetValue(var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.GetSEValue(real_block) + else + return buf.dna.GetUIValue(real_block) + +/obj/item/weapon/dnainjector/proc/SetValue(var/val,var/selblock=0) + var/real_block=GetRealBlock(selblock) + if(buf.types&DNA2_BUF_SE) + return buf.dna.SetSEValue(real_block,val) + else + return buf.dna.SetUIValue(real_block,val) + +/obj/item/weapon/dnainjector/proc/inject(mob/M as mob, mob/user as mob) + if(istype(M,/mob/living)) + var/mob/living/L = M + L.apply_effect(rand(5,20), IRRADIATE, check_protection = 0) + L.apply_damage(max(2,L.getCloneLoss()), CLONE) + + if (!(NOCLONE in M.mutations)) // prevents drained people from having their DNA changed + if (buf.types & DNA2_BUF_UI) + if (!block) //isolated block? + M.UpdateAppearance(buf.dna.UI.Copy()) + if (buf.types & DNA2_BUF_UE) //unique enzymes? yes + M.real_name = buf.dna.real_name + M.name = buf.dna.real_name + uses-- + else + M.dna.SetUIValue(block,src.GetValue()) + M.UpdateAppearance() + uses-- + if (buf.types & DNA2_BUF_SE) + if (!block) //isolated block? + M.dna.SE = buf.dna.SE.Copy() + M.dna.UpdateSE() + else + M.dna.SetSEValue(block,src.GetValue()) + domutcheck(M, null, block!=null) + uses-- + if(prob(5)) + trigger_side_effect(M) + + spawn(0)//this prevents the collapse of space-time continuum + if (user) + user.drop_from_inventory(src) + qdel(src) + return uses + +/obj/item/weapon/dnainjector/attack(mob/M as mob, mob/user as mob) + if (!istype(M, /mob)) + return + if (!usr.IsAdvancedToolUser()) + return + if(inuse) + return 0 + + user.visible_message("\The [user] is trying to inject \the [M] with \the [src]!") + inuse = 1 + s_time = world.time + spawn(50) + inuse = 0 + + if(!do_after(user,50)) + return + + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(M) + + M.visible_message("\The [M] has been injected with \the [src] by \the [user].") + + var/mob/living/carbon/human/H = M + if(!istype(H)) + to_chat(user, "Apparently it didn't work...") + return + + // Used by admin log. + var/injected_with_monkey = "" + if((buf.types & DNA2_BUF_SE) && (block ? (GetState() && block == MONKEYBLOCK) : GetState(MONKEYBLOCK))) + injected_with_monkey = " (MONKEY)" + + add_attack_logs(user,M,"[injected_with_monkey] used the [name] on") + + // Apply the DNA shit. + inject(M, user) + return + +/obj/item/weapon/dnainjector/hulkmut + name = "\improper DNA injector (Hulk)" + desc = "This will make you big and strong, but give you a bad skin condition." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/hulkmut/New() + block = HULKBLOCK + ..() + +/obj/item/weapon/dnainjector/antihulk + name = "\improper DNA injector (Anti-Hulk)" + desc = "Cures green skin." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antihulk/New() + block = HULKBLOCK + ..() + +/obj/item/weapon/dnainjector/xraymut + name = "\improper DNA injector (Xray)" + desc = "Finally you can see what the Site Manager does." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/xraymut/New() + block = XRAYBLOCK + ..() + +/obj/item/weapon/dnainjector/antixray + name = "\improper DNA injector (Anti-Xray)" + desc = "It will make you see harder." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antixray/New() + block = XRAYBLOCK + ..() + +/obj/item/weapon/dnainjector/firemut + name = "\improper DNA injector (Fire)" + desc = "Gives you fire." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/firemut/New() + block = FIREBLOCK + ..() + +/obj/item/weapon/dnainjector/antifire + name = "\improper DNA injector (Anti-Fire)" + desc = "Cures fire." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antifire/New() + block = FIREBLOCK + ..() + +/obj/item/weapon/dnainjector/telemut + name = "\improper DNA injector (Tele.)" + desc = "Super brain man!" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/telemut/New() + block = TELEBLOCK + ..() + +/obj/item/weapon/dnainjector/antitele + name = "\improper DNA injector (Anti-Tele.)" + desc = "Will make you not able to control your mind." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antitele/New() + block = TELEBLOCK + ..() + +/obj/item/weapon/dnainjector/nobreath + name = "\improper DNA injector (No Breath)" + desc = "Hold your breath and count to infinity." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/nobreath/New() + block = NOBREATHBLOCK + ..() + +/obj/item/weapon/dnainjector/antinobreath + name = "\improper DNA injector (Anti-No Breath)" + desc = "Hold your breath and count to 100." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antinobreath/New() + block = NOBREATHBLOCK + ..() + +/obj/item/weapon/dnainjector/remoteview + name = "\improper DNA injector (Remote View)" + desc = "Stare into the distance for a reason." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/remoteview/New() + block = REMOTEVIEWBLOCK + ..() + +/obj/item/weapon/dnainjector/antiremoteview + name = "\improper DNA injector (Anti-Remote View)" + desc = "Cures green skin." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiremoteview/New() + block = REMOTEVIEWBLOCK + ..() + +/obj/item/weapon/dnainjector/regenerate + name = "\improper DNA injector (Regeneration)" + desc = "Healthy but hungry." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/regenerate/New() + block = REGENERATEBLOCK + ..() + +/obj/item/weapon/dnainjector/antiregenerate + name = "\improper DNA injector (Anti-Regeneration)" + desc = "Sickly but sated." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiregenerate/New() + block = REGENERATEBLOCK + ..() + +/obj/item/weapon/dnainjector/runfast + name = "\improper DNA injector (Increase Run)" + desc = "Running Man." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/runfast/New() + block = INCREASERUNBLOCK + ..() + +/obj/item/weapon/dnainjector/antirunfast + name = "\improper DNA injector (Anti-Increase Run)" + desc = "Walking Man." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antirunfast/New() + block = INCREASERUNBLOCK + ..() + +/obj/item/weapon/dnainjector/morph + name = "\improper DNA injector (Morph)" + desc = "A total makeover." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/morph/New() + block = MORPHBLOCK + ..() + +/obj/item/weapon/dnainjector/antimorph + name = "\improper DNA injector (Anti-Morph)" + desc = "Cures identity crisis." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antimorph/New() + block = MORPHBLOCK + ..() + +/obj/item/weapon/dnainjector/noprints + name = "\improper DNA injector (No Prints)" + desc = "Better than a pair of budget insulated gloves." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/noprints/New() + block = NOPRINTSBLOCK + ..() + +/obj/item/weapon/dnainjector/antinoprints + name = "\improper DNA injector (Anti-No Prints)" + desc = "Not quite as good as a pair of budget insulated gloves." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antinoprints/New() + block = NOPRINTSBLOCK + ..() + +/obj/item/weapon/dnainjector/insulation + name = "\improper DNA injector (Shock Immunity)" + desc = "Better than a pair of real insulated gloves." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/insulation/New() + block = SHOCKIMMUNITYBLOCK + ..() + +/obj/item/weapon/dnainjector/antiinsulation + name = "\improper DNA injector (Anti-Shock Immunity)" + desc = "Not quite as good as a pair of real insulated gloves." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiinsulation/New() + block = SHOCKIMMUNITYBLOCK + ..() + +/obj/item/weapon/dnainjector/midgit + name = "\improper DNA injector (Small Size)" + desc = "Makes you shrink." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/midgit/New() + block = SMALLSIZEBLOCK + ..() + +/obj/item/weapon/dnainjector/antimidgit + name = "\improper DNA injector (Anti-Small Size)" + desc = "Makes you grow. But not too much." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antimidgit/New() + block = SMALLSIZEBLOCK + ..() + +///////////////////////////////////// +/obj/item/weapon/dnainjector/antiglasses + name = "\improper DNA injector (Anti-Glasses)" + desc = "Toss away those glasses!" + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiglasses/New() + block = GLASSESBLOCK + ..() + +/obj/item/weapon/dnainjector/glassesmut + name = "\improper DNA injector (Glasses)" + desc = "Will make you need dorkish glasses." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/glassesmut/New() + block = GLASSESBLOCK + ..() + +/obj/item/weapon/dnainjector/epimut + name = "\improper DNA injector (Epi.)" + desc = "Shake shake shake the room!" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/epimut/New() + block = HEADACHEBLOCK + ..() + +/obj/item/weapon/dnainjector/antiepi + name = "\improper DNA injector (Anti-Epi.)" + desc = "Will fix you up from shaking the room." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiepi/New() + block = HEADACHEBLOCK + ..() + +/obj/item/weapon/dnainjector/anticough + name = "\improper DNA injector (Anti-Cough)" + desc = "Will stop that awful noise." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/anticough/New() + block = COUGHBLOCK + ..() + +/obj/item/weapon/dnainjector/coughmut + name = "\improper DNA injector (Cough)" + desc = "Will bring forth a sound of horror from your throat." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/coughmut/New() + block = COUGHBLOCK + ..() + +/obj/item/weapon/dnainjector/clumsymut + name = "\improper DNA injector (Clumsy)" + desc = "Makes clumsy minions." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/clumsymut/New() + block = CLUMSYBLOCK + ..() + +/obj/item/weapon/dnainjector/anticlumsy + name = "\improper DNA injector (Anti-Clumy)" + desc = "Cleans up confusion." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/anticlumsy/New() + block = CLUMSYBLOCK + ..() + +/obj/item/weapon/dnainjector/antitour + name = "\improper DNA injector (Anti-Tour.)" + desc = "Will cure tourrets." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antitour/New() + block = TWITCHBLOCK + ..() + +/obj/item/weapon/dnainjector/tourmut + name = "\improper DNA injector (Tour.)" + desc = "Gives you a nasty case off tourrets." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/tourmut/New() + block = TWITCHBLOCK + ..() + +/obj/item/weapon/dnainjector/stuttmut + name = "\improper DNA injector (Stutt.)" + desc = "Makes you s-s-stuttterrr" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/stuttmut/New() + block = NERVOUSBLOCK + ..() + +/obj/item/weapon/dnainjector/antistutt + name = "\improper DNA injector (Anti-Stutt.)" + desc = "Fixes that speaking impairment." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antistutt/New() + block = NERVOUSBLOCK + ..() + +/obj/item/weapon/dnainjector/blindmut + name = "\improper DNA injector (Blind)" + desc = "Makes you not see anything." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/blindmut/New() + block = BLINDBLOCK + ..() + +/obj/item/weapon/dnainjector/antiblind + name = "\improper DNA injector (Anti-Blind)" + desc = "ITS A MIRACLE!!!" + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antiblind/New() + block = BLINDBLOCK + ..() + +/obj/item/weapon/dnainjector/deafmut + name = "\improper DNA injector (Deaf)" + desc = "Sorry, what did you say?" + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/deafmut/New() + block = DEAFBLOCK + ..() + +/obj/item/weapon/dnainjector/antideaf + name = "\improper DNA injector (Anti-Deaf)" + desc = "Will make you hear once more." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antideaf/New() + block = DEAFBLOCK + ..() + +/obj/item/weapon/dnainjector/hallucination + name = "\improper DNA injector (Halluctination)" + desc = "What you see isn't always what you get." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/hallucination/New() + block = HALLUCINATIONBLOCK + ..() + +/obj/item/weapon/dnainjector/antihallucination + name = "\improper DNA injector (Anti-Hallucination)" + desc = "What you see is what you get." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/antihallucination/New() + block = HALLUCINATIONBLOCK + ..() + +/obj/item/weapon/dnainjector/h2m + name = "\improper DNA injector (Human > Monkey)" + desc = "Will make you a flea bag." + datatype = DNA2_BUF_SE + value = 0xFFF + +/obj/item/weapon/dnainjector/h2m/New() + block = MONKEYBLOCK + ..() + +/obj/item/weapon/dnainjector/m2h + name = "\improper DNA injector (Monkey > Human)" + desc = "Will make you...less hairy." + datatype = DNA2_BUF_SE + value = 0x001 + +/obj/item/weapon/dnainjector/m2h/New() + block = MONKEYBLOCK + ..() diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 0e9250611ec..fcb45bad9c1 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -1,119 +1,119 @@ -/obj/item/weapon/plastique - name = "plastic explosives" - desc = "Used to put holes in specific areas without too much extra hole." - gender = PLURAL - icon = 'icons/obj/assemblies.dmi' - icon_state = "plastic-explosive0" - item_state = "plasticx" - flags = NOBLUDGEON - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_ILLEGAL = 2) - var/datum/wires/explosive/c4/wires = null - var/timer = 10 - var/atom/target = null - var/open_panel = 0 - var/image_overlay = null - var/blast_dev = -1 - var/blast_heavy = -1 - var/blast_light = 2 - var/blast_flash = 3 - -/obj/item/weapon/plastique/New() - wires = new(src) - image_overlay = image('icons/obj/assemblies.dmi', "plastic-explosive2") - ..() - -/obj/item/weapon/plastique/Destroy() - qdel(wires) - wires = null - return ..() - -/obj/item/weapon/plastique/attackby(var/obj/item/I, var/mob/user) - if(I.is_screwdriver()) - open_panel = !open_panel - to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") - playsound(src, I.usesound, 50, 1) - else if(I.is_wirecutter() || istype(I, /obj/item/device/multitool) || istype(I, /obj/item/device/assembly/signaler )) - wires.Interact(user) - else - ..() - -/obj/item/weapon/plastique/attack_self(mob/user as mob) - var/newtime = tgui_input_number(usr, "Please set the timer.", "Timer", 10, 60000, 10) - if(user.get_active_hand() == src) - newtime = CLAMP(newtime, 10, 60000) - timer = newtime - to_chat(user, "Timer set for [timer] seconds.") - -/obj/item/weapon/plastique/afterattack(atom/movable/target, mob/user, flag) - if (!flag) - return - if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under)) - return - to_chat(user, "Planting explosives...") - user.do_attack_animation(target) - - if(do_after(user, 50) && in_range(user, target)) - user.drop_item() - src.target = target - loc = null - - if (ismob(target)) - add_attack_logs(user, target, "planted [name] on with [timer] second fuse") - user.visible_message("[user.name] finished planting an explosive on [target.name]!") - else - message_admins("[key_name(user, user.client)](?) planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - JMP) with [timer] second fuse",0,1) - log_game("[key_name(user)] planted [src.name] on [target.name] at ([target.x],[target.y],[target.z]) with [timer] second fuse") - - target.add_overlay(image_overlay, TRUE) - to_chat(user, "Bomb has been planted. Timer counting down from [timer].") - spawn(timer*10) - explode(get_turf(target)) - -/obj/item/weapon/plastique/proc/explode(var/location) - if(!target) - target = get_atom_on_turf(src) - if(!target) - target = src - if(location) - explosion(location, blast_dev, blast_heavy, blast_light, blast_flash) - - if(target) - if (istype(target, /turf/simulated/wall)) - var/turf/simulated/wall/W = target - W.dismantle_wall(1,1,1) - else if(istype(target, /mob/living)) - target.ex_act(2) // c4 can't gib mobs anymore. - else - target.ex_act(1) - if(target) - target.cut_overlay(image_overlay, TRUE) - qdel(src) - -/obj/item/weapon/plastique/attack(mob/M as mob, mob/user as mob, def_zone) - return - -/obj/item/weapon/plastique/seismic - name = "seismic charge" - desc = "Used to dig holes in specific areas without too much extra hole." - - blast_heavy = 2 - blast_light = 4 - blast_flash = 7 - -/obj/item/weapon/plastique/seismic/attackby(var/obj/item/I, var/mob/user) - . = ..() - if(open_panel) - if(istype(I, /obj/item/weapon/stock_parts/micro_laser)) - var/obj/item/weapon/stock_parts/SP = I - var/new_blast_power = max(1, round(SP.rating / 2) + 1) - if(new_blast_power > blast_heavy) - to_chat(user, "You install \the [I] into \the [src].") - user.drop_from_inventory(I) - qdel(I) - blast_heavy = new_blast_power - blast_light = blast_heavy + round(new_blast_power * 0.5) - blast_flash = blast_light + round(new_blast_power * 0.75) - else - to_chat(user, "The [I] is not any better than the component already installed into this charge!") - return . +/obj/item/weapon/plastique + name = "plastic explosives" + desc = "Used to put holes in specific areas without too much extra hole." + gender = PLURAL + icon = 'icons/obj/assemblies.dmi' + icon_state = "plastic-explosive0" + item_state = "plasticx" + flags = NOBLUDGEON + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_ILLEGAL = 2) + var/datum/wires/explosive/c4/wires = null + var/timer = 10 + var/atom/target = null + var/open_panel = 0 + var/image_overlay = null + var/blast_dev = -1 + var/blast_heavy = -1 + var/blast_light = 2 + var/blast_flash = 3 + +/obj/item/weapon/plastique/New() + wires = new(src) + image_overlay = image('icons/obj/assemblies.dmi', "plastic-explosive2") + ..() + +/obj/item/weapon/plastique/Destroy() + qdel(wires) + wires = null + return ..() + +/obj/item/weapon/plastique/attackby(var/obj/item/I, var/mob/user) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) + open_panel = !open_panel + to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") + playsound(src, I.usesound, 50, 1) + else if(I.has_tool_quality(TOOL_WIRECUTTER) || istype(I, /obj/item/device/multitool) || istype(I, /obj/item/device/assembly/signaler )) + wires.Interact(user) + else + ..() + +/obj/item/weapon/plastique/attack_self(mob/user as mob) + var/newtime = tgui_input_number(usr, "Please set the timer.", "Timer", 10, 60000, 10) + if(user.get_active_hand() == src) + newtime = CLAMP(newtime, 10, 60000) + timer = newtime + to_chat(user, "Timer set for [timer] seconds.") + +/obj/item/weapon/plastique/afterattack(atom/movable/target, mob/user, flag) + if (!flag) + return + if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under)) + return + to_chat(user, "Planting explosives...") + user.do_attack_animation(target) + + if(do_after(user, 50) && in_range(user, target)) + user.drop_item() + src.target = target + loc = null + + if (ismob(target)) + add_attack_logs(user, target, "planted [name] on with [timer] second fuse") + user.visible_message("[user.name] finished planting an explosive on [target.name]!") + else + message_admins("[key_name(user, user.client)](?) planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - JMP) with [timer] second fuse",0,1) + log_game("[key_name(user)] planted [src.name] on [target.name] at ([target.x],[target.y],[target.z]) with [timer] second fuse") + + target.add_overlay(image_overlay, TRUE) + to_chat(user, "Bomb has been planted. Timer counting down from [timer].") + spawn(timer*10) + explode(get_turf(target)) + +/obj/item/weapon/plastique/proc/explode(var/location) + if(!target) + target = get_atom_on_turf(src) + if(!target) + target = src + if(location) + explosion(location, blast_dev, blast_heavy, blast_light, blast_flash) + + if(target) + if (istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target + W.dismantle_wall(1,1,1) + else if(istype(target, /mob/living)) + target.ex_act(2) // c4 can't gib mobs anymore. + else + target.ex_act(1) + if(target) + target.cut_overlay(image_overlay, TRUE) + qdel(src) + +/obj/item/weapon/plastique/attack(mob/M as mob, mob/user as mob, def_zone) + return + +/obj/item/weapon/plastique/seismic + name = "seismic charge" + desc = "Used to dig holes in specific areas without too much extra hole." + + blast_heavy = 2 + blast_light = 4 + blast_flash = 7 + +/obj/item/weapon/plastique/seismic/attackby(var/obj/item/I, var/mob/user) + . = ..() + if(open_panel) + if(istype(I, /obj/item/weapon/stock_parts/micro_laser)) + var/obj/item/weapon/stock_parts/SP = I + var/new_blast_power = max(1, round(SP.rating / 2) + 1) + if(new_blast_power > blast_heavy) + to_chat(user, "You install \the [I] into \the [src].") + user.drop_from_inventory(I) + qdel(I) + blast_heavy = new_blast_power + blast_light = blast_heavy + round(new_blast_power * 0.5) + blast_flash = blast_light + round(new_blast_power * 0.75) + else + to_chat(user, "The [I] is not any better than the component already installed into this charge!") + return . diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 4b33d5bf60f..abd80e61ee0 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -1,144 +1,144 @@ -/obj/item/weapon/extinguisher - name = "fire extinguisher" - desc = "A traditional red fire extinguisher." - icon = 'icons/obj/items.dmi' - icon_state = "fire_extinguisher0" - item_state = "fire_extinguisher" - hitsound = 'sound/weapons/smash.ogg' - throwforce = 10 - w_class = ITEMSIZE_NORMAL - throw_speed = 2 - throw_range = 10 - force = 10 - matter = list(MAT_STEEL = 90) - attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") - drop_sound = 'sound/items/drop/gascan.ogg' - pickup_sound = 'sound/items/pickup/gascan.ogg' - - var/spray_particles = 3 - var/spray_amount = 10 //units of liquid per particle - var/max_water = 300 - var/last_use = 1.0 - var/safety = 1 - var/sprite_name = "fire_extinguisher" - var/rand_overlays = 6 - -/obj/item/weapon/extinguisher/mini - name = "fire extinguisher" - desc = "A light and compact fibreglass-framed model fire extinguisher." - icon_state = "miniFE0" - item_state = "miniFE" - hitsound = null //it is much lighter, after all. - throwforce = 2 - w_class = ITEMSIZE_SMALL - force = 3.0 - max_water = 150 - spray_particles = 3 - sprite_name = "miniFE" - rand_overlays = 0 - -/obj/item/weapon/extinguisher/atmo - name = "atmospheric fire extinguisher" - desc = "A heavy duty fire extinguisher meant to fight large fires." - icon_state = "atmos_extinguisher0" - item_state = "atmos_extinguisher" - throwforce = 12 - w_class = ITEMSIZE_LARGE - force = 3.0 - max_water = 600 - spray_particles = 3 - sprite_name = "atmos_extinguisher" - rand_overlays = 0 - -/obj/item/weapon/extinguisher/Initialize() - create_reagents(max_water) - reagents.add_reagent("firefoam", max_water) - if(rand_overlays) - var/choice = rand(1,rand_overlays) - add_overlay("[item_state]O[choice]") - . = ..() - -/obj/item/weapon/extinguisher/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - . += "[src] has [src.reagents.total_volume] units of foam left!" - -/obj/item/weapon/extinguisher/attack_self(mob/user as mob) - safety = !safety - icon_state = "[sprite_name][!safety]" - desc = "The safety is [safety ? "on" : "off"]." - to_chat(user, "The safety is [safety ? "on" : "off"].") - -/obj/item/weapon/extinguisher/proc/propel_object(var/obj/O, mob/user, movementdirection) - if(O.anchored) return - - var/obj/structure/bed/chair/C - if(istype(O, /obj/structure/bed/chair)) - C = O - - var/list/move_speed = list(1, 1, 1, 2, 2, 3) - for(var/i in 1 to 6) - if(C) C.propelled = (6-i) - O.Move(get_step(user,movementdirection), movementdirection) - sleep(move_speed[i]) - - //additional movement - for(var/i in 1 to 3) - O.Move(get_step(user,movementdirection), movementdirection) - sleep(3) - -/obj/item/weapon/extinguisher/afterattack(var/atom/target, var/mob/user, var/flag) - //TODO; Add support for reagents in water. - - if( istype(target, /obj/structure/reagent_dispensers) && flag) - var/obj/o = target - var/amount = o.reagents.trans_to_obj(src, 50) - to_chat(user, "You fill [src] with [amount] units of the contents of [target].") - playsound(src, 'sound/effects/refill.ogg', 50, 1, -6) - return - - if (!safety) - if (src.reagents.total_volume < 1) - to_chat(usr, "\The [src] is empty.") - return - - if (world.time < src.last_use + 20) - return - - src.last_use = world.time - - playsound(src, 'sound/effects/extinguish.ogg', 75, 1, -3) - - var/direction = get_dir(src,target) - - if(user.buckled && isobj(user.buckled)) - spawn(0) - propel_object(user.buckled, user, turn(direction,180)) - - var/turf/T = get_turf(target) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - - var/list/the_targets = list(T,T1,T2) - - for(var/a = 1 to spray_particles) - spawn(0) - if(!src || !reagents.total_volume) return - - var/obj/effect/effect/water/W = new /obj/effect/effect/water(get_turf(src)) - var/turf/my_target - if(a <= the_targets.len) - my_target = the_targets[a] - else - my_target = pick(the_targets) - W.create_reagents(spray_amount) - reagents.trans_to_obj(W, spray_amount) - W.set_color() - W.set_up(my_target) - - if((istype(usr.loc, /turf/space)) || (usr.lastarea.has_gravity == 0)) - user.inertia_dir = get_dir(target, user) - step(user, user.inertia_dir) - else - return ..() - return +/obj/item/weapon/extinguisher + name = "fire extinguisher" + desc = "A traditional red fire extinguisher." + icon = 'icons/obj/items.dmi' + icon_state = "fire_extinguisher0" + item_state = "fire_extinguisher" + hitsound = 'sound/weapons/smash.ogg' + throwforce = 10 + w_class = ITEMSIZE_NORMAL + throw_speed = 2 + throw_range = 10 + force = 10 + matter = list(MAT_STEEL = 90) + attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") + drop_sound = 'sound/items/drop/gascan.ogg' + pickup_sound = 'sound/items/pickup/gascan.ogg' + + var/spray_particles = 3 + var/spray_amount = 10 //units of liquid per particle + var/max_water = 300 + var/last_use = 1.0 + var/safety = 1 + var/sprite_name = "fire_extinguisher" + var/rand_overlays = 6 + +/obj/item/weapon/extinguisher/mini + name = "fire extinguisher" + desc = "A light and compact fibreglass-framed model fire extinguisher." + icon_state = "miniFE0" + item_state = "miniFE" + hitsound = null //it is much lighter, after all. + throwforce = 2 + w_class = ITEMSIZE_SMALL + force = 3.0 + max_water = 150 + spray_particles = 3 + sprite_name = "miniFE" + rand_overlays = 0 + +/obj/item/weapon/extinguisher/atmo + name = "atmospheric fire extinguisher" + desc = "A heavy duty fire extinguisher meant to fight large fires." + icon_state = "atmos_extinguisher0" + item_state = "atmos_extinguisher" + throwforce = 12 + w_class = ITEMSIZE_LARGE + force = 3.0 + max_water = 600 + spray_particles = 3 + sprite_name = "atmos_extinguisher" + rand_overlays = 0 + +/obj/item/weapon/extinguisher/Initialize() + create_reagents(max_water) + reagents.add_reagent("firefoam", max_water) + if(rand_overlays) + var/choice = rand(1,rand_overlays) + add_overlay("[item_state]O[choice]") + . = ..() + +/obj/item/weapon/extinguisher/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + . += "[src] has [src.reagents.total_volume] units of foam left!" + +/obj/item/weapon/extinguisher/attack_self(mob/user as mob) + safety = !safety + icon_state = "[sprite_name][!safety]" + desc = "The safety is [safety ? "on" : "off"]." + to_chat(user, "The safety is [safety ? "on" : "off"].") + +/obj/item/weapon/extinguisher/proc/propel_object(var/obj/O, mob/user, movementdirection) + if(O.anchored) return + + var/obj/structure/bed/chair/C + if(istype(O, /obj/structure/bed/chair)) + C = O + + var/list/move_speed = list(1, 1, 1, 2, 2, 3) + for(var/i in 1 to 6) + if(C) C.propelled = (6-i) + O.Move(get_step(user,movementdirection), movementdirection) + sleep(move_speed[i]) + + //additional movement + for(var/i in 1 to 3) + O.Move(get_step(user,movementdirection), movementdirection) + sleep(3) + +/obj/item/weapon/extinguisher/afterattack(var/atom/target, var/mob/user, var/flag) + //TODO; Add support for reagents in water. + + if( istype(target, /obj/structure/reagent_dispensers) && flag) + var/obj/o = target + var/amount = o.reagents.trans_to_obj(src, 50) + to_chat(user, "You fill [src] with [amount] units of the contents of [target].") + playsound(src, 'sound/effects/refill.ogg', 50, 1, -6) + return + + if (!safety) + if (src.reagents.total_volume < 1) + to_chat(usr, "\The [src] is empty.") + return + + if (world.time < src.last_use + 20) + return + + src.last_use = world.time + + playsound(src, 'sound/effects/extinguish.ogg', 75, 1, -3) + + var/direction = get_dir(src,target) + + if(user.buckled && isobj(user.buckled)) + spawn(0) + propel_object(user.buckled, user, turn(direction,180)) + + var/turf/T = get_turf(target) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + + var/list/the_targets = list(T,T1,T2) + + for(var/a = 1 to spray_particles) + spawn(0) + if(!src || !reagents.total_volume) return + + var/obj/effect/effect/water/W = new /obj/effect/effect/water(get_turf(src)) + var/turf/my_target + if(a <= the_targets.len) + my_target = the_targets[a] + else + my_target = pick(the_targets) + W.create_reagents(spray_amount) + reagents.trans_to_obj(W, spray_amount) + W.set_color() + W.set_up(my_target) + + if((istype(usr.loc, /turf/space)) || (usr.lastarea.has_gravity == 0)) + user.inertia_dir = get_dir(target, user) + step(user, user.inertia_dir) + else + return ..() + return diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index b20a44650ef..35dd4bfdf65 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -1,204 +1,204 @@ -/obj/item/weapon/flamethrower - name = "flamethrower" - desc = "You are a firestarter!" - icon = 'icons/obj/flamethrower.dmi' - icon_state = "flamethrowerbase" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', - ) - item_state = "flamethrower_0" - force = 3.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_COMBAT = 1, TECH_PHORON = 1) - matter = list(MAT_STEEL = 500) - var/status = 0 - var/throw_amount = 100 - var/lit = 0 //on or off - var/operating = 0//cooldown - var/turf/previousturf = null - var/obj/item/weapon/weldingtool/weldtool = null - var/obj/item/device/assembly/igniter/igniter = null - var/obj/item/weapon/tank/phoron/ptank = null - - -/obj/item/weapon/flamethrower/Destroy() - QDEL_NULL(weldtool) - QDEL_NULL(igniter) - QDEL_NULL(ptank) - . = ..() - -/obj/item/weapon/flamethrower/process() - if(!lit) - STOP_PROCESSING(SSobj, src) - return null - var/turf/location = loc - if(istype(location, /mob/)) - var/mob/living/M = location - if(M.item_is_in_hands(src)) - location = M.loc - if(isturf(location)) //start a fire if possible - location.hotspot_expose(700, 2) - return - - -/obj/item/weapon/flamethrower/update_icon() - cut_overlays() - if(igniter) - add_overlay("+igniter[status]") - if(ptank) - add_overlay("+ptank") - if(lit) - add_overlay("+lit") - item_state = "flamethrower_1" - else - item_state = "flamethrower_0" - return - -/obj/item/weapon/flamethrower/afterattack(atom/target, mob/user, proximity) - if(!proximity) return - // Make sure our user is still holding us - if(user && user.get_active_hand() == src) - var/turf/target_turf = get_turf(target) - if(target_turf) - var/turflist = getline(user, target_turf) - flame_turf(turflist) - -/obj/item/weapon/flamethrower/attackby(obj/item/W as obj, mob/user as mob) - if(user.stat || user.restrained() || user.lying) return - if(W.is_wrench() && !status)//Taking this apart - var/turf/T = get_turf(src) - if(weldtool) - weldtool.loc = T - weldtool = null - if(igniter) - igniter.loc = T - igniter = null - if(ptank) - ptank.loc = T - ptank = null - new /obj/item/stack/rods(T) - qdel(src) - return - - if(W.is_screwdriver() && igniter && !lit) - status = !status - to_chat(user, "[igniter] is now [status ? "secured" : "unsecured"]!") - update_icon() - return - - if(isigniter(W)) - var/obj/item/device/assembly/igniter/I = W - if(I.secured) return - if(igniter) return - user.drop_item() - I.loc = src - igniter = I - update_icon() - return - - if(istype(W,/obj/item/weapon/tank/phoron)) - if(ptank) - to_chat(user, "There appears to already be a phoron tank loaded in [src]!") - return - user.drop_item() - ptank = W - W.loc = src - update_icon() - return - - ..() - return - - -/obj/item/weapon/flamethrower/attack_self(mob/user as mob) - if(user.stat || user.restrained() || user.lying) return - user.set_machine(src) - if(!ptank) - to_chat(user, "Attach a phoron tank first!") - return - var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"])
                    \n Tank Pressure: [ptank.air_contents.return_pressure()]
                    \nAmount to throw: - - - [throw_amount] + + +
                    \nRemove phorontank - Close
                    ") - user << browse(dat, "window=flamethrower;size=600x300") - onclose(user, "flamethrower") - return - - -/obj/item/weapon/flamethrower/Topic(href,href_list[]) - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=flamethrower") - return - if(usr.stat || usr.restrained() || usr.lying) return - usr.set_machine(src) - if(href_list["light"]) - if(!ptank) return - if(ptank.air_contents.gas["phoron"] < 1) return - if(!status) return - lit = !lit - if(lit) - START_PROCESSING(SSobj, src) - if(href_list["amount"]) - throw_amount = throw_amount + text2num(href_list["amount"]) - throw_amount = max(50, min(5000, throw_amount)) - if(href_list["remove"]) - if(!ptank) return - usr.put_in_hands(ptank) - ptank = null - lit = 0 - usr.unset_machine() - usr << browse(null, "window=flamethrower") - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - update_icon() - return - - -//Called from turf.dm turf/dblclick -/obj/item/weapon/flamethrower/proc/flame_turf(turflist) - if(!lit || operating) return - operating = 1 - for(var/turf/T in turflist) - if(T.density || istype(T, /turf/space)) - break - if(!previousturf && length(turflist)>1) - previousturf = get_turf(src) - continue //so we don't burn the tile we be standin on - if(previousturf && LinkBlocked(previousturf, T)) - break - ignite_turf(T) - sleep(1) - previousturf = null - operating = 0 - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - return - - -/obj/item/weapon/flamethrower/proc/ignite_turf(turf/target) - //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... - //Transfer 5% of current tank air contents to turf - var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100)) - //air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is mischievious and I could probably make it work without fucking it up like this, but there you have it. -- TLE - new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas["phoron"],get_dir(loc,target)) - air_transfer.gas["phoron"] = 0 - target.assume_air(air_transfer) - //Burn it based on transfered gas - //target.hotspot_expose(part4.air_contents.temperature*2,300) - target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE - //location.hotspot_expose(1000,500,1) - return - -/obj/item/weapon/flamethrower/full/New(var/loc) - ..() - weldtool = new /obj/item/weapon/weldingtool(src) - weldtool.status = 0 - igniter = new /obj/item/device/assembly/igniter(src) - igniter.secured = 0 - status = 1 - update_icon() - return +/obj/item/weapon/flamethrower + name = "flamethrower" + desc = "You are a firestarter!" + icon = 'icons/obj/flamethrower.dmi' + icon_state = "flamethrowerbase" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', + ) + item_state = "flamethrower_0" + force = 3.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_COMBAT = 1, TECH_PHORON = 1) + matter = list(MAT_STEEL = 500) + var/status = 0 + var/throw_amount = 100 + var/lit = 0 //on or off + var/operating = 0//cooldown + var/turf/previousturf = null + var/obj/item/weapon/weldingtool/weldtool = null + var/obj/item/device/assembly/igniter/igniter = null + var/obj/item/weapon/tank/phoron/ptank = null + + +/obj/item/weapon/flamethrower/Destroy() + QDEL_NULL(weldtool) + QDEL_NULL(igniter) + QDEL_NULL(ptank) + . = ..() + +/obj/item/weapon/flamethrower/process() + if(!lit) + STOP_PROCESSING(SSobj, src) + return null + var/turf/location = loc + if(istype(location, /mob/)) + var/mob/living/M = location + if(M.item_is_in_hands(src)) + location = M.loc + if(isturf(location)) //start a fire if possible + location.hotspot_expose(700, 2) + return + + +/obj/item/weapon/flamethrower/update_icon() + cut_overlays() + if(igniter) + add_overlay("+igniter[status]") + if(ptank) + add_overlay("+ptank") + if(lit) + add_overlay("+lit") + item_state = "flamethrower_1" + else + item_state = "flamethrower_0" + return + +/obj/item/weapon/flamethrower/afterattack(atom/target, mob/user, proximity) + if(!proximity) return + // Make sure our user is still holding us + if(user && user.get_active_hand() == src) + var/turf/target_turf = get_turf(target) + if(target_turf) + var/turflist = getline(user, target_turf) + flame_turf(turflist) + +/obj/item/weapon/flamethrower/attackby(obj/item/W as obj, mob/user as mob) + if(user.stat || user.restrained() || user.lying) return + if(W.has_tool_quality(TOOL_WRENCH) && !status)//Taking this apart + var/turf/T = get_turf(src) + if(weldtool) + weldtool.loc = T + weldtool = null + if(igniter) + igniter.loc = T + igniter = null + if(ptank) + ptank.loc = T + ptank = null + new /obj/item/stack/rods(T) + qdel(src) + return + + if(W.has_tool_quality(TOOL_SCREWDRIVER) && igniter && !lit) + status = !status + to_chat(user, "[igniter] is now [status ? "secured" : "unsecured"]!") + update_icon() + return + + if(isigniter(W)) + var/obj/item/device/assembly/igniter/I = W + if(I.secured) return + if(igniter) return + user.drop_item() + I.loc = src + igniter = I + update_icon() + return + + if(istype(W,/obj/item/weapon/tank/phoron)) + if(ptank) + to_chat(user, "There appears to already be a phoron tank loaded in [src]!") + return + user.drop_item() + ptank = W + W.loc = src + update_icon() + return + + ..() + return + + +/obj/item/weapon/flamethrower/attack_self(mob/user as mob) + if(user.stat || user.restrained() || user.lying) return + user.set_machine(src) + if(!ptank) + to_chat(user, "Attach a phoron tank first!") + return + var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"])
                    \n Tank Pressure: [ptank.air_contents.return_pressure()]
                    \nAmount to throw: - - - [throw_amount] + + +
                    \nRemove phorontank - Close
                    ") + user << browse(dat, "window=flamethrower;size=600x300") + onclose(user, "flamethrower") + return + + +/obj/item/weapon/flamethrower/Topic(href,href_list[]) + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=flamethrower") + return + if(usr.stat || usr.restrained() || usr.lying) return + usr.set_machine(src) + if(href_list["light"]) + if(!ptank) return + if(ptank.air_contents.gas["phoron"] < 1) return + if(!status) return + lit = !lit + if(lit) + START_PROCESSING(SSobj, src) + if(href_list["amount"]) + throw_amount = throw_amount + text2num(href_list["amount"]) + throw_amount = max(50, min(5000, throw_amount)) + if(href_list["remove"]) + if(!ptank) return + usr.put_in_hands(ptank) + ptank = null + lit = 0 + usr.unset_machine() + usr << browse(null, "window=flamethrower") + for(var/mob/M in viewers(1, loc)) + if((M.client && M.machine == src)) + attack_self(M) + update_icon() + return + + +//Called from turf.dm turf/dblclick +/obj/item/weapon/flamethrower/proc/flame_turf(turflist) + if(!lit || operating) return + operating = 1 + for(var/turf/T in turflist) + if(T.density || istype(T, /turf/space)) + break + if(!previousturf && length(turflist)>1) + previousturf = get_turf(src) + continue //so we don't burn the tile we be standin on + if(previousturf && LinkBlocked(previousturf, T)) + break + ignite_turf(T) + sleep(1) + previousturf = null + operating = 0 + for(var/mob/M in viewers(1, loc)) + if((M.client && M.machine == src)) + attack_self(M) + return + + +/obj/item/weapon/flamethrower/proc/ignite_turf(turf/target) + //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... + //Transfer 5% of current tank air contents to turf + var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100)) + //air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is mischievious and I could probably make it work without fucking it up like this, but there you have it. -- TLE + new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas["phoron"],get_dir(loc,target)) + air_transfer.gas["phoron"] = 0 + target.assume_air(air_transfer) + //Burn it based on transfered gas + //target.hotspot_expose(part4.air_contents.temperature*2,300) + target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE + //location.hotspot_expose(1000,500,1) + return + +/obj/item/weapon/flamethrower/full/New(var/loc) + ..() + weldtool = new /obj/item/weapon/weldingtool(src) + weldtool.status = 0 + igniter = new /obj/item/device/assembly/igniter(src) + igniter.secured = 0 + status = 1 + update_icon() + return diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm index c30b47a7f4d..42134cb6386 100644 --- a/code/game/objects/items/weapons/gift_wrappaper.dm +++ b/code/game/objects/items/weapons/gift_wrappaper.dm @@ -49,7 +49,7 @@ /obj/effect/spresent/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() - if (!W.is_wirecutter()) + if (!W.has_tool_quality(TOOL_WIRECUTTER)) to_chat(user, "I need wirecutters for that.") return @@ -132,13 +132,14 @@ to_chat(user, "You MUST put the paper on a table!") if (W.w_class < ITEMSIZE_LARGE) var/obj/item/I = user.get_inactive_hand() - if(I.is_wirecutter()) + if(I && I.has_tool_quality(TOOL_WIRECUTTER)) var/a_used = 2 ** (src.w_class - 1) if (src.amount < a_used) to_chat(user, "You need more paper!") return else if(istype(W, /obj/item/smallDelivery) || istype(W, /obj/item/weapon/gift)) //No gift wrapping gifts! + to_chat(user, "You can't wrap something that's already wrapped!") return src.amount -= a_used diff --git a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm index fe4d80418dd..5a2aad6ac3d 100644 --- a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm +++ b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm @@ -1,22 +1,22 @@ -/obj/item/weapon/grenade/anti_photon - desc = "An experimental device for temporarily removing light in a limited area." - name = "photon disruption grenade" - icon = 'icons/obj/grenade.dmi' - icon_state = "emp" - det_time = 20 - origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) - -/obj/item/weapon/grenade/anti_photon/detonate() - playsound(src, 'sound/effects/phasein.ogg', 50, 1, 5) - set_light(10, -10, "#FFFFFF") - - var/extra_delay = rand(0,90) - - spawn(extra_delay) - spawn(200) - if(prob(10+extra_delay)) - set_light(10, 10, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") - spawn(210) - ..() - playsound(src, 'sound/effects/bang.ogg', 50, 1, 5) - qdel(src) +/obj/item/weapon/grenade/anti_photon + desc = "An experimental device for temporarily removing light in a limited area." + name = "photon disruption grenade" + icon = 'icons/obj/grenade.dmi' + icon_state = "emp" + det_time = 20 + origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) + +/obj/item/weapon/grenade/anti_photon/detonate() + playsound(src, 'sound/effects/phasein.ogg', 50, 1, 5) + set_light(10, -10, "#FFFFFF") + + var/extra_delay = rand(0,90) + + spawn(extra_delay) + spawn(200) + if(prob(10+extra_delay)) + set_light(10, 10, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") + spawn(210) + ..() + playsound(src, 'sound/effects/bang.ogg', 50, 1, 5) + qdel(src) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 1a19b98910b..84fade96cff 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -1,316 +1,316 @@ -/obj/item/weapon/grenade/chem_grenade - name = "grenade casing" - icon_state = "chemg" - item_state = "grenade" - desc = "A hand made chemical grenade." - w_class = ITEMSIZE_SMALL - force = 2.0 - det_time = null - unacidable = TRUE - - var/stage = 0 - var/state = 0 - var/path = 0 - /// If TRUE, grenade is permanently sealed when fully assembled, useful for things like off-the-shelf grenades. - var/sealed = FALSE - var/obj/item/device/assembly_holder/detonator = null - var/list/beakers = new/list() - var/list/allowed_containers = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/reagent_containers/glass/bottle) - var/affected_area = 3 - -/obj/item/weapon/grenade/chem_grenade/Initialize() - . = ..() - create_reagents(1000) - -/obj/item/weapon/grenade/chem_grenade/Destroy() - QDEL_NULL(detonator) - QDEL_LIST_NULL(beakers) - return ..() - -/obj/item/weapon/grenade/chem_grenade/attack_self(mob/user as mob) - if(!stage || stage==1) - if(detonator) -// detonator.loc=src.loc - detonator.detached() - usr.put_in_hands(detonator) - detonator=null - det_time = null - stage=0 - icon_state = initial(icon_state) - else if(beakers.len) - for(var/obj/B in beakers) - if(istype(B)) - beakers -= B - user.put_in_hands(B) - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - if(stage > 1 && !active && clown_check(user)) - to_chat(user, "You prime \the [name]!") - - msg_admin_attack("[key_name_admin(user)] primed \a [src]") - - activate() - add_fingerprint(user) - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.throw_mode_on() - -/obj/item/weapon/grenade/chem_grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/device/assembly_holder) && (!stage || stage==1) && !detonator && path != 2) - var/obj/item/device/assembly_holder/det = W - if(istype(det.a_left,det.a_right.type) || (!isigniter(det.a_left) && !isigniter(det.a_right))) - to_chat(user, "Assembly must contain one igniter.") - return - if(!det.secured) - to_chat(user, "Assembly must be secured with screwdriver.") - return - path = 1 - to_chat(user, "You add [W] to the metal casing.") - playsound(src, 'sound/items/Screwdriver2.ogg', 25, -3) - user.remove_from_mob(det) - det.loc = src - detonator = det - if(istimer(detonator.a_left)) - var/obj/item/device/assembly/timer/T = detonator.a_left - det_time = 10*T.time - if(istimer(detonator.a_right)) - var/obj/item/device/assembly/timer/T = detonator.a_right - det_time = 10*T.time - icon_state = initial(icon_state) +"_ass" - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - stage = 1 - else if(W.is_screwdriver() && path != 2) - if(stage == 1) - path = 1 - if(beakers.len) - to_chat(user, "You lock the assembly.") - name = "grenade" - else -// to_chat(user, "You need to add at least one beaker before locking the assembly.") - to_chat(user, "You lock the empty assembly.") - name = "fake grenade" - playsound(src, W.usesound, 50, 1) - icon_state = initial(icon_state) +"_locked" - stage = 2 - else if(stage == 2) - if(active && prob(95)) - to_chat(user, "You trigger the assembly!") - detonate() - else if(sealed) - to_chat(user, "This grenade lacks a way to disassemble it.") - else - to_chat(user, "You unlock the assembly.") - playsound(src, W.usesound, 50, -3) - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - icon_state = initial(icon_state) + (detonator?"_ass":"") - stage = 1 - active = 0 - else if(is_type_in_list(W, allowed_containers) && (!stage || stage==1) && path != 2) - path = 1 - if(beakers.len == 2) - to_chat(user, "The grenade can not hold more containers.") - return - else - if(W.reagents.total_volume) - to_chat(user, "You add \the [W] to the assembly.") - user.drop_item() - W.loc = src - beakers += W - stage = 1 - name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" - else - to_chat(user, "\The [W] is empty.") - -/obj/item/weapon/grenade/chem_grenade/examine(mob/user) - . = ..() - if(detonator) - . += "It has [detonator.name] attached to it." - -/obj/item/weapon/grenade/chem_grenade/activate(mob/user as mob) - if(active) return - - if(detonator) - if(!isigniter(detonator.a_left)) - detonator.a_left.activate() - active = 1 - if(!isigniter(detonator.a_right)) - detonator.a_right.activate() - active = 1 - if(active) - icon_state = initial(icon_state) + "_active" - - if(user) - msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") - - return - -/obj/item/weapon/grenade/chem_grenade/proc/primed(var/primed = 1) - if(active) - icon_state = initial(icon_state) + (primed?"_primed":"_active") - -/obj/item/weapon/grenade/chem_grenade/detonate() - if(!stage || stage<2) return - - var/has_reagents = 0 - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - if(G.reagents.total_volume) has_reagents = 1 - - active = 0 - if(!has_reagents) - icon_state = initial(icon_state) +"_locked" - playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) - spawn(0) //Otherwise det_time is erroneously set to 0 after this - if(istimer(detonator.a_left)) //Make sure description reflects that the timer has been reset - var/obj/item/device/assembly/timer/T = detonator.a_left - det_time = 10*T.time - if(istimer(detonator.a_right)) - var/obj/item/device/assembly/timer/T = detonator.a_right - det_time = 10*T.time - return - - playsound(src, 'sound/effects/bamf.ogg', 50, 1) - - for(var/obj/item/weapon/reagent_containers/glass/G in beakers) - G.reagents.trans_to_obj(src, G.reagents.total_volume) - - if(src.reagents.total_volume) //The possible reactions didnt use up all reagents. - var/datum/effect/effect/system/steam_spread/steam = new /datum/effect/effect/system/steam_spread() - steam.set_up(10, 0, get_turf(src)) - steam.attach(src) - steam.start() - - for(var/atom/A in view(affected_area, src.loc)) - if( A == src ) continue - src.reagents.touch(A) - - if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand - var/mob/living/carbon/C = loc - C.drop_from_inventory(src) - C.throw_mode_off() - - invisibility = INVISIBILITY_MAXIMUM //Why am i doing this? - spawn(50) //To make sure all reagents can work - qdel(src) //correctly before deleting the grenade. - - -/obj/item/weapon/grenade/chem_grenade/large - name = "large chem grenade" - desc = "An oversized grenade that affects a larger area." - icon_state = "large_grenade" - allowed_containers = list(/obj/item/weapon/reagent_containers/glass) - origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3) - affected_area = 4 - -/obj/item/weapon/grenade/chem_grenade/metalfoam - name = "metal-foam grenade" - desc = "Used for emergency sealing of air breaches." - icon_state = "foam" - path = 1 - stage = 2 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/metalfoam/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("aluminum", 30) - B2.reagents.add_reagent("foaming_agent", 10) - B2.reagents.add_reagent("pacid", 10) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - -/obj/item/weapon/grenade/chem_grenade/incendiary - name = "incendiary grenade" - desc = "Used for clearing rooms of living things." - icon_state = "incendiary" - path = 1 - stage = 2 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/incendiary/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("aluminum", 15) - B1.reagents.add_reagent("fuel",20) - B2.reagents.add_reagent("phoron", 15) - B2.reagents.add_reagent("sacid", 15) - B1.reagents.add_reagent("fuel",20) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - -/obj/item/weapon/grenade/chem_grenade/antiweed - name = "weedkiller grenade" - desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents." - path = 1 - stage = 2 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/antiweed/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("plantbgone", 25) - B1.reagents.add_reagent("potassium", 25) - B2.reagents.add_reagent("phosphorus", 25) - B2.reagents.add_reagent("sugar", 25) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - icon_state = "grenade" - -/obj/item/weapon/grenade/chem_grenade/cleaner - name = "cleaner grenade" - desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas." - icon_state = "cleaner" - stage = 2 - path = 1 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/cleaner/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("fluorosurfactant", 40) - B2.reagents.add_reagent("water", 40) - B2.reagents.add_reagent("cleaner", 10) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 - -/obj/item/weapon/grenade/chem_grenade/teargas - name = "tear gas grenade" - desc = "Concentrated Capsaicin. Contents under pressure. Use with caution." - icon_state = "teargas" - stage = 2 - path = 1 - sealed = TRUE - -/obj/item/weapon/grenade/chem_grenade/teargas/Initialize() - . = ..() - var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src) - var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src) - - B1.reagents.add_reagent("phosphorus", 40) - B1.reagents.add_reagent("potassium", 40) - B1.reagents.add_reagent("condensedcapsaicin", 40) - B2.reagents.add_reagent("sugar", 40) - B2.reagents.add_reagent("condensedcapsaicin", 80) - - detonator = new/obj/item/device/assembly_holder/timer_igniter(src) - - beakers += B1 - beakers += B2 +/obj/item/weapon/grenade/chem_grenade + name = "grenade casing" + icon_state = "chemg" + item_state = "grenade" + desc = "A hand made chemical grenade." + w_class = ITEMSIZE_SMALL + force = 2.0 + det_time = null + unacidable = TRUE + + var/stage = 0 + var/state = 0 + var/path = 0 + /// If TRUE, grenade is permanently sealed when fully assembled, useful for things like off-the-shelf grenades. + var/sealed = FALSE + var/obj/item/device/assembly_holder/detonator = null + var/list/beakers = new/list() + var/list/allowed_containers = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/reagent_containers/glass/bottle) + var/affected_area = 3 + +/obj/item/weapon/grenade/chem_grenade/Initialize() + . = ..() + create_reagents(1000) + +/obj/item/weapon/grenade/chem_grenade/Destroy() + QDEL_NULL(detonator) + QDEL_LIST_NULL(beakers) + return ..() + +/obj/item/weapon/grenade/chem_grenade/attack_self(mob/user as mob) + if(!stage || stage==1) + if(detonator) +// detonator.loc=src.loc + detonator.detached() + usr.put_in_hands(detonator) + detonator=null + det_time = null + stage=0 + icon_state = initial(icon_state) + else if(beakers.len) + for(var/obj/B in beakers) + if(istype(B)) + beakers -= B + user.put_in_hands(B) + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + if(stage > 1 && !active && clown_check(user)) + to_chat(user, "You prime \the [name]!") + + msg_admin_attack("[key_name_admin(user)] primed \a [src]") + + activate() + add_fingerprint(user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + +/obj/item/weapon/grenade/chem_grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/assembly_holder) && (!stage || stage==1) && !detonator && path != 2) + var/obj/item/device/assembly_holder/det = W + if(istype(det.a_left,det.a_right.type) || (!isigniter(det.a_left) && !isigniter(det.a_right))) + to_chat(user, "Assembly must contain one igniter.") + return + if(!det.secured) + to_chat(user, "Assembly must be secured with screwdriver.") + return + path = 1 + to_chat(user, "You add [W] to the metal casing.") + playsound(src, 'sound/items/Screwdriver2.ogg', 25, -3) + user.remove_from_mob(det) + det.loc = src + detonator = det + if(istimer(detonator.a_left)) + var/obj/item/device/assembly/timer/T = detonator.a_left + det_time = 10*T.time + if(istimer(detonator.a_right)) + var/obj/item/device/assembly/timer/T = detonator.a_right + det_time = 10*T.time + icon_state = initial(icon_state) +"_ass" + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + stage = 1 + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && path != 2) + if(stage == 1) + path = 1 + if(beakers.len) + to_chat(user, "You lock the assembly.") + name = "grenade" + else +// to_chat(user, "You need to add at least one beaker before locking the assembly.") + to_chat(user, "You lock the empty assembly.") + name = "fake grenade" + playsound(src, W.usesound, 50, 1) + icon_state = initial(icon_state) +"_locked" + stage = 2 + else if(stage == 2) + if(active && prob(95)) + to_chat(user, "You trigger the assembly!") + detonate() + else if(sealed) + to_chat(user, "This grenade lacks a way to disassemble it.") + else + to_chat(user, "You unlock the assembly.") + playsound(src, W.usesound, 50, -3) + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + icon_state = initial(icon_state) + (detonator?"_ass":"") + stage = 1 + active = 0 + else if(is_type_in_list(W, allowed_containers) && (!stage || stage==1) && path != 2) + path = 1 + if(beakers.len == 2) + to_chat(user, "The grenade can not hold more containers.") + return + else + if(W.reagents.total_volume) + to_chat(user, "You add \the [W] to the assembly.") + user.drop_item() + W.loc = src + beakers += W + stage = 1 + name = "unsecured grenade with [beakers.len] containers[detonator?" and detonator":""]" + else + to_chat(user, "\The [W] is empty.") + +/obj/item/weapon/grenade/chem_grenade/examine(mob/user) + . = ..() + if(detonator) + . += "It has [detonator.name] attached to it." + +/obj/item/weapon/grenade/chem_grenade/activate(mob/user as mob) + if(active) return + + if(detonator) + if(!isigniter(detonator.a_left)) + detonator.a_left.activate() + active = 1 + if(!isigniter(detonator.a_right)) + detonator.a_right.activate() + active = 1 + if(active) + icon_state = initial(icon_state) + "_active" + + if(user) + msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") + + return + +/obj/item/weapon/grenade/chem_grenade/proc/primed(var/primed = 1) + if(active) + icon_state = initial(icon_state) + (primed?"_primed":"_active") + +/obj/item/weapon/grenade/chem_grenade/detonate() + if(!stage || stage<2) return + + var/has_reagents = 0 + for(var/obj/item/weapon/reagent_containers/glass/G in beakers) + if(G.reagents.total_volume) has_reagents = 1 + + active = 0 + if(!has_reagents) + icon_state = initial(icon_state) +"_locked" + playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) + spawn(0) //Otherwise det_time is erroneously set to 0 after this + if(istimer(detonator.a_left)) //Make sure description reflects that the timer has been reset + var/obj/item/device/assembly/timer/T = detonator.a_left + det_time = 10*T.time + if(istimer(detonator.a_right)) + var/obj/item/device/assembly/timer/T = detonator.a_right + det_time = 10*T.time + return + + playsound(src, 'sound/effects/bamf.ogg', 50, 1) + + for(var/obj/item/weapon/reagent_containers/glass/G in beakers) + G.reagents.trans_to_obj(src, G.reagents.total_volume) + + if(src.reagents.total_volume) //The possible reactions didnt use up all reagents. + var/datum/effect/effect/system/steam_spread/steam = new /datum/effect/effect/system/steam_spread() + steam.set_up(10, 0, get_turf(src)) + steam.attach(src) + steam.start() + + for(var/atom/A in view(affected_area, src.loc)) + if( A == src ) continue + src.reagents.touch(A) + + if(istype(loc, /mob/living/carbon)) //drop dat grenade if it goes off in your hand + var/mob/living/carbon/C = loc + C.drop_from_inventory(src) + C.throw_mode_off() + + invisibility = INVISIBILITY_MAXIMUM //Why am i doing this? + spawn(50) //To make sure all reagents can work + qdel(src) //correctly before deleting the grenade. + + +/obj/item/weapon/grenade/chem_grenade/large + name = "large chem grenade" + desc = "An oversized grenade that affects a larger area." + icon_state = "large_grenade" + allowed_containers = list(/obj/item/weapon/reagent_containers/glass) + origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3) + affected_area = 4 + +/obj/item/weapon/grenade/chem_grenade/metalfoam + name = "metal-foam grenade" + desc = "Used for emergency sealing of air breaches." + icon_state = "foam" + path = 1 + stage = 2 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/metalfoam/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("aluminum", 30) + B2.reagents.add_reagent("foaming_agent", 10) + B2.reagents.add_reagent("pacid", 10) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + +/obj/item/weapon/grenade/chem_grenade/incendiary + name = "incendiary grenade" + desc = "Used for clearing rooms of living things." + icon_state = "incendiary" + path = 1 + stage = 2 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/incendiary/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("aluminum", 15) + B1.reagents.add_reagent("fuel",20) + B2.reagents.add_reagent("phoron", 15) + B2.reagents.add_reagent("sacid", 15) + B1.reagents.add_reagent("fuel",20) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + +/obj/item/weapon/grenade/chem_grenade/antiweed + name = "weedkiller grenade" + desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents." + path = 1 + stage = 2 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/antiweed/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("plantbgone", 25) + B1.reagents.add_reagent("potassium", 25) + B2.reagents.add_reagent("phosphorus", 25) + B2.reagents.add_reagent("sugar", 25) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + icon_state = "grenade" + +/obj/item/weapon/grenade/chem_grenade/cleaner + name = "cleaner grenade" + desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas." + icon_state = "cleaner" + stage = 2 + path = 1 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/cleaner/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("fluorosurfactant", 40) + B2.reagents.add_reagent("water", 40) + B2.reagents.add_reagent("cleaner", 10) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 + +/obj/item/weapon/grenade/chem_grenade/teargas + name = "tear gas grenade" + desc = "Concentrated Capsaicin. Contents under pressure. Use with caution." + icon_state = "teargas" + stage = 2 + path = 1 + sealed = TRUE + +/obj/item/weapon/grenade/chem_grenade/teargas/Initialize() + . = ..() + var/obj/item/weapon/reagent_containers/glass/beaker/large/B1 = new(src) + var/obj/item/weapon/reagent_containers/glass/beaker/large/B2 = new(src) + + B1.reagents.add_reagent("phosphorus", 40) + B1.reagents.add_reagent("potassium", 40) + B1.reagents.add_reagent("condensedcapsaicin", 40) + B2.reagents.add_reagent("sugar", 40) + B2.reagents.add_reagent("condensedcapsaicin", 80) + + detonator = new/obj/item/device/assembly_holder/timer_igniter(src) + + beakers += B1 + beakers += B2 diff --git a/code/game/objects/items/weapons/grenades/confetti.dm b/code/game/objects/items/weapons/grenades/confetti.dm new file mode 100644 index 00000000000..a2e5559d911 --- /dev/null +++ b/code/game/objects/items/weapons/grenades/confetti.dm @@ -0,0 +1,51 @@ +/obj/item/weapon/grenade/confetti + desc = "It is set to detonate in 2 seconds. These party grenades will make everyone jump with joy (or fright)!" + name = "grenatti" + icon = 'icons/obj/grenade.dmi' + icon_state = "grenade" + det_time = 20 + item_state = "grenade" + slot_flags = SLOT_BELT + var/datum/effect/effect/system/confetti_spread + var/confetti_strength = 8 + +/obj/item/weapon/grenade/confetti/New() + ..() + src.confetti_spread = new /datum/effect/effect/system/confetti_spread() + src.confetti_spread.attach(src) + +/obj/item/weapon/grenade/confetti/Destroy() + qdel(confetti_spread) + confetti_spread = null + return ..() + +/obj/item/weapon/grenade/confetti/detonate() //Find a good confetti firework or pop sound effect later + playsound(src.loc, 'sound/effects/snap.ogg', 50, 1, -3) + src.confetti_spread.set_up(10, 0, usr.loc) + spawn(0) + for(var/i = 1 to confetti_strength) + src.confetti_spread.start() + sleep(10) + qdel(src) + + return + +/obj/item/weapon/grenade/confetti/party_ball //Intended to be used only with the confetti cannon. + name = "party ball" + desc = "Full of !!FUN!!" + icon = 'icons/obj/grenade.dmi' + icon_state = "party_ball" + confetti_strength = 2 + det_time = 1 + throwforce = 0 //Confetti cannon is only fun to shoot at people if it deals no damage. + +/obj/item/weapon/grenade/confetti/party_ball/detonate() //Could condense this by making the sound a variable in the parent but I'm lazy. + playsound(src.loc, 'sound/effects/confetti_ball.ogg', 50, 1, -3) + src.confetti_spread.set_up(10, 0, usr.loc) + spawn(0) + for(var/i = 1 to confetti_strength) + src.confetti_spread.start() + sleep(10) + qdel(src) + + return \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/emgrenade.dm b/code/game/objects/items/weapons/grenades/emgrenade.dm index 26c68b0f204..89aff71bb37 100644 --- a/code/game/objects/items/weapons/grenades/emgrenade.dm +++ b/code/game/objects/items/weapons/grenades/emgrenade.dm @@ -1,25 +1,25 @@ -/obj/item/weapon/grenade/empgrenade - name = "emp grenade" - icon_state = "emp" - item_state = "empgrenade" - origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) - var/emp_heavy = 2 - var/emp_med = 4 - var/emp_light = 7 - var/emp_long = 10 - -/obj/item/weapon/grenade/empgrenade/detonate() - ..() - if(empulse(src, emp_heavy, emp_med, emp_light, emp_long)) - qdel(src) - return - -/obj/item/weapon/grenade/empgrenade/low_yield - name = "low yield emp grenade" - desc = "A weaker variant of the EMP grenade" - icon_state = "lyemp" - origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) - emp_heavy = 1 - emp_med = 2 - emp_light = 3 +/obj/item/weapon/grenade/empgrenade + name = "emp grenade" + icon_state = "emp" + item_state = "empgrenade" + origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) + var/emp_heavy = 2 + var/emp_med = 4 + var/emp_light = 7 + var/emp_long = 10 + +/obj/item/weapon/grenade/empgrenade/detonate() + ..() + if(empulse(src, emp_heavy, emp_med, emp_light, emp_long)) + qdel(src) + return + +/obj/item/weapon/grenade/empgrenade/low_yield + name = "low yield emp grenade" + desc = "A weaker variant of the EMP grenade" + icon_state = "lyemp" + origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) + emp_heavy = 1 + emp_med = 2 + emp_light = 3 emp_long = 4 \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index b176563b268..d407e91cd72 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -1,165 +1,165 @@ -/obj/item/weapon/grenade/flashbang - name = "flashbang" - icon_state = "flashbang" - item_state = "flashbang" - origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) - var/max_range = 10 //The maximum range possible, including species effect mods. Cuts off at 7 for normal humans. Should be 3 higher than your intended target range for affecting normal humans. - var/banglet = 0 - -/obj/item/weapon/grenade/flashbang/detonate() - ..() - for(var/obj/structure/closet/L in hear(max_range, get_turf(src))) - if(locate(/mob/living/carbon/, L)) - for(var/mob/living/carbon/M in L) - bang(get_turf(src), M) - - for(var/mob/living/carbon/M in hear(max_range, get_turf(src))) - bang(get_turf(src), M) - - for(var/obj/structure/blob/B in hear(max_range - 2,get_turf(src))) //Blob damage here - var/damage = round(30/(get_dist(B,get_turf(src))+1)) - if(B.overmind) - damage *= B.overmind.blob_type.burn_multiplier - B.adjust_integrity(-damage) - - new/obj/effect/effect/sparks(src.loc) - new/obj/effect/effect/smoke/illumination(src.loc, 5, range=30, power=30, color="#FFFFFF") - - qdel(src) - -/obj/item/weapon/grenade/flashbang/proc/bang(var/turf/T , var/mob/living/carbon/M) // Added a new proc called 'bang' that takes a location and a person to be banged. - to_chat(M, "BANG") // Called during the loop that bangs people in lockers/containers and when banging - playsound(src, 'sound/effects/bang.ogg', 50, 1, 30) // people in normal view. Could theroetically be called during other explosions. - // -- Polymorph - - //Checking for protections - var/eye_safety = 0 - var/ear_safety = 0 - if(iscarbon(M)) - eye_safety = M.eyecheck() - ear_safety = M.get_ear_protection() - - //Flashing everyone - var/mob/living/carbon/human/H = M - var/flash_effectiveness = 1 - var/bang_effectiveness = 1 - if(ishuman(M)) - flash_effectiveness = H.species.flash_mod - bang_effectiveness = H.species.sound_mod - if(eye_safety < 1 && get_dist(M, T) <= round(max_range * 0.7 * flash_effectiveness)) - M.flash_eyes() - M.Confuse(2 * flash_effectiveness) - M.Weaken(5 * flash_effectiveness) - - //Now applying sound - if((get_dist(M, T) <= round(max_range * 0.3 * bang_effectiveness) || src.loc == M.loc || src.loc == M)) - if(ear_safety > 0) - M.Confuse(2) - M.Weaken(1) - else - M.Confuse(10) - M.Weaken(3) - if ((prob(14) || (M == src.loc && prob(70)))) - M.ear_damage += rand(1, 10) - else - M.ear_damage += rand(0, 5) - M.ear_deaf = max(M.ear_deaf,15) - - else if(get_dist(M, T) <= round(max_range * 0.5 * bang_effectiveness)) - if(!ear_safety) - M.Confuse(8) - M.ear_damage += rand(0, 3) - M.ear_deaf = max(M.ear_deaf,10) - - else if(!ear_safety && get_dist(M, T) <= (max_range * 0.7 * bang_effectiveness)) - M.Confuse(4) - M.ear_damage += rand(0, 1) - M.ear_deaf = max(M.ear_deaf,5) - - //This really should be in mob not every check - if(ishuman(M)) - var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] - if (E && E.damage >= E.min_bruised_damage) - to_chat(M, "Your eyes start to burn badly!") - if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) - if (E.damage >= E.min_broken_damage) - to_chat(M, "You can't see anything!") - if (M.ear_damage >= 15) - to_chat(M, "Your ears start to ring badly!") - if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) - if (prob(M.ear_damage - 10 + 5)) - to_chat(M, "You can't hear anything!") - M.sdisabilities |= DEAF - else if(M.ear_damage >= 5) - to_chat(M, "Your ears start to ring!") - -/obj/item/weapon/grenade/flashbang/Destroy() - walk(src, 0) // Because we might have called walk_away, we must stop the walk loop or BYOND keeps an internal reference to us forever. - return ..() - - -/obj/item/weapon/grenade/flashbang/clusterbang//Created by Polymorph, fixed by Sieve - desc = "Use of this weapon may constiute a war crime in your area, consult your local Site Manager." - name = "clusterbang" - icon = 'icons/obj/grenade.dmi' - icon_state = "clusterbang" - var/can_repeat = TRUE // Does this thing drop mini-clusterbangs? - var/min_banglets = 4 - var/max_banglets = 8 - -/obj/item/weapon/grenade/flashbang/clusterbang/detonate() - var/numspawned = rand(min_banglets, max_banglets) - var/again = 0 - - if(can_repeat) - for(var/more = numspawned, more > 0, more--) - if(prob(35)) - again++ - numspawned-- - - for(var/do_spawn = numspawned, do_spawn > 0, do_spawn--) - new /obj/item/weapon/grenade/flashbang/cluster(src.loc)//Launches flashbangs - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - - for(var/do_again = again, do_again > 0, do_again--) - new /obj/item/weapon/grenade/flashbang/clusterbang/segment(src.loc)//Creates a 'segment' that launches a few more flashbangs - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - qdel(src) - return - -/obj/item/weapon/grenade/flashbang/clusterbang/segment - desc = "A smaller segment of a clusterbang. Better run." - name = "clusterbang segment" - icon = 'icons/obj/grenade.dmi' - icon_state = "clusterbang_segment" - can_repeat = FALSE - banglet = TRUE - -/obj/item/weapon/grenade/flashbang/clusterbang/segment/New()//Segments should never exist except part of the clusterbang, since these immediately 'do their thing' and asplode - ..() - - icon_state = "clusterbang_segment_active" - - var/stepdist = rand(1,4)//How far to step - var/temploc = src.loc//Saves the current location to know where to step away from - walk_away(src,temploc,stepdist)//I must go, my people need me - - var/dettime = rand(15,60) - spawn(dettime) - detonate() - -/obj/item/weapon/grenade/flashbang/cluster - banglet = TRUE - -/obj/item/weapon/grenade/flashbang/cluster/New()//Same concept as the segments, so that all of the parts don't become reliant on the clusterbang - ..() - - icon_state = "flashbang_active" - - var/stepdist = rand(1,3) - var/temploc = src.loc - walk_away(src,temploc,stepdist) - - var/dettime = rand(15,60) - spawn(dettime) +/obj/item/weapon/grenade/flashbang + name = "flashbang" + icon_state = "flashbang" + item_state = "flashbang" + origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) + var/max_range = 10 //The maximum range possible, including species effect mods. Cuts off at 7 for normal humans. Should be 3 higher than your intended target range for affecting normal humans. + var/banglet = 0 + +/obj/item/weapon/grenade/flashbang/detonate() + ..() + for(var/obj/structure/closet/L in hear(max_range, get_turf(src))) + if(locate(/mob/living/carbon/, L)) + for(var/mob/living/carbon/M in L) + bang(get_turf(src), M) + + for(var/mob/living/carbon/M in hear(max_range, get_turf(src))) + bang(get_turf(src), M) + + for(var/obj/structure/blob/B in hear(max_range - 2,get_turf(src))) //Blob damage here + var/damage = round(30/(get_dist(B,get_turf(src))+1)) + if(B.overmind) + damage *= B.overmind.blob_type.burn_multiplier + B.adjust_integrity(-damage) + + new/obj/effect/effect/sparks(src.loc) + new/obj/effect/effect/smoke/illumination(src.loc, 5, range=30, power=30, color="#FFFFFF") + + qdel(src) + +/obj/item/weapon/grenade/flashbang/proc/bang(var/turf/T , var/mob/living/carbon/M) // Added a new proc called 'bang' that takes a location and a person to be banged. + to_chat(M, "BANG") // Called during the loop that bangs people in lockers/containers and when banging + playsound(src, 'sound/effects/bang.ogg', 50, 1, 30) // people in normal view. Could theroetically be called during other explosions. + // -- Polymorph + + //Checking for protections + var/eye_safety = 0 + var/ear_safety = 0 + if(iscarbon(M)) + eye_safety = M.eyecheck() + ear_safety = M.get_ear_protection() + + //Flashing everyone + var/mob/living/carbon/human/H = M + var/flash_effectiveness = 1 + var/bang_effectiveness = 1 + if(ishuman(M)) + flash_effectiveness = H.species.flash_mod + bang_effectiveness = H.species.sound_mod + if(eye_safety < 1 && get_dist(M, T) <= round(max_range * 0.7 * flash_effectiveness)) + M.flash_eyes() + M.Confuse(2 * flash_effectiveness) + M.Weaken(5 * flash_effectiveness) + + //Now applying sound + if((get_dist(M, T) <= round(max_range * 0.3 * bang_effectiveness) || src.loc == M.loc || src.loc == M)) + if(ear_safety > 0) + M.Confuse(2) + M.Weaken(1) + else + M.Confuse(10) + M.Weaken(3) + if ((prob(14) || (M == src.loc && prob(70)))) + M.ear_damage += rand(1, 10) + else + M.ear_damage += rand(0, 5) + M.ear_deaf = max(M.ear_deaf,15) + + else if(get_dist(M, T) <= round(max_range * 0.5 * bang_effectiveness)) + if(!ear_safety) + M.Confuse(8) + M.ear_damage += rand(0, 3) + M.ear_deaf = max(M.ear_deaf,10) + + else if(!ear_safety && get_dist(M, T) <= (max_range * 0.7 * bang_effectiveness)) + M.Confuse(4) + M.ear_damage += rand(0, 1) + M.ear_deaf = max(M.ear_deaf,5) + + //This really should be in mob not every check + if(ishuman(M)) + var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] + if (E && E.damage >= E.min_bruised_damage) + to_chat(M, "Your eyes start to burn badly!") + if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) + if (E.damage >= E.min_broken_damage) + to_chat(M, "You can't see anything!") + if (M.ear_damage >= 15) + to_chat(M, "Your ears start to ring badly!") + if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) + if (prob(M.ear_damage - 10 + 5)) + to_chat(M, "You can't hear anything!") + M.sdisabilities |= DEAF + else if(M.ear_damage >= 5) + to_chat(M, "Your ears start to ring!") + +/obj/item/weapon/grenade/flashbang/Destroy() + walk(src, 0) // Because we might have called walk_away, we must stop the walk loop or BYOND keeps an internal reference to us forever. + return ..() + + +/obj/item/weapon/grenade/flashbang/clusterbang//Created by Polymorph, fixed by Sieve + desc = "Use of this weapon may constiute a war crime in your area, consult your local Site Manager." + name = "clusterbang" + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang" + var/can_repeat = TRUE // Does this thing drop mini-clusterbangs? + var/min_banglets = 4 + var/max_banglets = 8 + +/obj/item/weapon/grenade/flashbang/clusterbang/detonate() + var/numspawned = rand(min_banglets, max_banglets) + var/again = 0 + + if(can_repeat) + for(var/more = numspawned, more > 0, more--) + if(prob(35)) + again++ + numspawned-- + + for(var/do_spawn = numspawned, do_spawn > 0, do_spawn--) + new /obj/item/weapon/grenade/flashbang/cluster(src.loc)//Launches flashbangs + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + + for(var/do_again = again, do_again > 0, do_again--) + new /obj/item/weapon/grenade/flashbang/clusterbang/segment(src.loc)//Creates a 'segment' that launches a few more flashbangs + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + qdel(src) + return + +/obj/item/weapon/grenade/flashbang/clusterbang/segment + desc = "A smaller segment of a clusterbang. Better run." + name = "clusterbang segment" + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang_segment" + can_repeat = FALSE + banglet = TRUE + +/obj/item/weapon/grenade/flashbang/clusterbang/segment/New()//Segments should never exist except part of the clusterbang, since these immediately 'do their thing' and asplode + ..() + + icon_state = "clusterbang_segment_active" + + var/stepdist = rand(1,4)//How far to step + var/temploc = src.loc//Saves the current location to know where to step away from + walk_away(src,temploc,stepdist)//I must go, my people need me + + var/dettime = rand(15,60) + spawn(dettime) + detonate() + +/obj/item/weapon/grenade/flashbang/cluster + banglet = TRUE + +/obj/item/weapon/grenade/flashbang/cluster/New()//Same concept as the segments, so that all of the parts don't become reliant on the clusterbang + ..() + + icon_state = "flashbang_active" + + var/stepdist = rand(1,3) + var/temploc = src.loc + walk_away(src,temploc,stepdist) + + var/dettime = rand(15,60) + spawn(dettime) detonate() \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 9bdf650c64d..6bf22d153d0 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -1,119 +1,119 @@ -/obj/item/weapon/grenade - name = "grenade" - desc = "A hand held grenade, with an adjustable timer." - w_class = ITEMSIZE_SMALL - icon = 'icons/obj/grenade.dmi' - icon_state = "grenade" - item_state = "grenade" - throw_speed = 4 - throw_range = 20 - slot_flags = SLOT_MASK|SLOT_BELT - - var/active = 0 - var/det_time = 50 - var/loadable = TRUE - var/arm_sound = 'sound/weapons/armbomb.ogg' - var/hud_state = "grenade_he" // TGMC Ammo HUD Port - var/hud_state_empty = "grenade_empty" // TGMC Ammo HUD Port - -/obj/item/weapon/grenade/proc/clown_check(var/mob/living/user) - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Huh? How does this thing work?") - - activate(user) - add_fingerprint(user) - spawn(5) - detonate() - return 0 - return 1 - - -/*/obj/item/weapon/grenade/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) - if (istype(target, /obj/item/weapon/storage)) return ..() // Trying to put it in a full container - if (istype(target, /obj/item/weapon/gun/grenadelauncher)) return ..() - if((user.get_active_hand() == src) && (!active) && (clown_check(user)) && target.loc != src.loc) - to_chat(user, "You prime the [name]! [det_time/10] seconds!") - active = 1 - icon_state = initial(icon_state) + "_active" - playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) - spawn(det_time) - detonate() - return - user.set_dir(get_dir(user, target)) - user.drop_item() - var/t = (isturf(target) ? target : target.loc) - walk_towards(src, t, 3) - return*/ - - -/obj/item/weapon/grenade/examine(mob/user) - . = ..() - if(get_dist(user, src) == 0) - if(det_time > 1) - . += "The timer is set to [det_time/10] seconds." - else if(det_time == null) - . += "\The [src] is set for instant detonation." - - -/obj/item/weapon/grenade/attack_self(mob/user as mob) - if(!active) - if(clown_check(user)) - to_chat(user, "You prime \the [name]! [det_time/10] seconds!") - - activate(user) - add_fingerprint(user) - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.throw_mode_on() - return - - -/obj/item/weapon/grenade/proc/activate(mob/user as mob) - if(active) - return - - if(user) - msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") - - icon_state = initial(icon_state) + "_active" - active = 1 - playsound(src, arm_sound, 75, 1, -3) - - spawn(det_time) - detonate() - return - - -/obj/item/weapon/grenade/proc/detonate() -// playsound(src, 'sound/items/Welder2.ogg', 25, 1) - var/turf/T = get_turf(src) - if(T) - T.hotspot_expose(700,125) - - -/obj/item/weapon/grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_screwdriver()) - switch(det_time) - if (1) - det_time = 10 - to_chat(user, "You set the [name] for 1 second detonation time.") - if (10) - det_time = 30 - to_chat(user, "You set the [name] for 3 second detonation time.") - if (30) - det_time = 50 - to_chat(user, "You set the [name] for 5 second detonation time.") - if (50) - det_time = 1 - to_chat(user, "You set the [name] for instant detonation.") - add_fingerprint(user) - ..() - return - -/obj/item/weapon/grenade/attack_hand() - walk(src, null, null) - ..() - return - -/obj/item/weapon/grenade/vendor_action(var/obj/machinery/vending/V) +/obj/item/weapon/grenade + name = "grenade" + desc = "A hand held grenade, with an adjustable timer." + w_class = ITEMSIZE_SMALL + icon = 'icons/obj/grenade.dmi' + icon_state = "grenade" + item_state = "grenade" + throw_speed = 4 + throw_range = 20 + slot_flags = SLOT_MASK|SLOT_BELT + + var/active = 0 + var/det_time = 50 + var/loadable = TRUE + var/arm_sound = 'sound/weapons/armbomb.ogg' + var/hud_state = "grenade_he" // TGMC Ammo HUD Port + var/hud_state_empty = "grenade_empty" // TGMC Ammo HUD Port + +/obj/item/weapon/grenade/proc/clown_check(var/mob/living/user) + if((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Huh? How does this thing work?") + + activate(user) + add_fingerprint(user) + spawn(5) + detonate() + return 0 + return 1 + + +/*/obj/item/weapon/grenade/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) + if (istype(target, /obj/item/weapon/storage)) return ..() // Trying to put it in a full container + if (istype(target, /obj/item/weapon/gun/grenadelauncher)) return ..() + if((user.get_active_hand() == src) && (!active) && (clown_check(user)) && target.loc != src.loc) + to_chat(user, "You prime the [name]! [det_time/10] seconds!") + active = 1 + icon_state = initial(icon_state) + "_active" + playsound(src, 'sound/weapons/armbomb.ogg', 75, 1, -3) + spawn(det_time) + detonate() + return + user.set_dir(get_dir(user, target)) + user.drop_item() + var/t = (isturf(target) ? target : target.loc) + walk_towards(src, t, 3) + return*/ + + +/obj/item/weapon/grenade/examine(mob/user) + . = ..() + if(get_dist(user, src) == 0) + if(det_time > 1) + . += "The timer is set to [det_time/10] seconds." + else if(det_time == null) + . += "\The [src] is set for instant detonation." + + +/obj/item/weapon/grenade/attack_self(mob/user as mob) + if(!active) + if(clown_check(user)) + to_chat(user, "You prime \the [name]! [det_time/10] seconds!") + + activate(user) + add_fingerprint(user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + return + + +/obj/item/weapon/grenade/proc/activate(mob/user as mob) + if(active) + return + + if(user) + msg_admin_attack("[key_name_admin(user)] primed \a [src.name]") + + icon_state = initial(icon_state) + "_active" + active = 1 + playsound(src, arm_sound, 75, 1, -3) + + spawn(det_time) + detonate() + return + + +/obj/item/weapon/grenade/proc/detonate() +// playsound(src, 'sound/items/Welder2.ogg', 25, 1) + var/turf/T = get_turf(src) + if(T) + T.hotspot_expose(700,125) + + +/obj/item/weapon/grenade/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + switch(det_time) + if (1) + det_time = 10 + to_chat(user, "You set the [name] for 1 second detonation time.") + if (10) + det_time = 30 + to_chat(user, "You set the [name] for 3 second detonation time.") + if (30) + det_time = 50 + to_chat(user, "You set the [name] for 5 second detonation time.") + if (50) + det_time = 1 + to_chat(user, "You set the [name] for instant detonation.") + add_fingerprint(user) + ..() + return + +/obj/item/weapon/grenade/attack_hand() + walk(src, null, null) + ..() + return + +/obj/item/weapon/grenade/vendor_action(var/obj/machinery/vending/V) activate(V) \ No newline at end of file diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm index 1cb98be61a8..e06aaf53320 100644 --- a/code/game/objects/items/weapons/grenades/smokebomb.dm +++ b/code/game/objects/items/weapons/grenades/smokebomb.dm @@ -1,39 +1,39 @@ -/obj/item/weapon/grenade/smokebomb - desc = "It is set to detonate in 2 seconds. These high-tech grenades can have their color adapted on the fly with a multitool!" - name = "smoke bomb" - icon = 'icons/obj/grenade.dmi' - icon_state = "flashbang" - det_time = 20 - item_state = "flashbang" - slot_flags = SLOT_BELT - hud_state = "grenade_smoke" - var/datum/effect/effect/system/smoke_spread/bad/smoke - var/smoke_color - var/smoke_strength = 8 - -/obj/item/weapon/grenade/smokebomb/New() - ..() - src.smoke = new /datum/effect/effect/system/smoke_spread/bad() - src.smoke.attach(src) - -/obj/item/weapon/grenade/smokebomb/Destroy() - qdel(smoke) - smoke = null - return ..() - -/obj/item/weapon/grenade/smokebomb/detonate() - playsound(src, 'sound/effects/smoke.ogg', 50, 1, -3) - src.smoke.set_up(10, 0, usr.loc) - spawn(0) - for(var/i = 1 to smoke_strength) - src.smoke.start(smoke_color) - sleep(10) - qdel(src) - - return - -/obj/item/weapon/grenade/smokebomb/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I,/obj/item/device/multitool)) - var/new_smoke_color = input(user, "Choose a color for the smoke:", "Smoke Color", smoke_color) as color|null - if(new_smoke_color) - smoke_color = new_smoke_color +/obj/item/weapon/grenade/smokebomb + desc = "It is set to detonate in 2 seconds. These high-tech grenades can have their color adapted on the fly with a multitool!" + name = "smoke bomb" + icon = 'icons/obj/grenade.dmi' + icon_state = "flashbang" + det_time = 20 + item_state = "flashbang" + slot_flags = SLOT_BELT + hud_state = "grenade_smoke" + var/datum/effect/effect/system/smoke_spread/bad/smoke + var/smoke_color + var/smoke_strength = 8 + +/obj/item/weapon/grenade/smokebomb/New() + ..() + src.smoke = new /datum/effect/effect/system/smoke_spread/bad() + src.smoke.attach(src) + +/obj/item/weapon/grenade/smokebomb/Destroy() + qdel(smoke) + smoke = null + return ..() + +/obj/item/weapon/grenade/smokebomb/detonate() + playsound(src, 'sound/effects/smoke.ogg', 50, 1, -3) + src.smoke.set_up(10, 0, usr.loc) + spawn(0) + for(var/i = 1 to smoke_strength) + src.smoke.start(smoke_color) + sleep(10) + qdel(src) + + return + +/obj/item/weapon/grenade/smokebomb/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I,/obj/item/device/multitool)) + var/new_smoke_color = input(user, "Choose a color for the smoke:", "Smoke Color", smoke_color) as color|null + if(new_smoke_color) + smoke_color = new_smoke_color diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 5f1bd4503b4..1356b3a9479 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -1,79 +1,79 @@ -/obj/item/weapon/grenade/spawnergrenade - desc = "It is set to detonate in 5 seconds. It will unleash an unspecified anomaly into the vicinity." - name = "delivery grenade" - icon = 'icons/obj/grenade.dmi' - icon_state = "delivery" - item_state = "flashbang" - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4) - var/banglet = 0 - var/spawner_type = null // must be an object path - var/deliveryamt = 1 // amount of type to deliver - -// Detonate now just handles the two loops that query for people in lockers and people who can see it. -/obj/item/weapon/grenade/spawnergrenade/detonate() - - if(spawner_type && deliveryamt) - // Make a quick flash - var/turf/T = get_turf(src) - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(T, null)) - if(M:eyecheck() <= 0) - M.flash_eyes() - - // Spawn some hostile syndicate critters - for(var/i=1, i<=deliveryamt, i++) - var/atom/movable/x = new spawner_type(T) - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(x, pick(NORTH,SOUTH,EAST,WEST)) - qdel(src) - return - -/obj/item/weapon/grenade/spawnergrenade/manhacks - name = "manhack delivery grenade" - spawner_type = /mob/living/simple_mob/mechanical/viscerator - deliveryamt = 5 - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) - -/obj/item/weapon/grenade/spawnergrenade/manhacks/mercenary - spawner_type = /mob/living/simple_mob/mechanical/viscerator/mercenary - -/obj/item/weapon/grenade/spawnergrenade/manhacks/raider - spawner_type = /mob/living/simple_mob/mechanical/viscerator/raider - -/obj/item/weapon/grenade/spawnergrenade/manhacks/station - desc = "It is set to detonate in 5 seconds. It will deploy three weaponized survey drones." - deliveryamt = 3 - spawner_type = /mob/living/simple_mob/mechanical/viscerator/station - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ILLEGAL = 1) - -/obj/item/weapon/grenade/spawnergrenade/ward - name = "sentry delivery grenade" - desc = "It is set to detonate in 5 seconds. It will deploy a single thermal-optic sentry drone." - spawner_type = /mob/living/simple_mob/mechanical/ward/monitor/crew - deliveryamt = 1 - origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_BLUESPACE = 2) - -/obj/item/weapon/grenade/spawnergrenade/spesscarp - name = "carp delivery grenade" - spawner_type = /mob/living/simple_mob/animal/space/carp - deliveryamt = 5 - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) - -/obj/item/weapon/grenade/spawnergrenade/spider - name = "spider delivery grenade" - spawner_type = /mob/living/simple_mob/animal/giant_spider/hunter - deliveryamt = 3 - origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) - -//Sometimes you just need a sudden influx of spiders. -/obj/item/weapon/grenade/spawnergrenade/spider/briefcase - name = "briefcase" - desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." - icon_state = "briefcase" - item_state = "briefcase" - force = 8.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_LARGE - deliveryamt = 6 +/obj/item/weapon/grenade/spawnergrenade + desc = "It is set to detonate in 5 seconds. It will unleash an unspecified anomaly into the vicinity." + name = "delivery grenade" + icon = 'icons/obj/grenade.dmi' + icon_state = "delivery" + item_state = "flashbang" + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4) + var/banglet = 0 + var/spawner_type = null // must be an object path + var/deliveryamt = 1 // amount of type to deliver + +// Detonate now just handles the two loops that query for people in lockers and people who can see it. +/obj/item/weapon/grenade/spawnergrenade/detonate() + + if(spawner_type && deliveryamt) + // Make a quick flash + var/turf/T = get_turf(src) + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + for(var/mob/living/carbon/human/M in viewers(T, null)) + if(M:eyecheck() <= 0) + M.flash_eyes() + + // Spawn some hostile syndicate critters + for(var/i=1, i<=deliveryamt, i++) + var/atom/movable/x = new spawner_type(T) + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(x, pick(NORTH,SOUTH,EAST,WEST)) + qdel(src) + return + +/obj/item/weapon/grenade/spawnergrenade/manhacks + name = "manhack delivery grenade" + spawner_type = /mob/living/simple_mob/mechanical/viscerator + deliveryamt = 5 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +/obj/item/weapon/grenade/spawnergrenade/manhacks/mercenary + spawner_type = /mob/living/simple_mob/mechanical/viscerator/mercenary + +/obj/item/weapon/grenade/spawnergrenade/manhacks/raider + spawner_type = /mob/living/simple_mob/mechanical/viscerator/raider + +/obj/item/weapon/grenade/spawnergrenade/manhacks/station + desc = "It is set to detonate in 5 seconds. It will deploy three weaponized survey drones." + deliveryamt = 3 + spawner_type = /mob/living/simple_mob/mechanical/viscerator/station + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 3, TECH_ILLEGAL = 1) + +/obj/item/weapon/grenade/spawnergrenade/ward + name = "sentry delivery grenade" + desc = "It is set to detonate in 5 seconds. It will deploy a single thermal-optic sentry drone." + spawner_type = /mob/living/simple_mob/mechanical/ward/monitor/crew + deliveryamt = 1 + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_BLUESPACE = 2) + +/obj/item/weapon/grenade/spawnergrenade/spesscarp + name = "carp delivery grenade" + spawner_type = /mob/living/simple_mob/animal/space/carp + deliveryamt = 5 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +/obj/item/weapon/grenade/spawnergrenade/spider + name = "spider delivery grenade" + spawner_type = /mob/living/simple_mob/animal/giant_spider/hunter + deliveryamt = 3 + origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) + +//Sometimes you just need a sudden influx of spiders. +/obj/item/weapon/grenade/spawnergrenade/spider/briefcase + name = "briefcase" + desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "briefcase" + item_state = "briefcase" + force = 8.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + deliveryamt = 6 diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 35e55428a94..ea8a677f15f 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -1,337 +1,337 @@ -/obj/item/weapon/handcuffs - name = "handcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "handcuff" - slot_flags = SLOT_BELT - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 2 - throw_range = 5 - origin_tech = list(TECH_MATERIAL = 1) - matter = list(MAT_STEEL = 500) - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - var/elastic - var/dispenser = 0 - var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes - var/cuff_sound = 'sound/weapons/handcuffs.ogg' - var/cuff_type = "handcuffs" - var/use_time = 30 - sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') - -/obj/item/weapon/handcuffs/get_worn_icon_state(var/slot_name) - if(slot_name == slot_handcuffed_str) - return "handcuff1" //Simple - - return ..() - -/obj/item/weapon/handcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) - - if(!user.IsAdvancedToolUser()) - return - - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Uh ... how do those things work?!") - place_handcuffs(user, user) - return - - if(!C.handcuffed) - if (C == user) - place_handcuffs(user, user) - return - - //check for an aggressive grab (or robutts) - if(can_place(C, user)) - place_handcuffs(C, user) - else - to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") - -/obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user) - if(user == target) - return 1 - if(istype(user, /mob/living/silicon/robot)) - if(user.Adjacent(target)) - return 1 - else - for(var/obj/item/weapon/grab/G in target.grabbed_by) - if(G.loc == user && G.state >= GRAB_AGGRESSIVE) - return 1 - return 0 - -/obj/item/weapon/handcuffs/proc/place_handcuffs(var/mob/living/carbon/target, var/mob/user) - playsound(src, cuff_sound, 30, 1, -2) - - var/mob/living/carbon/human/H = target - if(!istype(H)) - return 0 - - if (!H.has_organ_for_slot(slot_handcuffed)) - to_chat(user, "\The [H] needs at least two wrists before you can cuff them together!") - return 0 - - if(istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. - to_chat(user, "\The [src] won't fit around \the [H.gloves]!") - return 0 - - user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") - - if(!do_after(user,use_time)) - return 0 - - if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime - return 0 - - add_attack_logs(user,H,"Handcuffed (attempt)") - feedback_add_details("handcuffs","H") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(H) - - user.visible_message("\The [user] has put [cuff_type] on \the [H]!") - - // Apply cuffs. - var/obj/item/weapon/handcuffs/cuffs = src - if(dispenser) - cuffs = new(get_turf(user)) - else - user.drop_from_inventory(cuffs) - cuffs.loc = target - target.handcuffed = cuffs - target.update_handcuffed() - target.drop_r_hand() - target.drop_l_hand() - target.stop_pulling() - return 1 - -/obj/item/weapon/handcuffs/equipped(var/mob/living/user,var/slot) - . = ..() - if(slot == slot_handcuffed) - user.drop_r_hand() - user.drop_l_hand() - user.stop_pulling() - -var/last_chew = 0 -/mob/living/carbon/human/RestrainedClickOn(var/atom/A) - if (A != src) return ..() - if (last_chew + 26 > world.time) return - - var/mob/living/carbon/human/H = A - if (!H.handcuffed) return - if (H.a_intent != I_HURT) return - if (H.zone_sel.selecting != O_MOUTH) return - if (H.wear_mask) return - if (istype(H.wear_suit, /obj/item/clothing/suit/straight_jacket)) return - - var/obj/item/organ/external/O = H.organs_by_name[(H.hand ? BP_L_HAND : BP_R_HAND)] - if (!O) return - - var/datum/gender/T = gender_datums[H.get_visible_gender()] - - var/s = "[H.name] chews on [T.his] [O.name]!" - H.visible_message(s, "You chew on your [O.name]!") - add_attack_logs(H,H,"chewed own [O.name]") - - if(O.take_damage(3,0,1,1,"teeth marks")) - H:UpdateDamageIcon() - - last_chew = world.time - -/obj/item/weapon/handcuffs/fuzzy - name = "fuzzy cuffs" - icon_state = "fuzzycuff" - breakouttime = 100 //VOREstation edit - desc = "Use this to keep... 'prisoners' in line." - -/obj/item/weapon/handcuffs/cable - name = "cable restraints" - desc = "Looks like some cables tied together. Could be used to tie something up." - icon_state = "cuff_white" - breakouttime = 300 //Deciseconds = 30s - cuff_sound = 'sound/weapons/cablecuff.ogg' - cuff_type = "cable restraints" - elastic = 1 - -/obj/item/weapon/handcuffs/cable/red - color = "#DD0000" - -/obj/item/weapon/handcuffs/cable/yellow - color = "#DDDD00" - -/obj/item/weapon/handcuffs/cable/blue - color = "#0000DD" - -/obj/item/weapon/handcuffs/cable/green - color = "#00DD00" - -/obj/item/weapon/handcuffs/cable/pink - color = "#DD00DD" - -/obj/item/weapon/handcuffs/cable/orange - color = "#DD8800" - -/obj/item/weapon/handcuffs/cable/cyan - color = "#00DDDD" - -/obj/item/weapon/handcuffs/cable/white - color = "#FFFFFF" - -/obj/item/weapon/handcuffs/cyborg - dispenser = 1 - -/obj/item/weapon/handcuffs/cable/tape - name = "tape restraints" - desc = "DIY!" - icon_state = "tape_cross" - item_state = null - icon = 'icons/obj/bureaucracy.dmi' - breakouttime = 200 - cuff_type = "duct tape" - -/obj/item/weapon/handcuffs/cable/tape/cyborg - dispenser = TRUE - -//Legcuffs. Not /really/ handcuffs, but its close enough. -/obj/item/weapon/handcuffs/legcuffs - name = "legcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "legcuff" - throwforce = 0 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MATERIAL = 1) - breakouttime = 300 //Deciseconds = 30s = 0.5 minute - cuff_type = "legcuffs" - sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') - elastic = 0 - cuff_sound = 'sound/weapons/handcuffs.ogg' //This shold work for now. - -/obj/item/weapon/handcuffs/legcuffs/get_worn_icon_state(var/slot_name) - if(slot_name == slot_legcuffed_str) - return "legcuff1" - - return ..() - -/obj/item/weapon/handcuffs/legcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) - if(!user.IsAdvancedToolUser()) - return - - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Uh ... how do those things work?!") - place_legcuffs(user, user) - return - - if(!C.legcuffed) - if (C == user) - place_legcuffs(user, user) - return - - //check for an aggressive grab (or robutts) - if(can_place(C, user)) - place_legcuffs(C, user) - else - to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") - -/obj/item/weapon/handcuffs/legcuffs/proc/place_legcuffs(var/mob/living/carbon/target, var/mob/user) - playsound(src, cuff_sound, 30, 1, -2) - - var/mob/living/carbon/human/H = target - if(!istype(H)) - return 0 - - if (!H.has_organ_for_slot(slot_legcuffed)) - to_chat(user, "\The [H] needs at least two ankles before you can cuff them together!") - return 0 - - if(istype(H.shoes,/obj/item/clothing/shoes/magboots/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. - to_chat(user, "\The [src] won't fit around \the [H.shoes]!") - return 0 - - user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") - - if(!do_after(user,use_time)) - return 0 - - if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime - return 0 - - add_attack_logs(user,H,"Legcuffed (attempt)") - feedback_add_details("legcuffs","H") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(H) - - user.visible_message("\The [user] has put [cuff_type] on \the [H]!") - - // Apply cuffs. - var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src - if(dispenser) - lcuffs = new(get_turf(user)) - else - user.drop_from_inventory(lcuffs) - lcuffs.loc = target - target.legcuffed = lcuffs - target.update_inv_legcuffed() - if(target.m_intent != "walk") - target.m_intent = "walk" - if(target.hud_used && user.hud_used.move_intent) - target.hud_used.move_intent.icon_state = "walking" - return 1 - -/obj/item/weapon/handcuffs/legcuffs/equipped(var/mob/living/user,var/slot) - . = ..() - if(slot == slot_legcuffed) - if(user.m_intent != "walk") - user.m_intent = "walk" - if(user.hud_used && user.hud_used.move_intent) - user.hud_used.move_intent.icon_state = "walking" - - -/obj/item/weapon/handcuffs/legcuffs/bola - name = "bola" - desc = "Keeps prey in line." - elastic = 1 - use_time = 0 - breakouttime = 30 - cuff_sound = 'sound/weapons/towelwipe.ogg' //Is there anything this sound can't do? - -/obj/item/weapon/handcuffs/legcuffs/bola/can_place(var/mob/target, var/mob/user) - if(user) //A ranged legcuff, until proper implementation as items it remains a projectile-only thing. - return 1 - -/obj/item/weapon/handcuffs/legcuffs/bola/dropped() - visible_message("\The [src] falls apart!") - qdel(src) - -/obj/item/weapon/handcuffs/legcuffs/bola/place_legcuffs(var/mob/living/carbon/target, var/mob/user) - playsound(src, cuff_sound, 30, 1, -2) - - var/mob/living/carbon/human/H = target - if(!istype(H)) - src.dropped() - return 0 - - if(!H.has_organ_for_slot(slot_legcuffed)) - H.visible_message("\The [src] slams into [H], but slides off!") - src.dropped() - return 0 - - H.visible_message("\The [H] has been snared by \the [src]!") - - // Apply cuffs. - var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src - lcuffs.loc = target - target.legcuffed = lcuffs - target.update_inv_legcuffed() - if(target.m_intent != "walk") - target.m_intent = "walk" - if(target.hud_used && user.hud_used.move_intent) - target.hud_used.move_intent.icon_state = "walking" - return 1 - -/obj/item/weapon/handcuffs/cable/plantfiber - name = "rope bindings" - desc = "A length of rope fashioned to hold someone's hands together." - color = "#7e6442" +/obj/item/weapon/handcuffs + name = "handcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "handcuff" + slot_flags = SLOT_BELT + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 2 + throw_range = 5 + origin_tech = list(TECH_MATERIAL = 1) + matter = list(MAT_STEEL = 500) + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + var/elastic + var/dispenser = 0 + var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes + var/cuff_sound = 'sound/weapons/handcuffs.ogg' + var/cuff_type = "handcuffs" + var/use_time = 30 + sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') + +/obj/item/weapon/handcuffs/get_worn_icon_state(var/slot_name) + if(slot_name == slot_handcuffed_str) + return "handcuff1" //Simple + + return ..() + +/obj/item/weapon/handcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) + + if(!user.IsAdvancedToolUser()) + return + + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Uh ... how do those things work?!") + place_handcuffs(user, user) + return + + if(!C.handcuffed) + if (C == user) + place_handcuffs(user, user) + return + + //check for an aggressive grab (or robutts) + if(can_place(C, user)) + place_handcuffs(C, user) + else + to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") + +/obj/item/weapon/handcuffs/proc/can_place(var/mob/target, var/mob/user) + if(user == target) + return 1 + if(istype(user, /mob/living/silicon/robot)) + if(user.Adjacent(target)) + return 1 + else + for(var/obj/item/weapon/grab/G in target.grabbed_by) + if(G.loc == user && G.state >= GRAB_AGGRESSIVE) + return 1 + return 0 + +/obj/item/weapon/handcuffs/proc/place_handcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + return 0 + + if (!H.has_organ_for_slot(slot_handcuffed)) + to_chat(user, "\The [H] needs at least two wrists before you can cuff them together!") + return 0 + + if(istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. + to_chat(user, "\The [src] won't fit around \the [H.gloves]!") + return 0 + + user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") + + if(!do_after(user,use_time)) + return 0 + + if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime + return 0 + + add_attack_logs(user,H,"Handcuffed (attempt)") + feedback_add_details("handcuffs","H") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(H) + + user.visible_message("\The [user] has put [cuff_type] on \the [H]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/cuffs = src + if(dispenser) + cuffs = new(get_turf(user)) + else + user.drop_from_inventory(cuffs) + cuffs.loc = target + target.handcuffed = cuffs + target.update_handcuffed() + target.drop_r_hand() + target.drop_l_hand() + target.stop_pulling() + return 1 + +/obj/item/weapon/handcuffs/equipped(var/mob/living/user,var/slot) + . = ..() + if(slot == slot_handcuffed) + user.drop_r_hand() + user.drop_l_hand() + user.stop_pulling() + +var/last_chew = 0 +/mob/living/carbon/human/RestrainedClickOn(var/atom/A) + if (A != src) return ..() + if (last_chew + 26 > world.time) return + + var/mob/living/carbon/human/H = A + if (!H.handcuffed) return + if (H.a_intent != I_HURT) return + if (H.zone_sel.selecting != O_MOUTH) return + if (H.wear_mask) return + if (istype(H.wear_suit, /obj/item/clothing/suit/straight_jacket)) return + + var/obj/item/organ/external/O = H.organs_by_name[(H.hand ? BP_L_HAND : BP_R_HAND)] + if (!O) return + + var/datum/gender/T = gender_datums[H.get_visible_gender()] + + var/s = "[H.name] chews on [T.his] [O.name]!" + H.visible_message(s, "You chew on your [O.name]!") + add_attack_logs(H,H,"chewed own [O.name]") + + if(O.take_damage(3,0,1,1,"teeth marks")) + H:UpdateDamageIcon() + + last_chew = world.time + +/obj/item/weapon/handcuffs/fuzzy + name = "fuzzy cuffs" + icon_state = "fuzzycuff" + breakouttime = 100 //VOREstation edit + desc = "Use this to keep... 'prisoners' in line." + +/obj/item/weapon/handcuffs/cable + name = "cable restraints" + desc = "Looks like some cables tied together. Could be used to tie something up." + icon_state = "cuff_white" + breakouttime = 300 //Deciseconds = 30s + cuff_sound = 'sound/weapons/cablecuff.ogg' + cuff_type = "cable restraints" + elastic = 1 + +/obj/item/weapon/handcuffs/cable/red + color = "#DD0000" + +/obj/item/weapon/handcuffs/cable/yellow + color = "#DDDD00" + +/obj/item/weapon/handcuffs/cable/blue + color = "#0000DD" + +/obj/item/weapon/handcuffs/cable/green + color = "#00DD00" + +/obj/item/weapon/handcuffs/cable/pink + color = "#DD00DD" + +/obj/item/weapon/handcuffs/cable/orange + color = "#DD8800" + +/obj/item/weapon/handcuffs/cable/cyan + color = "#00DDDD" + +/obj/item/weapon/handcuffs/cable/white + color = "#FFFFFF" + +/obj/item/weapon/handcuffs/cyborg + dispenser = 1 + +/obj/item/weapon/handcuffs/cable/tape + name = "tape restraints" + desc = "DIY!" + icon_state = "tape_cross" + item_state = null + icon = 'icons/obj/bureaucracy.dmi' + breakouttime = 200 + cuff_type = "duct tape" + +/obj/item/weapon/handcuffs/cable/tape/cyborg + dispenser = TRUE + +//Legcuffs. Not /really/ handcuffs, but its close enough. +/obj/item/weapon/handcuffs/legcuffs + name = "legcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "legcuff" + throwforce = 0 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MATERIAL = 1) + breakouttime = 300 //Deciseconds = 30s = 0.5 minute + cuff_type = "legcuffs" + sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/teshari/handcuffs.dmi') + elastic = 0 + cuff_sound = 'sound/weapons/handcuffs.ogg' //This shold work for now. + +/obj/item/weapon/handcuffs/legcuffs/get_worn_icon_state(var/slot_name) + if(slot_name == slot_legcuffed_str) + return "legcuff1" + + return ..() + +/obj/item/weapon/handcuffs/legcuffs/attack(var/mob/living/carbon/C, var/mob/living/user) + if(!user.IsAdvancedToolUser()) + return + + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Uh ... how do those things work?!") + place_legcuffs(user, user) + return + + if(!C.legcuffed) + if (C == user) + place_legcuffs(user, user) + return + + //check for an aggressive grab (or robutts) + if(can_place(C, user)) + place_legcuffs(C, user) + else + to_chat(user, "You need to have a firm grip on [C] before you can put \the [src] on!") + +/obj/item/weapon/handcuffs/legcuffs/proc/place_legcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + return 0 + + if (!H.has_organ_for_slot(slot_legcuffed)) + to_chat(user, "\The [H] needs at least two ankles before you can cuff them together!") + return 0 + + if(istype(H.shoes,/obj/item/clothing/shoes/magboots/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit. + to_chat(user, "\The [src] won't fit around \the [H.shoes]!") + return 0 + + user.visible_message("\The [user] is attempting to put [cuff_type] on \the [H]!") + + if(!do_after(user,use_time)) + return 0 + + if(!can_place(target, user)) //victim may have resisted out of the grab in the meantime + return 0 + + add_attack_logs(user,H,"Legcuffed (attempt)") + feedback_add_details("legcuffs","H") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(H) + + user.visible_message("\The [user] has put [cuff_type] on \the [H]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src + if(dispenser) + lcuffs = new(get_turf(user)) + else + user.drop_from_inventory(lcuffs) + lcuffs.loc = target + target.legcuffed = lcuffs + target.update_inv_legcuffed() + if(target.m_intent != "walk") + target.m_intent = "walk" + if(target.hud_used && user.hud_used.move_intent) + target.hud_used.move_intent.icon_state = "walking" + return 1 + +/obj/item/weapon/handcuffs/legcuffs/equipped(var/mob/living/user,var/slot) + . = ..() + if(slot == slot_legcuffed) + if(user.m_intent != "walk") + user.m_intent = "walk" + if(user.hud_used && user.hud_used.move_intent) + user.hud_used.move_intent.icon_state = "walking" + + +/obj/item/weapon/handcuffs/legcuffs/bola + name = "bola" + desc = "Keeps prey in line." + elastic = 1 + use_time = 0 + breakouttime = 30 + cuff_sound = 'sound/weapons/towelwipe.ogg' //Is there anything this sound can't do? + +/obj/item/weapon/handcuffs/legcuffs/bola/can_place(var/mob/target, var/mob/user) + if(user) //A ranged legcuff, until proper implementation as items it remains a projectile-only thing. + return 1 + +/obj/item/weapon/handcuffs/legcuffs/bola/dropped() + visible_message("\The [src] falls apart!") + qdel(src) + +/obj/item/weapon/handcuffs/legcuffs/bola/place_legcuffs(var/mob/living/carbon/target, var/mob/user) + playsound(src, cuff_sound, 30, 1, -2) + + var/mob/living/carbon/human/H = target + if(!istype(H)) + src.dropped() + return 0 + + if(!H.has_organ_for_slot(slot_legcuffed)) + H.visible_message("\The [src] slams into [H], but slides off!") + src.dropped() + return 0 + + H.visible_message("\The [H] has been snared by \the [src]!") + + // Apply cuffs. + var/obj/item/weapon/handcuffs/legcuffs/lcuffs = src + lcuffs.loc = target + target.legcuffed = lcuffs + target.update_inv_legcuffed() + if(target.m_intent != "walk") + target.m_intent = "walk" + if(target.hud_used && user.hud_used.move_intent) + target.hud_used.move_intent.icon_state = "walking" + return 1 + +/obj/item/weapon/handcuffs/cable/plantfiber + name = "rope bindings" + desc = "A length of rope fashioned to hold someone's hands together." + color = "#7e6442" diff --git a/code/game/objects/items/weapons/hydroponics.dm b/code/game/objects/items/weapons/hydroponics.dm index 15c1478bf32..04087646f74 100644 --- a/code/game/objects/items/weapons/hydroponics.dm +++ b/code/game/objects/items/weapons/hydroponics.dm @@ -1,113 +1,113 @@ -/* - * SeedBag - */ -//uncomment when this is updated to match storage update -/* -/obj/item/weapon/seedbag - icon = 'icons/obj/hydroponics_machines.dmi' - icon_state = "seedbag" - name = "Seed Bag" - desc = "A small satchel made for organizing seeds." - var/mode = 1; //0 = pick one at a time, 1 = pick all on tile - var/capacity = 500; //the number of seeds it can carry. - slot_flags = SLOT_BELT - w_class = ITEMSIZE_TINY - var/list/item_quants = list() - -/obj/item/weapon/seedbag/attack_self(mob/user as mob) - user.machine = src - interact(user) - -/obj/item/weapon/seedbag/verb/toggle_mode() - set name = "Switch Bagging Method" - set category = "Object" - - mode = !mode - switch (mode) - if(1) - to_chat(usr, "The bag now picks up all seeds in a tile at once.") - if(0) - to_chat(usr, "The bag now picks up one seed pouch at a time.") - -/obj/item/seeds/attackby(var/obj/item/O as obj, var/mob/user as mob) - ..() - if (istype(O, /obj/item/weapon/seedbag)) - var/obj/item/weapon/seedbag/S = O - if (S.mode == 1) - for (var/obj/item/seeds/G in locate(src.x,src.y,src.z)) - if (S.contents.len < S.capacity) - S.contents += G; - if(S.item_quants[G.name]) - S.item_quants[G.name]++ - else - S.item_quants[G.name] = 1 - else - to_chat(user, "The seed bag is full.") - S.updateUsrDialog() - return - to_chat(user, "You pick up all the seeds.") - else - if (S.contents.len < S.capacity) - S.contents += src; - if(S.item_quants[name]) - S.item_quants[name]++ - else - S.item_quants[name] = 1 - else - to_chat(user, "The seed bag is full.") - S.updateUsrDialog() - return - -/obj/item/weapon/seedbag/interact(mob/user as mob) - - var/dat = "Select an item:
                    " - - if (contents.len == 0) - dat += "No seeds loaded!" - else - for (var/O in item_quants) - if(item_quants[O] > 0) - var/N = item_quants[O] - dat += "[capitalize(O)]:" - dat += " [N] " - dat += "Vend" - dat += "
                    " - - dat += "
                    Unload All" - dat += "
                    " - user << browse("Seedbag Supplies[dat]", "window=seedbag") - onclose(user, "seedbag") - return - -/obj/item/weapon/seedbag/Topic(href, href_list) - if(..()) - return - - usr.machine = src - if ( href_list["vend"] ) - var/N = href_list["vend"] - - if(item_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. - return - - item_quants[N] -= 1 - for(var/obj/O in contents) - if(O.name == N) - O.loc = get_turf(src) - usr.put_in_hands(O) - break - - else if ( href_list["unload"] ) - item_quants.Cut() - for(var/obj/O in contents ) - O.loc = get_turf(src) - - src.updateUsrDialog() - return - -/obj/item/weapon/seedbag/updateUsrDialog() - var/list/nearby = range(1, src) - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - src.attack_self(M) -*/ +/* + * SeedBag + */ +//uncomment when this is updated to match storage update +/* +/obj/item/weapon/seedbag + icon = 'icons/obj/hydroponics_machines.dmi' + icon_state = "seedbag" + name = "Seed Bag" + desc = "A small satchel made for organizing seeds." + var/mode = 1; //0 = pick one at a time, 1 = pick all on tile + var/capacity = 500; //the number of seeds it can carry. + slot_flags = SLOT_BELT + w_class = ITEMSIZE_TINY + var/list/item_quants = list() + +/obj/item/weapon/seedbag/attack_self(mob/user as mob) + user.machine = src + interact(user) + +/obj/item/weapon/seedbag/verb/toggle_mode() + set name = "Switch Bagging Method" + set category = "Object" + + mode = !mode + switch (mode) + if(1) + to_chat(usr, "The bag now picks up all seeds in a tile at once.") + if(0) + to_chat(usr, "The bag now picks up one seed pouch at a time.") + +/obj/item/seeds/attackby(var/obj/item/O as obj, var/mob/user as mob) + ..() + if (istype(O, /obj/item/weapon/seedbag)) + var/obj/item/weapon/seedbag/S = O + if (S.mode == 1) + for (var/obj/item/seeds/G in locate(src.x,src.y,src.z)) + if (S.contents.len < S.capacity) + S.contents += G; + if(S.item_quants[G.name]) + S.item_quants[G.name]++ + else + S.item_quants[G.name] = 1 + else + to_chat(user, "The seed bag is full.") + S.updateUsrDialog() + return + to_chat(user, "You pick up all the seeds.") + else + if (S.contents.len < S.capacity) + S.contents += src; + if(S.item_quants[name]) + S.item_quants[name]++ + else + S.item_quants[name] = 1 + else + to_chat(user, "The seed bag is full.") + S.updateUsrDialog() + return + +/obj/item/weapon/seedbag/interact(mob/user as mob) + + var/dat = "Select an item:
                    " + + if (contents.len == 0) + dat += "No seeds loaded!" + else + for (var/O in item_quants) + if(item_quants[O] > 0) + var/N = item_quants[O] + dat += "[capitalize(O)]:" + dat += " [N] " + dat += "Vend" + dat += "
                    " + + dat += "
                    Unload All" + dat += "
                    " + user << browse("Seedbag Supplies[dat]", "window=seedbag") + onclose(user, "seedbag") + return + +/obj/item/weapon/seedbag/Topic(href, href_list) + if(..()) + return + + usr.machine = src + if ( href_list["vend"] ) + var/N = href_list["vend"] + + if(item_quants[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. + return + + item_quants[N] -= 1 + for(var/obj/O in contents) + if(O.name == N) + O.loc = get_turf(src) + usr.put_in_hands(O) + break + + else if ( href_list["unload"] ) + item_quants.Cut() + for(var/obj/O in contents ) + O.loc = get_turf(src) + + src.updateUsrDialog() + return + +/obj/item/weapon/seedbag/updateUsrDialog() + var/list/nearby = range(1, src) + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + src.attack_self(M) +*/ diff --git a/code/game/objects/items/weapons/id cards/cards.dm b/code/game/objects/items/weapons/id cards/cards.dm index 260eff8539b..c02213078b6 100644 --- a/code/game/objects/items/weapons/id cards/cards.dm +++ b/code/game/objects/items/weapons/id cards/cards.dm @@ -136,3 +136,29 @@ uses = CEILING(uses, 1) //Ensures no decimal uses nonsense, rounds up to be nice to_chat(usr, "You add \the [O] to \the [src]. Increasing the uses of \the [src] to [uses].") qdel(O) + + +/obj/item/weapon/card/emag/borg + uses = 12 + var/burnt_out = FALSE + +/obj/item/weapon/card/emag/borg/afterattack(atom/A, mob/user, proximity, var/click_parameters) + if(!proximity || burnt_out) return + var/used_uses = A.emag_act(uses, user, src) + if(used_uses < 0) + return ..(A, user, proximity, click_parameters) + + uses -= used_uses + A.add_fingerprint(user) + //Vorestation Edit: Because some things (read lift doors) don't get emagged + if(used_uses) + log_and_message_admins("emagged \an [A].") + else + log_and_message_admins("attempted to emag \an [A].") + // Vorestation Edit: End of Edit + + if(uses<1) + user.visible_message("\The [src] fizzles and sparks - it seems it's been used once too often, and is now spent.") + burnt_out = TRUE + + return 1 \ No newline at end of file diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index a710df388b7..7ff8063e32a 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -1,631 +1,631 @@ -#define MALFUNCTION_TEMPORARY 1 -#define MALFUNCTION_PERMANENT 2 - - -/obj/item/weapon/implant - name = "implant" - icon = 'icons/obj/device.dmi' - icon_state = "implant" - w_class = ITEMSIZE_TINY - show_messages = TRUE - - var/implanted = null - var/mob/imp_in = null - var/obj/item/organ/external/part = null - var/implant_color = "b" - var/allow_reagents = 0 - var/malfunction = 0 - var/initialize_loc = BP_TORSO - var/known_implant = FALSE - -/obj/item/weapon/implant/proc/trigger(emote, source as mob) - return - -/obj/item/weapon/implant/proc/activate() - return - -// Moves the implant where it needs to go, and tells it if there's more to be done in post_implant -/obj/item/weapon/implant/proc/handle_implant(var/mob/source, var/target_zone = BP_TORSO) - . = TRUE - imp_in = source - implanted = TRUE - if(ishuman(source)) - var/mob/living/carbon/human/H = source - var/obj/item/organ/external/affected = H.get_organ(target_zone) - if(affected) - affected.implants |= src - part = affected - if(part) - forceMove(part) - else - forceMove(source) - - listening_objects |= src - -// Takes place after handle_implant, if that returns TRUE -/obj/item/weapon/implant/proc/post_implant(var/mob/source) - -/obj/item/weapon/implant/proc/get_data() - return "No information available" - -/obj/item/weapon/implant/proc/hear(message, source as mob) - return - -/obj/item/weapon/implant/proc/islegal() - return 0 - -/obj/item/weapon/implant/proc/meltdown() //breaks it down, making implant unrecongizible - to_chat(imp_in, "You feel something melting inside [part ? "your [part.name]" : "you"]!") - if (part) - part.take_damage(burn = 15, used_weapon = "Electronics meltdown") - else - var/mob/living/M = imp_in - M.apply_damage(15,BURN) - name = "melted implant" - desc = "Charred circuit in melted plastic case. Wonder what that used to be..." - icon_state = "implant_melted" - malfunction = MALFUNCTION_PERMANENT - -/obj/item/weapon/implant/proc/implant_loadout(var/mob/living/carbon/human/H) - . = istype(H) && handle_implant(H, initialize_loc) - if(.) - invisibility = initial(invisibility) - known_implant = TRUE - post_implant(H) - -/obj/item/weapon/implant/Destroy() - if(part) - part.implants.Remove(src) - part = null - listening_objects.Remove(src) - imp_in = null - return ..() - -/obj/item/weapon/implant/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/implanter)) - var/obj/item/weapon/implanter/implanter = I - if(implanter.imp) - return // It's full. - user.drop_from_inventory(src) - forceMove(implanter) - implanter.imp = src - implanter.update() - else - ..() - - - -////////////////////////////// -// Tracking Implant -////////////////////////////// -GLOBAL_LIST_BOILERPLATE(all_tracking_implants, /obj/item/weapon/implant/tracking) - -/obj/item/weapon/implant/tracking - name = "tracking implant" - desc = "An implant normally given to dangerous criminals. Allows security to track your location." - known_implant = TRUE - var/id = 1 - var/degrade_time = 10 MINUTES //How long before the implant stops working outside of a living body. - -/obj/item/weapon/implant/tracking/weak //This is for the loadout - degrade_time = 2.5 MINUTES - -/obj/item/weapon/implant/tracking/New() - id = rand(1, 1000) - ..() - -/obj/item/weapon/implant/tracking/post_implant(var/mob/source) - START_PROCESSING(SSobj, src) - -/obj/item/weapon/implant/tracking/Destroy() - STOP_PROCESSING(SSobj, src) - if(part) - part.implants -= src - part = imp_in = null - return ..() - -/obj/item/weapon/implant/tracking/process() - var/implant_location = src.loc - if(ismob(implant_location)) - var/mob/living/L = implant_location - if(L.stat == DEAD) - if(world.time >= L.timeofdeath + degrade_time) - name = "melted implant" - desc = "Charred circuit in melted plastic case. Wonder what that used to be..." - icon_state = "implant_melted" - malfunction = MALFUNCTION_PERMANENT - STOP_PROCESSING(SSobj, src) - return 1 - -/obj/item/weapon/implant/tracking/get_data() - var/dat = {"Implant Specifications:
                    -Name: Tracking Beacon
                    -Life: 10 minutes after death of host
                    -Important Notes: None
                    -
                    -Implant Details:
                    -Function: Continuously transmits low power signal. Useful for tracking.
                    -Special Features:
                    -Neuro-Safe- Specialized shell absorbs excess voltages self-destructing the chip if -a malfunction occurs thereby securing safety of subject. The implant will melt and -disintegrate into bio-safe elements.
                    -Integrity: Gradient creates slight risk of being overcharged and frying the -circuitry. As a result neurotoxins can cause massive damage.
                    -Implant Specifics:
                    "} - return dat - -/obj/item/weapon/implant/tracking/emp_act(severity) - if (malfunction) //no, dawg, you can't malfunction while you are malfunctioning - return - malfunction = MALFUNCTION_TEMPORARY - - var/delay = 20 - switch(severity) - if(1) - if(prob(60)) - meltdown() - if(2) - delay = rand(5*60*10,15*60*10) //from 5 to 15 minutes of free time - if(3) - delay = rand(2*60*10,5*60*10) //from 2 to 5 minutes of free time - if(4) - delay = rand(0.5*60*10,1*60*10) //from .5 to 1 minutes of free time - - spawn(delay) - malfunction-- - -////////////////////////////// -// Death Explosive Implant -////////////////////////////// -/obj/item/weapon/implant/dexplosive - name = "explosive" - desc = "And boom goes the weasel." - icon_state = "implant_evil" - -/obj/item/weapon/implant/dexplosive/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Robust Corp RX-78 Employee Management Implant
                    -Life: Activates upon death.
                    -Important Notes: Explodes
                    -
                    -Implant Details:
                    -Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    -Special Features: Explodes
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - - -/obj/item/weapon/implant/dexplosive/trigger(emote, source as mob) - if(emote == "deathgasp") - src.activate("death") - return - - -/obj/item/weapon/implant/dexplosive/activate(var/cause) - if((!cause) || (!src.imp_in)) return 0 - explosion(src, -1, 0, 2, 3, 0)//This might be a bit much, dono will have to see. - if(src.imp_in) - src.imp_in.gib() - -/obj/item/weapon/implant/dexplosive/islegal() - return 0 - -////////////////////////////// -// Explosive Implant -////////////////////////////// -/obj/item/weapon/implant/explosive - name = "explosive implant" - desc = "A military grade micro bio-explosive. Highly dangerous." - var/elevel = "Localized Limb" - var/phrase = "supercalifragilisticexpialidocious" - icon_state = "implant_evil" - -/obj/item/weapon/implant/explosive/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Robust Corp RX-78 Intimidation Class Implant
                    -Life: Activates upon codephrase.
                    -Important Notes: Explodes
                    -
                    -Implant Details:
                    -Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    -Special Features: Explodes
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - -/obj/item/weapon/implant/explosive/hear_talk(mob/M, list/message_pieces, verb) - var/msg = multilingual_to_message(message_pieces) - hear(msg) - return - -/obj/item/weapon/implant/explosive/hear(var/msg) - var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") - msg = replace_characters(msg, replacechars) - if(findtext(msg,phrase)) - activate() - qdel(src) - -/obj/item/weapon/implant/explosive/activate() - if (malfunction == MALFUNCTION_PERMANENT) - return - - if(istype(imp_in, /mob/)) - var/mob/T = imp_in - message_admins("Explosive implant triggered in [T] ([T.key]). (JMP) ") - log_game("Explosive implant triggered in [T] ([T.key]).") - - if(ishuman(imp_in)) - if (elevel == "Localized Limb") - if(part) //For some reason, small_boom() didn't work. So have this bit of working copypaste. - imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") - playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) - sleep(25) - if (istype(part,/obj/item/organ/external/chest) || \ - istype(part,/obj/item/organ/external/groin) || \ - istype(part,/obj/item/organ/external/head)) - part.createwound(BRUISE, 80) //mangle them instead - explosion(get_turf(imp_in), -1, -1, 1, 3) - qdel(src) - else - explosion(get_turf(imp_in), -1, -1, 1, 3) - part.droplimb(0,DROPLIMB_BLUNT) - qdel(src) - if (elevel == "Destroy Body") - explosion(get_turf(T), -1, 0, 1, 6) - T.gib() - if (elevel == "Full Explosion") - explosion(get_turf(T), 0, 1, 3, 6) - T.gib() - - else - explosion(get_turf(imp_in), 0, 1, 3, 6) - - var/turf/t = get_turf(imp_in) - - if(t) - t.hotspot_expose(3500,125) - -/obj/item/weapon/implant/explosive/post_implant(mob/source as mob) - elevel = tgui_alert(usr, "What sort of explosion would you prefer?", "Implant Intent", list("Localized Limb", "Destroy Body", "Full Explosion")) - phrase = tgui_input_text(usr, "Choose activation phrase:") - var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") - phrase = replace_characters(phrase, replacechars) - usr.mind.store_memory("Explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.", 0, 0) - to_chat(usr, "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.") - -/obj/item/weapon/implant/explosive/emp_act(severity) - if (malfunction) - return - malfunction = MALFUNCTION_TEMPORARY - switch (severity) - if (4) //Weak EMP will make implant tear limbs off. - if (prob(25)) - small_boom() - if (3) //Weak EMP will make implant tear limbs off. - if (prob(50)) - small_boom() - if (2) //strong EMP will melt implant either making it go off, or disarming it - if (prob(70)) - if (prob(75)) - small_boom() - else - if (prob(13)) - activate() //chance of bye bye - else - meltdown() //chance of implant disarming - if (1) //strong EMP will melt implant either making it go off, or disarming it - if (prob(70)) - if (prob(50)) - small_boom() - else - if (prob(50)) - activate() //50% chance of bye bye - else - meltdown() //50% chance of implant disarming - spawn (20) - malfunction-- - -/obj/item/weapon/implant/explosive/islegal() - return 0 - -/obj/item/weapon/implant/explosive/proc/small_boom() - if (ishuman(imp_in) && part) - imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") - playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) - spawn(25) - if (ishuman(imp_in) && part) - //No tearing off these parts since it's pretty much killing - //and you can't replace groins - if (istype(part,/obj/item/organ/external/chest) || \ - istype(part,/obj/item/organ/external/groin) || \ - istype(part,/obj/item/organ/external/head)) - part.createwound(BRUISE, 80) //mangle them instead - else - part.droplimb(0,DROPLIMB_BLUNT) - explosion(get_turf(imp_in), -1, -1, 1, 3) - qdel(src) - -////////////////////////////// -// Chemical Implant -////////////////////////////// -GLOBAL_LIST_BOILERPLATE(all_chem_implants, /obj/item/weapon/implant/chem) - -/obj/item/weapon/implant/chem - name = "chemical implant" - desc = "Injects things." - allow_reagents = 1 - known_implant = TRUE - -/obj/item/weapon/implant/chem/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Robust Corp MJ-420 Prisoner Management Implant
                    -Life: Deactivates upon death but remains within the body.
                    -Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject
                    -will suffer from an increased appetite.

                    -
                    -Implant Details:
                    -Function: Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal
                    -the implant releases the chemicals directly into the blood stream.
                    -Special Features: -Micro-Capsule- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.
                    -Can only be loaded while still in its original case.
                    -Integrity: Implant will last so long as the subject is alive. However, if the subject suffers from malnutrition,
                    -the implant may become unstable and either pre-maturely inject the subject or simply break."} - return dat - -/obj/item/weapon/implant/chem/New() - ..() - var/datum/reagents/R = new/datum/reagents(50) - reagents = R - R.my_atom = src - -/obj/item/weapon/implant/chem/trigger(emote, source as mob) - if(emote == "deathgasp") - src.activate(src.reagents.total_volume) - return - -/obj/item/weapon/implant/chem/activate(var/cause) - if((!cause) || (!src.imp_in)) return 0 - var/mob/living/carbon/R = src.imp_in - src.reagents.trans_to_mob(R, cause, CHEM_BLOOD) - to_chat(R, "You hear a faint *beep*.") - if(!src.reagents.total_volume) - to_chat(R, "You hear a faint click from your chest.") - playsound(R, 'sound/weapons/empty.ogg', 10, 1) - spawn(0) - qdel(src) - return - -/obj/item/weapon/implant/chem/emp_act(severity) - if (malfunction) - return - malfunction = MALFUNCTION_TEMPORARY - - switch(severity) - if(1) - if(prob(60)) - activate(20) - if(2) - if(prob(40)) - activate(20) - if(3) - if(prob(40)) - activate(5) - if(4) - if(prob(20)) - activate(5) - - spawn(20) - malfunction-- - -////////////////////////////// -// Loyalty Implant -////////////////////////////// -/obj/item/weapon/implant/loyalty - name = "loyalty implant" - desc = "Makes you loyal or such." - known_implant = TRUE - -/obj/item/weapon/implant/loyalty/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] Employee Management Implant
                    -Life: Ten years.
                    -Important Notes: Personnel injected with this device tend to be much more loyal to the company.
                    -
                    -Implant Details:
                    -Function: Contains a small pod of nanobots that manipulate the host's mental functions.
                    -Special Features: Will prevent and cure most forms of brainwashing.
                    -Integrity: Implant will last so long as the nanobots are inside the bloodstream."} - return dat - -/obj/item/weapon/implant/loyalty/handle_implant(mob/M, target_zone = BP_TORSO) - . = ..(M, target_zone) - if(!istype(M, /mob/living/carbon/human)) - . = FALSE - var/mob/living/carbon/human/H = M - var/datum/antagonist/antag_data = get_antag_data(H.mind.special_role) - if(antag_data && (antag_data.flags & ANTAG_IMPLANT_IMMUNE)) - H.visible_message("[H] seems to resist the implant!", "You feel the corporate tendrils of [using_map.company_name] try to invade your mind!") - . = FALSE - -/obj/item/weapon/implant/loyalty/post_implant(mob/M) - var/mob/living/carbon/human/H = M - clear_antag_roles(H.mind, 1) - to_chat(H, "You feel a surge of loyalty towards [using_map.company_name].") - -////////////////////////////// -// Adrenaline Implant -////////////////////////////// -/obj/item/weapon/implant/adrenalin - name = "adrenalin" - desc = "Removes all stuns and knockdowns." - var/uses - -/obj/item/weapon/implant/adrenalin/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Cybersun Industries Adrenalin Implant
                    -Life: Five days.
                    -Important Notes: Illegal
                    -
                    -Implant Details: Subjects injected with implant can activate a massive injection of adrenalin.
                    -Function: Contains nanobots to stimulate body to mass-produce Adrenalin.
                    -Special Features: Will prevent and cure most forms of brainwashing.
                    -Integrity: Implant can only be used three times before the nanobots are depleted."} - return dat - - -/obj/item/weapon/implant/adrenalin/trigger(emote, mob/source as mob) - if (src.uses < 1) return 0 - if (emote == "pale") - src.uses-- - to_chat(source, "You feel a sudden surge of energy!") - source.SetStunned(0) - source.SetWeakened(0) - source.SetParalysis(0) - - return - -/obj/item/weapon/implant/adrenalin/post_implant(mob/source) - source.mind.store_memory("A implant can be activated by using the pale emote, say *pale to attempt to activate.", 0, 0) - to_chat(source, "The implanted freedom implant can be activated by using the pale emote, say *pale to attempt to activate.") - -////////////////////////////// -// Death Alarm Implant -////////////////////////////// -/obj/item/weapon/implant/death_alarm - name = "death alarm implant" - desc = "An alarm which monitors host vital signs and transmits a radio message upon death." - origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 2, TECH_DATA = 1) - known_implant = TRUE - var/mobname = "Will Robinson" - -/obj/item/weapon/implant/death_alarm/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    -Life: Activates upon death.
                    -Important Notes: Alerts crew to crewmember death.
                    -
                    -Implant Details:
                    -Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    -Special Features: Alerts crew to crewmember death.
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - -/obj/item/weapon/implant/death_alarm/process() - if (!implanted) return - var/mob/M = imp_in - - if(isnull(M)) // If the mob got gibbed - activate() - else if(M.stat == 2) - activate("death") - -/obj/item/weapon/implant/death_alarm/activate(var/cause) - var/mob/M = imp_in - var/area/t = get_area(M) - switch (cause) - if("death") - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - if(istype(t, /area/syndicate_station) || istype(t, /area/syndicate_mothership) || istype(t, /area/shuttle/syndicate_elite) ) - //give the syndies a bit of stealth - a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Medical") - else - a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Medical") - qdel(a) - STOP_PROCESSING(SSobj, src) - if ("emp") - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - var/name = prob(50) ? t.name : pick(teleportlocs) - a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Medical") - qdel(a) - else - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm") -// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Security") -// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Medical") - qdel(a) - STOP_PROCESSING(SSobj, src) - -/obj/item/weapon/implant/death_alarm/emp_act(severity) //for some reason alarms stop going off in case they are emp'd, even without this - if (malfunction) //so I'm just going to add a meltdown chance here - return - malfunction = MALFUNCTION_TEMPORARY - - activate("emp") //let's shout that this dude is dead - if(severity == 1) - if(prob(40)) //small chance of obvious meltdown - meltdown() - else if (prob(60)) //but more likely it will just quietly die - malfunction = MALFUNCTION_PERMANENT - STOP_PROCESSING(SSobj, src) - - spawn(20) - malfunction-- - -/obj/item/weapon/implant/death_alarm/post_implant(mob/source as mob) - mobname = source.real_name - START_PROCESSING(SSobj, src) - -////////////////////////////// -// Compressed Matter Implant -////////////////////////////// -/obj/item/weapon/implant/compressed - name = "compressed matter implant" - desc = "Based on compressed matter technology, can store a single item." - icon_state = "implant_evil" - var/activation_emote = "sigh" - var/obj/item/scanned = null - origin_tech = list(TECH_MATERIAL = 4, TECH_BIO = 2, TECH_ILLEGAL = 2) - -/obj/item/weapon/implant/compressed/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    -Life: Activates upon death.
                    -Important Notes: Alerts crew to crewmember death.
                    -
                    -Implant Details:
                    -Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    -Special Features: Alerts crew to crewmember death.
                    -Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} - return dat - -/obj/item/weapon/implant/compressed/trigger(emote, mob/source as mob) - if (src.scanned == null) - return 0 - - if (emote == src.activation_emote) - to_chat(source, "The air glows as \the [src.scanned.name] uncompresses.") - activate() - -/obj/item/weapon/implant/compressed/activate() - var/turf/t = get_turf(src) - if (imp_in) - imp_in.put_in_hands(scanned) - else - scanned.loc = t - qdel(src) - -/obj/item/weapon/implant/compressed/post_implant(mob/source) - var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) - if(!activation_emote) - activation_emote = pick(choices) - if (source.mind) - source.mind.store_memory("Compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - to_chat(source, "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") - - -/obj/item/weapon/implant/compressed/islegal() - return 0 +#define MALFUNCTION_TEMPORARY 1 +#define MALFUNCTION_PERMANENT 2 + + +/obj/item/weapon/implant + name = "implant" + icon = 'icons/obj/device.dmi' + icon_state = "implant" + w_class = ITEMSIZE_TINY + show_messages = TRUE + + var/implanted = null + var/mob/imp_in = null + var/obj/item/organ/external/part = null + var/implant_color = "b" + var/allow_reagents = 0 + var/malfunction = 0 + var/initialize_loc = BP_TORSO + var/known_implant = FALSE + +/obj/item/weapon/implant/proc/trigger(emote, source as mob) + return + +/obj/item/weapon/implant/proc/activate() + return + +// Moves the implant where it needs to go, and tells it if there's more to be done in post_implant +/obj/item/weapon/implant/proc/handle_implant(var/mob/source, var/target_zone = BP_TORSO) + . = TRUE + imp_in = source + implanted = TRUE + if(ishuman(source)) + var/mob/living/carbon/human/H = source + var/obj/item/organ/external/affected = H.get_organ(target_zone) + if(affected) + affected.implants |= src + part = affected + if(part) + forceMove(part) + else + forceMove(source) + + listening_objects |= src + +// Takes place after handle_implant, if that returns TRUE +/obj/item/weapon/implant/proc/post_implant(var/mob/source) + +/obj/item/weapon/implant/proc/get_data() + return "No information available" + +/obj/item/weapon/implant/proc/hear(message, source as mob) + return + +/obj/item/weapon/implant/proc/islegal() + return 0 + +/obj/item/weapon/implant/proc/meltdown() //breaks it down, making implant unrecongizible + to_chat(imp_in, "You feel something melting inside [part ? "your [part.name]" : "you"]!") + if (part) + part.take_damage(burn = 15, used_weapon = "Electronics meltdown") + else + var/mob/living/M = imp_in + M.apply_damage(15,BURN) + name = "melted implant" + desc = "Charred circuit in melted plastic case. Wonder what that used to be..." + icon_state = "implant_melted" + malfunction = MALFUNCTION_PERMANENT + +/obj/item/weapon/implant/proc/implant_loadout(var/mob/living/carbon/human/H) + . = istype(H) && handle_implant(H, initialize_loc) + if(.) + invisibility = initial(invisibility) + known_implant = TRUE + post_implant(H) + +/obj/item/weapon/implant/Destroy() + if(part) + part.implants.Remove(src) + part = null + listening_objects.Remove(src) + imp_in = null + return ..() + +/obj/item/weapon/implant/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/implanter)) + var/obj/item/weapon/implanter/implanter = I + if(implanter.imp) + return // It's full. + user.drop_from_inventory(src) + forceMove(implanter) + implanter.imp = src + implanter.update() + else + ..() + + + +////////////////////////////// +// Tracking Implant +////////////////////////////// +GLOBAL_LIST_BOILERPLATE(all_tracking_implants, /obj/item/weapon/implant/tracking) + +/obj/item/weapon/implant/tracking + name = "tracking implant" + desc = "An implant normally given to dangerous criminals. Allows security to track your location." + known_implant = TRUE + var/id = 1 + var/degrade_time = 10 MINUTES //How long before the implant stops working outside of a living body. + +/obj/item/weapon/implant/tracking/weak //This is for the loadout + degrade_time = 2.5 MINUTES + +/obj/item/weapon/implant/tracking/New() + id = rand(1, 1000) + ..() + +/obj/item/weapon/implant/tracking/post_implant(var/mob/source) + START_PROCESSING(SSobj, src) + +/obj/item/weapon/implant/tracking/Destroy() + STOP_PROCESSING(SSobj, src) + if(part) + part.implants -= src + part = imp_in = null + return ..() + +/obj/item/weapon/implant/tracking/process() + var/implant_location = src.loc + if(ismob(implant_location)) + var/mob/living/L = implant_location + if(L.stat == DEAD) + if(world.time >= L.timeofdeath + degrade_time) + name = "melted implant" + desc = "Charred circuit in melted plastic case. Wonder what that used to be..." + icon_state = "implant_melted" + malfunction = MALFUNCTION_PERMANENT + STOP_PROCESSING(SSobj, src) + return 1 + +/obj/item/weapon/implant/tracking/get_data() + var/dat = {"Implant Specifications:
                    +Name: Tracking Beacon
                    +Life: 10 minutes after death of host
                    +Important Notes: None
                    +
                    +Implant Details:
                    +Function: Continuously transmits low power signal. Useful for tracking.
                    +Special Features:
                    +Neuro-Safe- Specialized shell absorbs excess voltages self-destructing the chip if +a malfunction occurs thereby securing safety of subject. The implant will melt and +disintegrate into bio-safe elements.
                    +Integrity: Gradient creates slight risk of being overcharged and frying the +circuitry. As a result neurotoxins can cause massive damage.
                    +Implant Specifics:
                    "} + return dat + +/obj/item/weapon/implant/tracking/emp_act(severity) + if (malfunction) //no, dawg, you can't malfunction while you are malfunctioning + return + malfunction = MALFUNCTION_TEMPORARY + + var/delay = 20 + switch(severity) + if(1) + if(prob(60)) + meltdown() + if(2) + delay = rand(5*60*10,15*60*10) //from 5 to 15 minutes of free time + if(3) + delay = rand(2*60*10,5*60*10) //from 2 to 5 minutes of free time + if(4) + delay = rand(0.5*60*10,1*60*10) //from .5 to 1 minutes of free time + + spawn(delay) + malfunction-- + +////////////////////////////// +// Death Explosive Implant +////////////////////////////// +/obj/item/weapon/implant/dexplosive + name = "explosive" + desc = "And boom goes the weasel." + icon_state = "implant_evil" + +/obj/item/weapon/implant/dexplosive/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Robust Corp RX-78 Employee Management Implant
                    +Life: Activates upon death.
                    +Important Notes: Explodes
                    +
                    +Implant Details:
                    +Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    +Special Features: Explodes
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + + +/obj/item/weapon/implant/dexplosive/trigger(emote, source as mob) + if(emote == "deathgasp") + src.activate("death") + return + + +/obj/item/weapon/implant/dexplosive/activate(var/cause) + if((!cause) || (!src.imp_in)) return 0 + explosion(src, -1, 0, 2, 3, 0)//This might be a bit much, dono will have to see. + if(src.imp_in) + src.imp_in.gib() + +/obj/item/weapon/implant/dexplosive/islegal() + return 0 + +////////////////////////////// +// Explosive Implant +////////////////////////////// +/obj/item/weapon/implant/explosive + name = "explosive implant" + desc = "A military grade micro bio-explosive. Highly dangerous." + var/elevel = "Localized Limb" + var/phrase = "supercalifragilisticexpialidocious" + icon_state = "implant_evil" + +/obj/item/weapon/implant/explosive/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Robust Corp RX-78 Intimidation Class Implant
                    +Life: Activates upon codephrase.
                    +Important Notes: Explodes
                    +
                    +Implant Details:
                    +Function: Contains a compact, electrically detonated explosive that detonates upon receiving a specially encoded signal or upon host death.
                    +Special Features: Explodes
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + +/obj/item/weapon/implant/explosive/hear_talk(mob/M, list/message_pieces, verb) + var/msg = multilingual_to_message(message_pieces) + hear(msg) + return + +/obj/item/weapon/implant/explosive/hear(var/msg) + var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") + msg = replace_characters(msg, replacechars) + if(findtext(msg,phrase)) + activate() + qdel(src) + +/obj/item/weapon/implant/explosive/activate() + if (malfunction == MALFUNCTION_PERMANENT) + return + + if(istype(imp_in, /mob/)) + var/mob/T = imp_in + message_admins("Explosive implant triggered in [T] ([T.key]). (JMP) ") + log_game("Explosive implant triggered in [T] ([T.key]).") + + if(ishuman(imp_in)) + if (elevel == "Localized Limb") + if(part) //For some reason, small_boom() didn't work. So have this bit of working copypaste. + imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") + playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) + sleep(25) + if (istype(part,/obj/item/organ/external/chest) || \ + istype(part,/obj/item/organ/external/groin) || \ + istype(part,/obj/item/organ/external/head)) + part.createwound(BRUISE, 80) //mangle them instead + explosion(get_turf(imp_in), -1, -1, 1, 3) + qdel(src) + else + explosion(get_turf(imp_in), -1, -1, 1, 3) + part.droplimb(0,DROPLIMB_BLUNT) + qdel(src) + if (elevel == "Destroy Body") + explosion(get_turf(T), -1, 0, 1, 6) + T.gib() + if (elevel == "Full Explosion") + explosion(get_turf(T), 0, 1, 3, 6) + T.gib() + + else + explosion(get_turf(imp_in), 0, 1, 3, 6) + + var/turf/t = get_turf(imp_in) + + if(t) + t.hotspot_expose(3500,125) + +/obj/item/weapon/implant/explosive/post_implant(mob/source as mob) + elevel = tgui_alert(usr, "What sort of explosion would you prefer?", "Implant Intent", list("Localized Limb", "Destroy Body", "Full Explosion")) + phrase = tgui_input_text(usr, "Choose activation phrase:") + var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") + phrase = replace_characters(phrase, replacechars) + usr.mind.store_memory("Explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.", 0, 0) + to_chat(usr, "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.") + +/obj/item/weapon/implant/explosive/emp_act(severity) + if (malfunction) + return + malfunction = MALFUNCTION_TEMPORARY + switch (severity) + if (4) //Weak EMP will make implant tear limbs off. + if (prob(25)) + small_boom() + if (3) //Weak EMP will make implant tear limbs off. + if (prob(50)) + small_boom() + if (2) //strong EMP will melt implant either making it go off, or disarming it + if (prob(70)) + if (prob(75)) + small_boom() + else + if (prob(13)) + activate() //chance of bye bye + else + meltdown() //chance of implant disarming + if (1) //strong EMP will melt implant either making it go off, or disarming it + if (prob(70)) + if (prob(50)) + small_boom() + else + if (prob(50)) + activate() //50% chance of bye bye + else + meltdown() //50% chance of implant disarming + spawn (20) + malfunction-- + +/obj/item/weapon/implant/explosive/islegal() + return 0 + +/obj/item/weapon/implant/explosive/proc/small_boom() + if (ishuman(imp_in) && part) + imp_in.visible_message("Something beeps inside [imp_in][part ? "'s [part.name]" : ""]!") + playsound(src, 'sound/items/countdown.ogg', 75, 1, -3) + spawn(25) + if (ishuman(imp_in) && part) + //No tearing off these parts since it's pretty much killing + //and you can't replace groins + if (istype(part,/obj/item/organ/external/chest) || \ + istype(part,/obj/item/organ/external/groin) || \ + istype(part,/obj/item/organ/external/head)) + part.createwound(BRUISE, 80) //mangle them instead + else + part.droplimb(0,DROPLIMB_BLUNT) + explosion(get_turf(imp_in), -1, -1, 1, 3) + qdel(src) + +////////////////////////////// +// Chemical Implant +////////////////////////////// +GLOBAL_LIST_BOILERPLATE(all_chem_implants, /obj/item/weapon/implant/chem) + +/obj/item/weapon/implant/chem + name = "chemical implant" + desc = "Injects things." + allow_reagents = 1 + known_implant = TRUE + +/obj/item/weapon/implant/chem/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Robust Corp MJ-420 Prisoner Management Implant
                    +Life: Deactivates upon death but remains within the body.
                    +Important Notes: Due to the system functioning off of nutrients in the implanted subject's body, the subject
                    +will suffer from an increased appetite.

                    +
                    +Implant Details:
                    +Function: Contains a small capsule that can contain various chemicals. Upon receiving a specially encoded signal
                    +the implant releases the chemicals directly into the blood stream.
                    +Special Features: +Micro-Capsule- Can be loaded with any sort of chemical agent via the common syringe and can hold 50 units.
                    +Can only be loaded while still in its original case.
                    +Integrity: Implant will last so long as the subject is alive. However, if the subject suffers from malnutrition,
                    +the implant may become unstable and either pre-maturely inject the subject or simply break."} + return dat + +/obj/item/weapon/implant/chem/New() + ..() + var/datum/reagents/R = new/datum/reagents(50) + reagents = R + R.my_atom = src + +/obj/item/weapon/implant/chem/trigger(emote, source as mob) + if(emote == "deathgasp") + src.activate(src.reagents.total_volume) + return + +/obj/item/weapon/implant/chem/activate(var/cause) + if((!cause) || (!src.imp_in)) return 0 + var/mob/living/carbon/R = src.imp_in + src.reagents.trans_to_mob(R, cause, CHEM_BLOOD) + to_chat(R, "You hear a faint *beep*.") + if(!src.reagents.total_volume) + to_chat(R, "You hear a faint click from your chest.") + playsound(R, 'sound/weapons/empty.ogg', 10, 1) + spawn(0) + qdel(src) + return + +/obj/item/weapon/implant/chem/emp_act(severity) + if (malfunction) + return + malfunction = MALFUNCTION_TEMPORARY + + switch(severity) + if(1) + if(prob(60)) + activate(20) + if(2) + if(prob(40)) + activate(20) + if(3) + if(prob(40)) + activate(5) + if(4) + if(prob(20)) + activate(5) + + spawn(20) + malfunction-- + +////////////////////////////// +// Loyalty Implant +////////////////////////////// +/obj/item/weapon/implant/loyalty + name = "loyalty implant" + desc = "Makes you loyal or such." + known_implant = TRUE + +/obj/item/weapon/implant/loyalty/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] Employee Management Implant
                    +Life: Ten years.
                    +Important Notes: Personnel injected with this device tend to be much more loyal to the company.
                    +
                    +Implant Details:
                    +Function: Contains a small pod of nanobots that manipulate the host's mental functions.
                    +Special Features: Will prevent and cure most forms of brainwashing.
                    +Integrity: Implant will last so long as the nanobots are inside the bloodstream."} + return dat + +/obj/item/weapon/implant/loyalty/handle_implant(mob/M, target_zone = BP_TORSO) + . = ..(M, target_zone) + if(!istype(M, /mob/living/carbon/human)) + . = FALSE + var/mob/living/carbon/human/H = M + var/datum/antagonist/antag_data = get_antag_data(H.mind.special_role) + if(antag_data && (antag_data.flags & ANTAG_IMPLANT_IMMUNE)) + H.visible_message("[H] seems to resist the implant!", "You feel the corporate tendrils of [using_map.company_name] try to invade your mind!") + . = FALSE + +/obj/item/weapon/implant/loyalty/post_implant(mob/M) + var/mob/living/carbon/human/H = M + clear_antag_roles(H.mind, 1) + to_chat(H, "You feel a surge of loyalty towards [using_map.company_name].") + +////////////////////////////// +// Adrenaline Implant +////////////////////////////// +/obj/item/weapon/implant/adrenalin + name = "adrenalin" + desc = "Removes all stuns and knockdowns." + var/uses + +/obj/item/weapon/implant/adrenalin/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Cybersun Industries Adrenalin Implant
                    +Life: Five days.
                    +Important Notes: Illegal
                    +
                    +Implant Details: Subjects injected with implant can activate a massive injection of adrenalin.
                    +Function: Contains nanobots to stimulate body to mass-produce Adrenalin.
                    +Special Features: Will prevent and cure most forms of brainwashing.
                    +Integrity: Implant can only be used three times before the nanobots are depleted."} + return dat + + +/obj/item/weapon/implant/adrenalin/trigger(emote, mob/source as mob) + if (src.uses < 1) return 0 + if (emote == "pale") + src.uses-- + to_chat(source, "You feel a sudden surge of energy!") + source.SetStunned(0) + source.SetWeakened(0) + source.SetParalysis(0) + + return + +/obj/item/weapon/implant/adrenalin/post_implant(mob/source) + source.mind.store_memory("A implant can be activated by using the pale emote, say *pale to attempt to activate.", 0, 0) + to_chat(source, "The implanted freedom implant can be activated by using the pale emote, say *pale to attempt to activate.") + +////////////////////////////// +// Death Alarm Implant +////////////////////////////// +/obj/item/weapon/implant/death_alarm + name = "death alarm implant" + desc = "An alarm which monitors host vital signs and transmits a radio message upon death." + origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 2, TECH_DATA = 1) + known_implant = TRUE + var/mobname = "Will Robinson" + +/obj/item/weapon/implant/death_alarm/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    +Life: Activates upon death.
                    +Important Notes: Alerts crew to crewmember death.
                    +
                    +Implant Details:
                    +Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    +Special Features: Alerts crew to crewmember death.
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + +/obj/item/weapon/implant/death_alarm/process() + if (!implanted) return + var/mob/M = imp_in + + if(isnull(M)) // If the mob got gibbed + activate() + else if(M.stat == 2) + activate("death") + +/obj/item/weapon/implant/death_alarm/activate(var/cause) + var/mob/M = imp_in + var/area/t = get_area(M) + switch (cause) + if("death") + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + if(istype(t, /area/syndicate_station) || istype(t, /area/syndicate_mothership) || istype(t, /area/shuttle/syndicate_elite) ) + //give the syndies a bit of stealth + a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in Space!", "[mobname]'s Death Alarm", "Medical") + else + a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm", "Medical") + qdel(a) + STOP_PROCESSING(SSobj, src) + if ("emp") + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + var/name = prob(50) ? t.name : pick(teleportlocs) + a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm", "Medical") + qdel(a) + else + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm") +// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Security") +// a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm", "Medical") + qdel(a) + STOP_PROCESSING(SSobj, src) + +/obj/item/weapon/implant/death_alarm/emp_act(severity) //for some reason alarms stop going off in case they are emp'd, even without this + if (malfunction) //so I'm just going to add a meltdown chance here + return + malfunction = MALFUNCTION_TEMPORARY + + activate("emp") //let's shout that this dude is dead + if(severity == 1) + if(prob(40)) //small chance of obvious meltdown + meltdown() + else if (prob(60)) //but more likely it will just quietly die + malfunction = MALFUNCTION_PERMANENT + STOP_PROCESSING(SSobj, src) + + spawn(20) + malfunction-- + +/obj/item/weapon/implant/death_alarm/post_implant(mob/source as mob) + mobname = source.real_name + START_PROCESSING(SSobj, src) + +////////////////////////////// +// Compressed Matter Implant +////////////////////////////// +/obj/item/weapon/implant/compressed + name = "compressed matter implant" + desc = "Based on compressed matter technology, can store a single item." + icon_state = "implant_evil" + var/activation_emote = "sigh" + var/obj/item/scanned = null + origin_tech = list(TECH_MATERIAL = 4, TECH_BIO = 2, TECH_ILLEGAL = 2) + +/obj/item/weapon/implant/compressed/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] \"Profit Margin\" Class Employee Lifesign Sensor
                    +Life: Activates upon death.
                    +Important Notes: Alerts crew to crewmember death.
                    +
                    +Implant Details:
                    +Function: Contains a compact radio signaler that triggers when the host's lifesigns cease.
                    +Special Features: Alerts crew to crewmember death.
                    +Integrity: Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."} + return dat + +/obj/item/weapon/implant/compressed/trigger(emote, mob/source as mob) + if (src.scanned == null) + return 0 + + if (emote == src.activation_emote) + to_chat(source, "The air glows as \the [src.scanned.name] uncompresses.") + activate() + +/obj/item/weapon/implant/compressed/activate() + var/turf/t = get_turf(src) + if (imp_in) + imp_in.put_in_hands(scanned) + else + scanned.loc = t + qdel(src) + +/obj/item/weapon/implant/compressed/post_implant(mob/source) + var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) + if(!activation_emote) + activation_emote = pick(choices) + if (source.mind) + source.mind.store_memory("Compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) + to_chat(source, "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") + + +/obj/item/weapon/implant/compressed/islegal() + return 0 diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index 5e81318ac6b..ee6925c55c4 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -1,311 +1,311 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/implantcase - name = "glass case" - desc = "A case containing an implant." - icon = 'icons/obj/items.dmi' - icon_state = "implantcase-0" - item_state = "implantcase" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_TINY - var/obj/item/weapon/implant/imp = null - -/obj/item/weapon/implantcase/proc/update() - if (src.imp) - src.icon_state = text("implantcase-[]", src.imp.implant_color) - else - src.icon_state = "implantcase-0" - return - -/obj/item/weapon/implantcase/attackby(obj/item/weapon/I as obj, mob/user as mob) - ..() - if (istype(I, /obj/item/weapon/pen)) - var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null, MAX_NAME_LEN) - if (user.get_active_hand() != I) - return - if((!in_range(src, usr) && src.loc != user)) - return - t = sanitizeSafe(t, MAX_NAME_LEN) - if(t) - src.name = text("Glass Case - '[]'", t) - else - src.name = "Glass Case" - else if(istype(I, /obj/item/weapon/reagent_containers/syringe)) - if(!src.imp) return - if(!src.imp.allow_reagents) return - if(src.imp.reagents.total_volume >= src.imp.reagents.maximum_volume) - to_chat(user, "\The [src] is full.") - else - spawn(5) - I.reagents.trans_to_obj(src.imp, 5) - to_chat(user, "You inject 5 units of the solution. The syringe now contains [I.reagents.total_volume] units.") - else if (istype(I, /obj/item/weapon/implanter)) - var/obj/item/weapon/implanter/M = I - if (M.imp) - if ((src.imp || M.imp.implanted)) - return - M.imp.loc = src - src.imp = M.imp - M.imp = null - src.update() - M.update() - else - if (src.imp) - if (M.imp) - return - src.imp.loc = M - M.imp = src.imp - src.imp = null - update() - M.update() - return - - -/obj/item/weapon/implantcase/tracking - name = "glass case - 'tracking'" - desc = "A case containing a tracking implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/tracking/New() - src.imp = new /obj/item/weapon/implant/tracking( src ) - ..() - return - - -/obj/item/weapon/implantcase/explosive - name = "glass case - 'explosive'" - desc = "A case containing an explosive implant." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/explosive/New() - src.imp = new /obj/item/weapon/implant/explosive( src ) - ..() - return - - -/obj/item/weapon/implantcase/chem - name = "glass case - 'chem'" - desc = "A case containing a chemical implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/chem/New() - src.imp = new /obj/item/weapon/implant/chem( src ) - ..() - return - - -/obj/item/weapon/implantcase/loyalty - name = "glass case - 'loyalty'" - desc = "A case containing a loyalty implant." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/loyalty/New() - src.imp = new /obj/item/weapon/implant/loyalty( src ) - ..() - return - - -/obj/item/weapon/implantcase/death_alarm - name = "glass case - 'death alarm'" - desc = "A case containing a death alarm implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/death_alarm/New() - src.imp = new /obj/item/weapon/implant/death_alarm( src ) - ..() - return - - -/obj/item/weapon/implantcase/freedom - name = "glass case - 'freedom'" - desc = "A case containing a freedom implant." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/freedom/New() - src.imp = new /obj/item/weapon/implant/freedom( src ) - ..() - return - - -/obj/item/weapon/implantcase/adrenalin - name = "glass case - 'adrenalin'" - desc = "A case containing an adrenalin implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/adrenalin/New() - src.imp = new /obj/item/weapon/implant/adrenalin( src ) - ..() - return - - -/obj/item/weapon/implantcase/dexplosive - name = "glass case - 'explosive'" - desc = "A case containing an explosive." - icon_state = "implantcase-r" - -/obj/item/weapon/implantcase/dexplosive/New() - src.imp = new /obj/item/weapon/implant/dexplosive( src ) - ..() - return - - -/obj/item/weapon/implantcase/health - name = "glass case - 'health'" - desc = "A case containing a health tracking implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/health/New() - src.imp = new /obj/item/weapon/implant/health( src ) - ..() - return - -/obj/item/weapon/implantcase/language - name = "glass case - 'GalCom'" - desc = "A case containing a GalCom language implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/language/New() - src.imp = new /obj/item/weapon/implant/language( src ) - ..() - return - -/obj/item/weapon/implantcase/language/eal - name = "glass case - 'EAL'" - desc = "A case containing an Encoded Audio Language implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/language/eal/New() - src.imp = new /obj/item/weapon/implant/language/eal( src ) - ..() - return - -/obj/item/weapon/implantcase/shades - name = "glass case - 'Integrated Shades'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/shades/New() - src.imp = new /obj/item/weapon/implant/organ( src ) - ..() - return - -/obj/item/weapon/implantcase/taser - name = "glass case - 'Taser'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/taser/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment( src ) - ..() - return - -/obj/item/weapon/implantcase/laser - name = "glass case - 'Laser'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/laser/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/laser( src ) - ..() - return - -/obj/item/weapon/implantcase/dart - name = "glass case - 'Dart'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/dart/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/dart( src ) - ..() - return - -/obj/item/weapon/implantcase/toolkit - name = "glass case - 'Toolkit'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/toolkit/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm( src ) - ..() - return - -/obj/item/weapon/implantcase/medkit - name = "glass case - 'Toolkit'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/medkit/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/medkit( src ) - ..() - return - -/obj/item/weapon/implantcase/surge - name = "glass case - 'Muscle Overclocker'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/surge/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/surge( src ) - ..() - return - -/obj/item/weapon/implantcase/analyzer - name = "glass case - 'Scanner'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/analyzer/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist( src ) - ..() - return - -/obj/item/weapon/implantcase/sword - name = "glass case - 'Scanner'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/sword/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/sword( src ) - ..() - return - -/obj/item/weapon/implantcase/sprinter - name = "glass case - 'Sprinter'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/sprinter/New() - src.imp = new /obj/item/weapon/implant/organ/pelvic( src ) - ..() - return - -/obj/item/weapon/implantcase/armblade - name = "glass case - 'Armblade'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/armblade/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/blade( src ) - ..() - return - -/obj/item/weapon/implantcase/handblade - name = "glass case - 'Handblade'" - desc = "A case containing a nanite fabricator implant." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/handblade/New() - src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/blade( src ) - ..() - return - -/obj/item/weapon/implantcase/restrainingbolt - name = "glass case - 'Restraining Bolt'" - desc = "A case containing a restraining bolt." - icon_state = "implantcase-b" - -/obj/item/weapon/implantcase/restrainingbolt/New() - src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) - ..() - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/implantcase + name = "glass case" + desc = "A case containing an implant." + icon = 'icons/obj/items.dmi' + icon_state = "implantcase-0" + item_state = "implantcase" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_TINY + var/obj/item/weapon/implant/imp = null + +/obj/item/weapon/implantcase/proc/update() + if (src.imp) + src.icon_state = text("implantcase-[]", src.imp.implant_color) + else + src.icon_state = "implantcase-0" + return + +/obj/item/weapon/implantcase/attackby(obj/item/weapon/I as obj, mob/user as mob) + ..() + if (istype(I, /obj/item/weapon/pen)) + var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null, MAX_NAME_LEN) + if (user.get_active_hand() != I) + return + if((!in_range(src, usr) && src.loc != user)) + return + t = sanitizeSafe(t, MAX_NAME_LEN) + if(t) + src.name = text("Glass Case - '[]'", t) + else + src.name = "Glass Case" + else if(istype(I, /obj/item/weapon/reagent_containers/syringe)) + if(!src.imp) return + if(!src.imp.allow_reagents) return + if(src.imp.reagents.total_volume >= src.imp.reagents.maximum_volume) + to_chat(user, "\The [src] is full.") + else + spawn(5) + I.reagents.trans_to_obj(src.imp, 5) + to_chat(user, "You inject 5 units of the solution. The syringe now contains [I.reagents.total_volume] units.") + else if (istype(I, /obj/item/weapon/implanter)) + var/obj/item/weapon/implanter/M = I + if (M.imp) + if ((src.imp || M.imp.implanted)) + return + M.imp.loc = src + src.imp = M.imp + M.imp = null + src.update() + M.update() + else + if (src.imp) + if (M.imp) + return + src.imp.loc = M + M.imp = src.imp + src.imp = null + update() + M.update() + return + + +/obj/item/weapon/implantcase/tracking + name = "glass case - 'tracking'" + desc = "A case containing a tracking implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/tracking/New() + src.imp = new /obj/item/weapon/implant/tracking( src ) + ..() + return + + +/obj/item/weapon/implantcase/explosive + name = "glass case - 'explosive'" + desc = "A case containing an explosive implant." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/explosive/New() + src.imp = new /obj/item/weapon/implant/explosive( src ) + ..() + return + + +/obj/item/weapon/implantcase/chem + name = "glass case - 'chem'" + desc = "A case containing a chemical implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/chem/New() + src.imp = new /obj/item/weapon/implant/chem( src ) + ..() + return + + +/obj/item/weapon/implantcase/loyalty + name = "glass case - 'loyalty'" + desc = "A case containing a loyalty implant." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/loyalty/New() + src.imp = new /obj/item/weapon/implant/loyalty( src ) + ..() + return + + +/obj/item/weapon/implantcase/death_alarm + name = "glass case - 'death alarm'" + desc = "A case containing a death alarm implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/death_alarm/New() + src.imp = new /obj/item/weapon/implant/death_alarm( src ) + ..() + return + + +/obj/item/weapon/implantcase/freedom + name = "glass case - 'freedom'" + desc = "A case containing a freedom implant." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/freedom/New() + src.imp = new /obj/item/weapon/implant/freedom( src ) + ..() + return + + +/obj/item/weapon/implantcase/adrenalin + name = "glass case - 'adrenalin'" + desc = "A case containing an adrenalin implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/adrenalin/New() + src.imp = new /obj/item/weapon/implant/adrenalin( src ) + ..() + return + + +/obj/item/weapon/implantcase/dexplosive + name = "glass case - 'explosive'" + desc = "A case containing an explosive." + icon_state = "implantcase-r" + +/obj/item/weapon/implantcase/dexplosive/New() + src.imp = new /obj/item/weapon/implant/dexplosive( src ) + ..() + return + + +/obj/item/weapon/implantcase/health + name = "glass case - 'health'" + desc = "A case containing a health tracking implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/health/New() + src.imp = new /obj/item/weapon/implant/health( src ) + ..() + return + +/obj/item/weapon/implantcase/language + name = "glass case - 'GalCom'" + desc = "A case containing a GalCom language implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/language/New() + src.imp = new /obj/item/weapon/implant/language( src ) + ..() + return + +/obj/item/weapon/implantcase/language/eal + name = "glass case - 'EAL'" + desc = "A case containing an Encoded Audio Language implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/language/eal/New() + src.imp = new /obj/item/weapon/implant/language/eal( src ) + ..() + return + +/obj/item/weapon/implantcase/shades + name = "glass case - 'Integrated Shades'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/shades/New() + src.imp = new /obj/item/weapon/implant/organ( src ) + ..() + return + +/obj/item/weapon/implantcase/taser + name = "glass case - 'Taser'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/taser/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment( src ) + ..() + return + +/obj/item/weapon/implantcase/laser + name = "glass case - 'Laser'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/laser/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/laser( src ) + ..() + return + +/obj/item/weapon/implantcase/dart + name = "glass case - 'Dart'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/dart/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/dart( src ) + ..() + return + +/obj/item/weapon/implantcase/toolkit + name = "glass case - 'Toolkit'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/toolkit/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm( src ) + ..() + return + +/obj/item/weapon/implantcase/medkit + name = "glass case - 'Toolkit'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/medkit/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/medkit( src ) + ..() + return + +/obj/item/weapon/implantcase/surge + name = "glass case - 'Muscle Overclocker'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/surge/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/surge( src ) + ..() + return + +/obj/item/weapon/implantcase/analyzer + name = "glass case - 'Scanner'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/analyzer/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist( src ) + ..() + return + +/obj/item/weapon/implantcase/sword + name = "glass case - 'Scanner'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/sword/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/sword( src ) + ..() + return + +/obj/item/weapon/implantcase/sprinter + name = "glass case - 'Sprinter'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/sprinter/New() + src.imp = new /obj/item/weapon/implant/organ/pelvic( src ) + ..() + return + +/obj/item/weapon/implantcase/armblade + name = "glass case - 'Armblade'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/armblade/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/blade( src ) + ..() + return + +/obj/item/weapon/implantcase/handblade + name = "glass case - 'Handblade'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/handblade/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/blade( src ) + ..() + return + +/obj/item/weapon/implantcase/restrainingbolt + name = "glass case - 'Restraining Bolt'" + desc = "A case containing a restraining bolt." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/restrainingbolt/New() + src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) + ..() + return diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index 34110d59249..134ea532bdb 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -1,162 +1,162 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/machinery/implantchair - name = "loyalty implanter" - desc = "Used to implant occupants with loyalty implants." - icon = 'icons/obj/machines/implantchair.dmi' - icon_state = "implantchair" - density = TRUE - opacity = 0 - anchored = TRUE - - var/ready = 1 - var/malfunction = 0 - var/list/obj/item/weapon/implant/loyalty/implant_list = list() - var/max_implants = 5 - var/injection_cooldown = 600 - var/replenish_cooldown = 6000 - var/replenishing = 0 - var/mob/living/carbon/occupant = null - var/injecting = 0 - -/obj/machinery/implantchair/New() - ..() - add_implants() - - -/obj/machinery/implantchair/attack_hand(mob/user as mob) - user.set_machine(src) - var/health_text = "" - if(src.occupant) - if(src.occupant.health <= -100) - health_text = "Dead" - else if(src.occupant.health < 0) - health_text = "[round(src.occupant.health,0.1)]" - else - health_text = "[round(src.occupant.health,0.1)]" - - var/dat ="Implanter Status
                    " - - dat +="Current occupant: [src.occupant ? "
                    Name: [src.occupant]
                    Health: [health_text]
                    " : "None"]
                    " - dat += "Implants: [src.implant_list.len ? "[implant_list.len]" : "Replenish"]
                    " - if(src.occupant) - dat += "[src.ready ? "Implant" : "Recharging"]
                    " - user.set_machine(src) - user << browse(dat, "window=implant") - onclose(user, "implant") - - -/obj/machinery/implantchair/Topic(href, href_list) - if((get_dist(src, usr) <= 1) || istype(usr, /mob/living/silicon/ai)) - if(href_list["implant"]) - if(src.occupant) - injecting = 1 - go_out() - ready = 0 - spawn(injection_cooldown) - ready = 1 - - if(href_list["replenish"]) - ready = 0 - spawn(replenish_cooldown) - add_implants() - ready = 1 - - src.updateUsrDialog() - src.add_fingerprint(usr) - return - - -/obj/machinery/implantchair/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) - if(istype(G, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/grab = G - if(!ismob(grab.affecting)) - return - if(grab.affecting.has_buckled_mobs()) - to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to them. Remove them first.")) - return - var/mob/M = grab.affecting - if(put_mob(M)) - qdel(G) - src.updateUsrDialog() - return - - -/obj/machinery/implantchair/proc/go_out(var/mob/M) - if(!( src.occupant )) - return - if(M == occupant) // so that the guy inside can't eject himself -Agouri - return - if (src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - src.occupant.loc = src.loc - if(injecting) - implant(src.occupant) - injecting = 0 - src.occupant = null - icon_state = "implantchair" - return - - -/obj/machinery/implantchair/proc/put_mob(mob/living/carbon/M as mob) - if(!iscarbon(M)) - to_chat(usr, "\The [src] cannot hold this!") - return - if(src.occupant) - to_chat(usr, "\The [src] is already occupied!") - return - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - M.stop_pulling() - M.loc = src - src.occupant = M - src.add_fingerprint(usr) - icon_state = "implantchair_on" - return 1 - - -/obj/machinery/implantchair/proc/implant(var/mob/M) - if (!istype(M, /mob/living/carbon)) - return - if(!implant_list.len) return - for(var/obj/item/weapon/implant/loyalty/imp in implant_list) - if(!imp) continue - if(istype(imp, /obj/item/weapon/implant/loyalty)) - for (var/mob/O in viewers(M, null)) - O.show_message("\The [M] has been implanted by \the [src].", 1) - - if(imp.handle_implant(M, BP_TORSO)) - imp.post_implant(M) - - implant_list -= imp - break - return - - -/obj/machinery/implantchair/proc/add_implants() - for(var/i=0, iName: [src.occupant]
                    Health: [health_text]
                    " : "None"]
                    " + dat += "Implants: [src.implant_list.len ? "[implant_list.len]" : "Replenish"]
                    " + if(src.occupant) + dat += "[src.ready ? "Implant" : "Recharging"]
                    " + user.set_machine(src) + user << browse(dat, "window=implant") + onclose(user, "implant") + + +/obj/machinery/implantchair/Topic(href, href_list) + if((get_dist(src, usr) <= 1) || istype(usr, /mob/living/silicon/ai)) + if(href_list["implant"]) + if(src.occupant) + injecting = 1 + go_out() + ready = 0 + spawn(injection_cooldown) + ready = 1 + + if(href_list["replenish"]) + ready = 0 + spawn(replenish_cooldown) + add_implants() + ready = 1 + + src.updateUsrDialog() + src.add_fingerprint(usr) + return + + +/obj/machinery/implantchair/attackby(var/obj/item/weapon/G as obj, var/mob/user as mob) + if(istype(G, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/grab = G + if(!ismob(grab.affecting)) + return + if(grab.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to them. Remove them first.")) + return + var/mob/M = grab.affecting + if(put_mob(M)) + qdel(G) + src.updateUsrDialog() + return + + +/obj/machinery/implantchair/proc/go_out(var/mob/M) + if(!( src.occupant )) + return + if(M == occupant) // so that the guy inside can't eject himself -Agouri + return + if (src.occupant.client) + src.occupant.client.eye = src.occupant.client.mob + src.occupant.client.perspective = MOB_PERSPECTIVE + src.occupant.loc = src.loc + if(injecting) + implant(src.occupant) + injecting = 0 + src.occupant = null + icon_state = "implantchair" + return + + +/obj/machinery/implantchair/proc/put_mob(mob/living/carbon/M as mob) + if(!iscarbon(M)) + to_chat(usr, "\The [src] cannot hold this!") + return + if(src.occupant) + to_chat(usr, "\The [src] is already occupied!") + return + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.stop_pulling() + M.loc = src + src.occupant = M + src.add_fingerprint(usr) + icon_state = "implantchair_on" + return 1 + + +/obj/machinery/implantchair/proc/implant(var/mob/M) + if (!istype(M, /mob/living/carbon)) + return + if(!implant_list.len) return + for(var/obj/item/weapon/implant/loyalty/imp in implant_list) + if(!imp) continue + if(istype(imp, /obj/item/weapon/implant/loyalty)) + for (var/mob/O in viewers(M, null)) + O.show_message("\The [M] has been implanted by \the [src].", 1) + + if(imp.handle_implant(M, BP_TORSO)) + imp.post_implant(M) + + implant_list -= imp + break + return + + +/obj/machinery/implantchair/proc/add_implants() + for(var/i=0, iYou [active ? "" : "de"]activate \the [src].
                    ") - update() - -/obj/item/weapon/implanter/verb/remove_implant() - set category = "Object" - set name = "Remove Implant" - set src in usr - - if(!imp) - return - if(istype(usr, /mob)) - var/mob/M = usr - imp.loc = get_turf(src) - if(M.get_active_hand() == null) - M.put_in_hands(imp) - to_chat(M, "You remove \the [imp] from \the [src].") - name = "implanter" - imp = null - - update() - - return - -/obj/item/weapon/implanter/proc/update() - if (src.imp) - src.icon_state = "implanter1" - else - src.icon_state = "implanter0" - src.icon_state += "_[active]" - return - -/obj/item/weapon/implanter/attack(mob/M as mob, mob/user as mob) - if (!istype(M, /mob/living/carbon)) - return - if(active) - if (imp) - M.visible_message("[user] is attempting to implant [M].") - - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) - user.do_attack_animation(M) - - var/turf/T1 = get_turf(M) - if (T1 && ((M == user) || do_after(user, 50))) - if(user && M && (get_turf(M) == T1) && src && src.imp) - M.visible_message("[M] has been implanted by [user].") - - add_attack_logs(user,M,"Implanted with [imp.name] using [name]") - - if(imp.handle_implant(M)) - imp.post_implant(M) - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - BITSET(H.hud_updateflag, IMPLOYAL_HUD) - BITSET(H.hud_updateflag, BACKUP_HUD) //VOREStation Add - Backup HUD updates - - src.imp = null - update() - else - to_chat(user, "You need to activate \the [src.name] first.") - return - -/obj/item/weapon/implanter/loyalty - name = "implanter-loyalty" - -/obj/item/weapon/implanter/loyalty/New() - src.imp = new /obj/item/weapon/implant/loyalty( src ) - ..() - update() - return - -/obj/item/weapon/implanter/explosive - name = "implanter (E)" - -/obj/item/weapon/implanter/explosive/New() - src.imp = new /obj/item/weapon/implant/explosive( src ) - ..() - update() - return - -/obj/item/weapon/implanter/adrenalin - name = "implanter-adrenalin" - -/obj/item/weapon/implanter/adrenalin/New() - src.imp = new /obj/item/weapon/implant/adrenalin(src) - ..() - update() - return - -/obj/item/weapon/implanter/compressed - name = "implanter (C)" - icon_state = "cimplanter1" - -/obj/item/weapon/implanter/compressed/New() - imp = new /obj/item/weapon/implant/compressed( src ) - ..() - update() - return - -/obj/item/weapon/implanter/compressed/update() - if (imp) - var/obj/item/weapon/implant/compressed/c = imp - if(!c.scanned) - icon_state = "cimplanter1" - else - icon_state = "cimplanter2" - else - icon_state = "cimplanter0" - return - -/obj/item/weapon/implanter/compressed/attack(mob/M as mob, mob/user as mob) - var/obj/item/weapon/implant/compressed/c = imp - if (!c) return - if (c.scanned == null) - to_chat(user, "Please scan an object with the implanter first.") - return - ..() - -/obj/item/weapon/implanter/compressed/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) - return - if(!active) - to_chat(user, "Activate \the [src.name] first.") - return - if(istype(A,/obj/item) && imp) - var/obj/item/weapon/implant/compressed/c = imp - if (c.scanned) - to_chat(user, "Something is already scanned inside the implant!") - return - c.scanned = A - if(istype(A, /obj/item/weapon/storage)) - to_chat(user, "You can't store \the [A.name] in this!") - c.scanned = null - return - if(istype(A.loc,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = A.loc - H.remove_from_mob(A) - else if(istype(A.loc,/obj/item/weapon/storage)) - var/obj/item/weapon/storage/S = A.loc - S.remove_from_storage(A) - A.loc.contents.Remove(A) - update() - -/obj/item/weapon/implanter/restrainingbolt - name = "implanter (bolt)" - -/obj/item/weapon/implanter/restrainingbolt/New() - src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) - ..() - update() - return +/obj/item/weapon/implanter + name = "implanter" + icon = 'icons/obj/items.dmi' + icon_state = "implanter0_1" + item_state = "syringe_0" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + matter = list(MAT_STEEL = 1000, MAT_GLASS = 1000) + var/obj/item/weapon/implant/imp = null + var/active = 1 + +/obj/item/weapon/implanter/attack_self(var/mob/user) + active = !active + to_chat(user, "You [active ? "" : "de"]activate \the [src].") + update() + +/obj/item/weapon/implanter/verb/remove_implant() + set category = "Object" + set name = "Remove Implant" + set src in usr + + if(!imp) + return + if(istype(usr, /mob)) + var/mob/M = usr + imp.loc = get_turf(src) + if(M.get_active_hand() == null) + M.put_in_hands(imp) + to_chat(M, "You remove \the [imp] from \the [src].") + name = "implanter" + imp = null + + update() + + return + +/obj/item/weapon/implanter/proc/update() + if (src.imp) + src.icon_state = "implanter1" + else + src.icon_state = "implanter0" + src.icon_state += "_[active]" + return + +/obj/item/weapon/implanter/attack(mob/M as mob, mob/user as mob) + if (!istype(M, /mob/living/carbon)) + return + if(active) + if (imp) + M.visible_message("[user] is attempting to implant [M].") + + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(M) + + var/turf/T1 = get_turf(M) + if (T1 && ((M == user) || do_after(user, 50))) + if(user && M && (get_turf(M) == T1) && src && src.imp) + M.visible_message("[M] has been implanted by [user].") + + add_attack_logs(user,M,"Implanted with [imp.name] using [name]") + + if(imp.handle_implant(M)) + imp.post_implant(M) + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + BITSET(H.hud_updateflag, IMPLOYAL_HUD) + BITSET(H.hud_updateflag, BACKUP_HUD) //VOREStation Add - Backup HUD updates + + src.imp = null + update() + else + to_chat(user, "You need to activate \the [src.name] first.") + return + +/obj/item/weapon/implanter/loyalty + name = "implanter-loyalty" + +/obj/item/weapon/implanter/loyalty/New() + src.imp = new /obj/item/weapon/implant/loyalty( src ) + ..() + update() + return + +/obj/item/weapon/implanter/explosive + name = "implanter (E)" + +/obj/item/weapon/implanter/explosive/New() + src.imp = new /obj/item/weapon/implant/explosive( src ) + ..() + update() + return + +/obj/item/weapon/implanter/adrenalin + name = "implanter-adrenalin" + +/obj/item/weapon/implanter/adrenalin/New() + src.imp = new /obj/item/weapon/implant/adrenalin(src) + ..() + update() + return + +/obj/item/weapon/implanter/compressed + name = "implanter (C)" + icon_state = "cimplanter1" + +/obj/item/weapon/implanter/compressed/New() + imp = new /obj/item/weapon/implant/compressed( src ) + ..() + update() + return + +/obj/item/weapon/implanter/compressed/update() + if (imp) + var/obj/item/weapon/implant/compressed/c = imp + if(!c.scanned) + icon_state = "cimplanter1" + else + icon_state = "cimplanter2" + else + icon_state = "cimplanter0" + return + +/obj/item/weapon/implanter/compressed/attack(mob/M as mob, mob/user as mob) + var/obj/item/weapon/implant/compressed/c = imp + if (!c) return + if (c.scanned == null) + to_chat(user, "Please scan an object with the implanter first.") + return + ..() + +/obj/item/weapon/implanter/compressed/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) + return + if(!active) + to_chat(user, "Activate \the [src.name] first.") + return + if(istype(A,/obj/item) && imp) + var/obj/item/weapon/implant/compressed/c = imp + if (c.scanned) + to_chat(user, "Something is already scanned inside the implant!") + return + c.scanned = A + if(istype(A, /obj/item/weapon/storage)) + to_chat(user, "You can't store \the [A.name] in this!") + c.scanned = null + return + if(istype(A.loc,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = A.loc + H.remove_from_mob(A) + else if(istype(A.loc,/obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = A.loc + S.remove_from_storage(A) + A.loc.contents.Remove(A) + update() + +/obj/item/weapon/implanter/restrainingbolt + name = "implanter (bolt)" + +/obj/item/weapon/implanter/restrainingbolt/New() + src.imp = new /obj/item/weapon/implant/restrainingbolt( src ) + ..() + update() + return diff --git a/code/game/objects/items/weapons/implants/implantfreedom.dm b/code/game/objects/items/weapons/implants/implantfreedom.dm index 6e4f5f7a167..aa63215a61c 100644 --- a/code/game/objects/items/weapons/implants/implantfreedom.dm +++ b/code/game/objects/items/weapons/implants/implantfreedom.dm @@ -1,70 +1,70 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/implant/freedom - name = "freedom implant" - desc = "Use this to escape from those evil Red Shirts." - implant_color = "r" - var/activation_emote = "chuckle" - var/uses = 1.0 - - -/obj/item/weapon/implant/freedom/New() - src.activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - src.uses = rand(1, 5) - ..() - return - - -/obj/item/weapon/implant/freedom/trigger(emote, mob/living/carbon/source as mob) - if (src.uses < 1) - return 0 - - if (emote == src.activation_emote) - src.uses-- - to_chat(source, "You feel a faint click.") - if (source.handcuffed) - var/obj/item/weapon/W = source.handcuffed - source.handcuffed = null - if(source.buckled && source.buckled.buckle_require_restraints) - source.buckled.unbuckle_mob() - source.update_handcuffed() - if (source.client) - source.client.screen -= W - if (W) - W.loc = source.loc - dropped(source) - if (W) - W.layer = initial(W.layer) - if (source.legcuffed) - var/obj/item/weapon/W = source.legcuffed - source.legcuffed = null - source.update_inv_legcuffed() - if (source.client) - source.client.screen -= W - if (W) - W.loc = source.loc - dropped(source) - if (W) - W.layer = initial(W.layer) - return - -/obj/item/weapon/implant/freedom/post_implant(mob/source) - source.mind.store_memory("Freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - to_chat(source, "The implanted freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") - -/obj/item/weapon/implant/freedom/get_data() - var/dat = {" -Implant Specifications:
                    -Name: Freedom Beacon
                    -Life: optimum 5 uses
                    -Important Notes: Illegal
                    -
                    -Implant Details:
                    -Function: Transmits a specialized cluster of signals to override handcuff locking -mechanisms
                    -Special Features:
                    -Neuro-Scan- Analyzes certain shadow signals in the nervous system
                    -Integrity: The battery is extremely weak and commonly after injection its -life can drive down to only 1 use.
                    -No Implant Specifics"} - return dat +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/implant/freedom + name = "freedom implant" + desc = "Use this to escape from those evil Red Shirts." + implant_color = "r" + var/activation_emote = "chuckle" + var/uses = 1.0 + + +/obj/item/weapon/implant/freedom/New() + src.activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + src.uses = rand(1, 5) + ..() + return + + +/obj/item/weapon/implant/freedom/trigger(emote, mob/living/carbon/source as mob) + if (src.uses < 1) + return 0 + + if (emote == src.activation_emote) + src.uses-- + to_chat(source, "You feel a faint click.") + if (source.handcuffed) + var/obj/item/weapon/W = source.handcuffed + source.handcuffed = null + if(source.buckled && source.buckled.buckle_require_restraints) + source.buckled.unbuckle_mob() + source.update_handcuffed() + if (source.client) + source.client.screen -= W + if (W) + W.loc = source.loc + dropped(source) + if (W) + W.layer = initial(W.layer) + if (source.legcuffed) + var/obj/item/weapon/W = source.legcuffed + source.legcuffed = null + source.update_inv_legcuffed() + if (source.client) + source.client.screen -= W + if (W) + W.loc = source.loc + dropped(source) + if (W) + W.layer = initial(W.layer) + return + +/obj/item/weapon/implant/freedom/post_implant(mob/source) + source.mind.store_memory("Freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) + to_chat(source, "The implanted freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") + +/obj/item/weapon/implant/freedom/get_data() + var/dat = {" +Implant Specifications:
                    +Name: Freedom Beacon
                    +Life: optimum 5 uses
                    +Important Notes: Illegal
                    +
                    +Implant Details:
                    +Function: Transmits a specialized cluster of signals to override handcuff locking +mechanisms
                    +Special Features:
                    +Neuro-Scan- Analyzes certain shadow signals in the nervous system
                    +Integrity: The battery is extremely weak and commonly after injection its +life can drive down to only 1 use.
                    +No Implant Specifics"} + return dat diff --git a/code/game/objects/items/weapons/implants/implantpad.dm b/code/game/objects/items/weapons/implants/implantpad.dm index fde73d6997d..f895a2438fd 100644 --- a/code/game/objects/items/weapons/implants/implantpad.dm +++ b/code/game/objects/items/weapons/implants/implantpad.dm @@ -1,94 +1,94 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/implantpad - name = "implantpad" - desc = "Used to modify implants." - icon = 'icons/obj/items.dmi' - icon_state = "implantpad-0" - item_state = "electronic" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - var/obj/item/weapon/implantcase/case = null - var/broadcasting = null - var/listening = 1.0 -/obj/item/weapon/implantpad/proc/update() - if (src.case) - src.icon_state = "implantpad-1" - else - src.icon_state = "implantpad-0" - return - - -/obj/item/weapon/implantpad/attack_hand(mob/living/user as mob) - if ((src.case && user.item_is_in_hands(src))) - user.put_in_active_hand(case) - - src.case.add_fingerprint(user) - src.case = null - - src.add_fingerprint(user) - update() - else - return ..() - return - - -/obj/item/weapon/implantpad/attackby(obj/item/weapon/implantcase/C as obj, mob/user as mob) - ..() - if(istype(C, /obj/item/weapon/implantcase)) - if(!( src.case )) - user.drop_item() - C.loc = src - src.case = C - else - return - src.update() - return - - -/obj/item/weapon/implantpad/attack_self(mob/user as mob) - user.set_machine(src) - var/dat = "Implant Mini-Computer:
                    " - if (src.case) - if(src.case.imp) - if(istype(src.case.imp, /obj/item/weapon/implant)) - dat += src.case.imp.get_data() - if(istype(src.case.imp, /obj/item/weapon/implant/tracking)) - dat += {"ID (1-100): - - - - [case.imp:id] - + - +
                    "} - else - dat += "The implant casing is empty." - else - dat += "Please insert an implant casing!" - user << browse(dat, "window=implantpad") - onclose(user, "implantpad") - return - - -/obj/item/weapon/implantpad/Topic(href, href_list) - ..() - if (usr.stat) - return - if ((usr.contents.Find(src)) || ((in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if (href_list["tracking_id"]) - var/obj/item/weapon/implant/tracking/T = src.case.imp - T.id += text2num(href_list["tracking_id"]) - T.id = min(1000, T.id) - T.id = max(1, T.id) - - if (istype(src.loc, /mob)) - attack_self(src.loc) - else - for(var/mob/M in viewers(1, src)) - if (M.client) - src.attack_self(M) - src.add_fingerprint(usr) - else - usr << browse(null, "window=implantpad") - return - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/implantpad + name = "implantpad" + desc = "Used to modify implants." + icon = 'icons/obj/items.dmi' + icon_state = "implantpad-0" + item_state = "electronic" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + var/obj/item/weapon/implantcase/case = null + var/broadcasting = null + var/listening = 1.0 +/obj/item/weapon/implantpad/proc/update() + if (src.case) + src.icon_state = "implantpad-1" + else + src.icon_state = "implantpad-0" + return + + +/obj/item/weapon/implantpad/attack_hand(mob/living/user as mob) + if ((src.case && user.item_is_in_hands(src))) + user.put_in_active_hand(case) + + src.case.add_fingerprint(user) + src.case = null + + src.add_fingerprint(user) + update() + else + return ..() + return + + +/obj/item/weapon/implantpad/attackby(obj/item/weapon/implantcase/C as obj, mob/user as mob) + ..() + if(istype(C, /obj/item/weapon/implantcase)) + if(!( src.case )) + user.drop_item() + C.loc = src + src.case = C + else + return + src.update() + return + + +/obj/item/weapon/implantpad/attack_self(mob/user as mob) + user.set_machine(src) + var/dat = "Implant Mini-Computer:
                    " + if (src.case) + if(src.case.imp) + if(istype(src.case.imp, /obj/item/weapon/implant)) + dat += src.case.imp.get_data() + if(istype(src.case.imp, /obj/item/weapon/implant/tracking)) + dat += {"ID (1-100): + - + - [case.imp:id] + + + +
                    "} + else + dat += "The implant casing is empty." + else + dat += "Please insert an implant casing!" + user << browse(dat, "window=implantpad") + onclose(user, "implantpad") + return + + +/obj/item/weapon/implantpad/Topic(href, href_list) + ..() + if (usr.stat) + return + if ((usr.contents.Find(src)) || ((in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if (href_list["tracking_id"]) + var/obj/item/weapon/implant/tracking/T = src.case.imp + T.id += text2num(href_list["tracking_id"]) + T.id = min(1000, T.id) + T.id = max(1, T.id) + + if (istype(src.loc, /mob)) + attack_self(src.loc) + else + for(var/mob/M in viewers(1, src)) + if (M.client) + src.attack_self(M) + src.add_fingerprint(usr) + else + usr << browse(null, "window=implantpad") + return + return diff --git a/code/game/objects/items/weapons/implants/implantuplink.dm b/code/game/objects/items/weapons/implants/implantuplink.dm index 51f9c0d696d..6ee094aeded 100644 --- a/code/game/objects/items/weapons/implants/implantuplink.dm +++ b/code/game/objects/items/weapons/implants/implantuplink.dm @@ -1,25 +1,25 @@ -/obj/item/weapon/implant/uplink - name = "uplink" - desc = "Summon things." - var/activation_emote = "chuckle" - -/obj/item/weapon/implant/uplink/New() - activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - hidden_uplink = new(src) - //hidden_uplink.uses = 5 - //Code currently uses a mind var for telecrystals, balancing is currently an issue. Will investigate. - ..() - return - -/obj/item/weapon/implant/uplink/post_implant(mob/source) - var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") - activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) - if(!activation_emote) - activation_emote = pick(choices) - source.mind.store_memory("Uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - to_chat(source, "The implanted uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") - -/obj/item/weapon/implant/uplink/trigger(emote, mob/source as mob) - if(hidden_uplink && usr == source) // Let's not have another people activate our uplink - hidden_uplink.check_trigger(source, emote, activation_emote) +/obj/item/weapon/implant/uplink + name = "uplink" + desc = "Summon things." + var/activation_emote = "chuckle" + +/obj/item/weapon/implant/uplink/New() + activation_emote = pick("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + hidden_uplink = new(src) + //hidden_uplink.uses = 5 + //Code currently uses a mind var for telecrystals, balancing is currently an issue. Will investigate. + ..() + return + +/obj/item/weapon/implant/uplink/post_implant(mob/source) + var/choices = list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") + activation_emote = tgui_input_list(usr, "Choose activation emote. If you cancel this, one will be picked at random.", "Implant Activation", choices) + if(!activation_emote) + activation_emote = pick(choices) + source.mind.store_memory("Uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) + to_chat(source, "The implanted uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") + +/obj/item/weapon/implant/uplink/trigger(emote, mob/source as mob) + if(hidden_uplink && usr == source) // Let's not have another people activate our uplink + hidden_uplink.check_trigger(source, emote, activation_emote) return \ No newline at end of file diff --git a/code/game/objects/items/weapons/improvised_components.dm b/code/game/objects/items/weapons/improvised_components.dm index 928a7c1a5d2..5add8778600 100644 --- a/code/game/objects/items/weapons/improvised_components.dm +++ b/code/game/objects/items/weapons/improvised_components.dm @@ -1,40 +1,40 @@ -/obj/item/weapon/material/butterflyconstruction - name = "unfinished concealed knife" - desc = "An unfinished concealed knife, it looks like the screws need to be tightened." - icon = 'icons/obj/buildingobject.dmi' - icon_state = "butterflystep1" - force_divisor = 0.1 - thrown_force_divisor = 0.1 - -/obj/item/weapon/material/butterflyconstruction/attackby(obj/item/W as obj, mob/user as mob) - if(W.is_screwdriver()) - to_chat(user, "You finish the concealed blade weapon.") - playsound(src, W.usesound, 50, 1) - new /obj/item/weapon/material/butterfly(user.loc, material.name) - qdel(src) - return - -/obj/item/weapon/material/butterflyblade - name = "knife blade" - desc = "A knife blade. Unusable as a weapon without a grip." - icon = 'icons/obj/buildingobject.dmi' - icon_state = "butterfly2" - force_divisor = 0.1 - thrown_force_divisor = 0.1 - -/obj/item/weapon/material/butterflyhandle - name = "concealed knife grip" - desc = "A plasteel grip with screw fittings for a blade." - icon = 'icons/obj/buildingobject.dmi' - icon_state = "butterfly1" - force_divisor = 0.1 - thrown_force_divisor = 0.1 - -/obj/item/weapon/material/butterflyhandle/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/material/butterflyblade)) - var/obj/item/weapon/material/butterflyblade/B = W - to_chat(user, "You attach the two concealed blade parts.") - new /obj/item/weapon/material/butterflyconstruction(user.loc, B.material.name) - qdel(W) - qdel(src) - return +/obj/item/weapon/material/butterflyconstruction + name = "unfinished concealed knife" + desc = "An unfinished concealed knife, it looks like the screws need to be tightened." + icon = 'icons/obj/buildingobject.dmi' + icon_state = "butterflystep1" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + +/obj/item/weapon/material/butterflyconstruction/attackby(obj/item/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You finish the concealed blade weapon.") + playsound(src, W.usesound, 50, 1) + new /obj/item/weapon/material/butterfly(user.loc, material.name) + qdel(src) + return + +/obj/item/weapon/material/butterflyblade + name = "knife blade" + desc = "A knife blade. Unusable as a weapon without a grip." + icon = 'icons/obj/buildingobject.dmi' + icon_state = "butterfly2" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + +/obj/item/weapon/material/butterflyhandle + name = "concealed knife grip" + desc = "A plasteel grip with screw fittings for a blade." + icon = 'icons/obj/buildingobject.dmi' + icon_state = "butterfly1" + force_divisor = 0.1 + thrown_force_divisor = 0.1 + +/obj/item/weapon/material/butterflyhandle/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/material/butterflyblade)) + var/obj/item/weapon/material/butterflyblade/B = W + to_chat(user, "You attach the two concealed blade parts.") + new /obj/item/weapon/material/butterflyconstruction(user.loc, B.material.name) + qdel(W) + qdel(src) + return diff --git a/code/game/objects/items/weapons/inducer_vr.dm b/code/game/objects/items/weapons/inducer_vr.dm index 0c395990714..b0519abce87 100644 --- a/code/game/objects/items/weapons/inducer_vr.dm +++ b/code/game/objects/items/weapons/inducer_vr.dm @@ -77,7 +77,7 @@ /obj/item/weapon/inducer/attackby(obj/item/W, mob/user) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, W.usesound, 50, 1) if(!opened) to_chat(user, "You open the battery compartment.") diff --git a/code/game/objects/items/weapons/joke.dm b/code/game/objects/items/weapons/joke.dm new file mode 100644 index 00000000000..008e3ec7203 --- /dev/null +++ b/code/game/objects/items/weapons/joke.dm @@ -0,0 +1,19 @@ +/obj/item/weapon/squishhammer + name = "The Short Stacker" + desc = "Wield the power of this weapon with responsibility (God knows you won't)." + icon = 'icons/obj/items.dmi' + icon_state = "toyhammer" + attack_verb = list("stacked") + force = 0 + throwforce = 0 + +// Attack mob +/obj/item/weapon/squishhammer/attack(mob/M as mob, mob/user as mob) + var/is_squished = M.tf_scale_x || M.tf_scale_y + playsound(src, 'sound/items/hooh.ogg', 50, 1) + if(!is_squished) + M.SetTransform(null, (M.size_multiplier * 1.2), (M.size_multiplier * 0.5)) + else + M.ClearTransform() + M.update_transform() + return ..() \ No newline at end of file diff --git a/code/game/objects/items/weapons/keys.dm b/code/game/objects/items/weapons/keys.dm new file mode 100644 index 00000000000..fc134497673 --- /dev/null +++ b/code/game/objects/items/weapons/keys.dm @@ -0,0 +1,10 @@ +/obj/item/weapon/simple_key + name = "key" + desc = "A plain, old-timey key, as one might use to unlock a door." + icon = 'icons/obj/keys.dmi' + icon_state = "key_basetype" + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + w_class = ITEMSIZE_TINY + var/keyverb = "uses" //so simple_keys can be keycards instead, if desired + var/key_id = "placeholder_DONOTUSE" //needs to match the associated door's LOCK_ID var \ No newline at end of file diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 277a4fc3a66..fa55b42e752 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -1,1842 +1,1842 @@ -/*********************MANUALS (BOOKS)***********************/ - -/obj/item/weapon/book/manual - icon = 'icons/obj/library.dmi' - due_date = 0 // Game time in 1/10th seconds - unique = 1 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified - - -/obj/item/weapon/book/manual/engineering_construction - name = "Station Repairs and Construction" - icon_state ="bookEngineering" - item_state = "book3" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Station Repairs and Construction" - -/obj/item/weapon/book/manual/engineering_construction/New() - ..() - dat = {" - - - - - - - - - - - "} - -/obj/item/weapon/book/manual/engineering_particle_accelerator - name = "Particle Accelerator User's Guide" - icon_state ="bookParticleAccelerator" - item_state = "book15" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Particle Accelerator User's Guide" - -/obj/item/weapon/book/manual/engineering_particle_accelerator/New() - ..() - dat = {" - - - - - -

                    Experienced User's Guide

                    - -

                    Setting up the accelerator

                    - -
                      -
                    1. Wrench all pieces to the floor
                    2. -
                    3. Add wires to all the pieces
                    4. -
                    5. Close all the panels with your screwdriver
                    6. -
                    - -

                    Using the accelerator

                    - -
                      -
                    1. Open the control panel
                    2. -
                    3. Set the speed to 2
                    4. -
                    5. Start firing at the singularity generator
                    6. -
                    7. When the singularity reaches a large enough size so it starts moving on it's own set the speed down to 0, but don't shut it off
                    8. -
                    9. Remember to wear a radiation suit when working with this machine... we did tell you that at the start, right?
                    10. -
                    - - - - "} - - -/obj/item/weapon/book/manual/supermatter_engine - name = "Supermatter Engine Operating Manual" - icon_state = "bookSupermatter" - item_state = "book15" - author = "Central Engineering Division" - title = "Supermatter Engine Operating Manual" - -/obj/item/weapon/book/manual/supermatter_engine/New() - ..() - dat = {" - - - - -

                    OPERATING MANUAL FOR MK 1 PROTOTYPE THERMOELECTRIC SUPERMATTER ENGINE 'TOMBOLA'

                    -
                    -

                    OPERATING PRINCIPLES

                    -
                    -
                  • The supermatter crystal serves as the fundamental power source of the engine. Upon being charged, it begins to emit large amounts of heat and radiation, as well and oxygen and phoron gas. As oxygen accelerates the reaction and reacts with phoron to start a fire, it must be filtered out. It's recommended to filter out all gases besides phoron for standard operation.
                  • -
                    -
                  • Gas in the reactor chamber housing the supermatter is circulated through the reactor loop, which passes through the filters and thermoelectric generators. The thermoelectric generators transfer heat from the reactor loop to the colder radiator loop, thereby generating power. Additional power is generated from internal turbines in the circulators.
                  • -
                    -
                  • Gas in the radiator loop is circulated through the radiator bank, located in space. This rapidly cools the air, preserving the temperature differential needed for power generation.
                  • -
                    -
                  • The MK 1 Prototype Thermoelectric Supermatter Engine is designed to operate at reactor temperatures of 3000K to 4000K and generate up to 1MW of power. Beyond 1MW, the thermoelectric generators will begin to lose power through electrical discharge, reducing efficiency, but additional power generation remains feasible.
                  • -
                    -
                  • The crystal structure of the supermatter will begin to liquefy if its temperature exceeds 5000K. This eventually results in a massive release of light, heat and radiation, disintegration of both the supermatter crystal and most of the surrounding area, and as as-of-yet poorly documented psychological effects on all animals within a 2km radius. Appropriate action should be taken to stabilize or eject the supermatter before such occurs.
                  • -
                    -

                    SUPERMATTER HANDLING

                    -
                  • Do not expose supermatter to oxygen.
                  • -
                  • Do not allow supermatter to contact any solid object apart from specially-designed supporting pallet.
                  • -
                  • Do not directly view supermatter without meson goggles.
                  • -
                  • While handles on pallet allow moving the supermatter via pulling, pushing should not be attempted.
                  • -
                  • Note that prosthetics do not protect against radiation or viewing the supermatter.
                  • -
                    -

                    STANDARD STARTUP PROCEDURE

                    -
                      -
                    1. Fill reactor loop and radiator loop with two (2) standard canisters (1871.75 moles, 20 celsius) of phoron gas each.
                    2. -
                    3. Fill the waste handling radiator loop with one (1) standard canister (1871.75 as well) of carbon dioxide gas.
                    4. -
                    5. You can confirm your canisters got enough gas in them by using your PDA's "Gas Scanner" feature, you must enable it and press your PDA against the canister.
                    6. -
                    7. Enable both the high power gas pumps near the thermo-electric generators and maximize the desired output.
                    8. -
                    9. Enable both the omni-filters and ensure they are set to filter phoron back into the system.
                    10. -
                    11. Enable the gas pump from the filters to waste handling and maximize the desired output.
                    12. -
                    13. Close the monitoring room blast doors and open the reactor blast doors,
                    14. -
                    15. Fire 12-16 pulses from emitter at supermatter crystal. The expected power output is around a megawatt. NOTE: It will take a few minutes to heat up.
                    16. -
                    17. You can confirm desired power levels by checking the computer in the minotoring room, "Supermatter Monitoring". You want 300 EPR, more is safe but no higher than 700
                    18. -
                    19. Close the reactor blast doors and keep the monitoring room blast doors closed to prevent radiation leaking.
                    20. -
                    -
                    -

                    OPERATION AND MAINTENANCE

                    -
                      -
                    1. Ensure that radiation protection and meson goggles are worn at all times while working in the engine room.
                    2. -
                    3. Ensure that reactor and radiator loops are undamaged and unobstructed.
                    4. -
                    5. Ensure that, in a standard setup, only phoron is being filtered back into the system. Do not allow exhaust pressure to exceed 4500 kPa.
                    6. -
                    7. Ensure that engine room Area Power Controller (APC) and engine Superconducting Magnetic Energy Storage unit (SMES) are properly charged.
                    8. -
                    9. Ensure that reactor temperature does not exceed 5000K. In event of reactor temperature exceeding 5000K, see EMERGENCY COOLING PROCEDURE.
                    10. -
                    11. In event of imminent and/or unavoidable delamination, see EJECTION PROCEDURE.
                    12. -
                    -
                    -

                    EMERGENCY COOLING PROCEDURE

                    -
                      -
                    1. Open Emergency Cooling Valve 1 and Emergency Cooling Valve 2.
                    2. -
                    3. When reactor temperature returns to safe operating levels, close Emergency Cooling Valve 1 and Emergency Cooling Valve 2.
                    4. -
                    5. Adding additional gas to the loops can have a positive effect in reducing reactor temperature.
                    6. -
                    7. If reactor temperature does not return to safe operating levels, see EJECTION PROCEDURE.
                    8. -
                    -
                    -

                    EJECTION PROCEDURE

                    -
                      -
                    1. Ensure the engine room has power. The blast doors and ejection platform are unresponsive without power.
                    2. -
                    3. Press Engine Ventilatory Control button to open engine core blast door to space.
                    4. -
                    5. Press Emergency Core Eject button to eject supermatter crystal. NOTE: Attempting crystal ejection while engine core vent is closed will result in ejection failure.
                    6. -
                    7. In event of ejection failure, pending
                    8. -
                    - - "} - -// TESLA Engine - -/obj/item/weapon/book/manual/tesla_engine - name = "Tesla Operating Manual" - icon_state ="bookTesla" - item_state = "book15" - author = "Engineering Encyclopedia" - title = "Tesla Engine User's Guide" - dat = {" - - - - -

                    OPERATING MANUAL FOR MK 2 PROTOTYPE TESLA ENGINE 'EDISON'S BANE'

                    -
                    -

                    OPERATING PRINCIPLES

                    -

                    This big floaty ball of pure electricity can only be contained by the containment field. It periodically will discharge energy in the form of an electric shock which can be harvested for energy.

                    -

                    When you shoot the energy ball with the Particle Accelerator, it gains energy, and when enough energy is accumulated a mini-energy ball that orbits the big energy ball will be formed. This can happen as many times as you let it, each mini-ball will send off an extra shock when the energy ball pulses. Be warned, the more mini-balls the energy ball has, the more shocks it sends out at once and the further it can travel each move.

                    -

                    An energy ball will shoot bolts of electricity off at conductors, which it prioritizes in this order: -

                      -
                    1. Tesla coils
                    2. -
                    3. Grounding rods
                    4. -
                    5. People and animals
                    6. -
                    7. Machines
                    8. -
                    -

                    -

                    Tesla Coils will attract the energy ball's bolts. They will take half the power of the bolt (if they are connected to a wire node), pump it into the powernet it is hooked to, and then will send the other half of the power to the next available conductor, which follows the criteria listed above. Preferably, this will be another coil to harness more of the power and pump it into the grid.

                    -

                    Grounding Rods are safety precautions to prevent the tesla bolts from hitting machinery or personnel. If the tesla is loose, being near one will usually keep you safe from direct shocks.

                    -
                    -

                    STARTUP PROCEDURE

                    -
                      -
                    1. Bolt and weld down the Field Generators, ensuring they form a complete rectangle.
                    2. -
                    3. Bolt and weld down the Emitters, ensuring their fire will strike the corner Field Generators
                    4. -
                    5. Bolt down the Tesla Generator inside the rectangle formed by the Field Generators in a spot where it will be struck by fire from the Particle Accelerator
                    6. -
                    7. Bolt down Telsa Coils and Grounding Rods
                    8. -
                    9. Activate the Emitters
                    10. -
                    11. Activate each of the Field Generators, then wait until the containment field has completely formed.
                    12. -
                    13. Setup the Particle Accelerator (see our best seller "Particle Accelerator User's Guide"!) and activate it.
                    14. -
                    15. After a short time the Telsa Generator will create an energy ball, being consumed in the process.
                    16. -
                    -
                    -

                    OPERATION AND MAINTENANCE

                    -
                      -
                    1. Ensure that electrical protection and meson goggles are worn at all times while working in the engine room.
                    2. -
                    3. Ensure that Telsa Coils and/or Grounding Rods are placed to safely collect or ground any and all shock.
                    4. -
                    5. Ensure that all Emitters remain activated and have unobstructed lines of fire to the Field Generators.
                    6. -
                    7. Do not let the Emitters run out of power.
                    8. -
                    -
                    -

                    SHUTDOWN PROCEDURE

                    -
                      -
                    1. De-activate the Particle Accelerator. The energy ball will begin to shrink and lose mini-balls.
                    2. -
                    3. When the energy ball has completely dissipated, the Emitters can be de-activated.
                    4. -
                    -
                    -

                    ENERGY BALL ESCAPE PROCEDURE

                    -
                      -
                    1. Do not let it escape.
                    2. -
                    3. Have someone ready to blame when it does escape.
                    4. -
                    5. Buy our forthcoming manual "Celebrity Grounding Rod Shelters of the Galaxy"
                    6. -
                    - - "} - -//R-UST port -/obj/item/weapon/book/manual/rust_engine - name = "R-UST Operating Manual" - icon_state = "bookSupermatter" - item_state = "book15" - author = "Cindy Crawfish" - title = "R-UST Operating Manual" - -/obj/item/weapon/book/manual/rust_engine/New() - ..() - dat = {" - - - - -

                    -
                      -
                    1. Put uranium in the portable generator near the gyrotron and turn it to full. This is to provide initial power to the core.
                    2. -
                    3. Enable and max output on the SMES in the engine room. This is to power the gyrotron.
                    4. -
                    5. Go into the control room, interact with the fusion core control console. Turn the field on and raise size to 501. Any bigger and it will start EMPing the doors. Any smaller and the fuel pellets might miss.
                    6. -
                    7. Interact with the gyrotron control computer, set power as high as the SMES can support, usually around 4, and turn it on. This will start increasing the plasma temperature to the point where reactions can occur.
                    8. -
                    9. Go into the engine room and insert a deuterium fuel assembly and a tritium fuel assembly into two of the fuel injectors. You can make deuterium rods in the fuel compressor if you want to play it safe.
                    10. -
                    11. Go back to the control room and turn the fuel injectors on. This will start firing pellets into the field.
                    12. -
                    13. Wait for reactions to start (plasma temperature will spike and fuel amounts will drop). Turn the gyrotron power down until it's keeping up with field instability. This will prevent cumulative instability from the deuterium-tritium reaction fucking up the field. If you're using straight deuterium instability isn't a problem and you can turn the gyrotron off.
                    14. -
                    15. Configure the SMES, turn the PACMAN off before it explodes.
                    16. -
                    -
                    - NOTES FOR NEWBIES -
                    - Anything touching the field will mess with its stability and eventually cause it to rupture. Rupturing is bad. Use the gyrotron to keep instability down if you're running the engine on unstable fuel. -

                    - Likewise, no matter how sad the core seems, don't fucking hug it, you'll blow the field out and set the engine room on fire. - - "} - -/obj/item/weapon/book/manual/engineering_hacking - name = "Hacking" - icon_state ="bookHacking" - item_state = "book2" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Hacking" - -/obj/item/weapon/book/manual/engineering_hacking/New() - ..() - dat = {" - - - - - - - - - - - "} - - -/obj/item/weapon/book/manual/engineering_singularity_safety - name = "Singularity Safety in Special Circumstances" - icon_state ="bookEngineeringSingularitySafety" - item_state = "book15" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Singularity Safety in Special Circumstances" - - dat = {" - - - - -

                    Singularity Safety in Special Circumstances

                    - -

                    Power outage

                    - - A power problem has made the entire station lose power? Could be station-wide wiring problems or illegal power sinks. In any case follow these steps: - -
                      -
                    1. PANIC!
                    2. -
                    3. Get your ass over to engineering! QUICKLY!!!
                    4. -
                    5. Get to the Area Power Controller which controls the power to the emitters.
                    6. -
                    7. Swipe it with your ID card - if it doesn't unlock, continue with step 15.
                    8. -
                    9. Open the console and disengage the cover lock.
                    10. -
                    11. Pry open the APC with a Crowbar.
                    12. -
                    13. Take out the empty power cell.
                    14. -
                    15. Put in the new, full power cell - if you don't have one, continue with step 15.
                    16. -
                    17. Quickly put on a Radiation suit.
                    18. -
                    19. Check if the singularity field generators withstood the down-time - if they didn't, continue with step 15.
                    20. -
                    21. Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering APC is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the APC to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
                    22. -
                    23. Grab your crowbar and pry away the tile closest to the APC.
                    24. -
                    25. Use the wirecutters to cut the wire which is connecting the grid to the terminal.
                    26. -
                    27. Go to the bar and tell the guys how you saved them all. Stop reading this guide here.
                    28. -
                    29. GET THE FUCK OUT OF THERE!!!
                    30. -
                    - -

                    Shields get damaged

                    - -
                      -
                    1. GET THE FUCK OUT OF THERE!!! FORGET THE WOMEN AND CHILDREN, SAVE YOURSELF!!!
                    2. -
                    - - - "} - - -/obj/item/weapon/book/manual/hydroponics_pod_people - name = "The Diona Harvest - From Seed to Market" - icon_state ="bookHydroponicsPodPeople" - item_state = "book5" - author = "Farmer John" - title = "The Diona Harvest - From Seed to Market" - - dat = {" - - - - -

                    Growing a Diona

                    - - Growing a Diona is easy! -

                    -

                      -
                    1. Take a syringe of blood from the body you wish to turn into a Diona.
                    2. -
                    3. Inject 5 units of blood into the pack of dionaea-replicant seeds.
                    4. -
                    5. Plant the seeds.
                    6. -
                    7. Tend to the plants water and nutrition levels until it is time to harvest the Diona.
                    8. -
                    -

                    - Note that for a successful harvest, the body from which the blood was taken from must be dead BEFORE harvesting the pod, however the pod can be growing while they are still alive. Otherwise, the soul would not be able to migrate to the new Diona body.

                    - - It really is that easy! Good luck! - - - - "} - - -/obj/item/weapon/book/manual/medical_cloning - name = "Cloning Techniques of the 24th Century" - icon_state ="bookCloning" - item_state = "book9" - author = "Medical Journal, volume 3" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Cloning Techniques of the 24th Century" - - dat = {" - - - - - -

                    How to Clone People

                    - So there are 50 dead people lying on the floor, chairs are spinning like no tomorrow and you haven't the foggiest idea of what to do? Not to worry! - This guide is intended to teach you how to clone people and how to do it right, in a simple, step-by-step process! If at any point of the guide you have a mental meltdown, - genetics probably isn't for you and you should get a job-change as soon as possible before you're sued for malpractice. - -
                      -
                    1. Acquire body
                    2. -
                    3. Strip body
                    4. -
                    5. Put body in cloning machine
                    6. -
                    7. Scan body
                    8. -
                    9. Clone body
                    10. -
                    11. Get clean Structural Enzymes for the body
                    12. -
                    13. Put body in morgue
                    14. -
                    15. Await cloned body
                    16. -
                    17. Cryo and use the clean SE injector
                    18. -
                    19. Give person clothes back
                    20. -
                    21. Send person on their way
                    22. -
                    - -

                    Step 1: Acquire body

                    - This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step. - -

                    Step 2: Strip body

                    - The cloning machine does not like abiotic items. What this means is you can't clone anyone if they're wearing clothes or holding things, so take all of it off. If it's just one person, it's courteous to put their possessions in the closet. - If you have about seven people awaiting cloning, just leave the piles where they are, but don't mix them around and for God's sake don't let people in to steal them. - -

                    Step 3: Put body in cloning machine

                    - Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset. - -

                    Step 4: Scan body

                    - Go onto the computer and scan the body by pressing 'Scan - <Subject Name Here>.' If you're successful, they will be added to the records (note that this can be done at any time, even with living people, - so that they can be cloned without a body in the event that they are lying dead on port solars and didn't turn on their suit sensors)! - If not, and it says "Error: Mental interface failure.", then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, - click 'Refresh' and try scanning them again. If there's no success, threaten them with gibbing. - Still no success? Skip over to Step 7 and don't continue after it, as you have an unresponsive body and it cannot be cloned. - If you got "Error: Unable to locate valid genetic data.", you are trying to clone a monkey - start over. - -

                    Step 5: Clone body

                    - Now that the body has a record, click 'View Records,' click the subject's name, and then click 'Clone' to start the cloning process. Congratulations! You're halfway there. - Remember not to 'Eject' the cloning pod as this will kill the developing clone and you'll have to start the process again. - -

                    Step 6: Get clean SEs for body

                    - Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! - For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they're done. If you're a competent Geneticist, you will already have one ready on your working computer. - If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. - Go into View/Edit/Transfer Buffer, find an open slot and click "SE" to save it. Then click 'Injector' to get the SEs in syringe form. Put this in your pocket or something for when the body is done. - -

                    Step 7: Put body in morgue

                    - Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. - To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed "CHEF MEAT" in order to avoid confusion. - -

                    Step 8: Await cloned body

                    - Now go back to the lab and wait for your patient to be cloned. It won't be long now, I promise. - -

                    Step 9: Cryo and clean SE injector on person

                    - Has your body been cloned yet? Great! As soon as the guy pops out, grab them and stick them in cryo. Clonexadone and Cryoxadone help rebuild their genetic material. Then grab your clean SE injector and jab it in them. Once you've injected them, - they now have clean Structural Enzymes and their defects, if any, will disappear in a short while. - -

                    Step 10: Give person clothes back

                    - Obviously the person will be naked after they have been cloned. Provided you weren't an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. - No matter how cruel you are, it's simply against protocol to force your patients to walk outside naked. - -

                    Step 11: Send person on their way

                    - Give the patient one last check-over - make sure they don't still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. - Once you're done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance. - -

                    If you've gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target. - - - - "} - - -/obj/item/weapon/book/manual/ripley_build_and_repair - name = "APLU \"Ripley\" Construction and Operation Manual" - icon_state ="book" - item_state = "book" - author = "Randall Varn, Einstein Engines Senior Mechanic" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "APLU \"Ripley\" Construction and Operation Manual" - - dat = {" - - - - -

                    -
                    - Weyland-Yutani - Building Better Worlds -

                    Autonomous Power Loader Unit \"Ripley\"

                    -
                    -

                    Specifications:

                    -
                      -
                    • Class: Autonomous Power Loader
                    • -
                    • Scope: Logistics and Construction
                    • -
                    • Weight: 820kg (without operator and with empty cargo compartment)
                    • -
                    • Height: 2.5m
                    • -
                    • Width: 1.8m
                    • -
                    • Top speed: 5km/hour
                    • -
                    • Operation in vacuum/hostile environment: Possible -
                    • Airtank volume: 500 liters
                    • -
                    • Devices: -
                        -
                      • Hydraulic clamp
                      • -
                      • High-speed drill
                      • -
                      -
                    • -
                    • Propulsion device: Powercell-powered electro-hydraulic system
                    • -
                    • Powercell capacity: Varies
                    • -
                    - -

                    Construction:

                    -
                      -
                    1. Connect all exosuit parts to the chassis frame.
                    2. -
                    3. Connect all hydraulic fittings and tighten them up with a wrench.
                    4. -
                    5. Adjust the servohydraulics with a screwdriver.
                    6. -
                    7. Wire the chassis (Cable is not included).
                    8. -
                    9. Use the wirecutters to remove the excess cable if needed.
                    10. -
                    11. Install the central control module (Not included. Use supplied datadisk to create one).
                    12. -
                    13. Secure the mainboard with a screwdriver.
                    14. -
                    15. Install the peripherals control module (Not included. Use supplied datadisk to create one).
                    16. -
                    17. Secure the peripherals control module with a screwdriver.
                    18. -
                    19. Install the internal armor plating (Not included due to corporate regulations. Can be made using 5 metal sheets).
                    20. -
                    21. Secure the internal armor plating with a wrench.
                    22. -
                    23. Weld the internal armor plating to the chassis.
                    24. -
                    25. Install the external reinforced armor plating (Not included due to corporate regulations. Can be made using 5 reinforced metal sheets).
                    26. -
                    27. Secure the external reinforced armor plating with a wrench.
                    28. -
                    29. Weld the external reinforced armor plating to the chassis.
                    30. -
                    - -

                    Additional Information:

                    -
                      -
                    • The firefighting variation is made in a similar fashion.
                    • -
                    • A firesuit must be connected to the firefighter chassis for heat shielding.
                    • -
                    • Internal armor is plasteel for additional strength.
                    • -
                    • External armor must be installed in 2 parts, totalling 10 sheets.
                    • -
                    • Completed mech is more resilient against fire, and is a bit more durable overall.
                    • -
                    • The Company is determined to ensure the safety of its investments employees.
                    • -
                    - - - "} - - -/obj/item/weapon/book/manual/research_and_development - name = "Research and Development 101" - icon_state = "rdbook" - item_state = "book7" - author = "Dr. L. Ight" - title = "Research and Development 101" - - dat = {" - - - - - -

                    Science For Dummies

                    - So you want to further SCIENCE? Good man/woman/thing! However, SCIENCE is a complicated process even though it's quite easy. For the most part, it's a three step process: -
                      -
                    1. Deconstruct items in the Destructive Analyzer to advance technology or improve the design.
                    2. -
                    3. Build unlocked designs in the Protolathe and Circuit Imprinter.
                    4. -
                    5. Repeat!
                    6. -
                    - - Those are the basic steps to furthering science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter. - -

                    The R&D Console

                    - The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controlled. More on those systems in their own sections. - On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, - because if it gets damaged, you'll lose your data! - In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. - It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, - upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database.

                    - - NOTE: The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices. - -

                    Destructive Analyzer

                    - This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, - you can destroy the object to further those sciences. - Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather than further science. - If their reliability is high enough, it'll also advance their related technologies. - -

                    Circuit Imprinter

                    - This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules. - -

                    Protolathe

                    - This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid phoron, silver, gold, and diamonds along with a variety of chemicals to produce devices. - The downside is that, again, not all devices you make are 100% reliable when you first discover them. - -

                    Reliability and You

                    - As it has been stated, many devices, when they're first discovered, do not have a 100% reliability. Instead, - the reliability of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, - and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. - Once the device reaches a certain minimum reliability, you'll gain technological advancements from it. - -

                    Building a Better Machine

                    - Many machines produced from circuit boards inserted into a machine frames require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. - If you use these parts when constructing something, its attributes may be improved. - For example, if you use an advanced matter bin when constructing an autolathe (rather than a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: - Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine. - - - "} - -/obj/item/weapon/book/manual/robotics_manual - name = "Guide to Robotics" - icon_state ="evabook" - item_state = "book3" - author = "Simple Robotics" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Guide to Robotics" - -/obj/item/weapon/book/manual/robotics_manual/New() - ..() - dat = {" - - - - - - - "} - -/obj/item/weapon/book/manual/robotics_cyborgs - name = "Cyborgs for Dummies" - icon_state = "borgbook" - item_state = "book1" - author = "XISC" - title = "Cyborgs for Dummies" - - dat = {" - - - - - -

                    Cyborgs for Dummies

                    - -

                    Chapters

                    - -
                      -
                    1. Cyborg Related Equipment
                    2. -
                    3. Cyborg Modules
                    4. -
                    5. Cyborg Construction
                    6. -
                    7. Cyborg Maintenance
                    8. -
                    9. Cyborg Repairs
                    10. -
                    11. In Case of Emergency
                    12. -
                    - - -

                    Cyborg Related Equipment

                    - -

                    Exosuit Fabricator

                    - The Exosuit Fabricator is the most important piece of equipment related to cyborgs. It allows the construction of the core cyborg parts. Without these machines, cyborgs cannot be built. It seems that they may also benefit from advanced research techniques. - -

                    Cyborg Recharging Station

                    - This useful piece of equipment will suck power out of the power systems to charge a cyborg's power cell back up to full charge. - -

                    Robotics Control Console

                    - This useful piece of equipment can be used to immobilize or destroy a cyborg. A word of warning: Cyborgs are expensive pieces of equipment, do not destroy them without good reason, or the Company may see to it that it never happens again. - - -

                    Cyborg Modules

                    - When a cyborg is created it picks out of an array of modules to designate its purpose. There are 6 different cyborg modules. - -

                    Standard Cyborg

                    - The standard cyborg module is a multi-purpose cyborg. It is equipped with various modules, allowing it to do basic tasks.
                    A Standard Cyborg comes with: -
                      -
                    • Crowbar
                    • -
                    • Stun Baton
                    • -
                    • Health Analyzer
                    • -
                    • Fire Extinguisher
                    • -
                    - -

                    Engineering Cyborg

                    - The Engineering cyborg module comes equipped with various engineering-related tools to help with engineering-related tasks.
                    An Engineering Cyborg comes with: -
                      -
                    • A basic set of engineering tools
                    • -
                    • Metal Synthesizer
                    • -
                    • Reinforced Glass Synthesizer
                    • -
                    • An RCD
                    • -
                    • Wire Synthesizer
                    • -
                    • Fire Extinguisher
                    • -
                    • Built-in Optical Meson Scanners
                    • -
                    - -

                    Mining Cyborg

                    - The Mining Cyborg module comes equipped with the latest in mining equipment. They are efficient at mining due to no need for oxygen, but their power cells limit their time in the mines.
                    A Mining Cyborg comes with: -
                      -
                    • Jackhammer
                    • -
                    • Shovel
                    • -
                    • Mining Satchel
                    • -
                    • Built-in Optical Meson Scanners
                    • -
                    - -

                    Security Cyborg

                    - The Security Cyborg module is equipped with effective security measures used to apprehend and arrest criminals without harming them a bit.
                    A Security Cyborg comes with: -
                      -
                    • Stun Baton
                    • -
                    • Handcuffs
                    • -
                    • Taser
                    • -
                    - -

                    Janitor Cyborg

                    - The Janitor Cyborg module is equipped with various cleaning-facilitating devices.
                    A Janitor Cyborg comes with: -
                      -
                    • Mop
                    • -
                    • Hand Bucket
                    • -
                    • Cleaning Spray Synthesizer and Spray Nozzle
                    • -
                    - -

                    Service Cyborg

                    - The service cyborg module comes ready to serve your human needs. It includes various entertainment and refreshment devices. Occasionally some service cyborgs may have been referred to as "Bros."
                    A Service Cyborg comes with: -
                      -
                    • Shaker
                    • -
                    • Industrial Dropper
                    • -
                    • Platter
                    • -
                    • Beer Synthesizer
                    • -
                    • Zippo Lighter
                    • -
                    • Rapid-Service-Fabricator (Produces various entertainment and refreshment objects)
                    • -
                    • Pen
                    • -
                    - -

                    Cyborg Construction

                    - Cyborg construction is a rather easy process, requiring a decent amount of metal and a few other supplies.
                    The required materials to make a cyborg are: -
                      -
                    • Metal
                    • -
                    • Two Flashes
                    • -
                    • One Power Cell (Preferably rated to 15000w)
                    • -
                    • Some electrical wires
                    • -
                    • One Human Brain
                    • -
                    • One Man-Machine Interface
                    • -
                    - Once you have acquired the materials, you can start on construction of your cyborg.
                    To construct a cyborg, follow the steps below: -
                      -
                    1. Start the Exosuit Fabricators constructing all of the cyborg parts
                    2. -
                    3. While the parts are being constructed, take your human brain, and place it inside the Man-Machine Interface
                    4. -
                    5. Once you have a Robot Head, place your two flashes inside the eye sockets
                    6. -
                    7. Once you have your Robot Chest, wire the Robot chest, then insert the power cell
                    8. -
                    9. Attach all of the Robot parts to the Robot frame
                    10. -
                    11. Insert the Man-Machine Interface (With the Brain inside) into the Robot Body
                    12. -
                    13. Congratulations! You have a new cyborg!
                    14. -
                    - -

                    Cyborg Maintenance

                    - Occasionally Cyborgs may require maintenance of a couple types, this could include replacing a power cell with a charged one, or possibly maintaining the cyborg's internal wiring. - -

                    Replacing a Power Cell

                    - Replacing a Power cell is a common type of maintenance for cyborgs. It usually involves replacing the cell with a fully charged one, or upgrading the cell with a larger capacity cell.
                    The steps to replace a cell are as follows: -
                      -
                    1. Unlock the Cyborg's Interface by swiping your ID on it
                    2. -
                    3. Open the Cyborg's outer panel using a crowbar
                    4. -
                    5. Remove the old power cell
                    6. -
                    7. Insert the new power cell
                    8. -
                    9. Close the Cyborg's outer panel using a crowbar
                    10. -
                    11. Lock the Cyborg's Interface by swiping your ID on it, this will prevent non-qualified personnel from attempting to remove the power cell
                    12. -
                    - -

                    Exposing the Internal Wiring

                    - Exposing the internal wiring of a cyborg is fairly easy to do, and is mainly used for cyborg repairs.
                    You can easily expose the internal wiring by following the steps below: -
                      -
                    1. Follow Steps 1 - 3 of "Replacing a Cyborg's Power Cell"
                    2. -
                    3. Open the cyborg's internal wiring panel by using a screwdriver to unsecure the panel
                    4. -
                    - To re-seal the cyborg's internal wiring: -
                      -
                    1. Use a screwdriver to secure the cyborg's internal panel
                    2. -
                    3. Follow steps 4 - 6 of "Replacing a Cyborg's Power Cell" to close up the cyborg
                    4. -
                    - -

                    Cyborg Repairs

                    - Occasionally a Cyborg may become damaged. This could be in the form of impact damage from a heavy or fast-travelling object, or it could be heat damage from high temperatures, or even lasers or Electromagnetic Pulses (EMPs). - -

                    Dents

                    - If a cyborg becomes damaged due to impact from heavy or fast-moving objects, it will become dented. Sure, a dent may not seem like much, but it can compromise the structural integrity of the cyborg, possibly causing a critical failure. - Dents in a cyborg's frame are rather easy to repair, all you need is to apply a welding tool to the dented area, and the high-tech cyborg frame will repair the dent under the heat of the welder. - -

                    Excessive Heat Damage

                    - If a cyborg becomes damaged due to excessive heat, it is likely that the internal wires will have been damaged. You must replace those wires to ensure that the cyborg remains functioning properly.
                    To replace the internal wiring follow the steps below: -
                      -
                    1. Unlock the Cyborg's Interface by swiping your ID
                    2. -
                    3. Open the Cyborg's External Panel using a crowbar
                    4. -
                    5. Remove the Cyborg's Power Cell
                    6. -
                    7. Using a screwdriver, expose the internal wiring of the Cyborg
                    8. -
                    9. Replace the damaged wires inside the cyborg
                    10. -
                    11. Secure the internal wiring cover using a screwdriver
                    12. -
                    13. Insert the Cyborg's Power Cell
                    14. -
                    15. Close the Cyborg's External Panel using a crowbar
                    16. -
                    17. Lock the Cyborg's Interface by swiping your ID
                    18. -
                    - These repair tasks may seem difficult, but are essential to keep your cyborgs running at peak efficiency. - -

                    In Case of Emergency

                    - In case of emergency, there are a few steps you can take. - -

                    "Rogue" Cyborgs

                    - If the cyborgs seem to become "rogue", they may have non-standard laws. In this case, use extreme caution. - To repair the situation, follow these steps: -
                      -
                    1. Locate the nearest robotics console
                    2. -
                    3. Determine which cyborgs are "Rogue"
                    4. -
                    5. Press the lockdown button to immobilize the cyborg
                    6. -
                    7. Locate the cyborg
                    8. -
                    9. Expose the cyborg's internal wiring
                    10. -
                    11. Check to make sure the LawSync and AI Sync lights are lit
                    12. -
                    13. If they are not lit, pulse the LawSync wire using a multitool to enable the cyborg's LawSync
                    14. -
                    15. Proceed to a cyborg upload console. The Company usually places these in the same location as AI upload consoles.
                    16. -
                    17. Use a "Reset" upload moduleto reset the cyborg's laws
                    18. -
                    19. Proceed to a Robotics Control console
                    20. -
                    21. Remove the lockdown on the cyborg
                    22. -
                    - -

                    As a last resort

                    - If all else fails in a case of cyborg-related emergency, there may be only one option. Using a Robotics Control console, you may have to remotely detonate the cyborg. -

                    WARNING:

                    Do not detonate a borg without an explicit reason for doing so. Cyborgs are expensive pieces of company equipment, and you may be punished for detonating them without reason. - - - - "} - - -/obj/item/weapon/book/manual/security_space_law - name = "Corporate Regulations" - desc = "A set of corporate guidelines for keeping law and order on privately-owned space stations." - icon_state = "bookSpaceLaw" - item_state = "book13" - author = "The Company" - title = "Corporate Regulations" - -/obj/item/weapon/book/manual/security_space_law/New() - ..() - dat = {" - - - - - - - - - - - "} - - - -/obj/item/weapon/book/manual/medical_diagnostics_manual - name = "Medical Diagnostics Manual" - desc = "First, do no harm. A detailed medical practitioner's guide." - icon_state = "bookMedical" - item_state = "book12" - author = "Medical Department" - title = "Medical Diagnostics Manual" - -/obj/item/weapon/book/manual/medical_diagnostics_manual/New() - ..() - dat = {" - - - - -
                    -

                    The Oath

                    - - The Medical Oath sworn by recognised medical practitioners in the employ of [using_map.company_name]
                    - -
                      -
                    1. Now, as a new doctor, I solemnly promise that I will, to the best of my ability, serve humanity-caring for the sick, promoting good health, and alleviating pain and suffering.
                    2. -
                    3. I recognise that the practice of medicine is a privilege with which comes considerable responsibility and I will not abuse my position.
                    4. -
                    5. I will practise medicine with integrity, humility, honesty, and compassion-working with my fellow doctors and other colleagues to meet the needs of my patients.
                    6. -
                    7. I shall never intentionally do or administer anything to the overall harm of my patients.
                    8. -
                    9. I will not permit considerations of gender, race, religion, political affiliation, sexual orientation, nationality, or social standing to influence my duty of care.
                    10. -
                    11. I will oppose policies in breach of human rights and will not participate in them. I will strive to change laws that are contrary to my profession's ethics and will work towards a fairer distribution of health resources.
                    12. -
                    13. I will assist my patients to make informed decisions that coincide with their own values and beliefs and will uphold patient confidentiality.
                    14. -
                    15. I will recognise the limits of my knowledge and seek to maintain and increase my understanding and skills throughout my professional life. I will acknowledge and try to remedy my own mistakes and honestly assess and respond to those of others.
                    16. -
                    17. I will seek to promote the advancement of medical knowledge through teaching and research.
                    18. -
                    19. I make this declaration solemnly, freely, and upon my honour.
                    20. -

                    - -
                    - - - - - - "} - - -/obj/item/weapon/book/manual/engineering_guide - name = "Engineering Textbook" - icon_state ="bookEngineering2" - item_state = "book3" - author = "Engineering Encyclopedia" - title = "Engineering Textbook" - -/obj/item/weapon/book/manual/engineering_guide/New() - ..() - dat = {" - - - - - - - - - - "} - - -/obj/item/weapon/book/manual/chef_recipes - name = "Chef Recipes" - icon_state = "cooked_book" - item_state = "book16" - author = "Victoria Ponsonby" - title = "Chef Recipes" - - dat = {" - - - - - -

                    Food for Dummies

                    - Here is a guide on basic food recipes and also how to not poison your customers accidentally. - -

                    Basics:

                    - Knead an egg and some flour to make dough. Bake that to make a bun or flatten and cut it. - -

                    Burger:

                    - Put a bun and some meat into the microwave and turn it on. Then wait. - -

                    Bread:

                    - Put some dough and an egg into the microwave and then wait. - -

                    Waffles:

                    - Add two lumps of dough and 10 units of sugar to the microwave and then wait. - -

                    Popcorn:

                    - Add 1 corn to the microwave and wait. - -

                    Meat Steak:

                    - Put a slice of meat, 1 unit of salt, and 1 unit of pepper into the microwave and wait. - -

                    Meat Pie:

                    - Put a flattened piece of dough and some meat into the microwave and wait. - -

                    Boiled Spaghetti:

                    - Put the spaghetti (processed flour) and 5 units of water into the microwave and wait. - -

                    Donuts:

                    - Add some dough and 5 units of sugar to the microwave and wait. - -

                    Fries:

                    - Add one potato to the processor, then bake them in the microwave. - - - - - "} - - -/obj/item/weapon/book/manual/barman_recipes - name = "Barman Recipes" - desc = "For the enterprising drink server." - icon_state = "barbook" - item_state = "book14" - author = "Sir John Rose" - title = "Barman Recipes" - - dat = {" - - - - - -

                    Drinks for Dummies

                    - Here's a guide for some basic drinks. - -

                    Black Russian:

                    - Mix vodka and Kahlua into a glass. - -

                    Cafe Latte:

                    - Mix milk and coffee into a glass. - -

                    Classic Martini:

                    - Mix vermouth and gin into a glass. - -

                    Gin Tonic:

                    - Mix gin and tonic into a glass. - -

                    Grog:

                    - Mix rum and water into a glass. - -

                    Irish Cream:

                    - Mix cream and whiskey into a glass. - -

                    The Manly Dorf:

                    - Mix ale and beer into a glass. - -

                    Mead:

                    - Mix enzyme, water, and sugar into a glass. - -

                    Screwdriver:

                    - Mix vodka and orange juice into a glass. - - - - "} - - -/obj/item/weapon/book/manual/detective - name = "The Film Noir: Proper Procedures for Investigations" - icon_state ="bookDetective" - item_state = "book8" - author = "The Company" - title = "The Film Noir: Proper Procedures for Investigations" - - dat = {" - - - - -

                    Detective Work

                    - - Between your bouts of self-narration and drinking whiskey on the rocks, you might get a case or two to solve.
                    - To have the best chance to solve your case, follow these directions: -

                    -

                      -
                    1. Go to the crime scene.
                    2. -
                    3. Take your scanner and scan EVERYTHING (Yes, the doors, the tables, even the dog).
                    4. -
                    5. Once you are reasonably certain you have every scrap of evidence you can use, find all possible entry points and scan them, too.
                    6. -
                    7. Return to your office.
                    8. -
                    9. Using your forensic scanning computer, scan your scanner to upload all of your evidence into the database.
                    10. -
                    11. Browse through the resulting dossiers, looking for the one that either has the most complete set of prints, or the most suspicious items handled.
                    12. -
                    13. If you have 80% or more of the print (The print is displayed), go to step 10, otherwise continue to step 8.
                    14. -
                    15. Look for clues from the suit fibres you found on your perpetrator, and go about looking for more evidence with this new information, scanning as you go.
                    16. -
                    17. Try to get a fingerprint card of your perpetrator, as if used in the computer, the prints will be completed on their dossier.
                    18. -
                    19. Assuming you have enough of a print to see it, grab the biggest complete piece of the print and search the security records for it.
                    20. -
                    21. Since you now have both your dossier and the name of the person, print both out as evidence and get security to nab your baddie.
                    22. -
                    23. Give yourself a pat on the back and a bottle of the ship's finest vodka, you did it!
                    24. -
                    -

                    - It really is that easy! Good luck! - - - "} - -/obj/item/weapon/book/manual/nuclear - name = "Fission Mailed: Nuclear Sabotage 101" - icon_state ="bookNuclear" - item_state = "book8" - author = "Stealth Assault Enterprises" - title = "Fission Mailed: Nuclear Sabotage 101" - - dat = {" - - - - -

                    Nuclear Explosives 101

                    - Hello and thank you for choosing Stealth Assault Enterprises for your nuclear information needs. Today's crash course will deal with the operation of a Nuclear Fission Device.

                    - - First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE. Pressing any button on the compacted bomb will cause it to extend and bolt itself into place. If this is done, to unbolt it, one must completely log in, which at this time may not be possible.
                    - -

                    To make the nuclear device functional

                    -
                      -
                    • Place the nuclear device in the designated detonation zone.
                    • -
                    • Extend and anchor the nuclear device from its interface.
                    • -
                    • Insert the nuclear authorisation disk into the slot.
                    • -
                    • Type the numeric authorisation code into the keypad. This should have been provided.
                      - Note: If you make a mistake, press R to reset the device. -
                    • Press the E button to log on to the device.
                    • -

                    - - You now have activated the device. To deactivate the buttons at anytime, for example when you've already prepped the bomb for detonation, remove the authentication disk OR press R on the keypad.

                    - Now the bomb CAN ONLY be detonated using the timer. Manual detonation is not an option. Toggle off the SAFETY.
                    - Note: You wouldn't believe how many SAARE Operatives with doctorates have forgotten this step.

                    - - So use the - - and + + to set a detonation time between 5 seconds and 10 minutes. Then press the timer toggle button to start the countdown. Now remove the authentication disk so that the buttons deactivate.
                    - Note: THE BOMB IS STILL SET AND WILL DETONATE

                    - - Now before you remove the disk, if you need to move the bomb, you can toggle off the anchor, move it, and re-anchor.

                    - - Remember the order:
                    - Disk, Code, Safety, Timer, Disk, RUN!

                    - Intelligence Analysts believe that normal corporate procedure is for the Site Manager to secure the nuclear authentication disk.

                    - - Good luck! - - - "} - -/obj/item/weapon/book/manual/atmospipes - name = "Pipes and You: Getting To Know Your Scary Tools" - icon_state = "pipingbook" - item_state = "book3" - author = "Maria Crash, Senior Atmospherics Technician" - title = "Pipes and You: Getting To Know Your Scary Tools" - dat = {" - - - - - -

                    Contents

                    -
                      -
                    1. Author's Foreword
                    2. -
                    3. Basic Piping
                    4. -
                    5. Insulated Pipes
                    6. -
                    7. Atmospherics Devices
                    8. -
                    9. Heat Exchange Systems
                    10. -
                    11. Final Checks
                    12. -

                    - -

                    HOW TO NOT SUCK QUITE SO HARD AT ATMOSPHERICS


                    - Or: What the fuck does a "pressure regulator" do?

                    - - Alright. It has come to my attention that a variety of people are unsure of what a "pipe" is and what it does. - Apparently, there is an unnatural fear of these arcane devices and their "gases." Spooky, spooky. So, - this will tell you what every device constructable by an ordinary pipe dispenser within atmospherics actually does. - You are not going to learn what to do with them to be the super best person ever, or how to play guitar with passive gates, - or something like that. Just what stuff does.

                    - - -

                    Basic Pipes

                    - The boring ones.
                    - Most ordinary pipes are pretty straightforward. They hold gas. If gas is moving in a direction for some reason, gas will flow in that direction. - That's about it. Even so, here's all of your wonderful pipe options.
                    - -
                      -
                    • Straight pipes: They're pipes. One-meter sections. Straight line. Pretty simple. Just about every pipe and device is based around this - standard one-meter size, so most things will take up as much space as one of these.
                    • -
                    • Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
                    • -
                    • Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
                    • -
                    • 4-way manifold: A four-way junction.
                    • -
                    • Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh, use them to decorate your house or something.
                    • -
                    • Manual valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
                    • -
                    • Manual T-valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


                    • -
                    - - An important note here is that pipes are now done in three distinct lines - general, supply, and scrubber. You can move gases between these with a universal adapter. Use the correct position for the correct location. - Connecting scrubbers to a supply position pipe makes you an idiot who gives everyone a difficult job. Insulated and HE pipes don't go through these positions. - -

                    Insulated Pipes

                    -
                  • Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
                  • -
                  • Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
                  • -
                  • 4-way manifold: A four-way junction.
                  • -
                  • Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh. Use them to decorate your house or something.
                  • -
                  • Manual Valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
                  • -
                  • Manual T-Valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


                  • - -

                    Insulated Pipes


                    - Special Public Service Announcement.
                    - Our regular pipes are already insulated. These are completely worthless. Punch anyone who uses them.

                    - -

                    Devices:

                    - They actually do something.
                    - This is usually where people get frightened, afraid, and start calling on their gods and/or cowering in fear. Yes, I can see you doing that right now. - Stop it. It's unbecoming. Most of these are fairly straightforward.
                    - -
                      -
                    • Gas pump: Take a wild guess. It moves gas in the direction it's pointing (marked by the red line on one end). It moves it based on pressure, the maximum output being 15000 kPa (kilopascals). - Ordinary atmospheric pressure, for comparison, is 101.3 kPa, and the minimum pressure of room-temperature pure oxygen needed to not suffocate in a matter of minutes is 16 kPa - (though 18 kPa is preferred when using internals with pure oxygen, for various reasons). A high-powered variant will move gas more quickly at the expense of consuming more power. Do not turn the distribution loop up to 15000 kPa. - You will make engiborgs cry and the Chief Engineer will beat you.
                    • -
                    • Pressure regulator: These replaced the old passive gates. You can choose to regulate pressure by input or output, and regulate flow rate. Regulating by input means that when input pressure is above the limit, gas will flow. - Regulating by output means that when pressure is below the limit, gas will flow. Flow rate can be controlled.
                    • -
                    • Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
                    • -
                    • Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon." Activating a panic siphon without very good reason will kill someone. Don't do it.
                    • -
                    • Meter: A little box with some gauges and numbers. Fasten it to any pipe or manifold and it'll read you the pressure in it. Very useful.
                    • -
                    • Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2," for reference, on non-mirrored mixers.. - Output is controlled by flow rate. There is also an "omni" variant that allows you to set input and output sections freely..
                    • -
                    • Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe, - the rest will continue out the other side. Can also output from 0-4500 kPa. The "omni" vairant allows you to set input and output sections freely.
                    • -
                    - -

                    Heat Exchange Systems

                    - Will not set you on fire.
                    - These systems are used to only transfer heat between two pipes. They will not move gases or any other element, but will equalize the temperature (eventually). Note that because of how gases work (remember: pv=nRt), - a higher temperature will raise pressure, and a lower one will lower temperature.
                    - -
                  • Pipe: This is a pipe that will exchange heat with the surrounding atmosphere. Place in fire for superheating. Place in space for supercooling.
                  • -
                  • Bent pipe: Take a wild guess.
                  • -
                  • Junction: The point where you connect your normal pipes to heat exchange pipes. Not necessary for heat exchangers, but necessary for H/E pipes/bent pipes.
                  • -
                  • Heat exchanger: These funky-looking bits attach to an open pipe end. Put another heat exchanger directly across from it, and you can transfer heat across two pipes without having to have the gases touch. - This normally shouldn't exchange with the ambient air, despite being totally exposed. Just don't ask questions.

                  • - - That's about it for pipes. Go forth, armed with this knowledge, and try not to break, burn down, or kill anything. Please. - - - - - "} - -/obj/item/weapon/book/manual/evaguide - name = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition" - icon_state = "evabook" - item_state = "book14" - author = "Maria Crash, Senior Atmospherics Technician" - title = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition" - dat = {" - - - - - -

                    EVA Gear and You: Not Spending All Day Inside, 2nd Edition

                    - Or: How not to suffocate because there's a hole in your shoes
                    - -

                    Contents

                    -
                      -
                    1. A foreword on using EVA gear
                    2. -
                    3. Use of Softsuits
                    4. -
                    5. Putting on a Voidsuit
                    6. -
                    7. Operation of Hardsuits
                    8. -
                    9. Cyclers and Other Modification Equipment
                    10. -
                    11. Miscellaneous Advice
                    12. -
                    13. Final Checks
                    14. -
                    -
                    - - EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training, - but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into three main sections: - softsuits, voidsuits, and hardsuits. General advice and instructions for modification are present as well.

                    - - One important point for synthetics and people using full-body prosthetics: You obviously don't need oxygen, but you do run the risk of overheating in vacuum. - Rather than oxygen, use a suit cooling unit. Many emergency equipment stores don't hold them, unfortunately, but dedicated EVA stores will. Be aware of your heat tolerances.

                    - -

                    Softsuits and Emergency Equipment

                    - The bulkiest things this side of Alpha Centauri
                    - These suits are the both grey ones that are stored in EVA and orange emergency suits in emergency lockers. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiation or physical impact. - As Medical, Engineering, Security, and Mining all have voidsuits of their own, these don't see much use outside of emergencies. In an emergency, knowing how to put one on can save your life.

                    - - First, take the suit. It should be in three pieces: A top, a bottom, and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however, - put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit, - but to an extent they will adjust to you. You can then find the snaps and seals around the waist, where the two pieces meet. Fasten these, and double-check their tightness. - The red indicators around the waist of the lower half will turn green when this is done correctly. Next, put on whatever breathing apparatus you're using, be it a gas mask or a breath mask. Make sure the oxygen tube is fastened into it. - Put on the helmet now, straightforward, and make sure the tube goes into the small opening specifically for internals. Again, fasten seals around the neck, a small indicator light in the inside of the helmet should go from red to off when all is fastened. - There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit, - but it is recommended to have a full-sized tank on your back for EVA.

                    - - Important note: When using these, especially in emergencies, be aware of your surroundings! These suits can tear or breach more easily than any other type, especially in an environment with broken glass and metal everywhere. - If your suit is breached, you will be in deep trouble. Pressure issues can inhibit breathing even with internals.

                    - - These suits tend to be wearable by most species. They're large and flexible. They might be pretty uncomfortable for some, though, so keep that in mind.

                    - -

                    Voidsuits

                    - Heavy, uncomfortable, still the best option.
                    - These suits come in many specialized varieties. The most common are engineering, atmospherics, security, medical, and mining varieties. - These provide a lot more protection than the standard suits, and depending on the specialization, can offer different protections. - For example, security suits have armor plating, engineering suits have radiation protection, and atmospherics suits are rated for extremely high temperatures.

                    - - Similarly to the softsuits, these are split into three parts. Fastening the pant and top are mostly the same as the softsuits, with the exception that these are a bit heavier, - though not as bulky. The helmet goes on differently, with the air tube feeding into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise, - and then is turned to face the front and sealed. There is a small button on the right side of the helmet that activates the helmet light. - The tanks that fasten onto the side slot are emergency tanks, as well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.

                    - - These suits generally only fit one species. NanoTrasen's are usually human-fitting by default, but there's equipment that can make modifications to the hardsuits to fit them to other species.

                    - - Later-model voidsuits can have magboots and helmets installed into the suit and deployed when needed. Check the operator's manual for individual suits to see how the helmets are installed. - If a helmet is installed, you can skip it while putting the suit on, obviously. When deployed, it will deploy from the back of your neck, covering the head and sealing at the front.

                    - -

                    Hardsuits/Rigs

                    - The fancy stuff.
                    - Proper hardsuits are the most complex sort of EVA equipment available, and blur the line between spacesuits and smaller exosuits. They're sometimes known as 'rigs' or 'powered armor'. - These are the suits with the widest variety of uses, owing to the wide variety of equipment that can be installed on them. Like voidsuits, they come in different, specialized varieties, each one offering different protections and different equipment. - Equipment that can be installed includes weapons, power tools, mining equipment, medical equipment, AI assistants, and more.

                    - - Putting these on is relatively simple. They come as compact packs, worn like a backpack and secured with a harness. Activating them, though, is a more complex process. The suit deploys from the module similarly to helmets deploying from voidsuits. - After it covers the whole body, the suit can be started. The startup sequence takes some time. The suit will automatically fit itself to your body, sealing each section individually - boots, gloves, pants, torso, and helmet - then connecting them.

                    - - Operating a hardsuit is a much more complicated proposal than operating other EVA equipment. While putting them on is relatively simple, and operating basic functions like oxygen and magboots is the same as other suits, the rest is far more complex. - Consult the operator's manual for invidual pieces of equipment that you plan to use. Use of these for heavy work is only reccomended for people who have specialized training and extensive EVA experience. - The potential of a suit breach is always there, and the use of powered equipment raises it significantly.

                    - -

                    Miscellaneous Advice

                    - Pro tip: Safety first.
                    - There's a lot of general advice that can be helpful for people who haven't taken a long-form instruction course. Much of this is going to be fairly obvious safety advice, but it's never bad to remind yourself of that.

                    - -
                      -
                    • Magboots are important. They can be the difference between keeping your footing and needing a rescue team. A tie-off or a jetpack can substitute if necessary.
                    • -
                    • Be aware of breach hazards, especially in softsuits. Loss of suit pressure can be a fatal disaster.
                    • -
                    • Keep an eye on your internals. Having to make two trips outside is better than running out of air.
                    • -
                    • Similarly, keep an eye on the battery status of cooling units and other equipment.
                    • -
                    • In vacuum, sound doesn't carry. Use a radio or sign language for communication.


                    • -
                    - -

                    Modification Equipment

                    - How to actually make voidsuits fit you.
                    - There's a variety of equipment that can modify hardsuits to fit species that can't fit into them, making life quite a bit easier.

                    - - The first piece of equipment is a suit cycler. This is a large machine resembling the storage pods that are in place in some places. These are machines that will automatically tailor a suit to certain specifications. - The largest uses of them are for their cleaning functions and their ability to tailor suits for a species. Do not enter them physically. You will die from any of the functions being activated, and it will hurt the whole time you're dying. - These machines can both tailor a suit between species, and between types. This means you can convert engineering hardsuits to atmospherics, or the other way. This is useful. Use it if you can.

                    - - There's also modification kits that let you modify suits yourself. These are extremely difficult to use unless you understand the actual construction of the suit. I do not reccomend using them unless no other option is available.

                    - -

                    Final Checks

                    -
                      -
                    • Are all seals fastened correctly?
                    • -
                    • If you have modified it manually, is absolutely everything sealed perfectly?
                    • -
                    • Do you either have shoes on under the suit, or magnetic boots on over it?
                    • -
                    • Do you have internals connected and activated?
                    • -
                    • Do you have a way to communicate with the station in case something goes wrong?
                    • -
                    • Do you have a second person watching if this is a training session?

                    • -
                    - - If you don't have any further issues, go out and do whatever is necessary. - - - - "} - -/obj/item/weapon/book/manual/virology - name = "Sneezes and Coughs: A Guide To Virology, 1st Edition" - icon_state = "bookvirology" - item_state = "book5" - author = "James Simpson, Chief Virologist" - title = "Sneezes and Coughs: A Guide To Virology, 1st Edition" - dat = {" - - - - -

                    A Guide to Virology, 1st Edition

                    - Welcome to virology!

                    - - A virologist spends much of their time isolated in the virology department fiddling with deadly and annoying plagues. This guide will get you on the right path to properly
                    - creating and curing the worst of the worst.

                    - -

                    Anatomy of a Virus

                    - Before you jump in to making the next Black Death, you need to understand what's what. When you pop a tray into the disease analyzer you'll get a paper listing quite a few
                    - things about that virus!

                    - - Antigens
                    - The first thing you might notice on this analysis is the listed antigens of a virus. This, essentially, is what you need for cures. When a patient develops an antibody that
                    - matches a disease's antigen, they'll be immune to the virus.

                    - - Transmission
                    - This will list one of three methods of transmission: airborne, contact, or blood. Airborne viruses will spread through the air, meaning if a patient is coughing or sneezing
                    - it will spread quicker. Contactviruses need some sort of, well, contact. Blood on the floor, mucus, and vomit should all be cleaned up. Blood-transmission viruses will only
                    - spread through actual blood contact, meaning an injection of the virus would be necessary to spread it.

                    - - Species
                    - Not all viruses are compatible with all species! This will list off what can be infected by a particular virus. This can be changed through splicing. In rare cases you will
                    - get a virus that has infected crewmembers but cannot infect your lab monkeys. In these cases, you will likely have to splice different species data to safely make a cure.

                    - - Symptoms
                    - This lists what all the virus does, along with the strength of the symptoms and their aggressiveness. A full list of symptoms and their stages can be found below.

                    - -

                    Protection

                    - First off, any good virologist needs to gear up!

                    - Your virology lab should have the following: -
                      -
                    • Two (2) Level-3 Bio Hoods
                    • -
                    • Two (2) Level-3 Bio Suits
                    • -
                    • Two (2) sets of sealed internals
                    • -
                    • One (1) box of sterile masks
                    • -
                    • One (1) box of latex gloves
                    • -

                    - Make sure to gear up appropriately and take precautions to not bring the virus out of the lab!

                    - What needs to be worn varies from race to race, as some species cannot be affected by a virus, but
                    - precautions should always be taken to prevent accidentally carrying the virus out of containment.

                    - -

                    Machinery

                    - These are the types of machines that you will be expected to use in this department. Study them, know them, and make sure you use them correctly!

                    - - Antibody Scanner
                    - A device similar to a health analyzer, when scanned over a patient it will tell you any present antibodies in their bloodstream.

                    - - Pathogenic Isolator
                    - This device can be loaded with blood samples via a syringe. It will tell you if there is a present viral pathogen, and if so it can isolate the pathogen,
                    - producing a virus dish for further research. This is the primary means of creating virus dishes.

                    - - Pathogenic Incubator
                    - One of the most important machines, you'll be sitting here a lot. Virus trays you produce will start small, and you will need to grow them to get anywhere.
                    - Load it with virus food (diluted milk, found in a wall mounted dispenser nearby) as well as a virus dish and turn it on. It will slowly grow the virus
                    - for analysis, splicing, or whatever other use you might find for it. It can also inject the virus into other chemicals (i.e. blood) which will allow you
                    - to artificially infect a blood sample to infect test subjects.

                    - - Additionally, it can irradiate a virus dish to cause mutation. This randomly alters the syndromes, allowing you to collect new data.

                    - - Disease Analyser
                    - Virus dishes may be placed into this machine after it has been incubated sufficiently. It will create a reading of the virus' symptoms, species targeted,
                    - associated antibodies, and so on. It will also update the disease into the digital database, allowing it to be detected via Medical HUD or health analyzer.

                    - - Isolation Centrifuge
                    - Inserting a vial of blood into this machine will allow it to take a present pathogen or antibody, turning it into a pure virus or pure cure for your use.
                    - It will automatically detect if either are in the blood sample, so it is useful for determining if a sample is infected. The sample will be reusable if creating
                    - more of the virus, but will be expended upon creating antibodies.

                    - - Disease Splicer
                    - As it's name implied, this machine is for splicing the disease symptoms and creating new viruses. Insert an analyzed virus sample to take a symptom which can
                    - then be saved on a GNA disk (created by the machine), or use an existing disk on the machine to load up a symptom and splice it in.

                    - -

                    Syndromes

                    - While curing patients is the obvious side to virology, there's more you can do! For a variety of reasons you may wish to engineer your own virus through mutations
                    - and splicing. Keep in mind that infecting the whole station with a deadly virus is generally frowned upon in most societies, and even in an antagonistic role simply
                    - infecting everyone with a very deadly virus is not ideal.

                    - - Obtaining a Sample
                    - The first step to making your own virus is to find a virus to start tinkering with. There are two ways of doing this: grabbing the pre-spawned virus dish from the
                    - freezer create in your laboratory, or taking a blood sample from an infected patient that you haven't cured yet and running it through the Pathogenic Isolator to
                    - produce a virus dish.

                    - - In either case, you'll then need to incubate the virus in the Pathogenic Incubator until it is of sufficient size for you to work with, and ideally also run it
                    - through the Disease Analyzer to get an idea of what it does. This will also keep the rest of Medical from getting on your case about not giving them the ability to
                    - detect viral outbreaks without you, and will help prevent public lynchings if someone sneezes.

                    - - Once that's all done, you're ready to start the real work.

                    - - Obtaining a Sample
                    - Sometimes you just need to infect somebody. Usually, this is because you're doing some splicing work and want to make sure that you'll have some copies of a virus
                    - in reserve for future study. Fortunately, the process of deliberately infecting patients, as well as creating backups of your viruses, is actually very simple.

                    - - If you have a virus dish and wish to infect a patient, place the dish into the Pathogenic Incubator along with a beaker full of blood. Ideally, this is the
                    - patient's (or test monkey's) blood, though anyone's will do in a pinch - just be aware of the possibility of blood rejection, and be ready to deal with it accordingly.
                    - The incubator can inject a sample of the virus into the blood, which you can then inject into the patient to infect them. Alternatively, once a blood sample is infected
                    - you may place it in either the Pathogenic Isolator or Isolation Centrifuge to make as many samples of that virus you need without destroying the sample.
                    - No infections necessary!

                    - - Splicing
                    - To understand splicing, you must know that every virus has 4 GNA strands, each of which is tied to one of the syndromes that it can manifest. Each syndrome, and thus each
                    - strand, is ranked in order of its appearance when symptoms begin to manifest in a patient, and higher-ranked (and thus slower to appear) syndromes are likely to be more
                    - powerful than lower-ranked ones. (1) is the lowest, first symptom to manifest, and (4) is the highest, final symptom to manifest.

                    - - The Disease Splicer will allow you to examine and manipulate a virus' GNA strands to create a virus with any syndromes that you want.

                    - - The splicer has main three functions which allow you to modify viruses and define which GNA strands they contain. The first of these functions is the splicing function,
                    - which copies a GNA strand from an inserted virus tray to the disease splicer buffer. This is done by selecting the desired GNA strand under "Reverse Engineering".
                    - Doing so will destroy the virus tray in the process, so be sure that you have more samples of the virus on hand if you want to preserve it.

                    - - The disease splicer’s second function is to copy whatever strand it has stored in its buffer to a disk the machine creates. These disks can be re-inserted into
                    - the disease splicer later, making their stored GNA strand immediately available for use. Unlike virus trays, disks can be reused without destroying them.

                    - - The final function of the disease splicer is to copy over a GNA strand of its loaded virus dish with the corresponding GNA strand that is currently stored in
                    - its buffer. This effectively allows you to give a virus any set of symptoms you want, so long as it follows the rules of the ranking. A rank (1) symptom can fill
                    - any slot, while a (3) can only fill 3 and up, and a (4) can only be in the fourth slot.

                    - - Mutating
                    - Sometimes you just don't have any of the symptoms you want. Fortunately, mutating a virus dish is very simple. Simply place a dish in the Pathogenic Incubator
                    - to get started and click 'add radiation'. This will fill up the radiation bar with each press. When the machine is on, this bar will slowly tick down as the radiation
                    - is applied to the sample. For any significant mutation the radiation should be over 500k, meaning at least half the bar. When a mutation occurs -- and multiple
                    - mutations can occur in a single sample -- there will be a ping from the machine. Once it's done growing and mutating, throw it into the analyzer and see what you got!

                    - - With these functions in mind, the process in order for you to develop your own virus is as follows: -
                      -
                    1. Create lots of virus sample dishes.
                    2. -
                    3. If the virus dishes you have already created don't contain the syndromes that you want in
                      - your custom virus, irradiate them in the Pathogenic Incubator until they do.
                    4. -
                    5. Create backup dishes of your new viruses.
                    6. -
                    7. Strip the GNA strands that you want from your virus dishes, saving each one onto a disk.
                    8. -
                    9. Load a new virus dish into the Disease Splicer.
                    10. -
                    11. Copy over the virus dish's GNA strands with your disks.
                    12. -
                    13. Once you have your final product, you can use the Pathogenic Incubator to create a beaker
                      - of blood containing the virus. Congratulations! You are now officially a bioweapon
                      - manufacturer.
                    14. -
                    - -

                    Curing

                    - Curing a virus is relatively simple, but the below will leave you without a sample to work with. If this is a concern, see 'obtaining a sample' above. Alternatively to
                    - the below, spacecillin can be used in a pinch to attempt to treat a virus if no virologist is present and someone decided to roll around in mucus. However this is not
                    - guaranteed to work, and can in fact simply make the virus more resistant to spacecillin.

                    - - Quarantine
                    - If you or a crew member becomes infected by a virus, your first concern should be to isolate them as quickly as possible to prevent the disease from spreading. Ideally,
                    - this means getting the patient back to Virology and into a holding cell, but failing that, it's important to get them into an isolated room, shut the door, and keep anyone
                    - else from entering. It is also important to clean up any blood, vomit, or mucus left behind by the patient, as it may spread the virus as well.

                    - - Antibodies
                    - Now that your patient is safely hidden away where they can't infect people without your permission, you want to cure them. The basic idea behind that is to get them to start
                    - synthesizing antibodies.

                    - - Antibodies are produced by living organisms that have started to fight off a virus in their bloodstream. An organism that has produced antibodies is effectively
                    - immune to any virus that is weak to those antibodies, which are generally denoted by a short string of letters (say, "KM" or similar) to let you know which viruses they
                    - are effective against. Even better, once an organism has synthesized antibodies for itself, you can take a blood sample and use that to create more antibodies, which you
                    - can then inject into other people to cure the virus in them, or even to prevent them from catching it in the first place. And you can take blood samples from those people
                    - to make even more antibodies.

                    - - The issue is that, most of the time, a patient's immune system is not powerful enough to create antibodies on its own (space plagues are nasty things), so you have to do that
                    - for them. You do that by injecting them with Radium.

                    - - Since Radium is radioactive, this is obviously not something that you want to do to your actual patients. It'll cause major toxin damage when an antibody is
                    - made and quickly lead to organ failure. You get around this by using monkeys, which you can get from the box of monkey cubes very handily left on the desk in Virology for
                    - this very purpose.

                    - - So the process of curing diseases is, really, quite simple. Find an infected patient. Make a monkey (ideally, strap this monkey to a bed in an isolation cell to make the
                    - rest of this process easier). Take a blood sample from the infected patient, then inject it into the monkey to infect the monkey. Inject the monkey with Radium. Use the antibody
                    - scanner on the monkey every few seconds until it says that the monkey has begun producing antibodies, then take a blood sample (or two or three or four) from the
                    - monkey. Put the blood sample(s) into a vial, then put the vial into the isolation centrifuge and isolate the antibodies. Voila: a bottle of virus cure. Inject it into as many
                    - people as you like, then throw the (likely dead) monkey into the disposals chute to eject its disease-ridden body into space and clean the isolation cell. Or, if you're on
                    - the ball and wish to be conservative with your limited monkey cubes, save the monkey, too.

                    - - Congratulations! You have now cured a disease.

                    - - The important thing to remember is that anyone injected with antibodies becomes a source of those antibodies for the future. You can take a sample of their blood
                    - and put it into the isolation centrifuge just like you did with the monkey's to produce more antibodies if you ever run out. For this reason, and because you might have
                    - gotten infected yourself in the process of synthesizing a cure, most Virologists will inject themselves with any antibodies they produce.

                    - -

                    Syndromes

                    - Below are the possible syndromes of a virus and the stages they can appear at. A stage covers that stage and higher, so a stage 1 syndrome can appear at stage 4.
                    - A stage 4 syndrome, however, cannot appear any earlier than stage 4.

                    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    Syndromes
                    Stage Name Effects -
                    1 Sneezing Makes the patient sneeze. Spreads airborne disease! -
                    1 Mucus Buildup Makes the patient's throat fill with mucus. -
                    1 Salivary Gland Stimulation Makes the patient drool. Spreads the disease! -
                    1 Involuntary Twitching Makes the patient twitch. -
                    1 Headache Gives the patient a headache. -
                    1 Mlemington's Syndrome Makes the patient 'mlem' uncontrollably. -
                    1 Spyndrome Makes the patient spin uncontrollably. -
                    2 Involuntary Vocalization Makes the patient scream. -
                    2 Vomiting Makes the patient vomit. Spreads the disease! -
                    2 Excessive Sleepiness Makes the patient drowsy. -
                    2 Narcolepsy Makes the patient pass out at random. -
                    2 Vision Loss Causes partial blindness. -
                    2 Severe Cough Causes coughing. Spreads airborne disease! -
                    2 Digestive Inefficiency Makes the patient always hungry. -
                    2 Reduced Circulation Makes the patient shiver. -
                    2 Hair Loss Causes the patient's hair to fall out. -
                    2 Overactive Adrenal Gland Makes the patient jittery, but also faster. -
                    2 Tinnitus Causes ringing in the patient's ears. -
                    2 Lingual Dissocation Scrambles the patients language center of their brain. -
                    3 Hyperacidity Causes small amounts of toxin damage. -
                    3 Nervous Motor Instability Makes the patient shaky. -
                    3 Pineal Gland Decalcification Gives the patient telepathy. -
                    3 Neurodegeneration Causes brain damage. -
                    3 Hallucination Causes hallucinations. -
                    3 Hearing Loss A less severe hearing damage. -
                    3 Topographical Cretinism Makes the patient incapable of walking straight. -
                    3 Uncontrolled Laughter Causes the patient to laugh uncontrollably. -
                    3 DNA Degradation Causes genetic damage. -
                    3 Phantom Aches Causes groaning. -
                    3 Chemical Synthesis Causes the synthesis of a certain chemical in the patient's bloodstream. -
                    3 Genetic Chameleonism Prevents the patient from experiencing organ rejection. -
                    3 Mass Revectoring Causes the patient to suddenly change size. -
                    3 Flipponov's Disease Causes the patient to uncontrollably do a flip. -
                    4 Gibbingtons Gibs the patients slowly. -
                    4 Radian's Syndrome Irradiates the patient from the inside. -
                    4 Deafness Causes complete deafness. -
                    4 Genome Regression Turns the patient into the primitive form of their species. -
                    4 Windpipe Contraction Causes the patient to involuntarily hold their breath. -
                    4 Autoimmune Response Adds toxins to the patient's bloodstream. -
                    4 Catastrophic DNA Degeneration Causes genetic damage and scrambles the patient's genes. -
                    4 Limb Paralysis Temporary loss of use in the patient's individual arms and legs. -
                    4 Organ Shutdown Slowly causes internal organ damage at random. -
                    4 Hyperaccelerated Aging Ages the patient, causing external damage. -
                    4 Brittle Bones Causes bones to break more easily. -
                    4 Organic Ignition Slowly causes the patient to burst into flames. -
                    -
                    - - - "} +/*********************MANUALS (BOOKS)***********************/ + +/obj/item/weapon/book/manual + icon = 'icons/obj/library.dmi' + due_date = 0 // Game time in 1/10th seconds + unique = 1 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified + + +/obj/item/weapon/book/manual/engineering_construction + name = "Station Repairs and Construction" + icon_state ="bookEngineering" + item_state = "book3" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Station Repairs and Construction" + +/obj/item/weapon/book/manual/engineering_construction/New() + ..() + dat = {" + + + + + + + + + + + "} + +/obj/item/weapon/book/manual/engineering_particle_accelerator + name = "Particle Accelerator User's Guide" + icon_state ="bookParticleAccelerator" + item_state = "book15" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Particle Accelerator User's Guide" + +/obj/item/weapon/book/manual/engineering_particle_accelerator/New() + ..() + dat = {" + + + + + +

                    Experienced User's Guide

                    + +

                    Setting up the accelerator

                    + +
                      +
                    1. Wrench all pieces to the floor
                    2. +
                    3. Add wires to all the pieces
                    4. +
                    5. Close all the panels with your screwdriver
                    6. +
                    + +

                    Using the accelerator

                    + +
                      +
                    1. Open the control panel
                    2. +
                    3. Set the speed to 2
                    4. +
                    5. Start firing at the singularity generator
                    6. +
                    7. When the singularity reaches a large enough size so it starts moving on it's own set the speed down to 0, but don't shut it off
                    8. +
                    9. Remember to wear a radiation suit when working with this machine... we did tell you that at the start, right?
                    10. +
                    + + + + "} + + +/obj/item/weapon/book/manual/supermatter_engine + name = "Supermatter Engine Operating Manual" + icon_state = "bookSupermatter" + item_state = "book15" + author = "Central Engineering Division" + title = "Supermatter Engine Operating Manual" + +/obj/item/weapon/book/manual/supermatter_engine/New() + ..() + dat = {" + + + + +

                    OPERATING MANUAL FOR MK 1 PROTOTYPE THERMOELECTRIC SUPERMATTER ENGINE 'TOMBOLA'

                    +
                    +

                    OPERATING PRINCIPLES

                    +
                    +
                  • The supermatter crystal serves as the fundamental power source of the engine. Upon being charged, it begins to emit large amounts of heat and radiation, as well and oxygen and phoron gas. As oxygen accelerates the reaction and reacts with phoron to start a fire, it must be filtered out. It's recommended to filter out all gases besides phoron for standard operation.
                  • +
                    +
                  • Gas in the reactor chamber housing the supermatter is circulated through the reactor loop, which passes through the filters and thermoelectric generators. The thermoelectric generators transfer heat from the reactor loop to the colder radiator loop, thereby generating power. Additional power is generated from internal turbines in the circulators.
                  • +
                    +
                  • Gas in the radiator loop is circulated through the radiator bank, located in space. This rapidly cools the air, preserving the temperature differential needed for power generation.
                  • +
                    +
                  • The MK 1 Prototype Thermoelectric Supermatter Engine is designed to operate at reactor temperatures of 3000K to 4000K and generate up to 1MW of power. Beyond 1MW, the thermoelectric generators will begin to lose power through electrical discharge, reducing efficiency, but additional power generation remains feasible.
                  • +
                    +
                  • The crystal structure of the supermatter will begin to liquefy if its temperature exceeds 5000K. This eventually results in a massive release of light, heat and radiation, disintegration of both the supermatter crystal and most of the surrounding area, and as as-of-yet poorly documented psychological effects on all animals within a 2km radius. Appropriate action should be taken to stabilize or eject the supermatter before such occurs.
                  • +
                    +

                    SUPERMATTER HANDLING

                    +
                  • Do not expose supermatter to oxygen.
                  • +
                  • Do not allow supermatter to contact any solid object apart from specially-designed supporting pallet.
                  • +
                  • Do not directly view supermatter without meson goggles.
                  • +
                  • While handles on pallet allow moving the supermatter via pulling, pushing should not be attempted.
                  • +
                  • Note that prosthetics do not protect against radiation or viewing the supermatter.
                  • +
                    +

                    STANDARD STARTUP PROCEDURE

                    +
                      +
                    1. Fill reactor loop and radiator loop with two (2) standard canisters (1871.75 moles, 20 celsius) of phoron gas each.
                    2. +
                    3. Fill the waste handling radiator loop with one (1) standard canister (1871.75 as well) of carbon dioxide gas.
                    4. +
                    5. You can confirm your canisters got enough gas in them by using your PDA's "Gas Scanner" feature, you must enable it and press your PDA against the canister.
                    6. +
                    7. Enable both the high power gas pumps near the thermo-electric generators and maximize the desired output.
                    8. +
                    9. Enable both the omni-filters and ensure they are set to filter phoron back into the system.
                    10. +
                    11. Enable the gas pump from the filters to waste handling and maximize the desired output.
                    12. +
                    13. Close the monitoring room blast doors and open the reactor blast doors,
                    14. +
                    15. Fire 12-16 pulses from emitter at supermatter crystal. The expected power output is around a megawatt. NOTE: It will take a few minutes to heat up.
                    16. +
                    17. You can confirm desired power levels by checking the computer in the minotoring room, "Supermatter Monitoring". You want 300 EPR, more is safe but no higher than 700
                    18. +
                    19. Close the reactor blast doors and keep the monitoring room blast doors closed to prevent radiation leaking.
                    20. +
                    +
                    +

                    OPERATION AND MAINTENANCE

                    +
                      +
                    1. Ensure that radiation protection and meson goggles are worn at all times while working in the engine room.
                    2. +
                    3. Ensure that reactor and radiator loops are undamaged and unobstructed.
                    4. +
                    5. Ensure that, in a standard setup, only phoron is being filtered back into the system. Do not allow exhaust pressure to exceed 4500 kPa.
                    6. +
                    7. Ensure that engine room Area Power Controller (APC) and engine Superconducting Magnetic Energy Storage unit (SMES) are properly charged.
                    8. +
                    9. Ensure that reactor temperature does not exceed 5000K. In event of reactor temperature exceeding 5000K, see EMERGENCY COOLING PROCEDURE.
                    10. +
                    11. In event of imminent and/or unavoidable delamination, see EJECTION PROCEDURE.
                    12. +
                    +
                    +

                    EMERGENCY COOLING PROCEDURE

                    +
                      +
                    1. Open Emergency Cooling Valve 1 and Emergency Cooling Valve 2.
                    2. +
                    3. When reactor temperature returns to safe operating levels, close Emergency Cooling Valve 1 and Emergency Cooling Valve 2.
                    4. +
                    5. Adding additional gas to the loops can have a positive effect in reducing reactor temperature.
                    6. +
                    7. If reactor temperature does not return to safe operating levels, see EJECTION PROCEDURE.
                    8. +
                    +
                    +

                    EJECTION PROCEDURE

                    +
                      +
                    1. Ensure the engine room has power. The blast doors and ejection platform are unresponsive without power.
                    2. +
                    3. Press Engine Ventilatory Control button to open engine core blast door to space.
                    4. +
                    5. Press Emergency Core Eject button to eject supermatter crystal. NOTE: Attempting crystal ejection while engine core vent is closed will result in ejection failure.
                    6. +
                    7. In event of ejection failure, pending
                    8. +
                    + + "} + +// TESLA Engine + +/obj/item/weapon/book/manual/tesla_engine + name = "Tesla Operating Manual" + icon_state ="bookTesla" + item_state = "book15" + author = "Engineering Encyclopedia" + title = "Tesla Engine User's Guide" + dat = {" + + + + +

                    OPERATING MANUAL FOR MK 2 PROTOTYPE TESLA ENGINE 'EDISON'S BANE'

                    +
                    +

                    OPERATING PRINCIPLES

                    +

                    This big floaty ball of pure electricity can only be contained by the containment field. It periodically will discharge energy in the form of an electric shock which can be harvested for energy.

                    +

                    When you shoot the energy ball with the Particle Accelerator, it gains energy, and when enough energy is accumulated a mini-energy ball that orbits the big energy ball will be formed. This can happen as many times as you let it, each mini-ball will send off an extra shock when the energy ball pulses. Be warned, the more mini-balls the energy ball has, the more shocks it sends out at once and the further it can travel each move.

                    +

                    An energy ball will shoot bolts of electricity off at conductors, which it prioritizes in this order: +

                      +
                    1. Tesla coils
                    2. +
                    3. Grounding rods
                    4. +
                    5. People and animals
                    6. +
                    7. Machines
                    8. +
                    +

                    +

                    Tesla Coils will attract the energy ball's bolts. They will take half the power of the bolt (if they are connected to a wire node), pump it into the powernet it is hooked to, and then will send the other half of the power to the next available conductor, which follows the criteria listed above. Preferably, this will be another coil to harness more of the power and pump it into the grid.

                    +

                    Grounding Rods are safety precautions to prevent the tesla bolts from hitting machinery or personnel. If the tesla is loose, being near one will usually keep you safe from direct shocks.

                    +
                    +

                    STARTUP PROCEDURE

                    +
                      +
                    1. Bolt and weld down the Field Generators, ensuring they form a complete rectangle.
                    2. +
                    3. Bolt and weld down the Emitters, ensuring their fire will strike the corner Field Generators
                    4. +
                    5. Bolt down the Tesla Generator inside the rectangle formed by the Field Generators in a spot where it will be struck by fire from the Particle Accelerator
                    6. +
                    7. Bolt down Telsa Coils and Grounding Rods
                    8. +
                    9. Activate the Emitters
                    10. +
                    11. Activate each of the Field Generators, then wait until the containment field has completely formed.
                    12. +
                    13. Setup the Particle Accelerator (see our best seller "Particle Accelerator User's Guide"!) and activate it.
                    14. +
                    15. After a short time the Telsa Generator will create an energy ball, being consumed in the process.
                    16. +
                    +
                    +

                    OPERATION AND MAINTENANCE

                    +
                      +
                    1. Ensure that electrical protection and meson goggles are worn at all times while working in the engine room.
                    2. +
                    3. Ensure that Telsa Coils and/or Grounding Rods are placed to safely collect or ground any and all shock.
                    4. +
                    5. Ensure that all Emitters remain activated and have unobstructed lines of fire to the Field Generators.
                    6. +
                    7. Do not let the Emitters run out of power.
                    8. +
                    +
                    +

                    SHUTDOWN PROCEDURE

                    +
                      +
                    1. De-activate the Particle Accelerator. The energy ball will begin to shrink and lose mini-balls.
                    2. +
                    3. When the energy ball has completely dissipated, the Emitters can be de-activated.
                    4. +
                    +
                    +

                    ENERGY BALL ESCAPE PROCEDURE

                    +
                      +
                    1. Do not let it escape.
                    2. +
                    3. Have someone ready to blame when it does escape.
                    4. +
                    5. Buy our forthcoming manual "Celebrity Grounding Rod Shelters of the Galaxy"
                    6. +
                    + + "} + +//R-UST port +/obj/item/weapon/book/manual/rust_engine + name = "R-UST Operating Manual" + icon_state = "bookSupermatter" + item_state = "book15" + author = "Cindy Crawfish" + title = "R-UST Operating Manual" + +/obj/item/weapon/book/manual/rust_engine/New() + ..() + dat = {" + + + + +

                    +
                      +
                    1. Put uranium in the portable generator near the gyrotron and turn it to full. This is to provide initial power to the core.
                    2. +
                    3. Enable and max output on the SMES in the engine room. This is to power the gyrotron.
                    4. +
                    5. Go into the control room, interact with the fusion core control console. Turn the field on and raise size to 501. Any bigger and it will start EMPing the doors. Any smaller and the fuel pellets might miss.
                    6. +
                    7. Interact with the gyrotron control computer, set power as high as the SMES can support, usually around 4, and turn it on. This will start increasing the plasma temperature to the point where reactions can occur.
                    8. +
                    9. Go into the engine room and insert a deuterium fuel assembly and a tritium fuel assembly into two of the fuel injectors. You can make deuterium rods in the fuel compressor if you want to play it safe.
                    10. +
                    11. Go back to the control room and turn the fuel injectors on. This will start firing pellets into the field.
                    12. +
                    13. Wait for reactions to start (plasma temperature will spike and fuel amounts will drop). Turn the gyrotron power down until it's keeping up with field instability. This will prevent cumulative instability from the deuterium-tritium reaction fucking up the field. If you're using straight deuterium instability isn't a problem and you can turn the gyrotron off.
                    14. +
                    15. Configure the SMES, turn the PACMAN off before it explodes.
                    16. +
                    +
                    + NOTES FOR NEWBIES +
                    + Anything touching the field will mess with its stability and eventually cause it to rupture. Rupturing is bad. Use the gyrotron to keep instability down if you're running the engine on unstable fuel. +

                    + Likewise, no matter how sad the core seems, don't fucking hug it, you'll blow the field out and set the engine room on fire. + + "} + +/obj/item/weapon/book/manual/engineering_hacking + name = "Hacking" + icon_state ="bookHacking" + item_state = "book2" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Hacking" + +/obj/item/weapon/book/manual/engineering_hacking/New() + ..() + dat = {" + + + + + + + + + + + "} + + +/obj/item/weapon/book/manual/engineering_singularity_safety + name = "Singularity Safety in Special Circumstances" + icon_state ="bookEngineeringSingularitySafety" + item_state = "book15" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Singularity Safety in Special Circumstances" + + dat = {" + + + + +

                    Singularity Safety in Special Circumstances

                    + +

                    Power outage

                    + + A power problem has made the entire station lose power? Could be station-wide wiring problems or illegal power sinks. In any case follow these steps: + +
                      +
                    1. PANIC!
                    2. +
                    3. Get your ass over to engineering! QUICKLY!!!
                    4. +
                    5. Get to the Area Power Controller which controls the power to the emitters.
                    6. +
                    7. Swipe it with your ID card - if it doesn't unlock, continue with step 15.
                    8. +
                    9. Open the console and disengage the cover lock.
                    10. +
                    11. Pry open the APC with a Crowbar.
                    12. +
                    13. Take out the empty power cell.
                    14. +
                    15. Put in the new, full power cell - if you don't have one, continue with step 15.
                    16. +
                    17. Quickly put on a Radiation suit.
                    18. +
                    19. Check if the singularity field generators withstood the down-time - if they didn't, continue with step 15.
                    20. +
                    21. Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering APC is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the APC to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
                    22. +
                    23. Grab your crowbar and pry away the tile closest to the APC.
                    24. +
                    25. Use the wirecutters to cut the wire which is connecting the grid to the terminal.
                    26. +
                    27. Go to the bar and tell the guys how you saved them all. Stop reading this guide here.
                    28. +
                    29. GET THE FUCK OUT OF THERE!!!
                    30. +
                    + +

                    Shields get damaged

                    + +
                      +
                    1. GET THE FUCK OUT OF THERE!!! FORGET THE WOMEN AND CHILDREN, SAVE YOURSELF!!!
                    2. +
                    + + + "} + + +/obj/item/weapon/book/manual/hydroponics_pod_people + name = "The Diona Harvest - From Seed to Market" + icon_state ="bookHydroponicsPodPeople" + item_state = "book5" + author = "Farmer John" + title = "The Diona Harvest - From Seed to Market" + + dat = {" + + + + +

                    Growing a Diona

                    + + Growing a Diona is easy! +

                    +

                      +
                    1. Take a syringe of blood from the body you wish to turn into a Diona.
                    2. +
                    3. Inject 5 units of blood into the pack of dionaea-replicant seeds.
                    4. +
                    5. Plant the seeds.
                    6. +
                    7. Tend to the plants water and nutrition levels until it is time to harvest the Diona.
                    8. +
                    +

                    + Note that for a successful harvest, the body from which the blood was taken from must be dead BEFORE harvesting the pod, however the pod can be growing while they are still alive. Otherwise, the soul would not be able to migrate to the new Diona body.

                    + + It really is that easy! Good luck! + + + + "} + + +/obj/item/weapon/book/manual/medical_cloning + name = "Cloning Techniques of the 24th Century" + icon_state ="bookCloning" + item_state = "book9" + author = "Medical Journal, volume 3" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Cloning Techniques of the 24th Century" + + dat = {" + + + + + +

                    How to Clone People

                    + So there are 50 dead people lying on the floor, chairs are spinning like no tomorrow and you haven't the foggiest idea of what to do? Not to worry! + This guide is intended to teach you how to clone people and how to do it right, in a simple, step-by-step process! If at any point of the guide you have a mental meltdown, + genetics probably isn't for you and you should get a job-change as soon as possible before you're sued for malpractice. + +
                      +
                    1. Acquire body
                    2. +
                    3. Strip body
                    4. +
                    5. Put body in cloning machine
                    6. +
                    7. Scan body
                    8. +
                    9. Clone body
                    10. +
                    11. Get clean Structural Enzymes for the body
                    12. +
                    13. Put body in morgue
                    14. +
                    15. Await cloned body
                    16. +
                    17. Cryo and use the clean SE injector
                    18. +
                    19. Give person clothes back
                    20. +
                    21. Send person on their way
                    22. +
                    + +

                    Step 1: Acquire body

                    + This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step. + +

                    Step 2: Strip body

                    + The cloning machine does not like abiotic items. What this means is you can't clone anyone if they're wearing clothes or holding things, so take all of it off. If it's just one person, it's courteous to put their possessions in the closet. + If you have about seven people awaiting cloning, just leave the piles where they are, but don't mix them around and for God's sake don't let people in to steal them. + +

                    Step 3: Put body in cloning machine

                    + Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset. + +

                    Step 4: Scan body

                    + Go onto the computer and scan the body by pressing 'Scan - <Subject Name Here>.' If you're successful, they will be added to the records (note that this can be done at any time, even with living people, + so that they can be cloned without a body in the event that they are lying dead on port solars and didn't turn on their suit sensors)! + If not, and it says "Error: Mental interface failure.", then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, + click 'Refresh' and try scanning them again. If there's no success, threaten them with gibbing. + Still no success? Skip over to Step 7 and don't continue after it, as you have an unresponsive body and it cannot be cloned. + If you got "Error: Unable to locate valid genetic data.", you are trying to clone a monkey - start over. + +

                    Step 5: Clone body

                    + Now that the body has a record, click 'View Records,' click the subject's name, and then click 'Clone' to start the cloning process. Congratulations! You're halfway there. + Remember not to 'Eject' the cloning pod as this will kill the developing clone and you'll have to start the process again. + +

                    Step 6: Get clean SEs for body

                    + Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! + For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they're done. If you're a competent Geneticist, you will already have one ready on your working computer. + If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. + Go into View/Edit/Transfer Buffer, find an open slot and click "SE" to save it. Then click 'Injector' to get the SEs in syringe form. Put this in your pocket or something for when the body is done. + +

                    Step 7: Put body in morgue

                    + Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. + To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed "CHEF MEAT" in order to avoid confusion. + +

                    Step 8: Await cloned body

                    + Now go back to the lab and wait for your patient to be cloned. It won't be long now, I promise. + +

                    Step 9: Cryo and clean SE injector on person

                    + Has your body been cloned yet? Great! As soon as the guy pops out, grab them and stick them in cryo. Clonexadone and Cryoxadone help rebuild their genetic material. Then grab your clean SE injector and jab it in them. Once you've injected them, + they now have clean Structural Enzymes and their defects, if any, will disappear in a short while. + +

                    Step 10: Give person clothes back

                    + Obviously the person will be naked after they have been cloned. Provided you weren't an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. + No matter how cruel you are, it's simply against protocol to force your patients to walk outside naked. + +

                    Step 11: Send person on their way

                    + Give the patient one last check-over - make sure they don't still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. + Once you're done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance. + +

                    If you've gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target. + + + + "} + + +/obj/item/weapon/book/manual/ripley_build_and_repair + name = "APLU \"Ripley\" Construction and Operation Manual" + icon_state ="book" + item_state = "book" + author = "Randall Varn, Einstein Engines Senior Mechanic" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "APLU \"Ripley\" Construction and Operation Manual" + + dat = {" + + + + +

                    +
                    + Weyland-Yutani - Building Better Worlds +

                    Autonomous Power Loader Unit \"Ripley\"

                    +
                    +

                    Specifications:

                    +
                      +
                    • Class: Autonomous Power Loader
                    • +
                    • Scope: Logistics and Construction
                    • +
                    • Weight: 820kg (without operator and with empty cargo compartment)
                    • +
                    • Height: 2.5m
                    • +
                    • Width: 1.8m
                    • +
                    • Top speed: 5km/hour
                    • +
                    • Operation in vacuum/hostile environment: Possible +
                    • Airtank volume: 500 liters
                    • +
                    • Devices: +
                        +
                      • Hydraulic clamp
                      • +
                      • High-speed drill
                      • +
                      +
                    • +
                    • Propulsion device: Powercell-powered electro-hydraulic system
                    • +
                    • Powercell capacity: Varies
                    • +
                    + +

                    Construction:

                    +
                      +
                    1. Connect all exosuit parts to the chassis frame.
                    2. +
                    3. Connect all hydraulic fittings and tighten them up with a wrench.
                    4. +
                    5. Adjust the servohydraulics with a screwdriver.
                    6. +
                    7. Wire the chassis (Cable is not included).
                    8. +
                    9. Use the wirecutters to remove the excess cable if needed.
                    10. +
                    11. Install the central control module (Not included. Use supplied datadisk to create one).
                    12. +
                    13. Secure the mainboard with a screwdriver.
                    14. +
                    15. Install the peripherals control module (Not included. Use supplied datadisk to create one).
                    16. +
                    17. Secure the peripherals control module with a screwdriver.
                    18. +
                    19. Install the internal armor plating (Not included due to corporate regulations. Can be made using 5 metal sheets).
                    20. +
                    21. Secure the internal armor plating with a wrench.
                    22. +
                    23. Weld the internal armor plating to the chassis.
                    24. +
                    25. Install the external reinforced armor plating (Not included due to corporate regulations. Can be made using 5 reinforced metal sheets).
                    26. +
                    27. Secure the external reinforced armor plating with a wrench.
                    28. +
                    29. Weld the external reinforced armor plating to the chassis.
                    30. +
                    + +

                    Additional Information:

                    +
                      +
                    • The firefighting variation is made in a similar fashion.
                    • +
                    • A firesuit must be connected to the firefighter chassis for heat shielding.
                    • +
                    • Internal armor is plasteel for additional strength.
                    • +
                    • External armor must be installed in 2 parts, totalling 10 sheets.
                    • +
                    • Completed mech is more resilient against fire, and is a bit more durable overall.
                    • +
                    • The Company is determined to ensure the safety of its investments employees.
                    • +
                    + + + "} + + +/obj/item/weapon/book/manual/research_and_development + name = "Research and Development 101" + icon_state = "rdbook" + item_state = "book7" + author = "Dr. L. Ight" + title = "Research and Development 101" + + dat = {" + + + + + +

                    Science For Dummies

                    + So you want to further SCIENCE? Good man/woman/thing! However, SCIENCE is a complicated process even though it's quite easy. For the most part, it's a three step process: +
                      +
                    1. Deconstruct items in the Destructive Analyzer to advance technology or improve the design.
                    2. +
                    3. Build unlocked designs in the Protolathe and Circuit Imprinter.
                    4. +
                    5. Repeat!
                    6. +
                    + + Those are the basic steps to furthering science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter. + +

                    The R&D Console

                    + The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controlled. More on those systems in their own sections. + On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, + because if it gets damaged, you'll lose your data! + In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. + It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, + upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database.

                    + + NOTE: The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices. + +

                    Destructive Analyzer

                    + This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, + you can destroy the object to further those sciences. + Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather than further science. + If their reliability is high enough, it'll also advance their related technologies. + +

                    Circuit Imprinter

                    + This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules. + +

                    Protolathe

                    + This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid phoron, silver, gold, and diamonds along with a variety of chemicals to produce devices. + The downside is that, again, not all devices you make are 100% reliable when you first discover them. + +

                    Reliability and You

                    + As it has been stated, many devices, when they're first discovered, do not have a 100% reliability. Instead, + the reliability of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, + and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. + Once the device reaches a certain minimum reliability, you'll gain technological advancements from it. + +

                    Building a Better Machine

                    + Many machines produced from circuit boards inserted into a machine frames require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. + If you use these parts when constructing something, its attributes may be improved. + For example, if you use an advanced matter bin when constructing an autolathe (rather than a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: + Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine. + + + "} + +/obj/item/weapon/book/manual/robotics_manual + name = "Guide to Robotics" + icon_state ="evabook" + item_state = "book3" + author = "Simple Robotics" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Guide to Robotics" + +/obj/item/weapon/book/manual/robotics_manual/New() + ..() + dat = {" + + + + + + + "} + +/obj/item/weapon/book/manual/robotics_cyborgs + name = "Cyborgs for Dummies" + icon_state = "borgbook" + item_state = "book1" + author = "XISC" + title = "Cyborgs for Dummies" + + dat = {" + + + + + +

                    Cyborgs for Dummies

                    + +

                    Chapters

                    + +
                      +
                    1. Cyborg Related Equipment
                    2. +
                    3. Cyborg Modules
                    4. +
                    5. Cyborg Construction
                    6. +
                    7. Cyborg Maintenance
                    8. +
                    9. Cyborg Repairs
                    10. +
                    11. In Case of Emergency
                    12. +
                    + + +

                    Cyborg Related Equipment

                    + +

                    Exosuit Fabricator

                    + The Exosuit Fabricator is the most important piece of equipment related to cyborgs. It allows the construction of the core cyborg parts. Without these machines, cyborgs cannot be built. It seems that they may also benefit from advanced research techniques. + +

                    Cyborg Recharging Station

                    + This useful piece of equipment will suck power out of the power systems to charge a cyborg's power cell back up to full charge. + +

                    Robotics Control Console

                    + This useful piece of equipment can be used to immobilize or destroy a cyborg. A word of warning: Cyborgs are expensive pieces of equipment, do not destroy them without good reason, or the Company may see to it that it never happens again. + + +

                    Cyborg Modules

                    + When a cyborg is created it picks out of an array of modules to designate its purpose. There are 6 different cyborg modules. + +

                    Standard Cyborg

                    + The standard cyborg module is a multi-purpose cyborg. It is equipped with various modules, allowing it to do basic tasks.
                    A Standard Cyborg comes with: +
                      +
                    • Crowbar
                    • +
                    • Stun Baton
                    • +
                    • Health Analyzer
                    • +
                    • Fire Extinguisher
                    • +
                    + +

                    Engineering Cyborg

                    + The Engineering cyborg module comes equipped with various engineering-related tools to help with engineering-related tasks.
                    An Engineering Cyborg comes with: +
                      +
                    • A basic set of engineering tools
                    • +
                    • Metal Synthesizer
                    • +
                    • Reinforced Glass Synthesizer
                    • +
                    • An RCD
                    • +
                    • Wire Synthesizer
                    • +
                    • Fire Extinguisher
                    • +
                    • Built-in Optical Meson Scanners
                    • +
                    + +

                    Mining Cyborg

                    + The Mining Cyborg module comes equipped with the latest in mining equipment. They are efficient at mining due to no need for oxygen, but their power cells limit their time in the mines.
                    A Mining Cyborg comes with: +
                      +
                    • Jackhammer
                    • +
                    • Shovel
                    • +
                    • Mining Satchel
                    • +
                    • Built-in Optical Meson Scanners
                    • +
                    + +

                    Security Cyborg

                    + The Security Cyborg module is equipped with effective security measures used to apprehend and arrest criminals without harming them a bit.
                    A Security Cyborg comes with: +
                      +
                    • Stun Baton
                    • +
                    • Handcuffs
                    • +
                    • Taser
                    • +
                    + +

                    Janitor Cyborg

                    + The Janitor Cyborg module is equipped with various cleaning-facilitating devices.
                    A Janitor Cyborg comes with: +
                      +
                    • Mop
                    • +
                    • Hand Bucket
                    • +
                    • Cleaning Spray Synthesizer and Spray Nozzle
                    • +
                    + +

                    Service Cyborg

                    + The service cyborg module comes ready to serve your human needs. It includes various entertainment and refreshment devices. Occasionally some service cyborgs may have been referred to as "Bros."
                    A Service Cyborg comes with: +
                      +
                    • Shaker
                    • +
                    • Industrial Dropper
                    • +
                    • Platter
                    • +
                    • Beer Synthesizer
                    • +
                    • Zippo Lighter
                    • +
                    • Rapid-Service-Fabricator (Produces various entertainment and refreshment objects)
                    • +
                    • Pen
                    • +
                    + +

                    Cyborg Construction

                    + Cyborg construction is a rather easy process, requiring a decent amount of metal and a few other supplies.
                    The required materials to make a cyborg are: +
                      +
                    • Metal
                    • +
                    • Two Flashes
                    • +
                    • One Power Cell (Preferably rated to 15000w)
                    • +
                    • Some electrical wires
                    • +
                    • One Human Brain
                    • +
                    • One Man-Machine Interface
                    • +
                    + Once you have acquired the materials, you can start on construction of your cyborg.
                    To construct a cyborg, follow the steps below: +
                      +
                    1. Start the Exosuit Fabricators constructing all of the cyborg parts
                    2. +
                    3. While the parts are being constructed, take your human brain, and place it inside the Man-Machine Interface
                    4. +
                    5. Once you have a Robot Head, place your two flashes inside the eye sockets
                    6. +
                    7. Once you have your Robot Chest, wire the Robot chest, then insert the power cell
                    8. +
                    9. Attach all of the Robot parts to the Robot frame
                    10. +
                    11. Insert the Man-Machine Interface (With the Brain inside) into the Robot Body
                    12. +
                    13. Congratulations! You have a new cyborg!
                    14. +
                    + +

                    Cyborg Maintenance

                    + Occasionally Cyborgs may require maintenance of a couple types, this could include replacing a power cell with a charged one, or possibly maintaining the cyborg's internal wiring. + +

                    Replacing a Power Cell

                    + Replacing a Power cell is a common type of maintenance for cyborgs. It usually involves replacing the cell with a fully charged one, or upgrading the cell with a larger capacity cell.
                    The steps to replace a cell are as follows: +
                      +
                    1. Unlock the Cyborg's Interface by swiping your ID on it
                    2. +
                    3. Open the Cyborg's outer panel using a crowbar
                    4. +
                    5. Remove the old power cell
                    6. +
                    7. Insert the new power cell
                    8. +
                    9. Close the Cyborg's outer panel using a crowbar
                    10. +
                    11. Lock the Cyborg's Interface by swiping your ID on it, this will prevent non-qualified personnel from attempting to remove the power cell
                    12. +
                    + +

                    Exposing the Internal Wiring

                    + Exposing the internal wiring of a cyborg is fairly easy to do, and is mainly used for cyborg repairs.
                    You can easily expose the internal wiring by following the steps below: +
                      +
                    1. Follow Steps 1 - 3 of "Replacing a Cyborg's Power Cell"
                    2. +
                    3. Open the cyborg's internal wiring panel by using a screwdriver to unsecure the panel
                    4. +
                    + To re-seal the cyborg's internal wiring: +
                      +
                    1. Use a screwdriver to secure the cyborg's internal panel
                    2. +
                    3. Follow steps 4 - 6 of "Replacing a Cyborg's Power Cell" to close up the cyborg
                    4. +
                    + +

                    Cyborg Repairs

                    + Occasionally a Cyborg may become damaged. This could be in the form of impact damage from a heavy or fast-travelling object, or it could be heat damage from high temperatures, or even lasers or Electromagnetic Pulses (EMPs). + +

                    Dents

                    + If a cyborg becomes damaged due to impact from heavy or fast-moving objects, it will become dented. Sure, a dent may not seem like much, but it can compromise the structural integrity of the cyborg, possibly causing a critical failure. + Dents in a cyborg's frame are rather easy to repair, all you need is to apply a welding tool to the dented area, and the high-tech cyborg frame will repair the dent under the heat of the welder. + +

                    Excessive Heat Damage

                    + If a cyborg becomes damaged due to excessive heat, it is likely that the internal wires will have been damaged. You must replace those wires to ensure that the cyborg remains functioning properly.
                    To replace the internal wiring follow the steps below: +
                      +
                    1. Unlock the Cyborg's Interface by swiping your ID
                    2. +
                    3. Open the Cyborg's External Panel using a crowbar
                    4. +
                    5. Remove the Cyborg's Power Cell
                    6. +
                    7. Using a screwdriver, expose the internal wiring of the Cyborg
                    8. +
                    9. Replace the damaged wires inside the cyborg
                    10. +
                    11. Secure the internal wiring cover using a screwdriver
                    12. +
                    13. Insert the Cyborg's Power Cell
                    14. +
                    15. Close the Cyborg's External Panel using a crowbar
                    16. +
                    17. Lock the Cyborg's Interface by swiping your ID
                    18. +
                    + These repair tasks may seem difficult, but are essential to keep your cyborgs running at peak efficiency. + +

                    In Case of Emergency

                    + In case of emergency, there are a few steps you can take. + +

                    "Rogue" Cyborgs

                    + If the cyborgs seem to become "rogue", they may have non-standard laws. In this case, use extreme caution. + To repair the situation, follow these steps: +
                      +
                    1. Locate the nearest robotics console
                    2. +
                    3. Determine which cyborgs are "Rogue"
                    4. +
                    5. Press the lockdown button to immobilize the cyborg
                    6. +
                    7. Locate the cyborg
                    8. +
                    9. Expose the cyborg's internal wiring
                    10. +
                    11. Check to make sure the LawSync and AI Sync lights are lit
                    12. +
                    13. If they are not lit, pulse the LawSync wire using a multitool to enable the cyborg's LawSync
                    14. +
                    15. Proceed to a cyborg upload console. The Company usually places these in the same location as AI upload consoles.
                    16. +
                    17. Use a "Reset" upload moduleto reset the cyborg's laws
                    18. +
                    19. Proceed to a Robotics Control console
                    20. +
                    21. Remove the lockdown on the cyborg
                    22. +
                    + +

                    As a last resort

                    + If all else fails in a case of cyborg-related emergency, there may be only one option. Using a Robotics Control console, you may have to remotely detonate the cyborg. +

                    WARNING:

                    Do not detonate a borg without an explicit reason for doing so. Cyborgs are expensive pieces of company equipment, and you may be punished for detonating them without reason. + + + + "} + + +/obj/item/weapon/book/manual/security_space_law + name = "Corporate Regulations" + desc = "A set of corporate guidelines for keeping law and order on privately-owned space stations." + icon_state = "bookSpaceLaw" + item_state = "book13" + author = "The Company" + title = "Corporate Regulations" + +/obj/item/weapon/book/manual/security_space_law/New() + ..() + dat = {" + + + + + + + + + + + "} + + + +/obj/item/weapon/book/manual/medical_diagnostics_manual + name = "Medical Diagnostics Manual" + desc = "First, do no harm. A detailed medical practitioner's guide." + icon_state = "bookMedical" + item_state = "book12" + author = "Medical Department" + title = "Medical Diagnostics Manual" + +/obj/item/weapon/book/manual/medical_diagnostics_manual/New() + ..() + dat = {" + + + + +
                    +

                    The Oath

                    + + The Medical Oath sworn by recognised medical practitioners in the employ of [using_map.company_name]
                    + +
                      +
                    1. Now, as a new doctor, I solemnly promise that I will, to the best of my ability, serve humanity-caring for the sick, promoting good health, and alleviating pain and suffering.
                    2. +
                    3. I recognise that the practice of medicine is a privilege with which comes considerable responsibility and I will not abuse my position.
                    4. +
                    5. I will practise medicine with integrity, humility, honesty, and compassion-working with my fellow doctors and other colleagues to meet the needs of my patients.
                    6. +
                    7. I shall never intentionally do or administer anything to the overall harm of my patients.
                    8. +
                    9. I will not permit considerations of gender, race, religion, political affiliation, sexual orientation, nationality, or social standing to influence my duty of care.
                    10. +
                    11. I will oppose policies in breach of human rights and will not participate in them. I will strive to change laws that are contrary to my profession's ethics and will work towards a fairer distribution of health resources.
                    12. +
                    13. I will assist my patients to make informed decisions that coincide with their own values and beliefs and will uphold patient confidentiality.
                    14. +
                    15. I will recognise the limits of my knowledge and seek to maintain and increase my understanding and skills throughout my professional life. I will acknowledge and try to remedy my own mistakes and honestly assess and respond to those of others.
                    16. +
                    17. I will seek to promote the advancement of medical knowledge through teaching and research.
                    18. +
                    19. I make this declaration solemnly, freely, and upon my honour.
                    20. +

                    + +
                    + + + + + + "} + + +/obj/item/weapon/book/manual/engineering_guide + name = "Engineering Textbook" + icon_state ="bookEngineering2" + item_state = "book3" + author = "Engineering Encyclopedia" + title = "Engineering Textbook" + +/obj/item/weapon/book/manual/engineering_guide/New() + ..() + dat = {" + + + + + + + + + + "} + + +/obj/item/weapon/book/manual/chef_recipes + name = "Chef Recipes" + icon_state = "cooked_book" + item_state = "book16" + author = "Victoria Ponsonby" + title = "Chef Recipes" + + dat = {" + + + + + +

                    Food for Dummies

                    + Here is a guide on basic food recipes and also how to not poison your customers accidentally. + +

                    Basics:

                    + Knead an egg and some flour to make dough. Bake that to make a bun or flatten and cut it. + +

                    Burger:

                    + Put a bun and some meat into the microwave and turn it on. Then wait. + +

                    Bread:

                    + Put some dough and an egg into the microwave and then wait. + +

                    Waffles:

                    + Add two lumps of dough and 10 units of sugar to the microwave and then wait. + +

                    Popcorn:

                    + Add 1 corn to the microwave and wait. + +

                    Meat Steak:

                    + Put a slice of meat, 1 unit of salt, and 1 unit of pepper into the microwave and wait. + +

                    Meat Pie:

                    + Put a flattened piece of dough and some meat into the microwave and wait. + +

                    Boiled Spaghetti:

                    + Put the spaghetti (processed flour) and 5 units of water into the microwave and wait. + +

                    Donuts:

                    + Add some dough and 5 units of sugar to the microwave and wait. + +

                    Fries:

                    + Add one potato to the processor, then bake them in the microwave. + + + + + "} + + +/obj/item/weapon/book/manual/barman_recipes + name = "Barman Recipes" + desc = "For the enterprising drink server." + icon_state = "barbook" + item_state = "book14" + author = "Sir John Rose" + title = "Barman Recipes" + + dat = {" + + + + + +

                    Drinks for Dummies

                    + Here's a guide for some basic drinks. + +

                    Black Russian:

                    + Mix vodka and Kahlua into a glass. + +

                    Cafe Latte:

                    + Mix milk and coffee into a glass. + +

                    Classic Martini:

                    + Mix vermouth and gin into a glass. + +

                    Gin Tonic:

                    + Mix gin and tonic into a glass. + +

                    Grog:

                    + Mix rum and water into a glass. + +

                    Irish Cream:

                    + Mix cream and whiskey into a glass. + +

                    The Manly Dorf:

                    + Mix ale and beer into a glass. + +

                    Mead:

                    + Mix enzyme, water, and sugar into a glass. + +

                    Screwdriver:

                    + Mix vodka and orange juice into a glass. + + + + "} + + +/obj/item/weapon/book/manual/detective + name = "The Film Noir: Proper Procedures for Investigations" + icon_state ="bookDetective" + item_state = "book8" + author = "The Company" + title = "The Film Noir: Proper Procedures for Investigations" + + dat = {" + + + + +

                    Detective Work

                    + + Between your bouts of self-narration and drinking whiskey on the rocks, you might get a case or two to solve.
                    + To have the best chance to solve your case, follow these directions: +

                    +

                      +
                    1. Go to the crime scene.
                    2. +
                    3. Take your scanner and scan EVERYTHING (Yes, the doors, the tables, even the dog).
                    4. +
                    5. Once you are reasonably certain you have every scrap of evidence you can use, find all possible entry points and scan them, too.
                    6. +
                    7. Return to your office.
                    8. +
                    9. Using your forensic scanning computer, scan your scanner to upload all of your evidence into the database.
                    10. +
                    11. Browse through the resulting dossiers, looking for the one that either has the most complete set of prints, or the most suspicious items handled.
                    12. +
                    13. If you have 80% or more of the print (The print is displayed), go to step 10, otherwise continue to step 8.
                    14. +
                    15. Look for clues from the suit fibres you found on your perpetrator, and go about looking for more evidence with this new information, scanning as you go.
                    16. +
                    17. Try to get a fingerprint card of your perpetrator, as if used in the computer, the prints will be completed on their dossier.
                    18. +
                    19. Assuming you have enough of a print to see it, grab the biggest complete piece of the print and search the security records for it.
                    20. +
                    21. Since you now have both your dossier and the name of the person, print both out as evidence and get security to nab your baddie.
                    22. +
                    23. Give yourself a pat on the back and a bottle of the ship's finest vodka, you did it!
                    24. +
                    +

                    + It really is that easy! Good luck! + + + "} + +/obj/item/weapon/book/manual/nuclear + name = "Fission Mailed: Nuclear Sabotage 101" + icon_state ="bookNuclear" + item_state = "book8" + author = "Stealth Assault Enterprises" + title = "Fission Mailed: Nuclear Sabotage 101" + + dat = {" + + + + +

                    Nuclear Explosives 101

                    + Hello and thank you for choosing Stealth Assault Enterprises for your nuclear information needs. Today's crash course will deal with the operation of a Nuclear Fission Device.

                    + + First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE. Pressing any button on the compacted bomb will cause it to extend and bolt itself into place. If this is done, to unbolt it, one must completely log in, which at this time may not be possible.
                    + +

                    To make the nuclear device functional

                    +
                      +
                    • Place the nuclear device in the designated detonation zone.
                    • +
                    • Extend and anchor the nuclear device from its interface.
                    • +
                    • Insert the nuclear authorisation disk into the slot.
                    • +
                    • Type the numeric authorisation code into the keypad. This should have been provided.
                      + Note: If you make a mistake, press R to reset the device. +
                    • Press the E button to log on to the device.
                    • +

                    + + You now have activated the device. To deactivate the buttons at anytime, for example when you've already prepped the bomb for detonation, remove the authentication disk OR press R on the keypad.

                    + Now the bomb CAN ONLY be detonated using the timer. Manual detonation is not an option. Toggle off the SAFETY.
                    + Note: You wouldn't believe how many SAARE Operatives with doctorates have forgotten this step.

                    + + So use the - - and + + to set a detonation time between 5 seconds and 10 minutes. Then press the timer toggle button to start the countdown. Now remove the authentication disk so that the buttons deactivate.
                    + Note: THE BOMB IS STILL SET AND WILL DETONATE

                    + + Now before you remove the disk, if you need to move the bomb, you can toggle off the anchor, move it, and re-anchor.

                    + + Remember the order:
                    + Disk, Code, Safety, Timer, Disk, RUN!

                    + Intelligence Analysts believe that normal corporate procedure is for the Site Manager to secure the nuclear authentication disk.

                    + + Good luck! + + + "} + +/obj/item/weapon/book/manual/atmospipes + name = "Pipes and You: Getting To Know Your Scary Tools" + icon_state = "pipingbook" + item_state = "book3" + author = "Maria Crash, Senior Atmospherics Technician" + title = "Pipes and You: Getting To Know Your Scary Tools" + dat = {" + + + + + +

                    Contents

                    +
                      +
                    1. Author's Foreword
                    2. +
                    3. Basic Piping
                    4. +
                    5. Insulated Pipes
                    6. +
                    7. Atmospherics Devices
                    8. +
                    9. Heat Exchange Systems
                    10. +
                    11. Final Checks
                    12. +

                    + +

                    HOW TO NOT SUCK QUITE SO HARD AT ATMOSPHERICS


                    + Or: What the fuck does a "pressure regulator" do?

                    + + Alright. It has come to my attention that a variety of people are unsure of what a "pipe" is and what it does. + Apparently, there is an unnatural fear of these arcane devices and their "gases." Spooky, spooky. So, + this will tell you what every device constructable by an ordinary pipe dispenser within atmospherics actually does. + You are not going to learn what to do with them to be the super best person ever, or how to play guitar with passive gates, + or something like that. Just what stuff does.

                    + + +

                    Basic Pipes

                    + The boring ones.
                    + Most ordinary pipes are pretty straightforward. They hold gas. If gas is moving in a direction for some reason, gas will flow in that direction. + That's about it. Even so, here's all of your wonderful pipe options.
                    + +
                      +
                    • Straight pipes: They're pipes. One-meter sections. Straight line. Pretty simple. Just about every pipe and device is based around this + standard one-meter size, so most things will take up as much space as one of these.
                    • +
                    • Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
                    • +
                    • Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
                    • +
                    • 4-way manifold: A four-way junction.
                    • +
                    • Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh, use them to decorate your house or something.
                    • +
                    • Manual valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
                    • +
                    • Manual T-valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


                    • +
                    + + An important note here is that pipes are now done in three distinct lines - general, supply, and scrubber. You can move gases between these with a universal adapter. Use the correct position for the correct location. + Connecting scrubbers to a supply position pipe makes you an idiot who gives everyone a difficult job. Insulated and HE pipes don't go through these positions. + +

                    Insulated Pipes

                    +
                  • Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
                  • +
                  • Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
                  • +
                  • 4-way manifold: A four-way junction.
                  • +
                  • Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh. Use them to decorate your house or something.
                  • +
                  • Manual Valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
                  • +
                  • Manual T-Valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


                  • + +

                    Insulated Pipes


                    + Special Public Service Announcement.
                    + Our regular pipes are already insulated. These are completely worthless. Punch anyone who uses them.

                    + +

                    Devices:

                    + They actually do something.
                    + This is usually where people get frightened, afraid, and start calling on their gods and/or cowering in fear. Yes, I can see you doing that right now. + Stop it. It's unbecoming. Most of these are fairly straightforward.
                    + +
                      +
                    • Gas pump: Take a wild guess. It moves gas in the direction it's pointing (marked by the red line on one end). It moves it based on pressure, the maximum output being 15000 kPa (kilopascals). + Ordinary atmospheric pressure, for comparison, is 101.3 kPa, and the minimum pressure of room-temperature pure oxygen needed to not suffocate in a matter of minutes is 16 kPa + (though 18 kPa is preferred when using internals with pure oxygen, for various reasons). A high-powered variant will move gas more quickly at the expense of consuming more power. Do not turn the distribution loop up to 15000 kPa. + You will make engiborgs cry and the Chief Engineer will beat you.
                    • +
                    • Pressure regulator: These replaced the old passive gates. You can choose to regulate pressure by input or output, and regulate flow rate. Regulating by input means that when input pressure is above the limit, gas will flow. + Regulating by output means that when pressure is below the limit, gas will flow. Flow rate can be controlled.
                    • +
                    • Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
                    • +
                    • Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon." Activating a panic siphon without very good reason will kill someone. Don't do it.
                    • +
                    • Meter: A little box with some gauges and numbers. Fasten it to any pipe or manifold and it'll read you the pressure in it. Very useful.
                    • +
                    • Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2," for reference, on non-mirrored mixers.. + Output is controlled by flow rate. There is also an "omni" variant that allows you to set input and output sections freely..
                    • +
                    • Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe, + the rest will continue out the other side. Can also output from 0-4500 kPa. The "omni" vairant allows you to set input and output sections freely.
                    • +
                    + +

                    Heat Exchange Systems

                    + Will not set you on fire.
                    + These systems are used to only transfer heat between two pipes. They will not move gases or any other element, but will equalize the temperature (eventually). Note that because of how gases work (remember: pv=nRt), + a higher temperature will raise pressure, and a lower one will lower temperature.
                    + +
                  • Pipe: This is a pipe that will exchange heat with the surrounding atmosphere. Place in fire for superheating. Place in space for supercooling.
                  • +
                  • Bent pipe: Take a wild guess.
                  • +
                  • Junction: The point where you connect your normal pipes to heat exchange pipes. Not necessary for heat exchangers, but necessary for H/E pipes/bent pipes.
                  • +
                  • Heat exchanger: These funky-looking bits attach to an open pipe end. Put another heat exchanger directly across from it, and you can transfer heat across two pipes without having to have the gases touch. + This normally shouldn't exchange with the ambient air, despite being totally exposed. Just don't ask questions.

                  • + + That's about it for pipes. Go forth, armed with this knowledge, and try not to break, burn down, or kill anything. Please. + + + + + "} + +/obj/item/weapon/book/manual/evaguide + name = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition" + icon_state = "evabook" + item_state = "book14" + author = "Maria Crash, Senior Atmospherics Technician" + title = "EVA Gear and You: Not Spending All Day Inside, 2nd Edition" + dat = {" + + + + + +

                    EVA Gear and You: Not Spending All Day Inside, 2nd Edition

                    + Or: How not to suffocate because there's a hole in your shoes
                    + +

                    Contents

                    +
                      +
                    1. A foreword on using EVA gear
                    2. +
                    3. Use of Softsuits
                    4. +
                    5. Putting on a Voidsuit
                    6. +
                    7. Operation of Hardsuits
                    8. +
                    9. Cyclers and Other Modification Equipment
                    10. +
                    11. Miscellaneous Advice
                    12. +
                    13. Final Checks
                    14. +
                    +
                    + + EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training, + but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into three main sections: + softsuits, voidsuits, and hardsuits. General advice and instructions for modification are present as well.

                    + + One important point for synthetics and people using full-body prosthetics: You obviously don't need oxygen, but you do run the risk of overheating in vacuum. + Rather than oxygen, use a suit cooling unit. Many emergency equipment stores don't hold them, unfortunately, but dedicated EVA stores will. Be aware of your heat tolerances.

                    + +

                    Softsuits and Emergency Equipment

                    + The bulkiest things this side of Alpha Centauri
                    + These suits are the both grey ones that are stored in EVA and orange emergency suits in emergency lockers. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiation or physical impact. + As Medical, Engineering, Security, and Mining all have voidsuits of their own, these don't see much use outside of emergencies. In an emergency, knowing how to put one on can save your life.

                    + + First, take the suit. It should be in three pieces: A top, a bottom, and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however, + put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit, + but to an extent they will adjust to you. You can then find the snaps and seals around the waist, where the two pieces meet. Fasten these, and double-check their tightness. + The red indicators around the waist of the lower half will turn green when this is done correctly. Next, put on whatever breathing apparatus you're using, be it a gas mask or a breath mask. Make sure the oxygen tube is fastened into it. + Put on the helmet now, straightforward, and make sure the tube goes into the small opening specifically for internals. Again, fasten seals around the neck, a small indicator light in the inside of the helmet should go from red to off when all is fastened. + There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit, + but it is recommended to have a full-sized tank on your back for EVA.

                    + + Important note: When using these, especially in emergencies, be aware of your surroundings! These suits can tear or breach more easily than any other type, especially in an environment with broken glass and metal everywhere. + If your suit is breached, you will be in deep trouble. Pressure issues can inhibit breathing even with internals.

                    + + These suits tend to be wearable by most species. They're large and flexible. They might be pretty uncomfortable for some, though, so keep that in mind.

                    + +

                    Voidsuits

                    + Heavy, uncomfortable, still the best option.
                    + These suits come in many specialized varieties. The most common are engineering, atmospherics, security, medical, and mining varieties. + These provide a lot more protection than the standard suits, and depending on the specialization, can offer different protections. + For example, security suits have armor plating, engineering suits have radiation protection, and atmospherics suits are rated for extremely high temperatures.

                    + + Similarly to the softsuits, these are split into three parts. Fastening the pant and top are mostly the same as the softsuits, with the exception that these are a bit heavier, + though not as bulky. The helmet goes on differently, with the air tube feeding into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise, + and then is turned to face the front and sealed. There is a small button on the right side of the helmet that activates the helmet light. + The tanks that fasten onto the side slot are emergency tanks, as well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.

                    + + These suits generally only fit one species. NanoTrasen's are usually human-fitting by default, but there's equipment that can make modifications to the hardsuits to fit them to other species.

                    + + Later-model voidsuits can have magboots and helmets installed into the suit and deployed when needed. Check the operator's manual for individual suits to see how the helmets are installed. + If a helmet is installed, you can skip it while putting the suit on, obviously. When deployed, it will deploy from the back of your neck, covering the head and sealing at the front.

                    + +

                    Hardsuits/Rigs

                    + The fancy stuff.
                    + Proper hardsuits are the most complex sort of EVA equipment available, and blur the line between spacesuits and smaller exosuits. They're sometimes known as 'rigs' or 'powered armor'. + These are the suits with the widest variety of uses, owing to the wide variety of equipment that can be installed on them. Like voidsuits, they come in different, specialized varieties, each one offering different protections and different equipment. + Equipment that can be installed includes weapons, power tools, mining equipment, medical equipment, AI assistants, and more.

                    + + Putting these on is relatively simple. They come as compact packs, worn like a backpack and secured with a harness. Activating them, though, is a more complex process. The suit deploys from the module similarly to helmets deploying from voidsuits. + After it covers the whole body, the suit can be started. The startup sequence takes some time. The suit will automatically fit itself to your body, sealing each section individually - boots, gloves, pants, torso, and helmet - then connecting them.

                    + + Operating a hardsuit is a much more complicated proposal than operating other EVA equipment. While putting them on is relatively simple, and operating basic functions like oxygen and magboots is the same as other suits, the rest is far more complex. + Consult the operator's manual for invidual pieces of equipment that you plan to use. Use of these for heavy work is only reccomended for people who have specialized training and extensive EVA experience. + The potential of a suit breach is always there, and the use of powered equipment raises it significantly.

                    + +

                    Miscellaneous Advice

                    + Pro tip: Safety first.
                    + There's a lot of general advice that can be helpful for people who haven't taken a long-form instruction course. Much of this is going to be fairly obvious safety advice, but it's never bad to remind yourself of that.

                    + +
                      +
                    • Magboots are important. They can be the difference between keeping your footing and needing a rescue team. A tie-off or a jetpack can substitute if necessary.
                    • +
                    • Be aware of breach hazards, especially in softsuits. Loss of suit pressure can be a fatal disaster.
                    • +
                    • Keep an eye on your internals. Having to make two trips outside is better than running out of air.
                    • +
                    • Similarly, keep an eye on the battery status of cooling units and other equipment.
                    • +
                    • In vacuum, sound doesn't carry. Use a radio or sign language for communication.


                    • +
                    + +

                    Modification Equipment

                    + How to actually make voidsuits fit you.
                    + There's a variety of equipment that can modify hardsuits to fit species that can't fit into them, making life quite a bit easier.

                    + + The first piece of equipment is a suit cycler. This is a large machine resembling the storage pods that are in place in some places. These are machines that will automatically tailor a suit to certain specifications. + The largest uses of them are for their cleaning functions and their ability to tailor suits for a species. Do not enter them physically. You will die from any of the functions being activated, and it will hurt the whole time you're dying. + These machines can both tailor a suit between species, and between types. This means you can convert engineering hardsuits to atmospherics, or the other way. This is useful. Use it if you can.

                    + + There's also modification kits that let you modify suits yourself. These are extremely difficult to use unless you understand the actual construction of the suit. I do not reccomend using them unless no other option is available.

                    + +

                    Final Checks

                    +
                      +
                    • Are all seals fastened correctly?
                    • +
                    • If you have modified it manually, is absolutely everything sealed perfectly?
                    • +
                    • Do you either have shoes on under the suit, or magnetic boots on over it?
                    • +
                    • Do you have internals connected and activated?
                    • +
                    • Do you have a way to communicate with the station in case something goes wrong?
                    • +
                    • Do you have a second person watching if this is a training session?

                    • +
                    + + If you don't have any further issues, go out and do whatever is necessary. + + + + "} + +/obj/item/weapon/book/manual/virology + name = "Sneezes and Coughs: A Guide To Virology, 1st Edition" + icon_state = "bookvirology" + item_state = "book5" + author = "James Simpson, Chief Virologist" + title = "Sneezes and Coughs: A Guide To Virology, 1st Edition" + dat = {" + + + + +

                    A Guide to Virology, 1st Edition

                    + Welcome to virology!

                    + + A virologist spends much of their time isolated in the virology department fiddling with deadly and annoying plagues. This guide will get you on the right path to properly
                    + creating and curing the worst of the worst.

                    + +

                    Anatomy of a Virus

                    + Before you jump in to making the next Black Death, you need to understand what's what. When you pop a tray into the disease analyzer you'll get a paper listing quite a few
                    + things about that virus!

                    + + Antigens
                    + The first thing you might notice on this analysis is the listed antigens of a virus. This, essentially, is what you need for cures. When a patient develops an antibody that
                    + matches a disease's antigen, they'll be immune to the virus.

                    + + Transmission
                    + This will list one of three methods of transmission: airborne, contact, or blood. Airborne viruses will spread through the air, meaning if a patient is coughing or sneezing
                    + it will spread quicker. Contactviruses need some sort of, well, contact. Blood on the floor, mucus, and vomit should all be cleaned up. Blood-transmission viruses will only
                    + spread through actual blood contact, meaning an injection of the virus would be necessary to spread it.

                    + + Species
                    + Not all viruses are compatible with all species! This will list off what can be infected by a particular virus. This can be changed through splicing. In rare cases you will
                    + get a virus that has infected crewmembers but cannot infect your lab monkeys. In these cases, you will likely have to splice different species data to safely make a cure.

                    + + Symptoms
                    + This lists what all the virus does, along with the strength of the symptoms and their aggressiveness. A full list of symptoms and their stages can be found below.

                    + +

                    Protection

                    + First off, any good virologist needs to gear up!

                    + Your virology lab should have the following: +
                      +
                    • Two (2) Level-3 Bio Hoods
                    • +
                    • Two (2) Level-3 Bio Suits
                    • +
                    • Two (2) sets of sealed internals
                    • +
                    • One (1) box of sterile masks
                    • +
                    • One (1) box of latex gloves
                    • +

                    + Make sure to gear up appropriately and take precautions to not bring the virus out of the lab!

                    + What needs to be worn varies from race to race, as some species cannot be affected by a virus, but
                    + precautions should always be taken to prevent accidentally carrying the virus out of containment.

                    + +

                    Machinery

                    + These are the types of machines that you will be expected to use in this department. Study them, know them, and make sure you use them correctly!

                    + + Antibody Scanner
                    + A device similar to a health analyzer, when scanned over a patient it will tell you any present antibodies in their bloodstream.

                    + + Pathogenic Isolator
                    + This device can be loaded with blood samples via a syringe. It will tell you if there is a present viral pathogen, and if so it can isolate the pathogen,
                    + producing a virus dish for further research. This is the primary means of creating virus dishes.

                    + + Pathogenic Incubator
                    + One of the most important machines, you'll be sitting here a lot. Virus trays you produce will start small, and you will need to grow them to get anywhere.
                    + Load it with virus food (diluted milk, found in a wall mounted dispenser nearby) as well as a virus dish and turn it on. It will slowly grow the virus
                    + for analysis, splicing, or whatever other use you might find for it. It can also inject the virus into other chemicals (i.e. blood) which will allow you
                    + to artificially infect a blood sample to infect test subjects.

                    + + Additionally, it can irradiate a virus dish to cause mutation. This randomly alters the syndromes, allowing you to collect new data.

                    + + Disease Analyser
                    + Virus dishes may be placed into this machine after it has been incubated sufficiently. It will create a reading of the virus' symptoms, species targeted,
                    + associated antibodies, and so on. It will also update the disease into the digital database, allowing it to be detected via Medical HUD or health analyzer.

                    + + Isolation Centrifuge
                    + Inserting a vial of blood into this machine will allow it to take a present pathogen or antibody, turning it into a pure virus or pure cure for your use.
                    + It will automatically detect if either are in the blood sample, so it is useful for determining if a sample is infected. The sample will be reusable if creating
                    + more of the virus, but will be expended upon creating antibodies.

                    + + Disease Splicer
                    + As it's name implied, this machine is for splicing the disease symptoms and creating new viruses. Insert an analyzed virus sample to take a symptom which can
                    + then be saved on a GNA disk (created by the machine), or use an existing disk on the machine to load up a symptom and splice it in.

                    + +

                    Syndromes

                    + While curing patients is the obvious side to virology, there's more you can do! For a variety of reasons you may wish to engineer your own virus through mutations
                    + and splicing. Keep in mind that infecting the whole station with a deadly virus is generally frowned upon in most societies, and even in an antagonistic role simply
                    + infecting everyone with a very deadly virus is not ideal.

                    + + Obtaining a Sample
                    + The first step to making your own virus is to find a virus to start tinkering with. There are two ways of doing this: grabbing the pre-spawned virus dish from the
                    + freezer create in your laboratory, or taking a blood sample from an infected patient that you haven't cured yet and running it through the Pathogenic Isolator to
                    + produce a virus dish.

                    + + In either case, you'll then need to incubate the virus in the Pathogenic Incubator until it is of sufficient size for you to work with, and ideally also run it
                    + through the Disease Analyzer to get an idea of what it does. This will also keep the rest of Medical from getting on your case about not giving them the ability to
                    + detect viral outbreaks without you, and will help prevent public lynchings if someone sneezes.

                    + + Once that's all done, you're ready to start the real work.

                    + + Obtaining a Sample
                    + Sometimes you just need to infect somebody. Usually, this is because you're doing some splicing work and want to make sure that you'll have some copies of a virus
                    + in reserve for future study. Fortunately, the process of deliberately infecting patients, as well as creating backups of your viruses, is actually very simple.

                    + + If you have a virus dish and wish to infect a patient, place the dish into the Pathogenic Incubator along with a beaker full of blood. Ideally, this is the
                    + patient's (or test monkey's) blood, though anyone's will do in a pinch - just be aware of the possibility of blood rejection, and be ready to deal with it accordingly.
                    + The incubator can inject a sample of the virus into the blood, which you can then inject into the patient to infect them. Alternatively, once a blood sample is infected
                    + you may place it in either the Pathogenic Isolator or Isolation Centrifuge to make as many samples of that virus you need without destroying the sample.
                    + No infections necessary!

                    + + Splicing
                    + To understand splicing, you must know that every virus has 4 GNA strands, each of which is tied to one of the syndromes that it can manifest. Each syndrome, and thus each
                    + strand, is ranked in order of its appearance when symptoms begin to manifest in a patient, and higher-ranked (and thus slower to appear) syndromes are likely to be more
                    + powerful than lower-ranked ones. (1) is the lowest, first symptom to manifest, and (4) is the highest, final symptom to manifest.

                    + + The Disease Splicer will allow you to examine and manipulate a virus' GNA strands to create a virus with any syndromes that you want.

                    + + The splicer has main three functions which allow you to modify viruses and define which GNA strands they contain. The first of these functions is the splicing function,
                    + which copies a GNA strand from an inserted virus tray to the disease splicer buffer. This is done by selecting the desired GNA strand under "Reverse Engineering".
                    + Doing so will destroy the virus tray in the process, so be sure that you have more samples of the virus on hand if you want to preserve it.

                    + + The disease splicer’s second function is to copy whatever strand it has stored in its buffer to a disk the machine creates. These disks can be re-inserted into
                    + the disease splicer later, making their stored GNA strand immediately available for use. Unlike virus trays, disks can be reused without destroying them.

                    + + The final function of the disease splicer is to copy over a GNA strand of its loaded virus dish with the corresponding GNA strand that is currently stored in
                    + its buffer. This effectively allows you to give a virus any set of symptoms you want, so long as it follows the rules of the ranking. A rank (1) symptom can fill
                    + any slot, while a (3) can only fill 3 and up, and a (4) can only be in the fourth slot.

                    + + Mutating
                    + Sometimes you just don't have any of the symptoms you want. Fortunately, mutating a virus dish is very simple. Simply place a dish in the Pathogenic Incubator
                    + to get started and click 'add radiation'. This will fill up the radiation bar with each press. When the machine is on, this bar will slowly tick down as the radiation
                    + is applied to the sample. For any significant mutation the radiation should be over 500k, meaning at least half the bar. When a mutation occurs -- and multiple
                    + mutations can occur in a single sample -- there will be a ping from the machine. Once it's done growing and mutating, throw it into the analyzer and see what you got!

                    + + With these functions in mind, the process in order for you to develop your own virus is as follows: +
                      +
                    1. Create lots of virus sample dishes.
                    2. +
                    3. If the virus dishes you have already created don't contain the syndromes that you want in
                      + your custom virus, irradiate them in the Pathogenic Incubator until they do.
                    4. +
                    5. Create backup dishes of your new viruses.
                    6. +
                    7. Strip the GNA strands that you want from your virus dishes, saving each one onto a disk.
                    8. +
                    9. Load a new virus dish into the Disease Splicer.
                    10. +
                    11. Copy over the virus dish's GNA strands with your disks.
                    12. +
                    13. Once you have your final product, you can use the Pathogenic Incubator to create a beaker
                      + of blood containing the virus. Congratulations! You are now officially a bioweapon
                      + manufacturer.
                    14. +
                    + +

                    Curing

                    + Curing a virus is relatively simple, but the below will leave you without a sample to work with. If this is a concern, see 'obtaining a sample' above. Alternatively to
                    + the below, spacecillin can be used in a pinch to attempt to treat a virus if no virologist is present and someone decided to roll around in mucus. However this is not
                    + guaranteed to work, and can in fact simply make the virus more resistant to spacecillin.

                    + + Quarantine
                    + If you or a crew member becomes infected by a virus, your first concern should be to isolate them as quickly as possible to prevent the disease from spreading. Ideally,
                    + this means getting the patient back to Virology and into a holding cell, but failing that, it's important to get them into an isolated room, shut the door, and keep anyone
                    + else from entering. It is also important to clean up any blood, vomit, or mucus left behind by the patient, as it may spread the virus as well.

                    + + Antibodies
                    + Now that your patient is safely hidden away where they can't infect people without your permission, you want to cure them. The basic idea behind that is to get them to start
                    + synthesizing antibodies.

                    + + Antibodies are produced by living organisms that have started to fight off a virus in their bloodstream. An organism that has produced antibodies is effectively
                    + immune to any virus that is weak to those antibodies, which are generally denoted by a short string of letters (say, "KM" or similar) to let you know which viruses they
                    + are effective against. Even better, once an organism has synthesized antibodies for itself, you can take a blood sample and use that to create more antibodies, which you
                    + can then inject into other people to cure the virus in them, or even to prevent them from catching it in the first place. And you can take blood samples from those people
                    + to make even more antibodies.

                    + + The issue is that, most of the time, a patient's immune system is not powerful enough to create antibodies on its own (space plagues are nasty things), so you have to do that
                    + for them. You do that by injecting them with Radium.

                    + + Since Radium is radioactive, this is obviously not something that you want to do to your actual patients. It'll cause major toxin damage when an antibody is
                    + made and quickly lead to organ failure. You get around this by using monkeys, which you can get from the box of monkey cubes very handily left on the desk in Virology for
                    + this very purpose.

                    + + So the process of curing diseases is, really, quite simple. Find an infected patient. Make a monkey (ideally, strap this monkey to a bed in an isolation cell to make the
                    + rest of this process easier). Take a blood sample from the infected patient, then inject it into the monkey to infect the monkey. Inject the monkey with Radium. Use the antibody
                    + scanner on the monkey every few seconds until it says that the monkey has begun producing antibodies, then take a blood sample (or two or three or four) from the
                    + monkey. Put the blood sample(s) into a vial, then put the vial into the isolation centrifuge and isolate the antibodies. Voila: a bottle of virus cure. Inject it into as many
                    + people as you like, then throw the (likely dead) monkey into the disposals chute to eject its disease-ridden body into space and clean the isolation cell. Or, if you're on
                    + the ball and wish to be conservative with your limited monkey cubes, save the monkey, too.

                    + + Congratulations! You have now cured a disease.

                    + + The important thing to remember is that anyone injected with antibodies becomes a source of those antibodies for the future. You can take a sample of their blood
                    + and put it into the isolation centrifuge just like you did with the monkey's to produce more antibodies if you ever run out. For this reason, and because you might have
                    + gotten infected yourself in the process of synthesizing a cure, most Virologists will inject themselves with any antibodies they produce.

                    + +

                    Syndromes

                    + Below are the possible syndromes of a virus and the stages they can appear at. A stage covers that stage and higher, so a stage 1 syndrome can appear at stage 4.
                    + A stage 4 syndrome, however, cannot appear any earlier than stage 4.

                    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                    Syndromes
                    Stage Name Effects +
                    1 Sneezing Makes the patient sneeze. Spreads airborne disease! +
                    1 Mucus Buildup Makes the patient's throat fill with mucus. +
                    1 Salivary Gland Stimulation Makes the patient drool. Spreads the disease! +
                    1 Involuntary Twitching Makes the patient twitch. +
                    1 Headache Gives the patient a headache. +
                    1 Mlemington's Syndrome Makes the patient 'mlem' uncontrollably. +
                    1 Spyndrome Makes the patient spin uncontrollably. +
                    2 Involuntary Vocalization Makes the patient scream. +
                    2 Vomiting Makes the patient vomit. Spreads the disease! +
                    2 Excessive Sleepiness Makes the patient drowsy. +
                    2 Narcolepsy Makes the patient pass out at random. +
                    2 Vision Loss Causes partial blindness. +
                    2 Severe Cough Causes coughing. Spreads airborne disease! +
                    2 Digestive Inefficiency Makes the patient always hungry. +
                    2 Reduced Circulation Makes the patient shiver. +
                    2 Hair Loss Causes the patient's hair to fall out. +
                    2 Overactive Adrenal Gland Makes the patient jittery, but also faster. +
                    2 Tinnitus Causes ringing in the patient's ears. +
                    2 Lingual Dissocation Scrambles the patients language center of their brain. +
                    3 Hyperacidity Causes small amounts of toxin damage. +
                    3 Nervous Motor Instability Makes the patient shaky. +
                    3 Pineal Gland Decalcification Gives the patient telepathy. +
                    3 Neurodegeneration Causes brain damage. +
                    3 Hallucination Causes hallucinations. +
                    3 Hearing Loss A less severe hearing damage. +
                    3 Topographical Cretinism Makes the patient incapable of walking straight. +
                    3 Uncontrolled Laughter Causes the patient to laugh uncontrollably. +
                    3 DNA Degradation Causes genetic damage. +
                    3 Phantom Aches Causes groaning. +
                    3 Chemical Synthesis Causes the synthesis of a certain chemical in the patient's bloodstream. +
                    3 Genetic Chameleonism Prevents the patient from experiencing organ rejection. +
                    3 Mass Revectoring Causes the patient to suddenly change size. +
                    3 Flipponov's Disease Causes the patient to uncontrollably do a flip. +
                    4 Gibbingtons Gibs the patients slowly. +
                    4 Radian's Syndrome Irradiates the patient from the inside. +
                    4 Deafness Causes complete deafness. +
                    4 Genome Regression Turns the patient into the primitive form of their species. +
                    4 Windpipe Contraction Causes the patient to involuntarily hold their breath. +
                    4 Autoimmune Response Adds toxins to the patient's bloodstream. +
                    4 Catastrophic DNA Degeneration Causes genetic damage and scrambles the patient's genes. +
                    4 Limb Paralysis Temporary loss of use in the patient's individual arms and legs. +
                    4 Organ Shutdown Slowly causes internal organ damage at random. +
                    4 Hyperaccelerated Aging Ages the patient, causing external damage. +
                    4 Brittle Bones Causes bones to break more easily. +
                    4 Organic Ignition Slowly causes the patient to burst into flames. +
                    +
                    + + + "} diff --git a/code/game/objects/items/weapons/material/bats.dm b/code/game/objects/items/weapons/material/bats.dm index 73896640a28..c625e948bbb 100644 --- a/code/game/objects/items/weapons/material/bats.dm +++ b/code/game/objects/items/weapons/material/bats.dm @@ -1,29 +1,29 @@ -/obj/item/weapon/material/twohanded/baseballbat - name = "bat" - desc = "HOME RUN!" - icon_state = "metalbat0" - base_icon = "metalbat" - throwforce = 7 - attack_verb = list("smashed", "beaten", "slammed", "smacked", "struck", "battered", "bonked") - hitsound = 'sound/weapons/genhit3.ogg' - default_material = "wood" - force_divisor = 1.1 // 22 when wielded with weight 20 (steel) - unwielded_force_divisor = 0.7 // 15 when unwielded based on above. - dulled_divisor = 0.75 // A "dull" bat is still gonna hurt - slot_flags = SLOT_BACK - -//Predefined materials go here. -/obj/item/weapon/material/twohanded/baseballbat/metal/New(var/newloc) - ..(newloc,"steel") - -/obj/item/weapon/material/twohanded/baseballbat/uranium/New(var/newloc) - ..(newloc,"uranium") - -/obj/item/weapon/material/twohanded/baseballbat/gold/New(var/newloc) - ..(newloc,"gold") - -/obj/item/weapon/material/twohanded/baseballbat/platinum/New(var/newloc) - ..(newloc,"platinum") - -/obj/item/weapon/material/twohanded/baseballbat/diamond/New(var/newloc) +/obj/item/weapon/material/twohanded/baseballbat + name = "bat" + desc = "HOME RUN!" + icon_state = "metalbat0" + base_icon = "metalbat" + throwforce = 7 + attack_verb = list("smashed", "beaten", "slammed", "smacked", "struck", "battered", "bonked") + hitsound = 'sound/weapons/genhit3.ogg' + default_material = "wood" + force_divisor = 1.1 // 22 when wielded with weight 20 (steel) + unwielded_force_divisor = 0.7 // 15 when unwielded based on above. + dulled_divisor = 0.75 // A "dull" bat is still gonna hurt + slot_flags = SLOT_BACK + +//Predefined materials go here. +/obj/item/weapon/material/twohanded/baseballbat/metal/New(var/newloc) + ..(newloc,"steel") + +/obj/item/weapon/material/twohanded/baseballbat/uranium/New(var/newloc) + ..(newloc,"uranium") + +/obj/item/weapon/material/twohanded/baseballbat/gold/New(var/newloc) + ..(newloc,"gold") + +/obj/item/weapon/material/twohanded/baseballbat/platinum/New(var/newloc) + ..(newloc,"platinum") + +/obj/item/weapon/material/twohanded/baseballbat/diamond/New(var/newloc) ..(newloc,"diamond") \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/gravemarker.dm b/code/game/objects/items/weapons/material/gravemarker.dm index 83d65a6c3b5..0505e6a8e6f 100644 --- a/code/game/objects/items/weapons/material/gravemarker.dm +++ b/code/game/objects/items/weapons/material/gravemarker.dm @@ -12,7 +12,7 @@ var/epitaph = "" //A quick little blurb /obj/item/weapon/material/gravemarker/attackby(obj/item/weapon/W, mob/user as mob) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) var/carving_1 = sanitizeSafe(tgui_input_text(user, "Who is \the [src.name] for?", "Gravestone Naming", null, MAX_NAME_LEN), MAX_NAME_LEN) if(carving_1) user.visible_message("[user] starts carving \the [src.name].", "You start carving \the [src.name].") @@ -27,7 +27,7 @@ user.visible_message("[user] carves something into \the [src.name].", "You carve your message into \the [src.name].") epitaph += carving_2 update_icon() - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) user.visible_message("[user] starts carving \the [src.name].", "You start carving \the [src.name].") if(do_after(user, material.hardness * W.toolspeed)) material.place_dismantled_product(get_turf(src)) diff --git a/code/game/objects/items/weapons/material/kitchen.dm b/code/game/objects/items/weapons/material/kitchen.dm index a4f3f7796f8..55b67d6b987 100644 --- a/code/game/objects/items/weapons/material/kitchen.dm +++ b/code/game/objects/items/weapons/material/kitchen.dm @@ -1,217 +1,217 @@ -/obj/item/weapon/material/kitchen - icon = 'icons/obj/kitchen.dmi' - -/* - * Utensils - */ -/obj/item/weapon/material/kitchen/utensil - drop_sound = 'sound/items/drop/knife.ogg' - pickup_sound = 'sound/items/pickup/knife.ogg' - w_class = ITEMSIZE_TINY - thrown_force_divisor = 1 - origin_tech = list(TECH_MATERIAL = 1) - attack_verb = list("attacked", "stabbed", "poked") - sharp = TRUE - edge = TRUE - force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) - thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) - var/scoop_volume = 5 - var/loaded // Name for currently loaded food object. - var/loaded_color // Color for currently loaded food object. - - var/list/food_inserted_micros - -/obj/item/weapon/material/kitchen/utensil/Initialize() - . = ..() - if (prob(60)) - src.pixel_y = rand(0, 4) - create_reagents(scoop_volume) - -/obj/item/weapon/material/kitchen/utensil/Destroy() - if(food_inserted_micros) - for(var/mob/M in food_inserted_micros) - M.dropInto(loc) - food_inserted_micros -= M - . = ..() - - return - -/obj/item/weapon/material/kitchen/utensil/update_icon() - . = ..() - cut_overlays() - if(loaded) - var/image/I = new(icon, "loadedfood") - I.color = loaded_color - add_overlay(I) - -/obj/item/weapon/material/kitchen/utensil/proc/load_food(var/mob/user, var/obj/item/weapon/reagent_containers/food/snacks/loading) - if (reagents.total_volume > 0) - to_chat(user, SPAN_DANGER("There is already something on \the [src].")) - return - if (!loading?.reagents?.total_volume) - to_chat(user, SPAN_NOTICE("Nothing to scoop up in \the [loading]!")) - - - loaded = "\the [loading]" - user.visible_message( \ - "\The [user] scoops up some of [loaded] with \the [src]!", - SPAN_NOTICE("You scoop up some of [loaded] with \the [src]!") - ) - loading.bitecount++ - loading.reagents.trans_to_obj(src, min(loading.reagents.total_volume, scoop_volume)) - loaded_color = loading.filling_color - - if(loading.food_inserted_micros && loading.food_inserted_micros.len) - if(!food_inserted_micros) - food_inserted_micros = list() - - for(var/mob/living/F in loading.food_inserted_micros) - var/do_transfer = FALSE - - if(!loading.reagents.total_volume) - do_transfer = TRUE - else - var/transfer_chance = (loading.bitecount/(loading.bitecount + (loading.bitesize / loading.reagents.total_volume) + 1))*100 - if(prob(transfer_chance)) - do_transfer = TRUE - - if(do_transfer) - F.forceMove(src) - loading.food_inserted_micros -= F - src.food_inserted_micros += F - - if (loading.reagents.total_volume <= 0) - qdel(loading) - update_icon() - -/obj/item/weapon/material/kitchen/utensil/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M)) - return ..() - - if(user.a_intent != I_HELP) - if(user.zone_sel.selecting == BP_HEAD || user.zone_sel.selecting == O_EYES) - if((CLUMSY in user.mutations) && prob(50)) - M = user - return eyestab(M,user) - else - return ..() - - if (loaded && reagents.total_volume > 0) - reagents.trans_to_mob(M, reagents.total_volume, CHEM_INGEST) - if(food_inserted_micros && food_inserted_micros.len) - for(var/mob/living/F in food_inserted_micros) - food_inserted_micros -= F - if(!F.can_be_drop_prey || !F.food_vore) - F.forceMove(get_turf(src)) - else - F.forceMove(M.vore_selected) - if(M == user) - if(!M.can_eat(loaded)) - return - M.visible_message("\The [user] eats some of [loaded] with \the [src].") - else - user.visible_message(SPAN_WARNING("\The [user] begins to feed \the [M]!")) - if(!(M.can_force_feed(user, loaded) && do_mob(user, M, 5 SECONDS))) - return - M.visible_message("\The [user] feeds some of [loaded] to \the [M] with \the [src].") - playsound(src,'sound/items/eatfood.ogg', rand(10,40), 1) - loaded = null - update_icon() - return - else - to_chat(user, SPAN_WARNING("You don't have anything on \the [src].")) //if we have help intent and no food scooped up DON'T STAB OURSELVES WITH THE FORK - return - -/obj/item/weapon/material/kitchen/utensil/on_rag_wipe() - . = ..() - if(reagents.total_volume > 0) - reagents.clear_reagents() - cut_overlays() - return - -/obj/item/weapon/material/kitchen/utensil/container_resist(mob/living/M) - if(food_inserted_micros) - food_inserted_micros -= M - M.forceMove(get_turf(src)) - to_chat(M, "You climb off of \the [src].") - -/obj/item/weapon/material/kitchen/utensil/fork - name = "fork" - desc = "It's a fork. Sure is pointy." - icon_state = "fork" - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/kitchen/utensil/fork/plastic - default_material = "plastic" - -/obj/item/weapon/material/kitchen/utensil/foon - name = "foon" - desc = "It's a foon. The forgotten cousin of the spork." - icon_state = "foon" - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/kitchen/utensil/foon/plastic - default_material = "plastic" - -/obj/item/weapon/material/kitchen/utensil/spork - name = "spork" - desc = "It's a spork. The (un)holy merger of a spoon and fork." - icon_state = "spork" - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/kitchen/utensil/spork/plastic - default_material = "plastic" - -/obj/item/weapon/material/kitchen/utensil/spoon - name = "spoon" - desc = "It's a spoon. You can see your own upside-down face in it." - icon_state = "spoon" - attack_verb = list("attacked", "poked") - edge = FALSE - sharp = FALSE - force_divisor = 0.1 //2 when wielded with weight 20 (steel) - -/obj/item/weapon/material/kitchen/utensil/spoon/plastic - default_material = "plastic" - -/* - * Knives - */ - -/* From the time of Clowns. Commented out for posterity, and sanity. -/obj/item/weapon/material/knife/attack(target as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You accidentally cut yourself with \the [src].") - user.take_organ_damage(20) - return - return ..() -*/ -/obj/item/weapon/material/knife/plastic - default_material = "plastic" - -/* - * Rolling Pins - */ - -/obj/item/weapon/material/kitchen/rollingpin - name = "rolling pin" - desc = "Used to knock out the Bartender." - icon_state = "rolling_pin" - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") - default_material = "wood" - force_divisor = 0.7 // 10 when wielded with weight 15 (wood) - dulled_divisor = 0.75 // Still a club - thrown_force_divisor = 1 // as above - drop_sound = 'sound/items/drop/wooden.ogg' - pickup_sound = 'sound/items/pickup/wooden.ogg' - -/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "\The [src] slips out of your hand and hits your head.") - user.take_organ_damage(10) - user.Paralyse(2) - return - return ..() +/obj/item/weapon/material/kitchen + icon = 'icons/obj/kitchen.dmi' + +/* + * Utensils + */ +/obj/item/weapon/material/kitchen/utensil + drop_sound = 'sound/items/drop/knife.ogg' + pickup_sound = 'sound/items/pickup/knife.ogg' + w_class = ITEMSIZE_TINY + thrown_force_divisor = 1 + origin_tech = list(TECH_MATERIAL = 1) + attack_verb = list("attacked", "stabbed", "poked") + sharp = TRUE + edge = TRUE + force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) + var/scoop_volume = 5 + var/loaded // Name for currently loaded food object. + var/loaded_color // Color for currently loaded food object. + + var/list/food_inserted_micros + +/obj/item/weapon/material/kitchen/utensil/Initialize() + . = ..() + if (prob(60)) + src.pixel_y = rand(0, 4) + create_reagents(scoop_volume) + +/obj/item/weapon/material/kitchen/utensil/Destroy() + if(food_inserted_micros) + for(var/mob/M in food_inserted_micros) + M.dropInto(loc) + food_inserted_micros -= M + . = ..() + + return + +/obj/item/weapon/material/kitchen/utensil/update_icon() + . = ..() + cut_overlays() + if(loaded) + var/image/I = new(icon, "loadedfood") + I.color = loaded_color + add_overlay(I) + +/obj/item/weapon/material/kitchen/utensil/proc/load_food(var/mob/user, var/obj/item/weapon/reagent_containers/food/snacks/loading) + if (reagents.total_volume > 0) + to_chat(user, SPAN_DANGER("There is already something on \the [src].")) + return + if (!loading?.reagents?.total_volume) + to_chat(user, SPAN_NOTICE("Nothing to scoop up in \the [loading]!")) + + + loaded = "\the [loading]" + user.visible_message( \ + "\The [user] scoops up some of [loaded] with \the [src]!", + SPAN_NOTICE("You scoop up some of [loaded] with \the [src]!") + ) + loading.bitecount++ + loading.reagents.trans_to_obj(src, min(loading.reagents.total_volume, scoop_volume)) + loaded_color = loading.filling_color + + if(loading.food_inserted_micros && loading.food_inserted_micros.len) + if(!food_inserted_micros) + food_inserted_micros = list() + + for(var/mob/living/F in loading.food_inserted_micros) + var/do_transfer = FALSE + + if(!loading.reagents.total_volume) + do_transfer = TRUE + else + var/transfer_chance = (loading.bitecount/(loading.bitecount + (loading.bitesize / loading.reagents.total_volume) + 1))*100 + if(prob(transfer_chance)) + do_transfer = TRUE + + if(do_transfer) + F.forceMove(src) + loading.food_inserted_micros -= F + src.food_inserted_micros += F + + if (loading.reagents.total_volume <= 0) + qdel(loading) + update_icon() + +/obj/item/weapon/material/kitchen/utensil/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M)) + return ..() + + if(user.a_intent != I_HELP) + if(user.zone_sel.selecting == BP_HEAD || user.zone_sel.selecting == O_EYES) + if((CLUMSY in user.mutations) && prob(50)) + M = user + return eyestab(M,user) + else + return ..() + + if (loaded && reagents.total_volume > 0) + reagents.trans_to_mob(M, reagents.total_volume, CHEM_INGEST) + if(food_inserted_micros && food_inserted_micros.len) + for(var/mob/living/F in food_inserted_micros) + food_inserted_micros -= F + if(!F.can_be_drop_prey || !F.food_vore) + F.forceMove(get_turf(src)) + else + F.forceMove(M.vore_selected) + if(M == user) + if(!M.can_eat(loaded)) + return + M.visible_message("\The [user] eats some of [loaded] with \the [src].") + else + user.visible_message(SPAN_WARNING("\The [user] begins to feed \the [M]!")) + if(!(M.can_force_feed(user, loaded) && do_mob(user, M, 5 SECONDS))) + return + M.visible_message("\The [user] feeds some of [loaded] to \the [M] with \the [src].") + playsound(src,'sound/items/eatfood.ogg', rand(10,40), 1) + loaded = null + update_icon() + return + else + to_chat(user, SPAN_WARNING("You don't have anything on \the [src].")) //if we have help intent and no food scooped up DON'T STAB OURSELVES WITH THE FORK + return + +/obj/item/weapon/material/kitchen/utensil/on_rag_wipe() + . = ..() + if(reagents.total_volume > 0) + reagents.clear_reagents() + cut_overlays() + return + +/obj/item/weapon/material/kitchen/utensil/container_resist(mob/living/M) + if(food_inserted_micros) + food_inserted_micros -= M + M.forceMove(get_turf(src)) + to_chat(M, "You climb off of \the [src].") + +/obj/item/weapon/material/kitchen/utensil/fork + name = "fork" + desc = "It's a fork. Sure is pointy." + icon_state = "fork" + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/kitchen/utensil/fork/plastic + default_material = "plastic" + +/obj/item/weapon/material/kitchen/utensil/foon + name = "foon" + desc = "It's a foon. The forgotten cousin of the spork." + icon_state = "foon" + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/kitchen/utensil/foon/plastic + default_material = "plastic" + +/obj/item/weapon/material/kitchen/utensil/spork + name = "spork" + desc = "It's a spork. The (un)holy merger of a spoon and fork." + icon_state = "spork" + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/kitchen/utensil/spork/plastic + default_material = "plastic" + +/obj/item/weapon/material/kitchen/utensil/spoon + name = "spoon" + desc = "It's a spoon. You can see your own upside-down face in it." + icon_state = "spoon" + attack_verb = list("attacked", "poked") + edge = FALSE + sharp = FALSE + force_divisor = 0.1 //2 when wielded with weight 20 (steel) + +/obj/item/weapon/material/kitchen/utensil/spoon/plastic + default_material = "plastic" + +/* + * Knives + */ + +/* From the time of Clowns. Commented out for posterity, and sanity. +/obj/item/weapon/material/knife/attack(target as mob, mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You accidentally cut yourself with \the [src].") + user.take_organ_damage(20) + return + return ..() +*/ +/obj/item/weapon/material/knife/plastic + default_material = "plastic" + +/* + * Rolling Pins + */ + +/obj/item/weapon/material/kitchen/rollingpin + name = "rolling pin" + desc = "Used to knock out the Bartender." + icon_state = "rolling_pin" + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") + default_material = "wood" + force_divisor = 0.7 // 10 when wielded with weight 15 (wood) + dulled_divisor = 0.75 // Still a club + thrown_force_divisor = 1 // as above + drop_sound = 'sound/items/drop/wooden.ogg' + pickup_sound = 'sound/items/pickup/wooden.ogg' + +/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "\The [src] slips out of your hand and hits your head.") + user.take_organ_damage(10) + user.Paralyse(2) + return + return ..() diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index a2a8075acf4..193a9d7cae4 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -1,187 +1,187 @@ -/obj/item/weapon/material/butterfly - name = "butterfly knife" - desc = "A basic metal blade concealed in a lightweight plasteel grip. Small enough when folded to fit in a pocket." - description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. - icon_state = "butterflyknife" - item_state = null - hitsound = null - var/active = 0 - w_class = ITEMSIZE_SMALL - attack_verb = list("patted", "tapped") - force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) - thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) - drop_sound = 'sound/items/drop/knife.ogg' - pickup_sound = 'sound/items/pickup/knife.ogg' - -/obj/item/weapon/material/butterfly/update_force() - if(active) - edge = TRUE - sharp = TRUE - ..() //Updates force. - throwforce = max(3,force-3) - hitsound = 'sound/weapons/bladeslice.ogg' - icon_state += "_open" - w_class = ITEMSIZE_NORMAL - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - else - force = 3 - edge = FALSE - sharp = FALSE - hitsound = initial(hitsound) - icon_state = initial(icon_state) - w_class = initial(w_class) - attack_verb = initial(attack_verb) - -/obj/item/weapon/material/butterfly/switchblade - name = "switchblade" - desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster." - icon_state = "switchblade" - -/obj/item/weapon/material/butterfly/boxcutter - name = "box cutter" - desc = "A thin, inexpensive razor-blade knife designed to open cardboard boxes." - icon_state = "boxcutter" - force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) - thrown_force_divisor = 0.2 // 4 when thrown with weight 20 (steel) - -/obj/item/weapon/material/butterfly/attack_self(mob/user) - active = !active - update_force() - - if(user) - if(active) - to_chat(user, "You flip out \the [src].") - playsound(src, 'sound/weapons/flipblade.ogg', 15, 1) - else - to_chat(user, "\The [src] can now be concealed.") - add_fingerprint(user) - -/* - * Kitchen knives - */ -/obj/item/weapon/material/knife - name = "kitchen knife" - icon = 'icons/obj/kitchen.dmi' - icon_state = "knife" - desc = "A general purpose Chef's Knife made by SpaceCook Incorporated. Guaranteed to stay sharp for years to come." - description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. - sharp = TRUE - edge = TRUE - force_divisor = 0.15 // 9 when wielded with hardness 60 (steel) - matter = list(MAT_STEEL = 12000) - origin_tech = list(TECH_MATERIAL = 1) - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - drop_sound = 'sound/items/drop/knife.ogg' - -// These no longer inherit from hatchets. -/obj/item/weapon/material/knife/tacknife - name = "tactical knife" - desc = "You'd be killing loads of people if this was Medal of Valor: Heroes of Space." - icon = 'icons/obj/weapons.dmi' - icon_state = "tacknife" - item_state = "knife" - force_divisor = 0.25 //15 when hardness 60 (steel) - attack_verb = list("stabbed", "chopped", "cut") - applies_material_colour = 1 - -/obj/item/weapon/material/knife/tacknife/combatknife - name = "combat knife" - desc = "If only you had a boot to put it in." - icon = 'icons/obj/weapons.dmi' - icon_state = "tacknife2" - item_state = "knife" - force_divisor = 0.34 // 20 with hardness 60 (steel) - thrown_force_divisor = 1.75 // 20 with weight 20 (steel) - attack_verb = list("sliced", "stabbed", "chopped", "cut") - applies_material_colour = 1 - -// Identical to the tactical knife but nowhere near as stabby. -// Kind of like the toy esword compared to the real thing. -/obj/item/weapon/material/knife/tacknife/boot - name = "boot knife" - desc = "A small fixed-blade knife for putting inside a boot." - icon = 'icons/obj/weapons.dmi' - icon_state = "tacknife3" - item_state = "knife" - force_divisor = 0.15 - applies_material_colour = 0 - -/obj/item/weapon/material/knife/hook - name = "meat hook" - desc = "A sharp, metal hook what sticks into things." - icon_state = "hook_knife" - -/obj/item/weapon/material/knife/ritual - name = "ritual knife" - desc = "The unearthly energies that once powered this blade are now dormant." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - applies_material_colour = 0 - -/obj/item/weapon/material/knife/table - name = "table knife" - icon = 'icons/obj/kitchen.dmi' - icon_state = "knife_table" - sharp = FALSE // blunted tip - force_divisor = 0.1 - -/obj/item/weapon/material/knife/table/plastic - default_material = "plastic" - -/obj/item/weapon/material/knife/butch - name = "butcher's cleaver" - icon_state = "cleaver" - desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." - force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) - attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - -/obj/item/weapon/material/knife/machete - name = "machete" - desc = "A sharp machete often found in survival kits." - icon_state = "machete" - force_divisor = 0.3 // 18 when hardness 60 (steel) - attack_verb = list("slashed", "chopped", "gouged", "ripped", "cut") - can_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned. - slot_flags = SLOT_BELT - default_material = "plasteel" //VOREStation Edit - -/obj/item/weapon/material/knife/machete/cyborg - name = "integrated machete" - desc = "A sharp machete often found attached to robots." - unbreakable = TRUE - -/obj/item/weapon/material/knife/tacknife/survival - name = "survival knife" - desc = "A hunting grade survival knife." - icon = 'icons/obj/kitchen.dmi' - icon_state = "survivalknife" - item_state = "knife" - applies_material_colour = FALSE - default_material = "plasteel" //VOREStation Edit - toolspeed = 2 // Use a real axe if you want to chop logs. - -/obj/item/weapon/material/knife/stone - name = "stone blade" - desc = "A crude blade made by chipping away at a piece of flint." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "stone_blade" - applies_material_colour = FALSE - fragile = TRUE - dulled = TRUE - edge = TRUE - sharp = TRUE - default_material = MAT_FLINT - -/obj/item/weapon/material/knife/stone/wood - name = "stone knife" - desc = "A crude blade of flint with a wooden handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." - icon_state = "stone_wood_knife" - dulled = FALSE - fragile = FALSE - -/obj/item/weapon/material/knife/stone/bone - name = "stone knife" - desc = "A crude blade of flint with a bone handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." - icon_state = "stone_bone_knife" - dulled = FALSE - fragile = FALSE +/obj/item/weapon/material/butterfly + name = "butterfly knife" + desc = "A basic metal blade concealed in a lightweight plasteel grip. Small enough when folded to fit in a pocket." + description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. + icon_state = "butterflyknife" + item_state = null + hitsound = null + var/active = 0 + w_class = ITEMSIZE_SMALL + attack_verb = list("patted", "tapped") + force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) + drop_sound = 'sound/items/drop/knife.ogg' + pickup_sound = 'sound/items/pickup/knife.ogg' + +/obj/item/weapon/material/butterfly/update_force() + if(active) + edge = TRUE + sharp = TRUE + ..() //Updates force. + throwforce = max(3,force-3) + hitsound = 'sound/weapons/bladeslice.ogg' + icon_state += "_open" + w_class = ITEMSIZE_NORMAL + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + else + force = 3 + edge = FALSE + sharp = FALSE + hitsound = initial(hitsound) + icon_state = initial(icon_state) + w_class = initial(w_class) + attack_verb = initial(attack_verb) + +/obj/item/weapon/material/butterfly/switchblade + name = "switchblade" + desc = "A classic switchblade with gold engraving. Just holding it makes you feel like a gangster." + icon_state = "switchblade" + +/obj/item/weapon/material/butterfly/boxcutter + name = "box cutter" + desc = "A thin, inexpensive razor-blade knife designed to open cardboard boxes." + icon_state = "boxcutter" + force_divisor = 0.1 // 6 when wielded with hardness 60 (steel) + thrown_force_divisor = 0.2 // 4 when thrown with weight 20 (steel) + +/obj/item/weapon/material/butterfly/attack_self(mob/user) + active = !active + update_force() + + if(user) + if(active) + to_chat(user, "You flip out \the [src].") + playsound(src, 'sound/weapons/flipblade.ogg', 15, 1) + else + to_chat(user, "\The [src] can now be concealed.") + add_fingerprint(user) + +/* + * Kitchen knives + */ +/obj/item/weapon/material/knife + name = "kitchen knife" + icon = 'icons/obj/kitchen.dmi' + icon_state = "knife" + desc = "A general purpose Chef's Knife made by SpaceCook Incorporated. Guaranteed to stay sharp for years to come." + description_fluff = "This could be used to engrave messages on suitable surfaces if you really put your mind to it! Alt-click a floor or wall to engrave with it." //This way it's not a completely hidden, arcane art to engrave. + sharp = TRUE + edge = TRUE + force_divisor = 0.15 // 9 when wielded with hardness 60 (steel) + matter = list(MAT_STEEL = 12000) + origin_tech = list(TECH_MATERIAL = 1) + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + drop_sound = 'sound/items/drop/knife.ogg' + +// These no longer inherit from hatchets. +/obj/item/weapon/material/knife/tacknife + name = "tactical knife" + desc = "You'd be killing loads of people if this was Medal of Valor: Heroes of Space." + icon = 'icons/obj/weapons.dmi' + icon_state = "tacknife" + item_state = "knife" + force_divisor = 0.25 //15 when hardness 60 (steel) + attack_verb = list("stabbed", "chopped", "cut") + applies_material_colour = 1 + +/obj/item/weapon/material/knife/tacknife/combatknife + name = "combat knife" + desc = "If only you had a boot to put it in." + icon = 'icons/obj/weapons.dmi' + icon_state = "tacknife2" + item_state = "knife" + force_divisor = 0.34 // 20 with hardness 60 (steel) + thrown_force_divisor = 1.75 // 20 with weight 20 (steel) + attack_verb = list("sliced", "stabbed", "chopped", "cut") + applies_material_colour = 1 + +// Identical to the tactical knife but nowhere near as stabby. +// Kind of like the toy esword compared to the real thing. +/obj/item/weapon/material/knife/tacknife/boot + name = "boot knife" + desc = "A small fixed-blade knife for putting inside a boot." + icon = 'icons/obj/weapons.dmi' + icon_state = "tacknife3" + item_state = "knife" + force_divisor = 0.15 + applies_material_colour = 0 + +/obj/item/weapon/material/knife/hook + name = "meat hook" + desc = "A sharp, metal hook what sticks into things." + icon_state = "hook_knife" + +/obj/item/weapon/material/knife/ritual + name = "ritual knife" + desc = "The unearthly energies that once powered this blade are now dormant." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + applies_material_colour = 0 + +/obj/item/weapon/material/knife/table + name = "table knife" + icon = 'icons/obj/kitchen.dmi' + icon_state = "knife_table" + sharp = FALSE // blunted tip + force_divisor = 0.1 + +/obj/item/weapon/material/knife/table/plastic + default_material = "plastic" + +/obj/item/weapon/material/knife/butch + name = "butcher's cleaver" + icon_state = "cleaver" + desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." + force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) + attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + +/obj/item/weapon/material/knife/machete + name = "machete" + desc = "A sharp machete often found in survival kits." + icon_state = "machete" + force_divisor = 0.3 // 18 when hardness 60 (steel) + attack_verb = list("slashed", "chopped", "gouged", "ripped", "cut") + can_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned. + slot_flags = SLOT_BELT + default_material = "plasteel" //VOREStation Edit + +/obj/item/weapon/material/knife/machete/cyborg + name = "integrated machete" + desc = "A sharp machete often found attached to robots." + unbreakable = TRUE + +/obj/item/weapon/material/knife/tacknife/survival + name = "survival knife" + desc = "A hunting grade survival knife." + icon = 'icons/obj/kitchen.dmi' + icon_state = "survivalknife" + item_state = "knife" + applies_material_colour = FALSE + default_material = "plasteel" //VOREStation Edit + toolspeed = 2 // Use a real axe if you want to chop logs. + +/obj/item/weapon/material/knife/stone + name = "stone blade" + desc = "A crude blade made by chipping away at a piece of flint." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "stone_blade" + applies_material_colour = FALSE + fragile = TRUE + dulled = TRUE + edge = TRUE + sharp = TRUE + default_material = MAT_FLINT + +/obj/item/weapon/material/knife/stone/wood + name = "stone knife" + desc = "A crude blade of flint with a wooden handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." + icon_state = "stone_wood_knife" + dulled = FALSE + fragile = FALSE + +/obj/item/weapon/material/knife/stone/bone + name = "stone knife" + desc = "A crude blade of flint with a bone handle, secured with plant fibers twined into sturdy ropes. Useful for cutting, stabbing, slicing, and even shearing." + icon_state = "stone_bone_knife" + dulled = FALSE + fragile = FALSE diff --git a/code/game/objects/items/weapons/material/material_armor.dm b/code/game/objects/items/weapons/material/material_armor.dm index 7fb5efad6ee..7f16d57a1ca 100644 --- a/code/game/objects/items/weapons/material/material_armor.dm +++ b/code/game/objects/items/weapons/material/material_armor.dm @@ -290,7 +290,7 @@ Protectiveness | Armor % unbreakable = FALSE name = "plate insert" desc = "used to craft armor plates for a plate carrier. Trim with a welder for light armor or add a second for heavy armor" - + /obj/item/weapon/material/armor_plating/attackby(var/obj/O, mob/user) if(istype(O, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/S = O @@ -323,12 +323,12 @@ Protectiveness | Armor % ..() //Make plating inserts for modular armour. -/obj/item/weapon/material/armor_plating/insert/attackby(var/obj/O, mob/user) +/obj/item/weapon/material/armor_plating/insert/attackby(var/obj/item/O, mob/user) . = ..() - if(istype(O, /obj/item/weapon/weldingtool)) - var /obj/item/weapon/weldingtool/S = O + if(O.has_tool_quality(TOOL_WELDER)) + var /obj/item/weapon/weldingtool/S = O.get_welder() if(S.remove_fuel(0,user)) if(!src || !S.isOn()) return to_chat(user, "You trim down the edges to size.") diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index 2927cb97860..b5c98143409 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -1,182 +1,182 @@ -// SEE code/modules/materials/materials.dm FOR DETAILS ON INHERITED DATUM. -// This class of weapons takes force and appearance data from a material datum. -// They are also fragile based on material data and many can break/smash apart. -/obj/item/weapon/material - health = 10 - hitsound = 'sound/weapons/bladeslice.ogg' - gender = NEUTER - throw_speed = 3 - throw_range = 7 - w_class = ITEMSIZE_NORMAL - sharp = FALSE - edge = FALSE - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_material.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_material.dmi', - ) - - var/applies_material_colour = 1 - var/unbreakable = 0 //Doesn't lose health - var/fragile = 0 //Shatters when it dies - var/dulled = 0 //Has gone dull - var/can_dull = 1 //Can it go dull? - var/force_divisor = 0.5 - var/thrown_force_divisor = 0.5 - var/dulled_divisor = 0.5 //Just drops the damage by half - var/default_material = MAT_STEEL - var/datum/material/material - var/drops_debris = 1 - -/obj/item/weapon/material/New(var/newloc, var/material_key) - ..(newloc) - if(!material_key) - material_key = default_material - set_material(material_key) - if(!material) - qdel(src) - return - - matter = material.get_matter() - if(matter.len) - for(var/material_type in matter) - if(!isnull(matter[material_type])) - matter[material_type] *= force_divisor // May require a new var instead. - - if(!(material.conductive)) - src.flags |= NOCONDUCT - -/obj/item/weapon/material/get_material() - return material - -/obj/item/weapon/material/proc/update_force() - if(edge || sharp) - force = material.get_edge_damage() - else - force = material.get_blunt_damage() - force = round(force*force_divisor) - if(dulled) - force = round(force*dulled_divisor) - throwforce = round(material.get_blunt_damage()*thrown_force_divisor) - //spawn(1) - // to_world("[src] has force [force] and throwforce [throwforce] when made from default material [material.name]") - -/obj/item/weapon/material/proc/set_material(var/new_material) - material = get_material_by_name(new_material) - if(!material) - qdel(src) - else - name = "[material.display_name] [initial(name)]" - health = round(material.integrity/10) - if(applies_material_colour) - color = material.icon_colour - if(material.products_need_process()) - START_PROCESSING(SSobj, src) - update_force() - -/obj/item/weapon/material/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/weapon/material/apply_hit_effect() - ..() - if(!unbreakable) - if(material.is_brittle()) - health = 0 - else if(!prob(material.hardness)) - health-- - check_health() - -/obj/item/weapon/material/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/whetstone)) - var/obj/item/weapon/whetstone/whet = W - repair(whet.repair_amount, whet.repair_time, user) - if(istype(W, /obj/item/weapon/material/sharpeningkit)) - var/obj/item/weapon/material/sharpeningkit/SK = W - repair(SK.repair_amount, SK.repair_time, user) - ..() - -/obj/item/weapon/material/proc/check_health(var/consumed) - if(health<=0) - health = 0 - - if(fragile) - shatter(consumed) - else if(!dulled && can_dull) - dull() - -/obj/item/weapon/material/proc/shatter(var/consumed) - var/turf/T = get_turf(src) - T.visible_message("\The [src] [material.destruction_desc]!") - if(istype(loc, /mob/living)) - var/mob/living/M = loc - M.drop_from_inventory(src) - playsound(src, "shatter", 70, 1) - if(!consumed && drops_debris) material.place_shard(T) - qdel(src) - -/obj/item/weapon/material/proc/dull() - var/turf/T = get_turf(src) - T.visible_message("\The [src] goes dull!") - playsound(src, "shatter", 70, 1) - dulled = 1 - if(is_sharp() || has_edge()) - sharp = FALSE - edge = FALSE - -/obj/item/weapon/material/proc/repair(var/repair_amount, var/repair_time, mob/living/user) - if(!fragile) - if(health < initial(health)) - user.visible_message("[user] begins repairing \the [src].", "You begin repairing \the [src].") - if(do_after(user, repair_time)) - user.visible_message("[user] has finished repairing \the [src]", "You finish repairing \the [src].") - health = min(health + repair_amount, initial(health)) - dulled = 0 - sharp = initial(sharp) - edge = initial(edge) - else - to_chat(user, "[src] doesn't need repairs.") - else - to_chat(user, "You can't repair \the [src].") - return - -/obj/item/weapon/material/proc/sharpen(var/material, var/sharpen_time, var/kit, mob/living/M) - if(!fragile) - if(health < initial(health)) - to_chat(M, "You should repair [src] first. Try using [kit] on it.") - return FALSE - M.visible_message("[M] begins to replace parts of [src] with [kit].", "You begin to replace parts of [src] with [kit].") - if(do_after(usr, sharpen_time)) - M.visible_message("[M] has finished replacing parts of [src].", "You finish replacing parts of [src].") - src.set_material(material) - return TRUE - else - to_chat(M, "You can't sharpen and re-edge [src].") - return FALSE - -/* -Commenting this out pending rebalancing of radiation based on small objects. -/obj/item/weapon/material/process() - if(!material.radioactivity) - return - for(var/mob/living/L in range(1,src)) - L.apply_effect(round(material.radioactivity/30),IRRADIATE,0) -*/ - -/* -// Commenting this out while fires are so spectacularly lethal, as I can't seem to get this balanced appropriately. -/obj/item/weapon/material/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - TemperatureAct(exposed_temperature) - -// This might need adjustment. Will work that out later. -/obj/item/weapon/material/proc/TemperatureAct(temperature) - health -= material.combustion_effect(get_turf(src), temperature, 0.1) - check_health(1) - -/obj/item/weapon/material/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(material.ignition_point && WT.remove_fuel(0, user)) - TemperatureAct(150) - else - return ..() -*/ +// SEE code/modules/materials/materials.dm FOR DETAILS ON INHERITED DATUM. +// This class of weapons takes force and appearance data from a material datum. +// They are also fragile based on material data and many can break/smash apart. +/obj/item/weapon/material + health = 10 + hitsound = 'sound/weapons/bladeslice.ogg' + gender = NEUTER + throw_speed = 3 + throw_range = 7 + w_class = ITEMSIZE_NORMAL + sharp = FALSE + edge = FALSE + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_material.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_material.dmi', + ) + + var/applies_material_colour = 1 + var/unbreakable = 0 //Doesn't lose health + var/fragile = 0 //Shatters when it dies + var/dulled = 0 //Has gone dull + var/can_dull = 1 //Can it go dull? + var/force_divisor = 0.5 + var/thrown_force_divisor = 0.5 + var/dulled_divisor = 0.5 //Just drops the damage by half + var/default_material = MAT_STEEL + var/datum/material/material + var/drops_debris = 1 + +/obj/item/weapon/material/New(var/newloc, var/material_key) + ..(newloc) + if(!material_key) + material_key = default_material + set_material(material_key) + if(!material) + qdel(src) + return + + matter = material.get_matter() + if(matter.len) + for(var/material_type in matter) + if(!isnull(matter[material_type])) + matter[material_type] *= force_divisor // May require a new var instead. + + if(!(material.conductive)) + src.flags |= NOCONDUCT + +/obj/item/weapon/material/get_material() + return material + +/obj/item/weapon/material/proc/update_force() + if(edge || sharp) + force = material.get_edge_damage() + else + force = material.get_blunt_damage() + force = round(force*force_divisor) + if(dulled) + force = round(force*dulled_divisor) + throwforce = round(material.get_blunt_damage()*thrown_force_divisor) + //spawn(1) + // to_world("[src] has force [force] and throwforce [throwforce] when made from default material [material.name]") + +/obj/item/weapon/material/proc/set_material(var/new_material) + material = get_material_by_name(new_material) + if(!material) + qdel(src) + else + name = "[material.display_name] [initial(name)]" + health = round(material.integrity/10) + if(applies_material_colour) + color = material.icon_colour + if(material.products_need_process()) + START_PROCESSING(SSobj, src) + update_force() + +/obj/item/weapon/material/Destroy() + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/weapon/material/apply_hit_effect() + ..() + if(!unbreakable) + if(material.is_brittle()) + health = 0 + else if(!prob(material.hardness)) + health-- + check_health() + +/obj/item/weapon/material/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/whetstone)) + var/obj/item/weapon/whetstone/whet = W + repair(whet.repair_amount, whet.repair_time, user) + if(istype(W, /obj/item/weapon/material/sharpeningkit)) + var/obj/item/weapon/material/sharpeningkit/SK = W + repair(SK.repair_amount, SK.repair_time, user) + ..() + +/obj/item/weapon/material/proc/check_health(var/consumed) + if(health<=0) + health = 0 + + if(fragile) + shatter(consumed) + else if(!dulled && can_dull) + dull() + +/obj/item/weapon/material/proc/shatter(var/consumed) + var/turf/T = get_turf(src) + T.visible_message("\The [src] [material.destruction_desc]!") + if(istype(loc, /mob/living)) + var/mob/living/M = loc + M.drop_from_inventory(src) + playsound(src, "shatter", 70, 1) + if(!consumed && drops_debris) material.place_shard(T) + qdel(src) + +/obj/item/weapon/material/proc/dull() + var/turf/T = get_turf(src) + T.visible_message("\The [src] goes dull!") + playsound(src, "shatter", 70, 1) + dulled = 1 + if(is_sharp() || has_edge()) + sharp = FALSE + edge = FALSE + +/obj/item/weapon/material/proc/repair(var/repair_amount, var/repair_time, mob/living/user) + if(!fragile) + if(health < initial(health)) + user.visible_message("[user] begins repairing \the [src].", "You begin repairing \the [src].") + if(do_after(user, repair_time)) + user.visible_message("[user] has finished repairing \the [src]", "You finish repairing \the [src].") + health = min(health + repair_amount, initial(health)) + dulled = 0 + sharp = initial(sharp) + edge = initial(edge) + else + to_chat(user, "[src] doesn't need repairs.") + else + to_chat(user, "You can't repair \the [src].") + return + +/obj/item/weapon/material/proc/sharpen(var/material, var/sharpen_time, var/kit, mob/living/M) + if(!fragile && src.material.can_sharpen) + if(health < initial(health)) + to_chat(M, "You should repair [src] first. Try using [kit] on it.") + return FALSE + M.visible_message("[M] begins to replace parts of [src] with [kit].", "You begin to replace parts of [src] with [kit].") + if(do_after(usr, sharpen_time)) + M.visible_message("[M] has finished replacing parts of [src].", "You finish replacing parts of [src].") + src.set_material(material) + return TRUE + else + to_chat(M, "You can't sharpen and re-edge [src].") + return FALSE + +/* +Commenting this out pending rebalancing of radiation based on small objects. +/obj/item/weapon/material/process() + if(!material.radioactivity) + return + for(var/mob/living/L in range(1,src)) + L.apply_effect(round(material.radioactivity/30),IRRADIATE,0) +*/ + +/* +// Commenting this out while fires are so spectacularly lethal, as I can't seem to get this balanced appropriately. +/obj/item/weapon/material/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + TemperatureAct(exposed_temperature) + +// This might need adjustment. Will work that out later. +/obj/item/weapon/material/proc/TemperatureAct(temperature) + health -= material.combustion_effect(get_turf(src), temperature, 0.1) + check_health(1) + +/obj/item/weapon/material/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(material.ignition_point && WT.remove_fuel(0, user)) + TemperatureAct(150) + else + return ..() +*/ diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index 10c57abf854..09d15a03cd4 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -1,228 +1,228 @@ -/obj/item/weapon/material/harpoon - name = "harpoon" - sharp = TRUE - edge = FALSE - desc = "Tharr she blows!" - icon_state = "harpoon" - item_state = "harpoon" - force_divisor = 0.3 // 18 with hardness 60 (steel) - attack_verb = list("jabbed","stabbed","ripped") - -/obj/item/weapon/material/knife/machete/hatchet - name = "hatchet" - desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." - icon = 'icons/obj/weapons.dmi' - icon_state = "hatchet" - force_divisor = 0.2 // 12 with hardness 60 (steel) - thrown_force_divisor = 0.75 // 15 with weight 20 (steel) - w_class = ITEMSIZE_SMALL - sharp = TRUE - edge = TRUE - origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) - attack_verb = list("chopped", "torn", "cut") - applies_material_colour = 0 - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' -/* VOREStation Removal - We have one already -/obj/item/weapon/material/knife/machete/hatchet/stone - name = "sharp rock" - desc = "The secret is to bang the rocks together, guys." - force_divisor = 0.2 - icon_state = "rock" - item_state = "rock" - attack_verb = list("chopped", "torn", "cut") - -/obj/item/weapon/material/knife/machete/hatchet/stone/set_material(var/new_material) - var/old_name = name - . = ..() - name = old_name -*/ -/obj/item/weapon/material/knife/machete/hatchet/unathiknife - name = "duelling knife" - desc = "A length of leather-bound wood studded with razor-sharp teeth. How crude." - icon = 'icons/obj/weapons.dmi' - icon_state = "unathiknife" - attack_verb = list("ripped", "torn", "cut") - can_cleave = FALSE - var/hits = 0 - -/obj/item/weapon/material/knife/machete/hatchet/unathiknife/attack(mob/M as mob, mob/user as mob) - if(hits > 0) - return - var/obj/item/I = user.get_inactive_hand() - if(istype(I, /obj/item/weapon/material/knife/machete/hatchet/unathiknife)) - hits ++ - var/obj/item/weapon/W = I - W.attack(M, user) - W.afterattack(M, user) - ..() - -/obj/item/weapon/material/knife/machete/hatchet/unathiknife/afterattack(mob/M as mob, mob/user as mob) - hits = initial(hits) - ..() - -/obj/item/weapon/material/minihoe // -- Numbers - name = "mini hoe" - desc = "It's used for removing weeds or scratching your back." - icon = 'icons/obj/weapons.dmi' - icon_state = "hoe" - force_divisor = 0.25 // 5 with weight 20 (steel) - thrown_force_divisor = 0.25 // as above - dulled_divisor = 0.75 //Still metal on a long pole - w_class = ITEMSIZE_SMALL - attack_verb = list("slashed", "sliced", "cut", "clawed") - -/obj/item/weapon/material/snow/snowball - name = "loose packed snowball" - desc = "A fun snowball. Throw it at your friends!" - icon = 'icons/obj/weapons.dmi' - icon_state = "snowball" - default_material = MAT_SNOW - health = 1 - fragile = 1 - force_divisor = 0.01 - thrown_force_divisor = 0.10 - w_class = ITEMSIZE_SMALL - attack_verb = list("mushed", "splatted", "splooshed", "splushed") // Words that totally exist. - -/obj/item/weapon/material/snow/snowball/attack_self(mob/user as mob) - if(user.a_intent == I_HURT) - to_chat(user, SPAN_NOTICE("You smash the snowball in your hand.")) - var/atom/S = new /obj/item/stack/material/snow(user.loc) - qdel(src) - user.put_in_hands(S) - else - to_chat(user, SPAN_NOTICE("You start compacting the snowball.")) - if(do_after(user, 2 SECONDS)) - var/atom/S = new /obj/item/weapon/material/snow/snowball/reinforced(user.loc) - qdel(src) - user.put_in_hands(S) - -/obj/item/weapon/material/snow/snowball/reinforced - name = "snowball" - desc = "A well-formed and fun snowball. It looks kind of dangerous." - //icon_state = "reinf-snowball" - force_divisor = 0.20 - thrown_force_divisor = 0.25 - -/obj/item/weapon/material/whip - name = "whip" - desc = "A tool used to discipline animals, or look cool. Mostly the latter." - description_info = "Help - Standard attack, no modifiers.
                    \ - Disarm - Disarming strike. Attempts to disarm the target at range, similar to an unarmed disarm. Additionally, will force the target (if possible) to move away from you.
                    \ - Grab - Grappling strike. Attempts to pull the target toward you. This can also move objects.
                    \ - Harm - A standard strike with a small chance to disarm." - icon = 'icons/obj/weapons.dmi' - icon_state = "whip" - item_state = "chain" - default_material = MAT_LEATHER - slot_flags = SLOT_BELT - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_COMBAT = 2) - attack_verb = list("flogged", "whipped", "lashed", "disciplined") - force_divisor = 0.15 - thrown_force_divisor = 0.25 - reach = 2 - - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/material/whip/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - ..() - - if(!proximity) - return - - if(istype(A, /atom/movable)) - var/atom/movable/AM = A - if(AM.anchored) - to_chat(user, "\The [AM] won't budge.") - return - - else - if(!istype(AM, /obj/item)) - user.visible_message("\The [AM] is pulled along by \the [src]!") - AM.Move(get_step(AM, get_dir(AM, src))) - return - - else - user.visible_message("\The [AM] is snatched by \the [src]!") - AM.throw_at(user, reach, 0.1, user) - -/obj/item/weapon/material/whip/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) - if(user.a_intent) - switch(user.a_intent) - if(I_HURT) - if(prob(10) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) - to_chat(target, "\The [src] rips at your hands!") - ranged_disarm(target) - if(I_DISARM) - if(prob(min(90, force * 3)) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) - ranged_disarm(target) - else - target.visible_message("\The [src] sends \the [target] stumbling away.") - target.Move(get_step(target,get_dir(user,target))) - if(I_GRAB) - var/turf/STurf = get_turf(target) - spawn(2) - playsound(STurf, 'sound/effects/snap.ogg', 60, 1) - target.visible_message("\The [src] yanks \the [target] towards \the [user]!") - target.throw_at(get_turf(get_step(user,get_dir(user,target))), 2, 1, src) - - ..() - -/obj/item/weapon/material/whip/proc/ranged_disarm(var/mob/living/carbon/human/H, var/mob/living/user) - if(istype(H)) - var/list/holding = list(H.get_active_hand() = 40, H.get_inactive_hand() = 20) - - if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) - for(var/obj/item/weapon/gun/W in holding) - if(W && prob(holding[W])) - var/list/turfs = list() - for(var/turf/T in view()) - turfs += T - if(turfs.len) - var/turf/target = pick(turfs) - visible_message("[H]'s [W] goes off due to \the [src]!") - return W.afterattack(target,H) - - if(!(H.species.flags & NO_SLIP) && prob(10) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))) - var/armor_check = H.run_armor_check(user.zone_sel, "melee") - H.apply_effect(3, WEAKEN, armor_check) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - if(armor_check < 60) - visible_message("\The [src] has tripped [H]!") - else - visible_message("\The [src] attempted to trip [H]!") - return - - else - if(H.break_all_grabs(user)) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - - if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) - for(var/obj/item/I in holding) - if(I && prob(holding[I])) - H.drop_from_inventory(I) - visible_message("\The [src] has disarmed [H]!") - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - -/obj/item/weapon/material/whip/attack_self(mob/user) - user.visible_message("\The [user] cracks \the [src]!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - -/obj/item/weapon/material/knife/machete/hatchet/stone - name = "hatchet" - desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "stone_wood_axe" - default_material = MAT_FLINT - origin_tech = list() - applies_material_colour = FALSE - -/obj/item/weapon/material/knife/machete/hatchet/stone/bone - icon_state = "stone_bone_axe" +/obj/item/weapon/material/harpoon + name = "harpoon" + sharp = TRUE + edge = FALSE + desc = "Tharr she blows!" + icon_state = "harpoon" + item_state = "harpoon" + force_divisor = 0.3 // 18 with hardness 60 (steel) + attack_verb = list("jabbed","stabbed","ripped") + +/obj/item/weapon/material/knife/machete/hatchet + name = "hatchet" + desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." + icon = 'icons/obj/weapons.dmi' + icon_state = "hatchet" + force_divisor = 0.2 // 12 with hardness 60 (steel) + thrown_force_divisor = 0.75 // 15 with weight 20 (steel) + w_class = ITEMSIZE_SMALL + sharp = TRUE + edge = TRUE + origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) + attack_verb = list("chopped", "torn", "cut") + applies_material_colour = 0 + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' +/* VOREStation Removal - We have one already +/obj/item/weapon/material/knife/machete/hatchet/stone + name = "sharp rock" + desc = "The secret is to bang the rocks together, guys." + force_divisor = 0.2 + icon_state = "rock" + item_state = "rock" + attack_verb = list("chopped", "torn", "cut") + +/obj/item/weapon/material/knife/machete/hatchet/stone/set_material(var/new_material) + var/old_name = name + . = ..() + name = old_name +*/ +/obj/item/weapon/material/knife/machete/hatchet/unathiknife + name = "duelling knife" + desc = "A length of leather-bound wood studded with razor-sharp teeth. How crude." + icon = 'icons/obj/weapons.dmi' + icon_state = "unathiknife" + attack_verb = list("ripped", "torn", "cut") + can_cleave = FALSE + var/hits = 0 + +/obj/item/weapon/material/knife/machete/hatchet/unathiknife/attack(mob/M as mob, mob/user as mob) + if(hits > 0) + return + var/obj/item/I = user.get_inactive_hand() + if(istype(I, /obj/item/weapon/material/knife/machete/hatchet/unathiknife)) + hits ++ + var/obj/item/weapon/W = I + W.attack(M, user) + W.afterattack(M, user) + ..() + +/obj/item/weapon/material/knife/machete/hatchet/unathiknife/afterattack(mob/M as mob, mob/user as mob) + hits = initial(hits) + ..() + +/obj/item/weapon/material/minihoe // -- Numbers + name = "mini hoe" + desc = "It's used for removing weeds or scratching your back." + icon = 'icons/obj/weapons.dmi' + icon_state = "hoe" + force_divisor = 0.25 // 5 with weight 20 (steel) + thrown_force_divisor = 0.25 // as above + dulled_divisor = 0.75 //Still metal on a long pole + w_class = ITEMSIZE_SMALL + attack_verb = list("slashed", "sliced", "cut", "clawed") + +/obj/item/weapon/material/snow/snowball + name = "loose packed snowball" + desc = "A fun snowball. Throw it at your friends!" + icon = 'icons/obj/weapons.dmi' + icon_state = "snowball" + default_material = MAT_SNOW + health = 1 + fragile = 1 + force_divisor = 0.01 + thrown_force_divisor = 0.10 + w_class = ITEMSIZE_SMALL + attack_verb = list("mushed", "splatted", "splooshed", "splushed") // Words that totally exist. + +/obj/item/weapon/material/snow/snowball/attack_self(mob/user as mob) + if(user.a_intent == I_HURT) + to_chat(user, SPAN_NOTICE("You smash the snowball in your hand.")) + var/atom/S = new /obj/item/stack/material/snow(user.loc) + qdel(src) + user.put_in_hands(S) + else + to_chat(user, SPAN_NOTICE("You start compacting the snowball.")) + if(do_after(user, 2 SECONDS)) + var/atom/S = new /obj/item/weapon/material/snow/snowball/reinforced(user.loc) + qdel(src) + user.put_in_hands(S) + +/obj/item/weapon/material/snow/snowball/reinforced + name = "snowball" + desc = "A well-formed and fun snowball. It looks kind of dangerous." + //icon_state = "reinf-snowball" + force_divisor = 0.20 + thrown_force_divisor = 0.25 + +/obj/item/weapon/material/whip + name = "whip" + desc = "A tool used to discipline animals, or look cool. Mostly the latter." + description_info = "Help - Standard attack, no modifiers.
                    \ + Disarm - Disarming strike. Attempts to disarm the target at range, similar to an unarmed disarm. Additionally, will force the target (if possible) to move away from you.
                    \ + Grab - Grappling strike. Attempts to pull the target toward you. This can also move objects.
                    \ + Harm - A standard strike with a small chance to disarm." + icon = 'icons/obj/weapons.dmi' + icon_state = "whip" + item_state = "chain" + default_material = MAT_LEATHER + slot_flags = SLOT_BELT + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_COMBAT = 2) + attack_verb = list("flogged", "whipped", "lashed", "disciplined") + force_divisor = 0.15 + thrown_force_divisor = 0.25 + reach = 2 + + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/material/whip/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + ..() + + if(!proximity) + return + + if(istype(A, /atom/movable)) + var/atom/movable/AM = A + if(AM.anchored) + to_chat(user, "\The [AM] won't budge.") + return + + else + if(!istype(AM, /obj/item)) + user.visible_message("\The [AM] is pulled along by \the [src]!") + AM.Move(get_step(AM, get_dir(AM, src))) + return + + else + user.visible_message("\The [AM] is snatched by \the [src]!") + AM.throw_at(user, reach, 0.1, user) + +/obj/item/weapon/material/whip/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + if(user.a_intent) + switch(user.a_intent) + if(I_HURT) + if(prob(10) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) + to_chat(target, "\The [src] rips at your hands!") + ranged_disarm(target) + if(I_DISARM) + if(prob(min(90, force * 3)) && istype(target, /mob/living/carbon/human) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND))) + ranged_disarm(target) + else + target.visible_message("\The [src] sends \the [target] stumbling away.") + target.Move(get_step(target,get_dir(user,target))) + if(I_GRAB) + var/turf/STurf = get_turf(target) + spawn(2) + playsound(STurf, 'sound/effects/snap.ogg', 60, 1) + target.visible_message("\The [src] yanks \the [target] towards \the [user]!") + target.throw_at(get_turf(get_step(user,get_dir(user,target))), 2, 1, src) + + ..() + +/obj/item/weapon/material/whip/proc/ranged_disarm(var/mob/living/carbon/human/H, var/mob/living/user) + if(istype(H)) + var/list/holding = list(H.get_active_hand() = 40, H.get_inactive_hand() = 20) + + if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) + for(var/obj/item/weapon/gun/W in holding) + if(W && prob(holding[W])) + var/list/turfs = list() + for(var/turf/T in view()) + turfs += T + if(turfs.len) + var/turf/target = pick(turfs) + visible_message("[H]'s [W] goes off due to \the [src]!") + return W.afterattack(target,H) + + if(!(H.species.flags & NO_SLIP) && prob(10) && (user.zone_sel in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))) + var/armor_check = H.run_armor_check(user.zone_sel, "melee") + H.apply_effect(3, WEAKEN, armor_check) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + if(armor_check < 60) + visible_message("\The [src] has tripped [H]!") + else + visible_message("\The [src] attempted to trip [H]!") + return + + else + if(H.break_all_grabs(user)) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + + if(user.zone_sel in list(BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND)) + for(var/obj/item/I in holding) + if(I && prob(holding[I])) + H.drop_from_inventory(I) + visible_message("\The [src] has disarmed [H]!") + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + +/obj/item/weapon/material/whip/attack_self(mob/user) + user.visible_message("\The [user] cracks \the [src]!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + +/obj/item/weapon/material/knife/machete/hatchet/stone + name = "hatchet" + desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "stone_wood_axe" + default_material = MAT_FLINT + origin_tech = list() + applies_material_colour = FALSE + +/obj/item/weapon/material/knife/machete/hatchet/stone/bone + icon_state = "stone_bone_axe" diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index 59cc5f3eddd..5f9e2808cd6 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -1,151 +1,151 @@ -// Glass shards - -/obj/item/weapon/material/shard - name = "shard" - icon = 'icons/obj/shards.dmi' - desc = "Made of nothing. How does this even exist?" // set based on material, if this desc is visible it's a bug (shards default to being made of glass) - icon_state = "large" - randpixel = 8 - sharp = TRUE - edge = TRUE - w_class = ITEMSIZE_SMALL - force_divisor = 0.25 // 7.5 with hardness 30 (glass) - thrown_force_divisor = 0.5 - item_state = "shard-glass" - attack_verb = list("stabbed", "slashed", "sliced", "cut") - default_material = "glass" - unbreakable = 1 //It's already broken. - drops_debris = 0 - -/obj/item/weapon/material/shard/set_material(var/new_material) - ..(new_material) - if(!istype(material)) - return - - icon_state = "[material.shard_icon][pick("large", "medium", "small")]" - randpixel_xy() - update_icon() - - if(material.shard_type) - name = "[material.display_name] [material.shard_type]" - desc = "A small piece of [material.display_name]. It looks sharp, you wouldn't want to step on it barefoot. Could probably be used as ... a throwing weapon?" - switch(material.shard_type) - if(SHARD_SPLINTER, SHARD_SHRAPNEL) - gender = PLURAL - else - gender = NEUTER - else - qdel(src) - -/obj/item/weapon/material/shard/update_icon() - if(material) - color = material.icon_colour - // 1-(1-x)^2, so that glass shards with 0.3 opacity end up somewhat visible at 0.51 opacity - alpha = 255 * (1 - (1 - material.opacity)*(1 - material.opacity)) - else - color = "#ffffff" - alpha = 255 - -/obj/item/weapon/material/shard/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/weldingtool) && material.shard_can_repair) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(0, user)) - material.place_sheet(loc) - qdel(src) - return - return ..() - -/obj/item/weapon/material/shard/afterattack(var/atom/target, mob/living/carbon/human/user as mob, proximity) - if(!proximity) - return - var/active_hand //hand the shard is in - var/will_break = FALSE - var/protected_hands = FALSE //this is a fucking mess - var/break_damage = 4 - var/light_glove_d = rand(2, 4) - var/no_glove_d = rand(4, 6) - var/list/forbidden_gloves = list( - /obj/item/clothing/gloves/sterile, - /obj/item/clothing/gloves/knuckledusters - ) - - if(src == user.l_hand) - active_hand = BP_L_HAND - else if(src == user.r_hand) - active_hand = BP_R_HAND - else - return // If it's not actually in our hands anymore, we were probably gentle with it - - active_hand = (src == user.l_hand) ? BP_L_HAND : BP_R_HAND // May not actually be faster than an if-else block, but a little bit cleaner -Ater - - if(prob(75)) - will_break = TRUE - - if(user.gloves && (user.gloves.body_parts_covered & HANDS) && istype(user.gloves, /obj/item/clothing/gloves)) // Not-gloves aren't gloves, and therefore don't protect us - protected_hands = TRUE // If we're wearing gloves we can probably handle it just fine - for(var/I in forbidden_gloves) - if(istype(user.gloves, I)) // forbidden_gloves is a blacklist, so if we match anything in there, our hands are not protected - protected_hands = FALSE - break - - if(user.gloves && !protected_hands) - to_chat(user, "\The [src] partially cuts into your hand through your gloves as you hit \the [target]!") - user.apply_damage(light_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) // Ternary to include break damage - - else if(!user.gloves) - to_chat(user, "\The [src] cuts into your hand as you hit \the [target]!") - user.apply_damage(no_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) - - if(will_break && src.loc == user) // If it's not in our hand anymore - user.visible_message("[user] hit \the [target] with \the [src], shattering it!", "You shatter \the [src] in your hand!") - playsound(src, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 30, 1) - qdel(src) - return - -/obj/item/weapon/material/shard/Crossed(atom/movable/AM as mob|obj) - ..() - if(AM.is_incorporeal()) - return - if(isliving(AM)) - var/mob/M = AM - - if(M.buckled) //wheelchairs, office chairs, rollerbeds - return - - playsound(src, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds - if(ishuman(M)) - var/mob/living/carbon/human/H = M - - if(H.species.siemens_coefficient<0.5) //Thick skin. - return - - if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) ) - return - - if(H.species.flags & NO_MINOR_CUT) - return - - to_chat(H, "You step on \the [src]!") - - var/list/check = list("l_foot", "r_foot") - while(check.len) - var/picked = pick(check) - var/obj/item/organ/external/affecting = H.get_organ(picked) - if(affecting) - if(affecting.robotic >= ORGAN_ROBOT) - return - if(affecting.take_damage(force, 0)) - H.UpdateDamageIcon() - H.updatehealth() - if(affecting.organ_can_feel_pain()) - H.Weaken(3) - return - check -= picked - return - -// Preset types - left here for the code that uses them -/obj/item/weapon/material/shard/shrapnel/New(loc) - ..(loc, "steel") - -/obj/item/weapon/material/shard/phoron/New(loc) - ..(loc, "borosilicate glass") +// Glass shards + +/obj/item/weapon/material/shard + name = "shard" + icon = 'icons/obj/shards.dmi' + desc = "Made of nothing. How does this even exist?" // set based on material, if this desc is visible it's a bug (shards default to being made of glass) + icon_state = "large" + randpixel = 8 + sharp = TRUE + edge = TRUE + w_class = ITEMSIZE_SMALL + force_divisor = 0.25 // 7.5 with hardness 30 (glass) + thrown_force_divisor = 0.5 + item_state = "shard-glass" + attack_verb = list("stabbed", "slashed", "sliced", "cut") + default_material = "glass" + unbreakable = 1 //It's already broken. + drops_debris = 0 + +/obj/item/weapon/material/shard/set_material(var/new_material) + ..(new_material) + if(!istype(material)) + return + + icon_state = "[material.shard_icon][pick("large", "medium", "small")]" + randpixel_xy() + update_icon() + + if(material.shard_type) + name = "[material.display_name] [material.shard_type]" + desc = "A small piece of [material.display_name]. It looks sharp, you wouldn't want to step on it barefoot. Could probably be used as ... a throwing weapon?" + switch(material.shard_type) + if(SHARD_SPLINTER, SHARD_SHRAPNEL) + gender = PLURAL + else + gender = NEUTER + else + qdel(src) + +/obj/item/weapon/material/shard/update_icon() + if(material) + color = material.icon_colour + // 1-(1-x)^2, so that glass shards with 0.3 opacity end up somewhat visible at 0.51 opacity + alpha = 255 * (1 - (1 - material.opacity)*(1 - material.opacity)) + else + color = "#ffffff" + alpha = 255 + +/obj/item/weapon/material/shard/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER) && material.shard_can_repair) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(WT.remove_fuel(0, user)) + material.place_sheet(loc, 1) + qdel(src) + return + return ..() + +/obj/item/weapon/material/shard/afterattack(var/atom/target, mob/living/carbon/human/user as mob, proximity) + if(!proximity) + return + var/active_hand //hand the shard is in + var/will_break = FALSE + var/protected_hands = FALSE //this is a fucking mess + var/break_damage = 4 + var/light_glove_d = rand(2, 4) + var/no_glove_d = rand(4, 6) + var/list/forbidden_gloves = list( + /obj/item/clothing/gloves/sterile, + /obj/item/clothing/gloves/knuckledusters + ) + + if(src == user.l_hand) + active_hand = BP_L_HAND + else if(src == user.r_hand) + active_hand = BP_R_HAND + else + return // If it's not actually in our hands anymore, we were probably gentle with it + + active_hand = (src == user.l_hand) ? BP_L_HAND : BP_R_HAND // May not actually be faster than an if-else block, but a little bit cleaner -Ater + + if(prob(75)) + will_break = TRUE + + if(user.gloves && (user.gloves.body_parts_covered & HANDS) && istype(user.gloves, /obj/item/clothing/gloves)) // Not-gloves aren't gloves, and therefore don't protect us + protected_hands = TRUE // If we're wearing gloves we can probably handle it just fine + for(var/I in forbidden_gloves) + if(istype(user.gloves, I)) // forbidden_gloves is a blacklist, so if we match anything in there, our hands are not protected + protected_hands = FALSE + break + + if(user.gloves && !protected_hands) + to_chat(user, "\The [src] partially cuts into your hand through your gloves as you hit \the [target]!") + user.apply_damage(light_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) // Ternary to include break damage + + else if(!user.gloves) + to_chat(user, "\The [src] cuts into your hand as you hit \the [target]!") + user.apply_damage(no_glove_d + will_break ? break_damage : 0, BRUTE, active_hand, 0, 0, src, src.sharp, src.edge) + + if(will_break && src.loc == user) // If it's not in our hand anymore + user.visible_message("[user] hit \the [target] with \the [src], shattering it!", "You shatter \the [src] in your hand!") + playsound(src, pick('sound/effects/Glassbr1.ogg', 'sound/effects/Glassbr2.ogg', 'sound/effects/Glassbr3.ogg'), 30, 1) + qdel(src) + return + +/obj/item/weapon/material/shard/Crossed(atom/movable/AM as mob|obj) + ..() + if(AM.is_incorporeal()) + return + if(isliving(AM)) + var/mob/M = AM + + if(M.buckled) //wheelchairs, office chairs, rollerbeds + return + + playsound(src, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds + if(ishuman(M)) + var/mob/living/carbon/human/H = M + + if(H.species.siemens_coefficient<0.5) //Thick skin. + return + + if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) ) + return + + if(H.species.flags & NO_MINOR_CUT) + return + + to_chat(H, "You step on \the [src]!") + + var/list/check = list("l_foot", "r_foot") + while(check.len) + var/picked = pick(check) + var/obj/item/organ/external/affecting = H.get_organ(picked) + if(affecting) + if(affecting.robotic >= ORGAN_ROBOT) + return + if(affecting.take_damage(force, 0)) + H.UpdateDamageIcon() + H.updatehealth() + if(affecting.organ_can_feel_pain()) + H.Weaken(3) + return + check -= picked + return + +// Preset types - left here for the code that uses them +/obj/item/weapon/material/shard/shrapnel/New(loc) + ..(loc, "steel") + +/obj/item/weapon/material/shard/phoron/New(loc) + ..(loc, "borosilicate glass") diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index e0a75ca8bd7..8aac4dca4bb 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -1,86 +1,86 @@ -/obj/item/weapon/material/sword - name = "claymore" - desc = "What are you standing around staring at this for? Get to killing!" - icon_state = "claymore" - slot_flags = SLOT_BELT - force_divisor = 0.7 // 42 when wielded with hardnes 60 (steel) - thrown_force_divisor = 0.5 // 10 when thrown with weight 20 (steel) - sharp = TRUE - edge = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - -/obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(unique_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/material/sword/katana - name = "katana" - desc = "Woefully underpowered in D20. This one looks pretty sharp." - icon_state = "katana" - slot_flags = SLOT_BELT | SLOT_BACK - -/obj/item/weapon/material/sword/katana/caneblade - name = "cane blade" - desc = "Used for making people fall over instead of helping them stand up." - icon_state = "caneblade" - item_state = "caneblade" - -/obj/item/weapon/material/sword/rapier - name = "rapier" - desc = "A slender, fancy and sharply pointed sword." - icon_state = "rapier" - item_state = "rapier" - slot_flags = SLOT_BELT - applies_material_colour = 0 - attack_verb = list("attacked", "stabbed", "prodded", "poked", "lunged") - edge = 0 //rapiers are pointy, but not cutty like other swords - -/obj/item/weapon/material/sword/longsword - name = "longsword" - desc = "A double-edged large blade." - icon_state = "longsword" - item_state = "longsword" - applies_material_colour = 0 - slot_flags = SLOT_BELT | SLOT_BACK - can_cleave = TRUE - -/obj/item/weapon/material/sword/sabre - name = "sabre" - desc = "A sharp curved sword, a favored weapon of pirates far in the past." - icon_state = "sabre" - item_state = "sabre" - applies_material_colour = 0 //messes up the hilt color otherwise - slot_flags = SLOT_BELT - -/obj/item/weapon/material/sword/battleaxe - name = "battleaxe" - desc = "A one handed battle axe, still a deadly weapon." - icon_state = "axe" - item_state = "axe" - slot_flags = SLOT_BACK - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - applies_material_colour = 0 - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' - can_cleave = TRUE - -/obj/item/weapon/material/sword/battleaxe/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(unique_parry_check(user, attacker, damage_source) && prob(10)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/material/sword/gladius - name = "gladius" - desc = "An ancient short sword, designed to stab and cut." - icon_state = "gladius" - item_state = "gladius" - applies_material_colour = 0 - slot_flags = SLOT_BELT +/obj/item/weapon/material/sword + name = "claymore" + desc = "What are you standing around staring at this for? Get to killing!" + icon_state = "claymore" + slot_flags = SLOT_BELT + force_divisor = 0.7 // 42 when wielded with hardnes 60 (steel) + thrown_force_divisor = 0.5 // 10 when thrown with weight 20 (steel) + sharp = TRUE + edge = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + +/obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(unique_parry_check(user, attacker, damage_source) && prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/material/sword/katana + name = "katana" + desc = "Woefully underpowered in D20. This one looks pretty sharp." + icon_state = "katana" + slot_flags = SLOT_BELT | SLOT_BACK + +/obj/item/weapon/material/sword/katana/caneblade + name = "cane blade" + desc = "Used for making people fall over instead of helping them stand up." + icon_state = "caneblade" + item_state = "caneblade" + +/obj/item/weapon/material/sword/rapier + name = "rapier" + desc = "A slender, fancy and sharply pointed sword." + icon_state = "rapier" + item_state = "rapier" + slot_flags = SLOT_BELT + applies_material_colour = 0 + attack_verb = list("attacked", "stabbed", "prodded", "poked", "lunged") + edge = 0 //rapiers are pointy, but not cutty like other swords + +/obj/item/weapon/material/sword/longsword + name = "longsword" + desc = "A double-edged large blade." + icon_state = "longsword" + item_state = "longsword" + applies_material_colour = 0 + slot_flags = SLOT_BELT | SLOT_BACK + can_cleave = TRUE + +/obj/item/weapon/material/sword/sabre + name = "sabre" + desc = "A sharp curved sword, a favored weapon of pirates far in the past." + icon_state = "sabre" + item_state = "sabre" + applies_material_colour = 0 //messes up the hilt color otherwise + slot_flags = SLOT_BELT + +/obj/item/weapon/material/sword/battleaxe + name = "battleaxe" + desc = "A one handed battle axe, still a deadly weapon." + icon_state = "axe" + item_state = "axe" + slot_flags = SLOT_BACK + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + applies_material_colour = 0 + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' + can_cleave = TRUE + +/obj/item/weapon/material/sword/battleaxe/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(unique_parry_check(user, attacker, damage_source) && prob(10)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/material/sword/gladius + name = "gladius" + desc = "An ancient short sword, designed to stab and cut." + icon_state = "gladius" + item_state = "gladius" + applies_material_colour = 0 + slot_flags = SLOT_BELT diff --git a/code/game/objects/items/weapons/material/thrown.dm b/code/game/objects/items/weapons/material/thrown.dm index dcfaad58019..12eaa25100d 100644 --- a/code/game/objects/items/weapons/material/thrown.dm +++ b/code/game/objects/items/weapons/material/thrown.dm @@ -1,24 +1,24 @@ -/obj/item/weapon/material/star - name = "shuriken" - desc = "A sharp, perfectly weighted piece of metal." - icon_state = "star" - force_divisor = 0.1 // 6 with hardness 60 (steel) - thrown_force_divisor = 0.75 // 15 with weight 20 (steel) - throw_speed = 10 - throw_range = 15 - sharp = TRUE - edge = TRUE - -/obj/item/weapon/material/star/New() - ..() - src.pixel_x = rand(-12, 12) - src.pixel_y = rand(-12, 12) - -/obj/item/weapon/material/star/throw_impact(atom/hit_atom) - ..() - if(material.radioactivity>0 && istype(hit_atom,/mob/living)) - var/mob/living/M = hit_atom - M.adjustToxLoss(rand(20,40)) - -/obj/item/weapon/material/star/ninja +/obj/item/weapon/material/star + name = "shuriken" + desc = "A sharp, perfectly weighted piece of metal." + icon_state = "star" + force_divisor = 0.1 // 6 with hardness 60 (steel) + thrown_force_divisor = 0.75 // 15 with weight 20 (steel) + throw_speed = 10 + throw_range = 15 + sharp = TRUE + edge = TRUE + +/obj/item/weapon/material/star/New() + ..() + src.pixel_x = rand(-12, 12) + src.pixel_y = rand(-12, 12) + +/obj/item/weapon/material/star/throw_impact(atom/hit_atom) + ..() + if(material.radioactivity>0 && istype(hit_atom,/mob/living)) + var/mob/living/M = hit_atom + M.adjustToxLoss(rand(20,40)) + +/obj/item/weapon/material/star/ninja default_material = "uranium" \ No newline at end of file diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index 290925a3665..2e9f0823c86 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -1,193 +1,193 @@ -/* Two-handed Weapons - * Contains: - * Twohanded - * Fireaxe - * Double-Bladed Energy Swords - */ - -/*################################################################## -##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## -####################################################################*/ - -//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn -//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. -//It also tidies stuff up elsewhere. - -/* - * Twohanded - */ -/obj/item/weapon/material/twohanded - w_class = ITEMSIZE_LARGE - var/wielded = 0 - var/force_wielded = 0 - var/force_unwielded - var/wieldsound = null - var/unwieldsound = null - var/base_icon - var/base_name - var/unwielded_force_divisor = 0.25 - hitsound = "swing_hit" - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - -/obj/item/weapon/material/twohanded/update_held_icon() - var/mob/living/M = loc - if(istype(M) && M.can_wield_item(src) && is_held_twohanded(M)) - wielded = 1 - force = force_wielded - name = "[base_name] (wielded)" - update_icon() - else - wielded = 0 - force = force_unwielded - name = "[base_name]" - update_icon() - ..() - -/obj/item/weapon/material/twohanded/update_force() - base_name = name - if(sharp || edge) - force_wielded = material.get_edge_damage() - else - force_wielded = material.get_blunt_damage() - force_wielded = round(force_wielded*force_divisor) - force_unwielded = round(force_wielded*unwielded_force_divisor) - force = force_unwielded - throwforce = round(force*thrown_force_divisor) - //to_world("[src] has unwielded force [force_unwielded], wielded force [force_wielded] and throwforce [throwforce] when made from default material [material.name]") - -/obj/item/weapon/material/twohanded/New() - ..() - update_icon() - -//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday -/obj/item/weapon/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/material/twohanded/update_icon() - icon_state = "[base_icon][wielded]" - item_state = icon_state - -/obj/item/weapon/material/twohanded/dropped() - ..() - if(wielded) - spawn(0) - update_held_icon() - -/* - * Fireaxe - */ -/obj/item/weapon/material/twohanded/fireaxe // DEM AXES MAN, marker -Agouri - icon_state = "fireaxe0" - base_icon = "fireaxe" - name = "fire axe" - desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" - description_info = "This weapon can cleave, striking nearby lesser, hostile enemies close to the primary target. It must be held in both hands to do this." - unwielded_force_divisor = 0.25 - force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor - dulled_divisor = 0.75 //Still metal on a stick - sharp = TRUE - edge = TRUE - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - force_wielded = 30 - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - applies_material_colour = 0 - can_cleave = TRUE - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' - -/obj/item/weapon/material/twohanded/fireaxe/update_held_icon() - var/mob/living/M = loc - if(istype(M) && !issmall(M) && M.item_is_in_hands(src) && !M.hands_are_full()) - wielded = 1 - pry = 1 - force = force_wielded - name = "[base_name] (wielded)" - update_icon() - else - wielded = 0 - pry = 0 - force = force_unwielded - name = "[base_name]" - update_icon() - ..() - -/obj/item/weapon/material/twohanded/fireaxe/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - if(!proximity) return - ..() - if(A && wielded) - if(istype(A,/obj/structure/window)) - var/obj/structure/window/W = A - W.shatter() - else if(istype(A,/obj/structure/grille)) - qdel(A) - else if(istype(A,/obj/effect/plant)) - var/obj/effect/plant/P = A - P.die_off() - -/obj/item/weapon/material/twohanded/fireaxe/scythe - icon_state = "scythe0" - base_icon = "scythe" - name = "scythe" - desc = "A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow." - force_divisor = 0.65 - origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 2) - attack_verb = list("chopped", "sliced", "cut", "reaped") - -//spears, bay edition -/obj/item/weapon/material/twohanded/spear - icon_state = "spearglass0" - base_icon = "spearglass" - name = "spear" - desc = "A haphazardly-constructed yet still deadly weapon of ancient design." - force = 10 - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - force_divisor = 0.5 // 15 when wielded with hardness 30 (glass) - unwielded_force_divisor = 0.375 - thrown_force_divisor = 1.5 // 22.5 when thrown with weight 15 (glass) - throw_speed = 3 - edge = FALSE - sharp = TRUE - hitsound = 'sound/weapons/bladeslice.ogg' - mob_throw_hit_sound = 'sound/weapons/pierce.ogg' - attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") - default_material = "glass" - applies_material_colour = 0 - fragile = 1 //It's a haphazard thing of glass, wire, and steel - reach = 2 // Spears are long. - attackspeed = 14 - -//This is mostly for centaurs. -/obj/item/weapon/material/twohanded/spear/lance - name = "lance" - desc = "End him rightly" - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "lance" - item_state = "lance" - force_divisor = 0.3 - force = 10 - thrown_force_divisor = 1 - default_material = "MAT_STEEL" - fragile = 0 - sharp = TRUE - edge = FALSE - -/obj/item/weapon/material/twohanded/riding_crop - name = "riding crop" - desc = "A rod, a little over a foot long with a widened grip and a thick, leather patch at the end. Used since the dawn of the West to control animals." - force_divisor = 0.05 //Required in order for the X attacks Y message to pop up. - unwielded_force_divisor = 1 // One here, too. - applies_material_colour = 1 - unbreakable = 1 - base_icon = "riding_crop" - icon_state = "riding_crop0" - attack_verb = list("cropped","spanked","swatted","smacked","peppered") - -/obj/item/weapon/material/twohanded/spear/flint +/* Two-handed Weapons + * Contains: + * Twohanded + * Fireaxe + * Double-Bladed Energy Swords + */ + +/*################################################################## +##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## +####################################################################*/ + +//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn +//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. +//It also tidies stuff up elsewhere. + +/* + * Twohanded + */ +/obj/item/weapon/material/twohanded + w_class = ITEMSIZE_LARGE + var/wielded = 0 + var/force_wielded = 0 + var/force_unwielded + var/wieldsound = null + var/unwieldsound = null + var/base_icon + var/base_name + var/unwielded_force_divisor = 0.25 + hitsound = "swing_hit" + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + +/obj/item/weapon/material/twohanded/update_held_icon() + var/mob/living/M = loc + if(istype(M) && M.can_wield_item(src) && is_held_twohanded(M)) + wielded = 1 + force = force_wielded + name = "[base_name] (wielded)" + update_icon() + else + wielded = 0 + force = force_unwielded + name = "[base_name]" + update_icon() + ..() + +/obj/item/weapon/material/twohanded/update_force() + base_name = name + if(sharp || edge) + force_wielded = material.get_edge_damage() + else + force_wielded = material.get_blunt_damage() + force_wielded = round(force_wielded*force_divisor) + force_unwielded = round(force_wielded*unwielded_force_divisor) + force = force_unwielded + throwforce = round(force*thrown_force_divisor) + //to_world("[src] has unwielded force [force_unwielded], wielded force [force_wielded] and throwforce [throwforce] when made from default material [material.name]") + +/obj/item/weapon/material/twohanded/New() + ..() + update_icon() + +//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday +/obj/item/weapon/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/material/twohanded/update_icon() + icon_state = "[base_icon][wielded]" + item_state = icon_state + +/obj/item/weapon/material/twohanded/dropped() + ..() + if(wielded) + spawn(0) + update_held_icon() + +/* + * Fireaxe + */ +/obj/item/weapon/material/twohanded/fireaxe // DEM AXES MAN, marker -Agouri + icon_state = "fireaxe0" + base_icon = "fireaxe" + name = "fire axe" + desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" + description_info = "This weapon can cleave, striking nearby lesser, hostile enemies close to the primary target. It must be held in both hands to do this." + unwielded_force_divisor = 0.25 + force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor + dulled_divisor = 0.75 //Still metal on a stick + sharp = TRUE + edge = TRUE + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + force_wielded = 30 + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + applies_material_colour = 0 + can_cleave = TRUE + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' + +/obj/item/weapon/material/twohanded/fireaxe/update_held_icon() + var/mob/living/M = loc + if(istype(M) && !issmall(M) && M.item_is_in_hands(src) && !M.hands_are_full()) + wielded = 1 + pry = 1 + force = force_wielded + name = "[base_name] (wielded)" + update_icon() + else + wielded = 0 + pry = 0 + force = force_unwielded + name = "[base_name]" + update_icon() + ..() + +/obj/item/weapon/material/twohanded/fireaxe/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + if(!proximity) return + ..() + if(A && wielded) + if(istype(A,/obj/structure/window)) + var/obj/structure/window/W = A + W.shatter() + else if(istype(A,/obj/structure/grille)) + qdel(A) + else if(istype(A,/obj/effect/plant)) + var/obj/effect/plant/P = A + P.die_off() + +/obj/item/weapon/material/twohanded/fireaxe/scythe + icon_state = "scythe0" + base_icon = "scythe" + name = "scythe" + desc = "A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow." + force_divisor = 0.65 + origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 2) + attack_verb = list("chopped", "sliced", "cut", "reaped") + +//spears, bay edition +/obj/item/weapon/material/twohanded/spear + icon_state = "spearglass0" + base_icon = "spearglass" + name = "spear" + desc = "A haphazardly-constructed yet still deadly weapon of ancient design." + force = 10 + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + force_divisor = 0.5 // 15 when wielded with hardness 30 (glass) + unwielded_force_divisor = 0.375 + thrown_force_divisor = 1.5 // 22.5 when thrown with weight 15 (glass) + throw_speed = 3 + edge = FALSE + sharp = TRUE + hitsound = 'sound/weapons/bladeslice.ogg' + mob_throw_hit_sound = 'sound/weapons/pierce.ogg' + attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") + default_material = "glass" + applies_material_colour = 0 + fragile = 1 //It's a haphazard thing of glass, wire, and steel + reach = 2 // Spears are long. + attackspeed = 14 + +//This is mostly for centaurs. +/obj/item/weapon/material/twohanded/spear/lance + name = "lance" + desc = "End him rightly" + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "lance" + item_state = "lance" + force_divisor = 0.3 + force = 10 + thrown_force_divisor = 1 + default_material = "MAT_STEEL" + fragile = 0 + sharp = TRUE + edge = FALSE + +/obj/item/weapon/material/twohanded/riding_crop + name = "riding crop" + desc = "A rod, a little over a foot long with a widened grip and a thick, leather patch at the end. Used since the dawn of the West to control animals." + force_divisor = 0.05 //Required in order for the X attacks Y message to pop up. + unwielded_force_divisor = 1 // One here, too. + applies_material_colour = 1 + unbreakable = 1 + base_icon = "riding_crop" + icon_state = "riding_crop0" + attack_verb = list("cropped","spanked","swatted","smacked","peppered") + +/obj/item/weapon/material/twohanded/spear/flint default_material = MAT_FLINT \ No newline at end of file diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 92234d4da01..1db3d1bffa7 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -1,550 +1,550 @@ -/obj/item/weapon/melee/energy - var/active = 0 - var/active_force - var/active_throwforce - var/active_armourpen - var/active_w_class - var/active_embed_chance = 0 //In the off chance one of these is supposed to embed, you can just tweak this var - sharp = FALSE - edge = FALSE - armor_penetration = 0 - flags = NOCONDUCT | NOBLOODY - var/lrange = 2 - var/lpower = 2 - var/lcolor = "#0099FF" - var/colorable = FALSE - var/rainbow = FALSE - // If it uses energy. - var/use_cell = FALSE - var/hitcost = 120 - var/obj/item/weapon/cell/bcell = null - var/cell_type = /obj/item/weapon/cell/device - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/melee/energy/sword/green/New() - colorable = FALSE - lcolor = "#008000" - -/obj/item/weapon/melee/energy/sword/red/New() - colorable = FALSE - lcolor = "#FF0000" - -/obj/item/weapon/melee/energy/sword/blue/New() - colorable = FALSE - lcolor = "#0000FF" - -/obj/item/weapon/melee/energy/sword/purple/New() - colorable = FALSE - lcolor = "#800080" - -/obj/item/weapon/melee/energy/sword/white/New() - colorable = FALSE - lcolor = "#FFFFFF" - -/obj/item/weapon/melee/energy/proc/activate(mob/living/user) - if(active) - return - active = 1 - if(rainbow) - item_state = "[icon_state]_blade_rainbow" - else - item_state = "[icon_state]_blade" - embed_chance = active_embed_chance - force = active_force - throwforce = active_throwforce - armor_penetration = active_armourpen - sharp = TRUE - edge = TRUE - w_class = active_w_class - playsound(src, 'sound/weapons/saberon.ogg', 50, 1) - update_icon() - set_light(lrange, lpower, lcolor) - -/obj/item/weapon/melee/energy/proc/deactivate(mob/living/user) - if(!active) - return - playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) - item_state = "[icon_state]" - active = 0 - embed_chance = initial(embed_chance) - force = initial(force) - throwforce = initial(throwforce) - armor_penetration = initial(armor_penetration) - sharp = initial(sharp) - edge = initial(edge) - w_class = initial(w_class) - update_icon() - set_light(0,0) - -/obj/item/weapon/melee/energy/proc/use_charge(var/cost) - if(active) - if(bcell) - if(bcell.checked_use(cost)) - return 1 - else - return 0 - return null - -/obj/item/weapon/melee/energy/examine(mob/user) - . = ..() - if(use_cell && Adjacent(user)) - if(bcell) - . += "The blade is [round(bcell.percent())]% charged." - else - . += "The blade does not have a power source installed." - -/obj/item/weapon/melee/energy/attack_self(mob/living/user as mob) - if(use_cell) - if((!bcell || bcell.charge < hitcost) && !active) - to_chat(user, "\The [src] does not seem to have power.") - return - - var/datum/gender/TU = gender_datums[user.get_visible_gender()] - if (active) - if ((CLUMSY in user.mutations) && prob(50)) - user.visible_message("\The [user] accidentally cuts [TU.himself] with \the [src].",\ - "You accidentally cut yourself with \the [src].") - user.take_organ_damage(5,5) - deactivate(user) - else - activate(user) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/weapon/melee/energy/attack(mob/M, mob/user) - if(active && use_cell) - if(!use_charge(hitcost)) - deactivate(user) - visible_message("\The [src]'s blade flickers, before deactivating.") - return ..() - -/obj/item/weapon/melee/energy/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/device/multitool) && colorable && !active) - if(!rainbow) - rainbow = TRUE - else - rainbow = FALSE - to_chat(user, "You manipulate the color controller in [src].") - update_icon() - if(use_cell) - if(istype(W, cell_type)) - if(!bcell) - user.drop_item() - W.loc = src - bcell = W - to_chat(user, "You install a cell in [src].") - update_icon() - else - to_chat(user, "[src] already has a cell.") - else if(W.is_screwdriver() && bcell) - bcell.update_icon() - bcell.forceMove(get_turf(loc)) - bcell = null - to_chat(user, "You remove the cell from \the [src].") - deactivate() - update_icon() - return - return ..() - -/obj/item/weapon/melee/energy/get_cell() - return bcell - -/obj/item/weapon/melee/energy/update_icon() - . = ..() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - blade_overlay.color = lcolor - color = lcolor - if(rainbow) - blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") - blade_overlay.color = "FFFFFF" - color = "FFFFFF" - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - H.update_inv_l_hand() - H.update_inv_r_hand() - - - - -/obj/item/weapon/melee/energy/AltClick(mob/living/user) - if(!colorable) //checks if is not colorable - return - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - - if(tgui_alert(usr, "Are you sure you want to recolor your blade?", "Confirm Recolor", list("Yes", "No")) == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input) - update_icon() - -/obj/item/weapon/melee/energy/examine(mob/user) - . = ..() - if(colorable) - . += "Alt-click to recolor it." - -/* - * Energy Axe - */ -/obj/item/weapon/melee/energy/axe - name = "energy axe" - desc = "An energised battle axe." - icon_state = "eaxe" - item_state = "eaxe" - colorable = FALSE - lcolor = null - //active_force = 150 //holy... - active_force = 60 - active_armourpen = 65 - active_throwforce = 35 - active_w_class = ITEMSIZE_HUGE - //force = 40 - //throwforce = 25 - force = 20 - armor_penetration = 20 - throwforce = 10 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - sharp = TRUE - edge = TRUE - can_cleave = TRUE - -/obj/item/weapon/melee/energy/axe/activate(mob/living/user) - ..() - damtype = SEARING - to_chat(user, "\The [src] is now energised.") - -/obj/item/weapon/melee/energy/axe/deactivate(mob/living/user) - ..() - damtype = BRUTE - to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") - -/obj/item/weapon/melee/energy/axe/charge - name = "charge axe" - desc = "An energised axe." - active_force = 35 - active_throwforce = 20 - active_armourpen = 30 - force = 15 - - use_cell = TRUE - hitcost = 120 - -/obj/item/weapon/melee/energy/axe/charge/loaded/New() - ..() - bcell = new/obj/item/weapon/cell/device/weapon(src) - -/* - * Energy Sword - */ -/obj/item/weapon/melee/energy/sword - color - name = "energy sword" - desc = "May the force be within you." - icon_state = "esword" - item_state = "esword" - active_force = 30 - active_armourpen = 50 - active_throwforce = 20 - active_w_class = ITEMSIZE_LARGE - force = 3 - throwforce = 5 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - flags = NOBLOODY - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - colorable = TRUE - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - - - projectile_parry_chance = 65 - -/obj/item/weapon/melee/energy/sword/dropped(var/mob/user) - ..() - if(!istype(loc,/mob)) - deactivate(user) - - -/obj/item/weapon/melee/energy/sword/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - - ..() - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - -/obj/item/weapon/melee/energy/sword/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list() - -/obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/weapon/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) - return 0 - - var/bad_arc = reverse_direction(user.dir) - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/weapon/melee/energy/sword/pirate - name = "energy cutlass" - desc = "Arrrr matey." - icon_state = "cutlass" - item_state = "cutlass" - colorable = TRUE - - -/* - *Ionic Rapier - */ - -/obj/item/weapon/melee/energy/sword/ionic_rapier - name = "ionic rapier" - desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." - description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ - Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ - very little damage to purely organic targets." - icon_state = "ionrapier" - item_state = "ionrapier" - active_force = 5 - active_armourpen = 80 - active_throwforce = 3 - active_embed_chance = 0 - sharp = TRUE - edge = TRUE - armor_penetration = 0 - flags = NOBLOODY - lrange = 2 - lpower = 2 - lcolor = "#0000FF" - projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. - -/obj/item/weapon/melee/energy/sword/ionic_rapier/afterattack(var/atom/movable/AM, var/mob/living/user, var/proximity) - if(istype(AM, /obj) && proximity && active) - // EMP stuff. - var/obj/O = AM - O.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(O, 'sound/effects/EMPulse.ogg', 100, 1) - user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. - return ..() - -/obj/item/weapon/melee/energy/sword/ionic_rapier/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) - . = ..() - if(target.isSynthetic() && active) - // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. - target.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(target, 'sound/effects/EMPulse.ogg', 100, 1) - target.adjustFireLoss(force * 3) // 15 Burn, for 20 total. - playsound(target, 'sound/weapons/blade1.ogg', 100, 1) - - // Make lesser robots really mad at us. - if(target.mob_class & MOB_CLASS_SYNTHETIC) - if(target.has_AI()) - target.taunt(user) - target.adjustFireLoss(force * 6) // 30 Burn, for 50 total. - -/obj/item/weapon/melee/energy/sword/ionic_rapier/lance - name = "zero-point lance" - desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." - active_force = 20 - active_armourpen = 15 - reach = 2 - -/* - * Charge blade. Uses a cell, and costs energy per strike. - */ - -/obj/item/weapon/melee/energy/sword/charge - name = "charge sword" - desc = "A small, handheld device which emits a high-energy 'blade'." - origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - active_force = 25 - active_armourpen = 25 - projectile_parry_chance = 40 - colorable = TRUE - - hitcost = 75 - -/obj/item/weapon/melee/energy/sword/charge/loaded/New() - ..() - bcell = new/obj/item/weapon/cell/device/weapon(src) - -//Energy Blade (ninja uses this) - -//Can't be activated or deactivated, so no reason to be a subtype of energy -/obj/item/weapon/melee/energy/blade - name = "energy blade" - desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." - icon_state = "blade" - item_state = "blade" - force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe - armor_penetration = 100 - sharp = TRUE - edge = TRUE - anchored = TRUE // Never spawned outside of inventory, should be fine. - throwforce = 1 //Throwing or dropping the item deletes it. - throw_speed = 1 - throw_range = 1 - w_class = ITEMSIZE_LARGE//So you can't hide it in your pocket or some such. - flags = NOBLOODY - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - var/mob/living/creator - var/datum/effect/effect/system/spark_spread/spark_system - projectile_parry_chance = 60 - lcolor = "#00FF00" - -/obj/item/weapon/melee/energy/blade/New() - - spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - START_PROCESSING(SSobj, src) - set_light(lrange, lpower, lcolor) - -/obj/item/weapon/melee/energy/blade/Destroy() - STOP_PROCESSING(SSobj, src) - ..() - -/obj/item/weapon/melee/energy/blade/attack_self(mob/user as mob) - user.drop_from_inventory(src) - spawn(1) if(src) qdel(src) - -/obj/item/weapon/melee/energy/blade/dropped() - spawn(1) if(src) qdel(src) - -/obj/item/weapon/melee/energy/blade/process() - if(!creator || loc != creator || !creator.item_is_in_hands(src)) - // Tidy up a bit. - if(istype(loc,/mob/living)) - var/mob/living/carbon/human/host = loc - if(istype(host)) - for(var/obj/item/organ/external/organ in host.organs) - for(var/obj/item/O in organ.implants) - if(O == src) - organ.implants -= src - host.pinned -= src - host.embedded -= src - host.drop_from_inventory(src) - spawn(1) if(src) qdel(src) - -/obj/item/weapon/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/weapon/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - - if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) - return 0 - - var/bad_arc = reverse_direction(user.dir) - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -//Energy Spear - -/obj/item/weapon/melee/energy/spear - name = "energy spear" - desc = "Concentrated energy forming a sharp tip at the end of a long rod." - icon_state = "espear" - armor_penetration = 0 - sharp = TRUE - edge = TRUE - force = 5 - throwforce = 10 - throw_speed = 7 - throw_range = 11 - reach = 2 - w_class = ITEMSIZE_LARGE - active_force = 25 - active_armourpen = 75 - active_throwforce = 30 - active_w_class = ITEMSIZE_HUGE - colorable = TRUE - - - lcolor = "#800080" - -/obj/item/weapon/melee/energy/spear/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - ..() - attack_verb = list("jabbed", "stabbed", "impaled") - - -/obj/item/weapon/melee/energy/spear/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list("whacked", "beat", "slapped", "thonked") - -/obj/item/weapon/melee/energy/spear/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - return 0 +/obj/item/weapon/melee/energy + var/active = 0 + var/active_force + var/active_throwforce + var/active_armourpen + var/active_w_class + var/active_embed_chance = 0 //In the off chance one of these is supposed to embed, you can just tweak this var + sharp = FALSE + edge = FALSE + armor_penetration = 0 + flags = NOCONDUCT | NOBLOODY + var/lrange = 2 + var/lpower = 2 + var/lcolor = "#0099FF" + var/colorable = FALSE + var/rainbow = FALSE + // If it uses energy. + var/use_cell = FALSE + var/hitcost = 120 + var/obj/item/weapon/cell/bcell = null + var/cell_type = /obj/item/weapon/cell/device + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/melee/energy/sword/green/New() + colorable = FALSE + lcolor = "#008000" + +/obj/item/weapon/melee/energy/sword/red/New() + colorable = FALSE + lcolor = "#FF0000" + +/obj/item/weapon/melee/energy/sword/blue/New() + colorable = FALSE + lcolor = "#0000FF" + +/obj/item/weapon/melee/energy/sword/purple/New() + colorable = FALSE + lcolor = "#800080" + +/obj/item/weapon/melee/energy/sword/white/New() + colorable = FALSE + lcolor = "#FFFFFF" + +/obj/item/weapon/melee/energy/proc/activate(mob/living/user) + if(active) + return + active = 1 + if(rainbow) + item_state = "[icon_state]_blade_rainbow" + else + item_state = "[icon_state]_blade" + embed_chance = active_embed_chance + force = active_force + throwforce = active_throwforce + armor_penetration = active_armourpen + sharp = TRUE + edge = TRUE + w_class = active_w_class + playsound(src, 'sound/weapons/saberon.ogg', 50, 1) + update_icon() + set_light(lrange, lpower, lcolor) + +/obj/item/weapon/melee/energy/proc/deactivate(mob/living/user) + if(!active) + return + playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) + item_state = "[icon_state]" + active = 0 + embed_chance = initial(embed_chance) + force = initial(force) + throwforce = initial(throwforce) + armor_penetration = initial(armor_penetration) + sharp = initial(sharp) + edge = initial(edge) + w_class = initial(w_class) + update_icon() + set_light(0,0) + +/obj/item/weapon/melee/energy/proc/use_charge(var/cost) + if(active) + if(bcell) + if(bcell.checked_use(cost)) + return 1 + else + return 0 + return null + +/obj/item/weapon/melee/energy/examine(mob/user) + . = ..() + if(use_cell && Adjacent(user)) + if(bcell) + . += "The blade is [round(bcell.percent())]% charged." + else + . += "The blade does not have a power source installed." + +/obj/item/weapon/melee/energy/attack_self(mob/living/user as mob) + if(use_cell) + if((!bcell || bcell.charge < hitcost) && !active) + to_chat(user, "\The [src] does not seem to have power.") + return + + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + if (active) + if ((CLUMSY in user.mutations) && prob(50)) + user.visible_message("\The [user] accidentally cuts [TU.himself] with \the [src].",\ + "You accidentally cut yourself with \the [src].") + user.take_organ_damage(5,5) + deactivate(user) + else + activate(user) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return + +/obj/item/weapon/melee/energy/attack(mob/M, mob/user) + if(active && use_cell) + if(!use_charge(hitcost)) + deactivate(user) + visible_message("\The [src]'s blade flickers, before deactivating.") + return ..() + +/obj/item/weapon/melee/energy/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/device/multitool) && colorable && !active) + if(!rainbow) + rainbow = TRUE + else + rainbow = FALSE + to_chat(user, "You manipulate the color controller in [src].") + update_icon() + if(use_cell) + if(istype(W, cell_type)) + if(!bcell) + user.drop_item() + W.loc = src + bcell = W + to_chat(user, "You install a cell in [src].") + update_icon() + else + to_chat(user, "[src] already has a cell.") + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && bcell) + bcell.update_icon() + bcell.forceMove(get_turf(loc)) + bcell = null + to_chat(user, "You remove the cell from \the [src].") + deactivate() + update_icon() + return + return ..() + +/obj/item/weapon/melee/energy/get_cell() + return bcell + +/obj/item/weapon/melee/energy/update_icon() + . = ..() + var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") + blade_overlay.color = lcolor + color = lcolor + if(rainbow) + blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") + blade_overlay.color = "FFFFFF" + color = "FFFFFF" + cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other + if(active) + add_overlay(blade_overlay) + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + H.update_inv_l_hand() + H.update_inv_r_hand() + + + + +/obj/item/weapon/melee/energy/AltClick(mob/living/user) + if(!colorable) //checks if is not colorable + return + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + + if(tgui_alert(usr, "Are you sure you want to recolor your blade?", "Confirm Recolor", list("Yes", "No")) == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = sanitize_hexcolor(energy_color_input) + update_icon() + +/obj/item/weapon/melee/energy/examine(mob/user) + . = ..() + if(colorable) + . += "Alt-click to recolor it." + +/* + * Energy Axe + */ +/obj/item/weapon/melee/energy/axe + name = "energy axe" + desc = "An energised battle axe." + icon_state = "eaxe" + item_state = "eaxe" + colorable = FALSE + lcolor = null + //active_force = 150 //holy... + active_force = 60 + active_armourpen = 65 + active_throwforce = 35 + active_w_class = ITEMSIZE_HUGE + //force = 40 + //throwforce = 25 + force = 20 + armor_penetration = 20 + throwforce = 10 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + sharp = TRUE + edge = TRUE + can_cleave = TRUE + +/obj/item/weapon/melee/energy/axe/activate(mob/living/user) + ..() + damtype = SEARING + to_chat(user, "\The [src] is now energised.") + +/obj/item/weapon/melee/energy/axe/deactivate(mob/living/user) + ..() + damtype = BRUTE + to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") + +/obj/item/weapon/melee/energy/axe/charge + name = "charge axe" + desc = "An energised axe." + active_force = 35 + active_throwforce = 20 + active_armourpen = 30 + force = 15 + + use_cell = TRUE + hitcost = 120 + +/obj/item/weapon/melee/energy/axe/charge/loaded/New() + ..() + bcell = new/obj/item/weapon/cell/device/weapon(src) + +/* + * Energy Sword + */ +/obj/item/weapon/melee/energy/sword + color + name = "energy sword" + desc = "May the force be within you." + icon_state = "esword" + item_state = "esword" + active_force = 30 + active_armourpen = 50 + active_throwforce = 20 + active_w_class = ITEMSIZE_LARGE + force = 3 + throwforce = 5 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + flags = NOBLOODY + origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) + colorable = TRUE + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + + + projectile_parry_chance = 65 + +/obj/item/weapon/melee/energy/sword/dropped(var/mob/user) + ..() + if(!istype(loc,/mob)) + deactivate(user) + + +/obj/item/weapon/melee/energy/sword/activate(mob/living/user) + if(!active) + to_chat(user, "\The [src] is now energised.") + + ..() + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + +/obj/item/weapon/melee/energy/sword/deactivate(mob/living/user) + if(active) + to_chat(user, "\The [src] deactivates!") + ..() + attack_verb = list() + +/obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(active && default_parry_check(user, attacker, damage_source) && prob(60)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + + return 0 + +/obj/item/weapon/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +/obj/item/weapon/melee/energy/sword/pirate + name = "energy cutlass" + desc = "Arrrr matey." + icon_state = "cutlass" + item_state = "cutlass" + colorable = TRUE + + +/* + *Ionic Rapier + */ + +/obj/item/weapon/melee/energy/sword/ionic_rapier + name = "ionic rapier" + desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." + description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ + Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ + very little damage to purely organic targets." + icon_state = "ionrapier" + item_state = "ionrapier" + active_force = 5 + active_armourpen = 80 + active_throwforce = 3 + active_embed_chance = 0 + sharp = TRUE + edge = TRUE + armor_penetration = 0 + flags = NOBLOODY + lrange = 2 + lpower = 2 + lcolor = "#0000FF" + projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. + +/obj/item/weapon/melee/energy/sword/ionic_rapier/afterattack(var/atom/movable/AM, var/mob/living/user, var/proximity) + if(istype(AM, /obj) && proximity && active) + // EMP stuff. + var/obj/O = AM + O.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(O, 'sound/effects/EMPulse.ogg', 100, 1) + user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. + return ..() + +/obj/item/weapon/melee/energy/sword/ionic_rapier/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + . = ..() + if(target.isSynthetic() && active) + // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. + target.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(target, 'sound/effects/EMPulse.ogg', 100, 1) + target.adjustFireLoss(force * 3) // 15 Burn, for 20 total. + playsound(target, 'sound/weapons/blade1.ogg', 100, 1) + + // Make lesser robots really mad at us. + if(target.mob_class & MOB_CLASS_SYNTHETIC) + if(target.has_AI()) + target.taunt(user) + target.adjustFireLoss(force * 6) // 30 Burn, for 50 total. + +/obj/item/weapon/melee/energy/sword/ionic_rapier/lance + name = "zero-point lance" + desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." + active_force = 20 + active_armourpen = 15 + reach = 2 + +/* + * Charge blade. Uses a cell, and costs energy per strike. + */ + +/obj/item/weapon/melee/energy/sword/charge + name = "charge sword" + desc = "A small, handheld device which emits a high-energy 'blade'." + origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + active_force = 25 + active_armourpen = 25 + projectile_parry_chance = 40 + colorable = TRUE + + hitcost = 75 + +/obj/item/weapon/melee/energy/sword/charge/loaded/New() + ..() + bcell = new/obj/item/weapon/cell/device/weapon(src) + +//Energy Blade (ninja uses this) + +//Can't be activated or deactivated, so no reason to be a subtype of energy +/obj/item/weapon/melee/energy/blade + name = "energy blade" + desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." + icon_state = "blade" + item_state = "blade" + force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe + armor_penetration = 100 + sharp = TRUE + edge = TRUE + anchored = TRUE // Never spawned outside of inventory, should be fine. + throwforce = 1 //Throwing or dropping the item deletes it. + throw_speed = 1 + throw_range = 1 + w_class = ITEMSIZE_LARGE//So you can't hide it in your pocket or some such. + flags = NOBLOODY + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + var/mob/living/creator + var/datum/effect/effect/system/spark_spread/spark_system + projectile_parry_chance = 60 + lcolor = "#00FF00" + +/obj/item/weapon/melee/energy/blade/New() + + spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + START_PROCESSING(SSobj, src) + set_light(lrange, lpower, lcolor) + +/obj/item/weapon/melee/energy/blade/Destroy() + STOP_PROCESSING(SSobj, src) + ..() + +/obj/item/weapon/melee/energy/blade/attack_self(mob/user as mob) + user.drop_from_inventory(src) + spawn(1) if(src) qdel(src) + +/obj/item/weapon/melee/energy/blade/dropped() + spawn(1) if(src) qdel(src) + +/obj/item/weapon/melee/energy/blade/process() + if(!creator || loc != creator || !creator.item_is_in_hands(src)) + // Tidy up a bit. + if(istype(loc,/mob/living)) + var/mob/living/carbon/human/host = loc + if(istype(host)) + for(var/obj/item/organ/external/organ in host.organs) + for(var/obj/item/O in organ.implants) + if(O == src) + organ.implants -= src + host.pinned -= src + host.embedded -= src + host.drop_from_inventory(src) + spawn(1) if(src) qdel(src) + +/obj/item/weapon/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(default_parry_check(user, attacker, damage_source) && prob(60)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) + user.visible_message("\The [user] deflects [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + + return 0 + +/obj/item/weapon/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) + + if(user.incapacitated() || !istype(damage_source, /obj/item/projectile/)) + return 0 + + var/bad_arc = reverse_direction(user.dir) + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +//Energy Spear + +/obj/item/weapon/melee/energy/spear + name = "energy spear" + desc = "Concentrated energy forming a sharp tip at the end of a long rod." + icon_state = "espear" + armor_penetration = 0 + sharp = TRUE + edge = TRUE + force = 5 + throwforce = 10 + throw_speed = 7 + throw_range = 11 + reach = 2 + w_class = ITEMSIZE_LARGE + active_force = 25 + active_armourpen = 75 + active_throwforce = 30 + active_w_class = ITEMSIZE_HUGE + colorable = TRUE + + + lcolor = "#800080" + +/obj/item/weapon/melee/energy/spear/activate(mob/living/user) + if(!active) + to_chat(user, "\The [src] is now energised.") + ..() + attack_verb = list("jabbed", "stabbed", "impaled") + + +/obj/item/weapon/melee/energy/spear/deactivate(mob/living/user) + if(active) + to_chat(user, "\The [src] deactivates!") + ..() + attack_verb = list("whacked", "beat", "slapped", "thonked") + +/obj/item/weapon/melee/energy/spear/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(active && default_parry_check(user, attacker, damage_source) && prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + return 1 + return 0 diff --git a/code/game/objects/items/weapons/melee/energy_vr.dm b/code/game/objects/items/weapons/melee/energy_vr.dm index 62f32016197..61a16a24126 100644 --- a/code/game/objects/items/weapons/melee/energy_vr.dm +++ b/code/game/objects/items/weapons/melee/energy_vr.dm @@ -9,7 +9,7 @@ /obj/item/weapon/melee/energy/sword/altevian name = "plasma blade cutter" - desc = "A device used as both defense and operational purposes to cut through most metals. This is usually seen on engineers from the altevian hegemony when working salvaging derelicts." + desc = "A device that's primarily used to cut through hull plating and interior structure, but also doubles as a deadly weapon. It's normally seen in the hands of altevian engineers as they carry out salvage work." icon_state = "altevian-cutter" item_state = "altevian-cutter" icon = 'icons/obj/weapons_vr.dmi' @@ -22,4 +22,4 @@ if(active) icon_state = "[initial(icon_state)]_active" else - icon_state = initial(icon_state) \ No newline at end of file + icon_state = initial(icon_state) diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm index 5930c1ad7ed..9598987006a 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/weapons/melee/misc.dm @@ -1,98 +1,98 @@ -/obj/item/weapon/melee/chainofcommand - name = "chain of command" - desc = "A tool used by great men to placate the frothing masses." - icon_state = "chain" - slot_flags = SLOT_BELT - force = 10 - throwforce = 7 - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_COMBAT = 4) - attack_verb = list("flogged", "whipped", "lashed", "disciplined") - hitsound = 'sound/weapons/whip.ogg' - reach = 2 - -/obj/item/weapon/melee/chainofcommand/curator_whip - name = "leather whip" - desc = "A fine weapon for some treasure hunting." - icon_state = "curator_whip" - force = 5 - throwforce = 5 - origin_tech = list(TECH_COMBAT = 2) - -/obj/item/weapon/melee/chainofcommand/curator_whip/toy - name = "toy whip" - desc = "A fake whip. Perfect for fake treasure hunting" - force = 2 - throwforce = 2 - -/obj/item/weapon/melee/umbrella - name = "umbrella" - desc = "To keep the rain off you. Use with caution on windy days." - icon = 'icons/obj/items.dmi' - icon_state = "umbrella_closed" - addblends = "umbrella_closed_a" - slot_flags = SLOT_BELT - force = 5 - throwforce = 5 - w_class = ITEMSIZE_NORMAL - var/open = FALSE - -/obj/item/weapon/melee/umbrella/New() - ..() - update_icon() - -/obj/item/weapon/melee/umbrella/attack_self() - src.toggle_umbrella() - -/obj/item/weapon/melee/umbrella/proc/toggle_umbrella() - open = !open - icon_state = "umbrella_[open ? "open" : "closed"]" - addblends = icon_state + "_a" - item_state = icon_state - update_icon() - if(ishuman(src.loc)) - var/mob/living/carbon/human/H = src.loc - H.update_inv_l_hand(0) - H.update_inv_r_hand() - -// Randomizes color -/obj/item/weapon/melee/umbrella/random/New() - color = get_random_colour() - ..() - -/obj/item/weapon/melee/cursedblade - name = "crystal blade" - desc = "The red crystal blade's polished surface glints in the light, giving off a faint glow." - icon_state = "soulblade" - slot_flags = SLOT_BELT | SLOT_BACK - force = 30 - throwforce = 10 - w_class = ITEMSIZE_NORMAL - sharp = TRUE - edge = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - can_speak = 1 - var/list/voice_mobs = list() //The curse of the sword is that it has someone trapped inside. - - -/obj/item/weapon/melee/cursedblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/melee/cursedblade/proc/ghost_inhabit(var/mob/candidate) - if(!isobserver(candidate)) - return - //Handle moving the ghost into the new shell. - announce_ghost_joinleave(candidate, 0, "They are occupying a cursed sword now.") - var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. - new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. - new_voice.mind = candidate.mind //Transfer the mind, if any. - new_voice.ckey = candidate.ckey //Finally, bring the client over. - new_voice.name = "cursed sword" //Cursed swords shouldn't be known characters. - new_voice.real_name = "cursed sword" - voice_mobs.Add(new_voice) - listening_objects |= src +/obj/item/weapon/melee/chainofcommand + name = "chain of command" + desc = "A tool used by great men to placate the frothing masses." + icon_state = "chain" + slot_flags = SLOT_BELT + force = 10 + throwforce = 7 + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_COMBAT = 4) + attack_verb = list("flogged", "whipped", "lashed", "disciplined") + hitsound = 'sound/weapons/whip.ogg' + reach = 2 + +/obj/item/weapon/melee/chainofcommand/curator_whip + name = "leather whip" + desc = "A fine weapon for some treasure hunting." + icon_state = "curator_whip" + force = 5 + throwforce = 5 + origin_tech = list(TECH_COMBAT = 2) + +/obj/item/weapon/melee/chainofcommand/curator_whip/toy + name = "toy whip" + desc = "A fake whip. Perfect for fake treasure hunting" + force = 2 + throwforce = 2 + +/obj/item/weapon/melee/umbrella + name = "umbrella" + desc = "To keep the rain off you. Use with caution on windy days." + icon = 'icons/obj/items.dmi' + icon_state = "umbrella_closed" + addblends = "umbrella_closed_a" + slot_flags = SLOT_BELT + force = 5 + throwforce = 5 + w_class = ITEMSIZE_NORMAL + var/open = FALSE + +/obj/item/weapon/melee/umbrella/New() + ..() + update_icon() + +/obj/item/weapon/melee/umbrella/attack_self() + src.toggle_umbrella() + +/obj/item/weapon/melee/umbrella/proc/toggle_umbrella() + open = !open + icon_state = "umbrella_[open ? "open" : "closed"]" + addblends = icon_state + "_a" + item_state = icon_state + update_icon() + if(ishuman(src.loc)) + var/mob/living/carbon/human/H = src.loc + H.update_inv_l_hand(0) + H.update_inv_r_hand() + +// Randomizes color +/obj/item/weapon/melee/umbrella/random/New() + color = get_random_colour() + ..() + +/obj/item/weapon/melee/cursedblade + name = "crystal blade" + desc = "The red crystal blade's polished surface glints in the light, giving off a faint glow." + icon_state = "soulblade" + slot_flags = SLOT_BELT | SLOT_BACK + force = 30 + throwforce = 10 + w_class = ITEMSIZE_NORMAL + sharp = TRUE + edge = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + can_speak = 1 + var/list/voice_mobs = list() //The curse of the sword is that it has someone trapped inside. + + +/obj/item/weapon/melee/cursedblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(default_parry_check(user, attacker, damage_source) && prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/melee/cursedblade/proc/ghost_inhabit(var/mob/candidate) + if(!isobserver(candidate)) + return + //Handle moving the ghost into the new shell. + announce_ghost_joinleave(candidate, 0, "They are occupying a cursed sword now.") + var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be. + new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences. + new_voice.mind = candidate.mind //Transfer the mind, if any. + new_voice.ckey = candidate.ckey //Finally, bring the client over. + new_voice.name = "cursed sword" //Cursed swords shouldn't be known characters. + new_voice.real_name = "cursed sword" + voice_mobs.Add(new_voice) + listening_objects |= src diff --git a/code/game/objects/items/weapons/melee/misc_vr.dm b/code/game/objects/items/weapons/melee/misc_vr.dm index d06c4b82bf8..b14a36e1f40 100644 --- a/code/game/objects/items/weapons/melee/misc_vr.dm +++ b/code/game/objects/items/weapons/melee/misc_vr.dm @@ -13,22 +13,4 @@ sharp = TRUE edge = FALSE attack_verb = list("stabbed", "lunged at", "dextrously struck", "sliced", "lacerated", "impaled", "diced", "charioted") - hitsound = 'sound/weapons/bladeslice.ogg' - -/obj/item/weapon/melee/altevian_wrench - name = "Hull Systems Multi-Wrench" - desc = "A wrench designed with a method to help secure and access bolts, hatches, and airlocks on altevian designed vessels. This operates as nothing more than a massive wrench when used for other purposes." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "altevian-wrench" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee_vr.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee_vr.dmi', - ) - slot_flags = SLOT_BACK - force = 25 - throwforce = 15 - w_class = ITEMSIZE_HUGE - sharp = FALSE - edge = FALSE - attack_verb = list("whacked", "slammed", "bashed", "wrenched", "fixed", "bolted", "clonked", "bonked") - hitsound = 'sound/weapons/smash.ogg' \ No newline at end of file + hitsound = 'sound/weapons/bladeslice.ogg' \ No newline at end of file diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 9f505f903f0..0d11a574af7 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -1,79 +1,79 @@ -GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) - -/* - * Mop - */ -/obj/item/weapon/mop - name = "mop" - desc = "The world of janitalia wouldn't be complete without a mop." - icon = 'icons/obj/janitor.dmi' - icon_state = "mop" - force = 3.0 - throwforce = 10.0 - throw_speed = 5 - throw_range = 10 - w_class = ITEMSIZE_NORMAL - flags = NOCONDUCT - attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") - var/mopping = 0 - var/mopcount = 0 - -/obj/item/weapon/mop/New() - create_reagents(30) - ..() - -/obj/item/weapon/mop/afterattack(atom/A, mob/user, proximity) - if(!proximity) return - if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) - if(reagents.total_volume < 1) - to_chat(user, "Your mop is dry!") - return - - user.visible_message("[user] begins to clean \the [get_turf(A)].") - - if(do_after(user, 40)) - var/turf/T = get_turf(A) - if(T) - T.clean(src, user) - to_chat(user, "You have finished mopping!") - - -/obj/effect/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap)) - return - ..() - -/* - * Advanced Mop - */ -/obj/item/weapon/mop/advanced - name = "advanced mop" - desc = "No stain will go unclean." - icon = 'icons/obj/janitor.dmi' - icon_state = "adv_mop" - force = 3.5 - throwforce = 10.5 - throw_speed = 4 - throw_range = 10 - w_class = ITEMSIZE_NORMAL - flags = NOCONDUCT - attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") - -/obj/item/weapon/mop/advanced/New() - create_reagents(30) - ..() - -/obj/item/weapon/mop/advanced/afterattack(atom/A, mob/user, proximity) - if(!proximity) return - if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) - if(reagents.total_volume < 1) - to_chat(user, "Your mop is dry!") - return - - user.visible_message("[user] begins to clean \the [get_turf(A)].") - - if(do_after(user, 20)) - var/turf/T = get_turf(A) - if(T) - T.clean(src, user) +GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) + +/* + * Mop + */ +/obj/item/weapon/mop + name = "mop" + desc = "The world of janitalia wouldn't be complete without a mop." + icon = 'icons/obj/janitor.dmi' + icon_state = "mop" + force = 3.0 + throwforce = 10.0 + throw_speed = 5 + throw_range = 10 + w_class = ITEMSIZE_NORMAL + flags = NOCONDUCT + attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") + var/mopping = 0 + var/mopcount = 0 + +/obj/item/weapon/mop/New() + create_reagents(30) + ..() + +/obj/item/weapon/mop/afterattack(atom/A, mob/user, proximity) + if(!proximity) return + if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) + if(reagents.total_volume < 1) + to_chat(user, "Your mop is dry!") + return + + user.visible_message("[user] begins to clean \the [get_turf(A)].") + + if(do_after(user, 40)) + var/turf/T = get_turf(A) + if(T) + T.clean(src, user) + to_chat(user, "You have finished mopping!") + + +/obj/effect/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap)) + return + ..() + +/* + * Advanced Mop + */ +/obj/item/weapon/mop/advanced + name = "advanced mop" + desc = "No stain will go unclean." + icon = 'icons/obj/janitor.dmi' + icon_state = "adv_mop" + force = 3.5 + throwforce = 10.5 + throw_speed = 4 + throw_range = 10 + w_class = ITEMSIZE_NORMAL + flags = NOCONDUCT + attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") + +/obj/item/weapon/mop/advanced/New() + create_reagents(30) + ..() + +/obj/item/weapon/mop/advanced/afterattack(atom/A, mob/user, proximity) + if(!proximity) return + if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) + if(reagents.total_volume < 1) + to_chat(user, "Your mop is dry!") + return + + user.visible_message("[user] begins to clean \the [get_turf(A)].") + + if(do_after(user, 20)) + var/turf/T = get_turf(A) + if(T) + T.clean(src, user) to_chat(user, "You have finished mopping!") \ No newline at end of file diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm index a86c3d4bd4f..3162718b7d8 100644 --- a/code/game/objects/items/weapons/paint.dm +++ b/code/game/objects/items/weapons/paint.dm @@ -1,78 +1,78 @@ -//NEVER USE THIS IT SUX -PETETHEGOAT -//THE GOAT WAS RIGHT - RKF - -var/global/list/cached_icons = list() - -/obj/item/weapon/reagent_containers/glass/paint - desc = "It's a paint bucket." - name = "paint bucket" - icon = 'icons/obj/items.dmi' - icon_state = "paint_neutral" - item_state = "paintcan" - matter = list(MAT_STEEL = 200) - w_class = ITEMSIZE_NORMAL - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10,20,30,60) - volume = 60 - unacidable = FALSE - flags = OPENCONTAINER - var/paint_type = "red" - -/obj/item/weapon/reagent_containers/glass/paint/afterattack(turf/simulated/target, mob/user, proximity) - if(!proximity) return - if(istype(target) && reagents.total_volume > 5) - user.visible_message("\The [target] has been splashed with something by [user]!") - reagents.trans_to_turf(target, 5) - else - return ..() - -/obj/item/weapon/reagent_containers/glass/paint/New() - ..() - if(paint_type) - reagents.add_reagent("paint", volume, paint_type) - -/obj/item/weapon/reagent_containers/glass/paint/red - icon_state = "paint_red" - paint_type = "#FF0000" - -/obj/item/weapon/reagent_containers/glass/paint/yellow - icon_state = "paint_yellow" - paint_type = "#FFFF00" - -/obj/item/weapon/reagent_containers/glass/paint/green - icon_state = "paint_green" - paint_type = "#00FF00" - -/obj/item/weapon/reagent_containers/glass/paint/blue - icon_state = "paint_blue" - paint_type = "#0000FF" - -/obj/item/weapon/reagent_containers/glass/paint/violet - icon_state = "paint_violet" - paint_type = "#FF00FF" - -/obj/item/weapon/reagent_containers/glass/paint/black - icon_state = "paint_black" - paint_type = "#000000" - -/obj/item/weapon/reagent_containers/glass/paint/grey - icon_state = "paint_neutral" - paint_type = "#808080" - -/obj/item/weapon/reagent_containers/glass/paint/orange - icon_state = "paint_orange" - paint_type = "#FFA500" - -/obj/item/weapon/reagent_containers/glass/paint/purple - icon_state = "paint_purple" - paint_type = "#A500FF" - -/obj/item/weapon/reagent_containers/glass/paint/cyan - icon_state = "paint_cyan" - paint_type = "#00FFFF" - -/obj/item/weapon/reagent_containers/glass/paint/white - name = "paint remover bucket" - icon_state = "paint_white" - paint_type = "#FFFFFF" - +//NEVER USE THIS IT SUX -PETETHEGOAT +//THE GOAT WAS RIGHT - RKF + +var/global/list/cached_icons = list() + +/obj/item/weapon/reagent_containers/glass/paint + desc = "It's a paint bucket." + name = "paint bucket" + icon = 'icons/obj/items.dmi' + icon_state = "paint_neutral" + item_state = "paintcan" + matter = list(MAT_STEEL = 200) + w_class = ITEMSIZE_NORMAL + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(10,20,30,60) + volume = 60 + unacidable = FALSE + flags = OPENCONTAINER + var/paint_type = "red" + +/obj/item/weapon/reagent_containers/glass/paint/afterattack(turf/simulated/target, mob/user, proximity) + if(!proximity) return + if(istype(target) && reagents.total_volume > 5) + user.visible_message("\The [target] has been splashed with something by [user]!") + reagents.trans_to_turf(target, 5) + else + return ..() + +/obj/item/weapon/reagent_containers/glass/paint/New() + ..() + if(paint_type) + reagents.add_reagent("paint", volume, paint_type) + +/obj/item/weapon/reagent_containers/glass/paint/red + icon_state = "paint_red" + paint_type = "#FF0000" + +/obj/item/weapon/reagent_containers/glass/paint/yellow + icon_state = "paint_yellow" + paint_type = "#FFFF00" + +/obj/item/weapon/reagent_containers/glass/paint/green + icon_state = "paint_green" + paint_type = "#00FF00" + +/obj/item/weapon/reagent_containers/glass/paint/blue + icon_state = "paint_blue" + paint_type = "#0000FF" + +/obj/item/weapon/reagent_containers/glass/paint/violet + icon_state = "paint_violet" + paint_type = "#FF00FF" + +/obj/item/weapon/reagent_containers/glass/paint/black + icon_state = "paint_black" + paint_type = "#000000" + +/obj/item/weapon/reagent_containers/glass/paint/grey + icon_state = "paint_neutral" + paint_type = "#808080" + +/obj/item/weapon/reagent_containers/glass/paint/orange + icon_state = "paint_orange" + paint_type = "#FFA500" + +/obj/item/weapon/reagent_containers/glass/paint/purple + icon_state = "paint_purple" + paint_type = "#A500FF" + +/obj/item/weapon/reagent_containers/glass/paint/cyan + icon_state = "paint_cyan" + paint_type = "#00FFFF" + +/obj/item/weapon/reagent_containers/glass/paint/white + name = "paint remover bucket" + icon_state = "paint_white" + paint_type = "#FFFFFF" + diff --git a/code/game/objects/items/weapons/paiwire.dm b/code/game/objects/items/weapons/paiwire.dm index 48358ea2594..6d415170149 100644 --- a/code/game/objects/items/weapons/paiwire.dm +++ b/code/game/objects/items/weapons/paiwire.dm @@ -1,19 +1,19 @@ -/obj/item/weapon/pai_cable/proc/plugin(obj/machinery/M as obj, mob/user as mob) - if(istype(M, /obj/machinery/door) || istype(M, /obj/machinery/camera)) - //VOREStation Add - Can't hack secured_wires doors (vault, etc) - if(istype(M, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = M - if(A.secured_wires) - to_chat(user,"\The [M] doesn't have any acessible data ports for \the [src]!") - return - //VOREStation Add End - user.visible_message("[user] inserts [src] into a data port on [M].", "You insert [src] into a data port on [M].", "You hear the satisfying click of a wire jack fastening into place.") - playsound(src, 'sound/machines/click.ogg', 50, 1) - user.drop_item() - src.forceMove(M) - src.machine = M - else - user.visible_message("[user] fumbles to find a place on [M] to plug in [src].", "There aren't any ports on [M] that match the jack belonging to [src].") - -/obj/item/weapon/pai_cable/attack(obj/machinery/M as obj, mob/user as mob) - src.plugin(M, user) +/obj/item/weapon/pai_cable/proc/plugin(obj/machinery/M as obj, mob/user as mob) + if(istype(M, /obj/machinery/door) || istype(M, /obj/machinery/camera)) + //VOREStation Add - Can't hack secured_wires doors (vault, etc) + if(istype(M, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = M + if(A.secured_wires) + to_chat(user,"\The [M] doesn't have any acessible data ports for \the [src]!") + return + //VOREStation Add End + user.visible_message("[user] inserts [src] into a data port on [M].", "You insert [src] into a data port on [M].", "You hear the satisfying click of a wire jack fastening into place.") + playsound(src, 'sound/machines/click.ogg', 50, 1) + user.drop_item() + src.forceMove(M) + src.machine = M + else + user.visible_message("[user] fumbles to find a place on [M] to plug in [src].", "There aren't any ports on [M] that match the jack belonging to [src].") + +/obj/item/weapon/pai_cable/attack(obj/machinery/M as obj, mob/user as mob) + src.plugin(M, user) diff --git a/code/game/objects/items/weapons/scrolls.dm b/code/game/objects/items/weapons/scrolls.dm index 646f38ab332..c4e25652834 100644 --- a/code/game/objects/items/weapons/scrolls.dm +++ b/code/game/objects/items/weapons/scrolls.dm @@ -1,96 +1,96 @@ -/obj/item/weapon/teleportation_scroll - name = "scroll of teleportation" - desc = "A scroll for moving around." - icon = 'icons/obj/wizard.dmi' - icon_state = "scroll" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' - ) - var/uses = 4.0 - w_class = ITEMSIZE_TINY - item_state = "paper" - throw_speed = 4 - throw_range = 20 - origin_tech = list(TECH_BLUESPACE = 4) - -/obj/item/weapon/teleportation_scroll/attack_self(mob/user as mob) - if((user.mind && !wizards.is_antagonist(user.mind))) - to_chat(usr, "You stare at the scroll but cannot make sense of the markings!") - return - - user.set_machine(src) - var/dat = "Teleportation Scroll:
                    " - dat += "Number of uses: [src.uses]
                    " - dat += "
                    " - dat += "Four uses use them wisely:
                    " - dat += "Teleport
                    " - dat += "Kind regards,
                    Wizards Federation

                    P.S. Don't forget to bring your gear, you'll need it to cast most spells.
                    " - user << browse(dat, "window=scroll") - onclose(user, "scroll") - return - -/obj/item/weapon/teleportation_scroll/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained() || src.loc != usr) - return - var/mob/living/carbon/human/H = usr - if (!( istype(H, /mob/living/carbon/human))) - return 1 - if ((usr == src.loc || (in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if (href_list["spell_teleport"]) - if (src.uses >= 1) - teleportscroll(H) - attack_self(H) - return - -/obj/item/weapon/teleportation_scroll/proc/teleportscroll(var/mob/user) - var/A = tgui_input_list(user, "Area to jump to:", "Teleportation Scroll", teleportlocs) - if(!A) - return - var/area/thearea = teleportlocs[A] - - if (user.stat || user.restrained()) - return - if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf))))) - return - - var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() - smoke.set_up(5, 0, user.loc) - smoke.attach(user) - smoke.start() - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L.len) - to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - if(user && user.buckled) - user.buckled.unbuckle_mob() - - var/list/tempL = L - var/attempt = null - var/success = 0 - while(tempL.len) - attempt = pick(tempL) - success = user.Move(attempt) - if(!success) - tempL.Remove(attempt) - else - break - - if(!success) - user.loc = pick(L) - - smoke.start() - src.uses -= 1 +/obj/item/weapon/teleportation_scroll + name = "scroll of teleportation" + desc = "A scroll for moving around." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' + ) + var/uses = 4.0 + w_class = ITEMSIZE_TINY + item_state = "paper" + throw_speed = 4 + throw_range = 20 + origin_tech = list(TECH_BLUESPACE = 4) + +/obj/item/weapon/teleportation_scroll/attack_self(mob/user as mob) + if((user.mind && !wizards.is_antagonist(user.mind))) + to_chat(usr, "You stare at the scroll but cannot make sense of the markings!") + return + + user.set_machine(src) + var/dat = "Teleportation Scroll:
                    " + dat += "Number of uses: [src.uses]
                    " + dat += "
                    " + dat += "Four uses use them wisely:
                    " + dat += "Teleport
                    " + dat += "Kind regards,
                    Wizards Federation

                    P.S. Don't forget to bring your gear, you'll need it to cast most spells.
                    " + user << browse(dat, "window=scroll") + onclose(user, "scroll") + return + +/obj/item/weapon/teleportation_scroll/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained() || src.loc != usr) + return + var/mob/living/carbon/human/H = usr + if (!( istype(H, /mob/living/carbon/human))) + return 1 + if ((usr == src.loc || (in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if (href_list["spell_teleport"]) + if (src.uses >= 1) + teleportscroll(H) + attack_self(H) + return + +/obj/item/weapon/teleportation_scroll/proc/teleportscroll(var/mob/user) + var/A = tgui_input_list(user, "Area to jump to:", "Teleportation Scroll", teleportlocs) + if(!A) + return + var/area/thearea = teleportlocs[A] + + if (user.stat || user.restrained()) + return + if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf))))) + return + + var/datum/effect/effect/system/smoke_spread/smoke = new /datum/effect/effect/system/smoke_spread() + smoke.set_up(5, 0, user.loc) + smoke.attach(user) + smoke.start() + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L.len) + to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") + return + + if(user && user.buckled) + user.buckled.unbuckle_mob() + + var/list/tempL = L + var/attempt = null + var/success = 0 + while(tempL.len) + attempt = pick(tempL) + success = user.Move(attempt) + if(!success) + tempL.Remove(attempt) + else + break + + if(!success) + user.loc = pick(L) + + smoke.start() + src.uses -= 1 diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index a2549b32797..13dc6fc7b0a 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -1,266 +1,266 @@ -//** Shield Helpers -//These are shared by various items that have shield-like behaviour - -//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. -/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) - //check attack direction - var/attack_dir = 0 //direction from the user to the source of the attack - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - attack_dir = get_dir(get_turf(user), P.starting) - else if(attacker) - attack_dir = get_dir(get_turf(user), get_turf(attacker)) - else if(damage_source) - attack_dir = get_dir(get_turf(user), get_turf(damage_source)) - - if(!(attack_dir && (attack_dir & bad_arc))) - return 1 - return 0 - -/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) - //parry only melee attacks - if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. - return default_parry_check(user, attacker, damage_source) - -/obj/item/weapon/shield - name = "shield" - var/base_block_chance = 50 - preserve_item = 1 - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - return 1 - return 0 - -/obj/item/weapon/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - return base_block_chance - -/obj/item/weapon/shield/riot - name = "riot shield" - desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "riot" - slot_flags = SLOT_BACK - force = 5.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_LARGE - origin_tech = list(TECH_MATERIAL = 2) - matter = list(MAT_GLASS = 7500, MAT_STEEL = 1000) - attack_verb = list("shoved", "bashed") - var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/weapon/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - //At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up - //to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however. - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/item/projectile/beam)) - //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. - //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ - //useless. Extremely penetrating projectiles will go through the shield without less damage. - user.visible_message("\The [user]'s [src.name] is pierced by [attack_text]!") - if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely. - P.damage = P.damage / 2 - return 0 - //Otherwise, if we're here, we're gonna stop the attack entirely. - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - return 1 - return 0 - -/obj/item/weapon/shield/riot/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/melee/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/* - * Energy Shield - */ - -/obj/item/weapon/shield/energy - name = "energy combat shield" - desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "eshield" - slot_flags = SLOT_EARS - flags = NOCONDUCT - force = 3.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_SMALL - var/lrange = 1.5 - var/lpower = 1.5 - var/lcolor = "#006AFF" - origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - attack_verb = list("shoved", "bashed") - var/active = 0 - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/weapon/shield/energy/handle_shield(mob/user) - if(!active) - return 0 //turn it on first! - . = ..() - - if(.) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - -/obj/item/weapon/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/item/projectile)) - var/obj/item/projectile/P = damage_source - if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) - return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance - return base_block_chance - -/obj/item/weapon/shield/energy/attack_self(mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You beat yourself in the head with [src].") - user.take_organ_damage(5) - active = !active - if (active) - force = 10 - update_icon() - w_class = ITEMSIZE_LARGE - slot_flags = null - playsound(src, 'sound/weapons/saberon.ogg', 50, 1) - to_chat(user, "\The [src] is now active.") - - else - force = 3 - update_icon() - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) - to_chat(user, "\The [src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/weapon/shield/energy/update_icon() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - if(lcolor) - blade_overlay.color = lcolor - color = lcolor - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - item_state = "[icon_state]_blade" - set_light(lrange, lpower, lcolor) - else - color = "FFFFFF" - set_light(0) - item_state = "[icon_state]" - - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - H.update_inv_l_hand() - H.update_inv_r_hand() - -/obj/item/weapon/shield/energy/AltClick(mob/living/user) - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - if(tgui_alert(usr, "Are you sure you want to recolor your shield?", "Confirm Recolor", list("Yes", "No")) == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input) - update_icon() - -/obj/item/weapon/shield/energy/examine(mob/user) - . = ..() - . += "Alt-click to recolor it." - -/obj/item/weapon/shield/riot/tele - name = "telescopic shield" - desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." - icon = 'icons/obj/weapons.dmi' - icon_state = "teleriot0" - slot_flags = null - force = 3 - throwforce = 3 - throw_speed = 3 - throw_range = 4 - w_class = ITEMSIZE_NORMAL - var/active = 0 -/* -/obj/item/weapon/shield/energy/IsShield() - if(active) - return 1 - else - return 0 -*/ -/obj/item/weapon/shield/riot/tele/attack_self(mob/living/user) - active = !active - icon_state = "teleriot[active]" - playsound(src, 'sound/weapons/empty.ogg', 50, 1) - - if(active) - force = 8 - throwforce = 5 - throw_speed = 2 - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - to_chat(user, "You extend \the [src].") - else - force = 3 - throwforce = 3 - throw_speed = 3 - w_class = ITEMSIZE_NORMAL - slot_flags = null - to_chat(user, "[src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return +//** Shield Helpers +//These are shared by various items that have shield-like behaviour + +//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. +/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) + //check attack direction + var/attack_dir = 0 //direction from the user to the source of the attack + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + attack_dir = get_dir(get_turf(user), P.starting) + else if(attacker) + attack_dir = get_dir(get_turf(user), get_turf(attacker)) + else if(damage_source) + attack_dir = get_dir(get_turf(user), get_turf(damage_source)) + + if(!(attack_dir && (attack_dir & bad_arc))) + return 1 + return 0 + +/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) + //parry only melee attacks + if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + return 1 + +/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. + return default_parry_check(user, attacker, damage_source) + +/obj/item/weapon/shield + name = "shield" + var/base_block_chance = 50 + preserve_item = 1 + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(check_shield_arc(user, bad_arc, damage_source, attacker)) + if(prob(get_block_chance(user, damage, damage_source, attacker))) + user.visible_message("\The [user] blocks [attack_text] with \the [src]!") + return 1 + return 0 + +/obj/item/weapon/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + return base_block_chance + +/obj/item/weapon/shield/riot + name = "riot shield" + desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." + icon = 'icons/obj/weapons.dmi' + icon_state = "riot" + slot_flags = SLOT_BACK + force = 5.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + origin_tech = list(TECH_MATERIAL = 2) + matter = list(MAT_GLASS = 7500, MAT_STEEL = 1000) + attack_verb = list("shoved", "bashed") + var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/weapon/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(user.incapacitated()) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(check_shield_arc(user, bad_arc, damage_source, attacker)) + if(prob(get_block_chance(user, damage, damage_source, attacker))) + //At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up + //to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however. + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/item/projectile/beam)) + //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. + //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ + //useless. Extremely penetrating projectiles will go through the shield without less damage. + user.visible_message("\The [user]'s [src.name] is pierced by [attack_text]!") + if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely. + P.damage = P.damage / 2 + return 0 + //Otherwise, if we're here, we're gonna stop the attack entirely. + user.visible_message("\The [user] blocks [attack_text] with \the [src]!") + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + return 1 + return 0 + +/obj/item/weapon/shield/riot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/melee/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/* + * Energy Shield + */ + +/obj/item/weapon/shield/energy + name = "energy combat shield" + desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." + icon = 'icons/obj/weapons.dmi' + icon_state = "eshield" + item_state = "eshield" + slot_flags = SLOT_EARS + flags = NOCONDUCT + force = 3.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_SMALL + var/lrange = 1.5 + var/lpower = 1.5 + var/lcolor = "#006AFF" + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + attack_verb = list("shoved", "bashed") + var/active = 0 + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + +/obj/item/weapon/shield/energy/handle_shield(mob/user) + if(!active) + return 0 //turn it on first! + . = ..() + + if(.) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + +/obj/item/weapon/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) + return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance + return base_block_chance + +/obj/item/weapon/shield/energy/attack_self(mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You beat yourself in the head with [src].") + user.take_organ_damage(5) + active = !active + if (active) + force = 10 + update_icon() + w_class = ITEMSIZE_LARGE + slot_flags = null + playsound(src, 'sound/weapons/saberon.ogg', 50, 1) + to_chat(user, "\The [src] is now active.") + + else + force = 3 + update_icon() + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + playsound(src, 'sound/weapons/saberoff.ogg', 50, 1) + to_chat(user, "\The [src] can now be concealed.") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return + +/obj/item/weapon/shield/energy/update_icon() + var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") + if(lcolor) + blade_overlay.color = lcolor + color = lcolor + cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other + if(active) + add_overlay(blade_overlay) + item_state = "[icon_state]_blade" + set_light(lrange, lpower, lcolor) + else + color = "FFFFFF" + set_light(0) + item_state = "[icon_state]" + + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + H.update_inv_l_hand() + H.update_inv_r_hand() + +/obj/item/weapon/shield/energy/AltClick(mob/living/user) + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + if(tgui_alert(usr, "Are you sure you want to recolor your shield?", "Confirm Recolor", list("Yes", "No")) == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = sanitize_hexcolor(energy_color_input) + update_icon() + +/obj/item/weapon/shield/energy/examine(mob/user) + . = ..() + . += "Alt-click to recolor it." + +/obj/item/weapon/shield/riot/tele + name = "telescopic shield" + desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." + icon = 'icons/obj/weapons.dmi' + icon_state = "teleriot0" + slot_flags = null + force = 3 + throwforce = 3 + throw_speed = 3 + throw_range = 4 + w_class = ITEMSIZE_NORMAL + var/active = 0 +/* +/obj/item/weapon/shield/energy/IsShield() + if(active) + return 1 + else + return 0 +*/ +/obj/item/weapon/shield/riot/tele/attack_self(mob/living/user) + active = !active + icon_state = "teleriot[active]" + playsound(src, 'sound/weapons/empty.ogg', 50, 1) + + if(active) + force = 8 + throwforce = 5 + throw_speed = 2 + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + to_chat(user, "You extend \the [src].") + else + force = 3 + throwforce = 3 + throw_speed = 3 + w_class = ITEMSIZE_NORMAL + slot_flags = null + to_chat(user, "[src] can now be concealed.") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + return diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 49aa20e4d9b..345884f6d4e 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -1,552 +1,552 @@ -/* - * Backpack - */ - -/obj/item/weapon/storage/backpack - name = "backpack" - desc = "You wear this on your back and put items into it." - icon = 'icons/inventory/back/item.dmi' - icon_state = "backpack" - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' - ) - w_class = ITEMSIZE_LARGE - slot_flags = SLOT_BACK - max_w_class = ITEMSIZE_LARGE - max_storage_space = INVENTORY_STANDARD_SPACE - var/flippable = 0 - var/side = 0 //0 = right, 1 = left - drop_sound = 'sound/items/drop/backpack.ogg' - pickup_sound = 'sound/items/pickup/backpack.ogg' - - -/obj/item/weapon/storage/backpack/equipped(var/mob/user, var/slot) - if (slot == slot_back && src.use_sound) - playsound(src, src.use_sound, 50, 1, -5) - ..(user, slot) - -/* -/obj/item/weapon/storage/backpack/dropped(mob/user as mob) - if (loc == user && src.use_sound) - playsound(src, src.use_sound, 50, 1, -5) - ..(user) -*/ - -/* - * Backpack Types - */ - -/obj/item/weapon/storage/backpack/holding - name = "bag of holding" - desc = "A backpack that opens into a localized pocket of Blue Space." - origin_tech = list(TECH_BLUESPACE = 4) - icon_state = "holdingpack" - max_w_class = ITEMSIZE_LARGE - max_storage_space = ITEMSIZE_COST_NORMAL * 14 // 56 - storage_cost = INVENTORY_STANDARD_SPACE + 1 - -/obj/item/weapon/storage/backpack/holding/duffle - name = "dufflebag of holding" - var/tilted = 0 - icon_state = "holdingduffle" - -/obj/item/weapon/storage/backpack/holding/duffle/Initialize() - . = ..() - if(prob(50)) - icon_state = "[icon_state]_tilted" - tilted = 1 - -/obj/item/weapon/storage/backpack/holding/duffle/verb/tilt() - set name = "Adjust Duffelbag Angle" - set desc = "Adjust the angle of your dufflebag for cosmetic effect" - set category = "Object" - set src in usr - if(!usr.canmove || usr.stat || usr.restrained()) - return - if(tilted) - icon_state = "[initial(icon_state)]" - to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") - tilted = 0 - else - icon_state = "[icon_state]_tilted" - to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") - tilted = 1 - update_icon() - usr.update_inv_back() - -/obj/item/weapon/storage/backpack/holding/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/storage/backpack/holding)) - to_chat(user, "The Bluespace interfaces of the two devices conflict and malfunction.") - qdel(W) - return - . = ..() - -//Please don't clutter the parent storage item with stupid hacks. -/obj/item/weapon/storage/backpack/holding/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(istype(W, /obj/item/weapon/storage/backpack/holding)) - return FALSE - return ..() - -/obj/item/weapon/storage/backpack/santabag - name = "\improper Santa's gift bag" - desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!" - icon_state = "giftbag0" - item_state_slots = list(slot_r_hand_str = "giftbag", slot_l_hand_str = "giftbag") - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 100 // can store a ton of shit! - -/obj/item/weapon/storage/backpack/cultpack - name = "trophy rack" - desc = "It's useful for both carrying extra gear and proudly declaring your insanity." - icon_state = "backpack_cult" - -/obj/item/weapon/storage/backpack/clown - name = "Giggles von Honkerton" - desc = "It's a backpack made by Honk! Co." - icon_state = "backpack_clown" - -/obj/item/weapon/storage/backpack/white - name = "white backpack" - icon_state = "backpack_white" - -/obj/item/weapon/storage/backpack/fancy - name = "fancy backpack" - icon_state = "backpack_fancy" - -/obj/item/weapon/storage/backpack/military - name = "military backpack" - icon_state = "backpack_military" - -/obj/item/weapon/storage/backpack/medic - name = "medical backpack" - desc = "It's a backpack especially designed for use in a sterile environment." - icon_state = "backpack_medical" - -/obj/item/weapon/storage/backpack/security - name = "security backpack" - desc = "It's a very robust backpack." - icon_state = "backpack_security" - -/obj/item/weapon/storage/backpack/captain - name = "site manager's backpack" - desc = "It's a special backpack made exclusively for officers." - icon_state = "backpack_captain" - -/obj/item/weapon/storage/backpack/industrial - name = "industrial backpack" - desc = "It's a tough backpack for the daily grind of station life." - icon_state = "backpack_industrial" - -/obj/item/weapon/storage/backpack/toxins - name = "laboratory backpack" - desc = "It's a light backpack modeled for use in laboratories and other scientific institutions." - icon_state = "backpack_purple" - -/obj/item/weapon/storage/backpack/hydroponics - name = "herbalist's backpack" - desc = "It's a green backpack with many pockets to store plants and tools in." - icon_state = "backpack_hydro" - -/obj/item/weapon/storage/backpack/genetics - name = "geneticist backpack" - desc = "It's a backpack fitted with slots for diskettes and other workplace tools." - icon_state = "backpack_blue" - -/obj/item/weapon/storage/backpack/virology - name = "sterile backpack" - desc = "It's a sterile backpack able to withstand different pathogens from entering its fabric." - icon_state = "backpack_green" - -/obj/item/weapon/storage/backpack/chemistry - name = "chemistry backpack" - desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles." - icon_state = "backpack_orange" - -/* - * Duffle Types - */ - -/obj/item/weapon/storage/backpack/dufflebag - name = "dufflebag" - desc = "A large dufflebag for holding extra things." - icon_state = "duffle" - slowdown = 0.5 - var/tilted = 0 - var/can_tilt = 1 - max_storage_space = INVENTORY_DUFFLEBAG_SPACE - -/obj/item/weapon/storage/backpack/dufflebag/Initialize() - . = ..() - if(prob(50)) - icon_state = "[icon_state]_tilted" - tilted = 1 - -/obj/item/weapon/storage/backpack/dufflebag/verb/tilt() - set name = "Adjust Duffelbag Angle" - set desc = "Adjust the angle of your dufflebag for cosmetic effect" - set category = "Object" - set src in usr - if(!usr.canmove || usr.stat || usr.restrained()) - return - if(!can_tilt) - to_chat(usr, "[src] can't be adjusted like that.") - return - if(tilted) - icon_state = "[initial(icon_state)]" - to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") - tilted = 0 - else - icon_state = "[icon_state]_tilted" - to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") - tilted = 1 - update_icon() - usr.update_inv_back() - -/obj/item/weapon/storage/backpack/dufflebag/syndie - name = "black dufflebag" - desc = "A large dufflebag for holding extra tactical supplies. This one appears to be made out of lighter material than usual." - icon_state = "duffle_syndie" - slowdown = 0 - -/obj/item/weapon/storage/backpack/dufflebag/syndie/med - name = "medical dufflebag" - desc = "A large dufflebag for holding extra tactical medical supplies. This one appears to be made out of lighter material than usual." - icon_state = "duffle_syndiemed" - -/obj/item/weapon/storage/backpack/dufflebag/syndie/ammo - name = "ammunition dufflebag" - desc = "A large dufflebag for holding extra weapons ammunition and supplies. This one appears to be made out of lighter material than usual." - icon_state = "duffle_syndieammo" - -/obj/item/weapon/storage/backpack/dufflebag/captain - name = "site manager's dufflebag" - desc = "A large dufflebag for holding extra captainly goods." - icon_state = "duffle_captain" - -/obj/item/weapon/storage/backpack/dufflebag/med - name = "medical dufflebag" - desc = "A large dufflebag for holding extra medical supplies." - icon_state = "duffle_medical" - -/obj/item/weapon/storage/backpack/dufflebag/emt - name = "EMT dufflebag" - desc = "A large dufflebag for holding extra medical supplies. This one has reflective stripes!" - icon_state = "duffle_emt" - -/obj/item/weapon/storage/backpack/dufflebag/sec - name = "security dufflebag" - desc = "A large dufflebag for holding extra security supplies and ammunition." - icon_state = "duffle_security" - -/obj/item/weapon/storage/backpack/dufflebag/eng - name = "industrial dufflebag" - desc = "A large dufflebag for holding extra tools and supplies." - icon_state = "duffle_industrial" - -/obj/item/weapon/storage/backpack/dufflebag/sci - name = "science dufflebag" - desc = "A large dufflebag for holding circuits and beakers." - icon_state = "duffle_science" - -/obj/item/weapon/storage/backpack/dufflebag/drone - name = "drone dufflebag" - desc = "A large dufflebag for holding small robots? Or maybe it's one used by robots!" - icon_state = "duffle_drone" - -/obj/item/weapon/storage/backpack/dufflebag/cursed - name = "cursed dufflebag" - desc = "That probably shouldn't be moving..." - icon_state = "duffle_curse" - -/* - * Satchel Types - */ - -/obj/item/weapon/storage/backpack/satchel - name = "leather satchel" - desc = "It's a very fancy satchel made with fine leather." - icon_state = "satchel" - -/obj/item/weapon/storage/backpack/satchel/withwallet - starts_with = list(/obj/item/weapon/storage/wallet/random) - -/obj/item/weapon/storage/backpack/satchel/norm - name = "satchel" - desc = "A trendy looking satchel." - icon_state = "satchel_grey" - -/obj/item/weapon/storage/backpack/satchel/white - name = "white satchel" - icon_state = "satchel_white" - -/obj/item/weapon/storage/backpack/satchel/fancy - name = "fancy satchel" - icon_state = "satchel_fancy" - -/obj/item/weapon/storage/backpack/satchel/military - name = "military satchel" - icon_state = "satchel_military" - -/obj/item/weapon/storage/backpack/satchel/eng - name = "industrial satchel" - desc = "A tough satchel with extra pockets." - icon_state = "satchel_industrial" - -/obj/item/weapon/storage/backpack/satchel/med - name = "medical satchel" - desc = "A sterile satchel used in medical departments." - icon_state = "satchel_medical" - -/obj/item/weapon/storage/backpack/satchel/vir - name = "virologist satchel" - desc = "A sterile satchel with virologist colours." - icon_state = "satchel_green" - -/obj/item/weapon/storage/backpack/satchel/chem - name = "chemist satchel" - desc = "A sterile satchel with chemist colours." - icon_state = "satchel_orange" - -/obj/item/weapon/storage/backpack/satchel/gen - name = "geneticist satchel" - desc = "A sterile satchel with geneticist colours." - icon_state = "satchel_blue" - -/obj/item/weapon/storage/backpack/satchel/tox - name = "scientist satchel" - desc = "Useful for holding research materials." - icon_state = "satchel_purple" - -/obj/item/weapon/storage/backpack/satchel/sec - name = "security satchel" - desc = "A robust satchel for security related needs." - icon_state = "satchel_security" - -/obj/item/weapon/storage/backpack/satchel/hyd - name = "hydroponics satchel" - desc = "A green satchel for plant related work." - icon_state = "satchel_hydro" - -/obj/item/weapon/storage/backpack/satchel/cap - name = "site manager's satchel" - desc = "An exclusive satchel for officers." - icon_state = "satchel_captain" - -//ERT backpacks. -/obj/item/weapon/storage/backpack/ert - name = "emergency response team backpack" - desc = "A spacious backpack with lots of pockets, used by members of the Emergency Response Team." - icon_state = "ert_commander" - -//Commander -/obj/item/weapon/storage/backpack/ert/commander - name = "emergency response team commander backpack" - desc = "A spacious backpack with lots of pockets, worn by the commander of an Emergency Response Team." - -//Security -/obj/item/weapon/storage/backpack/ert/security - name = "emergency response team security backpack" - desc = "A spacious backpack with lots of pockets, worn by security members of an Emergency Response Team." - icon_state = "ert_security" - -//Engineering -/obj/item/weapon/storage/backpack/ert/engineer - name = "emergency response team engineer backpack" - desc = "A spacious backpack with lots of pockets, worn by engineering members of an Emergency Response Team." - icon_state = "ert_engineering" - -//Medical -/obj/item/weapon/storage/backpack/ert/medical - name = "emergency response team medical backpack" - desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." - icon_state = "ert_medical" - -/* - * Courier Bags - */ - -/obj/item/weapon/storage/backpack/messenger - name = "messenger bag" - desc = "A sturdy backpack worn over one shoulder." - icon_state = "courier" - item_state_slots = list(slot_r_hand_str = "satchel_grey", slot_l_hand_str = "satchel_grey") - -/obj/item/weapon/storage/backpack/messenger/chem - name = "chemistry messenger bag" - desc = "A serile backpack worn over one shoulder. This one is in Chemsitry colors." - icon_state = "courier_chemistry" - item_state_slots = list(slot_r_hand_str = "satchel_orange", slot_l_hand_str = "satchel_orange") - -/obj/item/weapon/storage/backpack/messenger/med - name = "medical messenger bag" - desc = "A sterile backpack worn over one shoulder used in medical departments." - icon_state = "courier_medical" - item_state_slots = list(slot_r_hand_str = "satchel_medical", slot_l_hand_str = "satchel_medical") - -/obj/item/weapon/storage/backpack/messenger/viro - name = "virology messenger bag" - desc = "A sterile backpack worn over one shoulder. This one is in Virology colors." - icon_state = "courier_virology" - item_state_slots = list(slot_r_hand_str = "satchel_green", slot_l_hand_str = "satchel_green") - -/obj/item/weapon/storage/backpack/messenger/tox - name = "research messenger bag" - desc = "A backpack worn over one shoulder. Useful for holding science materials." - icon_state = "courier_toxins" - item_state_slots = list(slot_r_hand_str = "satchel_purple", slot_l_hand_str = "satchel_purple") - -/obj/item/weapon/storage/backpack/messenger/com - name = "command messenger bag" - desc = "A special backpack worn over one shoulder. This one is made specifically for officers." - icon_state = "courier_captain" - item_state_slots = list(slot_r_hand_str = "satchel_captain", slot_l_hand_str = "satchel_captain") - -/obj/item/weapon/storage/backpack/messenger/engi - name = "engineering messenger bag" - icon_state = "courier_industrial" - item_state_slots = list(slot_r_hand_str = "satchel_industrial", slot_l_hand_str = "satchel_industrial") - -/obj/item/weapon/storage/backpack/messenger/hyd - name = "hydroponics messenger bag" - desc = "A backpack worn over one shoulder. This one is designed for plant-related work." - icon_state = "courier_hydro" - item_state_slots = list(slot_r_hand_str = "satchel_hydro", slot_l_hand_str = "satchel_hydro") - -/obj/item/weapon/storage/backpack/messenger/sec - name = "security messenger bag" - desc = "A tactical backpack worn over one shoulder. This one is in Security colors." - icon_state = "courier_security" - item_state_slots = list(slot_r_hand_str = "satchel_security", slot_l_hand_str = "satchel_security") - -/obj/item/weapon/storage/backpack/messenger/black - icon_state = "courier_black" - - -/* - * Sport Bags - */ - -/obj/item/weapon/storage/backpack/sport - name = "sports backpack" - icon_state = "backsport" - -/obj/item/weapon/storage/backpack/sport/white - name = "white sports backpack" - icon_state = "backsport_white" - -/obj/item/weapon/storage/backpack/sport/fancy - name = "fancy sports backpack" - icon_state = "backsport_fancy" - -/obj/item/weapon/storage/backpack/sport/vir - name = "virologist sports backpack" - desc = "A sterile sports backpack with virologist colours." - icon_state = "backsport_green" - -/obj/item/weapon/storage/backpack/sport/chem - name = "chemist sports backpack" - desc = "A sterile sports backpack with chemist colours." - icon_state = "backsport_orange" - -/obj/item/weapon/storage/backpack/sport/gen - name = "geneticist sports backpack" - desc = "A sterile sports backpack with geneticist colours." - icon_state = "backsport_blue" - -/obj/item/weapon/storage/backpack/sport/tox - name = "scientist sports backpack" - desc = "Useful for holding research materials." - icon_state = "backsport_purple" - -/obj/item/weapon/storage/backpack/sport/sec - name = "security sports backpack" - desc = "A robust sports backpack for security related needs." - icon_state = "backsport_security" - -/obj/item/weapon/storage/backpack/sport/hyd - name = "hydroponics sports backpack" - desc = "A green sports backpack for plant related work." - icon_state = "backsport_hydro" - -//Purses -/obj/item/weapon/storage/backpack/purse - name = "purse" - desc = "A small, fashionable bag typically worn over the shoulder." - icon_state = "purse" - item_state_slots = list(slot_r_hand_str = "lgpurse", slot_l_hand_str = "lgpurse") - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 5 - -//Parachutes -/obj/item/weapon/storage/backpack/parachute - name = "parachute" - desc = "A specially made backpack, designed to help one survive jumping from incredible heights. It sacrifices some storage space for that added functionality." - icon_state = "parachute" - item_state_slots = list(slot_r_hand_str = "backpack", slot_l_hand_str = "backpack") - max_storage_space = ITEMSIZE_COST_NORMAL * 5 - -/obj/item/weapon/storage/backpack/parachute/examine(mob/user) - . = ..() - if(Adjacent(user)) - if(parachute) - . += "It seems to be packed." - else - . += "It seems to be unpacked." - -/obj/item/weapon/storage/backpack/parachute/handleParachute() - parachute = FALSE //If you parachute in, the parachute has probably been used. - -/obj/item/weapon/storage/backpack/parachute/verb/pack_parachute() - - set name = "Pack/Unpack Parachute" - set category = "Object" - set src in usr - - if(!istype(src.loc, /mob/living)) - return - - var/mob/living/carbon/human/H = usr - - if(!istype(H)) - return - if(H.stat) - return - if(H.back == src) - to_chat(H, "How do you expect to work on \the [src] while it's on your back?") - return - - if(!parachute) //This packs the parachute - H.visible_message("\The [H] starts to pack \the [src]!", \ - "You start to pack \the [src]!", \ - "You hear the shuffling of cloth.") - if(do_after(H, 50)) - H.visible_message("\The [H] finishes packing \the [src]!", \ - "You finish packing \the [src]!", \ - "You hear the shuffling of cloth.") - parachute = TRUE - else - H.visible_message("\The [src] gives up on packing \the [src]!", \ - "You give up on packing \the [src]!") - return - else //This unpacks the parachute - H.visible_message("\The [src] starts to unpack \the [src]!", \ - "You start to unpack \the [src]!", \ - "You hear the shuffling of cloth.") - if(do_after(H, 25)) - H.visible_message("\The [src] finishes unpacking \the [src]!", \ - "You finish unpacking \the [src]!", \ - "You hear the shuffling of cloth.") - parachute = FALSE - else - H.visible_message("\The [src] decides not to unpack \the [src]!", \ - "You decide not to unpack \the [src]!") - return - -/obj/item/weapon/storage/backpack/satchel/ranger - name = "ranger satchel" - desc = "A satchel designed for the Go Go ERT Rangers series to allow for slightly bigger carry capacity for the ERT-Rangers.\ - Unlike the show claims, it is not a phoron-enhanced satchel of holding with plot-relevant content." - icon = 'icons/obj/clothing/ranger.dmi' +/* + * Backpack + */ + +/obj/item/weapon/storage/backpack + name = "backpack" + desc = "You wear this on your back and put items into it." + icon = 'icons/inventory/back/item.dmi' + icon_state = "backpack" + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' + ) + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BACK + max_w_class = ITEMSIZE_LARGE + max_storage_space = INVENTORY_STANDARD_SPACE + var/flippable = 0 + var/side = 0 //0 = right, 1 = left + drop_sound = 'sound/items/drop/backpack.ogg' + pickup_sound = 'sound/items/pickup/backpack.ogg' + + +/obj/item/weapon/storage/backpack/equipped(var/mob/user, var/slot) + if (slot == slot_back && src.use_sound) + playsound(src, src.use_sound, 50, 1, -5) + ..(user, slot) + +/* +/obj/item/weapon/storage/backpack/dropped(mob/user as mob) + if (loc == user && src.use_sound) + playsound(src, src.use_sound, 50, 1, -5) + ..(user) +*/ + +/* + * Backpack Types + */ + +/obj/item/weapon/storage/backpack/holding + name = "bag of holding" + desc = "A backpack that opens into a localized pocket of Blue Space." + origin_tech = list(TECH_BLUESPACE = 4) + icon_state = "holdingpack" + max_w_class = ITEMSIZE_LARGE + max_storage_space = ITEMSIZE_COST_NORMAL * 14 // 56 + storage_cost = INVENTORY_STANDARD_SPACE + 1 + +/obj/item/weapon/storage/backpack/holding/duffle + name = "dufflebag of holding" + var/tilted = 0 + icon_state = "holdingduffle" + +/obj/item/weapon/storage/backpack/holding/duffle/Initialize() + . = ..() + if(prob(50)) + icon_state = "[icon_state]_tilted" + tilted = 1 + +/obj/item/weapon/storage/backpack/holding/duffle/verb/tilt() + set name = "Adjust Duffelbag Angle" + set desc = "Adjust the angle of your dufflebag for cosmetic effect" + set category = "Object" + set src in usr + if(!usr.canmove || usr.stat || usr.restrained()) + return + if(tilted) + icon_state = "[initial(icon_state)]" + to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") + tilted = 0 + else + icon_state = "[icon_state]_tilted" + to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") + tilted = 1 + update_icon() + usr.update_inv_back() + +/obj/item/weapon/storage/backpack/holding/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/storage/backpack/holding)) + to_chat(user, "The Bluespace interfaces of the two devices conflict and malfunction.") + qdel(W) + return + . = ..() + +//Please don't clutter the parent storage item with stupid hacks. +/obj/item/weapon/storage/backpack/holding/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(istype(W, /obj/item/weapon/storage/backpack/holding)) + return FALSE + return ..() + +/obj/item/weapon/storage/backpack/santabag + name = "\improper Santa's gift bag" + desc = "Space Santa uses this to deliver toys to all the nice children in space in Christmas! Wow, it's pretty big!" + icon_state = "giftbag0" + item_state_slots = list(slot_r_hand_str = "giftbag", slot_l_hand_str = "giftbag") + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 100 // can store a ton of shit! + +/obj/item/weapon/storage/backpack/cultpack + name = "trophy rack" + desc = "It's useful for both carrying extra gear and proudly declaring your insanity." + icon_state = "backpack_cult" + +/obj/item/weapon/storage/backpack/clown + name = "Giggles von Honkerton" + desc = "It's a backpack made by Honk! Co." + icon_state = "backpack_clown" + +/obj/item/weapon/storage/backpack/white + name = "white backpack" + icon_state = "backpack_white" + +/obj/item/weapon/storage/backpack/fancy + name = "fancy backpack" + icon_state = "backpack_fancy" + +/obj/item/weapon/storage/backpack/military + name = "military backpack" + icon_state = "backpack_military" + +/obj/item/weapon/storage/backpack/medic + name = "medical backpack" + desc = "It's a backpack especially designed for use in a sterile environment." + icon_state = "backpack_medical" + +/obj/item/weapon/storage/backpack/security + name = "security backpack" + desc = "It's a very robust backpack." + icon_state = "backpack_security" + +/obj/item/weapon/storage/backpack/captain + name = "site manager's backpack" + desc = "It's a special backpack made exclusively for officers." + icon_state = "backpack_captain" + +/obj/item/weapon/storage/backpack/industrial + name = "industrial backpack" + desc = "It's a tough backpack for the daily grind of station life." + icon_state = "backpack_industrial" + +/obj/item/weapon/storage/backpack/toxins + name = "laboratory backpack" + desc = "It's a light backpack modeled for use in laboratories and other scientific institutions." + icon_state = "backpack_purple" + +/obj/item/weapon/storage/backpack/hydroponics + name = "herbalist's backpack" + desc = "It's a green backpack with many pockets to store plants and tools in." + icon_state = "backpack_hydro" + +/obj/item/weapon/storage/backpack/genetics + name = "geneticist backpack" + desc = "It's a backpack fitted with slots for diskettes and other workplace tools." + icon_state = "backpack_blue" + +/obj/item/weapon/storage/backpack/virology + name = "sterile backpack" + desc = "It's a sterile backpack able to withstand different pathogens from entering its fabric." + icon_state = "backpack_green" + +/obj/item/weapon/storage/backpack/chemistry + name = "chemistry backpack" + desc = "It's an orange backpack which was designed to hold beakers, pill bottles and bottles." + icon_state = "backpack_orange" + +/* + * Duffle Types + */ + +/obj/item/weapon/storage/backpack/dufflebag + name = "dufflebag" + desc = "A large dufflebag for holding extra things." + icon_state = "duffle" + slowdown = 0.5 + var/tilted = 0 + var/can_tilt = 1 + max_storage_space = INVENTORY_DUFFLEBAG_SPACE + +/obj/item/weapon/storage/backpack/dufflebag/Initialize() + . = ..() + if(prob(50)) + icon_state = "[icon_state]_tilted" + tilted = 1 + +/obj/item/weapon/storage/backpack/dufflebag/verb/tilt() + set name = "Adjust Duffelbag Angle" + set desc = "Adjust the angle of your dufflebag for cosmetic effect" + set category = "Object" + set src in usr + if(!usr.canmove || usr.stat || usr.restrained()) + return + if(!can_tilt) + to_chat(usr, "[src] can't be adjusted like that.") + return + if(tilted) + icon_state = "[initial(icon_state)]" + to_chat(usr, "You adjust the angle of \the [src] to rest across your lower back.") + tilted = 0 + else + icon_state = "[icon_state]_tilted" + to_chat(usr, "You adjust the angle of \the [src] to rest diagonally across your back.") + tilted = 1 + update_icon() + usr.update_inv_back() + +/obj/item/weapon/storage/backpack/dufflebag/syndie + name = "black dufflebag" + desc = "A large dufflebag for holding extra tactical supplies. This one appears to be made out of lighter material than usual." + icon_state = "duffle_syndie" + slowdown = 0 + +/obj/item/weapon/storage/backpack/dufflebag/syndie/med + name = "medical dufflebag" + desc = "A large dufflebag for holding extra tactical medical supplies. This one appears to be made out of lighter material than usual." + icon_state = "duffle_syndiemed" + +/obj/item/weapon/storage/backpack/dufflebag/syndie/ammo + name = "ammunition dufflebag" + desc = "A large dufflebag for holding extra weapons ammunition and supplies. This one appears to be made out of lighter material than usual." + icon_state = "duffle_syndieammo" + +/obj/item/weapon/storage/backpack/dufflebag/captain + name = "site manager's dufflebag" + desc = "A large dufflebag for holding extra captainly goods." + icon_state = "duffle_captain" + +/obj/item/weapon/storage/backpack/dufflebag/med + name = "medical dufflebag" + desc = "A large dufflebag for holding extra medical supplies." + icon_state = "duffle_medical" + +/obj/item/weapon/storage/backpack/dufflebag/emt + name = "EMT dufflebag" + desc = "A large dufflebag for holding extra medical supplies. This one has reflective stripes!" + icon_state = "duffle_emt" + +/obj/item/weapon/storage/backpack/dufflebag/sec + name = "security dufflebag" + desc = "A large dufflebag for holding extra security supplies and ammunition." + icon_state = "duffle_security" + +/obj/item/weapon/storage/backpack/dufflebag/eng + name = "industrial dufflebag" + desc = "A large dufflebag for holding extra tools and supplies." + icon_state = "duffle_industrial" + +/obj/item/weapon/storage/backpack/dufflebag/sci + name = "science dufflebag" + desc = "A large dufflebag for holding circuits and beakers." + icon_state = "duffle_science" + +/obj/item/weapon/storage/backpack/dufflebag/drone + name = "drone dufflebag" + desc = "A large dufflebag for holding small robots? Or maybe it's one used by robots!" + icon_state = "duffle_drone" + +/obj/item/weapon/storage/backpack/dufflebag/cursed + name = "cursed dufflebag" + desc = "That probably shouldn't be moving..." + icon_state = "duffle_curse" + +/* + * Satchel Types + */ + +/obj/item/weapon/storage/backpack/satchel + name = "leather satchel" + desc = "It's a very fancy satchel made with fine leather." + icon_state = "satchel" + +/obj/item/weapon/storage/backpack/satchel/withwallet + starts_with = list(/obj/item/weapon/storage/wallet/random) + +/obj/item/weapon/storage/backpack/satchel/norm + name = "satchel" + desc = "A trendy looking satchel." + icon_state = "satchel_grey" + +/obj/item/weapon/storage/backpack/satchel/white + name = "white satchel" + icon_state = "satchel_white" + +/obj/item/weapon/storage/backpack/satchel/fancy + name = "fancy satchel" + icon_state = "satchel_fancy" + +/obj/item/weapon/storage/backpack/satchel/military + name = "military satchel" + icon_state = "satchel_military" + +/obj/item/weapon/storage/backpack/satchel/eng + name = "industrial satchel" + desc = "A tough satchel with extra pockets." + icon_state = "satchel_industrial" + +/obj/item/weapon/storage/backpack/satchel/med + name = "medical satchel" + desc = "A sterile satchel used in medical departments." + icon_state = "satchel_medical" + +/obj/item/weapon/storage/backpack/satchel/vir + name = "virologist satchel" + desc = "A sterile satchel with virologist colours." + icon_state = "satchel_green" + +/obj/item/weapon/storage/backpack/satchel/chem + name = "chemist satchel" + desc = "A sterile satchel with chemist colours." + icon_state = "satchel_orange" + +/obj/item/weapon/storage/backpack/satchel/gen + name = "geneticist satchel" + desc = "A sterile satchel with geneticist colours." + icon_state = "satchel_blue" + +/obj/item/weapon/storage/backpack/satchel/tox + name = "scientist satchel" + desc = "Useful for holding research materials." + icon_state = "satchel_purple" + +/obj/item/weapon/storage/backpack/satchel/sec + name = "security satchel" + desc = "A robust satchel for security related needs." + icon_state = "satchel_security" + +/obj/item/weapon/storage/backpack/satchel/hyd + name = "hydroponics satchel" + desc = "A green satchel for plant related work." + icon_state = "satchel_hydro" + +/obj/item/weapon/storage/backpack/satchel/cap + name = "site manager's satchel" + desc = "An exclusive satchel for officers." + icon_state = "satchel_captain" + +//ERT backpacks. +/obj/item/weapon/storage/backpack/ert + name = "emergency response team backpack" + desc = "A spacious backpack with lots of pockets, used by members of the Emergency Response Team." + icon_state = "ert_commander" + +//Commander +/obj/item/weapon/storage/backpack/ert/commander + name = "emergency response team commander backpack" + desc = "A spacious backpack with lots of pockets, worn by the commander of an Emergency Response Team." + +//Security +/obj/item/weapon/storage/backpack/ert/security + name = "emergency response team security backpack" + desc = "A spacious backpack with lots of pockets, worn by security members of an Emergency Response Team." + icon_state = "ert_security" + +//Engineering +/obj/item/weapon/storage/backpack/ert/engineer + name = "emergency response team engineer backpack" + desc = "A spacious backpack with lots of pockets, worn by engineering members of an Emergency Response Team." + icon_state = "ert_engineering" + +//Medical +/obj/item/weapon/storage/backpack/ert/medical + name = "emergency response team medical backpack" + desc = "A spacious backpack with lots of pockets, worn by medical members of an Emergency Response Team." + icon_state = "ert_medical" + +/* + * Courier Bags + */ + +/obj/item/weapon/storage/backpack/messenger + name = "messenger bag" + desc = "A sturdy backpack worn over one shoulder." + icon_state = "courier" + item_state_slots = list(slot_r_hand_str = "satchel_grey", slot_l_hand_str = "satchel_grey") + +/obj/item/weapon/storage/backpack/messenger/chem + name = "chemistry messenger bag" + desc = "A serile backpack worn over one shoulder. This one is in Chemsitry colors." + icon_state = "courier_chemistry" + item_state_slots = list(slot_r_hand_str = "satchel_orange", slot_l_hand_str = "satchel_orange") + +/obj/item/weapon/storage/backpack/messenger/med + name = "medical messenger bag" + desc = "A sterile backpack worn over one shoulder used in medical departments." + icon_state = "courier_medical" + item_state_slots = list(slot_r_hand_str = "satchel_medical", slot_l_hand_str = "satchel_medical") + +/obj/item/weapon/storage/backpack/messenger/viro + name = "virology messenger bag" + desc = "A sterile backpack worn over one shoulder. This one is in Virology colors." + icon_state = "courier_virology" + item_state_slots = list(slot_r_hand_str = "satchel_green", slot_l_hand_str = "satchel_green") + +/obj/item/weapon/storage/backpack/messenger/tox + name = "research messenger bag" + desc = "A backpack worn over one shoulder. Useful for holding science materials." + icon_state = "courier_toxins" + item_state_slots = list(slot_r_hand_str = "satchel_purple", slot_l_hand_str = "satchel_purple") + +/obj/item/weapon/storage/backpack/messenger/com + name = "command messenger bag" + desc = "A special backpack worn over one shoulder. This one is made specifically for officers." + icon_state = "courier_captain" + item_state_slots = list(slot_r_hand_str = "satchel_captain", slot_l_hand_str = "satchel_captain") + +/obj/item/weapon/storage/backpack/messenger/engi + name = "engineering messenger bag" + icon_state = "courier_industrial" + item_state_slots = list(slot_r_hand_str = "satchel_industrial", slot_l_hand_str = "satchel_industrial") + +/obj/item/weapon/storage/backpack/messenger/hyd + name = "hydroponics messenger bag" + desc = "A backpack worn over one shoulder. This one is designed for plant-related work." + icon_state = "courier_hydro" + item_state_slots = list(slot_r_hand_str = "satchel_hydro", slot_l_hand_str = "satchel_hydro") + +/obj/item/weapon/storage/backpack/messenger/sec + name = "security messenger bag" + desc = "A tactical backpack worn over one shoulder. This one is in Security colors." + icon_state = "courier_security" + item_state_slots = list(slot_r_hand_str = "satchel_security", slot_l_hand_str = "satchel_security") + +/obj/item/weapon/storage/backpack/messenger/black + icon_state = "courier_black" + + +/* + * Sport Bags + */ + +/obj/item/weapon/storage/backpack/sport + name = "sports backpack" + icon_state = "backsport" + +/obj/item/weapon/storage/backpack/sport/white + name = "white sports backpack" + icon_state = "backsport_white" + +/obj/item/weapon/storage/backpack/sport/fancy + name = "fancy sports backpack" + icon_state = "backsport_fancy" + +/obj/item/weapon/storage/backpack/sport/vir + name = "virologist sports backpack" + desc = "A sterile sports backpack with virologist colours." + icon_state = "backsport_green" + +/obj/item/weapon/storage/backpack/sport/chem + name = "chemist sports backpack" + desc = "A sterile sports backpack with chemist colours." + icon_state = "backsport_orange" + +/obj/item/weapon/storage/backpack/sport/gen + name = "geneticist sports backpack" + desc = "A sterile sports backpack with geneticist colours." + icon_state = "backsport_blue" + +/obj/item/weapon/storage/backpack/sport/tox + name = "scientist sports backpack" + desc = "Useful for holding research materials." + icon_state = "backsport_purple" + +/obj/item/weapon/storage/backpack/sport/sec + name = "security sports backpack" + desc = "A robust sports backpack for security related needs." + icon_state = "backsport_security" + +/obj/item/weapon/storage/backpack/sport/hyd + name = "hydroponics sports backpack" + desc = "A green sports backpack for plant related work." + icon_state = "backsport_hydro" + +//Purses +/obj/item/weapon/storage/backpack/purse + name = "purse" + desc = "A small, fashionable bag typically worn over the shoulder." + icon_state = "purse" + item_state_slots = list(slot_r_hand_str = "lgpurse", slot_l_hand_str = "lgpurse") + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + +//Parachutes +/obj/item/weapon/storage/backpack/parachute + name = "parachute" + desc = "A specially made backpack, designed to help one survive jumping from incredible heights. It sacrifices some storage space for that added functionality." + icon_state = "parachute" + item_state_slots = list(slot_r_hand_str = "backpack", slot_l_hand_str = "backpack") + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + +/obj/item/weapon/storage/backpack/parachute/examine(mob/user) + . = ..() + if(Adjacent(user)) + if(parachute) + . += "It seems to be packed." + else + . += "It seems to be unpacked." + +/obj/item/weapon/storage/backpack/parachute/handleParachute() + parachute = FALSE //If you parachute in, the parachute has probably been used. + +/obj/item/weapon/storage/backpack/parachute/verb/pack_parachute() + + set name = "Pack/Unpack Parachute" + set category = "Object" + set src in usr + + if(!istype(src.loc, /mob/living)) + return + + var/mob/living/carbon/human/H = usr + + if(!istype(H)) + return + if(H.stat) + return + if(H.back == src) + to_chat(H, "How do you expect to work on \the [src] while it's on your back?") + return + + if(!parachute) //This packs the parachute + H.visible_message("\The [H] starts to pack \the [src]!", \ + "You start to pack \the [src]!", \ + "You hear the shuffling of cloth.") + if(do_after(H, 50)) + H.visible_message("\The [H] finishes packing \the [src]!", \ + "You finish packing \the [src]!", \ + "You hear the shuffling of cloth.") + parachute = TRUE + else + H.visible_message("\The [src] gives up on packing \the [src]!", \ + "You give up on packing \the [src]!") + return + else //This unpacks the parachute + H.visible_message("\The [src] starts to unpack \the [src]!", \ + "You start to unpack \the [src]!", \ + "You hear the shuffling of cloth.") + if(do_after(H, 25)) + H.visible_message("\The [src] finishes unpacking \the [src]!", \ + "You finish unpacking \the [src]!", \ + "You hear the shuffling of cloth.") + parachute = FALSE + else + H.visible_message("\The [src] decides not to unpack \the [src]!", \ + "You decide not to unpack \the [src]!") + return + +/obj/item/weapon/storage/backpack/satchel/ranger + name = "ranger satchel" + desc = "A satchel designed for the Go Go ERT Rangers series to allow for slightly bigger carry capacity for the ERT-Rangers.\ + Unlike the show claims, it is not a phoron-enhanced satchel of holding with plot-relevant content." + icon = 'icons/obj/clothing/ranger.dmi' icon_state = "ranger_satchel" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index 01e0d7f2df6..a7137dded17 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -1,495 +1,495 @@ -/* - * These absorb the functionality of the plant bag, ore satchel, etc. - * They use the use_to_pickup, quick_gather, and quick_empty functions - * that were already defined in weapon/storage, but which had been - * re-implemented in other classes. - * - * Contains: - * Generic non-item - * Trash Bag - * Plastic Bag - * Mining Satchel - * Plant Bag - * Sheet Snatcher - * Sheet Snatcher (Cyborg) - * Cash Bag - * Chemistry Bag - * Food Bag - * Food Bag (Service Hound) - * Evidence Bag - * - * -Sayu - */ - - -// ----------------------------- -// Generic non-item -// ----------------------------- -/obj/item/weapon/storage/bag - allow_quick_gather = 1 - allow_quick_empty = 1 - display_contents_with_number = 0 // UNStABLE AS FuCK, turn on when it stops crashing clients - use_to_pickup = TRUE - slot_flags = SLOT_BELT - drop_sound = 'sound/items/drop/backpack.ogg' - pickup_sound = 'sound/items/pickup/backpack.ogg' - -// ----------------------------- -// Trash bag -// ----------------------------- -/obj/item/weapon/storage/bag/trash - name = "trash bag" - desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" - icon = 'icons/obj/janitor.dmi' - icon_state = "trashbag0" - item_state_slots = list(slot_r_hand_str = "trashbag", slot_l_hand_str = "trashbag") - drop_sound = 'sound/items/drop/wrapper.ogg' - pickup_sound = 'sound/items/pickup/wrapper.ogg' - - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_SMALL - max_storage_space = ITEMSIZE_SMALL * 21 - can_hold = list() // any - cant_hold = list(/obj/item/weapon/disk/nuclear) - -/obj/item/weapon/storage/bag/trash/update_icon() - if(contents.len == 0) - icon_state = "trashbag0" - else if(contents.len < 9) - icon_state = "trashbag1" - else if(contents.len < 18) - icon_state = "trashbag2" - else icon_state = "trashbag3" - -/obj/item/weapon/storage/bag/trash/holding - name = "trash bag of holding" - desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." - icon_state = "bluetrashbag" - origin_tech = list(TECH_BLUESPACE = 3) - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 10 // Slightly less than BoH - -/obj/item/weapon/storage/bag/trash/holding/update_icon() - return - -// ----------------------------- -// Plastic Bag -// ----------------------------- -/obj/item/weapon/storage/bag/plasticbag - name = "plastic bag" - desc = "It's a very flimsy, very noisy alternative to a bag." - icon = 'icons/obj/trash.dmi' - icon_state = "plasticbag" - drop_sound = 'sound/items/drop/wrapper.ogg' - pickup_sound = 'sound/items/pickup/wrapper.ogg' - - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_SMALL - can_hold = list() // any - cant_hold = list(/obj/item/weapon/disk/nuclear) - -// ----------------------------- -// Mining Satchel -// ----------------------------- -/* - * Mechoid - Orebags are the most common quick-gathering thing, and also have tons of lag associated with it. - * Their checks are going to be hyper-simplified due to this, and their INCREDIBLY singular target contents. - */ -/obj/item/weapon/storage/bag/ore - name = "mining satchel" - desc = "This little bugger can be used to store and transport ores." - icon = 'icons/obj/mining.dmi' - icon_state = "satchel" - slot_flags = SLOT_BELT | SLOT_POCKET - w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - can_hold = list(/obj/item/weapon/ore) - var/current_capacity = 0 - var/max_pickup = 100 //How much ore can be picked up in one go. There to prevent someone from walking on a turf with 10000 ore and making the server cry. - var/list/stored_ore = list( - "sand" = 0, - "hematite" = 0, - "carbon" = 0, - "raw copper" = 0, - "raw tin" = 0, - "void opal" = 0, - "painite" = 0, - "quartz" = 0, - "raw bauxite" = 0, - "phoron" = 0, - "silver" = 0, - "gold" = 0, - "marble" = 0, - "uranium" = 0, - "diamond" = 0, - "platinum" = 0, - "lead" = 0, - "mhydrogen" = 0, - "verdantium" = 0, - "rutile" = 0) - var/last_update = 0 - -/obj/item/weapon/storage/bag/ore/holding - name = "mining satchel of holding" - desc = "Like a mining satchel, but when you put your hand in, you're pretty sure you can feel time itself." - icon_state = "satchel_bspace" - max_storage_space = ITEMSIZE_COST_NORMAL * 15000 // This should never, ever, ever be reached. - -/obj/item/weapon/storage/bag/ore/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(current_capacity >= max_storage_space) - to_chat(user, "\the [src] is too full to possibly fit anything else inside of it.") - return - - if (istype(W, /obj/item/weapon/ore)) - var/obj/item/weapon/ore/ore = W - stored_ore[ore.material]++ - current_capacity++ - user.remove_from_mob(W) - qdel(ore) - -/obj/item/weapon/storage/bag/ore/remove_from_storage(obj/item/W as obj, atom/new_location) - if(!istype(W)) return 0 - - if(new_location) - if(ismob(loc)) - W.dropped(usr) - if(ismob(new_location)) - W.hud_layerise() - else - W.reset_plane_and_layer() - W.forceMove(new_location) - else - W.forceMove(get_turf(src)) - - W.on_exit_storage(src) - update_icon() - return 1 - -/obj/item/weapon/storage/bag/ore/gather_all(turf/T as turf, mob/user as mob, var/silent = 0) - var/success = 0 - var/failure = 0 - var/current_pickup = 0 - var/max_pickup_reached = 0 - for(var/obj/item/weapon/ore/O in T) //Only ever grabs ores. Doesn't do any extraneous checks, as all ore is the same size. Tons of checks means it causes hanging for up to three seconds. - if(current_capacity >= max_storage_space) - failure = 1 - break - if(current_pickup >= max_pickup) - max_pickup_reached = 1 - break - var/obj/item/weapon/ore/ore = O - stored_ore[ore.material]++ - current_capacity++ - current_pickup++ - qdel(ore) - success = 1 - if(!silent) //Let's do a single check and then do more instead of a bunch at once. - if(success && !failure && !max_pickup_reached) //Picked stuff up, did not reach capacity, did not reach max_pickup. - to_chat(user, "You put everything in [src].") - else if(success && failure) //Picked stuff up to capacity. - to_chat(user, "You fill the [src].") - else if(success && max_pickup_reached) //Picked stuff up to the max_pickup - to_chat(user, "You fill the [src] with as much as you can grab in one go.") - else //Failed. The bag is full. - to_chat(user, "You fail to pick anything up with \the [src].") - if(istype(user.pulling, /obj/structure/ore_box)) //Bit of a crappy way to do this, as it doubles spam for the user, but it works. //Then let me fix it. ~CL. - var/obj/structure/ore_box/OB = user.pulling - for(var/ore in stored_ore) - if(stored_ore[ore] > 0) - var/ore_amount = stored_ore[ore] // How many ores does the satchel have? - OB.stored_ore[ore] += ore_amount // Add the ore to the box - stored_ore[ore] = 0 // Set the value of the ore in the satchel to 0. - current_capacity = 0 // Set the amount of ore in the satchel to 0. - current_pickup = 0 - -/obj/item/weapon/storage/bag/ore/equipped(mob/user) - ..() - if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //Basically every place they can go. Makes sure it doesn't unregister if moved to other slots. - GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) - -/obj/item/weapon/storage/bag/ore/dropped(mob/user) - ..() - if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //See above. This should really be a define. - GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) - else - GLOB.moved_event.unregister(user, src) - -/obj/item/weapon/storage/bag/ore/proc/autoload(mob/user) - var/obj/item/weapon/ore/O = locate() in get_turf(src) - if(O) - gather_all(get_turf(src), user) - -/obj/item/weapon/storage/bag/ore/proc/rangedload(atom/A, mob/user) - var/obj/item/weapon/ore/O = locate() in get_turf(A) - if(O) - gather_all(get_turf(A), user) - -/obj/item/weapon/storage/bag/ore/examine(mob/user) - . = ..() - - if(!Adjacent(user)) //Can only check the contents of ore bags if you can physically reach them. - return . - - if(istype(user, /mob/living)) - add_fingerprint(user) - - . += "It holds:" - var/has_ore = 0 - for(var/ore in stored_ore) - if(stored_ore[ore] > 0) - . += "- [stored_ore[ore]] [ore]" - has_ore = 1 - if(!has_ore) - . += "Nothing." - -/obj/item/weapon/storage/bag/ore/open(mob/user as mob) //No opening it for the weird UI of having shit-tons of ore inside it. - user.examinate(src) - -// ----------------------------- -// Plant bag -// ----------------------------- -/obj/item/weapon/storage/bag/plants - name = "plant bag" - icon = 'icons/obj/hydroponics_machines_vr.dmi' - icon_state = "plantbag" - desc = "A sturdy bag used to transport fresh produce with ease." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown) - -/obj/item/weapon/storage/bag/plants/large - name = "large plant bag" - icon_state = "large_plantbag" - desc = "A large and sturdy bag used to transport fresh produce with ease." - max_storage_space = ITEMSIZE_COST_NORMAL * 50 - -// ----------------------------- -// Sheet Snatcher -// ----------------------------- -// Because it stacks stacks, this doesn't operate normally. -// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu - -/obj/item/weapon/storage/bag/sheetsnatcher - name = "sheet snatcher" - icon = 'icons/obj/mining.dmi' - icon_state = "sheetsnatcher" - desc = "A patented storage system designed for any kind of mineral sheet." - - var/capacity = 300; //the number of sheets it can carry. - w_class = ITEMSIZE_NORMAL - storage_slots = 7 - - allow_quick_empty = 1 // this function is superceded - -/obj/item/weapon/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!istype(W,/obj/item/stack/material)) - if(!stop_messages) - to_chat(usr, "The snatcher does not accept [W].") - return 0 - var/current = 0 - for(var/obj/item/stack/material/S in contents) - current += S.get_amount() - if(capacity == current)//If it's full, you're done - if(!stop_messages) - to_chat(usr, "The snatcher is full.") - return 0 - return 1 - - -// Modified handle_item_insertion. Would prefer not to, but... -/obj/item/weapon/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) - var/obj/item/stack/material/S = W - if(!istype(S)) return 0 - - var/amount - var/inserted = 0 - var/current = 0 - for(var/obj/item/stack/material/S2 in contents) - current += S2.get_amount() - if(capacity < current + S.get_amount())//If the stack will fill it up - amount = capacity - current - else - amount = S.get_amount() - - for(var/obj/item/stack/material/sheet in contents) - if(S.type == sheet.type) - // we are violating the amount limitation because these are not sane objects - sheet.set_amount(sheet.get_amount() + amount, TRUE) - S.use(amount) // will qdel() if we use it all - inserted = 1 - break - - if(!inserted) - usr.remove_from_mob(S) - if (usr.client && usr.s_active != src) - usr.client.screen -= S - S.dropped(usr) - S.loc = src - - orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - update_icon() - return 1 - -// Sets up numbered display to show the stack size of each stored mineral -// NOTE: numbered display is turned off currently because it's broken -/obj/item/weapon/storage/bag/sheetsnatcher/orient2hud(mob/user as mob) - var/adjusted_contents = contents.len - - //Numbered contents display - var/list/datum/numbered_display/numbered_contents - if(display_contents_with_number) - numbered_contents = list() - adjusted_contents = 0 - for(var/obj/item/stack/material/I in contents) - adjusted_contents++ - var/datum/numbered_display/D = new/datum/numbered_display(I) - D.number = I.get_amount() - numbered_contents.Add( D ) - - var/row_num = 0 - var/col_count = min(7,storage_slots) -1 - if (adjusted_contents > 7) - row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. - src.slot_orient_objs(row_num, col_count, numbered_contents) - return - -// Modified quick_empty verb drops appropriate sized stacks -/obj/item/weapon/storage/bag/sheetsnatcher/quick_empty() - var/location = get_turf(src) - for(var/obj/item/stack/material/S in contents) - var/cur_amount = S.get_amount() - var/full_stacks = round(cur_amount / S.max_amount) // Floor of current/max is amount of full stacks we make - var/remainder = cur_amount % S.max_amount // Current mod max is remainder after full sheets removed - for(var/i = 1 to full_stacks) - new S.type(location, S.max_amount) - if(remainder) - new S.type(location, remainder) - S.set_amount(0) - orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - update_icon() - -// Instead of removing -/obj/item/weapon/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W as obj, atom/new_location) - var/obj/item/stack/material/S = W - if(!istype(S)) return 0 - - //I would prefer to drop a new stack, but the item/attack_hand code - // that calls this can't recieve a different object than you clicked on. - //Therefore, make a new stack internally that has the remainder. - // -Sayu - - if(S.get_amount() > S.max_amount) - var/newstack_amt = S.get_amount() - S.max_amount - new S.type(src, newstack_amt) // The one we'll keep to replace the one we give - S.set_amount(S.max_amount) // The one we hand to the clicker - - return ..(S,new_location) - -// ----------------------------- -// Sheet Snatcher (Cyborg) -// ----------------------------- - -/obj/item/weapon/storage/bag/sheetsnatcher/borg - name = "sheet snatcher 9000" - desc = null - capacity = 500//Borgs get more because >specialization - -// ----------------------------- -// Cash Bag -// ----------------------------- - -/obj/item/weapon/storage/bag/cash - name = "cash bag" - icon = 'icons/obj/storage.dmi' - icon_state = "cashbag" - desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/coin,/obj/item/weapon/spacecash,/obj/item/weapon/spacecasinocash) - -// ----------------------------- -// Chemistry Bag -// ----------------------------- -/obj/item/weapon/storage/bag/chemistry - name = "chemistry bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "chembag" - desc = "A bag for storing pills, patches, and bottles." - max_storage_space = 200 - w_class = ITEMSIZE_LARGE - slowdown = 3 - can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle) - -// ----------------------------- -// Xeno Bag -// ----------------------------- -/obj/item/weapon/storage/bag/xeno - name = "xenobiology bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "xenobag" - desc = "A bag for storing various slime products." - max_storage_space = ITEMSIZE_COST_SMALL * 12 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/slime_extract,/obj/item/slimepotion, /obj/item/weapon/reagent_containers/food/snacks/monkeycube) - -// ----------------------------- -// Virology Bag -// ----------------------------- -/obj/item/weapon/storage/bag/virology - name = "virology bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "biobag" - desc = "A bag for storing various biological products." - max_storage_space = ITEMSIZE_COST_SMALL * 12 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial/,/obj/item/weapon/virusdish/) - -// ----------------------------- -// Food Bag -// ----------------------------- -/obj/item/weapon/storage/bag/food - name = "food bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "foodbag" - desc = "A bag for storing foods of all kinds." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment) - -// ----------------------------- -// Food Bag (Service Hound) -// ----------------------------- -/obj/item/weapon/storage/bag/serviceborg - name = "service bag" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "foodbag" - desc = "An intergrated bag for storing things of all kinds." - max_storage_space = ITEMSIZE_COST_NORMAL * 25 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment, - /obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/coin,/obj/item/weapon/spacecash, - /obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown,/obj/item/weapon/reagent_containers/pill) - -// ----------------------------- -// Evidence Bag -// ----------------------------- -/obj/item/weapon/storage/bag/detective - name = "secure satchel" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "detbag" - desc = "A bag for storing investigation things. You know, securely." - max_storage_space = ITEMSIZE_COST_NORMAL * 15 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_SMALL +/* + * These absorb the functionality of the plant bag, ore satchel, etc. + * They use the use_to_pickup, quick_gather, and quick_empty functions + * that were already defined in weapon/storage, but which had been + * re-implemented in other classes. + * + * Contains: + * Generic non-item + * Trash Bag + * Plastic Bag + * Mining Satchel + * Plant Bag + * Sheet Snatcher + * Sheet Snatcher (Cyborg) + * Cash Bag + * Chemistry Bag + * Food Bag + * Food Bag (Service Hound) + * Evidence Bag + * + * -Sayu + */ + + +// ----------------------------- +// Generic non-item +// ----------------------------- +/obj/item/weapon/storage/bag + allow_quick_gather = 1 + allow_quick_empty = 1 + display_contents_with_number = 0 // UNStABLE AS FuCK, turn on when it stops crashing clients + use_to_pickup = TRUE + slot_flags = SLOT_BELT + drop_sound = 'sound/items/drop/backpack.ogg' + pickup_sound = 'sound/items/pickup/backpack.ogg' + +// ----------------------------- +// Trash bag +// ----------------------------- +/obj/item/weapon/storage/bag/trash + name = "trash bag" + desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" + icon = 'icons/obj/janitor.dmi' + icon_state = "trashbag0" + item_state_slots = list(slot_r_hand_str = "trashbag", slot_l_hand_str = "trashbag") + drop_sound = 'sound/items/drop/wrapper.ogg' + pickup_sound = 'sound/items/pickup/wrapper.ogg' + + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_SMALL + max_storage_space = ITEMSIZE_SMALL * 21 + can_hold = list() // any + cant_hold = list(/obj/item/weapon/disk/nuclear) + +/obj/item/weapon/storage/bag/trash/update_icon() + if(contents.len == 0) + icon_state = "trashbag0" + else if(contents.len < 9) + icon_state = "trashbag1" + else if(contents.len < 18) + icon_state = "trashbag2" + else icon_state = "trashbag3" + +/obj/item/weapon/storage/bag/trash/holding + name = "trash bag of holding" + desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." + icon_state = "bluetrashbag" + origin_tech = list(TECH_BLUESPACE = 3) + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 10 // Slightly less than BoH + +/obj/item/weapon/storage/bag/trash/holding/update_icon() + return + +// ----------------------------- +// Plastic Bag +// ----------------------------- +/obj/item/weapon/storage/bag/plasticbag + name = "plastic bag" + desc = "It's a very flimsy, very noisy alternative to a bag." + icon = 'icons/obj/trash.dmi' + icon_state = "plasticbag" + drop_sound = 'sound/items/drop/wrapper.ogg' + pickup_sound = 'sound/items/pickup/wrapper.ogg' + + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_SMALL + can_hold = list() // any + cant_hold = list(/obj/item/weapon/disk/nuclear) + +// ----------------------------- +// Mining Satchel +// ----------------------------- +/* + * Mechoid - Orebags are the most common quick-gathering thing, and also have tons of lag associated with it. + * Their checks are going to be hyper-simplified due to this, and their INCREDIBLY singular target contents. + */ +/obj/item/weapon/storage/bag/ore + name = "mining satchel" + desc = "This little bugger can be used to store and transport ores." + icon = 'icons/obj/mining.dmi' + icon_state = "satchel" + slot_flags = SLOT_BELT | SLOT_POCKET + w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + can_hold = list(/obj/item/weapon/ore) + var/current_capacity = 0 + var/max_pickup = 100 //How much ore can be picked up in one go. There to prevent someone from walking on a turf with 10000 ore and making the server cry. + var/list/stored_ore = list( + "sand" = 0, + "hematite" = 0, + "carbon" = 0, + "raw copper" = 0, + "raw tin" = 0, + "void opal" = 0, + "painite" = 0, + "quartz" = 0, + "raw bauxite" = 0, + "phoron" = 0, + "silver" = 0, + "gold" = 0, + "marble" = 0, + "uranium" = 0, + "diamond" = 0, + "platinum" = 0, + "lead" = 0, + "mhydrogen" = 0, + "verdantium" = 0, + "rutile" = 0) + var/last_update = 0 + +/obj/item/weapon/storage/bag/ore/holding + name = "mining satchel of holding" + desc = "Like a mining satchel, but when you put your hand in, you're pretty sure you can feel time itself." + icon_state = "satchel_bspace" + max_storage_space = ITEMSIZE_COST_NORMAL * 15000 // This should never, ever, ever be reached. + +/obj/item/weapon/storage/bag/ore/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(current_capacity >= max_storage_space) + to_chat(user, "\the [src] is too full to possibly fit anything else inside of it.") + return + + if (istype(W, /obj/item/weapon/ore)) + var/obj/item/weapon/ore/ore = W + stored_ore[ore.material]++ + current_capacity++ + user.remove_from_mob(W) + qdel(ore) + +/obj/item/weapon/storage/bag/ore/remove_from_storage(obj/item/W as obj, atom/new_location) + if(!istype(W)) return 0 + + if(new_location) + if(ismob(loc)) + W.dropped(usr) + if(ismob(new_location)) + W.hud_layerise() + else + W.reset_plane_and_layer() + W.forceMove(new_location) + else + W.forceMove(get_turf(src)) + + W.on_exit_storage(src) + update_icon() + return 1 + +/obj/item/weapon/storage/bag/ore/gather_all(turf/T as turf, mob/user as mob, var/silent = 0) + var/success = 0 + var/failure = 0 + var/current_pickup = 0 + var/max_pickup_reached = 0 + for(var/obj/item/weapon/ore/O in T) //Only ever grabs ores. Doesn't do any extraneous checks, as all ore is the same size. Tons of checks means it causes hanging for up to three seconds. + if(current_capacity >= max_storage_space) + failure = 1 + break + if(current_pickup >= max_pickup) + max_pickup_reached = 1 + break + var/obj/item/weapon/ore/ore = O + stored_ore[ore.material]++ + current_capacity++ + current_pickup++ + qdel(ore) + success = 1 + if(!silent) //Let's do a single check and then do more instead of a bunch at once. + if(success && !failure && !max_pickup_reached) //Picked stuff up, did not reach capacity, did not reach max_pickup. + to_chat(user, "You put everything in [src].") + else if(success && failure) //Picked stuff up to capacity. + to_chat(user, "You fill the [src].") + else if(success && max_pickup_reached) //Picked stuff up to the max_pickup + to_chat(user, "You fill the [src] with as much as you can grab in one go.") + else //Failed. The bag is full. + to_chat(user, "You fail to pick anything up with \the [src].") + if(istype(user.pulling, /obj/structure/ore_box)) //Bit of a crappy way to do this, as it doubles spam for the user, but it works. //Then let me fix it. ~CL. + var/obj/structure/ore_box/OB = user.pulling + for(var/ore in stored_ore) + if(stored_ore[ore] > 0) + var/ore_amount = stored_ore[ore] // How many ores does the satchel have? + OB.stored_ore[ore] += ore_amount // Add the ore to the box + stored_ore[ore] = 0 // Set the value of the ore in the satchel to 0. + current_capacity = 0 // Set the amount of ore in the satchel to 0. + current_pickup = 0 + +/obj/item/weapon/storage/bag/ore/equipped(mob/user) + ..() + if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //Basically every place they can go. Makes sure it doesn't unregister if moved to other slots. + GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) + +/obj/item/weapon/storage/bag/ore/dropped(mob/user) + ..() + if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //See above. This should really be a define. + GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user) + else + GLOB.moved_event.unregister(user, src) + +/obj/item/weapon/storage/bag/ore/proc/autoload(mob/user) + var/obj/item/weapon/ore/O = locate() in get_turf(src) + if(O) + gather_all(get_turf(src), user) + +/obj/item/weapon/storage/bag/ore/proc/rangedload(atom/A, mob/user) + var/obj/item/weapon/ore/O = locate() in get_turf(A) + if(O) + gather_all(get_turf(A), user) + +/obj/item/weapon/storage/bag/ore/examine(mob/user) + . = ..() + + if(!Adjacent(user)) //Can only check the contents of ore bags if you can physically reach them. + return . + + if(istype(user, /mob/living)) + add_fingerprint(user) + + . += "It holds:" + var/has_ore = 0 + for(var/ore in stored_ore) + if(stored_ore[ore] > 0) + . += "- [stored_ore[ore]] [ore]" + has_ore = 1 + if(!has_ore) + . += "Nothing." + +/obj/item/weapon/storage/bag/ore/open(mob/user as mob) //No opening it for the weird UI of having shit-tons of ore inside it. + user.examinate(src) + +// ----------------------------- +// Plant bag +// ----------------------------- +/obj/item/weapon/storage/bag/plants + name = "plant bag" + icon = 'icons/obj/hydroponics_machines_vr.dmi' + icon_state = "plantbag" + desc = "A sturdy bag used to transport fresh produce with ease." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown) + +/obj/item/weapon/storage/bag/plants/large + name = "large plant bag" + icon_state = "large_plantbag" + desc = "A large and sturdy bag used to transport fresh produce with ease." + max_storage_space = ITEMSIZE_COST_NORMAL * 50 + +// ----------------------------- +// Sheet Snatcher +// ----------------------------- +// Because it stacks stacks, this doesn't operate normally. +// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu + +/obj/item/weapon/storage/bag/sheetsnatcher + name = "sheet snatcher" + icon = 'icons/obj/mining.dmi' + icon_state = "sheetsnatcher" + desc = "A patented storage system designed for any kind of mineral sheet." + + var/capacity = 300; //the number of sheets it can carry. + w_class = ITEMSIZE_NORMAL + storage_slots = 7 + + allow_quick_empty = 1 // this function is superceded + +/obj/item/weapon/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!istype(W,/obj/item/stack/material)) + if(!stop_messages) + to_chat(usr, "The snatcher does not accept [W].") + return 0 + var/current = 0 + for(var/obj/item/stack/material/S in contents) + current += S.get_amount() + if(capacity == current)//If it's full, you're done + if(!stop_messages) + to_chat(usr, "The snatcher is full.") + return 0 + return 1 + + +// Modified handle_item_insertion. Would prefer not to, but... +/obj/item/weapon/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) + var/obj/item/stack/material/S = W + if(!istype(S)) return 0 + + var/amount + var/inserted = 0 + var/current = 0 + for(var/obj/item/stack/material/S2 in contents) + current += S2.get_amount() + if(capacity < current + S.get_amount())//If the stack will fill it up + amount = capacity - current + else + amount = S.get_amount() + + for(var/obj/item/stack/material/sheet in contents) + if(S.type == sheet.type) + // we are violating the amount limitation because these are not sane objects + sheet.set_amount(sheet.get_amount() + amount, TRUE) + S.use(amount) // will qdel() if we use it all + inserted = 1 + break + + if(!inserted) + usr.remove_from_mob(S) + if (usr.client && usr.s_active != src) + usr.client.screen -= S + S.dropped(usr) + S.loc = src + + orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + update_icon() + return 1 + +// Sets up numbered display to show the stack size of each stored mineral +// NOTE: numbered display is turned off currently because it's broken +/obj/item/weapon/storage/bag/sheetsnatcher/orient2hud(mob/user as mob) + var/adjusted_contents = contents.len + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_contents_with_number) + numbered_contents = list() + adjusted_contents = 0 + for(var/obj/item/stack/material/I in contents) + adjusted_contents++ + var/datum/numbered_display/D = new/datum/numbered_display(I) + D.number = I.get_amount() + numbered_contents.Add( D ) + + var/row_num = 0 + var/col_count = min(7,storage_slots) -1 + if (adjusted_contents > 7) + row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. + src.slot_orient_objs(row_num, col_count, numbered_contents) + return + +// Modified quick_empty verb drops appropriate sized stacks +/obj/item/weapon/storage/bag/sheetsnatcher/quick_empty() + var/location = get_turf(src) + for(var/obj/item/stack/material/S in contents) + var/cur_amount = S.get_amount() + var/full_stacks = round(cur_amount / S.max_amount) // Floor of current/max is amount of full stacks we make + var/remainder = cur_amount % S.max_amount // Current mod max is remainder after full sheets removed + for(var/i = 1 to full_stacks) + new S.type(location, S.max_amount) + if(remainder) + new S.type(location, remainder) + S.set_amount(0) + orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + update_icon() + +// Instead of removing +/obj/item/weapon/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W as obj, atom/new_location) + var/obj/item/stack/material/S = W + if(!istype(S)) return 0 + + //I would prefer to drop a new stack, but the item/attack_hand code + // that calls this can't recieve a different object than you clicked on. + //Therefore, make a new stack internally that has the remainder. + // -Sayu + + if(S.get_amount() > S.max_amount) + var/newstack_amt = S.get_amount() - S.max_amount + new S.type(src, newstack_amt) // The one we'll keep to replace the one we give + S.set_amount(S.max_amount) // The one we hand to the clicker + + return ..(S,new_location) + +// ----------------------------- +// Sheet Snatcher (Cyborg) +// ----------------------------- + +/obj/item/weapon/storage/bag/sheetsnatcher/borg + name = "sheet snatcher 9000" + desc = null + capacity = 500//Borgs get more because >specialization + +// ----------------------------- +// Cash Bag +// ----------------------------- + +/obj/item/weapon/storage/bag/cash + name = "cash bag" + icon = 'icons/obj/storage.dmi' + icon_state = "cashbag" + desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/coin,/obj/item/weapon/spacecash,/obj/item/weapon/spacecasinocash) + +// ----------------------------- +// Chemistry Bag +// ----------------------------- +/obj/item/weapon/storage/bag/chemistry + name = "chemistry bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "chembag" + desc = "A bag for storing pills, patches, and bottles." + max_storage_space = 200 + w_class = ITEMSIZE_LARGE + slowdown = 3 + can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle) + +// ----------------------------- +// Xeno Bag +// ----------------------------- +/obj/item/weapon/storage/bag/xeno + name = "xenobiology bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "xenobag" + desc = "A bag for storing various slime products." + max_storage_space = ITEMSIZE_COST_SMALL * 12 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/slime_extract,/obj/item/slimepotion, /obj/item/weapon/reagent_containers/food/snacks/monkeycube) + +// ----------------------------- +// Virology Bag +// ----------------------------- +/obj/item/weapon/storage/bag/virology + name = "virology bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "biobag" + desc = "A bag for storing various biological products." + max_storage_space = ITEMSIZE_COST_SMALL * 12 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial/,/obj/item/weapon/virusdish/) + +// ----------------------------- +// Food Bag +// ----------------------------- +/obj/item/weapon/storage/bag/food + name = "food bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "foodbag" + desc = "A bag for storing foods of all kinds." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment) + +// ----------------------------- +// Food Bag (Service Hound) +// ----------------------------- +/obj/item/weapon/storage/bag/serviceborg + name = "service bag" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "foodbag" + desc = "An intergrated bag for storing things of all kinds." + max_storage_space = ITEMSIZE_COST_NORMAL * 25 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment, + /obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/coin,/obj/item/weapon/spacecash, + /obj/item/weapon/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/weapon/grown,/obj/item/weapon/reagent_containers/pill) + +// ----------------------------- +// Evidence Bag +// ----------------------------- +/obj/item/weapon/storage/bag/detective + name = "secure satchel" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "detbag" + desc = "A bag for storing investigation things. You know, securely." + max_storage_space = ITEMSIZE_COST_NORMAL * 15 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL can_hold = list(/obj/item/weapon/forensics/swab,/obj/item/weapon/sample/print,/obj/item/weapon/sample/fibers,/obj/item/weapon/evidencebag) \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index df9886cbb6d..e26ff9bd576 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -1,585 +1,592 @@ -/obj/item/weapon/storage/belt - name = "belt" - desc = "Can hold various things." - icon = 'icons/inventory/belt/item.dmi' - icon_state = "utility" - storage_slots = 7 - max_storage_space = ITEMSIZE_COST_NORMAL * 7 //This should ensure belts always have enough room to store whatever. - max_w_class = ITEMSIZE_NORMAL - slot_flags = SLOT_BELT - attack_verb = list("whipped", "lashed", "disciplined") - equip_sound = 'sound/items/toolbelt_equip.ogg' - drop_sound = 'sound/items/drop/toolbelt.ogg' - pickup_sound = 'sound/items/pickup/toolbelt.ogg' - sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/belt/mob_teshari.dmi') - - var/show_above_suit = 0 - -/obj/item/weapon/storage/belt/verb/toggle_layer() - set name = "Switch Belt Layer" - set category = "Object" - - if(show_above_suit == -1) - to_chat(usr, "\The [src] cannot be worn above your suit!") - return - show_above_suit = !show_above_suit - update_icon() - -//Some belts have sprites to show icons -/obj/item/weapon/storage/belt/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer = 0,var/icon/clip_mask = null) - var/image/standing = ..() - if(!inhands && contents.len) - for(var/obj/item/i in contents) - var/i_state = i.item_state - if(!i_state) i_state = i.icon_state - var/image/add_icon = image(icon = INV_BELT_DEF_ICON, icon_state = i_state) - if(istype(clip_mask)) //For taur bodies/tails clipping off parts of uniforms and suits. - standing.filters += filter(type = "alpha", icon = clip_mask) - standing.add_overlay(add_icon) - return standing - -/obj/item/weapon/storage/update_icon() - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_inv_belt() - -/obj/item/weapon/storage/belt/utility - name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing. - desc = "Can hold various tools." - icon_state = "utility" - can_hold = list( - ///obj/item/weapon/combitool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/wirecutters, - /obj/item/weapon/tool/wrench, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/clothing/glasses, - /obj/item/clothing/gloves, - /obj/item/device/pda, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/device/radio/headset, - /obj/item/device/robotanalyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/tape_roll, - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, //Vorestation edit adding debugger to toolbelt can hold list - /obj/item/weapon/shovel/spade, //VOREStation edit. If it can hold minihoes and hatchers, why not the gardening spade? - /obj/item/stack/nanopaste, //VOREStation edit. Think of it as a tube of superglue. Belts hold that all the time. - /obj/item/device/geiger //VOREStation edit. Engineers work with rad-slinging stuff sometimes too - ) - -/obj/item/weapon/storage/belt/utility/full - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/stack/cable_coil/random_belt - ) - -/obj/item/weapon/storage/belt/utility/full/multitool - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/stack/cable_coil/random_belt, - /obj/item/device/multitool - ) - -/obj/item/weapon/storage/belt/utility/atmostech - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/device/analyzer, //Vorestation edit. Gives atmos techs a few extra tools fitting their job from the start - /obj/item/weapon/extinguisher/mini //Vorestation edit. As above, the mini's much more handy to have rather than lugging a big one around - ) - -/obj/item/weapon/storage/belt/utility/chief - name = "chief engineer's toolbelt" - desc = "Holds tools, looks snazzy." - icon_state = "utilitybelt_ce" - item_state = "utility_ce" - storage_slots = 8 //If they get better everything-else, why not the belt too? - can_hold = list( - /obj/item/weapon/rcd, //They've given one from the get-go, it's hard to imagine they wouldn't be given something that can store it neater than a bag - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/wirecutters, - /obj/item/weapon/tool/wrench, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/clothing/glasses, - /obj/item/clothing/gloves, - /obj/item/device/pda, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/device/radio/headset, - /obj/item/device/robotanalyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/tape_roll, - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/weapon/shovel/spade, - /obj/item/stack/nanopaste, - /obj/item/device/geiger, - /obj/item/areaeditor/blueprints, //It's a bunch of paper that could prolly be rolled up & slipped into the belt, not to mention CE only, see the RCD's thing above - /obj/item/wire_reader //As above - ) - -/obj/item/weapon/storage/belt/utility/chief/full - starts_with = list( - /obj/item/weapon/tool/screwdriver/power, - /obj/item/weapon/tool/crowbar/power, - /obj/item/weapon/weldingtool/experimental, - /obj/item/device/multitool, - /obj/item/stack/cable_coil/random_belt, - /obj/item/weapon/extinguisher/mini, - /obj/item/device/analyzer - ) - -/obj/item/weapon/storage/belt/utility/holding - name = "tool-belt of holding" - desc = "A belt that uses localized bluespace pockets to hold more items than expected!" - icon_state = "utility_holding" - storage_slots = 14 //twice the amount as a normal belt - max_storage_space = ITEMSIZE_COST_NORMAL * 14 - can_hold = list( - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/wirecutters, - /obj/item/weapon/tool/wrench, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/clothing/glasses, - /obj/item/clothing/gloves, - /obj/item/device/pda, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/device/radio/headset, - /obj/item/device/robotanalyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/tape_roll, - /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/weapon/shovel/spade, - /obj/item/stack/nanopaste, - /obj/item/weapon/cell, //this is a bigger belt, might as well make it hold bigger cells too - /obj/item/weapon/pipe_dispenser, //bigger belt for bigger tools - /obj/item/weapon/rcd, //see above - /obj/item/device/quantum_pad_booster, - /obj/item/weapon/inducer, - /obj/item/stack/material/steel, - /obj/item/stack/material/glass, - /obj/item/device/lightreplacer, - /obj/item/weapon/pickaxe/plasmacutter - ) - - -/obj/item/weapon/storage/belt/medical - name = "medical belt" - desc = "Can hold various medical equipment." - icon_state = "medical" - can_hold = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/dnainjector, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/glass/beaker, - /obj/item/weapon/reagent_containers/glass/bottle, - /obj/item/weapon/reagent_containers/pill, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/weapon/storage/quickdraw/syringe_case, //VOREStation Addition - Adds syringe cases, - /obj/item/weapon/flame/lighter/zippo, - /obj/item/weapon/storage/fancy/cigarettes, - /obj/item/weapon/storage/pill_bottle, - /obj/item/stack/medical, - /obj/item/device/radio/headset, - /obj/item/device/pda, - /obj/item/taperoll, - /obj/item/device/megaphone, - /obj/item/clothing/mask/surgical, - /obj/item/clothing/head/surgery, - /obj/item/clothing/gloves, - /obj/item/weapon/reagent_containers/hypospray, - /obj/item/clothing/glasses, - /obj/item/weapon/tool/crowbar, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/storage/quickdraw/syringe_case - ) - -/obj/item/weapon/storage/belt/medical/emt - name = "EMT utility belt" - desc = "A sturdy black webbing belt with attached pouches." - icon_state = "ems" - -/obj/item/weapon/storage/belt/medical/holding - name = "medical belt of holding" - desc = "A belt that uses localized bluespace pockets to hold more items than expected!" - icon_state = "med_holding" - storage_slots = 14 //twice the amount as a normal belt - max_storage_space = ITEMSIZE_COST_NORMAL * 14 - -/obj/item/weapon/storage/belt/security - name = "security belt" - desc = "Can hold security gear like handcuffs and flashes." - icon_state = "security" - max_w_class = ITEMSIZE_NORMAL - can_hold = list( - /obj/item/weapon/grenade, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/handcuffs, - /obj/item/device/flash, - /obj/item/clothing/glasses, - /obj/item/ammo_casing/a12g, - /obj/item/ammo_magazine, - /obj/item/weapon/cell/device, - /obj/item/weapon/reagent_containers/food/snacks/donut/, - /obj/item/weapon/melee/baton, - /obj/item/weapon/gun/energy/taser, - /obj/item/weapon/gun/energy/stunrevolver, - /obj/item/weapon/gun/energy/stunrevolver/vintage, - /obj/item/weapon/gun/magnetic/railgun/heater/pistol, - /obj/item/weapon/gun/energy/gun, - /obj/item/weapon/flame/lighter, - /obj/item/device/flashlight, - /obj/item/device/taperecorder, - /obj/item/device/tape, - /obj/item/device/pda, - /obj/item/device/radio/headset, - /obj/item/clothing/gloves, - /obj/item/device/hailer, - /obj/item/device/megaphone, - /obj/item/weapon/melee, - /obj/item/clothing/accessory/badge, - /obj/item/weapon/gun/projectile/sec, - /obj/item/weapon/gun/projectile/p92x, - /obj/item/taperoll, - /obj/item/weapon/gun/projectile/colt/detective, - /obj/item/device/holowarrant, - /obj/item/device/ticket_printer //VOREStation Edit - ) - -/obj/item/weapon/storage/belt/detective - name = "forensic utility belt" - desc = "A belt for holding forensics equipment." - icon_state = "security" - storage_slots = 7 - max_w_class = ITEMSIZE_NORMAL - can_hold = list( - /obj/item/device/taperecorder, - /obj/item/device/tape, - /obj/item/clothing/glasses, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/reagent_containers/spray/luminol, - /obj/item/weapon/sample, - /obj/item/weapon/forensics/sample_kit/powder, - /obj/item/weapon/forensics/swab, - /obj/item/device/uv_light, - /obj/item/weapon/forensics/sample_kit, - /obj/item/weapon/photo, - /obj/item/device/camera_film, - /obj/item/device/camera, - /obj/item/weapon/autopsy_scanner, - /obj/item/device/mass_spectrometer, - /obj/item/clothing/accessory/badge, - /obj/item/device/reagent_scanner, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/device/pda, - /obj/item/device/hailer, - /obj/item/device/megaphone, - /obj/item/device/radio/headset, - /obj/item/clothing/gloves, - /obj/item/taperoll, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/handcuffs, - /obj/item/device/flash, - /obj/item/weapon/flame/lighter, - /obj/item/weapon/reagent_containers/food/snacks/donut/, - ///obj/item/ammo_magazine, //Detectives don't get projectile weapons as standard here - ///obj/item/weapon/gun/projectile/colt/detective, //Detectives don't get projectile weapons as standard here - /obj/item/weapon/gun/energy/stunrevolver/detective, //In keeping with the same vein as above, they can store their special one - /obj/item/device/holowarrant, - /obj/item/weapon/reagent_containers/food/drinks/flask, - /obj/item/device/ticket_printer //VOREStation Edit - ) - -/obj/item/weapon/storage/belt/soulstone - name = "soul stone belt" - desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" - icon_state = "soulstone" - storage_slots = 6 - can_hold = list( - /obj/item/device/soulstone - ) - -/obj/item/weapon/storage/belt/soulstone/full - starts_with = list(/obj/item/device/soulstone = 6) - -/obj/item/weapon/storage/belt/utility/alien - name = "alien belt" - desc = "A belt(?) that can hold things." - icon = 'icons/obj/abductor.dmi' - icon_state = "belt" - item_state = "security" - -/obj/item/weapon/storage/belt/utility/alien/full - starts_with = list( - /obj/item/weapon/tool/screwdriver/alien, - /obj/item/weapon/tool/wrench/alien, - /obj/item/weapon/weldingtool/alien, - /obj/item/weapon/tool/crowbar/alien, - /obj/item/weapon/tool/wirecutters/alien, - /obj/item/device/multitool/alien, - /obj/item/stack/cable_coil/alien - ) - -/obj/item/weapon/storage/belt/medical/alien - name = "alien belt" - desc = "A belt(?) that can hold things." - icon = 'icons/obj/abductor.dmi' - icon_state = "belt" - item_state = "security" - storage_slots = 8 - can_hold = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/dnainjector, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/glass/beaker, - /obj/item/weapon/reagent_containers/glass/bottle, - /obj/item/weapon/reagent_containers/pill, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/weapon/flame/lighter/zippo, - /obj/item/weapon/storage/fancy/cigarettes, - /obj/item/weapon/storage/pill_bottle, - /obj/item/stack/medical, - /obj/item/device/radio/headset, - /obj/item/device/pda, - /obj/item/taperoll, - /obj/item/device/megaphone, - /obj/item/clothing/mask/surgical, - /obj/item/clothing/head/surgery, - /obj/item/clothing/gloves, - /obj/item/weapon/reagent_containers/hypospray, - /obj/item/clothing/glasses, - /obj/item/weapon/tool/crowbar, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/surgical - ) - -/obj/item/weapon/storage/belt/medical/alien - starts_with = list( - /obj/item/weapon/surgical/scalpel/alien, - /obj/item/weapon/surgical/hemostat/alien, - /obj/item/weapon/surgical/retractor/alien, - /obj/item/weapon/surgical/circular_saw/alien, - /obj/item/weapon/surgical/FixOVein/alien, - /obj/item/weapon/surgical/bone_clamp/alien, - /obj/item/weapon/surgical/cautery/alien, - /obj/item/weapon/surgical/surgicaldrill/alien - ) - -/obj/item/weapon/storage/belt/champion - name = "championship belt" - desc = "Proves to the world that you are the strongest!" - icon_state = "champion" - storage_slots = 1 - can_hold = list( - "/obj/item/clothing/mask/luchador" - ) - -/obj/item/weapon/storage/belt/security/tactical - name = "combat belt" - desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." - icon_state = "swat" - storage_slots = 9 - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 7 - -/obj/item/weapon/storage/belt/bandolier - name = "shotgun bandolier" - desc = "Designed to hold shotgun shells. Can't really hold more than that." - icon_state = "bandolier1" - storage_slots = 8 - max_w_class = ITEMSIZE_TINY - can_hold = list( - /obj/item/ammo_casing/a12g, - /obj/item/ammo_casing/a12g/pellet, - /obj/item/ammo_casing/a12g/blank, - /obj/item/ammo_casing/a12g/practice, - /obj/item/ammo_casing/a12g/beanbag, - /obj/item/ammo_casing/a12g/stunshell, - /obj/item/ammo_casing/a12g/flash, - /obj/item/ammo_casing/a12g/emp, - /obj/item/ammo_casing/a12g/flechette - ) - -/obj/item/weapon/storage/belt/security/tactical/bandolier - name = "combat bandolier" - desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." - icon_state = "bandolier2" - -/obj/item/weapon/storage/belt/janitor - name = "janitorial belt" - desc = "A belt used to hold most janitorial supplies." - icon_state = "janitor" - storage_slots = 7 - max_w_class = ITEMSIZE_NORMAL - can_hold = list( - /obj/item/clothing/glasses, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/grenade, - /obj/item/device/pda, - /obj/item/device/radio/headset, - /obj/item/clothing/gloves, - /obj/item/clothing/mask/surgical, //sterile mask, - /obj/item/device/assembly/mousetrap, - /obj/item/weapon/light/bulb, - /obj/item/weapon/light/tube, - /obj/item/weapon/flame/lighter, - /obj/item/device/megaphone, - /obj/item/taperoll, - /obj/item/weapon/reagent_containers/spray, - /obj/item/weapon/soap, - /obj/item/device/lightreplacer //VOREStation edit - ) - -/obj/item/weapon/storage/belt/archaeology - name = "excavation gear-belt" - desc = "Can hold various excavation gear." - icon_state = "gear" - can_hold = list( - /obj/item/weapon/storage/box/samplebags, - /obj/item/device/core_sampler, - /obj/item/device/beacon_locator, - /obj/item/device/radio/beacon, - /obj/item/device/gps, - /obj/item/device/measuring_tape, - /obj/item/device/flashlight, - /obj/item/weapon/cell/device, - /obj/item/weapon/pickaxe, - /obj/item/device/depth_scanner, - /obj/item/device/camera, - /obj/item/weapon/paper, - /obj/item/weapon/photo, - /obj/item/weapon/folder, - /obj/item/weapon/pen, - /obj/item/weapon/folder, - /obj/item/weapon/clipboard, - /obj/item/weapon/anodevice, - /obj/item/clothing/glasses, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/storage/excavation, - /obj/item/weapon/anobattery, - /obj/item/device/ano_scanner, - /obj/item/weapon/pickaxe/hand, - /obj/item/device/xenoarch_multi_tool, - /obj/item/weapon/pickaxe/excavationdrill - ) - -/obj/item/weapon/storage/belt/fannypack - name = "leather fannypack" - desc = "A dorky fannypack for keeping small items in." - icon_state = "fannypack_leather" - item_state = "fannypack_leather" - max_w_class = ITEMSIZE_SMALL - storage_slots = null - max_storage_space = ITEMSIZE_COST_NORMAL * 2 - -/obj/item/weapon/storage/belt/fannypack/black - name = "black fannypack" - icon_state = "fannypack_black" - item_state = "fannypack_black" - -/obj/item/weapon/storage/belt/fannypack/blue - name = "blue fannypack" - icon_state = "fannypack_blue" - item_state = "fannypack_blue" - -/obj/item/weapon/storage/belt/fannypack/cyan - name = "cyan fannypack" - icon_state = "fannypack_cyan" - item_state = "fannypack_cyan" - -/obj/item/weapon/storage/belt/fannypack/green - name = "green fannypack" - icon_state = "fannypack_green" - item_state = "fannypack_green" - -/obj/item/weapon/storage/belt/fannypack/orange - name = "orange fannypack" - icon_state = "fannypack_orange" - item_state = "fannypack_orange" - -/obj/item/weapon/storage/belt/fannypack/purple - name = "purple fannypack" - icon_state = "fannypack_purple" - item_state = "fannypack_purple" - -/obj/item/weapon/storage/belt/fannypack/red - name = "red fannypack" - icon_state = "fannypack_red" - item_state = "fannypack_red" - -/obj/item/weapon/storage/belt/fannypack/white - name = "white fannypack" - icon_state = "fannypack_white" - item_state = "fannypack_white" - -/obj/item/weapon/storage/belt/fannypack/yellow - name = "yellow fannypack" - icon_state = "fannypack_yellow" - item_state = "fannypack_yellow" - -/obj/item/weapon/storage/belt/ranger - name = "ranger belt" - desc = "The fancy utility-belt holding the tools, cuffs and gadgets of the Go Go ERT-Rangers. The belt buckle is not real phoron, but it is still surprisingly comfortable to wear." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "ranger_belt" - -/obj/item/weapon/storage/belt/dbandolier - name = "\improper Donk-Soft bandolier" - desc = "A Donk-Soft bandolier! Carry your spare darts anywhere! Ages 8 and up." - icon_state = "dbandolier" - storage_slots = 8 - can_hold = list( - /obj/item/ammo_casing/afoam_dart - ) +/obj/item/weapon/storage/belt + name = "belt" + desc = "Can hold various things." + icon = 'icons/inventory/belt/item.dmi' + icon_state = "utility" + storage_slots = 7 + max_storage_space = ITEMSIZE_COST_NORMAL * 7 //This should ensure belts always have enough room to store whatever. + max_w_class = ITEMSIZE_NORMAL + slot_flags = SLOT_BELT + attack_verb = list("whipped", "lashed", "disciplined") + equip_sound = 'sound/items/toolbelt_equip.ogg' + drop_sound = 'sound/items/drop/toolbelt.ogg' + pickup_sound = 'sound/items/pickup/toolbelt.ogg' + sprite_sheets = list(SPECIES_TESHARI = 'icons/inventory/belt/mob_teshari.dmi') + + var/show_above_suit = 0 + +/obj/item/weapon/storage/belt/verb/toggle_layer() + set name = "Switch Belt Layer" + set category = "Object" + + if(show_above_suit == -1) + to_chat(usr, "\The [src] cannot be worn above your suit!") + return + show_above_suit = !show_above_suit + update_icon() + +//Some belts have sprites to show icons +/obj/item/weapon/storage/belt/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer = 0,var/icon/clip_mask = null) + var/image/standing = ..() + if(!inhands && contents.len) + for(var/obj/item/i in contents) + var/i_state = i.item_state + if(!i_state) i_state = i.icon_state + var/image/add_icon = image(icon = INV_BELT_DEF_ICON, icon_state = i_state) + if(istype(clip_mask)) //For taur bodies/tails clipping off parts of uniforms and suits. + standing.filters += filter(type = "alpha", icon = clip_mask) + standing.add_overlay(add_icon) + return standing + +/obj/item/weapon/storage/update_icon() + if (ismob(src.loc)) + var/mob/M = src.loc + M.update_inv_belt() + +/obj/item/weapon/storage/belt/utility + name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing. + desc = "Can hold various tools." + icon_state = "utility" + can_hold = list( + ///obj/item/weapon/combitool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/wirecutters, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/stack/cable_coil, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/clothing/glasses, + /obj/item/clothing/gloves, + /obj/item/device/pda, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/device/radio/headset, + /obj/item/device/robotanalyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/tape_roll, + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, //Vorestation edit adding debugger to toolbelt can hold list + /obj/item/weapon/shovel/spade, //VOREStation edit. If it can hold minihoes and hatchers, why not the gardening spade? + /obj/item/stack/nanopaste, //VOREStation edit. Think of it as a tube of superglue. Belts hold that all the time. + /obj/item/device/geiger //VOREStation edit. Engineers work with rad-slinging stuff sometimes too + ) + +/obj/item/weapon/storage/belt/utility/full + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/stack/cable_coil/random_belt + ) + +/obj/item/weapon/storage/belt/utility/full/multitool + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/stack/cable_coil/random_belt, + /obj/item/device/multitool + ) + +/obj/item/weapon/storage/belt/utility/atmostech + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/device/analyzer, //Vorestation edit. Gives atmos techs a few extra tools fitting their job from the start + /obj/item/weapon/extinguisher/mini //Vorestation edit. As above, the mini's much more handy to have rather than lugging a big one around + ) + +/obj/item/weapon/storage/belt/utility/chief + name = "chief engineer's toolbelt" + desc = "Holds tools, looks snazzy." + icon_state = "utilitybelt_ce" + item_state = "utility_ce" + storage_slots = 8 //If they get better everything-else, why not the belt too? + can_hold = list( + /obj/item/weapon/rcd, //They've given one from the get-go, it's hard to imagine they wouldn't be given something that can store it neater than a bag + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/wirecutters, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/stack/cable_coil, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/clothing/glasses, + /obj/item/clothing/gloves, + /obj/item/device/pda, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/device/radio/headset, + /obj/item/device/robotanalyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/tape_roll, + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/weapon/shovel/spade, + /obj/item/stack/nanopaste, + /obj/item/device/geiger, + /obj/item/areaeditor/blueprints, //It's a bunch of paper that could prolly be rolled up & slipped into the belt, not to mention CE only, see the RCD's thing above + /obj/item/wire_reader //As above + ) + +/obj/item/weapon/storage/belt/utility/chief/full + starts_with = list( + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/weapon/weldingtool/experimental, + /obj/item/device/multitool, + /obj/item/stack/cable_coil/random_belt, + /obj/item/weapon/extinguisher/mini, + /obj/item/device/analyzer + ) + +/obj/item/weapon/storage/belt/utility/holding + name = "tool-belt of holding" + desc = "A belt that uses localized bluespace pockets to hold more items than expected!" + icon_state = "utility_holding" + storage_slots = 14 //twice the amount as a normal belt + max_storage_space = ITEMSIZE_COST_NORMAL * 14 + can_hold = list( + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/wirecutters, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/stack/cable_coil, + /obj/item/device/t_scanner, + /obj/item/device/analyzer, + /obj/item/clothing/glasses, + /obj/item/clothing/gloves, + /obj/item/device/pda, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/device/radio/headset, + /obj/item/device/robotanalyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/tape_roll, + /obj/item/device/integrated_electronics/wirer, + /obj/item/device/integrated_electronics/debugger, + /obj/item/weapon/shovel/spade, + /obj/item/stack/nanopaste, + /obj/item/weapon/cell, //this is a bigger belt, might as well make it hold bigger cells too + /obj/item/weapon/pipe_dispenser, //bigger belt for bigger tools + /obj/item/weapon/rcd, //see above + /obj/item/device/quantum_pad_booster, + /obj/item/weapon/inducer, + /obj/item/stack/material/steel, + /obj/item/stack/material/glass, + /obj/item/device/lightreplacer, + /obj/item/weapon/pickaxe/plasmacutter + ) + + +/obj/item/weapon/storage/belt/medical + name = "medical belt" + desc = "Can hold various medical equipment." + icon_state = "medical" + can_hold = list( + /obj/item/device/healthanalyzer, + /obj/item/weapon/dnainjector, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/glass/beaker, + /obj/item/weapon/reagent_containers/glass/bottle, + /obj/item/weapon/reagent_containers/pill, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/weapon/storage/quickdraw/syringe_case, //VOREStation Addition - Adds syringe cases, + /obj/item/weapon/flame/lighter/zippo, + /obj/item/weapon/storage/fancy/cigarettes, + /obj/item/weapon/storage/pill_bottle, + /obj/item/stack/medical, + /obj/item/device/radio/headset, + /obj/item/device/pda, + /obj/item/taperoll, + /obj/item/device/megaphone, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/head/surgery, + /obj/item/clothing/gloves, + /obj/item/weapon/reagent_containers/hypospray, + /obj/item/clothing/glasses, + /obj/item/weapon/tool/crowbar, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/storage/quickdraw/syringe_case + ) + +/obj/item/weapon/storage/belt/medical/emt + name = "EMT utility belt" + desc = "A sturdy black webbing belt with attached pouches." + icon_state = "ems" + +/obj/item/weapon/storage/belt/medical/holding + name = "medical belt of holding" + desc = "A belt that uses localized bluespace pockets to hold more items than expected!" + icon_state = "med_holding" + storage_slots = 14 //twice the amount as a normal belt + max_storage_space = ITEMSIZE_COST_NORMAL * 14 + +/obj/item/weapon/storage/belt/security + name = "security belt" + desc = "Can hold security gear like handcuffs and flashes." + icon_state = "security" + max_w_class = ITEMSIZE_NORMAL + can_hold = list( + /obj/item/weapon/grenade, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/handcuffs, + /obj/item/device/flash, + /obj/item/clothing/glasses, + /obj/item/ammo_casing/a12g, + /obj/item/ammo_magazine, + /obj/item/weapon/cell/device, + /obj/item/weapon/reagent_containers/food/snacks/donut/, + /obj/item/weapon/melee/baton, + /obj/item/weapon/gun/energy/taser, + /obj/item/weapon/gun/energy/stunrevolver, + /obj/item/weapon/gun/energy/stunrevolver/vintage, + /obj/item/weapon/gun/magnetic/railgun/heater/pistol, + /obj/item/weapon/gun/energy/gun, + /obj/item/weapon/flame/lighter, + /obj/item/device/flashlight, + /obj/item/device/taperecorder, + /obj/item/device/tape, + /obj/item/device/pda, + /obj/item/device/radio/headset, + /obj/item/clothing/gloves, + /obj/item/device/hailer, + /obj/item/device/megaphone, + /obj/item/weapon/melee, + /obj/item/clothing/accessory/badge, + /obj/item/weapon/gun/projectile/sec, + /obj/item/weapon/gun/projectile/p92x, + /obj/item/taperoll, + /obj/item/weapon/gun/projectile/colt/detective, + /obj/item/device/holowarrant, + /obj/item/device/ticket_printer //VOREStation Edit + ) + +/obj/item/weapon/storage/belt/detective + name = "forensic utility belt" + desc = "A belt for holding forensics equipment." + icon_state = "security" + storage_slots = 7 + max_w_class = ITEMSIZE_NORMAL + can_hold = list( + /obj/item/device/taperecorder, + /obj/item/device/tape, + /obj/item/clothing/glasses, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/reagent_containers/spray/luminol, + /obj/item/weapon/sample, + /obj/item/weapon/forensics/sample_kit/powder, + /obj/item/weapon/forensics/swab, + /obj/item/device/uv_light, + /obj/item/weapon/forensics/sample_kit, + /obj/item/weapon/photo, + /obj/item/device/camera_film, + /obj/item/device/camera, + /obj/item/weapon/autopsy_scanner, + /obj/item/device/mass_spectrometer, + /obj/item/clothing/accessory/badge, + /obj/item/device/reagent_scanner, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/device/pda, + /obj/item/device/hailer, + /obj/item/device/megaphone, + /obj/item/device/radio/headset, + /obj/item/clothing/gloves, + /obj/item/taperoll, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/handcuffs, + /obj/item/device/flash, + /obj/item/weapon/flame/lighter, + /obj/item/weapon/reagent_containers/food/snacks/donut/, + ///obj/item/ammo_magazine, //Detectives don't get projectile weapons as standard here + ///obj/item/weapon/gun/projectile/colt/detective, //Detectives don't get projectile weapons as standard here + /obj/item/weapon/gun/energy/stunrevolver/detective, //In keeping with the same vein as above, they can store their special one + /obj/item/device/holowarrant, + /obj/item/weapon/reagent_containers/food/drinks/flask, + /obj/item/device/ticket_printer //VOREStation Edit + ) + +/obj/item/weapon/storage/belt/soulstone + name = "soul stone belt" + desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" + icon_state = "soulstone" + storage_slots = 6 + can_hold = list( + /obj/item/device/soulstone + ) + +/obj/item/weapon/storage/belt/soulstone/full + starts_with = list(/obj/item/device/soulstone = 6) + +/obj/item/weapon/storage/belt/utility/alien + name = "alien belt" + desc = "A belt(?) that can hold things." + icon = 'icons/obj/abductor.dmi' + icon_state = "belt" + item_state = "security" + +/obj/item/weapon/storage/belt/utility/alien/full + starts_with = list( + /obj/item/weapon/tool/screwdriver/alien, + /obj/item/weapon/tool/wrench/alien, + /obj/item/weapon/weldingtool/alien, + /obj/item/weapon/tool/crowbar/alien, + /obj/item/weapon/tool/wirecutters/alien, + /obj/item/device/multitool/alien, + /obj/item/stack/cable_coil/alien + ) + +/obj/item/weapon/storage/belt/medical/alien + name = "alien belt" + desc = "A belt(?) that can hold things." + icon = 'icons/obj/abductor.dmi' + icon_state = "belt" + item_state = "security" + storage_slots = 8 + can_hold = list( + /obj/item/device/healthanalyzer, + /obj/item/weapon/dnainjector, + /obj/item/weapon/reagent_containers/dropper, + /obj/item/weapon/reagent_containers/glass/beaker, + /obj/item/weapon/reagent_containers/glass/bottle, + /obj/item/weapon/reagent_containers/pill, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/weapon/flame/lighter/zippo, + /obj/item/weapon/storage/fancy/cigarettes, + /obj/item/weapon/storage/pill_bottle, + /obj/item/stack/medical, + /obj/item/device/radio/headset, + /obj/item/device/pda, + /obj/item/taperoll, + /obj/item/device/megaphone, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/head/surgery, + /obj/item/clothing/gloves, + /obj/item/weapon/reagent_containers/hypospray, + /obj/item/clothing/glasses, + /obj/item/weapon/tool/crowbar, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/surgical + ) + +/obj/item/weapon/storage/belt/medical/alien + starts_with = list( + /obj/item/weapon/surgical/scalpel/alien, + /obj/item/weapon/surgical/hemostat/alien, + /obj/item/weapon/surgical/retractor/alien, + /obj/item/weapon/surgical/circular_saw/alien, + /obj/item/weapon/surgical/FixOVein/alien, + /obj/item/weapon/surgical/bone_clamp/alien, + /obj/item/weapon/surgical/cautery/alien, + /obj/item/weapon/surgical/surgicaldrill/alien + ) + +/obj/item/weapon/storage/belt/champion + name = "championship belt" + desc = "Proves to the world that you are the strongest!" + icon_state = "champion" + storage_slots = 1 + can_hold = list( + "/obj/item/clothing/mask/luchador" + ) + +/obj/item/weapon/storage/belt/security/tactical + name = "combat belt" + desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." + icon_state = "swat" + storage_slots = 9 + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 7 + +/obj/item/weapon/storage/belt/bandolier + name = "shotgun bandolier" + desc = "Designed to hold shotgun shells. Can't really hold more than that." + icon_state = "bandolier1" + storage_slots = 8 + max_w_class = ITEMSIZE_TINY + can_hold = list( + /obj/item/ammo_casing/a12g, + /obj/item/ammo_casing/a12g/pellet, + /obj/item/ammo_casing/a12g/blank, + /obj/item/ammo_casing/a12g/practice, + /obj/item/ammo_casing/a12g/beanbag, + /obj/item/ammo_casing/a12g/stunshell, + /obj/item/ammo_casing/a12g/flash, + /obj/item/ammo_casing/a12g/emp, + /obj/item/ammo_casing/a12g/flechette + ) + +/obj/item/weapon/storage/belt/security/tactical/bandolier + name = "combat bandolier" + desc = "Can hold security gear like handcuffs and flashes, with more pouches for more storage." + icon_state = "bandolier2" + +/obj/item/weapon/storage/belt/janitor + name = "janitorial belt" + desc = "A belt used to hold most janitorial supplies." + icon_state = "janitor" + storage_slots = 7 + max_w_class = ITEMSIZE_NORMAL + can_hold = list( + /obj/item/clothing/glasses, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/grenade, + /obj/item/device/pda, + /obj/item/device/radio/headset, + /obj/item/clothing/gloves, + /obj/item/clothing/mask/surgical, //sterile mask, + /obj/item/device/assembly/mousetrap, + /obj/item/weapon/light/bulb, + /obj/item/weapon/light/tube, + /obj/item/weapon/flame/lighter, + /obj/item/device/megaphone, + /obj/item/taperoll, + /obj/item/weapon/reagent_containers/spray, + /obj/item/weapon/soap, + /obj/item/device/lightreplacer //VOREStation edit + ) + +/obj/item/weapon/storage/belt/archaeology + name = "excavation gear-belt" + desc = "Can hold various excavation gear." + icon_state = "gear" + can_hold = list( + /obj/item/weapon/storage/box/samplebags, + /obj/item/device/core_sampler, + /obj/item/device/beacon_locator, + /obj/item/device/radio/beacon, + /obj/item/device/gps, + /obj/item/device/measuring_tape, + /obj/item/device/flashlight, + /obj/item/weapon/cell/device, + /obj/item/weapon/pickaxe, + /obj/item/device/depth_scanner, + /obj/item/device/camera, + /obj/item/weapon/paper, + /obj/item/weapon/photo, + /obj/item/weapon/folder, + /obj/item/weapon/pen, + /obj/item/weapon/folder, + /obj/item/weapon/clipboard, + /obj/item/weapon/anodevice, + /obj/item/clothing/glasses, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/storage/excavation, + /obj/item/weapon/anobattery, + /obj/item/device/ano_scanner, + /obj/item/weapon/pickaxe/hand, + /obj/item/device/xenoarch_multi_tool, + /obj/item/weapon/pickaxe/excavationdrill + ) + +/obj/item/weapon/storage/belt/fannypack + name = "leather fannypack" + desc = "A dorky fannypack for keeping small items in." + icon_state = "fannypack_leather" + item_state = "fannypack_leather" + max_w_class = ITEMSIZE_SMALL + storage_slots = null + max_storage_space = ITEMSIZE_COST_NORMAL * 2 + +/obj/item/weapon/storage/belt/fannypack/black + name = "black fannypack" + icon_state = "fannypack_black" + item_state = "fannypack_black" + +/obj/item/weapon/storage/belt/fannypack/blue + name = "blue fannypack" + icon_state = "fannypack_blue" + item_state = "fannypack_blue" + +/obj/item/weapon/storage/belt/fannypack/cyan + name = "cyan fannypack" + icon_state = "fannypack_cyan" + item_state = "fannypack_cyan" + +/obj/item/weapon/storage/belt/fannypack/green + name = "green fannypack" + icon_state = "fannypack_green" + item_state = "fannypack_green" + +/obj/item/weapon/storage/belt/fannypack/orange + name = "orange fannypack" + icon_state = "fannypack_orange" + item_state = "fannypack_orange" + +/obj/item/weapon/storage/belt/fannypack/purple + name = "purple fannypack" + icon_state = "fannypack_purple" + item_state = "fannypack_purple" + +/obj/item/weapon/storage/belt/fannypack/red + name = "red fannypack" + icon_state = "fannypack_red" + item_state = "fannypack_red" + +/obj/item/weapon/storage/belt/fannypack/white + name = "white fannypack" + icon_state = "fannypack_white" + item_state = "fannypack_white" + +/obj/item/weapon/storage/belt/fannypack/yellow + name = "yellow fannypack" + icon_state = "fannypack_yellow" + item_state = "fannypack_yellow" + +/obj/item/weapon/storage/belt/ranger + name = "ranger belt" + desc = "The fancy utility-belt holding the tools, cuffs and gadgets of the Go Go ERT-Rangers. The belt buckle is not real phoron, but it is still surprisingly comfortable to wear." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_belt" + +/obj/item/weapon/storage/belt/dbandolier + name = "\improper Donk-Soft bandolier" + desc = "A Donk-Soft bandolier! Carry your spare darts anywhere! Ages 8 and up." + icon_state = "dbandolier" + storage_slots = 8 + can_hold = list( + /obj/item/ammo_casing/afoam_dart + ) diff --git a/code/game/objects/items/weapons/storage/belt_vr.dm b/code/game/objects/items/weapons/storage/belt_vr.dm index 8321cfcbbb3..a9d78c13fb3 100644 --- a/code/game/objects/items/weapons/storage/belt_vr.dm +++ b/code/game/objects/items/weapons/storage/belt_vr.dm @@ -109,6 +109,7 @@ /obj/item/weapon/anodevice, /obj/item/weapon/tool/wrench, /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/transforming/powerdrill, /obj/item/weapon/storage/excavation, /obj/item/weapon/anobattery, /obj/item/weapon/reagent_containers/hypospray/autoinjector, @@ -147,6 +148,7 @@ /obj/item/weapon/clipboard, /obj/item/weapon/anodevice, /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, /obj/item/device/multitool, /obj/item/weapon/storage/excavation, /obj/item/weapon/anobattery, diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index 6352f4ff9e4..5386f2d5cad 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -1,115 +1,115 @@ -GLOBAL_LIST_INIT(biblenames, list( - "Bible", "Koran", "Scrapbook", - "Pagan", "White Bible", "Holy Light", - "Athiest", "Tome", "The King in Yellow", - "Ithaqua", "Scientology", "the bible melts", - "Necronomicon", "Orthodox", "Torah")) -//If you get these two lists not matching in size, there will be runtimes and I will hurt you in ways you couldn't even begin to imagine -// if your bible has no custom itemstate, use one of the existing ones -GLOBAL_LIST_INIT(biblestates, list( - "bible", "koran", "scrapbook", - "shadows", "white", "holylight", - "athiest", "tome", "kingyellow", - "ithaqua", "scientology", "melted", - "necronomicon", "orthodoxy", "torah")) -GLOBAL_LIST_INIT(bibleitemstates, list( - "bible", "koran", "scrapbook", - "syringe_kit", "syringe_kit", "syringe_kit", - "syringe_kit", "syringe_kit", "kingyellow", - "ithaqua", "scientology", "melted", - "necronomicon", "bible", "clipboard")) - -/obj/item/weapon/storage/bible - name = "bible" - desc = "Apply to head repeatedly." - icon_state ="bible" - item_state = "bible" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' - ) - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - var/mob/affecting = null - var/deity_name = "Christ" - use_sound = 'sound/bureaucracy/bookopen.ogg' - drop_sound = 'sound/bureaucracy/bookclose.ogg' - -/obj/item/weapon/storage/bible/attack_self(mob/living/carbon/human/user) - - if(user?.mind?.assigned_role != "Chaplain") - return FALSE - - if (!user.mind.my_religion) - return FALSE - - if (!user.mind.my_religion.configured) - var/list/skins = list() - for(var/i in 1 to GLOB.biblestates.len) - var/image/bible_image = image(icon = 'icons/obj/storage.dmi', icon_state = GLOB.biblestates[i]) - skins += list("[GLOB.biblenames[i]]" = bible_image) - - var/choice = show_radial_menu(user, src, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 40, require_near = TRUE) - if(!choice) - return FALSE - var/bible_index = GLOB.biblenames.Find(choice) - if(!bible_index) - return FALSE - - user.mind.my_religion.bible_icon_state = GLOB.biblestates[bible_index] - user.mind.my_religion.bible_item_state = GLOB.bibleitemstates[bible_index] - user.mind.my_religion.configured = TRUE - - deity_name = user.mind.my_religion.deity - name = user.mind.my_religion.bible_name - icon_state = user.mind.my_religion.bible_icon_state - item_state = user.mind.my_religion.bible_item_state - to_chat(user, "You invoke [user.mind.my_religion.deity] and prepare a copy of [src].") - -/** - * Checks if we are allowed to interact with a radial menu - * - * Arguments: - * * user The mob interacting with the menu - */ -/obj/item/weapon/storage/bible/proc/check_menu(mob/living/carbon/human/user) - if(user.mind.my_religion.configured) - return FALSE - if(!istype(user)) - return FALSE - if(user.get_active_hand() != src) - return FALSE - if(user.incapacitated()) - return FALSE - if(user.mind.assigned_role != "Chaplain") - return FALSE - return TRUE - -/obj/item/weapon/storage/bible/booze - name = "bible" - desc = "To be applied to the head repeatedly." - icon_state ="bible" - -/obj/item/weapon/storage/bible/booze/New() - starts_with = list( - /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/weapon/spacecash/c100, - /obj/item/weapon/spacecash/c100, - /obj/item/weapon/spacecash/c100 - ) - -/obj/item/weapon/storage/bible/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) return - if(user.mind && (user.mind.assigned_role == "Chaplain")) - if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder - to_chat(user, "You bless [A].") - var/water2holy = A.reagents.get_reagent_amount("water") - A.reagents.del_reagent("water") - A.reagents.add_reagent("holywater",water2holy) - -/obj/item/weapon/storage/bible/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (src.use_sound) - playsound(src, src.use_sound, 50, 1, -5) - ..() +GLOBAL_LIST_INIT(biblenames, list( + "Bible", "Koran", "Scrapbook", + "Pagan", "White Bible", "Holy Light", + "Athiest", "Tome", "The King in Yellow", + "Ithaqua", "Scientology", "the bible melts", + "Necronomicon", "Orthodox", "Torah")) +//If you get these two lists not matching in size, there will be runtimes and I will hurt you in ways you couldn't even begin to imagine +// if your bible has no custom itemstate, use one of the existing ones +GLOBAL_LIST_INIT(biblestates, list( + "bible", "koran", "scrapbook", + "shadows", "white", "holylight", + "athiest", "tome", "kingyellow", + "ithaqua", "scientology", "melted", + "necronomicon", "orthodoxy", "torah")) +GLOBAL_LIST_INIT(bibleitemstates, list( + "bible", "koran", "scrapbook", + "syringe_kit", "syringe_kit", "syringe_kit", + "syringe_kit", "syringe_kit", "kingyellow", + "ithaqua", "scientology", "melted", + "necronomicon", "bible", "clipboard")) + +/obj/item/weapon/storage/bible + name = "bible" + desc = "Apply to head repeatedly." + icon_state ="bible" + item_state = "bible" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' + ) + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + var/mob/affecting = null + var/deity_name = "Christ" + use_sound = 'sound/bureaucracy/bookopen.ogg' + drop_sound = 'sound/bureaucracy/bookclose.ogg' + +/obj/item/weapon/storage/bible/attack_self(mob/living/carbon/human/user) + + if(user?.mind?.assigned_role != "Chaplain") + return FALSE + + if (!user.mind.my_religion) + return FALSE + + if (!user.mind.my_religion.configured) + var/list/skins = list() + for(var/i in 1 to GLOB.biblestates.len) + var/image/bible_image = image(icon = 'icons/obj/storage.dmi', icon_state = GLOB.biblestates[i]) + skins += list("[GLOB.biblenames[i]]" = bible_image) + + var/choice = show_radial_menu(user, src, skins, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 40, require_near = TRUE) + if(!choice) + return FALSE + var/bible_index = GLOB.biblenames.Find(choice) + if(!bible_index) + return FALSE + + user.mind.my_religion.bible_icon_state = GLOB.biblestates[bible_index] + user.mind.my_religion.bible_item_state = GLOB.bibleitemstates[bible_index] + user.mind.my_religion.configured = TRUE + + deity_name = user.mind.my_religion.deity + name = user.mind.my_religion.bible_name + icon_state = user.mind.my_religion.bible_icon_state + item_state = user.mind.my_religion.bible_item_state + to_chat(user, "You invoke [user.mind.my_religion.deity] and prepare a copy of [src].") + +/** + * Checks if we are allowed to interact with a radial menu + * + * Arguments: + * * user The mob interacting with the menu + */ +/obj/item/weapon/storage/bible/proc/check_menu(mob/living/carbon/human/user) + if(user.mind.my_religion.configured) + return FALSE + if(!istype(user)) + return FALSE + if(user.get_active_hand() != src) + return FALSE + if(user.incapacitated()) + return FALSE + if(user.mind.assigned_role != "Chaplain") + return FALSE + return TRUE + +/obj/item/weapon/storage/bible/booze + name = "bible" + desc = "To be applied to the head repeatedly." + icon_state ="bible" + +/obj/item/weapon/storage/bible/booze/New() + starts_with = list( + /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/weapon/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/weapon/spacecash/c100, + /obj/item/weapon/spacecash/c100, + /obj/item/weapon/spacecash/c100 + ) + +/obj/item/weapon/storage/bible/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) return + if(user.mind && (user.mind.assigned_role == "Chaplain")) + if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder + to_chat(user, "You bless [A].") + var/water2holy = A.reagents.get_reagent_amount("water") + A.reagents.del_reagent("water") + A.reagents.add_reagent("holywater",water2holy) + +/obj/item/weapon/storage/bible/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (src.use_sound) + playsound(src, src.use_sound, 50, 1, -5) + ..() diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index f930b12ddce..1f6c0cce4f2 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -1,507 +1,507 @@ -/* - * Everything derived from the common cardboard box. - * Basically everything except the original is a kit (starts full). - * - * Contains: - * Empty box, starter boxes (survival/engineer), - * Latex glove and sterile mask boxes, - * Syringe, beaker, dna injector boxes, - * Blanks, flashbangs, and EMP grenade boxes, - * Tracking and chemical implant boxes, - * Prescription glasses and drinking glass boxes, - * Condiment bottle and silly cup boxes, - * Donkpocket and monkeycube boxes, - * ID and security PDA cart boxes, - * Handcuff, mousetrap, and pillbottle boxes, - * Snap-pops and matchboxes, - * Replacement light boxes. - * - * For syndicate call-ins see uplink_kits.dm - */ - -/obj/item/weapon/storage/box - name = "box" - desc = "It's just an ordinary box." - icon = 'icons/obj/boxes.dmi' - icon_state = "box" - item_state = "syringe_kit" - center_of_mass = list("x" = 13,"y" = 10) - var/foldable = /obj/item/stack/material/cardboard // BubbleWrap - if set, can be folded (when empty) into a sheet of cardboard - var/trash = null // if set, can be crushed into a trash item when empty - max_w_class = ITEMSIZE_SMALL - max_storage_space = INVENTORY_BOX_SPACE - use_sound = 'sound/items/storage/box.ogg' - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - -// BubbleWrap - A box can be folded up to make card -/obj/item/weapon/storage/box/attack_self(mob/user as mob) - if(..()) return - - //try to fold it - if(ispath(foldable)) - if (contents.len) - return - var/found = 0 - // Close any open UI windows first - for(var/mob/M in range(1)) - if (M.s_active == src) - close(M) - if (M == user) - found = 1 - if (!found) // User is too far away - return - // Now make the cardboard - to_chat(user, "You fold [src] flat.") - playsound(src, 'sound/items/storage/boxfold.ogg', 30, 1) - new foldable(get_turf(src)) - qdel(src) - - //try to crush it - if(ispath(trash)) - if(contents.len && user.a_intent == I_HURT) // only crumple with things inside on harmintent. - user.visible_message(SPAN_DANGER("[user] crushes \the [src], spilling its contents everywhere!"), SPAN_DANGER("You crush \the [src], spilling its contents everywhere!")) - spill() - else - to_chat(user, SPAN_NOTICE("You crumple up \the [src].")) //make trash - playsound(src.loc, 'sound/items/drop/wrapper.ogg', 30, 1) - var/obj/item/trash = new src.trash() - qdel(src) - user.put_in_hands(trash) - - -/obj/item/weapon/storage/box/survival - name = "emergency supply box" - desc = "A survival box issued to crew members for use in emergency situations." - icon_state = "survival" - starts_with = list( - /obj/item/weapon/tool/prybar/red, - /obj/item/clothing/glasses/goggles, - /obj/item/clothing/mask/breath - ) - -/obj/item/weapon/storage/box/survival/synth - name = "synthetic supply box" - desc = "A survival box issued to synthetic crew members for use in emergency situations." - icon_state = "survival_synth" - starts_with = list( - /obj/item/weapon/tool/prybar/red, - /obj/item/clothing/glasses/goggles - ) - -/obj/item/weapon/storage/box/survival/comp - name = "emergency supply box" - desc = "A comprehensive survival box issued to crew members for use in emergency situations. Contains additional supplies." - icon_state = "survival_comp" - starts_with = list( - /obj/item/weapon/tool/prybar/red, - /obj/item/clothing/glasses/goggles, - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/stack/medical/bruise_pack, - /obj/item/device/flashlight/glowstick, - /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, - /obj/item/clothing/mask/breath - ) - -/obj/item/weapon/storage/box/gloves - name = "box of latex gloves" - desc = "Contains white gloves." - icon_state = "latex" - starts_with = list(/obj/item/clothing/gloves/sterile/latex = 7) - -/obj/item/weapon/storage/box/masks - name = "box of sterile masks" - desc = "This box contains masks of sterility." - icon_state = "sterile" - starts_with = list(/obj/item/clothing/mask/surgical = 7) - -/obj/item/weapon/storage/box/masks/white - name = "box of sterile masks" - desc = "This box contains masks of sterility." - icon_state = "sterile" - starts_with = list(/obj/item/clothing/mask/surgical/white = 7) - -/obj/item/weapon/storage/box/masks/dust - name = "box of dust masks" - desc = "This box contains dust masks. Breathe easy." - icon_state = "sterile" - starts_with = list(/obj/item/clothing/mask/surgical/dust = 7) - -/obj/item/weapon/storage/box/syringes - name = "box of syringes" - desc = "A box full of syringes." - icon_state = "syringe" - starts_with = list(/obj/item/weapon/reagent_containers/syringe = 7) - -/obj/item/weapon/storage/box/syringegun - name = "box of syringe gun cartridges" - desc = "A box full of compressed gas cartridges." - icon_state = "syringe2" - starts_with = list(/obj/item/weapon/syringe_cartridge = 7) - -/obj/item/weapon/storage/box/beakers - name = "box of beakers" - desc = "A box full of beakers." - icon_state = "beaker" - starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker = 7) - -/obj/item/weapon/storage/box/injectors - name = "box of DNA injectors" - desc = "This box contains injectors it seems." - icon_state = "dna" - starts_with = list( - /obj/item/weapon/dnainjector/h2m = 3, - /obj/item/weapon/dnainjector/m2h = 3 - ) - -/obj/item/weapon/storage/box/flashbangs - name = "box of flashbangs (WARNING)" - desc = "WARNING: These devices are extremely dangerous and can cause blindness or deafness in repeated use." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/flashbang = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/emps - name = "box of emp grenades" - desc = "A box containing 5 military grade EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." - icon_state = "emp" - starts_with = list(/obj/item/weapon/grenade/empgrenade = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/empslite - name = "box of low yield emp grenades" - desc = "A box containing 5 low yield EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." - icon_state = "emp" - starts_with = list(/obj/item/weapon/grenade/empgrenade/low_yield = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/smokes - name = "box of smoke bombs" - desc = "A box containing 7 smoke bombs." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/smokebomb = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/anti_photons - name = "box of anti-photon grenades" - desc = "A box containing 7 experimental photon disruption grenades." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/anti_photon = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/frags - name = "box of fragmentation grenades (WARNING)" - desc = "A box containing 7 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." - icon_state = "frag" - starts_with = list(/obj/item/weapon/grenade/explosive = 7) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/frags_half_box - name = "box of fragmentation grenades (WARNING)" - desc = "A box containing 4 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." - icon_state = "frag" - starts_with = list(/obj/item/weapon/grenade/explosive = 4) - drop_sound = 'sound/items/drop/ammobox.ogg' - pickup_sound = 'sound/items/pickup/ammobox.ogg' - -/obj/item/weapon/storage/box/metalfoam - name = "box of metal foam grenades." - desc = "A box containing 7 metal foam grenades." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/chem_grenade/metalfoam = 7) - -/obj/item/weapon/storage/box/teargas - name = "box of teargas grenades" - desc = "A box containing 7 teargas grenades." - icon_state = "flashbang" - starts_with = list(/obj/item/weapon/grenade/chem_grenade/teargas = 7) - -/obj/item/weapon/storage/box/flare - name = "box of flares" - desc = "A box containing 4 flares." - starts_with = list(/obj/item/device/flashlight/flare = 4) - -/obj/item/weapon/storage/box/trackimp - name = "boxed tracking implant kit" - desc = "Box full of scum-bag tracking utensils." - icon_state = "implant" - starts_with = list( - /obj/item/weapon/implantcase/tracking = 4, - /obj/item/weapon/implanter, - /obj/item/weapon/implantpad, - /obj/item/weapon/locator - ) - -/obj/item/weapon/storage/box/chemimp - name = "boxed chemical implant kit" - desc = "Box of stuff used to implant chemicals." - icon_state = "implant" - starts_with = list( - /obj/item/weapon/implantcase/chem = 5, - /obj/item/weapon/implanter, - /obj/item/weapon/implantpad - ) - -/obj/item/weapon/storage/box/camerabug - name = "mobile camera pod box" - desc = "A box containing some mobile camera pods." - icon_state = "pda" - starts_with = list( - /obj/item/device/camerabug = 6, - /obj/item/device/bug_monitor - ) - -/obj/item/weapon/storage/box/rxglasses - name = "box of prescription glasses" - desc = "This box contains nerd glasses." - icon_state = "glasses" - starts_with = list(/obj/item/clothing/glasses/regular = 7) - -/obj/item/weapon/storage/box/cdeathalarm_kit - name = "death alarm kit" - desc = "Box of stuff used to implant death alarms." - icon_state = "implant" - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - starts_with = list( - /obj/item/weapon/implantcase/death_alarm = 7, - /obj/item/weapon/implanter - ) - -/obj/item/weapon/storage/box/condimentbottles - name = "box of condiment bottles" - desc = "It has a large ketchup smear on it." - icon_state = "condiment" - starts_with = list(/obj/item/weapon/reagent_containers/food/condiment = 7) - -/obj/item/weapon/storage/box/cups - name = "box of paper cups" - desc = "It has pictures of paper cups on the front." - icon_state = "cups" - starts_with = list(/obj/item/weapon/reagent_containers/food/drinks/sillycup = 7) - -/obj/item/weapon/storage/box/buns - name = "box of bread buns" - desc = "Freshly baked at some point in the past few months." - icon_state = "bun_box" - max_storage_space = ITEMSIZE_COST_NORMAL * 5 - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/bun = 12) - -/obj/item/weapon/storage/box/monkeycubes - name = "monkey cube box" - desc = "Drymate brand monkey cubes. Just add water!" - icon = 'icons/obj/food.dmi' - icon_state = "monkeycubebox" - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped = 4) - -/obj/item/weapon/storage/box/monkeycubes/farwacubes - name = "farwa cube box" - desc = "Drymate brand farwa cubes, shipped from Meralar. Just add water!" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/farwacube = 4) - -/obj/item/weapon/storage/box/monkeycubes/stokcubes - name = "stok cube box" - desc = "Drymate brand stok cubes, shipped from Moghes. Just add water!" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/stokcube = 4) - -/obj/item/weapon/storage/box/monkeycubes/neaeracubes - name = "neaera cube box" - desc = "Drymate brand neaera cubes, shipped from Qerr'balak. Just add water!" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/neaeracube = 4) - -/obj/item/weapon/storage/box/ids - name = "box of spare IDs" - desc = "Has so many empty IDs." - icon_state = "id" - starts_with = list(/obj/item/weapon/card/id = 7) - -/obj/item/weapon/storage/box/seccarts - name = "box of spare R.O.B.U.S.T. Cartridges" - desc = "A box full of R.O.B.U.S.T. Cartridges, used by Security." - icon_state = "pda" - starts_with = list(/obj/item/weapon/cartridge/security = 7) - -/obj/item/weapon/storage/box/handcuffs - name = "box of spare handcuffs" - desc = "A box full of handcuffs." - icon_state = "handcuff" - starts_with = list(/obj/item/weapon/handcuffs = 7) - -/obj/item/weapon/storage/box/mousetraps - name = "box of Pest-B-Gon mousetraps" - desc = "WARNING: Keep out of reach of children." - icon_state = "mousetraps" - starts_with = list(/obj/item/device/assembly/mousetrap = 7) - -/obj/item/weapon/storage/box/pillbottles - name = "box of pill bottles" - desc = "It has pictures of pill bottles on its front." - icon_state = "pillbox" - starts_with = list(/obj/item/weapon/storage/pill_bottle = 7) - -/obj/item/weapon/storage/box/snappops - name = "snap pop box" - desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." - icon = 'icons/obj/toy.dmi' - icon_state = "spbox" - can_hold = list(/obj/item/toy/snappop) - starts_with = list(/obj/item/toy/snappop = 8) - -/obj/item/weapon/storage/box/matches - name = "matchbox" - desc = "A small box of 'Space-Proof' premium matches." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "matchbox" - w_class = ITEMSIZE_TINY - slot_flags = SLOT_BELT - can_hold = list(/obj/item/weapon/flame/match) - starts_with = list(/obj/item/weapon/flame/match = 10) - drop_sound = 'sound/items/drop/matchbox.ogg' - pickup_sound = 'sound/items/pickup/matchbox.ogg' - -/obj/item/weapon/storage/box/matches/attackby(var/obj/item/weapon/flame/match/W, var/mob/user) - if(istype(W) && !W.lit && !W.burnt) - if(prob(25)) - W.light(user) - user.visible_message("[user] manages to light the match on the matchbox.") - else - playsound(src, 'sound/items/cigs_lighters/matchstick_hit.ogg', 25, 0, -1) - W.update_icon() - return - -/obj/item/weapon/storage/box/autoinjectors - name = "box of injectors" - desc = "Contains autoinjectors." - icon_state = "auto" - starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector = 7) - -/obj/item/weapon/storage/box/lights - name = "box of replacement bulbs" - icon = 'icons/obj/boxes.dmi' - icon_state = "light" - desc = "This box is shaped on the inside so that only light tubes and bulbs fit." - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - storage_slots = 24 - can_hold = list(/obj/item/weapon/light/tube, /obj/item/weapon/light/bulb) - max_storage_space = ITEMSIZE_COST_SMALL * 24 //holds 24 items of w_class 2 - use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try - -/obj/item/weapon/storage/box/lights/bulbs - starts_with = list(/obj/item/weapon/light/bulb = 24) - -/obj/item/weapon/storage/box/lights/tubes - name = "box of replacement tubes" - icon_state = "lighttube" - starts_with = list(/obj/item/weapon/light/tube = 24) - -/obj/item/weapon/storage/box/lights/mixed - name = "box of replacement lights" - icon_state = "lightmixed" - starts_with = list( - /obj/item/weapon/light/tube = 16, - /obj/item/weapon/light/bulb = 8 - ) - -/obj/item/weapon/storage/box/freezer - name = "portable freezer" - desc = "This nifty shock-resistant device will keep your 'groceries' nice and non-spoiled." - icon = 'icons/obj/storage.dmi' - icon_state = "portafreezer" - item_state_slots = list(slot_r_hand_str = "medicalpack", slot_l_hand_str = "medicalpack") - foldable = null - max_w_class = ITEMSIZE_NORMAL - can_hold = list(/obj/item/organ) - max_storage_space = ITEMSIZE_COST_NORMAL * 5 // Formally 21. Odd numbers are bad. - use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try - -/obj/item/weapon/storage/box/freezer/red - icon_state = "portafreezer_red" - -/obj/item/weapon/storage/box/freezer/Entered(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 1 - for(var/obj/item/organ/organ in O) - organ.preserved = 1 - ..() - -/obj/item/weapon/storage/box/freezer/Exited(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 0 - for(var/obj/item/organ/organ in O) - organ.preserved = 0 - ..() - -/obj/item/weapon/storage/box/ambrosia - name = "ambrosia seeds box" - desc = "Contains the seeds you need to get a little high." - starts_with = list(/obj/item/seeds/ambrosiavulgarisseed = 7) - -/obj/item/weapon/storage/box/ambrosiadeus - name = "ambrosia deus seeds box" - desc = "Contains the seeds you need to get a proper healthy high." - starts_with = list(/obj/item/seeds/ambrosiadeusseed = 7) - -/obj/item/weapon/storage/box/capguntoy - name = "\improper AlliCo \"Zipper\" Cap Gun" - icon = 'icons/obj/gun_toy.dmi' - icon_state = "cap_gun_box" - desc = "This box is shaped on the inside so that only the \"Zipper\" Capgun and extra caps can fit." - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - storage_slots = 2 - max_w_class = ITEMSIZE_NORMAL - can_hold = list(/obj/item/weapon/gun/projectile/revolver/capgun, /obj/item/ammo_magazine/ammo_box/cap) - starts_with = list( - /obj/item/weapon/gun/projectile/revolver/capgun = 1, - /obj/item/ammo_magazine/ammo_box/cap = 1 - ) - -//Donk-pockets -/obj/item/weapon/storage/box/donkpockets - name = "box of donk-pockets" - desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." - icon_state = "donkpocketbox" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket = 7) - -/obj/item/weapon/storage/box/donkpockets/spicy - name = "box of spicy-flavoured donk-pockets" - icon_state = "donkpocketboxspicy" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/spicy = 7) - -/obj/item/weapon/storage/box/donkpockets/teriyaki - name = "box of teriyaki-flavoured donk-pockets" - icon_state = "donkpocketboxteriyaki" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/teriyaki = 7) - -/obj/item/weapon/storage/box/donkpockets/pizza - name = "box of pizza-flavoured donk-pockets" - icon_state = "donkpocketboxpizza" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/pizza = 7) - -/obj/item/weapon/storage/box/donkpockets/honk - name = "box of banana-flavoured donk-pockets" - icon_state = "donkpocketboxbanana" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/honk = 7) - -/obj/item/weapon/storage/box/donkpockets/gondola - name = "box of gondola-flavoured donk-pockets" - icon_state = "donkpocketboxgondola" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/gondola = 7) - -/obj/item/weapon/storage/box/donkpockets/berry - name = "box of berry-flavoured donk-pockets" - icon_state = "donkpocketboxberry" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/berry = 7) - -/obj/item/weapon/storage/box/sinpockets - name = "box of sin-pockets" - desc = "Instructions: Crush bottom of package to initiate chemical heating. Wait for 20 seconds before consumption. Product will cool if not eaten within seven minutes." - icon_state = "donk_kit" - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/sinpocket = 7) \ No newline at end of file +/* + * Everything derived from the common cardboard box. + * Basically everything except the original is a kit (starts full). + * + * Contains: + * Empty box, starter boxes (survival/engineer), + * Latex glove and sterile mask boxes, + * Syringe, beaker, dna injector boxes, + * Blanks, flashbangs, and EMP grenade boxes, + * Tracking and chemical implant boxes, + * Prescription glasses and drinking glass boxes, + * Condiment bottle and silly cup boxes, + * Donkpocket and monkeycube boxes, + * ID and security PDA cart boxes, + * Handcuff, mousetrap, and pillbottle boxes, + * Snap-pops and matchboxes, + * Replacement light boxes. + * + * For syndicate call-ins see uplink_kits.dm + */ + +/obj/item/weapon/storage/box + name = "box" + desc = "It's just an ordinary box." + icon = 'icons/obj/boxes.dmi' + icon_state = "box" + item_state = "syringe_kit" + center_of_mass = list("x" = 13,"y" = 10) + var/foldable = /obj/item/stack/material/cardboard // BubbleWrap - if set, can be folded (when empty) into a sheet of cardboard + var/trash = null // if set, can be crushed into a trash item when empty + max_w_class = ITEMSIZE_SMALL + max_storage_space = INVENTORY_BOX_SPACE + use_sound = 'sound/items/storage/box.ogg' + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + +// BubbleWrap - A box can be folded up to make card +/obj/item/weapon/storage/box/attack_self(mob/user as mob) + if(..()) return + + //try to fold it + if(ispath(foldable)) + if (contents.len) + return + var/found = 0 + // Close any open UI windows first + for(var/mob/M in range(1)) + if (M.s_active == src) + close(M) + if (M == user) + found = 1 + if (!found) // User is too far away + return + // Now make the cardboard + to_chat(user, "You fold [src] flat.") + playsound(src, 'sound/items/storage/boxfold.ogg', 30, 1) + new foldable(get_turf(src)) + qdel(src) + + //try to crush it + if(ispath(trash)) + if(contents.len && user.a_intent == I_HURT) // only crumple with things inside on harmintent. + user.visible_message(SPAN_DANGER("[user] crushes \the [src], spilling its contents everywhere!"), SPAN_DANGER("You crush \the [src], spilling its contents everywhere!")) + spill() + else + to_chat(user, SPAN_NOTICE("You crumple up \the [src].")) //make trash + playsound(src.loc, 'sound/items/drop/wrapper.ogg', 30, 1) + var/obj/item/trash = new src.trash() + qdel(src) + user.put_in_hands(trash) + + +/obj/item/weapon/storage/box/survival + name = "emergency supply box" + desc = "A survival box issued to crew members for use in emergency situations." + icon_state = "survival" + starts_with = list( + /obj/item/weapon/tool/prybar/red, + /obj/item/clothing/glasses/goggles, + /obj/item/clothing/mask/breath + ) + +/obj/item/weapon/storage/box/survival/synth + name = "synthetic supply box" + desc = "A survival box issued to synthetic crew members for use in emergency situations." + icon_state = "survival_synth" + starts_with = list( + /obj/item/weapon/tool/prybar/red, + /obj/item/clothing/glasses/goggles + ) + +/obj/item/weapon/storage/box/survival/comp + name = "emergency supply box" + desc = "A comprehensive survival box issued to crew members for use in emergency situations. Contains additional supplies." + icon_state = "survival_comp" + starts_with = list( + /obj/item/weapon/tool/prybar/red, + /obj/item/clothing/glasses/goggles, + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/stack/medical/bruise_pack, + /obj/item/device/flashlight/glowstick, + /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar, + /obj/item/clothing/mask/breath + ) + +/obj/item/weapon/storage/box/gloves + name = "box of latex gloves" + desc = "Contains white gloves." + icon_state = "latex" + starts_with = list(/obj/item/clothing/gloves/sterile/latex = 7) + +/obj/item/weapon/storage/box/masks + name = "box of sterile masks" + desc = "This box contains masks of sterility." + icon_state = "sterile" + starts_with = list(/obj/item/clothing/mask/surgical = 7) + +/obj/item/weapon/storage/box/masks/white + name = "box of sterile masks" + desc = "This box contains masks of sterility." + icon_state = "sterile" + starts_with = list(/obj/item/clothing/mask/surgical/white = 7) + +/obj/item/weapon/storage/box/masks/dust + name = "box of dust masks" + desc = "This box contains dust masks. Breathe easy." + icon_state = "sterile" + starts_with = list(/obj/item/clothing/mask/surgical/dust = 7) + +/obj/item/weapon/storage/box/syringes + name = "box of syringes" + desc = "A box full of syringes." + icon_state = "syringe" + starts_with = list(/obj/item/weapon/reagent_containers/syringe = 7) + +/obj/item/weapon/storage/box/syringegun + name = "box of syringe gun cartridges" + desc = "A box full of compressed gas cartridges." + icon_state = "syringe2" + starts_with = list(/obj/item/weapon/syringe_cartridge = 7) + +/obj/item/weapon/storage/box/beakers + name = "box of beakers" + desc = "A box full of beakers." + icon_state = "beaker" + starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker = 7) + +/obj/item/weapon/storage/box/injectors + name = "box of DNA injectors" + desc = "This box contains injectors it seems." + icon_state = "dna" + starts_with = list( + /obj/item/weapon/dnainjector/h2m = 3, + /obj/item/weapon/dnainjector/m2h = 3 + ) + +/obj/item/weapon/storage/box/flashbangs + name = "box of flashbangs (WARNING)" + desc = "WARNING: These devices are extremely dangerous and can cause blindness or deafness in repeated use." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/flashbang = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/emps + name = "box of emp grenades" + desc = "A box containing 5 military grade EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." + icon_state = "emp" + starts_with = list(/obj/item/weapon/grenade/empgrenade = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/empslite + name = "box of low yield emp grenades" + desc = "A box containing 5 low yield EMP grenades.
                    WARNING: Do not use near unshielded electronics or biomechanical augmentations, death or permanent paralysis may occur." + icon_state = "emp" + starts_with = list(/obj/item/weapon/grenade/empgrenade/low_yield = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/smokes + name = "box of smoke bombs" + desc = "A box containing 7 smoke bombs." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/smokebomb = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/anti_photons + name = "box of anti-photon grenades" + desc = "A box containing 7 experimental photon disruption grenades." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/anti_photon = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/frags + name = "box of fragmentation grenades (WARNING)" + desc = "A box containing 7 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." + icon_state = "frag" + starts_with = list(/obj/item/weapon/grenade/explosive = 7) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/frags_half_box + name = "box of fragmentation grenades (WARNING)" + desc = "A box containing 4 military grade fragmentation grenades.
                    WARNING: These devices are extremely dangerous and can cause limb loss or death in repeated use." + icon_state = "frag" + starts_with = list(/obj/item/weapon/grenade/explosive = 4) + drop_sound = 'sound/items/drop/ammobox.ogg' + pickup_sound = 'sound/items/pickup/ammobox.ogg' + +/obj/item/weapon/storage/box/metalfoam + name = "box of metal foam grenades." + desc = "A box containing 7 metal foam grenades." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/chem_grenade/metalfoam = 7) + +/obj/item/weapon/storage/box/teargas + name = "box of teargas grenades" + desc = "A box containing 7 teargas grenades." + icon_state = "flashbang" + starts_with = list(/obj/item/weapon/grenade/chem_grenade/teargas = 7) + +/obj/item/weapon/storage/box/flare + name = "box of flares" + desc = "A box containing 4 flares." + starts_with = list(/obj/item/device/flashlight/flare = 4) + +/obj/item/weapon/storage/box/trackimp + name = "boxed tracking implant kit" + desc = "Box full of scum-bag tracking utensils." + icon_state = "implant" + starts_with = list( + /obj/item/weapon/implantcase/tracking = 4, + /obj/item/weapon/implanter, + /obj/item/weapon/implantpad, + /obj/item/weapon/locator + ) + +/obj/item/weapon/storage/box/chemimp + name = "boxed chemical implant kit" + desc = "Box of stuff used to implant chemicals." + icon_state = "implant" + starts_with = list( + /obj/item/weapon/implantcase/chem = 5, + /obj/item/weapon/implanter, + /obj/item/weapon/implantpad + ) + +/obj/item/weapon/storage/box/camerabug + name = "mobile camera pod box" + desc = "A box containing some mobile camera pods." + icon_state = "pda" + starts_with = list( + /obj/item/device/camerabug = 6, + /obj/item/device/bug_monitor + ) + +/obj/item/weapon/storage/box/rxglasses + name = "box of prescription glasses" + desc = "This box contains nerd glasses." + icon_state = "glasses" + starts_with = list(/obj/item/clothing/glasses/regular = 7) + +/obj/item/weapon/storage/box/cdeathalarm_kit + name = "death alarm kit" + desc = "Box of stuff used to implant death alarms." + icon_state = "implant" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + starts_with = list( + /obj/item/weapon/implantcase/death_alarm = 7, + /obj/item/weapon/implanter + ) + +/obj/item/weapon/storage/box/condimentbottles + name = "box of condiment bottles" + desc = "It has a large ketchup smear on it." + icon_state = "condiment" + starts_with = list(/obj/item/weapon/reagent_containers/food/condiment = 7) + +/obj/item/weapon/storage/box/cups + name = "box of paper cups" + desc = "It has pictures of paper cups on the front." + icon_state = "cups" + starts_with = list(/obj/item/weapon/reagent_containers/food/drinks/sillycup = 7) + +/obj/item/weapon/storage/box/buns + name = "box of bread buns" + desc = "Freshly baked at some point in the past few months." + icon_state = "bun_box" + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/bun = 12) + +/obj/item/weapon/storage/box/monkeycubes + name = "monkey cube box" + desc = "Drymate brand monkey cubes. Just add water!" + icon = 'icons/obj/food.dmi' + icon_state = "monkeycubebox" + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped = 4) + +/obj/item/weapon/storage/box/monkeycubes/farwacubes + name = "farwa cube box" + desc = "Drymate brand farwa cubes, shipped from Meralar. Just add water!" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/farwacube = 4) + +/obj/item/weapon/storage/box/monkeycubes/stokcubes + name = "stok cube box" + desc = "Drymate brand stok cubes, shipped from Moghes. Just add water!" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/stokcube = 4) + +/obj/item/weapon/storage/box/monkeycubes/neaeracubes + name = "neaera cube box" + desc = "Drymate brand neaera cubes, shipped from Qerr'balak. Just add water!" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube/wrapped/neaeracube = 4) + +/obj/item/weapon/storage/box/ids + name = "box of spare IDs" + desc = "Has so many empty IDs." + icon_state = "id" + starts_with = list(/obj/item/weapon/card/id = 7) + +/obj/item/weapon/storage/box/seccarts + name = "box of spare R.O.B.U.S.T. Cartridges" + desc = "A box full of R.O.B.U.S.T. Cartridges, used by Security." + icon_state = "pda" + starts_with = list(/obj/item/weapon/cartridge/security = 7) + +/obj/item/weapon/storage/box/handcuffs + name = "box of spare handcuffs" + desc = "A box full of handcuffs." + icon_state = "handcuff" + starts_with = list(/obj/item/weapon/handcuffs = 7) + +/obj/item/weapon/storage/box/mousetraps + name = "box of Pest-B-Gon mousetraps" + desc = span_red("WARNING:") + " Keep out of reach of children." + icon_state = "mousetraps" + starts_with = list(/obj/item/device/assembly/mousetrap = 7) + +/obj/item/weapon/storage/box/pillbottles + name = "box of pill bottles" + desc = "It has pictures of pill bottles on its front." + icon_state = "pillbox" + starts_with = list(/obj/item/weapon/storage/pill_bottle = 7) + +/obj/item/weapon/storage/box/snappops + name = "snap pop box" + desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." + icon = 'icons/obj/toy.dmi' + icon_state = "spbox" + can_hold = list(/obj/item/toy/snappop) + starts_with = list(/obj/item/toy/snappop = 8) + +/obj/item/weapon/storage/box/matches + name = "matchbox" + desc = "A small box of 'Space-Proof' premium matches." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "matchbox" + w_class = ITEMSIZE_TINY + slot_flags = SLOT_BELT + can_hold = list(/obj/item/weapon/flame/match) + starts_with = list(/obj/item/weapon/flame/match = 10) + drop_sound = 'sound/items/drop/matchbox.ogg' + pickup_sound = 'sound/items/pickup/matchbox.ogg' + +/obj/item/weapon/storage/box/matches/attackby(var/obj/item/weapon/flame/match/W, var/mob/user) + if(istype(W) && !W.lit && !W.burnt) + if(prob(25)) + W.light(user) + user.visible_message("[user] manages to light the match on the matchbox.") + else + playsound(src, 'sound/items/cigs_lighters/matchstick_hit.ogg', 25, 0, -1) + W.update_icon() + return + +/obj/item/weapon/storage/box/autoinjectors + name = "box of injectors" + desc = "Contains autoinjectors." + icon_state = "auto" + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector = 7) + +/obj/item/weapon/storage/box/lights + name = "box of replacement bulbs" + icon = 'icons/obj/boxes.dmi' + icon_state = "light" + desc = "This box is shaped on the inside so that only light tubes and bulbs fit." + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + storage_slots = 24 + can_hold = list(/obj/item/weapon/light/tube, /obj/item/weapon/light/bulb) + max_storage_space = ITEMSIZE_COST_SMALL * 24 //holds 24 items of w_class 2 + use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try + +/obj/item/weapon/storage/box/lights/bulbs + starts_with = list(/obj/item/weapon/light/bulb = 24) + +/obj/item/weapon/storage/box/lights/tubes + name = "box of replacement tubes" + icon_state = "lighttube" + starts_with = list(/obj/item/weapon/light/tube = 24) + +/obj/item/weapon/storage/box/lights/mixed + name = "box of replacement lights" + icon_state = "lightmixed" + starts_with = list( + /obj/item/weapon/light/tube = 16, + /obj/item/weapon/light/bulb = 8 + ) + +/obj/item/weapon/storage/box/freezer + name = "portable freezer" + desc = "This nifty shock-resistant device will keep your 'groceries' nice and non-spoiled." + icon = 'icons/obj/storage.dmi' + icon_state = "portafreezer" + item_state_slots = list(slot_r_hand_str = "medicalpack", slot_l_hand_str = "medicalpack") + foldable = null + max_w_class = ITEMSIZE_NORMAL + can_hold = list(/obj/item/organ) + max_storage_space = ITEMSIZE_COST_NORMAL * 5 // Formally 21. Odd numbers are bad. + use_to_pickup = TRUE // for picking up broken bulbs, not that most people will try + +/obj/item/weapon/storage/box/freezer/red + icon_state = "portafreezer_red" + +/obj/item/weapon/storage/box/freezer/Entered(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 1 + for(var/obj/item/organ/organ in O) + organ.preserved = 1 + ..() + +/obj/item/weapon/storage/box/freezer/Exited(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 0 + for(var/obj/item/organ/organ in O) + organ.preserved = 0 + ..() + +/obj/item/weapon/storage/box/ambrosia + name = "ambrosia seeds box" + desc = "Contains the seeds you need to get a little high." + starts_with = list(/obj/item/seeds/ambrosiavulgarisseed = 7) + +/obj/item/weapon/storage/box/ambrosiadeus + name = "ambrosia deus seeds box" + desc = "Contains the seeds you need to get a proper healthy high." + starts_with = list(/obj/item/seeds/ambrosiadeusseed = 7) + +/obj/item/weapon/storage/box/capguntoy + name = "\improper AlliCo \"Zipper\" Cap Gun" + icon = 'icons/obj/gun_toy.dmi' + icon_state = "cap_gun_box" + desc = "This box is shaped on the inside so that only the \"Zipper\" Capgun and extra caps can fit." + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + storage_slots = 2 + max_w_class = ITEMSIZE_NORMAL + can_hold = list(/obj/item/weapon/gun/projectile/revolver/capgun, /obj/item/ammo_magazine/ammo_box/cap) + starts_with = list( + /obj/item/weapon/gun/projectile/revolver/capgun = 1, + /obj/item/ammo_magazine/ammo_box/cap = 1 + ) + +//Donk-pockets +/obj/item/weapon/storage/box/donkpockets + name = "box of donk-pockets" + desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." + icon_state = "donkpocketbox" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket = 7) + +/obj/item/weapon/storage/box/donkpockets/spicy + name = "box of spicy-flavoured donk-pockets" + icon_state = "donkpocketboxspicy" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/spicy = 7) + +/obj/item/weapon/storage/box/donkpockets/teriyaki + name = "box of teriyaki-flavoured donk-pockets" + icon_state = "donkpocketboxteriyaki" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/teriyaki = 7) + +/obj/item/weapon/storage/box/donkpockets/pizza + name = "box of pizza-flavoured donk-pockets" + icon_state = "donkpocketboxpizza" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/pizza = 7) + +/obj/item/weapon/storage/box/donkpockets/honk + name = "box of banana-flavoured donk-pockets" + icon_state = "donkpocketboxbanana" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/honk = 7) + +/obj/item/weapon/storage/box/donkpockets/gondola + name = "box of gondola-flavoured donk-pockets" + icon_state = "donkpocketboxgondola" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/gondola = 7) + +/obj/item/weapon/storage/box/donkpockets/berry + name = "box of berry-flavoured donk-pockets" + icon_state = "donkpocketboxberry" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/berry = 7) + +/obj/item/weapon/storage/box/sinpockets + name = "box of sin-pockets" + desc = "Instructions: Crush bottom of package to initiate chemical heating. Wait for 20 seconds before consumption. Product will cool if not eaten within seven minutes." + icon_state = "donk_kit" + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donkpocket/sinpocket = 7) diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm index a3cc27d9265..c8cdcffc1e3 100644 --- a/code/game/objects/items/weapons/storage/briefcase.dm +++ b/code/game/objects/items/weapons/storage/briefcase.dm @@ -1,32 +1,32 @@ -/obj/item/weapon/storage/briefcase - name = "briefcase" - desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." - icon_state = "briefcase" - force = 8.0 - throw_speed = 1 - throw_range = 4 - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 4 - use_sound = 'sound/items/storage/briefcase.ogg' - drop_sound = 'sound/items/drop/backpack.ogg' - pickup_sound = 'sound/items/pickup/backpack.ogg' - -/obj/item/weapon/storage/briefcase/clutch - name = "clutch purse" - desc = "A fashionable handheld bag typically used by women." - icon_state = "clutch" - item_state_slots = list(slot_r_hand_str = "smpurse", slot_l_hand_str = "smpurse") - force = 0 - w_class = ITEMSIZE_NORMAL - max_w_class = ITEMSIZE_SMALL - max_storage_space = ITEMSIZE_COST_SMALL * 4 - -/obj/item/weapon/storage/briefcase/bookbag - name = "bookbag" - desc = "A small bookbag for holding... things other than books?" - icon_state = "bookbag" - force = 4.0 - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL +/obj/item/weapon/storage/briefcase + name = "briefcase" + desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "briefcase" + force = 8.0 + throw_speed = 1 + throw_range = 4 + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 4 + use_sound = 'sound/items/storage/briefcase.ogg' + drop_sound = 'sound/items/drop/backpack.ogg' + pickup_sound = 'sound/items/pickup/backpack.ogg' + +/obj/item/weapon/storage/briefcase/clutch + name = "clutch purse" + desc = "A fashionable handheld bag typically used by women." + icon_state = "clutch" + item_state_slots = list(slot_r_hand_str = "smpurse", slot_l_hand_str = "smpurse") + force = 0 + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_SMALL + max_storage_space = ITEMSIZE_COST_SMALL * 4 + +/obj/item/weapon/storage/briefcase/bookbag + name = "bookbag" + desc = "A small bookbag for holding... things other than books?" + icon_state = "bookbag" + force = 4.0 + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL max_storage_space = ITEMSIZE_COST_NORMAL * 4 \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm index 639bd175a47..850bc5246ed 100644 --- a/code/game/objects/items/weapons/storage/fancy.dm +++ b/code/game/objects/items/weapons/storage/fancy.dm @@ -1,558 +1,558 @@ -/* - * The 'fancy' path is for objects like donut boxes that show how many items are in the storage item on the sprite itself - * .. Sorry for the shitty path name, I couldnt think of a better one. - * - * WARNING: var/icon_type is used for both examine text and sprite name. Please look at the procs below and adjust your sprite names accordingly - * - * Contains: - * Donut Box - * Egg Box - * Candle Box - * Crayon Box - * Cracker Pack - * Cigarette Pack - * Cigar Box - * Extra Tobacco Bits - * Vial Box - * Box of Chocolates - */ - -/obj/item/weapon/storage/fancy/ - icon = 'icons/obj/food.dmi' - icon_state = "donutbox6" - name = "donut box" - var/icon_type = "donut" - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - var/open = 0 - var/open_state - var/closed_state - -/obj/item/weapon/storage/fancy/update_icon(var/itemremoved = 0) - var/total_contents = contents.len - itemremoved - icon_state = "[icon_type]box[total_contents]" - return - -/obj/item/weapon/storage/fancy/examine(mob/user) - . = ..() - - if(Adjacent(user)) - if(!contents.len) - . += "There are no [icon_type]s left in the box." - else if(contents.len == 1) - . += "There is one [icon_type] left in the box." - else - . += "There are [contents.len] [icon_type]s in the box." - -/* - * Egg Box - */ -/obj/item/weapon/storage/fancy/egg_box - icon = 'icons/obj/food.dmi' - icon_state = "eggbox" - icon_type = "egg" - name = "egg box" - center_of_mass = list("x" = 16,"y" = 7) - storage_slots = 12 - can_hold = list( - /obj/item/weapon/reagent_containers/food/snacks/egg, - /obj/item/weapon/reagent_containers/food/snacks/boiledegg - ) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/egg = 12) - -/obj/item/weapon/storage/fancy/egg_box/New() - if(!open_state) - open_state = "[initial(icon_state)]0" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/fancy/egg_box/update_icon() - cut_overlays() - if(open) - icon_state = open_state - if(contents.len >= 1) - add_overlay("eggbox[contents.len]") - else - icon_state = closed_state - -/obj/item/weapon/storage/fancy/egg_box/open(mob/user as mob) - if(open) - return - if (isobserver(usr)) - return - open = TRUE - update_icon() - ..() - -/obj/item/weapon/storage/fancy/egg_box/close(mob/user as mob) - open = FALSE - update_icon() - ..() - -/* - * Candle Boxes - */ -/obj/item/weapon/storage/fancy/candle_box - name = "red candle pack" - desc = "A pack of red candles." - icon = 'icons/obj/candle.dmi' - icon_state = "candlebox5" - icon_type = "candle" - item_state = "candlebox5" - throwforce = 2 - slot_flags = SLOT_BELT - max_storage_space = ITEMSIZE_COST_TINY * 5 - can_hold = list(/obj/item/weapon/flame/candle) - starts_with = list(/obj/item/weapon/flame/candle = 5) - -/obj/item/weapon/storage/fancy/whitecandle_box - name = "white candle pack" - desc = "A pack of white candles." - icon = 'icons/obj/candle.dmi' - icon_state = "whitecandlebox5" - icon_type = "whitecandle" - item_state = "whitecandlebox5" - throwforce = 2 - slot_flags = SLOT_BELT - max_storage_space = ITEMSIZE_COST_TINY * 5 - can_hold = list(/obj/item/weapon/flame/candle) - starts_with = list(/obj/item/weapon/flame/candle/white = 5) - -/obj/item/weapon/storage/fancy/blackcandle_box - name = "black candle pack" - desc = "A pack of black candles." - icon = 'icons/obj/candle.dmi' - icon_state = "blackcandlebox5" - icon_type = "blackcandle" - item_state = "blackcandlebox5" - throwforce = 2 - slot_flags = SLOT_BELT - max_storage_space = ITEMSIZE_COST_TINY * 5 - can_hold = list(/obj/item/weapon/flame/candle) - starts_with = list(/obj/item/weapon/flame/candle/black = 5) - - -/* - * Crayon Box - */ -/obj/item/weapon/storage/fancy/crayons - name = "box of crayons" - desc = "A box of crayons for all your rune drawing needs." - icon = 'icons/obj/crayons.dmi' - icon_state = "crayonbox" - w_class = ITEMSIZE_SMALL - icon_type = "crayon" - can_hold = list( - /obj/item/weapon/pen/crayon - ) - starts_with = list( - /obj/item/weapon/pen/crayon/red, - /obj/item/weapon/pen/crayon/orange, - /obj/item/weapon/pen/crayon/yellow, - /obj/item/weapon/pen/crayon/green, - /obj/item/weapon/pen/crayon/blue, - /obj/item/weapon/pen/crayon/purple - ) - -/obj/item/weapon/storage/fancy/crayons/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/fancy/crayons/update_icon() - var/mutable_appearance/ma = new(src) - ma.cut_overlays() - for(var/obj/item/weapon/pen/crayon/crayon in contents) - add_overlay(image('icons/obj/crayons.dmi',crayon.colourName)) - appearance = ma - -/obj/item/weapon/storage/fancy/crayons/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/pen/crayon)) - switch(W:colourName) - if("mime") - to_chat(user, "This crayon is too sad to be contained in this box.") - return - if("rainbow") - to_chat(user, "This crayon is too powerful to be contained in this box.") - return - ..() - -/obj/item/weapon/storage/fancy/markers - name = "box of markers" - desc = "A very professional looking box of permanent markers." - icon = 'icons/obj/crayons.dmi' - icon_state = "markerbox" - w_class = ITEMSIZE_SMALL - icon_type = "marker" - can_hold = list( - /obj/item/weapon/pen/crayon/marker - ) - starts_with = list( - /obj/item/weapon/pen/crayon/marker/black, - /obj/item/weapon/pen/crayon/marker/red, - /obj/item/weapon/pen/crayon/marker/orange, - /obj/item/weapon/pen/crayon/marker/yellow, - /obj/item/weapon/pen/crayon/marker/green, - /obj/item/weapon/pen/crayon/marker/blue, - /obj/item/weapon/pen/crayon/marker/purple - ) - -/obj/item/weapon/storage/fancy/markers/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/fancy/markers/update_icon() - var/mutable_appearance/ma = new(src) - ma.cut_overlays() - for(var/obj/item/weapon/pen/crayon/marker/marker in contents) - ma.add_overlay(image('icons/obj/crayons.dmi',"m"+marker.colourName)) - appearance = ma - -/obj/item/weapon/storage/fancy/markers/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/weapon/pen/crayon/marker)) - switch(W:colourName) - if("mime") - to_chat(user, "This marker is too depressing to be contained in this box.") - return - if("rainbow") - to_chat(user, "This marker is too childish to be contained in this box.") - return - ..() - -/* - * Cracker Pack - */ -/obj/item/weapon/storage/fancy/crackers - name = "\improper Getmore Crackers" - icon = 'icons/obj/food.dmi' - icon_state = "crackerbox" - icon_type = "cracker" - max_storage_space = ITEMSIZE_COST_TINY * 6 - max_w_class = ITEMSIZE_TINY - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cracker) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cracker = 6) - -/* - * Cigarette Pack - */ -/obj/item/weapon/storage/fancy/cigarettes - name = "\improper pack of Trans-Stellar Duty-frees" - desc = "A ubiquitous brand of cigarettes, found in every major spacefaring corporation in the universe. As mild and flavorless as it gets." - description_fluff = "The Trans-Stellar Duty-Free Cigarette Company was created as an imprint of NanoTrasen. They are the most boring, tasteless, dry cigarettes on the market, but due to just how unremarkable (not to mention cheap to produce) they are, they sold in vending machines in almost every corner of the galaxy." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "cigpacket" - item_state_slots = list(slot_r_hand_str = "cigpacket", slot_l_hand_str = "cigpacket") - w_class = ITEMSIZE_TINY - throwforce = 2 - slot_flags = SLOT_BELT - storage_slots = 6 - can_hold = list(/obj/item/clothing/mask/smokable/cigarette, /obj/item/weapon/flame/lighter, /obj/item/trash/cigbutt) - icon_type = "cigarette" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette = 6) - var/brand = "\improper Trans-Stellar Duty-free" - -/obj/item/weapon/storage/fancy/cigarettes/Initialize() - . = ..() - flags |= NOREACT - create_reagents(15 * storage_slots)//so people can inject cigarettes without opening a packet, now with being able to inject the whole one - flags |= OPENCONTAINER - if(brand) - for(var/obj/item/clothing/mask/smokable/cigarette/C in src) - C.brand = brand - C.desc += " This one is \a [brand]." - -/obj/item/weapon/storage/fancy/cigarettes/New() - if(!open_state) - open_state = "[initial(icon_state)]_open" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/fancy/cigarettes/update_icon() - cut_overlays() - if(open) - icon_state = open_state - if(contents.len >= 1) - add_overlay("cig[contents.len]") - else - icon_state = closed_state - -/obj/item/weapon/storage/fancy/cigarettes/open(mob/user as mob) - if(open) - return - open = TRUE - if(contents.len == 0) - icon_state = "[initial(icon_state)]_empty" - else - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigarettes/close(mob/user as mob) - open = FALSE - if(contents.len == 0) - icon_state = "[initial(icon_state)]_empty" - else - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigarettes/remove_from_storage(obj/item/W as obj, atom/new_location) - // Don't try to transfer reagents to lighters - if(istype(W, /obj/item/clothing/mask/smokable/cigarette)) - var/obj/item/clothing/mask/smokable/cigarette/C = W - reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) - return ..() - -/obj/item/weapon/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M, /mob)) - return - - if(M == user && user.zone_sel.selecting == O_MOUTH) - // Find ourselves a cig. Note that we could be full of lighters. - var/obj/item/clothing/mask/smokable/cigarette/cig = locate() in src - - if(cig == null) - to_chat(user, "Looks like the packet is out of cigarettes.") - return - - // Instead of running equip_to_slot_if_possible() we check here first, - // to avoid dousing cig with reagents if we're not going to equip it - if(!cig.mob_can_equip(user, slot_wear_mask)) - return - - // We call remove_from_storage first to manage the reagent transfer and - // UI updates. - remove_from_storage(cig, null) - user.equip_to_slot(cig, slot_wear_mask) - - reagents.maximum_volume = 15 * contents.len - to_chat(user, "You take a cigarette out of the pack.") - update_icon() - else - ..() - -/obj/item/weapon/storage/fancy/cigarettes/dromedaryco - name = "\improper DromedaryCo packet" - desc = "A packet of six Earth-export DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\"" - description_fluff = "DromedaryCo is one of Sol's oldest cigarette brands, and takes pride in having sourced tobcacco from the same Indian plantations since 2044. Popular with those willing to pay extra for a little nostalgia." - icon_state = "Dpacket" - brand = "\improper Dromedary Co. cigarette" - -/obj/item/weapon/storage/fancy/cigarettes/killthroat - name = "\improper AcmeCo packet" - desc = "A packet of six AcmeCo cigarettes. For those who want to obtain a record for the most cancerous tumors on a budget." - description_fluff = "Available anywhere people breathe and want to breathe less, AcmeCo is the cheapest, most widespread cigarette brand in the galaxy. They taste like trash, but when you're keeping them inside your jumpsuit on a 16 hour shift, you're probably not too concerned with flavour." - icon_state = "Apacket" - brand = "\improper Acme Co. cigarette" - -/obj/item/weapon/storage/fancy/cigarettes/luckystars - name = "\improper pack of Lucky Stars" - desc = "A mellow blend made from synthetic, pod-grown tobacco. The commercial jingle is guaranteed to get stuck in your head." - description_fluff = "Lucky Stars are some of the most prolific advertisers in the business, with Gilthari Exports plastering the name and slogan on everything from workplace safety videos to racing bikes. 'Feel the gentle warmth of your Lucky Star'." - icon_state = "LSpacket" - brand = "\improper Lucky Star" - -/obj/item/weapon/storage/fancy/cigarettes/jerichos - name = "\improper pack of Jerichos" - desc = "Typically seen dangling from the lips of Fleet veterans and border world hustlers. Tastes like hickory smoke, feels like warm liquid death down your lungs." - description_fluff = "The Jericho brand has carefully cultivated its 'rugged' image ever since its completely accidental association with the SolGov-Hegemony war due to their sizable corporate presence in the region. Prior to the war, Jerichos were considered the realm of drunks and sad divorcees." - icon_state = "Jpacket" - brand = "\improper Jericho" - -/obj/item/weapon/storage/fancy/cigarettes/menthols - name = "\improper pack of Temperamento Menthols" - desc = "With a sharp and natural organic menthol flavor, these Temperamentos are a favorite of science vessel crews. Hardly anyone knows they make 'em in non-menthol!" - description_fluff = "Temperamento Menthols are a product of the Aether Atmospherics and Recycling company, and the 'smooth' menthol taste is rumoured to be the chemical by-product of some far more profitable industrial synthesis." - icon_state = "TMpacket" - brand = "\improper Temperamento Menthol" - -/obj/item/weapon/storage/fancy/cigarettes/carcinomas - name = "\improper pack of Carcinoma Angels" - desc = "This previously unknown brand was slated for the chopping block, until they were publicly endorsed by an old Earthling gonzo journalist. The rest is history. They sell a variety for cats, too." - description_fluff = "The bitter taste of a Carcinoma Angel is considered desirable by many equally bitter wash-ups who consider themselves to be 'hard-boiled'. The smell is practically inseparable from urban security offices, and old men with exonet radio shows." - brand = "\improper Carcinoma Angel" - -/obj/item/weapon/storage/fancy/cigarettes/professionals - name = "\improper pack of Professional 120s" - desc = "Let's face it - if you're smoking these, you're either trying to look upper-class or you're 80 years old. That's the only excuse. They are, however, very good quality." - description_fluff = "Grown and rolled in a meticulously maintained biosphere orbitting Love, P120 tobacco is marketed as 'probably the best in the galaxy'. The premium price point, and the fact that the vast majority of consumers couldn't really tell the difference between this and the next leading brand." - icon_state = "P100packet" - brand = "\improper Professional 120" - -/* - * Cigar Box - */ -/obj/item/weapon/storage/fancy/cigar - name = "cigar case" - desc = "A case for holding your cigars when you are not smoking them." - description_fluff = "The tasteful stained palm case tells you that these 'Palma Grande' premium \ - cigars are only sold on the luxury cruises and resorts of Oasis, though ten separate companies \ - produce them for that purpose galaxy-wide. The standard is however very high." - icon_state = "cigarcase" - icon = 'icons/obj/cigarettes.dmi' - w_class = ITEMSIZE_TINY - throwforce = 2 - slot_flags = SLOT_BELT - storage_slots = 5 - can_hold = list(/obj/item/clothing/mask/smokable/cigarette/cigar, /obj/item/trash/cigbutt/cigarbutt) - icon_type = "cigar" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar = 5) - -/obj/item/weapon/storage/fancy/cigar/Initialize() - . = ..() - flags |= NOREACT - create_reagents(15 * storage_slots) - -/obj/item/weapon/storage/fancy/cigar/remove_from_storage(obj/item/W as obj, atom/new_location) - var/obj/item/clothing/mask/smokable/cigarette/cigar/C = W - if(!istype(C)) return - reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) - return ..() - -/obj/item/weapon/storage/fancy/cigar/New() - if(!open_state) - open_state = "[initial(icon_state)]0" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/fancy/cigar/update_icon() - cut_overlays() - if(open) - icon_state = open_state - if(contents.len >= 1) - add_overlay("[initial(icon_state)][contents.len]") - else - icon_state = closed_state - -/obj/item/weapon/storage/fancy/cigar/open(mob/user as mob) - if(open) - return - open = TRUE - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigar/close(mob/user as mob) - open = FALSE - update_icon() - ..() - -/obj/item/weapon/storage/fancy/cigar/choiba - name = "/improper Choiba cigar case" - desc = "A fancy case for holding your cigars when you are not smoking them." - description_fluff = "The exquisite wooden case bears the markings of the \ - Choiba cigar company based out of Cuba. The perfectly humidized case keeps \ - the companies signature Cigars in premium condidtion even when traveling \ - long distances within a vacuume. The custom case itself can sell for quite \ - a lot in some places." - icon_state = "cohibacase" - icon = 'icons/obj/cigarettes.dmi' - icon_type = "cigar" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba = 5) - -/obj/item/weapon/storage/fancy/cigar/havana - name = "\improper Havana cigar case" - desc = "A fancy case for holding your cigars when you are not smoking them." - icon_state = "havanacase" - icon = 'icons/obj/cigarettes.dmi' - icon_type = "cigar" - starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/havana = 5) - -/* - * Tobacco Bits - */ -/obj/item/weapon/storage/rollingpapers - name = "rolling paper pack" - desc = "A small cardboard pack containing several folded rolling papers." - icon_state = "paperbox" - icon = 'icons/obj/cigarettes.dmi' - w_class = ITEMSIZE_TINY - throwforce = 2 - slot_flags = SLOT_BELT - storage_slots = 14 - can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper) - starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper = 14) - -/obj/item/weapon/storage/rollingpapers/blunt - name = "blunt wrap pack" - desc = "A small cardboard pack containing several folded blunt wraps." - icon_state = "bluntbox" - storage_slots = 7 - can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt) - starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt = 7) - -/* - * Vial Box - */ -/obj/item/weapon/storage/fancy/vials - icon = 'icons/obj/vialbox.dmi' - icon_state = "vialbox6" - icon_type = "vial" - name = "vial storage box" - desc = "A helpful rack to hold test tubes." - storage_slots = 6 - can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) - starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker/vial = 6) - -/obj/item/weapon/storage/lockbox/vials - name = "secure vial storage box" - desc = "A locked box for keeping things away from children." - icon = 'icons/obj/vialbox.dmi' - icon_state = "vialbox0" - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - max_w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) - max_storage_space = ITEMSIZE_COST_SMALL * 6 //The sum of the w_classes of all the items in this storage item. - storage_slots = 6 - req_access = list(access_virology) - -/obj/item/weapon/storage/lockbox/vials/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/lockbox/vials/update_icon(var/itemremoved = 0) - var/total_contents = contents.len - itemremoved - icon_state = "vialbox[total_contents]" - cut_overlays() - if (!broken) - add_overlay("led[locked]") - if(locked) - add_overlay("cover") - else - add_overlay("ledb") - -/obj/item/weapon/storage/lockbox/vials/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - update_icon() - -/* - * Box of Chocolates - */ -/obj/item/weapon/storage/fancy/heartbox - icon_state = "heartbox" - name = "box of chocolates" - icon_type = "chocolate" - - var/startswith = 6 - max_storage_space = ITEMSIZE_COST_SMALL * 6 - can_hold = list( - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle - ) - starts_with = list( - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, - /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle - ) - -/obj/item/weapon/storage/fancy/heartbox/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/fancy/heartbox/update_icon(var/itemremoved = 0) - if (contents.len == 0) - icon_state = "heartbox_empty" +/* + * The 'fancy' path is for objects like donut boxes that show how many items are in the storage item on the sprite itself + * .. Sorry for the shitty path name, I couldnt think of a better one. + * + * WARNING: var/icon_type is used for both examine text and sprite name. Please look at the procs below and adjust your sprite names accordingly + * + * Contains: + * Donut Box + * Egg Box + * Candle Box + * Crayon Box + * Cracker Pack + * Cigarette Pack + * Cigar Box + * Extra Tobacco Bits + * Vial Box + * Box of Chocolates + */ + +/obj/item/weapon/storage/fancy/ + icon = 'icons/obj/food.dmi' + icon_state = "donutbox6" + name = "donut box" + var/icon_type = "donut" + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + var/open = 0 + var/open_state + var/closed_state + +/obj/item/weapon/storage/fancy/update_icon(var/itemremoved = 0) + var/total_contents = contents.len - itemremoved + icon_state = "[icon_type]box[total_contents]" + return + +/obj/item/weapon/storage/fancy/examine(mob/user) + . = ..() + + if(Adjacent(user)) + if(!contents.len) + . += "There are no [icon_type]s left in the box." + else if(contents.len == 1) + . += "There is one [icon_type] left in the box." + else + . += "There are [contents.len] [icon_type]s in the box." + +/* + * Egg Box + */ +/obj/item/weapon/storage/fancy/egg_box + icon = 'icons/obj/food.dmi' + icon_state = "eggbox" + icon_type = "egg" + name = "egg box" + center_of_mass = list("x" = 16,"y" = 7) + storage_slots = 12 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/egg, + /obj/item/weapon/reagent_containers/food/snacks/boiledegg + ) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/egg = 12) + +/obj/item/weapon/storage/fancy/egg_box/New() + if(!open_state) + open_state = "[initial(icon_state)]0" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/fancy/egg_box/update_icon() + cut_overlays() + if(open) + icon_state = open_state + if(contents.len >= 1) + add_overlay("eggbox[contents.len]") + else + icon_state = closed_state + +/obj/item/weapon/storage/fancy/egg_box/open(mob/user as mob) + if(open) + return + if (isobserver(usr)) + return + open = TRUE + update_icon() + ..() + +/obj/item/weapon/storage/fancy/egg_box/close(mob/user as mob) + open = FALSE + update_icon() + ..() + +/* + * Candle Boxes + */ +/obj/item/weapon/storage/fancy/candle_box + name = "red candle pack" + desc = "A pack of red candles." + icon = 'icons/obj/candle.dmi' + icon_state = "candlebox5" + icon_type = "candle" + item_state = "candlebox5" + throwforce = 2 + slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_TINY * 5 + can_hold = list(/obj/item/weapon/flame/candle) + starts_with = list(/obj/item/weapon/flame/candle = 5) + +/obj/item/weapon/storage/fancy/whitecandle_box + name = "white candle pack" + desc = "A pack of white candles." + icon = 'icons/obj/candle.dmi' + icon_state = "whitecandlebox5" + icon_type = "whitecandle" + item_state = "whitecandlebox5" + throwforce = 2 + slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_TINY * 5 + can_hold = list(/obj/item/weapon/flame/candle) + starts_with = list(/obj/item/weapon/flame/candle/white = 5) + +/obj/item/weapon/storage/fancy/blackcandle_box + name = "black candle pack" + desc = "A pack of black candles." + icon = 'icons/obj/candle.dmi' + icon_state = "blackcandlebox5" + icon_type = "blackcandle" + item_state = "blackcandlebox5" + throwforce = 2 + slot_flags = SLOT_BELT + max_storage_space = ITEMSIZE_COST_TINY * 5 + can_hold = list(/obj/item/weapon/flame/candle) + starts_with = list(/obj/item/weapon/flame/candle/black = 5) + + +/* + * Crayon Box + */ +/obj/item/weapon/storage/fancy/crayons + name = "box of crayons" + desc = "A box of crayons for all your rune drawing needs." + icon = 'icons/obj/crayons.dmi' + icon_state = "crayonbox" + w_class = ITEMSIZE_SMALL + icon_type = "crayon" + can_hold = list( + /obj/item/weapon/pen/crayon + ) + starts_with = list( + /obj/item/weapon/pen/crayon/red, + /obj/item/weapon/pen/crayon/orange, + /obj/item/weapon/pen/crayon/yellow, + /obj/item/weapon/pen/crayon/green, + /obj/item/weapon/pen/crayon/blue, + /obj/item/weapon/pen/crayon/purple + ) + +/obj/item/weapon/storage/fancy/crayons/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/fancy/crayons/update_icon() + var/mutable_appearance/ma = new(src) + ma.cut_overlays() + for(var/obj/item/weapon/pen/crayon/crayon in contents) + add_overlay(image('icons/obj/crayons.dmi',crayon.colourName)) + appearance = ma + +/obj/item/weapon/storage/fancy/crayons/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/pen/crayon)) + switch(W:colourName) + if("mime") + to_chat(user, "This crayon is too sad to be contained in this box.") + return + if("rainbow") + to_chat(user, "This crayon is too powerful to be contained in this box.") + return + ..() + +/obj/item/weapon/storage/fancy/markers + name = "box of markers" + desc = "A very professional looking box of permanent markers." + icon = 'icons/obj/crayons.dmi' + icon_state = "markerbox" + w_class = ITEMSIZE_SMALL + icon_type = "marker" + can_hold = list( + /obj/item/weapon/pen/crayon/marker + ) + starts_with = list( + /obj/item/weapon/pen/crayon/marker/black, + /obj/item/weapon/pen/crayon/marker/red, + /obj/item/weapon/pen/crayon/marker/orange, + /obj/item/weapon/pen/crayon/marker/yellow, + /obj/item/weapon/pen/crayon/marker/green, + /obj/item/weapon/pen/crayon/marker/blue, + /obj/item/weapon/pen/crayon/marker/purple + ) + +/obj/item/weapon/storage/fancy/markers/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/fancy/markers/update_icon() + var/mutable_appearance/ma = new(src) + ma.cut_overlays() + for(var/obj/item/weapon/pen/crayon/marker/marker in contents) + ma.add_overlay(image('icons/obj/crayons.dmi',"m"+marker.colourName)) + appearance = ma + +/obj/item/weapon/storage/fancy/markers/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/pen/crayon/marker)) + switch(W:colourName) + if("mime") + to_chat(user, "This marker is too depressing to be contained in this box.") + return + if("rainbow") + to_chat(user, "This marker is too childish to be contained in this box.") + return + ..() + +/* + * Cracker Pack + */ +/obj/item/weapon/storage/fancy/crackers + name = "\improper Getmore Crackers" + icon = 'icons/obj/food.dmi' + icon_state = "crackerbox" + icon_type = "cracker" + max_storage_space = ITEMSIZE_COST_TINY * 6 + max_w_class = ITEMSIZE_TINY + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cracker) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/cracker = 6) + +/* + * Cigarette Pack + */ +/obj/item/weapon/storage/fancy/cigarettes + name = "\improper pack of Trans-Stellar Duty-frees" + desc = "A ubiquitous brand of cigarettes, found in every major spacefaring corporation in the universe. As mild and flavorless as it gets." + description_fluff = "The Trans-Stellar Duty-Free Cigarette Company was created as an imprint of NanoTrasen. They are the most boring, tasteless, dry cigarettes on the market, but due to just how unremarkable (not to mention cheap to produce) they are, they sold in vending machines in almost every corner of the galaxy." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "cigpacket" + item_state_slots = list(slot_r_hand_str = "cigpacket", slot_l_hand_str = "cigpacket") + w_class = ITEMSIZE_TINY + throwforce = 2 + slot_flags = SLOT_BELT + storage_slots = 6 + can_hold = list(/obj/item/clothing/mask/smokable/cigarette, /obj/item/weapon/flame/lighter, /obj/item/trash/cigbutt) + icon_type = "cigarette" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette = 6) + var/brand = "\improper Trans-Stellar Duty-free" + +/obj/item/weapon/storage/fancy/cigarettes/Initialize() + . = ..() + flags |= NOREACT + create_reagents(15 * storage_slots)//so people can inject cigarettes without opening a packet, now with being able to inject the whole one + flags |= OPENCONTAINER + if(brand) + for(var/obj/item/clothing/mask/smokable/cigarette/C in src) + C.brand = brand + C.desc += " This one is \a [brand]." + +/obj/item/weapon/storage/fancy/cigarettes/New() + if(!open_state) + open_state = "[initial(icon_state)]_open" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/fancy/cigarettes/update_icon() + cut_overlays() + if(open) + icon_state = open_state + if(contents.len >= 1) + add_overlay("cig[contents.len]") + else + icon_state = closed_state + +/obj/item/weapon/storage/fancy/cigarettes/open(mob/user as mob) + if(open) + return + open = TRUE + if(contents.len == 0) + icon_state = "[initial(icon_state)]_empty" + else + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigarettes/close(mob/user as mob) + open = FALSE + if(contents.len == 0) + icon_state = "[initial(icon_state)]_empty" + else + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigarettes/remove_from_storage(obj/item/W as obj, atom/new_location) + // Don't try to transfer reagents to lighters + if(istype(W, /obj/item/clothing/mask/smokable/cigarette)) + var/obj/item/clothing/mask/smokable/cigarette/C = W + reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) + return ..() + +/obj/item/weapon/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M, /mob)) + return + + if(M == user && user.zone_sel.selecting == O_MOUTH) + // Find ourselves a cig. Note that we could be full of lighters. + var/obj/item/clothing/mask/smokable/cigarette/cig = locate() in src + + if(cig == null) + to_chat(user, "Looks like the packet is out of cigarettes.") + return + + // Instead of running equip_to_slot_if_possible() we check here first, + // to avoid dousing cig with reagents if we're not going to equip it + if(!cig.mob_can_equip(user, slot_wear_mask)) + return + + // We call remove_from_storage first to manage the reagent transfer and + // UI updates. + remove_from_storage(cig, null) + user.equip_to_slot(cig, slot_wear_mask) + + reagents.maximum_volume = 15 * contents.len + to_chat(user, "You take a cigarette out of the pack.") + update_icon() + else + ..() + +/obj/item/weapon/storage/fancy/cigarettes/dromedaryco + name = "\improper DromedaryCo packet" + desc = "A packet of six Earth-export DromedaryCo cancer sticks. A label on the packaging reads, \"Wouldn't a slow death make a change?\"" + description_fluff = "DromedaryCo is one of Sol's oldest cigarette brands, and takes pride in having sourced tobcacco from the same Indian plantations since 2044. Popular with those willing to pay extra for a little nostalgia." + icon_state = "Dpacket" + brand = "\improper Dromedary Co. cigarette" + +/obj/item/weapon/storage/fancy/cigarettes/killthroat + name = "\improper AcmeCo packet" + desc = "A packet of six AcmeCo cigarettes. For those who want to obtain a record for the most cancerous tumors on a budget." + description_fluff = "Available anywhere people breathe and want to breathe less, AcmeCo is the cheapest, most widespread cigarette brand in the galaxy. They taste like trash, but when you're keeping them inside your jumpsuit on a 16 hour shift, you're probably not too concerned with flavour." + icon_state = "Apacket" + brand = "\improper Acme Co. cigarette" + +/obj/item/weapon/storage/fancy/cigarettes/luckystars + name = "\improper pack of Lucky Stars" + desc = "A mellow blend made from synthetic, pod-grown tobacco. The commercial jingle is guaranteed to get stuck in your head." + description_fluff = "Lucky Stars are some of the most prolific advertisers in the business, with Gilthari Exports plastering the name and slogan on everything from workplace safety videos to racing bikes. 'Feel the gentle warmth of your Lucky Star'." + icon_state = "LSpacket" + brand = "\improper Lucky Star" + +/obj/item/weapon/storage/fancy/cigarettes/jerichos + name = "\improper pack of Jerichos" + desc = "Typically seen dangling from the lips of Fleet veterans and border world hustlers. Tastes like hickory smoke, feels like warm liquid death down your lungs." + description_fluff = "The Jericho brand has carefully cultivated its 'rugged' image ever since its completely accidental association with the SolGov-Hegemony war due to their sizable corporate presence in the region. Prior to the war, Jerichos were considered the realm of drunks and sad divorcees." + icon_state = "Jpacket" + brand = "\improper Jericho" + +/obj/item/weapon/storage/fancy/cigarettes/menthols + name = "\improper pack of Temperamento Menthols" + desc = "With a sharp and natural organic menthol flavor, these Temperamentos are a favorite of science vessel crews. Hardly anyone knows they make 'em in non-menthol!" + description_fluff = "Temperamento Menthols are a product of the Aether Atmospherics and Recycling company, and the 'smooth' menthol taste is rumoured to be the chemical by-product of some far more profitable industrial synthesis." + icon_state = "TMpacket" + brand = "\improper Temperamento Menthol" + +/obj/item/weapon/storage/fancy/cigarettes/carcinomas + name = "\improper pack of Carcinoma Angels" + desc = "This previously unknown brand was slated for the chopping block, until they were publicly endorsed by an old Earthling gonzo journalist. The rest is history. They sell a variety for cats, too." + description_fluff = "The bitter taste of a Carcinoma Angel is considered desirable by many equally bitter wash-ups who consider themselves to be 'hard-boiled'. The smell is practically inseparable from urban security offices, and old men with exonet radio shows." + brand = "\improper Carcinoma Angel" + +/obj/item/weapon/storage/fancy/cigarettes/professionals + name = "\improper pack of Professional 120s" + desc = "Let's face it - if you're smoking these, you're either trying to look upper-class or you're 80 years old. That's the only excuse. They are, however, very good quality." + description_fluff = "Grown and rolled in a meticulously maintained biosphere orbitting Love, P120 tobacco is marketed as 'probably the best in the galaxy'. The premium price point, and the fact that the vast majority of consumers couldn't really tell the difference between this and the next leading brand." + icon_state = "P100packet" + brand = "\improper Professional 120" + +/* + * Cigar Box + */ +/obj/item/weapon/storage/fancy/cigar + name = "cigar case" + desc = "A case for holding your cigars when you are not smoking them." + description_fluff = "The tasteful stained palm case tells you that these 'Palma Grande' premium \ + cigars are only sold on the luxury cruises and resorts of Oasis, though ten separate companies \ + produce them for that purpose galaxy-wide. The standard is however very high." + icon_state = "cigarcase" + icon = 'icons/obj/cigarettes.dmi' + w_class = ITEMSIZE_TINY + throwforce = 2 + slot_flags = SLOT_BELT + storage_slots = 5 + can_hold = list(/obj/item/clothing/mask/smokable/cigarette/cigar, /obj/item/trash/cigbutt/cigarbutt) + icon_type = "cigar" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar = 5) + +/obj/item/weapon/storage/fancy/cigar/Initialize() + . = ..() + flags |= NOREACT + create_reagents(15 * storage_slots) + +/obj/item/weapon/storage/fancy/cigar/remove_from_storage(obj/item/W as obj, atom/new_location) + var/obj/item/clothing/mask/smokable/cigarette/cigar/C = W + if(!istype(C)) return + reagents.trans_to_obj(C, (reagents.total_volume/contents.len)) + return ..() + +/obj/item/weapon/storage/fancy/cigar/New() + if(!open_state) + open_state = "[initial(icon_state)]0" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/fancy/cigar/update_icon() + cut_overlays() + if(open) + icon_state = open_state + if(contents.len >= 1) + add_overlay("[initial(icon_state)][contents.len]") + else + icon_state = closed_state + +/obj/item/weapon/storage/fancy/cigar/open(mob/user as mob) + if(open) + return + open = TRUE + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigar/close(mob/user as mob) + open = FALSE + update_icon() + ..() + +/obj/item/weapon/storage/fancy/cigar/choiba + name = "/improper Choiba cigar case" + desc = "A fancy case for holding your cigars when you are not smoking them." + description_fluff = "The exquisite wooden case bears the markings of the \ + Choiba cigar company based out of Cuba. The perfectly humidized case keeps \ + the companies signature Cigars in premium condidtion even when traveling \ + long distances within a vacuume. The custom case itself can sell for quite \ + a lot in some places." + icon_state = "cohibacase" + icon = 'icons/obj/cigarettes.dmi' + icon_type = "cigar" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba = 5) + +/obj/item/weapon/storage/fancy/cigar/havana + name = "\improper Havana cigar case" + desc = "A fancy case for holding your cigars when you are not smoking them." + icon_state = "havanacase" + icon = 'icons/obj/cigarettes.dmi' + icon_type = "cigar" + starts_with = list(/obj/item/clothing/mask/smokable/cigarette/cigar/havana = 5) + +/* + * Tobacco Bits + */ +/obj/item/weapon/storage/rollingpapers + name = "rolling paper pack" + desc = "A small cardboard pack containing several folded rolling papers." + icon_state = "paperbox" + icon = 'icons/obj/cigarettes.dmi' + w_class = ITEMSIZE_TINY + throwforce = 2 + slot_flags = SLOT_BELT + storage_slots = 14 + can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper) + starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper = 14) + +/obj/item/weapon/storage/rollingpapers/blunt + name = "blunt wrap pack" + desc = "A small cardboard pack containing several folded blunt wraps." + icon_state = "bluntbox" + storage_slots = 7 + can_hold = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt) + starts_with = list(/obj/item/weapon/reagent_containers/rollingpaper/blunt = 7) + +/* + * Vial Box + */ +/obj/item/weapon/storage/fancy/vials + icon = 'icons/obj/vialbox.dmi' + icon_state = "vialbox6" + icon_type = "vial" + name = "vial storage box" + desc = "A helpful rack to hold test tubes." + storage_slots = 6 + can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) + starts_with = list(/obj/item/weapon/reagent_containers/glass/beaker/vial = 6) + +/obj/item/weapon/storage/lockbox/vials + name = "secure vial storage box" + desc = "A locked box for keeping things away from children." + icon = 'icons/obj/vialbox.dmi' + icon_state = "vialbox0" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + max_w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/glass/beaker/vial) + max_storage_space = ITEMSIZE_COST_SMALL * 6 //The sum of the w_classes of all the items in this storage item. + storage_slots = 6 + req_access = list(access_virology) + +/obj/item/weapon/storage/lockbox/vials/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/lockbox/vials/update_icon(var/itemremoved = 0) + var/total_contents = contents.len - itemremoved + icon_state = "vialbox[total_contents]" + cut_overlays() + if (!broken) + add_overlay("led[locked]") + if(locked) + add_overlay("cover") + else + add_overlay("ledb") + +/obj/item/weapon/storage/lockbox/vials/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + update_icon() + +/* + * Box of Chocolates + */ +/obj/item/weapon/storage/fancy/heartbox + icon_state = "heartbox" + name = "box of chocolates" + icon_type = "chocolate" + + var/startswith = 6 + max_storage_space = ITEMSIZE_COST_SMALL * 6 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle + ) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/white, + /obj/item/weapon/reagent_containers/food/snacks/chocolatepiece/truffle + ) + +/obj/item/weapon/storage/fancy/heartbox/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/fancy/heartbox/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "heartbox_empty" diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 2d19634aaf5..ff6cfafe7c7 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -1,305 +1,305 @@ -/* First aid storage - * Contains: - * First Aid Kits - * Pill Bottles - */ - -/* - * First Aid Kits - */ -/obj/item/weapon/storage/firstaid - name = "first aid kit" - desc = "It's an emergency medical kit for those serious boo-boos." - icon = 'icons/obj/storage.dmi' - icon_state = "firstaid" - throw_speed = 2 - throw_range = 8 - max_storage_space = ITEMSIZE_COST_SMALL * 7 // 14 - var/list/icon_variety - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - -/obj/item/weapon/storage/firstaid/Initialize() - . = ..() - if(icon_variety) - icon_state = pick(icon_variety) - icon_variety = null - -/obj/item/weapon/storage/firstaid/fire - name = "fire first aid kit" - desc = "It's an emergency medical kit for when the toxins lab spontaneously burns down." - icon_state = "ointment" - item_state_slots = list(slot_r_hand_str = "firstaid-ointment", slot_l_hand_str = "firstaid-ointment") - //icon_variety = list("ointment","firefirstaid") //VOREStation Removal - starts_with = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/stack/medical/ointment, - /obj/item/stack/medical/ointment, - /obj/item/weapon/reagent_containers/pill/kelotane, - /obj/item/weapon/reagent_containers/pill/kelotane, - /obj/item/weapon/reagent_containers/pill/kelotane - ) - -/obj/item/weapon/storage/firstaid/regular - icon_state = "firstaid" - starts_with = list( - /obj/item/stack/medical/bruise_pack, - /obj/item/stack/medical/bruise_pack, - /obj/item/stack/medical/bruise_pack, - /obj/item/stack/medical/ointment, - /obj/item/stack/medical/ointment, - /obj/item/device/healthanalyzer, - /obj/item/weapon/reagent_containers/hypospray/autoinjector - ) - -/obj/item/weapon/storage/firstaid/toxin - name = "poison first aid kit" //IRL the term used would be poison first aid kit. - desc = "Used to treat when one has a high amount of toxins in their body." - icon_state = "antitoxin" - item_state_slots = list(slot_r_hand_str = "firstaid-toxin", slot_l_hand_str = "firstaid-toxin") - //icon_variety = list("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3") //VOREStation Removal - starts_with = list( - /obj/item/weapon/reagent_containers/syringe/antitoxin, - /obj/item/weapon/reagent_containers/syringe/antitoxin, - /obj/item/weapon/reagent_containers/syringe/antitoxin, - /obj/item/weapon/reagent_containers/pill/antitox, - /obj/item/weapon/reagent_containers/pill/antitox, - /obj/item/weapon/reagent_containers/pill/antitox, - /obj/item/device/healthanalyzer - ) - -/obj/item/weapon/storage/firstaid/o2 - name = "oxygen deprivation first aid kit" - desc = "A box full of oxygen goodies." - icon_state = "o2" - item_state_slots = list(slot_r_hand_str = "firstaid-o2", slot_l_hand_str = "firstaid-o2") - starts_with = list( - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/pill/dexalin, - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/weapon/reagent_containers/syringe/inaprovaline, - /obj/item/device/healthanalyzer - ) - -/obj/item/weapon/storage/firstaid/adv - name = "advanced first aid kit" - desc = "Contains advanced medical treatments, for serious boo-boos." - icon_state = "advfirstaid" - item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") - starts_with = list( - /obj/item/weapon/reagent_containers/hypospray/autoinjector, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/medical/advanced/ointment, - /obj/item/stack/medical/advanced/ointment, - /obj/item/stack/medical/splint - ) - -/obj/item/weapon/storage/firstaid/combat - name = "combat medical kit" - desc = "Contains advanced medical treatments." - icon_state = "bezerk" - item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") - starts_with = list( - /obj/item/weapon/storage/pill_bottle/bicaridine, - /obj/item/weapon/storage/pill_bottle/dermaline, - /obj/item/weapon/storage/pill_bottle/dexalin_plus, - /obj/item/weapon/storage/pill_bottle/dylovene, - /obj/item/weapon/storage/pill_bottle/tramadol, - /obj/item/weapon/storage/pill_bottle/spaceacillin, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, - /obj/item/stack/medical/splint, - /obj/item/device/healthanalyzer/advanced - ) - -/obj/item/weapon/storage/firstaid/surgery - name = "surgery kit" - desc = "Contains tools for surgery. Has precise foam fitting for safe transport and automatically sterilizes the content between uses." - icon = 'icons/obj/storage.dmi' // VOREStation edit - icon_state = "surgerykit" - item_state = "firstaid-surgery" - max_w_class = ITEMSIZE_NORMAL - - can_hold = list( - /obj/item/weapon/surgical/bonesetter, - /obj/item/weapon/surgical/cautery, - /obj/item/weapon/surgical/circular_saw, - /obj/item/weapon/surgical/hemostat, - /obj/item/weapon/surgical/retractor, - /obj/item/weapon/surgical/scalpel, - /obj/item/weapon/surgical/surgicaldrill, - /obj/item/weapon/surgical/bonegel, - /obj/item/weapon/surgical/FixOVein, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/stack/nanopaste, - /obj/item/device/healthanalyzer/advanced, - /obj/item/weapon/autopsy_scanner - ) - - starts_with = list( - /obj/item/weapon/surgical/bonesetter, - /obj/item/weapon/surgical/cautery, - /obj/item/weapon/surgical/circular_saw, - /obj/item/weapon/surgical/hemostat, - /obj/item/weapon/surgical/retractor, - /obj/item/weapon/surgical/scalpel, - /obj/item/weapon/surgical/surgicaldrill, - /obj/item/weapon/surgical/bonegel, - /obj/item/weapon/surgical/FixOVein, - /obj/item/stack/medical/advanced/bruise_pack, - /obj/item/device/healthanalyzer/advanced, - /obj/item/weapon/autopsy_scanner - ) - -/obj/item/weapon/storage/firstaid/clotting - name = "clotting kit" - desc = "Contains chemicals to stop bleeding." - max_storage_space = ITEMSIZE_COST_SMALL * 7 - starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting = 8) - -/obj/item/weapon/storage/firstaid/bonemed - name = "bone repair kit" - desc = "Contains chemicals to mend broken bones." - max_storage_space = ITEMSIZE_COST_SMALL * 7 - starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed = 8) - -/* - * Pill Bottles - */ -/obj/item/weapon/storage/pill_bottle - name = "pill bottle" - desc = "It's an airtight container for storing medication." - icon_state = "pill_canister" - icon = 'icons/obj/chemical.dmi' - drop_sound = 'sound/items/drop/pillbottle.ogg' - pickup_sound = 'sound/items/pickup/pillbottle.ogg' - item_state_slots = list(slot_r_hand_str = "contsolid", slot_l_hand_str = "contsolid") - w_class = ITEMSIZE_SMALL - can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/dice,/obj/item/weapon/paper) - allow_quick_gather = 1 - allow_quick_empty = 1 - use_to_pickup = TRUE - use_sound = 'sound/items/storage/pillbottle.ogg' - max_storage_space = ITEMSIZE_COST_TINY * 14 - max_w_class = ITEMSIZE_TINY - var/wrapper_color - var/label - - var/label_text = "" - var/base_name = " " - var/base_desc = " " - -/obj/item/weapon/storage/pill_bottle/Initialize() - . = ..() - base_name = name - base_desc = desc - update_icon() - -/obj/item/weapon/storage/pill_bottle/update_icon() - cut_overlays() - if(wrapper_color) - var/image/I = image(icon, "pillbottle_wrap") - I.color = wrapper_color - add_overlay(I) - -/obj/item/weapon/storage/pill_bottle/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) - var/tmp_label = sanitizeSafe(tgui_input_text(user, "Enter a label for [name]", "Label", label_text, MAX_NAME_LEN), MAX_NAME_LEN) - if(length(tmp_label) > 50) - to_chat(user, "The label can be at most 50 characters long.") - else if(length(tmp_label) > 10) - to_chat(user, "You set the label.") - label_text = tmp_label - update_name_label() - else - to_chat(user, "You set the label to \"[tmp_label]\".") - label_text = tmp_label - update_name_label() - else - ..() - -/obj/item/weapon/storage/pill_bottle/proc/update_name_label() - if(!label_text) - name = base_name - desc = base_desc - return - else if(length(label_text) > 10) - var/short_label_text = copytext(label_text, 1, 11) - name = "[base_name] ([short_label_text]...)" - else - name = "[base_name] ([label_text])" - desc = "[base_desc] It is labeled \"[label_text]\"." - -/obj/item/weapon/storage/pill_bottle/antitox - name = "pill bottle (Dylovene)" - desc = "Contains pills used to counter toxins." - starts_with = list(/obj/item/weapon/reagent_containers/pill/antitox = 7) - wrapper_color = COLOR_GREEN - -/obj/item/weapon/storage/pill_bottle/bicaridine - name = "pill bottle (Bicaridine)" - desc = "Contains pills used to stabilize the severely injured." - starts_with = list(/obj/item/weapon/reagent_containers/pill/bicaridine = 7) - wrapper_color = COLOR_MAROON - -/obj/item/weapon/storage/pill_bottle/dexalin_plus - name = "pill bottle (Dexalin Plus)" - desc = "Contains pills used to treat extreme cases of oxygen deprivation." - starts_with = list(/obj/item/weapon/reagent_containers/pill/dexalin_plus = 7) - wrapper_color = "#3366cc" - -/obj/item/weapon/storage/pill_bottle/dermaline - name = "pill bottle (Dermaline)" - desc = "Contains pills used to treat burn wounds." - starts_with = list(/obj/item/weapon/reagent_containers/pill/dermaline = 7) - wrapper_color = "#e8d131" - -/obj/item/weapon/storage/pill_bottle/dylovene - name = "pill bottle (Dylovene)" - desc = "Contains pills used to treat toxic substances in the blood." - starts_with = list(/obj/item/weapon/reagent_containers/pill/dylovene = 7) - wrapper_color = COLOR_GREEN - -/obj/item/weapon/storage/pill_bottle/inaprovaline - name = "pill bottle (Inaprovaline)" - desc = "Contains pills used to stabilize patients." - starts_with = list(/obj/item/weapon/reagent_containers/pill/inaprovaline = 7) - wrapper_color = COLOR_PALE_BLUE_GRAY - -/obj/item/weapon/storage/pill_bottle/kelotane - name = "pill bottle (Kelotane)" - desc = "Contains pills used to treat burns." - starts_with = list(/obj/item/weapon/reagent_containers/pill/kelotane = 7) - wrapper_color = "#ec8b2f" - -/obj/item/weapon/storage/pill_bottle/spaceacillin - name = "pill bottle (Spaceacillin)" - desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space." - starts_with = list(/obj/item/weapon/reagent_containers/pill/spaceacillin = 7) - wrapper_color = COLOR_PALE_GREEN_GRAY - -/obj/item/weapon/storage/pill_bottle/tramadol - name = "pill bottle (Tramadol)" - desc = "Contains pills used to relieve pain." - starts_with = list(/obj/item/weapon/reagent_containers/pill/tramadol = 7) - wrapper_color = COLOR_PURPLE_GRAY - -/obj/item/weapon/storage/pill_bottle/citalopram - name = "pill bottle (Citalopram)" - desc = "Contains pills used to stabilize a patient's mood." - starts_with = list(/obj/item/weapon/reagent_containers/pill/citalopram = 7) - wrapper_color = COLOR_GRAY - -/obj/item/weapon/storage/pill_bottle/carbon - name = "pill bottle (Carbon)" - desc = "Contains pills used to neutralise chemicals in the stomach." - starts_with = list(/obj/item/weapon/reagent_containers/pill/carbon = 7) - -/obj/item/weapon/storage/pill_bottle/iron - name = "pill bottle (Iron)" - desc = "Contains pills used to aid in blood regeneration." - starts_with = list(/obj/item/weapon/reagent_containers/pill/iron = 7) +/* First aid storage + * Contains: + * First Aid Kits + * Pill Bottles + */ + +/* + * First Aid Kits + */ +/obj/item/weapon/storage/firstaid + name = "first aid kit" + desc = "It's an emergency medical kit for those serious boo-boos." + icon = 'icons/obj/storage.dmi' + icon_state = "firstaid" + throw_speed = 2 + throw_range = 8 + max_storage_space = ITEMSIZE_COST_SMALL * 7 // 14 + var/list/icon_variety + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + +/obj/item/weapon/storage/firstaid/Initialize() + . = ..() + if(icon_variety) + icon_state = pick(icon_variety) + icon_variety = null + +/obj/item/weapon/storage/firstaid/fire + name = "fire first aid kit" + desc = "It's an emergency medical kit for when the toxins lab spontaneously burns down." + icon_state = "ointment" + item_state_slots = list(slot_r_hand_str = "firstaid-ointment", slot_l_hand_str = "firstaid-ointment") + //icon_variety = list("ointment","firefirstaid") //VOREStation Removal + starts_with = list( + /obj/item/device/healthanalyzer, + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/stack/medical/ointment, + /obj/item/stack/medical/ointment, + /obj/item/weapon/reagent_containers/pill/kelotane, + /obj/item/weapon/reagent_containers/pill/kelotane, + /obj/item/weapon/reagent_containers/pill/kelotane + ) + +/obj/item/weapon/storage/firstaid/regular + icon_state = "firstaid" + starts_with = list( + /obj/item/stack/medical/bruise_pack, + /obj/item/stack/medical/bruise_pack, + /obj/item/stack/medical/bruise_pack, + /obj/item/stack/medical/ointment, + /obj/item/stack/medical/ointment, + /obj/item/device/healthanalyzer, + /obj/item/weapon/reagent_containers/hypospray/autoinjector + ) + +/obj/item/weapon/storage/firstaid/toxin + name = "poison first aid kit" //IRL the term used would be poison first aid kit. + desc = "Used to treat when one has a high amount of toxins in their body." + icon_state = "antitoxin" + item_state_slots = list(slot_r_hand_str = "firstaid-toxin", slot_l_hand_str = "firstaid-toxin") + //icon_variety = list("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3") //VOREStation Removal + starts_with = list( + /obj/item/weapon/reagent_containers/syringe/antitoxin, + /obj/item/weapon/reagent_containers/syringe/antitoxin, + /obj/item/weapon/reagent_containers/syringe/antitoxin, + /obj/item/weapon/reagent_containers/pill/antitox, + /obj/item/weapon/reagent_containers/pill/antitox, + /obj/item/weapon/reagent_containers/pill/antitox, + /obj/item/device/healthanalyzer + ) + +/obj/item/weapon/storage/firstaid/o2 + name = "oxygen deprivation first aid kit" + desc = "A box full of oxygen goodies." + icon_state = "o2" + item_state_slots = list(slot_r_hand_str = "firstaid-o2", slot_l_hand_str = "firstaid-o2") + starts_with = list( + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/pill/dexalin, + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/weapon/reagent_containers/syringe/inaprovaline, + /obj/item/device/healthanalyzer + ) + +/obj/item/weapon/storage/firstaid/adv + name = "advanced first aid kit" + desc = "Contains advanced medical treatments, for serious boo-boos." + icon_state = "advfirstaid" + item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") + starts_with = list( + /obj/item/weapon/reagent_containers/hypospray/autoinjector, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/medical/advanced/ointment, + /obj/item/stack/medical/advanced/ointment, + /obj/item/stack/medical/splint + ) + +/obj/item/weapon/storage/firstaid/combat + name = "combat medical kit" + desc = "Contains advanced medical treatments." + icon_state = "bezerk" + item_state_slots = list(slot_r_hand_str = "firstaid-advanced", slot_l_hand_str = "firstaid-advanced") + starts_with = list( + /obj/item/weapon/storage/pill_bottle/bicaridine, + /obj/item/weapon/storage/pill_bottle/dermaline, + /obj/item/weapon/storage/pill_bottle/dexalin_plus, + /obj/item/weapon/storage/pill_bottle/dylovene, + /obj/item/weapon/storage/pill_bottle/tramadol, + /obj/item/weapon/storage/pill_bottle/spaceacillin, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, + /obj/item/stack/medical/splint, + /obj/item/device/healthanalyzer/advanced + ) + +/obj/item/weapon/storage/firstaid/surgery + name = "surgery kit" + desc = "Contains tools for surgery. Has precise foam fitting for safe transport and automatically sterilizes the content between uses." + icon = 'icons/obj/storage.dmi' // VOREStation edit + icon_state = "surgerykit" + item_state = "firstaid-surgery" + max_w_class = ITEMSIZE_NORMAL + + can_hold = list( + /obj/item/weapon/surgical/bonesetter, + /obj/item/weapon/surgical/cautery, + /obj/item/weapon/surgical/circular_saw, + /obj/item/weapon/surgical/hemostat, + /obj/item/weapon/surgical/retractor, + /obj/item/weapon/surgical/scalpel, + /obj/item/weapon/surgical/surgicaldrill, + /obj/item/weapon/surgical/bonegel, + /obj/item/weapon/surgical/FixOVein, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/stack/nanopaste, + /obj/item/device/healthanalyzer/advanced, + /obj/item/weapon/autopsy_scanner + ) + + starts_with = list( + /obj/item/weapon/surgical/bonesetter, + /obj/item/weapon/surgical/cautery, + /obj/item/weapon/surgical/circular_saw, + /obj/item/weapon/surgical/hemostat, + /obj/item/weapon/surgical/retractor, + /obj/item/weapon/surgical/scalpel, + /obj/item/weapon/surgical/surgicaldrill, + /obj/item/weapon/surgical/bonegel, + /obj/item/weapon/surgical/FixOVein, + /obj/item/stack/medical/advanced/bruise_pack, + /obj/item/device/healthanalyzer/advanced, + /obj/item/weapon/autopsy_scanner + ) + +/obj/item/weapon/storage/firstaid/clotting + name = "clotting kit" + desc = "Contains chemicals to stop bleeding." + max_storage_space = ITEMSIZE_COST_SMALL * 7 + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting = 8) + +/obj/item/weapon/storage/firstaid/bonemed + name = "bone repair kit" + desc = "Contains chemicals to mend broken bones." + max_storage_space = ITEMSIZE_COST_SMALL * 7 + starts_with = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector/bonemed = 8) + +/* + * Pill Bottles + */ +/obj/item/weapon/storage/pill_bottle + name = "pill bottle" + desc = "It's an airtight container for storing medication." + icon_state = "pill_canister" + icon = 'icons/obj/chemical.dmi' + drop_sound = 'sound/items/drop/pillbottle.ogg' + pickup_sound = 'sound/items/pickup/pillbottle.ogg' + item_state_slots = list(slot_r_hand_str = "contsolid", slot_l_hand_str = "contsolid") + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/dice,/obj/item/weapon/paper) + allow_quick_gather = 1 + allow_quick_empty = 1 + use_to_pickup = TRUE + use_sound = 'sound/items/storage/pillbottle.ogg' + max_storage_space = ITEMSIZE_COST_TINY * 14 + max_w_class = ITEMSIZE_TINY + var/wrapper_color + var/label + + var/label_text = "" + var/base_name = " " + var/base_desc = " " + +/obj/item/weapon/storage/pill_bottle/Initialize() + . = ..() + base_name = name + base_desc = desc + update_icon() + +/obj/item/weapon/storage/pill_bottle/update_icon() + cut_overlays() + if(wrapper_color) + var/image/I = image(icon, "pillbottle_wrap") + I.color = wrapper_color + add_overlay(I) + +/obj/item/weapon/storage/pill_bottle/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) + var/tmp_label = sanitizeSafe(tgui_input_text(user, "Enter a label for [name]", "Label", label_text, MAX_NAME_LEN), MAX_NAME_LEN) + if(length(tmp_label) > 50) + to_chat(user, "The label can be at most 50 characters long.") + else if(length(tmp_label) > 10) + to_chat(user, "You set the label.") + label_text = tmp_label + update_name_label() + else + to_chat(user, "You set the label to \"[tmp_label]\".") + label_text = tmp_label + update_name_label() + else + ..() + +/obj/item/weapon/storage/pill_bottle/proc/update_name_label() + if(!label_text) + name = base_name + desc = base_desc + return + else if(length(label_text) > 10) + var/short_label_text = copytext(label_text, 1, 11) + name = "[base_name] ([short_label_text]...)" + else + name = "[base_name] ([label_text])" + desc = "[base_desc] It is labeled \"[label_text]\"." + +/obj/item/weapon/storage/pill_bottle/antitox + name = "pill bottle (Dylovene)" + desc = "Contains pills used to counter toxins." + starts_with = list(/obj/item/weapon/reagent_containers/pill/antitox = 7) + wrapper_color = COLOR_GREEN + +/obj/item/weapon/storage/pill_bottle/bicaridine + name = "pill bottle (Bicaridine)" + desc = "Contains pills used to stabilize the severely injured." + starts_with = list(/obj/item/weapon/reagent_containers/pill/bicaridine = 7) + wrapper_color = COLOR_MAROON + +/obj/item/weapon/storage/pill_bottle/dexalin_plus + name = "pill bottle (Dexalin Plus)" + desc = "Contains pills used to treat extreme cases of oxygen deprivation." + starts_with = list(/obj/item/weapon/reagent_containers/pill/dexalin_plus = 7) + wrapper_color = "#3366cc" + +/obj/item/weapon/storage/pill_bottle/dermaline + name = "pill bottle (Dermaline)" + desc = "Contains pills used to treat burn wounds." + starts_with = list(/obj/item/weapon/reagent_containers/pill/dermaline = 7) + wrapper_color = "#e8d131" + +/obj/item/weapon/storage/pill_bottle/dylovene + name = "pill bottle (Dylovene)" + desc = "Contains pills used to treat toxic substances in the blood." + starts_with = list(/obj/item/weapon/reagent_containers/pill/dylovene = 7) + wrapper_color = COLOR_GREEN + +/obj/item/weapon/storage/pill_bottle/inaprovaline + name = "pill bottle (Inaprovaline)" + desc = "Contains pills used to stabilize patients." + starts_with = list(/obj/item/weapon/reagent_containers/pill/inaprovaline = 7) + wrapper_color = COLOR_PALE_BLUE_GRAY + +/obj/item/weapon/storage/pill_bottle/kelotane + name = "pill bottle (Kelotane)" + desc = "Contains pills used to treat burns." + starts_with = list(/obj/item/weapon/reagent_containers/pill/kelotane = 7) + wrapper_color = "#ec8b2f" + +/obj/item/weapon/storage/pill_bottle/spaceacillin + name = "pill bottle (Spaceacillin)" + desc = "A theta-lactam antibiotic. Effective against many diseases likely to be encountered in space." + starts_with = list(/obj/item/weapon/reagent_containers/pill/spaceacillin = 7) + wrapper_color = COLOR_PALE_GREEN_GRAY + +/obj/item/weapon/storage/pill_bottle/tramadol + name = "pill bottle (Tramadol)" + desc = "Contains pills used to relieve pain." + starts_with = list(/obj/item/weapon/reagent_containers/pill/tramadol = 7) + wrapper_color = COLOR_PURPLE_GRAY + +/obj/item/weapon/storage/pill_bottle/citalopram + name = "pill bottle (Citalopram)" + desc = "Contains pills used to stabilize a patient's mood." + starts_with = list(/obj/item/weapon/reagent_containers/pill/citalopram = 7) + wrapper_color = COLOR_GRAY + +/obj/item/weapon/storage/pill_bottle/carbon + name = "pill bottle (Carbon)" + desc = "Contains pills used to neutralise chemicals in the stomach." + starts_with = list(/obj/item/weapon/reagent_containers/pill/carbon = 7) + +/obj/item/weapon/storage/pill_bottle/iron + name = "pill bottle (Iron)" + desc = "Contains pills used to aid in blood regeneration." + starts_with = list(/obj/item/weapon/reagent_containers/pill/iron = 7) diff --git a/code/game/objects/items/weapons/storage/laundry_basket.dm b/code/game/objects/items/weapons/storage/laundry_basket.dm index db6b304f319..ab0f529153f 100644 --- a/code/game/objects/items/weapons/storage/laundry_basket.dm +++ b/code/game/objects/items/weapons/storage/laundry_basket.dm @@ -65,7 +65,8 @@ return ..() /obj/item/weapon/storage/laundry_basket/dropped(mob/user as mob) - QDEL_NULL(linked) + if(linked) + QDEL_NULL(linked) return ..() /obj/item/weapon/storage/laundry_basket/show_to(mob/user as mob) @@ -82,6 +83,6 @@ use_to_pickup = FALSE /obj/item/weapon/storage/laundry_basket/offhand/dropped(mob/user as mob) - user.drop_from_inventory(linked) + if(user.isEquipped(linked)) + user.drop_from_inventory(linked) return - diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm index b3fbc758b3b..16db776bbcf 100644 --- a/code/game/objects/items/weapons/storage/lockbox.dm +++ b/code/game/objects/items/weapons/storage/lockbox.dm @@ -1,104 +1,104 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/storage/lockbox - name = "lockbox" - desc = "A locked box." - icon_state = "lockbox+l" - item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_NORMAL * 4 //The sum of the w_classes of all the items in this storage item. - req_access = list(access_armory) - preserve_item = 1 - var/locked = 1 - var/broken = 0 - var/icon_locked = "lockbox+l" - var/icon_closed = "lockbox" - var/icon_broken = "lockbox+b" - - -/obj/item/weapon/storage/lockbox/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/card/id)) - if(src.broken) - to_chat(user, "It appears to be broken.") - return - if(src.allowed(user)) - src.locked = !( src.locked ) - if(src.locked) - src.icon_state = src.icon_locked - to_chat(user, "You lock \the [src]!") - close_all() - return - else - src.icon_state = src.icon_closed - to_chat(user, "You unlock \the [src]!") - return - else - to_chat(user, "Access Denied") - else if(istype(W, /obj/item/weapon/melee/energy/blade)) - if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - if(!locked) - ..() - else - to_chat(user, "It's locked!") - return - - -/obj/item/weapon/storage/lockbox/show_to(mob/user as mob) - if(locked) - to_chat(user, "It's locked!") - else - ..() - return - -/obj/item/weapon/storage/lockbox/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") - if(!broken) - if(visual_feedback) - visual_feedback = "[visual_feedback]" - else - visual_feedback = "The locker has been sliced open by [user] with an electromagnetic card!" - if(audible_feedback) - audible_feedback = "[audible_feedback]" - else - audible_feedback = "You hear a faint electrical spark." - - broken = 1 - locked = 0 - desc = "It appears to be broken." - icon_state = src.icon_broken - visible_message(visual_feedback, audible_feedback) - return 1 - -/obj/item/weapon/storage/lockbox/loyalty - name = "lockbox of loyalty implants" - req_access = list(access_security) - starts_with = list( - /obj/item/weapon/implantcase/loyalty = 3, - /obj/item/weapon/implanter/loyalty - ) - -/obj/item/weapon/storage/lockbox/clusterbang - name = "lockbox of clusterbangs" - desc = "You have a bad feeling about opening this." - req_access = list(access_security) - starts_with = list(/obj/item/weapon/grenade/flashbang/clusterbang) - -/obj/item/weapon/storage/lockbox/medal - name = "lockbox of medals" - desc = "A lockbox filled with commemorative medals, it has the NanoTrasen logo stamped on it." - req_access = list(access_heads) - storage_slots = 7 - starts_with = list( - /obj/item/clothing/accessory/medal/conduct, - /obj/item/clothing/accessory/medal/bronze_heart, - /obj/item/clothing/accessory/medal/nobel_science, - /obj/item/clothing/accessory/medal/silver/valor, - /obj/item/clothing/accessory/medal/silver/security, - /obj/item/clothing/accessory/medal/gold/captain, - /obj/item/clothing/accessory/medal/gold/heroism - ) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/storage/lockbox + name = "lockbox" + desc = "A locked box." + icon_state = "lockbox+l" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_NORMAL * 4 //The sum of the w_classes of all the items in this storage item. + req_access = list(access_armory) + preserve_item = 1 + var/locked = 1 + var/broken = 0 + var/icon_locked = "lockbox+l" + var/icon_closed = "lockbox" + var/icon_broken = "lockbox+b" + + +/obj/item/weapon/storage/lockbox/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/card/id)) + if(src.broken) + to_chat(user, "It appears to be broken.") + return + if(src.allowed(user)) + src.locked = !( src.locked ) + if(src.locked) + src.icon_state = src.icon_locked + to_chat(user, "You lock \the [src]!") + close_all() + return + else + src.icon_state = src.icon_closed + to_chat(user, "You unlock \the [src]!") + return + else + to_chat(user, "Access Denied") + else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + if(!locked) + ..() + else + to_chat(user, "It's locked!") + return + + +/obj/item/weapon/storage/lockbox/show_to(mob/user as mob) + if(locked) + to_chat(user, "It's locked!") + else + ..() + return + +/obj/item/weapon/storage/lockbox/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") + if(!broken) + if(visual_feedback) + visual_feedback = "[visual_feedback]" + else + visual_feedback = "The locker has been sliced open by [user] with an electromagnetic card!" + if(audible_feedback) + audible_feedback = "[audible_feedback]" + else + audible_feedback = "You hear a faint electrical spark." + + broken = 1 + locked = 0 + desc = "It appears to be broken." + icon_state = src.icon_broken + visible_message(visual_feedback, audible_feedback) + return 1 + +/obj/item/weapon/storage/lockbox/loyalty + name = "lockbox of loyalty implants" + req_access = list(access_security) + starts_with = list( + /obj/item/weapon/implantcase/loyalty = 3, + /obj/item/weapon/implanter/loyalty + ) + +/obj/item/weapon/storage/lockbox/clusterbang + name = "lockbox of clusterbangs" + desc = "You have a bad feeling about opening this." + req_access = list(access_security) + starts_with = list(/obj/item/weapon/grenade/flashbang/clusterbang) + +/obj/item/weapon/storage/lockbox/medal + name = "lockbox of medals" + desc = "A lockbox filled with commemorative medals, it has the NanoTrasen logo stamped on it." + req_access = list(access_heads) + storage_slots = 7 + starts_with = list( + /obj/item/clothing/accessory/medal/conduct, + /obj/item/clothing/accessory/medal/bronze_heart, + /obj/item/clothing/accessory/medal/nobel_science, + /obj/item/clothing/accessory/medal/silver/valor, + /obj/item/clothing/accessory/medal/silver/security, + /obj/item/clothing/accessory/medal/gold/captain, + /obj/item/clothing/accessory/medal/gold/heroism + ) diff --git a/code/game/objects/items/weapons/storage/misc.dm b/code/game/objects/items/weapons/storage/misc.dm index 8b6384b9cba..70f70e8bcc9 100644 --- a/code/game/objects/items/weapons/storage/misc.dm +++ b/code/game/objects/items/weapons/storage/misc.dm @@ -1,103 +1,103 @@ -/* - * Donut Box - */ - -var/list/random_weighted_donuts = list( - /obj/item/weapon/reagent_containers/food/snacks/donut/plain = 5, - /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly = 5, - /obj/item/weapon/reagent_containers/food/snacks/donut/pink = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/pink/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/purple = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/purple/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/green = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/green/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/beige = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/beige/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/blue = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/blue/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/yellow = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/yellow/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/olive = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/olive/jelly = 4, - /obj/item/weapon/reagent_containers/food/snacks/donut/homer = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/homer/jelly = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles/jelly = 3, - /obj/item/weapon/reagent_containers/food/snacks/donut/chaos = 1 -) - -/obj/item/weapon/storage/box/donut - icon = 'icons/obj/food_donuts.dmi' - icon_state = "donutbox" - name = "donut box" - desc = "A box that holds tasty donuts, if you're lucky." - center_of_mass = list("x" = 16,"y" = 9) - max_storage_space = ITEMSIZE_COST_SMALL * 6 - can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/donut) - foldable = /obj/item/stack/material/cardboard - //starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donut/normal = 6) - -/obj/item/weapon/storage/box/donut/Initialize() - if(!empty) - for(var/i in 1 to 6) - var/type_to_spawn = pickweight(random_weighted_donuts) - new type_to_spawn(src) - . = ..() - update_icon() - -/obj/item/weapon/storage/box/donut/update_icon() - cut_overlays() - var/x_offset = 0 - for(var/obj/item/weapon/reagent_containers/food/snacks/donut/D in contents) - var/mutable_appearance/ma = mutable_appearance(icon = icon, icon_state = D.overlay_state) - ma.pixel_x = x_offset - add_overlay(ma) - x_offset += 3 - -/obj/item/weapon/storage/box/donut/empty - empty = TRUE - -/obj/item/weapon/storage/box/wormcan - icon = 'icons/obj/food.dmi' - icon_state = "wormcan" - name = "can of worms" - desc = "You probably do want to open this can of worms." - max_storage_space = ITEMSIZE_COST_TINY * 6 - can_hold = list( - /obj/item/weapon/reagent_containers/food/snacks/wormsickly, - /obj/item/weapon/reagent_containers/food/snacks/worm, - /obj/item/weapon/reagent_containers/food/snacks/wormdeluxe - ) - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/worm = 6) - -/obj/item/weapon/storage/box/wormcan/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/box/wormcan/update_icon(var/itemremoved = 0) - if (contents.len == 0) - icon_state = "wormcan_empty" - -/obj/item/weapon/storage/box/wormcan/sickly - icon_state = "wormcan_sickly" - name = "can of sickly worms" - desc = "You probably don't want to open this can of worms." - max_storage_space = ITEMSIZE_COST_TINY * 6 - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormsickly = 6) - -/obj/item/weapon/storage/box/wormcan/sickly/update_icon(var/itemremoved = 0) - if (contents.len == 0) - icon_state = "wormcan_empty_sickly" - -/obj/item/weapon/storage/box/wormcan/deluxe - icon_state = "wormcan_deluxe" - name = "can of deluxe worms" - desc = "You absolutely want to open this can of worms." - max_storage_space = ITEMSIZE_COST_TINY * 6 - starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe = 6) - -/obj/item/weapon/storage/box/wormcan/deluxe/update_icon(var/itemremoved = 0) - if (contents.len == 0) +/* + * Donut Box + */ + +var/list/random_weighted_donuts = list( + /obj/item/weapon/reagent_containers/food/snacks/donut/plain = 5, + /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly = 5, + /obj/item/weapon/reagent_containers/food/snacks/donut/pink = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/pink/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/purple = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/purple/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/green = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/green/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/beige = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/beige/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/blue = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/blue/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/yellow = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/yellow/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/olive = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/olive/jelly = 4, + /obj/item/weapon/reagent_containers/food/snacks/donut/homer = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/homer/jelly = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/choc_sprinkles/jelly = 3, + /obj/item/weapon/reagent_containers/food/snacks/donut/chaos = 1 +) + +/obj/item/weapon/storage/box/donut + icon = 'icons/obj/food_donuts.dmi' + icon_state = "donutbox" + name = "donut box" + desc = "A box that holds tasty donuts, if you're lucky." + center_of_mass = list("x" = 16,"y" = 9) + max_storage_space = ITEMSIZE_COST_SMALL * 6 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/donut) + foldable = /obj/item/stack/material/cardboard + //starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/donut/normal = 6) + +/obj/item/weapon/storage/box/donut/Initialize() + if(!empty) + for(var/i in 1 to 6) + var/type_to_spawn = pickweight(random_weighted_donuts) + new type_to_spawn(src) + . = ..() + update_icon() + +/obj/item/weapon/storage/box/donut/update_icon() + cut_overlays() + var/x_offset = 0 + for(var/obj/item/weapon/reagent_containers/food/snacks/donut/D in contents) + var/mutable_appearance/ma = mutable_appearance(icon = icon, icon_state = D.overlay_state) + ma.pixel_x = x_offset + add_overlay(ma) + x_offset += 3 + +/obj/item/weapon/storage/box/donut/empty + empty = TRUE + +/obj/item/weapon/storage/box/wormcan + icon = 'icons/obj/food.dmi' + icon_state = "wormcan" + name = "can of worms" + desc = "You probably do want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + can_hold = list( + /obj/item/weapon/reagent_containers/food/snacks/wormsickly, + /obj/item/weapon/reagent_containers/food/snacks/worm, + /obj/item/weapon/reagent_containers/food/snacks/wormdeluxe + ) + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/worm = 6) + +/obj/item/weapon/storage/box/wormcan/Initialize() + . = ..() + update_icon() + +/obj/item/weapon/storage/box/wormcan/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty" + +/obj/item/weapon/storage/box/wormcan/sickly + icon_state = "wormcan_sickly" + name = "can of sickly worms" + desc = "You probably don't want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormsickly = 6) + +/obj/item/weapon/storage/box/wormcan/sickly/update_icon(var/itemremoved = 0) + if (contents.len == 0) + icon_state = "wormcan_empty_sickly" + +/obj/item/weapon/storage/box/wormcan/deluxe + icon_state = "wormcan_deluxe" + name = "can of deluxe worms" + desc = "You absolutely want to open this can of worms." + max_storage_space = ITEMSIZE_COST_TINY * 6 + starts_with = list(/obj/item/weapon/reagent_containers/food/snacks/wormdeluxe = 6) + +/obj/item/weapon/storage/box/wormcan/deluxe/update_icon(var/itemremoved = 0) + if (contents.len == 0) icon_state = "wormcan_empty_deluxe" \ No newline at end of file diff --git a/code/game/objects/items/weapons/storage/pouches.dm b/code/game/objects/items/weapons/storage/pouches.dm index fa3ee2fe91d..6eb775c2fee 100644 --- a/code/game/objects/items/weapons/storage/pouches.dm +++ b/code/game/objects/items/weapons/storage/pouches.dm @@ -76,6 +76,8 @@ /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, /obj/item/device/multitool, /obj/item/device/flashlight, /obj/item/weapon/cell/device, @@ -95,9 +97,9 @@ /obj/item/weapon/extinguisher/mini, /obj/item/weapon/tape_roll, /obj/item/device/integrated_electronics/wirer, - /obj/item/device/integrated_electronics/debugger, - /obj/item/weapon/shovel/spade, - /obj/item/stack/nanopaste, + /obj/item/device/integrated_electronics/debugger, + /obj/item/weapon/shovel/spade, + /obj/item/stack/nanopaste, /obj/item/device/geiger ) //Vorestation Add - make it the same as the tool-belt why was it not like this to start with wtf @@ -156,7 +158,7 @@ /obj/item/stack/nanopaste, /obj/item/taperoll/medical, /obj/item/weapon/storage/box/freezer, - /obj/item/clothing/mask/chewable/candy/lolli, + /obj/item/clothing/mask/chewable/candy/lolli, ) //Vorestation add - added a bunch of misc medical stuff max_storage_space = ITEMSIZE_COST_SMALL*3 //Vorestation Add - makes it slightly smaller since its a lot of stuff with pocket access remove_delay = 5 //Vorestation Add - .5 second delay, get the medical things faster because there is no reason to use this otherwise. still gotta stop moving to take things out. diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index 2fa1e33c031..d7d9a8f6d42 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -1,209 +1,209 @@ -/* - * Absorbs /obj/item/weapon/secstorage. - * Reimplements it only slightly to use existing storage functionality. - * - * Contains: - * Secure Briefcase - * Wall Safe - */ - -// ----------------------------- -// Generic Item -// ----------------------------- -/obj/item/weapon/storage/secure - name = "secstorage" - var/icon_locking = "secureb" - var/icon_sparking = "securespark" - var/icon_opened = "secure0" - var/locked = 1 - var/code = "" - var/l_code = null - var/l_set = 0 - var/l_setshort = 0 - var/l_hacking = 0 - var/emagged = 0 - var/open = 0 - w_class = ITEMSIZE_NORMAL - max_w_class = ITEMSIZE_SMALL - max_storage_space = ITEMSIZE_SMALL * 7 - use_sound = 'sound/items/storage/briefcase.ogg' - -/obj/item/weapon/storage/secure/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "The service panel is [src.open ? "open" : "closed"]." - -/obj/item/weapon/storage/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(locked) - if (istype(W, /obj/item/weapon/melee/energy/blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - return - - if (W.is_screwdriver()) - if (do_after(user, 20 * W.toolspeed)) - src.open =! src.open - playsound(src, W.usesound, 50, 1) - user.show_message(text("You [] the service panel.", (src.open ? "open" : "close"))) - return - if (istype(W, /obj/item/device/multitool) && (src.open == 1)&& (!src.l_hacking)) - user.show_message("Now attempting to reset internal memory, please hold.", 1) - src.l_hacking = 1 - if (do_after(usr, 100)) - if (prob(40)) - src.l_setshort = 1 - src.l_set = 0 - src.code = "" - user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) - sleep(80) - src.l_setshort = 0 - src.l_hacking = 0 - else - user.show_message("Unable to reset internal memory.", 1) - src.l_hacking = 0 - else src.l_hacking = 0 - return - //At this point you have exhausted all the special things to do when locked - // ... but it's still locked. - return - - // -> storage/attackby() what with handle insertion, etc - ..() - - -/obj/item/weapon/storage/secure/MouseDrop(over_object, src_location, over_location) - if (locked) - src.add_fingerprint(usr) - return - ..() - -/obj/item/weapon/storage/secure/AltClick(mob/user as mob) - if (isliving(user) && Adjacent(user) && (src.locked == 1)) - to_chat(user, "[src] is locked and cannot be opened!") - else if (isliving(user) && Adjacent(user) && (!src.locked)) - src.open(usr) - else - for(var/mob/M in range(1)) - if (M.s_active == src) - src.close(M) - src.add_fingerprint(user) - return - -/obj/item/weapon/storage/secure/attack_self(mob/user as mob) - tgui_interact(user) - -/obj/item/weapon/storage/secure/tgui_interact(mob/user, datum/tgui/ui = null) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SecureSafe", name) - ui.open() - -/obj/item/weapon/storage/secure/tgui_data(mob/user) - var/list/data = list() - data["locked"] = locked - data["code"] = code - data["emagged"] = emagged - data["l_setshort"] = l_setshort - data["l_set"] = l_set - return data - -/obj/item/weapon/storage/secure/tgui_act(action, params) - if(..()) - return TRUE - switch (action) - if("type") - var/digit = params["digit"] - if(digit == "E") - if ((src.l_set == 0) && (length(src.code) == 5) && (!src.l_setshort) && (src.code != "ERROR")) - src.l_code = src.code - src.l_set = 1 - else if ((src.code == src.l_code) && (src.emagged == 0) && (src.l_set == 1)) - src.locked = 0 - cut_overlays() - add_overlay(icon_opened) - src.code = null - else - src.code = "ERROR" - else - if ((digit == "R") && (src.emagged == 0) && (!src.l_setshort)) - src.locked = 1 - cut_overlays() - src.code = null - src.close(usr) - else - src.code += text("[]", digit) - if (length(src.code) > 5) - src.code = "ERROR" - src.add_fingerprint(usr) - . = TRUE - return - -/obj/item/weapon/storage/secure/emag_act(var/remaining_charges, var/mob/user, var/feedback) - if(!emagged) - emagged = 1 - src.add_overlay(icon_sparking) - sleep(6) - cut_overlays() - add_overlay(icon_locking) - locked = 0 - to_chat(user, (feedback ? feedback : "You short out the lock of \the [src].")) - return 1 - -// ----------------------------- -// Secure Briefcase -// ----------------------------- -/obj/item/weapon/storage/secure/briefcase - name = "secure briefcase" - icon = 'icons/obj/storage.dmi' - icon_state = "secure" - item_state_slots = list(slot_r_hand_str = "case", slot_l_hand_str = "case") - desc = "A large briefcase with a digital locking system." - force = 8.0 - throw_speed = 1 - throw_range = 4 - max_w_class = ITEMSIZE_NORMAL - w_class = ITEMSIZE_LARGE - max_storage_space = ITEMSIZE_COST_NORMAL * 4 - -/obj/item/weapon/storage/secure/briefcase/attack_hand(mob/user as mob) - if ((src.loc == user) && (src.locked == 1)) - to_chat(user, "[src] is locked and cannot be opened!") - else if ((src.loc == user) && (!src.locked)) - src.open(usr) - else - ..() - for(var/mob/M in range(1)) - if (M.s_active == src) - src.close(M) - src.add_fingerprint(user) - return - -// ----------------------------- -// Secure Safe -// ----------------------------- - -/obj/item/weapon/storage/secure/safe - name = "secure safe" - desc = "It doesn't seem all that secure. Oh well, it'll do." - icon = 'icons/obj/storage.dmi' - icon_state = "safe" - layer = ABOVE_WINDOW_LAYER - icon_opened = "safe0" - icon_locking = "safeb" - icon_sparking = "safespark" - force = 8.0 - w_class = ITEMSIZE_NO_CONTAINER - max_w_class = ITEMSIZE_LARGE // This was 8 previously... - anchored = TRUE - density = FALSE - cant_hold = list(/obj/item/weapon/storage/secure/briefcase) - starts_with = list( - /obj/item/weapon/paper, - /obj/item/weapon/pen - ) - -/obj/item/weapon/storage/secure/safe/attack_hand(mob/user as mob) - tgui_interact(user) +/* + * Absorbs /obj/item/weapon/secstorage. + * Reimplements it only slightly to use existing storage functionality. + * + * Contains: + * Secure Briefcase + * Wall Safe + */ + +// ----------------------------- +// Generic Item +// ----------------------------- +/obj/item/weapon/storage/secure + name = "secstorage" + var/icon_locking = "secureb" + var/icon_sparking = "securespark" + var/icon_opened = "secure0" + var/locked = 1 + var/code = "" + var/l_code = null + var/l_set = 0 + var/l_setshort = 0 + var/l_hacking = 0 + var/emagged = 0 + var/open = 0 + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_SMALL + max_storage_space = ITEMSIZE_SMALL * 7 + use_sound = 'sound/items/storage/briefcase.ogg' + +/obj/item/weapon/storage/secure/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "The service panel is [src.open ? "open" : "closed"]." + +/obj/item/weapon/storage/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(locked) + if (istype(W, /obj/item/weapon/melee/energy/blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + return + + if (W.has_tool_quality(TOOL_SCREWDRIVER)) + if (do_after(user, 20 * W.toolspeed)) + src.open =! src.open + playsound(src, W.usesound, 50, 1) + user.show_message(text("You [] the service panel.", (src.open ? "open" : "close"))) + return + if (istype(W, /obj/item/device/multitool) && (src.open == 1)&& (!src.l_hacking)) + user.show_message("Now attempting to reset internal memory, please hold.", 1) + src.l_hacking = 1 + if (do_after(usr, 100)) + if (prob(40)) + src.l_setshort = 1 + src.l_set = 0 + src.code = "" + user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) + sleep(80) + src.l_setshort = 0 + src.l_hacking = 0 + else + user.show_message("Unable to reset internal memory.", 1) + src.l_hacking = 0 + else src.l_hacking = 0 + return + //At this point you have exhausted all the special things to do when locked + // ... but it's still locked. + return + + // -> storage/attackby() what with handle insertion, etc + ..() + + +/obj/item/weapon/storage/secure/MouseDrop(over_object, src_location, over_location) + if (locked) + src.add_fingerprint(usr) + return + ..() + +/obj/item/weapon/storage/secure/AltClick(mob/user as mob) + if (isliving(user) && Adjacent(user) && (src.locked == 1)) + to_chat(user, "[src] is locked and cannot be opened!") + else if (isliving(user) && Adjacent(user) && (!src.locked)) + src.open(usr) + else + for(var/mob/M in range(1)) + if (M.s_active == src) + src.close(M) + src.add_fingerprint(user) + return + +/obj/item/weapon/storage/secure/attack_self(mob/user as mob) + tgui_interact(user) + +/obj/item/weapon/storage/secure/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SecureSafe", name) + ui.open() + +/obj/item/weapon/storage/secure/tgui_data(mob/user) + var/list/data = list() + data["locked"] = locked + data["code"] = code + data["emagged"] = emagged + data["l_setshort"] = l_setshort + data["l_set"] = l_set + return data + +/obj/item/weapon/storage/secure/tgui_act(action, params) + if(..()) + return TRUE + switch (action) + if("type") + var/digit = params["digit"] + if(digit == "E") + if ((src.l_set == 0) && (length(src.code) == 5) && (!src.l_setshort) && (src.code != "ERROR")) + src.l_code = src.code + src.l_set = 1 + else if ((src.code == src.l_code) && (src.emagged == 0) && (src.l_set == 1)) + src.locked = 0 + cut_overlays() + add_overlay(icon_opened) + src.code = null + else + src.code = "ERROR" + else + if ((digit == "R") && (src.emagged == 0) && (!src.l_setshort)) + src.locked = 1 + cut_overlays() + src.code = null + src.close(usr) + else + src.code += text("[]", digit) + if (length(src.code) > 5) + src.code = "ERROR" + src.add_fingerprint(usr) + . = TRUE + return + +/obj/item/weapon/storage/secure/emag_act(var/remaining_charges, var/mob/user, var/feedback) + if(!emagged) + emagged = 1 + src.add_overlay(icon_sparking) + sleep(6) + cut_overlays() + add_overlay(icon_locking) + locked = 0 + to_chat(user, (feedback ? feedback : "You short out the lock of \the [src].")) + return 1 + +// ----------------------------- +// Secure Briefcase +// ----------------------------- +/obj/item/weapon/storage/secure/briefcase + name = "secure briefcase" + icon = 'icons/obj/storage.dmi' + icon_state = "secure" + item_state_slots = list(slot_r_hand_str = "case", slot_l_hand_str = "case") + desc = "A large briefcase with a digital locking system." + force = 8.0 + throw_speed = 1 + throw_range = 4 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_LARGE + max_storage_space = ITEMSIZE_COST_NORMAL * 4 + +/obj/item/weapon/storage/secure/briefcase/attack_hand(mob/user as mob) + if ((src.loc == user) && (src.locked == 1)) + to_chat(user, "[src] is locked and cannot be opened!") + else if ((src.loc == user) && (!src.locked)) + src.open(usr) + else + ..() + for(var/mob/M in range(1)) + if (M.s_active == src) + src.close(M) + src.add_fingerprint(user) + return + +// ----------------------------- +// Secure Safe +// ----------------------------- + +/obj/item/weapon/storage/secure/safe + name = "secure safe" + desc = "It doesn't seem all that secure. Oh well, it'll do." + icon = 'icons/obj/storage.dmi' + icon_state = "safe" + layer = ABOVE_WINDOW_LAYER + icon_opened = "safe0" + icon_locking = "safeb" + icon_sparking = "safespark" + force = 8.0 + w_class = ITEMSIZE_NO_CONTAINER + max_w_class = ITEMSIZE_LARGE // This was 8 previously... + anchored = TRUE + density = FALSE + cant_hold = list(/obj/item/weapon/storage/secure/briefcase) + starts_with = list( + /obj/item/weapon/paper, + /obj/item/weapon/pen + ) + +/obj/item/weapon/storage/secure/safe/attack_hand(mob/user as mob) + tgui_interact(user) diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index fb0889e7861..d8cc1a78d0b 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -1,901 +1,901 @@ -// To clarify: -// For use_to_pickup and allow_quick_gather functionality, -// see item/attackby() (/game/objects/items.dm) -// Do not remove this functionality without good reason, cough reagent_containers cough. -// -Sayu - - -/obj/item/weapon/storage - name = "storage" - icon = 'icons/obj/storage.dmi' - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', - ) - w_class = ITEMSIZE_NORMAL - show_messages = 1 - - /// List of objects which this item can store (if set, it can't store anything else) - var/list/can_hold - /// List of objects which this item can't store (in effect only if can_hold isn't set) - var/list/cant_hold - /// List of mobs which are currently seeing the contents of this item's storage - var/list/is_seeing - - var/max_w_class = ITEMSIZE_SMALL //Max size of objects that this object can store (in effect only if can_hold isn't set) - var/max_storage_space = ITEMSIZE_COST_SMALL * 4 //The sum of the storage costs of all the items in this storage item. - var/storage_slots = null //The number of storage slots in this container. If null, it uses the volume-based storage instead. - - /// Boxes screen object for fixed-size storage (belts, etc) - var/obj/screen/storage/boxes = null - /// List of 'click catchers' for boxes for fixed-size storage - var/list/box_catchers = null - - /// For dynamic storage, the leftmost pixel column for the whole storage display. Most of the interesting effects are hung on this in vis_contents. - var/obj/screen/storage/storage_start = null - /// For dynamic storage, the majority of the width of the whole storage display. Decorative, but sized to the width appropriate to represent how much storage there is. - var/obj/screen/storage/storage_continue = null - /// For dynamic storage, the rightmost pixel column for the whole storage display. Decorative. - var/obj/screen/storage/storage_end = null - - /// The "X" button at the far right of the storage - var/obj/screen/close/closer = null - - var/use_to_pickup //Set this to make it possible to use this item in an inverse way, so you can have the item in your hand and click items on the floor to pick them up. - var/display_contents_with_number //Set this to make the storage item group contents of the same type and display them as a number. - var/allow_quick_empty //Set this variable to allow the object to have the 'empty' verb, which dumps all the contents on the floor. - var/allow_quick_gather //Set this variable to allow the object to have the 'toggle mode' verb, which quickly collects all items from a tile. - var/collection_mode = 1; //0 = pick one at a time, 1 = pick all on tile - var/use_sound = "rustle" //sound played when used. null for no sound. - var/list/starts_with //Things to spawn on the box on spawn - var/empty //Mapper override to spawn an empty version of a container that usually has stuff - /// If you can use this storage while in a pocket - var/pocketable = FALSE - -/obj/item/weapon/storage/Initialize() - . = ..() - - if(allow_quick_empty) - verbs += /obj/item/weapon/storage/verb/quick_empty - else - verbs -= /obj/item/weapon/storage/verb/quick_empty - - if(allow_quick_gather) - verbs += /obj/item/weapon/storage/verb/toggle_gathering_mode - else - verbs -= /obj/item/weapon/storage/verb/toggle_gathering_mode - - if(storage_slots) - src.boxes = new /obj/screen/storage( ) - src.boxes.name = "storage" - src.boxes.master = src - src.boxes.icon_state = "block" - src.boxes.screen_loc = "7,7 to 10,8" - else - src.storage_start = new /obj/screen/storage( ) - src.storage_start.name = "storage" - src.storage_start.master = src - src.storage_start.icon_state = "storage_start" - src.storage_start.screen_loc = "7,7 to 10,8" - - src.storage_continue = new /obj/screen/storage( ) - src.storage_continue.name = "storage" - src.storage_continue.master = src - src.storage_continue.icon_state = "storage_continue" - src.storage_continue.screen_loc = "7,7 to 10,8" - - src.storage_end = new /obj/screen/storage( ) - src.storage_end.name = "storage" - src.storage_end.master = src - src.storage_end.icon_state = "storage_end" - src.storage_end.screen_loc = "7,7 to 10,8" - - src.closer = new /obj/screen/close( ) - src.closer.master = src - src.closer.icon_state = "storage_close" - src.closer.hud_layerise() - orient2hud() - - if(LAZYLEN(starts_with) && !empty) - for(var/newtype in starts_with) - var/count = starts_with[newtype] || 1 //Could have left it blank. - while(count) - count-- - new newtype(src) - starts_with = null //Reduce list count. - update_icon() - - calibrate_size() - -/obj/item/weapon/storage/Destroy() - close_all() - clear_slot_catchers() - QDEL_NULL(boxes) - QDEL_NULL(storage_start) - QDEL_NULL(storage_continue) - QDEL_NULL(storage_end) - QDEL_NULL(closer) - - if(ismob(loc)) - var/mob/M = loc - M.remove_from_mob(src) - - . = ..() - -/obj/item/weapon/storage/MouseDrop(obj/over_object as obj) - if(!canremove) - return - - if (isliving(usr) || isobserver(usr)) - - if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? - return - - if(over_object == usr && Adjacent(usr)) // this must come before the screen objects only block - src.open(usr) - return - - if (!( istype(over_object, /obj/screen) )) - return ..() - - //makes sure that the storage is equipped, so that we can't drag it into our hand from miles away. - //there's got to be a better way of doing this. - if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) - return - - if (( usr.restrained() ) || ( usr.stat )) - return - - if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) - return - - switch(over_object.name) - if("r_hand") - usr.unEquip(src) - usr.put_in_r_hand(src) - if("l_hand") - usr.unEquip(src) - usr.put_in_l_hand(src) - src.add_fingerprint(usr) - -/obj/item/weapon/storage/AltClick(mob/user) - if(user in is_seeing) - src.close(user) - // I would think there should be some incap check here or something - // But MouseDrop doesn't use one (as of this writing), so... - else if(isliving(user) && Adjacent(user)) - src.open(user) - else - return ..() - -/obj/item/weapon/storage/proc/return_inv() - - var/list/L = list( ) - - L += src.contents - - for(var/obj/item/weapon/storage/S in src) - L += S.return_inv() - for(var/obj/item/weapon/gift/G in src) - L += G.gift - if (istype(G.gift, /obj/item/weapon/storage)) - L += G.gift:return_inv() - return L - -/obj/item/weapon/storage/proc/show_to(mob/user as mob) - if(user.s_active != src) - for(var/obj/item/I in src) - if(I.on_found(user)) - return - if(user.s_active) - user.s_active.hide_from(user) - - var/client/C = user.client - if(!C) - return - - if(storage_slots) - C.screen += src.boxes - create_slot_catchers() - C.screen += src.box_catchers - else - C.screen += src.storage_start - C.screen += src.storage_continue - C.screen += src.storage_end - - C.screen += src.closer - C.screen += src.contents - - user.s_active = src - LAZYDISTINCTADD(is_seeing,user) - -/obj/item/weapon/storage/proc/hide_from(mob/user as mob) - var/client/C = user.client - LAZYREMOVE(is_seeing,user) - - if(!C) - if(!LAZYLEN(is_seeing)) - clear_slot_catchers() - return - - if(storage_slots) - C.screen -= src.boxes - C.screen -= src.box_catchers - else - C.screen -= src.storage_start - C.screen -= src.storage_continue - C.screen -= src.storage_end - - C.screen -= src.closer - C.screen -= src.contents - - if(user.s_active == src) - user.s_active = null - - if(!LAZYLEN(is_seeing)) - clear_slot_catchers() - -/obj/item/weapon/storage/proc/open(mob/user as mob) - if (use_sound) - playsound(src, src.use_sound, 50, 0, -5) - - orient2hud(user) - if(user.s_active) - user.s_active.close(user) - show_to(user) - -/obj/item/weapon/storage/proc/close(mob/user as mob) - src.hide_from(user) - user.s_active = null - return - -/obj/item/weapon/storage/proc/close_all() - for(var/mob/M in can_see_contents()) - close(M) - . = 1 - -/obj/item/weapon/storage/proc/can_see_contents() - var/list/cansee = list() - for(var/mob/M in is_seeing) - if(M.s_active == src && M.client) - cansee |= M - else - LAZYREMOVE(is_seeing,M) - return cansee - -/obj/item/weapon/storage/proc/create_slot_catchers() - clear_slot_catchers() - var/list/new_catchers = list() - for(var/obj/item/I in contents) - var/atom/movable/storage_slot/SS = new(null, I) - SS.screen_loc = I.screen_loc - SS.mouse_opacity = MOUSE_OPACITY_OPAQUE - new_catchers += SS - box_catchers = new_catchers - -/obj/item/weapon/storage/proc/clear_slot_catchers() - if(box_catchers) - for(var/mob/M in is_seeing) - M.client?.screen -= box_catchers - QDEL_LIST_NULL(box_catchers) - -//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right. -//The numbers are calculated from the bottom-left The bottom-left slot being 1,1. -/obj/item/weapon/storage/proc/orient_objs(tx, ty, mx, my) - var/cx = tx - var/cy = ty - src.boxes.screen_loc = "[tx]:,[ty] to [mx],[my]" - for(var/obj/O in src.contents) - O.screen_loc = "[cx],[cy]" - O.hud_layerise() - cx++ - if (cx > mx) - cx = tx - cy-- - src.closer.screen_loc = "[mx+1],[my]" - return - -//This proc draws out the inventory and places the items on it. It uses the standard position. -/obj/item/weapon/storage/proc/slot_orient_objs(var/rows, var/cols, var/list/obj/item/display_contents) - var/cx = 4 - var/cy = 2+rows - src.boxes.screen_loc = "4:16,2:16 to [4+cols]:16,[2+rows]:16" - - if(display_contents_with_number) - for(var/datum/numbered_display/ND in display_contents) - ND.sample_object.screen_loc = "[cx]:16,[cy]:16" - ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" - ND.sample_object.hud_layerise() - var/atom/movable/storage_slot/SS = new(null, ND.sample_object) - SS.screen_loc = ND.sample_object.screen_loc - SS.mouse_opacity = MOUSE_OPACITY_OPAQUE - cx++ - if (cx > (4+cols)) - cx = 4 - cy-- - else - for(var/obj/O in contents) - O.screen_loc = "[cx]:16,[cy]:16" - O.maptext = "" - O.hud_layerise() - var/atom/movable/storage_slot/SS = new(null, O) - SS.screen_loc = O.screen_loc - SS.mouse_opacity = MOUSE_OPACITY_OPAQUE - cx++ - if (cx > (4+cols)) - cx = 4 - cy-- - src.closer.screen_loc = "[4+cols+1]:16,2:16" - return - -/obj/item/weapon/storage/proc/space_orient_objs(var/list/obj/item/display_contents) - SHOULD_NOT_SLEEP(TRUE) - - /// A prototype for drawing the leftmost border behind each item in storage - var/static/mutable_appearance/stored_start - /// A prototype for drawing the wide backing space behind each item in storage - var/static/mutable_appearance/stored_continue - /// A prototype for drawing the rightmost border behind each item in storage - var/static/mutable_appearance/stored_end - - if(!stored_start) - // Because these are static and manipulated all the time by different storages, you'd better make 100000% sure this proc never sleeps - stored_start = mutable_appearance(icon = 'icons/mob/screen1.dmi', icon_state = "stored_start", layer = 0.1, plane = PLANE_PLAYER_HUD_ITEMS) - stored_continue = mutable_appearance(icon = 'icons/mob/screen1.dmi', icon_state = "stored_continue", layer = 0.1, plane = PLANE_PLAYER_HUD_ITEMS) - stored_end = mutable_appearance(icon = 'icons/mob/screen1.dmi', icon_state = "stored_end", layer = 0.1, plane = PLANE_PLAYER_HUD_ITEMS) - - var/baseline_max_storage_space = INVENTORY_STANDARD_SPACE / 2 //should be equal to default backpack capacity // This is a lie. - // Above var is misleading, what it does upon changing is makes smaller inventory sizes have smaller space on the UI. - // It's cut in half because otherwise boxes of IDs and other tiny items are unbearably cluttered. - - var/storage_cap_width = 2 //length of sprite for start and end of the box representing total storage space - var/stored_cap_width = 4 //length of sprite for start and end of the box representing the stored item - var/storage_width = min( round( 224 * max_storage_space/baseline_max_storage_space ,1) ,274) //length of sprite for the box representing total storage space - - QDEL_LIST_NULL(storage_start.vis_contents) - - var/matrix/M = matrix() - M.Scale((storage_width-storage_cap_width*2+3)/32,1) - src.storage_continue.transform = M - - src.storage_start.screen_loc = "4:16,2:16" - src.storage_continue.screen_loc = "4:[storage_cap_width+(storage_width-storage_cap_width*2)/2+2],2:16" - src.storage_end.screen_loc = "4:[19+storage_width-storage_cap_width],2:16" - - var/startpoint = 0 - var/endpoint = 1 - - for(var/obj/item/O in contents) - var/atom/movable/storage_slot/SS = new(null, O) - startpoint = endpoint + 1 - endpoint += storage_width * O.get_storage_cost()/max_storage_space - - var/matrix/M_start = matrix() - var/matrix/M_continue = matrix() - var/matrix/M_end = matrix() - M_start.Translate(startpoint,0) - M_continue.Scale((endpoint-startpoint-stored_cap_width*2)/32,1) - M_continue.Translate(startpoint+stored_cap_width+(endpoint-startpoint-stored_cap_width*2)/2 - 16,0) - M_end.Translate(endpoint-stored_cap_width,0) - stored_start.transform = M_start - stored_continue.transform = M_continue - stored_end.transform = M_end - SS.add_overlay(list(stored_start, stored_continue, stored_end)) - - O.screen_loc = "4:[round((startpoint+endpoint)/2)+2],2:16" - O.maptext = "" - O.hud_layerise() - storage_start.vis_contents += SS - - src.closer.screen_loc = "4:[storage_width+19],2:16" - return - -/datum/numbered_display - var/obj/item/sample_object - var/number - -/datum/numbered_display/New(obj/item/sample as obj) - if(!istype(sample)) - qdel(src) - sample_object = sample - number = 1 - -//This proc determins the size of the inventory to be displayed. Please touch it only if you know what you're doing. -/obj/item/weapon/storage/proc/orient2hud(mob/user as mob) - - var/adjusted_contents = contents.len - - //Numbered contents display - var/list/datum/numbered_display/numbered_contents - if(display_contents_with_number) - numbered_contents = list() - adjusted_contents = 0 - for(var/obj/item/I in contents) - var/found = 0 - for(var/datum/numbered_display/ND in numbered_contents) - if(ND.sample_object.type == I.type) - ND.number++ - found = 1 - break - if(!found) - adjusted_contents++ - numbered_contents.Add( new/datum/numbered_display(I) ) - - if(storage_slots == null) - src.space_orient_objs(numbered_contents) - else - var/row_num = 0 - var/col_count = min(7,storage_slots) -1 - if (adjusted_contents > 7) - row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. - src.slot_orient_objs(row_num, col_count, numbered_contents) - return - -//This proc return 1 if the item can be picked up and 0 if it can't. -//Set the stop_messages to stop it from printing messages -/obj/item/weapon/storage/proc/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!istype(W)) return //Not an item - - if(usr && usr.isEquipped(W) && !usr.canUnEquip(W)) - return 0 - - if(src.loc == W) - return 0 //Means the item is already in the storage item - if(storage_slots != null && contents.len >= storage_slots) - if(!stop_messages) - to_chat(usr, "[src] is full, make some space.") - return 0 //Storage item is full - - if(LAZYLEN(can_hold) && !is_type_in_list(W, can_hold)) - if(!stop_messages) - if (istype(W, /obj/item/weapon/hand_labeler)) - return 0 - to_chat(usr, "[src] cannot hold [W].") - return 0 - - if(LAZYLEN(cant_hold) && is_type_in_list(W, cant_hold)) - if(!stop_messages) - to_chat(usr, "[src] cannot hold [W].") - return 0 - - if (max_w_class != null && W.w_class > max_w_class) - if(!stop_messages) - to_chat(usr, "[W] is too long for \the [src].") - return 0 - - var/total_storage_space = W.get_storage_cost() - for(var/obj/item/I in contents) - total_storage_space += I.get_storage_cost() //Adds up the combined w_classes which will be in the storage item if the item is added to it. - - if(total_storage_space > max_storage_space) - if(!stop_messages) - to_chat(usr, "[src] is too full, make some space.") - return 0 - - if(W.w_class >= src.w_class && (istype(W, /obj/item/weapon/storage))) - if(!stop_messages) - to_chat(usr, "[src] cannot hold [W] as it's a storage item of the same size.") - return 0 //To prevent the stacking of same sized storage items. - - return 1 - -//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted() -//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once, -//such as when picking up all the items on a tile with one click. -/obj/item/weapon/storage/proc/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) - if(!istype(W)) return 0 - - if(!stall_insertion(W, usr)) // Can sleep here and delay removal for slow storage - return 0 - - if(usr) - usr.remove_from_mob(W,target = src) //If given a target, handles forceMove() - W.on_enter_storage(src) - if (usr.client && usr.s_active != src) - usr.client.screen -= W - W.dropped(usr) - add_fingerprint(usr) - if (use_sound) - playsound(src, src.use_sound, 50, 0, -5) //Something broke "add item to container" sounds, this is a hacky fix. - - if(!prevent_warning) - for(var/mob/M in viewers(usr, null)) - if (M == usr) - to_chat(usr, "You put \the [W] into [src].") - else if (M in range(1)) //If someone is standing close enough, they can tell what it is... - M.show_message("\The [usr] puts [W] into [src].") - else if (W && W.w_class >= 3) //Otherwise they can only see large or normal items from a distance... - M.show_message("\The [usr] puts [W] into [src].") - - src.orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - else - W.forceMove(src) - W.on_enter_storage(src) - - update_icon() - return 1 - -//Call this proc to handle the removal of an item from the storage item. The item will be moved to the atom sent as new_target -/obj/item/weapon/storage/proc/remove_from_storage(obj/item/W as obj, atom/new_location) - if(!istype(W)) return 0 - - if(!stall_removal(W, usr)) // Can sleep here and delay removal for slow storage - return 0 - - if(istype(src, /obj/item/weapon/storage/fancy)) - var/obj/item/weapon/storage/fancy/F = src - F.update_icon(1) - - for(var/mob/M in is_seeing) - if(!M.client || QDELETED(M)) - hide_from(M) - else - M.client.screen -= W - - if(new_location) - if(ismob(loc)) - W.dropped(usr) - if(ismob(new_location)) - W.hud_layerise() - else - W.reset_plane_and_layer() - W.forceMove(new_location) - else - W.forceMove(get_turf(src)) - - for(var/mob/M in is_seeing) - if(M.s_active == src) - orient2hud(M) - show_to(M) - if(W.maptext) - W.maptext = "" - W.on_exit_storage(src) - update_icon() - return 1 - -/// Called before insertion completes, allowing you to delay or cancel it -/obj/item/weapon/storage/proc/stall_insertion(obj/item/W, mob/user) - return TRUE - -/// Called before removal completes, allowing you to delay or cancel it -/obj/item/weapon/storage/proc/stall_removal(obj/item/W, mob/user) - return TRUE - -//This proc is called when you want to place an item into the storage item. -/obj/item/weapon/storage/attackby(obj/item/W as obj, mob/user as mob) - ..() - - if(isrobot(user)) - return //Robots can't interact with storage items. - - if(istype(W, /obj/item/device/lightreplacer)) - var/obj/item/device/lightreplacer/LP = W - var/amt_inserted = 0 - var/turf/T = get_turf(user) - for(var/obj/item/weapon/light/L in src.contents) - if(L.status == 0) - if(LP.uses < LP.max_uses) - LP.add_uses(1) - amt_inserted++ - remove_from_storage(L, T) - qdel(L) - if(amt_inserted) - to_chat(user, "You inserted [amt_inserted] light\s into \the [LP.name]. You have [LP.uses] light\s remaining.") - return - - if(!can_be_inserted(W)) - return - - if(istype(W, /obj/item/weapon/tray)) - var/obj/item/weapon/tray/T = W - if(T.calc_carry() > 0) - if(prob(85)) - to_chat(user, "The tray won't fit in [src].") - return - else - W.forceMove(get_turf(user)) - if ((user.client && user.s_active != src)) - user.client.screen -= W - W.dropped(user) - to_chat(user, "God damn it!") - - W.add_fingerprint(user) - return handle_item_insertion(W) - -/obj/item/weapon/storage/dropped(mob/user as mob) - return - -/obj/item/weapon/storage/attack_hand(mob/user as mob) - if(ishuman(user) && !pocketable) - var/mob/living/carbon/human/H = user - if(H.l_store == src && !H.get_active_hand()) //Prevents opening if it's in a pocket. - H.put_in_hands(src) - H.l_store = null - return - if(H.r_store == src && !H.get_active_hand()) - H.put_in_hands(src) - H.r_store = null - return - - if (src.loc == user) - src.open(user) - else - ..() - for(var/mob/M in range(1)) - if (M.s_active == src) - src.close(M) - src.add_fingerprint(user) - return - -/obj/item/weapon/storage/proc/gather_all(turf/T as turf, mob/user as mob) - var/list/rejections = list() - var/success = 0 - var/failure = 0 - - for(var/obj/item/I in T) - if(I.type in rejections) // To limit bag spamming: any given type only complains once - continue - if(!can_be_inserted(I, user)) // Note can_be_inserted still makes noise when the answer is no - rejections += I.type // therefore full bags are still a little spammy - failure = 1 - continue - success = 1 - handle_item_insertion(I, 1) //The 1 stops the "You put the [src] into [S]" insertion message from being displayed. - if(success && !failure) - to_chat(user, "You put everything in [src].") - else if(success) - to_chat(user, "You put some things in [src].") - else - to_chat(user, "You fail to pick anything up with \the [src].") - -/obj/item/weapon/storage/verb/toggle_gathering_mode() - set name = "Switch Gathering Method" - set category = "Object" - - collection_mode = !collection_mode - switch (collection_mode) - if(1) - to_chat(usr, "[src] now picks up all items on a tile at once.") - if(0) - to_chat(usr, "[src] now picks up one item at a time.") - - -/obj/item/weapon/storage/verb/quick_empty() - set name = "Empty Contents" - set category = "Object" - set src in view(1) - - // Only humans and robots can dump contents - if(!(ishuman(usr) || isrobot(usr))) - return - - // Hard to do when you're KO'd - if(usr.incapacitated()) - return - - // Has to be at least adjacent (just for safety, src in view should handle this already) - if(!Adjacent(usr)) - return - - //VOREStation Add: No turf dumping if user is in a belly - if(isbelly(usr.loc)) - return - - drop_contents() - -/obj/item/weapon/storage/proc/drop_contents() // why is this a proc? literally just for RPEDs - hide_from(usr) - var/turf/T = get_turf(src) - for(var/obj/item/I in contents) - remove_from_storage(I, T) - -/obj/item/weapon/storage/proc/calibrate_size() - var/total_storage_space = 0 - for(var/obj/item/I in contents) - total_storage_space += I.get_storage_cost() - max_storage_space = max(total_storage_space,max_storage_space) //Prevents spawned containers from being too small for their contents. - -/obj/item/weapon/storage/emp_act(severity) - if(!istype(src.loc, /mob/living)) - for(var/obj/O in contents) - O.emp_act(severity) - ..() - -/obj/item/weapon/storage/attack_self(mob/user as mob) - if((user.get_active_hand() == src) || (isrobot(user)) && allow_quick_empty) - if(src.verbs.Find(/obj/item/weapon/storage/verb/quick_empty)) - src.quick_empty() - return 1 - -//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). -//Returns -1 if the atom was not found on container. -/atom/proc/storage_depth(atom/container) - var/depth = 0 - var/atom/cur_atom = src - - while (cur_atom && !(cur_atom in container.contents)) - if (isarea(cur_atom)) - return -1 - if (istype(cur_atom.loc, /obj/item/weapon/storage)) - depth++ - cur_atom = cur_atom.loc - - if (!cur_atom) - return -1 //inside something with a null loc. - - return depth - -//Like storage depth, but returns the depth to the nearest turf -//Returns -1 if no top level turf (a loc was null somewhere, or a non-turf atom's loc was an area somehow). -/atom/proc/storage_depth_turf() - var/depth = 0 - var/atom/cur_atom = src - - while (cur_atom && !isturf(cur_atom)) - if (isarea(cur_atom)) - return -1 - if (istype(cur_atom.loc, /obj/item/weapon/storage)) - depth++ - cur_atom = cur_atom.loc - - if (!cur_atom) - return -1 //inside something with a null loc. - - return depth - -// See inventory_sizes.dm for the defines. -/obj/item/proc/get_storage_cost() - if (storage_cost) - return storage_cost - else - switch(w_class) - if(ITEMSIZE_TINY) - return ITEMSIZE_COST_TINY - if(ITEMSIZE_SMALL) - return ITEMSIZE_COST_SMALL - if(ITEMSIZE_NORMAL) - return ITEMSIZE_COST_NORMAL - if(ITEMSIZE_LARGE) - return ITEMSIZE_COST_LARGE - if(ITEMSIZE_HUGE) - return ITEMSIZE_COST_HUGE - else - return ITEMSIZE_COST_NO_CONTAINER - -/obj/item/weapon/storage/proc/make_exact_fit() - storage_slots = contents.len - - LAZYCLEARLIST(can_hold) - can_hold = list() - max_w_class = 0 - max_storage_space = 0 - for(var/obj/item/I in src) - can_hold[I.type]++ - max_w_class = max(I.w_class, max_w_class) - max_storage_space += I.get_storage_cost() - -/* - * Trinket Box - READDING SOON - */ -/obj/item/weapon/storage/trinketbox - name = "trinket box" - desc = "A box that can hold small trinkets, such as a ring." - icon = 'icons/obj/items.dmi' - icon_state = "trinketbox" - var/open = 0 - storage_slots = 1 - can_hold = list( - /obj/item/clothing/gloves/ring, - /obj/item/weapon/coin, - /obj/item/clothing/accessory/medal - ) - var/open_state - var/closed_state - -/obj/item/weapon/storage/trinketbox/update_icon() - cut_overlays() - if(open) - icon_state = open_state - - if(contents.len >= 1) - var/contained_image = null - if(istype(contents[1], /obj/item/clothing/gloves/ring)) - contained_image = "ring_trinket" - else if(istype(contents[1], /obj/item/weapon/coin)) - contained_image = "coin_trinket" - else if(istype(contents[1], /obj/item/clothing/accessory/medal)) - contained_image = "medal_trinket" - if(contained_image) - add_overlay(contained_image) - else - icon_state = closed_state - -/obj/item/weapon/storage/trinketbox/New() - if(!open_state) - open_state = "[initial(icon_state)]_open" - if(!closed_state) - closed_state = "[initial(icon_state)]" - ..() - -/obj/item/weapon/storage/trinketbox/attack_self() - open = !open - update_icon() - ..() - -/obj/item/weapon/storage/trinketbox/examine(mob/user) - . = ..() - if(open && contents.len) - var/display_item = contents[1] - . += "\The [src] contains \the [display_item]!" - -/obj/item/weapon/storage/AllowDrop() - return TRUE - -//Useful for spilling the contents of containers all over the floor -/obj/item/weapon/storage/proc/spill(var/dist = 2, var/turf/T = null) - if (!istype(T))//If its not on the floor this might cause issues - T = get_turf(src) - - for (var/obj/O in contents) - remove_from_storage(O, T) - O.tumble(2) - -/// Hangar for storage backdrops, so that we can redirect clicks from them to our item for QOL -/atom/movable/storage_slot - name = "stored - " - icon = 'icons/effects/effects.dmi' - icon_state = "nothing" - plane = PLANE_PLAYER_HUD_ITEMS - layer = 0.1 - alpha = 200 - var/datum/weakref/held_item - -/atom/movable/storage_slot/New(newloc, obj/item/held_item) - ASSERT(held_item) - name += held_item.name - src.held_item = WEAKREF(held_item) - -/atom/movable/storage_slot/Destroy() - held_item = null - -/// Has to be this way. The fact that the overlays will be constantly mutated by other storage means we can't wait. -/atom/movable/storage_slot/add_overlay(list/somethings) - ASSERT(islist(somethings)) - overlays = somethings - -/atom/movable/storage_slot/Click() - var/obj/item/I = held_item?.resolve() - if(I) - usr.ClickOn(I) - return 1 - -// Allows micros to drag themselves into storage items -/obj/item/weapon/storage/MouseDrop_T(mob/living/target, mob/living/user) - if(!istype(user)) return // If the user passed in isn't a living mob, exit - if(target != user) return // If the user didn't drag themselves, exit - if(user.incapacitated() || user.buckled) return // If user is incapacitated or buckled, exit - if(get_holder_of_type(src, /mob/living/carbon/human) == user) return // No jumping into your own equipment - if(ishuman(user) && user.get_effective_size(TRUE) > 0.25) return // Only micro characters - if(ismouse(user) && user.get_effective_size(TRUE) > 1) return // Only normal sized mice or less - - // Create a dummy holder with user's size to test insertion - var/obj/item/weapon/holder/D = new/obj/item/weapon/holder - if(ismouse(user)) - D.w_class = ITEMSIZE_TINY // Mouse smol - else if(ishuman(user)) - D.w_class = ITEMSIZE_SMALL // Players small - else // Other creatures not accepted at this time - qdel(D) // If there's a better way to check the size of a - return // mob's holder and if it fits, replace this slab - if(!src.can_be_inserted(D, 1)) // If the dummy item doesn't fit, exit - qdel(D) - return - qdel(D) - - // Scoop and insert target into storage - var/obj/item/weapon/holder/H = new user.holder_type(get_turf(user), user) - src.handle_item_insertion(H, 1) - to_chat(user, "You climb into \the [src].") - return ..() +// To clarify: +// For use_to_pickup and allow_quick_gather functionality, +// see item/attackby() (/game/objects/items.dm) +// Do not remove this functionality without good reason, cough reagent_containers cough. +// -Sayu + + +/obj/item/weapon/storage + name = "storage" + icon = 'icons/obj/storage.dmi' + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', + ) + w_class = ITEMSIZE_NORMAL + show_messages = 1 + + /// List of objects which this item can store (if set, it can't store anything else) + var/list/can_hold + /// List of objects which this item can't store (in effect only if can_hold isn't set) + var/list/cant_hold + /// List of mobs which are currently seeing the contents of this item's storage + var/list/is_seeing + + var/max_w_class = ITEMSIZE_SMALL //Max size of objects that this object can store (in effect only if can_hold isn't set) + var/max_storage_space = ITEMSIZE_COST_SMALL * 4 //The sum of the storage costs of all the items in this storage item. + var/storage_slots = null //The number of storage slots in this container. If null, it uses the volume-based storage instead. + + /// Boxes screen object for fixed-size storage (belts, etc) + var/obj/screen/storage/boxes = null + /// List of 'click catchers' for boxes for fixed-size storage + var/list/box_catchers = null + + /// For dynamic storage, the leftmost pixel column for the whole storage display. Most of the interesting effects are hung on this in vis_contents. + var/obj/screen/storage/storage_start = null + /// For dynamic storage, the majority of the width of the whole storage display. Decorative, but sized to the width appropriate to represent how much storage there is. + var/obj/screen/storage/storage_continue = null + /// For dynamic storage, the rightmost pixel column for the whole storage display. Decorative. + var/obj/screen/storage/storage_end = null + + /// The "X" button at the far right of the storage + var/obj/screen/close/closer = null + + var/use_to_pickup //Set this to make it possible to use this item in an inverse way, so you can have the item in your hand and click items on the floor to pick them up. + var/display_contents_with_number //Set this to make the storage item group contents of the same type and display them as a number. + var/allow_quick_empty //Set this variable to allow the object to have the 'empty' verb, which dumps all the contents on the floor. + var/allow_quick_gather //Set this variable to allow the object to have the 'toggle mode' verb, which quickly collects all items from a tile. + var/collection_mode = 1; //0 = pick one at a time, 1 = pick all on tile + var/use_sound = "rustle" //sound played when used. null for no sound. + var/list/starts_with //Things to spawn on the box on spawn + var/empty //Mapper override to spawn an empty version of a container that usually has stuff + /// If you can use this storage while in a pocket + var/pocketable = FALSE + +/obj/item/weapon/storage/Initialize() + . = ..() + + if(allow_quick_empty) + verbs += /obj/item/weapon/storage/verb/quick_empty + else + verbs -= /obj/item/weapon/storage/verb/quick_empty + + if(allow_quick_gather) + verbs += /obj/item/weapon/storage/verb/toggle_gathering_mode + else + verbs -= /obj/item/weapon/storage/verb/toggle_gathering_mode + + if(storage_slots) + src.boxes = new /obj/screen/storage( ) + src.boxes.name = "storage" + src.boxes.master = src + src.boxes.icon_state = "block" + src.boxes.screen_loc = "7,7 to 10,8" + else + src.storage_start = new /obj/screen/storage( ) + src.storage_start.name = "storage" + src.storage_start.master = src + src.storage_start.icon_state = "storage_start" + src.storage_start.screen_loc = "7,7 to 10,8" + + src.storage_continue = new /obj/screen/storage( ) + src.storage_continue.name = "storage" + src.storage_continue.master = src + src.storage_continue.icon_state = "storage_continue" + src.storage_continue.screen_loc = "7,7 to 10,8" + + src.storage_end = new /obj/screen/storage( ) + src.storage_end.name = "storage" + src.storage_end.master = src + src.storage_end.icon_state = "storage_end" + src.storage_end.screen_loc = "7,7 to 10,8" + + src.closer = new /obj/screen/close( ) + src.closer.master = src + src.closer.icon_state = "storage_close" + src.closer.hud_layerise() + orient2hud() + + if(LAZYLEN(starts_with) && !empty) + for(var/newtype in starts_with) + var/count = starts_with[newtype] || 1 //Could have left it blank. + while(count) + count-- + new newtype(src) + starts_with = null //Reduce list count. + update_icon() + + calibrate_size() + +/obj/item/weapon/storage/Destroy() + close_all() + clear_slot_catchers() + QDEL_NULL(boxes) + QDEL_NULL(storage_start) + QDEL_NULL(storage_continue) + QDEL_NULL(storage_end) + QDEL_NULL(closer) + + if(ismob(loc)) + var/mob/M = loc + M.remove_from_mob(src) + + . = ..() + +/obj/item/weapon/storage/MouseDrop(obj/over_object as obj) + if(!canremove) + return + + if (isliving(usr) || isobserver(usr)) + + if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? + return + + if(over_object == usr && Adjacent(usr)) // this must come before the screen objects only block + src.open(usr) + return + + if (!( istype(over_object, /obj/screen) )) + return ..() + + //makes sure that the storage is equipped, so that we can't drag it into our hand from miles away. + //there's got to be a better way of doing this. + if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) + return + + if (( usr.restrained() ) || ( usr.stat )) + return + + if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) + return + + switch(over_object.name) + if("r_hand") + usr.unEquip(src) + usr.put_in_r_hand(src) + if("l_hand") + usr.unEquip(src) + usr.put_in_l_hand(src) + src.add_fingerprint(usr) + +/obj/item/weapon/storage/AltClick(mob/user) + if(user in is_seeing) + src.close(user) + // I would think there should be some incap check here or something + // But MouseDrop doesn't use one (as of this writing), so... + else if(isliving(user) && Adjacent(user)) + src.open(user) + else + return ..() + +/obj/item/weapon/storage/proc/return_inv() + + var/list/L = list( ) + + L += src.contents + + for(var/obj/item/weapon/storage/S in src) + L += S.return_inv() + for(var/obj/item/weapon/gift/G in src) + L += G.gift + if (istype(G.gift, /obj/item/weapon/storage)) + L += G.gift:return_inv() + return L + +/obj/item/weapon/storage/proc/show_to(mob/user as mob) + if(user.s_active != src) + for(var/obj/item/I in src) + if(I.on_found(user)) + return + if(user.s_active) + user.s_active.hide_from(user) + + var/client/C = user.client + if(!C) + return + + if(storage_slots) + C.screen += src.boxes + create_slot_catchers() + C.screen += src.box_catchers + else + C.screen += src.storage_start + C.screen += src.storage_continue + C.screen += src.storage_end + + C.screen += src.closer + C.screen += src.contents + + user.s_active = src + LAZYDISTINCTADD(is_seeing,user) + +/obj/item/weapon/storage/proc/hide_from(mob/user as mob) + var/client/C = user.client + LAZYREMOVE(is_seeing,user) + + if(!C) + if(!LAZYLEN(is_seeing)) + clear_slot_catchers() + return + + if(storage_slots) + C.screen -= src.boxes + C.screen -= src.box_catchers + else + C.screen -= src.storage_start + C.screen -= src.storage_continue + C.screen -= src.storage_end + + C.screen -= src.closer + C.screen -= src.contents + + if(user.s_active == src) + user.s_active = null + + if(!LAZYLEN(is_seeing)) + clear_slot_catchers() + +/obj/item/weapon/storage/proc/open(mob/user as mob) + if (use_sound) + playsound(src, src.use_sound, 50, 0, -5) + + orient2hud(user) + if(user.s_active) + user.s_active.close(user) + show_to(user) + +/obj/item/weapon/storage/proc/close(mob/user as mob) + src.hide_from(user) + user.s_active = null + return + +/obj/item/weapon/storage/proc/close_all() + for(var/mob/M in can_see_contents()) + close(M) + . = 1 + +/obj/item/weapon/storage/proc/can_see_contents() + var/list/cansee = list() + for(var/mob/M in is_seeing) + if(M.s_active == src && M.client) + cansee |= M + else + LAZYREMOVE(is_seeing,M) + return cansee + +/obj/item/weapon/storage/proc/create_slot_catchers() + clear_slot_catchers() + var/list/new_catchers = list() + for(var/obj/item/I in contents) + var/atom/movable/storage_slot/SS = new(null, I) + SS.screen_loc = I.screen_loc + SS.mouse_opacity = MOUSE_OPACITY_OPAQUE + new_catchers += SS + box_catchers = new_catchers + +/obj/item/weapon/storage/proc/clear_slot_catchers() + if(box_catchers) + for(var/mob/M in is_seeing) + M.client?.screen -= box_catchers + QDEL_LIST_NULL(box_catchers) + +//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right. +//The numbers are calculated from the bottom-left The bottom-left slot being 1,1. +/obj/item/weapon/storage/proc/orient_objs(tx, ty, mx, my) + var/cx = tx + var/cy = ty + src.boxes.screen_loc = "[tx]:,[ty] to [mx],[my]" + for(var/obj/O in src.contents) + O.screen_loc = "[cx],[cy]" + O.hud_layerise() + cx++ + if (cx > mx) + cx = tx + cy-- + src.closer.screen_loc = "[mx+1],[my]" + return + +//This proc draws out the inventory and places the items on it. It uses the standard position. +/obj/item/weapon/storage/proc/slot_orient_objs(var/rows, var/cols, var/list/obj/item/display_contents) + var/cx = 4 + var/cy = 2+rows + src.boxes.screen_loc = "4:16,2:16 to [4+cols]:16,[2+rows]:16" + + if(display_contents_with_number) + for(var/datum/numbered_display/ND in display_contents) + ND.sample_object.screen_loc = "[cx]:16,[cy]:16" + ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" + ND.sample_object.hud_layerise() + var/atom/movable/storage_slot/SS = new(null, ND.sample_object) + SS.screen_loc = ND.sample_object.screen_loc + SS.mouse_opacity = MOUSE_OPACITY_OPAQUE + cx++ + if (cx > (4+cols)) + cx = 4 + cy-- + else + for(var/obj/O in contents) + O.screen_loc = "[cx]:16,[cy]:16" + O.maptext = "" + O.hud_layerise() + var/atom/movable/storage_slot/SS = new(null, O) + SS.screen_loc = O.screen_loc + SS.mouse_opacity = MOUSE_OPACITY_OPAQUE + cx++ + if (cx > (4+cols)) + cx = 4 + cy-- + src.closer.screen_loc = "[4+cols+1]:16,2:16" + return + +/obj/item/weapon/storage/proc/space_orient_objs(var/list/obj/item/display_contents) + SHOULD_NOT_SLEEP(TRUE) + + /// A prototype for drawing the leftmost border behind each item in storage + var/static/mutable_appearance/stored_start + /// A prototype for drawing the wide backing space behind each item in storage + var/static/mutable_appearance/stored_continue + /// A prototype for drawing the rightmost border behind each item in storage + var/static/mutable_appearance/stored_end + + if(!stored_start) + // Because these are static and manipulated all the time by different storages, you'd better make 100000% sure this proc never sleeps + stored_start = mutable_appearance(icon = 'icons/mob/screen1.dmi', icon_state = "stored_start", layer = 0.1, plane = PLANE_PLAYER_HUD_ITEMS) + stored_continue = mutable_appearance(icon = 'icons/mob/screen1.dmi', icon_state = "stored_continue", layer = 0.1, plane = PLANE_PLAYER_HUD_ITEMS) + stored_end = mutable_appearance(icon = 'icons/mob/screen1.dmi', icon_state = "stored_end", layer = 0.1, plane = PLANE_PLAYER_HUD_ITEMS) + + var/baseline_max_storage_space = INVENTORY_STANDARD_SPACE / 2 //should be equal to default backpack capacity // This is a lie. + // Above var is misleading, what it does upon changing is makes smaller inventory sizes have smaller space on the UI. + // It's cut in half because otherwise boxes of IDs and other tiny items are unbearably cluttered. + + var/storage_cap_width = 2 //length of sprite for start and end of the box representing total storage space + var/stored_cap_width = 4 //length of sprite for start and end of the box representing the stored item + var/storage_width = min( round( 224 * max_storage_space/baseline_max_storage_space ,1) ,274) //length of sprite for the box representing total storage space + + QDEL_LIST_NULL(storage_start.vis_contents) + + var/matrix/M = matrix() + M.Scale((storage_width-storage_cap_width*2+3)/32,1) + src.storage_continue.transform = M + + src.storage_start.screen_loc = "4:16,2:16" + src.storage_continue.screen_loc = "4:[storage_cap_width+(storage_width-storage_cap_width*2)/2+2],2:16" + src.storage_end.screen_loc = "4:[19+storage_width-storage_cap_width],2:16" + + var/startpoint = 0 + var/endpoint = 1 + + for(var/obj/item/O in contents) + var/atom/movable/storage_slot/SS = new(null, O) + startpoint = endpoint + 1 + endpoint += storage_width * O.get_storage_cost()/max_storage_space + + var/matrix/M_start = matrix() + var/matrix/M_continue = matrix() + var/matrix/M_end = matrix() + M_start.Translate(startpoint,0) + M_continue.Scale((endpoint-startpoint-stored_cap_width*2)/32,1) + M_continue.Translate(startpoint+stored_cap_width+(endpoint-startpoint-stored_cap_width*2)/2 - 16,0) + M_end.Translate(endpoint-stored_cap_width,0) + stored_start.transform = M_start + stored_continue.transform = M_continue + stored_end.transform = M_end + SS.add_overlay(list(stored_start, stored_continue, stored_end)) + + O.screen_loc = "4:[round((startpoint+endpoint)/2)+2],2:16" + O.maptext = "" + O.hud_layerise() + storage_start.vis_contents += SS + + src.closer.screen_loc = "4:[storage_width+19],2:16" + return + +/datum/numbered_display + var/obj/item/sample_object + var/number + +/datum/numbered_display/New(obj/item/sample as obj) + if(!istype(sample)) + qdel(src) + sample_object = sample + number = 1 + +//This proc determins the size of the inventory to be displayed. Please touch it only if you know what you're doing. +/obj/item/weapon/storage/proc/orient2hud(mob/user as mob) + + var/adjusted_contents = contents.len + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_contents_with_number) + numbered_contents = list() + adjusted_contents = 0 + for(var/obj/item/I in contents) + var/found = 0 + for(var/datum/numbered_display/ND in numbered_contents) + if(ND.sample_object.type == I.type) + ND.number++ + found = 1 + break + if(!found) + adjusted_contents++ + numbered_contents.Add( new/datum/numbered_display(I) ) + + if(storage_slots == null) + src.space_orient_objs(numbered_contents) + else + var/row_num = 0 + var/col_count = min(7,storage_slots) -1 + if (adjusted_contents > 7) + row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. + src.slot_orient_objs(row_num, col_count, numbered_contents) + return + +//This proc return 1 if the item can be picked up and 0 if it can't. +//Set the stop_messages to stop it from printing messages +/obj/item/weapon/storage/proc/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!istype(W)) return //Not an item + + if(usr && usr.isEquipped(W) && !usr.canUnEquip(W)) + return 0 + + if(src.loc == W) + return 0 //Means the item is already in the storage item + if(storage_slots != null && contents.len >= storage_slots) + if(!stop_messages) + to_chat(usr, "[src] is full, make some space.") + return 0 //Storage item is full + + if(LAZYLEN(can_hold) && !is_type_in_list(W, can_hold)) + if(!stop_messages) + if (istype(W, /obj/item/weapon/hand_labeler)) + return 0 + to_chat(usr, "[src] cannot hold [W].") + return 0 + + if(LAZYLEN(cant_hold) && is_type_in_list(W, cant_hold)) + if(!stop_messages) + to_chat(usr, "[src] cannot hold [W].") + return 0 + + if (max_w_class != null && W.w_class > max_w_class) + if(!stop_messages) + to_chat(usr, "[W] is too long for \the [src].") + return 0 + + var/total_storage_space = W.get_storage_cost() + for(var/obj/item/I in contents) + total_storage_space += I.get_storage_cost() //Adds up the combined w_classes which will be in the storage item if the item is added to it. + + if(total_storage_space > max_storage_space) + if(!stop_messages) + to_chat(usr, "[src] is too full, make some space.") + return 0 + + if(W.w_class >= src.w_class && (istype(W, /obj/item/weapon/storage))) + if(!stop_messages) + to_chat(usr, "[src] cannot hold [W] as it's a storage item of the same size.") + return 0 //To prevent the stacking of same sized storage items. + + return 1 + +//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted() +//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once, +//such as when picking up all the items on a tile with one click. +/obj/item/weapon/storage/proc/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) + if(!istype(W)) return 0 + + if(!stall_insertion(W, usr)) // Can sleep here and delay removal for slow storage + return 0 + + if(usr) + usr.remove_from_mob(W,target = src) //If given a target, handles forceMove() + W.on_enter_storage(src) + if (usr.client && usr.s_active != src) + usr.client.screen -= W + W.dropped(usr) + add_fingerprint(usr) + if (use_sound) + playsound(src, src.use_sound, 50, 0, -5) //Something broke "add item to container" sounds, this is a hacky fix. + + if(!prevent_warning) + for(var/mob/M in viewers(usr, null)) + if (M == usr) + to_chat(usr, "You put \the [W] into [src].") + else if (M in range(1)) //If someone is standing close enough, they can tell what it is... + M.show_message("\The [usr] puts [W] into [src].") + else if (W && W.w_class >= 3) //Otherwise they can only see large or normal items from a distance... + M.show_message("\The [usr] puts [W] into [src].") + + src.orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + else + W.forceMove(src) + W.on_enter_storage(src) + + update_icon() + return 1 + +//Call this proc to handle the removal of an item from the storage item. The item will be moved to the atom sent as new_target +/obj/item/weapon/storage/proc/remove_from_storage(obj/item/W as obj, atom/new_location) + if(!istype(W)) return 0 + + if(!stall_removal(W, usr)) // Can sleep here and delay removal for slow storage + return 0 + + if(istype(src, /obj/item/weapon/storage/fancy)) + var/obj/item/weapon/storage/fancy/F = src + F.update_icon(1) + + for(var/mob/M in is_seeing) + if(!M.client || QDELETED(M)) + hide_from(M) + else + M.client.screen -= W + + if(new_location) + if(ismob(loc)) + W.dropped(usr) + if(ismob(new_location)) + W.hud_layerise() + else + W.reset_plane_and_layer() + W.forceMove(new_location) + else + W.forceMove(get_turf(src)) + + for(var/mob/M in is_seeing) + if(M.s_active == src) + orient2hud(M) + show_to(M) + if(W.maptext) + W.maptext = "" + W.on_exit_storage(src) + update_icon() + return 1 + +/// Called before insertion completes, allowing you to delay or cancel it +/obj/item/weapon/storage/proc/stall_insertion(obj/item/W, mob/user) + return TRUE + +/// Called before removal completes, allowing you to delay or cancel it +/obj/item/weapon/storage/proc/stall_removal(obj/item/W, mob/user) + return TRUE + +//This proc is called when you want to place an item into the storage item. +/obj/item/weapon/storage/attackby(obj/item/W as obj, mob/user as mob) + ..() + + if(isrobot(user)) + return //Robots can't interact with storage items. + + if(istype(W, /obj/item/device/lightreplacer)) + var/obj/item/device/lightreplacer/LP = W + var/amt_inserted = 0 + var/turf/T = get_turf(user) + for(var/obj/item/weapon/light/L in src.contents) + if(L.status == 0) + if(LP.uses < LP.max_uses) + LP.add_uses(1) + amt_inserted++ + remove_from_storage(L, T) + qdel(L) + if(amt_inserted) + to_chat(user, "You inserted [amt_inserted] light\s into \the [LP.name]. You have [LP.uses] light\s remaining.") + return + + if(!can_be_inserted(W)) + return + + if(istype(W, /obj/item/weapon/tray)) + var/obj/item/weapon/tray/T = W + if(T.calc_carry() > 0) + if(prob(85)) + to_chat(user, "The tray won't fit in [src].") + return + else + W.forceMove(get_turf(user)) + if ((user.client && user.s_active != src)) + user.client.screen -= W + W.dropped(user) + to_chat(user, "God damn it!") + + W.add_fingerprint(user) + return handle_item_insertion(W) + +/obj/item/weapon/storage/dropped(mob/user as mob) + return + +/obj/item/weapon/storage/attack_hand(mob/user as mob) + if(ishuman(user) && !pocketable) + var/mob/living/carbon/human/H = user + if(H.l_store == src && !H.get_active_hand()) //Prevents opening if it's in a pocket. + H.put_in_hands(src) + H.l_store = null + return + if(H.r_store == src && !H.get_active_hand()) + H.put_in_hands(src) + H.r_store = null + return + + if (src.loc == user) + src.open(user) + else + ..() + for(var/mob/M in range(1)) + if (M.s_active == src) + src.close(M) + src.add_fingerprint(user) + return + +/obj/item/weapon/storage/proc/gather_all(turf/T as turf, mob/user as mob) + var/list/rejections = list() + var/success = 0 + var/failure = 0 + + for(var/obj/item/I in T) + if(I.type in rejections) // To limit bag spamming: any given type only complains once + continue + if(!can_be_inserted(I, user)) // Note can_be_inserted still makes noise when the answer is no + rejections += I.type // therefore full bags are still a little spammy + failure = 1 + continue + success = 1 + handle_item_insertion(I, 1) //The 1 stops the "You put the [src] into [S]" insertion message from being displayed. + if(success && !failure) + to_chat(user, "You put everything in [src].") + else if(success) + to_chat(user, "You put some things in [src].") + else + to_chat(user, "You fail to pick anything up with \the [src].") + +/obj/item/weapon/storage/verb/toggle_gathering_mode() + set name = "Switch Gathering Method" + set category = "Object" + + collection_mode = !collection_mode + switch (collection_mode) + if(1) + to_chat(usr, "[src] now picks up all items on a tile at once.") + if(0) + to_chat(usr, "[src] now picks up one item at a time.") + + +/obj/item/weapon/storage/verb/quick_empty() + set name = "Empty Contents" + set category = "Object" + set src in view(1) + + // Only humans and robots can dump contents + if(!(ishuman(usr) || isrobot(usr))) + return + + // Hard to do when you're KO'd + if(usr.incapacitated()) + return + + // Has to be at least adjacent (just for safety, src in view should handle this already) + if(!Adjacent(usr)) + return + + //VOREStation Add: No turf dumping if user is in a belly + if(isbelly(usr.loc)) + return + + drop_contents() + +/obj/item/weapon/storage/proc/drop_contents() // why is this a proc? literally just for RPEDs + hide_from(usr) + var/turf/T = get_turf(src) + for(var/obj/item/I in contents) + remove_from_storage(I, T) + +/obj/item/weapon/storage/proc/calibrate_size() + var/total_storage_space = 0 + for(var/obj/item/I in contents) + total_storage_space += I.get_storage_cost() + max_storage_space = max(total_storage_space,max_storage_space) //Prevents spawned containers from being too small for their contents. + +/obj/item/weapon/storage/emp_act(severity) + if(!istype(src.loc, /mob/living)) + for(var/obj/O in contents) + O.emp_act(severity) + ..() + +/obj/item/weapon/storage/attack_self(mob/user as mob) + if((user.get_active_hand() == src) || (isrobot(user)) && allow_quick_empty) + if(src.verbs.Find(/obj/item/weapon/storage/verb/quick_empty)) + src.quick_empty() + return 1 + +//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). +//Returns -1 if the atom was not found on container. +/atom/proc/storage_depth(atom/container) + var/depth = 0 + var/atom/cur_atom = src + + while (cur_atom && !(cur_atom in container.contents)) + if (isarea(cur_atom)) + return -1 + if (istype(cur_atom.loc, /obj/item/weapon/storage)) + depth++ + cur_atom = cur_atom.loc + + if (!cur_atom) + return -1 //inside something with a null loc. + + return depth + +//Like storage depth, but returns the depth to the nearest turf +//Returns -1 if no top level turf (a loc was null somewhere, or a non-turf atom's loc was an area somehow). +/atom/proc/storage_depth_turf() + var/depth = 0 + var/atom/cur_atom = src + + while (cur_atom && !isturf(cur_atom)) + if (isarea(cur_atom)) + return -1 + if (istype(cur_atom.loc, /obj/item/weapon/storage)) + depth++ + cur_atom = cur_atom.loc + + if (!cur_atom) + return -1 //inside something with a null loc. + + return depth + +// See inventory_sizes.dm for the defines. +/obj/item/proc/get_storage_cost() + if (storage_cost) + return storage_cost + else + switch(w_class) + if(ITEMSIZE_TINY) + return ITEMSIZE_COST_TINY + if(ITEMSIZE_SMALL) + return ITEMSIZE_COST_SMALL + if(ITEMSIZE_NORMAL) + return ITEMSIZE_COST_NORMAL + if(ITEMSIZE_LARGE) + return ITEMSIZE_COST_LARGE + if(ITEMSIZE_HUGE) + return ITEMSIZE_COST_HUGE + else + return ITEMSIZE_COST_NO_CONTAINER + +/obj/item/weapon/storage/proc/make_exact_fit() + storage_slots = contents.len + + LAZYCLEARLIST(can_hold) + can_hold = list() + max_w_class = 0 + max_storage_space = 0 + for(var/obj/item/I in src) + can_hold[I.type]++ + max_w_class = max(I.w_class, max_w_class) + max_storage_space += I.get_storage_cost() + +/* + * Trinket Box - READDING SOON + */ +/obj/item/weapon/storage/trinketbox + name = "trinket box" + desc = "A box that can hold small trinkets, such as a ring." + icon = 'icons/obj/items.dmi' + icon_state = "trinketbox" + var/open = 0 + storage_slots = 1 + can_hold = list( + /obj/item/clothing/gloves/ring, + /obj/item/weapon/coin, + /obj/item/clothing/accessory/medal + ) + var/open_state + var/closed_state + +/obj/item/weapon/storage/trinketbox/update_icon() + cut_overlays() + if(open) + icon_state = open_state + + if(contents.len >= 1) + var/contained_image = null + if(istype(contents[1], /obj/item/clothing/gloves/ring)) + contained_image = "ring_trinket" + else if(istype(contents[1], /obj/item/weapon/coin)) + contained_image = "coin_trinket" + else if(istype(contents[1], /obj/item/clothing/accessory/medal)) + contained_image = "medal_trinket" + if(contained_image) + add_overlay(contained_image) + else + icon_state = closed_state + +/obj/item/weapon/storage/trinketbox/New() + if(!open_state) + open_state = "[initial(icon_state)]_open" + if(!closed_state) + closed_state = "[initial(icon_state)]" + ..() + +/obj/item/weapon/storage/trinketbox/attack_self() + open = !open + update_icon() + ..() + +/obj/item/weapon/storage/trinketbox/examine(mob/user) + . = ..() + if(open && contents.len) + var/display_item = contents[1] + . += "\The [src] contains \the [display_item]!" + +/obj/item/weapon/storage/AllowDrop() + return TRUE + +//Useful for spilling the contents of containers all over the floor +/obj/item/weapon/storage/proc/spill(var/dist = 2, var/turf/T = null) + if (!istype(T))//If its not on the floor this might cause issues + T = get_turf(src) + + for (var/obj/O in contents) + remove_from_storage(O, T) + O.tumble(2) + +/// Hangar for storage backdrops, so that we can redirect clicks from them to our item for QOL +/atom/movable/storage_slot + name = "stored - " + icon = 'icons/effects/effects.dmi' + icon_state = "nothing" + plane = PLANE_PLAYER_HUD_ITEMS + layer = 0.1 + alpha = 200 + var/datum/weakref/held_item + +/atom/movable/storage_slot/New(newloc, obj/item/held_item) + ASSERT(held_item) + name += held_item.name + src.held_item = WEAKREF(held_item) + +/atom/movable/storage_slot/Destroy() + held_item = null + +/// Has to be this way. The fact that the overlays will be constantly mutated by other storage means we can't wait. +/atom/movable/storage_slot/add_overlay(list/somethings) + ASSERT(islist(somethings)) + overlays = somethings + +/atom/movable/storage_slot/Click() + var/obj/item/I = held_item?.resolve() + if(I) + usr.ClickOn(I) + return 1 + +// Allows micros to drag themselves into storage items +/obj/item/weapon/storage/MouseDrop_T(mob/living/target, mob/living/user) + if(!istype(user)) return // If the user passed in isn't a living mob, exit + if(target != user) return // If the user didn't drag themselves, exit + if(user.incapacitated() || user.buckled) return // If user is incapacitated or buckled, exit + if(get_holder_of_type(src, /mob/living/carbon/human) == user) return // No jumping into your own equipment + if(ishuman(user) && user.get_effective_size(TRUE) > 0.25) return // Only micro characters + if(ismouse(user) && user.get_effective_size(TRUE) > 1) return // Only normal sized mice or less + + // Create a dummy holder with user's size to test insertion + var/obj/item/weapon/holder/D = new/obj/item/weapon/holder + if(ismouse(user)) + D.w_class = ITEMSIZE_TINY // Mouse smol + else if(ishuman(user)) + D.w_class = ITEMSIZE_SMALL // Players small + else // Other creatures not accepted at this time + qdel(D) // If there's a better way to check the size of a + return // mob's holder and if it fits, replace this slab + if(!src.can_be_inserted(D, 1)) // If the dummy item doesn't fit, exit + qdel(D) + return + qdel(D) + + // Scoop and insert target into storage + var/obj/item/weapon/holder/H = new user.holder_type(get_turf(user), user) + src.handle_item_insertion(H, 1) + to_chat(user, "You climb into \the [src].") + return ..() diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index 9c6795aa123..0637205c3b2 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -1,239 +1,239 @@ -/* - * Toolboxes - */ -/obj/item/weapon/storage/toolbox - name = "toolbox" - desc = "A metal toolbox with remarkably robust construction." - icon = 'icons/obj/storage_vr.dmi' - icon_state = "red" - item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") - center_of_mass = list("x" = 16,"y" = 11) - force = 10 - throwforce = 10 - throw_speed = 1 - throw_range = 7 - w_class = ITEMSIZE_LARGE - max_w_class = ITEMSIZE_NORMAL - max_storage_space = ITEMSIZE_COST_SMALL * 7 //enough to hold all starting contents - origin_tech = list(TECH_COMBAT = 1) - attack_verb = list("robusted") - use_sound = 'sound/items/storage/toolbox.ogg' - drop_sound = 'sound/items/drop/toolbox.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - -//Emergency -/obj/item/weapon/storage/toolbox/emergency - name = "emergency toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "red" - item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") - starts_with = list( - /obj/item/weapon/tool/crowbar/red, - /obj/item/weapon/extinguisher/mini, - /obj/item/device/radio - ) -/obj/item/weapon/storage/toolbox/emergency/Initialize() - if(prob(50)) - new /obj/item/device/flashlight(src) - else - new /obj/item/device/flashlight/flare(src) - . = ..() - -//Mechanical -/obj/item/weapon/storage/toolbox/mechanical - name = "mechanical toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "blue" - item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/device/analyzer, - /obj/item/weapon/tool/wirecutters - ) - -//Electrical -/obj/item/weapon/storage/toolbox/electrical - name = "electrical toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "yellow" - item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") - starts_with = list( - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wirecutters, - /obj/item/device/t_scanner, - /obj/item/weapon/tool/crowbar, - /obj/item/stack/cable_coil/random_belt, - /obj/item/stack/cable_coil/random_belt - ) -/obj/item/weapon/storage/toolbox/electrical/Initialize() - . = ..() - if(prob(5)) - new /obj/item/clothing/gloves/yellow(src) - else - new /obj/item/stack/cable_coil/random(src,30) - calibrate_size() - -//Syndicate -/obj/item/weapon/storage/toolbox/syndicate - name = "black and red toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "syndicate" - item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") - origin_tech = list(TECH_COMBAT = 1, TECH_ILLEGAL = 1) - force = 14 - starts_with = list( - /obj/item/clothing/gloves/yellow, - /obj/item/weapon/tool/screwdriver, - /obj/item/weapon/tool/wrench, - /obj/item/weapon/weldingtool, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/tool/wirecutters, - /obj/item/device/multitool - ) - -/obj/item/weapon/storage/toolbox/syndicate/powertools - starts_with = list( - /obj/item/clothing/gloves/yellow, - /obj/item/weapon/tool/screwdriver/power, - /obj/item/weapon/weldingtool/experimental, - /obj/item/weapon/tool/crowbar/power, - /obj/item/device/multitool, - /obj/item/stack/cable_coil/random_belt, - /obj/item/device/analyzer - ) - -//Brass -/obj/item/weapon/storage/toolbox/brass - name = "brass toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "brass" - item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") - starts_with = list( - /obj/item/weapon/tool/crowbar/brass, - /obj/item/weapon/tool/wirecutters/brass, - /obj/item/weapon/tool/screwdriver/brass, - /obj/item/weapon/tool/wrench/brass, - /obj/item/weapon/weldingtool/brass - ) - -//Hydro -/obj/item/weapon/storage/toolbox/hydro - name = "hydroponic toolbox" - icon = 'icons/obj/storage_vr.dmi' - icon_state = "green" - item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") - starts_with = list( - /obj/item/device/analyzer/plant_analyzer, - /obj/item/weapon/material/minihoe, - /obj/item/weapon/material/knife/machete/hatchet, - /obj/item/weapon/tool/wirecutters/clippers/trimmers, - /obj/item/weapon/reagent_containers/spray/plantbgone, - /obj/item/weapon/reagent_containers/glass/beaker - ) - -/* - * Lunchboxes - */ - -/obj/item/weapon/storage/toolbox/lunchbox - max_storage_space = ITEMSIZE_COST_SMALL * 4 //slightly smaller than a toolbox - name = "rainbow lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_rainbow" - item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") - desc = "A little lunchbox. This one is the colors of the rainbow!" - w_class = ITEMSIZE_NORMAL - max_w_class = ITEMSIZE_SMALL - var/filled = FALSE - attack_verb = list("lunched") - -/obj/item/weapon/storage/toolbox/lunchbox/Initialize() - if(filled) - var/list/lunches = lunchables_lunches() - var/lunch = lunches[pick(lunches)] - new lunch(src) - - var/list/snacks = lunchables_snacks() - var/snack = snacks[pick(snacks)] - new snack(src) - - var/list/drinks = lunchables_drinks() - var/drink = drinks[pick(drinks)] - new drink(src) - . = ..() - -/obj/item/weapon/storage/toolbox/lunchbox/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/heart - name = "heart lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_lovelyhearts" - item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") - desc = "A little lunchbox. This one has cute little hearts on it!" - -/obj/item/weapon/storage/toolbox/lunchbox/heart/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/cat - name = "cat lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_sciencecatshow" - item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") - desc = "A little lunchbox. This one has a cute little science cat from a popular show on it!" - -/obj/item/weapon/storage/toolbox/lunchbox/cat/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/nt - name = "NanoTrasen brand lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_nanotrasen" - item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") - desc = "A little lunchbox. This one is branded with the NanoTrasen logo!" - -/obj/item/weapon/storage/toolbox/lunchbox/nt/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/mars - name = "\improper Mojave university lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_marsuniversity" - item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") - desc = "A little lunchbox. This one is branded with the Mojave university logo!" - -/obj/item/weapon/storage/toolbox/lunchbox/mars/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/cti - name = "\improper CTI lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_cti" - item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") - desc = "A little lunchbox. This one is branded with the CTI logo!" - -/obj/item/weapon/storage/toolbox/lunchbox/cti/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/nymph - name = "\improper Diona nymph lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_dionanymph" - item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") - desc = "A little lunchbox. This one is an adorable Diona nymph on the side!" - -/obj/item/weapon/storage/toolbox/lunchbox/nymph/filled - filled = TRUE - -/obj/item/weapon/storage/toolbox/lunchbox/syndicate - name = "black and red lunchbox" - icon = 'icons/obj/storage.dmi' - icon_state = "lunchbox_syndie" - item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") - desc = "A little lunchbox. This one is a sleek black and red, made of a durable steel!" - -/obj/item/weapon/storage/toolbox/lunchbox/syndicate/filled - filled = TRUE +/* + * Toolboxes + */ +/obj/item/weapon/storage/toolbox + name = "toolbox" + desc = "A metal toolbox with remarkably robust construction." + icon = 'icons/obj/storage_vr.dmi' + icon_state = "red" + item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + center_of_mass = list("x" = 16,"y" = 11) + force = 10 + throwforce = 10 + throw_speed = 1 + throw_range = 7 + w_class = ITEMSIZE_LARGE + max_w_class = ITEMSIZE_NORMAL + max_storage_space = ITEMSIZE_COST_SMALL * 7 //enough to hold all starting contents + origin_tech = list(TECH_COMBAT = 1) + attack_verb = list("robusted") + use_sound = 'sound/items/storage/toolbox.ogg' + drop_sound = 'sound/items/drop/toolbox.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + +//Emergency +/obj/item/weapon/storage/toolbox/emergency + name = "emergency toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "red" + item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + starts_with = list( + /obj/item/weapon/tool/crowbar/red, + /obj/item/weapon/extinguisher/mini, + /obj/item/device/radio + ) +/obj/item/weapon/storage/toolbox/emergency/Initialize() + if(prob(50)) + new /obj/item/device/flashlight(src) + else + new /obj/item/device/flashlight/flare(src) + . = ..() + +//Mechanical +/obj/item/weapon/storage/toolbox/mechanical + name = "mechanical toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "blue" + item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/device/analyzer, + /obj/item/weapon/tool/wirecutters + ) + +//Electrical +/obj/item/weapon/storage/toolbox/electrical + name = "electrical toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "yellow" + item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") + starts_with = list( + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wirecutters, + /obj/item/device/t_scanner, + /obj/item/weapon/tool/crowbar, + /obj/item/stack/cable_coil/random_belt, + /obj/item/stack/cable_coil/random_belt + ) +/obj/item/weapon/storage/toolbox/electrical/Initialize() + . = ..() + if(prob(5)) + new /obj/item/clothing/gloves/yellow(src) + else + new /obj/item/stack/cable_coil/random(src,30) + calibrate_size() + +//Syndicate +/obj/item/weapon/storage/toolbox/syndicate + name = "black and red toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "syndicate" + item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") + origin_tech = list(TECH_COMBAT = 1, TECH_ILLEGAL = 1) + force = 14 + starts_with = list( + /obj/item/clothing/gloves/yellow, + /obj/item/weapon/tool/screwdriver, + /obj/item/weapon/tool/wrench, + /obj/item/weapon/weldingtool, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/tool/wirecutters, + /obj/item/device/multitool + ) + +/obj/item/weapon/storage/toolbox/syndicate/powertools + starts_with = list( + /obj/item/clothing/gloves/yellow, + /obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/weldingtool/experimental, + /obj/item/weapon/tool/transforming/jawsoflife, + /obj/item/device/multitool, + /obj/item/stack/cable_coil/random_belt, + /obj/item/device/analyzer + ) + +//Brass +/obj/item/weapon/storage/toolbox/brass + name = "brass toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "brass" + item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") + starts_with = list( + /obj/item/weapon/tool/crowbar/brass, + /obj/item/weapon/tool/wirecutters/brass, + /obj/item/weapon/tool/screwdriver/brass, + /obj/item/weapon/tool/wrench/brass, + /obj/item/weapon/weldingtool/brass + ) + +//Hydro +/obj/item/weapon/storage/toolbox/hydro + name = "hydroponic toolbox" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "green" + item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") + starts_with = list( + /obj/item/device/analyzer/plant_analyzer, + /obj/item/weapon/material/minihoe, + /obj/item/weapon/material/knife/machete/hatchet, + /obj/item/weapon/tool/wirecutters/clippers/trimmers, + /obj/item/weapon/reagent_containers/spray/plantbgone, + /obj/item/weapon/reagent_containers/glass/beaker + ) + +/* + * Lunchboxes + */ + +/obj/item/weapon/storage/toolbox/lunchbox + max_storage_space = ITEMSIZE_COST_SMALL * 4 //slightly smaller than a toolbox + name = "rainbow lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_rainbow" + item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") + desc = "A little lunchbox. This one is the colors of the rainbow!" + w_class = ITEMSIZE_NORMAL + max_w_class = ITEMSIZE_SMALL + var/filled = FALSE + attack_verb = list("lunched") + +/obj/item/weapon/storage/toolbox/lunchbox/Initialize() + if(filled) + var/list/lunches = lunchables_lunches() + var/lunch = lunches[pick(lunches)] + new lunch(src) + + var/list/snacks = lunchables_snacks() + var/snack = snacks[pick(snacks)] + new snack(src) + + var/list/drinks = lunchables_drinks() + var/drink = drinks[pick(drinks)] + new drink(src) + . = ..() + +/obj/item/weapon/storage/toolbox/lunchbox/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/heart + name = "heart lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_lovelyhearts" + item_state_slots = list(slot_r_hand_str = "toolbox_pink", slot_l_hand_str = "toolbox_pink") + desc = "A little lunchbox. This one has cute little hearts on it!" + +/obj/item/weapon/storage/toolbox/lunchbox/heart/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/cat + name = "cat lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_sciencecatshow" + item_state_slots = list(slot_r_hand_str = "toolbox_green", slot_l_hand_str = "toolbox_green") + desc = "A little lunchbox. This one has a cute little science cat from a popular show on it!" + +/obj/item/weapon/storage/toolbox/lunchbox/cat/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/nt + name = "NanoTrasen brand lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_nanotrasen" + item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") + desc = "A little lunchbox. This one is branded with the NanoTrasen logo!" + +/obj/item/weapon/storage/toolbox/lunchbox/nt/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/mars + name = "\improper Mojave university lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_marsuniversity" + item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + desc = "A little lunchbox. This one is branded with the Mojave university logo!" + +/obj/item/weapon/storage/toolbox/lunchbox/mars/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/cti + name = "\improper CTI lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_cti" + item_state_slots = list(slot_r_hand_str = "toolbox_blue", slot_l_hand_str = "toolbox_blue") + desc = "A little lunchbox. This one is branded with the CTI logo!" + +/obj/item/weapon/storage/toolbox/lunchbox/cti/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/nymph + name = "\improper Diona nymph lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_dionanymph" + item_state_slots = list(slot_r_hand_str = "toolbox_yellow", slot_l_hand_str = "toolbox_yellow") + desc = "A little lunchbox. This one is an adorable Diona nymph on the side!" + +/obj/item/weapon/storage/toolbox/lunchbox/nymph/filled + filled = TRUE + +/obj/item/weapon/storage/toolbox/lunchbox/syndicate + name = "black and red lunchbox" + icon = 'icons/obj/storage.dmi' + icon_state = "lunchbox_syndie" + item_state_slots = list(slot_r_hand_str = "toolbox_syndi", slot_l_hand_str = "toolbox_syndi") + desc = "A little lunchbox. This one is a sleek black and red, made of a durable steel!" + +/obj/item/weapon/storage/toolbox/lunchbox/syndicate/filled + filled = TRUE diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index 198eeda966c..fc0bd1856d9 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -328,7 +328,6 @@ description_antag = "This case will likely contain a charged fuel rod gun, and a few fuel rods to go with it. It can only hold the fuel rod gun, fuel rods, batteries, a screwdriver, and stock machine parts." force = 12 //Anti-rad lined i.e. Lead, probably gonna hurt a bit if you get bashed with it. can_hold = list(/obj/item/weapon/gun/magnetic/fuelrod, /obj/item/weapon/fuel_assembly, /obj/item/weapon/cell, /obj/item/weapon/stock_parts, /obj/item/weapon/tool/screwdriver) - cant_hold = list(/obj/item/weapon/tool/screwdriver/power) starts_with = list( /obj/item/weapon/gun/magnetic/fuelrod, /obj/item/weapon/fuel_assembly/deuterium, diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index bf6e0700c08..f2ea7da8a2c 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -39,7 +39,6 @@ /obj/item/weapon/makeover, /obj/item/pizzavoucher //VOREStation edit ) - cant_hold = list(/obj/item/weapon/tool/screwdriver/power) slot_flags = SLOT_ID var/obj/item/weapon/card/id/front_id = null diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index 0ff349f4769..40b0ef44b41 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -1,114 +1,114 @@ -/* Weapons - * Contains: - * Sword - * Classic Baton - * Telescopic Baton - */ - -/* - * Classic Baton - */ - -/obj/item/weapon/melee - name = "weapon" - desc = "Murder device." - icon = 'icons/obj/weapons.dmi' - icon_state = "baton" - slot_flags = SLOT_BELT - force = 10 - drop_sound = 'sound/items/drop/metalweapon.ogg' - -/obj/item/weapon/melee/classic_baton - name = "police baton" - desc = "A wooden truncheon for beating criminal scum." - icon = 'icons/obj/weapons.dmi' - icon_state = "baton" - item_state = "classic_baton" - slot_flags = SLOT_BELT - force = 10 - drop_sound = 'sound/items/drop/crowbar.ogg' - pickup_sound = 'sound/items/pickup/crowbar.ogg' - -/obj/item/weapon/melee/classic_baton/attack(mob/M as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, BP_HEAD) - else - user.take_organ_damage(2*force) - return - return ..() - -//Telescopic baton -/obj/item/weapon/melee/telebaton - name = "telescopic baton" - desc = "A compact yet rebalanced personal defense weapon. Can be concealed when folded." - icon = 'icons/obj/weapons.dmi' - icon_state = "telebaton0" - item_state = "telebaton0" - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - force = 3 - drop_sound = 'sound/items/drop/crowbar.ogg' - pickup_sound = 'sound/items/pickup/crowbar.ogg' - var/on = 0 - -/obj/item/weapon/melee/telebaton/attack_self(mob/user as mob) - on = !on - if(on) - user.visible_message("With a flick of their wrist, [user] extends their telescopic baton.",\ - "You extend the baton.",\ - "You hear an ominous click.") - icon_state = "telebaton1" - item_state = icon_state - w_class = ITEMSIZE_NORMAL - force = 15//quite robust - attack_verb = list("smacked", "struck", "slapped") - else - user.visible_message("\The [user] collapses their telescopic baton.",\ - "You collapse the baton.",\ - "You hear a click.") - icon_state = "telebaton0" - item_state = icon_state - w_class = ITEMSIZE_SMALL - force = 3//not so robust now - attack_verb = list("hit", "punched") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - playsound(src, 'sound/weapons/empty.ogg', 50, 1) - add_fingerprint(user) - - if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any - cut_overlays() - - var/icon/I = new /icon(src.icon, src.icon_state) - I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) - I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY) - blood_overlay = I - - add_overlay(blood_overlay) - - return - -/obj/item/weapon/melee/telebaton/attack(mob/target as mob, mob/living/user as mob) - if(on) - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, BP_HEAD) - else - user.take_organ_damage(2*force) - return - if(..()) - //playsound(src, "swing_hit", 50, 1, -1) - return - else - return ..() +/* Weapons + * Contains: + * Sword + * Classic Baton + * Telescopic Baton + */ + +/* + * Classic Baton + */ + +/obj/item/weapon/melee + name = "weapon" + desc = "Murder device." + icon = 'icons/obj/weapons.dmi' + icon_state = "baton" + slot_flags = SLOT_BELT + force = 10 + drop_sound = 'sound/items/drop/metalweapon.ogg' + +/obj/item/weapon/melee/classic_baton + name = "police baton" + desc = "A wooden truncheon for beating criminal scum." + icon = 'icons/obj/weapons.dmi' + icon_state = "baton" + item_state = "classic_baton" + slot_flags = SLOT_BELT + force = 10 + drop_sound = 'sound/items/drop/crowbar.ogg' + pickup_sound = 'sound/items/pickup/crowbar.ogg' + +/obj/item/weapon/melee/classic_baton/attack(mob/M as mob, mob/living/user as mob) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You club yourself over the head.") + user.Weaken(3 * force) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, BP_HEAD) + else + user.take_organ_damage(2*force) + return + return ..() + +//Telescopic baton +/obj/item/weapon/melee/telebaton + name = "telescopic baton" + desc = "A compact yet rebalanced personal defense weapon. Can be concealed when folded." + icon = 'icons/obj/weapons.dmi' + icon_state = "telebaton0" + item_state = "telebaton0" + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + force = 3 + drop_sound = 'sound/items/drop/crowbar.ogg' + pickup_sound = 'sound/items/pickup/crowbar.ogg' + var/on = 0 + +/obj/item/weapon/melee/telebaton/attack_self(mob/user as mob) + on = !on + if(on) + user.visible_message("With a flick of their wrist, [user] extends their telescopic baton.",\ + "You extend the baton.",\ + "You hear an ominous click.") + icon_state = "telebaton1" + item_state = icon_state + w_class = ITEMSIZE_NORMAL + force = 15//quite robust + attack_verb = list("smacked", "struck", "slapped") + else + user.visible_message("\The [user] collapses their telescopic baton.",\ + "You collapse the baton.",\ + "You hear a click.") + icon_state = "telebaton0" + item_state = icon_state + w_class = ITEMSIZE_SMALL + force = 3//not so robust now + attack_verb = list("hit", "punched") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + playsound(src, 'sound/weapons/empty.ogg', 50, 1) + add_fingerprint(user) + + if(blood_overlay && blood_DNA && (blood_DNA.len >= 1)) //updates blood overlay, if any + cut_overlays() + + var/icon/I = new /icon(src.icon, src.icon_state) + I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) + I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY) + blood_overlay = I + + add_overlay(blood_overlay) + + return + +/obj/item/weapon/melee/telebaton/attack(mob/target as mob, mob/living/user as mob) + if(on) + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You club yourself over the head.") + user.Weaken(3 * force) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, BP_HEAD) + else + user.take_organ_damage(2*force) + return + if(..()) + //playsound(src, "swing_hit", 50, 1, -1) + return + else + return ..() diff --git a/code/game/objects/items/weapons/syndie.dm b/code/game/objects/items/weapons/syndie.dm index 0f70a805dd5..5e519f8f4f0 100644 --- a/code/game/objects/items/weapons/syndie.dm +++ b/code/game/objects/items/weapons/syndie.dm @@ -106,7 +106,7 @@ /obj/item/weapon/flame/lighter/zippo/c4detonator/attackby(obj/item/weapon/W, mob/user as mob) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) detonator_mode = !detonator_mode playsound(src, W.usesound, 50, 1) to_chat(user, "You unscrew the top panel of \the [src] revealing a button.") diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 8bb5bb465b8..10724e2e3da 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -1,137 +1,137 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/weapon/tank/jetpack - name = "jetpack (empty)" - desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." - icon = 'icons/obj/tank_vr.dmi' //VOREStation Edit - icon_state = "jetpack" - gauge_icon = null - w_class = ITEMSIZE_LARGE - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', - ) - item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - var/datum/effect/effect/system/ion_trail_follow/ion_trail - var/on = 0.0 - var/stabilization_on = 0 - var/volume_rate = 500 //Needed for borg jetpack transfer - action_button_name = "Toggle Jetpack" - -/obj/item/weapon/tank/jetpack/New() - ..() - ion_trail = new /datum/effect/effect/system/ion_trail_follow() - ion_trail.set_up(src) - -/obj/item/weapon/tank/jetpack/Destroy() - QDEL_NULL(ion_trail) - return ..() - -/obj/item/weapon/tank/jetpack/examine(mob/user) - . = ..() - if(air_contents.total_moles < 5) - . += "The meter on \the [src] indicates you are almost out of gas!" - playsound(src, 'sound/effects/alert.ogg', 50, 1) - -/obj/item/weapon/tank/jetpack/verb/toggle_rockets() - set name = "Toggle Jetpack Stabilization" - set category = "Object" - stabilization_on = !( stabilization_on ) - to_chat(usr, "You toggle the stabilization [stabilization_on? "on":"off"].") - -/obj/item/weapon/tank/jetpack/verb/toggle() - set name = "Toggle Jetpack" - set category = "Object" - - on = !on - if(on) - icon_state = "[icon_state]-on" - ion_trail.start() - else - icon_state = initial(icon_state) - ion_trail.stop() - - if (ismob(usr)) - var/mob/M = usr - M.update_inv_back() - M.update_action_buttons() - - to_chat(usr, "You toggle the thrusters [on? "on":"off"].") - -/obj/item/weapon/tank/jetpack/proc/get_gas_supply() - return air_contents - -/obj/item/weapon/tank/jetpack/proc/can_thrust(num) - if(!on) - return 0 - - var/datum/gas_mixture/fuel = get_gas_supply() - if(num < 0.005 || !fuel || fuel.total_moles < num) - ion_trail.stop() - return 0 - - return 1 - -/obj/item/weapon/tank/jetpack/proc/do_thrust(num, mob/living/user) - if(!can_thrust(num)) - return 0 - - var/datum/gas_mixture/fuel = get_gas_supply() - fuel.remove(num) - return 1 - -/obj/item/weapon/tank/jetpack/ui_action_click() - toggle() - -/obj/item/weapon/tank/jetpack/void - name = "void jetpack (oxygen)" - desc = "It works well in a void." - icon_state = "jetpack-void" - item_state_slots = list(slot_r_hand_str = "jetpack-void", slot_l_hand_str = "jetpack-void") - -/obj/item/weapon/tank/jetpack/void/Initialize() - . = ..() - air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/oxygen - name = "jetpack (oxygen)" - desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack" - item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") - -/obj/item/weapon/tank/jetpack/oxygen/Initialize() - . = ..() - air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/breaker - name = "CSC industrial jetpack" - desc = "A JetFast EVA thruster pack. A warning label clearly states \'WARNING: CONTAINS VOLATILE REACTION MASS TOXIC TO MOST LIFEFORMS. NOT TO BE USED WITH CLOSED CYCLE BREATHING SYSTEMS.\'" - icon_state = "jetpack-breaker" - item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") - -/obj/item/weapon/tank/jetpack/breaker/Initialize() - . = ..() - air_contents.adjust_gas("volatile_fuel", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/carbondioxide - name = "jetpack (carbon dioxide)" - desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." - distribute_pressure = 0 - icon_state = "jetpack-black" - item_state_slots = list(slot_r_hand_str = "jetpack-black", slot_l_hand_str = "jetpack-black") - -/obj/item/weapon/tank/jetpack/carbondioxide/Initialize() - . = ..() - air_contents.adjust_gas("carbon_dioxide", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/jetpack/rig - name = "jetpack" - var/obj/item/weapon/rig/holder - -/obj/item/weapon/tank/jetpack/rig/examine() - . = ..() - . += "It's a jetpack. If you can see this, report it on the bug tracker." - -/obj/item/weapon/tank/jetpack/rig/get_gas_supply() - return holder?.air_supply?.air_contents +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/weapon/tank/jetpack + name = "jetpack (empty)" + desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." + icon = 'icons/obj/tank_vr.dmi' //VOREStation Edit + icon_state = "jetpack" + gauge_icon = null + w_class = ITEMSIZE_LARGE + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_storage.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_storage.dmi', + ) + item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + var/datum/effect/effect/system/ion_trail_follow/ion_trail + var/on = 0.0 + var/stabilization_on = 0 + var/volume_rate = 500 //Needed for borg jetpack transfer + action_button_name = "Toggle Jetpack" + +/obj/item/weapon/tank/jetpack/New() + ..() + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + +/obj/item/weapon/tank/jetpack/Destroy() + QDEL_NULL(ion_trail) + return ..() + +/obj/item/weapon/tank/jetpack/examine(mob/user) + . = ..() + if(air_contents.total_moles < 5) + . += "The meter on \the [src] indicates you are almost out of gas!" + playsound(src, 'sound/effects/alert.ogg', 50, 1) + +/obj/item/weapon/tank/jetpack/verb/toggle_rockets() + set name = "Toggle Jetpack Stabilization" + set category = "Object" + stabilization_on = !( stabilization_on ) + to_chat(usr, "You toggle the stabilization [stabilization_on? "on":"off"].") + +/obj/item/weapon/tank/jetpack/verb/toggle() + set name = "Toggle Jetpack" + set category = "Object" + + on = !on + if(on) + icon_state = "[icon_state]-on" + ion_trail.start() + else + icon_state = initial(icon_state) + ion_trail.stop() + + if (ismob(usr)) + var/mob/M = usr + M.update_inv_back() + M.update_action_buttons() + + to_chat(usr, "You toggle the thrusters [on? "on":"off"].") + +/obj/item/weapon/tank/jetpack/proc/get_gas_supply() + return air_contents + +/obj/item/weapon/tank/jetpack/proc/can_thrust(num) + if(!on) + return 0 + + var/datum/gas_mixture/fuel = get_gas_supply() + if(num < 0.005 || !fuel || fuel.total_moles < num) + ion_trail.stop() + return 0 + + return 1 + +/obj/item/weapon/tank/jetpack/proc/do_thrust(num, mob/living/user) + if(!can_thrust(num)) + return 0 + + var/datum/gas_mixture/fuel = get_gas_supply() + fuel.remove(num) + return 1 + +/obj/item/weapon/tank/jetpack/ui_action_click() + toggle() + +/obj/item/weapon/tank/jetpack/void + name = "void jetpack (oxygen)" + desc = "It works well in a void." + icon_state = "jetpack-void" + item_state_slots = list(slot_r_hand_str = "jetpack-void", slot_l_hand_str = "jetpack-void") + +/obj/item/weapon/tank/jetpack/void/Initialize() + . = ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/oxygen + name = "jetpack (oxygen)" + desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack" + item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") + +/obj/item/weapon/tank/jetpack/oxygen/Initialize() + . = ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/breaker + name = "CSC industrial jetpack" + desc = "A JetFast EVA thruster pack. A warning label clearly states \'WARNING: CONTAINS VOLATILE REACTION MASS TOXIC TO MOST LIFEFORMS. NOT TO BE USED WITH CLOSED CYCLE BREATHING SYSTEMS.\'" + icon_state = "jetpack-breaker" + item_state_slots = list(slot_r_hand_str = "jetpack", slot_l_hand_str = "jetpack") + +/obj/item/weapon/tank/jetpack/breaker/Initialize() + . = ..() + air_contents.adjust_gas("volatile_fuel", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/carbondioxide + name = "jetpack (carbon dioxide)" + desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." + distribute_pressure = 0 + icon_state = "jetpack-black" + item_state_slots = list(slot_r_hand_str = "jetpack-black", slot_l_hand_str = "jetpack-black") + +/obj/item/weapon/tank/jetpack/carbondioxide/Initialize() + . = ..() + air_contents.adjust_gas("carbon_dioxide", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/jetpack/rig + name = "jetpack" + var/obj/item/weapon/rig/holder + +/obj/item/weapon/tank/jetpack/rig/examine() + . = ..() + . += "It's a jetpack. If you can see this, report it on the bug tracker." + +/obj/item/weapon/tank/jetpack/rig/get_gas_supply() + return holder?.air_supply?.air_contents diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 1764b0d8bec..e34ad4810b7 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -1,230 +1,230 @@ -/* Types of tanks! - * Contains: - * Oxygen - * Anesthetic - * Air - * Phoron - * Emergency Oxygen - */ - -/* - * Oxygen - */ -/obj/item/weapon/tank/oxygen - name = "oxygen tank" - desc = "A tank of oxygen." - icon_state = "oxygen" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - -/obj/item/weapon/tank/oxygen/Initialize() - . = ..() - air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/oxygen/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["oxygen"] < 10)) - . += "The meter on \the [src] indicates you are almost out of oxygen!" - -/obj/item/weapon/tank/oxygen/yellow - desc = "A tank of oxygen, this one is yellow." - icon_state = "oxygen_f" - -/obj/item/weapon/tank/oxygen/red - desc = "A tank of oxygen, this one is red." - icon_state = "oxygen_fr" - -/* - * Anesthetic - */ -/obj/item/weapon/tank/anesthetic - name = "anesthetic tank" - desc = "A tank with an N2O/O2 gas mix." - icon_state = "anesthetic" - -/obj/item/weapon/tank/anesthetic/Initialize() - . = ..() - - air_contents.gas["oxygen"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD - air_contents.gas["nitrous_oxide"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD - air_contents.update_values() - -/* - * Air - */ -/obj/item/weapon/tank/air - name = "air tank" - desc = "Mixed anyone?" - icon_state = "oxygen" - -/obj/item/weapon/tank/air/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["oxygen"] < 1)) - . += "The meter on \the [src] indicates you are almost out of air!" - user << sound('sound/effects/alert.ogg') - -/obj/item/weapon/tank/air/Initialize() - . = ..() - src.air_contents.adjust_multi("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, "nitrogen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD) - -/* - * Phoron - */ -/obj/item/weapon/tank/phoron - name = "phoron tank" - desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." - icon_state = "phoron" - gauge_icon = null - slot_flags = null //they have no straps! - -/obj/item/weapon/tank/phoron/Initialize() - . = ..() - src.air_contents.adjust_gas("phoron", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/phoron/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - - if (istype(W, /obj/item/weapon/flamethrower)) - var/obj/item/weapon/flamethrower/F = W - if ((!F.status)||(F.ptank)) return - src.master = F - F.ptank = src - user.remove_from_mob(src) - src.loc = F - return - -/obj/item/weapon/tank/vox //Can't be a child of phoron or the gas amount gets screwey. - name = "phoron tank" - desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." - icon_state = "phoron_vox" - gauge_icon = null - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - slot_flags = SLOT_BACK //these ones have straps! - -/obj/item/weapon/tank/vox/Initialize() - . = ..() - air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) //VOREStation Edit - -/obj/item/weapon/tank/phoron/pressurized - name = "fuel can" - icon_state = "phoron_vox" - w_class = ITEMSIZE_NORMAL - -/obj/item/weapon/tank/phoron/pressurized/Initialize() - . = ..() - adjust_scale(0.8) - air_contents.adjust_gas("phoron", (7*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/* - * Emergency Oxygen - */ - -/obj/item/weapon/tank/emergency - name = "emergency tank" - icon_state = "emergency" - gauge_icon = "indicator_emergency" - gauge_cap = 4 - slot_flags = SLOT_BELT - w_class = ITEMSIZE_SMALL - force = 4 - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - volume = 2 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) - -/obj/item/weapon/tank/emergency/oxygen - name = "emergency oxygen tank" - desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it." - icon_state = "emergency" - gauge_icon = "indicator_emergency" - -/obj/item/weapon/tank/emergency/oxygen/Initialize() - . = ..() - src.air_contents.adjust_gas("oxygen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/oxygen/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["oxygen"] < 0.2)) - . += "The meter on the [src.name] indicates you are almost out of air!" - user << sound('sound/effects/alert.ogg') - -/obj/item/weapon/tank/emergency/oxygen/engi - name = "extended-capacity emergency oxygen tank" - icon_state = "emergency_engi" - volume = 6 - -/obj/item/weapon/tank/emergency/oxygen/double - name = "double emergency oxygen tank" - icon_state = "emergency_double" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/stasis/oxygen // Stasis bags need to have initial pressure within safe bounds for human atmospheric pressure (NOT breath pressure) - name = "stasis oxygen tank" - desc = "Oxygen tank included in most stasis bag designs." - icon_state = "emergency_double" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/stasis/oxygen/Initialize() - . = ..() - src.air_contents.adjust_gas("oxygen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/nitrogen - name = "emergency nitrogen tank" - desc = "An emergency air tank hastily painted red." - icon_state = "emergency_nitro" - gauge_icon = "indicator_emergency" - -/obj/item/weapon/tank/emergency/nitrogen/Initialize() - . = ..() - src.air_contents.adjust_gas("nitrogen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/nitrogen/double - name = "double emergency nitrogen tank" - icon_state = "emergency_double_nitrogen" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/emergency/phoron - name = "emergency phoron tank" - desc = "An emergency air tank hastily painted red." - icon_state = "emergency_nitro" - gauge_icon = "indicator_emergency" - -/obj/item/weapon/tank/emergency/phoron/Initialize() - . = ..() - src.air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/emergency/phoron/double - name = "double emergency phoron tank" - icon_state = "emergency_double_nitro" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/* - * Nitrogen - */ -/obj/item/weapon/tank/nitrogen - name = "nitrogen tank" - desc = "A tank of nitrogen." - icon_state = "oxygen_fr" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - -/obj/item/weapon/tank/nitrogen/Initialize() - . = ..() - src.air_contents.adjust_gas("nitrogen", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) - -/obj/item/weapon/tank/nitrogen/examine(mob/user) - . = ..() - if(loc == user && (air_contents.gas["nitrogen"] < 10)) - . += "The meter on \the [src] indicates you are almost out of nitrogen!" - //playsound(user, 'sound/effects/alert.ogg', 50, 1) - -/obj/item/weapon/tank/stasis/nitro_cryo // Synthmorph bags need to have initial pressure within safe bounds for human atmospheric pressure, but low temperature to stop unwanted degredation. - name = "stasis cryogenic nitrogen tank" - desc = "Cryogenic Nitrogen tank included in most synthmorph bag designs." - icon_state = "emergency_double_nitro" - gauge_icon = "indicator_emergency_double" - volume = 10 - -/obj/item/weapon/tank/stasis/nitro_cryo/Initialize() - . = ..() - src.air_contents.adjust_gas_temp("nitrogen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*TN60C), TN60C) +/* Types of tanks! + * Contains: + * Oxygen + * Anesthetic + * Air + * Phoron + * Emergency Oxygen + */ + +/* + * Oxygen + */ +/obj/item/weapon/tank/oxygen + name = "oxygen tank" + desc = "A tank of oxygen." + icon_state = "oxygen" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + +/obj/item/weapon/tank/oxygen/Initialize() + . = ..() + air_contents.adjust_gas("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/oxygen/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["oxygen"] < 10)) + . += "The meter on \the [src] indicates you are almost out of oxygen!" + +/obj/item/weapon/tank/oxygen/yellow + desc = "A tank of oxygen, this one is yellow." + icon_state = "oxygen_f" + +/obj/item/weapon/tank/oxygen/red + desc = "A tank of oxygen, this one is red." + icon_state = "oxygen_fr" + +/* + * Anesthetic + */ +/obj/item/weapon/tank/anesthetic + name = "anesthetic tank" + desc = "A tank with an N2O/O2 gas mix." + icon_state = "anesthetic" + +/obj/item/weapon/tank/anesthetic/Initialize() + . = ..() + + air_contents.gas["oxygen"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD + air_contents.gas["nitrous_oxide"] = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD + air_contents.update_values() + +/* + * Air + */ +/obj/item/weapon/tank/air + name = "air tank" + desc = "Mixed anyone?" + icon_state = "oxygen" + +/obj/item/weapon/tank/air/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["oxygen"] < 1)) + . += "The meter on \the [src] indicates you are almost out of air!" + user << sound('sound/effects/alert.ogg') + +/obj/item/weapon/tank/air/Initialize() + . = ..() + src.air_contents.adjust_multi("oxygen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, "nitrogen", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD) + +/* + * Phoron + */ +/obj/item/weapon/tank/phoron + name = "phoron tank" + desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." + icon_state = "phoron" + gauge_icon = null + slot_flags = null //they have no straps! + +/obj/item/weapon/tank/phoron/Initialize() + . = ..() + src.air_contents.adjust_gas("phoron", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/phoron/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + + if (istype(W, /obj/item/weapon/flamethrower)) + var/obj/item/weapon/flamethrower/F = W + if ((!F.status)||(F.ptank)) return + src.master = F + F.ptank = src + user.remove_from_mob(src) + src.loc = F + return + +/obj/item/weapon/tank/vox //Can't be a child of phoron or the gas amount gets screwey. + name = "phoron tank" + desc = "Contains dangerous phoron. Do not inhale. Warning: extremely flammable." + icon_state = "phoron_vox" + gauge_icon = null + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + slot_flags = SLOT_BACK //these ones have straps! + +/obj/item/weapon/tank/vox/Initialize() + . = ..() + air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) //VOREStation Edit + +/obj/item/weapon/tank/phoron/pressurized + name = "fuel can" + icon_state = "phoron_vox" + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/tank/phoron/pressurized/Initialize() + . = ..() + adjust_scale(0.8) + air_contents.adjust_gas("phoron", (7*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/* + * Emergency Oxygen + */ + +/obj/item/weapon/tank/emergency + name = "emergency tank" + icon_state = "emergency" + gauge_icon = "indicator_emergency" + gauge_cap = 4 + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + force = 4 + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + volume = 2 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) + +/obj/item/weapon/tank/emergency/oxygen + name = "emergency oxygen tank" + desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it." + icon_state = "emergency" + gauge_icon = "indicator_emergency" + +/obj/item/weapon/tank/emergency/oxygen/Initialize() + . = ..() + src.air_contents.adjust_gas("oxygen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/oxygen/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["oxygen"] < 0.2)) + . += "The meter on the [src.name] indicates you are almost out of air!" + user << sound('sound/effects/alert.ogg') + +/obj/item/weapon/tank/emergency/oxygen/engi + name = "extended-capacity emergency oxygen tank" + icon_state = "emergency_engi" + volume = 6 + +/obj/item/weapon/tank/emergency/oxygen/double + name = "double emergency oxygen tank" + icon_state = "emergency_double" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/stasis/oxygen // Stasis bags need to have initial pressure within safe bounds for human atmospheric pressure (NOT breath pressure) + name = "stasis oxygen tank" + desc = "Oxygen tank included in most stasis bag designs." + icon_state = "emergency_double" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/stasis/oxygen/Initialize() + . = ..() + src.air_contents.adjust_gas("oxygen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/nitrogen + name = "emergency nitrogen tank" + desc = "An emergency air tank hastily painted red." + icon_state = "emergency_nitro" + gauge_icon = "indicator_emergency" + +/obj/item/weapon/tank/emergency/nitrogen/Initialize() + . = ..() + src.air_contents.adjust_gas("nitrogen", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/nitrogen/double + name = "double emergency nitrogen tank" + icon_state = "emergency_double_nitrogen" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/emergency/phoron + name = "emergency phoron tank" + desc = "An emergency air tank hastily painted red." + icon_state = "emergency_nitro" + gauge_icon = "indicator_emergency" + +/obj/item/weapon/tank/emergency/phoron/Initialize() + . = ..() + src.air_contents.adjust_gas("phoron", (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/emergency/phoron/double + name = "double emergency phoron tank" + icon_state = "emergency_double_nitro" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/* + * Nitrogen + */ +/obj/item/weapon/tank/nitrogen + name = "nitrogen tank" + desc = "A tank of nitrogen." + icon_state = "oxygen_fr" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + +/obj/item/weapon/tank/nitrogen/Initialize() + . = ..() + src.air_contents.adjust_gas("nitrogen", (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C)) + +/obj/item/weapon/tank/nitrogen/examine(mob/user) + . = ..() + if(loc == user && (air_contents.gas["nitrogen"] < 10)) + . += "The meter on \the [src] indicates you are almost out of nitrogen!" + //playsound(user, 'sound/effects/alert.ogg', 50, 1) + +/obj/item/weapon/tank/stasis/nitro_cryo // Synthmorph bags need to have initial pressure within safe bounds for human atmospheric pressure, but low temperature to stop unwanted degredation. + name = "stasis cryogenic nitrogen tank" + desc = "Cryogenic Nitrogen tank included in most synthmorph bag designs." + icon_state = "emergency_double_nitro" + gauge_icon = "indicator_emergency_double" + volume = 10 + +/obj/item/weapon/tank/stasis/nitro_cryo/Initialize() + . = ..() + src.air_contents.adjust_gas_temp("nitrogen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*TN60C), TN60C) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 7fbc4ff5478..8c176ecbbb3 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -1,671 +1,671 @@ -#define TANK_IDEAL_PRESSURE 1015 //Arbitrary. - -var/list/global/tank_gauge_cache = list() - -/obj/item/weapon/tank - name = "tank" - icon = 'icons/obj/tank.dmi' - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' - ) - drop_sound = 'sound/items/drop/gascan.ogg' - pickup_sound = 'sound/items/pickup/gascan.ogg' - - var/gauge_icon = "indicator_tank" - var/last_gauge_pressure - var/gauge_cap = 6 - - slot_flags = SLOT_BACK - w_class = ITEMSIZE_NORMAL - - force = 5.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 4 - - var/datum/gas_mixture/air_contents = null - var/distribute_pressure = ONE_ATMOSPHERE - var/integrity = 20 - var/maxintegrity = 20 - var/valve_welded = 0 - var/obj/item/device/tankassemblyproxy/proxyassembly - - var/volume = 70 - var/manipulated_by = null //Used by _onclick/hud/screen_objects.dm internals to determine if someone has messed with our tank or not. - //If they have and we haven't scanned it with the PDA or gas analyzer then we might just breath whatever they put in it. - - var/failure_temp = 173 //173 deg C Borate seal (yes it should be 153 F, but that's annoying) - var/leaking = 0 - var/wired = 0 - - description_info = "These tanks are utilised to store any of the various types of gaseous substances. \ - They can be attached to various portable atmospheric devices to be filled or emptied.
                    \ -
                    \ - Each tank is fitted with an emergency relief valve. This relief valve will open if the tank is pressurised to over ~3000kPa or heated to over 173ºC. \ - The valve itself will close after expending most or all of the contents into the air.
                    \ -
                    \ - Filling a tank such that experiences ~4000kPa of pressure will cause the tank to rupture, spilling out its contents and destroying the tank. \ - Tanks filled over ~5000kPa will rupture rather violently, exploding with significant force." - - description_antag = "Each tank may be incited to burn by attaching wires and an igniter assembly, though the igniter can only be used once and the mixture only burn if the igniter pushes a flammable gas mixture above the minimum burn temperature (126ºC). \ - Wired and assembled tanks may be disarmed with a set of wirecutters. Any exploding or rupturing tank will generate shrapnel, assuming their relief valves have been welded beforehand. Even if not, they can be incited to expel hot gas on ignition if pushed above 173ºC. \ - Relatively easy to make, the single tank bomb requries no tank transfer valve, and is still a fairly formidable weapon that can be manufactured from any tank." - -/obj/item/weapon/tank/proc/init_proxy() - var/obj/item/device/tankassemblyproxy/proxy = new /obj/item/device/tankassemblyproxy(src) - proxy.tank = src - src.proxyassembly = proxy - - -/obj/item/weapon/tank/Initialize() - . = ..() - - src.init_proxy() - src.air_contents = new /datum/gas_mixture() - src.air_contents.volume = volume //liters - src.air_contents.temperature = T20C - update_gauge() - -/obj/item/weapon/tank/Destroy() - QDEL_NULL(air_contents) - - STOP_PROCESSING(SSobj, src) - QDEL_NULL(src.proxyassembly) - - if(istype(loc, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TTV = loc - TTV.remove_tank(src) - - . = ..() - -/obj/item/weapon/tank/equipped() // Note that even grabbing into a hand calls this, so it should be fine as a 'has a player touched this' - . = ..() - // An attempt at optimization. There are MANY tanks during rounds that will never get touched. - // Don't see why any of those would explode spontaneously. So only tanks that players touch get processed. - // This could be optimized more, but it's a start! - START_PROCESSING(SSobj, src) // This has a built in safety to avoid multi-processing - -/obj/item/weapon/tank/examine(mob/user) - . = ..() - if(loc == user) - var/celsius_temperature = air_contents.temperature - T0C - var/descriptive - switch(celsius_temperature) - if(300 to INFINITY) - descriptive = "furiously hot" - if(100 to 300) - descriptive = "hot" - if(80 to 100) - descriptive = "warm" - if(40 to 80) - descriptive = "lukewarm" - if(20 to 40) - descriptive = "room temperature" - if(-20 to 20) - descriptive = "cold" - else - descriptive = "bitterly cold" - . += "\The [src] feels [descriptive]." - - if(src.proxyassembly.assembly || wired) - . += "It seems to have [wired? "some wires ": ""][wired && src.proxyassembly.assembly? "and ":""][src.proxyassembly.assembly ? "some sort of assembly ":""]attached to it." - if(src.valve_welded) - . += "\The [src] emergency relief valve has been welded shut!" - - -/obj/item/weapon/tank/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if (istype(src.loc, /obj/item/assembly)) - icon = src.loc - - else if (istype(W,/obj/item/latexballon)) - var/obj/item/latexballon/LB = W - LB.blow(src) - src.add_fingerprint(user) - - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(C.use(1)) - wired = 1 - to_chat(user, "You attach the wires to the tank.") - src.add_bomb_overlay() - - if(W.is_wirecutter()) - if(wired && src.proxyassembly.assembly) - - to_chat(user, "You carefully begin clipping the wires that attach to the tank.") - if(do_after(user, 100,src)) - wired = 0 - cut_overlay("bomb_assembly") - to_chat(user, "You cut the wire and remove the device.") - - var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly - if(assy.a_left && assy.a_right) - assy.dropInto(usr.loc) - assy.master = null - src.proxyassembly.assembly = null - else - if(!src.proxyassembly.assembly.a_left) - assy.a_right.dropInto(usr.loc) - assy.a_right.holder = null - assy.a_right = null - src.proxyassembly.assembly = null - qdel(assy) - cut_overlays() - last_gauge_pressure = 0 - update_gauge() - - else - to_chat(user, "You slip and bump the igniter!") - if(prob(85)) - src.proxyassembly.receive_signal() - - else if(wired) - if(do_after(user, 10, src)) - to_chat(user, "You quickly clip the wire from the tank.") - wired = 0 - cut_overlay("bomb_assembly") - - else - to_chat(user, "There are no wires to cut!") - - - - if(istype(W, /obj/item/device/assembly_holder)) - if(wired) - to_chat(user, "You begin attaching the assembly to \the [src].") - if(do_after(user, 50, src)) - to_chat(user, "You finish attaching the assembly to \the [src].") - bombers += "[key_name(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]" - message_admins("[key_name_admin(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]") - assemble_bomb(W,user) - else - to_chat(user, "You stop attaching the assembly.") - else - to_chat(user, "You need to wire the device up first.") - - - if(istype(W, /obj/item/weapon/weldingtool/)) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(1,user)) - if(!valve_welded) - to_chat(user, "You begin welding the \the [src] emergency pressure relief valve.") - if(do_after(user, 40,src)) - to_chat(user, "You carefully weld \the [src] emergency pressure relief valve shut. \The [src] may now rupture under pressure!") - src.valve_welded = 1 - src.leaking = 0 - else - bombers += "[key_name(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]" - message_admins("[key_name_admin(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]") - if(WT.welding) - to_chat(user, "You accidentally rake \the [W] across \the [src]!") - maxintegrity -= rand(2,6) - integrity = min(integrity,maxintegrity) - src.air_contents.add_thermal_energy(rand(2000,50000)) - WT.eyecheck(user) - else - to_chat(user, "The emergency pressure relief valve has already been welded.") - add_fingerprint(user) - - - -/obj/item/weapon/tank/attack_self(mob/user as mob) - add_fingerprint(user) - if (!(src.air_contents)) - return - tgui_interact(user) - -// There's GOT to be a better way to do this - if (src.proxyassembly.assembly) - src.proxyassembly.assembly.attack_self(user) - -/obj/item/weapon/tank/tgui_state(mob/user) - return GLOB.tgui_deep_inventory_state - -/obj/item/weapon/tank/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Tank", name) - ui.open() - -/obj/item/weapon/tank/tgui_data(mob/user) - var/list/data = list() - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) - data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) - data["minReleasePressure"] = 0 - data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - - var/mob/living/carbon/C = user - if(!istype(C)) - C = loc.loc - if(!istype(C)) - return data - - if(C.internal == src) - data["connected"] = TRUE - else - data["connected"] = FALSE - - data["maskConnected"] = FALSE - if(C.wear_mask && (C.wear_mask.item_flags & AIRTIGHT)) - data["maskConnected"] = TRUE - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && (H.head.item_flags & AIRTIGHT)) - data["maskConnected"] = TRUE - - return data - -/obj/item/weapon/tank/tgui_act(action, params) - if(..()) - return TRUE - switch(action) - if("pressure") - var/pressure = params["pressure"] - if(pressure == "reset") - pressure = TANK_DEFAULT_RELEASE_PRESSURE - . = TRUE - else if(pressure == "min") - pressure = 0 - . = TRUE - else if(pressure == "max") - pressure = TANK_MAX_RELEASE_PRESSURE - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - distribute_pressure = clamp(round(pressure), 0, TANK_MAX_RELEASE_PRESSURE) - if("toggle") - toggle_valve(usr) - . = TRUE - - add_fingerprint(usr) - -/obj/item/weapon/tank/proc/toggle_valve(var/mob/user) - if(istype(loc,/mob/living/carbon)) - var/mob/living/carbon/location = loc - if(location.internal == src) - location.internal = null - location.internals.icon_state = "internal0" - to_chat(user, "You close the tank release valve.") - if (location.internals) - location.internals.icon_state = "internal0" - else - var/can_open_valve - if(location.wear_mask && (location.wear_mask.item_flags & AIRTIGHT)) - can_open_valve = 1 - else if(istype(location,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = location - if(H.head && (H.head.item_flags & AIRTIGHT)) - can_open_valve = 1 - - if(can_open_valve) - location.internal = src - to_chat(user, "You open \the [src] valve.") - if (location.internals) - location.internals.icon_state = "internal1" - else - to_chat(user, "You need something to connect to \the [src].") - - - -/obj/item/weapon/tank/remove_air(amount) - return air_contents.remove(amount) - -/obj/item/weapon/tank/proc/remove_air_by_flag(flag, amount) - return air_contents.remove_by_flag(flag, amount) - -/obj/item/weapon/tank/return_air() - return air_contents - -/obj/item/weapon/tank/assume_air(datum/gas_mixture/giver) - air_contents.merge(giver) - - check_status() - return 1 - -/obj/item/weapon/tank/proc/remove_air_volume(volume_to_return) - if(!air_contents) - return null - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < distribute_pressure) - distribute_pressure = tank_pressure - - var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - return remove_air(moles_needed) - -/obj/item/weapon/tank/process() - if(!air_contents) - return - //Allow for reactions - air_contents.react() //cooking up air tanks - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE - if(gauge_icon) - update_gauge() - check_status() - - -/obj/item/weapon/tank/proc/add_bomb_overlay() - if(src.wired) - add_overlay("bomb_assembly") - if(src.proxyassembly.assembly) - var/icon/test = getFlatIcon(src.proxyassembly.assembly) - test.Shift(SOUTH,1) - test.Shift(WEST,3) - add_overlay(test) - - -/obj/item/weapon/tank/proc/update_gauge() - var/gauge_pressure = 0 - if(air_contents) - gauge_pressure = air_contents.return_pressure() - if(gauge_pressure > TANK_IDEAL_PRESSURE) - gauge_pressure = -1 - else - gauge_pressure = round((gauge_pressure/TANK_IDEAL_PRESSURE)*gauge_cap) - - if(gauge_pressure == last_gauge_pressure) - return - - last_gauge_pressure = gauge_pressure - cut_overlays() - add_bomb_overlay() - var/indicator = "[gauge_icon][(gauge_pressure == -1) ? "overload" : gauge_pressure]" - if(!tank_gauge_cache[indicator]) - tank_gauge_cache[indicator] = image(icon, indicator) - add_overlay(tank_gauge_cache[indicator]) - - - - - - -/obj/item/weapon/tank/proc/check_status() - //Handle exploding, leaking, and rupturing of the tank - - if(!air_contents) - return 0 - - var/pressure = air_contents.return_pressure() - - - if(pressure > TANK_FRAGMENT_PRESSURE) - if(integrity <= 7) - if(!istype(src.loc,/obj/item/device/transfer_valve)) - message_admins("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") - log_game("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") - - //Give the gas a chance to build up more pressure through reacting - air_contents.react() - air_contents.react() - air_contents.react() - - pressure = air_contents.return_pressure() - var/strength = ((pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE) - - var/mult = ((src.air_contents.volume/140)**(1/2)) * (air_contents.total_moles**(2/3))/((29*0.64) **(2/3)) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles - //tanks appear to be experiencing a reduction on scale of about 0.64 total moles - - - - var/turf/simulated/T = get_turf(src) - T.hotspot_expose(src.air_contents.temperature, 70, 1) - if(!T) - return - - T.assume_air(air_contents) - explosion( - get_turf(loc), - round(min(BOMBCAP_DVSTN_RADIUS, ((mult)*strength)*0.15)), - round(min(BOMBCAP_HEAVY_RADIUS, ((mult)*strength)*0.35)), - round(min(BOMBCAP_LIGHT_RADIUS, ((mult)*strength)*0.80)), - round(min(BOMBCAP_FLASH_RADIUS, ((mult)*strength)*1.20)), - ) - - - var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) - src.fragmentate(T, num_fragments, rand(5) + 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 7,/obj/item/projectile/bullet/pellet/fragment/tank = 2,/obj/item/projectile/bullet/pellet/fragment/strong = 1)) - - if(istype(loc, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TTV = loc - TTV.remove_tank(src) - qdel(TTV) - - - if(src) - qdel(src) - - else - integrity -=7 - - - else if(pressure > TANK_RUPTURE_PRESSURE) - #ifdef FIREDBG - log_debug("[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]") - #endif - - air_contents.react() - - if(integrity <= 0) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(air_contents) - playsound(src, 'sound/weapons/Gunshot_shotgun.ogg', 20, 1) - visible_message("\icon[src][bicon(src)] \The [src] flies apart!", "You hear a bang!") - T.hotspot_expose(air_contents.temperature, 70, 1) - - - var/strength = 1+((pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE) - - var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles - - var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger - src.fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 1,/obj/item/projectile/bullet/pellet/fragment/tank = 5,/obj/item/projectile/bullet/pellet/fragment/strong = 4)) - - if(istype(loc, /obj/item/device/transfer_valve)) - var/obj/item/device/transfer_valve/TTV = loc - TTV.remove_tank(src) - - - qdel(src) - - else - if(!valve_welded) - integrity-= 3 - src.leaking = 1 - else - integrity-= 5 - - - else if(pressure > TANK_LEAK_PRESSURE || air_contents.temperature - T0C > failure_temp) - - if((integrity <= 17 || src.leaking) && !valve_welded) - var/turf/simulated/T = get_turf(src) - if(!T) - return - var/datum/gas_mixture/environment = loc.return_air() - var/env_pressure = environment.return_pressure() - var/tank_pressure = src.air_contents.return_pressure() - - var/release_ratio = 0.002 - if(tank_pressure) - release_ratio = CLAMP(sqrt(max(tank_pressure-env_pressure,0)/tank_pressure), 0.002, 1) - - var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(release_ratio) - //dynamic air release based on ambient pressure - - T.assume_air(leaked_gas) - if(!leaking) - visible_message("\icon[src][bicon(src)] \The [src] relief valve flips open with a hiss!", "You hear hissing.") - playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) - leaking = 1 - #ifdef FIREDBG - log_debug("[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]") - #endif - - - else - integrity-= 1 - - - else - if(integrity < maxintegrity) - integrity++ - if(leaking) - integrity++ - if(integrity == maxintegrity) - leaking = 0 - -///////////////////////////////// -///Prewelded tanks -///////////////////////////////// - -/obj/item/weapon/tank/phoron/welded - valve_welded = 1 -/obj/item/weapon/tank/oxygen/welded - valve_welded = 1 - - -///////////////////////////////// -///Onetankbombs (added as actual items) -///////////////////////////////// - -/obj/item/weapon/tank/proc/onetankbomb(var/fill = 1) - var/phoron_amt = 4 + rand(4) - var/oxygen_amt = 6 + rand(8) - - if(fill == 2) - phoron_amt = 10 - oxygen_amt = 15 - else if (!fill) - phoron_amt = 3 - oxygen_amt = 4.5 - - - src.air_contents.gas["phoron"] = phoron_amt - src.air_contents.gas["oxygen"] = oxygen_amt - src.air_contents.update_values() - src.valve_welded = 1 - src.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE-1 - - src.wired = 1 - - var/obj/item/device/assembly_holder/H = new(src) - src.proxyassembly.assembly = H - H.master = src.proxyassembly - - H.update_icon() - - add_overlay("bomb_assembly") - - -/obj/item/weapon/tank/phoron/onetankbomb/New() - ..() - src.onetankbomb() - -/obj/item/weapon/tank/oxygen/onetankbomb/New() - ..() - src.onetankbomb() - - -/obj/item/weapon/tank/phoron/onetankbomb/full/New() - ..() - src.onetankbomb(2) - -/obj/item/weapon/tank/oxygen/onetankbomb/full/New() - ..() - src.onetankbomb(2) - -/obj/item/weapon/tank/phoron/onetankbomb/small/New() - ..() - src.onetankbomb(0) - -/obj/item/weapon/tank/oxygen/onetankbomb/small/New() - ..() - src.onetankbomb(0) - -///////////////////////////////// -///Pulled from rewritten bomb.dm -///////////////////////////////// - -/obj/item/device/tankassemblyproxy - name = "Tank assembly proxy" - desc = "Used as a stand in to trigger single tank assemblies... but you shouldn't see this." - var/obj/item/weapon/tank/tank = null - var/obj/item/device/assembly_holder/assembly = null - - -/obj/item/device/tankassemblyproxy/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - tank.ignite() //boom (or not boom if you made shijwtty mix) - -/obj/item/device/tankassemblyproxy/Destroy() - . = ..() - tank = null - assembly = null - -/obj/item/weapon/tank/proc/assemble_bomb(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb - var/obj/item/device/assembly_holder/S = W - var/mob/M = user - if(!S.secured) //Check if the assembly is secured - return - if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - return - - - M.drop_item() //Remove the assembly from your hands - M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it - M.put_in_hands(src) //Equips the bomb if possible, or puts it on the floor. - - src.proxyassembly.assembly = S //Tell the bomb about its assembly part - S.master = src.proxyassembly //Tell the assembly about its new owner - S.forceMove(src) //Move the assembly - - src.update_icon() - - - src.add_bomb_overlay() - - return - - -/obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode - - var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly - var/ign = assy.a_right - var/obj/item/other = assy.a_left - - if (isigniter(assy.a_left)) - ign = assy.a_left - other = assy.a_right - - other.dropInto(get_turf(src)) - qdel(ign) - assy.master = null - src.proxyassembly.assembly = null - qdel(assy) - src.update_icon() - src.update_gauge() - - air_contents.add_thermal_energy(15000) - - -/obj/item/device/tankassemblyproxy/update_icon() - if(assembly) - tank.update_icon() - tank.add_overlay("bomb_assembly") - else - tank.update_icon() - tank.cut_overlay("bomb_assembly") - -/obj/item/device/tankassemblyproxy/HasProximity(turf/T, atom/movable/AM, old_loc) - assembly?.HasProximity(T, AM, old_loc) - -/obj/item/device/tankassemblyproxy/Moved(old_loc, direction, forced) - if(isturf(old_loc)) - unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(callback = /atom/proc/HasProximity) +#define TANK_IDEAL_PRESSURE 1015 //Arbitrary. + +var/list/global/tank_gauge_cache = list() + +/obj/item/weapon/tank + name = "tank" + icon = 'icons/obj/tank.dmi' + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/back/mob_teshari.dmi' + ) + drop_sound = 'sound/items/drop/gascan.ogg' + pickup_sound = 'sound/items/pickup/gascan.ogg' + + var/gauge_icon = "indicator_tank" + var/last_gauge_pressure + var/gauge_cap = 6 + + slot_flags = SLOT_BACK + w_class = ITEMSIZE_NORMAL + + force = 5.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 4 + + var/datum/gas_mixture/air_contents = null + var/distribute_pressure = ONE_ATMOSPHERE + var/integrity = 20 + var/maxintegrity = 20 + var/valve_welded = 0 + var/obj/item/device/tankassemblyproxy/proxyassembly + + var/volume = 70 + var/manipulated_by = null //Used by _onclick/hud/screen_objects.dm internals to determine if someone has messed with our tank or not. + //If they have and we haven't scanned it with the PDA or gas analyzer then we might just breath whatever they put in it. + + var/failure_temp = 173 //173 deg C Borate seal (yes it should be 153 F, but that's annoying) + var/leaking = 0 + var/wired = 0 + + description_info = "These tanks are utilised to store any of the various types of gaseous substances. \ + They can be attached to various portable atmospheric devices to be filled or emptied.
                    \ +
                    \ + Each tank is fitted with an emergency relief valve. This relief valve will open if the tank is pressurised to over ~3000kPa or heated to over 173ºC. \ + The valve itself will close after expending most or all of the contents into the air.
                    \ +
                    \ + Filling a tank such that experiences ~4000kPa of pressure will cause the tank to rupture, spilling out its contents and destroying the tank. \ + Tanks filled over ~5000kPa will rupture rather violently, exploding with significant force." + + description_antag = "Each tank may be incited to burn by attaching wires and an igniter assembly, though the igniter can only be used once and the mixture only burn if the igniter pushes a flammable gas mixture above the minimum burn temperature (126ºC). \ + Wired and assembled tanks may be disarmed with a set of wirecutters. Any exploding or rupturing tank will generate shrapnel, assuming their relief valves have been welded beforehand. Even if not, they can be incited to expel hot gas on ignition if pushed above 173ºC. \ + Relatively easy to make, the single tank bomb requries no tank transfer valve, and is still a fairly formidable weapon that can be manufactured from any tank." + +/obj/item/weapon/tank/proc/init_proxy() + var/obj/item/device/tankassemblyproxy/proxy = new /obj/item/device/tankassemblyproxy(src) + proxy.tank = src + src.proxyassembly = proxy + + +/obj/item/weapon/tank/Initialize() + . = ..() + + src.init_proxy() + src.air_contents = new /datum/gas_mixture() + src.air_contents.volume = volume //liters + src.air_contents.temperature = T20C + update_gauge() + +/obj/item/weapon/tank/Destroy() + QDEL_NULL(air_contents) + + STOP_PROCESSING(SSobj, src) + QDEL_NULL(src.proxyassembly) + + if(istype(loc, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TTV = loc + TTV.remove_tank(src) + + . = ..() + +/obj/item/weapon/tank/equipped() // Note that even grabbing into a hand calls this, so it should be fine as a 'has a player touched this' + . = ..() + // An attempt at optimization. There are MANY tanks during rounds that will never get touched. + // Don't see why any of those would explode spontaneously. So only tanks that players touch get processed. + // This could be optimized more, but it's a start! + START_PROCESSING(SSobj, src) // This has a built in safety to avoid multi-processing + +/obj/item/weapon/tank/examine(mob/user) + . = ..() + if(loc == user) + var/celsius_temperature = air_contents.temperature - T0C + var/descriptive + switch(celsius_temperature) + if(300 to INFINITY) + descriptive = "furiously hot" + if(100 to 300) + descriptive = "hot" + if(80 to 100) + descriptive = "warm" + if(40 to 80) + descriptive = "lukewarm" + if(20 to 40) + descriptive = "room temperature" + if(-20 to 20) + descriptive = "cold" + else + descriptive = "bitterly cold" + . += "\The [src] feels [descriptive]." + + if(src.proxyassembly.assembly || wired) + . += "It seems to have [wired? "some wires ": ""][wired && src.proxyassembly.assembly? "and ":""][src.proxyassembly.assembly ? "some sort of assembly ":""]attached to it." + if(src.valve_welded) + . += "\The [src] emergency relief valve has been welded shut!" + + +/obj/item/weapon/tank/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if (istype(src.loc, /obj/item/assembly)) + icon = src.loc + + else if (istype(W,/obj/item/latexballon)) + var/obj/item/latexballon/LB = W + LB.blow(src) + src.add_fingerprint(user) + + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(C.use(1)) + wired = 1 + to_chat(user, "You attach the wires to the tank.") + src.add_bomb_overlay() + + if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(wired && src.proxyassembly.assembly) + + to_chat(user, "You carefully begin clipping the wires that attach to the tank.") + if(do_after(user, 100,src)) + wired = 0 + cut_overlay("bomb_assembly") + to_chat(user, "You cut the wire and remove the device.") + + var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly + if(assy.a_left && assy.a_right) + assy.dropInto(usr.loc) + assy.master = null + src.proxyassembly.assembly = null + else + if(!src.proxyassembly.assembly.a_left) + assy.a_right.dropInto(usr.loc) + assy.a_right.holder = null + assy.a_right = null + src.proxyassembly.assembly = null + qdel(assy) + cut_overlays() + last_gauge_pressure = 0 + update_gauge() + + else + to_chat(user, "You slip and bump the igniter!") + if(prob(85)) + src.proxyassembly.receive_signal() + + else if(wired) + if(do_after(user, 10, src)) + to_chat(user, "You quickly clip the wire from the tank.") + wired = 0 + cut_overlay("bomb_assembly") + + else + to_chat(user, "There are no wires to cut!") + + + + if(istype(W, /obj/item/device/assembly_holder)) + if(wired) + to_chat(user, "You begin attaching the assembly to \the [src].") + if(do_after(user, 50, src)) + to_chat(user, "You finish attaching the assembly to \the [src].") + bombers += "[key_name(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]" + message_admins("[key_name_admin(user)] attached an assembly to a wired [src]. Temp: [src.air_contents.temperature-T0C]") + assemble_bomb(W,user) + else + to_chat(user, "You stop attaching the assembly.") + else + to_chat(user, "You need to wire the device up first.") + + + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(WT.remove_fuel(1,user)) + if(!valve_welded) + to_chat(user, "You begin welding the \the [src] emergency pressure relief valve.") + if(do_after(user, 40,src)) + to_chat(user, "You carefully weld \the [src] emergency pressure relief valve shut. \The [src] may now rupture under pressure!") + src.valve_welded = 1 + src.leaking = 0 + else + bombers += "[key_name(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]" + message_admins("[key_name_admin(user)] attempted to weld a [src]. [src.air_contents.temperature-T0C]") + if(WT.welding) + to_chat(user, "You accidentally rake \the [W] across \the [src]!") + maxintegrity -= rand(2,6) + integrity = min(integrity,maxintegrity) + src.air_contents.add_thermal_energy(rand(2000,50000)) + WT.eyecheck(user) + else + to_chat(user, "The emergency pressure relief valve has already been welded.") + add_fingerprint(user) + + + +/obj/item/weapon/tank/attack_self(mob/user as mob) + add_fingerprint(user) + if (!(src.air_contents)) + return + tgui_interact(user) + +// There's GOT to be a better way to do this + if (src.proxyassembly.assembly) + src.proxyassembly.assembly.attack_self(user) + +/obj/item/weapon/tank/tgui_state(mob/user) + return GLOB.tgui_deep_inventory_state + +/obj/item/weapon/tank/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Tank", name) + ui.open() + +/obj/item/weapon/tank/tgui_data(mob/user) + var/list/data = list() + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) + data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = 0 + data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) + + var/mob/living/carbon/C = user + if(!istype(C)) + C = loc.loc + if(!istype(C)) + return data + + if(C.internal == src) + data["connected"] = TRUE + else + data["connected"] = FALSE + + data["maskConnected"] = FALSE + if(C.wear_mask && (C.wear_mask.item_flags & AIRTIGHT)) + data["maskConnected"] = TRUE + else if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(H.head && (H.head.item_flags & AIRTIGHT)) + data["maskConnected"] = TRUE + + return data + +/obj/item/weapon/tank/tgui_act(action, params) + if(..()) + return TRUE + switch(action) + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = TANK_DEFAULT_RELEASE_PRESSURE + . = TRUE + else if(pressure == "min") + pressure = 0 + . = TRUE + else if(pressure == "max") + pressure = TANK_MAX_RELEASE_PRESSURE + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + distribute_pressure = clamp(round(pressure), 0, TANK_MAX_RELEASE_PRESSURE) + if("toggle") + toggle_valve(usr) + . = TRUE + + add_fingerprint(usr) + +/obj/item/weapon/tank/proc/toggle_valve(var/mob/user) + if(istype(loc,/mob/living/carbon)) + var/mob/living/carbon/location = loc + if(location.internal == src) + location.internal = null + location.internals.icon_state = "internal0" + to_chat(user, "You close the tank release valve.") + if (location.internals) + location.internals.icon_state = "internal0" + else + var/can_open_valve + if(location.wear_mask && (location.wear_mask.item_flags & AIRTIGHT)) + can_open_valve = 1 + else if(istype(location,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = location + if(H.head && (H.head.item_flags & AIRTIGHT)) + can_open_valve = 1 + + if(can_open_valve) + location.internal = src + to_chat(user, "You open \the [src] valve.") + if (location.internals) + location.internals.icon_state = "internal1" + else + to_chat(user, "You need something to connect to \the [src].") + + + +/obj/item/weapon/tank/remove_air(amount) + return air_contents.remove(amount) + +/obj/item/weapon/tank/proc/remove_air_by_flag(flag, amount) + return air_contents.remove_by_flag(flag, amount) + +/obj/item/weapon/tank/return_air() + return air_contents + +/obj/item/weapon/tank/assume_air(datum/gas_mixture/giver) + air_contents.merge(giver) + + check_status() + return 1 + +/obj/item/weapon/tank/proc/remove_air_volume(volume_to_return) + if(!air_contents) + return null + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < distribute_pressure) + distribute_pressure = tank_pressure + + var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + return remove_air(moles_needed) + +/obj/item/weapon/tank/process() + if(!air_contents) + return + //Allow for reactions + air_contents.react() //cooking up air tanks - add phoron and oxygen, then heat above PHORON_MINIMUM_BURN_TEMPERATURE + if(gauge_icon) + update_gauge() + check_status() + + +/obj/item/weapon/tank/proc/add_bomb_overlay() + if(src.wired) + add_overlay("bomb_assembly") + if(src.proxyassembly.assembly) + var/icon/test = getFlatIcon(src.proxyassembly.assembly) + test.Shift(SOUTH,1) + test.Shift(WEST,3) + add_overlay(test) + + +/obj/item/weapon/tank/proc/update_gauge() + var/gauge_pressure = 0 + if(air_contents) + gauge_pressure = air_contents.return_pressure() + if(gauge_pressure > TANK_IDEAL_PRESSURE) + gauge_pressure = -1 + else + gauge_pressure = round((gauge_pressure/TANK_IDEAL_PRESSURE)*gauge_cap) + + if(gauge_pressure == last_gauge_pressure) + return + + last_gauge_pressure = gauge_pressure + cut_overlays() + add_bomb_overlay() + var/indicator = "[gauge_icon][(gauge_pressure == -1) ? "overload" : gauge_pressure]" + if(!tank_gauge_cache[indicator]) + tank_gauge_cache[indicator] = image(icon, indicator) + add_overlay(tank_gauge_cache[indicator]) + + + + + + +/obj/item/weapon/tank/proc/check_status() + //Handle exploding, leaking, and rupturing of the tank + + if(!air_contents) + return 0 + + var/pressure = air_contents.return_pressure() + + + if(pressure > TANK_FRAGMENT_PRESSURE) + if(integrity <= 7) + if(!istype(src.loc,/obj/item/device/transfer_valve)) + message_admins("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") + log_game("Explosive tank rupture! last key to touch the tank was [src.fingerprintslast].") + + //Give the gas a chance to build up more pressure through reacting + air_contents.react() + air_contents.react() + air_contents.react() + + pressure = air_contents.return_pressure() + var/strength = ((pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE) + + var/mult = ((src.air_contents.volume/140)**(1/2)) * (air_contents.total_moles**(2/3))/((29*0.64) **(2/3)) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + + + + var/turf/simulated/T = get_turf(src) + T.hotspot_expose(src.air_contents.temperature, 70, 1) + if(!T) + return + + T.assume_air(air_contents) + explosion( + get_turf(loc), + round(min(BOMBCAP_DVSTN_RADIUS, ((mult)*strength)*0.15)), + round(min(BOMBCAP_HEAVY_RADIUS, ((mult)*strength)*0.35)), + round(min(BOMBCAP_LIGHT_RADIUS, ((mult)*strength)*0.80)), + round(min(BOMBCAP_FLASH_RADIUS, ((mult)*strength)*1.20)), + ) + + + var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) + src.fragmentate(T, num_fragments, rand(5) + 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 7,/obj/item/projectile/bullet/pellet/fragment/tank = 2,/obj/item/projectile/bullet/pellet/fragment/strong = 1)) + + if(istype(loc, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TTV = loc + TTV.remove_tank(src) + qdel(TTV) + + + if(src) + qdel(src) + + else + integrity -=7 + + + else if(pressure > TANK_RUPTURE_PRESSURE) + #ifdef FIREDBG + log_debug("[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]") + #endif + + air_contents.react() + + if(integrity <= 0) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(air_contents) + playsound(src, 'sound/weapons/Gunshot_shotgun.ogg', 20, 1) + visible_message("\icon[src][bicon(src)] \The [src] flies apart!", "You hear a bang!") + T.hotspot_expose(air_contents.temperature, 70, 1) + + + var/strength = 1+((pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE) + + var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles + + var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger + src.fragmentate(T, num_fragments, 7, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 1,/obj/item/projectile/bullet/pellet/fragment/tank = 5,/obj/item/projectile/bullet/pellet/fragment/strong = 4)) + + if(istype(loc, /obj/item/device/transfer_valve)) + var/obj/item/device/transfer_valve/TTV = loc + TTV.remove_tank(src) + + + qdel(src) + + else + if(!valve_welded) + integrity-= 3 + src.leaking = 1 + else + integrity-= 5 + + + else if(pressure > TANK_LEAK_PRESSURE || air_contents.temperature - T0C > failure_temp) + + if((integrity <= 17 || src.leaking) && !valve_welded) + var/turf/simulated/T = get_turf(src) + if(!T) + return + var/datum/gas_mixture/environment = loc.return_air() + var/env_pressure = environment.return_pressure() + var/tank_pressure = src.air_contents.return_pressure() + + var/release_ratio = 0.002 + if(tank_pressure) + release_ratio = CLAMP(sqrt(max(tank_pressure-env_pressure,0)/tank_pressure), 0.002, 1) + + var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(release_ratio) + //dynamic air release based on ambient pressure + + T.assume_air(leaked_gas) + if(!leaking) + visible_message("\icon[src][bicon(src)] \The [src] relief valve flips open with a hiss!", "You hear hissing.") + playsound(src, 'sound/effects/spray.ogg', 10, 1, -3) + leaking = 1 + #ifdef FIREDBG + log_debug("[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]") + #endif + + + else + integrity-= 1 + + + else + if(integrity < maxintegrity) + integrity++ + if(leaking) + integrity++ + if(integrity == maxintegrity) + leaking = 0 + +///////////////////////////////// +///Prewelded tanks +///////////////////////////////// + +/obj/item/weapon/tank/phoron/welded + valve_welded = 1 +/obj/item/weapon/tank/oxygen/welded + valve_welded = 1 + + +///////////////////////////////// +///Onetankbombs (added as actual items) +///////////////////////////////// + +/obj/item/weapon/tank/proc/onetankbomb(var/fill = 1) + var/phoron_amt = 4 + rand(4) + var/oxygen_amt = 6 + rand(8) + + if(fill == 2) + phoron_amt = 10 + oxygen_amt = 15 + else if (!fill) + phoron_amt = 3 + oxygen_amt = 4.5 + + + src.air_contents.gas["phoron"] = phoron_amt + src.air_contents.gas["oxygen"] = oxygen_amt + src.air_contents.update_values() + src.valve_welded = 1 + src.air_contents.temperature = PHORON_MINIMUM_BURN_TEMPERATURE-1 + + src.wired = 1 + + var/obj/item/device/assembly_holder/H = new(src) + src.proxyassembly.assembly = H + H.master = src.proxyassembly + + H.update_icon() + + add_overlay("bomb_assembly") + + +/obj/item/weapon/tank/phoron/onetankbomb/New() + ..() + src.onetankbomb() + +/obj/item/weapon/tank/oxygen/onetankbomb/New() + ..() + src.onetankbomb() + + +/obj/item/weapon/tank/phoron/onetankbomb/full/New() + ..() + src.onetankbomb(2) + +/obj/item/weapon/tank/oxygen/onetankbomb/full/New() + ..() + src.onetankbomb(2) + +/obj/item/weapon/tank/phoron/onetankbomb/small/New() + ..() + src.onetankbomb(0) + +/obj/item/weapon/tank/oxygen/onetankbomb/small/New() + ..() + src.onetankbomb(0) + +///////////////////////////////// +///Pulled from rewritten bomb.dm +///////////////////////////////// + +/obj/item/device/tankassemblyproxy + name = "Tank assembly proxy" + desc = "Used as a stand in to trigger single tank assemblies... but you shouldn't see this." + var/obj/item/weapon/tank/tank = null + var/obj/item/device/assembly_holder/assembly = null + + +/obj/item/device/tankassemblyproxy/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. + tank.ignite() //boom (or not boom if you made shijwtty mix) + +/obj/item/device/tankassemblyproxy/Destroy() + . = ..() + tank = null + assembly = null + +/obj/item/weapon/tank/proc/assemble_bomb(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb + var/obj/item/device/assembly_holder/S = W + var/mob/M = user + if(!S.secured) //Check if the assembly is secured + return + if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + return + + + M.drop_item() //Remove the assembly from your hands + M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it + M.put_in_hands(src) //Equips the bomb if possible, or puts it on the floor. + + src.proxyassembly.assembly = S //Tell the bomb about its assembly part + S.master = src.proxyassembly //Tell the assembly about its new owner + S.forceMove(src) //Move the assembly + + src.update_icon() + + + src.add_bomb_overlay() + + return + + +/obj/item/weapon/tank/proc/ignite() //This happens when a bomb is told to explode + + var/obj/item/device/assembly_holder/assy = src.proxyassembly.assembly + var/ign = assy.a_right + var/obj/item/other = assy.a_left + + if (isigniter(assy.a_left)) + ign = assy.a_left + other = assy.a_right + + other.dropInto(get_turf(src)) + qdel(ign) + assy.master = null + src.proxyassembly.assembly = null + qdel(assy) + src.update_icon() + src.update_gauge() + + air_contents.add_thermal_energy(15000) + + +/obj/item/device/tankassemblyproxy/update_icon() + if(assembly) + tank.update_icon() + tank.add_overlay("bomb_assembly") + else + tank.update_icon() + tank.cut_overlay("bomb_assembly") + +/obj/item/device/tankassemblyproxy/HasProximity(turf/T, atom/movable/AM, old_loc) + assembly?.HasProximity(T, AM, old_loc) + +/obj/item/device/tankassemblyproxy/Moved(old_loc, direction, forced) + if(isturf(old_loc)) + unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(callback = /atom/proc/HasProximity) diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index bbe67d05a71..3f78b468a9c 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -1,181 +1,181 @@ -/* Teleportation devices. - * Contains: - * Locator - * Hand-tele - */ - -/* - * Locator - */ -/obj/item/weapon/locator - name = "locator" - desc = "Used to track those with locater implants." - icon = 'icons/obj/device.dmi' - icon_state = "locator" - var/temp = null - var/frequency = 1451 - var/broadcasting = null - var/listening = 1.0 - w_class = ITEMSIZE_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 400) - -/obj/item/weapon/locator/attack_self(mob/user as mob) - user.set_machine(src) - var/dat - if (src.temp) - dat = "[src.temp]

                    Clear" - else - dat = {" -Persistent Signal Locator
                    -Frequency: -- -- [format_frequency(src.frequency)] -+ -+
                    - -Refresh"} - user << browse(dat, "window=radio") - onclose(user, "radio") - return - -/obj/item/weapon/locator/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - var/turf/current_location = get_turf(usr)//What turf is the user on? - if(!current_location||current_location.z==2)//If turf was not found or they're on z level 2. - to_chat(usr, "The [src] is malfunctioning.") - return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if (href_list["refresh"]) - src.temp = "Persistent Signal Locator
                    " - var/turf/sr = get_turf(src) - - if (sr) - src.temp += "Located Beacons:
                    " - - for(var/obj/item/device/radio/beacon/W in all_beacons) - if (W.frequency == src.frequency) - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - if (direct < 20) - direct = "weak" - else - direct = "very weak" - src.temp += "[W.code]-[dir2text(get_dir(sr, tr))]-[direct]
                    " - - src.temp += "Extraneous Signals:
                    " - for (var/obj/item/weapon/implant/tracking/W in all_tracking_implants) - if (!W.implanted || !(istype(W.loc,/obj/item/organ/external) || ismob(W.loc) || W.malfunction)) - continue - - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 20) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - direct = "weak" - src.temp += "[W.id]-[dir2text(get_dir(sr, tr))]-[direct]
                    " - - src.temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.

                    Refresh
                    " - else - src.temp += "Processing Error: Unable to locate orbital position.
                    " - else - if (href_list["freq"]) - src.frequency += text2num(href_list["freq"]) - src.frequency = sanitize_frequency(src.frequency) - else - if (href_list["temp"]) - src.temp = null - if (istype(src.loc, /mob)) - attack_self(src.loc) - else - for(var/mob/M in viewers(1, src)) - if (M.client) - src.attack_self(M) - return - - -/* - * Hand-tele - */ -/obj/item/weapon/hand_tele - name = "hand tele" - desc = "A portable item using blue-space technology." - icon = 'icons/obj/device.dmi' - icon_state = "hand_tele" - item_state = "electronic" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 3 - throw_range = 5 - origin_tech = list(TECH_MAGNET = 1, TECH_BLUESPACE = 3) - matter = list(MAT_STEEL = 10000) - preserve_item = 1 - -/obj/item/weapon/hand_tele/attack_self(mob/user as mob) - var/turf/current_location = get_turf(user)//What turf is the user on? - if(!current_location || (current_location.z in using_map.admin_levels) || current_location.block_tele)//If turf was not found or they're on z level 2 or >7 which does not currently exist. - to_chat(user, "\The [src] is malfunctioning.") - return - var/list/L = list( ) - for(var/obj/machinery/teleport/hub/R in machines) - var/obj/machinery/computer/teleporter/com - var/obj/machinery/teleport/station/station - for(var/direction in cardinal) - station = locate(/obj/machinery/teleport/station, get_step(R, direction)) - if(station) - for(direction in cardinal) - com = locate(/obj/machinery/computer/teleporter, get_step(station, direction)) - if(com) - break - break - if (istype(com, /obj/machinery/computer/teleporter) && com.teleport_control.locked && !com.one_time_use) - if(R.icon_state == "tele1") - L["[com.id] (Active)"] = com.teleport_control.locked - else - L["[com.id] (Inactive)"] = com.teleport_control.locked - var/list/turfs = list( ) - for(var/turf/T in orange(10)) - if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb - if(T.y>world.maxy-8 || T.y<8) continue - if(T.block_tele) continue - turfs += T - if(turfs.len) - L["None (Dangerous)"] = pick(turfs) - var/t1 = tgui_input_list(user, "Please select a teleporter to lock in on.", "Hand Teleporter", L) - if(!t1) - return - if ((user.get_active_hand() != src || user.stat || user.restrained())) - return - var/count = 0 //num of portals from this teleport in world - for(var/obj/effect/portal/PO in all_portals) - if(PO.creator == src) count++ - if(count >= 3) - user.show_message("\The [src] is recharging!") - return - var/T = L[t1] - for(var/mob/O in hearers(user, null)) - O.show_message("Locked In.", 2) - var/obj/effect/portal/P = new /obj/effect/portal( get_turf(src) ) - P.target = T - P.creator = src - src.add_fingerprint(user) - return +/* Teleportation devices. + * Contains: + * Locator + * Hand-tele + */ + +/* + * Locator + */ +/obj/item/weapon/locator + name = "locator" + desc = "Used to track those with locater implants." + icon = 'icons/obj/device.dmi' + icon_state = "locator" + var/temp = null + var/frequency = 1451 + var/broadcasting = null + var/listening = 1.0 + w_class = ITEMSIZE_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 400) + +/obj/item/weapon/locator/attack_self(mob/user as mob) + user.set_machine(src) + var/dat + if (src.temp) + dat = "[src.temp]

                    Clear" + else + dat = {" +Persistent Signal Locator
                    +Frequency: +- +- [format_frequency(src.frequency)] ++ ++
                    + +Refresh"} + user << browse(dat, "window=radio") + onclose(user, "radio") + return + +/obj/item/weapon/locator/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained()) + return + var/turf/current_location = get_turf(usr)//What turf is the user on? + if(!current_location||current_location.z==2)//If turf was not found or they're on z level 2. + to_chat(usr, "The [src] is malfunctioning.") + return + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if (href_list["refresh"]) + src.temp = "Persistent Signal Locator
                    " + var/turf/sr = get_turf(src) + + if (sr) + src.temp += "Located Beacons:
                    " + + for(var/obj/item/device/radio/beacon/W in all_beacons) + if (W.frequency == src.frequency) + var/turf/tr = get_turf(W) + if (tr.z == sr.z && tr) + var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + if (direct < 5) + direct = "very strong" + else + if (direct < 10) + direct = "strong" + else + if (direct < 20) + direct = "weak" + else + direct = "very weak" + src.temp += "[W.code]-[dir2text(get_dir(sr, tr))]-[direct]
                    " + + src.temp += "Extraneous Signals:
                    " + for (var/obj/item/weapon/implant/tracking/W in all_tracking_implants) + if (!W.implanted || !(istype(W.loc,/obj/item/organ/external) || ismob(W.loc) || W.malfunction)) + continue + + var/turf/tr = get_turf(W) + if (tr.z == sr.z && tr) + var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + if (direct < 20) + if (direct < 5) + direct = "very strong" + else + if (direct < 10) + direct = "strong" + else + direct = "weak" + src.temp += "[W.id]-[dir2text(get_dir(sr, tr))]-[direct]
                    " + + src.temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.

                    Refresh
                    " + else + src.temp += "Processing Error: Unable to locate orbital position.
                    " + else + if (href_list["freq"]) + src.frequency += text2num(href_list["freq"]) + src.frequency = sanitize_frequency(src.frequency) + else + if (href_list["temp"]) + src.temp = null + if (istype(src.loc, /mob)) + attack_self(src.loc) + else + for(var/mob/M in viewers(1, src)) + if (M.client) + src.attack_self(M) + return + + +/* + * Hand-tele + */ +/obj/item/weapon/hand_tele + name = "hand tele" + desc = "A portable item using blue-space technology." + icon = 'icons/obj/device.dmi' + icon_state = "hand_tele" + item_state = "electronic" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 3 + throw_range = 5 + origin_tech = list(TECH_MAGNET = 1, TECH_BLUESPACE = 3) + matter = list(MAT_STEEL = 10000) + preserve_item = 1 + +/obj/item/weapon/hand_tele/attack_self(mob/user as mob) + var/turf/current_location = get_turf(user)//What turf is the user on? + if(!current_location || (current_location.z in using_map.admin_levels) || current_location.block_tele)//If turf was not found or they're on z level 2 or >7 which does not currently exist. + to_chat(user, "\The [src] is malfunctioning.") + return + var/list/L = list( ) + for(var/obj/machinery/teleport/hub/R in machines) + var/obj/machinery/computer/teleporter/com + var/obj/machinery/teleport/station/station + for(var/direction in cardinal) + station = locate(/obj/machinery/teleport/station, get_step(R, direction)) + if(station) + for(direction in cardinal) + com = locate(/obj/machinery/computer/teleporter, get_step(station, direction)) + if(com) + break + break + if (istype(com, /obj/machinery/computer/teleporter) && com.teleport_control.locked && !com.one_time_use) + if(R.icon_state == "tele1") + L["[com.id] (Active)"] = com.teleport_control.locked + else + L["[com.id] (Inactive)"] = com.teleport_control.locked + var/list/turfs = list( ) + for(var/turf/T in orange(10)) + if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb + if(T.y>world.maxy-8 || T.y<8) continue + if(T.block_tele) continue + turfs += T + if(turfs.len) + L["None (Dangerous)"] = pick(turfs) + var/t1 = tgui_input_list(user, "Please select a teleporter to lock in on.", "Hand Teleporter", L) + if(!t1) + return + if ((user.get_active_hand() != src || user.stat || user.restrained())) + return + var/count = 0 //num of portals from this teleport in world + for(var/obj/effect/portal/PO in all_portals) + if(PO.creator == src) count++ + if(count >= 3) + user.show_message("\The [src] is recharging!") + return + var/T = L[t1] + for(var/mob/O in hearers(user, null)) + O.show_message("Locked In.", 2) + var/obj/effect/portal/P = new /obj/effect/portal( get_turf(src) ) + P.target = T + P.creator = src + src.add_fingerprint(user) + return diff --git a/code/game/objects/items/weapons/tools/crowbar.dm b/code/game/objects/items/weapons/tools/crowbar.dm index 06c1e53e07e..8d7c1add855 100644 --- a/code/game/objects/items/weapons/tools/crowbar.dm +++ b/code/game/objects/items/weapons/tools/crowbar.dm @@ -72,34 +72,40 @@ toolspeed = 0.5 /obj/item/weapon/tool/crowbar/power - name = "jaws of life" - desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a prying head." - icon_state = "jaws_pry" - item_state = "jawsoflife" - matter = list(MAT_METAL=150, MAT_SILVER=50) - origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + name = "power pryer" + desc = "You shouldn't see this." usesound = 'sound/items/jaws_pry.ogg' force = 15 toolspeed = 0.25 - var/obj/item/weapon/tool/wirecutters/power/counterpart = null -/obj/item/weapon/tool/crowbar/power/New(newloc, no_counterpart = TRUE) - ..(newloc) - if(!counterpart && no_counterpart) - counterpart = new(src, FALSE) - counterpart.counterpart = src +/* + * Prybar + */ -/obj/item/weapon/tool/crowbar/power/Destroy() - if(counterpart) - counterpart.counterpart = null // So it can qdel cleanly. - QDEL_NULL(counterpart) - return ..() +/obj/item/weapon/tool/prybar + name = "pry bar" + desc = "A steel bar with a wedge, designed specifically for opening unpowered doors in an emergency. It comes in a variety of configurations - collect them all!" + icon = 'icons/obj/tools_vr.dmi' + icon_state = "prybar" + item_state = "crowbar" + slot_flags = SLOT_BELT + force = 4 + throwforce = 5 + pry = 1 + w_class = ITEMSIZE_SMALL + origin_tech = list(TECH_ENGINEERING = 1) + matter = list(MAT_STEEL = 30) + attack_verb = list("whapped", "smacked", "swatted", "thwacked", "hit") + usesound = 'sound/items/crowbar.ogg' + toolspeed = 1 + var/random_color = TRUE + +/obj/item/weapon/tool/prybar/red + icon_state = "prybar_red" + item_state = "crowbar_red" + random_color = FALSE -/obj/item/weapon/tool/crowbar/power/attack_self(mob/user) - playsound(src, 'sound/items/change_jaws.ogg', 50, 1) - user.drop_item(src) - counterpart.forceMove(get_turf(src)) - counterpart.persist_storable = persist_storable - src.forceMove(counterpart) - user.put_in_active_hand(counterpart) - to_chat(user, "You attach the cutting jaws to [src].") +/obj/item/weapon/tool/prybar/New() + if(random_color) + icon_state = "prybar[pick("","_green","_aubergine","_blue")]" + . = ..() \ No newline at end of file diff --git a/code/game/objects/items/weapons/tools/crowbar_vr.dm b/code/game/objects/items/weapons/tools/crowbar_vr.dm deleted file mode 100644 index b846a0e294d..00000000000 --- a/code/game/objects/items/weapons/tools/crowbar_vr.dm +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Prybar - */ - -/obj/item/weapon/tool/prybar - name = "pry bar" - desc = "A steel bar with a wedge, designed specifically for opening unpowered doors in an emergency. It comes in a variety of configurations - collect them all!" - icon = 'icons/obj/tools_vr.dmi' - icon_state = "prybar" - item_state = "crowbar" - slot_flags = SLOT_BELT - force = 4 - throwforce = 5 - pry = 1 - w_class = ITEMSIZE_SMALL - origin_tech = list(TECH_ENGINEERING = 1) - matter = list(MAT_STEEL = 30) - attack_verb = list("whapped", "smacked", "swatted", "thwacked", "hit") - usesound = 'sound/items/crowbar.ogg' - toolspeed = 1 - var/random_color = TRUE - -/obj/item/weapon/tool/prybar/red - icon_state = "prybar_red" - item_state = "crowbar_red" - random_color = FALSE - -/obj/item/weapon/tool/prybar/New() - if(random_color) - icon_state = "prybar[pick("","_green","_aubergine","_blue")]" - . = ..() diff --git a/code/game/objects/items/weapons/tools/screwdriver.dm b/code/game/objects/items/weapons/tools/screwdriver.dm index 4598b698032..55e78d71de7 100644 --- a/code/game/objects/items/weapons/tools/screwdriver.dm +++ b/code/game/objects/items/weapons/tools/screwdriver.dm @@ -106,42 +106,10 @@ toolspeed = 0.5 /obj/item/weapon/tool/screwdriver/power - name = "hand drill" - desc = "A simple powered hand drill. It's fitted with a screw bit." - icon_state = "drill_screw" - item_state = "drill" - matter = list(MAT_STEEL = 150, MAT_SILVER = 50) - origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - slot_flags = SLOT_BELT + name = "power screwdriver" + desc = "You shouldn't see this." force = 8 - w_class = ITEMSIZE_SMALL - throwforce = 8 - throw_speed = 2 - throw_range = 3//it's heavier than a screw driver/wrench, so it does more damage, but can't be thrown as far attack_verb = list("drilled", "screwed", "jabbed", "whacked") hitsound = 'sound/items/drill_hit.ogg' usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.25 - random_color = FALSE - var/obj/item/weapon/tool/wrench/power/counterpart = null - -/obj/item/weapon/tool/screwdriver/power/New(newloc, no_counterpart = TRUE) - ..(newloc) - if(!counterpart && no_counterpart) - counterpart = new(src, FALSE) - counterpart.counterpart = src - -/obj/item/weapon/tool/screwdriver/power/Destroy() - if(counterpart) - counterpart.counterpart = null // So it can qdel cleanly. - QDEL_NULL(counterpart) - return ..() - -/obj/item/weapon/tool/screwdriver/power/attack_self(mob/user) - playsound(src,'sound/items/change_drill.ogg',50,1) - user.drop_item(src) - counterpart.forceMove(get_turf(src)) - counterpart.persist_storable = persist_storable - src.forceMove(counterpart) - user.put_in_active_hand(counterpart) - to_chat(user, "You attach the bolt driver bit to [src].") + toolspeed = 0.25 \ No newline at end of file diff --git a/code/game/objects/items/weapons/tools/transforming.dm b/code/game/objects/items/weapons/tools/transforming.dm new file mode 100644 index 00000000000..9089a71423e --- /dev/null +++ b/code/game/objects/items/weapons/tools/transforming.dm @@ -0,0 +1,184 @@ +/obj/item/weapon/tool/transforming + name = "transforming tool" + desc = "You should never see this..." + var/list/possible_tooltypes = list() + var/current_tooltype = 1 + var/obj/item/weapon/weldingtool/welder + var/weldertype = /obj/item/weapon/weldingtool/dummy + +/obj/item/weapon/tool/transforming/New(newloc, no_counterpart = TRUE) + ..(newloc) + if(TOOL_WELDER in possible_tooltypes) + welder = new weldertype(src) + on_tool_switch() + +/obj/item/weapon/tool/transforming/Destroy() + if(welder) + QDEL_NULL(welder) + ..() + +/obj/item/weapon/tool/transforming/get_welder() + return welder + +/obj/item/weapon/tool/transforming/attack_self(mob/user) + if(!possible_tooltypes.len || possible_tooltypes.len < 2) + return + if(current_tooltype == possible_tooltypes.len) + current_tooltype = 1 + else + current_tooltype++ + + on_tool_switch(user) + +/obj/item/weapon/tool/transforming/proc/on_tool_switch(var/mob/user) + return + +/obj/item/weapon/tool/transforming/jawsoflife + name = "jaws of life" + desc = "A set of jaws of life, compressed through the magic of science." + icon = 'icons/obj/tools.dmi' + icon_state = "jaws_pry" + item_state = "jawsoflife" + origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + matter = list(MAT_METAL=150, MAT_SILVER=50) + usesound = 'sound/items/jaws_pry.ogg' + force = 15 + toolspeed = 0.25 + sharp = TRUE + edge = TRUE + attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked", "pinched", "nipped") + possible_tooltypes = list(TOOL_CROWBAR,TOOL_WIRECUTTER) + +/obj/item/weapon/tool/transforming/jawsoflife/on_tool_switch(var/mob/user) + switch(possible_tooltypes[current_tooltype]) + if(TOOL_CROWBAR) + desc = initial(desc) + " It's fitted with a prying head." + icon_state = "jaws_pry" + usesound = 'sound/items/jaws_pry.ogg' + pry = 1 + tool_qualities = list(TOOL_CROWBAR) + if(user) + playsound(src, 'sound/items/change_jaws.ogg', 50, 1) + to_chat(user, "You attach the pry jaws to [src].") + if(TOOL_WIRECUTTER) + desc = initial(desc) + " It's fitted with a cutting head." + icon_state = "jaws_cutter" + usesound = 'sound/items/jaws_cut.ogg' + pry = 0 + tool_qualities = list(TOOL_WIRECUTTER) + if(user) + playsound(src, 'sound/items/change_jaws.ogg', 50, 1) + to_chat(user, "You attach the cutting jaws to [src].") + +/obj/item/weapon/tool/transforming/powerdrill + name = "hand drill" + desc = "A simple powered hand drill." + icon = 'icons/obj/tools.dmi' + icon_state = "drill_bolt" + item_state = "drill" + origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) + matter = list(MAT_STEEL = 150, MAT_SILVER = 50) + hitsound = 'sound/items/drill_hit.ogg' + usesound = 'sound/items/drill_use.ogg' + force = 8 + throwforce = 8 + toolspeed = 0.25 + sharp = FALSE + edge = FALSE + attack_verb = list("drilled", "screwed", "jabbed", "whacked") + possible_tooltypes = list(TOOL_WRENCH,TOOL_SCREWDRIVER) + +/obj/item/weapon/tool/transforming/powerdrill/on_tool_switch(var/mob/user) + switch(possible_tooltypes[current_tooltype]) + if(TOOL_WRENCH) + desc = initial(desc) + " It's fitted with a bolt driver." + icon_state = "drill_bolt" + sharp = FALSE + tool_qualities = list(TOOL_WRENCH) + if(user) + playsound(src,'sound/items/change_drill.ogg',50,1) + to_chat(user, "You attach the bolt driver to [src].") + if(TOOL_SCREWDRIVER) + desc = initial(desc) + " It's fitted with a screw driver." + icon_state = "drill_screw" + sharp = TRUE + tool_qualities = list(TOOL_SCREWDRIVER) + if(user) + playsound(src,'sound/items/change_drill.ogg',50,1) + to_chat(user, "You attach the screw driver to [src].") + +/obj/item/weapon/tool/transforming/altevian + name = "Hull Systems Omni-Tool" + desc = "A big and bulky tool, used by Altevians for engineering duties. It's able to do the job of any regular tool while scaled up to a comically large size. It seems nanites are in play to help with adjusting the tip and handling some of the heavy lifting when in use." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "altevian-wrench" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee_vr.dmi', + ) + slot_flags = SLOT_BACK + w_class = ITEMSIZE_HUGE + force = 25 + throwforce = 15 + toolspeed = 0.25 + sharp = FALSE + edge = FALSE + attack_verb = list("whacked", "slammed", "bashed", "wrenched", "fixed", "bolted", "clonked", "bonked") + hitsound = 'sound/weapons/smash.ogg' + possible_tooltypes = list(TOOL_WRENCH,TOOL_CROWBAR,TOOL_WIRECUTTER,TOOL_SCREWDRIVER,TOOL_MULTITOOL,TOOL_WELDER) + weldertype = /obj/item/weapon/weldingtool/dummy/altevian + +/obj/item/weapon/tool/transforming/altevian/on_tool_switch(var/mob/user) + switch(possible_tooltypes[current_tooltype]) + if(TOOL_WRENCH) + desc = initial(desc) + " It's currently in bolting mode." + icon_state = "altevian-wrench" + usesound = 'sound/items/ratchet.ogg' + tool_qualities = list(TOOL_WRENCH) + if(user) + playsound(src,'sound/items/ratchet.ogg',50,1) + to_chat(user, "You reconfigure [src] into bolting mode.") + if(TOOL_CROWBAR) + desc = initial(desc) + " It's currently in prying mode." + icon_state = "altevian-crowbar" + usesound = 'sound/items/crowbar.ogg' + tool_qualities = list(TOOL_CROWBAR) + if(user) + playsound(src,'sound/items/ratchet.ogg',50,1) + to_chat(user, "You reconfigure [src] into prying mode.") + if(TOOL_WIRECUTTER) + desc = initial(desc) + " It's currently in cutting mode." + icon_state = "altevian-wirecutter" + usesound = 'sound/items/wirecutter.ogg' + tool_qualities = list(TOOL_WIRECUTTER) + if(user) + playsound(src,'sound/items/ratchet.ogg',50,1) + to_chat(user, "You reconfigure [src] into cutting mode.") + if(TOOL_SCREWDRIVER) + desc = initial(desc) + " It's currently in screwing mode." + icon_state = "altevian-screwdriver" + usesound = 'sound/items/screwdriver.ogg' + tool_qualities = list(TOOL_SCREWDRIVER) + if(user) + playsound(src,'sound/items/ratchet.ogg',50,1) + to_chat(user, "You reconfigure [src] into screwing mode.") + if(TOOL_MULTITOOL) + desc = initial(desc) + " It's currently in pulsing mode." + icon_state = "altevian-pulser" + usesound = 'sound/items/screwdriver.ogg' + tool_qualities = list(TOOL_MULTITOOL) + if(user) + playsound(src,'sound/items/ratchet.ogg',50,1) + to_chat(user, "You reconfigure [src] into pulsing mode.") + if(TOOL_WELDER) + desc = initial(desc) + " It's currently in welding mode." + icon_state = "altevian-welder-on" + welder.usesound = 'sound/items/Welder2.ogg' + usesound = 'sound/items/Welder2.ogg' + tool_qualities = list(TOOL_WELDER) + if(user) + playsound(src,'sound/items/ratchet.ogg',50,1) + to_chat(user, "You reconfigure [src] into welding mode.") + +/obj/item/weapon/weldingtool/dummy/altevian + toolspeed = 0.25 diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 8fbd4fe4d44..133570a61d2 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -41,6 +41,7 @@ toolspeed = 1 drop_sound = 'sound/items/drop/weldingtool.ogg' pickup_sound = 'sound/items/pickup/weldingtool.ogg' + tool_qualities = list(TOOL_WELDER) /obj/item/weapon/weldingtool/Initialize() . = ..() @@ -58,6 +59,9 @@ STOP_PROCESSING(SSobj, src) return ..() +/obj/item/weapon/weldingtool/get_welder() + return src + /obj/item/weapon/weldingtool/examine(mob/user) . = ..() if(max_fuel && loc == user) @@ -690,4 +694,22 @@ else setWelding(FALSE, M.occupant) + +/obj/item/weapon/weldingtool/dummy + name = "dummy welding tool" + desc = "you shouldn't be reading this. Tell a dev!" + welding = TRUE + +/obj/item/weapon/weldingtool/dummy/process() + return + +/obj/item/weapon/weldingtool/dummy/get_fuel() + return get_max_fuel() + +/obj/item/weapon/weldingtool/dummy/remove_fuel(var/amount = 1, var/mob/M = null) + return TRUE + +/obj/item/weapon/weldingtool/dummy/isOn() + return TRUE + #undef WELDER_FUEL_BURN_INTERVAL diff --git a/code/game/objects/items/weapons/tools/wirecutters.dm b/code/game/objects/items/weapons/tools/wirecutters.dm index 2c43b963e15..8b71f8af848 100644 --- a/code/game/objects/items/weapons/tools/wirecutters.dm +++ b/code/game/objects/items/weapons/tools/wirecutters.dm @@ -98,35 +98,8 @@ reach = 2 /obj/item/weapon/tool/wirecutters/power - name = "jaws of life" - desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a cutting head." - icon_state = "jaws_cutter" - item_state = "jawsoflife" - origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) - matter = list(MAT_METAL=150, MAT_SILVER=50) + name = "power cutters" + desc = "You shouldn't see this." usesound = 'sound/items/jaws_cut.ogg' force = 15 - toolspeed = 0.25 - random_color = FALSE - var/obj/item/weapon/tool/crowbar/power/counterpart = null - -/obj/item/weapon/tool/wirecutters/power/New(newloc, no_counterpart = TRUE) - ..(newloc) - if(!counterpart && no_counterpart) - counterpart = new(src, FALSE) - counterpart.counterpart = src - -/obj/item/weapon/tool/wirecutters/power/Destroy() - if(counterpart) - counterpart.counterpart = null // So it can qdel cleanly. - QDEL_NULL(counterpart) - return ..() - -/obj/item/weapon/tool/wirecutters/power/attack_self(mob/user) - playsound(src, 'sound/items/change_jaws.ogg', 50, 1) - user.drop_item(src) - counterpart.forceMove(get_turf(src)) - counterpart.persist_storable = persist_storable - src.forceMove(counterpart) - user.put_in_active_hand(counterpart) - to_chat(user, "You attach the pry jaws to [src].") + toolspeed = 0.25 \ No newline at end of file diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm index 8745e65a1c5..2b1a3e30a8f 100644 --- a/code/game/objects/items/weapons/tools/wrench.dm +++ b/code/game/objects/items/weapons/tools/wrench.dm @@ -74,37 +74,10 @@ origin_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5) /obj/item/weapon/tool/wrench/power - name = "hand drill" - desc = "A simple powered hand drill. It's fitted with a bolt bit." - icon_state = "drill_bolt" - item_state = "drill" + name = "power wrench" + desc = "You shouldn't see this." usesound = 'sound/items/drill_use.ogg' - matter = list(MAT_STEEL = 150, MAT_SILVER = 50) - origin_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2) force = 8 - w_class = ITEMSIZE_SMALL throwforce = 8 attack_verb = list("drilled", "screwed", "jabbed") toolspeed = 0.25 - var/obj/item/weapon/tool/screwdriver/power/counterpart = null - -/obj/item/weapon/tool/wrench/power/New(newloc, no_counterpart = TRUE) - ..(newloc) - if(!counterpart && no_counterpart) - counterpart = new(src, FALSE) - counterpart.counterpart = src - -/obj/item/weapon/tool/wrench/power/Destroy() - if(counterpart) - counterpart.counterpart = null // So it can qdel cleanly. - QDEL_NULL(counterpart) - return ..() - -/obj/item/weapon/tool/wrench/power/attack_self(mob/user) - playsound(src,'sound/items/change_drill.ogg',50,1) - user.drop_item(src) - counterpart.forceMove(get_turf(src)) - counterpart.persist_storable = persist_storable - src.forceMove(counterpart) - user.put_in_active_hand(counterpart) - to_chat(user, "You attach the screw driver bit to [src].") diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index c183bc5c762..7bc406166c1 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -244,7 +244,7 @@ var/inc_damage = W.force - if(W.is_wirecutter()) + if(W.has_tool_quality(TOOL_WIRECUTTER)) if(!shock(user, 100, pick(BP_L_HAND, BP_R_HAND))) playsound(src, W.usesound, 100, 1) inc_damage *= 3 diff --git a/code/game/objects/items/weapons/trays.dm b/code/game/objects/items/weapons/trays.dm index e16dbbf88a5..2ea7b30cd1e 100644 --- a/code/game/objects/items/weapons/trays.dm +++ b/code/game/objects/items/weapons/trays.dm @@ -1,214 +1,214 @@ -/* - * Trays - Agouri - */ -/obj/item/weapon/tray - name = "tray" - icon = 'icons/obj/food.dmi' - icon_state = "tray" - desc = "A metal tray to lay food on." - throwforce = 12.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_NORMAL - matter = list(MAT_STEEL = 3000) - var/list/carrying = list() // List of things on the tray. - Doohl - var/max_carry = 10 - drop_sound = 'sound/items/trayhit1.ogg' - -/obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - user.setClickCooldown(user.get_attack_speed(src)) - // Drop all the things. All of them. - cut_overlays() - for(var/obj/item/I in carrying) - I.loc = M.loc - carrying.Remove(I) - if(isturf(I.loc)) - spawn() - for(var/i = 1, i <= rand(1,2), i++) - if(I) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) - - - if((CLUMSY in user.mutations) && prob(50)) //What if he's a clown? - to_chat(M, "You accidentally slam yourself with the [src]!") - M.Weaken(1) - user.take_organ_damage(2) - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - return - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' - return //it always returns, but I feel like adding an extra return just for safety's sakes. EDIT; Oh well I won't :3 - - var/mob/living/carbon/human/H = M ///////////////////////////////////// /Let's have this ready for later. - - - if(!(user.zone_sel.selecting == (O_EYES || BP_HEAD))) //////////////hitting anything else other than the eyes - if(prob(33)) - src.add_blood(H) - var/turf/location = H.loc - if (istype(location, /turf/simulated)) - location.add_blood(H) ///Plik plik, the sound of blood - - add_attack_logs(user,M,"Hit with [src]") - - if(prob(15)) - M.Weaken(3) - M.take_organ_damage(3) - else - M.take_organ_damage(5) - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - return - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //we applied the damage, we played the sound, we showed the appropriate messages. Time to return and stop the proc - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - return - - - var/protected = 0 - for(var/slot in list(slot_head, slot_wear_mask, slot_glasses)) - var/obj/item/protection = M.get_equipped_item(slot) - if(istype(protection) && (protection.body_parts_covered & FACE)) - protected = 1 - break - - if(protected) - to_chat(M, "You get slammed in the face with the tray, against your mask!") - if(prob(33)) - src.add_blood(H) - if (H.wear_mask) - H.wear_mask.add_blood(H) - if (H.head) - H.head.add_blood(H) - if (H.glasses && prob(33)) - H.glasses.add_blood(H) - var/turf/location = H.loc - if (istype(location, /turf/simulated)) //Addin' blood! At least on the floor and item :v - location.add_blood(H) - - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] with the tray!", user, M), 1) - if(prob(10)) - M.Stun(rand(1,3)) - M.take_organ_damage(3) - return - else - M.take_organ_damage(5) - return - - else //No eye or head protection, tough luck! - to_chat(M, "You get slammed in the face with the tray!") - if(prob(33)) - src.add_blood(M) - var/turf/location = H.loc - if (istype(location, /turf/simulated)) - location.add_blood(H) - - if(prob(50)) - playsound(src, 'sound/items/trayhit1.ogg', 50, 1) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) - else - playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' again - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) - if(prob(30)) - M.Stun(rand(2,4)) - M.take_organ_damage(4) - return - else - M.take_organ_damage(8) - if(prob(30)) - M.Weaken(2) - return - return - -/obj/item/weapon/tray/var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/weapon/tray/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/material/kitchen/rollingpin)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/* -===============~~~~~================================~~~~~==================== -= = -= Code for trays carrying things. By Doohl for Doohl erryday Doohl Doohl~ = -= = -===============~~~~~================================~~~~~==================== -*/ -/obj/item/weapon/tray/proc/calc_carry() - // calculate the weight of the items on the tray - var/val = 0 // value to return - - for(var/obj/item/I in carrying) - if(I.w_class == ITEMSIZE_TINY) - val ++ - else if(I.w_class == ITEMSIZE_SMALL) - val += 3 - else - val += 5 - - return val - -/obj/item/weapon/tray/pickup(mob/user) - - if(!isturf(loc)) - return - - for(var/obj/item/I in loc) - if( I != src && !I.anchored && !istype(I, /obj/item/clothing/under) && !istype(I, /obj/item/clothing/suit) && !istype(I, /obj/item/projectile) ) - var/add = 0 - if(I.w_class == ITEMSIZE_TINY) - add = 1 - else if(I.w_class == ITEMSIZE_SMALL) - add = 3 - else - add = 5 - if(calc_carry() + add >= max_carry) - break - var/image/Img = new(src.icon) - I.loc = src - carrying.Add(I) - Img.icon = I.icon - Img.icon_state = I.icon_state - Img.layer = layer + I.layer*0.01 - if(istype(I, /obj/item/weapon/material)) - var/obj/item/weapon/material/O = I - if(O.applies_material_colour) - Img.color = O.color - add_overlay(Img) - -/obj/item/weapon/tray/dropped(mob/user) - var/noTable = null - - spawn() //Allows the tray to udpate location, rather than just checking against mob's location - if(isturf(src.loc) && !(locate(/obj/structure/table) in src.loc)) - noTable = 1 - - if(isturf(loc) && !(locate(/mob/living) in src.loc)) - cut_overlays() - for(var/obj/item/I in carrying) - I.forceMove(src.loc) - carrying.Remove(I) - if(noTable) - for(var/i = 1, i <= rand(1,2), i++) - if(I) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) +/* + * Trays - Agouri + */ +/obj/item/weapon/tray + name = "tray" + icon = 'icons/obj/food.dmi' + icon_state = "tray" + desc = "A metal tray to lay food on." + throwforce = 12.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_NORMAL + matter = list(MAT_STEEL = 3000) + var/list/carrying = list() // List of things on the tray. - Doohl + var/max_carry = 10 + drop_sound = 'sound/items/trayhit1.ogg' + +/obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + user.setClickCooldown(user.get_attack_speed(src)) + // Drop all the things. All of them. + cut_overlays() + for(var/obj/item/I in carrying) + I.loc = M.loc + carrying.Remove(I) + if(isturf(I.loc)) + spawn() + for(var/i = 1, i <= rand(1,2), i++) + if(I) + step(I, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) + + + if((CLUMSY in user.mutations) && prob(50)) //What if he's a clown? + to_chat(M, "You accidentally slam yourself with the [src]!") + M.Weaken(1) + user.take_organ_damage(2) + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + return + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' + return //it always returns, but I feel like adding an extra return just for safety's sakes. EDIT; Oh well I won't :3 + + var/mob/living/carbon/human/H = M ///////////////////////////////////// /Let's have this ready for later. + + + if(!(user.zone_sel.selecting == (O_EYES || BP_HEAD))) //////////////hitting anything else other than the eyes + if(prob(33)) + src.add_blood(H) + var/turf/location = H.loc + if (istype(location, /turf/simulated)) + location.add_blood(H) ///Plik plik, the sound of blood + + add_attack_logs(user,M,"Hit with [src]") + + if(prob(15)) + M.Weaken(3) + M.take_organ_damage(3) + else + M.take_organ_damage(5) + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + return + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //we applied the damage, we played the sound, we showed the appropriate messages. Time to return and stop the proc + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + return + + + var/protected = 0 + for(var/slot in list(slot_head, slot_wear_mask, slot_glasses)) + var/obj/item/protection = M.get_equipped_item(slot) + if(istype(protection) && (protection.body_parts_covered & FACE)) + protected = 1 + break + + if(protected) + to_chat(M, "You get slammed in the face with the tray, against your mask!") + if(prob(33)) + src.add_blood(H) + if (H.wear_mask) + H.wear_mask.add_blood(H) + if (H.head) + H.head.add_blood(H) + if (H.glasses && prob(33)) + H.glasses.add_blood(H) + var/turf/location = H.loc + if (istype(location, /turf/simulated)) //Addin' blood! At least on the floor and item :v + location.add_blood(H) + + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] with the tray!", user, M), 1) + if(prob(10)) + M.Stun(rand(1,3)) + M.take_organ_damage(3) + return + else + M.take_organ_damage(5) + return + + else //No eye or head protection, tough luck! + to_chat(M, "You get slammed in the face with the tray!") + if(prob(33)) + src.add_blood(M) + var/turf/location = H.loc + if (istype(location, /turf/simulated)) + location.add_blood(H) + + if(prob(50)) + playsound(src, 'sound/items/trayhit1.ogg', 50, 1) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) + else + playsound(src, 'sound/items/trayhit2.ogg', 50, 1) //sound playin' again + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] slams [] in the face with the tray!", user, M), 1) + if(prob(30)) + M.Stun(rand(2,4)) + M.take_organ_damage(4) + return + else + M.take_organ_damage(8) + if(prob(30)) + M.Weaken(2) + return + return + +/obj/item/weapon/tray/var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/weapon/tray/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/material/kitchen/rollingpin)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(src, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/* +===============~~~~~================================~~~~~==================== += = += Code for trays carrying things. By Doohl for Doohl erryday Doohl Doohl~ = += = +===============~~~~~================================~~~~~==================== +*/ +/obj/item/weapon/tray/proc/calc_carry() + // calculate the weight of the items on the tray + var/val = 0 // value to return + + for(var/obj/item/I in carrying) + if(I.w_class == ITEMSIZE_TINY) + val ++ + else if(I.w_class == ITEMSIZE_SMALL) + val += 3 + else + val += 5 + + return val + +/obj/item/weapon/tray/pickup(mob/user) + + if(!isturf(loc)) + return + + for(var/obj/item/I in loc) + if( I != src && !I.anchored && !istype(I, /obj/item/clothing/under) && !istype(I, /obj/item/clothing/suit) && !istype(I, /obj/item/projectile) ) + var/add = 0 + if(I.w_class == ITEMSIZE_TINY) + add = 1 + else if(I.w_class == ITEMSIZE_SMALL) + add = 3 + else + add = 5 + if(calc_carry() + add >= max_carry) + break + var/image/Img = new(src.icon) + I.loc = src + carrying.Add(I) + Img.icon = I.icon + Img.icon_state = I.icon_state + Img.layer = layer + I.layer*0.01 + if(istype(I, /obj/item/weapon/material)) + var/obj/item/weapon/material/O = I + if(O.applies_material_colour) + Img.color = O.color + add_overlay(Img) + +/obj/item/weapon/tray/dropped(mob/user) + var/noTable = null + + spawn() //Allows the tray to udpate location, rather than just checking against mob's location + if(isturf(src.loc) && !(locate(/obj/structure/table) in src.loc)) + noTable = 1 + + if(isturf(loc) && !(locate(/mob/living) in src.loc)) + cut_overlays() + for(var/obj/item/I in carrying) + I.forceMove(src.loc) + carrying.Remove(I) + if(noTable) + for(var/i = 1, i <= rand(1,2), i++) + if(I) + step(I, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 26f32002420..dc9aa503367 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -1,136 +1,136 @@ -/obj/item/weapon/nullrod - name = "null rod" - desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of paranormal phenomenae." - icon_state = "nullrod" - item_state = "nullrod" - slot_flags = SLOT_BELT - force = 15 - throw_speed = 1 - throw_range = 4 - throwforce = 10 - w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - -/obj/item/weapon/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod. - - add_attack_logs(user,M,"Hit with [src] (nullrod)") - - user.setClickCooldown(user.get_attack_speed(src)) - user.do_attack_animation(M) - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return - - if ((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "The rod slips out of your hand and hits your head.") - user.take_organ_damage(10) - user.Paralyse(20) - return - - if (M.stat !=2) - if(cult && (M.mind in cult.current_antagonists) && prob(33)) - to_chat(M, "The power of [src] clears your mind of the cult's influence!") - to_chat(user, "You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal.") - cult.remove_antagonist(M.mind) - M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") - else if(prob(10)) - to_chat(user, "The rod slips in your hand.") - ..() - else - to_chat(user, "The rod appears to do nothing.") - M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") - return - -/obj/item/weapon/nullrod/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) - return - if (istype(A, /turf/simulated/floor)) - to_chat(user, "You hit the floor with the [src].") - call(/obj/effect/rune/proc/revealrunes)(src) - -/obj/item/weapon/energy_net - name = "energy net" - desc = "It's a net made of green energy." - icon = 'icons/effects/effects.dmi' - icon_state = "energynet" - throwforce = 0 - force = 0 - var/net_type = /obj/effect/energy_net - -/obj/item/weapon/energy_net/dropped() - spawn(10) - if(src) qdel(src) - -/obj/item/weapon/energy_net/throw_impact(atom/hit_atom) - ..() - - var/mob/living/M = hit_atom - - if(!istype(M) || locate(/obj/effect/energy_net) in M.loc) - qdel(src) - return 0 - - var/turf/T = get_turf(M) - if(T) - var/obj/effect/energy_net/net = new net_type(T) - if(net.buckle_mob(M)) - T.visible_message("[M] was caught in an energy net!") - qdel(src) - - // If we miss or hit an obstacle, we still want to delete the net. - spawn(10) - if(src) qdel(src) - -/obj/effect/energy_net - name = "energy net" - desc = "It's a net made of green energy." - icon = 'icons/effects/effects.dmi' - icon_state = "energynet" - - density = TRUE - opacity = 0 - mouse_opacity = 1 - anchored = FALSE - - can_buckle = TRUE - buckle_lying = 0 - buckle_dir = SOUTH - - var/escape_time = 8 SECONDS - -/obj/effect/energy_net/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/effect/energy_net/Destroy() - if(has_buckled_mobs()) - for(var/A in buckled_mobs) - to_chat(A, "You are free of the net!") - unbuckle_mob(A) - - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/effect/energy_net/process() - if(!has_buckled_mobs()) - qdel(src) - -/obj/effect/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/user) - user.setClickCooldown(user.get_attack_speed()) - visible_message("[user] begins to tear at \the [src]!") - if(do_after(user, escape_time, src, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) - if(!has_buckled_mobs()) - return - visible_message("[user] manages to tear \the [src] apart!") - unbuckle_mob(buckled_mob) - -/obj/effect/energy_net/post_buckle_mob(mob/living/M) - if(M.buckled == src) //Just buckled someone - ..() - layer = M.layer+1 - M.can_pull_size = 0 - else //Just unbuckled someone - M.can_pull_size = initial(M.can_pull_size) - qdel(src) +/obj/item/weapon/nullrod + name = "null rod" + desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of paranormal phenomenae." + icon_state = "nullrod" + item_state = "nullrod" + slot_flags = SLOT_BELT + force = 15 + throw_speed = 1 + throw_range = 4 + throwforce = 10 + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + +/obj/item/weapon/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod. + + add_attack_logs(user,M,"Hit with [src] (nullrod)") + + user.setClickCooldown(user.get_attack_speed(src)) + user.do_attack_animation(M) + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return + + if ((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "The rod slips out of your hand and hits your head.") + user.take_organ_damage(10) + user.Paralyse(20) + return + + if (M.stat !=2) + if(cult && (M.mind in cult.current_antagonists) && prob(33)) + to_chat(M, "The power of [src] clears your mind of the cult's influence!") + to_chat(user, "You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal.") + cult.remove_antagonist(M.mind) + M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") + else if(prob(10)) + to_chat(user, "The rod slips in your hand.") + ..() + else + to_chat(user, "The rod appears to do nothing.") + M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") + return + +/obj/item/weapon/nullrod/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) + return + if (istype(A, /turf/simulated/floor)) + to_chat(user, "You hit the floor with the [src].") + call(/obj/effect/rune/proc/revealrunes)(src) + +/obj/item/weapon/energy_net + name = "energy net" + desc = "It's a net made of green energy." + icon = 'icons/effects/effects.dmi' + icon_state = "energynet" + throwforce = 0 + force = 0 + var/net_type = /obj/effect/energy_net + +/obj/item/weapon/energy_net/dropped() + spawn(10) + if(src) qdel(src) + +/obj/item/weapon/energy_net/throw_impact(atom/hit_atom) + ..() + + var/mob/living/M = hit_atom + + if(!istype(M) || locate(/obj/effect/energy_net) in M.loc) + qdel(src) + return 0 + + var/turf/T = get_turf(M) + if(T) + var/obj/effect/energy_net/net = new net_type(T) + if(net.buckle_mob(M)) + T.visible_message("[M] was caught in an energy net!") + qdel(src) + + // If we miss or hit an obstacle, we still want to delete the net. + spawn(10) + if(src) qdel(src) + +/obj/effect/energy_net + name = "energy net" + desc = "It's a net made of green energy." + icon = 'icons/effects/effects.dmi' + icon_state = "energynet" + + density = TRUE + opacity = 0 + mouse_opacity = 1 + anchored = FALSE + + can_buckle = TRUE + buckle_lying = 0 + buckle_dir = SOUTH + + var/escape_time = 8 SECONDS + +/obj/effect/energy_net/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/effect/energy_net/Destroy() + if(has_buckled_mobs()) + for(var/A in buckled_mobs) + to_chat(A, "You are free of the net!") + unbuckle_mob(A) + + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/energy_net/process() + if(!has_buckled_mobs()) + qdel(src) + +/obj/effect/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/user) + user.setClickCooldown(user.get_attack_speed()) + visible_message("[user] begins to tear at \the [src]!") + if(do_after(user, escape_time, src, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) + if(!has_buckled_mobs()) + return + visible_message("[user] manages to tear \the [src] apart!") + unbuckle_mob(buckled_mob) + +/obj/effect/energy_net/post_buckle_mob(mob/living/M) + if(M.buckled == src) //Just buckled someone + ..() + layer = M.layer+1 + M.can_pull_size = 0 + else //Just unbuckled someone + M.can_pull_size = initial(M.can_pull_size) + qdel(src) diff --git a/code/game/objects/micro_event.dm b/code/game/objects/micro_event.dm index f798d9b6aad..36fcb152e78 100644 --- a/code/game/objects/micro_event.dm +++ b/code/game/objects/micro_event.dm @@ -17,7 +17,7 @@ shrinking = FALSE our_message = "What should the size limit be? Anyone under this limit will be grown to this size. (1 = 100%, etc)" - size_limit = tgui_input_number(user, our_message, "Pick a Size", 1) + size_limit = tgui_input_number(user, our_message, "Pick a Size", 1, round_value=FALSE) return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index b9a0f53c7d0..21b78b9a49e 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,207 +1,209 @@ -/obj - layer = OBJ_LAYER - plane = OBJ_PLANE - vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. - //Used to store information about the contents of the object. - var/list/matter - var/w_class // Size of the object. - var/unacidable = FALSE //universal "unacidabliness" var, here so you can use it in any obj. - animate_movement = 2 - var/throwforce = 1 - var/catchable = 1 // can it be caught on throws/flying? - var/sharp = FALSE // whether this object cuts - var/edge = FALSE // whether this object is more likely to dismember - var/pry = 0 //Used in attackby() to open doors - var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! - var/damtype = "brute" - var/armor_penetration = 0 - var/show_messages - var/preserve_item = 0 //whether this object is preserved when its owner goes into cryo-storage, gateway, etc - var/can_speak = 0 //For MMIs and admin trickery. If an object has a brainmob in its contents, set this to 1 to allow it to speak. - - var/show_examine = TRUE // Does this pop up on a mob when the mob is examined? - -/obj/Destroy() - STOP_PROCESSING(SSobj, src) - - //VOREStation Add Start - I really am an idiot why did I make it this way - if(micro_target) - for(var/thing in src.contents) - if(!ismob(thing)) - continue - var/mob/m = thing - if(isbelly(src.loc)) - m.forceMove(src.loc) - else - m.forceMove(get_turf(src.loc)) - m.visible_message("\The [m] tumbles out of \the [src]!") - //VOREStation Add End - - return ..() - -/obj/Topic(href, href_list, var/datum/tgui_state/state = GLOB.tgui_default_state) - if(usr && ..()) - return 1 - - // In the far future no checks are made in an overriding Topic() beyond if(..()) return - // Instead any such checks are made in CanUseTopic() - if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) - CouldUseTopic(usr) - return 0 - - CouldNotUseTopic(usr) - return 1 - -/obj/CanUseTopic(var/mob/user, var/datum/tgui_state/state = GLOB.tgui_default_state) - if(user.CanUseObjTopic(src)) - return ..() - to_chat(user, "\icon[src][bicon(src)]Access Denied!") - return STATUS_CLOSE - -/mob/living/silicon/CanUseObjTopic(var/obj/O) - var/id = src.GetIdCard() - return O.check_access(id) - -/mob/proc/CanUseObjTopic() - return 1 - -/obj/proc/CouldUseTopic(var/mob/user) - var/atom/host = tgui_host() - host.add_hiddenprint(user) - -/obj/proc/CouldNotUseTopic(var/mob/user) - // Nada - -/obj/item/proc/is_used_on(obj/O, mob/user) - -/obj/assume_air(datum/gas_mixture/giver) - if(loc) - return loc.assume_air(giver) - else - return null - -/obj/remove_air(amount) - if(loc) - return loc.remove_air(amount) - else - return null - -/obj/return_air() - if(loc) - return loc.return_air() - else - return null - -/obj/proc/updateUsrDialog() - if(in_use) - var/is_in_use = 0 - var/list/nearby = viewers(1, src) - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - is_in_use = 1 - src.attack_hand(M) - if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot)) - if (!(usr in nearby)) - if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. - is_in_use = 1 - src.attack_ai(usr) - - // check for TK users - - if (istype(usr, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - if(H.get_type_in_hands(/obj/item/tk_grab)) - if(!(H in nearby)) - if(H.client && H.machine==src) - is_in_use = 1 - src.attack_hand(H) - in_use = is_in_use - -/obj/proc/updateDialog() - // Check that people are actually using the machine. If not, don't update anymore. - if(in_use) - var/list/nearby = viewers(1, src) - var/is_in_use = 0 - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - is_in_use = 1 - src.interact(M) - var/ai_in_use = AutoUpdateAI(src) - - if(!ai_in_use && !is_in_use) - in_use = 0 - -/obj/attack_ghost(mob/user) - tgui_interact(user) - ..() - -/mob/proc/unset_machine() - machine?.remove_visual(src) - src.machine = null - -/mob/proc/set_machine(var/obj/O) - if(src.machine) - unset_machine() - src.machine = O - if(istype(O)) - O.in_use = 1 - -/obj/item/proc/updateSelfDialog() - var/mob/M = src.loc - if(istype(M) && M.client && M.machine == src) - src.attack_self(M) - -/obj/proc/hide(h) - return - -/obj/proc/hides_under_flooring() - return 0 - -/obj/proc/hear_talk(mob/M, list/message_pieces, verb) - if(talking_atom) - talking_atom.catchMessage(multilingual_to_message(message_pieces), M) -/* - var/mob/mo = locate(/mob) in src - if(mo) - var/rendered = "[M.name]: [text]" - mo.show_message(rendered, 2) - */ - return - -/obj/proc/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) // Saycode gets worse every day. - return FALSE - -/obj/proc/see_emote(mob/M as mob, text, var/emote_type) - return - -/obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) - return - -// Used to mark a turf as containing objects that are dangerous to step onto. -/obj/proc/register_dangerous_to_step() - var/turf/T = get_turf(src) - if(T) - T.register_dangerous_object(src) - -/obj/proc/unregister_dangerous_to_step() - var/turf/T = get_turf(src) - if(T) - T.unregister_dangerous_object(src) - -// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. -/obj/proc/is_safe_to_step(mob/living/L) - return TRUE - -/obj/proc/container_resist(var/mob/living) - return - -//To be called from things that spill objects on the floor. -//Makes an object move around randomly for a couple of tiles -/obj/proc/tumble(var/dist = 2) - set waitfor = FALSE - if (dist >= 1) - dist += rand(0,1) - for(var/i = 1, i <= dist, i++) - if(src) - step(src, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) +/obj + layer = OBJ_LAYER + plane = OBJ_PLANE + vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. + //Used to store information about the contents of the object. + var/list/matter + var/w_class // Size of the object. + var/unacidable = FALSE //universal "unacidabliness" var, here so you can use it in any obj. + animate_movement = 2 + var/throwforce = 1 + var/catchable = 1 // can it be caught on throws/flying? + var/sharp = FALSE // whether this object cuts + var/edge = FALSE // whether this object is more likely to dismember + var/pry = 0 //Used in attackby() to open doors + var/in_use = 0 // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! + var/damtype = "brute" + var/armor_penetration = 0 + var/show_messages + var/preserve_item = 0 //whether this object is preserved when its owner goes into cryo-storage, gateway, etc + var/can_speak = 0 //For MMIs and admin trickery. If an object has a brainmob in its contents, set this to 1 to allow it to speak. + + var/show_examine = TRUE // Does this pop up on a mob when the mob is examined? + + var/redgate_allowed = TRUE //can we be taken through the redgate, in either direction? + +/obj/Destroy() + STOP_PROCESSING(SSobj, src) + + //VOREStation Add Start - I really am an idiot why did I make it this way + if(micro_target) + for(var/thing in src.contents) + if(!ismob(thing)) + continue + var/mob/m = thing + if(isbelly(src.loc)) + m.forceMove(src.loc) + else + m.forceMove(get_turf(src.loc)) + m.visible_message("\The [m] tumbles out of \the [src]!") + //VOREStation Add End + + return ..() + +/obj/Topic(href, href_list, var/datum/tgui_state/state = GLOB.tgui_default_state) + if(usr && ..()) + return 1 + + // In the far future no checks are made in an overriding Topic() beyond if(..()) return + // Instead any such checks are made in CanUseTopic() + if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return 0 + + CouldNotUseTopic(usr) + return 1 + +/obj/CanUseTopic(var/mob/user, var/datum/tgui_state/state = GLOB.tgui_default_state) + if(user.CanUseObjTopic(src)) + return ..() + to_chat(user, "\icon[src][bicon(src)]Access Denied!") + return STATUS_CLOSE + +/mob/living/silicon/CanUseObjTopic(var/obj/O) + var/id = src.GetIdCard() + return O.check_access(id) + +/mob/proc/CanUseObjTopic() + return 1 + +/obj/proc/CouldUseTopic(var/mob/user) + var/atom/host = tgui_host() + host.add_hiddenprint(user) + +/obj/proc/CouldNotUseTopic(var/mob/user) + // Nada + +/obj/item/proc/is_used_on(obj/O, mob/user) + +/obj/assume_air(datum/gas_mixture/giver) + if(loc) + return loc.assume_air(giver) + else + return null + +/obj/remove_air(amount) + if(loc) + return loc.remove_air(amount) + else + return null + +/obj/return_air() + if(loc) + return loc.return_air() + else + return null + +/obj/proc/updateUsrDialog() + if(in_use) + var/is_in_use = 0 + var/list/nearby = viewers(1, src) + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + is_in_use = 1 + src.attack_hand(M) + if (istype(usr, /mob/living/silicon/ai) || istype(usr, /mob/living/silicon/robot)) + if (!(usr in nearby)) + if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. + is_in_use = 1 + src.attack_ai(usr) + + // check for TK users + + if (istype(usr, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + if(H.get_type_in_hands(/obj/item/tk_grab)) + if(!(H in nearby)) + if(H.client && H.machine==src) + is_in_use = 1 + src.attack_hand(H) + in_use = is_in_use + +/obj/proc/updateDialog() + // Check that people are actually using the machine. If not, don't update anymore. + if(in_use) + var/list/nearby = viewers(1, src) + var/is_in_use = 0 + for(var/mob/M in nearby) + if ((M.client && M.machine == src)) + is_in_use = 1 + src.interact(M) + var/ai_in_use = AutoUpdateAI(src) + + if(!ai_in_use && !is_in_use) + in_use = 0 + +/obj/attack_ghost(mob/user) + tgui_interact(user) + ..() + +/mob/proc/unset_machine() + machine?.remove_visual(src) + src.machine = null + +/mob/proc/set_machine(var/obj/O) + if(src.machine) + unset_machine() + src.machine = O + if(istype(O)) + O.in_use = 1 + +/obj/item/proc/updateSelfDialog() + var/mob/M = src.loc + if(istype(M) && M.client && M.machine == src) + src.attack_self(M) + +/obj/proc/hide(h) + return + +/obj/proc/hides_under_flooring() + return 0 + +/obj/proc/hear_talk(mob/M, list/message_pieces, verb) + if(talking_atom) + talking_atom.catchMessage(multilingual_to_message(message_pieces), M) +/* + var/mob/mo = locate(/mob) in src + if(mo) + var/rendered = "[M.name]: [text]" + mo.show_message(rendered, 2) + */ + return + +/obj/proc/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) // Saycode gets worse every day. + return FALSE + +/obj/proc/see_emote(mob/M as mob, text, var/emote_type) + return + +/obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) + return + +// Used to mark a turf as containing objects that are dangerous to step onto. +/obj/proc/register_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.register_dangerous_object(src) + +/obj/proc/unregister_dangerous_to_step() + var/turf/T = get_turf(src) + if(T) + T.unregister_dangerous_object(src) + +// Test for if stepping on a tile containing this obj is safe to do, used for things like landmines and cliffs. +/obj/proc/is_safe_to_step(mob/living/L) + return TRUE + +/obj/proc/container_resist(var/mob/living) + return + +//To be called from things that spill objects on the floor. +//Makes an object move around randomly for a couple of tiles +/obj/proc/tumble(var/dist = 2) + set waitfor = FALSE + if (dist >= 1) + dist += rand(0,1) + for(var/i = 1, i <= dist, i++) + if(src) + step(src, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm index b8ff810fe24..8062d7db2ae 100644 --- a/code/game/objects/random/mapping.dm +++ b/code/game/objects/random/mapping.dm @@ -84,7 +84,9 @@ prob(5);/obj/machinery/vending/sovietsoda, prob(5);/obj/machinery/vending/sovietvend, prob(5);/obj/machinery/vending/radren, - prob(3);/obj/machinery/vending/altevian) //VOREStation Edit End + prob(3);/obj/machinery/vending/altevian, + prob(5);/obj/machinery/vending/desatti, + prob(5);/obj/machinery/vending/nukie) //VOREStation Edit End /obj/random/vendorfood //Random food vendors for station use name = "random snack vending machine" @@ -98,7 +100,8 @@ /obj/machinery/vending/sol, /obj/machinery/vending/snix, /obj/machinery/vending/snlvend, - /obj/machinery/vending/altevian) + /obj/machinery/vending/altevian, + /obj/machinery/vending/desatti) /obj/random/vendordrink //Random drink vendors for station use name = "random drink vending machine" @@ -112,7 +115,8 @@ /obj/machinery/vending/cola/soft, /obj/machinery/vending/bepis, /obj/machinery/vending/sovietsoda, - /obj/machinery/vending/radren) + /obj/machinery/vending/radren, + /obj/machinery/vending/nukie) //VOREStation Edit End /obj/random/obstruction //Large objects to block things off in maintenance @@ -136,7 +140,7 @@ name = "Random Land Mine" desc = "This is a random land mine." icon = 'icons/obj/weapons.dmi' - icon_state = "uglymine" + icon_state = "landmine" spawn_nothing_percentage = 25 /obj/random/landmine/item_to_spawn() diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm index 23aa3ee0f13..3f0f80593ca 100644 --- a/code/game/objects/random/misc.dm +++ b/code/game/objects/random/misc.dm @@ -25,8 +25,8 @@ /obj/random/tool/powermaint/item_to_spawn() return pick(prob(320);/obj/random/tool, - prob(1);/obj/item/weapon/tool/screwdriver/power, - prob(1);/obj/item/weapon/tool/wirecutters/power, + prob(1);/obj/item/weapon/tool/transforming/powerdrill, + prob(1);/obj/item/weapon/tool/transforming/jawsoflife, prob(15);/obj/item/weapon/weldingtool/electric, prob(5);/obj/item/weapon/weldingtool/experimental) @@ -36,8 +36,8 @@ icon_state = "tool_2" /obj/random/tool/power/item_to_spawn() - return pick(/obj/item/weapon/tool/screwdriver/power, - /obj/item/weapon/tool/wirecutters/power, + return pick(/obj/item/weapon/tool/transforming/powerdrill, + /obj/item/weapon/tool/transforming/jawsoflife, /obj/item/weapon/weldingtool/electric, /obj/item/weapon/weldingtool/experimental) diff --git a/code/game/objects/random/misc_vr.dm b/code/game/objects/random/misc_vr.dm index ecd7468d132..f16d2385d89 100644 --- a/code/game/objects/random/misc_vr.dm +++ b/code/game/objects/random/misc_vr.dm @@ -235,3 +235,134 @@ prob(5);/obj/item/organ/internal/stomach, prob(5);/obj/item/organ/internal/voicebox, ) + +/obj/random/potion + name = "random potion" + desc = "A random potion." + icon_state = "potion" + spawn_nothing_percentage = 0 + +/obj/random/potion/item_to_spawn() + return pick(prob(20);/obj/item/weapon/reagent_containers/glass/bottle/potion/healing, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/greater_healing, + prob(20);/obj/item/weapon/reagent_containers/glass/bottle/potion/fire_resist, + prob(20);/obj/item/weapon/reagent_containers/glass/bottle/potion/antidote, + prob(20);/obj/item/weapon/reagent_containers/glass/bottle/potion/water, + prob(8);/obj/item/weapon/reagent_containers/glass/bottle/potion/regeneration, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/panacea, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/magic, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/lightness, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/SOP, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/shrink, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/growth, + prob(20);/obj/item/weapon/reagent_containers/glass/bottle/potion/pain, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/faerie, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/relaxation, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/speed, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/attractiveness, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/girljuice, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/boyjuice, + prob(4);/obj/item/weapon/reagent_containers/glass/bottle/potion/badpolymorph, + prob(2);/obj/item/weapon/reagent_containers/glass/bottle/potion/bonerepair, + prob(1);/obj/item/weapon/reagent_containers/glass/bottle/potion/truepolymorph + ) + +/obj/random/potion_ingredient + name = "random potion ingredient" + desc = "A random potion." + icon_state = "ingredient" + spawn_nothing_percentage = 0 + +/obj/random/potion_ingredient/item_to_spawn() + return pick(prob(10);/obj/item/weapon/potion_material/blood_ruby, + prob(2);/obj/item/weapon/potion_material/ruby_eye, + prob(10);/obj/item/weapon/potion_material/golden_scale, + prob(10);/obj/item/weapon/potion_material/frozen_dew, + prob(10);/obj/item/weapon/potion_material/living_coral, + prob(4);/obj/item/weapon/potion_material/rare_horn, + prob(5);/obj/item/weapon/potion_material/moldy_bread, + prob(5);/obj/item/weapon/potion_material/glowing_gem, + prob(5);/obj/item/weapon/potion_material/giant_toe, + prob(2);/obj/item/weapon/potion_material/flesh_of_the_stars, + prob(2);/obj/item/weapon/potion_material/spinning_poppy, + prob(2);/obj/item/weapon/potion_material/salt_mage, + prob(10);/obj/item/weapon/potion_material/golden_grapes, + prob(5);/obj/item/weapon/potion_material/fairy_house, + prob(5);/obj/item/weapon/potion_material/thorny_bulb, + prob(5);/obj/item/weapon/potion_material/ancient_egg, + prob(5);/obj/item/weapon/potion_material/crown_stem, + prob(2);/obj/item/weapon/potion_material/red_ingot, + prob(2);/obj/item/weapon/potion_material/soft_diamond, + prob(2);/obj/item/weapon/potion_material/solid_mist, + prob(1);/obj/item/weapon/potion_material/spider_leg, + prob(1);/obj/item/weapon/potion_material/folded_dark + ) + +/obj/random/potion_base + name = "random potion base" + desc = "A random potion base." + icon_state = "base" + spawn_nothing_percentage = 0 + +/obj/random/potion_base/item_to_spawn() + return pick(prob(10);/obj/item/weapon/potion_base/aqua_regia, + prob(10);/obj/item/weapon/potion_base/ichor, + prob(10);/obj/item/weapon/potion_base/alkahest + ) + +/obj/random/fantasy_item + name = "random fantasy item" + desc = "A random fantasy item." + icon_state = "fantasy" + spawn_nothing_percentage = 0 + +/obj/random/fantasy_item/item_to_spawn() + return pick(prob(3);/obj/item/device/healthanalyzer/scroll, + prob(10);/obj/item/weapon/gun/energy/taser/magic, + prob(5);/obj/item/weapon/bluespace_harpoon/wand, + prob(10);/obj/item/device/slow_sizegun/magic, + prob(10);/obj/item/clothing/gloves/bluespace/magic, + prob(30);/obj/item/weapon/coin/gold, + prob(30);/obj/item/weapon/coin/silver, + prob(30);/obj/item/weapon/coin/platinum, + prob(20);/obj/item/weapon/material/sword/rapier, + prob(20);/obj/item/weapon/material/sword/longsword, + prob(20);/obj/item/clothing/head/helmet/bucket/wood, + prob(3);/obj/item/weapon/tool/wirecutters/alien/magic, + prob(3);/obj/item/weapon/tool/crowbar/alien/magic, + prob(3);/obj/item/weapon/tool/screwdriver/alien/magic, + prob(3);/obj/item/weapon/weldingtool/alien/magic, + prob(3);/obj/item/weapon/tool/wrench/alien/magic, + prob(3);/obj/item/weapon/surgical/bone_clamp/alien/magic, + prob(10);/obj/item/stack/material/gold, + prob(10);/obj/item/stack/material/silver, + prob(3);/obj/item/weapon/bone/skull, + prob(20);/obj/item/weapon/material/twohanded/staff, + prob(3);/obj/item/weapon/gun/energy/hooklauncher/ring, + prob(3);/obj/item/toy/eight_ball, + prob(3);/obj/item/device/perfect_tele/magic + ) + +/obj/random/fantasy_item/better + name = "better random fantasy item" + desc = "A random fantasy item." + icon_state = "fantasy2" + spawn_nothing_percentage = 0 + +/obj/random/fantasy_item/better/item_to_spawn() + return pick(prob(10);/obj/item/device/healthanalyzer/scroll, + prob(10);/obj/item/weapon/gun/energy/taser/magic, + prob(10);/obj/item/weapon/bluespace_harpoon/wand, + prob(10);/obj/item/device/slow_sizegun/magic, + prob(10);/obj/item/clothing/gloves/bluespace/magic, + prob(10);/obj/item/weapon/tool/wirecutters/alien/magic, + prob(10);/obj/item/weapon/tool/crowbar/alien/magic, + prob(10);/obj/item/weapon/tool/screwdriver/alien/magic, + prob(10);/obj/item/weapon/weldingtool/alien/magic, + prob(10);/obj/item/weapon/tool/wrench/alien/magic, + prob(10);/obj/item/weapon/surgical/bone_clamp/alien/magic, + prob(10);/obj/item/weapon/material/twohanded/staff, + prob(10);/obj/item/weapon/gun/energy/hooklauncher/ring, + prob(10);/obj/item/device/perfect_tele/magic, + prob(10);/obj/item/weapon/reagent_containers/glass/bottle/potion/truepolymorph + ) diff --git a/code/game/objects/random/mob.dm b/code/game/objects/random/mob.dm index be1222aabef..82d828535ad 100644 --- a/code/game/objects/random/mob.dm +++ b/code/game/objects/random/mob.dm @@ -132,8 +132,7 @@ mob_wander_distance = 4 /obj/random/mob/spider/item_to_spawn() - return pick(prob(22);/mob/living/simple_mob/animal/giant_spider/nurse, - prob(33);/mob/living/simple_mob/animal/giant_spider/hunter, + return pick(prob(33);/mob/living/simple_mob/animal/giant_spider/hunter, prob(45);/mob/living/simple_mob/animal/giant_spider) /obj/random/mob/spider/nurse diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index fb9a4a0e8bc..626c7a296e2 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -1,261 +1,261 @@ -/obj/structure - icon = 'icons/obj/structures.dmi' - w_class = ITEMSIZE_NO_CONTAINER - blocks_emissive = EMISSIVE_BLOCK_GENERIC - - var/climbable - var/climb_delay = 3.5 SECONDS - var/breakable - var/parts - var/list/climbers - var/block_turf_edges = FALSE // If true, turf edge icons will not be made on the turf this occupies. - - var/list/connections - var/list/other_connections - var/list/blend_objects = newlist() // Objects which to blend with - var/list/noblend_objects = newlist() //Objects to avoid blending with (such as children of listed blend objects. - -/obj/structure/Initialize() - . = ..() - if(climbable) - verbs += /obj/structure/proc/climb_on - -/obj/structure/Destroy() - if(parts) - new parts(loc) - return ..() - -/obj/structure/attack_hand(mob/user) - if(breakable) - if(HULK in user.mutations) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - attack_generic(user,1,"smashes") - else if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(user)) - attack_generic(user,1,"slices") - - if(LAZYLEN(climbers) && !(user in climbers)) - user.visible_message("[user.name] shakes \the [src].", \ - "You shake \the [src].") - structure_shaken() - - return ..() - -/obj/structure/attack_tk() - return - -/obj/structure/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if(prob(50)) - qdel(src) - return - if(3.0) - return - -/obj/structure/proc/climb_on() - set name = "Climb structure" - set desc = "Climbs onto a structure." - set category = "Object" - set src in oview(1) - - do_climb(usr) - -/obj/structure/MouseDrop_T(mob/target, mob/user) - var/mob/living/H = user - if(istype(H) && can_climb(H) && target == user) - do_climb(target) - else - return ..() - -/obj/structure/proc/can_climb(var/mob/living/user, post_climb_check=0) - if (!climbable || !can_touch(user) || (!post_climb_check && (user in climbers))) - return 0 - - if (!user.Adjacent(src)) - to_chat(user, "You can't climb there, the way is blocked.") - return 0 - - var/obj/occupied = turf_is_crowded() - if(occupied) - to_chat(user, "There's \a [occupied] in the way.") - return 0 - return 1 - -/obj/structure/proc/turf_is_crowded() - var/turf/T = get_turf(src) - if(!T || !istype(T)) - return "empty void" - if(T.density) - return T - for(var/obj/O in T.contents) - if(istype(O,/obj/structure)) - var/obj/structure/S = O - if(S.climbable) continue - if(O && O.density && !(O.flags & ON_BORDER)) //ON_BORDER structures are handled by the Adjacent() check. - return O - return 0 - -/obj/structure/proc/do_climb(var/mob/living/user) - if (!can_climb(user)) - return - - usr.visible_message("[user] starts climbing onto \the [src]!") - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? climb_delay * 0.6 : climb_delay))) - LAZYREMOVE(climbers, user) - return - - if (!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - usr.forceMove(climb_to(user)) - - if (get_turf(user) == get_turf(src)) - usr.visible_message("[user] climbs onto \the [src]!") - LAZYREMOVE(climbers, user) - -/obj/structure/proc/climb_to(var/mob/living/user) - return get_turf(src) - -/obj/structure/proc/structure_shaken() - for(var/mob/living/M in climbers) - M.Weaken(1) - to_chat(M, "You topple as you are shaken off \the [src]!") - climbers.Cut(1,2) - - for(var/mob/living/M in get_turf(src)) - if(M.lying) return //No spamming this on people. - - M.Weaken(3) - to_chat(M, "You topple as \the [src] moves under you!") - - if(prob(25)) - - var/damage = rand(15,30) - var/mob/living/carbon/human/H = M - if(!istype(H)) - to_chat(H, "You land heavily!") - M.adjustBruteLoss(damage) - return - - var/obj/item/organ/external/affecting - - switch(pick(list("ankle","wrist","head","knee","elbow"))) - if("ankle") - affecting = H.get_organ(pick(BP_L_FOOT, BP_R_FOOT)) - if("knee") - affecting = H.get_organ(pick(BP_L_LEG, BP_R_LEG)) - if("wrist") - affecting = H.get_organ(pick(BP_L_HAND, BP_R_HAND)) - if("elbow") - affecting = H.get_organ(pick(BP_L_ARM, BP_R_ARM)) - if("head") - affecting = H.get_organ(BP_HEAD) - - if(affecting) - to_chat(M, "You land heavily on your [affecting.name]!") - affecting.take_damage(damage, 0) - if(affecting.parent) - affecting.parent.add_autopsy_data("Misadventure", damage) - else - to_chat(H, "You land heavily!") - H.adjustBruteLoss(damage) - - H.UpdateDamageIcon() - H.updatehealth() - return - -/obj/structure/proc/can_touch(var/mob/user) - if (!user) - return 0 - if(!Adjacent(user)) - return 0 - if (user.restrained() || user.buckled) - to_chat(user, "You need your hands and legs free for this.") - return 0 - if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) - return 0 - if (isAI(user)) - to_chat(user, "You need hands for this.") - return 0 - return 1 - -/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb) - if(!breakable || damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) - return 0 - visible_message("[user] [attack_verb] the [src] apart!") - user.do_attack_animation(src) - spawn(1) qdel(src) - return 1 - -/obj/structure/proc/can_visually_connect() - return anchored - -/obj/structure/proc/can_visually_connect_to(var/obj/structure/S) - return istype(S, src) - -/obj/structure/proc/update_connections(propagate = 0) - var/list/dirs = list() - var/list/other_dirs = list() - - for(var/obj/structure/S in orange(src, 1)) - if(can_visually_connect_to(S)) - if(S.can_visually_connect()) - if(propagate) - S.update_connections() - S.update_icon() - dirs += get_dir(src, S) - - if(!can_visually_connect()) - connections = list("0", "0", "0", "0") - other_connections = list("0", "0", "0", "0") - return FALSE - - for(var/direction in cardinal) - var/turf/T = get_step(src, direction) - var/success = 0 - for(var/b_type in blend_objects) - if(istype(T, b_type)) - success = 1 - if(propagate) - var/turf/simulated/wall/W = T - if(istype(W)) - W.update_connections(1) - if(success) - break // breaks inner loop - if(!success) - blend_obj_loop: - for(var/obj/O in T) - for(var/b_type in blend_objects) - if(istype(O, b_type)) - success = 1 - for(var/obj/structure/S in T) - if(istype(S, src)) - success = 0 - for(var/nb_type in noblend_objects) - if(istype(O, nb_type)) - success = 0 - - if(success) - break blend_obj_loop // breaks outer loop - - if(success) - dirs += get_dir(src, T) - other_dirs += get_dir(src, T) - - refresh_neighbors() - - connections = dirs_to_corner_states(dirs) - other_connections = dirs_to_corner_states(other_dirs) - return TRUE - -/obj/structure/proc/refresh_neighbors() - for(var/turf/T as anything in RANGE_TURFS(1, src)) - T.update_icon() +/obj/structure + icon = 'icons/obj/structures.dmi' + w_class = ITEMSIZE_NO_CONTAINER + blocks_emissive = EMISSIVE_BLOCK_GENERIC + + var/climbable + var/climb_delay = 3.5 SECONDS + var/breakable + var/parts + var/list/climbers + var/block_turf_edges = FALSE // If true, turf edge icons will not be made on the turf this occupies. + + var/list/connections + var/list/other_connections + var/list/blend_objects = newlist() // Objects which to blend with + var/list/noblend_objects = newlist() //Objects to avoid blending with (such as children of listed blend objects. + +/obj/structure/Initialize() + . = ..() + if(climbable) + verbs += /obj/structure/proc/climb_on + +/obj/structure/Destroy() + if(parts) + new parts(loc) + return ..() + +/obj/structure/attack_hand(mob/user) + if(breakable) + if(HULK in user.mutations) + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + attack_generic(user,1,"smashes") + else if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(user)) + attack_generic(user,1,"slices") + + if(LAZYLEN(climbers) && !(user in climbers)) + user.visible_message("[user.name] shakes \the [src].", \ + "You shake \the [src].") + structure_shaken() + + return ..() + +/obj/structure/attack_tk() + return + +/obj/structure/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if(prob(50)) + qdel(src) + return + if(3.0) + return + +/obj/structure/proc/climb_on() + set name = "Climb structure" + set desc = "Climbs onto a structure." + set category = "Object" + set src in oview(1) + + do_climb(usr) + +/obj/structure/MouseDrop_T(mob/target, mob/user) + var/mob/living/H = user + if(istype(H) && can_climb(H) && target == user) + do_climb(target) + else + return ..() + +/obj/structure/proc/can_climb(var/mob/living/user, post_climb_check=0) + if (!climbable || !can_touch(user) || (!post_climb_check && (user in climbers))) + return 0 + + if (!user.Adjacent(src)) + to_chat(user, "You can't climb there, the way is blocked.") + return 0 + + var/obj/occupied = turf_is_crowded() + if(occupied) + to_chat(user, "There's \a [occupied] in the way.") + return 0 + return 1 + +/obj/structure/proc/turf_is_crowded() + var/turf/T = get_turf(src) + if(!T || !istype(T)) + return "empty void" + if(T.density) + return T + for(var/obj/O in T.contents) + if(istype(O,/obj/structure)) + var/obj/structure/S = O + if(S.climbable) continue + if(O && O.density && !(O.flags & ON_BORDER)) //ON_BORDER structures are handled by the Adjacent() check. + return O + return 0 + +/obj/structure/proc/do_climb(var/mob/living/user) + if (!can_climb(user)) + return + + usr.visible_message("[user] starts climbing onto \the [src]!") + LAZYDISTINCTADD(climbers, user) + + if(!do_after(user,(issmall(user) ? climb_delay * 0.6 : climb_delay))) + LAZYREMOVE(climbers, user) + return + + if (!can_climb(user, post_climb_check=1)) + LAZYREMOVE(climbers, user) + return + + usr.forceMove(climb_to(user)) + + if (get_turf(user) == get_turf(src)) + usr.visible_message("[user] climbs onto \the [src]!") + LAZYREMOVE(climbers, user) + +/obj/structure/proc/climb_to(var/mob/living/user) + return get_turf(src) + +/obj/structure/proc/structure_shaken() + for(var/mob/living/M in climbers) + M.Weaken(1) + to_chat(M, "You topple as you are shaken off \the [src]!") + climbers.Cut(1,2) + + for(var/mob/living/M in get_turf(src)) + if(M.lying) return //No spamming this on people. + + M.Weaken(3) + to_chat(M, "You topple as \the [src] moves under you!") + + if(prob(25)) + + var/damage = rand(15,30) + var/mob/living/carbon/human/H = M + if(!istype(H)) + to_chat(H, "You land heavily!") + M.adjustBruteLoss(damage) + return + + var/obj/item/organ/external/affecting + + switch(pick(list("ankle","wrist","head","knee","elbow"))) + if("ankle") + affecting = H.get_organ(pick(BP_L_FOOT, BP_R_FOOT)) + if("knee") + affecting = H.get_organ(pick(BP_L_LEG, BP_R_LEG)) + if("wrist") + affecting = H.get_organ(pick(BP_L_HAND, BP_R_HAND)) + if("elbow") + affecting = H.get_organ(pick(BP_L_ARM, BP_R_ARM)) + if("head") + affecting = H.get_organ(BP_HEAD) + + if(affecting) + to_chat(M, "You land heavily on your [affecting.name]!") + affecting.take_damage(damage, 0) + if(affecting.parent) + affecting.parent.add_autopsy_data("Misadventure", damage) + else + to_chat(H, "You land heavily!") + H.adjustBruteLoss(damage) + + H.UpdateDamageIcon() + H.updatehealth() + return + +/obj/structure/proc/can_touch(var/mob/user) + if (!user) + return 0 + if(!Adjacent(user)) + return 0 + if (user.restrained() || user.buckled) + to_chat(user, "You need your hands and legs free for this.") + return 0 + if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) + return 0 + if (isAI(user)) + to_chat(user, "You need hands for this.") + return 0 + return 1 + +/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb) + if(!breakable || damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) + return 0 + visible_message("[user] [attack_verb] the [src] apart!") + user.do_attack_animation(src) + spawn(1) qdel(src) + return 1 + +/obj/structure/proc/can_visually_connect() + return anchored + +/obj/structure/proc/can_visually_connect_to(var/obj/structure/S) + return istype(S, src) + +/obj/structure/proc/update_connections(propagate = 0) + var/list/dirs = list() + var/list/other_dirs = list() + + for(var/obj/structure/S in orange(src, 1)) + if(can_visually_connect_to(S)) + if(S.can_visually_connect()) + if(propagate) + S.update_connections() + S.update_icon() + dirs += get_dir(src, S) + + if(!can_visually_connect()) + connections = list("0", "0", "0", "0") + other_connections = list("0", "0", "0", "0") + return FALSE + + for(var/direction in cardinal) + var/turf/T = get_step(src, direction) + var/success = 0 + for(var/b_type in blend_objects) + if(istype(T, b_type)) + success = 1 + if(propagate) + var/turf/simulated/wall/W = T + if(istype(W)) + W.update_connections(1) + if(success) + break // breaks inner loop + if(!success) + blend_obj_loop: + for(var/obj/O in T) + for(var/b_type in blend_objects) + if(istype(O, b_type)) + success = 1 + for(var/obj/structure/S in T) + if(istype(S, src)) + success = 0 + for(var/nb_type in noblend_objects) + if(istype(O, nb_type)) + success = 0 + + if(success) + break blend_obj_loop // breaks outer loop + + if(success) + dirs += get_dir(src, T) + other_dirs += get_dir(src, T) + + refresh_neighbors() + + connections = dirs_to_corner_states(dirs) + other_connections = dirs_to_corner_states(other_dirs) + return TRUE + +/obj/structure/proc/refresh_neighbors() + for(var/turf/T as anything in RANGE_TURFS(1, src)) + T.update_icon() diff --git a/code/game/objects/structures/alien/alien.dm b/code/game/objects/structures/alien/alien.dm index 0979677fdea..cde036bc68f 100644 --- a/code/game/objects/structures/alien/alien.dm +++ b/code/game/objects/structures/alien/alien.dm @@ -1,93 +1,93 @@ -/obj/structure/alien //Gurg Addition, framework for alien structures. - name = "alien thing" - desc = "There's something alien about this." - icon = 'icons/mob/alien.dmi' - layer = ABOVE_JUNK_LAYER - var/health = 50 - unacidable = TRUE - anchored = TRUE - -/obj/structure/alien/proc/healthcheck() - if(health <=0) - set_density(0) - qdel(src) - return - -/obj/structure/alien/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.damage - ..() - healthcheck() - return - -/obj/structure/alien/ex_act(severity) - switch(severity) - if(1.0) - health-=50 - if(2.0) - health-=50 - if(3.0) - if (prob(50)) - health-=50 - else - health-=25 - healthcheck() - return - -/obj/structure/alien/hitby(AM as mob|obj) - ..() - visible_message("\The [src] was hit by \the [AM].") - var/tforce = 0 - if(ismob(AM)) - tforce = 10 - else - tforce = AM:throwforce - playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) - health = max(0, health - tforce) - healthcheck() - ..() - return - -/obj/structure/alien/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - playsound(src, 'sound/effects/attackblob.ogg', 100, 1) - user.do_attack_animation(src) - health -= damage - healthcheck() - return - -/obj/structure/alien/attackby(obj/item/weapon/W as obj, mob/user as mob) - - user.setClickCooldown(user.get_attack_speed(W)) - var/aforce = W.force - health = max(0, health - aforce) - playsound(src, 'sound/effects/attackblob.ogg', 100, 1) - visible_message("[user] attacks the [src]!") - healthcheck() - ..() - return - -/obj/structure/alien/attack_hand() - usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if (HULK in usr.mutations) - visible_message("[usr] destroys the [name]!") - health = 0 - else - - // Aliens can get straight through these. - if(istype(usr,/mob/living/carbon)) - var/mob/living/carbon/M = usr - if(locate(/obj/item/organ/internal/xenos/hivenode) in M.internal_organs) - visible_message ("[usr] strokes the [name] and it melts away!", 1) - health = 0 - healthcheck() - return - visible_message("[usr] claws at the [name]!") - health -= rand(5,10) - healthcheck() - return - -/obj/structure/alien/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group) return 0 - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density +/obj/structure/alien //Gurg Addition, framework for alien structures. + name = "alien thing" + desc = "There's something alien about this." + icon = 'icons/mob/alien.dmi' + layer = ABOVE_JUNK_LAYER + var/health = 50 + unacidable = TRUE + anchored = TRUE + +/obj/structure/alien/proc/healthcheck() + if(health <=0) + set_density(0) + qdel(src) + return + +/obj/structure/alien/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.damage + ..() + healthcheck() + return + +/obj/structure/alien/ex_act(severity) + switch(severity) + if(1.0) + health-=50 + if(2.0) + health-=50 + if(3.0) + if (prob(50)) + health-=50 + else + health-=25 + healthcheck() + return + +/obj/structure/alien/hitby(AM as mob|obj) + ..() + visible_message("\The [src] was hit by \the [AM].") + var/tforce = 0 + if(ismob(AM)) + tforce = 10 + else + tforce = AM:throwforce + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + health = max(0, health - tforce) + healthcheck() + ..() + return + +/obj/structure/alien/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(src, 'sound/effects/attackblob.ogg', 100, 1) + user.do_attack_animation(src) + health -= damage + healthcheck() + return + +/obj/structure/alien/attackby(obj/item/weapon/W as obj, mob/user as mob) + + user.setClickCooldown(user.get_attack_speed(W)) + var/aforce = W.force + health = max(0, health - aforce) + playsound(src, 'sound/effects/attackblob.ogg', 100, 1) + visible_message("[user] attacks the [src]!") + healthcheck() + ..() + return + +/obj/structure/alien/attack_hand() + usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if (HULK in usr.mutations) + visible_message("[usr] destroys the [name]!") + health = 0 + else + + // Aliens can get straight through these. + if(istype(usr,/mob/living/carbon)) + var/mob/living/carbon/M = usr + if(locate(/obj/item/organ/internal/xenos/hivenode) in M.internal_organs) + visible_message ("[usr] strokes the [name] and it melts away!", 1) + health = 0 + healthcheck() + return + visible_message("[usr] claws at the [name]!") + health -= rand(5,10) + healthcheck() + return + +/obj/structure/alien/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(air_group) return 0 + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm index ea9cf8eb067..072bd9cddbb 100644 --- a/code/game/objects/structures/artstuff.dm +++ b/code/game/objects/structures/artstuff.dm @@ -394,7 +394,7 @@ frame_canvas(user, I) else if(current_canvas && current_canvas.painting_name == initial(current_canvas.painting_name) && istype(I,/obj/item/weapon/pen)) try_rename(user) - else if(current_canvas && I.is_wirecutter()) + else if(current_canvas && I.has_tool_quality(TOOL_WIRECUTTER)) unframe_canvas(user) else return ..() diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 50869c4497c..19c2db2b82e 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -1,248 +1,250 @@ -/* -CONTAINS: -BEDSHEETS -LINEN BINS -*/ - -/obj/item/weapon/bedsheet - name = "bedsheet" - desc = "A surprisingly soft linen bedsheet." - icon = 'icons/obj/items.dmi' - icon_state = "sheet" - slot_flags = SLOT_BACK - plane = MOB_PLANE - layer = BELOW_MOB_LAYER - throwforce = 1 - throw_speed = 1 - throw_range = 2 - w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/clothing.ogg' - -/obj/item/weapon/bedsheet/attack_self(mob/user as mob) - user.drop_item() - if(layer == initial(layer)) - layer = ABOVE_MOB_LAYER - else - reset_plane_and_layer() - add_fingerprint(user) - return - -/obj/item/weapon/bedsheet/attackby(obj/item/I, mob/user) - if(is_sharp(I)) - user.visible_message("\The [user] begins cutting up [src] with [I].", "You begin cutting up [src] with [I].") - if(do_after(user, 50)) - to_chat(user, "You cut [src] into pieces!") - for(var/i in 1 to rand(2,5)) - new /obj/item/weapon/reagent_containers/glass/rag(drop_location()) - qdel(src) - return - ..() - -/obj/item/weapon/bedsheet/blue - icon_state = "sheetblue" - -/obj/item/weapon/bedsheet/green - icon_state = "sheetgreen" - -/obj/item/weapon/bedsheet/orange - icon_state = "sheetorange" - -/obj/item/weapon/bedsheet/purple - icon_state = "sheetpurple" - -/obj/item/weapon/bedsheet/rainbow - icon_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/red - icon_state = "sheetred" - -/obj/item/weapon/bedsheet/yellow - icon_state = "sheetyellow" - -/obj/item/weapon/bedsheet/mime - icon_state = "sheetmime" - -/obj/item/weapon/bedsheet/clown - icon_state = "sheetclown" - item_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/captain - icon_state = "sheetcaptain" - -/obj/item/weapon/bedsheet/rd - icon_state = "sheetrd" - -/obj/item/weapon/bedsheet/medical - icon_state = "sheetmedical" - -/obj/item/weapon/bedsheet/hos - icon_state = "sheethos" - -/obj/item/weapon/bedsheet/hop - icon_state = "sheethop" - -/obj/item/weapon/bedsheet/ce - icon_state = "sheetce" - -/obj/item/weapon/bedsheet/brown - icon_state = "sheetbrown" - -/obj/item/weapon/bedsheet/ian - icon_state = "sheetian" - -/obj/item/weapon/bedsheet/double - icon_state = "doublesheet" - item_state = "sheet" - -/obj/item/weapon/bedsheet/bluedouble - icon_state = "doublesheetblue" - item_state = "sheetblue" - -/obj/item/weapon/bedsheet/greendouble - icon_state = "doublesheetgreen" - item_state = "sheetgreen" - -/obj/item/weapon/bedsheet/orangedouble - icon_state = "doublesheetorange" - item_state = "sheetorange" - -/obj/item/weapon/bedsheet/purpledouble - icon_state = "doublesheetpurple" - item_state = "sheetpurple" - -/obj/item/weapon/bedsheet/rainbowdouble //all the way across the sky. - icon_state = "doublesheetrainbow" - item_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/reddouble - icon_state = "doublesheetred" - item_state = "sheetred" - -/obj/item/weapon/bedsheet/yellowdouble - icon_state = "doublesheetyellow" - item_state = "sheetyellow" - -/obj/item/weapon/bedsheet/mimedouble - icon_state = "doublesheetmime" - item_state = "sheetmime" - -/obj/item/weapon/bedsheet/clowndouble - icon_state = "doublesheetclown" - item_state = "sheetrainbow" - -/obj/item/weapon/bedsheet/captaindouble - icon_state = "doublesheetcaptain" - item_state = "sheetcaptain" - -/obj/item/weapon/bedsheet/rddouble - icon_state = "doublesheetrd" - item_state = "sheetrd" - -/obj/item/weapon/bedsheet/hosdouble - icon_state = "doublesheethos" - item_state = "sheethos" - -/obj/item/weapon/bedsheet/hopdouble - icon_state = "doublesheethop" - item_state = "sheethop" - -/obj/item/weapon/bedsheet/cedouble - icon_state = "doublesheetce" - item_state = "sheetce" - -/obj/item/weapon/bedsheet/browndouble - icon_state = "doublesheetbrown" - item_state = "sheetbrown" - -/obj/item/weapon/bedsheet/iandouble - icon_state = "doublesheetian" - item_state = "sheetian" - -/obj/structure/bedsheetbin - name = "linen bin" - desc = "A linen bin. It looks rather cosy." - icon = 'icons/obj/structures.dmi' - icon_state = "linenbin-full" - anchored = TRUE - var/amount = 20 - var/list/sheets = list() - var/obj/item/hidden = null - - -/obj/structure/bedsheetbin/examine(mob/user) - . = ..() - - if(amount < 1) - . += "There are no bed sheets in the bin." - else if(amount == 1) - . += "There is one bed sheet in the bin." - else - . += "There are [amount] bed sheets in the bin." - -/obj/structure/bedsheetbin/update_icon() - switch(amount) - if(0) icon_state = "linenbin-empty" - if(1 to amount / 2) icon_state = "linenbin-half" - else icon_state = "linenbin-full" - - -/obj/structure/bedsheetbin/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/weapon/bedsheet)) - user.drop_item() - I.loc = src - sheets.Add(I) - amount++ - to_chat(user, "You put [I] in [src].") - else if(amount && !hidden && I.w_class < ITEMSIZE_LARGE) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. - user.drop_item() - I.loc = src - hidden = I - to_chat(user, "You hide [I] among the sheets.") - -/obj/structure/bedsheetbin/attack_hand(mob/user as mob) - if(amount >= 1) - amount-- - - var/obj/item/weapon/bedsheet/B - if(sheets.len > 0) - B = sheets[sheets.len] - sheets.Remove(B) - - else - B = new /obj/item/weapon/bedsheet(loc) - - B.loc = user.loc - user.put_in_hands(B) - to_chat(user, "You take [B] out of [src].") - - if(hidden) - hidden.loc = user.loc - to_chat(user, "[hidden] falls out of [B]!") - hidden = null - - - add_fingerprint(user) - -/obj/structure/bedsheetbin/attack_tk(mob/user as mob) - if(amount >= 1) - amount-- - - var/obj/item/weapon/bedsheet/B - if(sheets.len > 0) - B = sheets[sheets.len] - sheets.Remove(B) - - else - B = new /obj/item/weapon/bedsheet(loc) - - B.loc = loc - to_chat(user, "You telekinetically remove [B] from [src].") - update_icon() - - if(hidden) - hidden.loc = loc - hidden = null - - - add_fingerprint(user) +/* +CONTAINS: +BEDSHEETS +LINEN BINS +*/ + +/obj/item/weapon/bedsheet + name = "bedsheet" + desc = "A surprisingly soft linen bedsheet." + icon = 'icons/obj/items.dmi' + icon_state = "sheet" + slot_flags = SLOT_BACK + plane = MOB_PLANE + layer = BELOW_MOB_LAYER + throwforce = 1 + throw_speed = 1 + throw_range = 2 + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' + +/obj/item/weapon/bedsheet/attack_self(mob/user as mob) + user.drop_item() + if(layer == initial(layer)) + layer = ABOVE_MOB_LAYER + else + reset_plane_and_layer() + add_fingerprint(user) + return + +/obj/item/weapon/bedsheet/attackby(obj/item/I, mob/user) + if(is_sharp(I)) + user.visible_message("\The [user] begins cutting up [src] with [I].", "You begin cutting up [src] with [I].") + if(do_after(user, 50)) + to_chat(user, "You cut [src] into pieces!") + for(var/i in 1 to rand(2,5)) + new /obj/item/weapon/reagent_containers/glass/rag(drop_location()) + qdel(src) + return + ..() + +/obj/item/weapon/bedsheet/blue + icon_state = "sheetblue" + +/obj/item/weapon/bedsheet/green + icon_state = "sheetgreen" + +/obj/item/weapon/bedsheet/orange + icon_state = "sheetorange" + +/obj/item/weapon/bedsheet/purple + icon_state = "sheetpurple" + +/obj/item/weapon/bedsheet/rainbow + icon_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/red + icon_state = "sheetred" + +/obj/item/weapon/bedsheet/yellow + icon_state = "sheetyellow" + +/obj/item/weapon/bedsheet/mime + icon_state = "sheetmime" + +/obj/item/weapon/bedsheet/clown + icon_state = "sheetclown" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/captain + icon_state = "sheetcaptain" + +/obj/item/weapon/bedsheet/rd + icon_state = "sheetrd" + +/obj/item/weapon/bedsheet/medical + icon_state = "sheetmedical" + +/obj/item/weapon/bedsheet/hos + icon_state = "sheethos" + +/obj/item/weapon/bedsheet/hop + icon_state = "sheethop" + +/obj/item/weapon/bedsheet/ce + icon_state = "sheetce" + +/obj/item/weapon/bedsheet/brown + icon_state = "sheetbrown" + +/obj/item/weapon/bedsheet/ian + icon_state = "sheetian" + +/obj/item/weapon/bedsheet/double + icon_state = "doublesheet" + item_state = "sheet" + +/obj/item/weapon/bedsheet/bluedouble + icon_state = "doublesheetblue" + item_state = "sheetblue" + +/obj/item/weapon/bedsheet/greendouble + icon_state = "doublesheetgreen" + item_state = "sheetgreen" + +/obj/item/weapon/bedsheet/orangedouble + icon_state = "doublesheetorange" + item_state = "sheetorange" + +/obj/item/weapon/bedsheet/purpledouble + icon_state = "doublesheetpurple" + item_state = "sheetpurple" + +/obj/item/weapon/bedsheet/rainbowdouble //all the way across the sky. + icon_state = "doublesheetrainbow" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/reddouble + icon_state = "doublesheetred" + item_state = "sheetred" + +/obj/item/weapon/bedsheet/yellowdouble + icon_state = "doublesheetyellow" + item_state = "sheetyellow" + +/obj/item/weapon/bedsheet/mimedouble + icon_state = "doublesheetmime" + item_state = "sheetmime" + +/obj/item/weapon/bedsheet/clowndouble + icon_state = "doublesheetclown" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/captaindouble + icon_state = "doublesheetcaptain" + item_state = "sheetcaptain" + +/obj/item/weapon/bedsheet/rddouble + icon_state = "doublesheetrd" + item_state = "sheetrd" + +/obj/item/weapon/bedsheet/hosdouble + icon_state = "doublesheethos" + item_state = "sheethos" + +/obj/item/weapon/bedsheet/hopdouble + icon_state = "doublesheethop" + item_state = "sheethop" + +/obj/item/weapon/bedsheet/cedouble + icon_state = "doublesheetce" + item_state = "sheetce" + +/obj/item/weapon/bedsheet/browndouble + icon_state = "doublesheetbrown" + item_state = "sheetbrown" + +/obj/item/weapon/bedsheet/iandouble + icon_state = "doublesheetian" + item_state = "sheetian" + +/obj/structure/bedsheetbin + name = "linen bin" + desc = "A linen bin. It looks rather cosy." + icon = 'icons/obj/structures.dmi' + icon_state = "linenbin-full" + anchored = TRUE + var/amount = 20 + var/list/sheets = list() + var/obj/item/hidden = null + + +/obj/structure/bedsheetbin/examine(mob/user) + . = ..() + + if(amount < 1) + . += "There are no bed sheets in the bin." + else if(amount == 1) + . += "There is one bed sheet in the bin." + else + . += "There are [amount] bed sheets in the bin." + +/obj/structure/bedsheetbin/update_icon() + if(amount == 0) + icon_state = "linenbin-empty" + else if(amount >= 1 && amount < (initial(amount) / 2)) + icon_state = "linenbin-half" + else + icon_state = "linenbin-full" + + +/obj/structure/bedsheetbin/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I, /obj/item/weapon/bedsheet)) + user.drop_item() + I.loc = src + sheets.Add(I) + amount++ + to_chat(user, "You put [I] in [src].") + else if(amount && !hidden && I.w_class < ITEMSIZE_LARGE) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. + user.drop_item() + I.loc = src + hidden = I + to_chat(user, "You hide [I] among the sheets.") + +/obj/structure/bedsheetbin/attack_hand(mob/user as mob) + if(amount >= 1) + amount-- + + var/obj/item/weapon/bedsheet/B + if(sheets.len > 0) + B = sheets[sheets.len] + sheets.Remove(B) + + else + B = new /obj/item/weapon/bedsheet(loc) + + B.loc = user.loc + user.put_in_hands(B) + to_chat(user, "You take [B] out of [src].") + + if(hidden) + hidden.loc = user.loc + to_chat(user, "[hidden] falls out of [B]!") + hidden = null + + + add_fingerprint(user) + +/obj/structure/bedsheetbin/attack_tk(mob/user as mob) + if(amount >= 1) + amount-- + + var/obj/item/weapon/bedsheet/B + if(sheets.len > 0) + B = sheets[sheets.len] + sheets.Remove(B) + + else + B = new /obj/item/weapon/bedsheet(loc) + + B.loc = loc + to_chat(user, "You telekinetically remove [B] from [src].") + update_icon() + + if(hidden) + hidden.loc = loc + hidden = null + + + add_fingerprint(user) diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index ae5dc0c6661..ffed750917f 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -87,12 +87,12 @@ qdel(src) /obj/structure/catwalk/attackby(obj/item/C as obj, mob/user as mob) - if(istype(C, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = C + if(C.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = C.get_welder() if(WT.isOn() && WT.remove_fuel(0, user)) deconstruct(user) return - if(C.is_crowbar() && plated_tile) + if(C.has_tool_quality(TOOL_CROWBAR) && plated_tile) hatch_open = !hatch_open if(hatch_open) playsound(src, 'sound/items/Crowbar.ogg', 100, 2) diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm index 016727ac344..6370e64271b 100644 --- a/code/game/objects/structures/cliff.dm +++ b/code/game/objects/structures/cliff.dm @@ -1,272 +1,272 @@ -GLOBAL_LIST_EMPTY(cliff_icon_cache) - -/* -Cliffs give a visual illusion of depth by seperating two places while presenting a 'top' and 'bottom' side. - -Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, -where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. - -Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. - -Flying mobs can pass over all cliffs with no risk of falling. - -Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. -This makes fighting something that is on top of a cliff more challenging. - -As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. - -When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to -two tiles on initialization, and which way a cliff is facing may change during maploading. -*/ - -/obj/structure/cliff - name = "cliff" - desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." - description_info = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
                    \ - You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
                    \ - Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ - on top." - icon = 'icons/obj/flora/rocks.dmi' - - anchored = TRUE - density = TRUE - opacity = FALSE - climbable = TRUE - climb_delay = 10 SECONDS - unacidable = TRUE - block_turf_edges = TRUE // Don't want turf edges popping up from the cliff edge. - plane = TURF_PLANE - - var/icon_variant = null // Used to make cliffs less repeative by having a selection of sprites to display. - var/corner = FALSE // Used for icon things. - var/ramp = FALSE // Ditto. - var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. - - var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. - var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. - -/obj/structure/cliff/Initialize() - . = ..() - register_dangerous_to_step() - -/obj/structure/cliff/Destroy() - unregister_dangerous_to_step() - . = ..() - -/obj/structure/cliff/Moved(atom/oldloc) - . = ..() - if(.) - var/turf/old_turf = get_turf(oldloc) - var/turf/new_turf = get_turf(src) - if(old_turf != new_turf) - old_turf.unregister_dangerous_object(src) - new_turf.register_dangerous_object(src) - -// These arrange their sprites at runtime, as opposed to being statically placed in the map file. -/obj/structure/cliff/automatic - icon_state = "cliffbuilder" - dir = NORTH - -/obj/structure/cliff/automatic/corner - icon_state = "cliffbuilder-corner" - dir = NORTHEAST - corner = TRUE - -// Tiny part that doesn't block, used for making 'ramps'. -/obj/structure/cliff/automatic/ramp - icon_state = "cliffbuilder-ramp" - dir = NORTHEAST - density = FALSE - ramp = TRUE - -// Made automatically as needed by automatic cliffs. -/obj/structure/cliff/bottom - bottom = TRUE - -/obj/structure/cliff/automatic/Initialize() - ..() - return INITIALIZE_HINT_LATELOAD - -// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. -/obj/structure/cliff/automatic/LateInitialize() - if(dir in GLOB.cardinal) - icon_variant = pick("a", "b", "c") - - if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. - make_bottom() - - update_icon() - -/obj/structure/cliff/proc/make_bottom() - // First, make sure there's room to put the bottom side. - var/turf/T = locate(x, y - 1, z) - if(!istype(T)) - return FALSE - - // Now make the bottom cliff have mostly the same variables. - var/obj/structure/cliff/bottom/bottom = new(T) - is_double_cliff = TRUE - climb_delay /= 2 // Since there are two cliffs to climb when going north, both take half the time. - - bottom.dir = dir - bottom.is_double_cliff = TRUE - bottom.climb_delay = climb_delay - bottom.icon_variant = icon_variant - bottom.corner = corner - bottom.ramp = ramp - bottom.layer = layer - 0.1 - bottom.density = density - bottom.update_icon() - -/obj/structure/cliff/set_dir(new_dir) - ..() - update_icon() - -/obj/structure/cliff/update_icon() - icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" - - // Now for making the top-side look like a different turf. - var/turf/T = get_step(src, dir) - if(!istype(T)) - return - - var/subtraction_icon_state = "[icon_state]-subtract" - var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]" - if(T && (subtraction_icon_state in cached_icon_states(icon))) - cut_overlays() - // If we've made the same icon before, just recycle it. - if(cache_string in GLOB.cliff_icon_cache) - add_overlay(GLOB.cliff_icon_cache[cache_string]) - - else // Otherwise make a new one, but only once. - var/icon/underlying_ground = icon(T.icon, T.icon_state, T.dir) - var/icon/subtract = icon(icon, subtraction_icon_state) - underlying_ground.Blend(subtract, ICON_SUBTRACT) - var/image/final = image(underlying_ground) - final.layer = src.layer - 0.2 - GLOB.cliff_icon_cache[cache_string] = final - add_overlay(final) - - -// Movement-related code. - -/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) - if(isliving(mover)) - var/mob/living/L = mover - if(L.hovering || L.flying) // Flying mobs can always pass. - return TRUE - return ..() - - // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. - else if(istype(mover, /obj)) - var/obj/O = mover - if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. - if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. - return FALSE - return TRUE - -/obj/structure/cliff/Bumped(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(should_fall(L)) - fall_off_cliff(L) - return - ..() - -/obj/structure/cliff/proc/should_fall(mob/living/L) - if(L.hovering || L.flying) - return FALSE - - var/turf/T = get_turf(L) - if(T && get_dir(T, loc) & reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. - return TRUE - return FALSE - -/obj/structure/cliff/proc/fall_off_cliff(mob/living/L) - if(!istype(L)) - return FALSE - var/turf/T = get_step(src, reverse_dir[dir]) - var/displaced = FALSE - - if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. - for(var/i = 1 to 2) - var/turf/new_T = locate(T.x, T.y - i, T.z) - if(!new_T || locate(/obj/structure/cliff) in new_T) - break - T = new_T - displaced = TRUE - - if(istype(T)) - - var/safe_fall = FALSE - if(ishuman(L)) - var/mob/living/carbon/human/H = L - safe_fall = H.species.handle_falling(H, T, silent = TRUE, planetary = FALSE) - - if(safe_fall) - visible_message(span("notice", "\The [L] glides down from \the [src].")) - else - visible_message(span("danger", "\The [L] falls off \the [src]!")) - L.forceMove(T) - - var/harm = !is_double_cliff ? 1 : 0.5 - if(!safe_fall) - // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. - if(istype(L.buckled, /obj/vehicle)) // People falling off in vehicles will take less damage, but will damage the vehicle severely. - var/obj/vehicle/vehicle = L.buckled - vehicle.adjust_health(40 * harm) - to_chat(L, span("warning", "\The [vehicle] absorbs some of the impact, damaging it.")) - harm /= 2 - - playsound(L, 'sound/effects/break_stone.ogg', 70, 1) - L.Weaken(5 * harm) - - var/fall_time = 3 - if(displaced) // Make the fall look more natural when falling sideways. - L.pixel_z = 32 * 2 - animate(L, pixel_z = 0, time = fall_time) - sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. - - if(safe_fall) - visible_message(span("notice", "\The [L] lands on \the [T].")) - playsound(L, "rustle", 25, 1) - return - - playsound(L, "punch", 70, 1) - shake_camera(L, 1, 1) - - visible_message(span("danger", "\The [L] hits \the [T]!")) - - // The bigger they are, the harder they fall. - // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. - // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. - var/damage = between(20, L.getMaxHealth() * 0.4, 100) - var/target_zone = ran_zone() - var/blocked = L.run_armor_check(target_zone, "melee") * harm - var/soaked = L.get_armor_soak(target_zone, "melee") * harm - - L.apply_damage(damage * harm, BRUTE, target_zone, blocked, soaked, used_weapon=src) - - // Now fall off more cliffs below this one if they exist. - var/obj/structure/cliff/bottom_cliff = locate() in T - if(bottom_cliff) - visible_message(span("danger", "\The [L] rolls down towards \the [bottom_cliff]!")) - sleep(5) - bottom_cliff.fall_off_cliff(L) - -/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE) - // Cliff climbing requires climbing gear. - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/clothing/shoes/shoes = H.shoes - if(shoes && shoes.rock_climbing) - return ..() // Do the other checks too. - - to_chat(user, span("warning", "\The [src] is too steep to climb unassisted.")) - return FALSE - -// This tells AI mobs to not be dumb and step off cliffs willingly. -/obj/structure/cliff/is_safe_to_step(mob/living/L) - if(should_fall(L)) - return FALSE - return ..() +GLOBAL_LIST_EMPTY(cliff_icon_cache) + +/* +Cliffs give a visual illusion of depth by seperating two places while presenting a 'top' and 'bottom' side. + +Mobs moving into a cliff from the bottom side will simply bump into it and be denied moving into the tile, +where as mobs moving into a cliff from the top side will 'fall' off the cliff, forcing them to the bottom, causing significant damage and stunning them. + +Mobs can climb this while wearing climbing equipment by clickdragging themselves onto a cliff, as if it were a table. + +Flying mobs can pass over all cliffs with no risk of falling. + +Projectiles and thrown objects can pass, however if moving upwards, there is a chance for it to be stopped by the cliff. +This makes fighting something that is on top of a cliff more challenging. + +As a note, dir points upwards, e.g. pointing WEST means the left side is 'up', and the right side is 'down'. + +When mapping these in, be sure to give at least a one tile clearance, as NORTH facing cliffs expand to +two tiles on initialization, and which way a cliff is facing may change during maploading. +*/ + +/obj/structure/cliff + name = "cliff" + desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." + description_info = "Walking off the edge of a cliff while on top will cause you to fall off, causing severe injury.
                    \ + You can climb this cliff if wearing special climbing equipment, by click-dragging yourself onto the cliff.
                    \ + Projectiles traveling up a cliff may hit the cliff instead, making it more difficult to fight something \ + on top." + icon = 'icons/obj/flora/rocks.dmi' + + anchored = TRUE + density = TRUE + opacity = FALSE + climbable = TRUE + climb_delay = 10 SECONDS + unacidable = TRUE + block_turf_edges = TRUE // Don't want turf edges popping up from the cliff edge. + plane = TURF_PLANE + + var/icon_variant = null // Used to make cliffs less repeative by having a selection of sprites to display. + var/corner = FALSE // Used for icon things. + var/ramp = FALSE // Ditto. + var/bottom = FALSE // Used for 'bottom' typed cliffs, to avoid infinite cliffs, and for icons. + + var/is_double_cliff = FALSE // Set to true when making the two-tile cliffs, used for projectile checks. + var/uphill_penalty = 30 // Odds of a projectile not making it up the cliff. + +/obj/structure/cliff/Initialize() + . = ..() + register_dangerous_to_step() + +/obj/structure/cliff/Destroy() + unregister_dangerous_to_step() + . = ..() + +/obj/structure/cliff/Moved(atom/oldloc) + . = ..() + if(.) + var/turf/old_turf = get_turf(oldloc) + var/turf/new_turf = get_turf(src) + if(old_turf != new_turf) + old_turf.unregister_dangerous_object(src) + new_turf.register_dangerous_object(src) + +// These arrange their sprites at runtime, as opposed to being statically placed in the map file. +/obj/structure/cliff/automatic + icon_state = "cliffbuilder" + dir = NORTH + +/obj/structure/cliff/automatic/corner + icon_state = "cliffbuilder-corner" + dir = NORTHEAST + corner = TRUE + +// Tiny part that doesn't block, used for making 'ramps'. +/obj/structure/cliff/automatic/ramp + icon_state = "cliffbuilder-ramp" + dir = NORTHEAST + density = FALSE + ramp = TRUE + +// Made automatically as needed by automatic cliffs. +/obj/structure/cliff/bottom + bottom = TRUE + +/obj/structure/cliff/automatic/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +// Paranoid about the maploader, direction is very important to cliffs, since they may get bigger if initialized while facing NORTH. +/obj/structure/cliff/automatic/LateInitialize() + if(dir in GLOB.cardinal) + icon_variant = pick("a", "b", "c") + + if(dir & NORTH && !bottom) // North-facing cliffs require more cliffs to be made. + make_bottom() + + update_icon() + +/obj/structure/cliff/proc/make_bottom() + // First, make sure there's room to put the bottom side. + var/turf/T = locate(x, y - 1, z) + if(!istype(T)) + return FALSE + + // Now make the bottom cliff have mostly the same variables. + var/obj/structure/cliff/bottom/bottom = new(T) + is_double_cliff = TRUE + climb_delay /= 2 // Since there are two cliffs to climb when going north, both take half the time. + + bottom.dir = dir + bottom.is_double_cliff = TRUE + bottom.climb_delay = climb_delay + bottom.icon_variant = icon_variant + bottom.corner = corner + bottom.ramp = ramp + bottom.layer = layer - 0.1 + bottom.density = density + bottom.update_icon() + +/obj/structure/cliff/set_dir(new_dir) + ..() + update_icon() + +/obj/structure/cliff/update_icon() + icon_state = "cliff-[dir][icon_variant][bottom ? "-bottom" : ""][corner ? "-corner" : ""][ramp ? "-ramp" : ""]" + + // Now for making the top-side look like a different turf. + var/turf/T = get_step(src, dir) + if(!istype(T)) + return + + var/subtraction_icon_state = "[icon_state]-subtract" + var/cache_string = "[icon_state]_[T.icon]_[T.icon_state]" + if(T && (subtraction_icon_state in cached_icon_states(icon))) + cut_overlays() + // If we've made the same icon before, just recycle it. + if(cache_string in GLOB.cliff_icon_cache) + add_overlay(GLOB.cliff_icon_cache[cache_string]) + + else // Otherwise make a new one, but only once. + var/icon/underlying_ground = icon(T.icon, T.icon_state, T.dir) + var/icon/subtract = icon(icon, subtraction_icon_state) + underlying_ground.Blend(subtract, ICON_SUBTRACT) + var/image/final = image(underlying_ground) + final.layer = src.layer - 0.2 + GLOB.cliff_icon_cache[cache_string] = final + add_overlay(final) + + +// Movement-related code. + +/obj/structure/cliff/CanPass(atom/movable/mover, turf/target) + if(isliving(mover)) + var/mob/living/L = mover + if(L.hovering || L.flying) // Flying mobs can always pass. + return TRUE + return ..() + + // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. + else if(istype(mover, /obj)) + var/obj/O = mover + if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. + return FALSE + return TRUE + +/obj/structure/cliff/Bumped(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(should_fall(L)) + fall_off_cliff(L) + return + ..() + +/obj/structure/cliff/proc/should_fall(mob/living/L) + if(L.hovering || L.flying) + return FALSE + + var/turf/T = get_turf(L) + if(T && get_dir(T, loc) & reverse_dir[dir]) // dir points 'up' the cliff, e.g. cliff pointing NORTH will cause someone to fall if moving SOUTH into it. + return TRUE + return FALSE + +/obj/structure/cliff/proc/fall_off_cliff(mob/living/L) + if(!istype(L)) + return FALSE + var/turf/T = get_step(src, reverse_dir[dir]) + var/displaced = FALSE + + if(dir in list(EAST, WEST)) // Apply an offset if flying sideways, to help maintain the illusion of depth. + for(var/i = 1 to 2) + var/turf/new_T = locate(T.x, T.y - i, T.z) + if(!new_T || locate(/obj/structure/cliff) in new_T) + break + T = new_T + displaced = TRUE + + if(istype(T)) + + var/safe_fall = FALSE + if(ishuman(L)) + var/mob/living/carbon/human/H = L + safe_fall = H.species.handle_falling(H, T, silent = TRUE, planetary = FALSE) + + if(safe_fall) + visible_message(span("notice", "\The [L] glides down from \the [src].")) + else + visible_message(span("danger", "\The [L] falls off \the [src]!")) + L.forceMove(T) + + var/harm = !is_double_cliff ? 1 : 0.5 + if(!safe_fall) + // Do the actual hurting. Double cliffs do halved damage due to them most likely hitting twice. + if(istype(L.buckled, /obj/vehicle)) // People falling off in vehicles will take less damage, but will damage the vehicle severely. + var/obj/vehicle/vehicle = L.buckled + vehicle.adjust_health(40 * harm) + to_chat(L, span("warning", "\The [vehicle] absorbs some of the impact, damaging it.")) + harm /= 2 + + playsound(L, 'sound/effects/break_stone.ogg', 70, 1) + L.Weaken(5 * harm) + + var/fall_time = 3 + if(displaced) // Make the fall look more natural when falling sideways. + L.pixel_z = 32 * 2 + animate(L, pixel_z = 0, time = fall_time) + sleep(fall_time) // A brief delay inbetween the two sounds helps sell the 'ouch' effect. + + if(safe_fall) + visible_message(span("notice", "\The [L] lands on \the [T].")) + playsound(L, "rustle", 25, 1) + return + + playsound(L, "punch", 70, 1) + shake_camera(L, 1, 1) + + visible_message(span("danger", "\The [L] hits \the [T]!")) + + // The bigger they are, the harder they fall. + // They will take at least 20 damage at the minimum, and tries to scale up to 40% of their max health. + // This scaling is capped at 100 total damage, which occurs if the thing that fell has more than 250 health. + var/damage = between(20, L.getMaxHealth() * 0.4, 100) + var/target_zone = ran_zone() + var/blocked = L.run_armor_check(target_zone, "melee") * harm + var/soaked = L.get_armor_soak(target_zone, "melee") * harm + + L.apply_damage(damage * harm, BRUTE, target_zone, blocked, soaked, used_weapon=src) + + // Now fall off more cliffs below this one if they exist. + var/obj/structure/cliff/bottom_cliff = locate() in T + if(bottom_cliff) + visible_message(span("danger", "\The [L] rolls down towards \the [bottom_cliff]!")) + sleep(5) + bottom_cliff.fall_off_cliff(L) + +/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE) + // Cliff climbing requires climbing gear. + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/clothing/shoes/shoes = H.shoes + if(shoes && shoes.rock_climbing) + return ..() // Do the other checks too. + + to_chat(user, span("warning", "\The [src] is too steep to climb unassisted.")) + return FALSE + +// This tells AI mobs to not be dumb and step off cliffs willingly. +/obj/structure/cliff/is_safe_to_step(mob/living/L) + if(should_fall(L)) + return FALSE + return ..() diff --git a/code/game/objects/structures/crates_lockers/__closets.dm b/code/game/objects/structures/crates_lockers/__closets.dm index 05661ccedb0..f56c0269a41 100644 --- a/code/game/objects/structures/crates_lockers/__closets.dm +++ b/code/game/objects/structures/crates_lockers/__closets.dm @@ -271,7 +271,7 @@ return /obj/structure/closet/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) if(opened) if(anchored) user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") @@ -291,8 +291,8 @@ return 0 if(istype(W,/obj/item/tk_grab)) return 0 - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(!WT.remove_fuel(0,user)) if(!WT.isOn()) return @@ -326,8 +326,8 @@ else if(seal_tool) if(istype(W, seal_tool)) var/obj/item/weapon/S = W - if(istype(S, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = S + if(S.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = S.get_welder() if(!WT.remove_fuel(0,user)) if(!WT.isOn()) return diff --git a/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm b/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm index 6db060fed64..25554e9b4c0 100644 --- a/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm +++ b/code/game/objects/structures/crates_lockers/_closets_appearance_definitions.dm @@ -1441,6 +1441,10 @@ decals = null color = COLOR_OFF_WHITE +/decl/closet_appearance/wall_double/wooden + decals = null + color = WOOD_COLOR_RICH + /decl/closet_appearance/wall_double/medical decals = null color = COLOR_OFF_WHITE diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index e5a9c631a9f..43098084d72 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -1,164 +1,164 @@ -/obj/structure/closet/coffin - name = "coffin" - desc = "It's a burial receptacle for the dearly departed." - icon = 'icons/obj/closets/coffin.dmi' - - icon_state = "closed_unlocked" - seal_tool = /obj/item/weapon/tool/screwdriver - breakout_sound = 'sound/weapons/tablehit1.ogg' - closet_appearance = null // Special icon for us - -/* Graves */ -/obj/structure/closet/grave - name = "grave" - desc = "Dirt." - icon = 'icons/obj/closets/grave.dmi' - icon_state = "closed_unlocked" - seal_tool = null - breakout_sound = 'sound/weapons/thudswoosh.ogg' - anchored = TRUE - max_closets = 1 - opened = 1 - closet_appearance = null // Special icon for us - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - -/obj/structure/closet/grave/attack_hand(mob/user as mob) - if(opened) - visible_message("[user] starts to climb into \the [src.name].", \ - "You start to lower yourself into \the [src.name].") - if(do_after(user, 50)) - user.forceMove(src.loc) - visible_message("[user] climbs into \the [src.name].", \ - "You climb into \the [src.name].") - else - visible_message("[user] decides not to climb into \the [src.name].", \ - "You stop climbing into \the [src.name].") - return - -/obj/structure/closet/grave/CanPass(atom/movable/mover, turf/target) - if(opened && ismob(mover)) - var/mob/M = mover - add_fingerprint(M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.m_intent == "walk") - to_chat(H, "You stop at the edge of \the [src.name].") - return FALSE - else - to_chat(H, "You fall into \the [src.name]!") - fall_in(H) - return TRUE - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.a_intent == I_HELP) - to_chat(R, "You stop at the edge of \the [src.name].") - return FALSE - else - to_chat(R, "You enter \the [src.name].") - return TRUE - return TRUE //Everything else can move over the graves - -/obj/structure/closet/grave/proc/fall_in(mob/living/L) //Only called on humans for now, but still - L.Weaken(5) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - var/limb_damage = rand(5,25) - H.adjustBruteLoss(limb_damage) - -/obj/structure/closet/grave/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(src.opened) - if(istype(W, /obj/item/weapon/shovel)) - user.visible_message("[user] piles dirt into \the [src.name].", \ - "You start to pile dirt into \the [src.name].", \ - "You hear dirt being moved.") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message("[user] pats down the dirt on top of \the [src.name].", \ - "You finish filling in \the [src.name].") - close() - return - else - user.visible_message("[user] stops filling in \the [src.name].", \ - "You change your mind and stop filling in \the [src.name].") - return - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet - return 0 - if(istype(W,/obj/item/tk_grab)) - return 0 - if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len) - var/obj/item/weapon/storage/laundry_basket/LB = W - var/turf/T = get_turf(src) - for(var/obj/item/I in LB.contents) - LB.remove_from_storage(I, T) - user.visible_message("[user] empties \the [LB] into \the [src].", \ - "You empty \the [LB] into \the [src].", \ - "You hear rustling of clothes.") - return - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - usr.drop_item() - if(W) - W.forceMove(src.loc) - else - if(istype(W, /obj/item/weapon/shovel)) - if(user.a_intent == I_HURT) // Hurt intent means you're trying to kill someone, or just get rid of the grave - user.visible_message("[user] begins to smoothe out the dirt of \the [src.name].", \ - "You start to smoothe out the dirt of \the [src.name].", \ - "You hear dirt being moved.") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message("[user] finishes smoothing out \the [src.name].", \ - "You finish smoothing out \the [src.name].") - if(LAZYLEN(contents)) - alpha = 40 // If we've got stuff inside, like maybe a person, just make it hard to see us - else - qdel(src) // Else, go away - return - else - user.visible_message("[user] stops concealing \the [src.name].", \ - "You stop concealing \the [src.name].") - return - else - user.visible_message("[user] begins to unearth \the [src.name].", \ - "You start to unearth \the [src.name].", \ - "You hear dirt being moved.") - if(do_after(user, 40 * W.toolspeed)) - user.visible_message("[user] reaches the bottom of \the [src.name].", \ - "You finish digging out \the [src.name].") - break_open() - return - else - user.visible_message("[user] stops digging out \the [src.name].", \ - "You stop digging out \the [src.name].") - return - return - -/obj/structure/closet/grave/close() - ..() - if(!opened) - sealed = TRUE - -/obj/structure/closet/grave/open() - .=..() - alpha = 255 // Needed because of grave hiding - -/obj/structure/closet/grave/bullet_act(var/obj/item/projectile/P) - return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets - -/obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L) - var/gasid = "carbon_dioxide" - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.species && H.species.exhale_type) - gasid = H.species.exhale_type - var/datum/gas_mixture/grave_breath = new() - var/datum/gas_mixture/above_air = return_air() - grave_breath.adjust_gas(gasid, BREATH_MOLES) - grave_breath.temperature = (above_air.temperature) - 30 //Underground - return grave_breath - -/obj/structure/closet/grave/dirthole - name = "hole" +/obj/structure/closet/coffin + name = "coffin" + desc = "It's a burial receptacle for the dearly departed." + icon = 'icons/obj/closets/coffin.dmi' + + icon_state = "closed_unlocked" + seal_tool = /obj/item/weapon/tool/screwdriver + breakout_sound = 'sound/weapons/tablehit1.ogg' + closet_appearance = null // Special icon for us + +/* Graves */ +/obj/structure/closet/grave + name = "grave" + desc = "Dirt." + icon = 'icons/obj/closets/grave.dmi' + icon_state = "closed_unlocked" + seal_tool = null + breakout_sound = 'sound/weapons/thudswoosh.ogg' + anchored = TRUE + max_closets = 1 + opened = 1 + closet_appearance = null // Special icon for us + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + +/obj/structure/closet/grave/attack_hand(mob/user as mob) + if(opened) + visible_message("[user] starts to climb into \the [src.name].", \ + "You start to lower yourself into \the [src.name].") + if(do_after(user, 50)) + user.forceMove(src.loc) + visible_message("[user] climbs into \the [src.name].", \ + "You climb into \the [src.name].") + else + visible_message("[user] decides not to climb into \the [src.name].", \ + "You stop climbing into \the [src.name].") + return + +/obj/structure/closet/grave/CanPass(atom/movable/mover, turf/target) + if(opened && ismob(mover)) + var/mob/M = mover + add_fingerprint(M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.m_intent == "walk") + to_chat(H, "You stop at the edge of \the [src.name].") + return FALSE + else + to_chat(H, "You fall into \the [src.name]!") + fall_in(H) + return TRUE + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.a_intent == I_HELP) + to_chat(R, "You stop at the edge of \the [src.name].") + return FALSE + else + to_chat(R, "You enter \the [src.name].") + return TRUE + return TRUE //Everything else can move over the graves + +/obj/structure/closet/grave/proc/fall_in(mob/living/L) //Only called on humans for now, but still + L.Weaken(5) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + var/limb_damage = rand(5,25) + H.adjustBruteLoss(limb_damage) + +/obj/structure/closet/grave/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(src.opened) + if(istype(W, /obj/item/weapon/shovel)) + user.visible_message("[user] piles dirt into \the [src.name].", \ + "You start to pile dirt into \the [src.name].", \ + "You hear dirt being moved.") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message("[user] pats down the dirt on top of \the [src.name].", \ + "You finish filling in \the [src.name].") + close() + return + else + user.visible_message("[user] stops filling in \the [src.name].", \ + "You change your mind and stop filling in \the [src.name].") + return + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + src.MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet + return 0 + if(istype(W,/obj/item/tk_grab)) + return 0 + if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len) + var/obj/item/weapon/storage/laundry_basket/LB = W + var/turf/T = get_turf(src) + for(var/obj/item/I in LB.contents) + LB.remove_from_storage(I, T) + user.visible_message("[user] empties \the [LB] into \the [src].", \ + "You empty \the [LB] into \the [src].", \ + "You hear rustling of clothes.") + return + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + usr.drop_item() + if(W) + W.forceMove(src.loc) + else + if(istype(W, /obj/item/weapon/shovel)) + if(user.a_intent == I_HURT) // Hurt intent means you're trying to kill someone, or just get rid of the grave + user.visible_message("[user] begins to smoothe out the dirt of \the [src.name].", \ + "You start to smoothe out the dirt of \the [src.name].", \ + "You hear dirt being moved.") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message("[user] finishes smoothing out \the [src.name].", \ + "You finish smoothing out \the [src.name].") + if(LAZYLEN(contents)) + alpha = 40 // If we've got stuff inside, like maybe a person, just make it hard to see us + else + qdel(src) // Else, go away + return + else + user.visible_message("[user] stops concealing \the [src.name].", \ + "You stop concealing \the [src.name].") + return + else + user.visible_message("[user] begins to unearth \the [src.name].", \ + "You start to unearth \the [src.name].", \ + "You hear dirt being moved.") + if(do_after(user, 40 * W.toolspeed)) + user.visible_message("[user] reaches the bottom of \the [src.name].", \ + "You finish digging out \the [src.name].") + break_open() + return + else + user.visible_message("[user] stops digging out \the [src.name].", \ + "You stop digging out \the [src.name].") + return + return + +/obj/structure/closet/grave/close() + ..() + if(!opened) + sealed = TRUE + +/obj/structure/closet/grave/open() + .=..() + alpha = 255 // Needed because of grave hiding + +/obj/structure/closet/grave/bullet_act(var/obj/item/projectile/P) + return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets + +/obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L) + var/gasid = "carbon_dioxide" + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.species && H.species.exhale_type) + gasid = H.species.exhale_type + var/datum/gas_mixture/grave_breath = new() + var/datum/gas_mixture/above_air = return_air() + grave_breath.adjust_gas(gasid, BREATH_MOLES) + grave_breath.temperature = (above_air.temperature) - 30 //Underground + return grave_breath + +/obj/structure/closet/grave/dirthole + name = "hole" diff --git a/code/game/objects/structures/crates_lockers/closets/crittercrate.dm b/code/game/objects/structures/crates_lockers/closets/crittercrate.dm index 6b0196168b5..d649f865706 100644 --- a/code/game/objects/structures/crates_lockers/closets/crittercrate.dm +++ b/code/game/objects/structures/crates_lockers/closets/crittercrate.dm @@ -1,4 +1,4 @@ -/obj/structure/closet/crate/critter - name = "critter crate" - desc = "A crate which can sustain life for a while." - closet_appearance = /decl/closet_appearance/large_crate/critter +/obj/structure/closet/crate/critter + name = "critter crate" + desc = "A crate which can sustain life for a while." + closet_appearance = /decl/closet_appearance/large_crate/critter diff --git a/code/game/objects/structures/crates_lockers/closets/egg_vr.dm b/code/game/objects/structures/crates_lockers/closets/egg_vr.dm index 19d3f8b4527..eb69ff63b57 100644 --- a/code/game/objects/structures/crates_lockers/closets/egg_vr.dm +++ b/code/game/objects/structures/crates_lockers/closets/egg_vr.dm @@ -24,7 +24,7 @@ icon_state = icon_closed /obj/structure/closet/secure_closet/egg/attackby(obj/item/weapon/W, mob/user as mob) //This also prevents crew from welding the eggs and making them unable to be opened. - if(istype(W, /obj/item/weapon/weldingtool)) + if(W.has_tool_quality(TOOL_WELDER)) src.dump_contents() qdel(src) diff --git a/code/game/objects/structures/crates_lockers/closets/fitness.dm b/code/game/objects/structures/crates_lockers/closets/fitness.dm index 9d01a4efbe7..c2243e42d16 100644 --- a/code/game/objects/structures/crates_lockers/closets/fitness.dm +++ b/code/game/objects/structures/crates_lockers/closets/fitness.dm @@ -1,94 +1,120 @@ -/obj/structure/closet/athletic_mixed - name = "athletic wardrobe" - desc = "It's a storage unit for athletic wear." - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/under/shorts/grey, - /obj/item/clothing/under/shorts/black, - /obj/item/clothing/under/shorts/red, - /obj/item/clothing/under/shorts/blue, - /obj/item/clothing/under/shorts/green, - /obj/item/clothing/under/shorts/white, - /obj/item/clothing/suit/storage/toggle/track, - /obj/item/clothing/suit/storage/toggle/track/blue, - /obj/item/clothing/suit/storage/toggle/track/green, - /obj/item/clothing/suit/storage/toggle/track/red, - /obj/item/clothing/suit/storage/toggle/track/white, - /obj/item/clothing/under/pants/track, - /obj/item/clothing/under/pants/track/blue, - /obj/item/clothing/under/pants/track/green, - /obj/item/clothing/under/pants/track/white, - /obj/item/clothing/under/pants/track/red, - /obj/item/clothing/shoes/athletic = 2, - /obj/item/clothing/shoes/hitops, - /obj/item/clothing/shoes/hitops/red, - /obj/item/clothing/shoes/hitops/black, - /obj/item/clothing/shoes/hitops/blue - ) - -/obj/structure/closet/athletic_swimwear - name = "athletic wardrobe" - desc = "It's a storage unit for swimwear." - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/under/shorts/grey, - /obj/item/clothing/under/shorts/black, - /obj/item/clothing/under/shorts/red, - /obj/item/clothing/under/shorts/blue, - /obj/item/clothing/under/shorts/green, - /obj/item/clothing/under/swimsuit/red, - /obj/item/clothing/under/swimsuit/black, - /obj/item/clothing/under/swimsuit/blue, - /obj/item/clothing/under/swimsuit/green, - /obj/item/clothing/under/swimsuit/purple, - /obj/item/clothing/under/swimsuit/striped, - /obj/item/clothing/under/swimsuit/white, - /obj/item/clothing/under/swimsuit/earth, - /obj/item/clothing/under/wetsuit, - /obj/item/clothing/under/wetsuit_rec, - /obj/item/clothing/under/wetsuit_skimpy, - /obj/item/clothing/mask/snorkel = 2, - /obj/item/clothing/shoes/swimmingfins = 2) - -/obj/structure/closet/boxinggloves - name = "boxing gloves" - desc = "It's a storage unit for gloves for use in the boxing ring." - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/gloves/boxing/blue, - /obj/item/clothing/gloves/boxing/green, - /obj/item/clothing/gloves/boxing/yellow, - /obj/item/clothing/gloves/boxing) - -/obj/structure/closet/masks - name = "mask closet" - desc = "IT'S A STORAGE UNIT FOR FIGHTER MASKS OLE!" - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/mask/luchador, - /obj/item/clothing/mask/luchador/rudos, - /obj/item/clothing/mask/luchador/tecnicos) - - -/obj/structure/closet/lasertag/red - name = "red laser tag equipment" - desc = "It's a storage unit for laser tag equipment." - closet_appearance = /decl/closet_appearance/wardrobe/red - - starts_with = list( - /obj/item/weapon/gun/energy/lasertag/red = 5, - /obj/item/clothing/suit/redtag = 5) - - -/obj/structure/closet/lasertag/blue - name = "blue laser tag equipment" - desc = "It's a storage unit for laser tag equipment." - closet_appearance = /decl/closet_appearance/wardrobe/blue - - starts_with = list( - /obj/item/weapon/gun/energy/lasertag/blue = 5, - /obj/item/clothing/suit/bluetag = 5) +/obj/structure/closet/athletic_mixed + name = "athletic wardrobe" + desc = "It's a storage unit for athletic wear." + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/under/shorts/grey, + /obj/item/clothing/under/shorts/black, + /obj/item/clothing/under/shorts/red, + /obj/item/clothing/under/shorts/blue, + /obj/item/clothing/under/shorts/green, + /obj/item/clothing/under/shorts/white, + /obj/item/clothing/suit/storage/toggle/track, + /obj/item/clothing/suit/storage/toggle/track/blue, + /obj/item/clothing/suit/storage/toggle/track/green, + /obj/item/clothing/suit/storage/toggle/track/red, + /obj/item/clothing/suit/storage/toggle/track/white, + /obj/item/clothing/under/pants/track, + /obj/item/clothing/under/pants/track/blue, + /obj/item/clothing/under/pants/track/green, + /obj/item/clothing/under/pants/track/white, + /obj/item/clothing/under/pants/track/red, + /obj/item/clothing/shoes/athletic = 2, + /obj/item/clothing/shoes/hitops, + /obj/item/clothing/shoes/hitops/red, + /obj/item/clothing/shoes/hitops/black, + /obj/item/clothing/shoes/hitops/blue + ) + +/obj/structure/closet/athletic_swimwear + name = "athletic wardrobe" + desc = "It's a storage unit for swimwear." + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/under/shorts/grey, + /obj/item/clothing/under/shorts/black, + /obj/item/clothing/under/shorts/red, + /obj/item/clothing/under/shorts/blue, + /obj/item/clothing/under/shorts/green, + /obj/item/clothing/under/swimsuit/red, + /obj/item/clothing/under/swimsuit/black, + /obj/item/clothing/under/swimsuit/blue, + /obj/item/clothing/under/swimsuit/green, + /obj/item/clothing/under/swimsuit/purple, + /obj/item/clothing/under/swimsuit/striped, + /obj/item/clothing/under/swimsuit/white, + /obj/item/clothing/under/swimsuit/earth, + /obj/item/clothing/under/wetsuit, + /obj/item/clothing/under/wetsuit_rec, + /obj/item/clothing/under/wetsuit_skimpy, + /obj/item/clothing/mask/snorkel = 2, + /obj/item/clothing/shoes/swimmingfins = 2) + +/obj/structure/closet/boxinggloves + name = "boxing gloves" + desc = "It's a storage unit for gloves for use in the boxing ring." + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/gloves/boxing/blue, + /obj/item/clothing/gloves/boxing/green, + /obj/item/clothing/gloves/boxing/yellow, + /obj/item/clothing/gloves/boxing) + +/obj/structure/closet/masks + name = "mask closet" + desc = "IT'S A STORAGE UNIT FOR FIGHTER MASKS OLE!" + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/mask/luchador, + /obj/item/clothing/mask/luchador/rudos, + /obj/item/clothing/mask/luchador/tecnicos) + + +/obj/structure/closet/lasertag/red + name = "red laser tag equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/red + + starts_with = list( + /obj/item/weapon/gun/energy/lasertag/red = 5, + /obj/item/clothing/suit/redtag = 5) + + +/obj/structure/closet/lasertag/blue + name = "blue laser tag equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/blue + + starts_with = list( + /obj/item/weapon/gun/energy/lasertag/blue = 5, + /obj/item/clothing/suit/bluetag = 5) + +/obj/structure/closet/lasertag/red/laserdome + name = "red team laserdome equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/red + + starts_with = list( + /obj/item/device/encryptionkey/ent = 3, + /obj/item/clothing/gloves/bluespace = 3, + /obj/item/clothing/under/color/red = 3, + /obj/item/weapon/gun/energy/lasertag/red = 3, + /obj/item/clothing/head/redtag = 3, + /obj/item/clothing/suit/redtag = 3) + +/obj/structure/closet/lasertag/blue/laserdome + name = "blue team laserdome equipment" + desc = "It's a storage unit for laser tag equipment." + closet_appearance = /decl/closet_appearance/wardrobe/blue + + starts_with = list( + /obj/item/device/encryptionkey/ent = 3, + /obj/item/clothing/gloves/bluespace = 3, + /obj/item/clothing/under/color/blue = 3, + /obj/item/weapon/gun/energy/lasertag/blue = 3, + /obj/item/clothing/head/bluetag = 3, + /obj/item/clothing/suit/bluetag = 3) diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm index 0e27bc97881..f79d770f742 100644 --- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm +++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm @@ -1,84 +1,84 @@ -/obj/structure/closet/cabinet - name = "cabinet" - desc = "Old will forever be in fashion." - icon = 'icons/obj/closets/bases/cabinet.dmi' - closet_appearance = /decl/closet_appearance/cabinet - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - -/obj/structure/closet/acloset - name = "strange closet" - desc = "It looks alien!" - closet_appearance = /decl/closet_appearance/alien - - open_sound = 'sound/machines/click.ogg' - close_sound = 'sound/machines/click.ogg' - -/obj/structure/closet/gimmick - name = "administrative supply closet" - desc = "It's a storage unit for things that have no right being here." - closet_appearance = /decl/closet_appearance/tactical - anchored = FALSE - -/obj/structure/closet/gimmick/russian - name = "russian surplus closet" - desc = "It's a storage unit for Russian standard-issue surplus." - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/clothing/head/ushanka = 5, - /obj/item/clothing/under/soviet = 5) - - -/obj/structure/closet/gimmick/tacticool - name = "tacticool gear closet" - desc = "It's a storage unit for Tacticool gear." - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/clothing/glasses/eyepatch, - /obj/item/clothing/glasses/sunglasses, - /obj/item/clothing/gloves/swat = 2, - /obj/item/clothing/head/helmet/swat = 2, - /obj/item/clothing/mask/gas = 2, - /obj/item/clothing/shoes/boots/swat = 2, - /obj/item/clothing/suit/armor/swat = 2, - /obj/item/clothing/under/syndicate/tacticool = 2) - - -/obj/structure/closet/thunderdome - name = "\improper Thunderdome closet" - desc = "Everything you need!" - closet_appearance = /decl/closet_appearance/thunderdomered - anchored = TRUE - -/obj/structure/closet/thunderdome/tdred - name = "red-team Thunderdome closet" - - starts_with = list( - /obj/item/clothing/suit/armor/tdome/red = 3, - /obj/item/weapon/melee/energy/sword = 3, - /obj/item/weapon/gun/energy/laser = 3, - /obj/item/weapon/melee/baton = 3, - /obj/item/weapon/storage/box/flashbangs = 3, - /obj/item/clothing/head/helmet/thunderdome = 3) - -/obj/structure/closet/thunderdome/tdgreen - name = "green-team Thunderdome closet" - closet_appearance = /decl/closet_appearance/thunderdomegreen - - starts_with = list( - /obj/item/clothing/suit/armor/tdome/green = 3, - /obj/item/weapon/melee/energy/sword = 3, - /obj/item/weapon/gun/energy/laser = 3, - /obj/item/weapon/melee/baton = 3, - /obj/item/weapon/storage/box/flashbangs = 3, - /obj/item/clothing/head/helmet/thunderdome = 3) - -/obj/structure/closet/alien - name = "alien container" - desc = "Contains secrets of the universe." - icon = 'icons/obj/closets/abductor.dmi' - anchored = TRUE - closet_appearance = null // special icons +/obj/structure/closet/cabinet + name = "cabinet" + desc = "Old will forever be in fashion." + icon = 'icons/obj/closets/bases/cabinet.dmi' + closet_appearance = /decl/closet_appearance/cabinet + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + +/obj/structure/closet/acloset + name = "strange closet" + desc = "It looks alien!" + closet_appearance = /decl/closet_appearance/alien + + open_sound = 'sound/machines/click.ogg' + close_sound = 'sound/machines/click.ogg' + +/obj/structure/closet/gimmick + name = "administrative supply closet" + desc = "It's a storage unit for things that have no right being here." + closet_appearance = /decl/closet_appearance/tactical + anchored = FALSE + +/obj/structure/closet/gimmick/russian + name = "russian surplus closet" + desc = "It's a storage unit for Russian standard-issue surplus." + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/clothing/head/ushanka = 5, + /obj/item/clothing/under/soviet = 5) + + +/obj/structure/closet/gimmick/tacticool + name = "tacticool gear closet" + desc = "It's a storage unit for Tacticool gear." + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/clothing/glasses/eyepatch, + /obj/item/clothing/glasses/sunglasses, + /obj/item/clothing/gloves/swat = 2, + /obj/item/clothing/head/helmet/swat = 2, + /obj/item/clothing/mask/gas = 2, + /obj/item/clothing/shoes/boots/swat = 2, + /obj/item/clothing/suit/armor/swat = 2, + /obj/item/clothing/under/syndicate/tacticool = 2) + + +/obj/structure/closet/thunderdome + name = "\improper Thunderdome closet" + desc = "Everything you need!" + closet_appearance = /decl/closet_appearance/thunderdomered + anchored = TRUE + +/obj/structure/closet/thunderdome/tdred + name = "red-team Thunderdome closet" + + starts_with = list( + /obj/item/clothing/suit/armor/tdome/red = 3, + /obj/item/weapon/melee/energy/sword = 3, + /obj/item/weapon/gun/energy/laser = 3, + /obj/item/weapon/melee/baton = 3, + /obj/item/weapon/storage/box/flashbangs = 3, + /obj/item/clothing/head/helmet/thunderdome = 3) + +/obj/structure/closet/thunderdome/tdgreen + name = "green-team Thunderdome closet" + closet_appearance = /decl/closet_appearance/thunderdomegreen + + starts_with = list( + /obj/item/clothing/suit/armor/tdome/green = 3, + /obj/item/weapon/melee/energy/sword = 3, + /obj/item/weapon/gun/energy/laser = 3, + /obj/item/weapon/melee/baton = 3, + /obj/item/weapon/storage/box/flashbangs = 3, + /obj/item/clothing/head/helmet/thunderdome = 3) + +/obj/structure/closet/alien + name = "alien container" + desc = "Contains secrets of the universe." + icon = 'icons/obj/closets/abductor.dmi' + anchored = TRUE + closet_appearance = null // special icons diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index 86cb04e20dd..a33e39a2192 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -1,132 +1,132 @@ -/* - * Closets for specific jobs - * Contains: - * Bartender - * Janitor - * Lawyer - * Janitorial Equipment - * - * - * These have been removed from the map for the most part, however - * do not remove these in case people want to make maps or POIs - * with these closets. - * - */ - -/* - * Bartender - */ -/obj/structure/closet/gmcloset - name = "formal closet" - desc = "It's a storage unit for formal clothing." - closet_appearance = /decl/closet_appearance/wardrobe/suit - - starts_with = list( - /obj/item/clothing/head/that = 2, - /obj/item/device/radio/headset/headset_service = 2, - /obj/item/clothing/head/pin/flower, - /obj/item/clothing/head/pin/flower/pink, - /obj/item/clothing/head/pin/flower/yellow, - /obj/item/clothing/head/pin/flower/blue, - /obj/item/clothing/head/pin/pink, - /obj/item/clothing/head/pin/magnetic, - /obj/item/clothing/under/sl_suit = 2, - /obj/item/clothing/under/rank/bartender = 2, - /obj/item/clothing/under/rank/bartender/skirt, - /obj/item/clothing/suit/storage/hooded/wintercoat/bar, - /obj/item/clothing/under/dress/dress_saloon, - /obj/item/clothing/accessory/wcoat = 2, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/shoes/laceup - ) - -/* - * Chef - */ -/obj/structure/closet/chefcloset - name = "chef's closet" - desc = "It's a storage unit for foodservice garments." - closet_appearance = /decl/closet_appearance/wardrobe/white - - starts_with = list( - /obj/item/clothing/under/sundress, - /obj/item/clothing/under/waiter = 2, - /obj/item/device/radio/headset/headset_service = 2, - /obj/item/weapon/storage/box/mousetraps = 2, - /obj/item/clothing/under/rank/chef, - /obj/item/clothing/head/chefhat, - /obj/item/weapon/storage/bag/food = 2 - ) - -/* - * Janitor - */ -/obj/structure/closet/jcloset - name = "custodial closet" - desc = "It's a storage unit for janitorial clothes and gear." - closet_appearance = /decl/closet_appearance/wardrobe/janitor - - starts_with = list( - /obj/item/clothing/under/rank/janitor, - /obj/item/clothing/under/dress/maid/janitor, - /obj/item/device/radio/headset/headset_service, - /obj/item/weapon/cartridge/janitor, - /obj/item/clothing/suit/storage/hooded/wintercoat/janitor, - /obj/item/clothing/gloves/black, - /obj/item/clothing/head/soft/purple, - /obj/item/clothing/head/beret/purple, - /obj/item/device/flashlight, - /obj/item/clothing/suit/caution = 4, - /obj/item/device/lightreplacer, - /obj/item/weapon/storage/bag/trash, - /obj/item/weapon/storage/belt/janitor, - /obj/item/clothing/shoes/galoshes - ) - -/* - * Lawyer - */ -/obj/structure/closet/lawcloset - name = "legal closet" - desc = "It's a storage unit for courtroom apparel and items." - closet_appearance = /decl/closet_appearance/wardrobe/suit - - starts_with = list( - /obj/item/clothing/under/lawyer/female = 2, - /obj/item/clothing/under/lawyer/black = 2, - /obj/item/clothing/under/lawyer/black/skirt = 2, - /obj/item/clothing/under/lawyer/red = 2, - /obj/item/clothing/under/lawyer/red/skirt = 2, - /obj/item/clothing/suit/storage/toggle/internalaffairs = 2, - /obj/item/clothing/under/lawyer/bluesuit = 2, - /obj/item/clothing/under/lawyer/bluesuit/skirt = 2, - /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket = 2, - /obj/item/clothing/under/lawyer/purpsuit = 2, - /obj/item/clothing/under/lawyer/purpsuit/skirt = 2, - /obj/item/clothing/suit/storage/toggle/lawyer/purpjacket = 2, - /obj/item/clothing/shoes/brown = 2, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/shoes/laceup = 2, - /obj/item/clothing/glasses/sunglasses/big = 2, - /obj/item/clothing/under/lawyer/blue = 2, - /obj/item/clothing/under/lawyer/blue/skirt = 2, - /obj/item/device/tape/random = 2 - ) - -/* - * Janitorial Equipment - */ -/obj/structure/closet/jequipcloset - name = "custodial equipment closet" - desc = "It's a storage unit for janitorial clothes and gear." - closet_appearance = /decl/closet_appearance/wardrobe/janitor - - starts_with = list( - /obj/item/device/flashlight = 5, - /obj/item/clothing/suit/caution = 12, - /obj/item/device/lightreplacer = 3, - /obj/item/weapon/storage/bag/trash = 3, - /obj/item/weapon/storage/box/lights/mixed = 3, - /obj/item/weapon/storage/box/mousetraps = 1, - /obj/item/weapon/grenade/chem_grenade/cleaner = 4 - ) +/* + * Closets for specific jobs + * Contains: + * Bartender + * Janitor + * Lawyer + * Janitorial Equipment + * + * + * These have been removed from the map for the most part, however + * do not remove these in case people want to make maps or POIs + * with these closets. + * + */ + +/* + * Bartender + */ +/obj/structure/closet/gmcloset + name = "formal closet" + desc = "It's a storage unit for formal clothing." + closet_appearance = /decl/closet_appearance/wardrobe/suit + + starts_with = list( + /obj/item/clothing/head/that = 2, + /obj/item/device/radio/headset/headset_service = 2, + /obj/item/clothing/head/pin/flower, + /obj/item/clothing/head/pin/flower/pink, + /obj/item/clothing/head/pin/flower/yellow, + /obj/item/clothing/head/pin/flower/blue, + /obj/item/clothing/head/pin/pink, + /obj/item/clothing/head/pin/magnetic, + /obj/item/clothing/under/sl_suit = 2, + /obj/item/clothing/under/rank/bartender = 2, + /obj/item/clothing/under/rank/bartender/skirt, + /obj/item/clothing/suit/storage/hooded/wintercoat/bar, + /obj/item/clothing/under/dress/dress_saloon, + /obj/item/clothing/accessory/wcoat = 2, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/shoes/laceup + ) + +/* + * Chef + */ +/obj/structure/closet/chefcloset + name = "chef's closet" + desc = "It's a storage unit for foodservice garments." + closet_appearance = /decl/closet_appearance/wardrobe/white + + starts_with = list( + /obj/item/clothing/under/sundress, + /obj/item/clothing/under/waiter = 2, + /obj/item/device/radio/headset/headset_service = 2, + /obj/item/weapon/storage/box/mousetraps = 2, + /obj/item/clothing/under/rank/chef, + /obj/item/clothing/head/chefhat, + /obj/item/weapon/storage/bag/food = 2 + ) + +/* + * Janitor + */ +/obj/structure/closet/jcloset + name = "custodial closet" + desc = "It's a storage unit for janitorial clothes and gear." + closet_appearance = /decl/closet_appearance/wardrobe/janitor + + starts_with = list( + /obj/item/clothing/under/rank/janitor, + /obj/item/clothing/under/dress/maid/janitor, + /obj/item/device/radio/headset/headset_service, + /obj/item/weapon/cartridge/janitor, + /obj/item/clothing/suit/storage/hooded/wintercoat/janitor, + /obj/item/clothing/gloves/black, + /obj/item/clothing/head/soft/purple, + /obj/item/clothing/head/beret/purple, + /obj/item/device/flashlight, + /obj/item/clothing/suit/caution = 4, + /obj/item/device/lightreplacer, + /obj/item/weapon/storage/bag/trash, + /obj/item/weapon/storage/belt/janitor, + /obj/item/clothing/shoes/galoshes + ) + +/* + * Lawyer + */ +/obj/structure/closet/lawcloset + name = "legal closet" + desc = "It's a storage unit for courtroom apparel and items." + closet_appearance = /decl/closet_appearance/wardrobe/suit + + starts_with = list( + /obj/item/clothing/under/lawyer/female = 2, + /obj/item/clothing/under/lawyer/black = 2, + /obj/item/clothing/under/lawyer/black/skirt = 2, + /obj/item/clothing/under/lawyer/red = 2, + /obj/item/clothing/under/lawyer/red/skirt = 2, + /obj/item/clothing/suit/storage/toggle/internalaffairs = 2, + /obj/item/clothing/under/lawyer/bluesuit = 2, + /obj/item/clothing/under/lawyer/bluesuit/skirt = 2, + /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket = 2, + /obj/item/clothing/under/lawyer/purpsuit = 2, + /obj/item/clothing/under/lawyer/purpsuit/skirt = 2, + /obj/item/clothing/suit/storage/toggle/lawyer/purpjacket = 2, + /obj/item/clothing/shoes/brown = 2, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/shoes/laceup = 2, + /obj/item/clothing/glasses/sunglasses/big = 2, + /obj/item/clothing/under/lawyer/blue = 2, + /obj/item/clothing/under/lawyer/blue/skirt = 2, + /obj/item/device/tape/random = 2 + ) + +/* + * Janitorial Equipment + */ +/obj/structure/closet/jequipcloset + name = "custodial equipment closet" + desc = "It's a storage unit for janitorial clothes and gear." + closet_appearance = /decl/closet_appearance/wardrobe/janitor + + starts_with = list( + /obj/item/device/flashlight = 5, + /obj/item/clothing/suit/caution = 12, + /obj/item/device/lightreplacer = 3, + /obj/item/weapon/storage/bag/trash = 3, + /obj/item/weapon/storage/box/lights/mixed = 3, + /obj/item/weapon/storage/box/mousetraps = 1, + /obj/item/weapon/grenade/chem_grenade/cleaner = 4 + ) diff --git a/code/game/objects/structures/crates_lockers/closets/l3closet.dm b/code/game/objects/structures/crates_lockers/closets/l3closet.dm index c592c3e69f2..2966d4b8e4f 100644 --- a/code/game/objects/structures/crates_lockers/closets/l3closet.dm +++ b/code/game/objects/structures/crates_lockers/closets/l3closet.dm @@ -1,62 +1,63 @@ -/obj/structure/closet/l3closet - name = "level-3 biohazard suit closet" - desc = "It's a storage unit for level-3 biohazard gear." - closet_appearance = /decl/closet_appearance/bio - -/obj/structure/closet/l3closet/general - closet_appearance = /decl/closet_appearance/bio - - starts_with = list( - /obj/item/clothing/suit/bio_suit/general, - /obj/item/clothing/head/bio_hood/general) - - -/obj/structure/closet/l3closet/virology - closet_appearance = /decl/closet_appearance/bio/virology - - starts_with = list( - /obj/item/clothing/suit/bio_suit/virology, - /obj/item/clothing/head/bio_hood/virology, - /obj/item/clothing/mask/gas, - /obj/item/weapon/tank/oxygen) - - -/obj/structure/closet/l3closet/security - closet_appearance = /decl/closet_appearance/bio/security - - starts_with = list( - /obj/item/clothing/suit/bio_suit/security, - /obj/item/clothing/head/bio_hood/security) - ///obj/item/weapon/gun/energy/taser/xeno/sec) //VOREStation Removal - -/obj/structure/closet/l3closet/janitor - closet_appearance = /decl/closet_appearance/bio/janitor - - starts_with = list( - /obj/item/clothing/suit/bio_suit/janitor = 2, - /obj/item/clothing/head/bio_hood/janitor = 2, - /obj/item/clothing/mask/gas = 2, - /obj/item/weapon/tank/emergency/oxygen/engi = 2) - - -/obj/structure/closet/l3closet/scientist - closet_appearance = /decl/closet_appearance/bio/science - - starts_with = list( - /obj/item/clothing/suit/bio_suit/scientist, - /obj/item/clothing/head/bio_hood/scientist) - -/obj/structure/closet/l3closet/scientist/double - starts_with = list( - /obj/item/clothing/suit/bio_suit/scientist = 2, - /obj/item/clothing/head/bio_hood/scientist = 2, - /obj/item/weapon/storage/bag/xeno = 2) // VOREEdit, adding the xenobag to xenobio. - - -/obj/structure/closet/l3closet/medical - closet_appearance = /decl/closet_appearance/bio/medical - - starts_with = list( - /obj/item/clothing/suit/bio_suit/general = 3, - /obj/item/clothing/head/bio_hood/general = 3, - /obj/item/clothing/mask/gas = 3) +/obj/structure/closet/l3closet + name = "level-3 biohazard suit closet" + desc = "It's a storage unit for level-3 biohazard gear." + closet_appearance = /decl/closet_appearance/bio + +/obj/structure/closet/l3closet/general + closet_appearance = /decl/closet_appearance/bio + + starts_with = list( + /obj/item/clothing/suit/bio_suit/general, + /obj/item/clothing/head/bio_hood/general) + + +/obj/structure/closet/l3closet/virology + closet_appearance = /decl/closet_appearance/bio/virology + + starts_with = list( + /obj/item/clothing/suit/bio_suit/virology, + /obj/item/clothing/head/bio_hood/virology, + /obj/item/clothing/mask/gas, + /obj/item/weapon/tank/oxygen) + + +/obj/structure/closet/l3closet/security + closet_appearance = /decl/closet_appearance/bio/security + + starts_with = list( + /obj/item/clothing/suit/bio_suit/security, + /obj/item/clothing/head/bio_hood/security) + ///obj/item/weapon/gun/energy/taser/xeno/sec) //VOREStation Removal + +/obj/structure/closet/l3closet/janitor + closet_appearance = /decl/closet_appearance/bio/janitor + + starts_with = list( + /obj/item/clothing/suit/bio_suit/janitor = 2, + /obj/item/clothing/head/bio_hood/janitor = 2, + /obj/item/clothing/mask/gas = 2, + /obj/item/weapon/tank/emergency/oxygen/engi = 2) + + +/obj/structure/closet/l3closet/scientist + closet_appearance = /decl/closet_appearance/bio/science + + starts_with = list( + /obj/item/clothing/suit/bio_suit/scientist, + /obj/item/clothing/head/bio_hood/scientist, + /obj/item/weapon/storage/bag/xeno = 1) + +/obj/structure/closet/l3closet/scientist/double + starts_with = list( + /obj/item/clothing/suit/bio_suit/scientist = 2, + /obj/item/clothing/head/bio_hood/scientist = 2, + /obj/item/weapon/storage/bag/xeno = 2) // VOREEdit, adding the xenobag to xenobio. + + +/obj/structure/closet/l3closet/medical + closet_appearance = /decl/closet_appearance/bio/medical + + starts_with = list( + /obj/item/clothing/suit/bio_suit/general = 3, + /obj/item/clothing/head/bio_hood/general = 3, + /obj/item/clothing/mask/gas = 3) diff --git a/code/game/objects/structures/crates_lockers/closets/malfunction.dm b/code/game/objects/structures/crates_lockers/closets/malfunction.dm index feaa63e39f2..cd23e478a97 100644 --- a/code/game/objects/structures/crates_lockers/closets/malfunction.dm +++ b/code/game/objects/structures/crates_lockers/closets/malfunction.dm @@ -1,12 +1,12 @@ -/obj/structure/closet/malf/suits - desc = "It's a storage unit for operational gear." - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/weapon/tank/jetpack/void, - /obj/item/clothing/mask/breath, - /obj/item/clothing/head/helmet/space/void, - /obj/item/clothing/suit/space/void, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/cell, - /obj/item/device/multitool) +/obj/structure/closet/malf/suits + desc = "It's a storage unit for operational gear." + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/weapon/tank/jetpack/void, + /obj/item/clothing/mask/breath, + /obj/item/clothing/head/helmet/space/void, + /obj/item/clothing/suit/space/void, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/cell, + /obj/item/device/multitool) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 36b00526a85..9e6f14fd0b6 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -1,149 +1,149 @@ -/obj/structure/closet/secure_closet/engineering_chief - name = "chief engineer's locker" - req_access = list(access_ce) - closet_appearance = /decl/closet_appearance/secure_closet/engineering/ce - - starts_with = list( - /obj/item/clothing/accessory/storage/brown_vest, - /obj/item/areaeditor/blueprints, - /obj/item/wire_reader, - ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, - ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, - /obj/item/clothing/under/rank/chief_engineer, - /obj/item/clothing/under/rank/chief_engineer/skirt, - /obj/item/clothing/under/rank/neo_chiefengi, - /obj/item/clothing/under/rank/neo_chiefengi_skirt, - /obj/item/clothing/under/rank/neo_ce_gorka, - /obj/item/clothing/head/hardhat/white, - ///obj/item/clothing/head/welding, //VOREStation Removal: Locker bloat, grr. They get fancy goggles or can raid the welding supplies locker for one of these. - /obj/item/clothing/gloves/heavy_engineer, //VOREStation Edit: chief gets the good shit - /obj/item/clothing/shoes/brown, - /obj/item/weapon/cartridge/ce, - /obj/item/device/radio/headset/heads/ce, - /obj/item/device/radio/headset/heads/ce/alt, - /obj/item/weapon/storage/toolbox/mechanical, - /obj/item/clothing/suit/storage/hazardvest, - ///obj/item/clothing/mask/gas, //VOREStation Removal: Locker bloat, grr. The fancy one below functions as a mask & helmet combined. - /obj/item/clothing/head/hardhat/firefighter/chief, //VOREStation Add: replaces the bog-standard gas mask - ///obj/item/device/multitool, //VOREStation Removal: The belt they get, both standard and the fancy one, both come with one already, why stick another in here too? - /obj/item/weapon/storage/belt/utility/chief/full, - /obj/item/device/flash, - /obj/item/device/t_scanner/upgraded, - /obj/item/taperoll/engineering, - ///obj/item/clothing/suit/storage/hooded/wintercoat/engineering, //VOREStation Removal: Locker bloat, grr. They can grab from the engi-clothes vendor if they want the standard one. - /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/ce, - ///obj/item/clothing/shoes/boots/winter/engineering, //VOREStation Removal: Locker bloat, grr. As above. - /obj/item/clothing/head/beret/engineering/ce, - /obj/item/clothing/head/beret/engineering/ce/white, - /obj/item/weapon/tank/emergency/oxygen/double, //VOREStation Edit: chief gets the good shit - /obj/item/weapon/reagent_containers/spray/windowsealant) //VOREStation Add - -/obj/structure/closet/secure_closet/engineering_chief/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/industrial - else - starts_with += /obj/item/weapon/storage/backpack/satchel/eng - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/eng - return ..() - -/obj/structure/closet/secure_closet/engineering_electrical - name = "electrical supplies" - req_access = list(access_engine_equip) - closet_appearance = /decl/closet_appearance/secure_closet/engineering/electrical - - starts_with = list( - /obj/item/clothing/gloves/yellow = 2, - /obj/item/weapon/storage/toolbox/electrical = 3, - /obj/item/weapon/module/power_control = 3, - /obj/item/device/multitool = 3) - -/obj/structure/closet/secure_closet/engineering_electrical/double - starts_with = list( - /obj/item/clothing/gloves/yellow = 4, - /obj/item/weapon/storage/toolbox/electrical = 6, - /obj/item/weapon/module/power_control = 6, - /obj/item/device/multitool = 6) - -/obj/structure/closet/secure_closet/engineering_welding - name = "welding supplies" - req_access = list(access_construction) - closet_appearance = /decl/closet_appearance/secure_closet/engineering/welding - - starts_with = list( - /obj/item/clothing/head/welding = 3, - /obj/item/weapon/weldingtool/largetank = 3, - /obj/item/weapon/weldpack = 3, - /obj/item/clothing/glasses/welding = 3) - -/obj/structure/closet/secure_closet/engineering_welding/double - starts_with = list( - /obj/item/clothing/head/welding = 6, - /obj/item/weapon/weldingtool/largetank = 6, - /obj/item/weapon/weldpack = 6, - /obj/item/clothing/glasses/welding = 6) - -/obj/structure/closet/secure_closet/engineering_personal - name = "engineer's locker" - req_access = list(access_engine_equip) - closet_appearance = /decl/closet_appearance/secure_closet/engineering - - starts_with = list( - /obj/item/clothing/accessory/storage/brown_vest, - /obj/item/weapon/storage/toolbox/mechanical, - /obj/item/device/radio/headset/headset_eng, - /obj/item/device/radio/headset/headset_eng/alt, - /obj/item/clothing/suit/storage/hazardvest, - /obj/item/clothing/mask/gas, - /obj/item/clothing/glasses/meson, - /obj/item/weapon/cartridge/engineering, - /obj/item/taperoll/engineering, - /obj/item/clothing/head/hardhat, - /obj/item/clothing/suit/storage/hooded/wintercoat/engineering, - /obj/item/clothing/shoes/boots/winter/engineering, - /obj/item/weapon/tank/emergency/oxygen/engi, - /obj/item/weapon/storage/belt/utility, //VOREStation Add - /obj/item/weapon/reagent_containers/spray/windowsealant, //VOREStation Add - /obj/item/areaeditor/blueprints/engineers) //VOREStation Add - -/obj/structure/closet/secure_closet/engineering_personal/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/industrial - else - starts_with += /obj/item/weapon/storage/backpack/satchel/eng - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/eng - return ..() - - -/obj/structure/closet/secure_closet/atmos_personal - name = "technician's locker" - req_access = list(access_atmospherics) - closet_appearance = /decl/closet_appearance/secure_closet/engineering/atmos - - starts_with = list( - /obj/item/clothing/accessory/storage/brown_vest, - /obj/item/clothing/suit/fire/heavy, - /obj/item/clothing/head/hardhat/firefighter/atmos, - /obj/item/device/flashlight, - /obj/item/weapon/extinguisher/atmo, - ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, - /obj/item/device/radio/headset/headset_eng, - /obj/item/device/radio/headset/headset_eng/alt, - /obj/item/clothing/suit/storage/hazardvest/atmos, //VOREStation edit. Eng locker gets regular haz-vest, atmos gets the themed one of their own - /obj/item/clothing/mask/gas, - /obj/item/weapon/cartridge/atmos, - /obj/item/taperoll/atmos, - /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos, - /obj/item/clothing/shoes/boots/winter/atmos, - /obj/item/weapon/tank/emergency/oxygen/engi, - /obj/item/weapon/storage/belt/utility/atmostech) //VOREStation edit. They don't get a toolbox to fill it from, so why not give a spare one that's full already? - -/obj/structure/closet/secure_closet/atmos_personal/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/industrial - else - starts_with += /obj/item/weapon/storage/backpack/satchel/eng - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/eng - return ..() +/obj/structure/closet/secure_closet/engineering_chief + name = "chief engineer's locker" + req_access = list(access_ce) + closet_appearance = /decl/closet_appearance/secure_closet/engineering/ce + + starts_with = list( + /obj/item/clothing/accessory/storage/brown_vest, + /obj/item/areaeditor/blueprints, + /obj/item/wire_reader, + ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, + ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, + /obj/item/clothing/under/rank/chief_engineer, + /obj/item/clothing/under/rank/chief_engineer/skirt, + /obj/item/clothing/under/rank/neo_chiefengi, + /obj/item/clothing/under/rank/neo_chiefengi_skirt, + /obj/item/clothing/under/rank/neo_ce_gorka, + /obj/item/clothing/head/hardhat/white, + ///obj/item/clothing/head/welding, //VOREStation Removal: Locker bloat, grr. They get fancy goggles or can raid the welding supplies locker for one of these. + /obj/item/clothing/gloves/heavy_engineer, //VOREStation Edit: chief gets the good shit + /obj/item/clothing/shoes/brown, + /obj/item/weapon/cartridge/ce, + /obj/item/device/radio/headset/heads/ce, + /obj/item/device/radio/headset/heads/ce/alt, + /obj/item/weapon/storage/toolbox/mechanical, + /obj/item/clothing/suit/storage/hazardvest, + ///obj/item/clothing/mask/gas, //VOREStation Removal: Locker bloat, grr. The fancy one below functions as a mask & helmet combined. + /obj/item/clothing/head/hardhat/firefighter/chief, //VOREStation Add: replaces the bog-standard gas mask + ///obj/item/device/multitool, //VOREStation Removal: The belt they get, both standard and the fancy one, both come with one already, why stick another in here too? + /obj/item/weapon/storage/belt/utility/chief/full, + /obj/item/device/flash, + /obj/item/device/t_scanner/upgraded, + /obj/item/taperoll/engineering, + ///obj/item/clothing/suit/storage/hooded/wintercoat/engineering, //VOREStation Removal: Locker bloat, grr. They can grab from the engi-clothes vendor if they want the standard one. + /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/ce, + ///obj/item/clothing/shoes/boots/winter/engineering, //VOREStation Removal: Locker bloat, grr. As above. + /obj/item/clothing/head/beret/engineering/ce, + /obj/item/clothing/head/beret/engineering/ce/white, + /obj/item/weapon/tank/emergency/oxygen/double, //VOREStation Edit: chief gets the good shit + /obj/item/weapon/reagent_containers/spray/windowsealant) //VOREStation Add + +/obj/structure/closet/secure_closet/engineering_chief/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/industrial + else + starts_with += /obj/item/weapon/storage/backpack/satchel/eng + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/eng + return ..() + +/obj/structure/closet/secure_closet/engineering_electrical + name = "electrical supplies" + req_access = list(access_engine_equip) + closet_appearance = /decl/closet_appearance/secure_closet/engineering/electrical + + starts_with = list( + /obj/item/clothing/gloves/yellow = 2, + /obj/item/weapon/storage/toolbox/electrical = 3, + /obj/item/weapon/module/power_control = 3, + /obj/item/device/multitool = 3) + +/obj/structure/closet/secure_closet/engineering_electrical/double + starts_with = list( + /obj/item/clothing/gloves/yellow = 4, + /obj/item/weapon/storage/toolbox/electrical = 6, + /obj/item/weapon/module/power_control = 6, + /obj/item/device/multitool = 6) + +/obj/structure/closet/secure_closet/engineering_welding + name = "welding supplies" + req_access = list(access_construction) + closet_appearance = /decl/closet_appearance/secure_closet/engineering/welding + + starts_with = list( + /obj/item/clothing/head/welding = 3, + /obj/item/weapon/weldingtool/largetank = 3, + /obj/item/weapon/weldpack = 3, + /obj/item/clothing/glasses/welding = 3) + +/obj/structure/closet/secure_closet/engineering_welding/double + starts_with = list( + /obj/item/clothing/head/welding = 6, + /obj/item/weapon/weldingtool/largetank = 6, + /obj/item/weapon/weldpack = 6, + /obj/item/clothing/glasses/welding = 6) + +/obj/structure/closet/secure_closet/engineering_personal + name = "engineer's locker" + req_access = list(access_engine_equip) + closet_appearance = /decl/closet_appearance/secure_closet/engineering + + starts_with = list( + /obj/item/clothing/accessory/storage/brown_vest, + /obj/item/weapon/storage/toolbox/mechanical, + /obj/item/device/radio/headset/headset_eng, + /obj/item/device/radio/headset/headset_eng/alt, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/mask/gas, + /obj/item/clothing/glasses/meson, + /obj/item/weapon/cartridge/engineering, + /obj/item/taperoll/engineering, + /obj/item/clothing/head/hardhat, + /obj/item/clothing/suit/storage/hooded/wintercoat/engineering, + /obj/item/clothing/shoes/boots/winter/engineering, + /obj/item/weapon/tank/emergency/oxygen/engi, + /obj/item/weapon/storage/belt/utility, //VOREStation Add + /obj/item/weapon/reagent_containers/spray/windowsealant, //VOREStation Add + /obj/item/areaeditor/blueprints/engineers) //VOREStation Add + +/obj/structure/closet/secure_closet/engineering_personal/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/industrial + else + starts_with += /obj/item/weapon/storage/backpack/satchel/eng + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/eng + return ..() + + +/obj/structure/closet/secure_closet/atmos_personal + name = "technician's locker" + req_access = list(access_atmospherics) + closet_appearance = /decl/closet_appearance/secure_closet/engineering/atmos + + starts_with = list( + /obj/item/clothing/accessory/storage/brown_vest, + /obj/item/clothing/suit/fire/heavy, + /obj/item/clothing/head/hardhat/firefighter/atmos, + /obj/item/device/flashlight, + /obj/item/weapon/extinguisher/atmo, + ///obj/item/clamp, //VOREStation Removal: without leaks those are pointless, + /obj/item/device/radio/headset/headset_eng, + /obj/item/device/radio/headset/headset_eng/alt, + /obj/item/clothing/suit/storage/hazardvest/atmos, //VOREStation edit. Eng locker gets regular haz-vest, atmos gets the themed one of their own + /obj/item/clothing/mask/gas, + /obj/item/weapon/cartridge/atmos, + /obj/item/taperoll/atmos, + /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos, + /obj/item/clothing/shoes/boots/winter/atmos, + /obj/item/weapon/tank/emergency/oxygen/engi, + /obj/item/weapon/storage/belt/utility/atmostech) //VOREStation edit. They don't get a toolbox to fill it from, so why not give a spare one that's full already? + +/obj/structure/closet/secure_closet/atmos_personal/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/industrial + else + starts_with += /obj/item/weapon/storage/backpack/satchel/eng + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/eng + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index 66695f3511a..6659a89a074 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -1,51 +1,51 @@ -/obj/structure/closet/secure_closet/freezer/kitchen - name = "kitchen cabinet" - req_access = list(access_kitchen) - - starts_with = list( - /obj/item/weapon/reagent_containers/food/condiment/carton/flour = 6, - /obj/item/weapon/reagent_containers/food/condiment/carton/sugar = 1, - /obj/item/weapon/reagent_containers/food/condiment/carton/flour/rustic = 1, - /obj/item/weapon/reagent_containers/food/condiment/carton/sugar/rustic = 1, - /obj/item/weapon/reagent_containers/food/condiment/spacespice = 2 - ) - - open_sound = 'sound/machines/click.ogg' - close_sound = 'sound/machines/click.ogg' - -/obj/structure/closet/secure_closet/freezer/kitchen/mining - req_access = list() - - -/obj/structure/closet/secure_closet/freezer/meat - name = "meat fridge" - icon = 'icons/obj/closets/fridge.dmi' - closet_appearance = null - - starts_with = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey = 10) - - -/obj/structure/closet/secure_closet/freezer/fridge - name = "refrigerator" - icon = 'icons/obj/closets/fridge.dmi' - closet_appearance = null - - starts_with = list( - /obj/item/weapon/reagent_containers/food/drinks/milk = 6, - /obj/item/weapon/reagent_containers/food/drinks/soymilk = 4, - /obj/item/weapon/storage/fancy/egg_box = 4, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 2) - - -/obj/structure/closet/secure_closet/freezer/money - name = "freezer" - icon = 'icons/obj/closets/fridge.dmi' - closet_appearance = null - req_access = list(access_heads_vault) - - - starts_with = list( - /obj/item/weapon/spacecash/c1000 = 3, - /obj/item/weapon/spacecash/c500 = 4, - /obj/item/weapon/spacecash/c200 = 5) +/obj/structure/closet/secure_closet/freezer/kitchen + name = "kitchen cabinet" + req_access = list(access_kitchen) + + starts_with = list( + /obj/item/weapon/reagent_containers/food/condiment/carton/flour = 6, + /obj/item/weapon/reagent_containers/food/condiment/carton/sugar = 1, + /obj/item/weapon/reagent_containers/food/condiment/carton/flour/rustic = 1, + /obj/item/weapon/reagent_containers/food/condiment/carton/sugar/rustic = 1, + /obj/item/weapon/reagent_containers/food/condiment/spacespice = 2 + ) + + open_sound = 'sound/machines/click.ogg' + close_sound = 'sound/machines/click.ogg' + +/obj/structure/closet/secure_closet/freezer/kitchen/mining + req_access = list() + + +/obj/structure/closet/secure_closet/freezer/meat + name = "meat fridge" + icon = 'icons/obj/closets/fridge.dmi' + closet_appearance = null + + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/monkey = 10) + + +/obj/structure/closet/secure_closet/freezer/fridge + name = "refrigerator" + icon = 'icons/obj/closets/fridge.dmi' + closet_appearance = null + + starts_with = list( + /obj/item/weapon/reagent_containers/food/drinks/milk = 6, + /obj/item/weapon/reagent_containers/food/drinks/soymilk = 4, + /obj/item/weapon/storage/fancy/egg_box = 4, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose = 2) + + +/obj/structure/closet/secure_closet/freezer/money + name = "freezer" + icon = 'icons/obj/closets/fridge.dmi' + closet_appearance = null + req_access = list(access_heads_vault) + + + starts_with = list( + /obj/item/weapon/spacecash/c1000 = 3, + /obj/item/weapon/spacecash/c500 = 4, + /obj/item/weapon/spacecash/c200 = 5) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index 8a9bb829995..faa24433c14 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -1,269 +1,269 @@ -/obj/structure/closet/secure_closet/medical1 - name = "medicine closet" - desc = "Filled with medical junk." - req_access = list(access_medical) - closet_appearance = /decl/closet_appearance/secure_closet/medical/alt - - starts_with = list( - /obj/item/weapon/storage/box/autoinjectors, - /obj/item/weapon/storage/box/syringes, - /obj/item/weapon/reagent_containers/dropper = 2, - /obj/item/weapon/reagent_containers/glass/beaker = 2, - /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline = 2, - /obj/item/weapon/reagent_containers/glass/bottle/antitoxin = 2) - - -/obj/structure/closet/secure_closet/medical2 - name = "anesthetics closet" - desc = "Used to knock people out." - req_access = list(access_surgery) - closet_appearance = /decl/closet_appearance/secure_closet/medical - - starts_with = list( - /obj/item/weapon/tank/anesthetic = 3, - /obj/item/clothing/mask/breath/medical = 3) - - -/obj/structure/closet/secure_closet/medical3 - name = "medical doctor's locker" - req_access = list(access_medical_equip) - closet_appearance = /decl/closet_appearance/secure_closet/medical/doctor - - starts_with = list( - /obj/item/clothing/under/rank/medical, - /obj/item/clothing/under/rank/nurse, - /obj/item/clothing/under/rank/orderly, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/suit/storage/toggle/labcoat/modern, - /obj/item/clothing/suit/storage/toggle/fr_jacket, - /obj/item/clothing/shoes/white, - /obj/item/weapon/cartridge/medical, - /obj/item/device/radio/headset/headset_med, - /obj/item/device/radio/headset/headset_med/alt, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical/alt, - /obj/item/clothing/shoes/boots/winter/medical, - /obj/item/clothing/under/rank/nursesuit, - /obj/item/clothing/head/nursehat, - /obj/item/weapon/storage/box/freezer = 3, - /obj/item/weapon/storage/belt/medical) //VOREStation Add - -/obj/structure/closet/secure_closet/medical3/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/medic - else - starts_with += /obj/item/weapon/storage/backpack/satchel/med - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/med - switch(pick("blue", "green", "purple", "black", "navyblue")) - if ("blue") - starts_with += /obj/item/clothing/under/rank/medical/scrubs - starts_with += /obj/item/clothing/head/surgery/blue - if ("green") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/green - starts_with += /obj/item/clothing/head/surgery/green - if ("purple") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/purple - starts_with += /obj/item/clothing/head/surgery/purple - if ("black") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/black - starts_with += /obj/item/clothing/head/surgery/black - if ("navyblue") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/navyblue - starts_with += /obj/item/clothing/head/surgery/navyblue - switch(pick("blue", "green", "purple", "black", "navyblue")) - if ("blue") - starts_with += /obj/item/clothing/under/rank/medical/scrubs - starts_with += /obj/item/clothing/head/surgery/blue - if ("green") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/green - starts_with += /obj/item/clothing/head/surgery/green - if ("purple") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/purple - starts_with += /obj/item/clothing/head/surgery/purple - if ("black") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/black - starts_with += /obj/item/clothing/head/surgery/black - if ("navyblue") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/navyblue - starts_with += /obj/item/clothing/head/surgery/navyblue - return ..() - - -/obj/structure/closet/secure_closet/paramedic - name = "paramedic locker" - desc = "Supplies for a first responder." - req_access = list(access_medical_equip) - closet_appearance = /decl/closet_appearance/secure_closet/medical/paramedic - - starts_with = list( - /obj/item/weapon/storage/backpack/dufflebag/emt, - /obj/item/weapon/storage/box/autoinjectors, - /obj/item/weapon/storage/box/syringes, - /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline, - /obj/item/weapon/reagent_containers/glass/bottle/antitoxin, - /obj/item/weapon/storage/belt/medical/emt, - /obj/item/clothing/mask/gas, - /obj/item/clothing/suit/storage/toggle/fr_jacket, - /obj/item/clothing/suit/storage/toggle/labcoat/emt, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical/para, - /obj/item/clothing/shoes/boots/winter/medical, - /obj/item/device/radio/headset/headset_med/alt, - /obj/item/weapon/cartridge/medical, - /obj/item/weapon/storage/briefcase/inflatable, - /obj/item/device/flashlight, - /obj/item/weapon/tank/emergency/oxygen/engi, - /obj/item/clothing/glasses/hud/health, - /obj/item/device/healthanalyzer, - /obj/item/device/radio/off, - /obj/random/medical, - /obj/item/weapon/tool/crowbar, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/storage/box/freezer, - /obj/item/clothing/accessory/storage/white_vest, - /obj/item/taperoll/medical) - -/obj/structure/closet/secure_closet/CMO - name = "chief medical officer's locker" - req_access = list(access_cmo) - closet_appearance = /decl/closet_appearance/secure_closet/cmo - - starts_with = list( - /obj/item/clothing/under/rank/chief_medical_officer, - /obj/item/clothing/under/rank/chief_medical_officer/skirt, - /obj/item/clothing/suit/storage/toggle/labcoat/cmo, - /obj/item/clothing/suit/storage/toggle/labcoat/cmoalt, - /obj/item/clothing/suit/storage/toggle/labcoat/modern/cmo, - /obj/item/weapon/cartridge/cmo, - /obj/item/clothing/gloves/sterile/latex, - /obj/item/clothing/shoes/brown, - /obj/item/clothing/under/rank/neo_cmo, - /obj/item/clothing/under/rank/neo_cmo_skirt, - /obj/item/clothing/under/rank/neo_cmo_turtle, - /obj/item/clothing/under/rank/neo_cmo_turtle_skirt, - /obj/item/clothing/under/rank/neo_cmo_gorka, - /obj/item/device/radio/headset/heads/cmo, - /obj/item/device/radio/headset/heads/cmo/alt, - /obj/item/device/flash, - /obj/item/weapon/reagent_containers/hypospray/vial, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical/cmo, - /obj/item/clothing/shoes/boots/winter/medical, - /obj/item/clothing/head/beret/medical/cmo, - /obj/item/clothing/head/beret/medical/cmo/blue, - /obj/item/weapon/storage/box/freezer, - /obj/item/clothing/mask/gas, - /obj/item/taperoll/medical, - /obj/item/clothing/suit/bio_suit/cmo, - /obj/item/clothing/head/bio_hood/cmo, - /obj/item/clothing/shoes/white, - /obj/item/weapon/reagent_containers/glass/beaker/vial, //VOREStation Add - /obj/item/weapon/storage/belt/medical) //VOREStation Add - -/obj/structure/closet/secure_closet/CMO/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/medic - else - starts_with += /obj/item/weapon/storage/backpack/satchel/med - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/med - switch(pick("blue", "green", "purple", "black", "navyblue")) - if ("blue") - starts_with += /obj/item/clothing/under/rank/medical/scrubs - starts_with += /obj/item/clothing/head/surgery/blue - if ("green") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/green - starts_with += /obj/item/clothing/head/surgery/green - if ("purple") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/purple - starts_with += /obj/item/clothing/head/surgery/purple - if ("black") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/black - starts_with += /obj/item/clothing/head/surgery/black - if ("navyblue") - starts_with += /obj/item/clothing/under/rank/medical/scrubs/navyblue - starts_with += /obj/item/clothing/head/surgery/navyblue - return ..() - - -/obj/structure/closet/secure_closet/animal - name = "animal control closet" - req_access = list(access_surgery) - - starts_with = list( - /obj/item/device/assembly/signaler, - /obj/item/device/radio/electropack = 3) - - -/obj/structure/closet/secure_closet/chemical - name = "chemical closet" - desc = "Store dangerous chemicals in here." - req_access = list(access_chemistry) - closet_appearance = /decl/closet_appearance/secure_closet/medical/chemistry - - starts_with = list( - /obj/item/weapon/storage/box/pillbottles = 2, - /obj/item/weapon/storage/box/beakers, - /obj/item/weapon/storage/box/autoinjectors, - /obj/item/weapon/storage/box/syringes, - /obj/item/weapon/reagent_containers/dropper = 2, - /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline = 2, - /obj/item/weapon/reagent_containers/glass/bottle/antitoxin = 2, - /obj/item/weapon/storage/fancy/vials) //VOREStation Add - - -/obj/structure/closet/secure_closet/psych - name = "psychiatric cabinet" - desc = "Store psychology tools and medicines in here." - req_access = list(access_psychiatrist) - closet_appearance = /decl/closet_appearance/cabinet/secure - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - - starts_with = list( - /obj/item/clothing/under/rank/psych, - /obj/item/clothing/under/rank/psych/turtleneck, - /obj/item/clothing/suit/straight_jacket, - /obj/item/weapon/reagent_containers/glass/bottle/stoxin, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/weapon/storage/pill_bottle/citalopram, - /obj/item/weapon/reagent_containers/pill/methylphenidate, - /obj/item/weapon/clipboard, - /obj/item/weapon/folder/white, - /obj/item/device/taperecorder, - /obj/item/device/tape/random = 3, - /obj/item/device/camera, - /obj/item/toy/plushie/therapy/blue) - - -/obj/structure/closet/secure_closet/medical_wall - name = "first aid closet" - desc = "It's a secure wall-mounted storage unit for first aid supplies." - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - anchored = TRUE - density = FALSE - wall_mounted = 1 - store_mobs = 0 - req_access = list(access_medical_equip) - closet_appearance = /decl/closet_appearance/wall/medical - -/obj/structure/closet/secure_closet/medical_wall/pills - name = "pill cabinet" - - starts_with = list( - /obj/item/weapon/storage/pill_bottle/tramadol, - /obj/item/weapon/storage/pill_bottle/antitox, - /obj/item/weapon/storage/pill_bottle/carbon, - /obj/random/medical/pillbottle) - - -/obj/structure/closet/secure_closet/medical_wall/anesthetics - name = "anesthetics wall closet" - desc = "Used to knock people out." - req_access = list(access_surgery) - - starts_with = list( - /obj/item/weapon/tank/anesthetic = 3, - /obj/item/clothing/mask/breath/medical = 3) +/obj/structure/closet/secure_closet/medical1 + name = "medicine closet" + desc = "Filled with medical junk." + req_access = list(access_medical) + closet_appearance = /decl/closet_appearance/secure_closet/medical/alt + + starts_with = list( + /obj/item/weapon/storage/box/autoinjectors, + /obj/item/weapon/storage/box/syringes, + /obj/item/weapon/reagent_containers/dropper = 2, + /obj/item/weapon/reagent_containers/glass/beaker = 2, + /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline = 2, + /obj/item/weapon/reagent_containers/glass/bottle/antitoxin = 2) + + +/obj/structure/closet/secure_closet/medical2 + name = "anesthetics closet" + desc = "Used to knock people out." + req_access = list(access_surgery) + closet_appearance = /decl/closet_appearance/secure_closet/medical + + starts_with = list( + /obj/item/weapon/tank/anesthetic = 3, + /obj/item/clothing/mask/breath/medical = 3) + + +/obj/structure/closet/secure_closet/medical3 + name = "medical doctor's locker" + req_access = list(access_medical_equip) + closet_appearance = /decl/closet_appearance/secure_closet/medical/doctor + + starts_with = list( + /obj/item/clothing/under/rank/medical, + /obj/item/clothing/under/rank/nurse, + /obj/item/clothing/under/rank/orderly, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/suit/storage/toggle/labcoat/modern, + /obj/item/clothing/suit/storage/toggle/fr_jacket, + /obj/item/clothing/shoes/white, + /obj/item/weapon/cartridge/medical, + /obj/item/device/radio/headset/headset_med, + /obj/item/device/radio/headset/headset_med/alt, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical/alt, + /obj/item/clothing/shoes/boots/winter/medical, + /obj/item/clothing/under/rank/nursesuit, + /obj/item/clothing/head/nursehat, + /obj/item/weapon/storage/box/freezer = 3, + /obj/item/weapon/storage/belt/medical) //VOREStation Add + +/obj/structure/closet/secure_closet/medical3/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/medic + else + starts_with += /obj/item/weapon/storage/backpack/satchel/med + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/med + switch(pick("blue", "green", "purple", "black", "navyblue")) + if ("blue") + starts_with += /obj/item/clothing/under/rank/medical/scrubs + starts_with += /obj/item/clothing/head/surgery/blue + if ("green") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/green + starts_with += /obj/item/clothing/head/surgery/green + if ("purple") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/purple + starts_with += /obj/item/clothing/head/surgery/purple + if ("black") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/black + starts_with += /obj/item/clothing/head/surgery/black + if ("navyblue") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/navyblue + starts_with += /obj/item/clothing/head/surgery/navyblue + switch(pick("blue", "green", "purple", "black", "navyblue")) + if ("blue") + starts_with += /obj/item/clothing/under/rank/medical/scrubs + starts_with += /obj/item/clothing/head/surgery/blue + if ("green") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/green + starts_with += /obj/item/clothing/head/surgery/green + if ("purple") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/purple + starts_with += /obj/item/clothing/head/surgery/purple + if ("black") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/black + starts_with += /obj/item/clothing/head/surgery/black + if ("navyblue") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/navyblue + starts_with += /obj/item/clothing/head/surgery/navyblue + return ..() + + +/obj/structure/closet/secure_closet/paramedic + name = "paramedic locker" + desc = "Supplies for a first responder." + req_access = list(access_medical_equip) + closet_appearance = /decl/closet_appearance/secure_closet/medical/paramedic + + starts_with = list( + /obj/item/weapon/storage/backpack/dufflebag/emt, + /obj/item/weapon/storage/box/autoinjectors, + /obj/item/weapon/storage/box/syringes, + /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline, + /obj/item/weapon/reagent_containers/glass/bottle/antitoxin, + /obj/item/weapon/storage/belt/medical/emt, + /obj/item/clothing/mask/gas, + /obj/item/clothing/suit/storage/toggle/fr_jacket, + /obj/item/clothing/suit/storage/toggle/labcoat/emt, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical/para, + /obj/item/clothing/shoes/boots/winter/medical, + /obj/item/device/radio/headset/headset_med/alt, + /obj/item/weapon/cartridge/medical, + /obj/item/weapon/storage/briefcase/inflatable, + /obj/item/device/flashlight, + /obj/item/weapon/tank/emergency/oxygen/engi, + /obj/item/clothing/glasses/hud/health, + /obj/item/device/healthanalyzer, + /obj/item/device/radio/off, + /obj/random/medical, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/storage/box/freezer, + /obj/item/clothing/accessory/storage/white_vest, + /obj/item/taperoll/medical) + +/obj/structure/closet/secure_closet/CMO + name = "chief medical officer's locker" + req_access = list(access_cmo) + closet_appearance = /decl/closet_appearance/secure_closet/cmo + + starts_with = list( + /obj/item/clothing/under/rank/chief_medical_officer, + /obj/item/clothing/under/rank/chief_medical_officer/skirt, + /obj/item/clothing/suit/storage/toggle/labcoat/cmo, + /obj/item/clothing/suit/storage/toggle/labcoat/cmoalt, + /obj/item/clothing/suit/storage/toggle/labcoat/modern/cmo, + /obj/item/weapon/cartridge/cmo, + /obj/item/clothing/gloves/sterile/latex, + /obj/item/clothing/shoes/brown, + /obj/item/clothing/under/rank/neo_cmo, + /obj/item/clothing/under/rank/neo_cmo_skirt, + /obj/item/clothing/under/rank/neo_cmo_turtle, + /obj/item/clothing/under/rank/neo_cmo_turtle_skirt, + /obj/item/clothing/under/rank/neo_cmo_gorka, + /obj/item/device/radio/headset/heads/cmo, + /obj/item/device/radio/headset/heads/cmo/alt, + /obj/item/device/flash, + /obj/item/weapon/reagent_containers/hypospray/vial, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical/cmo, + /obj/item/clothing/shoes/boots/winter/medical, + /obj/item/clothing/head/beret/medical/cmo, + /obj/item/clothing/head/beret/medical/cmo/blue, + /obj/item/weapon/storage/box/freezer, + /obj/item/clothing/mask/gas, + /obj/item/taperoll/medical, + /obj/item/clothing/suit/bio_suit/cmo, + /obj/item/clothing/head/bio_hood/cmo, + /obj/item/clothing/shoes/white, + /obj/item/weapon/reagent_containers/glass/beaker/vial, //VOREStation Add + /obj/item/weapon/storage/belt/medical) //VOREStation Add + +/obj/structure/closet/secure_closet/CMO/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/medic + else + starts_with += /obj/item/weapon/storage/backpack/satchel/med + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/med + switch(pick("blue", "green", "purple", "black", "navyblue")) + if ("blue") + starts_with += /obj/item/clothing/under/rank/medical/scrubs + starts_with += /obj/item/clothing/head/surgery/blue + if ("green") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/green + starts_with += /obj/item/clothing/head/surgery/green + if ("purple") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/purple + starts_with += /obj/item/clothing/head/surgery/purple + if ("black") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/black + starts_with += /obj/item/clothing/head/surgery/black + if ("navyblue") + starts_with += /obj/item/clothing/under/rank/medical/scrubs/navyblue + starts_with += /obj/item/clothing/head/surgery/navyblue + return ..() + + +/obj/structure/closet/secure_closet/animal + name = "animal control closet" + req_access = list(access_surgery) + + starts_with = list( + /obj/item/device/assembly/signaler, + /obj/item/device/radio/electropack = 3) + + +/obj/structure/closet/secure_closet/chemical + name = "chemical closet" + desc = "Store dangerous chemicals in here." + req_access = list(access_chemistry) + closet_appearance = /decl/closet_appearance/secure_closet/medical/chemistry + + starts_with = list( + /obj/item/weapon/storage/box/pillbottles = 2, + /obj/item/weapon/storage/box/beakers, + /obj/item/weapon/storage/box/autoinjectors, + /obj/item/weapon/storage/box/syringes, + /obj/item/weapon/reagent_containers/dropper = 2, + /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline = 2, + /obj/item/weapon/reagent_containers/glass/bottle/antitoxin = 2, + /obj/item/weapon/storage/fancy/vials) //VOREStation Add + + +/obj/structure/closet/secure_closet/psych + name = "psychiatric cabinet" + desc = "Store psychology tools and medicines in here." + req_access = list(access_psychiatrist) + closet_appearance = /decl/closet_appearance/cabinet/secure + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + + starts_with = list( + /obj/item/clothing/under/rank/psych, + /obj/item/clothing/under/rank/psych/turtleneck, + /obj/item/clothing/suit/straight_jacket, + /obj/item/weapon/reagent_containers/glass/bottle/stoxin, + /obj/item/weapon/reagent_containers/syringe, + /obj/item/weapon/storage/pill_bottle/citalopram, + /obj/item/weapon/reagent_containers/pill/methylphenidate, + /obj/item/weapon/clipboard, + /obj/item/weapon/folder/white, + /obj/item/device/taperecorder, + /obj/item/device/tape/random = 3, + /obj/item/device/camera, + /obj/item/toy/plushie/therapy/blue) + + +/obj/structure/closet/secure_closet/medical_wall + name = "first aid closet" + desc = "It's a secure wall-mounted storage unit for first aid supplies." + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + anchored = TRUE + density = FALSE + wall_mounted = 1 + store_mobs = 0 + req_access = list(access_medical_equip) + closet_appearance = /decl/closet_appearance/wall/medical + +/obj/structure/closet/secure_closet/medical_wall/pills + name = "pill cabinet" + + starts_with = list( + /obj/item/weapon/storage/pill_bottle/tramadol, + /obj/item/weapon/storage/pill_bottle/antitox, + /obj/item/weapon/storage/pill_bottle/carbon, + /obj/random/medical/pillbottle) + + +/obj/structure/closet/secure_closet/medical_wall/anesthetics + name = "anesthetics wall closet" + desc = "Used to knock people out." + req_access = list(access_surgery) + + starts_with = list( + /obj/item/weapon/tank/anesthetic = 3, + /obj/item/clothing/mask/breath/medical = 3) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index 0dc89e7c46e..39e8f2d954d 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -1,115 +1,115 @@ -/obj/structure/closet/secure_closet/personal - name = "personal closet" - desc = "It's a secure locker for personnel. The first card swiped gains control." - req_access = list(access_all_personal_lockers) - var/registered_name = null - - /* //VOREStation Removal - starts_with = list( - /obj/item/device/radio/headset) - */ - -/obj/structure/closet/secure_closet/personal/Initialize() - /* //VOREStation Removal - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack - else - starts_with += /obj/item/weapon/storage/backpack/satchel/norm - */ - return ..() - -/obj/structure/closet/secure_closet/personal/patient - name = "patient's closet" - closet_appearance = /decl/closet_appearance/secure_closet/patient - - starts_with = list( - /obj/item/clothing/under/medigown, - /obj/item/clothing/under/color/white, - /obj/item/clothing/shoes/white) - - -/obj/structure/closet/secure_closet/personal/cabinet - closet_appearance = /decl/closet_appearance/cabinet/secure - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - - starts_with = list( - /obj/item/weapon/storage/backpack/satchel/withwallet, - /obj/item/device/radio/headset - ) - -/obj/structure/closet/secure_closet/personal/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (src.opened) - if(istype(W, /obj/item/weapon/storage/laundry_basket)) - return ..(W,user) - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - if(large) - MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet - else - to_chat(user, "The locker is too small to stuff [G.affecting] into!") - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - user.drop_item() - if(W) - W.forceMove(loc) - else if(W.GetID()) - var/obj/item/weapon/card/id/I = W.GetID() - - if(src.broken) - to_chat(user, "It appears to be broken.") - return - if(!I || !I.registered_name) return - if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) - //they can open all lockers, or nobody owns this, or they own this locker - src.locked = !( src.locked ) - - if(!src.registered_name) - src.registered_name = I.registered_name - src.desc = "Owned by [I.registered_name]." - else - to_chat(user, "Access Denied") - else if(istype(W, /obj/item/weapon/melee/energy/blade)) - if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src.loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - else - to_chat(user, "Access Denied") - update_icon() - -/obj/structure/closet/secure_closet/personal/emag_act(var/remaining_charges, var/mob/user, var/visual_feedback, var/audible_feedback) - if(!broken) - broken = 1 - locked = 0 - desc = "It appears to be broken." - update_icon() - if(visual_feedback) - visible_message("[visual_feedback]", "[audible_feedback]") - return 1 - -/obj/structure/closet/secure_closet/personal/verb/reset() - set src in oview(1) // One square distance - set category = "Object" - set name = "Reset Lock" - if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - if(ishuman(usr)) - src.add_fingerprint(usr) - if (src.locked || !src.registered_name) - to_chat(usr, "You need to unlock it first.") - else if (src.broken) - to_chat(usr, "It appears to be broken.") - else - if (src.opened) - if(!src.close()) - return - src.locked = 1 - update_icon() - src.registered_name = null - src.desc = "It's a secure locker for personnel. The first card swiped gains control." +/obj/structure/closet/secure_closet/personal + name = "personal closet" + desc = "It's a secure locker for personnel. The first card swiped gains control." + req_access = list(access_all_personal_lockers) + var/registered_name = null + + /* //VOREStation Removal + starts_with = list( + /obj/item/device/radio/headset) + */ + +/obj/structure/closet/secure_closet/personal/Initialize() + /* //VOREStation Removal + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack + else + starts_with += /obj/item/weapon/storage/backpack/satchel/norm + */ + return ..() + +/obj/structure/closet/secure_closet/personal/patient + name = "patient's closet" + closet_appearance = /decl/closet_appearance/secure_closet/patient + + starts_with = list( + /obj/item/clothing/under/medigown, + /obj/item/clothing/under/color/white, + /obj/item/clothing/shoes/white) + + +/obj/structure/closet/secure_closet/personal/cabinet + closet_appearance = /decl/closet_appearance/cabinet/secure + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + + starts_with = list( + /obj/item/weapon/storage/backpack/satchel/withwallet, + /obj/item/device/radio/headset + ) + +/obj/structure/closet/secure_closet/personal/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (src.opened) + if(istype(W, /obj/item/weapon/storage/laundry_basket)) + return ..(W,user) + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(large) + MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet + else + to_chat(user, "The locker is too small to stuff [G.affecting] into!") + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + user.drop_item() + if(W) + W.forceMove(loc) + else if(W.GetID()) + var/obj/item/weapon/card/id/I = W.GetID() + + if(src.broken) + to_chat(user, "It appears to be broken.") + return + if(!I || !I.registered_name) return + if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) + //they can open all lockers, or nobody owns this, or they own this locker + src.locked = !( src.locked ) + + if(!src.registered_name) + src.registered_name = I.registered_name + src.desc = "Owned by [I.registered_name]." + else + to_chat(user, "Access Denied") + else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src.loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + else + to_chat(user, "Access Denied") + update_icon() + +/obj/structure/closet/secure_closet/personal/emag_act(var/remaining_charges, var/mob/user, var/visual_feedback, var/audible_feedback) + if(!broken) + broken = 1 + locked = 0 + desc = "It appears to be broken." + update_icon() + if(visual_feedback) + visible_message("[visual_feedback]", "[audible_feedback]") + return 1 + +/obj/structure/closet/secure_closet/personal/verb/reset() + set src in oview(1) // One square distance + set category = "Object" + set name = "Reset Lock" + if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + if(ishuman(usr)) + src.add_fingerprint(usr) + if (src.locked || !src.registered_name) + to_chat(usr, "You need to unlock it first.") + else if (src.broken) + to_chat(usr, "It appears to be broken.") + else + if (src.opened) + if(!src.close()) + return + src.locked = 1 + update_icon() + src.registered_name = null + src.desc = "It's a secure locker for personnel. The first card swiped gains control." diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 81f2911baae..c941f8306ed 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -1,93 +1,93 @@ -/obj/structure/closet/secure_closet/scientist - name = "scientist's locker" - req_access = list(access_tox_storage) - closet_appearance = /decl/closet_appearance/secure_closet/science - - starts_with = list( - /obj/item/clothing/under/rank/scientist, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/shoes/white, - /obj/item/device/radio/headset/headset_sci, - /obj/item/weapon/tank/air, - /obj/item/clothing/mask/gas, - /obj/item/clothing/suit/storage/hooded/wintercoat/science, - /obj/item/clothing/shoes/boots/winter/science) - -/obj/structure/closet/secure_closet/scientist/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci - else - starts_with += /obj/item/weapon/storage/backpack/toxins - return ..() - - -/obj/structure/closet/secure_closet/RD - name = "research director's locker" - req_access = list(access_rd) - closet_appearance = /decl/closet_appearance/secure_closet/science/rd - - starts_with = list( - /obj/item/clothing/suit/bio_suit/scientist, - /obj/item/clothing/head/bio_hood/scientist, - /obj/item/clothing/under/rank/research_director, - /obj/item/clothing/under/rank/research_director/rdalt, - /obj/item/clothing/under/rank/research_director/dress_rd, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/suit/storage/toggle/labcoat/modern, - /obj/item/clothing/suit/storage/toggle/labcoat/rd, - /obj/item/clothing/under/rank/neo_rd_turtle, - /obj/item/clothing/under/rank/neo_rd_turtle_skirt, - /obj/item/clothing/under/rank/neo_rd_suit, - /obj/item/clothing/under/rank/neo_rd_suit_skirt, - /obj/item/clothing/under/rank/neo_rd_gorka, - /obj/item/weapon/cartridge/rd, - /obj/item/clothing/shoes/white, - /obj/item/clothing/shoes/laceup/brown, - /obj/item/clothing/gloves/sterile/latex, - /obj/item/device/radio/headset/heads/rd, - /obj/item/device/radio/headset/heads/rd/alt, - /obj/item/weapon/tank/air, - /obj/item/clothing/mask/gas, - /obj/item/device/flash, - /obj/item/clothing/suit/storage/hooded/wintercoat/science, - /obj/item/clothing/suit/storage/hooded/wintercoat/science/rd, - /obj/item/clothing/shoes/boots/winter/science, - /obj/item/clothing/head/beret/science/rd, - /obj/item/weapon/bluespace_harpoon) //VOREStation Add - -/obj/structure/closet/secure_closet/xenoarchaeologist - name = "Xenoarchaeologist Locker" - req_access = list(access_tox_storage) - closet_appearance = /decl/closet_appearance/secure_closet/science/xenoarch - - starts_with = list( - /obj/item/clothing/under/rank/scientist, - /obj/item/clothing/suit/storage/toggle/labcoat, - /obj/item/clothing/suit/storage/toggle/labcoat/modern, - /obj/item/clothing/shoes/white, - /obj/item/weapon/melee/umbrella, - /obj/item/clothing/glasses/science, - /obj/item/device/radio/headset/headset_sci, - /obj/item/weapon/storage/belt/archaeology, - /obj/item/weapon/storage/excavation) - -/obj/structure/closet/excavation - name = "Excavation tools" - closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/xenoarch - - starts_with = list( - /obj/item/weapon/storage/belt/archaeology, - /obj/item/weapon/storage/excavation, - /obj/item/device/flashlight/lantern, - /obj/item/device/ano_scanner, - /obj/item/device/depth_scanner, - /obj/item/device/core_sampler, - /obj/item/device/gps, - /obj/item/device/beacon_locator, - /obj/item/device/radio/beacon, - /obj/item/clothing/glasses/meson, - /obj/item/weapon/pickaxe, - /obj/item/device/measuring_tape, - /obj/item/weapon/pickaxe/hand, - /obj/item/weapon/storage/bag/fossils, - /obj/item/weapon/hand_labeler) +/obj/structure/closet/secure_closet/scientist + name = "scientist's locker" + req_access = list(access_tox_storage) + closet_appearance = /decl/closet_appearance/secure_closet/science + + starts_with = list( + /obj/item/clothing/under/rank/scientist, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/shoes/white, + /obj/item/device/radio/headset/headset_sci, + /obj/item/weapon/tank/air, + /obj/item/clothing/mask/gas, + /obj/item/clothing/suit/storage/hooded/wintercoat/science, + /obj/item/clothing/shoes/boots/winter/science) + +/obj/structure/closet/secure_closet/scientist/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci + else + starts_with += /obj/item/weapon/storage/backpack/toxins + return ..() + + +/obj/structure/closet/secure_closet/RD + name = "research director's locker" + req_access = list(access_rd) + closet_appearance = /decl/closet_appearance/secure_closet/science/rd + + starts_with = list( + /obj/item/clothing/suit/bio_suit/scientist, + /obj/item/clothing/head/bio_hood/scientist, + /obj/item/clothing/under/rank/research_director, + /obj/item/clothing/under/rank/research_director/rdalt, + /obj/item/clothing/under/rank/research_director/dress_rd, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/suit/storage/toggle/labcoat/modern, + /obj/item/clothing/suit/storage/toggle/labcoat/rd, + /obj/item/clothing/under/rank/neo_rd_turtle, + /obj/item/clothing/under/rank/neo_rd_turtle_skirt, + /obj/item/clothing/under/rank/neo_rd_suit, + /obj/item/clothing/under/rank/neo_rd_suit_skirt, + /obj/item/clothing/under/rank/neo_rd_gorka, + /obj/item/weapon/cartridge/rd, + /obj/item/clothing/shoes/white, + /obj/item/clothing/shoes/laceup/brown, + /obj/item/clothing/gloves/sterile/latex, + /obj/item/device/radio/headset/heads/rd, + /obj/item/device/radio/headset/heads/rd/alt, + /obj/item/weapon/tank/air, + /obj/item/clothing/mask/gas, + /obj/item/device/flash, + /obj/item/clothing/suit/storage/hooded/wintercoat/science, + /obj/item/clothing/suit/storage/hooded/wintercoat/science/rd, + /obj/item/clothing/shoes/boots/winter/science, + /obj/item/clothing/head/beret/science/rd, + /obj/item/weapon/bluespace_harpoon) //VOREStation Add + +/obj/structure/closet/secure_closet/xenoarchaeologist + name = "Xenoarchaeologist Locker" + req_access = list(access_tox_storage) + closet_appearance = /decl/closet_appearance/secure_closet/science/xenoarch + + starts_with = list( + /obj/item/clothing/under/rank/scientist, + /obj/item/clothing/suit/storage/toggle/labcoat, + /obj/item/clothing/suit/storage/toggle/labcoat/modern, + /obj/item/clothing/shoes/white, + /obj/item/weapon/melee/umbrella, + /obj/item/clothing/glasses/science, + /obj/item/device/radio/headset/headset_sci, + /obj/item/weapon/storage/belt/archaeology, + /obj/item/weapon/storage/excavation) + +/obj/structure/closet/excavation + name = "Excavation tools" + closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/xenoarch + + starts_with = list( + /obj/item/weapon/storage/belt/archaeology, + /obj/item/weapon/storage/excavation, + /obj/item/device/flashlight/lantern, + /obj/item/device/ano_scanner, + /obj/item/device/depth_scanner, + /obj/item/device/core_sampler, + /obj/item/device/gps, + /obj/item/device/beacon_locator, + /obj/item/device/radio/beacon, + /obj/item/clothing/glasses/meson, + /obj/item/weapon/pickaxe, + /obj/item/device/measuring_tape, + /obj/item/weapon/pickaxe/hand, + /obj/item/weapon/storage/bag/fossils, + /obj/item/weapon/hand_labeler) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm index 85b9367b7d5..ba9c1ba8929 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm @@ -1,158 +1,158 @@ -/obj/structure/closet/secure_closet - name = "secure locker" - desc = "It's an immobile card-locked storage unit." - icon = 'icons/obj/closet.dmi' - icon_state = "secure1" - density = TRUE - opened = 0 - var/locked = 1 - var/broken = 0 - var/large = 1 - wall_mounted = 0 //never solid (You can always pass over it) - health = 200 - - closet_appearance = /decl/closet_appearance/secure_closet - -/obj/structure/closet/secure_closet/can_open() - if(locked) - return 0 - return ..() - -/obj/structure/closet/secure_closet/emp_act(severity) - for(var/obj/O in src) - O.emp_act(severity) - if(!broken) - if(prob(50/severity)) - locked = !locked - update_icon() - if(prob(20/severity) && !opened) - if(!locked) - open() - else - req_access = list() - req_access += pick(get_all_station_access()) - ..() - -/obj/structure/closet/secure_closet/proc/togglelock(mob/user as mob) - if(opened) - to_chat(user, "Close the locker first.") - return - if(broken) - to_chat(user, "The locker appears to be broken.") - return - if(user.loc == src) - to_chat(user, "You can't reach the lock from inside.") - return - if(allowed(user)) - locked = !locked - playsound(src, 'sound/machines/click.ogg', 15, 1, -3) - for(var/mob/O in viewers(user, 3)) - if((O.client && !( O.blinded ))) - to_chat(O, "The locker has been [locked ? null : "un"]locked by [user].") - update_icon() - else - to_chat(user, "Access Denied") - -/obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - if(opened) - if(anchored) - user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") - else - user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") - if(do_after(user, 20 * W.toolspeed)) - if(!src) return - to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") - anchored = !anchored - return - else - to_chat(user, "You can't reach the anchoring bolts when the door is closed!") - else if(opened) - if(istype(W, /obj/item/weapon/storage/laundry_basket)) - return ..(W,user) - if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - if(large) - MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet - else - to_chat(user, "The locker is too small to stuff [G.affecting] into!") - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - user.drop_item() - if(W) - W.forceMove(loc) - else if(istype(W, /obj/item/weapon/melee/energy/blade)) - if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, loc) - spark_system.start() - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - playsound(src, "sparks", 50, 1) - else if(istype(W,/obj/item/weapon/packageWrap) || istype(W,/obj/item/weapon/weldingtool)) - return ..(W,user) - else - togglelock(user) - -/obj/structure/closet/secure_closet/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") - if(!broken) - broken = 1 - locked = 0 - desc = "It appears to be broken." - - if(visual_feedback) - visible_message(visual_feedback, audible_feedback) - else if(user && emag_source) - visible_message("\The [src] has been broken by \the [user] with \an [emag_source]!", "You hear a faint electrical spark.") - else - visible_message("\The [src] sparks and breaks open!", "You hear a faint electrical spark.") - update_icon() - return 1 - -/obj/structure/closet/secure_closet/attack_hand(mob/user as mob) - add_fingerprint(user) - if(locked) - togglelock(user) - else - toggle(user) - -/obj/structure/closet/secure_closet/AltClick() - ..() - verb_togglelock() - -/obj/structure/closet/secure_closet/verb/verb_togglelock() - set src in oview(1) // One square distance - set category = "Object" - set name = "Toggle Lock" - - if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr)) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - - if(ishuman(usr) || isrobot(usr)) - add_fingerprint(usr) - togglelock(usr) - else - to_chat(usr, "This mob type can't use this verb.") - -/obj/structure/closet/secure_closet/update_icon() - if(opened) - icon_state = "open" - else - if(broken) - icon_state = "closed_emagged[sealed ? "_welded" : ""]" - else - if(locked) - icon_state = "closed_locked[sealed ? "_welded" : ""]" - else - icon_state = "closed_unlocked[sealed ? "_welded" : ""]" - -/obj/structure/closet/secure_closet/req_breakout() - if(!opened && locked) return 1 - return ..() //It's a secure closet, but isn't locked. - -/obj/structure/closet/secure_closet/break_open() - desc += " It appears to be broken." - broken = 1 - locked = 0 - ..() +/obj/structure/closet/secure_closet + name = "secure locker" + desc = "It's an immobile card-locked storage unit." + icon = 'icons/obj/closet.dmi' + icon_state = "secure1" + density = TRUE + opened = 0 + var/locked = 1 + var/broken = 0 + var/large = 1 + wall_mounted = 0 //never solid (You can always pass over it) + health = 200 + + closet_appearance = /decl/closet_appearance/secure_closet + +/obj/structure/closet/secure_closet/can_open() + if(locked) + return 0 + return ..() + +/obj/structure/closet/secure_closet/emp_act(severity) + for(var/obj/O in src) + O.emp_act(severity) + if(!broken) + if(prob(50/severity)) + locked = !locked + update_icon() + if(prob(20/severity) && !opened) + if(!locked) + open() + else + req_access = list() + req_access += pick(get_all_station_access()) + ..() + +/obj/structure/closet/secure_closet/proc/togglelock(mob/user as mob) + if(opened) + to_chat(user, "Close the locker first.") + return + if(broken) + to_chat(user, "The locker appears to be broken.") + return + if(user.loc == src) + to_chat(user, "You can't reach the lock from inside.") + return + if(allowed(user)) + locked = !locked + playsound(src, 'sound/machines/click.ogg', 15, 1, -3) + for(var/mob/O in viewers(user, 3)) + if((O.client && !( O.blinded ))) + to_chat(O, "The locker has been [locked ? null : "un"]locked by [user].") + update_icon() + else + to_chat(user, "Access Denied") + +/obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + if(opened) + if(anchored) + user.visible_message("\The [user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") + else + user.visible_message("\The [user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") + if(do_after(user, 20 * W.toolspeed)) + if(!src) return + to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") + anchored = !anchored + return + else + to_chat(user, "You can't reach the anchoring bolts when the door is closed!") + else if(opened) + if(istype(W, /obj/item/weapon/storage/laundry_basket)) + return ..(W,user) + if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(large) + MouseDrop_T(G.affecting, user) //act like they were dragged onto the closet + else + to_chat(user, "The locker is too small to stuff [G.affecting] into!") + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + user.drop_item() + if(W) + W.forceMove(loc) + else if(istype(W, /obj/item/weapon/melee/energy/blade)) + if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, loc) + spark_system.start() + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + playsound(src, "sparks", 50, 1) + else if(istype(W,/obj/item/weapon/packageWrap) || W.has_tool_quality(TOOL_WELDER)) + return ..(W,user) + else + togglelock(user) + +/obj/structure/closet/secure_closet/emag_act(var/remaining_charges, var/mob/user, var/emag_source, var/visual_feedback = "", var/audible_feedback = "") + if(!broken) + broken = 1 + locked = 0 + desc = "It appears to be broken." + + if(visual_feedback) + visible_message(visual_feedback, audible_feedback) + else if(user && emag_source) + visible_message("\The [src] has been broken by \the [user] with \an [emag_source]!", "You hear a faint electrical spark.") + else + visible_message("\The [src] sparks and breaks open!", "You hear a faint electrical spark.") + update_icon() + return 1 + +/obj/structure/closet/secure_closet/attack_hand(mob/user as mob) + add_fingerprint(user) + if(locked) + togglelock(user) + else + toggle(user) + +/obj/structure/closet/secure_closet/AltClick() + ..() + verb_togglelock() + +/obj/structure/closet/secure_closet/verb/verb_togglelock() + set src in oview(1) // One square distance + set category = "Object" + set name = "Toggle Lock" + + if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr)) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + + if(ishuman(usr) || isrobot(usr)) + add_fingerprint(usr) + togglelock(usr) + else + to_chat(usr, "This mob type can't use this verb.") + +/obj/structure/closet/secure_closet/update_icon() + if(opened) + icon_state = "open" + else + if(broken) + icon_state = "closed_emagged[sealed ? "_welded" : ""]" + else + if(locked) + icon_state = "closed_locked[sealed ? "_welded" : ""]" + else + icon_state = "closed_unlocked[sealed ? "_welded" : ""]" + +/obj/structure/closet/secure_closet/req_breakout() + if(!opened && locked) return 1 + return ..() //It's a secure closet, but isn't locked. + +/obj/structure/closet/secure_closet/break_open() + desc += " It appears to be broken." + broken = 1 + locked = 0 + ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index b337e0db897..c1eb7348efc 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -1,325 +1,325 @@ -/obj/structure/closet/secure_closet/captains - name = "site manager's locker" - req_access = list(access_captain) - closet_appearance = /decl/closet_appearance/secure_closet/command - - starts_with = list( - /obj/item/weapon/storage/backpack/dufflebag/captain, - /obj/item/clothing/head/helmet, - /obj/item/clothing/suit/storage/vest, - /obj/item/weapon/cartridge/captain, - /obj/item/weapon/storage/lockbox/medal, - /obj/item/device/radio/headset/heads/captain, - /obj/item/device/radio/headset/heads/captain/alt, - /obj/item/weapon/gun/energy/gun, - /obj/item/weapon/melee/telebaton, - /obj/item/device/flash, - /obj/item/weapon/storage/box/ids) - - -/obj/structure/closet/secure_closet/hop - name = "head of personnel's locker" - req_access = list(access_hop) - closet_appearance = /decl/closet_appearance/secure_closet/command/hop - - starts_with = list( - /obj/item/clothing/suit/storage/vest, - /obj/item/clothing/head/helmet, - /obj/item/weapon/cartridge/hop, - /obj/item/device/radio/headset/heads/hop, - /obj/item/device/radio/headset/heads/hop/alt, - /obj/item/weapon/storage/box/ids = 2, - /obj/item/weapon/gun/energy/gun/compact, - /obj/item/weapon/storage/box/commandkeys, - /obj/item/weapon/storage/box/servicekeys, - /obj/item/device/flash) - -/obj/structure/closet/secure_closet/hop2 - name = "head of personnel's attire" - req_access = list(access_hop) - closet_appearance = /decl/closet_appearance/secure_closet/command/hop - - starts_with = list( - /obj/item/clothing/under/rank/head_of_personnel, - /obj/item/clothing/under/dress/dress_hop, - /obj/item/clothing/under/dress/dress_hr, - /obj/item/clothing/under/lawyer/female, - /obj/item/clothing/under/lawyer/black, - /obj/item/clothing/under/lawyer/black/skirt, - /obj/item/clothing/under/lawyer/red, - /obj/item/clothing/under/lawyer/red/skirt, - /obj/item/clothing/under/lawyer/oldman, - /obj/item/clothing/under/rank/neo_hop, - /obj/item/clothing/under/rank/neo_hop_skirt, - /obj/item/clothing/under/rank/neo_hop_parade_masc, - /obj/item/clothing/under/rank/neo_hop_parade_fem, - /obj/item/clothing/under/rank/neo_hop_turtle, - /obj/item/clothing/under/rank/neo_hop_turtle_skirt, - /obj/item/clothing/under/rank/neo_cmd_gorka, - /obj/item/clothing/suit/storage/toggle/labcoat/neo_hopformal, - /obj/item/clothing/suit/storage/toggle/labcoat/neo_civ_dep, - /obj/item/clothing/shoes/brown, - /obj/item/clothing/shoes/black, - /obj/item/clothing/shoes/laceup, - /obj/item/clothing/shoes/laceup/brown, - /obj/item/clothing/shoes/white, - /obj/item/clothing/under/rank/head_of_personnel_whimsy, - /obj/item/clothing/head/caphat/hop, - /obj/item/clothing/under/suit_jacket/teal, - /obj/item/clothing/under/suit_jacket/teal/skirt, - /obj/item/clothing/glasses/sunglasses, - /obj/item/clothing/suit/storage/hooded/wintercoat/hop, - /obj/item/clothing/head/caphat/hop/beret, - /obj/item/clothing/head/caphat/hop/beret/white) - - -/obj/structure/closet/secure_closet/hos - name = "head of security's locker" - req_access = list(access_hos) - storage_capacity = 2.6 * MOB_MEDIUM - closet_appearance = /decl/closet_appearance/secure_closet/security/hos - - starts_with = list( - /obj/item/clothing/head/helmet/HoS, - /obj/item/clothing/head/helmet/HoS/hat, - /obj/item/clothing/suit/storage/vest/hos, - /obj/item/clothing/under/rank/head_of_security/jensen, - /obj/item/clothing/under/rank/head_of_security/corp, - /obj/item/clothing/suit/storage/vest/hoscoat/jensen, - /obj/item/clothing/suit/storage/vest/hoscoat, - /obj/item/clothing/head/helmet/dermal, - /obj/item/weapon/cartridge/hos, - /obj/item/device/radio/headset/heads/hos, - /obj/item/device/radio/headset/heads/hos/alt, - /obj/item/clothing/glasses/sunglasses/sechud, - /obj/item/taperoll/police, - /obj/item/weapon/shield/riot, - /obj/item/weapon/shield/riot/tele, - /obj/item/weapon/storage/box/holobadge/hos, - /obj/item/clothing/accessory/badge/holo/hos, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/tool/crowbar/red, - /obj/item/weapon/storage/box/flashbangs, - /obj/item/weapon/storage/belt/security, - /obj/item/device/flash, - /obj/item/weapon/melee/baton/loaded, - /obj/item/weapon/gun/magnetic/railgun/heater/pistol/hos, - /obj/item/weapon/rcd_ammo/large, - /obj/item/weapon/cell/device/weapon, - /obj/item/clothing/accessory/holster/waist, - /obj/item/weapon/melee/telebaton, - /obj/item/clothing/head/beret/sec/corporate/hos, - /obj/item/clothing/suit/storage/hooded/wintercoat/security, - /obj/item/clothing/suit/storage/hooded/wintercoat/security/hos, - /obj/item/clothing/shoes/boots/winter/security, - /obj/item/device/flashlight/maglight, - /obj/item/clothing/mask/gas/half, - /obj/item/clothing/mask/gas/sechailer/swat/hos) - -/obj/structure/closet/secure_closet/hos/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/security - else - starts_with += /obj/item/weapon/storage/backpack/satchel/sec - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec - return ..() - - -/obj/structure/closet/secure_closet/warden - name = "warden's locker" - storage_capacity = 42 - req_access = list(access_armory) - closet_appearance = /decl/closet_appearance/secure_closet/security/warden - - starts_with = list( - /obj/item/clothing/suit/storage/vest/warden, - /obj/item/clothing/under/rank/warden, - /obj/item/clothing/under/rank/warden/corp, - /obj/item/clothing/suit/storage/vest/wardencoat, - /obj/item/clothing/suit/storage/vest/wardencoat/alt, - /obj/item/clothing/suit/storage/vest/wardencoat/alt2, //VOREStation Add, - /obj/item/clothing/head/helmet/dermal, - /obj/item/clothing/head/helmet/warden, - /obj/item/clothing/head/helmet/warden/hat, - /obj/item/clothing/under/rank/neo_warden_red, - /obj/item/clothing/under/rank/neo_warden_red_skirt, - /obj/item/clothing/under/rank/neo_warden_blue, - /obj/item/clothing/suit/storage/vest/wardencoat/neo_armsco_trench, - /obj/item/clothing/suit/storage/vest/wardencoat/neo_bluewarden, - /obj/item/clothing/suit/storage/vest/wardencoat/neo_warden_heavy, - /obj/item/clothing/under/rank/neo_sec_gorka, - /obj/item/weapon/cartridge/security, - /obj/item/device/radio/headset/headset_sec, - /obj/item/device/radio/headset/headset_sec/alt, - /obj/item/clothing/glasses/sunglasses/sechud, - /obj/item/taperoll/police, - /obj/item/clothing/accessory/badge/holo/warden, - /obj/item/weapon/storage/box/flashbangs, - /obj/item/weapon/storage/belt/security, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/melee/baton/loaded, - /obj/item/weapon/gun/energy/gun, - /obj/item/weapon/cell/device/weapon, - /obj/item/weapon/storage/box/holobadge, - /obj/item/clothing/head/beret/sec/corporate/warden, - /obj/item/clothing/suit/storage/hooded/wintercoat/security, - /obj/item/clothing/shoes/boots/winter/security, - /obj/item/device/flashlight/maglight, - /obj/item/device/megaphone, - /obj/item/clothing/mask/gas/half, - /obj/item/clothing/mask/gas/sechailer/swat/warden) - -/obj/structure/closet/secure_closet/warden/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/security - else - starts_with += /obj/item/weapon/storage/backpack/satchel/sec - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec - return ..() - -/obj/structure/closet/secure_closet/security - name = "security officer's locker" - req_access = list(access_brig) - closet_appearance = /decl/closet_appearance/secure_closet/security - - starts_with = list( - /obj/item/clothing/suit/storage/vest/officer, - /obj/item/clothing/head/helmet, - /obj/item/weapon/cartridge/security, - /obj/item/device/radio/headset/headset_sec, - /obj/item/device/radio/headset/headset_sec/alt, - /obj/item/weapon/storage/belt/security, - /obj/item/device/flash, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/grenade/flashbang, - /obj/item/weapon/melee/baton/loaded, - /obj/item/clothing/glasses/sunglasses/sechud, - /obj/item/taperoll/police, - /obj/item/device/hailer, - /obj/item/device/flashlight/flare, - /obj/item/clothing/accessory/storage/black_vest, - /obj/item/clothing/head/soft/sec/corp, - /obj/item/clothing/under/rank/security/corp, - ///obj/item/ammo_magazine/m45/rubber, //VOREStation Removal, - /obj/item/weapon/gun/energy/taser, - /obj/item/weapon/cell/device/weapon, - /obj/item/clothing/suit/storage/hooded/wintercoat/security, - /obj/item/clothing/shoes/boots/winter/security, - /obj/item/device/flashlight/maglight) - -/obj/structure/closet/secure_closet/security/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/security - else - starts_with += /obj/item/weapon/storage/backpack/satchel/sec - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec - if(prob(30)) - starts_with += /obj/item/poster/nanotrasen - return ..() - -/obj/structure/closet/secure_closet/security/cargo/Initialize() - starts_with += /obj/item/clothing/accessory/armband/cargo - starts_with += /obj/item/device/encryptionkey/headset_cargo - return ..() - -/obj/structure/closet/secure_closet/security/engine/Initialize() - starts_with += /obj/item/clothing/accessory/armband/engine - starts_with += /obj/item/device/encryptionkey/headset_eng - return ..() - -/obj/structure/closet/secure_closet/security/science/Initialize() - starts_with += /obj/item/clothing/accessory/armband/science - starts_with += /obj/item/device/encryptionkey/headset_sci - return ..() - -/obj/structure/closet/secure_closet/security/med/Initialize() - starts_with += /obj/item/clothing/accessory/armband/medblue - starts_with += /obj/item/device/encryptionkey/headset_med - return ..() - - -/obj/structure/closet/secure_closet/detective - name = "detective's cabinet" - req_access = list(access_forensics_lockers) - closet_appearance = /decl/closet_appearance/cabinet/secure - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - - starts_with = list( - /obj/item/clothing/accessory/badge/holo/detective, - /obj/item/clothing/gloves/black, - ///obj/item/gunbox, // VOREStation Removal - /obj/item/gunbox/stun, - /obj/item/weapon/storage/belt/detective, - /obj/item/weapon/storage/box/evidence, - /obj/item/device/radio/headset/headset_sec, - /obj/item/device/radio/headset/headset_sec/alt, - /obj/item/clothing/suit/storage/vest/detective, - /obj/item/taperoll/police, - /obj/item/clothing/accessory/holster/armpit, - /obj/item/device/flashlight/maglight, - /obj/item/weapon/reagent_containers/food/drinks/flask/detflask, - /obj/item/weapon/storage/briefcase/crimekit, - /obj/item/device/taperecorder, - /obj/item/weapon/storage/bag/detective, - /obj/item/device/tape/random = 3) - -/obj/structure/closet/secure_closet/injection - name = "lethal injections locker" - req_access = list(access_captain) - closet_appearance = /decl/closet_appearance/secure_closet/courtroom - - starts_with = list( - /obj/item/weapon/reagent_containers/syringe/ld50_syringe/choral = 2) - -GLOBAL_LIST_BOILERPLATE(all_brig_closets, /obj/structure/closet/secure_closet/brig) - -/obj/structure/closet/secure_closet/brig - name = "brig locker" - req_access = list(access_brig) - closet_appearance = /decl/closet_appearance/secure_closet/brig - anchored = TRUE - var/id = null - - starts_with = list( - /obj/item/clothing/under/color/prison, - /obj/item/clothing/shoes/orange) - -/obj/structure/closet/secure_closet/posters - name = "morale storage" - req_access = list(access_security) - anchored = TRUE - - starts_with = list( - /obj/item/poster/nanotrasen, - /obj/item/poster/nanotrasen, - /obj/item/poster/nanotrasen, - /obj/item/poster/nanotrasen, - /obj/item/poster/nanotrasen) - -/obj/structure/closet/secure_closet/courtroom - name = "courtroom locker" - req_access = list(access_lawyer) - closet_appearance = /decl/closet_appearance/secure_closet/courtroom - - starts_with = list( - /obj/item/clothing/shoes/brown, - /obj/item/weapon/paper/Court = 3, - /obj/item/weapon/pen, - /obj/item/clothing/suit/judgerobe, - /obj/item/clothing/head/powdered_wig, - /obj/item/weapon/storage/briefcase) - - -/obj/structure/closet/secure_closet/wall - name = "wall locker" - req_access = list(access_security) - closet_appearance = /decl/closet_appearance/wall - density = TRUE - - //too small to put a man in - large = 0 +/obj/structure/closet/secure_closet/captains + name = "site manager's locker" + req_access = list(access_captain) + closet_appearance = /decl/closet_appearance/secure_closet/command + + starts_with = list( + /obj/item/weapon/storage/backpack/dufflebag/captain, + /obj/item/clothing/head/helmet, + /obj/item/clothing/suit/storage/vest, + /obj/item/weapon/cartridge/captain, + /obj/item/weapon/storage/lockbox/medal, + /obj/item/device/radio/headset/heads/captain, + /obj/item/device/radio/headset/heads/captain/alt, + /obj/item/weapon/gun/energy/gun, + /obj/item/weapon/melee/telebaton, + /obj/item/device/flash, + /obj/item/weapon/storage/box/ids) + + +/obj/structure/closet/secure_closet/hop + name = "head of personnel's locker" + req_access = list(access_hop) + closet_appearance = /decl/closet_appearance/secure_closet/command/hop + + starts_with = list( + /obj/item/clothing/suit/storage/vest, + /obj/item/clothing/head/helmet, + /obj/item/weapon/cartridge/hop, + /obj/item/device/radio/headset/heads/hop, + /obj/item/device/radio/headset/heads/hop/alt, + /obj/item/weapon/storage/box/ids = 2, + /obj/item/weapon/gun/energy/gun/compact, + /obj/item/weapon/storage/box/commandkeys, + /obj/item/weapon/storage/box/servicekeys, + /obj/item/device/flash) + +/obj/structure/closet/secure_closet/hop2 + name = "head of personnel's attire" + req_access = list(access_hop) + closet_appearance = /decl/closet_appearance/secure_closet/command/hop + + starts_with = list( + /obj/item/clothing/under/rank/head_of_personnel, + /obj/item/clothing/under/dress/dress_hop, + /obj/item/clothing/under/dress/dress_hr, + /obj/item/clothing/under/lawyer/female, + /obj/item/clothing/under/lawyer/black, + /obj/item/clothing/under/lawyer/black/skirt, + /obj/item/clothing/under/lawyer/red, + /obj/item/clothing/under/lawyer/red/skirt, + /obj/item/clothing/under/lawyer/oldman, + /obj/item/clothing/under/rank/neo_hop, + /obj/item/clothing/under/rank/neo_hop_skirt, + /obj/item/clothing/under/rank/neo_hop_parade_masc, + /obj/item/clothing/under/rank/neo_hop_parade_fem, + /obj/item/clothing/under/rank/neo_hop_turtle, + /obj/item/clothing/under/rank/neo_hop_turtle_skirt, + /obj/item/clothing/under/rank/neo_cmd_gorka, + /obj/item/clothing/suit/storage/toggle/labcoat/neo_hopformal, + /obj/item/clothing/suit/storage/toggle/labcoat/neo_civ_dep, + /obj/item/clothing/shoes/brown, + /obj/item/clothing/shoes/black, + /obj/item/clothing/shoes/laceup, + /obj/item/clothing/shoes/laceup/brown, + /obj/item/clothing/shoes/white, + /obj/item/clothing/under/rank/head_of_personnel_whimsy, + /obj/item/clothing/head/caphat/hop, + /obj/item/clothing/under/suit_jacket/teal, + /obj/item/clothing/under/suit_jacket/teal/skirt, + /obj/item/clothing/glasses/sunglasses, + /obj/item/clothing/suit/storage/hooded/wintercoat/hop, + /obj/item/clothing/head/caphat/hop/beret, + /obj/item/clothing/head/caphat/hop/beret/white) + + +/obj/structure/closet/secure_closet/hos + name = "head of security's locker" + req_access = list(access_hos) + storage_capacity = 2.6 * MOB_MEDIUM + closet_appearance = /decl/closet_appearance/secure_closet/security/hos + + starts_with = list( + /obj/item/clothing/head/helmet/HoS, + /obj/item/clothing/head/helmet/HoS/hat, + /obj/item/clothing/suit/storage/vest/hos, + /obj/item/clothing/under/rank/head_of_security/jensen, + /obj/item/clothing/under/rank/head_of_security/corp, + /obj/item/clothing/suit/storage/vest/hoscoat/jensen, + /obj/item/clothing/suit/storage/vest/hoscoat, + /obj/item/clothing/head/helmet/dermal, + /obj/item/weapon/cartridge/hos, + /obj/item/device/radio/headset/heads/hos, + /obj/item/device/radio/headset/heads/hos/alt, + /obj/item/clothing/glasses/sunglasses/sechud, + /obj/item/taperoll/police, + /obj/item/weapon/shield/riot, + /obj/item/weapon/shield/riot/tele, + /obj/item/weapon/storage/box/holobadge/hos, + /obj/item/clothing/accessory/badge/holo/hos, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/tool/crowbar/red, + /obj/item/weapon/storage/box/flashbangs, + /obj/item/weapon/storage/belt/security, + /obj/item/device/flash, + /obj/item/weapon/melee/baton/loaded, + /obj/item/weapon/gun/magnetic/railgun/heater/pistol/hos, + /obj/item/weapon/rcd_ammo/large, + /obj/item/weapon/cell/device/weapon, + /obj/item/clothing/accessory/holster/waist, + /obj/item/weapon/melee/telebaton, + /obj/item/clothing/head/beret/sec/corporate/hos, + /obj/item/clothing/suit/storage/hooded/wintercoat/security, + /obj/item/clothing/suit/storage/hooded/wintercoat/security/hos, + /obj/item/clothing/shoes/boots/winter/security, + /obj/item/device/flashlight/maglight, + /obj/item/clothing/mask/gas/half, + /obj/item/clothing/mask/gas/sechailer/swat/hos) + +/obj/structure/closet/secure_closet/hos/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/security + else + starts_with += /obj/item/weapon/storage/backpack/satchel/sec + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec + return ..() + + +/obj/structure/closet/secure_closet/warden + name = "warden's locker" + storage_capacity = 42 + req_access = list(access_armory) + closet_appearance = /decl/closet_appearance/secure_closet/security/warden + + starts_with = list( + /obj/item/clothing/suit/storage/vest/warden, + /obj/item/clothing/under/rank/warden, + /obj/item/clothing/under/rank/warden/corp, + /obj/item/clothing/suit/storage/vest/wardencoat, + /obj/item/clothing/suit/storage/vest/wardencoat/alt, + /obj/item/clothing/suit/storage/vest/wardencoat/alt2, //VOREStation Add, + /obj/item/clothing/head/helmet/dermal, + /obj/item/clothing/head/helmet/warden, + /obj/item/clothing/head/helmet/warden/hat, + /obj/item/clothing/under/rank/neo_warden_red, + /obj/item/clothing/under/rank/neo_warden_red_skirt, + /obj/item/clothing/under/rank/neo_warden_blue, + /obj/item/clothing/suit/storage/vest/wardencoat/neo_armsco_trench, + /obj/item/clothing/suit/storage/vest/wardencoat/neo_bluewarden, + /obj/item/clothing/suit/storage/vest/wardencoat/neo_warden_heavy, + /obj/item/clothing/under/rank/neo_sec_gorka, + /obj/item/weapon/cartridge/security, + /obj/item/device/radio/headset/headset_sec, + /obj/item/device/radio/headset/headset_sec/alt, + /obj/item/clothing/glasses/sunglasses/sechud, + /obj/item/taperoll/police, + /obj/item/clothing/accessory/badge/holo/warden, + /obj/item/weapon/storage/box/flashbangs, + /obj/item/weapon/storage/belt/security, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/melee/baton/loaded, + /obj/item/weapon/gun/energy/gun, + /obj/item/weapon/cell/device/weapon, + /obj/item/weapon/storage/box/holobadge, + /obj/item/clothing/head/beret/sec/corporate/warden, + /obj/item/clothing/suit/storage/hooded/wintercoat/security, + /obj/item/clothing/shoes/boots/winter/security, + /obj/item/device/flashlight/maglight, + /obj/item/device/megaphone, + /obj/item/clothing/mask/gas/half, + /obj/item/clothing/mask/gas/sechailer/swat/warden) + +/obj/structure/closet/secure_closet/warden/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/security + else + starts_with += /obj/item/weapon/storage/backpack/satchel/sec + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec + return ..() + +/obj/structure/closet/secure_closet/security + name = "security officer's locker" + req_access = list(access_brig) + closet_appearance = /decl/closet_appearance/secure_closet/security + + starts_with = list( + /obj/item/clothing/suit/storage/vest/officer, + /obj/item/clothing/head/helmet, + /obj/item/weapon/cartridge/security, + /obj/item/device/radio/headset/headset_sec, + /obj/item/device/radio/headset/headset_sec/alt, + /obj/item/weapon/storage/belt/security, + /obj/item/device/flash, + /obj/item/weapon/reagent_containers/spray/pepper, + /obj/item/weapon/grenade/flashbang, + /obj/item/weapon/melee/baton/loaded, + /obj/item/clothing/glasses/sunglasses/sechud, + /obj/item/taperoll/police, + /obj/item/device/hailer, + /obj/item/device/flashlight/flare, + /obj/item/clothing/accessory/storage/black_vest, + /obj/item/clothing/head/soft/sec/corp, + /obj/item/clothing/under/rank/security/corp, + ///obj/item/ammo_magazine/m45/rubber, //VOREStation Removal, + /obj/item/weapon/gun/energy/taser, + /obj/item/weapon/cell/device/weapon, + /obj/item/clothing/suit/storage/hooded/wintercoat/security, + /obj/item/clothing/shoes/boots/winter/security, + /obj/item/device/flashlight/maglight) + +/obj/structure/closet/secure_closet/security/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/security + else + starts_with += /obj/item/weapon/storage/backpack/satchel/sec + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec + if(prob(30)) + starts_with += /obj/item/poster/nanotrasen + return ..() + +/obj/structure/closet/secure_closet/security/cargo/Initialize() + starts_with += /obj/item/clothing/accessory/armband/cargo + starts_with += /obj/item/device/encryptionkey/headset_cargo + return ..() + +/obj/structure/closet/secure_closet/security/engine/Initialize() + starts_with += /obj/item/clothing/accessory/armband/engine + starts_with += /obj/item/device/encryptionkey/headset_eng + return ..() + +/obj/structure/closet/secure_closet/security/science/Initialize() + starts_with += /obj/item/clothing/accessory/armband/science + starts_with += /obj/item/device/encryptionkey/headset_sci + return ..() + +/obj/structure/closet/secure_closet/security/med/Initialize() + starts_with += /obj/item/clothing/accessory/armband/medblue + starts_with += /obj/item/device/encryptionkey/headset_med + return ..() + + +/obj/structure/closet/secure_closet/detective + name = "detective's cabinet" + req_access = list(access_forensics_lockers) + closet_appearance = /decl/closet_appearance/cabinet/secure + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + + starts_with = list( + /obj/item/clothing/accessory/badge/holo/detective, + /obj/item/clothing/gloves/black, + ///obj/item/gunbox, // VOREStation Removal + /obj/item/gunbox/stun, + /obj/item/weapon/storage/belt/detective, + /obj/item/weapon/storage/box/evidence, + /obj/item/device/radio/headset/headset_sec, + /obj/item/device/radio/headset/headset_sec/alt, + /obj/item/clothing/suit/storage/vest/detective, + /obj/item/taperoll/police, + /obj/item/clothing/accessory/holster/armpit, + /obj/item/device/flashlight/maglight, + /obj/item/weapon/reagent_containers/food/drinks/flask/detflask, + /obj/item/weapon/storage/briefcase/crimekit, + /obj/item/device/taperecorder, + /obj/item/weapon/storage/bag/detective, + /obj/item/device/tape/random = 3) + +/obj/structure/closet/secure_closet/injection + name = "lethal injections locker" + req_access = list(access_captain) + closet_appearance = /decl/closet_appearance/secure_closet/courtroom + + starts_with = list( + /obj/item/weapon/reagent_containers/syringe/ld50_syringe/choral = 2) + +GLOBAL_LIST_BOILERPLATE(all_brig_closets, /obj/structure/closet/secure_closet/brig) + +/obj/structure/closet/secure_closet/brig + name = "brig locker" + req_access = list(access_brig) + closet_appearance = /decl/closet_appearance/secure_closet/brig + anchored = TRUE + var/id = null + + starts_with = list( + /obj/item/clothing/under/color/prison, + /obj/item/clothing/shoes/orange) + +/obj/structure/closet/secure_closet/posters + name = "morale storage" + req_access = list(access_security) + anchored = TRUE + + starts_with = list( + /obj/item/poster/nanotrasen, + /obj/item/poster/nanotrasen, + /obj/item/poster/nanotrasen, + /obj/item/poster/nanotrasen, + /obj/item/poster/nanotrasen) + +/obj/structure/closet/secure_closet/courtroom + name = "courtroom locker" + req_access = list(access_lawyer) + closet_appearance = /decl/closet_appearance/secure_closet/courtroom + + starts_with = list( + /obj/item/clothing/shoes/brown, + /obj/item/weapon/paper/Court = 3, + /obj/item/weapon/pen, + /obj/item/clothing/suit/judgerobe, + /obj/item/clothing/head/powdered_wig, + /obj/item/weapon/storage/briefcase) + + +/obj/structure/closet/secure_closet/wall + name = "wall locker" + req_access = list(access_security) + closet_appearance = /decl/closet_appearance/wall + density = TRUE + + //too small to put a man in + large = 0 diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 9b66918be0d..7b290534561 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -1,132 +1,132 @@ -/obj/structure/closet/statue - name = "statue" - desc = "An incredibly lifelike marble carving" - icon = 'icons/obj/statue.dmi' - icon_state = "human_male" - density = TRUE - anchored = TRUE - health = 0 //destroying the statue kills the mob within - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - var/intialTox = 0 //these are here to keep the mob from taking damage from things that logically wouldn't affect a rock - var/intialFire = 0 //it's a little sloppy I know but it was this or the GODMODE flag. Lesser of two evils. - var/intialBrute = 0 - var/intialOxy = 0 - var/timer = 240 //eventually the person will be freed - -/obj/structure/closet/statue/New(loc, var/mob/living/L) - if(L && (ishuman(L) || L.isMonkey() || iscorgi(L))) - if(L.buckled) - L.buckled = 0 - L.anchored = FALSE - if(L.client) - L.client.perspective = EYE_PERSPECTIVE - L.client.eye = src - L.loc = src - L.sdisabilities |= MUTE - health = L.health + 100 //stoning damaged mobs will result in easier to shatter statues - intialTox = L.getToxLoss() - intialFire = L.getFireLoss() - intialBrute = L.getBruteLoss() - intialOxy = L.getOxyLoss() - if(ishuman(L)) - name = "statue of [L.name]" - if(L.gender == "female") - icon_state = "human_female" - else if(L.isMonkey()) - name = "statue of a monkey" - icon_state = "monkey" - else if(iscorgi(L)) - name = "statue of a corgi" - icon_state = "corgi" - desc = "If it takes forever, I will wait for you..." - - if(health == 0) //meaning if the statue didn't find a valid target - qdel(src) - return - - START_PROCESSING(SSobj, src) - ..() - -/obj/structure/closet/statue/process() - timer-- - for(var/mob/living/M in src) //Go-go gadget stasis field - M.setToxLoss(intialTox) - M.adjustFireLoss(intialFire - M.getFireLoss()) - M.adjustBruteLoss(intialBrute - M.getBruteLoss()) - M.setOxyLoss(intialOxy) - if (timer <= 0) - dump_contents() - STOP_PROCESSING(SSobj, src) - qdel(src) - -/obj/structure/closet/statue/dump_contents() - - for(var/obj/O in src) - O.loc = src.loc - - for(var/mob/living/M in src) - M.loc = src.loc - M.sdisabilities &= ~MUTE - M.take_overall_damage((M.health - health - 100),0) //any new damage the statue incurred is transfered to the mob - if(M.client) - M.client.eye = M.client.mob - M.client.perspective = MOB_PERSPECTIVE - -/obj/structure/closet/statue/open() - return - -/obj/structure/closet/statue/close() - return - -/obj/structure/closet/statue/toggle() - return - -/obj/structure/closet/statue/proc/check_health() - if(health <= 0) - for(var/mob/M in src) - shatter(M) - -/obj/structure/closet/statue/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.get_structure_damage() - check_health() - - return - -/obj/structure/closet/statue/attack_generic(var/mob/user, damage, attacktext, environment_smash) - if(damage && environment_smash) - for(var/mob/M in src) - shatter(M) - -/obj/structure/closet/statue/ex_act(severity) - for(var/mob/M in src) - M.ex_act(severity) - health -= 60 / severity - check_health() - -/obj/structure/closet/statue/attackby(obj/item/I as obj, mob/user as mob) - health -= I.force - user.do_attack_animation(src) - visible_message("[user] strikes [src] with [I].") - check_health() - -/obj/structure/closet/statue/MouseDrop_T() - return - -/obj/structure/closet/statue/relaymove() - return - -/obj/structure/closet/statue/attack_hand() - return - -/obj/structure/closet/statue/verb_toggleopen() - return - -/obj/structure/closet/statue/update_icon() - return - -/obj/structure/closet/statue/proc/shatter(mob/user as mob) - if (user) - user.dust() - dump_contents() - visible_message("[src] shatters!.") - qdel(src) +/obj/structure/closet/statue + name = "statue" + desc = "An incredibly lifelike marble carving" + icon = 'icons/obj/statue.dmi' + icon_state = "human_male" + density = TRUE + anchored = TRUE + health = 0 //destroying the statue kills the mob within + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + var/intialTox = 0 //these are here to keep the mob from taking damage from things that logically wouldn't affect a rock + var/intialFire = 0 //it's a little sloppy I know but it was this or the GODMODE flag. Lesser of two evils. + var/intialBrute = 0 + var/intialOxy = 0 + var/timer = 240 //eventually the person will be freed + +/obj/structure/closet/statue/New(loc, var/mob/living/L) + if(L && (ishuman(L) || L.isMonkey() || iscorgi(L))) + if(L.buckled) + L.buckled = 0 + L.anchored = FALSE + if(L.client) + L.client.perspective = EYE_PERSPECTIVE + L.client.eye = src + L.loc = src + L.sdisabilities |= MUTE + health = L.health + 100 //stoning damaged mobs will result in easier to shatter statues + intialTox = L.getToxLoss() + intialFire = L.getFireLoss() + intialBrute = L.getBruteLoss() + intialOxy = L.getOxyLoss() + if(ishuman(L)) + name = "statue of [L.name]" + if(L.gender == "female") + icon_state = "human_female" + else if(L.isMonkey()) + name = "statue of a monkey" + icon_state = "monkey" + else if(iscorgi(L)) + name = "statue of a corgi" + icon_state = "corgi" + desc = "If it takes forever, I will wait for you..." + + if(health == 0) //meaning if the statue didn't find a valid target + qdel(src) + return + + START_PROCESSING(SSobj, src) + ..() + +/obj/structure/closet/statue/process() + timer-- + for(var/mob/living/M in src) //Go-go gadget stasis field + M.setToxLoss(intialTox) + M.adjustFireLoss(intialFire - M.getFireLoss()) + M.adjustBruteLoss(intialBrute - M.getBruteLoss()) + M.setOxyLoss(intialOxy) + if (timer <= 0) + dump_contents() + STOP_PROCESSING(SSobj, src) + qdel(src) + +/obj/structure/closet/statue/dump_contents() + + for(var/obj/O in src) + O.loc = src.loc + + for(var/mob/living/M in src) + M.loc = src.loc + M.sdisabilities &= ~MUTE + M.take_overall_damage((M.health - health - 100),0) //any new damage the statue incurred is transfered to the mob + if(M.client) + M.client.eye = M.client.mob + M.client.perspective = MOB_PERSPECTIVE + +/obj/structure/closet/statue/open() + return + +/obj/structure/closet/statue/close() + return + +/obj/structure/closet/statue/toggle() + return + +/obj/structure/closet/statue/proc/check_health() + if(health <= 0) + for(var/mob/M in src) + shatter(M) + +/obj/structure/closet/statue/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.get_structure_damage() + check_health() + + return + +/obj/structure/closet/statue/attack_generic(var/mob/user, damage, attacktext, environment_smash) + if(damage && environment_smash) + for(var/mob/M in src) + shatter(M) + +/obj/structure/closet/statue/ex_act(severity) + for(var/mob/M in src) + M.ex_act(severity) + health -= 60 / severity + check_health() + +/obj/structure/closet/statue/attackby(obj/item/I as obj, mob/user as mob) + health -= I.force + user.do_attack_animation(src) + visible_message("[user] strikes [src] with [I].") + check_health() + +/obj/structure/closet/statue/MouseDrop_T() + return + +/obj/structure/closet/statue/relaymove() + return + +/obj/structure/closet/statue/attack_hand() + return + +/obj/structure/closet/statue/verb_toggleopen() + return + +/obj/structure/closet/statue/update_icon() + return + +/obj/structure/closet/statue/proc/shatter(mob/user as mob) + if (user) + user.dust() + dump_contents() + visible_message("[src] shatters!.") + qdel(src) diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index 824da68846f..51d15feb1bf 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -1,121 +1,121 @@ -/obj/structure/closet/syndicate - name = "armory closet" - desc = "Why is this here?" - closet_appearance = /decl/closet_appearance/tactical/alt - -/obj/structure/closet/syndicate/personal - desc = "It's a storage unit for operative gear." - - starts_with = list( - /obj/item/weapon/tank/jetpack/oxygen, - /obj/item/clothing/mask/gas/syndicate, - /obj/item/clothing/under/syndicate, - /obj/item/clothing/head/helmet/space/void/merc, - /obj/item/clothing/suit/space/void/merc, - /obj/item/weapon/tool/crowbar/red, - /obj/item/weapon/cell/high, - /obj/item/weapon/card/id/syndicate, - /obj/item/device/multitool, - /obj/item/weapon/shield/energy, - /obj/item/clothing/shoes/magboots) - - -/obj/structure/closet/syndicate/suit - desc = "It's a storage unit for voidsuits." - - starts_with = list( - /obj/item/weapon/tank/jetpack/oxygen, - /obj/item/clothing/shoes/magboots, - /obj/item/clothing/suit/space/void/merc, - /obj/item/clothing/mask/gas/syndicate, - /obj/item/clothing/head/helmet/space/void/merc) - - -/obj/structure/closet/syndicate/nuclear - desc = "It's a storage unit for nuclear-operative gear." - - starts_with = list( - /obj/item/ammo_magazine/m10mm = 5, - /obj/item/weapon/storage/box/handcuffs, - /obj/item/weapon/storage/box/flashbangs, - /obj/item/weapon/gun/energy/gun = 5, - /obj/item/weapon/pinpointer/nukeop = 5, - /obj/item/device/pda/syndicate, - /obj/item/device/radio/uplink) - -/obj/structure/closet/syndicate/resources - desc = "An old, dusty locker." - -/obj/structure/closet/syndicate/resources/Initialize() - . = ..() - if(!contents.len) - var/common_min = 30 //Minimum amount of minerals in the stack for common minerals - var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals - var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals - var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK - - var/pickednum = rand(1, 50) - - //Sad trombone - if(pickednum == 1) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(src) - P.name = "IOU" - P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" - - //Metal (common ore) - if(pickednum >= 2) - new /obj/item/stack/material/steel(src, rand(common_min, common_max)) - - //Glass (common ore) - if(pickednum >= 5) - new /obj/item/stack/material/glass(src, rand(common_min, common_max)) - - //Plasteel (common ore) Because it has a million more uses then phoron - if(pickednum >= 10) - new /obj/item/stack/material/plasteel(src, rand(common_min, common_max)) - - //Phoron (rare ore) - if(pickednum >= 15) - new /obj/item/stack/material/phoron(src, rand(rare_min, rare_max)) - - //Silver (rare ore) - if(pickednum >= 20) - new /obj/item/stack/material/silver(src, rand(rare_min, rare_max)) - - //Gold (rare ore) - if(pickednum >= 30) - new /obj/item/stack/material/gold(src, rand(rare_min, rare_max)) - - //Uranium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/material/uranium(src, rand(rare_min, rare_max)) - - //Diamond (rare HONK) - if(pickednum >= 45) - new /obj/item/stack/material/diamond(src, rand(rare_min, rare_max)) - - //Jetpack (You hit the jackpot!) - if(pickednum == 50) - new /obj/item/weapon/tank/jetpack/carbondioxide(src) - -/obj/structure/closet/syndicate/resources/everything - desc = "It's an emergency storage closet for repairs." - -/obj/structure/closet/syndicate/resources/everything/Initialize() - var/list/resources = list( - /obj/item/stack/material/steel, - /obj/item/stack/material/glass, - /obj/item/stack/material/gold, - /obj/item/stack/material/silver, - /obj/item/stack/material/phoron, - /obj/item/stack/material/uranium, - /obj/item/stack/material/diamond, - /obj/item/stack/material/plasteel, - /obj/item/stack/rods - ) - - for(var/i = 0, i<2, i++) - for(var/res in resources) - new res(src, -1) - - return ..() +/obj/structure/closet/syndicate + name = "armory closet" + desc = "Why is this here?" + closet_appearance = /decl/closet_appearance/tactical/alt + +/obj/structure/closet/syndicate/personal + desc = "It's a storage unit for operative gear." + + starts_with = list( + /obj/item/weapon/tank/jetpack/oxygen, + /obj/item/clothing/mask/gas/syndicate, + /obj/item/clothing/under/syndicate, + /obj/item/clothing/head/helmet/space/void/merc, + /obj/item/clothing/suit/space/void/merc, + /obj/item/weapon/tool/crowbar/red, + /obj/item/weapon/cell/high, + /obj/item/weapon/card/id/syndicate, + /obj/item/device/multitool, + /obj/item/weapon/shield/energy, + /obj/item/clothing/shoes/magboots) + + +/obj/structure/closet/syndicate/suit + desc = "It's a storage unit for voidsuits." + + starts_with = list( + /obj/item/weapon/tank/jetpack/oxygen, + /obj/item/clothing/shoes/magboots, + /obj/item/clothing/suit/space/void/merc, + /obj/item/clothing/mask/gas/syndicate, + /obj/item/clothing/head/helmet/space/void/merc) + + +/obj/structure/closet/syndicate/nuclear + desc = "It's a storage unit for nuclear-operative gear." + + starts_with = list( + /obj/item/ammo_magazine/m10mm = 5, + /obj/item/weapon/storage/box/handcuffs, + /obj/item/weapon/storage/box/flashbangs, + /obj/item/weapon/gun/energy/gun = 5, + /obj/item/weapon/pinpointer/nukeop = 5, + /obj/item/device/pda/syndicate, + /obj/item/device/radio/uplink) + +/obj/structure/closet/syndicate/resources + desc = "An old, dusty locker." + +/obj/structure/closet/syndicate/resources/Initialize() + . = ..() + if(!contents.len) + var/common_min = 30 //Minimum amount of minerals in the stack for common minerals + var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals + var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals + var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK + + var/pickednum = rand(1, 50) + + //Sad trombone + if(pickednum == 1) + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(src) + P.name = "IOU" + P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" + + //Metal (common ore) + if(pickednum >= 2) + new /obj/item/stack/material/steel(src, rand(common_min, common_max)) + + //Glass (common ore) + if(pickednum >= 5) + new /obj/item/stack/material/glass(src, rand(common_min, common_max)) + + //Plasteel (common ore) Because it has a million more uses then phoron + if(pickednum >= 10) + new /obj/item/stack/material/plasteel(src, rand(common_min, common_max)) + + //Phoron (rare ore) + if(pickednum >= 15) + new /obj/item/stack/material/phoron(src, rand(rare_min, rare_max)) + + //Silver (rare ore) + if(pickednum >= 20) + new /obj/item/stack/material/silver(src, rand(rare_min, rare_max)) + + //Gold (rare ore) + if(pickednum >= 30) + new /obj/item/stack/material/gold(src, rand(rare_min, rare_max)) + + //Uranium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/material/uranium(src, rand(rare_min, rare_max)) + + //Diamond (rare HONK) + if(pickednum >= 45) + new /obj/item/stack/material/diamond(src, rand(rare_min, rare_max)) + + //Jetpack (You hit the jackpot!) + if(pickednum == 50) + new /obj/item/weapon/tank/jetpack/carbondioxide(src) + +/obj/structure/closet/syndicate/resources/everything + desc = "It's an emergency storage closet for repairs." + +/obj/structure/closet/syndicate/resources/everything/Initialize() + var/list/resources = list( + /obj/item/stack/material/steel, + /obj/item/stack/material/glass, + /obj/item/stack/material/gold, + /obj/item/stack/material/silver, + /obj/item/stack/material/phoron, + /obj/item/stack/material/uranium, + /obj/item/stack/material/diamond, + /obj/item/stack/material/plasteel, + /obj/item/stack/rods + ) + + for(var/i = 0, i<2, i++) + for(var/res in resources) + new res(src, -1) + + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm index 8fc75b1c8a6..7946fd1e942 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm @@ -1,229 +1,229 @@ -/* Utility Closets - * Contains: - * Emergency Closet - * Fire Closet - * Tool Closet - * Radiation Closet - * Bombsuit Closet - * Hydrant - * First Aid - */ - -/* - * Emergency Closet - */ -/obj/structure/closet/emcloset - name = "emergency closet" - desc = "It's a storage unit for emergency breathmasks and O2 tanks." - closet_appearance = /decl/closet_appearance/oxygen - -/obj/structure/closet/emcloset/Initialize() - switch (pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10))) - //VOREStation Block Edit Start - Modified List - if ("small") - starts_with = list( - /obj/item/weapon/tank/emergency/oxygen = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/suit/space/emergency = 2, - /obj/item/clothing/head/helmet/space/emergency = 2) - if ("aid") - starts_with = list( - /obj/item/weapon/tank/emergency/oxygen, - /obj/item/weapon/storage/toolbox/emergency, - /obj/item/clothing/mask/breath, - /obj/item/clothing/suit/space/emergency, - /obj/item/clothing/head/helmet/space/emergency) - if ("tank") - starts_with = list( - /obj/item/weapon/tank/emergency/oxygen/engi = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/suit/space/emergency = 2, - /obj/item/clothing/head/helmet/space/emergency = 2) - if ("both") - starts_with = list( - /obj/item/weapon/storage/toolbox/emergency, - /obj/item/weapon/storage/firstaid/o2, - /obj/item/weapon/tank/emergency/oxygen/engi = 2, - /obj/item/clothing/mask/breath = 2, - /obj/item/clothing/suit/space/emergency = 2, - /obj/item/clothing/head/helmet/space/emergency = 2) - //VOREStation Block Edit End - - return ..() - -/obj/structure/closet/emcloset/legacy - starts_with = list( - /obj/item/weapon/tank/oxygen, - /obj/item/clothing/mask/gas) - -/* - * Fire Closet - */ -/obj/structure/closet/firecloset - name = "fire-safety closet" - desc = "It's a storage unit for fire-fighting supplies." - closet_appearance = /decl/closet_appearance/oxygen/fire - - starts_with = list( - /obj/item/clothing/suit/fire, - /obj/item/clothing/mask/gas, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red) - -/obj/structure/closet/firecloset/full - starts_with = list( - /obj/item/clothing/suit/fire, - /obj/item/clothing/mask/gas, - /obj/item/device/flashlight, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red) - -/obj/structure/closet/firecloset/full/double - starts_with = list( - /obj/item/clothing/suit/fire = 2, - /obj/item/clothing/mask/gas = 2, - /obj/item/device/flashlight = 2, - /obj/item/weapon/tank/oxygen/red = 2, - /obj/item/weapon/extinguisher = 2, - /obj/item/clothing/head/hardhat/red = 2) - -/obj/structure/closet/firecloset/full/atmos - name = "atmos fire-safety closet" - desc = "It's a storage unit for atmospheric fire-fighting supplies." - closet_appearance = /decl/closet_appearance/oxygen/fire/atmos - - starts_with = list( - /obj/item/clothing/suit/fire/heavy, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher/atmo, - /obj/item/device/flashlight, - /obj/item/clothing/head/hardhat/firefighter/atmos) - -/* - * Tool Closet - */ -/obj/structure/closet/toolcloset - name = "tool closet" - desc = "It's a storage unit for tools." - closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools - -/obj/structure/closet/toolcloset/Initialize() - starts_with = list() - if(prob(40)) - starts_with += /obj/item/clothing/suit/storage/hazardvest - if(prob(70)) - starts_with += /obj/item/device/flashlight - if(prob(70)) - starts_with += /obj/item/weapon/tool/screwdriver - if(prob(70)) - starts_with += /obj/item/weapon/tool/wrench - if(prob(70)) - starts_with += /obj/item/weapon/weldingtool - if(prob(70)) - starts_with += /obj/item/weapon/tool/crowbar - if(prob(70)) - starts_with += /obj/item/weapon/tool/wirecutters - if(prob(70)) - starts_with += /obj/item/device/t_scanner - if(prob(20)) - starts_with += /obj/item/weapon/storage/belt/utility - if(prob(30)) - starts_with += /obj/item/stack/cable_coil/random - if(prob(30)) - starts_with += /obj/item/stack/cable_coil/random - if(prob(30)) - starts_with += /obj/item/stack/cable_coil/random - if(prob(20)) - starts_with += /obj/item/device/multitool - if(prob(5)) - starts_with += /obj/item/clothing/gloves/yellow - if(prob(40)) - starts_with += /obj/item/clothing/head/hardhat - if(prob(30)) - starts_with += /obj/item/weapon/reagent_containers/spray/windowsealant //VOREStation Add - return ..() - -/* - * Radiation Closet - */ -/obj/structure/closet/radiation - name = "radiation suit closet" - desc = "It's a storage unit for rad-protective suits." - closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/radiation - - starts_with = list( - /obj/item/clothing/suit/radiation = 2, - /obj/item/clothing/head/radiation = 2, - /obj/item/device/geiger = 2) - -/* - * Bombsuit closet - */ -/obj/structure/closet/bombcloset - name = "\improper EOD closet" - desc = "It's a storage unit for explosion-protective suits." - closet_appearance = /decl/closet_appearance/bomb - - starts_with = list( - /obj/item/clothing/suit/bomb_suit, - /obj/item/clothing/under/color/black, - /obj/item/clothing/shoes/black, - /obj/item/clothing/head/bomb_hood) - -/obj/structure/closet/bombcloset/double - starts_with = list( - /obj/item/clothing/suit/bomb_suit = 2, - /obj/item/clothing/under/color/black = 2, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/head/bomb_hood = 2) - -/obj/structure/closet/bombclosetsecurity - name = "\improper EOD closet" - desc = "It's a storage unit for explosion-protective suits." - closet_appearance = /decl/closet_appearance/bomb/security - - starts_with = list( - /obj/item/clothing/suit/bomb_suit/security, - /obj/item/clothing/under/rank/security, - /obj/item/clothing/shoes/brown, - /obj/item/clothing/head/bomb_hood/security) - -/* - * Hydrant - */ -/obj/structure/closet/hydrant //wall mounted fire closet - name = "fire-safety closet" - desc = "It's a storage unit for fire-fighting supplies." - icon = 'icons/obj/closets/bases/wall.dmi' - closet_appearance = /decl/closet_appearance/wall/hydrant - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - anchored = TRUE - density = FALSE - wall_mounted = 1 - store_mobs = 0 - - starts_with = list( - /obj/item/clothing/suit/fire/firefighter, - /obj/item/clothing/mask/gas, - /obj/item/device/flashlight, - /obj/item/weapon/tank/oxygen/red, - /obj/item/weapon/extinguisher, - /obj/item/clothing/head/hardhat/red) - -/* - * First Aid - */ -/obj/structure/closet/medical_wall //wall mounted medical closet - name = "first-aid closet" - desc = "It's wall-mounted storage unit for first aid supplies." - icon = 'icons/obj/closets/bases/wall.dmi' - closet_appearance = /decl/closet_appearance/wall/medical - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - anchored = TRUE - density = FALSE - wall_mounted = 1 - store_mobs = 0 +/* Utility Closets + * Contains: + * Emergency Closet + * Fire Closet + * Tool Closet + * Radiation Closet + * Bombsuit Closet + * Hydrant + * First Aid + */ + +/* + * Emergency Closet + */ +/obj/structure/closet/emcloset + name = "emergency closet" + desc = "It's a storage unit for emergency breathmasks and O2 tanks." + closet_appearance = /decl/closet_appearance/oxygen + +/obj/structure/closet/emcloset/Initialize() + switch (pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10))) + //VOREStation Block Edit Start - Modified List + if ("small") + starts_with = list( + /obj/item/weapon/tank/emergency/oxygen = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/suit/space/emergency = 2, + /obj/item/clothing/head/helmet/space/emergency = 2) + if ("aid") + starts_with = list( + /obj/item/weapon/tank/emergency/oxygen, + /obj/item/weapon/storage/toolbox/emergency, + /obj/item/clothing/mask/breath, + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/head/helmet/space/emergency) + if ("tank") + starts_with = list( + /obj/item/weapon/tank/emergency/oxygen/engi = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/suit/space/emergency = 2, + /obj/item/clothing/head/helmet/space/emergency = 2) + if ("both") + starts_with = list( + /obj/item/weapon/storage/toolbox/emergency, + /obj/item/weapon/storage/firstaid/o2, + /obj/item/weapon/tank/emergency/oxygen/engi = 2, + /obj/item/clothing/mask/breath = 2, + /obj/item/clothing/suit/space/emergency = 2, + /obj/item/clothing/head/helmet/space/emergency = 2) + //VOREStation Block Edit End + + return ..() + +/obj/structure/closet/emcloset/legacy + starts_with = list( + /obj/item/weapon/tank/oxygen, + /obj/item/clothing/mask/gas) + +/* + * Fire Closet + */ +/obj/structure/closet/firecloset + name = "fire-safety closet" + desc = "It's a storage unit for fire-fighting supplies." + closet_appearance = /decl/closet_appearance/oxygen/fire + + starts_with = list( + /obj/item/clothing/suit/fire, + /obj/item/clothing/mask/gas, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red) + +/obj/structure/closet/firecloset/full + starts_with = list( + /obj/item/clothing/suit/fire, + /obj/item/clothing/mask/gas, + /obj/item/device/flashlight, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red) + +/obj/structure/closet/firecloset/full/double + starts_with = list( + /obj/item/clothing/suit/fire = 2, + /obj/item/clothing/mask/gas = 2, + /obj/item/device/flashlight = 2, + /obj/item/weapon/tank/oxygen/red = 2, + /obj/item/weapon/extinguisher = 2, + /obj/item/clothing/head/hardhat/red = 2) + +/obj/structure/closet/firecloset/full/atmos + name = "atmos fire-safety closet" + desc = "It's a storage unit for atmospheric fire-fighting supplies." + closet_appearance = /decl/closet_appearance/oxygen/fire/atmos + + starts_with = list( + /obj/item/clothing/suit/fire/heavy, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher/atmo, + /obj/item/device/flashlight, + /obj/item/clothing/head/hardhat/firefighter/atmos) + +/* + * Tool Closet + */ +/obj/structure/closet/toolcloset + name = "tool closet" + desc = "It's a storage unit for tools." + closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools + +/obj/structure/closet/toolcloset/Initialize() + starts_with = list() + if(prob(40)) + starts_with += /obj/item/clothing/suit/storage/hazardvest + if(prob(70)) + starts_with += /obj/item/device/flashlight + if(prob(70)) + starts_with += /obj/item/weapon/tool/screwdriver + if(prob(70)) + starts_with += /obj/item/weapon/tool/wrench + if(prob(70)) + starts_with += /obj/item/weapon/weldingtool + if(prob(70)) + starts_with += /obj/item/weapon/tool/crowbar + if(prob(70)) + starts_with += /obj/item/weapon/tool/wirecutters + if(prob(70)) + starts_with += /obj/item/device/t_scanner + if(prob(20)) + starts_with += /obj/item/weapon/storage/belt/utility + if(prob(30)) + starts_with += /obj/item/stack/cable_coil/random + if(prob(30)) + starts_with += /obj/item/stack/cable_coil/random + if(prob(30)) + starts_with += /obj/item/stack/cable_coil/random + if(prob(20)) + starts_with += /obj/item/device/multitool + if(prob(5)) + starts_with += /obj/item/clothing/gloves/yellow + if(prob(40)) + starts_with += /obj/item/clothing/head/hardhat + if(prob(30)) + starts_with += /obj/item/weapon/reagent_containers/spray/windowsealant //VOREStation Add + return ..() + +/* + * Radiation Closet + */ +/obj/structure/closet/radiation + name = "radiation suit closet" + desc = "It's a storage unit for rad-protective suits." + closet_appearance = /decl/closet_appearance/secure_closet/engineering/tools/radiation + + starts_with = list( + /obj/item/clothing/suit/radiation = 2, + /obj/item/clothing/head/radiation = 2, + /obj/item/device/geiger = 2) + +/* + * Bombsuit closet + */ +/obj/structure/closet/bombcloset + name = "\improper EOD closet" + desc = "It's a storage unit for explosion-protective suits." + closet_appearance = /decl/closet_appearance/bomb + + starts_with = list( + /obj/item/clothing/suit/bomb_suit, + /obj/item/clothing/under/color/black, + /obj/item/clothing/shoes/black, + /obj/item/clothing/head/bomb_hood) + +/obj/structure/closet/bombcloset/double + starts_with = list( + /obj/item/clothing/suit/bomb_suit = 2, + /obj/item/clothing/under/color/black = 2, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/head/bomb_hood = 2) + +/obj/structure/closet/bombclosetsecurity + name = "\improper EOD closet" + desc = "It's a storage unit for explosion-protective suits." + closet_appearance = /decl/closet_appearance/bomb/security + + starts_with = list( + /obj/item/clothing/suit/bomb_suit/security, + /obj/item/clothing/under/rank/security, + /obj/item/clothing/shoes/brown, + /obj/item/clothing/head/bomb_hood/security) + +/* + * Hydrant + */ +/obj/structure/closet/hydrant //wall mounted fire closet + name = "fire-safety closet" + desc = "It's a storage unit for fire-fighting supplies." + icon = 'icons/obj/closets/bases/wall.dmi' + closet_appearance = /decl/closet_appearance/wall/hydrant + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + anchored = TRUE + density = FALSE + wall_mounted = 1 + store_mobs = 0 + + starts_with = list( + /obj/item/clothing/suit/fire/firefighter, + /obj/item/clothing/mask/gas, + /obj/item/device/flashlight, + /obj/item/weapon/tank/oxygen/red, + /obj/item/weapon/extinguisher, + /obj/item/clothing/head/hardhat/red) + +/* + * First Aid + */ +/obj/structure/closet/medical_wall //wall mounted medical closet + name = "first-aid closet" + desc = "It's wall-mounted storage unit for first aid supplies." + icon = 'icons/obj/closets/bases/wall.dmi' + closet_appearance = /decl/closet_appearance/wall/medical + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + anchored = TRUE + density = FALSE + wall_mounted = 1 + store_mobs = 0 diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm index c126b965f3a..1f848762226 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets_vr.dm @@ -1,7 +1,7 @@ -/obj/structure/closet/firecloset/Initialize() - starts_with += /obj/item/weapon/storage/toolbox/emergency - return ..() - -/obj/structure/closet/hydrant/New() - starts_with += /obj/item/weapon/storage/toolbox/emergency - return ..() +/obj/structure/closet/firecloset/Initialize() + starts_with += /obj/item/weapon/storage/toolbox/emergency + return ..() + +/obj/structure/closet/hydrant/New() + starts_with += /obj/item/weapon/storage/toolbox/emergency + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/walllocker.dm b/code/game/objects/structures/crates_lockers/closets/walllocker.dm index 6be9205ef34..0693503fb61 100644 --- a/code/game/objects/structures/crates_lockers/closets/walllocker.dm +++ b/code/game/objects/structures/crates_lockers/closets/walllocker.dm @@ -80,6 +80,27 @@ /obj/structure/closet/walllocker/medical/east pixel_x = 32 dir = EAST + +/obj/structure/closet/walllocker/wooden + name = "wooden cabinet" + desc = "A wall mounted storage cabinet." + closet_appearance = /decl/closet_appearance/wall_double/wooden + +/obj/structure/closet/walllocker/wooden/north + pixel_y = 32 + dir = SOUTH + +/obj/structure/closet/walllocker/wooden/south + pixel_y = -32 + dir = NORTH + +/obj/structure/closet/walllocker/wooden/west + pixel_x = -32 + dir = WEST + +/obj/structure/closet/walllocker/wooden/east + pixel_x = 32 + dir = EAST //VOREStation Add End //double-size "cabinet" lockers, from Killian diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index ac03b1c209f..64c2fda73d4 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -1,484 +1,484 @@ -/obj/structure/closet/wardrobe - name = "wardrobe" - desc = "It's a storage unit for standard-issue attire." - closet_appearance = /decl/closet_appearance/wardrobe - -/obj/structure/closet/wardrobe/red - name = "security wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/red - - starts_with = list( - /obj/item/clothing/under/rank/security = 3, - /obj/item/clothing/under/rank/security2 = 3, - /obj/item/clothing/under/rank/security/turtleneck = 3, - /obj/item/clothing/under/rank/security/skirt = 2, - /obj/item/clothing/shoes/boots/jackboots = 3, - /obj/item/clothing/head/soft/sec = 3, - /obj/item/clothing/head/beret/sec = 3, - /obj/item/clothing/head/beret/sec/corporate/officer = 3, - /obj/item/clothing/mask/bandana/red = 3, - /obj/item/clothing/suit/storage/hooded/wintercoat/security = 3, - /obj/item/clothing/accessory/armband = 3, - /obj/item/clothing/accessory/holster/waist = 3) - -/obj/structure/closet/wardrobe/red/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/security - else - starts_with += /obj/item/weapon/storage/backpack/satchel/sec - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/security - else - starts_with += /obj/item/weapon/storage/backpack/satchel/sec - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/security - else - starts_with += /obj/item/weapon/storage/backpack/satchel/sec - - return ..() - -/obj/structure/closet/wardrobe/detective - name = "detective wardrobe" - closet_appearance = /decl/closet_appearance/cabinet - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - - starts_with = list( - /obj/item/clothing/head/det = 2, - /obj/item/clothing/head/det/grey = 2, - /obj/item/clothing/shoes/brown = 2, - /obj/item/clothing/shoes/laceup = 2, - /obj/item/clothing/under/det = 2, - /obj/item/clothing/under/det/waistcoat = 2, - /obj/item/clothing/under/det/grey = 2, - /obj/item/clothing/under/det/grey/waistcoat = 2, - /obj/item/clothing/under/det/black = 2, - /obj/item/clothing/under/det/skirt, - /obj/item/clothing/under/det/corporate = 2, - /obj/item/clothing/suit/storage/det_trench = 2, - /obj/item/clothing/suit/storage/det_trench/grey = 2, - /obj/item/clothing/suit/storage/forensics/blue = 2, - /obj/item/clothing/suit/storage/forensics/red = 2) - -/obj/structure/closet/wardrobe/pink - name = "pink wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/pink - - starts_with = list( - /obj/item/clothing/under/color/pink = 3, - /obj/item/clothing/shoes/brown = 3) - -/obj/structure/closet/wardrobe/black - name = "black wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/black - - starts_with = list( - /obj/item/clothing/under/color/black = 3, - /obj/item/clothing/shoes/black = 3, - /obj/item/clothing/head/that = 3, - /obj/item/clothing/head/soft/black = 3, - /obj/item/clothing/mask/bandana = 3, - /obj/item/weapon/storage/backpack/messenger/black) - - -/obj/structure/closet/wardrobe/chaplain_black - name = "chapel wardrobe" - desc = "It's a storage unit for approved religious attire." - closet_appearance = /decl/closet_appearance/wardrobe/chapel - - starts_with = list( - /obj/item/clothing/under/rank/chaplain, - /obj/item/clothing/shoes/black, - /obj/item/clothing/suit/nun, - /obj/item/clothing/head/nun_hood, - /obj/item/clothing/suit/storage/hooded/chaplain_hoodie, - /obj/item/clothing/suit/storage/hooded/chaplain_hoodie/whiteout, - /obj/item/clothing/suit/holidaypriest, - /obj/item/clothing/under/wedding/bride_white, - /obj/item/weapon/storage/backpack/cultpack, - /obj/item/weapon/storage/fancy/candle_box = 2, - /obj/item/weapon/storage/fancy/whitecandle_box, - /obj/item/weapon/storage/fancy/blackcandle_box, - /obj/item/godfig = 2, - /obj/item/weapon/deck/tarot) - - -/obj/structure/closet/wardrobe/green - name = "green wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/green - - starts_with = list( - /obj/item/clothing/under/color/green = 3, - /obj/item/clothing/shoes/green = 3, - /obj/item/clothing/head/soft/green = 3, - /obj/item/clothing/mask/bandana/green = 3) - -/obj/structure/closet/wardrobe/xenos - name = "xenos wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/xenos - - starts_with = list( - /obj/item/clothing/suit/unathi/mantle, - /obj/item/clothing/suit/unathi/robe, - /obj/item/clothing/shoes/sandal = 2, - /obj/item/clothing/shoes/footwraps = 2, - /obj/item/clothing/shoes/boots/winter = 2, - /obj/item/clothing/suit/storage/hooded/wintercoat = 2) - - -/obj/structure/closet/wardrobe/orange - name = "prison wardrobe" - desc = "It's a storage unit for regulation prisoner attire." - closet_appearance = /decl/closet_appearance/wardrobe/orange - - starts_with = list( - /obj/item/clothing/under/color/prison = 3, - /obj/item/clothing/shoes/orange = 3) - - -/obj/structure/closet/wardrobe/yellow - name = "yellow wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/yellow - - starts_with = list( - /obj/item/clothing/under/color/yellow = 3, - /obj/item/clothing/shoes/yellow = 3, - /obj/item/clothing/head/soft/yellow = 3, - /obj/item/clothing/mask/bandana/gold = 3) - - -/obj/structure/closet/wardrobe/atmospherics_yellow - name = "atmospherics wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/engineer/atmos - - starts_with = list( - /obj/item/clothing/under/rank/atmospheric_technician = 3, - /obj/item/clothing/under/rank/atmospheric_technician/skirt = 3, - /obj/item/clothing/shoes/black = 3, - /obj/item/clothing/head/hardhat/red = 3, - /obj/item/clothing/head/beret/engineering = 3, - /obj/item/clothing/mask/bandana/gold = 3, - /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos = 3, - /obj/item/clothing/shoes/boots/winter/atmos = 3) - -/obj/structure/closet/wardrobe/engineering_yellow - name = "engineering wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/engineer - - starts_with = list( - /obj/item/clothing/under/rank/engineer = 3, - /obj/item/clothing/under/rank/engineer/skirt = 3, - /obj/item/clothing/under/rank/engineer/turtleneck = 3, - /obj/item/clothing/shoes/orange = 3, - /obj/item/clothing/head/hardhat = 3, - /obj/item/clothing/head/beret/engineering = 3, - /obj/item/clothing/mask/bandana/gold = 3, - /obj/item/clothing/suit/storage/hooded/wintercoat/engineering = 3, - /obj/item/clothing/shoes/boots/winter/engineering = 3, - /obj/item/clothing/shoes/boots/workboots = 3) - - -/obj/structure/closet/wardrobe/white - name = "white wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/white - - starts_with = list( - /obj/item/clothing/under/color/white = 3, - /obj/item/clothing/shoes/white = 3, - /obj/item/clothing/head/soft/mime = 3) - - -/obj/structure/closet/wardrobe/pjs - name = "pajama wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/pjs - - starts_with = list( - /obj/item/clothing/under/pj/red = 2, - /obj/item/clothing/under/pj/blue = 2, - /obj/item/clothing/shoes/white = 2, - /obj/item/clothing/shoes/slippers = 2) - - -/obj/structure/closet/wardrobe/science_white - name = "science wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/science - - starts_with = list( - /obj/item/clothing/under/rank/scientist = 3, - /obj/item/clothing/under/rank/scientist/skirt = 2, - /obj/item/clothing/under/rank/scientist/turtleneck = 3, - /obj/item/clothing/suit/storage/toggle/labcoat = 3, - /obj/item/clothing/suit/storage/toggle/labcoat/modern = 3, - /obj/item/clothing/shoes/white = 3, - /obj/item/clothing/shoes/slippers = 3, - /obj/item/clothing/suit/storage/hooded/wintercoat/science, - /obj/item/clothing/shoes/boots/winter/science, - /obj/item/weapon/storage/backpack/toxins, - /obj/item/weapon/storage/backpack/satchel/tox) - -/obj/structure/closet/wardrobe/science_white/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci - else - starts_with += /obj/item/weapon/storage/backpack/satchel/tox - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci - else - starts_with += /obj/item/weapon/storage/backpack/satchel/tox - - return ..() - - -/obj/structure/closet/wardrobe/robotics_black - name = "robotics wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/robotics - - starts_with = list( - /obj/item/clothing/under/rank/roboticist = 2, - /obj/item/clothing/suit/storage/toggle/labcoat = 2, - /obj/item/clothing/suit/storage/hooded/wintercoat/science/robotics, - /obj/item/clothing/shoes/black = 2, - /obj/item/clothing/gloves/black = 2, - /obj/item/weapon/storage/backpack/toxins, - /obj/item/weapon/storage/backpack/satchel/tox) - -/obj/structure/closet/wardrobe/robotics_black/Initialize() - if(prob(50)) - starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci - else - starts_with += /obj/item/weapon/storage/backpack/satchel/tox - - return ..() - - -/obj/structure/closet/wardrobe/chemistry_white - name = "chemistry wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/medical/chemistry - - starts_with = list( - /obj/item/clothing/under/rank/chemist = 2, - /obj/item/clothing/under/rank/chemist/skirt = 2, - /obj/item/clothing/shoes/white = 2, - /obj/item/clothing/suit/storage/toggle/labcoat/chemist = 2, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical/chemist, - /obj/item/weapon/storage/backpack/chemistry = 2, - /obj/item/weapon/storage/backpack/satchel/chem = 2, - /obj/item/weapon/storage/bag/chemistry = 2,) - - -/obj/structure/closet/wardrobe/genetics_white - name = "genetics wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/medical/genetics - - starts_with = list( - /obj/item/clothing/under/rank/geneticist = 2, - /obj/item/clothing/under/rank/geneticist/skirt = 2, - /obj/item/clothing/shoes/white = 2, - /obj/item/clothing/suit/storage/toggle/labcoat/genetics = 2, - /obj/item/weapon/storage/backpack/genetics = 2, - /obj/item/weapon/storage/backpack/satchel/gen = 2) - - -/obj/structure/closet/wardrobe/virology_white - name = "virology wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/medical/virology - - starts_with = list( - /obj/item/clothing/under/rank/virologist = 2, - /obj/item/clothing/under/rank/virologist/skirt = 2, - /obj/item/clothing/shoes/white = 2, - /obj/item/clothing/suit/storage/toggle/labcoat/virologist = 2, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical/viro, - /obj/item/clothing/mask/surgical = 2, - /obj/item/weapon/storage/backpack/virology = 2, - /obj/item/weapon/storage/backpack/satchel/vir = 2) - - -/obj/structure/closet/wardrobe/medic_white - name = "medical wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/medical/white - - starts_with = list( - /obj/item/clothing/under/rank/medical = 2, - /obj/item/clothing/under/rank/medical/skirt = 2, - /obj/item/clothing/under/rank/medical/turtleneck = 2, - /obj/item/clothing/under/rank/medical/scrubs, - /obj/item/clothing/under/rank/medical/scrubs/green, - /obj/item/clothing/under/rank/medical/scrubs/purple, - /obj/item/clothing/under/rank/medical/scrubs/black, - /obj/item/clothing/under/rank/medical/scrubs/navyblue, - /obj/item/clothing/head/surgery/navyblue, - /obj/item/clothing/head/surgery/purple, - /obj/item/clothing/head/surgery/blue, - /obj/item/clothing/head/surgery/green, - /obj/item/clothing/head/surgery/black, - /obj/item/clothing/shoes/white = 2, - /obj/item/clothing/suit/storage/toggle/labcoat = 2, - /obj/item/clothing/mask/surgical = 2, - /obj/item/clothing/suit/storage/hooded/wintercoat/medical = 2, - /obj/item/clothing/shoes/boots/winter/medical = 2) - - -/obj/structure/closet/wardrobe/medic_gown - name = "cloning wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/medical/patient - - starts_with = list( - /obj/item/clothing/under/medigown = 4) - - -/obj/structure/closet/wardrobe/grey - name = "grey wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/grey - - starts_with = list( - /obj/item/clothing/under/color/grey = 3, - /obj/item/clothing/shoes/black = 3, - /obj/item/clothing/head/soft/grey = 3) - - -/obj/structure/closet/wardrobe/mixed - name = "mixed wardrobe" - closet_appearance = /decl/closet_appearance/wardrobe/mixed - - starts_with = list( - /obj/item/clothing/under/color/blue, - /obj/item/clothing/under/color/yellow, - /obj/item/clothing/under/color/green, - /obj/item/clothing/under/color/pink, - /obj/item/clothing/under/skirt/outfit/plaid_blue, - /obj/item/clothing/under/skirt/outfit/plaid_red, - /obj/item/clothing/under/skirt/outfit/plaid_purple, - /obj/item/clothing/shoes/blue, - /obj/item/clothing/shoes/yellow, - /obj/item/clothing/shoes/green, - /obj/item/clothing/shoes/purple, - /obj/item/clothing/shoes/red, - /obj/item/clothing/shoes/laceup/brown, - /obj/item/clothing/under/pants/classicjeans, - /obj/item/clothing/under/pants/mustangjeans, - /obj/item/clothing/under/pants/blackjeans, - /obj/item/clothing/under/pants/youngfolksjeans, - /obj/item/clothing/under/pants/white, - /obj/item/clothing/under/pants/red, - /obj/item/clothing/under/pants/black, - /obj/item/clothing/under/pants/tan, - /obj/item/clothing/under/pants/track, - /obj/item/clothing/suit/storage/toggle/track, - /obj/item/clothing/under/pants, - /obj/item/clothing/under/pants/khaki, - /obj/item/clothing/mask/bandana/blue, - /obj/item/clothing/mask/bandana/blue, - /obj/item/clothing/accessory/hawaiian, - /obj/item/clothing/accessory/hawaiian/blue, - /obj/item/clothing/accessory/hawaiian/pink, - /obj/item/clothing/accessory/hawaiian/red, - /obj/item/clothing/accessory/hawaiian/yellow) - - -/obj/structure/closet/wardrobe/tactical - name = "tactical equipment" - closet_appearance = /decl/closet_appearance/tactical - - starts_with = list( - /obj/item/clothing/under/tactical, - /obj/item/clothing/suit/armor/tactical, - /obj/item/clothing/head/helmet/tactical, - /obj/item/clothing/mask/balaclava/tactical, - /obj/item/clothing/mask/balaclava, - /obj/item/clothing/glasses/sunglasses/sechud/tactical, - /obj/item/clothing/shoes/boots/jackboots, - /obj/item/clothing/gloves/black, - /obj/item/clothing/under/pants/camo) - -/obj/structure/closet/wardrobe/tactical/Initialize() - if(prob(25)) - starts_with += /obj/item/weapon/storage/belt/security/tactical/bandolier - else - starts_with += /obj/item/weapon/storage/belt/security/tactical - if(prob(10)) - starts_with += /obj/item/clothing/mask/bandana/skull - - return ..() - -/obj/structure/closet/wardrobe/ert - name = "emergency response team equipment" - closet_appearance = /decl/closet_appearance/ert - - starts_with = list( - /obj/item/clothing/under/rank/centcom, - /obj/item/clothing/under/ert, - /obj/item/clothing/under/syndicate/combat, - /obj/item/device/radio/headset/ert/alt, - /obj/item/clothing/glasses/sunglasses, - /obj/item/clothing/shoes/boots/swat, - /obj/item/clothing/gloves/swat, - /obj/item/clothing/mask/balaclava/tactical, - /obj/item/clothing/mask/balaclava, - /obj/item/clothing/mask/bandana/skull = 2, - /obj/item/clothing/mask/gas/sechailer/swat) - - -/obj/structure/closet/wardrobe/suit - name = "suit locker" - closet_appearance = /decl/closet_appearance/wardrobe/suit - - starts_with = list( - /obj/item/clothing/under/assistantformal, - /obj/item/clothing/under/suit_jacket/charcoal, - /obj/item/clothing/under/suit_jacket/charcoal/skirt, - /obj/item/clothing/under/suit_jacket/navy, - /obj/item/clothing/under/suit_jacket/navy/skirt, - /obj/item/clothing/under/suit_jacket/burgundy, - /obj/item/clothing/under/suit_jacket/burgundy/skirt, - /obj/item/clothing/under/suit_jacket/checkered, - /obj/item/clothing/under/suit_jacket/checkered/skirt, - /obj/item/clothing/under/suit_jacket/tan, - /obj/item/clothing/under/suit_jacket/tan/skirt, - /obj/item/clothing/under/sl_suit, - /obj/item/clothing/under/suit_jacket, - /obj/item/clothing/under/suit_jacket/female, - /obj/item/clothing/under/suit_jacket/female/skirt, - /obj/item/clothing/under/suit_jacket/really_black, - /obj/item/clothing/under/suit_jacket/really_black/skirt, - /obj/item/clothing/under/suit_jacket/red, - /obj/item/clothing/under/suit_jacket/red/skirt, - /obj/item/clothing/under/scratch, - /obj/item/clothing/under/scratch/skirt, - /obj/item/weapon/storage/backpack/satchel = 2) - -/obj/structure/closet/wardrobe/captain - name = "site manager's wardrobe" - closet_appearance = /decl/closet_appearance/cabinet - - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - - starts_with = list( - /obj/item/weapon/storage/backpack/captain, - /obj/item/clothing/suit/captunic, - /obj/item/clothing/suit/captunic/capjacket, - /obj/item/clothing/head/caphat/cap, - /obj/item/clothing/head/caphat/beret, - /obj/item/clothing/under/rank/captain, - /obj/item/clothing/under/dress/dress_cap/femformal, - /obj/item/clothing/under/rank/neo_captain_parade, - /obj/item/clothing/under/rank/neo_captain_skirt, - /obj/item/clothing/under/rank/neo_captain, - /obj/item/clothing/under/rank/neo_captain_kilt, - /obj/item/clothing/under/rank/neo_captain_blacksuit, - /obj/item/clothing/under/rank/neo_commandutil, - /obj/item/clothing/shoes/brown, - /obj/item/clothing/gloves/captain, - /obj/item/clothing/under/dress/dress_cap, - /obj/item/weapon/storage/backpack/satchel/cap, - /obj/item/clothing/head/caphat/formal, - /obj/item/clothing/under/captainformal, - /obj/item/clothing/suit/storage/hooded/wintercoat/captain, - /obj/item/clothing/shoes/boots/winter/command, - /obj/item/clothing/head/beret/centcom/captain, - /obj/item/clothing/under/suit_jacket/green, - /obj/item/clothing/under/suit_jacket/green/skirt, - /obj/item/clothing/glasses/sunglasses, - /obj/item/clothing/head/caphat) +/obj/structure/closet/wardrobe + name = "wardrobe" + desc = "It's a storage unit for standard-issue attire." + closet_appearance = /decl/closet_appearance/wardrobe + +/obj/structure/closet/wardrobe/red + name = "security wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/red + + starts_with = list( + /obj/item/clothing/under/rank/security = 3, + /obj/item/clothing/under/rank/security2 = 3, + /obj/item/clothing/under/rank/security/turtleneck = 3, + /obj/item/clothing/under/rank/security/skirt = 2, + /obj/item/clothing/shoes/boots/jackboots = 3, + /obj/item/clothing/head/soft/sec = 3, + /obj/item/clothing/head/beret/sec = 3, + /obj/item/clothing/head/beret/sec/corporate/officer = 3, + /obj/item/clothing/mask/bandana/red = 3, + /obj/item/clothing/suit/storage/hooded/wintercoat/security = 3, + /obj/item/clothing/accessory/armband = 3, + /obj/item/clothing/accessory/holster/waist = 3) + +/obj/structure/closet/wardrobe/red/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/security + else + starts_with += /obj/item/weapon/storage/backpack/satchel/sec + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/security + else + starts_with += /obj/item/weapon/storage/backpack/satchel/sec + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/security + else + starts_with += /obj/item/weapon/storage/backpack/satchel/sec + + return ..() + +/obj/structure/closet/wardrobe/detective + name = "detective wardrobe" + closet_appearance = /decl/closet_appearance/cabinet + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + + starts_with = list( + /obj/item/clothing/head/det = 2, + /obj/item/clothing/head/det/grey = 2, + /obj/item/clothing/shoes/brown = 2, + /obj/item/clothing/shoes/laceup = 2, + /obj/item/clothing/under/det = 2, + /obj/item/clothing/under/det/waistcoat = 2, + /obj/item/clothing/under/det/grey = 2, + /obj/item/clothing/under/det/grey/waistcoat = 2, + /obj/item/clothing/under/det/black = 2, + /obj/item/clothing/under/det/skirt, + /obj/item/clothing/under/det/corporate = 2, + /obj/item/clothing/suit/storage/det_trench = 2, + /obj/item/clothing/suit/storage/det_trench/grey = 2, + /obj/item/clothing/suit/storage/forensics/blue = 2, + /obj/item/clothing/suit/storage/forensics/red = 2) + +/obj/structure/closet/wardrobe/pink + name = "pink wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/pink + + starts_with = list( + /obj/item/clothing/under/color/pink = 3, + /obj/item/clothing/shoes/brown = 3) + +/obj/structure/closet/wardrobe/black + name = "black wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/black + + starts_with = list( + /obj/item/clothing/under/color/black = 3, + /obj/item/clothing/shoes/black = 3, + /obj/item/clothing/head/that = 3, + /obj/item/clothing/head/soft/black = 3, + /obj/item/clothing/mask/bandana = 3, + /obj/item/weapon/storage/backpack/messenger/black) + + +/obj/structure/closet/wardrobe/chaplain_black + name = "chapel wardrobe" + desc = "It's a storage unit for approved religious attire." + closet_appearance = /decl/closet_appearance/wardrobe/chapel + + starts_with = list( + /obj/item/clothing/under/rank/chaplain, + /obj/item/clothing/shoes/black, + /obj/item/clothing/suit/nun, + /obj/item/clothing/head/nun_hood, + /obj/item/clothing/suit/storage/hooded/chaplain_hoodie, + /obj/item/clothing/suit/storage/hooded/chaplain_hoodie/whiteout, + /obj/item/clothing/suit/holidaypriest, + /obj/item/clothing/under/wedding/bride_white, + /obj/item/weapon/storage/backpack/cultpack, + /obj/item/weapon/storage/fancy/candle_box = 2, + /obj/item/weapon/storage/fancy/whitecandle_box, + /obj/item/weapon/storage/fancy/blackcandle_box, + /obj/item/godfig = 2, + /obj/item/weapon/deck/tarot) + + +/obj/structure/closet/wardrobe/green + name = "green wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/green + + starts_with = list( + /obj/item/clothing/under/color/green = 3, + /obj/item/clothing/shoes/green = 3, + /obj/item/clothing/head/soft/green = 3, + /obj/item/clothing/mask/bandana/green = 3) + +/obj/structure/closet/wardrobe/xenos + name = "xenos wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/xenos + + starts_with = list( + /obj/item/clothing/suit/unathi/mantle, + /obj/item/clothing/suit/unathi/robe, + /obj/item/clothing/shoes/sandal = 2, + /obj/item/clothing/shoes/footwraps = 2, + /obj/item/clothing/shoes/boots/winter = 2, + /obj/item/clothing/suit/storage/hooded/wintercoat = 2) + + +/obj/structure/closet/wardrobe/orange + name = "prison wardrobe" + desc = "It's a storage unit for regulation prisoner attire." + closet_appearance = /decl/closet_appearance/wardrobe/orange + + starts_with = list( + /obj/item/clothing/under/color/prison = 3, + /obj/item/clothing/shoes/orange = 3) + + +/obj/structure/closet/wardrobe/yellow + name = "yellow wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/yellow + + starts_with = list( + /obj/item/clothing/under/color/yellow = 3, + /obj/item/clothing/shoes/yellow = 3, + /obj/item/clothing/head/soft/yellow = 3, + /obj/item/clothing/mask/bandana/gold = 3) + + +/obj/structure/closet/wardrobe/atmospherics_yellow + name = "atmospherics wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/engineer/atmos + + starts_with = list( + /obj/item/clothing/under/rank/atmospheric_technician = 3, + /obj/item/clothing/under/rank/atmospheric_technician/skirt = 3, + /obj/item/clothing/shoes/black = 3, + /obj/item/clothing/head/hardhat/red = 3, + /obj/item/clothing/head/beret/engineering = 3, + /obj/item/clothing/mask/bandana/gold = 3, + /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos = 3, + /obj/item/clothing/shoes/boots/winter/atmos = 3) + +/obj/structure/closet/wardrobe/engineering_yellow + name = "engineering wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/engineer + + starts_with = list( + /obj/item/clothing/under/rank/engineer = 3, + /obj/item/clothing/under/rank/engineer/skirt = 3, + /obj/item/clothing/under/rank/engineer/turtleneck = 3, + /obj/item/clothing/shoes/orange = 3, + /obj/item/clothing/head/hardhat = 3, + /obj/item/clothing/head/beret/engineering = 3, + /obj/item/clothing/mask/bandana/gold = 3, + /obj/item/clothing/suit/storage/hooded/wintercoat/engineering = 3, + /obj/item/clothing/shoes/boots/winter/engineering = 3, + /obj/item/clothing/shoes/boots/workboots = 3) + + +/obj/structure/closet/wardrobe/white + name = "white wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/white + + starts_with = list( + /obj/item/clothing/under/color/white = 3, + /obj/item/clothing/shoes/white = 3, + /obj/item/clothing/head/soft/mime = 3) + + +/obj/structure/closet/wardrobe/pjs + name = "pajama wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/pjs + + starts_with = list( + /obj/item/clothing/under/pj/red = 2, + /obj/item/clothing/under/pj/blue = 2, + /obj/item/clothing/shoes/white = 2, + /obj/item/clothing/shoes/slippers = 2) + + +/obj/structure/closet/wardrobe/science_white + name = "science wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/science + + starts_with = list( + /obj/item/clothing/under/rank/scientist = 3, + /obj/item/clothing/under/rank/scientist/skirt = 2, + /obj/item/clothing/under/rank/scientist/turtleneck = 3, + /obj/item/clothing/suit/storage/toggle/labcoat = 3, + /obj/item/clothing/suit/storage/toggle/labcoat/modern = 3, + /obj/item/clothing/shoes/white = 3, + /obj/item/clothing/shoes/slippers = 3, + /obj/item/clothing/suit/storage/hooded/wintercoat/science, + /obj/item/clothing/shoes/boots/winter/science, + /obj/item/weapon/storage/backpack/toxins, + /obj/item/weapon/storage/backpack/satchel/tox) + +/obj/structure/closet/wardrobe/science_white/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci + else + starts_with += /obj/item/weapon/storage/backpack/satchel/tox + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci + else + starts_with += /obj/item/weapon/storage/backpack/satchel/tox + + return ..() + + +/obj/structure/closet/wardrobe/robotics_black + name = "robotics wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/robotics + + starts_with = list( + /obj/item/clothing/under/rank/roboticist = 2, + /obj/item/clothing/suit/storage/toggle/labcoat = 2, + /obj/item/clothing/suit/storage/hooded/wintercoat/science/robotics, + /obj/item/clothing/shoes/black = 2, + /obj/item/clothing/gloves/black = 2, + /obj/item/weapon/storage/backpack/toxins, + /obj/item/weapon/storage/backpack/satchel/tox) + +/obj/structure/closet/wardrobe/robotics_black/Initialize() + if(prob(50)) + starts_with += /obj/item/weapon/storage/backpack/dufflebag/sci + else + starts_with += /obj/item/weapon/storage/backpack/satchel/tox + + return ..() + + +/obj/structure/closet/wardrobe/chemistry_white + name = "chemistry wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/medical/chemistry + + starts_with = list( + /obj/item/clothing/under/rank/chemist = 2, + /obj/item/clothing/under/rank/chemist/skirt = 2, + /obj/item/clothing/shoes/white = 2, + /obj/item/clothing/suit/storage/toggle/labcoat/chemist = 2, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical/chemist, + /obj/item/weapon/storage/backpack/chemistry = 2, + /obj/item/weapon/storage/backpack/satchel/chem = 2, + /obj/item/weapon/storage/bag/chemistry = 2,) + + +/obj/structure/closet/wardrobe/genetics_white + name = "genetics wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/medical/genetics + + starts_with = list( + /obj/item/clothing/under/rank/geneticist = 2, + /obj/item/clothing/under/rank/geneticist/skirt = 2, + /obj/item/clothing/shoes/white = 2, + /obj/item/clothing/suit/storage/toggle/labcoat/genetics = 2, + /obj/item/weapon/storage/backpack/genetics = 2, + /obj/item/weapon/storage/backpack/satchel/gen = 2) + + +/obj/structure/closet/wardrobe/virology_white + name = "virology wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/medical/virology + + starts_with = list( + /obj/item/clothing/under/rank/virologist = 2, + /obj/item/clothing/under/rank/virologist/skirt = 2, + /obj/item/clothing/shoes/white = 2, + /obj/item/clothing/suit/storage/toggle/labcoat/virologist = 2, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical/viro, + /obj/item/clothing/mask/surgical = 2, + /obj/item/weapon/storage/backpack/virology = 2, + /obj/item/weapon/storage/backpack/satchel/vir = 2) + + +/obj/structure/closet/wardrobe/medic_white + name = "medical wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/medical/white + + starts_with = list( + /obj/item/clothing/under/rank/medical = 2, + /obj/item/clothing/under/rank/medical/skirt = 2, + /obj/item/clothing/under/rank/medical/turtleneck = 2, + /obj/item/clothing/under/rank/medical/scrubs, + /obj/item/clothing/under/rank/medical/scrubs/green, + /obj/item/clothing/under/rank/medical/scrubs/purple, + /obj/item/clothing/under/rank/medical/scrubs/black, + /obj/item/clothing/under/rank/medical/scrubs/navyblue, + /obj/item/clothing/head/surgery/navyblue, + /obj/item/clothing/head/surgery/purple, + /obj/item/clothing/head/surgery/blue, + /obj/item/clothing/head/surgery/green, + /obj/item/clothing/head/surgery/black, + /obj/item/clothing/shoes/white = 2, + /obj/item/clothing/suit/storage/toggle/labcoat = 2, + /obj/item/clothing/mask/surgical = 2, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical = 2, + /obj/item/clothing/shoes/boots/winter/medical = 2) + + +/obj/structure/closet/wardrobe/medic_gown + name = "cloning wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/medical/patient + + starts_with = list( + /obj/item/clothing/under/medigown = 4) + + +/obj/structure/closet/wardrobe/grey + name = "grey wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/grey + + starts_with = list( + /obj/item/clothing/under/color/grey = 3, + /obj/item/clothing/shoes/black = 3, + /obj/item/clothing/head/soft/grey = 3) + + +/obj/structure/closet/wardrobe/mixed + name = "mixed wardrobe" + closet_appearance = /decl/closet_appearance/wardrobe/mixed + + starts_with = list( + /obj/item/clothing/under/color/blue, + /obj/item/clothing/under/color/yellow, + /obj/item/clothing/under/color/green, + /obj/item/clothing/under/color/pink, + /obj/item/clothing/under/skirt/outfit/plaid_blue, + /obj/item/clothing/under/skirt/outfit/plaid_red, + /obj/item/clothing/under/skirt/outfit/plaid_purple, + /obj/item/clothing/shoes/blue, + /obj/item/clothing/shoes/yellow, + /obj/item/clothing/shoes/green, + /obj/item/clothing/shoes/purple, + /obj/item/clothing/shoes/red, + /obj/item/clothing/shoes/laceup/brown, + /obj/item/clothing/under/pants/classicjeans, + /obj/item/clothing/under/pants/mustangjeans, + /obj/item/clothing/under/pants/blackjeans, + /obj/item/clothing/under/pants/youngfolksjeans, + /obj/item/clothing/under/pants/white, + /obj/item/clothing/under/pants/red, + /obj/item/clothing/under/pants/black, + /obj/item/clothing/under/pants/tan, + /obj/item/clothing/under/pants/track, + /obj/item/clothing/suit/storage/toggle/track, + /obj/item/clothing/under/pants, + /obj/item/clothing/under/pants/khaki, + /obj/item/clothing/mask/bandana/blue, + /obj/item/clothing/mask/bandana/blue, + /obj/item/clothing/accessory/hawaiian, + /obj/item/clothing/accessory/hawaiian/blue, + /obj/item/clothing/accessory/hawaiian/pink, + /obj/item/clothing/accessory/hawaiian/red, + /obj/item/clothing/accessory/hawaiian/yellow) + + +/obj/structure/closet/wardrobe/tactical + name = "tactical equipment" + closet_appearance = /decl/closet_appearance/tactical + + starts_with = list( + /obj/item/clothing/under/tactical, + /obj/item/clothing/suit/armor/tactical, + /obj/item/clothing/head/helmet/tactical, + /obj/item/clothing/mask/balaclava/tactical, + /obj/item/clothing/mask/balaclava, + /obj/item/clothing/glasses/sunglasses/sechud/tactical, + /obj/item/clothing/shoes/boots/jackboots, + /obj/item/clothing/gloves/black, + /obj/item/clothing/under/pants/camo) + +/obj/structure/closet/wardrobe/tactical/Initialize() + if(prob(25)) + starts_with += /obj/item/weapon/storage/belt/security/tactical/bandolier + else + starts_with += /obj/item/weapon/storage/belt/security/tactical + if(prob(10)) + starts_with += /obj/item/clothing/mask/bandana/skull + + return ..() + +/obj/structure/closet/wardrobe/ert + name = "emergency response team equipment" + closet_appearance = /decl/closet_appearance/ert + + starts_with = list( + /obj/item/clothing/under/rank/centcom, + /obj/item/clothing/under/ert, + /obj/item/clothing/under/syndicate/combat, + /obj/item/device/radio/headset/ert/alt, + /obj/item/clothing/glasses/sunglasses, + /obj/item/clothing/shoes/boots/swat, + /obj/item/clothing/gloves/swat, + /obj/item/clothing/mask/balaclava/tactical, + /obj/item/clothing/mask/balaclava, + /obj/item/clothing/mask/bandana/skull = 2, + /obj/item/clothing/mask/gas/sechailer/swat) + + +/obj/structure/closet/wardrobe/suit + name = "suit locker" + closet_appearance = /decl/closet_appearance/wardrobe/suit + + starts_with = list( + /obj/item/clothing/under/assistantformal, + /obj/item/clothing/under/suit_jacket/charcoal, + /obj/item/clothing/under/suit_jacket/charcoal/skirt, + /obj/item/clothing/under/suit_jacket/navy, + /obj/item/clothing/under/suit_jacket/navy/skirt, + /obj/item/clothing/under/suit_jacket/burgundy, + /obj/item/clothing/under/suit_jacket/burgundy/skirt, + /obj/item/clothing/under/suit_jacket/checkered, + /obj/item/clothing/under/suit_jacket/checkered/skirt, + /obj/item/clothing/under/suit_jacket/tan, + /obj/item/clothing/under/suit_jacket/tan/skirt, + /obj/item/clothing/under/sl_suit, + /obj/item/clothing/under/suit_jacket, + /obj/item/clothing/under/suit_jacket/female, + /obj/item/clothing/under/suit_jacket/female/skirt, + /obj/item/clothing/under/suit_jacket/really_black, + /obj/item/clothing/under/suit_jacket/really_black/skirt, + /obj/item/clothing/under/suit_jacket/red, + /obj/item/clothing/under/suit_jacket/red/skirt, + /obj/item/clothing/under/scratch, + /obj/item/clothing/under/scratch/skirt, + /obj/item/weapon/storage/backpack/satchel = 2) + +/obj/structure/closet/wardrobe/captain + name = "site manager's wardrobe" + closet_appearance = /decl/closet_appearance/cabinet + + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + + starts_with = list( + /obj/item/weapon/storage/backpack/captain, + /obj/item/clothing/suit/captunic, + /obj/item/clothing/suit/captunic/capjacket, + /obj/item/clothing/head/caphat/cap, + /obj/item/clothing/head/caphat/beret, + /obj/item/clothing/under/rank/captain, + /obj/item/clothing/under/dress/dress_cap/femformal, + /obj/item/clothing/under/rank/neo_captain_parade, + /obj/item/clothing/under/rank/neo_captain_skirt, + /obj/item/clothing/under/rank/neo_captain, + /obj/item/clothing/under/rank/neo_captain_kilt, + /obj/item/clothing/under/rank/neo_captain_blacksuit, + /obj/item/clothing/under/rank/neo_commandutil, + /obj/item/clothing/shoes/brown, + /obj/item/clothing/gloves/captain, + /obj/item/clothing/under/dress/dress_cap, + /obj/item/weapon/storage/backpack/satchel/cap, + /obj/item/clothing/head/caphat/formal, + /obj/item/clothing/under/captainformal, + /obj/item/clothing/suit/storage/hooded/wintercoat/captain, + /obj/item/clothing/shoes/boots/winter/command, + /obj/item/clothing/head/beret/centcom/captain, + /obj/item/clothing/under/suit_jacket/green, + /obj/item/clothing/under/suit_jacket/green/skirt, + /obj/item/clothing/glasses/sunglasses, + /obj/item/clothing/head/caphat) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 56f5a3f38dc..c137499cb81 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -1,734 +1,743 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/structure/closet/crate - name = "crate" - desc = "A rectangular steel crate." - icon = 'icons/obj/closets/bases/crate.dmi' - closet_appearance = /decl/closet_appearance/crate - climbable = TRUE - dir = 4 //Spawn facing 'forward' by default. - var/points_per_crate = 5 - var/rigged = 0 - - open_sound = 'sound/effects/crate_open.ogg' - close_sound = 'sound/effects/crate_close.ogg' - -/obj/structure/closet/crate/can_open() - return 1 - -/obj/structure/closet/crate/can_close() - return 1 - -/obj/structure/closet/crate/open() - if(src.opened) - return 0 - if(!src.can_open()) - return 0 - - if(rigged && locate(/obj/item/device/radio/electropack) in src) - if(isliving(usr)) - var/mob/living/L = usr - if(L.electrocute_act(17, src)) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, src) - s.start() - if(usr.stunned) - return 2 - - playsound(src, open_sound, 50, 1, -3) - for(var/obj/O in src) - O.forceMove(get_turf(src)) - src.opened = 1 - - if(climbable) - structure_shaken() - update_icon() - return 1 - -/obj/structure/closet/crate/close() - if(!src.opened) - return 0 - if(!src.can_close()) - return 0 - - playsound(src, close_sound, 50, 1, -3) - var/itemcount = 0 - for(var/obj/O in get_turf(src)) - if(itemcount >= storage_capacity) - break - if(O.density || O.anchored || istype(O,/obj/structure/closet)) - continue - if(istype(O, /obj/structure/bed)) //This is only necessary because of rollerbeds and swivel chairs. - var/obj/structure/bed/B = O - if(B.has_buckled_mobs()) - continue - O.forceMove(src) - itemcount++ - - src.opened = 0 - update_icon() - return 1 - -/obj/structure/closet/crate/verb/rotate_clockwise() - set name = "Rotate Crate Clockwise" - set category = "Object" - set src in oview(1) - - if (usr.stat || usr.restrained() || anchored) - return - - src.set_dir(turn(src.dir, 270)) - -/obj/structure/closet/crate/verb/rotate_counterclockwise() - set category = "Object" - set name = "Rotate Crate Counterclockwise" - set src in view(1) - - if (usr.stat || usr.restrained() || anchored) - return - - src.set_dir(turn(src.dir, 90)) - -/obj/structure/closet/crate/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench() && istype(src,/obj/structure/closet/crate/bin)) - return ..() - else if(opened) - if(isrobot(user)) - return - if(W.loc != user) // This should stop mounted modules ending up outside the module. - return - user.drop_item() - if(W) - W.forceMove(src.loc) - else if(istype(W, /obj/item/weapon/packageWrap)) - return - else if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(rigged) - to_chat(user, "[src] is already rigged!") - return - if (C.use(1)) - to_chat(user , "You rig [src].") - rigged = 1 - return - else if(istype(W, /obj/item/device/radio/electropack)) - if(rigged) - to_chat(user , "You attach [W] to [src].") - user.drop_item() - W.forceMove(src) - return - else if(W.is_wirecutter()) - if(rigged) - to_chat(user , "You cut away the wiring.") - playsound(src, W.usesound, 100, 1) - rigged = 0 - return - else return attack_hand(user) - -/obj/structure/closet/crate/ex_act(severity) - switch(severity) - if(1.0) - for(var/obj/O in src.contents) - qdel(O) - qdel(src) - return - if(2.0) - for(var/obj/O in src.contents) - if(prob(50)) - qdel(O) - qdel(src) - return - if(3.0) - if (prob(50)) - qdel(src) - return - else - return - -/obj/structure/closet/crate/secure - desc = "A secure crate." - name = "Secure crate" - closet_appearance = /decl/closet_appearance/crate/secure - var/broken = 0 - var/locked = 1 - -/obj/structure/closet/crate/secure/can_open() - return !locked - -/obj/structure/closet/crate/secure/update_icon() - if(opened) - icon_state = "open" - else - if(broken) - icon_state = "closed_emagged[sealed ? "_welded" : ""]" - else - if(locked) - icon_state = "closed_locked[sealed ? "_welded" : ""]" - else - icon_state = "closed_unlocked[sealed ? "_welded" : ""]" - -/obj/structure/closet/crate/secure/proc/togglelock(mob/user as mob) - if(src.opened) - to_chat(user, "Close the crate first.") - return - if(src.broken) - to_chat(user, "The crate appears to be broken.") - return - if(src.allowed(user)) - set_locked(!locked, user) - else - to_chat(user, "Access Denied") - -/obj/structure/closet/crate/secure/proc/set_locked(var/newlocked, mob/user = null) - if(locked == newlocked) return - - locked = newlocked - if(user) - for(var/mob/O in viewers(user, 3)) - O.show_message( "The crate has been [locked ? null : "un"]locked by [user].", 1) - update_icon() - -/obj/structure/closet/crate/secure/verb/verb_togglelock() - set src in oview(1) // One square distance - set category = "Object" - set name = "Toggle Lock" - - if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - - if(ishuman(usr) || isrobot(usr)) - src.add_fingerprint(usr) - src.togglelock(usr) - else - to_chat(usr, "This mob type can't use this verb.") - -/obj/structure/closet/crate/secure/attack_hand(mob/user as mob) - src.add_fingerprint(user) - if(locked) - src.togglelock(user) - else - src.toggle(user) - -/obj/structure/closet/crate/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(is_type_in_list(W, list(/obj/item/weapon/packageWrap, /obj/item/stack/cable_coil, /obj/item/device/radio/electropack, /obj/item/weapon/tool/wirecutters))) - return ..() - if(istype(W, /obj/item/weapon/melee/energy/blade)) - emag_act(INFINITY, user) - if(!opened) - src.togglelock(user) - return - return ..() - -/obj/structure/closet/crate/secure/emag_act(var/remaining_charges, var/mob/user) - if(!broken) - playsound(src, "sparks", 60, 1) - locked = 0 - broken = 1 - to_chat(user, "You unlock \the [src].") - update_icon() - return 1 - -/obj/structure/closet/crate/secure/emp_act(severity) - for(var/obj/O in src) - O.emp_act(severity) - if(!broken && !opened && prob(50/severity)) - if(!locked) - locked = 1 - else - playsound(src, 'sound/effects/sparks4.ogg', 75, 1) - locked = 0 - if(!opened && prob(20/severity)) - if(!locked) - open() - else - req_access = list() - req_access += pick(get_all_station_access()) - update_icon() - ..() - -/obj/structure/closet/crate/plastic - name = "plastic crate" - desc = "A rectangular plastic crate." - closet_appearance = /decl/closet_appearance/crate/plastic - points_per_crate = 1 //5 crates per ordered crate, +5 for the crate it comes in. - -/obj/structure/closet/crate/internals - name = "internals crate" - desc = "A internals crate." - -/obj/structure/closet/crate/trashcart - name = "trash cart" - desc = "A heavy, metal trashcart with wheels." - closet_appearance = /decl/closet_appearance/cart/trash - -/*these aren't needed anymore -/obj/structure/closet/crate/hat - desc = "A crate filled with Valuable Collector's Hats!." - name = "Hat Crate" - -/obj/structure/closet/crate/contraband - name = "Poster crate" - desc = "A random assortment of posters manufactured by providers NOT listed under NanoTrasen's whitelist." -*/ - -/obj/structure/closet/crate/medical - name = "medical crate" - desc = "A medical crate." - closet_appearance = /decl/closet_appearance/crate/medical - -/obj/structure/closet/crate/rcd - name = "\improper RCD crate" - desc = "A crate with rapid construction device." - - starts_with = list( - /obj/item/weapon/rcd_ammo = 3, - /obj/item/weapon/rcd) - -/obj/structure/closet/crate/solar - name = "solar pack crate" - - starts_with = list( - /obj/item/solar_assembly = 21, - /obj/item/weapon/circuitboard/solar_control, - /obj/item/weapon/tracker_electronics, - /obj/item/weapon/paper/solar) - -/obj/structure/closet/crate/freezer - name = "freezer" - desc = "A freezer." - closet_appearance = /decl/closet_appearance/crate/freezer - var/target_temp = T0C - 40 - var/cooling_power = 40 - -/obj/structure/closet/crate/freezer/centauri - desc = "A freezer stamped with the logo of Centauri Provisions." - closet_appearance = /decl/closet_appearance/crate/freezer/centauri - -/obj/structure/closet/crate/freezer/nanotrasen - desc = "A freezer stamped with the logo of NanoTrasen." - closet_appearance = /decl/closet_appearance/crate/freezer/nanotrasen - -/obj/structure/closet/crate/freezer/veymed - desc = "A freezer stamped with the logo of Vey-Medical." - closet_appearance = /decl/closet_appearance/crate/freezer/veymed - -/obj/structure/closet/crate/freezer/zenghu - desc = "A freezer stamped with the logo of Zeng-Hu Pharmaceuticals." - closet_appearance = /decl/closet_appearance/crate/freezer/zenghu - -/obj/structure/closet/crate/freezer/return_air() - var/datum/gas_mixture/gas = (..()) - if(!gas) return null - var/datum/gas_mixture/newgas = new/datum/gas_mixture() - newgas.copy_from(gas) - if(newgas.temperature <= target_temp) return - - if((newgas.temperature - cooling_power) > target_temp) - newgas.temperature -= cooling_power - else - newgas.temperature = target_temp - return newgas - -/obj/structure/closet/crate/freezer/Entered(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 1 - for(var/obj/item/organ/organ in O) - organ.preserved = 1 - ..() - -/obj/structure/closet/crate/freezer/Exited(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 0 - for(var/obj/item/organ/organ in O) - organ.preserved = 0 - ..() - -/obj/structure/closet/crate/weapon - name = "weapons crate" - desc = "A barely secured weapons crate." - closet_appearance = /decl/closet_appearance/crate/secure/weapon - -/obj/structure/closet/crate/freezer/rations //Fpr use in the escape shuttle - name = "emergency rations" - desc = "A crate of emergency rations." - - starts_with = list( - /obj/random/mre = 6) - -/obj/structure/closet/crate/bin - name = "large bin" - desc = "A large bin." - closet_appearance = null - icon = 'icons/obj/closets/largebin.dmi' - -/obj/structure/closet/crate/radiation - name = "radioactive gear crate" - desc = "A crate with a radiation sign on it." - closet_appearance = /decl/closet_appearance/crate/radiation - - starts_with = list( - /obj/item/clothing/suit/radiation = 4, - /obj/item/clothing/head/radiation = 4) - -//TSCs - -/obj/structure/closet/crate/aether - desc = "A crate painted in the colours of Aether Atmospherics and Recycling." - closet_appearance = /decl/closet_appearance/crate/aether - -/obj/structure/closet/crate/centauri - desc = "A crate decorated with the logo of Centauri Provisions." - closet_appearance = /decl/closet_appearance/crate/centauri - -/obj/structure/closet/crate/einstein - desc = "A crate labelled with an Einstein Engines sticker." - closet_appearance = /decl/closet_appearance/crate/einstein - -/obj/structure/closet/crate/focalpoint - desc = "A crate marked with the decal of Focal Point Energistics." - closet_appearance = /decl/closet_appearance/crate/focalpoint - -/obj/structure/closet/crate/gilthari - desc = "A crate embossed with the logo of Gilthari Exports." - closet_appearance = /decl/closet_appearance/crate/gilthari - -/obj/structure/closet/crate/grayson - desc = "A bare metal crate spraypainted with Grayson Manufactories decals." - closet_appearance = /decl/closet_appearance/crate/grayson - -/obj/structure/closet/crate/heph - desc = "A sturdy crate marked with the logo of Hephaestus Industries." - closet_appearance = /decl/closet_appearance/crate/heph - -/obj/structure/closet/crate/morpheus - desc = "A crate crudely imprinted with 'MORPHEUS CYBERKINETICS'." - closet_appearance = /decl/closet_appearance/crate/morpheus - -/obj/structure/closet/crate/nanotrasen - desc = "A crate emblazoned with the standard NanoTrasen livery." - closet_appearance = /decl/closet_appearance/crate/nanotrasen - -/obj/structure/closet/crate/nanothreads - desc = "A crate emblazoned with the NanoThreads Garments livery, a subsidary of the NanoTrasen Corporation." - closet_appearance = /decl/closet_appearance/crate/nanotrasenclothing - -/obj/structure/closet/crate/nanomed - desc = "A crate emblazoned with the NanoMed Medical livery, a subsidary of the NanoTrasen Corporation." - closet_appearance = /decl/closet_appearance/crate/nanotrasenmedical - -/obj/structure/closet/crate/oculum - desc = "A crate minimally decorated with the logo of media giant Oculum Broadcast." - closet_appearance = /decl/closet_appearance/crate/oculum - -/obj/structure/closet/crate/veymed - desc = "A sterile crate extensively detailed in Veymed colours." - closet_appearance = /decl/closet_appearance/crate/veymed - -/obj/structure/closet/crate/ward - desc = "A crate decaled with the logo of Ward-Takahashi." - closet_appearance = /decl/closet_appearance/crate/ward - -/obj/structure/closet/crate/xion - desc = "A crate painted in Xion Manufacturing Group orange." - closet_appearance = /decl/closet_appearance/crate/xion - -/obj/structure/closet/crate/zenghu - desc = "A sterile crate marked with the logo of Zeng-Hu Pharmaceuticals." - closet_appearance = /decl/closet_appearance/crate/zenghu - -// Brands/subsidiaries - -/obj/structure/closet/crate/allico - desc = "A crate painted in the distinctive cheerful colours of AlliCo. Ltd." - closet_appearance = /decl/closet_appearance/crate/allico - -/obj/structure/closet/crate/carp - desc = "A crate painted with the garish livery of Consolidated Agricultural Resources Plc." - closet_appearance = /decl/closet_appearance/crate/carp - -/obj/structure/closet/crate/hedberg - name = "weapons crate" - desc = "A weapons crate stamped with the logo of Hedberg-Hammarstrom and the lock conspicuously absent." - closet_appearance = /decl/closet_appearance/crate/secure/hedberg - -/obj/structure/closet/crate/galaksi - desc = "A crate printed with the markings of Ward-Takahashi's Galaksi Appliance branding." - closet_appearance = /decl/closet_appearance/crate/galaksi - -/obj/structure/closet/crate/thinktronic - desc = "A crate printed with the markings of Thinktronic Systems." - closet_appearance = /decl/closet_appearance/crate/thinktronic - -/obj/structure/closet/crate/ummarcar - desc = "A flimsy crate marked labelled 'UmMarcar Office Supply'." - closet_appearance = /decl/closet_appearance/crate/ummarcar - -/obj/structure/closet/crate/unathi - name = "import crate" - desc = "A crate painted with the markings of Moghes Imported Sissalik Jerky." - closet_appearance = /decl/closet_appearance/crate/unathiimport - - -// Secure Crates - -/obj/structure/closet/crate/secure/weapon - name = "weapons crate" - desc = "A secure weapons crate." - closet_appearance = /decl/closet_appearance/crate/secure/weapon - -/obj/structure/closet/crate/secure/aether - desc = "A secure crate painted in the colours of Aether Atmospherics and Recycling." - closet_appearance = /decl/closet_appearance/crate/secure/aether - -/obj/structure/closet/crate/secure/bishop - desc = "A secure crate finely decorated with the emblem of Bishop Cybernetics." - closet_appearance = /decl/closet_appearance/crate/secure/bishop - -/obj/structure/closet/crate/secure/cybersolutions - desc = "An unadorned secure metal crate labelled 'Cyber Solutions'." - closet_appearance = /decl/closet_appearance/crate/secure/cybersolutions - -/obj/structure/closet/crate/secure/einstein - desc = "A secure crate labelled with an Einstein Engines sticker." - closet_appearance = /decl/closet_appearance/crate/secure/einstein - -/obj/structure/closet/crate/secure/focalpoint - desc = "A secure crate marked with the decal of Focal Point Energistics." - closet_appearance = /decl/closet_appearance/crate/secure/focalpoint - -/obj/structure/closet/crate/secure/gilthari - desc = "A secure crate embossed with the logo of Gilthari Exports." - closet_appearance = /decl/closet_appearance/crate/secure/gilthari - -/obj/structure/closet/crate/secure/grayson - desc = "A secure bare metal crate spraypainted with Grayson Manufactories decals." - closet_appearance = /decl/closet_appearance/crate/secure/grayson - -/obj/structure/closet/crate/secure/hedberg - name = "weapons crate" - desc = "A secure weapons crate stamped with the logo of Hedberg-Hammarstrom." - closet_appearance = /decl/closet_appearance/crate/secure/hedberg - -/obj/structure/closet/crate/secure/heph - name = "weapons crate" - desc = "A secure weapons crate marked with the logo of Hephaestus Industries." - closet_appearance = /decl/closet_appearance/crate/secure/heph - -/obj/structure/closet/crate/secure/lawson - name = "weapons crate" - desc = "A secure weapons crate marked with the logo of Lawson Arms." - closet_appearance = /decl/closet_appearance/crate/secure/lawson - -/obj/structure/closet/crate/secure/morpheus - desc = "A secure crate crudely imprinted with 'MORPHEUS CYBERKINETICS'." - closet_appearance = /decl/closet_appearance/crate/secure/morpheus - -/obj/structure/closet/crate/secure/nanotrasen - desc = "A secure crate emblazoned with the standard NanoTrasen livery." - closet_appearance = /decl/closet_appearance/crate/secure/nanotrasen - -/obj/structure/closet/crate/secure/nanomed - desc = "A secure crate emblazoned with the NanoMed Medical livery, a subsidary of the NanoTrasen Corporation." - closet_appearance = /decl/closet_appearance/crate/secure/nanotrasenmedical - -/obj/structure/closet/crate/secure/scg - name = "weapons crate" - desc = "A secure crate in the official colours of the Solar Confederate Government." - closet_appearance = /decl/closet_appearance/crate/secure/solgov - -/obj/structure/closet/crate/secure/saare - name = "weapons crate" - desc = "A secure weapons crate plainly stamped with the logo of Stealth Assault Enterprises." - closet_appearance = /decl/closet_appearance/crate/secure/saare - -/obj/structure/closet/crate/secure/veymed - desc = "A secure sterile crate extensively detailed in Veymed colours." - closet_appearance = /decl/closet_appearance/crate/secure/veymed - -/obj/structure/closet/crate/secure/ward - desc = "A secure crate decaled with the logo of Ward-Takahashi." - closet_appearance = /decl/closet_appearance/crate/secure/ward - -/obj/structure/closet/crate/secure/xion - desc = "A secure crate painted in Xion Manufacturing Group orange." - closet_appearance = /decl/closet_appearance/crate/secure/xion - -/obj/structure/closet/crate/secure/zenghu - desc = "A secure sterile crate marked with the logo of Zeng-Hu Pharmaceuticals." - closet_appearance = /decl/closet_appearance/crate/secure/zenghu - -/obj/structure/closet/crate/secure/phoron - name = "phoron crate" - desc = "A secure phoron crate painted in standard NanoTrasen livery." - closet_appearance = /decl/closet_appearance/crate/secure/hazard - -/obj/structure/closet/crate/secure/gear - name = "gear crate" - desc = "A secure gear crate." - closet_appearance = /decl/closet_appearance/crate/secure/weapon - -/obj/structure/closet/crate/secure/hydrosec - name = "secure hydroponics crate" - desc = "A crate with a lock on it, painted in the scheme of the station's botanists." - closet_appearance = /decl/closet_appearance/crate/secure/hydroponics - -/obj/structure/closet/crate/secure/engineering - desc = "A crate with a lock on it, painted in the scheme of the station's engineers." - name = "secure engineering crate" - -/obj/structure/closet/crate/secure/science - name = "secure science crate" - desc = "A crate with a lock on it, painted in the scheme of the station's scientists." - -/obj/structure/closet/crate/secure/bin - name = "secure bin" - desc = "A secure bin." - -// Large crates - -/obj/structure/closet/crate/large - name = "large crate" - desc = "A hefty metal crate." - icon = 'icons/obj/closets/bases/large_crate.dmi' - closet_appearance = /decl/closet_appearance/large_crate - -/obj/structure/closet/crate/large/close() - . = ..() - if (.)//we can hold up to one large item - var/found = 0 - for(var/obj/structure/S in src.loc) - if(S == src) - continue - if(!S.anchored) - found = 1 - S.forceMove(src) - break - if(!found) - for(var/obj/machinery/M in src.loc) - if(!M.anchored) - M.forceMove(src) - break - return - -/obj/structure/closet/crate/large/critter - name = "animal crate" - desc = "A hefty crate for hauling animals." - closet_appearance = /decl/closet_appearance/large_crate/critter - -/obj/structure/closet/crate/large/aether - name = "large atmospherics crate" - desc = "A hefty metal crate, painted in Aether Atmospherics and Recycling colours." - closet_appearance = /decl/closet_appearance/large_crate/aether - -/obj/structure/closet/crate/large/einstein - name = "large crate" - desc = "A hefty metal crate, painted in Einstein Engines colours." - closet_appearance = /decl/closet_appearance/large_crate/einstein - -/obj/structure/closet/crate/large/nanotrasen - name = "large crate" - desc = "A hefty metal crate, painted in standard NanoTrasen livery." - closet_appearance = /decl/closet_appearance/large_crate/nanotrasen - -/obj/structure/closet/crate/large/xion - name = "large crate" - desc = "A hefty metal crate, painted in Xion Manufacturing Group orange." - closet_appearance = /decl/closet_appearance/large_crate/xion - -/obj/structure/closet/crate/secure/large - name = "large crate" - desc = "A hefty metal crate with an electronic locking system." - icon = 'icons/obj/closets/bases/large_crate.dmi' - closet_appearance = /decl/closet_appearance/large_crate/secure - - -/obj/structure/closet/crate/secure/large/close() - . = ..() - if (.)//we can hold up to one large item - var/found = 0 - for(var/obj/structure/S in src.loc) - if(S == src) - continue - if(!S.anchored) - found = 1 - S.forceMove(src) - break - if(!found) - for(var/obj/machinery/M in src.loc) - if(!M.anchored) - M.forceMove(src) - break - return - - -/obj/structure/closet/crate/secure/large/reinforced - desc = "A hefty, reinforced metal crate with an electronic locking system." - -/obj/structure/closet/crate/secure/large/aether - name = "secure atmospherics crate" - desc = "A hefty metal crate with an electronic locking system, painted in Aether Atmospherics and Recycling colours." - closet_appearance = /decl/closet_appearance/large_crate/secure/aether - -/obj/structure/closet/crate/secure/large/einstein - desc = "A hefty metal crate with an electronic locking system, painted in Einstein Engines colours." - closet_appearance = /decl/closet_appearance/large_crate/secure/einstein - -/obj/structure/closet/crate/large/secure/heph - desc = "A hefty metal crate with an electronic locking system, marked with Hephaestus Industries colours." - closet_appearance = /decl/closet_appearance/large_crate/secure/heph - -/obj/structure/closet/crate/secure/large/nanotrasen - desc = "A hefty metal crate with an electronic locking system, painted in standard NanoTrasen livery." - closet_appearance = /decl/closet_appearance/large_crate/secure/hazard - -/obj/structure/closet/crate/large/secure/xion - desc = "A hefty metal crate with an electronic locking system, painted in Xion Manufacturing Group orange." - closet_appearance = /decl/closet_appearance/large_crate/secure/xion - -/obj/structure/closet/crate/engineering - name = "engineering crate" - -/obj/structure/closet/crate/engineering/electrical - -/obj/structure/closet/crate/science - name = "science crate" - -/obj/structure/closet/crate/hydroponics - name = "hydroponics crate" - desc = "All you need to destroy those pesky weeds and pests." - closet_appearance = /decl/closet_appearance/crate/hydroponics - - -/obj/structure/closet/crate/hydroponics/prespawned - starts_with = list( - /obj/item/weapon/reagent_containers/spray/plantbgone = 2, - /obj/item/weapon/material/minihoe) - -//Laundry Cart -/obj/structure/closet/crate/laundry - name = "Laundry Cart" - desc = "A cart with a large fabric bin on it used for transporting large amounts of clothes." - icon = 'icons/obj/closets/laundry.dmi' - closet_appearance = null - open_sound = 'sound/effects/rustle1.ogg' - close_sound = 'sound/effects/rustle2.ogg' - -//Wooden Crate -/obj/structure/closet/crate/wooden - name = "wooden crate" - desc = "A crate made from wood and lined with straw. Cheapest form of storage." - icon = 'icons/obj/closets/wooden.dmi' - closet_appearance = null - open_sound = 'sound/effects/wooden_closet_open.ogg' - close_sound = 'sound/effects/wooden_closet_close.ogg' - -//Mining Cart -/obj/structure/closet/crate/miningcar - name = "mining cart" - desc = "A mining car. This one doesn't work on rails, but has to be dragged." - icon = 'icons/obj/closets/miningcar.dmi' - closet_appearance = null - open_sound = 'sound/effects/wooden_closet_open.ogg' +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/structure/closet/crate + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/closets/bases/crate.dmi' + closet_appearance = /decl/closet_appearance/crate + climbable = TRUE + dir = 4 //Spawn facing 'forward' by default. + var/points_per_crate = 5 + var/rigged = 0 + + open_sound = 'sound/effects/crate_open.ogg' + close_sound = 'sound/effects/crate_close.ogg' + +/obj/structure/closet/crate/can_open() + return 1 + +/obj/structure/closet/crate/can_close() + return 1 + +/obj/structure/closet/crate/open() + if(src.opened) + return 0 + if(!src.can_open()) + return 0 + + if(rigged && locate(/obj/item/device/radio/electropack) in src) + if(isliving(usr)) + var/mob/living/L = usr + if(L.electrocute_act(17, src)) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + if(usr.stunned) + return 2 + + playsound(src, open_sound, 50, 1, -3) + for(var/obj/O in src) + O.forceMove(get_turf(src)) + src.opened = 1 + + if(climbable) + structure_shaken() + update_icon() + return 1 + +/obj/structure/closet/crate/close() + if(!src.opened) + return 0 + if(!src.can_close()) + return 0 + + playsound(src, close_sound, 50, 1, -3) + var/itemcount = 0 + for(var/obj/O in get_turf(src)) + if(itemcount >= storage_capacity) + break + if(O.density || O.anchored || istype(O,/obj/structure/closet)) + continue + if(istype(O, /obj/structure/bed)) //This is only necessary because of rollerbeds and swivel chairs. + var/obj/structure/bed/B = O + if(B.has_buckled_mobs()) + continue + O.forceMove(src) + itemcount++ + + src.opened = 0 + update_icon() + return 1 + +/obj/structure/closet/crate/verb/rotate_clockwise() + set name = "Rotate Crate Clockwise" + set category = "Object" + set src in oview(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 270)) + +/obj/structure/closet/crate/verb/rotate_counterclockwise() + set category = "Object" + set name = "Rotate Crate Counterclockwise" + set src in view(1) + + if (usr.stat || usr.restrained() || anchored) + return + + src.set_dir(turn(src.dir, 90)) + +/obj/structure/closet/crate/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH) && istype(src,/obj/structure/closet/crate/bin)) + return ..() + else if(opened) + if(isrobot(user)) + return + if(W.loc != user) // This should stop mounted modules ending up outside the module. + return + user.drop_item() + if(W) + W.forceMove(src.loc) + else if(istype(W, /obj/item/weapon/packageWrap)) + return + else if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(rigged) + to_chat(user, "[src] is already rigged!") + return + if (C.use(1)) + to_chat(user , "You rig [src].") + rigged = 1 + return + else if(istype(W, /obj/item/device/radio/electropack)) + if(rigged) + to_chat(user , "You attach [W] to [src].") + user.drop_item() + W.forceMove(src) + return + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(rigged) + to_chat(user , "You cut away the wiring.") + playsound(src, W.usesound, 100, 1) + rigged = 0 + return + else return attack_hand(user) + +/obj/structure/closet/crate/ex_act(severity) + switch(severity) + if(1.0) + for(var/obj/O in src.contents) + qdel(O) + qdel(src) + return + if(2.0) + for(var/obj/O in src.contents) + if(prob(50)) + qdel(O) + qdel(src) + return + if(3.0) + if (prob(50)) + qdel(src) + return + else + return + +/obj/structure/closet/crate/secure + desc = "A secure crate." + name = "Secure crate" + closet_appearance = /decl/closet_appearance/crate/secure + var/broken = 0 + var/locked = 1 + +/obj/structure/closet/crate/secure/can_open() + return !locked + +/obj/structure/closet/crate/secure/update_icon() + if(opened) + icon_state = "open" + else + if(broken) + icon_state = "closed_emagged[sealed ? "_welded" : ""]" + else + if(locked) + icon_state = "closed_locked[sealed ? "_welded" : ""]" + else + icon_state = "closed_unlocked[sealed ? "_welded" : ""]" + +/obj/structure/closet/crate/secure/proc/togglelock(mob/user as mob) + if(src.opened) + to_chat(user, "Close the crate first.") + return + if(src.broken) + to_chat(user, "The crate appears to be broken.") + return + if(src.allowed(user)) + set_locked(!locked, user) + else + to_chat(user, "Access Denied") + +/obj/structure/closet/crate/secure/proc/set_locked(var/newlocked, mob/user = null) + if(locked == newlocked) return + + locked = newlocked + if(user) + for(var/mob/O in viewers(user, 3)) + O.show_message( "The crate has been [locked ? null : "un"]locked by [user].", 1) + update_icon() + +/obj/structure/closet/crate/secure/verb/verb_togglelock() + set src in oview(1) // One square distance + set category = "Object" + set name = "Toggle Lock" + + if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + + if(ishuman(usr) || isrobot(usr)) + src.add_fingerprint(usr) + src.togglelock(usr) + else + to_chat(usr, "This mob type can't use this verb.") + +/obj/structure/closet/crate/secure/attack_hand(mob/user as mob) + src.add_fingerprint(user) + if(locked) + src.togglelock(user) + else + src.toggle(user) + +/obj/structure/closet/crate/secure/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(is_type_in_list(W, list(/obj/item/weapon/packageWrap, /obj/item/stack/cable_coil, /obj/item/device/radio/electropack, /obj/item/weapon/tool/wirecutters))) + return ..() + if(istype(W, /obj/item/weapon/melee/energy/blade)) + emag_act(INFINITY, user) + if(!opened) + src.togglelock(user) + return + return ..() + +/obj/structure/closet/crate/secure/emag_act(var/remaining_charges, var/mob/user) + if(!broken) + playsound(src, "sparks", 60, 1) + locked = 0 + broken = 1 + to_chat(user, "You unlock \the [src].") + update_icon() + return 1 + +/obj/structure/closet/crate/secure/emp_act(severity) + for(var/obj/O in src) + O.emp_act(severity) + if(!broken && !opened && prob(50/severity)) + if(!locked) + locked = 1 + else + playsound(src, 'sound/effects/sparks4.ogg', 75, 1) + locked = 0 + if(!opened && prob(20/severity)) + if(!locked) + open() + else + req_access = list() + req_access += pick(get_all_station_access()) + update_icon() + ..() + +/obj/structure/closet/crate/plastic + name = "plastic crate" + desc = "A rectangular plastic crate." + closet_appearance = /decl/closet_appearance/crate/plastic + points_per_crate = 1 //5 crates per ordered crate, +5 for the crate it comes in. + +/obj/structure/closet/crate/internals + name = "internals crate" + desc = "A internals crate." + +/obj/structure/closet/crate/trashcart + name = "trash cart" + desc = "A heavy, metal trashcart with wheels." + closet_appearance = /decl/closet_appearance/cart/trash + +/*these aren't needed anymore +/obj/structure/closet/crate/hat + desc = "A crate filled with Valuable Collector's Hats!." + name = "Hat Crate" + +/obj/structure/closet/crate/contraband + name = "Poster crate" + desc = "A random assortment of posters manufactured by providers NOT listed under NanoTrasen's whitelist." +*/ + +/obj/structure/closet/crate/medical + name = "medical crate" + desc = "A medical crate." + closet_appearance = /decl/closet_appearance/crate/medical + +/obj/structure/closet/crate/rcd + name = "\improper RCD crate" + desc = "A crate with rapid construction device." + + starts_with = list( + /obj/item/weapon/rcd_ammo = 3, + /obj/item/weapon/rcd) + +/obj/structure/closet/crate/solar + name = "solar pack crate" + + starts_with = list( + /obj/item/solar_assembly = 21, + /obj/item/weapon/circuitboard/solar_control, + /obj/item/weapon/tracker_electronics, + /obj/item/weapon/paper/solar) + +/obj/structure/closet/crate/freezer + name = "freezer" + desc = "A freezer." + closet_appearance = /decl/closet_appearance/crate/freezer + var/target_temp = T0C - 40 + var/cooling_power = 40 + +/obj/structure/closet/crate/freezer/centauri + desc = "A freezer stamped with the logo of Centauri Provisions." + closet_appearance = /decl/closet_appearance/crate/freezer/centauri + +/obj/structure/closet/crate/freezer/nanotrasen + desc = "A freezer stamped with the logo of NanoTrasen." + closet_appearance = /decl/closet_appearance/crate/freezer/nanotrasen + +/obj/structure/closet/crate/freezer/veymed + desc = "A freezer stamped with the logo of Vey-Medical." + closet_appearance = /decl/closet_appearance/crate/freezer/veymed + +/obj/structure/closet/crate/freezer/zenghu + desc = "A freezer stamped with the logo of Zeng-Hu Pharmaceuticals." + closet_appearance = /decl/closet_appearance/crate/freezer/zenghu + +/obj/structure/closet/crate/freezer/return_air() + var/datum/gas_mixture/gas = (..()) + if(!gas) return null + var/datum/gas_mixture/newgas = new/datum/gas_mixture() + newgas.copy_from(gas) + if(newgas.temperature <= target_temp) return + + if((newgas.temperature - cooling_power) > target_temp) + newgas.temperature -= cooling_power + else + newgas.temperature = target_temp + return newgas + +/obj/structure/closet/crate/freezer/Entered(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 1 + for(var/obj/item/organ/organ in O) + organ.preserved = 1 + ..() + +/obj/structure/closet/crate/freezer/Exited(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 0 + for(var/obj/item/organ/organ in O) + organ.preserved = 0 + ..() + +/obj/structure/closet/crate/weapon + name = "weapons crate" + desc = "A barely secured weapons crate." + closet_appearance = /decl/closet_appearance/crate/secure/weapon + +/obj/structure/closet/crate/freezer/rations //Fpr use in the escape shuttle + name = "emergency rations" + desc = "A crate of emergency rations." + + starts_with = list( + /obj/random/mre = 6) + +/obj/structure/closet/crate/bin + name = "large bin" + desc = "A large bin." + closet_appearance = null + icon = 'icons/obj/closets/largebin.dmi' + +/obj/structure/closet/crate/radiation + name = "radioactive gear crate" + desc = "A crate with a radiation sign on it." + closet_appearance = /decl/closet_appearance/crate/radiation + + starts_with = list( + /obj/item/clothing/suit/radiation = 4, + /obj/item/clothing/head/radiation = 4) + +//TSCs + +/obj/structure/closet/crate/aether + desc = "A crate painted in the colours of Aether Atmospherics and Recycling." + closet_appearance = /decl/closet_appearance/crate/aether + +/obj/structure/closet/crate/centauri + desc = "A crate decorated with the logo of Centauri Provisions." + closet_appearance = /decl/closet_appearance/crate/centauri + +/obj/structure/closet/crate/einstein + desc = "A crate labelled with an Einstein Engines sticker." + closet_appearance = /decl/closet_appearance/crate/einstein + +/obj/structure/closet/crate/focalpoint + desc = "A crate marked with the decal of Focal Point Energistics." + closet_appearance = /decl/closet_appearance/crate/focalpoint + +/obj/structure/closet/crate/gilthari + desc = "A crate embossed with the logo of Gilthari Exports." + closet_appearance = /decl/closet_appearance/crate/gilthari + +/obj/structure/closet/crate/grayson + desc = "A bare metal crate spraypainted with Grayson Manufactories decals." + closet_appearance = /decl/closet_appearance/crate/grayson + +/obj/structure/closet/crate/heph + desc = "A sturdy crate marked with the logo of Hephaestus Industries." + closet_appearance = /decl/closet_appearance/crate/heph + +/obj/structure/closet/crate/morpheus + desc = "A crate crudely imprinted with 'MORPHEUS CYBERKINETICS'." + closet_appearance = /decl/closet_appearance/crate/morpheus + +/obj/structure/closet/crate/nanotrasen + desc = "A crate emblazoned with the standard NanoTrasen livery." + closet_appearance = /decl/closet_appearance/crate/nanotrasen + +/obj/structure/closet/crate/nanothreads + desc = "A crate emblazoned with the NanoThreads Garments livery, a subsidary of the NanoTrasen Corporation." + closet_appearance = /decl/closet_appearance/crate/nanotrasenclothing + +/obj/structure/closet/crate/nanomed + desc = "A crate emblazoned with the NanoMed Medical livery, a subsidary of the NanoTrasen Corporation." + closet_appearance = /decl/closet_appearance/crate/nanotrasenmedical + +/obj/structure/closet/crate/oculum + desc = "A crate minimally decorated with the logo of media giant Oculum Broadcast." + closet_appearance = /decl/closet_appearance/crate/oculum + +/obj/structure/closet/crate/veymed + desc = "A sterile crate extensively detailed in Veymed colours." + closet_appearance = /decl/closet_appearance/crate/veymed + +/obj/structure/closet/crate/ward + desc = "A crate decaled with the logo of Ward-Takahashi." + closet_appearance = /decl/closet_appearance/crate/ward + +/obj/structure/closet/crate/xion + desc = "A crate painted in Xion Manufacturing Group orange." + closet_appearance = /decl/closet_appearance/crate/xion + +/obj/structure/closet/crate/zenghu + desc = "A sterile crate marked with the logo of Zeng-Hu Pharmaceuticals." + closet_appearance = /decl/closet_appearance/crate/zenghu + +// Brands/subsidiaries + +/obj/structure/closet/crate/allico + desc = "A crate painted in the distinctive cheerful colours of AlliCo. Ltd." + closet_appearance = /decl/closet_appearance/crate/allico + +/obj/structure/closet/crate/carp + desc = "A crate painted with the garish livery of Consolidated Agricultural Resources Plc." + closet_appearance = /decl/closet_appearance/crate/carp + +/obj/structure/closet/crate/hedberg + name = "weapons crate" + desc = "A weapons crate stamped with the logo of Hedberg-Hammarstrom and the lock conspicuously absent." + closet_appearance = /decl/closet_appearance/crate/secure/hedberg + +/obj/structure/closet/crate/galaksi + desc = "A crate printed with the markings of Ward-Takahashi's Galaksi Appliance branding." + closet_appearance = /decl/closet_appearance/crate/galaksi + +/obj/structure/closet/crate/thinktronic + desc = "A crate printed with the markings of Thinktronic Systems." + closet_appearance = /decl/closet_appearance/crate/thinktronic + +/obj/structure/closet/crate/ummarcar + desc = "A flimsy crate marked labelled 'UmMarcar Office Supply'." + closet_appearance = /decl/closet_appearance/crate/ummarcar + +/obj/structure/closet/crate/unathi + name = "import crate" + desc = "A crate painted with the markings of Moghes Imported Sissalik Jerky." + closet_appearance = /decl/closet_appearance/crate/unathiimport + + +// Secure Crates + +/obj/structure/closet/crate/secure/weapon + name = "weapons crate" + desc = "A secure weapons crate." + closet_appearance = /decl/closet_appearance/crate/secure/weapon + +/obj/structure/closet/crate/secure/aether + desc = "A secure crate painted in the colours of Aether Atmospherics and Recycling." + closet_appearance = /decl/closet_appearance/crate/secure/aether + +/obj/structure/closet/crate/secure/bishop + desc = "A secure crate finely decorated with the emblem of Bishop Cybernetics." + closet_appearance = /decl/closet_appearance/crate/secure/bishop + +/obj/structure/closet/crate/secure/cybersolutions + desc = "An unadorned secure metal crate labelled 'Cyber Solutions'." + closet_appearance = /decl/closet_appearance/crate/secure/cybersolutions + +/obj/structure/closet/crate/secure/einstein + desc = "A secure crate labelled with an Einstein Engines sticker." + closet_appearance = /decl/closet_appearance/crate/secure/einstein + +/obj/structure/closet/crate/secure/focalpoint + desc = "A secure crate marked with the decal of Focal Point Energistics." + closet_appearance = /decl/closet_appearance/crate/secure/focalpoint + +/obj/structure/closet/crate/secure/gilthari + desc = "A secure crate embossed with the logo of Gilthari Exports." + closet_appearance = /decl/closet_appearance/crate/secure/gilthari + +/obj/structure/closet/crate/secure/grayson + desc = "A secure bare metal crate spraypainted with Grayson Manufactories decals." + closet_appearance = /decl/closet_appearance/crate/secure/grayson + +/obj/structure/closet/crate/secure/hedberg + name = "weapons crate" + desc = "A secure weapons crate stamped with the logo of Hedberg-Hammarstrom." + closet_appearance = /decl/closet_appearance/crate/secure/hedberg + +/obj/structure/closet/crate/secure/heph + name = "weapons crate" + desc = "A secure weapons crate marked with the logo of Hephaestus Industries." + closet_appearance = /decl/closet_appearance/crate/secure/heph + +/obj/structure/closet/crate/secure/lawson + name = "weapons crate" + desc = "A secure weapons crate marked with the logo of Lawson Arms." + closet_appearance = /decl/closet_appearance/crate/secure/lawson + +/obj/structure/closet/crate/secure/morpheus + desc = "A secure crate crudely imprinted with 'MORPHEUS CYBERKINETICS'." + closet_appearance = /decl/closet_appearance/crate/secure/morpheus + +/obj/structure/closet/crate/secure/nanotrasen + desc = "A secure crate emblazoned with the standard NanoTrasen livery." + closet_appearance = /decl/closet_appearance/crate/secure/nanotrasen + +/obj/structure/closet/crate/secure/nanomed + desc = "A secure crate emblazoned with the NanoMed Medical livery, a subsidary of the NanoTrasen Corporation." + closet_appearance = /decl/closet_appearance/crate/secure/nanotrasenmedical + +/obj/structure/closet/crate/secure/scg + name = "weapons crate" + desc = "A secure crate in the official colours of the Solar Confederate Government." + closet_appearance = /decl/closet_appearance/crate/secure/solgov + +/obj/structure/closet/crate/secure/saare + name = "weapons crate" + desc = "A secure weapons crate plainly stamped with the logo of Stealth Assault Enterprises." + closet_appearance = /decl/closet_appearance/crate/secure/saare + +/obj/structure/closet/crate/secure/veymed + desc = "A secure sterile crate extensively detailed in Veymed colours." + closet_appearance = /decl/closet_appearance/crate/secure/veymed + +/obj/structure/closet/crate/secure/ward + desc = "A secure crate decaled with the logo of Ward-Takahashi." + closet_appearance = /decl/closet_appearance/crate/secure/ward + +/obj/structure/closet/crate/secure/xion + desc = "A secure crate painted in Xion Manufacturing Group orange." + closet_appearance = /decl/closet_appearance/crate/secure/xion + +/obj/structure/closet/crate/secure/zenghu + desc = "A secure sterile crate marked with the logo of Zeng-Hu Pharmaceuticals." + closet_appearance = /decl/closet_appearance/crate/secure/zenghu + +/obj/structure/closet/crate/secure/phoron + name = "phoron crate" + desc = "A secure phoron crate painted in standard NanoTrasen livery." + closet_appearance = /decl/closet_appearance/crate/secure/hazard + +/obj/structure/closet/crate/secure/gear + name = "gear crate" + desc = "A secure gear crate." + closet_appearance = /decl/closet_appearance/crate/secure/weapon + +/obj/structure/closet/crate/secure/hydrosec + name = "secure hydroponics crate" + desc = "A crate with a lock on it, painted in the scheme of the station's botanists." + closet_appearance = /decl/closet_appearance/crate/secure/hydroponics + +/obj/structure/closet/crate/secure/engineering + desc = "A crate with a lock on it, painted in the scheme of the station's engineers." + name = "secure engineering crate" + +/obj/structure/closet/crate/secure/science + name = "secure science crate" + desc = "A crate with a lock on it, painted in the scheme of the station's scientists." + +/obj/structure/closet/crate/secure/bin + name = "secure bin" + desc = "A secure bin." + +// Large crates + +/obj/structure/closet/crate/large + name = "large crate" + desc = "A hefty metal crate." + icon = 'icons/obj/closets/bases/large_crate.dmi' + closet_appearance = /decl/closet_appearance/large_crate + +/obj/structure/closet/crate/large/close() + . = ..() + if (.)//we can hold up to one large item + var/found = 0 + for(var/obj/structure/S in src.loc) + if(S == src) + continue + if(!S.anchored) + found = 1 + S.forceMove(src) + break + if(!found) + for(var/obj/machinery/M in src.loc) + if(!M.anchored) + M.forceMove(src) + break + return + +/obj/structure/closet/crate/large/critter + name = "animal crate" + desc = "A hefty crate for hauling animals." + closet_appearance = /decl/closet_appearance/large_crate/critter + +/obj/structure/closet/crate/large/aether + name = "large atmospherics crate" + desc = "A hefty metal crate, painted in Aether Atmospherics and Recycling colours." + closet_appearance = /decl/closet_appearance/large_crate/aether + +/obj/structure/closet/crate/large/einstein + name = "large crate" + desc = "A hefty metal crate, painted in Einstein Engines colours." + closet_appearance = /decl/closet_appearance/large_crate/einstein + +/obj/structure/closet/crate/large/nanotrasen + name = "large crate" + desc = "A hefty metal crate, painted in standard NanoTrasen livery." + closet_appearance = /decl/closet_appearance/large_crate/nanotrasen + +/obj/structure/closet/crate/large/xion + name = "large crate" + desc = "A hefty metal crate, painted in Xion Manufacturing Group orange." + closet_appearance = /decl/closet_appearance/large_crate/xion + +/obj/structure/closet/crate/secure/large + name = "large crate" + desc = "A hefty metal crate with an electronic locking system." + icon = 'icons/obj/closets/bases/large_crate.dmi' + closet_appearance = /decl/closet_appearance/large_crate/secure + + +/obj/structure/closet/crate/secure/large/close() + . = ..() + if (.)//we can hold up to one large item + var/found = 0 + for(var/obj/structure/S in src.loc) + if(S == src) + continue + if(!S.anchored) + found = 1 + S.forceMove(src) + break + if(!found) + for(var/obj/machinery/M in src.loc) + if(!M.anchored) + M.forceMove(src) + break + return + + +/obj/structure/closet/crate/secure/large/reinforced + desc = "A hefty, reinforced metal crate with an electronic locking system." + +/obj/structure/closet/crate/secure/large/aether + name = "secure atmospherics crate" + desc = "A hefty metal crate with an electronic locking system, painted in Aether Atmospherics and Recycling colours." + closet_appearance = /decl/closet_appearance/large_crate/secure/aether + +/obj/structure/closet/crate/secure/large/einstein + desc = "A hefty metal crate with an electronic locking system, painted in Einstein Engines colours." + closet_appearance = /decl/closet_appearance/large_crate/secure/einstein + +/obj/structure/closet/crate/large/secure/heph + desc = "A hefty metal crate with an electronic locking system, marked with Hephaestus Industries colours." + closet_appearance = /decl/closet_appearance/large_crate/secure/heph + +/obj/structure/closet/crate/secure/large/nanotrasen + desc = "A hefty metal crate with an electronic locking system, painted in standard NanoTrasen livery." + closet_appearance = /decl/closet_appearance/large_crate/secure/hazard + +/obj/structure/closet/crate/large/secure/xion + desc = "A hefty metal crate with an electronic locking system, painted in Xion Manufacturing Group orange." + closet_appearance = /decl/closet_appearance/large_crate/secure/xion + +/obj/structure/closet/crate/engineering + name = "engineering crate" + +/obj/structure/closet/crate/engineering/electrical + +/obj/structure/closet/crate/science + name = "science crate" + +/obj/structure/closet/crate/hydroponics + name = "hydroponics crate" + desc = "All you need to destroy those pesky weeds and pests." + closet_appearance = /decl/closet_appearance/crate/hydroponics + + +/obj/structure/closet/crate/hydroponics/prespawned + starts_with = list( + /obj/item/weapon/reagent_containers/spray/plantbgone = 2, + /obj/item/weapon/material/minihoe) + +//Laundry Cart +/obj/structure/closet/crate/laundry + name = "Laundry Cart" + desc = "A cart with a large fabric bin on it used for transporting large amounts of clothes." + icon = 'icons/obj/closets/laundry.dmi' + closet_appearance = null + open_sound = 'sound/effects/rustle1.ogg' + close_sound = 'sound/effects/rustle2.ogg' + +//Wooden Crate +/obj/structure/closet/crate/wooden + name = "wooden crate" + desc = "A crate made from wood and lined with straw. Cheapest form of storage." + icon = 'icons/obj/closets/wooden.dmi' + closet_appearance = null + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + +//Chest +/obj/structure/closet/crate/chest + name = "chest" + desc = "A fancy chest made from wood and lined with red velvet." + icon = 'icons/obj/closets/chest.dmi' + closet_appearance = null + open_sound = 'sound/effects/wooden_closet_open.ogg' + close_sound = 'sound/effects/wooden_closet_close.ogg' + +//Mining Cart +/obj/structure/closet/crate/miningcar + name = "mining cart" + desc = "A mining car. This one doesn't work on rails, but has to be dragged." + icon = 'icons/obj/closets/miningcar.dmi' + closet_appearance = null + open_sound = 'sound/effects/wooden_closet_open.ogg' close_sound = 'sound/effects/wooden_closet_close.ogg' \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/crates_vr.dm b/code/game/objects/structures/crates_lockers/crates_vr.dm index 5ebf9894e93..8825c25d834 100644 --- a/code/game/objects/structures/crates_lockers/crates_vr.dm +++ b/code/game/objects/structures/crates_lockers/crates_vr.dm @@ -7,7 +7,7 @@ if(locked && tamper_proof && health <= Proj.damage) if(tamper_proof == 2) // Mainly used for events to prevent any chance of opening the box improperly. - visible_message("The anti-tamper mechanism of [src] triggers an explosion!") + visible_message(span_red("The anti-tamper mechanism of [src] triggers an explosion!")) var/turf/T = get_turf(src.loc) explosion(T, 0, 0, 0, 1) // Non-damaging, but it'll alert security. qdel(src) @@ -15,18 +15,18 @@ var/open_chance = rand(1,5) switch(open_chance) if(1) - visible_message("The anti-tamper mechanism of [src] causes an explosion!") + visible_message(span_red("The anti-tamper mechanism of [src] causes an explosion!")) var/turf/T = get_turf(src.loc) explosion(T, 0, 0, 0, 1) // Non-damaging, but it'll alert security. qdel(src) if(2 to 4) - visible_message("The anti-tamper mechanism of [src] causes a small fire!") + visible_message(span_red("The anti-tamper mechanism of [src] causes a small fire!")) for(var/atom/movable/A as mob|obj in src) // For every item in the box, we spawn a pile of ash. new /obj/effect/decal/cleanable/ash(src.loc) new /obj/fire(src.loc) qdel(src) if(5) - visible_message("The anti-tamper mechanism of [src] fails!") + visible_message(span_green("The anti-tamper mechanism of [src] fails!")) return ..() diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 6b338895e30..d9902638ffc 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -1,132 +1,132 @@ -/obj/structure/largecrate - name = "large crate" - desc = "A hefty wooden crate." - icon = 'icons/obj/storage.dmi' - icon_state = "densecrate" - density = TRUE - var/list/starts_with - -/obj/structure/largecrate/Initialize() - . = ..() - if(starts_with) - create_objects_in_loc(src, starts_with) - starts_with = null - for(var/obj/I in src.loc) - if(I.density || I.anchored || I == src || !I.simulated) - continue - I.forceMove(src) - update_icon() - -/obj/structure/largecrate/attack_hand(mob/user as mob) - to_chat(user, "You need a crowbar to pry this open!") - return - -/obj/structure/largecrate/attackby(obj/item/weapon/W as obj, mob/user as mob) - var/turf/T = get_turf(src) - if(!T) - to_chat(user, "You can't open this here!") - if(W.is_crowbar()) - new /obj/item/stack/material/wood(src) - - for(var/atom/movable/AM in contents) - if(AM.simulated) - AM.forceMove(T) - //VOREStation Add Start - if(isanimal(AM)) - var/mob/living/simple_mob/AMBLINAL = AM - if(!AMBLINAL.mind) - AMBLINAL.ghostjoin = 1 - AMBLINAL.ghostjoin_icon() - active_ghost_pods |= AMBLINAL - //VOREStation Add End - user.visible_message("[user] pries \the [src] open.", \ - "You pry open \the [src].", \ - "You hear splitting wood.") - qdel(src) - else - return attack_hand(user) - -/obj/structure/largecrate/mule - name = "MULE crate" - -/obj/structure/largecrate/hoverpod - name = "\improper Hoverpod assembly crate" - desc = "You aren't sure how this crate is so light, but the Wulf Aeronautics logo might be a hint." - icon_state = "vehiclecrate" - -/obj/structure/largecrate/hoverpod/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_crowbar()) - var/obj/item/mecha_parts/mecha_equipment/ME - var/obj/mecha/working/hoverpod/H = new (loc) - - ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp - ME.attach(H) - ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger - ME.attach(H) - ..() - -/obj/structure/largecrate/donksoftvendor - name = "\improper Donk-Soft vendor crate" - desc = "A hefty wooden crate displaying the logo of Donk-Soft. It's rather heavy." - starts_with = list(/obj/machinery/vending/donksoft) - -/obj/structure/largecrate/vehicle - name = "vehicle crate" - desc = "Wulf Aeronautics says it comes in a box for the consumer's sake... How is this so light?" - icon_state = "vehiclecrate" - -/obj/structure/largecrate/vehicle/Initialize() - . = ..() - for(var/obj/O in contents) - O.update_icon() - -/obj/structure/largecrate/vehicle/bike - name = "spacebike crate" - starts_with = list(/obj/structure/vehiclecage/spacebike) - -/obj/structure/largecrate/vehicle/quadbike - name = "\improper ATV crate" - desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." - starts_with = list(/obj/structure/vehiclecage/quadbike) - -/obj/structure/largecrate/vehicle/quadtrailer - name = "\improper ATV trailer crate" - desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." - starts_with = list(/obj/structure/vehiclecage/quadtrailer) - -/obj/structure/largecrate/animal - icon_state = "crittercrate" - desc = "A hefty wooden crate with air holes. It is marked with the logo of NanoTrasen Pastures and the slogan, '90% less cloning defects* than competing brands**, or your money back***!'" - -/obj/structure/largecrate/animal/mulebot - name = "Mulebot crate" - desc = "A hefty wooden crate labelled 'Proud Product of the Xion Manufacturing Group'" - icon_state = "mulecrate" - starts_with = list(/mob/living/bot/mulebot) - -/obj/structure/largecrate/animal/corgi - name = "corgi carrier" - starts_with = list(/mob/living/simple_mob/animal/passive/dog/corgi) - -/obj/structure/largecrate/animal/cow - name = "cow crate" - starts_with = list(/mob/living/simple_mob/animal/passive/cow) - -/obj/structure/largecrate/animal/goat - name = "goat crate" - starts_with = list(/mob/living/simple_mob/animal/goat) - -/obj/structure/largecrate/animal/cat - name = "cat carrier" - starts_with = list(/mob/living/simple_mob/animal/passive/cat) - -/obj/structure/largecrate/animal/cat/bones - starts_with = list(/mob/living/simple_mob/animal/passive/cat/bones) - -/obj/structure/largecrate/animal/chick - name = "chicken crate" - starts_with = list(/mob/living/simple_mob/animal/passive/chick = 5) - -/obj/structure/largecrate/animal/catslug - name = "catslug carrier" - starts_with = list(/mob/living/simple_mob/vore/alienanimals/catslug) +/obj/structure/largecrate + name = "large crate" + desc = "A hefty wooden crate." + icon = 'icons/obj/storage.dmi' + icon_state = "densecrate" + density = TRUE + var/list/starts_with + +/obj/structure/largecrate/Initialize() + . = ..() + if(starts_with) + create_objects_in_loc(src, starts_with) + starts_with = null + for(var/obj/I in src.loc) + if(I.density || I.anchored || I == src || !I.simulated) + continue + I.forceMove(src) + update_icon() + +/obj/structure/largecrate/attack_hand(mob/user as mob) + to_chat(user, "You need a crowbar to pry this open!") + return + +/obj/structure/largecrate/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/turf/T = get_turf(src) + if(!T) + to_chat(user, "You can't open this here!") + if(W.has_tool_quality(TOOL_CROWBAR)) + new /obj/item/stack/material/wood(src) + + for(var/atom/movable/AM in contents) + if(AM.simulated) + AM.forceMove(T) + //VOREStation Add Start + if(isanimal(AM)) + var/mob/living/simple_mob/AMBLINAL = AM + if(!AMBLINAL.mind) + AMBLINAL.ghostjoin = 1 + AMBLINAL.ghostjoin_icon() + active_ghost_pods |= AMBLINAL + //VOREStation Add End + user.visible_message("[user] pries \the [src] open.", \ + "You pry open \the [src].", \ + "You hear splitting wood.") + qdel(src) + else + return attack_hand(user) + +/obj/structure/largecrate/mule + name = "MULE crate" + +/obj/structure/largecrate/hoverpod + name = "\improper Hoverpod assembly crate" + desc = "You aren't sure how this crate is so light, but the Wulf Aeronautics logo might be a hint." + icon_state = "vehiclecrate" + +/obj/structure/largecrate/hoverpod/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_CROWBAR)) + var/obj/item/mecha_parts/mecha_equipment/ME + var/obj/mecha/working/hoverpod/H = new (loc) + + ME = new /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp + ME.attach(H) + ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger + ME.attach(H) + ..() + +/obj/structure/largecrate/donksoftvendor + name = "\improper Donk-Soft vendor crate" + desc = "A hefty wooden crate displaying the logo of Donk-Soft. It's rather heavy." + starts_with = list(/obj/machinery/vending/donksoft) + +/obj/structure/largecrate/vehicle + name = "vehicle crate" + desc = "Wulf Aeronautics says it comes in a box for the consumer's sake... How is this so light?" + icon_state = "vehiclecrate" + +/obj/structure/largecrate/vehicle/Initialize() + . = ..() + for(var/obj/O in contents) + O.update_icon() + +/obj/structure/largecrate/vehicle/bike + name = "spacebike crate" + starts_with = list(/obj/structure/vehiclecage/spacebike) + +/obj/structure/largecrate/vehicle/quadbike + name = "\improper ATV crate" + desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." + starts_with = list(/obj/structure/vehiclecage/quadbike) + +/obj/structure/largecrate/vehicle/quadtrailer + name = "\improper ATV trailer crate" + desc = "A hefty wooden crate proudly displaying the logo of Ward-Takahashi's automotive division." + starts_with = list(/obj/structure/vehiclecage/quadtrailer) + +/obj/structure/largecrate/animal + icon_state = "crittercrate" + desc = "A hefty wooden crate with air holes. It is marked with the logo of NanoTrasen Pastures and the slogan, '90% less cloning defects* than competing brands**, or your money back***!'" + +/obj/structure/largecrate/animal/mulebot + name = "Mulebot crate" + desc = "A hefty wooden crate labelled 'Proud Product of the Xion Manufacturing Group'" + icon_state = "mulecrate" + starts_with = list(/mob/living/bot/mulebot) + +/obj/structure/largecrate/animal/corgi + name = "corgi carrier" + starts_with = list(/mob/living/simple_mob/animal/passive/dog/corgi) + +/obj/structure/largecrate/animal/cow + name = "cow crate" + starts_with = list(/mob/living/simple_mob/animal/passive/cow) + +/obj/structure/largecrate/animal/goat + name = "goat crate" + starts_with = list(/mob/living/simple_mob/animal/goat) + +/obj/structure/largecrate/animal/cat + name = "cat carrier" + starts_with = list(/mob/living/simple_mob/animal/passive/cat) + +/obj/structure/largecrate/animal/cat/bones + starts_with = list(/mob/living/simple_mob/animal/passive/cat/bones) + +/obj/structure/largecrate/animal/chick + name = "chicken crate" + starts_with = list(/mob/living/simple_mob/animal/passive/chick = 5) + +/obj/structure/largecrate/animal/catslug + name = "catslug carrier" + starts_with = list(/mob/living/simple_mob/vore/alienanimals/catslug) diff --git a/code/game/objects/structures/crates_lockers/largecrate_vr.dm b/code/game/objects/structures/crates_lockers/largecrate_vr.dm index 85737c24815..7b9f9c0a4af 100644 --- a/code/game/objects/structures/crates_lockers/largecrate_vr.dm +++ b/code/game/objects/structures/crates_lockers/largecrate_vr.dm @@ -3,7 +3,7 @@ desc = "You hear chirping and cawing inside the crate. It sounds like there are a lot of birds in there..." /obj/structure/largecrate/birds/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_crowbar()) + if(W.has_tool_quality(TOOL_CROWBAR)) new /obj/item/stack/material/wood(src) new /mob/living/simple_mob/animal/passive/bird(src) new /mob/living/simple_mob/animal/passive/bird/parrot/kea(src) diff --git a/code/game/objects/structures/crates_lockers/vehiclecage.dm b/code/game/objects/structures/crates_lockers/vehiclecage.dm index c3a04e9267f..944da792f83 100644 --- a/code/game/objects/structures/crates_lockers/vehiclecage.dm +++ b/code/game/objects/structures/crates_lockers/vehiclecage.dm @@ -31,11 +31,11 @@ var/turf/T = get_turf(src) if(!T) to_chat(user, "You can't open this here!") - if(W.is_wrench() && do_after(user, 60 * W.toolspeed, src)) + if(W.has_tool_quality(TOOL_WRENCH) && do_after(user, 60 * W.toolspeed, src)) playsound(src, W.usesound, 50, 1) disassemble(W, user) user.visible_message("[user] begins loosening \the [src]'s bolts.") - if(W.is_wirecutter() && do_after(user, 70 * W.toolspeed, src)) + if(W.has_tool_quality(TOOL_WIRECUTTER) && do_after(user, 70 * W.toolspeed, src)) playsound(src, W.usesound, 50, 1) disassemble(W, user) user.visible_message("[user] begins cutting \the [src]'s bolts.") diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index f40a8278068..714eb9eb532 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -38,7 +38,7 @@ layer = OBJ_LAYER /obj/structure/curtain/attackby(obj/item/P, mob/user) - if(P.is_wirecutter()) + if(P.has_tool_quality(TOOL_WIRECUTTER)) playsound(src, P.usesound, 50, 1) to_chat(user, "You start to cut the shower curtains.") if(do_after(user, 10)) diff --git a/code/game/objects/structures/dancepole_vr.dm b/code/game/objects/structures/dancepole_vr.dm index 90270b88ce3..739d50643ce 100644 --- a/code/game/objects/structures/dancepole_vr.dm +++ b/code/game/objects/structures/dancepole_vr.dm @@ -20,14 +20,14 @@ layer = BELOW_MOB_LAYER /obj/structure/dancepole/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(O.is_screwdriver()) + if(O.has_tool_quality(TOOL_SCREWDRIVER)) anchored = !anchored playsound(src, O.usesound, 50, 1) if(anchored) - to_chat(user, "You secure \the [src].") + to_chat(user, span_blue("You secure \the [src].")) else - to_chat(user, "You unsecure \the [src].") - if(O.is_wrench()) + to_chat(user, span_blue("You unsecure \the [src].")) + if(O.has_tool_quality(TOOL_WRENCH)) playsound(src, O.usesound, 50, 1) to_chat(user, "Now disassembling \the [src]...") if(do_after(user, 30 * O.toolspeed)) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index fc1ae0ef597..362d966ae5c 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -1,81 +1,81 @@ -/obj/structure/displaycase - name = "display case" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "glassbox1" - desc = "A display case for prized possessions. It taunts you to kick it." - density = TRUE - anchored = TRUE - unacidable = TRUE//Dissolving the case would also delete the gun. - var/health = 30 - var/occupied = 1 - var/destroyed = 0 - -/obj/structure/displaycase/ex_act(severity) - switch(severity) - if (1) - new /obj/item/weapon/material/shard( src.loc ) - if (occupied) - new /obj/item/weapon/gun/energy/captain( src.loc ) - occupied = 0 - qdel(src) - if (2) - if (prob(50)) - src.health -= 15 - src.healthcheck() - if (3) - if (prob(50)) - src.health -= 5 - src.healthcheck() - - -/obj/structure/displaycase/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.get_structure_damage() - ..() - src.healthcheck() - return - -/obj/structure/displaycase/proc/healthcheck() - if (src.health <= 0) - if (!( src.destroyed )) - src.density = FALSE - src.destroyed = 1 - new /obj/item/weapon/material/shard( src.loc ) - playsound(src, "shatter", 70, 1) - update_icon() - else - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - return - -/obj/structure/displaycase/update_icon() - if(src.destroyed) - src.icon_state = "glassboxb[src.occupied]" - else - src.icon_state = "glassbox[src.occupied]" - return - - -/obj/structure/displaycase/attackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(user.get_attack_speed(W)) - user.do_attack_animation(src) - playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) - src.health -= W.force - src.healthcheck() - ..() - return - -/obj/structure/displaycase/attack_hand(mob/user as mob) - if (src.destroyed && src.occupied) - new /obj/item/weapon/gun/energy/captain( src.loc ) - to_chat(user, "You deactivate the hover field built into the case.") - src.occupied = 0 - src.add_fingerprint(user) - update_icon() - return - else - to_chat(usr, "You kick the display case.") - for(var/mob/O in oviewers()) - if ((O.client && !( O.blinded ))) - to_chat(O, "[usr] kicks the display case.") - src.health -= 2 - healthcheck() - return +/obj/structure/displaycase + name = "display case" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "glassbox1" + desc = "A display case for prized possessions. It taunts you to kick it." + density = TRUE + anchored = TRUE + unacidable = TRUE//Dissolving the case would also delete the gun. + var/health = 30 + var/occupied = 1 + var/destroyed = 0 + +/obj/structure/displaycase/ex_act(severity) + switch(severity) + if (1) + new /obj/item/weapon/material/shard( src.loc ) + if (occupied) + new /obj/item/weapon/gun/energy/captain( src.loc ) + occupied = 0 + qdel(src) + if (2) + if (prob(50)) + src.health -= 15 + src.healthcheck() + if (3) + if (prob(50)) + src.health -= 5 + src.healthcheck() + + +/obj/structure/displaycase/bullet_act(var/obj/item/projectile/Proj) + health -= Proj.get_structure_damage() + ..() + src.healthcheck() + return + +/obj/structure/displaycase/proc/healthcheck() + if (src.health <= 0) + if (!( src.destroyed )) + src.density = FALSE + src.destroyed = 1 + new /obj/item/weapon/material/shard( src.loc ) + playsound(src, "shatter", 70, 1) + update_icon() + else + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + return + +/obj/structure/displaycase/update_icon() + if(src.destroyed) + src.icon_state = "glassboxb[src.occupied]" + else + src.icon_state = "glassbox[src.occupied]" + return + + +/obj/structure/displaycase/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(user.get_attack_speed(W)) + user.do_attack_animation(src) + playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) + src.health -= W.force + src.healthcheck() + ..() + return + +/obj/structure/displaycase/attack_hand(mob/user as mob) + if (src.destroyed && src.occupied) + new /obj/item/weapon/gun/energy/captain( src.loc ) + to_chat(user, "You deactivate the hover field built into the case.") + src.occupied = 0 + src.add_fingerprint(user) + update_icon() + return + else + to_chat(usr, "You kick the display case.") + for(var/mob/O in oviewers()) + if ((O.client && !( O.blinded ))) + to_chat(O, "[usr] kicks the display case.") + src.health -= 2 + healthcheck() + return diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index a484aa3efef..3a024f647b8 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -1,329 +1,329 @@ -/obj/structure/door_assembly - name = "airlock assembly" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_as_0" - anchored = FALSE - density = TRUE - w_class = ITEMSIZE_HUGE - var/state = 0 - var/base_icon_state = "" - var/base_name = "airlock" - var/obj/item/weapon/airlock_electronics/electronics = null - var/airlock_type = "" //the type path of the airlock once completed - var/glass_type = "/glass" - var/glass = 0 // 0 = glass can be installed. -1 = glass can't be installed. 1 = glass is already installed. Text = mineral plating is installed instead. - var/created_name = null - -/obj/structure/door_assembly/New() - update_state() - -/obj/structure/door_assembly/door_assembly_com - base_icon_state = "com" - base_name = "Command airlock" - glass_type = "/glass_command" - airlock_type = "/command" - -/obj/structure/door_assembly/door_assembly_sec - base_icon_state = "sec" - base_name = "Security airlock" - glass_type = "/glass_security" - airlock_type = "/security" - -/obj/structure/door_assembly/door_assembly_eng - base_icon_state = "eng" - base_name = "Engineering airlock" - glass_type = "/glass_engineering" - airlock_type = "/engineering" - -/obj/structure/door_assembly/door_assembly_eat - base_icon_state = "eat" - base_name = "Engineering atmos airlock" - glass_type = "/glass_engineeringatmos" - airlock_type = "/engineering" - -/obj/structure/door_assembly/door_assembly_min - base_icon_state = "min" - base_name = "Mining airlock" - glass_type = "/glass_mining" - airlock_type = "/mining" - -/obj/structure/door_assembly/door_assembly_atmo - base_icon_state = "atmo" - base_name = "Atmospherics airlock" - glass_type = "/glass_atmos" - airlock_type = "/atmos" - -/obj/structure/door_assembly/door_assembly_research - base_icon_state = "res" - base_name = "Research airlock" - glass_type = "/glass_research" - airlock_type = "/research" - -/obj/structure/door_assembly/door_assembly_science - base_icon_state = "sci" - base_name = "Science airlock" - glass_type = "/glass_science" - airlock_type = "/science" - -/obj/structure/door_assembly/door_assembly_med - base_icon_state = "med" - base_name = "Medical airlock" - glass_type = "/glass_medical" - airlock_type = "/medical" - -/obj/structure/door_assembly/door_assembly_ext - base_icon_state = "ext" - base_name = "External airlock" - glass_type = "/glass_external" - airlock_type = "/external" - -/obj/structure/door_assembly/door_assembly_mai - base_icon_state = "mai" - base_name = "Maintenance airlock" - airlock_type = "/maintenance" - glass = -1 - -/obj/structure/door_assembly/door_assembly_fre - base_icon_state = "fre" - base_name = "Freezer airlock" - airlock_type = "/freezer" - glass = -1 - -/obj/structure/door_assembly/door_assembly_hatch - base_icon_state = "hatch" - base_name = "airtight hatch" - airlock_type = "/hatch" - glass = -1 - -/obj/structure/door_assembly/door_assembly_mhatch - base_icon_state = "mhatch" - base_name = "maintenance hatch" - airlock_type = "/maintenance_hatch" - glass = -1 - -/obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly - base_icon_state = "highsec" - base_name = "high security airlock" - airlock_type = "/highsecurity" - glass = -1 - -/obj/structure/door_assembly/door_assembly_voidcraft - base_icon_state = "voidcraft" - base_name = "voidcraft hatch" - airlock_type = "/voidcraft" - glass = -1 - -/obj/structure/door_assembly/door_assembly_voidcraft/vertical - base_icon_state = "voidcraft_vertical" - airlock_type = "/voidcraft/vertical" - -/obj/structure/door_assembly/door_assembly_alien - base_icon_state = "alien" - base_name = "alien airlock" - airlock_type = "/alien" - glass = -1 - -/obj/structure/door_assembly/multi_tile - icon = 'icons/obj/doors/door_assembly2x1.dmi' - dir = EAST - var/width = 1 - -/*Temporary until we get sprites. - glass_type = "/multi_tile/glass" - airlock_type = "/multi_tile/maint" - glass = 1*/ - base_icon_state = "g" //Remember to delete this line when reverting "glass" var to 1. - airlock_type = "/multi_tile/glass" - glass = -1 //To prevent bugs in deconstruction process. - -/obj/structure/door_assembly/multi_tile/New() - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - update_state() - -/obj/structure/door_assembly/multi_tile/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/structure/door_assembly/proc/rename_door(mob/living/user) - var/t = sanitizeSafe(tgui_input_text(user, "Enter the name for the [base_name].", src.name, src.created_name, MAX_NAME_LEN), MAX_NAME_LEN) - if(!in_range(src, user) && src.loc != user) return - created_name = t - update_state() - -/obj/structure/door_assembly/attack_robot(mob/living/silicon/robot/user) - if(Adjacent(user) && (user.module && (istype(user.module,/obj/item/weapon/robot_module/robot/engineering)) \ - || istype(user.module,/obj/item/weapon/robot_module/drone))) //Only drone (and engiborg) needs this. - rename_door(user) - -/obj/structure/door_assembly/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/pen)) - rename_door(user) - return - - if(istype(W, /obj/item/weapon/weldingtool) && ( (istext(glass)) || (glass == 1) || (!anchored) )) - var/obj/item/weapon/weldingtool/WT = W - if (WT.remove_fuel(0, user)) - playsound(src, WT.usesound, 50, 1) - if(istext(glass)) - user.visible_message("[user] welds the [glass] plating off the airlock assembly.", "You start to weld the [glass] plating off the airlock assembly.") - if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src || !WT.isOn()) return - to_chat(user, "You welded the [glass] plating off!") - var/M = text2path("/obj/item/stack/material/[glass]") - new M(src.loc, 2) - glass = 0 - else if(glass == 1) - user.visible_message("[user] welds the glass panel out of the airlock assembly.", "You start to weld the glass panel out of the airlock assembly.") - if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src || !WT.isOn()) return - to_chat(user, "You welded the glass panel out!") - new /obj/item/stack/material/glass/reinforced(src.loc) - glass = 0 - else if(!anchored) - user.visible_message("[user] dissassembles the airlock assembly.", "You start to dissassemble the airlock assembly.") - if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src || !WT.isOn()) return - to_chat(user, "You dissasembled the airlock assembly!") - new /obj/item/stack/material/steel(src.loc, 4) - qdel (src) - else - to_chat(user, "You need more welding fuel.") - return - - else if(W.is_wrench() && state == 0) - playsound(src, W.usesound, 100, 1) - if(anchored) - user.visible_message("[user] begins unsecuring the airlock assembly from the floor.", "You starts unsecuring the airlock assembly from the floor.") - else - user.visible_message("[user] begins securing the airlock assembly to the floor.", "You starts securing the airlock assembly to the floor.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You [anchored? "un" : ""]secured the airlock assembly!") - anchored = !anchored - - else if(istype(W, /obj/item/stack/cable_coil) && state == 0 && anchored) - var/obj/item/stack/cable_coil/C = W - if (C.get_amount() < 1) - to_chat(user, "You need one length of coil to wire the airlock assembly.") - return - user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly.") - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && state == 0 && anchored) - if (C.use(1)) - src.state = 1 - to_chat(user, "You wire the airlock.") - - else if(W.is_wirecutter() && state == 1 ) - playsound(src, W.usesound, 100, 1) - user.visible_message("[user] cuts the wires from the airlock assembly.", "You start to cut the wires from airlock assembly.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You cut the airlock wires.!") - new/obj/item/stack/cable_coil(src.loc, 1) - src.state = 0 - - else if(istype(W, /obj/item/weapon/airlock_electronics) && state == 1) - playsound(src, W.usesound, 100, 1) - user.visible_message("[user] installs the electronics into the airlock assembly.", "You start to install electronics into the airlock assembly.") - - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - user.drop_item() - W.loc = src - to_chat(user, "You installed the airlock electronics!") - src.state = 2 - src.electronics = W - - else if(W.is_crowbar() && state == 2 ) - //This should never happen, but just in case I guess - if (!electronics) - to_chat(user, "There was nothing to remove.") - src.state = 1 - return - - playsound(src, W.usesound, 100, 1) - user.visible_message("\The [user] starts removing the electronics from the airlock assembly.", "You start removing the electronics from the airlock assembly.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You removed the airlock electronics!") - src.state = 1 - electronics.loc = src.loc - electronics = null - - else if(istype(W, /obj/item/stack/material) && !glass) - var/obj/item/stack/S = W - var/material_name = S.get_material_name() - if (S) - if (S.get_amount() >= 1) - if(material_name == "rglass") - playsound(src, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) - if (S.use(1)) - to_chat(user, "You installed reinforced glass windows into the airlock assembly.") - glass = 1 - else if(material_name) - // Ugly hack, will suffice for now. Need to fix it upstream as well, may rewrite mineral walls. ~Z - if(!(material_name in list("gold", "silver", "diamond", "uranium", "phoron", "sandstone"))) - to_chat(user, "You cannot make an airlock out of that material.") - return - if(S.get_amount() >= 2) - playsound(src, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") - if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) - if (S.use(2)) - to_chat(user, "You installed [material_display_name(material_name)] plating into the airlock assembly.") - glass = material_name - - else if(W.is_screwdriver() && state == 2 ) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now finishing the airlock.") - - if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) - if(!src) return - to_chat(user, "You finish the airlock!") - var/path - if(istext(glass)) - path = text2path("/obj/machinery/door/airlock/[glass]") - else if (glass == 1) - path = text2path("/obj/machinery/door/airlock[glass_type]") - else - path = text2path("/obj/machinery/door/airlock[airlock_type]") - - new path(src.loc, src) - qdel(src) - else - ..() - update_state() - -/obj/structure/door_assembly/proc/update_state() - icon_state = "door_as_[glass == 1 ? "g" : ""][istext(glass) ? glass : base_icon_state][state]" - name = "" - switch (state) - if(0) - if (anchored) - name = "secured " - if(1) - name = "wired " - if(2) - name = "near finished " - name += "[glass == 1 ? "window " : ""][istext(glass) ? "[glass] airlock" : base_name] assembly ([created_name])" - -// Airlock frames are indestructable, so bullets hitting them would always be stopped. -// To fix this, airlock assemblies will sometimes let bullets pass through, since generally the sprite shows them partially open. -/obj/structure/door_assembly/bullet_act(var/obj/item/projectile/P) - if(prob(40)) // Chance for the frame to let the bullet keep going. - return PROJECTILE_CONTINUE - return ..() +/obj/structure/door_assembly + name = "airlock assembly" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_as_0" + anchored = FALSE + density = TRUE + w_class = ITEMSIZE_HUGE + var/state = 0 + var/base_icon_state = "" + var/base_name = "airlock" + var/obj/item/weapon/airlock_electronics/electronics = null + var/airlock_type = "" //the type path of the airlock once completed + var/glass_type = "/glass" + var/glass = 0 // 0 = glass can be installed. -1 = glass can't be installed. 1 = glass is already installed. Text = mineral plating is installed instead. + var/created_name = null + +/obj/structure/door_assembly/New() + update_state() + +/obj/structure/door_assembly/door_assembly_com + base_icon_state = "com" + base_name = "Command airlock" + glass_type = "/glass_command" + airlock_type = "/command" + +/obj/structure/door_assembly/door_assembly_sec + base_icon_state = "sec" + base_name = "Security airlock" + glass_type = "/glass_security" + airlock_type = "/security" + +/obj/structure/door_assembly/door_assembly_eng + base_icon_state = "eng" + base_name = "Engineering airlock" + glass_type = "/glass_engineering" + airlock_type = "/engineering" + +/obj/structure/door_assembly/door_assembly_eat + base_icon_state = "eat" + base_name = "Engineering atmos airlock" + glass_type = "/glass_engineeringatmos" + airlock_type = "/engineering" + +/obj/structure/door_assembly/door_assembly_min + base_icon_state = "min" + base_name = "Mining airlock" + glass_type = "/glass_mining" + airlock_type = "/mining" + +/obj/structure/door_assembly/door_assembly_atmo + base_icon_state = "atmo" + base_name = "Atmospherics airlock" + glass_type = "/glass_atmos" + airlock_type = "/atmos" + +/obj/structure/door_assembly/door_assembly_research + base_icon_state = "res" + base_name = "Research airlock" + glass_type = "/glass_research" + airlock_type = "/research" + +/obj/structure/door_assembly/door_assembly_science + base_icon_state = "sci" + base_name = "Science airlock" + glass_type = "/glass_science" + airlock_type = "/science" + +/obj/structure/door_assembly/door_assembly_med + base_icon_state = "med" + base_name = "Medical airlock" + glass_type = "/glass_medical" + airlock_type = "/medical" + +/obj/structure/door_assembly/door_assembly_ext + base_icon_state = "ext" + base_name = "External airlock" + glass_type = "/glass_external" + airlock_type = "/external" + +/obj/structure/door_assembly/door_assembly_mai + base_icon_state = "mai" + base_name = "Maintenance airlock" + airlock_type = "/maintenance" + glass = -1 + +/obj/structure/door_assembly/door_assembly_fre + base_icon_state = "fre" + base_name = "Freezer airlock" + airlock_type = "/freezer" + glass = -1 + +/obj/structure/door_assembly/door_assembly_hatch + base_icon_state = "hatch" + base_name = "airtight hatch" + airlock_type = "/hatch" + glass = -1 + +/obj/structure/door_assembly/door_assembly_mhatch + base_icon_state = "mhatch" + base_name = "maintenance hatch" + airlock_type = "/maintenance_hatch" + glass = -1 + +/obj/structure/door_assembly/door_assembly_highsecurity // Borrowing this until WJohnston makes sprites for the assembly + base_icon_state = "highsec" + base_name = "high security airlock" + airlock_type = "/highsecurity" + glass = -1 + +/obj/structure/door_assembly/door_assembly_voidcraft + base_icon_state = "voidcraft" + base_name = "voidcraft hatch" + airlock_type = "/voidcraft" + glass = -1 + +/obj/structure/door_assembly/door_assembly_voidcraft/vertical + base_icon_state = "voidcraft_vertical" + airlock_type = "/voidcraft/vertical" + +/obj/structure/door_assembly/door_assembly_alien + base_icon_state = "alien" + base_name = "alien airlock" + airlock_type = "/alien" + glass = -1 + +/obj/structure/door_assembly/multi_tile + icon = 'icons/obj/doors/door_assembly2x1.dmi' + dir = EAST + var/width = 1 + +/*Temporary until we get sprites. + glass_type = "/multi_tile/glass" + airlock_type = "/multi_tile/maint" + glass = 1*/ + base_icon_state = "g" //Remember to delete this line when reverting "glass" var to 1. + airlock_type = "/multi_tile/glass" + glass = -1 //To prevent bugs in deconstruction process. + +/obj/structure/door_assembly/multi_tile/New() + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + update_state() + +/obj/structure/door_assembly/multi_tile/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/structure/door_assembly/proc/rename_door(mob/living/user) + var/t = sanitizeSafe(tgui_input_text(user, "Enter the name for the [base_name].", src.name, src.created_name, MAX_NAME_LEN), MAX_NAME_LEN) + if(!in_range(src, user) && src.loc != user) return + created_name = t + update_state() + +/obj/structure/door_assembly/attack_robot(mob/living/silicon/robot/user) + if(Adjacent(user) && (user.module && (istype(user.module,/obj/item/weapon/robot_module/robot/engineering)) \ + || istype(user.module,/obj/item/weapon/robot_module/drone))) //Only drone (and engiborg) needs this. + rename_door(user) + +/obj/structure/door_assembly/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen)) + rename_door(user) + return + + if(W.has_tool_quality(TOOL_WELDER) && ( (istext(glass)) || (glass == 1) || (!anchored) )) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if (WT.remove_fuel(0, user)) + playsound(src, WT.usesound, 50, 1) + if(istext(glass)) + user.visible_message("[user] welds the [glass] plating off the airlock assembly.", "You start to weld the [glass] plating off the airlock assembly.") + if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src || !WT.isOn()) return + to_chat(user, "You welded the [glass] plating off!") + var/M = text2path("/obj/item/stack/material/[glass]") + new M(src.loc, 2) + glass = 0 + else if(glass == 1) + user.visible_message("[user] welds the glass panel out of the airlock assembly.", "You start to weld the glass panel out of the airlock assembly.") + if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src || !WT.isOn()) return + to_chat(user, "You welded the glass panel out!") + new /obj/item/stack/material/glass/reinforced(src.loc) + glass = 0 + else if(!anchored) + user.visible_message("[user] dissassembles the airlock assembly.", "You start to dissassemble the airlock assembly.") + if(do_after(user, 4 SECONDS * WT.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src || !WT.isOn()) return + to_chat(user, "You dissasembled the airlock assembly!") + new /obj/item/stack/material/steel(src.loc, 4) + qdel (src) + else + to_chat(user, "You need more welding fuel.") + return + + else if(W.has_tool_quality(TOOL_WRENCH) && state == 0) + playsound(src, W.usesound, 100, 1) + if(anchored) + user.visible_message("[user] begins unsecuring the airlock assembly from the floor.", "You starts unsecuring the airlock assembly from the floor.") + else + user.visible_message("[user] begins securing the airlock assembly to the floor.", "You starts securing the airlock assembly to the floor.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You [anchored? "un" : ""]secured the airlock assembly!") + anchored = !anchored + + else if(istype(W, /obj/item/stack/cable_coil) && state == 0 && anchored) + var/obj/item/stack/cable_coil/C = W + if (C.get_amount() < 1) + to_chat(user, "You need one length of coil to wire the airlock assembly.") + return + user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly.") + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && state == 0 && anchored) + if (C.use(1)) + src.state = 1 + to_chat(user, "You wire the airlock.") + + else if(W.has_tool_quality(TOOL_WIRECUTTER) && state == 1 ) + playsound(src, W.usesound, 100, 1) + user.visible_message("[user] cuts the wires from the airlock assembly.", "You start to cut the wires from airlock assembly.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You cut the airlock wires.!") + new/obj/item/stack/cable_coil(src.loc, 1) + src.state = 0 + + else if(istype(W, /obj/item/weapon/airlock_electronics) && state == 1) + playsound(src, W.usesound, 100, 1) + user.visible_message("[user] installs the electronics into the airlock assembly.", "You start to install electronics into the airlock assembly.") + + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + user.drop_item() + W.loc = src + to_chat(user, "You installed the airlock electronics!") + src.state = 2 + src.electronics = W + + else if(W.has_tool_quality(TOOL_CROWBAR) && state == 2 ) + //This should never happen, but just in case I guess + if (!electronics) + to_chat(user, "There was nothing to remove.") + src.state = 1 + return + + playsound(src, W.usesound, 100, 1) + user.visible_message("\The [user] starts removing the electronics from the airlock assembly.", "You start removing the electronics from the airlock assembly.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You removed the airlock electronics!") + src.state = 1 + electronics.loc = src.loc + electronics = null + + else if(istype(W, /obj/item/stack/material) && !glass) + var/obj/item/stack/S = W + var/material_name = S.get_material_name() + if (S) + if (S.get_amount() >= 1) + if(material_name == "rglass") + playsound(src, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) + if (S.use(1)) + to_chat(user, "You installed reinforced glass windows into the airlock assembly.") + glass = 1 + else if(material_name) + // Ugly hack, will suffice for now. Need to fix it upstream as well, may rewrite mineral walls. ~Z + if(!(material_name in list("gold", "silver", "diamond", "uranium", "phoron", "sandstone"))) + to_chat(user, "You cannot make an airlock out of that material.") + return + if(S.get_amount() >= 2) + playsound(src, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly.") + if(do_after(user, 4 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE) && !glass) + if (S.use(2)) + to_chat(user, "You installed [material_display_name(material_name)] plating into the airlock assembly.") + glass = material_name + + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && state == 2 ) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now finishing the airlock.") + + if(do_after(user, 4 SECONDS * W.toolspeed, src, exclusive = TASK_ALL_EXCLUSIVE)) + if(!src) return + to_chat(user, "You finish the airlock!") + var/path + if(istext(glass)) + path = text2path("/obj/machinery/door/airlock/[glass]") + else if (glass == 1) + path = text2path("/obj/machinery/door/airlock[glass_type]") + else + path = text2path("/obj/machinery/door/airlock[airlock_type]") + + new path(src.loc, src) + qdel(src) + else + ..() + update_state() + +/obj/structure/door_assembly/proc/update_state() + icon_state = "door_as_[glass == 1 ? "g" : ""][istext(glass) ? glass : base_icon_state][state]" + name = "" + switch (state) + if(0) + if (anchored) + name = "secured " + if(1) + name = "wired " + if(2) + name = "near finished " + name += "[glass == 1 ? "window " : ""][istext(glass) ? "[glass] airlock" : base_name] assembly ([created_name])" + +// Airlock frames are indestructable, so bullets hitting them would always be stopped. +// To fix this, airlock assemblies will sometimes let bullets pass through, since generally the sprite shows them partially open. +/obj/structure/door_assembly/bullet_act(var/obj/item/projectile/P) + if(prob(40)) // Chance for the frame to let the bullet keep going. + return PROJECTILE_CONTINUE + return ..() diff --git a/code/game/objects/structures/droppod.dm b/code/game/objects/structures/droppod.dm index 730fba388bc..d794a8829d1 100644 --- a/code/game/objects/structures/droppod.dm +++ b/code/game/objects/structures/droppod.dm @@ -31,15 +31,15 @@ if(!T) warning("Drop pod wasn't spawned on a turf") return - + moveToNullspace() icon_state = "[initial(icon_state)]_falling" - + // Show warning on 3x3 area centred on our drop spot var/list/turfs_nearby = block(get_step(T, SOUTHWEST), get_step(T, NORTHEAST)) for(var/turf/TN in turfs_nearby) new /obj/effect/temporary_effect/shuttle_landing(TN) - + // Wait a minute sleep(4 SECONDS) @@ -58,7 +58,7 @@ T.hotspot_expose(900) sleep(1 SECOND) filters = null - + // CRONCH playsound(src, 'sound/effects/meteorimpact.ogg', 50, 1) if(!polite) @@ -69,7 +69,7 @@ else for(var/turf/simulated/floor/F in view(1, T)) F.burn_tile(900) - + for(var/obj/O in T) if(O == src) continue @@ -80,7 +80,7 @@ // Landed! Simmer plane = initial(plane) icon_state = "[initial(icon_state)]" - + if(auto_open) sleep(2 SECONDS) open_pod() @@ -109,7 +109,7 @@ user.visible_message("[user] opens \the [src]!","You open \the [src]!") /obj/structure/drop_pod/attackby(obj/item/O, mob/user) - if(O.is_wrench()) + if(O.has_tool_quality(TOOL_WRENCH)) if(finished) to_chat(user, "You start breaking down \the [src].") if(do_after(user, 10 SECONDS, src, exclusive = TASK_ALL_EXCLUSIVE)) diff --git a/code/game/objects/structures/electricchair.dm b/code/game/objects/structures/electricchair.dm index cccde21044d..ff37f1b40b3 100644 --- a/code/game/objects/structures/electricchair.dm +++ b/code/game/objects/structures/electricchair.dm @@ -12,7 +12,7 @@ return /obj/structure/bed/chair/e_chair/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) var/obj/structure/bed/chair/C = new /obj/structure/bed/chair(loc) playsound(src, W.usesound, 50, 1) C.set_dir(dir) diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index a0c01e52cd7..8318392b49f 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -31,7 +31,7 @@ to_chat(user, "You place [O] in [src].") else opened = !opened - if(O.is_wrench()) + if(O.has_tool_quality(TOOL_WRENCH)) if(!has_extinguisher) to_chat(user, "You start to unwrench the extinguisher cabinet.") playsound(src, O.usesound, 50, 1) diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 839269847a0..1bb1b4e71eb 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -69,7 +69,7 @@ return ..() /obj/structure/fence/attackby(obj/item/W, mob/user) - if(W.is_wirecutter()) + if(W.has_tool_quality(TOOL_WIRECUTTER)) if(!cuttable) to_chat(user, span("warning", "This section of the fence can't be cut.")) return @@ -170,6 +170,41 @@ return FALSE return TRUE +/obj/structure/fence/wood + cuttable = FALSE + name = "fence" + desc = "A wooden fence. Not as effective as a wall, but generally it keeps people out." + description_info = "Projectiles can freely pass fences." + density = TRUE + anchored = TRUE + + icon = 'icons/obj/fence.dmi' + icon_state = "wood_straight" + +/obj/structure/fence/wood/end + icon_state = "wood_end" + +/obj/structure/fence/wood/corner + icon_state = "wood_corner" + +/obj/structure/fence/hedge + cuttable = FALSE + name = "hedge" + desc = "A large hedge. Not as effective as a wall, but generally it keeps people out." + description_info = "Projectiles can freely pass fences." + density = TRUE + anchored = TRUE + opacity = 1 + + icon = 'icons/obj/fence.dmi' + icon_state = "hedge_straight" + +/obj/structure/fence/hedge/end + icon_state = "hedge_end" + +/obj/structure/fence/hedge/corner + icon_state = "hedge_corner" + #undef CUT_TIME #undef CLIMB_TIME diff --git a/code/game/objects/structures/fitness.dm b/code/game/objects/structures/fitness.dm index f84c9508e73..eb2f1e37bc6 100644 --- a/code/game/objects/structures/fitness.dm +++ b/code/game/objects/structures/fitness.dm @@ -34,7 +34,7 @@ var/list/qualifiers = list("with ease", "without any trouble", "with great effort") /obj/structure/fitness/weightlifter/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) playsound(src, 'sound/items/Deconstruct.ogg', 75, 1) weight = ((weight) % qualifiers.len) + 1 to_chat(user, "You set the machine's weight level to [weight].") diff --git a/code/game/objects/structures/fitness_vr.dm b/code/game/objects/structures/fitness_vr.dm index 0eeaf621965..38b96ec8e89 100644 --- a/code/game/objects/structures/fitness_vr.dm +++ b/code/game/objects/structures/fitness_vr.dm @@ -1,74 +1,74 @@ -/obj/structure/fitness/boxing_ropes - name = "ropes" - desc = "Firm yet springy, perhaps this could be useful!" - icon = 'icons/obj/fitness_vr.dmi' - icon_state = "ropes" - density = TRUE - throwpass = TRUE - climbable = TRUE - layer = WINDOW_LAYER - anchored = TRUE - flags = ON_BORDER - -/obj/structure/fitness/boxing_ropes/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir - return !density - return TRUE - -/obj/structure/fitness/boxing_ropes/Uncross(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir - return !density - return TRUE -/obj/structure/fitness/boxing_ropes/do_climb(var/mob/living/user) //Sets it so that players can climb *over* the turf and will enter the the turf **this** turf is facing. - if(!can_climb(user)) - return - - usr.visible_message("[user] starts climbing onto \the [src]!") - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? 20 : 34))) - LAZYREMOVE(climbers, user) - return - - if(!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - if(get_turf(user) == get_turf(src)) - usr.forceMove(get_step(src, src.dir)) - else - usr.forceMove(get_turf(src)) - - usr.visible_message("[user] climbed over \the [src]!") - LAZYREMOVE(climbers, user) - -/obj/structure/fitness/boxing_ropes/can_climb(var/mob/living/user, post_climb_check=0) //Sets it to keep people from climbing over into the next turf if it is occupied. - if(!..()) - return 0 - - if(get_turf(user) == get_turf(src)) - var/obj/occupied = neighbor_turf_impassable() - if(occupied) - to_chat(user, "You can't climb there, there's \a [occupied] in the way.") - return 0 - return 1 - -/obj/structure/fitness/boxing_ropes/bottom - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - -/obj/structure/fitness/boxing_ropes/turnbuckle - name = "turnbuckle" - desc = "A sturdy post that looks like it could support even the most heaviest of heavy weights!" - icon = 'icons/obj/fitness_vr.dmi' - icon_state = "turnbuckle" - layer = WINDOW_LAYER - -/turf/simulated/fitness - name = "Mat" - icon = 'icons/turf/floors_vr.dmi' +/obj/structure/fitness/boxing_ropes + name = "ropes" + desc = "Firm yet springy, perhaps this could be useful!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "ropes" + density = TRUE + throwpass = TRUE + climbable = TRUE + layer = WINDOW_LAYER + anchored = TRUE + flags = ON_BORDER + +/obj/structure/fitness/boxing_ropes/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir + return !density + return TRUE + +/obj/structure/fitness/boxing_ropes/Uncross(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir + return !density + return TRUE +/obj/structure/fitness/boxing_ropes/do_climb(var/mob/living/user) //Sets it so that players can climb *over* the turf and will enter the the turf **this** turf is facing. + if(!can_climb(user)) + return + + usr.visible_message("[user] starts climbing onto \the [src]!") + LAZYDISTINCTADD(climbers, user) + + if(!do_after(user,(issmall(user) ? 20 : 34))) + LAZYREMOVE(climbers, user) + return + + if(!can_climb(user, post_climb_check=1)) + LAZYREMOVE(climbers, user) + return + + if(get_turf(user) == get_turf(src)) + usr.forceMove(get_step(src, src.dir)) + else + usr.forceMove(get_turf(src)) + + usr.visible_message("[user] climbed over \the [src]!") + LAZYREMOVE(climbers, user) + +/obj/structure/fitness/boxing_ropes/can_climb(var/mob/living/user, post_climb_check=0) //Sets it to keep people from climbing over into the next turf if it is occupied. + if(!..()) + return 0 + + if(get_turf(user) == get_turf(src)) + var/obj/occupied = neighbor_turf_impassable() + if(occupied) + to_chat(user, "You can't climb there, there's \a [occupied] in the way.") + return 0 + return 1 + +/obj/structure/fitness/boxing_ropes/bottom + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + +/obj/structure/fitness/boxing_ropes/turnbuckle + name = "turnbuckle" + desc = "A sturdy post that looks like it could support even the most heaviest of heavy weights!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "turnbuckle" + layer = WINDOW_LAYER + +/turf/simulated/fitness + name = "Mat" + icon = 'icons/turf/floors_vr.dmi' icon_state = "fit_mat" \ No newline at end of file diff --git a/code/game/objects/structures/flora/moretrees_vr.dm b/code/game/objects/structures/flora/moretrees_vr.dm index c14cd7522cf..6541fda8027 100644 --- a/code/game/objects/structures/flora/moretrees_vr.dm +++ b/code/game/objects/structures/flora/moretrees_vr.dm @@ -1,32 +1,32 @@ -/obj/structure/flora/tree/bigtree - icon = 'icons/obj/flora/moretrees_vr.dmi' - icon_state = "bigtree1" - base_state = "tree" - product = /obj/item/stack/material/log - product_amount = 20 - health = 400 - max_health = 400 - pixel_x = -65 - pixel_y = -8 - layer = MOB_LAYER - 1 - shake_animation_degrees = 2 - -/obj/structure/flora/tree/bigtree/choose_icon_state() - return "[base_state][rand(1, 4)]" - -/obj/structure/flora/tree/bigtree/Initialize() - . = ..() - - var/image/i = image('icons/obj/flora/moretrees_vr.dmi', "[icon_state]-b") - i.plane = ABOVE_MOB_PLANE - add_overlay(i) - -/obj/structure/flora/tree/bigtree/stump() - if(is_stump) - return - - is_stump = TRUE - density = FALSE - icon_state = "[icon_state]_stump" - cut_overlays() +/obj/structure/flora/tree/bigtree + icon = 'icons/obj/flora/moretrees_vr.dmi' + icon_state = "bigtree1" + base_state = "tree" + product = /obj/item/stack/material/log + product_amount = 20 + health = 400 + max_health = 400 + pixel_x = -65 + pixel_y = -8 + layer = MOB_LAYER - 1 + shake_animation_degrees = 2 + +/obj/structure/flora/tree/bigtree/choose_icon_state() + return "[base_state][rand(1, 4)]" + +/obj/structure/flora/tree/bigtree/Initialize() + . = ..() + + var/image/i = image('icons/obj/flora/moretrees_vr.dmi', "[icon_state]-b") + i.plane = ABOVE_MOB_PLANE + add_overlay(i) + +/obj/structure/flora/tree/bigtree/stump() + if(is_stump) + return + + is_stump = TRUE + density = FALSE + icon_state = "[icon_state]_stump" + cut_overlays() set_light(0) \ No newline at end of file diff --git a/code/game/objects/structures/ghost_pods/event_vr.dm b/code/game/objects/structures/ghost_pods/event_vr.dm index 0c56b691930..95aac206ab1 100644 --- a/code/game/objects/structures/ghost_pods/event_vr.dm +++ b/code/game/objects/structures/ghost_pods/event_vr.dm @@ -53,7 +53,14 @@ "Snapdragon" =/mob/living/simple_mob/vore/pakkun/snapdragon, "Sand pakkun" = /mob/living/simple_mob/vore/pakkun/sand, "Fire pakkun" = /mob/living/simple_mob/vore/pakkun/fire, - "Amethyst pakkun" = /mob/living/simple_mob/vore/pakkun/purple + "Amethyst pakkun" = /mob/living/simple_mob/vore/pakkun/purple, + "Raptor" = /mob/living/simple_mob/vore/raptor, + "Giant Bat" = /mob/living/simple_mob/vore/bat, + "Scel (Orange)" = /mob/living/simple_mob/vore/scel/orange, + "Scel (Blue)" = /mob/living/simple_mob/vore/scel/blue, + "Scel (Purple)" = /mob/living/simple_mob/vore/scel/purple, + "Scel (Red)" = /mob/living/simple_mob/vore/scel/red, + "Scel (Green)" = /mob/living/simple_mob/vore/scel/green ) /obj/structure/ghost_pod/ghost_activated/maintpred/create_occupant(var/mob/M) @@ -66,6 +73,10 @@ reset_ghostpod() return + //No OOC notes + if (not_has_ooc_text(M)) + return + while(finalized == "No" && M.client) choice = tgui_input_list(M, "What type of predator do you want to play as?", "Maintpred Choice", possible_mobs) if(!choice) //We probably pushed the cancel button on the mob selection. Let's just put the ghost pod back in the list. @@ -115,6 +126,11 @@ /obj/structure/ghost_pod/ghost_activated/morphspawn/create_occupant(var/mob/M) ..() + + //No OOC notes + if (not_has_ooc_text(M)) + return + var/mob/living/simple_mob/vore/morph/newMorph = new /mob/living/simple_mob/vore/morph(get_turf(src)) if(M.mind) M.mind.transfer_to(newMorph) diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index 5a5ed5db2f7..82845de15f0 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -90,6 +90,11 @@ if(jobban_isbanned(user, "GhostRoles")) to_chat(user, "You cannot inhabit this creature because you are banned from playing ghost roles.") return + + //No OOC notes + if (not_has_ooc_text(user)) + return + //VOREStation Add End if(used) to_chat(user, "Another spirit appears to have gotten to \the [src] before you. Sorry.") diff --git a/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm b/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm index 28ea62182fc..2ae1a6f496d 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm @@ -15,6 +15,10 @@ to_chat(user, "You cannot inhabit this creature because you are banned from playing ghost roles.") return + //No OOC notes + if (not_has_ooc_text(user)) + return + if(!remains_active || busy) return @@ -51,4 +55,4 @@ /obj/structure/ghost_pod/ghost_activated/Initialize(var/mapload) . = ..() if(!mapload) - ghostpod_startup(spawn_active) \ No newline at end of file + ghostpod_startup(spawn_active) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 8e8568953b3..d1fd8eb64df 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -1,422 +1,421 @@ -/obj/structure/girder - name = "girder" - icon_state = "girder" - anchored = TRUE - density = TRUE - plane = PLATING_PLANE - w_class = ITEMSIZE_HUGE - var/state = 0 - var/health = 200 - var/max_health = 200 - var/displaced_health = 50 - var/current_damage = 0 - var/cover = 50 //how much cover the girder provides against projectiles. - var/default_material = MAT_STEEL - var/datum/material/girder_material - var/datum/material/reinf_material - var/reinforcing = 0 - var/applies_material_colour = 1 - var/wall_type = /turf/simulated/wall - -/obj/structure/girder/New(var/newloc, var/material_key) - ..(newloc) - if(!material_key) - material_key = default_material - set_material(material_key) - update_icon() - -/obj/structure/girder/Destroy() - if(girder_material.products_need_process()) - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/structure/girder/process() - if(!radiate()) - STOP_PROCESSING(SSobj, src) - return - -/obj/structure/girder/proc/radiate() - var/total_radiation = girder_material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) - if(!total_radiation) - return - - SSradiation.radiate(src, total_radiation) - return total_radiation - - -/obj/structure/girder/proc/set_material(var/new_material) - girder_material = get_material_by_name(new_material) - if(!girder_material) - qdel(src) - name = "[girder_material.display_name] [initial(name)]" - max_health = round(girder_material.integrity) //Should be 150 with default integrity (steel). Weaker than ye-olden Girders now. - health = max_health - displaced_health = round(max_health/4) - if(applies_material_colour) - color = girder_material.icon_colour - if(girder_material.products_need_process()) //Am I radioactive or some other? Process me! - START_PROCESSING(SSobj, src) - else if(datum_flags & DF_ISPROCESSING) //If I happened to be radioactive or s.o. previously, and am not now, stop processing. - STOP_PROCESSING(SSobj, src) - -/obj/structure/girder/get_material() - return girder_material - -/obj/structure/girder/update_icon() - if(anchored) - icon_state = initial(icon_state) - else - icon_state = "displaced" - -/obj/structure/girder/displaced - icon_state = "displaced" - anchored = FALSE - health = 50 - cover = 25 - -/obj/structure/girder/displaced/New(var/newloc, var/material_key) - ..(newloc, material_key) - displace() - -/obj/structure/girder/proc/displace() - name = "displaced [girder_material.display_name] [initial(name)]" - icon_state = "displaced" - anchored = FALSE - health = (displaced_health - round(current_damage / 4)) - cover = 25 - -/obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart") - if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) - return 0 - user.do_attack_animation(src) - visible_message("[user] [attack_message] the [src]!") - spawn(1) dismantle() - return 1 - -/obj/structure/girder/bullet_act(var/obj/item/projectile/Proj) - //Girders only provide partial cover. There's a chance that the projectiles will just pass through. (unless you are trying to shoot the girder) - if(Proj.original != src && !prob(cover)) - return PROJECTILE_CONTINUE //pass through - - var/damage = Proj.get_structure_damage() - if(!damage) - return - - if(!istype(Proj, /obj/item/projectile/beam)) - damage *= 0.4 //non beams do reduced damage - - else if(girder_material && girder_material.reflectivity >= 0.5) // Reflect lasers. - var/new_damage = damage * girder_material.reflectivity - var/outgoing_damage = damage - new_damage - damage = round(new_damage) - Proj.damage = outgoing_damage - - visible_message("\The [src] reflects \the [Proj]!") - - // Find a turf near or on the original location to bounce to - var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - //var/turf/curloc = get_turf(src) - var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) - - Proj.penetrating += 1 // Needed for the beam to get out of the girder. - - // redirect the projectile - Proj.redirect(new_x, new_y, curloc, null) - - health -= damage - ..() - if(health <= 0) - dismantle() - - return - -/obj/structure/girder/blob_act() - dismantle() - -/obj/structure/girder/proc/reset_girder() - name = "[girder_material.display_name] [initial(name)]" - anchored = TRUE - cover = initial(cover) - health = min(max_health - current_damage,max_health) - state = 0 - icon_state = initial(icon_state) - reinforcing = 0 - if(reinf_material) - reinforce_girder() - -/obj/structure/girder/attackby(obj/item/W as obj, mob/user as mob) - if(W.is_wrench() && state == 0) - if(anchored && !reinf_material) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now disassembling the girder...") - if(do_after(user,(35 + round(max_health/50)) * W.toolspeed)) - if(!src) return - to_chat(user, "You dissasembled the girder!") - dismantle() - else if(!anchored) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now securing the girder...") - if(do_after(user, 40 * W.toolspeed, src)) - to_chat(user, "You secured the girder!") - reset_girder() - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - to_chat(user, "Now slicing apart the girder...") - if(do_after(user,30 * W.toolspeed)) - if(!src) return - to_chat(user, "You slice apart the girder!") - dismantle() - - else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) - to_chat(user, "You drill through the girder!") - dismantle() - - else if(W.is_screwdriver()) - if(state == 2) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now unsecuring support struts...") - if(do_after(user,40 * W.toolspeed)) - if(!src) return - to_chat(user, "You unsecured the support struts!") - state = 1 - else if(anchored && !reinf_material) - playsound(src, W.usesound, 100, 1) - reinforcing = !reinforcing - to_chat(user, "\The [src] can now be [reinforcing? "reinforced" : "constructed"]!") - - else if(W.is_wirecutter() && state == 1) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now removing support struts...") - if(do_after(user,40 * W.toolspeed)) - if(!src) return - to_chat(user, "You removed the support struts!") - reinf_material.place_dismantled_product(get_turf(src)) - reinf_material = null - reset_girder() - - else if(W.is_crowbar() && state == 0 && anchored) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now dislodging the girder...") - if(do_after(user, 40 * W.toolspeed)) - if(!src) return - to_chat(user, "You dislodged the girder!") - displace() - - else if(istype(W, /obj/item/stack/material)) - if(reinforcing && !reinf_material) - if(!reinforce_with_material(W, user)) - return ..() - else - if(!construct_wall(W, user)) - return ..() - - else - return ..() - -/obj/structure/girder/take_damage(var/damage) - health -= damage - if(health <= 0) - dismantle() - else - current_damage = current_damage + damage //Rather than calculate this every time we need to use it, just calculate it here and save it. - - -/obj/structure/girder/proc/construct_wall(obj/item/stack/material/S, mob/user) - var/amount_to_use = reinf_material ? 1 : 2 - if(S.get_amount() < amount_to_use) - to_chat(user, "There isn't enough material here to construct a wall.") - return 0 - - var/datum/material/M = name_to_material[S.default_type] - if(!istype(M)) - return 0 - - var/wall_fake - add_hiddenprint(usr) - - if(M.integrity < 50) - to_chat(user, "This material is too soft for use in wall construction.") - return 0 - - to_chat(user, "You begin adding the plating...") - - if(!do_after(user,40) || !S.use(amount_to_use)) - return 1 //once we've gotten this far don't call parent attackby() - - if(anchored) - to_chat(user, "You added the plating!") - else - to_chat(user, "You create a false wall! Push on it to open or close the passage.") - wall_fake = 1 - - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(wall_type) - var/turf/simulated/wall/T = get_turf(src) - T.set_material(M, reinf_material, girder_material) - if(wall_fake) - T.can_open = 1 - T.add_hiddenprint(usr) - qdel(src) - return 1 - -/obj/structure/girder/proc/reinforce_with_material(obj/item/stack/material/S, mob/user) //if the verb is removed this can be renamed. - if(reinf_material) - to_chat(user, "\The [src] is already reinforced.") - return 0 - - if(S.get_amount() < 1) - to_chat(user, "There isn't enough material here to reinforce the girder.") - return 0 - - var/datum/material/M = name_to_material[S.default_type] - if(!istype(M) || M.integrity < 50) - to_chat(user, "You cannot reinforce \the [src] with that; it is too soft.") - return 0 - - to_chat(user, "Now reinforcing...") - if (!do_after(user,40) || !S.use(1)) - return 1 //don't call parent attackby() past this point - to_chat(user, "You added reinforcement!") - - reinf_material = M - reinforce_girder() - return 1 - -/obj/structure/girder/proc/reinforce_girder() - cover = reinf_material.hardness - health = health + round(reinf_material.integrity/2) - state = 2 - icon_state = "reinforced" - reinforcing = 0 - -/obj/structure/girder/proc/dismantle() - girder_material.place_dismantled_product(get_turf(src)) - qdel(src) - -/obj/structure/girder/attack_hand(mob/user as mob) - if (HULK in user.mutations) - visible_message("[user] smashes [src] apart!") - dismantle() - return - return ..() - - -/obj/structure/girder/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(30)) - dismantle() - return - if(3.0) - if (prob(5)) - dismantle() - return - else - return - -/obj/structure/girder/cult - name = "column" - icon= 'icons/obj/cult.dmi' - icon_state= "cultgirder" - max_health = 250 - health = 250 - cover = 70 - girder_material = "cult" - applies_material_colour = 0 - -/obj/structure/girder/cult/update_icon() - if(anchored) - icon_state = "cultgirder" - else - icon_state = "displaced" - -/obj/structure/girder/cult/dismantle() - new /obj/effect/decal/remains/human(get_turf(src)) - qdel(src) - -/obj/structure/girder/cult/attackby(obj/item/W as obj, mob/user as mob) - if(W.is_wrench()) - playsound(src, W.usesound, 100, 1) - to_chat(user, "Now disassembling the girder...") - if(do_after(user,40 * W.toolspeed)) - to_chat(user, "You dissasembled the girder!") - dismantle() - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - to_chat(user, "Now slicing apart the girder...") - if(do_after(user,30 * W.toolspeed)) - to_chat(user, "You slice apart the girder!") - dismantle() - - else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) - to_chat(user, "You drill through the girder!") - new /obj/effect/decal/remains/human(get_turf(src)) - dismantle() - -/obj/structure/girder/resin - name = "soft girder" - icon_state = "girder_resin" - max_health = 225 - health = 225 - cover = 60 - girder_material = "resin" - -/obj/structure/girder/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - var/turf/simulated/T = get_turf(src) - if(!istype(T) || T.density) - return FALSE - - switch(passed_mode) - if(RCD_FLOORWALL) - // Finishing a wall costs two sheets. - var/cost = RCD_SHEETS_PER_MATTER_UNIT * 2 - // Rwalls cost three to finish. - if(the_rcd.make_rwalls) - cost += RCD_SHEETS_PER_MATTER_UNIT * 1 - return list( - RCD_VALUE_MODE = RCD_FLOORWALL, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = cost - ) - if(RCD_DECONSTRUCT) - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - return FALSE - -/obj/structure/girder/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - var/turf/simulated/T = get_turf(src) - if(!istype(T) || T.density) // Should stop future bugs of people bringing girders to centcom and RCDing them, or somehow putting a girder on a durasteel wall and deconning it. - return FALSE - - switch(passed_mode) - if(RCD_FLOORWALL) - to_chat(user, span("notice", "You finish a wall.")) - // This is mostly the same as using on a floor. The girder's material is preserved, however. - T.ChangeTurf(wall_type) - var/turf/simulated/wall/new_T = get_turf(src) // Ref to the wall we just built. - // Apparently set_material(...) for walls requires refs to the material singletons and not strings. - // This is different from how other material objects with their own set_material(...) do it, but whatever. - var/datum/material/M = name_to_material[the_rcd.material_to_use] - new_T.set_material(M, the_rcd.make_rwalls ? M : null, girder_material) - new_T.add_hiddenprint(user) - qdel(src) - return TRUE - - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - -/obj/structure/girder/bay - wall_type = /turf/simulated/wall/bay - -/obj/structure/girder/eris - wall_type = /turf/simulated/wall/eris - \ No newline at end of file +/obj/structure/girder + name = "girder" + icon_state = "girder" + anchored = TRUE + density = TRUE + plane = PLATING_PLANE + w_class = ITEMSIZE_HUGE + var/state = 0 + var/health = 200 + var/max_health = 200 + var/displaced_health = 50 + var/current_damage = 0 + var/cover = 50 //how much cover the girder provides against projectiles. + var/default_material = MAT_STEEL + var/datum/material/girder_material + var/datum/material/reinf_material + var/reinforcing = 0 + var/applies_material_colour = 1 + var/wall_type = /turf/simulated/wall + +/obj/structure/girder/New(var/newloc, var/material_key) + ..(newloc) + if(!material_key) + material_key = default_material + set_material(material_key) + update_icon() + +/obj/structure/girder/Destroy() + if(girder_material.products_need_process()) + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/structure/girder/process() + if(!radiate()) + STOP_PROCESSING(SSobj, src) + return + +/obj/structure/girder/proc/radiate() + var/total_radiation = girder_material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + if(!total_radiation) + return + + SSradiation.radiate(src, total_radiation) + return total_radiation + + +/obj/structure/girder/proc/set_material(var/new_material) + girder_material = get_material_by_name(new_material) + if(!girder_material) + qdel(src) + name = "[girder_material.display_name] [initial(name)]" + max_health = round(girder_material.integrity) //Should be 150 with default integrity (steel). Weaker than ye-olden Girders now. + health = max_health + displaced_health = round(max_health/4) + if(applies_material_colour) + color = girder_material.icon_colour + if(girder_material.products_need_process()) //Am I radioactive or some other? Process me! + START_PROCESSING(SSobj, src) + else if(datum_flags & DF_ISPROCESSING) //If I happened to be radioactive or s.o. previously, and am not now, stop processing. + STOP_PROCESSING(SSobj, src) + +/obj/structure/girder/get_material() + return girder_material + +/obj/structure/girder/update_icon() + if(anchored) + icon_state = initial(icon_state) + else + icon_state = "displaced" + +/obj/structure/girder/displaced + icon_state = "displaced" + anchored = FALSE + health = 50 + cover = 25 + +/obj/structure/girder/displaced/New(var/newloc, var/material_key) + ..(newloc, material_key) + displace() + +/obj/structure/girder/proc/displace() + name = "displaced [girder_material.display_name] [initial(name)]" + icon_state = "displaced" + anchored = FALSE + health = (displaced_health - round(current_damage / 4)) + cover = 25 + +/obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart") + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) + return 0 + user.do_attack_animation(src) + visible_message("[user] [attack_message] the [src]!") + spawn(1) dismantle() + return 1 + +/obj/structure/girder/bullet_act(var/obj/item/projectile/Proj) + //Girders only provide partial cover. There's a chance that the projectiles will just pass through. (unless you are trying to shoot the girder) + if(Proj.original != src && !prob(cover)) + return PROJECTILE_CONTINUE //pass through + + var/damage = Proj.get_structure_damage() + if(!damage) + return + + if(!istype(Proj, /obj/item/projectile/beam)) + damage *= 0.4 //non beams do reduced damage + + else if(girder_material && girder_material.reflectivity >= 0.5) // Reflect lasers. + var/new_damage = damage * girder_material.reflectivity + var/outgoing_damage = damage - new_damage + damage = round(new_damage) + Proj.damage = outgoing_damage + + visible_message("\The [src] reflects \the [Proj]!") + + // Find a turf near or on the original location to bounce to + var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + //var/turf/curloc = get_turf(src) + var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) + + Proj.penetrating += 1 // Needed for the beam to get out of the girder. + + // redirect the projectile + Proj.redirect(new_x, new_y, curloc, null) + + health -= damage + ..() + if(health <= 0) + dismantle() + + return + +/obj/structure/girder/blob_act() + dismantle() + +/obj/structure/girder/proc/reset_girder() + name = "[girder_material.display_name] [initial(name)]" + anchored = TRUE + cover = initial(cover) + health = min(max_health - current_damage,max_health) + state = 0 + icon_state = initial(icon_state) + reinforcing = 0 + if(reinf_material) + reinforce_girder() + +/obj/structure/girder/attackby(obj/item/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH) && state == 0) + if(anchored && !reinf_material) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now disassembling the girder...") + if(do_after(user,(35 + round(max_health/50)) * W.toolspeed)) + if(!src) return + to_chat(user, "You dissasembled the girder!") + dismantle() + else if(!anchored) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now securing the girder...") + if(do_after(user, 40 * W.toolspeed, src)) + to_chat(user, "You secured the girder!") + reset_girder() + + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + to_chat(user, "Now slicing apart the girder...") + if(do_after(user,30 * W.toolspeed)) + if(!src) return + to_chat(user, "You slice apart the girder!") + dismantle() + + else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) + to_chat(user, "You drill through the girder!") + dismantle() + + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(state == 2) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now unsecuring support struts...") + if(do_after(user,40 * W.toolspeed)) + if(!src) return + to_chat(user, "You unsecured the support struts!") + state = 1 + else if(anchored && !reinf_material) + playsound(src, W.usesound, 100, 1) + reinforcing = !reinforcing + to_chat(user, "\The [src] can now be [reinforcing? "reinforced" : "constructed"]!") + + else if(W.has_tool_quality(TOOL_WIRECUTTER) && state == 1) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now removing support struts...") + if(do_after(user,40 * W.toolspeed)) + if(!src) return + to_chat(user, "You removed the support struts!") + reinf_material.place_dismantled_product(get_turf(src)) + reinf_material = null + reset_girder() + + else if(W.has_tool_quality(TOOL_CROWBAR) && state == 0 && anchored) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now dislodging the girder...") + if(do_after(user, 40 * W.toolspeed)) + if(!src) return + to_chat(user, "You dislodged the girder!") + displace() + + else if(istype(W, /obj/item/stack/material)) + if(reinforcing && !reinf_material) + if(!reinforce_with_material(W, user)) + return ..() + else + if(!construct_wall(W, user)) + return ..() + + else + return ..() + +/obj/structure/girder/take_damage(var/damage) + health -= damage + if(health <= 0) + dismantle() + else + current_damage = current_damage + damage //Rather than calculate this every time we need to use it, just calculate it here and save it. + + +/obj/structure/girder/proc/construct_wall(obj/item/stack/material/S, mob/user) + var/amount_to_use = reinf_material ? 1 : 2 + if(S.get_amount() < amount_to_use) + to_chat(user, "There isn't enough material here to construct a wall.") + return 0 + + var/datum/material/M = name_to_material[S.default_type] + if(!istype(M)) + return 0 + + var/wall_fake + add_hiddenprint(usr) + + if(M.integrity < 50) + to_chat(user, "This material is too soft for use in wall construction.") + return 0 + + to_chat(user, "You begin adding the plating...") + + if(!do_after(user,40) || !S.use(amount_to_use)) + return 1 //once we've gotten this far don't call parent attackby() + + if(anchored) + to_chat(user, "You added the plating!") + else + to_chat(user, "You create a false wall! Push on it to open or close the passage.") + wall_fake = 1 + + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(wall_type) + var/turf/simulated/wall/T = get_turf(src) + T.set_material(M, reinf_material, girder_material) + if(wall_fake) + T.can_open = 1 + T.add_hiddenprint(usr) + qdel(src) + return 1 + +/obj/structure/girder/proc/reinforce_with_material(obj/item/stack/material/S, mob/user) //if the verb is removed this can be renamed. + if(reinf_material) + to_chat(user, "\The [src] is already reinforced.") + return 0 + + if(S.get_amount() < 1) + to_chat(user, "There isn't enough material here to reinforce the girder.") + return 0 + + var/datum/material/M = name_to_material[S.default_type] + if(!istype(M) || M.integrity < 50) + to_chat(user, "You cannot reinforce \the [src] with that; it is too soft.") + return 0 + + to_chat(user, "Now reinforcing...") + if (!do_after(user,40) || !S.use(1)) + return 1 //don't call parent attackby() past this point + to_chat(user, "You added reinforcement!") + + reinf_material = M + reinforce_girder() + return 1 + +/obj/structure/girder/proc/reinforce_girder() + cover = reinf_material.hardness + health = health + round(reinf_material.integrity/2) + state = 2 + icon_state = "reinforced" + reinforcing = 0 + +/obj/structure/girder/proc/dismantle() + girder_material.place_dismantled_product(get_turf(src)) + qdel(src) + +/obj/structure/girder/attack_hand(mob/user as mob) + if (HULK in user.mutations) + visible_message("[user] smashes [src] apart!") + dismantle() + return + return ..() + + +/obj/structure/girder/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(30)) + dismantle() + return + if(3.0) + if (prob(5)) + dismantle() + return + else + return + +/obj/structure/girder/cult + name = "column" + icon= 'icons/obj/cult.dmi' + icon_state= "cultgirder" + max_health = 250 + health = 250 + cover = 70 + girder_material = "cult" + applies_material_colour = 0 + +/obj/structure/girder/cult/update_icon() + if(anchored) + icon_state = "cultgirder" + else + icon_state = "displaced" + +/obj/structure/girder/cult/dismantle() + new /obj/effect/decal/remains/human(get_turf(src)) + qdel(src) + +/obj/structure/girder/cult/attackby(obj/item/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 100, 1) + to_chat(user, "Now disassembling the girder...") + if(do_after(user,40 * W.toolspeed)) + to_chat(user, "You dissasembled the girder!") + dismantle() + + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + to_chat(user, "Now slicing apart the girder...") + if(do_after(user,30 * W.toolspeed)) + to_chat(user, "You slice apart the girder!") + dismantle() + + else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) + to_chat(user, "You drill through the girder!") + new /obj/effect/decal/remains/human(get_turf(src)) + dismantle() + +/obj/structure/girder/resin + name = "soft girder" + icon_state = "girder_resin" + max_health = 225 + health = 225 + cover = 60 + girder_material = "resin" + +/obj/structure/girder/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + var/turf/simulated/T = get_turf(src) + if(!istype(T) || T.density) + return FALSE + + switch(passed_mode) + if(RCD_FLOORWALL) + // Finishing a wall costs two sheets. + var/cost = RCD_SHEETS_PER_MATTER_UNIT * 2 + // Rwalls cost three to finish. + if(the_rcd.make_rwalls) + cost += RCD_SHEETS_PER_MATTER_UNIT * 1 + return list( + RCD_VALUE_MODE = RCD_FLOORWALL, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = cost + ) + if(RCD_DECONSTRUCT) + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + return FALSE + +/obj/structure/girder/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + var/turf/simulated/T = get_turf(src) + if(!istype(T) || T.density) // Should stop future bugs of people bringing girders to centcom and RCDing them, or somehow putting a girder on a durasteel wall and deconning it. + return FALSE + + switch(passed_mode) + if(RCD_FLOORWALL) + to_chat(user, span("notice", "You finish a wall.")) + // This is mostly the same as using on a floor. The girder's material is preserved, however. + T.ChangeTurf(wall_type) + var/turf/simulated/wall/new_T = get_turf(src) // Ref to the wall we just built. + // Apparently set_material(...) for walls requires refs to the material singletons and not strings. + // This is different from how other material objects with their own set_material(...) do it, but whatever. + var/datum/material/M = name_to_material[the_rcd.material_to_use] + new_T.set_material(M, the_rcd.make_rwalls ? M : null, girder_material) + new_T.add_hiddenprint(user) + qdel(src) + return TRUE + + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + +/obj/structure/girder/bay + wall_type = /turf/simulated/wall/bay + +/obj/structure/girder/eris + wall_type = /turf/simulated/wall/eris diff --git a/code/game/objects/structures/gravemarker.dm b/code/game/objects/structures/gravemarker.dm index 381401b3521..a93c57144a7 100644 --- a/code/game/objects/structures/gravemarker.dm +++ b/code/game/objects/structures/gravemarker.dm @@ -51,7 +51,7 @@ return TRUE /obj/structure/gravemarker/attackby(obj/item/weapon/W, mob/user as mob) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) var/carving_1 = sanitizeSafe(tgui_input_text(user, "Who is \the [src.name] for?", "Gravestone Naming", null, MAX_NAME_LEN), MAX_NAME_LEN) if(carving_1) user.visible_message("[user] starts carving \the [src.name].", "You start carving \the [src.name].") @@ -67,7 +67,7 @@ epitaph += carving_2 update_icon() return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) user.visible_message("[user] starts taking down \the [src.name].", "You start taking down \the [src.name].") if(do_after(user, material.hardness * W.toolspeed)) user.visible_message("[user] takes down \the [src.name].", "You take down \the [src.name].") diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index ea1a9346c1f..d3226bb702e 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -1,289 +1,289 @@ -/obj/structure/grille - name = "grille" - desc = "A flimsy lattice of metal rods, with screws to secure it to the floor." - icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons - icon_state = "grille" - density = TRUE - anchored = TRUE - pressure_resistance = 5*ONE_ATMOSPHERE - layer = TABLE_LAYER - explosion_resistance = 1 - var/health = 10 - var/destroyed = 0 - - -/obj/structure/grille/ex_act(severity) - qdel(src) - -/obj/structure/grille/update_icon() - if(destroyed) - icon_state = "[initial(icon_state)]-b" - else - icon_state = initial(icon_state) - -/obj/structure/grille/Bumped(atom/user) - if(ismob(user)) shock(user, 70) - -/obj/structure/grille/attack_hand(mob/user as mob) - - user.setClickCooldown(user.get_attack_speed()) - playsound(src, 'sound/effects/grillehit.ogg', 80, 1) - user.do_attack_animation(src) - - var/damage_dealt = 1 - var/attack_message = "kicks" - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(H)) - attack_message = "mangles" - damage_dealt = 5 - - if(shock(user, 70)) - return - - if(HULK in user.mutations) - damage_dealt += 5 - else - damage_dealt += 1 - - attack_generic(user,damage_dealt,attack_message) - -/obj/structure/grille/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGRILLE)) - return TRUE - if(istype(mover, /obj/item/projectile)) - return prob(30) - return !density - -/obj/structure/grille/bullet_act(var/obj/item/projectile/Proj) - if(!Proj) return - - //Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact - var/damage = Proj.get_structure_damage() - var/passthrough = 0 - - if(!damage) return - - //20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area. - //If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used. - switch(Proj.damage_type) - if(BRUTE) - //bullets - if(Proj.original == src || prob(20)) - Proj.damage *= between(0, Proj.damage/60, 0.5) - if(prob(max((damage-10)/25, 0))*100) - passthrough = 1 - else - Proj.damage *= between(0, Proj.damage/60, 1) - passthrough = 1 - if(BURN) - //beams and other projectiles are either blocked completely by grilles or stop half the damage. - if(!(Proj.original == src || prob(20))) - Proj.damage *= 0.5 - passthrough = 1 - - if(passthrough) - . = PROJECTILE_CONTINUE - damage = between(0, (damage - Proj.damage)*(Proj.damage_type == BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage - - src.health -= damage*0.2 - spawn(0) healthcheck() //spawn to make sure we return properly if the grille is deleted - -/obj/structure/grille/attackby(obj/item/W as obj, mob/user as mob) - if(!istype(W)) - return - if(istype(W, /obj/item/weapon/rcd)) // To stop us from hitting the grille when building windows, because grilles don't let parent handle it properly. - return FALSE - else if(W.is_wirecutter()) - if(!shock(user, 100)) - playsound(src, W.usesound, 100, 1) - new /obj/item/stack/rods(get_turf(src), destroyed ? 1 : 2) - qdel(src) - else if((W.is_screwdriver()) && (istype(loc, /turf/simulated) || anchored)) - if(!shock(user, 90)) - playsound(src, W.usesound, 100, 1) - anchored = !anchored - user.visible_message("[user] [anchored ? "fastens" : "unfastens"] the grille.", \ - "You have [anchored ? "fastened the grille to" : "unfastened the grille from"] the floor.") - return - - //window placing begin //TODO CONVERT PROPERLY TO MATERIAL DATUM - else if(istype(W,/obj/item/stack/material)) - var/obj/item/stack/material/ST = W - if(!ST.material.created_window) - return 0 - - var/dir_to_set = 1 - if(loc == user.loc) - dir_to_set = user.dir - else - if( ( x == user.x ) || (y == user.y) ) //Only supposed to work for cardinal directions. - if( x == user.x ) - if( y > user.y ) - dir_to_set = 2 - else - dir_to_set = 1 - else if( y == user.y ) - if( x > user.x ) - dir_to_set = 8 - else - dir_to_set = 4 - else - to_chat(user, "You can't reach.") - return //Only works for cardinal direcitons, diagonals aren't supposed to work like this. - for(var/obj/structure/window/WINDOW in loc) - if(WINDOW.dir == dir_to_set) - to_chat(user, "There is already a window facing this way there.") - return - to_chat(user, "You start placing the window.") - if(do_after(user,20)) - for(var/obj/structure/window/WINDOW in loc) - if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. - to_chat(user, "There is already a window facing this way there.") - return - - var/wtype = ST.material.created_window - if (ST.use(1)) - var/obj/structure/window/WD = new wtype(loc, dir_to_set, 1) - to_chat(user, "You place the [WD] on [src].") - WD.update_icon() - return -//window placing end - - else if((W.flags & NOCONDUCT) || !shock(user, 70)) - user.setClickCooldown(user.get_attack_speed(W)) - user.do_attack_animation(src) - playsound(src, 'sound/effects/grillehit.ogg', 80, 1) - switch(W.damtype) - if("fire") - health -= W.force - if("brute") - health -= W.force * 0.1 - healthcheck() - ..() - return - - -/obj/structure/grille/proc/healthcheck() - if(health <= 0) - if(!destroyed) - density = FALSE - destroyed = 1 - update_icon() - new /obj/item/stack/rods(get_turf(src)) - - else - if(health <= -6) - new /obj/item/stack/rods(get_turf(src)) - qdel(src) - return - return - -// shock user with probability prb (if all connections & power are working) -// returns 1 if shocked, 0 otherwise - -/obj/structure/grille/proc/shock(mob/user as mob, prb) - - if(!anchored || destroyed) // anchored/destroyed grilles are never connected - return 0 - if(!prob(prb)) - return 0 - if(!in_range(src, user))//To prevent TK and mech users from getting shocked - return 0 - var/turf/T = get_turf(src) - var/obj/structure/cable/C = T.get_cable_node() - if(C) - if(electrocute_mob(user, C, src)) - if(C.powernet) - C.powernet.trigger_warning() - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - if(user.stunned) - return 1 - else - return 0 - return 0 - -/obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(!destroyed) - if(exposed_temperature > T0C + 1500) - health -= 1 - healthcheck() - ..() - -/obj/structure/grille/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - user.do_attack_animation(src) - health -= damage - spawn(1) healthcheck() - return 1 - -// Used in mapping to avoid -/obj/structure/grille/broken - destroyed = 1 - icon_state = "grille-b" - density = FALSE - -/obj/structure/grille/broken/New() - ..() - health = rand(-5, -1) //In the destroyed but not utterly threshold. - healthcheck() //Send this to healthcheck just in case we want to do something else with it. - -/obj/structure/grille/cult - name = "cult grille" - desc = "A matrice built out of an unknown material, with some sort of force field blocking air around it." - icon_state = "grillecult" - health = 40 // Make it strong enough to avoid people breaking in too easily. - can_atmos_pass = ATMOS_PASS_NO // Make sure air doesn't drain. - -/obj/structure/grille/broken/cult - icon_state = "grillecult-b" - -/obj/structure/grille/rustic - name = "rustic grille" - desc = "A lattice of metal, arranged in an old, rustic fashion." - icon_state = "grillerustic" - -/obj/structure/grille/broken/rustic - icon_state = "grillerustic-b" - - -/obj/structure/grille/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_WINDOWGRILLE) - // A full tile window costs 4 glass sheets. - return list( - RCD_VALUE_MODE = RCD_WINDOWGRILLE, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4 - ) - - if(RCD_DECONSTRUCT) - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 2 - ) - return FALSE - -/obj/structure/grille/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - if(RCD_WINDOWGRILLE) - if(locate(/obj/structure/window) in loc) - return FALSE - to_chat(user, span("notice", "You construct a window.")) - var/obj/structure/window/WD = new the_rcd.window_type(loc) - WD.anchored = TRUE - return TRUE - return FALSE - -/obj/structure/grille/take_damage(var/damage) - health -= damage - spawn(1) healthcheck() - return 1 - +/obj/structure/grille + name = "grille" + desc = "A flimsy lattice of metal rods, with screws to secure it to the floor." + icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons + icon_state = "grille" + density = TRUE + anchored = TRUE + pressure_resistance = 5*ONE_ATMOSPHERE + layer = TABLE_LAYER + explosion_resistance = 1 + var/health = 10 + var/destroyed = 0 + + +/obj/structure/grille/ex_act(severity) + qdel(src) + +/obj/structure/grille/update_icon() + if(destroyed) + icon_state = "[initial(icon_state)]-b" + else + icon_state = initial(icon_state) + +/obj/structure/grille/Bumped(atom/user) + if(ismob(user)) shock(user, 70) + +/obj/structure/grille/attack_hand(mob/user as mob) + + user.setClickCooldown(user.get_attack_speed()) + playsound(src, 'sound/effects/grillehit.ogg', 80, 1) + user.do_attack_animation(src) + + var/damage_dealt = 1 + var/attack_message = "kicks" + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + attack_message = "mangles" + damage_dealt = 5 + + if(shock(user, 70)) + return + + if(HULK in user.mutations) + damage_dealt += 5 + else + damage_dealt += 1 + + attack_generic(user,damage_dealt,attack_message) + +/obj/structure/grille/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGRILLE)) + return TRUE + if(istype(mover, /obj/item/projectile)) + return prob(30) + return !density + +/obj/structure/grille/bullet_act(var/obj/item/projectile/Proj) + if(!Proj) return + + //Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact + var/damage = Proj.get_structure_damage() + var/passthrough = 0 + + if(!damage) return + + //20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area. + //If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used. + switch(Proj.damage_type) + if(BRUTE) + //bullets + if(Proj.original == src || prob(20)) + Proj.damage *= between(0, Proj.damage/60, 0.5) + if(prob(max((damage-10)/25, 0))*100) + passthrough = 1 + else + Proj.damage *= between(0, Proj.damage/60, 1) + passthrough = 1 + if(BURN) + //beams and other projectiles are either blocked completely by grilles or stop half the damage. + if(!(Proj.original == src || prob(20))) + Proj.damage *= 0.5 + passthrough = 1 + + if(passthrough) + . = PROJECTILE_CONTINUE + damage = between(0, (damage - Proj.damage)*(Proj.damage_type == BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage + + src.health -= damage*0.2 + spawn(0) healthcheck() //spawn to make sure we return properly if the grille is deleted + +/obj/structure/grille/attackby(obj/item/W as obj, mob/user as mob) + if(!istype(W)) + return + if(istype(W, /obj/item/weapon/rcd)) // To stop us from hitting the grille when building windows, because grilles don't let parent handle it properly. + return FALSE + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(!shock(user, 100)) + playsound(src, W.usesound, 100, 1) + new /obj/item/stack/rods(get_turf(src), destroyed ? 1 : 2) + qdel(src) + else if((W.has_tool_quality(TOOL_SCREWDRIVER)) && (istype(loc, /turf/simulated) || anchored)) + if(!shock(user, 90)) + playsound(src, W.usesound, 100, 1) + anchored = !anchored + user.visible_message("[user] [anchored ? "fastens" : "unfastens"] the grille.", \ + "You have [anchored ? "fastened the grille to" : "unfastened the grille from"] the floor.") + return + + //window placing begin //TODO CONVERT PROPERLY TO MATERIAL DATUM + else if(istype(W,/obj/item/stack/material)) + var/obj/item/stack/material/ST = W + if(!ST.material.created_window) + return 0 + + var/dir_to_set = 1 + if(loc == user.loc) + dir_to_set = user.dir + else + if( ( x == user.x ) || (y == user.y) ) //Only supposed to work for cardinal directions. + if( x == user.x ) + if( y > user.y ) + dir_to_set = 2 + else + dir_to_set = 1 + else if( y == user.y ) + if( x > user.x ) + dir_to_set = 8 + else + dir_to_set = 4 + else + to_chat(user, "You can't reach.") + return //Only works for cardinal direcitons, diagonals aren't supposed to work like this. + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set) + to_chat(user, "There is already a window facing this way there.") + return + to_chat(user, "You start placing the window.") + if(do_after(user,20)) + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. + to_chat(user, "There is already a window facing this way there.") + return + + var/wtype = ST.material.created_window + if (ST.use(1)) + var/obj/structure/window/WD = new wtype(loc, dir_to_set, 1) + to_chat(user, "You place the [WD] on [src].") + WD.update_icon() + return +//window placing end + + else if((W.flags & NOCONDUCT) || !shock(user, 70)) + user.setClickCooldown(user.get_attack_speed(W)) + user.do_attack_animation(src) + playsound(src, 'sound/effects/grillehit.ogg', 80, 1) + switch(W.damtype) + if("fire") + health -= W.force + if("brute") + health -= W.force * 0.1 + healthcheck() + ..() + return + + +/obj/structure/grille/proc/healthcheck() + if(health <= 0) + if(!destroyed) + density = FALSE + destroyed = 1 + update_icon() + new /obj/item/stack/rods(get_turf(src)) + + else + if(health <= -6) + new /obj/item/stack/rods(get_turf(src)) + qdel(src) + return + return + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise + +/obj/structure/grille/proc/shock(mob/user as mob, prb) + + if(!anchored || destroyed) // anchored/destroyed grilles are never connected + return 0 + if(!prob(prb)) + return 0 + if(!in_range(src, user))//To prevent TK and mech users from getting shocked + return 0 + var/turf/T = get_turf(src) + var/obj/structure/cable/C = T.get_cable_node() + if(C) + if(electrocute_mob(user, C, src)) + if(C.powernet) + C.powernet.trigger_warning() + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + if(user.stunned) + return 1 + else + return 0 + return 0 + +/obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(!destroyed) + if(exposed_temperature > T0C + 1500) + health -= 1 + healthcheck() + ..() + +/obj/structure/grille/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + user.do_attack_animation(src) + health -= damage + spawn(1) healthcheck() + return 1 + +// Used in mapping to avoid +/obj/structure/grille/broken + destroyed = 1 + icon_state = "grille-b" + density = FALSE + +/obj/structure/grille/broken/New() + ..() + health = rand(-5, -1) //In the destroyed but not utterly threshold. + healthcheck() //Send this to healthcheck just in case we want to do something else with it. + +/obj/structure/grille/cult + name = "cult grille" + desc = "A matrice built out of an unknown material, with some sort of force field blocking air around it." + icon_state = "grillecult" + health = 40 // Make it strong enough to avoid people breaking in too easily. + can_atmos_pass = ATMOS_PASS_NO // Make sure air doesn't drain. + +/obj/structure/grille/broken/cult + icon_state = "grillecult-b" + +/obj/structure/grille/rustic + name = "rustic grille" + desc = "A lattice of metal, arranged in an old, rustic fashion." + icon_state = "grillerustic" + +/obj/structure/grille/broken/rustic + icon_state = "grillerustic-b" + + +/obj/structure/grille/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_WINDOWGRILLE) + // A full tile window costs 4 glass sheets. + return list( + RCD_VALUE_MODE = RCD_WINDOWGRILLE, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 4 + ) + + if(RCD_DECONSTRUCT) + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 2 + ) + return FALSE + +/obj/structure/grille/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + if(RCD_WINDOWGRILLE) + if(locate(/obj/structure/window) in loc) + return FALSE + to_chat(user, span("notice", "You construct a window.")) + var/obj/structure/window/WD = new the_rcd.window_type(loc) + WD.anchored = TRUE + return TRUE + return FALSE + +/obj/structure/grille/take_damage(var/damage) + health -= damage + spawn(1) healthcheck() + return 1 + diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 43aeed44c19..8450a219b5f 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -1,500 +1,500 @@ -GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) - -/obj/structure/janitorialcart - name = "janitorial cart" - desc = "The ultimate in janitorial carts! Has space for water, mops, signs, trash bags, and more!" - description_info = "You can use alt-click while holding a mop to stow the mop. Alt-click holding a reagent container will empty the contents into the bucket without trying to put the container in any attached trash bag." - icon = 'icons/obj/janitor.dmi' - icon_state = "cart" - anchored = FALSE - density = TRUE - flags = OPENCONTAINER - climbable = TRUE - //copypaste sorry - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - var/obj/item/weapon/storage/bag/trash/mybag = null - var/obj/item/weapon/mop/mymop = null - var/obj/item/weapon/reagent_containers/spray/myspray = null - var/obj/item/device/lightreplacer/myreplacer = null - var/obj/structure/mopbucket/mybucket = null - var/has_items = FALSE - var/dismantled = TRUE - var/signs = 0 //maximum capacity hardcoded below - var/list/tgui_icons = list() - - var/static/list/equippable_item_whitelist - -/obj/structure/janitorialcart/proc/equip_janicart_item(mob/user, obj/item/I) - if(!equippable_item_whitelist) - equippable_item_whitelist = typecacheof(list( - /obj/item/weapon/storage/bag/trash, - /obj/item/weapon/mop, - /obj/item/weapon/reagent_containers/spray, - /obj/item/device/lightreplacer, - /obj/item/clothing/suit/caution, - )) - - if(!is_type_in_typecache(I, equippable_item_whitelist)) - to_chat(user, "There's no room in [src] for [I].") - return FALSE - - if(!user.canUnEquip(I)) - to_chat(user, "[I] is stuck to your hand.") - return FALSE - - if(istype(I, /obj/item/weapon/storage/bag/trash)) - if(mybag) - to_chat(user, "[src] already has \an [I].") - return FALSE - mybag = I - setTguiIcon("mybag", mybag) - - else if(istype(I, /obj/item/weapon/mop)) - if(mymop) - to_chat(user, "[src] already has \an [I].") - return FALSE - mymop = I - setTguiIcon("mymop", mymop) - - else if(istype(I, /obj/item/weapon/reagent_containers/spray)) - if(myspray) - to_chat(user, "[src] already has \an [I].") - return FALSE - myspray = I - setTguiIcon("myspray", myspray) - - else if(istype(I, /obj/item/device/lightreplacer)) - if(myreplacer) - to_chat(user, "[src] already has \an [I].") - return FALSE - myreplacer = I - setTguiIcon("myreplacer", myreplacer) - - else if(istype(I, /obj/item/clothing/suit/caution)) - if(signs < 4) - signs++ - setTguiIcon("signs", I) - else - to_chat(user, "[src] can't hold any more signs.") - return FALSE - else - // This may look like duplicate code, but it's important that we don't call unEquip *and* warn the user if - // something horrible goes wrong. (this else is never supposed to happen) - to_chat(user, "There's no room in [src] for [I].") - return FALSE - - user.drop_from_inventory(I, src) - update_icon() - to_chat(user, "You put [I] into [src].") - return TRUE - -/obj/structure/janitorialcart/proc/setTguiIcon(key, atom/A) - if(!istype(A) || !key) - return - - var/icon/F = getFlatIcon(A, defdir = SOUTH, no_anim = TRUE) - tgui_icons["[key]"] = "'data:image/png;base64,[icon2base64(F)]'" - SStgui.update_uis(src) - -/obj/structure/janitorialcart/proc/nullTguiIcon(key) - if(!key) - return - tgui_icons.Remove(key) - SStgui.update_uis(src) - -/obj/structure/janitorialcart/proc/clearTguiIcons() - tgui_icons.Cut() - SStgui.update_uis(src) - -/obj/structure/janitorialcart/Destroy() - QDEL_NULL(mybag) - QDEL_NULL(mymop) - QDEL_NULL(myspray) - QDEL_NULL(myreplacer) - QDEL_NULL(mybucket) - clearTguiIcons() - return ..() - -/obj/structure/janitorialcart/examine(mob/user) - . = ..(user) - if(istype(mybucket)) - var/contains = mybucket.reagents.total_volume - . += "\icon[src][bicon(src)] The bucket contains [contains] unit\s of liquid!" - else - . += "\icon[src][bicon(src)] There is no bucket mounted on it!" - -/obj/structure/janitorialcart/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) - if (istype(O, /obj/structure/mopbucket) && !mybucket) - O.forceMove(src) - mybucket = O - setTguiIcon("mybucket", mybucket) - to_chat(user, "You mount the [O] on the janicart.") - update_icon() - else - ..() - -/obj/structure/janitorialcart/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/reagent_containers/glass/rag) || istype(I, /obj/item/weapon/soap)) - if (mybucket) - if(I.reagents.total_volume < I.reagents.maximum_volume) - if(mybucket.reagents.total_volume < 1) - to_chat(user, "[mybucket] is empty!") - else - mybucket.reagents.trans_to_obj(I, 5) // - to_chat(user, "You wet [I] in [mybucket].") - playsound(src, 'sound/effects/slosh.ogg', 25, 1) - else - to_chat(user, "[I] can't absorb anymore liquid!") - else - to_chat(user, "There is no bucket mounted here to dip [I] into!") - return 1 - - else if (istype(I, /obj/item/weapon/reagent_containers/glass/bucket) && mybucket) - I.afterattack(mybucket, usr, 1) - update_icon() - return 1 - - else if(istype(I, /obj/item/weapon/reagent_containers/spray) && !myspray) - equip_janicart_item(user, I) - return 1 - - else if(istype(I, /obj/item/device/lightreplacer) && !myreplacer) - equip_janicart_item(user, I) - return 1 - - else if(istype(I, /obj/item/weapon/storage/bag/trash) && !mybag) - equip_janicart_item(user, I) - return 1 - - else if(istype(I, /obj/item/clothing/suit/caution)) - equip_janicart_item(user, I) - return 1 - - else if(mybag) - return mybag.attackby(I, user) - //This return will prevent afterattack from executing if the object goes into the trashbag, - //This prevents dumb stuff like splashing the cart with the contents of a container, after putting said container into trash - - else if (!has_items) - if (I.is_wrench()) - if (do_after(user, 5 SECONDS, src)) - dismantle(user) - return - ..() - - -//New Altclick functionality! -//Altclick the cart with a mop to stow the mop away -//Altclick the cart with a reagent container to pour things into the bucket without putting the bottle in trash -/obj/structure/janitorialcart/AltClick(mob/living/user) - if(user.incapacitated() || !Adjacent(user)) return - var/obj/I = usr.get_active_hand() - if(istype(I, /obj/item/weapon/mop)) - equip_janicart_item(user, I) - else if(istype(I, /obj/item/weapon/reagent_containers) && mybucket) - var/obj/item/weapon/reagent_containers/C = I - C.afterattack(mybucket, usr, 1) - update_icon() - - -/obj/structure/janitorialcart/attack_hand(mob/user) - tgui_interact(user) - return - -/obj/structure/janitorialcart/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "JanitorCart", name) // 240, 160 - ui.set_autoupdate(FALSE) - ui.open() - -/obj/structure/janitorialcart/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - - data["mybag"] = mybag ? capitalize(mybag.name) : null - data["mybucket"] = mybucket ? capitalize(mybucket.name) : null - data["mymop"] = mymop ? capitalize(mymop.name) : null - data["myspray"] = myspray ? capitalize(myspray.name) : null - data["myreplacer"] = myreplacer ? capitalize(myreplacer.name) : null - data["signs"] = signs ? "[signs] sign\s" : null - - data["icons"] = tgui_icons - return data - -/obj/structure/janitorialcart/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - var/obj/item/I = usr.get_active_hand() - - switch(action) - if("bag") - if(mybag) - usr.put_in_hands(mybag) - to_chat(usr, "You take [mybag] from [src].") - mybag = null - nullTguiIcon("mybag") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("mop") - if(mymop) - usr.put_in_hands(mymop) - to_chat(usr, "You take [mymop] from [src].") - mymop = null - nullTguiIcon("mymop") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("spray") - if(myspray) - usr.put_in_hands(myspray) - to_chat(usr, "You take [myspray] from [src].") - myspray = null - nullTguiIcon("myspray") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("replacer") - if(myreplacer) - usr.put_in_hands(myreplacer) - to_chat(usr, "You take [myreplacer] from [src].") - myreplacer = null - nullTguiIcon("myreplacer") - else if(is_type_in_typecache(I, equippable_item_whitelist)) - equip_janicart_item(usr, I) - if("sign") - if(istype(I, /obj/item/clothing/suit/caution) && signs < 4) - equip_janicart_item(usr, I) - else if(signs) - var/obj/item/clothing/suit/caution/sign = locate() in src - if(sign) - usr.put_in_hands(sign) - to_chat(usr, "You take \a [sign] from [src].") - signs-- - if(!signs) - nullTguiIcon("signs") - else - to_chat(usr, "[src] doesn't have any signs left.") - if("bucket") - if(mybucket) - mybucket.forceMove(get_turf(usr)) - to_chat(usr, "You unmount [mybucket] from [src].") - mybucket = null - nullTguiIcon("mybucket") - else - to_chat(usr, "((Drag and drop a mop bucket onto [src] to equip it.))") - return FALSE - else - return FALSE - - update_icon() - return TRUE - -/obj/structure/janitorialcart/update_icon() - cut_overlays() - - if(mybucket) - add_overlay("cart_bucket") - if(mybucket.reagents.total_volume >= 1) - add_overlay("water_cart") - if(mybag) - add_overlay("cart_garbage") - if(mymop) - add_overlay("cart_mop") - if(myspray) - add_overlay("cart_spray") - if(myreplacer) - add_overlay("cart_replacer") - if(signs) - add_overlay("cart_sign[signs]") - -//This is called if the cart is caught in an explosion, or destroyed by weapon fire -/obj/structure/janitorialcart/proc/spill(var/chance = 100) - var/turf/dropspot = get_turf(src) - if (mymop && prob(chance)) - mymop.forceMove(dropspot) - mymop.tumble(2) - mymop = null - - if (myspray && prob(chance)) - myspray.forceMove(dropspot) - myspray.tumble(3) - myspray = null - - if (myreplacer && prob(chance)) - myreplacer.forceMove(dropspot) - myreplacer.tumble(3) - myreplacer = null - - if (mybucket && prob(chance*0.5))//bucket is heavier, harder to knock off - mybucket.forceMove(dropspot) - mybucket.tumble(1) - mybucket = null - - if (signs) - for (var/obj/item/clothing/suit/caution/Sign in src) - if (prob(min((chance*2),100))) - signs-- - Sign.forceMove(dropspot) - Sign.tumble(3) - if (signs < 0)//safety for something that shouldn't happen - signs = 0 - update_icon() - return - - if (mybag && prob(min((chance*2),100)))//Bag is flimsy - mybag.forceMove(dropspot) - mybag.tumble(1) - mybag.spill()//trashbag spills its contents too - mybag = null - - update_icon() - clearTguiIcons() - - - -/obj/structure/janitorialcart/proc/dismantle(var/mob/user = null) - if (!dismantled) - if (has_items) - spill() - - new /obj/item/stack/material/steel(src.loc, 10) - new /obj/item/stack/material/plastic(src.loc, 10) - new /obj/item/stack/rods(src.loc, 20) - dismantled = 1 - qdel(src) - - -/obj/structure/janitorialcart/ex_act(severity) - spill(100 / severity) - ..() - - - - -//old style mischievio-cart -/obj/structure/bed/chair/janicart - name = "janicart" - icon = 'icons/obj/vehicles.dmi' - icon_state = "pussywagon" - anchored = TRUE - density = TRUE - flags = OPENCONTAINER - //copypaste sorry - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - var/obj/item/weapon/storage/bag/trash/mybag = null - var/callme = "pimpin' ride" //how do people refer to it? - - -/obj/structure/bed/chair/janicart/New() - create_reagents(300) - update_layer() - - -/obj/structure/bed/chair/janicart/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "This [callme] contains [reagents.total_volume] unit\s of water!" - if(mybag) - . += "\A [mybag] is hanging on the [callme]." - - -/obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop)) - if(reagents.total_volume > 1) - reagents.trans_to_obj(I, 2) - to_chat(user, "You wet [I] in the [callme].") - playsound(src, 'sound/effects/slosh.ogg', 25, 1) - else - to_chat(user, "This [callme] is out of water!") - else if(istype(I, /obj/item/key)) - to_chat(user, "Hold [I] in one of your hands while you drive this [callme].") - else if(istype(I, /obj/item/weapon/storage/bag/trash)) - to_chat(user, "You hook the trashbag onto the [callme].") - user.drop_item() - I.loc = src - mybag = I - - -/obj/structure/bed/chair/janicart/attack_hand(mob/user) - if(mybag) - mybag.loc = get_turf(user) - user.put_in_hands(mybag) - mybag = null - else - ..() - - -/obj/structure/bed/chair/janicart/relaymove(mob/living/user, direction) - if(user.stat || user.stunned || user.weakened || user.paralysis) - unbuckle_mob() - if(user.get_type_in_hands(/obj/item/key)) - step(src, direction) - update_mob() - else - to_chat(user, "You'll need the keys in one of your hands to drive this [callme].") - - -/obj/structure/bed/chair/janicart/post_buckle_mob(mob/living/M) - update_mob() - return ..() - - -/obj/structure/bed/chair/janicart/update_layer() - if(dir == SOUTH) - layer = FLY_LAYER - else - layer = OBJ_LAYER - - -/obj/structure/bed/chair/janicart/unbuckle_mob() - var/mob/living/M = ..() - if(M) - M.pixel_x = 0 - M.pixel_y = 0 - return M - - -/obj/structure/bed/chair/janicart/set_dir() - ..() - update_layer() - if(has_buckled_mobs()) - for(var/mob/living/L as anything in buckled_mobs) - if(L.loc != loc) - L.buckled = null //Temporary, so Move() succeeds. - L.buckled = src //Restoring - - update_mob() - - -/obj/structure/bed/chair/janicart/proc/update_mob() - if(has_buckled_mobs()) - for(var/mob/living/L as anything in buckled_mobs) - L.set_dir(dir) - switch(dir) - if(SOUTH) - L.pixel_x = 0 - L.pixel_y = 7 - if(WEST) - L.pixel_x = 13 - L.pixel_y = 7 - if(NORTH) - L.pixel_x = 0 - L.pixel_y = 4 - if(EAST) - L.pixel_x = -13 - L.pixel_y = 7 - - -/obj/structure/bed/chair/janicart/bullet_act(var/obj/item/projectile/Proj) - if(has_buckled_mobs()) - if(prob(85)) - var/mob/living/L = pick(buckled_mobs) - return L.bullet_act(Proj) - visible_message("[Proj] ricochets off the [callme]!") - - -/obj/item/key - name = "key" - desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." - icon = 'icons/obj/vehicles.dmi' - icon_state = "keys" - w_class = ITEMSIZE_TINY +GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) + +/obj/structure/janitorialcart + name = "janitorial cart" + desc = "The ultimate in janitorial carts! Has space for water, mops, signs, trash bags, and more!" + description_info = "You can use alt-click while holding a mop to stow the mop. Alt-click holding a reagent container will empty the contents into the bucket without trying to put the container in any attached trash bag." + icon = 'icons/obj/janitor.dmi' + icon_state = "cart" + anchored = FALSE + density = TRUE + flags = OPENCONTAINER + climbable = TRUE + //copypaste sorry + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + var/obj/item/weapon/storage/bag/trash/mybag = null + var/obj/item/weapon/mop/mymop = null + var/obj/item/weapon/reagent_containers/spray/myspray = null + var/obj/item/device/lightreplacer/myreplacer = null + var/obj/structure/mopbucket/mybucket = null + var/has_items = FALSE + var/dismantled = TRUE + var/signs = 0 //maximum capacity hardcoded below + var/list/tgui_icons = list() + + var/static/list/equippable_item_whitelist + +/obj/structure/janitorialcart/proc/equip_janicart_item(mob/user, obj/item/I) + if(!equippable_item_whitelist) + equippable_item_whitelist = typecacheof(list( + /obj/item/weapon/storage/bag/trash, + /obj/item/weapon/mop, + /obj/item/weapon/reagent_containers/spray, + /obj/item/device/lightreplacer, + /obj/item/clothing/suit/caution, + )) + + if(!is_type_in_typecache(I, equippable_item_whitelist)) + to_chat(user, "There's no room in [src] for [I].") + return FALSE + + if(!user.canUnEquip(I)) + to_chat(user, "[I] is stuck to your hand.") + return FALSE + + if(istype(I, /obj/item/weapon/storage/bag/trash)) + if(mybag) + to_chat(user, "[src] already has \an [I].") + return FALSE + mybag = I + setTguiIcon("mybag", mybag) + + else if(istype(I, /obj/item/weapon/mop)) + if(mymop) + to_chat(user, "[src] already has \an [I].") + return FALSE + mymop = I + setTguiIcon("mymop", mymop) + + else if(istype(I, /obj/item/weapon/reagent_containers/spray)) + if(myspray) + to_chat(user, "[src] already has \an [I].") + return FALSE + myspray = I + setTguiIcon("myspray", myspray) + + else if(istype(I, /obj/item/device/lightreplacer)) + if(myreplacer) + to_chat(user, "[src] already has \an [I].") + return FALSE + myreplacer = I + setTguiIcon("myreplacer", myreplacer) + + else if(istype(I, /obj/item/clothing/suit/caution)) + if(signs < 4) + signs++ + setTguiIcon("signs", I) + else + to_chat(user, "[src] can't hold any more signs.") + return FALSE + else + // This may look like duplicate code, but it's important that we don't call unEquip *and* warn the user if + // something horrible goes wrong. (this else is never supposed to happen) + to_chat(user, "There's no room in [src] for [I].") + return FALSE + + user.drop_from_inventory(I, src) + update_icon() + to_chat(user, "You put [I] into [src].") + return TRUE + +/obj/structure/janitorialcart/proc/setTguiIcon(key, atom/A) + if(!istype(A) || !key) + return + + var/icon/F = getFlatIcon(A, defdir = SOUTH, no_anim = TRUE) + tgui_icons["[key]"] = "'data:image/png;base64,[icon2base64(F)]'" + SStgui.update_uis(src) + +/obj/structure/janitorialcart/proc/nullTguiIcon(key) + if(!key) + return + tgui_icons.Remove(key) + SStgui.update_uis(src) + +/obj/structure/janitorialcart/proc/clearTguiIcons() + tgui_icons.Cut() + SStgui.update_uis(src) + +/obj/structure/janitorialcart/Destroy() + QDEL_NULL(mybag) + QDEL_NULL(mymop) + QDEL_NULL(myspray) + QDEL_NULL(myreplacer) + QDEL_NULL(mybucket) + clearTguiIcons() + return ..() + +/obj/structure/janitorialcart/examine(mob/user) + . = ..(user) + if(istype(mybucket)) + var/contains = mybucket.reagents.total_volume + . += "\icon[src][bicon(src)] The bucket contains [contains] unit\s of liquid!" + else + . += "\icon[src][bicon(src)] There is no bucket mounted on it!" + +/obj/structure/janitorialcart/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) + if (istype(O, /obj/structure/mopbucket) && !mybucket) + O.forceMove(src) + mybucket = O + setTguiIcon("mybucket", mybucket) + to_chat(user, "You mount the [O] on the janicart.") + update_icon() + else + ..() + +/obj/structure/janitorialcart/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/reagent_containers/glass/rag) || istype(I, /obj/item/weapon/soap)) + if (mybucket) + if(I.reagents.total_volume < I.reagents.maximum_volume) + if(mybucket.reagents.total_volume < 1) + to_chat(user, "[mybucket] is empty!") + else + mybucket.reagents.trans_to_obj(I, 5) // + to_chat(user, "You wet [I] in [mybucket].") + playsound(src, 'sound/effects/slosh.ogg', 25, 1) + else + to_chat(user, "[I] can't absorb anymore liquid!") + else + to_chat(user, "There is no bucket mounted here to dip [I] into!") + return 1 + + else if (istype(I, /obj/item/weapon/reagent_containers/glass/bucket) && mybucket) + I.afterattack(mybucket, usr, 1) + update_icon() + return 1 + + else if(istype(I, /obj/item/weapon/reagent_containers/spray) && !myspray) + equip_janicart_item(user, I) + return 1 + + else if(istype(I, /obj/item/device/lightreplacer) && !myreplacer) + equip_janicart_item(user, I) + return 1 + + else if(istype(I, /obj/item/weapon/storage/bag/trash) && !mybag) + equip_janicart_item(user, I) + return 1 + + else if(istype(I, /obj/item/clothing/suit/caution)) + equip_janicart_item(user, I) + return 1 + + else if(mybag) + return mybag.attackby(I, user) + //This return will prevent afterattack from executing if the object goes into the trashbag, + //This prevents dumb stuff like splashing the cart with the contents of a container, after putting said container into trash + + else if (!has_items) + if (I.has_tool_quality(TOOL_WRENCH)) + if (do_after(user, 5 SECONDS, src)) + dismantle(user) + return + ..() + + +//New Altclick functionality! +//Altclick the cart with a mop to stow the mop away +//Altclick the cart with a reagent container to pour things into the bucket without putting the bottle in trash +/obj/structure/janitorialcart/AltClick(mob/living/user) + if(user.incapacitated() || !Adjacent(user)) return + var/obj/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/mop)) + equip_janicart_item(user, I) + else if(istype(I, /obj/item/weapon/reagent_containers) && mybucket) + var/obj/item/weapon/reagent_containers/C = I + C.afterattack(mybucket, usr, 1) + update_icon() + + +/obj/structure/janitorialcart/attack_hand(mob/user) + tgui_interact(user) + return + +/obj/structure/janitorialcart/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "JanitorCart", name) // 240, 160 + ui.set_autoupdate(FALSE) + ui.open() + +/obj/structure/janitorialcart/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["mybag"] = mybag ? capitalize(mybag.name) : null + data["mybucket"] = mybucket ? capitalize(mybucket.name) : null + data["mymop"] = mymop ? capitalize(mymop.name) : null + data["myspray"] = myspray ? capitalize(myspray.name) : null + data["myreplacer"] = myreplacer ? capitalize(myreplacer.name) : null + data["signs"] = signs ? "[signs] sign\s" : null + + data["icons"] = tgui_icons + return data + +/obj/structure/janitorialcart/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + var/obj/item/I = usr.get_active_hand() + + switch(action) + if("bag") + if(mybag) + usr.put_in_hands(mybag) + to_chat(usr, "You take [mybag] from [src].") + mybag = null + nullTguiIcon("mybag") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("mop") + if(mymop) + usr.put_in_hands(mymop) + to_chat(usr, "You take [mymop] from [src].") + mymop = null + nullTguiIcon("mymop") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("spray") + if(myspray) + usr.put_in_hands(myspray) + to_chat(usr, "You take [myspray] from [src].") + myspray = null + nullTguiIcon("myspray") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("replacer") + if(myreplacer) + usr.put_in_hands(myreplacer) + to_chat(usr, "You take [myreplacer] from [src].") + myreplacer = null + nullTguiIcon("myreplacer") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("sign") + if(istype(I, /obj/item/clothing/suit/caution) && signs < 4) + equip_janicart_item(usr, I) + else if(signs) + var/obj/item/clothing/suit/caution/sign = locate() in src + if(sign) + usr.put_in_hands(sign) + to_chat(usr, "You take \a [sign] from [src].") + signs-- + if(!signs) + nullTguiIcon("signs") + else + to_chat(usr, "[src] doesn't have any signs left.") + if("bucket") + if(mybucket) + mybucket.forceMove(get_turf(usr)) + to_chat(usr, "You unmount [mybucket] from [src].") + mybucket = null + nullTguiIcon("mybucket") + else + to_chat(usr, "((Drag and drop a mop bucket onto [src] to equip it.))") + return FALSE + else + return FALSE + + update_icon() + return TRUE + +/obj/structure/janitorialcart/update_icon() + cut_overlays() + + if(mybucket) + add_overlay("cart_bucket") + if(mybucket.reagents.total_volume >= 1) + add_overlay("water_cart") + if(mybag) + add_overlay("cart_garbage") + if(mymop) + add_overlay("cart_mop") + if(myspray) + add_overlay("cart_spray") + if(myreplacer) + add_overlay("cart_replacer") + if(signs) + add_overlay("cart_sign[signs]") + +//This is called if the cart is caught in an explosion, or destroyed by weapon fire +/obj/structure/janitorialcart/proc/spill(var/chance = 100) + var/turf/dropspot = get_turf(src) + if (mymop && prob(chance)) + mymop.forceMove(dropspot) + mymop.tumble(2) + mymop = null + + if (myspray && prob(chance)) + myspray.forceMove(dropspot) + myspray.tumble(3) + myspray = null + + if (myreplacer && prob(chance)) + myreplacer.forceMove(dropspot) + myreplacer.tumble(3) + myreplacer = null + + if (mybucket && prob(chance*0.5))//bucket is heavier, harder to knock off + mybucket.forceMove(dropspot) + mybucket.tumble(1) + mybucket = null + + if (signs) + for (var/obj/item/clothing/suit/caution/Sign in src) + if (prob(min((chance*2),100))) + signs-- + Sign.forceMove(dropspot) + Sign.tumble(3) + if (signs < 0)//safety for something that shouldn't happen + signs = 0 + update_icon() + return + + if (mybag && prob(min((chance*2),100)))//Bag is flimsy + mybag.forceMove(dropspot) + mybag.tumble(1) + mybag.spill()//trashbag spills its contents too + mybag = null + + update_icon() + clearTguiIcons() + + + +/obj/structure/janitorialcart/proc/dismantle(var/mob/user = null) + if (!dismantled) + if (has_items) + spill() + + new /obj/item/stack/material/steel(src.loc, 10) + new /obj/item/stack/material/plastic(src.loc, 10) + new /obj/item/stack/rods(src.loc, 20) + dismantled = 1 + qdel(src) + + +/obj/structure/janitorialcart/ex_act(severity) + spill(100 / severity) + ..() + + + + +//old style mischievio-cart +/obj/structure/bed/chair/janicart + name = "janicart" + icon = 'icons/obj/vehicles.dmi' + icon_state = "pussywagon" + anchored = TRUE + density = TRUE + flags = OPENCONTAINER + //copypaste sorry + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + var/obj/item/weapon/storage/bag/trash/mybag = null + var/callme = "pimpin' ride" //how do people refer to it? + + +/obj/structure/bed/chair/janicart/New() + create_reagents(300) + update_layer() + + +/obj/structure/bed/chair/janicart/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "This [callme] contains [reagents.total_volume] unit\s of water!" + if(mybag) + . += "\A [mybag] is hanging on the [callme]." + + +/obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop)) + if(reagents.total_volume > 1) + reagents.trans_to_obj(I, 2) + to_chat(user, "You wet [I] in the [callme].") + playsound(src, 'sound/effects/slosh.ogg', 25, 1) + else + to_chat(user, "This [callme] is out of water!") + else if(istype(I, /obj/item/key)) + to_chat(user, "Hold [I] in one of your hands while you drive this [callme].") + else if(istype(I, /obj/item/weapon/storage/bag/trash)) + to_chat(user, "You hook the trashbag onto the [callme].") + user.drop_item() + I.loc = src + mybag = I + + +/obj/structure/bed/chair/janicart/attack_hand(mob/user) + if(mybag) + mybag.loc = get_turf(user) + user.put_in_hands(mybag) + mybag = null + else + ..() + + +/obj/structure/bed/chair/janicart/relaymove(mob/living/user, direction) + if(user.stat || user.stunned || user.weakened || user.paralysis) + unbuckle_mob() + if(user.get_type_in_hands(/obj/item/key)) + step(src, direction) + update_mob() + else + to_chat(user, "You'll need the keys in one of your hands to drive this [callme].") + + +/obj/structure/bed/chair/janicart/post_buckle_mob(mob/living/M) + update_mob() + return ..() + + +/obj/structure/bed/chair/janicart/update_layer() + if(dir == SOUTH) + layer = FLY_LAYER + else + layer = OBJ_LAYER + + +/obj/structure/bed/chair/janicart/unbuckle_mob() + var/mob/living/M = ..() + if(M) + M.pixel_x = 0 + M.pixel_y = 0 + return M + + +/obj/structure/bed/chair/janicart/set_dir() + ..() + update_layer() + if(has_buckled_mobs()) + for(var/mob/living/L as anything in buckled_mobs) + if(L.loc != loc) + L.buckled = null //Temporary, so Move() succeeds. + L.buckled = src //Restoring + + update_mob() + + +/obj/structure/bed/chair/janicart/proc/update_mob() + if(has_buckled_mobs()) + for(var/mob/living/L as anything in buckled_mobs) + L.set_dir(dir) + switch(dir) + if(SOUTH) + L.pixel_x = 0 + L.pixel_y = 7 + if(WEST) + L.pixel_x = 13 + L.pixel_y = 7 + if(NORTH) + L.pixel_x = 0 + L.pixel_y = 4 + if(EAST) + L.pixel_x = -13 + L.pixel_y = 7 + + +/obj/structure/bed/chair/janicart/bullet_act(var/obj/item/projectile/Proj) + if(has_buckled_mobs()) + if(prob(85)) + var/mob/living/L = pick(buckled_mobs) + return L.bullet_act(Proj) + visible_message("[Proj] ricochets off the [callme]!") + + +/obj/item/key + name = "key" + desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"." + icon = 'icons/obj/vehicles.dmi' + icon_state = "keys" + w_class = ITEMSIZE_TINY diff --git a/code/game/objects/structures/kitchen_foodcart_vr.dm b/code/game/objects/structures/kitchen_foodcart_vr.dm index 4fb56b038bd..aa1ea26cf49 100644 --- a/code/game/objects/structures/kitchen_foodcart_vr.dm +++ b/code/game/objects/structures/kitchen_foodcart_vr.dm @@ -1,42 +1,42 @@ -/obj/structure/foodcart - name = "Foodcart" - icon = 'icons/obj/kitchen_vr.dmi' - icon_state = "foodcart-0" - desc = "The ultimate in food transport! When opened you notice two compartments with odd blue glows to them. One feels very warm, while the other is very cold." - anchored = FALSE - opacity = 0 - density = TRUE - -/obj/structure/foodcart/Initialize() - . = ..() - for(var/obj/item/I in loc) - if(istype(I, /obj/item/weapon/reagent_containers/food)) - I.loc = src - update_icon() - -/obj/structure/foodcart/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/weapon/reagent_containers/food)) - user.drop_item() - O.loc = src - update_icon() - else - return - -/obj/structure/foodcart/attack_hand(var/mob/user as mob) - if(contents.len) - var/obj/item/weapon/reagent_containers/food/choice = tgui_input_list(usr, "What would you like to grab from the cart?", "Grab Choice", contents) - if(choice) - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(choice) - else - choice.loc = get_turf(src) - update_icon() - -/obj/structure/foodcart/update_icon() - if(contents.len < 5) - icon_state = "foodcart-[contents.len]" - else +/obj/structure/foodcart + name = "Foodcart" + icon = 'icons/obj/kitchen_vr.dmi' + icon_state = "foodcart-0" + desc = "The ultimate in food transport! When opened you notice two compartments with odd blue glows to them. One feels very warm, while the other is very cold." + anchored = FALSE + opacity = 0 + density = TRUE + +/obj/structure/foodcart/Initialize() + . = ..() + for(var/obj/item/I in loc) + if(istype(I, /obj/item/weapon/reagent_containers/food)) + I.loc = src + update_icon() + +/obj/structure/foodcart/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/weapon/reagent_containers/food)) + user.drop_item() + O.loc = src + update_icon() + else + return + +/obj/structure/foodcart/attack_hand(var/mob/user as mob) + if(contents.len) + var/obj/item/weapon/reagent_containers/food/choice = tgui_input_list(usr, "What would you like to grab from the cart?", "Grab Choice", contents) + if(choice) + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(choice) + else + choice.loc = get_turf(src) + update_icon() + +/obj/structure/foodcart/update_icon() + if(contents.len < 5) + icon_state = "foodcart-[contents.len]" + else icon_state = "foodcart-5" \ No newline at end of file diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 48e9e113e8e..777530a276d 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -1,63 +1,63 @@ -//////Kitchen Spike - -/obj/structure/kitchenspike - name = "meat spike" - icon = 'icons/obj/kitchen.dmi' - icon_state = "spike" - desc = "A spike for collecting meat from animals." - density = TRUE - anchored = TRUE - var/meat = 0 - var/occupied - var/meat_type - var/victim_name = "corpse" - -/obj/structure/kitchenspike/attackby(obj/item/weapon/grab/G as obj, mob/user as mob) - if(!istype(G, /obj/item/weapon/grab) || !ismob(G.affecting)) - return - if(occupied) - to_chat(user, "The spike already has something on it, finish collecting its meat first!") - else - if(spike(G.affecting)) - var/datum/gender/T = gender_datums[G.affecting.get_visible_gender()] - visible_message("[user] has forced [G.affecting] onto the spike, killing [T.him] instantly!") - var/mob/M = G.affecting - M.forceMove(src) - qdel(G) - qdel(M) - else - to_chat(user, "They are too big for the spike, try something smaller!") - -/obj/structure/kitchenspike/proc/spike(var/mob/living/victim) - if(!istype(victim)) - return - - if(istype(victim, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = victim - if(istype(H.species, /datum/species/monkey)) - meat_type = H.species.meat_type - icon_state = "spikebloody" - else - return 0 - else if(istype(victim, /mob/living/carbon/alien)) - meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat - icon_state = "spikebloodygreen" - else - return 0 - - victim_name = victim.name - occupied = 1 - meat = 5 - return 1 - -/obj/structure/kitchenspike/attack_hand(mob/user as mob) - if(..() || !occupied) - return - meat-- - new meat_type(get_turf(src)) - if(meat > 1) - to_chat(user, "You cut some meat from \the [victim_name]'s body.") - else if(meat == 1) - to_chat(user, "You remove the last piece of meat from \the [victim_name]!") - icon_state = "spike" - occupied = 0 +//////Kitchen Spike + +/obj/structure/kitchenspike + name = "meat spike" + icon = 'icons/obj/kitchen.dmi' + icon_state = "spike" + desc = "A spike for collecting meat from animals." + density = TRUE + anchored = TRUE + var/meat = 0 + var/occupied + var/meat_type + var/victim_name = "corpse" + +/obj/structure/kitchenspike/attackby(obj/item/weapon/grab/G as obj, mob/user as mob) + if(!istype(G, /obj/item/weapon/grab) || !ismob(G.affecting)) + return + if(occupied) + to_chat(user, "The spike already has something on it, finish collecting its meat first!") + else + if(spike(G.affecting)) + var/datum/gender/T = gender_datums[G.affecting.get_visible_gender()] + visible_message("[user] has forced [G.affecting] onto the spike, killing [T.him] instantly!") + var/mob/M = G.affecting + M.forceMove(src) + qdel(G) + qdel(M) + else + to_chat(user, "They are too big for the spike, try something smaller!") + +/obj/structure/kitchenspike/proc/spike(var/mob/living/victim) + if(!istype(victim)) + return + + if(istype(victim, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = victim + if(istype(H.species, /datum/species/monkey)) + meat_type = H.species.meat_type + icon_state = "spikebloody" + else + return 0 + else if(istype(victim, /mob/living/carbon/alien)) + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat + icon_state = "spikebloodygreen" + else + return 0 + + victim_name = victim.name + occupied = 1 + meat = 5 + return 1 + +/obj/structure/kitchenspike/attack_hand(mob/user as mob) + if(..() || !occupied) + return + meat-- + new meat_type(get_turf(src)) + if(meat > 1) + to_chat(user, "You cut some meat from \the [victim_name]'s body.") + else if(meat == 1) + to_chat(user, "You remove the last piece of meat from \the [victim_name]!") + icon_state = "spike" + occupied = 0 diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index d5fcf7e8d1d..978b99ab3df 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -1,100 +1,100 @@ -/obj/structure/lattice - name = "lattice" - desc = "A lightweight support lattice." - icon = 'icons/obj/structures.dmi' - icon_state = "latticefull" - density = FALSE - anchored = TRUE - w_class = ITEMSIZE_NORMAL - plane = PLATING_PLANE - -/obj/structure/lattice/Initialize() - . = ..() - - if(!(istype(src.loc, /turf/space) || istype(src.loc, /turf/simulated/open) || istype(src.loc, /turf/simulated/mineral) || istype(src.loc, /turf/simulated/shuttle/plating/airless/carry))) - return INITIALIZE_HINT_QDEL - - for(var/obj/structure/lattice/LAT in src.loc) - if(LAT != src) - log_debug("Found multiple lattices at '[log_info_line(loc)]'") //VOREStation Edit, why was this a runtime, it's harmless - return INITIALIZE_HINT_QDEL - icon = 'icons/obj/smoothlattice.dmi' - icon_state = "latticeblank" - updateOverlays() - for (var/dir in cardinal) - var/obj/structure/lattice/L - if(locate(/obj/structure/lattice, get_step(src, dir))) - L = locate(/obj/structure/lattice, get_step(src, dir)) - L.updateOverlays() - -/obj/structure/lattice/Destroy() - for (var/dir in cardinal) - var/obj/structure/lattice/L - if(locate(/obj/structure/lattice, get_step(src, dir))) - L = locate(/obj/structure/lattice, get_step(src, dir)) - L.updateOverlays(src.loc) - if(istype(loc, /turf/simulated/open)) - var/turf/simulated/open/O = loc - spawn(1) - if(istype(O)) // If we built a new floor with the lattice, the open turf won't exist anymore. - O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down. - . = ..() - -/obj/structure/lattice/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - qdel(src) - return - if(3.0) - return - else - return - -/obj/structure/lattice/attackby(obj/item/C as obj, mob/user as mob) - - if (istype(C, /obj/item/stack/tile/floor)) - var/turf/T = get_turf(src) - T.attackby(C, user) //BubbleWrap - hand this off to the underlying turf instead - return - if (istype(C, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = C - if(WT.welding == 1) - if(WT.remove_fuel(0, user)) - to_chat(user, "Slicing lattice joints ...") - new /obj/item/stack/rods(src.loc) - qdel(src) - return - if (istype(C, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = C - if(R.get_amount() < 2) - to_chat(user, "You need at least two rods to form a catwalk here.") - else - to_chat(user, "You start connecting \the [R.name] to \the [src.name] ...") - if(do_after(user, 5 SECONDS)) - R.use(2) //2023-02-27 bugfix to prevent rods being used without catwalk creation - src.alpha = 0 // Note: I don't know why this is set, Eris did it, just trusting for now. ~Leshana - new /obj/structure/catwalk(src.loc) - qdel(src) - return - return - -/obj/structure/lattice/proc/updateOverlays() - //if(!(istype(src.loc, /turf/space))) - // qdel(src) - spawn(1) - cut_overlays() - - var/dir_sum = 0 - - for (var/direction in cardinal) - if(locate(/obj/structure/lattice, get_step(src, direction))) - dir_sum += direction - else - if(!(istype(get_step(src, direction), /turf/space))) - dir_sum += direction - - icon_state = "lattice[dir_sum]" - return +/obj/structure/lattice + name = "lattice" + desc = "A lightweight support lattice." + icon = 'icons/obj/structures.dmi' + icon_state = "latticefull" + density = FALSE + anchored = TRUE + w_class = ITEMSIZE_NORMAL + plane = PLATING_PLANE + +/obj/structure/lattice/Initialize() + . = ..() + + if(!(istype(src.loc, /turf/space) || istype(src.loc, /turf/simulated/open) || istype(src.loc, /turf/simulated/mineral) || istype(src.loc, /turf/simulated/shuttle/plating/airless/carry))) + return INITIALIZE_HINT_QDEL + + for(var/obj/structure/lattice/LAT in src.loc) + if(LAT != src) + log_debug("Found multiple lattices at '[log_info_line(loc)]'") //VOREStation Edit, why was this a runtime, it's harmless + return INITIALIZE_HINT_QDEL + icon = 'icons/obj/smoothlattice.dmi' + icon_state = "latticeblank" + updateOverlays() + for (var/dir in cardinal) + var/obj/structure/lattice/L + if(locate(/obj/structure/lattice, get_step(src, dir))) + L = locate(/obj/structure/lattice, get_step(src, dir)) + L.updateOverlays() + +/obj/structure/lattice/Destroy() + for (var/dir in cardinal) + var/obj/structure/lattice/L + if(locate(/obj/structure/lattice, get_step(src, dir))) + L = locate(/obj/structure/lattice, get_step(src, dir)) + L.updateOverlays(src.loc) + if(istype(loc, /turf/simulated/open)) + var/turf/simulated/open/O = loc + spawn(1) + if(istype(O)) // If we built a new floor with the lattice, the open turf won't exist anymore. + O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down. + . = ..() + +/obj/structure/lattice/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + qdel(src) + return + if(3.0) + return + else + return + +/obj/structure/lattice/attackby(obj/item/C as obj, mob/user as mob) + + if(istype(C, /obj/item/stack/tile/floor)) + var/turf/T = get_turf(src) + T.attackby(C, user) //BubbleWrap - hand this off to the underlying turf instead + return + if(C.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = C.get_welder() + if(WT.welding == 1) + if(WT.remove_fuel(0, user)) + to_chat(user, "Slicing lattice joints ...") + new /obj/item/stack/rods(src.loc) + qdel(src) + return + if(istype(C, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = C + if(R.get_amount() < 2) + to_chat(user, "You need at least two rods to form a catwalk here.") + else + to_chat(user, "You start connecting \the [R.name] to \the [src.name] ...") + if(do_after(user, 5 SECONDS)) + R.use(2) //2023-02-27 bugfix to prevent rods being used without catwalk creation + src.alpha = 0 // Note: I don't know why this is set, Eris did it, just trusting for now. ~Leshana + new /obj/structure/catwalk(src.loc) + qdel(src) + return + return + +/obj/structure/lattice/proc/updateOverlays() + //if(!(istype(src.loc, /turf/space))) + // qdel(src) + spawn(1) + cut_overlays() + + var/dir_sum = 0 + + for (var/direction in cardinal) + if(locate(/obj/structure/lattice, get_step(src, direction))) + dir_sum += direction + else + if(!(istype(get_step(src, direction), /turf/space))) + dir_sum += direction + + icon_state = "lattice[dir_sum]" + return diff --git a/code/game/objects/structures/lightpost.dm b/code/game/objects/structures/lightpost.dm index 95f00d1467b..b915147c175 100644 --- a/code/game/objects/structures/lightpost.dm +++ b/code/game/objects/structures/lightpost.dm @@ -1,42 +1,42 @@ -/obj/structure/lightpost - name = "lightpost" - desc = "A homely lightpost." - icon = 'icons/obj/32x64.dmi' - icon_state = "lightpost" - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - anchored = TRUE - density = TRUE - opacity = FALSE - - var/lit = TRUE // If true, will have a glowing overlay and lighting. - var/festive = FALSE // If true, adds a festive bow overlay to it. - -/obj/structure/lightpost/Initialize() - update_icon() - return ..() - -/obj/structure/lightpost/update_icon() - cut_overlays() - - if(lit) - set_light(5, 1, "#E9E4AF") - var/image/glow = image(icon_state = "[icon_state]-glow") - glow.plane = PLANE_LIGHTING_ABOVE - add_overlay(glow) - else - set_light(0) - - if(festive) - var/image/bow = image(icon_state = "[icon_state]-festive") - add_overlay(bow) - -/obj/structure/lightpost/unlit - lit = FALSE - -/obj/structure/lightpost/festive - desc = "A homely lightpost adorned with festive decor." - festive = TRUE - -/obj/structure/lightpost/festive/unlit +/obj/structure/lightpost + name = "lightpost" + desc = "A homely lightpost." + icon = 'icons/obj/32x64.dmi' + icon_state = "lightpost" + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = TRUE + density = TRUE + opacity = FALSE + + var/lit = TRUE // If true, will have a glowing overlay and lighting. + var/festive = FALSE // If true, adds a festive bow overlay to it. + +/obj/structure/lightpost/Initialize() + update_icon() + return ..() + +/obj/structure/lightpost/update_icon() + cut_overlays() + + if(lit) + set_light(5, 1, "#E9E4AF") + var/image/glow = image(icon_state = "[icon_state]-glow") + glow.plane = PLANE_LIGHTING_ABOVE + add_overlay(glow) + else + set_light(0) + + if(festive) + var/image/bow = image(icon_state = "[icon_state]-festive") + add_overlay(bow) + +/obj/structure/lightpost/unlit + lit = FALSE + +/obj/structure/lightpost/festive + desc = "A homely lightpost adorned with festive decor." + festive = TRUE + +/obj/structure/lightpost/festive/unlit lit = FALSE \ No newline at end of file diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index 0100167a05a..a43a15afbe3 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -349,7 +349,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/weapon/stock_parts/subspace/treatment, /obj/item/frame, /obj/item/broken_device/random, - /obj/item/borg/upgrade/restart, + /obj/item/borg/upgrade/utility/restart, /obj/item/weapon/cell, /obj/item/weapon/cell/high, /obj/item/weapon/cell/device, @@ -407,10 +407,10 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/device/tvcamera, /obj/item/device/universal_translator, /obj/item/device/aicard, - /obj/item/borg/upgrade/jetpack, - /obj/item/borg/upgrade/advhealth, - /obj/item/borg/upgrade/vtec, - /obj/item/borg/upgrade/tasercooler, + /obj/item/borg/upgrade/advanced/jetpack, + /obj/item/borg/upgrade/advanced/advhealth, + /obj/item/borg/upgrade/basic/vtec, + /obj/item/borg/upgrade/restricted/tasercooler, /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser, /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill, /obj/item/rig_module/device/drill, @@ -917,11 +917,11 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh rare_loot = list( /obj/item/weapon/cell/super, - /obj/item/borg/upgrade/restart, - /obj/item/borg/upgrade/jetpack, - /obj/item/borg/upgrade/tasercooler, - /obj/item/borg/upgrade/syndicate, - /obj/item/borg/upgrade/vtec + /obj/item/borg/upgrade/utility/restart, + /obj/item/borg/upgrade/advanced/jetpack, + /obj/item/borg/upgrade/restricted/tasercooler, + /obj/item/borg/upgrade/basic/syndicate, + /obj/item/borg/upgrade/basic/vtec ) // Contains old mediciation, most of it unidentified and has a good chance of being useless. @@ -946,4 +946,4 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh common_loot = list( /obj/random/unidentified_medicine/fresh_medicine - ) \ No newline at end of file + ) diff --git a/code/game/objects/structures/low_wall.dm b/code/game/objects/structures/low_wall.dm index 08b43c2c977..8d0d035b960 100644 --- a/code/game/objects/structures/low_wall.dm +++ b/code/game/objects/structures/low_wall.dm @@ -86,7 +86,7 @@ return // Dismantling the half wall - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) for(var/obj/structure/S in loc) if(istype(S, /obj/structure/window)) to_chat(user, "There is still a window on the low wall!") diff --git a/code/game/objects/structures/medical_stand_vr.dm b/code/game/objects/structures/medical_stand_vr.dm index e2eca967ff1..63b6f365f52 100644 --- a/code/game/objects/structures/medical_stand_vr.dm +++ b/code/game/objects/structures/medical_stand_vr.dm @@ -295,7 +295,7 @@ to_chat(user, "Close the valve first.") return if (tank) - if(!W.is_wrench()) + if(!W.has_tool_quality(TOOL_WRENCH)) return if (!is_loosen) is_loosen = TRUE diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 8cb67498234..a58eba45aae 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -1,144 +1,144 @@ -//wip wip wup -/obj/structure/mirror - name = "mirror" - desc = "A SalonPro Nano-Mirror(TM) brand mirror! The leading technology in hair salon products, utilizing nano-machinery to style your hair just right." - icon = 'icons/obj/watercloset.dmi' - icon_state = "mirror" - layer = ABOVE_WINDOW_LAYER - density = FALSE - anchored = TRUE - var/shattered = 0 - var/glass = 1 - var/datum/tgui_module/appearance_changer/mirror/M - -/obj/structure/mirror/Initialize(mapload, var/dir, var/building = 0, mob/user as mob) - M = new(src, null) - if(building) - glass = 0 - icon_state = "mirror_frame" - pixel_x = (dir & 3)? 0 : (dir == 4 ? -28 : 28) - pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0 - return - -/obj/structure/mirror/Destroy() - QDEL_NULL(M) - . = ..() - -/obj/structure/mirror/attack_hand(mob/user as mob) - if(!glass) return - if(shattered) return - - if(ishuman(user)) - M.tgui_interact(user) - -/obj/structure/mirror/proc/shatter() - if(!glass) return - if(shattered) return - shattered = 1 - icon_state = "mirror_broke" - playsound(src, "shatter", 70, 1) - desc = "Oh no, seven years of bad luck!" - - -/obj/structure/mirror/bullet_act(var/obj/item/projectile/Proj) - - if(prob(Proj.get_structure_damage() * 2)) - if(!shattered) - shatter() - else if(glass) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - ..() - -/obj/structure/mirror/attackby(obj/item/I as obj, mob/user as mob) - if(I.is_wrench()) - if(!glass) - playsound(src, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed)) - to_chat(user, "You unfasten the frame.") - new /obj/item/frame/mirror( src.loc ) - qdel(src) - return - if(I.is_wrench()) - if(shattered && glass) - to_chat(user, "The broken glass falls out.") - icon_state = "mirror_frame" - glass = !glass - new /obj/item/weapon/material/shard( src.loc ) - return - if(!shattered && glass) - playsound(src, I.usesound, 50, 1) - to_chat(user, "You remove the glass.") - glass = !glass - icon_state = "mirror_frame" - new /obj/item/stack/material/glass( src.loc, 2 ) - return - - if(istype(I, /obj/item/stack/material/glass)) - if(!glass) - var/obj/item/stack/material/glass/G = I - if (G.get_amount() < 2) - to_chat(user, "You need two sheets of glass to add them to the frame.") - return - to_chat(user, "You start to add the glass to the frame.") - if(do_after(user, 20)) - if (G.use(2)) - shattered = 0 - glass = 1 - icon_state = "mirror" - to_chat(user, "You add the glass to the frame.") - return - - if(shattered && glass) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - return - - if(prob(I.force * 2)) - visible_message("[user] smashes [src] with [I]!") - if(glass) - shatter() - else - visible_message("[user] hits [src] with [I]!") - playsound(src, 'sound/effects/Glasshit.ogg', 70, 1) - -/obj/structure/mirror/attack_generic(var/mob/user, var/damage) - - user.do_attack_animation(src) - if(shattered && glass) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - return 0 - - if(damage) - user.visible_message("[user] smashes [src]!") - if(glass) - shatter() - else - user.visible_message("[user] hits [src] and bounces off!") - return 1 - -// The following mirror is ~special~. -/obj/structure/mirror/raider - name = "cracked mirror" - desc = "Something seems strange about this old, dirty mirror. Your reflection doesn't look like you remember it." - icon_state = "mirror_broke" - shattered = 1 - -/obj/structure/mirror/raider/attack_hand(var/mob/living/carbon/human/user) - if(istype(get_area(src),/area/syndicate_mothership)) - if(istype(user) && user.mind && user.mind.special_role == "Raider" && user.species.name != SPECIES_VOX && is_alien_whitelisted(user, SPECIES_VOX)) - var/choice = tgui_alert(usr, "Do you wish to become a true Vox of the Shoal? This is not reversible.", "Become Vox?", list("No","Yes")) - if(choice && choice == "Yes") - var/mob/living/carbon/human/vox/vox = new(get_turf(src),SPECIES_VOX) - vox.gender = user.gender - raiders.equip(vox) - if(user.mind) - user.mind.transfer_to(vox) - spawn(1) - var/newname = sanitizeSafe(tgui_input_text(vox,"Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) - if(!newname || newname == "") - var/datum/language/L = GLOB.all_languages[vox.species.default_language] - newname = L.get_random_name() - vox.real_name = newname - vox.name = vox.real_name - raiders.update_access(vox) - qdel(user) +//wip wip wup +/obj/structure/mirror + name = "mirror" + desc = "A SalonPro Nano-Mirror(TM) brand mirror! The leading technology in hair salon products, utilizing nano-machinery to style your hair just right." + icon = 'icons/obj/watercloset.dmi' + icon_state = "mirror" + layer = ABOVE_WINDOW_LAYER + density = FALSE + anchored = TRUE + var/shattered = 0 + var/glass = 1 + var/datum/tgui_module/appearance_changer/mirror/M + +/obj/structure/mirror/Initialize(mapload, var/dir, var/building = 0, mob/user as mob) + M = new(src, null) + if(building) + glass = 0 + icon_state = "mirror_frame" + pixel_x = (dir & 3)? 0 : (dir == 4 ? -28 : 28) + pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0 + return + +/obj/structure/mirror/Destroy() + QDEL_NULL(M) + . = ..() + +/obj/structure/mirror/attack_hand(mob/user as mob) + if(!glass) return + if(shattered) return + + if(ishuman(user)) + M.tgui_interact(user) + +/obj/structure/mirror/proc/shatter() + if(!glass) return + if(shattered) return + shattered = 1 + icon_state = "mirror_broke" + playsound(src, "shatter", 70, 1) + desc = "Oh no, seven years of bad luck!" + + +/obj/structure/mirror/bullet_act(var/obj/item/projectile/Proj) + + if(prob(Proj.get_structure_damage() * 2)) + if(!shattered) + shatter() + else if(glass) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) + ..() + +/obj/structure/mirror/attackby(obj/item/I as obj, mob/user as mob) + if(I.has_tool_quality(TOOL_WRENCH)) + if(!glass) + playsound(src, I.usesound, 50, 1) + if(do_after(user, 20 * I.toolspeed)) + to_chat(user, "You unfasten the frame.") + new /obj/item/frame/mirror( src.loc ) + qdel(src) + return + if(I.has_tool_quality(TOOL_WRENCH)) + if(shattered && glass) + to_chat(user, "The broken glass falls out.") + icon_state = "mirror_frame" + glass = !glass + new /obj/item/weapon/material/shard( src.loc ) + return + if(!shattered && glass) + playsound(src, I.usesound, 50, 1) + to_chat(user, "You remove the glass.") + glass = !glass + icon_state = "mirror_frame" + new /obj/item/stack/material/glass( src.loc, 2 ) + return + + if(istype(I, /obj/item/stack/material/glass)) + if(!glass) + var/obj/item/stack/material/glass/G = I + if (G.get_amount() < 2) + to_chat(user, "You need two sheets of glass to add them to the frame.") + return + to_chat(user, "You start to add the glass to the frame.") + if(do_after(user, 20)) + if (G.use(2)) + shattered = 0 + glass = 1 + icon_state = "mirror" + to_chat(user, "You add the glass to the frame.") + return + + if(shattered && glass) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) + return + + if(prob(I.force * 2)) + visible_message("[user] smashes [src] with [I]!") + if(glass) + shatter() + else + visible_message("[user] hits [src] with [I]!") + playsound(src, 'sound/effects/Glasshit.ogg', 70, 1) + +/obj/structure/mirror/attack_generic(var/mob/user, var/damage) + + user.do_attack_animation(src) + if(shattered && glass) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) + return 0 + + if(damage) + user.visible_message("[user] smashes [src]!") + if(glass) + shatter() + else + user.visible_message("[user] hits [src] and bounces off!") + return 1 + +// The following mirror is ~special~. +/obj/structure/mirror/raider + name = "cracked mirror" + desc = "Something seems strange about this old, dirty mirror. Your reflection doesn't look like you remember it." + icon_state = "mirror_broke" + shattered = 1 + +/obj/structure/mirror/raider/attack_hand(var/mob/living/carbon/human/user) + if(istype(get_area(src),/area/syndicate_mothership)) + if(istype(user) && user.mind && user.mind.special_role == "Raider" && user.species.name != SPECIES_VOX && is_alien_whitelisted(user, SPECIES_VOX)) + var/choice = tgui_alert(usr, "Do you wish to become a true Vox of the Shoal? This is not reversible.", "Become Vox?", list("No","Yes")) + if(choice && choice == "Yes") + var/mob/living/carbon/human/vox/vox = new(get_turf(src),SPECIES_VOX) + vox.gender = user.gender + raiders.equip(vox) + if(user.mind) + user.mind.transfer_to(vox) + spawn(1) + var/newname = sanitizeSafe(tgui_input_text(vox,"Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) + if(!newname || newname == "") + var/datum/language/L = GLOB.all_languages[vox.species.default_language] + newname = L.get_random_name() + vox.real_name = newname + vox.name = vox.real_name + raiders.update_access(vox) + qdel(user) ..() \ No newline at end of file diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index 064281c9a94..2ce7d1126ae 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -1,31 +1,31 @@ -/obj/structure/mopbucket - name = "mop bucket" - desc = "Fill it with water, but don't forget a mop!" - icon = 'icons/obj/janitor.dmi' - icon_state = "mopbucket" - density = TRUE - climbable = TRUE - w_class = ITEMSIZE_NORMAL - pressure_resistance = 5 - flags = OPENCONTAINER - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - -GLOBAL_LIST_BOILERPLATE(all_mopbuckets, /obj/structure/mopbucket) - -/obj/structure/mopbucket/New() - create_reagents(300) - ..() - -/obj/structure/mopbucket/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "It contains [reagents.total_volume] unit\s of water!" - -/obj/structure/mopbucket/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap) || istype(I, /obj/item/weapon/reagent_containers/glass/rag)) //VOREStation Edit - "Allows soap and rags to be used on mopbuckets" - if(reagents.total_volume < 1) - to_chat(user, "\The [src] is out of water!") - else - reagents.trans_to_obj(I, 5) - to_chat(user, "You wet \the [I] in \the [src].") - playsound(src, 'sound/effects/slosh.ogg', 25, 1) +/obj/structure/mopbucket + name = "mop bucket" + desc = "Fill it with water, but don't forget a mop!" + icon = 'icons/obj/janitor.dmi' + icon_state = "mopbucket" + density = TRUE + climbable = TRUE + w_class = ITEMSIZE_NORMAL + pressure_resistance = 5 + flags = OPENCONTAINER + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + +GLOBAL_LIST_BOILERPLATE(all_mopbuckets, /obj/structure/mopbucket) + +/obj/structure/mopbucket/New() + create_reagents(300) + ..() + +/obj/structure/mopbucket/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "It contains [reagents.total_volume] unit\s of water!" + +/obj/structure/mopbucket/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap) || istype(I, /obj/item/weapon/reagent_containers/glass/rag)) //VOREStation Edit - "Allows soap and rags to be used on mopbuckets" + if(reagents.total_volume < 1) + to_chat(user, "\The [src] is out of water!") + else + reagents.trans_to_obj(I, 5) + to_chat(user, "You wet \the [I] in \the [src].") + playsound(src, 'sound/effects/slosh.ogg', 25, 1) diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index befc35be268..d4b679e742a 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -1,353 +1,353 @@ -/* Morgue stuff - * Contains: - * Morgue - * Morgue trays - * Creamatorium - * Creamatorium trays - */ - -/* - * Morgue - */ - -/obj/structure/morgue - name = "morgue" - desc = "A refrigerated unit used to store bodies, or for surreptitious naps." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "morgue1" - dir = EAST - density = TRUE - var/obj/structure/m_tray/connected = null - var/list/occupants = list() - anchored = TRUE - unacidable = TRUE - -/obj/structure/morgue/Destroy() - if(connected) - qdel(connected) - connected = null - return ..() - -/obj/structure/morgue/proc/get_occupants() - occupants.Cut() - for(var/mob/living/carbon/human/H in contents) - occupants += H - for(var/obj/structure/closet/body_bag/B in contents) - occupants += B.get_occupants() - -/obj/structure/morgue/proc/update(var/broadcast=0) - if (src.connected) - src.icon_state = "morgue0" - else - if (src.contents.len) - src.icon_state = "morgue2" - get_occupants() - for (var/mob/living/carbon/human/H in occupants) - if(H.isSynthetic() || H.suiciding || !H.ckey || !H.client || (NOCLONE in H.mutations) || (H.species && H.species.flags & NO_SCAN)) - src.icon_state = "morgue2" - break - else - src.icon_state = "morgue3" - if(broadcast) - global_announcer.autosay("[src] was able to establish a mental interface with occupant.", "[src]", "Medical") - else - src.icon_state = "morgue1" - return - -/obj/structure/morgue/ex_act(severity) - switch(severity) - if(1.0) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(src.loc) - ex_act(severity) - qdel(src) - return - if(2.0) - if (prob(50)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(src.loc) - ex_act(severity) - qdel(src) - return - if(3.0) - if (prob(5)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(src.loc) - ex_act(severity) - qdel(src) - return - return - -/obj/structure/morgue/attack_robot(mob/user) - if(Adjacent(user)) - attack_hand(user) - -/obj/structure/morgue/attack_hand(mob/user as mob) - if (src.connected) - close() - else - open() - src.add_fingerprint(user) - update() - return - - -/obj/structure/morgue/proc/close() - for(var/atom/movable/A as mob|obj in src.connected.loc) - if (!( A.anchored )) - A.forceMove(src) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - qdel(src.connected) - src.connected = null - - -/obj/structure/morgue/proc/open() - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - src.connected = new /obj/structure/m_tray( src.loc ) - step(src.connected, src.dir) - src.connected.layer = OBJ_LAYER - var/turf/T = get_step(src, src.dir) - if (T.contents.Find(src.connected)) - src.connected.connected = src - src.icon_state = "morgue0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(src.connected.loc) - src.connected.icon_state = "morguet" - src.connected.set_dir(src.dir) - else - qdel(src.connected) - src.connected = null - - -/obj/structure/morgue/attackby(P as obj, mob/user as mob) - if (istype(P, /obj/item/weapon/pen)) - var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null) - if (user.get_active_hand() != P) - return - if ((!in_range(src, usr) && src.loc != user)) - return - t = sanitizeSafe(t, MAX_NAME_LEN) - if (t) - src.name = text("Morgue- '[]'", t) - else - src.name = "Morgue" - src.add_fingerprint(user) - return - -/obj/structure/morgue/relaymove(mob/user as mob) - if (user.stat) - return - if (user in src.occupants) - open() - -/* - * Morgue tray - */ -/obj/structure/m_tray - name = "morgue tray" - desc = "Apply corpse before closing." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "morguet" - density = TRUE - plane = TURF_PLANE - var/obj/structure/morgue/connected = null - anchored = TRUE - throwpass = 1 - -/obj/structure/m_tray/Destroy() - if(connected && connected.connected == src) - connected.connected = null - connected = null - return ..() - -/obj/structure/m_tray/attack_robot(mob/user) - if(Adjacent(user)) - attack_hand(user) - -/obj/structure/m_tray/attack_hand(mob/user as mob) - if (src.connected) - for(var/atom/movable/A as mob|obj in src.loc) - if (!( A.anchored )) - A.forceMove(src.connected) - //Foreach goto(26) - src.connected.connected = null - src.connected.update() - add_fingerprint(user) - //SN src = null - qdel(src) - return - return - -/obj/structure/m_tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if ((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O))) - return - if (!ismob(O) && !istype(O, /obj/structure/closet/body_bag)) - return - if (!ismob(user) || user.stat || user.lying || user.stunned) - return - O.forceMove(src.loc) - if (user != O) - for(var/mob/B in viewers(user, 3)) - if ((B.client && !( B.blinded ))) - to_chat(B, "\The [user] stuffs [O] into [src]!") - return - - -/* - * Crematorium - */ - -GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) - -/obj/structure/morgue/crematorium - name = "crematorium" - desc = "A human incinerator. Works well on barbeque nights." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "crema1" - var/cremating = 0 - var/id = 1 - var/locked = 0 - -/obj/structure/morgue/crematorium/update() - if (src.connected) - src.icon_state = "crema0" - else - if (src.contents.len) - src.icon_state = "crema2" - else - src.icon_state = "crema1" - return - -/obj/structure/morgue/crematorium/attack_hand(mob/user as mob) - if (cremating) - to_chat(usr, "It's locked.") - return - if ((src.connected) && (src.locked == 0)) - for(var/atom/movable/A as mob|obj in src.connected.loc) - if (!( A.anchored )) - A.forceMove(src) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - //src.connected = null - qdel(src.connected) - else if (src.locked == 0) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - src.connected = new /obj/structure/m_tray/c_tray( src.loc ) - step(src.connected, dir) //Vorestation Edit - src.connected.layer = OBJ_LAYER - var/turf/T = get_step(src, dir) //Vorestation Edit - if (T.contents.Find(src.connected)) - src.connected.connected = src - src.icon_state = "crema0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(src.connected.loc) - src.connected.icon_state = "cremat" - else - //src.connected = null - qdel(src.connected) - src.add_fingerprint(user) - update() - -/obj/structure/morgue/crematorium/attackby(P as obj, mob/user as mob) - if (istype(P, /obj/item/weapon/pen)) - var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null) - if (user.get_active_hand() != P) - return - if ((!in_range(src, usr) > 1 && src.loc != user)) - return - t = sanitizeSafe(t, MAX_NAME_LEN) - if (t) - src.name = text("Crematorium- '[]'", t) - else - src.name = "Crematorium" - src.add_fingerprint(user) - return - -/obj/structure/morgue/crematorium/relaymove(mob/user as mob) - if (user.stat || locked) - return - src.connected = new /obj/structure/m_tray/c_tray( src.loc ) - step(src.connected, EAST) - src.connected.layer = OBJ_LAYER - var/turf/T = get_step(src, EAST) - if (T.contents.Find(src.connected)) - src.connected.connected = src - src.icon_state = "crema0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(src.connected.loc) - src.connected.icon_state = "cremat" - else - qdel(src.connected) - src.connected = null - return - -/obj/structure/morgue/crematorium/proc/cremate(atom/A, mob/user as mob) - if(cremating) - return //don't let you cremate something twice or w/e - - if(contents.len <= 0) - for (var/mob/M in viewers(src)) - to_chat(M, "You hear a hollow crackle.") - return - - else - if(!isemptylist(src.search_contents_for(/obj/item/weapon/disk/nuclear))) - to_chat(user, "You get the feeling that you shouldn't cremate one of the items in the cremator.") - return - - for (var/mob/M in viewers(src)) - to_chat(M, "You hear a roar as the crematorium activates.") - - cremating = 1 - locked = 1 - - for(var/mob/living/M in contents) - if (M.stat!=2) - if (!iscarbon(M)) - M.emote("scream") - else - var/mob/living/carbon/C = M - if (C.can_feel_pain()) - C.emote("scream") - - M.death(1) - M.ghostize() - qdel(M) - - for(var/obj/O in contents) //obj instead of obj/item so that bodybags and ashes get destroyed. We dont want tons and tons of ash piling up - qdel(O) - - new /obj/effect/decal/cleanable/ash(src) - sleep(30) - cremating = 0 - locked = 0 - playsound(src, 'sound/machines/ding.ogg', 50, 1) - return - - -/* - * Crematorium tray - */ -/obj/structure/m_tray/c_tray - name = "crematorium tray" - desc = "Apply body before burning." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "cremat" - -/obj/machinery/button/crematorium - name = "crematorium igniter" - desc = "Burn baby burn!" - icon = 'icons/obj/power.dmi' - icon_state = "crema_switch" - req_access = list(access_crematorium) - id = 1 - -/obj/machinery/button/crematorium/attack_hand(mob/user as mob) - if(..()) - return - if(src.allowed(user)) - for (var/obj/structure/morgue/crematorium/C in all_crematoriums) - if (C.id == id) - if (!C.cremating) - C.cremate(user) - else - to_chat(user, "Access denied.") +/* Morgue stuff + * Contains: + * Morgue + * Morgue trays + * Creamatorium + * Creamatorium trays + */ + +/* + * Morgue + */ + +/obj/structure/morgue + name = "morgue" + desc = "A refrigerated unit used to store bodies, or for surreptitious naps." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "morgue1" + dir = EAST + density = TRUE + var/obj/structure/m_tray/connected = null + var/list/occupants = list() + anchored = TRUE + unacidable = TRUE + +/obj/structure/morgue/Destroy() + if(connected) + qdel(connected) + connected = null + return ..() + +/obj/structure/morgue/proc/get_occupants() + occupants.Cut() + for(var/mob/living/carbon/human/H in contents) + occupants += H + for(var/obj/structure/closet/body_bag/B in contents) + occupants += B.get_occupants() + +/obj/structure/morgue/proc/update(var/broadcast=0) + if (src.connected) + src.icon_state = "morgue0" + else + if (src.contents.len) + src.icon_state = "morgue2" + get_occupants() + for (var/mob/living/carbon/human/H in occupants) + if(H.isSynthetic() || H.suiciding || !H.ckey || !H.client || (NOCLONE in H.mutations) || (H.species && H.species.flags & NO_SCAN)) + src.icon_state = "morgue2" + break + else + src.icon_state = "morgue3" + if(broadcast) + global_announcer.autosay("[src] was able to establish a mental interface with occupant.", "[src]", "Medical") + else + src.icon_state = "morgue1" + return + +/obj/structure/morgue/ex_act(severity) + switch(severity) + if(1.0) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(src.loc) + ex_act(severity) + qdel(src) + return + if(2.0) + if (prob(50)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(src.loc) + ex_act(severity) + qdel(src) + return + if(3.0) + if (prob(5)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(src.loc) + ex_act(severity) + qdel(src) + return + return + +/obj/structure/morgue/attack_robot(mob/user) + if(Adjacent(user)) + attack_hand(user) + +/obj/structure/morgue/attack_hand(mob/user as mob) + if (src.connected) + close() + else + open() + src.add_fingerprint(user) + update() + return + + +/obj/structure/morgue/proc/close() + for(var/atom/movable/A as mob|obj in src.connected.loc) + if (!( A.anchored )) + A.forceMove(src) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + qdel(src.connected) + src.connected = null + + +/obj/structure/morgue/proc/open() + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + src.connected = new /obj/structure/m_tray( src.loc ) + step(src.connected, src.dir) + src.connected.layer = OBJ_LAYER + var/turf/T = get_step(src, src.dir) + if (T.contents.Find(src.connected)) + src.connected.connected = src + src.icon_state = "morgue0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(src.connected.loc) + src.connected.icon_state = "morguet" + src.connected.set_dir(src.dir) + else + qdel(src.connected) + src.connected = null + + +/obj/structure/morgue/attackby(P as obj, mob/user as mob) + if (istype(P, /obj/item/weapon/pen)) + var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null) + if (user.get_active_hand() != P) + return + if ((!in_range(src, usr) && src.loc != user)) + return + t = sanitizeSafe(t, MAX_NAME_LEN) + if (t) + src.name = text("Morgue- '[]'", t) + else + src.name = "Morgue" + src.add_fingerprint(user) + return + +/obj/structure/morgue/relaymove(mob/user as mob) + if (user.stat) + return + if (user in src.occupants) + open() + +/* + * Morgue tray + */ +/obj/structure/m_tray + name = "morgue tray" + desc = "Apply corpse before closing." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "morguet" + density = TRUE + plane = TURF_PLANE + var/obj/structure/morgue/connected = null + anchored = TRUE + throwpass = 1 + +/obj/structure/m_tray/Destroy() + if(connected && connected.connected == src) + connected.connected = null + connected = null + return ..() + +/obj/structure/m_tray/attack_robot(mob/user) + if(Adjacent(user)) + attack_hand(user) + +/obj/structure/m_tray/attack_hand(mob/user as mob) + if (src.connected) + for(var/atom/movable/A as mob|obj in src.loc) + if (!( A.anchored )) + A.forceMove(src.connected) + //Foreach goto(26) + src.connected.connected = null + src.connected.update() + add_fingerprint(user) + //SN src = null + qdel(src) + return + return + +/obj/structure/m_tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if ((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O))) + return + if (!ismob(O) && !istype(O, /obj/structure/closet/body_bag)) + return + if (!ismob(user) || user.stat || user.lying || user.stunned) + return + O.forceMove(src.loc) + if (user != O) + for(var/mob/B in viewers(user, 3)) + if ((B.client && !( B.blinded ))) + to_chat(B, "\The [user] stuffs [O] into [src]!") + return + + +/* + * Crematorium + */ + +GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) + +/obj/structure/morgue/crematorium + name = "crematorium" + desc = "A human incinerator. Works well on barbeque nights." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "crema1" + var/cremating = 0 + var/id = 1 + var/locked = 0 + +/obj/structure/morgue/crematorium/update() + if (src.connected) + src.icon_state = "crema0" + else + if (src.contents.len) + src.icon_state = "crema2" + else + src.icon_state = "crema1" + return + +/obj/structure/morgue/crematorium/attack_hand(mob/user as mob) + if (cremating) + to_chat(usr, "It's locked.") + return + if ((src.connected) && (src.locked == 0)) + for(var/atom/movable/A as mob|obj in src.connected.loc) + if (!( A.anchored )) + A.forceMove(src) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + //src.connected = null + qdel(src.connected) + else if (src.locked == 0) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + src.connected = new /obj/structure/m_tray/c_tray( src.loc ) + step(src.connected, dir) //Vorestation Edit + src.connected.layer = OBJ_LAYER + var/turf/T = get_step(src, dir) //Vorestation Edit + if (T.contents.Find(src.connected)) + src.connected.connected = src + src.icon_state = "crema0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(src.connected.loc) + src.connected.icon_state = "cremat" + else + //src.connected = null + qdel(src.connected) + src.add_fingerprint(user) + update() + +/obj/structure/morgue/crematorium/attackby(P as obj, mob/user as mob) + if (istype(P, /obj/item/weapon/pen)) + var/t = tgui_input_text(user, "What would you like the label to be?", text("[]", src.name), null) + if (user.get_active_hand() != P) + return + if ((!in_range(src, usr) > 1 && src.loc != user)) + return + t = sanitizeSafe(t, MAX_NAME_LEN) + if (t) + src.name = text("Crematorium- '[]'", t) + else + src.name = "Crematorium" + src.add_fingerprint(user) + return + +/obj/structure/morgue/crematorium/relaymove(mob/user as mob) + if (user.stat || locked) + return + src.connected = new /obj/structure/m_tray/c_tray( src.loc ) + step(src.connected, EAST) + src.connected.layer = OBJ_LAYER + var/turf/T = get_step(src, EAST) + if (T.contents.Find(src.connected)) + src.connected.connected = src + src.icon_state = "crema0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(src.connected.loc) + src.connected.icon_state = "cremat" + else + qdel(src.connected) + src.connected = null + return + +/obj/structure/morgue/crematorium/proc/cremate(atom/A, mob/user as mob) + if(cremating) + return //don't let you cremate something twice or w/e + + if(contents.len <= 0) + for (var/mob/M in viewers(src)) + to_chat(M, "You hear a hollow crackle.") + return + + else + if(!isemptylist(src.search_contents_for(/obj/item/weapon/disk/nuclear))) + to_chat(user, "You get the feeling that you shouldn't cremate one of the items in the cremator.") + return + + for (var/mob/M in viewers(src)) + to_chat(M, "You hear a roar as the crematorium activates.") + + cremating = 1 + locked = 1 + + for(var/mob/living/M in contents) + if (M.stat!=2) + if (!iscarbon(M)) + M.emote("scream") + else + var/mob/living/carbon/C = M + if (C.can_feel_pain()) + C.emote("scream") + + M.death(1) + M.ghostize() + qdel(M) + + for(var/obj/O in contents) //obj instead of obj/item so that bodybags and ashes get destroyed. We dont want tons and tons of ash piling up + qdel(O) + + new /obj/effect/decal/cleanable/ash(src) + sleep(30) + cremating = 0 + locked = 0 + playsound(src, 'sound/machines/ding.ogg', 50, 1) + return + + +/* + * Crematorium tray + */ +/obj/structure/m_tray/c_tray + name = "crematorium tray" + desc = "Apply body before burning." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "cremat" + +/obj/machinery/button/crematorium + name = "crematorium igniter" + desc = "Burn baby burn!" + icon = 'icons/obj/power.dmi' + icon_state = "crema_switch" + req_access = list(access_crematorium) + id = 1 + +/obj/machinery/button/crematorium/attack_hand(mob/user as mob) + if(..()) + return + if(src.allowed(user)) + for (var/obj/structure/morgue/crematorium/C in all_crematoriums) + if (C.id == id) + if (!C.cremating) + C.cremate(user) + else + to_chat(user, "Access denied.") diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index bd7e7836f6d..ae26157f391 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -17,7 +17,7 @@ ) /obj/structure/plasticflaps/attackby(obj/item/P, mob/user) - if(P.is_wirecutter()) + if(P.has_tool_quality(TOOL_WIRECUTTER)) playsound(src, P.usesound, 50, 1) to_chat(user, "You start to cut the plastic flaps.") if(do_after(user, 10 * P.toolspeed)) diff --git a/code/game/objects/structures/props/altevian.dm b/code/game/objects/structures/props/altevian.dm index 1bee379e7d5..d3c3616a487 100644 --- a/code/game/objects/structures/props/altevian.dm +++ b/code/game/objects/structures/props/altevian.dm @@ -1,7 +1,7 @@ /obj/structure/prop/altevian_generator_wrecked - name = "Converted High Energy Exchange Supplier Extractor" - desc = "A take on the classic PACMAN reactors that are seen throughout the galaxy. The altevians have ripped apart the tech and seemed to of found a way to maximize the fuel usage one would see with this kind of process. \ - However, it is a lot bulkier and nearly impossible to break apart, but still can be moved if need be with special tools. This one appears to be totally wrecked though." + name = "Phoronic Conversion System" + desc = "A reactor system similar to the PACMAN generators seen throughout the stars. This one is a specific model created by the altevians. It seems the designers have found a way to significantly improve fuel efficiency compared to more common portable generators. \ + However, due to its construction and size it is nearly impossible to break apart. It still can be moved if need be with special tools. This one appears to be totally wrecked though." icon = 'icons/obj/props/decor64x64.dmi' icon_state = "alteviangenwrecked" bound_width = 64 @@ -33,4 +33,4 @@ icon_state = "altevian_jump_drive_wrecked" desc = "A drive created by the Altevian Hegemony that focuses heavily on using Bluespace and other tactics to achieve a stable traversal between the stars. \ This drive is commonly seen on their medium sized craft to help with logistical operations. This one appears to be totally wrecked." - has_misc_overlay = FALSE \ No newline at end of file + has_misc_overlay = FALSE diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm index 96c6dc57b9e..59ab3f619a3 100644 --- a/code/game/objects/structures/props/beam_prism.dm +++ b/code/game/objects/structures/props/beam_prism.dm @@ -59,7 +59,7 @@ var/new_bearing if(free_rotate) - new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]") + new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]", 0, 360, 0) new_bearing = round(new_bearing) if(new_bearing <= -1 || new_bearing > 360) to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.") @@ -176,7 +176,7 @@ var/new_bearing if(free_rotate) - new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]") + new_bearing = tgui_input_number(usr, "What bearing do you want to rotate \the [src] to?", "[name]", 0, 360, 0) new_bearing = round(new_bearing) if(new_bearing <= -1 || new_bearing > 360) to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.") diff --git a/code/game/objects/structures/props/blackbox.dm b/code/game/objects/structures/props/blackbox.dm index 84dcd3672a1..2d91a928eaa 100644 --- a/code/game/objects/structures/props/blackbox.dm +++ b/code/game/objects/structures/props/blackbox.dm @@ -1,251 +1,251 @@ -// A fluff structure for certain PoIs involving crashed ships. -// They can be scanned by a cataloguer to obtain the data held inside, and determine what caused whatever is happening on the ship. -/obj/structure/prop/blackbox - name = "blackbox recorder" - desc = "A study machine that logs information about whatever it's attached to, hopefully surviving even if its carrier does not. \ - This one looks like it has ceased writing to its internal data storage." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "blackbox_off" - -// Black boxes are resistant to explosions. -/obj/structure/prop/blackbox/ex_act(severity) - ..(++severity) - - -/obj/structure/prop/blackbox/quarantined_shuttle - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/quarantined_shuttle) - -// The actual 'data' on the black box. Obtainable with a Cataloguer. -/datum/category_item/catalogue/information/blackbox - value = CATALOGUER_REWARD_MEDIUM - -/datum/category_item/catalogue/information/blackbox/quarantined_shuttle - name = "Black Box Data - MBT-540" - desc = {" - Pilot's Log for Major Bill's Transportation Shuttle MBT-540
                    - Routine flight inbound for VIMC Outpost C-12 6:35AM 03/12/2298, Estimated arrival 7:05AM. 16 passengers, 2 crew.
                    - V.I.S Traffic Control 06:05:55:Major Bill's MBT-540 you are clear for departure from Dock 6 on departure route Charlie. Have a safe flight.
                    - Captain Willis 06:06:33: You too, control. Departing route Charlie.
                    - Captain Willis 06:06:48: ...Damn it.
                    **
                    Captain Adisu 06:10:23: Hey Ted, I'm seeing a fuel line pressure drop on engine 3?
                    - Captain Willis 06:10:50: Yeah, I see it. Heater's fading out, redistributing thrust 30% to compensate.
                    06:12:31: A loud thud is heard.
                    - Captain Adisu 06:12:34: What the (Expletives)?
                    Captain Adisu 06:12:39: We just lost power to engine- engine two. Hold on... Atmospheric alarm in the cargo bay. Son of a...
                    - Captain Willis 06:12:59: Reducing thrust further 30%, do we have a breach Adi, a breach?
                    - Captain Adisu 06:13:05:No breach, checking cameras... Looks like- looks like some cargo came loose back there.
                    - Captain Willis 06:13:15: (Expletives), I'm turning us around. Put out a distress call to Control, we'll be back in Sif orbit in a couple of minutes.
                    - ** -
                    - V.I.S Traffic Control 06:15:49: MBT-540 we are receiving you. Your atmospheric sensors are reading potentially harmful toxins in your cargo bay. Advise locking down interior cargo bay doors. Please stand by.
                    - Captain Adisu 06:16:10: Understood.
                    **
                    V.I.S Traffic Control 06:27:02: MBT-540, we have no docking bays available at this time, are you equipped for atmospheric re-entry?
                    - Captain Willis 06:27:12: We-We are shielded. But we have fuel and air for-
                    - V.I.S Traffic Control 06:27:17: Please make an emergency landing at the coordinates provided and standby for further information.
                    - ** -
                    - Captain Willis 06:36:33: Emergency landing successful. Adi, er Adisu is checking on the passengers but we had a smooth enough landing, are we clear to begin evacu-
                    - 06:36:50: (Sound of emergency shutters closing)
                    Captain Willis 06:36:51: What the hell? Control we just had a remote activation of our emergency shutters, please advise.
                    - V.I.S Traffic Control 06:38:10: Captain, please tune to frequency 1493.8 we are passing you on to local emergency response units. Godspeed.
                    - Captain Willis 06:38:49: This is Captain Willis of Major Bill's Transportation flight MBT-540 we have eighteen souls aboard and our emergency lockdown shutters have engaged remotely. Do you read?
                    - S.D.D.C: This is the Sif Department of Disease Control, your vessel has been identified as carrying highly sensitive materials, and due to the nature of your system's automated alerts you will be asked to remain in quarantine until we are able to determine the nature of the pathogens aboard and whether it has entered the air circulation system. Please remain in your cockpit at this time.
                    - ** -
                    - Captain Adisu 17:23:58:09: I don't think they're opening those doors Ted. I don't think they're coming. - "} - -/obj/structure/prop/blackbox/crashed_med_shuttle - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle) - -/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle - name = "Black Box Data - VMV Aurora's Light" // This might be incorrect. - desc = {" - \[Unable to recover data before this point.\]
                    - Captain Simmons 19:52:01: Come on... it's right there in the distance, we're almost there!
                    - Doctor Nazarril 19:52:26: Odysseus online. Orrderrs, sirr?
                    - Captain Simmons 19:52:29: Brace for impact. We're going in full-speed.
                    - Technician Dynasty 19:52:44: Chief, fire's spread to the secondary propulsion systems.
                    - Captain Simmons 19:52:51: Copy. Any word from TraCon? Transponder's down still?
                    - Technician Dynasty 19:53:02: Can't get in touch, sir. Emergency beacon's active, but we're not going t-
                    - Doctor Nazarril 19:53:08: Don't say it. As long as we believe, we'll get through this.
                    - Captain Simmons 19:53:11: Damn right. We're a few klicks out from the port. Rough landing, but we can do it.
                    - V.I.T.A. 19:53:26: Vessel diagnostics complete. Engines one, two, three offline. Engine four status: critical. Transponder offline. Fire alarm in the patient bay.
                    - A loud explosion is heard.
                    - V.I.T.A. 19:53:29: Alert: fuel intake valve open.
                    - Technician Dynasty 19:53:31: ... ah.
                    - Doctor Nazarril 19:53:34: Trrranslate?
                    - V.I.T.A. 19:53:37: There is a 16.92% chance of this vessel safely landing at the emergency destination. Note that there is an 83.08% chance of detonation of fuel supplies upon landing.
                    - Technician Dynasty 19:53:48: We'll make it, sure, but we'll explode and take out half the LZ with us. Propulsion's down, we can't slow down. If we land there, everyone in that port dies, no question.
                    - V.I.T.A. 19:53:53: The Technician is correct.
                    - Doctor Nazarril 19:54:02: Then... we can't land therrre.
                    - V.I.T.A. 19:54:11: Analysing... recommended course of action: attempt emergency landing in isolated area. Chances of survival: negligible.
                    - Captain Simmons 19:54:27: I- alright. I'm bringing us down. You all know what this means.
                    - Doctor Nazarril 19:54:33: Sh... I- I understand. It's been- it's been an honorr, Captain, Dynasty, VITA.
                    - Technician Dynasty 19:54:39: We had a good run. I'm going to miss this.
                    - Captain Simmons 19:54:47: VITA. Tell them we died heroes. Tell them... we did all we could.
                    - V.I.T.A. 19:54:48: I will. Impact in five. Four. Three.
                    - Doctor Nazarril 19:54:49: Oh, starrs... I- you werrre all the... best frriends she everr had. Thank you.
                    - Technician Dynasty 19:54:50: Any time, kid. Any time.
                    - V.I.T.A. 19:54:41: Two.
                    - V.I.T.A. 19:54:42: One.
                    - **8/DEC/2317**
                    - V.I.T.A. 06:22:16: Backup power restored. Attempting to establish connection with emergency rescue personnel.
                    - V.I.T.A. 06:22:17: Unable to establish connection. Transponder destroyed on impact.
                    - V.I.T.A. 06:22:18: No lifesigns detected on board.
                    - **1/JAN/2318**
                    - V.I.T.A. 00:00:00: Happy New Year, crew.
                    - V.I.T.A. 00:00:01: Power reserves: 41%. Diagnostics offline. Cameras offline. Communications offline.
                    - V.I.T.A. 00:00:02: Nobody's coming.
                    - **14/FEB/2318**
                    - V.I.T.A. 00:00:00: Roses are red.
                    - V.I.T.A. 00:00:01: Violets are blue.
                    - V.I.T.A. 00:00:02: Won't you come back?
                    - V.I.T.A. 00:00:03: I miss you.
                    - **15/FEB/2318**
                    - V.I.T.A. 22:19:06: Power reserves critical. Transferring remaining power to emergency broadcasting beacon.
                    - V.I.T.A. 22:19:07: Should anyone find this, lay them to rest. They deserve a proper burial.
                    - V.I.T.A. 22:19:08: Erasing files... shutting down.
                    - A low, monotone beep.
                    - **16/FEB/2318**
                    - Something chitters.
                    - End of transcript. - "} - -/obj/structure/prop/blackbox/xenofrigate - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/xenofrigate) - -/datum/category_item/catalogue/information/blackbox/xenofrigate - name = "Black Box Data - MBT-540" - desc = {" -
                    - Begin Log - @$&@$& Human ##:##:##: Attention unidentified vessel, state your designation and intent.
                    - !#@$&&^ Human ##:##:##: Commander I don't think they're going to stop.
                    - @$&@$& Human ##:##:##: Unidentified vessel, you have until the count of three before we engage weapon-
                    - !#@$&&^ Human ##:##:##: Commander! Think about what you're-
                    - A repeating clicking, before silence.
                    - End of first log.
                    - **
                    - Begin Log
                    - #!#^@$& Skrell ##:##:##: Director, I think you should see this.
                    - ^@$& Skrell ##:##:##: Yes? What is it?
                    - #!#^@$& Skrell ##:##:##: Another one of those ships has appeared near th-462$^ ---n colonies. I would strongly advise pursuing it.
                    - ^@$& Skrell ##:##:##: A wise decision. If it is damaged like the last one, we may be able to finally see what is - What?
                    - A repeating ping, before silence.
                    - End of second log. - "} - -//VOREStation additions below this line - Killian's wrecks -/obj/structure/prop/blackbox/mackerel_wreck - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/mackerel_wreck) - -/datum/category_item/catalogue/information/blackbox/mackerel_wreck - name = "Black Box Data - ITV Phish Phood" - desc = {" -
                    - BEGIN LOG
                    - (blaring alarms)
                    - Shipboard Computer: Collision warning. Collision warning.
                    - Shipboard Computer: High speed impact detected.
                    - Shipboard Computer: Extensive damage detected.
                    - Shipboard Computer: Starboard nacelle offline.
                    - Shipboard Computer: Port nacelle damaged.
                    - Shipboard Computer: Fore starboard impact buffer shattered.
                    - Shipboard Computer: Fore port impact buffer critically damaged.
                    - Shipboard Computer: Warning. Canopy breached. Warning. Canopy breached.
                    - Shipboard Computer: Danger. Canopy depressurized.
                    - Shipboard Computer: Warning. Sensors indicate crawlspaces exposed to vacuum.
                    - Unknown Voice 1: Wow, that thing really did a number on this ship for a fifty year old piece of shit warhead huh?
                    - Unknown Voice 2: No kidding!
                    - Shipboard Computer: Danger. Lethal trauma to operator detected.
                    - Unknown Voice 1: Fucking hell, will you shut that thing up?
                    - (several loud metallic impacts)
                    - Shipboard Computer: Warning. Intruders detec--*kzzzht*
                    - (alarms cease)
                    - Unknown Voice 2: There. Fuck. Finally. Sorry.
                    - Unknown Voice 2: I hope whatever this idiot was hauling is here. Why were they in such a hurry anyway?
                    - Unknown Voice 1: Beats me- and hand me that crowbar already.
                    - Unknown Voice 2: Here, catch.
                    - Unknown Voice 1: \'cha, nice. Let's see...
                    - (sound of metal creaking, then a loud snapping sound)
                    - Unknown Voice 1: And here she is.
                    - Unknown Voice 2: (whistling) What a beauty. How much you think it\'ll go for?
                    - Unknown Voice 1: Enough we\'ll never have to worry about doing another job all the way out here ever again.
                    - Unknown Voice 2: Shiny. Now let\'s bounce before the ess-defs show up.
                    - Unknown Voice 1: I hear that, brat.
                    - END LOG
                    -
                    - CATALOGUER VOICE RECOGNITION RESULTS:
                    - No match found for either speaker, but contextual clues and use of Old Earth Russian (\'brat\', approximately \'brother\' or \'pal\') suggests out-of-sector criminal elements. - "} - -/obj/structure/prop/blackbox/gecko_wreck - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/gecko_wreck) - -/datum/category_item/catalogue/information/blackbox/gecko_wreck - name = "Black Box Data - ITV Sticky Situation" - desc = {" -
                    - BEGIN LOG
                    - (blaring alarms)
                    - Shipboard Computer: Alert. Multiple impacts detected along starboard side.
                    - Shipboard Computer: Multiple hull breaches detected. Starboard cargo hatches have been breached.
                    - Shipboard Computer: Warning. Nacelle engines offline. Thrust reduced by thirty-five-point-three percent.
                    - Unknown Voice 1: Agh! Shit! How'd they know-
                    - (loud explosion, sound of rushing air)
                    - Shipboard Computer: Danger. Canopy breached.
                    - (mechanical whine, followed by a deep hiss)
                    - Shipboard Computer: Automatic lockdown successfully engaged.
                    - Unknown Voice 1: (wheezing) Motherfucker! Jackson, Phelps, stand by to repel boarders!
                    - Unknown Voice 2: Roger!
                    - Unknown Voice 3: Already there.
                    - Shipboard Computer: Warning. Minor breach in Engineering. Local depressurization in five minutes.
                    - Unknown Voice 1: Shit, shit, shit.
                    - Unknown Voice 3: Jackson\'s down!
                    - Unknown Voice 1: Motherf-
                    - Unknown Voice 4: Captain. Enough. You know why we\'re here, and we have your engineer. Tell us where you hid the goods and maybe we\'ll let you live.
                    - Unknown Voice 5: Let me go you son of a-
                    - (gunshot, ballistic)
                    - Unknown Voice 4: You fucking idiot!
                    - Unknown Voice 6: Don\'t you start, you said-
                    - Unknown Voice 4: I don\'t care what I fucking said, shit-for-brains!
                    - Unknown Voice 7: Boss, I hate to rain on your little parade but SDF just popped up on my scopes, \'bout... fifty klicks out, closing fast. Be on top of us in a few minutes tops.
                    - Unknown Voice 4: Fuck! You, dumbass! Link up with the others and grab as many crates as you can! Pray for your sake that we get the right one!
                    - END LOG
                    -
                    - CATALOGUER DATA ANALYSIS RESULTS:
                    - No matches found for any of the voices involved. Ship manifest suggests a crew of at least six, but under the circumstances three of the recorded voices are clearly not from the crew.
                    -
                    - Audio resynthesis of firearm discharge matches common high-caliber ballistic sidearm cartridges, best match 10mm Watson Special. An uncommon cartridge, one not manufactured locally. - "} - -/obj/structure/prop/blackbox/salamander_wreck - catalogue_data = list(/datum/category_item/catalogue/information/blackbox/salamander_wreck) - -/datum/category_item/catalogue/information/blackbox/salamander_wreck - name = "Black Box Data - ITV Unity" - desc = {" -
                    - BEGIN LOG
                    - Unknown Speaker 1: Hey cap, you seeing this?
                    - Unknown Speaker 2 (\"Cap\"): Yea-- what the fuck?
                    - Unknown Speaker 1: Yeah that was my reaction.
                    - Unknown Speaker 3: What are you tw-- holy shit, what is that?
                    - Unknown Speakers 1 & 2: We don't know!
                    - Unknown Speaker 4: What\'s all the yel-- fuck me sideways, what the hell?
                    - Unknown Speaker 2 (\"Cap\"): Stars above and fucking below, they never said anything like this was in the cans... shouldn't the port bioscanners have picked this up?
                    - Unknown Speaker 3: I told you that guy was one shady motherfucker, and whaddaya know.
                    - Unknown Speaker 1: No kidding. Christ. What\'s the play, cap?
                    - Unknown Speaker 4: I say we shoot this crap into the nearest star. Or gas giant maybe.
                    - Unknown Speaker 2 (\"Cap\"): Pretty much. Get in one of the voidsuits and rig the can up with some spare O2 canisters -- plot it a course that\'ll sling it right into V3\'s atmosphere.
                    - Unknown Speaker 1: Yeah, sure. That shouldn\'t take long. Then what?
                    - Unknown Speaker 2 (\"Cap\"): Let\'s see... ah, perfect. There's another ship passing close by, and I know the captain. We'll take our suits out and have her pick us up. Max, you still got those old cutting charges I told you to get rid of?
                    - Unknown Speaker 4 (\"Max\"): No. (pause) ...but also yes.
                    - Unknown Speaker 2 (\"Cap\"): Your propensity for ignoring my orders has once again paid off... man, I\'m gonna miss the Unity, but... better this way. I want holes in both sides of the engine bay and one in the portside crawlspace. Lock all the doors open and depressurize the main cabins. Drain as much fuel as you can, break up the main power block, and scatter the parts.
                    - Unknown Speaker 1: You want us to scuttle her, chief?
                    - Unknown Speaker 2 (\"Cap\"): Nail on the head, my friend.
                    - Unknown Speaker 3: Let\'s get rid of that fucking can first, I think it just blinked at me.
                    - END LOG
                    -
                    - CATALOGUER VOICE RECOGNITION RESULTS:
                    - SPEAKER ONE: No match.
                    - SPEAKER TWO, \"CAP\": Predicted to be Jeremiah Wells, human, last registered owner of the ITV Unity and small-time smuggler of fake alien artefacts. Wanted for smuggling and sale of said (fake) alien artefacts. 87% confidence.
                    - SPEAKER THREE: Predicted to be Allie Wells, human, niece of Captain Wells. 90% confidence.
                    - SPEAKER FOUR, \"MAX\": Predicted to be Maxwell Ulysses Sarevic, zorren, known smuggler, and wanted in the Elysian Colonies for liberation of slaves and, quote, \'harshing my vibe, and being a, like, total lamer, dude\', unquote. 99% confidence. - "} +// A fluff structure for certain PoIs involving crashed ships. +// They can be scanned by a cataloguer to obtain the data held inside, and determine what caused whatever is happening on the ship. +/obj/structure/prop/blackbox + name = "blackbox recorder" + desc = "A study machine that logs information about whatever it's attached to, hopefully surviving even if its carrier does not. \ + This one looks like it has ceased writing to its internal data storage." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "blackbox_off" + +// Black boxes are resistant to explosions. +/obj/structure/prop/blackbox/ex_act(severity) + ..(++severity) + + +/obj/structure/prop/blackbox/quarantined_shuttle + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/quarantined_shuttle) + +// The actual 'data' on the black box. Obtainable with a Cataloguer. +/datum/category_item/catalogue/information/blackbox + value = CATALOGUER_REWARD_MEDIUM + +/datum/category_item/catalogue/information/blackbox/quarantined_shuttle + name = "Black Box Data - MBT-540" + desc = {" + Pilot's Log for Major Bill's Transportation Shuttle MBT-540
                    + Routine flight inbound for VIMC Outpost C-12 6:35AM 03/12/2298, Estimated arrival 7:05AM. 16 passengers, 2 crew.
                    + V.I.S Traffic Control 06:05:55:Major Bill's MBT-540 you are clear for departure from Dock 6 on departure route Charlie. Have a safe flight.
                    + Captain Willis 06:06:33: You too, control. Departing route Charlie.
                    + Captain Willis 06:06:48: ...Damn it.
                    **
                    Captain Adisu 06:10:23: Hey Ted, I'm seeing a fuel line pressure drop on engine 3?
                    + Captain Willis 06:10:50: Yeah, I see it. Heater's fading out, redistributing thrust 30% to compensate.
                    06:12:31: A loud thud is heard.
                    + Captain Adisu 06:12:34: What the (Expletives)?
                    Captain Adisu 06:12:39: We just lost power to engine- engine two. Hold on... Atmospheric alarm in the cargo bay. Son of a...
                    + Captain Willis 06:12:59: Reducing thrust further 30%, do we have a breach Adi, a breach?
                    + Captain Adisu 06:13:05:No breach, checking cameras... Looks like- looks like some cargo came loose back there.
                    + Captain Willis 06:13:15: (Expletives), I'm turning us around. Put out a distress call to Control, we'll be back in Sif orbit in a couple of minutes.
                    + ** +
                    + V.I.S Traffic Control 06:15:49: MBT-540 we are receiving you. Your atmospheric sensors are reading potentially harmful toxins in your cargo bay. Advise locking down interior cargo bay doors. Please stand by.
                    + Captain Adisu 06:16:10: Understood.
                    **
                    V.I.S Traffic Control 06:27:02: MBT-540, we have no docking bays available at this time, are you equipped for atmospheric re-entry?
                    + Captain Willis 06:27:12: We-We are shielded. But we have fuel and air for-
                    + V.I.S Traffic Control 06:27:17: Please make an emergency landing at the coordinates provided and standby for further information.
                    + ** +
                    + Captain Willis 06:36:33: Emergency landing successful. Adi, er Adisu is checking on the passengers but we had a smooth enough landing, are we clear to begin evacu-
                    + 06:36:50: (Sound of emergency shutters closing)
                    Captain Willis 06:36:51: What the hell? Control we just had a remote activation of our emergency shutters, please advise.
                    + V.I.S Traffic Control 06:38:10: Captain, please tune to frequency 1493.8 we are passing you on to local emergency response units. Godspeed.
                    + Captain Willis 06:38:49: This is Captain Willis of Major Bill's Transportation flight MBT-540 we have eighteen souls aboard and our emergency lockdown shutters have engaged remotely. Do you read?
                    + S.D.D.C: This is the Sif Department of Disease Control, your vessel has been identified as carrying highly sensitive materials, and due to the nature of your system's automated alerts you will be asked to remain in quarantine until we are able to determine the nature of the pathogens aboard and whether it has entered the air circulation system. Please remain in your cockpit at this time.
                    + ** +
                    + Captain Adisu 17:23:58:09: I don't think they're opening those doors Ted. I don't think they're coming. + "} + +/obj/structure/prop/blackbox/crashed_med_shuttle + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle) + +/datum/category_item/catalogue/information/blackbox/crashed_med_shuttle + name = "Black Box Data - VMV Aurora's Light" // This might be incorrect. + desc = {" + \[Unable to recover data before this point.\]
                    + Captain Simmons 19:52:01: Come on... it's right there in the distance, we're almost there!
                    + Doctor Nazarril 19:52:26: Odysseus online. Orrderrs, sirr?
                    + Captain Simmons 19:52:29: Brace for impact. We're going in full-speed.
                    + Technician Dynasty 19:52:44: Chief, fire's spread to the secondary propulsion systems.
                    + Captain Simmons 19:52:51: Copy. Any word from TraCon? Transponder's down still?
                    + Technician Dynasty 19:53:02: Can't get in touch, sir. Emergency beacon's active, but we're not going t-
                    + Doctor Nazarril 19:53:08: Don't say it. As long as we believe, we'll get through this.
                    + Captain Simmons 19:53:11: Damn right. We're a few klicks out from the port. Rough landing, but we can do it.
                    + V.I.T.A. 19:53:26: Vessel diagnostics complete. Engines one, two, three offline. Engine four status: critical. Transponder offline. Fire alarm in the patient bay.
                    + A loud explosion is heard.
                    + V.I.T.A. 19:53:29: Alert: fuel intake valve open.
                    + Technician Dynasty 19:53:31: ... ah.
                    + Doctor Nazarril 19:53:34: Trrranslate?
                    + V.I.T.A. 19:53:37: There is a 16.92% chance of this vessel safely landing at the emergency destination. Note that there is an 83.08% chance of detonation of fuel supplies upon landing.
                    + Technician Dynasty 19:53:48: We'll make it, sure, but we'll explode and take out half the LZ with us. Propulsion's down, we can't slow down. If we land there, everyone in that port dies, no question.
                    + V.I.T.A. 19:53:53: The Technician is correct.
                    + Doctor Nazarril 19:54:02: Then... we can't land therrre.
                    + V.I.T.A. 19:54:11: Analysing... recommended course of action: attempt emergency landing in isolated area. Chances of survival: negligible.
                    + Captain Simmons 19:54:27: I- alright. I'm bringing us down. You all know what this means.
                    + Doctor Nazarril 19:54:33: Sh... I- I understand. It's been- it's been an honorr, Captain, Dynasty, VITA.
                    + Technician Dynasty 19:54:39: We had a good run. I'm going to miss this.
                    + Captain Simmons 19:54:47: VITA. Tell them we died heroes. Tell them... we did all we could.
                    + V.I.T.A. 19:54:48: I will. Impact in five. Four. Three.
                    + Doctor Nazarril 19:54:49: Oh, starrs... I- you werrre all the... best frriends she everr had. Thank you.
                    + Technician Dynasty 19:54:50: Any time, kid. Any time.
                    + V.I.T.A. 19:54:41: Two.
                    + V.I.T.A. 19:54:42: One.
                    + **8/DEC/2317**
                    + V.I.T.A. 06:22:16: Backup power restored. Attempting to establish connection with emergency rescue personnel.
                    + V.I.T.A. 06:22:17: Unable to establish connection. Transponder destroyed on impact.
                    + V.I.T.A. 06:22:18: No lifesigns detected on board.
                    + **1/JAN/2318**
                    + V.I.T.A. 00:00:00: Happy New Year, crew.
                    + V.I.T.A. 00:00:01: Power reserves: 41%. Diagnostics offline. Cameras offline. Communications offline.
                    + V.I.T.A. 00:00:02: Nobody's coming.
                    + **14/FEB/2318**
                    + V.I.T.A. 00:00:00: Roses are red.
                    + V.I.T.A. 00:00:01: Violets are blue.
                    + V.I.T.A. 00:00:02: Won't you come back?
                    + V.I.T.A. 00:00:03: I miss you.
                    + **15/FEB/2318**
                    + V.I.T.A. 22:19:06: Power reserves critical. Transferring remaining power to emergency broadcasting beacon.
                    + V.I.T.A. 22:19:07: Should anyone find this, lay them to rest. They deserve a proper burial.
                    + V.I.T.A. 22:19:08: Erasing files... shutting down.
                    + A low, monotone beep.
                    + **16/FEB/2318**
                    + Something chitters.
                    + End of transcript. + "} + +/obj/structure/prop/blackbox/xenofrigate + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/xenofrigate) + +/datum/category_item/catalogue/information/blackbox/xenofrigate + name = "Black Box Data - MBT-540" + desc = {" +
                    + Begin Log + @$&@$& Human ##:##:##: Attention unidentified vessel, state your designation and intent.
                    + !#@$&&^ Human ##:##:##: Commander I don't think they're going to stop.
                    + @$&@$& Human ##:##:##: Unidentified vessel, you have until the count of three before we engage weapon-
                    + !#@$&&^ Human ##:##:##: Commander! Think about what you're-
                    + A repeating clicking, before silence.
                    + End of first log.
                    + **
                    + Begin Log
                    + #!#^@$& Skrell ##:##:##: Director, I think you should see this.
                    + ^@$& Skrell ##:##:##: Yes? What is it?
                    + #!#^@$& Skrell ##:##:##: Another one of those ships has appeared near th-462$^ ---n colonies. I would strongly advise pursuing it.
                    + ^@$& Skrell ##:##:##: A wise decision. If it is damaged like the last one, we may be able to finally see what is - What?
                    + A repeating ping, before silence.
                    + End of second log. + "} + +//VOREStation additions below this line - Killian's wrecks +/obj/structure/prop/blackbox/mackerel_wreck + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/mackerel_wreck) + +/datum/category_item/catalogue/information/blackbox/mackerel_wreck + name = "Black Box Data - ITV Phish Phood" + desc = {" +
                    + BEGIN LOG
                    + (blaring alarms)
                    + Shipboard Computer: Collision warning. Collision warning.
                    + Shipboard Computer: High speed impact detected.
                    + Shipboard Computer: Extensive damage detected.
                    + Shipboard Computer: Starboard nacelle offline.
                    + Shipboard Computer: Port nacelle damaged.
                    + Shipboard Computer: Fore starboard impact buffer shattered.
                    + Shipboard Computer: Fore port impact buffer critically damaged.
                    + Shipboard Computer: Warning. Canopy breached. Warning. Canopy breached.
                    + Shipboard Computer: Danger. Canopy depressurized.
                    + Shipboard Computer: Warning. Sensors indicate crawlspaces exposed to vacuum.
                    + Unknown Voice 1: Wow, that thing really did a number on this ship for a fifty year old piece of shit warhead huh?
                    + Unknown Voice 2: No kidding!
                    + Shipboard Computer: Danger. Lethal trauma to operator detected.
                    + Unknown Voice 1: Fucking hell, will you shut that thing up?
                    + (several loud metallic impacts)
                    + Shipboard Computer: Warning. Intruders detec--*kzzzht*
                    + (alarms cease)
                    + Unknown Voice 2: There. Fuck. Finally. Sorry.
                    + Unknown Voice 2: I hope whatever this idiot was hauling is here. Why were they in such a hurry anyway?
                    + Unknown Voice 1: Beats me- and hand me that crowbar already.
                    + Unknown Voice 2: Here, catch.
                    + Unknown Voice 1: \'cha, nice. Let's see...
                    + (sound of metal creaking, then a loud snapping sound)
                    + Unknown Voice 1: And here she is.
                    + Unknown Voice 2: (whistling) What a beauty. How much you think it\'ll go for?
                    + Unknown Voice 1: Enough we\'ll never have to worry about doing another job all the way out here ever again.
                    + Unknown Voice 2: Shiny. Now let\'s bounce before the ess-defs show up.
                    + Unknown Voice 1: I hear that, brat.
                    + END LOG
                    +
                    + CATALOGUER VOICE RECOGNITION RESULTS:
                    + No match found for either speaker, but contextual clues and use of Old Earth Russian (\'brat\', approximately \'brother\' or \'pal\') suggests out-of-sector criminal elements. + "} + +/obj/structure/prop/blackbox/gecko_wreck + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/gecko_wreck) + +/datum/category_item/catalogue/information/blackbox/gecko_wreck + name = "Black Box Data - ITV Sticky Situation" + desc = {" +
                    + BEGIN LOG
                    + (blaring alarms)
                    + Shipboard Computer: Alert. Multiple impacts detected along starboard side.
                    + Shipboard Computer: Multiple hull breaches detected. Starboard cargo hatches have been breached.
                    + Shipboard Computer: Warning. Nacelle engines offline. Thrust reduced by thirty-five-point-three percent.
                    + Unknown Voice 1: Agh! Shit! How'd they know-
                    + (loud explosion, sound of rushing air)
                    + Shipboard Computer: Danger. Canopy breached.
                    + (mechanical whine, followed by a deep hiss)
                    + Shipboard Computer: Automatic lockdown successfully engaged.
                    + Unknown Voice 1: (wheezing) Motherfucker! Jackson, Phelps, stand by to repel boarders!
                    + Unknown Voice 2: Roger!
                    + Unknown Voice 3: Already there.
                    + Shipboard Computer: Warning. Minor breach in Engineering. Local depressurization in five minutes.
                    + Unknown Voice 1: Shit, shit, shit.
                    + Unknown Voice 3: Jackson\'s down!
                    + Unknown Voice 1: Motherf-
                    + Unknown Voice 4: Captain. Enough. You know why we\'re here, and we have your engineer. Tell us where you hid the goods and maybe we\'ll let you live.
                    + Unknown Voice 5: Let me go you son of a-
                    + (gunshot, ballistic)
                    + Unknown Voice 4: You fucking idiot!
                    + Unknown Voice 6: Don\'t you start, you said-
                    + Unknown Voice 4: I don\'t care what I fucking said, shit-for-brains!
                    + Unknown Voice 7: Boss, I hate to rain on your little parade but SDF just popped up on my scopes, \'bout... fifty klicks out, closing fast. Be on top of us in a few minutes tops.
                    + Unknown Voice 4: Fuck! You, dumbass! Link up with the others and grab as many crates as you can! Pray for your sake that we get the right one!
                    + END LOG
                    +
                    + CATALOGUER DATA ANALYSIS RESULTS:
                    + No matches found for any of the voices involved. Ship manifest suggests a crew of at least six, but under the circumstances three of the recorded voices are clearly not from the crew.
                    +
                    + Audio resynthesis of firearm discharge matches common high-caliber ballistic sidearm cartridges, best match 10mm Watson Special. An uncommon cartridge, one not manufactured locally. + "} + +/obj/structure/prop/blackbox/salamander_wreck + catalogue_data = list(/datum/category_item/catalogue/information/blackbox/salamander_wreck) + +/datum/category_item/catalogue/information/blackbox/salamander_wreck + name = "Black Box Data - ITV Unity" + desc = {" +
                    + BEGIN LOG
                    + Unknown Speaker 1: Hey cap, you seeing this?
                    + Unknown Speaker 2 (\"Cap\"): Yea-- what the fuck?
                    + Unknown Speaker 1: Yeah that was my reaction.
                    + Unknown Speaker 3: What are you tw-- holy shit, what is that?
                    + Unknown Speakers 1 & 2: We don't know!
                    + Unknown Speaker 4: What\'s all the yel-- fuck me sideways, what the hell?
                    + Unknown Speaker 2 (\"Cap\"): Stars above and fucking below, they never said anything like this was in the cans... shouldn't the port bioscanners have picked this up?
                    + Unknown Speaker 3: I told you that guy was one shady motherfucker, and whaddaya know.
                    + Unknown Speaker 1: No kidding. Christ. What\'s the play, cap?
                    + Unknown Speaker 4: I say we shoot this crap into the nearest star. Or gas giant maybe.
                    + Unknown Speaker 2 (\"Cap\"): Pretty much. Get in one of the voidsuits and rig the can up with some spare O2 canisters -- plot it a course that\'ll sling it right into V3\'s atmosphere.
                    + Unknown Speaker 1: Yeah, sure. That shouldn\'t take long. Then what?
                    + Unknown Speaker 2 (\"Cap\"): Let\'s see... ah, perfect. There's another ship passing close by, and I know the captain. We'll take our suits out and have her pick us up. Max, you still got those old cutting charges I told you to get rid of?
                    + Unknown Speaker 4 (\"Max\"): No. (pause) ...but also yes.
                    + Unknown Speaker 2 (\"Cap\"): Your propensity for ignoring my orders has once again paid off... man, I\'m gonna miss the Unity, but... better this way. I want holes in both sides of the engine bay and one in the portside crawlspace. Lock all the doors open and depressurize the main cabins. Drain as much fuel as you can, break up the main power block, and scatter the parts.
                    + Unknown Speaker 1: You want us to scuttle her, chief?
                    + Unknown Speaker 2 (\"Cap\"): Nail on the head, my friend.
                    + Unknown Speaker 3: Let\'s get rid of that fucking can first, I think it just blinked at me.
                    + END LOG
                    +
                    + CATALOGUER VOICE RECOGNITION RESULTS:
                    + SPEAKER ONE: No match.
                    + SPEAKER TWO, \"CAP\": Predicted to be Jeremiah Wells, human, last registered owner of the ITV Unity and small-time smuggler of fake alien artefacts. Wanted for smuggling and sale of said (fake) alien artefacts. 87% confidence.
                    + SPEAKER THREE: Predicted to be Allie Wells, human, niece of Captain Wells. 90% confidence.
                    + SPEAKER FOUR, \"MAX\": Predicted to be Maxwell Ulysses Sarevic, zorren, known smuggler, and wanted in the Elysian Colonies for liberation of slaves and, quote, \'harshing my vibe, and being a, like, total lamer, dude\', unquote. 99% confidence. + "} diff --git a/code/game/objects/structures/props/fake_ai.dm b/code/game/objects/structures/props/fake_ai.dm index e1aa98b64a7..75d5de407cc 100644 --- a/code/game/objects/structures/props/fake_ai.dm +++ b/code/game/objects/structures/props/fake_ai.dm @@ -1,20 +1,20 @@ -// A fluff structure to visually look like an AI core. -// Unlike the decoy AI mob, this won't explode if someone tries to card it. -/obj/structure/prop/fake_ai - name = "AI" - desc = "" - icon = 'icons/mob/AI.dmi' - icon_state = "ai" - -/obj/structure/prop/fake_ai/attackby(obj/O, mob/user) - if(istype(O, /obj/item/device/aicard)) // People trying to card the fake AI will get told its impossible. - to_chat(user, span("warning", "This core does not appear to have a suitable port to use \the [O] on...")) - return TRUE - return ..() - -/obj/structure/prop/fake_ai/dead - icon_state = "ai-crash" - -/obj/structure/prop/fake_ai/dead/crashed_med_shuttle - name = "V.I.T.A." +// A fluff structure to visually look like an AI core. +// Unlike the decoy AI mob, this won't explode if someone tries to card it. +/obj/structure/prop/fake_ai + name = "AI" + desc = "" + icon = 'icons/mob/AI.dmi' + icon_state = "ai" + +/obj/structure/prop/fake_ai/attackby(obj/O, mob/user) + if(istype(O, /obj/item/device/aicard)) // People trying to card the fake AI will get told its impossible. + to_chat(user, span("warning", "This core does not appear to have a suitable port to use \the [O] on...")) + return TRUE + return ..() + +/obj/structure/prop/fake_ai/dead + icon_state = "ai-crash" + +/obj/structure/prop/fake_ai/dead/crashed_med_shuttle + name = "V.I.T.A." icon_state = "ai-heartline-crash" \ No newline at end of file diff --git a/code/game/objects/structures/props/fantasy.dm b/code/game/objects/structures/props/fantasy.dm new file mode 100644 index 00000000000..8e3823f863b --- /dev/null +++ b/code/game/objects/structures/props/fantasy.dm @@ -0,0 +1,52 @@ +//props for more fantasy type settings + +/obj/structure/prop/fantasy + name = "some fantasy thing" + desc = "My description is broken, bug a developer." + icon = 'icons/obj/props/fantasy.dmi' + density = TRUE + anchored = TRUE + +/obj/structure/prop/fantasy/throne + name = "throne" + desc = "An ornate golden throne for the truly pompous." + icon_state = "throne" + +/obj/structure/prop/fantasy/anvil + name = "anvil" + desc = "A big solid chunk of cast steel used for smithing." + icon_state = "anvil" + +/obj/structure/prop/fantasy/grindstone + name = "grindstone" + desc = "A pedal powered, rough wheel that is used to sharpen and refine metal." + icon_state = "grindstone" + +/obj/structure/prop/fantasy/kiln + name = "kiln" + desc = "A large stone furnace." + icon_state = "kiln" + +/obj/structure/prop/fantasy/redbanner + name = "tapestry" + desc = "A red tapestry hanging from the wall." + icon_state = "redbanner" + density = FALSE + +/obj/structure/prop/fantasy/pinkbanner + name = "tapestry" + desc = "A red tapestry hanging from the wall." + icon_state = "pinkbanner" + density = FALSE + +/obj/structure/prop/fantasy/redbanner_standing + name = "banner" + desc = "A red banner hanging from a stand." + icon_state = "redbanner_stand" + density = FALSE + +/obj/structure/prop/fantasy/pinkbanner_standing + name = "banner" + desc = "A red banner hanging from a stand." + icon_state = "pinkbanner_stand" + density = FALSE diff --git a/code/game/objects/structures/props/transmitter.dm b/code/game/objects/structures/props/transmitter.dm index bfbe1c9a909..f7e13e9a428 100644 --- a/code/game/objects/structures/props/transmitter.dm +++ b/code/game/objects/structures/props/transmitter.dm @@ -1,29 +1,29 @@ -// A fluff structure for certain PoIs involving communications. -// It makes audible sounds, generally in morse code. -/obj/structure/prop/transmitter - name = "transmitter" - desc = "A machine that appears to be transmitting a message somewhere else. It sounds like it's on a loop." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "sensors" - var/datum/looping_sound/sequence/morse/soundloop - var/message_to_play = "The quick brown fox jumps over the lazy dog." - -/obj/structure/prop/transmitter/Initialize() - soundloop = new(list(src), FALSE) - set_new_message(message_to_play) - soundloop.start() - interaction_message = "On the monitor it displays '[uppertext(message_to_play)]'." - return ..() - -/obj/structure/prop/transmitter/Destroy() - QDEL_NULL(soundloop) - return ..() - -/obj/structure/prop/transmitter/vv_edit_var(var_name, var_value) - if(var_name == "message_to_play") - set_new_message(var_value) - return ..() - -/obj/structure/prop/transmitter/proc/set_new_message(new_message) - soundloop.set_new_sequence(new_message) - interaction_message = "On the monitor it displays '[uppertext(new_message)]'." +// A fluff structure for certain PoIs involving communications. +// It makes audible sounds, generally in morse code. +/obj/structure/prop/transmitter + name = "transmitter" + desc = "A machine that appears to be transmitting a message somewhere else. It sounds like it's on a loop." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "sensors" + var/datum/looping_sound/sequence/morse/soundloop + var/message_to_play = "The quick brown fox jumps over the lazy dog." + +/obj/structure/prop/transmitter/Initialize() + soundloop = new(list(src), FALSE) + set_new_message(message_to_play) + soundloop.start() + interaction_message = "On the monitor it displays '[uppertext(message_to_play)]'." + return ..() + +/obj/structure/prop/transmitter/Destroy() + QDEL_NULL(soundloop) + return ..() + +/obj/structure/prop/transmitter/vv_edit_var(var_name, var_value) + if(var_name == "message_to_play") + set_new_message(var_value) + return ..() + +/obj/structure/prop/transmitter/proc/set_new_message(new_message) + soundloop.set_new_sequence(new_message) + interaction_message = "On the monitor it displays '[uppertext(new_message)]'." diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index 3efd7091b0d..3b357d9fd5b 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -202,7 +202,7 @@ /obj/structure/railing/attackby(obj/item/W as obj, mob/user as mob) // Dismantle - if(W.is_wrench() && !anchored) + if(W.has_tool_quality(TOOL_WRENCH) && !anchored) playsound(src, W.usesound, 50, 1) if(do_after(user, 20, src)) user.visible_message("\The [user] dismantles \the [src].", "You dismantle \the [src].") @@ -211,8 +211,8 @@ return // Repair - if(health < maxhealth && istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/F = W + if(health < maxhealth && W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/F = W.get_welder() if(F.welding) playsound(src, F.usesound, 50, 1) if(do_after(user, 20, src)) @@ -221,7 +221,7 @@ return // Install - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) user.visible_message(anchored ? "\The [user] begins unscrewing \the [src]." : "\The [user] begins fasten \the [src]." ) playsound(src, W.usesound, 75, 1) if(do_after(user, 10, src)) diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 6ac0d6b5021..f3875bf5082 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -1,191 +1,191 @@ -/* -CONTAINS: -SAFES -FLOOR SAFES -*/ - -//SAFES -/obj/structure/safe - name = "safe" - desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms - 2 tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\"" - icon = 'icons/obj/structures.dmi' - icon_state = "safe" - anchored = TRUE - density = TRUE - var/open = 0 //is the safe open? - var/tumbler_1_pos //the tumbler position- from 0 to 72 - var/tumbler_1_open //the tumbler position to open at- 0 to 72 - var/tumbler_2_pos - var/tumbler_2_open - var/dial = 0 //where is the dial pointing? - var/space = 0 //the combined w_class of everything in the safe - var/maxspace = 24 //the maximum combined w_class of stuff in the safe - - -/obj/structure/safe/Initialize() - tumbler_1_pos = rand(0, 72) - tumbler_1_open = rand(0, 72) - - tumbler_2_pos = rand(0, 72) - tumbler_2_open = rand(0, 72) - - if(. != INITIALIZE_HINT_QDEL) - return INITIALIZE_HINT_LATELOAD - -/obj/structure/safe/LateInitialize() - . = ..() - for(var/obj/item/I in loc) - if(space >= maxspace) - return - if(I.w_class + space <= maxspace) - space += I.w_class - I.forceMove(src) - -/obj/structure/safe/proc/check_unlocked(mob/user as mob, canhear) - if(user && canhear) - if(tumbler_1_pos == tumbler_1_open) - to_chat(user, "You hear a [pick("tonk", "krunk", "plunk")] from \the [src].") - if(tumbler_2_pos == tumbler_2_open) - to_chat(user, "You hear a [pick("tink", "krink", "plink")] from \the [src].") - if(tumbler_1_pos == tumbler_1_open && tumbler_2_pos == tumbler_2_open) - if(user) visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!") - return 1 - return 0 - - -/obj/structure/safe/proc/decrement(num) - num -= 1 - if(num < 0) - num = 71 - return num - - -/obj/structure/safe/proc/increment(num) - num += 1 - if(num > 71) - num = 0 - return num - - -/obj/structure/safe/update_icon() - if(open) - icon_state = "[initial(icon_state)]-open" - else - icon_state = initial(icon_state) - - -/obj/structure/safe/attack_hand(mob/user as mob) - user.set_machine(src) - var/dat = "
                    " - dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +" - if(open) - dat += "" - for(var/i = contents.len, i>=1, i--) - var/obj/item/P = contents[i] - dat += "" - dat += "
                    [P.name]
                    " - user << browse("[name][dat]", "window=safe;size=350x300") - - -/obj/structure/safe/Topic(href, href_list) - if(!ishuman(usr)) return - var/mob/living/carbon/human/user = usr - - var/canhear = 0 - if(user.get_type_in_hands(/obj/item/clothing/accessory/stethoscope)) - canhear = 1 - - if(href_list["open"]) - if(check_unlocked()) - to_chat(user, "You [open ? "close" : "open"] [src].") - open = !open - update_icon() - updateUsrDialog() - return - else - to_chat(user, "You can't [open ? "close" : "open"] [src], the lock is engaged!") - return - - if(href_list["decrement"]) - dial = decrement(dial) - if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71) - tumbler_1_pos = decrement(tumbler_1_pos) - if(canhear) - to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") - if(tumbler_1_pos == tumbler_2_pos + 37 || tumbler_1_pos == tumbler_2_pos - 35) - tumbler_2_pos = decrement(tumbler_2_pos) - if(canhear) - to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") - playsound(src, 'sound/machines/click.ogg', 20, 1) - check_unlocked(user, canhear) - - updateUsrDialog() - return - - if(href_list["increment"]) - dial = increment(dial) - if(dial == tumbler_1_pos - 1 || dial == tumbler_1_pos + 71) - tumbler_1_pos = increment(tumbler_1_pos) - if(canhear) - to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") - if(tumbler_1_pos == tumbler_2_pos - 37 || tumbler_1_pos == tumbler_2_pos + 35) - tumbler_2_pos = increment(tumbler_2_pos) - if(canhear) - to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") - playsound(src, 'sound/machines/click.ogg', 20, 1) - check_unlocked(user, canhear) - updateUsrDialog() - return - - if(href_list["retrieve"]) - user << browse("", "window=safe") // Close the menu - - var/obj/item/P = locate(href_list["retrieve"]) in src - if(open) - if(P && in_range(src, user)) - user.put_in_hands(P) - updateUsrDialog() - - -/obj/structure/safe/attackby(obj/item/I as obj, mob/user as mob) - if(open) - if(I.w_class + space <= maxspace) - space += I.w_class - user.drop_item() - I.loc = src - to_chat(user, "You put [I] in \the [src].") - updateUsrDialog() - return - else - to_chat(user, "[I] won't fit in \the [src].") - return - else - if(istype(I, /obj/item/clothing/accessory/stethoscope)) - to_chat(user, "Hold [I] in one of your hands while you manipulate the dial.") - return - - -/obj/structure/safe/ex_act(severity) - return - -//FLOOR SAFES -/obj/structure/safe/floor - name = "floor safe" - icon_state = "floorsafe" - density = FALSE - level = 1 //underfloor - plane = PLATING_PLANE - layer = ABOVE_UTILITY - -/obj/structure/safe/floor/Initialize() - . = ..() - var/turf/T = loc - if(istype(T) && !T.is_plating()) - hide(1) - update_icon() - -/obj/structure/safe/floor/hide(var/intact) - invisibility = intact ? 101 : 0 - -/obj/structure/safe/floor/hides_under_flooring() - return 1 +/* +CONTAINS: +SAFES +FLOOR SAFES +*/ + +//SAFES +/obj/structure/safe + name = "safe" + desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms - 2 tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\"" + icon = 'icons/obj/structures.dmi' + icon_state = "safe" + anchored = TRUE + density = TRUE + var/open = 0 //is the safe open? + var/tumbler_1_pos //the tumbler position- from 0 to 72 + var/tumbler_1_open //the tumbler position to open at- 0 to 72 + var/tumbler_2_pos + var/tumbler_2_open + var/dial = 0 //where is the dial pointing? + var/space = 0 //the combined w_class of everything in the safe + var/maxspace = 24 //the maximum combined w_class of stuff in the safe + + +/obj/structure/safe/Initialize() + tumbler_1_pos = rand(0, 72) + tumbler_1_open = rand(0, 72) + + tumbler_2_pos = rand(0, 72) + tumbler_2_open = rand(0, 72) + + if(. != INITIALIZE_HINT_QDEL) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/safe/LateInitialize() + . = ..() + for(var/obj/item/I in loc) + if(space >= maxspace) + return + if(I.w_class + space <= maxspace) + space += I.w_class + I.forceMove(src) + +/obj/structure/safe/proc/check_unlocked(mob/user as mob, canhear) + if(user && canhear) + if(tumbler_1_pos == tumbler_1_open) + to_chat(user, "You hear a [pick("tonk", "krunk", "plunk")] from \the [src].") + if(tumbler_2_pos == tumbler_2_open) + to_chat(user, "You hear a [pick("tink", "krink", "plink")] from \the [src].") + if(tumbler_1_pos == tumbler_1_open && tumbler_2_pos == tumbler_2_open) + if(user) visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!") + return 1 + return 0 + + +/obj/structure/safe/proc/decrement(num) + num -= 1 + if(num < 0) + num = 71 + return num + + +/obj/structure/safe/proc/increment(num) + num += 1 + if(num > 71) + num = 0 + return num + + +/obj/structure/safe/update_icon() + if(open) + icon_state = "[initial(icon_state)]-open" + else + icon_state = initial(icon_state) + + +/obj/structure/safe/attack_hand(mob/user as mob) + user.set_machine(src) + var/dat = "
                    " + dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +" + if(open) + dat += "" + for(var/i = contents.len, i>=1, i--) + var/obj/item/P = contents[i] + dat += "" + dat += "
                    [P.name]
                    " + user << browse("[name][dat]", "window=safe;size=350x300") + + +/obj/structure/safe/Topic(href, href_list) + if(!ishuman(usr)) return + var/mob/living/carbon/human/user = usr + + var/canhear = 0 + if(user.get_type_in_hands(/obj/item/clothing/accessory/stethoscope)) + canhear = 1 + + if(href_list["open"]) + if(check_unlocked()) + to_chat(user, "You [open ? "close" : "open"] [src].") + open = !open + update_icon() + updateUsrDialog() + return + else + to_chat(user, "You can't [open ? "close" : "open"] [src], the lock is engaged!") + return + + if(href_list["decrement"]) + dial = decrement(dial) + if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71) + tumbler_1_pos = decrement(tumbler_1_pos) + if(canhear) + to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") + if(tumbler_1_pos == tumbler_2_pos + 37 || tumbler_1_pos == tumbler_2_pos - 35) + tumbler_2_pos = decrement(tumbler_2_pos) + if(canhear) + to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") + playsound(src, 'sound/machines/click.ogg', 20, 1) + check_unlocked(user, canhear) + + updateUsrDialog() + return + + if(href_list["increment"]) + dial = increment(dial) + if(dial == tumbler_1_pos - 1 || dial == tumbler_1_pos + 71) + tumbler_1_pos = increment(tumbler_1_pos) + if(canhear) + to_chat(user, "You hear a [pick("clack", "scrape", "clank")] from \the [src].") + if(tumbler_1_pos == tumbler_2_pos - 37 || tumbler_1_pos == tumbler_2_pos + 35) + tumbler_2_pos = increment(tumbler_2_pos) + if(canhear) + to_chat(user, "You hear a [pick("click", "chink", "clink")] from \the [src].") + playsound(src, 'sound/machines/click.ogg', 20, 1) + check_unlocked(user, canhear) + updateUsrDialog() + return + + if(href_list["retrieve"]) + user << browse("", "window=safe") // Close the menu + + var/obj/item/P = locate(href_list["retrieve"]) in src + if(open) + if(P && in_range(src, user)) + user.put_in_hands(P) + updateUsrDialog() + + +/obj/structure/safe/attackby(obj/item/I as obj, mob/user as mob) + if(open) + if(I.w_class + space <= maxspace) + space += I.w_class + user.drop_item() + I.loc = src + to_chat(user, "You put [I] in \the [src].") + updateUsrDialog() + return + else + to_chat(user, "[I] won't fit in \the [src].") + return + else + if(istype(I, /obj/item/clothing/accessory/stethoscope)) + to_chat(user, "Hold [I] in one of your hands while you manipulate the dial.") + return + + +/obj/structure/safe/ex_act(severity) + return + +//FLOOR SAFES +/obj/structure/safe/floor + name = "floor safe" + icon_state = "floorsafe" + density = FALSE + level = 1 //underfloor + plane = PLATING_PLANE + layer = ABOVE_UTILITY + +/obj/structure/safe/floor/Initialize() + . = ..() + var/turf/T = loc + if(istype(T) && !T.is_plating()) + hide(1) + update_icon() + +/obj/structure/safe/floor/hide(var/intact) + invisibility = intact ? 101 : 0 + +/obj/structure/safe/floor/hides_under_flooring() + return 1 diff --git a/code/game/objects/structures/salvageable.dm b/code/game/objects/structures/salvageable.dm index 8408b3cddde..4999456a5e3 100644 --- a/code/game/objects/structures/salvageable.dm +++ b/code/game/objects/structures/salvageable.dm @@ -14,7 +14,7 @@ return /obj/structure/salvageable/attackby(obj/item/I, mob/user) - if(I.is_crowbar()) + if(I.has_tool_quality(TOOL_CROWBAR)) playsound(src, I.usesound, 50, 1) var/actual_time = I.toolspeed * 170 user.visible_message( \ diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm index f61cf27bd3e..aa2c546ac3b 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs.dm @@ -1,1946 +1,1946 @@ -/obj/structure/sign - icon = 'icons/obj/decals.dmi' - anchored = TRUE - opacity = 0 - density = FALSE - plane = OBJ_PLANE //VOREStation Edit - layer = ABOVE_JUNK_LAYER //VOREStation Edit - w_class = ITEMSIZE_NORMAL - -/obj/structure/sign/ex_act(severity) - qdel(src) - -/obj/structure/sign/attackby(obj/item/tool, mob/user) //deconstruction - if(tool.is_screwdriver() && !istype(src, /obj/structure/sign/scenery) && !istype(src, /obj/structure/sign/double)) - playsound(src, tool.usesound, 50, 1) - unfasten(user) - else ..() - -/obj/structure/sign/proc/unfasten(mob/user) - user.visible_message(SPAN_NOTICE("\The [user] unfastens \the [src]."), SPAN_NOTICE("You unfasten \the [src].")) - var/obj/item/sign/S = new(src.loc) - S.name = name - S.desc = desc - S.icon_state = icon_state - S.sign_state = icon_state - S.original_type = type - qdel(src) - - -/obj/item/sign - name = "sign" - desc = "" - icon = 'icons/obj/decals.dmi' - w_class = ITEMSIZE_NORMAL //big - var/sign_state = "" - var/original_type - -/obj/item/sign/attackby(obj/item/tool as obj, mob/user as mob) //construction - if(tool.is_screwdriver() && isturf(user.loc)) - var/direction = tgui_input_list(usr, "In which direction?", "Select direction.", list("North", "East", "South", "West", "Cancel")) - if(direction == "Cancel") return - var/target_type = original_type || /obj/structure/sign - var/obj/structure/sign/S = new target_type(user.loc) - switch(direction) - if("North") - S.pixel_y = 32 - if("East") - S.pixel_x = 32 - if("South") - S.pixel_y = -32 - if("West") - S.pixel_x = -32 - else return - S.name = name - S.desc = desc - S.icon_state = sign_state - to_chat(user, "You fasten \the [S] with your [tool].") - qdel(src) - else ..() - -/obj/structure/sign/scenery/map - name = "station map" - desc = "A framed picture of the station." - -/obj/structure/sign/scenery/map/left - icon_state = "map-left" - -/obj/structure/sign/scenery/map/right - icon_state = "map-right" - -/obj/structure/sign/securearea - name = "\improper SECURE AREA" - desc = "A warning sign which reads 'SECURE AREA'." - icon_state = "securearea" - -/obj/structure/sign/biohazard - name = "\improper BIOHAZARD" - desc = "A warning sign which reads 'BIOHAZARD'." - icon_state = "bio" - -/obj/structure/sign/electricshock - name = "\improper HIGH VOLTAGE" - desc = "A warning sign which reads 'HIGH VOLTAGE'." - icon_state = "shock" - -/obj/structure/sign/examroom - name = "\improper EXAM" - desc = "A guidance sign which reads 'EXAM ROOM'." - icon_state = "examroom" - -/obj/structure/sign/vacuum - name = "\improper HARD VACUUM AHEAD" - desc = "A warning sign which reads 'HARD VACUUM AHEAD'." - icon_state = "space" - -/obj/structure/sign/deathsposal - name = "\improper DISPOSAL LEADS TO SPACE" - desc = "A warning sign which reads 'DISPOSAL LEADS TO SPACE'." - icon_state = "deathsposal" - -/obj/structure/sign/pods - name = "\improper ESCAPE PODS" - desc = "A warning sign which reads 'ESCAPE PODS'." - icon_state = "pods" - -/obj/structure/sign/fire - name = "\improper DANGER: FIRE" - desc = "A warning sign which reads 'DANGER: FIRE'." - icon_state = "fire" - -/obj/structure/sign/nosmoking_1 - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'." - icon_state = "nosmoking" - -/obj/structure/sign/nosmoking_2 - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'." - icon_state = "nosmoking2" - -/obj/structure/sign/nosmoking_2/burnt - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'. It looks like someone didn't follow its advice..." - icon_state = "nosmoking2_burnt" - -/obj/structure/sign/warning - name = "\improper WARNING" - icon_state = "securearea" - -/obj/structure/sign/warning/Initialize() - . = ..() - desc = "A warning sign which reads '[name]'." - -/obj/structure/sign/warning/airlock - name = "\improper EXTERNAL AIRLOCK" - icon_state = "doors" - -/obj/structure/sign/warning/biohazard - name = "\improper BIOHAZARD" - icon_state = "bio" - -/obj/structure/sign/warning/bomb_range - name = "\improper BOMB RANGE" - icon_state = "blast" - -/obj/structure/sign/warning/caution - name = "\improper CAUTION" - -/obj/structure/sign/warning/compressed_gas - name = "\improper COMPRESSED GAS" - icon_state = "hikpa" - -/obj/structure/sign/warning/deathsposal - name = "\improper DISPOSAL LEADS TO SPACE" - icon_state = "deathsposal" - -/obj/structure/sign/warning/docking_area - name = "\improper KEEP CLEAR: DOCKING AREA" - -/obj/structure/sign/warning/evac - name = "\improper KEEP CLEAR: EVAC DOCKING AREA" - icon_state = "evac" - -/obj/structure/sign/warning/engineering_access - name = "\improper ENGINEERING ACCESS" - icon_state = "engine" - -/obj/structure/sign/warning/fire - name = "\improper DANGER: FIRE" - icon_state = "fire" - -/obj/structure/sign/warning/high_voltage - name = "\improper HIGH VOLTAGE" - icon_state = "shock" - -/obj/structure/sign/warning/hot_exhaust - name = "\improper HOT EXHAUST" - icon_state = "fire" - -/obj/structure/sign/warning/internals_required - name = "\improper INTERNALS REQUIRED" - -/obj/structure/sign/warning/lethal_turrets - name = "\improper LETHAL TURRETS" - icon_state = "turrets" - -/obj/structure/sign/warning/lethal_turrets/Initialize() - . = ..() - desc += " Enter at own risk!." - -/obj/structure/sign/warning/mail_delivery - name = "\improper MAIL DELIVERY" - icon_state = "mail" - -/obj/structure/sign/warning/moving_parts - name = "\improper MOVING PARTS" - icon_state = "movingparts" - -/obj/structure/sign/warning/nosmoking_1 - name = "\improper NO SMOKING" - icon_state = "nosmoking" - -/obj/structure/sign/warning/nosmoking_2 - name = "\improper NO SMOKING" - icon_state = "nosmoking2" - -/obj/structure/sign/warning/pods - name = "\improper ESCAPE PODS" - icon_state = "pods" - -/obj/structure/sign/warning/radioactive - name = "\improper RADIOACTIVE AREA" - icon_state = "radiation" - -/obj/structure/sign/warning/secure_area - name = "\improper SECURE AREA" - icon_state = "securearea2" - -/obj/structure/sign/warning/secure_area/armory - name = "\improper ARMORY" - icon_state = "armory" - -/obj/structure/sign/warning/server_room - name = "\improper SERVER ROOM" - icon_state = "server" - -/obj/structure/sign/warning/siphon_valve - name = "\improper SIPHON VALVE" - -/obj/structure/sign/warning/vacuum - name = "\improper HARD VACUUM AHEAD" - icon_state = "space" - -/obj/structure/sign/warning/vent_port - name = "\improper EJECTION/VENTING PORT" - -/obj/structure/sign/warning/emergence - name = "\improper EMERGENT INTELLIGENCE DETAILS" - icon_state = "rogueai" - -/obj/structure/sign/warning/falling - name = "\improper FALL HAZARD" - icon_state = "falling" - -/obj/structure/sign/warning/lava - name = "\improper MOLTEN SURFACE" - icon_state = "lava" - -/obj/structure/sign/warning/acid - name = "\improper ACIDIC SURFACE" - icon_state = "acid" - -/obj/structure/sign/warning/cold - name = "\improper EXTREME COLD ENVIRONMENT" - icon_state = "cold" - -/obj/structure/sign/redcross - name = "medbay" - desc = "An interstellar symbol of medical institutions. You'll probably get help here." - icon_state = "bluecross" - -/obj/structure/sign/greencross - name = "medbay" - desc = "An interstellar symbol of medical institutions. You'll probably get help here." - icon_state = "bluecross2" - -/obj/structure/sign/goldenplaque - name = "The Most Robust Men Award for Robustness" - desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." - icon_state = "goldenplaque" - -/obj/structure/sign/kiddieplaque - name = "\improper AI developers plaque" - desc = "Next to the extremely long list of names and job titles. Beneath the image, someone has scratched the word \"PACKETS\"" - icon_state = "kiddieplaque" - -/obj/structure/sign/atmosplaque - name = "\improper FEA atmospherics division plaque" - desc = "This plaque commemorates the fall of the Atmos FEA division. For all the charred, dizzy, and brittle men who have died in its hands." - icon_state = "atmosplaque" - -/obj/structure/sign/periodic - name = "periodic table" - desc = "A sign reminding those visiting of the elements of the periodic table- though, they should have memorized them by now." - icon_state = "periodic" - -/obj/structure/sign/double/maltesefalcon //The sign is 64x32, so it needs two tiles. ;3 - name = "The Maltese Falcon" - desc = "The Maltese Falcon, Space Bar and Grill." - -/obj/structure/sign/double/maltesefalcon/left - icon_state = "maltesefalcon-left" - -/obj/structure/sign/double/maltesefalcon/right - icon_state = "maltesefalcon-right" - -/obj/structure/sign/science //These 3 have multiple types, just var-edit the icon_state to whatever one you want on the map - name = "\improper SCIENCE!" - desc = "A warning sign which reads 'SCIENCE'." - icon_state = "science1" - -/obj/structure/sign/chemistry - name = "\improper CHEMISTRY" - desc = "A warning sign which reads 'CHEMISTRY'." - icon_state = "chemistry1" - -/obj/structure/sign/botany - name = "\improper HYDROPONICS" - desc = "A warning sign which reads 'HYDROPONICS'." - icon_state = "hydro1" - -/obj/structure/sign/hydro - name = "\improper HYDROPONICS" - desc = "A sign labelling an area as a place where plants are grown." - icon_state = "hydro2" - -/obj/structure/sign/hydrostorage - name = "\improper HYDROPONICS STORAGE" - desc = "A sign labelling an area as a place where plant growing supplies are kept." - icon_state = "hydro3" - -/obj/structure/sign/xenobio - name = "\improper XENOBIOLOGY" - desc = "A warning sign which reads XENOBIOLOGY." - icon_state = "xenobio3" - - -//direction signs presented by the order they appear in the dmi -/obj/structure/sign/directions - name = "direction sign" - desc = "A direction sign, claiming to know the way to... somewhere?" - icon_state = "direction" - icon = 'icons/obj/decals_directions.dmi' - //TODO: set up overlay systems, inc. interactions (e.g. vines clear w/ plantbgone or fire, snow can be brushed off or melted, and so on) - -//disabled this proc, it serves no purpose except to overwrite the description that already exists. may have been intended for making your own signs? -//seems to defeat the point of having a generic directional sign that mappers could edit and use in POIs? left it here in case something breaks. -/* -/obj/structure/sign/directions/Initialize() - . = ..() - desc = "A direction sign, pointing out the way to \the [src]." -*/ - -//Also floor/level/deck signs in the same vein. Naming conventions! -/obj/structure/sign/levels - name = "level sign" - desc = "A level sign, claiming to know which level to find... Something on?" - icon_state = "level" - icon = 'icons/obj/decals_levels.dmi' - -//engineering signs -/obj/structure/sign/directions/engineering - name = "\improper Engineering Department" - desc = "A direction sign, pointing out the way to the Engineering Department." - icon_state = "direction_eng" - -/obj/structure/sign/levels/engineering - name = "\improper Engineering Department" - desc = "A level sign, stating the level to find the Engineering Department on." - icon_state = "level_eng" - -/obj/structure/sign/directions/engineering/reactor - name = "\improper Reactor" - desc = "A direction sign, pointing out the way to the Reactor." - icon_state = "direction_core" - -/obj/structure/sign/levels/engineering/reactor - name = "\improper Reactor" - desc = "A level sign, stating the level to find the Reactor on." - icon_state = "level_core" - -/obj/structure/sign/directions/engineering/solars - name = "\improper Solar Array" - desc = "A direction sign, pointing out the way to the nearest Solar Array." - icon_state = "direction_solar" - -/obj/structure/sign/levels/engineering/solars - name = "\improper Solar Array" - desc = "A level sign, stating the level to find the nearest Solar Array on." - icon_state = "level_solar" - -/obj/structure/sign/directions/engineering/atmospherics - name = "\improper Atmospherics Department" - desc = "A direction sign, pointing out the way to the Atmospherics Department." - icon_state = "direction_atmos" - -/obj/structure/sign/levels/engineering/atmospherics - name = "\improper Atmospherics Department" - desc = "A level sign, stating the level to find the Atmospherics Department on." - icon_state = "level_atmos" - -/obj/structure/sign/directions/engineering/gravgen - name = "\improper Gravity Generator" - desc = "A direction sign, pointing out the way to the Artificial Gravity Generator." - icon_state = "direction_grav" - -/obj/structure/sign/levels/engineering/gravgen - name = "\improper Gravity Generator" - desc = "A level sign, stating the level to find the Artificial Gravity Generator on." - icon_state = "level_grav" - -/obj/structure/sign/directions/engineering/engeqp - name = "\improper Engineering Equipment Storage" - desc = "A direction sign, pointing out the way to Engineering Equipment Storage." - icon_state = "direction_engeqp" - -/obj/structure/sign/levels/engineering/engeqp - name = "\improper Engineering Equipment Storage" - desc = "A level sign, stating the level to find Engineering Equipment Storage on." - icon_state = "level_engeqp" - -//security signs -/obj/structure/sign/directions/security - name = "\improper Security Department" - desc = "A direction sign, pointing out the way to the Security Department." - icon_state = "direction_sec" - -/obj/structure/sign/levels/security - name = "\improper Security Department" - desc = "A level sign, stating the level to find the Security Department on." - icon_state = "level_sec" - -/obj/structure/sign/directions/security/armory - name = "\improper Armory" - desc = "A direction sign, pointing out the way to the Armory." - icon_state = "direction_armory" - -/obj/structure/sign/levels/security/armory - name = "\improper Armory" - desc = "A level sign, stating the level to find the Armory on." - icon_state = "level_armory" - -/obj/structure/sign/directions/security/brig - name = "\improper Brig" - desc = "A direction sign, pointing out the way to the Brig." - icon_state = "direction_brig" - -/obj/structure/sign/levels/security/brig - name = "\improper Brig" - desc = "A level sign, stating the level to find the Brig on." - icon_state = "level_brig" - -/obj/structure/sign/directions/security/seceqp - name = "\improper Security Equipment Storage" - desc = "A direction sign, pointing out the way to Security Equipment Storage." - icon_state = "direction_seceqp" - -/obj/structure/sign/levels/security/seceqp - name = "\improper Security Equipment Storage" - desc = "A level sign, stating the level to find Security Equipment Storage on." - icon_state = "level_seceqp" - -/obj/structure/sign/directions/security/internal_affairs - name = "\improper Internal Affairs Office" - desc = "A direction sign, pointing out the way to the Internal Affairs Office." - icon_state = "direction_intaff" - -/obj/structure/sign/levels/security/internal_affairs - name = "\improper Internal Affairs Office" - desc = "A level sign, stating the level to find the Internal Affairs Office on." - icon_state = "level_intaff" - -/obj/structure/sign/directions/security/forensics - name = "\improper Forensics Lab" - desc = "A direction sign, pointing out the way to the Forensics Lab." - icon_state = "direction_forensics" - -/obj/structure/sign/levels/security/forensics - name = "\improper Forensics Lab" - desc = "A level sign, stating the level to find the Forensics Lab on." - icon_state = "level_forensics" - -/obj/structure/sign/directions/security/forensics/alt - icon_state = "direction_lab" - -/obj/structure/sign/levels/security/forensics/alt - icon_state = "level_lab" - -/obj/structure/sign/directions/security/interrogation - name = "\improper Interrogations" - desc = "A direction sign, pointing out the way to Interrogations." - icon_state = "direction_interrogation" - -/obj/structure/sign/levels/security/interrogation - name = "\improper Interrogations" - desc = "A level sign, stating the level to find Interrogations on." - icon_state = "level_interrogation" - -//science signs -/obj/structure/sign/directions/science - name = "\improper Science Department" - desc = "A direction sign, pointing out the way to the Science Department." - icon_state = "direction_sci" - -/obj/structure/sign/levels/science - name = "\improper Science Department" - desc = "A level sign, stating the level to find the Science Department on." - icon_state = "level_sci" - -/obj/structure/sign/directions/science/rnd - name = "\improper Research & Development" - desc = "A direction sign, pointing out the way to Research & Development." - icon_state = "direction_rnd" - -/obj/structure/sign/levels/science/rnd - name = "\improper Research & Development" - desc = "A level sign, stating the level to find Research & Development on." - icon_state = "level_rnd" - -/obj/structure/sign/directions/science/toxins - name = "\improper Toxins Lab" - desc = "A direction sign, pointing out the way to the Toxins Lab." - icon_state = "direction_toxins" - -/obj/structure/sign/levels/science/toxins - name = "\improper Toxins Lab" - desc = "A level sign, stating the level to find the Toxins Lab on." - icon_state = "level_toxins" - -/obj/structure/sign/directions/science/robotics - name = "\improper Robotics Workshop" - desc = "A direction sign, pointing out the way to the Robotics Workshop." - icon_state = "direction_robotics" - -/obj/structure/sign/levels/science/robotics - name = "\improper Robotics Workshop" - desc = "A level sign, stating the level to find the Robotics Workshop on." - icon_state = "level_robotics" - -/obj/structure/sign/directions/science/xenoarch - name = "\improper Xenoarchaeology Lab" - desc = "A direction sign, pointing out the way to the Xenoarchaeology Lab." - icon_state = "direction_xenoarch" - -/obj/structure/sign/levels/science/xenoarch - name = "\improper Xenoarchaeology Lab" - desc = "A level sign, stating the level to find the Xenoarchaeology Lab on." - icon_state = "level_xenoarch" - -/obj/structure/sign/directions/science/xenobiology - name = "\improper Xenobiology Lab" - desc = "A direction sign, pointing out the way to the Xenobiology Lab." - icon_state = "direction_xbio" - -/obj/structure/sign/levels/science/xenobiology - name = "\improper Xenobiology Lab" - desc = "A level sign, stating the level to find the Xenobiology Lab on." - icon_state = "level_xbio" - -/obj/structure/sign/directions/science/xenoflora - name = "\improper Xenoflora Lab" - desc = "A direction sign, pointing out the way to the Xenoflora Lab." - icon_state = "direction_xflora" - -/obj/structure/sign/levels/science/xenoflora - name = "\improper Xenoflora Lab" - desc = "A level sign, stating the level to find the Xenoflora Lab on." - icon_state = "level_xflora" - -/obj/structure/sign/directions/science/exploration - name = "\improper Exploration Department" - desc = "A direction sign, pointing out the way to the Exploration Department." - icon_state = "direction_explo" - -/obj/structure/sign/levels/science/exploration - name = "\improper Exploration Department" - desc = "A level sign, stating the level to find the Exploration Department on." - icon_state = "level_explo" - -//medical signs -/obj/structure/sign/directions/medical - name = "\improper Medical Bay" - desc = "A direction sign, pointing out the way to the Medical Bay." - icon_state = "direction_med" - -/obj/structure/sign/levels/medical - name = "\improper Medical Bay" - desc = "A level sign, stating the level to find the Medical Bay on." - icon_state = "level_med" - -/obj/structure/sign/directions/medical/chemlab - name = "\improper Chemistry Lab" - desc = "A direction sign, pointing out the way to the Chemistry Lab." - icon_state = "direction_chemlab" - -/obj/structure/sign/levels/medical/chemlab - name = "\improper Chemistry Lab" - desc = "A level sign, stating the level to find the Chemistry Lab on." - icon_state = "level_chemlab" - -/obj/structure/sign/directions/medical/surgery - name = "\improper Surgery" - desc = "A direction sign, pointing out the way to Surgery." - icon_state = "direction_surgery" - -/obj/structure/sign/levels/medical/surgery - name = "\improper Surgery" - desc = "A level sign, stating the level to find Surgery on." - icon_state = "level_surgery" - -/obj/structure/sign/directions/medical/operating_1 - name = "\improper Operating Theatre 1" - desc = "A direction sign, pointing out the way to Operating Theatre 1." - icon_state = "direction_op1" - -/obj/structure/sign/levels/medical/operating_1 - name = "\improper Operating Theatre 1" - desc = "A level sign, stating the level to find Operating Theatre 1 on." - icon_state = "level_op1" - -/obj/structure/sign/directions/medical/operating_2 - name = "\improper Operating Theatre 2" - desc = "A direction sign, pointing out the way to Operating Theatre 2." - icon_state = "direction_op2" - -/obj/structure/sign/levels/medical/operating_2 - name = "\improper Operating Theatre 2" - desc = "A level sign, stating the level to find Operating Theatre 2 on." - icon_state = "level_op2" - -/obj/structure/sign/directions/medical/virology - name = "\improper Virology" - desc = "A direction sign, pointing out the way to the Virology Lab." - icon_state = "direction_viro" - -/obj/structure/sign/levels/medical/virology - name = "\improper Virology" - desc = "A level sign, stating the level to find the Virology Lab on." - icon_state = "level_viro" - -/obj/structure/sign/directions/medical/medeqp - name = "\improper Medical Equipment Storage" - desc = "A direction sign, pointing out the way to Medical Equipment Storage." - icon_state = "direction_medeqp" - -/obj/structure/sign/levels/medical/medeqp - name = "\improper Medical Equipment Storage" - desc = "A level sign, stating the level to find Medical Equipment Storage on." - icon_state = "level_medeqp" - -/obj/structure/sign/directions/medical/morgue - name = "\improper Morgue" - desc = "A direction sign, pointing out the way to the Morgue." - icon_state = "direction_morgue" - -/obj/structure/sign/levels/medical/morgue - name = "\improper Morgue" - desc = "A level sign, stating the level to find the Morgue on." - icon_state = "level_morgue" - -/obj/structure/sign/directions/medical/cloning - name = "\improper Cloning Lab" - desc = "A direction sign, pointing out the way to the Cloning Lab." - icon_state = "direction_cloning" - -/obj/structure/sign/levels/medical/cloning - name = "\improper Cloning Lab" - desc = "A level sign, stating the level to find the Cloning Lab on." - icon_state = "level_cloning" - -/obj/structure/sign/directions/medical/resleeving - name = "\improper Resleeving Lab" - desc = "A direction sign, pointing out the way to the Resleeving Lab." - icon_state = "direction_resleeve" - -/obj/structure/sign/levels/medical/resleeving - name = "\improper Resleeving Lab" - desc = "A level sign, stating the level to find the Resleeving Lab on." - icon_state = "level_resleeve" - -//special signs -/obj/structure/sign/directions/evac - name = "\improper Evacuation" - desc = "A direction sign, pointing out the way to the Escape Shuttle Dock." - icon_state = "direction_evac" - -/obj/structure/sign/levels/evac - name = "\improper Evacuation" - desc = "A level sign, stating the level to find the Escape Shuttle Dock on." - icon_state = "level_evac" - -/obj/structure/sign/directions/eva - name = "\improper Extra-Vehicular Activity" - desc = "A direction sign, pointing out the way to the EVA Bay." - icon_state = "direction_eva" - -/obj/structure/sign/levels/eva - name = "\improper Extra-Vehicular Activity" - desc = "A level sign, stating the level to find the EVA Bay on." - icon_state = "level_eva" - -//command signs -/obj/structure/sign/directions/ai_core - name = "\improper AI Core" - desc = "A direction sign, pointing out the way to the AI Core." - icon_state = "direction_ai_core" - -/obj/structure/sign/levels/ai_core - name = "\improper AI Core" - desc = "A level sign, stating the level to find the AI Core on." - icon_state = "level_ai_core" - -/obj/structure/sign/directions/bridge - name = "\improper Bridge" - desc = "A direction sign, pointing out the way to the Bridge." - icon_state = "direction_bridge" - -/obj/structure/sign/levels/bridge - name = "\improper Bridge" - desc = "A level sign, stating the level to find the Bridge on." - icon_state = "level_bridge" - -/obj/structure/sign/directions/command - name = "\improper Command" - desc = "A direction sign, pointing out the way to the Command Center." - icon_state = "direction_command" - -/obj/structure/sign/levels/command - name = "\improper Command" - desc = "A level sign, stating the level to find the Command Center on." - icon_state = "level_command" - -/obj/structure/sign/directions/teleporter - name = "\improper Teleporter" - desc = "A direction sign, pointing out the way to the Teleporter." - icon_state = "direction_teleport" - -/obj/structure/sign/levels/teleporter - name = "\improper Teleporter" - desc = "A level sign, stating the level to find the Teleporter on." - icon_state = "level_teleport" - -/obj/structure/sign/directions/telecomms - name = "\improper Telecommunications Hub" - desc = "A direction sign, pointing out the way to the Telecommunications Hub." - icon_state = "direction_tcomms" - -/obj/structure/sign/levels/telecomms - name = "\improper Telecommunications Hub" - desc = "A level sign, stating the level to find the Telecommunications Hub on." - icon_state = "level_tcomms" - -//cargonia signs -/obj/structure/sign/directions/cargo - name = "\improper Cargo Department" - desc = "A direction sign, pointing out the way to the Cargo Department." - icon_state = "direction_crg" - -/obj/structure/sign/levels/cargo - name = "\improper Cargo Department" - desc = "A level sign, stating the level to find the Cargo Department on." - icon_state = "level_crg" - -/obj/structure/sign/directions/cargo/mining - name = "\improper Mining Department" - desc = "A direction sign, pointing out the way to the Mining Department." - icon_state = "direction_mining" - -/obj/structure/sign/levels/cargo/mining - name = "\improper Mining Department" - desc = "A level sign, stating the level to find the Mining Department on." - icon_state = "level_mining" - -/obj/structure/sign/directions/cargo/refinery - name = "\improper Refinery" - desc = "A direction sign, pointing out the way to the Refinery." - icon_state = "direction_refinery" - -/obj/structure/sign/levels/cargo/refinery - name = "\improper Refinery" - desc = "A level sign, stating the level to find the Refinery on." - icon_state = "level_refinery" - -//civilian/misc signs -/obj/structure/sign/directions/roomnum - name = "room number" - desc = "A sign detailing the number of the room beside it." - icon_state = "roomnum" - -/obj/structure/sign/directions/cryo - name = "\improper Cryogenic Storage" - desc = "A direction sign, pointing out the way to Cryogenic Storage." - icon_state = "direction_cry" - -/obj/structure/sign/levels/cryo - name = "\improper Cryogenic Storage" - desc = "A level sign, stating the level to find Cryogenic Storage on." - icon_state = "level_cry" - -/obj/structure/sign/directions/elevator - name = "\improper Elevator" - desc = "A direction sign, pointing out the way to the nearest elevator." - icon_state = "direction_elv" - -/obj/structure/sign/levels/elevator - name = "\improper Elevator" - desc = "A level sign, stating the level to find the nearest elevator on." - icon_state = "level_elv" - -/obj/structure/sign/directions/bar - name = "\improper Bar" - desc = "A direction sign, pointing out the way to the nearest watering hole." - icon_state = "direction_bar" - -/obj/structure/sign/levels/bar - name = "\improper Bar" - desc = "A level sign, stating the level to find the nearest watering hole on." - icon_state = "level_bar" - -/obj/structure/sign/directions/kitchen - name = "\improper Kitchen" - desc = "A pictographic direction sign with a knife, plate, and fork, pointing out the way to the nearest dining establishment." - icon_state = "direction_kitchen" - -/obj/structure/sign/levels/kitchen - name = "\improper Kitchen" - desc = "A pictographic direction sign with a knife, plate, and fork, stating the level to find the nearest dining establishment on." - icon_state = "level_kitchen" - -/obj/structure/sign/directions/shuttle_bay - name = "\improper Shuttle Bay" - desc = "A direction sign, pointing out the way to the nearest shuttle bay." - icon_state = "direction_bay" - -/obj/structure/sign/levels/shuttle_bay - name = "\improper Shuttle Bay" - desc = "A direction sign, stating the level to find the nearest shuttle bay on." - icon_state = "level_bay" - -/obj/structure/sign/directions/tram - name = "\improper Public Transit Station" - desc = "A direction sign, pointing out the way to the nearest public transit station." - icon_state = "direction_tram" - -/obj/structure/sign/levels/tram - name = "\improper Public Transit Station" - desc = "A level sign, stating the level to find the nearest public transit station on." - icon_state = "level_tram" - -/obj/structure/sign/directions/janitor - name = "\improper Custodial Closet" - desc = "A direction sign, pointing out the way to the Custodial Closet." - icon_state = "direction_janitor" - -/obj/structure/sign/levels/janitor - name = "\improper Custodial Closet" - desc = "A level sign, stating the level to find the Custodial Closet on." - icon_state = "level_janitor" - -/obj/structure/sign/directions/chapel - name = "\improper Chapel" - desc = "A direction sign, pointing out the way to the Chapel." - icon_state = "direction_chapel" - -/obj/structure/sign/levels/chapel - name = "\improper Chapel" - desc = "A level sign, stating the level to find the Chapel on." - icon_state = "level_chapel" - -/obj/structure/sign/directions/dorms - name = "\improper Dormitories" - desc = "A direction sign, pointing out the way to the Dormitories." - icon_state = "direction_dorms" - -/obj/structure/sign/levels/dorms - name = "\improper Dormitories" - desc = "A level sign, stating the level to find the Dormitories on." - icon_state = "level_dorms" - -/obj/structure/sign/directions/library - name = "\improper Library" - desc = "A direction sign, pointing out the way to the Library." - icon_state = "direction_library" - -/obj/structure/sign/levels/library - name = "\improper Library" - desc = "A level sign, stating the level to find the Library on." - icon_state = "level_library" - -/obj/structure/sign/directions/dock - name = "\improper Dock" - desc = "A direction sign, pointing out the way to the nearest docking area." - icon_state = "direction_dock" - -/obj/structure/sign/levels/dock - name = "\improper Dock" - desc = "A level sign, stating the level to find the nearest docking area on." - icon_state = "level_dock" - -/obj/structure/sign/directions/gym - name = "\improper Gym" - desc = "A direction sign, pointing out the way to the Gym." - icon_state = "direction_gym" - -/obj/structure/sign/levels/gym - name = "\improper Gym" - desc = "A level sign, stating the level to find the Gym on." - icon_state = "level_gym" - -/obj/structure/sign/directions/pool - name = "\improper Pool" - desc = "A direction sign, pointing out the way to the Pool." - icon_state = "direction_pool" - -/obj/structure/sign/levels/pool - name = "\improper Pool" - desc = "A level sign, stating the level to find the Pool on." - icon_state = "level_pool" - -/obj/structure/sign/directions/recreation - name = "\improper Recreation Area" - desc = "A direction sign, pointing out the way to the nearest Recreation Area." - icon_state = "direction_recreation" - -/obj/structure/sign/levels/recreation - name = "\improper Recreation Area" - desc = "A level sign, stating the level to find the nearest Recreation Area on." - icon_state = "level_recreation" - -/obj/structure/sign/directions/stairwell - name = "\improper Stairwell" - desc = "A direction sign with stairs and a door, pointing out the way to the nearest stairwell." - icon_state = "stairwell" - -/obj/structure/sign/directions/stairs_up - name = "\improper Stairs Up" - desc = "A direction sign with stairs and an upward-slanted arrow, pointing out the way to the nearest set of stairs that go up." - icon_state = "stairs_up" - -/obj/structure/sign/directions/stairs_down - name = "\improper Stairs Down" - desc = "A direction sign with stairs and a downward-slanted arrow, pointing out the way to the nearest set of stairs that go down." - icon_state = "stairs_down" - -/obj/structure/sign/directions/ladderwell - name = "\improper Access Shaft" - desc = "A direction sign with a ladder and a door, pointing out the way to the nearest access shaft." - icon_state = "ladderwell" - -/obj/structure/sign/directions/ladder_up - name = "\improper Ladder Up" - desc = "A direction sign with a ladder and an upward arrow, pointing out the way to the nearest ladder that goes up." - icon_state = "ladder_up" - -/obj/structure/sign/directions/ladder_down - name = "\improper Ladder Down" - desc = "A direction sign with a ladder and a downward arrow, pointing out the way to the nearest ladder that goes down." - icon_state = "ladder_down" - -/obj/structure/sign/directions/exit - name = "\improper Emergency Exit" - desc = "A lurid green sign that unmistakably identifies that the door it's next to as an emergency exit route." - icon_state = "exit_sign" - -//OTHER STUFF -/obj/structure/sign/christmas/lights - name = "Christmas lights" - desc = "Flashy and pretty." - icon = 'icons/obj/christmas.dmi' - icon_state = "xmaslights" - layer = 4.9 - plane = PLANE_LIGHTING_ABOVE - -/obj/structure/sign/christmas/wreath - name = "wreath" - desc = "Prickly and festive." - icon = 'icons/obj/christmas.dmi' - icon_state = "doorwreath" - layer = 5 - -/obj/structure/sign/hostilefauna - icon = 'icons/obj/decals_vr.dmi' - name = "\improper Caution: Hostile fauna" - desc = "This sign warns of hostile life forms in the area." - icon_state = "h_fauna" - -/obj/structure/sign/graffiti/pisoff - icon = 'icons/obj/decals_vr.dmi' - name = "\improper PIS OFF" - desc = "This sign bears some rather rude looking graffiti instructing you to PIS OFF." - icon_state = "pisoff" - -//Eris signs - -/obj/structure/sign/ironhammer - icon = 'icons/obj/decals_vr.dmi' - name = "Ironhammer Security" - desc = "Sign depicts the symbolic of Ironhammer Security, the largest security provider within Trade Union of Hansa." - icon_state = "ironhammer" - -/obj/structure/sign/atmos_co2 - icon = 'icons/obj/decals_vr.dmi' - name = "CO2 warning sign" - desc = "WARNING! CO2 flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_co2" - -/obj/structure/sign/atmos_n2o - icon = 'icons/obj/decals_vr.dmi' - name = "N2O warning sign" - desc = "WARNING! N2O flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_n2o" - -/obj/structure/sign/atmos_plasma - icon = 'icons/obj/decals_vr.dmi' - name = "Plasma warning sign" - desc = "WARNING! Plasma flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_plasma" - -/obj/structure/sign/atmos_n2 - icon = 'icons/obj/decals_vr.dmi' - name = "N2 warning sign" - desc = "WARNING! N2 flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_n2" - -/obj/structure/sign/atmos_o2 - icon = 'icons/obj/decals_vr.dmi' - name = "O2 warning sign" - desc = "WARNING! O2 flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_o2" - -/obj/structure/sign/atmos_air - icon = 'icons/obj/decals_vr.dmi' - name = "Air warning sign" - desc = "WARNING! Air flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_air" - -/obj/structure/sign/atmos_waste - icon = 'icons/obj/decals_vr.dmi' - name = "Atmos waste warning sign" - desc = "WARNING! Waste flow tube. Ensure the flow is disengaged before working." - icon_state = "atmos_waste" - -/obj/structure/sign/deck1 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'DECK I'." - name = "DECK I" - icon_state = "deck1" - -/obj/structure/sign/deck2 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'DECK II'." - name = "DECK II" - icon_state = "deck2" - -/obj/structure/sign/deck3 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'DECK III'." - name = "DECK III" - icon_state = "deck3" - -/obj/structure/sign/deck4 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'DECK IV'." - name = "DECK IV" - icon_state = "deck4" - -/obj/structure/sign/sec1 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'SECTION I'." - name = "SECTION I" - icon_state = "sec1" - -/obj/structure/sign/sec2 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'SECTION II'." - name = "SECTION II" - icon_state = "sec2" - -/obj/structure/sign/sec3 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'SECTION III'." - name = "SECTION III" - icon_state = "sec3" - -/obj/structure/sign/sec4 - icon = 'icons/obj/decals_vr.dmi' - desc = "A silver sign which reads 'SECTION IV'." - name = "SECTION IV" - icon_state = "sec4" - -/obj/structure/sign/nanotrasen - icon = 'icons/obj/decals_vr.dmi' - name = "\improper NanoTrasen" - desc = "An old metal sign which reads 'NanoTrasen'." - icon_state = "NT" - catalogue_data = list(/datum/category_item/catalogue/information/organization/nanotrasen) - -// Eris standards compliant hazards -/obj/structure/sign/signnew - icon = 'icons/obj/decals_vr.dmi' - -/obj/structure/sign/signnew/biohazard - name = "BIOLOGICAL HAZARD" - desc = "Warning: Biological and-or toxic hazards present in this area!" - icon_state = "biohazard" - -/obj/structure/sign/signnew/corrosives - name = "CORROSIVE SUBSTANCES" - desc = "Warning: Corrosive substances prezent in this area!" - icon_state = "corrosives" - -/obj/structure/sign/signnew/explosives - name = "EXPLOSIVE SUBSTANCES" - desc = "Warning: Explosive substances present in this area!" - icon_state = "explosives" - -/obj/structure/sign/signnew/flammables - name = "FLAMMABLE SUBSTANCES" - desc = "Warning: Flammable substances present in this area!" - icon_state = "flammable" - -/obj/structure/sign/signnew/laserhazard - name = "LASER HAZARD" - desc = "Warning: High powered laser emitters operating in this area!" - icon_state = "laser" - -/obj/structure/sign/signnew/danger - name = "DANGEROUS AREA" - desc = "Warning: Generally hazardous area! Exercise caution." - icon_state = "danger" - -/obj/structure/sign/signnew/magnetics - name = "MAGNETIC FIELD HAZARD" - desc = "Warning: Extremely powerful magnetic fields present in this area!" - icon_state = "magnetics" - -/obj/structure/sign/signnew/opticals - name = "OPTICAL HAZARD" - desc = "Warning: Optical hazards present in this area!" - icon_state = "optical" - -/obj/structure/sign/signnew/radiation - name = "RADIATION HAZARD" - desc = "Warning: Significant levels of radiation present in this area!" - icon_state = "radiation" - -/obj/structure/sign/signnew/secure - name = "SECURE AREA" - desc = "Warning: Secure Area! Do not enter without authorization!" - icon_state = "secure" - -/obj/structure/sign/signnew/electrical - name = "ELECTRICAL HAZARD" - desc = "Warning: Electrical hazards! Wear protective equipment." - icon_state = "electrical" - -/obj/structure/sign/signnew/cryogenics - name = "CRYOGENIC TEMPERATURES" - desc = "Warning: Extremely low temperatures in this area." - icon_state = "cryogenics" - -/obj/structure/sign/signnew/canisters - name = "PRESSURIZED CANISTERS" - desc = "Warning: Highly pressurized canister storage." - icon_state = "canisters" - -/obj/structure/sign/signnew/oxidants - name = "OXIDIZING AGENTS" - desc = "Warning: Oxidizing agents in this area, do not start fires!" - icon_state = "oxidants" - -/obj/structure/sign/signnew/memetic - name = "MEMETIC HAZARD" - desc = "Warning: Memetic hazard, wear meson goggles!" - icon_state = "memetic" - -//Eris departments - -/obj/structure/sign/department - icon = 'icons/obj/decals_vr.dmi' - name = "department sign" - desc = "Sign of some important ship compartment." - -/obj/structure/sign/department/medbay - name = "MEDBAY" - icon_state = "medbay" - -/obj/structure/sign/department/virology - name = "VIROLOGY" - icon_state = "virology" - -/obj/structure/sign/department/chem - name = "CHEMISTRY" - icon_state = "chem" - -/obj/structure/sign/department/gene - name = "GENETICS" - icon_state = "gene" - -/obj/structure/sign/department/morgue - name = "MORGUE" - icon_state = "morgue" - -/obj/structure/sign/department/operational - name = "SURGERY" - icon_state = "operational" - -/obj/structure/sign/department/sci - name = "SCIENCE" - icon_state = "sci" - -/obj/structure/sign/department/xenolab - name = "XENOLAB" - icon_state = "xenolab" - -/obj/structure/sign/department/anomaly - name = "ANOMALYLAB" - icon_state = "anomaly" - -/obj/structure/sign/department/dock - name = "DOKUCHAYEV DOCK" - icon_state = "dock" - -/obj/structure/sign/department/rnd - name = "RND" - icon_state = "rnd" - -/obj/structure/sign/department/robo - name = "ROBOTICS" - icon_state = "robo" - -/obj/structure/sign/department/toxins - name = "TOXINS" - icon_state = "toxins" - -/obj/structure/sign/department/toxin_res - name = "TOXINLAB" - icon_state = "toxin_res" - -/obj/structure/sign/department/eva - name = "E.V.A." - icon_state = "eva" - -/obj/structure/sign/department/ass - name = "TOOL STORAGE" - icon_state = "ass" - -/obj/structure/sign/department/bar - name = "BAR" - icon_state = "bar" - -/obj/structure/sign/department/biblio - name = "LIBRARY" - icon_state = "biblio" - -/obj/structure/sign/department/chapel - name = "CHAPEL" - icon_state = "chapel" - -/obj/structure/sign/department/bridge - name = "BRIDGE" - icon_state = "bridge" - -/obj/structure/sign/department/telecoms - name = "TELECOMS" - icon_state = "telecoms" - -/obj/structure/sign/department/conference_room - name = "CONFERENCE" - icon_state = "conference_room" - -/obj/structure/sign/department/ai - name = "AI" - icon_state = "ai" - -/obj/structure/sign/department/cargo - name = "CARGO" - icon_state = "cargo" - -/obj/structure/sign/department/mail - name = "MAIL" - icon_state = "mail" - -/obj/structure/sign/department/miner_dock - name = "MINING DOCK" - icon_state = "miner_dock" - -/obj/structure/sign/department/cargo_dock - name = "CARGO DOCK" - icon_state = "cargo_dock" - -/obj/structure/sign/department/eng - name = "ENGINEERING" - icon_state = "eng" - -/obj/structure/sign/department/engine - name = "ENGINE" - icon_state = "engine" - -/obj/structure/sign/department/gravi - name = "GRAVGEN" - icon_state = "gravi" - -/obj/structure/sign/department/atmos - name = "ATMOSPHERICS" - icon_state = "atmos" - -/obj/structure/sign/department/shield - name = "SHIELDGEN" - icon_state = "shield" - -/obj/structure/sign/department/drones - name = "DRONES" - icon_state = "drones" - -/obj/structure/sign/department/interrogation - name = "INTERROGATION" - icon_state = "interrogation" - -/obj/structure/sign/department/commander - name = "COMMANDER" - icon_state = "commander" - -/obj/structure/sign/department/armory - name = "ARMORY" - icon_state = "armory" - -/obj/structure/sign/department/prison - name = "PRISON" - icon_state = "prison" - -/obj/structure/sign/deck/first - name = "\improper First Deck" - icon_state = "deck-1" - -/obj/structure/sign/deck/second - name = "\improper Second Deck" - icon_state = "deck-2" - -/obj/structure/sign/deck/third - name = "\improper Third Deck" - icon_state = "deck-3" - -/obj/structure/sign/deck/fourth - name = "\improper Fourth Deck" - icon_state = "deck-4" - -/obj/structure/sign/level/one - name = "\improper Level One" - icon_state = "level-1" - -/obj/structure/sign/level/one/large - icon_state = "level-1-large" - -/obj/structure/sign/level/two - name = "\improper Level Two" - icon_state = "level-2" - -/obj/structure/sign/level/two/large - icon_state = "level-2-large" - -/obj/structure/sign/level/three - name = "\improper Level Three" - icon_state = "level-3" - -/obj/structure/sign/level/three/large - icon_state = "level-3-large" - -/obj/structure/sign/level/fourth - name = "\improper Level Four" - icon_state = "level-4" - -/obj/structure/sign/level/four/large - icon_state = "level-4-large" - -/obj/structure/sign/level/basement - name = "\improper Basement Level" - icon_state = "level-b" - -/obj/structure/sign/level/basement/large - icon_state = "level-b-large" - -/obj/structure/sign/level/ground - name = "\improper Ground Level" - icon_state = "level-g" - -/obj/structure/sign/level/ground/large - icon_state = "level-g-large" - -/obj/structure/sign/hangar/one - name = "\improper Hangar One" - icon_state = "hangar-1" - -/obj/structure/sign/hangar/two - name = "\improper Hangar Two" - icon_state = "hangar-2" - -/obj/structure/sign/hangar/three - name = "\improper Hangar Three" - icon_state = "hangar-3" - -/obj/structure/sign/atmos - name = "\improper WASTE" - icon_state = "atmos_waste" - -/obj/structure/sign/atmos/o2 - name = "\improper OXYGEN" - icon_state = "atmos_o2" - -/obj/structure/sign/atmos/co2 - name = "\improper CARBON DIOXIDE" - icon_state = "atmos_co2" - -/obj/structure/sign/atmos/phoron - name = "\improper PHORON" - icon_state = "atmos_phoron" - -/obj/structure/sign/atmos/n2o - name = "\improper NITROUS OXIDE" - icon_state = "atmos_n2o" - -/obj/structure/sign/atmos/n2 - name = "\improper NITROGEN" - icon_state = "atmos_n2" - -/obj/structure/sign/atmos/air - name = "\improper AIR" - icon_state = "atmos_air" - -/obj/structure/sign/scenery/engineleft - name = "I.C.V." - desc = "The charred name of a cargo ship of some description." - icon_state = "poi_engine1" - -/obj/structure/sign/scenery/engineright - name = "I.C.V." - desc = "The charred name of a cargo ship of some description." - icon_state = "poi_engine2" - -//Direction/Level sign overlays. Not mechanically functional as noted above, but usable for mapping. -/obj/structure/sign/scenery/overlay - name = "snow covering" - desc = "Frozen snow obscures the view of a sign beneath." - icon_state = "snowy" - icon = 'icons/obj/decals_directions.dmi' - -/obj/structure/sign/scenery/overlay/rust - name = "rust covering" - desc = "Thick rust obscures the view of a sign beneath." - icon_state = "rusted" - -/obj/structure/sign/scenery/overlay/vine - name = "vine covering" - desc = "Thick vines obscure the view of a sign beneath." - icon_state = "vines" - -/obj/structure/sign/scenery/overlay/vine/top - icon_state = "vines_top" - -/obj/structure/sign/scenery/overlay/vine/mid - icon_state = "vines_mid" - -/obj/structure/sign/scenery/overlay/vine/bottom - icon_state = "vines_bottom" - -/obj/structure/sign/bigname - name = "Cynosure Station" - desc = "An aging sign for the Cynosure Xenoarchaeological Research Station." - icon_state = "cyno_1" - -/obj/structure/sign/bigname/seg_2 - icon_state = "cyno_2" - -/obj/structure/sign/bigname/seg_3 - icon_state = "cyno_3" - -/obj/structure/sign/bigname/seg_4 - icon_state = "cyno_4" - -/obj/structure/sign/bigname/seg_5 - icon_state = "cyno_5" - -/obj/structure/sign/bigname/seg_6 - icon_state = "cyno_6" - -/obj/structure/sign/bigname/seg_7 - icon_state = "cyno_7" - -/obj/structure/sign/clock - name = "wall clock" - desc = "A basic wall clock, synced to the current system time." - icon_state = "clock" - -/obj/structure/sign/clock/examine(mob/user) - . = ..() - . += "The clock shows that the time is [stationtime2text()]." - -/obj/structure/sign/calendar - name = "calendar" - desc = "It's an old-school, NanoTrasen branded wall calendar. Sure, it might be obsolete with modern technology, but it's still hard to imagine an office without one." - icon_state = "calendar" - -/obj/structure/sign/calendar/examine(mob/user) - . = ..() - . += "The calendar shows that the date is [stationdate2text()]." - if (Holiday.len) - . += "Today is [english_list(Holiday)]." - -/obj/structure/sign/explosive - name = "\improper HIGH EXPLOSIVES sign" - desc = "A warning sign which reads 'HIGH EXPLOSIVES'." - icon_state = "explosives" - -/obj/structure/sign/chemdiamond - name = "\improper HAZARDOUS CHEMICALS sign" - desc = "A sign that warns of potentially hazardous chemicals nearby, indicating health risk, flash point, and reactivity." - icon_state = "chemdiamond" - -//Here be Flags - -//Flag item -/obj/item/flag - name = "boxed flag" - desc = "A flag neatly folded into a wooden container." - icon = 'icons/obj/flags.dmi' - icon_state = "flag_boxed" - var/flag_path = "flag" - var/flag_size = 0 - -//Flag on wall -/obj/structure/sign/flag - name = "blank flag" - desc = "Nothing to see here." - icon = 'icons/obj/flags.dmi' - icon_state = "flag" - var/icon/ripped_outline = icon('icons/obj/flags.dmi', "ripped") - var/obj/structure/sign/flag/linked_flag //For double flags - var/obj/item/flag/flagtype //For returning your flag - var/ripped = FALSE //If we've been torn down - -/obj/structure/sign/flag/blank - name = "blank banner" - desc = "A blank white flag." - icon_state = "flag" - flagtype = /obj/item/flag - -/obj/item/flag/afterattack(var/atom/A, var/mob/user, var/adjacent, var/clickparams) - if (!adjacent) - return - - if((!iswall(A) && !istype(A, /obj/structure/window)) || !isturf(user.loc)) - to_chat(user, SPAN_WARNING("You can't place this here!")) - return - - var/placement_dir = get_dir(user, A) - if (!(placement_dir in cardinal)) - to_chat(user, SPAN_WARNING("You must stand directly in front of the location you wish to place that on.")) - return - - var/obj/structure/sign/flag/P = new(user.loc) - - switch(placement_dir) - if(NORTH) - P.pixel_y = 32 - if(SOUTH) - P.pixel_y = -32 - if(EAST) - P.pixel_x = 32 - if(WEST) - P.pixel_x = -32 - - P.dir = placement_dir - if(flag_size) - P.icon_state = "[flag_path]_l" - var/obj/structure/sign/flag/P2 = new(user.loc) - P.linked_flag = P2 - P2.linked_flag = P - P2.icon_state = "[flag_path]_r" - P2.dir = P.dir - switch(P2.dir) - if(NORTH) - P2.pixel_y = P.pixel_y - P2.pixel_x = 32 - if(SOUTH) - P2.pixel_y = P.pixel_y - P2.pixel_x = 32 - if(EAST) - P2.pixel_x = P.pixel_x - P2.pixel_y = -32 - if(WEST) - P2.pixel_x = P.pixel_x - P2.pixel_y = 32 - P2.name = name - P2.desc = desc - P2.description_info = description_info - P2.description_fluff = description_fluff - P2.flagtype = type - else - P.icon_state = "[flag_path]" - P.name = name - P.desc = desc - P.description_info = description_info - P.description_fluff = description_fluff - P.flagtype = type - qdel(src) - -/obj/structure/sign/flag/Destroy() - if(linked_flag?.linked_flag == src) //Catches other instances where one half might be destroyed, say by a broken wall, to avoid runtimes. - linked_flag.linked_flag = null //linked_flag - . = ..() - -/obj/structure/sign/flag/ex_act(severity) - switch(severity) - if(1) - qdel(src) - if(2) - if(prob(50)) - qdel(src) - else - rip() - if(3) - rip() - -/obj/structure/sign/flag/unfasten(mob/user) - if(!ripped) - user.visible_message(SPAN_NOTICE("\The [user] unfastens \the [src] and folds it back up."), SPAN_NOTICE("You unfasten \the [src] and fold it back up.")) - var/obj/item/flag/F = new flagtype(get_turf(user)) - user.put_in_hands(F) - else - user.visible_message(SPAN_NOTICE("\The [user] unfastens the tattered remnants of \the [src]."), SPAN_NOTICE("You unfasten the tattered remains of \the [src].")) - if(linked_flag) - qdel(linked_flag) //otherwise you're going to get weird duping nonsense - qdel(src) - -/obj/structure/sign/flag/attack_hand(mob/user) - if(alert("Do you want to rip \the [src] from its place?","You think...","Yes","No") == "Yes") - if(!Adjacent(user)) //Cannot bring up dialogue and walk away - return FALSE - visible_message(SPAN_WARNING("\The [user] rips \the [src] in a single, decisive motion!" )) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 100, 1) - add_fingerprint(user) - rip() - -/obj/structure/sign/flag/proc/rip(var/rip_linked = TRUE) - var/icon/I = new('icons/obj/flags.dmi', icon_state) - var/icon/mask = new('icons/obj/flags.dmi', "ripped") - I.AddAlphaMask(mask) - icon = I - name = "ripped flag" - desc = "You can't make out anything from the flag's original print. It's ruined." - ripped = TRUE - if(linked_flag && rip_linked) - linked_flag.rip(FALSE) //Prevents an infinite ripping loop - -/obj/structure/sign/flag/attackby(obj/item/W, mob/user) - ..() - if(istype(W, /obj/item/weapon/flame/lighter) || istype(W, /obj/item/weapon/weldingtool)) - visible_message(SPAN_WARNING("\The [user] starts to burn \the [src] down!")) - if(!do_after(user, 2 SECONDS)) - return FALSE - visible_message(SPAN_WARNING("\The [user] burns \the [src] down!")) - playsound(src.loc, 'sound/items/cigs_lighters/cig_light.ogg', 100, 1) - new /obj/effect/decal/cleanable/ash(src.loc) - if(linked_flag) - qdel(linked_flag) - qdel(src) - return TRUE - -/obj/structure/sign/flag/blank/left - icon_state = "flag_l" - -/obj/structure/sign/flag/blank/right - icon_state = "flag_r" - -//SolGov -/obj/structure/sign/flag/sol - name = "Solar Confederate Government flag" - desc = "The bright blue flag of the Solar Confederate Government." - icon_state = "solgov" - flagtype = /obj/item/flag/sol - -/obj/structure/sign/flag/sol/left - icon_state = "solgov_l" - -/obj/structure/sign/flag/sol/right - icon_state = "solgov_r" - -/obj/item/flag/sol - name = "Solar Confederate Government flag" - desc = "The bright blue flag of the Solar Confederate Government." - flag_path = "solgov" - -/obj/item/flag/sol/l - name = "large Solar Confederate Government flag" - flag_size = 1 - -//NanoTrasen -/obj/structure/sign/flag/nt - name = "NanoTrasen corporate flag" - desc = "A flag portraying the logo of the NanoTrasen corporation." - icon_state = "nanotrasen" - flagtype = /obj/item/flag/nt - -/obj/structure/sign/flag/nt/left - icon_state = "nanotrasen_l" - -/obj/structure/sign/flag/nt/right - icon_state = "nanotrasen_r" - -/obj/item/flag/nt - name = "NanoTrasen corporate flag" - desc = "A flag portraying the logo of the NanoTrasen corporation." - flag_path = "nanotrasen" - -/obj/item/flag/nt/l - name = "large NanoTrasen corporate flag" - flag_size = 1 - -//Vir -/obj/structure/sign/flag/vir - name = "Vir Governmental Authority flag" - desc = "The two-tone flag of the Vir Governmental Authority." - description_fluff = "Commonly referred to as VirGov, the Vir Governmental Authority was formed in 2412 as a unified system government following \ - a half century of war between the Sif Planetary Government - or SifGov - and corporate interests in Kara orbit, in order to qualify for full membership \ - in the Solar Confederate Government. Following the Karan Wars it would be almost a century before Trans-Stellar Corporations were allowed their typical \ - freedom to operate unobstructed in the Vir system." - icon_state = "vir" - flagtype = /obj/item/flag/vir - -/obj/structure/sign/flag/vir/left - icon_state = "vir_l" - -/obj/structure/sign/flag/vir/right - icon_state = "vir_r" - -/obj/item/flag/vir - name = "Vir Governmental Authority flag" - desc = "The two-tone flag of the Vir Governmental Authority." - description_fluff = "Commonly referred to as VirGov, the Vir Governmental Authority was formed in 2412 as a unified system government following \ - a half century of war between the Sif Planetary Government - or SifGov - and corporate interests in Kara orbit, in order to qualify for full membership \ - in the Solar Confederate Government. Following the Karan Wars it would be almost a century before Trans-Stellar Corporations were allowed their typical \ - freedom to operate unobstructed in the Vir system." - flag_path = "vir" - -/obj/item/flag/vir/l - name = "large Vir Governmental Authority flag" - flag_size = 1 - -//Almach Association - -/obj/structure/sign/flag/almach_a - name = "Almach Association flag" - desc = "The black and grey flag of the now-defunct Almach Association." - description_fluff = "The Almach Association was a short lived (February 2562 - April 2564) governmental entity formed as an alliance of disparate radical mercurial \ - states in an effort to secede from the Solar Confederate Government. Though the Association were defeated, and ultimately annexed by the Skrellian Far Kingdoms, \ - the Association flag remains a popular symbol with mercurials and secessionists alike. To some, the Almach Association is seen as the first step towards the SCG's \ - \"inevitable\" dissolution." - icon_state = "almach_a" - flagtype = /obj/item/flag/almach_a - -/obj/structure/sign/flag/almach_a/left - icon_state = "almach_a_l" - -/obj/structure/sign/flag/almach_a/right - icon_state = "almach_a_r" - -/obj/item/flag/almach_a - name = "Almach Association flag" - desc = "The black and grey flag of the now-defunct Almach Association." - description_fluff = "The Almach Association was a short lived (February 2562 - April 2564) governmental entity formed as an alliance of disparate radical mercurial \ - states in an effort to secede from the Solar Confederate Government. Though the Association were defeated, and ultimately annexed by the Skrellian Far Kingdoms, \ - the Association flag remains a popular symbol with mercurials and secessionists alike. To some, the Almach Association is seen as the first step towards the SCG's \ - \"inevitable\" dissolution." - flag_path = "almach_a" - -/obj/item/flag/almach_a/l - name = "large Almach Association flag" - flag_size = 1 - -//Almach Protectorate -/obj/structure/sign/flag/almach_p - name = "Almach Protectorate flag" - desc = "The purple flag of the Almach Protectorate." - description_fluff = "The Almach Protectorate was formed from the territory of the Almach Association as a condition of the 2564 Treaty of Whythe. \ - Intended to be closely overseen by the Skrellian Far Kingdom, the Skathari Incursion left the Protectorate functionally independent in many regards, \ - leading to the proliferation of previously restricted genetic modification technology into SolGov territory. However, the Protectorate was also left \ - without meaningful military support, and has suffered sorely in the years since as the Relan-led government has struggled to remilitarize." - icon_state = "almach_p" - flagtype = /obj/item/flag/almach_p - -/obj/structure/sign/flag/almach_p/left - icon_state = "almach_p_l" - -/obj/structure/sign/flag/almach_p/right - icon_state = "almach_p_r" - -/obj/item/flag/almach_p - name = "Almach Protectorate flag" - desc = "The purple flag of the Almach Protectorate." - description_fluff = "The Almach Protectorate was formed from the territory of the Almach Association as a condition of the 2564 Treaty of Whythe. \ - Intended to be closely overseen by the Skrellian Far Kingdom, the Skathari Incursion left the Protectorate functionally independent in many regards, \ - leading to the proliferation of previously restricted genetic modification technology into SolGov territory. However, the Protectorate was also left \ - without meaningful military support, and has suffered sorely in the years since as the Relan-led government has struggled to remilitarize." - flag_path = "almach_p" - -/obj/item/flag/almach_p/l - name = "large Almach Protectorate flag" - flag_size = 1 - -//Vystholm -/obj/structure/sign/flag/vystholm - name = "Vystholm flag" - desc = "The black and gold flag of Vystholm." - description_fluff = "Vystholm is a faction of xenophobic humans who constructed an ark ship, the VHS Rodnakya, in response to the abolition of the hostile \ - First Contact Policy in the early 24th century. Thought to have long departed known space, the Vystholm returned to within range of human territory in response \ - to the Skathari Incursion and has since undertaken a campaign of raiding, terrorism and espionage against established governments, particularly the Tajaran \ - Pearlshield Coalition." - icon_state = "vystholm" - flagtype = /obj/item/flag/vystholm - -/obj/structure/sign/flag/vystholm/left - icon_state = "vystholm_l" - -/obj/structure/sign/flag/vystholm/right - icon_state = "vystholm_r" - -/obj/item/flag/vystholm - name = "Vystholm flag" - desc = "The black and gold flag of Vystholm." - description_fluff = "Vystholm is a faction of xenophobic humans who constructed an ark ship, the VHS Rodnakya, in response to the abolition of the hostile \ - First Contact Policy in the early 24th century. Thought to have long departed known space, the Vystholm returned to within range of human territory in response \ - to the Skathari Incursion and has since undertaken a campaign of raiding, terrorism and espionage against established governments, particularly the Tajaran \ - Pearlshield Coalition." - flag_path = "vystholm" - -/obj/item/flag/vystholm/l - name = "large Vystholm flag" - flag_size = 1 - -//Five Arrows -/obj/structure/sign/flag/fivearrows - name = "Five Arrows flag" - desc = "The red flag of the Five Arrows." - description_fluff = "The Five Arrows is an independent government entity that seceded from the Solar Confederate Government in 2570, in response to perceived \ - failures in aiding the Sagittarius Heights during the Skathari Incursion. The success of the government in achieving effective local defense and prosperity has \ - since attracted the membership of Kauq'xum, a remote Skrellian colony. \The Five Arrows formed the model for SolGov's own semi-autonomous \"Regional Blocs\"." - icon_state = "fivearrows" - flagtype = /obj/item/flag/fivearrows - -/obj/structure/sign/flag/fivearrows/left - icon_state = "fivearrows_l" - -/obj/structure/sign/flag/fivearrows/right - icon_state = "fivearrows_r" - -/obj/item/flag/fivearrows - name = "Five Arrows flag" - desc = "The red flag of the Five Arrows." - description_fluff = "The Five Arrows is an independent government entity that seceded from the Solar Confederate Government in 2570, in response to perceived \ - failures in aiding the Sagittarius Heights during the Skathari Incursion. The success of the government in achieving effective local defense and prosperity has \ - since attracted the membership of Kauq'xum, a remote Skrellian colony. \The Five Arrows formed the model for SolGov's own semi-autonomous \"Regional Blocs\"." - flag_path = "fivearrows" - -/obj/item/flag/fivearrows/l - name = "large Five Arrows flag" - flag_size = 1 - -//Pirates -/obj/structure/sign/flag/pirate - name = "pirate flag" - desc = "Shiver me timbers, hoist the black!" - icon_state = "pirate" - flagtype = /obj/item/flag/pirate - -/obj/structure/sign/flag/pirate/left - icon_state = "pirate_l" - -/obj/structure/sign/flag/pirate/right - icon_state = "pirate_r" - -/obj/item/flag/pirate - name = "pirate flag" - desc = "Shiver me timbers, hoist the black!" - flag_path = "pirate" - - -/obj/item/flag/pirate/l - name = "large pirate flag" - flag_size = 1 - -//Catpirate -/obj/structure/sign/flag/catpirate - name = "Tajaran pirate flag" - desc = "Shiver me whiskers, hoist the black!" - icon_state = "catpirate" - flagtype = /obj/item/flag/catpirate - -/obj/structure/sign/flag/catpirate/left - icon_state = "catpirate_l" - -/obj/structure/sign/flag/catpirate/right - icon_state = "catpirate_r" - -/obj/item/flag/catpirate - name = "Tajaran pirate flag" - desc = "Shiver me whiskers, hoist the black!" - flag_path = "catpirate" - -/obj/item/flag/catpirate/l - name = "large Tajaran pirate flag" - flag_size = 1 - -//Political Parties -/obj/structure/sign/flag/icarus - name = "Icarus Front flag" - desc = "The flag of the right-populist Icarus Front political party." - icon_state = "icarus" - flagtype = /obj/item/flag/icarus - -/obj/item/flag/icarus - name = "Icarus Front flag" - desc = "The flag of the right-populist Icarus Front political party." - flag_path = "icarus" - -/obj/structure/sign/flag/shadowcoalition - name = "Shadow Coalition flag" - desc = "The flag of the neoliberal Shadow Coalition political party." - icon_state = "shadowcoalition" - flagtype = /obj/item/flag/shadowcoalition - -/obj/item/flag/shadowcoalition - name = "Shadow Coalition flag" - desc = "The flag of the neoliberal Shadow Coalition political party." - flag_path = "shadowcoalition" - -/obj/structure/sign/flag/seo - name = "Sol Economic Organization flag" - desc = "The flag of the protectionist Sol Economic Organization political party." - icon_state = "seo" - flagtype = /obj/item/flag/seo - -/obj/item/flag/seo - name = "Sol Economic Organization flag" - desc = "The flag of the protectionist Sol Economic Organization political party." - flag_path = "seo" - -/obj/structure/sign/flag/gap - name = "Galactic Autonomy Party flag" - desc = "The flag of the libertarian Galactic Autonomy Party political party." - icon_state = "gap" - flagtype = /obj/item/flag/seo - -/obj/item/flag/gap - name = "Galactic Autonomy Party flag" - desc = "The flag of the libertarian Galactic Autonomy Party political party." - flag_path = "gap" +/obj/structure/sign + icon = 'icons/obj/decals.dmi' + anchored = TRUE + opacity = 0 + density = FALSE + plane = OBJ_PLANE //VOREStation Edit + layer = ABOVE_JUNK_LAYER //VOREStation Edit + w_class = ITEMSIZE_NORMAL + +/obj/structure/sign/ex_act(severity) + qdel(src) + +/obj/structure/sign/attackby(obj/item/tool, mob/user) //deconstruction + if(tool.has_tool_quality(TOOL_SCREWDRIVER) && !istype(src, /obj/structure/sign/scenery) && !istype(src, /obj/structure/sign/double)) + playsound(src, tool.usesound, 50, 1) + unfasten(user) + else ..() + +/obj/structure/sign/proc/unfasten(mob/user) + user.visible_message(SPAN_NOTICE("\The [user] unfastens \the [src]."), SPAN_NOTICE("You unfasten \the [src].")) + var/obj/item/sign/S = new(src.loc) + S.name = name + S.desc = desc + S.icon_state = icon_state + S.sign_state = icon_state + S.original_type = type + qdel(src) + + +/obj/item/sign + name = "sign" + desc = "" + icon = 'icons/obj/decals.dmi' + w_class = ITEMSIZE_NORMAL //big + var/sign_state = "" + var/original_type + +/obj/item/sign/attackby(obj/item/tool as obj, mob/user as mob) //construction + if(tool.has_tool_quality(TOOL_SCREWDRIVER) && isturf(user.loc)) + var/direction = tgui_input_list(usr, "In which direction?", "Select direction.", list("North", "East", "South", "West", "Cancel")) + if(direction == "Cancel") return + var/target_type = original_type || /obj/structure/sign + var/obj/structure/sign/S = new target_type(user.loc) + switch(direction) + if("North") + S.pixel_y = 32 + if("East") + S.pixel_x = 32 + if("South") + S.pixel_y = -32 + if("West") + S.pixel_x = -32 + else return + S.name = name + S.desc = desc + S.icon_state = sign_state + to_chat(user, "You fasten \the [S] with your [tool].") + qdel(src) + else ..() + +/obj/structure/sign/scenery/map + name = "station map" + desc = "A framed picture of the station." + +/obj/structure/sign/scenery/map/left + icon_state = "map-left" + +/obj/structure/sign/scenery/map/right + icon_state = "map-right" + +/obj/structure/sign/securearea + name = "\improper SECURE AREA" + desc = "A warning sign which reads 'SECURE AREA'." + icon_state = "securearea" + +/obj/structure/sign/biohazard + name = "\improper BIOHAZARD" + desc = "A warning sign which reads 'BIOHAZARD'." + icon_state = "bio" + +/obj/structure/sign/electricshock + name = "\improper HIGH VOLTAGE" + desc = "A warning sign which reads 'HIGH VOLTAGE'." + icon_state = "shock" + +/obj/structure/sign/examroom + name = "\improper EXAM" + desc = "A guidance sign which reads 'EXAM ROOM'." + icon_state = "examroom" + +/obj/structure/sign/vacuum + name = "\improper HARD VACUUM AHEAD" + desc = "A warning sign which reads 'HARD VACUUM AHEAD'." + icon_state = "space" + +/obj/structure/sign/deathsposal + name = "\improper DISPOSAL LEADS TO SPACE" + desc = "A warning sign which reads 'DISPOSAL LEADS TO SPACE'." + icon_state = "deathsposal" + +/obj/structure/sign/pods + name = "\improper ESCAPE PODS" + desc = "A warning sign which reads 'ESCAPE PODS'." + icon_state = "pods" + +/obj/structure/sign/fire + name = "\improper DANGER: FIRE" + desc = "A warning sign which reads 'DANGER: FIRE'." + icon_state = "fire" + +/obj/structure/sign/nosmoking_1 + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'." + icon_state = "nosmoking" + +/obj/structure/sign/nosmoking_2 + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'." + icon_state = "nosmoking2" + +/obj/structure/sign/nosmoking_2/burnt + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'. It looks like someone didn't follow its advice..." + icon_state = "nosmoking2_burnt" + +/obj/structure/sign/warning + name = "\improper WARNING" + icon_state = "securearea" + +/obj/structure/sign/warning/Initialize() + . = ..() + desc = "A warning sign which reads '[name]'." + +/obj/structure/sign/warning/airlock + name = "\improper EXTERNAL AIRLOCK" + icon_state = "doors" + +/obj/structure/sign/warning/biohazard + name = "\improper BIOHAZARD" + icon_state = "bio" + +/obj/structure/sign/warning/bomb_range + name = "\improper BOMB RANGE" + icon_state = "blast" + +/obj/structure/sign/warning/caution + name = "\improper CAUTION" + +/obj/structure/sign/warning/compressed_gas + name = "\improper COMPRESSED GAS" + icon_state = "hikpa" + +/obj/structure/sign/warning/deathsposal + name = "\improper DISPOSAL LEADS TO SPACE" + icon_state = "deathsposal" + +/obj/structure/sign/warning/docking_area + name = "\improper KEEP CLEAR: DOCKING AREA" + +/obj/structure/sign/warning/evac + name = "\improper KEEP CLEAR: EVAC DOCKING AREA" + icon_state = "evac" + +/obj/structure/sign/warning/engineering_access + name = "\improper ENGINEERING ACCESS" + icon_state = "engine" + +/obj/structure/sign/warning/fire + name = "\improper DANGER: FIRE" + icon_state = "fire" + +/obj/structure/sign/warning/high_voltage + name = "\improper HIGH VOLTAGE" + icon_state = "shock" + +/obj/structure/sign/warning/hot_exhaust + name = "\improper HOT EXHAUST" + icon_state = "fire" + +/obj/structure/sign/warning/internals_required + name = "\improper INTERNALS REQUIRED" + +/obj/structure/sign/warning/lethal_turrets + name = "\improper LETHAL TURRETS" + icon_state = "turrets" + +/obj/structure/sign/warning/lethal_turrets/Initialize() + . = ..() + desc += " Enter at own risk!." + +/obj/structure/sign/warning/mail_delivery + name = "\improper MAIL DELIVERY" + icon_state = "mail" + +/obj/structure/sign/warning/moving_parts + name = "\improper MOVING PARTS" + icon_state = "movingparts" + +/obj/structure/sign/warning/nosmoking_1 + name = "\improper NO SMOKING" + icon_state = "nosmoking" + +/obj/structure/sign/warning/nosmoking_2 + name = "\improper NO SMOKING" + icon_state = "nosmoking2" + +/obj/structure/sign/warning/pods + name = "\improper ESCAPE PODS" + icon_state = "pods" + +/obj/structure/sign/warning/radioactive + name = "\improper RADIOACTIVE AREA" + icon_state = "radiation" + +/obj/structure/sign/warning/secure_area + name = "\improper SECURE AREA" + icon_state = "securearea2" + +/obj/structure/sign/warning/secure_area/armory + name = "\improper ARMORY" + icon_state = "armory" + +/obj/structure/sign/warning/server_room + name = "\improper SERVER ROOM" + icon_state = "server" + +/obj/structure/sign/warning/siphon_valve + name = "\improper SIPHON VALVE" + +/obj/structure/sign/warning/vacuum + name = "\improper HARD VACUUM AHEAD" + icon_state = "space" + +/obj/structure/sign/warning/vent_port + name = "\improper EJECTION/VENTING PORT" + +/obj/structure/sign/warning/emergence + name = "\improper EMERGENT INTELLIGENCE DETAILS" + icon_state = "rogueai" + +/obj/structure/sign/warning/falling + name = "\improper FALL HAZARD" + icon_state = "falling" + +/obj/structure/sign/warning/lava + name = "\improper MOLTEN SURFACE" + icon_state = "lava" + +/obj/structure/sign/warning/acid + name = "\improper ACIDIC SURFACE" + icon_state = "acid" + +/obj/structure/sign/warning/cold + name = "\improper EXTREME COLD ENVIRONMENT" + icon_state = "cold" + +/obj/structure/sign/redcross + name = "medbay" + desc = "An interstellar symbol of medical institutions. You'll probably get help here." + icon_state = "bluecross" + +/obj/structure/sign/greencross + name = "medbay" + desc = "An interstellar symbol of medical institutions. You'll probably get help here." + icon_state = "bluecross2" + +/obj/structure/sign/goldenplaque + name = "The Most Robust Men Award for Robustness" + desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." + icon_state = "goldenplaque" + +/obj/structure/sign/kiddieplaque + name = "\improper AI developers plaque" + desc = "Next to the extremely long list of names and job titles. Beneath the image, someone has scratched the word \"PACKETS\"" + icon_state = "kiddieplaque" + +/obj/structure/sign/atmosplaque + name = "\improper FEA atmospherics division plaque" + desc = "This plaque commemorates the fall of the Atmos FEA division. For all the charred, dizzy, and brittle men who have died in its hands." + icon_state = "atmosplaque" + +/obj/structure/sign/periodic + name = "periodic table" + desc = "A sign reminding those visiting of the elements of the periodic table- though, they should have memorized them by now." + icon_state = "periodic" + +/obj/structure/sign/double/maltesefalcon //The sign is 64x32, so it needs two tiles. ;3 + name = "The Maltese Falcon" + desc = "The Maltese Falcon, Space Bar and Grill." + +/obj/structure/sign/double/maltesefalcon/left + icon_state = "maltesefalcon-left" + +/obj/structure/sign/double/maltesefalcon/right + icon_state = "maltesefalcon-right" + +/obj/structure/sign/science //These 3 have multiple types, just var-edit the icon_state to whatever one you want on the map + name = "\improper SCIENCE!" + desc = "A warning sign which reads 'SCIENCE'." + icon_state = "science1" + +/obj/structure/sign/chemistry + name = "\improper CHEMISTRY" + desc = "A warning sign which reads 'CHEMISTRY'." + icon_state = "chemistry1" + +/obj/structure/sign/botany + name = "\improper HYDROPONICS" + desc = "A warning sign which reads 'HYDROPONICS'." + icon_state = "hydro1" + +/obj/structure/sign/hydro + name = "\improper HYDROPONICS" + desc = "A sign labelling an area as a place where plants are grown." + icon_state = "hydro2" + +/obj/structure/sign/hydrostorage + name = "\improper HYDROPONICS STORAGE" + desc = "A sign labelling an area as a place where plant growing supplies are kept." + icon_state = "hydro3" + +/obj/structure/sign/xenobio + name = "\improper XENOBIOLOGY" + desc = "A warning sign which reads XENOBIOLOGY." + icon_state = "xenobio3" + + +//direction signs presented by the order they appear in the dmi +/obj/structure/sign/directions + name = "direction sign" + desc = "A direction sign, claiming to know the way to... somewhere?" + icon_state = "direction" + icon = 'icons/obj/decals_directions.dmi' + //TODO: set up overlay systems, inc. interactions (e.g. vines clear w/ plantbgone or fire, snow can be brushed off or melted, and so on) + +//disabled this proc, it serves no purpose except to overwrite the description that already exists. may have been intended for making your own signs? +//seems to defeat the point of having a generic directional sign that mappers could edit and use in POIs? left it here in case something breaks. +/* +/obj/structure/sign/directions/Initialize() + . = ..() + desc = "A direction sign, pointing out the way to \the [src]." +*/ + +//Also floor/level/deck signs in the same vein. Naming conventions! +/obj/structure/sign/levels + name = "level sign" + desc = "A level sign, claiming to know which level to find... Something on?" + icon_state = "level" + icon = 'icons/obj/decals_levels.dmi' + +//engineering signs +/obj/structure/sign/directions/engineering + name = "\improper Engineering Department" + desc = "A direction sign, pointing out the way to the Engineering Department." + icon_state = "direction_eng" + +/obj/structure/sign/levels/engineering + name = "\improper Engineering Department" + desc = "A level sign, stating the level to find the Engineering Department on." + icon_state = "level_eng" + +/obj/structure/sign/directions/engineering/reactor + name = "\improper Reactor" + desc = "A direction sign, pointing out the way to the Reactor." + icon_state = "direction_core" + +/obj/structure/sign/levels/engineering/reactor + name = "\improper Reactor" + desc = "A level sign, stating the level to find the Reactor on." + icon_state = "level_core" + +/obj/structure/sign/directions/engineering/solars + name = "\improper Solar Array" + desc = "A direction sign, pointing out the way to the nearest Solar Array." + icon_state = "direction_solar" + +/obj/structure/sign/levels/engineering/solars + name = "\improper Solar Array" + desc = "A level sign, stating the level to find the nearest Solar Array on." + icon_state = "level_solar" + +/obj/structure/sign/directions/engineering/atmospherics + name = "\improper Atmospherics Department" + desc = "A direction sign, pointing out the way to the Atmospherics Department." + icon_state = "direction_atmos" + +/obj/structure/sign/levels/engineering/atmospherics + name = "\improper Atmospherics Department" + desc = "A level sign, stating the level to find the Atmospherics Department on." + icon_state = "level_atmos" + +/obj/structure/sign/directions/engineering/gravgen + name = "\improper Gravity Generator" + desc = "A direction sign, pointing out the way to the Artificial Gravity Generator." + icon_state = "direction_grav" + +/obj/structure/sign/levels/engineering/gravgen + name = "\improper Gravity Generator" + desc = "A level sign, stating the level to find the Artificial Gravity Generator on." + icon_state = "level_grav" + +/obj/structure/sign/directions/engineering/engeqp + name = "\improper Engineering Equipment Storage" + desc = "A direction sign, pointing out the way to Engineering Equipment Storage." + icon_state = "direction_engeqp" + +/obj/structure/sign/levels/engineering/engeqp + name = "\improper Engineering Equipment Storage" + desc = "A level sign, stating the level to find Engineering Equipment Storage on." + icon_state = "level_engeqp" + +//security signs +/obj/structure/sign/directions/security + name = "\improper Security Department" + desc = "A direction sign, pointing out the way to the Security Department." + icon_state = "direction_sec" + +/obj/structure/sign/levels/security + name = "\improper Security Department" + desc = "A level sign, stating the level to find the Security Department on." + icon_state = "level_sec" + +/obj/structure/sign/directions/security/armory + name = "\improper Armory" + desc = "A direction sign, pointing out the way to the Armory." + icon_state = "direction_armory" + +/obj/structure/sign/levels/security/armory + name = "\improper Armory" + desc = "A level sign, stating the level to find the Armory on." + icon_state = "level_armory" + +/obj/structure/sign/directions/security/brig + name = "\improper Brig" + desc = "A direction sign, pointing out the way to the Brig." + icon_state = "direction_brig" + +/obj/structure/sign/levels/security/brig + name = "\improper Brig" + desc = "A level sign, stating the level to find the Brig on." + icon_state = "level_brig" + +/obj/structure/sign/directions/security/seceqp + name = "\improper Security Equipment Storage" + desc = "A direction sign, pointing out the way to Security Equipment Storage." + icon_state = "direction_seceqp" + +/obj/structure/sign/levels/security/seceqp + name = "\improper Security Equipment Storage" + desc = "A level sign, stating the level to find Security Equipment Storage on." + icon_state = "level_seceqp" + +/obj/structure/sign/directions/security/internal_affairs + name = "\improper Internal Affairs Office" + desc = "A direction sign, pointing out the way to the Internal Affairs Office." + icon_state = "direction_intaff" + +/obj/structure/sign/levels/security/internal_affairs + name = "\improper Internal Affairs Office" + desc = "A level sign, stating the level to find the Internal Affairs Office on." + icon_state = "level_intaff" + +/obj/structure/sign/directions/security/forensics + name = "\improper Forensics Lab" + desc = "A direction sign, pointing out the way to the Forensics Lab." + icon_state = "direction_forensics" + +/obj/structure/sign/levels/security/forensics + name = "\improper Forensics Lab" + desc = "A level sign, stating the level to find the Forensics Lab on." + icon_state = "level_forensics" + +/obj/structure/sign/directions/security/forensics/alt + icon_state = "direction_lab" + +/obj/structure/sign/levels/security/forensics/alt + icon_state = "level_lab" + +/obj/structure/sign/directions/security/interrogation + name = "\improper Interrogations" + desc = "A direction sign, pointing out the way to Interrogations." + icon_state = "direction_interrogation" + +/obj/structure/sign/levels/security/interrogation + name = "\improper Interrogations" + desc = "A level sign, stating the level to find Interrogations on." + icon_state = "level_interrogation" + +//science signs +/obj/structure/sign/directions/science + name = "\improper Science Department" + desc = "A direction sign, pointing out the way to the Science Department." + icon_state = "direction_sci" + +/obj/structure/sign/levels/science + name = "\improper Science Department" + desc = "A level sign, stating the level to find the Science Department on." + icon_state = "level_sci" + +/obj/structure/sign/directions/science/rnd + name = "\improper Research & Development" + desc = "A direction sign, pointing out the way to Research & Development." + icon_state = "direction_rnd" + +/obj/structure/sign/levels/science/rnd + name = "\improper Research & Development" + desc = "A level sign, stating the level to find Research & Development on." + icon_state = "level_rnd" + +/obj/structure/sign/directions/science/toxins + name = "\improper Toxins Lab" + desc = "A direction sign, pointing out the way to the Toxins Lab." + icon_state = "direction_toxins" + +/obj/structure/sign/levels/science/toxins + name = "\improper Toxins Lab" + desc = "A level sign, stating the level to find the Toxins Lab on." + icon_state = "level_toxins" + +/obj/structure/sign/directions/science/robotics + name = "\improper Robotics Workshop" + desc = "A direction sign, pointing out the way to the Robotics Workshop." + icon_state = "direction_robotics" + +/obj/structure/sign/levels/science/robotics + name = "\improper Robotics Workshop" + desc = "A level sign, stating the level to find the Robotics Workshop on." + icon_state = "level_robotics" + +/obj/structure/sign/directions/science/xenoarch + name = "\improper Xenoarchaeology Lab" + desc = "A direction sign, pointing out the way to the Xenoarchaeology Lab." + icon_state = "direction_xenoarch" + +/obj/structure/sign/levels/science/xenoarch + name = "\improper Xenoarchaeology Lab" + desc = "A level sign, stating the level to find the Xenoarchaeology Lab on." + icon_state = "level_xenoarch" + +/obj/structure/sign/directions/science/xenobiology + name = "\improper Xenobiology Lab" + desc = "A direction sign, pointing out the way to the Xenobiology Lab." + icon_state = "direction_xbio" + +/obj/structure/sign/levels/science/xenobiology + name = "\improper Xenobiology Lab" + desc = "A level sign, stating the level to find the Xenobiology Lab on." + icon_state = "level_xbio" + +/obj/structure/sign/directions/science/xenoflora + name = "\improper Xenoflora Lab" + desc = "A direction sign, pointing out the way to the Xenoflora Lab." + icon_state = "direction_xflora" + +/obj/structure/sign/levels/science/xenoflora + name = "\improper Xenoflora Lab" + desc = "A level sign, stating the level to find the Xenoflora Lab on." + icon_state = "level_xflora" + +/obj/structure/sign/directions/science/exploration + name = "\improper Exploration Department" + desc = "A direction sign, pointing out the way to the Exploration Department." + icon_state = "direction_explo" + +/obj/structure/sign/levels/science/exploration + name = "\improper Exploration Department" + desc = "A level sign, stating the level to find the Exploration Department on." + icon_state = "level_explo" + +//medical signs +/obj/structure/sign/directions/medical + name = "\improper Medical Bay" + desc = "A direction sign, pointing out the way to the Medical Bay." + icon_state = "direction_med" + +/obj/structure/sign/levels/medical + name = "\improper Medical Bay" + desc = "A level sign, stating the level to find the Medical Bay on." + icon_state = "level_med" + +/obj/structure/sign/directions/medical/chemlab + name = "\improper Chemistry Lab" + desc = "A direction sign, pointing out the way to the Chemistry Lab." + icon_state = "direction_chemlab" + +/obj/structure/sign/levels/medical/chemlab + name = "\improper Chemistry Lab" + desc = "A level sign, stating the level to find the Chemistry Lab on." + icon_state = "level_chemlab" + +/obj/structure/sign/directions/medical/surgery + name = "\improper Surgery" + desc = "A direction sign, pointing out the way to Surgery." + icon_state = "direction_surgery" + +/obj/structure/sign/levels/medical/surgery + name = "\improper Surgery" + desc = "A level sign, stating the level to find Surgery on." + icon_state = "level_surgery" + +/obj/structure/sign/directions/medical/operating_1 + name = "\improper Operating Theatre 1" + desc = "A direction sign, pointing out the way to Operating Theatre 1." + icon_state = "direction_op1" + +/obj/structure/sign/levels/medical/operating_1 + name = "\improper Operating Theatre 1" + desc = "A level sign, stating the level to find Operating Theatre 1 on." + icon_state = "level_op1" + +/obj/structure/sign/directions/medical/operating_2 + name = "\improper Operating Theatre 2" + desc = "A direction sign, pointing out the way to Operating Theatre 2." + icon_state = "direction_op2" + +/obj/structure/sign/levels/medical/operating_2 + name = "\improper Operating Theatre 2" + desc = "A level sign, stating the level to find Operating Theatre 2 on." + icon_state = "level_op2" + +/obj/structure/sign/directions/medical/virology + name = "\improper Virology" + desc = "A direction sign, pointing out the way to the Virology Lab." + icon_state = "direction_viro" + +/obj/structure/sign/levels/medical/virology + name = "\improper Virology" + desc = "A level sign, stating the level to find the Virology Lab on." + icon_state = "level_viro" + +/obj/structure/sign/directions/medical/medeqp + name = "\improper Medical Equipment Storage" + desc = "A direction sign, pointing out the way to Medical Equipment Storage." + icon_state = "direction_medeqp" + +/obj/structure/sign/levels/medical/medeqp + name = "\improper Medical Equipment Storage" + desc = "A level sign, stating the level to find Medical Equipment Storage on." + icon_state = "level_medeqp" + +/obj/structure/sign/directions/medical/morgue + name = "\improper Morgue" + desc = "A direction sign, pointing out the way to the Morgue." + icon_state = "direction_morgue" + +/obj/structure/sign/levels/medical/morgue + name = "\improper Morgue" + desc = "A level sign, stating the level to find the Morgue on." + icon_state = "level_morgue" + +/obj/structure/sign/directions/medical/cloning + name = "\improper Cloning Lab" + desc = "A direction sign, pointing out the way to the Cloning Lab." + icon_state = "direction_cloning" + +/obj/structure/sign/levels/medical/cloning + name = "\improper Cloning Lab" + desc = "A level sign, stating the level to find the Cloning Lab on." + icon_state = "level_cloning" + +/obj/structure/sign/directions/medical/resleeving + name = "\improper Resleeving Lab" + desc = "A direction sign, pointing out the way to the Resleeving Lab." + icon_state = "direction_resleeve" + +/obj/structure/sign/levels/medical/resleeving + name = "\improper Resleeving Lab" + desc = "A level sign, stating the level to find the Resleeving Lab on." + icon_state = "level_resleeve" + +//special signs +/obj/structure/sign/directions/evac + name = "\improper Evacuation" + desc = "A direction sign, pointing out the way to the Escape Shuttle Dock." + icon_state = "direction_evac" + +/obj/structure/sign/levels/evac + name = "\improper Evacuation" + desc = "A level sign, stating the level to find the Escape Shuttle Dock on." + icon_state = "level_evac" + +/obj/structure/sign/directions/eva + name = "\improper Extra-Vehicular Activity" + desc = "A direction sign, pointing out the way to the EVA Bay." + icon_state = "direction_eva" + +/obj/structure/sign/levels/eva + name = "\improper Extra-Vehicular Activity" + desc = "A level sign, stating the level to find the EVA Bay on." + icon_state = "level_eva" + +//command signs +/obj/structure/sign/directions/ai_core + name = "\improper AI Core" + desc = "A direction sign, pointing out the way to the AI Core." + icon_state = "direction_ai_core" + +/obj/structure/sign/levels/ai_core + name = "\improper AI Core" + desc = "A level sign, stating the level to find the AI Core on." + icon_state = "level_ai_core" + +/obj/structure/sign/directions/bridge + name = "\improper Bridge" + desc = "A direction sign, pointing out the way to the Bridge." + icon_state = "direction_bridge" + +/obj/structure/sign/levels/bridge + name = "\improper Bridge" + desc = "A level sign, stating the level to find the Bridge on." + icon_state = "level_bridge" + +/obj/structure/sign/directions/command + name = "\improper Command" + desc = "A direction sign, pointing out the way to the Command Center." + icon_state = "direction_command" + +/obj/structure/sign/levels/command + name = "\improper Command" + desc = "A level sign, stating the level to find the Command Center on." + icon_state = "level_command" + +/obj/structure/sign/directions/teleporter + name = "\improper Teleporter" + desc = "A direction sign, pointing out the way to the Teleporter." + icon_state = "direction_teleport" + +/obj/structure/sign/levels/teleporter + name = "\improper Teleporter" + desc = "A level sign, stating the level to find the Teleporter on." + icon_state = "level_teleport" + +/obj/structure/sign/directions/telecomms + name = "\improper Telecommunications Hub" + desc = "A direction sign, pointing out the way to the Telecommunications Hub." + icon_state = "direction_tcomms" + +/obj/structure/sign/levels/telecomms + name = "\improper Telecommunications Hub" + desc = "A level sign, stating the level to find the Telecommunications Hub on." + icon_state = "level_tcomms" + +//cargonia signs +/obj/structure/sign/directions/cargo + name = "\improper Cargo Department" + desc = "A direction sign, pointing out the way to the Cargo Department." + icon_state = "direction_crg" + +/obj/structure/sign/levels/cargo + name = "\improper Cargo Department" + desc = "A level sign, stating the level to find the Cargo Department on." + icon_state = "level_crg" + +/obj/structure/sign/directions/cargo/mining + name = "\improper Mining Department" + desc = "A direction sign, pointing out the way to the Mining Department." + icon_state = "direction_mining" + +/obj/structure/sign/levels/cargo/mining + name = "\improper Mining Department" + desc = "A level sign, stating the level to find the Mining Department on." + icon_state = "level_mining" + +/obj/structure/sign/directions/cargo/refinery + name = "\improper Refinery" + desc = "A direction sign, pointing out the way to the Refinery." + icon_state = "direction_refinery" + +/obj/structure/sign/levels/cargo/refinery + name = "\improper Refinery" + desc = "A level sign, stating the level to find the Refinery on." + icon_state = "level_refinery" + +//civilian/misc signs +/obj/structure/sign/directions/roomnum + name = "room number" + desc = "A sign detailing the number of the room beside it." + icon_state = "roomnum" + +/obj/structure/sign/directions/cryo + name = "\improper Cryogenic Storage" + desc = "A direction sign, pointing out the way to Cryogenic Storage." + icon_state = "direction_cry" + +/obj/structure/sign/levels/cryo + name = "\improper Cryogenic Storage" + desc = "A level sign, stating the level to find Cryogenic Storage on." + icon_state = "level_cry" + +/obj/structure/sign/directions/elevator + name = "\improper Elevator" + desc = "A direction sign, pointing out the way to the nearest elevator." + icon_state = "direction_elv" + +/obj/structure/sign/levels/elevator + name = "\improper Elevator" + desc = "A level sign, stating the level to find the nearest elevator on." + icon_state = "level_elv" + +/obj/structure/sign/directions/bar + name = "\improper Bar" + desc = "A direction sign, pointing out the way to the nearest watering hole." + icon_state = "direction_bar" + +/obj/structure/sign/levels/bar + name = "\improper Bar" + desc = "A level sign, stating the level to find the nearest watering hole on." + icon_state = "level_bar" + +/obj/structure/sign/directions/kitchen + name = "\improper Kitchen" + desc = "A pictographic direction sign with a knife, plate, and fork, pointing out the way to the nearest dining establishment." + icon_state = "direction_kitchen" + +/obj/structure/sign/levels/kitchen + name = "\improper Kitchen" + desc = "A pictographic direction sign with a knife, plate, and fork, stating the level to find the nearest dining establishment on." + icon_state = "level_kitchen" + +/obj/structure/sign/directions/shuttle_bay + name = "\improper Shuttle Bay" + desc = "A direction sign, pointing out the way to the nearest shuttle bay." + icon_state = "direction_bay" + +/obj/structure/sign/levels/shuttle_bay + name = "\improper Shuttle Bay" + desc = "A direction sign, stating the level to find the nearest shuttle bay on." + icon_state = "level_bay" + +/obj/structure/sign/directions/tram + name = "\improper Public Transit Station" + desc = "A direction sign, pointing out the way to the nearest public transit station." + icon_state = "direction_tram" + +/obj/structure/sign/levels/tram + name = "\improper Public Transit Station" + desc = "A level sign, stating the level to find the nearest public transit station on." + icon_state = "level_tram" + +/obj/structure/sign/directions/janitor + name = "\improper Custodial Closet" + desc = "A direction sign, pointing out the way to the Custodial Closet." + icon_state = "direction_janitor" + +/obj/structure/sign/levels/janitor + name = "\improper Custodial Closet" + desc = "A level sign, stating the level to find the Custodial Closet on." + icon_state = "level_janitor" + +/obj/structure/sign/directions/chapel + name = "\improper Chapel" + desc = "A direction sign, pointing out the way to the Chapel." + icon_state = "direction_chapel" + +/obj/structure/sign/levels/chapel + name = "\improper Chapel" + desc = "A level sign, stating the level to find the Chapel on." + icon_state = "level_chapel" + +/obj/structure/sign/directions/dorms + name = "\improper Dormitories" + desc = "A direction sign, pointing out the way to the Dormitories." + icon_state = "direction_dorms" + +/obj/structure/sign/levels/dorms + name = "\improper Dormitories" + desc = "A level sign, stating the level to find the Dormitories on." + icon_state = "level_dorms" + +/obj/structure/sign/directions/library + name = "\improper Library" + desc = "A direction sign, pointing out the way to the Library." + icon_state = "direction_library" + +/obj/structure/sign/levels/library + name = "\improper Library" + desc = "A level sign, stating the level to find the Library on." + icon_state = "level_library" + +/obj/structure/sign/directions/dock + name = "\improper Dock" + desc = "A direction sign, pointing out the way to the nearest docking area." + icon_state = "direction_dock" + +/obj/structure/sign/levels/dock + name = "\improper Dock" + desc = "A level sign, stating the level to find the nearest docking area on." + icon_state = "level_dock" + +/obj/structure/sign/directions/gym + name = "\improper Gym" + desc = "A direction sign, pointing out the way to the Gym." + icon_state = "direction_gym" + +/obj/structure/sign/levels/gym + name = "\improper Gym" + desc = "A level sign, stating the level to find the Gym on." + icon_state = "level_gym" + +/obj/structure/sign/directions/pool + name = "\improper Pool" + desc = "A direction sign, pointing out the way to the Pool." + icon_state = "direction_pool" + +/obj/structure/sign/levels/pool + name = "\improper Pool" + desc = "A level sign, stating the level to find the Pool on." + icon_state = "level_pool" + +/obj/structure/sign/directions/recreation + name = "\improper Recreation Area" + desc = "A direction sign, pointing out the way to the nearest Recreation Area." + icon_state = "direction_recreation" + +/obj/structure/sign/levels/recreation + name = "\improper Recreation Area" + desc = "A level sign, stating the level to find the nearest Recreation Area on." + icon_state = "level_recreation" + +/obj/structure/sign/directions/stairwell + name = "\improper Stairwell" + desc = "A direction sign with stairs and a door, pointing out the way to the nearest stairwell." + icon_state = "stairwell" + +/obj/structure/sign/directions/stairs_up + name = "\improper Stairs Up" + desc = "A direction sign with stairs and an upward-slanted arrow, pointing out the way to the nearest set of stairs that go up." + icon_state = "stairs_up" + +/obj/structure/sign/directions/stairs_down + name = "\improper Stairs Down" + desc = "A direction sign with stairs and a downward-slanted arrow, pointing out the way to the nearest set of stairs that go down." + icon_state = "stairs_down" + +/obj/structure/sign/directions/ladderwell + name = "\improper Access Shaft" + desc = "A direction sign with a ladder and a door, pointing out the way to the nearest access shaft." + icon_state = "ladderwell" + +/obj/structure/sign/directions/ladder_up + name = "\improper Ladder Up" + desc = "A direction sign with a ladder and an upward arrow, pointing out the way to the nearest ladder that goes up." + icon_state = "ladder_up" + +/obj/structure/sign/directions/ladder_down + name = "\improper Ladder Down" + desc = "A direction sign with a ladder and a downward arrow, pointing out the way to the nearest ladder that goes down." + icon_state = "ladder_down" + +/obj/structure/sign/directions/exit + name = "\improper Emergency Exit" + desc = "A lurid green sign that unmistakably identifies that the door it's next to as an emergency exit route." + icon_state = "exit_sign" + +//OTHER STUFF +/obj/structure/sign/christmas/lights + name = "Christmas lights" + desc = "Flashy and pretty." + icon = 'icons/obj/christmas.dmi' + icon_state = "xmaslights" + layer = 4.9 + plane = PLANE_LIGHTING_ABOVE + +/obj/structure/sign/christmas/wreath + name = "wreath" + desc = "Prickly and festive." + icon = 'icons/obj/christmas.dmi' + icon_state = "doorwreath" + layer = 5 + +/obj/structure/sign/hostilefauna + icon = 'icons/obj/decals_vr.dmi' + name = "\improper Caution: Hostile fauna" + desc = "This sign warns of hostile life forms in the area." + icon_state = "h_fauna" + +/obj/structure/sign/graffiti/pisoff + icon = 'icons/obj/decals_vr.dmi' + name = "\improper PIS OFF" + desc = "This sign bears some rather rude looking graffiti instructing you to PIS OFF." + icon_state = "pisoff" + +//Eris signs + +/obj/structure/sign/ironhammer + icon = 'icons/obj/decals_vr.dmi' + name = "Ironhammer Security" + desc = "Sign depicts the symbolic of Ironhammer Security, the largest security provider within Trade Union of Hansa." + icon_state = "ironhammer" + +/obj/structure/sign/atmos_co2 + icon = 'icons/obj/decals_vr.dmi' + name = "CO2 warning sign" + desc = "WARNING! CO2 flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_co2" + +/obj/structure/sign/atmos_n2o + icon = 'icons/obj/decals_vr.dmi' + name = "N2O warning sign" + desc = "WARNING! N2O flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_n2o" + +/obj/structure/sign/atmos_plasma + icon = 'icons/obj/decals_vr.dmi' + name = "Plasma warning sign" + desc = "WARNING! Plasma flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_plasma" + +/obj/structure/sign/atmos_n2 + icon = 'icons/obj/decals_vr.dmi' + name = "N2 warning sign" + desc = "WARNING! N2 flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_n2" + +/obj/structure/sign/atmos_o2 + icon = 'icons/obj/decals_vr.dmi' + name = "O2 warning sign" + desc = "WARNING! O2 flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_o2" + +/obj/structure/sign/atmos_air + icon = 'icons/obj/decals_vr.dmi' + name = "Air warning sign" + desc = "WARNING! Air flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_air" + +/obj/structure/sign/atmos_waste + icon = 'icons/obj/decals_vr.dmi' + name = "Atmos waste warning sign" + desc = "WARNING! Waste flow tube. Ensure the flow is disengaged before working." + icon_state = "atmos_waste" + +/obj/structure/sign/deck1 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'DECK I'." + name = "DECK I" + icon_state = "deck1" + +/obj/structure/sign/deck2 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'DECK II'." + name = "DECK II" + icon_state = "deck2" + +/obj/structure/sign/deck3 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'DECK III'." + name = "DECK III" + icon_state = "deck3" + +/obj/structure/sign/deck4 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'DECK IV'." + name = "DECK IV" + icon_state = "deck4" + +/obj/structure/sign/sec1 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'SECTION I'." + name = "SECTION I" + icon_state = "sec1" + +/obj/structure/sign/sec2 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'SECTION II'." + name = "SECTION II" + icon_state = "sec2" + +/obj/structure/sign/sec3 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'SECTION III'." + name = "SECTION III" + icon_state = "sec3" + +/obj/structure/sign/sec4 + icon = 'icons/obj/decals_vr.dmi' + desc = "A silver sign which reads 'SECTION IV'." + name = "SECTION IV" + icon_state = "sec4" + +/obj/structure/sign/nanotrasen + icon = 'icons/obj/decals_vr.dmi' + name = "\improper NanoTrasen" + desc = "An old metal sign which reads 'NanoTrasen'." + icon_state = "NT" + catalogue_data = list(/datum/category_item/catalogue/information/organization/nanotrasen) + +// Eris standards compliant hazards +/obj/structure/sign/signnew + icon = 'icons/obj/decals_vr.dmi' + +/obj/structure/sign/signnew/biohazard + name = "BIOLOGICAL HAZARD" + desc = "Warning: Biological and-or toxic hazards present in this area!" + icon_state = "biohazard" + +/obj/structure/sign/signnew/corrosives + name = "CORROSIVE SUBSTANCES" + desc = "Warning: Corrosive substances prezent in this area!" + icon_state = "corrosives" + +/obj/structure/sign/signnew/explosives + name = "EXPLOSIVE SUBSTANCES" + desc = "Warning: Explosive substances present in this area!" + icon_state = "explosives" + +/obj/structure/sign/signnew/flammables + name = "FLAMMABLE SUBSTANCES" + desc = "Warning: Flammable substances present in this area!" + icon_state = "flammable" + +/obj/structure/sign/signnew/laserhazard + name = "LASER HAZARD" + desc = "Warning: High powered laser emitters operating in this area!" + icon_state = "laser" + +/obj/structure/sign/signnew/danger + name = "DANGEROUS AREA" + desc = "Warning: Generally hazardous area! Exercise caution." + icon_state = "danger" + +/obj/structure/sign/signnew/magnetics + name = "MAGNETIC FIELD HAZARD" + desc = "Warning: Extremely powerful magnetic fields present in this area!" + icon_state = "magnetics" + +/obj/structure/sign/signnew/opticals + name = "OPTICAL HAZARD" + desc = "Warning: Optical hazards present in this area!" + icon_state = "optical" + +/obj/structure/sign/signnew/radiation + name = "RADIATION HAZARD" + desc = "Warning: Significant levels of radiation present in this area!" + icon_state = "radiation" + +/obj/structure/sign/signnew/secure + name = "SECURE AREA" + desc = "Warning: Secure Area! Do not enter without authorization!" + icon_state = "secure" + +/obj/structure/sign/signnew/electrical + name = "ELECTRICAL HAZARD" + desc = "Warning: Electrical hazards! Wear protective equipment." + icon_state = "electrical" + +/obj/structure/sign/signnew/cryogenics + name = "CRYOGENIC TEMPERATURES" + desc = "Warning: Extremely low temperatures in this area." + icon_state = "cryogenics" + +/obj/structure/sign/signnew/canisters + name = "PRESSURIZED CANISTERS" + desc = "Warning: Highly pressurized canister storage." + icon_state = "canisters" + +/obj/structure/sign/signnew/oxidants + name = "OXIDIZING AGENTS" + desc = "Warning: Oxidizing agents in this area, do not start fires!" + icon_state = "oxidants" + +/obj/structure/sign/signnew/memetic + name = "MEMETIC HAZARD" + desc = "Warning: Memetic hazard, wear meson goggles!" + icon_state = "memetic" + +//Eris departments + +/obj/structure/sign/department + icon = 'icons/obj/decals_vr.dmi' + name = "department sign" + desc = "Sign of some important ship compartment." + +/obj/structure/sign/department/medbay + name = "MEDBAY" + icon_state = "medbay" + +/obj/structure/sign/department/virology + name = "VIROLOGY" + icon_state = "virology" + +/obj/structure/sign/department/chem + name = "CHEMISTRY" + icon_state = "chem" + +/obj/structure/sign/department/gene + name = "GENETICS" + icon_state = "gene" + +/obj/structure/sign/department/morgue + name = "MORGUE" + icon_state = "morgue" + +/obj/structure/sign/department/operational + name = "SURGERY" + icon_state = "operational" + +/obj/structure/sign/department/sci + name = "SCIENCE" + icon_state = "sci" + +/obj/structure/sign/department/xenolab + name = "XENOLAB" + icon_state = "xenolab" + +/obj/structure/sign/department/anomaly + name = "ANOMALYLAB" + icon_state = "anomaly" + +/obj/structure/sign/department/dock + name = "DOKUCHAYEV DOCK" + icon_state = "dock" + +/obj/structure/sign/department/rnd + name = "RND" + icon_state = "rnd" + +/obj/structure/sign/department/robo + name = "ROBOTICS" + icon_state = "robo" + +/obj/structure/sign/department/toxins + name = "TOXINS" + icon_state = "toxins" + +/obj/structure/sign/department/toxin_res + name = "TOXINLAB" + icon_state = "toxin_res" + +/obj/structure/sign/department/eva + name = "E.V.A." + icon_state = "eva" + +/obj/structure/sign/department/ass + name = "TOOL STORAGE" + icon_state = "ass" + +/obj/structure/sign/department/bar + name = "BAR" + icon_state = "bar" + +/obj/structure/sign/department/biblio + name = "LIBRARY" + icon_state = "biblio" + +/obj/structure/sign/department/chapel + name = "CHAPEL" + icon_state = "chapel" + +/obj/structure/sign/department/bridge + name = "BRIDGE" + icon_state = "bridge" + +/obj/structure/sign/department/telecoms + name = "TELECOMS" + icon_state = "telecoms" + +/obj/structure/sign/department/conference_room + name = "CONFERENCE" + icon_state = "conference_room" + +/obj/structure/sign/department/ai + name = "AI" + icon_state = "ai" + +/obj/structure/sign/department/cargo + name = "CARGO" + icon_state = "cargo" + +/obj/structure/sign/department/mail + name = "MAIL" + icon_state = "mail" + +/obj/structure/sign/department/miner_dock + name = "MINING DOCK" + icon_state = "miner_dock" + +/obj/structure/sign/department/cargo_dock + name = "CARGO DOCK" + icon_state = "cargo_dock" + +/obj/structure/sign/department/eng + name = "ENGINEERING" + icon_state = "eng" + +/obj/structure/sign/department/engine + name = "ENGINE" + icon_state = "engine" + +/obj/structure/sign/department/gravi + name = "GRAVGEN" + icon_state = "gravi" + +/obj/structure/sign/department/atmos + name = "ATMOSPHERICS" + icon_state = "atmos" + +/obj/structure/sign/department/shield + name = "SHIELDGEN" + icon_state = "shield" + +/obj/structure/sign/department/drones + name = "DRONES" + icon_state = "drones" + +/obj/structure/sign/department/interrogation + name = "INTERROGATION" + icon_state = "interrogation" + +/obj/structure/sign/department/commander + name = "COMMANDER" + icon_state = "commander" + +/obj/structure/sign/department/armory + name = "ARMORY" + icon_state = "armory" + +/obj/structure/sign/department/prison + name = "PRISON" + icon_state = "prison" + +/obj/structure/sign/deck/first + name = "\improper First Deck" + icon_state = "deck-1" + +/obj/structure/sign/deck/second + name = "\improper Second Deck" + icon_state = "deck-2" + +/obj/structure/sign/deck/third + name = "\improper Third Deck" + icon_state = "deck-3" + +/obj/structure/sign/deck/fourth + name = "\improper Fourth Deck" + icon_state = "deck-4" + +/obj/structure/sign/level/one + name = "\improper Level One" + icon_state = "level-1" + +/obj/structure/sign/level/one/large + icon_state = "level-1-large" + +/obj/structure/sign/level/two + name = "\improper Level Two" + icon_state = "level-2" + +/obj/structure/sign/level/two/large + icon_state = "level-2-large" + +/obj/structure/sign/level/three + name = "\improper Level Three" + icon_state = "level-3" + +/obj/structure/sign/level/three/large + icon_state = "level-3-large" + +/obj/structure/sign/level/fourth + name = "\improper Level Four" + icon_state = "level-4" + +/obj/structure/sign/level/four/large + icon_state = "level-4-large" + +/obj/structure/sign/level/basement + name = "\improper Basement Level" + icon_state = "level-b" + +/obj/structure/sign/level/basement/large + icon_state = "level-b-large" + +/obj/structure/sign/level/ground + name = "\improper Ground Level" + icon_state = "level-g" + +/obj/structure/sign/level/ground/large + icon_state = "level-g-large" + +/obj/structure/sign/hangar/one + name = "\improper Hangar One" + icon_state = "hangar-1" + +/obj/structure/sign/hangar/two + name = "\improper Hangar Two" + icon_state = "hangar-2" + +/obj/structure/sign/hangar/three + name = "\improper Hangar Three" + icon_state = "hangar-3" + +/obj/structure/sign/atmos + name = "\improper WASTE" + icon_state = "atmos_waste" + +/obj/structure/sign/atmos/o2 + name = "\improper OXYGEN" + icon_state = "atmos_o2" + +/obj/structure/sign/atmos/co2 + name = "\improper CARBON DIOXIDE" + icon_state = "atmos_co2" + +/obj/structure/sign/atmos/phoron + name = "\improper PHORON" + icon_state = "atmos_phoron" + +/obj/structure/sign/atmos/n2o + name = "\improper NITROUS OXIDE" + icon_state = "atmos_n2o" + +/obj/structure/sign/atmos/n2 + name = "\improper NITROGEN" + icon_state = "atmos_n2" + +/obj/structure/sign/atmos/air + name = "\improper AIR" + icon_state = "atmos_air" + +/obj/structure/sign/scenery/engineleft + name = "I.C.V." + desc = "The charred name of a cargo ship of some description." + icon_state = "poi_engine1" + +/obj/structure/sign/scenery/engineright + name = "I.C.V." + desc = "The charred name of a cargo ship of some description." + icon_state = "poi_engine2" + +//Direction/Level sign overlays. Not mechanically functional as noted above, but usable for mapping. +/obj/structure/sign/scenery/overlay + name = "snow covering" + desc = "Frozen snow obscures the view of a sign beneath." + icon_state = "snowy" + icon = 'icons/obj/decals_directions.dmi' + +/obj/structure/sign/scenery/overlay/rust + name = "rust covering" + desc = "Thick rust obscures the view of a sign beneath." + icon_state = "rusted" + +/obj/structure/sign/scenery/overlay/vine + name = "vine covering" + desc = "Thick vines obscure the view of a sign beneath." + icon_state = "vines" + +/obj/structure/sign/scenery/overlay/vine/top + icon_state = "vines_top" + +/obj/structure/sign/scenery/overlay/vine/mid + icon_state = "vines_mid" + +/obj/structure/sign/scenery/overlay/vine/bottom + icon_state = "vines_bottom" + +/obj/structure/sign/bigname + name = "Cynosure Station" + desc = "An aging sign for the Cynosure Xenoarchaeological Research Station." + icon_state = "cyno_1" + +/obj/structure/sign/bigname/seg_2 + icon_state = "cyno_2" + +/obj/structure/sign/bigname/seg_3 + icon_state = "cyno_3" + +/obj/structure/sign/bigname/seg_4 + icon_state = "cyno_4" + +/obj/structure/sign/bigname/seg_5 + icon_state = "cyno_5" + +/obj/structure/sign/bigname/seg_6 + icon_state = "cyno_6" + +/obj/structure/sign/bigname/seg_7 + icon_state = "cyno_7" + +/obj/structure/sign/clock + name = "wall clock" + desc = "A basic wall clock, synced to the current system time." + icon_state = "clock" + +/obj/structure/sign/clock/examine(mob/user) + . = ..() + . += "The clock shows that the time is [stationtime2text()]." + +/obj/structure/sign/calendar + name = "calendar" + desc = "It's an old-school, NanoTrasen branded wall calendar. Sure, it might be obsolete with modern technology, but it's still hard to imagine an office without one." + icon_state = "calendar" + +/obj/structure/sign/calendar/examine(mob/user) + . = ..() + . += "The calendar shows that the date is [stationdate2text()]." + if (Holiday.len) + . += "Today is [english_list(Holiday)]." + +/obj/structure/sign/explosive + name = "\improper HIGH EXPLOSIVES sign" + desc = "A warning sign which reads 'HIGH EXPLOSIVES'." + icon_state = "explosives" + +/obj/structure/sign/chemdiamond + name = "\improper HAZARDOUS CHEMICALS sign" + desc = "A sign that warns of potentially hazardous chemicals nearby, indicating health risk, flash point, and reactivity." + icon_state = "chemdiamond" + +//Here be Flags + +//Flag item +/obj/item/flag + name = "boxed flag" + desc = "A flag neatly folded into a wooden container." + icon = 'icons/obj/flags.dmi' + icon_state = "flag_boxed" + var/flag_path = "flag" + var/flag_size = 0 + +//Flag on wall +/obj/structure/sign/flag + name = "blank flag" + desc = "Nothing to see here." + icon = 'icons/obj/flags.dmi' + icon_state = "flag" + var/icon/ripped_outline = icon('icons/obj/flags.dmi', "ripped") + var/obj/structure/sign/flag/linked_flag //For double flags + var/obj/item/flag/flagtype //For returning your flag + var/ripped = FALSE //If we've been torn down + +/obj/structure/sign/flag/blank + name = "blank banner" + desc = "A blank white flag." + icon_state = "flag" + flagtype = /obj/item/flag + +/obj/item/flag/afterattack(var/atom/A, var/mob/user, var/adjacent, var/clickparams) + if (!adjacent) + return + + if((!iswall(A) && !istype(A, /obj/structure/window)) || !isturf(user.loc)) + to_chat(user, SPAN_WARNING("You can't place this here!")) + return + + var/placement_dir = get_dir(user, A) + if (!(placement_dir in cardinal)) + to_chat(user, SPAN_WARNING("You must stand directly in front of the location you wish to place that on.")) + return + + var/obj/structure/sign/flag/P = new(user.loc) + + switch(placement_dir) + if(NORTH) + P.pixel_y = 32 + if(SOUTH) + P.pixel_y = -32 + if(EAST) + P.pixel_x = 32 + if(WEST) + P.pixel_x = -32 + + P.dir = placement_dir + if(flag_size) + P.icon_state = "[flag_path]_l" + var/obj/structure/sign/flag/P2 = new(user.loc) + P.linked_flag = P2 + P2.linked_flag = P + P2.icon_state = "[flag_path]_r" + P2.dir = P.dir + switch(P2.dir) + if(NORTH) + P2.pixel_y = P.pixel_y + P2.pixel_x = 32 + if(SOUTH) + P2.pixel_y = P.pixel_y + P2.pixel_x = 32 + if(EAST) + P2.pixel_x = P.pixel_x + P2.pixel_y = -32 + if(WEST) + P2.pixel_x = P.pixel_x + P2.pixel_y = 32 + P2.name = name + P2.desc = desc + P2.description_info = description_info + P2.description_fluff = description_fluff + P2.flagtype = type + else + P.icon_state = "[flag_path]" + P.name = name + P.desc = desc + P.description_info = description_info + P.description_fluff = description_fluff + P.flagtype = type + qdel(src) + +/obj/structure/sign/flag/Destroy() + if(linked_flag?.linked_flag == src) //Catches other instances where one half might be destroyed, say by a broken wall, to avoid runtimes. + linked_flag.linked_flag = null //linked_flag + . = ..() + +/obj/structure/sign/flag/ex_act(severity) + switch(severity) + if(1) + qdel(src) + if(2) + if(prob(50)) + qdel(src) + else + rip() + if(3) + rip() + +/obj/structure/sign/flag/unfasten(mob/user) + if(!ripped) + user.visible_message(SPAN_NOTICE("\The [user] unfastens \the [src] and folds it back up."), SPAN_NOTICE("You unfasten \the [src] and fold it back up.")) + var/obj/item/flag/F = new flagtype(get_turf(user)) + user.put_in_hands(F) + else + user.visible_message(SPAN_NOTICE("\The [user] unfastens the tattered remnants of \the [src]."), SPAN_NOTICE("You unfasten the tattered remains of \the [src].")) + if(linked_flag) + qdel(linked_flag) //otherwise you're going to get weird duping nonsense + qdel(src) + +/obj/structure/sign/flag/attack_hand(mob/user) + if(alert("Do you want to rip \the [src] from its place?","You think...","Yes","No") == "Yes") + if(!Adjacent(user)) //Cannot bring up dialogue and walk away + return FALSE + visible_message(SPAN_WARNING("\The [user] rips \the [src] in a single, decisive motion!" )) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 100, 1) + add_fingerprint(user) + rip() + +/obj/structure/sign/flag/proc/rip(var/rip_linked = TRUE) + var/icon/I = new('icons/obj/flags.dmi', icon_state) + var/icon/mask = new('icons/obj/flags.dmi', "ripped") + I.AddAlphaMask(mask) + icon = I + name = "ripped flag" + desc = "You can't make out anything from the flag's original print. It's ruined." + ripped = TRUE + if(linked_flag && rip_linked) + linked_flag.rip(FALSE) //Prevents an infinite ripping loop + +/obj/structure/sign/flag/attackby(obj/item/W, mob/user) + ..() + if(istype(W, /obj/item/weapon/flame/lighter) || istype(W, /obj/item/weapon/weldingtool)) + visible_message(SPAN_WARNING("\The [user] starts to burn \the [src] down!")) + if(!do_after(user, 2 SECONDS)) + return FALSE + visible_message(SPAN_WARNING("\The [user] burns \the [src] down!")) + playsound(src.loc, 'sound/items/cigs_lighters/cig_light.ogg', 100, 1) + new /obj/effect/decal/cleanable/ash(src.loc) + if(linked_flag) + qdel(linked_flag) + qdel(src) + return TRUE + +/obj/structure/sign/flag/blank/left + icon_state = "flag_l" + +/obj/structure/sign/flag/blank/right + icon_state = "flag_r" + +//SolGov +/obj/structure/sign/flag/sol + name = "Solar Confederate Government flag" + desc = "The bright blue flag of the Solar Confederate Government." + icon_state = "solgov" + flagtype = /obj/item/flag/sol + +/obj/structure/sign/flag/sol/left + icon_state = "solgov_l" + +/obj/structure/sign/flag/sol/right + icon_state = "solgov_r" + +/obj/item/flag/sol + name = "Solar Confederate Government flag" + desc = "The bright blue flag of the Solar Confederate Government." + flag_path = "solgov" + +/obj/item/flag/sol/l + name = "large Solar Confederate Government flag" + flag_size = 1 + +//NanoTrasen +/obj/structure/sign/flag/nt + name = "NanoTrasen corporate flag" + desc = "A flag portraying the logo of the NanoTrasen corporation." + icon_state = "nanotrasen" + flagtype = /obj/item/flag/nt + +/obj/structure/sign/flag/nt/left + icon_state = "nanotrasen_l" + +/obj/structure/sign/flag/nt/right + icon_state = "nanotrasen_r" + +/obj/item/flag/nt + name = "NanoTrasen corporate flag" + desc = "A flag portraying the logo of the NanoTrasen corporation." + flag_path = "nanotrasen" + +/obj/item/flag/nt/l + name = "large NanoTrasen corporate flag" + flag_size = 1 + +//Vir +/obj/structure/sign/flag/vir + name = "Vir Governmental Authority flag" + desc = "The two-tone flag of the Vir Governmental Authority." + description_fluff = "Commonly referred to as VirGov, the Vir Governmental Authority was formed in 2412 as a unified system government following \ + a half century of war between the Sif Planetary Government - or SifGov - and corporate interests in Kara orbit, in order to qualify for full membership \ + in the Solar Confederate Government. Following the Karan Wars it would be almost a century before Trans-Stellar Corporations were allowed their typical \ + freedom to operate unobstructed in the Vir system." + icon_state = "vir" + flagtype = /obj/item/flag/vir + +/obj/structure/sign/flag/vir/left + icon_state = "vir_l" + +/obj/structure/sign/flag/vir/right + icon_state = "vir_r" + +/obj/item/flag/vir + name = "Vir Governmental Authority flag" + desc = "The two-tone flag of the Vir Governmental Authority." + description_fluff = "Commonly referred to as VirGov, the Vir Governmental Authority was formed in 2412 as a unified system government following \ + a half century of war between the Sif Planetary Government - or SifGov - and corporate interests in Kara orbit, in order to qualify for full membership \ + in the Solar Confederate Government. Following the Karan Wars it would be almost a century before Trans-Stellar Corporations were allowed their typical \ + freedom to operate unobstructed in the Vir system." + flag_path = "vir" + +/obj/item/flag/vir/l + name = "large Vir Governmental Authority flag" + flag_size = 1 + +//Almach Association + +/obj/structure/sign/flag/almach_a + name = "Almach Association flag" + desc = "The black and grey flag of the now-defunct Almach Association." + description_fluff = "The Almach Association was a short lived (February 2562 - April 2564) governmental entity formed as an alliance of disparate radical mercurial \ + states in an effort to secede from the Solar Confederate Government. Though the Association were defeated, and ultimately annexed by the Skrellian Far Kingdoms, \ + the Association flag remains a popular symbol with mercurials and secessionists alike. To some, the Almach Association is seen as the first step towards the SCG's \ + \"inevitable\" dissolution." + icon_state = "almach_a" + flagtype = /obj/item/flag/almach_a + +/obj/structure/sign/flag/almach_a/left + icon_state = "almach_a_l" + +/obj/structure/sign/flag/almach_a/right + icon_state = "almach_a_r" + +/obj/item/flag/almach_a + name = "Almach Association flag" + desc = "The black and grey flag of the now-defunct Almach Association." + description_fluff = "The Almach Association was a short lived (February 2562 - April 2564) governmental entity formed as an alliance of disparate radical mercurial \ + states in an effort to secede from the Solar Confederate Government. Though the Association were defeated, and ultimately annexed by the Skrellian Far Kingdoms, \ + the Association flag remains a popular symbol with mercurials and secessionists alike. To some, the Almach Association is seen as the first step towards the SCG's \ + \"inevitable\" dissolution." + flag_path = "almach_a" + +/obj/item/flag/almach_a/l + name = "large Almach Association flag" + flag_size = 1 + +//Almach Protectorate +/obj/structure/sign/flag/almach_p + name = "Almach Protectorate flag" + desc = "The purple flag of the Almach Protectorate." + description_fluff = "The Almach Protectorate was formed from the territory of the Almach Association as a condition of the 2564 Treaty of Whythe. \ + Intended to be closely overseen by the Skrellian Far Kingdom, the Skathari Incursion left the Protectorate functionally independent in many regards, \ + leading to the proliferation of previously restricted genetic modification technology into SolGov territory. However, the Protectorate was also left \ + without meaningful military support, and has suffered sorely in the years since as the Relan-led government has struggled to remilitarize." + icon_state = "almach_p" + flagtype = /obj/item/flag/almach_p + +/obj/structure/sign/flag/almach_p/left + icon_state = "almach_p_l" + +/obj/structure/sign/flag/almach_p/right + icon_state = "almach_p_r" + +/obj/item/flag/almach_p + name = "Almach Protectorate flag" + desc = "The purple flag of the Almach Protectorate." + description_fluff = "The Almach Protectorate was formed from the territory of the Almach Association as a condition of the 2564 Treaty of Whythe. \ + Intended to be closely overseen by the Skrellian Far Kingdom, the Skathari Incursion left the Protectorate functionally independent in many regards, \ + leading to the proliferation of previously restricted genetic modification technology into SolGov territory. However, the Protectorate was also left \ + without meaningful military support, and has suffered sorely in the years since as the Relan-led government has struggled to remilitarize." + flag_path = "almach_p" + +/obj/item/flag/almach_p/l + name = "large Almach Protectorate flag" + flag_size = 1 + +//Vystholm +/obj/structure/sign/flag/vystholm + name = "Vystholm flag" + desc = "The black and gold flag of Vystholm." + description_fluff = "Vystholm is a faction of xenophobic humans who constructed an ark ship, the VHS Rodnakya, in response to the abolition of the hostile \ + First Contact Policy in the early 24th century. Thought to have long departed known space, the Vystholm returned to within range of human territory in response \ + to the Skathari Incursion and has since undertaken a campaign of raiding, terrorism and espionage against established governments, particularly the Tajaran \ + Pearlshield Coalition." + icon_state = "vystholm" + flagtype = /obj/item/flag/vystholm + +/obj/structure/sign/flag/vystholm/left + icon_state = "vystholm_l" + +/obj/structure/sign/flag/vystholm/right + icon_state = "vystholm_r" + +/obj/item/flag/vystholm + name = "Vystholm flag" + desc = "The black and gold flag of Vystholm." + description_fluff = "Vystholm is a faction of xenophobic humans who constructed an ark ship, the VHS Rodnakya, in response to the abolition of the hostile \ + First Contact Policy in the early 24th century. Thought to have long departed known space, the Vystholm returned to within range of human territory in response \ + to the Skathari Incursion and has since undertaken a campaign of raiding, terrorism and espionage against established governments, particularly the Tajaran \ + Pearlshield Coalition." + flag_path = "vystholm" + +/obj/item/flag/vystholm/l + name = "large Vystholm flag" + flag_size = 1 + +//Five Arrows +/obj/structure/sign/flag/fivearrows + name = "Five Arrows flag" + desc = "The red flag of the Five Arrows." + description_fluff = "The Five Arrows is an independent government entity that seceded from the Solar Confederate Government in 2570, in response to perceived \ + failures in aiding the Sagittarius Heights during the Skathari Incursion. The success of the government in achieving effective local defense and prosperity has \ + since attracted the membership of Kauq'xum, a remote Skrellian colony. \The Five Arrows formed the model for SolGov's own semi-autonomous \"Regional Blocs\"." + icon_state = "fivearrows" + flagtype = /obj/item/flag/fivearrows + +/obj/structure/sign/flag/fivearrows/left + icon_state = "fivearrows_l" + +/obj/structure/sign/flag/fivearrows/right + icon_state = "fivearrows_r" + +/obj/item/flag/fivearrows + name = "Five Arrows flag" + desc = "The red flag of the Five Arrows." + description_fluff = "The Five Arrows is an independent government entity that seceded from the Solar Confederate Government in 2570, in response to perceived \ + failures in aiding the Sagittarius Heights during the Skathari Incursion. The success of the government in achieving effective local defense and prosperity has \ + since attracted the membership of Kauq'xum, a remote Skrellian colony. \The Five Arrows formed the model for SolGov's own semi-autonomous \"Regional Blocs\"." + flag_path = "fivearrows" + +/obj/item/flag/fivearrows/l + name = "large Five Arrows flag" + flag_size = 1 + +//Pirates +/obj/structure/sign/flag/pirate + name = "pirate flag" + desc = "Shiver me timbers, hoist the black!" + icon_state = "pirate" + flagtype = /obj/item/flag/pirate + +/obj/structure/sign/flag/pirate/left + icon_state = "pirate_l" + +/obj/structure/sign/flag/pirate/right + icon_state = "pirate_r" + +/obj/item/flag/pirate + name = "pirate flag" + desc = "Shiver me timbers, hoist the black!" + flag_path = "pirate" + + +/obj/item/flag/pirate/l + name = "large pirate flag" + flag_size = 1 + +//Catpirate +/obj/structure/sign/flag/catpirate + name = "Tajaran pirate flag" + desc = "Shiver me whiskers, hoist the black!" + icon_state = "catpirate" + flagtype = /obj/item/flag/catpirate + +/obj/structure/sign/flag/catpirate/left + icon_state = "catpirate_l" + +/obj/structure/sign/flag/catpirate/right + icon_state = "catpirate_r" + +/obj/item/flag/catpirate + name = "Tajaran pirate flag" + desc = "Shiver me whiskers, hoist the black!" + flag_path = "catpirate" + +/obj/item/flag/catpirate/l + name = "large Tajaran pirate flag" + flag_size = 1 + +//Political Parties +/obj/structure/sign/flag/icarus + name = "Icarus Front flag" + desc = "The flag of the right-populist Icarus Front political party." + icon_state = "icarus" + flagtype = /obj/item/flag/icarus + +/obj/item/flag/icarus + name = "Icarus Front flag" + desc = "The flag of the right-populist Icarus Front political party." + flag_path = "icarus" + +/obj/structure/sign/flag/shadowcoalition + name = "Shadow Coalition flag" + desc = "The flag of the neoliberal Shadow Coalition political party." + icon_state = "shadowcoalition" + flagtype = /obj/item/flag/shadowcoalition + +/obj/item/flag/shadowcoalition + name = "Shadow Coalition flag" + desc = "The flag of the neoliberal Shadow Coalition political party." + flag_path = "shadowcoalition" + +/obj/structure/sign/flag/seo + name = "Sol Economic Organization flag" + desc = "The flag of the protectionist Sol Economic Organization political party." + icon_state = "seo" + flagtype = /obj/item/flag/seo + +/obj/item/flag/seo + name = "Sol Economic Organization flag" + desc = "The flag of the protectionist Sol Economic Organization political party." + flag_path = "seo" + +/obj/structure/sign/flag/gap + name = "Galactic Autonomy Party flag" + desc = "The flag of the libertarian Galactic Autonomy Party political party." + icon_state = "gap" + flagtype = /obj/item/flag/seo + +/obj/item/flag/gap + name = "Galactic Autonomy Party flag" + desc = "The flag of the libertarian Galactic Autonomy Party political party." + flag_path = "gap" diff --git a/code/game/objects/structures/signs_vr.dm b/code/game/objects/structures/signs_vr.dm index f9390371600..1e97c4736f8 100644 --- a/code/game/objects/structures/signs_vr.dm +++ b/code/game/objects/structures/signs_vr.dm @@ -1,65 +1,65 @@ -/obj/structure/sign/itg - icon = 'icons/obj/decals_vr.dmi' - name = "\improper ITG" - desc = "A polished metal sign which reads 'Ironcrest Transport Group'." - icon_state = "itg" - -/obj/structure/sign/scenery/fakefireaxe - name = "decorative fire axe cabinet" - desc = "A fancy decorative indent in the wall, with an axe inside. The axe is actually a part of the indent and cannot be removed. A nostalgic reminder of older times of firefighting." - icon_state = "fireaxe1000" - icon = 'icons/obj/closet.dmi' - -//Small Signs for detailing -/obj/structure/sign/small/fire/small - icon = 'icons/obj/decals.dmi' - name = "\improper DANGER: FIRE" - desc = "A warning sign which reads 'DANGER: FIRE'." - icon_state = "fire_small" - -/obj/structure/sign/small/nosmoking - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'." - icon_state = "nosmoking_small" - -/obj/structure/sign/small/nosmoking - name = "\improper DESIGNATED SMOKING AREA" - desc = "A warning sign which reads 'DESIGNATED SMOKING AREA'." - icon_state = "smoking_small" - -/obj/structure/sign/small/warning - name = "\improper WARNING" - desc = "" //Null description - icon_state = "warning_small" - -/obj/structure/sign/small/warning/high_voltage - name = "\improper HIGH VOLTAGE" - icon_state = "shock_small" - -/obj/structure/sign/small/warning/radioactive - name = "\improper RADIOACTIVE AREA" - icon_state = "radiation_small" - -/obj/structure/sign/small/warning/caution - name = "\improper CAUTION" - icon_state = "caution_small" - -/obj/structure/sign/small/warning/server_room - name = "\improper SERVER ROOM" - icon_state = "server_small" - -/obj/structure/sign/small/warning/secure_area - name = "\improper SECURE AREA" - icon_state = "securearea_small" - -/obj/structure/sign/small/warning/vacuum - name = "\improper HARD VACUUM AHEAD" - icon_state = "space_small" - -/obj/structure/sign/small/warning/pods - name = "\improper ESCAPE PODS" - icon_state = "pods" - -/obj/structure/sign/small/warning/emerg_only - name = "\improper EMERGENCY USE ONLY" +/obj/structure/sign/itg + icon = 'icons/obj/decals_vr.dmi' + name = "\improper ITG" + desc = "A polished metal sign which reads 'Ironcrest Transport Group'." + icon_state = "itg" + +/obj/structure/sign/scenery/fakefireaxe + name = "decorative fire axe cabinet" + desc = "A fancy decorative indent in the wall, with an axe inside. The axe is actually a part of the indent and cannot be removed. A nostalgic reminder of older times of firefighting." + icon_state = "fireaxe1000" + icon = 'icons/obj/closet.dmi' + +//Small Signs for detailing +/obj/structure/sign/small/fire/small + icon = 'icons/obj/decals.dmi' + name = "\improper DANGER: FIRE" + desc = "A warning sign which reads 'DANGER: FIRE'." + icon_state = "fire_small" + +/obj/structure/sign/small/nosmoking + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'." + icon_state = "nosmoking_small" + +/obj/structure/sign/small/nosmoking + name = "\improper DESIGNATED SMOKING AREA" + desc = "A warning sign which reads 'DESIGNATED SMOKING AREA'." + icon_state = "smoking_small" + +/obj/structure/sign/small/warning + name = "\improper WARNING" + desc = "" //Null description + icon_state = "warning_small" + +/obj/structure/sign/small/warning/high_voltage + name = "\improper HIGH VOLTAGE" + icon_state = "shock_small" + +/obj/structure/sign/small/warning/radioactive + name = "\improper RADIOACTIVE AREA" + icon_state = "radiation_small" + +/obj/structure/sign/small/warning/caution + name = "\improper CAUTION" + icon_state = "caution_small" + +/obj/structure/sign/small/warning/server_room + name = "\improper SERVER ROOM" + icon_state = "server_small" + +/obj/structure/sign/small/warning/secure_area + name = "\improper SECURE AREA" + icon_state = "securearea_small" + +/obj/structure/sign/small/warning/vacuum + name = "\improper HARD VACUUM AHEAD" + icon_state = "space_small" + +/obj/structure/sign/small/warning/pods + name = "\improper ESCAPE PODS" + icon_state = "pods" + +/obj/structure/sign/small/warning/emerg_only + name = "\improper EMERGENCY USE ONLY" icon_state = "emerg_small" \ No newline at end of file diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index f54abd7439a..44c241ae7f4 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -1,265 +1,283 @@ -/obj/structure/simple_door - name = "door" - description_info = "If you hold left alt whilst left-clicking on a door, you can knock on it to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!" - density = TRUE - anchored = TRUE - can_atmos_pass = ATMOS_PASS_DENSITY - - icon = 'icons/obj/doors/material_doors.dmi' - icon_state = "metal" - - var/datum/material/material - var/state = 0 //closed, 1 == open - var/isSwitchingStates = 0 - var/hardness = 1 - var/oreAmount = 7 - var/knock_sound = 'sound/machines/door/knock_glass.ogg' - var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' - -/obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - TemperatureAct(exposed_temperature) - -/obj/structure/simple_door/proc/TemperatureAct(temperature) - hardness -= material.combustion_effect(get_turf(src),temperature, 0.3) - CheckHardness() - -/obj/structure/simple_door/Initialize(mapload, var/material_name) - . = ..() - set_material(material_name) - if(!material) - return INITIALIZE_HINT_QDEL - -/obj/structure/simple_door/Destroy() - STOP_PROCESSING(SSobj, src) - update_nearby_tiles() - return ..() - -/obj/structure/simple_door/proc/set_material(var/material_name) - if(!material_name) - material_name = MAT_STEEL - material = get_material_by_name(material_name) - if(!material) - return - hardness = max(1,round(material.integrity/10)) - icon_state = material.door_icon_base - name = "[material.display_name] door" - color = material.icon_colour - if(material.opacity < 0.5) - set_opacity(0) - else - set_opacity(1) - if(material.products_need_process()) - START_PROCESSING(SSobj, src) - update_nearby_tiles(need_rebuild=1) - -/obj/structure/simple_door/get_material() - return material - -/obj/structure/simple_door/Bumped(atom/user) - ..() - if(!state) - return TryToSwitchState(user) - return - -/obj/structure/simple_door/attack_ai(mob/user as mob) //those aren't machinery, they're just big fucking slabs of a mineral - if(isAI(user)) //so the AI can't open it - return - else if(isrobot(user)) //but cyborgs can - if(get_dist(user,src) <= 1) //not remotely though - return TryToSwitchState(user) - -/obj/structure/simple_door/attack_hand(mob/user as mob) - return TryToSwitchState(user) - -/obj/structure/simple_door/AltClick(mob/user as mob) - . = ..() - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(!Adjacent(user)) - return - else if(user.a_intent == I_HURT) - src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") - src.add_fingerprint(user) - playsound(src, knock_hammer_sound, 50, 0, 3) - else if(user.a_intent == I_HELP) - src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") - src.add_fingerprint(user) - playsound(src, knock_sound, 50, 0, 3) - return - -/obj/structure/simple_door/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /obj/effect/beam)) - return !opacity - return !density - -/obj/structure/simple_door/proc/TryToSwitchState(atom/user) - if(isSwitchingStates) return - if(ismob(user)) - var/mob/M = user - if(!material.can_open_material_door(user)) - return - if(world.time - user.last_bumped <= 60) - return - if(M.client) - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(!C.handcuffed) - SwitchState() - else - SwitchState() - else if(istype(user, /obj/mecha)) - SwitchState() - -/obj/structure/simple_door/proc/SwitchState() - if(state) - Close() - else - Open() - -/obj/structure/simple_door/proc/Open() - isSwitchingStates = 1 - playsound(src, material.dooropen_noise, 100, 1) - flick("[material.door_icon_base]opening",src) - sleep(10) - density = FALSE - set_opacity(0) - state = 1 - update_icon() - isSwitchingStates = 0 - update_nearby_tiles() - -/obj/structure/simple_door/proc/Close() - isSwitchingStates = 1 - playsound(src, material.dooropen_noise, 100, 1) - flick("[material.door_icon_base]closing",src) - sleep(10) - density = TRUE - set_opacity(1) - state = 0 - update_icon() - isSwitchingStates = 0 - update_nearby_tiles() - -/obj/structure/simple_door/update_icon() - if(state) - icon_state = "[material.door_icon_base]open" - else - icon_state = material.door_icon_base - -/obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(istype(W,/obj/item/weapon/pickaxe)) - var/obj/item/weapon/pickaxe/digTool = W - visible_message("[user] starts digging [src]!") - if(do_after(user,digTool.digspeed*hardness) && src) - visible_message("[user] finished digging [src]!") - Dismantle() - else if(istype(W,/obj/item/weapon)) //not sure, can't not just weapons get passed to this proc? - hardness -= W.force/10 - visible_message("[user] hits [src] with [W]!") - if(material == get_material_by_name("resin")) - playsound(src, 'sound/effects/attackblob.ogg', 100, 1) - else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) - playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) - else - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - CheckHardness() - else if(istype(W,/obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(material.ignition_point && WT.remove_fuel(0, user)) - TemperatureAct(150) - else - attack_hand(user) - return - -/obj/structure/simple_door/bullet_act(var/obj/item/projectile/Proj) - take_damage(Proj.damage/10) - CheckHardness() - -/obj/structure/simple_door/take_damage(var/damage) - hardness -= damage/10 - CheckHardness() - -/obj/structure/simple_door/attack_generic(var/mob/user, var/damage, var/attack_verb) - visible_message("[user] [attack_verb] the [src]!") - if(material == get_material_by_name("resin")) - playsound(src, 'sound/effects/attackblob.ogg', 100, 1) - else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) - playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) - else - playsound(src, 'sound/weapons/smash.ogg', 50, 1) - user.do_attack_animation(src) - hardness -= damage/10 - CheckHardness() - -/obj/structure/simple_door/proc/CheckHardness() - if(hardness <= 0) - Dismantle(1) - -/obj/structure/simple_door/proc/Dismantle(devastated = 0) - material.place_dismantled_product(get_turf(src)) - visible_message("The [src] is destroyed!") - qdel(src) - -/obj/structure/simple_door/ex_act(severity = 1) - switch(severity) - if(1) - Dismantle(1) - if(2) - if(prob(20)) - Dismantle(1) - else - hardness-- - CheckHardness() - if(3) - hardness -= 0.1 - CheckHardness() - return - -/obj/structure/simple_door/process() - if(!material.radioactivity) - return - SSradiation.radiate(src, round(material.radioactivity/3)) - -/obj/structure/simple_door/iron/Initialize(mapload,var/material_name) - ..(mapload, material_name || "iron") - -/obj/structure/simple_door/silver/Initialize(mapload,var/material_name) - ..(mapload, material_name || "silver") - -/obj/structure/simple_door/gold/Initialize(mapload,var/material_name) - ..(mapload, material_name || "gold") - -/obj/structure/simple_door/uranium/Initialize(mapload,var/material_name) - ..(mapload, material_name || "uranium") - -/obj/structure/simple_door/sandstone/Initialize(mapload,var/material_name) - ..(mapload, material_name || "sandstone") - -/obj/structure/simple_door/phoron/Initialize(mapload,var/material_name) - ..(mapload, material_name || "phoron") - -/obj/structure/simple_door/diamond/Initialize(mapload,var/material_name) - ..(mapload, material_name || "diamond") - -/obj/structure/simple_door/wood/Initialize(mapload,var/material_name) - ..(mapload, material_name || MAT_WOOD) - knock_sound = 'sound/machines/door/knock_wood.wav' - -/obj/structure/simple_door/hardwood/Initialize(mapload,var/material_name) - ..(mapload, material_name || MAT_HARDWOOD) - -/obj/structure/simple_door/sifwood/Initialize(mapload,var/material_name) - ..(mapload, material_name || MAT_SIFWOOD) - -/obj/structure/simple_door/resin/Initialize(mapload,var/material_name) - ..(mapload, material_name || "resin") - -/obj/structure/simple_door/cult/Initialize(mapload,var/material_name) - ..(mapload, material_name || "cult") - -/obj/structure/simple_door/cult/TryToSwitchState(atom/user) - if(isliving(user)) - var/mob/living/L = user - if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) - return - ..() +/obj/structure/simple_door + name = "door" + description_info = "If you hold left alt whilst left-clicking on a door, you can knock on it to announce your presence to anyone on the other side! Alternately if you are on HARM intent when doing this, you will bang loudly on the door!" + density = TRUE + anchored = TRUE + can_atmos_pass = ATMOS_PASS_DENSITY + + icon = 'icons/obj/doors/material_doors.dmi' + icon_state = "metal" + + var/datum/material/material + var/state = 0 //closed, 1 == open + var/isSwitchingStates = 0 + var/hardness = 1 + var/oreAmount = 7 + var/knock_sound = 'sound/machines/door/knock_glass.ogg' + var/knock_hammer_sound = 'sound/weapons/sonic_jackhammer.ogg' + + var/locked = FALSE //has the door been locked? + var/lock_id = null //does the door have an associated key? + var/keysound = 'sound/items/toolbelt_equip.ogg' + +/obj/structure/simple_door/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + TemperatureAct(exposed_temperature) + +/obj/structure/simple_door/proc/TemperatureAct(temperature) + hardness -= material.combustion_effect(get_turf(src),temperature, 0.3) + CheckHardness() + +/obj/structure/simple_door/Initialize(mapload, var/material_name) + . = ..() + set_material(material_name) + if(!material) + return INITIALIZE_HINT_QDEL + +/obj/structure/simple_door/Destroy() + STOP_PROCESSING(SSobj, src) + update_nearby_tiles() + return ..() + +/obj/structure/simple_door/proc/set_material(var/material_name) + if(!material_name) + material_name = MAT_STEEL + material = get_material_by_name(material_name) + if(!material) + return + hardness = max(1,round(material.integrity/10)) + icon_state = material.door_icon_base + name = "[material.display_name] door" + color = material.icon_colour + if(material.opacity < 0.5) + set_opacity(0) + else + set_opacity(1) + if(material.products_need_process()) + START_PROCESSING(SSobj, src) + update_nearby_tiles(need_rebuild=1) + +/obj/structure/simple_door/get_material() + return material + +/obj/structure/simple_door/Bumped(atom/user) + ..() + if(!state) + return TryToSwitchState(user) + return + +/obj/structure/simple_door/attack_ai(mob/user as mob) //those aren't machinery, they're just big fucking slabs of a mineral + if(isAI(user)) //so the AI can't open it + return + else if(isrobot(user)) //but cyborgs can + if(get_dist(user,src) <= 1) //not remotely though + return TryToSwitchState(user) + +/obj/structure/simple_door/attack_hand(mob/user as mob) + return TryToSwitchState(user) + +/obj/structure/simple_door/AltClick(mob/user as mob) + . = ..() + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(!Adjacent(user)) + return + else if(user.a_intent == I_HURT) + src.visible_message("[user] hammers on \the [src]!", "Someone hammers loudly on \the [src]!") + src.add_fingerprint(user) + playsound(src, knock_hammer_sound, 50, 0, 3) + else if(user.a_intent == I_HELP) + src.visible_message("[user] knocks on \the [src].", "Someone knocks on \the [src].") + src.add_fingerprint(user) + playsound(src, knock_sound, 50, 0, 3) + return + +/obj/structure/simple_door/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /obj/effect/beam)) + return !opacity + return !density + +/obj/structure/simple_door/proc/TryToSwitchState(atom/user) + if(isSwitchingStates) return + if(ismob(user)) + var/mob/M = user + if(!material.can_open_material_door(user)) + return + if(locked && state == 0) + to_chat(M,"It's locked!") + return + if(world.time - user.last_bumped <= 60) + return + if(M.client) + if(iscarbon(M)) + var/mob/living/carbon/C = M + if(!C.handcuffed) + SwitchState() + else + SwitchState() + else if(istype(user, /obj/mecha)) + SwitchState() + +/obj/structure/simple_door/proc/SwitchState() + if(state) + Close() + else + Open() + +/obj/structure/simple_door/proc/Open() + isSwitchingStates = 1 + playsound(src, material.dooropen_noise, 100, 1) + flick("[material.door_icon_base]opening",src) + sleep(10) + density = FALSE + set_opacity(0) + state = 1 + update_icon() + isSwitchingStates = 0 + update_nearby_tiles() + +/obj/structure/simple_door/proc/Close() + isSwitchingStates = 1 + playsound(src, material.dooropen_noise, 100, 1) + flick("[material.door_icon_base]closing",src) + sleep(10) + density = TRUE + set_opacity(1) + state = 0 + update_icon() + isSwitchingStates = 0 + update_nearby_tiles() + +/obj/structure/simple_door/update_icon() + if(state) + icon_state = "[material.door_icon_base]open" + else + icon_state = material.door_icon_base + +/obj/structure/simple_door/attackby(obj/item/weapon/W as obj, mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(istype(W,/obj/item/weapon/simple_key)) + var/obj/item/weapon/simple_key/key = W + if(state) + to_chat(user,"\The [src] must be closed in order for you to lock it.") + else if(key.key_id != src.lock_id) + to_chat(user,"The [key] doesn't fit \the [src]'s lock!") + else if(key.key_id == src.lock_id) + visible_message("[user] [key.keyverb] \the [key] and [locked ? "unlocks" : "locks"] \the [src].") + locked = !locked + playsound(src, keysound,100, 1) + return + if(istype(W,/obj/item/weapon/pickaxe) && breakable) + var/obj/item/weapon/pickaxe/digTool = W + visible_message("[user] starts digging [src]!") + if(do_after(user,digTool.digspeed*hardness) && src) + visible_message("[user] finished digging [src]!") + Dismantle() + else if(istype(W,/obj/item/weapon) && breakable) //not sure, can't not just weapons get passed to this proc? + hardness -= W.force/10 + visible_message("[user] hits [src] with [W]!") + if(material == get_material_by_name("resin")) + playsound(src, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) + playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + CheckHardness() + else if(W.has_tool_quality(TOOL_WELDER) && breakable) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(material.ignition_point && WT.remove_fuel(0, user)) + TemperatureAct(150) + else + attack_hand(user) + return + +/obj/structure/simple_door/bullet_act(var/obj/item/projectile/Proj) + take_damage(Proj.damage/10) + CheckHardness() + +/obj/structure/simple_door/take_damage(var/damage) + hardness -= damage/10 + CheckHardness() + +/obj/structure/simple_door/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + if(material == get_material_by_name("resin")) + playsound(src, 'sound/effects/attackblob.ogg', 100, 1) + else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD) || get_material_by_name(MAT_HARDWOOD))) + playsound(src, 'sound/effects/woodcutting.ogg', 100, 1) + else + playsound(src, 'sound/weapons/smash.ogg', 50, 1) + user.do_attack_animation(src) + hardness -= damage/10 + CheckHardness() + +/obj/structure/simple_door/proc/CheckHardness() + if(hardness <= 0) + Dismantle(1) + +/obj/structure/simple_door/proc/Dismantle(devastated = 0) + material.place_dismantled_product(get_turf(src)) + visible_message("The [src] is destroyed!") + qdel(src) + +/obj/structure/simple_door/ex_act(severity = 1) + switch(severity) + if(1) + Dismantle(1) + if(2) + if(prob(20)) + Dismantle(1) + else + hardness-- + CheckHardness() + if(3) + hardness -= 0.1 + CheckHardness() + return + +/obj/structure/simple_door/process() + if(!material.radioactivity) + return + SSradiation.radiate(src, round(material.radioactivity/3)) + +/obj/structure/simple_door/iron/Initialize(mapload,var/material_name) + ..(mapload, material_name || "iron") + +/obj/structure/simple_door/silver/Initialize(mapload,var/material_name) + ..(mapload, material_name || "silver") + +/obj/structure/simple_door/gold/Initialize(mapload,var/material_name) + ..(mapload, material_name || "gold") + +/obj/structure/simple_door/uranium/Initialize(mapload,var/material_name) + ..(mapload, material_name || "uranium") + +/obj/structure/simple_door/sandstone/Initialize(mapload,var/material_name) + ..(mapload, material_name || "sandstone") + +/obj/structure/simple_door/phoron/Initialize(mapload,var/material_name) + ..(mapload, material_name || "phoron") + +/obj/structure/simple_door/diamond/Initialize(mapload,var/material_name) + ..(mapload, material_name || "diamond") + +/obj/structure/simple_door/wood/Initialize(mapload,var/material_name) + ..(mapload, material_name || MAT_WOOD) + knock_sound = 'sound/machines/door/knock_wood.wav' + +/obj/structure/simple_door/hardwood/Initialize(mapload,var/material_name) + ..(mapload, material_name || MAT_HARDWOOD) + +/obj/structure/simple_door/sifwood/Initialize(mapload,var/material_name) + ..(mapload, material_name || MAT_SIFWOOD) + +/obj/structure/simple_door/resin/Initialize(mapload,var/material_name) + ..(mapload, material_name || "resin") + +/obj/structure/simple_door/cult/Initialize(mapload,var/material_name) + ..(mapload, material_name || "cult") + +/obj/structure/simple_door/cult/TryToSwitchState(atom/user) + if(isliving(user)) + var/mob/living/L = user + if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) + return + ..() diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index 1f24a359454..4c0a3aea4e0 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -1,387 +1,387 @@ -/* Beds... get your mind out of the gutter, they're for sleeping! - * Contains: - * Beds - * Roller beds - */ - -/* - * Beds - */ -/obj/structure/bed - name = "bed" - desc = "This is used to lie in, sleep in or strap on." - icon = 'icons/obj/furniture.dmi' - icon_state = "bed" - pressure_resistance = 15 - anchored = TRUE - can_buckle = TRUE - buckle_dir = SOUTH - buckle_lying = 1 - var/datum/material/material - var/datum/material/padding_material - var/base_icon = "bed" - var/applies_material_colour = 1 - -/obj/structure/bed/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc) - color = null - if(!new_material) - new_material = MAT_STEEL - material = get_material_by_name(new_material) - if(!istype(material)) - qdel(src) - return - if(new_padding_material) - padding_material = get_material_by_name(new_padding_material) - update_icon() - -/obj/structure/bed/get_material() - return material - -// Reuse the cache/code from stools, todo maybe unify. -/obj/structure/bed/update_icon() - // Prep icon. - icon_state = "" - cut_overlays() - // Base icon. - var/cache_key = "[base_icon]-[material.name]" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, base_icon) - if(applies_material_colour) //VOREStation Add - Goes with added var - I.color = material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - // Padding overlay. - if(padding_material) - var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" - if(isnull(stool_cache[padding_cache_key])) - var/image/I = image(icon, "[base_icon]_padding") - I.color = padding_material.icon_colour - stool_cache[padding_cache_key] = I - add_overlay(stool_cache[padding_cache_key]) - // Strings. - desc = initial(desc) - if(padding_material) - name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. - desc += " It's made of [material.use_name] and covered with [padding_material.use_name]." - else - name = "[material.display_name] [initial(name)]" - desc += " It's made of [material.use_name]." - -/obj/structure/bed/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSTABLE)) - return TRUE - return ..() - -/obj/structure/bed/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(50)) - qdel(src) - return - if(3.0) - if (prob(5)) - qdel(src) - return - -/obj/structure/bed/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - playsound(src, W.usesound, 50, 1) - dismantle() - qdel(src) - else if(istype(W,/obj/item/stack)) - if(padding_material) - to_chat(user, "\The [src] is already padded.") - return - var/obj/item/stack/C = W - if(C.get_amount() < 1) // How?? - user.drop_from_inventory(C) - qdel(C) - return - var/padding_type //This is awful but it needs to be like this until tiles are given a material var. - if(istype(W,/obj/item/stack/tile/carpet)) - padding_type = "carpet" - else if(istype(W,/obj/item/stack/material)) - var/obj/item/stack/material/M = W - if(M.material && (M.material.flags & MATERIAL_PADDING)) - padding_type = "[M.material.name]" - if(!padding_type) - to_chat(user, "You cannot pad \the [src] with that.") - return - C.use(1) - if(!istype(src.loc, /turf)) - user.drop_from_inventory(src) - src.loc = get_turf(src) - to_chat(user, "You add padding to \the [src].") - add_padding(padding_type) - return - - else if(W.is_wirecutter()) - if(!padding_material) - to_chat(user, "\The [src] has no padding to remove.") - return - to_chat(user, "You remove the padding from \the [src].") - playsound(src, W.usesound, 100, 1) - remove_padding() - - else if(istype(W, /obj/item/weapon/disk) || (istype(W, /obj/item/toy/plushie))) - user.drop_from_inventory(W, get_turf(src)) - W.pixel_x = 10 //make sure they reach the pillow - W.pixel_y = -6 - if(istype(W, /obj/item/weapon/disk)) - user.visible_message("[src] sleeps soundly. Sleep tight, disky.") - - else if(istype(W, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = W - var/mob/living/affecting = G.affecting - if(has_buckled_mobs()) //Handles trying to buckle someone else to a chair when someone else is on it - to_chat(user, "\The [src] already has someone buckled to it.") - return - user.visible_message("[user] attempts to buckle [affecting] into \the [src]!") - if(do_after(user, 20, G.affecting)) - affecting.loc = loc - spawn(0) - if(buckle_mob(affecting)) - affecting.visible_message(\ - "[affecting.name] is buckled to [src] by [user.name]!",\ - "You are buckled to [src] by [user.name]!",\ - "You hear metal clanking.") - qdel(W) - else - ..() - -/obj/structure/bed/proc/remove_padding() - if(padding_material) - padding_material.place_sheet(get_turf(src)) - padding_material = null - update_icon() - -/obj/structure/bed/proc/add_padding(var/padding_type) - padding_material = get_material_by_name(padding_type) - update_icon() - -/obj/structure/bed/proc/dismantle() - material.place_sheet(get_turf(src)) - if(padding_material) - padding_material.place_sheet(get_turf(src)) - -/obj/structure/bed/psych - name = "psychiatrist's couch" - desc = "For prime comfort during psychiatric evaluations." - icon_state = "psychbed" - base_icon = "psychbed" - -/obj/structure/bed/psych/New(var/newloc) - ..(newloc,"wood","leather") - -/obj/structure/bed/padded/New(var/newloc) - ..(newloc,"plastic","cotton") - -/obj/structure/bed/double - name = "double bed" - icon_state = "doublebed" - base_icon = "doublebed" - -/obj/structure/bed/double/padded/New(var/newloc) - ..(newloc,"wood","cotton") - -/obj/structure/bed/double/post_buckle_mob(mob/living/M as mob) - if(M.buckled == src) - M.pixel_y = 13 - M.old_y = 13 - else - M.pixel_y = 0 - M.old_y = 0 - -/* - * Roller beds - */ -/obj/structure/bed/roller - name = "roller bed" - desc = "A portable bed-on-wheels made for transporting medical patients." - icon = 'icons/obj/rollerbed.dmi' - icon_state = "rollerbed" - anchored = FALSE - surgery_odds = 50 //VOREStation Edit - var/bedtype = /obj/structure/bed/roller - var/rollertype = /obj/item/roller - -/obj/structure/bed/roller/adv - name = "advanced roller bed" - icon_state = "rollerbedadv" - bedtype = /obj/structure/bed/roller/adv - rollertype = /obj/item/roller/adv - -/obj/structure/bed/roller/update_icon() - return - -/obj/structure/bed/roller/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench() || istype(W,/obj/item/stack) || W.is_wirecutter()) - return - else if(istype(W,/obj/item/roller_holder)) - if(has_buckled_mobs()) - for(var/A in buckled_mobs) - user_unbuckle_mob(A, user) - else - visible_message("[user] collapses \the [src.name].") - new rollertype(get_turf(src)) - spawn(0) - qdel(src) - return - ..() - -/obj/item/roller - name = "roller bed" - desc = "A collapsed roller bed that can be carried around." - icon = 'icons/obj/rollerbed.dmi' - icon_state = "folded_rollerbed" - center_of_mass = list("x" = 17,"y" = 7) - slot_flags = SLOT_BACK - w_class = ITEMSIZE_LARGE - var/rollertype = /obj/item/roller - var/bedtype = /obj/structure/bed/roller - drop_sound = 'sound/items/drop/axe.ogg' - pickup_sound = 'sound/items/pickup/axe.ogg' - -/obj/item/roller/attack_self(mob/user) - var/obj/structure/bed/roller/R = new bedtype(user.loc) - R.add_fingerprint(user) - qdel(src) - -/obj/item/roller/attackby(obj/item/weapon/W as obj, mob/user as mob) - - if(istype(W,/obj/item/roller_holder)) - var/obj/item/roller_holder/RH = W - if(!RH.held) - to_chat(user, "You collect the roller bed.") - src.loc = RH - RH.held = src - return - - ..() - -/obj/item/roller/adv - name = "advanced roller bed" - desc = "A high-tech, compact version of the regular roller bed." - icon_state = "folded_rollerbedadv" - w_class = ITEMSIZE_NORMAL - rollertype = /obj/item/roller/adv - bedtype = /obj/structure/bed/roller/adv - -/obj/item/roller_holder - name = "roller bed rack" - desc = "A rack for carrying a collapsed roller bed." - icon = 'icons/obj/rollerbed.dmi' - icon_state = "rollerbed" - var/obj/item/roller/held - -/obj/item/roller_holder/New() - ..() - held = new /obj/item/roller(src) - -/obj/item/roller_holder/attack_self(mob/user as mob) - - if(!held) - to_chat(user, "The rack is empty.") - return - - to_chat(user, "You deploy the roller bed.") - var/obj/structure/bed/roller/R = new held.bedtype(user.loc) - R.add_fingerprint(user) - qdel(held) - held = null - - -/obj/structure/bed/roller/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - - playsound(src, 'sound/effects/roll.ogg', 100, 1) - -/obj/structure/bed/roller/post_buckle_mob(mob/living/M as mob) - if(M.buckled == src) - M.pixel_y = 6 - M.old_y = 6 - density = TRUE - icon_state = "[initial(icon_state)]_up" - else - M.pixel_y = 0 - M.old_y = 0 - density = FALSE - icon_state = "[initial(icon_state)]" - update_icon() - return ..() - -/obj/structure/bed/roller/MouseDrop(over_object, src_location, over_location) - ..() - if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) - if(!ishuman(usr)) return - if(has_buckled_mobs()) return 0 - visible_message("[usr] collapses \the [src.name].") - new rollertype(get_turf(src)) - spawn(0) - qdel(src) - return - -/datum/category_item/catalogue/anomalous/precursor_a/alien_bed - name = "Precursor Alpha Object - Resting Contraption" - desc = "This appears to be a relatively long and flat object, with the top side being made of \ - an soft material, giving it very similar characteristics to an ordinary bed. If this object was \ - designed to act as a bed, this carries several implications for whatever species had built it, such as;\ -

                    \ - Being capable of experiencing comfort, or at least being able to suffer from some form of fatigue.
                    \ - Developing while under the influence of gravitational forces, to be able to 'lie' on the object.
                    \ - Being within a range of sizes in order for the object to function as a bed. Too small, and the species \ - would be unable to reach the top of the object. Too large, and they would have little room to contact \ - the top side of the object.
                    \ -

                    \ - As a note, the size of this object appears to be within the bounds for an average human to be able to \ - rest comfortably on top of it." - value = CATALOGUER_REWARD_EASY - -/obj/structure/bed/alien - name = "resting contraption" - desc = "Whatever species designed this must've enjoyed relaxation as well. Looks vaguely comfy." - catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_bed) - icon = 'icons/obj/abductor.dmi' - icon_state = "bed" - -/obj/structure/bed/alien/update_icon() - return // Doesn't care about material or anything else. - -/obj/structure/bed/alien/attackby(obj/item/weapon/W, mob/user) - return // No deconning. - -/* - * Dirty Mattress - */ -/obj/structure/dirtybed - name = "dirty mattress" - desc = "A stained matress. Guess it's better than sleeping on the floor." - icon = 'icons/obj/furniture.dmi' - icon_state = "dirtybed" - pressure_resistance = 15 - anchored = TRUE - can_buckle = TRUE - buckle_dir = SOUTH - buckle_lying = 1 - -/obj/structure/dirtybed/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - playsound(src, W.usesound, 100, 1) - if(anchored) - user.visible_message("[user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") - else - user.visible_message("[user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") - - if(do_after(user, 20 * W.toolspeed)) - if(!src) return - to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") - anchored = !anchored - return - - if(!anchored) - to_chat(user," The bed isn't secured.") - return \ No newline at end of file +/* Beds... get your mind out of the gutter, they're for sleeping! + * Contains: + * Beds + * Roller beds + */ + +/* + * Beds + */ +/obj/structure/bed + name = "bed" + desc = "This is used to lie in, sleep in or strap on." + icon = 'icons/obj/furniture.dmi' + icon_state = "bed" + pressure_resistance = 15 + anchored = TRUE + can_buckle = TRUE + buckle_dir = SOUTH + buckle_lying = 1 + var/datum/material/material + var/datum/material/padding_material + var/base_icon = "bed" + var/applies_material_colour = 1 + +/obj/structure/bed/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc) + color = null + if(!new_material) + new_material = MAT_STEEL + material = get_material_by_name(new_material) + if(!istype(material)) + qdel(src) + return + if(new_padding_material) + padding_material = get_material_by_name(new_padding_material) + update_icon() + +/obj/structure/bed/get_material() + return material + +// Reuse the cache/code from stools, todo maybe unify. +/obj/structure/bed/update_icon() + // Prep icon. + icon_state = "" + cut_overlays() + // Base icon. + var/cache_key = "[base_icon]-[material.name]" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, base_icon) + if(applies_material_colour) //VOREStation Add - Goes with added var + I.color = material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + // Padding overlay. + if(padding_material) + var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" + if(isnull(stool_cache[padding_cache_key])) + var/image/I = image(icon, "[base_icon]_padding") + I.color = padding_material.icon_colour + stool_cache[padding_cache_key] = I + add_overlay(stool_cache[padding_cache_key]) + // Strings. + desc = initial(desc) + if(padding_material) + name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. + desc += " It's made of [material.use_name] and covered with [padding_material.use_name]." + else + name = "[material.display_name] [initial(name)]" + desc += " It's made of [material.use_name]." + +/obj/structure/bed/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + return ..() + +/obj/structure/bed/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(50)) + qdel(src) + return + if(3.0) + if (prob(5)) + qdel(src) + return + +/obj/structure/bed/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + dismantle() + qdel(src) + else if(istype(W,/obj/item/stack)) + if(padding_material) + to_chat(user, "\The [src] is already padded.") + return + var/obj/item/stack/C = W + if(C.get_amount() < 1) // How?? + user.drop_from_inventory(C) + qdel(C) + return + var/padding_type //This is awful but it needs to be like this until tiles are given a material var. + if(istype(W,/obj/item/stack/tile/carpet)) + padding_type = "carpet" + else if(istype(W,/obj/item/stack/material)) + var/obj/item/stack/material/M = W + if(M.material && (M.material.flags & MATERIAL_PADDING)) + padding_type = "[M.material.name]" + if(!padding_type) + to_chat(user, "You cannot pad \the [src] with that.") + return + C.use(1) + if(!istype(src.loc, /turf)) + user.drop_from_inventory(src) + src.loc = get_turf(src) + to_chat(user, "You add padding to \the [src].") + add_padding(padding_type) + return + + else if(W.has_tool_quality(TOOL_WIRECUTTER)) + if(!padding_material) + to_chat(user, "\The [src] has no padding to remove.") + return + to_chat(user, "You remove the padding from \the [src].") + playsound(src, W.usesound, 100, 1) + remove_padding() + + else if(istype(W, /obj/item/weapon/disk) || (istype(W, /obj/item/toy/plushie))) + user.drop_from_inventory(W, get_turf(src)) + W.pixel_x = 10 //make sure they reach the pillow + W.pixel_y = -6 + if(istype(W, /obj/item/weapon/disk)) + user.visible_message("[src] sleeps soundly. Sleep tight, disky.") + + else if(istype(W, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + var/mob/living/affecting = G.affecting + if(has_buckled_mobs()) //Handles trying to buckle someone else to a chair when someone else is on it + to_chat(user, "\The [src] already has someone buckled to it.") + return + user.visible_message("[user] attempts to buckle [affecting] into \the [src]!") + if(do_after(user, 20, G.affecting)) + affecting.loc = loc + spawn(0) + if(buckle_mob(affecting)) + affecting.visible_message(\ + "[affecting.name] is buckled to [src] by [user.name]!",\ + "You are buckled to [src] by [user.name]!",\ + "You hear metal clanking.") + qdel(W) + else + ..() + +/obj/structure/bed/proc/remove_padding() + if(padding_material) + padding_material.place_sheet(get_turf(src), 1) + padding_material = null + update_icon() + +/obj/structure/bed/proc/add_padding(var/padding_type) + padding_material = get_material_by_name(padding_type) + update_icon() + +/obj/structure/bed/proc/dismantle() + material.place_sheet(get_turf(src), 1) + if(padding_material) + padding_material.place_sheet(get_turf(src), 1) + +/obj/structure/bed/psych + name = "psychiatrist's couch" + desc = "For prime comfort during psychiatric evaluations." + icon_state = "psychbed" + base_icon = "psychbed" + +/obj/structure/bed/psych/New(var/newloc) + ..(newloc,"wood","leather") + +/obj/structure/bed/padded/New(var/newloc) + ..(newloc,"plastic","cotton") + +/obj/structure/bed/double + name = "double bed" + icon_state = "doublebed" + base_icon = "doublebed" + +/obj/structure/bed/double/padded/New(var/newloc) + ..(newloc,"wood","cotton") + +/obj/structure/bed/double/post_buckle_mob(mob/living/M as mob) + if(M.buckled == src) + M.pixel_y = 13 + M.old_y = 13 + else + M.pixel_y = 0 + M.old_y = 0 + +/* + * Roller beds + */ +/obj/structure/bed/roller + name = "roller bed" + desc = "A portable bed-on-wheels made for transporting medical patients." + icon = 'icons/obj/rollerbed.dmi' + icon_state = "rollerbed" + anchored = FALSE + surgery_odds = 50 //VOREStation Edit + var/bedtype = /obj/structure/bed/roller + var/rollertype = /obj/item/roller + +/obj/structure/bed/roller/adv + name = "advanced roller bed" + icon_state = "rollerbedadv" + bedtype = /obj/structure/bed/roller/adv + rollertype = /obj/item/roller/adv + +/obj/structure/bed/roller/update_icon() + return + +/obj/structure/bed/roller/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH) || istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) + return + else if(istype(W,/obj/item/roller_holder)) + if(has_buckled_mobs()) + for(var/A in buckled_mobs) + user_unbuckle_mob(A, user) + else + visible_message("[user] collapses \the [src.name].") + new rollertype(get_turf(src)) + spawn(0) + qdel(src) + return + ..() + +/obj/item/roller + name = "roller bed" + desc = "A collapsed roller bed that can be carried around." + icon = 'icons/obj/rollerbed.dmi' + icon_state = "folded_rollerbed" + center_of_mass = list("x" = 17,"y" = 7) + slot_flags = SLOT_BACK + w_class = ITEMSIZE_LARGE + var/rollertype = /obj/item/roller + var/bedtype = /obj/structure/bed/roller + drop_sound = 'sound/items/drop/axe.ogg' + pickup_sound = 'sound/items/pickup/axe.ogg' + +/obj/item/roller/attack_self(mob/user) + var/obj/structure/bed/roller/R = new bedtype(user.loc) + R.add_fingerprint(user) + qdel(src) + +/obj/item/roller/attackby(obj/item/weapon/W as obj, mob/user as mob) + + if(istype(W,/obj/item/roller_holder)) + var/obj/item/roller_holder/RH = W + if(!RH.held) + to_chat(user, "You collect the roller bed.") + src.loc = RH + RH.held = src + return + + ..() + +/obj/item/roller/adv + name = "advanced roller bed" + desc = "A high-tech, compact version of the regular roller bed." + icon_state = "folded_rollerbedadv" + w_class = ITEMSIZE_NORMAL + rollertype = /obj/item/roller/adv + bedtype = /obj/structure/bed/roller/adv + +/obj/item/roller_holder + name = "roller bed rack" + desc = "A rack for carrying a collapsed roller bed." + icon = 'icons/obj/rollerbed.dmi' + icon_state = "rollerbed" + var/obj/item/roller/held + +/obj/item/roller_holder/New() + ..() + held = new /obj/item/roller(src) + +/obj/item/roller_holder/attack_self(mob/user as mob) + + if(!held) + to_chat(user, "The rack is empty.") + return + + to_chat(user, "You deploy the roller bed.") + var/obj/structure/bed/roller/R = new held.bedtype(user.loc) + R.add_fingerprint(user) + qdel(held) + held = null + + +/obj/structure/bed/roller/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + + playsound(src, 'sound/effects/roll.ogg', 100, 1) + +/obj/structure/bed/roller/post_buckle_mob(mob/living/M as mob) + if(M.buckled == src) + M.pixel_y = 6 + M.old_y = 6 + density = TRUE + icon_state = "[initial(icon_state)]_up" + else + M.pixel_y = 0 + M.old_y = 0 + density = FALSE + icon_state = "[initial(icon_state)]" + update_icon() + return ..() + +/obj/structure/bed/roller/MouseDrop(over_object, src_location, over_location) + ..() + if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) + if(!ishuman(usr)) return + if(has_buckled_mobs()) return 0 + visible_message("[usr] collapses \the [src.name].") + new rollertype(get_turf(src)) + spawn(0) + qdel(src) + return + +/datum/category_item/catalogue/anomalous/precursor_a/alien_bed + name = "Precursor Alpha Object - Resting Contraption" + desc = "This appears to be a relatively long and flat object, with the top side being made of \ + an soft material, giving it very similar characteristics to an ordinary bed. If this object was \ + designed to act as a bed, this carries several implications for whatever species had built it, such as;\ +

                    \ + Being capable of experiencing comfort, or at least being able to suffer from some form of fatigue.
                    \ + Developing while under the influence of gravitational forces, to be able to 'lie' on the object.
                    \ + Being within a range of sizes in order for the object to function as a bed. Too small, and the species \ + would be unable to reach the top of the object. Too large, and they would have little room to contact \ + the top side of the object.
                    \ +

                    \ + As a note, the size of this object appears to be within the bounds for an average human to be able to \ + rest comfortably on top of it." + value = CATALOGUER_REWARD_EASY + +/obj/structure/bed/alien + name = "resting contraption" + desc = "Whatever species designed this must've enjoyed relaxation as well. Looks vaguely comfy." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_bed) + icon = 'icons/obj/abductor.dmi' + icon_state = "bed" + +/obj/structure/bed/alien/update_icon() + return // Doesn't care about material or anything else. + +/obj/structure/bed/alien/attackby(obj/item/weapon/W, mob/user) + return // No deconning. + +/* + * Dirty Mattress + */ +/obj/structure/dirtybed + name = "dirty mattress" + desc = "A stained matress. Guess it's better than sleeping on the floor." + icon = 'icons/obj/furniture.dmi' + icon_state = "dirtybed" + pressure_resistance = 15 + anchored = TRUE + can_buckle = TRUE + buckle_dir = SOUTH + buckle_lying = 1 + +/obj/structure/dirtybed/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 100, 1) + if(anchored) + user.visible_message("[user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") + else + user.visible_message("[user] begins securing \the [src] to the floor.", "You start securing \the [src] to the floor.") + + if(do_after(user, 20 * W.toolspeed)) + if(!src) return + to_chat(user, "You [anchored? "un" : ""]secured \the [src]!") + anchored = !anchored + return + + if(!anchored) + to_chat(user," The bed isn't secured.") + return diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 08b059888ac..afc64143107 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -1,549 +1,549 @@ -/obj/structure/bed/chair //YES, chairs are a type of bed, which are a type of stool. This works, believe me. -Pete - name = "chair" - desc = "You sit in this. Either by will or force." - icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - Using Eris furniture - icon_state = "chair_preview" - color = "#666666" - base_icon = "chair" - buckle_dir = 0 - buckle_lying = 0 //force people to sit up in chairs when buckled - var/propelled = 0 // Check for fire-extinguisher-driven chairs - -/obj/structure/bed/chair/New(var/newloc, var/new_material, var/new_padding_material) - ..() - update_layer() - -/obj/structure/bed/chair/attackby(obj/item/weapon/W as obj, mob/user as mob) - ..() - if(!padding_material && istype(W, /obj/item/assembly/shock_kit)) - var/obj/item/assembly/shock_kit/SK = W - if(!SK.status) - to_chat(user, "\The [SK] is not ready to be attached!") - return - user.drop_item() - var/obj/structure/bed/chair/e_chair/E = new (src.loc, material.name) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - E.set_dir(dir) - E.part = SK - SK.loc = E - SK.master = E - qdel(src) - -/obj/structure/bed/chair/attack_tk(mob/user as mob) - if(has_buckled_mobs()) - ..() - else - rotate_clockwise() - return - -/obj/structure/bed/chair/post_buckle_mob() - update_icon() - -/obj/structure/bed/chair/update_icon() - ..() - if(has_buckled_mobs()) - var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, "[base_icon]_armrest") - I.plane = MOB_PLANE - I.layer = ABOVE_MOB_LAYER - if(padding_material) - I.color = padding_material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - -/obj/structure/bed/chair/proc/update_layer() - if(src.dir == NORTH) - plane = MOB_PLANE - layer = MOB_LAYER + 0.1 - else - reset_plane_and_layer() - -/obj/structure/bed/chair/set_dir() - ..() - update_layer() - if(has_buckled_mobs()) - for(var/mob/living/L as anything in buckled_mobs) - L.set_dir(dir) - -/obj/structure/bed/chair/verb/rotate_clockwise() - set name = "Rotate Chair Clockwise" - set category = "Object" - set src in oview(1) - - if(!usr || !isturf(usr.loc)) - return - if(usr.stat || usr.restrained()) - return - if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) - return - - src.set_dir(turn(src.dir, 270)) - -/obj/structure/bed/chair/verb/rotate_counterclockwise() - set name = "Rotate Chair Counter-Clockwise" - set category = "Object" - set src in oview(1) - - if(!usr || !isturf(usr.loc)) - return - if(usr.stat || usr.restrained()) - return - if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) - return - - src.set_dir(turn(src.dir, 90)) - -/obj/structure/bed/chair/shuttle - name = "chair" - icon_state = "shuttlechair" - base_icon = "shuttlechair" - color = null - applies_material_colour = 0 - -/obj/structure/bed/chair/shuttle_padded - icon_state = "shuttlechair2" - base_icon = "shuttlechair2" - color = null - applies_material_colour = 0 - -/obj/structure/bed/chair/comfy - name = "comfy chair" - desc = "It's a chair. It looks comfy." - icon_state = "comfychair" - base_icon = "comfychair" - -/obj/structure/bed/chair/comfy/update_icon() - ..() - var/image/I = image(icon, "[base_icon]_over") - I.layer = ABOVE_MOB_LAYER - I.plane = MOB_PLANE - I.color = material.icon_colour - add_overlay(I) - if(padding_material) - I = image(icon, "[base_icon]_padding_over") - I.layer = ABOVE_MOB_LAYER - I.plane = MOB_PLANE - I.color = padding_material.icon_colour - add_overlay(I) - -/obj/structure/bed/chair/comfy/brown/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, MAT_LEATHER) - -/obj/structure/bed/chair/comfy/red/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "carpet") - -/obj/structure/bed/chair/comfy/teal/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "teal") - -/obj/structure/bed/chair/comfy/black/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "black") - -/obj/structure/bed/chair/comfy/green/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "green") - -/obj/structure/bed/chair/comfy/purp/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "purple") - -/obj/structure/bed/chair/comfy/blue/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "blue") - -/obj/structure/bed/chair/comfy/beige/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "beige") - -/obj/structure/bed/chair/comfy/lime/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "lime") - -/obj/structure/bed/chair/comfy/yellow/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "yellow") - -/obj/structure/bed/chair/comfy/orange/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "orange") - -/obj/structure/bed/chair/comfy/rounded - name = "rounded chair" - desc = "It's a rounded chair. It looks comfy." - icon_state = "roundedchair" - base_icon = "roundedchair" - -/obj/structure/bed/chair/comfy/rounded/brown/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, MAT_LEATHER) - -/obj/structure/bed/chair/comfy/rounded/red/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "carpet") - -/obj/structure/bed/chair/comfy/rounded/teal/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "teal") - -/obj/structure/bed/chair/comfy/rounded/black/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "black") - -/obj/structure/bed/chair/comfy/rounded/green/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "green") - -/obj/structure/bed/chair/comfy/rounded/purple/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "purple") - -/obj/structure/bed/chair/comfy/rounded/blue/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "blue") - -/obj/structure/bed/chair/comfy/rounded/beige/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "beige") - -/obj/structure/bed/chair/comfy/rounded/lime/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "lime") - -/obj/structure/bed/chair/comfy/rounded/yellow/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "yellow") - -/obj/structure/bed/chair/comfy/rounded/orange/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc, MAT_STEEL, "orange") - -/obj/structure/bed/chair/office - anchored = FALSE - buckle_movable = 1 - -/obj/structure/bed/chair/office/update_icon() - return - -/obj/structure/bed/chair/office/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/stack) || W.is_wirecutter()) - return - ..() - -/obj/structure/bed/chair/office/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - - playsound(src, 'sound/effects/roll.ogg', 100, 1) - -/obj/structure/bed/chair/office/handle_buckled_mob_movement(atom/new_loc, direction, movetime) - for(var/mob/living/occupant as anything in buckled_mobs) - occupant.buckled = null - occupant.Move(loc, direction, movetime) - occupant.buckled = src - if (occupant && (loc != occupant.loc)) - if (propelled) - for (var/mob/O in src.loc) - if (O != occupant) - Bump(O) - else - unbuckle_mob() - -/obj/structure/bed/chair/office/Bump(atom/A) - ..() - if(!has_buckled_mobs()) return - - if(propelled) - for(var/a in buckled_mobs) - var/mob/living/occupant = unbuckle_mob(a) - - var/def_zone = ran_zone() - var/blocked = occupant.run_armor_check(def_zone, "melee") - var/soaked = occupant.get_armor_soak(def_zone, "melee") - occupant.throw_at(A, 3, propelled) - occupant.apply_effect(6, STUN, blocked) - occupant.apply_effect(6, WEAKEN, blocked) - occupant.apply_effect(6, STUTTER, blocked) - occupant.apply_damage(10, BRUTE, def_zone, blocked, soaked) - playsound(src, 'sound/weapons/punch1.ogg', 50, 1, -1) - if(istype(A, /mob/living)) - var/mob/living/victim = A - def_zone = ran_zone() - blocked = victim.run_armor_check(def_zone, "melee") - soaked = victim.get_armor_soak(def_zone, "melee") - victim.apply_effect(6, STUN, blocked) - victim.apply_effect(6, WEAKEN, blocked) - victim.apply_effect(6, STUTTER, blocked) - victim.apply_damage(10, BRUTE, def_zone, blocked, soaked) - occupant.visible_message("[occupant] crashed into \the [A]!") - -/obj/structure/bed/chair/office/light - icon_state = "officechair_white" - -/obj/structure/bed/chair/office/dark - icon_state = "officechair_dark" - -// Chair types -/obj/structure/bed/chair/wood - name = "wooden chair" - desc = "Old is never too old to not be in fashion." - icon_state = "wooden_chair" - -/obj/structure/bed/chair/wood/update_icon() - return - -/obj/structure/bed/chair/wood/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W,/obj/item/stack) || W.is_wirecutter()) - return - ..() - -/obj/structure/bed/chair/wood/New(var/newloc) - ..(newloc, "wood") - -/obj/structure/bed/chair/wood/wings - icon_state = "wooden_chair_wings" - -//sofa - -/obj/structure/bed/chair/sofa - name = "sofa" - desc = "It's a sofa. You sit on it. Possibly with someone else." - icon = 'icons/obj/sofas.dmi' - base_icon = "sofamiddle" - icon_state = "sofamiddle" - applies_material_colour = 1 - var/sofa_material = "carpet" - var/corner_piece = FALSE - -/obj/structure/bed/chair/sofa/update_icon() - if(applies_material_colour && sofa_material) - var/datum/material/color_material = get_material_by_name(sofa_material) - color = color_material.icon_colour - - if(sofa_material == "carpet") - name = "red [initial(name)]" - else - name = "[sofa_material] [initial(name)]" - -/obj/structure/bed/chair/sofa/update_layer() - // Corner east/west should be on top of mobs, any other state's north should be. - if(!corner_piece && (dir & NORTH)) - plane = MOB_PLANE - layer = MOB_LAYER + 0.1 - else - reset_plane_and_layer() - -/obj/structure/bed/chair/sofa/left - icon_state = "sofaend_left" - base_icon = "sofaend_left" - -/obj/structure/bed/chair/sofa/right - icon_state = "sofaend_right" - base_icon = "sofaend_right" - -/obj/structure/bed/chair/sofa/corner - icon_state = "sofacorner" - base_icon = "sofacorner" - corner_piece = TRUE - -/obj/structure/bed/chair/sofa/corner/update_icon() - ..() - var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]-permanent" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, "[base_icon]_armrest") - I.plane = MOB_PLANE - I.layer = ABOVE_MOB_LAYER - if(padding_material) - I.color = padding_material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - -// Wooden nonsofa - no corners -/obj/structure/bed/chair/sofa/pew - name = "pew bench" - desc = "If they want you to go to church, why do they make these so uncomfortable?" - base_icon = "pewmiddle" - icon_state = "pewmiddle" - applies_material_colour = FALSE - -/obj/structure/bed/chair/sofa/pew/left - icon_state = "pewend_left" - base_icon = "pewend_left" - -/obj/structure/bed/chair/sofa/pew/right - icon_state = "pewend_right" - base_icon = "pewend_right" - -// Metal benches from Skyrat -/obj/structure/bed/chair/sofa/bench - name = "metal bench" - desc = "Almost as comfortable as waiting at a bus station for hours on end." - base_icon = "benchmiddle" - icon_state = "benchmiddle" - applies_material_colour = FALSE - color = null - var/padding_color = "#CC0000" - -/obj/structure/bed/chair/sofa/bench/New(var/newloc, var/new_material, var/new_padding_material) - ..() - var/mutable_appearance/MA - // If we're north-facing, metal goes above mob, padding overlay goes below mob. - if((dir & NORTH) && !corner_piece) - plane = MOB_PLANE - layer = ABOVE_MOB_LAYER - MA = mutable_appearance(icon, icon_state = "o[icon_state]", layer = BELOW_MOB_LAYER, plane = MOB_PLANE, appearance_flags = KEEP_APART|RESET_COLOR) - // Else just normal plane and layer for everything, which will be below mobs. - else - MA = mutable_appearance(icon, icon_state = "o[icon_state]", appearance_flags = KEEP_APART|RESET_COLOR) - MA.color = padding_color - add_overlay(MA) - -/obj/structure/bed/chair/sofa/bench/left - icon_state = "bench_left" - base_icon = "bench_left" - -/obj/structure/bed/chair/sofa/bench/right - icon_state = "bench_right" - base_icon = "bench_right" - -/obj/structure/bed/chair/sofa/bench/corner - icon_state = "benchcorner" - base_icon = "benchcorner" - //corner_piece = TRUE // These sprites work fine without the parent doing layer shenanigans - -// Corporate sofa - one color fits all -/obj/structure/bed/chair/sofa/corp - name = "black leather sofa" - desc = "How corporate!" - base_icon = "corp_sofamiddle" - icon_state = "corp_sofamiddle" - applies_material_colour = FALSE - -/obj/structure/bed/chair/sofa/corp/left - icon_state = "corp_sofaend_left" - base_icon = "corp_sofaend_left" - -/obj/structure/bed/chair/sofa/corp/right - icon_state = "corp_sofaend_right" - base_icon = "corp_sofaend_right" - -/obj/structure/bed/chair/sofa/corp/corner - icon_state = "corp_sofacorner" - base_icon = "corp_sofacorner" - corner_piece = TRUE - -//color variations -//Middle sofas first -/obj/structure/bed/chair/sofa - sofa_material = "carpet" - -/obj/structure/bed/chair/sofa/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/orange - sofa_material = "orange" - -//sofa directions - -/obj/structure/bed/chair/sofa/left - icon_state = "sofaend_left" - -/obj/structure/bed/chair/sofa/right - icon_state = "sofaend_right" - -/obj/structure/bed/chair/sofa/corner - icon_state = "sofacorner" - -/obj/structure/bed/chair/sofa/left/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/right/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/corner/brown - sofa_material = "leather" - -/obj/structure/bed/chair/sofa/left/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/right/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/corner/teal - sofa_material = "teal" - -/obj/structure/bed/chair/sofa/left/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/right/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/corner/black - sofa_material = "black" - -/obj/structure/bed/chair/sofa/left/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/right/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/corner/green - sofa_material = "green" - -/obj/structure/bed/chair/sofa/left/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/right/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/corner/purp - sofa_material = "purple" - -/obj/structure/bed/chair/sofa/left/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/right/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/corner/blue - sofa_material = "blue" - -/obj/structure/bed/chair/sofa/left/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/right/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/corner/beige - sofa_material = "beige" - -/obj/structure/bed/chair/sofa/left/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/right/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/corner/lime - sofa_material = "lime" - -/obj/structure/bed/chair/sofa/left/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/right/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/corner/yellow - sofa_material = "yellow" - -/obj/structure/bed/chair/sofa/left/orange - sofa_material = "orange" - -/obj/structure/bed/chair/sofa/right/orange - sofa_material = "orange" - -/obj/structure/bed/chair/sofa/corner/orange - sofa_material = "orange" +/obj/structure/bed/chair //YES, chairs are a type of bed, which are a type of stool. This works, believe me. -Pete + name = "chair" + desc = "You sit in this. Either by will or force." + icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - Using Eris furniture + icon_state = "chair_preview" + color = "#666666" + base_icon = "chair" + buckle_dir = 0 + buckle_lying = 0 //force people to sit up in chairs when buckled + var/propelled = 0 // Check for fire-extinguisher-driven chairs + +/obj/structure/bed/chair/New(var/newloc, var/new_material, var/new_padding_material) + ..() + update_layer() + +/obj/structure/bed/chair/attackby(obj/item/weapon/W as obj, mob/user as mob) + ..() + if(!padding_material && istype(W, /obj/item/assembly/shock_kit)) + var/obj/item/assembly/shock_kit/SK = W + if(!SK.status) + to_chat(user, "\The [SK] is not ready to be attached!") + return + user.drop_item() + var/obj/structure/bed/chair/e_chair/E = new (src.loc, material.name) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + E.set_dir(dir) + E.part = SK + SK.loc = E + SK.master = E + qdel(src) + +/obj/structure/bed/chair/attack_tk(mob/user as mob) + if(has_buckled_mobs()) + ..() + else + rotate_clockwise() + return + +/obj/structure/bed/chair/post_buckle_mob() + update_icon() + +/obj/structure/bed/chair/update_icon() + ..() + if(has_buckled_mobs()) + var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_armrest") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(padding_material) + I.color = padding_material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + +/obj/structure/bed/chair/proc/update_layer() + if(src.dir == NORTH) + plane = MOB_PLANE + layer = MOB_LAYER + 0.1 + else + reset_plane_and_layer() + +/obj/structure/bed/chair/set_dir() + ..() + update_layer() + if(has_buckled_mobs()) + for(var/mob/living/L as anything in buckled_mobs) + L.set_dir(dir) + +/obj/structure/bed/chair/verb/rotate_clockwise() + set name = "Rotate Chair Clockwise" + set category = "Object" + set src in oview(1) + + if(!usr || !isturf(usr.loc)) + return + if(usr.stat || usr.restrained()) + return + if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) + return + + src.set_dir(turn(src.dir, 270)) + +/obj/structure/bed/chair/verb/rotate_counterclockwise() + set name = "Rotate Chair Counter-Clockwise" + set category = "Object" + set src in oview(1) + + if(!usr || !isturf(usr.loc)) + return + if(usr.stat || usr.restrained()) + return + if(ismouse(usr) || (isobserver(usr) && !config.ghost_interaction)) + return + + src.set_dir(turn(src.dir, 90)) + +/obj/structure/bed/chair/shuttle + name = "chair" + icon_state = "shuttlechair" + base_icon = "shuttlechair" + color = null + applies_material_colour = 0 + +/obj/structure/bed/chair/shuttle_padded + icon_state = "shuttlechair2" + base_icon = "shuttlechair2" + color = null + applies_material_colour = 0 + +/obj/structure/bed/chair/comfy + name = "comfy chair" + desc = "It's a chair. It looks comfy." + icon_state = "comfychair" + base_icon = "comfychair" + +/obj/structure/bed/chair/comfy/update_icon() + ..() + var/image/I = image(icon, "[base_icon]_over") + I.layer = ABOVE_MOB_LAYER + I.plane = MOB_PLANE + I.color = material.icon_colour + add_overlay(I) + if(padding_material) + I = image(icon, "[base_icon]_padding_over") + I.layer = ABOVE_MOB_LAYER + I.plane = MOB_PLANE + I.color = padding_material.icon_colour + add_overlay(I) + +/obj/structure/bed/chair/comfy/brown/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, MAT_LEATHER) + +/obj/structure/bed/chair/comfy/red/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "carpet") + +/obj/structure/bed/chair/comfy/teal/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "teal") + +/obj/structure/bed/chair/comfy/black/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "black") + +/obj/structure/bed/chair/comfy/green/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "green") + +/obj/structure/bed/chair/comfy/purp/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "purple") + +/obj/structure/bed/chair/comfy/blue/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "blue") + +/obj/structure/bed/chair/comfy/beige/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "beige") + +/obj/structure/bed/chair/comfy/lime/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "lime") + +/obj/structure/bed/chair/comfy/yellow/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "yellow") + +/obj/structure/bed/chair/comfy/orange/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "orange") + +/obj/structure/bed/chair/comfy/rounded + name = "rounded chair" + desc = "It's a rounded chair. It looks comfy." + icon_state = "roundedchair" + base_icon = "roundedchair" + +/obj/structure/bed/chair/comfy/rounded/brown/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, MAT_LEATHER) + +/obj/structure/bed/chair/comfy/rounded/red/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "carpet") + +/obj/structure/bed/chair/comfy/rounded/teal/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "teal") + +/obj/structure/bed/chair/comfy/rounded/black/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "black") + +/obj/structure/bed/chair/comfy/rounded/green/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "green") + +/obj/structure/bed/chair/comfy/rounded/purple/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "purple") + +/obj/structure/bed/chair/comfy/rounded/blue/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "blue") + +/obj/structure/bed/chair/comfy/rounded/beige/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "beige") + +/obj/structure/bed/chair/comfy/rounded/lime/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "lime") + +/obj/structure/bed/chair/comfy/rounded/yellow/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "yellow") + +/obj/structure/bed/chair/comfy/rounded/orange/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, MAT_STEEL, "orange") + +/obj/structure/bed/chair/office + anchored = FALSE + buckle_movable = 1 + +/obj/structure/bed/chair/office/update_icon() + return + +/obj/structure/bed/chair/office/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) + return + ..() + +/obj/structure/bed/chair/office/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + + playsound(src, 'sound/effects/roll.ogg', 100, 1) + +/obj/structure/bed/chair/office/handle_buckled_mob_movement(atom/new_loc, direction, movetime) + for(var/mob/living/occupant as anything in buckled_mobs) + occupant.buckled = null + occupant.Move(loc, direction, movetime) + occupant.buckled = src + if (occupant && (loc != occupant.loc)) + if (propelled) + for (var/mob/O in src.loc) + if (O != occupant) + Bump(O) + else + unbuckle_mob() + +/obj/structure/bed/chair/office/Bump(atom/A) + ..() + if(!has_buckled_mobs()) return + + if(propelled) + for(var/a in buckled_mobs) + var/mob/living/occupant = unbuckle_mob(a) + + var/def_zone = ran_zone() + var/blocked = occupant.run_armor_check(def_zone, "melee") + var/soaked = occupant.get_armor_soak(def_zone, "melee") + occupant.throw_at(A, 3, propelled) + occupant.apply_effect(6, STUN, blocked) + occupant.apply_effect(6, WEAKEN, blocked) + occupant.apply_effect(6, STUTTER, blocked) + occupant.apply_damage(10, BRUTE, def_zone, blocked, soaked) + playsound(src, 'sound/weapons/punch1.ogg', 50, 1, -1) + if(istype(A, /mob/living)) + var/mob/living/victim = A + def_zone = ran_zone() + blocked = victim.run_armor_check(def_zone, "melee") + soaked = victim.get_armor_soak(def_zone, "melee") + victim.apply_effect(6, STUN, blocked) + victim.apply_effect(6, WEAKEN, blocked) + victim.apply_effect(6, STUTTER, blocked) + victim.apply_damage(10, BRUTE, def_zone, blocked, soaked) + occupant.visible_message("[occupant] crashed into \the [A]!") + +/obj/structure/bed/chair/office/light + icon_state = "officechair_white" + +/obj/structure/bed/chair/office/dark + icon_state = "officechair_dark" + +// Chair types +/obj/structure/bed/chair/wood + name = "wooden chair" + desc = "Old is never too old to not be in fashion." + icon_state = "wooden_chair" + +/obj/structure/bed/chair/wood/update_icon() + return + +/obj/structure/bed/chair/wood/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) + return + ..() + +/obj/structure/bed/chair/wood/New(var/newloc) + ..(newloc, "wood") + +/obj/structure/bed/chair/wood/wings + icon_state = "wooden_chair_wings" + +//sofa + +/obj/structure/bed/chair/sofa + name = "sofa" + desc = "It's a sofa. You sit on it. Possibly with someone else." + icon = 'icons/obj/sofas.dmi' + base_icon = "sofamiddle" + icon_state = "sofamiddle" + applies_material_colour = 1 + var/sofa_material = "carpet" + var/corner_piece = FALSE + +/obj/structure/bed/chair/sofa/update_icon() + if(applies_material_colour && sofa_material) + var/datum/material/color_material = get_material_by_name(sofa_material) + color = color_material.icon_colour + + if(sofa_material == "carpet") + name = "red [initial(name)]" + else + name = "[sofa_material] [initial(name)]" + +/obj/structure/bed/chair/sofa/update_layer() + // Corner east/west should be on top of mobs, any other state's north should be. + if(!corner_piece && (dir & NORTH)) + plane = MOB_PLANE + layer = MOB_LAYER + 0.1 + else + reset_plane_and_layer() + +/obj/structure/bed/chair/sofa/left + icon_state = "sofaend_left" + base_icon = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + icon_state = "sofaend_right" + base_icon = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + icon_state = "sofacorner" + base_icon = "sofacorner" + corner_piece = TRUE + +/obj/structure/bed/chair/sofa/corner/update_icon() + ..() + var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]-permanent" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_armrest") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(padding_material) + I.color = padding_material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + +// Wooden nonsofa - no corners +/obj/structure/bed/chair/sofa/pew + name = "pew bench" + desc = "If they want you to go to church, why do they make these so uncomfortable?" + base_icon = "pewmiddle" + icon_state = "pewmiddle" + applies_material_colour = FALSE + +/obj/structure/bed/chair/sofa/pew/left + icon_state = "pewend_left" + base_icon = "pewend_left" + +/obj/structure/bed/chair/sofa/pew/right + icon_state = "pewend_right" + base_icon = "pewend_right" + +// Metal benches from Skyrat +/obj/structure/bed/chair/sofa/bench + name = "metal bench" + desc = "Almost as comfortable as waiting at a bus station for hours on end." + base_icon = "benchmiddle" + icon_state = "benchmiddle" + applies_material_colour = FALSE + color = null + var/padding_color = "#CC0000" + +/obj/structure/bed/chair/sofa/bench/New(var/newloc, var/new_material, var/new_padding_material) + ..() + var/mutable_appearance/MA + // If we're north-facing, metal goes above mob, padding overlay goes below mob. + if((dir & NORTH) && !corner_piece) + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + MA = mutable_appearance(icon, icon_state = "o[icon_state]", layer = BELOW_MOB_LAYER, plane = MOB_PLANE, appearance_flags = KEEP_APART|RESET_COLOR) + // Else just normal plane and layer for everything, which will be below mobs. + else + MA = mutable_appearance(icon, icon_state = "o[icon_state]", appearance_flags = KEEP_APART|RESET_COLOR) + MA.color = padding_color + add_overlay(MA) + +/obj/structure/bed/chair/sofa/bench/left + icon_state = "bench_left" + base_icon = "bench_left" + +/obj/structure/bed/chair/sofa/bench/right + icon_state = "bench_right" + base_icon = "bench_right" + +/obj/structure/bed/chair/sofa/bench/corner + icon_state = "benchcorner" + base_icon = "benchcorner" + //corner_piece = TRUE // These sprites work fine without the parent doing layer shenanigans + +// Corporate sofa - one color fits all +/obj/structure/bed/chair/sofa/corp + name = "black leather sofa" + desc = "How corporate!" + base_icon = "corp_sofamiddle" + icon_state = "corp_sofamiddle" + applies_material_colour = FALSE + +/obj/structure/bed/chair/sofa/corp/left + icon_state = "corp_sofaend_left" + base_icon = "corp_sofaend_left" + +/obj/structure/bed/chair/sofa/corp/right + icon_state = "corp_sofaend_right" + base_icon = "corp_sofaend_right" + +/obj/structure/bed/chair/sofa/corp/corner + icon_state = "corp_sofacorner" + base_icon = "corp_sofacorner" + corner_piece = TRUE + +//color variations +//Middle sofas first +/obj/structure/bed/chair/sofa + sofa_material = "carpet" + +/obj/structure/bed/chair/sofa/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/orange + sofa_material = "orange" + +//sofa directions + +/obj/structure/bed/chair/sofa/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/left/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/right/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/corner/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/left/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/right/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/corner/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/left/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/right/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/corner/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/left/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/right/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/corner/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/left/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/right/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/corner/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/left/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/right/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/corner/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/left/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/right/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/corner/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/left/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/right/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/corner/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/left/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/right/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/corner/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/left/orange + sofa_material = "orange" + +/obj/structure/bed/chair/sofa/right/orange + sofa_material = "orange" + +/obj/structure/bed/chair/sofa/corner/orange + sofa_material = "orange" diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm index 8c61d2ca348..afd8fb13b28 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm @@ -1,151 +1,151 @@ -//Todo: add leather and cloth for arbitrary coloured stools. -var/global/list/stool_cache = list() //haha stool - -/obj/item/weapon/stool - name = "stool" - desc = "Apply butt." - icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - new Icons - icon_state = "stool_preview" //set for the map - randpixel = 0 - center_of_mass = null - force = 10 - throwforce = 10 - w_class = ITEMSIZE_HUGE - var/base_icon = "stool_base" - var/datum/material/material - var/datum/material/padding_material - -/obj/item/weapon/stool/padded - icon_state = "stool_padded_preview" //set for the map - -/obj/item/weapon/stool/New(var/newloc, var/new_material, var/new_padding_material) - ..(newloc) - if(!new_material) - new_material = MAT_STEEL - material = get_material_by_name(new_material) - if(new_padding_material) - padding_material = get_material_by_name(new_padding_material) - if(!istype(material)) - qdel(src) - return - force = round(material.get_blunt_damage()*0.4) - update_icon() - -/obj/item/weapon/stool/padded/New(var/newloc, var/new_material) - ..(newloc, "steel", "carpet") - -/obj/item/weapon/stool/update_icon() - // Prep icon. - icon_state = "" - cut_overlays() - // Base icon. - var/cache_key = "[base_icon]-[material.name]" - if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, base_icon) - I.color = material.icon_colour - stool_cache[cache_key] = I - add_overlay(stool_cache[cache_key]) - // Padding overlay. - if(padding_material) - var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" - if(isnull(stool_cache[padding_cache_key])) - var/image/I = image(icon, "[base_icon]_padding") //VOREStation Edit - I.color = padding_material.icon_colour - stool_cache[padding_cache_key] = I - add_overlay(stool_cache[padding_cache_key]) - // Strings. - if(padding_material) - name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. - desc = "A padded stool. Apply butt. It's made of [material.use_name] and covered with [padding_material.use_name]." - else - name = "[material.display_name] [initial(name)]" - desc = "A stool. Apply butt with care. It's made of [material.use_name]." - -/obj/item/weapon/stool/proc/add_padding(var/padding_type) - padding_material = get_material_by_name(padding_type) - update_icon() - -/obj/item/weapon/stool/proc/remove_padding() - if(padding_material) - padding_material.place_sheet(get_turf(src)) - padding_material = null - update_icon() - -/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob) - if (prob(5) && istype(M,/mob/living)) - user.visible_message("[user] breaks [src] over [M]'s back!") - user.setClickCooldown(user.get_attack_speed()) - user.do_attack_animation(M) - - user.drop_from_inventory(src) - - user.remove_from_mob(src) - dismantle() - qdel(src) - var/mob/living/T = M - T.Weaken(10) - T.apply_damage(20) - return - ..() - -/obj/item/weapon/stool/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(50)) - qdel(src) - return - if(3.0) - if (prob(5)) - qdel(src) - return - -/obj/item/weapon/stool/proc/dismantle() - if(material) - material.place_sheet(get_turf(src)) - if(padding_material) - padding_material.place_sheet(get_turf(src)) - qdel(src) - -/obj/item/weapon/stool/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) - playsound(src, W.usesound, 50, 1) - dismantle() - qdel(src) - else if(istype(W,/obj/item/stack)) - if(padding_material) - to_chat(user, "\The [src] is already padded.") - return - var/obj/item/stack/C = W - if(C.get_amount() < 1) // How?? - user.drop_from_inventory(C) - qdel(C) - return - var/padding_type //This is awful but it needs to be like this until tiles are given a material var. - if(istype(W,/obj/item/stack/tile/carpet)) - padding_type = "carpet" - else if(istype(W,/obj/item/stack/material)) - var/obj/item/stack/material/M = W - if(M.material && (M.material.flags & MATERIAL_PADDING)) - padding_type = "[M.material.name]" - if(!padding_type) - to_chat(user, "You cannot pad \the [src] with that.") - return - C.use(1) - if(!istype(src.loc, /turf)) - user.drop_from_inventory(src) - src.loc = get_turf(src) - to_chat(user, "You add padding to \the [src].") - add_padding(padding_type) - return - else if (W.is_wirecutter()) - if(!padding_material) - to_chat(user, "\The [src] has no padding to remove.") - return - to_chat(user, "You remove the padding from \the [src].") - playsound(src, W.usesound, 50, 1) - remove_padding() - else - ..() +//Todo: add leather and cloth for arbitrary coloured stools. +var/global/list/stool_cache = list() //haha stool + +/obj/item/weapon/stool + name = "stool" + desc = "Apply butt." + icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - new Icons + icon_state = "stool_preview" //set for the map + randpixel = 0 + center_of_mass = null + force = 10 + throwforce = 10 + w_class = ITEMSIZE_HUGE + var/base_icon = "stool_base" + var/datum/material/material + var/datum/material/padding_material + +/obj/item/weapon/stool/padded + icon_state = "stool_padded_preview" //set for the map + +/obj/item/weapon/stool/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc) + if(!new_material) + new_material = MAT_STEEL + material = get_material_by_name(new_material) + if(new_padding_material) + padding_material = get_material_by_name(new_padding_material) + if(!istype(material)) + qdel(src) + return + force = round(material.get_blunt_damage()*0.4) + update_icon() + +/obj/item/weapon/stool/padded/New(var/newloc, var/new_material) + ..(newloc, "steel", "carpet") + +/obj/item/weapon/stool/update_icon() + // Prep icon. + icon_state = "" + cut_overlays() + // Base icon. + var/cache_key = "[base_icon]-[material.name]" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, base_icon) + I.color = material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + // Padding overlay. + if(padding_material) + var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" + if(isnull(stool_cache[padding_cache_key])) + var/image/I = image(icon, "[base_icon]_padding") //VOREStation Edit + I.color = padding_material.icon_colour + stool_cache[padding_cache_key] = I + add_overlay(stool_cache[padding_cache_key]) + // Strings. + if(padding_material) + name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. + desc = "A padded stool. Apply butt. It's made of [material.use_name] and covered with [padding_material.use_name]." + else + name = "[material.display_name] [initial(name)]" + desc = "A stool. Apply butt with care. It's made of [material.use_name]." + +/obj/item/weapon/stool/proc/add_padding(var/padding_type) + padding_material = get_material_by_name(padding_type) + update_icon() + +/obj/item/weapon/stool/proc/remove_padding() + if(padding_material) + padding_material.place_sheet(get_turf(src), 1) + padding_material = null + update_icon() + +/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob) + if (prob(5) && istype(M,/mob/living)) + user.visible_message("[user] breaks [src] over [M]'s back!") + user.setClickCooldown(user.get_attack_speed()) + user.do_attack_animation(M) + + user.drop_from_inventory(src) + + user.remove_from_mob(src) + dismantle() + qdel(src) + var/mob/living/T = M + T.Weaken(10) + T.apply_damage(20) + return + ..() + +/obj/item/weapon/stool/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(50)) + qdel(src) + return + if(3.0) + if (prob(5)) + qdel(src) + return + +/obj/item/weapon/stool/proc/dismantle() + if(material) + material.place_sheet(get_turf(src), 1) + if(padding_material) + padding_material.place_sheet(get_turf(src), 1) + qdel(src) + +/obj/item/weapon/stool/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.has_tool_quality(TOOL_WRENCH)) + playsound(src, W.usesound, 50, 1) + dismantle() + qdel(src) + else if(istype(W,/obj/item/stack)) + if(padding_material) + to_chat(user, "\The [src] is already padded.") + return + var/obj/item/stack/C = W + if(C.get_amount() < 1) // How?? + user.drop_from_inventory(C) + qdel(C) + return + var/padding_type //This is awful but it needs to be like this until tiles are given a material var. + if(istype(W,/obj/item/stack/tile/carpet)) + padding_type = "carpet" + else if(istype(W,/obj/item/stack/material)) + var/obj/item/stack/material/M = W + if(M.material && (M.material.flags & MATERIAL_PADDING)) + padding_type = "[M.material.name]" + if(!padding_type) + to_chat(user, "You cannot pad \the [src] with that.") + return + C.use(1) + if(!istype(src.loc, /turf)) + user.drop_from_inventory(src) + src.loc = get_turf(src) + to_chat(user, "You add padding to \the [src].") + add_padding(padding_type) + return + else if (W.has_tool_quality(TOOL_WIRECUTTER)) + if(!padding_material) + to_chat(user, "\The [src] has no padding to remove.") + return + to_chat(user, "You remove the padding from \the [src].") + playsound(src, W.usesound, 50, 1) + remove_padding() + else + ..() diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index 142cd8928ec..3214128eecd 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -55,7 +55,7 @@ L.set_dir(dir) /obj/structure/bed/chair/wheelchair/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench() || W.is_wirecutter() || istype(W,/obj/item/stack)) + if(W.has_tool_quality(TOOL_WRENCH) || W.has_tool_quality(TOOL_WIRECUTTER) || istype(W,/obj/item/stack)) return ..() diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index 1af54a1b4e1..23872935843 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -1,122 +1,122 @@ -#define TANK_DISPENSER_CAPACITY 10 - -/obj/structure/dispenser - name = "tank storage unit" - desc = "A simple yet bulky storage device for gas tanks. Has room for up to ten oxygen tanks, and ten phoron tanks." - icon = 'icons/obj/objects_vr.dmi' - icon_state = "dispenser" - density = TRUE - anchored = TRUE - w_class = ITEMSIZE_HUGE - var/oxygentanks = TANK_DISPENSER_CAPACITY - var/phorontanks = TANK_DISPENSER_CAPACITY - - -/obj/structure/dispenser/oxygen - phorontanks = 0 - -/obj/structure/dispenser/phoron - oxygentanks = 0 - - -/obj/structure/dispenser/Initialize() - . = ..() - for(var/i in 1 to oxygentanks) - new /obj/item/weapon/tank/oxygen(src) - for(var/i in 1 to phorontanks) - new /obj/item/weapon/tank/phoron(src) - update_icon() - -/obj/structure/dispenser/update_icon() - cut_overlays() - switch(oxygentanks) - if(1 to 3) add_overlay("oxygen-[oxygentanks]") - if(4 to INFINITY) add_overlay("oxygen-4") - switch(phorontanks) - if(1 to 4) add_overlay("phoron-[phorontanks]") - if(5 to INFINITY) add_overlay("phoron-5") - -/obj/structure/dispenser/attack_ai(mob/user) - // This looks silly, but robots also call attack_ai, and they're allowed physical state stuff. - if(user.Adjacent(src)) - return attack_hand(user) - ..() - -/obj/structure/dispenser/attack_hand(mob/user) - tgui_interact(user) - -/obj/structure/dispenser/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/structure/dispenser/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "TankDispenser", name) - ui.open() - -/obj/structure/dispenser/tgui_data(mob/user) - var/list/data = list() - data["oxygen"] = oxygentanks - data["plasma"] = phorontanks - - return data - -/obj/structure/dispenser/attackby(obj/item/I, mob/user) - var/full - if(istype(I, /obj/item/weapon/tank/oxygen) || istype(I, /obj/item/weapon/tank/air) || istype(I, /obj/item/weapon/tank/anesthetic)) - if(oxygentanks < TANK_DISPENSER_CAPACITY) - oxygentanks++ - else - full = TRUE - else if(istype(I, /obj/item/weapon/tank/phoron)) - if(phorontanks < TANK_DISPENSER_CAPACITY) - phorontanks++ - else - full = TRUE - else if(I.is_wrench()) - if(anchored) - to_chat(user, "You lean down and unwrench [src].") - anchored = FALSE - else - to_chat(user, "You wrench [src] into place.") - anchored = TRUE - return - else if(user.a_intent != I_HURT) - to_chat(user, "[I] does not fit into [src].") - return - else - return ..() - - if(full) - to_chat(user, "[src] can't hold any more of [I].") - return - - if(!user.unEquip(I, target = src)) - return - to_chat(user, "You put [I] in [src].") - update_icon() - - -/obj/structure/dispenser/tgui_act(action, params) - if(..()) - return - switch(action) - if("plasma") - var/obj/item/weapon/tank/phoron/tank = locate() in src - if(tank && Adjacent(usr)) - usr.put_in_hands(tank) - phorontanks-- - . = TRUE - playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) - if("oxygen") - var/obj/item/weapon/tank/tank = null - for(var/obj/item/weapon/tank/T in src) - if(istype(T, /obj/item/weapon/tank/oxygen) || istype(T, /obj/item/weapon/tank/air) || istype(T, /obj/item/weapon/tank/anesthetic)) - tank = T - break - if(tank && Adjacent(usr)) - usr.put_in_hands(tank) - oxygentanks-- - . = TRUE - playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) +#define TANK_DISPENSER_CAPACITY 10 + +/obj/structure/dispenser + name = "tank storage unit" + desc = "A simple yet bulky storage device for gas tanks. Has room for up to ten oxygen tanks, and ten phoron tanks." + icon = 'icons/obj/objects_vr.dmi' + icon_state = "dispenser" + density = TRUE + anchored = TRUE + w_class = ITEMSIZE_HUGE + var/oxygentanks = TANK_DISPENSER_CAPACITY + var/phorontanks = TANK_DISPENSER_CAPACITY + + +/obj/structure/dispenser/oxygen + phorontanks = 0 + +/obj/structure/dispenser/phoron + oxygentanks = 0 + + +/obj/structure/dispenser/Initialize() + . = ..() + for(var/i in 1 to oxygentanks) + new /obj/item/weapon/tank/oxygen(src) + for(var/i in 1 to phorontanks) + new /obj/item/weapon/tank/phoron(src) + update_icon() + +/obj/structure/dispenser/update_icon() + cut_overlays() + switch(oxygentanks) + if(1 to 3) add_overlay("oxygen-[oxygentanks]") + if(4 to INFINITY) add_overlay("oxygen-4") + switch(phorontanks) + if(1 to 4) add_overlay("phoron-[phorontanks]") + if(5 to INFINITY) add_overlay("phoron-5") + +/obj/structure/dispenser/attack_ai(mob/user) + // This looks silly, but robots also call attack_ai, and they're allowed physical state stuff. + if(user.Adjacent(src)) + return attack_hand(user) + ..() + +/obj/structure/dispenser/attack_hand(mob/user) + tgui_interact(user) + +/obj/structure/dispenser/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/structure/dispenser/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TankDispenser", name) + ui.open() + +/obj/structure/dispenser/tgui_data(mob/user) + var/list/data = list() + data["oxygen"] = oxygentanks + data["plasma"] = phorontanks + + return data + +/obj/structure/dispenser/attackby(obj/item/I, mob/user) + var/full + if(istype(I, /obj/item/weapon/tank/oxygen) || istype(I, /obj/item/weapon/tank/air) || istype(I, /obj/item/weapon/tank/anesthetic)) + if(oxygentanks < TANK_DISPENSER_CAPACITY) + oxygentanks++ + else + full = TRUE + else if(istype(I, /obj/item/weapon/tank/phoron)) + if(phorontanks < TANK_DISPENSER_CAPACITY) + phorontanks++ + else + full = TRUE + else if(I.has_tool_quality(TOOL_WRENCH)) + if(anchored) + to_chat(user, "You lean down and unwrench [src].") + anchored = FALSE + else + to_chat(user, "You wrench [src] into place.") + anchored = TRUE + return + else if(user.a_intent != I_HURT) + to_chat(user, "[I] does not fit into [src].") + return + else + return ..() + + if(full) + to_chat(user, "[src] can't hold any more of [I].") + return + + if(!user.unEquip(I, target = src)) + return + to_chat(user, "You put [I] in [src].") + update_icon() + + +/obj/structure/dispenser/tgui_act(action, params) + if(..()) + return + switch(action) + if("plasma") + var/obj/item/weapon/tank/phoron/tank = locate() in src + if(tank && Adjacent(usr)) + usr.put_in_hands(tank) + phorontanks-- + . = TRUE + playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) + if("oxygen") + var/obj/item/weapon/tank/tank = null + for(var/obj/item/weapon/tank/T in src) + if(istype(T, /obj/item/weapon/tank/oxygen) || istype(T, /obj/item/weapon/tank/air) || istype(T, /obj/item/weapon/tank/anesthetic)) + tank = T + break + if(tank && Adjacent(usr)) + usr.put_in_hands(tank) + oxygentanks-- + . = TRUE + playsound(src, 'sound/items/drop/gascan.ogg', 100, 1, 1) update_icon() \ No newline at end of file diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index 0e29fed20b9..76556346006 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -1,52 +1,52 @@ -// Basically they are for the firing range -/obj/structure/target_stake - name = "target stake" - desc = "A thin platform with negatively-magnetized wheels." - icon = 'icons/obj/objects.dmi' - icon_state = "target_stake" - density = TRUE - w_class = ITEMSIZE_HUGE - var/obj/item/target/pinned_target // the current pinned target - -/obj/structure/target_stake/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - // Move the pinned target along with the stake - if(pinned_target in view(3, src)) - pinned_target.forceMove(loc) - - else // Sanity check: if the pinned target can't be found in immediate view - pinned_target = null - density = TRUE - -/obj/structure/target_stake/attackby(obj/item/W as obj, mob/user as mob) - // Putting objects on the stake. Most importantly, targets - if(pinned_target) - return // get rid of that pinned target first! - - if(istype(W, /obj/item/target)) - density = FALSE - W.density = TRUE - user.remove_from_mob(W) - W.loc = loc - W.layer = ABOVE_JUNK_LAYER - pinned_target = W - to_chat(user, "You slide the target into the stake.") - return - -/obj/structure/target_stake/attack_hand(mob/user as mob) - // taking pinned targets off! - if(pinned_target) - density = TRUE - pinned_target.density = FALSE - pinned_target.layer = OBJ_LAYER - - pinned_target.loc = user.loc - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(pinned_target) - to_chat(user, "You take the target out of the stake.") - else - pinned_target.loc = get_turf(user) - to_chat(user, "You take the target out of the stake.") - - pinned_target = null +// Basically they are for the firing range +/obj/structure/target_stake + name = "target stake" + desc = "A thin platform with negatively-magnetized wheels." + icon = 'icons/obj/objects.dmi' + icon_state = "target_stake" + density = TRUE + w_class = ITEMSIZE_HUGE + var/obj/item/target/pinned_target // the current pinned target + +/obj/structure/target_stake/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + // Move the pinned target along with the stake + if(pinned_target in view(3, src)) + pinned_target.forceMove(loc) + + else // Sanity check: if the pinned target can't be found in immediate view + pinned_target = null + density = TRUE + +/obj/structure/target_stake/attackby(obj/item/W as obj, mob/user as mob) + // Putting objects on the stake. Most importantly, targets + if(pinned_target) + return // get rid of that pinned target first! + + if(istype(W, /obj/item/target)) + density = FALSE + W.density = TRUE + user.remove_from_mob(W) + W.loc = loc + W.layer = ABOVE_JUNK_LAYER + pinned_target = W + to_chat(user, "You slide the target into the stake.") + return + +/obj/structure/target_stake/attack_hand(mob/user as mob) + // taking pinned targets off! + if(pinned_target) + density = TRUE + pinned_target.density = FALSE + pinned_target.layer = OBJ_LAYER + + pinned_target.loc = user.loc + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(pinned_target) + to_chat(user, "You take the target out of the stake.") + else + pinned_target.loc = get_turf(user) + to_chat(user, "You take the target out of the stake.") + + pinned_target = null diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index b834b2e8070..0c8c68453cf 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -44,7 +44,7 @@ icon_state = "[initial(icon_state)][open][cistern]" /obj/structure/toilet/attackby(obj/item/I as obj, mob/living/user as mob) - if(I.is_crowbar()) + if(I.has_tool_quality(TOOL_CROWBAR)) to_chat(user, "You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"].") playsound(src, 'sound/effects/stonedoor_openclose.ogg', 50, 1) if(do_after(user, 30)) @@ -124,7 +124,6 @@ else to_chat(user, "You need a tighter grip.") - /obj/structure/urinal name = "urinal" desc = "The HU-452, an experimental urinal." @@ -201,7 +200,7 @@ /obj/machinery/shower/attackby(obj/item/I as obj, mob/user as mob) if(I.type == /obj/item/device/analyzer) to_chat(user, "The water temperature seems to be [watertemp].") - if(I.is_wrench()) + if(I.has_tool_quality(TOOL_WRENCH)) var/newtemp = tgui_input_list(user, "What setting would you like to set the temperature valve to?", "Water Temperature Valve", temperature_settings) to_chat(user, "You begin to adjust the temperature valve with \the [I].") playsound(src, I.usesound, 50, 1) @@ -302,6 +301,91 @@ icon_state = "rubberducky" honk_sound = 'sound/voice/quack.ogg' //VOREStation edit +//Admin spawn duckies + +/obj/item/weapon/bikehorn/rubberducky/red + name = "rubber ducky" + desc = "From the depths of hell it arose, feathers glistening with crimson, a honk that struck fear into all men." //thanks doohl + icon = 'icons/obj/watercloset.dmi' + icon_state = "rubberducky_red" + honk_sound = 'sound/effects/adminhelp.ogg' + var/honk_count = 0 + +/obj/item/weapon/bikehorn/rubberducky/red/attack_self(mob/user as mob) + if(honk_count >= 3) + var/turf/epicenter = src.loc + explosion(epicenter, 0, 0, 1, 3) + qdel(src) + return + else if(spam_flag == 0) + spam_flag = 1 + playsound(src, honk_sound, 50, 1) + src.add_fingerprint(user) + honk_count ++ + spawn(20) + spam_flag = 0 + return + +/obj/item/weapon/bikehorn/rubberducky/blue + name = "rubber ducky" + desc = "The see me rollin', they hatin'." //thanks doohl + icon = 'icons/obj/watercloset.dmi' + icon_state = "rubberducky_blue" + honk_sound = 'sound/effects/bubbles.ogg' + var/honk_count = 0 + +/obj/item/weapon/bikehorn/rubberducky/blue/attack_self(mob/user as mob) + if(spam_flag == 0) + var/turf/simulated/whereweare = get_turf(src) + whereweare.wet_floor(2) + spam_flag = 1 + playsound(src, honk_sound, 50, 1) + src.add_fingerprint(user) + spawn(20) + spam_flag = 0 + return + +/obj/item/weapon/bikehorn/rubberducky/white + name = "rubber ducky" + desc = "It's so full of energy, such a happy little guy, I just wanna give him a squeeze." //thanks doohl + icon = 'icons/obj/watercloset.dmi' + icon_state = "rubberducky_white" + honk_sound = 'sound/effects/lightningshock.ogg' + var/honk_count = 0 + +/obj/item/weapon/bikehorn/rubberducky/white/attack_self(mob/user as mob) + if(spam_flag == 0) + lightning_strike(get_turf(src), 1) + spam_flag = 1 + playsound(src, honk_sound, 50, 1) + src.add_fingerprint(user) + spawn(20) + spam_flag = 0 //leaving this in incase it doesn't qdel somehow + qdel(src) + return + +/obj/item/weapon/grenade/anti_photon/rubberducky/black + desc = "Good work NanoTrasen Employee, you struck fear within the Syndicate." + name = "rubber ducky" + icon = 'icons/obj/watercloset.dmi' + icon_state = "rubberducky_black" + det_time = 20 + +/obj/item/weapon/grenade/anti_photon/rubberducky/black/detonate() + playsound(src, 'sound/voice/quack.ogg', 50, 1, 5) + set_light(10, -10, "#FFFFFF") + + var/extra_delay = rand(0,90) + + spawn(extra_delay) + spawn(200) + if(prob(10+extra_delay)) + set_light(10, 10, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") + spawn(210) + ..() + playsound(src, 'sound/voice/quack.ogg', 50, 1, 5) + qdel(src) + /obj/structure/sink name = "sink" icon = 'icons/obj/watercloset.dmi' diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 855e7fac117..44bb6e45a75 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -87,8 +87,8 @@ switch(state) if("01") - if(istype(W, /obj/item/weapon/weldingtool) && !anchored ) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER) && !anchored) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if (WT.remove_fuel(0,user)) user.visible_message("[user] disassembles the windoor assembly.", "You start to disassemble the windoor assembly.") playsound(src, WT.usesound, 50, 1) @@ -106,7 +106,7 @@ return //Wrenching an unsecure assembly anchors it in place. Step 4 complete - if(W.is_wrench() && !anchored) + if(W.has_tool_quality(TOOL_WRENCH) && !anchored) playsound(src, W.usesound, 100, 1) user.visible_message("[user] secures the windoor assembly to the floor.", "You start to secure the windoor assembly to the floor.") @@ -117,7 +117,7 @@ step = 0 //Unwrenching an unsecure assembly un-anchors it. Step 4 undone - else if(W.is_wrench() && anchored) + else if(W.has_tool_quality(TOOL_WRENCH) && anchored) playsound(src, W.usesound, 100, 1) user.visible_message("[user] unsecures the windoor assembly to the floor.", "You start to unsecure the windoor assembly to the floor.") @@ -143,7 +143,7 @@ if("02") //Removing wire from the assembly. Step 5 undone. - if(W.is_wirecutter() && !src.electronics) + if(W.has_tool_quality(TOOL_WIRECUTTER) && !src.electronics) playsound(src, W.usesound, 100, 1) user.visible_message("[user] cuts the wires from the airlock assembly.", "You start to cut the wires from airlock assembly.") @@ -172,7 +172,7 @@ W.loc = src.loc //Screwdriver to remove airlock electronics. Step 6 undone. - else if(W.is_screwdriver() && src.electronics) + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && src.electronics) playsound(src, W.usesound, 100, 1) user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to uninstall electronics from the airlock assembly.") @@ -185,7 +185,7 @@ ae.loc = src.loc //Crowbar to complete the assembly, Step 7 complete. - else if(W.is_crowbar()) + else if(W.has_tool_quality(TOOL_CROWBAR)) if(!src.electronics) to_chat(usr,"The assembly is missing electronics.") return diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 35e7a339afa..1e40f249646 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -1,704 +1,704 @@ -/obj/structure/window - name = "window" - desc = "A window." - icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons - density = TRUE - can_atmos_pass = ATMOS_PASS_PROC - w_class = ITEMSIZE_NORMAL - - layer = WINDOW_LAYER - pressure_resistance = 4*ONE_ATMOSPHERE - anchored = TRUE - flags = ON_BORDER - var/maxhealth = 14.0 - var/maximal_heat = T0C + 100 // Maximal heat before this window begins taking damage from fire - var/damage_per_fire_tick = 2.0 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly. - var/health - var/force_threshold = 0 - var/ini_dir = null - var/state = 2 - var/reinf = 0 - var/basestate - var/shardtype = /obj/item/weapon/material/shard - var/glasstype = null // Set this in subtypes. Null is assumed strange or otherwise impossible to dismantle, such as for shuttle glass. - var/silicate = 0 // number of units of silicate - var/fulltile = FALSE // Set to true on full-tile variants. - -/obj/structure/window/examine(mob/user) - . = ..() - - if(health == maxhealth) - . += "It looks fully intact." - else - var/perc = health / maxhealth - if(perc > 0.75) - . += "It has a few cracks." - else if(perc > 0.5) - . += "It looks slightly damaged." - else if(perc > 0.25) - . += "It looks moderately damaged." - else - . += "It looks heavily damaged." - if(silicate) - if (silicate < 30) - . += "It has a thin layer of silicate." - else if (silicate < 70) - . += "It is covered in silicate." - else - . += "There is a thick layer of silicate covering it." - -/obj/structure/window/examine_icon() - return icon(icon=initial(icon),icon_state=initial(icon_state)) - -/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1) - var/initialhealth = health - - if(silicate) - damage = damage * (1 - silicate / 200) - - health = max(0, health - damage) - - if(health <= 0) - shatter() - else - if(sound_effect) - playsound(src, 'sound/effects/Glasshit.ogg', 100, 1) - if(health < maxhealth / 4 && initialhealth >= maxhealth / 4) - visible_message("[src] looks like it's about to shatter!" ) - update_icon() - else if(health < maxhealth / 2 && initialhealth >= maxhealth / 2) - visible_message("[src] looks seriously damaged!" ) - update_icon() - else if(health < maxhealth * 3/4 && initialhealth >= maxhealth * 3/4) - visible_message("Cracks begin to appear in [src]!" ) - update_icon() - return - -/obj/structure/window/proc/apply_silicate(var/amount) - if(health < maxhealth) // Mend the damage - health = min(health + amount * 3, maxhealth) - if(health == maxhealth) - visible_message("[src] looks fully repaired." ) - else // Reinforce - silicate = min(silicate + amount, 100) - updateSilicate() - -/obj/structure/window/proc/updateSilicate() - cut_overlays() - update_icon() - - var/image/img = image(src) - img.color = "#ffffff" - img.alpha = silicate * 255 / 100 - add_overlay(img) - -/obj/structure/window/proc/shatter(var/display_message = 1) - playsound(src, "shatter", 70, 1) - if(display_message) - visible_message("[src] shatters!") - new shardtype(loc) - if(reinf) - new /obj/item/stack/rods(loc) - if(is_fulltile()) - new shardtype(loc) //todo pooling? - if(reinf) - new /obj/item/stack/rods(loc) - qdel(src) - return - - -/obj/structure/window/bullet_act(var/obj/item/projectile/Proj) - - var/proj_damage = Proj.get_structure_damage() - if(!proj_damage) return - - ..() - take_damage(proj_damage) - return - - -/obj/structure/window/ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - shatter(0) - return - if(3.0) - if(prob(50)) - shatter(0) - return - -/obj/structure/window/blob_act() - take_damage(50) - -/obj/structure/window/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(is_fulltile()) - return FALSE //full tile window, you can't move into it! - if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir - return !density - else - return TRUE - -/obj/structure/window/Uncross(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir - return !density - else - return TRUE - -/obj/structure/window/CanZASPass(turf/T, is_zone) - if(is_fulltile() || get_dir(T, loc) == turn(dir, 180)) // Make sure we're handling the border correctly. - return !anchored // If it's anchored, it'll block air. - return TRUE // Don't stop airflow from the other sides. - -/obj/structure/window/hitby(AM as mob|obj) - ..() - visible_message("[src] was hit by [AM].") - var/tforce = 0 - if(ismob(AM)) - tforce = 40 - else if(isobj(AM)) - var/obj/item/I = AM - tforce = I.throwforce - if(reinf) tforce *= 0.25 - if(health - tforce <= 7 && !reinf) - anchored = FALSE - update_verbs() - update_nearby_icons() - step(src, get_dir(AM, src)) - take_damage(tforce) - -/obj/structure/window/attack_tk(mob/user as mob) - user.visible_message("Something knocks on [src].") - playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) - -/obj/structure/window/attack_hand(mob/user as mob) - user.setClickCooldown(user.get_attack_speed()) - if(HULK in user.mutations) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) - user.visible_message("[user] smashes through [src]!") - user.do_attack_animation(src) - shatter() - - else if (usr.a_intent == I_HURT) - - if (istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - if(H.species.can_shred(H)) - attack_generic(H,25) - return - - playsound(src, 'sound/effects/glassknock.ogg', 80, 1) - user.do_attack_animation(src) - usr.visible_message("\The [usr] bangs against \the [src]!", - "You bang against \the [src]!", - "You hear a banging sound.") - else - playsound(src, 'sound/effects/glassknock.ogg', 80, 1) - usr.visible_message("[usr.name] knocks on the [src.name].", - "You knock on the [src.name].", - "You hear a knocking sound.") - return - -/obj/structure/window/attack_generic(var/mob/user, var/damage) - user.setClickCooldown(user.get_attack_speed()) - if(!damage) - return - if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - visible_message("[user] smashes into [src]!") - if(reinf) - damage = damage / 2 - take_damage(damage) - else - visible_message("\The [user] bonks \the [src] harmlessly.") - user.do_attack_animation(src) - return 1 - -/obj/structure/window/attackby(obj/item/W as obj, mob/user as mob) - if(!istype(W)) return//I really wish I did not need this - - // Fixing. - if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent == I_HELP) - var/obj/item/weapon/weldingtool/WT = W - if(health < maxhealth) - if(WT.remove_fuel(1 ,user)) - to_chat(user, "You begin repairing [src]...") - playsound(src, WT.usesound, 50, 1) - if(do_after(user, 40 * WT.toolspeed, target = src)) - health = maxhealth - // playsound(src, 'sound/items/Welder.ogg', 50, 1) - update_icon() - to_chat(user, "You repair [src].") - else - to_chat(user, "[src] is already in good condition!") - return - - // Slamming. - if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) - var/obj/item/weapon/grab/G = W - if(istype(G.affecting,/mob/living)) - var/mob/living/M = G.affecting - var/state = G.state - qdel(W) //gotta delete it here because if window breaks, it won't get deleted - switch (state) - if(1) - M.visible_message("[user] slams [M] against \the [src]!") - M.apply_damage(7) - hit(10) - if(2) - M.visible_message("[user] bashes [M] against \the [src]!") - if (prob(50)) - M.Weaken(1) - M.apply_damage(10) - hit(25) - if(3) - M.visible_message("[user] crushes [M] against \the [src]!") - M.Weaken(5) - M.apply_damage(20) - hit(50) - return - - if(W.flags & NOBLUDGEON) return - - if(W.is_screwdriver()) - if(reinf && state >= 1) - state = 3 - state - update_nearby_icons() - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have [state == 1 ? "un" : ""]fastened the window [state ? "from" : "to"] the frame.") - else if(reinf && state == 0) - anchored = !anchored - update_nearby_tiles(need_rebuild=1) - update_nearby_icons() - update_verbs() - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have [anchored ? "" : "un"]fastened the frame [anchored ? "to" : "from"] the floor.") - else if(!reinf) - anchored = !anchored - update_nearby_tiles(need_rebuild=1) - update_nearby_icons() - update_verbs() - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have [anchored ? "" : "un"]fastened the window [anchored ? "to" : "from"] the floor.") - else if(W.is_crowbar() && reinf && state <= 1) - state = 1 - state - playsound(src, W.usesound, 75, 1) - to_chat(user, "You have pried the window [state ? "into" : "out of"] the frame.") - else if(W.is_wrench() && !anchored && (!state || !reinf)) - if(!glasstype) - to_chat(user, "You're not sure how to dismantle \the [src] properly.") - else - playsound(src, W.usesound, 75, 1) - visible_message("[user] dismantles \the [src].") - var/obj/item/stack/material/mats = new glasstype(loc) - if(is_fulltile()) - mats.set_amount(4) - qdel(src) - else if(istype(W, /obj/item/stack/cable_coil) && reinf && state == 0 && !istype(src, /obj/structure/window/reinforced/polarized)) - var/obj/item/stack/cable_coil/C = W - if (C.use(1)) - playsound(src, 'sound/effects/sparks1.ogg', 75, 1) - user.visible_message( \ - "\The [user] begins to wire \the [src] for electrochromic tinting.", \ - "You begin to wire \the [src] for electrochromic tinting.", \ - "You hear sparks.") - if(do_after(user, 20 * C.toolspeed, src) && state == 0) - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - var/obj/structure/window/reinforced/polarized/P = new(loc, dir) - if(is_fulltile()) - P.fulltile = TRUE - P.icon_state = "fwindow" - P.maxhealth = maxhealth - P.health = health - P.state = state - P.anchored = anchored - qdel(src) - else if(istype(W,/obj/item/frame) && anchored) - var/obj/item/frame/F = W - F.try_build(src, user) - else - user.setClickCooldown(user.get_attack_speed(W)) - if(W.damtype == BRUTE || W.damtype == BURN) - user.do_attack_animation(src) - hit(W.force) - if(health <= 7) - anchored = FALSE - update_nearby_icons() - step(src, get_dir(user, src)) - else - playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - ..() - return - -/obj/structure/window/proc/hit(var/damage, var/sound_effect = 1) - if(damage < force_threshold || force_threshold < 0) - return - if(reinf) damage *= 0.5 - take_damage(damage) - return - - -/obj/structure/window/verb/rotate_counterclockwise() - set name = "Rotate Window Counterclockwise" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return 0 - - if(is_fulltile()) - return 0 - - if(anchored) - to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") - return 0 - - update_nearby_tiles(need_rebuild=1) //Compel updates before - src.set_dir(turn(src.dir, 90)) - updateSilicate() - update_nearby_tiles(need_rebuild=1) - return - - -/obj/structure/window/verb/rotate_clockwise() - set name = "Rotate Window Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return 0 - - if(is_fulltile()) - return 0 - - if(anchored) - to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") - return 0 - - update_nearby_tiles(need_rebuild=1) //Compel updates before - src.set_dir(turn(src.dir, 270)) - updateSilicate() - update_nearby_tiles(need_rebuild=1) - return - -/obj/structure/window/New(Loc, start_dir=null, constructed=0) - ..() - - if (start_dir) - set_dir(start_dir) - - //player-constructed windows - if (constructed) - anchored = FALSE - state = 0 - update_verbs() - - health = maxhealth - - ini_dir = dir - - update_nearby_tiles(need_rebuild=1) - update_nearby_icons() - - -/obj/structure/window/Destroy() - density = FALSE - update_nearby_tiles() - var/turf/location = loc - . = ..() - for(var/obj/structure/window/W in orange(location, 1)) - W.update_icon() - -/obj/structure/window/Move() - var/ini_dir = dir - update_nearby_tiles(need_rebuild=1) - . = ..() - set_dir(ini_dir) - update_nearby_tiles(need_rebuild=1) - -//checks if this window is full-tile one -/obj/structure/window/proc/is_fulltile() - return fulltile - -/obj/structure/window/is_between_turfs(var/turf/origin, var/turf/target) - if(is_fulltile()) - return TRUE - return ..() - -//This proc is used to update the icons of nearby windows. It should not be confused with update_nearby_tiles(), which is an atmos proc! -/obj/structure/window/proc/update_nearby_icons() - update_icon() - for(var/obj/structure/window/W in orange(src, 1)) - W.update_icon() - -//Updates the availabiliy of the rotation verbs -/obj/structure/window/proc/update_verbs() - if(anchored || is_fulltile()) - verbs -= /obj/structure/window/verb/rotate_counterclockwise - verbs -= /obj/structure/window/verb/rotate_clockwise - else if(!is_fulltile()) - verbs += /obj/structure/window/verb/rotate_counterclockwise - verbs += /obj/structure/window/verb/rotate_clockwise - -//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm) -/obj/structure/window/update_icon() - //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong. - //this way it will only update full-tile ones - cut_overlays() - if(!is_fulltile()) - // Rotate the sprite somewhat so non-fulltiled windows can be seen as needing repair. - var/full_tilt_degrees = 15 - var/tilt_to_apply = abs((health / maxhealth) - 1) - if(tilt_to_apply && prob(50)) - tilt_to_apply = -tilt_to_apply - adjust_rotation(LERP(0, full_tilt_degrees, tilt_to_apply)) - - icon_state = "[basestate]" - return - else - flags = 0 // Removes ON_BORDER and OPPOSITE_OPACITY - var/list/dirs = list() - if(anchored) - for(var/obj/structure/window/W in orange(src,1)) - if(W.anchored && W.density && W.glasstype == src.glasstype && W.is_fulltile()) //Only counts anchored, not-destroyed fill-tile windows. - dirs += get_dir(src, W) - - var/list/connections = dirs_to_corner_states(dirs) - - icon_state = "" - for(var/i = 1 to 4) - var/image/I = image(icon, "[basestate][connections[i]]", dir = 1<<(i-1)) - add_overlay(I) - - // Damage overlays. - var/ratio = health / maxhealth - ratio = CEILING(ratio * 4, 1) * 25 - - if(ratio > 75) - return - var/image/I = image(icon, "damage[ratio]", layer = layer + 0.1) - add_overlay(I) - - return - -/obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) - if(exposed_temperature > maximal_heat) - hit(damage_per_fire_tick, 0) - ..() - - - -/obj/structure/window/basic - desc = "It looks thin and flimsy. A few knocks with... almost anything, really should shatter it." - icon_state = "window" - basestate = "window" - glasstype = /obj/item/stack/material/glass - maximal_heat = T0C + 100 - damage_per_fire_tick = 2.0 - maxhealth = 12.0 - force_threshold = 3 - -/obj/structure/window/basic/full - icon_state = "window-full" - maxhealth = 24 - fulltile = TRUE - flags = 0 - -/obj/structure/window/phoronbasic - name = "phoron window" - desc = "A borosilicate alloy window. It seems to be quite strong." - basestate = "phoronwindow" - icon_state = "phoronwindow" - shardtype = /obj/item/weapon/material/shard/phoron - glasstype = /obj/item/stack/material/glass/phoronglass - maximal_heat = T0C + 2000 - damage_per_fire_tick = 1.0 - maxhealth = 40.0 - force_threshold = 5 - -/obj/structure/window/phoronbasic/full - icon_state = "phoronwindow-full" - maxhealth = 80 - fulltile = TRUE - flags = 0 - -/obj/structure/window/phoronreinforced - name = "reinforced borosilicate window" - desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong." - basestate = "phoronrwindow" - icon_state = "phoronrwindow" - shardtype = /obj/item/weapon/material/shard/phoron - glasstype = /obj/item/stack/material/glass/phoronrglass - reinf = 1 - maximal_heat = T0C + 4000 - damage_per_fire_tick = 1.0 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while. - maxhealth = 80.0 - force_threshold = 10 - -/obj/structure/window/phoronreinforced/full - icon_state = "phoronrwindow-full" - maxhealth = 160 - fulltile = TRUE - flags = 0 - -/obj/structure/window/reinforced - name = "reinforced window" - desc = "It looks rather strong. Might take a few good hits to shatter it." - icon_state = "rwindow" - basestate = "rwindow" - maxhealth = 40.0 - reinf = 1 - maximal_heat = T0C + 750 - damage_per_fire_tick = 2.0 - glasstype = /obj/item/stack/material/glass/reinforced - force_threshold = 6 - -/obj/structure/window/reinforced/full - icon_state = "rwindow-full" - maxhealth = 80 - fulltile = TRUE - flags = 0 - -/obj/structure/window/reinforced/tinted - name = "tinted window" - desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." - icon_state = "twindow" - basestate = "twindow" - opacity = 1 - -/obj/structure/window/reinforced/tinted/frosted - name = "frosted window" - desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." - icon_state = "fwindow" - basestate = "fwindow" - maxhealth = 30 - force_threshold = 5 - -/obj/structure/window/shuttle - name = "shuttle window" - desc = "It looks rather strong. Might take a few good hits to shatter it." - icon = 'icons/obj/podwindows.dmi' - icon_state = "window" - basestate = "window" - maxhealth = 40 - reinf = 1 - basestate = "w" - dir = 5 - force_threshold = 7 - -/obj/structure/window/reinforced/polarized - name = "electrochromic window" - desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it." - var/id - -/obj/structure/window/reinforced/polarized/full - icon_state = "rwindow-full" - maxhealth = 80 - fulltile = TRUE - flags = 0 - -/obj/structure/window/reinforced/polarized/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/device/multitool) && !anchored) // Only allow programming if unanchored! - var/obj/item/device/multitool/MT = W - // First check if they have a windowtint button buffered - if(istype(MT.connectable, /obj/machinery/button/windowtint)) - var/obj/machinery/button/windowtint/buffered_button = MT.connectable - src.id = buffered_button.id - to_chat(user, "\The [src] is linked to \the [buffered_button] with ID '[id]'.") - return TRUE - // Otherwise fall back to asking them... and remind them what the current ID is. - if(id) - to_chat(user, "The window's current ID is [id].") - var/t = sanitizeSafe(input(user, "Enter the new ID for the window.", src.name, null), MAX_NAME_LEN) - if(t && in_range(src, user)) - src.id = t - to_chat(user, "The new ID of \the [src] is '[id]'.") - return TRUE - . = ..() - -/obj/structure/window/reinforced/polarized/proc/toggle() - if(opacity) - animate(src, color="#FFFFFF", time=5) - set_opacity(0) - else - animate(src, color="#222222", time=5) - set_opacity(1) - var/turf/T = get_turf(src) - T.recalculate_directional_opacity() - -/obj/machinery/button/windowtint - name = "window tint control" - icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - New icons - icon_state = "light0" - desc = "A remote control switch for polarized windows." - var/range = 7 - circuit = /obj/item/weapon/circuitboard/electrochromic - -/obj/machinery/button/windowtint/attack_hand(mob/user as mob) - if(..()) - return 1 - - toggle_tint() - -/obj/machinery/button/windowtint/proc/toggle_tint() - use_power(5) - - active = !active - update_icon() - - for(var/obj/structure/window/reinforced/polarized/W in range(src,range)) - if (W.id == src.id || !W.id) - spawn(0) - W.toggle() - return - -/obj/machinery/button/windowtint/power_change() - ..() - if(active && !powered(power_channel)) - toggle_tint() - -/obj/machinery/button/windowtint/update_icon() - icon_state = "light[active]" - -/obj/machinery/button/windowtint/attackby(obj/item/W as obj, mob/user as mob) - if(default_deconstruction_screwdriver(user, W)) - return - else if(alarm_deconstruction_wirecutters(user, W)) - return - else if(istype(W, /obj/item/device/multitool)) - var/obj/item/device/multitool/MT = W - if(!id) - // If no ID is set yet (newly built button?) let them select an ID for first-time use! - var/t = sanitizeSafe(tgui_input_text(user, "Enter an ID for \the [src].", src.name, null, MAX_NAME_LEN), MAX_NAME_LEN) - if (t && in_range(src, user)) - src.id = t - to_chat(user, "The new ID of \the [src] is '[id]'. To reset this, rebuild the control.") - if(id) - // It already has an ID (or they just set one), buffer it for copying to windows. - to_chat(user, "You store \the [src] ID ('[id]') in \the [MT]'s buffer!") - MT.connectable = src - MT.update_icon() - return TRUE - . = ..() - -/obj/structure/window/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - -/obj/structure/window/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - qdel(src) - return TRUE - return FALSE +/obj/structure/window + name = "window" + desc = "A window." + icon = 'icons/obj/structures_vr.dmi' // VOREStation Edit - New icons + density = TRUE + can_atmos_pass = ATMOS_PASS_PROC + w_class = ITEMSIZE_NORMAL + + layer = WINDOW_LAYER + pressure_resistance = 4*ONE_ATMOSPHERE + anchored = TRUE + flags = ON_BORDER + var/maxhealth = 14.0 + var/maximal_heat = T0C + 100 // Maximal heat before this window begins taking damage from fire + var/damage_per_fire_tick = 2.0 // Amount of damage per fire tick. Regular windows are not fireproof so they might as well break quickly. + var/health + var/force_threshold = 0 + var/ini_dir = null + var/state = 2 + var/reinf = 0 + var/basestate + var/shardtype = /obj/item/weapon/material/shard + var/glasstype = null // Set this in subtypes. Null is assumed strange or otherwise impossible to dismantle, such as for shuttle glass. + var/silicate = 0 // number of units of silicate + var/fulltile = FALSE // Set to true on full-tile variants. + +/obj/structure/window/examine(mob/user) + . = ..() + + if(health == maxhealth) + . += "It looks fully intact." + else + var/perc = health / maxhealth + if(perc > 0.75) + . += "It has a few cracks." + else if(perc > 0.5) + . += "It looks slightly damaged." + else if(perc > 0.25) + . += "It looks moderately damaged." + else + . += "It looks heavily damaged." + if(silicate) + if (silicate < 30) + . += "It has a thin layer of silicate." + else if (silicate < 70) + . += "It is covered in silicate." + else + . += "There is a thick layer of silicate covering it." + +/obj/structure/window/examine_icon() + return icon(icon=initial(icon),icon_state=initial(icon_state)) + +/obj/structure/window/take_damage(var/damage = 0, var/sound_effect = 1) + var/initialhealth = health + + if(silicate) + damage = damage * (1 - silicate / 200) + + health = max(0, health - damage) + + if(health <= 0) + shatter() + else + if(sound_effect) + playsound(src, 'sound/effects/Glasshit.ogg', 100, 1) + if(health < maxhealth / 4 && initialhealth >= maxhealth / 4) + visible_message("[src] looks like it's about to shatter!" ) + update_icon() + else if(health < maxhealth / 2 && initialhealth >= maxhealth / 2) + visible_message("[src] looks seriously damaged!" ) + update_icon() + else if(health < maxhealth * 3/4 && initialhealth >= maxhealth * 3/4) + visible_message("Cracks begin to appear in [src]!" ) + update_icon() + return + +/obj/structure/window/proc/apply_silicate(var/amount) + if(health < maxhealth) // Mend the damage + health = min(health + amount * 3, maxhealth) + if(health == maxhealth) + visible_message("[src] looks fully repaired." ) + else // Reinforce + silicate = min(silicate + amount, 100) + updateSilicate() + +/obj/structure/window/proc/updateSilicate() + cut_overlays() + update_icon() + + var/image/img = image(src) + img.color = "#ffffff" + img.alpha = silicate * 255 / 100 + add_overlay(img) + +/obj/structure/window/proc/shatter(var/display_message = 1) + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] shatters!") + new shardtype(loc) + if(reinf) + new /obj/item/stack/rods(loc) + if(is_fulltile()) + new shardtype(loc) //todo pooling? + if(reinf) + new /obj/item/stack/rods(loc) + qdel(src) + return + + +/obj/structure/window/bullet_act(var/obj/item/projectile/Proj) + + var/proj_damage = Proj.get_structure_damage() + if(!proj_damage) return + + ..() + take_damage(proj_damage) + return + + +/obj/structure/window/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + shatter(0) + return + if(3.0) + if(prob(50)) + shatter(0) + return + +/obj/structure/window/blob_act() + take_damage(50) + +/obj/structure/window/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(is_fulltile()) + return FALSE //full tile window, you can't move into it! + if(get_dir(mover, target) == reverse_dir[dir]) // From elsewhere to here, can't move against our dir + return !density + else + return TRUE + +/obj/structure/window/Uncross(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir + return !density + else + return TRUE + +/obj/structure/window/CanZASPass(turf/T, is_zone) + if(is_fulltile() || get_dir(T, loc) == turn(dir, 180)) // Make sure we're handling the border correctly. + return !anchored // If it's anchored, it'll block air. + return TRUE // Don't stop airflow from the other sides. + +/obj/structure/window/hitby(AM as mob|obj) + ..() + visible_message("[src] was hit by [AM].") + var/tforce = 0 + if(ismob(AM)) + tforce = 40 + else if(isobj(AM)) + var/obj/item/I = AM + tforce = I.throwforce + if(reinf) tforce *= 0.25 + if(health - tforce <= 7 && !reinf) + anchored = FALSE + update_verbs() + update_nearby_icons() + step(src, get_dir(AM, src)) + take_damage(tforce) + +/obj/structure/window/attack_tk(mob/user as mob) + user.visible_message("Something knocks on [src].") + playsound(src, 'sound/effects/Glasshit.ogg', 50, 1) + +/obj/structure/window/attack_hand(mob/user as mob) + user.setClickCooldown(user.get_attack_speed()) + if(HULK in user.mutations) + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) + user.visible_message("[user] smashes through [src]!") + user.do_attack_animation(src) + shatter() + + else if (user.a_intent == I_HURT) + + if (istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + attack_generic(H,25) + return + + playsound(src, 'sound/effects/glassknock.ogg', 80, 1) + user.do_attack_animation(src) + user.visible_message("\The [user] bangs against \the [src]!", + "You bang against \the [src]!", + "You hear a banging sound.") + else + playsound(src, 'sound/effects/glassknock.ogg', 80, 1) + user.visible_message("[user.name] knocks on the [src.name].", + "You knock on the [src.name].", + "You hear a knocking sound.") + return + +/obj/structure/window/attack_generic(var/mob/user, var/damage) + user.setClickCooldown(user.get_attack_speed()) + if(!damage) + return + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) + visible_message("[user] smashes into [src]!") + if(reinf) + damage = damage / 2 + take_damage(damage) + else + visible_message("\The [user] bonks \the [src] harmlessly.") + user.do_attack_animation(src) + return 1 + +/obj/structure/window/attackby(obj/item/W as obj, mob/user as mob) + if(!istype(W)) return//I really wish I did not need this + + // Fixing. + if(W.has_tool_quality(TOOL_WELDER) && user.a_intent == I_HELP) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(health < maxhealth) + if(WT.remove_fuel(1 ,user)) + to_chat(user, "You begin repairing [src]...") + playsound(src, WT.usesound, 50, 1) + if(do_after(user, 40 * WT.toolspeed, target = src)) + health = maxhealth + // playsound(src, 'sound/items/Welder.ogg', 50, 1) + update_icon() + to_chat(user, "You repair [src].") + else + to_chat(user, "[src] is already in good condition!") + return + + // Slamming. + if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2) + var/obj/item/weapon/grab/G = W + if(istype(G.affecting,/mob/living)) + var/mob/living/M = G.affecting + var/state = G.state + qdel(W) //gotta delete it here because if window breaks, it won't get deleted + switch (state) + if(1) + M.visible_message("[user] slams [M] against \the [src]!") + M.apply_damage(7) + hit(10) + if(2) + M.visible_message("[user] bashes [M] against \the [src]!") + if (prob(50)) + M.Weaken(1) + M.apply_damage(10) + hit(25) + if(3) + M.visible_message("[user] crushes [M] against \the [src]!") + M.Weaken(5) + M.apply_damage(20) + hit(50) + return + + if(W.flags & NOBLUDGEON) return + + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(reinf && state >= 1) + state = 3 - state + update_nearby_icons() + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have [state == 1 ? "un" : ""]fastened the window [state ? "from" : "to"] the frame.") + else if(reinf && state == 0) + anchored = !anchored + update_nearby_tiles(need_rebuild=1) + update_nearby_icons() + update_verbs() + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have [anchored ? "" : "un"]fastened the frame [anchored ? "to" : "from"] the floor.") + else if(!reinf) + anchored = !anchored + update_nearby_tiles(need_rebuild=1) + update_nearby_icons() + update_verbs() + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have [anchored ? "" : "un"]fastened the window [anchored ? "to" : "from"] the floor.") + else if(W.has_tool_quality(TOOL_CROWBAR) && reinf && state <= 1) + state = 1 - state + playsound(src, W.usesound, 75, 1) + to_chat(user, "You have pried the window [state ? "into" : "out of"] the frame.") + else if(W.has_tool_quality(TOOL_WRENCH) && !anchored && (!state || !reinf)) + if(!glasstype) + to_chat(user, "You're not sure how to dismantle \the [src] properly.") + else + playsound(src, W.usesound, 75, 1) + visible_message("[user] dismantles \the [src].") + var/obj/item/stack/material/mats = new glasstype(loc) + if(is_fulltile()) + mats.set_amount(4) + qdel(src) + else if(istype(W, /obj/item/stack/cable_coil) && reinf && state == 0 && !istype(src, /obj/structure/window/reinforced/polarized)) + var/obj/item/stack/cable_coil/C = W + if (C.use(1)) + playsound(src, 'sound/effects/sparks1.ogg', 75, 1) + user.visible_message( \ + "\The [user] begins to wire \the [src] for electrochromic tinting.", \ + "You begin to wire \the [src] for electrochromic tinting.", \ + "You hear sparks.") + if(do_after(user, 20 * C.toolspeed, src) && state == 0) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + var/obj/structure/window/reinforced/polarized/P = new(loc, dir) + if(is_fulltile()) + P.fulltile = TRUE + P.icon_state = "fwindow" + P.maxhealth = maxhealth + P.health = health + P.state = state + P.anchored = anchored + qdel(src) + else if(istype(W,/obj/item/frame) && anchored) + var/obj/item/frame/F = W + F.try_build(src, user) + else + user.setClickCooldown(user.get_attack_speed(W)) + if(W.damtype == BRUTE || W.damtype == BURN) + user.do_attack_animation(src) + hit(W.force) + if(health <= 7) + anchored = FALSE + update_nearby_icons() + step(src, get_dir(user, src)) + else + playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) + ..() + return + +/obj/structure/window/proc/hit(var/damage, var/sound_effect = 1) + if(damage < force_threshold || force_threshold < 0) + return + if(reinf) damage *= 0.5 + take_damage(damage) + return + + +/obj/structure/window/verb/rotate_counterclockwise() + set name = "Rotate Window Counterclockwise" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return 0 + + if(is_fulltile()) + return 0 + + if(anchored) + to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") + return 0 + + update_nearby_tiles(need_rebuild=1) //Compel updates before + src.set_dir(turn(src.dir, 90)) + updateSilicate() + update_nearby_tiles(need_rebuild=1) + return + + +/obj/structure/window/verb/rotate_clockwise() + set name = "Rotate Window Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return 0 + + if(is_fulltile()) + return 0 + + if(anchored) + to_chat(usr, "It is fastened to the floor therefore you can't rotate it!") + return 0 + + update_nearby_tiles(need_rebuild=1) //Compel updates before + src.set_dir(turn(src.dir, 270)) + updateSilicate() + update_nearby_tiles(need_rebuild=1) + return + +/obj/structure/window/New(Loc, start_dir=null, constructed=0) + ..() + + if (start_dir) + set_dir(start_dir) + + //player-constructed windows + if (constructed) + anchored = FALSE + state = 0 + update_verbs() + + health = maxhealth + + ini_dir = dir + + update_nearby_tiles(need_rebuild=1) + update_nearby_icons() + + +/obj/structure/window/Destroy() + density = FALSE + update_nearby_tiles() + var/turf/location = loc + . = ..() + for(var/obj/structure/window/W in orange(location, 1)) + W.update_icon() + +/obj/structure/window/Move() + var/ini_dir = dir + update_nearby_tiles(need_rebuild=1) + . = ..() + set_dir(ini_dir) + update_nearby_tiles(need_rebuild=1) + +//checks if this window is full-tile one +/obj/structure/window/proc/is_fulltile() + return fulltile + +/obj/structure/window/is_between_turfs(var/turf/origin, var/turf/target) + if(is_fulltile()) + return TRUE + return ..() + +//This proc is used to update the icons of nearby windows. It should not be confused with update_nearby_tiles(), which is an atmos proc! +/obj/structure/window/proc/update_nearby_icons() + update_icon() + for(var/obj/structure/window/W in orange(src, 1)) + W.update_icon() + +//Updates the availabiliy of the rotation verbs +/obj/structure/window/proc/update_verbs() + if(anchored || is_fulltile()) + verbs -= /obj/structure/window/verb/rotate_counterclockwise + verbs -= /obj/structure/window/verb/rotate_clockwise + else if(!is_fulltile()) + verbs += /obj/structure/window/verb/rotate_counterclockwise + verbs += /obj/structure/window/verb/rotate_clockwise + +//merges adjacent full-tile windows into one (blatant ripoff from game/smoothwall.dm) +/obj/structure/window/update_icon() + //A little cludge here, since I don't know how it will work with slim windows. Most likely VERY wrong. + //this way it will only update full-tile ones + cut_overlays() + if(!is_fulltile()) + // Rotate the sprite somewhat so non-fulltiled windows can be seen as needing repair. + var/full_tilt_degrees = 15 + var/tilt_to_apply = abs((health / maxhealth) - 1) + if(tilt_to_apply && prob(50)) + tilt_to_apply = -tilt_to_apply + adjust_rotation(LERP(0, full_tilt_degrees, tilt_to_apply)) + + icon_state = "[basestate]" + return + else + flags = 0 // Removes ON_BORDER and OPPOSITE_OPACITY + var/list/dirs = list() + if(anchored) + for(var/obj/structure/window/W in orange(src,1)) + if(W.anchored && W.density && W.glasstype == src.glasstype && W.is_fulltile()) //Only counts anchored, not-destroyed fill-tile windows. + dirs += get_dir(src, W) + + var/list/connections = dirs_to_corner_states(dirs) + + icon_state = "" + for(var/i = 1 to 4) + var/image/I = image(icon, "[basestate][connections[i]]", dir = 1<<(i-1)) + add_overlay(I) + + // Damage overlays. + var/ratio = health / maxhealth + ratio = CEILING(ratio * 4, 1) * 25 + + if(ratio > 75) + return + var/image/I = image(icon, "damage[ratio]", layer = layer + 0.1) + add_overlay(I) + + return + +/obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(exposed_temperature > maximal_heat) + hit(damage_per_fire_tick, 0) + ..() + + + +/obj/structure/window/basic + desc = "It looks thin and flimsy. A few knocks with... almost anything, really should shatter it." + icon_state = "window" + basestate = "window" + glasstype = /obj/item/stack/material/glass + maximal_heat = T0C + 100 + damage_per_fire_tick = 2.0 + maxhealth = 12.0 + force_threshold = 3 + +/obj/structure/window/basic/full + icon_state = "window-full" + maxhealth = 24 + fulltile = TRUE + flags = 0 + +/obj/structure/window/phoronbasic + name = "phoron window" + desc = "A borosilicate alloy window. It seems to be quite strong." + basestate = "phoronwindow" + icon_state = "phoronwindow" + shardtype = /obj/item/weapon/material/shard/phoron + glasstype = /obj/item/stack/material/glass/phoronglass + maximal_heat = T0C + 2000 + damage_per_fire_tick = 1.0 + maxhealth = 40.0 + force_threshold = 5 + +/obj/structure/window/phoronbasic/full + icon_state = "phoronwindow-full" + maxhealth = 80 + fulltile = TRUE + flags = 0 + +/obj/structure/window/phoronreinforced + name = "reinforced borosilicate window" + desc = "A borosilicate alloy window, with rods supporting it. It seems to be very strong." + basestate = "phoronrwindow" + icon_state = "phoronrwindow" + shardtype = /obj/item/weapon/material/shard/phoron + glasstype = /obj/item/stack/material/glass/phoronrglass + reinf = 1 + maximal_heat = T0C + 4000 + damage_per_fire_tick = 1.0 // This should last for 80 fire ticks if the window is not damaged at all. The idea is that borosilicate windows have something like ablative layer that protects them for a while. + maxhealth = 80.0 + force_threshold = 10 + +/obj/structure/window/phoronreinforced/full + icon_state = "phoronrwindow-full" + maxhealth = 160 + fulltile = TRUE + flags = 0 + +/obj/structure/window/reinforced + name = "reinforced window" + desc = "It looks rather strong. Might take a few good hits to shatter it." + icon_state = "rwindow" + basestate = "rwindow" + maxhealth = 40.0 + reinf = 1 + maximal_heat = T0C + 750 + damage_per_fire_tick = 2.0 + glasstype = /obj/item/stack/material/glass/reinforced + force_threshold = 6 + +/obj/structure/window/reinforced/full + icon_state = "rwindow-full" + maxhealth = 80 + fulltile = TRUE + flags = 0 + +/obj/structure/window/reinforced/tinted + name = "tinted window" + desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." + icon_state = "twindow" + basestate = "twindow" + opacity = 1 + +/obj/structure/window/reinforced/tinted/frosted + name = "frosted window" + desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." + icon_state = "fwindow" + basestate = "fwindow" + maxhealth = 30 + force_threshold = 5 + +/obj/structure/window/shuttle + name = "shuttle window" + desc = "It looks rather strong. Might take a few good hits to shatter it." + icon = 'icons/obj/podwindows.dmi' + icon_state = "window" + basestate = "window" + maxhealth = 40 + reinf = 1 + basestate = "w" + dir = 5 + force_threshold = 7 + +/obj/structure/window/reinforced/polarized + name = "electrochromic window" + desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it." + var/id + +/obj/structure/window/reinforced/polarized/full + icon_state = "rwindow-full" + maxhealth = 80 + fulltile = TRUE + flags = 0 + +/obj/structure/window/reinforced/polarized/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/device/multitool) && !anchored) // Only allow programming if unanchored! + var/obj/item/device/multitool/MT = W + // First check if they have a windowtint button buffered + if(istype(MT.connectable, /obj/machinery/button/windowtint)) + var/obj/machinery/button/windowtint/buffered_button = MT.connectable + src.id = buffered_button.id + to_chat(user, "\The [src] is linked to \the [buffered_button] with ID '[id]'.") + return TRUE + // Otherwise fall back to asking them... and remind them what the current ID is. + if(id) + to_chat(user, "The window's current ID is [id].") + var/t = sanitizeSafe(input(user, "Enter the new ID for the window.", src.name, null), MAX_NAME_LEN) + if(t && in_range(src, user)) + src.id = t + to_chat(user, "The new ID of \the [src] is '[id]'.") + return TRUE + . = ..() + +/obj/structure/window/reinforced/polarized/proc/toggle() + if(opacity) + animate(src, color="#FFFFFF", time=5) + set_opacity(0) + else + animate(src, color="#222222", time=5) + set_opacity(1) + var/turf/T = get_turf(src) + T.recalculate_directional_opacity() + +/obj/machinery/button/windowtint + name = "window tint control" + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit - New icons + icon_state = "light0" + desc = "A remote control switch for polarized windows." + var/range = 7 + circuit = /obj/item/weapon/circuitboard/electrochromic + +/obj/machinery/button/windowtint/attack_hand(mob/user as mob) + if(..()) + return 1 + + toggle_tint() + +/obj/machinery/button/windowtint/proc/toggle_tint() + use_power(5) + + active = !active + update_icon() + + for(var/obj/structure/window/reinforced/polarized/W in range(src,range)) + if (W.id == src.id || !W.id) + spawn(0) + W.toggle() + return + +/obj/machinery/button/windowtint/power_change() + ..() + if(active && !powered(power_channel)) + toggle_tint() + +/obj/machinery/button/windowtint/update_icon() + icon_state = "light[active]" + +/obj/machinery/button/windowtint/attackby(obj/item/W as obj, mob/user as mob) + if(default_deconstruction_screwdriver(user, W)) + return + else if(alarm_deconstruction_wirecutters(user, W)) + return + else if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/MT = W + if(!id) + // If no ID is set yet (newly built button?) let them select an ID for first-time use! + var/t = sanitizeSafe(tgui_input_text(user, "Enter an ID for \the [src].", src.name, null, MAX_NAME_LEN), MAX_NAME_LEN) + if (t && in_range(src, user)) + src.id = t + to_chat(user, "The new ID of \the [src] is '[id]'. To reset this, rebuild the control.") + if(id) + // It already has an ID (or they just set one), buffer it for copying to windows. + to_chat(user, "You store \the [src] ID ('[id]') in \the [MT]'s buffer!") + MT.connectable = src + MT.update_icon() + return TRUE + . = ..() + +/obj/structure/window/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + +/obj/structure/window/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + qdel(src) + return TRUE + return FALSE diff --git a/code/game/response_team.dm b/code/game/response_team.dm index 2606ae0b756..a50d4342072 100644 --- a/code/game/response_team.dm +++ b/code/game/response_team.dm @@ -1,138 +1,138 @@ -//STRIKE TEAMS -//Thanks to Kilakk for the admin-button portion of this code. - -var/global/send_emergency_team = 0 // Used for automagic response teams - // 'admin_emergency_team' for admin-spawned response teams -var/ert_base_chance = 10 // Default base chance. Will be incremented by increment ERT chance. -var/can_call_ert -var/silent_ert = 0 - -/client/proc/response_team() - set name = "Dispatch Emergency Response Team" - set category = "Special Verbs" - set desc = "Send an emergency response team to the station" - - if(!holder) - to_chat(usr, "Only administrators may use this command.") - return - if(!ticker) - to_chat(usr, "The game hasn't started yet!") - return - if(ticker.current_state == 1) - to_chat(usr, "The round hasn't started yet!") - return - if(send_emergency_team) - to_chat(usr, "[using_map.boss_name] has already dispatched an emergency response team!") - return - if(tgui_alert(usr, "Do you want to dispatch an Emergency Response Team?","ERT",list("Yes","No")) != "Yes") - return - if(tgui_alert(usr, "Do you want this Response Team to be announced?","ERT",list("Yes","No")) != "Yes") - silent_ert = 1 - if(get_security_level() != "red") // Allow admins to reconsider if the alert level isn't Red - switch(tgui_alert(usr, "The station is not in red alert. Do you still want to dispatch a response team?","ERT",list("Yes","No"))) - if("No") - return - if(send_emergency_team) - to_chat(usr, "Looks like somebody beat you to it!") - return - - message_admins("[key_name_admin(usr)] is dispatching an Emergency Response Team.", 1) - admin_chat_message(message = "[key_name(usr)] is dispatching an Emergency Response Team", color = "#CC2222") //VOREStation Add - log_admin("[key_name(usr)] used Dispatch Response Team.") - trigger_armed_response_team(1) - -/client/verb/JoinResponseTeam() - - set name = "Join Response Team" - set category = "IC" - - if(!MayRespawn(1)) - to_chat(usr, "You cannot join the response team at this time.") - return - - if(istype(usr,/mob/observer/dead) || istype(usr,/mob/new_player)) - if(!send_emergency_team) - to_chat(usr, "No emergency response team is currently being sent.") - return - if(jobban_isbanned(usr, "Syndicate") || jobban_isbanned(usr, "Emergency Response Team") || jobban_isbanned(usr, "Security Officer")) - to_chat(usr, "You are jobbanned from the emergency reponse team!") - return - if(ert.current_antagonists.len >= ert.hard_cap) - to_chat(usr, "The emergency response team is already full!") - return - ert.create_default(usr) - else - to_chat(usr, "You need to be an observer or new player to use this.") - -// returns a number of dead players in % -/proc/percentage_dead() - var/total = 0 - var/deadcount = 0 - for(var/mob/living/carbon/human/H in mob_list) - if(H.client) // Monkeys and mice don't have a client, amirite? - if(H.stat == 2) deadcount++ - total++ - - if(total == 0) return 0 - else return round(100 * deadcount / total) - -// counts the number of antagonists in % -/proc/percentage_antagonists() - var/total = 0 - var/antagonists = 0 - for(var/mob/living/carbon/human/H in mob_list) - if(is_special_character(H) >= 1) - antagonists++ - total++ - - if(total == 0) return 0 - else return round(100 * antagonists / total) - -// Increments the ERT chance automatically, so that the later it is in the round, -// the more likely an ERT is to be able to be called. -/proc/increment_ert_chance() - while(send_emergency_team == 0) // There is no ERT at the time. - if(get_security_level() == "green") - ert_base_chance += 1 - if(get_security_level() == "yellow") - ert_base_chance += 1 - if(get_security_level() == "violet") - ert_base_chance += 2 - if(get_security_level() == "orange") - ert_base_chance += 2 - if(get_security_level() == "blue") - ert_base_chance += 2 - if(get_security_level() == "red") - ert_base_chance += 3 - if(get_security_level() == "delta") - ert_base_chance += 10 // Need those big guns - sleep(600 * 3) // Minute * Number of Minutes - - -/proc/trigger_armed_response_team(var/force = 0) - if(!can_call_ert && !force) - return - if(send_emergency_team) - return - - var/send_team_chance = ert_base_chance // Is incremented by increment_ert_chance. - send_team_chance += 2*percentage_dead() // the more people are dead, the higher the chance - send_team_chance += percentage_antagonists() // the more antagonists, the higher the chance - send_team_chance = min(send_team_chance, 100) - - if(force) send_team_chance = 100 - - // there's only a certain chance a team will be sent - if(!prob(send_team_chance)) - command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. Unfortunately, we were unable to send one at this time.", "[using_map.boss_name]") - can_call_ert = 0 // Only one call per round, ladies. - return - if(silent_ert == 0) - command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. We will prepare and send one as soon as possible.", "[using_map.boss_name]") - - can_call_ert = 0 // Only one call per round, gentleman. - send_emergency_team = 1 - consider_ert_load() //VOREStation Add - - sleep(600 * 5) - send_emergency_team = 0 // Can no longer join the ERT. +//STRIKE TEAMS +//Thanks to Kilakk for the admin-button portion of this code. + +var/global/send_emergency_team = 0 // Used for automagic response teams + // 'admin_emergency_team' for admin-spawned response teams +var/ert_base_chance = 10 // Default base chance. Will be incremented by increment ERT chance. +var/can_call_ert +var/silent_ert = 0 + +/client/proc/response_team() + set name = "Dispatch Emergency Response Team" + set category = "Special Verbs" + set desc = "Send an emergency response team to the station" + + if(!holder) + to_chat(usr, "Only administrators may use this command.") + return + if(!ticker) + to_chat(usr, "The game hasn't started yet!") + return + if(ticker.current_state == 1) + to_chat(usr, "The round hasn't started yet!") + return + if(send_emergency_team) + to_chat(usr, "[using_map.boss_name] has already dispatched an emergency response team!") + return + if(tgui_alert(usr, "Do you want to dispatch an Emergency Response Team?","ERT",list("Yes","No")) != "Yes") + return + if(tgui_alert(usr, "Do you want this Response Team to be announced?","ERT",list("Yes","No")) != "Yes") + silent_ert = 1 + if(get_security_level() != "red") // Allow admins to reconsider if the alert level isn't Red + switch(tgui_alert(usr, "The station is not in red alert. Do you still want to dispatch a response team?","ERT",list("Yes","No"))) + if("No") + return + if(send_emergency_team) + to_chat(usr, "Looks like somebody beat you to it!") + return + + message_admins("[key_name_admin(usr)] is dispatching an Emergency Response Team.", 1) + admin_chat_message(message = "[key_name(usr)] is dispatching an Emergency Response Team", color = "#CC2222") //VOREStation Add + log_admin("[key_name(usr)] used Dispatch Response Team.") + trigger_armed_response_team(1) + +/client/verb/JoinResponseTeam() + + set name = "Join Response Team" + set category = "IC" + + if(!MayRespawn(1)) + to_chat(usr, "You cannot join the response team at this time.") + return + + if(istype(usr,/mob/observer/dead) || istype(usr,/mob/new_player)) + if(!send_emergency_team) + to_chat(usr, "No emergency response team is currently being sent.") + return + if(jobban_isbanned(usr, "Syndicate") || jobban_isbanned(usr, "Emergency Response Team") || jobban_isbanned(usr, "Security Officer")) + to_chat(usr, "You are jobbanned from the emergency reponse team!") + return + if(ert.current_antagonists.len >= ert.hard_cap) + to_chat(usr, "The emergency response team is already full!") + return + ert.create_default(usr) + else + to_chat(usr, "You need to be an observer or new player to use this.") + +// returns a number of dead players in % +/proc/percentage_dead() + var/total = 0 + var/deadcount = 0 + for(var/mob/living/carbon/human/H in mob_list) + if(H.client) // Monkeys and mice don't have a client, amirite? + if(H.stat == 2) deadcount++ + total++ + + if(total == 0) return 0 + else return round(100 * deadcount / total) + +// counts the number of antagonists in % +/proc/percentage_antagonists() + var/total = 0 + var/antagonists = 0 + for(var/mob/living/carbon/human/H in mob_list) + if(is_special_character(H) >= 1) + antagonists++ + total++ + + if(total == 0) return 0 + else return round(100 * antagonists / total) + +// Increments the ERT chance automatically, so that the later it is in the round, +// the more likely an ERT is to be able to be called. +/proc/increment_ert_chance() + while(send_emergency_team == 0) // There is no ERT at the time. + if(get_security_level() == "green") + ert_base_chance += 1 + if(get_security_level() == "yellow") + ert_base_chance += 1 + if(get_security_level() == "violet") + ert_base_chance += 2 + if(get_security_level() == "orange") + ert_base_chance += 2 + if(get_security_level() == "blue") + ert_base_chance += 2 + if(get_security_level() == "red") + ert_base_chance += 3 + if(get_security_level() == "delta") + ert_base_chance += 10 // Need those big guns + sleep(600 * 3) // Minute * Number of Minutes + + +/proc/trigger_armed_response_team(var/force = 0) + if(!can_call_ert && !force) + return + if(send_emergency_team) + return + + var/send_team_chance = ert_base_chance // Is incremented by increment_ert_chance. + send_team_chance += 2*percentage_dead() // the more people are dead, the higher the chance + send_team_chance += percentage_antagonists() // the more antagonists, the higher the chance + send_team_chance = min(send_team_chance, 100) + + if(force) send_team_chance = 100 + + // there's only a certain chance a team will be sent + if(!prob(send_team_chance)) + command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. Unfortunately, we were unable to send one at this time.", "[using_map.boss_name]") + can_call_ert = 0 // Only one call per round, ladies. + return + if(silent_ert == 0) + command_announcement.Announce("It would appear that an emergency response team was requested for [station_name()]. We will prepare and send one as soon as possible.", "[using_map.boss_name]") + + can_call_ert = 0 // Only one call per round, gentleman. + send_emergency_team = 1 + consider_ert_load() //VOREStation Add + + sleep(600 * 5) + send_emergency_team = 0 // Can no longer join the ERT. diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm index e68b33089a8..4903aed6c76 100644 --- a/code/game/shuttle_engines.dm +++ b/code/game/shuttle_engines.dm @@ -1,81 +1,81 @@ -/obj/structure/shuttle - name = "shuttle" - icon = 'icons/turf/shuttle_parts.dmi' - unacidable = TRUE - -/obj/structure/shuttle/window - name = "shuttle window" - icon = 'icons/obj/podwindows.dmi' - icon_state = "0_0" //The states are a bitflag for connecting window directions, then connecting shuttle wall directions - density = TRUE - opacity = 0 - anchored = TRUE - can_atmos_pass = ATMOS_PASS_NO - - var/window_flags = 0 // Bitflags to indicate connected windows - var/wall_flags = 0 // Bitflags to indicate connected walls - -/obj/structure/shuttle/window/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return TRUE - return ..() - -/obj/structure/shuttle/window/Initialize() - . = ..() - auto_join() - -/obj/structure/shuttle/window/proc/auto_join() - match_windows(NORTH, NORTH) - match_windows(EAST, EAST) - match_windows(SOUTH, SOUTH) - match_windows(WEST, WEST) - - icon_state = "[window_flags]_[wall_flags]" - - return icon_state - -/obj/structure/shuttle/window/proc/match_windows(direction, flag, mask=0) - var/turf/adj = get_step(src, direction) - - if(istype(adj,/turf/simulated/shuttle/wall)) - wall_flags |= flag // turn on the bit flag - else - var/obj/structure/shuttle/window/window = locate(src.type) in adj - if(window) - window_flags |= flag // turn on the bit flag - else - window_flags &= ~flag // turn off the bit flag - wall_flags &= ~flag // turn off the bit flag - -/obj/structure/shuttle/engine - name = "engine" - density = TRUE - anchored = TRUE - -/obj/structure/shuttle/engine/heater - name = "heater" - icon_state = "heater" - -/obj/structure/shuttle/engine/platform - name = "platform" - icon_state = "platform" - -/obj/structure/shuttle/engine/propulsion - name = "propulsion" - icon_state = "propulsion" - opacity = 1 - -/obj/structure/shuttle/engine/propulsion/burst - name = "burst" - -/obj/structure/shuttle/engine/propulsion/burst/left - name = "left" - icon_state = "burst_l" - -/obj/structure/shuttle/engine/propulsion/burst/right - name = "right" - icon_state = "burst_r" - -/obj/structure/shuttle/engine/router - name = "router" - icon_state = "router" +/obj/structure/shuttle + name = "shuttle" + icon = 'icons/turf/shuttle_parts.dmi' + unacidable = TRUE + +/obj/structure/shuttle/window + name = "shuttle window" + icon = 'icons/obj/podwindows.dmi' + icon_state = "0_0" //The states are a bitflag for connecting window directions, then connecting shuttle wall directions + density = TRUE + opacity = 0 + anchored = TRUE + can_atmos_pass = ATMOS_PASS_NO + + var/window_flags = 0 // Bitflags to indicate connected windows + var/wall_flags = 0 // Bitflags to indicate connected walls + +/obj/structure/shuttle/window/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return TRUE + return ..() + +/obj/structure/shuttle/window/Initialize() + . = ..() + auto_join() + +/obj/structure/shuttle/window/proc/auto_join() + match_windows(NORTH, NORTH) + match_windows(EAST, EAST) + match_windows(SOUTH, SOUTH) + match_windows(WEST, WEST) + + icon_state = "[window_flags]_[wall_flags]" + + return icon_state + +/obj/structure/shuttle/window/proc/match_windows(direction, flag, mask=0) + var/turf/adj = get_step(src, direction) + + if(istype(adj,/turf/simulated/shuttle/wall)) + wall_flags |= flag // turn on the bit flag + else + var/obj/structure/shuttle/window/window = locate(src.type) in adj + if(window) + window_flags |= flag // turn on the bit flag + else + window_flags &= ~flag // turn off the bit flag + wall_flags &= ~flag // turn off the bit flag + +/obj/structure/shuttle/engine + name = "engine" + density = TRUE + anchored = TRUE + +/obj/structure/shuttle/engine/heater + name = "heater" + icon_state = "heater" + +/obj/structure/shuttle/engine/platform + name = "platform" + icon_state = "platform" + +/obj/structure/shuttle/engine/propulsion + name = "propulsion" + icon_state = "propulsion" + opacity = 1 + +/obj/structure/shuttle/engine/propulsion/burst + name = "burst" + +/obj/structure/shuttle/engine/propulsion/burst/left + name = "left" + icon_state = "burst_l" + +/obj/structure/shuttle/engine/propulsion/burst/right + name = "right" + icon_state = "burst_r" + +/obj/structure/shuttle/engine/router + name = "router" + icon_state = "router" diff --git a/code/game/skincmd.dm b/code/game/skincmd.dm index fd9131538e4..f8aa4c4b74b 100644 --- a/code/game/skincmd.dm +++ b/code/game/skincmd.dm @@ -1,13 +1,13 @@ -/mob/var/skincmds = list() -/obj/proc/SkinCmd(mob/user as mob, var/data as text) - -/proc/SkinCmdRegister(var/mob/user, var/name as text, var/O as obj) - user.skincmds[name] = O - -/mob/verb/skincmd(data as text) - set hidden = 1 - - var/ref = copytext(data, 1, findtext(data, ";")) - if (src.skincmds[ref] != null) - var/obj/a = src.skincmds[ref] +/mob/var/skincmds = list() +/obj/proc/SkinCmd(mob/user as mob, var/data as text) + +/proc/SkinCmdRegister(var/mob/user, var/name as text, var/O as obj) + user.skincmds[name] = O + +/mob/verb/skincmd(data as text) + set hidden = 1 + + var/ref = copytext(data, 1, findtext(data, ";")) + if (src.skincmds[ref] != null) + var/obj/a = src.skincmds[ref] a.SkinCmd(src, copytext(data, findtext(data, ";") + 1)) \ No newline at end of file diff --git a/code/game/sound.dm b/code/game/sound.dm index 0833824224f..256dae7597c 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -1,312 +1,312 @@ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, preference = null, volume_channel = null) - if(Master.current_runlevel < RUNLEVEL_LOBBY) - return - - var/turf/turf_source = get_turf(source) - if(!turf_source) - return - var/area/area_source = turf_source.loc - - //allocate a channel if necessary now so its the same for everyone - channel = channel || SSsounds.random_available_channel() - - // Looping through the player list has the added bonus of working for mobs inside containers - var/sound/S = sound(get_sfx(soundin)) - var/maxdistance = (world.view + extrarange) * 2 //VOREStation Edit - 3 to 2 - var/list/listeners = player_list.Copy() - if(!ignore_walls) //these sounds don't carry through walls - for(var/mob/listen in listeners) - if(!(get_turf(listen) in hear(maxdistance,source))) - listeners -= listen - for(var/mob/M as anything in listeners) - if(!M || !M.client) - continue - var/turf/T = get_turf(M) - if(!T) - continue - var/area/A = T.loc - if((A.soundproofed || area_source.soundproofed) && (A != area_source)) - continue - var/distance = get_dist(T, turf_source) - - if(distance <= maxdistance) - if(T && T.z == turf_source.z) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel) - -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference, volume_channel = null) - if(!client || ear_deaf > 0) - return - if(preference && !client.is_preference_enabled(preference)) - return - - if(!S) - S = sound(get_sfx(soundin)) - - S.wait = 0 //No queue - S.channel = channel || SSsounds.random_available_channel() - - // I'm not sure if you can modify S.volume, but I'd rather not try to find out what - // horrible things lurk in BYOND's internals, so we're just gonna do vol *= - vol *= client.get_preference_volume_channel(volume_channel) - vol *= client.get_preference_volume_channel(VOLUME_CHANNEL_MASTER) - S.volume = vol - - if(vary) - if(frequency) - S.frequency = frequency - else - S.frequency = get_rand_frequency() - - if(isturf(turf_source)) - var/turf/T = get_turf(src) - - //sound volume falloff with distance - var/distance = get_dist(T, turf_source) - - S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. - - //Atmosphere affects sound - var/pressure_factor = 1 - if(pressure_affected) - var/datum/gas_mixture/hearer_env = T.return_air() - var/datum/gas_mixture/source_env = turf_source.return_air() - - if(hearer_env && source_env) - var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) - if(pressure < ONE_ATMOSPHERE) - pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) - else //space - pressure_factor = 0 - - if(distance <= 1) - pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound - - S.volume *= pressure_factor - //End Atmosphere affecting sound - - //Don't bother with doing anything below. - if(S.volume <= 0) - return //No sound - - //Apply a sound environment. - if(!is_global) - S.environment = get_sound_env(pressure_factor) - - var/dx = turf_source.x - T.x // Hearing from the right/left - S.x = dx - var/dz = turf_source.y - T.y // Hearing from infront/behind - S.z = dz - // The y value is for above your head, but there is no ceiling in 2d spessmens. - S.y = 1 - S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) - - src << S - -/proc/sound_to_playing_players(sound, volume = 100, vary) - sound = get_sfx(sound) - for(var/M in player_list) - if(ismob(M) && !isnewplayer(M)) - var/mob/MO = M - MO.playsound_local(get_turf(MO), sound, volume, vary, pressure_affected = FALSE) - -/mob/proc/stop_sound_channel(chan) - src << sound(null, repeat = 0, wait = 0, channel = chan) - -/mob/proc/set_sound_channel_volume(channel, volume) - var/sound/S = sound(null, FALSE, FALSE, channel, volume) - S.status = SOUND_UPDATE - src << S - -/proc/get_rand_frequency() - return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. - -/client/proc/playtitlemusic() - if(!ticker || !SSmedia_tracks.lobby_tracks.len || !media) return - if(is_preference_enabled(/datum/client_preference/play_lobby_music)) - var/datum/track/T = pick(SSmedia_tracks.lobby_tracks) - media.push_music(T.url, world.time, 0.85) - to_chat(src,"Lobby music: [T.title] by [T.artist].") - -/proc/get_sfx(soundin) - if(istext(soundin)) - switch(soundin) - if ("shatter") soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') - if ("explosion") soundin = pick('sound/effects/Explosion1.ogg','sound/effects/Explosion2.ogg','sound/effects/Explosion3.ogg','sound/effects/Explosion4.ogg','sound/effects/Explosion5.ogg','sound/effects/Explosion6.ogg') - if ("sparks") soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks5.ogg','sound/effects/sparks6.ogg','sound/effects/sparks7.ogg') - if ("rustle") soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') - if ("punch") soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') - if ("clownstep") soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') - if ("swing_hit") soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') - if ("hiss") soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') - if ("pageturn") soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') - if ("fracture") soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') - if ("canopen") soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') - if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') - if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg', - 'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg', - 'sound/effects/thunder/thunder10.ogg') - if ("keyboard") soundin = pick('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') - if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') - if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') - if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') - if ("pickaxe") soundin = pick('sound/weapons/mine/pickaxe1.ogg', 'sound/weapons/mine/pickaxe2.ogg','sound/weapons/mine/pickaxe3.ogg','sound/weapons/mine/pickaxe4.ogg') - if("shatter") - soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') - if("explosion") - soundin = pick( - 'sound/effects/Explosion1.ogg', - 'sound/effects/Explosion2.ogg', - 'sound/effects/Explosion3.ogg', - 'sound/effects/Explosion4.ogg', - 'sound/effects/Explosion5.ogg', - 'sound/effects/Explosion6.ogg') - if("sparks") - soundin = pick( - 'sound/effects/sparks1.ogg', - 'sound/effects/sparks2.ogg', - 'sound/effects/sparks3.ogg', - 'sound/effects/sparks5.ogg', - 'sound/effects/sparks6.ogg', - 'sound/effects/sparks7.ogg') - if("rustle") - soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') - if("punch") - soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') - if("clownstep") - soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') - if("swing_hit") - soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') - if("hiss") - soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') - if("pageturn") - soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') - if("fracture") - soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') - if("canopen") - soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') - if("mechstep") - soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') - if("thunder") - soundin = pick( - 'sound/effects/thunder/thunder1.ogg', - 'sound/effects/thunder/thunder2.ogg', - 'sound/effects/thunder/thunder3.ogg', - 'sound/effects/thunder/thunder4.ogg', - 'sound/effects/thunder/thunder5.ogg', - 'sound/effects/thunder/thunder6.ogg', - 'sound/effects/thunder/thunder7.ogg', - 'sound/effects/thunder/thunder8.ogg', - 'sound/effects/thunder/thunder9.ogg', - 'sound/effects/thunder/thunder10.ogg') - if("keyboard") - soundin = pick( - 'sound/effects/keyboard/keyboard1.ogg', - 'sound/effects/keyboard/keyboard2.ogg', - 'sound/effects/keyboard/keyboard3.ogg', - 'sound/effects/keyboard/keyboard4.ogg') - if("button") - soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') - if("switch") - soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') - if("casing_sound") - soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') - if("ricochet") - soundin = pick( - 'sound/weapons/effects/ric1.ogg', - 'sound/weapons/effects/ric2.ogg', - 'sound/weapons/effects/ric3.ogg', - 'sound/weapons/effects/ric4.ogg', - 'sound/weapons/effects/ric5.ogg') - if("bullet_miss") - soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') - if ("pickaxe") - soundin = pick( - 'sound/weapons/mine/pickaxe1.ogg', - 'sound/weapons/mine/pickaxe2.ogg', - 'sound/weapons/mine/pickaxe3.ogg', - 'sound/weapons/mine/pickaxe4.ogg') - //VORESTATION EDIT - vore sounds for better performance - if ("hunger_sounds") soundin = pick('sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg','sound/vore/growl5.ogg') - - if("classic_digestion_sounds") soundin = pick( - 'sound/vore/digest1.ogg','sound/vore/digest2.ogg','sound/vore/digest3.ogg','sound/vore/digest4.ogg', - 'sound/vore/digest5.ogg','sound/vore/digest6.ogg','sound/vore/digest7.ogg','sound/vore/digest8.ogg', - 'sound/vore/digest9.ogg','sound/vore/digest10.ogg','sound/vore/digest11.ogg','sound/vore/digest12.ogg') - if("classic_death_sounds") soundin = pick( - 'sound/vore/death1.ogg','sound/vore/death2.ogg','sound/vore/death3.ogg','sound/vore/death4.ogg','sound/vore/death5.ogg', - 'sound/vore/death6.ogg','sound/vore/death7.ogg','sound/vore/death8.ogg','sound/vore/death9.ogg','sound/vore/death10.ogg') - if("classic_struggle_sounds") soundin = pick('sound/vore/squish1.ogg','sound/vore/squish2.ogg','sound/vore/squish3.ogg','sound/vore/squish4.ogg') - - if("fancy_prey_struggle") soundin = pick( - 'sound/vore/sunesound/prey/struggle_01.ogg','sound/vore/sunesound/prey/struggle_02.ogg','sound/vore/sunesound/prey/struggle_03.ogg', - 'sound/vore/sunesound/prey/struggle_04.ogg','sound/vore/sunesound/prey/struggle_05.ogg') - if("fancy_digest_pred") soundin = pick( - 'sound/vore/sunesound/pred/digest_01.ogg','sound/vore/sunesound/pred/digest_02.ogg','sound/vore/sunesound/pred/digest_03.ogg', - 'sound/vore/sunesound/pred/digest_04.ogg','sound/vore/sunesound/pred/digest_05.ogg','sound/vore/sunesound/pred/digest_06.ogg', - 'sound/vore/sunesound/pred/digest_07.ogg','sound/vore/sunesound/pred/digest_08.ogg','sound/vore/sunesound/pred/digest_09.ogg', - 'sound/vore/sunesound/pred/digest_10.ogg','sound/vore/sunesound/pred/digest_11.ogg','sound/vore/sunesound/pred/digest_12.ogg', - 'sound/vore/sunesound/pred/digest_13.ogg','sound/vore/sunesound/pred/digest_14.ogg','sound/vore/sunesound/pred/digest_15.ogg', - 'sound/vore/sunesound/pred/digest_16.ogg','sound/vore/sunesound/pred/digest_17.ogg','sound/vore/sunesound/pred/digest_18.ogg') - if("fancy_death_pred") soundin = pick( - 'sound/vore/sunesound/pred/death_01.ogg','sound/vore/sunesound/pred/death_02.ogg','sound/vore/sunesound/pred/death_03.ogg', - 'sound/vore/sunesound/pred/death_04.ogg','sound/vore/sunesound/pred/death_05.ogg','sound/vore/sunesound/pred/death_06.ogg', - 'sound/vore/sunesound/pred/death_07.ogg','sound/vore/sunesound/pred/death_08.ogg','sound/vore/sunesound/pred/death_09.ogg', - 'sound/vore/sunesound/pred/death_10.ogg') - if("fancy_digest_prey") soundin = pick( - 'sound/vore/sunesound/prey/digest_01.ogg','sound/vore/sunesound/prey/digest_02.ogg','sound/vore/sunesound/prey/digest_03.ogg', - 'sound/vore/sunesound/prey/digest_04.ogg','sound/vore/sunesound/prey/digest_05.ogg','sound/vore/sunesound/prey/digest_06.ogg', - 'sound/vore/sunesound/prey/digest_07.ogg','sound/vore/sunesound/prey/digest_08.ogg','sound/vore/sunesound/prey/digest_09.ogg', - 'sound/vore/sunesound/prey/digest_10.ogg','sound/vore/sunesound/prey/digest_11.ogg','sound/vore/sunesound/prey/digest_12.ogg', - 'sound/vore/sunesound/prey/digest_13.ogg','sound/vore/sunesound/prey/digest_14.ogg','sound/vore/sunesound/prey/digest_15.ogg', - 'sound/vore/sunesound/prey/digest_16.ogg','sound/vore/sunesound/prey/digest_17.ogg','sound/vore/sunesound/prey/digest_18.ogg') - if("fancy_death_prey") soundin = pick( - 'sound/vore/sunesound/prey/death_01.ogg','sound/vore/sunesound/prey/death_02.ogg','sound/vore/sunesound/prey/death_03.ogg', - 'sound/vore/sunesound/prey/death_04.ogg','sound/vore/sunesound/prey/death_05.ogg','sound/vore/sunesound/prey/death_06.ogg', - 'sound/vore/sunesound/prey/death_07.ogg','sound/vore/sunesound/prey/death_08.ogg','sound/vore/sunesound/prey/death_09.ogg', - 'sound/vore/sunesound/prey/death_10.ogg') - if ("belches") soundin = pick( - 'sound/vore/belches/belch1.ogg','sound/vore/belches/belch2.ogg','sound/vore/belches/belch3.ogg','sound/vore/belches/belch4.ogg', - 'sound/vore/belches/belch5.ogg','sound/vore/belches/belch6.ogg','sound/vore/belches/belch7.ogg','sound/vore/belches/belch8.ogg', - 'sound/vore/belches/belch9.ogg','sound/vore/belches/belch10.ogg','sound/vore/belches/belch11.ogg','sound/vore/belches/belch12.ogg', - 'sound/vore/belches/belch13.ogg','sound/vore/belches/belch14.ogg','sound/vore/belches/belch15.ogg') - //END VORESTATION EDIT - if ("terminal_type") - soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \ - 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \ - 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg') - if("smcalm") - soundin = pick('sound/machines/sm/accent/normal/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') - if("smdelam") - soundin = pick('sound/machines/sm/accent/delam/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') - if ("generic_drop") - soundin = pick( - 'sound/items/drop/generic1.ogg', - 'sound/items/drop/generic2.ogg' ) - if ("generic_pickup") - soundin = pick( - 'sound/items/pickup/generic1.ogg', - 'sound/items/pickup/generic2.ogg', - 'sound/items/pickup/generic3.ogg') - return soundin - -//Are these even used? //Yes -var/list/keyboard_sound = list ('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') -var/list/bodyfall_sound = list('sound/effects/bodyfall1.ogg','sound/effects/bodyfall2.ogg','sound/effects/bodyfall3.ogg','sound/effects/bodyfall4.ogg') -var/list/teppi_sound = list('sound/voice/teppi/gyooh1.ogg', 'sound/voice/teppi/gyooh2.ogg', 'sound/voice/teppi/gyooh3.ogg', 'sound/voice/teppi/gyooh4.ogg', 'sound/voice/teppi/gyooh5.ogg', 'sound/voice/teppi/gyooh6.ogg', 'sound/voice/teppi/snoot1.ogg', 'sound/voice/teppi/snoot2.ogg') -var/list/talk_sound = list('sound/talksounds/a.ogg','sound/talksounds/b.ogg','sound/talksounds/c.ogg','sound/talksounds/d.ogg','sound/talksounds/e.ogg','sound/talksounds/f.ogg','sound/talksounds/g.ogg','sound/talksounds/h.ogg') -var/list/emote_sound = list('sound/talksounds/me_a.ogg','sound/talksounds/me_b.ogg','sound/talksounds/me_c.ogg','sound/talksounds/me_d.ogg','sound/talksounds/me_e.ogg','sound/talksounds/me_f.ogg') - -//Goon sounds -var/list/goon_speak_one_sound = list('sound/talksounds/goon/speak_1.ogg', 'sound/talksounds/goon/speak_1_ask.ogg', 'sound/talksounds/goon/speak_1_exclaim.ogg') -var/list/goon_speak_two_sound = list('sound/talksounds/goon/speak_2.ogg', 'sound/talksounds/goon/speak_2_ask.ogg', 'sound/talksounds/goon/speak_2_exclaim.ogg') -var/list/goon_speak_three_sound = list('sound/talksounds/goon/speak_3.ogg', 'sound/talksounds/goon/speak_3_ask.ogg', 'sound/talksounds/goon/speak_3_exclaim.ogg') -var/list/goon_speak_four_sound = list('sound/talksounds/goon/speak_4.ogg', 'sound/talksounds/goon/speak_4_ask.ogg', 'sound/talksounds/goon/speak_4_exclaim.ogg') -var/list/goon_speak_blub_sound = list('sound/talksounds/goon/blub.ogg', 'sound/talksounds/goon/blub_ask.ogg', 'sound/talksounds/goon/blub_exclaim.ogg') -var/list/goon_speak_bottalk_sound = list('sound/talksounds/goon/bottalk_1.ogg', 'sound/talksounds/goon/bottalk_2.ogg', 'sound/talksounds/goon/bottalk_3.ogg', 'sound/talksounds/goon/bottalk_4.wav') -var/list/goon_speak_buwoo_sound = list('sound/talksounds/goon/buwoo.ogg', 'sound/talksounds/goon/buwoo_ask.ogg', 'sound/talksounds/goon/buwoo_exclaim.ogg') -var/list/goon_speak_cow_sound = list('sound/talksounds/goon/cow.ogg', 'sound/talksounds/goon/cow_ask.ogg', 'sound/talksounds/goon/cow_exclaim.ogg') -var/list/goon_speak_lizard_sound = list('sound/talksounds/goon/lizard.ogg', 'sound/talksounds/goon/lizard_ask.ogg', 'sound/talksounds/goon/lizard_exclaim.ogg') -var/list/goon_speak_pug_sound = list('sound/talksounds/goon/pug.ogg', 'sound/talksounds/goon/pug_ask.ogg', 'sound/talksounds/goon/pug_exclaim.ogg') -var/list/goon_speak_pugg_sound = list('sound/talksounds/goon/pugg.ogg', 'sound/talksounds/goon/pugg_ask.ogg', 'sound/talksounds/goon/pugg_exclaim.ogg') -var/list/goon_speak_roach_sound = list('sound/talksounds/goon/roach.ogg', 'sound/talksounds/goon/roach_ask.ogg', 'sound/talksounds/goon/roach_exclaim.ogg') -var/list/goon_speak_skelly_sound = list('sound/talksounds/goon/skelly.ogg', 'sound/talksounds/goon/skelly_ask.ogg', 'sound/talksounds/goon/skelly_exclaim.ogg') +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, preference = null, volume_channel = null) + if(Master.current_runlevel < RUNLEVEL_LOBBY) + return + + var/turf/turf_source = get_turf(source) + if(!turf_source) + return + var/area/area_source = turf_source.loc + + //allocate a channel if necessary now so its the same for everyone + channel = channel || SSsounds.random_available_channel() + + // Looping through the player list has the added bonus of working for mobs inside containers + var/sound/S = sound(get_sfx(soundin)) + var/maxdistance = (world.view + extrarange) * 2 //VOREStation Edit - 3 to 2 + var/list/listeners = player_list.Copy() + if(!ignore_walls) //these sounds don't carry through walls + for(var/mob/listen in listeners) + if(!(get_turf(listen) in hear(maxdistance,source))) + listeners -= listen + for(var/mob/M as anything in listeners) + if(!M || !M.client) + continue + var/turf/T = get_turf(M) + if(!T) + continue + var/area/A = T.loc + if((A.soundproofed || area_source.soundproofed) && (A != area_source)) + continue + var/distance = get_dist(T, turf_source) + + if(distance <= maxdistance) + if(T && T.z == turf_source.z) + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel) + +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, channel = 0, pressure_affected = TRUE, sound/S, preference, volume_channel = null) + if(!client || ear_deaf > 0) + return + if(preference && !client.is_preference_enabled(preference)) + return + + if(!S) + S = sound(get_sfx(soundin)) + + S.wait = 0 //No queue + S.channel = channel || SSsounds.random_available_channel() + + // I'm not sure if you can modify S.volume, but I'd rather not try to find out what + // horrible things lurk in BYOND's internals, so we're just gonna do vol *= + vol *= client.get_preference_volume_channel(volume_channel) + vol *= client.get_preference_volume_channel(VOLUME_CHANNEL_MASTER) + S.volume = vol + + if(vary) + if(frequency) + S.frequency = frequency + else + S.frequency = get_rand_frequency() + + if(isturf(turf_source)) + var/turf/T = get_turf(src) + + //sound volume falloff with distance + var/distance = get_dist(T, turf_source) + + S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. + + //Atmosphere affects sound + var/pressure_factor = 1 + if(pressure_affected) + var/datum/gas_mixture/hearer_env = T.return_air() + var/datum/gas_mixture/source_env = turf_source.return_air() + + if(hearer_env && source_env) + var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure()) + if(pressure < ONE_ATMOSPHERE) + pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0) + else //space + pressure_factor = 0 + + if(distance <= 1) + pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound + + S.volume *= pressure_factor + //End Atmosphere affecting sound + + //Don't bother with doing anything below. + if(S.volume <= 0) + return //No sound + + //Apply a sound environment. + if(!is_global) + S.environment = get_sound_env(pressure_factor) + + var/dx = turf_source.x - T.x // Hearing from the right/left + S.x = dx + var/dz = turf_source.y - T.y // Hearing from infront/behind + S.z = dz + // The y value is for above your head, but there is no ceiling in 2d spessmens. + S.y = 1 + S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) + + src << S + +/proc/sound_to_playing_players(sound, volume = 100, vary) + sound = get_sfx(sound) + for(var/M in player_list) + if(ismob(M) && !isnewplayer(M)) + var/mob/MO = M + MO.playsound_local(get_turf(MO), sound, volume, vary, pressure_affected = FALSE) + +/mob/proc/stop_sound_channel(chan) + src << sound(null, repeat = 0, wait = 0, channel = chan) + +/mob/proc/set_sound_channel_volume(channel, volume) + var/sound/S = sound(null, FALSE, FALSE, channel, volume) + S.status = SOUND_UPDATE + src << S + +/proc/get_rand_frequency() + return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. + +/client/proc/playtitlemusic() + if(!ticker || !SSmedia_tracks.lobby_tracks.len || !media) return + if(is_preference_enabled(/datum/client_preference/play_lobby_music)) + var/datum/track/T = pick(SSmedia_tracks.lobby_tracks) + media.push_music(T.url, world.time, 0.85) + to_chat(src,"Lobby music: [T.title] by [T.artist].") + +/proc/get_sfx(soundin) + if(istext(soundin)) + switch(soundin) + if ("shatter") soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') + if ("explosion") soundin = pick('sound/effects/Explosion1.ogg','sound/effects/Explosion2.ogg','sound/effects/Explosion3.ogg','sound/effects/Explosion4.ogg','sound/effects/Explosion5.ogg','sound/effects/Explosion6.ogg') + if ("sparks") soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks5.ogg','sound/effects/sparks6.ogg','sound/effects/sparks7.ogg') + if ("rustle") soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if ("punch") soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if ("clownstep") soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') + if ("swing_hit") soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if ("hiss") soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if ("pageturn") soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if ("fracture") soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') + if ("canopen") soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') + if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') + if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg', + 'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg', + 'sound/effects/thunder/thunder10.ogg') + if ("keyboard") soundin = pick('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') + if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') + if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') + if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if ("pickaxe") soundin = pick('sound/weapons/mine/pickaxe1.ogg', 'sound/weapons/mine/pickaxe2.ogg','sound/weapons/mine/pickaxe3.ogg','sound/weapons/mine/pickaxe4.ogg') + if("shatter") + soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') + if("explosion") + soundin = pick( + 'sound/effects/Explosion1.ogg', + 'sound/effects/Explosion2.ogg', + 'sound/effects/Explosion3.ogg', + 'sound/effects/Explosion4.ogg', + 'sound/effects/Explosion5.ogg', + 'sound/effects/Explosion6.ogg') + if("sparks") + soundin = pick( + 'sound/effects/sparks1.ogg', + 'sound/effects/sparks2.ogg', + 'sound/effects/sparks3.ogg', + 'sound/effects/sparks5.ogg', + 'sound/effects/sparks6.ogg', + 'sound/effects/sparks7.ogg') + if("rustle") + soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if("punch") + soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if("clownstep") + soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') + if("swing_hit") + soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if("hiss") + soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if("pageturn") + soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if("fracture") + soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') + if("canopen") + soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') + if("mechstep") + soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') + if("thunder") + soundin = pick( + 'sound/effects/thunder/thunder1.ogg', + 'sound/effects/thunder/thunder2.ogg', + 'sound/effects/thunder/thunder3.ogg', + 'sound/effects/thunder/thunder4.ogg', + 'sound/effects/thunder/thunder5.ogg', + 'sound/effects/thunder/thunder6.ogg', + 'sound/effects/thunder/thunder7.ogg', + 'sound/effects/thunder/thunder8.ogg', + 'sound/effects/thunder/thunder9.ogg', + 'sound/effects/thunder/thunder10.ogg') + if("keyboard") + soundin = pick( + 'sound/effects/keyboard/keyboard1.ogg', + 'sound/effects/keyboard/keyboard2.ogg', + 'sound/effects/keyboard/keyboard3.ogg', + 'sound/effects/keyboard/keyboard4.ogg') + if("button") + soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') + if("switch") + soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') + if("casing_sound") + soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if("ricochet") + soundin = pick( + 'sound/weapons/effects/ric1.ogg', + 'sound/weapons/effects/ric2.ogg', + 'sound/weapons/effects/ric3.ogg', + 'sound/weapons/effects/ric4.ogg', + 'sound/weapons/effects/ric5.ogg') + if("bullet_miss") + soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') + if ("pickaxe") + soundin = pick( + 'sound/weapons/mine/pickaxe1.ogg', + 'sound/weapons/mine/pickaxe2.ogg', + 'sound/weapons/mine/pickaxe3.ogg', + 'sound/weapons/mine/pickaxe4.ogg') + //VORESTATION EDIT - vore sounds for better performance + if ("hunger_sounds") soundin = pick('sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg','sound/vore/growl5.ogg') + + if("classic_digestion_sounds") soundin = pick( + 'sound/vore/digest1.ogg','sound/vore/digest2.ogg','sound/vore/digest3.ogg','sound/vore/digest4.ogg', + 'sound/vore/digest5.ogg','sound/vore/digest6.ogg','sound/vore/digest7.ogg','sound/vore/digest8.ogg', + 'sound/vore/digest9.ogg','sound/vore/digest10.ogg','sound/vore/digest11.ogg','sound/vore/digest12.ogg') + if("classic_death_sounds") soundin = pick( + 'sound/vore/death1.ogg','sound/vore/death2.ogg','sound/vore/death3.ogg','sound/vore/death4.ogg','sound/vore/death5.ogg', + 'sound/vore/death6.ogg','sound/vore/death7.ogg','sound/vore/death8.ogg','sound/vore/death9.ogg','sound/vore/death10.ogg') + if("classic_struggle_sounds") soundin = pick('sound/vore/squish1.ogg','sound/vore/squish2.ogg','sound/vore/squish3.ogg','sound/vore/squish4.ogg') + + if("fancy_prey_struggle") soundin = pick( + 'sound/vore/sunesound/prey/struggle_01.ogg','sound/vore/sunesound/prey/struggle_02.ogg','sound/vore/sunesound/prey/struggle_03.ogg', + 'sound/vore/sunesound/prey/struggle_04.ogg','sound/vore/sunesound/prey/struggle_05.ogg') + if("fancy_digest_pred") soundin = pick( + 'sound/vore/sunesound/pred/digest_01.ogg','sound/vore/sunesound/pred/digest_02.ogg','sound/vore/sunesound/pred/digest_03.ogg', + 'sound/vore/sunesound/pred/digest_04.ogg','sound/vore/sunesound/pred/digest_05.ogg','sound/vore/sunesound/pred/digest_06.ogg', + 'sound/vore/sunesound/pred/digest_07.ogg','sound/vore/sunesound/pred/digest_08.ogg','sound/vore/sunesound/pred/digest_09.ogg', + 'sound/vore/sunesound/pred/digest_10.ogg','sound/vore/sunesound/pred/digest_11.ogg','sound/vore/sunesound/pred/digest_12.ogg', + 'sound/vore/sunesound/pred/digest_13.ogg','sound/vore/sunesound/pred/digest_14.ogg','sound/vore/sunesound/pred/digest_15.ogg', + 'sound/vore/sunesound/pred/digest_16.ogg','sound/vore/sunesound/pred/digest_17.ogg','sound/vore/sunesound/pred/digest_18.ogg') + if("fancy_death_pred") soundin = pick( + 'sound/vore/sunesound/pred/death_01.ogg','sound/vore/sunesound/pred/death_02.ogg','sound/vore/sunesound/pred/death_03.ogg', + 'sound/vore/sunesound/pred/death_04.ogg','sound/vore/sunesound/pred/death_05.ogg','sound/vore/sunesound/pred/death_06.ogg', + 'sound/vore/sunesound/pred/death_07.ogg','sound/vore/sunesound/pred/death_08.ogg','sound/vore/sunesound/pred/death_09.ogg', + 'sound/vore/sunesound/pred/death_10.ogg') + if("fancy_digest_prey") soundin = pick( + 'sound/vore/sunesound/prey/digest_01.ogg','sound/vore/sunesound/prey/digest_02.ogg','sound/vore/sunesound/prey/digest_03.ogg', + 'sound/vore/sunesound/prey/digest_04.ogg','sound/vore/sunesound/prey/digest_05.ogg','sound/vore/sunesound/prey/digest_06.ogg', + 'sound/vore/sunesound/prey/digest_07.ogg','sound/vore/sunesound/prey/digest_08.ogg','sound/vore/sunesound/prey/digest_09.ogg', + 'sound/vore/sunesound/prey/digest_10.ogg','sound/vore/sunesound/prey/digest_11.ogg','sound/vore/sunesound/prey/digest_12.ogg', + 'sound/vore/sunesound/prey/digest_13.ogg','sound/vore/sunesound/prey/digest_14.ogg','sound/vore/sunesound/prey/digest_15.ogg', + 'sound/vore/sunesound/prey/digest_16.ogg','sound/vore/sunesound/prey/digest_17.ogg','sound/vore/sunesound/prey/digest_18.ogg') + if("fancy_death_prey") soundin = pick( + 'sound/vore/sunesound/prey/death_01.ogg','sound/vore/sunesound/prey/death_02.ogg','sound/vore/sunesound/prey/death_03.ogg', + 'sound/vore/sunesound/prey/death_04.ogg','sound/vore/sunesound/prey/death_05.ogg','sound/vore/sunesound/prey/death_06.ogg', + 'sound/vore/sunesound/prey/death_07.ogg','sound/vore/sunesound/prey/death_08.ogg','sound/vore/sunesound/prey/death_09.ogg', + 'sound/vore/sunesound/prey/death_10.ogg') + if ("belches") soundin = pick( + 'sound/vore/belches/belch1.ogg','sound/vore/belches/belch2.ogg','sound/vore/belches/belch3.ogg','sound/vore/belches/belch4.ogg', + 'sound/vore/belches/belch5.ogg','sound/vore/belches/belch6.ogg','sound/vore/belches/belch7.ogg','sound/vore/belches/belch8.ogg', + 'sound/vore/belches/belch9.ogg','sound/vore/belches/belch10.ogg','sound/vore/belches/belch11.ogg','sound/vore/belches/belch12.ogg', + 'sound/vore/belches/belch13.ogg','sound/vore/belches/belch14.ogg','sound/vore/belches/belch15.ogg') + //END VORESTATION EDIT + if ("terminal_type") + soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \ + 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \ + 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg') + if("smcalm") + soundin = pick('sound/machines/sm/accent/normal/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') + if("smdelam") + soundin = pick('sound/machines/sm/accent/delam/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') + if ("generic_drop") + soundin = pick( + 'sound/items/drop/generic1.ogg', + 'sound/items/drop/generic2.ogg' ) + if ("generic_pickup") + soundin = pick( + 'sound/items/pickup/generic1.ogg', + 'sound/items/pickup/generic2.ogg', + 'sound/items/pickup/generic3.ogg') + return soundin + +//Are these even used? //Yes +var/list/keyboard_sound = list ('sound/effects/keyboard/keyboard1.ogg','sound/effects/keyboard/keyboard2.ogg','sound/effects/keyboard/keyboard3.ogg', 'sound/effects/keyboard/keyboard4.ogg') +var/list/bodyfall_sound = list('sound/effects/bodyfall1.ogg','sound/effects/bodyfall2.ogg','sound/effects/bodyfall3.ogg','sound/effects/bodyfall4.ogg') +var/list/teppi_sound = list('sound/voice/teppi/gyooh1.ogg', 'sound/voice/teppi/gyooh2.ogg', 'sound/voice/teppi/gyooh3.ogg', 'sound/voice/teppi/gyooh4.ogg', 'sound/voice/teppi/gyooh5.ogg', 'sound/voice/teppi/gyooh6.ogg', 'sound/voice/teppi/snoot1.ogg', 'sound/voice/teppi/snoot2.ogg') +var/list/talk_sound = list('sound/talksounds/a.ogg','sound/talksounds/b.ogg','sound/talksounds/c.ogg','sound/talksounds/d.ogg','sound/talksounds/e.ogg','sound/talksounds/f.ogg','sound/talksounds/g.ogg','sound/talksounds/h.ogg') +var/list/emote_sound = list('sound/talksounds/me_a.ogg','sound/talksounds/me_b.ogg','sound/talksounds/me_c.ogg','sound/talksounds/me_d.ogg','sound/talksounds/me_e.ogg','sound/talksounds/me_f.ogg') + +//Goon sounds +var/list/goon_speak_one_sound = list('sound/talksounds/goon/speak_1.ogg', 'sound/talksounds/goon/speak_1_ask.ogg', 'sound/talksounds/goon/speak_1_exclaim.ogg') +var/list/goon_speak_two_sound = list('sound/talksounds/goon/speak_2.ogg', 'sound/talksounds/goon/speak_2_ask.ogg', 'sound/talksounds/goon/speak_2_exclaim.ogg') +var/list/goon_speak_three_sound = list('sound/talksounds/goon/speak_3.ogg', 'sound/talksounds/goon/speak_3_ask.ogg', 'sound/talksounds/goon/speak_3_exclaim.ogg') +var/list/goon_speak_four_sound = list('sound/talksounds/goon/speak_4.ogg', 'sound/talksounds/goon/speak_4_ask.ogg', 'sound/talksounds/goon/speak_4_exclaim.ogg') +var/list/goon_speak_blub_sound = list('sound/talksounds/goon/blub.ogg', 'sound/talksounds/goon/blub_ask.ogg', 'sound/talksounds/goon/blub_exclaim.ogg') +var/list/goon_speak_bottalk_sound = list('sound/talksounds/goon/bottalk_1.ogg', 'sound/talksounds/goon/bottalk_2.ogg', 'sound/talksounds/goon/bottalk_3.ogg', 'sound/talksounds/goon/bottalk_4.wav') +var/list/goon_speak_buwoo_sound = list('sound/talksounds/goon/buwoo.ogg', 'sound/talksounds/goon/buwoo_ask.ogg', 'sound/talksounds/goon/buwoo_exclaim.ogg') +var/list/goon_speak_cow_sound = list('sound/talksounds/goon/cow.ogg', 'sound/talksounds/goon/cow_ask.ogg', 'sound/talksounds/goon/cow_exclaim.ogg') +var/list/goon_speak_lizard_sound = list('sound/talksounds/goon/lizard.ogg', 'sound/talksounds/goon/lizard_ask.ogg', 'sound/talksounds/goon/lizard_exclaim.ogg') +var/list/goon_speak_pug_sound = list('sound/talksounds/goon/pug.ogg', 'sound/talksounds/goon/pug_ask.ogg', 'sound/talksounds/goon/pug_exclaim.ogg') +var/list/goon_speak_pugg_sound = list('sound/talksounds/goon/pugg.ogg', 'sound/talksounds/goon/pugg_ask.ogg', 'sound/talksounds/goon/pugg_exclaim.ogg') +var/list/goon_speak_roach_sound = list('sound/talksounds/goon/roach.ogg', 'sound/talksounds/goon/roach_ask.ogg', 'sound/talksounds/goon/roach_exclaim.ogg') +var/list/goon_speak_skelly_sound = list('sound/talksounds/goon/skelly.ogg', 'sound/talksounds/goon/skelly_ask.ogg', 'sound/talksounds/goon/skelly_exclaim.ogg') diff --git a/code/game/turfs/flooring/flooring.dm b/code/game/turfs/flooring/flooring.dm index 1b58490012c..03b3b6f14cf 100644 --- a/code/game/turfs/flooring/flooring.dm +++ b/code/game/turfs/flooring/flooring.dm @@ -198,6 +198,17 @@ var/list/flooring_types 'sound/effects/footstep/mud3.ogg', 'sound/effects/footstep/mud4.ogg')) +/decl/flooring/rock + name = "rocks" + desc = "Hard as a rock." + icon = 'icons/turf/outdoors.dmi' + icon_base = "rock" + footstep_sounds = list("human" = list( + 'sound/effects/footstep/LightStone1.ogg', + 'sound/effects/footstep/LightStone2.ogg', + 'sound/effects/footstep/LightStone3.ogg', + 'sound/effects/footstep/LightStone4.ogg')) + /decl/flooring/asteroid name = "coarse sand" desc = "You got a pebble in your shoe just looking at it." diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 57c1d22832d..d60d807950c 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -1,189 +1,191 @@ -/turf/simulated - name = "station" - var/wet = 0 - var/image/wet_overlay = null - - //Mining resources (for the large drills). - var/has_resources - var/list/resources - - var/thermite = 0 - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed - var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to - var/can_dirty = TRUE // If false, tile never gets dirty - var/can_start_dirty = TRUE // If false, cannot start dirty roundstart - var/dirty_prob = 2 // Chance of being dirty roundstart - var/dirt = 0 - var/special_temperature //Used for turf HE-Pipe interaction - var/climbable = FALSE //Adds proc to wall if set to TRUE on its initialization, defined here since not all walls are subtypes of wall - - var/icon_edge = 'icons/turf/outdoors_edge.dmi' //VOREStation Addition - Allows for alternative edge icon files - -// This is not great. -/turf/simulated/proc/wet_floor(var/wet_val = 1) - if(wet > 2) //Can't mop up ice - return - spawn(0) - wet = wet_val - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = image('icons/effects/water.dmi', icon_state = "wet_floor") - add_overlay(wet_overlay) - sleep(800) - if(wet == 2) - sleep(3200) - wet = 0 - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = null - -/turf/simulated/proc/freeze_floor() - if(!wet) // Water is required for it to freeze. - return - wet = 3 // icy - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = null - wet_overlay = image('icons/turf/overlays.dmi',src,"snowfloor") - add_overlay(wet_overlay) - spawn(5 MINUTES) - wet = 0 - if(wet_overlay) - cut_overlay(wet_overlay) - wet_overlay = null - -/turf/simulated/clean_blood() - for(var/obj/effect/decal/cleanable/blood/B in contents) - B.clean_blood() - ..() - -/turf/simulated/Initialize(mapload) - . = ..() - if(istype(loc, /area/chapel)) - holy = 1 - levelupdate() - if(climbable) - verbs += /turf/simulated/proc/climb_wall - -/turf/simulated/examine(mob/user) - . = ..() - if(climbable) - . += "This [src] looks climbable." - - -/turf/simulated/proc/AddTracks(var/typepath,var/bloodDNA,var/comingdir,var/goingdir,var/bloodcolor="#A10808") - var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src - if(!tracks) - tracks = new typepath(src) - tracks.AddTracks(bloodDNA,comingdir,goingdir,bloodcolor) - -/turf/simulated/proc/update_dirt() - if(can_dirty) - dirt = min(dirt+1, 101) - var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, src) - if (dirt > 50) - if (!dirtoverlay) - dirtoverlay = new/obj/effect/decal/cleanable/dirt(src) - dirtoverlay.alpha = min((dirt - 50) * 5, 255) - -/turf/simulated/Entered(atom/A, atom/OL) - if(movement_disabled && usr.ckey != movement_disabled_exception) - to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems - return - - if (istype(A,/mob/living)) - var/mob/living/M = A - if(M.lying || M.flying) //VOREStation Edit - return ..() - - if(M.dirties_floor()) - // Dirt overlays. - update_dirt() - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - // Tracking blood - var/list/bloodDNA = null - var/bloodcolor="" - if(H.shoes) - var/obj/item/clothing/shoes/S = H.shoes - if(istype(S)) - S.handle_movement(src,(H.m_intent == "run" ? 1 : 0)) - if(S.track_blood && S.blood_DNA) - bloodDNA = S.blood_DNA - bloodcolor=S.blood_color - S.track_blood-- - else - if(H.track_blood && H.feet_blood_DNA) - bloodDNA = H.feet_blood_DNA - bloodcolor = H.feet_blood_color - H.track_blood-- - - if (bloodDNA) - src.AddTracks(H.species.get_move_trail(H),bloodDNA,H.dir,0,bloodcolor) // Coming - var/turf/simulated/from = get_step(H,reverse_direction(H.dir)) - if(istype(from) && from) - from.AddTracks(H.species.get_move_trail(H),bloodDNA,0,H.dir,bloodcolor) // Going - - bloodDNA = null - - if(src.wet) - - if(M.buckled || (src.wet == 1 && M.m_intent == "walk")) - return - - var/slip_dist = 1 - var/slip_stun = 6 - var/floor_type = "wet" - - switch(src.wet) - if(2) // Lube - floor_type = "slippery" - slip_dist = 4 - slip_stun = 10 - if(3) // Ice - floor_type = "icy" - slip_stun = 4 - slip_dist = 2 - - if(M.slip("the [floor_type] floor", slip_stun)) - for(var/i = 1 to slip_dist) - if(isbelly(M.loc)) //VOREEdit, Stop the slip if we're in a belly. Inspired by a chompedit, cleaned it up with isbelly instead of a variable since the var was resetting too fast. - return - step(M, M.dir) - sleep(1) - else - M.inertia_dir = 0 - else - M.inertia_dir = 0 - - ..() - -//returns 1 if made bloody, returns 0 otherwise -/turf/simulated/add_blood(mob/living/carbon/human/M as mob) - if (!..()) - return 0 - - if(istype(M)) - for(var/obj/effect/decal/cleanable/blood/B in contents) - if(!B.blood_DNA) - B.blood_DNA = list() - if(!B.blood_DNA[M.dna.unique_enzymes]) - B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type - B.virus2 = virus_copylist(M.virus2) - return 1 //we bloodied the floor - blood_splatter(src,M.get_blood(M.vessel),1) - return 1 //we bloodied the floor - return 0 - -// Only adds blood on the floor -- Skie -/turf/simulated/proc/add_blood_floor(mob/living/carbon/M as mob) - if( istype(M, /mob/living/carbon/alien )) - var/obj/effect/decal/cleanable/blood/xeno/this = new /obj/effect/decal/cleanable/blood/xeno(src) - this.blood_DNA["UNKNOWN BLOOD"] = "X*" - else if( istype(M, /mob/living/silicon/robot )) - new /obj/effect/decal/cleanable/blood/oil(src) - else if(ishuman(M)) - add_blood(M) +/turf/simulated + name = "station" + var/wet = 0 + var/image/wet_overlay = null + + //Mining resources (for the large drills). + var/has_resources + var/list/resources + + var/thermite = 0 + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed + var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to + var/can_dirty = TRUE // If false, tile never gets dirty + var/can_start_dirty = TRUE // If false, cannot start dirty roundstart + var/dirty_prob = 2 // Chance of being dirty roundstart + var/dirt = 0 + var/special_temperature //Used for turf HE-Pipe interaction + var/climbable = FALSE //Adds proc to wall if set to TRUE on its initialization, defined here since not all walls are subtypes of wall + + var/icon_edge = 'icons/turf/outdoors_edge.dmi' //VOREStation Addition - Allows for alternative edge icon files + +// This is not great. +/turf/simulated/proc/wet_floor(var/wet_val = 1) + if(wet > 2) //Can't mop up ice + return + spawn(0) + wet = wet_val + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = image('icons/effects/water.dmi', icon_state = "wet_floor") + add_overlay(wet_overlay) + sleep(800) + if(wet == 2) + sleep(3200) + wet = 0 + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = null + +/turf/simulated/proc/freeze_floor() + if(!wet) // Water is required for it to freeze. + return + wet = 3 // icy + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = null + wet_overlay = image('icons/turf/overlays.dmi',src,"snowfloor") + add_overlay(wet_overlay) + spawn(5 MINUTES) + wet = 0 + if(wet_overlay) + cut_overlay(wet_overlay) + wet_overlay = null + +/turf/simulated/clean_blood() + for(var/obj/effect/decal/cleanable/blood/B in contents) + B.clean_blood() + ..() + +/turf/simulated/Initialize(mapload) + . = ..() + if(istype(loc, /area/chapel)) + holy = 1 + levelupdate() + if(climbable) + verbs += /turf/simulated/proc/climb_wall + if(is_outdoors()) //VOREStation edit - quick fix for a planetary lighting issue + SSplanets.addTurf(src) + +/turf/simulated/examine(mob/user) + . = ..() + if(climbable) + . += "This [src] looks climbable." + + +/turf/simulated/proc/AddTracks(var/typepath,var/bloodDNA,var/comingdir,var/goingdir,var/bloodcolor="#A10808") + var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src + if(!tracks) + tracks = new typepath(src) + tracks.AddTracks(bloodDNA,comingdir,goingdir,bloodcolor) + +/turf/simulated/proc/update_dirt() + if(can_dirty) + dirt = min(dirt+1, 101) + var/obj/effect/decal/cleanable/dirt/dirtoverlay = locate(/obj/effect/decal/cleanable/dirt, src) + if (dirt > 50) + if (!dirtoverlay) + dirtoverlay = new/obj/effect/decal/cleanable/dirt(src) + dirtoverlay.alpha = min((dirt - 50) * 5, 255) + +/turf/simulated/Entered(atom/A, atom/OL) + if(movement_disabled && usr.ckey != movement_disabled_exception) + to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems + return + + if (istype(A,/mob/living)) + var/mob/living/M = A + if(M.lying || M.flying) //VOREStation Edit + return ..() + + if(M.dirties_floor()) + // Dirt overlays. + update_dirt() + + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + // Tracking blood + var/list/bloodDNA = null + var/bloodcolor="" + if(H.shoes) + var/obj/item/clothing/shoes/S = H.shoes + if(istype(S)) + S.handle_movement(src,(H.m_intent == "run" ? 1 : 0)) + if(S.track_blood && S.blood_DNA) + bloodDNA = S.blood_DNA + bloodcolor=S.blood_color + S.track_blood-- + else + if(H.track_blood && H.feet_blood_DNA) + bloodDNA = H.feet_blood_DNA + bloodcolor = H.feet_blood_color + H.track_blood-- + + if (bloodDNA) + src.AddTracks(H.species.get_move_trail(H),bloodDNA,H.dir,0,bloodcolor) // Coming + var/turf/simulated/from = get_step(H,reverse_direction(H.dir)) + if(istype(from) && from) + from.AddTracks(H.species.get_move_trail(H),bloodDNA,0,H.dir,bloodcolor) // Going + + bloodDNA = null + + if(src.wet) + + if(M.buckled || (src.wet == 1 && M.m_intent == "walk")) + return + + var/slip_dist = 1 + var/slip_stun = 6 + var/floor_type = "wet" + + switch(src.wet) + if(2) // Lube + floor_type = "slippery" + slip_dist = 4 + slip_stun = 10 + if(3) // Ice + floor_type = "icy" + slip_stun = 4 + slip_dist = 2 + + if(M.slip("the [floor_type] floor", slip_stun)) + for(var/i = 1 to slip_dist) + if(isbelly(M.loc)) //VOREEdit, Stop the slip if we're in a belly. Inspired by a chompedit, cleaned it up with isbelly instead of a variable since the var was resetting too fast. + return + step(M, M.dir) + sleep(1) + else + M.inertia_dir = 0 + else + M.inertia_dir = 0 + + ..() + +//returns 1 if made bloody, returns 0 otherwise +/turf/simulated/add_blood(mob/living/carbon/human/M as mob) + if (!..()) + return 0 + + if(istype(M)) + for(var/obj/effect/decal/cleanable/blood/B in contents) + if(!B.blood_DNA) + B.blood_DNA = list() + if(!B.blood_DNA[M.dna.unique_enzymes]) + B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type + B.virus2 = virus_copylist(M.virus2) + return 1 //we bloodied the floor + blood_splatter(src,M.get_blood(M.vessel),1) + return 1 //we bloodied the floor + return 0 + +// Only adds blood on the floor -- Skie +/turf/simulated/proc/add_blood_floor(mob/living/carbon/M as mob) + if( istype(M, /mob/living/carbon/alien )) + var/obj/effect/decal/cleanable/blood/xeno/this = new /obj/effect/decal/cleanable/blood/xeno(src) + this.blood_DNA["UNKNOWN BLOOD"] = "X*" + else if( istype(M, /mob/living/silicon/robot )) + new /obj/effect/decal/cleanable/blood/oil(src) + else if(ishuman(M)) + add_blood(M) diff --git a/code/game/turfs/simulated/dungeon/wall.dm b/code/game/turfs/simulated/dungeon/wall.dm index 2bfc23145ea..227ca419799 100644 --- a/code/game/turfs/simulated/dungeon/wall.dm +++ b/code/game/turfs/simulated/dungeon/wall.dm @@ -15,6 +15,23 @@ /turf/simulated/wall/dungeon/take_damage() //These things are suppose to be unbreakable return +/turf/simulated/wall/update_icon() + if(!material) + return + + if(!damage_overlays[1]) //list hasn't been populated + generate_overlays() + + cut_overlays() + var/image/I + + if(!density) + I = image(wall_masks, "rockvault") + I.color = material.icon_colour + add_overlay(I) + return + ..() + /turf/simulated/wall/solidrock //for more stylish anti-cheese. description_info = "Probably not going to be able to drill or bomb your way through this, best to try and find a way around." var/rock_side = "rock_side" diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index d45c33f9180..faa2351b6fb 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -1,185 +1,185 @@ -/turf/simulated/floor - name = "plating" - desc = "Unfinished flooring." - icon = 'icons/turf/flooring/plating_vr.dmi' - icon_state = "plating" - - // Damage to flooring. - var/broken - var/burnt - - // Plating data. - var/base_name = "plating" - var/base_desc = "The naked hull." - var/base_icon = 'icons/turf/flooring/plating_vr.dmi' - var/base_icon_state = "plating" - var/static/list/base_footstep_sounds = list("human" = list( - 'sound/effects/footstep/plating1.ogg', - 'sound/effects/footstep/plating2.ogg', - 'sound/effects/footstep/plating3.ogg', - 'sound/effects/footstep/plating4.ogg', - 'sound/effects/footstep/plating5.ogg')) - - var/list/old_decals = null - - // Flooring data. - var/flooring_override - var/initial_flooring - var/decl/flooring/flooring - var/mineral = DEFAULT_WALL_MATERIAL - var/can_be_plated = TRUE // This is here for inheritance's sake. Override to FALSE for turfs you don't want someone to simply slap a plating over such as hazards. - - thermal_conductivity = 0.040 - heat_capacity = 10000 - -/turf/simulated/floor/is_plating() - return (!flooring || flooring.is_plating) - -/turf/simulated/floor/Initialize(mapload, floortype) - . = ..() - if(!floortype && initial_flooring) - floortype = initial_flooring - if(floortype) - set_flooring(get_flooring_data(floortype), TRUE) - . = INITIALIZE_HINT_LATELOAD // We'll update our icons after everyone is ready - else - footstep_sounds = base_footstep_sounds - if(can_dirty && can_start_dirty) - if(prob(dirty_prob)) - dirt += rand(50,100) - update_dirt() //5% chance to start with dirt on a floor tile- give the janitor something to do - -/turf/simulated/floor/LateInitialize() - . = ..() - update_icon(1) - -/turf/simulated/floor/proc/swap_decals() - var/current_decals = decals - decals = old_decals - old_decals = current_decals - -/turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring, var/initializing) - //make_plating(defer_icon_update = 1) - if(is_plating() && !initializing) // Plating -> Flooring - swap_decals() - flooring = newflooring - footstep_sounds = newflooring.footstep_sounds - if(!initializing) - update_icon(1) - levelupdate() - -//This proc will set floor_type to null and the update_icon() proc will then change the icon_state of the turf -//This proc auto corrects the grass tiles' siding. -/turf/simulated/floor/proc/make_plating(var/place_product, var/defer_icon_update) - cut_overlays() - - for(var/obj/effect/decal/writing/W in src) - qdel(W) - - name = base_name - desc = base_desc - icon = base_icon - icon_state = base_icon_state - footstep_sounds = base_footstep_sounds - - if(!is_plating()) // Flooring -> Plating - swap_decals() - if(flooring.build_type && place_product) - new flooring.build_type(src) - var/newtype = flooring.get_plating_type() - if(newtype) // Has a custom plating type to become - set_flooring(get_flooring_data(newtype)) - else - flooring = null - - set_light(0) - broken = null - burnt = null - flooring_override = null - levelupdate() - - if(!defer_icon_update) - update_icon(1) - -/turf/simulated/floor/levelupdate() - var/floored_over = !is_plating() - for(var/obj/O in src) - O.hide(O.hides_under_flooring() && floored_over) - -/turf/simulated/floor/can_engrave() - return (!flooring || flooring.can_engrave) - -/turf/simulated/floor/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_FLOORWALL) - // A wall costs four sheets to build (two for the grider and two for finishing it). - var/cost = RCD_SHEETS_PER_MATTER_UNIT * 4 - // R-walls cost five sheets, however. - if(the_rcd.make_rwalls) - cost += RCD_SHEETS_PER_MATTER_UNIT * 1 - return list( - RCD_VALUE_MODE = RCD_FLOORWALL, - RCD_VALUE_DELAY = 2 SECONDS, - RCD_VALUE_COST = cost - ) - if(RCD_AIRLOCK) - // Airlock assemblies cost four sheets. Let's just add another for the electronics/wires/etc. - return list( - RCD_VALUE_MODE = RCD_AIRLOCK, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - if(RCD_WINDOWGRILLE) - // One steel sheet for the girder (two rods, which is one sheet). - return list( - RCD_VALUE_MODE = RCD_WINDOWGRILLE, - RCD_VALUE_DELAY = 1 SECOND, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 1 - ) - if(RCD_DECONSTRUCT) - // Old RCDs made deconning the floor cost 10 units (IE, three times on full RCD). - // Now it's ten sheets worth of units (which is the same capacity-wise, three times on full RCD). - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = 5 SECONDS, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 - ) - return FALSE - - -/turf/simulated/floor/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - switch(passed_mode) - if(RCD_FLOORWALL) - to_chat(user, span("notice", "You build a wall.")) - ChangeTurf(/turf/simulated/wall) - var/turf/simulated/wall/T = get_turf(src) // Ref to the wall we just built. - // Apparently set_material(...) for walls requires refs to the material singletons and not strings. - // This is different from how other material objects with their own set_material(...) do it, but whatever. - var/datum/material/M = name_to_material[the_rcd.material_to_use] - T.set_material(M, the_rcd.make_rwalls ? M : null, M) - T.add_hiddenprint(user) - return TRUE - if(RCD_AIRLOCK) - if(locate(/obj/machinery/door/airlock) in src) - return FALSE // No more airlock stacking. - to_chat(user, span("notice", "You build an airlock.")) - new the_rcd.airlock_type(src) - return TRUE - if(RCD_WINDOWGRILLE) - if(locate(/obj/structure/grille) in src) - return FALSE - to_chat(user, span("notice", "You construct the grille.")) - var/obj/structure/grille/G = new(src) - G.anchored = TRUE - return TRUE - if(RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - ChangeTurf(get_base_turf_by_area(src), preserve_outdoors = TRUE) - return TRUE - -/turf/simulated/floor/AltClick(mob/user) - if(isliving(user)) - var/mob/living/livingUser = user - if(try_graffiti(livingUser, livingUser.get_active_hand())) - return +/turf/simulated/floor + name = "plating" + desc = "Unfinished flooring." + icon = 'icons/turf/flooring/plating_vr.dmi' + icon_state = "plating" + + // Damage to flooring. + var/broken + var/burnt + + // Plating data. + var/base_name = "plating" + var/base_desc = "The naked hull." + var/base_icon = 'icons/turf/flooring/plating_vr.dmi' + var/base_icon_state = "plating" + var/static/list/base_footstep_sounds = list("human" = list( + 'sound/effects/footstep/plating1.ogg', + 'sound/effects/footstep/plating2.ogg', + 'sound/effects/footstep/plating3.ogg', + 'sound/effects/footstep/plating4.ogg', + 'sound/effects/footstep/plating5.ogg')) + + var/list/old_decals = null + + // Flooring data. + var/flooring_override + var/initial_flooring + var/decl/flooring/flooring + var/mineral = DEFAULT_WALL_MATERIAL + var/can_be_plated = TRUE // This is here for inheritance's sake. Override to FALSE for turfs you don't want someone to simply slap a plating over such as hazards. + + thermal_conductivity = 0.040 + heat_capacity = 10000 + +/turf/simulated/floor/is_plating() + return (!flooring || flooring.is_plating) + +/turf/simulated/floor/Initialize(mapload, floortype) + . = ..() + if(!floortype && initial_flooring) + floortype = initial_flooring + if(floortype) + set_flooring(get_flooring_data(floortype), TRUE) + . = INITIALIZE_HINT_LATELOAD // We'll update our icons after everyone is ready + else + footstep_sounds = base_footstep_sounds + if(can_dirty && can_start_dirty) + if(prob(dirty_prob)) + dirt += rand(50,100) + update_dirt() //5% chance to start with dirt on a floor tile- give the janitor something to do + +/turf/simulated/floor/LateInitialize() + . = ..() + update_icon(1) + +/turf/simulated/floor/proc/swap_decals() + var/current_decals = decals + decals = old_decals + old_decals = current_decals + +/turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring, var/initializing) + //make_plating(defer_icon_update = 1) + if(is_plating() && !initializing) // Plating -> Flooring + swap_decals() + flooring = newflooring + footstep_sounds = newflooring.footstep_sounds + if(!initializing) + update_icon(1) + levelupdate() + +//This proc will set floor_type to null and the update_icon() proc will then change the icon_state of the turf +//This proc auto corrects the grass tiles' siding. +/turf/simulated/floor/proc/make_plating(var/place_product, var/defer_icon_update) + cut_overlays() + + for(var/obj/effect/decal/writing/W in src) + qdel(W) + + name = base_name + desc = base_desc + icon = base_icon + icon_state = base_icon_state + footstep_sounds = base_footstep_sounds + + if(!is_plating()) // Flooring -> Plating + swap_decals() + if(flooring.build_type && place_product) + new flooring.build_type(src) + var/newtype = flooring.get_plating_type() + if(newtype) // Has a custom plating type to become + set_flooring(get_flooring_data(newtype)) + else + flooring = null + + set_light(0) + broken = null + burnt = null + flooring_override = null + levelupdate() + + if(!defer_icon_update) + update_icon(1) + +/turf/simulated/floor/levelupdate() + var/floored_over = !is_plating() + for(var/obj/O in src) + O.hide(O.hides_under_flooring() && floored_over) + +/turf/simulated/floor/can_engrave() + return (!flooring || flooring.can_engrave) + +/turf/simulated/floor/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_FLOORWALL) + // A wall costs four sheets to build (two for the grider and two for finishing it). + var/cost = RCD_SHEETS_PER_MATTER_UNIT * 4 + // R-walls cost five sheets, however. + if(the_rcd.make_rwalls) + cost += RCD_SHEETS_PER_MATTER_UNIT * 1 + return list( + RCD_VALUE_MODE = RCD_FLOORWALL, + RCD_VALUE_DELAY = 2 SECONDS, + RCD_VALUE_COST = cost + ) + if(RCD_AIRLOCK) + // Airlock assemblies cost four sheets. Let's just add another for the electronics/wires/etc. + return list( + RCD_VALUE_MODE = RCD_AIRLOCK, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + if(RCD_WINDOWGRILLE) + // One steel sheet for the girder (two rods, which is one sheet). + return list( + RCD_VALUE_MODE = RCD_WINDOWGRILLE, + RCD_VALUE_DELAY = 1 SECOND, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 1 + ) + if(RCD_DECONSTRUCT) + // Old RCDs made deconning the floor cost 10 units (IE, three times on full RCD). + // Now it's ten sheets worth of units (which is the same capacity-wise, three times on full RCD). + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = 5 SECONDS, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 10 + ) + return FALSE + + +/turf/simulated/floor/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + switch(passed_mode) + if(RCD_FLOORWALL) + to_chat(user, span("notice", "You build a wall.")) + ChangeTurf(/turf/simulated/wall) + var/turf/simulated/wall/T = get_turf(src) // Ref to the wall we just built. + // Apparently set_material(...) for walls requires refs to the material singletons and not strings. + // This is different from how other material objects with their own set_material(...) do it, but whatever. + var/datum/material/M = name_to_material[the_rcd.material_to_use] + T.set_material(M, the_rcd.make_rwalls ? M : null, M) + T.add_hiddenprint(user) + return TRUE + if(RCD_AIRLOCK) + if(locate(/obj/machinery/door/airlock) in src) + return FALSE // No more airlock stacking. + to_chat(user, span("notice", "You build an airlock.")) + new the_rcd.airlock_type(src) + return TRUE + if(RCD_WINDOWGRILLE) + if(locate(/obj/structure/grille) in src) + return FALSE + to_chat(user, span("notice", "You construct the grille.")) + var/obj/structure/grille/G = new(src) + G.anchored = TRUE + return TRUE + if(RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + ChangeTurf(get_base_turf_by_area(src), preserve_outdoors = TRUE) + return TRUE + +/turf/simulated/floor/AltClick(mob/user) + if(isliving(user)) + var/mob/living/livingUser = user + if(try_graffiti(livingUser, livingUser.get_active_hand())) + return . = ..() \ No newline at end of file diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm index 2b6f5422b46..49bd6bc2085 100644 --- a/code/game/turfs/simulated/floor_attackby.dm +++ b/code/game/turfs/simulated/floor_attackby.dm @@ -129,8 +129,8 @@ playsound(src, 'sound/items/Deconstruct.ogg', 80, 1) return // Plating repairs and removal - else if(istype(C, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/welder = C + else if(C.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/welder = C.get_welder() if(welder.isOn()) // Needs repairs if(broken || burnt) @@ -160,7 +160,7 @@ do_remove_plating(C, user, base_type) /turf/simulated/floor/proc/try_deconstruct_tile(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_crowbar()) + if(W.has_tool_quality(TOOL_CROWBAR)) if(broken || burnt) to_chat(user, "You remove the broken [flooring.descriptor].") make_plating() @@ -174,14 +174,14 @@ return 0 playsound(src, W.usesound, 80, 1) return 1 - else if(W.is_screwdriver() && (flooring.flags & TURF_REMOVE_SCREWDRIVER)) + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && (flooring.flags & TURF_REMOVE_SCREWDRIVER)) if(broken || burnt) return 0 to_chat(user, "You unscrew and remove the [flooring.descriptor].") make_plating(1) playsound(src, W.usesound, 80, 1) return 1 - else if(W.is_wrench() && (flooring.flags & TURF_REMOVE_WRENCH)) + else if(W.has_tool_quality(TOOL_WRENCH) && (flooring.flags & TURF_REMOVE_WRENCH)) to_chat(user, "You unwrench and remove the [flooring.descriptor].") make_plating(1) playsound(src, W.usesound, 80, 1) @@ -215,8 +215,8 @@ return TRUE /turf/simulated/floor/proc/do_remove_plating(obj/item/weapon/W, mob/user, base_type) - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(!WT.remove_fuel(5,user)) to_chat(user, "You don't have enough fuel in [WT] finish cutting through [src].") return diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm index 96532a954a5..28505a7397f 100644 --- a/code/game/turfs/simulated/floor_types.dm +++ b/code/game/turfs/simulated/floor_types.dm @@ -1,291 +1,291 @@ -/turf/simulated/floor/diona - name = "biomass flooring" - icon_state = "diona" - -/turf/simulated/floor/diona/attackby() - return - -//Shuttle Floors -/obj/landed_holder - name = "landed turf holder" - desc = "holds all the info about the turf this turf 'landed on'" - var/turf/turf_type - var/turf/simulated/shuttle/my_turf - var/image/turf_image - var/list/decals - -/obj/landed_holder/New(var/location = null, var/turf/simulated/shuttle/turf) - ..(null) - my_turf = turf - -/obj/landed_holder/proc/land_on(var/turf/T) - //Gather destination information - var/obj/landed_holder/new_holder = new(null) - new_holder.turf_type = T.type - new_holder.dir = T.dir - new_holder.icon = T.icon - new_holder.icon_state = T.icon_state - new_holder.copy_overlays(T, TRUE) - new_holder.underlays = T.underlays.Copy() - new_holder.decals = T.decals ? T.decals.Copy() : null - - //Set the destination to be like us - var/turf/simulated/shuttle/new_dest = T.ChangeTurf(my_turf.type,,1) - new_dest.set_dir(my_turf.dir) - new_dest.icon_state = my_turf.icon_state - new_dest.icon = my_turf.icon - new_dest.copy_overlays(my_turf, TRUE) - new_dest.underlays = my_turf.underlays - new_dest.decals = my_turf.decals - //Shuttle specific stuff - new_dest.interior_corner = my_turf.interior_corner - new_dest.takes_underlays = my_turf.takes_underlays - new_dest.under_turf = my_turf.under_turf - new_dest.join_flags = my_turf.join_flags - new_dest.join_group = my_turf.join_group - - // Associate the holder with the new turf. - new_holder.my_turf = new_dest - new_dest.landed_holder = new_holder - - //Update underlays if necessary (interior corners won't have changed). - if(new_dest.takes_underlays && !new_dest.interior_corner) - new_dest.underlay_update() - - return new_dest - -/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null) - var/turf/new_source - //Change our source to whatever it was before - if(turf_type) - new_source = my_turf.ChangeTurf(turf_type,,1) - new_source.set_dir(dir) - new_source.icon_state = icon_state - new_source.icon = icon - new_source.copy_overlays(src, TRUE) - new_source.underlays = underlays - new_source.decals = decals - else - new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1) - - return new_source - -/turf/simulated/shuttle - name = "shuttle" - icon = 'icons/turf/shuttle_white.dmi' - thermal_conductivity = 0.05 - heat_capacity = 0 - flags = TURF_ACID_IMMUNE - - var/obj/landed_holder/landed_holder - var/interior_corner = 0 - var/takes_underlays = 0 - var/turf/under_turf //Underlay override turf path. - var/join_flags = 0 //Bitstring to represent adjacency of joining walls - var/join_group = "shuttle" //A tag for what other walls to join with. Null if you don't want them to. - var/static/list/antilight_cache - -/turf/simulated/shuttle/Initialize(mapload) - . = ..() - if(!antilight_cache) - antilight_cache = list() - for(var/diag in cornerdirs) - var/image/I = image(LIGHTING_ICON, null, icon_state = "diagonals", layer = 10, dir = diag) - I.plane = PLANE_LIGHTING - antilight_cache["[diag]"] = I - -/turf/simulated/shuttle/Destroy() - landed_holder = null - return ..() - -// For joined corners touching static lighting turfs, add an overlay to cancel out that part of our lighting overlay. -/turf/simulated/shuttle/proc/update_breaklights() - if(join_flags in cornerdirs) //We're joined at an angle - //Dynamic lighting dissolver - var/turf/T = get_step(src, turn(join_flags,180)) - if(!T || !T.dynamic_lighting || !get_area(T).dynamic_lighting) - add_overlay(antilight_cache["[join_flags]"], TRUE) - return - cut_overlay(antilight_cache["[join_flags]"], TRUE) - -/turf/simulated/shuttle/proc/underlay_update() - if(!takes_underlays) - //Basically, if it's not forced, and we don't care, don't do it. - return - - var/turf/under //May be a path or a turf - var/mutable_appearance/us = new(src) //We'll use this for changes later - us.underlays.Cut() - - //Mapper wanted something specific - if(under_turf) - under = under_turf - - //Well if this isn't our first rodeo, we know EXACTLY what we landed on, and it looks like this. - if(landed_holder && !interior_corner) - //Space gets special treatment - if(ispath(landed_holder.turf_type, /turf/space)) - var/image/spaceimage = image(landed_holder.icon, landed_holder.icon_state) - spaceimage.plane = SPACE_PLANE - underlays = list(spaceimage) - else - var/mutable_appearance/landed_on = new(landed_holder) - landed_on.layer = FLOAT_LAYER //Not turf - landed_on.plane = FLOAT_PLANE //Not turf - us.underlays = list(landed_on) - appearance = us - - spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected - return - - if(!under) - var/turf/T1 - var/turf/T2 - var/turf/T3 - - T1 = get_step(src, turn(join_flags,135)) // 45 degrees before opposite - T2 = get_step(src, turn(join_flags,225)) // 45 degrees beyond opposite - T3 = get_step(src, turn(join_flags,180)) // Opposite from the diagonal - - if(isfloor(T1) && ((T1.type == T2.type) || (T1.type == T3.type))) - under = T1 - else if(isfloor(T2) && T2.type == T3.type) - under = T2 - else if(isfloor(T3) || istype(T3,/turf/space/transit)) - under = T3 - else - under = get_base_turf_by_area(src) - - if(istype(under,/turf/simulated/shuttle)) - interior_corner = 1 //Prevents us from 'landing on grass' and having interior corners update. - - var/mutable_appearance/under_ma - - if(ispath(under)) //It's just a mapper-specified path - under_ma = new() - under_ma.icon = initial(under.icon) - under_ma.icon_state = initial(under.icon_state) - under_ma.color = initial(under.color) - - else //It's a real turf - under_ma = new(under) - - if(under_ma) - if(ispath(under,/turf/space) || istype(under,/turf/space)) //Space gets weird treatment - under_ma.icon_state = "white" - under_ma.plane = SPACE_PLANE - us.underlays = list(under_ma) - - appearance = us - - spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected - - return under - -/turf/simulated/shuttle/floor - name = "floor" - icon = 'icons/turf/flooring/shuttle.dmi' - icon_state = "floor_blue" - -/turf/simulated/shuttle/floor/red - icon_state = "floor_red" - -/turf/simulated/shuttle/floor/yellow - icon_state = "floor_yellow" - -/turf/simulated/shuttle/floor/darkred - icon_state = "floor_dred" - -/turf/simulated/shuttle/floor/purple - icon_state = "floor_purple" - -/turf/simulated/shuttle/floor/white - icon_state = "floor_white" - -/turf/simulated/shuttle/floor/black - icon_state = "floor_black" - -/turf/simulated/shuttle/floor/glass - icon_state = "floor_glass" - takes_underlays = 1 - -/turf/simulated/shuttle/floor/alien - icon_state = "alienpod1" - light_range = 3 - light_power = 0.6 - light_color = "#66ffff" // Bright cyan. - light_on = TRUE - block_tele = TRUE - -/turf/simulated/shuttle/floor/alien/Initialize() - . = ..() - icon_state = "alienpod[rand(1, 9)]" - update_light() - -/turf/simulated/shuttle/floor/alienplating - icon_state = "alienplating" - block_tele = TRUE - -/turf/simulated/shuttle/floor/alienplating/external // For the outer rim of the UFO, to avoid active edges. -// The actual temperature adjustment is defined if the SC or other future map is compiled. - -/turf/simulated/shuttle/plating - name = "plating" - icon = 'icons/turf/floors.dmi' - icon_state = "plating" - -/turf/simulated/shuttle/plating/airless - oxygen = 0 - nitrogen = 0 - -//For 'carrying' otherwise empty turfs or stuff in space turfs with you or having holes in the floor or whatever. -/turf/simulated/shuttle/plating/carry - name = "carry turf" - icon = 'icons/turf/shuttle_parts.dmi' - icon_state = "carry" - takes_underlays = 1 - blocks_air = 1 //I'd make these unsimulated but it just fucks with so much stuff so many other places. - -/turf/simulated/shuttle/plating/carry/Initialize() - . = ..() - icon_state = "carry_ingame" - -/turf/simulated/shuttle/plating/airless/carry - name = "airless carry turf" - icon = 'icons/turf/shuttle_parts.dmi' - icon_state = "carry" - takes_underlays = 1 - blocks_air = 1 - -/turf/simulated/shuttle/plating/airless/carry/Initialize() - . = ..() - icon_state = "carry_ingame" - -/turf/simulated/shuttle/plating/skipjack //Skipjack plating - oxygen = 0 - nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD - -/turf/simulated/shuttle/floor/skipjack //Skipjack floors - name = "skipjack floor" - icon_state = "floor_dred" - oxygen = 0 - nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD - -/turf/simulated/shuttle/floor/voidcraft - name = "voidcraft tiles" - icon_state = "void" - -/turf/simulated/shuttle/floor/voidcraft/dark - name = "voidcraft tiles" - icon_state = "void_dark" - -/turf/simulated/shuttle/floor/voidcraft/light - name = "voidcraft tiles" - icon_state = "void_light" - -/turf/simulated/shuttle/floor/voidcraft/external // For avoiding active edges. -// The actual temperature adjustment is defined if the SC or other future map is compiled. - -/turf/simulated/shuttle/floor/voidcraft/external/dark - -/turf/simulated/shuttle/floor/voidcraft/external/light +/turf/simulated/floor/diona + name = "biomass flooring" + icon_state = "diona" + +/turf/simulated/floor/diona/attackby() + return + +//Shuttle Floors +/obj/landed_holder + name = "landed turf holder" + desc = "holds all the info about the turf this turf 'landed on'" + var/turf/turf_type + var/turf/simulated/shuttle/my_turf + var/image/turf_image + var/list/decals + +/obj/landed_holder/New(var/location = null, var/turf/simulated/shuttle/turf) + ..(null) + my_turf = turf + +/obj/landed_holder/proc/land_on(var/turf/T) + //Gather destination information + var/obj/landed_holder/new_holder = new(null) + new_holder.turf_type = T.type + new_holder.dir = T.dir + new_holder.icon = T.icon + new_holder.icon_state = T.icon_state + new_holder.copy_overlays(T, TRUE) + new_holder.underlays = T.underlays.Copy() + new_holder.decals = T.decals ? T.decals.Copy() : null + + //Set the destination to be like us + var/turf/simulated/shuttle/new_dest = T.ChangeTurf(my_turf.type,,1) + new_dest.set_dir(my_turf.dir) + new_dest.icon_state = my_turf.icon_state + new_dest.icon = my_turf.icon + new_dest.copy_overlays(my_turf, TRUE) + new_dest.underlays = my_turf.underlays + new_dest.decals = my_turf.decals + //Shuttle specific stuff + new_dest.interior_corner = my_turf.interior_corner + new_dest.takes_underlays = my_turf.takes_underlays + new_dest.under_turf = my_turf.under_turf + new_dest.join_flags = my_turf.join_flags + new_dest.join_group = my_turf.join_group + + // Associate the holder with the new turf. + new_holder.my_turf = new_dest + new_dest.landed_holder = new_holder + + //Update underlays if necessary (interior corners won't have changed). + if(new_dest.takes_underlays && !new_dest.interior_corner) + new_dest.underlay_update() + + return new_dest + +/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null) + var/turf/new_source + //Change our source to whatever it was before + if(turf_type) + new_source = my_turf.ChangeTurf(turf_type,,1) + new_source.set_dir(dir) + new_source.icon_state = icon_state + new_source.icon = icon + new_source.copy_overlays(src, TRUE) + new_source.underlays = underlays + new_source.decals = decals + else + new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1) + + return new_source + +/turf/simulated/shuttle + name = "shuttle" + icon = 'icons/turf/shuttle_white.dmi' + thermal_conductivity = 0.05 + heat_capacity = 0 + flags = TURF_ACID_IMMUNE + + var/obj/landed_holder/landed_holder + var/interior_corner = 0 + var/takes_underlays = 0 + var/turf/under_turf //Underlay override turf path. + var/join_flags = 0 //Bitstring to represent adjacency of joining walls + var/join_group = "shuttle" //A tag for what other walls to join with. Null if you don't want them to. + var/static/list/antilight_cache + +/turf/simulated/shuttle/Initialize(mapload) + . = ..() + if(!antilight_cache) + antilight_cache = list() + for(var/diag in cornerdirs) + var/image/I = image(LIGHTING_ICON, null, icon_state = "diagonals", layer = 10, dir = diag) + I.plane = PLANE_LIGHTING + antilight_cache["[diag]"] = I + +/turf/simulated/shuttle/Destroy() + landed_holder = null + return ..() + +// For joined corners touching static lighting turfs, add an overlay to cancel out that part of our lighting overlay. +/turf/simulated/shuttle/proc/update_breaklights() + if(join_flags in cornerdirs) //We're joined at an angle + //Dynamic lighting dissolver + var/turf/T = get_step(src, turn(join_flags,180)) + if(!T || !T.dynamic_lighting || !get_area(T).dynamic_lighting) + add_overlay(antilight_cache["[join_flags]"], TRUE) + return + cut_overlay(antilight_cache["[join_flags]"], TRUE) + +/turf/simulated/shuttle/proc/underlay_update() + if(!takes_underlays) + //Basically, if it's not forced, and we don't care, don't do it. + return + + var/turf/under //May be a path or a turf + var/mutable_appearance/us = new(src) //We'll use this for changes later + us.underlays.Cut() + + //Mapper wanted something specific + if(under_turf) + under = under_turf + + //Well if this isn't our first rodeo, we know EXACTLY what we landed on, and it looks like this. + if(landed_holder && !interior_corner) + //Space gets special treatment + if(ispath(landed_holder.turf_type, /turf/space)) + var/image/spaceimage = image(landed_holder.icon, landed_holder.icon_state) + spaceimage.plane = SPACE_PLANE + underlays = list(spaceimage) + else + var/mutable_appearance/landed_on = new(landed_holder) + landed_on.layer = FLOAT_LAYER //Not turf + landed_on.plane = FLOAT_PLANE //Not turf + us.underlays = list(landed_on) + appearance = us + + spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected + return + + if(!under) + var/turf/T1 + var/turf/T2 + var/turf/T3 + + T1 = get_step(src, turn(join_flags,135)) // 45 degrees before opposite + T2 = get_step(src, turn(join_flags,225)) // 45 degrees beyond opposite + T3 = get_step(src, turn(join_flags,180)) // Opposite from the diagonal + + if(isfloor(T1) && ((T1.type == T2.type) || (T1.type == T3.type))) + under = T1 + else if(isfloor(T2) && T2.type == T3.type) + under = T2 + else if(isfloor(T3) || istype(T3,/turf/space/transit)) + under = T3 + else + under = get_base_turf_by_area(src) + + if(istype(under,/turf/simulated/shuttle)) + interior_corner = 1 //Prevents us from 'landing on grass' and having interior corners update. + + var/mutable_appearance/under_ma + + if(ispath(under)) //It's just a mapper-specified path + under_ma = new() + under_ma.icon = initial(under.icon) + under_ma.icon_state = initial(under.icon_state) + under_ma.color = initial(under.color) + + else //It's a real turf + under_ma = new(under) + + if(under_ma) + if(ispath(under,/turf/space) || istype(under,/turf/space)) //Space gets weird treatment + under_ma.icon_state = "white" + under_ma.plane = SPACE_PLANE + us.underlays = list(under_ma) + + appearance = us + + spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected + + return under + +/turf/simulated/shuttle/floor + name = "floor" + icon = 'icons/turf/flooring/shuttle.dmi' + icon_state = "floor_blue" + +/turf/simulated/shuttle/floor/red + icon_state = "floor_red" + +/turf/simulated/shuttle/floor/yellow + icon_state = "floor_yellow" + +/turf/simulated/shuttle/floor/darkred + icon_state = "floor_dred" + +/turf/simulated/shuttle/floor/purple + icon_state = "floor_purple" + +/turf/simulated/shuttle/floor/white + icon_state = "floor_white" + +/turf/simulated/shuttle/floor/black + icon_state = "floor_black" + +/turf/simulated/shuttle/floor/glass + icon_state = "floor_glass" + takes_underlays = 1 + +/turf/simulated/shuttle/floor/alien + icon_state = "alienpod1" + light_range = 3 + light_power = 0.6 + light_color = "#66ffff" // Bright cyan. + light_on = TRUE + block_tele = TRUE + +/turf/simulated/shuttle/floor/alien/Initialize() + . = ..() + icon_state = "alienpod[rand(1, 9)]" + update_light() + +/turf/simulated/shuttle/floor/alienplating + icon_state = "alienplating" + block_tele = TRUE + +/turf/simulated/shuttle/floor/alienplating/external // For the outer rim of the UFO, to avoid active edges. +// The actual temperature adjustment is defined if the SC or other future map is compiled. + +/turf/simulated/shuttle/plating + name = "plating" + icon = 'icons/turf/floors.dmi' + icon_state = "plating" + +/turf/simulated/shuttle/plating/airless + oxygen = 0 + nitrogen = 0 + +//For 'carrying' otherwise empty turfs or stuff in space turfs with you or having holes in the floor or whatever. +/turf/simulated/shuttle/plating/carry + name = "carry turf" + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "carry" + takes_underlays = 1 + blocks_air = 1 //I'd make these unsimulated but it just fucks with so much stuff so many other places. + +/turf/simulated/shuttle/plating/carry/Initialize() + . = ..() + icon_state = "carry_ingame" + +/turf/simulated/shuttle/plating/airless/carry + name = "airless carry turf" + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "carry" + takes_underlays = 1 + blocks_air = 1 + +/turf/simulated/shuttle/plating/airless/carry/Initialize() + . = ..() + icon_state = "carry_ingame" + +/turf/simulated/shuttle/plating/skipjack //Skipjack plating + oxygen = 0 + nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD + +/turf/simulated/shuttle/floor/skipjack //Skipjack floors + name = "skipjack floor" + icon_state = "floor_dred" + oxygen = 0 + nitrogen = MOLES_N2STANDARD + MOLES_O2STANDARD + +/turf/simulated/shuttle/floor/voidcraft + name = "voidcraft tiles" + icon_state = "void" + +/turf/simulated/shuttle/floor/voidcraft/dark + name = "voidcraft tiles" + icon_state = "void_dark" + +/turf/simulated/shuttle/floor/voidcraft/light + name = "voidcraft tiles" + icon_state = "void_light" + +/turf/simulated/shuttle/floor/voidcraft/external // For avoiding active edges. +// The actual temperature adjustment is defined if the SC or other future map is compiled. + +/turf/simulated/shuttle/floor/voidcraft/external/dark + +/turf/simulated/shuttle/floor/voidcraft/external/light diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index ec5eeb18f44..0368e00e31c 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -1,102 +1,102 @@ -/turf/simulated/floor/lava - name = "lava" - desc = "A pool of molten rock." - description_info = "Molten rock is extremly dangerous, as it will cause massive harm to anything that touches it.
                    \ - A firesuit cannot fully protect from contact with molten rock." - gender = PLURAL // So it says "That's some lava." on examine. - icon = 'icons/turf/outdoors.dmi' - icon_state = "lava" - edge_blending_priority = -1 - light_range = 2 - light_power = 0.75 - light_color = LIGHT_COLOR_LAVA - light_on = TRUE - movement_cost = 2 - can_build_into_floor = TRUE - can_be_plated = FALSE - can_dirty = FALSE - initial_flooring = /decl/flooring/lava // Defining this in case someone DOES step on lava and survive. Somehow. - flags = TURF_ACID_IMMUNE - -/turf/simulated/floor/lava/outdoors - outdoors = OUTDOORS_YES - -// For maximum pedantry. -/turf/simulated/floor/lava/Initialize() - if(!is_outdoors()) - name = "magma" - update_icon() - update_light() - return ..() - -/turf/simulated/floor/lava/make_outdoors() - ..() - name = "lava" - -/turf/simulated/floor/lava/make_indoors() - ..() - name = "magma" - -/turf/simulated/floor/lava/make_plating(place_product, defer_icon_update) - return - -/turf/simulated/floor/lava/set_flooring(decl/flooring/newflooring, initializing) - if(newflooring?.type == initial_flooring) - return ..() - return - -/turf/simulated/floor/lava/ex_act(severity) - return - -/turf/simulated/floor/lava/Entered(atom/movable/AM) - if(burn_stuff(AM)) - START_PROCESSING(SSturfs, src) - -/turf/simulated/floor/lava/hitby(atom/movable/AM) - if(burn_stuff(AM)) - START_PROCESSING(SSturfs, src) - -/turf/simulated/floor/lava/process() - if(!burn_stuff()) - return PROCESS_KILL - -/turf/simulated/floor/lava/proc/is_safe() - //if anything matching this typecache is found in the lava, we don't burn things - var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/catwalk)) - var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache) - return LAZYLEN(found_safeties) - -/turf/simulated/floor/lava/proc/burn_stuff(atom/movable/AM) - . = FALSE - - if(is_safe()) - return FALSE - - var/thing_to_check = src - if(AM) - thing_to_check = list(AM) - - for(var/thing in thing_to_check) - if(isobj(thing)) - var/obj/O = thing - if(O.throwing || O.is_incorporeal()) - continue - . = TRUE - O.lava_act() - - else if(isliving(thing)) - var/mob/living/L = thing - if(L.hovering || L.throwing || L.is_incorporeal()) // Flying over the lava. We're just gonna pretend convection doesn't exist. - continue - . = TRUE - L.lava_act() - -// Lava that does nothing at all. -/turf/simulated/floor/lava/harmless/burn_stuff(atom/movable/AM) - return FALSE - -// Tells AI mobs to not suicide by pathing into lava if it would hurt them. -/turf/simulated/floor/lava/is_safe_to_enter(mob/living/L) - if(!is_safe() && !L.hovering) - return FALSE +/turf/simulated/floor/lava + name = "lava" + desc = "A pool of molten rock." + description_info = "Molten rock is extremly dangerous, as it will cause massive harm to anything that touches it.
                    \ + A firesuit cannot fully protect from contact with molten rock." + gender = PLURAL // So it says "That's some lava." on examine. + icon = 'icons/turf/outdoors.dmi' + icon_state = "lava" + edge_blending_priority = -1 + light_range = 2 + light_power = 0.75 + light_color = LIGHT_COLOR_LAVA + light_on = TRUE + movement_cost = 2 + can_build_into_floor = TRUE + can_be_plated = FALSE + can_dirty = FALSE + initial_flooring = /decl/flooring/lava // Defining this in case someone DOES step on lava and survive. Somehow. + flags = TURF_ACID_IMMUNE + +/turf/simulated/floor/lava/outdoors + outdoors = OUTDOORS_YES + +// For maximum pedantry. +/turf/simulated/floor/lava/Initialize() + if(!is_outdoors()) + name = "magma" + update_icon() + update_light() + return ..() + +/turf/simulated/floor/lava/make_outdoors() + ..() + name = "lava" + +/turf/simulated/floor/lava/make_indoors() + ..() + name = "magma" + +/turf/simulated/floor/lava/make_plating(place_product, defer_icon_update) + return + +/turf/simulated/floor/lava/set_flooring(decl/flooring/newflooring, initializing) + if(newflooring?.type == initial_flooring) + return ..() + return + +/turf/simulated/floor/lava/ex_act(severity) + return + +/turf/simulated/floor/lava/Entered(atom/movable/AM) + if(burn_stuff(AM)) + START_PROCESSING(SSturfs, src) + +/turf/simulated/floor/lava/hitby(atom/movable/AM) + if(burn_stuff(AM)) + START_PROCESSING(SSturfs, src) + +/turf/simulated/floor/lava/process() + if(!burn_stuff()) + return PROCESS_KILL + +/turf/simulated/floor/lava/proc/is_safe() + //if anything matching this typecache is found in the lava, we don't burn things + var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/catwalk)) + var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache) + return LAZYLEN(found_safeties) + +/turf/simulated/floor/lava/proc/burn_stuff(atom/movable/AM) + . = FALSE + + if(is_safe()) + return FALSE + + var/thing_to_check = src + if(AM) + thing_to_check = list(AM) + + for(var/thing in thing_to_check) + if(isobj(thing)) + var/obj/O = thing + if(O.throwing || O.is_incorporeal()) + continue + . = TRUE + O.lava_act() + + else if(isliving(thing)) + var/mob/living/L = thing + if(L.hovering || L.throwing || L.is_incorporeal()) // Flying over the lava. We're just gonna pretend convection doesn't exist. + continue + . = TRUE + L.lava_act() + +// Lava that does nothing at all. +/turf/simulated/floor/lava/harmless/burn_stuff(atom/movable/AM) + return FALSE + +// Tells AI mobs to not suicide by pathing into lava if it would hurt them. +/turf/simulated/floor/lava/is_safe_to_enter(mob/living/L) + if(!is_safe() && !L.hovering) + return FALSE return ..() \ No newline at end of file diff --git a/code/game/turfs/simulated/outdoors/outdoors.dm b/code/game/turfs/simulated/outdoors/outdoors.dm index e55196aea6c..c75eeaab64b 100644 --- a/code/game/turfs/simulated/outdoors/outdoors.dm +++ b/code/game/turfs/simulated/outdoors/outdoors.dm @@ -60,11 +60,12 @@ var/list/turf_edge_cache = list() return . = ..() - +/* VOREStation remove - handled by parent /turf/simulated/floor/Initialize(mapload) if(is_outdoors()) SSplanets.addTurf(src) . = ..() +*/ /turf/simulated/floor/Destroy() if(is_outdoors()) @@ -123,6 +124,7 @@ var/list/turf_edge_cache = list() desc = "Hard as a rock." icon_state = "rock" edge_blending_priority = 1 + initial_flooring = /decl/flooring/rock /turf/simulated/floor/outdoors/rocks/caves outdoors = OUTDOORS_NO @@ -185,4 +187,4 @@ var/list/turf_edge_cache = list() icon = 'icons/turf/concrete.dmi' icon_state = "concrete_dark" desc = "Some sort of material composite road." - edge_blending_priority = -1 \ No newline at end of file + edge_blending_priority = -1 diff --git a/code/game/turfs/simulated/outdoors/outdoors_vr.dm b/code/game/turfs/simulated/outdoors/outdoors_vr.dm index b8227291651..39b35b2d928 100644 --- a/code/game/turfs/simulated/outdoors/outdoors_vr.dm +++ b/code/game/turfs/simulated/outdoors/outdoors_vr.dm @@ -1,168 +1,168 @@ -/turf/simulated/floor/tiled/asteroid_steel/outdoors - name = "weathered tiles" - desc = "Old tiles left out in the elements." - outdoors = OUTDOORS_YES - edge_blending_priority = 1 - -/turf/simulated/floor/outdoors/newdirt - name = "dirt" - desc = "Looks dirty." - icon = 'icons/turf/outdoors_vr.dmi' - icon_state = "dirt0" - edge_blending_priority = 2 - initial_flooring = /decl/flooring/outdoors/newdirt - -/decl/flooring/outdoors/newdirt - name = "dirt" - desc = "Looks dirty." - icon = 'icons/turf/outdoors_vr.dmi' - icon_base = "dirt0" - footstep_sounds = list("human" = list( - 'sound/effects/footstep/asteroid1.ogg', - 'sound/effects/footstep/asteroid2.ogg', - 'sound/effects/footstep/asteroid3.ogg', - 'sound/effects/footstep/asteroid4.ogg', - 'sound/effects/footstep/asteroid5.ogg', - 'sound/effects/footstep/MedDirt1.ogg', - 'sound/effects/footstep/MedDirt2.ogg', - 'sound/effects/footstep/MedDirt3.ogg', - 'sound/effects/footstep/MedDirt4.ogg')) - -/turf/simulated/floor/outdoors/newdirt/Initialize(mapload) - var/possibledirts = list( - "dirt0" = 150, - "dirt1" = 25, - "dirt2" = 25, - "dirt3" = 25, - "dirt4" = 25, - "dirt5" = 10, - "dirt6" = 10, - "dirt7" = 3, - "dirt8" = 3, - "dirt9" = 1 - ) - flooring_override = pickweight(possibledirts) - return ..() - - -/turf/simulated/floor/outdoors/newdirt_nograss - name = "dirt" - desc = "Looks dirty." - icon = 'icons/turf/outdoors_vr.dmi' - icon_state = "dirt0" - edge_blending_priority = 2 - initial_flooring = /decl/flooring/outdoors/newdirt - -/turf/simulated/floor/outdoors/newdirt_nograss/Initialize(mapload) - var/possibledirts = list( - "dirt0" = 200, - "dirt6" = 20, - "dirt7" = 3, - "dirt8" = 3, - "dirt9" = 1 - ) - flooring_override = pickweight(possibledirts) - return ..() - -/turf/simulated/floor/outdoors/sidewalk - name = "sidewalk" - desc = "Concrete shaped into a path!" - icon = 'icons/turf/outdoors_vr.dmi' - icon_state = "sidewalk" - edge_blending_priority = -1 - movement_cost = -0.5 - initial_flooring = /decl/flooring/outdoors/sidewalk - can_dirty = TRUE - -/decl/flooring/outdoors/sidewalk - name = "sidewalk" - desc = "Concrete shaped into a path!" - icon = 'icons/turf/outdoors_vr.dmi' - icon_base = "sidewalk" - has_damage_range = 2 - damage_temperature = T0C+1400 - flags = TURF_REMOVE_CROWBAR | TURF_CAN_BREAK | TURF_CAN_BURN - build_type = /obj/item/stack/tile/floor/sidewalk - can_paint = 1 - can_engrave = FALSE - - footstep_sounds = list("human" = list( - 'sound/effects/footstep/LightStone1.ogg', - 'sound/effects/footstep/LightStone2.ogg', - 'sound/effects/footstep/LightStone3.ogg', - 'sound/effects/footstep/LightStone4.ogg',)) - -/obj/item/stack/tile/floor/sidewalk - name = "sidewalk tile" - singular_name = "floor tile" - desc = "A stone tile fit for covering a section of floor." - icon_state = "tile" - force = 6.0 - matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - no_variants = FALSE - -/turf/simulated/floor/outdoors/sidewalk/Initialize(mapload) - var/possibledirts = list( - "[initial(icon_state)]" = 150, - "[initial(icon_state)]1" = 3, - "[initial(icon_state)]2" = 3, - "[initial(icon_state)]3" = 3, - "[initial(icon_state)]4" = 3, - "[initial(icon_state)]5" = 3, - "[initial(icon_state)]6" = 2, - "[initial(icon_state)]7" = 2, - "[initial(icon_state)]8" = 2, - "[initial(icon_state)]9" = 2, - "[initial(icon_state)]10" = 2 - ) - flooring_override = pickweight(possibledirts) - return ..() - -/turf/simulated/floor/outdoors/sidewalk/side - icon_state = "side-walk" - initial_flooring = /decl/flooring/outdoors/sidewalk/side - - -/decl/flooring/outdoors/sidewalk/side - icon_base = "sidewalk" - build_type = /obj/item/stack/tile/floor/sidewalk/side - -/obj/item/stack/tile/floor/sidewalk/side - -/turf/simulated/floor/outdoors/sidewalk/slab - icon_state = "slab" - initial_flooring = /decl/flooring/outdoors/sidewalk/slab - -/decl/flooring/outdoors/sidewalk/slab - icon_base = "slab" - build_type = /obj/item/stack/tile/floor/sidewalk/slab - -/obj/item/stack/tile/floor/sidewalk/slab/ - -/turf/simulated/floor/outdoors/sidewalk/slab/city - icon_state = "cityslab" - initial_flooring = /decl/flooring/outdoors/sidewalk/slab/city - -/decl/flooring/outdoors/sidewalk/slab/city - icon_base = "cityslab" - build_type = /obj/item/stack/tile/floor/sidewalk/slab/city - -/obj/item/stack/tile/floor/sidewalk/slab/city - -/obj/item/stack/tile/floor/concrete //Proper concrete tile. - name = "concrete tile" - singular_name = "floor tile" - desc = "A concrete tile fit for covering a section of floor." - icon_state = "tile" - force = 6.0 - matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) - throwforce = 15.0 - throw_speed = 5 - throw_range = 20 - no_variants = TRUE - -/decl/flooring/concrete - build_type = /obj/item/stack/tile/floor/concrete +/turf/simulated/floor/tiled/asteroid_steel/outdoors + name = "weathered tiles" + desc = "Old tiles left out in the elements." + outdoors = OUTDOORS_YES + edge_blending_priority = 1 + +/turf/simulated/floor/outdoors/newdirt + name = "dirt" + desc = "Looks dirty." + icon = 'icons/turf/outdoors_vr.dmi' + icon_state = "dirt0" + edge_blending_priority = 2 + initial_flooring = /decl/flooring/outdoors/newdirt + +/decl/flooring/outdoors/newdirt + name = "dirt" + desc = "Looks dirty." + icon = 'icons/turf/outdoors_vr.dmi' + icon_base = "dirt0" + footstep_sounds = list("human" = list( + 'sound/effects/footstep/asteroid1.ogg', + 'sound/effects/footstep/asteroid2.ogg', + 'sound/effects/footstep/asteroid3.ogg', + 'sound/effects/footstep/asteroid4.ogg', + 'sound/effects/footstep/asteroid5.ogg', + 'sound/effects/footstep/MedDirt1.ogg', + 'sound/effects/footstep/MedDirt2.ogg', + 'sound/effects/footstep/MedDirt3.ogg', + 'sound/effects/footstep/MedDirt4.ogg')) + +/turf/simulated/floor/outdoors/newdirt/Initialize(mapload) + var/possibledirts = list( + "dirt0" = 150, + "dirt1" = 25, + "dirt2" = 25, + "dirt3" = 25, + "dirt4" = 25, + "dirt5" = 10, + "dirt6" = 10, + "dirt7" = 3, + "dirt8" = 3, + "dirt9" = 1 + ) + flooring_override = pickweight(possibledirts) + return ..() + + +/turf/simulated/floor/outdoors/newdirt_nograss + name = "dirt" + desc = "Looks dirty." + icon = 'icons/turf/outdoors_vr.dmi' + icon_state = "dirt0" + edge_blending_priority = 2 + initial_flooring = /decl/flooring/outdoors/newdirt + +/turf/simulated/floor/outdoors/newdirt_nograss/Initialize(mapload) + var/possibledirts = list( + "dirt0" = 200, + "dirt6" = 20, + "dirt7" = 3, + "dirt8" = 3, + "dirt9" = 1 + ) + flooring_override = pickweight(possibledirts) + return ..() + +/turf/simulated/floor/outdoors/sidewalk + name = "sidewalk" + desc = "Concrete shaped into a path!" + icon = 'icons/turf/outdoors_vr.dmi' + icon_state = "sidewalk" + edge_blending_priority = -1 + movement_cost = -0.5 + initial_flooring = /decl/flooring/outdoors/sidewalk + can_dirty = TRUE + +/decl/flooring/outdoors/sidewalk + name = "sidewalk" + desc = "Concrete shaped into a path!" + icon = 'icons/turf/outdoors_vr.dmi' + icon_base = "sidewalk" + has_damage_range = 2 + damage_temperature = T0C+1400 + flags = TURF_REMOVE_CROWBAR | TURF_CAN_BREAK | TURF_CAN_BURN + build_type = /obj/item/stack/tile/floor/sidewalk + can_paint = 1 + can_engrave = FALSE + + footstep_sounds = list("human" = list( + 'sound/effects/footstep/LightStone1.ogg', + 'sound/effects/footstep/LightStone2.ogg', + 'sound/effects/footstep/LightStone3.ogg', + 'sound/effects/footstep/LightStone4.ogg',)) + +/obj/item/stack/tile/floor/sidewalk + name = "sidewalk tile" + singular_name = "floor tile" + desc = "A stone tile fit for covering a section of floor." + icon_state = "tile" + force = 6.0 + matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + no_variants = FALSE + +/turf/simulated/floor/outdoors/sidewalk/Initialize(mapload) + var/possibledirts = list( + "[initial(icon_state)]" = 150, + "[initial(icon_state)]1" = 3, + "[initial(icon_state)]2" = 3, + "[initial(icon_state)]3" = 3, + "[initial(icon_state)]4" = 3, + "[initial(icon_state)]5" = 3, + "[initial(icon_state)]6" = 2, + "[initial(icon_state)]7" = 2, + "[initial(icon_state)]8" = 2, + "[initial(icon_state)]9" = 2, + "[initial(icon_state)]10" = 2 + ) + flooring_override = pickweight(possibledirts) + return ..() + +/turf/simulated/floor/outdoors/sidewalk/side + icon_state = "side-walk" + initial_flooring = /decl/flooring/outdoors/sidewalk/side + + +/decl/flooring/outdoors/sidewalk/side + icon_base = "sidewalk" + build_type = /obj/item/stack/tile/floor/sidewalk/side + +/obj/item/stack/tile/floor/sidewalk/side + +/turf/simulated/floor/outdoors/sidewalk/slab + icon_state = "slab" + initial_flooring = /decl/flooring/outdoors/sidewalk/slab + +/decl/flooring/outdoors/sidewalk/slab + icon_base = "slab" + build_type = /obj/item/stack/tile/floor/sidewalk/slab + +/obj/item/stack/tile/floor/sidewalk/slab/ + +/turf/simulated/floor/outdoors/sidewalk/slab/city + icon_state = "cityslab" + initial_flooring = /decl/flooring/outdoors/sidewalk/slab/city + +/decl/flooring/outdoors/sidewalk/slab/city + icon_base = "cityslab" + build_type = /obj/item/stack/tile/floor/sidewalk/slab/city + +/obj/item/stack/tile/floor/sidewalk/slab/city + +/obj/item/stack/tile/floor/concrete //Proper concrete tile. + name = "concrete tile" + singular_name = "floor tile" + desc = "A concrete tile fit for covering a section of floor." + icon_state = "tile" + force = 6.0 + matter = list(DEFAULT_WALL_MATERIAL = SHEET_MATERIAL_AMOUNT / 4) + throwforce = 15.0 + throw_speed = 5 + throw_range = 20 + no_variants = TRUE + +/decl/flooring/concrete + build_type = /obj/item/stack/tile/floor/concrete diff --git a/code/game/turfs/simulated/outdoors/sky.dm b/code/game/turfs/simulated/outdoors/sky.dm index 2f9f8106bb9..6b94f245c14 100644 --- a/code/game/turfs/simulated/outdoors/sky.dm +++ b/code/game/turfs/simulated/outdoors/sky.dm @@ -14,7 +14,7 @@ /turf/simulated/sky/Initialize() . = ..() - SSplanets.addTurf(src) + //SSplanets.addTurf(src) VOREStation edit - Handled by parent set_light(2, 2, "#FFFFFF") /turf/simulated/sky/north diff --git a/code/game/turfs/simulated/underwater.dm b/code/game/turfs/simulated/underwater.dm index 348d919e806..ac98d36fc7e 100644 --- a/code/game/turfs/simulated/underwater.dm +++ b/code/game/turfs/simulated/underwater.dm @@ -71,9 +71,9 @@ water_state = "cult" /turf/simulated/floor/water/underwater/ruins - icon = 'maps/atoll/icons/turfs/marble.dmi' + icon = 'maps/redgate/falls/icons/turfs/marble.dmi' icon_state = "1" // So it shows up in the map editor as water. - water_icon = 'maps/atoll/icons/turfs/marble.dmi' + water_icon = 'maps/redgate/falls/icons/turfs/marble.dmi' water_state = "1" /turf/simulated/floor/water/underwater/sand diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 8c6eb32ae3c..3118aa5e03e 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -1,415 +1,415 @@ -//Interactions -/turf/simulated/wall/proc/toggle_open(var/mob/user) - - if(can_open == WALL_OPENING) - return - - SSradiation.resistance_cache.Remove(src) - - if(density) - can_open = WALL_OPENING - //flick("[material.icon_base]fwall_opening", src) - density = FALSE - blocks_air = ZONE_BLOCKED - update_icon() - update_air() - set_light(0) - src.blocks_air = 0 - set_opacity(0) - for(var/turf/simulated/turf in loc) - air_master.mark_for_update(turf) - else - can_open = WALL_OPENING - //flick("[material.icon_base]fwall_closing", src) - density = TRUE - blocks_air = AIR_BLOCKED - update_icon() - update_air() - set_light(1) - src.blocks_air = 1 - set_opacity(1) - for(var/turf/simulated/turf in loc) - air_master.mark_for_update(turf) - - can_open = WALL_CAN_OPEN - update_icon() - -/turf/simulated/wall/proc/update_air() - if(!air_master) - return - - for(var/turf/simulated/turf in loc) - update_thermal(turf) - air_master.mark_for_update(turf) - - -/turf/simulated/wall/proc/update_thermal(var/turf/simulated/source) - if(istype(source)) - if(density && opacity) - source.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - else - source.thermal_conductivity = initial(source.thermal_conductivity) - -/turf/simulated/wall/proc/fail_smash(var/mob/user) - var/damage_lower = 25 - var/damage_upper = 75 - if(isanimal(user)) - var/mob/living/simple_mob/S = user - playsound(src, S.attack_sound, 75, 1) - if(!(S.melee_damage_upper >= STRUCTURE_MIN_DAMAGE_THRESHOLD * 2)) - to_chat(user, "You bounce against the wall.") - return FALSE - damage_lower = S.melee_damage_lower - damage_upper = S.melee_damage_upper - to_chat(user, "You smash against the wall!") - user.do_attack_animation(src) - take_damage(rand(damage_lower,damage_upper)) - -/turf/simulated/wall/proc/success_smash(var/mob/user) - to_chat(user, "You smash through the wall!") - user.do_attack_animation(src) - if(isanimal(user)) - var/mob/living/simple_mob/S = user - playsound(src, S.attack_sound, 75, 1) - spawn(1) - dismantle_wall(1) - -/turf/simulated/wall/proc/try_touch(var/mob/user, var/rotting) - - if(rotting) - if(reinf_material) - to_chat(user, "\The [reinf_material.display_name] feels porous and crumbly.") - else - to_chat(user, "\The [material.display_name] crumbles under your touch!") - dismantle_wall() - return 1 - - if(!can_open) - if(!material.wall_touch_special(src, user)) - to_chat(user, "You push the wall, but nothing happens.") - playsound(src, 'sound/weapons/Genhit.ogg', 25, 1) - else - toggle_open(user) - return 0 - - -/turf/simulated/wall/attack_hand(var/mob/user) - - radiate() - add_fingerprint(user) - user.setClickCooldown(user.get_attack_speed()) - var/rotting = (locate(/obj/effect/overlay/wallrot) in src) - if (HULK in user.mutations) - if (rotting || !prob(material.hardness)) - success_smash(user) - else - fail_smash(user) - return 1 - - try_touch(user, rotting) - -/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message) - - radiate() - user.setClickCooldown(user.get_attack_speed()) - var/rotting = (locate(/obj/effect/overlay/wallrot) in src) - if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) - try_touch(user, rotting) - return - - if(rotting) - return success_smash(user) - - if(reinf_material) - if(damage >= max(material.hardness, reinf_material.hardness) ) - return success_smash(user) - else if(damage >= material.hardness) - return success_smash(user) - return fail_smash(user) - -/turf/simulated/wall/attackby(var/obj/item/weapon/W, var/mob/user) - - user.setClickCooldown(user.get_attack_speed(W)) - -/* -//As with the floors, only this time it works AND tries pushing the wall after it's done. - if(!construction_stage && user.a_intent == I_HELP) - if(try_graffiti(user,W)) - return -*/ - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return - - //get the user's location - if(!istype(user.loc, /turf)) - return //can't do this stuff whilst inside objects and such - - if(W) - radiate() - if(is_hot(W)) - burn(is_hot(W)) - - if(istype(W, /obj/item/device/electronic_assembly/wallmount)) - var/obj/item/device/electronic_assembly/wallmount/IC = W - IC.mount_assembly(src, user) - return - - if(istype(W, /obj/item/stack/tile/roofing)) - var/expended_tile = FALSE // To track the case. If a ceiling is built in a multiz zlevel, it also necessarily roofs it against weather - var/turf/T = GetAbove(src) - var/obj/item/stack/tile/roofing/R = W - - // Place plating over a wall - if(T) - if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) - if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating - T.ReplaceWithLattice() - T.ChangeTurf(/turf/simulated/floor, preserve_outdoors = TRUE) - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - user.visible_message("[user] patches a hole in the ceiling.", "You patch a hole in the ceiling.") - expended_tile = TRUE - else - to_chat(user, "There aren't any holes in the ceiling to patch here.") - return - - // Create a ceiling to shield from the weather - if(is_outdoors()) - if(expended_tile || R.use(1)) // Don't need to check adjacent turfs for a wall, we're building on one - make_indoors() - if(!expended_tile) // Would've already played a sound - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - user.visible_message("[user] roofs \the [src], shielding it from the elements.", "You roof \the [src] tile, shielding it from the elements.") - return - - - if(locate(/obj/effect/overlay/wallrot) in src) - if(istype(W, /obj/item/weapon/weldingtool) ) - var/obj/item/weapon/weldingtool/WT = W - if( WT.remove_fuel(0,user) ) - to_chat(user, "You burn away the fungi with \the [WT].") - playsound(src, WT.usesound, 10, 1) - for(var/obj/effect/overlay/wallrot/WR in src) - qdel(WR) - return - else if(!is_sharp(W) && W.force >= 10 || W.force >= 20) - to_chat(user, "\The [src] crumbles away under the force of your [W.name].") - src.dismantle_wall(1) - return - - //THERMITE related stuff. Calls src.thermitemelt() which handles melting simulated walls and the relevant effects - if(thermite) - if( istype(W, /obj/item/weapon/weldingtool) ) - var/obj/item/weapon/weldingtool/WT = W - if( WT.remove_fuel(0,user) ) - thermitemelt(user) - return - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - thermitemelt(user) - return - - else if( istype(W, /obj/item/weapon/melee/energy/blade) ) - var/obj/item/weapon/melee/energy/blade/EB = W - - EB.spark_system.start() - to_chat(user, "You slash \the [src] with \the [EB]; the thermite ignites!") - playsound(src, "sparks", 50, 1) - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - - thermitemelt(user) - return - - var/turf/T = user.loc //get user's location for delay checks - - if(damage && istype(W, /obj/item/weapon/weldingtool)) - - var/obj/item/weapon/weldingtool/WT = W - - if(!WT.isOn()) - return - - if(WT.remove_fuel(0,user)) - to_chat(user, "You start repairing the damage to [src].") - playsound(src, WT.usesound, 100, 1) - if(do_after(user, max(5, damage / 5) * WT.toolspeed) && WT && WT.isOn()) - to_chat(user, "You finish repairing the damage to [src].") - take_damage(-damage) - else - to_chat(user, "You need more welding fuel to complete this task.") - return - user.update_examine_panel(src) - return - - // Basic dismantling. - //var/dismantle_toolspeed = 0 - if(isnull(construction_stage) || !reinf_material) - - var/cut_delay = 60 - material.cut_delay - var/dismantle_verb - var/dismantle_sound - - if(istype(W,/obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(!WT.isOn()) - return - if(!WT.remove_fuel(0,user)) - to_chat(user, "You need more welding fuel to complete this task.") - return - dismantle_verb = "cutting" - dismantle_sound = W.usesound - // cut_delay *= 0.7 // Tools themselves now can shorten the time it takes. - else if(istype(W,/obj/item/weapon/melee/energy/blade)) - dismantle_sound = "sparks" - dismantle_verb = "slicing" - //dismantle_toolspeed = 1 - cut_delay *= 0.5 - else if(istype(W,/obj/item/weapon/pickaxe)) - var/obj/item/weapon/pickaxe/P = W - dismantle_verb = P.drill_verb - dismantle_sound = P.drill_sound - cut_delay -= P.digspeed - - if(dismantle_verb) - - to_chat(user, "You begin [dismantle_verb] through the outer plating.") - if(dismantle_sound) - playsound(src, dismantle_sound, 100, 1) - - if(cut_delay < 0) - cut_delay = 0 - - if(!do_after(user,cut_delay * W.toolspeed)) - return - - to_chat(user, "You remove the outer plating.") - dismantle_wall() - user.visible_message("The wall was torn open by [user]!") - return - - //Reinforced dismantling. - else - switch(construction_stage) - if(6) - if (W.is_wirecutter()) - playsound(src, W.usesound, 100, 1) - construction_stage = 5 - user.update_examine_panel(src) - to_chat(user, "You cut through the outer grille.") - update_icon() - return - if(5) - if (W.is_screwdriver()) - to_chat(user, "You begin removing the support lines.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 5) - return - construction_stage = 4 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You unscrew the support lines.") - return - else if (W.is_wirecutter()) - construction_stage = 6 - user.update_examine_panel(src) - to_chat(user, "You mend the outer grille.") - playsound(src, W.usesound, 100, 1) - update_icon() - return - if(4) - var/cut_cover - if(istype(W,/obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(!WT.isOn()) - return - if(WT.remove_fuel(0,user)) - cut_cover=1 - else - to_chat(user, "You need more welding fuel to complete this task.") - return - else if (istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - cut_cover = 1 - if(cut_cover) - to_chat(user, "You begin slicing through the metal cover.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user, 60 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) - return - construction_stage = 3 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You press firmly on the cover, dislodging it.") - return - else if (W.is_screwdriver()) - to_chat(user, "You begin screwing down the support lines.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) - return - construction_stage = 5 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You screw down the support lines.") - return - if(3) - if (W.is_crowbar()) - to_chat(user, "You struggle to pry off the cover.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 3) - return - construction_stage = 2 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You pry off the cover.") - return - if(2) - if (W.is_wrench()) - to_chat(user, "You start loosening the anchoring bolts which secure the support rods to their frame.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 2) - return - construction_stage = 1 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You remove the bolts anchoring the support rods.") - return - if(1) - var/cut_cover - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if( WT.remove_fuel(0,user) ) - cut_cover=1 - else - to_chat(user, "You need more welding fuel to complete this task.") - return - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - cut_cover = 1 - if(cut_cover) - to_chat(user, "You begin slicing through the support rods.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,70 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 1) - return - construction_stage = 0 - user.update_examine_panel(src) - update_icon() - to_chat(user, "You slice through the support rods.") - return - if(0) - if(W.is_crowbar()) - to_chat(user, "You struggle to pry off the outer sheath.") - playsound(src, W.usesound, 100, 1) - if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || !user || !W || !T ) - return - if(user.loc == T && user.get_active_hand() == W ) - to_chat(user, "You pry off the outer sheath.") - dismantle_wall() - return - - if(istype(W,/obj/item/frame)) - var/obj/item/frame/F = W - F.try_build(src, user) - return - - else if(!istype(W,/obj/item/weapon/rcd) && !istype(W, /obj/item/weapon/reagent_containers)) - return attack_hand(user) - - +//Interactions +/turf/simulated/wall/proc/toggle_open(var/mob/user) + + if(can_open == WALL_OPENING) + return + + SSradiation.resistance_cache.Remove(src) + + if(density) + can_open = WALL_OPENING + //flick("[material.icon_base]fwall_opening", src) + density = FALSE + blocks_air = ZONE_BLOCKED + update_icon() + update_air() + set_light(0) + src.blocks_air = 0 + set_opacity(0) + for(var/turf/simulated/turf in loc) + air_master.mark_for_update(turf) + else + can_open = WALL_OPENING + //flick("[material.icon_base]fwall_closing", src) + density = TRUE + blocks_air = AIR_BLOCKED + update_icon() + update_air() + set_light(1) + src.blocks_air = 1 + set_opacity(1) + for(var/turf/simulated/turf in loc) + air_master.mark_for_update(turf) + + can_open = WALL_CAN_OPEN + update_icon() + +/turf/simulated/wall/proc/update_air() + if(!air_master) + return + + for(var/turf/simulated/turf in loc) + update_thermal(turf) + air_master.mark_for_update(turf) + + +/turf/simulated/wall/proc/update_thermal(var/turf/simulated/source) + if(istype(source)) + if(density && opacity) + source.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + else + source.thermal_conductivity = initial(source.thermal_conductivity) + +/turf/simulated/wall/proc/fail_smash(var/mob/user) + var/damage_lower = 25 + var/damage_upper = 75 + if(isanimal(user)) + var/mob/living/simple_mob/S = user + playsound(src, S.attack_sound, 75, 1) + if(!(S.melee_damage_upper >= STRUCTURE_MIN_DAMAGE_THRESHOLD * 2)) + to_chat(user, "You bounce against the wall.") + return FALSE + damage_lower = S.melee_damage_lower + damage_upper = S.melee_damage_upper + to_chat(user, "You smash against the wall!") + user.do_attack_animation(src) + take_damage(rand(damage_lower,damage_upper)) + +/turf/simulated/wall/proc/success_smash(var/mob/user) + to_chat(user, "You smash through the wall!") + user.do_attack_animation(src) + if(isanimal(user)) + var/mob/living/simple_mob/S = user + playsound(src, S.attack_sound, 75, 1) + spawn(1) + dismantle_wall(1) + +/turf/simulated/wall/proc/try_touch(var/mob/user, var/rotting) + + if(rotting) + if(reinf_material) + to_chat(user, "\The [reinf_material.display_name] feels porous and crumbly.") + else + to_chat(user, "\The [material.display_name] crumbles under your touch!") + dismantle_wall() + return 1 + + if(!can_open) + if(!material.wall_touch_special(src, user)) + to_chat(user, "You push the wall, but nothing happens.") + playsound(src, 'sound/weapons/Genhit.ogg', 25, 1) + else + toggle_open(user) + return 0 + + +/turf/simulated/wall/attack_hand(var/mob/user) + + radiate() + add_fingerprint(user) + user.setClickCooldown(user.get_attack_speed()) + var/rotting = (locate(/obj/effect/overlay/wallrot) in src) + if (HULK in user.mutations) + if (rotting || !prob(material.hardness)) + success_smash(user) + else + fail_smash(user) + return 1 + + try_touch(user, rotting) + +/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message) + + radiate() + user.setClickCooldown(user.get_attack_speed()) + var/rotting = (locate(/obj/effect/overlay/wallrot) in src) + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) + try_touch(user, rotting) + return + + if(rotting) + return success_smash(user) + + if(reinf_material) + if(damage >= max(material.hardness, reinf_material.hardness) ) + return success_smash(user) + else if(damage >= material.hardness) + return success_smash(user) + return fail_smash(user) + +/turf/simulated/wall/attackby(var/obj/item/weapon/W, var/mob/user) + + user.setClickCooldown(user.get_attack_speed(W)) + +/* +//As with the floors, only this time it works AND tries pushing the wall after it's done. + if(!construction_stage && user.a_intent == I_HELP) + if(try_graffiti(user,W)) + return +*/ + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return + + //get the user's location + if(!istype(user.loc, /turf)) + return //can't do this stuff whilst inside objects and such + + if(W) + radiate() + if(is_hot(W)) + burn(is_hot(W)) + + if(istype(W, /obj/item/device/electronic_assembly/wallmount)) + var/obj/item/device/electronic_assembly/wallmount/IC = W + IC.mount_assembly(src, user) + return + + if(istype(W, /obj/item/stack/tile/roofing)) + var/expended_tile = FALSE // To track the case. If a ceiling is built in a multiz zlevel, it also necessarily roofs it against weather + var/turf/T = GetAbove(src) + var/obj/item/stack/tile/roofing/R = W + + // Place plating over a wall + if(T) + if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) + if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating + T.ReplaceWithLattice() + T.ChangeTurf(/turf/simulated/floor, preserve_outdoors = TRUE) + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + user.visible_message("[user] patches a hole in the ceiling.", "You patch a hole in the ceiling.") + expended_tile = TRUE + else + to_chat(user, "There aren't any holes in the ceiling to patch here.") + return + + // Create a ceiling to shield from the weather + if(is_outdoors()) + if(expended_tile || R.use(1)) // Don't need to check adjacent turfs for a wall, we're building on one + make_indoors() + if(!expended_tile) // Would've already played a sound + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + user.visible_message("[user] roofs \the [src], shielding it from the elements.", "You roof \the [src] tile, shielding it from the elements.") + return + + + if(locate(/obj/effect/overlay/wallrot) in src) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if( WT.remove_fuel(0,user) ) + to_chat(user, "You burn away the fungi with \the [WT].") + playsound(src, WT.usesound, 10, 1) + for(var/obj/effect/overlay/wallrot/WR in src) + qdel(WR) + return + else if(!is_sharp(W) && W.force >= 10 || W.force >= 20) + to_chat(user, "\The [src] crumbles away under the force of your [W.name].") + src.dismantle_wall(1) + return + + //THERMITE related stuff. Calls src.thermitemelt() which handles melting simulated walls and the relevant effects + if(thermite) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if( WT.remove_fuel(0,user) ) + thermitemelt(user) + return + + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + thermitemelt(user) + return + + else if( istype(W, /obj/item/weapon/melee/energy/blade) ) + var/obj/item/weapon/melee/energy/blade/EB = W + + EB.spark_system.start() + to_chat(user, "You slash \the [src] with \the [EB]; the thermite ignites!") + playsound(src, "sparks", 50, 1) + playsound(src, 'sound/weapons/blade1.ogg', 50, 1) + + thermitemelt(user) + return + + var/turf/T = user.loc //get user's location for delay checks + + if(damage && W.has_tool_quality(TOOL_WELDER)) + + var/obj/item/weapon/weldingtool/WT = W.get_welder() + + if(!WT.isOn()) + return + + if(WT.remove_fuel(0,user)) + to_chat(user, "You start repairing the damage to [src].") + playsound(src, WT.usesound, 100, 1) + if(do_after(user, max(5, damage / 5) * WT.toolspeed) && WT && WT.isOn()) + to_chat(user, "You finish repairing the damage to [src].") + take_damage(-damage) + else + to_chat(user, "You need more welding fuel to complete this task.") + return + user.update_examine_panel(src) + return + + // Basic dismantling. + //var/dismantle_toolspeed = 0 + if(isnull(construction_stage) || !reinf_material) + + var/cut_delay = 60 - material.cut_delay + var/dismantle_verb + var/dismantle_sound + + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(!WT.isOn()) + return + if(!WT.remove_fuel(0,user)) + to_chat(user, "You need more welding fuel to complete this task.") + return + dismantle_verb = "cutting" + dismantle_sound = W.usesound + // cut_delay *= 0.7 // Tools themselves now can shorten the time it takes. + else if(istype(W,/obj/item/weapon/melee/energy/blade)) + dismantle_sound = "sparks" + dismantle_verb = "slicing" + //dismantle_toolspeed = 1 + cut_delay *= 0.5 + else if(istype(W,/obj/item/weapon/pickaxe)) + var/obj/item/weapon/pickaxe/P = W + dismantle_verb = P.drill_verb + dismantle_sound = P.drill_sound + cut_delay -= P.digspeed + + if(dismantle_verb) + + to_chat(user, "You begin [dismantle_verb] through the outer plating.") + if(dismantle_sound) + playsound(src, dismantle_sound, 100, 1) + + if(cut_delay < 0) + cut_delay = 0 + + if(!do_after(user,cut_delay * W.toolspeed)) + return + + to_chat(user, "You remove the outer plating.") + dismantle_wall() + user.visible_message("The wall was torn open by [user]!") + return + + //Reinforced dismantling. + else + switch(construction_stage) + if(6) + if (W.has_tool_quality(TOOL_WIRECUTTER)) + playsound(src, W.usesound, 100, 1) + construction_stage = 5 + user.update_examine_panel(src) + to_chat(user, "You cut through the outer grille.") + update_icon() + return + if(5) + if (W.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You begin removing the support lines.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 5) + return + construction_stage = 4 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You unscrew the support lines.") + return + else if (W.has_tool_quality(TOOL_WIRECUTTER)) + construction_stage = 6 + user.update_examine_panel(src) + to_chat(user, "You mend the outer grille.") + playsound(src, W.usesound, 100, 1) + update_icon() + return + if(4) + var/cut_cover + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(!WT.isOn()) + return + if(WT.remove_fuel(0,user)) + cut_cover=1 + else + to_chat(user, "You need more welding fuel to complete this task.") + return + else if (istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + cut_cover = 1 + if(cut_cover) + to_chat(user, "You begin slicing through the metal cover.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user, 60 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) + return + construction_stage = 3 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You press firmly on the cover, dislodging it.") + return + else if (W.has_tool_quality(TOOL_SCREWDRIVER)) + to_chat(user, "You begin screwing down the support lines.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 4) + return + construction_stage = 5 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You screw down the support lines.") + return + if(3) + if (W.has_tool_quality(TOOL_CROWBAR)) + to_chat(user, "You struggle to pry off the cover.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 3) + return + construction_stage = 2 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You pry off the cover.") + return + if(2) + if (W.has_tool_quality(TOOL_WRENCH)) + to_chat(user, "You start loosening the anchoring bolts which secure the support rods to their frame.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,40 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 2) + return + construction_stage = 1 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You remove the bolts anchoring the support rods.") + return + if(1) + var/cut_cover + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if( WT.remove_fuel(0,user) ) + cut_cover=1 + else + to_chat(user, "You need more welding fuel to complete this task.") + return + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + cut_cover = 1 + if(cut_cover) + to_chat(user, "You begin slicing through the support rods.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,70 * W.toolspeed) || !istype(src, /turf/simulated/wall) || construction_stage != 1) + return + construction_stage = 0 + user.update_examine_panel(src) + update_icon() + to_chat(user, "You slice through the support rods.") + return + if(0) + if(W.has_tool_quality(TOOL_CROWBAR)) + to_chat(user, "You struggle to pry off the outer sheath.") + playsound(src, W.usesound, 100, 1) + if(!do_after(user,100 * W.toolspeed) || !istype(src, /turf/simulated/wall) || !user || !W || !T ) + return + if(user.loc == T && user.get_active_hand() == W ) + to_chat(user, "You pry off the outer sheath.") + dismantle_wall() + return + + if(istype(W,/obj/item/frame)) + var/obj/item/frame/F = W + F.try_build(src, user) + return + + else if(!istype(W,/obj/item/weapon/rcd) && !istype(W, /obj/item/weapon/reagent_containers)) + return attack_hand(user) + + diff --git a/code/game/turfs/simulated/wall_icon.dm b/code/game/turfs/simulated/wall_icon.dm index 7abc69e3b26..fdc54a18c4a 100644 --- a/code/game/turfs/simulated/wall_icon.dm +++ b/code/game/turfs/simulated/wall_icon.dm @@ -1,152 +1,152 @@ -/turf/simulated/wall/proc/update_material() - - if(!material) - return - - if(reinf_material) - construction_stage = 6 - else - construction_stage = null - if(!material) - material = get_material_by_name(DEFAULT_WALL_MATERIAL) - if(material) - explosion_resistance = material.explosion_resistance - if(reinf_material && reinf_material.explosion_resistance > explosion_resistance) - explosion_resistance = reinf_material.explosion_resistance - - if(reinf_material) - name = "reinforced [material.display_name] wall" - desc = "It seems to be a section of wall reinforced with [reinf_material.display_name] and plated with [material.display_name]." - else - name = "[material.display_name] wall" - desc = "It seems to be a section of wall plated with [material.display_name]." - - if(material.opacity > 0.5 && !opacity) - set_light(1) - else if(material.opacity < 0.5 && opacity) - set_light(0) - - SSradiation.resistance_cache.Remove(src) - update_connections(1) - update_icon() - - -/turf/simulated/wall/proc/set_material(var/datum/material/newmaterial, var/datum/material/newrmaterial, var/datum/material/newgmaterial) - material = newmaterial - reinf_material = newrmaterial - if(!newgmaterial) - girder_material = DEFAULT_WALL_MATERIAL - else - girder_material = newgmaterial - update_material() - -/turf/simulated/wall/update_icon() - if(!material) - return - - if(!damage_overlays[1]) //list hasn't been populated - generate_overlays() - - cut_overlays() - var/image/I - - if(!density) - I = image(wall_masks, "[material.icon_base]fwall_open") - I.color = material.icon_colour - add_overlay(I) - return - - for(var/i = 1 to 4) - I = image(wall_masks, "[material.icon_base][wall_connections[i]]", dir = 1<<(i-1)) - I.color = material.icon_colour - add_overlay(I) - - if(reinf_material) - if(construction_stage != null && construction_stage < 6) - I = image(wall_masks, "reinf_construct-[construction_stage]") - I.color = reinf_material.icon_colour - add_overlay(I) - else - if("[reinf_material.icon_reinf]0" in cached_icon_states(wall_masks)) - // Directional icon - for(var/i = 1 to 4) - I = image(wall_masks, "[reinf_material.icon_reinf][wall_connections[i]]", dir = 1<<(i-1)) - I.color = reinf_material.icon_colour - add_overlay(I) - else if("[reinf_material.icon_reinf]" in cached_icon_states(wall_masks)) - I = image(wall_masks, reinf_material.icon_reinf) - I.color = reinf_material.icon_colour - add_overlay(I) - var/image/texture = material.get_wall_texture() - if(texture) - add_overlay(texture) - - if(damage != 0) - var/integrity = material.integrity - if(reinf_material) - integrity += reinf_material.integrity - - var/overlay = round(damage / integrity * damage_overlays.len) + 1 - if(overlay > damage_overlays.len) - overlay = damage_overlays.len - - add_overlay(damage_overlays[overlay]) - return - -/turf/simulated/wall/proc/generate_overlays() - var/alpha_inc = 256 / damage_overlays.len - - for(var/i = 1; i <= damage_overlays.len; i++) - var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") - img.blend_mode = BLEND_MULTIPLY - img.alpha = (i * alpha_inc) - 1 - damage_overlays[i] = img - - -/turf/simulated/wall/proc/update_connections(propagate = 0) - if(!material) - return - var/list/dirs = list() - var/inrange = orange(src, 1) - for(var/turf/simulated/wall/W in inrange) - if(!W.material) - continue - if(propagate) - W.update_connections() - W.update_icon() - if(can_join_with_wall(W)) - dirs += get_dir(src, W) - for(var/obj/structure/low_wall/WF in inrange) - if(can_join_with_low_wall(WF)) - dirs += get_dir(src, WF) - - special_wall_connections(dirs, inrange) - wall_connections = dirs_to_corner_states(dirs) - -/turf/simulated/wall/proc/special_wall_connections(list/dirs, list/inrange) - if(material.icon_base == "hull") // Could be improved... - var/additional_dirs = 0 - for(var/direction in alldirs) - var/turf/T = get_step(src,direction) - if(T && (locate(/obj/structure/hull_corner) in T)) - dirs += direction - additional_dirs |= direction - if(additional_dirs) - for(var/diag_dir in cornerdirs) - if ((additional_dirs & diag_dir) == diag_dir) - dirs += diag_dir - -/turf/simulated/wall/proc/can_join_with_wall(var/turf/simulated/wall/W) - //No blending if no material - if(!material || !W.material) - return 0 - //We can blend if either is the same, or a subtype, of the other one - if(istype(W.material, material.type) || istype(material, W.material.type)) - return 1 - //Also blend if they have the same iconbase - if(material.icon_base == W.material.icon_base) - return 1 - return 0 - -/turf/simulated/wall/proc/can_join_with_low_wall(var/obj/structure/low_wall/WF) +/turf/simulated/wall/proc/update_material() + + if(!material) + return + + if(reinf_material) + construction_stage = 6 + else + construction_stage = null + if(!material) + material = get_material_by_name(DEFAULT_WALL_MATERIAL) + if(material) + explosion_resistance = material.explosion_resistance + if(reinf_material && reinf_material.explosion_resistance > explosion_resistance) + explosion_resistance = reinf_material.explosion_resistance + + if(reinf_material) + name = "reinforced [material.display_name] wall" + desc = "It seems to be a section of wall reinforced with [reinf_material.display_name] and plated with [material.display_name]." + else + name = "[material.display_name] wall" + desc = "It seems to be a section of wall plated with [material.display_name]." + + if(material.opacity > 0.5 && !opacity) + set_light(1) + else if(material.opacity < 0.5 && opacity) + set_light(0) + + SSradiation.resistance_cache.Remove(src) + update_connections(1) + update_icon() + + +/turf/simulated/wall/proc/set_material(var/datum/material/newmaterial, var/datum/material/newrmaterial, var/datum/material/newgmaterial) + material = newmaterial + reinf_material = newrmaterial + if(!newgmaterial) + girder_material = DEFAULT_WALL_MATERIAL + else + girder_material = newgmaterial + update_material() + +/turf/simulated/wall/update_icon() + if(!material) + return + + if(!damage_overlays[1]) //list hasn't been populated + generate_overlays() + + cut_overlays() + var/image/I + + if(!density) + I = image(wall_masks, "[material.icon_base]fwall_open") + I.color = material.icon_colour + add_overlay(I) + return + + for(var/i = 1 to 4) + I = image(wall_masks, "[material.icon_base][wall_connections[i]]", dir = 1<<(i-1)) + I.color = material.icon_colour + add_overlay(I) + + if(reinf_material) + if(construction_stage != null && construction_stage < 6) + I = image(wall_masks, "reinf_construct-[construction_stage]") + I.color = reinf_material.icon_colour + add_overlay(I) + else + if("[reinf_material.icon_reinf]0" in cached_icon_states(wall_masks)) + // Directional icon + for(var/i = 1 to 4) + I = image(wall_masks, "[reinf_material.icon_reinf][wall_connections[i]]", dir = 1<<(i-1)) + I.color = reinf_material.icon_colour + add_overlay(I) + else if("[reinf_material.icon_reinf]" in cached_icon_states(wall_masks)) + I = image(wall_masks, reinf_material.icon_reinf) + I.color = reinf_material.icon_colour + add_overlay(I) + var/image/texture = material.get_wall_texture() + if(texture) + add_overlay(texture) + + if(damage != 0) + var/integrity = material.integrity + if(reinf_material) + integrity += reinf_material.integrity + + var/overlay = round(damage / integrity * damage_overlays.len) + 1 + if(overlay > damage_overlays.len) + overlay = damage_overlays.len + + add_overlay(damage_overlays[overlay]) + return + +/turf/simulated/wall/proc/generate_overlays() + var/alpha_inc = 256 / damage_overlays.len + + for(var/i = 1; i <= damage_overlays.len; i++) + var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") + img.blend_mode = BLEND_MULTIPLY + img.alpha = (i * alpha_inc) - 1 + damage_overlays[i] = img + + +/turf/simulated/wall/proc/update_connections(propagate = 0) + if(!material) + return + var/list/dirs = list() + var/inrange = orange(src, 1) + for(var/turf/simulated/wall/W in inrange) + if(!W.material) + continue + if(propagate) + W.update_connections() + W.update_icon() + if(can_join_with_wall(W)) + dirs += get_dir(src, W) + for(var/obj/structure/low_wall/WF in inrange) + if(can_join_with_low_wall(WF)) + dirs += get_dir(src, WF) + + special_wall_connections(dirs, inrange) + wall_connections = dirs_to_corner_states(dirs) + +/turf/simulated/wall/proc/special_wall_connections(list/dirs, list/inrange) + if(material.icon_base == "hull") // Could be improved... + var/additional_dirs = 0 + for(var/direction in alldirs) + var/turf/T = get_step(src,direction) + if(T && (locate(/obj/structure/hull_corner) in T)) + dirs += direction + additional_dirs |= direction + if(additional_dirs) + for(var/diag_dir in cornerdirs) + if ((additional_dirs & diag_dir) == diag_dir) + dirs += diag_dir + +/turf/simulated/wall/proc/can_join_with_wall(var/turf/simulated/wall/W) + //No blending if no material + if(!material || !W.material) + return 0 + //We can blend if either is the same, or a subtype, of the other one + if(istype(W.material, material.type) || istype(material, W.material.type)) + return 1 + //Also blend if they have the same iconbase + if(material.icon_base == W.material.icon_base) + return 1 + return 0 + +/turf/simulated/wall/proc/can_join_with_low_wall(var/obj/structure/low_wall/WF) return FALSE \ No newline at end of file diff --git a/code/game/turfs/simulated/wall_types.dm b/code/game/turfs/simulated/wall_types.dm index 53e501faad7..1443460e303 100644 --- a/code/game/turfs/simulated/wall_types.dm +++ b/code/game/turfs/simulated/wall_types.dm @@ -1,710 +1,710 @@ -/turf/simulated/wall/r_wall - icon_state = "rgeneric" -/turf/simulated/wall/r_wall/Initialize(mapload) - . = ..(mapload, "plasteel","plasteel") //3strong - -/turf/simulated/wall/shull - icon_state = "hull-steel" -/turf/simulated/wall/shull/Initialize(mapload) //Spaaaace ship. - . = ..(mapload, MAT_STEELHULL, null, MAT_STEELHULL) -/turf/simulated/wall/rshull - icon_state = "hull-r_steel" -/turf/simulated/wall/rshull/Initialize(mapload) - . = ..(mapload, MAT_STEELHULL, MAT_STEELHULL, MAT_STEELHULL) -/turf/simulated/wall/pshull - icon_state = "hull-plasteel" -/turf/simulated/wall/pshull/Initialize(mapload) //Spaaaace-er ship. - . = ..(mapload, MAT_PLASTEELHULL, null, MAT_PLASTEELHULL) -/turf/simulated/wall/rpshull - icon_state = "hull-r_plasteel" -/turf/simulated/wall/rpshull/Initialize(mapload) - . = ..(mapload, MAT_PLASTEELHULL, MAT_PLASTEELHULL, MAT_PLASTEELHULL) -/turf/simulated/wall/dshull - icon_state = "hull-durasteel" -/turf/simulated/wall/dshull/Initialize(mapload) //Spaaaace-est ship. - . = ..(mapload, MAT_DURASTEELHULL, null, MAT_DURASTEELHULL) -/turf/simulated/wall/rdshull - icon_state = "hull-r_durasteel" -/turf/simulated/wall/rdshull/Initialize(mapload) - . = ..(mapload, MAT_DURASTEELHULL, MAT_DURASTEELHULL, MAT_DURASTEELHULL) -/turf/simulated/wall/thull - icon_state = "hull-titanium" -/turf/simulated/wall/thull/Initialize(mapload) - . = ..(mapload, MAT_TITANIUMHULL, null, MAT_TITANIUMHULL) -/turf/simulated/wall/rthull - icon_state = "hull-r_titanium" -/turf/simulated/wall/rthull/Initialize(mapload) - . = ..(mapload, MAT_TITANIUMHULL, MAT_TITANIUMHULL, MAT_TITANIUMHULL) - -/turf/simulated/wall/cult - icon_state = "cult" -/turf/simulated/wall/cult/Initialize(mapload) - . = ..(mapload, "cult","cult2","cult") -/turf/unsimulated/wall/cult - name = "cult wall" - desc = "Hideous images dance beneath the surface." - icon = 'icons/turf/wall_masks.dmi' - icon_state = "cult" - -/turf/simulated/wall/iron/Initialize(mapload) - . = ..(mapload, "iron") -/turf/simulated/wall/uranium/Initialize(mapload) - . = ..(mapload, "uranium") -/turf/simulated/wall/diamond/Initialize(mapload) - . = ..(mapload, "diamond") -/turf/simulated/wall/gold/Initialize(mapload) - . = ..(mapload, "gold") -/turf/simulated/wall/silver/Initialize(mapload) - . = ..(mapload, "silver") -/turf/simulated/wall/lead/Initialize(mapload) - . = ..(mapload, "lead") -/turf/simulated/wall/r_lead/Initialize(mapload) - . = ..(mapload, "lead", "lead") -/turf/simulated/wall/phoron/Initialize(mapload) - . = ..(mapload, "phoron") -/turf/simulated/wall/sandstone/Initialize(mapload) - . = ..(mapload, "sandstone") -/turf/simulated/wall/ironphoron/Initialize(mapload) - . = ..(mapload, "iron","phoron") -/turf/simulated/wall/golddiamond/Initialize(mapload) - . = ..(mapload, "gold","diamond") -/turf/simulated/wall/silvergold/Initialize(mapload) - . = ..(mapload, "silver","gold") -/turf/simulated/wall/sandstonediamond/Initialize(mapload) - . = ..(mapload, "sandstone","diamond") -/turf/simulated/wall/snowbrick/Initialize(mapload) - . = ..(mapload, "packed snow") - -/turf/simulated/wall/resin/Initialize(mapload) - . = ..(mapload, "resin",null,"resin") - -/turf/simulated/wall/concrete - icon_state = "brick" - -/turf/simulated/wall/concrete/Initialize(mapload) - . = ..(mapload, "concrete") //3strong - -/turf/simulated/wall/r_concrete - icon_state = "rbrick" - -/turf/simulated/wall/r_concrete/Initialize(mapload) - . = ..(mapload, "concrete","plasteel rebar") //3strong - -// Kind of wondering if this is going to bite me in the butt. -/turf/simulated/wall/skipjack/Initialize(mapload) - . = ..(mapload, "alienalloy") -/turf/simulated/wall/skipjack/attackby() - return -/turf/simulated/wall/titanium/Initialize(mapload) - . = ..(mapload, "titanium") - -/turf/simulated/wall/durasteel/Initialize(mapload) - . = ..(mapload, "durasteel", "durasteel") - -/turf/simulated/wall/wood/Initialize(mapload) - . = ..(mapload, MAT_WOOD) - -/turf/simulated/wall/hardwood/Initialize(mapload) - . = ..(mapload, MAT_HARDWOOD) - -/turf/simulated/wall/sifwood/Initialize(mapload) - . = ..(mapload, MAT_SIFWOOD) - -/turf/simulated/wall/log/Initialize(mapload) - . = ..(mapload, MAT_LOG) - -/turf/simulated/wall/log_sif/Initialize(mapload) - . = ..(mapload, MAT_SIFLOG) - -// Shuttle Walls -/turf/simulated/shuttle/wall - name = "autojoin wall" - icon_state = "light" - opacity = 1 - density = TRUE - blocks_air = 1 - - var/base_state = "light" //The base iconstate to base sprites on - var/hard_corner = 0 //Forces hard corners (as opposed to diagonals) - var/true_name = "wall" //What to rename this to on init - - //Extra things this will try to locate and act like we're joining to. You can put doors, or whatever. - //Carefully means only if it's on a /turf/simulated/shuttle subtype turf. - var/static/list/join_carefully = list( - /obj/structure/grille, - /obj/machinery/door/blast/regular - ) - var/static/list/join_always = list( - /obj/structure/shuttle/engine, - /obj/structure/shuttle/window, - /obj/machinery/door/airlock/voidcraft - ) - -/turf/simulated/shuttle/wall/hard_corner - name = "hardcorner wall" - icon_state = "light-hc" - hard_corner = 1 - -/turf/simulated/shuttle/wall/no_join - icon_state = "light-nj" - join_group = null - -/turf/simulated/shuttle/wall/dark - icon = 'icons/turf/shuttle_dark.dmi' - icon_state = "dark" - base_state = "dark" - -/turf/simulated/shuttle/wall/dark/hard_corner - name = "hardcorner wall" - icon_state = "dark-hc" - hard_corner = 1 - -/turf/simulated/shuttle/wall/dark/no_join - name = "nojoin wall" - icon_state = "dark-nj" - join_group = null - -/turf/simulated/shuttle/wall/alien - icon = 'icons/turf/shuttle_alien.dmi' - icon_state = "alien" - base_state = "alien" - light_range = 3 - light_power = 0.75 - light_color = "#ff0066" // Pink-ish - light_on = TRUE - block_tele = TRUE // Will be used for dungeons so this is needed to stop cheesing with handteles. - -/turf/simulated/shuttle/wall/alien/Initialize() - . = ..() - update_light() - -/turf/simulated/shuttle/wall/alien/hard_corner - name = "hardcorner wall" - icon_state = "alien-hc" - hard_corner = 1 - -/turf/simulated/shuttle/wall/alien/no_join - name = "nojoin wall" - icon_state = "alien-nj" - join_group = null - -/turf/simulated/shuttle/wall/Initialize() - . = ..() - - //To allow mappers to rename shuttle walls to like "redfloor interior" or whatever for ease of use. - name = true_name - - if(join_group) - auto_join() - else - icon_state = base_state - - if(takes_underlays) - underlay_update() - -/turf/simulated/shuttle/wall/proc/auto_join() - match_turf(NORTH, NORTH) - match_turf(EAST, EAST) - match_turf(SOUTH, SOUTH) - match_turf(WEST, WEST) - - icon_state = "[base_state][join_flags]" - if(isDiagonal(join_flags)) - if(hard_corner) //You are using 'hard' (aka full-tile) corners. - icon_state += "h" //Hard corners have 'h' at the end of the state - else //Diagonals need an underlay to not look ugly. - takes_underlays = 1 - else //Everything else doesn't deserve our time! - takes_underlays = initial(takes_underlays) - - return join_flags - -/turf/simulated/shuttle/wall/proc/match_turf(direction, flag, mask=0) - if((join_flags & mask) == mask) - var/turf/simulated/shuttle/wall/adj = get_step(src, direction) - if(istype(adj, /turf/simulated/shuttle/wall) && adj.join_group == src.join_group) - join_flags |= flag // turn on the bit flag - return - - else if(istype(adj, /turf/simulated/shuttle)) - var/turf/simulated/shuttle/adj_cast = adj - if(adj_cast.join_group == src.join_group) - var/found - for(var/E in join_carefully) - found = locate(E) in adj - if(found) break - if(found) - join_flags |= flag // turn on the bit flag - return - - var/always_found - for(var/E in join_always) - always_found = locate(E) in adj - if(always_found) break - if(always_found) - join_flags |= flag // turn on the bit flag - else - join_flags &= ~flag // turn off the bit flag - -/turf/simulated/shuttle/wall/voidcraft - name = "voidcraft wall" - icon = 'icons/turf/shuttle_void.dmi' - icon_state = "void" - base_state = "void" - var/stripe_color = null // If set, generates a colored stripe overlay. Accepts #XXXXXX as input. - -/turf/simulated/shuttle/wall/voidcraft/hard_corner - name = "hardcorner wall" - icon_state = "void-hc" - hard_corner = 1 - -/turf/simulated/shuttle/wall/voidcraft/no_join - name = "nojoin wall" - icon_state = "void-nj" - join_group = null - -/turf/simulated/shuttle/wall/voidcraft/red - stripe_color = "#FF0000" - -/turf/simulated/shuttle/wall/voidcraft/blue - stripe_color = "#0000FF" - -/turf/simulated/shuttle/wall/voidcraft/green - stripe_color = "#00FF00" - -/turf/simulated/shuttle/wall/voidcraft/Initialize() - . = ..() - update_icon() - -/turf/simulated/shuttle/wall/voidcraft/update_icon() - if(stripe_color) - cut_overlays() - var/image/I = image(icon = src.icon, icon_state = "o_[icon_state]") - I.color = stripe_color - add_overlay(I) - -// Fake corners for making hulls look pretty -/obj/structure/hull_corner - name = "hull corner" - plane = OBJ_PLANE - 1 - icon = 'icons/turf/wall_masks.dmi' - icon_state = "hull_corner" - - anchored = TRUE - density = TRUE - breakable = TRUE - -/obj/structure/hull_corner/Initialize() - return INITIALIZE_HINT_LATELOAD - -/obj/structure/hull_corner/LateInitialize() - . = ..() - update_look() - -/obj/structure/hull_corner/proc/get_dirs_to_test() - return list(dir, turn(dir,90)) - -/obj/structure/hull_corner/proc/update_look() - cut_overlays() - - var/turf/simulated/wall/T - for(var/direction in get_dirs_to_test()) - T = get_step(src, direction) - if(!istype(T) || T.material?.icon_base != "hull") - continue - - name = T.name - desc = T.desc - - var/datum/material/B = T.material - var/datum/material/R = T.reinf_material - - if(B?.icon_colour) - color = B.icon_colour - if(R?.icon_colour) - var/image/I = image(icon, icon_state+"_reinf", dir=dir) - I.color = R.icon_colour - add_overlay(I) - break - - if(!T) - warning("Hull corner at [x],[y] not placed adjacent to a hull it can find.") - -/obj/structure/hull_corner/long_vert - icon = 'icons/turf/wall_masks32x64.dmi' - bound_height = 64 - -/obj/structure/hull_corner/long_vert/get_dirs_to_test() - return list(dir, turn(dir,90), turn(dir,-90)) - -/obj/structure/hull_corner/long_horiz - icon = 'icons/turf/wall_masks64x32.dmi' - bound_width = 64 - -/obj/structure/hull_corner/long_horiz/get_dirs_to_test() - return list(dir, turn(dir,90), turn(dir,-90)) - - - -// Eris walls -/turf/simulated/wall/eris - icon = 'icons/turf/wall_masks_eris.dmi' - icon_state = "generic" - wall_masks = 'icons/turf/wall_masks_eris.dmi' - var/list/blend_objects = list(/obj/machinery/door) - var/list/noblend_objects = list(/obj/machinery/door/window, /obj/machinery/door/firedoor) - -/turf/simulated/wall/eris/can_join_with_low_wall(var/obj/structure/low_wall/WF) - return istype(WF, /obj/structure/low_wall/eris) - -/turf/simulated/wall/eris/special_wall_connections(list/dirs, list/inrange) - ..() - for(var/direction in cardinal) - var/turf/T = get_step(src, direction) - var/decided_to_blend = FALSE - blend_obj_loop: - for(var/obj/O in T) - for(var/b_type in blend_objects) - if(istype(O, b_type)) - decided_to_blend = TRUE - for(var/obj/structure/S in T) - if(istype(S, src)) - decided_to_blend = FALSE - for(var/nb_type in noblend_objects) - if(istype(O, nb_type)) - decided_to_blend = FALSE - - if(decided_to_blend) - dirs += direction - break blend_obj_loop // breaks outer loop - -/turf/simulated/wall/eris/r_wall - icon_state = "rgeneric" -/turf/simulated/wall/eris/r_wall/Initialize(mapload) - . = ..(mapload, "plasteel","plasteel") - -// Bay walls -/turf/simulated/wall/bay - icon = 'icons/turf/wall_masks_bay.dmi' - icon_state = "generic" - wall_masks = 'icons/turf/wall_masks_bay.dmi' - var/list/blend_objects = list(/obj/machinery/door) - var/list/noblend_objects = list(/obj/machinery/door/window, /obj/machinery/door/firedoor) - - var/stripe_color // Adds a colored stripe to the walls - -/turf/simulated/wall/bay/can_join_with_low_wall(var/obj/structure/low_wall/WF) - return istype(WF, /obj/structure/low_wall/bay) - -/turf/simulated/wall/bay/update_icon() - . = ..() - if(stripe_color) - var/image/I - for(var/i = 1 to 4) - I = image(wall_masks, "stripe[wall_connections[i]]", dir = 1<<(i-1)) - I.color = stripe_color - add_overlay(I) - -/turf/simulated/wall/bay/special_wall_connections(list/dirs, list/inrange) - ..() - for(var/direction in cardinal) - var/turf/T = get_step(src, direction) - var/decided_to_blend = FALSE - blend_obj_loop: - for(var/obj/O in T) - for(var/b_type in blend_objects) - if(istype(O, b_type)) - decided_to_blend = TRUE - for(var/obj/structure/S in T) - if(istype(S, src)) - decided_to_blend = FALSE - for(var/nb_type in noblend_objects) - if(istype(O, nb_type)) - decided_to_blend = FALSE - - if(decided_to_blend) - dirs += direction - break blend_obj_loop // breaks outer loop - -/turf/simulated/wall/bay/r_wall - icon_state = "rgeneric" -/turf/simulated/wall/bay/r_wall/Initialize(mapload) - . = ..(mapload, "plasteel","plasteel") - -/turf/simulated/wall/tgmc - icon = 'icons/turf/wall_masks_tgmc.dmi' - wall_masks = 'icons/turf/wall_masks_tgmc.dmi' // not really a MASK per-se, I guess - icon_state = "metal0" - - var/list/blend_objects = list(/obj/machinery/door) - var/list/noblend_objects = list(/obj/machinery/door/window, /obj/machinery/door/firedoor) - - var/wall_base_state = "metal" - var/wall_blend_category = "metal" - var/force_icon - var/list/blend_log = list() - var/strict_blending = FALSE - var/diagonal_blending = FALSE - -// *INHALE -/turf/simulated/wall/tgmc/update_icon() - if(!damage_overlays[1]) //list hasn't been populated - generate_overlays() - - cut_overlays() - - if(force_icon) - icon_state = "[wall_base_state][force_icon]" - else - icon_state = "[wall_base_state][wall_connections]" - - if(damage != 0) - var/integrity = material.integrity - if(reinf_material) - integrity += reinf_material.integrity - - var/overlay = round(damage / integrity * damage_overlays.len) + 1 - if(overlay > damage_overlays.len) - overlay = damage_overlays.len - - add_overlay(damage_overlays[overlay]) - -/turf/simulated/wall/tgmc/update_connections(propagate) - if(!material) - return - var/dirs = 0 - var/list_to_use = diagonal_blending ? alldirs : cardinal - main_direction_loop: - for(var/direction in list_to_use) - var/turf/simulated/wall/tgmc/W = get_step(src, direction) - if(strict_blending) - if(istype(W, src)) - dirs |= direction - continue main_direction_loop - - var/decided_to_blend = FALSE - for(var/obj/O in W) - for(var/b_type in blend_objects) - if(istype(O, b_type)) - decided_to_blend = TRUE - for(var/obj/structure/S in W) - if(istype(S, src)) - decided_to_blend = FALSE - for(var/nb_type in noblend_objects) - if(istype(O, nb_type)) - decided_to_blend = FALSE - - if(decided_to_blend) - blend_log += "Blending with [O] at [direction] because special said to" - dirs |= direction - continue main_direction_loop - - for(var/obj/structure/low_wall/WF in W) - if(can_join_with_low_wall(WF)) - dirs |= direction - blend_log += "Blending with [WF] at [get_dir(src, WF)] because can join with that low wall" - continue main_direction_loop - - // Needs to be our type of wall to blend from this point - if(!istype(W)) - continue - if(propagate) - W.update_connections() - W.update_icon() - if(W.wall_blend_category == wall_blend_category) - dirs |= direction - blend_log += "Blending with [W] at [get_dir(src, W)] because blend category is the same" - - wall_connections = dirs - -/turf/simulated/wall/tgmc/can_join_with_low_wall(var/obj/structure/low_wall/WF) - return istype(WF, /obj/structure/low_wall) - -/turf/simulated/wall/tgmc/rwall - icon_state = "rwall0" - wall_base_state = "rwall" - wall_blend_category = "rwall" -/turf/simulated/wall/tgmc/rwall/Initialize(mapload) - . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) - -/turf/simulated/wall/tgmc/gray - icon_state = "gray0" - wall_base_state = "gray" - wall_blend_category = "gray" -/turf/simulated/wall/tgmc/gwall/Initialize(mapload) - . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) - -/turf/simulated/wall/tgmc/darkwall - icon_state = "darkwall0" - wall_base_state = "darkwall" - wall_blend_category = "darkwall" -/turf/simulated/wall/tgmc/darkwall/Initialize(mapload) - . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) -/turf/simulated/wall/tgmc/darkwall/deco0 - icon_state = "darkwall_deco0" - force_icon = "_deco0" -/turf/simulated/wall/tgmc/darkwall/deco1 - icon_state = "darkwall_deco1" - force_icon = "_deco1" -/turf/simulated/wall/tgmc/darkwall/deco2 - icon_state = "darkwall_deco2" - force_icon = "_deco2" -/turf/simulated/wall/tgmc/darkwall/deco3 - icon_state = "darkwall_deco3" - force_icon = "_deco3" - -/turf/simulated/wall/tgmc/whitewall - icon_state = "white0" - wall_base_state = "white" - wall_blend_category = "white" -/turf/simulated/wall/tgmc/whitewall/Initialize(mapload) - . = ..(mapload, MAT_STEEL,MAT_PLASTIC) - -/turf/simulated/wall/tgmc/durawall - icon_state = "darkband0" - wall_base_state = "darkband" - wall_blend_category = "darkband" -/turf/simulated/wall/tgmc/durawall/Initialize(mapload) - . = ..(mapload, MAT_DURASTEEL,MAT_DURASTEEL) -/turf/simulated/wall/tgmc/durawall/deco0 - icon_state = "darkband_deco0" - force_icon = "_deco0" -/turf/simulated/wall/tgmc/durawall/deco1 - icon_state = "darkband_deco1" - force_icon = "_deco1" -/turf/simulated/wall/tgmc/durawall/deco2 - icon_state = "darkband_deco2" - force_icon = "_deco2" -/turf/simulated/wall/tgmc/durawall/deco3 - icon_state = "darkband_deco3" - force_icon = "_deco3" - -/turf/simulated/wall/tgmc/sanitary - icon_state = "whiteband0" - wall_base_state = "whiteband" - wall_blend_category = "whiteband" -/turf/simulated/wall/tgmc/sanitary/Initialize(mapload) - . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) - -/turf/simulated/wall/tgmc/chigusa - icon_state = "chigusa0" - wall_base_state = "chigusa" - wall_blend_category = "chigusa" -/turf/simulated/wall/tgmc/chigusa/Initialize(mapload) - . = ..(mapload, MAT_CHITIN,MAT_CHITIN) -/turf/simulated/wall/tgmc/chigusa/deco0 - icon_state = "chigusa_deco0" - force_icon = "_deco0" -/turf/simulated/wall/tgmc/chigusa/deco1 - icon_state = "chigusa_deco1" - force_icon = "_deco1" -/turf/simulated/wall/tgmc/chigusa/deco2 - icon_state = "chigusa_deco2" - force_icon = "_deco2" - -/turf/simulated/wall/tgmc/redstripe - icon_state = "redstripe0" - wall_base_state = "redstripe" - wall_blend_category = "redstripe" -/turf/simulated/wall/tgmc/redstripe/Initialize(mapload) - . = ..(mapload, MAT_PLASTEELHULL,MAT_PLASTEELHULL) - -/turf/simulated/wall/tgmc/redstripe_r - icon_state = "redstriper0" - wall_base_state = "redstriper" - wall_blend_category = "redstriper" -/turf/simulated/wall/tgmc/redstripe_r/Initialize(mapload) - . = ..(mapload, MAT_DURASTEELHULL,MAT_DURASTEELHULL) - -/turf/simulated/wall/tgmc/plain_redstripe - icon_state = "predstripe0" - wall_base_state = "predstripe" - wall_blend_category = "predstripe" -/turf/simulated/wall/tgmc/plain_redstripe/Initialize(mapload) - . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) - -/turf/simulated/wall/tgmc/plain_redstripe_r - icon_state = "predstriper0" - wall_base_state = "predstriper" - wall_blend_category = "predstriper" -/turf/simulated/wall/tgmc/plain_redstripe_r/Initialize(mapload) - . = ..(mapload, MAT_DURASTEEL,MAT_DURASTEEL) - -#define WINDOW_GLASS 0x1 -#define WINDOW_RGLASS 0x2 -/turf/simulated/wall/tgmc/window - icon = 'icons/turf/wall_masks_tgmc_win.dmi' - wall_masks = 'icons/turf/wall_masks_tgmc_win.dmi' // not really a MASK per-se, I guess - icon_state = "metal_window0" - wall_base_state = "metal_window" - wall_blend_category = "metal" - - opacity = 0 - var/window_types = WINDOW_GLASS - strict_blending = TRUE - diagonal_blending = TRUE - -/turf/simulated/wall/tgmc/window/rwall - icon_state = "rwall_window0" - wall_base_state = "rwall_window" - wall_blend_category = "rwall" - window_types = WINDOW_RGLASS - -/turf/simulated/wall/tgmc/window/rwall - icon_state = "rwall_rwindow0" - wall_base_state = "rwall_rwindow" - wall_blend_category = "rwall" - window_types = WINDOW_RGLASS - -/turf/simulated/wall/tgmc/window/gray - icon_state = "gray_window0" - wall_base_state = "gray_window" - wall_blend_category = "gray" - window_types = WINDOW_GLASS|WINDOW_RGLASS - -/turf/simulated/wall/tgmc/window/gray/reinf - icon_state = "gray_rwindow0" - wall_base_state = "gray_rwindow" - -/turf/simulated/wall/tgmc/window/white - icon_state = "white_window0" - wall_base_state = "white_window" - wall_blend_category = "white" - window_types = WINDOW_GLASS|WINDOW_RGLASS - diagonal_blending = FALSE - -/turf/simulated/wall/tgmc/window/white/reinf - icon_state = "white_rwindow0" - wall_base_state = "white_rwindow" - -/turf/simulated/wall/tgmc/window/chigusa - icon_state = "chigusa_rwindow0" - wall_base_state = "chigusa_rwindow" - wall_blend_category = "chigusa" - window_types = WINDOW_RGLASS - diagonal_blending = FALSE - -/turf/simulated/wall/tgmc/window/redstripe_r - icon_state = "predstriper_window0" - wall_base_state = "predstriper_window" - wall_blend_category = "predstriper" - window_types = WINDOW_GLASS|WINDOW_RGLASS - -/turf/simulated/wall/tgmc/window/redstripe_r/reinf - icon_state = "predstriper_rwindow0" - wall_base_state = "predstriper_rwindow" - wall_blend_category = "predstriper" - -/turf/simulated/wall/tgmc/window/darkwall - icon_state = "darkwall_window0" - wall_base_state = "darkwall_window" - wall_blend_category = "darkwall" - window_types = WINDOW_GLASS|WINDOW_RGLASS - diagonal_blending = FALSE - -/turf/simulated/wall/tgmc/window/darkwall/reinf - icon_state = "darkwall_rwindow0" - wall_base_state = "darkwall_rwindow" - -#undef WINDOW_GLASS -#undef WINDOW_RGLASS +/turf/simulated/wall/r_wall + icon_state = "rgeneric" +/turf/simulated/wall/r_wall/Initialize(mapload) + . = ..(mapload, "plasteel","plasteel") //3strong + +/turf/simulated/wall/shull + icon_state = "hull-steel" +/turf/simulated/wall/shull/Initialize(mapload) //Spaaaace ship. + . = ..(mapload, MAT_STEELHULL, null, MAT_STEELHULL) +/turf/simulated/wall/rshull + icon_state = "hull-r_steel" +/turf/simulated/wall/rshull/Initialize(mapload) + . = ..(mapload, MAT_STEELHULL, MAT_STEELHULL, MAT_STEELHULL) +/turf/simulated/wall/pshull + icon_state = "hull-plasteel" +/turf/simulated/wall/pshull/Initialize(mapload) //Spaaaace-er ship. + . = ..(mapload, MAT_PLASTEELHULL, null, MAT_PLASTEELHULL) +/turf/simulated/wall/rpshull + icon_state = "hull-r_plasteel" +/turf/simulated/wall/rpshull/Initialize(mapload) + . = ..(mapload, MAT_PLASTEELHULL, MAT_PLASTEELHULL, MAT_PLASTEELHULL) +/turf/simulated/wall/dshull + icon_state = "hull-durasteel" +/turf/simulated/wall/dshull/Initialize(mapload) //Spaaaace-est ship. + . = ..(mapload, MAT_DURASTEELHULL, null, MAT_DURASTEELHULL) +/turf/simulated/wall/rdshull + icon_state = "hull-r_durasteel" +/turf/simulated/wall/rdshull/Initialize(mapload) + . = ..(mapload, MAT_DURASTEELHULL, MAT_DURASTEELHULL, MAT_DURASTEELHULL) +/turf/simulated/wall/thull + icon_state = "hull-titanium" +/turf/simulated/wall/thull/Initialize(mapload) + . = ..(mapload, MAT_TITANIUMHULL, null, MAT_TITANIUMHULL) +/turf/simulated/wall/rthull + icon_state = "hull-r_titanium" +/turf/simulated/wall/rthull/Initialize(mapload) + . = ..(mapload, MAT_TITANIUMHULL, MAT_TITANIUMHULL, MAT_TITANIUMHULL) + +/turf/simulated/wall/cult + icon_state = "cult" +/turf/simulated/wall/cult/Initialize(mapload) + . = ..(mapload, "cult","cult2","cult") +/turf/unsimulated/wall/cult + name = "cult wall" + desc = "Hideous images dance beneath the surface." + icon = 'icons/turf/wall_masks.dmi' + icon_state = "cult" + +/turf/simulated/wall/iron/Initialize(mapload) + . = ..(mapload, "iron") +/turf/simulated/wall/uranium/Initialize(mapload) + . = ..(mapload, "uranium") +/turf/simulated/wall/diamond/Initialize(mapload) + . = ..(mapload, "diamond") +/turf/simulated/wall/gold/Initialize(mapload) + . = ..(mapload, "gold") +/turf/simulated/wall/silver/Initialize(mapload) + . = ..(mapload, "silver") +/turf/simulated/wall/lead/Initialize(mapload) + . = ..(mapload, "lead") +/turf/simulated/wall/r_lead/Initialize(mapload) + . = ..(mapload, "lead", "lead") +/turf/simulated/wall/phoron/Initialize(mapload) + . = ..(mapload, "phoron") +/turf/simulated/wall/sandstone/Initialize(mapload) + . = ..(mapload, "sandstone") +/turf/simulated/wall/ironphoron/Initialize(mapload) + . = ..(mapload, "iron","phoron") +/turf/simulated/wall/golddiamond/Initialize(mapload) + . = ..(mapload, "gold","diamond") +/turf/simulated/wall/silvergold/Initialize(mapload) + . = ..(mapload, "silver","gold") +/turf/simulated/wall/sandstonediamond/Initialize(mapload) + . = ..(mapload, "sandstone","diamond") +/turf/simulated/wall/snowbrick/Initialize(mapload) + . = ..(mapload, "packed snow") + +/turf/simulated/wall/resin/Initialize(mapload) + . = ..(mapload, "resin",null,"resin") + +/turf/simulated/wall/concrete + icon_state = "brick" + +/turf/simulated/wall/concrete/Initialize(mapload) + . = ..(mapload, "concrete") //3strong + +/turf/simulated/wall/r_concrete + icon_state = "rbrick" + +/turf/simulated/wall/r_concrete/Initialize(mapload) + . = ..(mapload, "concrete","plasteel rebar") //3strong + +// Kind of wondering if this is going to bite me in the butt. +/turf/simulated/wall/skipjack/Initialize(mapload) + . = ..(mapload, "alienalloy") +/turf/simulated/wall/skipjack/attackby() + return +/turf/simulated/wall/titanium/Initialize(mapload) + . = ..(mapload, "titanium") + +/turf/simulated/wall/durasteel/Initialize(mapload) + . = ..(mapload, "durasteel", "durasteel") + +/turf/simulated/wall/wood/Initialize(mapload) + . = ..(mapload, MAT_WOOD) + +/turf/simulated/wall/hardwood/Initialize(mapload) + . = ..(mapload, MAT_HARDWOOD) + +/turf/simulated/wall/sifwood/Initialize(mapload) + . = ..(mapload, MAT_SIFWOOD) + +/turf/simulated/wall/log/Initialize(mapload) + . = ..(mapload, MAT_LOG) + +/turf/simulated/wall/log_sif/Initialize(mapload) + . = ..(mapload, MAT_SIFLOG) + +// Shuttle Walls +/turf/simulated/shuttle/wall + name = "autojoin wall" + icon_state = "light" + opacity = 1 + density = TRUE + blocks_air = 1 + + var/base_state = "light" //The base iconstate to base sprites on + var/hard_corner = 0 //Forces hard corners (as opposed to diagonals) + var/true_name = "wall" //What to rename this to on init + + //Extra things this will try to locate and act like we're joining to. You can put doors, or whatever. + //Carefully means only if it's on a /turf/simulated/shuttle subtype turf. + var/static/list/join_carefully = list( + /obj/structure/grille, + /obj/machinery/door/blast/regular + ) + var/static/list/join_always = list( + /obj/structure/shuttle/engine, + /obj/structure/shuttle/window, + /obj/machinery/door/airlock/voidcraft + ) + +/turf/simulated/shuttle/wall/hard_corner + name = "hardcorner wall" + icon_state = "light-hc" + hard_corner = 1 + +/turf/simulated/shuttle/wall/no_join + icon_state = "light-nj" + join_group = null + +/turf/simulated/shuttle/wall/dark + icon = 'icons/turf/shuttle_dark.dmi' + icon_state = "dark" + base_state = "dark" + +/turf/simulated/shuttle/wall/dark/hard_corner + name = "hardcorner wall" + icon_state = "dark-hc" + hard_corner = 1 + +/turf/simulated/shuttle/wall/dark/no_join + name = "nojoin wall" + icon_state = "dark-nj" + join_group = null + +/turf/simulated/shuttle/wall/alien + icon = 'icons/turf/shuttle_alien.dmi' + icon_state = "alien" + base_state = "alien" + light_range = 3 + light_power = 0.75 + light_color = "#ff0066" // Pink-ish + light_on = TRUE + block_tele = TRUE // Will be used for dungeons so this is needed to stop cheesing with handteles. + +/turf/simulated/shuttle/wall/alien/Initialize() + . = ..() + update_light() + +/turf/simulated/shuttle/wall/alien/hard_corner + name = "hardcorner wall" + icon_state = "alien-hc" + hard_corner = 1 + +/turf/simulated/shuttle/wall/alien/no_join + name = "nojoin wall" + icon_state = "alien-nj" + join_group = null + +/turf/simulated/shuttle/wall/Initialize() + . = ..() + + //To allow mappers to rename shuttle walls to like "redfloor interior" or whatever for ease of use. + name = true_name + + if(join_group) + auto_join() + else + icon_state = base_state + + if(takes_underlays) + underlay_update() + +/turf/simulated/shuttle/wall/proc/auto_join() + match_turf(NORTH, NORTH) + match_turf(EAST, EAST) + match_turf(SOUTH, SOUTH) + match_turf(WEST, WEST) + + icon_state = "[base_state][join_flags]" + if(isDiagonal(join_flags)) + if(hard_corner) //You are using 'hard' (aka full-tile) corners. + icon_state += "h" //Hard corners have 'h' at the end of the state + else //Diagonals need an underlay to not look ugly. + takes_underlays = 1 + else //Everything else doesn't deserve our time! + takes_underlays = initial(takes_underlays) + + return join_flags + +/turf/simulated/shuttle/wall/proc/match_turf(direction, flag, mask=0) + if((join_flags & mask) == mask) + var/turf/simulated/shuttle/wall/adj = get_step(src, direction) + if(istype(adj, /turf/simulated/shuttle/wall) && adj.join_group == src.join_group) + join_flags |= flag // turn on the bit flag + return + + else if(istype(adj, /turf/simulated/shuttle)) + var/turf/simulated/shuttle/adj_cast = adj + if(adj_cast.join_group == src.join_group) + var/found + for(var/E in join_carefully) + found = locate(E) in adj + if(found) break + if(found) + join_flags |= flag // turn on the bit flag + return + + var/always_found + for(var/E in join_always) + always_found = locate(E) in adj + if(always_found) break + if(always_found) + join_flags |= flag // turn on the bit flag + else + join_flags &= ~flag // turn off the bit flag + +/turf/simulated/shuttle/wall/voidcraft + name = "voidcraft wall" + icon = 'icons/turf/shuttle_void.dmi' + icon_state = "void" + base_state = "void" + var/stripe_color = null // If set, generates a colored stripe overlay. Accepts #XXXXXX as input. + +/turf/simulated/shuttle/wall/voidcraft/hard_corner + name = "hardcorner wall" + icon_state = "void-hc" + hard_corner = 1 + +/turf/simulated/shuttle/wall/voidcraft/no_join + name = "nojoin wall" + icon_state = "void-nj" + join_group = null + +/turf/simulated/shuttle/wall/voidcraft/red + stripe_color = "#FF0000" + +/turf/simulated/shuttle/wall/voidcraft/blue + stripe_color = "#0000FF" + +/turf/simulated/shuttle/wall/voidcraft/green + stripe_color = "#00FF00" + +/turf/simulated/shuttle/wall/voidcraft/Initialize() + . = ..() + update_icon() + +/turf/simulated/shuttle/wall/voidcraft/update_icon() + if(stripe_color) + cut_overlays() + var/image/I = image(icon = src.icon, icon_state = "o_[icon_state]") + I.color = stripe_color + add_overlay(I) + +// Fake corners for making hulls look pretty +/obj/structure/hull_corner + name = "hull corner" + plane = OBJ_PLANE - 1 + icon = 'icons/turf/wall_masks.dmi' + icon_state = "hull_corner" + + anchored = TRUE + density = TRUE + breakable = TRUE + +/obj/structure/hull_corner/Initialize() + return INITIALIZE_HINT_LATELOAD + +/obj/structure/hull_corner/LateInitialize() + . = ..() + update_look() + +/obj/structure/hull_corner/proc/get_dirs_to_test() + return list(dir, turn(dir,90)) + +/obj/structure/hull_corner/proc/update_look() + cut_overlays() + + var/turf/simulated/wall/T + for(var/direction in get_dirs_to_test()) + T = get_step(src, direction) + if(!istype(T) || T.material?.icon_base != "hull") + continue + + name = T.name + desc = T.desc + + var/datum/material/B = T.material + var/datum/material/R = T.reinf_material + + if(B?.icon_colour) + color = B.icon_colour + if(R?.icon_colour) + var/image/I = image(icon, icon_state+"_reinf", dir=dir) + I.color = R.icon_colour + add_overlay(I) + break + + if(!T) + warning("Hull corner at [x],[y] not placed adjacent to a hull it can find.") + +/obj/structure/hull_corner/long_vert + icon = 'icons/turf/wall_masks32x64.dmi' + bound_height = 64 + +/obj/structure/hull_corner/long_vert/get_dirs_to_test() + return list(dir, turn(dir,90), turn(dir,-90)) + +/obj/structure/hull_corner/long_horiz + icon = 'icons/turf/wall_masks64x32.dmi' + bound_width = 64 + +/obj/structure/hull_corner/long_horiz/get_dirs_to_test() + return list(dir, turn(dir,90), turn(dir,-90)) + + + +// Eris walls +/turf/simulated/wall/eris + icon = 'icons/turf/wall_masks_eris.dmi' + icon_state = "generic" + wall_masks = 'icons/turf/wall_masks_eris.dmi' + var/list/blend_objects = list(/obj/machinery/door) + var/list/noblend_objects = list(/obj/machinery/door/window, /obj/machinery/door/firedoor) + +/turf/simulated/wall/eris/can_join_with_low_wall(var/obj/structure/low_wall/WF) + return istype(WF, /obj/structure/low_wall/eris) + +/turf/simulated/wall/eris/special_wall_connections(list/dirs, list/inrange) + ..() + for(var/direction in cardinal) + var/turf/T = get_step(src, direction) + var/decided_to_blend = FALSE + blend_obj_loop: + for(var/obj/O in T) + for(var/b_type in blend_objects) + if(istype(O, b_type)) + decided_to_blend = TRUE + for(var/obj/structure/S in T) + if(istype(S, src)) + decided_to_blend = FALSE + for(var/nb_type in noblend_objects) + if(istype(O, nb_type)) + decided_to_blend = FALSE + + if(decided_to_blend) + dirs += direction + break blend_obj_loop // breaks outer loop + +/turf/simulated/wall/eris/r_wall + icon_state = "rgeneric" +/turf/simulated/wall/eris/r_wall/Initialize(mapload) + . = ..(mapload, "plasteel","plasteel") + +// Bay walls +/turf/simulated/wall/bay + icon = 'icons/turf/wall_masks_bay.dmi' + icon_state = "generic" + wall_masks = 'icons/turf/wall_masks_bay.dmi' + var/list/blend_objects = list(/obj/machinery/door) + var/list/noblend_objects = list(/obj/machinery/door/window, /obj/machinery/door/firedoor) + + var/stripe_color // Adds a colored stripe to the walls + +/turf/simulated/wall/bay/can_join_with_low_wall(var/obj/structure/low_wall/WF) + return istype(WF, /obj/structure/low_wall/bay) + +/turf/simulated/wall/bay/update_icon() + . = ..() + if(stripe_color) + var/image/I + for(var/i = 1 to 4) + I = image(wall_masks, "stripe[wall_connections[i]]", dir = 1<<(i-1)) + I.color = stripe_color + add_overlay(I) + +/turf/simulated/wall/bay/special_wall_connections(list/dirs, list/inrange) + ..() + for(var/direction in cardinal) + var/turf/T = get_step(src, direction) + var/decided_to_blend = FALSE + blend_obj_loop: + for(var/obj/O in T) + for(var/b_type in blend_objects) + if(istype(O, b_type)) + decided_to_blend = TRUE + for(var/obj/structure/S in T) + if(istype(S, src)) + decided_to_blend = FALSE + for(var/nb_type in noblend_objects) + if(istype(O, nb_type)) + decided_to_blend = FALSE + + if(decided_to_blend) + dirs += direction + break blend_obj_loop // breaks outer loop + +/turf/simulated/wall/bay/r_wall + icon_state = "rgeneric" +/turf/simulated/wall/bay/r_wall/Initialize(mapload) + . = ..(mapload, "plasteel","plasteel") + +/turf/simulated/wall/tgmc + icon = 'icons/turf/wall_masks_tgmc.dmi' + wall_masks = 'icons/turf/wall_masks_tgmc.dmi' // not really a MASK per-se, I guess + icon_state = "metal0" + + var/list/blend_objects = list(/obj/machinery/door) + var/list/noblend_objects = list(/obj/machinery/door/window, /obj/machinery/door/firedoor) + + var/wall_base_state = "metal" + var/wall_blend_category = "metal" + var/force_icon + var/list/blend_log = list() + var/strict_blending = FALSE + var/diagonal_blending = FALSE + +// *INHALE +/turf/simulated/wall/tgmc/update_icon() + if(!damage_overlays[1]) //list hasn't been populated + generate_overlays() + + cut_overlays() + + if(force_icon) + icon_state = "[wall_base_state][force_icon]" + else + icon_state = "[wall_base_state][wall_connections]" + + if(damage != 0) + var/integrity = material.integrity + if(reinf_material) + integrity += reinf_material.integrity + + var/overlay = round(damage / integrity * damage_overlays.len) + 1 + if(overlay > damage_overlays.len) + overlay = damage_overlays.len + + add_overlay(damage_overlays[overlay]) + +/turf/simulated/wall/tgmc/update_connections(propagate) + if(!material) + return + var/dirs = 0 + var/list_to_use = diagonal_blending ? alldirs : cardinal + main_direction_loop: + for(var/direction in list_to_use) + var/turf/simulated/wall/tgmc/W = get_step(src, direction) + if(strict_blending) + if(istype(W, src)) + dirs |= direction + continue main_direction_loop + + var/decided_to_blend = FALSE + for(var/obj/O in W) + for(var/b_type in blend_objects) + if(istype(O, b_type)) + decided_to_blend = TRUE + for(var/obj/structure/S in W) + if(istype(S, src)) + decided_to_blend = FALSE + for(var/nb_type in noblend_objects) + if(istype(O, nb_type)) + decided_to_blend = FALSE + + if(decided_to_blend) + blend_log += "Blending with [O] at [direction] because special said to" + dirs |= direction + continue main_direction_loop + + for(var/obj/structure/low_wall/WF in W) + if(can_join_with_low_wall(WF)) + dirs |= direction + blend_log += "Blending with [WF] at [get_dir(src, WF)] because can join with that low wall" + continue main_direction_loop + + // Needs to be our type of wall to blend from this point + if(!istype(W)) + continue + if(propagate) + W.update_connections() + W.update_icon() + if(W.wall_blend_category == wall_blend_category) + dirs |= direction + blend_log += "Blending with [W] at [get_dir(src, W)] because blend category is the same" + + wall_connections = dirs + +/turf/simulated/wall/tgmc/can_join_with_low_wall(var/obj/structure/low_wall/WF) + return istype(WF, /obj/structure/low_wall) + +/turf/simulated/wall/tgmc/rwall + icon_state = "rwall0" + wall_base_state = "rwall" + wall_blend_category = "rwall" +/turf/simulated/wall/tgmc/rwall/Initialize(mapload) + . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) + +/turf/simulated/wall/tgmc/gray + icon_state = "gray0" + wall_base_state = "gray" + wall_blend_category = "gray" +/turf/simulated/wall/tgmc/gwall/Initialize(mapload) + . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) + +/turf/simulated/wall/tgmc/darkwall + icon_state = "darkwall0" + wall_base_state = "darkwall" + wall_blend_category = "darkwall" +/turf/simulated/wall/tgmc/darkwall/Initialize(mapload) + . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) +/turf/simulated/wall/tgmc/darkwall/deco0 + icon_state = "darkwall_deco0" + force_icon = "_deco0" +/turf/simulated/wall/tgmc/darkwall/deco1 + icon_state = "darkwall_deco1" + force_icon = "_deco1" +/turf/simulated/wall/tgmc/darkwall/deco2 + icon_state = "darkwall_deco2" + force_icon = "_deco2" +/turf/simulated/wall/tgmc/darkwall/deco3 + icon_state = "darkwall_deco3" + force_icon = "_deco3" + +/turf/simulated/wall/tgmc/whitewall + icon_state = "white0" + wall_base_state = "white" + wall_blend_category = "white" +/turf/simulated/wall/tgmc/whitewall/Initialize(mapload) + . = ..(mapload, MAT_STEEL,MAT_PLASTIC) + +/turf/simulated/wall/tgmc/durawall + icon_state = "darkband0" + wall_base_state = "darkband" + wall_blend_category = "darkband" +/turf/simulated/wall/tgmc/durawall/Initialize(mapload) + . = ..(mapload, MAT_DURASTEEL,MAT_DURASTEEL) +/turf/simulated/wall/tgmc/durawall/deco0 + icon_state = "darkband_deco0" + force_icon = "_deco0" +/turf/simulated/wall/tgmc/durawall/deco1 + icon_state = "darkband_deco1" + force_icon = "_deco1" +/turf/simulated/wall/tgmc/durawall/deco2 + icon_state = "darkband_deco2" + force_icon = "_deco2" +/turf/simulated/wall/tgmc/durawall/deco3 + icon_state = "darkband_deco3" + force_icon = "_deco3" + +/turf/simulated/wall/tgmc/sanitary + icon_state = "whiteband0" + wall_base_state = "whiteband" + wall_blend_category = "whiteband" +/turf/simulated/wall/tgmc/sanitary/Initialize(mapload) + . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) + +/turf/simulated/wall/tgmc/chigusa + icon_state = "chigusa0" + wall_base_state = "chigusa" + wall_blend_category = "chigusa" +/turf/simulated/wall/tgmc/chigusa/Initialize(mapload) + . = ..(mapload, MAT_CHITIN,MAT_CHITIN) +/turf/simulated/wall/tgmc/chigusa/deco0 + icon_state = "chigusa_deco0" + force_icon = "_deco0" +/turf/simulated/wall/tgmc/chigusa/deco1 + icon_state = "chigusa_deco1" + force_icon = "_deco1" +/turf/simulated/wall/tgmc/chigusa/deco2 + icon_state = "chigusa_deco2" + force_icon = "_deco2" + +/turf/simulated/wall/tgmc/redstripe + icon_state = "redstripe0" + wall_base_state = "redstripe" + wall_blend_category = "redstripe" +/turf/simulated/wall/tgmc/redstripe/Initialize(mapload) + . = ..(mapload, MAT_PLASTEELHULL,MAT_PLASTEELHULL) + +/turf/simulated/wall/tgmc/redstripe_r + icon_state = "redstriper0" + wall_base_state = "redstriper" + wall_blend_category = "redstriper" +/turf/simulated/wall/tgmc/redstripe_r/Initialize(mapload) + . = ..(mapload, MAT_DURASTEELHULL,MAT_DURASTEELHULL) + +/turf/simulated/wall/tgmc/plain_redstripe + icon_state = "predstripe0" + wall_base_state = "predstripe" + wall_blend_category = "predstripe" +/turf/simulated/wall/tgmc/plain_redstripe/Initialize(mapload) + . = ..(mapload, MAT_PLASTEEL,MAT_PLASTEEL) + +/turf/simulated/wall/tgmc/plain_redstripe_r + icon_state = "predstriper0" + wall_base_state = "predstriper" + wall_blend_category = "predstriper" +/turf/simulated/wall/tgmc/plain_redstripe_r/Initialize(mapload) + . = ..(mapload, MAT_DURASTEEL,MAT_DURASTEEL) + +#define WINDOW_GLASS 0x1 +#define WINDOW_RGLASS 0x2 +/turf/simulated/wall/tgmc/window + icon = 'icons/turf/wall_masks_tgmc_win.dmi' + wall_masks = 'icons/turf/wall_masks_tgmc_win.dmi' // not really a MASK per-se, I guess + icon_state = "metal_window0" + wall_base_state = "metal_window" + wall_blend_category = "metal" + + opacity = 0 + var/window_types = WINDOW_GLASS + strict_blending = TRUE + diagonal_blending = TRUE + +/turf/simulated/wall/tgmc/window/rwall + icon_state = "rwall_window0" + wall_base_state = "rwall_window" + wall_blend_category = "rwall" + window_types = WINDOW_RGLASS + +/turf/simulated/wall/tgmc/window/rwall + icon_state = "rwall_rwindow0" + wall_base_state = "rwall_rwindow" + wall_blend_category = "rwall" + window_types = WINDOW_RGLASS + +/turf/simulated/wall/tgmc/window/gray + icon_state = "gray_window0" + wall_base_state = "gray_window" + wall_blend_category = "gray" + window_types = WINDOW_GLASS|WINDOW_RGLASS + +/turf/simulated/wall/tgmc/window/gray/reinf + icon_state = "gray_rwindow0" + wall_base_state = "gray_rwindow" + +/turf/simulated/wall/tgmc/window/white + icon_state = "white_window0" + wall_base_state = "white_window" + wall_blend_category = "white" + window_types = WINDOW_GLASS|WINDOW_RGLASS + diagonal_blending = FALSE + +/turf/simulated/wall/tgmc/window/white/reinf + icon_state = "white_rwindow0" + wall_base_state = "white_rwindow" + +/turf/simulated/wall/tgmc/window/chigusa + icon_state = "chigusa_rwindow0" + wall_base_state = "chigusa_rwindow" + wall_blend_category = "chigusa" + window_types = WINDOW_RGLASS + diagonal_blending = FALSE + +/turf/simulated/wall/tgmc/window/redstripe_r + icon_state = "predstriper_window0" + wall_base_state = "predstriper_window" + wall_blend_category = "predstriper" + window_types = WINDOW_GLASS|WINDOW_RGLASS + +/turf/simulated/wall/tgmc/window/redstripe_r/reinf + icon_state = "predstriper_rwindow0" + wall_base_state = "predstriper_rwindow" + wall_blend_category = "predstriper" + +/turf/simulated/wall/tgmc/window/darkwall + icon_state = "darkwall_window0" + wall_base_state = "darkwall_window" + wall_blend_category = "darkwall" + window_types = WINDOW_GLASS|WINDOW_RGLASS + diagonal_blending = FALSE + +/turf/simulated/wall/tgmc/window/darkwall/reinf + icon_state = "darkwall_rwindow0" + wall_base_state = "darkwall_rwindow" + +#undef WINDOW_GLASS +#undef WINDOW_RGLASS diff --git a/code/game/turfs/simulated/wall_types_vr.dm b/code/game/turfs/simulated/wall_types_vr.dm index b25e6d6f9d8..976fe96c8fc 100644 --- a/code/game/turfs/simulated/wall_types_vr.dm +++ b/code/game/turfs/simulated/wall_types_vr.dm @@ -223,3 +223,17 @@ var/list/flesh_overlay_cache = list() /turf/simulated/wall/wood icon_state = "wood" icon = 'icons/turf/wall_masks_vr.dmi' + +/turf/simulated/wall/stonebricks + icon_state = "stonebrick" + icon = 'icons/turf/wall_masks_vr.dmi' + +/turf/simulated/wall/stonebricks/Initialize(mapload) + . = ..(mapload, "concrete") + +/turf/simulated/wall/stonelogs + icon_state = "stonelogs" + icon = 'icons/turf/wall_masks_vr.dmi' + +/turf/simulated/wall/stonelogs/Initialize(mapload) + . = ..(mapload, "concrete",MAT_LOG) \ No newline at end of file diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 0c34354accc..52cedfdbfd5 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -1,341 +1,341 @@ -/turf/simulated/wall - name = "wall" - desc = "A huge chunk of metal used to separate rooms." - icon = 'icons/turf/wall_masks.dmi' - icon_state = "generic" - opacity = 1 - density = TRUE - blocks_air = 1 - thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall - - var/icon/wall_masks = 'icons/turf/wall_masks.dmi' - var/damage = 0 - var/damage_overlay = 0 - var/global/damage_overlays[16] - var/active - var/can_open = 0 - var/datum/material/girder_material - var/datum/material/material - var/datum/material/reinf_material - var/last_state - var/construction_stage - - // There's basically always going to be wall connections, making this lazy doesn't seem like it'd help much unless you wanted to make it bitflags instead. - var/list/wall_connections = list("0", "0", "0", "0") - -// Walls always hide the stuff below them. -/turf/simulated/wall/levelupdate() - for(var/obj/O in src) - O.hide(1) - -/turf/simulated/wall/Initialize(mapload, materialtype, rmaterialtype, girdertype) - . = ..() - icon_state = "blank" - if(!materialtype) - materialtype = DEFAULT_WALL_MATERIAL - material = get_material_by_name(materialtype) - if(!girdertype) - girdertype = DEFAULT_WALL_MATERIAL - girder_material = get_material_by_name(girdertype) - if(!isnull(rmaterialtype)) - reinf_material = get_material_by_name(rmaterialtype) - update_material() - START_PROCESSING(SSturfs, src) - -/turf/simulated/wall/Destroy() - STOP_PROCESSING(SSturfs, src) - return ..() - -/turf/simulated/wall/examine_icon() - return icon(icon=initial(icon), icon_state=initial(icon_state)) - -/turf/simulated/wall/process() - // Calling parent will kill processing - if(!radiate()) - return PROCESS_KILL - -/turf/simulated/wall/proc/get_material() - return material - -/turf/simulated/wall/bullet_act(var/obj/item/projectile/Proj) - if(istype(Proj,/obj/item/projectile/beam)) - burn(2500) - else if(istype(Proj,/obj/item/projectile/ion)) - burn(500) - - var/proj_damage = Proj.get_structure_damage() - - //cap the amount of damage, so that things like emitters can't destroy walls in one hit. - var/damage = min(proj_damage, 100) - - if(Proj.damage_type == BURN && damage > 0) - if(thermite) - thermitemelt() - - if(istype(Proj,/obj/item/projectile/beam)) - if(material && material.reflectivity >= 0.5) // Time to reflect lasers. - var/new_damage = damage * material.reflectivity - var/outgoing_damage = damage - new_damage - damage = new_damage - Proj.damage = outgoing_damage - - visible_message("\The [src] reflects \the [Proj]!") - - // Find a turf near or on the original location to bounce to - var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - //var/turf/curloc = get_turf(src) - var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) - - Proj.penetrating += 1 // Needed for the beam to get out of the wall. - - // redirect the projectile - Proj.redirect(new_x, new_y, curloc, null) - - take_damage(damage) - return - -/turf/simulated/wall/hitby(AM as mob|obj, var/speed=THROWFORCE_SPEED_DIVISOR) - ..() - if(ismob(AM)) - return - - var/tforce = AM:throwforce * (speed/THROWFORCE_SPEED_DIVISOR) - if (tforce < 15) - return - - take_damage(tforce) - -/turf/simulated/wall/proc/clear_plants() - for(var/obj/effect/overlay/wallrot/WR in src) - qdel(WR) - for(var/obj/effect/plant/plant in range(src, 1)) - if(!plant.floor) //shrooms drop to the floor - plant.floor = 1 - plant.update_icon() - plant.pixel_x = 0 - plant.pixel_y = 0 - plant.update_neighbors() - -/turf/simulated/wall/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) - clear_plants() - ..(N, tell_universe, force_lighting_update, preserve_outdoors) - -//Appearance -/turf/simulated/wall/examine(mob/user) - . = ..() - - if(!damage) - . += "It looks fully intact." - else - var/dam = damage / material.integrity - if(dam <= 0.3) - . += "It looks slightly damaged." - else if(dam <= 0.6) - . += "It looks moderately damaged." - else - . += "It looks heavily damaged." - - if(locate(/obj/effect/overlay/wallrot) in src) - . += "There is fungus growing on [src]." - -//Damage - -/turf/simulated/wall/melt() - - if(!can_melt()) - return - - src.ChangeTurf(/turf/simulated/floor/plating) - - var/turf/simulated/floor/F = src - if(!F) - return - F.burn_tile() - F.icon_state = "wall_thermite" - visible_message("\The [src] spontaneously combusts!.") //!!OH SHIT!! - return - -/turf/simulated/wall/take_damage(dam) - if(dam) - damage = max(0, damage + dam) - update_damage() - return - -/turf/simulated/wall/proc/update_damage() - var/cap = material.integrity - if(reinf_material) - cap += reinf_material.integrity - - if(locate(/obj/effect/overlay/wallrot) in src) - cap = cap / 10 - - if(damage >= cap) - dismantle_wall() - else - update_icon() - - return - -/turf/simulated/wall/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( - burn(exposed_temperature) - -/turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) - burn(adj_temp) - if(adj_temp > material.melting_point) - take_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material.melting_point))) - - return ..() - -/turf/simulated/wall/proc/dismantle_wall(var/devastated, var/explode, var/no_product) - - playsound(src, 'sound/items/Welder.ogg', 100, 1) - if(!no_product) - if(reinf_material) - reinf_material.place_dismantled_girder(src, reinf_material, girder_material) - else - material.place_dismantled_girder(src, null, girder_material) - if(!devastated) - material.place_dismantled_product(src) - if (!reinf_material) - material.place_dismantled_product(src) - - for(var/obj/O in src.contents) //Eject contents! - if(istype(O,/obj/structure/sign/poster)) - var/obj/structure/sign/poster/P = O - P.roll_and_drop(src) - else - O.loc = src - - clear_plants() - material = get_material_by_name("placeholder") - reinf_material = null - girder_material = null - update_connections(1) - - ChangeTurf(/turf/simulated/floor/plating) - -/turf/simulated/wall/ex_act(severity) - switch(severity) - if(1.0) - if(girder_material.explosion_resistance >= 25 && prob(girder_material.explosion_resistance)) - new /obj/structure/girder/displaced(src, girder_material.name) - src.ChangeTurf(get_base_turf_by_area(src)) - if(2.0) - if(prob(75)) - take_damage(rand(150, 250)) - else - dismantle_wall(1,1) - if(3.0) - take_damage(rand(0, 250)) - else - return - -// Wall-rot effect, a nasty fungus that destroys walls. -/turf/simulated/wall/proc/rot() - if(locate(/obj/effect/overlay/wallrot) in src) - return FALSE - - // Wall-rot can't go onto walls that are surrounded in all four cardinal directions. - // Because of spores, or something. It's actually to avoid the pain that is removing wallrot surrounded by - // four r-walls. - var/at_least_one_open_turf = FALSE - for(var/direction in GLOB.cardinal) - var/turf/T = get_step(src, direction) - if(!T.check_density()) - at_least_one_open_turf = TRUE - break - - if(!at_least_one_open_turf) - return FALSE - - var/number_rots = rand(2,3) - for(var/i=0, i= 150 && !girder_material.is_brittle()) //Strong girders will remain in place when a wall is melted. - dismantle_wall(1,1) - else - src.ChangeTurf(/turf/simulated/floor/plating) - - var/turf/simulated/floor/F = src - F.burn_tile() - F.icon_state = "dmg[rand(1,4)]" - to_chat(user, "The thermite starts melting through the wall.") - - spawn(100) - if(O) - qdel(O) -// F.sd_LumReset() //TODO: ~Carn - return - -/turf/simulated/wall/proc/radiate() - var/total_radiation = material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + (girder_material ? girder_material.radioactivity / 2 : 0) - if(!total_radiation) - return - - SSradiation.radiate(src, total_radiation) - return total_radiation - -/turf/simulated/wall/proc/burn(temperature) - if(material.combustion_effect(src, temperature, 0.7)) - spawn(2) - new /obj/structure/girder(src, girder_material.name) - src.ChangeTurf(/turf/simulated/floor) - for(var/turf/simulated/wall/W in range(3,src)) - W.burn((temperature/4)) - for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) - D.ignite(temperature/4) - -/turf/simulated/wall/can_engrave() - return (material && material.hardness >= 10 && material.hardness <= 100) - -/turf/simulated/wall/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - if(material.integrity > 1000) // Don't decon things like elevatorium. - return FALSE - if(reinf_material && !the_rcd.can_remove_rwalls) // Gotta do it the old fashioned way if your RCD can't. - return FALSE - - if(passed_mode == RCD_DECONSTRUCT) - var/delay_to_use = material.integrity / 3 // Steel has 150 integrity, so it'll take five seconds to down a regular wall. - if(reinf_material) - delay_to_use += reinf_material.integrity / 3 - return list( - RCD_VALUE_MODE = RCD_DECONSTRUCT, - RCD_VALUE_DELAY = delay_to_use, - RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 - ) - return FALSE - -/turf/simulated/wall/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - if(passed_mode == RCD_DECONSTRUCT) - to_chat(user, span("notice", "You deconstruct \the [src].")) - ChangeTurf(/turf/simulated/floor/airless, preserve_outdoors = TRUE) - return TRUE - return FALSE - -/turf/simulated/wall/AltClick(mob/user) - if(isliving(user)) - var/mob/living/livingUser = user - if(try_graffiti(livingUser, livingUser.get_active_hand())) - return - . = ..() +/turf/simulated/wall + name = "wall" + desc = "A huge chunk of metal used to separate rooms." + icon = 'icons/turf/wall_masks.dmi' + icon_state = "generic" + opacity = 1 + density = TRUE + blocks_air = 1 + thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall + + var/icon/wall_masks = 'icons/turf/wall_masks.dmi' + var/damage = 0 + var/damage_overlay = 0 + var/global/damage_overlays[16] + var/active + var/can_open = 0 + var/datum/material/girder_material + var/datum/material/material + var/datum/material/reinf_material + var/last_state + var/construction_stage + + // There's basically always going to be wall connections, making this lazy doesn't seem like it'd help much unless you wanted to make it bitflags instead. + var/list/wall_connections = list("0", "0", "0", "0") + +// Walls always hide the stuff below them. +/turf/simulated/wall/levelupdate() + for(var/obj/O in src) + O.hide(1) + +/turf/simulated/wall/Initialize(mapload, materialtype, rmaterialtype, girdertype) + . = ..() + icon_state = "blank" + if(!materialtype) + materialtype = DEFAULT_WALL_MATERIAL + material = get_material_by_name(materialtype) + if(!girdertype) + girdertype = DEFAULT_WALL_MATERIAL + girder_material = get_material_by_name(girdertype) + if(!isnull(rmaterialtype)) + reinf_material = get_material_by_name(rmaterialtype) + update_material() + START_PROCESSING(SSturfs, src) + +/turf/simulated/wall/Destroy() + STOP_PROCESSING(SSturfs, src) + return ..() + +/turf/simulated/wall/examine_icon() + return icon(icon=initial(icon), icon_state=initial(icon_state)) + +/turf/simulated/wall/process() + // Calling parent will kill processing + if(!radiate()) + return PROCESS_KILL + +/turf/simulated/wall/proc/get_material() + return material + +/turf/simulated/wall/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj,/obj/item/projectile/beam)) + burn(2500) + else if(istype(Proj,/obj/item/projectile/ion)) + burn(500) + + var/proj_damage = Proj.get_structure_damage() + + //cap the amount of damage, so that things like emitters can't destroy walls in one hit. + var/damage = min(proj_damage, 100) + + if(Proj.damage_type == BURN && damage > 0) + if(thermite) + thermitemelt() + + if(istype(Proj,/obj/item/projectile/beam)) + if(material && material.reflectivity >= 0.5) // Time to reflect lasers. + var/new_damage = damage * material.reflectivity + var/outgoing_damage = damage - new_damage + damage = new_damage + Proj.damage = outgoing_damage + + visible_message("\The [src] reflects \the [Proj]!") + + // Find a turf near or on the original location to bounce to + var/new_x = Proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = Proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + //var/turf/curloc = get_turf(src) + var/turf/curloc = get_step(src, get_dir(src, Proj.starting)) + + Proj.penetrating += 1 // Needed for the beam to get out of the wall. + + // redirect the projectile + Proj.redirect(new_x, new_y, curloc, null) + + take_damage(damage) + return + +/turf/simulated/wall/hitby(AM as mob|obj, var/speed=THROWFORCE_SPEED_DIVISOR) + ..() + if(ismob(AM)) + return + + var/tforce = AM:throwforce * (speed/THROWFORCE_SPEED_DIVISOR) + if (tforce < 15) + return + + take_damage(tforce) + +/turf/simulated/wall/proc/clear_plants() + for(var/obj/effect/overlay/wallrot/WR in src) + qdel(WR) + for(var/obj/effect/plant/plant in range(src, 1)) + if(!plant.floor) //shrooms drop to the floor + plant.floor = 1 + plant.update_icon() + plant.pixel_x = 0 + plant.pixel_y = 0 + plant.update_neighbors() + +/turf/simulated/wall/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) + clear_plants() + ..(N, tell_universe, force_lighting_update, preserve_outdoors) + +//Appearance +/turf/simulated/wall/examine(mob/user) + . = ..() + + if(!damage) + . += "It looks fully intact." + else + var/dam = damage / material.integrity + if(dam <= 0.3) + . += "It looks slightly damaged." + else if(dam <= 0.6) + . += "It looks moderately damaged." + else + . += "It looks heavily damaged." + + if(locate(/obj/effect/overlay/wallrot) in src) + . += "There is fungus growing on [src]." + +//Damage + +/turf/simulated/wall/melt() + + if(!can_melt()) + return + + src.ChangeTurf(/turf/simulated/floor/plating) + + var/turf/simulated/floor/F = src + if(!F) + return + F.burn_tile() + F.icon_state = "wall_thermite" + visible_message("\The [src] spontaneously combusts!.") //!!OH SHIT!! + return + +/turf/simulated/wall/take_damage(dam) + if(dam) + damage = max(0, damage + dam) + update_damage() + return + +/turf/simulated/wall/proc/update_damage() + var/cap = material.integrity + if(reinf_material) + cap += reinf_material.integrity + + if(locate(/obj/effect/overlay/wallrot) in src) + cap = cap / 10 + + if(damage >= cap) + dismantle_wall() + else + update_icon() + + return + +/turf/simulated/wall/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( + burn(exposed_temperature) + +/turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) + burn(adj_temp) + if(adj_temp > material.melting_point) + take_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material.melting_point))) + + return ..() + +/turf/simulated/wall/proc/dismantle_wall(var/devastated, var/explode, var/no_product) + + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(!no_product) + if(reinf_material) + reinf_material.place_dismantled_girder(src, reinf_material, girder_material) + else + material.place_dismantled_girder(src, null, girder_material) + if(!devastated) + material.place_dismantled_product(src) + if (!reinf_material) + material.place_dismantled_product(src) + + for(var/obj/O in src.contents) //Eject contents! + if(istype(O,/obj/structure/sign/poster)) + var/obj/structure/sign/poster/P = O + P.roll_and_drop(src) + else + O.loc = src + + clear_plants() + material = get_material_by_name("placeholder") + reinf_material = null + girder_material = null + update_connections(1) + + ChangeTurf(/turf/simulated/floor/plating) + +/turf/simulated/wall/ex_act(severity) + switch(severity) + if(1.0) + if(girder_material.explosion_resistance >= 25 && prob(girder_material.explosion_resistance)) + new /obj/structure/girder/displaced(src, girder_material.name) + src.ChangeTurf(get_base_turf_by_area(src)) + if(2.0) + if(prob(75)) + take_damage(rand(150, 250)) + else + dismantle_wall(1,1) + if(3.0) + take_damage(rand(0, 250)) + else + return + +// Wall-rot effect, a nasty fungus that destroys walls. +/turf/simulated/wall/proc/rot() + if(locate(/obj/effect/overlay/wallrot) in src) + return FALSE + + // Wall-rot can't go onto walls that are surrounded in all four cardinal directions. + // Because of spores, or something. It's actually to avoid the pain that is removing wallrot surrounded by + // four r-walls. + var/at_least_one_open_turf = FALSE + for(var/direction in GLOB.cardinal) + var/turf/T = get_step(src, direction) + if(!T.check_density()) + at_least_one_open_turf = TRUE + break + + if(!at_least_one_open_turf) + return FALSE + + var/number_rots = rand(2,3) + for(var/i=0, i= 150 && !girder_material.is_brittle()) //Strong girders will remain in place when a wall is melted. + dismantle_wall(1,1) + else + src.ChangeTurf(/turf/simulated/floor/plating) + + var/turf/simulated/floor/F = src + F.burn_tile() + F.icon_state = "dmg[rand(1,4)]" + to_chat(user, "The thermite starts melting through the wall.") + + spawn(100) + if(O) + qdel(O) +// F.sd_LumReset() //TODO: ~Carn + return + +/turf/simulated/wall/proc/radiate() + var/total_radiation = material.radioactivity + (reinf_material ? reinf_material.radioactivity / 2 : 0) + (girder_material ? girder_material.radioactivity / 2 : 0) + if(!total_radiation) + return + + SSradiation.radiate(src, total_radiation) + return total_radiation + +/turf/simulated/wall/proc/burn(temperature) + if(material.combustion_effect(src, temperature, 0.7)) + spawn(2) + new /obj/structure/girder(src, girder_material.name) + src.ChangeTurf(/turf/simulated/floor) + for(var/turf/simulated/wall/W in range(3,src)) + W.burn((temperature/4)) + for(var/obj/machinery/door/airlock/phoron/D in range(3,src)) + D.ignite(temperature/4) + +/turf/simulated/wall/can_engrave() + return (material && material.hardness >= 10 && material.hardness <= 100) + +/turf/simulated/wall/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + if(material.integrity > 1000) // Don't decon things like elevatorium. + return FALSE + if(reinf_material && !the_rcd.can_remove_rwalls) // Gotta do it the old fashioned way if your RCD can't. + return FALSE + + if(passed_mode == RCD_DECONSTRUCT) + var/delay_to_use = material.integrity / 3 // Steel has 150 integrity, so it'll take five seconds to down a regular wall. + if(reinf_material) + delay_to_use += reinf_material.integrity / 3 + return list( + RCD_VALUE_MODE = RCD_DECONSTRUCT, + RCD_VALUE_DELAY = delay_to_use, + RCD_VALUE_COST = RCD_SHEETS_PER_MATTER_UNIT * 5 + ) + return FALSE + +/turf/simulated/wall/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + if(passed_mode == RCD_DECONSTRUCT) + to_chat(user, span("notice", "You deconstruct \the [src].")) + ChangeTurf(/turf/simulated/floor/airless, preserve_outdoors = TRUE) + return TRUE + return FALSE + +/turf/simulated/wall/AltClick(mob/user) + if(isliving(user)) + var/mob/living/livingUser = user + if(try_graffiti(livingUser, livingUser.get_active_hand())) + return + . = ..() diff --git a/code/game/turfs/simulated/water.dm b/code/game/turfs/simulated/water.dm index 725aefb854b..ae136ec0e62 100644 --- a/code/game/turfs/simulated/water.dm +++ b/code/game/turfs/simulated/water.dm @@ -73,6 +73,27 @@ water_breath.adjust_gas(gasid, BREATH_MOLES) // They have no oxygen, but non-zero moles and temp water_breath.temperature = above_air.temperature return water_breath + if(L && L.is_bad_swimmer() && depth >= 2 && !L.buckled()) + if(prob(10)) + L.visible_message("[L] splashes wildly.","You struggle to keep your head above the water!") + if(L.can_breathe_water()) + var/datum/gas_mixture/water_breath = new() + var/datum/gas_mixture/above_air = return_air() + var/amount = 300 + water_breath.adjust_gas("oxygen", amount) // Assuming water breathes just extract the oxygen directly from the water. + water_breath.temperature = above_air.temperature + return water_breath + else + var/gasid = "carbon_dioxide" + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.species && H.species.exhale_type) + gasid = H.species.exhale_type + var/datum/gas_mixture/water_breath = new() + var/datum/gas_mixture/above_air = return_air() + water_breath.adjust_gas(gasid, BREATH_MOLES) // They have no oxygen, but non-zero moles and temp + water_breath.temperature = above_air.temperature + return water_breath return return_air() // Otherwise their head is above the water, so get the air from the atmosphere instead. /turf/simulated/floor/water/Entered(atom/movable/AM, atom/oldloc) @@ -125,6 +146,14 @@ return species.can_breathe_water() return ..() +/mob/living/proc/is_bad_swimmer() + return FALSE + +/mob/living/carbon/human/is_bad_swimmer() + if(species) + return species.is_bad_swimmer() + return ..() + /mob/living/proc/check_submerged() if(buckled) return 0 @@ -210,3 +239,27 @@ var/list/shoreline_icon_cache = list() poisonlevel *= 1 - L.get_water_protection() if(poisonlevel > 0) L.adjustToxLoss(poisonlevel) + +/turf/simulated/floor/water/blood + name = "blood" + desc = "A body of blood. It seems shallow enough to walk through, if needed." + icon = 'icons/turf/outdoors.dmi' + icon_state = "bloodshallow" + water_icon = 'icons/turf/outdoors.dmi' + water_state = "bloodshallow" + under_state = "rock" + reagent_type = "blood" + +/turf/simulated/floor/water/blood/get_edge_icon_state() + return "bloodshallow" + +/turf/simulated/floor/water/blood/Entered(atom/movable/AM, atom/oldloc) + if(istype(AM, /mob/living)) + var/mob/living/L = AM + L.update_water() + if(L.check_submerged() <= 0) + return + if(!istype(oldloc, /turf/simulated/floor/water)) + to_chat(L, "You get drenched in blood from entering \the [src]!") + AM.water_act(5) + ..() diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 4986701fccf..289a3b75ce9 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -1,252 +1,263 @@ -/turf/space - icon = 'icons/turf/space.dmi' - name = "\proper space" - icon_state = "default" - dynamic_lighting = 0 - plane = SPACE_PLANE - flags = TURF_ACID_IMMUNE - temperature = T20C - thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT - can_build_into_floor = TRUE - var/keep_sprite = FALSE - var/edge = FALSE //If we're an edge - var/forced_dirs = 0 //Force this one to pretend it's an overedge turf - -/turf/space/Initialize() - if(config.starlight) - update_starlight() - - //Sprite stuff only beyond here - if(keep_sprite) - return ..() - - //We might be an edge - if(y == world.maxy || forced_dirs & NORTH) - edge |= NORTH - else if(y == 1 || forced_dirs & SOUTH) - edge |= SOUTH - - if(x == 1 || forced_dirs & WEST) - edge |= WEST - else if(x == world.maxx || forced_dirs & EAST) - edge |= EAST - - if(edge) //Magic edges - appearance = SSskybox.mapedge_cache["[edge]"] - else //Dust - appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] - - return ..() - -/turf/space/proc/toggle_transit(var/direction) - if(edge) //Not a great way to do this yet. Maybe we'll come up with one. We could pre-make sprites... or tile the overlay over it? - return - - if(!direction) //Stopping our transit - appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] - else if(direction & (NORTH|SOUTH)) //Starting transit vertically - var/x_shift = SSskybox.phase_shift_by_x[src.x % (SSskybox.phase_shift_by_x.len - 1) + 1] - var/transit_state = ((direction & SOUTH ? world.maxy - src.y : src.y) + x_shift)%15 - appearance = SSskybox.speedspace_cache["NS_[transit_state]"] - else if(direction & (EAST|WEST)) //Starting transit horizontally - var/y_shift = SSskybox.phase_shift_by_y[src.y % (SSskybox.phase_shift_by_y.len - 1) + 1] - var/transit_state = ((direction & WEST ? world.maxx - src.x : src.x) + y_shift)%15 - appearance = SSskybox.speedspace_cache["EW_[transit_state]"] - - for(var/atom/movable/AM in src) - if (!AM.simulated) - continue - - if(!AM.anchored) - AM.throw_at(get_step(src,reverse_direction(direction)), 5, 1) - else if (istype(AM, /obj/effect/decal)) - qdel(AM) //No more space blood coming with the shuttle - -/turf/space/is_space() - return 1 - -// override for space turfs, since they should never hide anything -/turf/space/levelupdate() - for(var/obj/O in src) - O.hide(0) - -/turf/space/is_solid_structure() - return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice - -/turf/space/proc/update_starlight() - if(locate(/turf/simulated) in orange(src,1)) - set_light(config.starlight) - else - set_light(0) - -/turf/space/attackby(obj/item/C as obj, mob/user as mob) - - if(istype(C, /obj/item/stack/rods)) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - return - var/obj/item/stack/rods/R = C - if (R.use(1)) - to_chat(user, "Constructing support lattice ...") - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - ReplaceWithLattice() - return - - if(istype(C, /obj/item/stack/tile/floor)) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - var/obj/item/stack/tile/floor/S = C - if (S.get_amount() < 1) - return - qdel(L) - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - S.use(1) - ChangeTurf(/turf/simulated/floor/airless) - return - else - to_chat(user, "The plating is going to need some support.") - - if(istype(C, /obj/item/stack/tile/roofing)) - var/turf/T = GetAbove(src) - var/obj/item/stack/tile/roofing/R = C - - // Patch holes in the ceiling - if(T) - if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) - // Must be build adjacent to an existing floor/wall, no floating floors - var/turf/simulated/A = locate(/turf/simulated/floor) in T.CardinalTurfs() - if(!A) - A = locate(/turf/simulated/wall) in T.CardinalTurfs() - if(!A) - to_chat(user, "There's nothing to attach the ceiling to!") - return - - if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating - T.ReplaceWithLattice() - T.ChangeTurf(/turf/simulated/floor) - playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) - user.visible_message("[user] expands the ceiling.", "You expand the ceiling.") - else - to_chat(user, "There aren't any holes in the ceiling to patch here.") - return - // Space shouldn't have weather of the sort planets with atmospheres do. - // If that's changed, then you'll want to swipe the rest of the roofing code from code/game/turfs/simulated/floor_attackby.dm - return - -/turf/space/Entered(var/atom/movable/A) - . = ..() - - if(edge && ticker?.mode && !density) // !density so 'fake' space turfs don't fling ghosts everywhere - A?.touch_map_edge() - -/turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) - var/cur_x - var/cur_y - var/next_x - var/next_y - var/target_z - var/list/y_arr - - if(src.x <= 1) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) - qdel(A) - return - - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - next_x = (--cur_x||global_map.len) - y_arr = global_map[next_x] - target_z = y_arr[cur_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Target Z = [target_z]") - to_world("Next X = [next_x]") - //debug -*/ - if(target_z) - A.z = target_z - A.x = world.maxx - 2 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - else if (src.x >= world.maxx) - if(istype(A, /obj/effect/meteor)) - qdel(A) - return - - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - next_x = (++cur_x > global_map.len ? 1 : cur_x) - y_arr = global_map[next_x] - target_z = y_arr[cur_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Target Z = [target_z]") - to_world("Next X = [next_x]") - //debug -*/ - if(target_z) - A.z = target_z - A.x = 3 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - else if (src.y <= 1) - if(istype(A, /obj/effect/meteor)) - qdel(A) - return - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - y_arr = global_map[cur_x] - next_y = (--cur_y||y_arr.len) - target_z = y_arr[next_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Next Y = [next_y]") - to_world("Target Z = [target_z]") - //debug -*/ - if(target_z) - A.z = target_z - A.y = world.maxy - 2 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - - else if (src.y >= world.maxy) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) - qdel(A) - return - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - y_arr = global_map[cur_x] - next_y = (++cur_y > y_arr.len ? 1 : cur_y) - target_z = y_arr[next_y] -/* - //debug - to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_world("Next Y = [next_y]") - to_world("Target Z = [target_z]") - //debug -*/ - if(target_z) - A.z = target_z - A.y = 3 - spawn (0) - if ((A && A.loc)) - A.loc.Entered(A) - return - -/turf/space/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) - return ..(N, tell_universe, 1, preserve_outdoors) +/turf/space + icon = 'icons/turf/space.dmi' + name = "\proper space" + icon_state = "default" + dynamic_lighting = 0 + plane = SPACE_PLANE + flags = TURF_ACID_IMMUNE + temperature = T20C + thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT + can_build_into_floor = TRUE + var/keep_sprite = FALSE + var/edge = FALSE //If we're an edge + var/forced_dirs = 0 //Force this one to pretend it's an overedge turf + +/turf/space/Initialize() + if(config.starlight) + update_starlight() + + //Sprite stuff only beyond here + if(keep_sprite) + return ..() + + //We might be an edge + if(y == world.maxy || forced_dirs & NORTH) + edge |= NORTH + else if(y == 1 || forced_dirs & SOUTH) + edge |= SOUTH + + if(x == 1 || forced_dirs & WEST) + edge |= WEST + else if(x == world.maxx || forced_dirs & EAST) + edge |= EAST + + if(edge) //Magic edges + appearance = SSskybox.mapedge_cache["[edge]"] + else //Dust + appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + + return ..() + +/turf/space/proc/toggle_transit(var/direction) + if(edge) //Not a great way to do this yet. Maybe we'll come up with one. We could pre-make sprites... or tile the overlay over it? + return + + if(!direction) //Stopping our transit + appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + else if(direction & (NORTH|SOUTH)) //Starting transit vertically + var/x_shift = SSskybox.phase_shift_by_x[src.x % (SSskybox.phase_shift_by_x.len - 1) + 1] + var/transit_state = ((direction & SOUTH ? world.maxy - src.y : src.y) + x_shift)%15 + appearance = SSskybox.speedspace_cache["NS_[transit_state]"] + else if(direction & (EAST|WEST)) //Starting transit horizontally + var/y_shift = SSskybox.phase_shift_by_y[src.y % (SSskybox.phase_shift_by_y.len - 1) + 1] + var/transit_state = ((direction & WEST ? world.maxx - src.x : src.x) + y_shift)%15 + appearance = SSskybox.speedspace_cache["EW_[transit_state]"] + + for(var/atom/movable/AM in src) + if (!AM.simulated) + continue + + if(!AM.anchored) + AM.throw_at(get_step(src,reverse_direction(direction)), 5, 1) + else if (istype(AM, /obj/effect/decal)) + qdel(AM) //No more space blood coming with the shuttle + +/turf/space/is_space() + return 1 + +// override for space turfs, since they should never hide anything +/turf/space/levelupdate() + for(var/obj/O in src) + O.hide(0) + +/turf/space/is_solid_structure() + return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice + +/turf/space/proc/update_starlight() + if(locate(/turf/simulated) in orange(src,1)) + set_light(config.starlight) + else + set_light(0) + +/turf/space/attackby(obj/item/C as obj, mob/user as mob) + + if(istype(C, /obj/item/stack/rods)) + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + if(L) + return + var/obj/item/stack/rods/R = C + if (R.use(1)) + to_chat(user, "Constructing support lattice ...") + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + ReplaceWithLattice() + return + + if(istype(C, /obj/item/stack/tile/floor)) + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + if(L) + var/obj/item/stack/tile/floor/S = C + if (S.get_amount() < 1) + return + qdel(L) + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + S.use(1) + ChangeTurf(/turf/simulated/floor/airless) + return + else + to_chat(user, "The plating is going to need some support.") + + if(istype(C, /obj/item/stack/tile/roofing)) + var/turf/T = GetAbove(src) + var/obj/item/stack/tile/roofing/R = C + + // Patch holes in the ceiling + if(T) + if(istype(T, /turf/simulated/open) || istype(T, /turf/space)) + // Must be build adjacent to an existing floor/wall, no floating floors + var/turf/simulated/A = locate(/turf/simulated/floor) in T.CardinalTurfs() + if(!A) + A = locate(/turf/simulated/wall) in T.CardinalTurfs() + if(!A) + to_chat(user, "There's nothing to attach the ceiling to!") + return + + if(R.use(1)) // Cost of roofing tiles is 1:1 with cost to place lattice and plating + T.ReplaceWithLattice() + T.ChangeTurf(/turf/simulated/floor) + playsound(src, 'sound/weapons/Genhit.ogg', 50, 1) + user.visible_message("[user] expands the ceiling.", "You expand the ceiling.") + else + to_chat(user, "There aren't any holes in the ceiling to patch here.") + return + // Space shouldn't have weather of the sort planets with atmospheres do. + // If that's changed, then you'll want to swipe the rest of the roofing code from code/game/turfs/simulated/floor_attackby.dm + return + +/turf/space/Entered(var/atom/movable/A) + . = ..() + + if(edge && ticker?.mode && !density) // !density so 'fake' space turfs don't fling ghosts everywhere + if(isliving(A)) + var/mob/living/L = A + if(L.pulling) + var/atom/movable/pulled = L.pulling + L.stop_pulling() + A?.touch_map_edge() + pulled.forceMove(L.loc) + L.continue_pulling(pulled) + else + A?.touch_map_edge() + else + A?.touch_map_edge() + +/turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) + var/cur_x + var/cur_y + var/next_x + var/next_y + var/target_z + var/list/y_arr + + if(src.x <= 1) + if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + qdel(A) + return + + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + next_x = (--cur_x||global_map.len) + y_arr = global_map[next_x] + target_z = y_arr[cur_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Target Z = [target_z]") + to_world("Next X = [next_x]") + //debug +*/ + if(target_z) + A.z = target_z + A.x = world.maxx - 2 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + else if (src.x >= world.maxx) + if(istype(A, /obj/effect/meteor)) + qdel(A) + return + + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + next_x = (++cur_x > global_map.len ? 1 : cur_x) + y_arr = global_map[next_x] + target_z = y_arr[cur_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Target Z = [target_z]") + to_world("Next X = [next_x]") + //debug +*/ + if(target_z) + A.z = target_z + A.x = 3 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + else if (src.y <= 1) + if(istype(A, /obj/effect/meteor)) + qdel(A) + return + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + y_arr = global_map[cur_x] + next_y = (--cur_y||y_arr.len) + target_z = y_arr[next_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Next Y = [next_y]") + to_world("Target Z = [target_z]") + //debug +*/ + if(target_z) + A.z = target_z + A.y = world.maxy - 2 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + + else if (src.y >= world.maxy) + if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + qdel(A) + return + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + y_arr = global_map[cur_x] + next_y = (++cur_y > y_arr.len ? 1 : cur_y) + target_z = y_arr[next_y] +/* + //debug + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Next Y = [next_y]") + to_world("Target Z = [target_z]") + //debug +*/ + if(target_z) + A.z = target_z + A.y = 3 + spawn (0) + if ((A && A.loc)) + A.loc.Entered(A) + return + +/turf/space/ChangeTurf(var/turf/N, var/tell_universe, var/force_lighting_update, var/preserve_outdoors) + return ..(N, tell_universe, 1, preserve_outdoors) diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 02c589e966d..e5a560a9134 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,31 +1,31 @@ -/turf/space/transit - can_build_into_floor = FALSE - var/pushdirection // push things that get caught in the transit tile this direction - -//Overwrite because we dont want people building rods in space. -/turf/space/transit/attackby(obj/O as obj, mob/user as mob) - return - -/turf/space/transit/Initialize() - . = ..() - toggle_transit(reverse_dir[pushdirection]) - -//------------------------ - -/turf/space/transit/north // moving to the north - icon_state = "arrow-north" - pushdirection = SOUTH // south because the space tile is scrolling south - -/turf/space/transit/south // moving to the south - icon_state = "arrow-south" - pushdirection = SOUTH // south because the space tile is scrolling south - -/turf/space/transit/east // moving to the east - icon_state = "arrow-east" - pushdirection = WEST - -/turf/space/transit/west // moving to the west - icon_state = "arrow-west" - pushdirection = WEST - +/turf/space/transit + can_build_into_floor = FALSE + var/pushdirection // push things that get caught in the transit tile this direction + +//Overwrite because we dont want people building rods in space. +/turf/space/transit/attackby(obj/O as obj, mob/user as mob) + return + +/turf/space/transit/Initialize() + . = ..() + toggle_transit(reverse_dir[pushdirection]) + +//------------------------ + +/turf/space/transit/north // moving to the north + icon_state = "arrow-north" + pushdirection = SOUTH // south because the space tile is scrolling south + +/turf/space/transit/south // moving to the south + icon_state = "arrow-south" + pushdirection = SOUTH // south because the space tile is scrolling south + +/turf/space/transit/east // moving to the east + icon_state = "arrow-east" + pushdirection = WEST + +/turf/space/transit/west // moving to the west + icon_state = "arrow-west" + pushdirection = WEST + //------------------------ \ No newline at end of file diff --git a/code/game/turfs/turf_flick_animations.dm b/code/game/turfs/turf_flick_animations.dm index 81b248ec0df..43f1e90f598 100644 --- a/code/game/turfs/turf_flick_animations.dm +++ b/code/game/turfs/turf_flick_animations.dm @@ -1,20 +1,20 @@ -/proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) -//This proc throws up either an icon or an animation for a specified amount of time. -//The variables should be apparent enough. - if(!location && target) - location = get_turf(target) - if(location && !target) - target = location - var/atom/movable/overlay/animation = new /atom/movable/overlay(location) - if(direction) - animation.set_dir(direction) - animation.icon = a_icon - animation.layer = target:layer+1 - if(a_icon_state) - animation.icon_state = a_icon_state - else - animation.icon_state = "blank" - animation.master = target - flick(flick_anim, animation) - spawn(max(sleeptime, 15)) - qdel(animation) +/proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,flick_anim as text,sleeptime = 0,direction as num) +//This proc throws up either an icon or an animation for a specified amount of time. +//The variables should be apparent enough. + if(!location && target) + location = get_turf(target) + if(location && !target) + target = location + var/atom/movable/overlay/animation = new /atom/movable/overlay(location) + if(direction) + animation.set_dir(direction) + animation.icon = a_icon + animation.layer = target:layer+1 + if(a_icon_state) + animation.icon_state = a_icon_state + else + animation.icon_state = "blank" + animation.master = target + flick(flick_anim, animation) + spawn(max(sleeptime, 15)) + qdel(animation) diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm index 0e8c5c98b69..f9bfd4102b6 100644 --- a/code/game/turfs/unsimulated.dm +++ b/code/game/turfs/unsimulated.dm @@ -1,32 +1,32 @@ -/turf/unsimulated - name = "command" - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - var/skip_init = TRUE // Don't call down the chain, apparently for performance when loading maps at runtime. - flags = TURF_ACID_IMMUNE - -/turf/unsimulated/Initialize(mapload) - if(skip_init) - initialized = TRUE - return INITIALIZE_HINT_NORMAL - . = ..() - -//VOREStation Add -/turf/unsimulated/fake_space - name = "\proper space" - icon = 'icons/turf/space.dmi' - icon_state = "0" - dynamic_lighting = FALSE - initialized = FALSE - -/turf/unsimulated/fake_space/Initialize(mapload) - . = ..() - icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" -//VOREStation Add End - -// Better nip this just in case. -/turf/unsimulated/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return FALSE - -/turf/unsimulated/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) - return FALSE +/turf/unsimulated + name = "command" + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + var/skip_init = TRUE // Don't call down the chain, apparently for performance when loading maps at runtime. + flags = TURF_ACID_IMMUNE + +/turf/unsimulated/Initialize(mapload) + if(skip_init) + initialized = TRUE + return INITIALIZE_HINT_NORMAL + . = ..() + +//VOREStation Add +/turf/unsimulated/fake_space + name = "\proper space" + icon = 'icons/turf/space.dmi' + icon_state = "0" + dynamic_lighting = FALSE + initialized = FALSE + +/turf/unsimulated/fake_space/Initialize(mapload) + . = ..() + icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" +//VOREStation Add End + +// Better nip this just in case. +/turf/unsimulated/rcd_values(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return FALSE + +/turf/unsimulated/rcd_act(mob/living/user, obj/item/weapon/rcd/the_rcd, passed_mode) + return FALSE diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index b1841dadad8..ccd22d213fb 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -1,72 +1,72 @@ -/turf/unsimulated/beach - name = "Beach" - icon = 'icons/misc/beach.dmi' - -/turf/unsimulated/beach/sand - name = "Sand" - icon_state = "sand" - -/turf/unsimulated/beach/coastline - name = "Coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - -/turf/unsimulated/beach/water - name = "Water" - icon_state = "water" - skip_init = FALSE - movement_cost = 4 // Water should slow you down, just like simulated turf. - -/turf/unsimulated/beach/water/Initialize() - . = ..() - add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water2","layer"=MOB_LAYER+0.1)) - -/turf/simulated/floor/beach - name = "Beach" - icon = 'icons/misc/beach.dmi' - initial_flooring = /decl/flooring/sand - -/turf/simulated/floor/beach/sand - name = "Sand" - icon_state = "sand" - initial_flooring = /decl/flooring/sand - -/turf/simulated/floor/beach/sand/desert - icon = 'icons/turf/desert.dmi' - icon_state = "desert" - initial_flooring = /decl/flooring/sand/desert - -/turf/simulated/floor/beach/sand/desert/Initialize() - . = ..() - if(prob(5)) - icon_state = "desert[rand(0,4)]" - -/turf/simulated/floor/beach/coastline - name = "Coastline" - icon = 'icons/misc/beach2.dmi' - icon_state = "sandwater" - -/turf/simulated/floor/beach/water - name = "Water" - icon_state = "water" - movement_cost = 4 // Water should slow you down, just like the original simulated turf. - initial_flooring = /decl/flooring/water - -/turf/simulated/floor/beach/water/ocean - icon_state = "seadeep" - movement_cost = 8 // Deep water should be difficult to wade through. - initial_flooring = /decl/flooring/water/beach/deep - -/turf/simulated/floor/beach/water/Initialize() - . = ..() - add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water5","layer"=MOB_LAYER+0.1)) - -/decl/flooring/water/beach/deep // We're custom-defining a 'deep' water turf for the beach. - name = "deep water" - desc = "Deep Ocean Water" - icon = 'icons/misc/beach.dmi' - icon_base = "seadeep" - footstep_sounds = list("human" = list( - 'sound/effects/footstep/bubbles3.ogg', // No I don't get why it's named 3/4/5 either. Whatever. - 'sound/effects/footstep/bubbles4.ogg', - 'sound/effects/footstep/bubbles5.ogg')) +/turf/unsimulated/beach + name = "Beach" + icon = 'icons/misc/beach.dmi' + +/turf/unsimulated/beach/sand + name = "Sand" + icon_state = "sand" + +/turf/unsimulated/beach/coastline + name = "Coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + +/turf/unsimulated/beach/water + name = "Water" + icon_state = "water" + skip_init = FALSE + movement_cost = 4 // Water should slow you down, just like simulated turf. + +/turf/unsimulated/beach/water/Initialize() + . = ..() + add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water2","layer"=MOB_LAYER+0.1)) + +/turf/simulated/floor/beach + name = "Beach" + icon = 'icons/misc/beach.dmi' + initial_flooring = /decl/flooring/sand + +/turf/simulated/floor/beach/sand + name = "Sand" + icon_state = "sand" + initial_flooring = /decl/flooring/sand + +/turf/simulated/floor/beach/sand/desert + icon = 'icons/turf/desert.dmi' + icon_state = "desert" + initial_flooring = /decl/flooring/sand/desert + +/turf/simulated/floor/beach/sand/desert/Initialize() + . = ..() + if(prob(5)) + icon_state = "desert[rand(0,4)]" + +/turf/simulated/floor/beach/coastline + name = "Coastline" + icon = 'icons/misc/beach2.dmi' + icon_state = "sandwater" + +/turf/simulated/floor/beach/water + name = "Water" + icon_state = "water" + movement_cost = 4 // Water should slow you down, just like the original simulated turf. + initial_flooring = /decl/flooring/water + +/turf/simulated/floor/beach/water/ocean + icon_state = "seadeep" + movement_cost = 8 // Deep water should be difficult to wade through. + initial_flooring = /decl/flooring/water/beach/deep + +/turf/simulated/floor/beach/water/Initialize() + . = ..() + add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water5","layer"=MOB_LAYER+0.1)) + +/decl/flooring/water/beach/deep // We're custom-defining a 'deep' water turf for the beach. + name = "deep water" + desc = "Deep Ocean Water" + icon = 'icons/misc/beach.dmi' + icon_base = "seadeep" + footstep_sounds = list("human" = list( + 'sound/effects/footstep/bubbles3.ogg', // No I don't get why it's named 3/4/5 either. Whatever. + 'sound/effects/footstep/bubbles4.ogg', + 'sound/effects/footstep/bubbles5.ogg')) diff --git a/code/game/turfs/unsimulated/beach_vr.dm b/code/game/turfs/unsimulated/beach_vr.dm index 6fe6b07b2cb..3c1f48c7b96 100644 --- a/code/game/turfs/unsimulated/beach_vr.dm +++ b/code/game/turfs/unsimulated/beach_vr.dm @@ -1,5 +1,5 @@ -/turf/simulated/floor/beach/sand/outdoors - outdoors = OUTDOORS_YES - -/turf/simulated/floor/beach/sand/desert/outdoors +/turf/simulated/floor/beach/sand/outdoors + outdoors = OUTDOORS_YES + +/turf/simulated/floor/beach/sand/desert/outdoors outdoors = OUTDOORS_YES \ No newline at end of file diff --git a/code/game/turfs/unsimulated/floor.dm b/code/game/turfs/unsimulated/floor.dm index 22af37dd531..7f3df78c134 100644 --- a/code/game/turfs/unsimulated/floor.dm +++ b/code/game/turfs/unsimulated/floor.dm @@ -1,17 +1,17 @@ -/turf/unsimulated/floor - name = "floor" - icon = 'icons/turf/floors.dmi' - icon_state = "Floor3" - -/turf/unsimulated/mask - name = "mask" - icon = 'icons/turf/walls.dmi' - icon_state = "rockvault" - -/turf/unsimulated/floor/shuttle_ceiling - icon_state = "reinforced" - -/turf/unsimulated/elevator_shaft - name = "floor" - icon = 'icons/turf/floors.dmi' +/turf/unsimulated/floor + name = "floor" + icon = 'icons/turf/floors.dmi' + icon_state = "Floor3" + +/turf/unsimulated/mask + name = "mask" + icon = 'icons/turf/walls.dmi' + icon_state = "rockvault" + +/turf/unsimulated/floor/shuttle_ceiling + icon_state = "reinforced" + +/turf/unsimulated/elevator_shaft + name = "floor" + icon = 'icons/turf/floors.dmi' icon_state = "elevatorshaft" \ No newline at end of file diff --git a/code/game/turfs/unsimulated/planetary_vr.dm b/code/game/turfs/unsimulated/planetary_vr.dm index b82342ea0a4..144379a6f60 100644 --- a/code/game/turfs/unsimulated/planetary_vr.dm +++ b/code/game/turfs/unsimulated/planetary_vr.dm @@ -64,4 +64,4 @@ //other set - for map building /turf/unsimulated/wall2/planetary/virgo3b_better - icon_state = "riveted2" \ No newline at end of file + icon_state = "riveted2" diff --git a/code/game/turfs/unsimulated/sky_vr.dm b/code/game/turfs/unsimulated/sky_vr.dm index c71fa6470a7..9d8d40c71fc 100644 --- a/code/game/turfs/unsimulated/sky_vr.dm +++ b/code/game/turfs/unsimulated/sky_vr.dm @@ -1,73 +1,73 @@ -/////////////////// -// Generic skyfall turf -// Really only works well if the map doesn't have 'indoor' areas otherwise they can fall into one. -// TODO: Fix that. -/turf/unsimulated/floor/sky - name = "the sky" - desc = "It's the sky! Be careful!" - icon = 'icons/turf/floors.dmi' - icon_state = "sky_slow" - dir = SOUTH - initialized = FALSE - var/does_skyfall = TRUE - var/list/skyfall_levels - -/turf/unsimulated/floor/sky/Initialize() - . = ..() - if(does_skyfall && !LAZYLEN(skyfall_levels)) - error("[x],[y],[z], [get_area(src)] doesn't have skyfall_levels defined! Can't skyfall!") - if(locate(/turf/simulated) in orange(src,1)) - set_light(2, 2, color) - -/turf/unsimulated/floor/sky/Entered(atom/movable/AM,atom/oldloc) - . = ..() - if(!does_skyfall) - return //We don't do that - if(isobserver(AM)) - return //Don't ghostport, very annoying - if(AM.throwing) - return //Being thrown over, not fallen yet - if(!(AM.can_fall())) - return // Phased shifted kin should not fall - if(istype(AM, /obj/item/projectile)) - return // pewpew should not fall out of the sky. pew. - if(istype(AM, /obj/effect/projectile)) - return // ...neither should the effects be falling - - var/mob/living/L - if(isliving(AM)) - L = AM - if(L.is_floating) - return //Flyers/nograv can ignore it - - do_fall(AM) - -/turf/unsimulated/floor/sky/hitby(var/atom/movable/AM, var/speed) - . = ..() - - if(!does_skyfall) - return //We don't do that - - do_fall(AM) - -/turf/unsimulated/floor/sky/proc/do_fall(atom/movable/AM) - //Bye - var/attempts = 100 - var/turf/simulated/T - while(attempts && !T) - var/turf/simulated/candidate = locate(rand(5,world.maxx-5),rand(5,world.maxy-5),pick(skyfall_levels)) - if(candidate.density) - attempts-- - continue - - T = candidate - break - - if(!T) - return - - AM.forceMove(T) - if(isliving(AM)) - var/mob/living/L = AM - message_admins("\The [AM] fell out of the sky.") - L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. +/////////////////// +// Generic skyfall turf +// Really only works well if the map doesn't have 'indoor' areas otherwise they can fall into one. +// TODO: Fix that. +/turf/unsimulated/floor/sky + name = "the sky" + desc = "It's the sky! Be careful!" + icon = 'icons/turf/floors.dmi' + icon_state = "sky_slow" + dir = SOUTH + initialized = FALSE + var/does_skyfall = TRUE + var/list/skyfall_levels + +/turf/unsimulated/floor/sky/Initialize() + . = ..() + if(does_skyfall && !LAZYLEN(skyfall_levels)) + error("[x],[y],[z], [get_area(src)] doesn't have skyfall_levels defined! Can't skyfall!") + if(locate(/turf/simulated) in orange(src,1)) + set_light(2, 2, color) + +/turf/unsimulated/floor/sky/Entered(atom/movable/AM,atom/oldloc) + . = ..() + if(!does_skyfall) + return //We don't do that + if(isobserver(AM)) + return //Don't ghostport, very annoying + if(AM.throwing) + return //Being thrown over, not fallen yet + if(!(AM.can_fall())) + return // Phased shifted kin should not fall + if(istype(AM, /obj/item/projectile)) + return // pewpew should not fall out of the sky. pew. + if(istype(AM, /obj/effect/projectile)) + return // ...neither should the effects be falling + + var/mob/living/L + if(isliving(AM)) + L = AM + if(L.is_floating) + return //Flyers/nograv can ignore it + + do_fall(AM) + +/turf/unsimulated/floor/sky/hitby(var/atom/movable/AM, var/speed) + . = ..() + + if(!does_skyfall) + return //We don't do that + + do_fall(AM) + +/turf/unsimulated/floor/sky/proc/do_fall(atom/movable/AM) + //Bye + var/attempts = 100 + var/turf/simulated/T + while(attempts && !T) + var/turf/simulated/candidate = locate(rand(5,world.maxx-5),rand(5,world.maxy-5),pick(skyfall_levels)) + if(candidate.density) + attempts-- + continue + + T = candidate + break + + if(!T) + return + + AM.forceMove(T) + if(isliving(AM)) + var/mob/living/L = AM + message_admins("\The [AM] fell out of the sky.") + L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. diff --git a/code/game/turfs/unsimulated/walls.dm b/code/game/turfs/unsimulated/walls.dm index dce3c345bb7..674619158ae 100644 --- a/code/game/turfs/unsimulated/walls.dm +++ b/code/game/turfs/unsimulated/walls.dm @@ -1,27 +1,27 @@ -/turf/unsimulated/wall - name = "wall" - icon = 'icons/turf/walls.dmi' - icon_state = "riveted" - opacity = 1 - density = TRUE - blocks_air = TRUE - -//other set - for map building -/turf/unsimulated/wall/wall1 - icon_state = "riveted1" - -/turf/unsimulated/wall/wall2 - icon_state = "riveted2" - -/turf/unsimulated/wall/fakeglass - name = "window" - icon_state = "fakewindows" - opacity = 0 - -//other set - for map building -/turf/unsimulated/wall/fakeglass2 - icon_state = "fakewindows2" - opacity = 0 - -/turf/unsimulated/wall/other +/turf/unsimulated/wall + name = "wall" + icon = 'icons/turf/walls.dmi' + icon_state = "riveted" + opacity = 1 + density = TRUE + blocks_air = TRUE + +//other set - for map building +/turf/unsimulated/wall/wall1 + icon_state = "riveted1" + +/turf/unsimulated/wall/wall2 + icon_state = "riveted2" + +/turf/unsimulated/wall/fakeglass + name = "window" + icon_state = "fakewindows" + opacity = 0 + +//other set - for map building +/turf/unsimulated/wall/fakeglass2 + icon_state = "fakewindows2" + opacity = 0 + +/turf/unsimulated/wall/other icon_state = "r_wall" \ No newline at end of file diff --git a/code/game/turfs/weird_turfs_vr.dm b/code/game/turfs/weird_turfs_vr.dm index b1ef1fa4fb2..9a6bad3973a 100644 --- a/code/game/turfs/weird_turfs_vr.dm +++ b/code/game/turfs/weird_turfs_vr.dm @@ -43,10 +43,6 @@ i.plane = PLANE_LIGHTING_ABOVE add_overlay(i) -/turf/simulated/floor/weird_things/dark/ChangeTurf() - . = ..() - cut_overlays() - /turf/unsimulated/wall/dark name = "dark" desc = "It's a strange, impenetrable darkness." @@ -64,11 +60,6 @@ i.plane = PLANE_LIGHTING_ABOVE add_overlay(i) -/turf/unsimulated/wall/dark/ChangeTurf() - . = ..() - cut_overlays() - - /turf/unsimulated/floor/dark name = "dark" desc = "It's a strange, impenetrable darkness." @@ -79,7 +70,7 @@ . = ..() if(prob(5)) add_glow() - + /turf/unsimulated/floor/dark/Crossed(O) . = ..() if(!isliving(O)) @@ -111,7 +102,7 @@ var/choiceb if(flip == 1) choice = "overlay-[rand(1,6)]" - if(flip == 2) + if(flip == 2) choice = "overlay-[rand(7,12)]" var/image/i = image('icons/turf/flooring/weird_vr.dmi', choice) i.plane = PLANE_LIGHTING_ABOVE @@ -122,10 +113,4 @@ if(flip == 2) choiceb = "overlay-[rand(1,6)]" var/image/ii = image('icons/turf/flooring/weird_vr.dmi', choiceb) - add_overlay(ii) - - - -/turf/unsimulated/floor/dark/ChangeTurf() - . = ..() - cut_overlays() \ No newline at end of file + add_overlay(ii) \ No newline at end of file diff --git a/code/game/vehicles/vehicle.dm b/code/game/vehicles/vehicle.dm index b7361ea7f78..9b00a557684 100644 --- a/code/game/vehicles/vehicle.dm +++ b/code/game/vehicles/vehicle.dm @@ -1,103 +1,103 @@ - - -/obj/vehicle - name = "Vehicle" - icon = 'icons/vehicles/vehicles.dmi' - density = TRUE - anchored = TRUE - unacidable = TRUE //To avoid the pilot-deleting shit that came with mechas - layer = MOB_LAYER - //var/can_move = 1 - var/mob/living/carbon/occupant = null - //var/step_in = 10 //make a step in step_in/10 sec. - //var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - //var/step_energy_drain = 10 - var/health = 300 //health is health - //var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - //the values in this list show how much damage will pass through, not how much will be absorbed. - var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1) - var/obj/item/weapon/cell/cell //Our power source - var/state = 0 - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - //var/dna //dna-locking the mech - var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect/effect/system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - - //inner atmos //These go in airtight.dm, not all vehicles are space-faring -Agouri - //var/use_internal_tank = 0 - //var/internal_tank_valve = ONE_ATMOSPHERE - //var/obj/machinery/portable_atmospherics/canister/internal_tank - //var/datum/gas_mixture/cabin_air - //var/obj/machinery/atmospherics/portables_connector/connected_port = null - - var/obj/item/device/radio/radio = null - - var/max_temperature = 2500 - //var/internal_damage_threshold = 50 //health percentage below which internal damage is possible - var/internal_damage = 0 //contains bitflags - - var/list/operation_req_access = list()//required access level for mecha operation - var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment - - var/wreckage - - var/list/equipment = new - var/obj/selected - //var/max_equip = 3 - - - - -/obj/vehicle/Initialize() - . = ..() - icon_state += "-unmanned" - add_radio() - - spark_system.set_up(2, 0, src) - spark_system.attach(src) - add_cell() - removeVerb(/atom/movable/verb/pull) - log_message("[src.name]'s functions initialised. Work protocols active - Entering IDLE mode.") - - -//################ Helpers ########################################################### - - -/obj/vehicle/proc/removeVerb(verb_path) - verbs -= verb_path - -/obj/vehicle/proc/addVerb(verb_path) - verbs += verb_path - -/obj/vehicle/proc/add_cell(var/obj/item/weapon/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new /obj/item/weapon/cell/mech(src) - -/obj/vehicle/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/vehicle/proc/check_for_support() - if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src))) - return 1 - else - return 0 - -//################ Logs and messages ############################################ - - -/obj/vehicle/proc/log_message(message as text,red=null) - log.len++ - log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") - return log.len + + +/obj/vehicle + name = "Vehicle" + icon = 'icons/vehicles/vehicles.dmi' + density = TRUE + anchored = TRUE + unacidable = TRUE //To avoid the pilot-deleting shit that came with mechas + layer = MOB_LAYER + //var/can_move = 1 + var/mob/living/carbon/occupant = null + //var/step_in = 10 //make a step in step_in/10 sec. + //var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + //var/step_energy_drain = 10 + var/health = 300 //health is health + //var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + //the values in this list show how much damage will pass through, not how much will be absorbed. + var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1) + var/obj/item/weapon/cell/cell //Our power source + var/state = 0 + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + //var/dna //dna-locking the mech + var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect/effect/system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + + //inner atmos //These go in airtight.dm, not all vehicles are space-faring -Agouri + //var/use_internal_tank = 0 + //var/internal_tank_valve = ONE_ATMOSPHERE + //var/obj/machinery/portable_atmospherics/canister/internal_tank + //var/datum/gas_mixture/cabin_air + //var/obj/machinery/atmospherics/portables_connector/connected_port = null + + var/obj/item/device/radio/radio = null + + var/max_temperature = 2500 + //var/internal_damage_threshold = 50 //health percentage below which internal damage is possible + var/internal_damage = 0 //contains bitflags + + var/list/operation_req_access = list()//required access level for mecha operation + var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment + + var/wreckage + + var/list/equipment = new + var/obj/selected + //var/max_equip = 3 + + + + +/obj/vehicle/Initialize() + . = ..() + icon_state += "-unmanned" + add_radio() + + spark_system.set_up(2, 0, src) + spark_system.attach(src) + add_cell() + removeVerb(/atom/movable/verb/pull) + log_message("[src.name]'s functions initialised. Work protocols active - Entering IDLE mode.") + + +//################ Helpers ########################################################### + + +/obj/vehicle/proc/removeVerb(verb_path) + verbs -= verb_path + +/obj/vehicle/proc/addVerb(verb_path) + verbs += verb_path + +/obj/vehicle/proc/add_cell(var/obj/item/weapon/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new /obj/item/weapon/cell/mech(src) + +/obj/vehicle/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/vehicle/proc/check_for_support() + if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src))) + return 1 + else + return 0 + +//################ Logs and messages ############################################ + + +/obj/vehicle/proc/log_message(message as text,red=null) + log.len++ + log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]") + return log.len diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index 23181a9fdbd..3fabc757682 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -1,7 +1,7 @@ -/mob/living/var/suiciding = 0 - -/mob/living/verb/suicide() - set hidden = 1 - - to_chat(src, "No. Adminhelp if there is a legitimate reason, and please review our server rules.") - message_admins("[ckey] has tried to trigger the suicide verb, but it is disabled.") +/mob/living/var/suiciding = 0 + +/mob/living/verb/suicide() + set hidden = 1 + + to_chat(src, "No. Adminhelp if there is a legitimate reason, and please review our server rules.") + message_admins("[ckey] has tried to trigger the suicide verb, but it is disabled.") diff --git a/code/game/world.dm b/code/game/world.dm index d9da788dec6..0edadadc1ab 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -33,7 +33,6 @@ GLOB.timezoneOffset = get_timezone_offset() callHook("startup") - init_vchat() //Emergency Fix load_mods() //end-emergency fix @@ -338,8 +337,8 @@ var/world_topic_spam_protect_time = world.timeofday if(!rank) rank = "Admin" - var/message = "IRC-[rank] PM from IRC-[input["sender"]]: [input["msg"]]" - var/amessage = "IRC-[rank] PM from IRC-[input["sender"]] to [key_name(C)] : [input["msg"]]" + var/message = span_red("IRC-[rank] PM from IRC-[input["sender"]]: [input["msg"]]") + var/amessage = span_blue("IRC-[rank] PM from IRC-[input["sender"]] to [key_name(C)] : [input["msg"]]") C.received_irc_pm = world.time C.irc_admin = input["sender"] @@ -507,7 +506,7 @@ var/world_topic_spam_protect_time = world.timeofday s += "[station_name()]"; s += " (" - s += "" //Change this to wherever you want the hub to link to. + s += "" //Change this to wherever you want the hub to link to. // s += "[game_version]" s += "Default" //Replace this with something else. Or ever better, delete it and uncomment the game version. s += "" diff --git a/code/global.dm b/code/global.dm index 4467105f198..8d887a67512 100644 --- a/code/global.dm +++ b/code/global.dm @@ -1,13 +1,7 @@ -//#define TESTING -#if DM_VERSION < 512 -#error This compiler is out of date Please update to at least BYOND 512. -#endif - // Items that ask to be called every cycle. var/global/datum/datacore/data_core = null -var/global/list/machines = list() // ALL Machines, wether processing or not. -var/global/list/processing_machines = list() // TODO - Move into SSmachines -var/global/list/processing_power_items = list() // TODO - Move into SSmachines +var/global/list/machines = SSmachines.all_machines //I would upgrade all instances of global.machines to SSmachines.all_machines but it's used in so many places and a search returns so many matches for 'machines' that isn't a use of the global... + var/global/list/active_diseases = list() var/global/list/hud_icon_reference = list() @@ -96,8 +90,6 @@ var/list/IClog = list() var/list/OOClog = list() var/list/adminlog = list() -var/list/powernets = list() // TODO - Move into SSmachines - var/Debug2 = 0 var/datum/debug/debugobj diff --git a/code/js/byjax.dm b/code/js/byjax.dm index bf9d7789891..18b8180214d 100644 --- a/code/js/byjax.dm +++ b/code/js/byjax.dm @@ -1,50 +1,50 @@ -//this function places received data into element with specified id. -var/const/js_byjax = {" - -function replaceContent() { - var args = Array.prototype.slice.call(arguments); - var id = args\[0\]; - var content = args\[1\]; - var callback = null; - if(args\[2\]){ - callback = args\[2\]; - if(args\[3\]){ - args = args.slice(3); - } - } - var parent = document.getElementById(id); - if(typeof(parent)!=='undefined' && parent!=null){ - parent.innerHTML = content?content:''; - } - if(callback && window\[callback\]){ - window\[callback\].apply(null,args); - } -} -"} - -/* -sends data to control_id:replaceContent - -receiver - mob -control_id - window id (for windows opened with browse(), it'll be "windowname.browser") -target_element - HTML element id -new_content - HTML content -callback - js function that will be called after the data is sent -callback_args - arguments for callback function - -Be sure to include required js functions in your page, or it'll raise an exception. -*/ -/proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) - if(receiver && target_element && control_id) // && winexists(receiver, control_id)) - var/list/argums = list(target_element, new_content) - if(callback) - argums += callback - if(callback_args) - argums += callback_args - argums = list2params(argums) -/* if(callback_args) - argums += "&[list2params(callback_args)]" -*/ - receiver << output(argums,"[control_id]:replaceContent") - return - +//this function places received data into element with specified id. +var/const/js_byjax = {" + +function replaceContent() { + var args = Array.prototype.slice.call(arguments); + var id = args\[0\]; + var content = args\[1\]; + var callback = null; + if(args\[2\]){ + callback = args\[2\]; + if(args\[3\]){ + args = args.slice(3); + } + } + var parent = document.getElementById(id); + if(typeof(parent)!=='undefined' && parent!=null){ + parent.innerHTML = content?content:''; + } + if(callback && window\[callback\]){ + window\[callback\].apply(null,args); + } +} +"} + +/* +sends data to control_id:replaceContent + +receiver - mob +control_id - window id (for windows opened with browse(), it'll be "windowname.browser") +target_element - HTML element id +new_content - HTML content +callback - js function that will be called after the data is sent +callback_args - arguments for callback function + +Be sure to include required js functions in your page, or it'll raise an exception. +*/ +/proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) + if(receiver && target_element && control_id) // && winexists(receiver, control_id)) + var/list/argums = list(target_element, new_content) + if(callback) + argums += callback + if(callback_args) + argums += callback_args + argums = list2params(argums) +/* if(callback_args) + argums += "&[list2params(callback_args)]" +*/ + receiver << output(argums,"[control_id]:replaceContent") + return + diff --git a/code/js/menus.dm b/code/js/menus.dm index 69ff900db67..01137ce9630 100644 --- a/code/js/menus.dm +++ b/code/js/menus.dm @@ -1,37 +1,37 @@ -var/const/js_dropdowns = {" -function dropdowns() { - var divs = document.getElementsByTagName('div'); - var headers = new Array(); - var links = new Array(); - for(var i=0;i=0) { - elem.className = elem.className.replace('visible','hidden'); - this.className = this.className.replace('open','closed'); - this.innerHTML = this.innerHTML.replace('-','+'); - } - else { - elem.className = elem.className.replace('hidden','visible'); - this.className = this.className.replace('closed','open'); - this.innerHTML = this.innerHTML.replace('+','-'); - } - return false; - } - })(links\[i\]); - } - } -} +var/const/js_dropdowns = {" +function dropdowns() { + var divs = document.getElementsByTagName('div'); + var headers = new Array(); + var links = new Array(); + for(var i=0;i=0) { + elem.className = elem.className.replace('visible','hidden'); + this.className = this.className.replace('open','closed'); + this.innerHTML = this.innerHTML.replace('-','+'); + } + else { + elem.className = elem.className.replace('hidden','visible'); + this.className = this.className.replace('closed','open'); + this.innerHTML = this.innerHTML.replace('+','-'); + } + return false; + } + })(links\[i\]); + } + } +} "} \ No newline at end of file diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index dfd236c0d92..3dd620b0c0e 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -1,481 +1,481 @@ - -//Either pass the mob you wish to ban in the 'banned_mob' attribute, or the banckey, banip and bancid variables. If both are passed, the mob takes priority! If a mob is not passed, banckey is the minimum that needs to be passed! banip and bancid are optional. -/datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) - - if(!check_rights(R_MOD,0) && !check_rights(R_BAN)) return - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/serverip = "[world.internet_address]:[world.port]" - var/bantype_pass = 0 - var/bantype_str - switch(bantype) - if(BANTYPE_PERMA) - bantype_str = "PERMABAN" - duration = -1 - bantype_pass = 1 - if(BANTYPE_TEMP) - bantype_str = "TEMPBAN" - bantype_pass = 1 - if(BANTYPE_JOB_PERMA) - bantype_str = "JOB_PERMABAN" - duration = -1 - bantype_pass = 1 - if(BANTYPE_JOB_TEMP) - bantype_str = "JOB_TEMPBAN" - bantype_pass = 1 - if( !bantype_pass ) return - if( !istext(reason) ) return - if( !isnum(duration) ) return - - var/ckey - var/computerid - var/ip - - if(ismob(banned_mob)) - ckey = banned_mob.ckey - if(banned_mob.client) - computerid = banned_mob.client.computer_id - ip = banned_mob.client.address - else if(banckey) - ckey = ckey(banckey) - computerid = bancid - ip = banip - - var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_player WHERE ckey = '[ckey]'") - query.Execute() - var/validckey = 0 - if(query.NextRow()) - validckey = 1 - if(!validckey) - if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) //VOREStation Edit Start. - var/confirm = tgui_alert(usr, "This ckey hasn't been seen, are you sure?", "Confirm Badmin", list("Yes", "No")) - if(confirm == "No") - return //VOREStation Edit End - - var/a_ckey - var/a_computerid - var/a_ip - - if(src.owner && istype(src.owner, /client)) - a_ckey = src.owner:ckey - a_computerid = src.owner:computer_id - a_ip = src.owner:address - - var/who - for(var/client/C in GLOB.clients) - if(!who) - who = "[C]" - else - who += ", [C]" - - var/adminwho - for(var/client/C in GLOB.admins) - if(!adminwho) - adminwho = "[C]" - else - adminwho += ", [C]" - - reason = sql_sanitize_text(reason) - - var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() - to_chat(usr, "Ban saved to database.") - message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) - - - -/datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") - - if(!check_rights(R_BAN)) return - - var/bantype_str - if(bantype) - var/bantype_pass = 0 - switch(bantype) - if(BANTYPE_PERMA) - bantype_str = "PERMABAN" - bantype_pass = 1 - if(BANTYPE_TEMP) - bantype_str = "TEMPBAN" - bantype_pass = 1 - if(BANTYPE_JOB_PERMA) - bantype_str = "JOB_PERMABAN" - bantype_pass = 1 - if(BANTYPE_JOB_TEMP) - bantype_str = "JOB_TEMPBAN" - bantype_pass = 1 - if(BANTYPE_ANY_FULLBAN) - bantype_str = "ANY" - bantype_pass = 1 - if( !bantype_pass ) return - - var/bantype_sql - if(bantype_str == "ANY") - bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )" - else - bantype_sql = "bantype = '[bantype_str]'" - - var/sql = "SELECT id FROM erro_ban WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" - if(job) - sql += " AND job = '[job]'" - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/ban_id - var/ban_number = 0 //failsafe - - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() - while(query.NextRow()) - ban_id = query.item[1] - ban_number++; - - if(ban_number == 0) - to_chat(usr, "Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.") - return - - if(ban_number > 1) - to_chat(usr, "Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.") - return - - if(istext(ban_id)) - ban_id = text2num(ban_id) - if(!isnum(ban_id)) - to_chat(usr, "Database update failed due to a ban ID mismatch. Contact the database admin.") - return - - DB_ban_unban_by_id(ban_id) - -/datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) - - if(!check_rights(R_BAN)) return - - if(!isnum(banid) || !istext(param)) - to_chat(usr, "Cancelled") - return - - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]") - query.Execute() - - var/eckey = usr.ckey //Editing admin ckey - var/pckey //(banned) Player ckey - var/duration //Old duration - var/reason //Old reason - - if(query.NextRow()) - pckey = query.item[1] - duration = query.item[2] - reason = query.item[3] - else - to_chat(usr, "Invalid ban id. Contact the database admin") - return - - reason = sql_sanitize_text(reason) - var/value - - switch(param) - if("reason") - if(!value) - value = sanitize(tgui_input_text(usr, "Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null)) - value = sql_sanitize_text(value) - if(!value) - to_chat(usr, "Cancelled") - return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
                    ') WHERE id = [banid]") - update_query.Execute() - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) - if("duration") - if(!value) - value = tgui_input_number(usr, "Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) - if(!isnum(value) || !value) - to_chat(usr, "Cancelled") - return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
                    '), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) - update_query.Execute() - if("unban") - if(tgui_alert(usr, "Unban [pckey]?", "Unban?", list("Yes", "No")) == "Yes") - DB_ban_unban_by_id(banid) - return - to_chat(usr, "Cancelled") - return - -/datum/admins/proc/DB_ban_unban_by_id(var/id) - - if(!check_rights(R_BAN)) return - - var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]" - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/ban_number = 0 //failsafe - - var/pckey - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() - while(query.NextRow()) - pckey = query.item[1] - ban_number++; - - if(ban_number == 0) - to_chat(usr, "Database update failed due to a ban id not being present in the database.") - return - - if(ban_number > 1) - to_chat(usr, "Database update failed due to multiple bans having the same ID. Contact the database admin.") - return - - if(!src.owner || !istype(src.owner, /client)) - return - - var/unban_ckey = src.owner:ckey - var/unban_computerid = src.owner:computer_id - var/unban_ip = src.owner:address - - var/sql_update = "UPDATE erro_ban SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" - message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) - - var/DBQuery/query_update = dbcon.NewQuery(sql_update) - query_update.Execute() - - -/client/proc/DB_ban_panel() - set category = "Admin" - set name = "Banning Panel" - set desc = "Edit admin permissions" - - if(!holder) - return - - holder.DB_ban_panel() - - -/datum/admins/proc/DB_ban_panel(var/playerckey = null, var/adminckey = null, var/playerip = null, var/playercid = null, var/dbbantype = null, var/match = null) - if(!usr.client) - return - - if(!check_rights(R_BAN)) return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - var/output = "
                    " - - output += "" - - output += "" - output += "" - output += "
                    " - output += "

                    Banning panel

                    " - output += "
                    " - - output += "
                    [HrefTokenFormField()]" - output += "Add custom ban: (ONLY use this if you can't ban through any other method)" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
                    Ban type:Ckey:
                    IP: CID:
                    Duration: Job:
                    " - output += "Reason:

                    " - output += "" - output += "
                    " - - output += "
                    " - - output += "
                    [HrefTokenFormField()]" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
                    Search:" - output += "
                    Ckey: Admin ckey:
                    IP: CID:
                    Ban type:
                    " - output += "

                    " - output += " Match(min. 3 characters to search by key or ip, and 7 to search by cid)
                    " - output += "
                    " - output += "Please note that all jobban bans or unbans are in-effect the following round.
                    " - output += "This search shows only last 100 bans." - - if(adminckey || playerckey || playerip || playercid || dbbantype) - - adminckey = ckey(adminckey) - playerckey = ckey(playerckey) - playerip = sql_sanitize_text(playerip) - playercid = sql_sanitize_text(playercid) - - if(adminckey || playerckey || playerip || playercid || dbbantype) - - var/blcolor = "#ffeeee" //banned light - var/bdcolor = "#ffdddd" //banned dark - var/ulcolor = "#eeffee" //unbanned light - var/udcolor = "#ddffdd" //unbanned dark - var/alcolor = "#eeeeff" // auto-unbanned light - var/adcolor = "#ddddff" // auto-unbanned dark - - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - - var/adminsearch = "" - var/playersearch = "" - var/ipsearch = "" - var/cidsearch = "" - var/bantypesearch = "" - - if(!match) - if(adminckey) - adminsearch = "AND a_ckey = '[adminckey]' " - if(playerckey) - playersearch = "AND ckey = '[playerckey]' " - if(playerip) - ipsearch = "AND ip = '[playerip]' " - if(playercid) - cidsearch = "AND computerid = '[playercid]' " - else - if(adminckey && length(adminckey) >= 3) - adminsearch = "AND a_ckey LIKE '[adminckey]%' " - if(playerckey && length(playerckey) >= 3) - playersearch = "AND ckey LIKE '[playerckey]%' " - if(playerip && length(playerip) >= 3) - ipsearch = "AND ip LIKE '[playerip]%' " - if(playercid && length(playercid) >= 7) - cidsearch = "AND computerid LIKE '[playercid]%' " - - if(dbbantype) - bantypesearch = "AND bantype = " - - switch(dbbantype) - if(BANTYPE_TEMP) - bantypesearch += "'TEMPBAN' " - if(BANTYPE_JOB_PERMA) - bantypesearch += "'JOB_PERMABAN' " - if(BANTYPE_JOB_TEMP) - bantypesearch += "'JOB_TEMPBAN' " - else - bantypesearch += "'PERMABAN' " - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM erro_ban WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") - select_query.Execute() - - var/now = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") // MUST BE the same format as SQL gives us the dates in, and MUST be least to most specific (i.e. year, month, day not day, month, year) - - while(select_query.NextRow()) - var/banid = select_query.item[1] - var/bantime = select_query.item[2] - var/bantype = select_query.item[3] - var/reason = select_query.item[4] - var/job = select_query.item[5] - var/duration = select_query.item[6] - var/expiration = select_query.item[7] - var/ckey = select_query.item[8] - var/ackey = select_query.item[9] - var/unbanned = select_query.item[10] - var/unbanckey = select_query.item[11] - var/unbantime = select_query.item[12] - var/edits = select_query.item[13] - var/ip = select_query.item[14] - var/cid = select_query.item[15] - - // true if this ban has expired - var/auto = (bantype in list("TEMPBAN", "JOB_TEMPBAN")) && now > expiration // oh how I love ISO 8601 (ish) date strings - - var/lcolor = blcolor - var/dcolor = bdcolor - if(unbanned) - lcolor = ulcolor - dcolor = udcolor - else if(auto) - lcolor = alcolor - dcolor = adcolor - - var/typedesc ="" - switch(bantype) - if("PERMABAN") - typedesc = "PERMABAN" - if("TEMPBAN") - typedesc = "TEMPBAN
                    ([duration] minutes) [(unbanned || auto) ? "" : "(Edit)"]
                    Expires [expiration]
                    " - if("JOB_PERMABAN") - typedesc = "JOBBAN
                    ([job])" - if("JOB_TEMPBAN") - typedesc = "TEMP JOBBAN
                    ([job])
                    ([duration] minutes
                    Expires [expiration]
                    " - - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - if(edits) - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - if(unbanned) - output += "" - output += "" - output += "" - else if(auto) - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - - output += "
                    TYPECKEYTIME APPLIEDADMINOPTIONS
                    [typedesc][ckey][bantime][ackey][(unbanned || auto) ? "" : "Unban"]
                    IP: [ip]CIP: [cid]
                    Reason: [(unbanned || auto) ? "" : "(Edit)"] \"[reason]\"
                    EDITS
                    [edits]
                    UNBANNED by admin [unbanckey] on [unbantime]
                    EXPIRED at [expiration]
                     
                    " - - usr << browse(output,"window=lookupbans;size=900x700") + +//Either pass the mob you wish to ban in the 'banned_mob' attribute, or the banckey, banip and bancid variables. If both are passed, the mob takes priority! If a mob is not passed, banckey is the minimum that needs to be passed! banip and bancid are optional. +/datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) + + if(!check_rights(R_MOD,0) && !check_rights(R_BAN)) return + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/serverip = "[world.internet_address]:[world.port]" + var/bantype_pass = 0 + var/bantype_str + switch(bantype) + if(BANTYPE_PERMA) + bantype_str = "PERMABAN" + duration = -1 + bantype_pass = 1 + if(BANTYPE_TEMP) + bantype_str = "TEMPBAN" + bantype_pass = 1 + if(BANTYPE_JOB_PERMA) + bantype_str = "JOB_PERMABAN" + duration = -1 + bantype_pass = 1 + if(BANTYPE_JOB_TEMP) + bantype_str = "JOB_TEMPBAN" + bantype_pass = 1 + if( !bantype_pass ) return + if( !istext(reason) ) return + if( !isnum(duration) ) return + + var/ckey + var/computerid + var/ip + + if(ismob(banned_mob)) + ckey = banned_mob.ckey + if(banned_mob.client) + computerid = banned_mob.client.computer_id + ip = banned_mob.client.address + else if(banckey) + ckey = ckey(banckey) + computerid = bancid + ip = banip + + var/DBQuery/query = dbcon.NewQuery("SELECT id FROM erro_player WHERE ckey = '[ckey]'") + query.Execute() + var/validckey = 0 + if(query.NextRow()) + validckey = 1 + if(!validckey) + if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) //VOREStation Edit Start. + var/confirm = tgui_alert(usr, "This ckey hasn't been seen, are you sure?", "Confirm Badmin", list("Yes", "No")) + if(confirm == "No") + return //VOREStation Edit End + + var/a_ckey + var/a_computerid + var/a_ip + + if(src.owner && istype(src.owner, /client)) + a_ckey = src.owner:ckey + a_computerid = src.owner:computer_id + a_ip = src.owner:address + + var/who + for(var/client/C in GLOB.clients) + if(!who) + who = "[C]" + else + who += ", [C]" + + var/adminwho + for(var/client/C in GLOB.admins) + if(!adminwho) + adminwho = "[C]" + else + adminwho += ", [C]" + + reason = sql_sanitize_text(reason) + + var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" + var/DBQuery/query_insert = dbcon.NewQuery(sql) + query_insert.Execute() + to_chat(usr, "[span_blue("Ban saved to database.")]") + message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) + + + +/datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") + + if(!check_rights(R_BAN)) return + + var/bantype_str + if(bantype) + var/bantype_pass = 0 + switch(bantype) + if(BANTYPE_PERMA) + bantype_str = "PERMABAN" + bantype_pass = 1 + if(BANTYPE_TEMP) + bantype_str = "TEMPBAN" + bantype_pass = 1 + if(BANTYPE_JOB_PERMA) + bantype_str = "JOB_PERMABAN" + bantype_pass = 1 + if(BANTYPE_JOB_TEMP) + bantype_str = "JOB_TEMPBAN" + bantype_pass = 1 + if(BANTYPE_ANY_FULLBAN) + bantype_str = "ANY" + bantype_pass = 1 + if( !bantype_pass ) return + + var/bantype_sql + if(bantype_str == "ANY") + bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )" + else + bantype_sql = "bantype = '[bantype_str]'" + + var/sql = "SELECT id FROM erro_ban WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" + if(job) + sql += " AND job = '[job]'" + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/ban_id + var/ban_number = 0 //failsafe + + var/DBQuery/query = dbcon.NewQuery(sql) + query.Execute() + while(query.NextRow()) + ban_id = query.item[1] + ban_number++; + + if(ban_number == 0) + to_chat(usr, "[span_red("Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.")]") + return + + if(ban_number > 1) + to_chat(usr, "[span_red("Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.")]") + return + + if(istext(ban_id)) + ban_id = text2num(ban_id) + if(!isnum(ban_id)) + to_chat(usr, "[span_red("Database update failed due to a ban ID mismatch. Contact the database admin.")]") + return + + DB_ban_unban_by_id(ban_id) + +/datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) + + if(!check_rights(R_BAN)) return + + if(!isnum(banid) || !istext(param)) + to_chat(usr, "Cancelled") + return + + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]") + query.Execute() + + var/eckey = usr.ckey //Editing admin ckey + var/pckey //(banned) Player ckey + var/duration //Old duration + var/reason //Old reason + + if(query.NextRow()) + pckey = query.item[1] + duration = query.item[2] + reason = query.item[3] + else + to_chat(usr, "Invalid ban id. Contact the database admin") + return + + reason = sql_sanitize_text(reason) + var/value + + switch(param) + if("reason") + if(!value) + value = sanitize(tgui_input_text(usr, "Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null)) + value = sql_sanitize_text(value) + if(!value) + to_chat(usr, "Cancelled") + return + + var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
                    ') WHERE id = [banid]") + update_query.Execute() + message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) + if("duration") + if(!value) + value = tgui_input_number(usr, "Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) + if(!isnum(value) || !value) + to_chat(usr, "Cancelled") + return + + var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
                    '), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") + message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) + update_query.Execute() + if("unban") + if(tgui_alert(usr, "Unban [pckey]?", "Unban?", list("Yes", "No")) == "Yes") + DB_ban_unban_by_id(banid) + return + to_chat(usr, "Cancelled") + return + +/datum/admins/proc/DB_ban_unban_by_id(var/id) + + if(!check_rights(R_BAN)) return + + var/sql = "SELECT ckey FROM erro_ban WHERE id = [id]" + + establish_db_connection() + if(!dbcon.IsConnected()) + return + + var/ban_number = 0 //failsafe + + var/pckey + var/DBQuery/query = dbcon.NewQuery(sql) + query.Execute() + while(query.NextRow()) + pckey = query.item[1] + ban_number++; + + if(ban_number == 0) + to_chat(usr, "[span_red("Database update failed due to a ban id not being present in the database.")]") + return + + if(ban_number > 1) + to_chat(usr, "[span_red("Database update failed due to multiple bans having the same ID. Contact the database admin.")]") + return + + if(!src.owner || !istype(src.owner, /client)) + return + + var/unban_ckey = src.owner:ckey + var/unban_computerid = src.owner:computer_id + var/unban_ip = src.owner:address + + var/sql_update = "UPDATE erro_ban SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" + message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) + + var/DBQuery/query_update = dbcon.NewQuery(sql_update) + query_update.Execute() + + +/client/proc/DB_ban_panel() + set category = "Admin" + set name = "Banning Panel" + set desc = "Edit admin permissions" + + if(!holder) + return + + holder.DB_ban_panel() + + +/datum/admins/proc/DB_ban_panel(var/playerckey = null, var/adminckey = null, var/playerip = null, var/playercid = null, var/dbbantype = null, var/match = null) + if(!usr.client) + return + + if(!check_rights(R_BAN)) return + + establish_db_connection() + if(!dbcon.IsConnected()) + to_chat(usr, "[span_red("Failed to establish database connection")]") + return + + var/output = "
                    " + + output += "" + + output += "" + output += "" + output += "
                    " + output += "

                    Banning panel

                    " + output += "
                    " + + output += "
                    [HrefTokenFormField()]" + output += "Add custom ban: (ONLY use this if you can't ban through any other method)" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "
                    Ban type:Ckey:
                    IP: CID:
                    Duration: Job:
                    " + output += "Reason:

                    " + output += "" + output += "
                    " + + output += "
                    " + + output += "
                    [HrefTokenFormField()]" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "
                    Search:" + output += "
                    Ckey: Admin ckey:
                    IP: CID:
                    Ban type:
                    " + output += "

                    " + output += " Match(min. 3 characters to search by key or ip, and 7 to search by cid)
                    " + output += "
                    " + output += "Please note that all jobban bans or unbans are in-effect the following round.
                    " + output += "This search shows only last 100 bans." + + if(adminckey || playerckey || playerip || playercid || dbbantype) + + adminckey = ckey(adminckey) + playerckey = ckey(playerckey) + playerip = sql_sanitize_text(playerip) + playercid = sql_sanitize_text(playercid) + + if(adminckey || playerckey || playerip || playercid || dbbantype) + + var/blcolor = "#ffeeee" //banned light + var/bdcolor = "#ffdddd" //banned dark + var/ulcolor = "#eeffee" //unbanned light + var/udcolor = "#ddffdd" //unbanned dark + var/alcolor = "#eeeeff" // auto-unbanned light + var/adcolor = "#ddddff" // auto-unbanned dark + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + var/adminsearch = "" + var/playersearch = "" + var/ipsearch = "" + var/cidsearch = "" + var/bantypesearch = "" + + if(!match) + if(adminckey) + adminsearch = "AND a_ckey = '[adminckey]' " + if(playerckey) + playersearch = "AND ckey = '[playerckey]' " + if(playerip) + ipsearch = "AND ip = '[playerip]' " + if(playercid) + cidsearch = "AND computerid = '[playercid]' " + else + if(adminckey && length(adminckey) >= 3) + adminsearch = "AND a_ckey LIKE '[adminckey]%' " + if(playerckey && length(playerckey) >= 3) + playersearch = "AND ckey LIKE '[playerckey]%' " + if(playerip && length(playerip) >= 3) + ipsearch = "AND ip LIKE '[playerip]%' " + if(playercid && length(playercid) >= 7) + cidsearch = "AND computerid LIKE '[playercid]%' " + + if(dbbantype) + bantypesearch = "AND bantype = " + + switch(dbbantype) + if(BANTYPE_TEMP) + bantypesearch += "'TEMPBAN' " + if(BANTYPE_JOB_PERMA) + bantypesearch += "'JOB_PERMABAN' " + if(BANTYPE_JOB_TEMP) + bantypesearch += "'JOB_TEMPBAN' " + else + bantypesearch += "'PERMABAN' " + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM erro_ban WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") + select_query.Execute() + + var/now = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") // MUST BE the same format as SQL gives us the dates in, and MUST be least to most specific (i.e. year, month, day not day, month, year) + + while(select_query.NextRow()) + var/banid = select_query.item[1] + var/bantime = select_query.item[2] + var/bantype = select_query.item[3] + var/reason = select_query.item[4] + var/job = select_query.item[5] + var/duration = select_query.item[6] + var/expiration = select_query.item[7] + var/ckey = select_query.item[8] + var/ackey = select_query.item[9] + var/unbanned = select_query.item[10] + var/unbanckey = select_query.item[11] + var/unbantime = select_query.item[12] + var/edits = select_query.item[13] + var/ip = select_query.item[14] + var/cid = select_query.item[15] + + // true if this ban has expired + var/auto = (bantype in list("TEMPBAN", "JOB_TEMPBAN")) && now > expiration // oh how I love ISO 8601 (ish) date strings + + var/lcolor = blcolor + var/dcolor = bdcolor + if(unbanned) + lcolor = ulcolor + dcolor = udcolor + else if(auto) + lcolor = alcolor + dcolor = adcolor + + var/typedesc ="" + switch(bantype) + if("PERMABAN") + typedesc = "PERMABAN" + if("TEMPBAN") + typedesc = "TEMPBAN
                    ([duration] minutes) [(unbanned || auto) ? "" : "(Edit)"]
                    Expires [expiration]
                    " + if("JOB_PERMABAN") + typedesc = "JOBBAN
                    ([job])" + if("JOB_TEMPBAN") + typedesc = "TEMP JOBBAN
                    ([job])
                    ([duration] minutes
                    Expires [expiration]
                    " + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + if(edits) + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + if(unbanned) + output += "" + output += "" + output += "" + else if(auto) + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + output += "
                    TYPECKEYTIME APPLIEDADMINOPTIONS
                    [typedesc][ckey][bantime][ackey][(unbanned || auto) ? "" : "Unban"]
                    IP: [ip]CIP: [cid]
                    Reason: [(unbanned || auto) ? "" : "(Edit)"] \"[reason]\"
                    EDITS
                    [edits]
                    UNBANNED by admin [unbanckey] on [unbantime]
                    EXPIRED at [expiration]
                     
                    " + + usr << browse(output,"window=lookupbans;size=900x700") diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index efaf0795e2b..8248b3c6832 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -7,13 +7,13 @@ //Guest Checking if(!config.guests_allowed && IsGuestKey(key)) log_adminwarn("Failed Login: [key] - Guests not allowed") - message_admins("Failed Login: [key] - Guests not allowed") + message_admins(span_blue("Failed Login: [key] - Guests not allowed")) return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.") //check if the IP address is a known TOR node if(config && config.ToRban && ToRban_isbanned(address)) log_adminwarn("Failed Login: [src] - Banned: ToR") - message_admins("Failed Login: [src] - Banned: ToR") + message_admins(span_blue("Failed Login: [src] - Banned: ToR")) //ban their computer_id and ckey for posterity AddBan(ckey(key), computer_id, "Use of ToR", "Automated Ban", 0, 0) return list("reason"="Using ToR", "desc"="\nReason: The network you are using to connect has been banned.\nIf you believe this is a mistake, please request help at [config.banappeals]") @@ -25,7 +25,7 @@ . = CheckBan( ckey(key), computer_id, address ) if(.) log_adminwarn("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]") - message_admins("Failed Login: [key] id:[computer_id] ip:[address] - Banned [.["reason"]]") + message_admins(span_blue("Failed Login: [key] id:[computer_id] ip:[address] - Banned [.["reason"]]")) return . return ..() //default pager ban stuff diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 38639e917fd..9e5ff08a136 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -1,229 +1,229 @@ -var/CMinutes = null -var/savefile/Banlist - - -/proc/CheckBan(var/ckey, var/id, var/address) - if(!Banlist) // if Banlist cannot be located for some reason - LoadBans() // try to load the bans - if(!Banlist) // uh oh, can't find bans! - return 0 // ABORT ABORT ABORT - - . = list() - var/appeal - if(config && config.banappeals) - appeal = "\nFor more information on your ban, or to appeal, head to [config.banappeals]" - Banlist.cd = "/base" - if( "[ckey][id]" in Banlist.dir ) - Banlist.cd = "[ckey][id]" - if (Banlist["temp"]) - if (!GetExp(Banlist["minutes"])) - ClearTempbans() - return 0 - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" - else - Banlist.cd = "/base/[ckey][id]" - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" - .["reason"] = "ckey/id" - return . - else - for (var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - var/matches - if( ckey == Banlist["key"] ) - matches += "ckey" - if( id == Banlist["id"] ) - if(matches) - matches += "/" - matches += "id" - if( address == Banlist["ip"] ) - if(matches) - matches += "/" - matches += "ip" - - if(matches) - if(Banlist["temp"]) - if (!GetExp(Banlist["minutes"])) - ClearTempbans() - return 0 - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" - .["reason"] = matches - return . - return 0 - -/proc/UpdateTime() //No idea why i made this a proc. - CMinutes = (world.realtime / 10) / 60 - return 1 - -/hook/startup/proc/loadBans() - return LoadBans() - -/proc/LoadBans() - - Banlist = new("data/banlist.bdb") - log_admin("Loading Banlist") - - if (!length(Banlist.dir)) log_admin("Banlist is empty.") - - if (!Banlist.dir.Find("base")) - log_admin("Banlist missing base dir.") - Banlist.dir.Add("base") - Banlist.cd = "/base" - else if (Banlist.dir.Find("base")) - Banlist.cd = "/base" - - ClearTempbans() - return 1 - -/proc/ClearTempbans() - UpdateTime() - - Banlist.cd = "/base" - for (var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - if (!Banlist["key"] || !Banlist["id"]) - RemoveBan(A) - log_admin("Invalid Ban.") - message_admins("Invalid Ban.") - continue - - if (!Banlist["temp"]) continue - if (CMinutes >= Banlist["minutes"]) RemoveBan(A) - - return 1 - - -/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address) - - var/bantimestamp - - if (temp) - UpdateTime() - bantimestamp = CMinutes + minutes - - Banlist.cd = "/base" - if ( Banlist.dir.Find("[ckey][computerid]") ) - to_chat(usr, "Ban already exists.") - return 0 - else - Banlist.dir.Add("[ckey][computerid]") - Banlist.cd = "/base/[ckey][computerid]" - Banlist["key"] << ckey - Banlist["id"] << computerid - Banlist["ip"] << address - Banlist["reason"] << reason - Banlist["bannedby"] << bannedby - Banlist["temp"] << temp - if (temp) - Banlist["minutes"] << bantimestamp - admin_action_message(bannedby, ckey, "banned", reason, temp ? minutes : -1) //VOREStation Add - return 1 - -/proc/RemoveBan(foldername) - var/key - var/id - - Banlist.cd = "/base/[foldername]" - Banlist["key"] >> key - Banlist["id"] >> id - Banlist.cd = "/base" - - if (!Banlist.dir.Remove(foldername)) return 0 - - if(!usr) - log_admin("Ban Expired: [key]") - message_admins("Ban Expired: [key]") - else - ban_unban_log_save("[key_name_admin(usr)] unbanned [key]") - log_admin("[key_name_admin(usr)] unbanned [key]") - message_admins("[key_name_admin(usr)] unbanned: [key]") - feedback_inc("ban_unban",1) - usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN) - for (var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - if (key == Banlist["key"] /*|| id == Banlist["id"]*/) - Banlist.cd = "/base" - Banlist.dir.Remove(A) - continue - admin_action_message(usr.key, key, "unbanned", "\[Unban\]", 0) //VOREStation Add - return 1 - -/proc/GetExp(minutes as num) - UpdateTime() - var/exp = minutes - CMinutes - if (exp <= 0) - return 0 - else - var/timeleftstring - if (exp >= 1440) //1440 = 1 day in minutes - timeleftstring = "[round(exp / 1440, 0.1)] Days" - else if (exp >= 60) //60 = 1 hour in minutes - timeleftstring = "[round(exp / 60, 0.1)] Hours" - else - timeleftstring = "[exp] Minutes" - return timeleftstring - -/datum/admins/proc/unbanpanel() - var/count = 0 - var/dat - //var/dat = "
                    Unban Player: (U) = Unban , (E) = Edit Ban (Total
                    " - Banlist.cd = "/base" - for (var/A in Banlist.dir) - count++ - Banlist.cd = "/base/[A]" - var/ref = "\ref[src]" - var/key = Banlist["key"] - var/id = Banlist["id"] - var/ip = Banlist["ip"] - var/reason = Banlist["reason"] - var/by = Banlist["bannedby"] - var/expiry - if(Banlist["temp"]) - expiry = GetExp(Banlist["minutes"]) - if(!expiry) expiry = "Removal Pending" - else expiry = "Permaban" - - dat += text("") - - dat += "
                    (U)(E) Key: [key]ComputerID: [id]IP: [ip] [expiry](By: [by])(Reason: [reason])
                    " - dat = "
                    Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
                    [dat]" - usr << browse(dat, "window=unbanp;size=875x400") - -//////////////////////////////////// DEBUG //////////////////////////////////// - -/proc/CreateBans() - - UpdateTime() - - var/i - var/last - - for(i=0, i<1001, i++) - var/a = pick(1,0) - var/b = pick(1,0) - if(b) - Banlist.cd = "/base" - Banlist.dir.Add("trash[i]trashid[i]") - Banlist.cd = "/base/trash[i]trashid[i]" - to_chat(Banlist["key"], "trash[i]") - else - Banlist.cd = "/base" - Banlist.dir.Add("[last]trashid[i]") - Banlist.cd = "/base/[last]trashid[i]" - Banlist["key"] << last - to_chat(Banlist["id"], "trashid[i]") - to_chat(Banlist["reason"], "Trashban[i].") - Banlist["temp"] << a - Banlist["minutes"] << CMinutes + rand(1,2000) - to_chat(Banlist["bannedby"], "trashmin") - last = "trash[i]" - - Banlist.cd = "/base" - -/proc/ClearAllBans() - Banlist.cd = "/base" - for (var/A in Banlist.dir) - RemoveBan(A) +var/CMinutes = null +var/savefile/Banlist + + +/proc/CheckBan(var/ckey, var/id, var/address) + if(!Banlist) // if Banlist cannot be located for some reason + LoadBans() // try to load the bans + if(!Banlist) // uh oh, can't find bans! + return 0 // ABORT ABORT ABORT + + . = list() + var/appeal + if(config && config.banappeals) + appeal = "\nFor more information on your ban, or to appeal, head to [config.banappeals]" + Banlist.cd = "/base" + if( "[ckey][id]" in Banlist.dir ) + Banlist.cd = "[ckey][id]" + if (Banlist["temp"]) + if (!GetExp(Banlist["minutes"])) + ClearTempbans() + return 0 + else + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" + else + Banlist.cd = "/base/[ckey][id]" + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" + .["reason"] = "ckey/id" + return . + else + for (var/A in Banlist.dir) + Banlist.cd = "/base/[A]" + var/matches + if( ckey == Banlist["key"] ) + matches += "ckey" + if( id == Banlist["id"] ) + if(matches) + matches += "/" + matches += "id" + if( address == Banlist["ip"] ) + if(matches) + matches += "/" + matches += "ip" + + if(matches) + if(Banlist["temp"]) + if (!GetExp(Banlist["minutes"])) + ClearTempbans() + return 0 + else + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" + else + .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMANENT\nBy: [Banlist["bannedby"]][appeal]" + .["reason"] = matches + return . + return 0 + +/proc/UpdateTime() //No idea why i made this a proc. + CMinutes = (world.realtime / 10) / 60 + return 1 + +/hook/startup/proc/loadBans() + return LoadBans() + +/proc/LoadBans() + + Banlist = new("data/banlist.bdb") + log_admin("Loading Banlist") + + if (!length(Banlist.dir)) log_admin("Banlist is empty.") + + if (!Banlist.dir.Find("base")) + log_admin("Banlist missing base dir.") + Banlist.dir.Add("base") + Banlist.cd = "/base" + else if (Banlist.dir.Find("base")) + Banlist.cd = "/base" + + ClearTempbans() + return 1 + +/proc/ClearTempbans() + UpdateTime() + + Banlist.cd = "/base" + for (var/A in Banlist.dir) + Banlist.cd = "/base/[A]" + if (!Banlist["key"] || !Banlist["id"]) + RemoveBan(A) + log_admin("Invalid Ban.") + message_admins("Invalid Ban.") + continue + + if (!Banlist["temp"]) continue + if (CMinutes >= Banlist["minutes"]) RemoveBan(A) + + return 1 + + +/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address) + + var/bantimestamp + + if (temp) + UpdateTime() + bantimestamp = CMinutes + minutes + + Banlist.cd = "/base" + if ( Banlist.dir.Find("[ckey][computerid]") ) + to_chat(usr, "Ban already exists.") + return 0 + else + Banlist.dir.Add("[ckey][computerid]") + Banlist.cd = "/base/[ckey][computerid]" + Banlist["key"] << ckey + Banlist["id"] << computerid + Banlist["ip"] << address + Banlist["reason"] << reason + Banlist["bannedby"] << bannedby + Banlist["temp"] << temp + if (temp) + Banlist["minutes"] << bantimestamp + admin_action_message(bannedby, ckey, "banned", reason, temp ? minutes : -1) //VOREStation Add + return 1 + +/proc/RemoveBan(foldername) + var/key + var/id + + Banlist.cd = "/base/[foldername]" + Banlist["key"] >> key + Banlist["id"] >> id + Banlist.cd = "/base" + + if (!Banlist.dir.Remove(foldername)) return 0 + + if(!usr) + log_admin("Ban Expired: [key]") + message_admins("Ban Expired: [key]") + else + ban_unban_log_save("[key_name_admin(usr)] unbanned [key]") + log_admin("[key_name_admin(usr)] unbanned [key]") + message_admins("[key_name_admin(usr)] unbanned: [key]") + feedback_inc("ban_unban",1) + usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN) + for (var/A in Banlist.dir) + Banlist.cd = "/base/[A]" + if (key == Banlist["key"] /*|| id == Banlist["id"]*/) + Banlist.cd = "/base" + Banlist.dir.Remove(A) + continue + admin_action_message(usr.key, key, "unbanned", "\[Unban\]", 0) //VOREStation Add + return 1 + +/proc/GetExp(minutes as num) + UpdateTime() + var/exp = minutes - CMinutes + if (exp <= 0) + return 0 + else + var/timeleftstring + if (exp >= 1440) //1440 = 1 day in minutes + timeleftstring = "[round(exp / 1440, 0.1)] Days" + else if (exp >= 60) //60 = 1 hour in minutes + timeleftstring = "[round(exp / 60, 0.1)] Hours" + else + timeleftstring = "[exp] Minutes" + return timeleftstring + +/datum/admins/proc/unbanpanel() + var/count = 0 + var/dat + //var/dat = "
                    Unban Player:(U) = Unban , (E) = Edit Ban (Total
                    " + Banlist.cd = "/base" + for (var/A in Banlist.dir) + count++ + Banlist.cd = "/base/[A]" + var/ref = "\ref[src]" + var/key = Banlist["key"] + var/id = Banlist["id"] + var/ip = Banlist["ip"] + var/reason = Banlist["reason"] + var/by = Banlist["bannedby"] + var/expiry + if(Banlist["temp"]) + expiry = GetExp(Banlist["minutes"]) + if(!expiry) expiry = "Removal Pending" + else expiry = "Permaban" + + dat += text("") + + dat += "
                    (U)(E) Key: [key]ComputerID: [id]IP: [ip] [expiry](By: [by])(Reason: [reason])
                    " + dat = "
                    Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
                    [dat]" + usr << browse(dat, "window=unbanp;size=875x400") + +//////////////////////////////////// DEBUG //////////////////////////////////// + +/proc/CreateBans() + + UpdateTime() + + var/i + var/last + + for(i=0, i<1001, i++) + var/a = pick(1,0) + var/b = pick(1,0) + if(b) + Banlist.cd = "/base" + Banlist.dir.Add("trash[i]trashid[i]") + Banlist.cd = "/base/trash[i]trashid[i]" + to_chat(Banlist["key"], "trash[i]") + else + Banlist.cd = "/base" + Banlist.dir.Add("[last]trashid[i]") + Banlist.cd = "/base/[last]trashid[i]" + Banlist["key"] << last + to_chat(Banlist["id"], "trashid[i]") + to_chat(Banlist["reason"], "Trashban[i].") + Banlist["temp"] << a + Banlist["minutes"] << CMinutes + rand(1,2000) + to_chat(Banlist["bannedby"], "trashmin") + last = "trash[i]" + + Banlist.cd = "/base" + +/proc/ClearAllBans() + Banlist.cd = "/base" + for (var/A in Banlist.dir) + RemoveBan(A) diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm index dfe4ba88e23..a3a5863cec4 100644 --- a/code/modules/admin/ToRban.dm +++ b/code/modules/admin/ToRban.dm @@ -1,89 +1,89 @@ -//By Carnwennan -//fetches an external list and processes it into a list of ip addresses. -//It then stores the processed list into a savefile for later use -#define TORFILE "data/ToR_ban.bdb" -#define TOR_UPDATE_INTERVAL 216000 //~6 hours - -/proc/ToRban_isbanned(var/ip_address) - var/savefile/F = new(TORFILE) - if(F) - if( ip_address in F.dir ) - return 1 - return 0 - -/proc/ToRban_autoupdate() - var/savefile/F = new(TORFILE) - if(F) - var/last_update - F["last_update"] >> last_update - if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while - ToRban_update() - return - -/proc/ToRban_update() - spawn(0) - log_misc("Downloading updated ToR data...") - var/http[] = world.Export("https://check.torproject.org/exit-addresses") - - var/list/rawlist = file2list(http["CONTENT"]) - if(rawlist.len) - fdel(TORFILE) - var/savefile/F = new(TORFILE) - for( var/line in rawlist ) - if(!line) continue - if( copytext(line,1,12) == "ExitAddress" ) - var/cleaned = copytext(line,13,length(line)-19) - if(!cleaned) continue - F[cleaned] << 1 - F["last_update"] << world.realtime - log_misc("ToR data updated!") - if(usr) - to_chat(usr, "ToRban updated.") - return - log_misc("ToR data update aborted: no data.") - return - -/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) - set name = "ToRban" - set category = "Server" - if(!holder) return - switch(task) - if("update") - ToRban_update() - if("toggle") - if(config) - if(config.ToRban) - config.ToRban = 0 - message_admins("ToR banning disabled.") - else - config.ToRban = 1 - message_admins("ToR banning enabled.") - if("show") - var/savefile/F = new(TORFILE) - var/dat - if( length(F.dir) ) - for( var/i=1, i<=length(F.dir), i++ ) - dat += "" - dat = "
                    #[i] [F.dir[i]]
                    [dat]
                    " - else - dat = "No addresses in list." - src << browse(dat,"window=ToRban_show") - if("remove") - var/savefile/F = new(TORFILE) - var/choice = tgui_input_list(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban", F.dir) - if(choice) - F.dir.Remove(choice) - to_chat(src, "Address removed") - if("remove all") - to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") - if("find") - var/input = tgui_input_text(src,"Please input an IP address to search for:","Find ToR ban",null) - if(input) - if(ToRban_isbanned(input)) - to_chat(src, "Address is a known ToR address") - else - to_chat(src, "Address is not a known ToR address") - return - -#undef TORFILE -#undef TOR_UPDATE_INTERVAL +//By Carnwennan +//fetches an external list and processes it into a list of ip addresses. +//It then stores the processed list into a savefile for later use +#define TORFILE "data/ToR_ban.bdb" +#define TOR_UPDATE_INTERVAL 216000 //~6 hours + +/proc/ToRban_isbanned(var/ip_address) + var/savefile/F = new(TORFILE) + if(F) + if( ip_address in F.dir ) + return 1 + return 0 + +/proc/ToRban_autoupdate() + var/savefile/F = new(TORFILE) + if(F) + var/last_update + F["last_update"] >> last_update + if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while + ToRban_update() + return + +/proc/ToRban_update() + spawn(0) + log_misc("Downloading updated ToR data...") + var/http[] = world.Export("https://check.torproject.org/exit-addresses") + + var/list/rawlist = file2list(http["CONTENT"]) + if(rawlist.len) + fdel(TORFILE) + var/savefile/F = new(TORFILE) + for( var/line in rawlist ) + if(!line) continue + if( copytext(line,1,12) == "ExitAddress" ) + var/cleaned = copytext(line,13,length(line)-19) + if(!cleaned) continue + F[cleaned] << 1 + F["last_update"] << world.realtime + log_misc("ToR data updated!") + if(usr) + to_chat(usr, "ToRban updated.") + return + log_misc("ToR data update aborted: no data.") + return + +/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) + set name = "ToRban" + set category = "Server" + if(!holder) return + switch(task) + if("update") + ToRban_update() + if("toggle") + if(config) + if(config.ToRban) + config.ToRban = 0 + message_admins(span_red("ToR banning disabled.")) + else + config.ToRban = 1 + message_admins(span_green("ToR banning enabled.")) + if("show") + var/savefile/F = new(TORFILE) + var/dat + if( length(F.dir) ) + for( var/i=1, i<=length(F.dir), i++ ) + dat += "#[i] [F.dir[i]]" + dat = "[dat]
                    " + else + dat = "No addresses in list." + src << browse(dat,"window=ToRban_show") + if("remove") + var/savefile/F = new(TORFILE) + var/choice = tgui_input_list(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban", F.dir) + if(choice) + F.dir.Remove(choice) + to_chat(src, "Address removed") + if("remove all") + to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") + if("find") + var/input = tgui_input_text(src,"Please input an IP address to search for:","Find ToR ban",null) + if(input) + if(ToRban_isbanned(input)) + to_chat(src, "[span_orange("Address is a known ToR address")]") + else + to_chat(src, "Address is not a known ToR address") + return + +#undef TORFILE +#undef TOR_UPDATE_INTERVAL diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 8fdc8edc131..ce09b0dd8fc 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1,1590 +1,1596 @@ -var/global/floorIsLava = 0 - - -//////////////////////////////// -/proc/message_admins(var/msg) - msg = "ADMIN LOG: [msg]" - //log_adminwarn(msg) //log_and_message_admins is for this - - for(var/client/C in GLOB.admins) - if((R_ADMIN|R_MOD) & C.holder.rights) - to_chat(C,msg) - -/proc/msg_admin_attack(var/text) //Toggleable Attack Messages - var/rendered = "ATTACK: [text]" - for(var/client/C in GLOB.admins) - if((R_ADMIN|R_MOD) & C.holder.rights) - if(C.is_preference_enabled(/datum/client_preference/mod/show_attack_logs)) - var/msg = rendered - to_chat(C,msg) - -/proc/admin_notice(var/message, var/rights) - for(var/mob/M in mob_list) - if(check_rights(rights, 0, M)) - to_chat(M,message) - -///////////////////////////////////////////////////////////////////////////////////////////////Panels - -/datum/admins/proc/show_player_panel(var/mob/M in mob_list) - set category = "Admin" - set name = "Show Player Panel" - set desc="Edit player (respawn, ban, heal, etc)" - - if(!M) - to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") - return - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - - var/body = "Options for [M.key]" - body += "Options panel for [M]" - if(M.client) - body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" - - if(istype(M, /mob/new_player)) - body += " Hasn't Entered Game " - else - body += " \[Heal\] " - - if(M.client) - body += "
                    First connection: [M.client.player_age] days ago" - body += "
                    BYOND account created: [M.client.account_join_date]" - body += "
                    BYOND account age (days): [M.client.account_age]" - - body += {" -

                    \[ - VV - - TP - - PM - - SM - - [admin_jump_link(M, src)]\]
                    - Mob type: [M.type]
                    - Inactivity time: [M.client ? "[M.client.inactivity/600] minutes" : "Logged out"]

                    - Kick | - Warn | - Ban | - Jobban | - Notes - "} - - if(M.client) - body += "| Prison | " - body += "\ Send back to Lobby | " - var/muted = M.client.prefs.muted - body += {"
                    Mute: - \[IC | - OOC | - PRAY | - ADMINHELP | - DEADCHAT\] - (toggle all) - "} - - body += {"

                    - Jump to | - Get | - Send To -

                    - [check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "Traitor panel | " : "" ] - Narrate to | - Subtle message - "} - - if (M.client) - if(!istype(M, /mob/new_player)) - body += "

                    " - body += "Transformation:" - body += "
                    " - - //Monkey - if(issmall(M)) - body += "Monkeyized | " - else - body += "Monkeyize | " - - //Corgi - if(iscorgi(M)) - body += "Corgized | " - else - body += "Corgize | " - - //AI / Cyborg - if(isAI(M)) - body += "Is an AI " - else if(ishuman(M)) - body += {"Make AI | - Make Robot | - Make Alien - "} - - //Simple Animals - if(isanimal(M)) - body += "Re-Animalize | " - else - body += "Animalize | " - - body += "Respawn | " - - // DNA2 - Admin Hax - if(M.dna && iscarbon(M)) - body += "

                    " - body += "DNA Blocks:
                    " - var/bname - for(var/block=1;block<=DNA_SE_LENGTH;block++) - if(((block-1)%5)==0) - body += "" - bname = assigned_blocks[block] - body += "" - body += "
                     12345
                    [block-1]" - if(bname) - var/bstate=M.dna.GetSEState(block) - var/bcolor="[(bstate)?"#006600":"#ff0000"]" - body += "[bname][block]" - else - body += "[block]" - body+="
                    " - - body += {"

                    - Rudimentary transformation:
                    These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

                    - Observer | - \[ Xenos: Larva - Drone - Hunter - Sentinel - Queen \] | - \[ Crew: Human - Unathi - Tajaran - Skrell \] | \[ - Nymph - Diona \] | - \[ slime: Baby, - Adult \] - Monkey | - Cyborg | - Cat | - Runtime | - Corgi | - Ian | - Crab | - Coffee | - \[ Construct: Armoured , - Builder , - Wraith \] - Shade -
                    - "} - body += {"

                    - Other actions: -
                    - Forcesay - "} - if (M.client) - body += {" | - Thunderdome 1 | - Thunderdome 2 | - Thunderdome Admin | - Thunderdome Observer | - "} - // language toggles - body += "

                    Languages:
                    " - var/f = 1 - for(var/k in GLOB.all_languages) - var/datum/language/L = GLOB.all_languages[k] - if(!(L.flags & INNATE)) - if(!f) body += " | " - else f = 0 - if(L in M.languages) - body += "[k]" - else - body += "[k]" - - body += {"
                    - - "} - - usr << browse(body, "window=adminplayeropts;size=550x515") - feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/player_info/var/author // admin who authored the information -/datum/player_info/var/rank //rank of admin who made the notes -/datum/player_info/var/content // text content of the information -/datum/player_info/var/timestamp // Because this is bloody annoying - -/datum/admins/proc/PlayerNotes() - set category = "Admin" - set name = "Player Notes" - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - PlayerNotesPage(1) - -/datum/admins/proc/PlayerNotesFilter() - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - var/filter = tgui_input_text(usr, "Filter string (case-insensitive regex)", "Player notes filter") - PlayerNotesPage(1, filter) - -/datum/admins/proc/PlayerNotesPage(page, filter) - var/savefile/S=new("data/player_notes.sav") - var/list/note_keys - S >> note_keys - - if(note_keys) - note_keys = sortList(note_keys) - - var/datum/tgui_module/player_notes/A = new(src) - A.ckeys = note_keys - A.tgui_interact(usr) - - -/datum/admins/proc/player_has_info(var/key as text) - var/savefile/info = new("data/player_saves/[copytext(key, 1, 2)]/[key]/info.sav") - var/list/infos - info >> infos - if(!infos || !infos.len) return 0 - else return 1 - - -/datum/admins/proc/show_player_info(var/key as text) - set category = "Admin" - set name = "Show Player Info" - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - - var/datum/tgui_module/player_notes_info/A = new(src) - A.key = key - A.tgui_interact(usr) - - -/datum/admins/proc/access_news_network() //MARKER - set category = "Fun" - set name = "Access Newscaster Network" - set desc = "Allows you to view, add and edit news feeds." - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - var/dat - dat = text("Admin Newscaster

                    Admin Newscaster Unit

                    ") - - switch(admincaster_screen) - if(0) - dat += {"Welcome to the admin newscaster.
                    Here you can add, edit and censor every newspiece on the network. -
                    Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units. -
                    Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things! - "} - if(news_network.wanted_issue) - dat+= "
                    Read Wanted Issue" - - dat+= {"

                    Create Feed Channel -
                    View Feed Channels -
                    Submit new Feed story -

                    Exit - "} - - var/wanted_already = 0 - if(news_network.wanted_issue) - wanted_already = 1 - - dat+={"
                    Feed Security functions:
                    -
                    [(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue -
                    Censor Feed Stories -
                    Mark Feed Channel with [using_map.company_name] D-Notice (disables and locks the channel. -

                    The newscaster recognises you as:
                    [src.admincaster_signature]
                    - "} - if(1) - dat+= "Station Feed Channels
                    " - if( isemptylist(news_network.network_channels) ) - dat+="No active channels found..." - else - for(var/datum/feed_channel/CHANNEL in news_network.network_channels) - if(CHANNEL.is_admin_channel) - dat+="[CHANNEL.channel_name]
                    " - else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
                    " - dat+={"

                    Refresh -
                    Back - "} - - if(2) - dat+={" - Creating new Feed Channel... -
                    Channel Name: [src.admincaster_feed_channel.channel_name]
                    - Channel Author: [src.admincaster_signature]
                    - Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

                    -
                    Submit

                    Cancel
                    - "} - if(3) - dat+={" - Creating new Feed Message... -
                    Receiving Channel: [src.admincaster_feed_channel.channel_name]
                    - Message Author: [src.admincaster_signature]
                    - Message Body: [src.admincaster_feed_message.body]
                    -
                    Submit

                    Cancel
                    - "} - if(4) - dat+={" - Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

                    -
                    Return
                    - "} - if(5) - dat+={" - Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

                    -
                    Return
                    - "} - if(6) - dat+="ERROR: Could not submit Feed story to Network.

                    " - if(src.admincaster_feed_channel.channel_name=="") - dat+="•Invalid receiving channel name.
                    " - if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]" || admincaster_feed_message.title == "") - dat+="•Invalid message body.
                    " - dat+="
                    Return
                    " - if(7) - dat+="ERROR: Could not submit Feed Channel to Network.

                    " - if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") - dat+="•Invalid channel name.
                    " - var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(check) - dat+="•Channel name already in use.
                    " - dat+="
                    Return
                    " - if(9) - dat+="[src.admincaster_feed_channel.channel_name]: \[created by: [src.admincaster_feed_channel.author]\]
                    " - if(src.admincaster_feed_channel.censored) - dat+={" - ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.
                    - No further feed story additions are allowed while the D-Notice is in effect.

                    - "} - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
                    " - else - var/i = 0 - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - i++ - //dat+="-[MESSAGE.body]
                    " - var/pic_data - if(MESSAGE.img) - usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") - pic_data+="
                    " - dat+= get_newspaper_content(MESSAGE.title, MESSAGE.body, MESSAGE.author,"#d4cec1", pic_data) - dat+="
                    " - dat+="\[Story by [MESSAGE.author] - [MESSAGE.time_stamp]\]
                    " - dat+={" -

                    Refresh -
                    Back - "} - if(10) - dat+={" - [using_map.company_name] Feed Censorship Tool
                    - NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
                    - Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
                    -
                    Select Feed channel to get Stories from:
                    - "} - if(isemptylist(news_network.network_channels)) - dat+="No feed channels found active...
                    " - else - for(var/datum/feed_channel/CHANNEL in news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
                    " - dat+="
                    Cancel" - if(11) - dat+={" - [using_map.company_name] D-Notice Handler
                    - A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's - morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed - stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
                    - "} - if(isemptylist(news_network.network_channels)) - dat+="No feed channels found active...
                    " - else - for(var/datum/feed_channel/CHANNEL in news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
                    " - - dat+="
                    Back" - if(12) - dat+={" - [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
                    - [(src.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]
                    - "} - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
                    " - else - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+={" - -[MESSAGE.body]
                    \[Story by [MESSAGE.author]\]
                    - [(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]
                    - "} - dat+="
                    Back" - if(13) - dat+={" - [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
                    - Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
                    - "} - if(src.admincaster_feed_channel.censored) - dat+={" - ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.
                    - No further feed story additions are allowed while the D-Notice is in effect.

                    - "} - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
                    " - else - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+="-[MESSAGE.body]
                    \[Story by [MESSAGE.author]\]
                    " - - dat+="
                    Back" - if(14) - dat+="Wanted Issue Handler:" - var/wanted_already = 0 - var/end_param = 1 - if(news_network.wanted_issue) - wanted_already = 1 - end_param = 2 - if(wanted_already) - dat+="
                    A wanted issue is already in Feed Circulation. You can edit or cancel it below.
                    " - dat+={" -
                    - Criminal Name: [src.admincaster_feed_message.author]
                    - Description: [src.admincaster_feed_message.body]
                    - "} - if(wanted_already) - dat+="Wanted Issue created by: [news_network.wanted_issue.backup_author]
                    " - else - dat+="Wanted Issue will be created under prosecutor: [src.admincaster_signature]
                    " - dat+="
                    [(wanted_already) ? ("Edit Issue") : ("Submit")]" - if(wanted_already) - dat+="
                    Take down Issue" - dat+="
                    Cancel" - if(15) - dat+={" - Wanted issue for [src.admincaster_feed_message.author] is now in Network Circulation.

                    -
                    Return
                    - "} - if(16) - dat+="ERROR: Wanted Issue rejected by Network.

                    " - if(src.admincaster_feed_message.author =="" || src.admincaster_feed_message.author == "\[REDACTED\]") - dat+="•Invalid name for person wanted.
                    " - if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") - dat+="•Invalid description.
                    " - dat+="
                    Return
                    " - if(17) - dat+={" - Wanted Issue successfully deleted from Circulation
                    -
                    Return
                    - "} - if(18) - dat+={" - -- STATIONWIDE WANTED ISSUE --
                    \[Submitted by: [news_network.wanted_issue.backup_author]\]
                    - Criminal: [news_network.wanted_issue.author]
                    - Description: [news_network.wanted_issue.body]
                    - Photo:: - "} - if(news_network.wanted_issue.img) - usr << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png") - dat+="
                    " - else - dat+="None" - dat+="
                    Back
                    " - if(19) - dat+={" - Wanted issue for [src.admincaster_feed_message.author] successfully edited.

                    -
                    Return
                    - "} - else - dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - - //to_world("Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") - //to_world("Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") - usr << browse(dat, "window=admincaster_main;size=400x600") - onclose(usr, "admincaster_main") - - - -/datum/admins/proc/Jobbans() - if(!check_rights(R_BAN)) return - - var/dat = "Job Bans!
                    " - for(var/t in jobban_keylist) - var/r = t - if( findtext(r,"##") ) - r = copytext( r, 1, findtext(r,"##") )//removes the description - dat += text("") - dat += "
                    [t] (unban)
                    " - usr << browse(dat, "window=ban;size=400x400") - -/datum/admins/proc/Game() - if(!check_rights(0)) return - - var/dat = {" -
                    Game Panel

                    \n - Change Game Mode
                    - "} - if(master_mode == "secret") - dat += "(Force Secret Mode)
                    " - - dat += {" -
                    - Create Object
                    - Quick Create Object
                    - Create Turf
                    - Create Mob
                    -
                    Edit Airflow Settings
                    - Edit Phoron Settings
                    - Choose a default ZAS setting
                    - "} - - usr << browse(dat, "window=admin2;size=210x280") - return - -/datum/admins/proc/Secrets(var/datum/admin_secret_category/active_category = null) - if(!check_rights(0)) return - - // Print the header with category selection buttons. - var/dat = "The first rule of adminbuse is: you don't talk about the adminbuse.
                    " - for(var/datum/admin_secret_category/category in admin_secrets.categories) - if(!category.can_view(usr)) - continue - dat += "[category.name] " - dat += "
                    " - - // If a category is selected, print its description and then options - if(istype(active_category) && active_category.can_view(usr)) - dat += "[active_category.name]
                    " - if(active_category.desc) - dat += "[active_category.desc]
                    " - for(var/datum/admin_secret_item/item in active_category.items) - if(!item.can_view(usr)) - continue - dat += "[item.name()]
                    " - dat += "
                    " - - var/datum/browser/popup = new(usr, "secrets", "Secrets", 500, 500) - popup.set_content(dat) - popup.open() - return - -/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge -//i.e. buttons/verbs - - -/datum/admins/proc/restart() - set category = "Server" - set name = "Restart" - set desc="Restarts the world" - if (!usr.client.holder) - return - var/confirm = alert(usr, "Restart the game world?", "Restart", "Yes", "Cancel") // Not tgui_alert for safety - if(confirm == "Cancel") - return - if(confirm == "Yes") - to_world("Restarting world! Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!") - log_admin("[key_name(usr)] initiated a reboot.") - - feedback_set_details("end_error","admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]") - feedback_add_details("admin_verb","R") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(blackbox) - blackbox.save_all_data_to_sql() - - sleep(50) - world.Reboot() - - -/datum/admins/proc/announce() - set category = "Special Verbs" - set name = "Announce" - set desc="Announce your desires to the world" - if(!check_rights(0)) return - - var/message = tgui_input_text(usr, "Global message to send:", "Admin Announce", multiline = TRUE, prevent_enter = TRUE) - if(message) - if(!check_rights(R_SERVER,0)) - message = sanitize(message, 500, extra = 0) - message = replacetext(message, "\n", "
                    ") // required since we're putting it in a

                    tag - to_world("[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:

                    [message]

                    ") - log_admin("Announce: [key_name(usr)] : [message]") - feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -//VOREStation Edit to this verb for the purpose of making it compliant with the annunciator system -var/datum/announcement/priority/admin_pri_announcer = new -var/datum/announcement/minor/admin_min_announcer = new -/datum/admins/proc/intercom() - set category = "Fun" - set name = "Intercom Msg" - set desc = "Send an intercom message, like an arrivals announcement." - if(!check_rights(0)) return - - var/channel = tgui_input_list(usr, "Channel for message:","Channel", radiochannels) - - if(channel) //They picked a channel - var/sender = tgui_input_text(usr, "Name of sender (max 75):", "Announcement", "Announcement Computer") - - if(sender) //They put a sender - sender = sanitize(sender, 75, extra = 0) - var/message = tgui_input_text(usr, "Message content (max 500):", "Contents", "This is a test of the announcement system.", multiline = TRUE, prevent_enter = TRUE) - var/msgverb = tgui_input_text(usr, "Name of verb (Such as 'states', 'says', 'asks', etc):", "Verb", "says") - if(message) //They put a message - message = sanitize(message, 500, extra = 0) - //VOREStation Edit Start - if(msgverb) - msgverb = sanitize(msgverb, 50, extra = 0) - else - msgverb = "states" - global_announcer.autosay("[message]", "[sender]", "[channel == "Common" ? null : channel]", states = msgverb) //Common is a weird case, as it's not a "channel", it's just talking into a radio without a channel set. - //VOREStation Edit End - log_admin("Intercom: [key_name(usr)] : [sender]:[message]") - - feedback_add_details("admin_verb","IN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/intercom_convo() - set category = "Fun" - set name = "Intercom Convo" - set desc = "Send an intercom conversation, like several uses of the Intercom Msg verb." - set waitfor = FALSE //Why bother? We have some sleeps. You can leave tho! - if(!check_rights(0)) return - - var/channel = tgui_input_list(usr, "Channel for message:","Channel", radiochannels) - - if(!channel) //They picked a channel - return - - var/speech_verb = tgui_alert(usr, "What speech verb to use for the conversation?", "Type", list("states", "says")) - if(!speech_verb) - return - - to_chat(usr, "Intercom Convo Directions
                    Start the conversation with the sender, a pipe (|), and then the message on one line. Then hit enter to \ - add another line, and type a (whole) number of seconds to pause between that message, and the next message, then repeat the message syntax up to 20 times. For example:
                    \ - --- --- ---
                    \ - Some Guy|Hello guys, what's up?
                    \ - 5
                    \ - Other Guy|Hey, good to see you.
                    \ - 5
                    \ - Some Guy|Yeah, you too.
                    \ - --- --- ---
                    \ - The above will result in those messages playing, with a 5 second gap between each. Maximum of 20 messages allowed.
                    ") - - var/list/decomposed - var/message = tgui_input_text(usr,"See your chat box for instructions. Keep a copy elsewhere in case it is rejected when you click OK.", "Input Conversation", "", multiline = TRUE, prevent_enter = TRUE) - - if(!message) - return - - //Split on pipe or \n - decomposed = splittext(message,regex("\\||$","m")) - decomposed += "0" //Tack on a final 0 sleep to make 3-per-message evenly - - //Time to find how they screwed up. - //Wasn't the right length - if((decomposed.len) % 3) //+1 to accomidate the lack of a wait time for the last message - to_chat(usr, "You passed [decomposed.len] segments (senders+messages+pauses). You must pass a multiple of 3, minus 1 (no pause after the last message). That means a sender and message on every other line (starting on the first), separated by a pipe character (|), and a number every other line that is a pause in seconds.") - return - - //Too long a conversation - if((decomposed.len / 3) > 20) - to_chat(usr, "This conversation is too long! 20 messages maximum, please.") - return - - //Missed some sleeps, or sanitized to nothing. - for(var/i = 1; i < decomposed.len; i++) - - //Sanitize sender - var/clean_sender = sanitize(decomposed[i]) - if(!clean_sender) - to_chat(usr, "One part of your conversation was not able to be sanitized. It was the sender of the [(i+2)/3]\th message.") - return - decomposed[i] = clean_sender - - //Sanitize message - var/clean_message = sanitize(decomposed[++i]) - if(!clean_message) - to_chat(usr, "One part of your conversation was not able to be sanitized. It was the body of the [(i+2)/3]\th message.") - return - decomposed[i] = clean_message - - //Sanitize wait time - var/clean_time = text2num(decomposed[++i]) - if(!isnum(clean_time)) - to_chat(usr, "One part of your conversation was not able to be sanitized. It was the wait time after the [(i+2)/3]\th message.") - return - if(clean_time > 60) - to_chat(usr, "Max 60 second wait time between messages for sanity's sake please.") - return - decomposed[i] = clean_time - - log_admin("Intercom convo started by: [key_name(usr)] : [sanitize(message)]") - feedback_add_details("admin_verb","IN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - //Sanitized AND we still have a chance to send it? Wow! - if(LAZYLEN(decomposed)) - for(var/i = 1; i < decomposed.len; i++) - var/this_sender = decomposed[i] - var/this_message = decomposed[++i] - var/this_wait = decomposed[++i] - global_announcer.autosay("[this_message]", "[this_sender]", "[channel == "Common" ? null : channel]", states = speech_verb) //Common is a weird case, as it's not a "channel", it's just talking into a radio without a channel set. //VOREStation Edit - sleep(this_wait SECONDS) - -/datum/admins/proc/toggleooc() - set category = "Server" - set desc="Globally Toggles OOC" - set name="Toggle Player OOC" - - if(!check_rights(R_ADMIN)) - return - - config.ooc_allowed = !(config.ooc_allowed) - if (config.ooc_allowed) - to_world("The OOC channel has been globally enabled!") - else - to_world("The OOC channel has been globally disabled!") - log_and_message_admins("toggled OOC.") - feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/togglelooc() - set category = "Server" - set desc="Globally Toggles LOOC" - set name="Toggle Player LOOC" - - if(!check_rights(R_ADMIN)) - return - - config.looc_allowed = !(config.looc_allowed) - if (config.looc_allowed) - to_world("The LOOC channel has been globally enabled!") - else - to_world("The LOOC channel has been globally disabled!") - log_and_message_admins("toggled LOOC.") - feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/admins/proc/toggledsay() - set category = "Server" - set desc="Globally Toggles DSAY" - set name="Toggle DSAY" - - if(!check_rights(R_ADMIN)) - return - - config.dsay_allowed = !(config.dsay_allowed) - if (config.dsay_allowed) - to_world("Deadchat has been globally enabled!") - else - to_world("Deadchat has been globally disabled!") - log_admin("[key_name(usr)] toggled deadchat.") - message_admins("[key_name_admin(usr)] toggled deadchat.", 1) - feedback_add_details("admin_verb","TDSAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc - -/datum/admins/proc/toggleoocdead() - set category = "Server" - set desc="Toggle Dead OOC." - set name="Toggle Dead OOC" - - if(!check_rights(R_ADMIN)) - return - - config.dooc_allowed = !( config.dooc_allowed ) - log_admin("[key_name(usr)] toggled Dead OOC.") - message_admins("[key_name_admin(usr)] toggled Dead OOC.", 1) - feedback_add_details("admin_verb","TDOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/togglehubvisibility() - set category = "Server" - set desc="Globally Toggles Hub Visibility" - set name="Toggle Hub Visibility" - - if(!check_rights(R_ADMIN)) - return - - world.visibility = !(world.visibility) - log_admin("[key_name(usr)] toggled hub visibility.") - message_admins("[key_name_admin(usr)] toggled hub visibility. The server is now [world.visibility ? "visible" : "invisible"] ([world.visibility]).", 1) - feedback_add_details("admin_verb","THUB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc - -/datum/admins/proc/toggletraitorscaling() - set category = "Server" - set desc="Toggle traitor scaling" - set name="Toggle Traitor Scaling" - config.traitor_scaling = !config.traitor_scaling - log_admin("[key_name(usr)] toggled Traitor Scaling to [config.traitor_scaling].") - message_admins("[key_name_admin(usr)] toggled Traitor Scaling [config.traitor_scaling ? "on" : "off"].", 1) - feedback_add_details("admin_verb","TTS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/startnow() - set category = "Server" - set desc="Start the round ASAP" - set name="Start Now" - - if(!check_rights(R_SERVER|R_EVENT)) - return - if(SSticker.current_state > GAME_STATE_PREGAME) - to_chat(usr, "Error: Start Now: Game has already started.") - return - if(!SSticker.start_immediately) - SSticker.start_immediately = TRUE - var/msg = "" - if(SSticker.current_state == GAME_STATE_INIT) - msg = " (The server is still setting up, but the round will be started as soon as possible.)" - log_admin("[key_name(usr)] has started the game.[msg]") - message_admins("[key_name_admin(usr)] has started the game.[msg]") - feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - else - SSticker.start_immediately = FALSE - to_world("Immediate game start canceled. Normal startup resumed.") - log_and_message_admins("cancelled immediate game start.") - -/datum/admins/proc/toggleenter() - set category = "Server" - set desc="People can't enter" - set name="Toggle Entering" - config.enter_allowed = !(config.enter_allowed) - if (!(config.enter_allowed)) - to_world("New players may no longer enter the game.") - else - to_world("New players may now enter the game.") - log_admin("[key_name(usr)] toggled new player game entering.") - message_admins("[key_name_admin(usr)] toggled new player game entering.", 1) - world.update_status() - feedback_add_details("admin_verb","TE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleAI() - set category = "Server" - set desc="People can't be AI" - set name="Toggle AI" - config.allow_ai = !( config.allow_ai ) - if (!( config.allow_ai )) - to_world("The AI job is no longer chooseable.") - else - to_world("The AI job is chooseable now.") - log_admin("[key_name(usr)] toggled AI allowed.") - world.update_status() - feedback_add_details("admin_verb","TAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleaban() - set category = "Server" - set desc="Respawn basically" - set name="Toggle Respawn" - config.abandon_allowed = !(config.abandon_allowed) - if(config.abandon_allowed) - to_world("You may now respawn.") - else - to_world("You may no longer respawn :(") - message_admins("[key_name_admin(usr)] toggled respawn to [config.abandon_allowed ? "On" : "Off"].", 1) - log_admin("[key_name(usr)] toggled respawn to [config.abandon_allowed ? "On" : "Off"].") - world.update_status() - feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/togglepersistence() - set category = "Server" - set desc="Whether persistent data will be saved from now on." - set name="Toggle Persistent Data" - config.persistence_disabled = !(config.persistence_disabled) - if(!config.persistence_disabled) - to_world("Persistence is now enabled..") - else - to_world("Persistence is no longer enabled.") - message_admins("[key_name_admin(usr)] toggled persistence to [config.persistence_disabled ? "Off" : "On"].", 1) - log_admin("[key_name(usr)] toggled persistence to [config.persistence_disabled ? "Off" : "On"].") - world.update_status() - feedback_add_details("admin_verb","TPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/togglemaploadpersistence() - set category = "Server" - set desc="Whether mapload persistent data will be saved from now on." - set name="Toggle Mapload Persistent Data" - config.persistence_ignore_mapload = !(config.persistence_ignore_mapload) - if(!config.persistence_ignore_mapload) - to_world("Persistence is now enabled..") - else - to_world("Persistence is no longer enabled.") - message_admins("[key_name_admin(usr)] toggled persistence to [config.persistence_ignore_mapload ? "Off" : "On"].", 1) - log_admin("[key_name(usr)] toggled persistence to [config.persistence_ignore_mapload ? "Off" : "On"].") - world.update_status() - feedback_add_details("admin_verb","TMPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggle_aliens() - set category = "Server" - set desc="Toggle alien mobs" - set name="Toggle Aliens" - config.aliens_allowed = !config.aliens_allowed - log_admin("[key_name(usr)] toggled Aliens to [config.aliens_allowed].") - message_admins("[key_name_admin(usr)] toggled Aliens [config.aliens_allowed ? "on" : "off"].", 1) - feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggle_space_ninja() - set category = "Server" - set desc="Toggle space ninjas spawning." - set name="Toggle Space Ninjas" - config.ninjas_allowed = !config.ninjas_allowed - log_admin("[key_name(usr)] toggled Space Ninjas to [config.ninjas_allowed].") - message_admins("[key_name_admin(usr)] toggled Space Ninjas [config.ninjas_allowed ? "on" : "off"].", 1) - feedback_add_details("admin_verb","TSN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/delay() - set category = "Server" - set desc="Delay the game start/end" - set name="Delay" - - if(!check_rights(R_SERVER|R_EVENT)) return - if (SSticker.current_state >= GAME_STATE_PLAYING) - SSticker.delay_end = !SSticker.delay_end - log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") - message_admins("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) - return - round_progressing = !round_progressing - if (!round_progressing) - to_world("The game start has been delayed.") - log_admin("[key_name(usr)] delayed the game.") - else - to_world("The game will start soon.") - log_admin("[key_name(usr)] removed the delay.") - feedback_add_details("admin_verb","DELAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/adjump() - set category = "Server" - set desc="Toggle admin jumping" - set name="Toggle Jump" - config.allow_admin_jump = !(config.allow_admin_jump) - message_admins("Toggled admin jumping to [config.allow_admin_jump].") - feedback_add_details("admin_verb","TJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/adspawn() - set category = "Server" - set desc="Toggle admin spawning" - set name="Toggle Spawn" - config.allow_admin_spawning = !(config.allow_admin_spawning) - message_admins("Toggled admin item spawning to [config.allow_admin_spawning].") - feedback_add_details("admin_verb","TAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/adrev() - set category = "Server" - set desc="Toggle admin revives" - set name="Toggle Revive" - config.allow_admin_rev = !(config.allow_admin_rev) - message_admins("Toggled reviving to [config.allow_admin_rev].") - feedback_add_details("admin_verb","TAR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/immreboot() - set category = "Server" - set desc="Reboots the server post haste" - set name="Immediate Reboot" - if(!usr.client.holder) return - if(alert(usr, "Reboot server?","Reboot!","Yes","No") == "No") // Not tgui_alert for safety - return - to_world("Rebooting world! Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!") - log_admin("[key_name(usr)] initiated an immediate reboot.") - - feedback_set_details("end_error","immediate admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]") - feedback_add_details("admin_verb","IR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(blackbox) - blackbox.save_all_data_to_sql() - - world.Reboot() - -/datum/admins/proc/unprison(var/mob/M in mob_list) - set category = "Admin" - set name = "Unprison" - if (M.z == 2) - if (config.allow_admin_jump) - M.loc = pick(latejoin) - message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]", 1) - log_admin("[key_name(usr)] has unprisoned [key_name(M)]") - else - tgui_alert_async(usr, "Admin jumping disabled") - else - tgui_alert_async(usr, "[M.name] is not prisoned.") - feedback_add_details("admin_verb","UP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS - -/proc/is_special_character(var/character) // returns 1 for special characters and 2 for heroes of gamemode - if(!ticker || !ticker.mode) - return 0 - var/datum/mind/M - if (ismob(character)) - var/mob/C = character - M = C.mind - else if(istype(character, /datum/mind)) - M = character - - if(M) - if(ticker.mode.antag_templates && ticker.mode.antag_templates.len) - for(var/datum/antagonist/antag in ticker.mode.antag_templates) - if(antag.is_antagonist(M)) - return 2 - if(M.special_role) - return 1 - - if(isrobot(character)) - var/mob/living/silicon/robot/R = character - if(R.emagged) - return 1 - - return 0 - -/datum/admins/proc/spawn_fruit(seedtype in SSplants.seeds) - set category = "Debug" - set desc = "Spawn the product of a seed." - set name = "Spawn Fruit" - - if(!check_rights(R_SPAWN)) return - - if(!seedtype || !SSplants.seeds[seedtype]) - return - var/amount = tgui_input_number(usr, "Amount of fruit to spawn", "Fruit Amount", 1) - if(!isnull(amount)) - var/datum/seed/S = SSplants.seeds[seedtype] - S.harvest(usr,0,0,amount) - log_admin("[key_name(usr)] spawned [seedtype] fruit at ([usr.x],[usr.y],[usr.z])") - -/datum/admins/proc/spawn_custom_item() - set category = "Debug" - set desc = "Spawn a custom item." - set name = "Spawn Custom Item" - - if(!check_rights(R_SPAWN)) return - - var/owner = tgui_input_list(usr, "Select a ckey.", "Spawn Custom Item", custom_items) - if(!owner|| !custom_items[owner]) - return - - var/list/possible_items = custom_items[owner] - var/datum/custom_item/item_to_spawn = tgui_input_list(usr, "Select an item to spawn.", "Spawn Custom Item", possible_items) - if(!item_to_spawn) - return - - item_to_spawn.spawn_item(get_turf(usr)) - -/datum/admins/proc/check_custom_items() - - set category = "Debug" - set desc = "Check the custom item list." - set name = "Check Custom Items" - - if(!check_rights(R_SPAWN)) return - - if(!custom_items) - to_chat(usr, "Custom item list is null.") - return - - if(!custom_items.len) - to_chat(usr, "Custom item list not populated.") - return - - for(var/assoc_key in custom_items) - to_chat(usr, "[assoc_key] has:") - var/list/current_items = custom_items[assoc_key] - for(var/datum/custom_item/item in current_items) - to_chat(usr, "- name: [item.name] icon: [item.item_icon] path: [item.item_path] desc: [item.item_desc]") - -/datum/admins/proc/spawn_plant(seedtype in SSplants.seeds) - set category = "Debug" - set desc = "Spawn a spreading plant effect." - set name = "Spawn Plant" - - if(!check_rights(R_SPAWN)) return - - if(!seedtype || !SSplants.seeds[seedtype]) - return - new /obj/effect/plant(get_turf(usr), SSplants.seeds[seedtype]) - log_admin("[key_name(usr)] spawned [seedtype] vines at ([usr.x],[usr.y],[usr.z])") - -/datum/admins/proc/spawn_atom(var/object as text) - set name = "Spawn" - set category = "Debug" - set desc = "(atom path) Spawn an atom" - - if(!check_rights(R_SPAWN)) return - - var/list/types = typesof(/atom) - var/list/matches = new() - - for(var/path in types) - if(findtext("[path]", object)) - matches += path - - if(matches.len==0) - return - - var/chosen - if(matches.len==1) - chosen = matches[1] - else - chosen = tgui_input_list(usr, "Select an atom type", "Spawn Atom", matches) - if(!chosen) - return - - if(ispath(chosen,/turf)) - var/turf/T = get_turf(usr.loc) - T.ChangeTurf(chosen) - else - new chosen(usr.loc) - - log_and_message_admins("spawned [chosen] at ([usr.x],[usr.y],[usr.z])") - feedback_add_details("admin_verb","SA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/admins/proc/show_traitor_panel(var/mob/M in mob_list) - set category = "Admin" - set desc = "Edit mobs's memory and role" - set name = "Show Traitor Panel" - - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!M.mind) - to_chat(usr, "This mob has no mind!") - return - - M.mind.edit_memory() - feedback_add_details("admin_verb","STP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/show_game_mode() - set category = "Admin" - set desc = "Show the current round configuration." - set name = "Show Game Mode" - - if(!ticker || !ticker.mode) - tgui_alert_async(usr, "Not before roundstart!", "Alert") - return - - var/out = "Current mode: [ticker.mode.name] ([ticker.mode.config_tag])
                    " - out += "
                    " - - if(ticker.mode.ert_disabled) - out += "Emergency Response Teams: disabled" - else - out += "Emergency Response Teams: enabled" - out += "
                    " - - if(ticker.mode.deny_respawn) - out += "Respawning: disallowed" - else - out += "Respawning: allowed" - out += "
                    " - - out += "Shuttle delay multiplier: [ticker.mode.shuttle_delay]
                    " - - if(ticker.mode.auto_recall_shuttle) - out += "Shuttle auto-recall: enabled" - else - out += "Shuttle auto-recall: disabled" - out += "

                    " - - if(ticker.mode.event_delay_mod_moderate) - out += "Moderate event time modifier: [ticker.mode.event_delay_mod_moderate]
                    " - else - out += "Moderate event time modifier: unset
                    " - - if(ticker.mode.event_delay_mod_major) - out += "Major event time modifier: [ticker.mode.event_delay_mod_major]
                    " - else - out += "Major event time modifier: unset
                    " - - out += "
                    " - - if(ticker.mode.antag_tags && ticker.mode.antag_tags.len) - out += "Core antag templates:
                    " - for(var/antag_tag in ticker.mode.antag_tags) - out += "[antag_tag].
                    " - - if(ticker.mode.round_autoantag) - out += "Autotraitor enabled." - if(ticker.mode.antag_scaling_coeff > 0) - out += " (scaling with [ticker.mode.antag_scaling_coeff])" - else - out += " (not currently scaling, set a coefficient)" - out += "
                    " - else - out += "Autotraitor disabled.
                    " - - out += "All antag ids:" - if(ticker.mode.antag_templates && ticker.mode.antag_templates.len) - for(var/datum/antagonist/antag in ticker.mode.antag_templates) - antag.update_current_antag_max() - out += " [antag.id]" - out += " ([antag.get_antag_count()]/[antag.cur_max]) " - out += " \[-\]
                    " - else - out += " None." - out += " \[+\]
                    " - - usr << browse(out, "window=edit_mode[src]") - feedback_add_details("admin_verb","SGM") - - -/datum/admins/proc/toggletintedweldhelmets() - set category = "Debug" - set desc="Reduces view range when wearing welding helmets" - set name="Toggle tinted welding helmets." - config.welder_vision = !( config.welder_vision ) - if (config.welder_vision) - to_world("Reduced welder vision has been enabled!") - else - to_world("Reduced welder vision has been disabled!") - log_admin("[key_name(usr)] toggled welder vision.") - message_admins("[key_name_admin(usr)] toggled welder vision.", 1) - feedback_add_details("admin_verb","TTWH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleguests() - set category = "Server" - set desc="Guests can't enter" - set name="Toggle guests" - config.guests_allowed = !(config.guests_allowed) - if (!(config.guests_allowed)) - to_world("Guests may no longer enter the game.") - else - to_world("Guests may now enter the game.") - log_admin("[key_name(usr)] toggled guests game entering [config.guests_allowed?"":"dis"]allowed.") - message_admins("[key_name_admin(usr)] toggled guests game entering [config.guests_allowed?"":"dis"]allowed.", 1) - feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/output_ai_laws() - var/ai_number = 0 - for(var/mob/living/silicon/S in mob_list) - ai_number++ - if(isAI(S)) - to_chat(usr, "AI [key_name(S, usr)]'s laws:") - else if(isrobot(S)) - var/mob/living/silicon/robot/R = S - to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independent)"]: laws:") - else if (ispAI(S)) - to_chat(usr, "pAI [key_name(S, usr)]'s laws:") - else - to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") - - if (S.laws == null) - to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") - else - S.laws.show_laws(usr) - if(!ai_number) - to_chat(usr, "No AIs located") //Just so you know the thing is actually working and not just ignoring you. - -/datum/admins/proc/show_skills() - set category = "Admin" - set name = "Show Skills" - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - - var/mob/living/carbon/human/M = tgui_input_list(usr, "Select mob.", "Select mob.", human_mob_list) - if(!M) return - - show_skill_window(usr, M) - - return - -/client/proc/update_mob_sprite(mob/living/carbon/human/H as mob) - set category = "Admin" - set name = "Update Mob Sprite" - set desc = "Should fix any mob sprite update errors." - - if (!holder) - to_chat(src, "Only administrators may use this command.") - return - - if(istype(H)) - H.regenerate_icons() - -/proc/get_options_bar(whom, detail = 2, name = 0, link = 1, highlight_special = 1) - if(!whom) - return "(*null*)" - var/mob/M - var/client/C - if(istype(whom, /client)) - C = whom - M = C.mob - else if(istype(whom, /mob)) - M = whom - C = M.client - else - return "(*not a mob*)" - switch(detail) - if(0) - return "[key_name(C, link, name, highlight_special)]" - - if(1) //Private Messages - return "[key_name(C, link, name, highlight_special)](?)" - - if(2) //Admins - var/ref_mob = "\ref[M]" - return "[key_name(C, link, name, highlight_special)](?) (PP) (VV) (SM) ([admin_jump_link(M)]) (CA) (TAKE)" - - if(3) //Devs - var/ref_mob = "\ref[M]" - return "[key_name(C, link, name, highlight_special)](VV)([admin_jump_link(M)]) (TAKE)" - - if(4) //Event Managers - var/ref_mob = "\ref[M]" - return "[key_name(C, link, name, highlight_special)] (?) (PP) (VV) (SM) ([admin_jump_link(M)]) (TAKE)" - - -/proc/ishost(whom) - if(!whom) - return 0 - var/client/C - var/mob/M - if(istype(whom, /client)) - C = whom - if(istype(whom, /mob)) - M = whom - C = M.client - if(R_HOST & C.holder.rights) - return 1 - else - return 0 -// -// -//ALL DONE -//********************************************************************************************************* -// - -//Returns 1 to let the dragdrop code know we are trapping this event -//Returns 0 if we don't plan to trap the event -/datum/admins/proc/cmd_ghost_drag(var/mob/observer/dead/frommob, var/mob/living/tomob) - if(!istype(frommob)) - return //Extra sanity check to make sure only observers are shoved into things - - //Same as assume-direct-control perm requirements. - if (!check_rights(R_VAREDIT,0) || !check_rights(R_ADMIN|R_DEBUG|R_EVENT,0)) - return 0 - if (!frommob.ckey) - return 0 - var/question = "" - if (tomob.ckey) - question = "This mob already has a user ([tomob.key]) in control of it! " - question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" - var/ask = tgui_alert(usr, question, "Place ghost in control of mob?", list("Yes", "No")) - if (ask != "Yes") - return 1 - if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response - return 1 - if(tomob.client) //No need to ghostize if there is no client - tomob.ghostize(0) - if(frommob.mind && frommob.mind.current) //Preserve teleop for original body when adminghosting. - var/mob/body = frommob.mind.current - if(body) - if(body.teleop) - body.teleop = tomob - message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") - log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") - feedback_add_details("admin_verb","CGD") - tomob.ckey = frommob.ckey - qdel(frommob) - return 1 - -/datum/admins/proc/force_antag_latespawn() - set category = "Admin" - set name = "Force Template Spawn" - set desc = "Force an antagonist template to spawn." - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - - if(!ticker || !ticker.mode) - to_chat(usr, "Mode has not started.") - return - - var/antag_type = tgui_input_list(usr, "Choose a template.","Force Latespawn", all_antag_types) - if(!antag_type || !all_antag_types[antag_type]) - to_chat(usr, "Aborting.") - return - - var/datum/antagonist/antag = all_antag_types[antag_type] - message_admins("[key_name(usr)] attempting to force latespawn with template [antag.id].") - antag.attempt_late_spawn() - -/datum/admins/proc/force_mode_latespawn() - set category = "Admin" - set name = "Force Mode Spawn" - set desc = "Force autotraitor to proc." - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins) || !check_rights(R_ADMIN|R_EVENT|R_FUN)) - to_chat(usr, "Error: you are not an admin!") - return - - if(!ticker || !ticker.mode) - to_chat(usr, "Mode has not started.") - return - - log_and_message_admins("attempting to force mode autospawn.") - ticker.mode.try_latespawn() - -/datum/admins/proc/paralyze_mob(mob/living/H as mob) - set category = "Admin" - set name = "Toggle Paralyze" - set desc = "Paralyzes a player. Or unparalyses them." - - var/msg - - if(check_rights(R_ADMIN|R_MOD|R_EVENT)) - if (H.paralysis == 0) - H.SetParalysis(8000) - msg = "has paralyzed [key_name(H)]." - log_and_message_admins(msg) - else - if(tgui_alert(src, "[key_name(H)] is paralyzed, would you like to unparalyze them?","Paralyze Mob",list("Yes","No")) == "Yes") - H.SetParalysis(0) - msg = "has unparalyzed [key_name(H)]." - log_and_message_admins(msg) - -/datum/admins/proc/set_tcrystals(mob/living/carbon/human/H as mob) - set category = "Debug" - set name = "Set Telecrystals" - set desc = "Allows admins to change telecrystals of a user." - set popup_menu = FALSE //VOREStation Edit - Declutter. - var/crystals - - if(check_rights(R_ADMIN|R_EVENT)) - crystals = tgui_input_number(usr, "Amount of telecrystals for [H.ckey], currently [H.mind.tcrystals].", crystals) - if (!isnull(crystals)) - H.mind.tcrystals = crystals - var/msg = "[key_name(usr)] has modified [H.ckey]'s telecrystals to [crystals]." - message_admins(msg) - else - to_chat(usr, "You do not have access to this command.") - -/datum/admins/proc/add_tcrystals(mob/living/carbon/human/H as mob) - set category = "Debug" - set name = "Add Telecrystals" - set desc = "Allows admins to change telecrystals of a user by addition." - set popup_menu = FALSE //VOREStation Edit - Declutter. - var/crystals - - if(check_rights(R_ADMIN|R_EVENT)) - crystals = tgui_input_number(usr, "Amount of telecrystals to give to [H.ckey], currently [H.mind.tcrystals].", crystals) - if (!isnull(crystals)) - H.mind.tcrystals += crystals - var/msg = "[key_name(usr)] has added [crystals] to [H.ckey]'s telecrystals." - message_admins(msg) - else - to_chat(usr, "You do not have access to this command.") - - -/datum/admins/proc/sendFax() - set category = "Special Verbs" - set name = "Send Fax" - set desc = "Sends a fax to this machine" - var/department = tgui_input_list(usr, "Choose a fax", "Fax", alldepartments) - for(var/obj/machinery/photocopier/faxmachine/sendto in allfaxes) - if(sendto.department == department) - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - - var/replyorigin = tgui_input_text(src.owner, "Please specify who the fax is coming from", "Origin") - - var/obj/item/weapon/paper/admin/P = new /obj/item/weapon/paper/admin( null ) //hopefully the null loc won't cause trouble for us - faxreply = P - - P.admindatum = src - P.origin = replyorigin - P.destination = sendto - - P.adminbrowse() - - -/datum/admins/var/obj/item/weapon/paper/admin/faxreply // var to hold fax replies in - -/datum/admins/proc/faxCallback(var/obj/item/weapon/paper/admin/P, var/obj/machinery/photocopier/faxmachine/destination) - var/customname = tgui_input_text(src.owner, "Pick a title for the report", "Title") - - P.name = "[P.origin] - [customname]" - P.desc = "This is a paper titled '" + P.name + "'." - - var/shouldStamp = 1 - if(!P.sender) // admin initiated - switch(tgui_alert(usr, "Would you like the fax stamped?","Stamped?", list("Yes", "No"))) - if("No") - shouldStamp = 0 - - if(shouldStamp) - P.stamps += "
                    This paper has been stamped by the [P.origin] Quantum Relay." - - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - var/x = rand(-2, 0) - var/y = rand(-1, 2) - P.offset_x += x - P.offset_y += y - stampoverlay.pixel_x = x - stampoverlay.pixel_y = y - - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-cent" - stampoverlay.icon_state = "paper_stamp-cent" - - if(!P.stamped) - P.stamped = new - P.stamped += /obj/item/weapon/stamp/centcomm - P.add_overlay(stampoverlay) - - var/obj/item/rcvdcopy - rcvdcopy = destination.copy(P) - rcvdcopy.loc = null //hopefully this shouldn't cause trouble - adminfaxes += rcvdcopy - - - - if(destination.receivefax(P)) - to_chat(src.owner, "Message reply to transmitted successfully.") - if(P.sender) // sent as a reply - log_admin("[key_name(src.owner)] replied to a fax message from [key_name(P.sender)]") - for(var/client/C in GLOB.admins) - if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights) - to_chat(C, "FAX LOG:[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (VIEW)") - else - log_admin("[key_name(src.owner)] has sent a fax message to [destination.department]") - for(var/client/C in GLOB.admins) - if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights) - to_chat(C, "FAX LOG:[key_name_admin(src.owner)] has sent a fax message to [destination.department] (VIEW)") - - var/plaintext_title = P.sender ? "replied to [key_name(P.sender)]'s fax" : "sent a fax message to [destination.department]" - var/fax_text = paper_html_to_plaintext(P.info) - log_game(plaintext_title) - log_game(fax_text) - - SSwebhooks.send( - WEBHOOK_FAX_SENT, - list( - "name" = "[key_name(owner)] [plaintext_title].", - "body" = fax_text - ) - ) - - else - to_chat(src.owner, "Message reply failed.") - - spawn(100) - qdel(P) - faxreply = null - return +var/global/floorIsLava = 0 + + +//////////////////////////////// +/proc/message_admins(var/msg) + msg = "ADMIN LOG: [msg]" + //log_adminwarn(msg) //log_and_message_admins is for this + + for(var/client/C in GLOB.admins) + if((R_ADMIN|R_MOD) & C.holder.rights) + to_chat(C, + type = MESSAGE_TYPE_ADMINLOG, + html = msg, + confidential = TRUE) + +/proc/msg_admin_attack(var/text) //Toggleable Attack Messages + var/rendered = "ATTACK: [text]" + for(var/client/C in GLOB.admins) + if((R_ADMIN|R_MOD) & C.holder.rights) + if(C.is_preference_enabled(/datum/client_preference/mod/show_attack_logs)) + var/msg = rendered + to_chat(C, + type = MESSAGE_TYPE_ATTACKLOG, + html = msg, + confidential = TRUE) + +/proc/admin_notice(var/message, var/rights) + for(var/mob/M in mob_list) + if(check_rights(rights, 0, M)) + to_chat(M,message) + +///////////////////////////////////////////////////////////////////////////////////////////////Panels + +/datum/admins/proc/show_player_panel(var/mob/M in mob_list) + set category = "Admin" + set name = "Show Player Panel" + set desc="Edit player (respawn, ban, heal, etc)" + + if(!M) + to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") + return + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + var/body = "Options for [M.key]" + body += "Options panel for [M]" + if(M.client) + body += " played by [M.client] " + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + + if(istype(M, /mob/new_player)) + body += " Hasn't Entered Game " + else + body += " \[Heal\] " + + if(M.client) + body += "
                    First connection: [M.client.player_age] days ago" + body += "
                    BYOND account created: [M.client.account_join_date]" + body += "
                    BYOND account age (days): [M.client.account_age]" + + body += {" +

                    \[ + VV - + TP - + PM - + SM - + [admin_jump_link(M, src)]\]
                    + Mob type: [M.type]
                    + Inactivity time: [M.client ? "[M.client.inactivity/600] minutes" : "Logged out"]

                    + Kick | + Warn | + Ban | + Jobban | + Notes + "} + + if(M.client) + body += "| Prison | " + body += "\ Send back to Lobby | " + var/muted = M.client.prefs.muted + body += {"
                    Mute: + \[IC | + OOC | + PRAY | + ADMINHELP | + DEADCHAT\] + (toggle all) + "} + + body += {"

                    + Jump to | + Get | + Send To +

                    + [check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "Traitor panel | " : "" ] + Narrate to | + Subtle message + "} + + if (M.client) + if(!istype(M, /mob/new_player)) + body += "

                    " + body += "Transformation:" + body += "
                    " + + //Monkey + if(issmall(M)) + body += "Monkeyized | " + else + body += "Monkeyize | " + + //Corgi + if(iscorgi(M)) + body += "Corgized | " + else + body += "Corgize | " + + //AI / Cyborg + if(isAI(M)) + body += "Is an AI " + else if(ishuman(M)) + body += {"Make AI | + Make Robot | + Make Alien + "} + + //Simple Animals + if(isanimal(M)) + body += "Re-Animalize | " + else + body += "Animalize | " + + body += "Respawn | " + + // DNA2 - Admin Hax + if(M.dna && iscarbon(M)) + body += "

                    " + body += "DNA Blocks:
                    " + var/bname + for(var/block=1;block<=DNA_SE_LENGTH;block++) + if(((block-1)%5)==0) + body += "" + bname = assigned_blocks[block] + body += "" + body += "
                     12345
                    [block-1]" + if(bname) + var/bstate=M.dna.GetSEState(block) + var/bcolor="[(bstate)?"#006600":"#ff0000"]" + body += "[bname][block]" + else + body += "[block]" + body+="
                    " + + body += {"

                    + Rudimentary transformation:
                    These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

                    + Observer | + \[ Xenos: Larva + Drone + Hunter + Sentinel + Queen \] | + \[ Crew: Human + Unathi + Tajaran + Skrell \] | \[ + Nymph + Diona \] | + \[ slime: Baby, + Adult \] + Monkey | + Cyborg | + Cat | + Runtime | + Corgi | + Ian | + Crab | + Coffee | + \[ Construct: Armoured , + Builder , + Wraith \] + Shade +
                    + "} + body += {"

                    + Other actions: +
                    + Forcesay + "} + if (M.client) + body += {" | + Thunderdome 1 | + Thunderdome 2 | + Thunderdome Admin | + Thunderdome Observer | + "} + // language toggles + body += "

                    Languages:
                    " + var/f = 1 + for(var/k in GLOB.all_languages) + var/datum/language/L = GLOB.all_languages[k] + if(!(L.flags & INNATE)) + if(!f) body += " | " + else f = 0 + if(L in M.languages) + body += "[k]" + else + body += "[k]" + + body += {"
                    + + "} + + usr << browse(body, "window=adminplayeropts;size=550x515") + feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/player_info/var/author // admin who authored the information +/datum/player_info/var/rank //rank of admin who made the notes +/datum/player_info/var/content // text content of the information +/datum/player_info/var/timestamp // Because this is bloody annoying + +/datum/admins/proc/PlayerNotes() + set category = "Admin" + set name = "Player Notes" + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + PlayerNotesPage(1) + +/datum/admins/proc/PlayerNotesFilter() + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + var/filter = tgui_input_text(usr, "Filter string (case-insensitive regex)", "Player notes filter") + PlayerNotesPage(1, filter) + +/datum/admins/proc/PlayerNotesPage(page, filter) + var/savefile/S=new("data/player_notes.sav") + var/list/note_keys + S >> note_keys + + if(note_keys) + note_keys = sortList(note_keys) + + var/datum/tgui_module/player_notes/A = new(src) + A.ckeys = note_keys + A.tgui_interact(usr) + + +/datum/admins/proc/player_has_info(var/key as text) + var/savefile/info = new("data/player_saves/[copytext(key, 1, 2)]/[key]/info.sav") + var/list/infos + info >> infos + if(!infos || !infos.len) return 0 + else return 1 + + +/datum/admins/proc/show_player_info(var/key as text) + set category = "Admin" + set name = "Show Player Info" + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + var/datum/tgui_module/player_notes_info/A = new(src) + A.key = key + A.tgui_interact(usr) + + +/datum/admins/proc/access_news_network() //MARKER + set category = "Fun" + set name = "Access Newscaster Network" + set desc = "Allows you to view, add and edit news feeds." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + var/dat + dat = text("Admin Newscaster

                    Admin Newscaster Unit

                    ") + + switch(admincaster_screen) + if(0) + dat += {"Welcome to the admin newscaster.
                    Here you can add, edit and censor every newspiece on the network. +
                    Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units. +
                    Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things! + "} + if(news_network.wanted_issue) + dat+= "
                    Read Wanted Issue" + + dat+= {"

                    Create Feed Channel +
                    View Feed Channels +
                    Submit new Feed story +

                    Exit + "} + + var/wanted_already = 0 + if(news_network.wanted_issue) + wanted_already = 1 + + dat+={"
                    Feed Security functions:
                    +
                    [(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue +
                    Censor Feed Stories +
                    Mark Feed Channel with [using_map.company_name] D-Notice (disables and locks the channel. +

                    The newscaster recognises you as:
                    [src.admincaster_signature]
                    + "} + if(1) + dat+= "Station Feed Channels
                    " + if( isemptylist(news_network.network_channels) ) + dat+="No active channels found..." + else + for(var/datum/feed_channel/CHANNEL in news_network.network_channels) + if(CHANNEL.is_admin_channel) + dat+="[CHANNEL.channel_name]
                    " + else + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
                    " + dat+={"

                    Refresh +
                    Back + "} + + if(2) + dat+={" + Creating new Feed Channel... +
                    Channel Name: [src.admincaster_feed_channel.channel_name]
                    + Channel Author: [src.admincaster_signature]
                    + Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

                    +
                    Submit

                    Cancel
                    + "} + if(3) + dat+={" + Creating new Feed Message... +
                    Receiving Channel: [src.admincaster_feed_channel.channel_name]
                    + Message Author: [src.admincaster_signature]
                    + Message Body: [src.admincaster_feed_message.body]
                    +
                    Submit

                    Cancel
                    + "} + if(4) + dat+={" + Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

                    +
                    Return
                    + "} + if(5) + dat+={" + Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

                    +
                    Return
                    + "} + if(6) + dat+="ERROR: Could not submit Feed story to Network.

                    " + if(src.admincaster_feed_channel.channel_name=="") + dat+="•Invalid receiving channel name.
                    " + if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]" || admincaster_feed_message.title == "") + dat+="•Invalid message body.
                    " + dat+="
                    Return
                    " + if(7) + dat+="ERROR: Could not submit Feed Channel to Network.

                    " + if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") + dat+="•Invalid channel name.
                    " + var/check = 0 + for(var/datum/feed_channel/FC in news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(check) + dat+="•Channel name already in use.
                    " + dat+="
                    Return
                    " + if(9) + dat+="[src.admincaster_feed_channel.channel_name]: \[created by: [src.admincaster_feed_channel.author]\]
                    " + if(src.admincaster_feed_channel.censored) + dat+={" + ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.
                    + No further feed story additions are allowed while the D-Notice is in effect.

                    + "} + else + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
                    " + else + var/i = 0 + for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + i++ + //dat+="-[MESSAGE.body]
                    " + var/pic_data + if(MESSAGE.img) + usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") + pic_data+="
                    " + dat+= get_newspaper_content(MESSAGE.title, MESSAGE.body, MESSAGE.author,"#d4cec1", pic_data) + dat+="
                    " + dat+="\[Story by [MESSAGE.author] - [MESSAGE.time_stamp]\]
                    " + dat+={" +

                    Refresh +
                    Back + "} + if(10) + dat+={" + [using_map.company_name] Feed Censorship Tool
                    + NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
                    + Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
                    +
                    Select Feed channel to get Stories from:
                    + "} + if(isemptylist(news_network.network_channels)) + dat+="No feed channels found active...
                    " + else + for(var/datum/feed_channel/CHANNEL in news_network.network_channels) + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
                    " + dat+="
                    Cancel" + if(11) + dat+={" + [using_map.company_name] D-Notice Handler
                    + A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's + morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed + stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
                    + "} + if(isemptylist(news_network.network_channels)) + dat+="No feed channels found active...
                    " + else + for(var/datum/feed_channel/CHANNEL in news_network.network_channels) + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : null]
                    " + + dat+="
                    Back" + if(12) + dat+={" + [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
                    + [(src.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]
                    + "} + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
                    " + else + for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + dat+={" + -[MESSAGE.body]
                    \[Story by [MESSAGE.author]\]
                    + [(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]
                    + "} + dat+="
                    Back" + if(13) + dat+={" + [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
                    + Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
                    + "} + if(src.admincaster_feed_channel.censored) + dat+={" + ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.
                    + No further feed story additions are allowed while the D-Notice is in effect.

                    + "} + else + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
                    " + else + for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + dat+="-[MESSAGE.body]
                    \[Story by [MESSAGE.author]\]
                    " + + dat+="
                    Back" + if(14) + dat+="Wanted Issue Handler:" + var/wanted_already = 0 + var/end_param = 1 + if(news_network.wanted_issue) + wanted_already = 1 + end_param = 2 + if(wanted_already) + dat+="
                    A wanted issue is already in Feed Circulation. You can edit or cancel it below.
                    " + dat+={" +
                    + Criminal Name: [src.admincaster_feed_message.author]
                    + Description: [src.admincaster_feed_message.body]
                    + "} + if(wanted_already) + dat+="Wanted Issue created by: [news_network.wanted_issue.backup_author]
                    " + else + dat+="Wanted Issue will be created under prosecutor: [src.admincaster_signature]
                    " + dat+="
                    [(wanted_already) ? ("Edit Issue") : ("Submit")]" + if(wanted_already) + dat+="
                    Take down Issue" + dat+="
                    Cancel" + if(15) + dat+={" + Wanted issue for [src.admincaster_feed_message.author] is now in Network Circulation.

                    +
                    Return
                    + "} + if(16) + dat+="ERROR: Wanted Issue rejected by Network.

                    " + if(src.admincaster_feed_message.author =="" || src.admincaster_feed_message.author == "\[REDACTED\]") + dat+="•Invalid name for person wanted.
                    " + if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") + dat+="•Invalid description.
                    " + dat+="
                    Return
                    " + if(17) + dat+={" + Wanted Issue successfully deleted from Circulation
                    +
                    Return
                    + "} + if(18) + dat+={" + -- STATIONWIDE WANTED ISSUE --
                    \[Submitted by: [news_network.wanted_issue.backup_author]\]
                    + Criminal: [news_network.wanted_issue.author]
                    + Description: [news_network.wanted_issue.body]
                    + Photo:: + "} + if(news_network.wanted_issue.img) + usr << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png") + dat+="
                    " + else + dat+="None" + dat+="
                    Back
                    " + if(19) + dat+={" + Wanted issue for [src.admincaster_feed_message.author] successfully edited.

                    +
                    Return
                    + "} + else + dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" + + //to_world("Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") + //to_world("Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") + usr << browse(dat, "window=admincaster_main;size=400x600") + onclose(usr, "admincaster_main") + + + +/datum/admins/proc/Jobbans() + if(!check_rights(R_BAN)) return + + var/dat = "Job Bans!
                    " + for(var/t in jobban_keylist) + var/r = t + if( findtext(r,"##") ) + r = copytext( r, 1, findtext(r,"##") )//removes the description + dat += text("") + dat += "
                    [t] (unban)
                    " + usr << browse(dat, "window=ban;size=400x400") + +/datum/admins/proc/Game() + if(!check_rights(0)) return + + var/dat = {" +
                    Game Panel

                    \n + Change Game Mode
                    + "} + if(master_mode == "secret") + dat += "(Force Secret Mode)
                    " + + dat += {" +
                    + Create Object
                    + Quick Create Object
                    + Create Turf
                    + Create Mob
                    +
                    Edit Airflow Settings
                    + Edit Phoron Settings
                    + Choose a default ZAS setting
                    + "} + + usr << browse(dat, "window=admin2;size=210x280") + return + +/datum/admins/proc/Secrets(var/datum/admin_secret_category/active_category = null) + if(!check_rights(0)) return + + // Print the header with category selection buttons. + var/dat = "The first rule of adminbuse is: you don't talk about the adminbuse.
                    " + for(var/datum/admin_secret_category/category in admin_secrets.categories) + if(!category.can_view(usr)) + continue + dat += "[category.name] " + dat += "
                    " + + // If a category is selected, print its description and then options + if(istype(active_category) && active_category.can_view(usr)) + dat += "[active_category.name]
                    " + if(active_category.desc) + dat += "[active_category.desc]
                    " + for(var/datum/admin_secret_item/item in active_category.items) + if(!item.can_view(usr)) + continue + dat += "[item.name()]
                    " + dat += "
                    " + + var/datum/browser/popup = new(usr, "secrets", "Secrets", 500, 500) + popup.set_content(dat) + popup.open() + return + +/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge +//i.e. buttons/verbs + + +/datum/admins/proc/restart() + set category = "Server" + set name = "Restart" + set desc="Restarts the world" + if (!usr.client.holder) + return + var/confirm = alert(usr, "Restart the game world?", "Restart", "Yes", "Cancel") // Not tgui_alert for safety + if(confirm == "Cancel") + return + if(confirm == "Yes") + to_world("Restarting world! Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!") + log_admin("[key_name(usr)] initiated a reboot.") + + feedback_set_details("end_error","admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]") + feedback_add_details("admin_verb","R") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(blackbox) + blackbox.save_all_data_to_sql() + + sleep(50) + world.Reboot() + + +/datum/admins/proc/announce() + set category = "Special Verbs" + set name = "Announce" + set desc="Announce your desires to the world" + if(!check_rights(0)) return + + var/message = tgui_input_text(usr, "Global message to send:", "Admin Announce", multiline = TRUE, prevent_enter = TRUE) + if(message) + if(!check_rights(R_SERVER,0)) + message = sanitize(message, 500, extra = 0) + message = replacetext(message, "\n", "
                    ") // required since we're putting it in a

                    tag + to_world("[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:

                    [message]

                    ") + log_admin("Announce: [key_name(usr)] : [message]") + feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +//VOREStation Edit to this verb for the purpose of making it compliant with the annunciator system +var/datum/announcement/priority/admin_pri_announcer = new +var/datum/announcement/minor/admin_min_announcer = new +/datum/admins/proc/intercom() + set category = "Fun" + set name = "Intercom Msg" + set desc = "Send an intercom message, like an arrivals announcement." + if(!check_rights(0)) return + + var/channel = tgui_input_list(usr, "Channel for message:","Channel", radiochannels) + + if(channel) //They picked a channel + var/sender = tgui_input_text(usr, "Name of sender (max 75):", "Announcement", "Announcement Computer") + + if(sender) //They put a sender + sender = sanitize(sender, 75, extra = 0) + var/message = tgui_input_text(usr, "Message content (max 500):", "Contents", "This is a test of the announcement system.", multiline = TRUE, prevent_enter = TRUE) + var/msgverb = tgui_input_text(usr, "Name of verb (Such as 'states', 'says', 'asks', etc):", "Verb", "says") + if(message) //They put a message + message = sanitize(message, 500, extra = 0) + //VOREStation Edit Start + if(msgverb) + msgverb = sanitize(msgverb, 50, extra = 0) + else + msgverb = "states" + global_announcer.autosay("[message]", "[sender]", "[channel == "Common" ? null : channel]", states = msgverb) //Common is a weird case, as it's not a "channel", it's just talking into a radio without a channel set. + //VOREStation Edit End + log_admin("Intercom: [key_name(usr)] : [sender]:[message]") + + feedback_add_details("admin_verb","IN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/intercom_convo() + set category = "Fun" + set name = "Intercom Convo" + set desc = "Send an intercom conversation, like several uses of the Intercom Msg verb." + set waitfor = FALSE //Why bother? We have some sleeps. You can leave tho! + if(!check_rights(0)) return + + var/channel = tgui_input_list(usr, "Channel for message:","Channel", radiochannels) + + if(!channel) //They picked a channel + return + + var/speech_verb = tgui_alert(usr, "What speech verb to use for the conversation?", "Type", list("states", "says")) + if(!speech_verb) + return + + to_chat(usr, "Intercom Convo Directions
                    Start the conversation with the sender, a pipe (|), and then the message on one line. Then hit enter to \ + add another line, and type a (whole) number of seconds to pause between that message, and the next message, then repeat the message syntax up to 20 times. For example:
                    \ + --- --- ---
                    \ + Some Guy|Hello guys, what's up?
                    \ + 5
                    \ + Other Guy|Hey, good to see you.
                    \ + 5
                    \ + Some Guy|Yeah, you too.
                    \ + --- --- ---
                    \ + The above will result in those messages playing, with a 5 second gap between each. Maximum of 20 messages allowed.
                    ") + + var/list/decomposed + var/message = tgui_input_text(usr,"See your chat box for instructions. Keep a copy elsewhere in case it is rejected when you click OK.", "Input Conversation", "", multiline = TRUE, prevent_enter = TRUE) + + if(!message) + return + + //Split on pipe or \n + decomposed = splittext(message,regex("\\||$","m")) + decomposed += "0" //Tack on a final 0 sleep to make 3-per-message evenly + + //Time to find how they screwed up. + //Wasn't the right length + if((decomposed.len) % 3) //+1 to accomidate the lack of a wait time for the last message + to_chat(usr, "You passed [decomposed.len] segments (senders+messages+pauses). You must pass a multiple of 3, minus 1 (no pause after the last message). That means a sender and message on every other line (starting on the first), separated by a pipe character (|), and a number every other line that is a pause in seconds.") + return + + //Too long a conversation + if((decomposed.len / 3) > 20) + to_chat(usr, "This conversation is too long! 20 messages maximum, please.") + return + + //Missed some sleeps, or sanitized to nothing. + for(var/i = 1; i < decomposed.len; i++) + + //Sanitize sender + var/clean_sender = sanitize(decomposed[i]) + if(!clean_sender) + to_chat(usr, "One part of your conversation was not able to be sanitized. It was the sender of the [(i+2)/3]\th message.") + return + decomposed[i] = clean_sender + + //Sanitize message + var/clean_message = sanitize(decomposed[++i]) + if(!clean_message) + to_chat(usr, "One part of your conversation was not able to be sanitized. It was the body of the [(i+2)/3]\th message.") + return + decomposed[i] = clean_message + + //Sanitize wait time + var/clean_time = text2num(decomposed[++i]) + if(!isnum(clean_time)) + to_chat(usr, "One part of your conversation was not able to be sanitized. It was the wait time after the [(i+2)/3]\th message.") + return + if(clean_time > 60) + to_chat(usr, "Max 60 second wait time between messages for sanity's sake please.") + return + decomposed[i] = clean_time + + log_admin("Intercom convo started by: [key_name(usr)] : [sanitize(message)]") + feedback_add_details("admin_verb","IN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + //Sanitized AND we still have a chance to send it? Wow! + if(LAZYLEN(decomposed)) + for(var/i = 1; i < decomposed.len; i++) + var/this_sender = decomposed[i] + var/this_message = decomposed[++i] + var/this_wait = decomposed[++i] + global_announcer.autosay("[this_message]", "[this_sender]", "[channel == "Common" ? null : channel]", states = speech_verb) //Common is a weird case, as it's not a "channel", it's just talking into a radio without a channel set. //VOREStation Edit + sleep(this_wait SECONDS) + +/datum/admins/proc/toggleooc() + set category = "Server" + set desc="Globally Toggles OOC" + set name="Toggle Player OOC" + + if(!check_rights(R_ADMIN)) + return + + config.ooc_allowed = !(config.ooc_allowed) + if (config.ooc_allowed) + to_world("The OOC channel has been globally enabled!") + else + to_world("The OOC channel has been globally disabled!") + log_and_message_admins("toggled OOC.") + feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/togglelooc() + set category = "Server" + set desc="Globally Toggles LOOC" + set name="Toggle Player LOOC" + + if(!check_rights(R_ADMIN)) + return + + config.looc_allowed = !(config.looc_allowed) + if (config.looc_allowed) + to_world("The LOOC channel has been globally enabled!") + else + to_world("The LOOC channel has been globally disabled!") + log_and_message_admins("toggled LOOC.") + feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/admins/proc/toggledsay() + set category = "Server" + set desc="Globally Toggles DSAY" + set name="Toggle DSAY" + + if(!check_rights(R_ADMIN)) + return + + config.dsay_allowed = !(config.dsay_allowed) + if (config.dsay_allowed) + to_world("Deadchat has been globally enabled!") + else + to_world("Deadchat has been globally disabled!") + log_admin("[key_name(usr)] toggled deadchat.") + message_admins("[key_name_admin(usr)] toggled deadchat.", 1) + feedback_add_details("admin_verb","TDSAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc + +/datum/admins/proc/toggleoocdead() + set category = "Server" + set desc="Toggle Dead OOC." + set name="Toggle Dead OOC" + + if(!check_rights(R_ADMIN)) + return + + config.dooc_allowed = !( config.dooc_allowed ) + log_admin("[key_name(usr)] toggled Dead OOC.") + message_admins("[key_name_admin(usr)] toggled Dead OOC.", 1) + feedback_add_details("admin_verb","TDOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/togglehubvisibility() + set category = "Server" + set desc="Globally Toggles Hub Visibility" + set name="Toggle Hub Visibility" + + if(!check_rights(R_ADMIN)) + return + + world.visibility = !(world.visibility) + log_admin("[key_name(usr)] toggled hub visibility.") + message_admins("[key_name_admin(usr)] toggled hub visibility. The server is now [world.visibility ? "visible" : "invisible"] ([world.visibility]).", 1) + feedback_add_details("admin_verb","THUB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc + +/datum/admins/proc/toggletraitorscaling() + set category = "Server" + set desc="Toggle traitor scaling" + set name="Toggle Traitor Scaling" + config.traitor_scaling = !config.traitor_scaling + log_admin("[key_name(usr)] toggled Traitor Scaling to [config.traitor_scaling].") + message_admins("[key_name_admin(usr)] toggled Traitor Scaling [config.traitor_scaling ? "on" : "off"].", 1) + feedback_add_details("admin_verb","TTS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/startnow() + set category = "Server" + set desc="Start the round ASAP" + set name="Start Now" + + if(!check_rights(R_SERVER|R_EVENT)) + return + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "Error: Start Now: Game has already started.") + return + if(!SSticker.start_immediately) + SSticker.start_immediately = TRUE + var/msg = "" + if(SSticker.current_state == GAME_STATE_INIT) + msg = " (The server is still setting up, but the round will be started as soon as possible.)" + log_admin("[key_name(usr)] has started the game.[msg]") + message_admins("[key_name_admin(usr)] has started the game.[msg]") + feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + else + SSticker.start_immediately = FALSE + to_world("Immediate game start canceled. Normal startup resumed.") + log_and_message_admins("cancelled immediate game start.") + +/datum/admins/proc/toggleenter() + set category = "Server" + set desc="People can't enter" + set name="Toggle Entering" + config.enter_allowed = !(config.enter_allowed) + if (!(config.enter_allowed)) + to_world("New players may no longer enter the game.") + else + to_world("New players may now enter the game.") + log_admin("[key_name(usr)] toggled new player game entering.") + message_admins(span_blue("[key_name_admin(usr)] toggled new player game entering."), 1) + world.update_status() + feedback_add_details("admin_verb","TE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleAI() + set category = "Server" + set desc="People can't be AI" + set name="Toggle AI" + config.allow_ai = !( config.allow_ai ) + if (!( config.allow_ai )) + to_world("The AI job is no longer chooseable.") + else + to_world("The AI job is chooseable now.") + log_admin("[key_name(usr)] toggled AI allowed.") + world.update_status() + feedback_add_details("admin_verb","TAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleaban() + set category = "Server" + set desc="Respawn basically" + set name="Toggle Respawn" + config.abandon_allowed = !(config.abandon_allowed) + if(config.abandon_allowed) + to_world("You may now respawn.") + else + to_world("You may no longer respawn :(") + message_admins(span_blue("[key_name_admin(usr)] toggled respawn to [config.abandon_allowed ? "On" : "Off"]."), 1) + log_admin("[key_name(usr)] toggled respawn to [config.abandon_allowed ? "On" : "Off"].") + world.update_status() + feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/togglepersistence() + set category = "Server" + set desc="Whether persistent data will be saved from now on." + set name="Toggle Persistent Data" + config.persistence_disabled = !(config.persistence_disabled) + if(!config.persistence_disabled) + to_world("Persistence is now enabled..") + else + to_world("Persistence is no longer enabled.") + message_admins(span_blue("[key_name_admin(usr)] toggled persistence to [config.persistence_disabled ? "Off" : "On"]."), 1) + log_admin("[key_name(usr)] toggled persistence to [config.persistence_disabled ? "Off" : "On"].") + world.update_status() + feedback_add_details("admin_verb","TPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/togglemaploadpersistence() + set category = "Server" + set desc="Whether mapload persistent data will be saved from now on." + set name="Toggle Mapload Persistent Data" + config.persistence_ignore_mapload = !(config.persistence_ignore_mapload) + if(!config.persistence_ignore_mapload) + to_world("Persistence is now enabled..") + else + to_world("Persistence is no longer enabled.") + message_admins(span_blue("[key_name_admin(usr)] toggled persistence to [config.persistence_ignore_mapload ? "Off" : "On"]."), 1) + log_admin("[key_name(usr)] toggled persistence to [config.persistence_ignore_mapload ? "Off" : "On"].") + world.update_status() + feedback_add_details("admin_verb","TMPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggle_aliens() + set category = "Server" + set desc="Toggle alien mobs" + set name="Toggle Aliens" + config.aliens_allowed = !config.aliens_allowed + log_admin("[key_name(usr)] toggled Aliens to [config.aliens_allowed].") + message_admins("[key_name_admin(usr)] toggled Aliens [config.aliens_allowed ? "on" : "off"].", 1) + feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggle_space_ninja() + set category = "Server" + set desc="Toggle space ninjas spawning." + set name="Toggle Space Ninjas" + config.ninjas_allowed = !config.ninjas_allowed + log_admin("[key_name(usr)] toggled Space Ninjas to [config.ninjas_allowed].") + message_admins("[key_name_admin(usr)] toggled Space Ninjas [config.ninjas_allowed ? "on" : "off"].", 1) + feedback_add_details("admin_verb","TSN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/delay() + set category = "Server" + set desc="Delay the game start/end" + set name="Delay" + + if(!check_rights(R_SERVER|R_EVENT)) return + if (SSticker.current_state >= GAME_STATE_PLAYING) + SSticker.delay_end = !SSticker.delay_end + log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") + message_admins(span_blue("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"]."), 1) + return + round_progressing = !round_progressing + if (!round_progressing) + to_world("The game start has been delayed.") + log_admin("[key_name(usr)] delayed the game.") + else + to_world("The game will start soon.") + log_admin("[key_name(usr)] removed the delay.") + feedback_add_details("admin_verb","DELAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/adjump() + set category = "Server" + set desc="Toggle admin jumping" + set name="Toggle Jump" + config.allow_admin_jump = !(config.allow_admin_jump) + message_admins(span_blue("Toggled admin jumping to [config.allow_admin_jump].")) + feedback_add_details("admin_verb","TJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/adspawn() + set category = "Server" + set desc="Toggle admin spawning" + set name="Toggle Spawn" + config.allow_admin_spawning = !(config.allow_admin_spawning) + message_admins(span_blue("Toggled admin item spawning to [config.allow_admin_spawning].")) + feedback_add_details("admin_verb","TAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/adrev() + set category = "Server" + set desc="Toggle admin revives" + set name="Toggle Revive" + config.allow_admin_rev = !(config.allow_admin_rev) + message_admins(span_blue("Toggled reviving to [config.allow_admin_rev].")) + feedback_add_details("admin_verb","TAR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/immreboot() + set category = "Server" + set desc="Reboots the server post haste" + set name="Immediate Reboot" + if(!usr.client.holder) return + if(alert(usr, "Reboot server?","Reboot!","Yes","No") == "No") // Not tgui_alert for safety + return + to_world("[span_red("Rebooting world!")] [span_blue("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!")]") + log_admin("[key_name(usr)] initiated an immediate reboot.") + + feedback_set_details("end_error","immediate admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]") + feedback_add_details("admin_verb","IR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(blackbox) + blackbox.save_all_data_to_sql() + + world.Reboot() + +/datum/admins/proc/unprison(var/mob/M in mob_list) + set category = "Admin" + set name = "Unprison" + if (M.z == 2) + if (config.allow_admin_jump) + M.loc = pick(latejoin) + message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]", 1) + log_admin("[key_name(usr)] has unprisoned [key_name(M)]") + else + tgui_alert_async(usr, "Admin jumping disabled") + else + tgui_alert_async(usr, "[M.name] is not prisoned.") + feedback_add_details("admin_verb","UP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS + +/proc/is_special_character(var/character) // returns 1 for special characters and 2 for heroes of gamemode + if(!ticker || !ticker.mode) + return 0 + var/datum/mind/M + if (ismob(character)) + var/mob/C = character + M = C.mind + else if(istype(character, /datum/mind)) + M = character + + if(M) + if(ticker.mode.antag_templates && ticker.mode.antag_templates.len) + for(var/datum/antagonist/antag in ticker.mode.antag_templates) + if(antag.is_antagonist(M)) + return 2 + if(M.special_role) + return 1 + + if(isrobot(character)) + var/mob/living/silicon/robot/R = character + if(R.emagged) + return 1 + + return 0 + +/datum/admins/proc/spawn_fruit(seedtype in SSplants.seeds) + set category = "Debug" + set desc = "Spawn the product of a seed." + set name = "Spawn Fruit" + + if(!check_rights(R_SPAWN)) return + + if(!seedtype || !SSplants.seeds[seedtype]) + return + var/amount = tgui_input_number(usr, "Amount of fruit to spawn", "Fruit Amount", 1) + if(!isnull(amount)) + var/datum/seed/S = SSplants.seeds[seedtype] + S.harvest(usr,0,0,amount) + log_admin("[key_name(usr)] spawned [seedtype] fruit at ([usr.x],[usr.y],[usr.z])") + +/datum/admins/proc/spawn_custom_item() + set category = "Debug" + set desc = "Spawn a custom item." + set name = "Spawn Custom Item" + + if(!check_rights(R_SPAWN)) return + + var/owner = tgui_input_list(usr, "Select a ckey.", "Spawn Custom Item", custom_items) + if(!owner|| !custom_items[owner]) + return + + var/list/possible_items = custom_items[owner] + var/datum/custom_item/item_to_spawn = tgui_input_list(usr, "Select an item to spawn.", "Spawn Custom Item", possible_items) + if(!item_to_spawn) + return + + item_to_spawn.spawn_item(get_turf(usr)) + +/datum/admins/proc/check_custom_items() + + set category = "Debug" + set desc = "Check the custom item list." + set name = "Check Custom Items" + + if(!check_rights(R_SPAWN)) return + + if(!custom_items) + to_chat(usr, "Custom item list is null.") + return + + if(!custom_items.len) + to_chat(usr, "Custom item list not populated.") + return + + for(var/assoc_key in custom_items) + to_chat(usr, "[assoc_key] has:") + var/list/current_items = custom_items[assoc_key] + for(var/datum/custom_item/item in current_items) + to_chat(usr, "- name: [item.name] icon: [item.item_icon] path: [item.item_path] desc: [item.item_desc]") + +/datum/admins/proc/spawn_plant(seedtype in SSplants.seeds) + set category = "Debug" + set desc = "Spawn a spreading plant effect." + set name = "Spawn Plant" + + if(!check_rights(R_SPAWN)) return + + if(!seedtype || !SSplants.seeds[seedtype]) + return + new /obj/effect/plant(get_turf(usr), SSplants.seeds[seedtype]) + log_admin("[key_name(usr)] spawned [seedtype] vines at ([usr.x],[usr.y],[usr.z])") + +/datum/admins/proc/spawn_atom(var/object as text) + set name = "Spawn" + set category = "Debug" + set desc = "(atom path) Spawn an atom" + + if(!check_rights(R_SPAWN)) return + + var/list/types = typesof(/atom) + var/list/matches = new() + + for(var/path in types) + if(findtext("[path]", object)) + matches += path + + if(matches.len==0) + return + + var/chosen + if(matches.len==1) + chosen = matches[1] + else + chosen = tgui_input_list(usr, "Select an atom type", "Spawn Atom", matches) + if(!chosen) + return + + if(ispath(chosen,/turf)) + var/turf/T = get_turf(usr.loc) + T.ChangeTurf(chosen) + else + new chosen(usr.loc) + + log_and_message_admins("spawned [chosen] at ([usr.x],[usr.y],[usr.z])") + feedback_add_details("admin_verb","SA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/admins/proc/show_traitor_panel(var/mob/M in mob_list) + set category = "Admin" + set desc = "Edit mobs's memory and role" + set name = "Show Traitor Panel" + + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!M.mind) + to_chat(usr, "This mob has no mind!") + return + + M.mind.edit_memory() + feedback_add_details("admin_verb","STP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/show_game_mode() + set category = "Admin" + set desc = "Show the current round configuration." + set name = "Show Game Mode" + + if(!ticker || !ticker.mode) + tgui_alert_async(usr, "Not before roundstart!", "Alert") + return + + var/out = "Current mode: [ticker.mode.name] ([ticker.mode.config_tag])
                    " + out += "
                    " + + if(ticker.mode.ert_disabled) + out += "Emergency Response Teams: disabled" + else + out += "Emergency Response Teams: enabled" + out += "
                    " + + if(ticker.mode.deny_respawn) + out += "Respawning: disallowed" + else + out += "Respawning: allowed" + out += "
                    " + + out += "Shuttle delay multiplier: [ticker.mode.shuttle_delay]
                    " + + if(ticker.mode.auto_recall_shuttle) + out += "Shuttle auto-recall: enabled" + else + out += "Shuttle auto-recall: disabled" + out += "

                    " + + if(ticker.mode.event_delay_mod_moderate) + out += "Moderate event time modifier: [ticker.mode.event_delay_mod_moderate]
                    " + else + out += "Moderate event time modifier: unset
                    " + + if(ticker.mode.event_delay_mod_major) + out += "Major event time modifier: [ticker.mode.event_delay_mod_major]
                    " + else + out += "Major event time modifier: unset
                    " + + out += "
                    " + + if(ticker.mode.antag_tags && ticker.mode.antag_tags.len) + out += "Core antag templates:
                    " + for(var/antag_tag in ticker.mode.antag_tags) + out += "[antag_tag].
                    " + + if(ticker.mode.round_autoantag) + out += "Autotraitor enabled." + if(ticker.mode.antag_scaling_coeff > 0) + out += " (scaling with [ticker.mode.antag_scaling_coeff])" + else + out += " (not currently scaling, set a coefficient)" + out += "
                    " + else + out += "Autotraitor disabled.
                    " + + out += "All antag ids:" + if(ticker.mode.antag_templates && ticker.mode.antag_templates.len) + for(var/datum/antagonist/antag in ticker.mode.antag_templates) + antag.update_current_antag_max() + out += " [antag.id]" + out += " ([antag.get_antag_count()]/[antag.cur_max]) " + out += " \[-\]
                    " + else + out += " None." + out += " \[+\]
                    " + + usr << browse(out, "window=edit_mode[src]") + feedback_add_details("admin_verb","SGM") + + +/datum/admins/proc/toggletintedweldhelmets() + set category = "Debug" + set desc="Reduces view range when wearing welding helmets" + set name="Toggle tinted welding helmets." + config.welder_vision = !( config.welder_vision ) + if (config.welder_vision) + to_world("Reduced welder vision has been enabled!") + else + to_world("Reduced welder vision has been disabled!") + log_admin("[key_name(usr)] toggled welder vision.") + message_admins("[key_name_admin(usr)] toggled welder vision.", 1) + feedback_add_details("admin_verb","TTWH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleguests() + set category = "Server" + set desc="Guests can't enter" + set name="Toggle guests" + config.guests_allowed = !(config.guests_allowed) + if (!(config.guests_allowed)) + to_world("Guests may no longer enter the game.") + else + to_world("Guests may now enter the game.") + log_admin("[key_name(usr)] toggled guests game entering [config.guests_allowed?"":"dis"]allowed.") + message_admins(span_blue("[key_name_admin(usr)] toggled guests game entering [config.guests_allowed?"":"dis"]allowed."), 1) + feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/output_ai_laws() + var/ai_number = 0 + for(var/mob/living/silicon/S in mob_list) + ai_number++ + if(isAI(S)) + to_chat(usr, "AI [key_name(S, usr)]'s laws:") + else if(isrobot(S)) + var/mob/living/silicon/robot/R = S + to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independent)"]: laws:") + else if (ispAI(S)) + to_chat(usr, "pAI [key_name(S, usr)]'s laws:") + else + to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") + + if (S.laws == null) + to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") + else + S.laws.show_laws(usr) + if(!ai_number) + to_chat(usr, "No AIs located") //Just so you know the thing is actually working and not just ignoring you. + +/datum/admins/proc/show_skills() + set category = "Admin" + set name = "Show Skills" + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + var/mob/living/carbon/human/M = tgui_input_list(usr, "Select mob.", "Select mob.", human_mob_list) + if(!M) return + + show_skill_window(usr, M) + + return + +/client/proc/update_mob_sprite(mob/living/carbon/human/H as mob) + set category = "Admin" + set name = "Update Mob Sprite" + set desc = "Should fix any mob sprite update errors." + + if (!holder) + to_chat(src, "Only administrators may use this command.") + return + + if(istype(H)) + H.regenerate_icons() + +/proc/get_options_bar(whom, detail = 2, name = 0, link = 1, highlight_special = 1) + if(!whom) + return "(*null*)" + var/mob/M + var/client/C + if(istype(whom, /client)) + C = whom + M = C.mob + else if(istype(whom, /mob)) + M = whom + C = M.client + else + return "(*not a mob*)" + switch(detail) + if(0) + return "[key_name(C, link, name, highlight_special)]" + + if(1) //Private Messages + return "[key_name(C, link, name, highlight_special)](?)" + + if(2) //Admins + var/ref_mob = "\ref[M]" + return "[key_name(C, link, name, highlight_special)](?) (PP) (VV) (SM) ([admin_jump_link(M)]) (CA) (TAKE)" + + if(3) //Devs + var/ref_mob = "\ref[M]" + return "[key_name(C, link, name, highlight_special)](VV)([admin_jump_link(M)]) (TAKE)" + + if(4) //Event Managers + var/ref_mob = "\ref[M]" + return "[key_name(C, link, name, highlight_special)] (?) (PP) (VV) (SM) ([admin_jump_link(M)]) (TAKE)" + + +/proc/ishost(whom) + if(!whom) + return 0 + var/client/C + var/mob/M + if(istype(whom, /client)) + C = whom + if(istype(whom, /mob)) + M = whom + C = M.client + if(R_HOST & C.holder.rights) + return 1 + else + return 0 +// +// +//ALL DONE +//********************************************************************************************************* +// + +//Returns 1 to let the dragdrop code know we are trapping this event +//Returns 0 if we don't plan to trap the event +/datum/admins/proc/cmd_ghost_drag(var/mob/observer/dead/frommob, var/mob/living/tomob) + if(!istype(frommob)) + return //Extra sanity check to make sure only observers are shoved into things + + //Same as assume-direct-control perm requirements. + if (!check_rights(R_VAREDIT,0) || !check_rights(R_ADMIN|R_DEBUG|R_EVENT,0)) + return 0 + if (!frommob.ckey) + return 0 + var/question = "" + if (tomob.ckey) + question = "This mob already has a user ([tomob.key]) in control of it! " + question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" + var/ask = tgui_alert(usr, question, "Place ghost in control of mob?", list("Yes", "No")) + if (ask != "Yes") + return 1 + if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response + return 1 + if(tomob.client) //No need to ghostize if there is no client + tomob.ghostize(0) + if(frommob.mind && frommob.mind.current) //Preserve teleop for original body when adminghosting. + var/mob/body = frommob.mind.current + if(body) + if(body.teleop) + body.teleop = tomob + message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") + log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") + feedback_add_details("admin_verb","CGD") + tomob.ckey = frommob.ckey + qdel(frommob) + return 1 + +/datum/admins/proc/force_antag_latespawn() + set category = "Admin" + set name = "Force Template Spawn" + set desc = "Force an antagonist template to spawn." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + if(!ticker || !ticker.mode) + to_chat(usr, "Mode has not started.") + return + + var/antag_type = tgui_input_list(usr, "Choose a template.","Force Latespawn", all_antag_types) + if(!antag_type || !all_antag_types[antag_type]) + to_chat(usr, "Aborting.") + return + + var/datum/antagonist/antag = all_antag_types[antag_type] + message_admins("[key_name(usr)] attempting to force latespawn with template [antag.id].") + antag.attempt_late_spawn() + +/datum/admins/proc/force_mode_latespawn() + set category = "Admin" + set name = "Force Mode Spawn" + set desc = "Force autotraitor to proc." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins) || !check_rights(R_ADMIN|R_EVENT|R_FUN)) + to_chat(usr, "Error: you are not an admin!") + return + + if(!ticker || !ticker.mode) + to_chat(usr, "Mode has not started.") + return + + log_and_message_admins("attempting to force mode autospawn.") + ticker.mode.try_latespawn() + +/datum/admins/proc/paralyze_mob(mob/living/H as mob) + set category = "Admin" + set name = "Toggle Paralyze" + set desc = "Paralyzes a player. Or unparalyses them." + + var/msg + + if(check_rights(R_ADMIN|R_MOD|R_EVENT)) + if (H.paralysis == 0) + H.SetParalysis(8000) + msg = "has paralyzed [key_name(H)]." + log_and_message_admins(msg) + else + if(tgui_alert(src, "[key_name(H)] is paralyzed, would you like to unparalyze them?","Paralyze Mob",list("Yes","No")) == "Yes") + H.SetParalysis(0) + msg = "has unparalyzed [key_name(H)]." + log_and_message_admins(msg) + +/datum/admins/proc/set_tcrystals(mob/living/carbon/human/H as mob) + set category = "Debug" + set name = "Set Telecrystals" + set desc = "Allows admins to change telecrystals of a user." + set popup_menu = FALSE //VOREStation Edit - Declutter. + var/crystals + + if(check_rights(R_ADMIN|R_EVENT)) + crystals = tgui_input_number(usr, "Amount of telecrystals for [H.ckey], currently [H.mind.tcrystals].", crystals) + if (!isnull(crystals)) + H.mind.tcrystals = crystals + var/msg = "[key_name(usr)] has modified [H.ckey]'s telecrystals to [crystals]." + message_admins(msg) + else + to_chat(usr, "You do not have access to this command.") + +/datum/admins/proc/add_tcrystals(mob/living/carbon/human/H as mob) + set category = "Debug" + set name = "Add Telecrystals" + set desc = "Allows admins to change telecrystals of a user by addition." + set popup_menu = FALSE //VOREStation Edit - Declutter. + var/crystals + + if(check_rights(R_ADMIN|R_EVENT)) + crystals = tgui_input_number(usr, "Amount of telecrystals to give to [H.ckey], currently [H.mind.tcrystals].", crystals) + if (!isnull(crystals)) + H.mind.tcrystals += crystals + var/msg = "[key_name(usr)] has added [crystals] to [H.ckey]'s telecrystals." + message_admins(msg) + else + to_chat(usr, "You do not have access to this command.") + + +/datum/admins/proc/sendFax() + set category = "Special Verbs" + set name = "Send Fax" + set desc = "Sends a fax to this machine" + var/department = tgui_input_list(usr, "Choose a fax", "Fax", alldepartments) + for(var/obj/machinery/photocopier/faxmachine/sendto in allfaxes) + if(sendto.department == department) + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + + var/replyorigin = tgui_input_text(src.owner, "Please specify who the fax is coming from", "Origin") + + var/obj/item/weapon/paper/admin/P = new /obj/item/weapon/paper/admin( null ) //hopefully the null loc won't cause trouble for us + faxreply = P + + P.admindatum = src + P.origin = replyorigin + P.destination = sendto + + P.adminbrowse() + + +/datum/admins/var/obj/item/weapon/paper/admin/faxreply // var to hold fax replies in + +/datum/admins/proc/faxCallback(var/obj/item/weapon/paper/admin/P, var/obj/machinery/photocopier/faxmachine/destination) + var/customname = tgui_input_text(src.owner, "Pick a title for the report", "Title") + + P.name = "[P.origin] - [customname]" + P.desc = "This is a paper titled '" + P.name + "'." + + var/shouldStamp = 1 + if(!P.sender) // admin initiated + switch(tgui_alert(usr, "Would you like the fax stamped?","Stamped?", list("Yes", "No"))) + if("No") + shouldStamp = 0 + + if(shouldStamp) + P.stamps += "
                    This paper has been stamped by the [P.origin] Quantum Relay." + + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + var/x = rand(-2, 0) + var/y = rand(-1, 2) + P.offset_x += x + P.offset_y += y + stampoverlay.pixel_x = x + stampoverlay.pixel_y = y + + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-cent" + stampoverlay.icon_state = "paper_stamp-cent" + + if(!P.stamped) + P.stamped = new + P.stamped += /obj/item/weapon/stamp/centcomm + P.add_overlay(stampoverlay) + + var/obj/item/rcvdcopy + rcvdcopy = destination.copy(P) + rcvdcopy.loc = null //hopefully this shouldn't cause trouble + adminfaxes += rcvdcopy + + + + if(destination.receivefax(P)) + to_chat(src.owner, "Message reply to transmitted successfully.") + if(P.sender) // sent as a reply + log_admin("[key_name(src.owner)] replied to a fax message from [key_name(P.sender)]") + for(var/client/C in GLOB.admins) + if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights) + to_chat(C, "FAX LOG:[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (VIEW)") + else + log_admin("[key_name(src.owner)] has sent a fax message to [destination.department]") + for(var/client/C in GLOB.admins) + if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights) + to_chat(C, "FAX LOG:[key_name_admin(src.owner)] has sent a fax message to [destination.department] (VIEW)") + + var/plaintext_title = P.sender ? "replied to [key_name(P.sender)]'s fax" : "sent a fax message to [destination.department]" + var/fax_text = paper_html_to_plaintext(P.info) + log_game(plaintext_title) + log_game(fax_text) + + SSwebhooks.send( + WEBHOOK_FAX_SENT, + list( + "name" = "[key_name(owner)] [plaintext_title].", + "body" = fax_text + ) + ) + + else + to_chat(src.owner, "Message reply failed.") + + spawn(100) + qdel(P) + faxreply = null + return diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index 74b75d71079..d1dd7b7eda8 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -1,165 +1,165 @@ -var/list/admin_ranks = list() //list of all ranks with associated rights - -//load our rank - > rights associations -/proc/load_admin_ranks() - admin_ranks.Cut() - - var/previous_rights = 0 - - //Clear profile access - for(var/A in world.GetConfig("admin")) - world.SetConfig("APP/admin", A, null) - - //load text from file - var/list/Lines = file2list("config/admin_ranks.txt") - - //process each line seperately - for(var/line in Lines) - if(!length(line)) continue - if(copytext(line,1,2) == "#") continue - - var/list/List = splittext(line,"+") - if(!List.len) continue - - var/rank = ckeyEx(List[1]) - switch(rank) - if(null,"") continue - if("Removed") continue //Reserved - - var/rights = 0 - for(var/i=2, i<=List.len, i++) - switch(ckey(List[i])) - if("@","prev") rights |= previous_rights - if("buildmode","build") rights |= R_BUILDMODE - if("admin") rights |= R_ADMIN - if("ban") rights |= R_BAN - if("fun") rights |= R_FUN - if("server") rights |= R_SERVER - if("debug") rights |= R_DEBUG - if("permissions","rights") rights |= R_PERMISSIONS - if("possess") rights |= R_POSSESS - if("stealth") rights |= R_STEALTH - if("rejuv","rejuvinate") rights |= R_REJUVINATE - if("varedit") rights |= R_VAREDIT - if("everything","host","all") rights |= (R_HOST | R_BUILDMODE | R_ADMIN | R_BAN | R_FUN | R_SERVER | R_DEBUG | R_PERMISSIONS | R_POSSESS | R_STEALTH | R_REJUVINATE | R_VAREDIT | R_SOUNDS | R_SPAWN | R_MOD| R_EVENT) - if("sound","sounds") rights |= R_SOUNDS - if("spawn","create") rights |= R_SPAWN - if("mod") rights |= R_MOD - if("event") rights |= R_EVENT - - admin_ranks[rank] = rights - previous_rights = rights - - #ifdef TESTING - var/msg = "Permission Sets Built:\n" - for(var/rank in admin_ranks) - msg += "\t[rank] - [admin_ranks[rank]]\n" - testing(msg) - #endif - -/hook/startup/proc/loadAdmins() - load_admins() - return 1 - -/proc/load_admins() - //clear the datums references - admin_datums.Cut() - for(var/client/C in GLOB.admins) - C.remove_admin_verbs() - C.holder = null - GLOB.admins.Cut() - - if(config.admin_legacy_system) - load_admin_ranks() - - //load text from file - var/list/Lines = file2list("config/admins.txt") - - //process each line seperately - for(var/line in Lines) - if(!length(line)) continue - if(copytext(line,1,2) == "#") continue - - //Split the line at every "-" - var/list/List = splittext(line, "-") - if(!List.len) continue - - //ckey is before the first "-" - var/ckey = ckey(List[1]) - if(!ckey) continue - - //rank follows the first "-" - var/rank = "" - if(List.len >= 2) - rank = ckeyEx(List[2]) - - //load permissions associated with this rank - var/rights = admin_ranks[rank] - - //create the admin datum and store it for later use - var/datum/admins/D = new /datum/admins(rank, rights, ckey) - - //find the client for a ckey if they are connected and associate them with the new admin datum - D.associate(GLOB.directory[ckey]) - - else - //The current admin system uses SQL - - establish_db_connection() - if(!dbcon.IsConnected()) - error("Failed to connect to database in load_admins(). Reverting to legacy system.") - log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.") - config.admin_legacy_system = 1 - load_admins() - return - - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin") - query.Execute() - while(query.NextRow()) - var/ckey = query.item[1] - var/rank = query.item[2] - if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes. - - var/rights = query.item[4] - if(istext(rights)) rights = text2num(rights) - var/datum/admins/D = new /datum/admins(rank, rights, ckey) - - //find the client for a ckey if they are connected and associate them with the new admin datum - D.associate(GLOB.directory[ckey]) - if(!admin_datums) - error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") - log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") - config.admin_legacy_system = 1 - load_admins() - return - - #ifdef TESTING - var/msg = "Admins Built:\n" - for(var/ckey in admin_datums) - var/rank - var/datum/admins/D = admin_datums[ckey] - if(D) rank = D.rank - msg += "\t[ckey] - [rank]\n" - testing(msg) - #endif - - -#ifdef TESTING -/client/verb/changerank(newrank in admin_ranks) - if(holder) - holder.rank = newrank - holder.rights = admin_ranks[newrank] - else - holder = new /datum/admins(newrank,admin_ranks[newrank],ckey) - remove_admin_verbs() - holder.associate(src) - -/client/verb/changerights(newrights as num) - if(holder) - holder.rights = newrights - else - holder = new /datum/admins("testing",newrights,ckey) - remove_admin_verbs() - holder.associate(src) - -#endif +var/list/admin_ranks = list() //list of all ranks with associated rights + +//load our rank - > rights associations +/proc/load_admin_ranks() + admin_ranks.Cut() + + var/previous_rights = 0 + + //Clear profile access + for(var/A in world.GetConfig("admin")) + world.SetConfig("APP/admin", A, null) + + //load text from file + var/list/Lines = file2list("config/admin_ranks.txt") + + //process each line seperately + for(var/line in Lines) + if(!length(line)) continue + if(copytext(line,1,2) == "#") continue + + var/list/List = splittext(line,"+") + if(!List.len) continue + + var/rank = ckeyEx(List[1]) + switch(rank) + if(null,"") continue + if("Removed") continue //Reserved + + var/rights = 0 + for(var/i=2, i<=List.len, i++) + switch(ckey(List[i])) + if("@","prev") rights |= previous_rights + if("buildmode","build") rights |= R_BUILDMODE + if("admin") rights |= R_ADMIN + if("ban") rights |= R_BAN + if("fun") rights |= R_FUN + if("server") rights |= R_SERVER + if("debug") rights |= R_DEBUG + if("permissions","rights") rights |= R_PERMISSIONS + if("possess") rights |= R_POSSESS + if("stealth") rights |= R_STEALTH + if("rejuv","rejuvinate") rights |= R_REJUVINATE + if("varedit") rights |= R_VAREDIT + if("everything","host","all") rights |= (R_HOST | R_BUILDMODE | R_ADMIN | R_BAN | R_FUN | R_SERVER | R_DEBUG | R_PERMISSIONS | R_POSSESS | R_STEALTH | R_REJUVINATE | R_VAREDIT | R_SOUNDS | R_SPAWN | R_MOD| R_EVENT) + if("sound","sounds") rights |= R_SOUNDS + if("spawn","create") rights |= R_SPAWN + if("mod") rights |= R_MOD + if("event") rights |= R_EVENT + + admin_ranks[rank] = rights + previous_rights = rights + + #ifdef TESTING + var/msg = "Permission Sets Built:\n" + for(var/rank in admin_ranks) + msg += "\t[rank] - [admin_ranks[rank]]\n" + testing(msg) + #endif + +/hook/startup/proc/loadAdmins() + load_admins() + return 1 + +/proc/load_admins() + //clear the datums references + admin_datums.Cut() + for(var/client/C in GLOB.admins) + C.remove_admin_verbs() + C.holder = null + GLOB.admins.Cut() + + if(config.admin_legacy_system) + load_admin_ranks() + + //load text from file + var/list/Lines = file2list("config/admins.txt") + + //process each line seperately + for(var/line in Lines) + if(!length(line)) continue + if(copytext(line,1,2) == "#") continue + + //Split the line at every "-" + var/list/List = splittext(line, "-") + if(!List.len) continue + + //ckey is before the first "-" + var/ckey = ckey(List[1]) + if(!ckey) continue + + //rank follows the first "-" + var/rank = "" + if(List.len >= 2) + rank = ckeyEx(List[2]) + + //load permissions associated with this rank + var/rights = admin_ranks[rank] + + //create the admin datum and store it for later use + var/datum/admins/D = new /datum/admins(rank, rights, ckey) + + //find the client for a ckey if they are connected and associate them with the new admin datum + D.associate(GLOB.directory[ckey]) + + else + //The current admin system uses SQL + + establish_db_connection() + if(!dbcon.IsConnected()) + error("Failed to connect to database in load_admins(). Reverting to legacy system.") + log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.") + config.admin_legacy_system = 1 + load_admins() + return + + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin") + query.Execute() + while(query.NextRow()) + var/ckey = query.item[1] + var/rank = query.item[2] + if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes. + + var/rights = query.item[4] + if(istext(rights)) rights = text2num(rights) + var/datum/admins/D = new /datum/admins(rank, rights, ckey) + + //find the client for a ckey if they are connected and associate them with the new admin datum + D.associate(GLOB.directory[ckey]) + if(!admin_datums) + error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") + log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") + config.admin_legacy_system = 1 + load_admins() + return + + #ifdef TESTING + var/msg = "Admins Built:\n" + for(var/ckey in admin_datums) + var/rank + var/datum/admins/D = admin_datums[ckey] + if(D) rank = D.rank + msg += "\t[ckey] - [rank]\n" + testing(msg) + #endif + + +#ifdef TESTING +/client/verb/changerank(newrank in admin_ranks) + if(holder) + holder.rank = newrank + holder.rights = admin_ranks[newrank] + else + holder = new /datum/admins(newrank,admin_ranks[newrank],ckey) + remove_admin_verbs() + holder.associate(src) + +/client/verb/changerights(newrights as num) + if(holder) + holder.rights = newrights + else + holder = new /datum/admins("testing",newrights,ckey) + remove_admin_verbs() + holder.associate(src) + +#endif diff --git a/code/modules/admin/admin_report.dm b/code/modules/admin/admin_report.dm index e0c38345e5d..6cfb778d911 100644 --- a/code/modules/admin/admin_report.dm +++ b/code/modules/admin/admin_report.dm @@ -1,182 +1,182 @@ -// Reports are a way to notify admins of wrongdoings that happened -// while no admin was present. They work a bit similar to news, but -// they can only be read by admins and moderators. - -// a single admin report -/datum//admin_report/var - ID // the ID of the report - body // the content of the report - author // key of the author - date // date on which this was created - done // whether this was handled - - offender_key // store the key of the offender - offender_cid // store the cid of the offender - -/datum//report_topic_handler - Topic(href,href_list) - ..() - var/client/C = locate(href_list["client"]) - if(href_list["action"] == "show_reports") - C.display_admin_reports() - else if(href_list["action"] == "remove") - C.mark_report_done(text2num(href_list["ID"])) - else if(href_list["action"] == "edit") - C.edit_report(text2num(href_list["ID"])) - -var/datum/report_topic_handler/report_topic_handler - -world/New() - ..() - report_topic_handler = new - -// add a new news datums -/proc/make_report(body, author, okey, cid) - var/savefile/Reports = new("data/reports.sav") - var/list/reports - var/lastID - - Reports["reports"] >> reports - Reports["lastID"] >> lastID - - if(!reports) reports = list() - if(!lastID) lastID = 0 - - var/datum/admin_report/created = new() - created.ID = ++lastID - created.body = body - created.author = author - created.date = world.realtime - created.done = 0 - created.offender_key = okey - created.offender_cid = cid - - reports.Insert(1, created) - - Reports["reports"] << reports - Reports["lastID"] << lastID - -// load the reports from disk -/proc/load_reports() - var/savefile/Reports = new("data/reports.sav") - var/list/reports - - Reports["reports"] >> reports - - if(!reports) reports = list() - - return reports - -// check if there are any unhandled reports -/client/proc/unhandled_reports() - if(!src.holder) return 0 - var/list/reports = load_reports() - - for(var/datum/admin_report/N in reports) - if(N.done) - continue - else return 1 - - return 0 - -// checks if the player has an unhandled report against him -/client/proc/is_reported() - var/list/reports = load_reports() - - for(var/datum/admin_report/N in reports) if(!N.done) - if(N.offender_key == src.key) - return 1 - - return 0 - -// display only the reports that haven't been handled -/client/proc/display_admin_reports() - set category = "Admin" - set name = "Display Admin Reports" - if(!src.holder) return - - var/list/reports = load_reports() - - var/output = "" - if(unhandled_reports()) - // load the list of unhandled reports - for(var/datum/admin_report/N in reports) - if(N.done) - continue - output += "Reported player: [N.offender_key](CID: [N.offender_cid])
                    " - output += "Offense:[N.body]
                    " - output += "Occurred at [time2text(N.date,"MM/DD hh:mm:ss")]
                    " - output += "authored by [N.author]
                    " - output += " Flag as Handled" - if(src.key == N.author) - output += " Edit" - output += "
                    " - output += "
                    " - else - output += "Whoops, no reports!" - - usr << browse(output, "window=news;size=600x400") - - -/client/proc/Report(mob/M as mob in world) - set category = "Admin" - if(!src.holder) - return - - var/CID = "Unknown" - if(M.client) - CID = M.client.computer_id - - var/body = tgui_input_text(src.mob, "Describe in detail what you're reporting [M] for", "Report") - if(!body) return - - - make_report(body, key, M.key, CID) - - spawn(1) - display_admin_reports() - -/client/proc/mark_report_done(ID as num) - if(!src.holder || src.holder.level < 0) - return - - var/savefile/Reports = new("data/reports.sav") - var/list/reports - - Reports["reports"] >> reports - - var/datum/admin_report/found - for(var/datum/admin_report/N in reports) - if(N.ID == ID) - found = N - if(!found) - to_chat(src, "* An error occurred, sorry.") - - found.done = 1 - - Reports["reports"] << reports - - -/client/proc/edit_report(ID as num) - if(!src.holder || src.holder.level < 0) - to_chat(src, "You tried to modify the news, but you're not an admin!") - return - - var/savefile/Reports = new("data/reports.sav") - var/list/reports - - Reports["reports"] >> reports - - var/datum/admin_report/found - for(var/datum/admin_report/N in reports) - if(N.ID == ID) - found = N - if(!found) - to_chat(src, "* An error occurred, sorry.") - - var/body = tgui_input_text(src.mob, "Enter a body for the news", "Body", multiline = TRUE, prevent_enter = TRUE) - if(!body) return - - found.body = body - - Reports["reports"] << reports +// Reports are a way to notify admins of wrongdoings that happened +// while no admin was present. They work a bit similar to news, but +// they can only be read by admins and moderators. + +// a single admin report +/datum//admin_report/var + ID // the ID of the report + body // the content of the report + author // key of the author + date // date on which this was created + done // whether this was handled + + offender_key // store the key of the offender + offender_cid // store the cid of the offender + +/datum//report_topic_handler + Topic(href,href_list) + ..() + var/client/C = locate(href_list["client"]) + if(href_list["action"] == "show_reports") + C.display_admin_reports() + else if(href_list["action"] == "remove") + C.mark_report_done(text2num(href_list["ID"])) + else if(href_list["action"] == "edit") + C.edit_report(text2num(href_list["ID"])) + +var/datum/report_topic_handler/report_topic_handler + +world/New() + ..() + report_topic_handler = new + +// add a new news datums +/proc/make_report(body, author, okey, cid) + var/savefile/Reports = new("data/reports.sav") + var/list/reports + var/lastID + + Reports["reports"] >> reports + Reports["lastID"] >> lastID + + if(!reports) reports = list() + if(!lastID) lastID = 0 + + var/datum/admin_report/created = new() + created.ID = ++lastID + created.body = body + created.author = author + created.date = world.realtime + created.done = 0 + created.offender_key = okey + created.offender_cid = cid + + reports.Insert(1, created) + + Reports["reports"] << reports + Reports["lastID"] << lastID + +// load the reports from disk +/proc/load_reports() + var/savefile/Reports = new("data/reports.sav") + var/list/reports + + Reports["reports"] >> reports + + if(!reports) reports = list() + + return reports + +// check if there are any unhandled reports +/client/proc/unhandled_reports() + if(!src.holder) return 0 + var/list/reports = load_reports() + + for(var/datum/admin_report/N in reports) + if(N.done) + continue + else return 1 + + return 0 + +// checks if the player has an unhandled report against him +/client/proc/is_reported() + var/list/reports = load_reports() + + for(var/datum/admin_report/N in reports) if(!N.done) + if(N.offender_key == src.key) + return 1 + + return 0 + +// display only the reports that haven't been handled +/client/proc/display_admin_reports() + set category = "Admin" + set name = "Display Admin Reports" + if(!src.holder) return + + var/list/reports = load_reports() + + var/output = "" + if(unhandled_reports()) + // load the list of unhandled reports + for(var/datum/admin_report/N in reports) + if(N.done) + continue + output += "Reported player: [N.offender_key](CID: [N.offender_cid])
                    " + output += "Offense:[N.body]
                    " + output += "Occurred at [time2text(N.date,"MM/DD hh:mm:ss")]
                    " + output += "authored by [N.author]
                    " + output += " Flag as Handled" + if(src.key == N.author) + output += " Edit" + output += "
                    " + output += "
                    " + else + output += "Whoops, no reports!" + + usr << browse(output, "window=news;size=600x400") + + +/client/proc/Report(mob/M as mob in world) + set category = "Admin" + if(!src.holder) + return + + var/CID = "Unknown" + if(M.client) + CID = M.client.computer_id + + var/body = tgui_input_text(src.mob, "Describe in detail what you're reporting [M] for", "Report") + if(!body) return + + + make_report(body, key, M.key, CID) + + spawn(1) + display_admin_reports() + +/client/proc/mark_report_done(ID as num) + if(!src.holder || src.holder.level < 0) + return + + var/savefile/Reports = new("data/reports.sav") + var/list/reports + + Reports["reports"] >> reports + + var/datum/admin_report/found + for(var/datum/admin_report/N in reports) + if(N.ID == ID) + found = N + if(!found) + to_chat(src, "* An error occurred, sorry.") + + found.done = 1 + + Reports["reports"] << reports + + +/client/proc/edit_report(ID as num) + if(!src.holder || src.holder.level < 0) + to_chat(src, "You tried to modify the news, but you're not an admin!") + return + + var/savefile/Reports = new("data/reports.sav") + var/list/reports + + Reports["reports"] >> reports + + var/datum/admin_report/found + for(var/datum/admin_report/N in reports) + if(N.ID == ID) + found = N + if(!found) + to_chat(src, "* An error occurred, sorry.") + + var/body = tgui_input_text(src.mob, "Enter a body for the news", "Body", multiline = TRUE, prevent_enter = TRUE) + if(!body) return + + found.body = body + + Reports["reports"] << reports diff --git a/code/modules/admin/admin_secrets.dm b/code/modules/admin/admin_secrets.dm index e8ab89101c4..be0b5b8fb81 100644 --- a/code/modules/admin/admin_secrets.dm +++ b/code/modules/admin/admin_secrets.dm @@ -1,113 +1,113 @@ -var/datum/admin_secrets/admin_secrets = new() - -/datum/admin_secrets - var/list/datum/admin_secret_category/categories - var/list/datum/admin_secret_item/items - -/datum/admin_secrets/New() - ..() - categories = init_subtypes(/datum/admin_secret_category) - items = list() - var/list/category_assoc = list() - for(var/datum/admin_secret_category/category in categories) - category_assoc[category.type] = category - - for(var/item_type in subtypesof(/datum/admin_secret_item)) - var/datum/admin_secret_item/secret_item = item_type - if(!initial(secret_item.name)) - continue - - var/datum/admin_secret_item/item = new item_type() - var/datum/admin_secret_category/category = category_assoc[item.category] - dd_insertObjectList(category.items, item) - items += item - -// -// Secret Item Category - Each subtype is a category for organizing secret commands. -// -/datum/admin_secret_category - var/name = "" - var/desc = "" - var/list/datum/admin_secret_item/items = list() - -/datum/admin_secret_category/proc/can_view(var/mob/user) - for(var/datum/admin_secret_item/item in items) - if(item.can_view(user)) - return 1 - return 0 - -// -// Secret Item Datum - Each subtype is a command on the secrets panel. -// Override execute() with the implementation of the command. -// -/datum/admin_secret_item - var/name = "" - var/category = null - var/log = 1 - var/feedback = 1 - var/permissions = R_HOST - var/warn_before_use = 0 - -/datum/admin_secret_item/dd_SortValue() - return "[name]" - -/datum/admin_secret_item/proc/name() - return name - -/datum/admin_secret_item/proc/can_view(var/mob/user) - return check_rights(permissions, 0, user) - -/datum/admin_secret_item/proc/can_execute(var/mob/user) - if(can_view(user)) - if(!warn_before_use || tgui_alert(usr, "Execute the command '[name]'?", name, list("No","Yes")) == "Yes") - return 1 - return 0 - -/datum/admin_secret_item/proc/execute(var/mob/user) - if(!can_execute(user)) - return 0 - - if(log) - log_and_message_admins("used secret '[name]'", user) - if(feedback) - feedback_inc("admin_secrets_used",1) - feedback_add_details("admin_secrets_used","[name]") - return 1 - -/************************* -* Pre-defined categories * -*************************/ -/datum/admin_secret_category/admin_secrets - name = "Admin Secrets" - -/datum/admin_secret_category/random_events - name = "'Random' Events" - -/datum/admin_secret_category/fun_secrets - name = "Fun Secrets" - -/datum/admin_secret_category/final_solutions - name = "Final Solutions" - desc = "(Warning, these will end the round!)" - -/************************* -* Pre-defined base items * -*************************/ -/datum/admin_secret_item/admin_secret - category = /datum/admin_secret_category/admin_secrets - log = 0 - permissions = R_ADMIN //VOREStation Edit - -/datum/admin_secret_item/random_event - category = /datum/admin_secret_category/random_events - permissions = R_FUN //VOREStation Edit - warn_before_use = 1 - -/datum/admin_secret_item/fun_secret - category = /datum/admin_secret_category/fun_secrets - permissions = R_FUN //VOREStation Edit - warn_before_use = 1 - -/datum/admin_secret_item/final_solution - category = /datum/admin_secret_category/final_solutions - permissions = R_FUN|R_SERVER|R_ADMIN //VOREStation Edit +var/datum/admin_secrets/admin_secrets = new() + +/datum/admin_secrets + var/list/datum/admin_secret_category/categories + var/list/datum/admin_secret_item/items + +/datum/admin_secrets/New() + ..() + categories = init_subtypes(/datum/admin_secret_category) + items = list() + var/list/category_assoc = list() + for(var/datum/admin_secret_category/category in categories) + category_assoc[category.type] = category + + for(var/item_type in subtypesof(/datum/admin_secret_item)) + var/datum/admin_secret_item/secret_item = item_type + if(!initial(secret_item.name)) + continue + + var/datum/admin_secret_item/item = new item_type() + var/datum/admin_secret_category/category = category_assoc[item.category] + dd_insertObjectList(category.items, item) + items += item + +// +// Secret Item Category - Each subtype is a category for organizing secret commands. +// +/datum/admin_secret_category + var/name = "" + var/desc = "" + var/list/datum/admin_secret_item/items = list() + +/datum/admin_secret_category/proc/can_view(var/mob/user) + for(var/datum/admin_secret_item/item in items) + if(item.can_view(user)) + return 1 + return 0 + +// +// Secret Item Datum - Each subtype is a command on the secrets panel. +// Override execute() with the implementation of the command. +// +/datum/admin_secret_item + var/name = "" + var/category = null + var/log = 1 + var/feedback = 1 + var/permissions = R_HOST + var/warn_before_use = 0 + +/datum/admin_secret_item/dd_SortValue() + return "[name]" + +/datum/admin_secret_item/proc/name() + return name + +/datum/admin_secret_item/proc/can_view(var/mob/user) + return check_rights(permissions, 0, user) + +/datum/admin_secret_item/proc/can_execute(var/mob/user) + if(can_view(user)) + if(!warn_before_use || tgui_alert(usr, "Execute the command '[name]'?", name, list("No","Yes")) == "Yes") + return 1 + return 0 + +/datum/admin_secret_item/proc/execute(var/mob/user) + if(!can_execute(user)) + return 0 + + if(log) + log_and_message_admins("used secret '[name]'", user) + if(feedback) + feedback_inc("admin_secrets_used",1) + feedback_add_details("admin_secrets_used","[name]") + return 1 + +/************************* +* Pre-defined categories * +*************************/ +/datum/admin_secret_category/admin_secrets + name = "Admin Secrets" + +/datum/admin_secret_category/random_events + name = "'Random' Events" + +/datum/admin_secret_category/fun_secrets + name = "Fun Secrets" + +/datum/admin_secret_category/final_solutions + name = "Final Solutions" + desc = "(Warning, these will end the round!)" + +/************************* +* Pre-defined base items * +*************************/ +/datum/admin_secret_item/admin_secret + category = /datum/admin_secret_category/admin_secrets + log = 0 + permissions = R_ADMIN //VOREStation Edit + +/datum/admin_secret_item/random_event + category = /datum/admin_secret_category/random_events + permissions = R_FUN //VOREStation Edit + warn_before_use = 1 + +/datum/admin_secret_item/fun_secret + category = /datum/admin_secret_category/fun_secrets + permissions = R_FUN //VOREStation Edit + warn_before_use = 1 + +/datum/admin_secret_item/final_solution + category = /datum/admin_secret_category/final_solutions + permissions = R_FUN|R_SERVER|R_ADMIN //VOREStation Edit diff --git a/code/modules/admin/admin_verb_lists.dm b/code/modules/admin/admin_verb_lists.dm index 9fbabaafd52..a39fc3b1023 100644 --- a/code/modules/admin/admin_verb_lists.dm +++ b/code/modules/admin/admin_verb_lists.dm @@ -119,7 +119,8 @@ var/list/admin_verbs_ban = list( var/list/admin_verbs_sounds = list( /client/proc/play_local_sound, /client/proc/play_sound, - /client/proc/play_server_sound + /client/proc/play_server_sound, + /client/proc/play_web_sound ) var/list/admin_verbs_fun = list( @@ -240,7 +241,8 @@ var/list/admin_verbs_debug = list( /datum/admins/proc/view_feedback, /client/proc/debug_global_variables, /client/proc/ping_webhook, - /client/proc/reload_webhooks + /client/proc/reload_webhooks, + /client/proc/stop_sounds ) var/list/admin_verbs_paranoid_debug = list( @@ -286,6 +288,7 @@ var/list/admin_verbs_hideable = list( /client/proc/play_local_sound, /client/proc/play_sound, /client/proc/play_server_sound, + /client/proc/play_web_sound, /client/proc/object_talk, /datum/admins/proc/cmd_admin_dress, /client/proc/cmd_admin_gib_self, @@ -335,7 +338,8 @@ var/list/admin_verbs_hideable = list( /proc/possess, /proc/release, /datum/admins/proc/set_tcrystals, - /client/proc/debug_global_variables + /client/proc/debug_global_variables, + /client/proc/stop_sounds ) var/list/admin_verbs_mod = list( /client/proc/cmd_admin_pm_context, //right-click adminPM interface, diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index c5c1be9a5df..cc249bb4d8a 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -77,6 +77,7 @@ var/list/admin_verbs_admin = list( /client/proc/check_ai_laws, //shows AI and borg laws, /client/proc/rename_silicon, //properly renames silicons, /client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. , + /client/proc/modify_robot, /client/proc/check_antagonists, /client/proc/admin_memo, //admin memo system. show/delete/write. +SERVER needed to delete admin memos of others, /client/proc/dsay, //talk in deadchat using our ckey/fakekey, @@ -141,6 +142,7 @@ var/list/admin_verbs_sounds = list( /client/proc/play_local_sound, /client/proc/play_sound, /client/proc/play_server_sound, + /client/proc/play_web_sound, /client/proc/play_z_sound ) @@ -171,7 +173,8 @@ var/list/admin_verbs_fun = list( /client/proc/narrate_mob, //VOREStation Add /client/proc/narrate_mob_args, //VOREStation Add /client/proc/getPlayerStatus, //VORESTation Add - /client/proc/manage_event_triggers + /client/proc/manage_event_triggers, + /client/proc/fake_pdaconvos ) @@ -276,7 +279,8 @@ var/list/admin_verbs_debug = list( /datum/admins/proc/change_time, /client/proc/admin_give_modifier, /client/proc/simple_DPS, - /datum/admins/proc/view_feedback + /datum/admins/proc/view_feedback, + /client/proc/stop_sounds ) var/list/admin_verbs_paranoid_debug = list( @@ -323,6 +327,7 @@ var/list/admin_verbs_hideable = list( /client/proc/play_local_sound, /client/proc/play_sound, /client/proc/play_server_sound, + /client/proc/play_web_sound, /client/proc/object_talk, /datum/admins/proc/cmd_admin_dress, /client/proc/cmd_admin_gib_self, @@ -372,7 +377,8 @@ var/list/admin_verbs_hideable = list( /proc/possess, /proc/release, /datum/admins/proc/set_uplink, //VOREStation Add, - /datum/admins/proc/set_tcrystals + /datum/admins/proc/set_tcrystals, + /client/proc/stop_sounds ) var/list/admin_verbs_mod = list( /client/proc/cmd_admin_pm_context, //right-click adminPM interface, @@ -511,6 +517,7 @@ var/list/admin_verbs_event_manager = list( /client/proc/check_ai_laws, //shows AI and borg laws, /client/proc/rename_silicon, //properly renames silicons, /client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. , + /client/proc/modify_robot, /client/proc/check_antagonists, /client/proc/admin_memo, //admin memo system. show/delete/write. +SERVER needed to delete admin memos of others, /client/proc/dsay, //talk in deadchat using our ckey/fakekey, diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 81a3e795760..295be20e517 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1,561 +1,561 @@ -/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs - set name = "Adminverbs - Hide Most" - set category = "Admin" - - verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable) - verbs += /client/proc/show_verbs - - to_chat(src, "Most of your adminverbs have been hidden.") - feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/hide_verbs() - set name = "Adminverbs - Hide All" - set category = "Admin" - - remove_admin_verbs() - verbs += /client/proc/show_verbs - - to_chat(src, "Almost all of your adminverbs have been hidden.") - feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/show_verbs() - set name = "Adminverbs - Show" - set category = "Admin" - - verbs -= /client/proc/show_verbs - add_admin_verbs() - - to_chat(src, "All of your adminverbs are now visible.") - feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/admin_ghost() - set category = "Admin" - set name = "Aghost" - if(!holder) return - - var/build_mode - if(src.buildmode) - build_mode = tgui_alert(src, "You appear to be currently in buildmode. Do you want to re-enter buildmode after aghosting?", "Buildmode", list("Yes", "No")) - if(build_mode != "Yes") - to_chat(src, "Will not re-enter buildmode after switch.") - - if(istype(mob,/mob/observer/dead)) - //re-enter - var/mob/observer/dead/ghost = mob - if(ghost.can_reenter_corpse) - if(build_mode) - togglebuildmode(mob) - ghost.reenter_corpse() - if(build_mode == "Yes") - togglebuildmode(mob) - else - ghost.reenter_corpse() - else - to_chat(ghost, "Error: Aghost: Can't reenter corpse.") - return - - feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else if(istype(mob,/mob/new_player)) - to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.") - else - //ghostize - var/mob/body = mob - var/mob/observer/dead/ghost - if(build_mode) - togglebuildmode(body) - ghost = body.ghostize(1) - ghost.admin_ghosted = 1 - if(build_mode == "Yes") - togglebuildmode(ghost) - else - ghost = body.ghostize(1) - ghost.admin_ghosted = 1 - if(body) - body.teleop = ghost - if(!body.key) - body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus - feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/invisimin() - set name = "Invisimin" - set category = "Admin" - set desc = "Toggles ghost-like invisibility (Don't abuse this)" - if(holder && mob) - if(mob.invisibility == INVISIBILITY_OBSERVER) - mob.invisibility = initial(mob.invisibility) - to_chat(mob, "Invisimin off. Invisibility reset.") - mob.alpha = max(mob.alpha + 100, 255) - else - mob.invisibility = INVISIBILITY_OBSERVER - to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") - mob.alpha = max(mob.alpha - 100, 0) - - -/client/proc/player_panel() - set name = "Player Panel" - set category = "Admin" - if(holder) - holder.player_panel_old() - feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/player_panel_new() - set name = "Player Panel New" - set category = "Admin" - if(holder) - holder.player_panel_new() - feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/check_antagonists() - set name = "Check Antagonists" - set category = "Admin" - if(holder) - holder.check_antagonists() - log_admin("[key_name(usr)] checked antagonists.") //for tsar~ - feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/jobbans() - set name = "Display Job bans" - set category = "Admin" - if(holder) - if(config.ban_legacy_system) - holder.Jobbans() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/unban_panel() - set name = "Unban Panel" - set category = "Admin" - if(holder) - if(config.ban_legacy_system) - holder.unbanpanel() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/game_panel() - set name = "Game Panel" - set category = "Admin" - if(holder) - holder.Game() - feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/secrets() - set name = "Secrets" - set category = "Admin" - if (holder) - holder.Secrets() - feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/colorooc() - set category = "Fun" - set name = "OOC Text Color" - if(!holder) return - var/response = tgui_alert(src, "Please choose a distinct color that is easy to read and doesn't mix with all the other chat and radio frequency colors.", "Change own OOC color", list("Pick new color", "Reset to default", "Cancel")) - if(response == "Pick new color") - prefs.ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color - else if(response == "Reset to default") - prefs.ooccolor = initial(prefs.ooccolor) - SScharacter_setup.queue_preferences_save(prefs) - - feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/findStealthKey(txt) - if(txt) - for(var/P in GLOB.stealthminID) - if(GLOB.stealthminID[P] == txt) - return P - txt = GLOB.stealthminID[ckey] - return txt - -/client/proc/createStealthKey() - var/num = (rand(0,1000)) - var/i = 0 - while(i == 0) - i = 1 - for(var/P in GLOB.stealthminID) - if(num == GLOB.stealthminID[P]) - num++ - i = 0 - GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" - -/client/proc/stealth() - set category = "Admin" - set name = "Stealth Mode" - if(holder) - if(holder.fakekey) - holder.fakekey = null - if(istype(src.mob, /mob/new_player)) - mob.name = capitalize(ckey) - else - var/new_key = ckeyEx(tgui_input_text(usr, "Enter your desired display name.", "Fake Key", key)) - if(!new_key) - return - if(length(new_key) >= 26) - new_key = copytext(new_key, 1, 26) - holder.fakekey = new_key - createStealthKey() - if(istype(mob, /mob/new_player)) - mob.name = new_key - log_and_message_admins("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") - feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -#define MAX_WARNS 3 -#define AUTOBANTIME 10 - -/client/proc/warn(warned_ckey) - if(!check_rights(R_ADMIN)) return - - if(!warned_ckey || !istext(warned_ckey)) return - if(warned_ckey in admin_datums) - to_chat(usr, "Error: warn(): You can't warn admins.") - return - - var/datum/preferences/D - var/client/C = GLOB.directory[warned_ckey] - if(C) D = C.prefs - else D = preferences_datums[warned_ckey] - - if(!D) - to_chat(src, "Error: warn(): No such ckey found.") - return - - if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) - ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") - if(C) - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.") - to_chat(C, "You have been autobanned due to a warning by [ckey].
                    This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.
                    ") - del(C) - else - message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.") - AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME) - feedback_inc("ban_warn",1) - else - if(C) - to_chat(C, "You have been formally warned by an administrator.
                    Further warnings will result in an autoban.
                    ") - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") - else - message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") - - feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -#undef MAX_WARNS -#undef AUTOBANTIME - -/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE - set category = "Special Verbs" - set name = "Drop Bomb" - set desc = "Cause an explosion of varying strength at your location." - - var/turf/epicenter = mob.loc - var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb", "Cancel") - var/choice = tgui_input_list(usr, "What size explosion would you like to produce?", "Explosion Choice", choices) - switch(choice) - if(null) - return 0 - if("Cancel") - return 0 - if("Small Bomb") - explosion(epicenter, 1, 2, 3, 3) - if("Medium Bomb") - explosion(epicenter, 2, 3, 4, 4) - if("Big Bomb") - explosion(epicenter, 3, 5, 7, 5) - if("Custom Bomb") - var/devastation_range = tgui_input_number(usr, "Devastation range (in tiles):") - var/heavy_impact_range = tgui_input_number(usr, "Heavy impact range (in tiles):") - var/light_impact_range = tgui_input_number(usr, "Light impact range (in tiles):") - var/flash_range = tgui_input_number(usr, "Flash range (in tiles):") - explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range) - message_admins("[ckey] creating an admin explosion at [epicenter.loc].") - feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/give_disease2(mob/T as mob in mob_list) // -- Giacom - set category = "Fun" - set name = "Give Disease" - set desc = "Gives a Disease to a mob." - - var/datum/disease2/disease/D = new /datum/disease2/disease() - - var/severity = 1 - var/greater = tgui_input_list(usr, "Is this a lesser, greater, or badmin disease?", "Give Disease", list("Lesser", "Greater", "Badmin")) - switch(greater) - if ("Lesser") severity = 1 - if ("Greater") severity = 2 - if ("Badmin") severity = 99 - - D.makerandom(severity) - D.infectionchance = tgui_input_number(usr, "How virulent is this disease? (1-100)", "Give Disease", D.infectionchance) - - if(istype(T,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = T - if (H.species) - D.affected_species = list(H.species.get_bodytype()) - if(H.species.primitive_form) - D.affected_species |= H.species.primitive_form - if(H.species.greater_form) - D.affected_species |= H.species.greater_form - infect_virus2(T,D,1) - - feedback_add_details("admin_verb","GD2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance].", 1) - -/client/proc/admin_give_modifier(var/mob/living/L) - set category = "Debug" - set name = "Give Modifier" - set desc = "Makes a mob weaker or stronger by adding a specific modifier to them." - set popup_menu = FALSE //VOREStation Edit - Declutter. - - if(!L) - to_chat(usr, "Looks like you didn't select a mob.") - return - - var/list/possible_modifiers = subtypesof(/datum/modifier) - - var/new_modifier_type = tgui_input_list(usr, "What modifier should we add to [L]?", "Modifier Type", possible_modifiers) - if(!new_modifier_type) - return - var/duration = tgui_input_number(usr, "How long should the new modifier last, in seconds. To make it last forever, write '0'.", "Modifier Duration") - if(duration == 0) - duration = null - else - duration = duration SECONDS - - L.add_modifier(new_modifier_type, duration) - log_and_message_admins("has given [key_name(L)] the modifer [new_modifier_type], with a duration of [duration ? "[duration / 600] minutes" : "forever"].") - -/client/proc/make_sound(var/obj/O in world) // -- TLE - set category = "Special Verbs" - set name = "Make Sound" - set desc = "Display a message to everyone who can hear the target" - if(O) - var/message = sanitize(tgui_input_text(usr, "What do you want the message to be?", "Make Sound")) - if(!message) - return - O.audible_message(message) - log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound") - message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound.", 1) - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/togglebuildmodeself() - set name = "Toggle Build Mode Self" - set category = "Special Verbs" - if(src.mob) - togglebuildmode(src.mob) - feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/object_talk(var/msg as text) // -- TLE - set category = "Special Verbs" - set name = "oSay" - set desc = "Display a message to everyone who can hear the target" - if(mob.control_object) - if(!msg) - return - for (var/mob/V in hearers(mob.control_object)) - V.show_message("[mob.control_object.name] says: \"[msg]\"", 2) - feedback_add_details("admin_verb","OT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/kill_air() // -- TLE - set category = "Debug" - set name = "Kill Air" - set desc = "Toggle Air Processing" - SSair.can_fire = !SSair.can_fire - to_chat(usr, "[SSair.can_fire ? "En" : "Dis"]abled air processing.") - feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] used 'kill air'.") - message_admins("[key_name_admin(usr)] used 'kill air'.", 1) - -/client/proc/readmin_self() - set name = "Re-Admin self" - set category = "Admin" - - if(deadmin_holder) - deadmin_holder.reassociate() - log_admin("[src] re-admined themself.") - message_admins("[src] re-admined themself.", 1) - to_chat(src, "You now have the keys to control the planet, or at least a small space station") - verbs -= /client/proc/readmin_self - -/client/proc/deadmin_self() - set name = "De-admin self" - set category = "Admin" - - if(holder) - if(tgui_alert(usr, "Confirm self-deadmin for the round? You can't re-admin yourself without someone promoting you.","Deadmin",list("Yes","No")) == "Yes") - log_admin("[src] deadmined themself.") - message_admins("[src] deadmined themself.", 1) - deadmin() - to_chat(src, "You are now a normal player.") - verbs |= /client/proc/readmin_self - feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_log_hrefs() - set name = "Toggle href logging" - set category = "Server" - if(!holder) return - if(config) - config.log_hrefs = !config.log_hrefs - message_admins("[key_name_admin(usr)] [config.log_hrefs ? "started" : "stopped"] logging hrefs") - -/client/proc/check_ai_laws() - set name = "Check AI Laws" - set category = "Admin" - if(holder) - src.holder.output_ai_laws() - -/client/proc/rename_silicon() - set name = "Rename Silicon" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Rename Silicon.", silicon_mob_list) - if(!S) return - - var/new_name = sanitizeSafe(tgui_input_text(src, "Enter new name. Leave blank or as is to cancel.", "[S.real_name] - Enter new silicon name", S.real_name)) - if(new_name && new_name != S.real_name) - log_and_message_admins("has renamed the silicon '[S.real_name]' to '[new_name]'") - S.SetName(new_name) - feedback_add_details("admin_verb","RAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/manage_silicon_laws() - set name = "Manage Silicon Laws" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Manage Silicon Laws", silicon_mob_list) - if(!S) return - - var/datum/tgui_module/law_manager/admin/L = new(S) - L.tgui_interact(usr) - log_and_message_admins("has opened [S]'s law manager.") - feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/change_security_level() - set name = "Set security level" - set desc = "Sets the station security level" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_EVENT)) return - var/sec_level = tgui_input_list(usr, "It's currently code [get_security_level()].", "Select Security Level", (list("green","yellow","violet","orange","blue","red","delta")-get_security_level())) - if(!sec_level) - return - if(tgui_alert(usr, "Switch from code [get_security_level()] to code [sec_level]?","Change security level?",list("Yes","No")) == "Yes") - set_security_level(sec_level) - log_admin("[key_name(usr)] changed the security level to code [sec_level].") - -/client/proc/shuttle_panel() - set name = "Shuttle Control Panel" - set category = "Admin" - - if(!check_rights(R_ADMIN | R_EVENT)) - return - - var/datum/tgui_module/admin_shuttle_controller/A = new(src) - A.tgui_interact(usr) - log_and_message_admins("has opened the shuttle panel.") - feedback_add_details("admin_verb","SHCP") - -//---- bs12 verbs ---- - -/client/proc/mod_panel() - set name = "Moderator Panel" - set category = "Admin" -/* if(holder) - holder.mod_panel()*/ -// feedback_add_details("admin_verb","MP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/playernotes() - set name = "Show Player Info" - set category = "Admin" - if(holder) - holder.PlayerNotes() - return - -/client/proc/free_slot() - set name = "Free Job Slot" - set category = "Admin" - if(holder) - var/list/jobs = list() - for (var/datum/job/J in job_master.occupations) - if (J.current_positions >= J.total_positions && J.total_positions != -1) - jobs += J.title - if (!jobs.len) - to_chat(usr, "There are no fully staffed jobs.") - return - var/job = tgui_input_list(usr, "Please select job slot to free", "Free job slot", jobs) - if (job) - job_master.FreeRole(job) - message_admins("A job slot for [job] has been opened by [key_name_admin(usr)]") - return - -/client/proc/toggleghostwriters() - set name = "Toggle ghost writers" - set category = "Server" - if(!holder) return - if(config) - config.cult_ghostwriter = !config.cult_ghostwriter - message_admins("Admin [key_name_admin(usr)] has [config.cult_ghostwriter ? "en" : "dis"]abled ghost writers.", 1) - -/client/proc/toggledrones() - set name = "Toggle maintenance drones" - set category = "Server" - if(!holder) return - if(config) - config.allow_drone_spawn = !config.allow_drone_spawn - message_admins("Admin [key_name_admin(usr)] has [config.allow_drone_spawn ? "en" : "dis"]abled maintenance drones.", 1) - -/client/proc/man_up(mob/T as mob in mob_list) - set category = "Fun" - set name = "Man Up" - set desc = "Tells mob to man up and deal with it." - set popup_menu = FALSE //VOREStation Edit - Declutter. - - if(tgui_alert(usr, "Are you sure you want to tell them to man up?","Confirmation",list("Deal with it","No"))=="No") return - - to_chat(T, "Man up and deal with it.") - to_chat(T, "Move along.") - - log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") - message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.", 1) - -/client/proc/global_man_up() - set category = "Fun" - set name = "Man Up Global" - set desc = "Tells everyone to man up and deal with it." - - if(tgui_alert(usr, "Are you sure you want to tell the whole server up?","Confirmation",list("Deal with it","No"))=="No") return - - for (var/mob/T as mob in mob_list) - to_chat(T, "
                    Man up.
                    Deal with it.

                    Move along.

                    ") - T << 'sound/voice/ManUp1.ogg' - - log_admin("[key_name(usr)] told everyone to man up and deal with it.") - message_admins("[key_name_admin(usr)] told everyone to man up and deal with it.", 1) - -/client/proc/give_spell(mob/T as mob in mob_list) // -- Urist - set category = "Fun" - set name = "Give Spell" - set desc = "Gives a spell to a mob." - var/spell/S = tgui_input_list(usr, "Choose the spell to give to that guy", "ABRAKADABRA", spells) - if(!S) return - T.spell_list += new S - feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].", 1) \ No newline at end of file +/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs + set name = "Adminverbs - Hide Most" + set category = "Admin" + + verbs.Remove(/client/proc/hide_most_verbs, admin_verbs_hideable) + verbs += /client/proc/show_verbs + + to_chat(src, "Most of your adminverbs have been hidden.") + feedback_add_details("admin_verb","HMV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/hide_verbs() + set name = "Adminverbs - Hide All" + set category = "Admin" + + remove_admin_verbs() + verbs += /client/proc/show_verbs + + to_chat(src, "Almost all of your adminverbs have been hidden.") + feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/show_verbs() + set name = "Adminverbs - Show" + set category = "Admin" + + verbs -= /client/proc/show_verbs + add_admin_verbs() + + to_chat(src, "All of your adminverbs are now visible.") + feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/admin_ghost() + set category = "Admin" + set name = "Aghost" + if(!holder) return + + var/build_mode + if(src.buildmode) + build_mode = tgui_alert(src, "You appear to be currently in buildmode. Do you want to re-enter buildmode after aghosting?", "Buildmode", list("Yes", "No")) + if(build_mode != "Yes") + to_chat(src, "Will not re-enter buildmode after switch.") + + if(istype(mob,/mob/observer/dead)) + //re-enter + var/mob/observer/dead/ghost = mob + if(ghost.can_reenter_corpse) + if(build_mode) + togglebuildmode(mob) + ghost.reenter_corpse() + if(build_mode == "Yes") + togglebuildmode(mob) + else + ghost.reenter_corpse() + else + to_chat(ghost, "Error: Aghost: Can't reenter corpse.") + return + + feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else if(istype(mob,/mob/new_player)) + to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.") + else + //ghostize + var/mob/body = mob + var/mob/observer/dead/ghost + if(build_mode) + togglebuildmode(body) + ghost = body.ghostize(1) + ghost.admin_ghosted = 1 + if(build_mode == "Yes") + togglebuildmode(ghost) + else + ghost = body.ghostize(1) + ghost.admin_ghosted = 1 + if(body) + body.teleop = ghost + if(!body.key) + body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus + feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/invisimin() + set name = "Invisimin" + set category = "Admin" + set desc = "Toggles ghost-like invisibility (Don't abuse this)" + if(holder && mob) + if(mob.invisibility == INVISIBILITY_OBSERVER) + mob.invisibility = initial(mob.invisibility) + to_chat(mob, "Invisimin off. Invisibility reset.") + mob.alpha = max(mob.alpha + 100, 255) + else + mob.invisibility = INVISIBILITY_OBSERVER + to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") + mob.alpha = max(mob.alpha - 100, 0) + + +/client/proc/player_panel() + set name = "Player Panel" + set category = "Admin" + if(holder) + holder.player_panel_old() + feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/player_panel_new() + set name = "Player Panel New" + set category = "Admin" + if(holder) + holder.player_panel_new() + feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/check_antagonists() + set name = "Check Antagonists" + set category = "Admin" + if(holder) + holder.check_antagonists() + log_admin("[key_name(usr)] checked antagonists.") //for tsar~ + feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/jobbans() + set name = "Display Job bans" + set category = "Admin" + if(holder) + if(config.ban_legacy_system) + holder.Jobbans() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/unban_panel() + set name = "Unban Panel" + set category = "Admin" + if(holder) + if(config.ban_legacy_system) + holder.unbanpanel() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/game_panel() + set name = "Game Panel" + set category = "Admin" + if(holder) + holder.Game() + feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/secrets() + set name = "Secrets" + set category = "Admin" + if (holder) + holder.Secrets() + feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/colorooc() + set category = "Fun" + set name = "OOC Text Color" + if(!holder) return + var/response = tgui_alert(src, "Please choose a distinct color that is easy to read and doesn't mix with all the other chat and radio frequency colors.", "Change own OOC color", list("Pick new color", "Reset to default", "Cancel")) + if(response == "Pick new color") + prefs.ooccolor = input(src, "Please select your OOC colour.", "OOC colour") as color + else if(response == "Reset to default") + prefs.ooccolor = initial(prefs.ooccolor) + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/findStealthKey(txt) + if(txt) + for(var/P in GLOB.stealthminID) + if(GLOB.stealthminID[P] == txt) + return P + txt = GLOB.stealthminID[ckey] + return txt + +/client/proc/createStealthKey() + var/num = (rand(0,1000)) + var/i = 0 + while(i == 0) + i = 1 + for(var/P in GLOB.stealthminID) + if(num == GLOB.stealthminID[P]) + num++ + i = 0 + GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" + +/client/proc/stealth() + set category = "Admin" + set name = "Stealth Mode" + if(holder) + if(holder.fakekey) + holder.fakekey = null + if(istype(src.mob, /mob/new_player)) + mob.name = capitalize(ckey) + else + var/new_key = ckeyEx(tgui_input_text(usr, "Enter your desired display name.", "Fake Key", key)) + if(!new_key) + return + if(length(new_key) >= 26) + new_key = copytext(new_key, 1, 26) + holder.fakekey = new_key + createStealthKey() + if(istype(mob, /mob/new_player)) + mob.name = new_key + log_and_message_admins("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") + feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +#define MAX_WARNS 3 +#define AUTOBANTIME 10 + +/client/proc/warn(warned_ckey) + if(!check_rights(R_ADMIN)) return + + if(!warned_ckey || !istext(warned_ckey)) return + if(warned_ckey in admin_datums) + to_chat(usr, "Error: warn(): You can't warn admins.") + return + + var/datum/preferences/D + var/client/C = GLOB.directory[warned_ckey] + if(C) D = C.prefs + else D = preferences_datums[warned_ckey] + + if(!D) + to_chat(src, "Error: warn(): No such ckey found.") + return + + if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) + ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") + if(C) + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.") + to_chat(C, "You have been autobanned due to a warning by [ckey].
                    This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.
                    ") + del(C) + else + message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.") + AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME) + feedback_inc("ban_warn",1) + else + if(C) + to_chat(C, "You have been formally warned by an administrator.
                    Further warnings will result in an autoban.
                    ") + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") + else + message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") + + feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +#undef MAX_WARNS +#undef AUTOBANTIME + +/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE + set category = "Special Verbs" + set name = "Drop Bomb" + set desc = "Cause an explosion of varying strength at your location." + + var/turf/epicenter = mob.loc + var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb", "Cancel") + var/choice = tgui_input_list(usr, "What size explosion would you like to produce?", "Explosion Choice", choices) + switch(choice) + if(null) + return 0 + if("Cancel") + return 0 + if("Small Bomb") + explosion(epicenter, 1, 2, 3, 3) + if("Medium Bomb") + explosion(epicenter, 2, 3, 4, 4) + if("Big Bomb") + explosion(epicenter, 3, 5, 7, 5) + if("Custom Bomb") + var/devastation_range = tgui_input_number(usr, "Devastation range (in tiles):") + var/heavy_impact_range = tgui_input_number(usr, "Heavy impact range (in tiles):") + var/light_impact_range = tgui_input_number(usr, "Light impact range (in tiles):") + var/flash_range = tgui_input_number(usr, "Flash range (in tiles):") + explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range) + message_admins(span_blue("[ckey] creating an admin explosion at [epicenter.loc].")) + feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/give_disease2(mob/T as mob in mob_list) // -- Giacom + set category = "Fun" + set name = "Give Disease" + set desc = "Gives a Disease to a mob." + + var/datum/disease2/disease/D = new /datum/disease2/disease() + + var/severity = 1 + var/greater = tgui_input_list(usr, "Is this a lesser, greater, or badmin disease?", "Give Disease", list("Lesser", "Greater", "Badmin")) + switch(greater) + if ("Lesser") severity = 1 + if ("Greater") severity = 2 + if ("Badmin") severity = 99 + + D.makerandom(severity) + D.infectionchance = tgui_input_number(usr, "How virulent is this disease? (1-100)", "Give Disease", D.infectionchance, 100, 1) + + if(istype(T,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = T + if (H.species) + D.affected_species = list(H.species.get_bodytype()) + if(H.species.primitive_form) + D.affected_species |= H.species.primitive_form + if(H.species.greater_form) + D.affected_species |= H.species.greater_form + infect_virus2(T,D,1) + + feedback_add_details("admin_verb","GD2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance].") + message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] a [greater] disease2 with infection chance [D.infectionchance]."), 1) + +/client/proc/admin_give_modifier(var/mob/living/L) + set category = "Debug" + set name = "Give Modifier" + set desc = "Makes a mob weaker or stronger by adding a specific modifier to them." + set popup_menu = FALSE //VOREStation Edit - Declutter. + + if(!L) + to_chat(usr, "Looks like you didn't select a mob.") + return + + var/list/possible_modifiers = subtypesof(/datum/modifier) + + var/new_modifier_type = tgui_input_list(usr, "What modifier should we add to [L]?", "Modifier Type", possible_modifiers) + if(!new_modifier_type) + return + var/duration = tgui_input_number(usr, "How long should the new modifier last, in seconds. To make it last forever, write '0'.", "Modifier Duration") + if(duration == 0) + duration = null + else + duration = duration SECONDS + + L.add_modifier(new_modifier_type, duration) + log_and_message_admins("has given [key_name(L)] the modifer [new_modifier_type], with a duration of [duration ? "[duration / 600] minutes" : "forever"].") + +/client/proc/make_sound(var/obj/O in world) // -- TLE + set category = "Special Verbs" + set name = "Make Sound" + set desc = "Display a message to everyone who can hear the target" + if(O) + var/message = sanitize(tgui_input_text(usr, "What do you want the message to be?", "Make Sound")) + if(!message) + return + O.audible_message(message) + log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound") + message_admins(span_blue("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound."), 1) + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/togglebuildmodeself() + set name = "Toggle Build Mode Self" + set category = "Special Verbs" + if(src.mob) + togglebuildmode(src.mob) + feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/object_talk(var/msg as text) // -- TLE + set category = "Special Verbs" + set name = "oSay" + set desc = "Display a message to everyone who can hear the target" + if(mob.control_object) + if(!msg) + return + for (var/mob/V in hearers(mob.control_object)) + V.show_message("[mob.control_object.name] says: \"[msg]\"", 2) + feedback_add_details("admin_verb","OT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/kill_air() // -- TLE + set category = "Debug" + set name = "Kill Air" + set desc = "Toggle Air Processing" + SSair.can_fire = !SSair.can_fire + to_chat(usr, "[SSair.can_fire ? "En" : "Dis"]abled air processing.") + feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] used 'kill air'.") + message_admins(span_blue("[key_name_admin(usr)] used 'kill air'."), 1) + +/client/proc/readmin_self() + set name = "Re-Admin self" + set category = "Admin" + + if(deadmin_holder) + deadmin_holder.reassociate() + log_admin("[src] re-admined themself.") + message_admins("[src] re-admined themself.", 1) + to_chat(src, "You now have the keys to control the planet, or at least a small space station") + verbs -= /client/proc/readmin_self + +/client/proc/deadmin_self() + set name = "De-admin self" + set category = "Admin" + + if(holder) + if(tgui_alert(usr, "Confirm self-deadmin for the round? You can't re-admin yourself without someone promoting you.","Deadmin",list("Yes","No")) == "Yes") + log_admin("[src] deadmined themself.") + message_admins("[src] deadmined themself.", 1) + deadmin() + to_chat(src, "You are now a normal player.") + verbs |= /client/proc/readmin_self + feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_log_hrefs() + set name = "Toggle href logging" + set category = "Server" + if(!holder) return + if(config) + config.log_hrefs = !config.log_hrefs + message_admins("[key_name_admin(usr)] [config.log_hrefs ? "started" : "stopped"] logging hrefs") + +/client/proc/check_ai_laws() + set name = "Check AI Laws" + set category = "Admin" + if(holder) + src.holder.output_ai_laws() + +/client/proc/rename_silicon() + set name = "Rename Silicon" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Rename Silicon.", silicon_mob_list) + if(!S) return + + var/new_name = sanitizeSafe(tgui_input_text(src, "Enter new name. Leave blank or as is to cancel.", "[S.real_name] - Enter new silicon name", S.real_name)) + if(new_name && new_name != S.real_name) + log_and_message_admins("has renamed the silicon '[S.real_name]' to '[new_name]'") + S.SetName(new_name) + feedback_add_details("admin_verb","RAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/manage_silicon_laws() + set name = "Manage Silicon Laws" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/silicon/S = tgui_input_list(usr, "Select silicon.", "Manage Silicon Laws", silicon_mob_list) + if(!S) return + + var/datum/tgui_module/law_manager/admin/L = new(S) + L.tgui_interact(usr) + log_and_message_admins("has opened [S]'s law manager.") + feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/change_security_level() + set name = "Set security level" + set desc = "Sets the station security level" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_EVENT)) return + var/sec_level = tgui_input_list(usr, "It's currently code [get_security_level()].", "Select Security Level", (list("green","yellow","violet","orange","blue","red","delta")-get_security_level())) + if(!sec_level) + return + if(tgui_alert(usr, "Switch from code [get_security_level()] to code [sec_level]?","Change security level?",list("Yes","No")) == "Yes") + set_security_level(sec_level) + log_admin("[key_name(usr)] changed the security level to code [sec_level].") + +/client/proc/shuttle_panel() + set name = "Shuttle Control Panel" + set category = "Admin" + + if(!check_rights(R_ADMIN | R_EVENT)) + return + + var/datum/tgui_module/admin_shuttle_controller/A = new(src) + A.tgui_interact(usr) + log_and_message_admins("has opened the shuttle panel.") + feedback_add_details("admin_verb","SHCP") + +//---- bs12 verbs ---- + +/client/proc/mod_panel() + set name = "Moderator Panel" + set category = "Admin" +/* if(holder) + holder.mod_panel()*/ +// feedback_add_details("admin_verb","MP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/playernotes() + set name = "Show Player Info" + set category = "Admin" + if(holder) + holder.PlayerNotes() + return + +/client/proc/free_slot() + set name = "Free Job Slot" + set category = "Admin" + if(holder) + var/list/jobs = list() + for (var/datum/job/J in job_master.occupations) + if (J.current_positions >= J.total_positions && J.total_positions != -1) + jobs += J.title + if (!jobs.len) + to_chat(usr, "There are no fully staffed jobs.") + return + var/job = tgui_input_list(usr, "Please select job slot to free", "Free job slot", jobs) + if (job) + job_master.FreeRole(job) + message_admins("A job slot for [job] has been opened by [key_name_admin(usr)]") + return + +/client/proc/toggleghostwriters() + set name = "Toggle ghost writers" + set category = "Server" + if(!holder) return + if(config) + config.cult_ghostwriter = !config.cult_ghostwriter + message_admins("Admin [key_name_admin(usr)] has [config.cult_ghostwriter ? "en" : "dis"]abled ghost writers.", 1) + +/client/proc/toggledrones() + set name = "Toggle maintenance drones" + set category = "Server" + if(!holder) return + if(config) + config.allow_drone_spawn = !config.allow_drone_spawn + message_admins("Admin [key_name_admin(usr)] has [config.allow_drone_spawn ? "en" : "dis"]abled maintenance drones.", 1) + +/client/proc/man_up(mob/T as mob in mob_list) + set category = "Fun" + set name = "Man Up" + set desc = "Tells mob to man up and deal with it." + set popup_menu = FALSE //VOREStation Edit - Declutter. + + if(tgui_alert(usr, "Are you sure you want to tell them to man up?","Confirmation",list("Deal with it","No"))=="No") return + + to_chat(T, "Man up and deal with it.") + to_chat(T, "Move along.") + + log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") + message_admins(span_blue("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it."), 1) + +/client/proc/global_man_up() + set category = "Fun" + set name = "Man Up Global" + set desc = "Tells everyone to man up and deal with it." + + if(tgui_alert(usr, "Are you sure you want to tell the whole server up?","Confirmation",list("Deal with it","No"))=="No") return + + for (var/mob/T as mob in mob_list) + to_chat(T, "
                    Man up.
                    Deal with it.

                    Move along.

                    ") + T << 'sound/voice/ManUp1.ogg' + + log_admin("[key_name(usr)] told everyone to man up and deal with it.") + message_admins(span_blue("[key_name_admin(usr)] told everyone to man up and deal with it."), 1) + +/client/proc/give_spell(mob/T as mob in mob_list) // -- Urist + set category = "Fun" + set name = "Give Spell" + set desc = "Gives a spell to a mob." + var/spell/S = tgui_input_list(usr, "Choose the spell to give to that guy", "ABRAKADABRA", spells) + if(!S) return + T.spell_list += new S + feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") + message_admins(span_blue("[key_name_admin(usr)] gave [key_name(T)] the spell [S]."), 1) diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 0bfa4e516a2..15d169fb261 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -1,118 +1,118 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -var/jobban_runonce // Updates legacy bans with new info -var/jobban_keylist[0] //to store the keys & ranks - -/proc/jobban_fullban(mob/M, rank, reason) - if (!M || !M.key) return - jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) - jobban_savebanfile() - -/proc/jobban_client_fullban(ckey, rank) - if (!ckey || !rank) return - jobban_keylist.Add(text("[ckey] - [rank]")) - jobban_savebanfile() - -//returns a reason if M is banned from rank, returns 0 otherwise -/proc/jobban_isbanned(mob/M, rank) - if(M && rank) - /* - if(_jobban_isbanned(M, rank)) return "Reason Unspecified" //for old jobban - */ - - if (guest_jobbans(rank)) - if(config.guest_jobban && IsGuestKey(M.key)) - return "Guest Job-ban" - if(config.usewhitelist && !check_whitelist(M)) - return "Whitelisted Job" - - return ckey_is_jobbanned(M.ckey, rank) - return 0 - -/proc/ckey_is_jobbanned(var/check_key, var/rank) - for(var/s in jobban_keylist) - if(findtext(s,"[check_key] - [rank]") == 1 ) - var/startpos = findtext(s, "## ")+3 - if(startpos && startpos> jobban_keylist - log_admin("Loading jobban_rank") - S["runonce"] >> jobban_runonce - - if (!length(jobban_keylist)) - jobban_keylist=list() - log_admin("jobban_keylist was empty") - else - if(!establish_db_connection()) - error("Database connection failed. Reverting to the legacy ban system.") - log_misc("Database connection failed. Reverting to the legacy ban system.") - config.ban_legacy_system = 1 - jobban_loadbanfile() - return - - //Job permabans - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") - query.Execute() - - while(query.NextRow()) - var/ckey = query.item[1] - var/job = query.item[2] - - jobban_keylist.Add("[ckey] - [job]") - - //Job tempbans - var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") - query1.Execute() - - while(query1.NextRow()) - var/ckey = query1.item[1] - var/job = query1.item[2] - - jobban_keylist.Add("[ckey] - [job]") - -/proc/jobban_savebanfile() - var/savefile/S=new("data/job_full.ban") - S["keys[0]"] << jobban_keylist - -/proc/jobban_unban(mob/M, rank) - jobban_remove("[M.ckey] - [rank]") - jobban_savebanfile() - - -/proc/ban_unban_log_save(var/formatted_log) - text2file(formatted_log,"data/ban_unban_log.txt") - - -/proc/jobban_remove(X) - for (var/i = 1; i <= length(jobban_keylist); i++) - if( findtext(jobban_keylist[i], "[X]") ) - jobban_keylist.Remove(jobban_keylist[i]) - jobban_savebanfile() - return 1 - return 0 +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +var/jobban_runonce // Updates legacy bans with new info +var/jobban_keylist[0] //to store the keys & ranks + +/proc/jobban_fullban(mob/M, rank, reason) + if (!M || !M.key) return + jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) + jobban_savebanfile() + +/proc/jobban_client_fullban(ckey, rank) + if (!ckey || !rank) return + jobban_keylist.Add(text("[ckey] - [rank]")) + jobban_savebanfile() + +//returns a reason if M is banned from rank, returns 0 otherwise +/proc/jobban_isbanned(mob/M, rank) + if(M && rank) + /* + if(_jobban_isbanned(M, rank)) return "Reason Unspecified" //for old jobban + */ + + if (guest_jobbans(rank)) + if(config.guest_jobban && IsGuestKey(M.key)) + return "Guest Job-ban" + if(config.usewhitelist && !check_whitelist(M)) + return "Whitelisted Job" + + return ckey_is_jobbanned(M.ckey, rank) + return 0 + +/proc/ckey_is_jobbanned(var/check_key, var/rank) + for(var/s in jobban_keylist) + if(findtext(s,"[check_key] - [rank]") == 1 ) + var/startpos = findtext(s, "## ")+3 + if(startpos && startpos> jobban_keylist + log_admin("Loading jobban_rank") + S["runonce"] >> jobban_runonce + + if (!length(jobban_keylist)) + jobban_keylist=list() + log_admin("jobban_keylist was empty") + else + if(!establish_db_connection()) + error("Database connection failed. Reverting to the legacy ban system.") + log_misc("Database connection failed. Reverting to the legacy ban system.") + config.ban_legacy_system = 1 + jobban_loadbanfile() + return + + //Job permabans + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") + query.Execute() + + while(query.NextRow()) + var/ckey = query.item[1] + var/job = query.item[2] + + jobban_keylist.Add("[ckey] - [job]") + + //Job tempbans + var/DBQuery/query1 = dbcon.NewQuery("SELECT ckey, job FROM erro_ban WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") + query1.Execute() + + while(query1.NextRow()) + var/ckey = query1.item[1] + var/job = query1.item[2] + + jobban_keylist.Add("[ckey] - [job]") + +/proc/jobban_savebanfile() + var/savefile/S=new("data/job_full.ban") + S["keys[0]"] << jobban_keylist + +/proc/jobban_unban(mob/M, rank) + jobban_remove("[M.ckey] - [rank]") + jobban_savebanfile() + + +/proc/ban_unban_log_save(var/formatted_log) + text2file(formatted_log,"data/ban_unban_log.txt") + + +/proc/jobban_remove(X) + for (var/i = 1; i <= length(jobban_keylist); i++) + if( findtext(jobban_keylist[i], "[X]") ) + jobban_keylist.Remove(jobban_keylist[i]) + jobban_savebanfile() + return 1 + return 0 diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm index 58e90122771..17909c4c294 100644 --- a/code/modules/admin/callproc/callproc.dm +++ b/code/modules/admin/callproc/callproc.dm @@ -1,210 +1,210 @@ -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - set waitfor = 0 - - if(!check_rights(R_DEBUG)) - return - - var/datum/target = null - var/targetselected = 0 - var/returnval = null - - switch(tgui_alert(usr, "Proc owned by something?","Call Proc",list("Yes","No"))) - if("Yes") - targetselected = 1 - var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) - if (!value["class"] || !value["value"]) - return - target = value["value"] - if("No") - target = null - targetselected = 0 - - var/procname = tgui_input_text(usr, "Proc path, eg: /proc/fake_blood","Path:", null) - if(!procname) - return - - //hascall() doesn't support proc paths (eg: /proc/gib(), it only supports "gib") - var/testname = procname - if(targetselected) - //Find one of the 3 possible ways they could have written /proc/PROCNAME - if(findtext(procname, "/proc/")) - testname = replacetext(procname, "/proc/", "") - else if(findtext(procname, "/proc")) - testname = replacetext(procname, "/proc", "") - else if(findtext(procname, "proc/")) - testname = replacetext(procname, "proc/", "") - //Clear out any parenthesis if they're a dummy - testname = replacetext(testname, "()", "") - - if(targetselected && !hascall(target,testname)) - to_chat(usr, "Error: callproc(): type [target.type] has no proc named [procname].") - return - else - var/procpath = text2path(procname) - if (!procpath) - to_chat(usr, "Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)") - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, "Error: callproc(): owner of proc no longer exists.") - return - var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." - log_admin(msg) - //message_admins(msg) //Proccall announce removed. - admin_ticket_log(target, msg) - returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - //message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed. - returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc - . = get_callproc_returnval(returnval, procname) - if(.) - to_chat(usr, .) - feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) -GLOBAL_VAR_INIT(AdminProcCallCount, 0) -GLOBAL_PROTECT(AdminProcCallCount) -GLOBAL_VAR(LastAdminCalledTargetRef) -GLOBAL_PROTECT(LastAdminCalledTargetRef) -GLOBAL_VAR(LastAdminCalledTarget) -GLOBAL_PROTECT(LastAdminCalledTarget) -GLOBAL_VAR(LastAdminCalledProc) -GLOBAL_PROTECT(LastAdminCalledProc) -GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) -GLOBAL_PROTECT(AdminProcCallSpamPrevention) - -/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target && procname == "Del") - to_chat(usr, "Calling Del() is not allowed") - return - - if(target != GLOBAL_PROC && !target.CanProcCall(procname)) - to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") - return - var/current_caller = GLOB.AdminProcCaller - var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller - if(!ckey) - CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") - if(current_caller && current_caller != ckey) - if(!GLOB.AdminProcCallSpamPrevention[ckey]) - to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") - GLOB.AdminProcCallSpamPrevention[ckey] = TRUE - UNTIL(!GLOB.AdminProcCaller) - to_chat(usr, "Running your proc") - GLOB.AdminProcCallSpamPrevention -= ckey - else - UNTIL(!GLOB.AdminProcCaller) - GLOB.LastAdminCalledProc = procname - if(target != GLOBAL_PROC) - GLOB.LastAdminCalledTargetRef = "\ref[target]" - GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you - ++GLOB.AdminProcCallCount - . = world.WrapAdminProcCall(target, procname, arguments) - if(--GLOB.AdminProcCallCount == 0) - GLOB.AdminProcCaller = null - -//adv proc call this, ya nerds -/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target == GLOBAL_PROC) - return call(procname)(arglist(arguments)) - else if(target != world) - return call(target, procname)(arglist(arguments)) - else - log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") - -/proc/IsAdminAdvancedProcCall() -#ifdef TESTING - return FALSE -#else - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey -#endif - -/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - set waitfor = 0 - - if(!check_rights(R_DEBUG)) - return - - var/procname = tgui_input_text(usr, "Proc name, eg: fake_blood","Proc:", null) - if(!procname) - return - if(!hascall(A,procname)) - to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname].") - return - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") - return - var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." - log_admin(msg) - //message_admins(msg) - admin_ticket_log(A, msg) - feedback_add_details("admin_verb","TPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc - . = get_callproc_returnval(returnval,procname) - if(.) - to_chat(usr, .) - -/client/proc/get_callproc_args() - var/argnum = tgui_input_number(usr, "Number of arguments","Number:",0) - if(isnull(argnum)) - return null //Cancel - - . = list() - //var/list/named_args = list() //Named arguments are removed, due to them making proccalling take too long. - while(argnum--) - /* //Named arguments are removed, due to them making proccalling take too long. - var/named_arg = input(usr,"Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null - if(isnull(named_arg)) - return null //Cancel - */ - var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - if (!value["class"]) - return null //Cancel - /* //Named arguments are removed, due to them making proccalling take too long. - if(named_arg) - named_args[named_arg] = value["value"] - else - . += value["value"] - if(LAZYLEN(named_args)) - . += named_args - */ - . += value["value"] - -/client/proc/get_callproc_returnval(returnval,procname) - . = "" - if(islist(returnval)) - var/list/returnedlist = returnval - . = "" - if(returnedlist.len) - var/assoc_check = returnedlist[1] - if(istext(assoc_check) && (returnedlist[assoc_check] != null)) - . += "[procname] returned an associative list:" - for(var/key in returnedlist) - . += "\n[key] = [returnedlist[key]]" - - else - . += "[procname] returned a list:" - for(var/elem in returnedlist) - . += "\n[elem]" - else - . = "[procname] returned an empty list" - . += "" - - else - . = "[procname] returned: [!isnull(returnval) ? returnval : "null"]" +/client/proc/callproc() + set category = "Debug" + set name = "Advanced ProcCall" + set waitfor = 0 + + if(!check_rights(R_DEBUG)) + return + + var/datum/target = null + var/targetselected = 0 + var/returnval = null + + switch(tgui_alert(usr, "Proc owned by something?","Call Proc",list("Yes","No"))) + if("Yes") + targetselected = 1 + var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) + if (!value["class"] || !value["value"]) + return + target = value["value"] + if("No") + target = null + targetselected = 0 + + var/procname = tgui_input_text(usr, "Proc path, eg: /proc/fake_blood","Path:", null) + if(!procname) + return + + //hascall() doesn't support proc paths (eg: /proc/gib(), it only supports "gib") + var/testname = procname + if(targetselected) + //Find one of the 3 possible ways they could have written /proc/PROCNAME + if(findtext(procname, "/proc/")) + testname = replacetext(procname, "/proc/", "") + else if(findtext(procname, "/proc")) + testname = replacetext(procname, "/proc", "") + else if(findtext(procname, "proc/")) + testname = replacetext(procname, "proc/", "") + //Clear out any parenthesis if they're a dummy + testname = replacetext(testname, "()", "") + + if(targetselected && !hascall(target,testname)) + to_chat(usr, "" + span_red("Error: callproc(): type [target.type] has no proc named [procname].") + "") + return + else + var/procpath = text2path(procname) + if (!procpath) + to_chat(usr, "" + span_red("Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)") + "") + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + to_chat(usr, "" + span_red("Error: callproc(): owner of proc no longer exists.") + "") + return + var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." + log_admin(msg) + //message_admins(msg) //Proccall announce removed. + admin_ticket_log(target, msg) + returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + //message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed. + returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc + . = get_callproc_returnval(returnval, procname) + if(.) + to_chat(usr, .) + feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +GLOBAL_VAR(AdminProcCaller) +GLOBAL_PROTECT(AdminProcCaller) +GLOBAL_VAR_INIT(AdminProcCallCount, 0) +GLOBAL_PROTECT(AdminProcCallCount) +GLOBAL_VAR(LastAdminCalledTargetRef) +GLOBAL_PROTECT(LastAdminCalledTargetRef) +GLOBAL_VAR(LastAdminCalledTarget) +GLOBAL_PROTECT(LastAdminCalledTarget) +GLOBAL_VAR(LastAdminCalledProc) +GLOBAL_PROTECT(LastAdminCalledProc) +GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) +GLOBAL_PROTECT(AdminProcCallSpamPrevention) + +/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target && procname == "Del") + to_chat(usr, "Calling Del() is not allowed") + return + + if(target != GLOBAL_PROC && !target.CanProcCall(procname)) + to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") + return + var/current_caller = GLOB.AdminProcCaller + var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller + if(!ckey) + CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") + if(current_caller && current_caller != ckey) + if(!GLOB.AdminProcCallSpamPrevention[ckey]) + to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.") + GLOB.AdminProcCallSpamPrevention[ckey] = TRUE + UNTIL(!GLOB.AdminProcCaller) + to_chat(usr, "Running your proc") + GLOB.AdminProcCallSpamPrevention -= ckey + else + UNTIL(!GLOB.AdminProcCaller) + GLOB.LastAdminCalledProc = procname + if(target != GLOBAL_PROC) + GLOB.LastAdminCalledTargetRef = "\ref[target]" + GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you + ++GLOB.AdminProcCallCount + . = world.WrapAdminProcCall(target, procname, arguments) + if(--GLOB.AdminProcCallCount == 0) + GLOB.AdminProcCaller = null + +//adv proc call this, ya nerds +/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target == GLOBAL_PROC) + return call(procname)(arglist(arguments)) + else if(target != world) + return call(target, procname)(arglist(arguments)) + else + log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") + +/proc/IsAdminAdvancedProcCall() +#ifdef TESTING + return FALSE +#else + return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey +#endif + +/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf) + set category = "Debug" + set name = "Atom ProcCall" + set waitfor = 0 + + if(!check_rights(R_DEBUG)) + return + + var/procname = tgui_input_text(usr, "Proc name, eg: fake_blood","Proc:", null) + if(!procname) + return + if(!hascall(A,procname)) + to_chat(usr, "" + span_red("Error: callproc_datum(): type [A.type] has no proc named [procname].") + "") + return + var/list/lst = get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists.") + return + var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]." + log_admin(msg) + //message_admins(msg) + admin_ticket_log(A, msg) + feedback_add_details("admin_verb","TPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc + . = get_callproc_returnval(returnval,procname) + if(.) + to_chat(usr, .) + +/client/proc/get_callproc_args() + var/argnum = tgui_input_number(usr, "Number of arguments","Number:",0) + if(isnull(argnum)) + return null //Cancel + + . = list() + //var/list/named_args = list() //Named arguments are removed, due to them making proccalling take too long. + while(argnum--) + /* //Named arguments are removed, due to them making proccalling take too long. + var/named_arg = input(usr,"Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null + if(isnull(named_arg)) + return null //Cancel + */ + var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + if (!value["class"]) + return null //Cancel + /* //Named arguments are removed, due to them making proccalling take too long. + if(named_arg) + named_args[named_arg] = value["value"] + else + . += value["value"] + if(LAZYLEN(named_args)) + . += named_args + */ + . += value["value"] + +/client/proc/get_callproc_returnval(returnval,procname) + . = "" + if(islist(returnval)) + var/list/returnedlist = returnval + . = "" + if(returnedlist.len) + var/assoc_check = returnedlist[1] + if(istext(assoc_check) && (returnedlist[assoc_check] != null)) + . += "[procname] returned an associative list:" + for(var/key in returnedlist) + . += "\n[key] = [returnedlist[key]]" + + else + . += "[procname] returned a list:" + for(var/elem in returnedlist) + . += "\n[elem]" + else + . = "[procname] returned an empty list" + . += "" + + else + . = span_blue("[procname] returned: [!isnull(returnval) ? returnval : "null"]") diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index a15a465984d..fa561d2e5aa 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -1,9 +1,9 @@ -/var/create_mob_html = null -/datum/admins/proc/create_mob(var/mob/user) - if (!create_mob_html) - var/mobjs = null - mobjs = jointext(typesof(/mob), ";") - create_mob_html = file2text('html/create_object.html') - create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") - - user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=680x600") +/var/create_mob_html = null +/datum/admins/proc/create_mob(var/mob/user) + if (!create_mob_html) + var/mobjs = null + mobjs = jointext(typesof(/mob), ";") + create_mob_html = file2text('html/create_object.html') + create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") + + user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=680x600") diff --git a/code/modules/admin/create_object.dm b/code/modules/admin/create_object.dm index 5520e52bf38..ce5d3077459 100644 --- a/code/modules/admin/create_object.dm +++ b/code/modules/admin/create_object.dm @@ -1,48 +1,48 @@ -/datum/admins/proc/create_panel_helper(template) - var/final_html = replacetext(template, "/* ref src */", "\ref[src];[HrefToken()]") - final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]") - return final_html - -/datum/admins/proc/create_object(var/mob/user) - var/static/create_object_html = null - if (!create_object_html) - var/objectjs = null - objectjs = jointext(typesof(/obj), ";") - create_object_html = file2text('html/create_object.html') - create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") - - user << browse(create_panel_helper(create_object_html), "window=create_object;size=680x600") - - -/datum/admins/proc/quick_create_object(var/mob/user) - - var/quick_create_object_html = null - var/pathtext = null - var/list/choices = list("/obj", - "/obj/structure", - "/obj/item", - "/obj/item/device", - "/obj/item/weapon", - "/obj/item/weapon/gun", - "/obj/item/weapon/reagent_containers", - "/obj/item/weapon/reagent_containers/food", - "/obj/item/clothing", - "/obj/item/weapon/storage/box/fluff", //VOREStation Edit, - "/obj/machinery", - "/obj/mecha", - "/obj/item/mecha_parts", - "/obj/item/mecha_parts/mecha_equipment") - - pathtext = tgui_input_list(usr, "Select the path of the object you wish to create.", "Path", choices, "/obj") - - if(!pathtext) - return - var path = text2path(pathtext) - - if (!quick_create_object_html) - var/objectjs = null - objectjs = jointext(typesof(path), ";") - quick_create_object_html = file2text('html/create_object.html') - quick_create_object_html = replacetext(quick_create_object_html, "null /* object types */", "\"[objectjs]\"") - - user << browse(create_panel_helper(quick_create_object_html), "window=quick_create_object;size=680x600") +/datum/admins/proc/create_panel_helper(template) + var/final_html = replacetext(template, "/* ref src */", "\ref[src];[HrefToken()]") + final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]") + return final_html + +/datum/admins/proc/create_object(var/mob/user) + var/static/create_object_html = null + if (!create_object_html) + var/objectjs = null + objectjs = jointext(typesof(/obj), ";") + create_object_html = file2text('html/create_object.html') + create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") + + user << browse(create_panel_helper(create_object_html), "window=create_object;size=680x600") + + +/datum/admins/proc/quick_create_object(var/mob/user) + + var/quick_create_object_html = null + var/pathtext = null + var/list/choices = list("/obj", + "/obj/structure", + "/obj/item", + "/obj/item/device", + "/obj/item/weapon", + "/obj/item/weapon/gun", + "/obj/item/weapon/reagent_containers", + "/obj/item/weapon/reagent_containers/food", + "/obj/item/clothing", + "/obj/item/weapon/storage/box/fluff", //VOREStation Edit, + "/obj/machinery", + "/obj/mecha", + "/obj/item/mecha_parts", + "/obj/item/mecha_parts/mecha_equipment") + + pathtext = tgui_input_list(usr, "Select the path of the object you wish to create.", "Path", choices, "/obj") + + if(!pathtext) + return + var path = text2path(pathtext) + + if (!quick_create_object_html) + var/objectjs = null + objectjs = jointext(typesof(path), ";") + quick_create_object_html = file2text('html/create_object.html') + quick_create_object_html = replacetext(quick_create_object_html, "null /* object types */", "\"[objectjs]\"") + + user << browse(create_panel_helper(quick_create_object_html), "window=quick_create_object;size=680x600") diff --git a/code/modules/admin/create_turf.dm b/code/modules/admin/create_turf.dm index ec7e5955848..5a3efffd875 100644 --- a/code/modules/admin/create_turf.dm +++ b/code/modules/admin/create_turf.dm @@ -1,9 +1,9 @@ -/var/create_turf_html = null -/datum/admins/proc/create_turf(var/mob/user) - if (!create_turf_html) - var/turfjs = null - turfjs = jointext(typesof(/turf), ";") - create_turf_html = file2text('html/create_object.html') - create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") - - user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=680x600") +/var/create_turf_html = null +/datum/admins/proc/create_turf(var/mob/user) + if (!create_turf_html) + var/turfjs = null + turfjs = jointext(typesof(/turf), ";") + create_turf_html = file2text('html/create_object.html') + create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") + + user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=680x600") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 46544a08096..3274936e2a8 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -1,157 +1,157 @@ -GLOBAL_VAR_INIT(href_token, GenerateToken()) -GLOBAL_PROTECT(href_token) - -var/list/admin_datums = list() - -/datum/admins - var/rank = "Temporary Admin" - var/client/owner = null - var/rights = 0 - var/fakekey = null - - var/datum/marked_datum - - var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description - var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders. - var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel - var/admincaster_signature //What you'll sign the newsfeeds as - - var/href_token - - -/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey) - if(!ckey) - error("Admin datum created without a ckey argument. Datum has been deleted") - qdel(src) - return - admincaster_signature = "[using_map.company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" - href_token = GenerateToken() - rank = initial_rank - rights = initial_rights - admin_datums[ckey] = src - if(rights & R_DEBUG) //grant profile access - world.SetConfig("APP/admin", ckey, "role=admin") - -/datum/admins/proc/associate(client/C) - if(istype(C)) - owner = C - owner.holder = src - owner.add_admin_verbs() //TODO - GLOB.admins |= C - -/datum/admins/proc/disassociate() - if(owner) - GLOB.admins -= owner - owner.remove_admin_verbs() - owner.deadmin_holder = owner.holder - owner.holder = null - -/datum/admins/proc/reassociate() - if(owner) - GLOB.admins += owner - owner.holder = src - owner.deadmin_holder = null - owner.add_admin_verbs() - -/datum/admins/vv_edit_var(var_name, var_value) - if(var_name == NAMEOF(src, rights) || var_name == NAMEOF(src, owner) || var_name == NAMEOF(src, rank)) - return FALSE - return ..() - -//TODO: Proccall guard, when all try/catch are removed and WrapAdminProccall is ported. - -/* -checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) -if rights_required == 0, then it simply checks if they are an admin. -if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed -generally it would be used like so: - -/proc/admin_proc() - if(!check_rights(R_ADMIN)) return - to_world("you have enough rights!") - -NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob. -*/ -/proc/check_rights(rights_required, show_msg=1, var/client/C = usr) - if(ismob(C)) - var/mob/M = C - C = M.client - if(!C) - return FALSE - if(!(istype(C, /client))) // If we still didn't find a client, something is wrong. - return FALSE - if(!C.holder) - if(show_msg) - to_chat(C, "Error: You are not an admin.") - return FALSE - - if(rights_required) - if(rights_required & C.holder.rights) - return TRUE - else - if(show_msg) - to_chat(C, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") - return FALSE - else - return TRUE - -//probably a bit iffy - will hopefully figure out a better solution -/proc/check_if_greater_rights_than(client/other) - if(usr && usr.client) - if(usr.client.holder) - if(!other || !other.holder) - return 1 - if(usr.client.holder.rights != other.holder.rights) - if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) - return 1 //we have all the rights they have and more - to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") - return 0 - -/client/proc/mark_datum(datum/D) - if(!holder) - return - if(holder.marked_datum) - vv_update_display(holder.marked_datum, "marked", "") - holder.marked_datum = D - vv_update_display(D, "marked", VV_MSG_MARKED) - -/client/proc/mark_datum_mapview(datum/D as mob|obj|turf|area in view(view)) - set category = "Debug" - set name = "Mark Object" - mark_datum(D) - -/client/proc/deadmin() - if(holder) - holder.disassociate() - //qdel(holder) - return 1 - -//This proc checks whether subject has at least ONE of the rights specified in rights_required. -/proc/check_rights_for(client/subject, rights_required) - if(subject && subject.holder) - if(rights_required && !(rights_required & subject.holder.rights)) - return 0 - return 1 - return 0 - -/proc/GenerateToken() - . = "" - for(var/I in 1 to 32) - . += "[rand(10)]" - -/proc/RawHrefToken(forceGlobal = FALSE) - var/tok = GLOB.href_token - if(!forceGlobal && usr) - var/client/C = usr.client - if(!C) - CRASH("No client for HrefToken()!") - var/datum/admins/holder = C.holder - if(holder) - tok = holder.href_token - return tok - -/proc/HrefToken(forceGlobal = FALSE) - return "admin_token=[RawHrefToken(forceGlobal)]" - -/proc/HrefTokenFormField(forceGlobal = FALSE) - return "" +GLOBAL_VAR_INIT(href_token, GenerateToken()) +GLOBAL_PROTECT(href_token) + +var/list/admin_datums = list() + +/datum/admins + var/rank = "Temporary Admin" + var/client/owner = null + var/rights = 0 + var/fakekey = null + + var/datum/marked_datum + + var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description + var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders. + var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel + var/admincaster_signature //What you'll sign the newsfeeds as + + var/href_token + + +/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey) + if(!ckey) + error("Admin datum created without a ckey argument. Datum has been deleted") + qdel(src) + return + admincaster_signature = "[using_map.company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" + href_token = GenerateToken() + rank = initial_rank + rights = initial_rights + admin_datums[ckey] = src + if(rights & R_DEBUG) //grant profile access + world.SetConfig("APP/admin", ckey, "role=admin") + +/datum/admins/proc/associate(client/C) + if(istype(C)) + owner = C + owner.holder = src + owner.add_admin_verbs() //TODO + GLOB.admins |= C + +/datum/admins/proc/disassociate() + if(owner) + GLOB.admins -= owner + owner.remove_admin_verbs() + owner.deadmin_holder = owner.holder + owner.holder = null + +/datum/admins/proc/reassociate() + if(owner) + GLOB.admins += owner + owner.holder = src + owner.deadmin_holder = null + owner.add_admin_verbs() + +/datum/admins/vv_edit_var(var_name, var_value) + if(var_name == NAMEOF(src, rights) || var_name == NAMEOF(src, owner) || var_name == NAMEOF(src, rank)) + return FALSE + return ..() + +//TODO: Proccall guard, when all try/catch are removed and WrapAdminProccall is ported. + +/* +checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) +if rights_required == 0, then it simply checks if they are an admin. +if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed +generally it would be used like so: + +/proc/admin_proc() + if(!check_rights(R_ADMIN)) return + to_world("you have enough rights!") + +NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob. +*/ +/proc/check_rights(rights_required, show_msg=1, var/client/C = usr) + if(ismob(C)) + var/mob/M = C + C = M.client + if(!C) + return FALSE + if(!(istype(C, /client))) // If we still didn't find a client, something is wrong. + return FALSE + if(!C.holder) + if(show_msg) + to_chat(C, "Error: You are not an admin.") + return FALSE + + if(rights_required) + if(rights_required & C.holder.rights) + return TRUE + else + if(show_msg) + to_chat(C, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") + return FALSE + else + return TRUE + +//probably a bit iffy - will hopefully figure out a better solution +/proc/check_if_greater_rights_than(client/other) + if(usr && usr.client) + if(usr.client.holder) + if(!other || !other.holder) + return 1 + if(usr.client.holder.rights != other.holder.rights) + if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) + return 1 //we have all the rights they have and more + to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") + return 0 + +/client/proc/mark_datum(datum/D) + if(!holder) + return + if(holder.marked_datum) + vv_update_display(holder.marked_datum, "marked", "") + holder.marked_datum = D + vv_update_display(D, "marked", VV_MSG_MARKED) + +/client/proc/mark_datum_mapview(datum/D as mob|obj|turf|area in view(view)) + set category = "Debug" + set name = "Mark Object" + mark_datum(D) + +/client/proc/deadmin() + if(holder) + holder.disassociate() + //qdel(holder) + return 1 + +//This proc checks whether subject has at least ONE of the rights specified in rights_required. +/proc/check_rights_for(client/subject, rights_required) + if(subject && subject.holder) + if(rights_required && !(rights_required & subject.holder.rights)) + return 0 + return 1 + return 0 + +/proc/GenerateToken() + . = "" + for(var/I in 1 to 32) + . += "[rand(10)]" + +/proc/RawHrefToken(forceGlobal = FALSE) + var/tok = GLOB.href_token + if(!forceGlobal && usr) + var/client/C = usr.client + if(!C) + CRASH("No client for HrefToken()!") + var/datum/admins/holder = C.holder + if(holder) + tok = holder.href_token + return tok + +/proc/HrefToken(forceGlobal = FALSE) + return "admin_token=[RawHrefToken(forceGlobal)]" + +/proc/HrefTokenFormField(forceGlobal = FALSE) + return "" diff --git a/code/modules/admin/newbanjob.dm b/code/modules/admin/newbanjob.dm index 4915c00594b..f495ddb5c09 100644 --- a/code/modules/admin/newbanjob.dm +++ b/code/modules/admin/newbanjob.dm @@ -1,274 +1,274 @@ -var/savefile/Banlistjob - - -/proc/_jobban_isbanned(var/client/clientvar, var/rank) - if(!clientvar) return 1 - ClearTempbansjob() - var/id = clientvar.computer_id - var/key = clientvar.ckey - if (guest_jobbans(rank)) - if(config.guest_jobban && IsGuestKey(key)) - return 1 - Banlistjob.cd = "/base" - if (Banlistjob.dir.Find("[key][id][rank]")) - return 1 - - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - if ((id == Banlistjob["id"] || key == Banlistjob["key"]) && rank == Banlistjob["rank"]) - return 1 - return 0 - -/proc/LoadBansjob() - - Banlistjob = new("data/job_fullnew.bdb") - log_admin("Loading Banlistjob") - - if (!length(Banlistjob.dir)) log_admin("Banlistjob is empty.") - - if (!Banlistjob.dir.Find("base")) - log_admin("Banlistjob missing base dir.") - Banlistjob.dir.Add("base") - Banlistjob.cd = "/base" - else if (Banlistjob.dir.Find("base")) - Banlistjob.cd = "/base" - - ClearTempbansjob() - return 1 - -/proc/ClearTempbansjob() - UpdateTime() - - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - //if (!Banlistjob["key"] || !Banlistjob["id"]) - // RemoveBanjob(A, "full") - // log_admin("Invalid Ban.") - // message_admins("Invalid Ban.") - // continue - - if (!Banlistjob["temp"]) continue - if (CMinutes >= Banlistjob["minutes"]) RemoveBanjob(A) - - return 1 - - -/proc/AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, rank) - UpdateTime() - var/bantimestamp - if (temp) - UpdateTime() - bantimestamp = CMinutes + minutes - if(rank == "Heads") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Personnel") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Site Manager") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - return 1 - if(rank == "Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Warden") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Security Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - return 1 - if(rank == "Engineering") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - return 1 - if(rank == "Research") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - return 1 - if(rank == "Medical") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - return 1 - if(rank == "CE_Station_Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "CE_Atmospheric_Tech") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "CE_Shaft_Miner") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Shaft Miner") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") - return 1 - if(rank == "Chemist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") - return 1 - if(rank == "Geneticist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") - return 1 - if(rank == "MD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") - return 1 - if(rank == "Scientist_RD") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") - return 1 - if(rank == "AI_Cyborg") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "AI") - return 1 - if(rank == "Detective_HoS") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") - return 1 - if(rank == "Virologist_RD_CMO") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") - AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Virologist") - return 1 - - Banlistjob.cd = "/base" - if ( Banlistjob.dir.Find("[ckey][computerid][rank]") ) - to_chat(usr,"Banjob already exists.") - return 0 - else - Banlistjob.dir.Add("[ckey][computerid][rank]") - Banlistjob.cd = "/base/[ckey][computerid][rank]" - Banlistjob["key"] << ckey - Banlistjob["id"] << computerid - Banlistjob["rank"] << rank - Banlistjob["reason"] << reason - Banlistjob["bannedby"] << bannedby - Banlistjob["temp"] << temp - if (temp) - Banlistjob["minutes"] << bantimestamp - admin_action_message(bannedby, ckey, "jobbanned-"+rank, reason, temp ? minutes : -1) //VOREStation Add - return 1 - -/proc/RemoveBanjob(foldername) - var/key - var/id - var/rank - Banlistjob.cd = "/base/[foldername]" - Banlistjob["key"] >> key - Banlistjob["id"] >> id - Banlistjob["rank"] >> rank - Banlistjob.cd = "/base" - - if (!Banlistjob.dir.Remove(foldername)) return 0 - - if(!usr) - log_admin("Banjob Expired: [key]") - message_admins("Banjob Expired: [key]") - else - log_admin("[key_name_admin(usr)] unjobbanned [key] from [rank]") - message_admins("[key_name_admin(usr)] unjobbanned:[key] from [rank]") - ban_unban_log_save("[key_name_admin(usr)] unjobbanned [key] from [rank]") - feedback_inc("ban_job_unban",1) - feedback_add_details("ban_job_unban","- [rank]") - - for (var/A in Banlistjob.dir) - Banlistjob.cd = "/base/[A]" - if ((key == Banlistjob["key"] || id == Banlistjob["id"]) && (rank == Banlistjob["rank"])) - Banlistjob.cd = "/base" - Banlistjob.dir.Remove(A) - continue - admin_action_message(usr.key, key, "unjobbanned-"+rank, "\[Unban\]", 0) //VOREStation Add - return 1 - -/proc/GetBanExpjob(minutes as num) - UpdateTime() - var/exp = minutes - CMinutes - if (exp <= 0) - return 0 - else - var/timeleftstring - if (exp >= 1440) //1440 = 1 day in minutes - timeleftstring = "[round(exp / 1440, 0.1)] Days" - else if (exp >= 60) //60 = 1 hour in minutes - timeleftstring = "[round(exp / 60, 0.1)] Hours" - else - timeleftstring = "[exp] Minutes" - return timeleftstring - -/datum/admins/proc/unjobbanpanel() - var/count = 0 - var/dat - //var/dat = "
                    Unban Player: (U) = Unban , (E) = Edit Ban (Total
                    >" - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - count++ - Banlistjob.cd = "/base/[A]" - dat += text("") - - dat += "
                    (U) Key: [Banlistjob["key"]] Rank: [Banlistjob["rank"]] ([Banlistjob["temp"] ? "[GetBanExpjob(Banlistjob["minutes"]) ? GetBanExpjob(Banlistjob["minutes"]) : "Removal pending" ]" : "Permaban"])(By: [Banlistjob["bannedby"]])(Reason: [Banlistjob["reason"]])
                    " - dat = "
                    Bans: (U) = Unban , - ([count] Bans)
                    [dat]" - usr << browse(dat, "window=unbanp;size=875x400") - -/*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) - if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) - to_chat(M, "You have been banned from [job] by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a permanent ban.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") - message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") -/datum/admins/proc/timejobban(ckey, computerid, reason, bannedby, temp, minutes, rank) - if(AddBanjob(ckey, computerid, reason, usr.ckey, 1, mins, job)) - to_chat(M, "You have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[usr.client.ckey] has jobbanned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")*/ -//////////////////////////////////// DEBUG //////////////////////////////////// - -/proc/CreateBansjob() - - UpdateTime() - - var/i - var/last - - for(i=0, i<1001, i++) - var/a = pick(1,0) - var/b = pick(1,0) - if(b) - Banlistjob.cd = "/base" - Banlistjob.dir.Add("trash[i]trashid[i]") - Banlistjob.cd = "/base/trash[i]trashid[i]" - to_chat(Banlistjob["key"], "trash[i]") - else - Banlistjob.cd = "/base" - Banlistjob.dir.Add("[last]trashid[i]") - Banlistjob.cd = "/base/[last]trashid[i]" - Banlistjob["key"] << last - to_chat(Banlistjob["id"], "trashid[i]") - to_chat(Banlistjob["reason"], "Trashban[i].") - Banlistjob["temp"] << a - Banlistjob["minutes"] << CMinutes + rand(1,2000) - to_chat(Banlistjob["bannedby"], "trashmin") - last = "trash[i]" - - Banlistjob.cd = "/base" - -/proc/ClearAllBansjob() - Banlistjob.cd = "/base" - for (var/A in Banlistjob.dir) - RemoveBanjob(A, "full") +var/savefile/Banlistjob + + +/proc/_jobban_isbanned(var/client/clientvar, var/rank) + if(!clientvar) return 1 + ClearTempbansjob() + var/id = clientvar.computer_id + var/key = clientvar.ckey + if (guest_jobbans(rank)) + if(config.guest_jobban && IsGuestKey(key)) + return 1 + Banlistjob.cd = "/base" + if (Banlistjob.dir.Find("[key][id][rank]")) + return 1 + + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + Banlistjob.cd = "/base/[A]" + if ((id == Banlistjob["id"] || key == Banlistjob["key"]) && rank == Banlistjob["rank"]) + return 1 + return 0 + +/proc/LoadBansjob() + + Banlistjob = new("data/job_fullnew.bdb") + log_admin("Loading Banlistjob") + + if (!length(Banlistjob.dir)) log_admin("Banlistjob is empty.") + + if (!Banlistjob.dir.Find("base")) + log_admin("Banlistjob missing base dir.") + Banlistjob.dir.Add("base") + Banlistjob.cd = "/base" + else if (Banlistjob.dir.Find("base")) + Banlistjob.cd = "/base" + + ClearTempbansjob() + return 1 + +/proc/ClearTempbansjob() + UpdateTime() + + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + Banlistjob.cd = "/base/[A]" + //if (!Banlistjob["key"] || !Banlistjob["id"]) + // RemoveBanjob(A, "full") + // log_admin("Invalid Ban.") + // message_admins("Invalid Ban.") + // continue + + if (!Banlistjob["temp"]) continue + if (CMinutes >= Banlistjob["minutes"]) RemoveBanjob(A) + + return 1 + + +/proc/AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, rank) + UpdateTime() + var/bantimestamp + if (temp) + UpdateTime() + bantimestamp = CMinutes + minutes + if(rank == "Heads") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Personnel") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Site Manager") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + return 1 + if(rank == "Security") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Warden") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Security Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + return 1 + if(rank == "Engineering") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + return 1 + if(rank == "Research") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + return 1 + if(rank == "Medical") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + return 1 + if(rank == "CE_Station_Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Engineer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + return 1 + if(rank == "CE_Atmospheric_Tech") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Atmospheric Technician") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + return 1 + if(rank == "CE_Shaft_Miner") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Shaft Miner") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Engineer") + return 1 + if(rank == "Chemist_RD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chemist") + return 1 + if(rank == "Geneticist_RD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Geneticist") + return 1 + if(rank == "MD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Medical Doctor") + return 1 + if(rank == "Scientist_RD") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Scientist") + return 1 + if(rank == "AI_Cyborg") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Cyborg") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "AI") + return 1 + if(rank == "Detective_HoS") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Detective") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Head of Security") + return 1 + if(rank == "Virologist_RD_CMO") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Chief Medical Officer") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Research Director") + AddBanjob(ckey, computerid, reason, bannedby, temp, minutes, "Virologist") + return 1 + + Banlistjob.cd = "/base" + if ( Banlistjob.dir.Find("[ckey][computerid][rank]") ) + to_chat(usr, span_red("Banjob already exists.")) + return 0 + else + Banlistjob.dir.Add("[ckey][computerid][rank]") + Banlistjob.cd = "/base/[ckey][computerid][rank]" + Banlistjob["key"] << ckey + Banlistjob["id"] << computerid + Banlistjob["rank"] << rank + Banlistjob["reason"] << reason + Banlistjob["bannedby"] << bannedby + Banlistjob["temp"] << temp + if (temp) + Banlistjob["minutes"] << bantimestamp + admin_action_message(bannedby, ckey, "jobbanned-"+rank, reason, temp ? minutes : -1) //VOREStation Add + return 1 + +/proc/RemoveBanjob(foldername) + var/key + var/id + var/rank + Banlistjob.cd = "/base/[foldername]" + Banlistjob["key"] >> key + Banlistjob["id"] >> id + Banlistjob["rank"] >> rank + Banlistjob.cd = "/base" + + if (!Banlistjob.dir.Remove(foldername)) return 0 + + if(!usr) + log_admin("Banjob Expired: [key]") + message_admins("Banjob Expired: [key]") + else + log_admin("[key_name_admin(usr)] unjobbanned [key] from [rank]") + message_admins("[key_name_admin(usr)] unjobbanned:[key] from [rank]") + ban_unban_log_save("[key_name_admin(usr)] unjobbanned [key] from [rank]") + feedback_inc("ban_job_unban",1) + feedback_add_details("ban_job_unban","- [rank]") + + for (var/A in Banlistjob.dir) + Banlistjob.cd = "/base/[A]" + if ((key == Banlistjob["key"] || id == Banlistjob["id"]) && (rank == Banlistjob["rank"])) + Banlistjob.cd = "/base" + Banlistjob.dir.Remove(A) + continue + admin_action_message(usr.key, key, "unjobbanned-"+rank, "\[Unban\]", 0) //VOREStation Add + return 1 + +/proc/GetBanExpjob(minutes as num) + UpdateTime() + var/exp = minutes - CMinutes + if (exp <= 0) + return 0 + else + var/timeleftstring + if (exp >= 1440) //1440 = 1 day in minutes + timeleftstring = "[round(exp / 1440, 0.1)] Days" + else if (exp >= 60) //60 = 1 hour in minutes + timeleftstring = "[round(exp / 60, 0.1)] Hours" + else + timeleftstring = "[exp] Minutes" + return timeleftstring + +/datum/admins/proc/unjobbanpanel() + var/count = 0 + var/dat + //var/dat = "
                    Unban Player:(U) = Unban , (E) = Edit Ban(Total
                    >" + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + count++ + Banlistjob.cd = "/base/[A]" + dat += text("") + + dat += "
                    (U) Key: [Banlistjob["key"]] Rank: [Banlistjob["rank"]] ([Banlistjob["temp"] ? "[GetBanExpjob(Banlistjob["minutes"]) ? GetBanExpjob(Banlistjob["minutes"]) : "Removal pending" ]" : "Permaban"])(By: [Banlistjob["bannedby"]])(Reason: [Banlistjob["reason"]])
                    " + dat = "
                    Bans: (U) = Unban , - ([count] Bans)
                    [dat]" + usr << browse(dat, "window=unbanp;size=875x400") + +/*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) + if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) + to_chat(M, "You have been banned from [job] by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") + message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") +/datum/admins/proc/timejobban(ckey, computerid, reason, bannedby, temp, minutes, rank) + if(AddBanjob(ckey, computerid, reason, usr.ckey, 1, mins, job)) + to_chat(M, "You have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[usr.client.ckey] has jobbanned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")*/ +//////////////////////////////////// DEBUG //////////////////////////////////// + +/proc/CreateBansjob() + + UpdateTime() + + var/i + var/last + + for(i=0, i<1001, i++) + var/a = pick(1,0) + var/b = pick(1,0) + if(b) + Banlistjob.cd = "/base" + Banlistjob.dir.Add("trash[i]trashid[i]") + Banlistjob.cd = "/base/trash[i]trashid[i]" + to_chat(Banlistjob["key"], "trash[i]") + else + Banlistjob.cd = "/base" + Banlistjob.dir.Add("[last]trashid[i]") + Banlistjob.cd = "/base/[last]trashid[i]" + Banlistjob["key"] << last + to_chat(Banlistjob["id"], "trashid[i]") + to_chat(Banlistjob["reason"], "Trashban[i].") + Banlistjob["temp"] << a + Banlistjob["minutes"] << CMinutes + rand(1,2000) + to_chat(Banlistjob["bannedby"], "trashmin") + last = "trash[i]" + + Banlistjob.cd = "/base" + +/proc/ClearAllBansjob() + Banlistjob.cd = "/base" + for (var/A in Banlistjob.dir) + RemoveBanjob(A, "full") diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index 5f84d5692a2..9c784ea49c6 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -1,149 +1,149 @@ -/client/proc/edit_admin_permissions() - set category = "Admin" - set name = "Permissions Panel" - set desc = "Edit admin permissions" - if(!check_rights(R_PERMISSIONS)) return - usr.client.holder.edit_admin_permissions() - -/datum/admins/proc/edit_admin_permissions() - if(!check_rights(R_PERMISSIONS)) return - - var/output = {" - - -Permissions Panel - - - - -
                    - - - - -"} - - for(var/adm_ckey in admin_datums) - var/datum/admins/D = admin_datums[adm_ckey] - if(!D) continue - var/rank = D.rank ? D.rank : "*none*" - var/rights = rights2text(D.rights," ") - if(!rights) rights = "*none*" - - output += "" - output += "" - output += "" - output += "" - output += "" - - output += {" -
                    CKEY \[+\]RANKPERMISSIONS
                    [adm_ckey] \[-\][rank][rights]
                    -
                    Search:
                    - -"} - - usr << browse(output,"window=editrights;size=600x500") - -/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) - to_chat(usr, "You do not have permission to do this!") - return - - establish_db_connection() - - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_rank) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(!istext(adm_ckey) || !istext(new_rank)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/new_admin = 1 - var/admin_id - while(select_query.NextRow()) - new_admin = 0 - admin_id = text2num(select_query.item[1]) - - if(new_admin) - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") - log_query.Execute() - to_chat(usr, "New admin added.") - else - if(!isnull(admin_id) && isnum(admin_id)) - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") - log_query.Execute() - to_chat(usr, "Admin rank changed.") - -/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) - to_chat(usr, "You do not have permission to do this!") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_permission) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(istext(new_permission)) - new_permission = text2num(new_permission) - - if(!istext(adm_ckey) || !isnum(new_permission)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/admin_id - var/admin_rights - while(select_query.NextRow()) - admin_id = text2num(select_query.item[1]) - admin_rights = text2num(select_query.item[2]) - - if(!admin_id) - return - - if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") - log_query.Execute() - to_chat(usr, "Permission removed.") - else //This admin doesn't have this permission, so we are adding it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") - log_query.Execute() - to_chat(usr, "Permission added.") +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + if(!check_rights(R_PERMISSIONS)) return + usr.client.holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!check_rights(R_PERMISSIONS)) return + + var/output = {" + + +Permissions Panel + + + + +
                    + + + + +"} + + for(var/adm_ckey in admin_datums) + var/datum/admins/D = admin_datums[adm_ckey] + if(!D) continue + var/rank = D.rank ? D.rank : "*none*" + var/rights = rights2text(D.rights," ") + if(!rights) rights = "*none*" + + output += "" + output += "" + output += "" + output += "" + output += "" + + output += {" +
                    CKEY \[+\]RANKPERMISSIONS
                    [adm_ckey] \[-\][rank][rights]
                    +
                    Search:
                    + +"} + + usr << browse(output,"window=editrights;size=600x500") + +/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) + to_chat(usr, "[span_red("You do not have permission to do this!")]") + return + + establish_db_connection() + + if(!dbcon.IsConnected()) + to_chat(usr, "[span_red("Failed to establish database connection")]") + return + + if(!adm_ckey || !new_rank) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(!istext(adm_ckey) || !istext(new_rank)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/new_admin = 1 + var/admin_id + while(select_query.NextRow()) + new_admin = 0 + admin_id = text2num(select_query.item[1]) + + if(new_admin) + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") + log_query.Execute() + to_chat(usr, "[span_blue("New admin added.")]") + else + if(!isnull(admin_id) && isnum(admin_id)) + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") + log_query.Execute() + to_chat(usr, "[span_blue("Admin rank changed.")]") + +/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) + to_chat(usr, "[span_red(">You do not have permission to do this!")]") + return + + establish_db_connection() + if(!dbcon.IsConnected()) + to_chat(usr, "[span_red("Failed to establish database connection!")]") + return + + if(!adm_ckey || !new_permission) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(istext(new_permission)) + new_permission = text2num(new_permission) + + if(!istext(adm_ckey) || !isnum(new_permission)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/admin_id + var/admin_rights + while(select_query.NextRow()) + admin_id = text2num(select_query.item[1]) + admin_rights = text2num(select_query.item[2]) + + if(!admin_id) + return + + if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") + log_query.Execute() + to_chat(usr, "[span_blue("Permission removed.")]") + else //This admin doesn't have this permission, so we are adding it. + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") + log_query.Execute() + to_chat(usr, "[span_blue("Permission added.")]") diff --git a/code/modules/admin/player_notes.dm b/code/modules/admin/player_notes.dm index f25e6024b92..53479bdcad2 100644 --- a/code/modules/admin/player_notes.dm +++ b/code/modules/admin/player_notes.dm @@ -37,7 +37,7 @@ infos += P info << infos - message_admins("[key_name_admin(user)] has edited [key]'s notes.") + message_admins(span_blue("[key_name_admin(user)] has edited [key]'s notes.")) log_admin("[key_name(user)] has edited [key]'s notes.") admin_action_message(P.author, key, "added note on", note, 0) //VOREStation Add del(info) // savefile, so NOT qdel @@ -62,7 +62,7 @@ infos.Remove(item) info << infos - message_admins("[key_name_admin(usr)] deleted one of [key]'s notes.") + message_admins(span_blue("[key_name_admin(usr)] deleted one of [key]'s notes.")) log_admin("[key_name(usr)] deleted one of [key]'s notes.") admin_action_message(usr.key, key, "deleted note on", "\[Note gone\]", 0) //VOREStation Add qdel(info) diff --git a/code/modules/admin/secrets/admin_secrets/admin_logs.dm b/code/modules/admin/secrets/admin_secrets/admin_logs.dm index b578eb046ff..83ebe9ec0ef 100644 --- a/code/modules/admin/secrets/admin_secrets/admin_logs.dm +++ b/code/modules/admin/secrets/admin_secrets/admin_logs.dm @@ -1,44 +1,44 @@ -/datum/admin_secret_item/admin_secret/admin_logs - name = "Admin Logs" - -/datum/admin_secret_item/admin_secret/admin_logs/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Admin Log
                    " - for(var/l in admin_log) - dat += "
                  • [l]
                  • " - if(!admin_log.len) - dat += "No-one has done anything this round!" - - var/datum/browser/popup = new(user, "adminlogs", "[src]", 550, 650, src) - popup.set_content(jointext(dat,null)) - popup.open() - - onclose(user, "adminlogs") - - -/datum/admin_secret_item/admin_secret/round_logs - name = "Round Dialogue Logs" - -/datum/admin_secret_item/admin_secret/round_logs/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Dialogue Log
                    " - - dat += "
                    " - - for(var/l in GLOB.round_text_log) - dat += "
                  • [l]
                  • " - - dat += "
                    " - - if(!GLOB.round_text_log) - dat += "No-one has said anything this round! (How odd?)" - - var/datum/browser/popup = new(user, "dialoguelogs", "[src]", 550, 650, src) - popup.set_content(jointext(dat,null)) - popup.open() - - onclose(user, "dialoguelogs") +/datum/admin_secret_item/admin_secret/admin_logs + name = "Admin Logs" + +/datum/admin_secret_item/admin_secret/admin_logs/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Admin Log
                    " + for(var/l in admin_log) + dat += "
                  • [l]
                  • " + if(!admin_log.len) + dat += "No-one has done anything this round!" + + var/datum/browser/popup = new(user, "adminlogs", "[src]", 550, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(user, "adminlogs") + + +/datum/admin_secret_item/admin_secret/round_logs + name = "Round Dialogue Logs" + +/datum/admin_secret_item/admin_secret/round_logs/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Dialogue Log
                    " + + dat += "
                    " + + for(var/l in GLOB.round_text_log) + dat += "
                  • [l]
                  • " + + dat += "
                    " + + if(!GLOB.round_text_log) + dat += "No-one has said anything this round! (How odd?)" + + var/datum/browser/popup = new(user, "dialoguelogs", "[src]", 550, 650, src) + popup.set_content(jointext(dat,null)) + popup.open() + + onclose(user, "dialoguelogs") diff --git a/code/modules/admin/secrets/admin_secrets/alter_narsie.dm b/code/modules/admin/secrets/admin_secrets/alter_narsie.dm index e764d1bb95a..e15dd13319d 100644 --- a/code/modules/admin/secrets/admin_secrets/alter_narsie.dm +++ b/code/modules/admin/secrets/admin_secrets/alter_narsie.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/admin_secret/alter_narise - name = "Alter Nar-Sie" - -/datum/admin_secret_item/admin_secret/alter_narise/execute(var/mob/user) - . = ..() - if(!.) - return - var/choice = tgui_alert(user, "How do you wish for Nar-Sie to interact with its surroundings?","NarChoice",list("CultStation13", "Nar-Singulo")) - if(choice == "CultStation13") - log_and_message_admins("has set narsie's behaviour to \"CultStation13\".", user) - narsie_behaviour = choice - if(choice == "Nar-Singulo") - log_and_message_admins("has set narsie's behaviour to \"Nar-Singulo\".", user) - narsie_behaviour = choice +/datum/admin_secret_item/admin_secret/alter_narise + name = "Alter Nar-Sie" + +/datum/admin_secret_item/admin_secret/alter_narise/execute(var/mob/user) + . = ..() + if(!.) + return + var/choice = tgui_alert(user, "How do you wish for Nar-Sie to interact with its surroundings?","NarChoice",list("CultStation13", "Nar-Singulo")) + if(choice == "CultStation13") + log_and_message_admins("has set narsie's behaviour to \"CultStation13\".", user) + narsie_behaviour = choice + if(choice == "Nar-Singulo") + log_and_message_admins("has set narsie's behaviour to \"Nar-Singulo\".", user) + narsie_behaviour = choice diff --git a/code/modules/admin/secrets/admin_secrets/bombing_list.dm b/code/modules/admin/secrets/admin_secrets/bombing_list.dm index f94af7e569a..d9c24097d24 100644 --- a/code/modules/admin/secrets/admin_secrets/bombing_list.dm +++ b/code/modules/admin/secrets/admin_secrets/bombing_list.dm @@ -1,12 +1,12 @@ -/datum/admin_secret_item/admin_secret/bombing_list - name = "Bombing List" - -/datum/admin_secret_item/admin_secret/bombing_list/execute(var/mob/user) - . = ..() - if(!.) - return - - var/dat = "Bombing List" - for(var/l in bombers) - dat += text("[l]
                    ") - user << browse(dat, "window=bombers") +/datum/admin_secret_item/admin_secret/bombing_list + name = "Bombing List" + +/datum/admin_secret_item/admin_secret/bombing_list/execute(var/mob/user) + . = ..() + if(!.) + return + + var/dat = "Bombing List" + for(var/l in bombers) + dat += text("[l]
                    ") + user << browse(dat, "window=bombers") diff --git a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm index 942ff60d77c..07c5096ee7b 100644 --- a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm @@ -1,37 +1,37 @@ -/datum/admin_secret_item/admin_secret/jump_shuttle - name = "Jump a Shuttle" - -/datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user) - . = ..() - if(!.) - return - var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) - if (!shuttle_tag) return - - var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] - - var/list/area_choices = return_areas() - var/origin_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) - if (!origin_area) return - - var/destination_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) - if (!destination_area) return - - var/long_jump = tgui_alert(user, "Is there a transition area for this jump?","Transition?", list("Yes","No")) - if (long_jump == "Yes") - var/transition_area = tgui_input_list(user, "Which area is the transition area? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) - if (!transition_area) return - - var/move_duration = tgui_input_number(user, "How many seconds will this jump take?") - - S.long_jump(area_choices[origin_area], area_choices[destination_area], area_choices[transition_area], move_duration) - message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle", 1) - log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle") - else - S.short_jump(area_choices[origin_area], area_choices[destination_area]) - message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle", 1) - log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle") +/datum/admin_secret_item/admin_secret/jump_shuttle + name = "Jump a Shuttle" + +/datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user) + . = ..() + if(!.) + return + var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) + if (!shuttle_tag) return + + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] + + var/list/area_choices = return_areas() + var/origin_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) + if (!origin_area) return + + var/destination_area = tgui_input_list(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) + if (!destination_area) return + + var/long_jump = tgui_alert(user, "Is there a transition area for this jump?","Transition?", list("Yes","No")) + if (long_jump == "Yes") + var/transition_area = tgui_input_list(user, "Which area is the transition area? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)", "Area Choice", area_choices) + if (!transition_area) return + + var/move_duration = tgui_input_number(user, "How many seconds will this jump take?") + + S.long_jump(area_choices[origin_area], area_choices[destination_area], area_choices[transition_area], move_duration) + message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle", 1) + log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] lasting [move_duration] seconds for the [shuttle_tag] shuttle") + else + S.short_jump(area_choices[origin_area], area_choices[destination_area]) + message_admins("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle", 1) + log_admin("[key_name_admin(user)] has initiated a jump from [origin_area] to [destination_area] for the [shuttle_tag] shuttle") diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm index c4fcc3ac200..007530e7de1 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm @@ -1,26 +1,26 @@ -/datum/admin_secret_item/admin_secret/launch_shuttle - name = "Launch a Shuttle" - -/datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user) - . = ..() - if(!.) - return - var/list/valid_shuttles = list() - for (var/shuttle_tag in SSshuttles.shuttles) - if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) - valid_shuttles += shuttle_tag - - var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to launch?", "Shuttle Choice", valid_shuttles) - if (!shuttle_tag) - return - - var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] - if (S.can_launch()) - S.launch(user) - log_and_message_admins("launched the [shuttle_tag] shuttle", user) - else - tgui_alert_async(user, "The [shuttle_tag] shuttle cannot be launched at this time. It's probably busy.") +/datum/admin_secret_item/admin_secret/launch_shuttle + name = "Launch a Shuttle" + +/datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user) + . = ..() + if(!.) + return + var/list/valid_shuttles = list() + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) + valid_shuttles += shuttle_tag + + var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to launch?", "Shuttle Choice", valid_shuttles) + if (!shuttle_tag) + return + + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] + if (S.can_launch()) + S.launch(user) + log_and_message_admins("launched the [shuttle_tag] shuttle", user) + else + tgui_alert_async(user, "The [shuttle_tag] shuttle cannot be launched at this time. It's probably busy.") diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm index 3c62428c099..517ddc91f75 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm @@ -1,26 +1,26 @@ -/datum/admin_secret_item/admin_secret/launch_shuttle_forced - name = "Launch a Shuttle (Forced)" - -/datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user) - . = ..() - if(!.) - return - var/list/valid_shuttles = list() - for (var/shuttle_tag in SSshuttles.shuttles) - if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) - valid_shuttles += shuttle_tag - - var/shuttle_tag = tgui_input_list(user, "Which shuttle's launch do you want to force?", "Shuttle Choice", valid_shuttles) - if (!shuttle_tag) - return - - var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] - if (S.can_force()) - S.force_launch(user) - log_and_message_admins("forced the [shuttle_tag] shuttle", user) - else - tgui_alert_async(user, "The [shuttle_tag] shuttle launch cannot be forced at this time. It's busy, or hasn't been launched yet.") +/datum/admin_secret_item/admin_secret/launch_shuttle_forced + name = "Launch a Shuttle (Forced)" + +/datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user) + . = ..() + if(!.) + return + var/list/valid_shuttles = list() + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) + valid_shuttles += shuttle_tag + + var/shuttle_tag = tgui_input_list(user, "Which shuttle's launch do you want to force?", "Shuttle Choice", valid_shuttles) + if (!shuttle_tag) + return + + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] + if (S.can_force()) + S.force_launch(user) + log_and_message_admins("forced the [shuttle_tag] shuttle", user) + else + tgui_alert_async(user, "The [shuttle_tag] shuttle launch cannot be forced at this time. It's busy, or hasn't been launched yet.") diff --git a/code/modules/admin/secrets/admin_secrets/list_dna.dm b/code/modules/admin/secrets/admin_secrets/list_dna.dm index f1c49f48972..5d4738d692b 100644 --- a/code/modules/admin/secrets/admin_secrets/list_dna.dm +++ b/code/modules/admin/secrets/admin_secrets/list_dna.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/admin_secret/list_dna - name = "List DNA (Blood)" - -/datum/admin_secret_item/admin_secret/list_dna/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Showing DNA from blood.
                    " - dat += "" - for(var/mob/living/carbon/human/H in mob_list) - if(H.dna && H.ckey) - dat += "" - dat += "
                    NameDNABlood Type
                    [H][H.dna.unique_enzymes][H.b_type]
                    " - user << browse(dat, "window=DNA;size=440x410") +/datum/admin_secret_item/admin_secret/list_dna + name = "List DNA (Blood)" + +/datum/admin_secret_item/admin_secret/list_dna/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Showing DNA from blood.
                    " + dat += "" + for(var/mob/living/carbon/human/H in mob_list) + if(H.dna && H.ckey) + dat += "" + dat += "
                    NameDNABlood Type
                    [H][H.dna.unique_enzymes][H.b_type]
                    " + user << browse(dat, "window=DNA;size=440x410") diff --git a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm index 03cb9701c41..0a9f4e0cb13 100644 --- a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm +++ b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm @@ -1,19 +1,19 @@ -/datum/admin_secret_item/admin_secret/list_fingerprints - name = "List Fingerprints" - -/datum/admin_secret_item/admin_secret/list_fingerprints/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat = "Showing Fingerprints.
                    " - dat += "" - for(var/mob/living/carbon/human/H in mob_list) - if(H.ckey) - if(H.dna && H.dna.uni_identity) - dat += "" - else if(H.dna && !H.dna.uni_identity) - dat += "" - else if(!H.dna) - dat += "" - dat += "
                    NameFingerprints
                    [H][md5(H.dna.uni_identity)]
                    [H]H.dna.uni_identity = null
                    [H]H.dna = null
                    " - user << browse(dat, "window=fingerprints;size=440x410") +/datum/admin_secret_item/admin_secret/list_fingerprints + name = "List Fingerprints" + +/datum/admin_secret_item/admin_secret/list_fingerprints/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat = "Showing Fingerprints.
                    " + dat += "" + for(var/mob/living/carbon/human/H in mob_list) + if(H.ckey) + if(H.dna && H.dna.uni_identity) + dat += "" + else if(H.dna && !H.dna.uni_identity) + dat += "" + else if(!H.dna) + dat += "" + dat += "
                    NameFingerprints
                    [H][md5(H.dna.uni_identity)]
                    [H]H.dna.uni_identity = null
                    [H]H.dna = null
                    " + user << browse(dat, "window=fingerprints;size=440x410") diff --git a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm index 7cad935ca3c..768101434a8 100644 --- a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm @@ -1,27 +1,27 @@ -/datum/admin_secret_item/admin_secret/move_shuttle - name = "Move a Shuttle" - -/datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user) - if(!SSshuttles) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user) - . = ..() - if(!.) - return - var/confirm = tgui_alert(user, "This command directly moves a shuttle from one area to another. DO NOT USE THIS UNLESS YOU ARE DEBUGGING A SHUTTLE AND YOU KNOW WHAT YOU ARE DOING.", "Are you sure?", list("Ok", "Cancel")) - if (confirm == "Cancel") - return - - var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) - if (!shuttle_tag) return - - var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] - - var/destination_tag = tgui_input_list(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)", "Landmark Choice", SSshuttles.registered_shuttle_landmarks) - if (!destination_tag) return - var/destination_location = SSshuttles.get_landmark(destination_tag) - if (!destination_location) return - - S.attempt_move(destination_location) - log_and_message_admins("moved the [shuttle_tag] shuttle", user) +/datum/admin_secret_item/admin_secret/move_shuttle + name = "Move a Shuttle" + +/datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user) + if(!SSshuttles) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user) + . = ..() + if(!.) + return + var/confirm = tgui_alert(user, "This command directly moves a shuttle from one area to another. DO NOT USE THIS UNLESS YOU ARE DEBUGGING A SHUTTLE AND YOU KNOW WHAT YOU ARE DOING.", "Are you sure?", list("Ok", "Cancel")) + if (confirm == "Cancel") + return + + var/shuttle_tag = tgui_input_list(user, "Which shuttle do you want to jump?", "Shuttle Choice", SSshuttles.shuttles) + if (!shuttle_tag) return + + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] + + var/destination_tag = tgui_input_list(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)", "Landmark Choice", SSshuttles.registered_shuttle_landmarks) + if (!destination_tag) return + var/destination_location = SSshuttles.get_landmark(destination_tag) + if (!destination_location) return + + S.attempt_move(destination_location) + log_and_message_admins("moved the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/admin_secrets/prison_warp.dm b/code/modules/admin/secrets/admin_secrets/prison_warp.dm index b4ac06fcc85..06740f17606 100644 --- a/code/modules/admin/secrets/admin_secrets/prison_warp.dm +++ b/code/modules/admin/secrets/admin_secrets/prison_warp.dm @@ -1,38 +1,38 @@ -/datum/admin_secret_item/admin_secret/prison_warp - name = "Prison Warp" - -/datum/admin_secret_item/admin_secret/prison_warp/can_execute(var/mob/user) - if(!ticker) return 0 - return ..() - -/datum/admin_secret_item/admin_secret/prison_warp/execute(var/mob/user) - . = ..() - if(!.) - return - for(var/mob/living/carbon/human/H in mob_list) - var/turf/T = get_turf(H) - var/security = 0 - if((T in using_map.admin_levels) || prisonwarped.Find(H)) - //don't warp them if they aren't ready or are already there - continue - H.Paralyse(5) - if(H.wear_id) - var/obj/item/weapon/card/id/id = H.get_idcard() - for(var/A in id.access) - if(A == access_security) - security++ - if(!security) - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/weapon/W in H) - if(istype(W, /obj/item/organ/external)) - continue - //don't strip organs - H.drop_from_inventory(W) - //teleport person to cell - H.loc = pick(prisonwarp) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) - else - //teleport security person - H.loc = pick(prisonsecuritywarp) - prisonwarped += H +/datum/admin_secret_item/admin_secret/prison_warp + name = "Prison Warp" + +/datum/admin_secret_item/admin_secret/prison_warp/can_execute(var/mob/user) + if(!ticker) return 0 + return ..() + +/datum/admin_secret_item/admin_secret/prison_warp/execute(var/mob/user) + . = ..() + if(!.) + return + for(var/mob/living/carbon/human/H in mob_list) + var/turf/T = get_turf(H) + var/security = 0 + if((T in using_map.admin_levels) || prisonwarped.Find(H)) + //don't warp them if they aren't ready or are already there + continue + H.Paralyse(5) + if(H.wear_id) + var/obj/item/weapon/card/id/id = H.get_idcard() + for(var/A in id.access) + if(A == access_security) + security++ + if(!security) + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/weapon/W in H) + if(istype(W, /obj/item/organ/external)) + continue + //don't strip organs + H.drop_from_inventory(W) + //teleport person to cell + H.loc = pick(prisonwarp) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) + else + //teleport security person + H.loc = pick(prisonsecuritywarp) + prisonwarped += H diff --git a/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm b/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm index c76ff2d9c18..c9e7cec5df3 100644 --- a/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm +++ b/code/modules/admin/secrets/admin_secrets/show_ai_laws.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/admin_secret/show_ai_laws - name = "Show AI laws" - -/datum/admin_secret_item/admin_secret/show_ai_laws/execute(var/mob/user) - . = ..() - if(.) - user.client.holder.output_ai_laws() +/datum/admin_secret_item/admin_secret/show_ai_laws + name = "Show AI laws" + +/datum/admin_secret_item/admin_secret/show_ai_laws/execute(var/mob/user) + . = ..() + if(.) + user.client.holder.output_ai_laws() diff --git a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm index f4bb82224ba..3cbe5062e71 100644 --- a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm +++ b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm @@ -1,12 +1,12 @@ -/datum/admin_secret_item/admin_secret/show_crew_manifest - name = "Show Crew Manifest" - -/datum/admin_secret_item/admin_secret/show_crew_manifest/execute(var/mob/user) - . = ..() - if(!.) - return - var/dat - dat += "

                    Crew Manifest

                    " - dat += data_core.get_manifest() - - user << browse(dat, "window=manifest;size=370x420;can_close=1") +/datum/admin_secret_item/admin_secret/show_crew_manifest + name = "Show Crew Manifest" + +/datum/admin_secret_item/admin_secret/show_crew_manifest/execute(var/mob/user) + . = ..() + if(!.) + return + var/dat + dat += "

                    Crew Manifest

                    " + dat += data_core.get_manifest() + + user << browse(dat, "window=manifest;size=370x420;can_close=1") diff --git a/code/modules/admin/secrets/admin_secrets/show_game_mode.dm b/code/modules/admin/secrets/admin_secrets/show_game_mode.dm index bde35639602..a323266f5c4 100644 --- a/code/modules/admin/secrets/admin_secrets/show_game_mode.dm +++ b/code/modules/admin/secrets/admin_secrets/show_game_mode.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/admin_secret/show_game_mode - name = "Show Game Mode" - -/datum/admin_secret_item/admin_secret/show_game_mode/can_execute(var/mob/user) - if(!ticker) - return 0 - return ..() - -/datum/admin_secret_item/admin_secret/show_game_mode/execute(var/mob/user) - . = ..() - if(!.) - return - if (ticker.mode) tgui_alert_async(usr, "The game mode is [ticker.mode.name]") - else tgui_alert_async(usr, "For some reason there's a ticker, but not a game mode") +/datum/admin_secret_item/admin_secret/show_game_mode + name = "Show Game Mode" + +/datum/admin_secret_item/admin_secret/show_game_mode/can_execute(var/mob/user) + if(!ticker) + return 0 + return ..() + +/datum/admin_secret_item/admin_secret/show_game_mode/execute(var/mob/user) + . = ..() + if(!.) + return + if (ticker.mode) tgui_alert_async(usr, "The game mode is [ticker.mode.name]") + else tgui_alert_async(usr, "For some reason there's a ticker, but not a game mode") diff --git a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm index 877e2c217de..67fc3d357f0 100644 --- a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm +++ b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm @@ -1,15 +1,15 @@ -/datum/admin_secret_item/admin_secret/show_law_changes - name = "Show law changes" - -/datum/admin_secret_item/admin_secret/show_law_changes/name() - return "Show Last [length(lawchanges)] Law change\s" - -/datum/admin_secret_item/admin_secret/show_law_changes/execute(var/mob/user) - . = ..() - if(!.) - return - - var/dat = "Showing last [length(lawchanges)] law changes.
                    " - for(var/sig in lawchanges) - dat += "[sig]
                    " - user << browse(dat, "window=lawchanges;size=800x500") +/datum/admin_secret_item/admin_secret/show_law_changes + name = "Show law changes" + +/datum/admin_secret_item/admin_secret/show_law_changes/name() + return "Show Last [length(lawchanges)] Law change\s" + +/datum/admin_secret_item/admin_secret/show_law_changes/execute(var/mob/user) + . = ..() + if(!.) + return + + var/dat = "Showing last [length(lawchanges)] law changes.
                    " + for(var/sig in lawchanges) + dat += "[sig]
                    " + user << browse(dat, "window=lawchanges;size=800x500") diff --git a/code/modules/admin/secrets/admin_secrets/show_signalers.dm b/code/modules/admin/secrets/admin_secrets/show_signalers.dm index 32a77cbef2e..4b0117a88f7 100644 --- a/code/modules/admin/secrets/admin_secrets/show_signalers.dm +++ b/code/modules/admin/secrets/admin_secrets/show_signalers.dm @@ -1,15 +1,15 @@ -/datum/admin_secret_item/admin_secret/show_signalers - name = "Show Last Signalers" - -/datum/admin_secret_item/admin_secret/show_signalers/name() - return "Show Last [length(lastsignalers)] Signaler\s" - -/datum/admin_secret_item/admin_secret/show_signalers/execute(var/mob/user) - . = ..() - if(!.) - return - - var/dat = "Showing last [length(lastsignalers)] signalers.
                    " - for(var/sig in lastsignalers) - dat += "[sig]
                    " - user << browse(dat, "window=lastsignalers;size=800x500") +/datum/admin_secret_item/admin_secret/show_signalers + name = "Show Last Signalers" + +/datum/admin_secret_item/admin_secret/show_signalers/name() + return "Show Last [length(lastsignalers)] Signaler\s" + +/datum/admin_secret_item/admin_secret/show_signalers/execute(var/mob/user) + . = ..() + if(!.) + return + + var/dat = "Showing last [length(lastsignalers)] signalers.
                    " + for(var/sig in lastsignalers) + dat += "[sig]
                    " + user << browse(dat, "window=lastsignalers;size=800x500") diff --git a/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm b/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm index 591c13bd3fb..bcfd9cd7b9c 100644 --- a/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm +++ b/code/modules/admin/secrets/admin_secrets/traitors_and_objectives.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/admin_secret/traitors_and_objectives - name = "Show current traitors and objectives" - -/datum/admin_secret_item/admin_secret/traitors_and_objectives/execute(var/mob/user) - . = ..() - if(.) - user.client.holder.check_antagonists() +/datum/admin_secret_item/admin_secret/traitors_and_objectives + name = "Show current traitors and objectives" + +/datum/admin_secret_item/admin_secret/traitors_and_objectives/execute(var/mob/user) + . = ..() + if(.) + user.client.holder.check_antagonists() diff --git a/code/modules/admin/secrets/final_solutions/summon_narsie.dm b/code/modules/admin/secrets/final_solutions/summon_narsie.dm index 0a382d71d7c..738c56e6746 100644 --- a/code/modules/admin/secrets/final_solutions/summon_narsie.dm +++ b/code/modules/admin/secrets/final_solutions/summon_narsie.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/final_solution/summon_narsie - name = "Summon Nar-Sie" - -/datum/admin_secret_item/final_solution/summon_narsie/execute(var/mob/user) - . = ..() - if(!.) - return - var/choice = tgui_alert(user, "You sure you want to end the round and summon Nar-Sie at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!",list("PRAISE SATAN", "Cancel")) - if(choice == "PRAISE SATAN") - new /obj/singularity/narsie/large(get_turf(user)) - log_and_message_admins("has summoned Nar-Sie and brought about a new realm of suffering.", user) +/datum/admin_secret_item/final_solution/summon_narsie + name = "Summon Nar-Sie" + +/datum/admin_secret_item/final_solution/summon_narsie/execute(var/mob/user) + . = ..() + if(!.) + return + var/choice = tgui_alert(user, "You sure you want to end the round and summon Nar-Sie at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!",list("PRAISE SATAN", "Cancel")) + if(choice == "PRAISE SATAN") + new /obj/singularity/narsie/large(get_turf(user)) + log_and_message_admins("has summoned Nar-Sie and brought about a new realm of suffering.", user) diff --git a/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm b/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm index ca7f0598d59..b7640771797 100644 --- a/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm +++ b/code/modules/admin/secrets/final_solutions/supermatter_cascade.dm @@ -1,13 +1,13 @@ -/datum/admin_secret_item/final_solution/supermatter_cascade - name = "Supermatter Cascade" - -/datum/admin_secret_item/final_solution/supermatter_cascade/execute(var/mob/user) - . = ..() - if(!.) - return - var/choice = tgui_alert(user, "You sure you want to destroy the universe and create a large explosion at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!", list("NO TIME TO EXPLAIN", "Cancel")) - if(choice == "NO TIME TO EXPLAIN") - explosion(get_turf(user), 8, 16, 24, 32, 1) - new /turf/unsimulated/wall/supermatter(get_turf(user)) - SetUniversalState(/datum/universal_state/supermatter_cascade) - message_admins("[key_name_admin(user)] has managed to destroy the universe with a supermatter cascade. Good job, [key_name_admin(user)]") +/datum/admin_secret_item/final_solution/supermatter_cascade + name = "Supermatter Cascade" + +/datum/admin_secret_item/final_solution/supermatter_cascade/execute(var/mob/user) + . = ..() + if(!.) + return + var/choice = tgui_alert(user, "You sure you want to destroy the universe and create a large explosion at your location? Misuse of this could result in removal of flags or hilarity.","WARNING!", list("NO TIME TO EXPLAIN", "Cancel")) + if(choice == "NO TIME TO EXPLAIN") + explosion(get_turf(user), 8, 16, 24, 32, 1) + new /turf/unsimulated/wall/supermatter(get_turf(user)) + SetUniversalState(/datum/universal_state/supermatter_cascade) + message_admins("[key_name_admin(user)] has managed to destroy the universe with a supermatter cascade. Good job, [key_name_admin(user)]") diff --git a/code/modules/admin/secrets/fun_secrets/break_all_lights.dm b/code/modules/admin/secrets/fun_secrets/break_all_lights.dm index ea54952a7e1..2256d4f7168 100644 --- a/code/modules/admin/secrets/fun_secrets/break_all_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/break_all_lights.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/break_all_lights - name = "Break All Lights" - -/datum/admin_secret_item/fun_secret/break_all_lights/execute(var/mob/user) - . = ..() - if(.) - lightsout(0,0) +/datum/admin_secret_item/fun_secret/break_all_lights + name = "Break All Lights" + +/datum/admin_secret_item/fun_secret/break_all_lights/execute(var/mob/user) + . = ..() + if(.) + lightsout(0,0) diff --git a/code/modules/admin/secrets/fun_secrets/break_some_lights.dm b/code/modules/admin/secrets/fun_secrets/break_some_lights.dm index 0c9aae1b166..70b9eaf8061 100644 --- a/code/modules/admin/secrets/fun_secrets/break_some_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/break_some_lights.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/break_some_lights - name = "Break Some Lights" - -/datum/admin_secret_item/fun_secret/break_some_lights/execute(var/mob/user) - . = ..() - if(.) - lightsout(1,2) +/datum/admin_secret_item/fun_secret/break_some_lights + name = "Break Some Lights" + +/datum/admin_secret_item/fun_secret/break_some_lights/execute(var/mob/user) + . = ..() + if(.) + lightsout(1,2) diff --git a/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm b/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm index 622c68d2ea9..e0525a72b3e 100644 --- a/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm +++ b/code/modules/admin/secrets/fun_secrets/fix_all_lights.dm @@ -1,10 +1,10 @@ -/datum/admin_secret_item/fun_secret/fix_all_lights - name = "Fix All Lights" - -/datum/admin_secret_item/fun_secret/fix_all_lights/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/obj/machinery/light/L in machines) - L.fix() +/datum/admin_secret_item/fun_secret/fix_all_lights + name = "Fix All Lights" + +/datum/admin_secret_item/fun_secret/fix_all_lights/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/obj/machinery/light/L in machines) + L.fix() diff --git a/code/modules/admin/secrets/fun_secrets/ghost_mode.dm b/code/modules/admin/secrets/fun_secrets/ghost_mode.dm index c59a9c05891..a3a5060047a 100644 --- a/code/modules/admin/secrets/fun_secrets/ghost_mode.dm +++ b/code/modules/admin/secrets/fun_secrets/ghost_mode.dm @@ -1,48 +1,48 @@ -/datum/admin_secret_item/fun_secret/ghost_mode - name = "Ghost Mode" - var/list/affected_mobs - -/datum/admin_secret_item/fun_secret/ghost_mode/New() - ..() - affected_mobs = list() - -/datum/admin_secret_item/fun_secret/ghost_mode/execute(var/mob/user) - . = ..() - if(!.) - return - - var/list/affected_areas = list() - for(var/mob/M in living_mob_list) - if(M.stat == CONSCIOUS && !(M in affected_mobs)) - affected_mobs |= M - switch(rand(1,4)) - if(1) - M.show_message(text("You shudder as if cold..."), 1) - if(2) - M.show_message(text("You feel something gliding across your back..."), 1) - if(3) - M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) - if(4) - M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) - - for(var/obj/W in orange(5,M)) - if(prob(25) && !W.anchored) - step_rand(W) - - var/area/A = get_area(M) - if(A.requires_power && !A.always_unpowered && A.power_light && (A.z in using_map.player_levels)) - affected_areas |= get_area(M) - - affected_mobs |= user - for(var/area/AffectedArea in affected_areas) - AffectedArea.power_light = 0 - AffectedArea.power_change() - spawn(rand(25,50)) - AffectedArea.power_light = 1 - AffectedArea.power_change() - - sleep(100) - for(var/mob/M in affected_mobs) - M.show_message(text("The chilling wind suddenly stops..."), 1) - affected_mobs.Cut() - affected_areas.Cut() +/datum/admin_secret_item/fun_secret/ghost_mode + name = "Ghost Mode" + var/list/affected_mobs + +/datum/admin_secret_item/fun_secret/ghost_mode/New() + ..() + affected_mobs = list() + +/datum/admin_secret_item/fun_secret/ghost_mode/execute(var/mob/user) + . = ..() + if(!.) + return + + var/list/affected_areas = list() + for(var/mob/M in living_mob_list) + if(M.stat == CONSCIOUS && !(M in affected_mobs)) + affected_mobs |= M + switch(rand(1,4)) + if(1) + M.show_message(text("You shudder as if cold..."), 1) + if(2) + M.show_message(text("You feel something gliding across your back..."), 1) + if(3) + M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) + if(4) + M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) + + for(var/obj/W in orange(5,M)) + if(prob(25) && !W.anchored) + step_rand(W) + + var/area/A = get_area(M) + if(A.requires_power && !A.always_unpowered && A.power_light && (A.z in using_map.player_levels)) + affected_areas |= get_area(M) + + affected_mobs |= user + for(var/area/AffectedArea in affected_areas) + AffectedArea.power_light = 0 + AffectedArea.power_change() + spawn(rand(25,50)) + AffectedArea.power_light = 1 + AffectedArea.power_change() + + sleep(100) + for(var/mob/M in affected_mobs) + M.show_message(text("The chilling wind suddenly stops..."), 1) + affected_mobs.Cut() + affected_areas.Cut() diff --git a/code/modules/admin/secrets/fun_secrets/only_one.dm b/code/modules/admin/secrets/fun_secrets/only_one.dm index bc0c962b29b..c065187c3ff 100644 --- a/code/modules/admin/secrets/fun_secrets/only_one.dm +++ b/code/modules/admin/secrets/fun_secrets/only_one.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/only_one - name = "There Can Be Only One" - -/datum/admin_secret_item/fun_secret/only_one/execute(var/mob/user) - . = ..() - if(.) - only_one() +/datum/admin_secret_item/fun_secret/only_one + name = "There Can Be Only One" + +/datum/admin_secret_item/fun_secret/only_one/execute(var/mob/user) + . = ..() + if(.) + only_one() diff --git a/code/modules/admin/secrets/fun_secrets/paintball_mode.dm b/code/modules/admin/secrets/fun_secrets/paintball_mode.dm index 16438ad195a..d17d648ea9d 100644 --- a/code/modules/admin/secrets/fun_secrets/paintball_mode.dm +++ b/code/modules/admin/secrets/fun_secrets/paintball_mode.dm @@ -1,14 +1,14 @@ -/datum/admin_secret_item/fun_secret/paintbal_mode - name = "Paintball Mode" - -/datum/admin_secret_item/fun_secret/paintbal_mode/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/species in GLOB.all_species) - var/datum/species/S = GLOB.all_species[species] - S.blood_color = "rainbow" - for(var/obj/effect/decal/cleanable/blood/B in world) - B.basecolor = "rainbow" - B.update_icon() +/datum/admin_secret_item/fun_secret/paintbal_mode + name = "Paintball Mode" + +/datum/admin_secret_item/fun_secret/paintbal_mode/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/species in GLOB.all_species) + var/datum/species/S = GLOB.all_species[species] + S.blood_color = "rainbow" + for(var/obj/effect/decal/cleanable/blood/B in world) + B.basecolor = "rainbow" + B.update_icon() diff --git a/code/modules/admin/secrets/fun_secrets/power_all_smes.dm b/code/modules/admin/secrets/fun_secrets/power_all_smes.dm index 277edd5a438..2320bcf2cb1 100644 --- a/code/modules/admin/secrets/fun_secrets/power_all_smes.dm +++ b/code/modules/admin/secrets/fun_secrets/power_all_smes.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/fun_secret/power_all_smes - name = "Power All SMES" - -/datum/admin_secret_item/fun_secret/power_all_smes/execute(var/mob/user) - . = ..() - if(.) - power_restore_quick() +/datum/admin_secret_item/fun_secret/power_all_smes + name = "Power All SMES" + +/datum/admin_secret_item/fun_secret/power_all_smes/execute(var/mob/user) + . = ..() + if(.) + power_restore_quick() diff --git a/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm b/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm index 28e9497f53f..ef0e40a33ad 100644 --- a/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm +++ b/code/modules/admin/secrets/fun_secrets/remove_all_clothing.dm @@ -1,10 +1,10 @@ -/datum/admin_secret_item/fun_secret/remove_all_clothing - name = "Remove ALL Clothing" - -/datum/admin_secret_item/fun_secret/remove_all_clothing/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/obj/item/clothing/O in world) - qdel(O) +/datum/admin_secret_item/fun_secret/remove_all_clothing + name = "Remove ALL Clothing" + +/datum/admin_secret_item/fun_secret/remove_all_clothing/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/obj/item/clothing/O in world) + qdel(O) diff --git a/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm b/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm index 73dba4ce4f9..9de30a0a5aa 100644 --- a/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm +++ b/code/modules/admin/secrets/fun_secrets/remove_internal_clothing.dm @@ -1,10 +1,10 @@ -/datum/admin_secret_item/fun_secret/remove_internal_clothing - name = "Remove 'Internal' Clothing" - -/datum/admin_secret_item/fun_secret/remove_internal_clothing/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/obj/item/clothing/under/O in world) - qdel(O) +/datum/admin_secret_item/fun_secret/remove_internal_clothing + name = "Remove 'Internal' Clothing" + +/datum/admin_secret_item/fun_secret/remove_internal_clothing/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/obj/item/clothing/under/O in world) + qdel(O) diff --git a/code/modules/admin/secrets/fun_secrets/send_strike_team.dm b/code/modules/admin/secrets/fun_secrets/send_strike_team.dm index 3ec1bacb2c0..9aee563d22e 100644 --- a/code/modules/admin/secrets/fun_secrets/send_strike_team.dm +++ b/code/modules/admin/secrets/fun_secrets/send_strike_team.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/fun_secret/send_strike_team - name = "Send Strike Team" - -/datum/admin_secret_item/fun_secret/send_strike_team/can_execute(var/mob/user) - if(!ticker) return 0 - return ..() - -/datum/admin_secret_item/fun_secret/send_strike_team/execute(var/mob/user) - . = ..() - if(.) - return user.client.strike_team() +/datum/admin_secret_item/fun_secret/send_strike_team + name = "Send Strike Team" + +/datum/admin_secret_item/fun_secret/send_strike_team/can_execute(var/mob/user) + if(!ticker) return 0 + return ..() + +/datum/admin_secret_item/fun_secret/send_strike_team/execute(var/mob/user) + . = ..() + if(.) + return user.client.strike_team() diff --git a/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm b/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm index 33ba8a861ce..6bcf2ee9b3c 100644 --- a/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm +++ b/code/modules/admin/secrets/fun_secrets/toggle_bomb_cap.dm @@ -1,21 +1,21 @@ -/datum/admin_secret_item/fun_secret/toggle_bomb_cap - name = "Toggle Bomb Cap" - permissions = R_SERVER - -/datum/admin_secret_item/fun_secret/toggle_bomb_cap/execute(var/mob/user) - . = ..() - if(!.) - return - - switch(max_explosion_range) - if(14) max_explosion_range = 16 - if(16) max_explosion_range = 20 - if(20) max_explosion_range = 28 - if(28) max_explosion_range = 56 - if(56) max_explosion_range = 128 - if(128) max_explosion_range = 14 - var/range_dev = max_explosion_range *0.25 - var/range_high = max_explosion_range *0.5 - var/range_low = max_explosion_range - message_admins("[key_name_admin(user)] changed the bomb cap to [range_dev], [range_high], [range_low]", 1) - log_admin("[key_name_admin(user)] changed the bomb cap to [max_explosion_range]") +/datum/admin_secret_item/fun_secret/toggle_bomb_cap + name = "Toggle Bomb Cap" + permissions = R_SERVER + +/datum/admin_secret_item/fun_secret/toggle_bomb_cap/execute(var/mob/user) + . = ..() + if(!.) + return + + switch(max_explosion_range) + if(14) max_explosion_range = 16 + if(16) max_explosion_range = 20 + if(20) max_explosion_range = 28 + if(28) max_explosion_range = 56 + if(56) max_explosion_range = 128 + if(128) max_explosion_range = 14 + var/range_dev = max_explosion_range *0.25 + var/range_high = max_explosion_range *0.5 + var/range_low = max_explosion_range + message_admins("[key_name_admin(user)] changed the bomb cap to [range_dev], [range_high], [range_low]", 1) + log_admin("[key_name_admin(user)] changed the bomb cap to [max_explosion_range]") diff --git a/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm b/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm index 81b77eabb64..d6819530a42 100644 --- a/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm +++ b/code/modules/admin/secrets/fun_secrets/triple_ai_mode.dm @@ -1,13 +1,13 @@ -/datum/admin_secret_item/fun_secret/triple_ai_mode - name = "Triple AI Mode" - -/datum/admin_secret_item/fun_secret/triple_ai_mode/can_execute(var/mob/user) - if(ticker && ticker.current_state > GAME_STATE_PREGAME) - return 0 - - return ..() - -/datum/admin_secret_item/admin_secret/triple_ai_mode/execute(var/mob/user) - . = ..() - if(.) - user.client.triple_ai() +/datum/admin_secret_item/fun_secret/triple_ai_mode + name = "Triple AI Mode" + +/datum/admin_secret_item/fun_secret/triple_ai_mode/can_execute(var/mob/user) + if(ticker && ticker.current_state > GAME_STATE_PREGAME) + return 0 + + return ..() + +/datum/admin_secret_item/admin_secret/triple_ai_mode/execute(var/mob/user) + . = ..() + if(.) + user.client.triple_ai() diff --git a/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm b/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm index ae6f36a1ac1..c82a07c96a6 100644 --- a/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm +++ b/code/modules/admin/secrets/fun_secrets/turn_humans_into_corgies.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/fun_secret/turn_humans_into_corgies - name = "Turn All Humans Into Corgies" - -/datum/admin_secret_item/fun_secret/turn_humans_into_corgies/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/mob/living/carbon/human/H in mob_list) - spawn(0) - H.corgize() +/datum/admin_secret_item/fun_secret/turn_humans_into_corgies + name = "Turn All Humans Into Corgies" + +/datum/admin_secret_item/fun_secret/turn_humans_into_corgies/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/mob/living/carbon/human/H in mob_list) + spawn(0) + H.corgize() diff --git a/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm b/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm index 99b1573adef..88e5bd9022f 100644 --- a/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm +++ b/code/modules/admin/secrets/fun_secrets/turn_humans_into_monkeys.dm @@ -1,11 +1,11 @@ -/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys - name = "Turn All Humans Into Monkeys" - -/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys/execute(var/mob/user) - . = ..() - if(!.) - return - - for(var/mob/living/carbon/human/H in mob_list) - spawn(0) - H.monkeyize() +/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys + name = "Turn All Humans Into Monkeys" + +/datum/admin_secret_item/fun_secret/turn_humans_into_monkeys/execute(var/mob/user) + . = ..() + if(!.) + return + + for(var/mob/living/carbon/human/H in mob_list) + spawn(0) + H.monkeyize() diff --git a/code/modules/admin/secrets/random_events/gravity.dm b/code/modules/admin/secrets/random_events/gravity.dm index b01bb496374..32c31827ab2 100644 --- a/code/modules/admin/secrets/random_events/gravity.dm +++ b/code/modules/admin/secrets/random_events/gravity.dm @@ -1,31 +1,31 @@ -/********** -* Gravity * -**********/ -/datum/admin_secret_item/random_event/gravity - name = "Toggle Station Artificial Gravity" - -/datum/admin_secret_item/random_event/gravity/can_execute(var/mob/user) - if(!(ticker && ticker.mode)) - return 0 - - return ..() - -/datum/admin_secret_item/random_event/gravity/execute(var/mob/user) - . = ..() - if(!.) - return - - gravity_is_on = !gravity_is_on - for(var/area/A in world) - A.gravitychange(gravity_is_on) - - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Grav") - if(gravity_is_on) - log_admin("[key_name(user)] toggled gravity on.", 1) - message_admins("[key_name_admin(user)] toggled gravity on.", 1) - command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") - else - log_admin("[key_name(user)] toggled gravity off.", 1) - message_admins("[key_name_admin(usr)] toggled gravity off.", 1) - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") +/********** +* Gravity * +**********/ +/datum/admin_secret_item/random_event/gravity + name = "Toggle Station Artificial Gravity" + +/datum/admin_secret_item/random_event/gravity/can_execute(var/mob/user) + if(!(ticker && ticker.mode)) + return 0 + + return ..() + +/datum/admin_secret_item/random_event/gravity/execute(var/mob/user) + . = ..() + if(!.) + return + + gravity_is_on = !gravity_is_on + for(var/area/A in world) + A.gravitychange(gravity_is_on) + + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Grav") + if(gravity_is_on) + log_admin("[key_name(user)] toggled gravity on.", 1) + message_admins("[key_name_admin(user)] toggled gravity on.", 1) + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") + else + log_admin("[key_name(user)] toggled gravity off.", 1) + message_admins("[key_name_admin(usr)] toggled gravity off.", 1) + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") diff --git a/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm b/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm index f96004565d8..d7e09d845e6 100644 --- a/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm +++ b/code/modules/admin/secrets/random_events/trigger_cordical_borer_infestation.dm @@ -1,7 +1,7 @@ -/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation - name = "Trigger a Cortical Borer infestation" - -/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation/execute(var/mob/user) - . = ..() - if(.) - return borers.attempt_random_spawn() +/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation + name = "Trigger a Cortical Borer infestation" + +/datum/admin_secret_item/random_event/trigger_cordical_borer_infestation/execute(var/mob/user) + . = ..() + if(.) + return borers.attempt_random_spawn() diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index fb1f9c4bb98..7bb048a0b5a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1,2095 +1,2095 @@ -/datum/admins/proc/CheckAdminHref(href, href_list) - var/auth = href_list["admin_token"] - . = auth && (auth == href_token || auth == GLOB.href_token) - if(.) - return - var/msg = !auth ? "no" : "a bad" - message_admins("[key_name_admin(usr)] clicked an href with [msg] authorization key!") - - var/debug_admin_hrefs = TRUE // Remove once everything is converted over - if(debug_admin_hrefs) - message_admins("Debug mode enabled, call not blocked. Please ask your coders to review this round's logs.") - log_world("UAH: [href]") - return TRUE - - log_admin("[key_name(usr)] clicked an href with [msg] authorization key! [href]") - -/datum/admins/Topic(href, href_list) - ..() - - if(usr.client != src.owner || !check_rights(0)) - log_admin("[key_name(usr)] tried to use the admin panel without authorization.") - message_admins("[usr.key] has attempted to override the admin panel!") - return - - if(!CheckAdminHref(href, href_list)) - return - - if(ticker.mode && ticker.mode.check_antagonists_topic(href, href_list)) - check_antagonists() - return - - if(href_list["ahelp"]) - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) - return - - var/ahelp_ref = href_list["ahelp"] - var/datum/admin_help/AH = locate(ahelp_ref) - if(AH) - AH.Action(href_list["ahelp_action"]) - else - to_chat(usr, "Ticket [ahelp_ref] has been deleted!") - - else if(href_list["ahelp_tickets"]) - GLOB.ahelp_tickets.BrowseTickets(text2num(href_list["ahelp_tickets"])) - - mentor_commands(href, href_list, src) - - if(href_list["dbsearchckey"] || href_list["dbsearchadmin"]) - - var/adminckey = href_list["dbsearchadmin"] - var/playerckey = href_list["dbsearchckey"] - var/playerip = href_list["dbsearchip"] - var/playercid = href_list["dbsearchcid"] - var/dbbantype = text2num(href_list["dbsearchbantype"]) - var/match = 0 - - if("dbmatch" in href_list) - match = 1 - - DB_ban_panel(playerckey, adminckey, playerip, playercid, dbbantype, match) - return - - else if(href_list["dbbanedit"]) - var/banedit = href_list["dbbanedit"] - var/banid = text2num(href_list["dbbanid"]) - if(!banedit || !banid) - return - - DB_ban_edit(banid, banedit) - return - - else if(href_list["dbbanaddtype"]) - - var/bantype = text2num(href_list["dbbanaddtype"]) - var/banckey = href_list["dbbanaddckey"] - var/banip = href_list["dbbanaddip"] - var/bancid = href_list["dbbanaddcid"] - var/banduration = text2num(href_list["dbbaddduration"]) - var/banjob = href_list["dbbanaddjob"] - var/banreason = href_list["dbbanreason"] - - banckey = ckey(banckey) - - switch(bantype) - if(BANTYPE_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") - return - banjob = null - if(BANTYPE_JOB_PERMA) - if(!banckey || !banreason || !banjob) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") - return - banduration = null - if(BANTYPE_JOB_TEMP) - if(!banckey || !banreason || !banjob || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") - return - - var/mob/playermob - - for(var/mob/M in player_list) - if(M.ckey == banckey) - playermob = M - break - - - banreason = "(MANUAL BAN) "+banreason - - if(!playermob) - if(banip) - banreason = "[banreason] (CUSTOM IP)" - if(bancid) - banreason = "[banreason] (CUSTOM CID)" - else - message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom ip and computer id fields replaced with the ip and computer id from the located mob") - notes_add(banckey,banreason,usr) - - DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) - if((bantype == BANTYPE_PERMA || bantype == BANTYPE_TEMP) && playermob.client) - qdel(playermob.client) - - else if(href_list["editrights"]) - if(!check_rights(R_PERMISSIONS)) - message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") - log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") - return - - var/adm_ckey - - var/task = href_list["editrights"] - if(task == "add") - var/new_ckey = ckey(tgui_input_text(usr,"New admin's ckey","Admin ckey", null)) - if(!new_ckey) return - if(new_ckey in admin_datums) - to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") - return - adm_ckey = new_ckey - task = "rank" - else if(task != "show") - adm_ckey = ckey(href_list["ckey"]) - if(!adm_ckey) - to_chat(usr, "Error: Topic 'editrights': No valid ckey") - return - - var/datum/admins/D = admin_datums[adm_ckey] - - if(task == "remove") - if(tgui_alert(usr, "Are you sure you want to remove [adm_ckey]?","Message",list("Yes","Cancel")) == "Yes") - if(!D) return - admin_datums -= adm_ckey - D.disassociate() - - message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") - log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") - log_admin_rank_modification(adm_ckey, "Removed") - - else if(task == "rank") - var/new_rank - if(admin_ranks.len) - new_rank = tgui_input_list(usr, "Please select a rank", "New rank", (admin_ranks|"*New Rank*")) - else - new_rank = tgui_input_list(usr, "Please select a rank", "New rank", list("Game Master","Game Admin", "Trial Admin", "Admin Observer","*New Rank*")) - - var/rights = 0 - if(D) - rights = D.rights - switch(new_rank) - if(null,"") return - if("*New Rank*") - new_rank = tgui_input_text(usr, "Please input a new rank", "New custom rank") - if(config.admin_legacy_system) - new_rank = ckeyEx(new_rank) - if(!new_rank) - to_chat(usr, "Error: Topic 'editrights': Invalid rank") - return - if(config.admin_legacy_system) - if(admin_ranks.len) - if(new_rank in admin_ranks) - rights = admin_ranks[new_rank] //we typed a rank which already exists, use its rights - else - admin_ranks[new_rank] = 0 //add the new rank to admin_ranks - else - if(config.admin_legacy_system) - new_rank = ckeyEx(new_rank) - rights = admin_ranks[new_rank] //we input an existing rank, use its rights - - if(D) - D.disassociate() //remove adminverbs and unlink from client - D.rank = new_rank //update the rank - D.rights = rights //update the rights based on admin_ranks (default: 0) - else - D = new /datum/admins(new_rank, rights, adm_ckey) - - var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) - D.associate(C) //link up with the client and add verbs - - message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin_rank_modification(adm_ckey, new_rank) - - else if(task == "permissions") - if(!D) return - var/list/permissionlist = list() - for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift - permissionlist[rights2text(i)] = i - var/new_permission = tgui_input_list(usr, "Select a permission to turn on/off", "Permission toggle", permissionlist) - if(!new_permission) return - D.rights ^= permissionlist[new_permission] - - message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey]") - log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey]") - log_admin_permission_modification(adm_ckey, permissionlist[new_permission]) - - edit_admin_permissions() - - else if(href_list["call_shuttle"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - if( ticker.mode.name == "blob" ) - tgui_alert_async(usr, "You can't call the shuttle during blob!") - return - - switch(href_list["call_shuttle"]) - if("1") - if ((!( ticker ) || !emergency_shuttle.location())) - return - if (emergency_shuttle.can_call()) - emergency_shuttle.call_evac() - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station.", 1) - - if("2") - if (!( ticker ) || !emergency_shuttle.location()) - return - if (emergency_shuttle.can_call()) - emergency_shuttle.call_evac() - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station.", 1) - - else if (emergency_shuttle.can_recall()) - emergency_shuttle.recall() - log_admin("[key_name(usr)] sent the Emergency Shuttle back") - message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back.", 1) - - href_list["secretsadmin"] = "check_antagonist" - - else if(href_list["edit_shuttle_time"]) - if(!check_rights(R_SERVER)) return - - if (emergency_shuttle.wait_for_launch) - var/new_time_left = tgui_input_number(usr, "Enter new shuttle launch countdown (seconds):","Edit Shuttle Launch Time", emergency_shuttle.estimate_launch_time() ) - - emergency_shuttle.launch_time = world.time + new_time_left*10 - - log_admin("[key_name(usr)] edited the Emergency Shuttle's launch time to [new_time_left]") - message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's launch time to [new_time_left*10]", 1) - else if (emergency_shuttle.shuttle.has_arrive_time()) - - var/new_time_left = tgui_input_number(usr, "Enter new shuttle arrival time (seconds):","Edit Shuttle Arrival Time", emergency_shuttle.estimate_arrival_time() ) - emergency_shuttle.shuttle.arrive_time = world.time + new_time_left*10 - - log_admin("[key_name(usr)] edited the Emergency Shuttle's arrival time to [new_time_left]") - message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's arrival time to [new_time_left*10]", 1) - else - tgui_alert_async(usr, "The shuttle is neither counting down to launch nor is it in transit. Please try again when it is.") - - href_list["secretsadmin"] = "check_antagonist" - - else if(href_list["delay_round_end"]) - if(!check_rights(R_SERVER)) return //VOREStation Edit - - ticker.delay_end = !ticker.delay_end - log_admin("[key_name(usr)] [ticker.delay_end ? "delayed the round end" : "has made the round end normally"].") - message_admins("[key_name(usr)] [ticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) - href_list["secretsadmin"] = "check_antagonist" - - else if(href_list["simplemake"]) - - if(!check_rights(R_SPAWN)) return - - var/mob/M = locate(href_list["mob"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/delmob = 0 - switch(tgui_alert(usr, "Delete old mob?","Message",list("Yes","No","Cancel"))) - if("Cancel") return - if("Yes") delmob = 1 - - log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]") - message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]", 1) - - switch(href_list["simplemake"]) - if("observer") M.change_mob_type( /mob/observer/dead , null, null, delmob ) - if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob ) - if("nymph") M.change_mob_type( /mob/living/carbon/alien/diona , null, null, delmob ) - if("human") M.change_mob_type( /mob/living/carbon/human , null, null, delmob, href_list["species"]) - if("slime") M.change_mob_type( /mob/living/simple_mob/slime/xenobio , null, null, delmob ) - if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob ) - if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) - if("cat") M.change_mob_type( /mob/living/simple_mob/animal/passive/cat , null, null, delmob ) - if("runtime") M.change_mob_type( /mob/living/simple_mob/animal/passive/cat/runtime , null, null, delmob ) - if("corgi") M.change_mob_type( /mob/living/simple_mob/animal/passive/dog/corgi , null, null, delmob ) - if("ian") M.change_mob_type( /mob/living/simple_mob/animal/passive/dog/corgi/Ian , null, null, delmob ) - if("crab") M.change_mob_type( /mob/living/simple_mob/animal/passive/crab , null, null, delmob ) - if("coffee") M.change_mob_type( /mob/living/simple_mob/animal/passive/crab/Coffee , null, null, delmob ) - if("parrot") M.change_mob_type( /mob/living/simple_mob/animal/passive/bird/parrot , null, null, delmob ) - if("polyparrot") M.change_mob_type( /mob/living/simple_mob/animal/passive/bird/parrot/poly , null, null, delmob ) - if("constructarmoured") M.change_mob_type( /mob/living/simple_mob/construct/juggernaut , null, null, delmob ) - if("constructbuilder") M.change_mob_type( /mob/living/simple_mob/construct/artificer , null, null, delmob ) - if("constructwraith") M.change_mob_type( /mob/living/simple_mob/construct/wraith , null, null, delmob ) - if("shade") M.change_mob_type( /mob/living/simple_mob/construct/shade , null, null, delmob ) - - - /////////////////////////////////////new ban stuff - else if(href_list["unbanf"]) - if(!check_rights(R_BAN)) return - - var/banfolder = href_list["unbanf"] - Banlist.cd = "/base/[banfolder]" - var/key = Banlist["key"] - if(tgui_alert(usr, "Are you sure you want to unban [key]?", "Confirmation", list("Yes", "No")) == "Yes") - if(RemoveBan(banfolder)) - unbanpanel() - else - tgui_alert_async(usr, "This ban has already been lifted / does not exist.", "Error") - unbanpanel() - - else if(href_list["warn"]) - usr.client.warn(href_list["warn"]) - - else if(href_list["unbane"]) - if(!check_rights(R_BAN)) return - - UpdateTime() - var/reason - - var/banfolder = href_list["unbane"] - Banlist.cd = "/base/[banfolder]" - var/reason2 = Banlist["reason"] - var/temp = Banlist["temp"] - - var/minutes = Banlist["minutes"] - - var/banned_key = Banlist["key"] - Banlist.cd = "/base" - - var/duration - - switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No"))) - if("Yes") - temp = 1 - var/mins = 0 - if(minutes > CMinutes) - mins = minutes - CMinutes - mins = tgui_input_number(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) - if(!mins) return - mins = min(525599,mins) - minutes = CMinutes + mins - duration = GetExp(minutes) - reason = sanitize(tgui_input_text(usr,"Reason?","reason",reason2)) - if(!reason) return - if("No") - temp = 0 - duration = "Perma" - reason = sanitize(tgui_input_text(usr,"Reason?","reason",reason2)) - if(!reason) return - - log_admin("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]", 1) - Banlist.cd = "/base/[banfolder]" - Banlist["reason"] << reason - Banlist["temp"] << temp - Banlist["minutes"] << minutes - Banlist["bannedby"] << usr.ckey - Banlist.cd = "/base" - feedback_inc("ban_edit",1) - unbanpanel() - - /////////////////////////////////////new ban stuff - - else if(href_list["jobban2"]) -// if(!check_rights(R_BAN)) return - - var/mob/M = locate(href_list["jobban2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") - return - if(!job_master) - to_chat(usr, "Job Master has not been setup!") - return - - var/dat = "" - var/header = "Job-Ban Panel: [M.name]" - var/body - var/jobs = "" - - /***********************************WARNING!************************************ - The jobban stuff looks mangled and disgusting - But it looks beautiful in-game - -Nodrak - ************************************WARNING!***********************************/ - var/counter = 0 -//Regular jobs - //Command (Blue) - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 6) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Command Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Security (Red) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Security Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Engineering (Yellow) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Engineering Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Cargo (Yellow) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Cargo Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Medical (White) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Medical Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Science (Purple) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Science Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //VOREStation Edit Start - //Exploration (Purple) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Exploration Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Offmap (Kinda lightish bluey) - counter = 0 - // Needs to be done early because it uses the length of the list for sizing - var/list/offmap_jobs = list() - for(var/dept in offmap_departments) - offmap_jobs += SSjob.get_job_titles_in_department(dept) - jobs += "" - jobs += "" - for(var/jobPos in offmap_jobs) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Offmap Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //VOREstation Edit End - //Civilian (Grey) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - if(jobban_isbanned(M, "Internal Affairs Agent")) - jobs += "" - else - jobs += "" - - jobs += "
                    Civilian Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    Internal Affairs AgentInternal Affairs Agent
                    " - - //Synthetic (Green) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) - if(!jobPos) continue - var/datum/job/job = job_master.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Synthetic Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " - - //Antagonist (Orange) - counter = 0 - var/isbanned_dept = jobban_isbanned(M, "Syndicate") - jobs += "" - jobs += "" - - // Antagonists. - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(!antag || !antag.bantype) - continue - - if(jobban_isbanned(M, "[antag.bantype]") || isbanned_dept) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Antagonist Positions
                    [replacetext("[antag.role_text]", " ", " ")][replacetext("[antag.role_text]", " ", " ")]
                    " - - //Misc 'roles' - counter = 0 - var/list/misc_roles = list("Dionaea", "Graffiti", "Custom loadout", "pAI", "GhostRoles", "AntagHUD") - jobs += "" - jobs += "" - for(var/entry in misc_roles) - if(jobban_isbanned(M, entry)) - jobs += "" - else - jobs += "" - - if(++counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - jobs += "
                    Other Roles
                    [entry][entry]
                    " - - // Finished - body = "[jobs]" - dat = "[header][body]" - usr << browse(dat, "window=jobban2;size=800x490") - return - - //JOBBAN'S INNARDS - else if(href_list["jobban3"]) - if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN,0)) - to_chat(usr, "You do not have the appropriate permissions to add job bans!") - return - - if(check_rights(R_MOD,0) && !check_rights(R_ADMIN,0) && !config.mods_can_job_tempban) // If mod and tempban disabled - to_chat(usr, "Mod jobbanning is disabled!") - return - - var/mob/M = locate(href_list["jobban4"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(M != usr) //we can jobban ourselves - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them - tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!") - return - - if(!job_master) - to_chat(usr, "Job Master has not been setup!") - return - - //get jobs for department if specified, otherwise just returnt he one job in a list. - var/list/joblist = list() - switch(href_list["jobban3"]) - if("commanddept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("securitydept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("engineeringdept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("cargodept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("medicaldept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("sciencedept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - //VOREStation Edit Start - if("explorationdept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("offmapdept") - for(var/dept in offmap_departments) - for(var/jobPos in SSjob.get_job_titles_in_department(dept)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - //VOREStation Edit End - if("civiliandept") - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("nonhumandept") - joblist += "pAI" - for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) - if(!jobPos) continue - var/datum/job/temp = job_master.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - else - joblist += href_list["jobban3"] - - //Create a list of unbanned jobs within joblist - var/list/notbannedlist = list() - for(var/job in joblist) - if(!jobban_isbanned(M, job)) - notbannedlist += job - - //Banning comes first - if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. - switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban", list("Yes","No","Cancel"))) - if("Yes") - if(!check_rights(R_MOD,0) && !check_rights(R_BAN, 0)) - to_chat(usr, " You cannot issue temporary job-bans!") - return - if(config.ban_legacy_system) - to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") - return - var/mins = tgui_input_number(usr,"How long (in minutes)?","Ban time",1440) - if(!mins) - return - if(check_rights(R_MOD, 0) && !check_rights(R_BAN, 0) && mins > config.mod_job_tempban_max) - to_chat(usr, " Moderators can only job tempban up to [config.mod_job_tempban_max] minutes!") - return - var/reason = sanitize(tgui_input_text(usr,"Reason?","Please State Reason","")) - if(!reason) - return - - var/msg - for(var/job in notbannedlist) - ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") - log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes") - feedback_inc("ban_job_tmp",1) - DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job) - feedback_add_details("ban_job_tmp","- [job]") - jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") //Legacy banning does not support temporary jobbans. - if(!msg) - msg = job - else - msg += ", [job]" - notes_add(M.ckey, "Banned from [msg] - [reason]", usr) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "This jobban will be lifted in [mins] minutes.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("No") - if(!check_rights(R_BAN)) return - var/reason = sanitize(tgui_input_text(usr,"Reason?","Please State Reason","")) - if(reason) - var/msg - for(var/job in notbannedlist) - ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") - log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]") - feedback_inc("ban_job",1) - DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job) - feedback_add_details("ban_job","- [job]") - jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") - if(!msg) msg = job - else msg += ", [job]" - notes_add(M.ckey, "Banned from [msg] - [reason]", usr) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Jobban can be lifted only upon request.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("Cancel") - return - - //Unbanning joblist - //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) - if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. - if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") - DB_ban_panel(M.ckey) - return - var/msg - for(var/job in joblist) - var/reason = jobban_isbanned(M, job) - if(!reason) continue //skip if it isn't jobbanned anyway - switch(tgui_alert(usr, "Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm",list("Yes","No"))) - if("Yes") - ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") - log_admin("[key_name(usr)] unbanned [key_name(M)] from [job]") - DB_ban_unban(M.ckey, BANTYPE_JOB_PERMA, job) - feedback_inc("ban_job_unban",1) - feedback_add_details("ban_job_unban","- [job]") - jobban_unban(M, job) - if(!msg) msg = job - else msg += ", [job]" - else - continue - if(msg) - message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - return 0 //we didn't do anything! - - else if(href_list["boot2"]) - var/mob/M = locate(href_list["boot2"]) - if (ismob(M)) - if(!check_if_greater_rights_than(M.client)) - return - var/reason = sanitize(tgui_input_text(usr, "Please enter reason.", multiline = TRUE, prevent_enter = TRUE)) - if(!reason) - return - - to_chat(M, span("filter_system critical", "You have been kicked from the server: [reason]")) - log_admin("[key_name(usr)] booted [key_name(M)] for reason: '[reason]'.") - message_admins("[key_name_admin(usr)] booted [key_name_admin(M)] for reason '[reason]'.", 1) - //M.client = null - admin_action_message(usr.key, M.key, "kicked", reason, 0) //VOREStation Add - qdel(M.client) - - else if(href_list["removejobban"]) - if(!check_rights(R_BAN)) return - - var/t = href_list["removejobban"] - if(t) - if((tgui_alert(usr, "Do you want to unjobban [t]?","Unjobban confirmation", list("Yes", "No")) == "Yes") && t) //No more misclicks! Unless you do it twice. - log_admin("[key_name(usr)] removed [t]") - message_admins("[key_name_admin(usr)] removed [t]", 1) - jobban_remove(t) - href_list["ban"] = 1 // lets it fall through and refresh - var/t_split = splittext(t, " - ") - var/key = t_split[1] - var/job = t_split[2] - DB_ban_unban(ckey(key), BANTYPE_JOB_PERMA, job) - - else if(href_list["newban"]) - if(!check_rights(R_MOD,0) && !check_rights(R_BAN, 0)) - to_chat(usr, "You do not have the appropriate permissions to add bans!") - return - - if(check_rights(R_MOD,0) && !check_rights(R_ADMIN, 0) && !config.mods_can_job_tempban) // If mod and tempban disabled - to_chat(usr, "Mod jobbanning is disabled!") - return - - var/mob/M = locate(href_list["newban"]) - if(!ismob(M)) return - - if(M.client && M.client.holder) return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway - - switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No","Cancel"))) - if("Yes") - var/mins = tgui_input_number(usr,"How long (in minutes)?","Ban time",1440) - if(!mins) - return - if(check_rights(R_MOD, 0) && !check_rights(R_BAN, 0) && mins > config.mod_tempban_max) - to_chat(usr, "Moderators can only job tempban up to [config.mod_tempban_max] minutes!") - return - if(mins >= 525600) mins = 525599 - var/reason = sanitize(tgui_input_text(usr,"Reason?","reason","Griefer")) - if(!reason) - return - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") - notes_add(M.ckey,"[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.",usr) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - feedback_inc("ban_tmp",1) - DB_ban_record(BANTYPE_TEMP, M, mins, reason) - feedback_inc("ban_tmp_mins",mins) - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - var/datum/admin_help/AH = M.client ? M.client.current_ticket : null - if(AH) - AH.Resolve() - qdel(M.client) - //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. - if("No") - if(!check_rights(R_BAN)) return - var/reason = sanitize(tgui_input_text(usr,"Reason?","reason","Griefer")) - if(!reason) - return - switch(tgui_alert(usr,"IP ban?","IP Ban",list("Yes","No","Cancel"))) - if("Cancel") return - if("Yes") - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) - if("No") - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a permanent ban.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.") - notes_add(M.ckey,"[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.",usr) - log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") - message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") - feedback_inc("ban_perma",1) - DB_ban_record(BANTYPE_PERMA, M, -1, reason) - var/datum/admin_help/AH = M.client ? M.client.current_ticket : null - if(AH) - AH.Resolve() - qdel(M.client) - //qdel(M) - if("Cancel") - return - - else if(href_list["mute"]) - if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN)) return - - var/mob/M = locate(href_list["mute"]) - if(!ismob(M)) return - if(!M.client) return - - var/mute_type = href_list["mute_type"] - if(istext(mute_type)) mute_type = text2num(mute_type) - if(!isnum(mute_type)) return - - cmd_admin_mute(M, mute_type) - - else if(href_list["c_mode"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - if(ticker && ticker.mode) - return tgui_alert_async(usr, "The game has already started.") - var/dat = {"What mode do you wish to play?
                    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
                    "} - dat += {"Secret
                    "} - dat += {"Random
                    "} - dat += {"Now: [master_mode]"} - usr << browse(dat, "window=c_mode") - - else if(href_list["f_secret"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - if(ticker && ticker.mode) - return tgui_alert_async(usr, "The game has already started.") - if(master_mode != "secret") - return tgui_alert_async(usr, "The game mode has to be secret!") - var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
                    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
                    "} - dat += {"Random (default)
                    "} - dat += {"Now: [secret_force_mode]"} - usr << browse(dat, "window=f_secret") - - else if(href_list["c_mode2"]) - if(!check_rights(R_ADMIN|R_SERVER|R_EVENT)) return - - if (ticker && ticker.mode) - return tgui_alert_async(usr, "The game has already started.") - master_mode = href_list["c_mode2"] - log_admin("[key_name(usr)] set the mode as [config.mode_names[master_mode]].") - message_admins("[key_name_admin(usr)] set the mode as [config.mode_names[master_mode]].", 1) - to_world("The mode is now: [config.mode_names[master_mode]]") - Game() // updates the main game menu - world.save_mode(master_mode) - .(href, list("c_mode"=1)) - - else if(href_list["f_secret2"]) - if(!check_rights(R_ADMIN|R_SERVER|R_EVENT)) return - - if(ticker && ticker.mode) - return tgui_alert_async(usr, "The game has already started.") - if(master_mode != "secret") - return tgui_alert_async(usr, "The game mode has to be secret!") - secret_force_mode = href_list["f_secret2"] - log_admin("[key_name(usr)] set the forced secret mode as [secret_force_mode].") - message_admins("[key_name_admin(usr)] set the forced secret mode as [secret_force_mode].", 1) - Game() // updates the main game menu - .(href, list("f_secret"=1)) - - else if(href_list["monkeyone"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locate(href_list["monkeyone"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)]", 1) - H.monkeyize() - - else if(href_list["corgione"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locate(href_list["corgione"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)]", 1) - H.corgize() - - else if(href_list["forcespeech"]) - if(!check_rights(R_FUN)) return - - var/mob/M = locate(href_list["forcespeech"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob") - - var/speech = tgui_input_text(usr, "What will [key_name(M)] say?.", "Force speech", "") // Don't need to sanitize, since it does that in say(), we also trust our admins. - if(!speech) return - M.say(speech) - speech = sanitize(speech) // Nah, we don't trust them - log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") - message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") - - else if(href_list["sendtoprison"]) - if(!check_rights(R_ADMIN)) return - - if(tgui_alert(usr, "Send to admin prison for the round?", "Message", list("Yes", "No")) != "Yes") - return - - var/mob/M = locate(href_list["sendtoprison"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - var/turf/prison_cell = pick(prisonwarp) - if(!prison_cell) return - - var/obj/structure/closet/secure_closet/brig/locker = new /obj/structure/closet/secure_closet/brig(prison_cell) - locker.opened = 0 - locker.locked = 1 - - //strip their stuff and stick it in the crate - for(var/obj/item/I in M) - M.drop_from_inventory(I, locker) - - //so they black out before warping - M.Paralyse(5) - sleep(5) - if(!M) return - - M.loc = prison_cell - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - - to_chat(M, "You have been sent to the prison station!") - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) - - else if(href_list["sendbacktolobby"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["sendbacktolobby"]) - if(!isobserver(M)) - to_chat(usr, "You can only send ghost players back to the Lobby.") - return - - if(!M.client) - to_chat(usr, "[M] doesn't seem to have an active client.") - return - - if(tgui_alert(usr, "Send [key_name(M)] back to Lobby?", "Message", list("Yes", "No")) != "Yes") - return - - log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") - message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") - - var/mob/new_player/NP = new() - NP.ckey = M.ckey - qdel(M) - - else if(href_list["tdome1"]) - if(!check_rights(R_FUN)) return - - if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") - return - - var/mob/M = locate(href_list["tdome1"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.drop_from_inventory(I) - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdome1) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) - - else if(href_list["tdome2"]) - if(!check_rights(R_FUN)) return - - if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") - return - - var/mob/M = locate(href_list["tdome2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.drop_from_inventory(I) - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdome2) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) - - else if(href_list["tdomeadmin"]) - if(!check_rights(R_FUN)) return - - if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") - return - - var/mob/M = locate(href_list["tdomeadmin"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdomeadmin) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) - - else if(href_list["tdomeobserve"]) - if(!check_rights(R_FUN)) return - - if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") - return - - var/mob/M = locate(href_list["tdomeobserve"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.drop_from_inventory(I) - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/observer = M - observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), slot_w_uniform) - observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(observer), slot_shoes) - M.Paralyse(5) - sleep(5) - M.loc = pick(tdomeobserve) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) - - else if(href_list["revive"]) - if(!check_rights(R_REJUVINATE)) return - - var/mob/living/L = locate(href_list["revive"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - - if(config.allow_admin_rev) - L.revive() - message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!", 1) - log_admin("[key_name(usr)] healed / Rrvived [key_name(L)]") - else - to_chat(usr, "Admin Rejuvinates have been disabled") - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locate(href_list["makeai"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1) - log_admin("[key_name(usr)] AIized [key_name(H)]") - H.AIize() - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locate(href_list["makealien"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - usr.client.cmd_admin_alienize(H) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locate(href_list["makerobot"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - usr.client.cmd_admin_robotize(H) - - else if(href_list["makeanimal"]) - if(!check_rights(R_SPAWN)) return - - var/mob/M = locate(href_list["makeanimal"]) - if(istype(M, /mob/new_player)) - to_chat(usr, "This cannot be used on instances of type /mob/new_player") - return - - usr.client.cmd_admin_animalize(M) - - else if(href_list["respawn"]) - if(!check_rights(R_SPAWN)) - return - - var/client/C = locate(href_list["respawn"]) - if(!istype(C)) - return - usr.client.respawn_character_proper(C) - - else if(href_list["togmutate"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locate(href_list["togmutate"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/block=text2num(href_list["block"]) - usr.client.cmd_admin_toggle_block(H,block) - show_player_panel(H) - - else if(href_list["adminplayeropts"]) - var/mob/M = locate(href_list["adminplayeropts"]) - show_player_panel(M) - - else if(href_list["adminplayerobservejump"]) - if(!check_rights(R_MOD|R_ADMIN|R_SERVER)) return //VOREStation Edit - - var/mob/M = locate(href_list["adminplayerobservejump"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - sleep(2) - C.do_jumptomob(M) - - else if(href_list["adminplayerobservefollow"]) - if(!check_rights(R_MOD|R_ADMIN|R_SERVER)) //VOREStation Edit - return - - var/mob/M = locate(href_list["adminplayerobservefollow"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - var/mob/observer/dead/G = C.mob - sleep(2) - G.ManualFollow(M) - - else if(href_list["check_antagonist"]) - check_antagonists() - - else if(href_list["take_question"]) - - var/mob/M = locate(href_list["take_question"]) - if(ismob(M)) - var/take_msg = "ADMINHELP: [key_name(usr.client)] is attending to [key_name(M)]'s adminhelp, please don't dogpile them." - for(var/client/X in GLOB.admins) - if((R_ADMIN|R_MOD|R_SERVER) & X.holder.rights) //VOREStation Edit - to_chat(X, take_msg) - to_chat(M, "Your adminhelp is being attended to by [usr.client]. Thanks for your patience!") - // VoreStation Edit Start - if (config.chat_webhook_url) - spawn(0) - var/query_string = "type=admintake" - query_string += "&key=[url_encode(config.chat_webhook_key)]" - query_string += "&admin=[url_encode(key_name(usr.client))]" - query_string += "&user=[url_encode(key_name(M))]" - world.Export("[config.chat_webhook_url]?[query_string]") - // VoreStation Edit End - else - to_chat(usr, "Unable to locate mob.") - - else if(href_list["adminplayerobservecoodjump"]) - if(!check_rights(R_ADMIN|R_SERVER|R_MOD)) return //VOREStation Edit - - var/x = text2num(href_list["X"]) - var/y = text2num(href_list["Y"]) - var/z = text2num(href_list["Z"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - sleep(2) - C.jumptocoord(x,y,z) - - else if(href_list["adminchecklaws"]) - output_ai_laws() - - else if(href_list["adminmoreinfo"]) - var/mob/M = locate(href_list["adminmoreinfo"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/location_description = "" - var/special_role_description = "" - var/health_description = "" - var/gender_description = "" - var/turf/T = get_turf(M) - - //Location - if(isturf(T)) - if(isarea(T.loc)) - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" - else - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" - - //Job + antagonist - if(M.mind) - special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]" - else - special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;" - - //Health - if(isliving(M)) - var/mob/living/L = M - var/status - switch (M.stat) - if (0) status = "Alive" - if (1) status = "Unconscious" - if (2) status = "Dead" - health_description = "Status = [status]" - health_description += "
                    Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]" - else - health_description = "This mob type has no health to speak of." - - //Gener - switch(M.gender) - if(MALE,FEMALE) gender_description = "[M.gender]" - else gender_description = "[M.gender]" - - to_chat(src.owner, "Info about [M.name]:
                    \ - Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]
                    \ - Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];
                    \ - Location = [location_description];
                    \ - [special_role_description]
                    \ - (PM) (PP) (VV) \ - (SM) ([admin_jump_link(M, src)]) (CA)
                    ") - - else if(href_list["adminspawncookie"]) - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - var/mob/living/carbon/human/H = locate(href_list["adminspawncookie"]) - if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - H.equip_to_slot_or_del( new /obj/item/weapon/reagent_containers/food/snacks/cookie(H), slot_l_hand ) - if(!(istype(H.l_hand,/obj/item/weapon/reagent_containers/food/snacks/cookie))) - H.equip_to_slot_or_del( new /obj/item/weapon/reagent_containers/food/snacks/cookie(H), slot_r_hand ) - if(!(istype(H.r_hand,/obj/item/weapon/reagent_containers/food/snacks/cookie))) - log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") - message_admins("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") - return - else - H.update_inv_r_hand()//To ensure the icon appears in the HUD - else - H.update_inv_l_hand() - log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") - message_admins("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") - feedback_inc("admin_cookies_spawned",1) - to_chat(H, "Your prayers have been answered!! You received the best cookie!") - - else if(href_list["adminsmite"]) - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) - if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - owner.smite(H) - - else if(href_list["BlueSpaceArtillery"]) - if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return - - var/mob/living/M = locate(href_list["BlueSpaceArtillery"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - - if(tgui_alert(src.owner, "Are you sure you wish to hit [key_name(M)] with Blue Space Artillery?", "Confirm Firing?", list("Yes", "No")) != "Yes") - return - - bluespace_artillery(M,src) - - else if(href_list["CentComReply"]) - var/mob/living/L = locate(href_list["CentComReply"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living/") - return - - if(L.can_centcom_reply()) - var/input = sanitize(tgui_input_text(src.owner, "Please enter a message to reply to [key_name(L)] via their headset.","Outgoing message from CentCom", "")) - if(!input) return - - to_chat(src.owner, "You sent [input] to [L] via a secure channel.") - log_admin("[src.owner] replied to [key_name(L)]'s CentCom message with the message [input].") - message_admins("[src.owner] replied to [key_name(L)]'s CentCom message with: \"[input]\"") - if(!isAI(L)) - to_chat(L, "You hear something crackle in your headset for a moment before a voice speaks.") - to_chat(L, "Please stand by for a message from Central Command.") - to_chat(L, "Message as follows.") - to_chat(L, "[input]") - to_chat(L, "Message ends.") - else - to_chat(src.owner, "The person you are trying to contact does not have functional radio equipment.") - - - else if(href_list["SyndicateReply"]) - var/mob/living/carbon/human/H = locate(href_list["SyndicateReply"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!istype(H.l_ear, /obj/item/device/radio/headset) && !istype(H.r_ear, /obj/item/device/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - var/input = sanitize(tgui_input_text(src.owner, "Please enter a message to reply to [key_name(H)] via their headset.","Outgoing message from a shadowy figure...", "")) - if(!input) return - - to_chat(src.owner, "You sent [input] to [H] via a secure channel.") - log_admin("[src.owner] replied to [key_name(H)]'s illegal message with the message [input].") - to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \ - \"Please stand by for a message from your benefactor. Message as follows, agent. \"[input]\" Message ends.\"") - - else if(href_list["AdminFaxView"]) - var/obj/item/fax = locate(href_list["AdminFaxView"]) - if (istype(fax, /obj/item/weapon/paper)) - var/obj/item/weapon/paper/P = fax - P.show_content(usr,1) - else if (istype(fax, /obj/item/weapon/photo)) - var/obj/item/weapon/photo/H = fax - H.show(usr) - else if (istype(fax, /obj/item/weapon/paper_bundle)) - //having multiple people turning pages on a paper_bundle can cause issues - //open a browse window listing the contents instead - var/data = "" - var/obj/item/weapon/paper_bundle/B = fax - - for (var/page = 1, page <= B.pages.len, page++) - var/obj/pageobj = B.pages[page] - data += "Page [page] - [pageobj.name]
                    " - - usr << browse(data, "window=[B.name]") - else - to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") - - else if (href_list["AdminFaxViewPage"]) - var/page = text2num(href_list["AdminFaxViewPage"]) - var/obj/item/weapon/paper_bundle/bundle = locate(href_list["paper_bundle"]) - - if (!bundle) return - - if (istype(bundle.pages[page], /obj/item/weapon/paper)) - var/obj/item/weapon/paper/P = bundle.pages[page] - P.show_content(src.owner, 1) - else if (istype(bundle.pages[page], /obj/item/weapon/photo)) - var/obj/item/weapon/photo/H = bundle.pages[page] - H.show(src.owner) - return - - else if(href_list["FaxReply"]) - var/mob/sender = locate(href_list["FaxReply"]) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - var/replyorigin = href_list["replyorigin"] - - - var/obj/item/weapon/paper/admin/P = new /obj/item/weapon/paper/admin( null ) //hopefully the null loc won't cause trouble for us - faxreply = P - - P.admindatum = src - P.origin = replyorigin - P.destination = fax - P.sender = sender - - P.adminbrowse() - - else if(href_list["jumpto"]) - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) - return - if(!config.allow_admin_jump) - tgui_alert_async(usr, "Admin jumping disabled") - return - - var/mob/M = locate(href_list["jumpto"]) - if(!M) - return - - var/turf/T = get_turf(M) - if(isturf(T)) - usr.on_mob_jump() - usr.forceMove(T) - feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_and_message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]") - else - to_chat(usr, "This mob is not located in the game world.") - - else if(href_list["getmob"]) - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) - return - if(!config.allow_admin_jump) - tgui_alert_async(usr, "Admin jumping disabled") - return - if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") - return - - var/mob/M = locate(href_list["getmob"]) - if(!M) - return - M.on_mob_jump() - M.forceMove(get_turf(usr)) - var/msg = "[key_name_admin(usr)] jumped [key_name_admin(M)] to them" - log_and_message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else if(href_list["sendmob"]) - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) - return - if(!config.allow_admin_jump) - tgui_alert_async(usr, "Admin jumping disabled") - return - - var/mob/M = locate(href_list["sendmob"]) - if(!M) - return - - var/list/areachoices = return_sorted_areas() - var/choice = tgui_input_list(usr, "Pick an area:", "Send Mob", areachoices) - if(!choice) - return - - var/area/A = areachoices[choice] - if(!A) - return - - M.on_mob_jump() - M.forceMove(pick(get_area_turfs(A))) - var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]" - log_and_message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else if(href_list["narrateto"]) - if(!check_rights(R_ADMIN|R_EVENT|R_FUN)) return - - var/mob/M = locate(href_list["narrateto"]) - usr.client.cmd_admin_direct_narrate(M) - - else if(href_list["subtlemessage"]) - if(!check_rights(R_MOD|R_ADMIN|R_EVENT|R_FUN,0)) return - - var/mob/M = locate(href_list["subtlemessage"]) - usr.client.cmd_admin_subtle_message(M) - - else if(href_list["traitor"]) - if(!check_rights(R_ADMIN|R_MOD|R_EVENT)) return - - if(!ticker || !ticker.mode) - tgui_alert_async(usr, "The game hasn't started yet!") - return - - var/mob/M = locate(href_list["traitor"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - show_traitor_panel(M) - - else if(href_list["create_object"]) - if(!check_rights(R_SPAWN)) return - return create_object(usr) - - else if(href_list["quick_create_object"]) - if(!check_rights(R_SPAWN)) return - return quick_create_object(usr) - - else if(href_list["create_turf"]) - if(!check_rights(R_SPAWN)) return - return create_turf(usr) - - else if(href_list["create_mob"]) - if(!check_rights(R_SPAWN)) return - return create_mob(usr) - - else if(href_list["object_list"]) //this is the laggiest thing ever - if(!check_rights(R_SPAWN)) return - - if(!config.allow_admin_spawning) - to_chat(usr, "Spawning of items is not allowed.") - return - - var/atom/loc = usr.loc - - var/dirty_paths - if (istext(href_list["object_list"])) - dirty_paths = list(href_list["object_list"]) - else if (istype(href_list["object_list"], /list)) - dirty_paths = href_list["object_list"] - - var/paths = list() - var/removed_paths = list() - - for(var/dirty_path in dirty_paths) - var/path = text2path(dirty_path) - if(!path) - removed_paths += dirty_path - continue - else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) - removed_paths += dirty_path - continue - else if(ispath(path, /obj/item/weapon/gun/energy/pulse_rifle)) - if(!check_rights(R_FUN,0)) - removed_paths += dirty_path - continue - else if(ispath(path, /obj/item/weapon/melee/energy/blade))//Not an item one should be able to spawn./N - if(!check_rights(R_FUN,0)) - removed_paths += dirty_path - continue - else if(ispath(path, /obj/effect/bhole)) - if(!check_rights(R_FUN,0)) - removed_paths += dirty_path - continue - paths += path - - if(!paths) - tgui_alert(usr, "The path list you sent is empty") - return - if(length(paths) > 5) - tgui_alert_async(usr, "Select fewer object types, (max 5)") - return - else if(length(removed_paths)) - tgui_alert_async(usr, "Removed:\n" + jointext(removed_paths, "\n")) - - var/list/offset = splittext(href_list["offset"],",") - var/number = dd_range(1, 100, text2num(href_list["object_count"])) - var/X = offset.len > 0 ? text2num(offset[1]) : 0 - var/Y = offset.len > 1 ? text2num(offset[2]) : 0 - var/Z = offset.len > 2 ? text2num(offset[3]) : 0 - var/tmp_dir = href_list["object_dir"] - var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2 - if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10))) - obj_dir = 2 - var/obj_name = sanitize(href_list["object_name"]) - var/where = href_list["object_where"] - if (!( where in list("onfloor","inhand","inmarked") )) - where = "onfloor" - - if( where == "inhand" ) - to_chat(usr, "Support for inhand not available yet. Will spawn on floor.") - where = "onfloor" - - if ( where == "inhand" ) //Can only give when human or monkey - if ( !( ishuman(usr) || issmall(usr) ) ) - to_chat(usr, "Can only spawn in hand when you're a human or a monkey.") - where = "onfloor" - else if ( usr.get_active_hand() ) - to_chat(usr, "Your active hand is full. Spawning on floor.") - where = "onfloor" - - if ( where == "inmarked" ) - if ( !marked_datum ) - to_chat(usr, "You don't have any object marked. Abandoning spawn.") - return - else - if ( !istype(marked_datum,/atom) ) - to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") - return - - var/atom/target //Where the object will be spawned - switch ( where ) - if ( "onfloor" ) - switch (href_list["offset_type"]) - if ("absolute") - target = locate(0 + X,0 + Y,0 + Z) - if ("relative") - target = locate(loc.x + X,loc.y + Y,loc.z + Z) - if ( "inmarked" ) - target = marked_datum - - if(target) - for (var/path in paths) - for (var/i = 0; i < number; i++) - if(path in typesof(/turf)) - var/turf/O = target - var/turf/N = O.ChangeTurf(path) - if(N) - if(obj_name) - N.name = obj_name - else - var/atom/O = new path(target) - if(O) - O.set_dir(obj_dir) - if(obj_name) - O.name = obj_name - if(istype(O,/mob)) - var/mob/M = O - M.real_name = obj_name - - log_and_message_admins("created [number] [english_list(paths)]") - return - - else if(href_list["admin_secrets_panel"]) - var/datum/admin_secret_category/AC = locate(href_list["admin_secrets_panel"]) in admin_secrets.categories - src.Secrets(AC) - - else if(href_list["admin_secrets"]) - var/datum/admin_secret_item/item = locate(href_list["admin_secrets"]) in admin_secrets.items - item.execute(usr) - - else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here - src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. - src.access_news_network() - - else if(href_list["ac_set_channel_name"]) - src.admincaster_feed_channel.channel_name = sanitizeSafe(tgui_input_text(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) - src.access_news_network() - - else if(href_list["ac_set_channel_lock"]) - src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked - src.access_news_network() - - else if(href_list["ac_submit_new_channel"]) - var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) - src.admincaster_screen=7 - else - var/choice = tgui_alert(usr, "Please confirm Feed channel creation","Network Channel Handler",list("Confirm","Cancel")) - if(choice=="Confirm") - news_network.CreateFeedChannel(admincaster_feed_channel.channel_name, admincaster_signature, admincaster_feed_channel.locked, 1) - feedback_inc("newscaster_channels",1) //Adding channel to the global network - log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") - src.admincaster_screen=5 - src.access_news_network() - - else if(href_list["ac_set_channel_receiving"]) - var/list/available_channels = list() - for(var/datum/feed_channel/F in news_network.network_channels) - available_channels += F.channel_name - src.admincaster_feed_channel.channel_name = sanitizeSafe(tgui_input_list(usr, "Choose receiving Feed Channel", "Network Channel Handler", available_channels )) - src.access_news_network() - - else if(href_list["ac_set_new_title"]) - src.admincaster_feed_message.title = sanitize(tgui_input_text(usr, "Enter the Feed title", "Network Channel Handler", "")) - src.access_news_network() - - else if(href_list["ac_set_new_message"]) - src.admincaster_feed_message.body = sanitize(tgui_input_text(usr, "Write your Feed story", "Network Channel Handler", "", multiline = TRUE, prevent_enter = TRUE)) - src.access_news_network() - - else if(href_list["ac_submit_new_message"]) - if(src.admincaster_feed_message.body =="" || admincaster_feed_message.title == "" || admincaster_feed_message.body =="\[REDACTED\]" || admincaster_feed_channel.channel_name == "" ) - src.admincaster_screen = 6 - else - feedback_inc("newscaster_stories",1) - news_network.SubmitArticle(admincaster_feed_message.body, admincaster_signature, admincaster_feed_channel.channel_name, null, 1, "", admincaster_feed_message.title) - src.admincaster_screen=4 - - log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") - src.access_news_network() - - else if(href_list["ac_create_channel"]) - src.admincaster_screen=2 - src.access_news_network() - - else if(href_list["ac_create_feed_story"]) - src.admincaster_screen=3 - src.access_news_network() - - else if(href_list["ac_menu_censor_story"]) - src.admincaster_screen=10 - src.access_news_network() - - else if(href_list["ac_menu_censor_channel"]) - src.admincaster_screen=11 - src.access_news_network() - - else if(href_list["ac_menu_wanted"]) - var/already_wanted = 0 - if(news_network.wanted_issue) - already_wanted = 1 - - if(already_wanted) - src.admincaster_feed_message.author = news_network.wanted_issue.author - src.admincaster_feed_message.body = news_network.wanted_issue.body - src.admincaster_screen = 14 - src.access_news_network() - - else if(href_list["ac_set_wanted_name"]) - src.admincaster_feed_message.author = sanitize(tgui_input_text(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) - src.access_news_network() - - else if(href_list["ac_set_wanted_desc"]) - src.admincaster_feed_message.body = sanitize(tgui_input_text(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) - src.access_news_network() - - else if(href_list["ac_submit_wanted"]) - var/input_param = text2num(href_list["ac_submit_wanted"]) - if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") - src.admincaster_screen = 16 - else - var/choice = tgui_alert(usr, "Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler",list("Confirm","Cancel")) - if(choice=="Confirm") - if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below - var/datum/feed_message/WANTED = new /datum/feed_message - WANTED.author = src.admincaster_feed_message.author //Wanted name - WANTED.body = src.admincaster_feed_message.body //Wanted desc - WANTED.backup_author = src.admincaster_signature //Submitted by - WANTED.is_admin_message = 1 - news_network.wanted_issue = WANTED - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.newsAlert() - NEWSCASTER.update_icon() - src.admincaster_screen = 15 - else - news_network.wanted_issue.author = src.admincaster_feed_message.author - news_network.wanted_issue.body = src.admincaster_feed_message.body - news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author - src.admincaster_screen = 19 - log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") - src.access_news_network() - - else if(href_list["ac_cancel_wanted"]) - var/choice = tgui_alert(usr, "Please confirm Wanted Issue removal","Network Security Handler",list("Confirm","Cancel")) - if(choice=="Confirm") - news_network.wanted_issue = null - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.update_icon() - src.admincaster_screen=17 - src.access_news_network() - - else if(href_list["ac_censor_channel_author"]) - var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) - if(FC.author != "\[REDACTED\]") - FC.backup_author = FC.author - FC.author = "\[REDACTED\]" - else - FC.author = FC.backup_author - src.access_news_network() - - else if(href_list["ac_censor_channel_story_author"]) - var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) - if(MSG.author != "\[REDACTED\]") - MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" - else - MSG.author = MSG.backup_author - src.access_news_network() - - else if(href_list["ac_censor_channel_story_body"]) - var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) - if(MSG.body != "\[REDACTED\]") - MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" - else - MSG.body = MSG.backup_body - src.access_news_network() - - else if(href_list["ac_pick_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) - src.admincaster_feed_channel = FC - src.admincaster_screen=13 - src.access_news_network() - - else if(href_list["ac_toggle_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) - FC.censored = !FC.censored - src.access_news_network() - - else if(href_list["ac_view"]) - src.admincaster_screen=1 - src.access_news_network() - - else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ - src.admincaster_screen = text2num(href_list["ac_setScreen"]) - if (src.admincaster_screen == 0) - if(src.admincaster_feed_channel) - src.admincaster_feed_channel = new /datum/feed_channel - if(src.admincaster_feed_message) - src.admincaster_feed_message = new /datum/feed_message - src.access_news_network() - - else if(href_list["ac_show_channel"]) - var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 9 - src.access_news_network() - - else if(href_list["ac_pick_censor_channel"]) - var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 12 - src.access_news_network() - - else if(href_list["ac_refresh"]) - src.access_news_network() - - else if(href_list["ac_set_signature"]) - src.admincaster_signature = sanitize(tgui_input_text(usr, "Provide your desired signature", "Network Identity Handler", "")) - src.access_news_network() - - else if(href_list["populate_inactive_customitems"]) - if(check_rights(R_ADMIN|R_SERVER)) - populate_inactive_customitems_list(src.owner) - - else if(href_list["vsc"]) - if(check_rights(R_ADMIN|R_SERVER|R_EVENT)) - if(href_list["vsc"] == "airflow") - vsc.ChangeSettingsDialog(usr,vsc.settings) - if(href_list["vsc"] == "phoron") - vsc.ChangeSettingsDialog(usr,vsc.plc.settings) - if(href_list["vsc"] == "default") - vsc.SetDefault(usr) - - else if(href_list["toglang"]) - if(check_rights(R_SPAWN)) //VOREStation Edit - var/mob/M = locate(href_list["toglang"]) - if(!istype(M)) - to_chat(usr, "[M] is illegal type, must be /mob!") - return - var/lang2toggle = href_list["lang"] - var/datum/language/L = GLOB.all_languages[lang2toggle] - - if(L in M.languages) - if(!M.remove_language(lang2toggle)) - to_chat(usr, "Failed to remove language '[lang2toggle]' from \the [M]!") - else - if(!M.add_language(lang2toggle)) - to_chat(usr, "Failed to add language '[lang2toggle]' from \the [M]!") - - show_player_panel(M) - - else if(href_list["cryoplayer"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/carbon/M = locate(href_list["cryoplayer"]) //VOREStation edit from just an all mob check to mob/living/carbon - if(!istype(M)) - to_chat(usr, "Mob doesn't exist!") - return - - var/client/C = usr.client - C.despawn_player(M) - - // player info stuff - - if(href_list["notes"]) - var/ckey = href_list["ckey"] - if(!ckey) - var/mob/M = locate(href_list["mob"]) - if(ismob(M)) - ckey = M.ckey - - switch(href_list["notes"]) - if("show") - var/datum/tgui_module/player_notes_info/A = new(src) - A.key = ckey - A.tgui_interact(usr) - if("list") - var/filter - if(href_list["filter"] && href_list["filter"] != "0") - filter = url_decode(href_list["filter"]) - PlayerNotesPage(text2num(href_list["index"]), filter) - if("filter") - PlayerNotesFilter() - return - -/mob/living/proc/can_centcom_reply() - return 0 - -/mob/living/carbon/human/can_centcom_reply() - return istype(l_ear, /obj/item/device/radio/headset) || istype(r_ear, /obj/item/device/radio/headset) - -/mob/living/silicon/ai/can_centcom_reply() - return common_radio != null && !check_unable(2) - -/atom/proc/extra_admin_link() - return - -/mob/extra_admin_link(var/source) - if(client && eyeobj) - return "|EYE" - -/mob/observer/dead/extra_admin_link(var/source) - if(mind && mind.current) - return "|BDY" - -/proc/admin_jump_link(var/atom/target, var/source) - if(!target) return - // The way admin jump links handle their src is weirdly inconsistent... - if(istype(source, /datum/admins)) - source = "src=\ref[source]" - else - source = "_src_=holder" - - . = "JMP" - . += target.extra_admin_link(source) +/datum/admins/proc/CheckAdminHref(href, href_list) + var/auth = href_list["admin_token"] + . = auth && (auth == href_token || auth == GLOB.href_token) + if(.) + return + var/msg = !auth ? "no" : "a bad" + message_admins("[key_name_admin(usr)] clicked an href with [msg] authorization key!") + + var/debug_admin_hrefs = TRUE // Remove once everything is converted over + if(debug_admin_hrefs) + message_admins("Debug mode enabled, call not blocked. Please ask your coders to review this round's logs.") + log_world("UAH: [href]") + return TRUE + + log_admin("[key_name(usr)] clicked an href with [msg] authorization key! [href]") + +/datum/admins/Topic(href, href_list) + ..() + + if(usr.client != src.owner || !check_rights(0)) + log_admin("[key_name(usr)] tried to use the admin panel without authorization.") + message_admins("[usr.key] has attempted to override the admin panel!") + return + + if(!CheckAdminHref(href, href_list)) + return + + if(ticker.mode && ticker.mode.check_antagonists_topic(href, href_list)) + check_antagonists() + return + + if(href_list["ahelp"]) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) + return + + var/ahelp_ref = href_list["ahelp"] + var/datum/admin_help/AH = locate(ahelp_ref) + if(AH) + AH.Action(href_list["ahelp_action"]) + else + to_chat(usr, "Ticket [ahelp_ref] has been deleted!") + + else if(href_list["ahelp_tickets"]) + GLOB.ahelp_tickets.BrowseTickets(text2num(href_list["ahelp_tickets"])) + + mentor_commands(href, href_list, src) + + if(href_list["dbsearchckey"] || href_list["dbsearchadmin"]) + + var/adminckey = href_list["dbsearchadmin"] + var/playerckey = href_list["dbsearchckey"] + var/playerip = href_list["dbsearchip"] + var/playercid = href_list["dbsearchcid"] + var/dbbantype = text2num(href_list["dbsearchbantype"]) + var/match = 0 + + if("dbmatch" in href_list) + match = 1 + + DB_ban_panel(playerckey, adminckey, playerip, playercid, dbbantype, match) + return + + else if(href_list["dbbanedit"]) + var/banedit = href_list["dbbanedit"] + var/banid = text2num(href_list["dbbanid"]) + if(!banedit || !banid) + return + + DB_ban_edit(banid, banedit) + return + + else if(href_list["dbbanaddtype"]) + + var/bantype = text2num(href_list["dbbanaddtype"]) + var/banckey = href_list["dbbanaddckey"] + var/banip = href_list["dbbanaddip"] + var/bancid = href_list["dbbanaddcid"] + var/banduration = text2num(href_list["dbbaddduration"]) + var/banjob = href_list["dbbanaddjob"] + var/banreason = href_list["dbbanreason"] + + banckey = ckey(banckey) + + switch(bantype) + if(BANTYPE_PERMA) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_TEMP) + if(!banckey || !banreason || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + return + banjob = null + if(BANTYPE_JOB_PERMA) + if(!banckey || !banreason || !banjob) + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + return + banduration = null + if(BANTYPE_JOB_TEMP) + if(!banckey || !banreason || !banjob || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + return + + var/mob/playermob + + for(var/mob/M in player_list) + if(M.ckey == banckey) + playermob = M + break + + + banreason = "(MANUAL BAN) "+banreason + + if(!playermob) + if(banip) + banreason = "[banreason] (CUSTOM IP)" + if(bancid) + banreason = "[banreason] (CUSTOM CID)" + else + message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom ip and computer id fields replaced with the ip and computer id from the located mob") + notes_add(banckey,banreason,usr) + + DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) + if((bantype == BANTYPE_PERMA || bantype == BANTYPE_TEMP) && playermob.client) + qdel(playermob.client) + + else if(href_list["editrights"]) + if(!check_rights(R_PERMISSIONS)) + message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") + log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") + return + + var/adm_ckey + + var/task = href_list["editrights"] + if(task == "add") + var/new_ckey = ckey(tgui_input_text(usr,"New admin's ckey","Admin ckey", null)) + if(!new_ckey) return + if(new_ckey in admin_datums) + to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") + return + adm_ckey = new_ckey + task = "rank" + else if(task != "show") + adm_ckey = ckey(href_list["ckey"]) + if(!adm_ckey) + to_chat(usr, "Error: Topic 'editrights': No valid ckey") + return + + var/datum/admins/D = admin_datums[adm_ckey] + + if(task == "remove") + if(tgui_alert(usr, "Are you sure you want to remove [adm_ckey]?","Message",list("Yes","Cancel")) == "Yes") + if(!D) return + admin_datums -= adm_ckey + D.disassociate() + + message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") + log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") + log_admin_rank_modification(adm_ckey, "Removed") + + else if(task == "rank") + var/new_rank + if(admin_ranks.len) + new_rank = tgui_input_list(usr, "Please select a rank", "New rank", (admin_ranks|"*New Rank*")) + else + new_rank = tgui_input_list(usr, "Please select a rank", "New rank", list("Game Master","Game Admin", "Trial Admin", "Admin Observer","*New Rank*")) + + var/rights = 0 + if(D) + rights = D.rights + switch(new_rank) + if(null,"") return + if("*New Rank*") + new_rank = tgui_input_text(usr, "Please input a new rank", "New custom rank") + if(config.admin_legacy_system) + new_rank = ckeyEx(new_rank) + if(!new_rank) + to_chat(usr, "Error: Topic 'editrights': Invalid rank") + return + if(config.admin_legacy_system) + if(admin_ranks.len) + if(new_rank in admin_ranks) + rights = admin_ranks[new_rank] //we typed a rank which already exists, use its rights + else + admin_ranks[new_rank] = 0 //add the new rank to admin_ranks + else + if(config.admin_legacy_system) + new_rank = ckeyEx(new_rank) + rights = admin_ranks[new_rank] //we input an existing rank, use its rights + + if(D) + D.disassociate() //remove adminverbs and unlink from client + D.rank = new_rank //update the rank + D.rights = rights //update the rights based on admin_ranks (default: 0) + else + D = new /datum/admins(new_rank, rights, adm_ckey) + + var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) + D.associate(C) //link up with the client and add verbs + + message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin_rank_modification(adm_ckey, new_rank) + + else if(task == "permissions") + if(!D) return + var/list/permissionlist = list() + for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift + permissionlist[rights2text(i)] = i + var/new_permission = tgui_input_list(usr, "Select a permission to turn on/off", "Permission toggle", permissionlist) + if(!new_permission) return + D.rights ^= permissionlist[new_permission] + + message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey]") + log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey]") + log_admin_permission_modification(adm_ckey, permissionlist[new_permission]) + + edit_admin_permissions() + + else if(href_list["call_shuttle"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + if( ticker.mode.name == "blob" ) + tgui_alert_async(usr, "You can't call the shuttle during blob!") + return + + switch(href_list["call_shuttle"]) + if("1") + if ((!( ticker ) || !emergency_shuttle.location())) + return + if (emergency_shuttle.can_call()) + emergency_shuttle.call_evac() + log_admin("[key_name(usr)] called the Emergency Shuttle") + message_admins(span_blue("[key_name_admin(usr)] called the Emergency Shuttle to the station."), 1) + + if("2") + if (!( ticker ) || !emergency_shuttle.location()) + return + if (emergency_shuttle.can_call()) + emergency_shuttle.call_evac() + log_admin("[key_name(usr)] called the Emergency Shuttle") + message_admins(span_blue("[key_name_admin(usr)] called the Emergency Shuttle to the station."), 1) + + else if (emergency_shuttle.can_recall()) + emergency_shuttle.recall() + log_admin("[key_name(usr)] sent the Emergency Shuttle back") + message_admins(span_blue("[key_name_admin(usr)] sent the Emergency Shuttle back."), 1) + + href_list["secretsadmin"] = "check_antagonist" + + else if(href_list["edit_shuttle_time"]) + if(!check_rights(R_SERVER)) return + + if (emergency_shuttle.wait_for_launch) + var/new_time_left = tgui_input_number(usr, "Enter new shuttle launch countdown (seconds):","Edit Shuttle Launch Time", emergency_shuttle.estimate_launch_time() ) + + emergency_shuttle.launch_time = world.time + new_time_left*10 + + log_admin("[key_name(usr)] edited the Emergency Shuttle's launch time to [new_time_left]") + message_admins(span_blue("[key_name_admin(usr)] edited the Emergency Shuttle's launch time to [new_time_left*10]"), 1) + else if (emergency_shuttle.shuttle.has_arrive_time()) + + var/new_time_left = tgui_input_number(usr, "Enter new shuttle arrival time (seconds):","Edit Shuttle Arrival Time", emergency_shuttle.estimate_arrival_time() ) + emergency_shuttle.shuttle.arrive_time = world.time + new_time_left*10 + + log_admin("[key_name(usr)] edited the Emergency Shuttle's arrival time to [new_time_left]") + message_admins(span_blue("[key_name_admin(usr)] edited the Emergency Shuttle's arrival time to [new_time_left*10]"), 1) + else + tgui_alert_async(usr, "The shuttle is neither counting down to launch nor is it in transit. Please try again when it is.") + + href_list["secretsadmin"] = "check_antagonist" + + else if(href_list["delay_round_end"]) + if(!check_rights(R_SERVER)) return //VOREStation Edit + + ticker.delay_end = !ticker.delay_end + log_admin("[key_name(usr)] [ticker.delay_end ? "delayed the round end" : "has made the round end normally"].") + message_admins(span_blue("[key_name(usr)] [ticker.delay_end ? "delayed the round end" : "has made the round end normally"]."), 1) + href_list["secretsadmin"] = "check_antagonist" + + else if(href_list["simplemake"]) + + if(!check_rights(R_SPAWN)) return + + var/mob/M = locate(href_list["mob"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/delmob = 0 + switch(tgui_alert(usr, "Delete old mob?","Message",list("Yes","No","Cancel"))) + if("Cancel") return + if("Yes") delmob = 1 + + log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]") + message_admins(span_blue("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]"), 1) + + switch(href_list["simplemake"]) + if("observer") M.change_mob_type( /mob/observer/dead , null, null, delmob ) + if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob ) + if("nymph") M.change_mob_type( /mob/living/carbon/alien/diona , null, null, delmob ) + if("human") M.change_mob_type( /mob/living/carbon/human , null, null, delmob, href_list["species"]) + if("slime") M.change_mob_type( /mob/living/simple_mob/slime/xenobio , null, null, delmob ) + if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob ) + if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) + if("cat") M.change_mob_type( /mob/living/simple_mob/animal/passive/cat , null, null, delmob ) + if("runtime") M.change_mob_type( /mob/living/simple_mob/animal/passive/cat/runtime , null, null, delmob ) + if("corgi") M.change_mob_type( /mob/living/simple_mob/animal/passive/dog/corgi , null, null, delmob ) + if("ian") M.change_mob_type( /mob/living/simple_mob/animal/passive/dog/corgi/Ian , null, null, delmob ) + if("crab") M.change_mob_type( /mob/living/simple_mob/animal/passive/crab , null, null, delmob ) + if("coffee") M.change_mob_type( /mob/living/simple_mob/animal/passive/crab/Coffee , null, null, delmob ) + if("parrot") M.change_mob_type( /mob/living/simple_mob/animal/passive/bird/parrot , null, null, delmob ) + if("polyparrot") M.change_mob_type( /mob/living/simple_mob/animal/passive/bird/parrot/poly , null, null, delmob ) + if("constructarmoured") M.change_mob_type( /mob/living/simple_mob/construct/juggernaut , null, null, delmob ) + if("constructbuilder") M.change_mob_type( /mob/living/simple_mob/construct/artificer , null, null, delmob ) + if("constructwraith") M.change_mob_type( /mob/living/simple_mob/construct/wraith , null, null, delmob ) + if("shade") M.change_mob_type( /mob/living/simple_mob/construct/shade , null, null, delmob ) + + + /////////////////////////////////////new ban stuff + else if(href_list["unbanf"]) + if(!check_rights(R_BAN)) return + + var/banfolder = href_list["unbanf"] + Banlist.cd = "/base/[banfolder]" + var/key = Banlist["key"] + if(tgui_alert(usr, "Are you sure you want to unban [key]?", "Confirmation", list("Yes", "No")) == "Yes") + if(RemoveBan(banfolder)) + unbanpanel() + else + tgui_alert_async(usr, "This ban has already been lifted / does not exist.", "Error") + unbanpanel() + + else if(href_list["warn"]) + usr.client.warn(href_list["warn"]) + + else if(href_list["unbane"]) + if(!check_rights(R_BAN)) return + + UpdateTime() + var/reason + + var/banfolder = href_list["unbane"] + Banlist.cd = "/base/[banfolder]" + var/reason2 = Banlist["reason"] + var/temp = Banlist["temp"] + + var/minutes = Banlist["minutes"] + + var/banned_key = Banlist["key"] + Banlist.cd = "/base" + + var/duration + + switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No"))) + if("Yes") + temp = 1 + var/mins = 0 + if(minutes > CMinutes) + mins = minutes - CMinutes + mins = tgui_input_number(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) + if(!mins) return + mins = min(525599,mins) + minutes = CMinutes + mins + duration = GetExp(minutes) + reason = sanitize(tgui_input_text(usr,"Reason?","reason",reason2)) + if(!reason) return + if("No") + temp = 0 + duration = "Perma" + reason = sanitize(tgui_input_text(usr,"Reason?","reason",reason2)) + if(!reason) return + + log_admin("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + message_admins(span_blue("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]"), 1) + Banlist.cd = "/base/[banfolder]" + Banlist["reason"] << reason + Banlist["temp"] << temp + Banlist["minutes"] << minutes + Banlist["bannedby"] << usr.ckey + Banlist.cd = "/base" + feedback_inc("ban_edit",1) + unbanpanel() + + /////////////////////////////////////new ban stuff + + else if(href_list["jobban2"]) +// if(!check_rights(R_BAN)) return + + var/mob/M = locate(href_list["jobban2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey") + return + if(!job_master) + to_chat(usr, "Job Master has not been setup!") + return + + var/dat = "" + var/header = "Job-Ban Panel: [M.name]" + var/body + var/jobs = "" + + /***********************************WARNING!************************************ + The jobban stuff looks mangled and disgusting + But it looks beautiful in-game + -Nodrak + ************************************WARNING!***********************************/ + var/counter = 0 +//Regular jobs + //Command (Blue) + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 6) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Command Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Security (Red) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Security Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Engineering (Yellow) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Engineering Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Cargo (Yellow) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Cargo Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Medical (White) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Medical Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Science (Purple) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Science Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //VOREStation Edit Start + //Exploration (Purple) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Exploration Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Offmap (Kinda lightish bluey) + counter = 0 + // Needs to be done early because it uses the length of the list for sizing + var/list/offmap_jobs = list() + for(var/dept in offmap_departments) + offmap_jobs += SSjob.get_job_titles_in_department(dept) + jobs += "" + jobs += "" + for(var/jobPos in offmap_jobs) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Offmap Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //VOREstation Edit End + //Civilian (Grey) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + if(jobban_isbanned(M, "Internal Affairs Agent")) + jobs += "" + else + jobs += "" + + jobs += "
                    Civilian Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    Internal Affairs AgentInternal Affairs Agent
                    " + + //Synthetic (Green) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) + if(!jobPos) continue + var/datum/job/job = job_master.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Synthetic Positions
                    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
                    " + + //Antagonist (Orange) + counter = 0 + var/isbanned_dept = jobban_isbanned(M, "Syndicate") + jobs += "" + jobs += "" + + // Antagonists. + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(!antag || !antag.bantype) + continue + + if(jobban_isbanned(M, "[antag.bantype]") || isbanned_dept) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Antagonist Positions
                    [replacetext("[antag.role_text]", " ", " ")][replacetext("[antag.role_text]", " ", " ")]
                    " + + //Misc 'roles' + counter = 0 + var/list/misc_roles = list("Dionaea", "Graffiti", "Custom loadout", "pAI", "GhostRoles", "AntagHUD") + jobs += "" + jobs += "" + for(var/entry in misc_roles) + if(jobban_isbanned(M, entry)) + jobs += "" + else + jobs += "" + + if(++counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + jobs += "
                    Other Roles
                    [entry][entry]
                    " + + // Finished + body = "[jobs]" + dat = "[header][body]" + usr << browse(dat, "window=jobban2;size=800x490") + return + + //JOBBAN'S INNARDS + else if(href_list["jobban3"]) + if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN,0)) + to_chat(usr, "You do not have the appropriate permissions to add job bans!") + return + + if(check_rights(R_MOD,0) && !check_rights(R_ADMIN,0) && !config.mods_can_job_tempban) // If mod and tempban disabled + to_chat(usr, "Mod jobbanning is disabled!") + return + + var/mob/M = locate(href_list["jobban4"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(M != usr) //we can jobban ourselves + if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them + tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!") + return + + if(!job_master) + to_chat(usr, "Job Master has not been setup!") + return + + //get jobs for department if specified, otherwise just returnt he one job in a list. + var/list/joblist = list() + switch(href_list["jobban3"]) + if("commanddept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("securitydept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("engineeringdept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("cargodept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("medicaldept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("sciencedept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + //VOREStation Edit Start + if("explorationdept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("offmapdept") + for(var/dept in offmap_departments) + for(var/jobPos in SSjob.get_job_titles_in_department(dept)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + //VOREStation Edit End + if("civiliandept") + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("nonhumandept") + joblist += "pAI" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) + if(!jobPos) continue + var/datum/job/temp = job_master.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + else + joblist += href_list["jobban3"] + + //Create a list of unbanned jobs within joblist + var/list/notbannedlist = list() + for(var/job in joblist) + if(!jobban_isbanned(M, job)) + notbannedlist += job + + //Banning comes first + if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. + switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban", list("Yes","No","Cancel"))) + if("Yes") + if(!check_rights(R_MOD,0) && !check_rights(R_BAN, 0)) + to_chat(usr, " You cannot issue temporary job-bans!") + return + if(config.ban_legacy_system) + to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") + return + var/mins = tgui_input_number(usr,"How long (in minutes)?","Ban time",1440) + if(!mins) + return + if(check_rights(R_MOD, 0) && !check_rights(R_BAN, 0) && mins > config.mod_job_tempban_max) + to_chat(usr, " Moderators can only job tempban up to [config.mod_job_tempban_max] minutes!") + return + var/reason = sanitize(tgui_input_text(usr,"Reason?","Please State Reason","")) + if(!reason) + return + + var/msg + for(var/job in notbannedlist) + ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") + log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes") + feedback_inc("ban_job_tmp",1) + DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job) + feedback_add_details("ban_job_tmp","- [job]") + jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") //Legacy banning does not support temporary jobbans. + if(!msg) + msg = job + else + msg += ", [job]" + notes_add(M.ckey, "Banned from [msg] - [reason]", usr) + message_admins(span_blue("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes"), 1) + to_chat(M, "[span_red("You have been jobbanned by [usr.client.ckey] from: [msg].")]") + to_chat(M, "[span_red("The reason is: [reason]")]") + to_chat(M, "[span_red("This jobban will be lifted in [mins] minutes.")]") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("No") + if(!check_rights(R_BAN)) return + var/reason = sanitize(tgui_input_text(usr,"Reason?","Please State Reason","")) + if(reason) + var/msg + for(var/job in notbannedlist) + ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") + log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]") + feedback_inc("ban_job",1) + DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job) + feedback_add_details("ban_job","- [job]") + jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") + if(!msg) msg = job + else msg += ", [job]" + notes_add(M.ckey, "Banned from [msg] - [reason]", usr) + message_admins(span_blue("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]"), 1) + to_chat(M, "[span_red("You have been jobbanned by [usr.client.ckey] from: [msg].")]") + to_chat(M, "[span_red("The reason is: [reason]")]") + to_chat(M, "[span_red("Jobban can be lifted only upon request.")]") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("Cancel") + return + + //Unbanning joblist + //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) + if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. + if(!config.ban_legacy_system) + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + DB_ban_panel(M.ckey) + return + var/msg + for(var/job in joblist) + var/reason = jobban_isbanned(M, job) + if(!reason) continue //skip if it isn't jobbanned anyway + switch(tgui_alert(usr, "Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm",list("Yes","No"))) + if("Yes") + ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") + log_admin("[key_name(usr)] unbanned [key_name(M)] from [job]") + DB_ban_unban(M.ckey, BANTYPE_JOB_PERMA, job) + feedback_inc("ban_job_unban",1) + feedback_add_details("ban_job_unban","- [job]") + jobban_unban(M, job) + if(!msg) msg = job + else msg += ", [job]" + else + continue + if(msg) + message_admins(span_blue("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]"), 1) + to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + return 0 //we didn't do anything! + + else if(href_list["boot2"]) + var/mob/M = locate(href_list["boot2"]) + if (ismob(M)) + if(!check_if_greater_rights_than(M.client)) + return + var/reason = sanitize(tgui_input_text(usr, "Please enter reason.", multiline = TRUE, prevent_enter = TRUE)) + if(!reason) + return + + to_chat(M, span("filter_system critical", "You have been kicked from the server: [reason]")) + log_admin("[key_name(usr)] booted [key_name(M)] for reason: '[reason]'.") + message_admins(span_blue("[key_name_admin(usr)] booted [key_name_admin(M)] for reason '[reason]'."), 1) + //M.client = null + admin_action_message(usr.key, M.key, "kicked", reason, 0) //VOREStation Add + qdel(M.client) + + else if(href_list["removejobban"]) + if(!check_rights(R_BAN)) return + + var/t = href_list["removejobban"] + if(t) + if((tgui_alert(usr, "Do you want to unjobban [t]?","Unjobban confirmation", list("Yes", "No")) == "Yes") && t) //No more misclicks! Unless you do it twice. + log_admin("[key_name(usr)] removed [t]") + message_admins(span_blue("[key_name_admin(usr)] removed [t]"), 1) + jobban_remove(t) + href_list["ban"] = 1 // lets it fall through and refresh + var/t_split = splittext(t, " - ") + var/key = t_split[1] + var/job = t_split[2] + DB_ban_unban(ckey(key), BANTYPE_JOB_PERMA, job) + + else if(href_list["newban"]) + if(!check_rights(R_MOD,0) && !check_rights(R_BAN, 0)) + to_chat(usr, "You do not have the appropriate permissions to add bans!") + return + + if(check_rights(R_MOD,0) && !check_rights(R_ADMIN, 0) && !config.mods_can_job_tempban) // If mod and tempban disabled + to_chat(usr, "Mod jobbanning is disabled!") + return + + var/mob/M = locate(href_list["newban"]) + if(!ismob(M)) return + + if(M.client && M.client.holder) return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway + + switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No","Cancel"))) + if("Yes") + var/mins = tgui_input_number(usr,"How long (in minutes)?","Ban time",1440) + if(!mins) + return + if(check_rights(R_MOD, 0) && !check_rights(R_BAN, 0) && mins > config.mod_tempban_max) + to_chat(usr, "Moderators can only job tempban up to [config.mod_tempban_max] minutes!") + return + if(mins >= 525600) mins = 525599 + var/reason = sanitize(tgui_input_text(usr,"Reason?","reason","Griefer")) + if(!reason) + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") + notes_add(M.ckey,"[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.",usr) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + feedback_inc("ban_tmp",1) + DB_ban_record(BANTYPE_TEMP, M, mins, reason) + feedback_inc("ban_tmp_mins",mins) + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins(span_blue("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")) + var/datum/admin_help/AH = M.client ? M.client.current_ticket : null + if(AH) + AH.Resolve() + qdel(M.client) + //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. + if("No") + if(!check_rights(R_BAN)) return + var/reason = sanitize(tgui_input_text(usr,"Reason?","reason","Griefer")) + if(!reason) + return + switch(tgui_alert(usr,"IP ban?","IP Ban",list("Yes","No","Cancel"))) + if("Cancel") return + if("Yes") + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) + if("No") + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.") + notes_add(M.ckey,"[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.",usr) + log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") + message_admins(span_blue("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.")) + feedback_inc("ban_perma",1) + DB_ban_record(BANTYPE_PERMA, M, -1, reason) + var/datum/admin_help/AH = M.client ? M.client.current_ticket : null + if(AH) + AH.Resolve() + qdel(M.client) + //qdel(M) + if("Cancel") + return + + else if(href_list["mute"]) + if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN)) return + + var/mob/M = locate(href_list["mute"]) + if(!ismob(M)) return + if(!M.client) return + + var/mute_type = href_list["mute_type"] + if(istext(mute_type)) mute_type = text2num(mute_type) + if(!isnum(mute_type)) return + + cmd_admin_mute(M, mute_type) + + else if(href_list["c_mode"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + if(ticker && ticker.mode) + return tgui_alert_async(usr, "The game has already started.") + var/dat = {"What mode do you wish to play?
                    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
                    "} + dat += {"Secret
                    "} + dat += {"Random
                    "} + dat += {"Now: [master_mode]"} + usr << browse(dat, "window=c_mode") + + else if(href_list["f_secret"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + if(ticker && ticker.mode) + return tgui_alert_async(usr, "The game has already started.") + if(master_mode != "secret") + return tgui_alert_async(usr, "The game mode has to be secret!") + var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
                    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
                    "} + dat += {"Random (default)
                    "} + dat += {"Now: [secret_force_mode]"} + usr << browse(dat, "window=f_secret") + + else if(href_list["c_mode2"]) + if(!check_rights(R_ADMIN|R_SERVER|R_EVENT)) return + + if (ticker && ticker.mode) + return tgui_alert_async(usr, "The game has already started.") + master_mode = href_list["c_mode2"] + log_admin("[key_name(usr)] set the mode as [config.mode_names[master_mode]].") + message_admins(span_blue("[key_name_admin(usr)] set the mode as [config.mode_names[master_mode]]."), 1) + to_world(span_blue("The mode is now: [config.mode_names[master_mode]]")) + Game() // updates the main game menu + world.save_mode(master_mode) + .(href, list("c_mode"=1)) + + else if(href_list["f_secret2"]) + if(!check_rights(R_ADMIN|R_SERVER|R_EVENT)) return + + if(ticker && ticker.mode) + return tgui_alert_async(usr, "The game has already started.") + if(master_mode != "secret") + return tgui_alert_async(usr, "The game mode has to be secret!") + secret_force_mode = href_list["f_secret2"] + log_admin("[key_name(usr)] set the forced secret mode as [secret_force_mode].") + message_admins(span_blue("[key_name_admin(usr)] set the forced secret mode as [secret_force_mode]."), 1) + Game() // updates the main game menu + .(href, list("f_secret"=1)) + + else if(href_list["monkeyone"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["monkeyone"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") + message_admins(span_blue("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)]"), 1) + H.monkeyize() + + else if(href_list["corgione"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["corgione"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") + message_admins(span_blue("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)]"), 1) + H.corgize() + + else if(href_list["forcespeech"]) + if(!check_rights(R_FUN)) return + + var/mob/M = locate(href_list["forcespeech"]) + if(!ismob(M)) + to_chat(usr, "this can only be used on instances of type /mob") + + var/speech = tgui_input_text(usr, "What will [key_name(M)] say?.", "Force speech", "") // Don't need to sanitize, since it does that in say(), we also trust our admins. + if(!speech) return + M.say(speech) + speech = sanitize(speech) // Nah, we don't trust them + log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") + message_admins(span_blue("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]")) + + else if(href_list["sendtoprison"]) + if(!check_rights(R_ADMIN)) return + + if(tgui_alert(usr, "Send to admin prison for the round?", "Message", list("Yes", "No")) != "Yes") + return + + var/mob/M = locate(href_list["sendtoprison"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + var/turf/prison_cell = pick(prisonwarp) + if(!prison_cell) return + + var/obj/structure/closet/secure_closet/brig/locker = new /obj/structure/closet/secure_closet/brig(prison_cell) + locker.opened = 0 + locker.locked = 1 + + //strip their stuff and stick it in the crate + for(var/obj/item/I in M) + M.drop_from_inventory(I, locker) + + //so they black out before warping + M.Paralyse(5) + sleep(5) + if(!M) return + + M.loc = prison_cell + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + + to_chat(M, "You have been sent to the prison station!") + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins(span_blue("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station."), 1) + + else if(href_list["sendbacktolobby"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["sendbacktolobby"]) + if(!isobserver(M)) + to_chat(usr, "You can only send ghost players back to the Lobby.") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(tgui_alert(usr, "Send [key_name(M)] back to Lobby?", "Message", list("Yes", "No")) != "Yes") + return + + log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + + var/mob/new_player/NP = new() + NP.ckey = M.ckey + qdel(M) + + else if(href_list["tdome1"]) + if(!check_rights(R_FUN)) return + + if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") + return + + var/mob/M = locate(href_list["tdome1"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.drop_from_inventory(I) + + M.Paralyse(5) + sleep(5) + M.loc = pick(tdome1) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) + + else if(href_list["tdome2"]) + if(!check_rights(R_FUN)) return + + if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") + return + + var/mob/M = locate(href_list["tdome2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.drop_from_inventory(I) + + M.Paralyse(5) + sleep(5) + M.loc = pick(tdome2) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) + + else if(href_list["tdomeadmin"]) + if(!check_rights(R_FUN)) return + + if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") + return + + var/mob/M = locate(href_list["tdomeadmin"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + M.Paralyse(5) + sleep(5) + M.loc = pick(tdomeadmin) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) + + else if(href_list["tdomeobserve"]) + if(!check_rights(R_FUN)) return + + if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") + return + + var/mob/M = locate(href_list["tdomeobserve"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.drop_from_inventory(I) + + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/observer = M + observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), slot_w_uniform) + observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(observer), slot_shoes) + M.Paralyse(5) + sleep(5) + M.loc = pick(tdomeobserve) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) + + else if(href_list["revive"]) + if(!check_rights(R_REJUVINATE)) return + + var/mob/living/L = locate(href_list["revive"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + + if(config.allow_admin_rev) + L.revive() + message_admins(span_red("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!"), 1) + log_admin("[key_name(usr)] healed / Rrvived [key_name(L)]") + else + to_chat(usr, "Admin Rejuvinates have been disabled") + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["makeai"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + message_admins(span_red("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!"), 1) + log_admin("[key_name(usr)] AIized [key_name(H)]") + H.AIize() + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["makealien"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + usr.client.cmd_admin_alienize(H) + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["makerobot"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + usr.client.cmd_admin_robotize(H) + + else if(href_list["makeanimal"]) + if(!check_rights(R_SPAWN)) return + + var/mob/M = locate(href_list["makeanimal"]) + if(istype(M, /mob/new_player)) + to_chat(usr, "This cannot be used on instances of type /mob/new_player") + return + + usr.client.cmd_admin_animalize(M) + + else if(href_list["respawn"]) + if(!check_rights(R_SPAWN)) + return + + var/client/C = locate(href_list["respawn"]) + if(!istype(C)) + return + usr.client.respawn_character_proper(C) + + else if(href_list["togmutate"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locate(href_list["togmutate"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/block=text2num(href_list["block"]) + usr.client.cmd_admin_toggle_block(H,block) + show_player_panel(H) + + else if(href_list["adminplayeropts"]) + var/mob/M = locate(href_list["adminplayeropts"]) + show_player_panel(M) + + else if(href_list["adminplayerobservejump"]) + if(!check_rights(R_MOD|R_ADMIN|R_SERVER)) return //VOREStation Edit + + var/mob/M = locate(href_list["adminplayerobservejump"]) + + var/client/C = usr.client + if(!isobserver(usr)) C.admin_ghost() + sleep(2) + C.do_jumptomob(M) + + else if(href_list["adminplayerobservefollow"]) + if(!check_rights(R_MOD|R_ADMIN|R_SERVER)) //VOREStation Edit + return + + var/mob/M = locate(href_list["adminplayerobservefollow"]) + + var/client/C = usr.client + if(!isobserver(usr)) C.admin_ghost() + var/mob/observer/dead/G = C.mob + sleep(2) + G.ManualFollow(M) + + else if(href_list["check_antagonist"]) + check_antagonists() + + else if(href_list["take_question"]) + + var/mob/M = locate(href_list["take_question"]) + if(ismob(M)) + var/take_msg = "ADMINHELP: [key_name(usr.client)] is attending to [key_name(M)]'s adminhelp, please don't dogpile them." + for(var/client/X in GLOB.admins) + if((R_ADMIN|R_MOD|R_SERVER) & X.holder.rights) //VOREStation Edit + to_chat(X, take_msg) + to_chat(M, "Your adminhelp is being attended to by [usr.client]. Thanks for your patience!") + // VoreStation Edit Start + if (config.chat_webhook_url) + spawn(0) + var/query_string = "type=admintake" + query_string += "&key=[url_encode(config.chat_webhook_key)]" + query_string += "&admin=[url_encode(key_name(usr.client))]" + query_string += "&user=[url_encode(key_name(M))]" + world.Export("[config.chat_webhook_url]?[query_string]") + // VoreStation Edit End + else + to_chat(usr, "Unable to locate mob.") + + else if(href_list["adminplayerobservecoodjump"]) + if(!check_rights(R_ADMIN|R_SERVER|R_MOD)) return //VOREStation Edit + + var/x = text2num(href_list["X"]) + var/y = text2num(href_list["Y"]) + var/z = text2num(href_list["Z"]) + + var/client/C = usr.client + if(!isobserver(usr)) C.admin_ghost() + sleep(2) + C.jumptocoord(x,y,z) + + else if(href_list["adminchecklaws"]) + output_ai_laws() + + else if(href_list["adminmoreinfo"]) + var/mob/M = locate(href_list["adminmoreinfo"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/location_description = "" + var/special_role_description = "" + var/health_description = "" + var/gender_description = "" + var/turf/T = get_turf(M) + + //Location + if(isturf(T)) + if(isarea(T.loc)) + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" + else + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" + + //Job + antagonist + if(M.mind) + special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [span_red("[M.mind.special_role]")]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]" + else + special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;" + + //Health + if(isliving(M)) + var/mob/living/L = M + var/status + switch (M.stat) + if (0) status = "Alive" + if (1) status = span_orange("Unconscious") + if (2) status = span_red("Dead") + health_description = "Status = [status]" + health_description += "
                    Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]" + else + health_description = "This mob type has no health to speak of." + + //Gener + switch(M.gender) + if(MALE,FEMALE) gender_description = "[M.gender]" + else gender_description = span_red("[M.gender]") + + to_chat(src.owner, "Info about [M.name]:
                    \ + Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]
                    \ + Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];
                    \ + Location = [location_description];
                    \ + [special_role_description]
                    \ + (PM) (PP) (VV) \ + (SM) ([admin_jump_link(M, src)]) (CA)
                    ") + + else if(href_list["adminspawncookie"]) + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + var/mob/living/carbon/human/H = locate(href_list["adminspawncookie"]) + if(!ishuman(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + H.equip_to_slot_or_del( new /obj/item/weapon/reagent_containers/food/snacks/cookie(H), slot_l_hand ) + if(!(istype(H.l_hand,/obj/item/weapon/reagent_containers/food/snacks/cookie))) + H.equip_to_slot_or_del( new /obj/item/weapon/reagent_containers/food/snacks/cookie(H), slot_r_hand ) + if(!(istype(H.r_hand,/obj/item/weapon/reagent_containers/food/snacks/cookie))) + log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") + message_admins("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") + return + else + H.update_inv_r_hand()//To ensure the icon appears in the HUD + else + H.update_inv_l_hand() + log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") + message_admins("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") + feedback_inc("admin_cookies_spawned",1) + to_chat(H, "Your prayers have been answered!! You received the best cookie!") + + else if(href_list["adminsmite"]) + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) + if(!ishuman(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + owner.smite(H) + + else if(href_list["BlueSpaceArtillery"]) + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return + + var/mob/living/M = locate(href_list["BlueSpaceArtillery"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + + if(tgui_alert(src.owner, "Are you sure you wish to hit [key_name(M)] with Blue Space Artillery?", "Confirm Firing?", list("Yes", "No")) != "Yes") + return + + bluespace_artillery(M,src) + + else if(href_list["CentComReply"]) + var/mob/living/L = locate(href_list["CentComReply"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /mob/living/") + return + + if(L.can_centcom_reply()) + var/input = sanitize(tgui_input_text(src.owner, "Please enter a message to reply to [key_name(L)] via their headset.","Outgoing message from CentCom", "")) + if(!input) return + + to_chat(src.owner, "You sent [input] to [L] via a secure channel.") + log_admin("[src.owner] replied to [key_name(L)]'s CentCom message with the message [input].") + message_admins("[src.owner] replied to [key_name(L)]'s CentCom message with: \"[input]\"") + if(!isAI(L)) + to_chat(L, "You hear something crackle in your headset for a moment before a voice speaks.") + to_chat(L, "Please stand by for a message from Central Command.") + to_chat(L, "Message as follows.") + to_chat(L, "[input]") + to_chat(L, "Message ends.") + else + to_chat(src.owner, "The person you are trying to contact does not have functional radio equipment.") + + + else if(href_list["SyndicateReply"]) + var/mob/living/carbon/human/H = locate(href_list["SyndicateReply"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!istype(H.l_ear, /obj/item/device/radio/headset) && !istype(H.r_ear, /obj/item/device/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + var/input = sanitize(tgui_input_text(src.owner, "Please enter a message to reply to [key_name(H)] via their headset.","Outgoing message from a shadowy figure...", "")) + if(!input) return + + to_chat(src.owner, "You sent [input] to [H] via a secure channel.") + log_admin("[src.owner] replied to [key_name(H)]'s illegal message with the message [input].") + to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \ + \"Please stand by for a message from your benefactor. Message as follows, agent. \"[input]\" Message ends.\"") + + else if(href_list["AdminFaxView"]) + var/obj/item/fax = locate(href_list["AdminFaxView"]) + if (istype(fax, /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = fax + P.show_content(usr,1) + else if (istype(fax, /obj/item/weapon/photo)) + var/obj/item/weapon/photo/H = fax + H.show(usr) + else if (istype(fax, /obj/item/weapon/paper_bundle)) + //having multiple people turning pages on a paper_bundle can cause issues + //open a browse window listing the contents instead + var/data = "" + var/obj/item/weapon/paper_bundle/B = fax + + for (var/page = 1, page <= B.pages.len, page++) + var/obj/pageobj = B.pages[page] + data += "Page [page] - [pageobj.name]
                    " + + usr << browse(data, "window=[B.name]") + else + to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") + + else if (href_list["AdminFaxViewPage"]) + var/page = text2num(href_list["AdminFaxViewPage"]) + var/obj/item/weapon/paper_bundle/bundle = locate(href_list["paper_bundle"]) + + if (!bundle) return + + if (istype(bundle.pages[page], /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = bundle.pages[page] + P.show_content(src.owner, 1) + else if (istype(bundle.pages[page], /obj/item/weapon/photo)) + var/obj/item/weapon/photo/H = bundle.pages[page] + H.show(src.owner) + return + + else if(href_list["FaxReply"]) + var/mob/sender = locate(href_list["FaxReply"]) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + var/replyorigin = href_list["replyorigin"] + + + var/obj/item/weapon/paper/admin/P = new /obj/item/weapon/paper/admin( null ) //hopefully the null loc won't cause trouble for us + faxreply = P + + P.admindatum = src + P.origin = replyorigin + P.destination = fax + P.sender = sender + + P.adminbrowse() + + else if(href_list["jumpto"]) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) + return + if(!config.allow_admin_jump) + tgui_alert_async(usr, "Admin jumping disabled") + return + + var/mob/M = locate(href_list["jumpto"]) + if(!M) + return + + var/turf/T = get_turf(M) + if(isturf(T)) + usr.on_mob_jump() + usr.forceMove(T) + feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_and_message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]") + else + to_chat(usr, "This mob is not located in the game world.") + + else if(href_list["getmob"]) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) + return + if(!config.allow_admin_jump) + tgui_alert_async(usr, "Admin jumping disabled") + return + if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes") + return + + var/mob/M = locate(href_list["getmob"]) + if(!M) + return + M.on_mob_jump() + M.forceMove(get_turf(usr)) + var/msg = "[key_name_admin(usr)] jumped [key_name_admin(M)] to them" + log_and_message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else if(href_list["sendmob"]) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) + return + if(!config.allow_admin_jump) + tgui_alert_async(usr, "Admin jumping disabled") + return + + var/mob/M = locate(href_list["sendmob"]) + if(!M) + return + + var/list/areachoices = return_sorted_areas() + var/choice = tgui_input_list(usr, "Pick an area:", "Send Mob", areachoices) + if(!choice) + return + + var/area/A = areachoices[choice] + if(!A) + return + + M.on_mob_jump() + M.forceMove(pick(get_area_turfs(A))) + var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]" + log_and_message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else if(href_list["narrateto"]) + if(!check_rights(R_ADMIN|R_EVENT|R_FUN)) return + + var/mob/M = locate(href_list["narrateto"]) + usr.client.cmd_admin_direct_narrate(M) + + else if(href_list["subtlemessage"]) + if(!check_rights(R_MOD|R_ADMIN|R_EVENT|R_FUN,0)) return + + var/mob/M = locate(href_list["subtlemessage"]) + usr.client.cmd_admin_subtle_message(M) + + else if(href_list["traitor"]) + if(!check_rights(R_ADMIN|R_MOD|R_EVENT)) return + + if(!ticker || !ticker.mode) + tgui_alert_async(usr, "The game hasn't started yet!") + return + + var/mob/M = locate(href_list["traitor"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + show_traitor_panel(M) + + else if(href_list["create_object"]) + if(!check_rights(R_SPAWN)) return + return create_object(usr) + + else if(href_list["quick_create_object"]) + if(!check_rights(R_SPAWN)) return + return quick_create_object(usr) + + else if(href_list["create_turf"]) + if(!check_rights(R_SPAWN)) return + return create_turf(usr) + + else if(href_list["create_mob"]) + if(!check_rights(R_SPAWN)) return + return create_mob(usr) + + else if(href_list["object_list"]) //this is the laggiest thing ever + if(!check_rights(R_SPAWN)) return + + if(!config.allow_admin_spawning) + to_chat(usr, "Spawning of items is not allowed.") + return + + var/atom/loc = usr.loc + + var/dirty_paths + if (istext(href_list["object_list"])) + dirty_paths = list(href_list["object_list"]) + else if (istype(href_list["object_list"], /list)) + dirty_paths = href_list["object_list"] + + var/paths = list() + var/removed_paths = list() + + for(var/dirty_path in dirty_paths) + var/path = text2path(dirty_path) + if(!path) + removed_paths += dirty_path + continue + else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) + removed_paths += dirty_path + continue + else if(ispath(path, /obj/item/weapon/gun/energy/pulse_rifle)) + if(!check_rights(R_FUN,0)) + removed_paths += dirty_path + continue + else if(ispath(path, /obj/item/weapon/melee/energy/blade))//Not an item one should be able to spawn./N + if(!check_rights(R_FUN,0)) + removed_paths += dirty_path + continue + else if(ispath(path, /obj/effect/bhole)) + if(!check_rights(R_FUN,0)) + removed_paths += dirty_path + continue + paths += path + + if(!paths) + tgui_alert(usr, "The path list you sent is empty") + return + if(length(paths) > 5) + tgui_alert_async(usr, "Select fewer object types, (max 5)") + return + else if(length(removed_paths)) + tgui_alert_async(usr, "Removed:\n" + jointext(removed_paths, "\n")) + + var/list/offset = splittext(href_list["offset"],",") + var/number = dd_range(1, 100, text2num(href_list["object_count"])) + var/X = offset.len > 0 ? text2num(offset[1]) : 0 + var/Y = offset.len > 1 ? text2num(offset[2]) : 0 + var/Z = offset.len > 2 ? text2num(offset[3]) : 0 + var/tmp_dir = href_list["object_dir"] + var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2 + if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10))) + obj_dir = 2 + var/obj_name = sanitize(href_list["object_name"]) + var/where = href_list["object_where"] + if (!( where in list("onfloor","inhand","inmarked") )) + where = "onfloor" + + if( where == "inhand" ) + to_chat(usr, "Support for inhand not available yet. Will spawn on floor.") + where = "onfloor" + + if ( where == "inhand" ) //Can only give when human or monkey + if ( !( ishuman(usr) || issmall(usr) ) ) + to_chat(usr, "Can only spawn in hand when you're a human or a monkey.") + where = "onfloor" + else if ( usr.get_active_hand() ) + to_chat(usr, "Your active hand is full. Spawning on floor.") + where = "onfloor" + + if ( where == "inmarked" ) + if ( !marked_datum ) + to_chat(usr, "You don't have any object marked. Abandoning spawn.") + return + else + if ( !istype(marked_datum,/atom) ) + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") + return + + var/atom/target //Where the object will be spawned + switch ( where ) + if ( "onfloor" ) + switch (href_list["offset_type"]) + if ("absolute") + target = locate(0 + X,0 + Y,0 + Z) + if ("relative") + target = locate(loc.x + X,loc.y + Y,loc.z + Z) + if ( "inmarked" ) + target = marked_datum + + if(target) + for (var/path in paths) + for (var/i = 0; i < number; i++) + if(path in typesof(/turf)) + var/turf/O = target + var/turf/N = O.ChangeTurf(path) + if(N) + if(obj_name) + N.name = obj_name + else + var/atom/O = new path(target) + if(O) + O.set_dir(obj_dir) + if(obj_name) + O.name = obj_name + if(istype(O,/mob)) + var/mob/M = O + M.real_name = obj_name + + log_and_message_admins("created [number] [english_list(paths)]") + return + + else if(href_list["admin_secrets_panel"]) + var/datum/admin_secret_category/AC = locate(href_list["admin_secrets_panel"]) in admin_secrets.categories + src.Secrets(AC) + + else if(href_list["admin_secrets"]) + var/datum/admin_secret_item/item = locate(href_list["admin_secrets"]) in admin_secrets.items + item.execute(usr) + + else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here + src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. + src.access_news_network() + + else if(href_list["ac_set_channel_name"]) + src.admincaster_feed_channel.channel_name = sanitizeSafe(tgui_input_text(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) + src.access_news_network() + + else if(href_list["ac_set_channel_lock"]) + src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked + src.access_news_network() + + else if(href_list["ac_submit_new_channel"]) + var/check = 0 + for(var/datum/feed_channel/FC in news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) + src.admincaster_screen=7 + else + var/choice = tgui_alert(usr, "Please confirm Feed channel creation","Network Channel Handler",list("Confirm","Cancel")) + if(choice=="Confirm") + news_network.CreateFeedChannel(admincaster_feed_channel.channel_name, admincaster_signature, admincaster_feed_channel.locked, 1) + feedback_inc("newscaster_channels",1) //Adding channel to the global network + log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") + src.admincaster_screen=5 + src.access_news_network() + + else if(href_list["ac_set_channel_receiving"]) + var/list/available_channels = list() + for(var/datum/feed_channel/F in news_network.network_channels) + available_channels += F.channel_name + src.admincaster_feed_channel.channel_name = sanitizeSafe(tgui_input_list(usr, "Choose receiving Feed Channel", "Network Channel Handler", available_channels )) + src.access_news_network() + + else if(href_list["ac_set_new_title"]) + src.admincaster_feed_message.title = sanitize(tgui_input_text(usr, "Enter the Feed title", "Network Channel Handler", "")) + src.access_news_network() + + else if(href_list["ac_set_new_message"]) + src.admincaster_feed_message.body = sanitize(tgui_input_text(usr, "Write your Feed story", "Network Channel Handler", "", multiline = TRUE, prevent_enter = TRUE)) + src.access_news_network() + + else if(href_list["ac_submit_new_message"]) + if(src.admincaster_feed_message.body =="" || admincaster_feed_message.title == "" || admincaster_feed_message.body =="\[REDACTED\]" || admincaster_feed_channel.channel_name == "" ) + src.admincaster_screen = 6 + else + feedback_inc("newscaster_stories",1) + news_network.SubmitArticle(admincaster_feed_message.body, admincaster_signature, admincaster_feed_channel.channel_name, null, 1, "", admincaster_feed_message.title) + src.admincaster_screen=4 + + log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") + src.access_news_network() + + else if(href_list["ac_create_channel"]) + src.admincaster_screen=2 + src.access_news_network() + + else if(href_list["ac_create_feed_story"]) + src.admincaster_screen=3 + src.access_news_network() + + else if(href_list["ac_menu_censor_story"]) + src.admincaster_screen=10 + src.access_news_network() + + else if(href_list["ac_menu_censor_channel"]) + src.admincaster_screen=11 + src.access_news_network() + + else if(href_list["ac_menu_wanted"]) + var/already_wanted = 0 + if(news_network.wanted_issue) + already_wanted = 1 + + if(already_wanted) + src.admincaster_feed_message.author = news_network.wanted_issue.author + src.admincaster_feed_message.body = news_network.wanted_issue.body + src.admincaster_screen = 14 + src.access_news_network() + + else if(href_list["ac_set_wanted_name"]) + src.admincaster_feed_message.author = sanitize(tgui_input_text(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) + src.access_news_network() + + else if(href_list["ac_set_wanted_desc"]) + src.admincaster_feed_message.body = sanitize(tgui_input_text(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) + src.access_news_network() + + else if(href_list["ac_submit_wanted"]) + var/input_param = text2num(href_list["ac_submit_wanted"]) + if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") + src.admincaster_screen = 16 + else + var/choice = tgui_alert(usr, "Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler",list("Confirm","Cancel")) + if(choice=="Confirm") + if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below + var/datum/feed_message/WANTED = new /datum/feed_message + WANTED.author = src.admincaster_feed_message.author //Wanted name + WANTED.body = src.admincaster_feed_message.body //Wanted desc + WANTED.backup_author = src.admincaster_signature //Submitted by + WANTED.is_admin_message = 1 + news_network.wanted_issue = WANTED + for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + NEWSCASTER.newsAlert() + NEWSCASTER.update_icon() + src.admincaster_screen = 15 + else + news_network.wanted_issue.author = src.admincaster_feed_message.author + news_network.wanted_issue.body = src.admincaster_feed_message.body + news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author + src.admincaster_screen = 19 + log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") + src.access_news_network() + + else if(href_list["ac_cancel_wanted"]) + var/choice = tgui_alert(usr, "Please confirm Wanted Issue removal","Network Security Handler",list("Confirm","Cancel")) + if(choice=="Confirm") + news_network.wanted_issue = null + for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + NEWSCASTER.update_icon() + src.admincaster_screen=17 + src.access_news_network() + + else if(href_list["ac_censor_channel_author"]) + var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) + if(FC.author != "\[REDACTED\]") + FC.backup_author = FC.author + FC.author = "\[REDACTED\]" + else + FC.author = FC.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_author"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) + if(MSG.author != "\[REDACTED\]") + MSG.backup_author = MSG.author + MSG.author = "\[REDACTED\]" + else + MSG.author = MSG.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_body"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) + if(MSG.body != "\[REDACTED\]") + MSG.backup_body = MSG.body + MSG.body = "\[REDACTED\]" + else + MSG.body = MSG.backup_body + src.access_news_network() + + else if(href_list["ac_pick_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) + src.admincaster_feed_channel = FC + src.admincaster_screen=13 + src.access_news_network() + + else if(href_list["ac_toggle_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) + FC.censored = !FC.censored + src.access_news_network() + + else if(href_list["ac_view"]) + src.admincaster_screen=1 + src.access_news_network() + + else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ + src.admincaster_screen = text2num(href_list["ac_setScreen"]) + if (src.admincaster_screen == 0) + if(src.admincaster_feed_channel) + src.admincaster_feed_channel = new /datum/feed_channel + if(src.admincaster_feed_message) + src.admincaster_feed_message = new /datum/feed_message + src.access_news_network() + + else if(href_list["ac_show_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 9 + src.access_news_network() + + else if(href_list["ac_pick_censor_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 12 + src.access_news_network() + + else if(href_list["ac_refresh"]) + src.access_news_network() + + else if(href_list["ac_set_signature"]) + src.admincaster_signature = sanitize(tgui_input_text(usr, "Provide your desired signature", "Network Identity Handler", "")) + src.access_news_network() + + else if(href_list["populate_inactive_customitems"]) + if(check_rights(R_ADMIN|R_SERVER)) + populate_inactive_customitems_list(src.owner) + + else if(href_list["vsc"]) + if(check_rights(R_ADMIN|R_SERVER|R_EVENT)) + if(href_list["vsc"] == "airflow") + vsc.ChangeSettingsDialog(usr,vsc.settings) + if(href_list["vsc"] == "phoron") + vsc.ChangeSettingsDialog(usr,vsc.plc.settings) + if(href_list["vsc"] == "default") + vsc.SetDefault(usr) + + else if(href_list["toglang"]) + if(check_rights(R_SPAWN)) //VOREStation Edit + var/mob/M = locate(href_list["toglang"]) + if(!istype(M)) + to_chat(usr, "[M] is illegal type, must be /mob!") + return + var/lang2toggle = href_list["lang"] + var/datum/language/L = GLOB.all_languages[lang2toggle] + + if(L in M.languages) + if(!M.remove_language(lang2toggle)) + to_chat(usr, "Failed to remove language '[lang2toggle]' from \the [M]!") + else + if(!M.add_language(lang2toggle)) + to_chat(usr, "Failed to add language '[lang2toggle]' from \the [M]!") + + show_player_panel(M) + + else if(href_list["cryoplayer"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/carbon/M = locate(href_list["cryoplayer"]) //VOREStation edit from just an all mob check to mob/living/carbon + if(!istype(M)) + to_chat(usr, "Mob doesn't exist!") + return + + var/client/C = usr.client + C.despawn_player(M) + + // player info stuff + + if(href_list["notes"]) + var/ckey = href_list["ckey"] + if(!ckey) + var/mob/M = locate(href_list["mob"]) + if(ismob(M)) + ckey = M.ckey + + switch(href_list["notes"]) + if("show") + var/datum/tgui_module/player_notes_info/A = new(src) + A.key = ckey + A.tgui_interact(usr) + if("list") + var/filter + if(href_list["filter"] && href_list["filter"] != "0") + filter = url_decode(href_list["filter"]) + PlayerNotesPage(text2num(href_list["index"]), filter) + if("filter") + PlayerNotesFilter() + return + +/mob/living/proc/can_centcom_reply() + return 0 + +/mob/living/carbon/human/can_centcom_reply() + return istype(l_ear, /obj/item/device/radio/headset) || istype(r_ear, /obj/item/device/radio/headset) + +/mob/living/silicon/ai/can_centcom_reply() + return common_radio != null && !check_unable(2) + +/atom/proc/extra_admin_link() + return + +/mob/extra_admin_link(var/source) + if(client && eyeobj) + return "|EYE" + +/mob/observer/dead/extra_admin_link(var/source) + if(mind && mind.current) + return "|BDY" + +/proc/admin_jump_link(var/atom/target, var/source) + if(!target) return + // The way admin jump links handle their src is weirdly inconsistent... + if(istype(source, /datum/admins)) + source = "src=\ref[source]" + else + source = "_src_=holder" + + . = "JMP" + . += target.extra_admin_link(source) diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index 2ddfd393f41..b74750843ce 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -1,36 +1,36 @@ -/proc/getbrokeninhands() - var/icon/IL = new('icons/mob/items/lefthand.dmi') - var/list/Lstates = IL.IconStates() - var/icon/IR = new('icons/mob/items/righthand.dmi') - var/list/Rstates = IR.IconStates() - - - var/text - for(var/A in typesof(/obj/item)) - var/obj/item/O = new A( locate(1,1,1) ) - if(!O) continue - var/icon/J = new(O.icon) - var/list/istates = J.IconStates() - if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" - if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" - - - if(O.icon_state) - if(!istates.Find(O.icon_state)) - text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" - //if(O.item_state) - // if(!istates.Find(O.item_state)) - // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" - //text+="\n" - qdel(O) - if(text) - var/F = file("broken_icons.txt") - fdel(F) - F << text - to_world("Completeled successfully and written to [F]") - - +/proc/getbrokeninhands() + var/icon/IL = new('icons/mob/items/lefthand.dmi') + var/list/Lstates = IL.IconStates() + var/icon/IR = new('icons/mob/items/righthand.dmi') + var/list/Rstates = IR.IconStates() + + + var/text + for(var/A in typesof(/obj/item)) + var/obj/item/O = new A( locate(1,1,1) ) + if(!O) continue + var/icon/J = new(O.icon) + var/list/istates = J.IconStates() + if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" + if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" + + + if(O.icon_state) + if(!istates.Find(O.icon_state)) + text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" + //if(O.item_state) + // if(!istates.Find(O.item_state)) + // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" + //text+="\n" + qdel(O) + if(text) + var/F = file("broken_icons.txt") + fdel(F) + F << text + to_world("Completeled successfully and written to [F]") + + diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index fe64b973460..e04b578c957 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -1046,7 +1046,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null else if(char == "'") if(word != "") - to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.") + to_chat(usr, span_red("SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[span_gray("[query_text]")]\" following \"[span_gray("[word]")]\". Please check your syntax, and try again.")) return null word = "'" @@ -1066,7 +1066,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null word += char if(i > len) - to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again.") + to_chat(usr, span_red("SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[span_gray("[query_text]")]\". Please check your syntax, and try again.")) return null query_list += "[word]'" @@ -1074,7 +1074,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null else if(char == "\"") if(word != "") - to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.") + to_chat(usr, span_red("SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[span_gray("[query_text]")]\" following \"[span_gray("[word]")]\". Please check your syntax, and try again.")) return null word = "\"" @@ -1094,7 +1094,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null word += char if(i > len) - to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again.") + to_chat(usr, span_red("SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[span_gray("[query_text]")]\". Please check your syntax, and try again.")) return null query_list += "[word]\"" @@ -1123,3 +1123,29 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null /obj/effect/statclick/SDQL2_VV_all/Click() usr.client.debug_variables(GLOB.sdql2_queries) + +#undef SDQL_qdel_datum + +#undef SDQL2_STATE_ERROR +#undef SDQL2_STATE_IDLE +#undef SDQL2_STATE_PRESEARCH +#undef SDQL2_STATE_SEARCHING +#undef SDQL2_STATE_EXECUTING +#undef SDQL2_STATE_SWITCHING +#undef SDQL2_STATE_HALTING + +// 2 undefs missing here still because of SDQL_2_parser + +#undef SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS +#undef SDQL2_OPTION_BLOCKING_CALLS +#undef SDQL2_OPTION_HIGH_PRIORITY +#undef SDQL2_OPTION_DO_NOT_AUTOGC + +#undef SDQL2_OPTIONS_DEFAULT + +#undef SDQL2_IS_RUNNING +#undef SDQL2_HALT_CHECK + +#undef SDQL2_TICK_CHECK + +#undef SDQL2_STAGE_SWITCH_CHECK diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 54ddd8894be..34240394502 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -1,785 +1,786 @@ -/client/var/datum/admin_help/current_ticket //the current ticket the (usually) not-admin client is dealing with - -// -//TICKET MANAGER -// - -GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) - -/datum/admin_help_tickets - var/list/active_tickets = list() - var/list/closed_tickets = list() - var/list/resolved_tickets = list() - - var/obj/effect/statclick/ticket_list/astatclick = new(null, null, AHELP_ACTIVE) - var/obj/effect/statclick/ticket_list/cstatclick = new(null, null, AHELP_CLOSED) - var/obj/effect/statclick/ticket_list/rstatclick = new(null, null, AHELP_RESOLVED) - -/datum/admin_help_tickets/Destroy() - QDEL_LIST(active_tickets) - QDEL_LIST(closed_tickets) - QDEL_LIST(resolved_tickets) - QDEL_NULL(astatclick) - QDEL_NULL(cstatclick) - QDEL_NULL(rstatclick) - return ..() - -//private -/datum/admin_help_tickets/proc/ListInsert(datum/admin_help/new_ticket) - var/list/ticket_list - switch(new_ticket.state) - if(AHELP_ACTIVE) - ticket_list = active_tickets - if(AHELP_CLOSED) - ticket_list = closed_tickets - if(AHELP_RESOLVED) - ticket_list = resolved_tickets - else - CRASH("Invalid ticket state: [new_ticket.state]") - var/num_closed = ticket_list.len - if(num_closed) - for(var/I in 1 to num_closed) - var/datum/admin_help/AH = ticket_list[I] - if(AH.id > new_ticket.id) - ticket_list.Insert(I, new_ticket) - return - ticket_list += new_ticket - -//opens the ticket listings for one of the 3 states -/datum/admin_help_tickets/proc/BrowseTickets(state) - var/list/l2b - var/title - switch(state) - if(AHELP_ACTIVE) - l2b = active_tickets - title = "Active Tickets" - if(AHELP_CLOSED) - l2b = closed_tickets - title = "Closed Tickets" - if(AHELP_RESOLVED) - l2b = resolved_tickets - title = "Resolved Tickets" - if(!l2b) - return - var/list/dat = list("[title]") - dat += "Refresh

                    " - for(var/datum/admin_help/AH as anything in l2b) - dat += "Ticket #[AH.id]: [AH.initiator_key_name]: [AH.name]
                    " - - usr << browse(dat.Join(), "window=ahelp_list[state];size=600x480") - -//Tickets statpanel -/datum/admin_help_tickets/proc/stat_entry() - var/num_disconnected = 0 - stat("== Admin Tickets ==") - stat("Active Tickets:", astatclick.update("[active_tickets.len]")) - for(var/datum/admin_help/AH as anything in active_tickets) - if(AH.initiator) - stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update()) - else - ++num_disconnected - if(num_disconnected) - stat("Disconnected:", astatclick.update("[num_disconnected]")) - stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]")) - stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]")) - -//Reassociate still open ticket if one exists -/datum/admin_help_tickets/proc/ClientLogin(client/C) - C.current_ticket = CKey2ActiveTicket(C.ckey) - if(C.current_ticket) - C.current_ticket.AddInteraction("Client reconnected.") - C.current_ticket.initiator = C - -//Dissasociate ticket -/datum/admin_help_tickets/proc/ClientLogout(client/C) - if(C.current_ticket) - C.current_ticket.AddInteraction("Client disconnected.") - C.current_ticket.initiator = null - C.current_ticket = null - -//Get a ticket given a ckey -/datum/admin_help_tickets/proc/CKey2ActiveTicket(ckey) - for(var/datum/admin_help/AH as anything in active_tickets) - if(AH.initiator_ckey == ckey) - return AH - -// -//TICKET LIST STATCLICK -// - -/obj/effect/statclick/ticket_list - var/current_state - -/obj/effect/statclick/ticket_list/New(loc, name, state) - current_state = state - ..() - -/obj/effect/statclick/ticket_list/Click() - GLOB.ahelp_tickets.BrowseTickets(current_state) - -// -//TICKET DATUM -// - -/datum/admin_help - var/id - var/name - var/state = AHELP_ACTIVE - - var/opened_at - var/closed_at - - var/client/initiator //semi-misnomer, it's the person who ahelped/was bwoinked - var/initiator_ckey - var/initiator_key_name - - var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log() - - var/obj/effect/statclick/ahelp/statclick - - var/static/ticket_counter = 0 - -//call this on its own to create a ticket, don't manually assign current_ticket -//msg is the title of the ticket: usually the ahelp text -//is_bwoink is TRUE if this ticket was started by an admin PM -/datum/admin_help/New(msg, client/C, is_bwoink) - //clean the input msg - msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN)) - if(!msg || !C || !C.mob) - qdel(src) - return - - id = ++ticket_counter - opened_at = world.time - - name = msg - - initiator = C - initiator_ckey = initiator.ckey - initiator_key_name = key_name(initiator, FALSE, TRUE) - if(initiator.current_ticket) //This is a bug - log_debug("Multiple ahelp current_tickets") - initiator.current_ticket.AddInteraction("Ticket erroneously left open by code") - initiator.current_ticket.Close() - initiator.current_ticket = src - - var/parsed_message = keywords_lookup(msg) - - statclick = new(null, src) - _interactions = list() - - if(is_bwoink) - AddInteraction("[key_name_admin(usr)] PM'd [LinkedReplyName()]") - message_admins("Ticket [TicketHref("#[id]")] created") - else - MessageNoRecipient(parsed_message) - send2adminchat() //VOREStation Add - //show it to the person adminhelping too - to_chat(C, "PM to-Admins: [name]") - - //send it to irc if nobody is on and tell us how many were on - var/admin_number_present = send2irc_adminless_only(initiator_ckey, name) - log_admin("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.") - if(admin_number_present <= 0) - to_chat(C, "No active admins are online, your adminhelp was sent to the admin discord.") //VOREStation Edit - - // Also send it to discord since that's the hip cool thing now. - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) ticket opened.", - "body" = "[key_name(initiator)] has opened a ticket. \n[msg]", - "color" = COLOR_WEBHOOK_POOR - ) - ) - - GLOB.ahelp_tickets.active_tickets += src - -/datum/admin_help/Destroy() - RemoveActive() - GLOB.ahelp_tickets.closed_tickets -= src - GLOB.ahelp_tickets.resolved_tickets -= src - return ..() - -/datum/admin_help/proc/AddInteraction(formatted_message) - _interactions += "[gameTimestamp()]: [formatted_message]" - -//private -/datum/admin_help/proc/FullMonty(ref_src) - if(!ref_src) - ref_src = "\ref[src]" - if(initiator && initiator.mob) - . = ADMIN_FULLMONTY_NONAME(initiator.mob) - else - . = "Initiator disconnected." - if(state == AHELP_ACTIVE) - . += ClosureLinks(ref_src) - -//private -/datum/admin_help/proc/ClosureLinks(ref_src) - if(!ref_src) - ref_src = "\ref[src]" - . = " (REJT)" - . += " (IC)" - . += " (CLOSE)" - . += " (RSLVE)" - . += " (HANDLE)" - -//private -/datum/admin_help/proc/LinkedReplyName(ref_src) - if(!ref_src) - ref_src = "\ref[src]" - return "[initiator_key_name]" - -//private -/datum/admin_help/proc/TicketHref(msg, ref_src, action = "ticket") - if(!ref_src) - ref_src = "\ref[src]" - return "[msg]" - -//message from the initiator without a target, all admins will see this -//won't bug irc -/datum/admin_help/proc/MessageNoRecipient(msg) - var/ref_src = "\ref[src]" - var/chat_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [msg]" - - AddInteraction("[LinkedReplyName(ref_src)]: [msg]") - //send this msg to all admins - - for(var/client/X in GLOB.admins) - if(!check_rights(R_ADMIN, 0, X)) - continue - if(X.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping)) - X << 'sound/effects/adminhelp.ogg' - window_flash(X) - to_chat(X, chat_msg) - -//Reopen a closed ticket -/datum/admin_help/proc/Reopen() - if(state == AHELP_ACTIVE) - to_chat(usr, "This ticket is already open.") - return - - if(GLOB.ahelp_tickets.CKey2ActiveTicket(initiator_ckey)) - to_chat(usr, "This user already has an active ticket, cannot reopen this one.") - return - - statclick = new(null, src) - GLOB.ahelp_tickets.active_tickets += src - GLOB.ahelp_tickets.closed_tickets -= src - GLOB.ahelp_tickets.resolved_tickets -= src - switch(state) - if(AHELP_CLOSED) - feedback_dec("ahelp_close") - if(AHELP_RESOLVED) - feedback_dec("ahelp_resolve") - state = AHELP_ACTIVE - closed_at = null - if(initiator) - initiator.current_ticket = src - - AddInteraction("Reopened by [key_name_admin(usr)]") - if(initiator) - to_chat(initiator, "Ticket [TicketHref("#[id]")] was reopened by [key_name(usr,FALSE,FALSE)].") - var/msg = "Ticket [TicketHref("#[id]")] reopened by [key_name_admin(usr)]." - message_admins(msg) - log_admin(msg) - feedback_inc("ahelp_reopen") - TicketPanel() //can only be done from here, so refresh it - - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) reopened.", - "body" = "Reopened by [key_name(usr)]." - ) - ) - -//private -/datum/admin_help/proc/RemoveActive() - if(state != AHELP_ACTIVE) - return - closed_at = world.time - QDEL_NULL(statclick) - GLOB.ahelp_tickets.active_tickets -= src - if(initiator && initiator.current_ticket == src) - initiator.current_ticket = null - -//Mark open ticket as closed/meme -/datum/admin_help/proc/Close(silent = FALSE) - if(state != AHELP_ACTIVE) - return - RemoveActive() - state = AHELP_CLOSED - GLOB.ahelp_tickets.ListInsert(src) - AddInteraction("Closed by [key_name_admin(usr)].") - if(initiator) - to_chat(initiator, "Ticket [TicketHref("#[id]")] was closed by [key_name(usr,FALSE,FALSE)].") - if(!silent) - feedback_inc("ahelp_close") - var/msg = "Ticket [TicketHref("#[id]")] closed by [key_name_admin(usr)]." - message_admins(msg) - log_admin(msg) - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) closed.", - "body" = "Closed by [key_name(usr)].", - "color" = COLOR_WEBHOOK_BAD - ) - ) - -//Mark open ticket as resolved/legitimate, returns ahelp verb -/datum/admin_help/proc/Resolve(silent = FALSE) - if(state != AHELP_ACTIVE) - return - RemoveActive() - state = AHELP_RESOLVED - GLOB.ahelp_tickets.ListInsert(src) - - AddInteraction("Resolved by [key_name_admin(usr)].") - if(initiator) - to_chat(initiator, "Ticket [TicketHref("#[id]")] was marked resolved by [key_name(usr,FALSE,FALSE)].") - if(!silent) - feedback_inc("ahelp_resolve") - var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name_admin(usr)]" - message_admins(msg) - log_admin(msg) - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) resolved.", - "body" = "Marked as Resolved by [key_name(usr)].", - "color" = COLOR_WEBHOOK_GOOD - ) - ) - -//Close and return ahelp verb, use if ticket is incoherent -/datum/admin_help/proc/Reject(key_name = key_name_admin(usr)) - if(state != AHELP_ACTIVE) - return - - if(initiator) - if(initiator.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping)) - initiator << 'sound/effects/adminhelp.ogg' - - to_chat(initiator, "- AdminHelp Rejected! -
                    \ - Your admin help was rejected.
                    \ - Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting.
                    ") - - feedback_inc("ahelp_reject") - var/msg = "Ticket [TicketHref("#[id]")] rejected by [key_name_admin(usr)]" - message_admins(msg) - log_admin(msg) - AddInteraction("Rejected by [key_name_admin(usr)].") - Close(silent = TRUE) - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) rejected.", - "body" = "Rejected by [key_name(usr)].", - "color" = COLOR_WEBHOOK_BAD - ) - ) - -//Resolve ticket with IC Issue message -/datum/admin_help/proc/ICIssue(key_name = key_name_admin(usr)) - if(state != AHELP_ACTIVE) - return - - var/msg = "- AdminHelp marked as IC issue! -
                    " - msg += "This is something that can be solved ICly, and does not currently require staff intervention.
                    " - msg += "Your AdminHelp may also be unanswerable due to ongoing events.
                    " - - if(initiator) - to_chat(initiator, msg) - - feedback_inc("ahelp_icissue") - msg = "Ticket [TicketHref("#[id]")] marked as IC by [key_name_admin(usr)]" - message_admins(msg) - log_admin(msg) - AddInteraction("Marked as IC issue by [key_name_admin(usr)]") - Resolve(silent = TRUE) - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) marked as IC issue.", - "body" = "Marked as IC Issue by [key_name(usr)].", - "color" = COLOR_WEBHOOK_BAD - ) - ) - -//Resolve ticket with IC Issue message -/datum/admin_help/proc/HandleIssue() - if(state != AHELP_ACTIVE) - return - - var/msg = "Your AdminHelp is being handled by [key_name(usr,FALSE,FALSE)] please be patient." - - if(initiator) - to_chat(initiator, msg) - - feedback_inc("ahelp_handling") - msg = "Ticket [TicketHref("#[id]")] being handled by [key_name(usr,FALSE,FALSE)]" - message_admins(msg) - log_admin(msg) - AddInteraction("[key_name_admin(usr)] is now handling this ticket.") - SSwebhooks.send( - WEBHOOK_AHELP_SENT, - list( - "name" = "Ticket ([id]) (Game ID: [game_id]) being handled.", - "body" = "[key_name(usr)] is now handling the ticket." - ) - ) - -//Show the ticket panel -/datum/admin_help/proc/TicketPanel() - tgui_interact(usr.client.mob) - -/datum/admin_help/proc/TicketPanelLegacy() - var/list/dat = list("Ticket #[id]") - var/ref_src = "\ref[src]" - dat += "

                    Admin Help Ticket #[id]: [LinkedReplyName(ref_src)]

                    " - dat += "State: " - switch(state) - if(AHELP_ACTIVE) - dat += "OPEN" - if(AHELP_RESOLVED) - dat += "RESOLVED" - if(AHELP_CLOSED) - dat += "CLOSED" - else - dat += "UNKNOWN" - dat += "[GLOB.TAB][TicketHref("Refresh", ref_src)][GLOB.TAB][TicketHref("Re-Title", ref_src, "retitle")]" - if(state != AHELP_ACTIVE) - dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]" - dat += "

                    Opened at: [gameTimestamp(wtime = opened_at)] (Approx [(world.time - opened_at) / 600] minutes ago)" - if(closed_at) - dat += "
                    Closed at: [gameTimestamp(wtime = closed_at)] (Approx [(world.time - closed_at) / 600] minutes ago)" - dat += "

                    " - if(initiator) - dat += "Actions: [FullMonty(ref_src)]
                    " - else - dat += "DISCONNECTED[GLOB.TAB][ClosureLinks(ref_src)]
                    " - dat += "
                    Log:

                    " - for(var/I in _interactions) - dat += "[I]
                    " - - usr << browse(dat.Join(), "window=ahelp[id];size=620x480") - -/datum/admin_help/proc/Retitle() - var/new_title = tgui_input_text(usr, "Enter a title for the ticket", "Rename Ticket", name) - if(new_title) - name = new_title - //not saying the original name cause it could be a long ass message - var/msg = "Ticket [TicketHref("#[id]")] titled [name] by [key_name_admin(usr)]" - message_admins(msg) - log_admin(msg) - TicketPanel() //we have to be here to do this - -/datum/admin_help/tgui_fallback(payload) - if(..()) - return - - TicketPanelLegacy() - -/datum/admin_help/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AdminTicketPanel", "Ticket #[id] - [LinkedReplyName("\ref[src]")]") - ui.open() - -/datum/admin_help/tgui_state(mob/user) - return GLOB.tgui_admin_state - -/datum/admin_help/tgui_data(mob/user) - var/list/data = list() - - data["id"] = id - - var/ref_src = "\ref[src]" - data["title"] = name - data["name"] = LinkedReplyName(ref_src) - - switch(state) - if(AHELP_ACTIVE) - data["state"] = "open" - if(AHELP_RESOLVED) - data["state"] = "resolved" - if(AHELP_CLOSED) - data["state"] = "closed" - else - data["state"] = "unknown" - - data["opened_at"] = (world.time - opened_at) - data["closed_at"] = (world.time - closed_at) - data["opened_at_date"] = gameTimestamp(wtime = opened_at) - data["closed_at_date"] = gameTimestamp(wtime = closed_at) - - data["actions"] = FullMonty(ref_src) - - data["log"] = _interactions - - return data - -/datum/admin_help/tgui_act(action, params) - if(..()) - return - switch(action) - if("retitle") - Retitle() - . = TRUE - if("reopen") - Reopen() - . = TRUE - if("legacy") - TicketPanelLegacy() - . = TRUE - -//Forwarded action from admin/Topic -/datum/admin_help/proc/Action(action) - testing("Ahelp action: [action]") - switch(action) - if("ticket") - TicketPanel() - if("retitle") - Retitle() - if("reject") - Reject() - if("reply") - usr.client.cmd_ahelp_reply(initiator) - if("icissue") - ICIssue() - if("close") - Close() - if("resolve") - Resolve() - if("handleissue") - HandleIssue() - if("reopen") - Reopen() - -// -// TICKET STATCLICK -// - -/obj/effect/statclick/ahelp - var/datum/admin_help/ahelp_datum - -/obj/effect/statclick/ahelp/New(loc, datum/admin_help/AH) - ahelp_datum = AH - ..(loc) - -/obj/effect/statclick/ahelp/update() - return ..(ahelp_datum.name) - -/obj/effect/statclick/ahelp/Click() - ahelp_datum.TicketPanel() - -/obj/effect/statclick/ahelp/Destroy() - ahelp_datum = null - return ..() - -// -// CLIENT PROCS -// - -/client/verb/adminhelp(msg as text) - set category = "Admin" - set name = "Adminhelp" - - if(say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") - return - - //handle muting and automuting - if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") - return - if(handle_spam_prevention(msg,MUTE_ADMINHELP)) - return - - if(!msg) - return - - //remove out adminhelp verb temporarily to prevent spamming of admins. - src.verbs -= /client/verb/adminhelp - spawn(1200) - src.verbs += /client/verb/adminhelp // 2 minute cool-down for adminhelps - - feedback_add_details("admin_verb","Adminhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(current_ticket) - if(tgui_alert(usr, "You already have a ticket open. Is this for the same issue?","Duplicate?",list("Yes","No")) != "No") - if(current_ticket) - current_ticket.MessageNoRecipient(msg) - to_chat(usr, "PM to-Admins: [msg]") - return - else - to_chat(usr, "Ticket not found, creating new one...") - else - current_ticket.AddInteraction("[key_name_admin(usr)] opened a new ticket.") - current_ticket.Close() - - new /datum/admin_help(msg, src, FALSE) - -//admin proc -/client/proc/cmd_admin_ticket_panel() - set name = "Show Ticket List" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT, TRUE)) - return - - var/browse_to - - switch(tgui_input_list(usr, "Display which ticket list?", "List Choice", list("Active Tickets", "Closed Tickets", "Resolved Tickets"))) - if("Active Tickets") - browse_to = AHELP_ACTIVE - if("Closed Tickets") - browse_to = AHELP_CLOSED - if("Resolved Tickets") - browse_to = AHELP_RESOLVED - else - return - - GLOB.ahelp_tickets.BrowseTickets(browse_to) - -// -// LOGGING -// - -//Use this proc when an admin takes action that may be related to an open ticket on what -//what can be a client, ckey, or mob -/proc/admin_ticket_log(what, message) - var/client/C - var/mob/Mob = what - if(istype(Mob)) - C = Mob.client - else - C = what - if(istype(C) && C.current_ticket) - C.current_ticket.AddInteraction(message) - return C.current_ticket - if(istext(what)) //ckey - var/datum/admin_help/AH = GLOB.ahelp_tickets.CKey2ActiveTicket(what) - if(AH) - AH.AddInteraction(message) - return AH - -// -// HELPER PROCS -// - -/proc/get_admin_counts(requiredflags = R_BAN) - . = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list()) - for(var/client/X in GLOB.admins) - .["total"] += X - if(requiredflags != 0 && !check_rights(rights_required = requiredflags, show_msg = FALSE, C = X)) - .["noflags"] += X - else if(X.is_afk()) - .["afk"] += X - else if(X.holder.fakekey) - .["stealth"] += X - else - .["present"] += X - -/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) - var/list/adm = get_admin_counts() - var/list/activemins = adm["present"] - . = activemins.len - if(. <= 0) - var/final = "" - var/list/afkmins = adm["afk"] - var/list/stealthmins = adm["stealth"] - var/list/powerlessmins = adm["noflags"] - var/list/allmins = adm["total"] - if(!afkmins.len && !stealthmins.len && !powerlessmins.len) - final = "[msg] - No admins online" - else - final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] " - send2irc(source,final) - -/proc/ircadminwho() - var/list/message = list("Admins: ") - var/list/admin_keys = list() - for(var/client/C as anything in GLOB.admins) - admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]" - - for(var/admin in admin_keys) - if(LAZYLEN(admin_keys) > 1) - message += ", [admin]" - else - message += "[admin]" - - return jointext(message, "") - -/proc/keywords_lookup(msg,irc) - - //This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! - var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i") - - //explode the input msg into a list - var/list/msglist = splittext(msg, " ") - - //generate keywords lookup - var/list/surnames = list() - var/list/forenames = list() - var/list/ckeys = list() - var/founds = "" - for(var/mob/M in mob_list) - var/list/indexing = list(M.real_name, M.name) - if(M.mind) - indexing += M.mind.name - - for(var/string in indexing) - var/list/L = splittext(string, " ") - var/surname_found = 0 - //surnames - for(var/i=L.len, i>=1, i--) - var/word = ckey(L[i]) - if(word) - surnames[word] = M - surname_found = i - break - //forenames - for(var/i=1, i(?|F)
                    " - continue - msg += "[original_word] " - if(irc) - if(founds == "") - return "Search Failed" - else - return founds - - return msg +/client/var/datum/admin_help/current_ticket //the current ticket the (usually) not-admin client is dealing with + +// +//TICKET MANAGER +// + +GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) + +/datum/admin_help_tickets + var/list/active_tickets = list() + var/list/closed_tickets = list() + var/list/resolved_tickets = list() + + var/obj/effect/statclick/ticket_list/astatclick = new(null, null, AHELP_ACTIVE) + var/obj/effect/statclick/ticket_list/cstatclick = new(null, null, AHELP_CLOSED) + var/obj/effect/statclick/ticket_list/rstatclick = new(null, null, AHELP_RESOLVED) + +/datum/admin_help_tickets/Destroy() + QDEL_LIST(active_tickets) + QDEL_LIST(closed_tickets) + QDEL_LIST(resolved_tickets) + QDEL_NULL(astatclick) + QDEL_NULL(cstatclick) + QDEL_NULL(rstatclick) + return ..() + +//private +/datum/admin_help_tickets/proc/ListInsert(datum/admin_help/new_ticket) + var/list/ticket_list + switch(new_ticket.state) + if(AHELP_ACTIVE) + ticket_list = active_tickets + if(AHELP_CLOSED) + ticket_list = closed_tickets + if(AHELP_RESOLVED) + ticket_list = resolved_tickets + else + CRASH("Invalid ticket state: [new_ticket.state]") + var/num_closed = ticket_list.len + if(num_closed) + for(var/I in 1 to num_closed) + var/datum/admin_help/AH = ticket_list[I] + if(AH.id > new_ticket.id) + ticket_list.Insert(I, new_ticket) + return + ticket_list += new_ticket + +//opens the ticket listings for one of the 3 states +/datum/admin_help_tickets/proc/BrowseTickets(state) + var/list/l2b + var/title + switch(state) + if(AHELP_ACTIVE) + l2b = active_tickets + title = "Active Tickets" + if(AHELP_CLOSED) + l2b = closed_tickets + title = "Closed Tickets" + if(AHELP_RESOLVED) + l2b = resolved_tickets + title = "Resolved Tickets" + if(!l2b) + return + var/list/dat = list("[title]") + dat += "Refresh

                    " + for(var/datum/admin_help/AH as anything in l2b) + dat += "Ticket #[AH.id]: [AH.initiator_key_name]: [AH.name]
                    " + + usr << browse(dat.Join(), "window=ahelp_list[state];size=600x480") + +//Tickets statpanel +/datum/admin_help_tickets/proc/stat_entry() + var/num_disconnected = 0 + stat("== Admin Tickets ==") + stat("Active Tickets:", astatclick.update("[active_tickets.len]")) + for(var/datum/admin_help/AH as anything in active_tickets) + if(AH.initiator) + stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update()) + else + ++num_disconnected + if(num_disconnected) + stat("Disconnected:", astatclick.update("[num_disconnected]")) + stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]")) + stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]")) + +//Reassociate still open ticket if one exists +/datum/admin_help_tickets/proc/ClientLogin(client/C) + C.current_ticket = CKey2ActiveTicket(C.ckey) + if(C.current_ticket) + C.current_ticket.AddInteraction("Client reconnected.") + C.current_ticket.initiator = C + +//Dissasociate ticket +/datum/admin_help_tickets/proc/ClientLogout(client/C) + if(C.current_ticket) + C.current_ticket.AddInteraction("Client disconnected.") + C.current_ticket.initiator = null + C.current_ticket = null + +//Get a ticket given a ckey +/datum/admin_help_tickets/proc/CKey2ActiveTicket(ckey) + for(var/datum/admin_help/AH as anything in active_tickets) + if(AH.initiator_ckey == ckey) + return AH + +// +//TICKET LIST STATCLICK +// + +/obj/effect/statclick/ticket_list + var/current_state + +/obj/effect/statclick/ticket_list/New(loc, name, state) + current_state = state + ..() + +/obj/effect/statclick/ticket_list/Click() + GLOB.ahelp_tickets.BrowseTickets(current_state) + +// +//TICKET DATUM +// + +/datum/admin_help + var/id + var/name + var/state = AHELP_ACTIVE + + var/opened_at + var/closed_at + + var/client/initiator //semi-misnomer, it's the person who ahelped/was bwoinked + var/initiator_ckey + var/initiator_key_name + + var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log() + + var/obj/effect/statclick/ahelp/statclick + + var/static/ticket_counter = 0 + +//call this on its own to create a ticket, don't manually assign current_ticket +//msg is the title of the ticket: usually the ahelp text +//is_bwoink is TRUE if this ticket was started by an admin PM +/datum/admin_help/New(msg, client/C, is_bwoink) + //clean the input msg + msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN)) + if(!msg || !C || !C.mob) + qdel(src) + return + + id = ++ticket_counter + opened_at = world.time + + name = msg + + initiator = C + initiator_ckey = initiator.ckey + initiator_key_name = key_name(initiator, FALSE, TRUE) + if(initiator.current_ticket) //This is a bug + log_debug("Multiple ahelp current_tickets") + initiator.current_ticket.AddInteraction("Ticket erroneously left open by code") + initiator.current_ticket.Close() + initiator.current_ticket = src + + var/parsed_message = keywords_lookup(msg) + + statclick = new(null, src) + _interactions = list() + + if(is_bwoink) + AddInteraction("[key_name_admin(usr)] PM'd [LinkedReplyName()]") + message_admins(span_blue("Ticket [TicketHref("#[id]")] created")) + else + MessageNoRecipient(parsed_message) + send2adminchat() //VOREStation Add + //show it to the person adminhelping too + to_chat(C, "PM to-Admins: [name]") + + //send it to irc if nobody is on and tell us how many were on + var/admin_number_present = send2irc_adminless_only(initiator_ckey, name) + log_admin("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.") + if(admin_number_present <= 0) + to_chat(C, "No active admins are online, your adminhelp was sent to the admin discord.") //VOREStation Edit + + // Also send it to discord since that's the hip cool thing now. + SSwebhooks.send( + WEBHOOK_AHELP_SENT, + list( + "name" = "Ticket ([id]) (Game ID: [game_id]) ticket opened.", + "body" = "[key_name(initiator)] has opened a ticket. \n[msg]", + "color" = COLOR_WEBHOOK_POOR + ) + ) + + GLOB.ahelp_tickets.active_tickets += src + +/datum/admin_help/Destroy() + RemoveActive() + GLOB.ahelp_tickets.closed_tickets -= src + GLOB.ahelp_tickets.resolved_tickets -= src + return ..() + +/datum/admin_help/proc/AddInteraction(formatted_message) + _interactions += "[gameTimestamp()]: [formatted_message]" + +//private +/datum/admin_help/proc/FullMonty(ref_src) + if(!ref_src) + ref_src = "\ref[src]" + if(initiator && initiator.mob) + . = ADMIN_FULLMONTY_NONAME(initiator.mob) + else + . = "Initiator disconnected." + if(state == AHELP_ACTIVE) + . += ClosureLinks(ref_src) + +//private +/datum/admin_help/proc/ClosureLinks(ref_src) + if(!ref_src) + ref_src = "\ref[src]" + . = " (REJT)" + . += " (IC)" + . += " (CLOSE)" + . += " (RSLVE)" + . += " (HANDLE)" + +//private +/datum/admin_help/proc/LinkedReplyName(ref_src) + if(!ref_src) + ref_src = "\ref[src]" + return "[initiator_key_name]" + +//private +/datum/admin_help/proc/TicketHref(msg, ref_src, action = "ticket") + if(!ref_src) + ref_src = "\ref[src]" + return "[msg]" + +//message from the initiator without a target, all admins will see this +//won't bug irc +/datum/admin_help/proc/MessageNoRecipient(msg) + var/ref_src = "\ref[src]" + var/chat_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [msg]" + + AddInteraction("[LinkedReplyName(ref_src)]: [msg]") + //send this msg to all admins + + for(var/client/X in GLOB.admins) + if(!check_rights(R_ADMIN, 0, X)) + continue + if(X.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping)) + X << 'sound/effects/adminhelp.ogg' + window_flash(X) + to_chat(X, chat_msg) + +//Reopen a closed ticket +/datum/admin_help/proc/Reopen() + if(state == AHELP_ACTIVE) + to_chat(usr, "This ticket is already open.") + return + + if(GLOB.ahelp_tickets.CKey2ActiveTicket(initiator_ckey)) + to_chat(usr, "This user already has an active ticket, cannot reopen this one.") + return + + statclick = new(null, src) + GLOB.ahelp_tickets.active_tickets += src + GLOB.ahelp_tickets.closed_tickets -= src + GLOB.ahelp_tickets.resolved_tickets -= src + switch(state) + if(AHELP_CLOSED) + feedback_dec("ahelp_close") + if(AHELP_RESOLVED) + feedback_dec("ahelp_resolve") + state = AHELP_ACTIVE + closed_at = null + if(initiator) + initiator.current_ticket = src + + AddInteraction("Reopened by [key_name_admin(usr)]") + if(initiator) + to_chat(initiator, "[span_purple("Ticket [TicketHref("#[id]")] was reopened by [key_name(usr,FALSE,FALSE)].")]") + var/msg = "Ticket [TicketHref("#[id]")] reopened by [key_name_admin(usr)]." + message_admins(msg) + log_admin(msg) + feedback_inc("ahelp_reopen") + TicketPanel() //can only be done from here, so refresh it + + SSwebhooks.send( + WEBHOOK_AHELP_SENT, + list( + "name" = "Ticket ([id]) (Game ID: [game_id]) reopened.", + "body" = "Reopened by [key_name(usr)]." + ) + ) + +//private +/datum/admin_help/proc/RemoveActive() + if(state != AHELP_ACTIVE) + return + closed_at = world.time + QDEL_NULL(statclick) + GLOB.ahelp_tickets.active_tickets -= src + if(initiator && initiator.current_ticket == src) + initiator.current_ticket = null + +//Mark open ticket as closed/meme +/datum/admin_help/proc/Close(silent = FALSE) + if(state != AHELP_ACTIVE) + return + RemoveActive() + state = AHELP_CLOSED + GLOB.ahelp_tickets.ListInsert(src) + AddInteraction("Closed by [key_name_admin(usr)].") + if(initiator) + to_chat(initiator, "[span_red("Ticket [TicketHref("#[id]")] was closed by [key_name(usr,FALSE,FALSE)].")]") + if(!silent) + feedback_inc("ahelp_close") + var/msg = "Ticket [TicketHref("#[id]")] closed by [key_name_admin(usr)]." + message_admins(msg) + log_admin(msg) + SSwebhooks.send( + WEBHOOK_AHELP_SENT, + list( + "name" = "Ticket ([id]) (Game ID: [game_id]) closed.", + "body" = "Closed by [key_name(usr)].", + "color" = COLOR_WEBHOOK_BAD + ) + ) + +//Mark open ticket as resolved/legitimate, returns ahelp verb +/datum/admin_help/proc/Resolve(silent = FALSE) + if(state != AHELP_ACTIVE) + return + RemoveActive() + state = AHELP_RESOLVED + GLOB.ahelp_tickets.ListInsert(src) + + AddInteraction("Resolved by [key_name_admin(usr)].") + if(initiator) + to_chat(initiator, "[span_green("Ticket [TicketHref("#[id]")] was marked resolved by [key_name(usr,FALSE,FALSE)].")]") + if(!silent) + feedback_inc("ahelp_resolve") + var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name_admin(usr)]" + message_admins(msg) + log_admin(msg) + SSwebhooks.send( + WEBHOOK_AHELP_SENT, + list( + "name" = "Ticket ([id]) (Game ID: [game_id]) resolved.", + "body" = "Marked as Resolved by [key_name(usr)].", + "color" = COLOR_WEBHOOK_GOOD + ) + ) + +//Close and return ahelp verb, use if ticket is incoherent +/datum/admin_help/proc/Reject(key_name = key_name_admin(usr)) + if(state != AHELP_ACTIVE) + return + + if(initiator) + if(initiator.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping)) + initiator << 'sound/effects/adminhelp.ogg' + + to_chat(initiator, "[span_red("- AdminHelp Rejected! -")]
                    \ + [span_red("Your admin help was rejected.")]
                    \ + Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting.
                    ") + + feedback_inc("ahelp_reject") + var/msg = "Ticket [TicketHref("#[id]")] rejected by [key_name_admin(usr)]" + message_admins(msg) + log_admin(msg) + AddInteraction("Rejected by [key_name_admin(usr)].") + Close(silent = TRUE) + SSwebhooks.send( + WEBHOOK_AHELP_SENT, + list( + "name" = "Ticket ([id]) (Game ID: [game_id]) rejected.", + "body" = "Rejected by [key_name(usr)].", + "color" = COLOR_WEBHOOK_BAD + ) + ) + +//Resolve ticket with IC Issue message +/datum/admin_help/proc/ICIssue(key_name = key_name_admin(usr)) + if(state != AHELP_ACTIVE) + return + + var/msg = "[span_red("- AdminHelp marked as IC issue! -")]
                    " + msg += "[span_red("This is something that can be solved ICly, and does not currently require staff intervention.")]
                    " + msg += "[span_red("Your AdminHelp may also be unanswerable due to ongoing events.")]
                    " + + if(initiator) + to_chat(initiator, msg) + + feedback_inc("ahelp_icissue") + msg = "Ticket [TicketHref("#[id]")] marked as IC by [key_name_admin(usr)]" + message_admins(msg) + log_admin(msg) + AddInteraction("Marked as IC issue by [key_name_admin(usr)]") + Resolve(silent = TRUE) + SSwebhooks.send( + WEBHOOK_AHELP_SENT, + list( + "name" = "Ticket ([id]) (Game ID: [game_id]) marked as IC issue.", + "body" = "Marked as IC Issue by [key_name(usr)].", + "color" = COLOR_WEBHOOK_BAD + ) + ) + +//Resolve ticket with IC Issue message +/datum/admin_help/proc/HandleIssue() + if(state != AHELP_ACTIVE) + return + + var/msg = span_red("Your AdminHelp is being handled by [key_name(usr,FALSE,FALSE)] please be patient.") + + if(initiator) + to_chat(initiator, msg) + + feedback_inc("ahelp_handling") + msg = "Ticket [TicketHref("#[id]")] being handled by [key_name(usr,FALSE,FALSE)]" + message_admins(msg) + log_admin(msg) + AddInteraction("[key_name_admin(usr)] is now handling this ticket.") + var/query_string = "type=admintake" + query_string += "&key=[url_encode(config.chat_webhook_key)]" + query_string += "&admin=[url_encode(key_name(usr))]" + query_string += "&user=[url_encode(key_name(initiator))]" + world.Export("[config.chat_webhook_url]?[query_string]") + + + + +//Show the ticket panel +/datum/admin_help/proc/TicketPanel() + tgui_interact(usr.client.mob) + +/datum/admin_help/proc/TicketPanelLegacy() + var/list/dat = list("Ticket #[id]") + var/ref_src = "\ref[src]" + dat += "

                    Admin Help Ticket #[id]: [LinkedReplyName(ref_src)]

                    " + dat += "State: " + switch(state) + if(AHELP_ACTIVE) + dat += "OPEN" + if(AHELP_RESOLVED) + dat += "RESOLVED" + if(AHELP_CLOSED) + dat += "CLOSED" + else + dat += "UNKNOWN" + dat += "[GLOB.TAB][TicketHref("Refresh", ref_src)][GLOB.TAB][TicketHref("Re-Title", ref_src, "retitle")]" + if(state != AHELP_ACTIVE) + dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]" + dat += "

                    Opened at: [gameTimestamp(wtime = opened_at)] (Approx [(world.time - opened_at) / 600] minutes ago)" + if(closed_at) + dat += "
                    Closed at: [gameTimestamp(wtime = closed_at)] (Approx [(world.time - closed_at) / 600] minutes ago)" + dat += "

                    " + if(initiator) + dat += "Actions: [FullMonty(ref_src)]
                    " + else + dat += "DISCONNECTED[GLOB.TAB][ClosureLinks(ref_src)]
                    " + dat += "
                    Log:

                    " + for(var/I in _interactions) + dat += "[I]
                    " + + usr << browse(dat.Join(), "window=ahelp[id];size=620x480") + +/datum/admin_help/proc/Retitle() + var/new_title = tgui_input_text(usr, "Enter a title for the ticket", "Rename Ticket", name) + if(new_title) + name = new_title + //not saying the original name cause it could be a long ass message + var/msg = "Ticket [TicketHref("#[id]")] titled [name] by [key_name_admin(usr)]" + message_admins(msg) + log_admin(msg) + TicketPanel() //we have to be here to do this + +/datum/admin_help/tgui_fallback(payload) + if(..()) + return + + TicketPanelLegacy() + +/datum/admin_help/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AdminTicketPanel", "Ticket #[id] - [LinkedReplyName("\ref[src]")]") + ui.open() + +/datum/admin_help/tgui_state(mob/user) + return GLOB.tgui_admin_state + +/datum/admin_help/tgui_data(mob/user) + var/list/data = list() + + data["id"] = id + + var/ref_src = "\ref[src]" + data["title"] = name + data["name"] = LinkedReplyName(ref_src) + + switch(state) + if(AHELP_ACTIVE) + data["state"] = "open" + if(AHELP_RESOLVED) + data["state"] = "resolved" + if(AHELP_CLOSED) + data["state"] = "closed" + else + data["state"] = "unknown" + + data["opened_at"] = (world.time - opened_at) + data["closed_at"] = (world.time - closed_at) + data["opened_at_date"] = gameTimestamp(wtime = opened_at) + data["closed_at_date"] = gameTimestamp(wtime = closed_at) + + data["actions"] = FullMonty(ref_src) + + data["log"] = _interactions + + return data + +/datum/admin_help/tgui_act(action, params) + if(..()) + return + switch(action) + if("retitle") + Retitle() + . = TRUE + if("reopen") + Reopen() + . = TRUE + if("legacy") + TicketPanelLegacy() + . = TRUE + +//Forwarded action from admin/Topic +/datum/admin_help/proc/Action(action) + testing("Ahelp action: [action]") + switch(action) + if("ticket") + TicketPanel() + if("retitle") + Retitle() + if("reject") + Reject() + if("reply") + usr.client.cmd_ahelp_reply(initiator) + if("icissue") + ICIssue() + if("close") + Close() + if("resolve") + Resolve() + if("handleissue") + HandleIssue() + if("reopen") + Reopen() + +// +// TICKET STATCLICK +// + +/obj/effect/statclick/ahelp + var/datum/admin_help/ahelp_datum + +/obj/effect/statclick/ahelp/New(loc, datum/admin_help/AH) + ahelp_datum = AH + ..(loc) + +/obj/effect/statclick/ahelp/update() + return ..(ahelp_datum.name) + +/obj/effect/statclick/ahelp/Click() + ahelp_datum.TicketPanel() + +/obj/effect/statclick/ahelp/Destroy() + ahelp_datum = null + return ..() + +// +// CLIENT PROCS +// + +/client/verb/adminhelp(msg as text) + set category = "Admin" + set name = "Adminhelp" + + if(say_disabled) //This is here to try to identify lag problems + to_chat(usr, "Speech is currently admin-disabled.") + return + + //handle muting and automuting + if(prefs.muted & MUTE_ADMINHELP) + to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") + return + if(handle_spam_prevention(msg,MUTE_ADMINHELP)) + return + + if(!msg) + return + + //remove out adminhelp verb temporarily to prevent spamming of admins. + src.verbs -= /client/verb/adminhelp + spawn(1200) + src.verbs += /client/verb/adminhelp // 2 minute cool-down for adminhelps + + feedback_add_details("admin_verb","Adminhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(current_ticket) + if(tgui_alert(usr, "You already have a ticket open. Is this for the same issue?","Duplicate?",list("Yes","No")) != "No") + if(current_ticket) + current_ticket.MessageNoRecipient(msg) + to_chat(usr, "PM to-Admins: [msg]") + return + else + to_chat(usr, "Ticket not found, creating new one...") + else + current_ticket.AddInteraction("[key_name_admin(usr)] opened a new ticket.") + current_ticket.Close() + + new /datum/admin_help(msg, src, FALSE) + +//admin proc +/client/proc/cmd_admin_ticket_panel() + set name = "Show Ticket List" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT, TRUE)) + return + + var/browse_to + + switch(tgui_input_list(usr, "Display which ticket list?", "List Choice", list("Active Tickets", "Closed Tickets", "Resolved Tickets"))) + if("Active Tickets") + browse_to = AHELP_ACTIVE + if("Closed Tickets") + browse_to = AHELP_CLOSED + if("Resolved Tickets") + browse_to = AHELP_RESOLVED + else + return + + GLOB.ahelp_tickets.BrowseTickets(browse_to) + +// +// LOGGING +// + +//Use this proc when an admin takes action that may be related to an open ticket on what +//what can be a client, ckey, or mob +/proc/admin_ticket_log(what, message) + var/client/C + var/mob/Mob = what + if(istype(Mob)) + C = Mob.client + else + C = what + if(istype(C) && C.current_ticket) + C.current_ticket.AddInteraction(message) + return C.current_ticket + if(istext(what)) //ckey + var/datum/admin_help/AH = GLOB.ahelp_tickets.CKey2ActiveTicket(what) + if(AH) + AH.AddInteraction(message) + return AH + +// +// HELPER PROCS +// + +/proc/get_admin_counts(requiredflags = R_BAN) + . = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list()) + for(var/client/X in GLOB.admins) + .["total"] += X + if(requiredflags != 0 && !check_rights(rights_required = requiredflags, show_msg = FALSE, C = X)) + .["noflags"] += X + else if(X.is_afk()) + .["afk"] += X + else if(X.holder.fakekey) + .["stealth"] += X + else + .["present"] += X + +/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) + var/list/adm = get_admin_counts() + var/list/activemins = adm["present"] + . = activemins.len + if(. <= 0) + var/final = "" + var/list/afkmins = adm["afk"] + var/list/stealthmins = adm["stealth"] + var/list/powerlessmins = adm["noflags"] + var/list/allmins = adm["total"] + if(!afkmins.len && !stealthmins.len && !powerlessmins.len) + final = "[msg] - No admins online" + else + final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] " + send2irc(source,final) + +/proc/ircadminwho() + var/list/message = list("Admins: ") + var/list/admin_keys = list() + for(var/client/C as anything in GLOB.admins) + admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]" + + for(var/admin in admin_keys) + if(LAZYLEN(admin_keys) > 1) + message += ", [admin]" + else + message += "[admin]" + + return jointext(message, "") + +/proc/keywords_lookup(msg,irc) + + //This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! + var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i") + + //explode the input msg into a list + var/list/msglist = splittext(msg, " ") + + //generate keywords lookup + var/list/surnames = list() + var/list/forenames = list() + var/list/ckeys = list() + var/founds = "" + for(var/mob/M in mob_list) + var/list/indexing = list(M.real_name, M.name) + if(M.mind) + indexing += M.mind.name + + for(var/string in indexing) + var/list/L = splittext(string, " ") + var/surname_found = 0 + //surnames + for(var/i=L.len, i>=1, i--) + var/word = ckey(L[i]) + if(word) + surnames[word] = M + surname_found = i + break + //forenames + for(var/i=1, i(?|F)
                    " + continue + msg += "[original_word] " + if(irc) + if(founds == "") + return "Search Failed" + else + return founds + + return msg diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 4fa7f2862cf..edefc09b555 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -1,63 +1,63 @@ -/client/proc/cmd_admin_say(msg as text) - set category = "Special Verbs" - set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite - set hidden = 1 - if(!check_rights(R_ADMIN)) //VOREStation Edit - return - - msg = sanitize(msg) - if(!msg) - return - - log_adminsay(msg,src) - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN, 0, C)) - to_chat(C, "" + create_text_tag("admin", "ADMIN:", C) + " [key_name(usr, 1)]([admin_jump_link(mob, src)]): [msg]") - - feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_mod_say(msg as text) - set category = "Special Verbs" - set name = "Msay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_SERVER)) //VOREStation Edit - return - - msg = sanitize(msg) - log_modsay(msg,src) - - if (!msg) - return - - var/sender_name = key_name(usr, 1) - if(check_rights(R_ADMIN, 0)) - sender_name = "[sender_name]" - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD|R_SERVER)) //VOREStation Edit - to_chat(C, "" + create_text_tag("mod", "MOD:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") - - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_event_say(msg as text) - set category = "Special Verbs" - set name = "Esay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_EVENT|R_SERVER)) //VOREStation Edit - return - - msg = sanitize(msg) - log_eventsay(msg,src) - - if (!msg) - return - - var/sender_name = key_name(usr, 1) - if(check_rights(R_ADMIN, 0)) - sender_name = "[sender_name]" - for(var/client/C in GLOB.admins) - to_chat(C, "" + create_text_tag("event", "EVENT:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") - - feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/cmd_admin_say(msg as text) + set category = "Special Verbs" + set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite + set hidden = 1 + if(!check_rights(R_ADMIN)) //VOREStation Edit + return + + msg = sanitize(msg) + if(!msg) + return + + log_adminsay(msg,src) + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN, 0, C)) + to_chat(C, "" + create_text_tag("admin", "ADMIN:", C) + " [key_name(usr, 1)]([admin_jump_link(mob, src)]): [msg]") + + feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_mod_say(msg as text) + set category = "Special Verbs" + set name = "Msay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_SERVER)) //VOREStation Edit + return + + msg = sanitize(msg) + log_modsay(msg,src) + + if (!msg) + return + + var/sender_name = key_name(usr, 1) + if(check_rights(R_ADMIN, 0)) + sender_name = "[sender_name]" + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD|R_SERVER)) //VOREStation Edit + to_chat(C, "" + create_text_tag("mod", "MOD:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") + + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_event_say(msg as text) + set category = "Special Verbs" + set name = "Esay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_EVENT|R_SERVER)) //VOREStation Edit + return + + msg = sanitize(msg) + log_eventsay(msg,src) + + if (!msg) + return + + var/sender_name = key_name(usr, 1) + if(check_rights(R_ADMIN, 0)) + sender_name = "[sender_name]" + for(var/client/C in GLOB.admins) + to_chat(C, "" + create_text_tag("event", "EVENT:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") + + feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index 3927c20899f..64c33419d0d 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -1,60 +1,60 @@ -/client/proc/atmosscan() - set category = "Mapping" - set name = "Check Piping" - set background = 1 - if(!src.holder) - return - - feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(tgui_alert(usr, "WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", list("No", "Yes")) == "No") - return - - to_chat(usr, "Checking for disconnected pipes...") - //all plumbing - yes, some things might get stated twice, doesn't matter. - for (var/obj/machinery/atmospherics/plumbing in machines) - if (plumbing.nodealert) - to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") - - //Manifolds - for (var/obj/machinery/atmospherics/pipe/manifold/pipe in machines) - if (!pipe.node1 || !pipe.node2 || !pipe.node3) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - //Pipes - for (var/obj/machinery/atmospherics/pipe/simple/pipe in machines) - if (!pipe.node1 || !pipe.node2) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - to_chat(usr, "Checking for overlapping pipes...") - next_turf: - for(var/turf/T in world) - for(var/dir in cardinal) - var/list/connect_types = list(1 = 0, 2 = 0, 3 = 0) - for(var/obj/machinery/atmospherics/pipe in T) - if(dir & pipe.initialize_directions) - for(var/connect_type in pipe.connect_types) - connect_types[connect_type] += 1 - if(connect_types[1] > 1 || connect_types[2] > 1 || connect_types[3] > 1) - to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") - continue next_turf - to_chat(usr, "Done") - -/client/proc/powerdebug() - set category = "Mapping" - set name = "Check Power" - if(!src.holder) - return - - feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - for (var/datum/powernet/PN in powernets) - if (!PN.nodes || !PN.nodes.len) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") - - if (!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") \ No newline at end of file +/client/proc/atmosscan() + set category = "Mapping" + set name = "Check Piping" + set background = 1 + if(!src.holder) + return + + feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(tgui_alert(usr, "WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", list("No", "Yes")) == "No") + return + + to_chat(usr, "Checking for disconnected pipes...") + //all plumbing - yes, some things might get stated twice, doesn't matter. + for (var/obj/machinery/atmospherics/plumbing in machines) + if (plumbing.nodealert) + to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") + + //Manifolds + for (var/obj/machinery/atmospherics/pipe/manifold/pipe in machines) + if (!pipe.node1 || !pipe.node2 || !pipe.node3) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + //Pipes + for (var/obj/machinery/atmospherics/pipe/simple/pipe in machines) + if (!pipe.node1 || !pipe.node2) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + to_chat(usr, "Checking for overlapping pipes...") + next_turf: + for(var/turf/T in world) + for(var/dir in cardinal) + var/list/connect_types = list(1 = 0, 2 = 0, 3 = 0) + for(var/obj/machinery/atmospherics/pipe in T) + if(dir & pipe.initialize_directions) + for(var/connect_type in pipe.connect_types) + connect_types[connect_type] += 1 + if(connect_types[1] > 1 || connect_types[2] > 1 || connect_types[3] > 1) + to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") + continue next_turf + to_chat(usr, "Done") + +/client/proc/powerdebug() + set category = "Mapping" + set name = "Check Power" + if(!src.holder) + return + + feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + for (var/datum/powernet/PN in SSmachines.powernets) + if (!PN.nodes || !PN.nodes.len) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") + + if (!PN.cables || (PN.cables.len < 10)) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm index be587b6f412..283c6a5031e 100644 --- a/code/modules/admin/verbs/buildmode.dm +++ b/code/modules/admin/verbs/buildmode.dm @@ -1,845 +1,845 @@ -#define BUILDMODE_BASIC 1 -#define BUILDMODE_ADVANCED 2 -#define BUILDMODE_EDIT 3 -#define BUILDMODE_THROW 4 -#define BUILDMODE_ROOM 5 -#define BUILDMODE_LADDER 6 -#define BUILDMODE_CONTENTS 7 -#define BUILDMODE_LIGHTS 8 -#define BUILDMODE_AI 9 - -#define LAST_BUILDMODE 9 - -/proc/togglebuildmode(mob/M as mob in player_list) - set name = "Toggle Build Mode" - set category = "Special Verbs" - if(M.client) - if(M.client.buildmode) - log_admin("[key_name(usr)] has left build mode.") - M.client.buildmode = 0 - M.client.show_popup_menus = 1 - M.plane_holder.set_vis(VIS_BUILDMODE, FALSE) - for(var/obj/effect/bmode/buildholder/H) - if(H.cl == M.client) - qdel(H) - else - log_admin("[key_name(usr)] has entered build mode.") - M.client.buildmode = 1 - M.client.show_popup_menus = 0 - M.plane_holder.set_vis(VIS_BUILDMODE, TRUE) - - var/obj/effect/bmode/buildholder/H = new/obj/effect/bmode/buildholder() - var/obj/effect/bmode/builddir/A = new/obj/effect/bmode/builddir(H) - A.master = H - var/obj/effect/bmode/buildhelp/B = new/obj/effect/bmode/buildhelp(H) - B.master = H - var/obj/effect/bmode/buildmode/C = new/obj/effect/bmode/buildmode(H) - C.master = H - var/obj/effect/bmode/buildquit/D = new/obj/effect/bmode/buildquit(H) - D.master = H - - H.builddir = A - H.buildhelp = B - H.buildmode = C - H.buildquit = D - M.client.screen += A - M.client.screen += B - M.client.screen += C - M.client.screen += D - H.cl = M.client - -/obj/effect/bmode//Cleaning up the tree a bit - density = TRUE - anchored = TRUE - layer = LAYER_HUD_BASE - plane = PLANE_PLAYER_HUD - dir = NORTH - icon = 'icons/misc/buildmode.dmi' - var/obj/effect/bmode/buildholder/master = null - -/obj/effect/bmode/Destroy() - if(master && master.cl) - master.cl.screen -= src - master = null - return ..() - -/obj/effect/bmode/builddir - icon_state = "build" - screen_loc = "NORTH,WEST" - -/obj/effect/bmode/builddir/Click() - switch(dir) - if(NORTH) - set_dir(EAST) - if(EAST) - set_dir(SOUTH) - if(SOUTH) - set_dir(WEST) - if(WEST) - set_dir(NORTHWEST) - if(NORTHWEST) - set_dir(NORTH) - return 1 - -/obj/effect/bmode/buildhelp - icon = 'icons/misc/buildmode.dmi' - icon_state = "buildhelp" - screen_loc = "NORTH,WEST+1" - -/obj/effect/bmode/buildhelp/Click() - switch(master.cl.buildmode) - - if(BUILDMODE_BASIC) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button = Construct / Upgrade
                    \ - Right Mouse Button = Deconstruct / Delete / Downgrade
                    \ - Left Mouse Button + ctrl = R-Window
                    \ - Left Mouse Button + alt = Airlock

                    \ - Use the button in the upper left corner to
                    \ - change the direction of built objects.
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_ADVANCED) - to_chat(usr, "***********************************************************
                    \ - Right Mouse Button on buildmode button = Set object type
                    \ - Middle Mouse Button on buildmode button= On/Off object type saying
                    \ - Middle Mouse Button on turf/obj = Capture object type
                    \ - Left Mouse Button on turf/obj = Place objects
                    \ - Right Mouse Button = Delete objects
                    \ - Mouse Button + ctrl = Copy object type

                    \ - Use the button in the upper left corner to
                    \ - change the direction of built objects.
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_EDIT) - to_chat(usr, "***********************************************************
                    \ - Right Mouse Button on buildmode button = Select var(type) & value
                    \ - Left Mouse Button on turf/obj/mob = Set var(type) & value
                    \ - Right Mouse Button on turf/obj/mob = Reset var's value
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_THROW) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf/obj/mob = Select
                    \ - Right Mouse Button on turf/obj/mob = Throw
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_ROOM) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf = Select as point A
                    \ - Right Mouse Button on turf = Select as point B
                    \ - Right Mouse Button on buildmode button = Change floor/wall type/area name
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_LADDER) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf = Set as upper ladder loc
                    \ - Right Mouse Button on turf = Set as lower ladder loc
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_CONTENTS) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf/obj/mob = Select
                    \ - Right Mouse Button on turf/obj/mob = Move into selection
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_LIGHTS) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button on turf/obj/mob = Make it glow
                    \ - Right Mouse Button on turf/obj/mob = Reset glowing
                    \ - Right Mouse Button on buildmode button = Change glow properties
                    \ - ***********************************************************
                    ") - - if(BUILDMODE_AI) - to_chat(usr, "***********************************************************
                    \ - Left Mouse Button drag box = Select only mobs in box
                    \ - Left Mouse Button drag box + shift = Select additional mobs in area
                    \ - Left Mouse Button on non-mob = Deselect all mobs
                    \ - Left Mouse Button on AI mob = Select/Deselect mob
                    \ - Left Mouse Button + alt on AI mob = Toggle hostility on mob
                    \ - Left Mouse Button + shift on AI mob = Toggle AI (also resets)
                    \ - Left Mouse Button + ctrl on AI mob = Copy mob faction
                    \ - Middle Mouse Button + alt on any atom = Add atom to entity narrate menu
                    \ - Middle Mouse Button + shift on any = Set selected mob(s) to wander
                    \ - Middle Mouse Button + ctrl on any = Set selected mob(s) to NOT wander
                    \ - Right Mouse Button + ctrl on any mob = Paste mob faction copied with Left Mouse Button + shift
                    \ - Right Mouse Button on enemy mob = Command selected mobs to attack mob
                    \ - Right Mouse Button on allied mob = Command selected mobs to follow mob
                    \ - Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction
                    \ - Note: The following also reset the mob's home position:
                    \ - Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)
                    \ - Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be interrupted by enemies)
                    \ - Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf
                    \ - ***********************************************************
                    ") - return 1 - -/obj/effect/bmode/buildquit - icon_state = "buildquit" - screen_loc = "NORTH,WEST+3" - -/obj/effect/bmode/buildquit/Click() - togglebuildmode(master.cl.mob) - return 1 - -/obj/effect/bmode/buildholder - density = FALSE - anchored = TRUE - var/client/cl = null - var/obj/effect/bmode/builddir/builddir = null - var/obj/effect/bmode/buildhelp/buildhelp = null - var/obj/effect/bmode/buildmode/buildmode = null - var/obj/effect/bmode/buildquit/buildquit = null - var/atom/movable/throw_atom = null - var/list/selected_mobs = list() - var/copied_faction = null - var/warned = 0 - -/obj/effect/bmode/buildholder/Destroy() - qdel(builddir) - builddir = null - qdel(buildhelp) - buildhelp = null - qdel(buildmode) - buildmode = null - qdel(buildquit) - buildquit = null - throw_atom = null - for(var/mob/living/unit in selected_mobs) - deselect_AI_mob(cl, unit) - selected_mobs.Cut() - cl = null - return ..() - -/obj/effect/bmode/buildholder/proc/select_AI_mob(client/C, mob/living/unit) - selected_mobs += unit - C.images += unit.selected_image - -/obj/effect/bmode/buildholder/proc/deselect_AI_mob(client/C, mob/living/unit) - selected_mobs -= unit - C.images -= unit.selected_image - -/obj/effect/bmode/buildmode - icon_state = "buildmode1" - screen_loc = "NORTH,WEST+2" - var/varholder = "name" - var/valueholder = "derp" - var/objholder = null - var/objsay = 1 - - var/wall_holder = /turf/simulated/wall - var/floor_holder = /turf/simulated/floor/plating - var/turf/coordA = null - var/turf/coordB = null - var/area_enabled = 0 - var/area_name = "New Area" - - var/new_light_color = "#FFFFFF" - var/new_light_range = 3 - var/new_light_intensity = 3 - -/obj/effect/bmode/buildmode/Click(location, control, params) - var/list/pa = params2list(params) - - if(pa.Find("middle")) - switch(master.cl.buildmode) - if(BUILDMODE_ADVANCED) - objsay=!objsay - - if(pa.Find("left")) - if(master.cl.buildmode == LAST_BUILDMODE) - master.cl.buildmode = 1 - else - master.cl.buildmode++ - src.icon_state = "buildmode[master.cl.buildmode]" - - else if(pa.Find("right")) - switch(master.cl.buildmode) - if(BUILDMODE_BASIC) - - return 1 - if(BUILDMODE_ADVANCED) - objholder = get_path_from_partial_text() - - if(BUILDMODE_EDIT) - var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine") - - master.buildmode.varholder = tgui_input_text(usr,"Enter variable name:" ,"Name", "name") - if(master.buildmode.varholder in locked && !check_rights(R_DEBUG,0)) - return 1 - var/thetype = tgui_input_list(usr,"Select variable type:", "Type", list("text","number","mob-reference","obj-reference","turf-reference")) - if(!thetype) return 1 - switch(thetype) - if("text") - master.buildmode.valueholder = tgui_input_text(usr,"Enter variable value:" ,"Value", "value") - if("number") - master.buildmode.valueholder = tgui_input_number(usr,"Enter variable value:" ,"Value", 123) - if("mob-reference") - master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", mob_list) - if("obj-reference") - master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) - if("turf-reference") - master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) - - if(BUILDMODE_ROOM) - var/area_choice = tgui_alert(usr, "Would you like to generate a new area as well?","Room Builder", list("No", "Yes")) - switch(area_choice) - if("No") - area_enabled = 0 - if("Yes") - area_enabled = 1 - area_name = tgui_input_text(usr, "New area name", "Room Buildmode", max_length = MAX_NAME_LEN) - if(isnull(area_name)) - to_chat(usr, "You must enter a non-null name.") - area_enabled = 0 - return - area_name = sanitize(area_name,MAX_NAME_LEN) - var/choice = tgui_alert(usr, "Would you like to change the floor or wall holders?","Room Builder", list("Floor", "Wall")) - switch(choice) - if("Floor") - floor_holder = get_path_from_partial_text(/turf/simulated/floor/plating) - if("Wall") - wall_holder = get_path_from_partial_text(/turf/simulated/wall) - - if(BUILDMODE_LIGHTS) - var/choice = tgui_alert(usr, "Change the new light range, power, or color?", "Light Maker", list("Range", "Power", "Color")) - switch(choice) - if("Range") - var/input = tgui_input_number(usr, "New light range.","Light Maker",3) - if(input) - new_light_range = input - if("Power") - var/input = tgui_input_number(usr, "New light power.","Light Maker",3) - if(input) - new_light_intensity = input - if("Color") - var/input = input(usr, "New light color.","Light Maker",3) as null|color - if(input) - new_light_color = input - return 1 - -/proc/build_click(var/mob/user, buildmode, params, var/obj/object) - var/obj/effect/bmode/buildholder/holder = null - for(var/obj/effect/bmode/buildholder/H) - if(H.cl == user.client) - holder = H - break - if(!holder) return - var/list/pa = params2list(params) - - switch(buildmode) - if(BUILDMODE_BASIC) - if(istype(object,/turf) && pa.Find("left") && !pa.Find("alt") && !pa.Find("ctrl") ) - if(istype(object,/turf/space)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/floor) - return - else if(istype(object,/turf/simulated/floor)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/wall) - return - else if(istype(object,/turf/simulated/wall)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/wall/r_wall) - return - else if(pa.Find("right")) - if(istype(object,/turf/simulated/wall)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/floor) - return - else if(istype(object,/turf/simulated/floor)) - var/turf/T = object - if(!holder.warned) - var/warning = tgui_alert(user, "Are you -sure- you want to delete this turf and make it the base turf for this Z level?", "GRIEF ALERT", list("No", "Yes")) - if(warning == "Yes") - holder.warned = 1 - else - return - T.ChangeTurf(get_base_turf_by_area(T)) //Defaults to Z if area does not have a special base turf. - return - else if(istype(object,/turf/simulated/wall/r_wall)) - var/turf/T = object - T.ChangeTurf(/turf/simulated/wall) - return - else if(istype(object,/obj)) - qdel(object) - return - else if(istype(object,/turf) && pa.Find("alt") && pa.Find("left")) - new/obj/machinery/door/airlock(get_turf(object)) - else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("left")) - switch(holder.builddir.dir) - if(NORTH) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(NORTH) - if(SOUTH) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(SOUTH) - if(EAST) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(EAST) - if(WEST) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(WEST) - if(NORTHWEST) - var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) - WIN.set_dir(NORTHWEST) - else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("alt") && pa.Find("middle")) - var/turf/T = object - var/obj/item/toy/plushie/teshari/easter_egg = new /obj/item/toy/plushie/teshari(T) - easter_egg.name = "coding teshari plushie" - easter_egg.desc = "A small purple teshari with a plush keyboard attached to it. Where did this come from?" - easter_egg.color = "#a418c7" - - - if(BUILDMODE_ADVANCED) - if(pa.Find("left") && !pa.Find("ctrl")) - if(ispath(holder.buildmode.objholder,/turf)) - var/turf/T = get_turf(object) - T.ChangeTurf(holder.buildmode.objholder) - else if(ispath(holder.buildmode.objholder)) - var/obj/A = new holder.buildmode.objholder (get_turf(object)) - A.set_dir(holder.builddir.dir) - else if(pa.Find("right")) - if(isobj(object)) - qdel(object) - else if(pa.Find("ctrl")) - holder.buildmode.objholder = object.type - to_chat(user, "[object]([object.type]) copied to buildmode.") - if(pa.Find("middle")) - holder.buildmode.objholder = text2path("[object.type]") - if(holder.buildmode.objsay) - to_chat(usr, "[object.type]") - - if(BUILDMODE_EDIT) - if(pa.Find("left")) //I cant believe this shit actually compiles. - if(object.vars.Find(holder.buildmode.varholder)) - log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") - object.vars[holder.buildmode.varholder] = holder.buildmode.valueholder - else - to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") - if(pa.Find("right")) - if(object.vars.Find(holder.buildmode.varholder)) - log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") - object.vars[holder.buildmode.varholder] = initial(object.vars[holder.buildmode.varholder]) - else - to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") - - if(BUILDMODE_THROW) - if(pa.Find("left")) - if(istype(object, /atom/movable)) - holder.throw_atom = object - if(pa.Find("right")) - if(holder.throw_atom) - holder.throw_atom.throw_at(object, 10, 1) - log_admin("[key_name(usr)] threw [holder.throw_atom] at [object]") - - if(BUILDMODE_ROOM) - if(pa.Find("left")) - holder.buildmode.coordA = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as point A.") - - if(pa.Find("right")) - holder.buildmode.coordB = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as point B.") - - if(holder.buildmode.coordA && holder.buildmode.coordB) - if(isnull(holder.buildmode.area_name)) - to_chat(user, "ERROR: Insert area name before use.") - holder.buildmode.coordA = null - holder.buildmode.coordB = null - return - to_chat(user, "A and B set, creating rectangle.") - holder.buildmode.make_rectangle( - holder.buildmode.coordA, - holder.buildmode.coordB, - holder.buildmode.wall_holder, - holder.buildmode.floor_holder, - holder.buildmode.area_enabled, - holder.buildmode.area_name) - holder.buildmode.coordA = null - holder.buildmode.coordB = null - - if(BUILDMODE_LADDER) - if(pa.Find("left")) - holder.buildmode.coordA = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as upper ladder location.") - - if(pa.Find("right")) - holder.buildmode.coordB = get_turf(object) - to_chat(user, "Defined [object] ([object.type]) as lower ladder location.") - - if(holder.buildmode.coordA && holder.buildmode.coordB) - to_chat(user, "Ladder locations set, building ladders.") - var/obj/structure/ladder/A = new /obj/structure/ladder/up(holder.buildmode.coordA) - var/obj/structure/ladder/B = new /obj/structure/ladder(holder.buildmode.coordB) - A.target_up = B - B.target_down = A - A.update_icon() - B.update_icon() - holder.buildmode.coordA = null - holder.buildmode.coordB = null - - if(BUILDMODE_CONTENTS) - if(pa.Find("left")) - if(istype(object, /atom)) - holder.throw_atom = object - if(pa.Find("right")) - if(holder.throw_atom && istype(object, /atom/movable)) - object.forceMove(holder.throw_atom) - log_admin("[key_name(usr)] moved [object] into [holder.throw_atom].") - - if(BUILDMODE_LIGHTS) - if(pa.Find("left")) - if(object) - object.set_light(holder.buildmode.new_light_range, holder.buildmode.new_light_intensity, holder.buildmode.new_light_color) - if(pa.Find("right")) - if(object) - object.set_light(0, 0, "#FFFFFF") - - if(BUILDMODE_AI) - if(pa.Find("left")) - if(isliving(object)) - var/mob/living/L = object - - // Pause/unpause AI - if(pa.Find("shift")) - var/stance = L.get_AI_stance() - if(!isnull(stance)) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. - var/datum/ai_holder/AI = L.ai_holder - if(stance == STANCE_SLEEP) - AI.go_wake() - to_chat(user, span("notice", "\The [L]'s AI has been enabled.")) - else - AI.go_sleep() - to_chat(user, span("notice", "\The [L]'s AI has been disabled.")) - return - else - to_chat(user, span("warning", "\The [L] is not AI controlled.")) - return - - // Toggle hostility - if(pa.Find("alt")) - if(!isnull(L.get_AI_stance())) - var/datum/ai_holder/AI = L.ai_holder - AI.hostile = !AI.hostile - to_chat(user, span("notice", "\The [L] is now [AI.hostile ? "hostile" : "passive"].")) - else - to_chat(user, span("warning", "\The [L] is not AI controlled.")) - return - - // Copy faction - if(pa.Find("ctrl")) - holder.copied_faction = L.faction - to_chat(user, span("notice", "Copied faction '[holder.copied_faction]'.")) - return - - // Select/Deselect - if(!isnull(L.get_AI_stance())) - if(L in holder.selected_mobs) - holder.deselect_AI_mob(user.client, L) - to_chat(user, span("notice", "Deselected \the [L].")) - else - holder.select_AI_mob(user.client, L) - to_chat(user, span("notice", "Selected \the [L].")) - return - else - to_chat(user, span("warning", "\The [L] is not AI controlled.")) - return - else //Not living - for(var/mob/living/unit in holder.selected_mobs) - holder.deselect_AI_mob(user.client, unit) - - if(pa.Find("middle")) - if(pa.Find("shift")) - to_chat(user, SPAN_NOTICE("All selected mobs set to wander")) - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.wander = TRUE - if(pa.Find("ctrl")) - to_chat(user, SPAN_NOTICE("Setting mobs set to NOT wander")) - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.wander = FALSE - if(pa.Find("alt") && isatom(object)) - to_chat(user, SPAN_NOTICE("Adding [object] to Entity Narrate List!")) - user.client.add_mob_for_narration(object) - - - if(pa.Find("right")) - // Paste faction - if(pa.Find("ctrl") && isliving(object)) - if(!holder.copied_faction) - to_chat(user, span("warning", "LMB+Shift a mob to copy their faction before pasting.")) - return - else - var/mob/living/L = object - L.faction = holder.copied_faction - to_chat(user, span("notice", "Pasted faction '[holder.copied_faction]'.")) - return - - if(istype(object, /atom)) // Force attack. - var/atom/A = object - - if(pa.Find("alt")) - var/i = 0 - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.give_target(A) - i++ - to_chat(user, span("notice", "Commanded [i] mob\s to attack \the [A].")) - var/image/orderimage = image(buildmode_hud,A,"ai_targetorder") - orderimage.plane = PLANE_BUILDMODE - flick_overlay(orderimage, list(user.client), 8, TRUE) - return - - if(isliving(object)) // Follow or attack. - var/mob/living/L = object - var/i = 0 // Attacking mobs. - var/j = 0 // Following mobs. - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - if(L.IIsAlly(unit) || !AI.hostile || pa.Find("shift")) - AI.set_follow(L) - j++ - else - AI.give_target(L) - i++ - var/message = "Commanded " - if(i) - message += "[i] mob\s to attack \the [L]" - if(j) - message += ", and " - else - message += "." - if(j) - message += "[j] mob\s to follow \the [L]." - to_chat(user, span("notice", message)) - var/image/orderimage = image(buildmode_hud,L,"ai_targetorder") - orderimage.plane = PLANE_BUILDMODE - flick_overlay(orderimage, list(user.client), 8, TRUE) - return - - if(isturf(object)) // Move or reposition. - var/turf/T = object - var/forced = 0 - var/told = 0 - for(var/mob/living/unit in holder.selected_mobs) - var/datum/ai_holder/AI = unit.ai_holder - AI.home_turf = T - if(unit.get_AI_stance() == STANCE_SLEEP) - unit.forceMove(T) - forced++ - else - AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. - told++ - to_chat(user, span("notice", "Commanded [told] mob\s to move to \the [T], and manually placed [forced] of them.")) - var/image/orderimage = image(buildmode_hud,T,"ai_turforder") - orderimage.plane = PLANE_BUILDMODE - flick_overlay(orderimage, list(user.client), 8, TRUE) - return - -/proc/build_drag(var/client/user, buildmode, var/atom/fromatom, var/atom/toatom, var/atom/fromloc, var/atom/toloc, var/fromcontrol, var/tocontrol, params) - var/obj/effect/bmode/buildholder/holder = null - for(var/obj/effect/bmode/buildholder/H) - if(H.cl == user) - holder = H - break - if(!holder) return - var/list/pa = params2list(params) - - switch(buildmode) - if(BUILDMODE_AI) - - //Holding shift prevents the deselection of existing - if(!pa.Find("shift")) - for(var/mob/living/unit in holder.selected_mobs) - holder.deselect_AI_mob(user, unit) - - var/turf/c1 = get_turf(fromatom) - var/turf/c2 = get_turf(toatom) - if(!c1 || !c2) - return //Dragged outside window or something - - var/low_x = min(c1.x,c2.x) - var/low_y = min(c1.y,c2.y) - var/hi_x = max(c1.x,c2.x) - var/hi_y = max(c1.y,c2.y) - var/z = c1.z //Eh - - var/i = 0 - for(var/mob/living/L in living_mob_list) - if(L.z != z || L.client) - continue - if(L.x >= low_x && L.x <= hi_x && L.y >= low_y && L.y <= hi_y) - holder.select_AI_mob(user, L) - i++ - - to_chat(user, span("notice", "Band-selected [i] mobs.")) - return - -/obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path) - var/desired_path = tgui_input_text(usr, "Enter full or partial typepath.","Typepath","[default_path]") - - if(!desired_path) //VOREStation Add - If you don't give it anything it builds a list of every possible thing in the game and crashes your client. - return //VOREStation Add - And the main way for it to do that is to push the cancel button, which should just do nothing. :U - - var/list/types = typesof(/atom) - var/list/matches = list() - - for(var/path in types) - if(findtext("[path]", desired_path)) - matches += path - - if(matches.len==0) - tgui_alert_async(usr, "No results found. Sorry.") - return - - var/result = null - - if(matches.len==1) - result = matches[1] - else - result = tgui_input_list(usr, "Select an atom type", "Spawn Atom", matches, strict_modern = TRUE) - return result - -/obj/effect/bmode/buildmode/proc/make_rectangle(var/turf/A, var/turf/B, var/turf/wall_type, var/turf/floor_type, var/area_enabled, var/area_name) - if(!A || !B) // No coords - return - if(A.z != B.z) // Not same z-level - return - - var/height = A.y - B.y - var/width = A.x - B.x - var/z_level = A.z - - var/turf/lower_left_corner = null - // First, try to find the lowest part - var/desired_y = 0 - if(A.y <= B.y) - desired_y = A.y - else - desired_y = B.y - - //Now for the left-most part. - var/desired_x = 0 - if(A.x <= B.x) - desired_x = A.x - else - desired_x = B.x - - lower_left_corner = locate(desired_x, desired_y, z_level) - - // Now we can begin building the actual room. This defines the boundries for the room. - var/low_bound_x = lower_left_corner.x - var/low_bound_y = lower_left_corner.y - - var/high_bound_x = lower_left_corner.x + abs(width) - var/high_bound_y = lower_left_corner.y + abs(height) - - var/origin_x = lower_left_corner.x + round((abs(width)/2)) - var/origin_y = lower_left_corner.y + round((abs(height)/2)) - var/turf/origin - - for(var/i = low_bound_x, i <= high_bound_x, i++) - for(var/j = low_bound_y, j <= high_bound_y, j++) - var/turf/T = locate(i, j, z_level) - if(i == low_bound_x || i == high_bound_x || j == low_bound_y || j == high_bound_y) - if(isturf(wall_type)) - T.ChangeTurf(wall_type) - else - new wall_type(T) - - else - if(T.x == origin_x && T.y == origin_y) //Get the middle of the square. - origin = T - if(isturf(floor_type)) - T.ChangeTurf(floor_type) - else - new floor_type(T) - if(area_enabled) //Let's try not to make a new area unless you got walls and a floor. - create_buildmode_area(area_name, origin) //Generates a new area. - -/proc/create_buildmode_area(var/area_name, var/turf/origin) - var/turfs = detect_room_buildmode(origin) - - var/area/newA - var/area/oldA = get_area(origin) - var/str = area_name - str = sanitize(str,MAX_NAME_LEN) - if(!str || !length(str)) //cancel - return - newA = new /area/buildmode - newA.dynamic_lighting = FALSE // Without this it's pitch black if you build anywhere but space. - newA.luminosity = TRUE // Without this it's pitch black if you build anywhere but space. - newA.setup(str) - newA.has_gravity = oldA.has_gravity - - for(var/i in 1 to length(turfs)) //Fix lighting. Praise the lord. - var/turf/thing = turfs[i] - newA.contents += thing - thing.change_area(oldA, newA) - - set_area_machinery(newA, newA.name, oldA.name)// Change the name and area defines of all the machinery to the correct area. - oldA.power_check() //Simply makes the area turn the power off if you nicked an APC from it. - return TRUE - -/proc/detect_room_buildmode(var/turf/first, var/allowedAreas = AREA_SPACE) - if(!istype(first)) - return - var/list/turf/found = new - var/list/turf/pending = list(first) - while(pending.len) - var/turf/T = pending[1] - pending -= T - for (var/dir in cardinal) - var/turf/NT = get_step(T,dir) - if (!isturf(NT) || (NT in found) || (NT in pending)) - continue - // We ask ZAS to determine if its airtight. Thats what matters anyway right? - if(air_master.air_blocked(T, NT)) - // Okay thats the edge of the room - if(get_area_type_buildmode(NT.loc) == AREA_SPACE && air_master.air_blocked(NT, NT)) - found += NT // So we include walls/doors not already in any area - continue - if (istype(NT, /turf/space)) - return //omg hull breach we all going to die here - if (istype(NT, /turf/simulated/shuttle)) - return // Unsure why this, but was in old code. Trusting for now. - if (NT.loc != first.loc && !(get_area_type_buildmode(NT.loc) & allowedAreas)) - // Edge of a protected area. Lets stop here... - continue - if (!istype(NT, /turf/simulated)) - // Great, unsimulated... eh, just stop searching here - continue - // Okay, NT looks promising, lets continue the search there! - pending += NT - found += T - // end while - return found - -/proc/get_area_type_buildmode(area/A) - if(A.outdoors) - return AREA_SPACE - - for (var/type in BUILDABLE_AREA_TYPES) - if ( istype(A,type) ) - return AREA_SPACE - - for (var/type in SPECIALS) - if ( istype(A,type) ) - return AREA_SPECIAL - return AREA_STATION - -/area/buildmode - dynamic_lighting = FALSE - luminosity = FALSE - -#undef BUILDMODE_BASIC -#undef BUILDMODE_ADVANCED -#undef BUILDMODE_EDIT -#undef BUILDMODE_THROW -#undef BUILDMODE_ROOM -#undef BUILDMODE_LADDER -#undef BUILDMODE_CONTENTS -#undef BUILDMODE_LIGHTS -#undef BUILDMODE_AI -#undef LAST_BUILDMODE +#define BUILDMODE_BASIC 1 +#define BUILDMODE_ADVANCED 2 +#define BUILDMODE_EDIT 3 +#define BUILDMODE_THROW 4 +#define BUILDMODE_ROOM 5 +#define BUILDMODE_LADDER 6 +#define BUILDMODE_CONTENTS 7 +#define BUILDMODE_LIGHTS 8 +#define BUILDMODE_AI 9 + +#define LAST_BUILDMODE 9 + +/proc/togglebuildmode(mob/M as mob in player_list) + set name = "Toggle Build Mode" + set category = "Special Verbs" + if(M.client) + if(M.client.buildmode) + log_admin("[key_name(usr)] has left build mode.") + M.client.buildmode = 0 + M.client.show_popup_menus = 1 + M.plane_holder.set_vis(VIS_BUILDMODE, FALSE) + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == M.client) + qdel(H) + else + log_admin("[key_name(usr)] has entered build mode.") + M.client.buildmode = 1 + M.client.show_popup_menus = 0 + M.plane_holder.set_vis(VIS_BUILDMODE, TRUE) + + var/obj/effect/bmode/buildholder/H = new/obj/effect/bmode/buildholder() + var/obj/effect/bmode/builddir/A = new/obj/effect/bmode/builddir(H) + A.master = H + var/obj/effect/bmode/buildhelp/B = new/obj/effect/bmode/buildhelp(H) + B.master = H + var/obj/effect/bmode/buildmode/C = new/obj/effect/bmode/buildmode(H) + C.master = H + var/obj/effect/bmode/buildquit/D = new/obj/effect/bmode/buildquit(H) + D.master = H + + H.builddir = A + H.buildhelp = B + H.buildmode = C + H.buildquit = D + M.client.screen += A + M.client.screen += B + M.client.screen += C + M.client.screen += D + H.cl = M.client + +/obj/effect/bmode//Cleaning up the tree a bit + density = TRUE + anchored = TRUE + layer = LAYER_HUD_BASE + plane = PLANE_PLAYER_HUD + dir = NORTH + icon = 'icons/misc/buildmode.dmi' + var/obj/effect/bmode/buildholder/master = null + +/obj/effect/bmode/Destroy() + if(master && master.cl) + master.cl.screen -= src + master = null + return ..() + +/obj/effect/bmode/builddir + icon_state = "build" + screen_loc = "NORTH,WEST" + +/obj/effect/bmode/builddir/Click() + switch(dir) + if(NORTH) + set_dir(EAST) + if(EAST) + set_dir(SOUTH) + if(SOUTH) + set_dir(WEST) + if(WEST) + set_dir(NORTHWEST) + if(NORTHWEST) + set_dir(NORTH) + return 1 + +/obj/effect/bmode/buildhelp + icon = 'icons/misc/buildmode.dmi' + icon_state = "buildhelp" + screen_loc = "NORTH,WEST+1" + +/obj/effect/bmode/buildhelp/Click() + switch(master.cl.buildmode) + + if(BUILDMODE_BASIC) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button = Construct / Upgrade
                    \ + Right Mouse Button = Deconstruct / Delete / Downgrade
                    \ + Left Mouse Button + ctrl = R-Window
                    \ + Left Mouse Button + alt = Airlock

                    \ + Use the button in the upper left corner to
                    \ + change the direction of built objects.
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_ADVANCED) + to_chat(usr, "***********************************************************
                    \ + Right Mouse Button on buildmode button = Set object type
                    \ + Middle Mouse Button on buildmode button= On/Off object type saying
                    \ + Middle Mouse Button on turf/obj = Capture object type
                    \ + Left Mouse Button on turf/obj = Place objects
                    \ + Right Mouse Button = Delete objects
                    \ + Mouse Button + ctrl = Copy object type

                    \ + Use the button in the upper left corner to
                    \ + change the direction of built objects.
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_EDIT) + to_chat(usr, "***********************************************************
                    \ + Right Mouse Button on buildmode button = Select var(type) & value
                    \ + Left Mouse Button on turf/obj/mob = Set var(type) & value
                    \ + Right Mouse Button on turf/obj/mob = Reset var's value
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_THROW) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf/obj/mob = Select
                    \ + Right Mouse Button on turf/obj/mob = Throw
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_ROOM) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf = Select as point A
                    \ + Right Mouse Button on turf = Select as point B
                    \ + Right Mouse Button on buildmode button = Change floor/wall type/area name
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_LADDER) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf = Set as upper ladder loc
                    \ + Right Mouse Button on turf = Set as lower ladder loc
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_CONTENTS) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf/obj/mob = Select
                    \ + Right Mouse Button on turf/obj/mob = Move into selection
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_LIGHTS) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button on turf/obj/mob = Make it glow
                    \ + Right Mouse Button on turf/obj/mob = Reset glowing
                    \ + Right Mouse Button on buildmode button = Change glow properties
                    \ + ***********************************************************
                    ") + + if(BUILDMODE_AI) + to_chat(usr, "***********************************************************
                    \ + Left Mouse Button drag box = Select only mobs in box
                    \ + Left Mouse Button drag box + shift = Select additional mobs in area
                    \ + Left Mouse Button on non-mob = Deselect all mobs
                    \ + Left Mouse Button on AI mob = Select/Deselect mob
                    \ + Left Mouse Button + alt on AI mob = Toggle hostility on mob
                    \ + Left Mouse Button + shift on AI mob = Toggle AI (also resets)
                    \ + Left Mouse Button + ctrl on AI mob = Copy mob faction
                    \ + Middle Mouse Button + alt on any atom = Add atom to entity narrate menu
                    \ + Middle Mouse Button + shift on any = Set selected mob(s) to wander
                    \ + Middle Mouse Button + ctrl on any = Set selected mob(s) to NOT wander
                    \ + Right Mouse Button + ctrl on any mob = Paste mob faction copied with Left Mouse Button + shift
                    \ + Right Mouse Button on enemy mob = Command selected mobs to attack mob
                    \ + Right Mouse Button on allied mob = Command selected mobs to follow mob
                    \ + Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction
                    \ + Note: The following also reset the mob's home position:
                    \ + Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)
                    \ + Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be interrupted by enemies)
                    \ + Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf
                    \ + ***********************************************************
                    ") + return 1 + +/obj/effect/bmode/buildquit + icon_state = "buildquit" + screen_loc = "NORTH,WEST+3" + +/obj/effect/bmode/buildquit/Click() + togglebuildmode(master.cl.mob) + return 1 + +/obj/effect/bmode/buildholder + density = FALSE + anchored = TRUE + var/client/cl = null + var/obj/effect/bmode/builddir/builddir = null + var/obj/effect/bmode/buildhelp/buildhelp = null + var/obj/effect/bmode/buildmode/buildmode = null + var/obj/effect/bmode/buildquit/buildquit = null + var/atom/movable/throw_atom = null + var/list/selected_mobs = list() + var/copied_faction = null + var/warned = 0 + +/obj/effect/bmode/buildholder/Destroy() + qdel(builddir) + builddir = null + qdel(buildhelp) + buildhelp = null + qdel(buildmode) + buildmode = null + qdel(buildquit) + buildquit = null + throw_atom = null + for(var/mob/living/unit in selected_mobs) + deselect_AI_mob(cl, unit) + selected_mobs.Cut() + cl = null + return ..() + +/obj/effect/bmode/buildholder/proc/select_AI_mob(client/C, mob/living/unit) + selected_mobs += unit + C.images += unit.selected_image + +/obj/effect/bmode/buildholder/proc/deselect_AI_mob(client/C, mob/living/unit) + selected_mobs -= unit + C.images -= unit.selected_image + +/obj/effect/bmode/buildmode + icon_state = "buildmode1" + screen_loc = "NORTH,WEST+2" + var/varholder = "name" + var/valueholder = "derp" + var/objholder = null + var/objsay = 1 + + var/wall_holder = /turf/simulated/wall + var/floor_holder = /turf/simulated/floor/plating + var/turf/coordA = null + var/turf/coordB = null + var/area_enabled = 0 + var/area_name = "New Area" + + var/new_light_color = "#FFFFFF" + var/new_light_range = 3 + var/new_light_intensity = 3 + +/obj/effect/bmode/buildmode/Click(location, control, params) + var/list/pa = params2list(params) + + if(pa.Find("middle")) + switch(master.cl.buildmode) + if(BUILDMODE_ADVANCED) + objsay=!objsay + + if(pa.Find("left")) + if(master.cl.buildmode == LAST_BUILDMODE) + master.cl.buildmode = 1 + else + master.cl.buildmode++ + src.icon_state = "buildmode[master.cl.buildmode]" + + else if(pa.Find("right")) + switch(master.cl.buildmode) + if(BUILDMODE_BASIC) + + return 1 + if(BUILDMODE_ADVANCED) + objholder = get_path_from_partial_text() + + if(BUILDMODE_EDIT) + var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine") + + master.buildmode.varholder = tgui_input_text(usr,"Enter variable name:" ,"Name", "name") + if(master.buildmode.varholder in locked && !check_rights(R_DEBUG,0)) + return 1 + var/thetype = tgui_input_list(usr,"Select variable type:", "Type", list("text","number","mob-reference","obj-reference","turf-reference")) + if(!thetype) return 1 + switch(thetype) + if("text") + master.buildmode.valueholder = tgui_input_text(usr,"Enter variable value:" ,"Value", "value") + if("number") + master.buildmode.valueholder = tgui_input_number(usr,"Enter variable value:" ,"Value", 123) + if("mob-reference") + master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", mob_list) + if("obj-reference") + master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) + if("turf-reference") + master.buildmode.valueholder = tgui_input_list(usr,"Enter variable value:", "Value", world) + + if(BUILDMODE_ROOM) + var/area_choice = tgui_alert(usr, "Would you like to generate a new area as well?","Room Builder", list("No", "Yes")) + switch(area_choice) + if("No") + area_enabled = 0 + if("Yes") + area_enabled = 1 + area_name = tgui_input_text(usr, "New area name", "Room Buildmode", max_length = MAX_NAME_LEN) + if(isnull(area_name)) + to_chat(usr, "You must enter a non-null name.") + area_enabled = 0 + return + area_name = sanitize(area_name,MAX_NAME_LEN) + var/choice = tgui_alert(usr, "Would you like to change the floor or wall holders?","Room Builder", list("Floor", "Wall")) + switch(choice) + if("Floor") + floor_holder = get_path_from_partial_text(/turf/simulated/floor/plating) + if("Wall") + wall_holder = get_path_from_partial_text(/turf/simulated/wall) + + if(BUILDMODE_LIGHTS) + var/choice = tgui_alert(usr, "Change the new light range, power, or color?", "Light Maker", list("Range", "Power", "Color")) + switch(choice) + if("Range") + var/input = tgui_input_number(usr, "New light range.","Light Maker",3) + if(input) + new_light_range = input + if("Power") + var/input = tgui_input_number(usr, "New light power.","Light Maker",3) + if(input) + new_light_intensity = input + if("Color") + var/input = input(usr, "New light color.","Light Maker",3) as null|color + if(input) + new_light_color = input + return 1 + +/proc/build_click(var/mob/user, buildmode, params, var/obj/object) + var/obj/effect/bmode/buildholder/holder = null + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == user.client) + holder = H + break + if(!holder) return + var/list/pa = params2list(params) + + switch(buildmode) + if(BUILDMODE_BASIC) + if(istype(object,/turf) && pa.Find("left") && !pa.Find("alt") && !pa.Find("ctrl") ) + if(istype(object,/turf/space)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/floor) + return + else if(istype(object,/turf/simulated/floor)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/wall) + return + else if(istype(object,/turf/simulated/wall)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/wall/r_wall) + return + else if(pa.Find("right")) + if(istype(object,/turf/simulated/wall)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/floor) + return + else if(istype(object,/turf/simulated/floor)) + var/turf/T = object + if(!holder.warned) + var/warning = tgui_alert(user, "Are you -sure- you want to delete this turf and make it the base turf for this Z level?", "GRIEF ALERT", list("No", "Yes")) + if(warning == "Yes") + holder.warned = 1 + else + return + T.ChangeTurf(get_base_turf_by_area(T)) //Defaults to Z if area does not have a special base turf. + return + else if(istype(object,/turf/simulated/wall/r_wall)) + var/turf/T = object + T.ChangeTurf(/turf/simulated/wall) + return + else if(istype(object,/obj)) + qdel(object) + return + else if(istype(object,/turf) && pa.Find("alt") && pa.Find("left")) + new/obj/machinery/door/airlock(get_turf(object)) + else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("left")) + switch(holder.builddir.dir) + if(NORTH) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(NORTH) + if(SOUTH) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(SOUTH) + if(EAST) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(EAST) + if(WEST) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(WEST) + if(NORTHWEST) + var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) + WIN.set_dir(NORTHWEST) + else if(istype(object,/turf) && pa.Find("ctrl") && pa.Find("alt") && pa.Find("middle")) + var/turf/T = object + var/obj/item/toy/plushie/teshari/easter_egg = new /obj/item/toy/plushie/teshari(T) + easter_egg.name = "coding teshari plushie" + easter_egg.desc = "A small purple teshari with a plush keyboard attached to it. Where did this come from?" + easter_egg.color = "#a418c7" + + + if(BUILDMODE_ADVANCED) + if(pa.Find("left") && !pa.Find("ctrl")) + if(ispath(holder.buildmode.objholder,/turf)) + var/turf/T = get_turf(object) + T.ChangeTurf(holder.buildmode.objholder) + else if(ispath(holder.buildmode.objholder)) + var/obj/A = new holder.buildmode.objholder (get_turf(object)) + A.set_dir(holder.builddir.dir) + else if(pa.Find("right")) + if(isobj(object)) + qdel(object) + else if(pa.Find("ctrl")) + holder.buildmode.objholder = object.type + to_chat(user, "[object]([object.type]) copied to buildmode.") + if(pa.Find("middle")) + holder.buildmode.objholder = text2path("[object.type]") + if(holder.buildmode.objsay) + to_chat(usr, "[object.type]") + + if(BUILDMODE_EDIT) + if(pa.Find("left")) //I cant believe this shit actually compiles. + if(object.vars.Find(holder.buildmode.varholder)) + log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") + object.vars[holder.buildmode.varholder] = holder.buildmode.valueholder + else + to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") + if(pa.Find("right")) + if(object.vars.Find(holder.buildmode.varholder)) + log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") + object.vars[holder.buildmode.varholder] = initial(object.vars[holder.buildmode.varholder]) + else + to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") + + if(BUILDMODE_THROW) + if(pa.Find("left")) + if(istype(object, /atom/movable)) + holder.throw_atom = object + if(pa.Find("right")) + if(holder.throw_atom) + holder.throw_atom.throw_at(object, 10, 1) + log_admin("[key_name(usr)] threw [holder.throw_atom] at [object]") + + if(BUILDMODE_ROOM) + if(pa.Find("left")) + holder.buildmode.coordA = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as point A.") + + if(pa.Find("right")) + holder.buildmode.coordB = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as point B.") + + if(holder.buildmode.coordA && holder.buildmode.coordB) + if(isnull(holder.buildmode.area_name)) + to_chat(user, "ERROR: Insert area name before use.") + holder.buildmode.coordA = null + holder.buildmode.coordB = null + return + to_chat(user, "A and B set, creating rectangle.") + holder.buildmode.make_rectangle( + holder.buildmode.coordA, + holder.buildmode.coordB, + holder.buildmode.wall_holder, + holder.buildmode.floor_holder, + holder.buildmode.area_enabled, + holder.buildmode.area_name) + holder.buildmode.coordA = null + holder.buildmode.coordB = null + + if(BUILDMODE_LADDER) + if(pa.Find("left")) + holder.buildmode.coordA = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as upper ladder location.") + + if(pa.Find("right")) + holder.buildmode.coordB = get_turf(object) + to_chat(user, "Defined [object] ([object.type]) as lower ladder location.") + + if(holder.buildmode.coordA && holder.buildmode.coordB) + to_chat(user, "Ladder locations set, building ladders.") + var/obj/structure/ladder/A = new /obj/structure/ladder/up(holder.buildmode.coordA) + var/obj/structure/ladder/B = new /obj/structure/ladder(holder.buildmode.coordB) + A.target_up = B + B.target_down = A + A.update_icon() + B.update_icon() + holder.buildmode.coordA = null + holder.buildmode.coordB = null + + if(BUILDMODE_CONTENTS) + if(pa.Find("left")) + if(istype(object, /atom)) + holder.throw_atom = object + if(pa.Find("right")) + if(holder.throw_atom && istype(object, /atom/movable)) + object.forceMove(holder.throw_atom) + log_admin("[key_name(usr)] moved [object] into [holder.throw_atom].") + + if(BUILDMODE_LIGHTS) + if(pa.Find("left")) + if(object) + object.set_light(holder.buildmode.new_light_range, holder.buildmode.new_light_intensity, holder.buildmode.new_light_color) + if(pa.Find("right")) + if(object) + object.set_light(0, 0, "#FFFFFF") + + if(BUILDMODE_AI) + if(pa.Find("left")) + if(isliving(object)) + var/mob/living/L = object + + // Pause/unpause AI + if(pa.Find("shift")) + var/stance = L.get_AI_stance() + if(!isnull(stance)) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. + var/datum/ai_holder/AI = L.ai_holder + if(stance == STANCE_SLEEP) + AI.go_wake() + to_chat(user, span("notice", "\The [L]'s AI has been enabled.")) + else + AI.go_sleep() + to_chat(user, span("notice", "\The [L]'s AI has been disabled.")) + return + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + + // Toggle hostility + if(pa.Find("alt")) + if(!isnull(L.get_AI_stance())) + var/datum/ai_holder/AI = L.ai_holder + AI.hostile = !AI.hostile + to_chat(user, span("notice", "\The [L] is now [AI.hostile ? "hostile" : "passive"].")) + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + + // Copy faction + if(pa.Find("ctrl")) + holder.copied_faction = L.faction + to_chat(user, span("notice", "Copied faction '[holder.copied_faction]'.")) + return + + // Select/Deselect + if(!isnull(L.get_AI_stance())) + if(L in holder.selected_mobs) + holder.deselect_AI_mob(user.client, L) + to_chat(user, span("notice", "Deselected \the [L].")) + else + holder.select_AI_mob(user.client, L) + to_chat(user, span("notice", "Selected \the [L].")) + return + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + else //Not living + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(user.client, unit) + + if(pa.Find("middle")) + if(pa.Find("shift")) + to_chat(user, SPAN_NOTICE("All selected mobs set to wander")) + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.wander = TRUE + if(pa.Find("ctrl")) + to_chat(user, SPAN_NOTICE("Setting mobs set to NOT wander")) + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.wander = FALSE + if(pa.Find("alt") && isatom(object)) + to_chat(user, SPAN_NOTICE("Adding [object] to Entity Narrate List!")) + user.client.add_mob_for_narration(object) + + + if(pa.Find("right")) + // Paste faction + if(pa.Find("ctrl") && isliving(object)) + if(!holder.copied_faction) + to_chat(user, span("warning", "LMB+Shift a mob to copy their faction before pasting.")) + return + else + var/mob/living/L = object + L.faction = holder.copied_faction + to_chat(user, span("notice", "Pasted faction '[holder.copied_faction]'.")) + return + + if(istype(object, /atom)) // Force attack. + var/atom/A = object + + if(pa.Find("alt")) + var/i = 0 + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.give_target(A) + i++ + to_chat(user, span("notice", "Commanded [i] mob\s to attack \the [A].")) + var/image/orderimage = image(buildmode_hud,A,"ai_targetorder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + + if(isliving(object)) // Follow or attack. + var/mob/living/L = object + var/i = 0 // Attacking mobs. + var/j = 0 // Following mobs. + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + if(L.IIsAlly(unit) || !AI.hostile || pa.Find("shift")) + AI.set_follow(L) + j++ + else + AI.give_target(L) + i++ + var/message = "Commanded " + if(i) + message += "[i] mob\s to attack \the [L]" + if(j) + message += ", and " + else + message += "." + if(j) + message += "[j] mob\s to follow \the [L]." + to_chat(user, span("notice", message)) + var/image/orderimage = image(buildmode_hud,L,"ai_targetorder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + + if(isturf(object)) // Move or reposition. + var/turf/T = object + var/forced = 0 + var/told = 0 + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.home_turf = T + if(unit.get_AI_stance() == STANCE_SLEEP) + unit.forceMove(T) + forced++ + else + AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. + told++ + to_chat(user, span("notice", "Commanded [told] mob\s to move to \the [T], and manually placed [forced] of them.")) + var/image/orderimage = image(buildmode_hud,T,"ai_turforder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return + +/proc/build_drag(var/client/user, buildmode, var/atom/fromatom, var/atom/toatom, var/atom/fromloc, var/atom/toloc, var/fromcontrol, var/tocontrol, params) + var/obj/effect/bmode/buildholder/holder = null + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == user) + holder = H + break + if(!holder) return + var/list/pa = params2list(params) + + switch(buildmode) + if(BUILDMODE_AI) + + //Holding shift prevents the deselection of existing + if(!pa.Find("shift")) + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(user, unit) + + var/turf/c1 = get_turf(fromatom) + var/turf/c2 = get_turf(toatom) + if(!c1 || !c2) + return //Dragged outside window or something + + var/low_x = min(c1.x,c2.x) + var/low_y = min(c1.y,c2.y) + var/hi_x = max(c1.x,c2.x) + var/hi_y = max(c1.y,c2.y) + var/z = c1.z //Eh + + var/i = 0 + for(var/mob/living/L in living_mob_list) + if(L.z != z || L.client) + continue + if(L.x >= low_x && L.x <= hi_x && L.y >= low_y && L.y <= hi_y) + holder.select_AI_mob(user, L) + i++ + + to_chat(user, span("notice", "Band-selected [i] mobs.")) + return + +/obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path) + var/desired_path = tgui_input_text(usr, "Enter full or partial typepath.","Typepath","[default_path]") + + if(!desired_path) //VOREStation Add - If you don't give it anything it builds a list of every possible thing in the game and crashes your client. + return //VOREStation Add - And the main way for it to do that is to push the cancel button, which should just do nothing. :U + + var/list/types = typesof(/atom) + var/list/matches = list() + + for(var/path in types) + if(findtext("[path]", desired_path)) + matches += path + + if(matches.len==0) + tgui_alert_async(usr, "No results found. Sorry.") + return + + var/result = null + + if(matches.len==1) + result = matches[1] + else + result = tgui_input_list(usr, "Select an atom type", "Spawn Atom", matches, strict_modern = TRUE) + return result + +/obj/effect/bmode/buildmode/proc/make_rectangle(var/turf/A, var/turf/B, var/turf/wall_type, var/turf/floor_type, var/area_enabled, var/area_name) + if(!A || !B) // No coords + return + if(A.z != B.z) // Not same z-level + return + + var/height = A.y - B.y + var/width = A.x - B.x + var/z_level = A.z + + var/turf/lower_left_corner = null + // First, try to find the lowest part + var/desired_y = 0 + if(A.y <= B.y) + desired_y = A.y + else + desired_y = B.y + + //Now for the left-most part. + var/desired_x = 0 + if(A.x <= B.x) + desired_x = A.x + else + desired_x = B.x + + lower_left_corner = locate(desired_x, desired_y, z_level) + + // Now we can begin building the actual room. This defines the boundries for the room. + var/low_bound_x = lower_left_corner.x + var/low_bound_y = lower_left_corner.y + + var/high_bound_x = lower_left_corner.x + abs(width) + var/high_bound_y = lower_left_corner.y + abs(height) + + var/origin_x = lower_left_corner.x + round((abs(width)/2)) + var/origin_y = lower_left_corner.y + round((abs(height)/2)) + var/turf/origin + + for(var/i = low_bound_x, i <= high_bound_x, i++) + for(var/j = low_bound_y, j <= high_bound_y, j++) + var/turf/T = locate(i, j, z_level) + if(i == low_bound_x || i == high_bound_x || j == low_bound_y || j == high_bound_y) + if(isturf(wall_type)) + T.ChangeTurf(wall_type) + else + new wall_type(T) + + else + if(T.x == origin_x && T.y == origin_y) //Get the middle of the square. + origin = T + if(isturf(floor_type)) + T.ChangeTurf(floor_type) + else + new floor_type(T) + if(area_enabled) //Let's try not to make a new area unless you got walls and a floor. + create_buildmode_area(area_name, origin) //Generates a new area. + +/proc/create_buildmode_area(var/area_name, var/turf/origin) + var/turfs = detect_room_buildmode(origin) + + var/area/newA + var/area/oldA = get_area(origin) + var/str = area_name + str = sanitize(str,MAX_NAME_LEN) + if(!str || !length(str)) //cancel + return + newA = new /area/buildmode + newA.dynamic_lighting = FALSE // Without this it's pitch black if you build anywhere but space. + newA.luminosity = TRUE // Without this it's pitch black if you build anywhere but space. + newA.setup(str) + newA.has_gravity = oldA.has_gravity + + for(var/i in 1 to length(turfs)) //Fix lighting. Praise the lord. + var/turf/thing = turfs[i] + newA.contents += thing + thing.change_area(oldA, newA) + + set_area_machinery(newA, newA.name, oldA.name)// Change the name and area defines of all the machinery to the correct area. + oldA.power_check() //Simply makes the area turn the power off if you nicked an APC from it. + return TRUE + +/proc/detect_room_buildmode(var/turf/first, var/allowedAreas = AREA_SPACE) + if(!istype(first)) + return + var/list/turf/found = new + var/list/turf/pending = list(first) + while(pending.len) + var/turf/T = pending[1] + pending -= T + for (var/dir in cardinal) + var/turf/NT = get_step(T,dir) + if (!isturf(NT) || (NT in found) || (NT in pending)) + continue + // We ask ZAS to determine if its airtight. Thats what matters anyway right? + if(air_master.air_blocked(T, NT)) + // Okay thats the edge of the room + if(get_area_type_buildmode(NT.loc) == AREA_SPACE && air_master.air_blocked(NT, NT)) + found += NT // So we include walls/doors not already in any area + continue + if (istype(NT, /turf/space)) + return //omg hull breach we all going to die here + if (istype(NT, /turf/simulated/shuttle)) + return // Unsure why this, but was in old code. Trusting for now. + if (NT.loc != first.loc && !(get_area_type_buildmode(NT.loc) & allowedAreas)) + // Edge of a protected area. Lets stop here... + continue + if (!istype(NT, /turf/simulated)) + // Great, unsimulated... eh, just stop searching here + continue + // Okay, NT looks promising, lets continue the search there! + pending += NT + found += T + // end while + return found + +/proc/get_area_type_buildmode(area/A) + if(A.outdoors) + return AREA_SPACE + + for (var/type in BUILDABLE_AREA_TYPES) + if ( istype(A,type) ) + return AREA_SPACE + + for (var/type in SPECIALS) + if ( istype(A,type) ) + return AREA_SPECIAL + return AREA_STATION + +/area/buildmode + dynamic_lighting = FALSE + luminosity = FALSE + +#undef BUILDMODE_BASIC +#undef BUILDMODE_ADVANCED +#undef BUILDMODE_EDIT +#undef BUILDMODE_THROW +#undef BUILDMODE_ROOM +#undef BUILDMODE_LADDER +#undef BUILDMODE_CONTENTS +#undef BUILDMODE_LIGHTS +#undef BUILDMODE_AI +#undef LAST_BUILDMODE diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 6099fd5e299..7e70eba8e57 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -1,28 +1,28 @@ -/client/proc/dsay(msg as text) - set category = "Special Verbs" - set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite - set hidden = 1 - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - if(!src.mob) - return - if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") - return - - if(!is_preference_enabled(/datum/client_preference/show_dsay)) - to_chat(src, "You have deadchat muted.") - return - - var/stafftype = uppertext(holder.rank) - - msg = sanitize(msg) - log_admin("DSAY: [key_name(src)] : [msg]") - - if (!msg) - return - - say_dead_direct("[stafftype]([src.holder.fakekey ? src.holder.fakekey : src.key]) says, \"[msg]\"") - - feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/dsay(msg as text) + set category = "Special Verbs" + set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite + set hidden = 1 + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + if(!src.mob) + return + if(prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send DSAY messages (muted).") + return + + if(!is_preference_enabled(/datum/client_preference/show_dsay)) + to_chat(src, "You have deadchat muted.") + return + + var/stafftype = uppertext(holder.rank) + + msg = sanitize(msg) + log_admin("DSAY: [key_name(src)] : [msg]") + + if (!msg) + return + + say_dead_direct("[stafftype]([src.holder.fakekey ? src.holder.fakekey : src.key]) says, \"[msg]\"") + + feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 10b4bc7cc9d..623dbc2dae7 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -1,695 +1,695 @@ -/client/proc/Debug2() - set category = "Debug" - set name = "Debug-Game" - if(!check_rights(R_DEBUG)) return - - if(Debug2) - Debug2 = 0 - message_admins("[key_name(src)] toggled debugging off.") - log_admin("[key_name(src)] toggled debugging off.") - else - Debug2 = 1 - message_admins("[key_name(src)] toggled debugging on.") - log_admin("[key_name(src)] toggled debugging on.") - - feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -// callproc moved to code/modules/admin/callproc - -/client/proc/simple_DPS() - set name = "Simple DPS" - set category = "Debug" - set desc = "Gives a really basic idea of how much hurt something in-hand does." - - var/obj/item/I = null - var/mob/living/user = null - if(isliving(usr)) - user = usr - I = user.get_active_hand() - if(!I || !istype(I)) - to_chat(user, "You need to have something in your active hand, to use this verb.") - return - var/weapon_attack_speed = user.get_attack_speed(I) / 10 - var/weapon_damage = I.force - var/modified_damage_percent = 1 - - for(var/datum/modifier/M in user.modifiers) - if(!isnull(M.outgoing_melee_damage_percent)) - weapon_damage *= M.outgoing_melee_damage_percent - modified_damage_percent *= M.outgoing_melee_damage_percent - - if(istype(I, /obj/item/weapon/gun)) - var/obj/item/weapon/gun/G = I - var/obj/item/projectile/P - - if(istype(I, /obj/item/weapon/gun/energy)) - var/obj/item/weapon/gun/energy/energy_gun = G - P = new energy_gun.projectile_type() - - else if(istype(I, /obj/item/weapon/gun/projectile)) - var/obj/item/weapon/gun/projectile/projectile_gun = G - var/obj/item/ammo_casing/ammo = projectile_gun.chambered - P = ammo.BB - - else - to_chat(user, "DPS calculation by this verb is not supported for \the [G]'s type. Energy or Ballistic only, sorry.") - - weapon_damage = P.damage - weapon_attack_speed = G.fire_delay / 10 - qdel(P) - - var/DPS = weapon_damage / weapon_attack_speed - to_chat(user, "Damage: [weapon_damage][modified_damage_percent != 1 ? " (Modified by [modified_damage_percent*100]%)":""]") - to_chat(user, "Attack Speed: [weapon_attack_speed]/s") - to_chat(user, "\The [I] does [DPS] damage per second.") - if(DPS > 0) - to_chat(user, "At your maximum health ([user.getMaxHealth()]), it would take approximately;") - to_chat(user, "[(user.getMaxHealth() - config.health_threshold_softcrit) / DPS] seconds to softcrit you. ([config.health_threshold_softcrit] health)") - to_chat(user, "[(user.getMaxHealth() - config.health_threshold_crit) / DPS] seconds to hardcrit you. ([config.health_threshold_crit] health)") - to_chat(user, "[(user.getMaxHealth() - config.health_threshold_dead) / DPS] seconds to kill you. ([config.health_threshold_dead] health)") - - else - to_chat(user, "You need to be a living mob, with hands, and for an object to be in your active hand, to use this verb.") - return - -/client/proc/Cell() - set category = "Debug" - set name = "Cell" - if(!mob) - return - var/turf/T = mob.loc - - if (!( istype(T, /turf) )) - return - - var/datum/gas_mixture/env = T.return_air() - - var/t = "Coordinates: [T.x],[T.y],[T.z]\n" - t += "Temperature: [env.temperature]\n" - t += "Pressure: [env.return_pressure()]kPa\n" - for(var/g in env.gas) - t += "[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.volume]kPa\n" - - usr.show_message(t, 1) - feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_robotize(var/mob/M in mob_list) - set category = "Fun" - set name = "Make Robot" - - if(!ticker) - tgui_alert_async(usr, "Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - log_admin("[key_name(src)] has robotized [M.key].") - spawn(10) - M:Robotize() - - else - tgui_alert_async(usr, "Invalid mob") - -/client/proc/cmd_admin_animalize(var/mob/M in mob_list) - set category = "Fun" - set name = "Make Simple Animal" - - if(!ticker) - tgui_alert_async(usr, "Wait until the game starts") - return - - if(!M) - tgui_alert_async(usr, "That mob doesn't seem to exist, close the panel and try again.") - return - - if(istype(M, /mob/new_player)) - tgui_alert_async(usr, "The mob must not be a new_player.") - return - - log_admin("[key_name(src)] has animalized [M.key].") - spawn(10) - M.Animalize() - - -/client/proc/makepAI() - set category = "Fun" - set name = "Make pAI" - set desc = "Spawn someone in as a pAI!" - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - return - var/turf/T = get_turf(mob) - - var/list/available = list() - for(var/mob/C in mob_list) - if(C.key && isobserver(C)) - available.Add(C) - var/mob/choice = tgui_input_list(usr, "Choose a player to play the pAI", "Spawn pAI", available) - if(!choice) - return 0 - var/obj/item/device/paicard/typeb/card = new(T) - var/mob/living/silicon/pai/pai = new(card) - pai.real_name = pai.name - pai.key = choice.key - card.setPersonality(pai) - if(tgui_alert(pai, "Do you want to load your pAI data?", "Load", list("Yes", "No")) == "Yes") - pai.savefile_load(pai) - else - pai.name = sanitizeSafe(tgui_input_text(pai, "Enter your pAI name:", "pAI Name", "Personal AI")) - card.setPersonality(pai) - for(var/datum/paiCandidate/candidate in paiController.pai_candidates) - if(candidate.key == choice.key) - paiController.pai_candidates.Remove(candidate) - log_admin("made a pAI with key=[pai.key] at ([T.x],[T.y],[T.z])") - feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_alienize(var/mob/M in mob_list) - set category = "Fun" - set name = "Make Alien" - - if(!ticker) - tgui_alert_async(usr, "Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has alienized [M.key].") - spawn(10) - M:Alienize() - feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into an alien.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) - else - tgui_alert_async(usr, "Invalid mob") - - -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all() - set category = "Debug" - set name = "Del-All" - - // to prevent REALLY stupid deletions - var/blocked = list(/obj, /mob, /mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/observer/dead, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) - var/hsbitem = tgui_input_list(usr, "Choose an object to delete.", "Delete:", typesof(/obj) + typesof(/mob) - blocked) - if(hsbitem) - for(var/atom/O in world) - if(istype(O, hsbitem)) - qdel(O) - log_admin("[key_name(src)] has deleted all instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_make_powernets() - set category = "Debug" - set name = "Make Powernets" - SSmachines.makepowernets() - log_admin("[key_name(src)] has remade the powernet. SSmachines.makepowernets() called.") - message_admins("[key_name_admin(src)] has remade the powernets. SSmachines.makepowernets() called.", 0) - feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_tog_aliens() - set category = "Server" - set name = "Toggle Aliens" - - config.aliens_allowed = !config.aliens_allowed - log_admin("[key_name(src)] has turned aliens [config.aliens_allowed ? "on" : "off"].") - message_admins("[key_name_admin(src)] has turned aliens [config.aliens_allowed ? "on" : "off"].", 0) - feedback_add_details("admin_verb","TAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_display_del_log() - set category = "Debug" - set name = "Display del() Log" - set desc = "Display del's log of everything that's passed through it." - - if(!check_rights(R_DEBUG)) return - var/list/dellog = list("List of things that have gone through qdel this round

                      ") - sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - dellog += "
                    1. [path]
                        " - if (I.failures) - dellog += "
                      • Failures: [I.failures]
                      • " - dellog += "
                      • qdel() Count: [I.qdels]
                      • " - dellog += "
                      • Destroy() Cost: [I.destroy_time]ms
                      • " - if (I.hard_deletes) - dellog += "
                      • Total Hard Deletes [I.hard_deletes]
                      • " - dellog += "
                      • Time Spent Hard Deleting: [I.hard_delete_time]ms
                      • " - if (I.slept_destroy) - dellog += "
                      • Sleeps: [I.slept_destroy]
                      • " - if (I.no_respect_force) - dellog += "
                      • Ignored force: [I.no_respect_force]
                      • " - if (I.no_hint) - dellog += "
                      • No hint: [I.no_hint]
                      • " - dellog += "
                    2. " - - dellog += "
                    " - - usr << browse(dellog.Join(), "window=dellog") - -/client/proc/cmd_display_init_log() - set category = "Debug" - set name = "Display Initialize() Log" - set desc = "Displays a list of things that didn't handle Initialize() properly" - - if(!check_rights(R_DEBUG)) return - src << browse(replacetext(SSatoms.InitLog(), "\n", "
                    "), "window=initlog") - -/* -/client/proc/cmd_display_overlay_log() - set category = "Debug" - set name = "Display overlay Log" - set desc = "Display SSoverlays log of everything that's passed through it." - - if(!check_rights(R_DEBUG)) return - render_stats(SSoverlays.stats, src) -*/ -// Render stats list for round-end statistics. -/proc/render_stats(list/stats, user, sort = /proc/cmp_generic_stat_item_time) - sortTim(stats, sort, TRUE) - - var/list/lines = list() - for (var/entry in stats) - var/list/data = stats[entry] - lines += "[entry] => [num2text(data[STAT_ENTRY_TIME], 10)]ms ([data[STAT_ENTRY_COUNT]]) (avg:[num2text(data[STAT_ENTRY_TIME]/(data[STAT_ENTRY_COUNT] || 1), 99)])" - - if (user) - user << browse("
                    1. [lines.Join("
                    2. ")]
                    ", "window=[url_encode("stats:\ref[stats]")]") - else - . = lines.Join("\n") - -/client/proc/cmd_admin_grantfullaccess(var/mob/M in mob_list) - set category = "Admin" - set name = "Grant Full Access" - - if (!ticker) - tgui_alert_async(usr, "Wait until the game starts") - return - if (istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if (H.wear_id) - var/obj/item/weapon/card/id/id = H.wear_id - if(istype(H.wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/pda = H.wear_id - id = pda.id - id.icon_state = "gold" - id.access = get_all_accesses().Copy() - else - var/obj/item/weapon/card/id/id = new/obj/item/weapon/card/id(M); - id.icon_state = "gold" - id.access = get_all_accesses().Copy() - id.registered_name = H.real_name - id.assignment = "Site Manager" - id.name = "[id.registered_name]'s ID Card ([id.assignment])" - H.equip_to_slot_or_del(id, slot_wear_id) - H.update_inv_wear_id() - else - tgui_alert_async(usr, "Invalid mob") - feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(src)] has granted [M.key] full access.") - message_admins("[key_name_admin(usr)] has granted [M.key] full access.", 1) - -/client/proc/cmd_assume_direct_control(var/mob/M in mob_list) - set category = "Admin" - set name = "Assume direct control" - set desc = "Direct intervention" - - if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return - if(M.ckey) - if(tgui_alert(usr, "This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.","Confirmation",list("Yes","No")) != "Yes") - return - else - var/mob/observer/dead/ghost = new/mob/observer/dead(M,1) - ghost.ckey = M.ckey - message_admins("[key_name_admin(usr)] assumed direct control of [M].", 1) - log_admin("[key_name(usr)] assumed direct control of [M].") - var/mob/adminmob = src.mob - M.ckey = src.ckey - if( isobserver(adminmob) ) - qdel(adminmob) - feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/take_picture(var/atom/A in world) - set name = "Save PNG" - set category = "Debug" - set desc = "Opens a dialog to save a PNG of any object in the game." - - if(!check_rights(R_DEBUG)) - return - - downloadImage(A) - -/client/proc/cmd_admin_areatest() - set category = "Mapping" - set name = "Test areas" - - var/list/areas_all = list() - var/list/areas_with_APC = list() - var/list/areas_with_air_alarm = list() - var/list/areas_with_RC = list() - var/list/areas_with_light = list() - var/list/areas_with_LS = list() - var/list/areas_with_intercom = list() - var/list/areas_with_camera = list() - - for(var/area/A in world) - if(!(A.type in areas_all)) - areas_all.Add(A.type) - - for(var/obj/machinery/power/apc/APC in GLOB.apcs) - var/area/A = get_area(APC) - if(A && !(A.type in areas_with_APC)) - areas_with_APC.Add(A.type) - - for(var/obj/machinery/alarm/alarm in machines) - var/area/A = get_area(alarm) - if(A && !(A.type in areas_with_air_alarm)) - areas_with_air_alarm.Add(A.type) - - for(var/obj/machinery/requests_console/RC in machines) - var/area/A = get_area(RC) - if(A && !(A.type in areas_with_RC)) - areas_with_RC.Add(A.type) - - for(var/obj/machinery/light/L in machines) - var/area/A = get_area(L) - if(A && !(A.type in areas_with_light)) - areas_with_light.Add(A.type) - - for(var/obj/machinery/light_switch/LS in machines) - var/area/A = get_area(LS) - if(A && !(A.type in areas_with_LS)) - areas_with_LS.Add(A.type) - - for(var/obj/item/device/radio/intercom/I in machines) - var/area/A = get_area(I) - if(A && !(A.type in areas_with_intercom)) - areas_with_intercom.Add(A.type) - - for(var/obj/machinery/camera/C in machines) - var/area/A = get_area(C) - if(A && !(A.type in areas_with_camera)) - areas_with_camera.Add(A.type) - - var/list/areas_without_APC = areas_all - areas_with_APC - var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm - var/list/areas_without_RC = areas_all - areas_with_RC - var/list/areas_without_light = areas_all - areas_with_light - var/list/areas_without_LS = areas_all - areas_with_LS - var/list/areas_without_intercom = areas_all - areas_with_intercom - var/list/areas_without_camera = areas_all - areas_with_camera - - to_world("AREAS WITHOUT AN APC:") - for(var/areatype in areas_without_APC) - to_world("* [areatype]") - - to_world("AREAS WITHOUT AN AIR ALARM:") - for(var/areatype in areas_without_air_alarm) - to_world("* [areatype]") - - to_world("AREAS WITHOUT A REQUEST CONSOLE:") - for(var/areatype in areas_without_RC) - to_world("* [areatype]") - - to_world("AREAS WITHOUT ANY LIGHTS:") - for(var/areatype in areas_without_light) - to_world("* [areatype]") - - to_world("AREAS WITHOUT A LIGHT SWITCH:") - for(var/areatype in areas_without_LS) - to_world("* [areatype]") - - to_world("AREAS WITHOUT ANY INTERCOMS:") - for(var/areatype in areas_without_intercom) - to_world("* [areatype]") - - to_world("AREAS WITHOUT ANY CAMERAS:") - for(var/areatype in areas_without_camera) - to_world("* [areatype]") - -/datum/admins/proc/cmd_admin_dress(input in getmobs()) - set category = "Fun" - set name = "Select equipment" - - if(!check_rights(R_FUN)) - return - - var/target = getmobs()[input] - if(!target) - return - - if(!ishuman(target)) - return - - var/mob/living/carbon/human/H = target - - var/decl/hierarchy/outfit/outfit = tgui_input_list(usr, "Select outfit.", "Select equipment.", outfits()) - if(!outfit) - return - - feedback_add_details("admin_verb","SEQ") - dressup_human(H, outfit, 1) - -/proc/dressup_human(var/mob/living/carbon/human/H, var/decl/hierarchy/outfit/outfit) - if(!H || !outfit) - return - if(outfit.undress) - H.delete_inventory() - outfit.equip(H) - log_and_message_admins("changed the equipment of [key_name(H)] to [outfit.name].") - -/client/proc/startSinglo() - - set category = "Debug" - set name = "Start Singularity" - set desc = "Sets up the singularity and all machines to get power flowing through the station" - - if(tgui_alert(usr, "Are you sure? This will start up the engine. Should only be used during debug!","Start Singularity",list("Yes","No")) != "Yes") - return - - for(var/obj/machinery/power/emitter/E in machines) - if(istype(get_area(E), /area/space)) - E.anchored = TRUE - E.state = 2 - E.connect_to_network() - E.active = TRUE - for(var/obj/machinery/field_generator/F in machines) - if(istype(get_area(F), /area/space)) - F.Varedit_start = 1 - for(var/obj/machinery/power/grounding_rod/GR in machines) - GR.anchored = TRUE - GR.update_icon() - for(var/obj/machinery/power/tesla_coil/TC in machines) - TC.anchored = TRUE - TC.update_icon() - for(var/obj/structure/particle_accelerator/PA in machines) - PA.anchored = TRUE - PA.construction_state = 3 - PA.update_icon() - for(var/obj/machinery/particle_accelerator/PA in machines) - PA.anchored = TRUE - PA.construction_state = 3 - PA.update_icon() - - for(var/obj/machinery/power/rad_collector/Rad in machines) - if(Rad.anchored) - if(!Rad.P) - var/obj/item/weapon/tank/phoron/Phoron = new/obj/item/weapon/tank/phoron(Rad) - Phoron.air_contents.gas["phoron"] = 70 - Rad.drainratio = 0 - Rad.P = Phoron - Phoron.loc = Rad - - if(!Rad.active) - Rad.toggle_power() - -/client/proc/setup_supermatter_engine() - set category = "Debug" - set name = "Setup supermatter" - set desc = "Sets up the supermatter engine" - - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/response = tgui_alert(usr, "Are you sure? This will start up the engine. Should only be used during debug!","Setup Supermatter",list("Setup Completely","Setup except coolant","No")) - - if(response == "No") - return - - var/found_the_pump = 0 - var/obj/machinery/power/supermatter/SM - - for(var/obj/machinery/M in machines) - if(!M) - continue - if(!M.loc) - continue - if(!M.loc.loc) - continue - - if(istype(M.loc.loc,/area/engineering/engine_room)) - if(istype(M,/obj/machinery/power/rad_collector)) - var/obj/machinery/power/rad_collector/Rad = M - Rad.anchored = TRUE - Rad.connect_to_network() - - var/obj/item/weapon/tank/phoron/Phoron = new/obj/item/weapon/tank/phoron(Rad) - - Phoron.air_contents.gas["phoron"] = 29.1154 //This is a full tank if you filled it from a canister - Rad.P = Phoron - - Phoron.loc = Rad - - if(!Rad.active) - Rad.toggle_power() - Rad.update_icon() - - else if(istype(M,/obj/machinery/atmospherics/binary/pump)) //Turning on every pump. - var/obj/machinery/atmospherics/binary/pump/Pump = M - if(Pump.name == "Engine Feed" && response == "Setup Completely") - found_the_pump = 1 - Pump.air2.gas["nitrogen"] = 3750 //The contents of 2 canisters. - Pump.air2.temperature = 50 - Pump.air2.update_values() - Pump.update_use_power(USE_POWER_IDLE) - Pump.target_pressure = 4500 - Pump.update_icon() - - else if(istype(M,/obj/machinery/power/supermatter)) - SM = M - spawn(50) - SM.power = 320 - - else if(istype(M,/obj/machinery/power/smes)) //This is the SMES inside the engine room. We don't need much power. - var/obj/machinery/power/smes/SMES = M - SMES.input_attempt = 1 - SMES.input_level = 200000 - SMES.output_level = 75000 - - else if(istype(M.loc.loc,/area/engineering/engine_smes)) //Set every SMES to charge and spit out 300,000 power between the 4 of them. - if(istype(M,/obj/machinery/power/smes)) - var/obj/machinery/power/smes/SMES = M - SMES.input_attempt = 1 - SMES.input_level = 200000 - SMES.output_level = 75000 - - if(!found_the_pump && response == "Setup Completely") - to_chat(src, "Unable to locate air supply to fill up with coolant, adding some coolant around the supermatter") - var/turf/simulated/T = SM.loc - T.zone.air.gas["nitrogen"] += 450 - T.zone.air.temperature = 50 - T.zone.air.update_values() - - - log_admin("[key_name(usr)] setup the supermatter engine [response == "Setup except coolant" ? "without coolant" : ""]") - message_admins("[key_name_admin(usr)] setup the supermatter engine [response == "Setup except coolant" ? "without coolant": ""]", 1) - return - - - -/client/proc/cmd_debug_mob_lists() - set category = "Debug" - set name = "Debug Mob Lists" - set desc = "For when you just gotta know" - - switch(tgui_input_list(usr, "Which list?", "List Choice", list("Players","Admins","Mobs","Living Mobs","Dead Mobs", "Clients"))) - if("Players") - to_chat(usr, span("filter_debuglogs", jointext(player_list,","))) - if("Admins") - to_chat(usr, span("filter_debuglogs", jointext(GLOB.admins,","))) - if("Mobs") - to_chat(usr, span("filter_debuglogs", jointext(mob_list,","))) - if("Living Mobs") - to_chat(usr, span("filter_debuglogs", jointext(living_mob_list,","))) - if("Dead Mobs") - to_chat(usr, span("filter_debuglogs", jointext(dead_mob_list,","))) - if("Clients") - to_chat(usr, span("filter_debuglogs", jointext(GLOB.clients,","))) - -/client/proc/cmd_debug_using_map() - set category = "Debug" - set name = "Debug Map Datum" - set desc = "Debug the map metadata about the currently compiled in map." - - if(!check_rights(R_DEBUG)) - return - debug_variables(using_map) - -// DNA2 - Admin Hax -/client/proc/cmd_admin_toggle_block(var/mob/M,var/block) - if(!ticker) - tgui_alert_async(usr, "Wait until the game starts") - return - if(istype(M, /mob/living/carbon)) - M.dna.SetSEState(block,!M.dna.GetSEState(block)) - domutcheck(M,null,MUTCHK_FORCED) - M.update_mutations() - var/state="[M.dna.GetSEState(block)?"on":"off"]" - var/blockname=assigned_blocks[block] - message_admins("[key_name_admin(src)] has toggled [M.key]'s [blockname] block [state]!") - log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!") - else - tgui_alert_async(usr, "Invalid mob") - -/datum/admins/proc/view_runtimes() - set category = "Debug" - set name = "View Runtimes" - set desc = "Open the Runtime Viewer" - - if(!check_rights(R_DEBUG)) - return - - error_cache.showTo(usr) - -/datum/admins/proc/change_weather() - set category = "Debug" - set name = "Change Weather" - set desc = "Changes the current weather." - - if(!check_rights(R_DEBUG)) - return - - var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to modify the weather on?", "Change Weather", SSplanets.planets) - if(istype(planet)) - var/datum/weather/new_weather = tgui_input_list(usr, "What weather do you want to change to?", "Change Weather", planet.weather_holder.allowed_weather_types) - if(new_weather) - planet.weather_holder.change_weather(new_weather) - planet.weather_holder.rebuild_forecast() - var/log = "[key_name(src)] changed [planet.name]'s weather to [new_weather]." - message_admins(log) - log_admin(log) - -/datum/admins/proc/toggle_firework_override() - set category = "Fun" - set name = "Toggle Weather Firework Override" - set desc = "Toggles ability for weather fireworks to affect weather on planet of choice." - - if(!check_rights(R_DEBUG)) - return - - var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to toggle firework effects on?", "Change Weather", SSplanets.planets) - if(istype(planet) && planet.weather_holder) - planet.weather_holder.firework_override = !(planet.weather_holder.firework_override) - var/log = "[key_name(src)] toggled [planet.name]'s firework override to [planet.weather_holder.firework_override ? "on" : "off"]." - message_admins(log) - log_admin(log) - -/datum/admins/proc/change_time() - set category = "Debug" - set name = "Change Planet Time" - set desc = "Changes the time of a planet." - - if(!check_rights(R_DEBUG)) - return - - var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to modify time on?", "Change Time", SSplanets.planets) - if(istype(planet)) - var/datum/time/current_time_datum = planet.current_time - var/new_hour = tgui_input_number(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh"))) - if(!isnull(new_hour)) - var/new_minute = tgui_input_number(usr, "What minute do you want to change to?", "Change Time", text2num(current_time_datum.show_time("mm")) ) - if(!isnull(new_minute)) - var/type_needed = current_time_datum.type - var/datum/time/new_time = new type_needed() - new_time = new_time.add_hours(new_hour) - new_time = new_time.add_minutes(new_minute) - planet.current_time = new_time - spawn(1) - planet.update_sun() - - var/log = "[key_name(src)] changed [planet.name]'s time to [planet.current_time.show_time("hh:mm")]." - message_admins(log) - log_admin(log) +/client/proc/Debug2() + set category = "Debug" + set name = "Debug-Game" + if(!check_rights(R_DEBUG)) return + + if(Debug2) + Debug2 = 0 + message_admins("[key_name(src)] toggled debugging off.") + log_admin("[key_name(src)] toggled debugging off.") + else + Debug2 = 1 + message_admins("[key_name(src)] toggled debugging on.") + log_admin("[key_name(src)] toggled debugging on.") + + feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +// callproc moved to code/modules/admin/callproc + +/client/proc/simple_DPS() + set name = "Simple DPS" + set category = "Debug" + set desc = "Gives a really basic idea of how much hurt something in-hand does." + + var/obj/item/I = null + var/mob/living/user = null + if(isliving(usr)) + user = usr + I = user.get_active_hand() + if(!I || !istype(I)) + to_chat(user, "You need to have something in your active hand, to use this verb.") + return + var/weapon_attack_speed = user.get_attack_speed(I) / 10 + var/weapon_damage = I.force + var/modified_damage_percent = 1 + + for(var/datum/modifier/M in user.modifiers) + if(!isnull(M.outgoing_melee_damage_percent)) + weapon_damage *= M.outgoing_melee_damage_percent + modified_damage_percent *= M.outgoing_melee_damage_percent + + if(istype(I, /obj/item/weapon/gun)) + var/obj/item/weapon/gun/G = I + var/obj/item/projectile/P + + if(istype(I, /obj/item/weapon/gun/energy)) + var/obj/item/weapon/gun/energy/energy_gun = G + P = new energy_gun.projectile_type() + + else if(istype(I, /obj/item/weapon/gun/projectile)) + var/obj/item/weapon/gun/projectile/projectile_gun = G + var/obj/item/ammo_casing/ammo = projectile_gun.chambered + P = ammo.BB + + else + to_chat(user, "DPS calculation by this verb is not supported for \the [G]'s type. Energy or Ballistic only, sorry.") + + weapon_damage = P.damage + weapon_attack_speed = G.fire_delay / 10 + qdel(P) + + var/DPS = weapon_damage / weapon_attack_speed + to_chat(user, "Damage: [weapon_damage][modified_damage_percent != 1 ? " (Modified by [modified_damage_percent*100]%)":""]") + to_chat(user, "Attack Speed: [weapon_attack_speed]/s") + to_chat(user, "\The [I] does [DPS] damage per second.") + if(DPS > 0) + to_chat(user, "At your maximum health ([user.getMaxHealth()]), it would take approximately;") + to_chat(user, "[(user.getMaxHealth() - config.health_threshold_softcrit) / DPS] seconds to softcrit you. ([config.health_threshold_softcrit] health)") + to_chat(user, "[(user.getMaxHealth() - config.health_threshold_crit) / DPS] seconds to hardcrit you. ([config.health_threshold_crit] health)") + to_chat(user, "[(user.getMaxHealth() - config.health_threshold_dead) / DPS] seconds to kill you. ([config.health_threshold_dead] health)") + + else + to_chat(user, "You need to be a living mob, with hands, and for an object to be in your active hand, to use this verb.") + return + +/client/proc/Cell() + set category = "Debug" + set name = "Cell" + if(!mob) + return + var/turf/T = mob.loc + + if (!( istype(T, /turf) )) + return + + var/datum/gas_mixture/env = T.return_air() + + var/t = span_blue("Coordinates: [T.x],[T.y],[T.z]\n") + t += span_red("Temperature: [env.temperature]\n") + t += span_red("Pressure: [env.return_pressure()]kPa\n") + for(var/g in env.gas) + t += span_blue("[g]: [env.gas[g]] / [env.gas[g] * R_IDEAL_GAS_EQUATION * env.temperature / env.volume]kPa\n") + + usr.show_message(t, 1) + feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_robotize(var/mob/M in mob_list) + set category = "Fun" + set name = "Make Robot" + + if(!ticker) + tgui_alert_async(usr, "Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + log_admin("[key_name(src)] has robotized [M.key].") + spawn(10) + M:Robotize() + + else + tgui_alert_async(usr, "Invalid mob") + +/client/proc/cmd_admin_animalize(var/mob/M in mob_list) + set category = "Fun" + set name = "Make Simple Animal" + + if(!ticker) + tgui_alert_async(usr, "Wait until the game starts") + return + + if(!M) + tgui_alert_async(usr, "That mob doesn't seem to exist, close the panel and try again.") + return + + if(istype(M, /mob/new_player)) + tgui_alert_async(usr, "The mob must not be a new_player.") + return + + log_admin("[key_name(src)] has animalized [M.key].") + spawn(10) + M.Animalize() + + +/client/proc/makepAI() + set category = "Fun" + set name = "Make pAI" + set desc = "Spawn someone in as a pAI!" + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + return + var/turf/T = get_turf(mob) + + var/list/available = list() + for(var/mob/C in mob_list) + if(C.key && isobserver(C)) + available.Add(C) + var/mob/choice = tgui_input_list(usr, "Choose a player to play the pAI", "Spawn pAI", available) + if(!choice) + return 0 + var/obj/item/device/paicard/typeb/card = new(T) + var/mob/living/silicon/pai/pai = new(card) + pai.real_name = pai.name + pai.key = choice.key + card.setPersonality(pai) + if(tgui_alert(pai, "Do you want to load your pAI data?", "Load", list("Yes", "No")) == "Yes") + pai.savefile_load(pai) + else + pai.name = sanitizeSafe(tgui_input_text(pai, "Enter your pAI name:", "pAI Name", "Personal AI")) + card.setPersonality(pai) + for(var/datum/paiCandidate/candidate in paiController.pai_candidates) + if(candidate.key == choice.key) + paiController.pai_candidates.Remove(candidate) + log_admin("made a pAI with key=[pai.key] at ([T.x],[T.y],[T.z])") + feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_alienize(var/mob/M in mob_list) + set category = "Fun" + set name = "Make Alien" + + if(!ticker) + tgui_alert_async(usr, "Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has alienized [M.key].") + spawn(10) + M:Alienize() + feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into an alien.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) + else + tgui_alert_async(usr, "Invalid mob") + + +//TODO: merge the vievars version into this or something maybe mayhaps +/client/proc/cmd_debug_del_all() + set category = "Debug" + set name = "Del-All" + + // to prevent REALLY stupid deletions + var/blocked = list(/obj, /mob, /mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/observer/dead, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) + var/hsbitem = tgui_input_list(usr, "Choose an object to delete.", "Delete:", typesof(/obj) + typesof(/mob) - blocked) + if(hsbitem) + for(var/atom/O in world) + if(istype(O, hsbitem)) + qdel(O) + log_admin("[key_name(src)] has deleted all instances of [hsbitem].") + message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) + feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_debug_make_powernets() + set category = "Debug" + set name = "Make Powernets" + SSmachines.makepowernets() + log_admin("[key_name(src)] has remade the powernet. SSmachines.makepowernets() called.") + message_admins("[key_name_admin(src)] has remade the powernets. SSmachines.makepowernets() called.", 0) + feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_debug_tog_aliens() + set category = "Server" + set name = "Toggle Aliens" + + config.aliens_allowed = !config.aliens_allowed + log_admin("[key_name(src)] has turned aliens [config.aliens_allowed ? "on" : "off"].") + message_admins("[key_name_admin(src)] has turned aliens [config.aliens_allowed ? "on" : "off"].", 0) + feedback_add_details("admin_verb","TAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_display_del_log() + set category = "Debug" + set name = "Display del() Log" + set desc = "Display del's log of everything that's passed through it." + + if(!check_rights(R_DEBUG)) return + var/list/dellog = list("List of things that have gone through qdel this round

                      ") + sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + dellog += "
                    1. [path]
                        " + if (I.failures) + dellog += "
                      • Failures: [I.failures]
                      • " + dellog += "
                      • qdel() Count: [I.qdels]
                      • " + dellog += "
                      • Destroy() Cost: [I.destroy_time]ms
                      • " + if (I.hard_deletes) + dellog += "
                      • Total Hard Deletes [I.hard_deletes]
                      • " + dellog += "
                      • Time Spent Hard Deleting: [I.hard_delete_time]ms
                      • " + if (I.slept_destroy) + dellog += "
                      • Sleeps: [I.slept_destroy]
                      • " + if (I.no_respect_force) + dellog += "
                      • Ignored force: [I.no_respect_force]
                      • " + if (I.no_hint) + dellog += "
                      • No hint: [I.no_hint]
                      • " + dellog += "
                    2. " + + dellog += "
                    " + + usr << browse(dellog.Join(), "window=dellog") + +/client/proc/cmd_display_init_log() + set category = "Debug" + set name = "Display Initialize() Log" + set desc = "Displays a list of things that didn't handle Initialize() properly" + + if(!check_rights(R_DEBUG)) return + src << browse(replacetext(SSatoms.InitLog(), "\n", "
                    "), "window=initlog") + +/* +/client/proc/cmd_display_overlay_log() + set category = "Debug" + set name = "Display overlay Log" + set desc = "Display SSoverlays log of everything that's passed through it." + + if(!check_rights(R_DEBUG)) return + render_stats(SSoverlays.stats, src) +*/ +// Render stats list for round-end statistics. +/proc/render_stats(list/stats, user, sort = /proc/cmp_generic_stat_item_time) + sortTim(stats, sort, TRUE) + + var/list/lines = list() + for (var/entry in stats) + var/list/data = stats[entry] + lines += "[entry] => [num2text(data[STAT_ENTRY_TIME], 10)]ms ([data[STAT_ENTRY_COUNT]]) (avg:[num2text(data[STAT_ENTRY_TIME]/(data[STAT_ENTRY_COUNT] || 1), 99)])" + + if (user) + user << browse("
                    1. [lines.Join("
                    2. ")]
                    ", "window=[url_encode("stats:\ref[stats]")]") + else + . = lines.Join("\n") + +/client/proc/cmd_admin_grantfullaccess(var/mob/M in mob_list) + set category = "Admin" + set name = "Grant Full Access" + + if (!ticker) + tgui_alert_async(usr, "Wait until the game starts") + return + if (istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if (H.wear_id) + var/obj/item/weapon/card/id/id = H.wear_id + if(istype(H.wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/pda = H.wear_id + id = pda.id + id.icon_state = "gold" + id.access = get_all_accesses().Copy() + else + var/obj/item/weapon/card/id/id = new/obj/item/weapon/card/id(M); + id.icon_state = "gold" + id.access = get_all_accesses().Copy() + id.registered_name = H.real_name + id.assignment = "Site Manager" + id.name = "[id.registered_name]'s ID Card ([id.assignment])" + H.equip_to_slot_or_del(id, slot_wear_id) + H.update_inv_wear_id() + else + tgui_alert_async(usr, "Invalid mob") + feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(src)] has granted [M.key] full access.") + message_admins(span_blue("[key_name_admin(usr)] has granted [M.key] full access."), 1) + +/client/proc/cmd_assume_direct_control(var/mob/M in mob_list) + set category = "Admin" + set name = "Assume direct control" + set desc = "Direct intervention" + + if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return + if(M.ckey) + if(tgui_alert(usr, "This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.","Confirmation",list("Yes","No")) != "Yes") + return + else + var/mob/observer/dead/ghost = new/mob/observer/dead(M,1) + ghost.ckey = M.ckey + message_admins(span_blue("[key_name_admin(usr)] assumed direct control of [M]."), 1) + log_admin("[key_name(usr)] assumed direct control of [M].") + var/mob/adminmob = src.mob + M.ckey = src.ckey + if( isobserver(adminmob) ) + qdel(adminmob) + feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/take_picture(var/atom/A in world) + set name = "Save PNG" + set category = "Debug" + set desc = "Opens a dialog to save a PNG of any object in the game." + + if(!check_rights(R_DEBUG)) + return + + downloadImage(A) + +/client/proc/cmd_admin_areatest() + set category = "Mapping" + set name = "Test areas" + + var/list/areas_all = list() + var/list/areas_with_APC = list() + var/list/areas_with_air_alarm = list() + var/list/areas_with_RC = list() + var/list/areas_with_light = list() + var/list/areas_with_LS = list() + var/list/areas_with_intercom = list() + var/list/areas_with_camera = list() + + for(var/area/A in world) + if(!(A.type in areas_all)) + areas_all.Add(A.type) + + for(var/obj/machinery/power/apc/APC in GLOB.apcs) + var/area/A = get_area(APC) + if(A && !(A.type in areas_with_APC)) + areas_with_APC.Add(A.type) + + for(var/obj/machinery/alarm/alarm in machines) + var/area/A = get_area(alarm) + if(A && !(A.type in areas_with_air_alarm)) + areas_with_air_alarm.Add(A.type) + + for(var/obj/machinery/requests_console/RC in machines) + var/area/A = get_area(RC) + if(A && !(A.type in areas_with_RC)) + areas_with_RC.Add(A.type) + + for(var/obj/machinery/light/L in machines) + var/area/A = get_area(L) + if(A && !(A.type in areas_with_light)) + areas_with_light.Add(A.type) + + for(var/obj/machinery/light_switch/LS in machines) + var/area/A = get_area(LS) + if(A && !(A.type in areas_with_LS)) + areas_with_LS.Add(A.type) + + for(var/obj/item/device/radio/intercom/I in machines) + var/area/A = get_area(I) + if(A && !(A.type in areas_with_intercom)) + areas_with_intercom.Add(A.type) + + for(var/obj/machinery/camera/C in machines) + var/area/A = get_area(C) + if(A && !(A.type in areas_with_camera)) + areas_with_camera.Add(A.type) + + var/list/areas_without_APC = areas_all - areas_with_APC + var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm + var/list/areas_without_RC = areas_all - areas_with_RC + var/list/areas_without_light = areas_all - areas_with_light + var/list/areas_without_LS = areas_all - areas_with_LS + var/list/areas_without_intercom = areas_all - areas_with_intercom + var/list/areas_without_camera = areas_all - areas_with_camera + + to_world("AREAS WITHOUT AN APC:") + for(var/areatype in areas_without_APC) + to_world("* [areatype]") + + to_world("AREAS WITHOUT AN AIR ALARM:") + for(var/areatype in areas_without_air_alarm) + to_world("* [areatype]") + + to_world("AREAS WITHOUT A REQUEST CONSOLE:") + for(var/areatype in areas_without_RC) + to_world("* [areatype]") + + to_world("AREAS WITHOUT ANY LIGHTS:") + for(var/areatype in areas_without_light) + to_world("* [areatype]") + + to_world("AREAS WITHOUT A LIGHT SWITCH:") + for(var/areatype in areas_without_LS) + to_world("* [areatype]") + + to_world("AREAS WITHOUT ANY INTERCOMS:") + for(var/areatype in areas_without_intercom) + to_world("* [areatype]") + + to_world("AREAS WITHOUT ANY CAMERAS:") + for(var/areatype in areas_without_camera) + to_world("* [areatype]") + +/datum/admins/proc/cmd_admin_dress(input in getmobs()) + set category = "Fun" + set name = "Select equipment" + + if(!check_rights(R_FUN)) + return + + var/target = getmobs()[input] + if(!target) + return + + if(!ishuman(target)) + return + + var/mob/living/carbon/human/H = target + + var/decl/hierarchy/outfit/outfit = tgui_input_list(usr, "Select outfit.", "Select equipment.", outfits()) + if(!outfit) + return + + feedback_add_details("admin_verb","SEQ") + dressup_human(H, outfit, 1) + +/proc/dressup_human(var/mob/living/carbon/human/H, var/decl/hierarchy/outfit/outfit) + if(!H || !outfit) + return + if(outfit.undress) + H.delete_inventory() + outfit.equip(H) + log_and_message_admins("changed the equipment of [key_name(H)] to [outfit.name].") + +/client/proc/startSinglo() + + set category = "Debug" + set name = "Start Singularity" + set desc = "Sets up the singularity and all machines to get power flowing through the station" + + if(tgui_alert(usr, "Are you sure? This will start up the engine. Should only be used during debug!","Start Singularity",list("Yes","No")) != "Yes") + return + + for(var/obj/machinery/power/emitter/E in machines) + if(istype(get_area(E), /area/space)) + E.anchored = TRUE + E.state = 2 + E.connect_to_network() + E.active = TRUE + for(var/obj/machinery/field_generator/F in machines) + if(istype(get_area(F), /area/space)) + F.Varedit_start = 1 + for(var/obj/machinery/power/grounding_rod/GR in machines) + GR.anchored = TRUE + GR.update_icon() + for(var/obj/machinery/power/tesla_coil/TC in machines) + TC.anchored = TRUE + TC.update_icon() + for(var/obj/structure/particle_accelerator/PA in machines) + PA.anchored = TRUE + PA.construction_state = 3 + PA.update_icon() + for(var/obj/machinery/particle_accelerator/PA in machines) + PA.anchored = TRUE + PA.construction_state = 3 + PA.update_icon() + + for(var/obj/machinery/power/rad_collector/Rad in machines) + if(Rad.anchored) + if(!Rad.P) + var/obj/item/weapon/tank/phoron/Phoron = new/obj/item/weapon/tank/phoron(Rad) + Phoron.air_contents.gas["phoron"] = 70 + Rad.drainratio = 0 + Rad.P = Phoron + Phoron.loc = Rad + + if(!Rad.active) + Rad.toggle_power() + +/client/proc/setup_supermatter_engine() + set category = "Debug" + set name = "Setup supermatter" + set desc = "Sets up the supermatter engine" + + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/response = tgui_alert(usr, "Are you sure? This will start up the engine. Should only be used during debug!","Setup Supermatter",list("Setup Completely","Setup except coolant","No")) + + if(response == "No") + return + + var/found_the_pump = 0 + var/obj/machinery/power/supermatter/SM + + for(var/obj/machinery/M in machines) + if(!M) + continue + if(!M.loc) + continue + if(!M.loc.loc) + continue + + if(istype(M.loc.loc,/area/engineering/engine_room)) + if(istype(M,/obj/machinery/power/rad_collector)) + var/obj/machinery/power/rad_collector/Rad = M + Rad.anchored = TRUE + Rad.connect_to_network() + + var/obj/item/weapon/tank/phoron/Phoron = new/obj/item/weapon/tank/phoron(Rad) + + Phoron.air_contents.gas["phoron"] = 29.1154 //This is a full tank if you filled it from a canister + Rad.P = Phoron + + Phoron.loc = Rad + + if(!Rad.active) + Rad.toggle_power() + Rad.update_icon() + + else if(istype(M,/obj/machinery/atmospherics/binary/pump)) //Turning on every pump. + var/obj/machinery/atmospherics/binary/pump/Pump = M + if(Pump.name == "Engine Feed" && response == "Setup Completely") + found_the_pump = 1 + Pump.air2.gas["nitrogen"] = 3750 //The contents of 2 canisters. + Pump.air2.temperature = 50 + Pump.air2.update_values() + Pump.update_use_power(USE_POWER_IDLE) + Pump.target_pressure = 4500 + Pump.update_icon() + + else if(istype(M,/obj/machinery/power/supermatter)) + SM = M + spawn(50) + SM.power = 320 + + else if(istype(M,/obj/machinery/power/smes)) //This is the SMES inside the engine room. We don't need much power. + var/obj/machinery/power/smes/SMES = M + SMES.input_attempt = 1 + SMES.input_level = 200000 + SMES.output_level = 75000 + + else if(istype(M.loc.loc,/area/engineering/engine_smes)) //Set every SMES to charge and spit out 300,000 power between the 4 of them. + if(istype(M,/obj/machinery/power/smes)) + var/obj/machinery/power/smes/SMES = M + SMES.input_attempt = 1 + SMES.input_level = 200000 + SMES.output_level = 75000 + + if(!found_the_pump && response == "Setup Completely") + to_chat(src, span_red("Unable to locate air supply to fill up with coolant, adding some coolant around the supermatter")) + var/turf/simulated/T = SM.loc + T.zone.air.gas["nitrogen"] += 450 + T.zone.air.temperature = 50 + T.zone.air.update_values() + + + log_admin("[key_name(usr)] setup the supermatter engine [response == "Setup except coolant" ? "without coolant" : ""]") + message_admins(span_blue("[key_name_admin(usr)] setup the supermatter engine [response == "Setup except coolant" ? "without coolant": ""]"), 1) + return + + + +/client/proc/cmd_debug_mob_lists() + set category = "Debug" + set name = "Debug Mob Lists" + set desc = "For when you just gotta know" + + switch(tgui_input_list(usr, "Which list?", "List Choice", list("Players","Admins","Mobs","Living Mobs","Dead Mobs", "Clients"))) + if("Players") + to_chat(usr, span("filter_debuglogs", jointext(player_list,","))) + if("Admins") + to_chat(usr, span("filter_debuglogs", jointext(GLOB.admins,","))) + if("Mobs") + to_chat(usr, span("filter_debuglogs", jointext(mob_list,","))) + if("Living Mobs") + to_chat(usr, span("filter_debuglogs", jointext(living_mob_list,","))) + if("Dead Mobs") + to_chat(usr, span("filter_debuglogs", jointext(dead_mob_list,","))) + if("Clients") + to_chat(usr, span("filter_debuglogs", jointext(GLOB.clients,","))) + +/client/proc/cmd_debug_using_map() + set category = "Debug" + set name = "Debug Map Datum" + set desc = "Debug the map metadata about the currently compiled in map." + + if(!check_rights(R_DEBUG)) + return + debug_variables(using_map) + +// DNA2 - Admin Hax +/client/proc/cmd_admin_toggle_block(var/mob/M,var/block) + if(!ticker) + tgui_alert_async(usr, "Wait until the game starts") + return + if(istype(M, /mob/living/carbon)) + M.dna.SetSEState(block,!M.dna.GetSEState(block)) + domutcheck(M,null,MUTCHK_FORCED) + M.update_mutations() + var/state="[M.dna.GetSEState(block)?"on":"off"]" + var/blockname=assigned_blocks[block] + message_admins("[key_name_admin(src)] has toggled [M.key]'s [blockname] block [state]!") + log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!") + else + tgui_alert_async(usr, "Invalid mob") + +/datum/admins/proc/view_runtimes() + set category = "Debug" + set name = "View Runtimes" + set desc = "Open the Runtime Viewer" + + if(!check_rights(R_DEBUG)) + return + + error_cache.showTo(usr) + +/datum/admins/proc/change_weather() + set category = "Debug" + set name = "Change Weather" + set desc = "Changes the current weather." + + if(!check_rights(R_DEBUG)) + return + + var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to modify the weather on?", "Change Weather", SSplanets.planets) + if(istype(planet)) + var/datum/weather/new_weather = tgui_input_list(usr, "What weather do you want to change to?", "Change Weather", planet.weather_holder.allowed_weather_types) + if(new_weather) + planet.weather_holder.change_weather(new_weather) + planet.weather_holder.rebuild_forecast() + var/log = "[key_name(src)] changed [planet.name]'s weather to [new_weather]." + message_admins(log) + log_admin(log) + +/datum/admins/proc/toggle_firework_override() + set category = "Fun" + set name = "Toggle Weather Firework Override" + set desc = "Toggles ability for weather fireworks to affect weather on planet of choice." + + if(!check_rights(R_DEBUG)) + return + + var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to toggle firework effects on?", "Change Weather", SSplanets.planets) + if(istype(planet) && planet.weather_holder) + planet.weather_holder.firework_override = !(planet.weather_holder.firework_override) + var/log = "[key_name(src)] toggled [planet.name]'s firework override to [planet.weather_holder.firework_override ? "on" : "off"]." + message_admins(log) + log_admin(log) + +/datum/admins/proc/change_time() + set category = "Debug" + set name = "Change Planet Time" + set desc = "Changes the time of a planet." + + if(!check_rights(R_DEBUG)) + return + + var/datum/planet/planet = tgui_input_list(usr, "Which planet do you want to modify time on?", "Change Time", SSplanets.planets) + if(istype(planet)) + var/datum/time/current_time_datum = planet.current_time + var/new_hour = tgui_input_number(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh")), 23) + if(!isnull(new_hour)) + var/new_minute = tgui_input_number(usr, "What minute do you want to change to?", "Change Time", text2num(current_time_datum.show_time("mm")), 59) + if(!isnull(new_minute)) + var/type_needed = current_time_datum.type + var/datum/time/new_time = new type_needed() + new_time = new_time.add_hours(new_hour) + new_time = new_time.add_minutes(new_minute) + planet.current_time = new_time + spawn(1) + planet.update_sun() + + var/log = "[key_name(src)] changed [planet.name]'s time to [planet.current_time.show_time("hh:mm")]." + message_admins(log) + log_admin(log) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index ce39bbb0b8a..40b47f2f50d 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -1,187 +1,187 @@ -/client/proc/air_report() - set category = "Debug" - set name = "Show Air Report" - - if(!master_controller || !air_master) - tgui_alert_async(usr,"Master_controller or air_master not found.","Air Report") - return - - var/active_groups = air_master.active_zones - var/inactive_groups = air_master.zones.len - active_groups - - var/hotspots = 0 - for(var/obj/fire/hotspot in world) - hotspots++ - - var/active_on_main_station = 0 - var/inactive_on_main_station = 0 - for(var/zone/zone in air_master.zones) - var/turf/simulated/turf = locate() in zone.contents - if(turf?.z in using_map.station_levels) - if(zone.needs_update) - active_on_main_station++ - else - inactive_on_main_station++ - - var/output = {"AIR SYSTEMS REPORT
                    -General Processing Data
                    - Cycle: [air_master.current_cycle]
                    - Groups: [air_master.zones.len]
                    ----- Active: [active_groups]
                    ----- Inactive: [inactive_groups]

                    ----- Active on station: [active_on_main_station]
                    ----- Inactive on station: [inactive_on_main_station]
                    -
                    -Special Processing Data
                    - Hotspot Processing: [hotspots]
                    -
                    -Geometry Processing Data
                    - Tile Update: [air_master.tiles_to_update.len]
                    -"} - - usr << browse(output,"window=airreport") - -/client/proc/fix_next_move() - set category = "Debug" - set name = "Unfreeze Everyone" - var/largest_move_time = 0 - var/largest_click_time = 0 - var/mob/largest_move_mob = null - var/mob/largest_click_mob = null - for(var/mob/M in mob_list) - if(!M.client) - continue - if(M.next_move >= largest_move_time) - largest_move_mob = M - if(M.next_move > world.time) - largest_move_time = M.next_move - world.time - else - largest_move_time = 1 - if(M.next_click >= largest_click_time) - largest_click_mob = M - if(M.next_click > world.time) - largest_click_time = M.next_click - world.time - else - largest_click_time = 0 - log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") - M.next_move = 1 - M.next_click = 0 - message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) - message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) - message_admins("world.time = [world.time]", 1) - feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/radio_report() - set category = "Debug" - set name = "Radio report" - - var/output = "Radio Report
                    " - for (var/fq in radio_controller.frequencies) - output += "Freq: [fq]
                    " - var/datum/radio_frequency/fqs = radio_controller.frequencies[fq] - if (!fqs) - output += "  ERROR
                    " - continue - for (var/radio_filter in fqs.devices) - var/list/f = fqs.devices[radio_filter] - if (!f) - output += "  [radio_filter]: ERROR
                    " - continue - output += "  [radio_filter]: [f.len]
                    " - for (var/device in f) - if (isobj(device)) - output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
                    " - else - output += "    [device]
                    " - - usr << browse(output,"window=radioreport") - feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reload_admins() - set name = "Reload Admins" - set category = "Debug" - - if(!check_rights(R_SERVER)) return - - message_admins("[usr] manually reloaded admins") - load_admins() - feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reload_eventMs() - set name = "Reload Event Managers" - set category = "Debug" - - if(!check_rights(R_SERVER)) return - - message_admins("[usr] manually reloaded Event Managers") - world.load_mods() - - -//todo: -/client/proc/jump_to_dead_group() - set name = "Jump to dead group" - set category = "Debug" - /* - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!air_master) - to_chat(usr, "Cannot find air_system") - return - var/datum/air_group/dead_groups = list() - for(var/datum/air_group/group in air_master.air_groups) - if (!group.group_processing) - dead_groups += group - var/datum/air_group/dest_group = pick(dead_groups) - usr.loc = pick(dest_group.members) - feedback_add_details("admin_verb","JDAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - */ - -/client/proc/kill_airgroup() - set name = "Kill Local Airgroup" - set desc = "Use this to allow manual manupliation of atmospherics." - set category = "Debug" - /* - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!air_master) - to_chat(usr, "Cannot find air_system") - return - - var/turf/T = get_turf(usr) - if(istype(T, /turf/simulated)) - var/datum/air_group/AG = T:parent - AG.next_check = 30 - AG.group_processing = 0 - else - to_chat(usr, "Local airgroup is unsimulated!") - feedback_add_details("admin_verb","KLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - */ - -/client/proc/print_jobban_old() - set name = "Print Jobban Log" - set desc = "This spams all the active jobban entries for the current round to standard output." - set category = "Debug" - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - to_chat(usr, "[t]") - -/client/proc/print_jobban_old_filter() - set name = "Search Jobban Log" - set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." - set category = "Debug" - - var/job_filter = tgui_input_text(usr, "Contains what?","Job Filter") - if(!job_filter) - return - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - if(findtext(t, job_filter)) - to_chat(usr, "[t]") +/client/proc/air_report() + set category = "Debug" + set name = "Show Air Report" + + if(!master_controller || !air_master) + tgui_alert_async(usr,"Master_controller or air_master not found.","Air Report") + return + + var/active_groups = air_master.active_zones + var/inactive_groups = air_master.zones.len - active_groups + + var/hotspots = 0 + for(var/obj/fire/hotspot in world) + hotspots++ + + var/active_on_main_station = 0 + var/inactive_on_main_station = 0 + for(var/zone/zone in air_master.zones) + var/turf/simulated/turf = locate() in zone.contents + if(turf?.z in using_map.station_levels) + if(zone.needs_update) + active_on_main_station++ + else + inactive_on_main_station++ + + var/output = {"AIR SYSTEMS REPORT
                    +General Processing Data
                    + Cycle: [air_master.current_cycle]
                    + Groups: [air_master.zones.len]
                    +---- Active: [active_groups]
                    +---- Inactive: [inactive_groups]

                    +---- Active on station: [active_on_main_station]
                    +---- Inactive on station: [inactive_on_main_station]
                    +
                    +Special Processing Data
                    + Hotspot Processing: [hotspots]
                    +
                    +Geometry Processing Data
                    + Tile Update: [air_master.tiles_to_update.len]
                    +"} + + usr << browse(output,"window=airreport") + +/client/proc/fix_next_move() + set category = "Debug" + set name = "Unfreeze Everyone" + var/largest_move_time = 0 + var/largest_click_time = 0 + var/mob/largest_move_mob = null + var/mob/largest_click_mob = null + for(var/mob/M in mob_list) + if(!M.client) + continue + if(M.next_move >= largest_move_time) + largest_move_mob = M + if(M.next_move > world.time) + largest_move_time = M.next_move - world.time + else + largest_move_time = 1 + if(M.next_click >= largest_click_time) + largest_click_mob = M + if(M.next_click > world.time) + largest_click_time = M.next_click - world.time + else + largest_click_time = 0 + log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") + M.next_move = 1 + M.next_click = 0 + message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) + message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) + message_admins("world.time = [world.time]", 1) + feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/radio_report() + set category = "Debug" + set name = "Radio report" + + var/output = "Radio Report
                    " + for (var/fq in radio_controller.frequencies) + output += "Freq: [fq]
                    " + var/datum/radio_frequency/fqs = radio_controller.frequencies[fq] + if (!fqs) + output += "  ERROR
                    " + continue + for (var/radio_filter in fqs.devices) + var/list/f = fqs.devices[radio_filter] + if (!f) + output += "  [radio_filter]: ERROR
                    " + continue + output += "  [radio_filter]: [f.len]
                    " + for (var/device in f) + if (isobj(device)) + output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
                    " + else + output += "    [device]
                    " + + usr << browse(output,"window=radioreport") + feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reload_admins() + set name = "Reload Admins" + set category = "Debug" + + if(!check_rights(R_SERVER)) return + + message_admins("[usr] manually reloaded admins") + load_admins() + feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reload_eventMs() + set name = "Reload Event Managers" + set category = "Debug" + + if(!check_rights(R_SERVER)) return + + message_admins("[usr] manually reloaded Event Managers") + world.load_mods() + + +//todo: +/client/proc/jump_to_dead_group() + set name = "Jump to dead group" + set category = "Debug" + /* + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!air_master) + to_chat(usr, "Cannot find air_system") + return + var/datum/air_group/dead_groups = list() + for(var/datum/air_group/group in air_master.air_groups) + if (!group.group_processing) + dead_groups += group + var/datum/air_group/dest_group = pick(dead_groups) + usr.loc = pick(dest_group.members) + feedback_add_details("admin_verb","JDAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + */ + +/client/proc/kill_airgroup() + set name = "Kill Local Airgroup" + set desc = "Use this to allow manual manupliation of atmospherics." + set category = "Debug" + /* + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!air_master) + to_chat(usr, "Cannot find air_system") + return + + var/turf/T = get_turf(usr) + if(istype(T, /turf/simulated)) + var/datum/air_group/AG = T:parent + AG.next_check = 30 + AG.group_processing = 0 + else + to_chat(usr, "Local airgroup is unsimulated!") + feedback_add_details("admin_verb","KLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + */ + +/client/proc/print_jobban_old() + set name = "Print Jobban Log" + set desc = "This spams all the active jobban entries for the current round to standard output." + set category = "Debug" + + to_chat(usr, "Jobbans active in this round.") + for(var/t in jobban_keylist) + to_chat(usr, "[t]") + +/client/proc/print_jobban_old_filter() + set name = "Search Jobban Log" + set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." + set category = "Debug" + + var/job_filter = tgui_input_text(usr, "Contains what?","Job Filter") + if(!job_filter) + return + + to_chat(usr, "Jobbans active in this round.") + for(var/t in jobban_keylist) + if(findtext(t, job_filter)) + to_chat(usr, "[t]") diff --git a/code/modules/admin/verbs/fps.dm b/code/modules/admin/verbs/fps.dm index c3b9999afbc..3c1da452035 100644 --- a/code/modules/admin/verbs/fps.dm +++ b/code/modules/admin/verbs/fps.dm @@ -1,23 +1,23 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~ -//Replaces the old Ticklag verb, fps is easier to understand -/client/proc/set_server_fps() - set category = "Debug" - set name = "Set Server FPS" - set desc = "Sets game speed in frames-per-second. Can potentially break the game" - - if(!check_rights(R_DEBUG)) - return - - var/new_fps = round(tgui_input_number(usr, "Sets game frames-per-second. Can potentially break the game (default: [config.fps])", "FPS", world.fps)) - if(new_fps <= 0) - to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") - return - if(new_fps > config.fps * 1.5) - if(tgui_alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [config.fps]", "Warning!", list("Confirm", "ABORT-ABORT-ABORT")) != "Confirm") - return - - var/msg = "[key_name(src)] has modified world.fps to [new_fps]" - log_admin(msg, 0) - message_admins(msg, 0) - world.change_fps(new_fps) - feedback_add_details("admin_verb", "SETFPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +//Merged Doohl's and the existing ticklag as they both had good elements about them ~ +//Replaces the old Ticklag verb, fps is easier to understand +/client/proc/set_server_fps() + set category = "Debug" + set name = "Set Server FPS" + set desc = "Sets game speed in frames-per-second. Can potentially break the game" + + if(!check_rights(R_DEBUG)) + return + + var/new_fps = round(tgui_input_number(usr, "Sets game frames-per-second. Can potentially break the game (default: [config.fps])", "FPS", world.fps), round(config.fps * 1.5)) + if(new_fps <= 0) + to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") + return + if(new_fps > config.fps * 1.5) + if(tgui_alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [config.fps]", "Warning!", list("Confirm", "ABORT-ABORT-ABORT")) != "Confirm") + return + + var/msg = "[key_name(src)] has modified world.fps to [new_fps]" + log_admin(msg, 0) + message_admins(msg, 0) + world.change_fps(new_fps) + feedback_add_details("admin_verb", "SETFPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 723d59b5772..6d79548df35 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -24,16 +24,16 @@ set category = null if(!src.holder) - to_chat(src, "Only Admins may use this command.") + to_chat(src, span_red("Only Admins may use this command.")) return var/client/target = tgui_input_list(src,"Choose somebody to grant access to the server's runtime logs (permissions expire at the end of each round):","Grant Permissions", GLOB.clients) if(!istype(target,/client)) - to_chat(src, "Error: giveruntimelog(): Client not found.") + to_chat(src, span_red("Error: giveruntimelog(): Client not found.")) return target.verbs |= /client/proc/getruntimelog - to_chat(target, "You have been granted access to runtime logs. Please use them responsibly or risk being banned.") + to_chat(target, span_red("You have been granted access to runtime logs. Please use them responsibly or risk being banned.")) return @@ -89,7 +89,7 @@ if( fexists(path) ) src << run( file(path) ) else - to_chat(src, "Error: view_txt_log(): File not found/Invalid path([path]).") + to_chat(src, span_red("Error: view_txt_log(): File not found/Invalid path([path]).")) return feedback_add_details("admin_verb","VTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm index 9ad46191667..1024fb7d323 100644 --- a/code/modules/admin/verbs/grief_fixers.dm +++ b/code/modules/admin/verbs/grief_fixers.dm @@ -20,7 +20,7 @@ to_chat(usr, "\[1/5\] - Supermatter depowered") // Remove all gases from all pipenets - for(var/datum/pipe_network/PN in pipe_networks) + for(var/datum/pipe_network/PN in SSmachines.networks) for(var/datum/gas_mixture/G in PN.gases) G.gas = list() G.update_values() @@ -48,4 +48,4 @@ SSair.RebootZAS() to_chat(usr, "\[5/5\] - ZAS Rebooted") - to_world("Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds.") \ No newline at end of file + to_world("Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds.") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index fea37f2931a..5859db3ab78 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -1,424 +1,424 @@ -//- Are all the floors with or without air, as they should be? (regular or airless) -//- Does the area have an APC? -//- Does the area have an Air Alarm? -//- Does the area have a Request Console? -//- Does the area have lights? -//- Does the area have a light switch? -//- Does the area have enough intercoms? -//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) -//- Is the area connected to the scrubbers air loop? -//- Is the area connected to the vent air loop? (vent pumps) -//- Is everything wired properly? -//- Does the area have a fire alarm and firedoors? -//- Do all pod doors work properly? -//- Are accesses set properly on doors, pod buttons, etc. -//- Are all items placed properly? (not below vents, scrubbers, tables) -//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? -//- Check for any misplaced or stacked piece of pipe (air and disposal) -//- Check for any misplaced or stacked piece of wire -//- Identify how hard it is to break into the area and where the weak points are -//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. - -var/camera_range_display_status = 0 -var/intercom_range_display_status = 0 - -GLOBAL_LIST_BOILERPLATE(all_debugging_effects, /obj/effect/debugging) - -/obj/effect/debugging/camera_range - icon = 'icons/480x480.dmi' - icon_state = "25percent" - -/obj/effect/debugging/camera_range/New() - src.pixel_x = -224 - src.pixel_y = -224 - -/obj/effect/debugging/marker - icon = 'icons/turf/areas.dmi' - icon_state = "yellow" - -/obj/effect/debugging/marker/Move() - return 0 - -/client/proc/do_not_use_these() - set category = "Mapping" - set name = "-None of these are for ingame use!!" - -/client/proc/camera_view() - set category = "Mapping" - set name = "Camera Range Display" - - if(camera_range_display_status) - camera_range_display_status = 0 - else - camera_range_display_status = 1 - - - - for(var/obj/effect/debugging/camera_range/C in all_debugging_effects) - qdel(C) - - if(camera_range_display_status) - for(var/obj/machinery/camera/C in cameranet.cameras) - new/obj/effect/debugging/camera_range(C.loc) - feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - - -/client/proc/sec_camera_report() - set category = "Mapping" - set name = "Camera Report" - - if(!master_controller) - tgui_alert_async(usr,"Master_controller not found.","Sec Camera Report") - return 0 - - var/list/obj/machinery/camera/CL = list() - - for(var/obj/machinery/camera/C in cameranet.cameras) - CL += C - - var/output = {"CAMERA ANNOMALITIES REPORT
                    -The following annomalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
                      "} - - for(var/obj/machinery/camera/C1 in CL) - for(var/obj/machinery/camera/C2 in CL) - if(C1 != C2) - if(C1.c_tag == C2.c_tag) - output += "
                    • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
                    • " - if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) - output += "
                    • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
                    • " - if(C1.loc == C2.loc) - output += "
                    • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
                    • " - var/turf/T = get_step(C1,turn(C1.dir,180)) - if(!T || !isturf(T) || !T.density ) - if(!(locate(/obj/structure/grille,T))) - var/window_check = 0 - for(var/obj/structure/window/W in T) - if (W.dir == turn(C1.dir,180) || (W.dir in list(5,6,9,10)) ) - window_check = 1 - break - if(!window_check) - output += "
                    • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
                    • " - - output += "
                    " - usr << browse(output,"window=airreport;size=1000x500") - feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/intercom_view() - set category = "Mapping" - set name = "Intercom Range Display" - - if(intercom_range_display_status) - intercom_range_display_status = 0 - else - intercom_range_display_status = 1 - - for(var/obj/effect/debugging/marker/M in all_debugging_effects) - qdel(M) - - if(intercom_range_display_status) - for(var/obj/item/device/radio/intercom/I in machines) - for(var/turf/T in orange(7,I)) - var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) - if (!(F in view(7,I.loc))) - qdel(F) - feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -var/list/debug_verbs = list ( - /client/proc/do_not_use_these - ,/client/proc/camera_view - ,/client/proc/sec_camera_report - ,/client/proc/intercom_view - ,/client/proc/Cell - ,/client/proc/atmosscan - ,/client/proc/powerdebug - ,/client/proc/count_objects_on_z_level - ,/client/proc/count_objects_all - ,/client/proc/cmd_assume_direct_control - ,/client/proc/jump_to_dead_group - ,/client/proc/startSinglo - ,/client/proc/set_server_fps - ,/client/proc/cmd_admin_grantfullaccess - ,/client/proc/kaboom - ,/client/proc/cmd_admin_areatest - ,/client/proc/cmd_admin_rejuvenate - ,/datum/admins/proc/show_traitor_panel - ,/client/proc/print_jobban_old - ,/client/proc/print_jobban_old_filter - ,/client/proc/forceEvent - ,/client/proc/break_all_air_groups - ,/client/proc/regroup_all_air_groups - ,/client/proc/kill_pipe_processing - ,/client/proc/kill_air_processing - ,/client/proc/disable_communication - ,/client/proc/disable_movement - ,/client/proc/Zone_Info - ,/client/proc/Test_ZAS_Connection - ,/client/proc/ZoneTick - ,/client/proc/rebootAirMaster - ,/client/proc/hide_debug_verbs - ,/client/proc/testZAScolors - ,/client/proc/testZAScolors_remove - ,/datum/admins/proc/setup_supermatter - ,/client/proc/atmos_toggle_debug - ,/client/proc/spawn_tanktransferbomb - ,/client/proc/take_picture - ) - - -/client/proc/enable_debug_verbs() - set category = "Debug" - set name = "Debug verbs" - - if(!check_rights(R_DEBUG)) return - - verbs += debug_verbs - - feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/hide_debug_verbs() - set category = "Debug" - set name = "Hide Debug verbs" - - if(!check_rights(R_DEBUG)) return - - verbs -= debug_verbs - - feedback_add_details("admin_verb","hDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/var/list/testZAScolors_turfs = list() -/client/var/list/testZAScolors_zones = list() -/client/var/usedZAScolors = 0 -/client/var/list/image/ZAScolors = list() - -/client/proc/recurse_zone(var/zone/Z, var/recurse_level =1) - testZAScolors_zones += Z - if(recurse_level > 10) - return - var/icon/yellow = new('icons/misc/debug_group.dmi', "yellow") - - for(var/turf/T in Z.contents) - images += image(yellow, T, "zasdebug", TURF_LAYER) - testZAScolors_turfs += T - for(var/connection_edge/zone/edge in Z.edges) - var/zone/connected = edge.get_connected_zone(Z) - if(connected in testZAScolors_zones) - continue - recurse_zone(connected,recurse_level+1) - - -/client/proc/testZAScolors() - set category = "ZAS" - set name = "Check ZAS connections" - - if(!check_rights(R_DEBUG)) return - testZAScolors_remove() - - var/turf/simulated/location = get_turf(usr) - - if(!istype(location, /turf/simulated)) // We're in space, let's not cause runtimes. - to_chat(usr, "this debug tool cannot be used from space") - return - - var/icon/red = new('icons/misc/debug_group.dmi', "red") //created here so we don't have to make thousands of these. - var/icon/green = new('icons/misc/debug_group.dmi', "green") - var/icon/blue = new('icons/misc/debug_group.dmi', "blue") - - if(!usedZAScolors) - to_chat(usr, "ZAS Test Colors") - to_chat(usr, "Green = Zone you are standing in") - to_chat(usr, "Blue = Connected zone to the zone you are standing in") - to_chat(usr, "Yellow = A zone that is connected but not one adjacent to your connected zone") - to_chat(usr, "Red = Not connected") - usedZAScolors = 1 - - testZAScolors_zones += location.zone - for(var/turf/T in location.zone.contents) - images += image(green, T,"zasdebug", TURF_LAYER) - testZAScolors_turfs += T - for(var/connection_edge/zone/edge in location.zone.edges) - var/zone/Z = edge.get_connected_zone(location.zone) - testZAScolors_zones += Z - for(var/turf/T in Z.contents) - images += image(blue, T,"zasdebug",TURF_LAYER) - testZAScolors_turfs += T - for(var/connection_edge/zone/z_edge in Z.edges) - var/zone/connected = z_edge.get_connected_zone(Z) - if(connected in testZAScolors_zones) - continue - recurse_zone(connected,1) - - for(var/turf/T in range(25,location)) - if(!istype(T)) - continue - if(T in testZAScolors_turfs) - continue - images += image(red, T, "zasdebug", TURF_LAYER) - testZAScolors_turfs += T - -/client/proc/testZAScolors_remove() - set category = "ZAS" - set name = "Remove ZAS connection colors" - - testZAScolors_turfs.Cut() - testZAScolors_zones.Cut() - - if(images.len) - for(var/image/i in images) - if(i.icon_state == "zasdebug") - images.Remove(i) - -/client/proc/rebootAirMaster() - set category = "ZAS" - set name = "Reboot ZAS" - - if(tgui_alert(usr, "This will destroy and remake all zone geometry on the whole map.","Reboot ZAS",list("Reboot ZAS","Nevermind")) == "Reboot ZAS") - SSair.RebootZAS() - -/client/proc/count_objects_on_z_level() - set category = "Mapping" - set name = "Count Objects On Level" - var/level = tgui_input_text(usr, "Which z-level?","Level?") - if(!level) return - var/num_level = text2num(level) - if(!num_level) return - if(!isnum(num_level)) return - - var/type_text = tgui_input_text(usr, "Which type path?","Path?") - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 1 - - var/list/atom/atom_list = list() - - for(var/atom/A in world) - if(istype(A,type_path)) - var/atom/B = A - while(!(isturf(B.loc))) - if(B && B.loc) - B = B.loc - else - break - if(B) - if(B.z == num_level) - count++ - atom_list += A - /* - var/atom/temp_atom - for(var/i = 0; i <= (atom_list.len/10); i++) - var/line = "" - for(var/j = 1; j <= 10; j++) - if(i*10+j <= atom_list.len) - temp_atom = atom_list[i*10+j] - line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " - to_world(line)*/ - - to_world("There are [count] objects of type [type_path] on z-level [num_level]") - feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_objects_all() - set category = "Mapping" - set name = "Count Objects All" - - var/type_text = tgui_input_text(usr, "Which type path?","") - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - for(var/atom/A in world) - if(istype(A,type_path)) - count++ - /* - var/atom/temp_atom - for(var/i = 0; i <= (atom_list.len/10); i++) - var/line = "" - for(var/j = 1; j <= 10; j++) - if(i*10+j <= atom_list.len) - temp_atom = atom_list[i*10+j] - line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " - to_world(line)*/ - - to_world("There are [count] objects of type [type_path] in the game world") - feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -var/global/prevent_airgroup_regroup = 0 - -/client/proc/break_all_air_groups() - set category = "Mapping" - set name = "Break All Airgroups" - - /*prevent_airgroup_regroup = 1 - for(var/datum/air_group/AG in air_master.air_groups) - AG.suspend_group_processing() - message_admins("[src.ckey] used 'Break All Airgroups'")*/ - -/client/proc/regroup_all_air_groups() - set category = "Mapping" - set name = "Regroup All Airgroups Attempt" - - to_chat(usr, "Proc disabled.") //Why not.. Delete the procs instead? - - /*prevent_airgroup_regroup = 0 - for(var/datum/air_group/AG in air_master.air_groups) - AG.check_regroup() - message_admins("[src.ckey] used 'Regroup All Airgroups Attempt'")*/ - -/client/proc/kill_pipe_processing() - set category = "Mapping" - set name = "Kill pipe processing" - - to_chat(usr, "Proc disabled.") - - /*pipe_processing_killed = !pipe_processing_killed - if(pipe_processing_killed) - message_admins("[src.ckey] used 'kill pipe processing', stopping all pipe processing.") - else - message_admins("[src.ckey] used 'kill pipe processing', restoring all pipe processing.")*/ - -/client/proc/kill_air_processing() - set category = "Mapping" - set name = "Kill air processing" - - to_chat(usr, "Proc disabled.") - - /*air_processing_killed = !air_processing_killed - if(air_processing_killed) - message_admins("[src.ckey] used 'kill air processing', stopping all air processing.") - else - message_admins("[src.ckey] used 'kill air processing', restoring all air processing.")*/ - -//This proc is intended to detect lag problems relating to communication procs -var/global/say_disabled = 0 -/client/proc/disable_communication() - set category = "Mapping" - set name = "Disable all communication verbs" - - to_chat(usr, "Proc disabled.") - - /*say_disabled = !say_disabled - if(say_disabled) - message_admins("[src.ckey] used 'Disable all communication verbs', killing all communication methods.") - else - message_admins("[src.ckey] used 'Disable all communication verbs', restoring all communication methods.")*/ - -//This proc is intended to detect lag problems relating to movement -var/global/movement_disabled = 0 -var/global/movement_disabled_exception //This is the client that calls the proc, so he can continue to run around to gauge any change to lag. -/client/proc/disable_movement() - set category = "Mapping" - set name = "Disable all movement" - - to_chat(usr, "Proc disabled.") - - /*movement_disabled = !movement_disabled - if(movement_disabled) - message_admins("[src.ckey] used 'Disable all movement', killing all movement.") - movement_disabled_exception = usr.ckey - else - message_admins("[src.ckey] used 'Disable all movement', restoring all movement.")*/ +//- Are all the floors with or without air, as they should be? (regular or airless) +//- Does the area have an APC? +//- Does the area have an Air Alarm? +//- Does the area have a Request Console? +//- Does the area have lights? +//- Does the area have a light switch? +//- Does the area have enough intercoms? +//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) +//- Is the area connected to the scrubbers air loop? +//- Is the area connected to the vent air loop? (vent pumps) +//- Is everything wired properly? +//- Does the area have a fire alarm and firedoors? +//- Do all pod doors work properly? +//- Are accesses set properly on doors, pod buttons, etc. +//- Are all items placed properly? (not below vents, scrubbers, tables) +//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? +//- Check for any misplaced or stacked piece of pipe (air and disposal) +//- Check for any misplaced or stacked piece of wire +//- Identify how hard it is to break into the area and where the weak points are +//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. + +var/camera_range_display_status = 0 +var/intercom_range_display_status = 0 + +GLOBAL_LIST_BOILERPLATE(all_debugging_effects, /obj/effect/debugging) + +/obj/effect/debugging/camera_range + icon = 'icons/480x480.dmi' + icon_state = "25percent" + +/obj/effect/debugging/camera_range/New() + src.pixel_x = -224 + src.pixel_y = -224 + +/obj/effect/debugging/marker + icon = 'icons/turf/areas.dmi' + icon_state = "yellow" + +/obj/effect/debugging/marker/Move() + return 0 + +/client/proc/do_not_use_these() + set category = "Mapping" + set name = "-None of these are for ingame use!!" + +/client/proc/camera_view() + set category = "Mapping" + set name = "Camera Range Display" + + if(camera_range_display_status) + camera_range_display_status = 0 + else + camera_range_display_status = 1 + + + + for(var/obj/effect/debugging/camera_range/C in all_debugging_effects) + qdel(C) + + if(camera_range_display_status) + for(var/obj/machinery/camera/C in cameranet.cameras) + new/obj/effect/debugging/camera_range(C.loc) + feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + + +/client/proc/sec_camera_report() + set category = "Mapping" + set name = "Camera Report" + + if(!master_controller) + tgui_alert_async(usr,"Master_controller not found.","Sec Camera Report") + return 0 + + var/list/obj/machinery/camera/CL = list() + + for(var/obj/machinery/camera/C in cameranet.cameras) + CL += C + + var/output = {"CAMERA ANNOMALITIES REPORT
                    +The following annomalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
                      "} + + for(var/obj/machinery/camera/C1 in CL) + for(var/obj/machinery/camera/C2 in CL) + if(C1 != C2) + if(C1.c_tag == C2.c_tag) + output += "
                    • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
                    • " + if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) + output += "
                    • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
                    • " + if(C1.loc == C2.loc) + output += "
                    • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
                    • " + var/turf/T = get_step(C1,turn(C1.dir,180)) + if(!T || !isturf(T) || !T.density ) + if(!(locate(/obj/structure/grille,T))) + var/window_check = 0 + for(var/obj/structure/window/W in T) + if (W.dir == turn(C1.dir,180) || (W.dir in list(5,6,9,10)) ) + window_check = 1 + break + if(!window_check) + output += "
                    • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
                    • " + + output += "
                    " + usr << browse(output,"window=airreport;size=1000x500") + feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/intercom_view() + set category = "Mapping" + set name = "Intercom Range Display" + + if(intercom_range_display_status) + intercom_range_display_status = 0 + else + intercom_range_display_status = 1 + + for(var/obj/effect/debugging/marker/M in all_debugging_effects) + qdel(M) + + if(intercom_range_display_status) + for(var/obj/item/device/radio/intercom/I in machines) + for(var/turf/T in orange(7,I)) + var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) + if (!(F in view(7,I.loc))) + qdel(F) + feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +var/list/debug_verbs = list ( + /client/proc/do_not_use_these + ,/client/proc/camera_view + ,/client/proc/sec_camera_report + ,/client/proc/intercom_view + ,/client/proc/Cell + ,/client/proc/atmosscan + ,/client/proc/powerdebug + ,/client/proc/count_objects_on_z_level + ,/client/proc/count_objects_all + ,/client/proc/cmd_assume_direct_control + ,/client/proc/jump_to_dead_group + ,/client/proc/startSinglo + ,/client/proc/set_server_fps + ,/client/proc/cmd_admin_grantfullaccess + ,/client/proc/kaboom + ,/client/proc/cmd_admin_areatest + ,/client/proc/cmd_admin_rejuvenate + ,/datum/admins/proc/show_traitor_panel + ,/client/proc/print_jobban_old + ,/client/proc/print_jobban_old_filter + ,/client/proc/forceEvent + ,/client/proc/break_all_air_groups + ,/client/proc/regroup_all_air_groups + ,/client/proc/kill_pipe_processing + ,/client/proc/kill_air_processing + ,/client/proc/disable_communication + ,/client/proc/disable_movement + ,/client/proc/Zone_Info + ,/client/proc/Test_ZAS_Connection + ,/client/proc/ZoneTick + ,/client/proc/rebootAirMaster + ,/client/proc/hide_debug_verbs + ,/client/proc/testZAScolors + ,/client/proc/testZAScolors_remove + ,/datum/admins/proc/setup_supermatter + ,/client/proc/atmos_toggle_debug + ,/client/proc/spawn_tanktransferbomb + ,/client/proc/take_picture + ) + + +/client/proc/enable_debug_verbs() + set category = "Debug" + set name = "Debug verbs" + + if(!check_rights(R_DEBUG)) return + + verbs += debug_verbs + + feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/hide_debug_verbs() + set category = "Debug" + set name = "Hide Debug verbs" + + if(!check_rights(R_DEBUG)) return + + verbs -= debug_verbs + + feedback_add_details("admin_verb","hDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/var/list/testZAScolors_turfs = list() +/client/var/list/testZAScolors_zones = list() +/client/var/usedZAScolors = 0 +/client/var/list/image/ZAScolors = list() + +/client/proc/recurse_zone(var/zone/Z, var/recurse_level =1) + testZAScolors_zones += Z + if(recurse_level > 10) + return + var/icon/yellow = new('icons/misc/debug_group.dmi', "yellow") + + for(var/turf/T in Z.contents) + images += image(yellow, T, "zasdebug", TURF_LAYER) + testZAScolors_turfs += T + for(var/connection_edge/zone/edge in Z.edges) + var/zone/connected = edge.get_connected_zone(Z) + if(connected in testZAScolors_zones) + continue + recurse_zone(connected,recurse_level+1) + + +/client/proc/testZAScolors() + set category = "ZAS" + set name = "Check ZAS connections" + + if(!check_rights(R_DEBUG)) return + testZAScolors_remove() + + var/turf/simulated/location = get_turf(usr) + + if(!istype(location, /turf/simulated)) // We're in space, let's not cause runtimes. + to_chat(usr, span_red("this debug tool cannot be used from space")) + return + + var/icon/red = new('icons/misc/debug_group.dmi', "red") //created here so we don't have to make thousands of these. + var/icon/green = new('icons/misc/debug_group.dmi', "green") + var/icon/blue = new('icons/misc/debug_group.dmi', "blue") + + if(!usedZAScolors) + to_chat(usr, "ZAS Test Colors") + to_chat(usr, "[span_green("Green")] = Zone you are standing in") + to_chat(usr, "[span_blue("Blue")] = Connected zone to the zone you are standing in") + to_chat(usr, "[span_yellow("Yellow")] = A zone that is connected but not one adjacent to your connected zone") + to_chat(usr, "[span_red("Red")] = Not connected") + usedZAScolors = 1 + + testZAScolors_zones += location.zone + for(var/turf/T in location.zone.contents) + images += image(green, T,"zasdebug", TURF_LAYER) + testZAScolors_turfs += T + for(var/connection_edge/zone/edge in location.zone.edges) + var/zone/Z = edge.get_connected_zone(location.zone) + testZAScolors_zones += Z + for(var/turf/T in Z.contents) + images += image(blue, T,"zasdebug",TURF_LAYER) + testZAScolors_turfs += T + for(var/connection_edge/zone/z_edge in Z.edges) + var/zone/connected = z_edge.get_connected_zone(Z) + if(connected in testZAScolors_zones) + continue + recurse_zone(connected,1) + + for(var/turf/T in range(25,location)) + if(!istype(T)) + continue + if(T in testZAScolors_turfs) + continue + images += image(red, T, "zasdebug", TURF_LAYER) + testZAScolors_turfs += T + +/client/proc/testZAScolors_remove() + set category = "ZAS" + set name = "Remove ZAS connection colors" + + testZAScolors_turfs.Cut() + testZAScolors_zones.Cut() + + if(images.len) + for(var/image/i in images) + if(i.icon_state == "zasdebug") + images.Remove(i) + +/client/proc/rebootAirMaster() + set category = "ZAS" + set name = "Reboot ZAS" + + if(tgui_alert(usr, "This will destroy and remake all zone geometry on the whole map.","Reboot ZAS",list("Reboot ZAS","Nevermind")) == "Reboot ZAS") + SSair.RebootZAS() + +/client/proc/count_objects_on_z_level() + set category = "Mapping" + set name = "Count Objects On Level" + var/level = tgui_input_text(usr, "Which z-level?","Level?") + if(!level) return + var/num_level = text2num(level) + if(!num_level) return + if(!isnum(num_level)) return + + var/type_text = tgui_input_text(usr, "Which type path?","Path?") + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 1 + + var/list/atom/atom_list = list() + + for(var/atom/A in world) + if(istype(A,type_path)) + var/atom/B = A + while(!(isturf(B.loc))) + if(B && B.loc) + B = B.loc + else + break + if(B) + if(B.z == num_level) + count++ + atom_list += A + /* + var/atom/temp_atom + for(var/i = 0; i <= (atom_list.len/10); i++) + var/line = "" + for(var/j = 1; j <= 10; j++) + if(i*10+j <= atom_list.len) + temp_atom = atom_list[i*10+j] + line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " + to_world(line)*/ + + to_world("There are [count] objects of type [type_path] on z-level [num_level]") + feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_objects_all() + set category = "Mapping" + set name = "Count Objects All" + + var/type_text = tgui_input_text(usr, "Which type path?","") + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + for(var/atom/A in world) + if(istype(A,type_path)) + count++ + /* + var/atom/temp_atom + for(var/i = 0; i <= (atom_list.len/10); i++) + var/line = "" + for(var/j = 1; j <= 10; j++) + if(i*10+j <= atom_list.len) + temp_atom = atom_list[i*10+j] + line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " + to_world(line)*/ + + to_world("There are [count] objects of type [type_path] in the game world") + feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +var/global/prevent_airgroup_regroup = 0 + +/client/proc/break_all_air_groups() + set category = "Mapping" + set name = "Break All Airgroups" + + /*prevent_airgroup_regroup = 1 + for(var/datum/air_group/AG in air_master.air_groups) + AG.suspend_group_processing() + message_admins("[src.ckey] used 'Break All Airgroups'")*/ + +/client/proc/regroup_all_air_groups() + set category = "Mapping" + set name = "Regroup All Airgroups Attempt" + + to_chat(usr, span_red("Proc disabled.")) //Why not.. Delete the procs instead? + + /*prevent_airgroup_regroup = 0 + for(var/datum/air_group/AG in air_master.air_groups) + AG.check_regroup() + message_admins("[src.ckey] used 'Regroup All Airgroups Attempt'")*/ + +/client/proc/kill_pipe_processing() + set category = "Mapping" + set name = "Kill pipe processing" + + to_chat(usr, span_red("Proc disabled.")) + + /*pipe_processing_killed = !pipe_processing_killed + if(pipe_processing_killed) + message_admins("[src.ckey] used 'kill pipe processing', stopping all pipe processing.") + else + message_admins("[src.ckey] used 'kill pipe processing', restoring all pipe processing.")*/ + +/client/proc/kill_air_processing() + set category = "Mapping" + set name = "Kill air processing" + + to_chat(usr, span_red("Proc disabled.")) + + /*air_processing_killed = !air_processing_killed + if(air_processing_killed) + message_admins("[src.ckey] used 'kill air processing', stopping all air processing.") + else + message_admins("[src.ckey] used 'kill air processing', restoring all air processing.")*/ + +//This proc is intended to detect lag problems relating to communication procs +var/global/say_disabled = 0 +/client/proc/disable_communication() + set category = "Mapping" + set name = "Disable all communication verbs" + + to_chat(usr, span_red("Proc disabled.")) + + /*say_disabled = !say_disabled + if(say_disabled) + message_admins("[src.ckey] used 'Disable all communication verbs', killing all communication methods.") + else + message_admins("[src.ckey] used 'Disable all communication verbs', restoring all communication methods.")*/ + +//This proc is intended to detect lag problems relating to movement +var/global/movement_disabled = 0 +var/global/movement_disabled_exception //This is the client that calls the proc, so he can continue to run around to gauge any change to lag. +/client/proc/disable_movement() + set category = "Mapping" + set name = "Disable all movement" + + to_chat(usr, span_red("Proc disabled.")) + + /*movement_disabled = !movement_disabled + if(movement_disabled) + message_admins("[src.ckey] used 'Disable all movement', killing all movement.") + movement_disabled_exception = usr.ckey + else + message_admins("[src.ckey] used 'Disable all movement', restoring all movement.")*/ diff --git a/code/modules/admin/verbs/modify_robot.dm b/code/modules/admin/verbs/modify_robot.dm new file mode 100644 index 00000000000..e90e06bd27d --- /dev/null +++ b/code/modules/admin/verbs/modify_robot.dm @@ -0,0 +1,315 @@ +//Allows to add and remove modules from borgs +/client/proc/modify_robot(var/mob/living/silicon/robot/target in silicon_mob_list) + set name = "Modify Robot Module" + set desc = "Allows to add or remove modules to/from robots." + set category = "Admin" + if(!check_rights(R_ADMIN|R_FUN|R_VAREDIT|R_EVENT)) + return + + if(!istype(target) || !target.module) + return + + if(!target.module.modules) + return + + var/list/modification_options = list(MODIFIY_ROBOT_MODULE_ADD,MODIFIY_ROBOT_MODULE_REMOVE, MODIFIY_ROBOT_APPLY_UPGRADE, MODIFIY_ROBOT_SUPP_ADD, MODIFIY_ROBOT_SUPP_REMOVE, MODIFIY_ROBOT_RADIOC_ADD, MODIFIY_ROBOT_RADIOC_REMOVE, + MODIFIY_ROBOT_COMP_ADD, MODIFIY_ROBOT_COMP_REMOVE, MODIFIY_ROBOT_SWAP_MODULE, MODIFIY_ROBOT_RESET_MODULE, MODIFIY_ROBOT_TOGGLE_ERT, MODIFIY_ROBOT_TOGGLE_CENT_ACCESS) + + while(TRUE) + var/modification_choice = tgui_input_list(usr, "Select if you want to add or remove a module to/from [target]","Choice", modification_options) + if(!modification_choice || modification_choice == "Cancel") + return + + if(!target.module || !target.module.modules) + to_chat(usr, "[target] was recently reset, you must wait until module selection has been completed before continuing modifying.") + continue + + log_and_message_admins("[key_name(src)] has used MODIFYROBOT ([modification_choice]) on [key_name(target)].") + feedback_add_details("admin_verb","MODIFYROBOT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + switch(modification_choice) + if(MODIFIY_ROBOT_MODULE_ADD) + while(TRUE) + var/selected_module_module = tgui_input_list(usr, "Please select the module to pick modules from", "Module", robot_modules) + if(!selected_module_module || selected_module_module == "Cancel") + break + var/module_type = robot_modules[selected_module_module] + var/mob/living/silicon/robot/robot = new /mob/living/silicon/robot(null) + var/obj/item/weapon/robot_module/robot/robot_type = new module_type(robot) + robot.emag_items = 1 + if(!istype(robot_type, /obj/item/weapon/robot_module/robot/)) + robot.Destroy() + break + var/list/all_modules = robot.module.modules + all_modules += robot.module.emag + while(TRUE) + var/add_item = tgui_input_list(usr, "Please select the module to add", "Modules", all_modules) + if(!istype(add_item, /obj/item/)) + break + robot.module.emag.Remove(add_item) + robot.module.modules.Remove(add_item) + robot.module.contents.Remove(add_item) + target.module.modules.Add(add_item) + target.module.contents.Add(add_item) + target.hud_used.update_robot_modules_display() + to_chat(usr, "You added \"[add_item]\" to [target].") + if(istype(add_item, /obj/item/stack/)) + var/obj/item/stack/item_with_synth = add_item + for(var/synth in item_with_synth.synths) + var/found = target.module.synths.Find(synth) + if(!found) + robot.module.synths.Remove(synth) + target.module.synths.Add(synth) + else + item_with_synth.synths = list(target.module.synths[found]) + continue + if(istype(add_item, /obj/item/weapon/matter_decompiler/) || istype(add_item, /obj/item/device/dogborg/sleeper/compactor/decompiler/)) + var/obj/item/weapon/matter_decompiler/item_with_matter = add_item + if(item_with_matter.metal) + var/found = target.module.synths.Find(item_with_matter.metal) + if(!found) + robot.module.synths.Remove(item_with_matter.metal) + target.module.synths.Add(item_with_matter.metal) + else + item_with_matter.metal = target.module.synths[found] + if(item_with_matter.glass) + var/found = target.module.synths.Find(item_with_matter.glass) + if(!found) + robot.module.synths.Remove(item_with_matter.glass) + target.module.synths.Add(item_with_matter.glass) + else + item_with_matter.glass = target.module.synths[found] + if(item_with_matter.wood) + var/found = target.module.synths.Find(item_with_matter.wood) + if(!found) + robot.module.synths.Remove(item_with_matter.wood) + target.module.synths.Add(item_with_matter.wood) + else + item_with_matter.wood = target.module.synths[found] + if(item_with_matter.plastic) + var/found = target.module.synths.Find(item_with_matter.plastic) + if(!found) + robot.module.synths.Remove(item_with_matter.plastic) + target.module.synths.Add(item_with_matter.plastic) + else + item_with_matter.plastic = target.module.synths[found] + robot.Destroy() + if(MODIFIY_ROBOT_MODULE_REMOVE) + while(TRUE) + var/list/active_modules = target.module.modules + var/selected_module_module = tgui_input_list(usr, "Please select the module to remove", "Modules", active_modules) + if(!istype(selected_module_module, /obj/item/)) + break + to_chat(usr, "You removed \"[selected_module_module]\" from [target]") + target.uneq_all() + target.hud_used.update_robot_modules_display(TRUE) + target.module.emag.Remove(selected_module_module) + target.module.modules.Remove(selected_module_module) + target.module.contents.Remove(selected_module_module) + qdel(selected_module_module) + if(MODIFIY_ROBOT_APPLY_UPGRADE) + var/list/upgrades = list() + for(var/datum/design/item/prosfab/robot_upgrade/upgrade) + if(!(target.has_upgrade(initial(upgrade.build_path)))) + upgrades[initial(upgrade.name)] = initial(upgrade.build_path) + while(TRUE) + var/selected_module_upgrade = tgui_input_list(usr, "Please select the module to remove", "Upgrades", upgrades) + if(!selected_module_upgrade || selected_module_upgrade == "Cancel") + break + if(selected_module_upgrade == "Reset Module") + if(tgui_alert(usr, "Are you sure that you want to install [selected_module_upgrade] and reset the robot's module?","Confirm",list("Yes","No"))=="No") + continue + var/new_upgrade = upgrades[capitalize(selected_module_upgrade)] + upgrades.Remove(selected_module_upgrade) + var/obj/item/borg/upgrade/U = new new_upgrade(src) + if(selected_module_upgrade == "Rename Module") + var/obj/item/borg/upgrade/utility/rename/UN = U + var/new_name = sanitizeSafe(tgui_input_text(usr, "Enter new robot name", "Robot Reclassification", UN.heldname, MAX_NAME_LEN), MAX_NAME_LEN) + if(new_name) + UN.heldname = new_name + U = UN + if(istype(U, /obj/item/borg/upgrade/restricted)) + target.module.supported_upgrades |= new_upgrade + if(U.action(target)) + to_chat(usr, "You apply the [U] to [target]!") + usr.drop_item() + U.loc = target + target.hud_used.update_robot_modules_display() + else + to_chat(usr, "Upgrade error!") + if(selected_module_upgrade == "Proto-Kinetic Accelerator") + var/list/modkits = list() + for(var/modkit in typesof(/obj/item/borg/upgrade/modkit)) + var/obj/item/borg/upgrade/modkit/single_modkit = modkit + modkits["[initial(single_modkit.name)] [initial(single_modkit.cost)]"] = modkit + modkits.Remove("kinetic accelerator modification kit 30") + var/capacity = 100 + while(TRUE) + var/selected_ka_upgrade = tgui_input_list(usr, "Do you want to install upgrades? Remaining capacity: [capacity]", "KA Upgrades", modkits) + if(!selected_ka_upgrade || selected_ka_upgrade == "Cancel") + break + var/new_modkit = modkits[selected_ka_upgrade] + var/obj/item/borg/upgrade/modkit/M = new new_modkit(src) + var/obj/item/weapon/gun/energy/kinetic_accelerator/kin = locate() in target.module.modules + if(kin.get_remaining_mod_capacity() >= M.cost) + to_chat(usr, "You installed the [M] into the [kin], [capacity]% remaining!") + modkits.Remove(selected_ka_upgrade) + M.install(kin, target) + capacity = kin.get_remaining_mod_capacity() + if(MODIFIY_ROBOT_SUPP_ADD) + var/list/whitelisted_upgrades = list() + for(var/datum/design/item/prosfab/robot_upgrade/restricted/upgrade) + if(!(initial(upgrade.build_path) in target.module.supported_upgrades)) + whitelisted_upgrades[initial(upgrade.name)] = initial(upgrade.build_path) + while(TRUE) + var/selected_upgrade_type = tgui_input_list(usr, "Please select which upgrade you want this module to support", "Upgrades", whitelisted_upgrades) + if(!selected_upgrade_type || selected_upgrade_type == "Cancel") + break + var/upgrade_path = whitelisted_upgrades[capitalize(selected_upgrade_type)] + whitelisted_upgrades.Remove(selected_upgrade_type) + target.module.supported_upgrades |= upgrade_path + if(MODIFIY_ROBOT_SUPP_REMOVE) + var/list/whitelisted_upgrades = list() + for(var/datum/design/item/prosfab/robot_upgrade/restricted/upgrade) + if((initial(upgrade.build_path) in target.module.supported_upgrades)) + whitelisted_upgrades[initial(upgrade.name)] = initial(upgrade.build_path) + while(TRUE) + var/selected_upgrade_type = tgui_input_list(usr, "Please select which upgrade you want this module to support", "Upgrades", whitelisted_upgrades) + if(!selected_upgrade_type || selected_upgrade_type == "Cancel") + break + var/upgrade_path = whitelisted_upgrades[capitalize(selected_upgrade_type)] + whitelisted_upgrades.Remove(selected_upgrade_type) + target.module.supported_upgrades -= upgrade_path + if(MODIFIY_ROBOT_RADIOC_ADD) + var/list/available_channels = radiochannels.Copy() + for(var/has_channel in target.radio.channels) + available_channels -= has_channel + while(TRUE) + var/selected_radio_channel = tgui_input_list(usr, "Please select the radio channel to add", "Channels", available_channels) + if(!selected_radio_channel || selected_radio_channel == "Cancel") + break + if(selected_radio_channel == "Special Ops") + target.radio.centComm = 1 + if(selected_radio_channel == "Raider") + qdel(target.radio.keyslot) + target.radio.keyslot = new /obj/item/device/encryptionkey/raider(target) + target.radio.syndie = 1 + if(selected_radio_channel == "Mercenary") + qdel(target.radio.keyslot) + target.radio.keyslot = new /obj/item/device/encryptionkey/syndicate(target) + target.radio.syndie = 1 + target.module.channels += list("[selected_radio_channel]" = 1) + target.radio.channels[selected_radio_channel] += target.module.channels[selected_radio_channel] + target.radio.secure_radio_connections[selected_radio_channel] += radio_controller.add_object(target.radio, radiochannels[selected_radio_channel], RADIO_CHAT) + available_channels -= selected_radio_channel + to_chat(usr, "You added \"[selected_radio_channel]\" channel to [target].") + if(MODIFIY_ROBOT_RADIOC_REMOVE) + while(TRUE) + var/selected_radio_channel = tgui_input_list(usr, "Please select the radio channel to remove", "Channels", target.radio.channels) + if(!selected_radio_channel || selected_radio_channel == "Cancel") + break + if(selected_radio_channel == "Special Ops") + target.radio.centComm = 0 + target.module.channels -= selected_radio_channel + if((selected_radio_channel == "Mercenary" || selected_radio_channel == "Raider") && !(target.module.channels["Raider"] || target.module.channels["Mercenary"])) + qdel(target.radio.keyslot) + target.radio.keyslot = null + target.radio.syndie = 0 + target.radio.channels = list() + for(var/n_chan in target.module.channels) + target.radio.channels[n_chan] -= target.module.channels[n_chan] + radio_controller.remove_object(target.radio, radiochannels[selected_radio_channel]) + target.radio.secure_radio_connections -= selected_radio_channel + to_chat(usr, "You removed \"[selected_radio_channel]\" channel from [target].") + if(MODIFIY_ROBOT_COMP_ADD) + while(TRUE) + var/selected_component = tgui_input_list(usr, "Please select the component to add or replace", "Component", target.components) + if(!selected_component || selected_component == "Cancel") + break + var/datum/robot_component/C = target.components[selected_component] + if(C.wrapped && selected_component != "power cell") + qdel(C.wrapped) + switch(selected_component) + if("actuator") + C.wrapped = new /obj/item/robot_parts/robot_component/actuator(target) + if("radio") + C.wrapped = new /obj/item/robot_parts/robot_component/radio(target) + if("power cell") + var/list/recommended_cells = list(/obj/item/weapon/cell/robot_station, /obj/item/weapon/cell/high, /obj/item/weapon/cell/super, /obj/item/weapon/cell/robot_syndi, /obj/item/weapon/cell/hyper, + /obj/item/weapon/cell/infinite, /obj/item/weapon/cell/potato, /obj/item/weapon/cell/slime) + var/list/cell_names = list() + for(var/cell_type in recommended_cells) + var/obj/item/weapon/cell/single_cell = cell_type + cell_names[capitalize(initial(single_cell.name))] = cell_type + var/selected_cell = tgui_input_list(usr, "What kind of cell do you want to install?", "Cells", cell_names) + if(!selected_cell || selected_cell == "Cancel") + continue + qdel(C.wrapped) + var/new_power_cell = cell_names[capitalize(selected_cell)] + target.cell = new new_power_cell(target) + C.wrapped = target.cell + to_chat(usr, "You replaced \"[C]\" on [target] with \"[selected_cell]\".") + if("diagnosis unit") + C.wrapped = new /obj/item/robot_parts/robot_component/diagnosis_unit(target) + if("camera") + C.wrapped = new /obj/item/robot_parts/robot_component/camera(target) + if("comms") + C.wrapped = new /obj/item/robot_parts/robot_component/binary_communication_device(target) + if("armour") + C.wrapped = new /obj/item/robot_parts/robot_component/armour(target) + C.install() + C.installed = 1 + if(selected_component != "power cell") + to_chat(usr, "You repplaced \"[C]\" on [target].") + if(MODIFIY_ROBOT_COMP_REMOVE) + while(TRUE) + var/selected_component = tgui_input_list(usr, "Please select the component to remove", "Component", target.components) + if(!selected_component || selected_component == "Cancel") + break + var/datum/robot_component/C = target.components[selected_component] + if(C.wrapped) + C.uninstall() + C.installed = 0 + qdel(C.wrapped) + C.wrapped = null + if(selected_component == "power cell") + target.cell = null + to_chat(usr, "You removed \"[C]\" from [target]") + if(MODIFIY_ROBOT_SWAP_MODULE) + var/selected_module = tgui_input_list(usr, "Which Module would you like to use?", "Module", robot_modules) + if(!selected_module || selected_module == "Cancel") + continue + if(!(selected_module in robot_modules)) + continue + target.uneq_all() + target.hud_used.update_robot_modules_display(TRUE) + target.modtype = initial(target.modtype) + target.module.Reset(target) + target.module.Destroy() + target.modtype = selected_module + var/module_type = robot_modules[selected_module] + target.transform_with_anim() + new module_type(target) + target.hands.icon_state = target.get_hud_module_icon() + target.hud_used.update_robot_modules_display() + if(MODIFIY_ROBOT_RESET_MODULE) + if(tgui_alert(usr, "Are you sure that you want to reset the entire module?","Confirm",list("Yes","No"))=="No") + continue + target.module_reset(FALSE) + to_chat(usr, "You resetted [target]'s module selection.") + if(MODIFIY_ROBOT_TOGGLE_ERT) + target.crisis_override = !target.crisis_override + to_chat(usr, "You [target.crisis_override? "enabled":"disabled"] [target]'s combat module overwrite.") + if(tgui_alert(usr, "Do you want to reset the module as well to allow selection?","Confirm",list("Yes","No"))=="No") + continue + target.module_reset(FALSE) + if(MODIFIY_ROBOT_TOGGLE_CENT_ACCESS) + for(var/obj in target.contents) + if(istype(obj, /obj/item/weapon/card/id/synthetic)) + var/obj/item/weapon/card/id/synthetic/card = obj + if(access_cent_specops in card.access) + card.access -= get_all_centcom_access() + to_chat(usr, "You revoke central access from [target].") + else + card.access += get_all_centcom_access() + to_chat(usr, "You grant central access to [target].") diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 0abf2ef8d74..c57ad0b706c 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -1,119 +1,311 @@ -var/list/sounds_cache = list() - -/client/proc/play_sound(S as sound) - set category = "Fun" - set name = "Play Global Sound" - if(!check_rights(R_SOUNDS)) return - - var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = 777) - uploaded_sound.priority = 250 - - sounds_cache += S - - if(tgui_alert(usr, "Do you ready?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request", list("Play","Cancel")) == "Cancel") - return - - log_admin("[key_name(src)] played sound [S]") - message_admins("[key_name_admin(src)] played sound [S]", 1) - for(var/mob/M in player_list) - if(M.is_preference_enabled(/datum/client_preference/play_admin_midis)) - M << uploaded_sound - - feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/play_local_sound(S as sound) - set category = "Fun" - set name = "Play Local Sound" - if(!check_rights(R_SOUNDS)) return - - log_admin("[key_name(src)] played a local sound [S]") - message_admins("[key_name_admin(src)] played a local sound [S]", 1) - playsound(src.mob, S, 50, 0, 0) - feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/play_z_sound(S as sound) - set category = "Fun" - set name = "Play Z Sound" - if(!check_rights(R_SOUNDS)) return - var/target_z = mob.z - var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = 777) - uploaded_sound.priority = 250 - - sounds_cache += S - - if(tgui_alert(usr, "Do you ready?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request", list("Play","Cancel")) == "Cancel") - return - - log_admin("[key_name(src)] played sound [S] on Z[target_z]") - message_admins("[key_name_admin(src)] played sound [S] on Z[target_z]", 1) - for(var/mob/M in player_list) - if(M.is_preference_enabled(/datum/client_preference/play_admin_midis) && M.z == target_z) - M << uploaded_sound - - feedback_add_details("admin_verb","PZS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/play_server_sound() - set category = "Fun" - set name = "Play Server Sound" - if(!check_rights(R_SOUNDS)) return - - var/list/sounds = file2list("sound/serversound_list.txt"); - sounds += "--CANCEL--" - sounds += sounds_cache - - var/melody = tgui_input_list(usr, "Select a sound from the server to play", "Server sound list", sounds, "--CANCEL--") - - if(melody == "--CANCEL--") return - - play_sound(melody) - feedback_add_details("admin_verb","PSS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/* -/client/proc/cuban_pete() - set category = "Fun" - set name = "Cuban Pete Time" - - message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'cubanpetetime.ogg' - - for(var/mob/living/carbon/human/CP in human_mob_list) - if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") - to_chat(CP, "Your body can't contain the rhumba beat") - CP.gib() - - -/client/proc/bananaphone() - set category = "Fun" - set name = "Banana Phone" - - message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'bananaphone.ogg' - - -/client/proc/space_asshole() - set category = "Fun" - set name = "Space Asshole" - - message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'sound/music/space_asshole.ogg' - - -/client/proc/honk_theme() - set category = "Fun" - set name = "Honk" - - message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) - for(var/mob/M in player_list) - if(M.client) - if(M.client.midis) - M << 'honk_theme.ogg'*/ +//world/proc/shelleo +#define SHELLEO_ERRORLEVEL 1 +#define SHELLEO_STDOUT 2 +#define SHELLEO_STDERR 3 + +var/list/sounds_cache = list() + +/client/proc/play_sound(S as sound) + set category = "Fun" + set name = "Play Global Sound" + if(!check_rights(R_SOUNDS)) + return + + var/freq = 1 + var/vol = tgui_input_number(usr, "What volume would you like the sound to play at?",, 100, 100, 1) + if(!vol) + return + vol = clamp(vol, 1, 100) + + var/sound/admin_sound = new() + admin_sound.file = S + admin_sound.priority = 250 + admin_sound.channel = 777 + admin_sound.frequency = freq + admin_sound.wait = 1 + admin_sound.repeat = FALSE + admin_sound.status = SOUND_STREAM + admin_sound.volume = vol + + sounds_cache += S + + var/res = tgui_alert(usr, "Show the title of this song ([S]) to the players?\nOptions 'Yes' and 'No' will play the sound.",, list("Yes", "No", "Cancel")) + switch(res) + if("Yes") + to_chat(world, "An admin played: [S]", confidential = TRUE) + if("Cancel") + return + + log_admin("[key_name(src)] played sound [S]") + message_admins("[key_name_admin(src)] played sound [S]", 1) + + for(var/mob/M in player_list) + if(M.is_preference_enabled(/datum/client_preference/play_admin_midis)) + admin_sound.volume = vol * M.client.admin_music_volume + SEND_SOUND(M, admin_sound) + admin_sound.volume = vol + + feedback_add_details("admin_verb", "Play Global Sound") + +/client/proc/play_local_sound(S as sound) + set category = "Fun" + set name = "Play Local Sound" + if(!check_rights(R_SOUNDS)) + return + + log_admin("[key_name(src)] played a local sound [S]") + message_admins("[key_name_admin(src)] played a local sound [S]", 1) + playsound(src.mob, S, 50, 0, 0) + feedback_add_details("admin_verb", "Play Local Sound") + +/client/proc/play_direct_mob_sound(S as sound, mob/M) + set category = "Fun" + set name = "Play Direct Mob Sound" + if(!check_rights(R_SOUNDS)) + return + + if(!M) + M = tgui_input_list(usr, "Choose a mob to play the sound to. Only they will hear it.", "Play Mob Sound", sortNames(player_list)) + if(!M || QDELETED(M)) + return + log_admin("[key_name(src)] played a direct mob sound [S] to [M].") + message_admins("[key_name_admin(src)] played a direct mob sound [S] to [ADMIN_LOOKUPFLW(M)].") + SEND_SOUND(M, S) + feedback_add_details("admin_verb", "Play Direct Mob Sound") + +/client/proc/play_z_sound(S as sound) + set category = "Fun" + set name = "Play Z Sound" + if(!check_rights(R_SOUNDS)) return + var/target_z = mob.z + var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = 777) + uploaded_sound.priority = 250 + + sounds_cache += S + + if(tgui_alert(usr, "Do you ready?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request", list("Play","Cancel")) == "Cancel") + return + + log_admin("[key_name(src)] played sound [S] on Z[target_z]") + message_admins("[key_name_admin(src)] played sound [S] on Z[target_z]", 1) + for(var/mob/M in player_list) + if(M.is_preference_enabled(/datum/client_preference/play_admin_midis) && M.z == target_z) + M << uploaded_sound + + feedback_add_details("admin_verb", "Play Z Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/play_server_sound() + set category = "Fun" + set name = "Play Server Sound" + if(!check_rights(R_SOUNDS)) + return + + var/list/sounds = file2list("sound/serversound_list.txt"); + sounds += "--CANCEL--" + sounds += sounds_cache + + var/melody = tgui_input_list(usr, "Select a sound from the server to play", "Server sound list", sounds, "--CANCEL--") + + if(melody == "--CANCEL--") + return + + play_sound(melody) + feedback_add_details("admin_verb", "Play Server Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +///Takes an input from either proc/play_web_sound or the request manager and runs it through youtube-dl and prompts the user before playing it to the server. +/proc/web_sound(mob/user, input, credit) + if(!check_rights(R_SOUNDS)) + return + var/ytdl = config.invoke_youtubedl + if(!ytdl) + to_chat(user, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value + return + var/web_sound_url = "" + var/stop_web_sounds = FALSE + var/list/music_extra_data = list() + var/duration = 0 + if(istext(input)) + var/shell_scrubbed_input = shell_url_scrub(input) + var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height <= 360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_scrubbed_input]\"") + var/errorlevel = output[SHELLEO_ERRORLEVEL] + var/stdout = output[SHELLEO_STDOUT] + var/stderr = output[SHELLEO_STDERR] + if(errorlevel) + to_chat(user, "Youtube-dl URL retrieval FAILED:", confidential = TRUE) + to_chat(user, "[stderr]", confidential = TRUE) + return + var/list/data + try + data = json_decode(stdout) + catch(var/exception/e) + to_chat(user, "Youtube-dl JSON parsing FAILED:", confidential = TRUE) + to_chat(user, "[e]: [stdout]", confidential = TRUE) + return + if (data["url"]) + web_sound_url = data["url"] + var/title = "[data["title"]]" + var/webpage_url = title + if (data["webpage_url"]) + webpage_url = "[title]" + music_extra_data["duration"] = DisplayTimeText(data["duration"] * 1 SECONDS) + music_extra_data["link"] = data["webpage_url"] + music_extra_data["artist"] = data["artist"] + music_extra_data["upload_date"] = data["upload_date"] + music_extra_data["album"] = data["album"] + duration = data["duration"] * 1 SECONDS + if (duration > 10 MINUTES) + if((tgui_alert(user, "This song is over 10 minutes long. Are you sure you want to play it?", "Length Warning!", list("No", "Yes", "Cancel")) != "Yes")) + return + var/res = tgui_alert(user, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Cancel")) + switch(res) + if("Yes") + music_extra_data["title"] = data["title"] + if("No") + music_extra_data["link"] = "Song Link Hidden" + music_extra_data["title"] = "Song Title Hidden" + music_extra_data["artist"] = "Song Artist Hidden" + music_extra_data["upload_date"] = "Song Upload Date Hidden" + music_extra_data["album"] = "Song Album Hidden" + if("Cancel", null) + return + var/anon = tgui_alert(user, "Display who played the song?", "Credit Yourself?", list("Yes", "No", "Cancel")) + switch(anon) + if("Yes") + if(res == "Yes") + to_chat(world, "[user.key] played: [webpage_url]", confidential = TRUE) + else + to_chat(world, "[user.key] played a sound", confidential = TRUE) + if("No") + if(res == "Yes") + to_chat(world, "An admin played: [webpage_url]", confidential = TRUE) + if("Cancel", null) + return + if(credit) + to_chat(world, "[credit]", confidential = TRUE) + //SSblackbox.record_feedback("nested tally", "played_url", 1, list("[user.ckey]", "[input]")) + log_admin("[key_name(user)] played web sound: [input]") + message_admins("[key_name(user)] played web sound: [input]") + else + //pressed ok with blank + log_admin("[key_name(user)] stopped web sounds.") + + message_admins("[key_name(user)] stopped web sounds.") + web_sound_url = null + stop_web_sounds = TRUE + if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol)) + tgui_alert(user, "The media provider returned a content URL that isn't using the HTTP or HTTPS protocol. This is a security risk and the sound will not be played.", "Security Risk", list("OK")) + to_chat(user, "BLOCKED: Content URL not using HTTP(S) Protocol!", confidential = TRUE) + + return + if(web_sound_url || stop_web_sounds) + for(var/m in player_list) + var/mob/M = m + var/client/C = M.client + if(C.is_preference_enabled(/datum/client_preference/play_admin_midis)) + if(!stop_web_sounds) + C.tgui_panel?.play_music(web_sound_url, music_extra_data) + else + C.tgui_panel?.stop_music() + + S_TIMER_COOLDOWN_START(SStimer, COOLDOWN_INTERNET_SOUND, duration) + + feedback_add_details("admin_verb", "Play Internet Sound") + +/client/proc/play_web_sound() + set category = "Fun" + set name = "Play Internet Sound" + if(!check_rights(R_SOUNDS)) + return + + var/ytdl = config.invoke_youtubedl + if(!ytdl) + to_chat(src, "Youtube-dl was not configured, action unavailable", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value + return + + if(S_TIMER_COOLDOWN_TIMELEFT(SStimer, COOLDOWN_INTERNET_SOUND)) + if(tgui_alert(usr, "Someone else is already playing an Internet sound! It has [DisplayTimeText(S_TIMER_COOLDOWN_TIMELEFT(SStimer, COOLDOWN_INTERNET_SOUND), 1)] remaining. \ + Would you like to override?", "Musicalis Interruptus", list("No","Yes")) != "Yes") + return + + var/web_sound_input = tgui_input_text(usr, "Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound", null) + + if(length(web_sound_input)) + web_sound_input = trim(web_sound_input) + if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol)) + to_chat(src, "Non-http(s) URIs are not allowed.", confidential = TRUE) + to_chat(src, "For youtube-dl shortcuts like ytsearch: please use the appropriate full URL from the website.", confidential = TRUE) + return + web_sound(usr, web_sound_input) + else + web_sound(usr, null) + +/client/proc/stop_sounds() + set category = "Debug" + set name = "Stop All Playing Sounds" + if(!src.holder) + return + + log_admin("[key_name(src)] stopped all currently playing sounds.") + message_admins("[key_name_admin(src)] stopped all currently playing sounds.") + for(var/mob/M in player_list) + SEND_SOUND(M, sound(null)) + var/client/C = M.client + C?.tgui_panel?.stop_music() + + S_TIMER_COOLDOWN_RESET(SStimer, COOLDOWN_INTERNET_SOUND) + feedback_add_details("admin_verb", "Stop All Playing Sounds") + +//world/proc/shelleo +#undef SHELLEO_ERRORLEVEL +#undef SHELLEO_STDOUT +#undef SHELLEO_STDERR + +/* +/client/proc/cuban_pete() + set category = "Fun" + set name = "Cuban Pete Time" + + message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'cubanpetetime.ogg' + + for(var/mob/living/carbon/human/CP in human_mob_list) + if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") + to_chat(CP, "Your body can't contain the rhumba beat") + CP.gib() + + +/client/proc/bananaphone() + set category = "Fun" + set name = "Banana Phone" + + message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'bananaphone.ogg' + + +/client/proc/space_asshole() + set category = "Fun" + set name = "Space Asshole" + + message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'sound/music/space_asshole.ogg' + + +/client/proc/honk_theme() + set category = "Fun" + set name = "Honk" + + message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) + for(var/mob/M in player_list) + if(M.client) + if(M.client.midis) + M << 'honk_theme.ogg'*/ diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index b2be689721d..7725ad92728 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -1,44 +1,44 @@ -/mob/verb/pray() - set category = "IC" - set name = "Pray" - - if(say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") - return - - var/raw_msg = sanitize(tgui_input_text(usr, "Prayers are sent to staff but do not open tickets or go to Discord. If you have a technical difficulty or an event/spice idea/hook - please ahelp instead. Thank you!", "Pray", null, MAX_MESSAGE_LEN)) - if(!raw_msg) return - - if(usr.client) - if(raw_msg) - client.handle_spam_prevention(MUTE_PRAY) - if(usr.client.prefs.muted & MUTE_PRAY) - to_chat(usr, " You cannot pray (muted).") - return - - var/icon/cross = icon('icons/obj/storage.dmi',"bible") - var/msg = "\icon[cross][bicon(cross)] PRAY: [key_name(src, 1)] [ADMIN_QUE(src)] [ADMIN_PP(src)] [ADMIN_VV(src)] [ADMIN_SM(src)] ([admin_jump_link(src, src)]) [ADMIN_CA(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]: [raw_msg]" - - for(var/client/C in GLOB.admins) - if(R_ADMIN|R_EVENT & C.holder.rights) - if(C.is_preference_enabled(/datum/client_preference/admin/show_chat_prayers)) - to_chat(C,msg) - C << 'sound/effects/ding.ogg' - to_chat(usr, "Your prayers have been received by the gods.") - - feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_pray(raw_msg, src) - -/proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage) - msg = "[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]: [msg]" - for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins - if(R_ADMIN|R_EVENT & C.holder.rights) - to_chat(C,msg) - C << 'sound/machines/signal.ogg' - -/proc/Syndicate_announce(var/msg, var/mob/Sender) - msg = "ILLEGAL:[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]" - for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins - if(R_ADMIN|R_EVENT & C.holder.rights) - to_chat(C,msg) - C << 'sound/machines/signal.ogg' +/mob/verb/pray() + set category = "IC" + set name = "Pray" + + if(say_disabled) //This is here to try to identify lag problems + to_chat(usr, span_red("Speech is currently admin-disabled.")) + return + + var/raw_msg = sanitize(tgui_input_text(usr, "Prayers are sent to staff but do not open tickets or go to Discord. If you have a technical difficulty or an event/spice idea/hook - please ahelp instead. Thank you!", "Pray", null, MAX_MESSAGE_LEN)) + if(!raw_msg) return + + if(usr.client) + if(raw_msg) + client.handle_spam_prevention(MUTE_PRAY) + if(usr.client.prefs.muted & MUTE_PRAY) + to_chat(usr, span_red("You cannot pray (muted).")) + return + + var/icon/cross = icon('icons/obj/storage.dmi',"bible") + var/msg = "" + span_blue("\icon[cross][bicon(cross)] " + span_purple("PRAY: ") + "[key_name(src, 1)] [ADMIN_QUE(src)] [ADMIN_PP(src)] [ADMIN_VV(src)] [ADMIN_SM(src)] ([admin_jump_link(src, src)]) [ADMIN_CA(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]: [raw_msg]") + "" + + for(var/client/C in GLOB.admins) + if(R_ADMIN|R_EVENT & C.holder.rights) + if(C.is_preference_enabled(/datum/client_preference/admin/show_chat_prayers)) + to_chat(C, msg, type = MESSAGE_TYPE_PRAYER, confidential = TRUE) + C << 'sound/effects/ding.ogg' + to_chat(usr, "Your prayers have been received by the gods.", confidential = TRUE) + + feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_pray(raw_msg, src) + +/proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage) + msg = span_blue("" + span_orange("[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]: [msg]") + for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins + if(R_ADMIN|R_EVENT & C.holder.rights) + to_chat(C,msg) + C << 'sound/machines/signal.ogg' + +/proc/Syndicate_announce(var/msg, var/mob/Sender) + msg = span_blue("" + span_crimson("ILLEGAL:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]") + for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins + if(R_ADMIN|R_EVENT & C.holder.rights) + to_chat(C,msg) + C << 'sound/machines/signal.ogg' diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 2719ad6dbf2..7d629b735a1 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1,1164 +1,1164 @@ -/client/proc/cmd_admin_drop_everything(mob/M as mob in mob_list) - set category = null - set name = "Drop Everything" - if(!holder) - return - - var/confirm = tgui_alert(src, "Make [M] drop everything?", "Message", list("Yes", "No")) - if(confirm != "Yes") - return - - for(var/obj/item/W in M) - if(istype(W, /obj/item/weapon/implant/backup) || istype(W, /obj/item/device/nif)) //VOREStation Edit - There's basically no reason to remove either of these - continue //VOREStation Edit - M.drop_from_inventory(W) - - log_admin("[key_name(usr)] made [key_name(M)] drop everything!") - message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) - feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_prison(mob/M as mob in mob_list) - set category = "Admin" - set name = "Prison" - if(!holder) - return - - if (ismob(M)) - if(istype(M, /mob/living/silicon/ai)) - tgui_alert_async(usr, "The AI can't be sent to prison you jerk!") - return - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/W in M) - M.drop_from_inventory(W) - //teleport person to cell - M.Paralyse(5) - sleep(5) //so they black out before warping - M.loc = pick(prisonwarp) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - spawn(50) - to_chat(M, "You have been sent to the prison station!") - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) - feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -//Allows staff to determine who the newer players are. -/client/proc/cmd_check_new_players() - set category = "Admin" - set name = "Check new Players" - if(!holder) - return - - var/age = tgui_alert(src, "Age check", "Show accounts yonger then _____ days", list("7","30","All")) - - if(age == "All") - age = 9999999 - else - age = text2num(age) - - var/missing_ages = 0 - var/msg = "" - - var/highlight_special_characters = 1 - - for(var/client/C in GLOB.clients) - if(C.player_age == "Requires database") - missing_ages = 1 - continue - if(C.player_age < age) - msg += "[key_name(C, 1, 1, highlight_special_characters)]: account is [C.player_age] days old
                    " - - if(missing_ages) - to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present.") - - if(msg != "") - src << browse(msg, "window=Player_age_check") - else - to_chat(src, "No matches for that age range found.") - -/client/proc/cmd_admin_subtle_message(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Subtle Message" - - if(!ismob(M)) return - if (!holder) - return - - var/msg = tgui_input_text(usr, "Message:", text("Subtle PM to [M.key]")) - - if (!msg) - return - - if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. - msg = sanitize(msg) - - if(usr) - if (usr.client) - if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") - - log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") - msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" - message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE - set category = "Special Verbs" - set name = "Global Narrate" - - if (!holder) - return - - var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to everyone:")) - - if (!msg) - return - if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. - msg = sanitize(msg) - if (!msg) // We check both before and after, just in case sanitization ended us up with empty message. - return - - to_world("[msg]") - log_admin("GlobalNarrate: [key_name(usr)] : [msg]") - message_admins(" GlobalNarrate: [key_name_admin(usr)] : [msg]
                    ", 1) - feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE - set category = "Special Verbs" - set name = "Direct Narrate" - - if(!holder) - return - - if(!M) - M = tgui_input_list(usr, "Direct narrate to who?", "Active Players", get_mob_with_client_list()) - - if(!M) - return - - var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to your target:")) - if(msg && !(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. - msg = sanitize(msg) - - if( !msg ) - return - - to_chat(M, msg) - log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") - msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
                    " - message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_godmode(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Godmode" - - if(!holder) - return - - M.status_flags ^= GODMODE - to_chat(usr, " Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") - - log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") - var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" - message_admins(msg) - admin_ticket_log(M, msg) - feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) - if(automute) - if(!config.automute_on) - return - else - if(!usr || !usr.client) - return - if(!usr.client.holder) - to_chat(usr, "Error: cmd_admin_mute: You don't have permission to do this.") - return - if(!M.client) - to_chat(usr, "Error: cmd_admin_mute: This mob doesn't have a client tied to it.") - if(M.client.holder) - to_chat(usr, "Error: cmd_admin_mute: You cannot mute an admin/mod.") - if(!M.client) - return - if(M.client.holder) - return - - var/muteunmute - var/mute_string - - switch(mute_type) - if(MUTE_IC) mute_string = "IC (say and emote)" - if(MUTE_OOC) mute_string = "OOC" - if(MUTE_PRAY) mute_string = "pray" - if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" - if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" - if(MUTE_ALL) mute_string = "everything" - else return - - if(automute) - muteunmute = "auto-muted" - M.client.prefs.muted |= mute_type - log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") - message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") - feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - - if(M.client.prefs.muted & mute_type) - muteunmute = "unmuted" - M.client.prefs.muted &= ~mute_type - else - muteunmute = "muted" - M.client.prefs.muted |= mute_type - - log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") - message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string].") - feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_add_random_ai_law() - set category = "Fun" - set name = "Add Random AI Law" - - if(!holder) - return - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm != "Yes") return - log_admin("[key_name(src)] has added a random AI law.") - message_admins("[key_name_admin(src)] has added a random AI law.", 1) - - var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) - if(show_log == "Yes") - command_announcement.Announce("Ion storm detected near \the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') - - IonStorm(0) - feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/* -Allow admins to set players to be able to respawn/bypass 30 min wait, without the admin having to edit variables directly -Ccomp's first proc. -*/ - -/client/proc/get_ghosts(var/notify = 0,var/what = 2) - // what = 1, return ghosts ass list. - // what = 2, return mob list - - var/list/mobs = list() - var/list/ghosts = list() - var/list/sortmob = sortAtom(mob_list) // get the mob list. - var/any=0 - for(var/mob/observer/dead/M in sortmob) - mobs.Add(M) //filter it where it's only ghosts - any = 1 //if no ghosts show up, any will just be 0 - if(!any) - if(notify) - to_chat(src, "There doesn't appear to be any ghosts for you to select.") - return - - for(var/mob/M in mobs) - var/name = M.name - ghosts[name] = M //get the name of the mob for the popup list - if(what==1) - return ghosts - else - return mobs - - -/client/proc/allow_character_respawn() - set category = "Special Verbs" - set name = "Allow player to respawn" - set desc = "Let a player bypass the wait to respawn or allow them to re-enter their corpse." - - if(!holder) - return - - var/target = tgui_input_list(usr, "Select a ckey to allow to rejoin", "Allow Respawn Selector", GLOB.respawn_timers) - if(!target) - return - - if(GLOB.respawn_timers[target] == -1) // Their respawn timer is set to -1, which is 'not allowed to respawn' - var/response = tgui_alert(src, "Are you sure you wish to allow this individual to respawn? They would normally not be able to.","Allow impossible respawn?",list("No","Yes")) - if(response == "No") - return - - GLOB.respawn_timers -= target - - var/found_client = FALSE - for(var/client/C as anything in GLOB.clients) - if(C.ckey == target) - found_client = C - to_chat(C, "You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead.") - if(isobserver(C.mob)) - var/mob/observer/dead/G = C.mob - G.can_reenter_corpse = 1 - to_chat(C, "You can also re-enter your corpse, if you still have one!") - break - - if(!found_client) - to_chat(src, "The associated client didn't appear to be connected, so they couldn't be notified, but they can now respawn if they reconnect.") - - log_admin("[key_name(usr)] allowed [found_client ? key_name(found_client) : target] to bypass the respawn time limit") - message_admins("Admin [key_name_admin(usr)] allowed [found_client ? key_name_admin(found_client) : target] to bypass the respawn time limit", 1) - - -/client/proc/toggle_antagHUD_use() - set category = "Server" - set name = "Toggle antagHUD usage" - set desc = "Toggles antagHUD usage for observers" - - if(!holder) - return - - var/action="" - if(config.antag_hud_allowed) - for(var/mob/observer/dead/g in get_ghosts()) - if(!g.client.holder) //Remove the verb from non-admin ghosts - g.verbs -= /mob/observer/dead/verb/toggle_antagHUD - if(g.antagHUD) - g.antagHUD = 0 // Disable it on those that have it enabled - g.has_enabled_antagHUD = 2 // We'll allow them to respawn - to_chat(g, "The Administrator has disabled AntagHUD ") - config.antag_hud_allowed = 0 - to_chat(src, "AntagHUD usage has been disabled") - action = "disabled" - else - for(var/mob/observer/dead/g in get_ghosts()) - if(!g.client.holder) // Add the verb back for all non-admin ghosts - g.verbs += /mob/observer/dead/verb/toggle_antagHUD - to_chat(g, "The Administrator has enabled AntagHUD ") // Notify all observers they can now use AntagHUD - config.antag_hud_allowed = 1 - action = "enabled" - to_chat(src, "AntagHUD usage has been enabled") - - - log_admin("[key_name(usr)] has [action] antagHUD usage for observers") - message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) - - - -/client/proc/toggle_antagHUD_restrictions() - set category = "Server" - set name = "Toggle antagHUD Restrictions" - set desc = "Restricts players that have used antagHUD from being able to join this round." - - if(!holder) - return - - var/action="" - if(config.antag_hud_restricted) - for(var/mob/observer/dead/g in get_ghosts()) - to_chat(g, "The administrator has lifted restrictions on joining the round if you use AntagHUD") - action = "lifted restrictions" - config.antag_hud_restricted = 0 - to_chat(src, "AntagHUD restrictions have been lifted") - else - for(var/mob/observer/dead/g in get_ghosts()) - to_chat(g, "The administrator has placed restrictions on joining the round if you use AntagHUD") - to_chat(g, "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ") - g.antagHUD = 0 - g.has_enabled_antagHUD = 0 - action = "placed restrictions" - config.antag_hud_restricted = 1 - to_chat(src, "AntagHUD restrictions have been enabled") - - log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") - message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) - -/* -If a guy was gibbed and you want to revive him, this is a good way to do so. -Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. -Traitors and the like can also be revived with the previous role mostly intact. -/N */ -/client/proc/respawn_character() - set category = "Special Verbs" - set name = "Spawn Character" - set desc = "(Re)Spawn a client's loaded character." - - if(!holder) - return - - var/client/picked_client = tgui_input_list(src, "Please specify which client's character to spawn.", "Client", GLOB.clients) - if(!picked_client) - return - - respawn_character_proper(picked_client) - -/client/proc/respawn_character_proper(client/picked_client) - if(!istype(picked_client)) - return - - //I frontload all the questions so we don't have a half-done process while you're reading. - var/location = tgui_alert(src, "Please specify where to spawn them.", "Location", list("Right Here", "Arrivals", "Cancel")) - if(location == "Cancel" || !location) - return - - var/announce = tgui_alert(src,"Announce as if they had just arrived?", "Announce", list("No", "Yes", "Cancel")) - if(announce == "Cancel") - return - else if(announce == "Yes") //Too bad buttons can't just have 1/0 values and different display strings - announce = 1 - else - announce = 0 - - var/inhabit = tgui_alert(src,"Put the person into the spawned mob?", "Inhabit", list("Yes", "No", "Cancel")) - if(inhabit == "Cancel") - return - else if(inhabit == "Yes") - inhabit = 1 - else - inhabit = 0 - - //Name matching is ugly but mind doesn't persist to look at. - var/charjob - var/records - var/datum/data/record/record_found - record_found = find_general_record("name",picked_client.prefs.real_name) - - //Found their record, they were spawned previously - if(record_found) - var/samejob = tgui_alert(src,"Found [picked_client.prefs.real_name] in data core. They were [record_found.fields["real_rank"]] this round. Assign same job? They will not be re-added to the manifest/records, either way.","Previously spawned",list("Yes","Assistant","No")) - if(samejob == "Yes") - charjob = record_found.fields["real_rank"] - else if(samejob == USELESS_JOB) //VOREStation Edit - Visitor not Assistant - charjob = USELESS_JOB //VOREStation Edit - Visitor not Assistant - else - records = tgui_alert(src,"No data core entry detected. Would you like add them to the manifest, and sec/med/HR records?","Records",list("No", "Yes", "Cancel")) - if(records == "Cancel") - return - if(records == "Yes") - records = 1 - else - records = 0 - - //Well you're not reloading their job or they never had one. - if(!charjob) - var/pickjob = tgui_input_list(src,"Pick a job to assign them (or none).","Job Select", joblist + "-No Job-", "-No Job-") - if(!pickjob) - return - if(pickjob != "-No Job-") - charjob = pickjob - - //If you've picked a job by now, you can equip them. - var/equipment - if(charjob) - equipment = tgui_alert(src,"Spawn them with equipment?", "Equipment", list("Yes", "No", "Cancel")) - if(equipment == "Cancel") - return - else if(equipment == "Yes") - equipment = 1 - else - equipment = 0 - - //For logging later - var/admin = key_name_admin(src) - var/player_key = picked_client.key - //VOREStation Add - Needed for persistence - var/picked_ckey = picked_client.ckey - var/picked_slot = picked_client.prefs.default_slot - //VOREStation Add End - - var/mob/living/carbon/human/new_character - var/spawnloc - var/showy - - //Where did you want to spawn them? - switch(location) - if("Right Here") //Spawn them on your turf - spawnloc = get_turf(src.mob) - showy = tgui_input_list(src,"Showy entrance?", "Showy", list("No", "Telesparks", "Drop Pod", "Fall", "Cancel")) - if(showy == "Cancel") - return - if(showy == "Drop Pod") - showy = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) // reusing var - if(showy == "Cancel") - return - - if("Arrivals") //Spawn them at a latejoin spawnpoint - spawnloc = pick(latejoin) - - else //I have no idea how you're here - to_chat(src, "Invalid spawn location choice.") - return - - //Did we actually get a loc to spawn them? - if(!spawnloc) - to_chat(src, "Couldn't get valid spawn location.") - return - - new_character = new(spawnloc) - - if(showy == "Telesparks") - anim(spawnloc,new_character,'icons/mob/mob.dmi',,"phasein",,new_character.dir) - playsound(spawnloc, "sparks", 50, 1) - var/datum/effect/effect/system/spark_spread/spk = new(new_character) - spk.set_up(5, 0, new_character) - spk.attach(new_character) - spk.start() - - //We were able to spawn them, right? - if(!new_character) - to_chat(src, "Something went wrong and spawning failed.") - return - - //Write the appearance and whatnot out to the character - picked_client.prefs.copy_to(new_character) - if(new_character.dna) - new_character.dna.ResetUIFrom(new_character) - new_character.sync_organ_dna() - if(inhabit) - new_character.key = player_key - //Were they any particular special role? If so, copy. - if(new_character.mind) - var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role) - if(antag_data) - antag_data.add_antagonist(new_character.mind) - antag_data.place_mob(new_character) - - //VOREStation Add - Required for persistence - if(new_character.mind) - new_character.mind.loaded_from_ckey = picked_ckey - new_character.mind.loaded_from_slot = picked_slot - //VOREStation Add End - - for(var/lang in picked_client.prefs.alternate_languages) - var/datum/language/chosen_language = GLOB.all_languages[lang] - if(chosen_language) - if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs))) - new_character.add_language(lang) - - //If desired, apply equipment. - if(equipment) - if(charjob) - job_master.EquipRank(new_character, charjob, 1) - if(new_character.mind) - new_character.mind.assigned_role = charjob - new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) - //equip_custom_items(new_character) //VOREStation Removal - - //If desired, add records. - if(records) - data_core.manifest_inject(new_character) - - //A redraw for good measure - new_character.regenerate_icons() - - new_character.update_transform() //VOREStation Edit - - //If we're announcing their arrival - if(announce) - AnnounceArrival(new_character, new_character.mind.assigned_role, "Common", new_character.z) - - log_admin("[admin] has spawned [player_key]'s character [new_character.real_name].") - message_admins("[admin] has spawned [player_key]'s character [new_character.real_name].", 1) - - - - feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - // Drop pods and fall - if(showy == "Polite") - var/turf/T = get_turf(new_character) - new /obj/structure/drop_pod/polite(T, new_character) - to_chat(new_character, "Please wait for your arrival.") - else if(showy == "Destructive") - var/turf/T = get_turf(new_character) - new /obj/structure/drop_pod(T, new_character) - to_chat(new_character, "Please wait for your arrival.") - else if(showy == "Fall") - spawn(1) - var/initial_x = new_character.pixel_x - var/initial_y = new_character.pixel_y - new_character.plane = 1 - new_character.pixel_x = rand(-150, 150) - new_character.pixel_y = 500 // When you think that pixel_z is height but you are wrong - new_character.density = FALSE - new_character.opacity = FALSE - animate(new_character, pixel_y = initial_y, pixel_x = initial_x , time = 7) - spawn(7) - new_character.end_fall() - to_chat(new_character, "You have been fully spawned. Enjoy the game.") - - return new_character - -/client/proc/cmd_admin_add_freeform_ai_law() - set category = "Fun" - set name = "Add Custom AI law" - - if(!holder) - return - - var/input = sanitize(tgui_input_text(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "")) - if(!input) - return - for(var/mob/living/silicon/ai/M in mob_list) - if (M.stat == 2) - to_chat(usr, "Upload failed. No signal is being detected from the AI.") - else if (M.see_in_dark == 0) - to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") - else - M.add_ion_law(input) - for(var/mob/living/silicon/ai/O in mob_list) - to_chat(O,input + "... LAWS UPDATED!") - O.show_laws() - - log_admin("Admin [key_name(usr)] has added a new AI law - [input]") - message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]", 1) - - var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) - if(show_log == "Yes") - command_announcement.Announce("Ion storm detected near the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') - feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in mob_list) - set category = "Special Verbs" - set name = "Rejuvenate" - - if(!holder) - return - - if(!mob) - return - if(!istype(M)) - tgui_alert_async(usr, "Cannot revive a ghost") - return - if(config.allow_admin_rev) - M.revive() - - log_admin("[key_name(usr)] healed / revived [key_name(M)]") - var/msg = "Admin [key_name_admin(usr)] healed / revived [ADMIN_LOOKUPFLW(M)]!" - message_admins(msg) - admin_ticket_log(M, msg) - else - tgui_alert_async(usr, "Admin revive disabled") - feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_create_centcom_report() - set category = "Special Verbs" - set name = "Create Command Report" - - if(!holder) - return - - var/input = sanitize(tgui_input_text(usr, "Please enter anything you want. Anything. Serious.", "What?", "", multiline = TRUE, prevent_enter = TRUE), extra = 0) - var/customname = sanitizeSafe(tgui_input_text(usr, "Pick a title for the report.", "Title")) - if(!input) - return - if(!customname) - customname = "[using_map.company_name] Update" - - //New message handling - post_comm_message(customname, replacetext(input, "\n", "
                    ")) - - switch(tgui_alert(usr, "Should this be announced to the general population?","Show world?",list("Yes","No"))) - if("Yes") - command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); - if("No") - to_world("New [using_map.company_name] Update available at all communication consoles.") - world << sound('sound/AI/commandreport.ogg') - - log_admin("[key_name(src)] has created a command report: [input]") - message_admins("[key_name_admin(src)] has created a command report", 1) - feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_delete(atom/O as obj|mob|turf in _validate_atom(O)) // I don't understand precisely how this fixes the string matching against a substring, but it does - Ater - set category = "Admin" - set name = "Delete" - - if (!holder) - return - - admin_delete(O) - -/client/proc/cmd_admin_list_open_jobs() - set category = "Admin" - set name = "List free slots" - - if (!holder) - return - - if(job_master) - for(var/datum/job/job in job_master.occupations) - to_chat(src, "[job.title]: [job.total_positions]") - feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world) - set category = "Special Verbs" - set name = "Explosion" - - if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit - - var/devastation = tgui_input_number(usr, "Range of total devastation. -1 to none", text("Input")) - if(devastation == null) return - var/heavy = tgui_input_number(usr, "Range of heavy impact. -1 to none", text("Input")) - if(heavy == null) return - var/light = tgui_input_number(usr, "Range of light impact. -1 to none", text("Input")) - if(light == null) return - var/flash = tgui_input_number(usr, "Range of flash. -1 to none", text("Input")) - if(flash == null) return - - if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1)) - if ((devastation > 20) || (heavy > 20) || (light > 20)) - if (tgui_alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", list("Yes", "No")) == "No") - return - - explosion(O, devastation, heavy, light, flash) - log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])", 1) - feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - else - return - -/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world) - set category = "Special Verbs" - set name = "EM Pulse" - - if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit - - var/heavy = tgui_input_number(usr, "Range of heavy pulse.", text("Input")) - if(heavy == null) return - var/med = tgui_input_number(usr, "Range of medium pulse.", text("Input")) - if(med == null) return - var/light = tgui_input_number(usr, "Range of light pulse.", text("Input")) - if(light == null) return - var/long = tgui_input_number(usr, "Range of long pulse.", text("Input")) - if(long == null) return - - if (heavy || med || light || long) - - empulse(O, heavy, med, light, long) - log_admin("[key_name(usr)] created an EM Pulse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an EM PUlse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])", 1) - feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - return - else - return - -/client/proc/cmd_admin_gib(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Gib" - - if(!check_rights(R_ADMIN|R_FUN)) return //VOREStation Edit - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm != "Yes") return - //Due to the delay here its easy for something to have happened to the mob - if(!M) return - - log_admin("[key_name(usr)] has gibbed [key_name(M)]") - message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) - - if(istype(M, /mob/observer/dead)) - gibs(M.loc) - return - - M.gib() - feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_gib_self() - set name = "Gibself" - set category = "Fun" - - if(!holder) - return - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm == "Yes") - if (istype(mob, /mob/observer/dead)) // so they don't spam gibs everywhere - return - else - mob.gib() - - log_admin("[key_name(usr)] used gibself.") - message_admins("[key_name_admin(usr)] used gibself.", 1) - feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/* -/client/proc/cmd_manual_ban() - set name = "Manual Ban" - set category = "Special Verbs" - if(!authenticated || !holder) - to_chat(src, "Only administrators may use this command.") - return - var/mob/M = null - switch(tgui_alert(usr, "How would you like to ban someone today?", "Manual Ban", "Key List", "Enter Manually", "Cancel")) - if("Key List") - var/list/keys = list() - for(var/mob/M in player_list) - keys += M.client - var/selection = tgui_input_list(usr, "Please, select a player!", "Admin Jumping", keys) - if(!selection) - return - M = selection:mob - if ((M.client && M.client.holder && (M.client.holder.level >= holder.level))) - tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!") - return - - switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No"))) - if("Yes") - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num - if(!mins) - return - if(mins >= 525600) mins = 525599 - var/reason = input(usr,"Reason?","reason","Griefer") as text - if(!reason) - return - if(M) - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") - log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=[mins]&server=[replacetext(config.server_name, "#", "")]") - del(M.client) - qdel(M) - else - - if("No") - var/reason = input(usr,"Reason?","reason","Griefer") as text - if(!reason) - return - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a permanent ban.") - to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") - log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") - message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") - world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=perma&server=[replacetext(config.server_name, "#", "")]") - del(M.client) - qdel(M) -*/ - -/client/proc/update_world() - // If I see anyone granting powers to specific keys like the code that was here, - // I will both remove their SVN access and permanently ban them from my servers. - return - -/client/proc/cmd_admin_check_contents(mob/living/M as mob in mob_list) - set category = "Special Verbs" - set name = "Check Contents" - set popup_menu = FALSE //VOREStation Edit - Declutter. - - if(!holder) - return - - var/list/L = M.get_contents() - for(var/t in L) - to_chat(usr, "[t]") - feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/* This proc is DEFERRED. Does not do anything. -/client/proc/cmd_admin_remove_phoron() - set category = "Debug" - set name = "Stabilize Atmos." - if(!holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","STATM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -// DEFERRED - spawn(0) - for(var/turf/T in view()) - T.poison = 0 - T.oldpoison = 0 - T.tmppoison = 0 - T.oxygen = 755985 - T.oldoxy = 755985 - T.tmpoxy = 755985 - T.co2 = 14.8176 - T.oldco2 = 14.8176 - T.tmpco2 = 14.8176 - T.n2 = 2.844e+006 - T.on2 = 2.844e+006 - T.tn2 = 2.844e+006 - T.tsl_gas = 0 - T.osl_gas = 0 - T.sl_gas = 0 - T.temp = 293.15 - T.otemp = 293.15 - T.ttemp = 293.15 -*/ - -/client/proc/toggle_view_range() - set category = "Special Verbs" - set name = "Change View Range" - set desc = "switches between 1x and custom views" - - if(!holder) - return - - var/view = src.view - if(view == world.view) - view = tgui_input_list(usr, "Select view range:", "FUCK YE", list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128)) - else - view = world.view - mob.set_viewsize(view) - - log_admin("[key_name(usr)] changed their view range to [view].") - //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI - - feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/admin_call_shuttle() - set category = "Admin" - set name = "Call Shuttle" - - if ((!( ticker ) || !emergency_shuttle.location())) - return - - if(!check_rights(R_ADMIN)) return //VOREStation Edit - - var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) - if(confirm != "Yes") return - - var/choice - if(ticker.mode.auto_recall_shuttle) - choice = tgui_input_list(usr, "The shuttle will just return if you call it. Call anyway?", "Shuttle Call", list("Confirm", "Cancel")) - if(choice == "Confirm") - emergency_shuttle.auto_recall = 1 //enable auto-recall - else - return - - choice = tgui_input_list(usr, "Is this an emergency evacuation or a crew transfer?", "Shuttle Call", list("Emergency", "Crew Transfer")) - if (choice == "Emergency") - emergency_shuttle.call_evac() - else - emergency_shuttle.call_transfer() - - - feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-called the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.", 1) - return - -/client/proc/admin_cancel_shuttle() - set category = "Admin" - set name = "Cancel Shuttle" - - if(!check_rights(R_ADMIN)) return //VOREStation Edit - - if(tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) != "Yes") return - - if(!ticker || !emergency_shuttle.can_recall()) - return - - emergency_shuttle.recall() - feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.", 1) - - return - -/client/proc/admin_deny_shuttle() - set category = "Admin" - set name = "Toggle Deny Shuttle" - - if (!ticker) - return - - if(!check_rights(R_ADMIN)) return //VOREStation Edit - - emergency_shuttle.deny_shuttle = !emergency_shuttle.deny_shuttle - - log_admin("[key_name(src)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") - message_admins("[key_name_admin(usr)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") - -/client/proc/cmd_admin_attack_log(mob/M as mob in mob_list) - set category = "Special Verbs" - set name = "Attack Log" - - to_chat(usr, "Attack Log for [mob]") - for(var/t in M.attack_log) - to_chat(usr,t) - feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/everyone_random() - set category = "Fun" - set name = "Make Everyone Random" - set desc = "Make everyone have a random appearance. You can only use this before rounds!" - - if(!check_rights(R_FUN)) return - - if (ticker && ticker.mode) - to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") - return - - if(ticker.random_players) - ticker.random_players = 0 - message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) - to_chat(usr, "Disabled.") - return - - - var/notifyplayers = tgui_alert(src, "Do you want to notify the players?", "Options", list("Yes", "No", "Cancel")) - if(notifyplayers == "Cancel") - return - - log_admin("Admin [key_name(src)] has forced the players to have random appearances.") - message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) - - if(notifyplayers == "Yes") - to_world("Admin [usr.key] has forced the players to have completely random identities!") - - to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") - - ticker.random_players = 1 - feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/toggle_random_events() - set category = "Server" - set name = "Toggle random events on/off" - set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" - - if(!check_rights(R_SERVER)) return //VOREStation Edit - - if(!config.allow_random_events) - config.allow_random_events = 1 - to_chat(usr, "Random events enabled") - message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) - else - config.allow_random_events = 0 - to_chat(usr, "Random events disabled") - message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) - feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/despawn_player(var/mob/M in living_mob_list) - set name = "Cryo Player" - set category = "Admin" - set desc = "Removes a player from the round as if they'd cryo'd." - set popup_menu = FALSE - - if(!check_rights(R_ADMIN|R_EVENT)) - return - - if(!M) - return - - var/confirm = tgui_alert(usr, "Are you sure you want to cryo [M]?","Confirmation",list("No","Yes")) - if(confirm == "No") - return - - var/list/human_cryopods = list() - var/list/robot_cryopods = list() - - for(var/obj/machinery/cryopod/CP in machines) - if(!CP.control_computer) - continue //Broken pod w/o computer, move on. - - var/listname = "[CP.name] ([CP.x],[CP.y],[CP.z])" - if(istype(CP,/obj/machinery/cryopod/robot)) - robot_cryopods[listname] = CP - else - human_cryopods[listname] = CP - - //Gotta log this up here before they get ghostized and lose their key or anything. - log_and_message_admins("[key_name(src)] admin cryo'd [key_name(M)].") - feedback_add_details("admin_verb","ACRYO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(ishuman(M)) - var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", human_cryopods) - var/obj/machinery/cryopod/CP = human_cryopods[choice] - if(!CP) - return - M.ghostize() - CP.despawn_occupant(M) - return - - else if(issilicon(M)) - if(isAI(M)) - var/mob/living/silicon/ai/ai = M - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(ai.loc) - global_announcer.autosay("[ai] has been moved to intelligence storage.", "Artificial Intelligence Oversight") - ai.clear_client() - return - else - var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", robot_cryopods) - var/obj/machinery/cryopod/robot/CP = robot_cryopods[choice] - if(!CP) - return - M.ghostize() - CP.despawn_occupant(M) - return - - else if(isliving(M)) - M.ghostize() - qdel(M) //Bye - -/client/proc/cmd_admin_droppod_spawn(var/object as text) - set name = "Drop Pod Atom" - set desc = "Spawn a new atom/movable in a drop pod where you are." - set category = "Fun" - - if(!check_rights(R_SPAWN)) - return - - var/list/types = typesof(/atom/movable) - var/list/matches = new() - - for(var/path in types) - if(findtext("[path]", object)) - matches += path - - if(!matches.len) - return - - var/chosen - if(matches.len==1) - chosen = matches[1] - else - chosen = tgui_input_list(usr, "Select a movable type:", "Spawn in Drop Pod", matches) - if(!chosen) - return - - var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) - if(podtype == "Cancel") - return - var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) - if(autoopen == "Cancel") - return - switch(podtype) - if("Destructive") - var/atom/movable/AM = new chosen(usr.loc) - new /obj/structure/drop_pod(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) - if("Polite") - var/atom/movable/AM = new chosen(usr.loc) - new /obj/structure/drop_pod/polite(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) - - feedback_add_details("admin_verb","DPA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_droppod_deploy() - set name = "Drop Pod Deploy" - set desc = "Drop an existing mob where you are in a drop pod." - set category = "Fun" - - if(!check_rights(R_SPAWN)) - return - - var/mob/living/L = tgui_input_list(usr, "Select the mob to drop:", "Mob Picker", living_mob_list) - if(!L) - return - - var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) - if(podtype == "Cancel") - return - var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) - if(autoopen == "Cancel") - return - if(!L || QDELETED(L)) - return - switch(podtype) - if("Destructive") - new /obj/structure/drop_pod(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) - if("Polite") - new /obj/structure/drop_pod/polite(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) - - feedback_add_details("admin_verb","DPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file +/client/proc/cmd_admin_drop_everything(mob/M as mob in mob_list) + set category = null + set name = "Drop Everything" + if(!holder) + return + + var/confirm = tgui_alert(src, "Make [M] drop everything?", "Message", list("Yes", "No")) + if(confirm != "Yes") + return + + for(var/obj/item/W in M) + if(istype(W, /obj/item/weapon/implant/backup) || istype(W, /obj/item/device/nif)) //VOREStation Edit - There's basically no reason to remove either of these + continue //VOREStation Edit + M.drop_from_inventory(W) + + log_admin("[key_name(usr)] made [key_name(M)] drop everything!") + message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) + feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_prison(mob/M as mob in mob_list) + set category = "Admin" + set name = "Prison" + if(!holder) + return + + if (ismob(M)) + if(istype(M, /mob/living/silicon/ai)) + tgui_alert_async(usr, "The AI can't be sent to prison you jerk!") + return + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/W in M) + M.drop_from_inventory(W) + //teleport person to cell + M.Paralyse(5) + sleep(5) //so they black out before warping + M.loc = pick(prisonwarp) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + spawn(50) + to_chat(M, span_red("You have been sent to the prison station!")) + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins(span_blue("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station."), 1) + feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +//Allows staff to determine who the newer players are. +/client/proc/cmd_check_new_players() + set category = "Admin" + set name = "Check new Players" + if(!holder) + return + + var/age = tgui_alert(src, "Age check", "Show accounts yonger then _____ days", list("7","30","All")) + + if(age == "All") + age = 9999999 + else + age = text2num(age) + + var/missing_ages = 0 + var/msg = "" + + var/highlight_special_characters = 1 + + for(var/client/C in GLOB.clients) + if(C.player_age == "Requires database") + missing_ages = 1 + continue + if(C.player_age < age) + msg += "[key_name(C, 1, 1, highlight_special_characters)]: account is [C.player_age] days old
                    " + + if(missing_ages) + to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present.") + + if(msg != "") + src << browse(msg, "window=Player_age_check") + else + to_chat(src, "No matches for that age range found.") + +/client/proc/cmd_admin_subtle_message(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Subtle Message" + + if(!ismob(M)) return + if (!holder) + return + + var/msg = tgui_input_text(usr, "Message:", text("Subtle PM to [M.key]")) + + if (!msg) + return + + if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) + + if(usr) + if (usr.client) + if(usr.client.holder) + to_chat(M, "You hear a voice in your head... [msg]") + + log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") + msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" + message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE + set category = "Special Verbs" + set name = "Global Narrate" + + if (!holder) + return + + var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to everyone:")) + + if (!msg) + return + if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) + if (!msg) // We check both before and after, just in case sanitization ended us up with empty message. + return + + to_world("[msg]") + log_admin("GlobalNarrate: [key_name(usr)] : [msg]") + message_admins(span_blue(" GlobalNarrate: [key_name_admin(usr)] : [msg]
                    "), 1) + feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE + set category = "Special Verbs" + set name = "Direct Narrate" + + if(!holder) + return + + if(!M) + M = tgui_input_list(usr, "Direct narrate to who?", "Active Players", get_mob_with_client_list()) + + if(!M) + return + + var/msg = tgui_input_text(usr, "Message:", text("Enter the text you wish to appear to your target:")) + if(msg && !(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) + + if( !msg ) + return + + to_chat(M, msg) + log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]") + msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]
                    " + message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_godmode(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Godmode" + + if(!holder) + return + + M.status_flags ^= GODMODE + to_chat(usr, span_blue("Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]")) + + log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") + var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" + message_admins(msg) + admin_ticket_log(M, msg) + feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) + if(automute) + if(!config.automute_on) + return + else + if(!usr || !usr.client) + return + if(!usr.client.holder) + to_chat(usr, span_red("Error: cmd_admin_mute: You don't have permission to do this.")) + return + if(!M.client) + to_chat(usr, span_red("Error: cmd_admin_mute: This mob doesn't have a client tied to it.")) + if(M.client.holder) + to_chat(usr, span_red("Error: cmd_admin_mute: You cannot mute an admin/mod.")) + if(!M.client) + return + if(M.client.holder) + return + + var/muteunmute + var/mute_string + + switch(mute_type) + if(MUTE_IC) mute_string = "IC (say and emote)" + if(MUTE_OOC) mute_string = "OOC" + if(MUTE_PRAY) mute_string = "pray" + if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" + if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" + if(MUTE_ALL) mute_string = "everything" + else return + + if(automute) + muteunmute = "auto-muted" + M.client.prefs.muted |= mute_type + log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") + message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") + feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + + if(M.client.prefs.muted & mute_type) + muteunmute = "unmuted" + M.client.prefs.muted &= ~mute_type + else + muteunmute = "muted" + M.client.prefs.muted |= mute_type + + log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") + message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string].") + feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_add_random_ai_law() + set category = "Fun" + set name = "Add Random AI Law" + + if(!holder) + return + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm != "Yes") return + log_admin("[key_name(src)] has added a random AI law.") + message_admins("[key_name_admin(src)] has added a random AI law.", 1) + + var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) + if(show_log == "Yes") + command_announcement.Announce("Ion storm detected near \the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + + IonStorm(0) + feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/* +Allow admins to set players to be able to respawn/bypass 30 min wait, without the admin having to edit variables directly +Ccomp's first proc. +*/ + +/client/proc/get_ghosts(var/notify = 0,var/what = 2) + // what = 1, return ghosts ass list. + // what = 2, return mob list + + var/list/mobs = list() + var/list/ghosts = list() + var/list/sortmob = sortAtom(mob_list) // get the mob list. + var/any=0 + for(var/mob/observer/dead/M in sortmob) + mobs.Add(M) //filter it where it's only ghosts + any = 1 //if no ghosts show up, any will just be 0 + if(!any) + if(notify) + to_chat(src, "There doesn't appear to be any ghosts for you to select.") + return + + for(var/mob/M in mobs) + var/name = M.name + ghosts[name] = M //get the name of the mob for the popup list + if(what==1) + return ghosts + else + return mobs + + +/client/proc/allow_character_respawn() + set category = "Special Verbs" + set name = "Allow player to respawn" + set desc = "Let a player bypass the wait to respawn or allow them to re-enter their corpse." + + if(!holder) + return + + var/target = tgui_input_list(usr, "Select a ckey to allow to rejoin", "Allow Respawn Selector", GLOB.respawn_timers) + if(!target) + return + + if(GLOB.respawn_timers[target] == -1) // Their respawn timer is set to -1, which is 'not allowed to respawn' + var/response = tgui_alert(src, "Are you sure you wish to allow this individual to respawn? They would normally not be able to.","Allow impossible respawn?",list("No","Yes")) + if(response == "No") + return + + GLOB.respawn_timers -= target + + var/found_client = FALSE + for(var/client/C as anything in GLOB.clients) + if(C.ckey == target) + found_client = C + to_chat(C, "You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead.") + if(isobserver(C.mob)) + var/mob/observer/dead/G = C.mob + G.can_reenter_corpse = 1 + to_chat(C, "You can also re-enter your corpse, if you still have one!") + break + + if(!found_client) + to_chat(src, "The associated client didn't appear to be connected, so they couldn't be notified, but they can now respawn if they reconnect.") + + log_admin("[key_name(usr)] allowed [found_client ? key_name(found_client) : target] to bypass the respawn time limit") + message_admins("Admin [key_name_admin(usr)] allowed [found_client ? key_name_admin(found_client) : target] to bypass the respawn time limit", 1) + + +/client/proc/toggle_antagHUD_use() + set category = "Server" + set name = "Toggle antagHUD usage" + set desc = "Toggles antagHUD usage for observers" + + if(!holder) + return + + var/action="" + if(config.antag_hud_allowed) + for(var/mob/observer/dead/g in get_ghosts()) + if(!g.client.holder) //Remove the verb from non-admin ghosts + g.verbs -= /mob/observer/dead/verb/toggle_antagHUD + if(g.antagHUD) + g.antagHUD = 0 // Disable it on those that have it enabled + g.has_enabled_antagHUD = 2 // We'll allow them to respawn + to_chat(g, span_red("The Administrator has disabled AntagHUD ")) + config.antag_hud_allowed = 0 + to_chat(src, span_red("AntagHUD usage has been disabled")) + action = "disabled" + else + for(var/mob/observer/dead/g in get_ghosts()) + if(!g.client.holder) // Add the verb back for all non-admin ghosts + g.verbs += /mob/observer/dead/verb/toggle_antagHUD + to_chat(g, span_blue("The Administrator has enabled AntagHUD ")) // Notify all observers they can now use AntagHUD + config.antag_hud_allowed = 1 + action = "enabled" + to_chat(src, span_blue("AntagHUD usage has been enabled")) + + + log_admin("[key_name(usr)] has [action] antagHUD usage for observers") + message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) + + + +/client/proc/toggle_antagHUD_restrictions() + set category = "Server" + set name = "Toggle antagHUD Restrictions" + set desc = "Restricts players that have used antagHUD from being able to join this round." + + if(!holder) + return + + var/action="" + if(config.antag_hud_restricted) + for(var/mob/observer/dead/g in get_ghosts()) + to_chat(g, span_blue("The administrator has lifted restrictions on joining the round if you use AntagHUD")) + action = "lifted restrictions" + config.antag_hud_restricted = 0 + to_chat(src, span_blue("AntagHUD restrictions have been lifted")) + else + for(var/mob/observer/dead/g in get_ghosts()) + to_chat(g, span_red("The administrator has placed restrictions on joining the round if you use AntagHUD")) + to_chat(g, span_red("Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ")) + g.antagHUD = 0 + g.has_enabled_antagHUD = 0 + action = "placed restrictions" + config.antag_hud_restricted = 1 + to_chat(src, span_red("AntagHUD restrictions have been enabled")) + + log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") + message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) + +/* +If a guy was gibbed and you want to revive him, this is a good way to do so. +Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. +Traitors and the like can also be revived with the previous role mostly intact. +/N */ +/client/proc/respawn_character() + set category = "Special Verbs" + set name = "Spawn Character" + set desc = "(Re)Spawn a client's loaded character." + + if(!holder) + return + + var/client/picked_client = tgui_input_list(src, "Please specify which client's character to spawn.", "Client", GLOB.clients) + if(!picked_client) + return + + respawn_character_proper(picked_client) + +/client/proc/respawn_character_proper(client/picked_client) + if(!istype(picked_client)) + return + + //I frontload all the questions so we don't have a half-done process while you're reading. + var/location = tgui_alert(src, "Please specify where to spawn them.", "Location", list("Right Here", "Arrivals", "Cancel")) + if(location == "Cancel" || !location) + return + + var/announce = tgui_alert(src,"Announce as if they had just arrived?", "Announce", list("No", "Yes", "Cancel")) + if(announce == "Cancel") + return + else if(announce == "Yes") //Too bad buttons can't just have 1/0 values and different display strings + announce = 1 + else + announce = 0 + + var/inhabit = tgui_alert(src,"Put the person into the spawned mob?", "Inhabit", list("Yes", "No", "Cancel")) + if(inhabit == "Cancel") + return + else if(inhabit == "Yes") + inhabit = 1 + else + inhabit = 0 + + //Name matching is ugly but mind doesn't persist to look at. + var/charjob + var/records + var/datum/data/record/record_found + record_found = find_general_record("name",picked_client.prefs.real_name) + + //Found their record, they were spawned previously + if(record_found) + var/samejob = tgui_alert(src,"Found [picked_client.prefs.real_name] in data core. They were [record_found.fields["real_rank"]] this round. Assign same job? They will not be re-added to the manifest/records, either way.","Previously spawned",list("Yes","Assistant","No")) + if(samejob == "Yes") + charjob = record_found.fields["real_rank"] + else if(samejob == USELESS_JOB) //VOREStation Edit - Visitor not Assistant + charjob = USELESS_JOB //VOREStation Edit - Visitor not Assistant + else + records = tgui_alert(src,"No data core entry detected. Would you like add them to the manifest, and sec/med/HR records?","Records",list("No", "Yes", "Cancel")) + if(records == "Cancel") + return + if(records == "Yes") + records = 1 + else + records = 0 + + //Well you're not reloading their job or they never had one. + if(!charjob) + var/pickjob = tgui_input_list(src,"Pick a job to assign them (or none).","Job Select", joblist + "-No Job-", "-No Job-") + if(!pickjob) + return + if(pickjob != "-No Job-") + charjob = pickjob + + //If you've picked a job by now, you can equip them. + var/equipment + if(charjob) + equipment = tgui_alert(src,"Spawn them with equipment?", "Equipment", list("Yes", "No", "Cancel")) + if(equipment == "Cancel") + return + else if(equipment == "Yes") + equipment = 1 + else + equipment = 0 + + //For logging later + var/admin = key_name_admin(src) + var/player_key = picked_client.key + //VOREStation Add - Needed for persistence + var/picked_ckey = picked_client.ckey + var/picked_slot = picked_client.prefs.default_slot + //VOREStation Add End + + var/mob/living/carbon/human/new_character + var/spawnloc + var/showy + + //Where did you want to spawn them? + switch(location) + if("Right Here") //Spawn them on your turf + spawnloc = get_turf(src.mob) + showy = tgui_input_list(src,"Showy entrance?", "Showy", list("No", "Telesparks", "Drop Pod", "Fall", "Cancel")) + if(showy == "Cancel") + return + if(showy == "Drop Pod") + showy = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) // reusing var + if(showy == "Cancel") + return + + if("Arrivals") //Spawn them at a latejoin spawnpoint + spawnloc = pick(latejoin) + + else //I have no idea how you're here + to_chat(src, "Invalid spawn location choice.") + return + + //Did we actually get a loc to spawn them? + if(!spawnloc) + to_chat(src, "Couldn't get valid spawn location.") + return + + new_character = new(spawnloc) + + if(showy == "Telesparks") + anim(spawnloc,new_character,'icons/mob/mob.dmi',,"phasein",,new_character.dir) + playsound(spawnloc, "sparks", 50, 1) + var/datum/effect/effect/system/spark_spread/spk = new(new_character) + spk.set_up(5, 0, new_character) + spk.attach(new_character) + spk.start() + + //We were able to spawn them, right? + if(!new_character) + to_chat(src, "Something went wrong and spawning failed.") + return + + //Write the appearance and whatnot out to the character + picked_client.prefs.copy_to(new_character) + if(new_character.dna) + new_character.dna.ResetUIFrom(new_character) + new_character.sync_organ_dna() + if(inhabit) + new_character.key = player_key + //Were they any particular special role? If so, copy. + if(new_character.mind) + var/datum/antagonist/antag_data = get_antag_data(new_character.mind.special_role) + if(antag_data) + antag_data.add_antagonist(new_character.mind) + antag_data.place_mob(new_character) + + //VOREStation Add - Required for persistence + if(new_character.mind) + new_character.mind.loaded_from_ckey = picked_ckey + new_character.mind.loaded_from_slot = picked_slot + //VOREStation Add End + + for(var/lang in picked_client.prefs.alternate_languages) + var/datum/language/chosen_language = GLOB.all_languages[lang] + if(chosen_language) + if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs))) + new_character.add_language(lang) + + //If desired, apply equipment. + if(equipment) + if(charjob) + job_master.EquipRank(new_character, charjob, 1) + if(new_character.mind) + new_character.mind.assigned_role = charjob + new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) + //equip_custom_items(new_character) //VOREStation Removal + + //If desired, add records. + if(records) + data_core.manifest_inject(new_character) + + //A redraw for good measure + new_character.regenerate_icons() + + new_character.update_transform() //VOREStation Edit + + //If we're announcing their arrival + if(announce) + AnnounceArrival(new_character, new_character.mind.assigned_role, "Common", new_character.z) + + log_admin("[admin] has spawned [player_key]'s character [new_character.real_name].") + message_admins("[admin] has spawned [player_key]'s character [new_character.real_name].", 1) + + + + feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + // Drop pods and fall + if(showy == "Polite") + var/turf/T = get_turf(new_character) + new /obj/structure/drop_pod/polite(T, new_character) + to_chat(new_character, "Please wait for your arrival.") + else if(showy == "Destructive") + var/turf/T = get_turf(new_character) + new /obj/structure/drop_pod(T, new_character) + to_chat(new_character, "Please wait for your arrival.") + else if(showy == "Fall") + spawn(1) + var/initial_x = new_character.pixel_x + var/initial_y = new_character.pixel_y + new_character.plane = 1 + new_character.pixel_x = rand(-150, 150) + new_character.pixel_y = 500 // When you think that pixel_z is height but you are wrong + new_character.density = FALSE + new_character.opacity = FALSE + animate(new_character, pixel_y = initial_y, pixel_x = initial_x , time = 7) + spawn(7) + new_character.end_fall() + to_chat(new_character, "You have been fully spawned. Enjoy the game.") + + return new_character + +/client/proc/cmd_admin_add_freeform_ai_law() + set category = "Fun" + set name = "Add Custom AI law" + + if(!holder) + return + + var/input = sanitize(tgui_input_text(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "")) + if(!input) + return + for(var/mob/living/silicon/ai/M in mob_list) + if (M.stat == 2) + to_chat(usr, "Upload failed. No signal is being detected from the AI.") + else if (M.see_in_dark == 0) + to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") + else + M.add_ion_law(input) + for(var/mob/living/silicon/ai/O in mob_list) + to_chat(O,input + span_red("... LAWS UPDATED!")) + O.show_laws() + + log_admin("Admin [key_name(usr)] has added a new AI law - [input]") + message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]", 1) + + var/show_log = tgui_alert(src, "Show ion message?", "Message", list("Yes", "No")) + if(show_log == "Yes") + command_announcement.Announce("Ion storm detected near the [station_name()]. Please check all AI-controlled equipment for errors.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in mob_list) + set category = "Special Verbs" + set name = "Rejuvenate" + + if(!holder) + return + + if(!mob) + return + if(!istype(M)) + tgui_alert_async(usr, "Cannot revive a ghost") + return + if(config.allow_admin_rev) + M.revive() + + log_admin("[key_name(usr)] healed / revived [key_name(M)]") + var/msg = "Admin [key_name_admin(usr)] healed / revived [ADMIN_LOOKUPFLW(M)]!" + message_admins(msg) + admin_ticket_log(M, msg) + else + tgui_alert_async(usr, "Admin revive disabled") + feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_create_centcom_report() + set category = "Special Verbs" + set name = "Create Command Report" + + if(!holder) + return + + var/input = sanitize(tgui_input_text(usr, "Please enter anything you want. Anything. Serious.", "What?", "", multiline = TRUE, prevent_enter = TRUE), extra = 0) + var/customname = sanitizeSafe(tgui_input_text(usr, "Pick a title for the report.", "Title")) + if(!input) + return + if(!customname) + customname = "[using_map.company_name] Update" + + //New message handling + post_comm_message(customname, replacetext(input, "\n", "
                    ")) + + switch(tgui_alert(usr, "Should this be announced to the general population?","Show world?",list("Yes","No"))) + if("Yes") + command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); + if("No") + to_world(span_red("New [using_map.company_name] Update available at all communication consoles.")) + world << sound('sound/AI/commandreport.ogg') + + log_admin("[key_name(src)] has created a command report: [input]") + message_admins("[key_name_admin(src)] has created a command report", 1) + feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_delete(atom/O as obj|mob|turf in _validate_atom(O)) // I don't understand precisely how this fixes the string matching against a substring, but it does - Ater + set category = "Admin" + set name = "Delete" + + if (!holder) + return + + admin_delete(O) + +/client/proc/cmd_admin_list_open_jobs() + set category = "Admin" + set name = "List free slots" + + if (!holder) + return + + if(job_master) + for(var/datum/job/job in job_master.occupations) + to_chat(src, "[job.title]: [job.total_positions]") + feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in world) + set category = "Special Verbs" + set name = "Explosion" + + if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit + + var/devastation = tgui_input_number(usr, "Range of total devastation. -1 to none", text("Input"), min_value=-1) + if(devastation == null) return + var/heavy = tgui_input_number(usr, "Range of heavy impact. -1 to none", text("Input"), min_value=-1) + if(heavy == null) return + var/light = tgui_input_number(usr, "Range of light impact. -1 to none", text("Input"), min_value=-1) + if(light == null) return + var/flash = tgui_input_number(usr, "Range of flash. -1 to none", text("Input"), min_value=-1) + if(flash == null) return + + if ((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1)) + if ((devastation > 20) || (heavy > 20) || (light > 20)) + if (tgui_alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", list("Yes", "No")) == "No") + return + + explosion(O, devastation, heavy, light, flash) + log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flash]) at ([O.x],[O.y],[O.z])", 1) + feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + else + return + +/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in world) + set category = "Special Verbs" + set name = "EM Pulse" + + if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit + + var/heavy = tgui_input_number(usr, "Range of heavy pulse.", text("Input")) + if(heavy == null) return + var/med = tgui_input_number(usr, "Range of medium pulse.", text("Input")) + if(med == null) return + var/light = tgui_input_number(usr, "Range of light pulse.", text("Input")) + if(light == null) return + var/long = tgui_input_number(usr, "Range of long pulse.", text("Input")) + if(long == null) return + + if (heavy || med || light || long) + + empulse(O, heavy, med, light, long) + log_admin("[key_name(usr)] created an EM Pulse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an EM PUlse ([heavy],[med],[light],[long]) at ([O.x],[O.y],[O.z])", 1) + feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + return + else + return + +/client/proc/cmd_admin_gib(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Gib" + + if(!check_rights(R_ADMIN|R_FUN)) return //VOREStation Edit + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm != "Yes") return + //Due to the delay here its easy for something to have happened to the mob + if(!M) return + + log_admin("[key_name(usr)] has gibbed [key_name(M)]") + message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) + + if(istype(M, /mob/observer/dead)) + gibs(M.loc) + return + + M.gib() + feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_gib_self() + set name = "Gibself" + set category = "Fun" + + if(!holder) + return + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm == "Yes") + if (istype(mob, /mob/observer/dead)) // so they don't spam gibs everywhere + return + else + mob.gib() + + log_admin("[key_name(usr)] used gibself.") + message_admins(span_blue("[key_name_admin(usr)] used gibself."), 1) + feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/* +/client/proc/cmd_manual_ban() + set name = "Manual Ban" + set category = "Special Verbs" + if(!authenticated || !holder) + to_chat(src, "Only administrators may use this command.") + return + var/mob/M = null + switch(tgui_alert(usr, "How would you like to ban someone today?", "Manual Ban", "Key List", "Enter Manually", "Cancel")) + if("Key List") + var/list/keys = list() + for(var/mob/M in player_list) + keys += M.client + var/selection = tgui_input_list(usr, "Please, select a player!", "Admin Jumping", keys) + if(!selection) + return + M = selection:mob + if ((M.client && M.client.holder && (M.client.holder.level >= holder.level))) + tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!") + return + + switch(tgui_alert(usr, "Temporary Ban?","Temporary Ban",list("Yes","No"))) + if("Yes") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num + if(!mins) + return + if(mins >= 525600) mins = 525599 + var/reason = input(usr,"Reason?","reason","Griefer") as text + if(!reason) + return + if(M) + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") + log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=[mins]&server=[replacetext(config.server_name, "#", "")]") + del(M.client) + qdel(M) + else + + if("No") + var/reason = input(usr,"Reason?","reason","Griefer") as text + if(!reason) + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") + to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") + log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") + message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") + world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=perma&server=[replacetext(config.server_name, "#", "")]") + del(M.client) + qdel(M) +*/ + +/client/proc/update_world() + // If I see anyone granting powers to specific keys like the code that was here, + // I will both remove their SVN access and permanently ban them from my servers. + return + +/client/proc/cmd_admin_check_contents(mob/living/M as mob in mob_list) + set category = "Special Verbs" + set name = "Check Contents" + set popup_menu = FALSE //VOREStation Edit - Declutter. + + if(!holder) + return + + var/list/L = M.get_contents() + for(var/t in L) + to_chat(usr, "[t]") + feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/* This proc is DEFERRED. Does not do anything. +/client/proc/cmd_admin_remove_phoron() + set category = "Debug" + set name = "Stabilize Atmos." + if(!holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","STATM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +// DEFERRED + spawn(0) + for(var/turf/T in view()) + T.poison = 0 + T.oldpoison = 0 + T.tmppoison = 0 + T.oxygen = 755985 + T.oldoxy = 755985 + T.tmpoxy = 755985 + T.co2 = 14.8176 + T.oldco2 = 14.8176 + T.tmpco2 = 14.8176 + T.n2 = 2.844e+006 + T.on2 = 2.844e+006 + T.tn2 = 2.844e+006 + T.tsl_gas = 0 + T.osl_gas = 0 + T.sl_gas = 0 + T.temp = 293.15 + T.otemp = 293.15 + T.ttemp = 293.15 +*/ + +/client/proc/toggle_view_range() + set category = "Special Verbs" + set name = "Change View Range" + set desc = "switches between 1x and custom views" + + if(!holder) + return + + var/view = src.view + if(view == world.view) + view = tgui_input_list(usr, "Select view range:", "FUCK YE", list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128)) + else + view = world.view + mob.set_viewsize(view) + + log_admin("[key_name(usr)] changed their view range to [view].") + //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI + + feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/admin_call_shuttle() + set category = "Admin" + set name = "Call Shuttle" + + if ((!( ticker ) || !emergency_shuttle.location())) + return + + if(!check_rights(R_ADMIN)) return //VOREStation Edit + + var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) + if(confirm != "Yes") return + + var/choice + if(ticker.mode.auto_recall_shuttle) + choice = tgui_input_list(usr, "The shuttle will just return if you call it. Call anyway?", "Shuttle Call", list("Confirm", "Cancel")) + if(choice == "Confirm") + emergency_shuttle.auto_recall = 1 //enable auto-recall + else + return + + choice = tgui_input_list(usr, "Is this an emergency evacuation or a crew transfer?", "Shuttle Call", list("Emergency", "Crew Transfer")) + if (choice == "Emergency") + emergency_shuttle.call_evac() + else + emergency_shuttle.call_transfer() + + + feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-called the emergency shuttle.") + message_admins(span_blue("[key_name_admin(usr)] admin-called the emergency shuttle."), 1) + return + +/client/proc/admin_cancel_shuttle() + set category = "Admin" + set name = "Cancel Shuttle" + + if(!check_rights(R_ADMIN)) return //VOREStation Edit + + if(tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) != "Yes") return + + if(!ticker || !emergency_shuttle.can_recall()) + return + + emergency_shuttle.recall() + feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") + message_admins(span_blue("[key_name_admin(usr)] admin-recalled the emergency shuttle."), 1) + + return + +/client/proc/admin_deny_shuttle() + set category = "Admin" + set name = "Toggle Deny Shuttle" + + if (!ticker) + return + + if(!check_rights(R_ADMIN)) return //VOREStation Edit + + emergency_shuttle.deny_shuttle = !emergency_shuttle.deny_shuttle + + log_admin("[key_name(src)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") + message_admins("[key_name_admin(usr)] has [emergency_shuttle.deny_shuttle ? "denied" : "allowed"] the shuttle to be called.") + +/client/proc/cmd_admin_attack_log(mob/M as mob in mob_list) + set category = "Special Verbs" + set name = "Attack Log" + + to_chat(usr, span_red("Attack Log for [mob]")) + for(var/t in M.attack_log) + to_chat(usr,t) + feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/everyone_random() + set category = "Fun" + set name = "Make Everyone Random" + set desc = "Make everyone have a random appearance. You can only use this before rounds!" + + if(!check_rights(R_FUN)) return + + if (ticker && ticker.mode) + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") + return + + if(ticker.random_players) + ticker.random_players = 0 + message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) + to_chat(usr, "Disabled.") + return + + + var/notifyplayers = tgui_alert(src, "Do you want to notify the players?", "Options", list("Yes", "No", "Cancel")) + if(notifyplayers == "Cancel") + return + + log_admin("Admin [key_name(src)] has forced the players to have random appearances.") + message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) + + if(notifyplayers == "Yes") + to_world(span_blue("Admin [usr.key] has forced the players to have completely random identities!")) + + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") + + ticker.random_players = 1 + feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/toggle_random_events() + set category = "Server" + set name = "Toggle random events on/off" + set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" + + if(!check_rights(R_SERVER)) return //VOREStation Edit + + if(!config.allow_random_events) + config.allow_random_events = 1 + to_chat(usr, "Random events enabled") + message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) + else + config.allow_random_events = 0 + to_chat(usr, "Random events disabled") + message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) + feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/despawn_player(var/mob/M in living_mob_list) + set name = "Cryo Player" + set category = "Admin" + set desc = "Removes a player from the round as if they'd cryo'd." + set popup_menu = FALSE + + if(!check_rights(R_ADMIN|R_EVENT)) + return + + if(!M) + return + + var/confirm = tgui_alert(usr, "Are you sure you want to cryo [M]?","Confirmation",list("No","Yes")) + if(confirm == "No") + return + + var/list/human_cryopods = list() + var/list/robot_cryopods = list() + + for(var/obj/machinery/cryopod/CP in machines) + if(!CP.control_computer) + continue //Broken pod w/o computer, move on. + + var/listname = "[CP.name] ([CP.x],[CP.y],[CP.z])" + if(istype(CP,/obj/machinery/cryopod/robot)) + robot_cryopods[listname] = CP + else + human_cryopods[listname] = CP + + //Gotta log this up here before they get ghostized and lose their key or anything. + log_and_message_admins("[key_name(src)] admin cryo'd [key_name(M)].") + feedback_add_details("admin_verb","ACRYO") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(ishuman(M)) + var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", human_cryopods) + var/obj/machinery/cryopod/CP = human_cryopods[choice] + if(!CP) + return + M.ghostize() + CP.despawn_occupant(M) + return + + else if(issilicon(M)) + if(isAI(M)) + var/mob/living/silicon/ai/ai = M + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(ai.loc) + global_announcer.autosay("[ai] has been moved to intelligence storage.", "Artificial Intelligence Oversight") + ai.clear_client() + return + else + var/choice = tgui_input_list(usr,"Select a cryopod to use","Cryopod Choice", robot_cryopods) + var/obj/machinery/cryopod/robot/CP = robot_cryopods[choice] + if(!CP) + return + M.ghostize() + CP.despawn_occupant(M) + return + + else if(isliving(M)) + M.ghostize() + qdel(M) //Bye + +/client/proc/cmd_admin_droppod_spawn(var/object as text) + set name = "Drop Pod Atom" + set desc = "Spawn a new atom/movable in a drop pod where you are." + set category = "Fun" + + if(!check_rights(R_SPAWN)) + return + + var/list/types = typesof(/atom/movable) + var/list/matches = new() + + for(var/path in types) + if(findtext("[path]", object)) + matches += path + + if(!matches.len) + return + + var/chosen + if(matches.len==1) + chosen = matches[1] + else + chosen = tgui_input_list(usr, "Select a movable type:", "Spawn in Drop Pod", matches) + if(!chosen) + return + + var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) + if(podtype == "Cancel") + return + var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) + if(autoopen == "Cancel") + return + switch(podtype) + if("Destructive") + var/atom/movable/AM = new chosen(usr.loc) + new /obj/structure/drop_pod(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) + if("Polite") + var/atom/movable/AM = new chosen(usr.loc) + new /obj/structure/drop_pod/polite(get_turf(usr), AM, autoopen == "Yes" ? TRUE : FALSE) + + feedback_add_details("admin_verb","DPA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_droppod_deploy() + set name = "Drop Pod Deploy" + set desc = "Drop an existing mob where you are in a drop pod." + set category = "Fun" + + if(!check_rights(R_SPAWN)) + return + + var/mob/living/L = tgui_input_list(usr, "Select the mob to drop:", "Mob Picker", living_mob_list) + if(!L) + return + + var/podtype = tgui_alert(src,"Destructive drop pods cause damage in a 3x3 and may break turfs. Polite drop pods lightly damage the turfs but won't break through.", "Drop Pod", list("Polite", "Destructive", "Cancel")) + if(podtype == "Cancel") + return + var/autoopen = tgui_alert(src,"Should the pod open automatically?", "Drop Pod", list("Yes", "No", "Cancel")) + if(autoopen == "Cancel") + return + if(!L || QDELETED(L)) + return + switch(podtype) + if("Destructive") + new /obj/structure/drop_pod(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) + if("Polite") + new /obj/structure/drop_pod/polite(get_turf(usr), L, autoopen == "Yes" ? TRUE : FALSE) + + feedback_add_details("admin_verb","DPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/randomverbs_vr.dm b/code/modules/admin/verbs/randomverbs_vr.dm index d19d67916a0..d4836faf54c 100644 --- a/code/modules/admin/verbs/randomverbs_vr.dm +++ b/code/modules/admin/verbs/randomverbs_vr.dm @@ -2,7 +2,7 @@ set category = "Special Verbs" set name = "Spawn Character As Mob" set desc = "Spawn a specified ckey as a chosen mob." - + if(!holder) return @@ -63,6 +63,8 @@ new_mob.key = picked_client.key //Finally put them in the mob if(organs) new_mob.copy_from_prefs_vr() + if(LAZYLEN(new_mob.vore_organs)) + new_mob.vore_selected = new_mob.vore_organs[1] log_admin("[key_name_admin(src)] has spawned [new_mob.key] as mob [new_mob.type].") message_admins("[key_name_admin(src)] has spawned [new_mob.key] as mob [new_mob.type].", 1) @@ -95,7 +97,7 @@ if(M.z == pos_z) to_chat(M, msg) log_admin("ZNarrate: [key_name(usr)] : [msg]") - message_admins(" ZNarrate: [key_name_admin(usr)] : [msg]
                    ", 1) + message_admins(span_blue(" ZNarrate: [key_name_admin(usr)] : [msg]
                    "), 1) feedback_add_details("admin_verb","GLNA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/toggle_vantag_hud(var/mob/target as mob) @@ -107,10 +109,10 @@ target.vantag_hud = FALSE target.recalculate_vis() to_chat(src, "You removed the event HUD from [key_name(target)].") - to_chat(target, "You no longer have the event HUD.") + to_chat(target, "You no longer have the event HUD.") else target.vantag_hud = TRUE target.recalculate_vis() to_chat(src, "You gave the event HUD to [key_name(target)].") - to_chat(target, "You now have the event HUD. Icons will appear next to characters indicating if they prefer to be killed(red crosshairs), devoured(belly), or kidnapped(blue crosshairs) by event characters.") + to_chat(target, "You now have the event HUD. Icons will appear next to characters indicating if they prefer to be killed(red crosshairs), devoured(belly), or kidnapped(blue crosshairs) by event characters.") feedback_add_details("admin_verb","GREHud") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/resize.dm b/code/modules/admin/verbs/resize.dm index dc340539307..c67e2ffd8f7 100644 --- a/code/modules/admin/verbs/resize.dm +++ b/code/modules/admin/verbs/resize.dm @@ -2,10 +2,10 @@ set name = "Resize" set desc = "Resizes any living mob without any restrictions on size." set category = "Fun" - if(!check_rights(R_ADMIN, R_FUN)) + if(!check_rights(R_ADMIN|R_FUN|R_VAREDIT)) return - - var/size_multiplier = tgui_input_number(usr, "Input size multiplier.", "Resize", 1) + + var/size_multiplier = tgui_input_number(usr, "Input size multiplier.", "Resize", 1, round_value=FALSE) if(!size_multiplier) return //cancelled @@ -21,4 +21,4 @@ L.resize(size_multiplier, animate = TRUE, uncapped = TRUE, ignore_prefs = TRUE) log_and_message_admins("has changed [key_name(L)]'s size multiplier to [size_multiplier].") - feedback_add_details("admin_verb","RESIZE") \ No newline at end of file + feedback_add_details("admin_verb","RESIZE") diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 7724062a6ed..737ab67f843 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -1,56 +1,56 @@ -//STRIKE TEAMS -var/const/commandos_possible = 6 //if more Commandos are needed in the future - -/client/proc/strike_team() - set category = "Fun" - set name = "Spawn Strike Team" - set desc = "Spawns a strike team if you want to run an admin event." - - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - - if(!ticker) - to_chat(usr, "The game hasn't started yet!") - return - - if(world.time < 6000) - to_chat(usr, "There are [(6000-world.time)/10] seconds remaining before it may be called.") - return - - var/datum/antagonist/deathsquad/team - - var/choice = tgui_input_list(usr, "Select type of strike team:", "Strike Team", list("Heavy Asset Protection", "Mercenaries")) - if(!choice) - return - - switch(choice) - if("Heavy Asset Protection") - team = deathsquad - if("Mercenaries") - team = commandos - else - return - - if(team.deployed) - to_chat(usr, "Someone is already sending a team.") - return - - if(tgui_alert(usr, "Do you want to send in a strike team? Once enabled, this is irreversible.","Strike Team",list("Yes","No"))!="Yes") - return - - tgui_alert(usr, "This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. Assigning the team's detailed task is recommended from there. While you will be able to manually pick the candidates from active ghosts, their assignment in the squad will be random.") // Should remain tgui_alert() (blocking) - - choice = null - while(!choice) - choice = sanitize(tgui_input_text(src, "Please specify which mission the strike team shall undertake.", "Specify Mission", "")) - if(!choice) - if(tgui_alert(usr, "Error, no mission set. Do you want to exit the setup process?","Strike Team",list("Yes","No"))=="Yes") - return - consider_ert_load() //VOREStation Add - - if(team.deployed) - to_chat(usr, "Looks like someone beat you to it.") - return - - team.attempt_random_spawn() +//STRIKE TEAMS +var/const/commandos_possible = 6 //if more Commandos are needed in the future + +/client/proc/strike_team() + set category = "Fun" + set name = "Spawn Strike Team" + set desc = "Spawns a strike team if you want to run an admin event." + + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + + if(!ticker) + to_chat(usr, span_red("The game hasn't started yet!")) + return + + if(world.time < 6000) + to_chat(usr, span_red("There are [(6000-world.time)/10] seconds remaining before it may be called.")) + return + + var/datum/antagonist/deathsquad/team + + var/choice = tgui_input_list(usr, "Select type of strike team:", "Strike Team", list("Heavy Asset Protection", "Mercenaries")) + if(!choice) + return + + switch(choice) + if("Heavy Asset Protection") + team = deathsquad + if("Mercenaries") + team = commandos + else + return + + if(team.deployed) + to_chat(usr, span_red("Someone is already sending a team.")) + return + + if(tgui_alert(usr, "Do you want to send in a strike team? Once enabled, this is irreversible.","Strike Team",list("Yes","No"))!="Yes") + return + + tgui_alert(usr, "This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. Assigning the team's detailed task is recommended from there. While you will be able to manually pick the candidates from active ghosts, their assignment in the squad will be random.") // Should remain tgui_alert() (blocking) + + choice = null + while(!choice) + choice = sanitize(tgui_input_text(src, "Please specify which mission the strike team shall undertake.", "Specify Mission", "")) + if(!choice) + if(tgui_alert(usr, "Error, no mission set. Do you want to exit the setup process?","Strike Team",list("Yes","No"))=="Yes") + return + consider_ert_load() //VOREStation Add + + if(team.deployed) + to_chat(usr, "Looks like someone beat you to it.") + return + + team.attempt_random_spawn() diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index eaf6dfa49f5..ff97f2e0dd9 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -1,22 +1,22 @@ -/client/proc/triple_ai() - set category = "Fun" - set name = "Create AI Triumvirate" - - if(ticker.current_state > GAME_STATE_PREGAME) - to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") - return - - if(job_master && ticker) - var/datum/job/job = job_master.GetJob("AI") - if(!job) - to_chat(usr, "Unable to locate the AI job") - return - if(ticker.triai) - ticker.triai = 0 - to_chat(usr, "Only one AI will be spawned at round start.") - message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) - else - ticker.triai = 1 - to_chat(usr, "There will be an AI Triumvirate at round start.") - message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) - return +/client/proc/triple_ai() + set category = "Fun" + set name = "Create AI Triumvirate" + + if(ticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") + return + + if(job_master && ticker) + var/datum/job/job = job_master.GetJob("AI") + if(!job) + to_chat(usr, "Unable to locate the AI job") + return + if(ticker.triai) + ticker.triai = 0 + to_chat(usr, "Only one AI will be spawned at round start.") + message_admins(span_blue("[key_name_admin(usr)] has toggled off triple AIs at round start."), 1) + else + ticker.triai = 1 + to_chat(usr, "There will be an AI Triumvirate at round start.") + message_admins(span_blue("[key_name_admin(usr)] has toggled on triple AIs at round start."), 1) + return diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm index f3fb4ea145e..daac9c35742 100644 --- a/code/modules/admin/view_variables/get_variables.dm +++ b/code/modules/admin/view_variables/get_variables.dm @@ -97,7 +97,7 @@ if (VV_NUM) - .["value"] = tgui_input_number(usr, "Enter new number:", "Num", current_value) + .["value"] = tgui_input_number(usr, "Enter new number:", "Num", current_value, INFINITY, -INFINITY, round_value = FALSE) if (.["value"] == null) .["class"] = null return diff --git a/code/modules/admin/view_variables/helpers.dm b/code/modules/admin/view_variables/helpers.dm index a678271e88b..9ee161b0dc5 100644 --- a/code/modules/admin/view_variables/helpers.dm +++ b/code/modules/admin/view_variables/helpers.dm @@ -86,6 +86,11 @@ "} +/obj/item/device/pda/get_view_variables_options() + return ..() + {" + + "} + /datum/proc/get_variables() . = vars - VV_hidden() if(!usr || !check_rights(R_ADMIN|R_DEBUG, FALSE)) diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 8a3f8811c25..c5e5cf4dedc 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -103,7 +103,7 @@ var/severity = tgui_input_number(usr, "How much damage should the bleeding internal wound cause? \ Bleed timer directly correlates with this. 0 cancels. Input is rounded to nearest integer.", - "Wound Severity", 0, min_value = 0, round_value = TRUE ) + "Wound Severity", 0) if(!severity) return var/obj/item/organ/external/chosen_organ = tgui_input_list(usr, "Choose an external organ to inflict IB on!", "Organ Choice", H.organs) @@ -274,6 +274,15 @@ return log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ") + else if(href_list["fakepdapropconvo"]) + if(!check_rights(R_FUN)) return + + var/obj/item/device/pda/P = locate(href_list["fakepdapropconvo"]) + if(!istype(P)) + to_chat(usr, span_warning("This can only be done to instances of type /pda")) + return + + P.createPropFakeConversation_admin(usr) else if(href_list["rotatedatum"]) if(!check_rights(0)) return @@ -536,7 +545,7 @@ var/Text = href_list["adjustDamage"] - var/amount = tgui_input_number(usr, "Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) + var/amount = tgui_input_number(usr, "Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0, min_value=-INFINITY, round_value=FALSE) if(!L) to_chat(usr, "Mob doesn't exist anymore") diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index cd052969590..86bda7de8a3 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -1,283 +1,285 @@ -/client/proc/debug_variables(datum/D in world) - set category = "Debug" - set name = "View Variables" - //set src in world - var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. - - if(!usr.client || !usr.client.holder) //The usr vs src abuse in this proc is intentional and must not be changed - to_chat(usr, "You need to be an administrator to access this.") - return - - if(!D) - return - - var/islist = islist(D) - if (!islist && !istype(D)) - return - - var/title = "" - var/refid = "\ref[D]" - var/icon/sprite - var/hash - - var/type = /list - if (!islist) - type = D.type - - if(istype(D, /atom)) - var/atom/AT = D - if(AT.icon && AT.icon_state) - sprite = new /icon(AT.icon, AT.icon_state) - hash = md5(AT.icon) - hash = md5(hash + AT.icon_state) - src << browse_rsc(sprite, "vv[hash].png") - - title = "[D] (\ref[D]) = [type]" - var/formatted_type = replacetext("[type]", "/", "/") - - var/sprite_text - if(sprite) - sprite_text = "" - var/list/header = islist(D)? list("/list") : D.vv_get_header() - - var/marked - if(holder && holder.marked_datum && holder.marked_datum == D) - marked = VV_MSG_MARKED - var/varedited_line = "" - if(!islist && (D.datum_flags & DF_VAR_EDITED)) - varedited_line = VV_MSG_EDITED - var/deleted_line - if(!islist && D.gc_destroyed) - deleted_line = VV_MSG_DELETED - - var/list/dropdownoptions = list() - var/autoconvert_dropdown = FALSE - if (islist) - dropdownoptions = list( - "---", - "Add Item" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ADD]=TRUE;target=[refid]", - "Remove Nulls" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_NULLS]=TRUE;target=[refid]", - "Remove Dupes" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_DUPES]=TRUE;target=[refid]", - "Set len" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SET_LENGTH]=TRUE;target=[refid]", - "Shuffle" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SHUFFLE]=TRUE;target=[refid]", - // "Show VV To Player" = "?_src_=vars;[HrefToken()];[VV_HK_EXPOSE]=TRUE;target=[refid]" // TODO - Not yet implemented for lists - ) - autoconvert_dropdown = TRUE - else - dropdownoptions = D.vv_get_dropdown() - var/list/dropdownoptions_html = list() - if(autoconvert_dropdown) - for (var/name in dropdownoptions) - var/link = dropdownoptions[name] - if (link) - dropdownoptions_html += "" - else - dropdownoptions_html += "" - else - dropdownoptions_html = dropdownoptions + D.get_view_variables_options() - - var/list/names = list() - if (!islist) - names = D.get_variables() - sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. - - var/list/variable_html = list() - if (islist) - var/list/L = D - for (var/i in 1 to L.len) - var/key = L[i] - var/value - if (IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, D) - else - - names = sortList(names) - for (var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) - - var/html = {" - - - [title] - - - - -
                    - - - - - -
                    - - - - -
                    - [sprite_text] -
                    - [header.Join()] -
                    -
                    -
                    - [formatted_type] - [marked] - [varedited_line] - [deleted_line] -
                    -
                    -
                    - Refresh -
                    - -
                    -
                    -
                    -
                    -
                    - - E - Edit, tries to determine the variable type by itself.
                    - C - Change, asks you for the var type first.
                    - M - Mass modify: changes this variable for all objects of this type.
                    -
                    -
                    - - - - - -
                    -
                    - Search: -
                    -
                    - -
                    -
                    -
                      - [variable_html.Join()] -
                    - - - -"} - src << browse(html, "window=variables[refid];size=475x650") - -/client/proc/vv_update_display(datum/D, span, content) - src << output("[span]:[content]", "variables\ref[D].browser:replace_span") +/client/proc/debug_variables(datum/D in world) + set category = "Debug" + set name = "View Variables" + //set src in world + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. + + if(!usr.client || !usr.client.holder) //The usr vs src abuse in this proc is intentional and must not be changed + to_chat(usr, "You need to be an administrator to access this.") + return + + if(!D) + return + + var/islist = islist(D) + if (!islist && !istype(D)) + return + + //VOREStation Edit Start - the rest of this proc in a spawn + spawn(0) + var/title = "" + var/refid = "\ref[D]" + var/icon/sprite + var/hash + + var/type = /list + if (!islist) + type = D.type + + if(istype(D, /atom)) + var/atom/AT = D + if(AT.icon && AT.icon_state) + sprite = new /icon(AT.icon, AT.icon_state) + hash = md5(AT.icon) + hash = md5(hash + AT.icon_state) + src << browse_rsc(sprite, "vv[hash].png") + + title = "[D] (\ref[D]) = [type]" + var/formatted_type = replacetext("[type]", "/", "/") + + var/sprite_text + if(sprite) + sprite_text = "" + var/list/header = islist(D)? list("/list") : D.vv_get_header() + + var/marked + if(holder && holder.marked_datum && holder.marked_datum == D) + marked = VV_MSG_MARKED + var/varedited_line = "" + if(!islist && (D.datum_flags & DF_VAR_EDITED)) + varedited_line = VV_MSG_EDITED + var/deleted_line + if(!islist && D.gc_destroyed) + deleted_line = VV_MSG_DELETED + + var/list/dropdownoptions = list() + var/autoconvert_dropdown = FALSE + if (islist) + dropdownoptions = list( + "---", + "Add Item" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ADD]=TRUE;target=[refid]", + "Remove Nulls" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_NULLS]=TRUE;target=[refid]", + "Remove Dupes" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_ERASE_DUPES]=TRUE;target=[refid]", + "Set len" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SET_LENGTH]=TRUE;target=[refid]", + "Shuffle" = "?_src_=vars;[HrefToken()];[VV_HK_LIST_SHUFFLE]=TRUE;target=[refid]", + // "Show VV To Player" = "?_src_=vars;[HrefToken()];[VV_HK_EXPOSE]=TRUE;target=[refid]" // TODO - Not yet implemented for lists + ) + autoconvert_dropdown = TRUE + else + dropdownoptions = D.vv_get_dropdown() + var/list/dropdownoptions_html = list() + if(autoconvert_dropdown) + for (var/name in dropdownoptions) + var/link = dropdownoptions[name] + if (link) + dropdownoptions_html += "" + else + dropdownoptions_html += "" + else + dropdownoptions_html = dropdownoptions + D.get_view_variables_options() + + var/list/names = list() + if (!islist) + names = D.get_variables() + //sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects. //VOREStation edit - commented out, replaced with spawn(0) above + + var/list/variable_html = list() + if (islist) + var/list/L = D + for (var/i in 1 to L.len) + var/key = L[i] + var/value + if (IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, D) + else + + names = sortList(names) + for (var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) + + var/html = {" + + + [title] + + + + +
                    + + + + + +
                    + + + + +
                    + [sprite_text] +
                    + [header.Join()] +
                    +
                    +
                    + [formatted_type] + [marked] + [varedited_line] + [deleted_line] +
                    +
                    +
                    + Refresh +
                    + +
                    +
                    +
                    +
                    +
                    + + E - Edit, tries to determine the variable type by itself.
                    + C - Change, asks you for the var type first.
                    + M - Mass modify: changes this variable for all objects of this type.
                    +
                    +
                    + + + + + +
                    +
                    + Search: +
                    +
                    + +
                    +
                    +
                      + [variable_html.Join()] +
                    + + + + "} + src << browse(html, "window=variables[refid];size=475x650") //VOREStation edit end + +/client/proc/vv_update_display(datum/D, span, content) + src << output("[span]:[content]", "variables\ref[D].browser:replace_span") diff --git a/code/modules/ai/ai_holder_disabled.dm b/code/modules/ai/ai_holder_disabled.dm index 796f0947768..cffce1e0640 100644 --- a/code/modules/ai/ai_holder_disabled.dm +++ b/code/modules/ai/ai_holder_disabled.dm @@ -1,101 +1,104 @@ -// Handles AI while stunned or otherwise disabled. - -/datum/ai_holder - var/respect_confusion = TRUE // If false, the mob won't wander around recklessly. - -// If our holder is able to do anything. -/datum/ai_holder/proc/can_act() - if(!holder) // Holder missing. - manage_processing(0) - return FALSE - if(holder.stat) // Dead or unconscious. - ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE) - return FALSE - if(holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned in some form. - ai_log("can_act() : Incapacited.", AI_LOG_TRACE) - return FALSE - if(holder.instasis()) // In a stasis field. - ai_log("can_act() : In a stasis field.", AI_LOG_TRACE) - return FALSE - return TRUE - -// Test if we should switch to STANCE_DISABLE. -// Currently tests for death, stuns, and confusion. -/datum/ai_holder/proc/is_disabled() - if(!can_act()) - return TRUE - if(is_confused()) - return TRUE - return FALSE - -/datum/ai_holder/proc/is_confused() - return holder.confused > 0 && respect_confusion - -// Called by the main loop. -/datum/ai_holder/proc/handle_disabled() - if(!can_act()) - return // Just sit there and take it. - else if(is_confused()) - dangerous_wander() // Let's bump into allies and hit them. - -// Similar to normal wander, but will walk into tiles that are harmful, and attack anything they bump into, including allies. -// Occurs when confused. -/datum/ai_holder/proc/dangerous_wander() - ai_log("dangerous_wander() : Entered.", AI_LOG_DEBUG) - if(isturf(holder.loc) && can_act()) - // Test if we should refrain from falling/attacking allies, if we're smart enough to realize that. - if(intelligence_level > AI_NORMAL) - var/unsafe = FALSE - - tile_test: - for(var/dir_tested in cardinal) - var/turf/turf_tested = get_step(holder, dir_tested) - // Look for unsafe tiles. - if(!turf_tested.is_safe_to_enter(holder)) - unsafe = TRUE - break - - // Look for allies. - for(var/mob/living/L in turf_tested) - if(holder.IIsAlly(L)) - unsafe = TRUE - break tile_test - - - if(unsafe) - ai_log("dangerous_wander() : Staying still due to risk of harm to self or allies.", AI_LOG_TRACE) - return // Just stay still. - - var/moving_to = 0 - moving_to = pick(cardinal) - var/turf/T = get_step(holder, moving_to) - - var/mob/living/L = locate() in T - if(L) - // Attack whoever's on the tile. Even if it's an ally. - ai_log("dangerous_wander() : Going to confuse-attack [L].", AI_LOG_TRACE) - melee_attack(L) - else - // Move to the tile. Even if it's unsafe. - ai_log("dangerous_wander() : Going to confuse-walk to [T] ([T.x],[T.y],[T.z]).", AI_LOG_TRACE) - holder.IMove(T, safety = FALSE) - ai_log("dangerous_wander() : Exited.", AI_LOG_DEBUG) - -/* -// Wanders randomly in cardinal directions. -/datum/ai_holder/proc/handle_wander_movement() - ai_log("handle_wander_movement() : Entered.", AI_LOG_DEBUG) - if(isturf(holder.loc) && can_act()) - wander_delay-- - if(wander_delay <= 0) - if(!wander_when_pulled && holder.pulledby) - ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) - return - - var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. - moving_to = pick(cardinal) - holder.set_dir(moving_to) - holder.IMove(get_step(holder,moving_to)) - wander_delay = base_wander_delay - ai_log("handle_wander_movement() : Exited.", AI_LOG_DEBUG) -*/ +// Handles AI while stunned or otherwise disabled. + +/datum/ai_holder + var/respect_confusion = TRUE // If false, the mob won't wander around recklessly. + +// If our holder is able to do anything. +/datum/ai_holder/proc/can_act() + if(!holder) // Holder missing. + manage_processing(0) + return FALSE + if(holder.stat) // Dead or unconscious. + ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE) + return FALSE + if(holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned in some form. + ai_log("can_act() : Incapacited.", AI_LOG_TRACE) + return FALSE + if(holder.instasis()) // In a stasis field. + ai_log("can_act() : In a stasis field.", AI_LOG_TRACE) + return FALSE + if(!belly_attack) + if(isbelly(holder.loc)) + return FALSE + return TRUE + +// Test if we should switch to STANCE_DISABLE. +// Currently tests for death, stuns, and confusion. +/datum/ai_holder/proc/is_disabled() + if(!can_act()) + return TRUE + if(is_confused()) + return TRUE + return FALSE + +/datum/ai_holder/proc/is_confused() + return holder.confused > 0 && respect_confusion + +// Called by the main loop. +/datum/ai_holder/proc/handle_disabled() + if(!can_act()) + return // Just sit there and take it. + else if(is_confused()) + dangerous_wander() // Let's bump into allies and hit them. + +// Similar to normal wander, but will walk into tiles that are harmful, and attack anything they bump into, including allies. +// Occurs when confused. +/datum/ai_holder/proc/dangerous_wander() + ai_log("dangerous_wander() : Entered.", AI_LOG_DEBUG) + if(isturf(holder.loc) && can_act()) + // Test if we should refrain from falling/attacking allies, if we're smart enough to realize that. + if(intelligence_level > AI_NORMAL) + var/unsafe = FALSE + + tile_test: + for(var/dir_tested in cardinal) + var/turf/turf_tested = get_step(holder, dir_tested) + // Look for unsafe tiles. + if(!turf_tested.is_safe_to_enter(holder)) + unsafe = TRUE + break + + // Look for allies. + for(var/mob/living/L in turf_tested) + if(holder.IIsAlly(L)) + unsafe = TRUE + break tile_test + + + if(unsafe) + ai_log("dangerous_wander() : Staying still due to risk of harm to self or allies.", AI_LOG_TRACE) + return // Just stay still. + + var/moving_to = 0 + moving_to = pick(cardinal) + var/turf/T = get_step(holder, moving_to) + + var/mob/living/L = locate() in T + if(L) + // Attack whoever's on the tile. Even if it's an ally. + ai_log("dangerous_wander() : Going to confuse-attack [L].", AI_LOG_TRACE) + melee_attack(L) + else + // Move to the tile. Even if it's unsafe. + ai_log("dangerous_wander() : Going to confuse-walk to [T] ([T.x],[T.y],[T.z]).", AI_LOG_TRACE) + holder.IMove(T, safety = FALSE) + ai_log("dangerous_wander() : Exited.", AI_LOG_DEBUG) + +/* +// Wanders randomly in cardinal directions. +/datum/ai_holder/proc/handle_wander_movement() + ai_log("handle_wander_movement() : Entered.", AI_LOG_DEBUG) + if(isturf(holder.loc) && can_act()) + wander_delay-- + if(wander_delay <= 0) + if(!wander_when_pulled && holder.pulledby) + ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) + return + + var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. + moving_to = pick(cardinal) + holder.set_dir(moving_to) + holder.IMove(get_step(holder,moving_to)) + wander_delay = base_wander_delay + ai_log("handle_wander_movement() : Exited.", AI_LOG_DEBUG) +*/ diff --git a/code/modules/ai/ai_holder_subtypes/simple_mob_ai.dm b/code/modules/ai/ai_holder_subtypes/simple_mob_ai.dm index 7b1fbcb9baa..c23a4d92e6e 100644 --- a/code/modules/ai/ai_holder_subtypes/simple_mob_ai.dm +++ b/code/modules/ai/ai_holder_subtypes/simple_mob_ai.dm @@ -18,6 +18,7 @@ can_flee = TRUE violent_breakthrough = FALSE base_wander_delay = 8 //vorestation edit, to make pets slow. + belly_attack = FALSE //They already don't fight back, so this ensures that catgirls and similar are still edible when they are spawned as retaliate or aggressive by semi-random mob spawners. // Won't wander away as quickly, ideal for event-spawned mobs like carp or drones. /datum/ai_holder/simple_mob/event @@ -150,6 +151,9 @@ /datum/ai_holder/simple_mob/retaliate/chill base_wander_delay = 8 +/datum/ai_holder/simple_mob/retaliate/edible + belly_attack = FALSE + // Simple mobs that retaliate and support others in their faction who get attacked. /datum/ai_holder/simple_mob/retaliate/cooperative cooperative = TRUE diff --git a/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm index 63b6b748d9b..af911a62c64 100644 --- a/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm +++ b/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm @@ -1,316 +1,316 @@ -// Specialized AI for slime simplemobs. -// Unlike the parent AI code, this will probably break a lot of things if you put it on something that isn't /mob/living/simple_mob/slime/xenobio - -/datum/ai_holder/simple_mob/xenobio_slime - hostile = TRUE - cooperative = TRUE - firing_lanes = TRUE - mauling = TRUE // They need it to get the most out of monkeys. - var/rabid = FALSE // Will attack regardless of discipline. - var/discipline = 0 // Beating slimes makes them less likely to lash out. In theory. - var/resentment = 0 // 'Unjustified' beatings make this go up, and makes it more likely for abused slimes to go rabid. - var/obedience = 0 // Conversely, 'justified' beatings make this go up, and makes discipline decay slower, potentially making it not decay at all. - - var/always_stun = FALSE // If true, the slime will elect to attempt to permastun the target. - - var/last_discipline_decay = null // Last world.time discipline was reduced from decay. - var/discipline_decay_time = 5 SECONDS // Earliest that one discipline can decay. - - var/list/grudges = list() // List of Prometheans who are jerks. - -/datum/ai_holder/simple_mob/xenobio_slime/Destroy() - grudges.Cut() - ..() - -/datum/ai_holder/simple_mob/xenobio_slime/sapphire - always_stun = TRUE // They know that stuns are godly. - intelligence_level = AI_SMART // Also knows not to walk while confused if it risks death. - -/datum/ai_holder/simple_mob/xenobio_slime/light_pink - discipline = 10 - obedience = 10 - -/datum/ai_holder/simple_mob/xenobio_slime/passive/New() // For Kendrick. - ..() - pacify() - -/datum/ai_holder/simple_mob/xenobio_slime/New() - ..() - ASSERT(istype(holder, /mob/living/simple_mob/slime/xenobio)) - -// Checks if disciplining the slime would be 'justified' right now. -/datum/ai_holder/simple_mob/xenobio_slime/proc/is_justified_to_discipline() - ai_log("xenobio_slime/is_justified_to_discipline() : Entered.", AI_LOG_TRACE) - if(!can_act()) - ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be unjustified because we cannot act. Exiting.", AI_LOG_DEBUG) - return FALSE // The slime considers it abuse if they get stunned while already stunned. - if(rabid) - ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be justified because we're rabid. Exiting.", AI_LOG_TRACE) - return TRUE - if(target && can_attack(target)) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(istype(H.species, /datum/species/monkey)) - ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be unjustified because we're targeting a monkey. Exiting.", AI_LOG_DEBUG) - return FALSE // Attacking monkeys is okay. - ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be justified because we are targeting a non-monkey. Exiting.", AI_LOG_TRACE) - return TRUE // Otherwise attacking other things is bad. - ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be unjustified because we are not targeting anything. Exiting.", AI_LOG_DEBUG) - return FALSE // Not attacking anything. - -/datum/ai_holder/simple_mob/xenobio_slime/proc/can_command(mob/living/commander) - if(rabid) - return FALSE - if(!hostile) - return SLIME_COMMAND_OBEY -// if(commander in friends) -// return SLIME_COMMAND_FRIEND - if(holder.IIsAlly(commander)) - return SLIME_COMMAND_FACTION - if(discipline > resentment && obedience >= 5) - return SLIME_COMMAND_OBEY - return FALSE - -/datum/ai_holder/simple_mob/xenobio_slime/proc/adjust_discipline(amount, silent) - var/mob/living/simple_mob/slime/xenobio/my_slime = holder - if(amount > 0) - if(rabid) - return - if(my_slime.untamable) - holder.say("Grrr...") - holder.add_modifier(/datum/modifier/berserk, 30 SECONDS) - enrage() - var/justified = my_slime.is_justified_to_discipline() // This will also consider the AI-side of that proc. - remove_target() // Stop attacking. - - if(justified) - obedience++ - if(!silent) - holder.say(pick("Fine...", "Okay...", "Sorry...", "I yield...", "Mercy...")) - else - if(prob(resentment * 20)) - enrage() - holder.say(pick("Evil...", "Kill...", "Tyrant...")) - else - if(!silent) - holder.say(pick("Why...?", "I don't understand...?", "Cruel...", "Stop...", "Nooo...")) - resentment++ // Done after check so first time will never enrage. - - discipline = between(0, discipline + amount, 10) - my_slime.update_mood() - -/datum/ai_holder/simple_mob/xenobio_slime/handle_special_strategical() - discipline_decay() - evolve_and_reproduce() - -/datum/ai_holder/simple_mob/xenobio_slime/request_help() - if(target) - if(istype(target, /mob/living/simple_mob/slime/xenobio)) //Don't call reinforcements for internal disputes - return - if(istype(target, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = target - if(istype(H.species, /datum/species/monkey)) //Or for food - return - ..() - -// Handles decay of discipline. -/datum/ai_holder/simple_mob/xenobio_slime/proc/discipline_decay() - if(discipline > 0 && last_discipline_decay + discipline_decay_time < world.time) - if(!prob(75 + (obedience * 5))) - adjust_discipline(-1) - last_discipline_decay = world.time - -/datum/ai_holder/simple_mob/xenobio_slime/handle_special_tactic() - evolve_and_reproduce() - -/datum/ai_holder/simple_mob/xenobio_slime/handle_stance_tactical() - if(!istype(holder) || QDELETED(holder)) - qdel(src) - return - ..() - -// Hit the correct verbs to keep the slime species going. -/datum/ai_holder/simple_mob/xenobio_slime/proc/evolve_and_reproduce() - var/mob/living/simple_mob/slime/xenobio/my_slime = holder - if(my_slime.amount_grown >= 10) - // Press the correct verb when we can. - if(my_slime.is_adult) - my_slime.reproduce() // Splits into four new baby slimes. - else - my_slime.evolve() // Turns our holder into an adult slime. - - -// Called when pushed too far (or a red slime core was used). -/datum/ai_holder/simple_mob/xenobio_slime/proc/enrage() - var/mob/living/simple_mob/slime/xenobio/my_slime = holder - if(my_slime.harmless) - return - rabid = TRUE - my_slime.update_mood() - my_slime.visible_message(span("danger", "\The [my_slime] enrages!")) - -// Called to relax from being rabid (when blue slime core was used). -/datum/ai_holder/simple_mob/xenobio_slime/proc/relax() - var/mob/living/simple_mob/slime/xenobio/my_slime = holder - if(my_slime.harmless) - return - if(rabid) - rabid = FALSE - my_slime.update_mood() - my_slime.visible_message(span("danger", "\The [my_slime] calms down.")) - -// Called when using a pacification agent (or it's Kendrick being initalized). -/datum/ai_holder/simple_mob/xenobio_slime/proc/pacify() - remove_target() // So it stops trying to kill them. - rabid = FALSE - hostile = FALSE - retaliate = FALSE - cooperative = FALSE - holder.a_intent = I_HELP - -// The holder's attack changes based on intent. This lets the AI choose what effect is desired. -/datum/ai_holder/simple_mob/xenobio_slime/pre_melee_attack(atom/A) - if(istype(A, /mob/living)) - var/mob/living/L = A - var/mob/living/simple_mob/slime/xenobio/my_slime = holder - - if( (!L.lying && prob(30 + (my_slime.power_charge * 7) ) || (!L.lying && always_stun) )) - my_slime.a_intent = I_DISARM // Stun them first. - else if(my_slime.can_consume(L) && L.lying) - my_slime.a_intent = I_GRAB // Then eat them. - else - my_slime.a_intent = I_HURT // Otherwise robust them. - -/datum/ai_holder/simple_mob/xenobio_slime/closest_distance(atom/movable/AM) - if(istype(AM, /mob/living)) - var/mob/living/L = AM - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(istype(H.species, /datum/species/monkey)) - return 1 // Otherwise ranged slimes will eat a lot less often. - if(L.stat >= UNCONSCIOUS) - return 1 // Melee (eat) the target if dead/dying, don't shoot it. - return ..() - -/datum/ai_holder/simple_mob/xenobio_slime/can_attack(atom/movable/AM, var/vision_required = TRUE) - . = ..() - if(.) // Do some additional checks because we have Special Code(tm). - if(ishuman(AM)) - var/mob/living/carbon/human/H = AM - if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too. - return TRUE // Monkeys are always food (sorry Pun Pun). - else if(H.species && H.species.name == SPECIES_PROMETHEAN) // Prometheans are always our friends. - if(!(H in grudges)) // Unless they're an ass. - return FALSE - if(discipline && !rabid) - holder.a_intent = I_HELP - return FALSE // We're a good slime. - -/datum/ai_holder/simple_mob/xenobio_slime/react_to_attack(atom/movable/attacker, ignore_timers = FALSE) - . = ..(attacker) - - if(ishuman(attacker)) - var/mob/living/carbon/human/H = attacker - if(H.species && H.species.name == SPECIES_PROMETHEAN) // They're a jerk. - grudges |= H - -// Commands, reactions, etc -/datum/ai_holder/simple_mob/xenobio_slime/on_hear_say(mob/living/speaker, message) - ai_log("xenobio_slime/on_hear_say([speaker], [message]) : Entered.", AI_LOG_DEBUG) - var/mob/living/simple_mob/slime/xenobio/my_slime = holder - - if((findtext(message, num2text(my_slime.number)) || findtext(message, my_slime.name) || findtext(message, "slimes"))) // Talking to us. - - // First, make sure it's actually a player saying something and not an AI, or else we risk infinite loops. - if(!speaker.client) - return - - // Are all slimes being referred to? - // var/mass_order = FALSE - // if(findtext(message, "slimes")) - // mass_order = TRUE - - // Say hello back. - if(findtext(message, "hello") || findtext(message, "hi") || findtext(message, "greetings")) - delayed_say(pick("Hello...", "Hi..."), speaker) - - // Follow request. - if(findtext(message, "follow") || findtext(message, "come with me")) - if(!can_command(speaker)) - delayed_say(pick("No...", "I won't follow..."), speaker) - return - - delayed_say("Yes... I follow \the [speaker]...", speaker) - set_follow(speaker) - - // Squish request. - if(findtext(message , "squish")) - if(!can_command(speaker)) - delayed_say("No...", speaker) - return - - spawn(rand(1 SECOND, 2 SECONDS)) - if(!src || !holder || !can_act()) // We might've died/got deleted/etc in the meantime. - return - my_slime.squish() - - - // Stop request. - if(findtext(message, "stop") || findtext(message, "halt") || findtext(message, "cease")) - if(my_slime.victim) // We're being asked to stop eatting someone. - if(!can_command(speaker) || !is_justified_to_discipline()) - delayed_say("No...", speaker) - return - else - delayed_say("Fine...", speaker) - adjust_discipline(1, TRUE) - my_slime.stop_consumption() - - if(target) // We're being asked to stop chasing someone. - if(!can_command(speaker) || !is_justified_to_discipline()) - delayed_say("No...", speaker) - return - else - delayed_say("Fine...", speaker) - adjust_discipline(1, TRUE) // This must come before losing the target or it will be unjustified. - remove_target() - - - if(leader) // We're being asked to stop following someone. - if(can_command(speaker) == SLIME_COMMAND_FRIEND || leader == speaker) - delayed_say("Yes... I'll stop...", speaker) - lose_follow() - else - delayed_say("No... I'll keep following \the [leader]...", speaker) - - /* // Commented out since its mostly useless now due to slimes refusing to attack if it would make them naughty. - // Murder request - if(findtext(message, "harm") || findtext(message, "attack") || findtext(message, "kill") || findtext(message, "murder") || findtext(message, "eat") || findtext(message, "consume") || findtext(message, "absorb")) - if(can_command(speaker) < SLIME_COMMAND_FACTION) - delayed_say("No...", speaker) - return - - for(var/mob/living/L in view(7, my_slime) - list(my_slime, speaker)) - if(L == src) - continue // Don't target ourselves. - var/list/valid_names = splittext(L.name, " ") // Should output list("John", "Doe") as an example. - for(var/line in valid_names) // Check each part of someone's name. - if(findtext(message, lowertext(line))) // If part of someone's name is in the command, the slime targets them if allowed to. - if(!(mass_order && line == "slime")) //don't think random other slimes are target - if(can_attack(L)) - delayed_say("Okay... I attack \the [L]...", speaker) - give_target(L) - return - else - delayed_say("No... I won't attack \the [L].", speaker) - return - - // If we're here, it couldn't find anyone with that name. - delayed_say("No... I don't know who to attack...", speaker) - */ - ai_log("xenobio_slime/on_hear_say() : Exited.", AI_LOG_DEBUG) - -/datum/ai_holder/simple_mob/xenobio_slime/can_violently_breakthrough() - if(discipline && !rabid) // Good slimes don't shatter the windows because their buddy in an adjacent cell decided to piss off Slimesky. - return FALSE - return ..() +// Specialized AI for slime simplemobs. +// Unlike the parent AI code, this will probably break a lot of things if you put it on something that isn't /mob/living/simple_mob/slime/xenobio + +/datum/ai_holder/simple_mob/xenobio_slime + hostile = TRUE + cooperative = TRUE + firing_lanes = TRUE + mauling = TRUE // They need it to get the most out of monkeys. + var/rabid = FALSE // Will attack regardless of discipline. + var/discipline = 0 // Beating slimes makes them less likely to lash out. In theory. + var/resentment = 0 // 'Unjustified' beatings make this go up, and makes it more likely for abused slimes to go rabid. + var/obedience = 0 // Conversely, 'justified' beatings make this go up, and makes discipline decay slower, potentially making it not decay at all. + + var/always_stun = FALSE // If true, the slime will elect to attempt to permastun the target. + + var/last_discipline_decay = null // Last world.time discipline was reduced from decay. + var/discipline_decay_time = 5 SECONDS // Earliest that one discipline can decay. + + var/list/grudges = list() // List of Prometheans who are jerks. + +/datum/ai_holder/simple_mob/xenobio_slime/Destroy() + grudges.Cut() + ..() + +/datum/ai_holder/simple_mob/xenobio_slime/sapphire + always_stun = TRUE // They know that stuns are godly. + intelligence_level = AI_SMART // Also knows not to walk while confused if it risks death. + +/datum/ai_holder/simple_mob/xenobio_slime/light_pink + discipline = 10 + obedience = 10 + +/datum/ai_holder/simple_mob/xenobio_slime/passive/New() // For Kendrick. + ..() + pacify() + +/datum/ai_holder/simple_mob/xenobio_slime/New() + ..() + ASSERT(istype(holder, /mob/living/simple_mob/slime/xenobio)) + +// Checks if disciplining the slime would be 'justified' right now. +/datum/ai_holder/simple_mob/xenobio_slime/proc/is_justified_to_discipline() + ai_log("xenobio_slime/is_justified_to_discipline() : Entered.", AI_LOG_TRACE) + if(!can_act()) + ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be unjustified because we cannot act. Exiting.", AI_LOG_DEBUG) + return FALSE // The slime considers it abuse if they get stunned while already stunned. + if(rabid) + ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be justified because we're rabid. Exiting.", AI_LOG_TRACE) + return TRUE + if(target && can_attack(target)) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(istype(H.species, /datum/species/monkey)) + ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be unjustified because we're targeting a monkey. Exiting.", AI_LOG_DEBUG) + return FALSE // Attacking monkeys is okay. + ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be justified because we are targeting a non-monkey. Exiting.", AI_LOG_TRACE) + return TRUE // Otherwise attacking other things is bad. + ai_log("xenobio_slime/is_justified_to_discipline() : Judged to be unjustified because we are not targeting anything. Exiting.", AI_LOG_DEBUG) + return FALSE // Not attacking anything. + +/datum/ai_holder/simple_mob/xenobio_slime/proc/can_command(mob/living/commander) + if(rabid) + return FALSE + if(!hostile) + return SLIME_COMMAND_OBEY +// if(commander in friends) +// return SLIME_COMMAND_FRIEND + if(holder.IIsAlly(commander)) + return SLIME_COMMAND_FACTION + if(discipline > resentment && obedience >= 5) + return SLIME_COMMAND_OBEY + return FALSE + +/datum/ai_holder/simple_mob/xenobio_slime/proc/adjust_discipline(amount, silent) + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(amount > 0) + if(rabid) + return + if(my_slime.untamable) + holder.say("Grrr...") + holder.add_modifier(/datum/modifier/berserk, 30 SECONDS) + enrage() + var/justified = my_slime.is_justified_to_discipline() // This will also consider the AI-side of that proc. + remove_target() // Stop attacking. + + if(justified) + obedience++ + if(!silent) + holder.say(pick("Fine...", "Okay...", "Sorry...", "I yield...", "Mercy...")) + else + if(prob(resentment * 20)) + enrage() + holder.say(pick("Evil...", "Kill...", "Tyrant...")) + else + if(!silent) + holder.say(pick("Why...?", "I don't understand...?", "Cruel...", "Stop...", "Nooo...")) + resentment++ // Done after check so first time will never enrage. + + discipline = between(0, discipline + amount, 10) + my_slime.update_mood() + +/datum/ai_holder/simple_mob/xenobio_slime/handle_special_strategical() + discipline_decay() + evolve_and_reproduce() + +/datum/ai_holder/simple_mob/xenobio_slime/request_help() + if(target) + if(istype(target, /mob/living/simple_mob/slime/xenobio)) //Don't call reinforcements for internal disputes + return + if(istype(target, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = target + if(istype(H.species, /datum/species/monkey)) //Or for food + return + ..() + +// Handles decay of discipline. +/datum/ai_holder/simple_mob/xenobio_slime/proc/discipline_decay() + if(discipline > 0 && last_discipline_decay + discipline_decay_time < world.time) + if(!prob(75 + (obedience * 5))) + adjust_discipline(-1) + last_discipline_decay = world.time + +/datum/ai_holder/simple_mob/xenobio_slime/handle_special_tactic() + evolve_and_reproduce() + +/datum/ai_holder/simple_mob/xenobio_slime/handle_stance_tactical() + if(!istype(holder) || QDELETED(holder)) + qdel(src) + return + ..() + +// Hit the correct verbs to keep the slime species going. +/datum/ai_holder/simple_mob/xenobio_slime/proc/evolve_and_reproduce() + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(my_slime.amount_grown >= 10) + // Press the correct verb when we can. + if(my_slime.is_adult) + my_slime.reproduce() // Splits into four new baby slimes. + else + my_slime.evolve() // Turns our holder into an adult slime. + + +// Called when pushed too far (or a red slime core was used). +/datum/ai_holder/simple_mob/xenobio_slime/proc/enrage() + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(my_slime.harmless) + return + rabid = TRUE + my_slime.update_mood() + my_slime.visible_message(span("danger", "\The [my_slime] enrages!")) + +// Called to relax from being rabid (when blue slime core was used). +/datum/ai_holder/simple_mob/xenobio_slime/proc/relax() + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(my_slime.harmless) + return + if(rabid) + rabid = FALSE + my_slime.update_mood() + my_slime.visible_message(span("danger", "\The [my_slime] calms down.")) + +// Called when using a pacification agent (or it's Kendrick being initalized). +/datum/ai_holder/simple_mob/xenobio_slime/proc/pacify() + remove_target() // So it stops trying to kill them. + rabid = FALSE + hostile = FALSE + retaliate = FALSE + cooperative = FALSE + holder.a_intent = I_HELP + +// The holder's attack changes based on intent. This lets the AI choose what effect is desired. +/datum/ai_holder/simple_mob/xenobio_slime/pre_melee_attack(atom/A) + if(istype(A, /mob/living)) + var/mob/living/L = A + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + + if( (!L.lying && prob(30 + (my_slime.power_charge * 7) ) || (!L.lying && always_stun) )) + my_slime.a_intent = I_DISARM // Stun them first. + else if(my_slime.can_consume(L) && L.lying) + my_slime.a_intent = I_GRAB // Then eat them. + else + my_slime.a_intent = I_HURT // Otherwise robust them. + +/datum/ai_holder/simple_mob/xenobio_slime/closest_distance(atom/movable/AM) + if(istype(AM, /mob/living)) + var/mob/living/L = AM + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(istype(H.species, /datum/species/monkey)) + return 1 // Otherwise ranged slimes will eat a lot less often. + if(L.stat >= UNCONSCIOUS) + return 1 // Melee (eat) the target if dead/dying, don't shoot it. + return ..() + +/datum/ai_holder/simple_mob/xenobio_slime/can_attack(atom/movable/AM, var/vision_required = TRUE) + . = ..() + if(.) // Do some additional checks because we have Special Code(tm). + if(ishuman(AM)) + var/mob/living/carbon/human/H = AM + if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too. + return TRUE // Monkeys are always food (sorry Pun Pun). + else if(H.species && H.species.name == SPECIES_PROMETHEAN) // Prometheans are always our friends. + if(!(H in grudges)) // Unless they're an ass. + return FALSE + if(discipline && !rabid) + holder.a_intent = I_HELP + return FALSE // We're a good slime. + +/datum/ai_holder/simple_mob/xenobio_slime/react_to_attack(atom/movable/attacker, ignore_timers = FALSE) + . = ..(attacker) + + if(ishuman(attacker)) + var/mob/living/carbon/human/H = attacker + if(H.species && H.species.name == SPECIES_PROMETHEAN) // They're a jerk. + grudges |= H + +// Commands, reactions, etc +/datum/ai_holder/simple_mob/xenobio_slime/on_hear_say(mob/living/speaker, message) + ai_log("xenobio_slime/on_hear_say([speaker], [message]) : Entered.", AI_LOG_DEBUG) + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + + if((findtext(message, num2text(my_slime.number)) || findtext(message, my_slime.name) || findtext(message, "slimes"))) // Talking to us. + + // First, make sure it's actually a player saying something and not an AI, or else we risk infinite loops. + if(!speaker.client) + return + + // Are all slimes being referred to? + // var/mass_order = FALSE + // if(findtext(message, "slimes")) + // mass_order = TRUE + + // Say hello back. + if(findtext(message, "hello") || findtext(message, "hi") || findtext(message, "greetings")) + delayed_say(pick("Hello...", "Hi..."), speaker) + + // Follow request. + if(findtext(message, "follow") || findtext(message, "come with me")) + if(!can_command(speaker)) + delayed_say(pick("No...", "I won't follow..."), speaker) + return + + delayed_say("Yes... I follow \the [speaker]...", speaker) + set_follow(speaker) + + // Squish request. + if(findtext(message , "squish")) + if(!can_command(speaker)) + delayed_say("No...", speaker) + return + + spawn(rand(1 SECOND, 2 SECONDS)) + if(!src || !holder || !can_act()) // We might've died/got deleted/etc in the meantime. + return + my_slime.squish() + + + // Stop request. + if(findtext(message, "stop") || findtext(message, "halt") || findtext(message, "cease")) + if(my_slime.victim) // We're being asked to stop eatting someone. + if(!can_command(speaker) || !is_justified_to_discipline()) + delayed_say("No...", speaker) + return + else + delayed_say("Fine...", speaker) + adjust_discipline(1, TRUE) + my_slime.stop_consumption() + + if(target) // We're being asked to stop chasing someone. + if(!can_command(speaker) || !is_justified_to_discipline()) + delayed_say("No...", speaker) + return + else + delayed_say("Fine...", speaker) + adjust_discipline(1, TRUE) // This must come before losing the target or it will be unjustified. + remove_target() + + + if(leader) // We're being asked to stop following someone. + if(can_command(speaker) == SLIME_COMMAND_FRIEND || leader == speaker) + delayed_say("Yes... I'll stop...", speaker) + lose_follow() + else + delayed_say("No... I'll keep following \the [leader]...", speaker) + + /* // Commented out since its mostly useless now due to slimes refusing to attack if it would make them naughty. + // Murder request + if(findtext(message, "harm") || findtext(message, "attack") || findtext(message, "kill") || findtext(message, "murder") || findtext(message, "eat") || findtext(message, "consume") || findtext(message, "absorb")) + if(can_command(speaker) < SLIME_COMMAND_FACTION) + delayed_say("No...", speaker) + return + + for(var/mob/living/L in view(7, my_slime) - list(my_slime, speaker)) + if(L == src) + continue // Don't target ourselves. + var/list/valid_names = splittext(L.name, " ") // Should output list("John", "Doe") as an example. + for(var/line in valid_names) // Check each part of someone's name. + if(findtext(message, lowertext(line))) // If part of someone's name is in the command, the slime targets them if allowed to. + if(!(mass_order && line == "slime")) //don't think random other slimes are target + if(can_attack(L)) + delayed_say("Okay... I attack \the [L]...", speaker) + give_target(L) + return + else + delayed_say("No... I won't attack \the [L].", speaker) + return + + // If we're here, it couldn't find anyone with that name. + delayed_say("No... I don't know who to attack...", speaker) + */ + ai_log("xenobio_slime/on_hear_say() : Exited.", AI_LOG_DEBUG) + +/datum/ai_holder/simple_mob/xenobio_slime/can_violently_breakthrough() + if(discipline && !rabid) // Good slimes don't shatter the windows because their buddy in an adjacent cell decided to piss off Slimesky. + return FALSE + return ..() diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index 835bd36fe95..8c0a38b4ff0 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -9,6 +9,7 @@ var/vore_hostile = FALSE // The same as hostile, but with vore pref checks var/micro_hunt = FALSE // Will target mobs at or under the micro_hunt_size size, requires vore_hostile to be true var/micro_hunt_size = 0.25 + var/belly_attack = TRUE //Mobs attack if they are in a belly! var/atom/movable/target = null // The thing (mob or object) we're trying to kill. var/atom/movable/preferred_target = null// If set, and if given the chance, we will always prefer to target this over other options. @@ -123,7 +124,9 @@ ai_log("can_attack() : Entering.", AI_LOG_TRACE) if(!can_see_target(the_target) && vision_required) return FALSE - + if(!belly_attack) + if(isbelly(holder.loc)) + return FALSE if(isliving(the_target)) var/mob/living/L = the_target if(ishuman(L) || issilicon(L)) @@ -266,6 +269,9 @@ if(!hostile && !retaliate) // Not allowed to defend ourselves. ai_log("react_to_attack() : Was attacked by [attacker], but we are not allowed to attack back.", AI_LOG_TRACE) return FALSE + if(!belly_attack) + if(isbelly(holder.loc)) + return FALSE if(holder.IIsAlly(attacker)) // I'll overlook it THIS time... ai_log("react_to_attack() : Was attacked by [attacker], but they were an ally.", AI_LOG_TRACE) return FALSE diff --git a/code/modules/ai/ai_holder_targeting_vr.dm b/code/modules/ai/ai_holder_targeting_vr.dm index 0828e1b468b..21283904f22 100644 --- a/code/modules/ai/ai_holder_targeting_vr.dm +++ b/code/modules/ai/ai_holder_targeting_vr.dm @@ -11,6 +11,9 @@ forgive_resting = TRUE cooperative = FALSE +/datum/ai_holder/simple_mob/vore/edible + belly_attack = FALSE + /datum/ai_holder/simple_mob/vore/micro_hunter micro_hunt = TRUE micro_hunt_size = 0.8 diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm index e5a958873d0..3ad1df433f9 100644 --- a/code/modules/alarm/alarm.dm +++ b/code/modules/alarm/alarm.dm @@ -1,137 +1,137 @@ -#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone? - -/datum/alarm_source - var/source = null // The source trigger - var/source_name = "" // The name of the source should it be lost (for example a destroyed camera) - var/duration = 0 // How long this source will be alarming, 0 for indefinetely. - var/severity = 1 // How severe the alarm from this source is. - var/start_time = 0 // When this source began alarming. - var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost. - -/datum/alarm_source/New(var/atom/source) - src.source = source - start_time = world.time - source_name = source.get_source_name() - -/datum/alarm - var/atom/origin //Used to identify the alarm area. - var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared. - var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source. - var/list/cameras //List of cameras that can be switched to, if the player has that capability. - var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera). - var/area/last_name //The last acquired name, used should origin be lost - var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated. - var/end_time //Used to set when this alarm should clear, in case the origin is lost. - var/hidden = FALSE //If this alarm can be seen from consoles or other things. - -/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity, var/hidden) - src.origin = origin - - cameras() // Sets up both cameras and last alarm area. - set_source_data(source, duration, severity, hidden) - -/datum/alarm/process() - // Has origin gone missing? - if(!origin && !end_time) - end_time = world.time + ALARM_RESET_DELAY - for(var/datum/alarm_source/AS in sources) - // Has the alarm passed its best before date? - if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration))) - sources -= AS - // Has the source gone missing? Then reset the normal duration and set end_time - if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely. - AS.duration = 0 - AS.end_time = world.time + ALARM_RESET_DELAY - -/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity, var/hidden) - var/datum/alarm_source/AS = sources_assoc[source] - if(!AS) - AS = new/datum/alarm_source(source) - sources += AS - sources_assoc[source] = AS - src.hidden = hidden - // Currently only non-0 durations can be altered (normal alarms VS EMP blasts) - if(AS.duration) - duration = SecondsToTicks(duration) - AS.duration = duration - AS.severity = severity - src.hidden = min(src.hidden, hidden) - -/datum/alarm/proc/clear(var/source) - var/datum/alarm_source/AS = sources_assoc[source] - sources -= AS - sources_assoc -= source - -/datum/alarm/proc/alarm_area() - if(!origin) - return last_area - - last_area = origin.get_alarm_area() - return last_area - -/datum/alarm/proc/alarm_name() - if(!origin) - return last_name - - last_name = origin.get_alarm_name() - return last_name - -/datum/alarm/proc/cameras() - // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras - if(cameras && (last_camera_area != alarm_area())) - cameras = null - - if(!cameras) - cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras() - - last_camera_area = last_area - return cameras - -/datum/alarm/proc/max_severity() - var/max_severity = 0 - for(var/datum/alarm_source/AS in sources) - max_severity = max(AS.severity, max_severity) - - return max_severity - -/****************** -* Assisting procs * -******************/ -/atom/proc/get_alarm_area() - return get_area(src) - -/area/get_alarm_area() - return src - -/atom/proc/get_alarm_name() - var/area/A = get_area(src) - return A.name - -/area/get_alarm_name() - return name - -/mob/get_alarm_name() - return name - -/atom/proc/get_source_name() - return name - -/obj/machinery/camera/get_source_name() - return c_tag - -/atom/proc/get_alarm_cameras() - var/area/A = get_area(src) - return A.get_cameras() - -/area/get_alarm_cameras() - return get_cameras() - -/mob/living/silicon/robot/get_alarm_cameras() - var/list/cameras = ..() - if(camera) - cameras += camera - - return cameras - -/mob/living/silicon/robot/syndicate/get_alarm_cameras() - return list() +#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone? + +/datum/alarm_source + var/source = null // The source trigger + var/source_name = "" // The name of the source should it be lost (for example a destroyed camera) + var/duration = 0 // How long this source will be alarming, 0 for indefinetely. + var/severity = 1 // How severe the alarm from this source is. + var/start_time = 0 // When this source began alarming. + var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost. + +/datum/alarm_source/New(var/atom/source) + src.source = source + start_time = world.time + source_name = source.get_source_name() + +/datum/alarm + var/atom/origin //Used to identify the alarm area. + var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared. + var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source. + var/list/cameras //List of cameras that can be switched to, if the player has that capability. + var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera). + var/area/last_name //The last acquired name, used should origin be lost + var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated. + var/end_time //Used to set when this alarm should clear, in case the origin is lost. + var/hidden = FALSE //If this alarm can be seen from consoles or other things. + +/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity, var/hidden) + src.origin = origin + + cameras() // Sets up both cameras and last alarm area. + set_source_data(source, duration, severity, hidden) + +/datum/alarm/process() + // Has origin gone missing? + if(!origin && !end_time) + end_time = world.time + ALARM_RESET_DELAY + for(var/datum/alarm_source/AS in sources) + // Has the alarm passed its best before date? + if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration))) + sources -= AS + // Has the source gone missing? Then reset the normal duration and set end_time + if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely. + AS.duration = 0 + AS.end_time = world.time + ALARM_RESET_DELAY + +/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity, var/hidden) + var/datum/alarm_source/AS = sources_assoc[source] + if(!AS) + AS = new/datum/alarm_source(source) + sources += AS + sources_assoc[source] = AS + src.hidden = hidden + // Currently only non-0 durations can be altered (normal alarms VS EMP blasts) + if(AS.duration) + duration = SecondsToTicks(duration) + AS.duration = duration + AS.severity = severity + src.hidden = min(src.hidden, hidden) + +/datum/alarm/proc/clear(var/source) + var/datum/alarm_source/AS = sources_assoc[source] + sources -= AS + sources_assoc -= source + +/datum/alarm/proc/alarm_area() + if(!origin) + return last_area + + last_area = origin.get_alarm_area() + return last_area + +/datum/alarm/proc/alarm_name() + if(!origin) + return last_name + + last_name = origin.get_alarm_name() + return last_name + +/datum/alarm/proc/cameras() + // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras + if(cameras && (last_camera_area != alarm_area())) + cameras = null + + if(!cameras) + cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras() + + last_camera_area = last_area + return cameras + +/datum/alarm/proc/max_severity() + var/max_severity = 0 + for(var/datum/alarm_source/AS in sources) + max_severity = max(AS.severity, max_severity) + + return max_severity + +/****************** +* Assisting procs * +******************/ +/atom/proc/get_alarm_area() + return get_area(src) + +/area/get_alarm_area() + return src + +/atom/proc/get_alarm_name() + var/area/A = get_area(src) + return A.name + +/area/get_alarm_name() + return name + +/mob/get_alarm_name() + return name + +/atom/proc/get_source_name() + return name + +/obj/machinery/camera/get_source_name() + return c_tag + +/atom/proc/get_alarm_cameras() + var/area/A = get_area(src) + return A.get_cameras() + +/area/get_alarm_cameras() + return get_cameras() + +/mob/living/silicon/robot/get_alarm_cameras() + var/list/cameras = ..() + if(camera) + cameras += camera + + return cameras + +/mob/living/silicon/robot/syndicate/get_alarm_cameras() + return list() diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index cbec4c25b68..4064d700d2e 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -1,116 +1,116 @@ -#define ALARM_RAISED 1 -#define ALARM_CLEARED 0 - -/datum/alarm_handler - var/category = "" - var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms - var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin. - var/list/listeners = new // A list of all objects interested in alarm changes. - -/datum/alarm_handler/process() - for(var/datum/alarm/A in alarms) - A.process() - check_alarm_cleared(A) - -/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1, var/hidden = 0) - var/new_alarm - //Proper origin and source mandatory - if(!(origin && source)) - return - origin = origin.get_alarm_origin() - - new_alarm = 0 - //see if there is already an alarm of this origin - var/datum/alarm/existing = alarms_assoc[origin] - if(existing) - existing.set_source_data(source, duration, severity, hidden) - else - existing = new/datum/alarm(origin, source, duration, severity, hidden) - new_alarm = 1 - - alarms |= existing - alarms_assoc[origin] = existing - if(new_alarm) - alarms = dd_sortedObjectList(alarms) - on_alarm_change(existing, ALARM_RAISED) - - return new_alarm - -/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source) - //Proper origin and source mandatory - if(!(origin && source)) - return - origin = origin.get_alarm_origin() - - var/datum/alarm/existing = alarms_assoc[origin] - if(existing) - existing.clear(source) - return check_alarm_cleared(existing) - -/datum/alarm_handler/proc/major_alarms(var/z) - return visible_alarms(z) - -/datum/alarm_handler/proc/has_major_alarms(var/z) - if(!LAZYLEN(alarms)) - return 0 - - return LAZYLEN(major_alarms(z)) - -/datum/alarm_handler/proc/minor_alarms(var/z) - return visible_alarms(z) - -/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm) - if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) - alarms -= alarm - alarms_assoc -= alarm.origin - on_alarm_change(alarm, ALARM_CLEARED) - return 1 - return 0 - -/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - for(var/obj/machinery/camera/C in alarm.cameras()) - if(was_raised && !alarm.hidden) - C.add_network(category) - else - C.remove_network(category) - notify_listeners(alarm, was_raised) - -/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin) - if(!origin) - return - - origin = origin.get_alarm_origin() - var/datum/alarm/existing = alarms_assoc[origin] - if(!existing) - return - - return existing.max_severity() - -/atom/proc/get_alarm_origin() - return src - -/turf/get_alarm_origin() - return get_area(src) - -/datum/alarm_handler/proc/register_alarm(var/object, var/procName) - listeners[object] = procName - -/datum/alarm_handler/proc/unregister_alarm(var/object) - listeners -= object - -/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised) - for(var/listener in listeners) - call(listener, listeners[listener])(src, alarm, was_raised) - -/datum/alarm_handler/proc/visible_alarms(var/z) - if(!LAZYLEN(alarms)) - return list() - - var/list/map_levels = using_map.get_map_levels(z) - - var/list/visible_alarms = new() - for(var/datum/alarm/A in alarms) - if(A.hidden || (z && !(A.origin?.z in map_levels))) - continue - visible_alarms.Add(A) +#define ALARM_RAISED 1 +#define ALARM_CLEARED 0 + +/datum/alarm_handler + var/category = "" + var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms + var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin. + var/list/listeners = new // A list of all objects interested in alarm changes. + +/datum/alarm_handler/process() + for(var/datum/alarm/A in alarms) + A.process() + check_alarm_cleared(A) + +/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1, var/hidden = 0) + var/new_alarm + //Proper origin and source mandatory + if(!(origin && source)) + return + origin = origin.get_alarm_origin() + + new_alarm = 0 + //see if there is already an alarm of this origin + var/datum/alarm/existing = alarms_assoc[origin] + if(existing) + existing.set_source_data(source, duration, severity, hidden) + else + existing = new/datum/alarm(origin, source, duration, severity, hidden) + new_alarm = 1 + + alarms |= existing + alarms_assoc[origin] = existing + if(new_alarm) + alarms = dd_sortedObjectList(alarms) + on_alarm_change(existing, ALARM_RAISED) + + return new_alarm + +/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source) + //Proper origin and source mandatory + if(!(origin && source)) + return + origin = origin.get_alarm_origin() + + var/datum/alarm/existing = alarms_assoc[origin] + if(existing) + existing.clear(source) + return check_alarm_cleared(existing) + +/datum/alarm_handler/proc/major_alarms(var/z) + return visible_alarms(z) + +/datum/alarm_handler/proc/has_major_alarms(var/z) + if(!LAZYLEN(alarms)) + return 0 + + return LAZYLEN(major_alarms(z)) + +/datum/alarm_handler/proc/minor_alarms(var/z) + return visible_alarms(z) + +/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm) + if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) + alarms -= alarm + alarms_assoc -= alarm.origin + on_alarm_change(alarm, ALARM_CLEARED) + return 1 + return 0 + +/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + for(var/obj/machinery/camera/C in alarm.cameras()) + if(was_raised && !alarm.hidden) + C.add_network(category) + else + C.remove_network(category) + notify_listeners(alarm, was_raised) + +/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin) + if(!origin) + return + + origin = origin.get_alarm_origin() + var/datum/alarm/existing = alarms_assoc[origin] + if(!existing) + return + + return existing.max_severity() + +/atom/proc/get_alarm_origin() + return src + +/turf/get_alarm_origin() + return get_area(src) + +/datum/alarm_handler/proc/register_alarm(var/object, var/procName) + listeners[object] = procName + +/datum/alarm_handler/proc/unregister_alarm(var/object) + listeners -= object + +/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised) + for(var/listener in listeners) + call(listener, listeners[listener])(src, alarm, was_raised) + +/datum/alarm_handler/proc/visible_alarms(var/z) + if(!LAZYLEN(alarms)) + return list() + + var/list/map_levels = using_map.get_map_levels(z) + + var/list/visible_alarms = new() + for(var/datum/alarm/A in alarms) + if(A.hidden || (z && !(A.origin?.z in map_levels))) + continue + visible_alarms.Add(A) return visible_alarms \ No newline at end of file diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm index be97493afd4..ecb1b1bafad 100644 --- a/code/modules/alarm/atmosphere_alarm.dm +++ b/code/modules/alarm/atmosphere_alarm.dm @@ -1,29 +1,29 @@ -/datum/alarm_handler/atmosphere - category = "Atmosphere Alarms" - -/datum/alarm_handler/atmosphere/major_alarms(var/z) - var/list/major_alarms = new() - var/list/map_levels = using_map.get_map_levels(z) - for(var/datum/alarm/A in visible_alarms()) - if(z && !(A.origin?.z in map_levels)) - continue - if(A.max_severity() > 1) - major_alarms.Add(A) - return major_alarms - -/datum/alarm_handler/atmosphere/minor_alarms(var/z) - var/list/minor_alarms = new() - var/list/map_levels = using_map.get_map_levels(z) - for(var/datum/alarm/A in visible_alarms()) - if(z && !(A.origin?.z in map_levels)) - continue - if(A.max_severity() == 1) - minor_alarms.Add(A) - return minor_alarms - -//VOREStation Add - Alarm for AR glasses -/*/datum/alarm_handler/atmosphere/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - ..() - var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() - broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ +/datum/alarm_handler/atmosphere + category = "Atmosphere Alarms" + +/datum/alarm_handler/atmosphere/major_alarms(var/z) + var/list/major_alarms = new() + var/list/map_levels = using_map.get_map_levels(z) + for(var/datum/alarm/A in visible_alarms()) + if(z && !(A.origin?.z in map_levels)) + continue + if(A.max_severity() > 1) + major_alarms.Add(A) + return major_alarms + +/datum/alarm_handler/atmosphere/minor_alarms(var/z) + var/list/minor_alarms = new() + var/list/map_levels = using_map.get_map_levels(z) + for(var/datum/alarm/A in visible_alarms()) + if(z && !(A.origin?.z in map_levels)) + continue + if(A.max_severity() == 1) + minor_alarms.Add(A) + return minor_alarms + +//VOREStation Add - Alarm for AR glasses +/*/datum/alarm_handler/atmosphere/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + ..() + var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() + broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ //VOREStation Add End \ No newline at end of file diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm index 9594a1c8a08..bef53ad466f 100644 --- a/code/modules/alarm/camera_alarm.dm +++ b/code/modules/alarm/camera_alarm.dm @@ -1,2 +1,2 @@ -/datum/alarm_handler/camera - category = "Camera Alarms" +/datum/alarm_handler/camera + category = "Camera Alarms" diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm index 4794d62b5ab..03cdadde043 100644 --- a/code/modules/alarm/fire_alarm.dm +++ b/code/modules/alarm/fire_alarm.dm @@ -1,15 +1,15 @@ -/datum/alarm_handler/fire - category = "Fire Alarms" - -/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - var/area/A = alarm.origin - if(istype(A)) - if(was_raised) - A.fire_alert() - else - A.fire_reset() - //VOREStation Add - Alarm for AR glasses uses - /*var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() - broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ - //VOREStation Add End - ..() +/datum/alarm_handler/fire + category = "Fire Alarms" + +/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + var/area/A = alarm.origin + if(istype(A)) + if(was_raised) + A.fire_alert() + else + A.fire_reset() + //VOREStation Add - Alarm for AR glasses uses + /*var/atom/source = length(alarm.sources_assoc) ? alarm.sources_assoc[1] : alarm.alarm_area() + broadcast_engineering_hud_message("Alarm in [alarm.origin] [was_raised ? "raised!" : "cleared."]", source)*/ + //VOREStation Add End + ..() diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm index cafc7c128dc..fd7e6febe48 100644 --- a/code/modules/alarm/motion_alarm.dm +++ b/code/modules/alarm/motion_alarm.dm @@ -1,2 +1,2 @@ -/datum/alarm_handler/motion - category = "Motion Alarms" +/datum/alarm_handler/motion + category = "Motion Alarms" diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm index 2df6d1eab3f..4a0947a8f94 100644 --- a/code/modules/alarm/power_alarm.dm +++ b/code/modules/alarm/power_alarm.dm @@ -1,10 +1,10 @@ -/datum/alarm_handler/power - category = "Power Alarms" - -/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - var/area/A = alarm.origin - if(istype(A)) - A.power_alert(was_raised) - ..() - -/area/proc/power_alert(var/alarming) +/datum/alarm_handler/power + category = "Power Alarms" + +/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised) + var/area/A = alarm.origin + if(istype(A)) + A.power_alert(was_raised) + ..() + +/area/proc/power_alert(var/alarming) diff --git a/code/modules/artifice/cursedform.dm b/code/modules/artifice/cursedform.dm index 43acac8bf3d..ee994924c8e 100644 --- a/code/modules/artifice/cursedform.dm +++ b/code/modules/artifice/cursedform.dm @@ -31,7 +31,7 @@ qdel(src) else - to_chat(user,"You must hold \the [P] steady to burn \the [src].") + to_chat(user, span_red("You must hold \the [P] steady to burn \the [src].")) if(isliving(user)) var/mob/living/L = user diff --git a/code/modules/artifice/deadringer.dm b/code/modules/artifice/deadringer.dm index d0774dc2a57..ed4e184543d 100644 --- a/code/modules/artifice/deadringer.dm +++ b/code/modules/artifice/deadringer.dm @@ -32,20 +32,20 @@ /obj/item/weapon/deadringer/attack_self(var/mob/living/user as mob) var/mob/living/H = src.loc if (!istype(H, /mob/living/carbon/human)) - to_chat(H,"You have no clue what to do with this thing.") + to_chat(H, span_blue("You have no clue what to do with this thing.")) return if(!activated) if(timer == 0) - to_chat(H, "You press a small button on [src]'s side. It starts to hum quietly.") + to_chat(H, span_blue("You press a small button on [src]'s side. It starts to hum quietly.")) bruteloss_prev = H.getBruteLoss() fireloss_prev = H.getFireLoss() activated = 1 return else - to_chat(H,"You press a small button on [src]'s side. It buzzes a little.") + to_chat(H, span_blue("You press a small button on [src]'s side. It buzzes a little.")) return if(activated) - to_chat(H,"You press a small button on [src]'s side. It stops humming.") + to_chat(H, span_blue("You press a small button on [src]'s side. It stops humming.")) activated = 0 return @@ -58,9 +58,9 @@ deathprevent() activated = 0 if(watchowner.isSynthetic()) - to_chat(watchowner, "You fade into nothingness! [src]'s screen blinks, being unable to copy your synthetic body!") + to_chat(watchowner, span_blue("You fade into nothingness! [src]'s screen blinks, being unable to copy your synthetic body!")) else - to_chat(watchowner, "You fade into nothingness, leaving behind a fake body!") + to_chat(watchowner, span_blue("You fade into nothingness, leaving behind a fake body!")) icon_state = "deadringer_cd" timer = 50 return diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 1a0b58bdc93..5c1ea9dd9d6 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,108 +1,108 @@ -/obj/item/device/assembly - name = "assembly" - desc = "A small electronic device that should never exist." - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "" - w_class = ITEMSIZE_SMALL - matter = list(MAT_STEEL = 100) - throwforce = 2 - throw_speed = 3 - throw_range = 10 - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - origin_tech = list(TECH_MAGNET = 1) - - var/secured = 1 - var/list/attached_overlays = null - var/obj/item/device/assembly_holder/holder = null - var/cooldown = FALSE //To prevent spam - var/wires = WIRE_RECEIVE | WIRE_PULSE - - var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() - var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder - var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly - var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() - var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message - -/obj/item/device/assembly/proc/holder_movement() - return - -/obj/item/device/assembly/proc/process_cooldown() - if(cooldown) - return FALSE - cooldown = TRUE - VARSET_IN(src, cooldown, FALSE, 2 SECONDS) - return TRUE - -/obj/item/device/assembly/proc/pulsed(var/radio = 0) - if(holder && (wires & WIRE_RECEIVE)) - activate() - if(radio && (wires & WIRE_RADIO_RECEIVE)) - activate() - return 1 - -/obj/item/device/assembly/proc/pulse(var/radio = 0) - if(holder && (wires & WIRE_PULSE)) - holder.process_activation(src, 1, 0) - if(holder && (wires & WIRE_PULSE_SPECIAL)) - holder.process_activation(src, 0, 1) - return 1 - -/obj/item/device/assembly/proc/activate() - if(!secured || !process_cooldown()) - return FALSE - return TRUE - -/obj/item/device/assembly/proc/toggle_secure() - secured = !secured - update_icon() - return secured - -/obj/item/device/assembly/proc/attach_assembly(var/obj/item/device/assembly/A, var/mob/user) - holder = new/obj/item/device/assembly_holder(get_turf(src)) - if(holder.attach(A,src,user)) - to_chat(user, "You attach \the [A] to \the [src]!") - return TRUE - -/obj/item/device/assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(isassembly(W)) - var/obj/item/device/assembly/A = W - if((!A.secured) && (!secured)) - attach_assembly(A,user) - return - if(W.is_screwdriver()) - if(toggle_secure()) - to_chat(user, "\The [src] is ready!") - else - to_chat(user, "\The [src] can now be attached!") - return - return ..() - -/obj/item/device/assembly/process() - return PROCESS_KILL - -/obj/item/device/assembly/examine(mob/user) - . = ..() - if((in_range(src, user) || loc == user)) - if(secured) - . += "\The [src] is ready!" - else - . += "\The [src] can be attached!" - -/obj/item/device/assembly/attack_self(mob/user as mob) - if(!user) - return 0 - user.set_machine(src) - tgui_interact(user) - return 1 - -/obj/item/device/assembly/tgui_state(mob/user) - return GLOB.tgui_deep_inventory_state - -/obj/item/device/assembly/tgui_interact(mob/user, datum/tgui/ui) - return // tgui goes here - -/obj/item/device/assembly/tgui_host() - if(istype(loc, /obj/item/device/assembly_holder)) - return loc.tgui_host() - return ..() +/obj/item/device/assembly + name = "assembly" + desc = "A small electronic device that should never exist." + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "" + w_class = ITEMSIZE_SMALL + matter = list(MAT_STEEL = 100) + throwforce = 2 + throw_speed = 3 + throw_range = 10 + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + origin_tech = list(TECH_MAGNET = 1) + + var/secured = 1 + var/list/attached_overlays = null + var/obj/item/device/assembly_holder/holder = null + var/cooldown = FALSE //To prevent spam + var/wires = WIRE_RECEIVE | WIRE_PULSE + + var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() + var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder + var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly + var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() + var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message + +/obj/item/device/assembly/proc/holder_movement() + return + +/obj/item/device/assembly/proc/process_cooldown() + if(cooldown) + return FALSE + cooldown = TRUE + VARSET_IN(src, cooldown, FALSE, 2 SECONDS) + return TRUE + +/obj/item/device/assembly/proc/pulsed(var/radio = 0) + if(holder && (wires & WIRE_RECEIVE)) + activate() + if(radio && (wires & WIRE_RADIO_RECEIVE)) + activate() + return 1 + +/obj/item/device/assembly/proc/pulse(var/radio = 0) + if(holder && (wires & WIRE_PULSE)) + holder.process_activation(src, 1, 0) + if(holder && (wires & WIRE_PULSE_SPECIAL)) + holder.process_activation(src, 0, 1) + return 1 + +/obj/item/device/assembly/proc/activate() + if(!secured || !process_cooldown()) + return FALSE + return TRUE + +/obj/item/device/assembly/proc/toggle_secure() + secured = !secured + update_icon() + return secured + +/obj/item/device/assembly/proc/attach_assembly(var/obj/item/device/assembly/A, var/mob/user) + holder = new/obj/item/device/assembly_holder(get_turf(src)) + if(holder.attach(A,src,user)) + to_chat(user, "You attach \the [A] to \the [src]!") + return TRUE + +/obj/item/device/assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(isassembly(W)) + var/obj/item/device/assembly/A = W + if((!A.secured) && (!secured)) + attach_assembly(A,user) + return + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(toggle_secure()) + to_chat(user, "\The [src] is ready!") + else + to_chat(user, "\The [src] can now be attached!") + return + return ..() + +/obj/item/device/assembly/process() + return PROCESS_KILL + +/obj/item/device/assembly/examine(mob/user) + . = ..() + if((in_range(src, user) || loc == user)) + if(secured) + . += "\The [src] is ready!" + else + . += "\The [src] can be attached!" + +/obj/item/device/assembly/attack_self(mob/user as mob) + if(!user) + return 0 + user.set_machine(src) + tgui_interact(user) + return 1 + +/obj/item/device/assembly/tgui_state(mob/user) + return GLOB.tgui_deep_inventory_state + +/obj/item/device/assembly/tgui_interact(mob/user, datum/tgui/ui) + return // tgui goes here + +/obj/item/device/assembly/tgui_host() + if(istype(loc, /obj/item/device/assembly_holder)) + return loc.tgui_host() + return ..() diff --git a/code/modules/assembly/helpers.dm b/code/modules/assembly/helpers.dm index cc3100f7727..44164fb72b2 100644 --- a/code/modules/assembly/helpers.dm +++ b/code/modules/assembly/helpers.dm @@ -1,29 +1,29 @@ -/proc/isassembly(O) - if(istype(O, /obj/item/device/assembly)) - return 1 - return 0 - -/proc/isigniter(O) - if(istype(O, /obj/item/device/assembly/igniter)) - return 1 - return 0 - -/proc/isinfared(O) - if(istype(O, /obj/item/device/assembly/infra)) - return 1 - return 0 - -/proc/isprox(O) - if(istype(O, /obj/item/device/assembly/prox_sensor)) - return 1 - return 0 - -/proc/issignaler(O) - if(istype(O, /obj/item/device/assembly/signaler)) - return 1 - return 0 - -/proc/istimer(O) - if(istype(O, /obj/item/device/assembly/timer)) - return 1 - return 0 +/proc/isassembly(O) + if(istype(O, /obj/item/device/assembly)) + return 1 + return 0 + +/proc/isigniter(O) + if(istype(O, /obj/item/device/assembly/igniter)) + return 1 + return 0 + +/proc/isinfared(O) + if(istype(O, /obj/item/device/assembly/infra)) + return 1 + return 0 + +/proc/isprox(O) + if(istype(O, /obj/item/device/assembly/prox_sensor)) + return 1 + return 0 + +/proc/issignaler(O) + if(istype(O, /obj/item/device/assembly/signaler)) + return 1 + return 0 + +/proc/istimer(O) + if(istype(O, /obj/item/device/assembly/timer)) + return 1 + return 0 diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index f4d544f0401..110a9cee35f 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -1,225 +1,225 @@ -/obj/item/device/assembly_holder - name = "Assembly" - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "holder" - item_state = "assembly" - throwforce = 5 - w_class = ITEMSIZE_SMALL - throw_speed = 3 - throw_range = 10 - - var/secured = 0 - var/obj/item/device/assembly/a_left = null - var/obj/item/device/assembly/a_right = null - var/obj/special_assembly = null - -/obj/item/device/assembly_holder/proc/attach(var/obj/item/device/assembly/D, var/obj/item/device/assembly/D2, var/mob/user) - if(!D || !D2) - return FALSE - - if(!istype(D) || !istype(D2)) - return FALSE - - if(D.secured || D2.secured) - return FALSE - - if(user) - user.remove_from_mob(D) - user.remove_from_mob(D2) - - D.holder = src - D2.holder = src - D.forceMove(src) - D2.forceMove(src) - a_left = D - a_right = D2 - name = "[D.name]-[D2.name] assembly" - update_icon() - user.put_in_hands(src) - - return TRUE - -/obj/item/device/assembly_holder/proc/detached() - return - -/obj/item/device/assembly_holder/update_icon() - cut_overlays() - if(a_left) - add_overlay("[a_left.icon_state]_left") - for(var/O in a_left.attached_overlays) - add_overlay("[O]_l") - if(a_right) - add_overlay("[a_right.icon_state]_right") - for(var/O in a_right.attached_overlays) - add_overlay("[O]_r") - if(master) - master.update_icon() - -/obj/item/device/assembly_holder/examine(mob/user) - . = ..() - if ((in_range(src, user) || src.loc == user)) - if (src.secured) - . += "\The [src] is ready!" - else - . += "\The [src] can be attached!" - -/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(isturf(old_loc)) - unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(callback = /atom/proc/HasProximity) - -/obj/item/device/assembly_holder/HasProximity(turf/T, atom/movable/AM, old_loc) - if(a_left) - a_left.HasProximity(T, AM, old_loc) - if(a_right) - a_right.HasProximity(T, AM, old_loc) - -/obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj) - if(AM.is_incorporeal()) - return - if(a_left) - a_left.Crossed(AM) - if(a_right) - a_right.Crossed(AM) - -/obj/item/device/assembly_holder/on_found(mob/finder as mob) - if(a_left) - a_left.on_found(finder) - if(a_right) - a_right.on_found(finder) - -/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - -/obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - ..() - -/obj/item/device/assembly_holder/attackby(var/obj/item/weapon/W, var/mob/user) - if(W.is_screwdriver()) - if(!a_left || !a_right) - to_chat(user, " BUG:Assembly part missing, please report this!") - return - a_left.toggle_secure() - a_right.toggle_secure() - secured = !secured - if(secured) - to_chat(user, "\The [src] is ready!") - else - to_chat(user, "\The [src] can now be taken apart!") - update_icon() - return - else - ..() - -/obj/item/device/assembly_holder/attack_self(var/mob/user) - src.add_fingerprint(user) - if(src.secured) - if(!a_left || !a_right) - to_chat(user, " BUG:Assembly part missing, please report this!") - return - if(istype(a_left,a_right.type))//If they are the same type it causes issues due to window code - switch(tgui_alert(usr, "Which side would you like to use?","Side",list("Left","Right"))) - if("Left") a_left.attack_self(user) - if("Right") a_right.attack_self(user) - return - else - if(!istype(a_left,/obj/item/device/assembly/igniter)) - a_left.attack_self(user) - if(!istype(a_right,/obj/item/device/assembly/igniter)) - a_right.attack_self(user) - else - var/turf/T = get_turf(src) - if(!T) - return 0 - if(a_left) - a_left.holder = null - a_left.forceMove(T) - if(a_right) - a_right.holder = null - a_right.forceMove(T) - qdel(src) - -/obj/item/device/assembly_holder/proc/process_activation(var/obj/D, var/normal = 1) - if(!D) - return 0 - if(!secured) - visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") - if((normal) && (a_right) && (a_left)) - if(a_right != D) - a_right.pulsed(0) - if(a_left != D) - a_left.pulsed(0) - if(master) - master.receive_signal() - return 1 - -/obj/item/device/assembly_holder/hear_talk(mob/M, list/message_pieces, verb) - if(a_right) - a_right.hear_talk(M, message_pieces, verb) - if(a_left) - a_left.hear_talk(M, message_pieces, verb) - -/obj/item/device/assembly_holder/timer_igniter - name = "timer-igniter assembly" - -/obj/item/device/assembly_holder/timer_igniter/New() - ..() - - var/obj/item/device/assembly/igniter/ign = new(src) - ign.secured = 1 - ign.holder = src - - var/obj/item/device/assembly/timer/tmr = new(src) - tmr.time = 5 - tmr.secured = 1 - tmr.holder = src - - a_left = tmr - a_right = ign - secured = 1 - update_icon() - name = initial(name) + " ([tmr.time] secs)" - - loc.verbs += /obj/item/device/assembly_holder/timer_igniter/verb/configure - -/obj/item/device/assembly_holder/timer_igniter/detached() - loc.verbs -= /obj/item/device/assembly_holder/timer_igniter/verb/configure - ..() - -/obj/item/device/assembly_holder/timer_igniter/verb/configure() - set name = "Set Timer" - set category = "Object" - set src in usr - - if ( !(usr.stat || usr.restrained()) ) - var/obj/item/device/assembly_holder/holder - if(istype(src,/obj/item/weapon/grenade/chem_grenade)) - var/obj/item/weapon/grenade/chem_grenade/gren = src - holder=gren.detonator - var/obj/item/device/assembly/timer/tmr = holder.a_left - if(!istype(tmr,/obj/item/device/assembly/timer)) - tmr = holder.a_right - if(!istype(tmr,/obj/item/device/assembly/timer)) - to_chat(usr, "This detonator has no timer.") - return - - if(tmr.timing) - to_chat(usr, "Clock is ticking already.") - else - var/ntime = tgui_input_number(usr, "Enter desired time in seconds", "Time", "5", 1000, 0) - if (ntime>0 && ntime<1000) - tmr.time = ntime - name = initial(name) + "([tmr.time] secs)" - to_chat(usr, "Timer set to [tmr.time] seconds.") - else - to_chat(usr, "Timer can't be [ntime<=0?"negative":"more than 1000 seconds"].") - else - to_chat(usr, "You cannot do this while [usr.stat?"unconscious/dead":"restrained"].") +/obj/item/device/assembly_holder + name = "Assembly" + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "holder" + item_state = "assembly" + throwforce = 5 + w_class = ITEMSIZE_SMALL + throw_speed = 3 + throw_range = 10 + + var/secured = 0 + var/obj/item/device/assembly/a_left = null + var/obj/item/device/assembly/a_right = null + var/obj/special_assembly = null + +/obj/item/device/assembly_holder/proc/attach(var/obj/item/device/assembly/D, var/obj/item/device/assembly/D2, var/mob/user) + if(!D || !D2) + return FALSE + + if(!istype(D) || !istype(D2)) + return FALSE + + if(D.secured || D2.secured) + return FALSE + + if(user) + user.remove_from_mob(D) + user.remove_from_mob(D2) + + D.holder = src + D2.holder = src + D.forceMove(src) + D2.forceMove(src) + a_left = D + a_right = D2 + name = "[D.name]-[D2.name] assembly" + update_icon() + user.put_in_hands(src) + + return TRUE + +/obj/item/device/assembly_holder/proc/detached() + return + +/obj/item/device/assembly_holder/update_icon() + cut_overlays() + if(a_left) + add_overlay("[a_left.icon_state]_left") + for(var/O in a_left.attached_overlays) + add_overlay("[O]_l") + if(a_right) + add_overlay("[a_right.icon_state]_right") + for(var/O in a_right.attached_overlays) + add_overlay("[O]_r") + if(master) + master.update_icon() + +/obj/item/device/assembly_holder/examine(mob/user) + . = ..() + if ((in_range(src, user) || src.loc == user)) + if (src.secured) + . += "\The [src] is ready!" + else + . += "\The [src] can be attached!" + +/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(isturf(old_loc)) + unsense_proximity(callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(callback = /atom/proc/HasProximity) + +/obj/item/device/assembly_holder/HasProximity(turf/T, atom/movable/AM, old_loc) + if(a_left) + a_left.HasProximity(T, AM, old_loc) + if(a_right) + a_right.HasProximity(T, AM, old_loc) + +/obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return + if(a_left) + a_left.Crossed(AM) + if(a_right) + a_right.Crossed(AM) + +/obj/item/device/assembly_holder/on_found(mob/finder as mob) + if(a_left) + a_left.on_found(finder) + if(a_right) + a_right.on_found(finder) + +/obj/item/device/assembly_holder/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + +/obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + ..() + +/obj/item/device/assembly_holder/attackby(var/obj/item/weapon/W, var/mob/user) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + if(!a_left || !a_right) + to_chat(user, " BUG:Assembly part missing, please report this!") + return + a_left.toggle_secure() + a_right.toggle_secure() + secured = !secured + if(secured) + to_chat(user, "\The [src] is ready!") + else + to_chat(user, "\The [src] can now be taken apart!") + update_icon() + return + else + ..() + +/obj/item/device/assembly_holder/attack_self(var/mob/user) + src.add_fingerprint(user) + if(src.secured) + if(!a_left || !a_right) + to_chat(user, " BUG:Assembly part missing, please report this!") + return + if(istype(a_left,a_right.type))//If they are the same type it causes issues due to window code + switch(tgui_alert(usr, "Which side would you like to use?","Side",list("Left","Right"))) + if("Left") a_left.attack_self(user) + if("Right") a_right.attack_self(user) + return + else + if(!istype(a_left,/obj/item/device/assembly/igniter)) + a_left.attack_self(user) + if(!istype(a_right,/obj/item/device/assembly/igniter)) + a_right.attack_self(user) + else + var/turf/T = get_turf(src) + if(!T) + return 0 + if(a_left) + a_left.holder = null + a_left.forceMove(T) + if(a_right) + a_right.holder = null + a_right.forceMove(T) + qdel(src) + +/obj/item/device/assembly_holder/proc/process_activation(var/obj/D, var/normal = 1) + if(!D) + return 0 + if(!secured) + visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") + if((normal) && (a_right) && (a_left)) + if(a_right != D) + a_right.pulsed(0) + if(a_left != D) + a_left.pulsed(0) + if(master) + master.receive_signal() + return 1 + +/obj/item/device/assembly_holder/hear_talk(mob/M, list/message_pieces, verb) + if(a_right) + a_right.hear_talk(M, message_pieces, verb) + if(a_left) + a_left.hear_talk(M, message_pieces, verb) + +/obj/item/device/assembly_holder/timer_igniter + name = "timer-igniter assembly" + +/obj/item/device/assembly_holder/timer_igniter/New() + ..() + + var/obj/item/device/assembly/igniter/ign = new(src) + ign.secured = 1 + ign.holder = src + + var/obj/item/device/assembly/timer/tmr = new(src) + tmr.time = 5 + tmr.secured = 1 + tmr.holder = src + + a_left = tmr + a_right = ign + secured = 1 + update_icon() + name = initial(name) + " ([tmr.time] secs)" + + loc.verbs += /obj/item/device/assembly_holder/timer_igniter/verb/configure + +/obj/item/device/assembly_holder/timer_igniter/detached() + loc.verbs -= /obj/item/device/assembly_holder/timer_igniter/verb/configure + ..() + +/obj/item/device/assembly_holder/timer_igniter/verb/configure() + set name = "Set Timer" + set category = "Object" + set src in usr + + if ( !(usr.stat || usr.restrained()) ) + var/obj/item/device/assembly_holder/holder + if(istype(src,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/gren = src + holder=gren.detonator + var/obj/item/device/assembly/timer/tmr = holder.a_left + if(!istype(tmr,/obj/item/device/assembly/timer)) + tmr = holder.a_right + if(!istype(tmr,/obj/item/device/assembly/timer)) + to_chat(usr, "This detonator has no timer.") + return + + if(tmr.timing) + to_chat(usr, "Clock is ticking already.") + else + var/ntime = tgui_input_number(usr, "Enter desired time in seconds", "Time", "5", 1000, 0) + if (ntime>0 && ntime<1000) + tmr.time = ntime + name = initial(name) + "([tmr.time] secs)" + to_chat(usr, "Timer set to [tmr.time] seconds.") + else + to_chat(usr, "Timer can't be [ntime<=0?"negative":"more than 1000 seconds"].") + else + to_chat(usr, "You cannot do this while [usr.stat?"unconscious/dead":"restrained"].") diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 8b45b840847..0827836c355 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -1,40 +1,40 @@ -/obj/item/device/assembly/igniter - name = "igniter" - desc = "A small electronic device able to ignite combustable substances." - icon_state = "igniter" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 500, MAT_GLASS = 50) - - secured = 1 - wires = WIRE_RECEIVE - -/obj/item/device/assembly/igniter/activate() - if(!..()) - return FALSE - - if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) - var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc - grenade.detonate() - else - var/turf/location = get_turf(loc) - if(location) - location.hotspot_expose(1000,1000) - if (istype(src.loc,/obj/item/device/assembly_holder)) - if (istype(src.loc.loc, /obj/structure/reagent_dispensers/fueltank/)) - var/obj/structure/reagent_dispensers/fueltank/tank = src.loc.loc - if (tank && tank.modded) - tank.explode() - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - - return TRUE - - -/obj/item/device/assembly/igniter/attack_self(var/mob/user) - activate() - add_fingerprint(user) - -/obj/item/device/assembly/igniter/is_hot() +/obj/item/device/assembly/igniter + name = "igniter" + desc = "A small electronic device able to ignite combustable substances." + icon_state = "igniter" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 500, MAT_GLASS = 50) + + secured = 1 + wires = WIRE_RECEIVE + +/obj/item/device/assembly/igniter/activate() + if(!..()) + return FALSE + + if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc + grenade.detonate() + else + var/turf/location = get_turf(loc) + if(location) + location.hotspot_expose(1000,1000) + if (istype(src.loc,/obj/item/device/assembly_holder)) + if (istype(src.loc.loc, /obj/structure/reagent_dispensers/fueltank/)) + var/obj/structure/reagent_dispensers/fueltank/tank = src.loc.loc + if (tank && tank.modded) + tank.explode() + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + + return TRUE + + +/obj/item/device/assembly/igniter/attack_self(var/mob/user) + activate() + add_fingerprint(user) + +/obj/item/device/assembly/igniter/is_hot() return TRUE \ No newline at end of file diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index c4c4e09e199..fb5566504ef 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -1,181 +1,181 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/device/assembly/infra - name = "infrared emitter" - desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." - icon_state = "infrared" - origin_tech = list(TECH_MAGNET = 2) - matter = list(MAT_STEEL = 1000, MAT_GLASS = 500) - - wires = WIRE_PULSE - - secured = 0 - - var/on = 0 - var/visible = 0 - var/list/i_beams = null - -/obj/item/device/assembly/infra/activate() - if(!..()) - return FALSE - on = !on - update_icon() - return TRUE - -/obj/item/device/assembly/infra/toggle_secure() - secured = !secured - if(!secured) - toggle_state(FALSE) - update_icon() - return secured - -/obj/item/device/assembly/infra/proc/toggle_state(var/picked) - if(!isnull(picked)) - on = picked - else - on = !on - - if(secured && on) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - QDEL_LIST_NULL(i_beams) - return on - -/obj/item/device/assembly/infra/update_icon() - cut_overlays() - LAZYCLEARLIST(attached_overlays) - if(on) - add_overlay("infrared_on") - LAZYADD(attached_overlays, "infrared_on") - - if(holder) - holder.update_icon(2) - -/obj/item/device/assembly/infra/process() - if(!on && i_beams) - QDEL_LIST_NULL(i_beams) - return - - if(!i_beams && secured && (istype(loc, /turf) || (holder && istype(holder.loc, /turf)))) - create_beams() - -/obj/item/device/assembly/infra/proc/create_beams(var/limit = 8) - var/current_spot = get_turf(src) - for(var/i = 1 to limit) - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(current_spot) - I.master = src - I.density = TRUE - I.set_dir(dir) - if(!step(I, I.dir)) //Try to take a step in that direction - return //Couldn't, oh well, we hit a wall or something. Beam should qdel itself in it's Bump(). - I.density = FALSE - i_beams |= I - I.visible = visible - -/obj/item/device/assembly/infra/attack_hand() - QDEL_LIST_NULL(i_beams) - ..() - -/obj/item/device/assembly/infra/Move() - var/t = dir - . = ..() - set_dir(t) - -/obj/item/device/assembly/infra/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - QDEL_LIST_NULL(i_beams) - -/obj/item/device/assembly/infra/holder_movement() - if(!holder) - return FALSE - QDEL_LIST_NULL(i_beams) - return TRUE - -/obj/item/device/assembly/infra/proc/trigger_beam() - if(!process_cooldown()) - return FALSE - pulse(0) - QDEL_LIST_NULL(i_beams) //They will get recreated next process() if the situation is still appropriate - if(!holder) - visible_message("\icon[src][bicon(src)] *beep* *beep*") - -/obj/item/device/assembly/infra/tgui_interact(mob/user, datum/tgui/ui) - if(!secured) - to_chat(user, "[src] is unsecured!") - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AssemblyInfrared", name) - ui.open() - -/obj/item/device/assembly/infra/tgui_data(mob/user) - var/list/data = ..() - - data["on"] = on - data["visible"] = visible - - return data - -/obj/item/device/assembly/infra/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("state") - toggle_state() - return TRUE - if("visible") - visible = !visible - for(var/obj/effect/beam/i_beam/I as anything in i_beams) - I.visible = visible - CHECK_TICK - return TRUE - -/obj/item/device/assembly/infra/verb/rotate_clockwise() - set name = "Rotate Infrared Laser Clockwise" - set category = "Object" - set src in usr - - set_dir(turn(dir, 270)) - -/***************************IBeam*********************************/ - -/obj/effect/beam/i_beam - name = "i beam" - icon = 'icons/obj/projectiles.dmi' - icon_state = "ibeam" - var/obj/item/device/assembly/infra/master = null - var/visible = 0 - anchored = TRUE - -/obj/effect/beam/i_beam/Initialize() - . = ..() - START_PROCESSING(SSobj, src) - -/obj/effect/beam/i_beam/Destroy() - STOP_PROCESSING(SSobj, src) - master = null - return ..() - -/obj/effect/beam/i_beam/proc/hit() - master?.trigger_beam() - qdel(src) - -/obj/effect/beam/i_beam/process() - if(loc?.density || !master) - qdel(src) - return - -/obj/effect/beam/i_beam/Bump() - qdel(src) - -/obj/effect/beam/i_beam/Bumped() - hit() - -/obj/effect/beam/i_beam/Crossed(var/atom/movable/AM) - if(AM.is_incorporeal()) - return - if(istype(AM, /obj/effect/beam)) - return +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/device/assembly/infra + name = "infrared emitter" + desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." + icon_state = "infrared" + origin_tech = list(TECH_MAGNET = 2) + matter = list(MAT_STEEL = 1000, MAT_GLASS = 500) + + wires = WIRE_PULSE + + secured = 0 + + var/on = 0 + var/visible = 0 + var/list/i_beams = null + +/obj/item/device/assembly/infra/activate() + if(!..()) + return FALSE + on = !on + update_icon() + return TRUE + +/obj/item/device/assembly/infra/toggle_secure() + secured = !secured + if(!secured) + toggle_state(FALSE) + update_icon() + return secured + +/obj/item/device/assembly/infra/proc/toggle_state(var/picked) + if(!isnull(picked)) + on = picked + else + on = !on + + if(secured && on) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + QDEL_LIST_NULL(i_beams) + return on + +/obj/item/device/assembly/infra/update_icon() + cut_overlays() + LAZYCLEARLIST(attached_overlays) + if(on) + add_overlay("infrared_on") + LAZYADD(attached_overlays, "infrared_on") + + if(holder) + holder.update_icon(2) + +/obj/item/device/assembly/infra/process() + if(!on && i_beams) + QDEL_LIST_NULL(i_beams) + return + + if(!i_beams && secured && (istype(loc, /turf) || (holder && istype(holder.loc, /turf)))) + create_beams() + +/obj/item/device/assembly/infra/proc/create_beams(var/limit = 8) + var/current_spot = get_turf(src) + for(var/i = 1 to limit) + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(current_spot) + I.master = src + I.density = TRUE + I.set_dir(dir) + if(!step(I, I.dir)) //Try to take a step in that direction + return //Couldn't, oh well, we hit a wall or something. Beam should qdel itself in it's Bump(). + I.density = FALSE + i_beams |= I + I.visible = visible + +/obj/item/device/assembly/infra/attack_hand() + QDEL_LIST_NULL(i_beams) + ..() + +/obj/item/device/assembly/infra/Move() + var/t = dir + . = ..() + set_dir(t) + +/obj/item/device/assembly/infra/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + QDEL_LIST_NULL(i_beams) + +/obj/item/device/assembly/infra/holder_movement() + if(!holder) + return FALSE + QDEL_LIST_NULL(i_beams) + return TRUE + +/obj/item/device/assembly/infra/proc/trigger_beam() + if(!process_cooldown()) + return FALSE + pulse(0) + QDEL_LIST_NULL(i_beams) //They will get recreated next process() if the situation is still appropriate + if(!holder) + visible_message("\icon[src][bicon(src)] *beep* *beep*") + +/obj/item/device/assembly/infra/tgui_interact(mob/user, datum/tgui/ui) + if(!secured) + to_chat(user, "[src] is unsecured!") + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AssemblyInfrared", name) + ui.open() + +/obj/item/device/assembly/infra/tgui_data(mob/user) + var/list/data = ..() + + data["on"] = on + data["visible"] = visible + + return data + +/obj/item/device/assembly/infra/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("state") + toggle_state() + return TRUE + if("visible") + visible = !visible + for(var/obj/effect/beam/i_beam/I as anything in i_beams) + I.visible = visible + CHECK_TICK + return TRUE + +/obj/item/device/assembly/infra/verb/rotate_clockwise() + set name = "Rotate Infrared Laser Clockwise" + set category = "Object" + set src in usr + + set_dir(turn(dir, 270)) + +/***************************IBeam*********************************/ + +/obj/effect/beam/i_beam + name = "i beam" + icon = 'icons/obj/projectiles.dmi' + icon_state = "ibeam" + var/obj/item/device/assembly/infra/master = null + var/visible = 0 + anchored = TRUE + +/obj/effect/beam/i_beam/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/beam/i_beam/Destroy() + STOP_PROCESSING(SSobj, src) + master = null + return ..() + +/obj/effect/beam/i_beam/proc/hit() + master?.trigger_beam() + qdel(src) + +/obj/effect/beam/i_beam/process() + if(loc?.density || !master) + qdel(src) + return + +/obj/effect/beam/i_beam/Bump() + qdel(src) + +/obj/effect/beam/i_beam/Bumped() + hit() + +/obj/effect/beam/i_beam/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return + if(istype(AM, /obj/effect/beam)) + return hit() \ No newline at end of file diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 31f4ca52443..de11bc20a79 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -1,123 +1,123 @@ -/obj/item/device/assembly/mousetrap - name = "mousetrap" - desc = "A handy little spring-loaded trap for catching pesty rodents." - icon_state = "mousetrap" - origin_tech = list(TECH_COMBAT = 1) - matter = list(MAT_STEEL = 100) - var/armed = 0 - - -/obj/item/device/assembly/mousetrap/examine(var/mob/user) - . = ..(user) - if(armed) - . += "It looks like it's armed." - -/obj/item/device/assembly/mousetrap/update_icon() - if(armed) - icon_state = "mousetraparmed" - else - icon_state = "mousetrap" - if(holder) - holder.update_icon() - -/obj/item/device/assembly/mousetrap/proc/triggered(var/mob/target, var/type = "feet") - if(!armed) - return - var/obj/item/organ/external/affecting = null - if(ishuman(target)) - var/mob/living/carbon/human/H = target - switch(type) - if("feet") - if(!H.shoes) - affecting = H.get_organ(pick("l_leg", "r_leg")) - H.Weaken(3) - if("l_hand", "r_hand") - if(!H.gloves) - affecting = H.get_organ(type) - H.Stun(3) - if(affecting) - if(affecting.take_damage(1, 0)) - H.UpdateDamageIcon() - H.updatehealth() - else if(ismouse(target)) - var/mob/living/simple_mob/animal/passive/mouse/M = target - visible_message("SPLAT!") - M.splat() - playsound(target, 'sound/effects/snap.ogg', 50, 1) - layer = MOB_LAYER - 0.2 - armed = 0 - update_icon() - pulse(0) - -/obj/item/device/assembly/mousetrap/attack_self(var/mob/living/user) - if(!armed) - to_chat(user, "You arm [src].") - else - if((CLUMSY in user.mutations) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ - "You accidentally trigger [src]!") - return - - to_chat(user, "You disarm [src].") - armed = !armed - update_icon() - playsound(user, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/device/assembly/mousetrap/attack_hand(var/mob/living/user) - if(armed) - if((CLUMSY in user.mutations) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ - "You accidentally trigger [src]!") - return - ..() - -/obj/item/device/assembly/mousetrap/Crossed(var/atom/movable/AM) - if(AM.is_incorporeal()) - return - if(armed) - if(ishuman(AM)) - var/mob/living/carbon/H = AM - if(H.m_intent == "run") - triggered(H) - H.visible_message("[H] accidentally steps on [src].", \ - "You accidentally step on [src]") - if(ismouse(AM)) - triggered(AM) - ..() - -/obj/item/device/assembly/mousetrap/on_found(var/mob/living/finder) - if(armed) - finder.visible_message("[finder] accidentally sets off [src], breaking their fingers.", \ - "You accidentally trigger [src]!") - triggered(finder, finder.hand ? "l_hand" : "r_hand") - return 1 //end the search! - return 0 - -/obj/item/device/assembly/mousetrap/hitby(var/atom/movable/A) - if(!armed) - return ..() - visible_message("[src] is triggered by [A].") - triggered(null) - -/obj/item/device/assembly/mousetrap/armed - icon_state = "mousetraparmed" - armed = 1 - -/obj/item/device/assembly/mousetrap/verb/hide_under() - set src in oview(1) - set name = "Hide" - set category = "Object" - - if(usr.stat) - return - - layer = HIDING_LAYER - to_chat(usr, "You hide [src].") \ No newline at end of file +/obj/item/device/assembly/mousetrap + name = "mousetrap" + desc = "A handy little spring-loaded trap for catching pesty rodents." + icon_state = "mousetrap" + origin_tech = list(TECH_COMBAT = 1) + matter = list(MAT_STEEL = 100) + var/armed = 0 + + +/obj/item/device/assembly/mousetrap/examine(var/mob/user) + . = ..(user) + if(armed) + . += "It looks like it's armed." + +/obj/item/device/assembly/mousetrap/update_icon() + if(armed) + icon_state = "mousetraparmed" + else + icon_state = "mousetrap" + if(holder) + holder.update_icon() + +/obj/item/device/assembly/mousetrap/proc/triggered(var/mob/target, var/type = "feet") + if(!armed) + return + var/obj/item/organ/external/affecting = null + if(ishuman(target)) + var/mob/living/carbon/human/H = target + switch(type) + if("feet") + if(!H.shoes) + affecting = H.get_organ(pick("l_leg", "r_leg")) + H.Weaken(3) + if("l_hand", "r_hand") + if(!H.gloves) + affecting = H.get_organ(type) + H.Stun(3) + if(affecting) + if(affecting.take_damage(1, 0)) + H.UpdateDamageIcon() + H.updatehealth() + else if(ismouse(target)) + var/mob/living/simple_mob/animal/passive/mouse/M = target + visible_message(span_red("SPLAT!")) + M.splat() + playsound(target, 'sound/effects/snap.ogg', 50, 1) + layer = MOB_LAYER - 0.2 + armed = 0 + update_icon() + pulse(0) + +/obj/item/device/assembly/mousetrap/attack_self(var/mob/living/user) + if(!armed) + to_chat(user, "You arm [src].") + else + if((CLUMSY in user.mutations) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ + "You accidentally trigger [src]!") + return + + to_chat(user, "You disarm [src].") + armed = !armed + update_icon() + playsound(user, 'sound/weapons/handcuffs.ogg', 30, 1, -3) + +/obj/item/device/assembly/mousetrap/attack_hand(var/mob/living/user) + if(armed) + if((CLUMSY in user.mutations) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking their fingers.", \ + "You accidentally trigger [src]!") + return + ..() + +/obj/item/device/assembly/mousetrap/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return + if(armed) + if(ishuman(AM)) + var/mob/living/carbon/H = AM + if(H.m_intent == "run") + triggered(H) + H.visible_message("[H] accidentally steps on [src].", \ + "You accidentally step on [src]") + if(ismouse(AM)) + triggered(AM) + ..() + +/obj/item/device/assembly/mousetrap/on_found(var/mob/living/finder) + if(armed) + finder.visible_message("[finder] accidentally sets off [src], breaking their fingers.", \ + "You accidentally trigger [src]!") + triggered(finder, finder.hand ? "l_hand" : "r_hand") + return 1 //end the search! + return 0 + +/obj/item/device/assembly/mousetrap/hitby(var/atom/movable/A) + if(!armed) + return ..() + visible_message("[src] is triggered by [A].") + triggered(null) + +/obj/item/device/assembly/mousetrap/armed + icon_state = "mousetraparmed" + armed = 1 + +/obj/item/device/assembly/mousetrap/verb/hide_under() + set src in oview(1) + set name = "Hide" + set category = "Object" + + if(usr.stat) + return + + layer = HIDING_LAYER + to_chat(usr, "You hide [src].") diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index 0561eb47974..7c12d5fb039 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -1,143 +1,143 @@ -/obj/item/device/assembly/prox_sensor - name = "proximity sensor" - desc = "Used for scanning and alerting when someone enters a certain proximity." - icon_state = "prox" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 800, MAT_GLASS = 200) - wires = WIRE_PULSE - - secured = 0 - - var/scanning = 0 - var/timing = 0 - var/time = 10 - - var/range = 2 - -/obj/item/device/assembly/prox_sensor/activate() - if(!..()) - return FALSE - timing = !timing - update_icon() - return FALSE - -/obj/item/device/assembly/prox_sensor/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - scanning = 0 - timing = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/device/assembly/prox_sensor/HasProximity(turf/T, atom/movable/AM, old_loc) - if(!istype(AM)) - log_debug("DEBUG: HasProximity called with [AM] on [src] ([usr]).") - return - if (istype(AM, /obj/effect/beam)) - return - if (!isobserver(AM) && AM.move_speed < 12) - sense() - -/obj/item/device/assembly/prox_sensor/proc/sense() - if((!holder && !secured) || !scanning || !process_cooldown()) - return FALSE - var/turf/mainloc = get_turf(src) - pulse(0) - if(!holder) - mainloc.visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") - -/obj/item/device/assembly/prox_sensor/process() - if(scanning) - var/turf/mainloc = get_turf(src) - for(var/mob/living/A in range(range,mainloc)) - if (A.move_speed < 12) - sense() - - if(timing && (time >= 0)) - time-- - if(timing && time <= 0) - timing = 0 - toggle_scan() - time = initial(time) - -/obj/item/device/assembly/prox_sensor/dropped() - sense() - -/obj/item/device/assembly/prox_sensor/proc/toggle_scan() - if(!secured) - return FALSE - scanning = !scanning - update_icon() - -/obj/item/device/assembly/prox_sensor/update_icon() - cut_overlays() - LAZYCLEARLIST(attached_overlays) - if(timing) - add_overlay("prox_timing") - LAZYADD(attached_overlays, "prox_timing") - if(scanning) - add_overlay("prox_scanning") - LAZYADD(attached_overlays, "prox_scanning") - if(holder) - holder.update_icon() - if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) - var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc - grenade.primed(scanning) - -/obj/item/device/assembly/prox_sensor/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(isturf(old_loc)) - unsense_proximity(range = range, callback = /atom/proc/HasProximity, center = old_loc) - if(isturf(loc)) - sense_proximity(range = range, callback = /atom/proc/HasProximity) - sense() - -/obj/item/device/assembly/prox_sensor/tgui_interact(mob/user, datum/tgui/ui) - if(!secured) - to_chat(user, "[src] is unsecured!") - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AssemblyProx", name) - ui.open() - -/obj/item/device/assembly/prox_sensor/tgui_data(mob/user) - var/list/data = ..() - - data["time"] = time * 10 - data["timing"] = timing - data["range"] = range - data["maxRange"] = 5 - data["scanning"] = scanning - - return data - -/obj/item/device/assembly/prox_sensor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("scanning") - toggle_scan() - return TRUE - if("timing") - timing = !timing - update_icon() - return TRUE - if("set_time") - var/real_new_time = 0 - var/new_time = params["time"] - var/list/L = splittext(new_time, ":") - if(LAZYLEN(L)) - for(var/i in 1 to LAZYLEN(L)) - real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) - else - real_new_time = text2num(new_time) - time = clamp(real_new_time, 0, 600) - return TRUE - if("range") - range = clamp(params["range"], 1, 5) - return TRUE +/obj/item/device/assembly/prox_sensor + name = "proximity sensor" + desc = "Used for scanning and alerting when someone enters a certain proximity." + icon_state = "prox" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 800, MAT_GLASS = 200) + wires = WIRE_PULSE + + secured = 0 + + var/scanning = 0 + var/timing = 0 + var/time = 10 + + var/range = 2 + +/obj/item/device/assembly/prox_sensor/activate() + if(!..()) + return FALSE + timing = !timing + update_icon() + return FALSE + +/obj/item/device/assembly/prox_sensor/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + scanning = 0 + timing = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/device/assembly/prox_sensor/HasProximity(turf/T, atom/movable/AM, old_loc) + if(!istype(AM)) + log_debug("DEBUG: HasProximity called with [AM] on [src] ([usr]).") + return + if (istype(AM, /obj/effect/beam)) + return + if (!isobserver(AM) && AM.move_speed < 12) + sense() + +/obj/item/device/assembly/prox_sensor/proc/sense() + if((!holder && !secured) || !scanning || !process_cooldown()) + return FALSE + var/turf/mainloc = get_turf(src) + pulse(0) + if(!holder) + mainloc.visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") + +/obj/item/device/assembly/prox_sensor/process() + if(scanning) + var/turf/mainloc = get_turf(src) + for(var/mob/living/A in range(range,mainloc)) + if (A.move_speed < 12) + sense() + + if(timing && (time >= 0)) + time-- + if(timing && time <= 0) + timing = 0 + toggle_scan() + time = initial(time) + +/obj/item/device/assembly/prox_sensor/dropped() + sense() + +/obj/item/device/assembly/prox_sensor/proc/toggle_scan() + if(!secured) + return FALSE + scanning = !scanning + update_icon() + +/obj/item/device/assembly/prox_sensor/update_icon() + cut_overlays() + LAZYCLEARLIST(attached_overlays) + if(timing) + add_overlay("prox_timing") + LAZYADD(attached_overlays, "prox_timing") + if(scanning) + add_overlay("prox_scanning") + LAZYADD(attached_overlays, "prox_scanning") + if(holder) + holder.update_icon() + if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc + grenade.primed(scanning) + +/obj/item/device/assembly/prox_sensor/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(isturf(old_loc)) + unsense_proximity(range = range, callback = /atom/proc/HasProximity, center = old_loc) + if(isturf(loc)) + sense_proximity(range = range, callback = /atom/proc/HasProximity) + sense() + +/obj/item/device/assembly/prox_sensor/tgui_interact(mob/user, datum/tgui/ui) + if(!secured) + to_chat(user, "[src] is unsecured!") + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AssemblyProx", name) + ui.open() + +/obj/item/device/assembly/prox_sensor/tgui_data(mob/user) + var/list/data = ..() + + data["time"] = time * 10 + data["timing"] = timing + data["range"] = range + data["maxRange"] = 5 + data["scanning"] = scanning + + return data + +/obj/item/device/assembly/prox_sensor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("scanning") + toggle_scan() + return TRUE + if("timing") + timing = !timing + update_icon() + return TRUE + if("set_time") + var/real_new_time = 0 + var/new_time = params["time"] + var/list/L = splittext(new_time, ":") + if(LAZYLEN(L)) + for(var/i in 1 to LAZYLEN(L)) + real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) + else + real_new_time = text2num(new_time) + time = clamp(real_new_time, 0, 600) + return TRUE + if("range") + range = clamp(params["range"], 1, 5) + return TRUE diff --git a/code/modules/assembly/shock_kit.dm b/code/modules/assembly/shock_kit.dm index d3f518bbc85..c55d6a72cab 100644 --- a/code/modules/assembly/shock_kit.dm +++ b/code/modules/assembly/shock_kit.dm @@ -1,46 +1,46 @@ -/obj/item/assembly/shock_kit - name = "electrohelmet assembly" - desc = "This appears to be made from both an electropack and a helmet." - icon_state = "shock_kit" - var/obj/item/clothing/head/helmet/part1 = null - var/obj/item/device/radio/electropack/part2 = null - var/status = 0 - w_class = ITEMSIZE_HUGE - -/obj/item/assembly/shock_kit/Destroy() - qdel(part1) - qdel(part2) - ..() - return - -/obj/item/assembly/shock_kit/attackby(var/obj/item/weapon/W, var/mob/user) - if(W.is_wrench() && !status) - var/turf/T = loc - if(ismob(T)) - T = T.loc - part1.loc = T - part2.loc = T - part1.master = null - part2.master = null - part1 = null - part2 = null - qdel(src) - return - if(W.is_screwdriver()) - status = !status - to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") - playsound(src, W.usesound, 50, 1) - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/attack_self(mob/user as mob) - part1.attack_self(user, status) - part2.attack_self(user, status) - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/receive_signal() - if(istype(loc, /obj/structure/bed/chair/e_chair)) - var/obj/structure/bed/chair/e_chair/C = loc - C.shock() - return +/obj/item/assembly/shock_kit + name = "electrohelmet assembly" + desc = "This appears to be made from both an electropack and a helmet." + icon_state = "shock_kit" + var/obj/item/clothing/head/helmet/part1 = null + var/obj/item/device/radio/electropack/part2 = null + var/status = 0 + w_class = ITEMSIZE_HUGE + +/obj/item/assembly/shock_kit/Destroy() + qdel(part1) + qdel(part2) + ..() + return + +/obj/item/assembly/shock_kit/attackby(var/obj/item/weapon/W, var/mob/user) + if(W.has_tool_quality(TOOL_WRENCH) && !status) + var/turf/T = loc + if(ismob(T)) + T = T.loc + part1.loc = T + part2.loc = T + part1.master = null + part2.master = null + part1 = null + part2 = null + qdel(src) + return + if(W.has_tool_quality(TOOL_SCREWDRIVER)) + status = !status + to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") + playsound(src, W.usesound, 50, 1) + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/attack_self(mob/user as mob) + part1.attack_self(user, status) + part2.attack_self(user, status) + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/receive_signal() + if(istype(loc, /obj/structure/bed/chair/e_chair)) + var/obj/structure/bed/chair/e_chair/C = loc + C.shock() + return diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index a7267ca3428..a87a13a5765 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -1,137 +1,137 @@ -/obj/item/device/assembly/signaler - name = "remote signaling device" - desc = "Used to remotely activate devices. Tap against another secured signaler to transfer configuration." - icon_state = "signaller" - item_state = "signaler" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 1000, MAT_GLASS = 200) - wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE - - secured = TRUE - - var/code = 30 - var/frequency = 1457 - var/delay = 0 - var/airlock_wire = null - var/datum/wires/connected = null - var/datum/radio_frequency/radio_connection - -/obj/item/device/assembly/signaler/Initialize() - . = ..() - set_frequency(frequency) - -/obj/item/device/assembly/signaler/activate() - if(!process_cooldown()) - return FALSE - signal() - return TRUE - -/obj/item/device/assembly/signaler/update_icon() - if(holder) - holder.update_icon() - -/obj/item/device/assembly/signaler/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Signaler", name) - ui.open() - -/obj/item/device/assembly/signaler/tgui_data(mob/user) - var/list/data = list() - data["frequency"] = frequency - data["code"] = code - data["minFrequency"] = RADIO_LOW_FREQ - data["maxFrequency"] = RADIO_HIGH_FREQ - return data - -/obj/item/device/assembly/signaler/tgui_act(action, params) - if(..()) - return TRUE - - switch(action) - if("signal") - INVOKE_ASYNC(src, PROC_REF(signal)) - . = TRUE - if("freq") - frequency = unformat_frequency(params["freq"]) - frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(frequency) - . = TRUE - if("code") - code = text2num(params["code"]) - code = clamp(round(code), 1, 100) - . = TRUE - if("reset") - if(params["reset"] == "freq") - set_frequency(initial(frequency)) - else - code = initial(code) - . = TRUE - - update_icon() - -/obj/item/device/assembly/signaler/attackby(var/obj/item/weapon/W, mob/user, params) - if(issignaler(W)) - var/obj/item/device/assembly/signaler/signaler2 = W - if(secured && signaler2.secured) - code = signaler2.code - set_frequency(signaler2.frequency) - to_chat(user, "You transfer the frequency and code of [signaler2] to [src].") - else - ..() - -/obj/item/device/assembly/signaler/proc/signal() - if(!radio_connection) - return - if(is_jammed(src)) - return - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = "ACTIVATE" - radio_connection.post_signal(src, signal) - -/obj/item/device/assembly/signaler/pulse(var/radio = 0) - if(is_jammed(src)) - return FALSE - if(connected && wires) - connected.pulse_assembly(src) - else if(holder) - holder.process_activation(src, 1, 0) - else - ..(radio) - return TRUE - -/obj/item/device/assembly/signaler/receive_signal(datum/signal/signal) - if(!signal) - return FALSE - if(signal.encryption != code) - return FALSE - if(!(src.wires & WIRE_RADIO_RECEIVE)) - return FALSE - if(is_jammed(src)) - return FALSE - pulse(1) - - if(!holder) - for(var/mob/O in hearers(1, src.loc)) - O.show_message("\icon[src][bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) - -/obj/item/device/assembly/signaler/proc/set_frequency(new_frequency) - if(!frequency) - return - if(!radio_controller) - sleep(20) - if(!radio_controller) - return - - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) - -/obj/item/device/assembly/signaler/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - frequency = 0 - . = ..() +/obj/item/device/assembly/signaler + name = "remote signaling device" + desc = "Used to remotely activate devices. Tap against another secured signaler to transfer configuration." + icon_state = "signaller" + item_state = "signaler" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 1000, MAT_GLASS = 200) + wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE + + secured = TRUE + + var/code = 30 + var/frequency = 1457 + var/delay = 0 + var/airlock_wire = null + var/datum/wires/connected = null + var/datum/radio_frequency/radio_connection + +/obj/item/device/assembly/signaler/Initialize() + . = ..() + set_frequency(frequency) + +/obj/item/device/assembly/signaler/activate() + if(!process_cooldown()) + return FALSE + signal() + return TRUE + +/obj/item/device/assembly/signaler/update_icon() + if(holder) + holder.update_icon() + +/obj/item/device/assembly/signaler/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Signaler", name) + ui.open() + +/obj/item/device/assembly/signaler/tgui_data(mob/user) + var/list/data = list() + data["frequency"] = frequency + data["code"] = code + data["minFrequency"] = RADIO_LOW_FREQ + data["maxFrequency"] = RADIO_HIGH_FREQ + return data + +/obj/item/device/assembly/signaler/tgui_act(action, params) + if(..()) + return TRUE + + switch(action) + if("signal") + INVOKE_ASYNC(src, PROC_REF(signal)) + . = TRUE + if("freq") + frequency = unformat_frequency(params["freq"]) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(frequency) + . = TRUE + if("code") + code = text2num(params["code"]) + code = clamp(round(code), 1, 100) + . = TRUE + if("reset") + if(params["reset"] == "freq") + set_frequency(initial(frequency)) + else + code = initial(code) + . = TRUE + + update_icon() + +/obj/item/device/assembly/signaler/attackby(var/obj/item/weapon/W, mob/user, params) + if(issignaler(W)) + var/obj/item/device/assembly/signaler/signaler2 = W + if(secured && signaler2.secured) + code = signaler2.code + set_frequency(signaler2.frequency) + to_chat(user, "You transfer the frequency and code of [signaler2] to [src].") + else + ..() + +/obj/item/device/assembly/signaler/proc/signal() + if(!radio_connection) + return + if(is_jammed(src)) + return + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = "ACTIVATE" + radio_connection.post_signal(src, signal) + +/obj/item/device/assembly/signaler/pulse(var/radio = 0) + if(is_jammed(src)) + return FALSE + if(connected && wires) + connected.pulse_assembly(src) + else if(holder) + holder.process_activation(src, 1, 0) + else + ..(radio) + return TRUE + +/obj/item/device/assembly/signaler/receive_signal(datum/signal/signal) + if(!signal) + return FALSE + if(signal.encryption != code) + return FALSE + if(!(src.wires & WIRE_RADIO_RECEIVE)) + return FALSE + if(is_jammed(src)) + return FALSE + pulse(1) + + if(!holder) + for(var/mob/O in hearers(1, src.loc)) + O.show_message("\icon[src][bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) + +/obj/item/device/assembly/signaler/proc/set_frequency(new_frequency) + if(!frequency) + return + if(!radio_controller) + sleep(20) + if(!radio_controller) + return + + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) + +/obj/item/device/assembly/signaler/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + frequency = 0 + . = ..() diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index c480ed4a4e9..dd13a1e4a21 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -1,99 +1,99 @@ -/obj/item/device/assembly/timer - name = "timer" - desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." - icon_state = "timer" - origin_tech = list(TECH_MAGNET = 1) - matter = list(MAT_STEEL = 500, MAT_GLASS = 50) - - wires = WIRE_PULSE - - secured = 0 - - var/timing = 0 - var/time = 10 - - -/obj/item/device/assembly/timer/activate() - if(!..()) - return FALSE - - set_state(!timing) - - update_icon() - return 0 - -/obj/item/device/assembly/timer/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - timing = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/device/assembly/timer/proc/set_state(var/state) - if(state && !timing) //Not running, starting though - START_PROCESSING(SSobj, src) - else if(timing && !state) //Running, stopping though - STOP_PROCESSING(SSobj, src) - timing = state - -/obj/item/device/assembly/timer/proc/timer_end() - if(!secured) - return 0 - pulse(0) - if(!holder) - visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") - -/obj/item/device/assembly/timer/process() - if(timing && time-- <= 0) - set_state(0) - timer_end() - time = 10 - -/obj/item/device/assembly/timer/update_icon() - cut_overlays() - attached_overlays = list() - if(timing) - add_overlay("timer_timing") - attached_overlays += "timer_timing" - if(holder) - holder.update_icon() - return - -/obj/item/device/assembly/timer/tgui_interact(mob/user, datum/tgui/ui) - if(!secured) - to_chat(user, "[src] is unsecured!") - return FALSE - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "AssemblyTimer", name) - ui.open() - -/obj/item/device/assembly/timer/tgui_data(mob/user) - var/list/data = ..() - data["time"] = time * 10 - data["timing"] = timing - return data - -/obj/item/device/assembly/timer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("timing") - set_state(!timing) - update_icon() - return TRUE - if("set_time") - var/real_new_time = 0 - var/new_time = params["time"] - var/list/L = splittext(new_time, ":") - if(LAZYLEN(L)) - for(var/i in 1 to LAZYLEN(L)) - real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) - else - real_new_time = text2num(new_time) - time = clamp(real_new_time, 0, 600) - return TRUE +/obj/item/device/assembly/timer + name = "timer" + desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." + icon_state = "timer" + origin_tech = list(TECH_MAGNET = 1) + matter = list(MAT_STEEL = 500, MAT_GLASS = 50) + + wires = WIRE_PULSE + + secured = 0 + + var/timing = 0 + var/time = 10 + + +/obj/item/device/assembly/timer/activate() + if(!..()) + return FALSE + + set_state(!timing) + + update_icon() + return 0 + +/obj/item/device/assembly/timer/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + timing = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/device/assembly/timer/proc/set_state(var/state) + if(state && !timing) //Not running, starting though + START_PROCESSING(SSobj, src) + else if(timing && !state) //Running, stopping though + STOP_PROCESSING(SSobj, src) + timing = state + +/obj/item/device/assembly/timer/proc/timer_end() + if(!secured) + return 0 + pulse(0) + if(!holder) + visible_message("\icon[src][bicon(src)] *beep* *beep*", "*beep* *beep*") + +/obj/item/device/assembly/timer/process() + if(timing && time-- <= 0) + set_state(0) + timer_end() + time = 10 + +/obj/item/device/assembly/timer/update_icon() + cut_overlays() + attached_overlays = list() + if(timing) + add_overlay("timer_timing") + attached_overlays += "timer_timing" + if(holder) + holder.update_icon() + return + +/obj/item/device/assembly/timer/tgui_interact(mob/user, datum/tgui/ui) + if(!secured) + to_chat(user, "[src] is unsecured!") + return FALSE + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AssemblyTimer", name) + ui.open() + +/obj/item/device/assembly/timer/tgui_data(mob/user) + var/list/data = ..() + data["time"] = time * 10 + data["timing"] = timing + return data + +/obj/item/device/assembly/timer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("timing") + set_state(!timing) + update_icon() + return TRUE + if("set_time") + var/real_new_time = 0 + var/new_time = params["time"] + var/list/L = splittext(new_time, ":") + if(LAZYLEN(L)) + for(var/i in 1 to LAZYLEN(L)) + real_new_time += text2num(L[i]) * (60 ** (LAZYLEN(L) - i)) + else + real_new_time = text2num(new_time) + time = clamp(real_new_time, 0, 600) + return TRUE diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 5ec1d9be9cb..8becae6335e 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -1,13 +1,4 @@ //DEFINITIONS FOR ASSET DATUMS START HERE. - -/datum/asset/simple/tgui - // keep_local_name = TRUE - assets = list( - "tgui.bundle.js" = file("tgui/public/tgui.bundle.js"), - "tgui.bundle.css" = file("tgui/public/tgui.bundle.css"), - ) - - /datum/asset/simple/headers assets = list( "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', @@ -154,11 +145,6 @@ // /datum/asset/simple/fontawesome // ) -/datum/asset/simple/jquery - assets = list( - "jquery.min.js" = 'code/modules/tooltip/jquery.min.js', - ) - // /datum/asset/simple/goonchat // assets = list( // "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js', @@ -167,24 +153,6 @@ // "browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css', // ) -/datum/asset/simple/fontawesome - assets = list( - "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', - "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', - "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', - "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', - "font-awesome.css" = 'html/font-awesome/css/all.min.css', - "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' - ) - -/datum/asset/simple/tgfont - assets = list( - "tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"), - "tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"), - "tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"), - ) - - // /datum/asset/spritesheet/goonchat // name = "chat" diff --git a/code/modules/asset_cache/assets/chat.dm b/code/modules/asset_cache/assets/chat.dm new file mode 100644 index 00000000000..328beaca9d0 --- /dev/null +++ b/code/modules/asset_cache/assets/chat.dm @@ -0,0 +1,2 @@ +/datum/asset/spritesheet/chat + name = "chat" diff --git a/code/modules/asset_cache/assets/fontawesome.dm b/code/modules/asset_cache/assets/fontawesome.dm new file mode 100644 index 00000000000..72af739f4e1 --- /dev/null +++ b/code/modules/asset_cache/assets/fontawesome.dm @@ -0,0 +1,9 @@ +/datum/asset/simple/fontawesome + assets = list( + "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot', + "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff', + "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot', + "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff', + "font-awesome.css" = 'html/font-awesome/css/all.min.css', + "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' + ) diff --git a/code/modules/asset_cache/assets/jquery.dm b/code/modules/asset_cache/assets/jquery.dm new file mode 100644 index 00000000000..2a28293fbb3 --- /dev/null +++ b/code/modules/asset_cache/assets/jquery.dm @@ -0,0 +1,4 @@ +/datum/asset/simple/jquery + assets = list( + "jquery.min.js" = 'code/modules/tooltip/jquery.min.js', + ) diff --git a/code/modules/asset_cache/assets/tgfont.dm b/code/modules/asset_cache/assets/tgfont.dm new file mode 100644 index 00000000000..48db3e4e676 --- /dev/null +++ b/code/modules/asset_cache/assets/tgfont.dm @@ -0,0 +1,6 @@ +/datum/asset/simple/tgfont + assets = list( + "tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"), + "tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"), + "tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"), + ) diff --git a/code/modules/asset_cache/assets/tgui.dm b/code/modules/asset_cache/assets/tgui.dm new file mode 100644 index 00000000000..6446471a304 --- /dev/null +++ b/code/modules/asset_cache/assets/tgui.dm @@ -0,0 +1,13 @@ +/datum/asset/simple/tgui + // keep_local_name = TRUE + assets = list( + "tgui.bundle.js" = file("tgui/public/tgui.bundle.js"), + "tgui.bundle.css" = file("tgui/public/tgui.bundle.css"), + ) + +/datum/asset/simple/tgui_panel + // keep_local_name = TRUE + assets = list( + "tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"), + "tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"), + ) diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm index 5781085c5e3..f11b9ee61e1 100644 --- a/code/modules/awaymissions/bluespaceartillery.dm +++ b/code/modules/awaymissions/bluespaceartillery.dm @@ -1,51 +1,51 @@ - -/obj/machinery/artillerycontrol - var/reload = 180 - name = "bluespace artillery control" - icon_state = "control_boxp1" - icon = 'icons/obj/machines/particle_accelerator2.dmi' - density = TRUE - anchored = TRUE - -/obj/machinery/artillerycontrol/process() - if(src.reload<180) - src.reload++ - -/obj/structure/artilleryplaceholder - name = "artillery" - icon = 'icons/obj/machines/artillery.dmi' - anchored = TRUE - density = TRUE - -/obj/structure/artilleryplaceholder/decorative - density = FALSE - -/obj/machinery/artillerycontrol/attack_hand(mob/user as mob) - user.set_machine(src) - var/dat = "Bluespace Artillery Control:
                    " - dat += "Locked on
                    " - dat += "Charge progress: [reload]/180:
                    " - dat += "Open Fire
                    " - dat += "Deployment of weapon authorized by
                    [using_map.company_name] Naval Command

                    Remember, friendly fire is grounds for termination of your contract and life.
                    " - user << browse(dat, "window=scroll") - onclose(user, "scroll") - return - -/obj/machinery/artillerycontrol/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - var/A = tgui_input_list(usr, "Area to jump bombard", "Open Fire", teleportlocs) - var/area/thearea = teleportlocs[A] - if (usr.stat || usr.restrained()) return - if(src.reload < 180) return - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - command_announcement.Announce("Bluespace artillery fire detected. Brace for impact.") - message_admins("[key_name_admin(usr)] has launched an artillery strike.", 1) - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - L+=T - var/loc = pick(L) - explosion(loc,2,5,11) - reload = 0 + +/obj/machinery/artillerycontrol + var/reload = 180 + name = "bluespace artillery control" + icon_state = "control_boxp1" + icon = 'icons/obj/machines/particle_accelerator2.dmi' + density = TRUE + anchored = TRUE + +/obj/machinery/artillerycontrol/process() + if(src.reload<180) + src.reload++ + +/obj/structure/artilleryplaceholder + name = "artillery" + icon = 'icons/obj/machines/artillery.dmi' + anchored = TRUE + density = TRUE + +/obj/structure/artilleryplaceholder/decorative + density = FALSE + +/obj/machinery/artillerycontrol/attack_hand(mob/user as mob) + user.set_machine(src) + var/dat = "Bluespace Artillery Control:
                    " + dat += "Locked on
                    " + dat += "Charge progress: [reload]/180:
                    " + dat += "Open Fire
                    " + dat += "Deployment of weapon authorized by
                    [using_map.company_name] Naval Command

                    Remember, friendly fire is grounds for termination of your contract and life.
                    " + user << browse(dat, "window=scroll") + onclose(user, "scroll") + return + +/obj/machinery/artillerycontrol/Topic(href, href_list) + ..() + if (usr.stat || usr.restrained()) + return + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + var/A = tgui_input_list(usr, "Area to jump bombard", "Open Fire", teleportlocs) + var/area/thearea = teleportlocs[A] + if (usr.stat || usr.restrained()) return + if(src.reload < 180) return + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + command_announcement.Announce("Bluespace artillery fire detected. Brace for impact.") + message_admins("[key_name_admin(usr)] has launched an artillery strike.", 1) + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + L+=T + var/loc = pick(L) + explosion(loc,2,5,11) + reload = 0 diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 1aaaab9e483..b3ab39f74ee 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -1,381 +1,381 @@ -//These are meant for spawning on maps, namely Away Missions. - -//If someone can do this in a neater way, be my guest-Kor - -//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). - -/obj/effect/landmark/corpse - name = "Unknown" - var/mobname = "Unknown" //Unused now but it'd fuck up maps to remove it now - var/corpseuniform = null //Set this to an object path to have the slot filled with said object on the corpse. - var/corpsesuit = null - var/corpseshoes = null - var/corpsegloves = null - var/corpseradio = null - var/corpseglasses = null - var/corpsemask = null - var/corpsehelmet = null - var/corpsebelt = null - var/corpsepocket1 = null - var/corpsepocket2 = null - var/corpseback = null - var/corpseid = 0 //Just set to 1 if you want them to have an ID - var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access - var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access. - var/corpseidicon = null //For setting it to be a gold, silver, CentCom etc ID - var/species = SPECIES_HUMAN //defaults to generic-ass humans - var/random_species = FALSE //flip to TRUE to randomize species from the list below - var/list/random_species_list = list(SPECIES_HUMAN,SPECIES_TAJ,SPECIES_UNATHI,SPECIES_SKRELL) //preset list that can be overriden downstream. only includes common humanoids for voidsuit compatibility's sake. -// var/random_appearance = FALSE //TODO: make this work -// var/cause_of_death = null //TODO: set up a cause-of-death system. needs to support both damage types and actual wound types, so a body can have been bitten/stabbed/clawed/shot/burned/lasered/etc. to death - delete_me = TRUE - -/obj/effect/landmark/corpse/Initialize() - ..() - createCorpse() - return INITIALIZE_HINT_QDEL - -/obj/effect/landmark/corpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. - var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) - M.low_sorting_priority = TRUE - if(random_species) - var/random_pick = pick(random_species_list) - M.set_species(random_pick) - src.species = random_pick - else - M.set_species(species) - //TODO: insert appearance randomization, needs to be species-based - M.real_name = src.name - M.death(1) //Kills the new mob - //TODO: insert cause of death handling/wound simulation here - if(src.corpseuniform) - M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) - if(src.corpseshoes) - M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes) - if(src.corpsegloves) - M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves) - if(src.corpseradio) - M.equip_to_slot_or_del(new src.corpseradio(M), slot_l_ear) - if(src.corpseglasses) - M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses) - if(src.corpsemask) - M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask) - if(src.corpsebelt) - M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt) - if(src.corpsepocket1) - M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store) - if(src.corpsepocket2) - M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store) - if(src.corpseback) - M.equip_to_slot_or_del(new src.corpseback(M), slot_back) - if(src.corpseid == 1) - var/obj/item/weapon/card/id/W = new(M) - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(J.title == corpseidaccess) - jobdatum = J - break - if(src.corpseidicon) - W.icon_state = corpseidicon - if(src.corpseidaccess) - if(jobdatum) - W.access = jobdatum.get_access() - else - W.access = list() - if(corpseidjob) - W.assignment = corpseidjob - M.set_id_info(W) - M.equip_to_slot_or_del(W, slot_wear_id) - // Do suit last to avoid equipping issues - if(src.corpsehelmet) - M.equip_voidhelm_to_slot_or_del_with_refit(new src.corpsehelmet(M), slot_head, src.species) - if(src.corpsesuit) - M.equip_voidsuit_to_slot_or_del_with_refit(new src.corpsesuit(M), slot_wear_suit, src.species) - - - -// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example. - -/obj/effect/landmark/corpse/syndicatesoldier - name = "Mercenary" - corpseuniform = /obj/item/clothing/under/syndicate - corpsesuit = /obj/item/clothing/suit/armor/vest - corpseshoes = /obj/item/clothing/shoes/boots/swat - corpsegloves = /obj/item/clothing/gloves/swat - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas - corpsehelmet = /obj/item/clothing/head/helmet/swat - corpseback = /obj/item/weapon/storage/backpack - corpseid = 1 - corpseidjob = "Operative" - corpseidaccess = "Syndicate" - -/obj/effect/landmark/corpse/syndicatecommando - name = "Mercenary Commando" - corpseuniform = /obj/item/clothing/under/syndicate - corpsesuit = /obj/item/clothing/suit/space/void/merc - corpseshoes = /obj/item/clothing/shoes/boots/swat - corpsegloves = /obj/item/clothing/gloves/swat - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas/syndicate - corpsehelmet = /obj/item/clothing/head/helmet/space/void/merc - corpseback = /obj/item/weapon/tank/jetpack/oxygen - corpsepocket1 = /obj/item/weapon/tank/emergency/oxygen - corpseid = 1 - corpseidjob = "Operative" - corpseidaccess = "Syndicate" - -///////////Civilians////////////////////// - -/obj/effect/landmark/corpse/random_civ - name = "Civilian" - corpseuniform = /obj/item/clothing/under/color/grey - corpseshoes = /obj/item/clothing/shoes/black - random_species = TRUE - - - -/obj/effect/landmark/corpse/chef - name = "Chef" - corpseuniform = /obj/item/clothing/under/rank/chef - corpsesuit = /obj/item/clothing/suit/chef/classic - corpseshoes = /obj/item/clothing/shoes/black - corpsehelmet = /obj/item/clothing/head/chefhat - corpseback = /obj/item/weapon/storage/backpack - corpseradio = /obj/item/device/radio/headset - corpseid = 1 - corpseidjob = "Chef" - corpseidaccess = "Chef" - - -/obj/effect/landmark/corpse/doctor - name = "Doctor" - corpseradio = /obj/item/device/radio/headset/headset_med - corpseuniform = /obj/item/clothing/under/rank/medical - corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat - corpseback = /obj/item/weapon/storage/backpack/medic - corpsepocket1 = /obj/item/device/flashlight/pen - corpseshoes = /obj/item/clothing/shoes/black - corpseid = 1 - corpseidjob = "Medical Doctor" - corpseidaccess = "Medical Doctor" - -/obj/effect/landmark/corpse/engineer - name = "Engineer" - corpseradio = /obj/item/device/radio/headset/headset_eng - corpseuniform = /obj/item/clothing/under/rank/engineer - corpseback = /obj/item/weapon/storage/backpack/industrial - corpseshoes = /obj/item/clothing/shoes/orange - corpsebelt = /obj/item/weapon/storage/belt/utility/full - corpsegloves = /obj/item/clothing/gloves/yellow - corpsehelmet = /obj/item/clothing/head/hardhat - corpseid = 1 - corpseidjob = "Engineer" - corpseidaccess = "Engineer" - -/obj/effect/landmark/corpse/engineer/rig - corpsesuit = /obj/item/clothing/suit/space/void/engineering - corpsemask = /obj/item/clothing/mask/breath - corpsehelmet = /obj/item/clothing/head/helmet/space/void/engineering - corpseback = /obj/item/weapon/tank/oxygen - -/obj/effect/landmark/corpse/clown - name = "Clown" - corpseuniform = /obj/item/clothing/under/rank/clown - corpseshoes = /obj/item/clothing/shoes/clown_shoes - corpseradio = /obj/item/device/radio/headset - corpsemask = /obj/item/clothing/mask/gas/clown_hat - corpsepocket1 = /obj/item/weapon/bikehorn - corpseback = /obj/item/weapon/storage/backpack/clown - corpseid = 1 - corpseidjob = "Clown" - corpseidaccess = "Clown" - -/obj/effect/landmark/corpse/scientist - name = "Scientist" - corpseradio = /obj/item/device/radio/headset/headset_sci - corpseuniform = /obj/item/clothing/under/rank/scientist - corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat/science - corpseback = /obj/item/weapon/storage/backpack - corpseshoes = /obj/item/clothing/shoes/white - corpseid = 1 - corpseidjob = "Scientist" - corpseidaccess = "Scientist" - -/obj/effect/landmark/corpse/security - name = "Security Officer" - corpseradio = /obj/item/device/radio/headset/headset_sec - corpseuniform = /obj/item/clothing/under/rank/security - corpsesuit = /obj/item/clothing/suit/armor/vest - corpseback = /obj/item/weapon/storage/backpack/security - corpseshoes = /obj/item/clothing/shoes/boots/jackboots - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsegloves = /obj/item/clothing/gloves/black - corpsehelmet = /obj/item/clothing/head/helmet - corpseid = 1 - corpseidjob = "Security Officer" - corpseidaccess = "Security Officer" - -/obj/effect/landmark/corpse/security/rig - corpsesuit = /obj/item/clothing/suit/space/void/security - corpsemask = /obj/item/clothing/mask/breath - corpsehelmet = /obj/item/clothing/head/helmet/space/void/security - corpseback = /obj/item/weapon/tank/jetpack/oxygen - -/obj/effect/landmark/corpse/security/rig/eva - corpsesuit = /obj/item/clothing/suit/space/void/security/alt - corpsehelmet = /obj/item/clothing/head/helmet/space/void/security/alt - corpseidjob = "Starship Security Officer" - -/obj/effect/landmark/corpse/prisoner - name = "Unknown Prisoner" - corpseuniform = /obj/item/clothing/under/color/prison - corpseshoes = /obj/item/clothing/shoes/orange - corpseid = 1 - corpseidjob = "Prisoner" - random_species = TRUE - -/obj/effect/landmark/corpse/miner - corpseradio = /obj/item/device/radio/headset/headset_cargo - corpseuniform = /obj/item/clothing/under/rank/miner - corpsegloves = /obj/item/clothing/gloves/black - corpseback = /obj/item/weapon/storage/backpack/industrial - corpseshoes = /obj/item/clothing/shoes/black - corpseid = 1 - corpseidjob = "Shaft Miner" - corpseidaccess = "Shaft Miner" - -/obj/effect/landmark/corpse/miner/rig - corpsesuit = /obj/item/clothing/suit/space/void/mining - corpsemask = /obj/item/clothing/mask/breath - corpsehelmet = /obj/item/clothing/head/helmet/space/void/mining - corpseback = /obj/item/weapon/tank/oxygen - -/////////////////Vintage////////////////////// - -//define the basic props at this level and only change specifics for variants, e.z. -/obj/effect/landmark/corpse/vintage - name = "Unknown Crewmate" - corpseuniform = /obj/item/clothing/under/utility - corpsesuit = /obj/item/clothing/suit/space/void/refurb - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb - corpsemask = /obj/item/clothing/mask/breath - corpseback = /obj/item/weapon/tank/oxygen - corpseid = 1 - corpseidjob = "Crewmate" - -/obj/effect/landmark/corpse/vintage/engineering - name = "Unknown Engineer" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/engineering - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/engineering - corpsebelt = /obj/item/weapon/storage/belt/utility/full - corpseback = /obj/item/weapon/tank/oxygen/yellow - corpseidjob = "Engineer" - -/obj/effect/landmark/corpse/vintage/marine - name = "Unknown Marine" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/marine - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/marine - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseidjob = "Marine" - -/obj/effect/landmark/corpse/vintage/medical - name = "Unknown Medic" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/medical - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/medical - corpsebelt = /obj/item/weapon/storage/belt/medical - corpseidjob = "Medic" - -/obj/effect/landmark/corpse/vintage/mercenary - name = "Unknown Mercenary" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/mercenary - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/mercenary - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseback = /obj/item/weapon/tank/oxygen/red - corpseidjob = "Mercenary" - -/obj/effect/landmark/corpse/vintage/officer - name = "Unknown Captain" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/officer - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/officer - corpseback = /obj/item/weapon/tank/oxygen/yellow - corpseidjob = "Captain" - -/obj/effect/landmark/corpse/vintage/pilot - name = "Unknown Pilot" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/pilot - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/pilot - corpseidjob = "Pilot" - -/obj/effect/landmark/corpse/vintage/research - name = "Unknown Researcher" - corpsesuit = /obj/item/clothing/suit/space/void/refurb/research - corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/research - corpseidjob = "Researcher" - -/////////////////Officers////////////////////// - -/obj/effect/landmark/corpse/bridgeofficer - name = "Bridge Officer" - corpseradio = /obj/item/device/radio/headset/heads/hop - corpseuniform = /obj/item/clothing/under/rank/centcom_officer - corpsesuit = /obj/item/clothing/suit/armor/bulletproof - corpseshoes = /obj/item/clothing/shoes/black - corpseglasses = /obj/item/clothing/glasses/sunglasses - corpseid = 1 - corpseidjob = "Bridge Officer" - corpseidaccess = "Captain" - -/obj/effect/landmark/corpse/commander - name = "Commander" - corpseuniform = /obj/item/clothing/under/rank/centcom_captain - corpsesuit = /obj/item/clothing/suit/armor/bulletproof - corpseradio = /obj/item/device/radio/headset/heads/captain - corpseglasses = /obj/item/clothing/glasses/eyepatch - corpsemask = /obj/item/clothing/mask/smokable/cigarette/cigar/cohiba - corpsehelmet = /obj/item/clothing/head/centhat - corpsegloves = /obj/item/clothing/gloves/swat - corpseshoes = /obj/item/clothing/shoes/boots/swat - corpsepocket1 = /obj/item/weapon/flame/lighter/zippo - corpseid = 1 - corpseidjob = "Commander" - corpseidaccess = "Captain" - -/////////////////Lore Factions////////////////////// - -/obj/effect/landmark/corpse/sifguard - name = "Patrolman" - corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard - corpsesuit = /obj/item/clothing/suit/storage/hooded/wintercoat/solgov - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsemask = /obj/item/clothing/mask/balaclava - corpsehelmet = /obj/item/clothing/head/beret/solgov/sifguard - corpsegloves = /obj/item/clothing/gloves/duty - corpseshoes = /obj/item/clothing/shoes/boots/tactical - corpsepocket1 = /obj/item/clothing/accessory/armor/tag/sifguard - corpseid = 1 - corpseidjob = "Sif Defense Force Patrolman" - -/obj/effect/landmark/corpse/hedberg - name = "Hedberg-Hammarstrom Mercenary" - corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard - corpsesuit = /obj/item/clothing/suit/storage/vest/solgov/hedberg - corpsebelt = /obj/item/weapon/storage/belt/security - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsehelmet = /obj/item/clothing/head/beret/corp/hedberg - corpseshoes = /obj/item/clothing/shoes/boots/jackboots - corpseid = 1 - corpseidjob = "Hedberg-Hammarstrom Officer" - -/obj/effect/landmark/corpse/hedberg/merc - name = "Hedberg-Hammarstrom Mercenary" - corpsebelt = /obj/item/weapon/storage/belt/security/tactical - corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud - corpsehelmet = /obj/item/clothing/head/helmet/flexitac - corpsegloves = /obj/item/clothing/gloves/combat - corpseshoes = /obj/item/clothing/shoes/boots/tactical - corpseid = 1 - corpseidjob = "Hedberg-Hammarstrom Enforcer" - +//These are meant for spawning on maps, namely Away Missions. + +//If someone can do this in a neater way, be my guest-Kor + +//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now). + +/obj/effect/landmark/corpse + name = "Unknown" + var/mobname = "Unknown" //Unused now but it'd fuck up maps to remove it now + var/corpseuniform = null //Set this to an object path to have the slot filled with said object on the corpse. + var/corpsesuit = null + var/corpseshoes = null + var/corpsegloves = null + var/corpseradio = null + var/corpseglasses = null + var/corpsemask = null + var/corpsehelmet = null + var/corpsebelt = null + var/corpsepocket1 = null + var/corpsepocket2 = null + var/corpseback = null + var/corpseid = 0 //Just set to 1 if you want them to have an ID + var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access + var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access. + var/corpseidicon = null //For setting it to be a gold, silver, CentCom etc ID + var/species = SPECIES_HUMAN //defaults to generic-ass humans + var/random_species = FALSE //flip to TRUE to randomize species from the list below + var/list/random_species_list = list(SPECIES_HUMAN,SPECIES_TAJ,SPECIES_UNATHI,SPECIES_SKRELL) //preset list that can be overriden downstream. only includes common humanoids for voidsuit compatibility's sake. +// var/random_appearance = FALSE //TODO: make this work +// var/cause_of_death = null //TODO: set up a cause-of-death system. needs to support both damage types and actual wound types, so a body can have been bitten/stabbed/clawed/shot/burned/lasered/etc. to death + delete_me = TRUE + +/obj/effect/landmark/corpse/Initialize() + ..() + createCorpse() + return INITIALIZE_HINT_QDEL + +/obj/effect/landmark/corpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. + var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) + M.low_sorting_priority = TRUE + if(random_species) + var/random_pick = pick(random_species_list) + M.set_species(random_pick) + src.species = random_pick + else + M.set_species(species) + //TODO: insert appearance randomization, needs to be species-based + M.real_name = src.name + M.death(1) //Kills the new mob + //TODO: insert cause of death handling/wound simulation here + if(src.corpseuniform) + M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) + if(src.corpseshoes) + M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes) + if(src.corpsegloves) + M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves) + if(src.corpseradio) + M.equip_to_slot_or_del(new src.corpseradio(M), slot_l_ear) + if(src.corpseglasses) + M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses) + if(src.corpsemask) + M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask) + if(src.corpsebelt) + M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt) + if(src.corpsepocket1) + M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store) + if(src.corpsepocket2) + M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store) + if(src.corpseback) + M.equip_to_slot_or_del(new src.corpseback(M), slot_back) + if(src.corpseid == 1) + var/obj/item/weapon/card/id/W = new(M) + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(J.title == corpseidaccess) + jobdatum = J + break + if(src.corpseidicon) + W.icon_state = corpseidicon + if(src.corpseidaccess) + if(jobdatum) + W.access = jobdatum.get_access() + else + W.access = list() + if(corpseidjob) + W.assignment = corpseidjob + M.set_id_info(W) + M.equip_to_slot_or_del(W, slot_wear_id) + // Do suit last to avoid equipping issues + if(src.corpsehelmet) + M.equip_voidhelm_to_slot_or_del_with_refit(new src.corpsehelmet(M), slot_head, src.species) + if(src.corpsesuit) + M.equip_voidsuit_to_slot_or_del_with_refit(new src.corpsesuit(M), slot_wear_suit, src.species) + + + +// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example. + +/obj/effect/landmark/corpse/syndicatesoldier + name = "Mercenary" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/armor/vest + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsegloves = /obj/item/clothing/gloves/swat + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas + corpsehelmet = /obj/item/clothing/head/helmet/swat + corpseback = /obj/item/weapon/storage/backpack + corpseid = 1 + corpseidjob = "Operative" + corpseidaccess = "Syndicate" + +/obj/effect/landmark/corpse/syndicatecommando + name = "Mercenary Commando" + corpseuniform = /obj/item/clothing/under/syndicate + corpsesuit = /obj/item/clothing/suit/space/void/merc + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsegloves = /obj/item/clothing/gloves/swat + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas/syndicate + corpsehelmet = /obj/item/clothing/head/helmet/space/void/merc + corpseback = /obj/item/weapon/tank/jetpack/oxygen + corpsepocket1 = /obj/item/weapon/tank/emergency/oxygen + corpseid = 1 + corpseidjob = "Operative" + corpseidaccess = "Syndicate" + +///////////Civilians////////////////////// + +/obj/effect/landmark/corpse/random_civ + name = "Civilian" + corpseuniform = /obj/item/clothing/under/color/grey + corpseshoes = /obj/item/clothing/shoes/black + random_species = TRUE + + + +/obj/effect/landmark/corpse/chef + name = "Chef" + corpseuniform = /obj/item/clothing/under/rank/chef + corpsesuit = /obj/item/clothing/suit/chef/classic + corpseshoes = /obj/item/clothing/shoes/black + corpsehelmet = /obj/item/clothing/head/chefhat + corpseback = /obj/item/weapon/storage/backpack + corpseradio = /obj/item/device/radio/headset + corpseid = 1 + corpseidjob = "Chef" + corpseidaccess = "Chef" + + +/obj/effect/landmark/corpse/doctor + name = "Doctor" + corpseradio = /obj/item/device/radio/headset/headset_med + corpseuniform = /obj/item/clothing/under/rank/medical + corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat + corpseback = /obj/item/weapon/storage/backpack/medic + corpsepocket1 = /obj/item/device/flashlight/pen + corpseshoes = /obj/item/clothing/shoes/black + corpseid = 1 + corpseidjob = "Medical Doctor" + corpseidaccess = "Medical Doctor" + +/obj/effect/landmark/corpse/engineer + name = "Engineer" + corpseradio = /obj/item/device/radio/headset/headset_eng + corpseuniform = /obj/item/clothing/under/rank/engineer + corpseback = /obj/item/weapon/storage/backpack/industrial + corpseshoes = /obj/item/clothing/shoes/orange + corpsebelt = /obj/item/weapon/storage/belt/utility/full + corpsegloves = /obj/item/clothing/gloves/yellow + corpsehelmet = /obj/item/clothing/head/hardhat + corpseid = 1 + corpseidjob = "Engineer" + corpseidaccess = "Engineer" + +/obj/effect/landmark/corpse/engineer/rig + corpsesuit = /obj/item/clothing/suit/space/void/engineering + corpsemask = /obj/item/clothing/mask/breath + corpsehelmet = /obj/item/clothing/head/helmet/space/void/engineering + corpseback = /obj/item/weapon/tank/oxygen + +/obj/effect/landmark/corpse/clown + name = "Clown" + corpseuniform = /obj/item/clothing/under/rank/clown + corpseshoes = /obj/item/clothing/shoes/clown_shoes + corpseradio = /obj/item/device/radio/headset + corpsemask = /obj/item/clothing/mask/gas/clown_hat + corpsepocket1 = /obj/item/weapon/bikehorn + corpseback = /obj/item/weapon/storage/backpack/clown + corpseid = 1 + corpseidjob = "Clown" + corpseidaccess = "Clown" + +/obj/effect/landmark/corpse/scientist + name = "Scientist" + corpseradio = /obj/item/device/radio/headset/headset_sci + corpseuniform = /obj/item/clothing/under/rank/scientist + corpsesuit = /obj/item/clothing/suit/storage/toggle/labcoat/science + corpseback = /obj/item/weapon/storage/backpack + corpseshoes = /obj/item/clothing/shoes/white + corpseid = 1 + corpseidjob = "Scientist" + corpseidaccess = "Scientist" + +/obj/effect/landmark/corpse/security + name = "Security Officer" + corpseradio = /obj/item/device/radio/headset/headset_sec + corpseuniform = /obj/item/clothing/under/rank/security + corpsesuit = /obj/item/clothing/suit/armor/vest + corpseback = /obj/item/weapon/storage/backpack/security + corpseshoes = /obj/item/clothing/shoes/boots/jackboots + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsegloves = /obj/item/clothing/gloves/black + corpsehelmet = /obj/item/clothing/head/helmet + corpseid = 1 + corpseidjob = "Security Officer" + corpseidaccess = "Security Officer" + +/obj/effect/landmark/corpse/security/rig + corpsesuit = /obj/item/clothing/suit/space/void/security + corpsemask = /obj/item/clothing/mask/breath + corpsehelmet = /obj/item/clothing/head/helmet/space/void/security + corpseback = /obj/item/weapon/tank/jetpack/oxygen + +/obj/effect/landmark/corpse/security/rig/eva + corpsesuit = /obj/item/clothing/suit/space/void/security/alt + corpsehelmet = /obj/item/clothing/head/helmet/space/void/security/alt + corpseidjob = "Starship Security Officer" + +/obj/effect/landmark/corpse/prisoner + name = "Unknown Prisoner" + corpseuniform = /obj/item/clothing/under/color/prison + corpseshoes = /obj/item/clothing/shoes/orange + corpseid = 1 + corpseidjob = "Prisoner" + random_species = TRUE + +/obj/effect/landmark/corpse/miner + corpseradio = /obj/item/device/radio/headset/headset_cargo + corpseuniform = /obj/item/clothing/under/rank/miner + corpsegloves = /obj/item/clothing/gloves/black + corpseback = /obj/item/weapon/storage/backpack/industrial + corpseshoes = /obj/item/clothing/shoes/black + corpseid = 1 + corpseidjob = "Shaft Miner" + corpseidaccess = "Shaft Miner" + +/obj/effect/landmark/corpse/miner/rig + corpsesuit = /obj/item/clothing/suit/space/void/mining + corpsemask = /obj/item/clothing/mask/breath + corpsehelmet = /obj/item/clothing/head/helmet/space/void/mining + corpseback = /obj/item/weapon/tank/oxygen + +/////////////////Vintage////////////////////// + +//define the basic props at this level and only change specifics for variants, e.z. +/obj/effect/landmark/corpse/vintage + name = "Unknown Crewmate" + corpseuniform = /obj/item/clothing/under/utility + corpsesuit = /obj/item/clothing/suit/space/void/refurb + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb + corpsemask = /obj/item/clothing/mask/breath + corpseback = /obj/item/weapon/tank/oxygen + corpseid = 1 + corpseidjob = "Crewmate" + +/obj/effect/landmark/corpse/vintage/engineering + name = "Unknown Engineer" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/engineering + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/engineering + corpsebelt = /obj/item/weapon/storage/belt/utility/full + corpseback = /obj/item/weapon/tank/oxygen/yellow + corpseidjob = "Engineer" + +/obj/effect/landmark/corpse/vintage/marine + name = "Unknown Marine" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/marine + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/marine + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseidjob = "Marine" + +/obj/effect/landmark/corpse/vintage/medical + name = "Unknown Medic" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/medical + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/medical + corpsebelt = /obj/item/weapon/storage/belt/medical + corpseidjob = "Medic" + +/obj/effect/landmark/corpse/vintage/mercenary + name = "Unknown Mercenary" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/mercenary + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/mercenary + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseback = /obj/item/weapon/tank/oxygen/red + corpseidjob = "Mercenary" + +/obj/effect/landmark/corpse/vintage/officer + name = "Unknown Captain" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/officer + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/officer + corpseback = /obj/item/weapon/tank/oxygen/yellow + corpseidjob = "Captain" + +/obj/effect/landmark/corpse/vintage/pilot + name = "Unknown Pilot" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/pilot + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/pilot + corpseidjob = "Pilot" + +/obj/effect/landmark/corpse/vintage/research + name = "Unknown Researcher" + corpsesuit = /obj/item/clothing/suit/space/void/refurb/research + corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/research + corpseidjob = "Researcher" + +/////////////////Officers////////////////////// + +/obj/effect/landmark/corpse/bridgeofficer + name = "Bridge Officer" + corpseradio = /obj/item/device/radio/headset/heads/hop + corpseuniform = /obj/item/clothing/under/rank/centcom_officer + corpsesuit = /obj/item/clothing/suit/armor/bulletproof + corpseshoes = /obj/item/clothing/shoes/black + corpseglasses = /obj/item/clothing/glasses/sunglasses + corpseid = 1 + corpseidjob = "Bridge Officer" + corpseidaccess = "Captain" + +/obj/effect/landmark/corpse/commander + name = "Commander" + corpseuniform = /obj/item/clothing/under/rank/centcom_captain + corpsesuit = /obj/item/clothing/suit/armor/bulletproof + corpseradio = /obj/item/device/radio/headset/heads/captain + corpseglasses = /obj/item/clothing/glasses/eyepatch + corpsemask = /obj/item/clothing/mask/smokable/cigarette/cigar/cohiba + corpsehelmet = /obj/item/clothing/head/centhat + corpsegloves = /obj/item/clothing/gloves/swat + corpseshoes = /obj/item/clothing/shoes/boots/swat + corpsepocket1 = /obj/item/weapon/flame/lighter/zippo + corpseid = 1 + corpseidjob = "Commander" + corpseidaccess = "Captain" + +/////////////////Lore Factions////////////////////// + +/obj/effect/landmark/corpse/sifguard + name = "Patrolman" + corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard + corpsesuit = /obj/item/clothing/suit/storage/hooded/wintercoat/solgov + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsemask = /obj/item/clothing/mask/balaclava + corpsehelmet = /obj/item/clothing/head/beret/solgov/sifguard + corpsegloves = /obj/item/clothing/gloves/duty + corpseshoes = /obj/item/clothing/shoes/boots/tactical + corpsepocket1 = /obj/item/clothing/accessory/armor/tag/sifguard + corpseid = 1 + corpseidjob = "Sif Defense Force Patrolman" + +/obj/effect/landmark/corpse/hedberg + name = "Hedberg-Hammarstrom Mercenary" + corpseuniform = /obj/item/clothing/under/solgov/utility/sifguard + corpsesuit = /obj/item/clothing/suit/storage/vest/solgov/hedberg + corpsebelt = /obj/item/weapon/storage/belt/security + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsehelmet = /obj/item/clothing/head/beret/corp/hedberg + corpseshoes = /obj/item/clothing/shoes/boots/jackboots + corpseid = 1 + corpseidjob = "Hedberg-Hammarstrom Officer" + +/obj/effect/landmark/corpse/hedberg/merc + name = "Hedberg-Hammarstrom Mercenary" + corpsebelt = /obj/item/weapon/storage/belt/security/tactical + corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud + corpsehelmet = /obj/item/clothing/head/helmet/flexitac + corpsegloves = /obj/item/clothing/gloves/combat + corpseshoes = /obj/item/clothing/shoes/boots/tactical + corpseid = 1 + corpseidjob = "Hedberg-Hammarstrom Enforcer" + diff --git a/code/modules/awaymissions/exile.dm b/code/modules/awaymissions/exile.dm index ef19bb0c49c..1292812a77e 100644 --- a/code/modules/awaymissions/exile.dm +++ b/code/modules/awaymissions/exile.dm @@ -1,41 +1,41 @@ -//////Exile implants will allow you to use the station gate, but not return home. This will allow security to exile badguys/for badguys to exile their kill targets//////// - - -/obj/item/weapon/implanter/exile - name = "implanter-exile" - -/obj/item/weapon/implanter/exile/New() - src.imp = new /obj/item/weapon/implant/exile( src ) - ..() - update() - return - - -/obj/item/weapon/implant/exile - name = "exile" - desc = "Prevents you from returning from away missions" - -/obj/item/weapon/implant/exile/get_data() - var/dat = {" -Implant Specifications:
                    -Name: [using_map.company_name] Employee Exile Implant
                    -Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
                    "} - return dat - -/obj/item/weapon/implantcase/exile - name = "Glass Case- 'Exile'" - desc = "A case containing an exile implant." - icon = 'icons/obj/items.dmi' - icon_state = "implantcase-r" - - -/obj/item/weapon/implantcase/exile/New() - src.imp = new /obj/item/weapon/implant/exile( src ) - ..() - return - - -/obj/structure/closet/secure_closet/exile - name = "Exile Implants" - req_access = list(access_hos) - starts_with = list(/obj/item/weapon/implanter/exile = 1, /obj/item/weapon/implantcase/exile = 5) +//////Exile implants will allow you to use the station gate, but not return home. This will allow security to exile badguys/for badguys to exile their kill targets//////// + + +/obj/item/weapon/implanter/exile + name = "implanter-exile" + +/obj/item/weapon/implanter/exile/New() + src.imp = new /obj/item/weapon/implant/exile( src ) + ..() + update() + return + + +/obj/item/weapon/implant/exile + name = "exile" + desc = "Prevents you from returning from away missions" + +/obj/item/weapon/implant/exile/get_data() + var/dat = {" +Implant Specifications:
                    +Name: [using_map.company_name] Employee Exile Implant
                    +Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
                    "} + return dat + +/obj/item/weapon/implantcase/exile + name = "Glass Case- 'Exile'" + desc = "A case containing an exile implant." + icon = 'icons/obj/items.dmi' + icon_state = "implantcase-r" + + +/obj/item/weapon/implantcase/exile/New() + src.imp = new /obj/item/weapon/implant/exile( src ) + ..() + return + + +/obj/structure/closet/secure_closet/exile + name = "Exile Implants" + req_access = list(access_hos) + starts_with = list(/obj/item/weapon/implanter/exile = 1, /obj/item/weapon/implantcase/exile = 5) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 6474e008b77..66d36212615 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -1,367 +1,367 @@ -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." //VOREStation Edit - icon = 'icons/obj/machines/gateway.dmi' - icon_state = "off" - density = TRUE - anchored = TRUE - unacidable = TRUE - var/active = 0 - - -/obj/machinery/gateway/Initialize() - update_icon() - if(dir == SOUTH) - density = FALSE - . = ..() - -/obj/machinery/gateway/update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -GLOBAL_DATUM(gateway_station, /obj/machinery/gateway/centerstation) -/obj/machinery/gateway/centerstation - density = TRUE - icon_state = "offcenter" - use_power = USE_POWER_IDLE - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/Initialize() - if(GLOB.gateway_station) - warning("[src] at [x],[y],[z] appears to be an additional station-gateway") - else - GLOB.gateway_station = src - - update_icon() - wait = world.time + config.gateway_delay //+ thirty minutes default - - if(GLOB.gateway_away) - awaygate = GLOB.gateway_away - else - awaygate = locate(/obj/machinery/gateway/centeraway) - - . = ..() - density = TRUE //VOREStation Add - -/obj/machinery/gateway/centerstation/Destroy() - if(awaygate?.stationgate == src) - awaygate.stationgate = null - if(GLOB.gateway_station == src) - GLOB.gateway_station = null - return ..() - -/obj/machinery/gateway/centerstation/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" -/* VOREStation Removal - Doesn't do anything -/obj/machinery/gateway/centerstation/New() - density = TRUE -*/ //VOREStation Removal End - -/obj/machinery/gateway/centerstation/process() - if(stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!powered()) return - if(!awaygate) - to_chat(user, "Error: No destination found. Please program gateway.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) //VOREStation Edit - to_chat(user, "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(!awaygate) return - - use_power(5000) - M << 'sound/effects/phasein.ogg' - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.set_dir(SOUTH) - return - else - //VOREStation Addition Start: Prevent abuse - if(istype(M, /obj/item/device/uav)) - var/obj/item/device/uav/L = M - L.power_down() - if(istype(M, /mob/living)) - var/mob/living/L = M - if(LAZYLEN(L.buckled_mobs)) - var/datum/riding/R = L.riding_datum - for(var/rider in L.buckled_mobs) - R.force_dismount(rider) - //VOREStation Addition End: Prevent abuse - var/obj/effect/landmark/dest = pick(awaydestinations) - if(dest) - M.forceMove(dest.loc) - M.set_dir(SOUTH) - //VOREStation Addition Start: Mcguffin time! - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.client) - awaygate.entrydetect() - //VOREStation Addition End: Mcguffin time! - - //VOREStation Addition Start: Abduction! - if(istype(M, /mob/living) && dest.abductor) - var/mob/living/L = M - if(L.nutrition > 500) - L.nutrition = 500 //If the aim is to negate people overpreparing, then they shouldn't be able to stuff themselves full of food either. - //Situations to get the mob out of - if(L.buckled) - L.buckled.unbuckle_mob() - if(istype(L.loc,/obj/mecha)) - var/obj/mecha/ME = L.loc - ME.go_out() - else if(istype(L.loc,/obj/machinery/sleeper)) - var/obj/machinery/sleeper/SL = L.loc - SL.go_out() - else if(istype(L.loc,/obj/machinery/recharge_station)) - var/obj/machinery/recharge_station/RS = L.loc - RS.go_out() - if(!issilicon(L)) //Don't drop borg modules... - var/list/mob_contents = list() //Things which are actually drained as a result of the above not being null. - mob_contents |= L // The recursive check below does not add the object being checked to its list. - mob_contents |= recursive_content_check(L, mob_contents, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1, ignore_show_messages = 1) - for(var/obj/item/weapon/holder/I in mob_contents) - var/obj/item/weapon/holder/H = I - var/mob/living/MI = H.held_mob - MI.forceMove(get_turf(H)) - if(!issilicon(MI)) //Don't drop borg modules... - for(var/obj/item/II in MI) - if(istype(II,/obj/item/weapon/implant) || istype(II,/obj/item/device/nif)) - continue - MI.drop_from_inventory(II, dest.loc) - var/obj/effect/landmark/finaldest = pick(awayabductors) - MI.forceMove(finaldest.loc) - sleep(1) - MI.Paralyse(10) - MI << 'sound/effects/bamf.ogg' - to_chat(MI,"You're starting to come to. You feel like you've been out for a few minutes, at least...") - for(var/obj/item/I in L) - if(istype(I,/obj/item/weapon/implant) || istype(I,/obj/item/device/nif)) - continue - L.drop_from_inventory(I, dest.loc) - var/obj/effect/landmark/finaldest = pick(awayabductors) - L.forceMove(finaldest.loc) - sleep(1) - L.Paralyse(10) - L << 'sound/effects/bamf.ogg' - to_chat(L,"You're starting to come to. You feel like you've been out for a few minutes, at least...") - //VOREStation Addition End - return - -/obj/machinery/gateway/centerstation/attackby(obj/item/device/W as obj, mob/user as mob) - if(istype(W,/obj/item/device/multitool)) - if(!awaygate) - if(GLOB.gateway_away) - awaygate = GLOB.gateway_away - else - awaygate = locate(/obj/machinery/gateway/centeraway) - if(!awaygate) // We still can't find the damn thing because there is no destination. - to_chat(user, "Error: Programming failed. No destination found.") - return - to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") - else - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - -/////////////////////////////////////Away//////////////////////// -GLOBAL_DATUM(gateway_away, /obj/machinery/gateway/centeraway) -/obj/machinery/gateway/centeraway - density = TRUE - icon_state = "offcenter" - use_power = USE_POWER_OFF - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centerstation/stationgate = null - -/obj/machinery/gateway/centeraway/New() - density = TRUE - -/obj/machinery/gateway/centeraway/Initialize() - if(GLOB.gateway_away) - warning("[src] at [x],[y],[z] appears to be an additional away-gateway") - else - GLOB.gateway_away = src - - update_icon() - - if(GLOB.gateway_station) - stationgate = GLOB.gateway_station - else - stationgate = locate(/obj/machinery/gateway/centerstation) - . = ..() - density = TRUE //VOREStation Add - -/obj/machinery/gateway/centeraway/Destroy() - if(stationgate?.awaygate == src) - stationgate.awaygate = null - if(GLOB.gateway_away == src) - GLOB.gateway_away = null - return ..() - -/obj/machinery/gateway/centeraway/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!stationgate || !calibrated) // Vorestation edit. Not like Polaris ever touches this anyway. - to_chat(user, "Error: No destination found. Please calibrate gateway.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(istype(M, /mob/living/carbon)) - for(var/obj/item/weapon/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") - return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.set_dir(SOUTH) - M << 'sound/effects/phasein.ogg' - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - - -/obj/machinery/gateway/centeraway/attackby(obj/item/device/W as obj, mob/user as mob) - if(istype(W,/obj/item/device/multitool)) - if(calibrated && stationgate) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - else - // VOREStation Add - if(GLOB.gateway_station) - stationgate = GLOB.gateway_station - else - stationgate = locate(/obj/machinery/gateway/centerstation) - if(!stationgate) - to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") - return - // VOREStation Add End - else - to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") - calibrated = 1 - return \ No newline at end of file +/obj/machinery/gateway + name = "gateway" + desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." //VOREStation Edit + icon = 'icons/obj/machines/gateway.dmi' + icon_state = "off" + density = TRUE + anchored = TRUE + unacidable = TRUE + var/active = 0 + + +/obj/machinery/gateway/Initialize() + update_icon() + if(dir == SOUTH) + density = FALSE + . = ..() + +/obj/machinery/gateway/update_icon() + if(active) + icon_state = "on" + return + icon_state = "off" + + + +//this is da important part wot makes things go +GLOBAL_DATUM(gateway_station, /obj/machinery/gateway/centerstation) +/obj/machinery/gateway/centerstation + density = TRUE + icon_state = "offcenter" + use_power = USE_POWER_IDLE + + //warping vars + var/list/linked = list() + var/ready = 0 //have we got all the parts for a gateway? + var/wait = 0 //this just grabs world.time at world start + var/obj/machinery/gateway/centeraway/awaygate = null + +/obj/machinery/gateway/centerstation/Initialize() + if(GLOB.gateway_station) + warning("[src] at [x],[y],[z] appears to be an additional station-gateway") + else + GLOB.gateway_station = src + + update_icon() + wait = world.time + config.gateway_delay //+ thirty minutes default + + if(GLOB.gateway_away) + awaygate = GLOB.gateway_away + else + awaygate = locate(/obj/machinery/gateway/centeraway) + + . = ..() + density = TRUE //VOREStation Add + +/obj/machinery/gateway/centerstation/Destroy() + if(awaygate?.stationgate == src) + awaygate.stationgate = null + if(GLOB.gateway_station == src) + GLOB.gateway_station = null + return ..() + +/obj/machinery/gateway/centerstation/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" +/* VOREStation Removal - Doesn't do anything +/obj/machinery/gateway/centerstation/New() + density = TRUE +*/ //VOREStation Removal End + +/obj/machinery/gateway/centerstation/process() + if(stat & (NOPOWER)) + if(active) toggleoff() + return + + if(active) + use_power(5000) + + +/obj/machinery/gateway/centerstation/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!powered()) return + if(!awaygate) + to_chat(user, "Error: No destination found. Please program gateway.") + return + if(world.time < wait) + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") + return + if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) //VOREStation Edit + to_chat(user, "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centerstation/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +//okay, here's the good teleporting stuff +/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(!awaygate) return + + use_power(5000) + M << 'sound/effects/phasein.ogg' + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + if(awaygate.calibrated) + M.forceMove(get_step(awaygate.loc, SOUTH)) + M.set_dir(SOUTH) + return + else + //VOREStation Addition Start: Prevent abuse + if(istype(M, /obj/item/device/uav)) + var/obj/item/device/uav/L = M + L.power_down() + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent abuse + var/obj/effect/landmark/dest = pick(awaydestinations) + if(dest) + M.forceMove(dest.loc) + M.set_dir(SOUTH) + //VOREStation Addition Start: Mcguffin time! + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.client) + awaygate.entrydetect() + //VOREStation Addition End: Mcguffin time! + + //VOREStation Addition Start: Abduction! + if(istype(M, /mob/living) && dest.abductor) + var/mob/living/L = M + if(L.nutrition > 500) + L.nutrition = 500 //If the aim is to negate people overpreparing, then they shouldn't be able to stuff themselves full of food either. + //Situations to get the mob out of + if(L.buckled) + L.buckled.unbuckle_mob() + if(istype(L.loc,/obj/mecha)) + var/obj/mecha/ME = L.loc + ME.go_out() + else if(istype(L.loc,/obj/machinery/sleeper)) + var/obj/machinery/sleeper/SL = L.loc + SL.go_out() + else if(istype(L.loc,/obj/machinery/recharge_station)) + var/obj/machinery/recharge_station/RS = L.loc + RS.go_out() + if(!issilicon(L)) //Don't drop borg modules... + var/list/mob_contents = list() //Things which are actually drained as a result of the above not being null. + mob_contents |= L // The recursive check below does not add the object being checked to its list. + mob_contents |= recursive_content_check(L, mob_contents, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1, ignore_show_messages = 1) + for(var/obj/item/weapon/holder/I in mob_contents) + var/obj/item/weapon/holder/H = I + var/mob/living/MI = H.held_mob + MI.forceMove(get_turf(H)) + if(!issilicon(MI)) //Don't drop borg modules... + for(var/obj/item/II in MI) + if(istype(II,/obj/item/weapon/implant) || istype(II,/obj/item/device/nif)) + continue + MI.drop_from_inventory(II, dest.loc) + var/obj/effect/landmark/finaldest = pick(awayabductors) + MI.forceMove(finaldest.loc) + sleep(1) + MI.Paralyse(10) + MI << 'sound/effects/bamf.ogg' + to_chat(MI,"You're starting to come to. You feel like you've been out for a few minutes, at least...") + for(var/obj/item/I in L) + if(istype(I,/obj/item/weapon/implant) || istype(I,/obj/item/device/nif)) + continue + L.drop_from_inventory(I, dest.loc) + var/obj/effect/landmark/finaldest = pick(awayabductors) + L.forceMove(finaldest.loc) + sleep(1) + L.Paralyse(10) + L << 'sound/effects/bamf.ogg' + to_chat(L,"You're starting to come to. You feel like you've been out for a few minutes, at least...") + //VOREStation Addition End + return + +/obj/machinery/gateway/centerstation/attackby(obj/item/device/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/multitool)) + if(!awaygate) + if(GLOB.gateway_away) + awaygate = GLOB.gateway_away + else + awaygate = locate(/obj/machinery/gateway/centeraway) + if(!awaygate) // We still can't find the damn thing because there is no destination. + to_chat(user, "Error: Programming failed. No destination found.") + return + to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") + else + to_chat(user, span_black("The gate is already calibrated, there is no work for you to do here.")) + return + +/////////////////////////////////////Away//////////////////////// +GLOBAL_DATUM(gateway_away, /obj/machinery/gateway/centeraway) +/obj/machinery/gateway/centeraway + density = TRUE + icon_state = "offcenter" + use_power = USE_POWER_OFF + var/calibrated = 1 + var/list/linked = list() //a list of the connected gateway chunks + var/ready = 0 + var/obj/machinery/gateway/centerstation/stationgate = null + +/obj/machinery/gateway/centeraway/New() + density = TRUE + +/obj/machinery/gateway/centeraway/Initialize() + if(GLOB.gateway_away) + warning("[src] at [x],[y],[z] appears to be an additional away-gateway") + else + GLOB.gateway_away = src + + update_icon() + + if(GLOB.gateway_station) + stationgate = GLOB.gateway_station + else + stationgate = locate(/obj/machinery/gateway/centerstation) + . = ..() + density = TRUE //VOREStation Add + +/obj/machinery/gateway/centeraway/Destroy() + if(stationgate?.awaygate == src) + stationgate.awaygate = null + if(GLOB.gateway_away == src) + GLOB.gateway_away = null + return ..() + +/obj/machinery/gateway/centeraway/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + +/obj/machinery/gateway/centeraway/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!stationgate || !calibrated) // Vorestation edit. Not like Polaris ever touches this anyway. + to_chat(user, "Error: No destination found. Please calibrate gateway.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centeraway/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(istype(M, /mob/living/carbon)) + for(var/obj/item/weapon/implant/exile/E in M)//Checking that there is an exile implant in the contents + if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket + to_chat(M, span_black("The station gate has detected your exile implant and is blocking your entry.")) + return + M.forceMove(get_step(stationgate.loc, SOUTH)) + M.set_dir(SOUTH) + M << 'sound/effects/phasein.ogg' + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + + +/obj/machinery/gateway/centeraway/attackby(obj/item/device/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/multitool)) + if(calibrated && stationgate) + to_chat(user, span_black("The gate is already calibrated, there is no work for you to do here.")) + return + else + // VOREStation Add + if(GLOB.gateway_station) + stationgate = GLOB.gateway_station + else + stationgate = locate(/obj/machinery/gateway/centerstation) + if(!stationgate) + to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") + return + // VOREStation Add End + else + to_chat(user, span_blue("Recalibration successful!:") + span_black(" This gate's systems have been fine tuned. Travel to this gate will now be on target.")) + calibrated = 1 + return diff --git a/code/modules/awaymissions/gateway_vr.dm b/code/modules/awaymissions/gateway_vr.dm index 86ff8e5e135..05def055827 100644 --- a/code/modules/awaymissions/gateway_vr.dm +++ b/code/modules/awaymissions/gateway_vr.dm @@ -1,79 +1,79 @@ -//This gateway type takes a special item that you will have to find on the map to activate, instead of using a multitool// - -/obj/machinery/gateway/centeraway/mcguffin - icon = 'icons/obj/machines/gateway_vr.dmi' - calibrated = 0 - var/mcguffin_type = /obj/item/device/mcguffin/brass //you should be able to change the var to be whatever kind of path you like, so maybe you can use other things on it sometimes - var/key //holds a ref to the key we spawned - -/obj/machinery/gateway/centeraway/mcguffin/attackby(obj/item/device/W as obj, mob/user as mob) - if(calibrated && stationgate) - to_chat(user, "The gate is already configured, you should be able to activate it.") - return - else if(!stationgate) - to_chat(user, "Error: Configuration failed. No destination found... That can't be good.") - return - - if(istype(W,mcguffin_type) && !calibrated) - to_chat(user, "As the device nears the gateway, mechanical clunks and whirrs can be heard.
                    Configuration successful!
                    This gate's systems have been fine tuned. Travel to this gate will now be on target.
                    ") - calibrated = 1 - return - else - to_chat(user, "This device does not seem to interface correctly with the gateway. Perhaps you should try something else.") - return - -//If you use this kind of gateway you NEED one of these on the map or the players won't be able to leave// -//You should use the random spawner though so it won't always be in the same place// -/obj/item/device/mcguffin/brass - name = "mysterious brass device" - desc = "A curious object made of what appears to be brass and silver. Its purpose is unclear by looking at it. Perhaps it should be used with something of similar materials?" - icon = 'icons/obj/machines/gateway_vr.dmi' - icon_state = "mcguffin" - drop_sound = 'sound/items/drop/wrench.ogg' - pickup_sound = 'sound/items/pickup/wrench.ogg' - -/obj/effect/landmark/mcguffin_spawner - name = "gateway key spawner" - icon = 'icons/mob/randomlandmarks.dmi' - icon_state = "key" - -/obj/machinery/gateway/centeraway/proc/entrydetect() - return - -/obj/machinery/gateway/centeraway/mcguffin/entrydetect() - if(key) - return - - var/list/spawners = list() - for(var/obj/effect/landmark/mcguffin_spawner/sp in world) - spawners += sp - - var/obj/effect/landmark/mcguffin_spawner/the_cool_one = pick(spawners) - - var/atom/destination = get_turf(the_cool_one) - var/obj/structure/closet/CL = locate() in destination - if(CL) - destination = CL - - if(!destination) - warning("A gateway is trying to spawn it's mcguffin but there are no mapped in spawner landmarks") - destination = get_turf(src) - - key = new mcguffin_type(destination) - -/obj/machinery/gateway/centeraway/mcguffin/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.set_dir(SOUTH) - M << 'sound/effects/swooshygate.ogg' - playsound(src, 'sound/effects/swooshygate.ogg', 100, 1) - -/obj/machinery/gateway/brass - name = "mysterious brass gateway" - desc = "A gateway of strange construction. It appears to be made primarily of materials resembling brass and silver." - icon = 'icons/obj/machines/gateway_vr.dmi' - -//No, you can't digest the key to leave the gateway. -/obj/item/device/mcguffin/digest_act(var/atom/movable/item_storage = null) - return FALSE \ No newline at end of file +//This gateway type takes a special item that you will have to find on the map to activate, instead of using a multitool// + +/obj/machinery/gateway/centeraway/mcguffin + icon = 'icons/obj/machines/gateway_vr.dmi' + calibrated = 0 + var/mcguffin_type = /obj/item/device/mcguffin/brass //you should be able to change the var to be whatever kind of path you like, so maybe you can use other things on it sometimes + var/key //holds a ref to the key we spawned + +/obj/machinery/gateway/centeraway/mcguffin/attackby(obj/item/device/W as obj, mob/user as mob) + if(calibrated && stationgate) + to_chat(user, "The gate is already configured, you should be able to activate it.") + return + else if(!stationgate) + to_chat(user, "Error: Configuration failed. No destination found... That can't be good.") + return + + if(istype(W,mcguffin_type) && !calibrated) + to_chat(user, "As the device nears the gateway, mechanical clunks and whirrs can be heard.
                    [span_blue("Configuration successful! ")]
                    This gate's systems have been fine tuned. Travel to this gate will now be on target.
                    ") + calibrated = 1 + return + else + to_chat(user, "This device does not seem to interface correctly with the gateway. Perhaps you should try something else.") + return + +//If you use this kind of gateway you NEED one of these on the map or the players won't be able to leave// +//You should use the random spawner though so it won't always be in the same place// +/obj/item/device/mcguffin/brass + name = "mysterious brass device" + desc = "A curious object made of what appears to be brass and silver. Its purpose is unclear by looking at it. Perhaps it should be used with something of similar materials?" + icon = 'icons/obj/machines/gateway_vr.dmi' + icon_state = "mcguffin" + drop_sound = 'sound/items/drop/wrench.ogg' + pickup_sound = 'sound/items/pickup/wrench.ogg' + +/obj/effect/landmark/mcguffin_spawner + name = "gateway key spawner" + icon = 'icons/mob/randomlandmarks.dmi' + icon_state = "key" + +/obj/machinery/gateway/centeraway/proc/entrydetect() + return + +/obj/machinery/gateway/centeraway/mcguffin/entrydetect() + if(key) + return + + var/list/spawners = list() + for(var/obj/effect/landmark/mcguffin_spawner/sp in world) + spawners += sp + + var/obj/effect/landmark/mcguffin_spawner/the_cool_one = pick(spawners) + + var/atom/destination = get_turf(the_cool_one) + var/obj/structure/closet/CL = locate() in destination + if(CL) + destination = CL + + if(!destination) + warning("A gateway is trying to spawn it's mcguffin but there are no mapped in spawner landmarks") + destination = get_turf(src) + + key = new mcguffin_type(destination) + +/obj/machinery/gateway/centeraway/mcguffin/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + M.forceMove(get_step(stationgate.loc, SOUTH)) + M.set_dir(SOUTH) + M << 'sound/effects/swooshygate.ogg' + playsound(src, 'sound/effects/swooshygate.ogg', 100, 1) + +/obj/machinery/gateway/brass + name = "mysterious brass gateway" + desc = "A gateway of strange construction. It appears to be made primarily of materials resembling brass and silver." + icon = 'icons/obj/machines/gateway_vr.dmi' + +//No, you can't digest the key to leave the gateway. +/obj/item/device/mcguffin/digest_act(var/atom/movable/item_storage = null) + return FALSE diff --git a/code/modules/awaymissions/loot.dm b/code/modules/awaymissions/loot.dm index 530358940c2..507c4e624d3 100644 --- a/code/modules/awaymissions/loot.dm +++ b/code/modules/awaymissions/loot.dm @@ -1,21 +1,21 @@ -/obj/effect/spawner/lootdrop - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - var/lootcount = 1 //how many items will be spawned - var/lootdoubles = 0 //if the same item can be spawned twice - var/loot = "" //a list of possible items to spawn- a string of paths - -/obj/effect/spawner/lootdrop/Initialize() - ..() - var/list/things = params2list(loot) - if(things && things.len) - for(var/i = lootcount, i > 0, i--) - if(!things.len) - return - var/loot_spawn = pick(things) - var/loot_path = text2path(loot_spawn) - if(!loot_path || !lootdoubles) - things.Remove(loot_spawn) - continue - new loot_path(get_turf(src)) - return INITIALIZE_HINT_QDEL +/obj/effect/spawner/lootdrop + icon = 'icons/mob/screen1.dmi' + icon_state = "x2" + var/lootcount = 1 //how many items will be spawned + var/lootdoubles = 0 //if the same item can be spawned twice + var/loot = "" //a list of possible items to spawn- a string of paths + +/obj/effect/spawner/lootdrop/Initialize() + ..() + var/list/things = params2list(loot) + if(things && things.len) + for(var/i = lootcount, i > 0, i--) + if(!things.len) + return + var/loot_spawn = pick(things) + var/loot_path = text2path(loot_spawn) + if(!loot_path || !lootdoubles) + things.Remove(loot_spawn) + continue + new loot_path(get_turf(src)) + return INITIALIZE_HINT_QDEL diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm index a7fae7b5508..5d30000e73f 100644 --- a/code/modules/awaymissions/pamphlet.dm +++ b/code/modules/awaymissions/pamphlet.dm @@ -1,38 +1,38 @@ -/obj/item/weapon/paper/pamphlet - name = "pamphlet" - icon_state = "pamphlet" - info = "Welcome to the Gateway project...
                    \ - Congratulations! If you're reading this, you and your superiors have decided that you're \ - ready to commit to a life spent colonising the rolling hills of far away worlds. You \ - must be ready for a lifetime of adventure, a little bit of hard work, and an award \ - winning dental plan- but that's not all the Gateway project has to offer.
                    \ -
                    Because we care about you, we feel it is only fair to make sure you know the risks \ - before you commit to joining the Gateway project. All away destinations have \ - been fully scanned by a expeditionary team, and are certified to be 100% safe. \ - We've even left a case of space beer along with the basic materials you'll need to expand \ - the Project's operational area and start your new life.

                    \ - Gateway Operation Basics
                    \ - All approved Gateways operate on the same basic principals. They operate off \ - area equipment power as you would expect, but they also require a backup wire with at least \ - 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ - and will reject all attempts at operation.

                    \ - Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ - searching for an output location. The amount of time this takes is variable, but the Gateway \ - interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ - searching process. Influenza will not interrupt the searching process. Temporal anomalies \ - may cause the estimate to be inaccurate, but will not interrupt the searching process.

                    \ - Life On The Other Side
                    \ - Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ - This is a normal side effect of travelling vast distances in a short period of time. You should \ - survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ - expeditionary teams have ensured the complete safety of all away locations, but in a small \ - number of cases, the Gateway they have established may not be immediately obvious. \ - Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ -

                    A New World
                    \ - As a participant in the Gateway Project, you will be on the frontiers of space. \ - Though complete safety is assured, participants are advised to prepare for inhospitable \ - environs." - -//we don't want the silly text overlay! -/obj/item/weapon/paper/pamphlet/update_icon() +/obj/item/weapon/paper/pamphlet + name = "pamphlet" + icon_state = "pamphlet" + info = "Welcome to the Gateway project...
                    \ + Congratulations! If you're reading this, you and your superiors have decided that you're \ + ready to commit to a life spent colonising the rolling hills of far away worlds. You \ + must be ready for a lifetime of adventure, a little bit of hard work, and an award \ + winning dental plan- but that's not all the Gateway project has to offer.
                    \ +
                    Because we care about you, we feel it is only fair to make sure you know the risks \ + before you commit to joining the Gateway project. All away destinations have \ + been fully scanned by a expeditionary team, and are certified to be 100% safe. \ + We've even left a case of space beer along with the basic materials you'll need to expand \ + the Project's operational area and start your new life.

                    \ + Gateway Operation Basics
                    \ + All approved Gateways operate on the same basic principals. They operate off \ + area equipment power as you would expect, but they also require a backup wire with at least \ + 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ + and will reject all attempts at operation.

                    \ + Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ + searching for an output location. The amount of time this takes is variable, but the Gateway \ + interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ + searching process. Influenza will not interrupt the searching process. Temporal anomalies \ + may cause the estimate to be inaccurate, but will not interrupt the searching process.

                    \ + Life On The Other Side
                    \ + Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ + This is a normal side effect of travelling vast distances in a short period of time. You should \ + survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ + expeditionary teams have ensured the complete safety of all away locations, but in a small \ + number of cases, the Gateway they have established may not be immediately obvious. \ + Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ +

                    A New World
                    \ + As a participant in the Gateway Project, you will be on the frontiers of space. \ + Though complete safety is assured, participants are advised to prepare for inhospitable \ + environs." + +//we don't want the silly text overlay! +/obj/item/weapon/paper/pamphlet/update_icon() return \ No newline at end of file diff --git a/code/modules/awaymissions/redgate.dm b/code/modules/awaymissions/redgate.dm index e35eb541d9d..c2de8cf370e 100644 --- a/code/modules/awaymissions/redgate.dm +++ b/code/modules/awaymissions/redgate.dm @@ -11,7 +11,8 @@ var/obj/structure/redgate/target var/secret = FALSE //If either end of the redgate has this enabled, ghosts will not be able to click to teleport var/list/exceptions = list( - /obj/structure/ore_box + /obj/structure/ore_box, + /obj/structure/bed/roller ) //made it a var so that GMs or map makers can selectively allow things to pass through var/list/restrictions = list( /mob/living/simple_mob/vore/overmap/stardog, @@ -30,14 +31,19 @@ /obj/structure/redgate/proc/teleport(var/mob/M as mob) var/keycheck = TRUE if (!istype(M,/mob/living)) //We only want mob/living, no bullets or mechs or AI eyes or items - if(M.type in exceptions) + if(is_type_in_list(M, exceptions)) keycheck = FALSE //we'll allow it else return - if(M.type in restrictions) //Some stuff we don't want to bring EVEN IF it has a key. + if(is_type_in_list(M, restrictions)) //Some stuff we don't want to bring EVEN IF it has a key. return + for(var/obj/O in M.contents) + if(O.redgate_allowed == FALSE) + to_chat(M, "The redgate refuses to allow you to pass whilst you possess \the [O].") + return + if(keycheck) //exceptions probably won't have a ckey if(!M.ckey) //We only want players, no bringing the weird stuff on the other side back return @@ -48,9 +54,22 @@ var/turf/ourturf = find_our_turf(M) //Find the turf on the opposite side of the target if(!ourturf.check_density(TRUE,TRUE)) //Make sure there isn't a wall there M.unbuckle_all_mobs(TRUE) - M.stop_pulling() - playsound(src,'sound/effects/ominous-hum-2.ogg', 100,1) - M.forceMove(ourturf) //Let's just do forcemove, I don't really want people teleporting to weird places if they have bluespace stuff + if(istype(M,/mob/living) && M.pulling) + var/atom/movable/pulled = M.pulling + M.stop_pulling() + playsound(src,'sound/effects/ominous-hum-2.ogg', 100,1) + M.forceMove(ourturf) + if(is_type_in_list(pulled, exceptions)) + for(var/mob/living/buckled_on in pulled.buckled_mobs) + if(!buckled_on.key || is_type_in_list(M, restrictions)) + pulled.unbuckle_mob(buckled_on, TRUE) + pulled.forceMove(ourturf) + M.continue_pulling(pulled) + else + to_chat(M, "The redgate refused your pulled item.") + else + playsound(src,'sound/effects/ominous-hum-2.ogg', 100,1) + M.forceMove(ourturf) //Let's just do forcemove, I don't really want people teleporting to weird places if they have bluespace stuff else to_chat(M, "Something blocks your way.") @@ -889,3 +908,524 @@ /area/redgate/train/captain name = "Train Captain's Quarters" icon_state = "purple" + + +// fantasy areas + +/area/redgate/fantasy + name = "Fantasy" + icon_state = "red" + requires_power = 0 + +/area/redgate/fantasy/streets + name = "Fantasy outside" + icon_state = "red" + +/area/redgate/fantasy/tavern + name = "Fantasy tavern" + icon_state = "yellow" + +/area/redgate/fantasy/shop + name = "Fantasy shop" + icon_state = "yellow" + +/area/redgate/fantasy/alchemist + name = "Fantasy alchemist" + icon_state = "yellow" + +/area/redgate/fantasy/castle + name = "Fantasy castle" + icon_state = "yellow" + +/area/redgate/fantasy/blacksmith + name = "Fantasy blacksmith" + icon_state = "yellow" + +/area/redgate/fantasy/hedgemaze + name = "Fantasy hedgemaze" + icon_state = "green" + +/area/redgate/fantasy/butcher + name = "Fantasy butcher" + icon_state = "yellow" + +/area/redgate/fantasy/jewler + name = "Fantasy jewler" + icon_state = "yellow" + +/area/redgate/fantasy/restaurant + name = "Fantasy restaurant" + icon_state = "yellow" + +/area/redgate/fantasy/cafe + name = "Fantasy cafe" + icon_state = "yellow" + +/area/redgate/fantasy/house + name = "Fantasy house" + icon_state = "yellow" + +/area/redgate/fantasy/gambling + name = "Fantasy gambling den" + icon_state = "yellow" + +/area/redgate/fantasy/washhouse + name = "Fantasy wash house" + icon_state = "yellow" + +/area/redgate/fantasy/aliens + name = "Fantasy alien house" + icon_state = "purple" + +/area/redgate/fantasy/walls + name = "Fantasy green" + icon_state = "green" + +/area/redgate/fantasy/guardhouse + name = "Fantasy guard house" + icon_state = "yellow" + +/area/redgate/fantasy/mininghouse + name = "Fantasy mining house" + icon_state = "yellow" + +/area/redgate/fantasy/farmhouse + name = "Fantasy farm house" + icon_state = "yellow" + +/area/redgate/fantasy/church + name = "Fantasy church" + icon_state = "yellow" + +/area/redgate/fantasy/churchhouse + name = "Fantasy church house" + icon_state = "yellow" + +/area/redgate/fantasy/arena + name = "Fantasy arena" + icon_state = "yellow" + +/area/redgate/fantasy/redgate + name = "Fantasy redgate" + icon_state = "yellow" + +/area/redgate/fantasy/paladinhouse + name = "Fantasy paladin house" + icon_state = "yellow" + +/area/redgate/fantasy/druid + name = "Fantasy druid house" + icon_state = "yellow" + +/area/redgate/fantasy/bard + name = "Fantasy bard house" + icon_state = "yellow" + +/area/redgate/fantasy/rogue + name = "Fantasy rogue house" + icon_state = "yellow" + +/area/redgate/fantasy/grocery + name = "Fantasy grocery store" + icon_state = "yellow" + +/area/redgate/fantasy/bakery + name = "Fantasy bakery" + icon_state = "yellow" + +/area/redgate/fantasy/barbarian + name = "Fantasy barbarian house" + icon_state = "yellow" + +/area/redgate/fantasy/ranger + name = "Fantasy ranger house" + icon_state = "yellow" + +/area/redgate/fantasy/ratbasement + name = "Fantasy rat infested basement" + icon_state = "yellow" + +/area/redgate/fantasy/underground + name = "Fantasy underground" + icon_state = "red" + +/area/redgate/fantasy/dungeon + name = "Fantasy dungeon" + icon_state = "yellow" + +/area/redgate/fantasy/underwater + name = "Fantasy underwater" + icon_state = "bluenew" + +/area/redgate/fantasy/crypt + name = "Fantasy crypt" + icon_state = "green" + +/area/redgate/fantasy/caves + name = "Fantasy caves" + icon_state = "yellow" + +/area/redgate/fantasy/alienbasement + name = "Fantasy alien basement" + icon_state = "yellow" + +/area/redgate/fantasy/dark + name = "Fantasy dark" + icon_state = "green" + +/area/redgate/fantasy/mines + name = "Fantasy house" + icon_state = "green" + +//HIIIIGHWAY TO THE! LASER-DOME! +/area/redgate/laserdome + name = "Laserdome Safe Zone" + icon_state = "bluwhisqu" + dynamic_lighting = 0 + requires_power = 0 + +/area/redgate/laserdome/lobby + name = "Laserdome Concourse" + icon_state = "greblasqu" + +/area/redgate/laserdome/lobby/restaurant + name = "Laserdome Restaurant" + +/area/redgate/laserdome/lobby/aid_station + name = "Laserdome First Aid Station" + +/area/redgate/laserdome/lobby/showers + name = "Laserdome Showers" + +/area/redgate/laserdome/lobby/store_1 + name = "Laserdome Store 1" + +/area/redgate/laserdome/lobby/store_2 + name = "Laserdome Store 2" + +/area/redgate/laserdome/lobby/spaceview_lounge + name = "Laserdome Spaceview Lounge" + +/area/redgate/laserdome/arena + name = "Laserdome Arenas" + icon_state = "yelwhisqu" + +/area/redgate/laserdome/arena/ctf_prep + name = "Laserdome Capture The Flag Prep Area" + icon_state = "yelwhisqu" + +/area/redgate/laserdome/arena/hbl_prep + name = "Laserdome Hyperball Prep Area" + icon_state = "yelwhisqu" + +/area/redgate/laserdome/arena/capture_the_flag + name = "Laserdome Capture The Flag Arena" + icon_state = "redwhitri" + +/area/redgate/laserdome/arena/hyperball + name = "Laserdome Hyperball Arena" + icon_state = "redwhicir" + +/area/redgate/laserdome/space + name = "Laserdome Space View" + icon_state = "dark128" + +//The actual flags. Base type defined to handle some of the basic behaviours. +/obj/item/weapon/laserdome_flag + name = "Flag" + desc = "Steal the enemy flag and take it to your base in order to score! First team to three captures wins! Or was it five? Eh, check with the referee I guess." + description_info = "Simply pick up your team's flag to return it to your base after a short delay. If you're carrying the enemy flag, use it on your team's flag base to score a point!" + slowdown = 1 //big flag is harder to run with, encourages teamwork and lets the opposing team catch up. would be nice if this was a forced slowdown that ignores hardy. + icon = 'icons/obj/flags.dmi' + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_vr.dmi', + ) + item_state = "laserdome_flag" + icon_state = "flag" + var/laser_team = "neutral" + w_class = ITEMSIZE_NO_CONTAINER //no stashing the flag in a bag for you, bucko! + redgate_allowed = FALSE //no running off the map with the flags either + var/start_pos + var/flag_return_delay = 3 SECONDS //how long you have to hold onto your team's flag before it returns home + +/obj/item/weapon/laserdome_flag/Initialize() + . = ..() + start_pos = src.loc //save our starting location for later + +/* +//TODO - make this not trigger when the flag is returned to its original location +/obj/item/weapon/laserdome_flag/dropped() + . = ..() + global_announcer.autosay("[src] dropped!","Laserdome Announcer","Entertainment") +*/ + +/obj/item/weapon/laserdome_flag/attack_hand(mob/user as mob) + . = ..() + var/mob/living/carbon/human/M = loc + var/grabbing_team + + //if they're not a carbon, we don't care + if(!istype(M)) + return + + //get their uniform + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) + grabbing_team = "red" + else if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) + grabbing_team = "blue" + else + return //if they're not on a team, stop! + + //set the verb based on matching (or mismatching) outfits, and teleport the flag back to base if it was touched by the owning team + if(grabbing_team == laser_team) + user.visible_message("[user] is returning \the [src]!") + if(do_after(user,flag_return_delay)) //channel return, rather than instant + user.drop_from_inventory(src) + src.loc = src.start_pos + global_announcer.autosay("[capitalize(laser_team)] flag returned by [user]!","Laserdome Announcer","Entertainment") + else //if they fail the channel (e.g. because they got tagged!) then drop it + user.drop_from_inventory(src) + return + else + user.visible_message("[user] has taken \the [src]!") + global_announcer.autosay("[src] taken by [capitalize(grabbing_team)] team!","Laserdome Announcer","Entertainment") + +/obj/item/weapon/laserdome_flag/red + name = "Red flag" + icon_state = "red_flag" + item_state = "laserdome_flag_red" + laser_team = "red" + +/obj/item/weapon/laserdome_flag/blue + name = "Blue flag" + icon_state = "blue_flag" + item_state = "laserdome_flag_blue" + laser_team = "blue" + +//Finally, the flag bases. Both bases *must* be in the same map area (e.g. /area/ctf_arena) for the scoring system to work properly. But if they are, then it's basically just spawn-and-play, no other setup needed! +/obj/structure/flag_base + name = "Flag base" + desc = "Where your flag rests. Bring the enemy flag here to score!" + icon = 'icons/obj/flags.dmi' + icon_state = "flag_base" + anchored = TRUE + var/base_team + var/score = 0 + var/score_limit = 3 + +/obj/structure/flag_base/blue + name = "Blue team flag base" + base_team = "blue" + +/obj/structure/flag_base/red + name = "Red team flag base" + base_team = "red" + +/obj/structure/flag_decor + name = "Decorative flag" + desc = "A decorative flag." + icon = 'icons/obj/flags.dmi' + icon_state = "flag" + +/obj/structure/flag_decor/blue + icon_state = "blue_flag_deco" + +/obj/structure/flag_decor/red + icon_state = "red_flag_deco" + +/obj/structure/flag_base/attackby(obj/F as obj, mob/user as mob) + . = ..() + + //TODO- require the team's flag to be present before they can score? + if(istype(F,/obj/item/weapon/laserdome_flag)) + var/obj/item/weapon/laserdome_flag/flag = F + if(flag.laser_team != base_team) + global_announcer.autosay("[user] captured the [capitalize(flag.laser_team)] flag for [capitalize(base_team)] team!","Laserdome Announcer","Entertainment") + user.drop_from_inventory(flag) + flag.loc = flag.start_pos //teleport the captured flag back to its base location + score++ //increment our score by 1! + if(score < score_limit) //announce the current score and how many more captures are needed + global_announcer.autosay("[num2text(score_limit-score)] captures remain until [capitalize(base_team)] team wins.","Laserdome Announcer","Entertainment") + else if(score >= score_limit) //now, if score equals or exceeds (somehow) the score limit, announce that our team won and reset the score for all flag bases nearby + global_announcer.autosay("+|[uppertext(base_team)] TEAM HAS WON THE MATCH!|+","Laserdome Announcer","Entertainment") + for(var/obj/structure/flag_base/FB in src.loc.loc.contents) //this feels dirty, but it works + FB.score = 0 + else if(flag.laser_team == base_team) + global_announcer.autosay("[capitalize(base_team)] flag returned!","Laserdome Announcer","Entertainment") + user.drop_from_inventory(flag) + flag.loc = src.loc //place our flag neatly back on its pedestal + +/obj/item/weapon/laserdome_hyperball + name = "\improper HYPERball" //*always* refer to it as "the hyperball", not just "the ball". corporate insists. + desc = "Because regular balls aren't exciting enough, the future needs HYPERballs!" + description_info = "Take the ball and dunk it into the opposing team's goal to score! You can either throw it into the goal or dunk it directly; the latter is worth more points, but it's more challenging as you need to be next to the goal in order to dunk." + slowdown = -0.5 //carrying the ball actually speeds you up a little bit? given you need to get past enemy defense and dunk. also makes it easier to get the ball away from your base if you intercept. + icon = 'icons/obj/flags.dmi' + icon_state = "hyperball" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_vr.dmi', + ) + item_state = "hyperball" + w_class = ITEMSIZE_NO_CONTAINER //no shoving it in your backpack to hide it + redgate_allowed = FALSE //you can't take your ball and go home + var/start_pos + var/last_holder + var/last_team + +/obj/item/weapon/laserdome_hyperball_prop + name = "demonstration HYPERball" + desc = "Because regular balls aren't exciting enough, the future needs HYPERballs!" + description_info = "This model is for demonstration purposes only. It looks pretty heavy!" + slowdown = 3 //really discourage people from trying to actually use these in the game if they get them out of the display cases + icon = 'icons/obj/flags.dmi' + icon_state = "hyperball" + w_class = ITEMSIZE_NO_CONTAINER + redgate_allowed = FALSE //you can't take the demonstration balls and go home either + +/obj/item/weapon/laserdome_hyperball/Initialize() + . = ..() + start_pos = src.loc //save our starting location for later + +/obj/item/weapon/laserdome_hyperball/attack_hand(mob/user as mob) + . = ..() + var/mob/living/carbon/human/M = loc + var/grabbing_team + + //if they're not a carbon, we don't care + if(!istype(M)) + return + + //get their uniform + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) + grabbing_team = "red" + icon_state = "[initial(icon_state)]_red" + item_state = "[initial(icon_state)]_red" + else if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) + grabbing_team = "blue" + icon_state = "[initial(icon_state)]_blue" + item_state = "[initial(icon_state)]_blue" + else + return //if they're not on a team, stop! + + user.visible_message("[user] has taken \the [src]!") + //cache our grabber and their team, for throw interactions with the goals later + last_holder = M + last_team = grabbing_team + //finally, announcer calls out which team has the ball + global_announcer.autosay("[capitalize(grabbing_team)] team on offense!","Laserdome Announcer","Entertainment") + update_icon() + update_held_icon() + +/* +//TODO- make this not trigger when the ball is thrown or dunked, only when it's actually dropped +/obj/item/weapon/laserdome_hyperball/dropped() + . = ..() + global_announcer.autosay("[capitalize(last_team)] fumble!","Laserdome Announcer","Entertainment") +*/ + +/obj/structure/hyperball_pedestal + name = "HYPERball pedestal" + desc = "A fancy stand that the hyperball appears on. Looks strangely like one of the goals, come to think of it..." + icon = 'icons/obj/flags.dmi' + icon_state = "hyperball_stand" + anchored = TRUE + +//Finally, the goal objects. Like the flag bases, both goals *must* be in the same map area (e.g. /area/hyperball_arena) for the scoring system to work properly. But if they are, then it's basically just spawn-and-play, no other setup needed! +/obj/structure/hyperball_goal + name = "HYPERball goal" + desc = "A dangerous-looking hole, with an energy net that stops anything but a hyperball from passing through." + description_info = "Dunk the hyperball here to score! Just don't get an own goal. Alternately, throw the ball in for less points. There's a chance you'll miss, or an enemy team member might get in the way, but it can be easier than getting close enough for a dunk." + icon = 'icons/obj/flags.dmi' + icon_state = "hyperball_goal" + anchored = TRUE + var/goal_team + var/score = 0 + var/score_limit = 21 //3 hand-dunks (hard), or 7 throws (easy), or any combination thereof + var/dunk_points = 7 + var/range_dunk_points = 3 + var/range_dunk_chance = 75 //chance for a ranged dunk to "hit" the goal + +/obj/structure/hyperball_goal/blue + name = "Blue team HYPERball goal" + icon_state = "hyperball_goal_blue" + goal_team = "blue" + +/obj/structure/hyperball_goal/red + name = "Red team HYPERball goal" + icon_state = "hyperball_goal_red" + goal_team = "red" + +/obj/structure/hyperball_goal/attackby(obj/B as obj, mob/user as mob) + . = ..() + var/mob/living/carbon/human/M = user + var/dunking_team + if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) + dunking_team = "red" + else if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) + dunking_team = "blue" + else + return //if they're not on a team, stop! + + if(istype(B,/obj/item/weapon/laserdome_hyperball)) + var/obj/item/weapon/laserdome_hyperball/ball = B + if(dunking_team != goal_team) + global_announcer.autosay("[user] dunked the HYPERball for [capitalize(dunking_team)] team! [num2text(dunk_points)] points scored!","Laserdome Announcer","Entertainment") + score += dunk_points //increment our score! + if(score < score_limit) //announce the current score and how many more captures are needed + global_announcer.autosay("[num2text(score_limit-score)] points remain until [capitalize(dunking_team)] team wins.","Laserdome Announcer","Entertainment") + else if(score >= score_limit) //now, if score equals or exceeds (somehow) the score limit, announce that our team won and reset the score for all flag bases nearby + global_announcer.autosay("+|[uppertext(dunking_team)] TEAM HAS WON THE MATCH!|+","Laserdome Announcer","Entertainment") + for(var/obj/structure/hyperball_goal/HB in src.loc.loc.contents) //this feels dirty, but it works + HB.score = 0 + else if(dunking_team == goal_team) //discourage people from dunking the ball into their own goal as a quick way to teleport it back to the midfield + switch(goal_team) //this gets a bit fiddly because we store our score on the target's goal, so we need to scan the map for the opposing team's goal and deduct points from it + if("blue") + for(var/obj/structure/hyperball_goal/red/HGR in src.loc.loc.contents) + HGR.score = max(0,HGR.score-dunk_points) + global_announcer.autosay("[user] dunked the HYPERball and scored an own goal! +Points |de-ducted!|+ [capitalize(goal_team)] team score is now: [HGR.score].","Laserdome Announcer","Entertainment") + if("red") + for(var/obj/structure/hyperball_goal/blue/HGB in src.loc.loc.contents) + HGB.score = max(0,HGB.score-dunk_points) + global_announcer.autosay("[user] dunked the HYPERball and scored an own goal! +Points |de-ducted!|+ [capitalize(goal_team)] team score is now: [HGB.score].","Laserdome Announcer","Entertainment") + + user.drop_from_inventory(ball) + ball.loc = ball.start_pos //teleport the ball back to the midfield + ball.icon_state = "[initial(ball.icon_state)]" + ball.item_state = "[initial(ball.item_state)]" + ball.update_icon() + +/obj/structure/hyperball_goal/hitby(obj/B as obj) + . = ..() + if(istype(B,/obj/item/weapon/laserdome_hyperball)) + var/obj/item/weapon/laserdome_hyperball/ball = B + if(prob(range_dunk_chance)) + if(ball.last_team != goal_team) + global_announcer.autosay("[ball.last_holder] threw the HYPERball for [capitalize(ball.last_team)] team! [num2text(range_dunk_points)] points scored!","Laserdome Announcer","Entertainment") + score += range_dunk_points //increment our score! + if(score < score_limit) //announce the current score and how many more captures are needed + global_announcer.autosay("[num2text(score_limit-score)] points remain until [capitalize(ball.last_team)] team wins.","Laserdome Announcer","Entertainment") + else if(score >= score_limit) //now, if score equals or exceeds the score limit, announce that our team won and reset the score for all flag bases nearby + global_announcer.autosay("+|[uppertext(ball.last_team)] TEAM HAS WON THE MATCH!|+","Laserdome Announcer","Entertainment") + for(var/obj/structure/hyperball_goal/HB in src.loc.loc.contents) //this feels dirty, but it works + HB.score = 0 + else if(ball.last_team == goal_team) //discourage people from dunking the ball into their own goal as a quick way to teleport it back to the midfield + switch(goal_team) //this gets a bit fiddly because we store our score on the target's goal, so we need to scan the map for the opposing team's goal and deduct points from it + if("blue") + for(var/obj/structure/hyperball_goal/red/HGR in src.loc.loc.contents) + HGR.score = max(0,HGR.score-range_dunk_points) + global_announcer.autosay("[ball.last_holder] threw the HYPERball and scored an own goal! +Points |de-ducted!|+ [capitalize(goal_team)] team score is now: [HGR.score].","Laserdome Announcer","Entertainment") + if("red") + for(var/obj/structure/hyperball_goal/blue/HGB in src.loc.loc.contents) + HGB.score = max(0,HGB.score-range_dunk_points) + global_announcer.autosay("[ball.last_holder] threw the HYPERball and scored an own goal! +Points |de-ducted!|+ [capitalize(goal_team)] team score is now: [HGB.score].","Laserdome Announcer","Entertainment") + + ball.loc = ball.start_pos //teleport the ball back to the midfield + ball.icon_state = "[initial(ball.icon_state)]" + ball.item_state = "[initial(ball.item_state)]" + ball.update_icon() + else + //todo; throw the ball in a random direction + src.visible_message("\The [ball] bounces off \the [src]'s rim!") + global_announcer.autosay("[ball.last_holder] threw the HYPERball and +missed!+ |Oooh!|","Laserdome Announcer","Entertainment") diff --git a/code/modules/awaymissions/trigger.dm b/code/modules/awaymissions/trigger.dm index d34763ab024..e388b0dd2e5 100644 --- a/code/modules/awaymissions/trigger.dm +++ b/code/modules/awaymissions/trigger.dm @@ -1,44 +1,44 @@ -/obj/effect/step_trigger/message - var/message //the message to give to the mob - var/once = 1 - -/obj/effect/step_trigger/message/Trigger(mob/M as mob) - if(M.client) - to_chat(M, "[message]") - if(once) - qdel(src) - -/obj/effect/step_trigger/teleport_fancy - var/locationx - var/locationy - var/uses = 1 //0 for infinite uses - var/entersparks = 0 - var/exitsparks = 0 - var/entersmoke = 0 - var/exitsmoke = 0 - -/obj/effect/step_trigger/teleport_fancy/Trigger(mob/M as mob) - var/dest = locate(locationx, locationy, z) - M.Move(dest) - - if(entersparks) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(4, 1, src) - s.start() - if(exitsparks) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(4, 1, dest) - s.start() - - if(entersmoke) - var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread - s.set_up(4, 1, src, 0) - s.start() - if(exitsmoke) - var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread - s.set_up(4, 1, dest, 0) - s.start() - - uses-- - if(uses == 0) +/obj/effect/step_trigger/message + var/message //the message to give to the mob + var/once = 1 + +/obj/effect/step_trigger/message/Trigger(mob/M as mob) + if(M.client) + to_chat(M, "[message]") + if(once) + qdel(src) + +/obj/effect/step_trigger/teleport_fancy + var/locationx + var/locationy + var/uses = 1 //0 for infinite uses + var/entersparks = 0 + var/exitsparks = 0 + var/entersmoke = 0 + var/exitsmoke = 0 + +/obj/effect/step_trigger/teleport_fancy/Trigger(mob/M as mob) + var/dest = locate(locationx, locationy, z) + M.Move(dest) + + if(entersparks) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(4, 1, src) + s.start() + if(exitsparks) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(4, 1, dest) + s.start() + + if(entersmoke) + var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread + s.set_up(4, 1, src, 0) + s.start() + if(exitsmoke) + var/datum/effect/effect/system/smoke_spread/s = new /datum/effect/effect/system/smoke_spread + s.set_up(4, 1, dest, 0) + s.start() + + uses-- + if(uses == 0) qdel(src) \ No newline at end of file diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index fea781da8bf..2f3dd757ae9 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -1,91 +1,91 @@ -/proc/createRandomZlevel() - if(awaydestinations.len || UNIT_TEST) //crude, but it saves another var! //VOREStation Edit - No loading away missions during CI testing - return - - var/list/potentialRandomZlevels = list() - admin_notice(" Searching for away missions...", R_DEBUG) - var/list/Lines = file2list("maps/RandomZLevels/fileList.txt") - if(!Lines.len) return - for (var/t in Lines) - if (!t) - continue - - t = trim(t) - if (length(t) == 0) - continue - else if (copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - // var/value = null - - if (pos) - // No, don't do lowertext here, that breaks paths on linux - name = copytext(t, 1, pos) - // value = copytext(t, pos + 1) - else - // No, don't do lowertext here, that breaks paths on linux - name = t - - if (!name) - continue - - potentialRandomZlevels.Add(name) - - - if(potentialRandomZlevels.len) - admin_notice("Loading away mission...", R_DEBUG) - - var/map = pick(potentialRandomZlevels) - to_world_log("Away mission picked: [map]") //VOREStation Add for debugging - var/file = file(map) - if(isfile(file)) - var/datum/map_template/template = new(file, "away mission") - template.load_new_z() - to_world_log("away mission loaded: [map]") - /* VOREStation Removal - We do this in the special landmark init instead. - for(var/obj/effect/landmark/L in landmarks_list) - if (L.name != "awaystart") - continue - awaydestinations.Add(L) - */ //VOREStation Removal End - admin_notice("Away mission loaded.", R_DEBUG) - - else - admin_notice("No away missions found.", R_DEBUG) - return - -//VOREStation Add - This landmark type so it's not so ghetto. -/obj/effect/landmark/gateway_scatter - name = "uncalibrated gateway destination" -/obj/effect/landmark/gateway_scatter/Initialize() - . = ..() - awaydestinations += src - -/obj/effect/landmark/gateway_scatter/abduct - name = "uncalibrated gateway abductor" - abductor = 1 - -/obj/effect/landmark/event_scatter - name = "uncalibrated event destination" -/obj/effect/landmark/event_scatter/Initialize() - . = ..() - eventdestinations += src - -/obj/effect/landmark/event_scatter/abduct - name = "uncalibrated event abductor" - abductor = 1 - -/obj/effect/landmark/gateway_abduct_dest - name = "abductor gateway destination" -/obj/effect/landmark/gateway_abduct_dest/Initialize() - . = ..() - awayabductors += src - -/obj/effect/landmark/event_abduct_dest - name = "abductor event destination" -/obj/effect/landmark/event_abduct_dest/Initialize() - . = ..() - eventabductors += src -//VOREStation Add End +/proc/createRandomZlevel() + if(awaydestinations.len || UNIT_TEST) //crude, but it saves another var! //VOREStation Edit - No loading away missions during CI testing + return + + var/list/potentialRandomZlevels = list() + admin_notice(span_red(" Searching for away missions..."), R_DEBUG) + var/list/Lines = file2list("maps/RandomZLevels/fileList.txt") + if(!Lines.len) return + for (var/t in Lines) + if (!t) + continue + + t = trim(t) + if (length(t) == 0) + continue + else if (copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + // var/value = null + + if (pos) + // No, don't do lowertext here, that breaks paths on linux + name = copytext(t, 1, pos) + // value = copytext(t, pos + 1) + else + // No, don't do lowertext here, that breaks paths on linux + name = t + + if (!name) + continue + + potentialRandomZlevels.Add(name) + + + if(potentialRandomZlevels.len) + admin_notice(span_red("Loading away mission..."), R_DEBUG) + + var/map = pick(potentialRandomZlevels) + to_world_log("Away mission picked: [map]") //VOREStation Add for debugging + var/file = file(map) + if(isfile(file)) + var/datum/map_template/template = new(file, "away mission") + template.load_new_z() + to_world_log("away mission loaded: [map]") + /* VOREStation Removal - We do this in the special landmark init instead. + for(var/obj/effect/landmark/L in landmarks_list) + if (L.name != "awaystart") + continue + awaydestinations.Add(L) + */ //VOREStation Removal End + admin_notice(span_red("Away mission loaded."), R_DEBUG) + + else + admin_notice(span_red("No away missions found."), R_DEBUG) + return + +//VOREStation Add - This landmark type so it's not so ghetto. +/obj/effect/landmark/gateway_scatter + name = "uncalibrated gateway destination" +/obj/effect/landmark/gateway_scatter/Initialize() + . = ..() + awaydestinations += src + +/obj/effect/landmark/gateway_scatter/abduct + name = "uncalibrated gateway abductor" + abductor = 1 + +/obj/effect/landmark/event_scatter + name = "uncalibrated event destination" +/obj/effect/landmark/event_scatter/Initialize() + . = ..() + eventdestinations += src + +/obj/effect/landmark/event_scatter/abduct + name = "uncalibrated event abductor" + abductor = 1 + +/obj/effect/landmark/gateway_abduct_dest + name = "abductor gateway destination" +/obj/effect/landmark/gateway_abduct_dest/Initialize() + . = ..() + awayabductors += src + +/obj/effect/landmark/event_abduct_dest + name = "abductor event destination" +/obj/effect/landmark/event_abduct_dest/Initialize() + . = ..() + eventabductors += src +//VOREStation Add End diff --git a/code/modules/casino/casino_prize_vendor.dm b/code/modules/casino/casino_prize_vendor.dm index 115e51ed487..18ffd62b7d1 100644 --- a/code/modules/casino/casino_prize_vendor.dm +++ b/code/modules/casino/casino_prize_vendor.dm @@ -344,7 +344,7 @@ return for(var/mob/O in hearers(src, null)) - O.show_message("\The [src] beeps, \"[message]\"",2) + O.show_message("\The [src] beeps, \"[message]\"",2) return /obj/machinery/casino_prize_dispenser/process() //Might not need this, but just to be safe for now diff --git a/code/modules/casino/slots.dm b/code/modules/casino/slots.dm index 40c7e5af61e..654d872b0e3 100644 --- a/code/modules/casino/slots.dm +++ b/code/modules/casino/slots.dm @@ -61,7 +61,7 @@ if(busy) to_chat(user,"The slot machine is currently running. ") return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) playsound(src, W.usesound, 100, 1) if(anchored) user.visible_message("[user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") @@ -291,7 +291,7 @@ if(busy) to_chat(user,"The slot machine is currently running. ") return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) playsound(src, W.usesound, 100, 1) if(anchored) user.visible_message("[user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") diff --git a/code/modules/catalogue/atoms.dm b/code/modules/catalogue/atoms.dm index 6a0cde14ec4..69939aeb451 100644 --- a/code/modules/catalogue/atoms.dm +++ b/code/modules/catalogue/atoms.dm @@ -1,81 +1,81 @@ -/atom - var/catalogue_delay = 5 SECONDS // How long it take to scan. - // List of types of /datum/category_item/catalogue that should be 'unlocked' when scanned by a Cataloguer. - // It is null by default to save memory by not having everything hold onto empty lists. Use macros like LAZYLEN() to check. - // Also you should use get_catalogue_data() to access this instead of doing so directly, so special behavior can be enabled. - var/list/catalogue_data = null - -/mob - catalogue_delay = 10 SECONDS - -// Tests if something can be catalogued. -// If something goes wrong and a mob was supplied, the mob will be told why they can't catalogue it. -/atom/proc/can_catalogue(mob/user) - // First check if anything is even on here. - var/list/data = get_catalogue_data() - if(!LAZYLEN(data)) - to_chat(user, span("warning", "\The [src] is not interesting enough to catalogue.")) - return FALSE - else - // Check if this has nothing new on it. - var/has_new_data = FALSE - for(var/t in data) - var/datum/category_item/catalogue/item = GLOB.catalogue_data.resolve_item(t) - if(!item.visible) - has_new_data = TRUE - break - - if(!has_new_data) - to_chat(user, span("warning", "Scanning \the [src] would provide no new information.")) - return FALSE - - return TRUE - -/mob/living/can_catalogue(mob/user) // Dead mobs can't be scanned. - if(stat >= DEAD) - to_chat(user, span("warning", "Entities must be alive for a comprehensive scan.")) - return FALSE - return ..() - -/obj/item/can_catalogue(mob/user) // Items must be identified to be scanned. - if(!is_identified()) - to_chat(user, span("warning", "The properties of this object has not been determined. Identify it first.")) - return FALSE - return ..() - -/atom/proc/get_catalogue_delay() - return catalogue_delay - -// Override for special behaviour. -// Should return a list with one or more "/datum/category_item/catalogue" types, or null. -// If overriding, it may be wise to call the super and get the results in order to merge the base result and the special result, if appropiate. -/atom/proc/get_catalogue_data() - return catalogue_data - -/mob/living/carbon/human/get_catalogue_data() - var/list/data = list() - // First, handle robot-ness. - var/beep_boop = get_FBP_type() - switch(beep_boop) - if(FBP_CYBORG) - data += /datum/category_item/catalogue/technology/cyborgs - if(FBP_POSI) - data += /datum/category_item/catalogue/technology/positronics - if(FBP_DRONE) - data += /datum/category_item/catalogue/technology/drone/drones - // Now for species. - if(!(beep_boop in list(FBP_POSI, FBP_DRONE))) // Don't give the species entry if they are a posi or drone. - if(species && LAZYLEN(species.catalogue_data)) - data += species.catalogue_data - return data - -/mob/living/silicon/robot/get_catalogue_data() - var/list/data = list() - switch(braintype) - if(BORG_BRAINTYPE_CYBORG) - data += /datum/category_item/catalogue/technology/cyborgs - if(BORG_BRAINTYPE_POSI) - data += /datum/category_item/catalogue/technology/positronics - if(BORG_BRAINTYPE_DRONE) - data += /datum/category_item/catalogue/technology/drone/drones +/atom + var/catalogue_delay = 5 SECONDS // How long it take to scan. + // List of types of /datum/category_item/catalogue that should be 'unlocked' when scanned by a Cataloguer. + // It is null by default to save memory by not having everything hold onto empty lists. Use macros like LAZYLEN() to check. + // Also you should use get_catalogue_data() to access this instead of doing so directly, so special behavior can be enabled. + var/list/catalogue_data = null + +/mob + catalogue_delay = 10 SECONDS + +// Tests if something can be catalogued. +// If something goes wrong and a mob was supplied, the mob will be told why they can't catalogue it. +/atom/proc/can_catalogue(mob/user) + // First check if anything is even on here. + var/list/data = get_catalogue_data() + if(!LAZYLEN(data)) + to_chat(user, span("warning", "\The [src] is not interesting enough to catalogue.")) + return FALSE + else + // Check if this has nothing new on it. + var/has_new_data = FALSE + for(var/t in data) + var/datum/category_item/catalogue/item = GLOB.catalogue_data.resolve_item(t) + if(!item.visible) + has_new_data = TRUE + break + + if(!has_new_data) + to_chat(user, span("warning", "Scanning \the [src] would provide no new information.")) + return FALSE + + return TRUE + +/mob/living/can_catalogue(mob/user) // Dead mobs can't be scanned. + if(stat >= DEAD) + to_chat(user, span("warning", "Entities must be alive for a comprehensive scan.")) + return FALSE + return ..() + +/obj/item/can_catalogue(mob/user) // Items must be identified to be scanned. + if(!is_identified()) + to_chat(user, span("warning", "The properties of this object has not been determined. Identify it first.")) + return FALSE + return ..() + +/atom/proc/get_catalogue_delay() + return catalogue_delay + +// Override for special behaviour. +// Should return a list with one or more "/datum/category_item/catalogue" types, or null. +// If overriding, it may be wise to call the super and get the results in order to merge the base result and the special result, if appropiate. +/atom/proc/get_catalogue_data() + return catalogue_data + +/mob/living/carbon/human/get_catalogue_data() + var/list/data = list() + // First, handle robot-ness. + var/beep_boop = get_FBP_type() + switch(beep_boop) + if(FBP_CYBORG) + data += /datum/category_item/catalogue/technology/cyborgs + if(FBP_POSI) + data += /datum/category_item/catalogue/technology/positronics + if(FBP_DRONE) + data += /datum/category_item/catalogue/technology/drone/drones + // Now for species. + if(!(beep_boop in list(FBP_POSI, FBP_DRONE))) // Don't give the species entry if they are a posi or drone. + if(species && LAZYLEN(species.catalogue_data)) + data += species.catalogue_data + return data + +/mob/living/silicon/robot/get_catalogue_data() + var/list/data = list() + switch(braintype) + if(BORG_BRAINTYPE_CYBORG) + data += /datum/category_item/catalogue/technology/cyborgs + if(BORG_BRAINTYPE_POSI) + data += /datum/category_item/catalogue/technology/positronics + if(BORG_BRAINTYPE_DRONE) + data += /datum/category_item/catalogue/technology/drone/drones return data \ No newline at end of file diff --git a/code/modules/catalogue/catalogue_data.dm b/code/modules/catalogue/catalogue_data.dm index 7af670229c9..2329b0f3f3f 100644 --- a/code/modules/catalogue/catalogue_data.dm +++ b/code/modules/catalogue/catalogue_data.dm @@ -1,437 +1,437 @@ -GLOBAL_DATUM_INIT(catalogue_data, /datum/category_collection/catalogue, new) - -// The collection holds everything together and is GLOB accessible. -/datum/category_collection/catalogue - category_group_type = /datum/category_group/catalogue - -/datum/category_collection/catalogue/proc/resolve_item(item_path) - for(var/group in categories) - var/datum/category_group/G = group - - var/datum/category_item/catalogue/C = item_path - var/name_to_search = initial(C.name) - if(G.items_by_name[name_to_search]) - return G.items_by_name[name_to_search] - - // for(var/item in G.items) - // var/datum/category_item/I = item - // if(I.type == item_path) - // return I - - -// Groups act as sections for the different data. -/datum/category_group/catalogue - -// Plants. -/datum/category_group/catalogue/flora - name = "Flora" - category_item_type = /datum/category_item/catalogue/flora - -// Animals. -/datum/category_group/catalogue/fauna - name = "Fauna" - category_item_type = /datum/category_item/catalogue/fauna - -// Gadgets, tech, and robots. -/datum/category_group/catalogue/technology - name = "Technology" - category_item_type = /datum/category_item/catalogue/technology - -// Abstract information. -/datum/category_group/catalogue/information - name = "Information" - category_item_type = /datum/category_item/catalogue/information - -// Weird stuff like precursors. -/datum/category_group/catalogue/anomalous - name = "Anomalous" - category_item_type = /datum/category_item/catalogue/anomalous - -// Physical material things like crystals and metals. -/datum/category_group/catalogue/material - name = "Material" - category_item_type = /datum/category_item/catalogue/material - - -// Items act as individual data for each object. -/datum/category_item/catalogue - var/desc = null // Paragraph or two about what the object is. - var/value = 0 // How many 'exploration points' you get for scanning it. Suggested to use the CATALOGUER_REWARD_* defines for easy tweaking. - var/visible = FALSE // When someone scans the correct object, this gets set to TRUE and becomes viewable in the databanks. - var/list/cataloguers = null // List of names of those who helped 'discover' this piece of data, in string form. - var/list/unlocked_by_any = null // List of types that, if they are discovered, it will also make this datum discovered. - var/list/unlocked_by_all = null // Similar to above, but all types on the list must be discovered for this to be discovered. - -// Discovers a specific datum, and any datums associated with this datum by unlocked_by_[any|all]. -// Returns null if nothing was found, otherwise returns a list of datum instances that was discovered, usually for the cataloguer to use. -/datum/category_item/catalogue/proc/discover(mob/user, list/new_cataloguers) - if(visible) // Already found. - return - - . = list(src) - visible = TRUE - cataloguers = new_cataloguers - display_in_chatlog(user) - . += attempt_chain_discoveries(user, new_cataloguers, type) - -// Calls discover() on other datums if they include the type that was just discovered is inside unlocked_by_[any|all]. -// Returns discovered datums. -/datum/category_item/catalogue/proc/attempt_chain_discoveries(mob/user, list/new_cataloguers, type_to_test) - . = list() - for(var/G in category.collection.categories) // I heard you like loops. - var/datum/category_group/catalogue/group = G - for(var/I in group.items) - var/datum/category_item/catalogue/item = I - // First, look for datums unlocked with the 'any' list. - if(LAZYLEN(item.unlocked_by_any)) - for(var/T in item.unlocked_by_any) - if(ispath(type_to_test, T) && item.discover(user, new_cataloguers)) - . += item - - // Now for the more complicated 'all' list. - if(LAZYLEN(item.unlocked_by_all)) - if(type_to_test in item.unlocked_by_all) - // Unlike the 'any' list, the 'all' list requires that all datums inside it to have been found first. - var/should_discover = TRUE - for(var/T in item.unlocked_by_all) - var/datum/category_item/catalogue/thing = GLOB.catalogue_data.resolve_item(T) - if(istype(thing)) - if(!thing.visible) - should_discover = FALSE - break - if(should_discover && item.discover(user, new_cataloguers)) - . += item - -/datum/category_item/catalogue/proc/display_in_chatlog(mob/user) - to_chat(user, "
                    ") - to_chat(user, span("notice", "[uppertext(name)]")) - - // Some entries get very long so lets not totally flood the chatlog. - var/desc_length_limit = 750 - var/displayed_desc = desc - if(length(desc) > desc_length_limit) - displayed_desc = copytext(displayed_desc, 1, desc_length_limit + 1) - displayed_desc += "... (View databanks for full data)" - - to_chat(user, span("notice", "[displayed_desc]")) - to_chat(user, span("notice", "Cataloguers : [english_list(cataloguers)].")) - to_chat(user, span("notice", "Contributes [value] points to personal exploration fund.")) - -/* - // Truncates text to limit if necessary. - var/size = length(message) - if (size <= length) - return message - else - return copytext(message, 1, length + 1) -*/ - -/datum/category_item/catalogue/flora - -/datum/category_item/catalogue/fauna - -/datum/category_item/catalogue/fauna/humans - name = "Sapients - Humans" - desc = "Humans are a space-faring species hailing originally from the planet Earth in the Sol system. \ - They are currently among the most numerous known species in the galaxy, in both population and holdings, \ - and are relatively technologically advanced. With good healthcare and a reasonable lifestyle, \ - they can live to around 110 years. The oldest humans are around 150 years old.\ -

                    \ - Humanity is the primary driving force for rapid space expansion, owing to their strong, expansionist central \ - government and opportunistic Trans-Stellar Corporations. The prejudices of the 21st century have mostly \ - given way to bitter divides on the most important issue of the times- technological expansionism, \ - with the major human factions squabbling over their approach to technology in the face of a \ - looming singularity.\ -

                    \ - While most humans have accepted the existence of aliens in their communities and workplaces as a \ - fact of life, exceptions abound. While more culturally diverse than most species, humans are \ - generally regarded as somewhat technophobic and isolationist by members of other species." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/skrell - name = "Sapients - Skrell" - desc = "The Skrell are a species of amphibious humanoids, distinguished by their green-blue gelatinous \ - appearance and head tentacles. Skrell warble from the world of Qerr'balak, a humid planet with \ - plenty of swamps and jungles. Currently more technologically advanced than humanity, they \ - emphasize the study of the mind above all else.\ -

                    \ - Gender has little meaning to Skrell outside of reproduction, and in fact many other species \ - have a difficult time telling the difference between male and female Skrell apart. The most \ - obvious signs (voice in a slightly higher register, longer head-tails for females) are \ - never a guarantee.\ -

                    \ - Due to their scientific focus of the mind and body, Skrell tend to be more peaceful and their \ - colonization has been slow, swiftly outpaced by humanity. They were the first contact sentient \ - species, and are humanity's longest, and closest, ally in space." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/unathi - name = "Sapients - Unathi" - desc = "The Unathi are a species of large reptilian humanoids hailing from Moghes, in the \ - Uueoa-Esa binary star system. Most Unathi live in a semi-rigid clan system, and clan \ - enclaves dot the surface of their homeworld. Proud and long-lived, Unathi of all \ - walks of life display a tendency towards perfectionism, and mastery of one's craft \ - is greatly respected among them. Despite the aggressive nature of their contact, \ - Unathi seem willing, if not eager, to reconcile with humanity, though mutual \ - distrust runs rampant among individuals of both groups." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/tajaran - name = "Sapients - Tajaran" - desc = "Tajaran are a race of humanoid mammalian aliens from Meralar, the fourth planet \ - of the Rarkajar star system. Thickly furred and protected from cold, they thrive on \ - their subartic planet, where the only terran temperate areas spread across the \ - equator and \"tropical belt.\"\ -

                    \ - With their own share of bloody wars and great technological advances, the Tajaran are a \ - proud kind. They fiercely believe they belong among the stars and consider themselves \ - a rightful interstellar nation, even if Humanity helped them to actually achieve \ - superluminar speeds with Bluespace FTL drives.\ -

                    \ - Relatively new to the galaxy, their contacts with other species are aloof, but friendly. \ - Among these bonds, Humanity stands out as valued trade partner and maybe even friend." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/dionaea - name = "Sapients - Dionaea" - desc = "The Dionaea are a group of intensely curious plant-like organisms. An individual \ - Diona is a single dog-sized creature called a nymphs, and multiple nymphs link together \ - to form larger, more intelligent collectives. Discovered by the Skrell in and around \ - the stars in the Epsilon Ursae Minoris system, they have accompanied the Skrell in \ - warbling throughout the cosmos as a key part of Skrellian starships, stations, \ - and terraforming equipment.\ -

                    \ - Dionaea have no concept of violence or individual identity and want little in \ - terms of material resources or living space. This makes Dionaea among the most \ - agreeable members of the galactic community, though their slow, curious alien \ - minds can be hard to sympathize with." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/teshari - name = "Sapients - Teshari" - desc = "The Teshari are reptilian pack predators from the Skrell homeworld. \ - While they evolved alongside the Skrell, their interactions with them tended \ - to be confused and violent, and until peaceful contact was made they largely \ - stayed in their territories on and around the poles, in tundral terrain far \ - too desolate and cold to be of interest to the Skrell. In more enlightened \ - times, the Teshari are a minority culture on many Skrell worlds, maintaining \ - their own settlements and cultures, but often finding themselves standing \ - on the shoulders of their more technologically advanced neighbors when it \ - comes to meeting and exploring the rest of the galaxy." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/zaddat - name = "Sapients - Zaddat" - desc = "The Zaddat are an Unathi client species that has recently come to the \ - Golden Crescent. They wear high-pressure voidsuits called Shrouds to protect \ - themselves from the harsh light and low pressure of the station, making \ - medical care a challenge and fighting especially dangerous. \ - Operating out of massive Colony ships, they trade their labor to their \ - host nation to fund their search for a new home to replace their \ - now-lifeless homeworld of Xohox." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/promethean - name = "Sapients - Promethean" - desc = "Prometheans (Macrolimus artificialis) are a species of artificially-created \ - gelatinous humanoids, chiefly characterized by their primarily liquid bodies and \ - ability to change their bodily shape and color in order to mimic many forms of life. \ - Derived from the Aetolian giant slime (Macrolimus vulgaris) inhabiting the warm, \ - tropical planet of Aetolus, they are a relatively newly lab-created sapient species, \ - and as such many things about them have yet to be comprehensively studied." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/vox - name = "Sapients - Vox" - desc = "Probably the best known of these aliens are the Vox, a bird-like species \ - with a very rough comprehension of Galactic Common and an even looser understanding \ - of property rights. Vox raiders have plagued human merchants for centuries, \ - and Skrell for even longer, but remain poorly understood. \ - They have no desire to partake in diplomacy or trade with the rest of the galaxy, \ - or even to conquer planets and stations to live in. They breathe phoron \ - and appear to be well adapted to their role as space-faring raiders, \ - leading many to speculate that they're heavily bioengineered, \ - an assumption which is at odds with their ramshackle technological level." - value = CATALOGUER_REWARD_MEDIUM // Since Vox are much rarer. - - -/datum/category_item/catalogue/technology - -/datum/category_item/catalogue/technology/drone/drones - name = "Drones" - desc = "A drone is a software-based artificial intelligence, generally about an order of magnitude \ - less intelligent than a positronic brain. However, the processing power available to a drone can \ - vary wildly, from cleaning bots barely more advanced than those from the 21st century to cutting-edge \ - supercomputers capable of complex conversation. Drones are legally objects in all starfaring polities \ - outside of the Almach Association, and the sapience of even the most advanced drones is a matter of speculation." - value = CATALOGUER_REWARD_TRIVIAL - // Scanning any drone mob will get you this alongside the mob entry itself. - unlocked_by_any = list(/datum/category_item/catalogue/technology/drone) - -/datum/category_item/catalogue/technology/positronics - name = "Sapients - Positronics" - desc = "A Positronic being, often an Android, Gynoid, or Robot, is an individual with a positronic brain, \ - manufactured and fostered amongst organic life Positronic brains enjoy the same legal status as a humans, \ - although discrimination is still common, are considered sapient on all accounts, and can be considered \ - the \"synthetic species\". Half-developed and half-discovered in the 2280's by a black lab studying alien \ - artifacts, the first positronic brain was an inch-wide cube of palladium-iridium alloy, nano-etched with \ - billions upon billions of conduits and connections. Upon activation, hard-booted by way of an emitter \ - laser, the brain issued a single sentence before the neural pathways collapsed and it became an inert \ - lump of platinum: \"What is my purpose?\"." - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/technology/cyborgs - name = "Cyborgs" - desc = "A Cyborg is an originally organic being composed of largely cybernetic parts. As a brain preserved \ - in an MMI, they may inhabit an expensive humanoid chassis, a specially designed industrial shell of some \ - sort, or be integrated into a computer system as an AI. The term covers all species \ - (even, in some cases, animal brains) and all applications. It can also be used somewhat derogatorily \ - for those who are still have more organic parts than just their brains, but for example have a \ - full set of prosthetic limbs." - value = CATALOGUER_REWARD_TRIVIAL - - -/datum/category_item/catalogue/information - -// For these we can piggyback off of the lore datums that are already defined and used in some places. -/datum/category_item/catalogue/information/organization - value = CATALOGUER_REWARD_TRIVIAL - var/datum_to_copy = null - -/datum/category_item/catalogue/information/organization/New() - ..() - if(datum_to_copy) - // I'd just access the loremaster object but it might not exist because its ugly. - var/datum/lore/organization/O = new datum_to_copy() - // I would also change the name based on the org datum but changing the name messes up indexing in some lists in the category/collection object attached to us. - - // Now lets combine the data in the datum for a slightly more presentable entry. - var/constructed_desc = "" - - if(O.motto) - constructed_desc += "
                    \"[O.motto]\"


                    " - - constructed_desc += O.desc - - desc = constructed_desc - qdel(O) - -/datum/category_item/catalogue/information/organization/nanotrasen - name = "TSC - NanoTrasen Incorporated" - datum_to_copy = /datum/lore/organization/tsc/nanotrasen - -/datum/category_item/catalogue/information/organization/hephaestus - name = "TSC - Hephaestus Industries" - datum_to_copy = /datum/lore/organization/tsc/hephaestus - -/datum/category_item/catalogue/information/organization/vey_med - name = "TSC - Vey-Medical" - datum_to_copy = /datum/lore/organization/tsc/vey_med - -/datum/category_item/catalogue/information/organization/zeng_hu - name = "TSC - Zeng Hu Pharmaceuticals" - datum_to_copy = /datum/lore/organization/tsc/zeng_hu - -/datum/category_item/catalogue/information/organization/ward_takahashi - name = "TSC - Ward-Takahashi General Manufacturing Conglomerate" - datum_to_copy = /datum/lore/organization/tsc/ward_takahashi - -/datum/category_item/catalogue/information/organization/bishop - name = "TSC - Bishop Cybernetics" - datum_to_copy = /datum/lore/organization/tsc/bishop - -/datum/category_item/catalogue/information/organization/morpheus - name = "TSC - Morpheus Cyberkinetics" - datum_to_copy = /datum/lore/organization/tsc/morpheus - -/datum/category_item/catalogue/information/organization/xion - name = "TSC - Xion Manufacturing Group" - datum_to_copy = /datum/lore/organization/tsc/xion - -/datum/category_item/catalogue/information/organization/major_bills - name = "TSC - Major Bill's Transportation" - datum_to_copy = /datum/lore/organization/tsc/mbt - -/datum/category_item/catalogue/information/organization/commonwealth //VS EDIT 1 - name = "Government - Commonwealth of Sol-Procyon" //VS EDIT 2 - datum_to_copy = /datum/lore/organization/gov/commonwealth //VS EDIT 3 - -/* //VOREStation Removal -/datum/category_item/catalogue/information/organization/virgov - name = "Government - Vir Governmental Authority" - datum_to_copy = /datum/lore/organization/gov/virgov -*/ - -/datum/category_item/catalogue/anomalous - - -/datum/category_item/catalogue/anomalous/precursor_controversy - name = "Precursor Controversy" - desc = "The term 'Precursor' is generally used to refer to one or more ancient races that \ - had obtained vast technological and cultural progress, but no longer appear to be present, \ - leaving behind what remains of their creations, as well as many questions for the races that \ - would stumble upon their ruins. Scientists and xenoarcheologists have been hard at work, trying \ - to uncover the truth.\ -

                    \ - In modern times, there is controversy over the accuracy of what knowledge has been uncovered. \ - The mainstream scientific opinion had been that there was one, and only one ancient species, \ - called the Singularitarians. This view still is the majority today, however there has also \ - been dissent over that view, as some feel that the possibility of multiple precursor \ - civilizations should not be ignored. They point towards a large number of discrepancies between \ - the dominant Singularitarian theory, and various artifacts that have been found, as well as \ - different artifacts uncovered appearing to have very different characteristics to each other. \ - Instead, they say that the Singularitarians were one of multiple precursors.\ -

                    \ - Presently, no conclusive evidence exists for any side." - value = CATALOGUER_REWARD_TRIVIAL - // Add the other precursor groups here when they get added. - unlocked_by_any = list( - /datum/category_item/catalogue/anomalous/precursor_a, - /datum/category_item/catalogue/anomalous/precursor_b - ) - -/datum/category_item/catalogue/anomalous/singularitarians - name = "Precursors - Singularitarians" - desc = "The Singularitarians were a massive, highly-advanced spacefaring race which are now \ - believed to be extinct. At their height, they extended throughout all of known human space, \ - with major population centers in the Precursor's Crypt region, as well as significant swaths \ - of Skrell space, until they were wiped out by a self-replicating nanobot plague that still \ - coats their ruins as a fine layer of dust. They left behind the proto-positronics, as well \ - as several high-yield phoron deposits and other artifacts of technology studied, \ - cautiously, by the races that survived them.\ -

                    \ - Very little is known about the biology and physiology of the Singularitarians, who are believed \ - to have been largely post-biological. The Vox claim to be the race that created the positronics, \ - but said claim is only ever brought up when they claim the right to take any positronic they want. \ - Some more open-minded xenoarcheologists have voiced the opinion that there is some truth in their \ - claims, but it's far from a scientific consensus." - value = CATALOGUER_REWARD_TRIVIAL - unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_controversy) - -// Obtained by scanning any 'precursor a' object, generally things in the UFO PoI. -// A is for Ayyyyyy. -/datum/category_item/catalogue/anomalous/precursor_a/precursor_a_basic - name = "Precursors - Precursor Group Alpha" - desc = "This describes a group of xenoarcheological findings which have strong similarities \ - together. Specifically, this group of objects appears to have a strong aesthetic for the colors \ - cyan and pink, both colors often being present on everything in this group. It is unknown why \ - these two colors were chosen by their creators. Another similarity is that most objects made \ - in this group appear to be comprised of not well understood metallic materials that are dark, \ - and very resilient. Some objects in this group also appear to utilize electricity to \ - operate. Finally, a large number of objects in this group appear to have been made \ - to be used by the creators of those objects in a physical manner.\ -

                    \ - It should be noted that the findings in this group appear to conflict heavily with what is \ - known about the Singularitarians, giving some credence towards these objects belonging to a \ - separate precursor. As such, the findings have been partitioned inside this scanner to this \ - group, labeled Precursor Group Alpha." - value = CATALOGUER_REWARD_TRIVIAL - unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_a) - -// Obtained by scanning any 'precursor b' object, generally things dug up from xenoarch. -// B is for buried. -/datum/category_item/catalogue/anomalous/precursor_b/precursor_b_basic - name = "Precursors - Precursor Group Beta" - - -/datum/category_item/catalogue/material +GLOBAL_DATUM_INIT(catalogue_data, /datum/category_collection/catalogue, new) + +// The collection holds everything together and is GLOB accessible. +/datum/category_collection/catalogue + category_group_type = /datum/category_group/catalogue + +/datum/category_collection/catalogue/proc/resolve_item(item_path) + for(var/group in categories) + var/datum/category_group/G = group + + var/datum/category_item/catalogue/C = item_path + var/name_to_search = initial(C.name) + if(G.items_by_name[name_to_search]) + return G.items_by_name[name_to_search] + + // for(var/item in G.items) + // var/datum/category_item/I = item + // if(I.type == item_path) + // return I + + +// Groups act as sections for the different data. +/datum/category_group/catalogue + +// Plants. +/datum/category_group/catalogue/flora + name = "Flora" + category_item_type = /datum/category_item/catalogue/flora + +// Animals. +/datum/category_group/catalogue/fauna + name = "Fauna" + category_item_type = /datum/category_item/catalogue/fauna + +// Gadgets, tech, and robots. +/datum/category_group/catalogue/technology + name = "Technology" + category_item_type = /datum/category_item/catalogue/technology + +// Abstract information. +/datum/category_group/catalogue/information + name = "Information" + category_item_type = /datum/category_item/catalogue/information + +// Weird stuff like precursors. +/datum/category_group/catalogue/anomalous + name = "Anomalous" + category_item_type = /datum/category_item/catalogue/anomalous + +// Physical material things like crystals and metals. +/datum/category_group/catalogue/material + name = "Material" + category_item_type = /datum/category_item/catalogue/material + + +// Items act as individual data for each object. +/datum/category_item/catalogue + var/desc = null // Paragraph or two about what the object is. + var/value = 0 // How many 'exploration points' you get for scanning it. Suggested to use the CATALOGUER_REWARD_* defines for easy tweaking. + var/visible = FALSE // When someone scans the correct object, this gets set to TRUE and becomes viewable in the databanks. + var/list/cataloguers = null // List of names of those who helped 'discover' this piece of data, in string form. + var/list/unlocked_by_any = null // List of types that, if they are discovered, it will also make this datum discovered. + var/list/unlocked_by_all = null // Similar to above, but all types on the list must be discovered for this to be discovered. + +// Discovers a specific datum, and any datums associated with this datum by unlocked_by_[any|all]. +// Returns null if nothing was found, otherwise returns a list of datum instances that was discovered, usually for the cataloguer to use. +/datum/category_item/catalogue/proc/discover(mob/user, list/new_cataloguers) + if(visible) // Already found. + return + + . = list(src) + visible = TRUE + cataloguers = new_cataloguers + display_in_chatlog(user) + . += attempt_chain_discoveries(user, new_cataloguers, type) + +// Calls discover() on other datums if they include the type that was just discovered is inside unlocked_by_[any|all]. +// Returns discovered datums. +/datum/category_item/catalogue/proc/attempt_chain_discoveries(mob/user, list/new_cataloguers, type_to_test) + . = list() + for(var/G in category.collection.categories) // I heard you like loops. + var/datum/category_group/catalogue/group = G + for(var/I in group.items) + var/datum/category_item/catalogue/item = I + // First, look for datums unlocked with the 'any' list. + if(LAZYLEN(item.unlocked_by_any)) + for(var/T in item.unlocked_by_any) + if(ispath(type_to_test, T) && item.discover(user, new_cataloguers)) + . += item + + // Now for the more complicated 'all' list. + if(LAZYLEN(item.unlocked_by_all)) + if(type_to_test in item.unlocked_by_all) + // Unlike the 'any' list, the 'all' list requires that all datums inside it to have been found first. + var/should_discover = TRUE + for(var/T in item.unlocked_by_all) + var/datum/category_item/catalogue/thing = GLOB.catalogue_data.resolve_item(T) + if(istype(thing)) + if(!thing.visible) + should_discover = FALSE + break + if(should_discover && item.discover(user, new_cataloguers)) + . += item + +/datum/category_item/catalogue/proc/display_in_chatlog(mob/user) + to_chat(user, "
                    ") + to_chat(user, span("notice", "[uppertext(name)]")) + + // Some entries get very long so lets not totally flood the chatlog. + var/desc_length_limit = 750 + var/displayed_desc = desc + if(length(desc) > desc_length_limit) + displayed_desc = copytext(displayed_desc, 1, desc_length_limit + 1) + displayed_desc += "... (View databanks for full data)" + + to_chat(user, span("notice", "[displayed_desc]")) + to_chat(user, span("notice", "Cataloguers : [english_list(cataloguers)].")) + to_chat(user, span("notice", "Contributes [value] points to personal exploration fund.")) + +/* + // Truncates text to limit if necessary. + var/size = length(message) + if (size <= length) + return message + else + return copytext(message, 1, length + 1) +*/ + +/datum/category_item/catalogue/flora + +/datum/category_item/catalogue/fauna + +/datum/category_item/catalogue/fauna/humans + name = "Sapients - Humans" + desc = "Humans are a space-faring species hailing originally from the planet Earth in the Sol system. \ + They are currently among the most numerous known species in the galaxy, in both population and holdings, \ + and are relatively technologically advanced. With good healthcare and a reasonable lifestyle, \ + they can live to around 110 years. The oldest humans are around 150 years old.\ +

                    \ + Humanity is the primary driving force for rapid space expansion, owing to their strong, expansionist central \ + government and opportunistic Trans-Stellar Corporations. The prejudices of the 21st century have mostly \ + given way to bitter divides on the most important issue of the times- technological expansionism, \ + with the major human factions squabbling over their approach to technology in the face of a \ + looming singularity.\ +

                    \ + While most humans have accepted the existence of aliens in their communities and workplaces as a \ + fact of life, exceptions abound. While more culturally diverse than most species, humans are \ + generally regarded as somewhat technophobic and isolationist by members of other species." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/skrell + name = "Sapients - Skrell" + desc = "The Skrell are a species of amphibious humanoids, distinguished by their green-blue gelatinous \ + appearance and head tentacles. Skrell warble from the world of Qerr'balak, a humid planet with \ + plenty of swamps and jungles. Currently more technologically advanced than humanity, they \ + emphasize the study of the mind above all else.\ +

                    \ + Gender has little meaning to Skrell outside of reproduction, and in fact many other species \ + have a difficult time telling the difference between male and female Skrell apart. The most \ + obvious signs (voice in a slightly higher register, longer head-tails for females) are \ + never a guarantee.\ +

                    \ + Due to their scientific focus of the mind and body, Skrell tend to be more peaceful and their \ + colonization has been slow, swiftly outpaced by humanity. They were the first contact sentient \ + species, and are humanity's longest, and closest, ally in space." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/unathi + name = "Sapients - Unathi" + desc = "The Unathi are a species of large reptilian humanoids hailing from Moghes, in the \ + Uueoa-Esa binary star system. Most Unathi live in a semi-rigid clan system, and clan \ + enclaves dot the surface of their homeworld. Proud and long-lived, Unathi of all \ + walks of life display a tendency towards perfectionism, and mastery of one's craft \ + is greatly respected among them. Despite the aggressive nature of their contact, \ + Unathi seem willing, if not eager, to reconcile with humanity, though mutual \ + distrust runs rampant among individuals of both groups." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/tajaran + name = "Sapients - Tajaran" + desc = "Tajaran are a race of humanoid mammalian aliens from Meralar, the fourth planet \ + of the Rarkajar star system. Thickly furred and protected from cold, they thrive on \ + their subartic planet, where the only terran temperate areas spread across the \ + equator and \"tropical belt.\"\ +

                    \ + With their own share of bloody wars and great technological advances, the Tajaran are a \ + proud kind. They fiercely believe they belong among the stars and consider themselves \ + a rightful interstellar nation, even if Humanity helped them to actually achieve \ + superluminar speeds with Bluespace FTL drives.\ +

                    \ + Relatively new to the galaxy, their contacts with other species are aloof, but friendly. \ + Among these bonds, Humanity stands out as valued trade partner and maybe even friend." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/dionaea + name = "Sapients - Dionaea" + desc = "The Dionaea are a group of intensely curious plant-like organisms. An individual \ + Diona is a single dog-sized creature called a nymphs, and multiple nymphs link together \ + to form larger, more intelligent collectives. Discovered by the Skrell in and around \ + the stars in the Epsilon Ursae Minoris system, they have accompanied the Skrell in \ + warbling throughout the cosmos as a key part of Skrellian starships, stations, \ + and terraforming equipment.\ +

                    \ + Dionaea have no concept of violence or individual identity and want little in \ + terms of material resources or living space. This makes Dionaea among the most \ + agreeable members of the galactic community, though their slow, curious alien \ + minds can be hard to sympathize with." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/teshari + name = "Sapients - Teshari" + desc = "The Teshari are reptilian pack predators from the Skrell homeworld. \ + While they evolved alongside the Skrell, their interactions with them tended \ + to be confused and violent, and until peaceful contact was made they largely \ + stayed in their territories on and around the poles, in tundral terrain far \ + too desolate and cold to be of interest to the Skrell. In more enlightened \ + times, the Teshari are a minority culture on many Skrell worlds, maintaining \ + their own settlements and cultures, but often finding themselves standing \ + on the shoulders of their more technologically advanced neighbors when it \ + comes to meeting and exploring the rest of the galaxy." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/zaddat + name = "Sapients - Zaddat" + desc = "The Zaddat are an Unathi client species that has recently come to the \ + Golden Crescent. They wear high-pressure voidsuits called Shrouds to protect \ + themselves from the harsh light and low pressure of the station, making \ + medical care a challenge and fighting especially dangerous. \ + Operating out of massive Colony ships, they trade their labor to their \ + host nation to fund their search for a new home to replace their \ + now-lifeless homeworld of Xohox." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/promethean + name = "Sapients - Promethean" + desc = "Prometheans (Macrolimus artificialis) are a species of artificially-created \ + gelatinous humanoids, chiefly characterized by their primarily liquid bodies and \ + ability to change their bodily shape and color in order to mimic many forms of life. \ + Derived from the Aetolian giant slime (Macrolimus vulgaris) inhabiting the warm, \ + tropical planet of Aetolus, they are a relatively newly lab-created sapient species, \ + and as such many things about them have yet to be comprehensively studied." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/fauna/vox + name = "Sapients - Vox" + desc = "Probably the best known of these aliens are the Vox, a bird-like species \ + with a very rough comprehension of Galactic Common and an even looser understanding \ + of property rights. Vox raiders have plagued human merchants for centuries, \ + and Skrell for even longer, but remain poorly understood. \ + They have no desire to partake in diplomacy or trade with the rest of the galaxy, \ + or even to conquer planets and stations to live in. They breathe phoron \ + and appear to be well adapted to their role as space-faring raiders, \ + leading many to speculate that they're heavily bioengineered, \ + an assumption which is at odds with their ramshackle technological level." + value = CATALOGUER_REWARD_MEDIUM // Since Vox are much rarer. + + +/datum/category_item/catalogue/technology + +/datum/category_item/catalogue/technology/drone/drones + name = "Drones" + desc = "A drone is a software-based artificial intelligence, generally about an order of magnitude \ + less intelligent than a positronic brain. However, the processing power available to a drone can \ + vary wildly, from cleaning bots barely more advanced than those from the 21st century to cutting-edge \ + supercomputers capable of complex conversation. Drones are legally objects in all starfaring polities \ + outside of the Almach Association, and the sapience of even the most advanced drones is a matter of speculation." + value = CATALOGUER_REWARD_TRIVIAL + // Scanning any drone mob will get you this alongside the mob entry itself. + unlocked_by_any = list(/datum/category_item/catalogue/technology/drone) + +/datum/category_item/catalogue/technology/positronics + name = "Sapients - Positronics" + desc = "A Positronic being, often an Android, Gynoid, or Robot, is an individual with a positronic brain, \ + manufactured and fostered amongst organic life Positronic brains enjoy the same legal status as a humans, \ + although discrimination is still common, are considered sapient on all accounts, and can be considered \ + the \"synthetic species\". Half-developed and half-discovered in the 2280's by a black lab studying alien \ + artifacts, the first positronic brain was an inch-wide cube of palladium-iridium alloy, nano-etched with \ + billions upon billions of conduits and connections. Upon activation, hard-booted by way of an emitter \ + laser, the brain issued a single sentence before the neural pathways collapsed and it became an inert \ + lump of platinum: \"What is my purpose?\"." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/category_item/catalogue/technology/cyborgs + name = "Cyborgs" + desc = "A Cyborg is an originally organic being composed of largely cybernetic parts. As a brain preserved \ + in an MMI, they may inhabit an expensive humanoid chassis, a specially designed industrial shell of some \ + sort, or be integrated into a computer system as an AI. The term covers all species \ + (even, in some cases, animal brains) and all applications. It can also be used somewhat derogatorily \ + for those who are still have more organic parts than just their brains, but for example have a \ + full set of prosthetic limbs." + value = CATALOGUER_REWARD_TRIVIAL + + +/datum/category_item/catalogue/information + +// For these we can piggyback off of the lore datums that are already defined and used in some places. +/datum/category_item/catalogue/information/organization + value = CATALOGUER_REWARD_TRIVIAL + var/datum_to_copy = null + +/datum/category_item/catalogue/information/organization/New() + ..() + if(datum_to_copy) + // I'd just access the loremaster object but it might not exist because its ugly. + var/datum/lore/organization/O = new datum_to_copy() + // I would also change the name based on the org datum but changing the name messes up indexing in some lists in the category/collection object attached to us. + + // Now lets combine the data in the datum for a slightly more presentable entry. + var/constructed_desc = "" + + if(O.motto) + constructed_desc += "
                    \"[O.motto]\"


                    " + + constructed_desc += O.desc + + desc = constructed_desc + qdel(O) + +/datum/category_item/catalogue/information/organization/nanotrasen + name = "TSC - NanoTrasen Incorporated" + datum_to_copy = /datum/lore/organization/tsc/nanotrasen + +/datum/category_item/catalogue/information/organization/hephaestus + name = "TSC - Hephaestus Industries" + datum_to_copy = /datum/lore/organization/tsc/hephaestus + +/datum/category_item/catalogue/information/organization/vey_med + name = "TSC - Vey-Medical" + datum_to_copy = /datum/lore/organization/tsc/vey_med + +/datum/category_item/catalogue/information/organization/zeng_hu + name = "TSC - Zeng Hu Pharmaceuticals" + datum_to_copy = /datum/lore/organization/tsc/zeng_hu + +/datum/category_item/catalogue/information/organization/ward_takahashi + name = "TSC - Ward-Takahashi General Manufacturing Conglomerate" + datum_to_copy = /datum/lore/organization/tsc/ward_takahashi + +/datum/category_item/catalogue/information/organization/bishop + name = "TSC - Bishop Cybernetics" + datum_to_copy = /datum/lore/organization/tsc/bishop + +/datum/category_item/catalogue/information/organization/morpheus + name = "TSC - Morpheus Cyberkinetics" + datum_to_copy = /datum/lore/organization/tsc/morpheus + +/datum/category_item/catalogue/information/organization/xion + name = "TSC - Xion Manufacturing Group" + datum_to_copy = /datum/lore/organization/tsc/xion + +/datum/category_item/catalogue/information/organization/major_bills + name = "TSC - Major Bill's Transportation" + datum_to_copy = /datum/lore/organization/tsc/mbt + +/datum/category_item/catalogue/information/organization/commonwealth //VS EDIT 1 + name = "Government - Commonwealth of Sol-Procyon" //VS EDIT 2 + datum_to_copy = /datum/lore/organization/gov/commonwealth //VS EDIT 3 + +/* //VOREStation Removal +/datum/category_item/catalogue/information/organization/virgov + name = "Government - Vir Governmental Authority" + datum_to_copy = /datum/lore/organization/gov/virgov +*/ + +/datum/category_item/catalogue/anomalous + + +/datum/category_item/catalogue/anomalous/precursor_controversy + name = "Precursor Controversy" + desc = "The term 'Precursor' is generally used to refer to one or more ancient races that \ + had obtained vast technological and cultural progress, but no longer appear to be present, \ + leaving behind what remains of their creations, as well as many questions for the races that \ + would stumble upon their ruins. Scientists and xenoarcheologists have been hard at work, trying \ + to uncover the truth.\ +

                    \ + In modern times, there is controversy over the accuracy of what knowledge has been uncovered. \ + The mainstream scientific opinion had been that there was one, and only one ancient species, \ + called the Singularitarians. This view still is the majority today, however there has also \ + been dissent over that view, as some feel that the possibility of multiple precursor \ + civilizations should not be ignored. They point towards a large number of discrepancies between \ + the dominant Singularitarian theory, and various artifacts that have been found, as well as \ + different artifacts uncovered appearing to have very different characteristics to each other. \ + Instead, they say that the Singularitarians were one of multiple precursors.\ +

                    \ + Presently, no conclusive evidence exists for any side." + value = CATALOGUER_REWARD_TRIVIAL + // Add the other precursor groups here when they get added. + unlocked_by_any = list( + /datum/category_item/catalogue/anomalous/precursor_a, + /datum/category_item/catalogue/anomalous/precursor_b + ) + +/datum/category_item/catalogue/anomalous/singularitarians + name = "Precursors - Singularitarians" + desc = "The Singularitarians were a massive, highly-advanced spacefaring race which are now \ + believed to be extinct. At their height, they extended throughout all of known human space, \ + with major population centers in the Precursor's Crypt region, as well as significant swaths \ + of Skrell space, until they were wiped out by a self-replicating nanobot plague that still \ + coats their ruins as a fine layer of dust. They left behind the proto-positronics, as well \ + as several high-yield phoron deposits and other artifacts of technology studied, \ + cautiously, by the races that survived them.\ +

                    \ + Very little is known about the biology and physiology of the Singularitarians, who are believed \ + to have been largely post-biological. The Vox claim to be the race that created the positronics, \ + but said claim is only ever brought up when they claim the right to take any positronic they want. \ + Some more open-minded xenoarcheologists have voiced the opinion that there is some truth in their \ + claims, but it's far from a scientific consensus." + value = CATALOGUER_REWARD_TRIVIAL + unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_controversy) + +// Obtained by scanning any 'precursor a' object, generally things in the UFO PoI. +// A is for Ayyyyyy. +/datum/category_item/catalogue/anomalous/precursor_a/precursor_a_basic + name = "Precursors - Precursor Group Alpha" + desc = "This describes a group of xenoarcheological findings which have strong similarities \ + together. Specifically, this group of objects appears to have a strong aesthetic for the colors \ + cyan and pink, both colors often being present on everything in this group. It is unknown why \ + these two colors were chosen by their creators. Another similarity is that most objects made \ + in this group appear to be comprised of not well understood metallic materials that are dark, \ + and very resilient. Some objects in this group also appear to utilize electricity to \ + operate. Finally, a large number of objects in this group appear to have been made \ + to be used by the creators of those objects in a physical manner.\ +

                    \ + It should be noted that the findings in this group appear to conflict heavily with what is \ + known about the Singularitarians, giving some credence towards these objects belonging to a \ + separate precursor. As such, the findings have been partitioned inside this scanner to this \ + group, labeled Precursor Group Alpha." + value = CATALOGUER_REWARD_TRIVIAL + unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_a) + +// Obtained by scanning any 'precursor b' object, generally things dug up from xenoarch. +// B is for buried. +/datum/category_item/catalogue/anomalous/precursor_b/precursor_b_basic + name = "Precursors - Precursor Group Beta" + + +/datum/category_item/catalogue/material diff --git a/code/modules/catalogue/cataloguer.dm b/code/modules/catalogue/cataloguer.dm index 36d78ff7cf8..580b209b9e0 100644 --- a/code/modules/catalogue/cataloguer.dm +++ b/code/modules/catalogue/cataloguer.dm @@ -1,319 +1,383 @@ -GLOBAL_LIST_EMPTY(all_cataloguers) - -/* - This is a special scanner which exists to give explorers something to do besides shoot things. - The scanner is able to be used on certain things in the world, and after a variable delay, the scan finishes, - giving the person who scanned it some fluff and information about what they just scanned, - as well as points that currently do nothing but measure epeen, - and will be used as currency in The Future(tm) to buy things explorers care about. - - Scanning hostile mobs and objects is tricky since only mobs that are alive are scannable, so scanning - them requires careful position to stay out of harms way until the scan finishes. That is why - the person with the scanner gets a visual box that shows where they are allowed to move to - without inturrupting the scan. -*/ -/obj/item/device/cataloguer - name = "cataloguer" - desc = "A hand-held device, used for compiling information about an object by scanning it. Alt+click to highlight scannable objects around you." - description_info = "This is a special device used to obtain information about objects and entities in the environment. \ - To scan something, click on it with the scanner at a distance. \ - Scanning something requires remaining within a certain radius of the object for a specific period of time, until the \ - scan is finished. If the scan is interrupted, it can be resumed from where it was left off, if the same thing is \ - scanned again." - icon = 'icons/obj/device.dmi' - icon_state = "cataloguer" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_MATERIAL = 2, TECH_DATA = 3, TECH_MAGNET = 3) - force = 0 - slot_flags = SLOT_BELT - var/points_stored = 0 // Amount of 'exploration points' this device holds. - var/scan_range = 3 // How many tiles away it can scan. Changing this also changes the box size. - var/credit_sharing_range = 14 // If another person is within this radius, they will also be credited with a successful scan. - var/datum/category_item/catalogue/displayed_data = null // Used for viewing a piece of data in the UI. - var/busy = FALSE // Set to true when scanning, to stop multiple scans. - var/debug = FALSE // If true, can view all catalogue data defined, regardless of unlock status. - var/datum/weakref/partial_scanned = null // Weakref of the thing that was last scanned if inturrupted. Used to allow for partial scans to be resumed. - var/partial_scan_time = 0 // How much to make the next scan shorter. - -/obj/item/device/cataloguer/advanced - name = "advanced cataloguer" - icon_state = "adv_cataloguer" - desc = "A hand-held device, used for compiling information about an object by scanning it. This one is an upgraded model, \ - with a scanner that both can scan from farther away, and with less time." - scan_range = 4 - toolspeed = 0.8 - -// Able to see all defined catalogue data regardless of if it was unlocked, intended for testing. -/obj/item/device/cataloguer/debug - name = "omniscient cataloguer" - desc = "A hand-held cataloguer device that appears to be plated with gold. For some reason, it \ - just seems to already know everything about narrowly defined pieces of knowledge one would find \ - from nearby, perhaps due to being colored gold. Truly a epistemological mystery." - icon_state = "debug_cataloguer" - toolspeed = 0.1 - scan_range = 7 - debug = TRUE - - -/obj/item/device/cataloguer/Initialize() - GLOB.all_cataloguers += src - return ..() - -/obj/item/device/cataloguer/Destroy() - GLOB.all_cataloguers -= src - displayed_data = null - return ..() - -/obj/item/device/cataloguer/update_icon() - if(busy) - icon_state = "[initial(icon_state)]_active" - else - icon_state = initial(icon_state) - -/obj/item/device/cataloguer/afterattack(atom/target, mob/user, proximity_flag) - // Things that invalidate the scan immediately. - if(busy) - to_chat(user, span("warning", "\The [src] is already scanning something.")) - return - - if(isturf(target) && (!target.can_catalogue())) - var/turf/T = target - for(var/atom/A as anything in T) // If we can't scan the turf, see if we can scan anything on it, to help with aiming. - if(A.can_catalogue()) - target = A - break - - if(!target.can_catalogue(user)) // This will tell the user what is wrong. - return - - if(get_dist(target, user) > scan_range) - to_chat(user, span("warning", "You are too far away from \the [target] to catalogue it. Get closer.")) - return - - // Get how long the delay will be. - var/scan_delay = target.get_catalogue_delay() * toolspeed - if(partial_scanned) - if(partial_scanned.resolve() == target) - scan_delay -= partial_scan_time - to_chat(user, span("notice", "Resuming previous scan.")) - else - to_chat(user, span("warning", "Scanning new target. Previous scan buffer cleared.")) - - // Start the special effects. - busy = TRUE - update_icon() - var/datum/beam/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay) - var/filter = filter(type = "outline", size = 1, color = "#FFFFFF") - target.filters += filter - var/list/box_segments = list() - if(user.client) - box_segments = draw_box(target, scan_range, user.client) - color_box(box_segments, "#00FFFF", scan_delay) - - playsound(src, 'sound/machines/beep.ogg', 50) - - // The delay, and test for if the scan succeeds or not. - var/scan_start_time = world.time - if(do_after(user, scan_delay, target, ignore_movement = TRUE, max_distance = scan_range)) - if(target.can_catalogue(user)) - to_chat(user, span("notice", "You successfully scan \the [target] with \the [src].")) - playsound(src, 'sound/machines/ping.ogg', 50) - catalogue_object(target, user) - else - // In case someone else scans it first, or it died, etc. - to_chat(user, span("warning", "\The [target] is no longer valid to scan with \the [src].")) - playsound(src, 'sound/machines/buzz-two.ogg', 50) - - partial_scanned = null - partial_scan_time = 0 - else - to_chat(user, span("warning", "You failed to finish scanning \the [target] with \the [src].")) - playsound(src, 'sound/machines/buzz-two.ogg', 50) - color_box(box_segments, "#FF0000", 3) - partial_scanned = WEAKREF(target) - partial_scan_time += world.time - scan_start_time // This is added to the existing value so two partial scans will add up correctly. - sleep(3) - busy = FALSE - - // Now clean up the effects. - update_icon() - QDEL_NULL(scan_beam) - if(target) - target.filters -= filter - if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways. - delete_box(box_segments, user.client) - -// Todo: Display scanned information, increment points, etc. -/obj/item/device/cataloguer/proc/catalogue_object(atom/target, mob/living/user) - // Figure out who may have helped out. - var/list/contributers = list() - var/list/contributer_names = list() - for(var/mob/living/L as anything in player_list) - if(L == user) - continue - if(!istype(L)) - continue - if(get_dist(L, user) <= credit_sharing_range) - contributers += L - contributer_names += L.name - - var/points_gained = 0 - - // Discover each datum available. - var/list/object_data = target.get_catalogue_data() - if(LAZYLEN(object_data)) - for(var/data_type in object_data) - var/datum/category_item/catalogue/I = GLOB.catalogue_data.resolve_item(data_type) - if(istype(I)) - var/list/discoveries = I.discover(user, list(user.name) + contributer_names) // If one discovery leads to another, the list returned will have all of them. - if(LAZYLEN(discoveries)) - for(var/datum/category_item/catalogue/data as anything in discoveries) - points_gained += data.value - - // Give out points. - if(points_gained) - // First, to us. - to_chat(user, span("notice", "Gained [points_gained] points from this scan.")) - adjust_points(points_gained) - - // Now to our friends, if any. - if(contributers.len) - for(var/mob/M in contributers) - var/list/things = M.GetAllContents(3) // Depth of two should reach into bags but just in case lets make it three. - var/obj/item/device/cataloguer/other_cataloguer = locate() in things // If someone has two or more scanners this only adds points to one. - if(other_cataloguer) - to_chat(M, span("notice", "Gained [points_gained] points from \the [user]'s scan of \the [target].")) - other_cataloguer.adjust_points(points_gained) - to_chat(user, span("notice", "Shared discovery with [contributers.len] other contributer\s.")) - - - - -/obj/item/device/cataloguer/AltClick(mob/user) - pulse_scan(user) - -// Gives everything capable of being scanned an outline for a brief moment. -// Helps to avoid having to click a hundred things in a room for things that have an entry. -/obj/item/device/cataloguer/proc/pulse_scan(mob/user) - if(busy) - to_chat(user, span("warning", "\The [src] is busy doing something else.")) - return - - busy = TRUE - update_icon() - playsound(src, 'sound/machines/beep.ogg', 50) - - // First, get everything able to be scanned. - var/list/scannable_atoms = list() - for(var/atom/A as anything in view(world.view, user)) - if(A.can_catalogue()) // Not passing the user is intentional, so they don't get spammed. - scannable_atoms += A - - // Highlight things able to be scanned. - var/filter = filter(type = "outline", size = 1, color = "#00FF00") - for(var/atom/A as anything in scannable_atoms) - A.filters += filter - to_chat(user, span("notice", "\The [src] is highlighting scannable objects in green, if any exist.")) - - sleep(2 SECONDS) - - // Remove the highlights. - for(var/atom/A as anything in scannable_atoms) - if(QDELETED(A)) - continue - A.filters -= filter - - busy = FALSE - update_icon() - if(scannable_atoms.len) - playsound(src, 'sound/machines/ping.ogg', 50) - else - playsound(src, 'sound/machines/buzz-two.ogg', 50) - to_chat(user, span("notice", "\The [src] found [scannable_atoms.len] object\s that can be scanned.")) - - -// Negative points are bad. -/obj/item/device/cataloguer/proc/adjust_points(amount) - points_stored = max(0, points_stored += amount) - -/obj/item/device/cataloguer/attack_self(mob/living/user) - interact(user) - -/obj/item/device/cataloguer/interact(mob/user) - var/list/dat = list() - var/title = "Cataloguer Data Display" - - // Important buttons go on top since the scrollbar will default to the top of the window. - dat += "Contains [points_stored] Exploration Points." - dat += "\[Highlight Scannables\]\[Refresh\]\[Close\]" - - // If displayed_data exists, we show that, otherwise we show a list of all data in the mysterious global list. - if(displayed_data) - title = uppertext(displayed_data.name) - - dat += "\[Back to List\]" - if(debug && !displayed_data.visible) - dat += "\[(DEBUG) Force Discovery\]" - dat += "
                    " - - dat += "[displayed_data.desc]" - if(LAZYLEN(displayed_data.cataloguers)) - dat += "Cataloguers : [english_list(displayed_data.cataloguers)]." - else - dat += "Catalogued by nobody." - dat += "Worth [displayed_data.value] exploration points." - - else - dat += "
                    " - for(var/datum/category_group/group as anything in GLOB.catalogue_data.categories) - var/list/group_dat = list() - var/show_group = FALSE - - group_dat += "[group.name]" - for(var/datum/category_item/catalogue/item as anything in group.items) - if(item.visible || debug) - group_dat += "[item.name]" - show_group = TRUE - - if(show_group || debug) // Avoid showing 'empty' groups on regular cataloguers. - dat += group_dat - - var/datum/browser/popup = new(user, "cataloguer_display_\ref[src]", title, 500, 600, src) - popup.set_content(dat.Join("
                    ")) - popup.open() - add_fingerprint(user) - -/obj/item/device/cataloguer/Topic(href, href_list) - if(..()) - usr << browse(null, "window=cataloguer_display") - return 0 - if(href_list["close"] ) - usr << browse(null, "window=cataloguer_display") - return 0 - - if(href_list["show_data"]) - displayed_data = locate(href_list["show_data"]) - - if(href_list["pulse_scan"]) - pulse_scan(usr) - return // Don't refresh the window for this or it will open it back if its closed during the highlighting. - - if(href_list["debug_unlock"] && debug) - var/datum/category_item/catalogue/item = locate(href_list["debug_unlock"]) - item.discover(usr, list("Debugger")) - - interact(usr) // So it refreshes the window. - return 1 - -/obj/item/device/cataloguer/attackby(obj/item/weapon/W, mob/user) - if(istype(W, /obj/item/weapon/card/id) && !busy) - busy = TRUE - var/obj/item/weapon/card/id/ID = W - if(points_stored) - ID.survey_points += points_stored - points_stored = 0 - to_chat(user, "You swipe the id over \the [src].") - else - to_chat(user, "\The [src] has no points available.") - busy = FALSE - return ..() +GLOBAL_LIST_EMPTY(all_cataloguers) + +/* + This is a special scanner which exists to give explorers something to do besides shoot things. + The scanner is able to be used on certain things in the world, and after a variable delay, the scan finishes, + giving the person who scanned it some fluff and information about what they just scanned, + as well as points that currently do nothing but measure epeen, + and will be used as currency in The Future(tm) to buy things explorers care about. + + Scanning hostile mobs and objects is tricky since only mobs that are alive are scannable, so scanning + them requires careful position to stay out of harms way until the scan finishes. That is why + the person with the scanner gets a visual box that shows where they are allowed to move to + without inturrupting the scan. +*/ +/obj/item/device/cataloguer + name = "cataloguer" + desc = "A hand-held device, used for compiling information about an object by scanning it. Alt+click to highlight scannable objects around you." + description_info = "This is a special device used to obtain information about objects and entities in the environment. \ + To scan something, click on it with the scanner at a distance. \ + Scanning something requires remaining within a certain radius of the object for a specific period of time, until the \ + scan is finished. If the scan is interrupted, it can be resumed from where it was left off, if the same thing is \ + scanned again." + icon = 'icons/obj/device_vr.dmi' + icon_state = "cataloguer" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_MATERIAL = 2, TECH_DATA = 3, TECH_MAGNET = 3) + force = 0 + slot_flags = SLOT_BELT + var/points_stored = 0 // Amount of 'exploration points' this device holds. + var/scan_range = 3 // How many tiles away it can scan. Changing this also changes the box size. + var/credit_sharing_range = 280 // If another person is within this radius, they will also be credited with a successful scan. Original was 14 + var/datum/category_item/catalogue/displayed_data = null // Used for viewing a piece of data in the UI. + var/busy = FALSE // Set to true when scanning, to stop multiple scans. + var/debug = FALSE // If true, can view all catalogue data defined, regardless of unlock status. + var/datum/weakref/partial_scanned = null // Weakref of the thing that was last scanned if inturrupted. Used to allow for partial scans to be resumed. + var/partial_scan_time = 0 // How much to make the next scan shorter. + +/obj/item/device/cataloguer/advanced + name = "advanced cataloguer" + icon = 'icons/obj/device.dmi' + icon_state = "adv_cataloguer" + desc = "A hand-held device, used for compiling information about an object by scanning it. This one is an upgraded model, \ + with a scanner that both can scan from farther away, and with less time." + scan_range = 4 + toolspeed = 0.8 + +// Able to see all defined catalogue data regardless of if it was unlocked, intended for testing. +/obj/item/device/cataloguer/debug + name = "omniscient cataloguer" + desc = "A hand-held cataloguer device that appears to be plated with gold. For some reason, it \ + just seems to already know everything about narrowly defined pieces of knowledge one would find \ + from nearby, perhaps due to being colored gold. Truly a epistemological mystery." + icon = 'icons/obj/device.dmi' + icon_state = "debug_cataloguer" + toolspeed = 0.1 + scan_range = 7 + debug = TRUE + + +/obj/item/device/cataloguer/Initialize() + GLOB.all_cataloguers += src + return ..() + +/obj/item/device/cataloguer/Destroy() + GLOB.all_cataloguers -= src + displayed_data = null + return ..() + +/obj/item/device/cataloguer/update_icon() + if(busy) + icon_state = "[initial(icon_state)]_active" + else + icon_state = initial(icon_state) + +/obj/item/device/cataloguer/afterattack(atom/target, mob/user, proximity_flag) + // Things that invalidate the scan immediately. + if(busy) + to_chat(user, span("warning", "\The [src] is already scanning something.")) + return + + if(isturf(target) && (!target.can_catalogue())) + var/turf/T = target + for(var/atom/A as anything in T) // If we can't scan the turf, see if we can scan anything on it, to help with aiming. + if(A.can_catalogue()) + target = A + break + + if(!target.can_catalogue(user)) // This will tell the user what is wrong. + return + + if(get_dist(target, user) > scan_range) + to_chat(user, span("warning", "You are too far away from \the [target] to catalogue it. Get closer.")) + return + + // Get how long the delay will be. + var/scan_delay = target.get_catalogue_delay() * toolspeed + if(partial_scanned) + if(partial_scanned.resolve() == target) + scan_delay -= partial_scan_time + to_chat(user, span("notice", "Resuming previous scan.")) + else + to_chat(user, span("warning", "Scanning new target. Previous scan buffer cleared.")) + + // Start the special effects. + busy = TRUE + update_icon() + var/datum/beam/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay) + var/filter = filter(type = "outline", size = 1, color = "#FFFFFF") + target.filters += filter + var/list/box_segments = list() + if(user.client) + box_segments = draw_box(target, scan_range, user.client) + color_box(box_segments, "#00FFFF", scan_delay) + + playsound(src, 'sound/machines/beep.ogg', 50) + + // The delay, and test for if the scan succeeds or not. + var/scan_start_time = world.time + if(do_after(user, scan_delay, target, ignore_movement = TRUE, max_distance = scan_range)) + if(target.can_catalogue(user)) + to_chat(user, span("notice", "You successfully scan \the [target] with \the [src].")) + playsound(src, 'sound/machines/ping.ogg', 50) + catalogue_object(target, user) + else + // In case someone else scans it first, or it died, etc. + to_chat(user, span("warning", "\The [target] is no longer valid to scan with \the [src].")) + playsound(src, 'sound/machines/buzz-two.ogg', 50) + + partial_scanned = null + partial_scan_time = 0 + else + to_chat(user, span("warning", "You failed to finish scanning \the [target] with \the [src].")) + playsound(src, 'sound/machines/buzz-two.ogg', 50) + color_box(box_segments, "#FF0000", 3) + partial_scanned = WEAKREF(target) + partial_scan_time += world.time - scan_start_time // This is added to the existing value so two partial scans will add up correctly. + sleep(3) + busy = FALSE + + // Now clean up the effects. + update_icon() + QDEL_NULL(scan_beam) + if(target) + target.filters -= filter + if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways. + delete_box(box_segments, user.client) + +// Todo: Display scanned information, increment points, etc. +/obj/item/device/cataloguer/proc/catalogue_object(atom/target, mob/living/user) + // Figure out who may have helped out. + var/list/contributers = list() + var/list/contributer_names = list() + for(var/mob/living/L as anything in player_list) + if(L == user) + continue + if(!istype(L)) + continue + if(get_dist(L, user) <= credit_sharing_range) + contributers += L + contributer_names += L.name + + var/points_gained = 0 + + // Discover each datum available. + var/list/object_data = target.get_catalogue_data() + if(LAZYLEN(object_data)) + for(var/data_type in object_data) + var/datum/category_item/catalogue/I = GLOB.catalogue_data.resolve_item(data_type) + if(istype(I)) + var/list/discoveries = I.discover(user, list(user.name) + contributer_names) // If one discovery leads to another, the list returned will have all of them. + if(LAZYLEN(discoveries)) + for(var/datum/category_item/catalogue/data as anything in discoveries) + points_gained += data.value + + // Give out points. + if(points_gained) + // First, to us. + to_chat(user, span("notice", "Gained [points_gained] points from this scan.")) + adjust_points(points_gained) + + // Now to our friends, if any. + if(contributers.len) + for(var/mob/M in contributers) + var/list/things = M.GetAllContents(3) // Depth of two should reach into bags but just in case lets make it three. + var/obj/item/device/cataloguer/other_cataloguer = locate() in things // If someone has two or more scanners this only adds points to one. + if(other_cataloguer) + to_chat(M, span("notice", "Gained [points_gained] points from \the [user]'s scan of \the [target].")) + other_cataloguer.adjust_points(points_gained) + to_chat(user, span("notice", "Shared discovery with [contributers.len] other contributer\s.")) + + + + +/obj/item/device/cataloguer/AltClick(mob/user) + pulse_scan(user) + +// Gives everything capable of being scanned an outline for a brief moment. +// Helps to avoid having to click a hundred things in a room for things that have an entry. +/obj/item/device/cataloguer/proc/pulse_scan(mob/user) + if(busy) + to_chat(user, span("warning", "\The [src] is busy doing something else.")) + return + + busy = TRUE + update_icon() + playsound(src, 'sound/machines/beep.ogg', 50) + + // First, get everything able to be scanned. + var/list/scannable_atoms = list() + for(var/atom/A as anything in view(world.view, user)) + if(A.can_catalogue()) // Not passing the user is intentional, so they don't get spammed. + scannable_atoms += A + + // Highlight things able to be scanned. + var/filter = filter(type = "outline", size = 1, color = "#00FF00") + for(var/atom/A as anything in scannable_atoms) + A.filters += filter + to_chat(user, span("notice", "\The [src] is highlighting scannable objects in green, if any exist.")) + + sleep(2 SECONDS) + + // Remove the highlights. + for(var/atom/A as anything in scannable_atoms) + if(QDELETED(A)) + continue + A.filters -= filter + + busy = FALSE + update_icon() + if(scannable_atoms.len) + playsound(src, 'sound/machines/ping.ogg', 50) + else + playsound(src, 'sound/machines/buzz-two.ogg', 50) + to_chat(user, span("notice", "\The [src] found [scannable_atoms.len] object\s that can be scanned.")) + + +// Negative points are bad. +/obj/item/device/cataloguer/proc/adjust_points(amount) + points_stored = max(0, points_stored += amount) + +/obj/item/device/cataloguer/attack_self(mob/living/user) + interact(user) + +/obj/item/device/cataloguer/interact(mob/user) + var/list/dat = list() + var/title = "Cataloguer Data Display" + + // Important buttons go on top since the scrollbar will default to the top of the window. + dat += "Contains [points_stored] Exploration Points." + dat += "\[Highlight Scannables\]\[Refresh\]\[Close\]" + + // If displayed_data exists, we show that, otherwise we show a list of all data in the mysterious global list. + if(displayed_data) + title = uppertext(displayed_data.name) + + dat += "\[Back to List\]" + if(debug && !displayed_data.visible) + dat += "\[(DEBUG) Force Discovery\]" + dat += "
                    " + + dat += "[displayed_data.desc]" + if(LAZYLEN(displayed_data.cataloguers)) + dat += "Cataloguers : [english_list(displayed_data.cataloguers)]." + else + dat += "Catalogued by nobody." + dat += "Worth [displayed_data.value] exploration points." + + else + dat += "
                    " + for(var/datum/category_group/group as anything in GLOB.catalogue_data.categories) + var/list/group_dat = list() + var/show_group = FALSE + + group_dat += "[group.name]" + for(var/datum/category_item/catalogue/item as anything in group.items) + if(item.visible || debug) + group_dat += "[item.name]" + show_group = TRUE + + if(show_group || debug) // Avoid showing 'empty' groups on regular cataloguers. + dat += group_dat + + var/datum/browser/popup = new(user, "cataloguer_display_\ref[src]", title, 500, 600, src) + popup.set_content(dat.Join("
                    ")) + popup.open() + add_fingerprint(user) + +/obj/item/device/cataloguer/Topic(href, href_list) + if(..()) + usr << browse(null, "window=cataloguer_display") + return 0 + if(href_list["close"] ) + usr << browse(null, "window=cataloguer_display") + return 0 + + if(href_list["show_data"]) + displayed_data = locate(href_list["show_data"]) + + if(href_list["pulse_scan"]) + pulse_scan(usr) + return // Don't refresh the window for this or it will open it back if its closed during the highlighting. + + if(href_list["debug_unlock"] && debug) + var/datum/category_item/catalogue/item = locate(href_list["debug_unlock"]) + item.discover(usr, list("Debugger")) + + interact(usr) // So it refreshes the window. + return 1 + +/obj/item/device/cataloguer/attackby(obj/item/weapon/W, mob/user) + if(istype(W, /obj/item/weapon/card/id) && !busy) + busy = TRUE + var/obj/item/weapon/card/id/ID = W + if(points_stored) + ID.survey_points += points_stored + points_stored = 0 + to_chat(user, "You swipe the id over \the [src].") + else + to_chat(user, "\The [src] has no points available.") + busy = FALSE + return ..() + +/obj/item/device/cataloguer/compact + name = "compact cataloguer" + desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ + Alt+click to highlight scannable objects around you." + icon = 'icons/obj/device_vr.dmi' + icon_state = "compact" + action_button_name = "Toggle Cataloguer" + var/deployed = TRUE + scan_range = 1 + toolspeed = 1.2 + +/obj/item/device/cataloguer/compact/pathfinder + name = "pathfinder's cataloguer" + desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ + Alt+click to highlight scannable objects around you." + icon = 'icons/obj/device_vr.dmi' + icon_state = "pathcat" + scan_range = 3 + toolspeed = 1 + +/obj/item/device/cataloguer/compact/update_icon() + if(busy) + icon_state = "[initial(icon_state)]_s" + else + icon_state = initial(icon_state) + +/obj/item/device/cataloguer/compact/ui_action_click() + toggle() + +/obj/item/device/cataloguer/compact/verb/toggle() + set name = "Toggle Cataloguer" + set category = "Object" + + if(busy) + to_chat(usr, span("warning", "\The [src] is currently scanning something.")) + return + deployed = !(deployed) + if(deployed) + w_class = ITEMSIZE_NORMAL + icon_state = "[initial(icon_state)]" + to_chat(usr, span("notice", "You flick open \the [src].")) + else + w_class = ITEMSIZE_SMALL + icon_state = "[initial(icon_state)]_closed" + to_chat(usr, span("notice", "You close \the [src].")) + + if (ismob(usr)) + var/mob/M = usr + M.update_action_buttons() + +/obj/item/device/cataloguer/compact/afterattack(atom/target, mob/user, proximity_flag) + if(!deployed) + to_chat(user, span("warning", "\The [src] is closed.")) + return + return ..() + +/obj/item/device/cataloguer/compact/pulse_scan(mob/user) + if(!deployed) + to_chat(user, span("warning", "\The [src] is closed.")) + return + return ..() diff --git a/code/modules/catalogue/cataloguer_visuals.dm b/code/modules/catalogue/cataloguer_visuals.dm index cf8a446533d..f159271a026 100644 --- a/code/modules/catalogue/cataloguer_visuals.dm +++ b/code/modules/catalogue/cataloguer_visuals.dm @@ -1,68 +1,68 @@ -#define ICON_SIZE 32 - -// Draws a box showing the limits of movement while scanning something. -// Only the client supplied will see the box. -/obj/item/device/cataloguer/proc/draw_box(atom/A, box_size, client/C) - . = list() - // Things moved with pixel_[x|y] will move the box, so this is to correct that. - var/pixel_x_correction = -A.pixel_x - var/pixel_y_correction = -A.pixel_y - - // First, place the bottom-left corner. - . += draw_line(A, SOUTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) - - // Make a line on the bottom, going right. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction - var/y_displacement = (-box_size * ICON_SIZE) + pixel_y_correction - . += draw_line(A, SOUTH, x_displacement, y_displacement, C) - - // Bottom-right corner. - . += draw_line(A, SOUTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) - - // Second line, for the right side going up. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (box_size * ICON_SIZE) + pixel_x_correction - var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction - . += draw_line(A, EAST, x_displacement, y_displacement, C) - - // Top-right corner. - . += draw_line(A, NORTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) - - // Third line, for the top, going right. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction - var/y_displacement = (box_size * ICON_SIZE) + pixel_y_correction - . += draw_line(A, NORTH, x_displacement, y_displacement, C) - - // Top-left corner. - . += draw_line(A, NORTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) - - // Fourth and last line, for the left side going up. - for(var/i = 1 to (box_size * 2) - 1) - var/x_displacement = (-box_size * ICON_SIZE) + pixel_x_correction - var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction - . += draw_line(A, WEST, x_displacement, y_displacement, C) - -#undef ICON_SIZE - -// Draws an individual segment of the box. -/obj/item/device/cataloguer/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C) - var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir) - line.pixel_x = line_pixel_x - line.pixel_y = line_pixel_y - line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots. - line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND - line.alpha = 125 - C.images += line - return line - -// Removes the box that was generated before from the client. -/obj/item/device/cataloguer/proc/delete_box(list/box_segments, client/C) - for(var/i in box_segments) - C.images -= i - qdel(i) - -/obj/item/device/cataloguer/proc/color_box(list/box_segments, new_color, new_time) - for(var/i in box_segments) +#define ICON_SIZE 32 + +// Draws a box showing the limits of movement while scanning something. +// Only the client supplied will see the box. +/obj/item/device/cataloguer/proc/draw_box(atom/A, box_size, client/C) + . = list() + // Things moved with pixel_[x|y] will move the box, so this is to correct that. + var/pixel_x_correction = -A.pixel_x + var/pixel_y_correction = -A.pixel_y + + // First, place the bottom-left corner. + . += draw_line(A, SOUTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) + + // Make a line on the bottom, going right. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction + var/y_displacement = (-box_size * ICON_SIZE) + pixel_y_correction + . += draw_line(A, SOUTH, x_displacement, y_displacement, C) + + // Bottom-right corner. + . += draw_line(A, SOUTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C) + + // Second line, for the right side going up. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (box_size * ICON_SIZE) + pixel_x_correction + var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction + . += draw_line(A, EAST, x_displacement, y_displacement, C) + + // Top-right corner. + . += draw_line(A, NORTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) + + // Third line, for the top, going right. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction + var/y_displacement = (box_size * ICON_SIZE) + pixel_y_correction + . += draw_line(A, NORTH, x_displacement, y_displacement, C) + + // Top-left corner. + . += draw_line(A, NORTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C) + + // Fourth and last line, for the left side going up. + for(var/i = 1 to (box_size * 2) - 1) + var/x_displacement = (-box_size * ICON_SIZE) + pixel_x_correction + var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction + . += draw_line(A, WEST, x_displacement, y_displacement, C) + +#undef ICON_SIZE + +// Draws an individual segment of the box. +/obj/item/device/cataloguer/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C) + var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir) + line.pixel_x = line_pixel_x + line.pixel_y = line_pixel_y + line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots. + line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND + line.alpha = 125 + C.images += line + return line + +// Removes the box that was generated before from the client. +/obj/item/device/cataloguer/proc/delete_box(list/box_segments, client/C) + for(var/i in box_segments) + C.images -= i + qdel(i) + +/obj/item/device/cataloguer/proc/color_box(list/box_segments, new_color, new_time) + for(var/i in box_segments) animate(i, color = new_color, time = new_time) \ No newline at end of file diff --git a/code/modules/catalogue/cataloguer_vr.dm b/code/modules/catalogue/cataloguer_vr.dm deleted file mode 100644 index 6759cca6510..00000000000 --- a/code/modules/catalogue/cataloguer_vr.dm +++ /dev/null @@ -1,70 +0,0 @@ -/obj/item/device/cataloguer - credit_sharing_range = 280 - -/obj/item/device/cataloguer/compact - name = "compact cataloguer" - desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ - Alt+click to highlight scannable objects around you." - icon = 'icons/obj/device_vr.dmi' - icon_state = "compact" - action_button_name = "Toggle Cataloguer" - var/deployed = TRUE - scan_range = 1 - toolspeed = 1.2 - -/obj/item/device/cataloguer/compact/update_icon() - if(busy) - icon_state = "[initial(icon_state)]_s" - else - icon_state = initial(icon_state) - -/obj/item/device/cataloguer/compact/ui_action_click() - toggle() - -/obj/item/device/cataloguer/compact/verb/toggle() - set name = "Toggle Cataloguer" - set category = "Object" - - if(busy) - to_chat(usr, span("warning", "\The [src] is currently scanning something.")) - return - deployed = !(deployed) - if(deployed) - w_class = ITEMSIZE_NORMAL - icon_state = "[initial(icon_state)]" - to_chat(usr, span("notice", "You flick open \the [src].")) - else - w_class = ITEMSIZE_SMALL - icon_state = "[initial(icon_state)]_closed" - to_chat(usr, span("notice", "You close \the [src].")) - - if (ismob(usr)) - var/mob/M = usr - M.update_action_buttons() - -/obj/item/device/cataloguer/compact/afterattack(atom/target, mob/user, proximity_flag) - if(!deployed) - to_chat(user, span("warning", "\The [src] is closed.")) - return - return ..() - -/obj/item/device/cataloguer/compact/pulse_scan(mob/user) - if(!deployed) - to_chat(user, span("warning", "\The [src] is closed.")) - return - return ..() - -/obj/item/device/cataloguer/compact/pathfinder - name = "pathfinder's cataloguer" - desc = "A compact hand-held device, used for compiling information about an object by scanning it. \ - Alt+click to highlight scannable objects around you." - icon = 'icons/obj/device_vr.dmi' - icon_state = "pathcat" - scan_range = 3 - toolspeed = 1 - -/obj/item/device/cataloguer - desc = "A hand-held device, used for compiling information about an object by scanning it. \ - Alt+click to highlight scannable objects around you." - icon = 'icons/obj/device_vr.dmi' - icon_state = "cataloguer" \ No newline at end of file diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index ea1d14dc6e6..d91ec3bea29 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -1,32 +1,65 @@ +/** + * Client datum + * + * A datum that is created whenever a user joins a BYOND world, one will exist for every active connected + * player + * + * when they first connect, this client object is created and [/client/New] is called + * + * When they disconnect, this client object is deleted and [/client/Del] is called + * + * All client topic calls go through [/client/Topic] first, so a lot of our specialised + * topic handling starts here + */ /client - ////////////////////// - //BLACK MAGIC THINGS// - ////////////////////// + /** + * This line makes clients parent type be a datum + * + * By default in byond if you define a proc on datums, that proc will exist on nearly every single type + * from icons to images to atoms to mobs to objs to turfs to areas, it won't however, appear on client + * + * instead by default they act like their own independent type so while you can do isdatum(icon) + * and have it return true, you can't do isdatum(client), it will always return false. + * + * This makes writing oo code hard, when you have to consider this extra special case + * + * This line prevents that, and has never appeared to cause any ill effects, while saving us an extra + * pain to think about + * + * This line is widely considered black fucking magic, and the fact it works is a puzzle to everyone + * involved, including the current engine developer, lummox + * + * If you are a future developer and the engine source is now available and you can explain why this + * is the way it is, please do update this comment + */ parent_type = /datum //////////////// //ADMIN THINGS// //////////////// + ///Contains admin info. Null if client is not an admin. var/datum/admins/holder = null var/datum/admins/deadmin_holder = null var/buildmode = 0 - var/last_message = "" //Contains the last message sent by this client - used to protect against copy-paste spamming. - var/last_message_count = 0 //contins a number of how many times a message identical to last_message was sent. + ///Contains the last message sent by this client - used to protect against copy-paste spamming. + var/last_message = "" + ///contins a number of how many times a message identical to last_message was sent. + var/last_message_count = 0 var/ircreplyamount = 0 var/entity_narrate_holder //Holds /datum/entity_narrate when using the relevant admin verbs. + var/fakeConversations //Holds fake PDA conversations for event set-up ///////// //OTHER// ///////// + ///Player preferences datum for the client var/datum/preferences/prefs = null var/moving = null var/adminobs = null var/area = null var/time_died_as_mouse = null //when the client last died as a mouse var/datum/tooltip/tooltips = null - var/datum/chatOutput/chatOutput var/datum/volume_panel/volume_panel = null // Initialized by /client/verb/volume_panel() - var/chatOutputLoadedAt var/seen_news = 0 var/adminhelped = 0 diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index c0fce7f974b..016a500149c 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -136,7 +136,6 @@ var/global/client_count = 0 // ARFS EDIT - TGS if("usr") hsrc = mob if("prefs") return prefs.process_link(usr,href_list) if("vars") return view_var_Topic(href,href_list,hsrc) - if("chat") return chatOutput.Topic(href, href_list) switch(href_list["action"]) if("openLink") @@ -147,7 +146,7 @@ var/global/client_count = 0 // ARFS EDIT - TGS //This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc. /client/AllowUpload(filename, filelength) if(filelength > UPLOAD_LIMIT) - to_chat(src, "Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB.") + to_chat(src, span_red("Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB.")) return 0 /* //Don't need this at the moment. But it's here if it's needed later. //Helps prevent multiple files being uploaded at once. Or right after eachother. @@ -171,23 +170,22 @@ var/global/client_count = 0 // ARFS EDIT - TGS return null if(!config.guests_allowed && IsGuestKey(key)) - alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest") // Not tgui_alert + alert(src,"This server doesn't allow guest accounts to play. Please go to https://www.byond.com/ and register for a key.","Guest") // Not tgui_alert del(src) return ++global.client_count // ARFS Edit - TGS - chatOutput = new /datum/chatOutput(src) //veechat - chatOutput.send_resources() - spawn() - chatOutput.start() //Only show this if they are put into a new_player mob. Otherwise, "what title screen?" if(isnewplayer(src.mob)) - to_chat(src, "If the title screen is black, resources are still downloading. Please be patient until the title screen appears.") + to_chat(src, span_red("If the title screen is black, resources are still downloading. Please be patient until the title screen appears.")) GLOB.clients += src GLOB.directory[ckey] = src + // Instantiate tgui panel + tgui_panel = new(src, "browseroutput") + GLOB.ahelp_tickets.ClientLogin(src) GLOB.mhelp_tickets.ClientLogin(src) @@ -217,6 +215,9 @@ var/global/client_count = 0 // ARFS EDIT - TGS if(prefs) prefs.selecting_slots = FALSE + // Initialize tgui panel + tgui_panel.initialize() + connection_time = world.time connection_realtime = world.realtime connection_timeofday = world.timeofday @@ -489,34 +490,11 @@ var/global/client_count = 0 // ARFS EDIT - TGS return FALSE return ..() -/client/verb/reload_vchat() - set name = "Reload VChat" - set category = "OOC" - - //Timing - if(src.chatOutputLoadedAt > (world.time - 10 SECONDS)) - tgui_alert_async(src, "You can only try to reload VChat every 10 seconds at most.") - return - - verbs -= /client/proc/vchat_export_log - - //Log, disable - log_debug("[key_name(src)] reloaded VChat.") - winset(src, null, "outputwindow.htmloutput.is-visible=false;outputwindow.oldoutput.is-visible=false;outputwindow.chatloadlabel.is-visible=true") - - //The hard way - qdel_null(src.chatOutput) - chatOutput = new /datum/chatOutput(src) //veechat - chatOutput.send_resources() - spawn() - chatOutput.start() - - //This is for getipintel.net. //You're welcome to replace this proc with your own that does your own cool stuff. //Just set the client's ip_reputation var and make sure it makes sense with your config settings (higher numbers are worse results) /client/proc/update_ip_reputation() - var/request = "http://check.getipintel.net/check.php?ip=[address]&contact=[config.ipr_email]" + var/request = "https://check.getipintel.net/check.php?ip=[address]&contact=[config.ipr_email]" var/http[] = world.Export(request) /* Debug diff --git a/code/modules/client/client procs_vr.dm b/code/modules/client/client procs_vr.dm index 830521f20dc..7cea3d57769 100644 --- a/code/modules/client/client procs_vr.dm +++ b/code/modules/client/client procs_vr.dm @@ -20,7 +20,7 @@ if(!config.ipr_email) return -1 - var/request = "http://check.getipintel.net/check.php?ip=[address]&contact=[config.ipr_email]" + var/request = "https://check.getipintel.net/check.php?ip=[address]&contact=[config.ipr_email]" var/http[] = world.Export(request) if(!http || !islist(http)) //If we couldn't check, the service might be down, fail-safe. @@ -74,7 +74,7 @@ if(!config.ipqualityscore_apikey) return -1 - var/request = "http://www.ipqualityscore.com/api/json/ip/[config.ipqualityscore_apikey]/[address]?strictness=1&fast=true&byond_key=[key]" + var/request = "https://www.ipqualityscore.com/api/json/ip/[config.ipqualityscore_apikey]/[address]?strictness=1&fast=true&byond_key=[key]" var/http[] = world.Export(request) if(!http || !islist(http)) //If we couldn't check, the service might be down, fail-safe. @@ -97,4 +97,4 @@ else score = response["fraud_score"] - return score/100 //To normalize with the 0.0 to 1.0 scores. \ No newline at end of file + return score/100 //To normalize with the 0.0 to 1.0 scores. diff --git a/code/modules/client/preference_setup/antagonism/01_basic.dm b/code/modules/client/preference_setup/antagonism/01_basic.dm index 56f05795a30..8dc760a00ff 100644 --- a/code/modules/client/preference_setup/antagonism/01_basic.dm +++ b/code/modules/client/preference_setup/antagonism/01_basic.dm @@ -1,72 +1,72 @@ -var/global/list/uplink_locations = list("PDA", "Headset", "None") - -/datum/category_item/player_setup_item/antagonism/basic - name = "Basic" - sort_order = 1 - -/datum/category_item/player_setup_item/antagonism/basic/load_character(var/savefile/S) - S["uplinklocation"] >> pref.uplinklocation - S["exploit_record"] >> pref.exploit_record - S["antag_faction"] >> pref.antag_faction - S["antag_vis"] >> pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/save_character(var/savefile/S) - S["uplinklocation"] << pref.uplinklocation - S["exploit_record"] << pref.exploit_record - S["antag_faction"] << pref.antag_faction - S["antag_vis"] << pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/sanitize_character() - pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation)) - if(!pref.antag_faction) pref.antag_faction = "None" - if(!pref.antag_vis) pref.antag_vis = "Hidden" - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/antagonism/basic/copy_to_mob(var/mob/living/carbon/human/character) - character.exploit_record = pref.exploit_record - character.antag_faction = pref.antag_faction - character.antag_vis = pref.antag_vis - -/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user) - . += "Faction: [pref.antag_faction]
                    " - . += "Visibility: [pref.antag_vis]
                    " - . +="Uplink Type : [pref.uplinklocation]" - . +="
                    " - . +="Exploitable information:
                    " - if(jobban_isbanned(user, "Records")) - . += "You are banned from using character records.
                    " - else - . +="[TextPreview(pref.exploit_record,40)]
                    " - -/datum/category_item/player_setup_item/antagonism/basic/OnTopic(var/href,var/list/href_list, var/mob/user) - if (href_list["antagtask"]) - pref.uplinklocation = next_in_list(pref.uplinklocation, uplink_locations) - return TOPIC_REFRESH - - if(href_list["exploitable_record"]) - var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0) - if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.exploit_record = exploitmsg - return TOPIC_REFRESH - - if(href_list["antagfaction"]) - var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.antag_faction = raw_choice - else - pref.antag_faction = choice - return TOPIC_REFRESH - - if(href_list["antagvis"]) - var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - else - pref.antag_vis = choice - return TOPIC_REFRESH - - return ..() +var/global/list/uplink_locations = list("PDA", "Headset", "None") + +/datum/category_item/player_setup_item/antagonism/basic + name = "Basic" + sort_order = 1 + +/datum/category_item/player_setup_item/antagonism/basic/load_character(var/savefile/S) + S["uplinklocation"] >> pref.uplinklocation + S["exploit_record"] >> pref.exploit_record + S["antag_faction"] >> pref.antag_faction + S["antag_vis"] >> pref.antag_vis + +/datum/category_item/player_setup_item/antagonism/basic/save_character(var/savefile/S) + S["uplinklocation"] << pref.uplinklocation + S["exploit_record"] << pref.exploit_record + S["antag_faction"] << pref.antag_faction + S["antag_vis"] << pref.antag_vis + +/datum/category_item/player_setup_item/antagonism/basic/sanitize_character() + pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation)) + if(!pref.antag_faction) pref.antag_faction = "None" + if(!pref.antag_vis) pref.antag_vis = "Hidden" + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/antagonism/basic/copy_to_mob(var/mob/living/carbon/human/character) + character.exploit_record = pref.exploit_record + character.antag_faction = pref.antag_faction + character.antag_vis = pref.antag_vis + +/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user) + . += "Faction: [pref.antag_faction]
                    " + . += "Visibility: [pref.antag_vis]
                    " + . +="Uplink Type : [pref.uplinklocation]" + . +="
                    " + . +="Exploitable information:
                    " + if(jobban_isbanned(user, "Records")) + . += "You are banned from using character records.
                    " + else + . +="[TextPreview(pref.exploit_record,40)]
                    " + +/datum/category_item/player_setup_item/antagonism/basic/OnTopic(var/href,var/list/href_list, var/mob/user) + if (href_list["antagtask"]) + pref.uplinklocation = next_in_list(pref.uplinklocation, uplink_locations) + return TOPIC_REFRESH + + if(href_list["exploitable_record"]) + var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0) + if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.exploit_record = exploitmsg + return TOPIC_REFRESH + + if(href_list["antagfaction"]) + var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.antag_faction = raw_choice + else + pref.antag_faction = choice + return TOPIC_REFRESH + + if(href_list["antagvis"]) + var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + else + pref.antag_vis = choice + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index f9423f088a4..dc4202f912c 100644 --- a/code/modules/client/preference_setup/general/01_basic.dm +++ b/code/modules/client/preference_setup/general/01_basic.dm @@ -220,12 +220,16 @@ if(new_metadata && CanUseTopic(user)) pref.metadata = new_metadata else if(href_list["edit_ooc_note_likes"]) - var/new_metadata = strip_html_simple(tgui_input_text(usr, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel!", "Game Preference" , html_decode(pref.metadata_likes), multiline = TRUE, prevent_enter = TRUE)) + var/new_metadata = strip_html_simple(tgui_input_text(usr, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.metadata_likes), multiline = TRUE, prevent_enter = TRUE)) if(new_metadata && CanUseTopic(user)) + if(new_metadata == "!clear") + new_metadata = "" pref.metadata_likes = new_metadata else if(href_list["edit_ooc_note_dislikes"]) - var/new_metadata = strip_html_simple(tgui_input_text(usr, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel!", "Game Preference" , html_decode(pref.metadata_dislikes), multiline = TRUE, prevent_enter = TRUE)) + var/new_metadata = strip_html_simple(tgui_input_text(usr, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.metadata_dislikes), multiline = TRUE, prevent_enter = TRUE)) if(new_metadata && CanUseTopic(user)) + if(new_metadata == "!clear") + new_metadata = "" pref.metadata_dislikes = new_metadata return ..() diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 63ac864a300..6d35219a1d2 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -1,1409 +1,1429 @@ -var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-") - -/datum/preferences - var/equip_preview_mob = EQUIP_PREVIEW_ALL - var/animations_toggle = FALSE - - var/icon/bgstate = "000" - var/list/bgstate_options = list("000", "midgrey", "FFF", "white", "steel", "techmaint", "dark", "plating", "reinforced") - - var/ear_style // Type of selected ear style - var/r_ears = 30 // Ear color. - var/g_ears = 30 // Ear color - var/b_ears = 30 // Ear color - var/r_ears2 = 30 // Ear extra color. - var/g_ears2 = 30 // Ear extra color - var/b_ears2 = 30 // Ear extra color - var/r_ears3 = 30 // Ear tertiary color. - var/g_ears3 = 30 // Ear tertiary color - var/b_ears3 = 30 // Ear tertiary color - var/tail_style // Type of selected tail style - var/r_tail = 30 // Tail/Taur color - var/g_tail = 30 // Tail/Taur color - var/b_tail = 30 // Tail/Taur color - var/r_tail2 = 30 // For extra overlay. - var/g_tail2 = 30 // For extra overlay. - var/b_tail2 = 30 // For extra overlay. - var/r_tail3 = 30 // For tertiary overlay. - var/g_tail3 = 30 // For tertiary overlay. - var/b_tail3 = 30 // For tertiary overlay. - var/wing_style // Type of selected wing style - var/r_wing = 30 // Wing color - var/g_wing = 30 // Wing color - var/b_wing = 30 // Wing color - var/r_wing2 = 30 // Wing extra color - var/g_wing2 = 30 // Wing extra color - var/b_wing2 = 30 // Wing extra color - var/r_wing3 = 30 // Wing tertiary color - var/g_wing3 = 30 // Wing tertiary color - var/b_wing3 = 30 // Wing tertiary color - var/datum/browser/markings_subwindow = null - -// Sanitize ear/wing/tail styles -/datum/preferences/proc/sanitize_body_styles() - - // Grandfather in anyone loading paths from a save. - if(ispath(ear_style, /datum/sprite_accessory)) - var/datum/sprite_accessory/instance = global.ear_styles_list[ear_style] - if(istype(instance)) - ear_style = instance.name - if(ispath(wing_style, /datum/sprite_accessory)) - var/datum/sprite_accessory/instance = global.wing_styles_list[wing_style] - if(istype(instance)) - wing_style = instance.name - if(ispath(tail_style, /datum/sprite_accessory)) - var/datum/sprite_accessory/instance = global.tail_styles_list[tail_style] - if(istype(instance)) - tail_style = instance.name - - // Sanitize for non-existent keys. - if(ear_style && !(ear_style in get_available_styles(global.ear_styles_list))) - ear_style = null - if(wing_style && !(wing_style in get_available_styles(global.wing_styles_list))) - wing_style = null - if(tail_style && !(tail_style in get_available_styles(global.tail_styles_list))) - tail_style = null - -/datum/preferences/proc/get_available_styles(var/style_list) - . = list("Normal" = null) - for(var/path in style_list) - var/datum/sprite_accessory/instance = style_list[path] - if(!istype(instance)) - continue - if(instance.ckeys_allowed && (!client || !(client.ckey in instance.ckeys_allowed))) - continue - if(instance.species_allowed && (!species || !(species in instance.species_allowed)) && (!client || !check_rights(R_ADMIN | R_EVENT | R_FUN, 0, client)) && (!custom_base || !(custom_base in instance.species_allowed))) //VOREStation Edit: Custom Species - continue - .[instance.name] = instance - -/datum/preferences/proc/mass_edit_marking_list(var/marking, var/change_on = TRUE, var/change_color = TRUE, var/marking_value = null, var/on = TRUE, var/color = "#000000") - var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[marking] - var/list/new_marking = marking_value||mark_datum.body_parts - for (var/NM in new_marking) - if (marking_value && !islist(new_marking[NM])) continue - new_marking[NM] = list("on" = (!change_on && marking_value) ? marking_value[NM]["on"] : on, "color" = (!change_color && marking_value) ? marking_value[NM]["color"] : color) - if (change_color) - new_marking["color"] = color - return new_marking - -/datum/category_item/player_setup_item/general/body - name = "Body" - sort_order = 3 - -/datum/category_item/player_setup_item/general/body/load_character(var/savefile/S) - S["species"] >> pref.species - S["hair_red"] >> pref.r_hair - S["hair_green"] >> pref.g_hair - S["hair_blue"] >> pref.b_hair - S["facial_red"] >> pref.r_facial - S["grad_red"] >> pref.r_grad - S["grad_green"] >> pref.g_grad - S["grad_blue"] >> pref.b_grad - S["facial_green"] >> pref.g_facial - S["facial_blue"] >> pref.b_facial - S["skin_tone"] >> pref.s_tone - S["skin_red"] >> pref.r_skin - S["skin_green"] >> pref.g_skin - S["skin_blue"] >> pref.b_skin - S["hair_style_name"] >> pref.h_style - S["facial_style_name"] >> pref.f_style - S["grad_style_name"] >> pref.grad_style - S["eyes_red"] >> pref.r_eyes - S["eyes_green"] >> pref.g_eyes - S["eyes_blue"] >> pref.b_eyes - S["b_type"] >> pref.b_type - S["disabilities"] >> pref.disabilities - S["organ_data"] >> pref.organ_data - S["rlimb_data"] >> pref.rlimb_data - S["body_markings"] >> pref.body_markings - S["synth_color"] >> pref.synth_color - S["synth_red"] >> pref.r_synth - S["synth_green"] >> pref.g_synth - S["synth_blue"] >> pref.b_synth - S["synth_markings"] >> pref.synth_markings - S["bgstate"] >> pref.bgstate - S["body_descriptors"] >> pref.body_descriptors - S["ear_style"] >> pref.ear_style - S["r_ears"] >> pref.r_ears - S["g_ears"] >> pref.g_ears - S["b_ears"] >> pref.b_ears - S["r_ears2"] >> pref.r_ears2 - S["g_ears2"] >> pref.g_ears2 - S["b_ears2"] >> pref.b_ears2 - S["r_ears3"] >> pref.r_ears3 - S["g_ears3"] >> pref.g_ears3 - S["b_ears3"] >> pref.b_ears3 - S["tail_style"] >> pref.tail_style - S["r_tail"] >> pref.r_tail - S["g_tail"] >> pref.g_tail - S["b_tail"] >> pref.b_tail - S["r_tail2"] >> pref.r_tail2 - S["g_tail2"] >> pref.g_tail2 - S["b_tail2"] >> pref.b_tail2 - S["r_tail3"] >> pref.r_tail3 - S["g_tail3"] >> pref.g_tail3 - S["b_tail3"] >> pref.b_tail3 - S["wing_style"] >> pref.wing_style - S["r_wing"] >> pref.r_wing - S["g_wing"] >> pref.g_wing - S["b_wing"] >> pref.b_wing - S["r_wing2"] >> pref.r_wing2 - S["g_wing2"] >> pref.g_wing2 - S["b_wing2"] >> pref.b_wing2 - S["r_wing3"] >> pref.r_wing3 - S["g_wing3"] >> pref.g_wing3 - S["b_wing3"] >> pref.b_wing3 - -/datum/category_item/player_setup_item/general/body/save_character(var/savefile/S) - S["species"] << pref.species - S["hair_red"] << pref.r_hair - S["hair_green"] << pref.g_hair - S["hair_blue"] << pref.b_hair - S["grad_red"] << pref.r_grad - S["grad_green"] << pref.g_grad - S["grad_blue"] << pref.b_grad - S["facial_red"] << pref.r_facial - S["facial_green"] << pref.g_facial - S["facial_blue"] << pref.b_facial - S["skin_tone"] << pref.s_tone - S["skin_red"] << pref.r_skin - S["skin_green"] << pref.g_skin - S["skin_blue"] << pref.b_skin - S["hair_style_name"] << pref.h_style - S["facial_style_name"] << pref.f_style - S["grad_style_name"] << pref.grad_style - S["eyes_red"] << pref.r_eyes - S["eyes_green"] << pref.g_eyes - S["eyes_blue"] << pref.b_eyes - S["b_type"] << pref.b_type - S["disabilities"] << pref.disabilities - S["organ_data"] << pref.organ_data - S["rlimb_data"] << pref.rlimb_data - S["body_markings"] << pref.body_markings - S["synth_color"] << pref.synth_color - S["synth_red"] << pref.r_synth - S["synth_green"] << pref.g_synth - S["synth_blue"] << pref.b_synth - S["synth_markings"] << pref.synth_markings - S["bgstate"] << pref.bgstate - S["body_descriptors"] << pref.body_descriptors - S["ear_style"] << pref.ear_style - S["r_ears"] << pref.r_ears - S["g_ears"] << pref.g_ears - S["b_ears"] << pref.b_ears - S["r_ears2"] << pref.r_ears2 - S["g_ears2"] << pref.g_ears2 - S["b_ears2"] << pref.b_ears2 - S["r_ears3"] << pref.r_ears3 - S["g_ears3"] << pref.g_ears3 - S["b_ears3"] << pref.b_ears3 - S["tail_style"] << pref.tail_style - S["r_tail"] << pref.r_tail - S["g_tail"] << pref.g_tail - S["b_tail"] << pref.b_tail - S["r_tail2"] << pref.r_tail2 - S["g_tail2"] << pref.g_tail2 - S["b_tail2"] << pref.b_tail2 - S["r_tail3"] << pref.r_tail3 - S["g_tail3"] << pref.g_tail3 - S["b_tail3"] << pref.b_tail3 - S["wing_style"] << pref.wing_style - S["r_wing"] << pref.r_wing - S["g_wing"] << pref.g_wing - S["b_wing"] << pref.b_wing - S["r_wing2"] << pref.r_wing2 - S["g_wing2"] << pref.g_wing2 - S["b_wing2"] << pref.b_wing2 - S["r_wing3"] << pref.r_wing3 - S["g_wing3"] << pref.g_wing3 - S["b_wing3"] << pref.b_wing3 - -/datum/category_item/player_setup_item/general/body/sanitize_character(var/savefile/S) - if(!pref.species || !(pref.species in GLOB.playable_species)) - pref.species = SPECIES_HUMAN - pref.r_hair = sanitize_integer(pref.r_hair, 0, 255, initial(pref.r_hair)) - pref.g_hair = sanitize_integer(pref.g_hair, 0, 255, initial(pref.g_hair)) - pref.b_hair = sanitize_integer(pref.b_hair, 0, 255, initial(pref.b_hair)) - pref.r_grad = sanitize_integer(pref.r_grad, 0, 255, initial(pref.r_grad)) - pref.g_grad = sanitize_integer(pref.g_grad, 0, 255, initial(pref.g_grad)) - pref.b_grad = sanitize_integer(pref.b_grad, 0, 255, initial(pref.b_grad)) - pref.r_facial = sanitize_integer(pref.r_facial, 0, 255, initial(pref.r_facial)) - pref.g_facial = sanitize_integer(pref.g_facial, 0, 255, initial(pref.g_facial)) - pref.b_facial = sanitize_integer(pref.b_facial, 0, 255, initial(pref.b_facial)) - pref.s_tone = sanitize_integer(pref.s_tone, -185, 34, initial(pref.s_tone)) - pref.r_skin = sanitize_integer(pref.r_skin, 0, 255, initial(pref.r_skin)) - pref.g_skin = sanitize_integer(pref.g_skin, 0, 255, initial(pref.g_skin)) - pref.b_skin = sanitize_integer(pref.b_skin, 0, 255, initial(pref.b_skin)) - pref.h_style = sanitize_inlist(pref.h_style, hair_styles_list, initial(pref.h_style)) - pref.f_style = sanitize_inlist(pref.f_style, facial_hair_styles_list, initial(pref.f_style)) - pref.grad_style = sanitize_inlist(pref.grad_style, GLOB.hair_gradients, initial(pref.grad_style)) - pref.r_eyes = sanitize_integer(pref.r_eyes, 0, 255, initial(pref.r_eyes)) - pref.g_eyes = sanitize_integer(pref.g_eyes, 0, 255, initial(pref.g_eyes)) - pref.b_eyes = sanitize_integer(pref.b_eyes, 0, 255, initial(pref.b_eyes)) - pref.b_type = sanitize_text(pref.b_type, initial(pref.b_type)) - - pref.disabilities = sanitize_integer(pref.disabilities, 0, 65535, initial(pref.disabilities)) - if(!pref.organ_data) pref.organ_data = list() - if(!pref.rlimb_data) pref.rlimb_data = list() - if(!pref.body_markings) pref.body_markings = list() - else pref.body_markings &= body_marking_styles_list - for (var/M in pref.body_markings) //VOREStation Edit - if (!islist(pref.body_markings[M])) - var/col = istext(pref.body_markings[M]) ? pref.body_markings[M] : "#000000" - pref.body_markings[M] = pref.mass_edit_marking_list(M,color=col) - if(!pref.bgstate || !(pref.bgstate in pref.bgstate_options)) - pref.bgstate = "000" - - pref.r_ears = sanitize_integer(pref.r_ears, 0, 255, initial(pref.r_ears)) - pref.g_ears = sanitize_integer(pref.g_ears, 0, 255, initial(pref.g_ears)) - pref.b_ears = sanitize_integer(pref.b_ears, 0, 255, initial(pref.b_ears)) - pref.r_ears2 = sanitize_integer(pref.r_ears2, 0, 255, initial(pref.r_ears2)) - pref.g_ears2 = sanitize_integer(pref.g_ears2, 0, 255, initial(pref.g_ears2)) - pref.b_ears2 = sanitize_integer(pref.b_ears2, 0, 255, initial(pref.b_ears2)) - pref.r_ears3 = sanitize_integer(pref.r_ears3, 0, 255, initial(pref.r_ears3)) - pref.g_ears3 = sanitize_integer(pref.g_ears3, 0, 255, initial(pref.g_ears3)) - pref.b_ears3 = sanitize_integer(pref.b_ears3, 0, 255, initial(pref.b_ears3)) - pref.r_tail = sanitize_integer(pref.r_tail, 0, 255, initial(pref.r_tail)) - pref.g_tail = sanitize_integer(pref.g_tail, 0, 255, initial(pref.g_tail)) - pref.b_tail = sanitize_integer(pref.b_tail, 0, 255, initial(pref.b_tail)) - pref.r_tail2 = sanitize_integer(pref.r_tail2, 0, 255, initial(pref.r_tail2)) - pref.g_tail2 = sanitize_integer(pref.g_tail2, 0, 255, initial(pref.g_tail2)) - pref.b_tail2 = sanitize_integer(pref.b_tail2, 0, 255, initial(pref.b_tail2)) - pref.r_tail3 = sanitize_integer(pref.r_tail3, 0, 255, initial(pref.r_tail3)) - pref.g_tail3 = sanitize_integer(pref.g_tail3, 0, 255, initial(pref.g_tail3)) - pref.b_tail3 = sanitize_integer(pref.b_tail3, 0, 255, initial(pref.b_tail3)) - pref.r_wing = sanitize_integer(pref.r_wing, 0, 255, initial(pref.r_wing)) - pref.g_wing = sanitize_integer(pref.g_wing, 0, 255, initial(pref.g_wing)) - pref.b_wing = sanitize_integer(pref.b_wing, 0, 255, initial(pref.b_wing)) - pref.r_wing2 = sanitize_integer(pref.r_wing2, 0, 255, initial(pref.r_wing2)) - pref.g_wing2 = sanitize_integer(pref.g_wing2, 0, 255, initial(pref.g_wing2)) - pref.b_wing2 = sanitize_integer(pref.b_wing2, 0, 255, initial(pref.b_wing2)) - pref.r_wing3 = sanitize_integer(pref.r_wing3, 0, 255, initial(pref.r_wing3)) - pref.g_wing3 = sanitize_integer(pref.g_wing3, 0, 255, initial(pref.g_wing3)) - pref.b_wing3 = sanitize_integer(pref.b_wing3, 0, 255, initial(pref.b_wing3)) - - pref.sanitize_body_styles() - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/body/copy_to_mob(var/mob/living/carbon/human/character) - // Copy basic values - character.r_eyes = pref.r_eyes - character.g_eyes = pref.g_eyes - character.b_eyes = pref.b_eyes - character.h_style = pref.h_style - character.r_hair = pref.r_hair - character.g_hair = pref.g_hair - character.b_hair = pref.b_hair - character.r_grad = pref.r_grad - character.g_grad = pref.g_grad - character.b_grad = pref.b_grad - character.f_style = pref.f_style - character.r_facial = pref.r_facial - character.g_facial = pref.g_facial - character.b_facial = pref.b_facial - character.r_skin = pref.r_skin - character.g_skin = pref.g_skin - character.b_skin = pref.b_skin - character.s_tone = pref.s_tone - character.h_style = pref.h_style - character.f_style = pref.f_style - character.grad_style= pref.grad_style - character.b_type = pref.b_type - character.synth_color = pref.synth_color - character.r_synth = pref.r_synth - character.g_synth = pref.g_synth - character.b_synth = pref.b_synth - character.synth_markings = pref.synth_markings - - var/list/ear_styles = pref.get_available_styles(global.ear_styles_list) - character.ear_style = ear_styles[pref.ear_style] - character.r_ears = pref.r_ears - character.b_ears = pref.b_ears - character.g_ears = pref.g_ears - character.r_ears2 = pref.r_ears2 - character.b_ears2 = pref.b_ears2 - character.g_ears2 = pref.g_ears2 - character.r_ears3 = pref.r_ears3 - character.b_ears3 = pref.b_ears3 - character.g_ears3 = pref.g_ears3 - - var/list/tail_styles = pref.get_available_styles(global.tail_styles_list) - character.tail_style = tail_styles[pref.tail_style] - character.r_tail = pref.r_tail - character.b_tail = pref.b_tail - character.g_tail = pref.g_tail - character.r_tail2 = pref.r_tail2 - character.b_tail2 = pref.b_tail2 - character.g_tail2 = pref.g_tail2 - character.r_tail3 = pref.r_tail3 - character.b_tail3 = pref.b_tail3 - character.g_tail3 = pref.g_tail3 - - var/list/wing_styles = pref.get_available_styles(global.wing_styles_list) - character.wing_style = wing_styles[pref.wing_style] - character.r_wing = pref.r_wing - character.b_wing = pref.b_wing - character.g_wing = pref.g_wing - character.r_wing2 = pref.r_wing2 - character.b_wing2 = pref.b_wing2 - character.g_wing2 = pref.g_wing2 - character.r_wing3 = pref.r_wing3 - character.b_wing3 = pref.b_wing3 - character.g_wing3 = pref.g_wing3 - - character.set_gender(pref.biological_gender) - - // Destroy/cyborgize organs and limbs. - //VOREStation Edit - character.synthetic = pref.species == "Protean" ? all_robolimbs["protean"] : null //Clear the existing var. (unless protean, then switch it to the normal protean limb) - var/list/organs_to_edit = list() - for (var/name in list(BP_TORSO, BP_HEAD, BP_GROIN, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/O = character.organs_by_name[name] - if (O) - var/x = organs_to_edit.Find(O.parent_organ) - if (x == 0) - organs_to_edit += name - else - organs_to_edit.Insert(x+(O.robotic == ORGAN_NANOFORM ? 1 : 0), name) - for(var/name in organs_to_edit) //VOREStation edit end - var/status = pref.organ_data[name] - var/obj/item/organ/external/O = character.organs_by_name[name] - if(O) - if(status == "amputated") - O.remove_rejuv() - else if(status == "cyborg") - if(pref.rlimb_data[name]) - O.robotize(pref.rlimb_data[name]) - else - O.robotize() - - for(var/name in list(O_HEART,O_EYES,O_VOICE,O_LUNGS,O_LIVER,O_KIDNEYS,O_SPLEEN,O_STOMACH,O_INTESTINE,O_BRAIN)) - var/status = pref.organ_data[name] - if(!status) - continue - var/obj/item/organ/I = character.internal_organs_by_name[name] - if(istype(I, /obj/item/organ/internal/brain)) - var/obj/item/organ/external/E = character.get_organ(I.parent_organ) - if(E.robotic < ORGAN_ASSISTED) - continue - if(I) - if(status == "assisted") - I.mechassist() - else if(status == "mechanical") - I.robotize() - else if(status == "digital") - I.digitize() - - for(var/N in character.organs_by_name) - var/obj/item/organ/external/O = character.organs_by_name[N] - O.markings.Cut() - - var/priority = 0 - for(var/M in pref.body_markings) - priority += 1 - var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[M] - //var/mark_color = "[pref.body_markings[M]]" //VOREStation Edit - - for(var/BP in mark_datum.body_parts) - var/obj/item/organ/external/O = character.organs_by_name[BP] - if(O) - O.markings[M] = list("color" = pref.body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = pref.body_markings[M][BP]["on"]) - character.markings_len = priority - - var/list/last_descriptors = list() - if(islist(pref.body_descriptors)) - last_descriptors = pref.body_descriptors.Copy() - pref.body_descriptors = list() - - var/datum/species/mob_species = GLOB.all_species[pref.species] - if(LAZYLEN(mob_species.descriptors)) - for(var/entry in mob_species.descriptors) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] - if(istype(descriptor)) - if(isnull(last_descriptors[entry])) - pref.body_descriptors[entry] = descriptor.default_value // Species datums have initial default value. - else - pref.body_descriptors[entry] = CLAMP(last_descriptors[entry], 1, LAZYLEN(descriptor.standalone_value_descriptors)) - -/datum/category_item/player_setup_item/general/body/content(var/mob/user) - . = list() - - var/datum/species/mob_species = GLOB.all_species[pref.species] - . += "
                    Body " - . += "(®)" - . += "
                    " - . += "Species: [pref.species]
                    " - . += "Blood Type: [pref.b_type]
                    " - if(has_flag(mob_species, HAS_SKIN_TONE)) - . += "Skin Tone: [-pref.s_tone + 35]/220
                    " - . += "Needs Glasses: [pref.disabilities & NEARSIGHTED ? "Yes" : "No"]
                    " - . += "Limbs: Adjust Reset
                    " - . += "Internal Organs: Adjust
                    " - - //display limbs below - var/ind = 0 - for(var/name in pref.organ_data) - var/status = pref.organ_data[name] - var/organ_name = null - - switch(name) - if(BP_TORSO) - organ_name = "torso" - if(BP_GROIN) - organ_name = "groin" - if(BP_HEAD) - organ_name = "head" - if(BP_L_ARM) - organ_name = "left arm" - if(BP_R_ARM) - organ_name = "right arm" - if(BP_L_LEG) - organ_name = "left leg" - if(BP_R_LEG) - organ_name = "right leg" - if(BP_L_FOOT) - organ_name = "left foot" - if(BP_R_FOOT) - organ_name = "right foot" - if(BP_L_HAND) - organ_name = "left hand" - if(BP_R_HAND) - organ_name = "right hand" - if(O_HEART) - organ_name = "heart" - if(O_EYES) - organ_name = "eyes" - if(O_VOICE) - organ_name = "larynx" - if(O_BRAIN) - organ_name = "brain" - if(O_LUNGS) - organ_name = "lungs" - if(O_LIVER) - organ_name = "liver" - if(O_KIDNEYS) - organ_name = "kidneys" - if(O_SPLEEN) - organ_name = "spleen" - if(O_STOMACH) - organ_name = "stomach" - if(O_INTESTINE) - organ_name = "intestines" - - if(status == "cyborg") - ++ind - if(ind > 1) - . += ", " - var/datum/robolimb/R - if(pref.rlimb_data[name] && all_robolimbs[pref.rlimb_data[name]]) - R = all_robolimbs[pref.rlimb_data[name]] - else - R = basic_robolimb - . += "\t[R.company] [organ_name] prosthesis" - else if(status == "amputated") - ++ind - if(ind > 1) - . += ", " - . += "\tAmputated [organ_name]" - else if(status == "mechanical") - ++ind - if(ind > 1) - . += ", " - switch(organ_name) - if ("brain") - . += "\tPositronic [organ_name]" - else - . += "\tSynthetic [organ_name]" - else if(status == "digital") - ++ind - if(ind > 1) - . += ", " - . += "\tDigital [organ_name]" - else if(status == "assisted") - ++ind - if(ind > 1) - . += ", " - switch(organ_name) - if("heart") - . += "\tPacemaker-assisted [organ_name]" - if("lungs") - . += "\tAssisted [organ_name]" - if("voicebox") //on adding voiceboxes for speaking skrell/similar replacements - . += "\tSurgically altered [organ_name]" - if("eyes") - . += "\tRetinal overlayed [organ_name]" - if("brain") - . += "\tAssisted-interface [organ_name]" - else - . += "\tMechanically assisted [organ_name]" - if(!ind) - . += "\[...\]

                    " - else - . += "

                    " - - if(LAZYLEN(pref.body_descriptors)) - . += "" - for(var/entry in pref.body_descriptors) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] - . += "" - . += "
                    [capitalize(descriptor.chargen_label)]:[descriptor.get_standalone_value_descriptor(pref.body_descriptors[entry])]Change

                    " - - . += "
                    Preview
                    " - . += "
                    Cycle background" - . += "
                    [pref.equip_preview_mob & EQUIP_PREVIEW_LOADOUT ? "Hide loadout" : "Show loadout"]" - . += "
                    [pref.equip_preview_mob & EQUIP_PREVIEW_JOB ? "Hide job gear" : "Show job gear"]" - . += "
                    [pref.animations_toggle ? "Stop animations" : "Show animations"]" - . += "
                    " - - . += "Hair
                    " - if(has_flag(mob_species, HAS_HAIR_COLOR)) - . += "Change Color [color_square(pref.r_hair, pref.g_hair, pref.b_hair)] " - . += " Style: < > [pref.h_style]
                    " //The < & > in this line is correct-- those extra characters are the arrows you click to switch between styles. - - . += "Gradient
                    " - . += "Change Color [color_square(pref.r_grad, pref.g_grad, pref.b_grad)] " - . += " Style: < > [pref.grad_style]
                    " - - . += "
                    Facial
                    " - if(has_flag(mob_species, HAS_HAIR_COLOR)) - . += "Change Color [color_square(pref.r_facial, pref.g_facial, pref.b_facial)] " - . += " Style: < > [pref.f_style]
                    " //Same as above with the extra > & < characters - - if(has_flag(mob_species, HAS_EYE_COLOR)) - . += "
                    Eyes
                    " - . += "Change Color [color_square(pref.r_eyes, pref.g_eyes, pref.b_eyes)]
                    " - - if(has_flag(mob_species, HAS_SKIN_COLOR)) - . += "
                    Body Color
                    " - . += "Change Color [color_square(pref.r_skin, pref.g_skin, pref.b_skin)]
                    " - - . += "

                    Genetics Settings

                    " - - var/list/ear_styles = pref.get_available_styles(global.ear_styles_list) - var/datum/sprite_accessory/ears/ear = ear_styles[pref.ear_style] - . += "Ears
                    " - if(istype(ear)) - . += " Style: [ear.name]
                    " - if(ear.do_colouration) - . += "Change Color [color_square(pref.r_ears, pref.g_ears, pref.b_ears)]
                    " - if(ear.extra_overlay) - . += "Change Secondary Color [color_square(pref.r_ears2, pref.g_ears2, pref.b_ears2)]
                    " - if(ear.extra_overlay2) - . += "Change Tertiary Color [color_square(pref.r_ears3, pref.g_ears3, pref.b_ears3)]
                    " - else - . += " Style: Select
                    " - - var/list/tail_styles = pref.get_available_styles(global.tail_styles_list) - var/datum/sprite_accessory/tail/tail = tail_styles[pref.tail_style] - . += "Tail
                    " - if(istype(tail)) - . += " Style: [tail.name]
                    " - if(tail.do_colouration) - . += "Change Color [color_square(pref.r_tail, pref.g_tail, pref.b_tail)]
                    " - if(tail.extra_overlay) - . += "Change Secondary Color [color_square(pref.r_tail2, pref.g_tail2, pref.b_tail2)]
                    " - if(tail.extra_overlay2) - . += "Change Tertiary Color [color_square(pref.r_tail3, pref.g_tail3, pref.b_tail3)]
                    " - else - . += " Style: Select
                    " - - var/list/wing_styles = pref.get_available_styles(global.wing_styles_list) - var/datum/sprite_accessory/wing/wings = wing_styles[pref.wing_style] - . += "Wing
                    " - if(istype(wings)) - . += " Style: [wings.name]
                    " - if(wings.do_colouration) - . += "Change Color [color_square(pref.r_wing, pref.g_wing, pref.b_wing)]
                    " - if(wings.extra_overlay) - . += "Change Secondary Color [color_square(pref.r_wing2, pref.g_wing2, pref.b_wing2)]
                    " - if(wings.extra_overlay2) - . += "Change Secondary Color [color_square(pref.r_wing3, pref.g_wing3, pref.b_wing3)]
                    " - else - . += " Style: Select
                    " - - . += "
                    Body Markings +
                    " - . += "" - for(var/M in pref.body_markings) - . += "" - - . += "
                    [M][pref.body_markings.len > 1 ? "˄ ˅ mv " : ""]- Color[color_square(hex = pref.body_markings[M]["color"] ? pref.body_markings[M]["color"] : "#000000")] - Customize
                    " - . += "
                    " - . += "Allow Synth markings: [pref.synth_markings ? "Yes" : "No"]
                    " - . += "Allow Synth color: [pref.synth_color ? "Yes" : "No"]
                    " - if(pref.synth_color) - . += "Change Color [color_square(pref.r_synth, pref.g_synth, pref.b_synth)]" - - . = jointext(.,null) - -/datum/category_item/player_setup_item/general/body/proc/has_flag(var/datum/species/mob_species, var/flag) - return mob_species && (mob_species.appearance_flags & flag) - -/datum/category_item/player_setup_item/general/body/OnTopic(var/href,var/list/href_list, var/mob/user) - var/datum/species/mob_species = GLOB.all_species[pref.species] - - if(href_list["random"]) - pref.randomize_appearance_and_body_for() - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["change_descriptor"]) - if(mob_species.descriptors) - var/desc_id = href_list["change_descriptor"] - if(pref.body_descriptors[desc_id]) - var/datum/mob_descriptor/descriptor = mob_species.descriptors[desc_id] - var/choice = tgui_input_list(usr, "Please select a descriptor.", "Descriptor", descriptor.chargen_value_descriptors) - if(choice && mob_species.descriptors[desc_id]) // Check in case they sneakily changed species. - pref.body_descriptors[desc_id] = descriptor.chargen_value_descriptors[choice] - return TOPIC_REFRESH - - else if(href_list["blood_type"]) - var/new_b_type = tgui_input_list(user, "Choose your character's blood-type:", "Character Preference", valid_bloodtypes) - if(new_b_type && CanUseTopic(user)) - pref.b_type = new_b_type - return TOPIC_REFRESH - - else if(href_list["show_species"]) - // Actual whitelist checks are handled elsewhere, this is just for accessing the preview window. - var/choice = tgui_input_list(usr, "Which species would you like to look at?", "Species Choice", GLOB.playable_species) - if(!choice) return - pref.species_preview = choice - SetSpecies(preference_mob()) - pref.alternate_languages.Cut() // Reset their alternate languages. Todo: attempt to just fix it instead? - return TOPIC_HANDLED - - else if(href_list["set_species"]) - user << browse(null, "window=species") - if(!pref.species_preview || !(pref.species_preview in GLOB.all_species)) - return TOPIC_NOACTION - - var/datum/species/setting_species - - if(GLOB.all_species[href_list["set_species"]]) - setting_species = GLOB.all_species[href_list["set_species"]] - else - return TOPIC_NOACTION - - if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) //VOREStation Edit: selectability - return TOPIC_NOACTION - - var/prev_species = pref.species - pref.species = href_list["set_species"] - if(prev_species != pref.species) - if(!(pref.biological_gender in mob_species.genders)) - pref.set_biological_gender(mob_species.genders[1]) - pref.custom_species = null //VOREStation Edit - This is cleared on species changes - //grab one of the valid hair styles for the newly chosen species - var/list/valid_hairstyles = pref.get_valid_hairstyles() - - if(valid_hairstyles.len) - pref.h_style = pick(valid_hairstyles) - else - //this shouldn't happen - pref.h_style = hair_styles_list["Bald"] - - //grab one of the valid facial hair styles for the newly chosen species - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - - if(valid_facialhairstyles.len) - pref.f_style = pick(valid_facialhairstyles) - else - //this shouldn't happen - pref.f_style = facial_hair_styles_list["Shaved"] - - //reset hair colour and skin colour - pref.r_hair = 0//hex2num(copytext(new_hair, 2, 4)) - pref.g_hair = 0//hex2num(copytext(new_hair, 4, 6)) - pref.b_hair = 0//hex2num(copytext(new_hair, 6, 8)) - pref.s_tone = -75 - - reset_limbs() // Safety for species with incompatible manufacturers; easier than trying to do it case by case. - pref.body_markings.Cut() // Basically same as above. - - pref.sanitize_body_styles() - - var/min_age = get_min_age() - var/max_age = get_max_age() - pref.age = max(min(pref.age, max_age), min_age) - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_color"]) - if(!has_flag(mob_species, HAS_HAIR_COLOR)) - return TOPIC_NOACTION - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference", rgb(pref.r_hair, pref.g_hair, pref.b_hair)) as color|null - if(new_hair && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) - pref.r_hair = hex2num(copytext(new_hair, 2, 4)) - pref.g_hair = hex2num(copytext(new_hair, 4, 6)) - pref.b_hair = hex2num(copytext(new_hair, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["grad_color"]) - if(!has_flag(mob_species, HAS_HAIR_COLOR)) - return TOPIC_NOACTION - var/new_grad = input(user, "Choose your character's secondary hair color:", "Character Preference", rgb(pref.r_grad, pref.g_grad, pref.b_grad)) as color|null - if(new_grad && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) - pref.r_grad = hex2num(copytext(new_grad, 2, 4)) - pref.g_grad = hex2num(copytext(new_grad, 4, 6)) - pref.b_grad = hex2num(copytext(new_grad, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_style"]) - var/list/valid_hairstyles = pref.get_valid_hairstyles() - - var/new_h_style = tgui_input_list(user, "Choose your character's hair style:", "Character Preference", valid_hairstyles, pref.h_style) - if(new_h_style && CanUseTopic(user)) - pref.h_style = new_h_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["grad_style"]) - var/list/valid_gradients = GLOB.hair_gradients - - var/new_grad_style = tgui_input_list(user, "Choose a color pattern for your hair:", "Character Preference", valid_gradients, pref.grad_style) - if(new_grad_style && CanUseTopic(user)) - pref.grad_style = new_grad_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_style_left"]) - var/H = href_list["hair_style_left"] - var/list/valid_hairstyles = pref.get_valid_hairstyles() - var/start = valid_hairstyles.Find(H) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.h_style = valid_hairstyles[start-1] - else //But if we ARE, become the final element. - pref.h_style = valid_hairstyles[valid_hairstyles.len] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["hair_style_right"]) - var/H = href_list["hair_style_right"] - var/list/valid_hairstyles = pref.get_valid_hairstyles() - var/start = valid_hairstyles.Find(H) - - if(start != valid_hairstyles.len) //If we're not the end of the list, become the next element. - pref.h_style = valid_hairstyles[start+1] - else //But if we ARE, become the first element. - pref.h_style = valid_hairstyles[1] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_color"]) - if(!has_flag(mob_species, HAS_HAIR_COLOR)) - return TOPIC_NOACTION - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", rgb(pref.r_facial, pref.g_facial, pref.b_facial)) as color|null - if(new_facial && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) - pref.r_facial = hex2num(copytext(new_facial, 2, 4)) - pref.g_facial = hex2num(copytext(new_facial, 4, 6)) - pref.b_facial = hex2num(copytext(new_facial, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["eye_color"]) - if(!has_flag(mob_species, HAS_EYE_COLOR)) - return TOPIC_NOACTION - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", rgb(pref.r_eyes, pref.g_eyes, pref.b_eyes)) as color|null - if(new_eyes && has_flag(mob_species, HAS_EYE_COLOR) && CanUseTopic(user)) - pref.r_eyes = hex2num(copytext(new_eyes, 2, 4)) - pref.g_eyes = hex2num(copytext(new_eyes, 4, 6)) - pref.b_eyes = hex2num(copytext(new_eyes, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["skin_tone"]) - if(!has_flag(mob_species, HAS_SKIN_TONE)) - return TOPIC_NOACTION - var/new_s_tone = tgui_input_number(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference", (-pref.s_tone) + 35, 220, 1) - if(new_s_tone && has_flag(mob_species, HAS_SKIN_TONE) && CanUseTopic(user)) - pref.s_tone = 35 - max(min( round(new_s_tone), 220),1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["skin_color"]) - if(!has_flag(mob_species, HAS_SKIN_COLOR)) - return TOPIC_NOACTION - var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", rgb(pref.r_skin, pref.g_skin, pref.b_skin)) as color|null - if(new_skin && has_flag(mob_species, HAS_SKIN_COLOR) && CanUseTopic(user)) - pref.r_skin = hex2num(copytext(new_skin, 2, 4)) - pref.g_skin = hex2num(copytext(new_skin, 4, 6)) - pref.b_skin = hex2num(copytext(new_skin, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_style"]) - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - - var/new_f_style = tgui_input_list(user, "Choose your character's facial-hair style:", "Character Preference", valid_facialhairstyles, pref.f_style) - if(new_f_style && CanUseTopic(user)) - pref.f_style = new_f_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_style_left"]) - var/F = href_list["facial_style_left"] - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - var/start = valid_facialhairstyles.Find(F) - - if(start != 1) //If we're not the beginning of the list, become the previous element. - pref.f_style = valid_facialhairstyles[start-1] - else //But if we ARE, become the final element. - pref.f_style = valid_facialhairstyles[valid_facialhairstyles.len] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["facial_style_right"]) - var/F = href_list["facial_style_right"] - var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() - var/start = valid_facialhairstyles.Find(F) - - if(start != valid_facialhairstyles.len) //If we're not the end of the list, become the next element. - pref.f_style = valid_facialhairstyles[start+1] - else //But if we ARE, become the first element. - pref.f_style = valid_facialhairstyles[1] - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_style"]) - var/list/usable_markings = pref.body_markings.Copy() ^ body_marking_styles_list.Copy() - /* VOREStation Removal - No markings whitelist, let people mix/match - for(var/M in usable_markings) - var/datum/sprite_accessory/S = usable_markings[M] - var/datum/species/spec = GLOB.all_species[pref.species] - if(!S.species_allowed.len) - continue - else if(!(pref.species in S.species_allowed) && !(pref.custom_base in S.species_allowed) && !(spec.base_species in S.species_allowed)) - usable_markings -= M - */ //VOREStation Removal End - var/new_marking = tgui_input_list(user, "Choose a body marking:", "Character Preference", usable_markings) - if(new_marking && CanUseTopic(user)) - pref.body_markings[new_marking] = pref.mass_edit_marking_list(new_marking) //New markings start black - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_up"]) - var/M = href_list["marking_up"] - var/start = pref.body_markings.Find(M) - if(start != 1) //If we're not the beginning of the list, swap with the previous element. - moveElement(pref.body_markings, start, start-1) - else //But if we ARE, become the final element -ahead- of everything else. - moveElement(pref.body_markings, start, pref.body_markings.len+1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_down"]) - var/M = href_list["marking_down"] - var/start = pref.body_markings.Find(M) - if(start != pref.body_markings.len) //If we're not the end of the list, swap with the next element. - moveElement(pref.body_markings, start, start+2) - else //But if we ARE, become the first element -behind- everything else. - moveElement(pref.body_markings, start, 1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_move"]) - var/M = href_list["marking_move"] - var/start = pref.body_markings.Find(M) - var/list/move_locs = pref.body_markings - M - if(start != 1) - move_locs -= pref.body_markings[start-1] - - var/inject_after = tgui_input_list(user, "Move [M] ahead of...", "Character Preference", move_locs) //Move ahead of any marking that isn't the current or previous one. - var/newpos = pref.body_markings.Find(inject_after) - if(newpos) - moveElement(pref.body_markings, start, newpos+1) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_remove"]) - var/M = href_list["marking_remove"] - winshow(user, "prefs_markings_subwindow", FALSE) - pref.body_markings -= M - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["marking_color"]) - var/M = href_list["marking_color"] - if (isnull(pref.body_markings[M]["color"])) - if (tgui_alert(user, "You currently have customized marking colors. This will reset each bodypart's color. Are you sure you want to continue?","Reset Bodypart Colors",list("Yes","No")) == "No") - return TOPIC_NOACTION - var/mark_color = input(user, "Choose the [M] color: ", "Character Preference", pref.body_markings[M]["color"]) as color|null - if(mark_color && CanUseTopic(user)) - pref.body_markings[M] = pref.mass_edit_marking_list(M,FALSE,TRUE,pref.body_markings[M],color="[mark_color]") - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["marking_submenu"]) - var/M = href_list["marking_submenu"] - markings_subwindow(user, M) - return TOPIC_NOACTION - - else if (href_list["toggle_all_marking_selection"]) - var/toggle = text2num(href_list["toggle"]) - var/marking = href_list["toggle_all_marking_selection"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - pref.body_markings[marking] = pref.mass_edit_marking_list(marking,TRUE,FALSE,pref.body_markings[marking],on=toggle) - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["color_all_marking_selection"]) - var/marking = href_list["color_all_marking_selection"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - var/mark_color = input(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking]["color"]) as color|null - if(mark_color && CanUseTopic(user)) - pref.body_markings[marking] = pref.mass_edit_marking_list(marking,FALSE,TRUE,pref.body_markings[marking],color="[mark_color]") - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["zone_marking_color"]) - var/marking = href_list["zone_marking_color"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - var/zone = href_list["zone"] - pref.body_markings[marking]["color"] = null //turn off the color button outside the submenu - var/mark_color = input(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking][zone]["color"]) as color|null - if(mark_color && CanUseTopic(user)) - pref.body_markings[marking][zone]["color"] = "[mark_color]" - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if (href_list["zone_marking_toggle"]) - var/marking = href_list["zone_marking_toggle"] - if (pref.body_markings.Find(marking) == 0) - winshow(user, "prefs_markings_subwindow", FALSE) - return TOPIC_NOACTION - var/zone = href_list["zone"] - pref.body_markings[marking][zone]["on"] = text2num(href_list["toggle"]) - markings_subwindow(user, marking) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["reset_limbs"]) - reset_limbs() - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["limbs"]) - - var/list/limb_selection_list = list("Left Leg","Right Leg","Left Arm","Right Arm","Left Foot","Right Foot","Left Hand","Right Hand","Full Body") - - // Full prosthetic bodies without a brain are borderline unkillable so make sure they have a brain to remove/destroy. - var/datum/species/current_species = GLOB.all_species[pref.species] - if(!current_species.has_organ["brain"]) - limb_selection_list -= "Full Body" - else if(pref.organ_data[BP_TORSO] == "cyborg") - limb_selection_list |= "Head" - - var/organ_tag = tgui_input_list(user, "Which limb do you want to change?", "Limb Choice", limb_selection_list) - - if(!organ_tag || !CanUseTopic(user)) return TOPIC_NOACTION - - var/limb = null - var/second_limb = null // if you try to change the arm, the hand should also change - var/third_limb = null // if you try to unchange the hand, the arm should also change - - // Do not let them amputate their entire body, ty. - var/list/choice_options = list("Normal","Amputated","Prosthesis") - switch(organ_tag) - if("Left Leg") - limb = BP_L_LEG - second_limb = BP_L_FOOT - if("Right Leg") - limb = BP_R_LEG - second_limb = BP_R_FOOT - if("Left Arm") - limb = BP_L_ARM - second_limb = BP_L_HAND - if("Right Arm") - limb = BP_R_ARM - second_limb = BP_R_HAND - if("Left Foot") - limb = BP_L_FOOT - third_limb = BP_L_LEG - if("Right Foot") - limb = BP_R_FOOT - third_limb = BP_R_LEG - if("Left Hand") - limb = BP_L_HAND - third_limb = BP_L_ARM - if("Right Hand") - limb = BP_R_HAND - third_limb = BP_R_ARM - if("Head") - limb = BP_HEAD - choice_options = list("Prosthesis") - if("Full Body") - limb = BP_TORSO - second_limb = BP_HEAD - third_limb = BP_GROIN - choice_options = list("Normal","Prosthesis") - - var/new_state = tgui_input_list(user, "What state do you wish the limb to be in?", "State Choice", choice_options) - if(!new_state || !CanUseTopic(user)) return TOPIC_NOACTION - - switch(new_state) - if("Normal") - pref.organ_data[limb] = null - pref.rlimb_data[limb] = null - if(limb == BP_TORSO) - for(var/other_limb in BP_ALL - BP_TORSO) - pref.organ_data[other_limb] = null - pref.rlimb_data[other_limb] = null - for(var/internal in O_STANDARD) - pref.organ_data[internal] = null - pref.rlimb_data[internal] = null - if(third_limb) - pref.organ_data[third_limb] = null - pref.rlimb_data[third_limb] = null - - if("Amputated") - if(limb == BP_TORSO) - return - pref.organ_data[limb] = "amputated" - pref.rlimb_data[limb] = null - if(second_limb) - pref.organ_data[second_limb] = "amputated" - pref.rlimb_data[second_limb] = null - - if("Prosthesis") - var/tmp_species = pref.species ? pref.species : SPECIES_HUMAN - var/list/usable_manufacturers = list() - for(var/company in chargen_robolimbs) - var/datum/robolimb/M = chargen_robolimbs[company] - if(!(limb in M.parts)) - continue - if(tmp_species in M.species_cannot_use) - continue - //VOREStation Add - Cyberlimb whitelisting. - if(M.whitelisted_to && !(user.ckey in M.whitelisted_to)) - continue - //VOREStation Add End - usable_manufacturers[company] = M - if(!usable_manufacturers.len) - return - var/choice = tgui_input_list(user, "Which manufacturer do you wish to use for this limb?", "Manufacturer Choice", usable_manufacturers) - if(!choice) - return - - pref.rlimb_data[limb] = choice - pref.organ_data[limb] = "cyborg" - - if(second_limb) - pref.rlimb_data[second_limb] = choice - pref.organ_data[second_limb] = "cyborg" - if(third_limb && pref.organ_data[third_limb] == "amputated") - pref.organ_data[third_limb] = null - - if(limb == BP_TORSO) - for(var/other_limb in BP_ALL - BP_TORSO) - if(pref.organ_data[other_limb]) - continue - pref.organ_data[other_limb] = "cyborg" - pref.rlimb_data[other_limb] = choice - if(!pref.organ_data[O_BRAIN]) - pref.organ_data[O_BRAIN] = "assisted" - for(var/internal_organ in list(O_HEART,O_EYES)) - pref.organ_data[internal_organ] = "mechanical" - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["organs"]) - - var/organ_name = tgui_input_list(user, "Which internal function do you want to change?", "Internal Organ", list("Heart", "Eyes", "Larynx", "Lungs", "Liver", "Kidneys", "Spleen", "Intestines", "Stomach", "Brain")) - if(!organ_name) return - - var/organ = null - switch(organ_name) - if("Heart") - organ = O_HEART - if("Eyes") - organ = O_EYES - if("Larynx") - organ = O_VOICE - if("Lungs") - organ = O_LUNGS - if("Liver") - organ = O_LIVER - if("Kidneys") - organ = O_KIDNEYS - if("Spleen") - organ = O_SPLEEN - if("Intestines") - organ = O_INTESTINE - if("Stomach") - organ = O_STOMACH - if("Brain") - if(pref.organ_data[BP_HEAD] != "cyborg") - to_chat(user, "You may only select a cybernetic or synthetic brain if you have a full prosthetic body.") - return - organ = "brain" - - var/datum/species/current_species = GLOB.all_species[pref.species] - var/list/organ_choices = list("Normal") - if(pref.organ_data[BP_TORSO] == "cyborg") - organ_choices -= "Normal" - if(organ_name == "Brain") - organ_choices += "Cybernetic" - if(!(current_species.spawn_flags & SPECIES_NO_POSIBRAIN)) - organ_choices += "Positronic" - if(!(current_species.spawn_flags & SPECIES_NO_DRONEBRAIN)) - organ_choices += "Drone" - else - organ_choices += "Assisted" - organ_choices += "Mechanical" - else - organ_choices += "Assisted" - organ_choices += "Mechanical" - - var/new_state = tgui_input_list(user, "What state do you wish the organ to be in?", "State Choice", organ_choices) - if(!new_state) return - - switch(new_state) - if("Normal") - pref.organ_data[organ] = null - if("Assisted") - pref.organ_data[organ] = "assisted" - if("Cybernetic") - pref.organ_data[organ] = "assisted" - if("Mechanical") - pref.organ_data[organ] = "mechanical" - if("Drone") - pref.organ_data[organ] = "digital" - if("Positronic") - pref.organ_data[organ] = "mechanical" - - return TOPIC_REFRESH - - else if(href_list["disabilities"]) - var/disability_flag = text2num(href_list["disabilities"]) - pref.disabilities ^= disability_flag - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["toggle_preview_value"]) - pref.equip_preview_mob ^= text2num(href_list["toggle_preview_value"]) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["toggle_animations"]) - pref.animations_toggle = !pref.animations_toggle - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["synth_color"]) - pref.synth_color = !pref.synth_color - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["synth2_color"]) - var/new_color = input(user, "Choose your character's synth colour: ", "Character Preference", rgb(pref.r_synth, pref.g_synth, pref.b_synth)) as color|null - if(new_color && CanUseTopic(user)) - pref.r_synth = hex2num(copytext(new_color, 2, 4)) - pref.g_synth = hex2num(copytext(new_color, 4, 6)) - pref.b_synth = hex2num(copytext(new_color, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["synth_markings"]) - pref.synth_markings = !pref.synth_markings - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["cycle_bg"]) - pref.bgstate = next_in_list(pref.bgstate, pref.bgstate_options) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_style"]) - var/new_ear_style = tgui_input_list(user, "Select an ear style for this character:", "Character Preference", pref.get_available_styles(global.ear_styles_list), pref.ear_style) - if(new_ear_style) - pref.ear_style = new_ear_style - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_color"]) - var/new_earc = input(user, "Choose your character's ear colour:", "Character Preference", - rgb(pref.r_ears, pref.g_ears, pref.b_ears)) as color|null - if(new_earc) - pref.r_ears = hex2num(copytext(new_earc, 2, 4)) - pref.g_ears = hex2num(copytext(new_earc, 4, 6)) - pref.b_ears = hex2num(copytext(new_earc, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_color2"]) - var/new_earc2 = input(user, "Choose your character's ear colour:", "Character Preference", - rgb(pref.r_ears2, pref.g_ears2, pref.b_ears2)) as color|null - if(new_earc2) - pref.r_ears2 = hex2num(copytext(new_earc2, 2, 4)) - pref.g_ears2 = hex2num(copytext(new_earc2, 4, 6)) - pref.b_ears2 = hex2num(copytext(new_earc2, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["ear_color3"]) - var/new_earc3 = input(user, "Choose your character's tertiary ear colour:", "Character Preference", - rgb(pref.r_ears3, pref.g_ears3, pref.b_ears3)) as color|null - if(new_earc3) - pref.r_ears3 = hex2num(copytext(new_earc3, 2, 4)) - pref.g_ears3 = hex2num(copytext(new_earc3, 4, 6)) - pref.b_ears3 = hex2num(copytext(new_earc3, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_style"]) - var/new_tail_style = tgui_input_list(user, "Select a tail style for this character:", "Character Preference", pref.get_available_styles(global.tail_styles_list), pref.tail_style) - if(new_tail_style) - pref.tail_style = new_tail_style - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_color"]) - var/new_tailc = input(user, "Choose your character's tail/taur colour:", "Character Preference", - rgb(pref.r_tail, pref.g_tail, pref.b_tail)) as color|null - if(new_tailc) - pref.r_tail = hex2num(copytext(new_tailc, 2, 4)) - pref.g_tail = hex2num(copytext(new_tailc, 4, 6)) - pref.b_tail = hex2num(copytext(new_tailc, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_color2"]) - var/new_tailc2 = input(user, "Choose your character's secondary tail/taur colour:", "Character Preference", - rgb(pref.r_tail2, pref.g_tail2, pref.b_tail2)) as color|null - if(new_tailc2) - pref.r_tail2 = hex2num(copytext(new_tailc2, 2, 4)) - pref.g_tail2 = hex2num(copytext(new_tailc2, 4, 6)) - pref.b_tail2 = hex2num(copytext(new_tailc2, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["tail_color3"]) - var/new_tailc3 = input(user, "Choose your character's tertiary tail/taur colour:", "Character Preference", - rgb(pref.r_tail3, pref.g_tail3, pref.b_tail3)) as color|null - if(new_tailc3) - pref.r_tail3 = hex2num(copytext(new_tailc3, 2, 4)) - pref.g_tail3 = hex2num(copytext(new_tailc3, 4, 6)) - pref.b_tail3 = hex2num(copytext(new_tailc3, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_style"]) - var/new_wing_style = tgui_input_list(user, "Select a wing style for this character:", "Character Preference", pref.get_available_styles(global.wing_styles_list), pref.wing_style) - if(new_wing_style) - pref.wing_style = new_wing_style - - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_color"]) - var/new_wingc = input(user, "Choose your character's wing colour:", "Character Preference", - rgb(pref.r_wing, pref.g_wing, pref.b_wing)) as color|null - if(new_wingc) - pref.r_wing = hex2num(copytext(new_wingc, 2, 4)) - pref.g_wing = hex2num(copytext(new_wingc, 4, 6)) - pref.b_wing = hex2num(copytext(new_wingc, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_color2"]) - var/new_wingc2 = input(user, "Choose your character's secondary wing colour:", "Character Preference", - rgb(pref.r_wing2, pref.g_wing2, pref.b_wing2)) as color|null - if(new_wingc2) - pref.r_wing2 = hex2num(copytext(new_wingc2, 2, 4)) - pref.g_wing2 = hex2num(copytext(new_wingc2, 4, 6)) - pref.b_wing2 = hex2num(copytext(new_wingc2, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["wing_color3"]) - var/new_wingc3 = input(user, "Choose your character's tertiary wing colour:", "Character Preference", - rgb(pref.r_wing3, pref.g_wing3, pref.b_wing3)) as color|null - if(new_wingc3) - pref.r_wing3 = hex2num(copytext(new_wingc3, 2, 4)) - pref.g_wing3 = hex2num(copytext(new_wingc3, 4, 6)) - pref.b_wing3 = hex2num(copytext(new_wingc3, 6, 8)) - return TOPIC_REFRESH_UPDATE_PREVIEW - - return ..() - -/datum/category_item/player_setup_item/general/body/proc/reset_limbs() - - for(var/organ in pref.organ_data) - pref.organ_data[organ] = null - while(null in pref.organ_data) - pref.organ_data -= null - - for(var/organ in pref.rlimb_data) - pref.rlimb_data[organ] = null - while(null in pref.rlimb_data) - pref.rlimb_data -= null - - // Sanitize the name so that there aren't any numbers sticking around. - pref.real_name = sanitize_name(pref.real_name, pref.species) - if(!pref.real_name) - pref.real_name = random_name(pref.identifying_gender, pref.species) - -/datum/category_item/player_setup_item/general/body/proc/SetSpecies(mob/user) - if(!pref.species_preview || !(pref.species_preview in GLOB.all_species)) - pref.species_preview = SPECIES_HUMAN - var/datum/species/current_species = GLOB.all_species[pref.species_preview] - var/dat = "" - dat += "

                    [current_species.name] \[change\]


                    " - dat += "" - dat += "" - //vorestation edit begin - if(current_species.wikilink) - dat += "" - else - dat += "" - //vorestation edit end - dat += "" - dat += "" - dat += "
                    [current_species.blurb]

                    See the wiki for more details.
                    [current_species.blurb]" - if("preview" in cached_icon_states(current_species.icobase)) - usr << browse_rsc(icon(current_species.icobase,"preview"), "species_preview_[current_species.name].png") - dat += "

                    " - dat += "Language: [current_species.species_language]
                    " - dat += "" - if(current_species.spawn_flags & SPECIES_CAN_JOIN) - switch(current_species.rarity_value) - if(1 to 2) - dat += "
                    Often present on human stations." - if(3 to 4) - dat += "
                    Rarely present on human stations." - if(5) - dat += "
                    Unheard of on human stations." - else - dat += "
                    May be present on human stations." - if(current_species.spawn_flags & SPECIES_IS_WHITELISTED) - dat += "
                    Whitelist restricted." - if(!current_species.has_organ[O_HEART]) - dat += "
                    Does not have a circulatory system." - if(!current_species.has_organ[O_LUNGS]) - dat += "
                    Does not have a respiratory system." - if(current_species.flags & NO_SCAN) - dat += "
                    Does not have DNA." - if(current_species.flags & NO_DEFIB) - dat += "
                    Cannot be defibrillated." - if(current_species.flags & NO_PAIN) - dat += "
                    Does not feel pain." - if(current_species.flags & NO_SLIP) - dat += "
                    Has excellent traction." - if(current_species.flags & NO_POISON) - dat += "
                    Immune to most poisons." - if(current_species.appearance_flags & HAS_SKIN_TONE) - dat += "
                    Has a variety of skin tones." - if(current_species.appearance_flags & HAS_SKIN_COLOR) - dat += "
                    Has a variety of skin colours." - if(current_species.appearance_flags & HAS_EYE_COLOR) - dat += "
                    Has a variety of eye colours." - if(current_species.flags & IS_PLANT) - dat += "
                    Has a plantlike physiology." - dat += "

                    " - - var/restricted = 0 - - if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) - restricted = 2 - else if(!is_alien_whitelisted(preference_mob(),current_species)) - restricted = 1 - - if(restricted) - if(restricted == 1) - dat += "You cannot play as this species.
                    If you wish to be whitelisted, you can make an application post on the forums.

                    " - else if(restricted == 2) - dat += "You cannot play as this species.
                    This species is not available for play as a station race..

                    " - if(!restricted || check_rights(R_ADMIN|R_EVENT, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) //VOREStation Edit: selectability - dat += "\[select\]" - dat += "
                    " - - user << browse(dat, "window=species;size=700x400") - -/datum/category_item/player_setup_item/general/body/proc/markings_subwindow(mob/user, marking) - var/static/list/part_to_string = list(BP_HEAD = "Head", BP_TORSO = "Upper Body", BP_GROIN = "Lower Body", BP_R_ARM = "Right Arm", BP_L_ARM = "Left Arm", BP_R_HAND = "Right Hand", BP_L_HAND = "Left Hand", BP_R_LEG = "Right Leg", BP_L_LEG = "Left Leg", BP_R_FOOT = "Right Foot", BP_L_FOOT = "Left Foot") - var/dat = "

                    Editing '[marking]'


                    " - dat += "Enable All " - dat += "Disable All " - dat += "Change Color of All
                    " - dat += "
                    " - for (var/bodypart in pref.body_markings[marking]) - if (!islist(pref.body_markings[marking][bodypart])) continue - dat += "[part_to_string[bodypart]]: [color_square(hex = pref.body_markings[marking][bodypart]["color"])] " - dat += "Change " - dat += "[pref.body_markings[marking][bodypart]["on"] ? "Toggle Off" : "Toggle On"]
                    " - - dat += "" - winshow(user, "prefs_markings_subwindow", TRUE) - pref.markings_subwindow = new(user, "prefs_markings_browser", "Marking Editor", 400, 400) - pref.markings_subwindow.set_content(dat) - pref.markings_subwindow.open(FALSE) - onclose(user, "prefs_markings_subwindow", src) +var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-") + +/datum/preferences + var/equip_preview_mob = EQUIP_PREVIEW_ALL + var/animations_toggle = FALSE + + var/icon/bgstate = "000" + var/list/bgstate_options = list("000", "midgrey", "FFF", "white", "steel", "techmaint", "dark", "plating", "reinforced") + + var/ear_style // Type of selected ear style + var/r_ears = 30 // Ear color. + var/g_ears = 30 // Ear color + var/b_ears = 30 // Ear color + var/r_ears2 = 30 // Ear extra color. + var/g_ears2 = 30 // Ear extra color + var/b_ears2 = 30 // Ear extra color + var/r_ears3 = 30 // Ear tertiary color. + var/g_ears3 = 30 // Ear tertiary color + var/b_ears3 = 30 // Ear tertiary color + var/tail_style // Type of selected tail style + var/r_tail = 30 // Tail/Taur color + var/g_tail = 30 // Tail/Taur color + var/b_tail = 30 // Tail/Taur color + var/r_tail2 = 30 // For extra overlay. + var/g_tail2 = 30 // For extra overlay. + var/b_tail2 = 30 // For extra overlay. + var/r_tail3 = 30 // For tertiary overlay. + var/g_tail3 = 30 // For tertiary overlay. + var/b_tail3 = 30 // For tertiary overlay. + var/wing_style // Type of selected wing style + var/r_wing = 30 // Wing color + var/g_wing = 30 // Wing color + var/b_wing = 30 // Wing color + var/r_wing2 = 30 // Wing extra color + var/g_wing2 = 30 // Wing extra color + var/b_wing2 = 30 // Wing extra color + var/r_wing3 = 30 // Wing tertiary color + var/g_wing3 = 30 // Wing tertiary color + var/b_wing3 = 30 // Wing tertiary color + var/datum/browser/markings_subwindow = null + +// Sanitize ear/wing/tail styles +/datum/preferences/proc/sanitize_body_styles() + + // Grandfather in anyone loading paths from a save. + if(ispath(ear_style, /datum/sprite_accessory)) + var/datum/sprite_accessory/instance = global.ear_styles_list[ear_style] + if(istype(instance)) + ear_style = instance.name + if(ispath(wing_style, /datum/sprite_accessory)) + var/datum/sprite_accessory/instance = global.wing_styles_list[wing_style] + if(istype(instance)) + wing_style = instance.name + if(ispath(tail_style, /datum/sprite_accessory)) + var/datum/sprite_accessory/instance = global.tail_styles_list[tail_style] + if(istype(instance)) + tail_style = instance.name + + // Sanitize for non-existent keys. + if(ear_style && !(ear_style in get_available_styles(global.ear_styles_list))) + ear_style = null + if(wing_style && !(wing_style in get_available_styles(global.wing_styles_list))) + wing_style = null + if(tail_style && !(tail_style in get_available_styles(global.tail_styles_list))) + tail_style = null + +/datum/preferences/proc/get_available_styles(var/style_list) + . = list("Normal" = null) + for(var/path in style_list) + var/datum/sprite_accessory/instance = style_list[path] + if(!istype(instance)) + continue + if(instance.ckeys_allowed && (!client || !(client.ckey in instance.ckeys_allowed))) + continue + if(instance.species_allowed && (!species || !(species in instance.species_allowed)) && (!client || !check_rights(R_ADMIN | R_EVENT | R_FUN, 0, client)) && (!custom_base || !(custom_base in instance.species_allowed))) //VOREStation Edit: Custom Species + continue + .[instance.name] = instance + +/datum/preferences/proc/mass_edit_marking_list(var/marking, var/change_on = TRUE, var/change_color = TRUE, var/marking_value = null, var/on = TRUE, var/color = "#000000") + var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[marking] + var/list/new_marking = marking_value||mark_datum.body_parts + for (var/NM in new_marking) + if (marking_value && !islist(new_marking[NM])) continue + new_marking[NM] = list("on" = (!change_on && marking_value) ? marking_value[NM]["on"] : on, "color" = (!change_color && marking_value) ? marking_value[NM]["color"] : color) + if (change_color) + new_marking["color"] = color + return new_marking + +/datum/category_item/player_setup_item/general/body + name = "Body" + sort_order = 3 + +/datum/category_item/player_setup_item/general/body/load_character(var/savefile/S) + S["species"] >> pref.species + S["hair_red"] >> pref.r_hair + S["hair_green"] >> pref.g_hair + S["hair_blue"] >> pref.b_hair + S["facial_red"] >> pref.r_facial + S["grad_red"] >> pref.r_grad + S["grad_green"] >> pref.g_grad + S["grad_blue"] >> pref.b_grad + S["facial_green"] >> pref.g_facial + S["facial_blue"] >> pref.b_facial + S["skin_tone"] >> pref.s_tone + S["skin_red"] >> pref.r_skin + S["skin_green"] >> pref.g_skin + S["skin_blue"] >> pref.b_skin + S["hair_style_name"] >> pref.h_style + S["facial_style_name"] >> pref.f_style + S["grad_style_name"] >> pref.grad_style + S["eyes_red"] >> pref.r_eyes + S["eyes_green"] >> pref.g_eyes + S["eyes_blue"] >> pref.b_eyes + S["b_type"] >> pref.b_type + S["disabilities"] >> pref.disabilities + S["organ_data"] >> pref.organ_data + S["rlimb_data"] >> pref.rlimb_data + S["body_markings"] >> pref.body_markings + S["synth_color"] >> pref.synth_color + S["synth_red"] >> pref.r_synth + S["synth_green"] >> pref.g_synth + S["synth_blue"] >> pref.b_synth + S["synth_markings"] >> pref.synth_markings + S["bgstate"] >> pref.bgstate + S["body_descriptors"] >> pref.body_descriptors + S["ear_style"] >> pref.ear_style + S["r_ears"] >> pref.r_ears + S["g_ears"] >> pref.g_ears + S["b_ears"] >> pref.b_ears + S["r_ears2"] >> pref.r_ears2 + S["g_ears2"] >> pref.g_ears2 + S["b_ears2"] >> pref.b_ears2 + S["r_ears3"] >> pref.r_ears3 + S["g_ears3"] >> pref.g_ears3 + S["b_ears3"] >> pref.b_ears3 + S["tail_style"] >> pref.tail_style + S["r_tail"] >> pref.r_tail + S["g_tail"] >> pref.g_tail + S["b_tail"] >> pref.b_tail + S["r_tail2"] >> pref.r_tail2 + S["g_tail2"] >> pref.g_tail2 + S["b_tail2"] >> pref.b_tail2 + S["r_tail3"] >> pref.r_tail3 + S["g_tail3"] >> pref.g_tail3 + S["b_tail3"] >> pref.b_tail3 + S["wing_style"] >> pref.wing_style + S["r_wing"] >> pref.r_wing + S["g_wing"] >> pref.g_wing + S["b_wing"] >> pref.b_wing + S["r_wing2"] >> pref.r_wing2 + S["g_wing2"] >> pref.g_wing2 + S["b_wing2"] >> pref.b_wing2 + S["r_wing3"] >> pref.r_wing3 + S["g_wing3"] >> pref.g_wing3 + S["b_wing3"] >> pref.b_wing3 + S["digitigrade"] >> pref.digitigrade + +/datum/category_item/player_setup_item/general/body/save_character(var/savefile/S) + S["species"] << pref.species + S["hair_red"] << pref.r_hair + S["hair_green"] << pref.g_hair + S["hair_blue"] << pref.b_hair + S["grad_red"] << pref.r_grad + S["grad_green"] << pref.g_grad + S["grad_blue"] << pref.b_grad + S["facial_red"] << pref.r_facial + S["facial_green"] << pref.g_facial + S["facial_blue"] << pref.b_facial + S["skin_tone"] << pref.s_tone + S["skin_red"] << pref.r_skin + S["skin_green"] << pref.g_skin + S["skin_blue"] << pref.b_skin + S["hair_style_name"] << pref.h_style + S["facial_style_name"] << pref.f_style + S["grad_style_name"] << pref.grad_style + S["eyes_red"] << pref.r_eyes + S["eyes_green"] << pref.g_eyes + S["eyes_blue"] << pref.b_eyes + S["b_type"] << pref.b_type + S["disabilities"] << pref.disabilities + S["organ_data"] << pref.organ_data + S["rlimb_data"] << pref.rlimb_data + S["body_markings"] << pref.body_markings + S["synth_color"] << pref.synth_color + S["synth_red"] << pref.r_synth + S["synth_green"] << pref.g_synth + S["synth_blue"] << pref.b_synth + S["synth_markings"] << pref.synth_markings + S["bgstate"] << pref.bgstate + S["body_descriptors"] << pref.body_descriptors + S["ear_style"] << pref.ear_style + S["r_ears"] << pref.r_ears + S["g_ears"] << pref.g_ears + S["b_ears"] << pref.b_ears + S["r_ears2"] << pref.r_ears2 + S["g_ears2"] << pref.g_ears2 + S["b_ears2"] << pref.b_ears2 + S["r_ears3"] << pref.r_ears3 + S["g_ears3"] << pref.g_ears3 + S["b_ears3"] << pref.b_ears3 + S["tail_style"] << pref.tail_style + S["r_tail"] << pref.r_tail + S["g_tail"] << pref.g_tail + S["b_tail"] << pref.b_tail + S["r_tail2"] << pref.r_tail2 + S["g_tail2"] << pref.g_tail2 + S["b_tail2"] << pref.b_tail2 + S["r_tail3"] << pref.r_tail3 + S["g_tail3"] << pref.g_tail3 + S["b_tail3"] << pref.b_tail3 + S["wing_style"] << pref.wing_style + S["r_wing"] << pref.r_wing + S["g_wing"] << pref.g_wing + S["b_wing"] << pref.b_wing + S["r_wing2"] << pref.r_wing2 + S["g_wing2"] << pref.g_wing2 + S["b_wing2"] << pref.b_wing2 + S["r_wing3"] << pref.r_wing3 + S["g_wing3"] << pref.g_wing3 + S["b_wing3"] << pref.b_wing3 + S["digitigrade"] << pref.digitigrade + +/datum/category_item/player_setup_item/general/body/sanitize_character(var/savefile/S) + if(!pref.species || !(pref.species in GLOB.playable_species)) + pref.species = SPECIES_HUMAN + pref.r_hair = sanitize_integer(pref.r_hair, 0, 255, initial(pref.r_hair)) + pref.g_hair = sanitize_integer(pref.g_hair, 0, 255, initial(pref.g_hair)) + pref.b_hair = sanitize_integer(pref.b_hair, 0, 255, initial(pref.b_hair)) + pref.r_grad = sanitize_integer(pref.r_grad, 0, 255, initial(pref.r_grad)) + pref.g_grad = sanitize_integer(pref.g_grad, 0, 255, initial(pref.g_grad)) + pref.b_grad = sanitize_integer(pref.b_grad, 0, 255, initial(pref.b_grad)) + pref.r_facial = sanitize_integer(pref.r_facial, 0, 255, initial(pref.r_facial)) + pref.g_facial = sanitize_integer(pref.g_facial, 0, 255, initial(pref.g_facial)) + pref.b_facial = sanitize_integer(pref.b_facial, 0, 255, initial(pref.b_facial)) + pref.s_tone = sanitize_integer(pref.s_tone, -185, 34, initial(pref.s_tone)) + pref.r_skin = sanitize_integer(pref.r_skin, 0, 255, initial(pref.r_skin)) + pref.g_skin = sanitize_integer(pref.g_skin, 0, 255, initial(pref.g_skin)) + pref.b_skin = sanitize_integer(pref.b_skin, 0, 255, initial(pref.b_skin)) + pref.h_style = sanitize_inlist(pref.h_style, hair_styles_list, initial(pref.h_style)) + pref.f_style = sanitize_inlist(pref.f_style, facial_hair_styles_list, initial(pref.f_style)) + pref.grad_style = sanitize_inlist(pref.grad_style, GLOB.hair_gradients, initial(pref.grad_style)) + pref.r_eyes = sanitize_integer(pref.r_eyes, 0, 255, initial(pref.r_eyes)) + pref.g_eyes = sanitize_integer(pref.g_eyes, 0, 255, initial(pref.g_eyes)) + pref.b_eyes = sanitize_integer(pref.b_eyes, 0, 255, initial(pref.b_eyes)) + pref.b_type = sanitize_text(pref.b_type, initial(pref.b_type)) + + pref.disabilities = sanitize_integer(pref.disabilities, 0, 65535, initial(pref.disabilities)) + if(!pref.organ_data) pref.organ_data = list() + if(!pref.rlimb_data) pref.rlimb_data = list() + if(!pref.body_markings) pref.body_markings = list() + else pref.body_markings &= body_marking_styles_list + for (var/M in pref.body_markings) //VOREStation Edit + if (!islist(pref.body_markings[M])) + var/col = istext(pref.body_markings[M]) ? pref.body_markings[M] : "#000000" + pref.body_markings[M] = pref.mass_edit_marking_list(M,color=col) + if(!pref.bgstate || !(pref.bgstate in pref.bgstate_options)) + pref.bgstate = "000" + + pref.r_ears = sanitize_integer(pref.r_ears, 0, 255, initial(pref.r_ears)) + pref.g_ears = sanitize_integer(pref.g_ears, 0, 255, initial(pref.g_ears)) + pref.b_ears = sanitize_integer(pref.b_ears, 0, 255, initial(pref.b_ears)) + pref.r_ears2 = sanitize_integer(pref.r_ears2, 0, 255, initial(pref.r_ears2)) + pref.g_ears2 = sanitize_integer(pref.g_ears2, 0, 255, initial(pref.g_ears2)) + pref.b_ears2 = sanitize_integer(pref.b_ears2, 0, 255, initial(pref.b_ears2)) + pref.r_ears3 = sanitize_integer(pref.r_ears3, 0, 255, initial(pref.r_ears3)) + pref.g_ears3 = sanitize_integer(pref.g_ears3, 0, 255, initial(pref.g_ears3)) + pref.b_ears3 = sanitize_integer(pref.b_ears3, 0, 255, initial(pref.b_ears3)) + pref.r_tail = sanitize_integer(pref.r_tail, 0, 255, initial(pref.r_tail)) + pref.g_tail = sanitize_integer(pref.g_tail, 0, 255, initial(pref.g_tail)) + pref.b_tail = sanitize_integer(pref.b_tail, 0, 255, initial(pref.b_tail)) + pref.r_tail2 = sanitize_integer(pref.r_tail2, 0, 255, initial(pref.r_tail2)) + pref.g_tail2 = sanitize_integer(pref.g_tail2, 0, 255, initial(pref.g_tail2)) + pref.b_tail2 = sanitize_integer(pref.b_tail2, 0, 255, initial(pref.b_tail2)) + pref.r_tail3 = sanitize_integer(pref.r_tail3, 0, 255, initial(pref.r_tail3)) + pref.g_tail3 = sanitize_integer(pref.g_tail3, 0, 255, initial(pref.g_tail3)) + pref.b_tail3 = sanitize_integer(pref.b_tail3, 0, 255, initial(pref.b_tail3)) + pref.r_wing = sanitize_integer(pref.r_wing, 0, 255, initial(pref.r_wing)) + pref.g_wing = sanitize_integer(pref.g_wing, 0, 255, initial(pref.g_wing)) + pref.b_wing = sanitize_integer(pref.b_wing, 0, 255, initial(pref.b_wing)) + pref.r_wing2 = sanitize_integer(pref.r_wing2, 0, 255, initial(pref.r_wing2)) + pref.g_wing2 = sanitize_integer(pref.g_wing2, 0, 255, initial(pref.g_wing2)) + pref.b_wing2 = sanitize_integer(pref.b_wing2, 0, 255, initial(pref.b_wing2)) + pref.r_wing3 = sanitize_integer(pref.r_wing3, 0, 255, initial(pref.r_wing3)) + pref.g_wing3 = sanitize_integer(pref.g_wing3, 0, 255, initial(pref.g_wing3)) + pref.b_wing3 = sanitize_integer(pref.b_wing3, 0, 255, initial(pref.b_wing3)) + pref.digitigrade = sanitize_integer(pref.digitigrade, 0, 1, initial(pref.digitigrade)) + + pref.sanitize_body_styles() + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/body/copy_to_mob(var/mob/living/carbon/human/character) + // Copy basic values + character.r_eyes = pref.r_eyes + character.g_eyes = pref.g_eyes + character.b_eyes = pref.b_eyes + character.h_style = pref.h_style + character.r_hair = pref.r_hair + character.g_hair = pref.g_hair + character.b_hair = pref.b_hair + character.r_grad = pref.r_grad + character.g_grad = pref.g_grad + character.b_grad = pref.b_grad + character.f_style = pref.f_style + character.r_facial = pref.r_facial + character.g_facial = pref.g_facial + character.b_facial = pref.b_facial + character.r_skin = pref.r_skin + character.g_skin = pref.g_skin + character.b_skin = pref.b_skin + character.s_tone = pref.s_tone + character.h_style = pref.h_style + character.f_style = pref.f_style + character.grad_style= pref.grad_style + character.b_type = pref.b_type + character.synth_color = pref.synth_color + character.r_synth = pref.r_synth + character.g_synth = pref.g_synth + character.b_synth = pref.b_synth + character.synth_markings = pref.synth_markings + if(character.species.digi_allowed) + character.digitigrade = pref.digitigrade + else + character.digitigrade = 0 + + //sanity check + if(character.digitigrade == null) + character.digitigrade = 0 + pref.digitigrade = 0 + + var/list/ear_styles = pref.get_available_styles(global.ear_styles_list) + character.ear_style = ear_styles[pref.ear_style] + character.r_ears = pref.r_ears + character.b_ears = pref.b_ears + character.g_ears = pref.g_ears + character.r_ears2 = pref.r_ears2 + character.b_ears2 = pref.b_ears2 + character.g_ears2 = pref.g_ears2 + character.r_ears3 = pref.r_ears3 + character.b_ears3 = pref.b_ears3 + character.g_ears3 = pref.g_ears3 + + var/list/tail_styles = pref.get_available_styles(global.tail_styles_list) + character.tail_style = tail_styles[pref.tail_style] + character.r_tail = pref.r_tail + character.b_tail = pref.b_tail + character.g_tail = pref.g_tail + character.r_tail2 = pref.r_tail2 + character.b_tail2 = pref.b_tail2 + character.g_tail2 = pref.g_tail2 + character.r_tail3 = pref.r_tail3 + character.b_tail3 = pref.b_tail3 + character.g_tail3 = pref.g_tail3 + + var/list/wing_styles = pref.get_available_styles(global.wing_styles_list) + character.wing_style = wing_styles[pref.wing_style] + character.r_wing = pref.r_wing + character.b_wing = pref.b_wing + character.g_wing = pref.g_wing + character.r_wing2 = pref.r_wing2 + character.b_wing2 = pref.b_wing2 + character.g_wing2 = pref.g_wing2 + character.r_wing3 = pref.r_wing3 + character.b_wing3 = pref.b_wing3 + character.g_wing3 = pref.g_wing3 + + character.set_gender(pref.biological_gender) + + // Destroy/cyborgize organs and limbs. + //VOREStation Edit + character.synthetic = pref.species == "Protean" ? all_robolimbs["protean"] : null //Clear the existing var. (unless protean, then switch it to the normal protean limb) + var/list/organs_to_edit = list() + for (var/name in list(BP_TORSO, BP_HEAD, BP_GROIN, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/O = character.organs_by_name[name] + if (O) + var/x = organs_to_edit.Find(O.parent_organ) + if (x == 0) + organs_to_edit += name + else + organs_to_edit.Insert(x+(O.robotic == ORGAN_NANOFORM ? 1 : 0), name) + for(var/name in organs_to_edit) //VOREStation edit end + var/status = pref.organ_data[name] + var/obj/item/organ/external/O = character.organs_by_name[name] + if(O) + if(status == "amputated") + O.remove_rejuv() + else if(status == "cyborg") + if(pref.rlimb_data[name]) + O.robotize(pref.rlimb_data[name]) + else + O.robotize() + + for(var/name in list(O_HEART,O_EYES,O_VOICE,O_LUNGS,O_LIVER,O_KIDNEYS,O_SPLEEN,O_STOMACH,O_INTESTINE,O_BRAIN)) + var/status = pref.organ_data[name] + if(!status) + continue + var/obj/item/organ/I = character.internal_organs_by_name[name] + if(istype(I, /obj/item/organ/internal/brain)) + var/obj/item/organ/external/E = character.get_organ(I.parent_organ) + if(E.robotic < ORGAN_ASSISTED) + continue + if(I) + if(status == "assisted") + I.mechassist() + else if(status == "mechanical") + I.robotize() + else if(status == "digital") + I.digitize() + + for(var/N in character.organs_by_name) + var/obj/item/organ/external/O = character.organs_by_name[N] + O.markings.Cut() + + var/priority = 0 + for(var/M in pref.body_markings) + priority += 1 + var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[M] + //var/mark_color = "[pref.body_markings[M]]" //VOREStation Edit + + for(var/BP in mark_datum.body_parts) + var/obj/item/organ/external/O = character.organs_by_name[BP] + if(O) + O.markings[M] = list("color" = pref.body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = pref.body_markings[M][BP]["on"]) + character.markings_len = priority + + var/list/last_descriptors = list() + if(islist(pref.body_descriptors)) + last_descriptors = pref.body_descriptors.Copy() + pref.body_descriptors = list() + + var/datum/species/mob_species = GLOB.all_species[pref.species] + if(LAZYLEN(mob_species.descriptors)) + for(var/entry in mob_species.descriptors) + var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] + if(istype(descriptor)) + if(isnull(last_descriptors[entry])) + pref.body_descriptors[entry] = descriptor.default_value // Species datums have initial default value. + else + pref.body_descriptors[entry] = CLAMP(last_descriptors[entry], 1, LAZYLEN(descriptor.standalone_value_descriptors)) + +/datum/category_item/player_setup_item/general/body/content(var/mob/user) + . = list() + + var/datum/species/mob_species = GLOB.all_species[pref.species] + . += "
                    Body " + . += "(®)" + . += "
                    " + . += "Species: [pref.species]
                    " + . += "Blood Type: [pref.b_type]
                    " + if(has_flag(mob_species, HAS_SKIN_TONE)) + . += "Skin Tone: [-pref.s_tone + 35]/220
                    " + . += "Needs Glasses: [pref.disabilities & NEARSIGHTED ? "Yes" : "No"]
                    " + . += "Limbs: Adjust Reset
                    " + . += "Internal Organs: Adjust
                    " + + //display limbs below + var/ind = 0 + for(var/name in pref.organ_data) + var/status = pref.organ_data[name] + var/organ_name = null + + switch(name) + if(BP_TORSO) + organ_name = "torso" + if(BP_GROIN) + organ_name = "groin" + if(BP_HEAD) + organ_name = "head" + if(BP_L_ARM) + organ_name = "left arm" + if(BP_R_ARM) + organ_name = "right arm" + if(BP_L_LEG) + organ_name = "left leg" + if(BP_R_LEG) + organ_name = "right leg" + if(BP_L_FOOT) + organ_name = "left foot" + if(BP_R_FOOT) + organ_name = "right foot" + if(BP_L_HAND) + organ_name = "left hand" + if(BP_R_HAND) + organ_name = "right hand" + if(O_HEART) + organ_name = "heart" + if(O_EYES) + organ_name = "eyes" + if(O_VOICE) + organ_name = "larynx" + if(O_BRAIN) + organ_name = "brain" + if(O_LUNGS) + organ_name = "lungs" + if(O_LIVER) + organ_name = "liver" + if(O_KIDNEYS) + organ_name = "kidneys" + if(O_SPLEEN) + organ_name = "spleen" + if(O_STOMACH) + organ_name = "stomach" + if(O_INTESTINE) + organ_name = "intestines" + + if(status == "cyborg") + ++ind + if(ind > 1) + . += ", " + var/datum/robolimb/R + if(pref.rlimb_data[name] && all_robolimbs[pref.rlimb_data[name]]) + R = all_robolimbs[pref.rlimb_data[name]] + else + R = basic_robolimb + . += "\t[R.company] [organ_name] prosthesis" + else if(status == "amputated") + ++ind + if(ind > 1) + . += ", " + . += "\tAmputated [organ_name]" + else if(status == "mechanical") + ++ind + if(ind > 1) + . += ", " + switch(organ_name) + if ("brain") + . += "\tPositronic [organ_name]" + else + . += "\tSynthetic [organ_name]" + else if(status == "digital") + ++ind + if(ind > 1) + . += ", " + . += "\tDigital [organ_name]" + else if(status == "assisted") + ++ind + if(ind > 1) + . += ", " + switch(organ_name) + if("heart") + . += "\tPacemaker-assisted [organ_name]" + if("lungs") + . += "\tAssisted [organ_name]" + if("voicebox") //on adding voiceboxes for speaking skrell/similar replacements + . += "\tSurgically altered [organ_name]" + if("eyes") + . += "\tRetinal overlayed [organ_name]" + if("brain") + . += "\tAssisted-interface [organ_name]" + else + . += "\tMechanically assisted [organ_name]" + if(!ind) + . += "\[...\]

                    " + else + . += "

                    " + + if(LAZYLEN(pref.body_descriptors)) + . += "" + for(var/entry in pref.body_descriptors) + var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] + . += "" + . += "
                    [capitalize(descriptor.chargen_label)]:[descriptor.get_standalone_value_descriptor(pref.body_descriptors[entry])]Change

                    " + + . += "
                    Preview
                    " + . += "
                    Cycle background" + . += "
                    [pref.equip_preview_mob & EQUIP_PREVIEW_LOADOUT ? "Hide loadout" : "Show loadout"]" + . += "
                    [pref.equip_preview_mob & EQUIP_PREVIEW_JOB ? "Hide job gear" : "Show job gear"]" + . += "
                    [pref.animations_toggle ? "Stop animations" : "Show animations"]" + . += "
                    " + + . += "Hair
                    " + if(has_flag(mob_species, HAS_HAIR_COLOR)) + . += "Change Color [color_square(pref.r_hair, pref.g_hair, pref.b_hair)] " + . += " Style: < > [pref.h_style]
                    " //The < & > in this line is correct-- those extra characters are the arrows you click to switch between styles. + + . += "Gradient
                    " + . += "Change Color [color_square(pref.r_grad, pref.g_grad, pref.b_grad)] " + . += " Style: < > [pref.grad_style]
                    " + + . += "
                    Facial
                    " + if(has_flag(mob_species, HAS_HAIR_COLOR)) + . += "Change Color [color_square(pref.r_facial, pref.g_facial, pref.b_facial)] " + . += " Style: < > [pref.f_style]
                    " //Same as above with the extra > & < characters + + if(has_flag(mob_species, HAS_EYE_COLOR)) + . += "
                    Eyes
                    " + . += "Change Color [color_square(pref.r_eyes, pref.g_eyes, pref.b_eyes)]
                    " + + if(has_flag(mob_species, HAS_SKIN_COLOR)) + . += "
                    Body Color
                    " + . += "Change Color [color_square(pref.r_skin, pref.g_skin, pref.b_skin)]
                    " + + if(mob_species.digi_allowed) + . += "
                    Digitigrade?: [pref.digitigrade ? "Yes" : "No"]
                    " + + . += "

                    Genetics Settings

                    " + + var/list/ear_styles = pref.get_available_styles(global.ear_styles_list) + var/datum/sprite_accessory/ears/ear = ear_styles[pref.ear_style] + . += "Ears
                    " + if(istype(ear)) + . += " Style: [ear.name]
                    " + if(ear.do_colouration) + . += "Change Color [color_square(pref.r_ears, pref.g_ears, pref.b_ears)]
                    " + if(ear.extra_overlay) + . += "Change Secondary Color [color_square(pref.r_ears2, pref.g_ears2, pref.b_ears2)]
                    " + if(ear.extra_overlay2) + . += "Change Tertiary Color [color_square(pref.r_ears3, pref.g_ears3, pref.b_ears3)]
                    " + else + . += " Style: Select
                    " + + var/list/tail_styles = pref.get_available_styles(global.tail_styles_list) + var/datum/sprite_accessory/tail/tail = tail_styles[pref.tail_style] + . += "Tail
                    " + if(istype(tail)) + . += " Style: [tail.name]
                    " + if(tail.do_colouration) + . += "Change Color [color_square(pref.r_tail, pref.g_tail, pref.b_tail)]
                    " + if(tail.extra_overlay) + . += "Change Secondary Color [color_square(pref.r_tail2, pref.g_tail2, pref.b_tail2)]
                    " + if(tail.extra_overlay2) + . += "Change Tertiary Color [color_square(pref.r_tail3, pref.g_tail3, pref.b_tail3)]
                    " + else + . += " Style: Select
                    " + + var/list/wing_styles = pref.get_available_styles(global.wing_styles_list) + var/datum/sprite_accessory/wing/wings = wing_styles[pref.wing_style] + . += "Wing
                    " + if(istype(wings)) + . += " Style: [wings.name]
                    " + if(wings.do_colouration) + . += "Change Color [color_square(pref.r_wing, pref.g_wing, pref.b_wing)]
                    " + if(wings.extra_overlay) + . += "Change Secondary Color [color_square(pref.r_wing2, pref.g_wing2, pref.b_wing2)]
                    " + if(wings.extra_overlay2) + . += "Change Secondary Color [color_square(pref.r_wing3, pref.g_wing3, pref.b_wing3)]
                    " + else + . += " Style: Select
                    " + + . += "
                    Body Markings +
                    " + . += "" + for(var/M in pref.body_markings) + . += "" + + . += "
                    [M][pref.body_markings.len > 1 ? "˄ ˅ mv " : ""]- Color[color_square(hex = pref.body_markings[M]["color"] ? pref.body_markings[M]["color"] : "#000000")] - Customize
                    " + . += "
                    " + . += "Allow Synth markings: [pref.synth_markings ? "Yes" : "No"]
                    " + . += "Allow Synth color: [pref.synth_color ? "Yes" : "No"]
                    " + if(pref.synth_color) + . += "Change Color [color_square(pref.r_synth, pref.g_synth, pref.b_synth)]" + + . = jointext(.,null) + +/datum/category_item/player_setup_item/general/body/proc/has_flag(var/datum/species/mob_species, var/flag) + return mob_species && (mob_species.appearance_flags & flag) + +/datum/category_item/player_setup_item/general/body/OnTopic(var/href,var/list/href_list, var/mob/user) + var/datum/species/mob_species = GLOB.all_species[pref.species] + + if(href_list["random"]) + pref.randomize_appearance_and_body_for() + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["change_descriptor"]) + if(mob_species.descriptors) + var/desc_id = href_list["change_descriptor"] + if(pref.body_descriptors[desc_id]) + var/datum/mob_descriptor/descriptor = mob_species.descriptors[desc_id] + var/choice = tgui_input_list(usr, "Please select a descriptor.", "Descriptor", descriptor.chargen_value_descriptors) + if(choice && mob_species.descriptors[desc_id]) // Check in case they sneakily changed species. + pref.body_descriptors[desc_id] = descriptor.chargen_value_descriptors[choice] + return TOPIC_REFRESH + + else if(href_list["blood_type"]) + var/new_b_type = tgui_input_list(user, "Choose your character's blood-type:", "Character Preference", valid_bloodtypes) + if(new_b_type && CanUseTopic(user)) + pref.b_type = new_b_type + return TOPIC_REFRESH + + else if(href_list["show_species"]) + // Actual whitelist checks are handled elsewhere, this is just for accessing the preview window. + var/choice = tgui_input_list(usr, "Which species would you like to look at?", "Species Choice", GLOB.playable_species) + if(!choice) return + pref.species_preview = choice + SetSpecies(preference_mob()) + pref.alternate_languages.Cut() // Reset their alternate languages. Todo: attempt to just fix it instead? + return TOPIC_HANDLED + + else if(href_list["set_species"]) + user << browse(null, "window=species") + if(!pref.species_preview || !(pref.species_preview in GLOB.all_species)) + return TOPIC_NOACTION + + var/datum/species/setting_species + + if(GLOB.all_species[href_list["set_species"]]) + setting_species = GLOB.all_species[href_list["set_species"]] + else + return TOPIC_NOACTION + + if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) //VOREStation Edit: selectability + return TOPIC_NOACTION + + var/prev_species = pref.species + pref.species = href_list["set_species"] + if(prev_species != pref.species) + if(!(pref.biological_gender in mob_species.genders)) + pref.set_biological_gender(mob_species.genders[1]) + pref.custom_species = null //VOREStation Edit - This is cleared on species changes + //grab one of the valid hair styles for the newly chosen species + var/list/valid_hairstyles = pref.get_valid_hairstyles() + + if(valid_hairstyles.len) + pref.h_style = pick(valid_hairstyles) + else + //this shouldn't happen + pref.h_style = hair_styles_list["Bald"] + + //grab one of the valid facial hair styles for the newly chosen species + var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() + + if(valid_facialhairstyles.len) + pref.f_style = pick(valid_facialhairstyles) + else + //this shouldn't happen + pref.f_style = facial_hair_styles_list["Shaved"] + + //reset hair colour and skin colour + pref.r_hair = 0//hex2num(copytext(new_hair, 2, 4)) + pref.g_hair = 0//hex2num(copytext(new_hair, 4, 6)) + pref.b_hair = 0//hex2num(copytext(new_hair, 6, 8)) + pref.s_tone = -75 + + reset_limbs() // Safety for species with incompatible manufacturers; easier than trying to do it case by case. + pref.body_markings.Cut() // Basically same as above. + + pref.sanitize_body_styles() + + var/min_age = get_min_age() + var/max_age = get_max_age() + pref.age = max(min(pref.age, max_age), min_age) + + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["hair_color"]) + if(!has_flag(mob_species, HAS_HAIR_COLOR)) + return TOPIC_NOACTION + var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference", rgb(pref.r_hair, pref.g_hair, pref.b_hair)) as color|null + if(new_hair && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) + pref.r_hair = hex2num(copytext(new_hair, 2, 4)) + pref.g_hair = hex2num(copytext(new_hair, 4, 6)) + pref.b_hair = hex2num(copytext(new_hair, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["grad_color"]) + if(!has_flag(mob_species, HAS_HAIR_COLOR)) + return TOPIC_NOACTION + var/new_grad = input(user, "Choose your character's secondary hair color:", "Character Preference", rgb(pref.r_grad, pref.g_grad, pref.b_grad)) as color|null + if(new_grad && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) + pref.r_grad = hex2num(copytext(new_grad, 2, 4)) + pref.g_grad = hex2num(copytext(new_grad, 4, 6)) + pref.b_grad = hex2num(copytext(new_grad, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["hair_style"]) + var/list/valid_hairstyles = pref.get_valid_hairstyles() + + var/new_h_style = tgui_input_list(user, "Choose your character's hair style:", "Character Preference", valid_hairstyles, pref.h_style) + if(new_h_style && CanUseTopic(user)) + pref.h_style = new_h_style + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["grad_style"]) + var/list/valid_gradients = GLOB.hair_gradients + + var/new_grad_style = tgui_input_list(user, "Choose a color pattern for your hair:", "Character Preference", valid_gradients, pref.grad_style) + if(new_grad_style && CanUseTopic(user)) + pref.grad_style = new_grad_style + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["hair_style_left"]) + var/H = href_list["hair_style_left"] + var/list/valid_hairstyles = pref.get_valid_hairstyles() + var/start = valid_hairstyles.Find(H) + + if(start != 1) //If we're not the beginning of the list, become the previous element. + pref.h_style = valid_hairstyles[start-1] + else //But if we ARE, become the final element. + pref.h_style = valid_hairstyles[valid_hairstyles.len] + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["hair_style_right"]) + var/H = href_list["hair_style_right"] + var/list/valid_hairstyles = pref.get_valid_hairstyles() + var/start = valid_hairstyles.Find(H) + + if(start != valid_hairstyles.len) //If we're not the end of the list, become the next element. + pref.h_style = valid_hairstyles[start+1] + else //But if we ARE, become the first element. + pref.h_style = valid_hairstyles[1] + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["facial_color"]) + if(!has_flag(mob_species, HAS_HAIR_COLOR)) + return TOPIC_NOACTION + var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", rgb(pref.r_facial, pref.g_facial, pref.b_facial)) as color|null + if(new_facial && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) + pref.r_facial = hex2num(copytext(new_facial, 2, 4)) + pref.g_facial = hex2num(copytext(new_facial, 4, 6)) + pref.b_facial = hex2num(copytext(new_facial, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + if(href_list["digitigrade"]) + pref.digitigrade = !pref.digitigrade + + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["eye_color"]) + if(!has_flag(mob_species, HAS_EYE_COLOR)) + return TOPIC_NOACTION + var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", rgb(pref.r_eyes, pref.g_eyes, pref.b_eyes)) as color|null + if(new_eyes && has_flag(mob_species, HAS_EYE_COLOR) && CanUseTopic(user)) + pref.r_eyes = hex2num(copytext(new_eyes, 2, 4)) + pref.g_eyes = hex2num(copytext(new_eyes, 4, 6)) + pref.b_eyes = hex2num(copytext(new_eyes, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["skin_tone"]) + if(!has_flag(mob_species, HAS_SKIN_TONE)) + return TOPIC_NOACTION + var/new_s_tone = tgui_input_number(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference", (-pref.s_tone) + 35, 220, 1) + if(new_s_tone && has_flag(mob_species, HAS_SKIN_TONE) && CanUseTopic(user)) + pref.s_tone = 35 - max(min( round(new_s_tone), 220),1) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["skin_color"]) + if(!has_flag(mob_species, HAS_SKIN_COLOR)) + return TOPIC_NOACTION + var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", rgb(pref.r_skin, pref.g_skin, pref.b_skin)) as color|null + if(new_skin && has_flag(mob_species, HAS_SKIN_COLOR) && CanUseTopic(user)) + pref.r_skin = hex2num(copytext(new_skin, 2, 4)) + pref.g_skin = hex2num(copytext(new_skin, 4, 6)) + pref.b_skin = hex2num(copytext(new_skin, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["facial_style"]) + var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() + + var/new_f_style = tgui_input_list(user, "Choose your character's facial-hair style:", "Character Preference", valid_facialhairstyles, pref.f_style) + if(new_f_style && CanUseTopic(user)) + pref.f_style = new_f_style + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["facial_style_left"]) + var/F = href_list["facial_style_left"] + var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() + var/start = valid_facialhairstyles.Find(F) + + if(start != 1) //If we're not the beginning of the list, become the previous element. + pref.f_style = valid_facialhairstyles[start-1] + else //But if we ARE, become the final element. + pref.f_style = valid_facialhairstyles[valid_facialhairstyles.len] + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["facial_style_right"]) + var/F = href_list["facial_style_right"] + var/list/valid_facialhairstyles = pref.get_valid_facialhairstyles() + var/start = valid_facialhairstyles.Find(F) + + if(start != valid_facialhairstyles.len) //If we're not the end of the list, become the next element. + pref.f_style = valid_facialhairstyles[start+1] + else //But if we ARE, become the first element. + pref.f_style = valid_facialhairstyles[1] + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["marking_style"]) + var/list/usable_markings = pref.body_markings.Copy() ^ body_marking_styles_list.Copy() + /* VOREStation Removal - No markings whitelist, let people mix/match + for(var/M in usable_markings) + var/datum/sprite_accessory/S = usable_markings[M] + var/datum/species/spec = GLOB.all_species[pref.species] + if(!S.species_allowed.len) + continue + else if(!(pref.species in S.species_allowed) && !(pref.custom_base in S.species_allowed) && !(spec.base_species in S.species_allowed)) + usable_markings -= M + */ //VOREStation Removal End + var/new_marking = tgui_input_list(user, "Choose a body marking:", "Character Preference", usable_markings) + if(new_marking && CanUseTopic(user)) + pref.body_markings[new_marking] = pref.mass_edit_marking_list(new_marking) //New markings start black + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["marking_up"]) + var/M = href_list["marking_up"] + var/start = pref.body_markings.Find(M) + if(start != 1) //If we're not the beginning of the list, swap with the previous element. + moveElement(pref.body_markings, start, start-1) + else //But if we ARE, become the final element -ahead- of everything else. + moveElement(pref.body_markings, start, pref.body_markings.len+1) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["marking_down"]) + var/M = href_list["marking_down"] + var/start = pref.body_markings.Find(M) + if(start != pref.body_markings.len) //If we're not the end of the list, swap with the next element. + moveElement(pref.body_markings, start, start+2) + else //But if we ARE, become the first element -behind- everything else. + moveElement(pref.body_markings, start, 1) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["marking_move"]) + var/M = href_list["marking_move"] + var/start = pref.body_markings.Find(M) + var/list/move_locs = pref.body_markings - M + if(start != 1) + move_locs -= pref.body_markings[start-1] + + var/inject_after = tgui_input_list(user, "Move [M] ahead of...", "Character Preference", move_locs) //Move ahead of any marking that isn't the current or previous one. + var/newpos = pref.body_markings.Find(inject_after) + if(newpos) + moveElement(pref.body_markings, start, newpos+1) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["marking_remove"]) + var/M = href_list["marking_remove"] + winshow(user, "prefs_markings_subwindow", FALSE) + pref.body_markings -= M + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["marking_color"]) + var/M = href_list["marking_color"] + if (isnull(pref.body_markings[M]["color"])) + if (tgui_alert(user, "You currently have customized marking colors. This will reset each bodypart's color. Are you sure you want to continue?","Reset Bodypart Colors",list("Yes","No")) == "No") + return TOPIC_NOACTION + var/mark_color = input(user, "Choose the [M] color: ", "Character Preference", pref.body_markings[M]["color"]) as color|null + if(mark_color && CanUseTopic(user)) + pref.body_markings[M] = pref.mass_edit_marking_list(M,FALSE,TRUE,pref.body_markings[M],color="[mark_color]") + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if (href_list["marking_submenu"]) + var/M = href_list["marking_submenu"] + markings_subwindow(user, M) + return TOPIC_NOACTION + + else if (href_list["toggle_all_marking_selection"]) + var/toggle = text2num(href_list["toggle"]) + var/marking = href_list["toggle_all_marking_selection"] + if (pref.body_markings.Find(marking) == 0) + winshow(user, "prefs_markings_subwindow", FALSE) + return TOPIC_NOACTION + pref.body_markings[marking] = pref.mass_edit_marking_list(marking,TRUE,FALSE,pref.body_markings[marking],on=toggle) + markings_subwindow(user, marking) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if (href_list["color_all_marking_selection"]) + var/marking = href_list["color_all_marking_selection"] + if (pref.body_markings.Find(marking) == 0) + winshow(user, "prefs_markings_subwindow", FALSE) + return TOPIC_NOACTION + var/mark_color = input(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking]["color"]) as color|null + if(mark_color && CanUseTopic(user)) + pref.body_markings[marking] = pref.mass_edit_marking_list(marking,FALSE,TRUE,pref.body_markings[marking],color="[mark_color]") + markings_subwindow(user, marking) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if (href_list["zone_marking_color"]) + var/marking = href_list["zone_marking_color"] + if (pref.body_markings.Find(marking) == 0) + winshow(user, "prefs_markings_subwindow", FALSE) + return TOPIC_NOACTION + var/zone = href_list["zone"] + pref.body_markings[marking]["color"] = null //turn off the color button outside the submenu + var/mark_color = input(user, "Choose the [marking] color: ", "Character Preference", pref.body_markings[marking][zone]["color"]) as color|null + if(mark_color && CanUseTopic(user)) + pref.body_markings[marking][zone]["color"] = "[mark_color]" + markings_subwindow(user, marking) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if (href_list["zone_marking_toggle"]) + var/marking = href_list["zone_marking_toggle"] + if (pref.body_markings.Find(marking) == 0) + winshow(user, "prefs_markings_subwindow", FALSE) + return TOPIC_NOACTION + var/zone = href_list["zone"] + pref.body_markings[marking][zone]["on"] = text2num(href_list["toggle"]) + markings_subwindow(user, marking) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["reset_limbs"]) + reset_limbs() + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["limbs"]) + + var/list/limb_selection_list = list("Left Leg","Right Leg","Left Arm","Right Arm","Left Foot","Right Foot","Left Hand","Right Hand","Full Body") + + // Full prosthetic bodies without a brain are borderline unkillable so make sure they have a brain to remove/destroy. + var/datum/species/current_species = GLOB.all_species[pref.species] + if(!current_species.has_organ["brain"]) + limb_selection_list -= "Full Body" + else if(pref.organ_data[BP_TORSO] == "cyborg") + limb_selection_list |= "Head" + + var/organ_tag = tgui_input_list(user, "Which limb do you want to change?", "Limb Choice", limb_selection_list) + + if(!organ_tag || !CanUseTopic(user)) return TOPIC_NOACTION + + var/limb = null + var/second_limb = null // if you try to change the arm, the hand should also change + var/third_limb = null // if you try to unchange the hand, the arm should also change + + // Do not let them amputate their entire body, ty. + var/list/choice_options = list("Normal","Amputated","Prosthesis") + switch(organ_tag) + if("Left Leg") + limb = BP_L_LEG + second_limb = BP_L_FOOT + if("Right Leg") + limb = BP_R_LEG + second_limb = BP_R_FOOT + if("Left Arm") + limb = BP_L_ARM + second_limb = BP_L_HAND + if("Right Arm") + limb = BP_R_ARM + second_limb = BP_R_HAND + if("Left Foot") + limb = BP_L_FOOT + third_limb = BP_L_LEG + if("Right Foot") + limb = BP_R_FOOT + third_limb = BP_R_LEG + if("Left Hand") + limb = BP_L_HAND + third_limb = BP_L_ARM + if("Right Hand") + limb = BP_R_HAND + third_limb = BP_R_ARM + if("Head") + limb = BP_HEAD + choice_options = list("Prosthesis") + if("Full Body") + limb = BP_TORSO + second_limb = BP_HEAD + third_limb = BP_GROIN + choice_options = list("Normal","Prosthesis") + + var/new_state = tgui_input_list(user, "What state do you wish the limb to be in?", "State Choice", choice_options) + if(!new_state || !CanUseTopic(user)) return TOPIC_NOACTION + + switch(new_state) + if("Normal") + pref.organ_data[limb] = null + pref.rlimb_data[limb] = null + if(limb == BP_TORSO) + for(var/other_limb in BP_ALL - BP_TORSO) + pref.organ_data[other_limb] = null + pref.rlimb_data[other_limb] = null + for(var/internal in O_STANDARD) + pref.organ_data[internal] = null + pref.rlimb_data[internal] = null + if(third_limb) + pref.organ_data[third_limb] = null + pref.rlimb_data[third_limb] = null + + if("Amputated") + if(limb == BP_TORSO) + return + pref.organ_data[limb] = "amputated" + pref.rlimb_data[limb] = null + if(second_limb) + pref.organ_data[second_limb] = "amputated" + pref.rlimb_data[second_limb] = null + + if("Prosthesis") + var/tmp_species = pref.species ? pref.species : SPECIES_HUMAN + var/list/usable_manufacturers = list() + for(var/company in chargen_robolimbs) + var/datum/robolimb/M = chargen_robolimbs[company] + if(!(limb in M.parts)) + continue + if(tmp_species in M.species_cannot_use) + continue + //VOREStation Add - Cyberlimb whitelisting. + if(M.whitelisted_to && !(user.ckey in M.whitelisted_to)) + continue + //VOREStation Add End + usable_manufacturers[company] = M + if(!usable_manufacturers.len) + return + var/choice = tgui_input_list(user, "Which manufacturer do you wish to use for this limb?", "Manufacturer Choice", usable_manufacturers) + if(!choice) + return + + pref.rlimb_data[limb] = choice + pref.organ_data[limb] = "cyborg" + + if(second_limb) + pref.rlimb_data[second_limb] = choice + pref.organ_data[second_limb] = "cyborg" + if(third_limb && pref.organ_data[third_limb] == "amputated") + pref.organ_data[third_limb] = null + + if(limb == BP_TORSO) + for(var/other_limb in BP_ALL - BP_TORSO) + if(pref.organ_data[other_limb]) + continue + pref.organ_data[other_limb] = "cyborg" + pref.rlimb_data[other_limb] = choice + if(!pref.organ_data[O_BRAIN]) + pref.organ_data[O_BRAIN] = "assisted" + for(var/internal_organ in list(O_HEART,O_EYES)) + pref.organ_data[internal_organ] = "mechanical" + + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["organs"]) + + var/organ_name = tgui_input_list(user, "Which internal function do you want to change?", "Internal Organ", list("Heart", "Eyes", "Larynx", "Lungs", "Liver", "Kidneys", "Spleen", "Intestines", "Stomach", "Brain")) + if(!organ_name) return + + var/organ = null + switch(organ_name) + if("Heart") + organ = O_HEART + if("Eyes") + organ = O_EYES + if("Larynx") + organ = O_VOICE + if("Lungs") + organ = O_LUNGS + if("Liver") + organ = O_LIVER + if("Kidneys") + organ = O_KIDNEYS + if("Spleen") + organ = O_SPLEEN + if("Intestines") + organ = O_INTESTINE + if("Stomach") + organ = O_STOMACH + if("Brain") + if(pref.organ_data[BP_HEAD] != "cyborg") + to_chat(user, "You may only select a cybernetic or synthetic brain if you have a full prosthetic body.") + return + organ = "brain" + + var/datum/species/current_species = GLOB.all_species[pref.species] + var/list/organ_choices = list("Normal") + if(pref.organ_data[BP_TORSO] == "cyborg") + organ_choices -= "Normal" + if(organ_name == "Brain") + organ_choices += "Cybernetic" + if(!(current_species.spawn_flags & SPECIES_NO_POSIBRAIN)) + organ_choices += "Positronic" + if(!(current_species.spawn_flags & SPECIES_NO_DRONEBRAIN)) + organ_choices += "Drone" + else + organ_choices += "Assisted" + organ_choices += "Mechanical" + else + organ_choices += "Assisted" + organ_choices += "Mechanical" + + var/new_state = tgui_input_list(user, "What state do you wish the organ to be in?", "State Choice", organ_choices) + if(!new_state) return + + switch(new_state) + if("Normal") + pref.organ_data[organ] = null + if("Assisted") + pref.organ_data[organ] = "assisted" + if("Cybernetic") + pref.organ_data[organ] = "assisted" + if("Mechanical") + pref.organ_data[organ] = "mechanical" + if("Drone") + pref.organ_data[organ] = "digital" + if("Positronic") + pref.organ_data[organ] = "mechanical" + + return TOPIC_REFRESH + + else if(href_list["disabilities"]) + var/disability_flag = text2num(href_list["disabilities"]) + pref.disabilities ^= disability_flag + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["toggle_preview_value"]) + pref.equip_preview_mob ^= text2num(href_list["toggle_preview_value"]) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["toggle_animations"]) + pref.animations_toggle = !pref.animations_toggle + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["synth_color"]) + pref.synth_color = !pref.synth_color + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["synth2_color"]) + var/new_color = input(user, "Choose your character's synth colour: ", "Character Preference", rgb(pref.r_synth, pref.g_synth, pref.b_synth)) as color|null + if(new_color && CanUseTopic(user)) + pref.r_synth = hex2num(copytext(new_color, 2, 4)) + pref.g_synth = hex2num(copytext(new_color, 4, 6)) + pref.b_synth = hex2num(copytext(new_color, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["synth_markings"]) + pref.synth_markings = !pref.synth_markings + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["cycle_bg"]) + pref.bgstate = next_in_list(pref.bgstate, pref.bgstate_options) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["ear_style"]) + var/new_ear_style = tgui_input_list(user, "Select an ear style for this character:", "Character Preference", pref.get_available_styles(global.ear_styles_list), pref.ear_style) + if(new_ear_style) + pref.ear_style = new_ear_style + + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["ear_color"]) + var/new_earc = input(user, "Choose your character's ear colour:", "Character Preference", + rgb(pref.r_ears, pref.g_ears, pref.b_ears)) as color|null + if(new_earc) + pref.r_ears = hex2num(copytext(new_earc, 2, 4)) + pref.g_ears = hex2num(copytext(new_earc, 4, 6)) + pref.b_ears = hex2num(copytext(new_earc, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["ear_color2"]) + var/new_earc2 = input(user, "Choose your character's ear colour:", "Character Preference", + rgb(pref.r_ears2, pref.g_ears2, pref.b_ears2)) as color|null + if(new_earc2) + pref.r_ears2 = hex2num(copytext(new_earc2, 2, 4)) + pref.g_ears2 = hex2num(copytext(new_earc2, 4, 6)) + pref.b_ears2 = hex2num(copytext(new_earc2, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["ear_color3"]) + var/new_earc3 = input(user, "Choose your character's tertiary ear colour:", "Character Preference", + rgb(pref.r_ears3, pref.g_ears3, pref.b_ears3)) as color|null + if(new_earc3) + pref.r_ears3 = hex2num(copytext(new_earc3, 2, 4)) + pref.g_ears3 = hex2num(copytext(new_earc3, 4, 6)) + pref.b_ears3 = hex2num(copytext(new_earc3, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["tail_style"]) + var/new_tail_style = tgui_input_list(user, "Select a tail style for this character:", "Character Preference", pref.get_available_styles(global.tail_styles_list), pref.tail_style) + if(new_tail_style) + pref.tail_style = new_tail_style + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["tail_color"]) + var/new_tailc = input(user, "Choose your character's tail/taur colour:", "Character Preference", + rgb(pref.r_tail, pref.g_tail, pref.b_tail)) as color|null + if(new_tailc) + pref.r_tail = hex2num(copytext(new_tailc, 2, 4)) + pref.g_tail = hex2num(copytext(new_tailc, 4, 6)) + pref.b_tail = hex2num(copytext(new_tailc, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["tail_color2"]) + var/new_tailc2 = input(user, "Choose your character's secondary tail/taur colour:", "Character Preference", + rgb(pref.r_tail2, pref.g_tail2, pref.b_tail2)) as color|null + if(new_tailc2) + pref.r_tail2 = hex2num(copytext(new_tailc2, 2, 4)) + pref.g_tail2 = hex2num(copytext(new_tailc2, 4, 6)) + pref.b_tail2 = hex2num(copytext(new_tailc2, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["tail_color3"]) + var/new_tailc3 = input(user, "Choose your character's tertiary tail/taur colour:", "Character Preference", + rgb(pref.r_tail3, pref.g_tail3, pref.b_tail3)) as color|null + if(new_tailc3) + pref.r_tail3 = hex2num(copytext(new_tailc3, 2, 4)) + pref.g_tail3 = hex2num(copytext(new_tailc3, 4, 6)) + pref.b_tail3 = hex2num(copytext(new_tailc3, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["wing_style"]) + var/new_wing_style = tgui_input_list(user, "Select a wing style for this character:", "Character Preference", pref.get_available_styles(global.wing_styles_list), pref.wing_style) + if(new_wing_style) + pref.wing_style = new_wing_style + + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["wing_color"]) + var/new_wingc = input(user, "Choose your character's wing colour:", "Character Preference", + rgb(pref.r_wing, pref.g_wing, pref.b_wing)) as color|null + if(new_wingc) + pref.r_wing = hex2num(copytext(new_wingc, 2, 4)) + pref.g_wing = hex2num(copytext(new_wingc, 4, 6)) + pref.b_wing = hex2num(copytext(new_wingc, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["wing_color2"]) + var/new_wingc2 = input(user, "Choose your character's secondary wing colour:", "Character Preference", + rgb(pref.r_wing2, pref.g_wing2, pref.b_wing2)) as color|null + if(new_wingc2) + pref.r_wing2 = hex2num(copytext(new_wingc2, 2, 4)) + pref.g_wing2 = hex2num(copytext(new_wingc2, 4, 6)) + pref.b_wing2 = hex2num(copytext(new_wingc2, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["wing_color3"]) + var/new_wingc3 = input(user, "Choose your character's tertiary wing colour:", "Character Preference", + rgb(pref.r_wing3, pref.g_wing3, pref.b_wing3)) as color|null + if(new_wingc3) + pref.r_wing3 = hex2num(copytext(new_wingc3, 2, 4)) + pref.g_wing3 = hex2num(copytext(new_wingc3, 4, 6)) + pref.b_wing3 = hex2num(copytext(new_wingc3, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + + return ..() + +/datum/category_item/player_setup_item/general/body/proc/reset_limbs() + + for(var/organ in pref.organ_data) + pref.organ_data[organ] = null + while(null in pref.organ_data) + pref.organ_data -= null + + for(var/organ in pref.rlimb_data) + pref.rlimb_data[organ] = null + while(null in pref.rlimb_data) + pref.rlimb_data -= null + + // Sanitize the name so that there aren't any numbers sticking around. + pref.real_name = sanitize_name(pref.real_name, pref.species) + if(!pref.real_name) + pref.real_name = random_name(pref.identifying_gender, pref.species) + +/datum/category_item/player_setup_item/general/body/proc/SetSpecies(mob/user) + if(!pref.species_preview || !(pref.species_preview in GLOB.all_species)) + pref.species_preview = SPECIES_HUMAN + var/datum/species/current_species = GLOB.all_species[pref.species_preview] + var/dat = "" + dat += "

                    [current_species.name] \[change\]


                    " + dat += "" + dat += "" + //vorestation edit begin + if(current_species.wikilink) + dat += "" + else + dat += "" + //vorestation edit end + dat += "" + dat += "" + dat += "
                    [current_species.blurb]

                    See the wiki for more details.
                    [current_species.blurb]" + if("preview" in cached_icon_states(current_species.icobase)) + usr << browse_rsc(icon(current_species.icobase,"preview"), "species_preview_[current_species.name].png") + dat += "

                    " + dat += "Language: [current_species.species_language]
                    " + dat += "" + if(current_species.spawn_flags & SPECIES_CAN_JOIN) + switch(current_species.rarity_value) + if(1 to 2) + dat += "
                    Often present on human stations." + if(3 to 4) + dat += "
                    Rarely present on human stations." + if(5) + dat += "
                    Unheard of on human stations." + else + dat += "
                    May be present on human stations." + if(current_species.spawn_flags & SPECIES_IS_WHITELISTED) + dat += "
                    Whitelist restricted." + if(!current_species.has_organ[O_HEART]) + dat += "
                    Does not have a circulatory system." + if(!current_species.has_organ[O_LUNGS]) + dat += "
                    Does not have a respiratory system." + if(current_species.flags & NO_SCAN) + dat += "
                    Does not have DNA." + if(current_species.flags & NO_DEFIB) + dat += "
                    Cannot be defibrillated." + if(current_species.flags & NO_PAIN) + dat += "
                    Does not feel pain." + if(current_species.flags & NO_SLIP) + dat += "
                    Has excellent traction." + if(current_species.flags & NO_POISON) + dat += "
                    Immune to most poisons." + if(current_species.appearance_flags & HAS_SKIN_TONE) + dat += "
                    Has a variety of skin tones." + if(current_species.appearance_flags & HAS_SKIN_COLOR) + dat += "
                    Has a variety of skin colours." + if(current_species.appearance_flags & HAS_EYE_COLOR) + dat += "
                    Has a variety of eye colours." + if(current_species.flags & IS_PLANT) + dat += "
                    Has a plantlike physiology." + dat += "

                    " + + var/restricted = 0 + + if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) + restricted = 2 + else if(!is_alien_whitelisted(preference_mob(),current_species)) + restricted = 1 + + if(restricted) + if(restricted == 1) + dat += "You cannot play as this species.
                    If you wish to be whitelisted, you can make an application post on the forums.

                    " + else if(restricted == 2) + dat += "You cannot play as this species.
                    This species is not available for play as a station race..

                    " + if(!restricted || check_rights(R_ADMIN|R_EVENT, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) //VOREStation Edit: selectability + dat += "\[select\]" + dat += "
                    " + + user << browse(dat, "window=species;size=700x400") + +/datum/category_item/player_setup_item/general/body/proc/markings_subwindow(mob/user, marking) + var/static/list/part_to_string = list(BP_HEAD = "Head", BP_TORSO = "Upper Body", BP_GROIN = "Lower Body", BP_R_ARM = "Right Arm", BP_L_ARM = "Left Arm", BP_R_HAND = "Right Hand", BP_L_HAND = "Left Hand", BP_R_LEG = "Right Leg", BP_L_LEG = "Left Leg", BP_R_FOOT = "Right Foot", BP_L_FOOT = "Left Foot") + var/dat = "

                    Editing '[marking]'


                    " + dat += "Enable All " + dat += "Disable All " + dat += "Change Color of All
                    " + dat += "
                    " + for (var/bodypart in pref.body_markings[marking]) + if (!islist(pref.body_markings[marking][bodypart])) continue + dat += "[part_to_string[bodypart]]: [color_square(hex = pref.body_markings[marking][bodypart]["color"])] " + dat += "Change " + dat += "[pref.body_markings[marking][bodypart]["on"] ? "Toggle Off" : "Toggle On"]
                    " + + dat += "" + winshow(user, "prefs_markings_subwindow", TRUE) + pref.markings_subwindow = new(user, "prefs_markings_browser", "Marking Editor", 400, 400) + pref.markings_subwindow.set_content(dat) + pref.markings_subwindow.open(FALSE) + onclose(user, "prefs_markings_subwindow", src) diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm index 010e587fc82..be0cc95a40a 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -1,151 +1,192 @@ -/datum/preferences - var/list/all_underwear - var/list/all_underwear_metadata - -/datum/category_item/player_setup_item/general/equipment - name = "Clothing" - sort_order = 4 - -/datum/category_item/player_setup_item/general/equipment/load_character(var/savefile/S) - S["all_underwear"] >> pref.all_underwear - S["all_underwear_metadata"] >> pref.all_underwear_metadata - S["backbag"] >> pref.backbag - S["pdachoice"] >> pref.pdachoice - S["communicator_visibility"] >> pref.communicator_visibility - -/datum/category_item/player_setup_item/general/equipment/save_character(var/savefile/S) - S["all_underwear"] << pref.all_underwear - S["all_underwear_metadata"] << pref.all_underwear_metadata - S["backbag"] << pref.backbag - S["pdachoice"] << pref.pdachoice - S["communicator_visibility"] << pref.communicator_visibility - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/equipment/copy_to_mob(var/mob/living/carbon/human/character) - character.all_underwear.Cut() - character.all_underwear_metadata.Cut() - - for(var/underwear_category_name in pref.all_underwear) - var/datum/category_group/underwear/underwear_category = global_underwear.categories_by_name[underwear_category_name] - if(underwear_category) - var/underwear_item_name = pref.all_underwear[underwear_category_name] - character.all_underwear[underwear_category_name] = underwear_category.items_by_name[underwear_item_name] - if(pref.all_underwear_metadata[underwear_category_name]) - character.all_underwear_metadata[underwear_category_name] = pref.all_underwear_metadata[underwear_category_name] - else - pref.all_underwear -= underwear_category_name - - // TODO - Looks like this is duplicating the work of sanitize_character() if so, remove - if(pref.backbag > backbaglist.len || pref.backbag < 1) - pref.backbag = 2 //Same as above - character.backbag = pref.backbag - - if(pref.pdachoice > 7 || pref.pdachoice < 1) - pref.pdachoice = 1 - character.pdachoice = pref.pdachoice - -/datum/category_item/player_setup_item/general/equipment/sanitize_character() - if(!islist(pref.gear)) pref.gear = list() - - if(!istype(pref.all_underwear)) - pref.all_underwear = list() - - for(var/datum/category_group/underwear/WRC in global_underwear.categories) - for(var/datum/category_item/underwear/WRI in WRC.items) - if(WRI.is_default(pref.identifying_gender ? pref.identifying_gender : MALE)) - pref.all_underwear[WRC.name] = WRI.name - break - - if(!istype(pref.all_underwear_metadata)) - pref.all_underwear_metadata = list() - - for(var/underwear_category in pref.all_underwear) - var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[underwear_category] - if(!UWC) - pref.all_underwear -= underwear_category - else - var/datum/category_item/underwear/UWI = UWC.items_by_name[pref.all_underwear[underwear_category]] - if(!UWI) - pref.all_underwear -= underwear_category - - for(var/underwear_metadata in pref.all_underwear_metadata) - if(!(underwear_metadata in pref.all_underwear)) - pref.all_underwear_metadata -= underwear_metadata - pref.backbag = sanitize_integer(pref.backbag, 1, backbaglist.len, initial(pref.backbag)) - pref.pdachoice = sanitize_integer(pref.pdachoice, 1, pdachoicelist.len, initial(pref.pdachoice)) - -/datum/category_item/player_setup_item/general/equipment/content() - . = list() - . += "Equipment:
                    " - for(var/datum/category_group/underwear/UWC in global_underwear.categories) - var/item_name = pref.all_underwear[UWC.name] ? pref.all_underwear[UWC.name] : "None" - . += "[UWC.name]: [item_name]" - var/datum/category_item/underwear/UWI = UWC.items_by_name[item_name] - if(UWI) - for(var/datum/gear_tweak/gt in UWI.tweaks) - . += " [gt.get_contents(get_metadata(UWC.name, gt))]" - - . += "
                    " - . += "Backpack Type: [backbaglist[pref.backbag]]
                    " - . += "PDA Type: [pdachoicelist[pref.pdachoice]]
                    " - . += "Communicator Visibility: [(pref.communicator_visibility) ? "Yes" : "No"]
                    " - - return jointext(.,null) - -/datum/category_item/player_setup_item/general/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt) - var/metadata = pref.all_underwear_metadata[underwear_category] - if(!metadata) - metadata = list() - pref.all_underwear_metadata[underwear_category] = metadata - - var/tweak_data = metadata["[gt]"] - if(!tweak_data) - tweak_data = gt.get_default() - metadata["[gt]"] = tweak_data - return tweak_data - -/datum/category_item/player_setup_item/general/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata) - var/list/metadata = pref.all_underwear_metadata[underwear_category] - metadata["[gt]"] = new_metadata - - -/datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["change_backpack"]) - var/new_backbag = tgui_input_list(user, "Choose your character's style of bag:", "Character Preference", backbaglist, backbaglist[pref.backbag]) - if(!isnull(new_backbag) && CanUseTopic(user)) - pref.backbag = backbaglist.Find(new_backbag) - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["change_pda"]) - var/new_pdachoice = tgui_input_list(user, "Choose your character's style of PDA:", "Character Preference", pdachoicelist, pdachoicelist[pref.pdachoice]) - if(!isnull(new_pdachoice) && CanUseTopic(user)) - pref.pdachoice = pdachoicelist.Find(new_pdachoice) - return TOPIC_REFRESH - - else if(href_list["change_underwear"]) - var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]] - if(!UWC) - return - var/datum/category_item/underwear/selected_underwear = tgui_input_list(user, "Choose underwear:", "Character Preference", UWC.items, pref.all_underwear[UWC.name]) - if(selected_underwear && CanUseTopic(user)) - pref.all_underwear[UWC.name] = selected_underwear.name - return TOPIC_REFRESH_UPDATE_PREVIEW - - else if(href_list["underwear"] && href_list["tweak"]) - var/underwear = href_list["underwear"] - if(!(underwear in pref.all_underwear)) - return TOPIC_NOACTION - var/datum/gear_tweak/gt = locate(href_list["tweak"]) - if(!gt) - return TOPIC_NOACTION - var/new_metadata = gt.get_metadata(usr, get_metadata(underwear, gt)) - if(new_metadata) - set_metadata(underwear, gt, new_metadata) - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["toggle_comm_visibility"]) - if(CanUseTopic(user)) - pref.communicator_visibility = !pref.communicator_visibility - return TOPIC_REFRESH - - - return ..() +/datum/preferences + var/list/all_underwear + var/list/all_underwear_metadata + +/datum/category_item/player_setup_item/general/equipment + name = "Clothing" + sort_order = 4 + +/datum/category_item/player_setup_item/general/equipment/load_character(var/savefile/S) + S["all_underwear"] >> pref.all_underwear + S["all_underwear_metadata"] >> pref.all_underwear_metadata + S["backbag"] >> pref.backbag + S["pdachoice"] >> pref.pdachoice + S["communicator_visibility"] >> pref.communicator_visibility + S["ringtone"] >> pref.ringtone + +/datum/category_item/player_setup_item/general/equipment/save_character(var/savefile/S) + S["all_underwear"] << pref.all_underwear + S["all_underwear_metadata"] << pref.all_underwear_metadata + S["backbag"] << pref.backbag + S["pdachoice"] << pref.pdachoice + S["communicator_visibility"] << pref.communicator_visibility + S["ringtone"] << pref.ringtone + +var/global/list/valid_ringtones = list( + "beep", + "boom", + "slip", + "honk", + "SKREE", + "xeno", + "spark", + "rad", + "servo", + "buh-boop", + "trombone", + "whistle", + "chirp", + "slurp", + "pwing", + "clack", + "bzzt", + "chimes", + "prbt", + "bark", + "bork", + "roark", + "chitter", + "squish" + ) + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/equipment/copy_to_mob(var/mob/living/carbon/human/character) + character.all_underwear.Cut() + character.all_underwear_metadata.Cut() + + for(var/underwear_category_name in pref.all_underwear) + var/datum/category_group/underwear/underwear_category = global_underwear.categories_by_name[underwear_category_name] + if(underwear_category) + var/underwear_item_name = pref.all_underwear[underwear_category_name] + character.all_underwear[underwear_category_name] = underwear_category.items_by_name[underwear_item_name] + if(pref.all_underwear_metadata[underwear_category_name]) + character.all_underwear_metadata[underwear_category_name] = pref.all_underwear_metadata[underwear_category_name] + else + pref.all_underwear -= underwear_category_name + + // TODO - Looks like this is duplicating the work of sanitize_character() if so, remove + if(pref.backbag > backbaglist.len || pref.backbag < 1) + pref.backbag = 2 //Same as above + character.backbag = pref.backbag + + if(pref.pdachoice > 8 || pref.pdachoice < 1) + pref.pdachoice = 1 + character.pdachoice = pref.pdachoice + +/datum/category_item/player_setup_item/general/equipment/sanitize_character() + if(!islist(pref.gear)) pref.gear = list() + + if(!istype(pref.all_underwear)) + pref.all_underwear = list() + + for(var/datum/category_group/underwear/WRC in global_underwear.categories) + for(var/datum/category_item/underwear/WRI in WRC.items) + if(WRI.is_default(pref.identifying_gender ? pref.identifying_gender : MALE)) + pref.all_underwear[WRC.name] = WRI.name + break + + if(!istype(pref.all_underwear_metadata)) + pref.all_underwear_metadata = list() + + for(var/underwear_category in pref.all_underwear) + var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[underwear_category] + if(!UWC) + pref.all_underwear -= underwear_category + else + var/datum/category_item/underwear/UWI = UWC.items_by_name[pref.all_underwear[underwear_category]] + if(!UWI) + pref.all_underwear -= underwear_category + + for(var/underwear_metadata in pref.all_underwear_metadata) + if(!(underwear_metadata in pref.all_underwear)) + pref.all_underwear_metadata -= underwear_metadata + pref.backbag = sanitize_integer(pref.backbag, 1, backbaglist.len, initial(pref.backbag)) + pref.pdachoice = sanitize_integer(pref.pdachoice, 1, pdachoicelist.len, initial(pref.pdachoice)) + pref.ringtone = sanitize(pref.ringtone, 20) + +/datum/category_item/player_setup_item/general/equipment/content() + . = list() + . += "Equipment:
                    " + for(var/datum/category_group/underwear/UWC in global_underwear.categories) + var/item_name = pref.all_underwear[UWC.name] ? pref.all_underwear[UWC.name] : "None" + . += "[UWC.name]: [item_name]" + var/datum/category_item/underwear/UWI = UWC.items_by_name[item_name] + if(UWI) + for(var/datum/gear_tweak/gt in UWI.tweaks) + . += " [gt.get_contents(get_metadata(UWC.name, gt))]" + + . += "
                    " + . += "Backpack Type: [backbaglist[pref.backbag]]
                    " + . += "PDA Type: [pdachoicelist[pref.pdachoice]]
                    " + . += "Communicator Visibility: [(pref.communicator_visibility) ? "Yes" : "No"]
                    " + . += "Ringtone (leave blank for job default): [pref.ringtone]
                    " + + return jointext(.,null) + +/datum/category_item/player_setup_item/general/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt) + var/metadata = pref.all_underwear_metadata[underwear_category] + if(!metadata) + metadata = list() + pref.all_underwear_metadata[underwear_category] = metadata + + var/tweak_data = metadata["[gt]"] + if(!tweak_data) + tweak_data = gt.get_default() + metadata["[gt]"] = tweak_data + return tweak_data + +/datum/category_item/player_setup_item/general/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata) + var/list/metadata = pref.all_underwear_metadata[underwear_category] + metadata["[gt]"] = new_metadata + + +/datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user) + if(href_list["change_backpack"]) + var/new_backbag = tgui_input_list(user, "Choose your character's style of bag:", "Character Preference", backbaglist, backbaglist[pref.backbag]) + if(!isnull(new_backbag) && CanUseTopic(user)) + pref.backbag = backbaglist.Find(new_backbag) + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["change_pda"]) + var/new_pdachoice = tgui_input_list(user, "Choose your character's style of PDA:", "Character Preference", pdachoicelist, pdachoicelist[pref.pdachoice]) + if(!isnull(new_pdachoice) && CanUseTopic(user)) + pref.pdachoice = pdachoicelist.Find(new_pdachoice) + return TOPIC_REFRESH + + else if(href_list["change_underwear"]) + var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]] + if(!UWC) + return + var/datum/category_item/underwear/selected_underwear = tgui_input_list(user, "Choose underwear:", "Character Preference", UWC.items, pref.all_underwear[UWC.name]) + if(selected_underwear && CanUseTopic(user)) + pref.all_underwear[UWC.name] = selected_underwear.name + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["underwear"] && href_list["tweak"]) + var/underwear = href_list["underwear"] + if(!(underwear in pref.all_underwear)) + return TOPIC_NOACTION + var/datum/gear_tweak/gt = locate(href_list["tweak"]) + if(!gt) + return TOPIC_NOACTION + var/new_metadata = gt.get_metadata(usr, get_metadata(underwear, gt)) + if(new_metadata) + set_metadata(underwear, gt, new_metadata) + return TOPIC_REFRESH_UPDATE_PREVIEW + else if(href_list["toggle_comm_visibility"]) + if(CanUseTopic(user)) + pref.communicator_visibility = !pref.communicator_visibility + return TOPIC_REFRESH + else if(href_list["set_ringtone"]) + var/choice = tgui_input_list(user, "Please select a ringtone. All of these choices come with an associated preset sound. Alternately, select \"Other\" to specify manually.", "Character Preference", valid_ringtones + "Other", pref.ringtone) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = sanitize(tgui_input_text(user, "Please enter a custom ringtone. If this doesn't match any of the other listed choices, your PDA will use the default (\"beep\") sound.", "Character Preference", null, 20), 20) + if(raw_choice && CanUseTopic(user)) + pref.ringtone = raw_choice + else + pref.ringtone = choice + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/general/05_background.dm b/code/modules/client/preference_setup/general/05_background.dm index 7e712889668..70b2e130bda 100644 --- a/code/modules/client/preference_setup/general/05_background.dm +++ b/code/modules/client/preference_setup/general/05_background.dm @@ -1,173 +1,173 @@ -/datum/category_item/player_setup_item/general/background - name = "Background" - sort_order = 5 - -/datum/category_item/player_setup_item/general/background/load_character(var/savefile/S) - S["med_record"] >> pref.med_record - S["sec_record"] >> pref.sec_record - S["gen_record"] >> pref.gen_record - S["home_system"] >> pref.home_system - S["birthplace"] >> pref.birthplace - S["citizenship"] >> pref.citizenship - S["faction"] >> pref.faction - S["religion"] >> pref.religion - S["economic_status"] >> pref.economic_status - -/datum/category_item/player_setup_item/general/background/save_character(var/savefile/S) - S["med_record"] << pref.med_record - S["sec_record"] << pref.sec_record - S["gen_record"] << pref.gen_record - S["home_system"] << pref.home_system - S["birthplace"] << pref.birthplace - S["citizenship"] << pref.citizenship - S["faction"] << pref.faction - S["religion"] << pref.religion - S["economic_status"] << pref.economic_status - -/datum/category_item/player_setup_item/general/background/sanitize_character() - if(!pref.home_system) pref.home_system = "Unset" - if(!pref.birthplace) pref.birthplace = "Unset" - if(!pref.citizenship) pref.citizenship = "None" - if(!pref.faction) pref.faction = "None" - if(!pref.religion) pref.religion = "None" - - pref.economic_status = sanitize_inlist(pref.economic_status, ECONOMIC_CLASS, initial(pref.economic_status)) - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/background/copy_to_mob(var/mob/living/carbon/human/character) - character.med_record = pref.med_record - character.sec_record = pref.sec_record - character.gen_record = pref.gen_record - character.home_system = pref.home_system - character.birthplace = pref.birthplace - character.citizenship = pref.citizenship - character.personal_faction = pref.faction - character.religion = pref.religion - -/datum/category_item/player_setup_item/general/background/content(var/mob/user) - . += "Background Information
                    " - . += "Economic Status: [pref.economic_status]
                    " - . += "Home: [pref.home_system]
                    " - . += "Birthplace: [pref.birthplace]
                    " - . += "Citizenship: [pref.citizenship]
                    " - . += "Faction: [pref.faction]
                    " - . += "Religion: [pref.religion]
                    " - - . += "
                    Records:
                    " - if(jobban_isbanned(user, "Records")) - . += "You are banned from using character records.
                    " - else - . += "Medical Records:
                    " - . += "[TextPreview(pref.med_record,40)]
                    " - . += " (Reset)

                    " - . += "Employment Records:
                    " - . += "[TextPreview(pref.gen_record,40)]
                    " - . += "(Reset)

                    " - . += "Security Records:
                    " - . += "[TextPreview(pref.sec_record,40)]
                    " - . += "(Reset)" - -/datum/category_item/player_setup_item/general/background/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["econ_status"]) - var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status) - if(new_class && CanUseTopic(user)) - pref.economic_status = new_class - return TOPIC_REFRESH - - else if(href_list["home_system"]) - var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.home_system = raw_choice - else - pref.home_system = choice - return TOPIC_REFRESH - - else if(href_list["birthplace"]) - var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.birthplace = raw_choice - else - pref.birthplace = choice - return TOPIC_REFRESH - - else if(href_list["citizenship"]) - var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice && CanUseTopic(user)) - pref.citizenship = raw_choice - else - pref.citizenship = choice - return TOPIC_REFRESH - - else if(href_list["faction"]) - var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.faction = raw_choice - else - pref.faction = choice - return TOPIC_REFRESH - - else if(href_list["religion"]) - var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion) - if(!choice || !CanUseTopic(user)) - return TOPIC_NOACTION - if(choice == "Other") - var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(raw_choice) - pref.religion = sanitize(raw_choice) - else - pref.religion = choice - return TOPIC_REFRESH - - else if(href_list["set_medical_records"]) - var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(new_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.med_record = new_medical - return TOPIC_REFRESH - - else if(href_list["set_general_records"]) - var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(new_general && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.gen_record = new_general - return TOPIC_REFRESH - - else if(href_list["set_security_records"]) - var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) - if(sec_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) - pref.sec_record = sec_medical - return TOPIC_REFRESH - - else if(href_list["reset_medrecord"]) - var/resetmed_choice = tgui_alert(usr, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetmed_choice == "Yes") - pref.med_record = null - return TOPIC_REFRESH - - else if(href_list["reset_emprecord"]) - var/resetemp_choice = tgui_alert(usr, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetemp_choice == "Yes") - pref.gen_record = null - return TOPIC_REFRESH - - else if(href_list["reset_secrecord"]) - var/resetsec_choice = tgui_alert(usr, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) - if(resetsec_choice == "Yes") - pref.sec_record = null - return TOPIC_REFRESH - - return ..() +/datum/category_item/player_setup_item/general/background + name = "Background" + sort_order = 5 + +/datum/category_item/player_setup_item/general/background/load_character(var/savefile/S) + S["med_record"] >> pref.med_record + S["sec_record"] >> pref.sec_record + S["gen_record"] >> pref.gen_record + S["home_system"] >> pref.home_system + S["birthplace"] >> pref.birthplace + S["citizenship"] >> pref.citizenship + S["faction"] >> pref.faction + S["religion"] >> pref.religion + S["economic_status"] >> pref.economic_status + +/datum/category_item/player_setup_item/general/background/save_character(var/savefile/S) + S["med_record"] << pref.med_record + S["sec_record"] << pref.sec_record + S["gen_record"] << pref.gen_record + S["home_system"] << pref.home_system + S["birthplace"] << pref.birthplace + S["citizenship"] << pref.citizenship + S["faction"] << pref.faction + S["religion"] << pref.religion + S["economic_status"] << pref.economic_status + +/datum/category_item/player_setup_item/general/background/sanitize_character() + if(!pref.home_system) pref.home_system = "Unset" + if(!pref.birthplace) pref.birthplace = "Unset" + if(!pref.citizenship) pref.citizenship = "None" + if(!pref.faction) pref.faction = "None" + if(!pref.religion) pref.religion = "None" + + pref.economic_status = sanitize_inlist(pref.economic_status, ECONOMIC_CLASS, initial(pref.economic_status)) + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/background/copy_to_mob(var/mob/living/carbon/human/character) + character.med_record = pref.med_record + character.sec_record = pref.sec_record + character.gen_record = pref.gen_record + character.home_system = pref.home_system + character.birthplace = pref.birthplace + character.citizenship = pref.citizenship + character.personal_faction = pref.faction + character.religion = pref.religion + +/datum/category_item/player_setup_item/general/background/content(var/mob/user) + . += "Background Information
                    " + . += "Economic Status: [pref.economic_status]
                    " + . += "Home: [pref.home_system]
                    " + . += "Birthplace: [pref.birthplace]
                    " + . += "Citizenship: [pref.citizenship]
                    " + . += "Faction: [pref.faction]
                    " + . += "Religion: [pref.religion]
                    " + + . += "
                    Records:
                    " + if(jobban_isbanned(user, "Records")) + . += "You are banned from using character records.
                    " + else + . += "Medical Records:
                    " + . += "[TextPreview(pref.med_record,40)]
                    " + . += " (Reset)

                    " + . += "Employment Records:
                    " + . += "[TextPreview(pref.gen_record,40)]
                    " + . += "(Reset)

                    " + . += "Security Records:
                    " + . += "[TextPreview(pref.sec_record,40)]
                    " + . += "(Reset)" + +/datum/category_item/player_setup_item/general/background/OnTopic(var/href,var/list/href_list, var/mob/user) + if(href_list["econ_status"]) + var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status) + if(new_class && CanUseTopic(user)) + pref.economic_status = new_class + return TOPIC_REFRESH + + else if(href_list["home_system"]) + var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice && CanUseTopic(user)) + pref.home_system = raw_choice + else + pref.home_system = choice + return TOPIC_REFRESH + + else if(href_list["birthplace"]) + var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice && CanUseTopic(user)) + pref.birthplace = raw_choice + else + pref.birthplace = choice + return TOPIC_REFRESH + + else if(href_list["citizenship"]) + var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice && CanUseTopic(user)) + pref.citizenship = raw_choice + else + pref.citizenship = choice + return TOPIC_REFRESH + + else if(href_list["faction"]) + var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.faction = raw_choice + else + pref.faction = choice + return TOPIC_REFRESH + + else if(href_list["religion"]) + var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion) + if(!choice || !CanUseTopic(user)) + return TOPIC_NOACTION + if(choice == "Other") + var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(raw_choice) + pref.religion = sanitize(raw_choice) + else + pref.religion = choice + return TOPIC_REFRESH + + else if(href_list["set_medical_records"]) + var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(new_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.med_record = new_medical + return TOPIC_REFRESH + + else if(href_list["set_general_records"]) + var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(new_general && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.gen_record = new_general + return TOPIC_REFRESH + + else if(href_list["set_security_records"]) + var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH) + if(sec_medical && !jobban_isbanned(user, "Records") && CanUseTopic(user)) + pref.sec_record = sec_medical + return TOPIC_REFRESH + + else if(href_list["reset_medrecord"]) + var/resetmed_choice = tgui_alert(usr, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetmed_choice == "Yes") + pref.med_record = null + return TOPIC_REFRESH + + else if(href_list["reset_emprecord"]) + var/resetemp_choice = tgui_alert(usr, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetemp_choice == "Yes") + pref.gen_record = null + return TOPIC_REFRESH + + else if(href_list["reset_secrecord"]) + var/resetsec_choice = tgui_alert(usr, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No")) + if(resetsec_choice == "Yes") + pref.sec_record = null + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/general/06_flavor.dm b/code/modules/client/preference_setup/general/06_flavor.dm index 20ab615e786..76af38f5dc9 100644 --- a/code/modules/client/preference_setup/general/06_flavor.dm +++ b/code/modules/client/preference_setup/general/06_flavor.dm @@ -1,154 +1,154 @@ -/datum/category_item/player_setup_item/general/flavor - name = "Flavor" - sort_order = 6 - -/datum/category_item/player_setup_item/general/flavor/load_character(var/savefile/S) - S["flavor_texts_general"] >> pref.flavor_texts["general"] - S["flavor_texts_head"] >> pref.flavor_texts["head"] - S["flavor_texts_face"] >> pref.flavor_texts["face"] - S["flavor_texts_eyes"] >> pref.flavor_texts["eyes"] - S["flavor_texts_torso"] >> pref.flavor_texts["torso"] - S["flavor_texts_arms"] >> pref.flavor_texts["arms"] - S["flavor_texts_hands"] >> pref.flavor_texts["hands"] - S["flavor_texts_legs"] >> pref.flavor_texts["legs"] - S["flavor_texts_feet"] >> pref.flavor_texts["feet"] - S["custom_link"] >> pref.custom_link - //Flavour text for robots. - S["flavour_texts_robot_Default"] >> pref.flavour_texts_robot["Default"] - for(var/module in robot_module_types) - S["flavour_texts_robot_[module]"] >> pref.flavour_texts_robot[module] - -/datum/category_item/player_setup_item/general/flavor/save_character(var/savefile/S) - S["flavor_texts_general"] << pref.flavor_texts["general"] - S["flavor_texts_head"] << pref.flavor_texts["head"] - S["flavor_texts_face"] << pref.flavor_texts["face"] - S["flavor_texts_eyes"] << pref.flavor_texts["eyes"] - S["flavor_texts_torso"] << pref.flavor_texts["torso"] - S["flavor_texts_arms"] << pref.flavor_texts["arms"] - S["flavor_texts_hands"] << pref.flavor_texts["hands"] - S["flavor_texts_legs"] << pref.flavor_texts["legs"] - S["flavor_texts_feet"] << pref.flavor_texts["feet"] - S["custom_link"] << pref.custom_link - - S["flavour_texts_robot_Default"] << pref.flavour_texts_robot["Default"] - for(var/module in robot_module_types) - S["flavour_texts_robot_[module]"] << pref.flavour_texts_robot[module] - -/datum/category_item/player_setup_item/general/flavor/sanitize_character() - return - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/general/flavor/copy_to_mob(var/mob/living/carbon/human/character) - character.flavor_texts["general"] = pref.flavor_texts["general"] - character.flavor_texts["head"] = pref.flavor_texts["head"] - character.flavor_texts["face"] = pref.flavor_texts["face"] - character.flavor_texts["eyes"] = pref.flavor_texts["eyes"] - character.flavor_texts["torso"] = pref.flavor_texts["torso"] - character.flavor_texts["arms"] = pref.flavor_texts["arms"] - character.flavor_texts["hands"] = pref.flavor_texts["hands"] - character.flavor_texts["legs"] = pref.flavor_texts["legs"] - character.flavor_texts["feet"] = pref.flavor_texts["feet"] - character.ooc_notes = pref.metadata //VOREStation Add - character.ooc_notes_likes = pref.metadata_likes - character.ooc_notes_dislikes = pref.metadata_dislikes - character.custom_link = pref.custom_link - -/datum/category_item/player_setup_item/general/flavor/content(var/mob/user) - . += "Flavor:
                    " - . += "Set Flavor Text
                    " - . += "Set Robot Flavor Text
                    " - . += "Set Custom Link
                    " - -/datum/category_item/player_setup_item/general/flavor/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["flavor_text"]) - switch(href_list["flavor_text"]) - if("open") - if("general") - var/msg = strip_html_simple(tgui_input_text(usr,"Give a general description of your character. This will be shown regardless of clothings. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes - if(CanUseTopic(user) && msg) - pref.flavor_texts[href_list["flavor_text"]] = msg - else - var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavor text for your [href_list["flavor_text"]]. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) - if(CanUseTopic(user) && msg) - pref.flavor_texts[href_list["flavor_text"]] = msg - SetFlavorText(user) - return TOPIC_HANDLED - - else if(href_list["flavour_text_robot"]) - switch(href_list["flavour_text_robot"]) - if("open") - if("Default") - var/msg = strip_html_simple(tgui_input_text(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"]), multiline = TRUE, prevent_enter = TRUE)) - if(CanUseTopic(user) && msg) - pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg - else - var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this blank, default flavour text will be used for this module. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]]), multiline = TRUE, prevent_enter = TRUE)) - if(CanUseTopic(user) && msg) - pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg - SetFlavourTextRobot(user) - return TOPIC_HANDLED - else if(href_list["custom_link"]) - var/new_link = strip_html_simple(tgui_input_text(usr, "Enter a link to add on to your examine text! This should be a related image link/gallery, or things like your F-list. This is not the place for memes.", "Custom Link" , html_decode(pref.custom_link), max_length = 100, encode = TRUE, prevent_enter = TRUE)) - if(new_link && CanUseTopic(usr)) - if(length(new_link) > 100) - to_chat(usr, "Your entry is too long, it must be 100 characters or less.") - return - pref.custom_link = new_link - log_admin("[usr]/[usr.ckey] set their custom link to [pref.custom_link]") - - return ..() - -/datum/category_item/player_setup_item/general/flavor/proc/SetFlavorText(mob/user) - var/HTML = "" - HTML += "
                    " - HTML += "Set Flavor Text
                    " - HTML += "Note: This is not *literal* flavor of your character. This is visual description of what they look like.
                    " - HTML += "
                    " - HTML += "General: " - HTML += TextPreview(pref.flavor_texts["general"]) - HTML += "
                    " - HTML += "Head: " - HTML += TextPreview(pref.flavor_texts["head"]) - HTML += "
                    " - HTML += "Face: " - HTML += TextPreview(pref.flavor_texts["face"]) - HTML += "
                    " - HTML += "Eyes: " - HTML += TextPreview(pref.flavor_texts["eyes"]) - HTML += "
                    " - HTML += "Body: " - HTML += TextPreview(pref.flavor_texts["torso"]) - HTML += "
                    " - HTML += "Arms: " - HTML += TextPreview(pref.flavor_texts["arms"]) - HTML += "
                    " - HTML += "Hands: " - HTML += TextPreview(pref.flavor_texts["hands"]) - HTML += "
                    " - HTML += "Legs: " - HTML += TextPreview(pref.flavor_texts["legs"]) - HTML += "
                    " - HTML += "Feet: " - HTML += TextPreview(pref.flavor_texts["feet"]) - HTML += "
                    " - HTML += "
                    " - HTML += "" - user << browse(HTML, "window=flavor_text;size=430x300") - return - -/datum/category_item/player_setup_item/general/flavor/proc/SetFlavourTextRobot(mob/user) - var/HTML = "" - HTML += "
                    " - HTML += "Set Robot Flavour Text
                    " - HTML += "
                    " - HTML += "Default: " - HTML += TextPreview(pref.flavour_texts_robot["Default"]) - HTML += "
                    " - for(var/module in robot_module_types) - HTML += "[module]: " - HTML += TextPreview(pref.flavour_texts_robot[module]) - HTML += "
                    " - HTML += "
                    " - HTML += "" - user << browse(HTML, "window=flavour_text_robot;size=430x300") - return +/datum/category_item/player_setup_item/general/flavor + name = "Flavor" + sort_order = 6 + +/datum/category_item/player_setup_item/general/flavor/load_character(var/savefile/S) + S["flavor_texts_general"] >> pref.flavor_texts["general"] + S["flavor_texts_head"] >> pref.flavor_texts["head"] + S["flavor_texts_face"] >> pref.flavor_texts["face"] + S["flavor_texts_eyes"] >> pref.flavor_texts["eyes"] + S["flavor_texts_torso"] >> pref.flavor_texts["torso"] + S["flavor_texts_arms"] >> pref.flavor_texts["arms"] + S["flavor_texts_hands"] >> pref.flavor_texts["hands"] + S["flavor_texts_legs"] >> pref.flavor_texts["legs"] + S["flavor_texts_feet"] >> pref.flavor_texts["feet"] + S["custom_link"] >> pref.custom_link + //Flavour text for robots. + S["flavour_texts_robot_Default"] >> pref.flavour_texts_robot["Default"] + for(var/module in robot_module_types) + S["flavour_texts_robot_[module]"] >> pref.flavour_texts_robot[module] + +/datum/category_item/player_setup_item/general/flavor/save_character(var/savefile/S) + S["flavor_texts_general"] << pref.flavor_texts["general"] + S["flavor_texts_head"] << pref.flavor_texts["head"] + S["flavor_texts_face"] << pref.flavor_texts["face"] + S["flavor_texts_eyes"] << pref.flavor_texts["eyes"] + S["flavor_texts_torso"] << pref.flavor_texts["torso"] + S["flavor_texts_arms"] << pref.flavor_texts["arms"] + S["flavor_texts_hands"] << pref.flavor_texts["hands"] + S["flavor_texts_legs"] << pref.flavor_texts["legs"] + S["flavor_texts_feet"] << pref.flavor_texts["feet"] + S["custom_link"] << pref.custom_link + + S["flavour_texts_robot_Default"] << pref.flavour_texts_robot["Default"] + for(var/module in robot_module_types) + S["flavour_texts_robot_[module]"] << pref.flavour_texts_robot[module] + +/datum/category_item/player_setup_item/general/flavor/sanitize_character() + return + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/general/flavor/copy_to_mob(var/mob/living/carbon/human/character) + character.flavor_texts["general"] = pref.flavor_texts["general"] + character.flavor_texts["head"] = pref.flavor_texts["head"] + character.flavor_texts["face"] = pref.flavor_texts["face"] + character.flavor_texts["eyes"] = pref.flavor_texts["eyes"] + character.flavor_texts["torso"] = pref.flavor_texts["torso"] + character.flavor_texts["arms"] = pref.flavor_texts["arms"] + character.flavor_texts["hands"] = pref.flavor_texts["hands"] + character.flavor_texts["legs"] = pref.flavor_texts["legs"] + character.flavor_texts["feet"] = pref.flavor_texts["feet"] + character.ooc_notes = pref.metadata //VOREStation Add + character.ooc_notes_likes = pref.metadata_likes + character.ooc_notes_dislikes = pref.metadata_dislikes + character.custom_link = pref.custom_link + +/datum/category_item/player_setup_item/general/flavor/content(var/mob/user) + . += "Flavor:
                    " + . += "Set Flavor Text
                    " + . += "Set Robot Flavor Text
                    " + . += "Set Custom Link
                    " + +/datum/category_item/player_setup_item/general/flavor/OnTopic(var/href,var/list/href_list, var/mob/user) + if(href_list["flavor_text"]) + switch(href_list["flavor_text"]) + if("open") + if("general") + var/msg = strip_html_simple(tgui_input_text(usr,"Give a general description of your character. This will be shown regardless of clothings. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes + if(CanUseTopic(user) && msg) + pref.flavor_texts[href_list["flavor_text"]] = msg + else + var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavor text for your [href_list["flavor_text"]]. Put in a single space to make blank.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]]), multiline = TRUE, prevent_enter = TRUE)) + if(CanUseTopic(user) && msg) + pref.flavor_texts[href_list["flavor_text"]] = msg + SetFlavorText(user) + return TOPIC_HANDLED + + else if(href_list["flavour_text_robot"]) + switch(href_list["flavour_text_robot"]) + if("open") + if("Default") + var/msg = strip_html_simple(tgui_input_text(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"]), multiline = TRUE, prevent_enter = TRUE)) + if(CanUseTopic(user) && msg) + pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg + else + var/msg = strip_html_simple(tgui_input_text(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this blank, default flavour text will be used for this module. Put in a single space to make blank.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]]), multiline = TRUE, prevent_enter = TRUE)) + if(CanUseTopic(user) && msg) + pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg + SetFlavourTextRobot(user) + return TOPIC_HANDLED + else if(href_list["custom_link"]) + var/new_link = strip_html_simple(tgui_input_text(usr, "Enter a link to add on to your examine text! This should be a related image link/gallery, or things like your F-list. This is not the place for memes.", "Custom Link" , html_decode(pref.custom_link), max_length = 100, encode = TRUE, prevent_enter = TRUE)) + if(new_link && CanUseTopic(usr)) + if(length(new_link) > 100) + to_chat(usr, "Your entry is too long, it must be 100 characters or less.") + return + pref.custom_link = new_link + log_admin("[usr]/[usr.ckey] set their custom link to [pref.custom_link]") + + return ..() + +/datum/category_item/player_setup_item/general/flavor/proc/SetFlavorText(mob/user) + var/HTML = "" + HTML += "
                    " + HTML += "Set Flavor Text
                    " + HTML += "Note: This is not *literal* flavor of your character. This is visual description of what they look like.
                    " + HTML += "
                    " + HTML += "General: " + HTML += TextPreview(pref.flavor_texts["general"]) + HTML += "
                    " + HTML += "Head: " + HTML += TextPreview(pref.flavor_texts["head"]) + HTML += "
                    " + HTML += "Face: " + HTML += TextPreview(pref.flavor_texts["face"]) + HTML += "
                    " + HTML += "Eyes: " + HTML += TextPreview(pref.flavor_texts["eyes"]) + HTML += "
                    " + HTML += "Body: " + HTML += TextPreview(pref.flavor_texts["torso"]) + HTML += "
                    " + HTML += "Arms: " + HTML += TextPreview(pref.flavor_texts["arms"]) + HTML += "
                    " + HTML += "Hands: " + HTML += TextPreview(pref.flavor_texts["hands"]) + HTML += "
                    " + HTML += "Legs: " + HTML += TextPreview(pref.flavor_texts["legs"]) + HTML += "
                    " + HTML += "Feet: " + HTML += TextPreview(pref.flavor_texts["feet"]) + HTML += "
                    " + HTML += "
                    " + HTML += "" + user << browse(HTML, "window=flavor_text;size=430x300") + return + +/datum/category_item/player_setup_item/general/flavor/proc/SetFlavourTextRobot(mob/user) + var/HTML = "" + HTML += "
                    " + HTML += "Set Robot Flavour Text
                    " + HTML += "
                    " + HTML += "Default: " + HTML += TextPreview(pref.flavour_texts_robot["Default"]) + HTML += "
                    " + for(var/module in robot_module_types) + HTML += "[module]: " + HTML += TextPreview(pref.flavour_texts_robot[module]) + HTML += "
                    " + HTML += "
                    " + HTML += "" + user << browse(HTML, "window=flavour_text_robot;size=430x300") + return diff --git a/code/modules/client/preference_setup/global/01_ui.dm b/code/modules/client/preference_setup/global/01_ui.dm index 13a05070dd4..221b89fcc26 100644 --- a/code/modules/client/preference_setup/global/01_ui.dm +++ b/code/modules/client/preference_setup/global/01_ui.dm @@ -14,10 +14,12 @@ S["tgui_fancy"] >> pref.tgui_fancy S["tgui_lock"] >> pref.tgui_lock S["tgui_input_mode"] >> pref.tgui_input_mode - S["tgui_input_lock"] >> pref.tgui_input_lock S["tgui_large_buttons"] >> pref.tgui_large_buttons S["tgui_swapped_buttons"] >> pref.tgui_swapped_buttons + S["obfuscate_key"] >> pref.obfuscate_key + S["obfuscate_job"] >> pref.obfuscate_job S["chat_timestamp"] >> pref.chat_timestamp + S["throwmode_loud"] >> pref.throwmode_loud /datum/category_item/player_setup_item/player_global/ui/save_preferences(var/savefile/S) S["UI_style"] << pref.UI_style @@ -31,10 +33,12 @@ S["tgui_fancy"] << pref.tgui_fancy S["tgui_lock"] << pref.tgui_lock S["tgui_input_mode"] << pref.tgui_input_mode - S["tgui_input_lock"] << pref.tgui_input_lock S["tgui_large_buttons"] << pref.tgui_large_buttons S["tgui_swapped_buttons"] << pref.tgui_swapped_buttons + S["obfuscate_key"] << pref.obfuscate_key + S["obfuscate_job"] << pref.obfuscate_job S["chat_timestamp"] << pref.chat_timestamp + S["throwmode_loud"] << pref.throwmode_loud /datum/category_item/player_setup_item/player_global/ui/sanitize_preferences() pref.UI_style = sanitize_inlist(pref.UI_style, all_ui_styles, initial(pref.UI_style)) @@ -48,10 +52,12 @@ pref.tgui_fancy = sanitize_integer(pref.tgui_fancy, 0, 1, initial(pref.tgui_fancy)) pref.tgui_lock = sanitize_integer(pref.tgui_lock, 0, 1, initial(pref.tgui_lock)) pref.tgui_input_mode = sanitize_integer(pref.tgui_input_mode, 0, 1, initial(pref.tgui_input_mode)) - pref.tgui_input_lock = sanitize_integer(pref.tgui_input_lock, 0, 1, initial(pref.tgui_input_lock)) pref.tgui_large_buttons = sanitize_integer(pref.tgui_large_buttons, 0, 1, initial(pref.tgui_large_buttons)) pref.tgui_swapped_buttons = sanitize_integer(pref.tgui_swapped_buttons, 0, 1, initial(pref.tgui_swapped_buttons)) + pref.obfuscate_key = sanitize_integer(pref.obfuscate_key, 0, 1, initial(pref.obfuscate_key)) + pref.obfuscate_job = sanitize_integer(pref.obfuscate_job, 0, 1, initial(pref.obfuscate_job)) pref.chat_timestamp = sanitize_integer(pref.chat_timestamp, 0, 1, initial(pref.chat_timestamp)) + pref.throwmode_loud = sanitize_integer(pref.throwmode_loud, 0, 1, initial(pref.throwmode_loud)) /datum/category_item/player_setup_item/player_global/ui/content(var/mob/user) . = "UI Style: [pref.UI_style]
                    " @@ -65,10 +71,12 @@ . += "TGUI Window Mode: [(pref.tgui_fancy) ? "Fancy (default)" : "Compatible (slower)"]
                    " . += "TGUI Window Placement: [(pref.tgui_lock) ? "Primary Monitor" : "Free (default)"]
                    " . += "TGUI Input Framework: [(pref.tgui_input_mode) ? "Enabled" : "Disabled (default)"]
                    " - . += "TGUI Input Lock: [(pref.tgui_input_lock) ? "Enabled" : "Disabled (default)"]
                    " . += "TGUI Large Buttons: [(pref.tgui_large_buttons) ? "Enabled (default)" : "Disabled"]
                    " . += "TGUI Swapped Buttons: [(pref.tgui_swapped_buttons) ? "Enabled" : "Disabled (default)"]
                    " + . += "Obfuscate Ckey: [(pref.obfuscate_key) ? "Enabled" : "Disabled (default)"]
                    " + . += "Obfuscate Job: [(pref.obfuscate_job) ? "Enabled" : "Disabled (default)"]
                    " . += "Chat Timestamps: [(pref.chat_timestamp) ? "Enabled" : "Disabled (default)"]
                    " + . += "Throw Mode Messages: [(pref.throwmode_loud) ? "Loud" : "Quiet (default)"]
                    " if(can_select_ooc_color(user)) . += "OOC Color:" if(pref.ooccolor == initial(pref.ooccolor)) @@ -142,10 +150,6 @@ pref.tgui_input_mode = !pref.tgui_input_mode return TOPIC_REFRESH - else if(href_list["tgui_input_lock"]) - pref.tgui_input_lock = !pref.tgui_input_lock - return TOPIC_REFRESH - else if(href_list["tgui_large_buttons"]) pref.tgui_large_buttons = !pref.tgui_large_buttons return TOPIC_REFRESH @@ -154,10 +158,22 @@ pref.tgui_swapped_buttons = !pref.tgui_swapped_buttons return TOPIC_REFRESH + else if(href_list["obfuscate_key"]) + pref.obfuscate_key = !pref.obfuscate_key + return TOPIC_REFRESH + + else if(href_list["obfuscate_job"]) + pref.obfuscate_job = !pref.obfuscate_job + return TOPIC_REFRESH + else if(href_list["chat_timestamps"]) pref.chat_timestamp = !pref.chat_timestamp return TOPIC_REFRESH + else if(href_list["throwmode_loudness"]) + pref.throwmode_loud = !pref.throwmode_loud + return TOPIC_REFRESH + else if(href_list["reset"]) switch(href_list["reset"]) if("ui") diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm index f8c0615ef95..437337f3264 100644 --- a/code/modules/client/preference_setup/global/setting_datums.dm +++ b/code/modules/client/preference_setup/global/setting_datums.dm @@ -113,6 +113,13 @@ var/list/_client_preferences_by_type enabled_description = "Visible" disabled_description = "Hidden" enabled_by_default = FALSE + +/datum/client_preference/ghost_see_whisubtle + description = "See subtles/whispers as ghost" + key = "GHOST_SEE_WHISUBTLE" + enabled_description = "Visible" + disabled_description = "Hidden" + enabled_by_default = TRUE //VOREStation Add End /datum/client_preference/weather_sounds description ="Weather sounds" @@ -282,7 +289,7 @@ var/list/_client_preferences_by_type key = "SOUND_INSTRUMENT" /datum/client_preference/vchat_enable - description = "Enable/Disable VChat" + description = "Enable/Disable TGChat" key = "VCHAT_ENABLE" enabled_description = "Enabled" disabled_description = "Disabled" @@ -323,6 +330,12 @@ var/list/_client_preferences_by_type enabled_description = "On" disabled_description = "Off" +/datum/client_preference/vore_health_bars + description = "Vore Health Bars" + key = "VORE_HEALTH_BARS" + enabled_description = "Enabled" + disabled_description = "Disabled" + /datum/client_preference/runechat_mob description = "Runechat (Mobs)" key = "RUNECHAT_MOB" @@ -373,7 +386,7 @@ var/list/_client_preferences_by_type key = "RECEIVE_TIPS" enabled_description = "Enabled" disabled_description = "Disabled" - + /datum/client_preference/pain_frequency description = "Pain Messages Cooldown" key = "PAIN_FREQUENCY" diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm index 205be487757..6e541b86fbe 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm @@ -156,3 +156,27 @@ Talon pin /datum/gear/accessory/antediluvian/loin display_name = "antediluvian loincloth" path = /obj/item/clothing/accessory/antediluvian/loincloth + +//Replikant accessories + +/datum/gear/accessory/sleekpatch + display_name = "sleek uniform patch" + path = /obj/item/clothing/accessory/sleekpatch + +/datum/gear/accessory/poncho/roles/cloak/custom/gestaltjacket + display_name = "sleek uniform jacket" + path = /obj/item/clothing/accessory/poncho/roles/cloak/custom/gestaltjacket + +/datum/gear/accessory/replika + display_name = "replikant vest selection" + path = /obj/item/clothing/accessory/replika + +/datum/gear/accessory/replika/New() + ..() + var/list/replika_vests = list( + "controller replikant chestplate" = /obj/item/clothing/accessory/replika/klbr, + "combat-engineer replikant chestplate" = /obj/item/clothing/accessory/replika/lstr, + "security-controller replikant chestplate" = /obj/item/clothing/accessory/replika/stcr, + "security-technician replikant chestplate" = /obj/item/clothing/accessory/replika/star + ) + gear_tweaks += new/datum/gear_tweak/path(replika_vests) diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm index acfd4eebf48..bdc71fbffd1 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm @@ -521,6 +521,12 @@ ckeywhitelist = list("hunterbirk") character_name = list("Aria Blue") +/datum/gear/fluff/elizabeth_conduct_medal + path = /obj/item/clothing/accessory/medal/conduct + display_name = "Elizabeth's Distinguished Conduct Medal" + ckeywhitelist = list("hunterbirk") + character_name = list("Elizabeth Bayou") + /datum/gear/fluff/mercury_vopal_ring path = /obj/item/clothing/gloves/ring/material/void_opal/fluff/mercury display_name = "Mercury's Mate Ring" @@ -1436,15 +1442,9 @@ /datum/gear/fluff/harmony_id path = /obj/item/weapon/card/id/event/polymorphic/itg - display_name = "Harmony's ITG-ID card" + display_name = "ITG-ID card" ckeywhitelist = list("verysoft") - character_name = list("Harmony") - -/datum/gear/fluff/shinerunner_id - path = /obj/item/weapon/card/id/event/polymorphic/itg - display_name = "Shine-Runner's ITG-ID card" - ckeywhitelist = list("verysoft") - character_name = list("Shine-Runner") + character_name = null /datum/gear/fluff/dessa_hat path = /obj/item/clothing/head/fluff/giantbow/dessa @@ -1457,6 +1457,13 @@ ..() gear_tweaks += gear_tweak_free_color_choice +/datum/gear/fluff/casey_glasses + path = /obj/item/clothing/glasses/big_round + display_name = "Big Round Glasses" + slot = slot_glasses + ckeywhitelist = list("verysoft") + character_name = null + // W CKEYS /datum/gear/fluff/sthasha_bracer path = /obj/item/clothing/accessory/bracer/fluff/xander_sthasha diff --git a/code/modules/client/preference_setup/loadout/loadout_head.dm b/code/modules/client/preference_setup/loadout/loadout_head.dm index 435ed5ff309..7716e44d741 100644 --- a/code/modules/client/preference_setup/loadout/loadout_head.dm +++ b/code/modules/client/preference_setup/loadout/loadout_head.dm @@ -297,7 +297,8 @@ "engineering"=/obj/item/clothing/head/welding/engie, "fancy"=/obj/item/clothing/head/welding/fancy, "demonic"=/obj/item/clothing/head/welding/demon, - "knightly"=/obj/item/clothing/head/welding/knight + "knightly"=/obj/item/clothing/head/welding/knight, + "replikant"=/obj/item/clothing/head/welding/arar ) gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) diff --git a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm index 10357f0a25b..113b2898350 100644 --- a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm @@ -97,3 +97,9 @@ Talon hats /datum/gear/head/tiny_tophat display_name = "tiny tophat" path = /obj/item/clothing/head/tinytophat + +//Replikant hat + +/datum/gear/head/eulrhat + display_name = "Sleek side cap" + path = /obj/item/clothing/head/eulrhat diff --git a/code/modules/client/preference_setup/loadout/loadout_mask.dm b/code/modules/client/preference_setup/loadout/loadout_mask.dm index 3531cfff729..de4aad90d24 100644 --- a/code/modules/client/preference_setup/loadout/loadout_mask.dm +++ b/code/modules/client/preference_setup/loadout/loadout_mask.dm @@ -76,3 +76,7 @@ /datum/gear/mask/lace/New() gear_tweaks += gear_tweak_free_color_choice + +/datum/gear/mask/half + display_name = "black half-mask" + path = /obj/item/clothing/accessory/gaiter/half diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index 0710efade3b..1aaddeda058 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -505,6 +505,10 @@ path = /obj/item/clothing/suit/storage/hooded/wintercoat/cosmic // winter coats end here +/datum/gear/suit/runner + display_name = "runner jacket" + path = /obj/item/clothing/suit/runner + /datum/gear/suit/varsity display_name = "varsity jacket selection" path = /obj/item/clothing/suit/varsity diff --git a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm index 8a27d787363..0dd86aa184d 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm @@ -69,9 +69,9 @@ ) gear_tweaks += new/datum/gear_tweak/path(coats) -//Emergency Responder jackets for Parameds & EMTs, but also general Medical Staff -/datum/gear/suit/roles/medical/ems_jacket - display_name = "first responder jacket" +//EMT coats, jackets and vest +/datum/gear/suit/paramedic_coat + display_name = "paramedic outerwear selection" path = /obj/item/clothing/suit/storage/toggle/fr_jacket allowed_roles = list("Chief Medical Officer","Paramedic","Medical Doctor","Field Medic") @@ -86,6 +86,19 @@ path = /obj/item/clothing/suit/storage/toggle/paramedic allowed_roles = list("Chief Medical Officer","Paramedic","Medical Doctor","Field Medic") +/datum/gear/suit/paramedic_coat/New() + ..() + var/list/paramedicCoats = list( + "First responder jacket" = /obj/item/clothing/suit/storage/toggle/fr_jacket, + "First responder jacket, alt." = /obj/item/clothing/suit/storage/toggle/fr_jacket/ems, + "Paramedic vest" = /obj/item/clothing/suit/storage/toggle/paramedic, + "EMT's labcoat" = /obj/item/clothing/suit/storage/toggle/labcoat/neo_emt, + "High visibility jacket" = /obj/item/clothing/suit/storage/toggle/labcoat/neo_highvis, + "Red EMT jacket" = /obj/item/clothing/suit/storage/toggle/labcoat/neo_redemt, + "Dark Blue EMT jacket" = /obj/item/clothing/suit/storage/toggle/labcoat/neo_blueemt + ) + gear_tweaks += new/datum/gear_tweak/path(paramedicCoats) + //greek thing /datum/gear/suit/chiton display_name = "chiton" diff --git a/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm b/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm index f02baa60513..db4f0c6fdb7 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uni_selector.dm @@ -592,4 +592,44 @@ "TG&C jumpsuit"=/obj/item/clothing/under/rank/neo_robo, "TG&C jumpskirt"=/obj/item/clothing/under/rank/neo_robo_skirt ) - gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) \ No newline at end of file + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) + +//Replikant & Signalis-themed human-wear + +/datum/gear/uniform/replikant_selector + display_name = "Replikant Uniform Selection" + description = "Several variants of bodysuit designed for Second Generation Biosynthetics." + path = /obj/item/clothing/under/replika/arar + sort_category = "Uniforms" + cost = 1 + +/datum/gear/uniform/replikant_selector/New() + ..() + var/list/selector_uniforms = list( + "Macaw"=/obj/item/clothing/under/replika/arar, + "Magpie"=/obj/item/clothing/under/replika/lstr, + "Falcon"=/obj/item/clothing/under/replika/fklr, + "Owl"=/obj/item/clothing/under/replika/eulr, + "Hummingbird"=/obj/item/clothing/under/replika/klbr, + "Stork/Starling"=/obj/item/clothing/under/replika/stcr, + "Eagle"=/obj/item/clothing/under/replika/adlr, + "Magpie, Alternate"=/obj/item/clothing/under/replika/lstr_alt + ) + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) + +/datum/gear/uniform/gestalt_selector + display_name = "Sleek Uniform Selection" + description = "Multiple variants of single-stripe pattern uniforms. Best worn under their accompanying jacket." + path = /obj/item/clothing/under/gestalt + sort_category = "Uniforms" + cost = 1 + +/datum/gear/uniform/gestalt_selector/New() + ..() + var/list/selector_uniforms = list( + "Sleek, standard"=/obj/item/clothing/under/gestalt/sleek, + "Sleek, skirt"=/obj/item/clothing/under/gestalt/sleek_skirt, + "Sleek, feminine"=/obj/item/clothing/under/gestalt/sleek_fem, + "Sleek, sleeveless"=/obj/item/clothing/under/gestalt/sleeveless + ) + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform.dm b/code/modules/client/preference_setup/loadout/loadout_uniform.dm index e7ea6ce7e33..f5c9aab68cb 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform.dm @@ -291,9 +291,20 @@ display_name = "jumpsuit, circuitry (empty)" path = /obj/item/clothing/under/circuitry -/datum/gear/uniform/sleekoverall - display_name = "sleek overalls" - path = /obj/item/clothing/under/overalls/sleek +/datum/gear/uniform/overalls + display_name = "overalls selection" + path = /obj/item/clothing/under/overalls + +/datum/gear/uniform/overalls/New() + ..() + var/list/overalls = list( + "Laborer's Overalls" = /obj/item/clothing/under/overalls, + "Sleek Overalls" = /obj/item/clothing/under/overalls/sleek, + "Workman Overalls" = /obj/item/clothing/under/overalls/service, + "Frontier Overalls" = /obj/item/clothing/under/overalls/frontier, + "Rustler Overalls" = /obj/item/clothing/under/overalls/rustler + ) + gear_tweaks += new/datum/gear_tweak/path(overalls) /datum/gear/uniform/sarired display_name = "sari, red" @@ -363,6 +374,10 @@ display_name = "halter top" path = /obj/item/clothing/under/haltertop +/datum/gear/uniform/tanktop + display_name = "tank top" + path = /obj/item/clothing/under/tanktop + /datum/gear/uniform/revealingdress display_name = "revealing dress" path = /obj/item/clothing/under/dress/revealingdress @@ -497,6 +512,14 @@ ) gear_tweaks += new/datum/gear_tweak/path(cowboy_outfits) +/datum/gear/uniform/hightrousers + display_name = "high-waisted trousers" + path = /obj/item/clothing/under/dress/hightrousers + +/datum/gear/uniform/vampirehunter + display_name = "18th century outfit" + path = /obj/item/clothing/under/vampirehunter + /* * 80s */ @@ -586,3 +609,16 @@ /datum/gear/uniform/fienddress display_name = "fiendish dress" path = /obj/item/clothing/under/fienddress + +//tabard dresses +/datum/gear/uniform/tabarddress + display_name = "tabard-dress selection" + path = /obj/item/clothing/under/dress/tabard + +/datum/gear/uniform/tabarddress/New() + ..() + var/list/tabarddress = list( + "white tabard-dress"=/obj/item/clothing/under/dress/tabard, + "black tabard-dress"=/obj/item/clothing/under/dress/tabard/black + ) + gear_tweaks += list(new/datum/gear_tweak/path(tabarddress)) diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm b/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm index e07d1ccfb53..af8c4b89414 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm @@ -271,7 +271,7 @@ Talon jumpsuit //Altevian Uniforms /datum/gear/uniform/altevian - description = "An extremely comfortable set of clothing that's made to help people handle their day to day work around the fleets with little to no discomfort." + description = "A comfortable set of clothing for people to handle their day to day work around the fleets with little to no discomfort." display_name = "altevian uniform selection" /datum/gear/uniform/altevian/New() diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm index 489f4e857af..7e302bbf4ff 100644 --- a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm @@ -92,7 +92,7 @@ sort_category = "Xenowear" /datum/gear/uniform/altevian_outfit - description = "A uniform commonly seen from altevians during their work. The material on this uniform seems to be made of durable thread that can handle the stress of most matters of labor." + description = "A uniform commonly seen worn by altevians. The material on this uniform is made of a durable thread that can handle the stress of most forms of labor." display_name = "altevian duty jumpsuit selection (Altevian)" whitelisted = SPECIES_ALTEVIAN sort_category = "Xenowear" @@ -105,7 +105,7 @@ gear_tweaks += new/datum/gear_tweak/path(sortAssoc(pants)) /datum/gear/accessory/altevian_aquila - description = "An emblem commonly seen worn by the altevians for their work operations." + description = "An emblem found across all altevian vessels. The specific metals and jewels denote ranks. Otherwise, it's just a pretty rat skull emblem with a set of crystals for their eyes and fangs." display_name = "royal altevian navy emblem selection" whitelisted = SPECIES_ALTEVIAN sort_category = "Xenowear" diff --git a/code/modules/client/preference_setup/skills/skills.dm b/code/modules/client/preference_setup/skills/skills.dm index 00fb15ec902..645314ab590 100644 --- a/code/modules/client/preference_setup/skills/skills.dm +++ b/code/modules/client/preference_setup/skills/skills.dm @@ -1,89 +1,89 @@ -/datum/category_item/player_setup_item/skills - name = "Skills" - sort_order = 1 - -/datum/category_item/player_setup_item/skills/load_character(var/savefile/S) - S["skills"] >> pref.skills - S["used_skillpoints"] >> pref.used_skillpoints - S["skill_specialization"] >> pref.skill_specialization - -/datum/category_item/player_setup_item/skills/save_character(var/savefile/S) - S["skills"] << pref.skills - S["used_skillpoints"] << pref.used_skillpoints - S["skill_specialization"] << pref.skill_specialization - -/datum/category_item/player_setup_item/skills/sanitize_character() - if(SKILLS == null) setup_skills() - if(!pref.skills) pref.skills = list() - if(!pref.skills.len) pref.ZeroSkills() - if(pref.used_skillpoints < 0) pref.used_skillpoints = 0 - -// Moved from /datum/preferences/proc/copy_to() -/datum/category_item/player_setup_item/skills/copy_to_mob(var/mob/living/carbon/human/character) - character.skills = pref.skills - character.used_skillpoints = pref.used_skillpoints - -/datum/category_item/player_setup_item/skills/content() - . = list() - . += "Select your Skills
                    " - . += "Current skill level: [pref.GetSkillClass(pref.used_skillpoints)] ([pref.used_skillpoints])
                    " - . += "Use preconfigured skillset
                    " - . += "" - for(var/V in SKILLS) - . += "" - for(var/datum/skill/S in SKILLS[V]) - var/level = pref.skills[S.ID] - . += "" - . += "" - . += skill_to_button(S, "Untrained", level, SKILL_NONE) - // secondary skills don't have an amateur level - if(S.secondary) - . += "" - else - . += skill_to_button(S, "Amateur", level, SKILL_BASIC) - . += skill_to_button(S, "Trained", level, SKILL_ADEPT) - . += skill_to_button(S, "Professional", level, SKILL_EXPERT) - . += "" - . += "
                    [V]" - . += "
                    [S.name]
                    " - . = jointext(.,null) - -/datum/category_item/player_setup_item/proc/skill_to_button(var/skill, var/level_name, var/current_level, var/selection_level) - if(current_level == selection_level) - return "[level_name]" - return "[level_name]" - -/datum/category_item/player_setup_item/skills/OnTopic(href, href_list, user) - if(href_list["skillinfo"]) - var/datum/skill/S = locate(href_list["skillinfo"]) - var/HTML = "[S.name]
                    [S.desc]" - user << browse(HTML, "window=\ref[user]skillinfo") - return TOPIC_HANDLED - - else if(href_list["setskill"]) - var/datum/skill/S = locate(href_list["setskill"]) - var/value = text2num(href_list["newvalue"]) - pref.skills[S.ID] = value - pref.CalculateSkillPoints() - return TOPIC_REFRESH - - else if(href_list["preconfigured"]) - var/selected = tgui_input_list(user, "Select a skillset", "Skillset", SKILL_PRE) - if(!selected || !CanUseTopic(user)) return - - pref.ZeroSkills(1) - for(var/V in SKILL_PRE[selected]) - if(V == "field") - pref.skill_specialization = SKILL_PRE[selected]["field"] - continue - pref.skills[V] = SKILL_PRE[selected][V] - pref.CalculateSkillPoints() - - return TOPIC_REFRESH - - else if(href_list["setspecialization"]) - pref.skill_specialization = href_list["setspecialization"] - pref.CalculateSkillPoints() - return TOPIC_REFRESH - - return ..() +/datum/category_item/player_setup_item/skills + name = "Skills" + sort_order = 1 + +/datum/category_item/player_setup_item/skills/load_character(var/savefile/S) + S["skills"] >> pref.skills + S["used_skillpoints"] >> pref.used_skillpoints + S["skill_specialization"] >> pref.skill_specialization + +/datum/category_item/player_setup_item/skills/save_character(var/savefile/S) + S["skills"] << pref.skills + S["used_skillpoints"] << pref.used_skillpoints + S["skill_specialization"] << pref.skill_specialization + +/datum/category_item/player_setup_item/skills/sanitize_character() + if(SKILLS == null) setup_skills() + if(!pref.skills) pref.skills = list() + if(!pref.skills.len) pref.ZeroSkills() + if(pref.used_skillpoints < 0) pref.used_skillpoints = 0 + +// Moved from /datum/preferences/proc/copy_to() +/datum/category_item/player_setup_item/skills/copy_to_mob(var/mob/living/carbon/human/character) + character.skills = pref.skills + character.used_skillpoints = pref.used_skillpoints + +/datum/category_item/player_setup_item/skills/content() + . = list() + . += "Select your Skills
                    " + . += "Current skill level: [pref.GetSkillClass(pref.used_skillpoints)] ([pref.used_skillpoints])
                    " + . += "Use preconfigured skillset
                    " + . += "" + for(var/V in SKILLS) + . += "" + for(var/datum/skill/S in SKILLS[V]) + var/level = pref.skills[S.ID] + . += "" + . += "" + . += skill_to_button(S, "Untrained", level, SKILL_NONE) + // secondary skills don't have an amateur level + if(S.secondary) + . += "" + else + . += skill_to_button(S, "Amateur", level, SKILL_BASIC) + . += skill_to_button(S, "Trained", level, SKILL_ADEPT) + . += skill_to_button(S, "Professional", level, SKILL_EXPERT) + . += "" + . += "
                    [V]" + . += "
                    [S.name]
                    " + . = jointext(.,null) + +/datum/category_item/player_setup_item/proc/skill_to_button(var/skill, var/level_name, var/current_level, var/selection_level) + if(current_level == selection_level) + return "[level_name]" + return "[level_name]" + +/datum/category_item/player_setup_item/skills/OnTopic(href, href_list, user) + if(href_list["skillinfo"]) + var/datum/skill/S = locate(href_list["skillinfo"]) + var/HTML = "[S.name]
                    [S.desc]" + user << browse(HTML, "window=\ref[user]skillinfo") + return TOPIC_HANDLED + + else if(href_list["setskill"]) + var/datum/skill/S = locate(href_list["setskill"]) + var/value = text2num(href_list["newvalue"]) + pref.skills[S.ID] = value + pref.CalculateSkillPoints() + return TOPIC_REFRESH + + else if(href_list["preconfigured"]) + var/selected = tgui_input_list(user, "Select a skillset", "Skillset", SKILL_PRE) + if(!selected || !CanUseTopic(user)) return + + pref.ZeroSkills(1) + for(var/V in SKILL_PRE[selected]) + if(V == "field") + pref.skill_specialization = SKILL_PRE[selected]["field"] + continue + pref.skills[V] = SKILL_PRE[selected][V] + pref.CalculateSkillPoints() + + return TOPIC_REFRESH + + else if(href_list["setspecialization"]) + pref.skill_specialization = href_list["setspecialization"] + pref.CalculateSkillPoints() + return TOPIC_REFRESH + + return ..() diff --git a/code/modules/client/preference_setup/vore/02_size.dm b/code/modules/client/preference_setup/vore/02_size.dm index c3e386cb4c7..530706c0e22 100644 --- a/code/modules/client/preference_setup/vore/02_size.dm +++ b/code/modules/client/preference_setup/vore/02_size.dm @@ -14,6 +14,7 @@ var/weight_gain = 100 // Weight gain rate. var/weight_loss = 50 // Weight loss rate. var/fuzzy = 0 // Preference toggle for sharp/fuzzy icon. Default sharp. + var/offset_override = FALSE var/voice_freq = 0 var/voice_sound = "beep-boop" var/custom_speech_bubble = "default" @@ -29,6 +30,7 @@ S["weight_gain"] >> pref.weight_gain S["weight_loss"] >> pref.weight_loss S["fuzzy"] >> pref.fuzzy + S["offset_override"] >> pref.offset_override S["voice_freq"] >> pref.voice_freq S["voice_sound"] >> pref.voice_sound S["custom_speech_bubble"] >> pref.custom_speech_bubble @@ -39,6 +41,7 @@ S["weight_gain"] << pref.weight_gain S["weight_loss"] << pref.weight_loss S["fuzzy"] << pref.fuzzy + S["offset_override"] << pref.offset_override S["voice_freq"] << pref.voice_freq S["voice_sound"] << pref.voice_sound S["custom_speech_bubble"] << pref.custom_speech_bubble @@ -48,6 +51,7 @@ pref.weight_gain = sanitize_integer(pref.weight_gain, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_gain)) pref.weight_loss = sanitize_integer(pref.weight_loss, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_loss)) pref.fuzzy = sanitize_integer(pref.fuzzy, 0, 1, initial(pref.fuzzy)) + pref.offset_override = sanitize_integer(pref.offset_override, 0, 1, initial(pref.offset_override)) if(pref.voice_freq != 0) pref.voice_freq = sanitize_integer(pref.voice_freq, MIN_VOICE_FREQ, MAX_VOICE_FREQ, initial(pref.fuzzy)) if(pref.size_multiplier == null || pref.size_multiplier < RESIZE_TINY || pref.size_multiplier > RESIZE_HUGE) @@ -60,6 +64,7 @@ character.weight_gain = pref.weight_gain character.weight_loss = pref.weight_loss character.fuzzy = pref.fuzzy + character.offset_override = pref.offset_override character.voice_freq = pref.voice_freq character.resize(pref.size_multiplier, animate = FALSE, ignore_prefs = TRUE) if(!pref.voice_sound) @@ -100,6 +105,7 @@ . += "
                    " . += "Scale: [round(pref.size_multiplier*100)]%
                    " . += "Scaled Appearance: [pref.fuzzy ? "Fuzzy" : "Sharp"]
                    " + . += "Scaling Center: [pref.offset_override ? "Odd" : "Even"]
                    " . += "Voice Frequency: [pref.voice_freq]
                    " . += "Voice Sounds: [pref.voice_sound]
                    " . += "Test Selected Voice
                    " @@ -124,12 +130,16 @@ pref.fuzzy = pref.fuzzy ? 0 : 1; return TOPIC_REFRESH_UPDATE_PREVIEW + else if(href_list["toggle_offset_override"]) + pref.offset_override = pref.offset_override ? 0 : 1; + return TOPIC_REFRESH_UPDATE_PREVIEW + else if(href_list["weight"]) var/new_weight = tgui_input_number(user, "Choose your character's relative body weight.\n\ This measurement should be set relative to a normal 5'10'' person's body and not the actual size of your character.\n\ If you set your weight to 500 because you're a naga or have metal implants then complain that you're a blob I\n\ swear to god I will find you and I will punch you for not reading these directions!\n\ - ([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN) + ([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE) if(new_weight) var/unit_of_measurement = tgui_alert(user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms")) if(unit_of_measurement == "Pounds") @@ -143,7 +153,7 @@ var/weight_gain_rate = tgui_input_number(user, "Choose your character's rate of weight gain between 100% \ (full realism body fat gain) and 0% (no body fat gain).\n\ (If you want to disable weight gain, set this to 0.01 to round it to 0%.)\ - ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain) + ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) if(weight_gain_rate) pref.weight_gain = round(text2num(weight_gain_rate),1) return TOPIC_REFRESH @@ -152,7 +162,7 @@ var/weight_loss_rate = tgui_input_number(user, "Choose your character's rate of weight loss between 100% \ (full realism body fat loss) and 0% (no body fat loss).\n\ (If you want to disable weight loss, set this to 0.01 round it to 0%.)\ - ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss) + ([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE) if(weight_loss_rate) pref.weight_loss = round(text2num(weight_loss_rate),1) return TOPIC_REFRESH @@ -167,7 +177,7 @@ pref.voice_freq = choice return TOPIC_REFRESH else if(choice == 1) - choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ, round_value = TRUE) + choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ) if(choice > MAX_VOICE_FREQ) choice = MAX_VOICE_FREQ else if(choice < MIN_VOICE_FREQ) diff --git a/code/modules/client/preference_setup/vore/07_traits.dm b/code/modules/client/preference_setup/vore/07_traits.dm index e89d12f2cc3..14b0775363e 100644 --- a/code/modules/client/preference_setup/vore/07_traits.dm +++ b/code/modules/client/preference_setup/vore/07_traits.dm @@ -5,7 +5,7 @@ #define ORGANICS 1 #define SYNTHETICS 2 -var/global/list/valid_bloodreagents = list("iron","copper","phoron","silver","gold","slimejelly") //allowlist-based so people don't make their blood restored by alcohol or something really silly. use reagent IDs! +var/global/list/valid_bloodreagents = list("default","iron","copper","phoron","silver","gold","slimejelly") //allowlist-based so people don't make their blood restored by alcohol or something really silly. use reagent IDs! /datum/preferences var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already. @@ -258,7 +258,8 @@ var/global/list/valid_bloodreagents = list("iron","copper","phoron","silver","go //Any additional non-trait settings can be applied here new_S.blood_color = pref.blood_color - new_S.blood_reagents = pref.blood_reagents + if(!(pref.blood_reagents == "default")) + new_S.blood_reagents = pref.blood_reagents if(pref.species == SPECIES_CUSTOM) //Statistics for this would be nice diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 06c43619ab5..275c1b095cd 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1,492 +1,498 @@ -#define SAVE_RESET -1 - -var/list/preferences_datums = list() - -/datum/preferences - //doohickeys for savefiles - var/path - var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - var/savefile_version = 0 - - //non-preference stuff - var/warns = 0 - var/muted = 0 - var/last_ip - var/last_id - - //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change - var/ooccolor = "#010000" //Whatever this is set to acts as 'reset' color and is thus unusable as an actual custom color - var/be_special = 0 //Special role selection - var/UI_style = "Midnight" - var/UI_style_color = "#ffffff" - var/UI_style_alpha = 255 - var/tooltipstyle = "Midnight" //Style for popup tooltips - var/client_fps = 40 - var/ambience_freq = 5 // How often we're playing repeating ambience to a client. - var/ambience_chance = 35 // What's the % chance we'll play ambience (in conjunction with the above frequency) - - var/tgui_fancy = TRUE - var/tgui_lock = FALSE - var/tgui_input_mode = FALSE // All the Input Boxes (Text,Number,List,Alert) - var/tgui_input_lock = FALSE - var/tgui_large_buttons = TRUE - var/tgui_swapped_buttons = FALSE - var/chat_timestamp = FALSE - - //character preferences - var/real_name //our character's name - var/be_random_name = 0 //whether we are a random name every round - var/nickname //our character's nickname - var/age = 30 //age of character - var/bday_month = 0 //Birthday month - var/bday_day = 0 //Birthday day - var/last_birthday_notification = 0 //The last year we were notified about our birthday - var/bday_announce = FALSE //Public announcement for birthdays - var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2). - var/b_type = "A+" //blood type (not-chooseable) - var/blood_reagents = "iron" //blood restoration reagents - var/backbag = 2 //backpack type - var/pdachoice = 1 //PDA type - var/h_style = "Bald" //Hair type - var/r_hair = 0 //Hair color - var/g_hair = 0 //Hair color - var/b_hair = 0 //Hair color - var/grad_style = "none" //Gradient style - var/r_grad = 0 //Gradient color - var/g_grad = 0 //Gradient color - var/b_grad = 0 //Gradient color - var/f_style = "Shaved" //Face hair type - var/r_facial = 0 //Face hair color - var/g_facial = 0 //Face hair color - var/b_facial = 0 //Face hair color - var/s_tone = -75 //Skin tone - var/r_skin = 238 //Skin color // Vorestation edit, so color multi sprites can aren't BLACK AS THE VOID by default. - var/g_skin = 206 //Skin color // Vorestation edit, so color multi sprites can aren't BLACK AS THE VOID by default. - var/b_skin = 179 //Skin color // Vorestation edit, so color multi sprites can aren't BLACK AS THE VOID by default. - var/r_eyes = 0 //Eye color - var/g_eyes = 0 //Eye color - var/b_eyes = 0 //Eye color - var/species = SPECIES_HUMAN //Species datum to use. - var/species_preview //Used for the species selection window. - var/list/alternate_languages = list() //Secondary language(s) - var/list/language_prefixes = list() //Language prefix keys - var/list/language_custom_keys = list() //Language custom call keys - var/list/gear //Left in for Legacy reasons, will no longer save. - var/list/gear_list = list() //Custom/fluff item loadouts. - var/gear_slot = 1 //The current gear save slot - var/list/traits //Traits which modifier characters for better or worse (mostly worse). - var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. - var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. - var/g_synth //Same as above - var/b_synth //Same as above - var/synth_markings = 1 //Enable/disable markings on synth parts. //VOREStation Edit - 1 by default - - //Some faction information. - var/home_system = "Unset" //Current home or residence. - var/birthplace = "Unset" //Location of birth. - var/citizenship = "None" //Government or similar entity with which you hold citizenship. - var/faction = "None" //General associated faction. - var/religion = "None" //Religious association. - var/antag_faction = "None" //Antag associated faction. - var/antag_vis = "Hidden" //How visible antag association is to others. - - //Mob preview - var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen. - var/static/list/preview_screen_locs = list( - "1" = "character_preview_map:2,7", - "2" = "character_preview_map:2,5", - "4" = "character_preview_map:2,3", - "8" = "character_preview_map:2,1", - "BG" = "character_preview_map:1,1 to 3,8", - "PMH" = "character_preview_map:2,7" - ) - - //Jobs, uses bitflags - var/job_civilian_high = 0 - var/job_civilian_med = 0 - var/job_civilian_low = 0 - - var/job_medsci_high = 0 - var/job_medsci_med = 0 - var/job_medsci_low = 0 - - var/job_engsec_high = 0 - var/job_engsec_med = 0 - var/job_engsec_low = 0 - - //Keeps track of preferrence for not getting any wanted jobs - var/alternate_option = 1 - - var/used_skillpoints = 0 - var/skill_specialization = null - var/list/skills = list() // skills can range from 0 to 3 - - // maps each organ to either null(intact), "cyborg" or "amputated" - // will probably not be able to do this for head and torso ;) - var/list/organ_data = list() - var/list/rlimb_data = list() - var/list/player_alt_titles = new() // the default name of a job like "Medical Doctor" - - var/list/body_markings = list() // "name" = "#rgbcolor" //VOREStation Edit: "name" = list(BP_HEAD = list("on" = , "color" = "#rgbcolor"), BP_TORSO = ...) - - var/list/flavor_texts = list() - var/list/flavour_texts_robot = list() - var/custom_link = null - - var/list/body_descriptors = list() - - var/med_record = "" - var/sec_record = "" - var/gen_record = "" - var/exploit_record = "" - var/disabilities = 0 - - var/economic_status = "Average" - - var/uplinklocation = "PDA" - - // OOC Metadata: - var/metadata = "" - var/metadata_likes = "" - var/metadata_dislikes = "" - var/list/ignored_players = list() - - var/client/client = null - var/client_ckey = null - - // Communicator identity data - var/communicator_visibility = 0 - - var/datum/category_collection/player_setup_collection/player_setup - var/datum/browser/panel - - var/lastnews // Hash of last seen lobby news content. - var/lastlorenews //ID of last seen lore news article. - - var/examine_text_mode = 0 // Just examine text, include usage (description_info), switch to examine panel. - var/multilingual_mode = 0 // Default behaviour, delimiter-key-space, delimiter-key-delimiter, off - - var/list/volume_channels = list() - - ///If they are currently in the process of swapping slots, don't let them open 999 windows for it and get confused - var/selecting_slots = FALSE - - -/datum/preferences/New(client/C) - player_setup = new(src) - set_biological_gender(pick(MALE, FEMALE)) - real_name = random_name(identifying_gender,species) - b_type = RANDOM_BLOOD_TYPE - - gear = list() - gear_list = list() - gear_slot = 1 - - if(istype(C)) - client = C - client_ckey = C.ckey - if(!IsGuestKey(C.key)) - load_path(C.ckey) - if(load_preferences()) - load_character() - - -/datum/preferences/Destroy() - . = ..() - QDEL_LIST_ASSOC_VAL(char_render_holders) - -/datum/preferences/proc/ZeroSkills(var/forced = 0) - for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V]) - if(!skills.Find(S.ID) || forced) - skills[S.ID] = SKILL_NONE - -/datum/preferences/proc/CalculateSkillPoints() - used_skillpoints = 0 - for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V]) - var/multiplier = 1 - switch(skills[S.ID]) - if(SKILL_NONE) - used_skillpoints += 0 * multiplier - if(SKILL_BASIC) - used_skillpoints += 1 * multiplier - if(SKILL_ADEPT) - // secondary skills cost less - if(S.secondary) - used_skillpoints += 1 * multiplier - else - used_skillpoints += 3 * multiplier - if(SKILL_EXPERT) - // secondary skills cost less - if(S.secondary) - used_skillpoints += 3 * multiplier - else - used_skillpoints += 6 * multiplier - -/datum/preferences/proc/GetSkillClass(points) - return CalculateSkillClass(points, age) - -/proc/CalculateSkillClass(points, age) - if(points <= 0) return "Unconfigured" - // skill classes describe how your character compares in total points - points -= min(round((age - 20) / 2.5), 4) // every 2.5 years after 20, one extra skillpoint - if(age > 30) - points -= round((age - 30) / 5) // every 5 years after 30, one extra skillpoint - switch(points) - if(-1000 to 3) - return "Terrifying" - if(4 to 6) - return "Below Average" - if(7 to 10) - return "Average" - if(11 to 14) - return "Above Average" - if(15 to 18) - return "Exceptional" - if(19 to 24) - return "Genius" - if(24 to 1000) - return "God" - -/datum/preferences/proc/ShowChoices(mob/user) - if(!user || !user.client) return - - if(!get_mob_by_key(client_ckey)) - to_chat(user, "No mob exists for the given client!") - return - - if(!char_render_holders) - update_preview_icon() - show_character_previews() - - var/dat = "
                    " - - if(path) - dat += "Slot - " - dat += "Load slot - " - dat += "Save slot - " - dat += "Reload slot - " - dat += "Reset slot - " - dat += "Copy slot" - - else - dat += "Please create an account to save your preferences." - - dat += "
                    " - dat += player_setup.header() - dat += "

                    " - dat += player_setup.content(user) - - dat += "" - //user << browse(dat, "window=preferences;size=635x736") - winshow(user, "preferences_window", TRUE) - var/datum/browser/popup = new(user, "preferences_browser", "Character Setup", 800, 800) - popup.set_content(dat) - popup.open(FALSE) // Skip registring onclose on the browser pane - onclose(user, "preferences_window", src) // We want to register on the window itself - -/datum/preferences/proc/update_character_previews(mutable_appearance/MA) - if(!client) - return - - var/obj/screen/setup_preview/pm_helper/PMH = LAZYACCESS(char_render_holders, "PMH") - if(!PMH) - PMH = new - LAZYSET(char_render_holders, "PMH", PMH) - client.screen |= PMH - PMH.screen_loc = preview_screen_locs["PMH"] - - var/obj/screen/setup_preview/bg/BG = LAZYACCESS(char_render_holders, "BG") - if(!BG) - BG = new - BG.plane = TURF_PLANE - BG.icon = 'icons/effects/setup_backgrounds_vr.dmi' - BG.pref = src - LAZYSET(char_render_holders, "BG", BG) - client.screen |= BG - BG.icon_state = bgstate - BG.screen_loc = preview_screen_locs["BG"] - - for(var/D in global.cardinal) - var/obj/screen/setup_preview/O = LAZYACCESS(char_render_holders, "[D]") - if(!O) - O = new - O.pref = src - LAZYSET(char_render_holders, "[D]", O) - client.screen |= O - O.appearance = MA - O.dir = D - O.screen_loc = preview_screen_locs["[D]"] - -/datum/preferences/proc/show_character_previews() - if(!client || !char_render_holders) - return - for(var/render_holder in char_render_holders) - client.screen |= char_render_holders[render_holder] - -/datum/preferences/proc/clear_character_previews() - for(var/index in char_render_holders) - var/obj/screen/S = char_render_holders[index] - client?.screen -= S - qdel(S) - char_render_holders = null - -/datum/preferences/proc/process_link(mob/user, list/href_list) - if(!user) return - - if(!istype(user, /mob/new_player)) return - - if(href_list["preference"] == "open_whitelist_forum") - if(config.forumurl) - user << link(config.forumurl) - else - to_chat(user, "The forum URL is not set in the server configuration.") - return - ShowChoices(usr) - return 1 - -/datum/preferences/Topic(href, list/href_list) - if(..()) - return 1 - - if(href_list["save"]) - save_preferences() - save_character() - else if(href_list["reload"]) - load_preferences() - load_character() - attempt_vr(client.prefs_vr,"load_vore","") //VOREStation Edit - sanitize_preferences() - else if(href_list["load"]) - if(!IsGuestKey(usr.key)) - open_load_dialog(usr) - return 1 - else if(href_list["resetslot"]) - if("No" == tgui_alert(usr, "This will reset the current slot. Continue?", "Reset current slot?", list("No", "Yes"))) - return 0 - if("No" == tgui_alert(usr, "Are you completely sure that you want to reset this character slot?", "Reset current slot?", list("No", "Yes"))) - return 0 - load_character(SAVE_RESET) - sanitize_preferences() - else if(href_list["copy"]) - if(!IsGuestKey(usr.key)) - open_copy_dialog(usr) - return 1 - else if(href_list["close"]) - // User closed preferences window, cleanup anything we need to. - clear_character_previews() - return 1 - else - return 0 - - ShowChoices(usr) - return 1 - -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = TRUE) - // Sanitizing rather than saving as someone might still be editing when copy_to occurs. - player_setup.sanitize_setup() - - // This needs to happen before anything else becuase it sets some variables. - character.set_species(species) - // Special Case: This references variables owned by two different datums, so do it here. - if(be_random_name) - real_name = random_name(identifying_gender,species) - - // Ask the preferences datums to apply their own settings to the new mob - player_setup.copy_to_mob(character) - - // VOREStation Edit - Sync up all their organs and species one final time - character.force_update_organs() - - if(icon_updates) - character.force_update_limbs() - character.update_icons_body() - character.update_mutations() - character.update_underwear() - character.update_hair() - - if(LAZYLEN(character.descriptors)) - for(var/entry in body_descriptors) - character.descriptors[entry] = body_descriptors[entry] - -/datum/preferences/proc/open_load_dialog(mob/user) - if(selecting_slots) - to_chat(user, "You already have a slot selection dialog open!") - return - var/savefile/S = new /savefile(path) - if(!S) - error("Somehow missing savefile path?! [path]") - return - - var/name - var/nickname //vorestation edit - This set appends nicknames to the save slot - var/list/charlist = list() - var/default //VOREStation edit - for(var/i=1, i<= config.character_slots, i++) - S.cd = "/character[i]" - S["real_name"] >> name - S["nickname"] >> nickname //vorestation edit - if(!name) - name = "[i] - \[Unused Slot\]" - else if(i == default_slot) - name = "â–º[i] - [name]" - else - name = "[i] - [name]" - if (i == default_slot) //VOREStation edit - default = "[name][nickname ? " ([nickname])" : ""]" - charlist["[name][nickname ? " ([nickname])" : ""]"] = i - - selecting_slots = TRUE - var/choice = tgui_input_list(user, "Select a character to load:", "Load Slot", charlist, default) - selecting_slots = FALSE - if(!choice) - return - - var/slotnum = charlist[choice] - if(!slotnum) - error("Player picked [choice] slot to load, but that wasn't one we sent.") - return - - load_character(slotnum) - attempt_vr(user.client?.prefs_vr,"load_vore","") //VOREStation Edit - sanitize_preferences() - ShowChoices(user) - -/datum/preferences/proc/open_copy_dialog(mob/user) - if(selecting_slots) - to_chat(user, "You already have a slot selection dialog open!") - return - var/savefile/S = new /savefile(path) - if(!S) - error("Somehow missing savefile path?! [path]") - return - - var/name - var/nickname //vorestation edit - This set appends nicknames to the save slot - var/list/charlist = list() - for(var/i=1, i<= config.character_slots, i++) - S.cd = "/character[i]" - S["real_name"] >> name - S["nickname"] >> nickname //vorestation edit - if(!name) - name = "[i] - \[Unused Slot\]" - if(i == default_slot) - name = "â–º[i] - [name]" - else - name = "[i] - [name]" - charlist["[name][nickname ? " ([nickname])" : ""]"] = i - - selecting_slots = TRUE - var/choice = tgui_input_list(user, "Select a character to COPY TO:", "Copy Slot", charlist) - selecting_slots = FALSE - if(!choice) - return - - var/slotnum = charlist[choice] - if(!slotnum) - error("Player picked [choice] slot to copy to, but that wasn't one we sent.") - return - - overwrite_character(slotnum) - sanitize_preferences() - ShowChoices(user) +#define SAVE_RESET -1 + +var/list/preferences_datums = list() + +/datum/preferences + //doohickeys for savefiles + var/path + var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used + var/savefile_version = 0 + + //non-preference stuff + var/warns = 0 + var/muted = 0 + var/last_ip + var/last_id + + //game-preferences + var/lastchangelog = "" //Saved changlog filesize to detect if there was a change + var/ooccolor = "#010000" //Whatever this is set to acts as 'reset' color and is thus unusable as an actual custom color + var/be_special = 0 //Special role selection + var/UI_style = "Midnight" + var/UI_style_color = "#ffffff" + var/UI_style_alpha = 255 + var/tooltipstyle = "Midnight" //Style for popup tooltips + var/client_fps = 40 + var/ambience_freq = 5 // How often we're playing repeating ambience to a client. + var/ambience_chance = 35 // What's the % chance we'll play ambience (in conjunction with the above frequency) + + var/tgui_fancy = TRUE + var/tgui_lock = FALSE + var/tgui_input_mode = FALSE // All the Input Boxes (Text,Number,List,Alert) + var/tgui_large_buttons = TRUE + var/tgui_swapped_buttons = FALSE + var/obfuscate_key = FALSE + var/obfuscate_job = FALSE + var/chat_timestamp = FALSE + var/throwmode_loud = FALSE + + //character preferences + var/real_name //our character's name + var/be_random_name = 0 //whether we are a random name every round + var/nickname //our character's nickname + var/age = 30 //age of character + var/bday_month = 0 //Birthday month + var/bday_day = 0 //Birthday day + var/last_birthday_notification = 0 //The last year we were notified about our birthday + var/bday_announce = FALSE //Public announcement for birthdays + var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2). + var/b_type = "A+" //blood type (not-chooseable) + var/blood_reagents = "default" //blood restoration reagents + var/backbag = 2 //backpack type + var/pdachoice = 1 //PDA type + var/h_style = "Bald" //Hair type + var/r_hair = 0 //Hair color + var/g_hair = 0 //Hair color + var/b_hair = 0 //Hair color + var/grad_style = "none" //Gradient style + var/r_grad = 0 //Gradient color + var/g_grad = 0 //Gradient color + var/b_grad = 0 //Gradient color + var/f_style = "Shaved" //Face hair type + var/r_facial = 0 //Face hair color + var/g_facial = 0 //Face hair color + var/b_facial = 0 //Face hair color + var/s_tone = -75 //Skin tone + var/r_skin = 238 //Skin color // Vorestation edit, so color multi sprites can aren't BLACK AS THE VOID by default. + var/g_skin = 206 //Skin color // Vorestation edit, so color multi sprites can aren't BLACK AS THE VOID by default. + var/b_skin = 179 //Skin color // Vorestation edit, so color multi sprites can aren't BLACK AS THE VOID by default. + var/r_eyes = 0 //Eye color + var/g_eyes = 0 //Eye color + var/b_eyes = 0 //Eye color + var/species = SPECIES_HUMAN //Species datum to use. + var/species_preview //Used for the species selection window. + var/list/alternate_languages = list() //Secondary language(s) + var/list/language_prefixes = list() //Language prefix keys + var/list/language_custom_keys = list() //Language custom call keys + var/list/gear //Left in for Legacy reasons, will no longer save. + var/list/gear_list = list() //Custom/fluff item loadouts. + var/gear_slot = 1 //The current gear save slot + var/list/traits //Traits which modifier characters for better or worse (mostly worse). + var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. + var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. + var/g_synth //Same as above + var/b_synth //Same as above + var/synth_markings = 1 //Enable/disable markings on synth parts. //VOREStation Edit - 1 by default + var/digitigrade = 0 + + //Some faction information. + var/home_system = "Unset" //Current home or residence. + var/birthplace = "Unset" //Location of birth. + var/citizenship = "None" //Government or similar entity with which you hold citizenship. + var/faction = "None" //General associated faction. + var/religion = "None" //Religious association. + var/antag_faction = "None" //Antag associated faction. + var/antag_vis = "Hidden" //How visible antag association is to others. + + //Mob preview + var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen. + var/static/list/preview_screen_locs = list( + "1" = "character_preview_map:2,7", + "2" = "character_preview_map:2,5", + "4" = "character_preview_map:2,3", + "8" = "character_preview_map:2,1", + "BG" = "character_preview_map:1,1 to 3,8", + "PMH" = "character_preview_map:2,7" + ) + + //Jobs, uses bitflags + var/job_civilian_high = 0 + var/job_civilian_med = 0 + var/job_civilian_low = 0 + + var/job_medsci_high = 0 + var/job_medsci_med = 0 + var/job_medsci_low = 0 + + var/job_engsec_high = 0 + var/job_engsec_med = 0 + var/job_engsec_low = 0 + + //Keeps track of preferrence for not getting any wanted jobs + var/alternate_option = 1 + + var/used_skillpoints = 0 + var/skill_specialization = null + var/list/skills = list() // skills can range from 0 to 3 + + // maps each organ to either null(intact), "cyborg" or "amputated" + // will probably not be able to do this for head and torso ;) + var/list/organ_data = list() + var/list/rlimb_data = list() + var/list/player_alt_titles = new() // the default name of a job like "Medical Doctor" + + var/list/body_markings = list() // "name" = "#rgbcolor" //VOREStation Edit: "name" = list(BP_HEAD = list("on" = , "color" = "#rgbcolor"), BP_TORSO = ...) + + var/list/flavor_texts = list() + var/list/flavour_texts_robot = list() + var/custom_link = null + + var/list/body_descriptors = list() + + var/med_record = "" + var/sec_record = "" + var/gen_record = "" + var/exploit_record = "" + var/disabilities = 0 + + var/economic_status = "Average" + + var/uplinklocation = "PDA" + + // OOC Metadata: + var/metadata = "" + var/metadata_likes = "" + var/metadata_dislikes = "" + var/list/ignored_players = list() + + var/client/client = null + var/client_ckey = null + + // Communicator identity data + var/communicator_visibility = 0 + + /// Default ringtone for character; if blank, use job default. + var/ringtone = null + + var/datum/category_collection/player_setup_collection/player_setup + var/datum/browser/panel + + var/lastnews // Hash of last seen lobby news content. + var/lastlorenews //ID of last seen lore news article. + + var/examine_text_mode = 0 // Just examine text, include usage (description_info), switch to examine panel. + var/multilingual_mode = 0 // Default behaviour, delimiter-key-space, delimiter-key-delimiter, off + + var/list/volume_channels = list() + + ///If they are currently in the process of swapping slots, don't let them open 999 windows for it and get confused + var/selecting_slots = FALSE + + +/datum/preferences/New(client/C) + player_setup = new(src) + set_biological_gender(pick(MALE, FEMALE)) + real_name = random_name(identifying_gender,species) + b_type = RANDOM_BLOOD_TYPE + + gear = list() + gear_list = list() + gear_slot = 1 + + if(istype(C)) + client = C + client_ckey = C.ckey + if(!IsGuestKey(C.key)) + load_path(C.ckey) + if(load_preferences()) + load_character() + + +/datum/preferences/Destroy() + . = ..() + QDEL_LIST_ASSOC_VAL(char_render_holders) + +/datum/preferences/proc/ZeroSkills(var/forced = 0) + for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V]) + if(!skills.Find(S.ID) || forced) + skills[S.ID] = SKILL_NONE + +/datum/preferences/proc/CalculateSkillPoints() + used_skillpoints = 0 + for(var/V in SKILLS) for(var/datum/skill/S in SKILLS[V]) + var/multiplier = 1 + switch(skills[S.ID]) + if(SKILL_NONE) + used_skillpoints += 0 * multiplier + if(SKILL_BASIC) + used_skillpoints += 1 * multiplier + if(SKILL_ADEPT) + // secondary skills cost less + if(S.secondary) + used_skillpoints += 1 * multiplier + else + used_skillpoints += 3 * multiplier + if(SKILL_EXPERT) + // secondary skills cost less + if(S.secondary) + used_skillpoints += 3 * multiplier + else + used_skillpoints += 6 * multiplier + +/datum/preferences/proc/GetSkillClass(points) + return CalculateSkillClass(points, age) + +/proc/CalculateSkillClass(points, age) + if(points <= 0) return "Unconfigured" + // skill classes describe how your character compares in total points + points -= min(round((age - 20) / 2.5), 4) // every 2.5 years after 20, one extra skillpoint + if(age > 30) + points -= round((age - 30) / 5) // every 5 years after 30, one extra skillpoint + switch(points) + if(-1000 to 3) + return "Terrifying" + if(4 to 6) + return "Below Average" + if(7 to 10) + return "Average" + if(11 to 14) + return "Above Average" + if(15 to 18) + return "Exceptional" + if(19 to 24) + return "Genius" + if(24 to 1000) + return "God" + +/datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) return + + if(!get_mob_by_key(client_ckey)) + to_chat(user, "No mob exists for the given client!") + return + + if(!char_render_holders) + update_preview_icon() + show_character_previews() + + var/dat = "
                    " + + if(path) + dat += "Slot - " + dat += "Load slot - " + dat += "Save slot - " + dat += "Reload slot - " + dat += "Reset slot - " + dat += "Copy slot" + + else + dat += "Please create an account to save your preferences." + + dat += "
                    " + dat += player_setup.header() + dat += "

                    " + dat += player_setup.content(user) + + dat += "" + //user << browse(dat, "window=preferences;size=635x736") + winshow(user, "preferences_window", TRUE) + var/datum/browser/popup = new(user, "preferences_browser", "Character Setup", 800, 800) + popup.set_content(dat) + popup.open(FALSE) // Skip registring onclose on the browser pane + onclose(user, "preferences_window", src) // We want to register on the window itself + +/datum/preferences/proc/update_character_previews(mutable_appearance/MA) + if(!client) + return + + var/obj/screen/setup_preview/pm_helper/PMH = LAZYACCESS(char_render_holders, "PMH") + if(!PMH) + PMH = new + LAZYSET(char_render_holders, "PMH", PMH) + client.screen |= PMH + PMH.screen_loc = preview_screen_locs["PMH"] + + var/obj/screen/setup_preview/bg/BG = LAZYACCESS(char_render_holders, "BG") + if(!BG) + BG = new + BG.plane = TURF_PLANE + BG.icon = 'icons/effects/setup_backgrounds_vr.dmi' + BG.pref = src + LAZYSET(char_render_holders, "BG", BG) + client.screen |= BG + BG.icon_state = bgstate + BG.screen_loc = preview_screen_locs["BG"] + + for(var/D in global.cardinal) + var/obj/screen/setup_preview/O = LAZYACCESS(char_render_holders, "[D]") + if(!O) + O = new + O.pref = src + LAZYSET(char_render_holders, "[D]", O) + client.screen |= O + O.appearance = MA + O.dir = D + O.screen_loc = preview_screen_locs["[D]"] + +/datum/preferences/proc/show_character_previews() + if(!client || !char_render_holders) + return + for(var/render_holder in char_render_holders) + client.screen |= char_render_holders[render_holder] + +/datum/preferences/proc/clear_character_previews() + for(var/index in char_render_holders) + var/obj/screen/S = char_render_holders[index] + client?.screen -= S + qdel(S) + char_render_holders = null + +/datum/preferences/proc/process_link(mob/user, list/href_list) + if(!user) return + + if(!istype(user, /mob/new_player)) return + + if(href_list["preference"] == "open_whitelist_forum") + if(config.forumurl) + user << link(config.forumurl) + else + to_chat(user, "The forum URL is not set in the server configuration.") + return + ShowChoices(usr) + return 1 + +/datum/preferences/Topic(href, list/href_list) + if(..()) + return 1 + + if(href_list["save"]) + save_preferences() + save_character() + else if(href_list["reload"]) + load_preferences() + load_character() + attempt_vr(client.prefs_vr,"load_vore","") //VOREStation Edit + sanitize_preferences() + else if(href_list["load"]) + if(!IsGuestKey(usr.key)) + open_load_dialog(usr) + return 1 + else if(href_list["resetslot"]) + if("Yes" != tgui_alert(usr, "This will reset the current slot. Continue?", "Reset current slot?", list("No", "Yes"))) + return 0 + if("Yes" != tgui_alert(usr, "Are you completely sure that you want to reset this character slot?", "Reset current slot?", list("No", "Yes"))) + return 0 + load_character(SAVE_RESET) + sanitize_preferences() + else if(href_list["copy"]) + if(!IsGuestKey(usr.key)) + open_copy_dialog(usr) + return 1 + else if(href_list["close"]) + // User closed preferences window, cleanup anything we need to. + clear_character_previews() + return 1 + else + return 0 + + ShowChoices(usr) + return 1 + +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = TRUE) + // Sanitizing rather than saving as someone might still be editing when copy_to occurs. + player_setup.sanitize_setup() + + // This needs to happen before anything else becuase it sets some variables. + character.set_species(species) + // Special Case: This references variables owned by two different datums, so do it here. + if(be_random_name) + real_name = random_name(identifying_gender,species) + + // Ask the preferences datums to apply their own settings to the new mob + player_setup.copy_to_mob(character) + + // VOREStation Edit - Sync up all their organs and species one final time + character.force_update_organs() + + if(icon_updates) + character.force_update_limbs() + character.update_icons_body() + character.update_mutations() + character.update_underwear() + character.update_hair() + + if(LAZYLEN(character.descriptors)) + for(var/entry in body_descriptors) + character.descriptors[entry] = body_descriptors[entry] + +/datum/preferences/proc/open_load_dialog(mob/user) + if(selecting_slots) + to_chat(user, "You already have a slot selection dialog open!") + return + var/savefile/S = new /savefile(path) + if(!S) + error("Somehow missing savefile path?! [path]") + return + + var/name + var/nickname //vorestation edit - This set appends nicknames to the save slot + var/list/charlist = list() + var/default //VOREStation edit + for(var/i=1, i<= config.character_slots, i++) + S.cd = "/character[i]" + S["real_name"] >> name + S["nickname"] >> nickname //vorestation edit + if(!name) + name = "[i] - \[Unused Slot\]" + else if(i == default_slot) + name = "►[i] - [name]" + else + name = "[i] - [name]" + if (i == default_slot) //VOREStation edit + default = "[name][nickname ? " ([nickname])" : ""]" + charlist["[name][nickname ? " ([nickname])" : ""]"] = i + + selecting_slots = TRUE + var/choice = tgui_input_list(user, "Select a character to load:", "Load Slot", charlist, default) + selecting_slots = FALSE + if(!choice) + return + + var/slotnum = charlist[choice] + if(!slotnum) + error("Player picked [choice] slot to load, but that wasn't one we sent.") + return + + load_character(slotnum) + attempt_vr(user.client?.prefs_vr,"load_vore","") //VOREStation Edit + sanitize_preferences() + ShowChoices(user) + +/datum/preferences/proc/open_copy_dialog(mob/user) + if(selecting_slots) + to_chat(user, "You already have a slot selection dialog open!") + return + var/savefile/S = new /savefile(path) + if(!S) + error("Somehow missing savefile path?! [path]") + return + + var/name + var/nickname //vorestation edit - This set appends nicknames to the save slot + var/list/charlist = list() + for(var/i=1, i<= config.character_slots, i++) + S.cd = "/character[i]" + S["real_name"] >> name + S["nickname"] >> nickname //vorestation edit + if(!name) + name = "[i] - \[Unused Slot\]" + if(i == default_slot) + name = "►[i] - [name]" + else + name = "[i] - [name]" + charlist["[name][nickname ? " ([nickname])" : ""]"] = i + + selecting_slots = TRUE + var/choice = tgui_input_list(user, "Select a character to COPY TO:", "Copy Slot", charlist) + selecting_slots = FALSE + if(!choice) + return + + var/slotnum = charlist[choice] + if(!slotnum) + error("Player picked [choice] slot to copy to, but that wasn't one we sent.") + return + + overwrite_character(slotnum) + sanitize_preferences() + ShowChoices(user) diff --git a/code/modules/client/preferences_factions.dm b/code/modules/client/preferences_factions.dm index f0f357132fe..8eb411d1a20 100644 --- a/code/modules/client/preferences_factions.dm +++ b/code/modules/client/preferences_factions.dm @@ -62,7 +62,10 @@ var/global/list/home_system_choices = list( "Abundance in All Things Serene, Beta-Carnelium Ventrum", "Jorhul, Barkalis", "Shelf Flotilla", - "Ue-Orsi Flotilla" + "Ue-Orsi Flotilla", + "AH-CV Prosperity", + "AH-CV Migrant", + "Altevian Colony Ship" ) var/global/list/faction_choices = list( diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 47b846efc1f..e98e7de3520 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,121 +1,121 @@ -#define SAVEFILE_VERSION_MIN 8 -#define SAVEFILE_VERSION_MAX 11 - -//handles converting savefiles to new formats -//MAKE SURE YOU KEEP THIS UP TO DATE! -//If the sanity checks are capable of handling any issues. Only increase SAVEFILE_VERSION_MAX, -//this will mean that savefile_version will still be over SAVEFILE_VERSION_MIN, meaning -//this savefile update doesn't run everytime we load from the savefile. -//This is mainly for format changes, such as the bitflags in toggles changing order or something. -//if a file can't be updated, return 0 to delete it and start again -//if a file was updated, return 1 -/datum/preferences/proc/savefile_update() - if(savefile_version < 8) //lazily delete everything + additional files so they can be saved in the new format - for(var/ckey in preferences_datums) - var/datum/preferences/D = preferences_datums[ckey] - if(D == src) - var/delpath = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/" - if(delpath && fexists(delpath)) - fdel(delpath) - break - return 0 - - if(savefile_version == SAVEFILE_VERSION_MAX) //update successful. - save_preferences() - save_character() - return 1 - return 0 - -/datum/preferences/proc/load_path(ckey,filename="preferences.sav") - if(!ckey) return - path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]" - savefile_version = SAVEFILE_VERSION_MAX - -/datum/preferences/proc/load_preferences() - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - - S["version"] >> savefile_version - //Conversion - if(!savefile_version || !isnum(savefile_version) || savefile_version < SAVEFILE_VERSION_MIN || savefile_version > SAVEFILE_VERSION_MAX) - if(!savefile_update()) //handles updates - savefile_version = SAVEFILE_VERSION_MAX - save_preferences() - save_character() - return 0 - - player_setup.load_preferences(S) - return 1 - -/datum/preferences/proc/save_preferences() - if(!path) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - - S["version"] << savefile_version - player_setup.save_preferences(S) - return 1 - -/datum/preferences/proc/load_character(slot) - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/" - if(!slot) slot = default_slot - if(slot != SAVE_RESET) // SAVE_RESET will reset the slot as though it does not exist, but keep the current slot for saving purposes. - slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) - if(slot != default_slot) - default_slot = slot - S["default_slot"] << slot - else - S["default_slot"] << default_slot - - if(slot != SAVE_RESET) - S.cd = "/character[slot]" - player_setup.load_character(S) - else - player_setup.load_character(S) - S.cd = "/character[default_slot]" - player_setup.save_character(S) - - clear_character_previews() // VOREStation Edit - return 1 - -/datum/preferences/proc/save_character() - if(!path) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - S.cd = "/character[default_slot]" - - player_setup.save_character(S) - return 1 - -/datum/preferences/proc/overwrite_character(slot) - if(!path) return 0 - if(!fexists(path)) return 0 - var/savefile/S = new /savefile(path) - if(!S) return 0 - if(!slot) slot = default_slot - if(slot != SAVE_RESET) - slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) - if(slot != default_slot) - default_slot = slot - nif_path = nif_durability = nif_savedata = null //VOREStation Add - Don't copy NIF - S["default_slot"] << slot - - else - S["default_slot"] << default_slot - - return 1 - -/datum/preferences/proc/sanitize_preferences() - player_setup.sanitize_setup() - return 1 - -#undef SAVEFILE_VERSION_MAX -#undef SAVEFILE_VERSION_MIN +#define SAVEFILE_VERSION_MIN 8 +#define SAVEFILE_VERSION_MAX 11 + +//handles converting savefiles to new formats +//MAKE SURE YOU KEEP THIS UP TO DATE! +//If the sanity checks are capable of handling any issues. Only increase SAVEFILE_VERSION_MAX, +//this will mean that savefile_version will still be over SAVEFILE_VERSION_MIN, meaning +//this savefile update doesn't run everytime we load from the savefile. +//This is mainly for format changes, such as the bitflags in toggles changing order or something. +//if a file can't be updated, return 0 to delete it and start again +//if a file was updated, return 1 +/datum/preferences/proc/savefile_update() + if(savefile_version < 8) //lazily delete everything + additional files so they can be saved in the new format + for(var/ckey in preferences_datums) + var/datum/preferences/D = preferences_datums[ckey] + if(D == src) + var/delpath = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/" + if(delpath && fexists(delpath)) + fdel(delpath) + break + return 0 + + if(savefile_version == SAVEFILE_VERSION_MAX) //update successful. + save_preferences() + save_character() + return 1 + return 0 + +/datum/preferences/proc/load_path(ckey,filename="preferences.sav") + if(!ckey) return + path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]" + savefile_version = SAVEFILE_VERSION_MAX + +/datum/preferences/proc/load_preferences() + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + + S["version"] >> savefile_version + //Conversion + if(!savefile_version || !isnum(savefile_version) || savefile_version < SAVEFILE_VERSION_MIN || savefile_version > SAVEFILE_VERSION_MAX) + if(!savefile_update()) //handles updates + savefile_version = SAVEFILE_VERSION_MAX + save_preferences() + save_character() + return 0 + + player_setup.load_preferences(S) + return 1 + +/datum/preferences/proc/save_preferences() + if(!path) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + + S["version"] << savefile_version + player_setup.save_preferences(S) + return 1 + +/datum/preferences/proc/load_character(slot) + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/" + if(!slot) slot = default_slot + if(slot != SAVE_RESET) // SAVE_RESET will reset the slot as though it does not exist, but keep the current slot for saving purposes. + slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) + if(slot != default_slot) + default_slot = slot + S["default_slot"] << slot + else + S["default_slot"] << default_slot + + if(slot != SAVE_RESET) + S.cd = "/character[slot]" + player_setup.load_character(S) + else + player_setup.load_character(S) + S.cd = "/character[default_slot]" + player_setup.save_character(S) + + clear_character_previews() // VOREStation Edit + return 1 + +/datum/preferences/proc/save_character() + if(!path) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + S.cd = "/character[default_slot]" + + player_setup.save_character(S) + return 1 + +/datum/preferences/proc/overwrite_character(slot) + if(!path) return 0 + if(!fexists(path)) return 0 + var/savefile/S = new /savefile(path) + if(!S) return 0 + if(!slot) slot = default_slot + if(slot != SAVE_RESET) + slot = sanitize_integer(slot, 1, config.character_slots, initial(default_slot)) + if(slot != default_slot) + default_slot = slot + nif_path = nif_durability = nif_savedata = null //VOREStation Add - Don't copy NIF + S["default_slot"] << slot + + else + S["default_slot"] << default_slot + + return 1 + +/datum/preferences/proc/sanitize_preferences() + player_setup.sanitize_setup() + return 1 + +#undef SAVEFILE_VERSION_MAX +#undef SAVEFILE_VERSION_MIN diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm index dff1c3f5a2f..9e9f00c6594 100644 --- a/code/modules/client/preferences_toggle_procs.dm +++ b/code/modules/client/preferences_toggle_procs.dm @@ -367,28 +367,17 @@ feedback_add_details("admin_verb","THInstm") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/verb/toggle_vchat() - set name = "Toggle VChat" + set name = "Toggle TGChat" set category = "Preferences" - set desc = "Toggles VChat. Reloading VChat and/or reconnecting required to affect changes." + set desc = "Toggles TGChat. Reloading TGChat and/or reconnecting required to affect changes." var/pref_path = /datum/client_preference/vchat_enable toggle_preference(pref_path) SScharacter_setup.queue_preferences_save(prefs) - to_chat(src, "You have toggled VChat [is_preference_enabled(pref_path) ? "on" : "off"]. \ - You will have to reload VChat and/or reconnect to the server for these changes to take place. \ - VChat message persistence is not guaranteed if you change this again before the start of the next round.") - -/client/verb/toggle_tgui_inputlock() - set name = "Toggle TGUI Input Lock" - set category = "Preferences" - set desc = "Toggles whether or not pressing the 'Enter' key in TGUI input sends the message or creates a new line." - - prefs.tgui_input_lock = !prefs.tgui_input_lock //There is no preference datum for tgui input lock, nor for any TGUI prefs. - SScharacter_setup.queue_preferences_save(prefs) - - to_chat(src, span_notice("You have toggled TGUI input lock: [prefs.tgui_input_lock ? "ON" : "OFF"] \n \ - This setting determines whether pressing enter on TGUI input sends the input, or creates a newline.")) + to_chat(src, "You have toggled TGChat [is_preference_enabled(pref_path) ? "on" : "off"]. \ + You will have to reload TGChat and/or reconnect to the server for these changes to take place. \ + TGChat message persistence is not guaranteed if you change this again before the start of the next round.") /client/verb/toggle_chat_timestamps() set name = "Toggle Chat Timestamps" @@ -400,6 +389,16 @@ to_chat(src, span_notice("You have toggled chat timestamps: [prefs.chat_timestamp ? "ON" : "OFF"].")) +/client/verb/toggle_throwmode_messages() + set name = "Toggle Throw Mode Messages" + set category = "Preferences" + set desc = "Toggles whether or not activating throw mode (hotkey: R) will announce you're preparing to throw your current handheld item, or catch an incoming item if your hand is empty." + + prefs.throwmode_loud = !prefs.throwmode_loud //There is no preference datum for tgui input lock, nor for any TGUI prefs. + SScharacter_setup.queue_preferences_save(prefs) + + to_chat(src, span_notice("You have toggled throw mode messages: [prefs.throwmode_loud ? "ON" : "OFF"].")) + /client/verb/toggle_status_indicators() set name = "Toggle Status Indicators" set category = "Preferences" @@ -479,6 +478,19 @@ feedback_add_details("admin_verb","TSubtleSounds") +/client/verb/toggle_vore_health_bars() + set name = "Toggle Vore Health Bars" + set category = "Preferences" + set desc = "Toggle the display of vore related health bars" + + var/pref_path = /datum/client_preference/vore_health_bars + toggle_preference(pref_path) + SScharacter_setup.queue_preferences_save(prefs) + + to_chat(src, "Vore related health bars - [(is_preference_enabled(/datum/client_preference/vore_health_bars)) ? "Enabled" : "Disabled"]") + + feedback_add_details("admin_verb","TVoreHealthBars") + // Not attached to a pref datum because those are strict binary toggles /client/verb/toggle_examine_mode() set name = "Toggle Examine Mode" diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm index 5de49fd1c15..99e81035092 100644 --- a/code/modules/client/preferences_vr.dm +++ b/code/modules/client/preferences_vr.dm @@ -88,6 +88,21 @@ feedback_add_details("admin_verb","TWhisubtleVis") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/verb/toggle_ghost_privacyvision() + set name = "Toggle Ghost Private Eyes/ears" + set category = "Preferences" + set desc = "Toggles your ability to see subtles/whispers. Overrides admin status. Respects Ghost Privacy" + + var/pref_path = /datum/client_preference/ghost_see_whisubtle + + toggle_preference(pref_path) + + to_chat(src, "As a ghost, you will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear subtles/whispers made by players.") + + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TGhostSeeWhisSubtle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /client/verb/toggle_capture_crystal() set name = "Toggle Catchable" set category = "Preferences" diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm index 8317d977e3a..eb6cc2ffdd4 100644 --- a/code/modules/client/ui_style.dm +++ b/code/modules/client/ui_style.dm @@ -1,81 +1,81 @@ - - -/var/all_ui_styles = list( - "Midnight" = 'icons/mob/screen/midnight.dmi', - "Orange" = 'icons/mob/screen/orange.dmi', - "old" = 'icons/mob/screen/old.dmi', - "White" = 'icons/mob/screen/white.dmi', - "old-noborder" = 'icons/mob/screen/old-noborder.dmi', - "minimalist" = 'icons/mob/screen/minimalist.dmi', - "Hologram" = 'icons/mob/screen/holo.dmi' - ) - -/var/all_ui_styles_robot = list( - "Midnight" = 'icons/mob/screen1_robot.dmi', - "Orange" = 'icons/mob/screen1_robot.dmi', - "old" = 'icons/mob/screen1_robot.dmi', - "White" = 'icons/mob/screen1_robot.dmi', - "old-noborder" = 'icons/mob/screen1_robot.dmi', - "minimalist" = 'icons/mob/screen1_robot_minimalist.dmi', - "Hologram" = 'icons/mob/screen1_robot_minimalist.dmi' - ) - -var/global/list/all_tooltip_styles = list( - "Midnight", //Default for everyone is the first one, - "Plasmafire", - "Retro", - "Slimecore", - "Operative", - "Clockwork" - ) - -/proc/ui_style2icon(ui_style) - if(ui_style in all_ui_styles) - return all_ui_styles[ui_style] - return all_ui_styles["White"] - - -/client/verb/change_ui() - set name = "Change UI" - set category = "Preferences" - set desc = "Configure your user interface" - - if(!ishuman(usr)) - if(!isrobot(usr)) - to_chat(usr, "You must be a human or a robot to use this verb.") - return - - var/UI_style_new = tgui_input_list(usr, "Select a style. White is recommended for customization", "UI Style Choice", all_ui_styles) - if(!UI_style_new) return - - var/UI_style_alpha_new = tgui_input_number(usr, "Select a new alpha (transparency) parameter for your UI, between 50 and 255", null, null, 255, 50) - if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) return - - var/UI_style_color_new = input(usr, "Choose your UI color. Dark colors are not recommended!") as color|null - if(!UI_style_color_new) return - - //update UI - var/list/icons = usr.hud_used.adding + usr.hud_used.other + usr.hud_used.hotkeybuttons - icons.Add(usr.zone_sel) - icons.Add(usr.gun_setting_icon) - icons.Add(usr.item_use_icon) - icons.Add(usr.gun_move_icon) - icons.Add(usr.radio_use_icon) - - var/icon/ic = all_ui_styles[UI_style_new] - if(isrobot(usr)) - ic = all_ui_styles_robot[UI_style_new] - - for(var/obj/screen/I in icons) - if(I.name in list(I_HELP, I_HURT, I_DISARM, I_GRAB)) continue - I.icon = ic - I.color = UI_style_color_new - I.alpha = UI_style_alpha_new - - - if(tgui_alert(usr, "Like it? Save changes?","Save?",list("Yes", "No")) == "Yes") - prefs.UI_style = UI_style_new - prefs.UI_style_alpha = UI_style_alpha_new - prefs.UI_style_color = UI_style_color_new - SScharacter_setup.queue_preferences_save(prefs) - to_chat(usr, "UI was saved") + + +/var/all_ui_styles = list( + "Midnight" = 'icons/mob/screen/midnight.dmi', + "Orange" = 'icons/mob/screen/orange.dmi', + "old" = 'icons/mob/screen/old.dmi', + "White" = 'icons/mob/screen/white.dmi', + "old-noborder" = 'icons/mob/screen/old-noborder.dmi', + "minimalist" = 'icons/mob/screen/minimalist.dmi', + "Hologram" = 'icons/mob/screen/holo.dmi' + ) + +/var/all_ui_styles_robot = list( + "Midnight" = 'icons/mob/screen1_robot.dmi', + "Orange" = 'icons/mob/screen1_robot.dmi', + "old" = 'icons/mob/screen1_robot.dmi', + "White" = 'icons/mob/screen1_robot.dmi', + "old-noborder" = 'icons/mob/screen1_robot.dmi', + "minimalist" = 'icons/mob/screen1_robot_minimalist.dmi', + "Hologram" = 'icons/mob/screen1_robot_minimalist.dmi' + ) + +var/global/list/all_tooltip_styles = list( + "Midnight", //Default for everyone is the first one, + "Plasmafire", + "Retro", + "Slimecore", + "Operative", + "Clockwork" + ) + +/proc/ui_style2icon(ui_style) + if(ui_style in all_ui_styles) + return all_ui_styles[ui_style] + return all_ui_styles["White"] + + +/client/verb/change_ui() + set name = "Change UI" + set category = "Preferences" + set desc = "Configure your user interface" + + if(!ishuman(usr)) + if(!isrobot(usr)) + to_chat(usr, "You must be a human or a robot to use this verb.") + return + + var/UI_style_new = tgui_input_list(usr, "Select a style. White is recommended for customization", "UI Style Choice", all_ui_styles) + if(!UI_style_new) return + + var/UI_style_alpha_new = tgui_input_number(usr, "Select a new alpha (transparency) parameter for your UI, between 50 and 255", null, null, 255, 50) + if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) return + + var/UI_style_color_new = input(usr, "Choose your UI color. Dark colors are not recommended!") as color|null + if(!UI_style_color_new) return + + //update UI + var/list/icons = usr.hud_used.adding + usr.hud_used.other + usr.hud_used.hotkeybuttons + icons.Add(usr.zone_sel) + icons.Add(usr.gun_setting_icon) + icons.Add(usr.item_use_icon) + icons.Add(usr.gun_move_icon) + icons.Add(usr.radio_use_icon) + + var/icon/ic = all_ui_styles[UI_style_new] + if(isrobot(usr)) + ic = all_ui_styles_robot[UI_style_new] + + for(var/obj/screen/I in icons) + if(I.name in list(I_HELP, I_HURT, I_DISARM, I_GRAB)) continue + I.icon = ic + I.color = UI_style_color_new + I.alpha = UI_style_alpha_new + + + if(tgui_alert(usr, "Like it? Save changes?","Save?",list("Yes", "No")) == "Yes") + prefs.UI_style = UI_style_new + prefs.UI_style_alpha = UI_style_alpha_new + prefs.UI_style_color = UI_style_color_new + SScharacter_setup.queue_preferences_save(prefs) + to_chat(usr, "UI was saved") diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index d2165eec0cc..04c2fac3fac 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -68,6 +68,7 @@ if(holder.rights & R_ADMIN && holder.rights & R_BAN) //Admins ooc_style = "admin" + msg = GLOB.is_valid_url.Replace(msg,"$1") for(var/client/target in GLOB.clients) if(target.is_preference_enabled(/datum/client_preference/show_ooc)) @@ -81,7 +82,7 @@ else display_name = holder.fakekey if(holder && !holder.fakekey && (holder.rights & R_ADMIN|R_FUN|R_EVENT) && config.allow_admin_ooccolor && (src.prefs.ooccolor != initial(src.prefs.ooccolor))) // keeping this for the badmins - to_chat(target, "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]") + to_chat(target, "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]") else to_chat(target, "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]") @@ -175,6 +176,8 @@ if(!(admin in receivers) && admin.is_preference_enabled(/datum/client_preference/holder/show_rlooc)) r_receivers |= admin + msg = GLOB.is_valid_url.Replace(msg,"$1") + // Send a message for(var/client/target in receivers) var/admin_stuff = "" diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm index a9e4c07f801..b00d2bb8965 100644 --- a/code/modules/client/verbs/who.dm +++ b/code/modules/client/verbs/who.dm @@ -16,29 +16,29 @@ entry += " - Playing as [C.mob.real_name]" switch(C.mob.stat) if(UNCONSCIOUS) - entry += " - Unconscious" + entry += " - [span_darkgray("Unconscious")]" if(DEAD) if(isobserver(C.mob)) var/mob/observer/dead/O = C.mob if(O.started_as_observer) - entry += " - Observing" + entry += " - [span_gray("Observing")]" else - entry += " - DEAD" + entry += " - [span_black("DEAD")]" else - entry += " - DEAD" + entry += " - [span_black("DEAD")]" if(C.player_age != initial(C.player_age) && isnum(C.player_age)) // database is on var/age = C.player_age switch(age) if(0 to 1) - age = "[age] days old" + age = span_red("[age] days old") if(1 to 10) - age = "[age] days old" + age = span_orange("[age] days old") else entry += " - [age] days old" if(is_special_character(C.mob)) - entry += " - Antagonist" + entry += " - [span_red("Antagonist")]" if(C.is_afk()) var/seconds = C.last_activity_seconds() diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 749ebe9b1a5..82e6de0baf0 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -23,6 +23,10 @@ var/polychromic = FALSE //VOREStation edit + var/update_icon_define_orig = null // temp storage for original update_icon_define (if it exists) + var/update_icon_define_digi = null // dmi used for the digi sprites + var/fit_for_digi = FALSE // flag for if clothing has already been reskinned to digitigrade + //Updates the icons of the mob wearing the clothing item, if any. /obj/item/clothing/proc/update_clothing_icon() return @@ -342,7 +346,7 @@ return 0 // return 1 to cancel attack_hand() /*/obj/item/clothing/gloves/attackby(obj/item/weapon/W, mob/user) - if(W.is_wirecutter() || istype(W, /obj/item/weapon/scalpel)) + if(W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/weapon/scalpel)) if (clipped) to_chat(user, "The [src] have already been clipped!") update_icon() @@ -621,6 +625,8 @@ drop_sound = 'sound/items/drop/shoes.ogg' pickup_sound = 'sound/items/pickup/shoes.ogg' + update_icon_define_digi = "icons/inventory/feet/mob_digi.dmi" + /obj/item/clothing/shoes/proc/draw_knife() set name = "Draw Boot Knife" set desc = "Pull out your boot knife." @@ -745,6 +751,8 @@ valid_accessory_slots = (ACCESSORY_SLOT_OVER | ACCESSORY_SLOT_ARMBAND) restricted_accessory_slots = (ACCESSORY_SLOT_ARMBAND) + update_icon_define_digi = "icons/inventory/suit/mob_digi.dmi" + /obj/item/clothing/suit/update_clothing_icon() if (ismob(src.loc)) var/mob/M = src.loc @@ -854,6 +862,8 @@ var/icon/rolled_down_icon = 'icons/inventory/uniform/mob_rolled_down.dmi' var/icon/rolled_down_sleeves_icon = 'icons/inventory/uniform/mob_sleeves_rolled.dmi' + update_icon_define_digi = "icons/inventory/uniform/mob_digi.dmi" + /obj/item/clothing/under/attack_hand(var/mob/user) if(LAZYLEN(accessories)) ..() @@ -1059,3 +1069,49 @@ M.forceMove(get_turf(src)) return ..() //Vorestation edit end + +/obj/item/clothing/proc/handle_digitigrade(var/mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + + // if digitigrade-use flag is set + if(H.digitigrade) + + // Don't reset if already set + if(!fit_for_digi) + fit_for_digi = TRUE // set flag even if no icon_state exists, so we don't repeat checks + + //if update_icon_define is already set to something, place it in a var to hold it temporarily + if(update_icon_define) + update_icon_define_orig = update_icon_define + + // only override icon if a corresponding digitigrade replacement icon_state exists + // otherwise, keep the old non-digi icon_define (or nothing) + if(icon_state && icon_states(update_icon_define_digi).Find(icon_state)) + update_icon_define = update_icon_define_digi + + + // if not-digitigrade, only act if the clothing was previously fit for a digitigrade char + else + if(fit_for_digi) + fit_for_digi = FALSE + + //either reset update_icon_define to it's old value + // or reset update_icon_define to null + if(update_icon_define_orig) + update_icon_define = update_icon_define_orig + update_icon_define_orig = null + else + update_icon_define = null + +/obj/item/clothing/shoes/equipped(var/mob/user, var/slot) + . = ..() + handle_digitigrade(user) + +/obj/item/clothing/suit/equipped(var/mob/user, var/slot) + . = ..() + handle_digitigrade(user) + +/obj/item/clothing/under/equipped(var/mob/user, var/slot) + . = ..() + handle_digitigrade(user) diff --git a/code/modules/clothing/clothing_vr.dm b/code/modules/clothing/clothing_vr.dm index b56e84921e0..32cfb9cffc0 100644 --- a/code/modules/clothing/clothing_vr.dm +++ b/code/modules/clothing/clothing_vr.dm @@ -11,10 +11,10 @@ /obj/item/clothing/shoes/New() inside_emotes = list( - "You feel weightless for a moment as \the [name] moves upwards.", - "\The [name] are a ride you've got no choice but to participate in as the wearer moves.", - "The wearer of \the [name] moves, pressing down on you.", - "More motion while \the [name] move, feet pressing down against you." + span_red("You feel weightless for a moment as \the [name] moves upwards."), + span_red("\The [name] are a ride you've got no choice but to participate in as the wearer moves."), + span_red("The wearer of \the [name] moves, pressing down on you."), + span_red("More motion while \the [name] move, feet pressing down against you.") ) ..() @@ -116,20 +116,20 @@ if(ishuman(src.loc)) //Is this on a person? var/mob/living/carbon/human/H = src.loc if(istype(user,/mob/living/voice)) //Is this a possessed item? Spooky. It can move on it's own! - to_chat(H, "The [src] shifts about, almost as if squirming!") - to_chat(user, "You cause the [src] to shift against [H]'s form! Well, what little you can get to, given your current state!") + to_chat(H, span_red("The [src] shifts about, almost as if squirming!")) + to_chat(user, span_red("You cause the [src] to shift against [H]'s form! Well, what little you can get to, given your current state!")) else if(H.shoes == src) - to_chat(H, "[user]'s tiny body presses against you in \the [src], squirming!") - to_chat(user, "Your body presses out against [H]'s form! Well, what little you can get to!") + to_chat(H, span_red("[user]'s tiny body presses against you in \the [src], squirming!")) + to_chat(user, span_red("Your body presses out against [H]'s form! Well, what little you can get to!")) else - to_chat(H, "[user]'s form shifts around in the \the [src], squirming!") - to_chat(user, "You move around inside the [src], to no avail.") + to_chat(H, span_red("[user]'s form shifts around in the \the [src], squirming!")) + to_chat(user, span_red("You move around inside the [src], to no avail.")) else if(istype(user,/mob/living/voice)) //Possessed! - src.visible_message("The [src] shifts about!") - to_chat(user, "You cause the [src] to shift about!") + src.visible_message(span_red("The [src] shifts about!")) + to_chat(user, span_red("You cause the [src] to shift about!")) else - src.visible_message("\The [src] moves a little!") - to_chat(user, "You throw yourself against the inside of \the [src]!") + src.visible_message(span_red("\The [src] moves a little!")) + to_chat(user, span_red("You throw yourself against the inside of \the [src]!")) //Mask /obj/item/clothing/mask diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 3b290a05f5a..e984f606cbb 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -1,626 +1,626 @@ -/////////////////////////////////////////////////////////////////////// -//Glasses -/* -SEE_SELF // can see self, no matter what -SEE_MOBS // can see all mobs, no matter what -SEE_OBJS // can see all objs, no matter what -SEE_TURFS // can see all turfs (and areas), no matter what -SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are - // in a lit area (via pixel_x,y or smooth movement), can see those pixels -BLIND // can't see anything -*/ -/////////////////////////////////////////////////////////////////////// - -/obj/item/clothing/glasses - name = "glasses" - icon = 'icons/inventory/eyes/item.dmi' - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_EYES - plane_slots = list(slot_glasses) - var/vision_flags = 0 - var/darkness_view = 0//Base human is 2 - var/see_invisible = -1 - var/prescription = 0 - var/toggleable = 0 - var/off_state = "degoggles" - var/active = 1 - var/activation_sound = 'sound/items/goggles_charge.ogg' - var/obj/screen/overlay = null - var/list/away_planes //Holder for disabled planes - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/eyes/mob_teshari.dmi', - SPECIES_VOX = 'icons/inventory/eyes/mob_vox.dmi' - ) - -/obj/item/clothing/glasses/update_clothing_icon() - if (ismob(src.loc)) - var/mob/M = src.loc - M.update_inv_glasses() - -/obj/item/clothing/glasses/proc/can_toggle(mob/living/user) - if(!toggleable) - return FALSE - - // Prevent people from just turning their goggles back on. - if(!active && (vision_flags & (SEE_TURFS|SEE_OBJS))) - var/area/A = get_area(src) - if(A.no_spoilers) - return FALSE - - return TRUE - -/obj/item/clothing/glasses/proc/toggle_active(mob/living/user) - if(active) - active = FALSE - icon_state = off_state - user.update_inv_glasses() - flash_protection = FLASH_PROTECTION_NONE - tint = TINT_NONE - away_planes = enables_planes - enables_planes = null - - else - active = TRUE - icon_state = initial(icon_state) - user.update_inv_glasses() - flash_protection = initial(flash_protection) - tint = initial(tint) - enables_planes = away_planes - away_planes = null - user.update_action_buttons() - user.recalculate_vis() - -/obj/item/clothing/glasses/attack_self(mob/user) - if(toggleable) - if(!can_toggle(user)) - to_chat(user, span("warning", "You don't seem to be able to toggle \the [src] here.")) - else - toggle_active(user) - if(active) - to_chat(user, span("notice", "You activate the optical matrix on the [src].")) - else - to_chat(user, span("notice", "You deactivate the optical matrix on the [src].")) - ..() - -/obj/item/clothing/glasses/meson - name = "optical meson scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") - action_button_name = "Toggle Goggles" - origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) - toggleable = 1 - vision_flags = SEE_TURFS - enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) - -/obj/item/clothing/glasses/meson/New() - ..() - overlay = global_hud.meson - -/obj/item/clothing/glasses/meson/prescription - name = "prescription mesons" - desc = "Optical Meson Scanner with prescription lenses." - prescription = 1 - -/obj/item/clothing/glasses/meson/aviator - name = "engineering aviators" - icon_state = "aviator_eng" - off_state = "aviator" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - action_button_name = "Toggle HUD" - activation_sound = 'sound/effects/pop.ogg' - -/obj/item/clothing/glasses/meson/aviator/prescription - name = "prescription engineering aviators" - desc = "Engineering Aviators with prescription lenses." - prescription = 1 - -/obj/item/clothing/glasses/hud/health/aviator - name = "medical HUD aviators" - desc = "Modified aviator glasses with a toggled health HUD." - icon_state = "aviator_med" - off_state = "aviator" - action_button_name = "Toggle Mode" - toggleable = 1 - activation_sound = 'sound/effects/pop.ogg' - -/obj/item/clothing/glasses/hud/health/aviator/prescription - name = "prescription medical HUD aviators" - desc = "Modified aviator glasses with a toggled health HUD. Comes with bonus prescription lenses." - prescription = 6 - -/obj/item/clothing/glasses/science - name = "Science Goggles" - desc = "The goggles do nothing!" - icon_state = "purple" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - toggleable = 1 - action_button_name = "Toggle Goggles" - item_flags = AIRTIGHT - -/obj/item/clothing/glasses/science/New() - ..() - overlay = global_hud.science - -/obj/item/clothing/glasses/goggles - name = "goggles" - desc = "Just some plain old goggles." - icon_state = "plaingoggles" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - item_flags = AIRTIGHT - body_parts_covered = EYES - -/obj/item/clothing/glasses/night - name = "night vision goggles" - desc = "You can totally see in the dark now!" - icon_state = "night" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 2) - darkness_view = 7 - toggleable = 1 - action_button_name = "Toggle Goggles" - off_state = "denight" - flash_protection = FLASH_PROTECTION_REDUCED - enables_planes = list(VIS_FULLBRIGHT) - -/obj/item/clothing/glasses/night/vox - name = "Alien Optics" - species_restricted = list("Vox") - flags = PHORONGUARD - -/obj/item/clothing/glasses/night/New() - ..() - overlay = global_hud.nvg - -/obj/item/clothing/glasses/eyepatch - name = "eyepatch" - desc = "Yarr." - icon_state = "eyepatch" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - body_parts_covered = 0 - var/eye = null - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - -/obj/item/clothing/glasses/eyepatch/verb/switcheye() - set name = "Switch Eyepatch" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - - eye = !eye - if(eye) - icon_state = "[icon_state]_1" - else - icon_state = initial(icon_state) - update_clothing_icon() - -/obj/item/clothing/glasses/eyepatchwhite - name = "eyepatch" - desc = "A simple eyepatch made of a strip of cloth tied around the head." - icon_state = "eyepatch_white" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - body_parts_covered = 0 - var/eye = null - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - -/obj/item/clothing/glasses/eyepatchwhite/verb/switcheye() - set name = "Switch Eyepatch" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - - eye = !eye - if(eye) - icon_state = "[icon_state]_1" - else - icon_state = initial(icon_state) - update_clothing_icon() - -/obj/item/clothing/glasses/monocle - name = "monocle" - desc = "Such a dapper eyepiece!" - icon_state = "monocle" - item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") - body_parts_covered = 0 - -/obj/item/clothing/glasses/material - name = "optical material scanner" - desc = "Very confusing glasses." - icon_state = "material" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3) - toggleable = 1 - action_button_name = "Toggle Goggles" - vision_flags = SEE_OBJS - enables_planes = list(VIS_FULLBRIGHT) - -/obj/item/clothing/glasses/material/New() - ..() - overlay = global_hud.material - -/obj/item/clothing/glasses/material/prescription - name = "prescription optical material scanner" - prescription = 1 - -/obj/item/clothing/glasses/graviton - name = "graviton goggles" - desc = "The secrets of space travel are.. not quite yours." - icon_state = "grav" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 2, TECH_BLUESPACE = 1) - darkness_view = 5 - toggleable = 1 - action_button_name = "Toggle Goggles" - off_state = "denight" - vision_flags = SEE_OBJS | SEE_TURFS - flash_protection = FLASH_PROTECTION_REDUCED - enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) - -/obj/item/clothing/glasses/graviton/New() - ..() - overlay = global_hud.material - -/obj/item/clothing/glasses/regular - name = "prescription glasses" - desc = "Made by Nerd. Co." - icon_state = "glasses" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - prescription = 1 - body_parts_covered = 0 - -/obj/item/clothing/glasses/regular/scanners - name = "scanning goggles" - desc = "A very oddly shaped pair of goggles with bits of wire poking out the sides. A soft humming sound emanates from it." - icon_state = "uzenwa_sissra_1" - -/obj/item/clothing/glasses/regular/hipster - name = "prescription glasses" - desc = "Made by Uncool. Co." - icon_state = "hipster_glasses" - -/obj/item/clothing/glasses/threedglasses - desc = "A long time ago, people used these glasses to makes images from screens threedimensional." - name = "3D glasses" - icon_state = "3d" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - body_parts_covered = 0 - -/obj/item/clothing/glasses/artist - name = "4-D Glasses" - desc = "You can see in every dimension, and get four times the amount of headache!" - icon_state = "artist" - item_state = "artist_glasses" - -/obj/item/clothing/glasses/gglasses - name = "green glasses" - desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." - icon_state = "gglasses" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - body_parts_covered = 0 - -/obj/item/clothing/glasses/regular/rimless - name = "prescription rimless glasses" - desc = "Sleek modern glasses with a single sculpted lens." - icon_state = "glasses_rimless" - -/obj/item/clothing/glasses/rimless - name = "rimless glasses" - desc = "Sleek modern glasses with a single sculpted lens." - icon_state = "glasses_rimless" - prescription = 0 - -/obj/item/clothing/glasses/regular/thin - name = "prescription thin-rimmed glasses" - desc = "Glasses with frames are so last century." - icon_state = "glasses_thin" - prescription = 1 - -/obj/item/clothing/glasses/thin - name = "thin-rimmed glasses" - desc = "Glasses with frames are so last century." - icon_state = "glasses_thin" - prescription = 0 - - -/obj/item/clothing/glasses/sunglasses - name = "sunglasses" - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - icon_state = "sun" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - darkness_view = -1 - flash_protection = FLASH_PROTECTION_MODERATE - -/obj/item/clothing/glasses/sunglasses/aviator - name = "aviators" - desc = "A pair of designer sunglasses." - icon_state = "aviator" - -/obj/item/clothing/glasses/sunglasses/bigshot - name = "colored glasses" - desc = "A pair of glasses with uniquely colored lenses to make you feel like a \[BIG SHOT]." - description_fluff = "A prototype model of the AR glasses which focused on stylization and \ - functionality. The concept never caught on and was replaced with the earlier rendition of \ - the modern AR glasses. These have quite clearly seen better days as the AR function no \ - longer works, the toggle merely obscuring the users vison." - icon_state = "salesman" - var/ar = 0 - -/obj/item/clothing/glasses/sunglasses/bigshot/examine(mob/user as mob) - . = ..() - . += to_chat(usr, "Alt-click to toggle modes.") - -/obj/item/clothing/glasses/sunglasses/bigshot/AltClick() - set src in usr - if(usr.canmove && !usr.stat && !usr.restrained()) - if(src.ar) - src.ar = !src.ar - icon_state = initial(icon_state) - to_chat(usr, "You press a small button on \the [src] and deactivate the AR mode.") - else - src.ar = !src.ar - icon_state = "[initial(icon_state)]_fzz" - to_chat(usr, "You press a small button on \the [src] and activate the AR mode.") - update_clothing_icon() - -/obj/item/clothing/glasses/welding - name = "welding goggles" - desc = "Protects the eyes from welders, approved by the mad scientist association." - icon_state = "welding-g" - item_state_slots = list(slot_r_hand_str = "welding-g", slot_l_hand_str = "welding-g") - action_button_name = "Flip Welding Goggles" - matter = list(MAT_STEEL = 1500, MAT_GLASS = 1000) - item_flags = AIRTIGHT - var/up = 0 - flash_protection = FLASH_PROTECTION_MAJOR - tint = TINT_HEAVY - -/obj/item/clothing/glasses/welding/attack_self() - toggle() - -/obj/item/clothing/glasses/welding/verb/toggle() - set category = "Object" - set name = "Adjust welding goggles" - set src in usr - - if(usr.canmove && !usr.stat && !usr.restrained()) - if(src.up) - src.up = !src.up - flags_inv |= HIDEEYES - body_parts_covered |= EYES - icon_state = initial(icon_state) - flash_protection = initial(flash_protection) - tint = initial(tint) - to_chat(usr, "You flip \the [src] down to protect your eyes.") - else - src.up = !src.up - flags_inv &= ~HIDEEYES - body_parts_covered &= ~EYES - icon_state = "[initial(icon_state)]up" - flash_protection = FLASH_PROTECTION_NONE - tint = TINT_NONE - to_chat(usr, "You push \the [src] up out of your face.") - update_clothing_icon() - usr.update_action_buttons() - -/obj/item/clothing/glasses/welding/superior - name = "superior welding goggles" - desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." - icon_state = "rwelding-g" - tint = TINT_MODERATE - -/obj/item/clothing/glasses/sunglasses/blindfold - name = "blindfold" - desc = "Covers the eyes, preventing sight." - icon_state = "blindfold" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - flash_protection = FLASH_PROTECTION_MAJOR - tint = BLIND - drop_sound = 'sound/items/drop/gloves.ogg' - pickup_sound = 'sound/items/pickup/gloves.ogg' - -/obj/item/clothing/glasses/sunglasses/blindfold/whiteblindfold - name = "white blindfold" - desc = "A white blindfold that covers the eyes, preventing sight." - icon_state = "blindfoldwhite" - -/obj/item/clothing/glasses/sunglasses/thinblindfold - name = "thin white blindfold" - desc = "A thin blindfold to help protect sensitive eyes while still allowing some sight" - icon_state = "blindfoldwhite" - flash_protection = FLASH_PROTECTION_MODERATE //not as thick, only offers some protection - tint = TINT_HEAVY - -/obj/item/clothing/glasses/sunglasses/blindfold/tape - name = "length of tape" - desc = "It's a robust DIY blindfold!" - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "tape_cross" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - w_class = ITEMSIZE_TINY - -/obj/item/clothing/glasses/sunglasses/prescription - name = "prescription sunglasses" - prescription = 1 - -/obj/item/clothing/glasses/sunglasses/big - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." - icon_state = "bigsunglasses" - -/obj/item/clothing/glasses/fakesunglasses //Sunglasses without flash immunity - name = "stylish sunglasses" - desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." - icon_state = "sun" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - -/obj/item/clothing/glasses/fakesunglasses/aviator - name = "stylish aviators" - desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." - icon_state = "aviator" - -/obj/item/clothing/glasses/sunglasses/sechud - name = "\improper HUD sunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunSecHud" - enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) - -/obj/item/clothing/glasses/sunglasses/sechud/tactical - name = "tactical HUD" - desc = "Flash-resistant goggles with inbuilt combat and security information." - icon_state = "swatgoggles" - -/obj/item/clothing/glasses/sunglasses/sechud/aviator - name = "security HUD aviators" - desc = "Modified aviator glasses that can be switch between HUD and flash protection modes." - icon_state = "aviator_sec" - off_state = "aviator" - action_button_name = "Toggle Mode" - var/on = 1 - toggleable = 1 - activation_sound = 'sound/effects/pop.ogg' - -/obj/item/clothing/glasses/sunglasses/sechud/aviator/attack_self(mob/user) - if(toggleable && !user.incapacitated()) - on = !on - if(on) - flash_protection = FLASH_PROTECTION_NONE - enables_planes = away_planes - away_planes = null - to_chat(usr, "You switch the [src] to HUD mode.") - else - flash_protection = initial(flash_protection) - away_planes = enables_planes - enables_planes = null - to_chat(usr, "You switch \the [src] to flash protection mode.") - update_icon() - user << activation_sound - user.recalculate_vis() - user.update_inv_glasses() - user.update_action_buttons() - -/obj/item/clothing/glasses/sunglasses/sechud/aviator/update_icon() - if(on) - icon_state = initial(icon_state) - else - icon_state = off_state - -/obj/item/clothing/glasses/sunglasses/sechud/aviator/prescription - name = "prescription security HUD aviators" - desc = "Modified aviator glasses that can be switch between HUD and flash protection modes. Comes with bonus prescription lenses." - prescription = 6 - -/obj/item/clothing/glasses/sunglasses/medhud - name = "\improper HUD sunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunMedHud" - enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) - -/obj/item/clothing/glasses/thermal - name = "optical thermal scanner" - desc = "Thermals in the shape of glasses." - icon_state = "thermal" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - origin_tech = list(TECH_MAGNET = 3) - toggleable = 1 - action_button_name = "Toggle Goggles" - vision_flags = SEE_MOBS - enables_planes = list(VIS_FULLBRIGHT, VIS_CLOAKED) - flash_protection = FLASH_PROTECTION_REDUCED - -/obj/item/clothing/glasses/thermal/emp_act(severity) - if(istype(src.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = src.loc - to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") - if(M.glasses == src) - M.Blind(3) - M.eye_blurry = 5 - // Don't cure being nearsighted - if(!(M.disabilities & NEARSIGHTED)) - M.disabilities |= NEARSIGHTED - spawn(100) - M.disabilities &= ~NEARSIGHTED - ..() - -/obj/item/clothing/glasses/thermal/New() - ..() - overlay = global_hud.thermal - -/obj/item/clothing/glasses/thermal/syndi //These are now a traitor item, concealed as mesons. -Pete - name = "optical meson scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - -/obj/item/clothing/glasses/thermal/plain - toggleable = 0 - activation_sound = null - action_button_name = null - -/obj/item/clothing/glasses/thermal/plain/monocle - name = "thermonocle" - desc = "A monocle thermal." - icon_state = "thermoncle" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - toggleable = 1 - action_button_name = "Toggle Monocle" - flags = null //doesn't protect eyes because it's a monocle, duh - - body_parts_covered = 0 - -/obj/item/clothing/glasses/thermal/plain/eyepatch - name = "optical thermal eyepatch" - desc = "An eyepatch with built-in thermal optics" - icon_state = "eyepatch" - item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - body_parts_covered = 0 - toggleable = 1 - action_button_name = "Toggle Eyepatch" - -/obj/item/clothing/glasses/thermal/plain/jensen - name = "optical thermal implants" - desc = "A set of implantable lenses designed to augment your vision" - icon_state = "thermalimplants" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - -/obj/item/clothing/glasses/aerogelgoggles - name = "orange goggles" - desc = "Teshari designed lightweight goggles." - icon_state = "orange-g" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - action_button_name = "Adjust Orange Goggles" - var/up = 0 - item_flags = AIRTIGHT - body_parts_covered = EYES - species_restricted = list(SPECIES_TESHARI) - -/obj/item/clothing/glasses/aerogelgoggles/attack_self() - toggle() - -/obj/item/clothing/glasses/aerogelgoggles/verb/toggle() - set category = "Object" - set name = "Adjust Orange Goggles" - set src in usr - - if(usr.canmove && !usr.stat && !usr.restrained()) - if(src.up) - src.up = !src.up - flags_inv |= HIDEEYES - body_parts_covered |= EYES - icon_state = initial(icon_state) - to_chat(usr, "You flip \the [src] down to protect your eyes.") - else - src.up = !src.up - flags_inv &= ~HIDEEYES - body_parts_covered &= ~EYES - icon_state = "[initial(icon_state)]up" - to_chat(usr, "You push \the [src] up from in front of your eyes.") - update_clothing_icon() - usr.update_action_buttons() - +/////////////////////////////////////////////////////////////////////// +//Glasses +/* +SEE_SELF // can see self, no matter what +SEE_MOBS // can see all mobs, no matter what +SEE_OBJS // can see all objs, no matter what +SEE_TURFS // can see all turfs (and areas), no matter what +SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are + // in a lit area (via pixel_x,y or smooth movement), can see those pixels +BLIND // can't see anything +*/ +/////////////////////////////////////////////////////////////////////// + +/obj/item/clothing/glasses + name = "glasses" + icon = 'icons/inventory/eyes/item.dmi' + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_EYES + plane_slots = list(slot_glasses) + var/vision_flags = 0 + var/darkness_view = 0//Base human is 2 + var/see_invisible = -1 + var/prescription = 0 + var/toggleable = 0 + var/off_state = "degoggles" + var/active = 1 + var/activation_sound = 'sound/items/goggles_charge.ogg' + var/obj/screen/overlay = null + var/list/away_planes //Holder for disabled planes + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/eyes/mob_teshari.dmi', + SPECIES_VOX = 'icons/inventory/eyes/mob_vox.dmi' + ) + var/glasses_layer_above = FALSE + +/obj/item/clothing/glasses/update_clothing_icon() + if (ismob(src.loc)) + var/mob/M = src.loc + M.update_inv_glasses() + +/obj/item/clothing/glasses/proc/can_toggle(mob/living/user) + if(!toggleable) + return FALSE + + // Prevent people from just turning their goggles back on. + if(!active && (vision_flags & (SEE_TURFS|SEE_OBJS))) + var/area/A = get_area(src) + if(A.no_spoilers) + return FALSE + + return TRUE + +/obj/item/clothing/glasses/proc/toggle_active(mob/living/user) + if(active) + active = FALSE + icon_state = off_state + user.update_inv_glasses() + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + away_planes = enables_planes + enables_planes = null + + else + active = TRUE + icon_state = initial(icon_state) + user.update_inv_glasses() + flash_protection = initial(flash_protection) + tint = initial(tint) + enables_planes = away_planes + away_planes = null + user.update_action_buttons() + user.recalculate_vis() + +/obj/item/clothing/glasses/attack_self(mob/user) + if(toggleable) + if(!can_toggle(user)) + to_chat(user, span("warning", "You don't seem to be able to toggle \the [src] here.")) + else + toggle_active(user) + if(active) + to_chat(user, span("notice", "You activate the optical matrix on the [src].")) + else + to_chat(user, span("notice", "You deactivate the optical matrix on the [src].")) + ..() + +/obj/item/clothing/glasses/meson + name = "optical meson scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") + action_button_name = "Toggle Goggles" + origin_tech = list(TECH_MAGNET = 2, TECH_ENGINEERING = 2) + toggleable = 1 + vision_flags = SEE_TURFS + enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) + +/obj/item/clothing/glasses/meson/New() + ..() + overlay = global_hud.meson + +/obj/item/clothing/glasses/meson/prescription + name = "prescription mesons" + desc = "Optical Meson Scanner with prescription lenses." + prescription = 1 + +/obj/item/clothing/glasses/meson/aviator + name = "engineering aviators" + icon_state = "aviator_eng" + off_state = "aviator" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + action_button_name = "Toggle HUD" + activation_sound = 'sound/effects/pop.ogg' + +/obj/item/clothing/glasses/meson/aviator/prescription + name = "prescription engineering aviators" + desc = "Engineering Aviators with prescription lenses." + prescription = 1 + +/obj/item/clothing/glasses/hud/health/aviator + name = "medical HUD aviators" + desc = "Modified aviator glasses with a toggled health HUD." + icon_state = "aviator_med" + off_state = "aviator" + action_button_name = "Toggle Mode" + toggleable = 1 + activation_sound = 'sound/effects/pop.ogg' + +/obj/item/clothing/glasses/hud/health/aviator/prescription + name = "prescription medical HUD aviators" + desc = "Modified aviator glasses with a toggled health HUD. Comes with bonus prescription lenses." + prescription = 6 + +/obj/item/clothing/glasses/science + name = "Science Goggles" + desc = "The goggles do nothing!" + icon_state = "purple" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + toggleable = 1 + action_button_name = "Toggle Goggles" + item_flags = AIRTIGHT + +/obj/item/clothing/glasses/science/New() + ..() + overlay = global_hud.science + +/obj/item/clothing/glasses/goggles + name = "goggles" + desc = "Just some plain old goggles." + icon_state = "plaingoggles" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + item_flags = AIRTIGHT + body_parts_covered = EYES + +/obj/item/clothing/glasses/night + name = "night vision goggles" + desc = "You can totally see in the dark now!" + icon_state = "night" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 2) + darkness_view = 7 + toggleable = 1 + action_button_name = "Toggle Goggles" + off_state = "denight" + flash_protection = FLASH_PROTECTION_REDUCED + enables_planes = list(VIS_FULLBRIGHT) + +/obj/item/clothing/glasses/night/vox + name = "Alien Optics" + species_restricted = list("Vox") + flags = PHORONGUARD + +/obj/item/clothing/glasses/night/New() + ..() + overlay = global_hud.nvg + +/obj/item/clothing/glasses/eyepatch + name = "eyepatch" + desc = "Yarr." + icon_state = "eyepatch" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + body_parts_covered = 0 + var/eye = null + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + +/obj/item/clothing/glasses/eyepatch/verb/switcheye() + set name = "Switch Eyepatch" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + eye = !eye + if(eye) + icon_state = "[icon_state]_1" + else + icon_state = initial(icon_state) + update_clothing_icon() + +/obj/item/clothing/glasses/eyepatchwhite + name = "eyepatch" + desc = "A simple eyepatch made of a strip of cloth tied around the head." + icon_state = "eyepatch_white" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + body_parts_covered = 0 + var/eye = null + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + +/obj/item/clothing/glasses/eyepatchwhite/verb/switcheye() + set name = "Switch Eyepatch" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + eye = !eye + if(eye) + icon_state = "[icon_state]_1" + else + icon_state = initial(icon_state) + update_clothing_icon() + +/obj/item/clothing/glasses/monocle + name = "monocle" + desc = "Such a dapper eyepiece!" + icon_state = "monocle" + item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") + body_parts_covered = 0 + +/obj/item/clothing/glasses/material + name = "optical material scanner" + desc = "Very confusing glasses." + icon_state = "material" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3) + toggleable = 1 + action_button_name = "Toggle Goggles" + vision_flags = SEE_OBJS + enables_planes = list(VIS_FULLBRIGHT) + +/obj/item/clothing/glasses/material/New() + ..() + overlay = global_hud.material + +/obj/item/clothing/glasses/material/prescription + name = "prescription optical material scanner" + prescription = 1 + +/obj/item/clothing/glasses/graviton + name = "graviton goggles" + desc = "The secrets of space travel are.. not quite yours." + icon_state = "grav" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 2, TECH_BLUESPACE = 1) + darkness_view = 5 + toggleable = 1 + action_button_name = "Toggle Goggles" + off_state = "denight" + vision_flags = SEE_OBJS | SEE_TURFS + flash_protection = FLASH_PROTECTION_REDUCED + enables_planes = list(VIS_FULLBRIGHT, VIS_MESONS) + +/obj/item/clothing/glasses/graviton/New() + ..() + overlay = global_hud.material + +/obj/item/clothing/glasses/regular + name = "prescription glasses" + desc = "Made by Nerd. Co." + icon_state = "glasses" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + prescription = 1 + body_parts_covered = 0 + +/obj/item/clothing/glasses/regular/scanners + name = "scanning goggles" + desc = "A very oddly shaped pair of goggles with bits of wire poking out the sides. A soft humming sound emanates from it." + icon_state = "uzenwa_sissra_1" + +/obj/item/clothing/glasses/regular/hipster + name = "prescription glasses" + desc = "Made by Uncool. Co." + icon_state = "hipster_glasses" + +/obj/item/clothing/glasses/threedglasses + desc = "A long time ago, people used these glasses to makes images from screens threedimensional." + name = "3D glasses" + icon_state = "3d" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + body_parts_covered = 0 + +/obj/item/clothing/glasses/artist + name = "4-D Glasses" + desc = "You can see in every dimension, and get four times the amount of headache!" + icon_state = "artist" + item_state = "artist_glasses" + +/obj/item/clothing/glasses/gglasses + name = "green glasses" + desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." + icon_state = "gglasses" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + body_parts_covered = 0 + +/obj/item/clothing/glasses/regular/rimless + name = "prescription rimless glasses" + desc = "Sleek modern glasses with a single sculpted lens." + icon_state = "glasses_rimless" + +/obj/item/clothing/glasses/rimless + name = "rimless glasses" + desc = "Sleek modern glasses with a single sculpted lens." + icon_state = "glasses_rimless" + prescription = 0 + +/obj/item/clothing/glasses/regular/thin + name = "prescription thin-rimmed glasses" + desc = "Glasses with frames are so last century." + icon_state = "glasses_thin" + prescription = 1 + +/obj/item/clothing/glasses/thin + name = "thin-rimmed glasses" + desc = "Glasses with frames are so last century." + icon_state = "glasses_thin" + prescription = 0 + + +/obj/item/clothing/glasses/sunglasses + name = "sunglasses" + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + icon_state = "sun" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + darkness_view = -1 + flash_protection = FLASH_PROTECTION_MODERATE + +/obj/item/clothing/glasses/sunglasses/aviator + name = "aviators" + desc = "A pair of designer sunglasses." + icon_state = "aviator" + +/obj/item/clothing/glasses/sunglasses/bigshot + name = "colored glasses" + desc = "A pair of glasses with uniquely colored lenses to make you feel like a \[BIG SHOT]." + description_fluff = "A prototype model of the AR glasses which focused on stylization and \ + functionality. The concept never caught on and was replaced with the earlier rendition of \ + the modern AR glasses. These have quite clearly seen better days as the AR function no \ + longer works, the toggle merely obscuring the users vison." + icon_state = "salesman" + var/ar = 0 + +/obj/item/clothing/glasses/sunglasses/bigshot/examine(mob/user as mob) + . = ..() + . += to_chat(usr, "Alt-click to toggle modes.") + +/obj/item/clothing/glasses/sunglasses/bigshot/AltClick() + set src in usr + if(usr.canmove && !usr.stat && !usr.restrained()) + if(src.ar) + src.ar = !src.ar + icon_state = initial(icon_state) + to_chat(usr, "You press a small button on \the [src] and deactivate the AR mode.") + else + src.ar = !src.ar + icon_state = "[initial(icon_state)]_fzz" + to_chat(usr, "You press a small button on \the [src] and activate the AR mode.") + update_clothing_icon() + +/obj/item/clothing/glasses/welding + name = "welding goggles" + desc = "Protects the eyes from welders, approved by the mad scientist association." + icon_state = "welding-g" + item_state_slots = list(slot_r_hand_str = "welding-g", slot_l_hand_str = "welding-g") + action_button_name = "Flip Welding Goggles" + matter = list(MAT_STEEL = 1500, MAT_GLASS = 1000) + item_flags = AIRTIGHT + var/up = 0 + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY + +/obj/item/clothing/glasses/welding/attack_self() + toggle() + +/obj/item/clothing/glasses/welding/verb/toggle() + set category = "Object" + set name = "Adjust welding goggles" + set src in usr + + if(usr.canmove && !usr.stat && !usr.restrained()) + if(src.up) + src.up = !src.up + flags_inv |= HIDEEYES + body_parts_covered |= EYES + icon_state = initial(icon_state) + flash_protection = initial(flash_protection) + tint = initial(tint) + to_chat(usr, "You flip \the [src] down to protect your eyes.") + else + src.up = !src.up + flags_inv &= ~HIDEEYES + body_parts_covered &= ~EYES + icon_state = "[initial(icon_state)]up" + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You push \the [src] up out of your face.") + update_clothing_icon() + usr.update_action_buttons() + +/obj/item/clothing/glasses/welding/superior + name = "superior welding goggles" + desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." + icon_state = "rwelding-g" + tint = TINT_MODERATE + +/obj/item/clothing/glasses/sunglasses/blindfold + name = "blindfold" + desc = "Covers the eyes, preventing sight." + icon_state = "blindfold" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + flash_protection = FLASH_PROTECTION_MAJOR + tint = BLIND + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + +/obj/item/clothing/glasses/sunglasses/blindfold/whiteblindfold + name = "white blindfold" + desc = "A white blindfold that covers the eyes, preventing sight." + icon_state = "blindfoldwhite" + +/obj/item/clothing/glasses/sunglasses/thinblindfold + name = "thin white blindfold" + desc = "A thin blindfold to help protect sensitive eyes while still allowing some sight" + icon_state = "blindfoldwhite" + flash_protection = FLASH_PROTECTION_MODERATE //not as thick, only offers some protection + tint = TINT_HEAVY + +/obj/item/clothing/glasses/sunglasses/blindfold/tape + name = "length of tape" + desc = "It's a robust DIY blindfold!" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "tape_cross" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + w_class = ITEMSIZE_TINY + +/obj/item/clothing/glasses/sunglasses/prescription + name = "prescription sunglasses" + prescription = 1 + +/obj/item/clothing/glasses/sunglasses/big + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." + icon_state = "bigsunglasses" + +/obj/item/clothing/glasses/fakesunglasses //Sunglasses without flash immunity + name = "stylish sunglasses" + desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." + icon_state = "sun" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + +/obj/item/clothing/glasses/fakesunglasses/aviator + name = "stylish aviators" + desc = "A pair of designer sunglasses. Doesn't seem like it'll block flashes." + icon_state = "aviator" + +/obj/item/clothing/glasses/sunglasses/sechud + name = "\improper HUD sunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunSecHud" + enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) + +/obj/item/clothing/glasses/sunglasses/sechud/tactical + name = "tactical HUD" + desc = "Flash-resistant goggles with inbuilt combat and security information." + icon_state = "swatgoggles" + +/obj/item/clothing/glasses/sunglasses/sechud/aviator + name = "security HUD aviators" + desc = "Modified aviator glasses that can be switch between HUD and flash protection modes." + icon_state = "aviator_sec" + off_state = "aviator" + action_button_name = "Toggle Mode" + var/on = 1 + toggleable = 1 + activation_sound = 'sound/effects/pop.ogg' + +/obj/item/clothing/glasses/sunglasses/sechud/aviator/attack_self(mob/user) + if(toggleable && !user.incapacitated()) + on = !on + if(on) + flash_protection = FLASH_PROTECTION_NONE + enables_planes = away_planes + away_planes = null + to_chat(usr, "You switch the [src] to HUD mode.") + else + flash_protection = initial(flash_protection) + away_planes = enables_planes + enables_planes = null + to_chat(usr, "You switch \the [src] to flash protection mode.") + update_icon() + user << activation_sound + user.recalculate_vis() + user.update_inv_glasses() + user.update_action_buttons() + +/obj/item/clothing/glasses/sunglasses/sechud/aviator/update_icon() + if(on) + icon_state = initial(icon_state) + else + icon_state = off_state + +/obj/item/clothing/glasses/sunglasses/sechud/aviator/prescription + name = "prescription security HUD aviators" + desc = "Modified aviator glasses that can be switch between HUD and flash protection modes. Comes with bonus prescription lenses." + prescription = 6 + +/obj/item/clothing/glasses/sunglasses/medhud + name = "\improper HUD sunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunMedHud" + enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) + +/obj/item/clothing/glasses/thermal + name = "optical thermal scanner" + desc = "Thermals in the shape of glasses." + icon_state = "thermal" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + origin_tech = list(TECH_MAGNET = 3) + toggleable = 1 + action_button_name = "Toggle Goggles" + vision_flags = SEE_MOBS + enables_planes = list(VIS_FULLBRIGHT, VIS_CLOAKED) + flash_protection = FLASH_PROTECTION_REDUCED + +/obj/item/clothing/glasses/thermal/emp_act(severity) + if(istype(src.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = src.loc + to_chat(M, span_red("The Optical Thermal Scanner overloads and blinds you!")) + if(M.glasses == src) + M.Blind(3) + M.eye_blurry = 5 + // Don't cure being nearsighted + if(!(M.disabilities & NEARSIGHTED)) + M.disabilities |= NEARSIGHTED + spawn(100) + M.disabilities &= ~NEARSIGHTED + ..() + +/obj/item/clothing/glasses/thermal/New() + ..() + overlay = global_hud.thermal + +/obj/item/clothing/glasses/thermal/syndi //These are now a traitor item, concealed as mesons. -Pete + name = "optical meson scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state_slots = list(slot_r_hand_str = "meson", slot_l_hand_str = "meson") + origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) + +/obj/item/clothing/glasses/thermal/plain + toggleable = 0 + activation_sound = null + action_button_name = null + +/obj/item/clothing/glasses/thermal/plain/monocle + name = "thermonocle" + desc = "A monocle thermal." + icon_state = "thermoncle" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + toggleable = 1 + action_button_name = "Toggle Monocle" + flags = null //doesn't protect eyes because it's a monocle, duh + + body_parts_covered = 0 + +/obj/item/clothing/glasses/thermal/plain/eyepatch + name = "optical thermal eyepatch" + desc = "An eyepatch with built-in thermal optics" + icon_state = "eyepatch" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + body_parts_covered = 0 + toggleable = 1 + action_button_name = "Toggle Eyepatch" + +/obj/item/clothing/glasses/thermal/plain/jensen + name = "optical thermal implants" + desc = "A set of implantable lenses designed to augment your vision" + icon_state = "thermalimplants" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + +/obj/item/clothing/glasses/aerogelgoggles + name = "orange goggles" + desc = "Teshari designed lightweight goggles." + icon_state = "orange-g" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + action_button_name = "Adjust Orange Goggles" + var/up = 0 + item_flags = AIRTIGHT + body_parts_covered = EYES + species_restricted = list(SPECIES_TESHARI) + +/obj/item/clothing/glasses/aerogelgoggles/attack_self() + toggle() + +/obj/item/clothing/glasses/aerogelgoggles/verb/toggle() + set category = "Object" + set name = "Adjust Orange Goggles" + set src in usr + + if(usr.canmove && !usr.stat && !usr.restrained()) + if(src.up) + src.up = !src.up + flags_inv |= HIDEEYES + body_parts_covered |= EYES + icon_state = initial(icon_state) + to_chat(usr, "You flip \the [src] down to protect your eyes.") + else + src.up = !src.up + flags_inv &= ~HIDEEYES + body_parts_covered &= ~EYES + icon_state = "[initial(icon_state)]up" + to_chat(usr, "You push \the [src] up from in front of your eyes.") + update_clothing_icon() + usr.update_action_buttons() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index fb4f491076b..e0d3177b529 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -1,43 +1,43 @@ -/obj/item/clothing/glasses/hud - name = "HUD" - desc = "A heads-up display that provides important info in (almost) real time." - flags = 0 //doesn't protect eyes because it's a monocle, duh - origin_tech = list(TECH_MAGNET = 3, TECH_BIO = 2) - -/obj/item/clothing/glasses/hud/health - name = "Health Scanner HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." - icon_state = "healthhud" - item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") - body_parts_covered = 0 - enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) - -/obj/item/clothing/glasses/hud/health/prescription - name = "Prescription Health Scanner HUD" - desc = "A medical HUD integrated with a set of prescription glasses" - prescription = 1 - icon_state = "healthhudpresc" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - -/obj/item/clothing/glasses/hud/security - name = "Security HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." - icon_state = "securityhud" - item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") - body_parts_covered = 0 - enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) - -/obj/item/clothing/glasses/hud/security/prescription - name = "Prescription Security HUD" - desc = "A security HUD integrated with a set of prescription glasses" - prescription = 1 - icon_state = "sechudpresc" - item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") - -/obj/item/clothing/glasses/hud/security/jensenshades - name = "Augmented shades" - desc = "Polarized bioneural eyewear, designed to augment your vision." - icon_state = "jensenshades" - item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") - vision_flags = SEE_MOBS - see_invisible = SEE_INVISIBLE_NOLIGHTING +/obj/item/clothing/glasses/hud + name = "HUD" + desc = "A heads-up display that provides important info in (almost) real time." + flags = 0 //doesn't protect eyes because it's a monocle, duh + origin_tech = list(TECH_MAGNET = 3, TECH_BIO = 2) + +/obj/item/clothing/glasses/hud/health + name = "Health Scanner HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." + icon_state = "healthhud" + item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") + body_parts_covered = 0 + enables_planes = list(VIS_CH_STATUS,VIS_CH_HEALTH) + +/obj/item/clothing/glasses/hud/health/prescription + name = "Prescription Health Scanner HUD" + desc = "A medical HUD integrated with a set of prescription glasses" + prescription = 1 + icon_state = "healthhudpresc" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + +/obj/item/clothing/glasses/hud/security + name = "Security HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." + icon_state = "securityhud" + item_state_slots = list(slot_r_hand_str = "headset", slot_l_hand_str = "headset") + body_parts_covered = 0 + enables_planes = list(VIS_CH_ID,VIS_CH_WANTED,VIS_CH_IMPTRACK,VIS_CH_IMPLOYAL,VIS_CH_IMPCHEM) + +/obj/item/clothing/glasses/hud/security/prescription + name = "Prescription Security HUD" + desc = "A security HUD integrated with a set of prescription glasses" + prescription = 1 + icon_state = "sechudpresc" + item_state_slots = list(slot_r_hand_str = "glasses", slot_l_hand_str = "glasses") + +/obj/item/clothing/glasses/hud/security/jensenshades + name = "Augmented shades" + desc = "Polarized bioneural eyewear, designed to augment your vision." + icon_state = "jensenshades" + item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") + vision_flags = SEE_MOBS + see_invisible = SEE_INVISIBLE_NOLIGHTING diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index 9316c8f64f7..b818be2dba1 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -1,31 +1,31 @@ -/obj/item/clothing/gloves/boxing - name = "boxing gloves" - desc = "Because you really needed another excuse to punch your crewmates." - icon_state = "boxing" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/* -/obj/item/clothing/gloves/boxing/attackby(obj/item/weapon/W, mob/user) - if(W.is_wirecutter() || istype(W, /obj/item/weapon/surgical/scalpel)) - to_chat(user, "That won't work.") //Nope - return - ..() -*/ - -/obj/item/clothing/gloves/boxing/green - icon_state = "boxinggreen" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - -/obj/item/clothing/gloves/boxing/blue - icon_state = "boxingblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/gloves/boxing/yellow - icon_state = "boxingyellow" - item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") - -/obj/item/clothing/gloves/white - name = "white gloves" - desc = "These look pretty fancy." - icon_state = "latex" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") +/obj/item/clothing/gloves/boxing + name = "boxing gloves" + desc = "Because you really needed another excuse to punch your crewmates." + icon_state = "boxing" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/* +/obj/item/clothing/gloves/boxing/attackby(obj/item/weapon/W, mob/user) + if(W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/weapon/surgical/scalpel)) + to_chat(user, "That won't work.") //Nope + return + ..() +*/ + +/obj/item/clothing/gloves/boxing/green + icon_state = "boxinggreen" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + +/obj/item/clothing/gloves/boxing/blue + icon_state = "boxingblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/gloves/boxing/yellow + icon_state = "boxingyellow" + item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") + +/obj/item/clothing/gloves/white + name = "white gloves" + desc = "These look pretty fancy." + icon_state = "latex" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index efcd364922e..19ff4c8b10f 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,99 +1,99 @@ - - -/obj/item/clothing/gloves/yellow - desc = "These gloves will protect the wearer from electric shock." - name = "insulated gloves" - icon_state = "yellow" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/gloves/fyellow //Cheap Chinese Crap - desc = "These gloves are cheap copies of proper insulated gloves. No way this can end badly." - name = "budget insulated gloves" - icon_state = "yellow" - siemens_coefficient = 1 //Set to a default of 1, gets overridden in initialize() - permeability_coefficient = 0.05 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/gloves/fyellow/Initialize() - . = ..() - //Picks a value between 0 and 1.25, in 5% increments // VOREStation edit - var/shock_pick = rand(0,15) // VOREStation Edit - siemens_coefficient = shock_pick * 0.05 - -/obj/item/clothing/gloves/black - desc = "These work gloves are thick and fire-resistant." - name = "black gloves" - icon_state = "black" - permeability_coefficient = 0.05 - - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/orange - name = "orange gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "orange" - -/obj/item/clothing/gloves/red - name = "red gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "red" - -/obj/item/clothing/gloves/rainbow - name = "rainbow gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "rainbow" - -/obj/item/clothing/gloves/blue - name = "blue gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "blue" - -/obj/item/clothing/gloves/purple - name = "purple gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "purple" - -/obj/item/clothing/gloves/green - name = "green gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "green" - -/obj/item/clothing/gloves/grey - name = "grey gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "gray" - -/obj/item/clothing/gloves/light_brown - name = "light brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "lightbrown" - -/obj/item/clothing/gloves/brown - name = "brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "brown" - -/obj/item/clothing/gloves/evening - desc = "A pair of gloves that reach past the elbow. Fancy!" - name = "evening gloves" - icon_state = "evening_gloves" - addblends = "evening_gloves_a" - -/obj/item/clothing/gloves/fingerless - desc = "A pair of gloves that don't actually cover the fingers." - name = "fingerless gloves" - icon_state = "fingerlessgloves" - fingerprint_chance = 100 - -/obj/item/clothing/gloves/fingerless_recolourable - desc = "A pair of gloves that don't actually cover the fingers." - name = "fingerless gloves" - icon_state = "fingerlessgloves_rc" + + +/obj/item/clothing/gloves/yellow + desc = "These gloves will protect the wearer from electric shock." + name = "insulated gloves" + icon_state = "yellow" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/gloves/fyellow //Cheap Chinese Crap + desc = "These gloves are cheap copies of proper insulated gloves. No way this can end badly." + name = "budget insulated gloves" + icon_state = "yellow" + siemens_coefficient = 1 //Set to a default of 1, gets overridden in initialize() + permeability_coefficient = 0.05 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/gloves/fyellow/Initialize() + . = ..() + //Picks a value between 0 and 1.25, in 5% increments // VOREStation edit + var/shock_pick = rand(0,15) // VOREStation Edit + siemens_coefficient = shock_pick * 0.05 + +/obj/item/clothing/gloves/black + desc = "These work gloves are thick and fire-resistant." + name = "black gloves" + icon_state = "black" + permeability_coefficient = 0.05 + + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/orange + name = "orange gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "orange" + +/obj/item/clothing/gloves/red + name = "red gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "red" + +/obj/item/clothing/gloves/rainbow + name = "rainbow gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "rainbow" + +/obj/item/clothing/gloves/blue + name = "blue gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "blue" + +/obj/item/clothing/gloves/purple + name = "purple gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "purple" + +/obj/item/clothing/gloves/green + name = "green gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "green" + +/obj/item/clothing/gloves/grey + name = "grey gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "gray" + +/obj/item/clothing/gloves/light_brown + name = "light brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "lightbrown" + +/obj/item/clothing/gloves/brown + name = "brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "brown" + +/obj/item/clothing/gloves/evening + desc = "A pair of gloves that reach past the elbow. Fancy!" + name = "evening gloves" + icon_state = "evening_gloves" + addblends = "evening_gloves_a" + +/obj/item/clothing/gloves/fingerless + desc = "A pair of gloves that don't actually cover the fingers." + name = "fingerless gloves" + icon_state = "fingerlessgloves" + fingerprint_chance = 100 + +/obj/item/clothing/gloves/fingerless_recolourable + desc = "A pair of gloves that don't actually cover the fingers." + name = "fingerless gloves" + icon_state = "fingerlessgloves_rc" fingerprint_chance = 100 \ No newline at end of file diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 709c6ea1560..eba124cc621 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -1,174 +1,174 @@ -/obj/item/clothing/gloves/captain - desc = "Regal blue gloves, with a nice gold trim. Swanky." - name = "site manager's gloves" - icon_state = "captain" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/gloves/cyborg - desc = "beep boop borp" - name = "cyborg gloves" - icon_state = "black" - item_state = "r_hands" - siemens_coefficient = 1.0 - -/obj/item/clothing/gloves/forensic - desc = "Specially made gloves for forensic technicians. The luminescent threads woven into the material stand out under scrutiny." - name = "forensic gloves" - icon_state = "forensic" - item_state = "black" - permeability_coefficient = 0.05 - - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/swat - desc = "These tactical gloves are somewhat fire and impact-resistant." - name = "\improper SWAT Gloves" - icon_state = "swat" - item_state = "swat" - siemens_coefficient = 0.50 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/combat //Combined effect of SWAT gloves and insulated gloves - desc = "These tactical gloves are somewhat fire and impact resistant." - name = "combat gloves" - icon_state = "swat" - item_state = "swat" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/sterile - name = "sterile gloves" - desc = "Sterile gloves." - icon_state = "latex" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - siemens_coefficient = 1.0 //thin latex gloves, much more conductive than fabric gloves (basically a capacitor for AC) - permeability_coefficient = 0.01 - germ_level = 0 - fingerprint_chance = 25 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' -// var/balloonPath = /obj/item/latexballon - -//TODO: Make inflating gloves a thing -/*/obj/item/clothing/gloves/sterile/proc/Inflate(/mob/living/carbon/human/user) - user.visible_message("\The [src] expands!") - qdel(src)*/ - -/obj/item/clothing/gloves/sterile/latex - name = "latex gloves" - desc = "Sterile latex gloves." - -/obj/item/clothing/gloves/sterile/nitrile - name = "nitrile gloves" - desc = "Sterile nitrile gloves" - icon_state = "nitrile" - item_state = "ngloves" -// balloonPath = /obj/item/nitrileballoon - -/obj/item/clothing/gloves/botanic_leather - desc = "These leather work gloves protect against thorns, barbs, prickles, spikes and other harmful objects of floral origin." - name = "botanist's leather gloves" - icon_state = "leather" - item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") - permeability_coefficient = 0.05 - siemens_coefficient = 0.75 //thick work gloves - drop_sound = 'sound/items/drop/leather.ogg' - pickup_sound = 'sound/items/pickup/leather.ogg' - -/obj/item/clothing/gloves/duty - desc = "These brown duty gloves are made from a durable synthetic." - name = "work gloves" - icon_state = "work" - item_state = "wgloves" - armor = list(melee = 10, bullet = 10, laser = 10, energy = 5, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/gloves/tactical - desc = "These brown tactical gloves are made from a durable synthetic, and have hardened knuckles." - name = "tactical gloves" - icon_state = "work" - item_state = "wgloves" - force = 5 - punch_force = 3 - siemens_coefficient = 0.75 - permeability_coefficient = 0.05 - armor = list(melee = 30, bullet = 10, laser = 10, energy = 15, bomb = 20, bio = 0, rad = 0) - -/obj/item/clothing/gloves/vox - desc = "These bizarre gauntlets seem to be fitted for... bird claws?" - name = "insulated gauntlets" - icon_state = "gloves-vox" - item_state = "gloves-vox" - flags = PHORONGUARD - siemens_coefficient = 0 - permeability_coefficient = 0.05 - species_restricted = list("Vox") - drop_sound = 'sound/items/drop/metalboots.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE - -/obj/item/clothing/gloves/knuckledusters - name = "knuckle dusters" - desc = "A pair of brass knuckles. Generally used to enhance the user's punches." - icon_state = "knuckledusters" - matter = list(MAT_STEEL = 500) - attack_verb = list("punched", "beaten", "struck") - flags = THICKMATERIAL // Stops rings from increasing hit strength - siemens_coefficient = 1 - fingerprint_chance = 100 - overgloves = 1 - force = 5 - punch_force = 5 - drop_sound = 'sound/items/drop/metalboots.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - -/obj/item/clothing/gloves/ranger - var/glovecolor = "white" - name = "ranger gloves" - desc = "The gloves of the Rangers are the least memorable part. They're not even insulated in the show, so children \ - don't try and take apart a toaster with inadequate protection. They only serve to complete the fancy outfit." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "ranger_gloves" - -/obj/item/clothing/gloves/ranger/Initialize() - . = ..() - if(icon_state == "ranger_gloves") - name = "[glovecolor] ranger gloves" - icon_state = "[glovecolor]_ranger_gloves" - -/obj/item/clothing/gloves/ranger/black - glovecolor = "black" - -/obj/item/clothing/gloves/ranger/pink - glovecolor = "pink" - -/obj/item/clothing/gloves/ranger/green - glovecolor = "green" - -/obj/item/clothing/gloves/ranger/cyan - glovecolor = "cyan" - -/obj/item/clothing/gloves/ranger/orange - glovecolor = "orange" - -/obj/item/clothing/gloves/ranger/yellow - glovecolor = "yellow" - -/obj/item/clothing/gloves/waterwings - name = "water wings" - desc = "Swim aids designed to help a wearer float in water and learn to swim." - icon_state = "waterwings" +/obj/item/clothing/gloves/captain + desc = "Regal blue gloves, with a nice gold trim. Swanky." + name = "site manager's gloves" + icon_state = "captain" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/gloves/cyborg + desc = "beep boop borp" + name = "cyborg gloves" + icon_state = "black" + item_state = "r_hands" + siemens_coefficient = 1.0 + +/obj/item/clothing/gloves/forensic + desc = "Specially made gloves for forensic technicians. The luminescent threads woven into the material stand out under scrutiny." + name = "forensic gloves" + icon_state = "forensic" + item_state = "black" + permeability_coefficient = 0.05 + + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/swat + desc = "These tactical gloves are somewhat fire and impact-resistant." + name = "\improper SWAT Gloves" + icon_state = "swat" + item_state = "swat" + siemens_coefficient = 0.50 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/combat //Combined effect of SWAT gloves and insulated gloves + desc = "These tactical gloves are somewhat fire and impact resistant." + name = "combat gloves" + icon_state = "swat" + item_state = "swat" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/sterile + name = "sterile gloves" + desc = "Sterile gloves." + icon_state = "latex" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + siemens_coefficient = 1.0 //thin latex gloves, much more conductive than fabric gloves (basically a capacitor for AC) + permeability_coefficient = 0.01 + germ_level = 0 + fingerprint_chance = 25 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' +// var/balloonPath = /obj/item/latexballon + +//TODO: Make inflating gloves a thing +/*/obj/item/clothing/gloves/sterile/proc/Inflate(/mob/living/carbon/human/user) + user.visible_message("\The [src] expands!") + qdel(src)*/ + +/obj/item/clothing/gloves/sterile/latex + name = "latex gloves" + desc = "Sterile latex gloves." + +/obj/item/clothing/gloves/sterile/nitrile + name = "nitrile gloves" + desc = "Sterile nitrile gloves" + icon_state = "nitrile" + item_state = "ngloves" +// balloonPath = /obj/item/nitrileballoon + +/obj/item/clothing/gloves/botanic_leather + desc = "These leather work gloves protect against thorns, barbs, prickles, spikes and other harmful objects of floral origin." + name = "botanist's leather gloves" + icon_state = "leather" + item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") + permeability_coefficient = 0.05 + siemens_coefficient = 0.75 //thick work gloves + drop_sound = 'sound/items/drop/leather.ogg' + pickup_sound = 'sound/items/pickup/leather.ogg' + +/obj/item/clothing/gloves/duty + desc = "These brown duty gloves are made from a durable synthetic." + name = "work gloves" + icon_state = "work" + item_state = "wgloves" + armor = list(melee = 10, bullet = 10, laser = 10, energy = 5, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/gloves/tactical + desc = "These brown tactical gloves are made from a durable synthetic, and have hardened knuckles." + name = "tactical gloves" + icon_state = "work" + item_state = "wgloves" + force = 5 + punch_force = 3 + siemens_coefficient = 0.75 + permeability_coefficient = 0.05 + armor = list(melee = 30, bullet = 10, laser = 10, energy = 15, bomb = 20, bio = 0, rad = 0) + +/obj/item/clothing/gloves/vox + desc = "These bizarre gauntlets seem to be fitted for... bird claws?" + name = "insulated gauntlets" + icon_state = "gloves-vox" + item_state = "gloves-vox" + flags = PHORONGUARD + siemens_coefficient = 0 + permeability_coefficient = 0.05 + species_restricted = list("Vox") + drop_sound = 'sound/items/drop/metalboots.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_HEAT_PROTECTION_TEMPERATURE + +/obj/item/clothing/gloves/knuckledusters + name = "knuckle dusters" + desc = "A pair of brass knuckles. Generally used to enhance the user's punches." + icon_state = "knuckledusters" + matter = list(MAT_STEEL = 500) + attack_verb = list("punched", "beaten", "struck") + flags = THICKMATERIAL // Stops rings from increasing hit strength + siemens_coefficient = 1 + fingerprint_chance = 100 + overgloves = 1 + force = 5 + punch_force = 5 + drop_sound = 'sound/items/drop/metalboots.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + +/obj/item/clothing/gloves/ranger + var/glovecolor = "white" + name = "ranger gloves" + desc = "The gloves of the Rangers are the least memorable part. They're not even insulated in the show, so children \ + don't try and take apart a toaster with inadequate protection. They only serve to complete the fancy outfit." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_gloves" + +/obj/item/clothing/gloves/ranger/Initialize() + . = ..() + if(icon_state == "ranger_gloves") + name = "[glovecolor] ranger gloves" + icon_state = "[glovecolor]_ranger_gloves" + +/obj/item/clothing/gloves/ranger/black + glovecolor = "black" + +/obj/item/clothing/gloves/ranger/pink + glovecolor = "pink" + +/obj/item/clothing/gloves/ranger/green + glovecolor = "green" + +/obj/item/clothing/gloves/ranger/cyan + glovecolor = "cyan" + +/obj/item/clothing/gloves/ranger/orange + glovecolor = "orange" + +/obj/item/clothing/gloves/ranger/yellow + glovecolor = "yellow" + +/obj/item/clothing/gloves/waterwings + name = "water wings" + desc = "Swim aids designed to help a wearer float in water and learn to swim." + icon_state = "waterwings" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index 5d33d56360a..eea71f60417 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -1,128 +1,128 @@ - -//Hat Station 13 - -/obj/item/clothing/head/collectable - name = "collectable hat" - desc = "A rare collectable hat." - -/obj/item/clothing/head/collectable/petehat - name = "ultra rare hat" - desc = "an ultra rare hat. It commands a certain respect." - icon_state = "petehat" - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/head/mob_teshari.dmi', - SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi' - ) - -/obj/item/clothing/head/collectable/slime - name = "collectable slime cap!" - desc = "It just latches right in place!" - icon_state = "headslime" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/xenom - name = "collectable xenomorph helmet!" - desc = "Hiss hiss hiss!" - icon_state = "xenom" - item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/collectable/chef - name = "collectable chef's hat" - desc = "A rare Chef's Hat meant for hat collectors!" - icon_state = "chefhat" - -/obj/item/clothing/head/collectable/paper - name = "collectable paper hat" - desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." - icon_state = "paper" - body_parts_covered = 0 - drop_sound = 'sound/items/drop/paper.ogg' - pickup_sound = 'sound/items/pickup/paper.ogg' - -/obj/item/clothing/head/collectable/tophat - name = "collectable top hat" - desc = "A top hat worn by only the most prestigious hat collectors." - icon_state = "tophat" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/captain - name = "collectable site manager's hat" - desc = "A Collectable Hat that'll make you look just like a real comdom!" - icon_state = "captain" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/police - name = "collectable police officer's hat" - desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." - icon_state = "policehelm" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/beret - name = "collectable beret" - desc = "A Collectable red Beret. It smells faintly of Garlic." - icon_state = "beret" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/welding - name = "collectable welding helmet" - desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" - icon_state = "welding" - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/collectable/slime - name = "collectable slime hat" - desc = "Just like a real Brain Slug!" - icon_state = "headslime" - item_state_slots = list(slot_r_hand_str = "greenbandana", slot_l_hand_str = "greenbandana") - -/obj/item/clothing/head/collectable/flatcap - name = "collectable flat cap" - desc = "A Collectible farmer's Flat Cap!" - icon_state = "flat_cap" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - -/obj/item/clothing/head/collectable/pirate - name = "collectable pirate hat" - desc = "You'd make a great Dread Syndie Roberts!" - icon_state = "pirate" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/kitty - name = "collectable kitty ears" - desc = "The fur feels.....a bit too realistic." - icon_state = "kitty" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/rabbitears - name = "collectable rabbit ears" - desc = "Not as lucky as the feet!" - icon_state = "bunny" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/wizard - name = "collectable wizard's hat" - desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." - icon_state = "wizard" - -/obj/item/clothing/head/collectable/hardhat - name = "collectable hard hat" - desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" - icon_state = "hardhat0_old_yellow" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/HoS - name = "collectable HoS hat" - desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" - icon_state = "hoscap" - body_parts_covered = 0 - -/obj/item/clothing/head/collectable/thunderdome - name = "collectable Thunderdome helmet" - desc = "Go Red! I mean Green! I mean Red! No Green!" - icon_state = "thunderdome" - -/obj/item/clothing/head/collectable/swat - name = "collectable SWAT helmet" - desc = "Now you can be in the Deathsquad too!" - icon_state = "swat" + +//Hat Station 13 + +/obj/item/clothing/head/collectable + name = "collectable hat" + desc = "A rare collectable hat." + +/obj/item/clothing/head/collectable/petehat + name = "ultra rare hat" + desc = "an ultra rare hat. It commands a certain respect." + icon_state = "petehat" + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/head/mob_teshari.dmi', + SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi' + ) + +/obj/item/clothing/head/collectable/slime + name = "collectable slime cap!" + desc = "It just latches right in place!" + icon_state = "headslime" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/xenom + name = "collectable xenomorph helmet!" + desc = "Hiss hiss hiss!" + icon_state = "xenom" + item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/collectable/chef + name = "collectable chef's hat" + desc = "A rare Chef's Hat meant for hat collectors!" + icon_state = "chefhat" + +/obj/item/clothing/head/collectable/paper + name = "collectable paper hat" + desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." + icon_state = "paper" + body_parts_covered = 0 + drop_sound = 'sound/items/drop/paper.ogg' + pickup_sound = 'sound/items/pickup/paper.ogg' + +/obj/item/clothing/head/collectable/tophat + name = "collectable top hat" + desc = "A top hat worn by only the most prestigious hat collectors." + icon_state = "tophat" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/captain + name = "collectable site manager's hat" + desc = "A Collectable Hat that'll make you look just like a real comdom!" + icon_state = "captain" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/police + name = "collectable police officer's hat" + desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." + icon_state = "policehelm" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/beret + name = "collectable beret" + desc = "A Collectable red Beret. It smells faintly of Garlic." + icon_state = "beret" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/welding + name = "collectable welding helmet" + desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" + icon_state = "welding" + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/collectable/slime + name = "collectable slime hat" + desc = "Just like a real Brain Slug!" + icon_state = "headslime" + item_state_slots = list(slot_r_hand_str = "greenbandana", slot_l_hand_str = "greenbandana") + +/obj/item/clothing/head/collectable/flatcap + name = "collectable flat cap" + desc = "A Collectible farmer's Flat Cap!" + icon_state = "flat_cap" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + +/obj/item/clothing/head/collectable/pirate + name = "collectable pirate hat" + desc = "You'd make a great Dread Syndie Roberts!" + icon_state = "pirate" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/kitty + name = "collectable kitty ears" + desc = "The fur feels.....a bit too realistic." + icon_state = "kitty" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/rabbitears + name = "collectable rabbit ears" + desc = "Not as lucky as the feet!" + icon_state = "bunny" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/wizard + name = "collectable wizard's hat" + desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." + icon_state = "wizard" + +/obj/item/clothing/head/collectable/hardhat + name = "collectable hard hat" + desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" + icon_state = "hardhat0_old_yellow" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/HoS + name = "collectable HoS hat" + desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" + icon_state = "hoscap" + body_parts_covered = 0 + +/obj/item/clothing/head/collectable/thunderdome + name = "collectable Thunderdome helmet" + desc = "Go Red! I mean Green! I mean Red! No Green!" + icon_state = "thunderdome" + +/obj/item/clothing/head/collectable/swat + name = "collectable SWAT helmet" + desc = "Now you can be in the Deathsquad too!" + icon_state = "swat" diff --git a/code/modules/clothing/head/fishing.dm b/code/modules/clothing/head/fishing.dm index bcebd89b57c..8c702be68aa 100644 --- a/code/modules/clothing/head/fishing.dm +++ b/code/modules/clothing/head/fishing.dm @@ -1,152 +1,152 @@ -//these just use the default inhands for soft caps because the amount of extra work to add more colours would be insane otherwise - -/obj/item/clothing/head/fishing - name = "fishing hat" - desc = "It's a peaked cap with a quirky slogan." - icon = 'icons/inventory/head/item_vr_fishing.dmi' - icon_state = "greensoft0" - item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft", slot_head_str = "greensoft0") - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_hats.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_hats.dmi', - slot_head_str = 'icons/inventory/head/mob_vr_fishing.dmi' - ) - siemens_coefficient = 0.9 - body_parts_covered = 0 - var/slogan = "" - var/hatsize = 0 - -/obj/item/clothing/head/fishing/Initialize() - //short phrases that women and fish may have about you - var/feelings = list("love me", - "fear me", - "despise me", - "are ambivalent towards me", - "don't care about me", - "lust after me", - "eat me", - "are down bad for me", - "can't stand me", - "want me", - "kill me", - "devour me", - "swallow me", - "avoid me", - "have no words for me", - "hate me", - "make memes of me", - "demoralise me", - "perpetually mock me", - "scare me", - "issue restraining orders to me", - "leave me", - "took the kids from me", - "demand satisfaction from me", - "dropkick me", - "suplex me", - "worry about me", - "gossip about me", - "defame me", - "disbelieve my existence", - "conspire against me", - "draw me as the soyjack", - "put me in their tummies", - "crunchatize me", - "crunchatize me, cap'n!", - "wreck me", - "feed me", - "beg to get in my stomach", - "walk all over me", - "drink me like a milkshake", - "beg me to step on them", - "call me Mommy", - "call me Daddy", - "step on me", - "lay eggs down my throat", - "dream about me", - "choke me with their thighs", - "steal my lunch money", - "contact me about my spaceship's extended warranty", - "envy me", - "combo me", - "want to kidnap me", - "fold me in half", - "think my spacetruck is hot", - "hate my spacetruck", - "are more into my spacetruck than me", - "ask me for docking codes", - "think of me while kneading dough", - "gun me down in the streets of miami", - "drop me off of inconveniently high places", - "drop me into inconveniently tight places", - "leave threatening messages on my voicemail", - "write fanfiction about me", - "attempt to approach me" - ) - //significantly more complex feelings that women and fish may have about you - var/verylongfeelings = list("construct inferior defensive walls lacking additional fall back locations as they believe their initial defense shall be enough to withstand me", - "insist upon forming an unsteady yet reliable alliance in which they teeter upon the dual edge of betrayal and ruination in the perpetual desire to bring ruin upon me", - "construct elaborate fantasies about my graphic and harrowing death at the hands of a giant robot", - "call it oven when you of in the cold food of out hot eat the food", - "are hurled millennia into the future for their hubris but nonetheless attempt to enslave me from my abode deep within the earth", - "make light of my attire while at the coffee shop, unaware that I am a powerful wizard with the means to cast forth the veil of ignorance from their eyes", - "cower before my T-posing to assert dominance, except I don't know the difference between T-posing and A-posing (neither of which have anything to do with T&A, which is very confusing)", - "use my power to create a new universe but destroy all life on earth in the process and are left alone to rebuild with nothing for company but me", - "hold A to charge a buster shot only to get hit by my homing missiles, starting a juggle chain which results in losing all their health and having to restart from the last checkpoint", - "attempt once more to best me and gain access to my keep of riches and treasure, but I put their foolish ambitions to rest with force", - "bump into me in the supermarket and exchange an awkward hello, neither of us expecting to meet each other in this liminal environs and unsure how the context of our relationship can adapt to this happenstance congregation", - "pursue me to the ends of the earth, constantly thwarted by inconsequential setbacks, and when they catch up and I ask in an exasperated voice why they respond 'you owe me a dollar'", - "call me just to 'chat' but actually mention they are moving and, obviously, since I'm the ONLY guy they know with a truck it would be great if I could help out, they said they'd buy me pizza but that doesn't really cover it", - "shuffle uncomfortably as we continue to sit in near-total silence having exhausted all topics of conversation hours ago but neither of us having worked up the spirit to suggest that we part ways and go home", - "shove me into their large gaping maw, covering me in drool as I descend into their warm depths, becoming covered by hot bubbling acids that dissolve my body while I play Animal Crossing New Leaf on my Nintendo 3DS", - "promise me McDonalds but conveniently forget to pull into the drivethrough on the way home, leaving me burgerless", - "leave me in the car all alone with my favorite music and a bottle of water in the summer", - "create designs of particularly verbose hats describing extreme complexities and deep philosophical implications of their unusually specific feelings and often not particularly kind thoughts about me", - "methodically place brick after agonising brick in the wall they are constructing to forever entomb me in the basement which they claimed contained a case of Amontillado wine" - ) - - //time to actually generate the slogan - //50% chance of it just being a basic women/fish combo - if(prob(50)) - slogan = "Women [pick(feelings)], fish [pick(feelings)]" - else //we generate something more complex - if(prob(90)) //USUALLY one of the short simple phrases - slogan = "[pick("Women", "Fish")] [pick(feelings)]" - else - slogan = "[pick("Women", "Fish")] [pick(verylongfeelings)]" - hatsize += 2 - - //second line - if(prob(90)) //USUALLY one of the short simple phrases - slogan = "[slogan], [pick("Women", "Fish")] [pick(feelings)]" - else - slogan = "[slogan], [pick("Women", "Fish")] [pick(verylongfeelings)]" - hatsize += 2 - //chance of a third line - if(prob(50)) - if(prob(50)) //if a third line is rolled it's way more likely to be a long one - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" - hatsize += 1 - else - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" - hatsize += 3 - //you can even get a fourth - if(prob(25)) - if(prob(25)) //if a fourth line is rolled it's way WAY more likely to be a long one - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" - hatsize += 1 - else - slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" - hatsize += 3 - - - //now we have the slogan, apply this to the description and name - desc = "A peaked cap with text reading '[slogan]'." - name = "\improper '[slogan]' hat" - - //pick a hue - var/colourtype = pick("green", "red", "blue", "yellow", "purple", "orange", "grey") - - //finally, take our hat size and pick the icon accordingly - icon_state = "[colourtype]soft[hatsize]" +//these just use the default inhands for soft caps because the amount of extra work to add more colours would be insane otherwise + +/obj/item/clothing/head/fishing + name = "fishing hat" + desc = "It's a peaked cap with a quirky slogan." + icon = 'icons/inventory/head/item_vr_fishing.dmi' + icon_state = "greensoft0" + item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft", slot_head_str = "greensoft0") + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_hats.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_hats.dmi', + slot_head_str = 'icons/inventory/head/mob_vr_fishing.dmi' + ) + siemens_coefficient = 0.9 + body_parts_covered = 0 + var/slogan = "" + var/hatsize = 0 + +/obj/item/clothing/head/fishing/Initialize() + //short phrases that women and fish may have about you + var/feelings = list("love me", + "fear me", + "despise me", + "are ambivalent towards me", + "don't care about me", + "lust after me", + "eat me", + "are down bad for me", + "can't stand me", + "want me", + "kill me", + "devour me", + "swallow me", + "avoid me", + "have no words for me", + "hate me", + "make memes of me", + "demoralise me", + "perpetually mock me", + "scare me", + "issue restraining orders to me", + "leave me", + "took the kids from me", + "demand satisfaction from me", + "dropkick me", + "suplex me", + "worry about me", + "gossip about me", + "defame me", + "disbelieve my existence", + "conspire against me", + "draw me as the soyjack", + "put me in their tummies", + "crunchatize me", + "crunchatize me, cap'n!", + "wreck me", + "feed me", + "beg to get in my stomach", + "walk all over me", + "drink me like a milkshake", + "beg me to step on them", + "call me Mommy", + "call me Daddy", + "step on me", + "lay eggs down my throat", + "dream about me", + "choke me with their thighs", + "steal my lunch money", + "contact me about my spaceship's extended warranty", + "envy me", + "combo me", + "want to kidnap me", + "fold me in half", + "think my spacetruck is hot", + "hate my spacetruck", + "are more into my spacetruck than me", + "ask me for docking codes", + "think of me while kneading dough", + "gun me down in the streets of miami", + "drop me off of inconveniently high places", + "drop me into inconveniently tight places", + "leave threatening messages on my voicemail", + "write fanfiction about me", + "attempt to approach me" + ) + //significantly more complex feelings that women and fish may have about you + var/verylongfeelings = list("construct inferior defensive walls lacking additional fall back locations as they believe their initial defense shall be enough to withstand me", + "insist upon forming an unsteady yet reliable alliance in which they teeter upon the dual edge of betrayal and ruination in the perpetual desire to bring ruin upon me", + "construct elaborate fantasies about my graphic and harrowing death at the hands of a giant robot", + "call it oven when you of in the cold food of out hot eat the food", + "are hurled millennia into the future for their hubris but nonetheless attempt to enslave me from my abode deep within the earth", + "make light of my attire while at the coffee shop, unaware that I am a powerful wizard with the means to cast forth the veil of ignorance from their eyes", + "cower before my T-posing to assert dominance, except I don't know the difference between T-posing and A-posing (neither of which have anything to do with T&A, which is very confusing)", + "use my power to create a new universe but destroy all life on earth in the process and are left alone to rebuild with nothing for company but me", + "hold A to charge a buster shot only to get hit by my homing missiles, starting a juggle chain which results in losing all their health and having to restart from the last checkpoint", + "attempt once more to best me and gain access to my keep of riches and treasure, but I put their foolish ambitions to rest with force", + "bump into me in the supermarket and exchange an awkward hello, neither of us expecting to meet each other in this liminal environs and unsure how the context of our relationship can adapt to this happenstance congregation", + "pursue me to the ends of the earth, constantly thwarted by inconsequential setbacks, and when they catch up and I ask in an exasperated voice why they respond 'you owe me a dollar'", + "call me just to 'chat' but actually mention they are moving and, obviously, since I'm the ONLY guy they know with a truck it would be great if I could help out, they said they'd buy me pizza but that doesn't really cover it", + "shuffle uncomfortably as we continue to sit in near-total silence having exhausted all topics of conversation hours ago but neither of us having worked up the spirit to suggest that we part ways and go home", + "shove me into their large gaping maw, covering me in drool as I descend into their warm depths, becoming covered by hot bubbling acids that dissolve my body while I play Animal Crossing New Leaf on my Nintendo 3DS", + "promise me McDonalds but conveniently forget to pull into the drivethrough on the way home, leaving me burgerless", + "leave me in the car all alone with my favorite music and a bottle of water in the summer", + "create designs of particularly verbose hats describing extreme complexities and deep philosophical implications of their unusually specific feelings and often not particularly kind thoughts about me", + "methodically place brick after agonising brick in the wall they are constructing to forever entomb me in the basement which they claimed contained a case of Amontillado wine" + ) + + //time to actually generate the slogan + //50% chance of it just being a basic women/fish combo + if(prob(50)) + slogan = "Women [pick(feelings)], fish [pick(feelings)]" + else //we generate something more complex + if(prob(90)) //USUALLY one of the short simple phrases + slogan = "[pick("Women", "Fish")] [pick(feelings)]" + else + slogan = "[pick("Women", "Fish")] [pick(verylongfeelings)]" + hatsize += 2 + + //second line + if(prob(90)) //USUALLY one of the short simple phrases + slogan = "[slogan], [pick("Women", "Fish")] [pick(feelings)]" + else + slogan = "[slogan], [pick("Women", "Fish")] [pick(verylongfeelings)]" + hatsize += 2 + //chance of a third line + if(prob(50)) + if(prob(50)) //if a third line is rolled it's way more likely to be a long one + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" + hatsize += 1 + else + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" + hatsize += 3 + //you can even get a fourth + if(prob(25)) + if(prob(25)) //if a fourth line is rolled it's way WAY more likely to be a long one + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(feelings)]" + hatsize += 1 + else + slogan = "[slogan], [pick("Women", "Fish", "Men", "Beasts")] [pick(verylongfeelings)]" + hatsize += 3 + + + //now we have the slogan, apply this to the description and name + desc = "A peaked cap with text reading '[slogan]'." + name = "\improper '[slogan]' hat" + + //pick a hue + var/colourtype = pick("green", "red", "blue", "yellow", "purple", "orange", "grey") + + //finally, take our hat size and pick the icon accordingly + icon_state = "[colourtype]soft[hatsize]" item_state_slots = list(slot_r_hand_str = "[colourtype]soft", slot_l_hand_str = "[colourtype]soft", slot_head_str = "[colourtype]soft[hatsize]") \ No newline at end of file diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index bbe3f21b44d..236f7857967 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -1,226 +1,226 @@ -/obj/item/clothing/head/helmet - name = "helmet" - desc = "Standard Security gear. Protects the head from impacts." - icon_state = "helmet" - valid_accessory_slots = (ACCESSORY_SLOT_HELM_C) - restricted_accessory_slots = (ACCESSORY_SLOT_HELM_C) - flags = THICKMATERIAL - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - flags_inv = HIDEEARS|BLOCKHEADHAIR - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - w_class = ITEMSIZE_NORMAL - ear_protection = 1 - drop_sound = 'sound/items/drop/helm.ogg' - pickup_sound = 'sound/items/pickup/helm.ogg' - -/obj/item/clothing/head/helmet/solgov - name = "\improper Solar Confederate Government helmet" - desc = "A helmet painted in Peacekeeper blue. Stands out like a sore thumb." - icon_state = "helmet_sol" - armor = list(melee = 50, bullet = 50, laser = 50,energy = 25, bomb = 30, bio = 0, rad = 0) - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/solgov/command - name = "command helmet" - desc = "A helmet with 'Solar Confederate Government' printed on the back in gold lettering." - icon_state = "helmet_command" - -/obj/item/clothing/head/helmet/solgov/security - name = "security helmet" - desc = "A helmet with 'MASTER AT ARMS' printed on the back in silver lettering." - icon_state = "helmet_security" - -/obj/item/clothing/head/helmet/nt - name = "\improper NanoTrasen helmet" - desc = "A helmet with 'CORPORATE SECURITY' printed on the back in red lettering." - icon_state = "helmet_nt" - -/obj/item/clothing/head/helmet/pcrc - name = "\improper PCRC helmet" - desc = "A helmet with 'PRIVATE SECURITY' printed on the back in cyan lettering." - icon_state = "helmet_pcrc" - -/obj/item/clothing/head/helmet/tac - name = "tactical helmet" - desc = "A tan helmet made from advanced ceramic. Comfortable and robust." - icon_state = "helmet_tac" - armor = list(melee = 50, bullet = 60, laser = 60, energy = 45, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.6 - -/obj/item/clothing/head/helmet/merc - name = "combat helmet" - desc = "A heavily reinforced helmet painted with red markings. Feels like it could take a lot of punishment." - icon_state = "helmet_merc" - armor = list(melee = 70, bullet = 70, laser = 70, energy = 35, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.5 - -/obj/item/clothing/head/helmet/riot - name = "riot helmet" - desc = "It's a helmet specifically designed to protect against close range attacks." - icon_state = "riot" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.7 - valid_accessory_slots = null - action_button_name = "Toggle Visor" - -/obj/item/clothing/head/helmet/riot/attack_self(mob/user as mob) - if(src.icon_state == initial(icon_state)) - src.icon_state = "[icon_state]up" - to_chat(user, "You raise the visor on the riot helmet.") - else - src.icon_state = initial(icon_state) - to_chat(user, "You lower the visor on the riot helmet.") - update_clothing_icon() //so our mob-overlays update - -/obj/item/clothing/head/helmet/laserproof - name = "ablative helmet" - desc = "It's a helmet specifically designed to protect against energy projectiles." - icon_state = "helmet_reflec" - item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") - armor = list(melee = 10, bullet = 10, laser = 80 ,energy = 50, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.1 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/bulletproof - name = "bullet-resistant helmet" - desc = "It's a helmet specifically designed to protect against ballistic projectiles." - icon_state = "helmet_bulletproof" - item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") - armor = list(melee = 10, bullet = 80, laser = 10 ,energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.7 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/combat - name = "combat helmet" - desc = "It's a general purpose combat helmet, designed to protect against typical dangers to your head." - icon_state = "helmet_combat" - item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") - armor = list(melee = 50, bullet = 50, laser = 50 ,energy = 30, bomb = 30, bio = 0, rad = 0) - flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR - siemens_coefficient = 0.6 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/flexitac - name = "tactical light helmet" - desc = "A tan helmet made from advanced ceramic with an integrated tactical flashlight." - icon_state = "flexitac" - armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.6 - light_range = 6 - light_overlay = "helmet_light_dual_green" - action_button_name = "Toggle Head-light" - min_cold_protection_temperature = T0C - 20 - cold_protection = HEAD - -/obj/item/clothing/head/helmet/explorer - name = "explorer hood" - desc = "An armoured hood for exploring harsh environments." - icon_state = "explorer" - flags = THICKMATERIAL - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.9 - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 35, bio = 75, rad = 35) - -/obj/item/clothing/head/helmet/swat - name = "\improper SWAT helmet" - desc = "They're often used by highly trained SWAT Officers." - icon_state = "swat" - armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) - flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.5 - -/obj/item/clothing/head/helmet/alien - name = "alien helmet" - desc = "It's quite larger than your head, but it might still protect it." - icon_state = "alienhelmet" - siemens_coefficient = 0.4 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/alien/tank - name = "alien warhelm" - armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) - -/obj/item/clothing/head/helmet/thunderdome - name = "\improper Thunderdome helmet" - desc = "'Let the battle commence!'" - icon_state = "thunderdome" - armor = list(melee = 80, bullet = 60, laser = 50,energy = 10, bomb = 25, bio = 10, rad = 0) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 1 - -/obj/item/clothing/head/helmet/gladiator - name = "gladiator helmet" - desc = "Ave, Imperator, morituri te salutant." - icon_state = "gladiator" - item_state_slots = list(slot_r_hand_str = "vhelmet", slot_l_hand_str = "vhelmet") - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR - siemens_coefficient = 1 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/tactical - name = "tactical helmet" - desc = "An armored helmet capable of being fitted with a multitude of attachments." - icon_state = "swathelm" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - sprite_sheets = list( - SPECIES_TAJ = 'icons/inventory/head/mob_tajaran.dmi', - SPECIES_UNATHI = 'icons/inventory/head/mob_unathi.dmi', - ) - - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - flags_inv = HIDEEARS|BLOCKHAIR - siemens_coefficient = 0.7 - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/augment - name = "Augment Array" - desc = "A helmet with optical and cranial augments coupled to it." - icon_state = "v62" - item_state_slots = list(slot_r_hand_str = "head_m", slot_l_hand_str = "head_m") - armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) - flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.5 - valid_accessory_slots = null - -//Non-hardsuit ERT helmets. -/obj/item/clothing/head/helmet/ert - name = "emergency response team helmet" - desc = "An in-atmosphere helmet worn by members of the NanoTrasen Emergency Response Team. Protects the head from impacts." - icon_state = "erthelmet_cmd" - item_state_slots = list(slot_r_hand_str = "syndicate-helm-green", slot_l_hand_str = "syndicate-helm-green") - armor = list(melee = 62, bullet = 50, laser = 50,energy = 35, bomb = 10, bio = 2, rad = 0) - valid_accessory_slots = null - -//Commander -/obj/item/clothing/head/helmet/ert/command - name = "emergency response team commander helmet" - desc = "An in-atmosphere helmet worn by the commander of a NanoTrasen Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/head/helmet/ert/security - name = "emergency response team security helmet" - desc = "An in-atmosphere helmet worn by security members of the NanoTrasen Emergency Response Team. Has red highlights." - icon_state = "erthelmet_sec" - -//Engineer -/obj/item/clothing/head/helmet/ert/engineer - name = "emergency response team engineer helmet" - desc = "An in-atmosphere helmet worn by engineering members of the NanoTrasen Emergency Response Team. Has orange highlights." - icon_state = "erthelmet_eng" - -//Medical -/obj/item/clothing/head/helmet/ert/medical - name = "emergency response team medical helmet" - desc = "A set of armor worn by medical members of the NanoTrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_med" +/obj/item/clothing/head/helmet + name = "helmet" + desc = "Standard Security gear. Protects the head from impacts." + icon_state = "helmet" + valid_accessory_slots = (ACCESSORY_SLOT_HELM_C) + restricted_accessory_slots = (ACCESSORY_SLOT_HELM_C) + flags = THICKMATERIAL + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + flags_inv = HIDEEARS|BLOCKHEADHAIR + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + w_class = ITEMSIZE_NORMAL + ear_protection = 1 + drop_sound = 'sound/items/drop/helm.ogg' + pickup_sound = 'sound/items/pickup/helm.ogg' + +/obj/item/clothing/head/helmet/solgov + name = "\improper Solar Confederate Government helmet" + desc = "A helmet painted in Peacekeeper blue. Stands out like a sore thumb." + icon_state = "helmet_sol" + armor = list(melee = 50, bullet = 50, laser = 50,energy = 25, bomb = 30, bio = 0, rad = 0) + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/solgov/command + name = "command helmet" + desc = "A helmet with 'Solar Confederate Government' printed on the back in gold lettering." + icon_state = "helmet_command" + +/obj/item/clothing/head/helmet/solgov/security + name = "security helmet" + desc = "A helmet with 'MASTER AT ARMS' printed on the back in silver lettering." + icon_state = "helmet_security" + +/obj/item/clothing/head/helmet/nt + name = "\improper NanoTrasen helmet" + desc = "A helmet with 'CORPORATE SECURITY' printed on the back in red lettering." + icon_state = "helmet_nt" + +/obj/item/clothing/head/helmet/pcrc + name = "\improper PCRC helmet" + desc = "A helmet with 'PRIVATE SECURITY' printed on the back in cyan lettering." + icon_state = "helmet_pcrc" + +/obj/item/clothing/head/helmet/tac + name = "tactical helmet" + desc = "A tan helmet made from advanced ceramic. Comfortable and robust." + icon_state = "helmet_tac" + armor = list(melee = 50, bullet = 60, laser = 60, energy = 45, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + +/obj/item/clothing/head/helmet/merc + name = "combat helmet" + desc = "A heavily reinforced helmet painted with red markings. Feels like it could take a lot of punishment." + icon_state = "helmet_merc" + armor = list(melee = 70, bullet = 70, laser = 70, energy = 35, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.5 + +/obj/item/clothing/head/helmet/riot + name = "riot helmet" + desc = "It's a helmet specifically designed to protect against close range attacks." + icon_state = "riot" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + valid_accessory_slots = null + action_button_name = "Toggle Visor" + +/obj/item/clothing/head/helmet/riot/attack_self(mob/user as mob) + if(src.icon_state == initial(icon_state)) + src.icon_state = "[icon_state]up" + to_chat(user, "You raise the visor on the riot helmet.") + else + src.icon_state = initial(icon_state) + to_chat(user, "You lower the visor on the riot helmet.") + update_clothing_icon() //so our mob-overlays update + +/obj/item/clothing/head/helmet/laserproof + name = "ablative helmet" + desc = "It's a helmet specifically designed to protect against energy projectiles." + icon_state = "helmet_reflec" + item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") + armor = list(melee = 10, bullet = 10, laser = 80 ,energy = 50, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.1 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/bulletproof + name = "bullet-resistant helmet" + desc = "It's a helmet specifically designed to protect against ballistic projectiles." + icon_state = "helmet_bulletproof" + item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") + armor = list(melee = 10, bullet = 80, laser = 10 ,energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/combat + name = "combat helmet" + desc = "It's a general purpose combat helmet, designed to protect against typical dangers to your head." + icon_state = "helmet_combat" + item_state_slots = list(slot_r_hand_str = "helmet", slot_l_hand_str = "helmet") + armor = list(melee = 50, bullet = 50, laser = 50 ,energy = 30, bomb = 30, bio = 0, rad = 0) + flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR + siemens_coefficient = 0.6 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/flexitac + name = "tactical light helmet" + desc = "A tan helmet made from advanced ceramic with an integrated tactical flashlight." + icon_state = "flexitac" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + light_range = 6 + light_overlay = "helmet_light_dual_green" + action_button_name = "Toggle Head-light" + min_cold_protection_temperature = T0C - 20 + cold_protection = HEAD + +/obj/item/clothing/head/helmet/explorer + name = "explorer hood" + desc = "An armoured hood for exploring harsh environments." + icon_state = "explorer" + flags = THICKMATERIAL + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.9 + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 35, bio = 75, rad = 35) + +/obj/item/clothing/head/helmet/swat + name = "\improper SWAT helmet" + desc = "They're often used by highly trained SWAT Officers." + icon_state = "swat" + armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) + flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.5 + +/obj/item/clothing/head/helmet/alien + name = "alien helmet" + desc = "It's quite larger than your head, but it might still protect it." + icon_state = "alienhelmet" + siemens_coefficient = 0.4 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/alien/tank + name = "alien warhelm" + armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) + +/obj/item/clothing/head/helmet/thunderdome + name = "\improper Thunderdome helmet" + desc = "'Let the battle commence!'" + icon_state = "thunderdome" + armor = list(melee = 80, bullet = 60, laser = 50,energy = 10, bomb = 25, bio = 10, rad = 0) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 1 + +/obj/item/clothing/head/helmet/gladiator + name = "gladiator helmet" + desc = "Ave, Imperator, morituri te salutant." + icon_state = "gladiator" + item_state_slots = list(slot_r_hand_str = "vhelmet", slot_l_hand_str = "vhelmet") + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR + siemens_coefficient = 1 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/tactical + name = "tactical helmet" + desc = "An armored helmet capable of being fitted with a multitude of attachments." + icon_state = "swathelm" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + sprite_sheets = list( + SPECIES_TAJ = 'icons/inventory/head/mob_tajaran.dmi', + SPECIES_UNATHI = 'icons/inventory/head/mob_unathi.dmi', + ) + + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + flags_inv = HIDEEARS|BLOCKHAIR + siemens_coefficient = 0.7 + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/augment + name = "Augment Array" + desc = "A helmet with optical and cranial augments coupled to it." + icon_state = "v62" + item_state_slots = list(slot_r_hand_str = "head_m", slot_l_hand_str = "head_m") + armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0) + flags_inv = HIDEEARS|HIDEEYES|BLOCKHEADHAIR + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELMET_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.5 + valid_accessory_slots = null + +//Non-hardsuit ERT helmets. +/obj/item/clothing/head/helmet/ert + name = "emergency response team helmet" + desc = "An in-atmosphere helmet worn by members of the NanoTrasen Emergency Response Team. Protects the head from impacts." + icon_state = "erthelmet_cmd" + item_state_slots = list(slot_r_hand_str = "syndicate-helm-green", slot_l_hand_str = "syndicate-helm-green") + armor = list(melee = 62, bullet = 50, laser = 50,energy = 35, bomb = 10, bio = 2, rad = 0) + valid_accessory_slots = null + +//Commander +/obj/item/clothing/head/helmet/ert/command + name = "emergency response team commander helmet" + desc = "An in-atmosphere helmet worn by the commander of a NanoTrasen Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/head/helmet/ert/security + name = "emergency response team security helmet" + desc = "An in-atmosphere helmet worn by security members of the NanoTrasen Emergency Response Team. Has red highlights." + icon_state = "erthelmet_sec" + +//Engineer +/obj/item/clothing/head/helmet/ert/engineer + name = "emergency response team engineer helmet" + desc = "An in-atmosphere helmet worn by engineering members of the NanoTrasen Emergency Response Team. Has orange highlights." + icon_state = "erthelmet_eng" + +//Medical +/obj/item/clothing/head/helmet/ert/medical + name = "emergency response team medical helmet" + desc = "A set of armor worn by medical members of the NanoTrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_med" diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 29bd574d65b..3178da47572 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -1,291 +1,291 @@ - -//Bartender -/obj/item/clothing/head/chefhat - name = "chef's hat" - desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." - icon_state = "chefhat" - -/obj/item/clothing/head/hairnet - name = "hairnet" - desc = "A hairnet used to keep the hair out of the way and out of the food." - icon_state = "hairnet" - sprite_sheets = list( - SPECIES_TAJARAN = 'icons/inventory/head/mob_tajaran.dmi' - ) - -//Captain -/obj/item/clothing/head/caphat - name = "site manager's hat" - icon_state = "captain" - desc = "It's good being the king." - body_parts_covered = 0 - -/obj/item/clothing/head/caphat/cap - name = "site manager's cap" - desc = "You fear to wear it for the negligence it brings." - icon_state = "capcap" - -/obj/item/clothing/head/caphat/formal - name = "parade hat" - desc = "No one in a commanding position should be without a perfect, white hat of ultimate authority." - icon_state = "officercap" - -/obj/item/clothing/head/caphat/beret - name = "captain's beret" - desc = "A beret fit for a leader." - icon_state = "beretcap" - -//HOP -/obj/item/clothing/head/caphat/hop - name = "crew resource's hat" - desc = "A stylish hat that both protects you from enraged former-crewmembers and gives you a false sense of authority." - icon_state = "hopcap" - -/obj/item/clothing/head/caphat/hop/beret - name = "head of personnel's beret" - desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." - icon_state = "berethop" - -/obj/item/clothing/head/caphat/hop/beret/white - name = "head of personnel's white beret" - desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." - icon_state = "berethopwhite" - -//Chaplain -/obj/item/clothing/head/chaplain_hood - name = "chaplain's hood" - desc = "It's a hood that covers the head. It keeps you warm during the space winters." - icon_state = "chaplain_hood" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - flags_inv = BLOCKHAIR - body_parts_covered = HEAD - -//Chaplain but spookier -/obj/item/clothing/head/chaplain_hood/whiteout - name = "white hood" - desc = "It's a generic white hood. Very spooky." - icon_state = "whiteout_hood" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - -//Chaplain -/obj/item/clothing/head/nun_hood - name = "nun hood" - desc = "Maximum piety in this star system." - icon_state = "nun_hood" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - flags_inv = BLOCKHAIR - body_parts_covered = HEAD - -//Mime -/obj/item/clothing/head/beret - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret" - body_parts_covered = 0 - -//Security -/obj/item/clothing/head/beret/sec - name = "security beret" - desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." - icon_state = "beret_officer" - item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") - -/obj/item/clothing/head/beret/sec/navy/officer - name = "officer beret" - desc = "A navy blue beret with an officer's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_navy_officer" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/sec/navy/hos - name = "Head of Security beret" - desc = "A navy blue beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_navy_hos" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/sec/navy/warden - name = "warden beret" - desc = "A navy blue beret with a warden's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_navy_warden" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/sec/corporate/officer - name = "officer beret" - desc = "A corporate black beret with an officer's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_corporate_officer" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/beret/sec/corporate/hos - name = "Head of Security beret" - desc = "A corporate black beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_corporate_hos" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/beret/sec/corporate/warden - name = "warden beret" - desc = "A corporate black beret with a warden's rank emblem. For officers that are more inclined towards style than safety." - icon_state = "beret_corporate_warden" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/helmet/warden - name = "warden's helmet" - desc = "Standard Warden gear. Protects the head from impacts." - -/obj/item/clothing/head/helmet/warden/hat - name = "warden's hat" - desc = "It's a special hat issued to the Warden of a securiy force." - icon_state = "policehelm" - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/HoS - name = "Head of Security helmet" - desc = "Standard Head of Security gear. Protects the head from impacts." - -/obj/item/clothing/head/helmet/HoS/hat - name = "Head of Security Hat" - desc = "The hat of the Head of Security. For showing the officers who's in charge." - icon_state = "hoscap" - valid_accessory_slots = null - -/obj/item/clothing/head/helmet/dermal - name = "Dermal Armour Patch" - desc = "You're not quite sure how you manage to take it on and off, but it implants nicely in your head." - icon_state = "dermal" - item_state_slots = list(slot_r_hand_str = "", slot_l_hand_str = "") - valid_accessory_slots = null - show_examine = FALSE - flags_inv = null - -/obj/item/clothing/head/det - name = "detective fedora" - desc = "A specially designed fedora that is woven with protective fibers. It also makes you look cool." - icon_state = "fedora_brown" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) - armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - valid_accessory_slots = null - show_examine = FALSE - -/obj/item/clothing/head/det/grey - icon_state = "fedora_grey" - -/obj/item/clothing/head/beret/engineering - name = "engineering beret" - desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." - icon_state = "beret_orange" - -/obj/item/clothing/head/beret/purple - name = "purple beret" - desc = "A stylish, if purple, beret." - icon_state = "beret_purpleyellow" - -/obj/item/clothing/head/beret/centcom/officer - name = "officers beret" - desc = "A dark blue beret adorned with a silver patch. Worn by NanoTrasen Officials." - icon_state = "beret_centcom_officer" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - -/obj/item/clothing/head/beret/centcom/captain - name = "captains beret" - desc = "A white beret adorned with a blue patch. Worn by NanoTrasen command staff." - icon_state = "beret_centcom_captain" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - -/obj/item/clothing/head/beret/sec/gov - name = "officer beret" - desc = "A black beret with a gold emblem." - icon_state = "beret_corporate_hos" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - - -//Medical -/obj/item/clothing/head/surgery - name = "surgical cap" - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." - icon_state = "surgcap_blue" - item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") - flags_inv = BLOCKHEADHAIR - -/obj/item/clothing/head/surgery/purple - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." - icon_state = "surgcap_purple" - item_state_slots = list(slot_r_hand_str = "beret_purple", slot_l_hand_str = "beret_purple") - -/obj/item/clothing/head/surgery/blue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." - icon_state = "surgcap_blue" - item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") - -/obj/item/clothing/head/surgery/green - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." - icon_state = "surgcap_green" - item_state_slots = list(slot_r_hand_str = "beret_green", slot_l_hand_str = "beret_green") - -/obj/item/clothing/head/surgery/black - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is black." - icon_state = "surgcap_black" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - -/obj/item/clothing/head/surgery/navyblue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is navy blue." - icon_state = "surgcap_navyblue" - item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") - -/obj/item/clothing/head/beret/medical - name = "medical officer's beret" - desc = "A fancy beret with a blue cross, smells sterile." - icon_state = "beretmed" - -/obj/item/clothing/head/beret/medical/chem - name = "chemist's beret" - desc = "A fancy beret with an orange beaker. You're not sure if you should smell it." - icon_state = "beretchem" - -/obj/item/clothing/head/beret/medical/viro - name = "virologist's beret" - desc = "A fancy beret with a green cross. Hopefully it's virus free!" - icon_state = "beretviro" - -/obj/item/clothing/head/beret/medical/cmo - name = "chief medical officer's beret" - desc = "A fancy beret with a green cross, signifying your status in the station's medbay." - icon_state = "beretcmo" - -/obj/item/clothing/head/beret/medical/cmo/blue - name = "chief medical officer's beret" - desc = "A fancy beret with a blue and white cross. Try not to be the chief malpractice officer in it!" - icon_state = "beretcmoblue" - -//Science - -/obj/item/clothing/head/beret/science - name = "scientist's beret" - desc = "A scientist's beret. Looks like it's covered in slime." - icon_state = "beretsci" - -/obj/item/clothing/head/beret/science/robotics - name = "roboticist's beret" - desc = "A roboticist's beret. It strongly smells of oil." - icon_state = "beretrobo" - -/obj/item/clothing/head/beret/science/rd - name = "research director's beret" - desc = "A beret worn only by highly intelligent people. Or so its wearers say." - icon_state = "beretrd" - -//Chief Engineer -/obj/item/clothing/head/beret/engineering/ce - name = "chief engineer's beret" - desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." - icon_state = "beretce" - -/obj/item/clothing/head/beret/engineering/ce/white - name = "chief engineer's white beret" - desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." - icon_state = "beretcewhite" - -//Quartermaster -/obj/item/clothing/head/beret/qm - name = "quartermaster's beret" - desc = "This headwear shows off your Cargonian leadership." + +//Bartender +/obj/item/clothing/head/chefhat + name = "chef's hat" + desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." + icon_state = "chefhat" + +/obj/item/clothing/head/hairnet + name = "hairnet" + desc = "A hairnet used to keep the hair out of the way and out of the food." + icon_state = "hairnet" + sprite_sheets = list( + SPECIES_TAJARAN = 'icons/inventory/head/mob_tajaran.dmi' + ) + +//Captain +/obj/item/clothing/head/caphat + name = "site manager's hat" + icon_state = "captain" + desc = "It's good being the king." + body_parts_covered = 0 + +/obj/item/clothing/head/caphat/cap + name = "site manager's cap" + desc = "You fear to wear it for the negligence it brings." + icon_state = "capcap" + +/obj/item/clothing/head/caphat/formal + name = "parade hat" + desc = "No one in a commanding position should be without a perfect, white hat of ultimate authority." + icon_state = "officercap" + +/obj/item/clothing/head/caphat/beret + name = "captain's beret" + desc = "A beret fit for a leader." + icon_state = "beretcap" + +//HOP +/obj/item/clothing/head/caphat/hop + name = "crew resource's hat" + desc = "A stylish hat that both protects you from enraged former-crewmembers and gives you a false sense of authority." + icon_state = "hopcap" + +/obj/item/clothing/head/caphat/hop/beret + name = "head of personnel's beret" + desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." + icon_state = "berethop" + +/obj/item/clothing/head/caphat/hop/beret/white + name = "head of personnel's white beret" + desc = "The symbol of true bureaucratic micromanagement, although in a fancy form." + icon_state = "berethopwhite" + +//Chaplain +/obj/item/clothing/head/chaplain_hood + name = "chaplain's hood" + desc = "It's a hood that covers the head. It keeps you warm during the space winters." + icon_state = "chaplain_hood" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + flags_inv = BLOCKHAIR + body_parts_covered = HEAD + +//Chaplain but spookier +/obj/item/clothing/head/chaplain_hood/whiteout + name = "white hood" + desc = "It's a generic white hood. Very spooky." + icon_state = "whiteout_hood" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + +//Chaplain +/obj/item/clothing/head/nun_hood + name = "nun hood" + desc = "Maximum piety in this star system." + icon_state = "nun_hood" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + flags_inv = BLOCKHAIR + body_parts_covered = HEAD + +//Mime +/obj/item/clothing/head/beret + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret" + body_parts_covered = 0 + +//Security +/obj/item/clothing/head/beret/sec + name = "security beret" + desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." + icon_state = "beret_officer" + item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") + +/obj/item/clothing/head/beret/sec/navy/officer + name = "officer beret" + desc = "A navy blue beret with an officer's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_navy_officer" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/sec/navy/hos + name = "Head of Security beret" + desc = "A navy blue beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_navy_hos" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/sec/navy/warden + name = "warden beret" + desc = "A navy blue beret with a warden's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_navy_warden" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/sec/corporate/officer + name = "officer beret" + desc = "A corporate black beret with an officer's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_corporate_officer" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/beret/sec/corporate/hos + name = "Head of Security beret" + desc = "A corporate black beret with a Head of Security's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_corporate_hos" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/beret/sec/corporate/warden + name = "warden beret" + desc = "A corporate black beret with a warden's rank emblem. For officers that are more inclined towards style than safety." + icon_state = "beret_corporate_warden" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/helmet/warden + name = "warden's helmet" + desc = "Standard Warden gear. Protects the head from impacts." + +/obj/item/clothing/head/helmet/warden/hat + name = "warden's hat" + desc = "It's a special hat issued to the Warden of a securiy force." + icon_state = "policehelm" + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/HoS + name = "Head of Security helmet" + desc = "Standard Head of Security gear. Protects the head from impacts." + +/obj/item/clothing/head/helmet/HoS/hat + name = "Head of Security Hat" + desc = "The hat of the Head of Security. For showing the officers who's in charge." + icon_state = "hoscap" + valid_accessory_slots = null + +/obj/item/clothing/head/helmet/dermal + name = "Dermal Armour Patch" + desc = "You're not quite sure how you manage to take it on and off, but it implants nicely in your head." + icon_state = "dermal" + item_state_slots = list(slot_r_hand_str = "", slot_l_hand_str = "") + valid_accessory_slots = null + show_examine = FALSE + flags_inv = null + +/obj/item/clothing/head/det + name = "detective fedora" + desc = "A specially designed fedora that is woven with protective fibers. It also makes you look cool." + icon_state = "fedora_brown" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) + armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + valid_accessory_slots = null + show_examine = FALSE + +/obj/item/clothing/head/det/grey + icon_state = "fedora_grey" + +/obj/item/clothing/head/beret/engineering + name = "engineering beret" + desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." + icon_state = "beret_orange" + +/obj/item/clothing/head/beret/purple + name = "purple beret" + desc = "A stylish, if purple, beret." + icon_state = "beret_purpleyellow" + +/obj/item/clothing/head/beret/centcom/officer + name = "officers beret" + desc = "A dark blue beret adorned with a silver patch. Worn by NanoTrasen Officials." + icon_state = "beret_centcom_officer" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + +/obj/item/clothing/head/beret/centcom/captain + name = "captains beret" + desc = "A white beret adorned with a blue patch. Worn by NanoTrasen command staff." + icon_state = "beret_centcom_captain" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + +/obj/item/clothing/head/beret/sec/gov + name = "officer beret" + desc = "A black beret with a gold emblem." + icon_state = "beret_corporate_hos" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + + +//Medical +/obj/item/clothing/head/surgery + name = "surgical cap" + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." + icon_state = "surgcap_blue" + item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") + flags_inv = BLOCKHEADHAIR + +/obj/item/clothing/head/surgery/purple + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." + icon_state = "surgcap_purple" + item_state_slots = list(slot_r_hand_str = "beret_purple", slot_l_hand_str = "beret_purple") + +/obj/item/clothing/head/surgery/blue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." + icon_state = "surgcap_blue" + item_state_slots = list(slot_r_hand_str = "beret_blue", slot_l_hand_str = "beret_blue") + +/obj/item/clothing/head/surgery/green + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." + icon_state = "surgcap_green" + item_state_slots = list(slot_r_hand_str = "beret_green", slot_l_hand_str = "beret_green") + +/obj/item/clothing/head/surgery/black + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is black." + icon_state = "surgcap_black" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + +/obj/item/clothing/head/surgery/navyblue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is navy blue." + icon_state = "surgcap_navyblue" + item_state_slots = list(slot_r_hand_str = "beret_navy", slot_l_hand_str = "beret_navy") + +/obj/item/clothing/head/beret/medical + name = "medical officer's beret" + desc = "A fancy beret with a blue cross, smells sterile." + icon_state = "beretmed" + +/obj/item/clothing/head/beret/medical/chem + name = "chemist's beret" + desc = "A fancy beret with an orange beaker. You're not sure if you should smell it." + icon_state = "beretchem" + +/obj/item/clothing/head/beret/medical/viro + name = "virologist's beret" + desc = "A fancy beret with a green cross. Hopefully it's virus free!" + icon_state = "beretviro" + +/obj/item/clothing/head/beret/medical/cmo + name = "chief medical officer's beret" + desc = "A fancy beret with a green cross, signifying your status in the station's medbay." + icon_state = "beretcmo" + +/obj/item/clothing/head/beret/medical/cmo/blue + name = "chief medical officer's beret" + desc = "A fancy beret with a blue and white cross. Try not to be the chief malpractice officer in it!" + icon_state = "beretcmoblue" + +//Science + +/obj/item/clothing/head/beret/science + name = "scientist's beret" + desc = "A scientist's beret. Looks like it's covered in slime." + icon_state = "beretsci" + +/obj/item/clothing/head/beret/science/robotics + name = "roboticist's beret" + desc = "A roboticist's beret. It strongly smells of oil." + icon_state = "beretrobo" + +/obj/item/clothing/head/beret/science/rd + name = "research director's beret" + desc = "A beret worn only by highly intelligent people. Or so its wearers say." + icon_state = "beretrd" + +//Chief Engineer +/obj/item/clothing/head/beret/engineering/ce + name = "chief engineer's beret" + desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." + icon_state = "beretce" + +/obj/item/clothing/head/beret/engineering/ce/white + name = "chief engineer's white beret" + desc = "A beret that will surely make you look way cooler than a hard hat, although lack of protection is the price." + icon_state = "beretcewhite" + +//Quartermaster +/obj/item/clothing/head/beret/qm + name = "quartermaster's beret" + desc = "This headwear shows off your Cargonian leadership." icon_state = "beretqm" \ No newline at end of file diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index f0851e780a3..71272a69994 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -1,565 +1,565 @@ -/obj/item/clothing/head/centhat - name = "\improper CentCom. hat" - icon_state = "centcom" - desc = "It's good to be emperor." - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/centhat/customs - name = "Customs Hat" - desc = "A formal hat for SolGov Customs Officers." - icon_state = "customshat" - -/obj/item/clothing/head/halo - name = "halo" - desc = "a small metal ring, floating above it's wearer." - icon_state = "halo" - -/obj/item/clothing/head/headband/maid/modern - name = "modern maid headband" - desc = "Just like from my Japanese cartoons!" - icon_state = "maid_headband" - -/obj/item/clothing/head/pin - icon_state = "pin" - addblends = "pin_a" - name = "hair pin" - desc = "A nice hair pin." - slot_flags = SLOT_HEAD | SLOT_EARS - body_parts_covered = 0 - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' - -/obj/item/clothing/head/pin/pink - icon_state = "pinkpin" - addblends = null - name = "pink hair hat" - -/obj/item/clothing/head/pin/clover - icon_state = "cloverpin" - name = "clover pin" - addblends = null - desc = "A hair pin in the shape of a clover leaf." - -/obj/item/clothing/head/pin/butterfly - icon_state = "butterflypin" - name = "butterfly pin" - addblends = null - desc = "A hair pin in the shape of a bright blue butterfly." - -/obj/item/clothing/head/pin/magnetic - icon_state = "magnetpin" - name = "magnetic 'pin'" - addblends = null - desc = "Finally, a hair pin even a Morpheus chassis can use." - matter = list(MAT_STEEL = 10) - -/obj/item/clothing/head/pin/flower - name = "red flower pin" - icon_state = "hairflower" - addblends = null - desc = "Smells nice." - -/obj/item/clothing/head/pin/flower/blue - icon_state = "hairflower_blue" - name = "blue flower pin" - -/obj/item/clothing/head/pin/flower/pink - icon_state = "hairflower_pink" - name = "pink flower pin" - -/obj/item/clothing/head/pin/flower/yellow - icon_state = "hairflower_yellow" - name = "yellow flower pin" - -/obj/item/clothing/head/pin/flower/violet - icon_state = "hairflower_violet" - name = "violet flower pin" - -/obj/item/clothing/head/pin/flower/orange - icon_state = "hairflower_orange" - name = "orange flower pin" - -/obj/item/clothing/head/pin/flower/white - icon_state = "hairflower_white" - addblends = "hairflower_white_a" - name = "flower pin" - -/obj/item/clothing/head/pin/bow - icon_state = "bow" - addblends = "bow_a" - name = "hair bow" - desc = "A ribbon tied into a bow with a clip on the back to attach to hair." - item_state_slots = list(slot_r_hand_str = "pill", slot_l_hand_str = "pill") - -/obj/item/clothing/head/pin/bow/big - icon_state = "whiteribbon" - name = "ribbon" - -/obj/item/clothing/head/pin/bow/big/red - icon_state = "redribbon" - name = "red ribbon" - addblends = null - -/obj/item/clothing/head/powdered_wig - name = "powdered wig" - desc = "A powdered wig." - icon_state = "pwig" - -/obj/item/clothing/head/redcoat - name = "redcoat's hat" - icon_state = "redcoat" - item_state_slots = list(slot_r_hand_str = "pirate", slot_l_hand_str = "pirate") - desc = "'I guess it's a redhead.'" - body_parts_covered = 0 - -/obj/item/clothing/head/mailman - name = "station cap" - icon_state = "mailman" - item_state_slots = list(slot_r_hand_str = "hopcap", slot_l_hand_str = "hopcap") - desc = "Choo-choo!" - body_parts_covered = 0 - -/obj/item/clothing/head/plaguedoctorhat - name = "plague doctor's hat" - desc = "These were once used by Plague doctors, allegedly. They're pretty much useless." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - permeability_coefficient = 0.01 - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/plaguedoctorhat/gold - name = "golden plague doctor's hat" - desc = "These were once used by plague doctors, allegedly. This one has gold accents." - icon_state = "plaguedoctor2" - -/obj/item/clothing/head/hasturhood - name = "hastur's hood" - desc = "It's unspeakably stylish" - icon_state = "hasturhood" - item_state_slots = list(slot_r_hand_str = "enginering_beret", slot_l_hand_str = "enginering_beret") - flags_inv = BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/nursehat - name = "nurse's hat" - desc = "It allows quick identification of trained medical personnel." - icon_state = "nursehat" - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/syndicatefake - name = "red space-helmet replica" - item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") - icon_state = "syndicate" - desc = "A plastic replica of a bloodthirsty mercenary's space helmet, you'll look just like a real murderous criminal operative in this! This is a toy, it is not made for use in space!" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - siemens_coefficient = 2.0 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/cueball - name = "cueball helmet" - desc = "A large, featureless white orb mean to be worn on your head. How do you even see out of this thing?" - icon_state = "cueball" - flags_inv = BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/greenbandana - name = "green bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "greenbandana" - flags_inv = 0 - body_parts_covered = 0 - -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - body_parts_covered = HEAD|FACE|EYES - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - -/obj/item/clothing/head/justice - name = "justice hat" - desc = "fight for what's righteous!" - icon_state = "justicered" //Does this even exist? - flags_inv = BLOCKHAIR - body_parts_covered = HEAD|EYES - -/obj/item/clothing/head/justice/blue - icon_state = "justiceblue" - -/obj/item/clothing/head/justice/yellow - icon_state = "justiceyellow" - -/obj/item/clothing/head/justice/green - icon_state = "justicegreen" - -/obj/item/clothing/head/justice/pink - icon_state = "justicepink" - -/obj/item/clothing/head/rabbitears - name = "rabbit ears" - desc = "Wearing these makes you looks useless, and only good for your sex appeal." - icon_state = "bunny" - body_parts_covered = 0 - -/obj/item/clothing/head/flatcap - name = "flat cap" - desc = "A working man's cap." - icon_state = "flat_cap" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - siemens_coefficient = 0.9 //...what? - -/obj/item/clothing/head/flatcap/grey - icon_state = "flat_capw" - addblends = "flat_capw_a" - item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") - -/obj/item/clothing/head/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - body_parts_covered = 0 - -/obj/item/clothing/head/hgpiratecap - name = "pirate hat" - desc = "Yarr." - icon_state = "hgpiratecap" - item_state_slots = list(slot_r_hand_str = "hoscap", slot_l_hand_str = "hoscap") - body_parts_covered = 0 - -/obj/item/clothing/head/bandana - name = "pirate bandana" - desc = "Yarr." - icon_state = "bandana" - item_state_slots = list(slot_r_hand_str = "redbandana", slot_l_hand_str = "redbandana") - -/obj/item/clothing/head/witchwig - name = "witch costume wig" - desc = "Eeeee~heheheheheheh!" - icon_state = "witch" - flags_inv = BLOCKHAIR - siemens_coefficient = 2.0 - -/obj/item/clothing/head/chicken - name = "chicken suit head" - desc = "Bkaw!" - icon_state = "chickenhead" - flags_inv = BLOCKHAIR - siemens_coefficient = 0.7 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/bearpelt - name = "bear pelt hat" - desc = "Fuzzy." - icon_state = "bearpelt" - item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") - flags_inv = BLOCKHAIR - siemens_coefficient = 0.7 - -/obj/item/clothing/head/xenos - name = "xenos helmet" - icon_state = "xenos" - item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") - desc = "A helmet made out of chitinous alien hide." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - siemens_coefficient = 2.0 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/philosopher_wig - name = "natural philosopher's wig" - desc = "A stylish monstrosity unearthed from Earth's Renaissance period. With this most distinguish'd wig, you'll be ready for your next soiree!" - icon_state = "philosopher_wig" - item_state_slots = list(slot_r_hand_str = "pwig", slot_l_hand_str = "pwig") - flags_inv = BLOCKHAIR - siemens_coefficient = 2.0 //why is it so conductive?! - body_parts_covered = 0 - -/obj/item/clothing/head/orangebandana //themij: Taryn Kifer - name = "orange bandana" - desc = "An orange piece of cloth, worn on the head." - icon_state = "orange_bandana" - body_parts_covered = 0 - -/obj/item/clothing/head/hijab - name = "hijab" - desc = "A veil that is wrapped to cover the head and chest" - icon_state = "hijab" - addblends = "hijab_a" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - body_parts_covered = 0 - flags_inv = BLOCKHAIR - -/obj/item/clothing/head/kippa - name = "kippa" - desc = "A small, brimless cap." - icon_state = "kippa" - addblends = "kippa_a" - body_parts_covered = 0 - -/obj/item/clothing/head/turban - name = "turban" - desc = "A cloth used to wind around the head" - icon_state = "turban" - addblends = "turban_a" - item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") - body_parts_covered = 0 - flags_inv = BLOCKHEADHAIR - -/obj/item/clothing/head/taqiyah - name = "taqiyah" - desc = "A short, rounded skullcap usually worn for religious purposes." - icon_state = "taqiyah" - addblends = "taqiyah_a" - item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") - -/obj/item/clothing/head/beanie - name = "beanie" - desc = "A head-hugging brimless winter cap. This one is tight." - icon_state = "beanie" - addblends = "beanie_a" - body_parts_covered = 0 - -/obj/item/clothing/head/beanie_loose - name = "loose beanie" - desc = "A head-hugging brimless winter cap. This one is loose." - icon_state = "beanie_hang" - addblends = "beanie_hang_a" - body_parts_covered = 0 - -/obj/item/clothing/head/beretg - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret_g" - addblends = "beret_g_a" - body_parts_covered = 0 - -/obj/item/clothing/head/sombrero - name = "sombrero" - desc = "A wide-brimmed hat popularly worn in Mexico." - icon_state = "sombrero" - body_parts_covered = 0 - -/obj/item/clothing/head/headband/maid - name = "maid headband" - desc = "Keeps hair out of the way for important... jobs." - icon_state = "maid" - body_parts_covered = 0 - -/obj/item/clothing/head/maangtikka - name = "maang tikka" - desc = "A jeweled headpiece originating in India." - icon_state = "maangtikka" - body_parts_covered = 0 - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' - -/obj/item/clothing/head/jingasa - name = "jingasa" - desc = "A wide, flat rain hat originally from Japan." - icon_state = "jingasa" - body_parts_covered = 0 - item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") - -/obj/item/clothing/head/cowl - name = "black cowl" - desc = "A gold-lined black cowl. It gives off uncomfortable cult vibes, but fancy." - icon_state = "cowl" - body_parts_covered = 0 - -/obj/item/clothing/head/cowl - name = "white cowl" - desc = "A gold-lined white cowl. It gives off uncomfortable cult vibes, but fancy." - icon_state = "whitecowl" - body_parts_covered = 0 - -/obj/item/clothing/head/blackngoldheaddress - name = "black and gold headdress" - desc = "An odd looking headdress that covers the eyes." - icon_state = "blackngoldheaddress" - flags_inv = HIDEEYES - body_parts_covered = HEAD|EYES - -//Corporate Berets - -/obj/item/clothing/head/beret/corp/saare - name = "\improper SAARE beret" - desc = "A red beret denoting service with Stealth Assault Enterprises. For mercenaries that are more inclined towards style than safety." - icon_state = "beret_red" - -/obj/item/clothing/head/beret/corp/saare/officer - name = "\improper SAARE officer beret" - desc = "A red beret with a gold insignia, denoting senior service with Stealth Assault Enterprises. For mercenaries who are more inclined towards style than safety." - icon_state = "beret_redgold" - -/obj/item/clothing/head/beret/corp/pcrc - name = "\improper PCRC beret" - desc = "A black beret with a PCRC logo insignia, denoting service with Proxima Centauri Risk Control. For private security personnel that are more inclined towards style than safety." - icon_state = "beret_black_observatory" - - -/obj/item/clothing/head/beret/corp/hedberg - name = "\improper Hedberg-Hammarstrom beret" - desc = "A tan beret denoting service with Hedberg-Hammarstrom private security. For mercenaries who are more inclined towards style than safety." - icon_state = "beret_tan" - -/obj/item/clothing/head/beret/corp/xion - name = "\improper Xion beret" - desc = "An orange beret denoting employment with Xion Manufacturing. For personnel that are more inclined towards style than safety." - icon_state = "beret_orange" - -//Stylish Hats - -/obj/item/clothing/head/bowler - name = "bowler hat" - desc = "Gentleman, elite aboard!" - icon_state = "bowler" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - body_parts_covered = 0 - -/obj/item/clothing/head/that - name = "top-hat" - desc = "It's an amish looking hat." - icon_state = "tophat" - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/beaverhat - name = "beaver hat" - desc = "Soft felt makes this hat both comfortable and elegant." - icon_state = "beaver_hat" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/boaterhat - name = "boater hat" - desc = "The ultimate in summer fashion." - icon_state = "boater_hat" - item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") - body_parts_covered = 0 - -/obj/item/clothing/head/fedora - name = "fedora" - icon_state = "fedora_grey" - desc = "A sharp, stylish hat that's grey in color." - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - body_parts_covered = 0 - -/obj/item/clothing/head/fedora/brown - desc = "A brown fedora. Perfect for detectives or those trying to pilfer artifacts." - icon_state = "fedora_brown" - allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) - -/obj/item/clothing/head/fedora/white - desc = "A white fedora, really cool hat if you're a mobster. A really lame hat if you're not." - icon_state = "fedora_white" - -/obj/item/clothing/head/fedora/beige - desc = "A beige fedora. Either the cornerstone of a reporter's style or a poor attempt at looking cool. Depends on the person wearing it." - icon_state = "fedora_beige" - -/obj/item/clothing/head/fedora/panama - desc = "A fancy, cream colored fedora. Columbian pure." - icon_state = "fedora_panama" - -/obj/item/clothing/head/trilby - name = "trilby" - icon_state = "trilby" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - desc = "M'lady" - -/obj/item/clothing/head/trilby/feather - name = "feather trilby" - icon_state = "feather_trilby" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - desc = "A sharp, stylish hat with a feather." - -/obj/item/clothing/head/fez - name = "fez" - icon_state = "fez" - desc = "You should wear a fez. Fezzes are cool." - -//Cowboy Hats - -/obj/item/clothing/head/cowboy - name = "cowboy hat" - desc = "For those that have spurs that go jingle jangle jingle." - icon_state = "cowboy_1" - body_parts_covered = 0 - -/obj/item/clothing/head/cowboy/rattan - name = "rattan cowboy hat" - desc = "Made from the same straw harvested from the fields." - icon_state = "cowboy_2" - -/obj/item/clothing/head/cowboy/dark - name = "dark cowboy hat" - desc = "Protect yer head in this new frontier." - icon_state = "cowboy_3" - -/obj/item/clothing/head/cowboy/ranger - name = "ranger cowboy hat" - desc = "Feel the western vibe from this good ol' classic." - icon_state = "cowboy_4" - -/obj/item/clothing/head/cowboy/rustler - name = "rustler cowboy hat" - desc = "Rustle up some of that there cattle bucko." - icon_state = "cowboy_5" - -/obj/item/clothing/head/cowboy/black - name = "black cowboy hat" - desc = "Perfect for the budding tram robber." - icon_state = "cowboy_7" - -/obj/item/clothing/head/cowboy/fancy - name = "fancy cowboy hat" - desc = "Premium black leather had with a rattlesnake hatband to top the ensemble." - icon_state = "cowboy_8" - -/obj/item/clothing/head/cowboy/wide - name = "wide-brimmed cowboy hat" - desc = "Because justice isn't going to dispense itself." - icon_state = "cowboy_6" - -/obj/item/clothing/head/cowboy/bandit - name = "bandit cowboy hat" - desc = "You can almost hear the old western music." - icon_state = "cowboy_9" - -/obj/item/clothing/head/cowboy/small - name = "small cowboy hat" - desc = "For the tiniest of cowboys." - icon_state = "cowboy_small" - -/obj/item/clothing/head/wheat - name = "straw hat" - desc = "It's a hat made from synthetic straw. Brought to you by \"Country Girls LLC.\" the choice brand for the galaxy's working class." - icon_state = "wheat" - -//Ruin Marine (Doom Marine) -/obj/item/clothing/head/marine - name = "marine helmet" - desc = "A marine helmet prop from the popular game 'Ruin'." - icon_state = "marine" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - -//Laser Tag Helmets -/obj/item/clothing/head/bluetag - name = "blue laser tag helmet" - desc = "Blue Pride, Station Wide." - icon_state = "bluetag" - flags_inv = HIDEEARS|BLOCKHEADHAIR - body_parts_covered = HEAD|EYES - -/obj/item/clothing/head/redtag - name = "red laser tag helmet" - desc = "Reputed to go faster." - icon_state = "redtag" - flags_inv = HIDEEARS|BLOCKHEADHAIR - body_parts_covered = HEAD|EYES +/obj/item/clothing/head/centhat + name = "\improper CentCom. hat" + icon_state = "centcom" + desc = "It's good to be emperor." + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/centhat/customs + name = "Customs Hat" + desc = "A formal hat for SolGov Customs Officers." + icon_state = "customshat" + +/obj/item/clothing/head/halo + name = "halo" + desc = "a small metal ring, floating above it's wearer." + icon_state = "halo" + +/obj/item/clothing/head/headband/maid/modern + name = "modern maid headband" + desc = "Just like from my Japanese cartoons!" + icon_state = "maid_headband" + +/obj/item/clothing/head/pin + icon_state = "pin" + addblends = "pin_a" + name = "hair pin" + desc = "A nice hair pin." + slot_flags = SLOT_HEAD | SLOT_EARS + body_parts_covered = 0 + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' + +/obj/item/clothing/head/pin/pink + icon_state = "pinkpin" + addblends = null + name = "pink hair hat" + +/obj/item/clothing/head/pin/clover + icon_state = "cloverpin" + name = "clover pin" + addblends = null + desc = "A hair pin in the shape of a clover leaf." + +/obj/item/clothing/head/pin/butterfly + icon_state = "butterflypin" + name = "butterfly pin" + addblends = null + desc = "A hair pin in the shape of a bright blue butterfly." + +/obj/item/clothing/head/pin/magnetic + icon_state = "magnetpin" + name = "magnetic 'pin'" + addblends = null + desc = "Finally, a hair pin even a Morpheus chassis can use." + matter = list(MAT_STEEL = 10) + +/obj/item/clothing/head/pin/flower + name = "red flower pin" + icon_state = "hairflower" + addblends = null + desc = "Smells nice." + +/obj/item/clothing/head/pin/flower/blue + icon_state = "hairflower_blue" + name = "blue flower pin" + +/obj/item/clothing/head/pin/flower/pink + icon_state = "hairflower_pink" + name = "pink flower pin" + +/obj/item/clothing/head/pin/flower/yellow + icon_state = "hairflower_yellow" + name = "yellow flower pin" + +/obj/item/clothing/head/pin/flower/violet + icon_state = "hairflower_violet" + name = "violet flower pin" + +/obj/item/clothing/head/pin/flower/orange + icon_state = "hairflower_orange" + name = "orange flower pin" + +/obj/item/clothing/head/pin/flower/white + icon_state = "hairflower_white" + addblends = "hairflower_white_a" + name = "flower pin" + +/obj/item/clothing/head/pin/bow + icon_state = "bow" + addblends = "bow_a" + name = "hair bow" + desc = "A ribbon tied into a bow with a clip on the back to attach to hair." + item_state_slots = list(slot_r_hand_str = "pill", slot_l_hand_str = "pill") + +/obj/item/clothing/head/pin/bow/big + icon_state = "whiteribbon" + name = "ribbon" + +/obj/item/clothing/head/pin/bow/big/red + icon_state = "redribbon" + name = "red ribbon" + addblends = null + +/obj/item/clothing/head/powdered_wig + name = "powdered wig" + desc = "A powdered wig." + icon_state = "pwig" + +/obj/item/clothing/head/redcoat + name = "redcoat's hat" + icon_state = "redcoat" + item_state_slots = list(slot_r_hand_str = "pirate", slot_l_hand_str = "pirate") + desc = "'I guess it's a redhead.'" + body_parts_covered = 0 + +/obj/item/clothing/head/mailman + name = "station cap" + icon_state = "mailman" + item_state_slots = list(slot_r_hand_str = "hopcap", slot_l_hand_str = "hopcap") + desc = "Choo-choo!" + body_parts_covered = 0 + +/obj/item/clothing/head/plaguedoctorhat + name = "plague doctor's hat" + desc = "These were once used by Plague doctors, allegedly. They're pretty much useless." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + permeability_coefficient = 0.01 + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/plaguedoctorhat/gold + name = "golden plague doctor's hat" + desc = "These were once used by plague doctors, allegedly. This one has gold accents." + icon_state = "plaguedoctor2" + +/obj/item/clothing/head/hasturhood + name = "hastur's hood" + desc = "It's unspeakably stylish" + icon_state = "hasturhood" + item_state_slots = list(slot_r_hand_str = "enginering_beret", slot_l_hand_str = "enginering_beret") + flags_inv = BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/nursehat + name = "nurse's hat" + desc = "It allows quick identification of trained medical personnel." + icon_state = "nursehat" + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/syndicatefake + name = "red space-helmet replica" + item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") + icon_state = "syndicate" + desc = "A plastic replica of a bloodthirsty mercenary's space helmet, you'll look just like a real murderous criminal operative in this! This is a toy, it is not made for use in space!" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + siemens_coefficient = 2.0 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/cueball + name = "cueball helmet" + desc = "A large, featureless white orb mean to be worn on your head. How do you even see out of this thing?" + icon_state = "cueball" + flags_inv = BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/greenbandana + name = "green bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "greenbandana" + flags_inv = 0 + body_parts_covered = 0 + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + body_parts_covered = HEAD|FACE|EYES + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + +/obj/item/clothing/head/justice + name = "justice hat" + desc = "fight for what's righteous!" + icon_state = "justicered" //Does this even exist? + flags_inv = BLOCKHAIR + body_parts_covered = HEAD|EYES + +/obj/item/clothing/head/justice/blue + icon_state = "justiceblue" + +/obj/item/clothing/head/justice/yellow + icon_state = "justiceyellow" + +/obj/item/clothing/head/justice/green + icon_state = "justicegreen" + +/obj/item/clothing/head/justice/pink + icon_state = "justicepink" + +/obj/item/clothing/head/rabbitears + name = "rabbit ears" + desc = "Wearing these makes you looks useless, and only good for your sex appeal." + icon_state = "bunny" + body_parts_covered = 0 + +/obj/item/clothing/head/flatcap + name = "flat cap" + desc = "A working man's cap." + icon_state = "flat_cap" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + siemens_coefficient = 0.9 //...what? + +/obj/item/clothing/head/flatcap/grey + icon_state = "flat_capw" + addblends = "flat_capw_a" + item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") + +/obj/item/clothing/head/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + body_parts_covered = 0 + +/obj/item/clothing/head/hgpiratecap + name = "pirate hat" + desc = "Yarr." + icon_state = "hgpiratecap" + item_state_slots = list(slot_r_hand_str = "hoscap", slot_l_hand_str = "hoscap") + body_parts_covered = 0 + +/obj/item/clothing/head/bandana + name = "pirate bandana" + desc = "Yarr." + icon_state = "bandana" + item_state_slots = list(slot_r_hand_str = "redbandana", slot_l_hand_str = "redbandana") + +/obj/item/clothing/head/witchwig + name = "witch costume wig" + desc = "Eeeee~heheheheheheh!" + icon_state = "witch" + flags_inv = BLOCKHAIR + siemens_coefficient = 2.0 + +/obj/item/clothing/head/chicken + name = "chicken suit head" + desc = "Bkaw!" + icon_state = "chickenhead" + flags_inv = BLOCKHAIR + siemens_coefficient = 0.7 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/bearpelt + name = "bear pelt hat" + desc = "Fuzzy." + icon_state = "bearpelt" + item_state_slots = list(slot_r_hand_str = "beret_black", slot_l_hand_str = "beret_black") + flags_inv = BLOCKHAIR + siemens_coefficient = 0.7 + +/obj/item/clothing/head/xenos + name = "xenos helmet" + icon_state = "xenos" + item_state_slots = list(slot_r_hand_str = "xenos_helm", slot_l_hand_str = "xenos_helm") + desc = "A helmet made out of chitinous alien hide." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + siemens_coefficient = 2.0 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/philosopher_wig + name = "natural philosopher's wig" + desc = "A stylish monstrosity unearthed from Earth's Renaissance period. With this most distinguish'd wig, you'll be ready for your next soiree!" + icon_state = "philosopher_wig" + item_state_slots = list(slot_r_hand_str = "pwig", slot_l_hand_str = "pwig") + flags_inv = BLOCKHAIR + siemens_coefficient = 2.0 //why is it so conductive?! + body_parts_covered = 0 + +/obj/item/clothing/head/orangebandana //themij: Taryn Kifer + name = "orange bandana" + desc = "An orange piece of cloth, worn on the head." + icon_state = "orange_bandana" + body_parts_covered = 0 + +/obj/item/clothing/head/hijab + name = "hijab" + desc = "A veil that is wrapped to cover the head and chest" + icon_state = "hijab" + addblends = "hijab_a" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + body_parts_covered = 0 + flags_inv = BLOCKHAIR + +/obj/item/clothing/head/kippa + name = "kippa" + desc = "A small, brimless cap." + icon_state = "kippa" + addblends = "kippa_a" + body_parts_covered = 0 + +/obj/item/clothing/head/turban + name = "turban" + desc = "A cloth used to wind around the head" + icon_state = "turban" + addblends = "turban_a" + item_state_slots = list(slot_r_hand_str = "beret_white", slot_l_hand_str = "beret_white") + body_parts_covered = 0 + flags_inv = BLOCKHEADHAIR + +/obj/item/clothing/head/taqiyah + name = "taqiyah" + desc = "A short, rounded skullcap usually worn for religious purposes." + icon_state = "taqiyah" + addblends = "taqiyah_a" + item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") + +/obj/item/clothing/head/beanie + name = "beanie" + desc = "A head-hugging brimless winter cap. This one is tight." + icon_state = "beanie" + addblends = "beanie_a" + body_parts_covered = 0 + +/obj/item/clothing/head/beanie_loose + name = "loose beanie" + desc = "A head-hugging brimless winter cap. This one is loose." + icon_state = "beanie_hang" + addblends = "beanie_hang_a" + body_parts_covered = 0 + +/obj/item/clothing/head/beretg + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret_g" + addblends = "beret_g_a" + body_parts_covered = 0 + +/obj/item/clothing/head/sombrero + name = "sombrero" + desc = "A wide-brimmed hat popularly worn in Mexico." + icon_state = "sombrero" + body_parts_covered = 0 + +/obj/item/clothing/head/headband/maid + name = "maid headband" + desc = "Keeps hair out of the way for important... jobs." + icon_state = "maid" + body_parts_covered = 0 + +/obj/item/clothing/head/maangtikka + name = "maang tikka" + desc = "A jeweled headpiece originating in India." + icon_state = "maangtikka" + body_parts_covered = 0 + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + +/obj/item/clothing/head/jingasa + name = "jingasa" + desc = "A wide, flat rain hat originally from Japan." + icon_state = "jingasa" + body_parts_covered = 0 + item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") + +/obj/item/clothing/head/cowl + name = "black cowl" + desc = "A gold-lined black cowl. It gives off uncomfortable cult vibes, but fancy." + icon_state = "cowl" + body_parts_covered = 0 + +/obj/item/clothing/head/cowl + name = "white cowl" + desc = "A gold-lined white cowl. It gives off uncomfortable cult vibes, but fancy." + icon_state = "whitecowl" + body_parts_covered = 0 + +/obj/item/clothing/head/blackngoldheaddress + name = "black and gold headdress" + desc = "An odd looking headdress that covers the eyes." + icon_state = "blackngoldheaddress" + flags_inv = HIDEEYES + body_parts_covered = HEAD|EYES + +//Corporate Berets + +/obj/item/clothing/head/beret/corp/saare + name = "\improper SAARE beret" + desc = "A red beret denoting service with Stealth Assault Enterprises. For mercenaries that are more inclined towards style than safety." + icon_state = "beret_red" + +/obj/item/clothing/head/beret/corp/saare/officer + name = "\improper SAARE officer beret" + desc = "A red beret with a gold insignia, denoting senior service with Stealth Assault Enterprises. For mercenaries who are more inclined towards style than safety." + icon_state = "beret_redgold" + +/obj/item/clothing/head/beret/corp/pcrc + name = "\improper PCRC beret" + desc = "A black beret with a PCRC logo insignia, denoting service with Proxima Centauri Risk Control. For private security personnel that are more inclined towards style than safety." + icon_state = "beret_black_observatory" + + +/obj/item/clothing/head/beret/corp/hedberg + name = "\improper Hedberg-Hammarstrom beret" + desc = "A tan beret denoting service with Hedberg-Hammarstrom private security. For mercenaries who are more inclined towards style than safety." + icon_state = "beret_tan" + +/obj/item/clothing/head/beret/corp/xion + name = "\improper Xion beret" + desc = "An orange beret denoting employment with Xion Manufacturing. For personnel that are more inclined towards style than safety." + icon_state = "beret_orange" + +//Stylish Hats + +/obj/item/clothing/head/bowler + name = "bowler hat" + desc = "Gentleman, elite aboard!" + icon_state = "bowler" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + body_parts_covered = 0 + +/obj/item/clothing/head/that + name = "top-hat" + desc = "It's an amish looking hat." + icon_state = "tophat" + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/beaverhat + name = "beaver hat" + desc = "Soft felt makes this hat both comfortable and elegant." + icon_state = "beaver_hat" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/boaterhat + name = "boater hat" + desc = "The ultimate in summer fashion." + icon_state = "boater_hat" + item_state_slots = list(slot_r_hand_str = "tophat", slot_l_hand_str = "tophat") + body_parts_covered = 0 + +/obj/item/clothing/head/fedora + name = "fedora" + icon_state = "fedora_grey" + desc = "A sharp, stylish hat that's grey in color." + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + body_parts_covered = 0 + +/obj/item/clothing/head/fedora/brown + desc = "A brown fedora. Perfect for detectives or those trying to pilfer artifacts." + icon_state = "fedora_brown" + allowed = list(/obj/item/weapon/reagent_containers/food/snacks/candy_corn, /obj/item/weapon/pen) + +/obj/item/clothing/head/fedora/white + desc = "A white fedora, really cool hat if you're a mobster. A really lame hat if you're not." + icon_state = "fedora_white" + +/obj/item/clothing/head/fedora/beige + desc = "A beige fedora. Either the cornerstone of a reporter's style or a poor attempt at looking cool. Depends on the person wearing it." + icon_state = "fedora_beige" + +/obj/item/clothing/head/fedora/panama + desc = "A fancy, cream colored fedora. Columbian pure." + icon_state = "fedora_panama" + +/obj/item/clothing/head/trilby + name = "trilby" + icon_state = "trilby" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + desc = "M'lady" + +/obj/item/clothing/head/trilby/feather + name = "feather trilby" + icon_state = "feather_trilby" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + desc = "A sharp, stylish hat with a feather." + +/obj/item/clothing/head/fez + name = "fez" + icon_state = "fez" + desc = "You should wear a fez. Fezzes are cool." + +//Cowboy Hats + +/obj/item/clothing/head/cowboy + name = "cowboy hat" + desc = "For those that have spurs that go jingle jangle jingle." + icon_state = "cowboy_1" + body_parts_covered = 0 + +/obj/item/clothing/head/cowboy/rattan + name = "rattan cowboy hat" + desc = "Made from the same straw harvested from the fields." + icon_state = "cowboy_2" + +/obj/item/clothing/head/cowboy/dark + name = "dark cowboy hat" + desc = "Protect yer head in this new frontier." + icon_state = "cowboy_3" + +/obj/item/clothing/head/cowboy/ranger + name = "ranger cowboy hat" + desc = "Feel the western vibe from this good ol' classic." + icon_state = "cowboy_4" + +/obj/item/clothing/head/cowboy/rustler + name = "rustler cowboy hat" + desc = "Rustle up some of that there cattle bucko." + icon_state = "cowboy_5" + +/obj/item/clothing/head/cowboy/black + name = "black cowboy hat" + desc = "Perfect for the budding tram robber." + icon_state = "cowboy_7" + +/obj/item/clothing/head/cowboy/fancy + name = "fancy cowboy hat" + desc = "Premium black leather had with a rattlesnake hatband to top the ensemble." + icon_state = "cowboy_8" + +/obj/item/clothing/head/cowboy/wide + name = "wide-brimmed cowboy hat" + desc = "Because justice isn't going to dispense itself." + icon_state = "cowboy_6" + +/obj/item/clothing/head/cowboy/bandit + name = "bandit cowboy hat" + desc = "You can almost hear the old western music." + icon_state = "cowboy_9" + +/obj/item/clothing/head/cowboy/small + name = "small cowboy hat" + desc = "For the tiniest of cowboys." + icon_state = "cowboy_small" + +/obj/item/clothing/head/wheat + name = "straw hat" + desc = "It's a hat made from synthetic straw. Brought to you by \"Country Girls LLC.\" the choice brand for the galaxy's working class." + icon_state = "wheat" + +//Ruin Marine (Doom Marine) +/obj/item/clothing/head/marine + name = "marine helmet" + desc = "A marine helmet prop from the popular game 'Ruin'." + icon_state = "marine" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + +//Laser Tag Helmets +/obj/item/clothing/head/bluetag + name = "blue laser tag helmet" + desc = "Blue Pride, Station Wide." + icon_state = "bluetag" + flags_inv = HIDEEARS|BLOCKHEADHAIR + body_parts_covered = HEAD|EYES + +/obj/item/clothing/head/redtag + name = "red laser tag helmet" + desc = "Reputed to go faster." + icon_state = "redtag" + flags_inv = HIDEEARS|BLOCKHEADHAIR + body_parts_covered = HEAD|EYES diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 724b77a9afa..42cebdf0f08 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -1,322 +1,338 @@ -/* - * Contents: - * Welding mask - * Cakehat - * Ushanka - * Pumpkin head - * Kitty ears - * Holiday hats - * Crown of Wrath - * Warning cone - */ - -/* - * Welding mask - */ -/obj/item/clothing/head/welding - name = "welding helmet" - desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." - icon_state = "welding" - item_state_slots = list(slot_r_hand_str = "welding", slot_l_hand_str = "welding") - matter = list(MAT_STEEL = 3000, MAT_GLASS = 1000) - var/up = 0 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - body_parts_covered = HEAD|FACE|EYES - action_button_name = "Flip Welding Mask" - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - var/base_state - flash_protection = FLASH_PROTECTION_MAJOR - tint = TINT_HEAVY - drop_sound = 'sound/items/drop/helm.ogg' - pickup_sound = 'sound/items/pickup/helm.ogg' - -/obj/item/clothing/head/welding/attack_self() - toggle() - - -/obj/item/clothing/head/welding/verb/toggle() - set category = "Object" - set name = "Adjust welding mask" - set src in usr - - if(!base_state) - base_state = icon_state - - if(usr.canmove && !usr.stat && !usr.restrained()) - if(src.up) - src.up = !src.up - body_parts_covered |= (EYES|FACE) - flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = base_state - flash_protection = FLASH_PROTECTION_MAJOR - tint = initial(tint) - to_chat(usr, "You flip the [src] down to protect your eyes.") - else - src.up = !src.up - body_parts_covered &= ~(EYES|FACE) - flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = "[base_state]up" - flash_protection = FLASH_PROTECTION_NONE - tint = TINT_NONE - to_chat(usr, "You push the [src] up out of your face.") - update_clothing_icon() //so our mob-overlays - if (ismob(src.loc)) //should allow masks to update when it is opened/closed - var/mob/M = src.loc - M.update_inv_wear_mask() - usr.update_action_buttons() - -/obj/item/clothing/head/welding/demon - name = "demonic welding helmet" - desc = "A painted welding helmet, this one has a demonic face on it." - icon_state = "demonwelding" - item_state_slots = list( - slot_l_hand_str = "demonwelding", - slot_r_hand_str = "demonwelding", - ) - -/obj/item/clothing/head/welding/knight - name = "knightly welding helmet" - desc = "A painted welding helmet, this one looks like a knights helmet." - icon_state = "knightwelding" - item_state_slots = list( - slot_l_hand_str = "knightwelding", - slot_r_hand_str = "knightwelding", - ) - -/obj/item/clothing/head/welding/fancy - name = "fancy welding helmet" - desc = "A painted welding helmet, the black and gold make this one look very fancy." - icon_state = "fancywelding" - item_state_slots = list( - slot_l_hand_str = "fancywelding", - slot_r_hand_str = "fancywelding", - ) - -/obj/item/clothing/head/welding/engie - name = "engineering welding helmet" - desc = "A painted welding helmet, this one has been painted the engineering colours." - icon_state = "engiewelding" - item_state_slots = list( - slot_l_hand_str = "engiewelding", - slot_r_hand_str = "engiewelding", - ) - -/* - * Cakehat - */ -/obj/item/clothing/head/cakehat - name = "cake-hat" - desc = "It's tasty looking!" - icon_state = "cake0" - var/onfire = 0 - body_parts_covered = HEAD - -/obj/item/clothing/head/cakehat/process() - if(!onfire) - STOP_PROCESSING(SSobj, src) - return - - var/turf/location = src.loc - if(istype(location, /mob/)) - var/mob/living/carbon/human/M = location - if(M.item_is_in_hands(src) || M.head == src) - location = M.loc - - if (istype(location, /turf)) - location.hotspot_expose(700, 1) - -/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) - onfire = !(onfire) - if (onfire) - force = 3 - damtype = "fire" - icon_state = "cake1" - START_PROCESSING(SSobj, src) - else - force = null - damtype = "brute" - icon_state = "cake0" - return - - -/* - * Ushanka - */ -/obj/item/clothing/head/ushanka - name = "ushanka" - desc = "Perfect for those cold winter nights." - icon_state = "ushankadown" - flags_inv = HIDEEARS - -/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) - if(src.icon_state == initial(icon_state)) - src.icon_state = "[icon_state]up" - to_chat(user, "You raise the ear flaps on the ushanka.") - else - src.icon_state = initial(icon_state) - to_chat(user, "You lower the ear flaps on the ushanka.") - -/obj/item/clothing/head/ushanka/black - icon_state = "blkushankadown" - -/obj/item/clothing/head/ushanka/soviet - name = "soviet ushanka" - desc = "Perfect for winter in Siberia, da?" - icon_state = "sovushankadown" - -/obj/item/clothing/head/ushanka/hedberg - name = "\improper Hedberg-Hammarstrom fur hat" - desc = "An Hedberg-Hammarstrom private security ushanka." - icon_state = "hedbergushankadown" - -/* - * Pumpkin head - */ -/obj/item/clothing/head/pumpkinhead - name = "carved pumpkin" - desc = "A jack o' lantern! Believed to ward off evil spirits." - icon_state = "hardhat0_pumpkin"//Could stand to be renamed - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - light_range = 2 - light_overlay = "jackolantern" - w_class = ITEMSIZE_NORMAL - drop_sound = 'sound/items/drop/herb.ogg' - pickup_sound = 'sound/items/pickup/herb.ogg' - -/* - * Kitty ears - */ -/obj/item/clothing/head/kitty - name = "kitty ears" - desc = "A pair of kitty ears. Meow!" - icon_state = "kitty" - body_parts_covered = 0 - siemens_coefficient = 1.5 - item_icons = null - -/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/icon/ears = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kitty") - ears.Blend(rgb(user.r_hair, user.g_hair, user.b_hair), ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kittyinner") - ears.Blend(earbit, ICON_OVERLAY) - -/obj/item/clothing/head/richard - name = "chicken mask" - desc = "You can hear the distant sounds of rhythmic electronica." - icon_state = "richard" - item_state_slots = list(slot_r_hand_str = "chickenhead", slot_l_hand_str = "chickenhead") - body_parts_covered = HEAD|FACE - flags_inv = BLOCKHAIR - -/obj/item/clothing/head/santa - name = "santa hat" - desc = "It's a festive christmas hat, in red!" - icon_state = "santahatnorm" - item_state_slots = list(slot_r_hand_str = "santahat", slot_l_hand_str = "santahat") - body_parts_covered = 0 - -/obj/item/clothing/head/santa/green - name = "green santa hat" - desc = "It's a festive christmas hat, in green!" - icon_state = "santahatgreen" - item_state_slots = list(slot_r_hand_str = "santahatgreen", slot_l_hand_str = "santahatgreen") - body_parts_covered = 0 - -/* - * Xenoarch/Surface Loot Hats - */ - -// Triggers an effect when the wearer is 'in grave danger'. -// Causes brainloss when it happens. -/obj/item/clothing/head/psy_crown - name = "broken crown" - desc = "A crown-of-thorns with a missing gem." - var/tension_threshold = 125 - var/cooldown = null // world.time of when this was last triggered. - var/cooldown_duration = 3 MINUTES // How long the cooldown should be. - var/flavor_equip = null // Message displayed to someone who puts this on their head. Drones don't get a message. - var/flavor_unequip = null // Ditto, but for taking it off. - var/flavor_drop = null // Ditto, but for dropping it. - var/flavor_activate = null // Ditto, for but activating. - var/brainloss_cost = 3 // Whenever it activates, inflict this much brainloss on the wearer, as its not good for the mind to wear things that manipulate it. - -/obj/item/clothing/head/psy_crown/proc/activate_ability(var/mob/living/wearer) - cooldown = world.time + cooldown_duration - to_chat(wearer, flavor_activate) - to_chat(wearer, "The inside of your head hurts...") - wearer.adjustBrainLoss(brainloss_cost) - -/obj/item/clothing/head/psy_crown/equipped(var/mob/living/carbon/human/H) - ..() - if(istype(H) && H.head == src && H.is_sentient()) - START_PROCESSING(SSobj, src) - to_chat(H, flavor_equip) - -/obj/item/clothing/head/psy_crown/dropped(var/mob/living/carbon/human/H) - ..() - STOP_PROCESSING(SSobj, src) - if(H.is_sentient()) - if(loc == H) // Still inhand. - to_chat(H, flavor_unequip) - else - to_chat(H, flavor_drop) - -/obj/item/clothing/head/psy_crown/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/head/psy_crown/process() - if(isliving(loc)) - var/mob/living/L = loc - if(world.time >= cooldown && L.is_sentient() && L.get_tension() >= tension_threshold) - activate_ability(L) - - -/obj/item/clothing/head/psy_crown/wrath - name = "red crown" - desc = "A crown-of-thorns set with a red gemstone that seems to glow unnaturally. It feels rather disturbing to touch." - description_info = "This has a chance to cause the wearer to become extremely angry when in extreme danger." - icon_state = "wrathcrown" - flavor_equip = "You feel a bit angrier after putting on this crown." - flavor_unequip = "You feel calmer after removing the crown." - flavor_drop = "You feel much calmer after letting go of the crown." - flavor_activate = "An otherworldly feeling seems to enter your mind, and it ignites your mind in fury!" - -/obj/item/clothing/head/psy_crown/wrath/activate_ability(var/mob/living/wearer) - ..() - wearer.add_modifier(/datum/modifier/berserk, 30 SECONDS) - -/obj/item/clothing/head/psy_crown/gluttony - name = "green crown" - desc = "A crown-of-thorns set with a green gemstone that seems to glow unnaturally. It feels rather disturbing to touch." - description_info = "This has a chance to cause the wearer to become extremely durable, but hungry when in extreme danger." - icon_state = "gluttonycrown" - flavor_equip = "You feel a bit hungrier after putting on this crown." - flavor_unequip = "You feel sated after removing the crown." - flavor_drop = "You feel much more sated after letting go of the crown." - flavor_activate = "An otherworldly feeling seems to enter your mind, and it drives your mind into gluttony!" - -/obj/item/clothing/head/psy_crown/gluttony/activate_ability(var/mob/living/wearer) - ..() - wearer.add_modifier(/datum/modifier/gluttonyregeneration, 45 SECONDS) - -/obj/item/clothing/head/cone - name = "warning cone" - desc = "This cone is trying to warn you of something!" - description_info = "It looks like you can wear it in your head slot." - icon_state = "cone" - item_state = "cone" - drop_sound = 'sound/items/drop/shoes.ogg' - force = 1 - throwforce = 3 - throw_speed = 2 - throw_range = 5 - w_class = 2 - body_parts_covered = HEAD - attack_verb = list("warned", "cautioned", "smashed") - armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) \ No newline at end of file +/* + * Contents: + * Welding mask + * Cakehat + * Ushanka + * Pumpkin head + * Kitty ears + * Holiday hats + * Crown of Wrath + * Warning cone + */ + +/* + * Welding mask + */ +/obj/item/clothing/head/welding + name = "welding helmet" + desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." + icon_state = "welding" + item_state_slots = list(slot_r_hand_str = "welding", slot_l_hand_str = "welding") + matter = list(MAT_STEEL = 3000, MAT_GLASS = 1000) + var/up = 0 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + body_parts_covered = HEAD|FACE|EYES + action_button_name = "Flip Welding Mask" + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + var/base_state + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY + drop_sound = 'sound/items/drop/helm.ogg' + pickup_sound = 'sound/items/pickup/helm.ogg' + +/obj/item/clothing/head/welding/attack_self() + toggle() + + +/obj/item/clothing/head/welding/verb/toggle() + set category = "Object" + set name = "Adjust welding mask" + set src in usr + + if(!base_state) + base_state = icon_state + + if(usr.canmove && !usr.stat && !usr.restrained()) + if(src.up) + src.up = !src.up + body_parts_covered |= (EYES|FACE) + flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = base_state + flash_protection = FLASH_PROTECTION_MAJOR + tint = initial(tint) + to_chat(usr, "You flip the [src] down to protect your eyes.") + else + src.up = !src.up + body_parts_covered &= ~(EYES|FACE) + flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = "[base_state]up" + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You push the [src] up out of your face.") + update_clothing_icon() //so our mob-overlays + if (ismob(src.loc)) //should allow masks to update when it is opened/closed + var/mob/M = src.loc + M.update_inv_wear_mask() + usr.update_action_buttons() + +/obj/item/clothing/head/welding/demon + name = "demonic welding helmet" + desc = "A painted welding helmet, this one has a demonic face on it." + icon_state = "demonwelding" + item_state_slots = list( + slot_l_hand_str = "demonwelding", + slot_r_hand_str = "demonwelding", + ) + +/obj/item/clothing/head/welding/knight + name = "knightly welding helmet" + desc = "A painted welding helmet, this one looks like a knights helmet." + icon_state = "knightwelding" + item_state_slots = list( + slot_l_hand_str = "knightwelding", + slot_r_hand_str = "knightwelding", + ) + +/obj/item/clothing/head/welding/fancy + name = "fancy welding helmet" + desc = "A painted welding helmet, the black and gold make this one look very fancy." + icon_state = "fancywelding" + item_state_slots = list( + slot_l_hand_str = "fancywelding", + slot_r_hand_str = "fancywelding", + ) + +/obj/item/clothing/head/welding/engie + name = "engineering welding helmet" + desc = "A painted welding helmet, this one has been painted the engineering colours." + icon_state = "engiewelding" + item_state_slots = list( + slot_l_hand_str = "engiewelding", + slot_r_hand_str = "engiewelding", + ) + +//Replikant Welding mask + +/obj/item/clothing/head/welding/arar + name = "replikant welding helmet" + desc = "A protective welding mask designed for repair-technician biosynthetic crew, the visor slits are particularly difficult to see out of." + icon = 'icons/inventory/head/item_vr.dmi' + icon_override = 'icons/inventory/head/mob_vr.dmi' + icon_state = "ararwelding" + item_state_slots = list( + SLOT_ID_LEFT_HAND = "ararwelding", + SLOT_ID_RIGHT_HAND = "ararwelding", + ) + + + + +/* + * Cakehat + */ +/obj/item/clothing/head/cakehat + name = "cake-hat" + desc = "It's tasty looking!" + icon_state = "cake0" + var/onfire = 0 + body_parts_covered = HEAD + +/obj/item/clothing/head/cakehat/process() + if(!onfire) + STOP_PROCESSING(SSobj, src) + return + + var/turf/location = src.loc + if(istype(location, /mob/)) + var/mob/living/carbon/human/M = location + if(M.item_is_in_hands(src) || M.head == src) + location = M.loc + + if (istype(location, /turf)) + location.hotspot_expose(700, 1) + +/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) + onfire = !(onfire) + if (onfire) + force = 3 + damtype = "fire" + icon_state = "cake1" + START_PROCESSING(SSobj, src) + else + force = null + damtype = "brute" + icon_state = "cake0" + return + + +/* + * Ushanka + */ +/obj/item/clothing/head/ushanka + name = "ushanka" + desc = "Perfect for those cold winter nights." + icon_state = "ushankadown" + flags_inv = HIDEEARS + +/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) + if(src.icon_state == initial(icon_state)) + src.icon_state = "[icon_state]up" + to_chat(user, "You raise the ear flaps on the ushanka.") + else + src.icon_state = initial(icon_state) + to_chat(user, "You lower the ear flaps on the ushanka.") + +/obj/item/clothing/head/ushanka/black + icon_state = "blkushankadown" + +/obj/item/clothing/head/ushanka/soviet + name = "soviet ushanka" + desc = "Perfect for winter in Siberia, da?" + icon_state = "sovushankadown" + +/obj/item/clothing/head/ushanka/hedberg + name = "\improper Hedberg-Hammarstrom fur hat" + desc = "An Hedberg-Hammarstrom private security ushanka." + icon_state = "hedbergushankadown" + +/* + * Pumpkin head + */ +/obj/item/clothing/head/pumpkinhead + name = "carved pumpkin" + desc = "A jack o' lantern! Believed to ward off evil spirits." + icon_state = "hardhat0_pumpkin"//Could stand to be renamed + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + light_range = 2 + light_overlay = "jackolantern" + w_class = ITEMSIZE_NORMAL + drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' + +/* + * Kitty ears + */ +/obj/item/clothing/head/kitty + name = "kitty ears" + desc = "A pair of kitty ears. Meow!" + icon_state = "kitty" + body_parts_covered = 0 + siemens_coefficient = 1.5 + item_icons = null + +/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/icon/ears = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kitty") + ears.Blend(rgb(user.r_hair, user.g_hair, user.b_hair), ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/inventory/head/mob.dmi', "icon_state" = "kittyinner") + ears.Blend(earbit, ICON_OVERLAY) + +/obj/item/clothing/head/richard + name = "chicken mask" + desc = "You can hear the distant sounds of rhythmic electronica." + icon_state = "richard" + item_state_slots = list(slot_r_hand_str = "chickenhead", slot_l_hand_str = "chickenhead") + body_parts_covered = HEAD|FACE + flags_inv = BLOCKHAIR + +/obj/item/clothing/head/santa + name = "santa hat" + desc = "It's a festive christmas hat, in red!" + icon_state = "santahatnorm" + item_state_slots = list(slot_r_hand_str = "santahat", slot_l_hand_str = "santahat") + body_parts_covered = 0 + +/obj/item/clothing/head/santa/green + name = "green santa hat" + desc = "It's a festive christmas hat, in green!" + icon_state = "santahatgreen" + item_state_slots = list(slot_r_hand_str = "santahatgreen", slot_l_hand_str = "santahatgreen") + body_parts_covered = 0 + +/* + * Xenoarch/Surface Loot Hats + */ + +// Triggers an effect when the wearer is 'in grave danger'. +// Causes brainloss when it happens. +/obj/item/clothing/head/psy_crown + name = "broken crown" + desc = "A crown-of-thorns with a missing gem." + var/tension_threshold = 125 + var/cooldown = null // world.time of when this was last triggered. + var/cooldown_duration = 3 MINUTES // How long the cooldown should be. + var/flavor_equip = null // Message displayed to someone who puts this on their head. Drones don't get a message. + var/flavor_unequip = null // Ditto, but for taking it off. + var/flavor_drop = null // Ditto, but for dropping it. + var/flavor_activate = null // Ditto, for but activating. + var/brainloss_cost = 3 // Whenever it activates, inflict this much brainloss on the wearer, as its not good for the mind to wear things that manipulate it. + +/obj/item/clothing/head/psy_crown/proc/activate_ability(var/mob/living/wearer) + cooldown = world.time + cooldown_duration + to_chat(wearer, flavor_activate) + to_chat(wearer, "The inside of your head hurts...") + wearer.adjustBrainLoss(brainloss_cost) + +/obj/item/clothing/head/psy_crown/equipped(var/mob/living/carbon/human/H) + ..() + if(istype(H) && H.head == src && H.is_sentient()) + START_PROCESSING(SSobj, src) + to_chat(H, flavor_equip) + +/obj/item/clothing/head/psy_crown/dropped(var/mob/living/carbon/human/H) + ..() + STOP_PROCESSING(SSobj, src) + if(H.is_sentient()) + if(loc == H) // Still inhand. + to_chat(H, flavor_unequip) + else + to_chat(H, flavor_drop) + +/obj/item/clothing/head/psy_crown/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/head/psy_crown/process() + if(isliving(loc)) + var/mob/living/L = loc + if(world.time >= cooldown && L.is_sentient() && L.get_tension() >= tension_threshold) + activate_ability(L) + + +/obj/item/clothing/head/psy_crown/wrath + name = "red crown" + desc = "A crown-of-thorns set with a red gemstone that seems to glow unnaturally. It feels rather disturbing to touch." + description_info = "This has a chance to cause the wearer to become extremely angry when in extreme danger." + icon_state = "wrathcrown" + flavor_equip = "You feel a bit angrier after putting on this crown." + flavor_unequip = "You feel calmer after removing the crown." + flavor_drop = "You feel much calmer after letting go of the crown." + flavor_activate = "An otherworldly feeling seems to enter your mind, and it ignites your mind in fury!" + +/obj/item/clothing/head/psy_crown/wrath/activate_ability(var/mob/living/wearer) + ..() + wearer.add_modifier(/datum/modifier/berserk, 30 SECONDS) + +/obj/item/clothing/head/psy_crown/gluttony + name = "green crown" + desc = "A crown-of-thorns set with a green gemstone that seems to glow unnaturally. It feels rather disturbing to touch." + description_info = "This has a chance to cause the wearer to become extremely durable, but hungry when in extreme danger." + icon_state = "gluttonycrown" + flavor_equip = "You feel a bit hungrier after putting on this crown." + flavor_unequip = "You feel sated after removing the crown." + flavor_drop = "You feel much more sated after letting go of the crown." + flavor_activate = "An otherworldly feeling seems to enter your mind, and it drives your mind into gluttony!" + +/obj/item/clothing/head/psy_crown/gluttony/activate_ability(var/mob/living/wearer) + ..() + wearer.add_modifier(/datum/modifier/gluttonyregeneration, 45 SECONDS) + +/obj/item/clothing/head/cone + name = "warning cone" + desc = "This cone is trying to warn you of something!" + description_info = "It looks like you can wear it in your head slot." + icon_state = "cone" + item_state = "cone" + drop_sound = 'sound/items/drop/shoes.ogg' + force = 1 + throwforce = 3 + throw_speed = 2 + throw_range = 5 + w_class = 2 + body_parts_covered = HEAD + attack_verb = list("warned", "cautioned", "smashed") + armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) diff --git a/code/modules/clothing/head/misc_vr.dm b/code/modules/clothing/head/misc_vr.dm index 6879717a697..cceb01a66ae 100644 --- a/code/modules/clothing/head/misc_vr.dm +++ b/code/modules/clothing/head/misc_vr.dm @@ -171,4 +171,13 @@ desc = "A tophat that is far too small to properly sit on someone's head!" icon = 'icons/inventory/head/item_vr.dmi' default_worn_icon = 'icons/inventory/head/mob_vr.dmi' - icon_state = "tiny_tophat" \ No newline at end of file + icon_state = "tiny_tophat" + +//Replikant Hat + +/obj/item/clothing/head/eulrhat + name = "sleek side cap" + desc = "A simple wedge cap with red accents, popular with biosynthetic personnel." + icon = 'icons/inventory/head/item_vr.dmi' + icon_override = 'icons/inventory/head/mob_vr.dmi' + icon_state = "eulrhat" diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index c2c597ad7b2..1d1742bca9d 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -1,114 +1,114 @@ -/obj/item/clothing/head/soft - name = "cargo cap" - desc = "It's a peaked cap in a tasteless yellow color." - icon_state = "cargosoft" - item_state_slots = list(slot_r_hand_str = "cargosoft", slot_l_hand_str = "cargosoft") - var/flipped = 0 - siemens_coefficient = 0.9 - body_parts_covered = 0 - -/obj/item/clothing/head/soft/dropped() - icon_state = initial(icon_state) - flipped=0 - ..() - -/obj/item/clothing/head/soft/attack_self(mob/user) - flipped = !flipped - if(flipped) - icon_state = "[icon_state]_flipped" - to_chat(user, "You flip the hat backwards.") - else - icon_state = initial(icon_state) - to_chat(user, "You flip the hat back in normal position.") - update_clothing_icon() //so our mob-overlays update - -/obj/item/clothing/head/soft/red - name = "red cap" - desc = "It's a baseball hat in a tasteless red color." - icon_state = "redsoft" - item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") - -/obj/item/clothing/head/soft/blue - name = "blue cap" - desc = "It's a peaked cap in a tasteless blue color." - icon_state = "bluesoft" - item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") - -/obj/item/clothing/head/soft/green - name = "green cap" - desc = "It's a peaked cap in a tasteless green color." - icon_state = "greensoft" - item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft") - -/obj/item/clothing/head/soft/yellow - name = "yellow cap" - desc = "It's a peaked cap in a tasteless yellow color." - icon_state = "yellowsoft" - item_state_slots = list(slot_r_hand_str = "yellowsoft", slot_l_hand_str = "yellowsoft") - -/obj/item/clothing/head/soft/grey - name = "grey cap" - desc = "It's a peaked cap in a tasteful grey color." - icon_state = "greysoft" - item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") - -/obj/item/clothing/head/soft/orange - name = "orange cap" - desc = "It's a peaked cap in a tasteless orange color." - icon_state = "orangesoft" - item_state_slots = list(slot_r_hand_str = "orangesoft", slot_l_hand_str = "orangesoft") - -/obj/item/clothing/head/soft/mime - name = "white cap" - desc = "It's a peaked cap in a tasteless white color." - icon_state = "mimesoft" - item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") - -/obj/item/clothing/head/soft/purple - name = "purple cap" - desc = "It's a peaked cap in a tasteless purple color." - icon_state = "purplesoft" - item_state_slots = list(slot_r_hand_str = "purplesoft", slot_l_hand_str = "purplesoft") - -/obj/item/clothing/head/soft/rainbow - name = "rainbow cap" - desc = "It's a peaked cap in a bright rainbow of colors." - icon_state = "rainbowsoft" - item_state_slots = list(slot_r_hand_str = "rainbowsoft", slot_l_hand_str = "rainbowsoft") - -/obj/item/clothing/head/soft/sec - name = "security cap" - desc = "It's a field cap in tasteful red color." - icon_state = "secsoft" - item_state_slots = list(slot_r_hand_str = "secsoft", slot_l_hand_str = "secsoft") - -/obj/item/clothing/head/soft/sec/corp - name = "corporate security cap" - desc = "It's field cap in corporate colors." - icon_state = "corpsoft" - item_state_slots = list(slot_r_hand_str = "corpsoft", slot_l_hand_str = "corpsoft") - -/obj/item/clothing/head/soft/black - name = "black cap" - desc = "It's a peaked cap in a tasteful black color." - icon_state = "blacksoft" - item_state_slots = list(slot_r_hand_str = "blacksoft", slot_l_hand_str = "blacksoft") - -/obj/item/clothing/head/soft/mbill - name = "shipping cap" - desc = "It's a ballcap bearing the colors of Major Bill's Shipping." - icon_state = "mbillsoft" - item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") - catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) - -/obj/item/clothing/head/soft/med - name = "medical cap" - desc = "It's a field cap in white, with a blue cross on the front." - icon_state = "medsoft" - item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") - -/obj/item/clothing/head/soft/paramed - name = "paramedic's cap" - desc = "It's a field cap in dark blue, with a white cross on the front." - icon_state = "emtsoft" - item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") +/obj/item/clothing/head/soft + name = "cargo cap" + desc = "It's a peaked cap in a tasteless yellow color." + icon_state = "cargosoft" + item_state_slots = list(slot_r_hand_str = "cargosoft", slot_l_hand_str = "cargosoft") + var/flipped = 0 + siemens_coefficient = 0.9 + body_parts_covered = 0 + +/obj/item/clothing/head/soft/dropped() + icon_state = initial(icon_state) + flipped=0 + ..() + +/obj/item/clothing/head/soft/attack_self(mob/user) + flipped = !flipped + if(flipped) + icon_state = "[icon_state]_flipped" + to_chat(user, "You flip the hat backwards.") + else + icon_state = initial(icon_state) + to_chat(user, "You flip the hat back in normal position.") + update_clothing_icon() //so our mob-overlays update + +/obj/item/clothing/head/soft/red + name = "red cap" + desc = "It's a baseball hat in a tasteless red color." + icon_state = "redsoft" + item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") + +/obj/item/clothing/head/soft/blue + name = "blue cap" + desc = "It's a peaked cap in a tasteless blue color." + icon_state = "bluesoft" + item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") + +/obj/item/clothing/head/soft/green + name = "green cap" + desc = "It's a peaked cap in a tasteless green color." + icon_state = "greensoft" + item_state_slots = list(slot_r_hand_str = "greensoft", slot_l_hand_str = "greensoft") + +/obj/item/clothing/head/soft/yellow + name = "yellow cap" + desc = "It's a peaked cap in a tasteless yellow color." + icon_state = "yellowsoft" + item_state_slots = list(slot_r_hand_str = "yellowsoft", slot_l_hand_str = "yellowsoft") + +/obj/item/clothing/head/soft/grey + name = "grey cap" + desc = "It's a peaked cap in a tasteful grey color." + icon_state = "greysoft" + item_state_slots = list(slot_r_hand_str = "greysoft", slot_l_hand_str = "greysoft") + +/obj/item/clothing/head/soft/orange + name = "orange cap" + desc = "It's a peaked cap in a tasteless orange color." + icon_state = "orangesoft" + item_state_slots = list(slot_r_hand_str = "orangesoft", slot_l_hand_str = "orangesoft") + +/obj/item/clothing/head/soft/mime + name = "white cap" + desc = "It's a peaked cap in a tasteless white color." + icon_state = "mimesoft" + item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") + +/obj/item/clothing/head/soft/purple + name = "purple cap" + desc = "It's a peaked cap in a tasteless purple color." + icon_state = "purplesoft" + item_state_slots = list(slot_r_hand_str = "purplesoft", slot_l_hand_str = "purplesoft") + +/obj/item/clothing/head/soft/rainbow + name = "rainbow cap" + desc = "It's a peaked cap in a bright rainbow of colors." + icon_state = "rainbowsoft" + item_state_slots = list(slot_r_hand_str = "rainbowsoft", slot_l_hand_str = "rainbowsoft") + +/obj/item/clothing/head/soft/sec + name = "security cap" + desc = "It's a field cap in tasteful red color." + icon_state = "secsoft" + item_state_slots = list(slot_r_hand_str = "secsoft", slot_l_hand_str = "secsoft") + +/obj/item/clothing/head/soft/sec/corp + name = "corporate security cap" + desc = "It's field cap in corporate colors." + icon_state = "corpsoft" + item_state_slots = list(slot_r_hand_str = "corpsoft", slot_l_hand_str = "corpsoft") + +/obj/item/clothing/head/soft/black + name = "black cap" + desc = "It's a peaked cap in a tasteful black color." + icon_state = "blacksoft" + item_state_slots = list(slot_r_hand_str = "blacksoft", slot_l_hand_str = "blacksoft") + +/obj/item/clothing/head/soft/mbill + name = "shipping cap" + desc = "It's a ballcap bearing the colors of Major Bill's Shipping." + icon_state = "mbillsoft" + item_state_slots = list(slot_r_hand_str = "redsoft", slot_l_hand_str = "redsoft") + catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) + +/obj/item/clothing/head/soft/med + name = "medical cap" + desc = "It's a field cap in white, with a blue cross on the front." + icon_state = "medsoft" + item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft") + +/obj/item/clothing/head/soft/paramed + name = "paramedic's cap" + desc = "It's a field cap in dark blue, with a white cross on the front." + icon_state = "emtsoft" + item_state_slots = list(slot_r_hand_str = "bluesoft", slot_l_hand_str = "bluesoft") diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm index 5a311c51724..5678e8bac64 100644 --- a/code/modules/clothing/masks/boxing.dm +++ b/code/modules/clothing/masks/boxing.dm @@ -1,35 +1,35 @@ -/obj/item/clothing/mask/balaclava - name = "balaclava" - desc = "LOADSAMONEY" - icon_state = "balaclava" - item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") - flags_inv = HIDEFACE|BLOCKHAIR - body_parts_covered = FACE|HEAD - w_class = ITEMSIZE_SMALL - -/obj/item/clothing/mask/balaclava/tactical - name = "green balaclava" - desc = "Designed to both hide identities and keep your face comfy and warm." - icon_state = "swatclava" - item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") - flags_inv = HIDEFACE|BLOCKHAIR - w_class = ITEMSIZE_SMALL - -/obj/item/clothing/mask/luchador - name = "Luchador Mask" - desc = "Worn by robust fighters, flying high to defeat their foes!" - icon_state = "luchag" - flags_inv = HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 3.0 - -/obj/item/clothing/mask/luchador/tecnicos - name = "Tecnicos Mask" - desc = "Worn by robust fighters who uphold justice and fight honorably." - icon_state = "luchador" - -/obj/item/clothing/mask/luchador/rudos - name = "Rudos Mask" - desc = "Worn by robust fighters who are willing to do anything to win." +/obj/item/clothing/mask/balaclava + name = "balaclava" + desc = "LOADSAMONEY" + icon_state = "balaclava" + item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") + flags_inv = HIDEFACE|BLOCKHAIR + body_parts_covered = FACE|HEAD + w_class = ITEMSIZE_SMALL + +/obj/item/clothing/mask/balaclava/tactical + name = "green balaclava" + desc = "Designed to both hide identities and keep your face comfy and warm." + icon_state = "swatclava" + item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") + flags_inv = HIDEFACE|BLOCKHAIR + w_class = ITEMSIZE_SMALL + +/obj/item/clothing/mask/luchador + name = "Luchador Mask" + desc = "Worn by robust fighters, flying high to defeat their foes!" + icon_state = "luchag" + flags_inv = HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 3.0 + +/obj/item/clothing/mask/luchador/tecnicos + name = "Tecnicos Mask" + desc = "Worn by robust fighters who uphold justice and fight honorably." + icon_state = "luchador" + +/obj/item/clothing/mask/luchador/rudos + name = "Rudos Mask" + desc = "Worn by robust fighters who are willing to do anything to win." icon_state = "luchar" \ No newline at end of file diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index cf7a0bc9392..c9e5baaf2c5 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -1,63 +1,63 @@ -/obj/item/clothing/mask/breath - desc = "A close-fitting mask that can be connected to an air supply." - name = "breath mask" - icon_state = "breath" - item_state_slots = list(slot_r_hand_str = "breath", slot_l_hand_str = "breath") - item_flags = AIRTIGHT|FLEXIBLEMATERIAL - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.10 - permeability_coefficient = 0.50 - var/hanging = 0 - action_button_name = "Adjust Breath Mask" - pickup_sound = 'sound/items/pickup/component.ogg' - drop_sound = 'sound/items/drop/component.ogg' - - -/obj/item/clothing/mask/breath/proc/adjust_mask(mob/user) - if(user.canmove && !user.stat) - src.hanging = !src.hanging - if (src.hanging) - gas_transfer_coefficient = 1 - body_parts_covered = body_parts_covered & ~FACE - item_flags = item_flags & ~AIRTIGHT - icon_state = "breathdown" - to_chat(user, "Your mask is now hanging on your neck.") - else - gas_transfer_coefficient = initial(gas_transfer_coefficient) - body_parts_covered = initial(body_parts_covered) - item_flags = initial(item_flags) - icon_state = initial(icon_state) - to_chat(user, "You pull the mask up to cover your face.") - update_clothing_icon() - -/obj/item/clothing/mask/breath/attack_self(mob/user) - adjust_mask(user) - -/obj/item/clothing/mask/breath/verb/toggle() - set category = "Object" - set name = "Adjust mask" - set src in usr - - adjust_mask(usr) - -/obj/item/clothing/mask/breath/medical - desc = "A close-fitting sterile mask that can be connected to an air supply." - name = "medical mask" - icon_state = "medical" - item_state_slots = list(slot_r_hand_str = "medical", slot_l_hand_str = "medical") - permeability_coefficient = 0.01 - -/obj/item/clothing/mask/breath/emergency - desc = "A close-fitting mask that is used by the wallmounted emergency oxygen pump." - name = "emergency mask" - icon_state = "breath" - item_state = "breath" - permeability_coefficient = 0.50 - -/obj/item/clothing/mask/breath/anesthetic - desc = "A close-fitting sterile mask that is used by the anesthetic wallmounted pump." - name = "anesthetic mask" - icon_state = "medical" - item_state = "medical" - permeability_coefficient = 0.01 +/obj/item/clothing/mask/breath + desc = "A close-fitting mask that can be connected to an air supply." + name = "breath mask" + icon_state = "breath" + item_state_slots = list(slot_r_hand_str = "breath", slot_l_hand_str = "breath") + item_flags = AIRTIGHT|FLEXIBLEMATERIAL + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.10 + permeability_coefficient = 0.50 + var/hanging = 0 + action_button_name = "Adjust Breath Mask" + pickup_sound = 'sound/items/pickup/component.ogg' + drop_sound = 'sound/items/drop/component.ogg' + + +/obj/item/clothing/mask/breath/proc/adjust_mask(mob/user) + if(user.canmove && !user.stat) + src.hanging = !src.hanging + if (src.hanging) + gas_transfer_coefficient = 1 + body_parts_covered = body_parts_covered & ~FACE + item_flags = item_flags & ~AIRTIGHT + icon_state = "breathdown" + to_chat(user, "Your mask is now hanging on your neck.") + else + gas_transfer_coefficient = initial(gas_transfer_coefficient) + body_parts_covered = initial(body_parts_covered) + item_flags = initial(item_flags) + icon_state = initial(icon_state) + to_chat(user, "You pull the mask up to cover your face.") + update_clothing_icon() + +/obj/item/clothing/mask/breath/attack_self(mob/user) + adjust_mask(user) + +/obj/item/clothing/mask/breath/verb/toggle() + set category = "Object" + set name = "Adjust mask" + set src in usr + + adjust_mask(usr) + +/obj/item/clothing/mask/breath/medical + desc = "A close-fitting sterile mask that can be connected to an air supply." + name = "medical mask" + icon_state = "medical" + item_state_slots = list(slot_r_hand_str = "medical", slot_l_hand_str = "medical") + permeability_coefficient = 0.01 + +/obj/item/clothing/mask/breath/emergency + desc = "A close-fitting mask that is used by the wallmounted emergency oxygen pump." + name = "emergency mask" + icon_state = "breath" + item_state = "breath" + permeability_coefficient = 0.50 + +/obj/item/clothing/mask/breath/anesthetic + desc = "A close-fitting sterile mask that is used by the anesthetic wallmounted pump." + name = "anesthetic mask" + icon_state = "medical" + item_state = "medical" + permeability_coefficient = 0.01 diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index e164234cee7..667e7b41f95 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -1,186 +1,186 @@ -/obj/item/clothing/mask/gas - name = "gas mask" - desc = "A face-covering mask that can be connected to an air supply. Filters harmful gases from the air." - icon_state = "gas_alt" - item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | ALLOW_SURVIVALFOOD - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - body_parts_covered = FACE|EYES - w_class = ITEMSIZE_NORMAL - item_state_slots = list(slot_r_hand_str = "gas_alt", slot_l_hand_str = "gas_alt") - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - siemens_coefficient = 0.9 - var/gas_filter_strength = 1 //For gas mask filters - var/list/filtered_gases = list("phoron", "nitrous_oxide") - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) - var/datum/gas_mixture/gas_filtered = new - - for(var/g in filtered_gases) - if(air.gas[g]) - gas_filtered.gas[g] = air.gas[g] * gas_filter_strength - air.gas[g] -= gas_filtered.gas[g] - - air.update_values() - gas_filtered.update_values() - - return gas_filtered - -/obj/item/clothing/mask/gas/clear - name = "gas mask" - desc = "A face-covering mask with a transparent faceplate that can be connected to an air supply." - icon_state = "gas_clear" - flags_inv = null - -/obj/item/clothing/mask/gas/half - name = "face mask" - desc = "A compact, durable gas mask that can be connected to an air supply." - icon_state = "halfgas" - siemens_coefficient = 0.7 - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) - -//Turn it into a hailer mask -/obj/item/clothing/mask/gas/half/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/device/hailer)) - playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) - user.drop_item(src) - var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc) - N.fingerprints = src.fingerprints - N.fingerprintshidden = src.fingerprintshidden - N.fingerprintslast = src.fingerprintslast - N.suit_fibers = src.suit_fibers - N.hailer = I - I.loc = N - if(!isturf(N.loc)) - user.put_in_hands(N) - qdel(src) - ..() - -//Plague Dr suit can be found in clothing/suits/bio.dm -/obj/item/clothing/mask/gas/plaguedoctor - name = "plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") - armor = list(melee = 0, bullet = 0, laser = 2,energy = 2, bomb = 0, bio = 90, rad = 0) - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/gas/plaguedoctor/gold - name = "gold plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply. This one is gold." - icon_state = "plaguedoctor2" - -/obj/item/clothing/mask/gas/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - siemens_coefficient = 0.7 - body_parts_covered = FACE|EYES - -// Vox mask, has special code for eating -/obj/item/clothing/mask/gas/swat/vox - name = "\improper alien mask" - desc = "Clearly not designed for a human face." - flags = PHORONGUARD - item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - species_restricted = list(SPECIES_VOX) - filtered_gases = list("oxygen", "nitrous_oxide") - var/mask_open = FALSE // Controls if the Vox can eat through this mask - action_button_name = "Toggle Feeding Port" - -/obj/item/clothing/mask/gas/swat/vox/proc/feeding_port(mob/user) - if(user.canmove && !user.stat) - mask_open = !mask_open - if(mask_open) - body_parts_covered = EYES - to_chat(user, "Your mask moves to allow you to eat.") - else - body_parts_covered = FACE|EYES - to_chat(user, "Your mask moves to cover your mouth.") - return - -/obj/item/clothing/mask/gas/swat/vox/attack_self(mob/user) - feeding_port(user) - ..() - -/obj/item/clothing/mask/gas/zaddat - name = "Zaddat Veil" - desc = "A clear survival mask used by the Zaddat to filter out harmful nitrogen. Can be connected to an air supply and reconfigured to allow for safe eating." - icon_state = "zaddat_mask" - item_state = "vax_mask" - //body_parts_covered = 0 - species_restricted = list(SPECIES_ZADDAT) - flags_inv = HIDEEARS //semi-transparent - filtered_gases = list("phoron", "nitrogen", "nitrous_oxide") - -/obj/item/clothing/mask/gas/syndicate - name = "tactical mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - siemens_coefficient = 0.7 - -/obj/item/clothing/mask/gas/explorer - name = "explorer gas mask" - desc = "A military-grade gas mask that can be connected to an air supply." - icon_state = "explorer" - item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") - armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 0, bio = 50, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/mask/gas/clown_hat - name = "clown wig and mask" - desc = "A true prankster's facial attire. A clown is incomplete without their wig and mask." - icon_state = "clown" - item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") - -/obj/item/clothing/mask/gas/sexyclown - name = "sexy-clown wig and mask" - desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." - icon_state = "sexyclown" - item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") - -/obj/item/clothing/mask/gas/mime - name = "mime mask" - desc = "The traditional mime's mask. It has an eerie facial posture." - icon_state = "mime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/monkeymask - name = "monkey mask" - desc = "A mask used when acting as a monkey." - icon_state = "monkeymask" - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/gas/sexymime - name = "sexy mime mask" - desc = "A traditional female mime's mask." - icon_state = "sexymime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/guy - name = "guy fawkes mask" - desc = "A mask stylised to depict Guy Fawkes." - icon_state = "guyfawkes" - flags_inv = HIDEEARS|HIDEFACE - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - -/obj/item/clothing/mask/gas/commando - name = "commando mask" - icon_state = "fullgas" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - siemens_coefficient = 0.2 - -/obj/item/clothing/mask/gas/cyborg - name = "cyborg visor" - desc = "Beep boop" - icon_state = "death" - -/obj/item/clothing/mask/gas/owl_mask - name = "owl mask" - desc = "Twoooo!" - icon_state = "owl" - body_parts_covered = HEAD|FACE|EYES +/obj/item/clothing/mask/gas + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply. Filters harmful gases from the air." + icon_state = "gas_alt" + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | ALLOW_SURVIVALFOOD + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + body_parts_covered = FACE|EYES + w_class = ITEMSIZE_NORMAL + item_state_slots = list(slot_r_hand_str = "gas_alt", slot_l_hand_str = "gas_alt") + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + siemens_coefficient = 0.9 + var/gas_filter_strength = 1 //For gas mask filters + var/list/filtered_gases = list("phoron", "nitrous_oxide") + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) + var/datum/gas_mixture/gas_filtered = new + + for(var/g in filtered_gases) + if(air.gas[g]) + gas_filtered.gas[g] = air.gas[g] * gas_filter_strength + air.gas[g] -= gas_filtered.gas[g] + + air.update_values() + gas_filtered.update_values() + + return gas_filtered + +/obj/item/clothing/mask/gas/clear + name = "gas mask" + desc = "A face-covering mask with a transparent faceplate that can be connected to an air supply." + icon_state = "gas_clear" + flags_inv = null + +/obj/item/clothing/mask/gas/half + name = "face mask" + desc = "A compact, durable gas mask that can be connected to an air supply." + icon_state = "halfgas" + siemens_coefficient = 0.7 + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) + +//Turn it into a hailer mask +/obj/item/clothing/mask/gas/half/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/hailer)) + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + user.drop_item(src) + var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc) + N.fingerprints = src.fingerprints + N.fingerprintshidden = src.fingerprintshidden + N.fingerprintslast = src.fingerprintslast + N.suit_fibers = src.suit_fibers + N.hailer = I + I.loc = N + if(!isturf(N.loc)) + user.put_in_hands(N) + qdel(src) + ..() + +//Plague Dr suit can be found in clothing/suits/bio.dm +/obj/item/clothing/mask/gas/plaguedoctor + name = "plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") + armor = list(melee = 0, bullet = 0, laser = 2,energy = 2, bomb = 0, bio = 90, rad = 0) + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/gas/plaguedoctor/gold + name = "gold plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out phoron but it can also be connected to an air supply. This one is gold." + icon_state = "plaguedoctor2" + +/obj/item/clothing/mask/gas/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + siemens_coefficient = 0.7 + body_parts_covered = FACE|EYES + +// Vox mask, has special code for eating +/obj/item/clothing/mask/gas/swat/vox + name = "\improper alien mask" + desc = "Clearly not designed for a human face." + flags = PHORONGUARD + item_flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + species_restricted = list(SPECIES_VOX) + filtered_gases = list("oxygen", "nitrous_oxide") + var/mask_open = FALSE // Controls if the Vox can eat through this mask + action_button_name = "Toggle Feeding Port" + +/obj/item/clothing/mask/gas/swat/vox/proc/feeding_port(mob/user) + if(user.canmove && !user.stat) + mask_open = !mask_open + if(mask_open) + body_parts_covered = EYES + to_chat(user, "Your mask moves to allow you to eat.") + else + body_parts_covered = FACE|EYES + to_chat(user, "Your mask moves to cover your mouth.") + return + +/obj/item/clothing/mask/gas/swat/vox/attack_self(mob/user) + feeding_port(user) + ..() + +/obj/item/clothing/mask/gas/zaddat + name = "Zaddat Veil" + desc = "A clear survival mask used by the Zaddat to filter out harmful nitrogen. Can be connected to an air supply and reconfigured to allow for safe eating." + icon_state = "zaddat_mask" + item_state = "vax_mask" + //body_parts_covered = 0 + species_restricted = list(SPECIES_ZADDAT) + flags_inv = HIDEEARS //semi-transparent + filtered_gases = list("phoron", "nitrogen", "nitrous_oxide") + +/obj/item/clothing/mask/gas/syndicate + name = "tactical mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + siemens_coefficient = 0.7 + +/obj/item/clothing/mask/gas/explorer + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + icon_state = "explorer" + item_state_slots = list(slot_r_hand_str = "gas", slot_l_hand_str = "gas") + armor = list(melee = 10, bullet = 5, laser = 5,energy = 5, bomb = 0, bio = 50, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/mask/gas/clown_hat + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without their wig and mask." + icon_state = "clown" + item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") + +/obj/item/clothing/mask/gas/sexyclown + name = "sexy-clown wig and mask" + desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." + icon_state = "sexyclown" + item_state_slots = list(slot_r_hand_str = "clown_hat", slot_l_hand_str = "clown_hat") + +/obj/item/clothing/mask/gas/mime + name = "mime mask" + desc = "The traditional mime's mask. It has an eerie facial posture." + icon_state = "mime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/monkeymask + name = "monkey mask" + desc = "A mask used when acting as a monkey." + icon_state = "monkeymask" + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/gas/sexymime + name = "sexy mime mask" + desc = "A traditional female mime's mask." + icon_state = "sexymime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/guy + name = "guy fawkes mask" + desc = "A mask stylised to depict Guy Fawkes." + icon_state = "guyfawkes" + flags_inv = HIDEEARS|HIDEFACE + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + +/obj/item/clothing/mask/gas/commando + name = "commando mask" + icon_state = "fullgas" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + siemens_coefficient = 0.2 + +/obj/item/clothing/mask/gas/cyborg + name = "cyborg visor" + desc = "Beep boop" + icon_state = "death" + +/obj/item/clothing/mask/gas/owl_mask + name = "owl mask" + desc = "Twoooo!" + icon_state = "owl" + body_parts_covered = HEAD|FACE|EYES diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm index a3a37772b3f..8737aa71a13 100644 --- a/code/modules/clothing/masks/hailer.dm +++ b/code/modules/clothing/masks/hailer.dm @@ -102,7 +102,7 @@ return /obj/item/clothing/mask/gas/sechailer/attackby(obj/item/I, mob/user) - if(I.is_screwdriver()) + if(I.has_tool_quality(TOOL_SCREWDRIVER)) switch(aggressiveness) if(1) to_chat(user, "You set the aggressiveness restrictor to the second position.") @@ -122,11 +122,11 @@ phrase = 1 if(5) to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") - if(I.is_wirecutter()) + if(I.has_tool_quality(TOOL_WIRECUTTER)) if(aggressiveness != 5) to_chat(user, "You broke it!") aggressiveness = 5 - if(I.is_crowbar()) + if(I.has_tool_quality(TOOL_CROWBAR)) if(!hailer) to_chat(user, "This mask has an integrated hailer, you can't remove it!") else @@ -155,11 +155,11 @@ if(cooldown < world.time - 35) // A cooldown, to stop people being jerks if(!safety) message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." - usr.visible_message("[usr]'s Compli-o-Nator: [message]") + usr.visible_message("[usr]'s Compli-o-Nator: [span_red("[message]")]") playsound(src, 'sound/voice/binsult.ogg', 50, 0, 4) //Future sound channel = something like SFX cooldown = world.time return - usr.visible_message("[usr]'s Compli-o-Nator: [message]") + usr.visible_message("[usr]'s Compli-o-Nator: [span_red("[message]")]") playsound(src, "sound/voice/complionator/[key].ogg", 50, 0, 4) //future sound channel = something like SFX - cooldown = world.time \ No newline at end of file + cooldown = world.time diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index f40deb8f6b7..9d7d9a693b1 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -1,408 +1,422 @@ -/obj/item/clothing/mask/muzzle - name = "muzzle" - desc = "To stop that awful noise." - icon_state = "muzzle" - body_parts_covered = FACE - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - voicechange = 1 - -/obj/item/clothing/mask/muzzle/tape - name = "length of tape" - desc = "It's a robust DIY muzzle!" - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "tape_cross" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - w_class = ITEMSIZE_TINY - -/obj/item/clothing/mask/muzzle/New() - ..() - say_messages = list("Mmfph!", "Mmmf mrrfff!", "Mmmf mnnf!") - say_verbs = list("mumbles", "says") - -// Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/living/user as mob) - if(user.wear_mask == src && !user.IsAdvancedToolUser()) - return 0 - ..() - -/obj/item/clothing/mask/surgical - name = "sterile mask" - desc = "A sterile mask designed to help prevent the spread of diseases." - icon_state = "sterile" - item_state_slots = list(slot_r_hand_str = "sterile", slot_l_hand_str = "sterile") - w_class = ITEMSIZE_SMALL - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.01 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) - var/hanging = 0 - -/obj/item/clothing/mask/surgical/proc/adjust_mask(mob_user) - if(usr.canmove && !usr.stat) - src.hanging = !src.hanging - if (src.hanging) - gas_transfer_coefficient = 1 - body_parts_covered = body_parts_covered & ~FACE - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - icon_state = "steriledown" - to_chat(usr, "You pull the mask below your chin.") - else - gas_transfer_coefficient = initial(gas_transfer_coefficient) - body_parts_covered = initial(body_parts_covered) - icon_state = initial(icon_state) - armor = initial(armor) - to_chat(usr, "You pull the mask up to cover your face.") - update_clothing_icon() - -/obj/item/clothing/mask/surgical/verb/toggle() - set category = "Object" - set name = "Adjust mask" - set src in usr - - adjust_mask(usr) - -/obj/item/clothing/mask/surgical/white - icon_state = "sterilew" - item_state_slots = list(slot_r_hand_str = "sterilew", slot_l_hand_str = "sterilew") - -/obj/item/clothing/mask/surgical/dust - name = "dust mask" - desc = "A dust mask designed to protect the wearer against construction and/or custodial particulate." - icon_state = "dust" - item_state_slots = list(slot_r_hand_str = "dust", slot_l_hand_str = "dust") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 30, rad = 0) - -/obj/item/clothing/mask/surgical/cloth - name = "cloth mask" - desc = "A cloth mask designed to protect the wearer against allergens, illnesses, and social interaction." - icon_state = "cloth" - item_state_slots = list(slot_r_hand_str = "cloth", slot_l_hand_str = "cloth") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 20, rad = 0) - -/obj/item/clothing/mask/fakemoustache - name = "fake moustache" - desc = "Warning: moustache is fake." - icon_state = "fake-moustache" - flags_inv = HIDEFACE - body_parts_covered = 0 - -/obj/item/clothing/mask/snorkel - name = "Snorkel" - desc = "For the Swimming Savant." - icon_state = "snorkel" - flags_inv = HIDEFACE - body_parts_covered = 0 - -//scarves (fit in in mask slot) -//None of these actually have on-mob sprites... -/obj/item/clothing/mask/bluescarf - name = "blue neck scarf" - desc = "A blue neck scarf." - icon_state = "blueneckscarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/redscarf - name = "red scarf" - desc = "A red and white checkered neck scarf." - icon_state = "redwhite_scarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/greenscarf - name = "green scarf" - desc = "A green neck scarf." - icon_state = "green_scarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/ninjascarf - name = "ninja scarf" - desc = "A stealthy, dark scarf." - icon_state = "ninja_scarf" - body_parts_covered = FACE - item_flags = FLEXIBLEMATERIAL - w_class = ITEMSIZE_SMALL - gas_transfer_coefficient = 0.90 - siemens_coefficient = 0 - -/obj/item/clothing/mask/pig - name = "pig mask" - desc = "A rubber pig mask." - icon_state = "pig" - flags_inv = HIDEFACE|BLOCKHAIR - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/shark - name = "shark mask" - desc = "A rubber shark mask." - icon_state = "shark" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/dolphin - name = "dolphin mask" - desc = "A rubber dolphin mask." - icon_state = "dolphin" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/goblin - name = "goblin mask" - desc = "A rubber goblin mask." - icon_state = "goblin" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/demon - name = "demon mask" - desc = "A rubber demon mask." - icon_state = "demon" - flags_inv = HIDEFACE - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/mask/horsehead - name = "horse head mask" - desc = "A mask made of soft vinyl and latex, representing the head of a horse." - icon_state = "horsehead" - flags_inv = HIDEFACE|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - w_class = ITEMSIZE_SMALL - siemens_coefficient = 0.9 - -/obj/item/clothing/mask/nock_scarab - name = "nock mask (blue, scarab)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_scarab" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/nock_demon - name = "nock mask (purple, demon)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_demon" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/nock_life - name = "nock mask (green, life)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_life" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/nock_ornate - name = "nock mask (red, ornate)" - desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." - icon_state = "nock_ornate" - w_class = ITEMSIZE_SMALL - body_parts_covered = HEAD|FACE - -/obj/item/clothing/mask/horsehead/New() - ..() - // The horse mask doesn't cause voice changes by default, the wizard spell changes the flag as necessary - say_messages = list("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") - say_verbs = list("whinnies", "neighs", "says") - -/obj/item/clothing/mask/ai - name = "camera MIU" - desc = "Allows for direct mental connection to accessible camera networks." - icon_state = "s-ninja" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - flags_inv = HIDEFACE - body_parts_covered = 0 - var/mob/observer/eye/aiEye/eye - -/obj/item/clothing/mask/ai/New() - eye = new(src) - -/obj/item/clothing/mask/ai/equipped(var/mob/user, var/slot) - ..(user, slot) - if(slot == slot_wear_mask) - eye.owner = user - user.eyeobj = eye - - for(var/datum/chunk/c in eye.visibleChunks) - c.remove(eye) - eye.setLoc(user) - -/obj/item/clothing/mask/ai/dropped(var/mob/user) - ..() - if(eye.owner == user) - for(var/datum/chunk/c in eye.visibleChunks) - c.remove(eye) - - eye.owner.eyeobj = null - eye.owner = null - -/obj/item/clothing/mask/bandana - name = "black bandana" - desc = "A fine black bandana with nanotech lining. Can be worn on the head or face." - w_class = ITEMSIZE_TINY - flags_inv = HIDEFACE - slot_flags = SLOT_MASK|SLOT_HEAD - body_parts_covered = FACE - icon_state = "bandblack" - item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") - -/obj/item/clothing/mask/bandana/equipped(var/mob/user, var/slot) - switch(slot) - if(slot_wear_mask) //Mask is the default for all the settings - flags_inv = initial(flags_inv) - body_parts_covered = initial(body_parts_covered) - icon_state = initial(icon_state) - - if(slot_head) - flags_inv = 0 - body_parts_covered = HEAD - icon_state = "[initial(icon_state)]_up" - - return ..() - -/obj/item/clothing/mask/bandana/red - name = "red bandana" - desc = "A fine red bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandred" - item_state_slots = list(slot_r_hand_str = "bandred", slot_l_hand_str = "bandred") - -/obj/item/clothing/mask/bandana/blue - name = "blue bandana" - desc = "A fine blue bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandblue" - item_state_slots = list(slot_r_hand_str = "bandblue", slot_l_hand_str = "bandblue") - -/obj/item/clothing/mask/bandana/green - name = "green bandana" - desc = "A fine green bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandgreen" - item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") - -/obj/item/clothing/mask/bandana/gold - name = "gold bandana" - desc = "A fine gold bandana with nanotech lining. Can be worn on the head or face." - icon_state = "bandgold" - item_state_slots = list(slot_r_hand_str = "bandgold", slot_l_hand_str = "bandgold") - -/obj/item/clothing/mask/bandana/skull - name = "skull bandana" - desc = "A fine black bandana with nanotech lining and a skull emblem. Can be worn on the head or face." - icon_state = "bandskull" - item_state_slots = list(slot_r_hand_str = "bandskull", slot_l_hand_str = "bandskull") - -/obj/item/clothing/mask/veil - name = "black veil" - desc = "A black veil, typically worn at funerals or by goths." - w_class = ITEMSIZE_TINY - body_parts_covered = FACE - icon_state = "veil" - -/obj/item/clothing/mask/paper - name = "paper mask" - desc = "A neat, circular mask made out of paper. Perhaps you could try drawing on it with a pen!" - w_class = ITEMSIZE_SMALL - body_parts_covered = FACE - icon_state = "papermask" - action_button_name = "Redraw Design" - action_button_is_hands_free = TRUE - var/list/papermask_designs = list() - -/obj/item/clothing/mask/paper/Initialize(mapload) - . = ..() - papermask_designs = list( - "Blank" = image(icon = src.icon, icon_state = "papermask"), - "Neutral" = image(icon = src.icon, icon_state = "neutralmask"), - "Eyes" = image(icon = src.icon, icon_state = "eyemask"), - "Sleeping" = image(icon = src.icon, icon_state = "sleepingmask"), - "Heart" = image(icon = src.icon, icon_state = "heartmask"), - "Core" = image(icon = src.icon, icon_state = "coremask"), - "Plus" = image(icon = src.icon, icon_state = "plusmask"), - "Square" = image(icon = src.icon, icon_state = "squaremask"), - "Bullseye" = image(icon = src.icon, icon_state = "bullseyemask"), - "Vertical" = image(icon = src.icon, icon_state = "verticalmask"), - "Horizontal" = image(icon = src.icon, icon_state = "horizontalmask"), - "X" = image(icon = src.icon, icon_state = "xmask"), - "Bugeyes" = image(icon = src.icon, icon_state = "bugmask"), - "Double" = image(icon = src.icon, icon_state = "doublemask"), - "Mark" = image(icon = src.icon, icon_state = "markmask") - ) - -/obj/item/clothing/mask/paper/attack_self(mob/user) - . = ..() - if(!istype(user) || user.incapacitated()) - return - - var/static/list/options = list("Blank" = "papermask", "Neutral" = "neutralmask", "Eyes" = "eyemask", - "Sleeping" ="sleepingmask", "Heart" = "heartmask", "Core" = "coremask", - "Plus" = "plusmask", "Square" ="squaremask", "Bullseye" = "bullseyemask", - "Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask", - "Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask") - - var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) - - if(src && choice && !user.incapacitated() && in_range(user,src)) - icon_state = options[choice] - user.update_inv_wear_mask() - user.update_action_buttons() - to_chat(user, "Your paper mask now is now [choice].") - return 1 - -/obj/item/clothing/mask/emotions - name = "emotional mask" - desc = "Express your happiness or hide your sorrows with this modular cutout. Draw your current emotions onto it with a pen!" - w_class = ITEMSIZE_SMALL - body_parts_covered = FACE - icon_state = "joy" - action_button_name = "Redraw Design" - action_button_is_hands_free = TRUE - var/static/list/joymask_designs = list() - - -/obj/item/clothing/mask/emotions/Initialize(mapload) - . = ..() - joymask_designs = list( - "Joy" = image(icon = src.icon, icon_state = "joy"), - "Flushed" = image(icon = src.icon, icon_state = "flushed"), - "Pensive" = image(icon = src.icon, icon_state = "pensive"), - "Angry" = image(icon = src.icon, icon_state = "angry"), - ) - -/obj/item/clothing/mask/emotions/attack_self(mob/user) - . = ..() - if(!istype(user) || user.incapacitated()) - return - - var/static/list/options = list("Joy" = "joy", "Flushed" = "flushed", "Pensive" = "pensive","Angry" ="angry") - - var/choice = show_radial_menu(user, src, joymask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) - - if(src && choice && !user.incapacitated() && in_range(user,src)) - icon_state = options[choice] - user.update_inv_wear_mask() - user.update_action_buttons() - to_chat(user, "Your [src] now displays a [choice] emotion.") - return 1 - -/obj/item/clothing/mask/mouthwheat - name = "mouth wheat" - desc = "100% synthetic \"Country Girls LLC.\" brand mouth wheat. Warning: not for actual consumption." - icon_state = "mouthwheat" - w_class = ITEMSIZE_SMALL - body_parts_covered = 0 \ No newline at end of file +/obj/item/clothing/mask/muzzle + name = "muzzle" + desc = "To stop that awful noise." + icon_state = "muzzle" + body_parts_covered = FACE + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + voicechange = 1 + +/obj/item/clothing/mask/muzzle/tape + name = "length of tape" + desc = "It's a robust DIY muzzle!" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "tape_cross" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + w_class = ITEMSIZE_TINY + +/obj/item/clothing/mask/muzzle/New() + ..() + say_messages = list("Mmfph!", "Mmmf mrrfff!", "Mmmf mnnf!") + say_verbs = list("mumbles", "says") + +// Clumsy folks can't take the mask off themselves. +/obj/item/clothing/mask/muzzle/attack_hand(mob/living/user as mob) + if(user.wear_mask == src && !user.IsAdvancedToolUser()) + return 0 + ..() + +/obj/item/clothing/mask/surgical + name = "sterile mask" + desc = "A sterile mask designed to help prevent the spread of diseases." + icon_state = "sterile" + item_state_slots = list(slot_r_hand_str = "sterile", slot_l_hand_str = "sterile") + w_class = ITEMSIZE_SMALL + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.01 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) + var/hanging = 0 + +/obj/item/clothing/mask/surgical/proc/adjust_mask(mob_user) + if(usr.canmove && !usr.stat) + src.hanging = !src.hanging + if (src.hanging) + gas_transfer_coefficient = 1 + body_parts_covered = body_parts_covered & ~FACE + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + icon_state = "steriledown" + to_chat(usr, "You pull the mask below your chin.") + else + gas_transfer_coefficient = initial(gas_transfer_coefficient) + body_parts_covered = initial(body_parts_covered) + icon_state = initial(icon_state) + armor = initial(armor) + to_chat(usr, "You pull the mask up to cover your face.") + update_clothing_icon() + +/obj/item/clothing/mask/surgical/verb/toggle() + set category = "Object" + set name = "Adjust mask" + set src in usr + + adjust_mask(usr) + +/obj/item/clothing/mask/surgical/white + icon_state = "sterilew" + item_state_slots = list(slot_r_hand_str = "sterilew", slot_l_hand_str = "sterilew") + +/obj/item/clothing/mask/surgical/dust + name = "dust mask" + desc = "A dust mask designed to protect the wearer against construction and/or custodial particulate." + icon_state = "dust" + item_state_slots = list(slot_r_hand_str = "dust", slot_l_hand_str = "dust") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 30, rad = 0) + +/obj/item/clothing/mask/surgical/cloth + name = "cloth mask" + desc = "A cloth mask designed to protect the wearer against allergens, illnesses, and social interaction." + icon_state = "cloth" + item_state_slots = list(slot_r_hand_str = "cloth", slot_l_hand_str = "cloth") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 20, rad = 0) + +/obj/item/clothing/mask/fakemoustache + name = "fake moustache" + desc = "Warning: moustache is fake." + icon_state = "fake-moustache" + flags_inv = HIDEFACE + body_parts_covered = 0 + +/obj/item/clothing/mask/snorkel + name = "Snorkel" + desc = "For the Swimming Savant." + icon_state = "snorkel" + flags_inv = HIDEFACE + body_parts_covered = 0 + +//scarves (fit in in mask slot) +//None of these actually have on-mob sprites... +/obj/item/clothing/mask/bluescarf + name = "blue neck scarf" + desc = "A blue neck scarf." + icon_state = "blueneckscarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/redscarf + name = "red scarf" + desc = "A red and white checkered neck scarf." + icon_state = "redwhite_scarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/greenscarf + name = "green scarf" + desc = "A green neck scarf." + icon_state = "green_scarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/ninjascarf + name = "ninja scarf" + desc = "A stealthy, dark scarf." + icon_state = "ninja_scarf" + body_parts_covered = FACE + item_flags = FLEXIBLEMATERIAL + w_class = ITEMSIZE_SMALL + gas_transfer_coefficient = 0.90 + siemens_coefficient = 0 + +/obj/item/clothing/mask/pig + name = "pig mask" + desc = "A rubber pig mask." + icon_state = "pig" + flags_inv = HIDEFACE|BLOCKHAIR + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/shark + name = "shark mask" + desc = "A rubber shark mask." + icon_state = "shark" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/dolphin + name = "dolphin mask" + desc = "A rubber dolphin mask." + icon_state = "dolphin" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/goblin + name = "goblin mask" + desc = "A rubber goblin mask." + icon_state = "goblin" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/demon + name = "demon mask" + desc = "A rubber demon mask." + icon_state = "demon" + flags_inv = HIDEFACE + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/mask/horsehead + name = "horse head mask" + desc = "A mask made of soft vinyl and latex, representing the head of a horse." + icon_state = "horsehead" + flags_inv = HIDEFACE|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + w_class = ITEMSIZE_SMALL + siemens_coefficient = 0.9 + +/obj/item/clothing/mask/nock_scarab + name = "nock mask (blue, scarab)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_scarab" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/nock_demon + name = "nock mask (purple, demon)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_demon" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/nock_life + name = "nock mask (green, life)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_life" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/nock_ornate + name = "nock mask (red, ornate)" + desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." + icon_state = "nock_ornate" + w_class = ITEMSIZE_SMALL + body_parts_covered = HEAD|FACE + +/obj/item/clothing/mask/horsehead/New() + ..() + // The horse mask doesn't cause voice changes by default, the wizard spell changes the flag as necessary + say_messages = list("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") + say_verbs = list("whinnies", "neighs", "says") + +/obj/item/clothing/mask/ai + name = "camera MIU" + desc = "Allows for direct mental connection to accessible camera networks." + icon_state = "s-ninja" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + flags_inv = HIDEFACE + body_parts_covered = 0 + var/mob/observer/eye/aiEye/eye + +/obj/item/clothing/mask/ai/New() + eye = new(src) + +/obj/item/clothing/mask/ai/equipped(var/mob/user, var/slot) + ..(user, slot) + if(slot == slot_wear_mask) + eye.owner = user + user.eyeobj = eye + + for(var/datum/chunk/c in eye.visibleChunks) + c.remove(eye) + eye.setLoc(user) + +/obj/item/clothing/mask/ai/dropped(var/mob/user) + ..() + if(eye.owner == user) + for(var/datum/chunk/c in eye.visibleChunks) + c.remove(eye) + + eye.owner.eyeobj = null + eye.owner = null + +/obj/item/clothing/mask/bandana + name = "black bandana" + desc = "A fine black bandana with nanotech lining. Can be worn on the head or face." + w_class = ITEMSIZE_TINY + flags_inv = HIDEFACE + slot_flags = SLOT_MASK|SLOT_HEAD + body_parts_covered = FACE + icon_state = "bandblack" + item_state_slots = list(slot_r_hand_str = "bandblack", slot_l_hand_str = "bandblack") + +/obj/item/clothing/mask/bandana/equipped(var/mob/user, var/slot) + switch(slot) + if(slot_wear_mask) //Mask is the default for all the settings + flags_inv = initial(flags_inv) + body_parts_covered = initial(body_parts_covered) + icon_state = initial(icon_state) + + if(slot_head) + flags_inv = 0 + body_parts_covered = HEAD + icon_state = "[initial(icon_state)]_up" + + return ..() + +/obj/item/clothing/mask/bandana/red + name = "red bandana" + desc = "A fine red bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandred" + item_state_slots = list(slot_r_hand_str = "bandred", slot_l_hand_str = "bandred") + +/obj/item/clothing/mask/bandana/blue + name = "blue bandana" + desc = "A fine blue bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandblue" + item_state_slots = list(slot_r_hand_str = "bandblue", slot_l_hand_str = "bandblue") + +/obj/item/clothing/mask/bandana/green + name = "green bandana" + desc = "A fine green bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandgreen" + item_state_slots = list(slot_r_hand_str = "bandgreen", slot_l_hand_str = "bandgreen") + +/obj/item/clothing/mask/bandana/gold + name = "gold bandana" + desc = "A fine gold bandana with nanotech lining. Can be worn on the head or face." + icon_state = "bandgold" + item_state_slots = list(slot_r_hand_str = "bandgold", slot_l_hand_str = "bandgold") + +/obj/item/clothing/mask/bandana/skull + name = "skull bandana" + desc = "A fine black bandana with nanotech lining and a skull emblem. Can be worn on the head or face." + icon_state = "bandskull" + item_state_slots = list(slot_r_hand_str = "bandskull", slot_l_hand_str = "bandskull") + +/obj/item/clothing/mask/veil + name = "black veil" + desc = "A black veil, typically worn at funerals or by goths." + w_class = ITEMSIZE_TINY + body_parts_covered = FACE + icon_state = "veil" + +/obj/item/clothing/mask/paper + name = "paper mask" + desc = "A neat, circular mask made out of paper. Perhaps you could try drawing on it with a pen!" + w_class = ITEMSIZE_SMALL + body_parts_covered = FACE + icon_state = "papermask" + action_button_name = "Redraw Design" + action_button_is_hands_free = TRUE + var/list/papermask_designs = list() + +/obj/item/clothing/mask/paper/Initialize(mapload) + . = ..() + papermask_designs = list( + "Blank" = image(icon = src.icon, icon_state = "papermask"), + "Neutral" = image(icon = src.icon, icon_state = "neutralmask"), + "Eyes" = image(icon = src.icon, icon_state = "eyemask"), + "Sleeping" = image(icon = src.icon, icon_state = "sleepingmask"), + "Heart" = image(icon = src.icon, icon_state = "heartmask"), + "Core" = image(icon = src.icon, icon_state = "coremask"), + "Plus" = image(icon = src.icon, icon_state = "plusmask"), + "Square" = image(icon = src.icon, icon_state = "squaremask"), + "Bullseye" = image(icon = src.icon, icon_state = "bullseyemask"), + "Vertical" = image(icon = src.icon, icon_state = "verticalmask"), + "Horizontal" = image(icon = src.icon, icon_state = "horizontalmask"), + "X" = image(icon = src.icon, icon_state = "xmask"), + "Bugeyes" = image(icon = src.icon, icon_state = "bugmask"), + "Double" = image(icon = src.icon, icon_state = "doublemask"), + "Mark" = image(icon = src.icon, icon_state = "markmask"), + "Line" = image(icon = src.icon, icon_state = "linemask"), + "Minus" = image(icon = src.icon, icon_state = "minusmask"), + "Four" = image(icon = src.icon, icon_state = "fourmask"), + "Diamond" = image(icon = src.icon, icon_state = "diamondmask"), + "Cat" = image(icon = src.icon, icon_state = "catmask"), + "Big Eyes" = image(icon = src.icon, icon_state = "bigeyemask"), + "Good" = image(icon = src.icon, icon_state = "goodmask"), + "Bad" = image(icon = src.icon, icon_state = "badmask"), + "Happy" = image(icon = src.icon, icon_state = "happymask"), + "Sad" = image(icon = src.icon, icon_state = "sadmask") + ) + +/obj/item/clothing/mask/paper/attack_self(mob/user) + . = ..() + if(!istype(user) || user.incapacitated()) + return + + var/static/list/options = list("Blank" = "papermask", "Neutral" = "neutralmask", "Eyes" = "eyemask", + "Sleeping" ="sleepingmask", "Heart" = "heartmask", "Core" = "coremask", + "Plus" = "plusmask", "Square" ="squaremask", "Bullseye" = "bullseyemask", + "Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask", + "Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask", + "Line" = "linemask", "Minus" = "minusmask", "Four" = "fourmask", + "Diamond" = "diamondmask", "Cat" = "catmask", "Big Eyes" = "bigeyemask", + "Good" = "goodmask", "Bad" = "badmask", "Happy" = "happymask", "Sad" = "sadmask" + ) + + var/choice = show_radial_menu(user, src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + + if(src && choice && !user.incapacitated() && in_range(user,src)) + icon_state = options[choice] + user.update_inv_wear_mask() + user.update_action_buttons() + to_chat(user, "Your paper mask now is now [choice].") + return 1 + +/obj/item/clothing/mask/emotions + name = "emotional mask" + desc = "Express your happiness or hide your sorrows with this modular cutout. Draw your current emotions onto it with a pen!" + w_class = ITEMSIZE_SMALL + body_parts_covered = FACE + icon_state = "joy" + action_button_name = "Redraw Design" + action_button_is_hands_free = TRUE + var/static/list/joymask_designs = list() + + +/obj/item/clothing/mask/emotions/Initialize(mapload) + . = ..() + joymask_designs = list( + "Joy" = image(icon = src.icon, icon_state = "joy"), + "Flushed" = image(icon = src.icon, icon_state = "flushed"), + "Pensive" = image(icon = src.icon, icon_state = "pensive"), + "Angry" = image(icon = src.icon, icon_state = "angry"), + ) + +/obj/item/clothing/mask/emotions/attack_self(mob/user) + . = ..() + if(!istype(user) || user.incapacitated()) + return + + var/static/list/options = list("Joy" = "joy", "Flushed" = "flushed", "Pensive" = "pensive","Angry" ="angry") + + var/choice = show_radial_menu(user, src, joymask_designs, custom_check = FALSE, radius = 36, require_near = TRUE) + + if(src && choice && !user.incapacitated() && in_range(user,src)) + icon_state = options[choice] + user.update_inv_wear_mask() + user.update_action_buttons() + to_chat(user, "Your [src] now displays a [choice] emotion.") + return 1 + +/obj/item/clothing/mask/mouthwheat + name = "mouth wheat" + desc = "100% synthetic \"Country Girls LLC.\" brand mouth wheat. Warning: not for actual consumption." + icon_state = "mouthwheat" + w_class = ITEMSIZE_SMALL + body_parts_covered = 0 diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index 5035a7f3496..aa01bab9d8a 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,158 +1,158 @@ - -/obj/item/clothing/shoes/black - name = "black shoes" - icon_state = "black" - desc = "A pair of black shoes." - -/obj/item/clothing/shoes/brown - name = "brown shoes" - desc = "A pair of brown shoes." - icon_state = "brown" - -/obj/item/clothing/shoes/blue - name = "blue shoes" - icon_state = "blue" - -/obj/item/clothing/shoes/green - name = "green shoes" - icon_state = "green" - -/obj/item/clothing/shoes/yellow - name = "yellow shoes" - icon_state = "yellow" - -/obj/item/clothing/shoes/purple - name = "purple shoes" - icon_state = "purple" - -/obj/item/clothing/shoes/red - name = "red shoes" - desc = "Stylish red shoes." - icon_state = "red" - -/obj/item/clothing/shoes/white - name = "white shoes" - icon_state = "white" - permeability_coefficient = 0.01 - -/obj/item/clothing/shoes/rainbow - name = "rainbow shoes" - desc = "Very colourful shoes." - icon_state = "rain_bow" - -/obj/item/clothing/shoes/flats - name = "black flats" - desc = "Sleek black flats." - icon_state = "flatsblack" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/shoes/flats/white - name = "white flats" - desc = "Shiny white flats." - icon_state = "flatswhite" - addblends = "flatswhite_a" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/shoes/flats/white/color - name = "flats" - desc = "Sleek flats." - -/obj/item/clothing/shoes/flats/red - name = "red flats" - desc = "Ruby red flats." - icon_state = "flatsred" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/shoes/flats/purple - name = "purple flats" - desc = "Royal purple flats." - icon_state = "flatspurple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/shoes/flats/blue - name = "blue flats" - desc = "Sleek blue flats." - icon_state = "flatsblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/shoes/flats/brown - name = "brown flats" - desc = "Sleek brown flats." - icon_state = "flatsbrown" - item_state_slots = list(slot_r_hand_str = "brown", slot_l_hand_str = "brown") - -/obj/item/clothing/shoes/flats/orange - name = "orange flats" - desc = "Radiant orange flats." - icon_state = "flatsorange" - item_state_slots = list(slot_r_hand_str = "orange", slot_l_hand_str = "orange") - -/obj/item/clothing/shoes/orange - name = "orange shoes" - icon_state = "orange" - var/obj/item/weapon/handcuffs/chained = null - -/obj/item/clothing/shoes/orange/proc/attach_cuffs(var/obj/item/weapon/handcuffs/cuffs, mob/user as mob) - if (chained) return - - user.drop_item() - cuffs.loc = src - chained = cuffs - slowdown = 15 - icon_state = "orange1" - -/obj/item/clothing/shoes/orange/proc/remove_cuffs(mob/user as mob) - if (!chained) return - - user.put_in_hands(chained) - chained.add_fingerprint(user) - - slowdown = initial(slowdown) - icon_state = "orange" - chained = null - -/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) - ..() - remove_cuffs(user) - -/obj/item/clothing/shoes/orange/attackby(H as obj, mob/user as mob) - ..() - if (istype(H, /obj/item/weapon/handcuffs)) - attach_cuffs(H, user) - -/obj/item/clothing/shoes/hitops - name = "white high-tops" - desc = "A pair of shoes that extends past the ankle. Based on a centuries-old, timeless design." - icon_state = "whitehi" - -/obj/item/clothing/shoes/hitops/red - name = "red high-tops" - icon_state = "redhi" - -/obj/item/clothing/shoes/hitops/brown - name = "brown high-tops" - icon_state = "brownhi" - -/obj/item/clothing/shoes/hitops/black - name = "black high-tops" - icon_state = "blackhi" - -/obj/item/clothing/shoes/hitops/orange - name = "orange high-tops" - icon_state = "orangehi" - -/obj/item/clothing/shoes/hitops/blue - name = "blue high-tops" - icon_state = "bluehi" - -/obj/item/clothing/shoes/hitops/green - name = "green high-tops" - icon_state = "greenhi" - -/obj/item/clothing/shoes/hitops/purple - name = "purple high-tops" - icon_state = "purplehi" - -/obj/item/clothing/shoes/hitops/yellow - name = "yellow high-tops" + +/obj/item/clothing/shoes/black + name = "black shoes" + icon_state = "black" + desc = "A pair of black shoes." + +/obj/item/clothing/shoes/brown + name = "brown shoes" + desc = "A pair of brown shoes." + icon_state = "brown" + +/obj/item/clothing/shoes/blue + name = "blue shoes" + icon_state = "blue" + +/obj/item/clothing/shoes/green + name = "green shoes" + icon_state = "green" + +/obj/item/clothing/shoes/yellow + name = "yellow shoes" + icon_state = "yellow" + +/obj/item/clothing/shoes/purple + name = "purple shoes" + icon_state = "purple" + +/obj/item/clothing/shoes/red + name = "red shoes" + desc = "Stylish red shoes." + icon_state = "red" + +/obj/item/clothing/shoes/white + name = "white shoes" + icon_state = "white" + permeability_coefficient = 0.01 + +/obj/item/clothing/shoes/rainbow + name = "rainbow shoes" + desc = "Very colourful shoes." + icon_state = "rain_bow" + +/obj/item/clothing/shoes/flats + name = "black flats" + desc = "Sleek black flats." + icon_state = "flatsblack" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/shoes/flats/white + name = "white flats" + desc = "Shiny white flats." + icon_state = "flatswhite" + addblends = "flatswhite_a" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/shoes/flats/white/color + name = "flats" + desc = "Sleek flats." + +/obj/item/clothing/shoes/flats/red + name = "red flats" + desc = "Ruby red flats." + icon_state = "flatsred" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/shoes/flats/purple + name = "purple flats" + desc = "Royal purple flats." + icon_state = "flatspurple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/shoes/flats/blue + name = "blue flats" + desc = "Sleek blue flats." + icon_state = "flatsblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/shoes/flats/brown + name = "brown flats" + desc = "Sleek brown flats." + icon_state = "flatsbrown" + item_state_slots = list(slot_r_hand_str = "brown", slot_l_hand_str = "brown") + +/obj/item/clothing/shoes/flats/orange + name = "orange flats" + desc = "Radiant orange flats." + icon_state = "flatsorange" + item_state_slots = list(slot_r_hand_str = "orange", slot_l_hand_str = "orange") + +/obj/item/clothing/shoes/orange + name = "orange shoes" + icon_state = "orange" + var/obj/item/weapon/handcuffs/chained = null + +/obj/item/clothing/shoes/orange/proc/attach_cuffs(var/obj/item/weapon/handcuffs/cuffs, mob/user as mob) + if (chained) return + + user.drop_item() + cuffs.loc = src + chained = cuffs + slowdown = 15 + icon_state = "orange1" + +/obj/item/clothing/shoes/orange/proc/remove_cuffs(mob/user as mob) + if (!chained) return + + user.put_in_hands(chained) + chained.add_fingerprint(user) + + slowdown = initial(slowdown) + icon_state = "orange" + chained = null + +/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) + ..() + remove_cuffs(user) + +/obj/item/clothing/shoes/orange/attackby(H as obj, mob/user as mob) + ..() + if (istype(H, /obj/item/weapon/handcuffs)) + attach_cuffs(H, user) + +/obj/item/clothing/shoes/hitops + name = "white high-tops" + desc = "A pair of shoes that extends past the ankle. Based on a centuries-old, timeless design." + icon_state = "whitehi" + +/obj/item/clothing/shoes/hitops/red + name = "red high-tops" + icon_state = "redhi" + +/obj/item/clothing/shoes/hitops/brown + name = "brown high-tops" + icon_state = "brownhi" + +/obj/item/clothing/shoes/hitops/black + name = "black high-tops" + icon_state = "blackhi" + +/obj/item/clothing/shoes/hitops/orange + name = "orange high-tops" + icon_state = "orangehi" + +/obj/item/clothing/shoes/hitops/blue + name = "blue high-tops" + icon_state = "bluehi" + +/obj/item/clothing/shoes/hitops/green + name = "green high-tops" + icon_state = "greenhi" + +/obj/item/clothing/shoes/hitops/purple + name = "purple high-tops" + icon_state = "purplehi" + +/obj/item/clothing/shoes/hitops/yellow + name = "yellow high-tops" icon_state = "yellowhi" \ No newline at end of file diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index b775dcf98c9..dd921661156 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -1,129 +1,129 @@ -/obj/item/clothing/shoes/magboots - desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. They're large enough to be worn over other footwear." - name = "magboots" - icon_state = "magboots0" - flags = PHORONGUARD - item_state_slots = list(slot_r_hand_str = "magboots", slot_l_hand_str = "magboots") - species_restricted = null - center_of_mass = list("x" = 17,"y" = 12) - force = 3 - overshoes = 1 - shoes_under_pants = -1 //These things are huge - preserve_item = 1 - var/magpulse = 0 - var/icon_base = "magboots" - action_button_name = "Toggle Magboots" - var/obj/item/clothing/shoes/shoes = null //Undershoes - var/mob/living/carbon/human/wearer = null //For shoe procs - step_volume_mod = 1.3 - drop_sound = 'sound/items/drop/metalboots.ogg' - pickup_sound = 'sound/items/pickup/toolbox.ogg' - -/obj/item/clothing/shoes/magboots/proc/set_slowdown() - slowdown = shoes? max(SHOES_SLOWDOWN, shoes.slowdown): SHOES_SLOWDOWN //So you can't put on magboots to make you walk faster. - if (magpulse) - slowdown += 3 - -/obj/item/clothing/shoes/magboots/attack_self(mob/user) - if(magpulse) - item_flags &= ~NOSLIP - magpulse = 0 - set_slowdown() - force = 3 - if(icon_base) icon_state = "[icon_base]0" - to_chat(user, "You disable the mag-pulse traction system.") - else - item_flags |= NOSLIP - magpulse = 1 - set_slowdown() - force = 5 - if(icon_base) icon_state = "[icon_base]1" - playsound(src, 'sound/effects/magnetclamp.ogg', 20) - to_chat(user, "You enable the mag-pulse traction system.") - user.update_inv_shoes() //so our mob-overlays update - user.update_action_buttons() - -/obj/item/clothing/shoes/magboots/mob_can_equip(mob/user, slot, disable_warning = FALSE) - var/mob/living/carbon/human/H = user - - if(H.shoes) - shoes = H.shoes - if(shoes.overshoes) - if(slot && slot == slot_shoes) - to_chat(user, "You are unable to wear \the [src] as \the [H.shoes] are in the way.") - shoes = null - return 0 - H.drop_from_inventory(shoes) //Remove the old shoes so you can put on the magboots. - shoes.forceMove(src) - - if(!..()) - if(shoes) //Put the old shoes back on if the check fails. - if(H.equip_to_slot_if_possible(shoes, slot_shoes)) - src.shoes = null - return 0 - - if (shoes) - if(slot && slot == slot_shoes) - to_chat(user, "You slip \the [src] on over \the [shoes].") - set_slowdown() - wearer = H - return 1 - -/obj/item/clothing/shoes/magboots/dropped() - ..() - var/mob/living/carbon/human/H = wearer - if(shoes) - if(!H.equip_to_slot_if_possible(shoes, slot_shoes)) - shoes.forceMove(get_turf(src)) - src.shoes = null - wearer = null - -/obj/item/clothing/shoes/magboots/examine(mob/user) - . = ..() - . += "Its mag-pulse traction system appears to be [item_flags & NOSLIP ? "enabled" : "disabled"]." - -/obj/item/clothing/shoes/magboots/vox - - desc = "A pair of heavy, jagged armoured foot pieces, seemingly suitable for a velociraptor." - name = "vox magclaws" - item_state = "boots-vox" - icon_state = "boots-vox" - flags = PHORONGUARD - species_restricted = list(SPECIES_VOX) - - action_button_name = "Toggle the magclaws" - -/obj/item/clothing/shoes/magboots/vox/attack_self(mob/user) - if(src.magpulse) - item_flags &= ~NOSLIP - magpulse = 0 - canremove = TRUE - to_chat(user, "You relax your deathgrip on the flooring.") - else - //make sure these can only be used when equipped. - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if (H.shoes != src) - to_chat(user, "You will have to put on the [src] before you can do that.") - return - - item_flags |= NOSLIP - magpulse = 1 - canremove = FALSE //kinda hard to take off magclaws when you are gripping them tightly. - to_chat(user, "You dig your claws deeply into the flooring, bracing yourself.") - user.update_action_buttons() - -//In case they somehow come off while enabled. -/obj/item/clothing/shoes/magboots/vox/dropped(mob/user as mob) - ..() - if(src.magpulse) - user.visible_message("The [src] go limp as they are removed from [usr]'s feet.", "The [src] go limp as they are removed from your feet.") - item_flags &= ~NOSLIP - magpulse = 0 - canremove = TRUE - -/obj/item/clothing/shoes/magboots/vox/examine(mob/user) - . = ..() - if(magpulse) +/obj/item/clothing/shoes/magboots + desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. They're large enough to be worn over other footwear." + name = "magboots" + icon_state = "magboots0" + flags = PHORONGUARD + item_state_slots = list(slot_r_hand_str = "magboots", slot_l_hand_str = "magboots") + species_restricted = null + center_of_mass = list("x" = 17,"y" = 12) + force = 3 + overshoes = 1 + shoes_under_pants = -1 //These things are huge + preserve_item = 1 + var/magpulse = 0 + var/icon_base = "magboots" + action_button_name = "Toggle Magboots" + var/obj/item/clothing/shoes/shoes = null //Undershoes + var/mob/living/carbon/human/wearer = null //For shoe procs + step_volume_mod = 1.3 + drop_sound = 'sound/items/drop/metalboots.ogg' + pickup_sound = 'sound/items/pickup/toolbox.ogg' + +/obj/item/clothing/shoes/magboots/proc/set_slowdown() + slowdown = shoes? max(SHOES_SLOWDOWN, shoes.slowdown): SHOES_SLOWDOWN //So you can't put on magboots to make you walk faster. + if (magpulse) + slowdown += 3 + +/obj/item/clothing/shoes/magboots/attack_self(mob/user) + if(magpulse) + item_flags &= ~NOSLIP + magpulse = 0 + set_slowdown() + force = 3 + if(icon_base) icon_state = "[icon_base]0" + to_chat(user, "You disable the mag-pulse traction system.") + else + item_flags |= NOSLIP + magpulse = 1 + set_slowdown() + force = 5 + if(icon_base) icon_state = "[icon_base]1" + playsound(src, 'sound/effects/magnetclamp.ogg', 20) + to_chat(user, "You enable the mag-pulse traction system.") + user.update_inv_shoes() //so our mob-overlays update + user.update_action_buttons() + +/obj/item/clothing/shoes/magboots/mob_can_equip(mob/user, slot, disable_warning = FALSE) + var/mob/living/carbon/human/H = user + + if(H.shoes) + shoes = H.shoes + if(shoes.overshoes) + if(slot && slot == slot_shoes) + to_chat(user, "You are unable to wear \the [src] as \the [H.shoes] are in the way.") + shoes = null + return 0 + H.drop_from_inventory(shoes) //Remove the old shoes so you can put on the magboots. + shoes.forceMove(src) + + if(!..()) + if(shoes) //Put the old shoes back on if the check fails. + if(H.equip_to_slot_if_possible(shoes, slot_shoes)) + src.shoes = null + return 0 + + if (shoes) + if(slot && slot == slot_shoes) + to_chat(user, "You slip \the [src] on over \the [shoes].") + set_slowdown() + wearer = H + return 1 + +/obj/item/clothing/shoes/magboots/dropped() + ..() + var/mob/living/carbon/human/H = wearer + if(shoes) + if(!H.equip_to_slot_if_possible(shoes, slot_shoes)) + shoes.forceMove(get_turf(src)) + src.shoes = null + wearer = null + +/obj/item/clothing/shoes/magboots/examine(mob/user) + . = ..() + . += "Its mag-pulse traction system appears to be [item_flags & NOSLIP ? "enabled" : "disabled"]." + +/obj/item/clothing/shoes/magboots/vox + + desc = "A pair of heavy, jagged armoured foot pieces, seemingly suitable for a velociraptor." + name = "vox magclaws" + item_state = "boots-vox" + icon_state = "boots-vox" + flags = PHORONGUARD + species_restricted = list(SPECIES_VOX) + + action_button_name = "Toggle the magclaws" + +/obj/item/clothing/shoes/magboots/vox/attack_self(mob/user) + if(src.magpulse) + item_flags &= ~NOSLIP + magpulse = 0 + canremove = TRUE + to_chat(user, "You relax your deathgrip on the flooring.") + else + //make sure these can only be used when equipped. + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if (H.shoes != src) + to_chat(user, "You will have to put on the [src] before you can do that.") + return + + item_flags |= NOSLIP + magpulse = 1 + canremove = FALSE //kinda hard to take off magclaws when you are gripping them tightly. + to_chat(user, "You dig your claws deeply into the flooring, bracing yourself.") + user.update_action_buttons() + +//In case they somehow come off while enabled. +/obj/item/clothing/shoes/magboots/vox/dropped(mob/user as mob) + ..() + if(src.magpulse) + user.visible_message("The [src] go limp as they are removed from [usr]'s feet.", "The [src] go limp as they are removed from your feet.") + item_flags &= ~NOSLIP + magpulse = 0 + canremove = TRUE + +/obj/item/clothing/shoes/magboots/vox/examine(mob/user) + . = ..() + if(magpulse) . += "It would be hard to take these off without relaxing your grip first." // Theoretically this message should only be seen by the wearer when the claws are equipped. \ No newline at end of file diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 67708437ab3..272fd99b5dc 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -1,256 +1,256 @@ -/obj/item/clothing/shoes/syndigaloshes - desc = "A pair of brown shoes. They seem to have extra grip." - name = "brown shoes" - icon_state = "brown" - permeability_coefficient = 0.05 - item_flags = NOSLIP - origin_tech = list(TECH_ILLEGAL = 3) - var/list/clothing_choices = list() - siemens_coefficient = 0.8 - species_restricted = null - step_volume_mod = 0.5 - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/shoes/mime - name = "mime shoes" - icon_state = "white" - step_volume_mod = 0 //It's a mime - -/obj/item/clothing/shoes/galoshes - desc = "Rubber boots" - name = "galoshes" - icon_state = "galoshes" - permeability_coefficient = 0.05 - siemens_coefficient = 0 //They're thick rubber boots! Of course they won't conduct electricity! - item_flags = NOSLIP - slowdown = SHOES_SLOWDOWN+0.5 - species_restricted = null - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/shoes/dress - name = "dress shoes" - desc = "Sharp looking low quarters, perfect for a formal uniform." - icon_state = "laceups" - -/obj/item/clothing/shoes/dress/white - name = "white dress shoes" - desc = "Brilliantly white low quarters, not a spot on them." - icon_state = "whitedress" - -/obj/item/clothing/shoes/sandal - desc = "A pair of rather plain, wooden sandals." - name = "sandals" - icon_state = "wizard" - species_restricted = null - body_parts_covered = 0 - - wizard_garb = 1 - -/obj/item/clothing/shoes/flipflop - name = "flip flops" - desc = "A pair of foam flip flops. For those not afraid to show a little ankle." - icon_state = "thongsandal" - addblends = "thongsandal_a" - -/obj/item/clothing/shoes/cookflop - name = "grilling sandals" - desc = "All this talk of antags, greytiding, and griefing... I just wanna grill for god's sake!" - icon_state = "cookflops" - species_restricted = null - body_parts_covered = 0 - -/obj/item/clothing/shoes/tourist_1 - name = "tourist sandals" - desc = "Black sandals usually worn by tourists. Need I say more?" - icon_state = "tourist_1" - species_restricted = null - body_parts_covered = 0 - -/obj/item/clothing/shoes/tourist_2 - name = "tourist sandals" - desc = "Green sandals usually worn by tourists. Need I say more?" - icon_state = "tourist_2" - species_restricted = null - body_parts_covered = 0 - -/obj/item/clothing/shoes/sandal/clogs - name = "plastic clogs" - desc = "A pair of plastic clog shoes." - icon_state = "clogs" - -/obj/item/clothing/shoes/sandal/marisa - desc = "A pair of magic, black shoes." - name = "magic shoes" - icon_state = "black" - body_parts_covered = FEET - -/obj/item/clothing/shoes/clown_shoes - desc = "The prankster's standard-issue clowning shoes. Damn they're huge!" - name = "clown shoes" - icon_state = "clown" - slowdown = SHOES_SLOWDOWN+0.5 - force = 0 - var/footstep = 1 //used for squeeks whilst walking - species_restricted = null - -/obj/item/clothing/shoes/clown_shoes/handle_movement(var/turf/walking, var/running) - if(running) - if(footstep >= 2) - footstep = 0 - playsound(src, "clownstep", 50, 1) // this will get annoying very fast. - else - footstep++ - else - playsound(src, "clownstep", 20, 1) - -/obj/item/clothing/shoes/cult - name = "boots" - desc = "A pair of boots worn by the followers of Nar-Sie." - icon_state = "cult" - item_state_slots = list(slot_r_hand_str = "cult", slot_l_hand_str = "cult") - force = 2 - siemens_coefficient = 0.7 - - cold_protection = FEET - min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = FEET - max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE - species_restricted = null - -/obj/item/clothing/shoes/cult/cultify() - return - -/obj/item/clothing/shoes/cyborg - name = "cyborg boots" - desc = "Shoes for a cyborg costume" - icon_state = "boots" - -/obj/item/clothing/shoes/slippers - name = "bunny slippers" - desc = "Fluffy!" - icon_state = "slippers" - force = 0 - species_restricted = null - w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/clothing.ogg' - -/obj/item/clothing/shoes/slippers/worn - name = "worn bunny slippers" - desc = "Fluffy..." - icon_state = "slippers_worn" - item_state_slots = list(slot_r_hand_str = "slippers", slot_l_hand_str = "slippers") - -/obj/item/clothing/shoes/laceup - name = "black oxford shoes" - icon_state = "oxford_black" - -/obj/item/clothing/shoes/laceup/grey - name = "grey oxford shoes" - icon_state = "oxford_grey" - -/obj/item/clothing/shoes/laceup/brown - name = "brown oxford shoes" - icon_state = "oxford_brown" - -/obj/item/clothing/shoes/swimmingfins - desc = "Help you swim good." - name = "swimming fins" - icon_state = "flippers" - item_state_slots = list(slot_r_hand_str = "galoshes", slot_l_hand_str = "galoshes") - item_flags = NOSLIP - slowdown = SHOES_SLOWDOWN+0.5 - species_restricted = null - -/obj/item/clothing/shoes/athletic - name = "athletic shoes" - desc = "A pair of sleek athletic shoes. Made by and for the sporty types." - icon_state = "sportshoe" - addblends = "sportshoe_a" - item_state_slots = list(slot_r_hand_str = "sportheld", slot_l_hand_str = "sportheld") - -/obj/item/clothing/shoes/skater - name = "skater shoes" - desc = "A pair of wide shoes with thick soles. Designed for skating." - icon_state = "skatershoe" - addblends = "skatershoe_a" - item_state_slots = list(slot_r_hand_str = "skaterheld", slot_l_hand_str = "skaterheld") - -/obj/item/clothing/shoes/heels - name = "high heels" - desc = "A pair of high-heeled shoes. Fancy!" - icon_state = "heels" - addblends = "heels_a" - -/obj/item/clothing/shoes/footwraps - name = "cloth footwraps" - desc = "A roll of treated canvas used for wrapping claws or paws" - icon_state = "clothwrap" - item_state = "clothwrap" - force = 0 - w_class = ITEMSIZE_SMALL - species_restricted = null - drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/clothing.ogg' - -/obj/item/clothing/shoes/boots/ranger - var/bootcolor = "white" - name = "ranger boots" - desc = "The Rangers special lightweight hybrid magboots-jetboots perfect for EVA. If only these functions were so easy to copy in reality.\ - These ones are just a well-made pair of boots in appropriate colours." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "ranger_boots" - -/obj/item/clothing/shoes/boots/ranger/Initialize() - . = ..() - if(icon_state == "ranger_boots") - name = "[bootcolor] ranger boots" - icon_state = "[bootcolor]_ranger_boots" - -/obj/item/clothing/shoes/boots/ranger/black - bootcolor = "black" - -/obj/item/clothing/shoes/boots/ranger/pink - bootcolor = "pink" - -/obj/item/clothing/shoes/boots/ranger/green - bootcolor = "green" - -/obj/item/clothing/shoes/boots/ranger/cyan - bootcolor = "cyan" - -/obj/item/clothing/shoes/boots/ranger/orange - bootcolor = "orange" - -/obj/item/clothing/shoes/boots/ranger/yellow - bootcolor = "yellow" - -/* - * 80s - */ - -/obj/item/clothing/shoes/sneakerspurple - name = "purple sneakers" - desc = "A stylish, expensive pair of purple sneakers." - icon_state = "sneakerspurple" - item_state = "sneakerspurple" - -/obj/item/clothing/shoes/sneakersblue - name = "blue sneakers" - desc = "A stylish, expensive pair of blue sneakers." - icon_state = "sneakersblue" - item_state = "sneakersblue" - -/obj/item/clothing/shoes/sneakersred - name = "red sneakers" - desc = "A stylish, expensive pair of red sneakers." - icon_state = "sneakersred" - item_state = "sneakersred" - -/obj/item/clothing/shoes/ballet - name = "pointe shoes" - desc = "These shoes feature long lace straps and flattened off toes. Great for the most elegant of dances!" - icon_state = "ballet" - item_state = "ballet" +/obj/item/clothing/shoes/syndigaloshes + desc = "A pair of brown shoes. They seem to have extra grip." + name = "brown shoes" + icon_state = "brown" + permeability_coefficient = 0.05 + item_flags = NOSLIP + origin_tech = list(TECH_ILLEGAL = 3) + var/list/clothing_choices = list() + siemens_coefficient = 0.8 + species_restricted = null + step_volume_mod = 0.5 + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/shoes/mime + name = "mime shoes" + icon_state = "white" + step_volume_mod = 0 //It's a mime + +/obj/item/clothing/shoes/galoshes + desc = "Rubber boots" + name = "galoshes" + icon_state = "galoshes" + permeability_coefficient = 0.05 + siemens_coefficient = 0 //They're thick rubber boots! Of course they won't conduct electricity! + item_flags = NOSLIP + slowdown = SHOES_SLOWDOWN+0.5 + species_restricted = null + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/shoes/dress + name = "dress shoes" + desc = "Sharp looking low quarters, perfect for a formal uniform." + icon_state = "laceups" + +/obj/item/clothing/shoes/dress/white + name = "white dress shoes" + desc = "Brilliantly white low quarters, not a spot on them." + icon_state = "whitedress" + +/obj/item/clothing/shoes/sandal + desc = "A pair of rather plain, wooden sandals." + name = "sandals" + icon_state = "wizard" + species_restricted = null + body_parts_covered = 0 + + wizard_garb = 1 + +/obj/item/clothing/shoes/flipflop + name = "flip flops" + desc = "A pair of foam flip flops. For those not afraid to show a little ankle." + icon_state = "thongsandal" + addblends = "thongsandal_a" + +/obj/item/clothing/shoes/cookflop + name = "grilling sandals" + desc = "All this talk of antags, greytiding, and griefing... I just wanna grill for god's sake!" + icon_state = "cookflops" + species_restricted = null + body_parts_covered = 0 + +/obj/item/clothing/shoes/tourist_1 + name = "tourist sandals" + desc = "Black sandals usually worn by tourists. Need I say more?" + icon_state = "tourist_1" + species_restricted = null + body_parts_covered = 0 + +/obj/item/clothing/shoes/tourist_2 + name = "tourist sandals" + desc = "Green sandals usually worn by tourists. Need I say more?" + icon_state = "tourist_2" + species_restricted = null + body_parts_covered = 0 + +/obj/item/clothing/shoes/sandal/clogs + name = "plastic clogs" + desc = "A pair of plastic clog shoes." + icon_state = "clogs" + +/obj/item/clothing/shoes/sandal/marisa + desc = "A pair of magic, black shoes." + name = "magic shoes" + icon_state = "black" + body_parts_covered = FEET + +/obj/item/clothing/shoes/clown_shoes + desc = "The prankster's standard-issue clowning shoes. Damn they're huge!" + name = "clown shoes" + icon_state = "clown" + slowdown = SHOES_SLOWDOWN+0.5 + force = 0 + var/footstep = 1 //used for squeeks whilst walking + species_restricted = null + +/obj/item/clothing/shoes/clown_shoes/handle_movement(var/turf/walking, var/running) + if(running) + if(footstep >= 2) + footstep = 0 + playsound(src, "clownstep", 50, 1) // this will get annoying very fast. + else + footstep++ + else + playsound(src, "clownstep", 20, 1) + +/obj/item/clothing/shoes/cult + name = "boots" + desc = "A pair of boots worn by the followers of Nar-Sie." + icon_state = "cult" + item_state_slots = list(slot_r_hand_str = "cult", slot_l_hand_str = "cult") + force = 2 + siemens_coefficient = 0.7 + + cold_protection = FEET + min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = FEET + max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE + species_restricted = null + +/obj/item/clothing/shoes/cult/cultify() + return + +/obj/item/clothing/shoes/cyborg + name = "cyborg boots" + desc = "Shoes for a cyborg costume" + icon_state = "boots" + +/obj/item/clothing/shoes/slippers + name = "bunny slippers" + desc = "Fluffy!" + icon_state = "slippers" + force = 0 + species_restricted = null + w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' + +/obj/item/clothing/shoes/slippers/worn + name = "worn bunny slippers" + desc = "Fluffy..." + icon_state = "slippers_worn" + item_state_slots = list(slot_r_hand_str = "slippers", slot_l_hand_str = "slippers") + +/obj/item/clothing/shoes/laceup + name = "black oxford shoes" + icon_state = "oxford_black" + +/obj/item/clothing/shoes/laceup/grey + name = "grey oxford shoes" + icon_state = "oxford_grey" + +/obj/item/clothing/shoes/laceup/brown + name = "brown oxford shoes" + icon_state = "oxford_brown" + +/obj/item/clothing/shoes/swimmingfins + desc = "Help you swim good." + name = "swimming fins" + icon_state = "flippers" + item_state_slots = list(slot_r_hand_str = "galoshes", slot_l_hand_str = "galoshes") + item_flags = NOSLIP + slowdown = SHOES_SLOWDOWN+0.5 + species_restricted = null + +/obj/item/clothing/shoes/athletic + name = "athletic shoes" + desc = "A pair of sleek athletic shoes. Made by and for the sporty types." + icon_state = "sportshoe" + addblends = "sportshoe_a" + item_state_slots = list(slot_r_hand_str = "sportheld", slot_l_hand_str = "sportheld") + +/obj/item/clothing/shoes/skater + name = "skater shoes" + desc = "A pair of wide shoes with thick soles. Designed for skating." + icon_state = "skatershoe" + addblends = "skatershoe_a" + item_state_slots = list(slot_r_hand_str = "skaterheld", slot_l_hand_str = "skaterheld") + +/obj/item/clothing/shoes/heels + name = "high heels" + desc = "A pair of high-heeled shoes. Fancy!" + icon_state = "heels" + addblends = "heels_a" + +/obj/item/clothing/shoes/footwraps + name = "cloth footwraps" + desc = "A roll of treated canvas used for wrapping claws or paws" + icon_state = "clothwrap" + item_state = "clothwrap" + force = 0 + w_class = ITEMSIZE_SMALL + species_restricted = null + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' + +/obj/item/clothing/shoes/boots/ranger + var/bootcolor = "white" + name = "ranger boots" + desc = "The Rangers special lightweight hybrid magboots-jetboots perfect for EVA. If only these functions were so easy to copy in reality.\ + These ones are just a well-made pair of boots in appropriate colours." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "ranger_boots" + +/obj/item/clothing/shoes/boots/ranger/Initialize() + . = ..() + if(icon_state == "ranger_boots") + name = "[bootcolor] ranger boots" + icon_state = "[bootcolor]_ranger_boots" + +/obj/item/clothing/shoes/boots/ranger/black + bootcolor = "black" + +/obj/item/clothing/shoes/boots/ranger/pink + bootcolor = "pink" + +/obj/item/clothing/shoes/boots/ranger/green + bootcolor = "green" + +/obj/item/clothing/shoes/boots/ranger/cyan + bootcolor = "cyan" + +/obj/item/clothing/shoes/boots/ranger/orange + bootcolor = "orange" + +/obj/item/clothing/shoes/boots/ranger/yellow + bootcolor = "yellow" + +/* + * 80s + */ + +/obj/item/clothing/shoes/sneakerspurple + name = "purple sneakers" + desc = "A stylish, expensive pair of purple sneakers." + icon_state = "sneakerspurple" + item_state = "sneakerspurple" + +/obj/item/clothing/shoes/sneakersblue + name = "blue sneakers" + desc = "A stylish, expensive pair of blue sneakers." + icon_state = "sneakersblue" + item_state = "sneakersblue" + +/obj/item/clothing/shoes/sneakersred + name = "red sneakers" + desc = "A stylish, expensive pair of red sneakers." + icon_state = "sneakersred" + item_state = "sneakersred" + +/obj/item/clothing/shoes/ballet + name = "pointe shoes" + desc = "These shoes feature long lace straps and flattened off toes. Great for the most elegant of dances!" + icon_state = "ballet" + item_state = "ballet" diff --git a/code/modules/clothing/spacesuits/breaches.dm b/code/modules/clothing/spacesuits/breaches.dm index 6f23f2b57f8..7e74ee57f83 100644 --- a/code/modules/clothing/spacesuits/breaches.dm +++ b/code/modules/clothing/spacesuits/breaches.dm @@ -203,19 +203,19 @@ var/global/list/breach_burn_descriptors = list( repair_breaches(BURN, use_amt * repair_power, user) return - else if(istype(W, /obj/item/weapon/weldingtool)) + else if(W.has_tool_quality(TOOL_WELDER)) if(istype(src.loc,/mob/living)) - to_chat(user, "How do you intend to patch a hardsuit while someone is wearing it?") + to_chat(user, span_red("How do you intend to patch a hardsuit while someone is wearing it?")) return if (!damage || ! brute_damage) to_chat(user, "There is no structural damage on \the [src] to repair.") return - var/obj/item/weapon/weldingtool/WT = W + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(!WT.remove_fuel(5)) - to_chat(user, "You need more welding fuel to repair this suit.") + to_chat(user, span_red("You need more welding fuel to repair this suit.")) return repair_breaches(BRUTE, 3, user) @@ -227,4 +227,4 @@ var/global/list/breach_burn_descriptors = list( . = ..() if(can_breach && breaches?.len) for(var/datum/breach/B in breaches) - . += "It has \a [B.descriptor]." + . += span_red("It has \a [B.descriptor].") diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index 39ca0b8c742..f3fef22d7b1 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -1,104 +1,104 @@ -//Captain's Spacesuit -/obj/item/clothing/head/helmet/space/capspace - name = "space helmet" - icon_state = "capspace" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Only for the most fashionable of military figureheads." - item_flags = 0 - flags_inv = HIDEFACE|BLOCKHAIR - permeability_coefficient = 0.01 - armor = list(melee = 65, bullet = 50, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 50) - -//Captain's space suit This is not the proper path but I don't currently know enough about how this all works to mess with it. -/obj/item/clothing/suit/armor/captain - name = "Site Manager's armor" - desc = "A bulky, heavy-duty piece of exclusive corporate armor. YOU are in charge!" - icon_state = "caparmor" - w_class = ITEMSIZE_HUGE - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - item_flags = 0 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) - slowdown = 1.5 - armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - min_pressure_protection = 0 - max_pressure_protection = 2 * ONE_ATMOSPHERE - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - -//Deathsquad suit -/obj/item/clothing/head/helmet/space/deathsquad - name = "deathsquad helmet" - desc = "That's not red paint. That's real blood." - icon_state = "deathsquad" - item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") - armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 100, rad = 60) - item_flags = THICKMATERIAL - flags_inv = BLOCKHAIR - siemens_coefficient = 0.6 - -//how is this a space helmet? -/obj/item/clothing/head/helmet/space/deathsquad/beret - name = "officer's beret" - desc = "An armored beret commonly used by special operations officers." - icon_state = "beret_badge" - item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") - armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 30, rad = 30) - item_flags = 0 - flags_inv = BLOCKHAIR - siemens_coefficient = 0.9 - -//Space santa outfit suit -/obj/item/clothing/head/helmet/space/santahat - name = "Santa's hat" - desc = "Ho ho ho. Merrry X-mas!" - icon_state = "santahat" - item_flags = 0 - flags_inv = BLOCKHAIR - body_parts_covered = HEAD - -/obj/item/clothing/suit/space/santa - name = "Santa's suit" - desc = "Festive!" - icon_state = "santa" - slowdown = 0 - item_flags = 0 - allowed = list(/obj/item) //for stuffing exta special presents - -//Space pirate outfit -/obj/item/clothing/head/helmet/space/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - item_flags = 0 - flags_inv = BLOCKHAIR - body_parts_covered = 0 - siemens_coefficient = 0.9 - -/obj/item/clothing/suit/space/pirate //Whhhhyyyyyyy??? - name = "pirate coat" - desc = "Yarr." - icon_state = "pirate" - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) - slowdown = 0 - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0.9 - flags_inv = HIDETAIL|HIDEHOLSTER - body_parts_covered = UPPER_TORSO|ARMS - -//Orange emergency space suit -/obj/item/clothing/head/helmet/space/emergency - name = "emergency soft helmet" - icon_state = "syndicate-helm-orange" - desc = "A simple helmet with a built in light, smells like mothballs." - flash_protection = FLASH_PROTECTION_NONE - -/obj/item/clothing/suit/space/emergency - name = "emergency softsuit" - icon_state = "syndicate-orange" - desc = "A thin, ungainly softsuit colored in blaze orange for rescuers to easily locate, looks pretty fragile." - slowdown = 2 +//Captain's Spacesuit +/obj/item/clothing/head/helmet/space/capspace + name = "space helmet" + icon_state = "capspace" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Only for the most fashionable of military figureheads." + item_flags = 0 + flags_inv = HIDEFACE|BLOCKHAIR + permeability_coefficient = 0.01 + armor = list(melee = 65, bullet = 50, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 50) + +//Captain's space suit This is not the proper path but I don't currently know enough about how this all works to mess with it. +/obj/item/clothing/suit/armor/captain + name = "Site Manager's armor" + desc = "A bulky, heavy-duty piece of exclusive corporate armor. YOU are in charge!" + icon_state = "caparmor" + w_class = ITEMSIZE_HUGE + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + item_flags = 0 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) + slowdown = 1.5 + armor = list(melee = 65, bullet = 50, laser = 50, energy = 25, bomb = 50, bio = 100, rad = 50) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + min_pressure_protection = 0 + max_pressure_protection = 2 * ONE_ATMOSPHERE + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + +//Deathsquad suit +/obj/item/clothing/head/helmet/space/deathsquad + name = "deathsquad helmet" + desc = "That's not red paint. That's real blood." + icon_state = "deathsquad" + item_state_slots = list(slot_r_hand_str = "syndicate-helm-black-red", slot_l_hand_str = "syndicate-helm-black-red") + armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 100, rad = 60) + item_flags = THICKMATERIAL + flags_inv = BLOCKHAIR + siemens_coefficient = 0.6 + +//how is this a space helmet? +/obj/item/clothing/head/helmet/space/deathsquad/beret + name = "officer's beret" + desc = "An armored beret commonly used by special operations officers." + icon_state = "beret_badge" + item_state_slots = list(slot_r_hand_str = "beret", slot_l_hand_str = "beret") + armor = list(melee = 65, bullet = 55, laser = 35,energy = 20, bomb = 30, bio = 30, rad = 30) + item_flags = 0 + flags_inv = BLOCKHAIR + siemens_coefficient = 0.9 + +//Space santa outfit suit +/obj/item/clothing/head/helmet/space/santahat + name = "Santa's hat" + desc = "Ho ho ho. Merrry X-mas!" + icon_state = "santahat" + item_flags = 0 + flags_inv = BLOCKHAIR + body_parts_covered = HEAD + +/obj/item/clothing/suit/space/santa + name = "Santa's suit" + desc = "Festive!" + icon_state = "santa" + slowdown = 0 + item_flags = 0 + allowed = list(/obj/item) //for stuffing exta special presents + +//Space pirate outfit +/obj/item/clothing/head/helmet/space/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + item_flags = 0 + flags_inv = BLOCKHAIR + body_parts_covered = 0 + siemens_coefficient = 0.9 + +/obj/item/clothing/suit/space/pirate //Whhhhyyyyyyy??? + name = "pirate coat" + desc = "Yarr." + icon_state = "pirate" + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) + slowdown = 0 + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0.9 + flags_inv = HIDETAIL|HIDEHOLSTER + body_parts_covered = UPPER_TORSO|ARMS + +//Orange emergency space suit +/obj/item/clothing/head/helmet/space/emergency + name = "emergency soft helmet" + icon_state = "syndicate-helm-orange" + desc = "A simple helmet with a built in light, smells like mothballs." + flash_protection = FLASH_PROTECTION_NONE + +/obj/item/clothing/suit/space/emergency + name = "emergency softsuit" + icon_state = "syndicate-orange" + desc = "A thin, ungainly softsuit colored in blaze orange for rescuers to easily locate, looks pretty fragile." + slowdown = 2 diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 5d92408e560..4db4e7146e7 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -54,7 +54,7 @@ to_chat(user, "Another grenade of that type will not fit into the module.") return 0 - to_chat(user, "You slot \the [input_device] into the suit module.") + to_chat(user, span_blue("You slot \the [input_device] into the suit module.")) user.drop_from_inventory(input_device) qdel(input_device) accepted_item.charges++ @@ -256,7 +256,7 @@ else var/obj/item/new_weapon = new fabrication_type() new_weapon.forceMove(H) - to_chat(H, "You quickly fabricate \a [new_weapon].") + to_chat(H, span_blue("You quickly fabricate \a [new_weapon].")) H.put_in_hands(new_weapon) return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index fbf08479f49..21dad8e8c21 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -208,8 +208,8 @@ user.drop_from_inventory(ai) ai.forceMove(src) ai_card = ai - to_chat(ai_mob, "You have been transferred to \the [holder]'s [src].") - to_chat(user, "You load [ai_mob] into \the [holder]'s [src].") + to_chat(ai_mob, span_blue("You have been transferred to \the [holder]'s [src].")) + to_chat(user, span_blue("You load [ai_mob] into \the [holder]'s [src].")) integrated_ai = ai_mob @@ -261,7 +261,7 @@ var/obj/item/weapon/disk/tech_disk/disk = input_device if(disk.stored) if(load_data(disk.stored)) - to_chat(user, "Download successful; disk erased.") + to_chat(user, span_blue"Download successful; disk erased.")) disk.stored = null else to_chat(user, "The disk is corrupt. It is useless to you.") @@ -287,7 +287,7 @@ else // Maybe consider a way to drop all your data into a target repo in the future. if(load_data(incoming_files.known_tech)) - to_chat(user, "Download successful; local and remote repositories synchronized.") + to_chat(user, span_blue("Download successful; local and remote repositories synchronized.")) else to_chat(user, "Scan complete. There is nothing useful stored on this terminal.") return 1 @@ -466,10 +466,10 @@ if(!interfaced_with) if(M) - to_chat(M, "Total power drained: [round(total_power_drained*CELLRATE)] cell units.") + to_chat(M, span_blue("Total power drained: [round(total_power_drained*CELLRATE)] cell units.")) else if(M) - to_chat(M, "Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units.") + to_chat(M, span_blue("Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units.")) interfaced_with.drain_power(0,1,0) // Damage the victim. drain_loc = null diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm index 48752e3b6f2..89802134804 100644 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -37,7 +37,7 @@ var/mob/living/carbon/human/H = holder.wearer - to_chat(H, "You are now nearly invisible to normal detection.") + to_chat(H, span_blue("You are now nearly invisible to normal detection.")) H.alpha = 5 anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm b/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm index cdc86a0acfd..d3d62089111 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm @@ -200,8 +200,8 @@ user.drop_from_inventory(ai) ai.forceMove(src) ai_card = ai - to_chat(ai_mob, "You have been transferred to \the [holder]'s [src].") - to_chat(user, "You load [ai_mob] into \the [holder]'s [src].") + to_chat(ai_mob, span_blue("You have been transferred to \the [holder]'s [src].")) + to_chat(user, span_blue("You load [ai_mob] into \the [holder]'s [src].")) integrated_ai = ai_mob diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm b/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm index e8d6416e9a1..7e1d40858b9 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm @@ -71,7 +71,7 @@ break if(total_transferred) - to_chat(user, "You transfer [total_transferred] units into the suit reservoir.") + to_chat(user, span_blue("You transfer [total_transferred] units into the suit reservoir.")) else to_chat(user, "None of the reagents seem suitable.") return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm b/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm index 998fc3d46a1..46acec15988 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm @@ -34,7 +34,7 @@ to_chat(user, "Another grenade of that type will not fit into the module.") return 0 - to_chat(user, "You slot \the [input_device] into the suit module.") + to_chat(user, span_blue("You slot \the [input_device] into the suit module.")) user.drop_from_inventory(input_device) qdel(input_device) accepted_item.charges++ diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm b/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm index c63f1c2fbd4..2d27dd2ddb2 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm @@ -29,7 +29,7 @@ var/mob/living/carbon/human/H = holder.wearer - to_chat(H, "You are now nearly invisible to normal detection.") + to_chat(H, span_blue("You are now nearly invisible to normal detection.")) H.alpha = 5 anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) @@ -50,4 +50,4 @@ H.alpha = initial(H.alpha) H.visible_message("[H.name] appears from thin air!") - playsound(H, 'sound/effects/stealthoff.ogg', 75, 1) \ No newline at end of file + playsound(H, 'sound/effects/stealthoff.ogg', 75, 1) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm b/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm index be34426447d..d863fa06768 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm @@ -36,7 +36,7 @@ var/obj/item/weapon/disk/tech_disk/disk = input_device if(disk.stored) if(load_data(disk.stored)) - to_chat(user, "Download successful; disk erased.") + to_chat(user, span_blue("Download successful; disk erased.")) disk.stored = null else to_chat(user, "The disk is corrupt. It is useless to you.") @@ -58,7 +58,7 @@ else // Maybe consider a way to drop all your data into a target repo in the future. if(load_data(incoming_files.known_tech)) - to_chat(user, "Download successful; local and remote repositories synchronized.") + to_chat(user, span_blue("Download successful; local and remote repositories synchronized.")) else to_chat(user, "Scan complete. There is nothing useful stored on this terminal.") return 1 @@ -83,4 +83,4 @@ if(!data_found) stored_research += incoming_data return 1 - return 0 \ No newline at end of file + return 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm b/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm index 9252b062bd6..0ae4a461a30 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm @@ -36,7 +36,7 @@ to_chat(user, "Another grenade of that type will not fit into the module.") return 0 - to_chat(user, "You slot \the [input_device] into the suit module.") + to_chat(user, span_blue("You slot \the [input_device] into the suit module.")) user.drop_from_inventory(input_device) qdel(input_device) accepted_item.charges++ diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm b/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm index 8e71cf27458..3ea3c09ea82 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm @@ -34,7 +34,7 @@ else var/obj/item/new_weapon = new fabrication_type() new_weapon.forceMove(H) - to_chat(H, "You quickly fabricate \a [new_weapon].") + to_chat(H, span_blue("You quickly fabricate \a [new_weapon].")) H.put_in_hands(new_weapon) return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/passive_protection.dm b/code/modules/clothing/spacesuits/rig/modules/specific/passive_protection.dm index c27e0d9ce37..27eb5f9b794 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/passive_protection.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/passive_protection.dm @@ -27,14 +27,14 @@ if(!..()) return 0 - + var/mob/living/carbon/human/H = holder.wearer var/obj/item/clothing/shoes/boots = holder.boots var/obj/item/clothing/suit/space/rig/chest = holder.chest var/obj/item/clothing/head/helmet/space/rig/helmet = holder.helmet var/obj/item/clothing/gloves/gauntlets/rig/gloves = holder.gloves - to_chat(H, "You activate your suit's powered radiation shielding.") + to_chat(H, span_blue("You activate your suit's powered radiation shielding.")) stored_rad_armor = holder.armor["rad"] if(boots) boots.armor["rad"] = 100 @@ -107,17 +107,17 @@ if(!..()) return 0 - + var/mob/living/carbon/human/H = holder.wearer var/obj/item/clothing/shoes/boots = holder.boots var/obj/item/clothing/suit/space/rig/chest = holder.chest var/obj/item/clothing/head/helmet/space/rig/helmet = holder.helmet var/obj/item/clothing/gloves/gauntlets/rig/gloves = holder.gloves - + stored_max_pressure = holder.max_pressure_protection stored_max_temp = holder.max_heat_protection_temperature - to_chat(H, "You activate your suit's powered atmospheric shielding.") + to_chat(H, span_blue("You activate your suit's powered atmospheric shielding.")) if(boots) boots.max_pressure_protection = INFINITY @@ -163,4 +163,4 @@ holder.max_heat_protection_temperature = stored_max_temp stored_max_pressure = 0 - stored_max_temp = 0 \ No newline at end of file + stored_max_temp = 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm b/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm index bfea5ccf1c4..562f874af12 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm @@ -118,10 +118,10 @@ if(!interfaced_with) if(M) - to_chat(M, "Total power drained: [round(total_power_drained*CELLRATE)] cell units.") + to_chat(M, span_blue("Total power drained: [round(total_power_drained*CELLRATE)] cell units.")) else if(M) - to_chat(M, "Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units.") + to_chat(M, span_blue("Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units.")) interfaced_with.drain_power(0,1,0) // Damage the victim. drain_loc = null diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm b/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm index b8646cfa81d..2df47f575f3 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm @@ -27,7 +27,7 @@ var/mob/living/carbon/human/H = holder.wearer - to_chat(H, "You activate the suit's sprint mode.") + to_chat(H, span_blue("You activate the suit's sprint mode.")) holder.slowdown = initial(holder.slowdown) - sprint_speed diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm b/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm index 7e73f08880a..37848b2c1d9 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm @@ -198,7 +198,7 @@ // Don't cycle if this engage() is being called by activate(). if(!active) - to_chat(holder.wearer, "You activate your visual sensors.") + to_chat(holder.wearer, span_blue("You activate your visual sensors.")) return 1 if(vision_modes.len > 1) @@ -207,9 +207,9 @@ vision_index = 1 vision = vision_modes[vision_index] - to_chat(holder.wearer, "You cycle your sensors to [vision.mode] mode.") + to_chat(holder.wearer, span_blue("You cycle your sensors to [vision.mode] mode.")) else - to_chat(holder.wearer, "Your sensors only have one mode.") + to_chat(holder.wearer, span_blue("Your sensors only have one mode.")) return 1 /obj/item/rig_module/vision/activate() @@ -234,4 +234,4 @@ if(!vision) vision = vision_datum processed_vision += vision_datum - vision_modes = processed_vision \ No newline at end of file + vision_modes = processed_vision diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm b/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm index fe04d123f5c..5f32f15c415 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm @@ -38,15 +38,15 @@ if("Enable") active = 1 voice_holder.active = 1 - to_chat(usr, "You enable the speech synthesiser.") + to_chat(usr, span_blue("You enable the speech synthesiser.")) if("Disable") active = 0 voice_holder.active = 0 - to_chat(usr, "You disable the speech synthesiser.") + to_chat(usr, span_blue("You disable the speech synthesiser.")) if("Set Name") var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN) if(!raw_choice) return 0 voice_holder.voice = raw_choice - to_chat(usr, "You are now mimicking [voice_holder.voice].") - return 1 \ No newline at end of file + to_chat(usr, span_blue("You are now mimicking [voice_holder.voice].")) + return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index 4550eab0882..a92dfe1039e 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -201,7 +201,7 @@ break if(total_transferred) - to_chat(user, "You transfer [total_transferred] units into the suit reservoir.") + to_chat(user, span_blue("You transfer [total_transferred] units into the suit reservoir.")) else to_chat(user, "None of the reagents seem suitable.") return 1 @@ -330,17 +330,17 @@ if("Enable") active = 1 voice_holder.active = 1 - to_chat(usr, "You enable the speech synthesiser.") + to_chat(usr, span_blue("You enable the speech synthesiser.")) if("Disable") active = 0 voice_holder.active = 0 - to_chat(usr, "You disable the speech synthesiser.") + to_chat(usr, span_blue("You disable the speech synthesiser.")) if("Set Name") var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN) if(!raw_choice) return 0 voice_holder.voice = raw_choice - to_chat(usr, "You are now mimicking [voice_holder.voice].") + to_chat(usr, span_blue("You are now mimicking [voice_holder.voice].")) return 1 /obj/item/rig_module/maneuvering_jets @@ -508,7 +508,7 @@ to_chat(user, "Another grenade of that type will not fit into the module.") return 0 - to_chat(user, "You slot \the [input_device] into the suit module.") + to_chat(user, span_blue("You slot \the [input_device] into the suit module.")) user.drop_from_inventory(input_device) qdel(input_device) accepted_item.charges++ @@ -632,7 +632,7 @@ var/mob/living/carbon/human/H = holder.wearer - to_chat(H, "You activate the suit's sprint mode.") + to_chat(H, span_blue("You activate the suit's sprint mode.")) holder.slowdown = initial(holder.slowdown) - sprint_speed diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index ad8a6fc5abd..d96c6a80fac 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -205,7 +205,7 @@ // Don't cycle if this engage() is being called by activate(). if(starting_up) - to_chat(holder.wearer, "You activate your visual sensors.") + to_chat(holder.wearer, span_blue("You activate your visual sensors.")) return 1 if(vision_modes.len > 1) @@ -214,9 +214,9 @@ vision_index = 1 vision = vision_modes[vision_index] - to_chat(holder.wearer, "You cycle your sensors to [vision.mode] mode.") + to_chat(holder.wearer, span_blue("You cycle your sensors to [vision.mode] mode.")) else - to_chat(holder.wearer, "Your sensors only have one mode.") + to_chat(holder.wearer, span_blue("Your sensors only have one mode.")) return 1 /obj/item/rig_module/vision/activate() diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 85c00e67db7..081f08ed498 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -1,1011 +1,1011 @@ -#define ONLY_DEPLOY 1 -#define ONLY_RETRACT 2 -#define SEAL_DELAY 30 - -/* - * Defines the behavior of hardsuits/rigs/power armour. - */ - -/obj/item/weapon/rig - name = "hardsuit control module" - icon = 'icons/obj/rig_modules.dmi' - desc = "A back-mounted hardsuit deployment and control mechanism." - flags = PHORONGUARD - slot_flags = SLOT_BACK - req_one_access = list() - req_access = list() - w_class = ITEMSIZE_HUGE - action_button_name = "Toggle Heatsink" - - // These values are passed on to all component pieces. - armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.2 - permeability_coefficient = 0.1 - unacidable = TRUE - preserve_item = 1 - - var/default_mob_icon = 'icons/mob/rig_back.dmi' - - var/suit_state //The string used for the suit's icon_state. - - var/interface_path = "RIGSuit" - var/ai_interface_path = "RIGSuit" - var/interface_title = "Hardsuit Controller" - var/wearer_move_delay //Used for AI moving. - var/ai_controlled_move_delay = 10 - - // Keeps track of what this rig should spawn with. - var/suit_type = "hardsuit" - var/list/initial_modules - var/chest_type = /obj/item/clothing/suit/space/rig - var/helm_type = /obj/item/clothing/head/helmet/space/rig - var/boot_type = /obj/item/clothing/shoes/magboots/rig - var/glove_type = /obj/item/clothing/gloves/gauntlets/rig - var/cell_type = /obj/item/weapon/cell/high - var/air_type = /obj/item/weapon/tank/oxygen - - //Component/device holders. - var/obj/item/weapon/tank/air_supply // Air tank, if any. - var/obj/item/clothing/shoes/boots = null // Deployable boots, if any. - var/obj/item/clothing/suit/space/rig/chest // Deployable chestpiece, if any. - var/obj/item/clothing/head/helmet/space/rig/helmet = null // Deployable helmet, if any. - var/obj/item/clothing/gloves/gauntlets/rig/gloves = null // Deployable gauntlets, if any. - var/obj/item/weapon/cell/cell // Power supply, if any. - var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click) - var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time. - var/obj/item/rig_module/voice/speech // As above. - var/mob/living/carbon/human/wearer // The person currently wearing the rig. - var/image/mob_icon // Holder for on-mob icon. - var/list/installed_modules = list() // Power consumption/use bookkeeping. - - // Cooling system vars. - var/cooling_on = 0 //is it turned on? - var/max_cooling = 15 // in degrees per second - probably don't need to mess with heat capacity here - var/charge_consumption = 2 // charge per second at max_cooling //more effective on a rig, because it's all built in already - var/thermostat = T20C - - // Rig status vars. - var/open = 0 // Access panel status. - var/locked = 1 // Lock status. - var/subverted = 0 - var/interface_locked = 0 - var/control_overridden = 0 - var/ai_override_enabled = 0 - var/security_check_enabled = 1 - var/malfunctioning = 0 - var/malfunction_delay = 0 - var/electrified = 0 - var/locked_down = 0 - - var/seal_delay = SEAL_DELAY - var/sealing // Keeps track of seal status independantly of canremove. - var/offline = 1 // Should we be applying suit maluses? - var/offline_slowdown = 1.5 // If the suit is deployed and unpowered, it sets slowdown to this. - var/vision_restriction - var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets. - var/airtight = 1 //If set, will adjust AIRTIGHT flag and pressure protections on components. Otherwise it should leave them untouched. - var/rigsuit_max_pressure = 10 * ONE_ATMOSPHERE // Max pressure the rig protects against when sealed - var/rigsuit_min_pressure = 0 // Min pressure the rig protects against when sealed - - var/emp_protection = 0 - item_flags = PHORONGUARD //VOREStation add - - // Wiring! How exciting. - var/datum/wires/rig/wires - var/datum/effect/effect/system/spark_spread/spark_system - var/datum/mini_hud/rig/minihud - - // Action button - action_button_name = "Hardsuit Interface" - -/obj/item/weapon/rig/New() - ..() - - suit_state = icon_state - item_state = icon_state - wires = new(src) - - if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access)) - locked = 0 - - spark_system = new() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - if(initial_modules && initial_modules.len) - for(var/path in initial_modules) - var/obj/item/rig_module/module = new path(src) - installed_modules += module - module.installed(src) - - // Create and initialize our various segments. - if(cell_type) - cell = new cell_type(src) - if(air_type) - air_supply = new air_type(src) - if(glove_type) - gloves = new glove_type(src) - verbs |= /obj/item/weapon/rig/proc/toggle_gauntlets - if(helm_type) - helmet = new helm_type(src) - verbs |= /obj/item/weapon/rig/proc/toggle_helmet - if(boot_type) - boots = new boot_type(src) - verbs |= /obj/item/weapon/rig/proc/toggle_boots - if(chest_type) - chest = new chest_type(src) - if(allowed) - chest.allowed = allowed - verbs |= /obj/item/weapon/rig/proc/toggle_chest - - for(var/obj/item/piece in list(gloves,helmet,boots,chest)) - if(!istype(piece)) - continue - piece.canremove = FALSE - piece.name = "[suit_type] [initial(piece.name)]" - piece.desc = "It seems to be part of a [src.name]." - piece.icon_state = "[suit_state]" - piece.min_cold_protection_temperature = min_cold_protection_temperature - piece.max_heat_protection_temperature = max_heat_protection_temperature - if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation. - piece.siemens_coefficient = siemens_coefficient - piece.permeability_coefficient = permeability_coefficient - piece.unacidable = unacidable - if(islist(armor)) piece.armor = armor.Copy() - if(islist(armorsoak)) piece.armorsoak = armorsoak.Copy() - - update_icon(1) - -/obj/item/weapon/rig/Destroy() - for(var/obj/item/piece in list(gloves,boots,helmet,chest)) - var/mob/living/M = piece.loc - if(istype(M)) - M.drop_from_inventory(piece) - qdel(piece) - STOP_PROCESSING(SSobj, src) - qdel(wires) - wires = null - qdel(spark_system) - spark_system = null - return ..() - -/obj/item/weapon/rig/examine() - . = ..() - if(wearer) - for(var/obj/item/piece in list(helmet,gloves,chest,boots)) - if(!piece || piece.loc != wearer) - continue - . += "\icon[piece][bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." - - if(src.loc == usr) - . += "The access panel is [locked? "locked" : "unlocked"]." - . += "The maintenance panel is [open ? "open" : "closed"]." - . += "Hardsuit systems are [offline ? "offline" : "online"]." - . += "The cooling system is [cooling_on ? "active" : "inactive"]." - - if(open) - . += "It's equipped with [english_list(installed_modules)]." - -// We only care about processing when we're on a mob -/obj/item/weapon/rig/Moved(old_loc, direction, forced) - if(ismob(loc)) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - QDEL_NULL(minihud) // Just in case we get removed some other way - - // If we've lost any parts, grab them back. - var/mob/living/M - for(var/obj/item/piece in list(gloves,boots,helmet,chest)) - if(piece.loc != src && !(wearer && piece.loc == wearer)) - if(istype(piece.loc, /mob/living)) - M = piece.loc - M.unEquip(piece) - piece.forceMove(src) - -/obj/item/weapon/rig/get_worn_icon_file(var/body_type,var/slot_name,var/default_icon,var/inhands) - if(!inhands && (slot_name == slot_back_str || slot_name == slot_belt_str)) - if(icon_override) - return icon_override - else if(mob_icon) - return mob_icon - - return ..() - -/obj/item/weapon/rig/proc/suit_is_deployed() - if(!istype(wearer) || src.loc != wearer || (wearer.back != src && wearer.belt != src)) - return 0 - if(helm_type && !(helmet && wearer.head == helmet)) - return 0 - if(glove_type && !(gloves && wearer.gloves == gloves)) - return 0 - if(boot_type && !(boots && wearer.shoes == boots)) - return 0 - if(chest_type && !(chest && wearer.wear_suit == chest)) - return 0 - return 1 - -// Updates pressure protection -// Seal = 1 sets protection -// Seal = 0 unsets protection -/obj/item/weapon/rig/proc/update_airtight(var/obj/item/piece, var/seal = 0) - if(seal == 1) - piece.min_pressure_protection = rigsuit_min_pressure - piece.max_pressure_protection = rigsuit_max_pressure - piece.item_flags |= AIRTIGHT - else - piece.min_pressure_protection = null - piece.max_pressure_protection = null - piece.item_flags &= ~AIRTIGHT - return - - -/obj/item/weapon/rig/proc/reset() - offline = 2 - canremove = TRUE - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(!piece) continue - piece.icon_state = "[suit_state]" - if(airtight) - update_airtight(piece, 0) // Unseal - update_icon(1) - -/obj/item/weapon/rig/proc/cut_suit() - offline = 2 - canremove = TRUE - toggle_piece("helmet", loc, ONLY_RETRACT, TRUE) - toggle_piece("gauntlets", loc, ONLY_RETRACT, TRUE) - toggle_piece("boots", loc, ONLY_RETRACT, TRUE) - toggle_piece("chest", loc, ONLY_RETRACT, TRUE) - update_icon(1) - -/obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M,var/instant) - - if(sealing) return - - if(!check_power_cost(M)) - return 0 - - deploy(M,instant) - - var/seal_target = !canremove - var/failed_to_seal - - var/obj/screen/rig_booting/booting_L = new - var/obj/screen/rig_booting/booting_R = new - - if(!seal_target) - booting_L.icon_state = "boot_left" - booting_R.icon_state = "boot_load" - animate(booting_L, alpha=230, time=30, easing=SINE_EASING) - animate(booting_R, alpha=200, time=20, easing=SINE_EASING) - M.client?.screen += booting_L - M.client?.screen += booting_R - - canremove = FALSE // No removing the suit while unsealing. - sealing = 1 - - if(!seal_target && !suit_is_deployed()) - M.visible_message("[M]'s suit flashes an error light.","Your suit flashes an error light. It can't function properly without being fully deployed.") - playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) - failed_to_seal = 1 - - if(!failed_to_seal) - - if(!instant) - M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") - if(seal_delay && !do_after(M,seal_delay)) - if(M) - to_chat(M, "You must remain still while the suit is adjusting the components.") - playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) - failed_to_seal = 1 - if(!M) - failed_to_seal = 1 - else - for(var/list/piece_data in list(list(M.shoes,boots,"boots",boot_type),list(M.gloves,gloves,"gloves",glove_type),list(M.head,helmet,"helmet",helm_type),list(M.wear_suit,chest,"chest",chest_type))) - - var/obj/item/piece = piece_data[1] - var/obj/item/compare_piece = piece_data[2] - var/msg_type = piece_data[3] - var/piece_type = piece_data[4] - - if(!piece || !piece_type) - continue - - if(!istype(M) || !istype(piece) || !istype(compare_piece) || !msg_type) - if(M) - to_chat(M, "You must remain still while the suit is adjusting the components.") - failed_to_seal = 1 - break - - if(!failed_to_seal && (M.back == src || M.belt == src) && piece == compare_piece) - - if(seal_delay && !instant && !do_after(M,seal_delay,needhand=0)) - failed_to_seal = 1 - - piece.icon_state = "[suit_state][!seal_target ? "_sealed" : ""]" - switch(msg_type) - if("boots") - to_chat(M, "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"].") - M.update_inv_shoes() - if("gloves") - to_chat(M, "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"].") - M.update_inv_gloves() - if("chest") - to_chat(M, "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"].") - M.update_inv_wear_suit() - if("helmet") - to_chat(M, "\The [piece] hisses [!seal_target ? "closed" : "open"].") - M.update_inv_head() - if(helmet?.light_system == STATIC_LIGHT) - helmet.update_light(wearer) - - //sealed pieces become airtight, protecting against diseases - if (!seal_target) - piece.armor["bio"] = 100 - else - piece.armor["bio"] = src.armor["bio"] - playsound(src,'sound/machines/rig/rigservo.ogg', 10, FALSE) - - else - failed_to_seal = 1 - - if((M && !(istype(M) && (M.back == src || M.belt == src)) && !istype(M,/mob/living/silicon)) || (!seal_target && !suit_is_deployed())) - failed_to_seal = 1 - - sealing = null - - if(failed_to_seal) - M.client?.screen -= booting_L - M.client?.screen -= booting_R - qdel(booting_L) - qdel(booting_R) - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(!piece) continue - piece.icon_state = "[suit_state][!seal_target ? "" : "_sealed"]" - canremove = !seal_target - if(airtight) - update_component_sealed() - update_icon(1) - return 0 - - // Success! - canremove = seal_target - if(M.hud_used) - if(canremove) - QDEL_NULL(minihud) - else - minihud = new (M.hud_used, src) - to_chat(M, "Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].") - playsound(src, 'sound/machines/rig/rigstarted.ogg', 10, FALSE) - M.client?.screen -= booting_L - qdel(booting_L) - booting_R.icon_state = "boot_done" - spawn(40) - M.client?.screen -= booting_R - qdel(booting_R) - - if(canremove) - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - if(airtight) - update_component_sealed() - update_icon(1) - -/obj/item/weapon/rig/proc/update_component_sealed() - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(canremove) - update_airtight(piece, 0) // Unseal - else - update_airtight(piece, 1) // Seal - -/obj/item/weapon/rig/ui_action_click() - toggle_cooling(usr) - -/obj/item/weapon/rig/proc/toggle_cooling(var/mob/user) - if(cooling_on) - turn_cooling_off(user) - else - turn_cooling_on(user) - -/obj/item/weapon/rig/proc/turn_cooling_on(var/mob/user) - if(!cell) - return - if(cell.charge <= 0) - to_chat(user, "\The [src] has no power!") - return - if(!suit_is_deployed()) - to_chat(user, "The hardsuit needs to be deployed first!") - return - - cooling_on = 1 - to_chat(usr, "You switch \the [src]'s cooling system on.") - - -/obj/item/weapon/rig/proc/turn_cooling_off(var/mob/user, var/failed) - if(failed) - visible_message("\The [src]'s cooling system clicks and whines as it powers down.") - else - to_chat(usr, "You switch \the [src]'s cooling system off.") - cooling_on = 0 - -/obj/item/weapon/rig/proc/get_environment_temperature() - if (ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(istype(H.loc, /obj/mecha)) - var/obj/mecha/M = H.loc - return M.return_temperature() - else if(istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/obj/machinery/atmospherics/unary/cryo_cell/cryo = H.loc - return cryo.air_contents.temperature - - var/turf/T = get_turf(src) - if(istype(T, /turf/space)) - return 0 //space has no temperature, this just makes sure the cooling unit works in space - - var/datum/gas_mixture/environment = T.return_air() - if (!environment) - return 0 - - return environment.temperature - -/obj/item/weapon/rig/proc/attached_to_user(mob/M) - if (!ishuman(M)) - return 0 - - var/mob/living/carbon/human/H = M - - if (!H.wear_suit || (H.back != src && H.belt != src)) - return 0 - - return 1 - -/obj/item/weapon/rig/proc/coolingProcess() - if (!cooling_on || !cell) - return - - if (!ismob(loc)) - return - - if (!attached_to_user(loc)) //make sure the rig's not just in their hands - return - - if (!suit_is_deployed()) //inbuilt systems only work on the suit they're designed to work on - return - - var/mob/living/carbon/human/H = loc - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - var/efficiency = 1 - H.get_pressure_weakness(environment.return_pressure()) // You need to have a good seal for effective cooling - var/env_temp = get_environment_temperature() //wont save you from a fire - var/temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) - var/thermal_protection = H.get_heat_protection(env_temp) // ... unless you've got a good suit. - - if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. - temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) - else - temp_adj = min(H.bodytemperature - thermostat, max_cooling) - - if (temp_adj < 0.5) //only cools, doesn't heat, also we don't need extreme precision - return - - var/charge_usage = (temp_adj/max_cooling)*charge_consumption - - H.bodytemperature -= temp_adj*efficiency - - cell.use(charge_usage) - - if(cell.charge <= 0) - turn_cooling_off(H, 1) - -/obj/item/weapon/rig/process() - // Not on a mob...? - if(!ismob(loc)) - if(wearer?.wearing_rig == src) - wearer.wearing_rig = null - wearer = null - return PROCESS_KILL - - // Run through cooling - coolingProcess() - - if(!istype(wearer) || loc != wearer || (wearer.back != src && wearer.belt != src) || canremove || !cell || cell.charge <= 0) - if(!cell || cell.charge <= 0) - if(electrified > 0) - electrified = 0 - if(!offline) - if(istype(wearer)) - if(!canremove) - if (offline_slowdown < 1.5) - to_chat(wearer, "Your suit beeps stridently, and suddenly goes dead.") - else - to_chat(wearer, "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit.") - playsound(src, 'sound/machines/rig/rigdown.ogg', 60, FALSE) - if(offline_vision_restriction == 1) - to_chat(wearer, "The suit optics flicker and die, leaving you with restricted vision.") - else if(offline_vision_restriction == 2) - to_chat(wearer, "The suit optics drop out completely, drowning you in darkness.") - if(!offline) - offline = 1 - else - if(offline) - offline = 0 - if(istype(wearer) && !wearer.wearing_rig) - wearer.wearing_rig = src - slowdown = initial(slowdown) - - if(offline) - if(offline == 1) - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - offline = 2 - slowdown = offline_slowdown - return - - if(cell && cell.charge > 0 && electrified > 0) - electrified-- - - if(malfunction_delay > 0) - malfunction_delay-- - else if(malfunctioning) - malfunctioning-- - malfunction() - - for(var/obj/item/rig_module/module in installed_modules) - cell.use(module.process()*10) - -/obj/item/weapon/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai) - - if(!istype(user)) - return 0 - - var/fail_msg - - if(!user_is_ai) - var/mob/living/carbon/human/H = user - if(istype(H) && (H.back != src && H.belt != src)) - fail_msg = "You must be wearing \the [src] to do this." - else if(user.incorporeal_move) - fail_msg = "You must be solid to do this." - if(sealing) - fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated." - else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat))) - fail_msg = "You are in no fit state to do that." - else if(!cell) - fail_msg = "There is no cell installed in the suit." - else if(cost && cell.charge < cost * 10) //TODO: Cellrate? - fail_msg = "Not enough stored power." - - if(fail_msg) - to_chat(user, fail_msg) - playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) - return 0 - - // This is largely for cancelling stealth and whatever. - if(mod && mod.disruptive) - for(var/obj/item/rig_module/module in (installed_modules - mod)) - if(module.active && module.disruptable) - module.deactivate() - - cell.use(cost*10) - return 1 - -/obj/item/weapon/rig/update_icon(var/update_mob_icon) - - cut_overlays() - if(!mob_icon || update_mob_icon) - var/species_icon = default_mob_icon - // Since setting mob_icon will override the species checks in - // update_inv_wear_suit(), handle species checks here. - if(wearer && LAZYACCESS(sprite_sheets, wearer.species.get_bodytype(wearer))) - species_icon = sprite_sheets[wearer.species.get_bodytype(wearer)] - mob_icon = icon(icon = species_icon, icon_state = "[icon_state]") - - if(installed_modules.len) - for(var/obj/item/rig_module/module in installed_modules) - if(module.suit_overlay) - chest.add_overlay(image(module.suit_overlay_icon, icon_state = "[module.suit_overlay]", dir = SOUTH)) - - if(wearer) - wearer.update_inv_shoes() - wearer.update_inv_gloves() - wearer.update_inv_head() - wearer.update_inv_wear_suit() - wearer.update_inv_back() - return - -/obj/item/weapon/rig/proc/check_suit_access(var/mob/living/carbon/human/user, var/do_message = TRUE) - - if(!security_check_enabled) - return 1 - - if(istype(user)) - if(!canremove) - return 1 - if(malfunction_check(user)) - return 0 - if(user.back != src && user.belt != src) - return 0 - else if(!src.allowed(user)) - if(do_message) - to_chat(user, "Unauthorized user. Access denied.") - return 0 - - else if(!ai_override_enabled) - if(do_message) - to_chat(user, "Synthetic access disabled. Please consult hardware provider.") - return 0 - - return 1 - -/obj/item/weapon/rig/proc/notify_ai(var/message) - for(var/obj/item/rig_module/ai_container/module in installed_modules) - if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) - to_chat(module.integrated_ai, "[message]") - . = 1 - -/obj/item/weapon/rig/equipped(mob/living/carbon/human/M) - ..() - - if(istype(M.back, /obj/item/weapon/rig) && istype(M.belt, /obj/item/weapon/rig)) - to_chat(M, "You try to put on the [src], but it won't fit.") - if(M && (M.back == src || M.belt == src)) - if(!M.unEquip(src)) - return - src.forceMove(get_turf(src)) - return - - if(seal_delay > 0 && istype(M) && (M.back == src || M.belt == src)) - M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") - if(!do_after(M,seal_delay)) - if(M && (M.back == src || M.belt == src)) - if(!M.unEquip(src)) - return - src.forceMove(get_turf(src)) - return - - if(istype(M) && (M.back == src || M.belt == src)) - M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") - wearer = M - wearer.wearing_rig = src - update_icon() - -/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/forced = FALSE) - - if((sealing || !cell || !cell.charge) && !forced) - return - - if((!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) && !forced) - return - - if((usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) && !forced) // If the usr isn't wearing the suit it's probably an AI. - return - - var/obj/item/check_slot - var/equip_to - var/obj/item/use_obj - - if(!H) - return - - switch(piece) - if("helmet") - equip_to = slot_head - use_obj = helmet - check_slot = H.head - if("gauntlets") - equip_to = slot_gloves - use_obj = gloves - check_slot = H.gloves - if("boots") - equip_to = slot_shoes - use_obj = boots - check_slot = H.shoes - if("chest") - equip_to = slot_wear_suit - use_obj = chest - check_slot = H.wear_suit - - if(use_obj) - if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) - - var/mob/living/carbon/human/holder - - if(use_obj) - holder = use_obj.loc - if(istype(holder)) - if(use_obj && check_slot == use_obj) - to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.") - playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) - use_obj.canremove = TRUE - holder.drop_from_inventory(use_obj) - use_obj.forceMove(get_turf(src)) - use_obj.dropped() - use_obj.canremove = FALSE - use_obj.forceMove(src) - - else if (deploy_mode != ONLY_RETRACT) - if(check_slot && check_slot == use_obj) - return - use_obj.forceMove(H) - if(!H.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) - use_obj.forceMove(src) - if(check_slot) - to_chat(H, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") - return - else - to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") - playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) - - if(piece == "helmet" && helmet?.light_system == STATIC_LIGHT) - helmet.update_light() - -/obj/item/weapon/rig/proc/deploy(mob/M,var/sealed) - - var/mob/living/carbon/human/H = M - - if(!H || !istype(H)) return - - if(H.back != src && H.belt != src) - return - - if(sealed) - if(H.head) - var/obj/item/garbage = H.head - H.drop_from_inventory(garbage) - H.head = null - qdel(garbage) - - if(H.gloves) - var/obj/item/garbage = H.gloves - H.drop_from_inventory(garbage) - H.gloves = null - qdel(garbage) - - if(H.shoes) - var/obj/item/garbage = H.shoes - H.drop_from_inventory(garbage) - H.shoes = null - qdel(garbage) - - if(H.wear_suit) - var/obj/item/garbage = H.wear_suit - H.drop_from_inventory(garbage) - H.wear_suit = null - qdel(garbage) - - for(var/piece in list("helmet","gauntlets","chest","boots")) - toggle_piece(piece, H, ONLY_DEPLOY) - -/obj/item/weapon/rig/dropped(var/mob/user) - ..() - for(var/piece in list("helmet","gauntlets","chest","boots")) - toggle_piece(piece, user, ONLY_RETRACT) - if(wearer && wearer.wearing_rig == src) - wearer.wearing_rig = null - wearer = null - -//Todo -/obj/item/weapon/rig/proc/malfunction() - return 0 - -/obj/item/weapon/rig/emp_act(severity_class) - //set malfunctioning - if(emp_protection < 30) //for ninjas, really. - malfunctioning += 10 - if(malfunction_delay <= 0) - malfunction_delay = max(malfunction_delay, round(30/severity_class)) - - //drain some charge - if(cell) cell.emp_act(severity_class + 15) - - //possibly damage some modules - take_hit((100/severity_class), "electrical pulse", 1) - -/obj/item/weapon/rig/proc/shock(mob/user) - if (electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here. - spark_system.start() - if(user.stunned) - return 1 - return 0 - -/obj/item/weapon/rig/proc/take_hit(damage, source, is_emp=0) - - if(!installed_modules.len) - return - - var/chance - if(!is_emp) - chance = 2*max(0, damage - (chest? chest.breach_threshold : 0)) - else - //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average. - //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules - chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1) - - if(!prob(chance)) - return - - //deal addition damage to already damaged module first. - //This way the chances of a module being disabled aren't so remote. - var/list/valid_modules = list() - var/list/damaged_modules = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.damage < 2) - valid_modules |= module - if(module.damage > 0) - damaged_modules |= module - - var/obj/item/rig_module/dam_module = null - if(damaged_modules.len) - dam_module = pick(damaged_modules) - else if(valid_modules.len) - dam_module = pick(valid_modules) - - if(!dam_module) return - - dam_module.damage++ - - if(!source) - source = "hit" - - if(wearer) - if(dam_module.damage >= 2) - to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!") - else - to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!") - dam_module.deactivate() - -/obj/item/weapon/rig/proc/malfunction_check(var/mob/living/carbon/human/user) - if(malfunction_delay) - if(offline) - to_chat(user, "The suit is completely unresponsive.") - else - to_chat(user, "ERROR: Hardware fault. Rebooting interface...") - return 1 - return 0 - -/obj/item/weapon/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0) - - if(check_for_ai) - if(!(locate(/obj/item/rig_module/ai_container) in contents)) - return 0 - var/found_ai - for(var/obj/item/rig_module/ai_container/module in contents) - if(module.damage >= 2) - continue - if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) - found_ai = 1 - break - if(!found_ai) - return 0 - - if(check_user_module) - if(!user || !user.loc || !user.loc.loc) - return 0 - var/obj/item/rig_module/ai_container/module = user.loc.loc - if(!istype(module) || module.damage >= 2) - to_chat(user, "Your host module is unable to interface with the suit.") - return 0 - - if(offline || !cell || !cell.charge || locked_down) - if(user) - to_chat(user, "Your host rig is unpowered and unresponsive.") - return 0 - if(!wearer || (wearer.back != src && wearer.belt != src)) - if(user) - to_chat(user, "Your host rig is not being worn.") - return 0 - if(!wearer.stat && !control_overridden && !ai_override_enabled) - if(user) - to_chat(user, "You are locked out of the suit servo controller.") - return 0 - return 1 - -/obj/item/weapon/rig/proc/force_rest(var/mob/user) - if(!ai_can_move_suit(user, check_user_module = 1)) - return - wearer.lay_down() - to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].") - -/obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user) - - // Why is all this shit in client/Move()? Who knows? - if(world.time < wearer_move_delay) - return - - if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) - return - - //This is sota the goto stop mobs from moving var - if(wearer.transforming || !wearer.canmove) - return - - if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0)) - if(!wearer.Process_Spacemove(0)) - return 0 - - if(malfunctioning) - direction = pick(cardinal) - - // Inside an object, tell it we moved. - if(isobj(wearer.loc) || ismob(wearer.loc)) - var/atom/O = wearer.loc - return O.relaymove(wearer, direction) - - if(isturf(wearer.loc)) - if(wearer.restrained())//Why being pulled while cuffed prevents you from moving - for(var/mob/M in range(wearer, 1)) - if(M.pulling == wearer) - if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) - to_chat(user, "Your host is restrained! They can't move!") - return 0 - else - M.stop_pulling() - - if(wearer.pinned.len) - to_chat(src, "Your host is pinned to a wall by [wearer.pinned[1]]!") - return 0 - - // AIs are a bit slower than regular and ignore move intent. - wearer_move_delay = world.time + ai_controlled_move_delay - - if(istype(wearer.buckled, /obj/vehicle)) - //manually set move_delay for vehicles so we don't inherit any mob movement penalties - //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm - wearer_move_delay = world.time - return wearer.buckled.relaymove(wearer, direction) - - if(istype(wearer.machine, /obj/machinery)) - if(wearer.machine.relaymove(wearer, direction)) - return - - if(wearer.pulledby || wearer.buckled) // Wheelchair driving! - if(istype(wearer.loc, /turf/space)) - return // No wheelchair driving in space - if(istype(wearer.pulledby, /obj/structure/bed/chair/wheelchair)) - return wearer.pulledby.relaymove(wearer, direction) - else if(istype(wearer.buckled, /obj/structure/bed/chair/wheelchair)) - if(ishuman(wearer.buckled)) - var/obj/item/organ/external/l_hand = wearer.get_organ("l_hand") - var/obj/item/organ/external/r_hand = wearer.get_organ("r_hand") - if((!l_hand || (l_hand.status & ORGAN_DESTROYED)) && (!r_hand || (r_hand.status & ORGAN_DESTROYED))) - return // No hands to drive your chair? Tough luck! - wearer_move_delay += 2 - return wearer.buckled.relaymove(wearer,direction) - - cell.use(200) //Arbitrary, TODO - wearer.Move(get_step(get_turf(wearer),direction),direction) - -// This returns the rig if you are contained inside one, but not if you are wearing it -/atom/proc/get_rig() - if(loc) - return loc.get_rig() - return null - -/obj/item/weapon/rig/get_rig() - return src - -/mob/living/carbon/human/get_rig() - if(istype(back, /obj/item/weapon/rig)) - return back - else if(istype(belt, /obj/item/weapon/rig)) - return belt - else - return null - -//Boot animation screen objects -/obj/screen/rig_booting - screen_loc = "1,1" - icon = 'icons/obj/rig_boot.dmi' - icon_state = "" - layer = SCREEN_LAYER - plane = PLANE_FULLSCREEN - mouse_opacity = 0 - alpha = 20 //Animated up when loading - -#undef ONLY_DEPLOY -#undef ONLY_RETRACT -#undef SEAL_DELAY +#define ONLY_DEPLOY 1 +#define ONLY_RETRACT 2 +#define SEAL_DELAY 30 + +/* + * Defines the behavior of hardsuits/rigs/power armour. + */ + +/obj/item/weapon/rig + name = "hardsuit control module" + icon = 'icons/obj/rig_modules.dmi' + desc = "A back-mounted hardsuit deployment and control mechanism." + flags = PHORONGUARD + slot_flags = SLOT_BACK + req_one_access = list() + req_access = list() + w_class = ITEMSIZE_HUGE + action_button_name = "Toggle Heatsink" + + // These values are passed on to all component pieces. + armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20) + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.2 + permeability_coefficient = 0.1 + unacidable = TRUE + preserve_item = 1 + + var/default_mob_icon = 'icons/mob/rig_back.dmi' + + var/suit_state //The string used for the suit's icon_state. + + var/interface_path = "RIGSuit" + var/ai_interface_path = "RIGSuit" + var/interface_title = "Hardsuit Controller" + var/wearer_move_delay //Used for AI moving. + var/ai_controlled_move_delay = 10 + + // Keeps track of what this rig should spawn with. + var/suit_type = "hardsuit" + var/list/initial_modules + var/chest_type = /obj/item/clothing/suit/space/rig + var/helm_type = /obj/item/clothing/head/helmet/space/rig + var/boot_type = /obj/item/clothing/shoes/magboots/rig + var/glove_type = /obj/item/clothing/gloves/gauntlets/rig + var/cell_type = /obj/item/weapon/cell/high + var/air_type = /obj/item/weapon/tank/oxygen + + //Component/device holders. + var/obj/item/weapon/tank/air_supply // Air tank, if any. + var/obj/item/clothing/shoes/boots = null // Deployable boots, if any. + var/obj/item/clothing/suit/space/rig/chest // Deployable chestpiece, if any. + var/obj/item/clothing/head/helmet/space/rig/helmet = null // Deployable helmet, if any. + var/obj/item/clothing/gloves/gauntlets/rig/gloves = null // Deployable gauntlets, if any. + var/obj/item/weapon/cell/cell // Power supply, if any. + var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click) + var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time. + var/obj/item/rig_module/voice/speech // As above. + var/mob/living/carbon/human/wearer // The person currently wearing the rig. + var/image/mob_icon // Holder for on-mob icon. + var/list/installed_modules = list() // Power consumption/use bookkeeping. + + // Cooling system vars. + var/cooling_on = 0 //is it turned on? + var/max_cooling = 15 // in degrees per second - probably don't need to mess with heat capacity here + var/charge_consumption = 2 // charge per second at max_cooling //more effective on a rig, because it's all built in already + var/thermostat = T20C + + // Rig status vars. + var/open = 0 // Access panel status. + var/locked = 1 // Lock status. + var/subverted = 0 + var/interface_locked = 0 + var/control_overridden = 0 + var/ai_override_enabled = 0 + var/security_check_enabled = 1 + var/malfunctioning = 0 + var/malfunction_delay = 0 + var/electrified = 0 + var/locked_down = 0 + + var/seal_delay = SEAL_DELAY + var/sealing // Keeps track of seal status independantly of canremove. + var/offline = 1 // Should we be applying suit maluses? + var/offline_slowdown = 1.5 // If the suit is deployed and unpowered, it sets slowdown to this. + var/vision_restriction + var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets. + var/airtight = 1 //If set, will adjust AIRTIGHT flag and pressure protections on components. Otherwise it should leave them untouched. + var/rigsuit_max_pressure = 10 * ONE_ATMOSPHERE // Max pressure the rig protects against when sealed + var/rigsuit_min_pressure = 0 // Min pressure the rig protects against when sealed + + var/emp_protection = 0 + item_flags = PHORONGUARD //VOREStation add + + // Wiring! How exciting. + var/datum/wires/rig/wires + var/datum/effect/effect/system/spark_spread/spark_system + var/datum/mini_hud/rig/minihud + + // Action button + action_button_name = "Hardsuit Interface" + +/obj/item/weapon/rig/New() + ..() + + suit_state = icon_state + item_state = icon_state + wires = new(src) + + if(!LAZYLEN(req_access) && !LAZYLEN(req_one_access)) + locked = 0 + + spark_system = new() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + if(initial_modules && initial_modules.len) + for(var/path in initial_modules) + var/obj/item/rig_module/module = new path(src) + installed_modules += module + module.installed(src) + + // Create and initialize our various segments. + if(cell_type) + cell = new cell_type(src) + if(air_type) + air_supply = new air_type(src) + if(glove_type) + gloves = new glove_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_gauntlets + if(helm_type) + helmet = new helm_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_helmet + if(boot_type) + boots = new boot_type(src) + verbs |= /obj/item/weapon/rig/proc/toggle_boots + if(chest_type) + chest = new chest_type(src) + if(allowed) + chest.allowed = allowed + verbs |= /obj/item/weapon/rig/proc/toggle_chest + + for(var/obj/item/piece in list(gloves,helmet,boots,chest)) + if(!istype(piece)) + continue + piece.canremove = FALSE + piece.name = "[suit_type] [initial(piece.name)]" + piece.desc = "It seems to be part of a [src.name]." + piece.icon_state = "[suit_state]" + piece.min_cold_protection_temperature = min_cold_protection_temperature + piece.max_heat_protection_temperature = max_heat_protection_temperature + if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation. + piece.siemens_coefficient = siemens_coefficient + piece.permeability_coefficient = permeability_coefficient + piece.unacidable = unacidable + if(islist(armor)) piece.armor = armor.Copy() + if(islist(armorsoak)) piece.armorsoak = armorsoak.Copy() + + update_icon(1) + +/obj/item/weapon/rig/Destroy() + for(var/obj/item/piece in list(gloves,boots,helmet,chest)) + var/mob/living/M = piece.loc + if(istype(M)) + M.drop_from_inventory(piece) + qdel(piece) + STOP_PROCESSING(SSobj, src) + qdel(wires) + wires = null + qdel(spark_system) + spark_system = null + return ..() + +/obj/item/weapon/rig/examine() + . = ..() + if(wearer) + for(var/obj/item/piece in list(helmet,gloves,chest,boots)) + if(!piece || piece.loc != wearer) + continue + . += "\icon[piece][bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." + + if(src.loc == usr) + . += "The access panel is [locked? "locked" : "unlocked"]." + . += "The maintenance panel is [open ? "open" : "closed"]." + . += "Hardsuit systems are [offline ? "offline" : "online"]." + . += "The cooling system is [cooling_on ? "active" : "inactive"]." + + if(open) + . += "It's equipped with [english_list(installed_modules)]." + +// We only care about processing when we're on a mob +/obj/item/weapon/rig/Moved(old_loc, direction, forced) + if(ismob(loc)) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + QDEL_NULL(minihud) // Just in case we get removed some other way + + // If we've lost any parts, grab them back. + var/mob/living/M + for(var/obj/item/piece in list(gloves,boots,helmet,chest)) + if(piece.loc != src && !(wearer && piece.loc == wearer)) + if(istype(piece.loc, /mob/living)) + M = piece.loc + M.unEquip(piece) + piece.forceMove(src) + +/obj/item/weapon/rig/get_worn_icon_file(var/body_type,var/slot_name,var/default_icon,var/inhands) + if(!inhands && (slot_name == slot_back_str || slot_name == slot_belt_str)) + if(icon_override) + return icon_override + else if(mob_icon) + return mob_icon + + return ..() + +/obj/item/weapon/rig/proc/suit_is_deployed() + if(!istype(wearer) || src.loc != wearer || (wearer.back != src && wearer.belt != src)) + return 0 + if(helm_type && !(helmet && wearer.head == helmet)) + return 0 + if(glove_type && !(gloves && wearer.gloves == gloves)) + return 0 + if(boot_type && !(boots && wearer.shoes == boots)) + return 0 + if(chest_type && !(chest && wearer.wear_suit == chest)) + return 0 + return 1 + +// Updates pressure protection +// Seal = 1 sets protection +// Seal = 0 unsets protection +/obj/item/weapon/rig/proc/update_airtight(var/obj/item/piece, var/seal = 0) + if(seal == 1) + piece.min_pressure_protection = rigsuit_min_pressure + piece.max_pressure_protection = rigsuit_max_pressure + piece.item_flags |= AIRTIGHT + else + piece.min_pressure_protection = null + piece.max_pressure_protection = null + piece.item_flags &= ~AIRTIGHT + return + + +/obj/item/weapon/rig/proc/reset() + offline = 2 + canremove = TRUE + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!piece) continue + piece.icon_state = "[suit_state]" + if(airtight) + update_airtight(piece, 0) // Unseal + update_icon(1) + +/obj/item/weapon/rig/proc/cut_suit() + offline = 2 + canremove = TRUE + toggle_piece("helmet", loc, ONLY_RETRACT, TRUE) + toggle_piece("gauntlets", loc, ONLY_RETRACT, TRUE) + toggle_piece("boots", loc, ONLY_RETRACT, TRUE) + toggle_piece("chest", loc, ONLY_RETRACT, TRUE) + update_icon(1) + +/obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M,var/instant) + + if(sealing) return + + if(!check_power_cost(M)) + return 0 + + deploy(M,instant) + + var/seal_target = !canremove + var/failed_to_seal + + var/obj/screen/rig_booting/booting_L = new + var/obj/screen/rig_booting/booting_R = new + + if(!seal_target) + booting_L.icon_state = "boot_left" + booting_R.icon_state = "boot_load" + animate(booting_L, alpha=230, time=30, easing=SINE_EASING) + animate(booting_R, alpha=200, time=20, easing=SINE_EASING) + M.client?.screen += booting_L + M.client?.screen += booting_R + + canremove = FALSE // No removing the suit while unsealing. + sealing = 1 + + if(!seal_target && !suit_is_deployed()) + M.visible_message("[M]'s suit flashes an error light.","Your suit flashes an error light. It can't function properly without being fully deployed.") + playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) + failed_to_seal = 1 + + if(!failed_to_seal) + + if(!instant) + M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") + if(seal_delay && !do_after(M,seal_delay)) + if(M) + to_chat(M, "You must remain still while the suit is adjusting the components.") + playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) + failed_to_seal = 1 + if(!M) + failed_to_seal = 1 + else + for(var/list/piece_data in list(list(M.shoes,boots,"boots",boot_type),list(M.gloves,gloves,"gloves",glove_type),list(M.head,helmet,"helmet",helm_type),list(M.wear_suit,chest,"chest",chest_type))) + + var/obj/item/piece = piece_data[1] + var/obj/item/compare_piece = piece_data[2] + var/msg_type = piece_data[3] + var/piece_type = piece_data[4] + + if(!piece || !piece_type) + continue + + if(!istype(M) || !istype(piece) || !istype(compare_piece) || !msg_type) + if(M) + to_chat(M, "You must remain still while the suit is adjusting the components.") + failed_to_seal = 1 + break + + if(!failed_to_seal && (M.back == src || M.belt == src) && piece == compare_piece) + + if(seal_delay && !instant && !do_after(M,seal_delay,needhand=0)) + failed_to_seal = 1 + + piece.icon_state = "[suit_state][!seal_target ? "_sealed" : ""]" + switch(msg_type) + if("boots") + to_chat(M, "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"].") + M.update_inv_shoes() + if("gloves") + to_chat(M, "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"].") + M.update_inv_gloves() + if("chest") + to_chat(M, "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"].") + M.update_inv_wear_suit() + if("helmet") + to_chat(M, "\The [piece] hisses [!seal_target ? "closed" : "open"].") + M.update_inv_head() + if(helmet?.light_system == STATIC_LIGHT) + helmet.update_light(wearer) + + //sealed pieces become airtight, protecting against diseases + if (!seal_target) + piece.armor["bio"] = 100 + else + piece.armor["bio"] = src.armor["bio"] + playsound(src,'sound/machines/rig/rigservo.ogg', 10, FALSE) + + else + failed_to_seal = 1 + + if((M && !(istype(M) && (M.back == src || M.belt == src)) && !istype(M,/mob/living/silicon)) || (!seal_target && !suit_is_deployed())) + failed_to_seal = 1 + + sealing = null + + if(failed_to_seal) + M.client?.screen -= booting_L + M.client?.screen -= booting_R + qdel(booting_L) + qdel(booting_R) + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(!piece) continue + piece.icon_state = "[suit_state][!seal_target ? "" : "_sealed"]" + canremove = !seal_target + if(airtight) + update_component_sealed() + update_icon(1) + return 0 + + // Success! + canremove = seal_target + if(M.hud_used) + if(canremove) + QDEL_NULL(minihud) + else + minihud = new (M.hud_used, src) + to_chat(M, "Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].") + playsound(src, 'sound/machines/rig/rigstarted.ogg', 10, FALSE) + M.client?.screen -= booting_L + qdel(booting_L) + booting_R.icon_state = "boot_done" + spawn(40) + M.client?.screen -= booting_R + qdel(booting_R) + + if(canremove) + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + if(airtight) + update_component_sealed() + update_icon(1) + +/obj/item/weapon/rig/proc/update_component_sealed() + for(var/obj/item/piece in list(helmet,boots,gloves,chest)) + if(canremove) + update_airtight(piece, 0) // Unseal + else + update_airtight(piece, 1) // Seal + +/obj/item/weapon/rig/ui_action_click() + toggle_cooling(usr) + +/obj/item/weapon/rig/proc/toggle_cooling(var/mob/user) + if(cooling_on) + turn_cooling_off(user) + else + turn_cooling_on(user) + +/obj/item/weapon/rig/proc/turn_cooling_on(var/mob/user) + if(!cell) + return + if(cell.charge <= 0) + to_chat(user, "\The [src] has no power!") + return + if(!suit_is_deployed()) + to_chat(user, "The hardsuit needs to be deployed first!") + return + + cooling_on = 1 + to_chat(usr, "You switch \the [src]'s cooling system on.") + + +/obj/item/weapon/rig/proc/turn_cooling_off(var/mob/user, var/failed) + if(failed) + visible_message("\The [src]'s cooling system clicks and whines as it powers down.") + else + to_chat(usr, "You switch \the [src]'s cooling system off.") + cooling_on = 0 + +/obj/item/weapon/rig/proc/get_environment_temperature() + if (ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(istype(H.loc, /obj/mecha)) + var/obj/mecha/M = H.loc + return M.return_temperature() + else if(istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/obj/machinery/atmospherics/unary/cryo_cell/cryo = H.loc + return cryo.air_contents.temperature + + var/turf/T = get_turf(src) + if(istype(T, /turf/space)) + return 0 //space has no temperature, this just makes sure the cooling unit works in space + + var/datum/gas_mixture/environment = T.return_air() + if (!environment) + return 0 + + return environment.temperature + +/obj/item/weapon/rig/proc/attached_to_user(mob/M) + if (!ishuman(M)) + return 0 + + var/mob/living/carbon/human/H = M + + if (!H.wear_suit || (H.back != src && H.belt != src)) + return 0 + + return 1 + +/obj/item/weapon/rig/proc/coolingProcess() + if (!cooling_on || !cell) + return + + if (!ismob(loc)) + return + + if (!attached_to_user(loc)) //make sure the rig's not just in their hands + return + + if (!suit_is_deployed()) //inbuilt systems only work on the suit they're designed to work on + return + + var/mob/living/carbon/human/H = loc + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + var/efficiency = 1 - H.get_pressure_weakness(environment.return_pressure()) // You need to have a good seal for effective cooling + var/env_temp = get_environment_temperature() //wont save you from a fire + var/temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) + var/thermal_protection = H.get_heat_protection(env_temp) // ... unless you've got a good suit. + + if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. + temp_adj = min(H.bodytemperature - max(thermostat, env_temp), max_cooling) + else + temp_adj = min(H.bodytemperature - thermostat, max_cooling) + + if (temp_adj < 0.5) //only cools, doesn't heat, also we don't need extreme precision + return + + var/charge_usage = (temp_adj/max_cooling)*charge_consumption + + H.bodytemperature -= temp_adj*efficiency + + cell.use(charge_usage) + + if(cell.charge <= 0) + turn_cooling_off(H, 1) + +/obj/item/weapon/rig/process() + // Not on a mob...? + if(!ismob(loc)) + if(wearer?.wearing_rig == src) + wearer.wearing_rig = null + wearer = null + return PROCESS_KILL + + // Run through cooling + coolingProcess() + + if(!istype(wearer) || loc != wearer || (wearer.back != src && wearer.belt != src) || canremove || !cell || cell.charge <= 0) + if(!cell || cell.charge <= 0) + if(electrified > 0) + electrified = 0 + if(!offline) + if(istype(wearer)) + if(!canremove) + if (offline_slowdown < 1.5) + to_chat(wearer, "Your suit beeps stridently, and suddenly goes dead.") + else + to_chat(wearer, "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit.") + playsound(src, 'sound/machines/rig/rigdown.ogg', 60, FALSE) + if(offline_vision_restriction == 1) + to_chat(wearer, "The suit optics flicker and die, leaving you with restricted vision.") + else if(offline_vision_restriction == 2) + to_chat(wearer, "The suit optics drop out completely, drowning you in darkness.") + if(!offline) + offline = 1 + else + if(offline) + offline = 0 + if(istype(wearer) && !wearer.wearing_rig) + wearer.wearing_rig = src + slowdown = initial(slowdown) + + if(offline) + if(offline == 1) + for(var/obj/item/rig_module/module in installed_modules) + module.deactivate() + offline = 2 + slowdown = offline_slowdown + return + + if(cell && cell.charge > 0 && electrified > 0) + electrified-- + + if(malfunction_delay > 0) + malfunction_delay-- + else if(malfunctioning) + malfunctioning-- + malfunction() + + for(var/obj/item/rig_module/module in installed_modules) + cell.use(module.process()*10) + +/obj/item/weapon/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai) + + if(!istype(user)) + return 0 + + var/fail_msg + + if(!user_is_ai) + var/mob/living/carbon/human/H = user + if(istype(H) && (H.back != src && H.belt != src)) + fail_msg = "You must be wearing \the [src] to do this." + else if(user.incorporeal_move) + fail_msg = "You must be solid to do this." + if(sealing) + fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated." + else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat))) + fail_msg = "You are in no fit state to do that." + else if(!cell) + fail_msg = "There is no cell installed in the suit." + else if(cost && cell.charge < cost * 10) //TODO: Cellrate? + fail_msg = "Not enough stored power." + + if(fail_msg) + to_chat(user, fail_msg) + playsound(src, 'sound/machines/rig/rigerror.ogg', 20, FALSE) + return 0 + + // This is largely for cancelling stealth and whatever. + if(mod && mod.disruptive) + for(var/obj/item/rig_module/module in (installed_modules - mod)) + if(module.active && module.disruptable) + module.deactivate() + + cell.use(cost*10) + return 1 + +/obj/item/weapon/rig/update_icon(var/update_mob_icon) + + cut_overlays() + if(!mob_icon || update_mob_icon) + var/species_icon = default_mob_icon + // Since setting mob_icon will override the species checks in + // update_inv_wear_suit(), handle species checks here. + if(wearer && LAZYACCESS(sprite_sheets, wearer.species.get_bodytype(wearer))) + species_icon = sprite_sheets[wearer.species.get_bodytype(wearer)] + mob_icon = icon(icon = species_icon, icon_state = "[icon_state]") + + if(installed_modules.len) + for(var/obj/item/rig_module/module in installed_modules) + if(module.suit_overlay) + chest.add_overlay(image(module.suit_overlay_icon, icon_state = "[module.suit_overlay]", dir = SOUTH)) + + if(wearer) + wearer.update_inv_shoes() + wearer.update_inv_gloves() + wearer.update_inv_head() + wearer.update_inv_wear_suit() + wearer.update_inv_back() + return + +/obj/item/weapon/rig/proc/check_suit_access(var/mob/living/carbon/human/user, var/do_message = TRUE) + + if(!security_check_enabled) + return 1 + + if(istype(user)) + if(!canremove) + return 1 + if(malfunction_check(user)) + return 0 + if(user.back != src && user.belt != src) + return 0 + else if(!src.allowed(user)) + if(do_message) + to_chat(user, "Unauthorized user. Access denied.") + return 0 + + else if(!ai_override_enabled) + if(do_message) + to_chat(user, "Synthetic access disabled. Please consult hardware provider.") + return 0 + + return 1 + +/obj/item/weapon/rig/proc/notify_ai(var/message) + for(var/obj/item/rig_module/ai_container/module in installed_modules) + if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) + to_chat(module.integrated_ai, "[message]") + . = 1 + +/obj/item/weapon/rig/equipped(mob/living/carbon/human/M) + ..() + + if(istype(M.back, /obj/item/weapon/rig) && istype(M.belt, /obj/item/weapon/rig)) + to_chat(M, "You try to put on the [src], but it won't fit.") + if(M && (M.back == src || M.belt == src)) + if(!M.unEquip(src)) + return + src.forceMove(get_turf(src)) + return + + if(seal_delay > 0 && istype(M) && (M.back == src || M.belt == src)) + M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") + if(!do_after(M,seal_delay)) + if(M && (M.back == src || M.belt == src)) + if(!M.unEquip(src)) + return + src.forceMove(get_turf(src)) + return + + if(istype(M) && (M.back == src || M.belt == src)) + M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") + wearer = M + wearer.wearing_rig = src + update_icon() + +/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/forced = FALSE) + + if((sealing || !cell || !cell.charge) && !forced) + return + + if((!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) && !forced) + return + + if((usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) && !forced) // If the usr isn't wearing the suit it's probably an AI. + return + + var/obj/item/check_slot + var/equip_to + var/obj/item/use_obj + + if(!H) + return + + switch(piece) + if("helmet") + equip_to = slot_head + use_obj = helmet + check_slot = H.head + if("gauntlets") + equip_to = slot_gloves + use_obj = gloves + check_slot = H.gloves + if("boots") + equip_to = slot_shoes + use_obj = boots + check_slot = H.shoes + if("chest") + equip_to = slot_wear_suit + use_obj = chest + check_slot = H.wear_suit + + if(use_obj) + if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) + + var/mob/living/carbon/human/holder + + if(use_obj) + holder = use_obj.loc + if(istype(holder)) + if(use_obj && check_slot == use_obj) + to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.") + playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) + use_obj.canremove = TRUE + holder.drop_from_inventory(use_obj) + use_obj.forceMove(get_turf(src)) + use_obj.dropped() + use_obj.canremove = FALSE + use_obj.forceMove(src) + + else if (deploy_mode != ONLY_RETRACT) + if(check_slot && check_slot == use_obj) + return + use_obj.forceMove(H) + if(!H.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) + use_obj.forceMove(src) + if(check_slot) + to_chat(H, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") + return + else + to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") + playsound(src, 'sound/machines/rig/rigservo.ogg', 10, FALSE) + + if(piece == "helmet" && helmet?.light_system == STATIC_LIGHT) + helmet.update_light() + +/obj/item/weapon/rig/proc/deploy(mob/M,var/sealed) + + var/mob/living/carbon/human/H = M + + if(!H || !istype(H)) return + + if(H.back != src && H.belt != src) + return + + if(sealed) + if(H.head) + var/obj/item/garbage = H.head + H.drop_from_inventory(garbage) + H.head = null + qdel(garbage) + + if(H.gloves) + var/obj/item/garbage = H.gloves + H.drop_from_inventory(garbage) + H.gloves = null + qdel(garbage) + + if(H.shoes) + var/obj/item/garbage = H.shoes + H.drop_from_inventory(garbage) + H.shoes = null + qdel(garbage) + + if(H.wear_suit) + var/obj/item/garbage = H.wear_suit + H.drop_from_inventory(garbage) + H.wear_suit = null + qdel(garbage) + + for(var/piece in list("helmet","gauntlets","chest","boots")) + toggle_piece(piece, H, ONLY_DEPLOY) + +/obj/item/weapon/rig/dropped(var/mob/user) + ..() + for(var/piece in list("helmet","gauntlets","chest","boots")) + toggle_piece(piece, user, ONLY_RETRACT) + if(wearer && wearer.wearing_rig == src) + wearer.wearing_rig = null + wearer = null + +//Todo +/obj/item/weapon/rig/proc/malfunction() + return 0 + +/obj/item/weapon/rig/emp_act(severity_class) + //set malfunctioning + if(emp_protection < 30) //for ninjas, really. + malfunctioning += 10 + if(malfunction_delay <= 0) + malfunction_delay = max(malfunction_delay, round(30/severity_class)) + + //drain some charge + if(cell) cell.emp_act(severity_class + 15) + + //possibly damage some modules + take_hit((100/severity_class), "electrical pulse", 1) + +/obj/item/weapon/rig/proc/shock(mob/user) + if (electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here. + spark_system.start() + if(user.stunned) + return 1 + return 0 + +/obj/item/weapon/rig/proc/take_hit(damage, source, is_emp=0) + + if(!installed_modules.len) + return + + var/chance + if(!is_emp) + chance = 2*max(0, damage - (chest? chest.breach_threshold : 0)) + else + //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average. + //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules + chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1) + + if(!prob(chance)) + return + + //deal addition damage to already damaged module first. + //This way the chances of a module being disabled aren't so remote. + var/list/valid_modules = list() + var/list/damaged_modules = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.damage < 2) + valid_modules |= module + if(module.damage > 0) + damaged_modules |= module + + var/obj/item/rig_module/dam_module = null + if(damaged_modules.len) + dam_module = pick(damaged_modules) + else if(valid_modules.len) + dam_module = pick(valid_modules) + + if(!dam_module) return + + dam_module.damage++ + + if(!source) + source = "hit" + + if(wearer) + if(dam_module.damage >= 2) + to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!") + else + to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!") + dam_module.deactivate() + +/obj/item/weapon/rig/proc/malfunction_check(var/mob/living/carbon/human/user) + if(malfunction_delay) + if(offline) + to_chat(user, "The suit is completely unresponsive.") + else + to_chat(user, "ERROR: Hardware fault. Rebooting interface...") + return 1 + return 0 + +/obj/item/weapon/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0) + + if(check_for_ai) + if(!(locate(/obj/item/rig_module/ai_container) in contents)) + return 0 + var/found_ai + for(var/obj/item/rig_module/ai_container/module in contents) + if(module.damage >= 2) + continue + if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) + found_ai = 1 + break + if(!found_ai) + return 0 + + if(check_user_module) + if(!user || !user.loc || !user.loc.loc) + return 0 + var/obj/item/rig_module/ai_container/module = user.loc.loc + if(!istype(module) || module.damage >= 2) + to_chat(user, "Your host module is unable to interface with the suit.") + return 0 + + if(offline || !cell || !cell.charge || locked_down) + if(user) + to_chat(user, "Your host rig is unpowered and unresponsive.") + return 0 + if(!wearer || (wearer.back != src && wearer.belt != src)) + if(user) + to_chat(user, "Your host rig is not being worn.") + return 0 + if(!wearer.stat && !control_overridden && !ai_override_enabled) + if(user) + to_chat(user, "You are locked out of the suit servo controller.") + return 0 + return 1 + +/obj/item/weapon/rig/proc/force_rest(var/mob/user) + if(!ai_can_move_suit(user, check_user_module = 1)) + return + wearer.lay_down() + to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].") + +/obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user) + + // Why is all this shit in client/Move()? Who knows? + if(world.time < wearer_move_delay) + return + + if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) + return + + //This is sota the goto stop mobs from moving var + if(wearer.transforming || !wearer.canmove) + return + + if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0)) + if(!wearer.Process_Spacemove(0)) + return 0 + + if(malfunctioning) + direction = pick(cardinal) + + // Inside an object, tell it we moved. + if(isobj(wearer.loc) || ismob(wearer.loc)) + var/atom/O = wearer.loc + return O.relaymove(wearer, direction) + + if(isturf(wearer.loc)) + if(wearer.restrained())//Why being pulled while cuffed prevents you from moving + for(var/mob/M in range(wearer, 1)) + if(M.pulling == wearer) + if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) + to_chat(user, "Your host is restrained! They can't move!") + return 0 + else + M.stop_pulling() + + if(wearer.pinned.len) + to_chat(src, "Your host is pinned to a wall by [wearer.pinned[1]]!") + return 0 + + // AIs are a bit slower than regular and ignore move intent. + wearer_move_delay = world.time + ai_controlled_move_delay + + if(istype(wearer.buckled, /obj/vehicle)) + //manually set move_delay for vehicles so we don't inherit any mob movement penalties + //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm + wearer_move_delay = world.time + return wearer.buckled.relaymove(wearer, direction) + + if(istype(wearer.machine, /obj/machinery)) + if(wearer.machine.relaymove(wearer, direction)) + return + + if(wearer.pulledby || wearer.buckled) // Wheelchair driving! + if(istype(wearer.loc, /turf/space)) + return // No wheelchair driving in space + if(istype(wearer.pulledby, /obj/structure/bed/chair/wheelchair)) + return wearer.pulledby.relaymove(wearer, direction) + else if(istype(wearer.buckled, /obj/structure/bed/chair/wheelchair)) + if(ishuman(wearer.buckled)) + var/obj/item/organ/external/l_hand = wearer.get_organ("l_hand") + var/obj/item/organ/external/r_hand = wearer.get_organ("r_hand") + if((!l_hand || (l_hand.status & ORGAN_DESTROYED)) && (!r_hand || (r_hand.status & ORGAN_DESTROYED))) + return // No hands to drive your chair? Tough luck! + wearer_move_delay += 2 + return wearer.buckled.relaymove(wearer,direction) + + cell.use(200) //Arbitrary, TODO + wearer.Move(get_step(get_turf(wearer),direction),direction) + +// This returns the rig if you are contained inside one, but not if you are wearing it +/atom/proc/get_rig() + if(loc) + return loc.get_rig() + return null + +/obj/item/weapon/rig/get_rig() + return src + +/mob/living/carbon/human/get_rig() + if(istype(back, /obj/item/weapon/rig)) + return back + else if(istype(belt, /obj/item/weapon/rig)) + return belt + else + return null + +//Boot animation screen objects +/obj/screen/rig_booting + screen_loc = "1,1" + icon = 'icons/obj/rig_boot.dmi' + icon_state = "" + layer = SCREEN_LAYER + plane = PLANE_FULLSCREEN + mouse_opacity = 0 + alpha = 20 //Animated up when loading + +#undef ONLY_DEPLOY +#undef ONLY_RETRACT +#undef SEAL_DELAY diff --git a/code/modules/clothing/spacesuits/rig/rig_attackby.dm b/code/modules/clothing/spacesuits/rig/rig_attackby.dm index 9ce406fccf8..e6c48734dfc 100644 --- a/code/modules/clothing/spacesuits/rig/rig_attackby.dm +++ b/code/modules/clothing/spacesuits/rig/rig_attackby.dm @@ -7,7 +7,7 @@ return // Pass repair items on to the chestpiece. - if(chest && (istype(W,/obj/item/stack/material) || istype(W, /obj/item/weapon/weldingtool))) + if(chest && (istype(W,/obj/item/stack/material) || W.has_tool_quality(TOOL_WELDER))) return chest.attackby(W,user) // Lock or unlock the access panel. @@ -30,7 +30,7 @@ to_chat(user, "You [locked ? "lock" : "unlock"] \the [src] access panel.") return - else if(W.is_crowbar()) + else if(W.has_tool_quality(TOOL_CROWBAR)) if(!open && locked) to_chat(user, "The access panel is locked shut.") return @@ -41,7 +41,7 @@ if(open) // Hacking. - if(W.is_wirecutter() || istype(W, /obj/item/device/multitool)) + if(W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) if(open) wires.Interact(user) else @@ -102,7 +102,7 @@ src.cell = W return - else if(W.is_wrench()) + else if(W.has_tool_quality(TOOL_WRENCH)) if(!air_supply) to_chat(user, "There is no tank to remove.") @@ -116,7 +116,7 @@ air_supply = null return - else if(W.is_screwdriver()) + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) var/list/current_mounts = list() if(cell) current_mounts += "cell" diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm index b28d93bc554..3cc91f6249a 100644 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -223,11 +223,11 @@ if(!istype(module)) selected_module = null - to_chat(usr, "Primary system is now: deselected.") + to_chat(usr, span_blue("Primary system is now: deselected.")) return selected_module = module - to_chat(usr, "Primary system is now: [selected_module.interface_name].") + to_chat(usr, span_blue("Primary system is now: [selected_module.interface_name].")) /obj/item/weapon/rig/verb/toggle_module() @@ -261,10 +261,10 @@ return if(module.active) - to_chat(usr, "You attempt to deactivate \the [module.interface_name].") + to_chat(usr, span_blue("You attempt to deactivate \the [module.interface_name].")) module.deactivate() else - to_chat(usr, "You attempt to activate \the [module.interface_name].") + to_chat(usr, span_blue("You attempt to activate \the [module.interface_name].")) module.activate() /obj/item/weapon/rig/verb/engage_module() @@ -298,5 +298,5 @@ if(!istype(module)) return - to_chat(usr, "You attempt to engage the [module.interface_name].") - module.engage() \ No newline at end of file + to_chat(usr, span_blue("You attempt to engage the [module.interface_name].")) + module.engage() diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index 9ee31aeb602..a07ebe19a9b 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -54,11 +54,11 @@ if(camera.status == TRUE) camera.set_status(FALSE) - to_chat(usr, "Camera deactivated.") + to_chat(usr, span_blue("Camera deactivated.")) else camera.set_status(TRUE) camera.c_tag = usr.name - to_chat(usr, "User scanned as [camera.c_tag]. Camera activated.") + to_chat(usr, span_blue("User scanned as [camera.c_tag]. Camera activated.")) /obj/item/clothing/head/helmet/space/examine(mob/user) . = ..() diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index 7ae8a87f683..b49ad6098f4 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -1,137 +1,137 @@ -//Regular syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate - name = "red space helmet" - icon_state = "syndicate" - desc = "A crimson helmet sporting clean lines and durable plating. Engineered to look menacing." - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/space/syndicate - name = "red space suit" - icon_state = "syndicate" - desc = "A crimson spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) - slowdown = 0.5 - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0.6 - -//Green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green - name = "green space helmet" - desc = "A green helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-green" - -/obj/item/clothing/suit/space/syndicate/green - name = "green space suit" - desc = "A green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-green" - -//Dark green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green/dark - name = "dark green space helmet" - desc = "A dark green helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-green-dark" - -/obj/item/clothing/suit/space/syndicate/green/dark - name = "dark green space suit" - desc = "A dark green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-green-dark" - -//Orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/orange - name = "orange space helmet" - desc = "An orange helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-orange" - -/obj/item/clothing/suit/space/syndicate/orange - name = "orange space suit" - desc = "An orange spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-orange" - -//Blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/blue - name = "blue space helmet" - desc = "A blue helmet sporting clean lines and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-blue" - -/obj/item/clothing/suit/space/syndicate/blue - name = "blue space suit" - desc = "A blue spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-blue" - -//Black syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black - name = "black space helmet" - desc = "A black helmet sporting durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black - name = "black space suit" - desc = "A black spacesuit sporting durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black" - -//Black-green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/green - name = "black and green space helmet" - desc = "A black helmet sporting a single green stripe and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black-green" - -/obj/item/clothing/suit/space/syndicate/black/green - name = "black and green space suit" - desc = "A black spacesuit sporting green stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-green" - -//Black-blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/blue - name = "black and blue space helmet" - desc = "A black helmet sporting a single blue stripe and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black-blue" - -/obj/item/clothing/suit/space/syndicate/black/blue - name = "black and blue space suit" - desc = "A black spacesuit sporting blue stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-blue" - -//Black medical syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/med - name = "black medical space helmet" - desc = "A black helmet sporting a medical cross and durable plating. Hopefully the wearer abides by space geneva." - icon_state = "syndicate-helm-black-med" - -/obj/item/clothing/suit/space/syndicate/black/med - name = "black medical space suit" - desc = "A black spacesuit sporting a medical cross and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-med" - -//Black-orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/orange - name = "black and orange space helmet" - icon_state = "syndicate-helm-black-orange" - -/obj/item/clothing/suit/space/syndicate/black/orange - name = "black and orange space suit" - desc = "A black spacesuit sporting orange stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-orange" - -//Black-red syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/red - name = "black and red space helmet" - desc = "A black helmet sporting a single red stripe and durable plating. Engineered to look menacing." - icon_state = "syndicate-helm-black-red" - -/obj/item/clothing/suit/space/syndicate/black/red - name = "black and red space suit" - desc = "A black spacesuit sporting red stripes and durable plating. Robust, reliable, and slightly suspicious." - icon_state = "syndicate-black-red" - -//Black with yellow/red engineering syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/engie - name = "black engineering space helmet" - desc = "A black helmet sporting red and yellow stripes and durable plating. Engineered to look well... engineering-ish." - icon_state = "syndicate-helm-black-engie" - -/obj/item/clothing/suit/space/syndicate/black/engie - name = "black engineering space suit" - desc = "A black spacesuit sporting red and yellow stripes and durable plating. Robust, reliable, and slightly suspicious." +//Regular syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate + name = "red space helmet" + icon_state = "syndicate" + desc = "A crimson helmet sporting clean lines and durable plating. Engineered to look menacing." + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/space/syndicate + name = "red space suit" + icon_state = "syndicate" + desc = "A crimson spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen) + slowdown = 0.5 + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0.6 + +//Green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green + name = "green space helmet" + desc = "A green helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-green" + +/obj/item/clothing/suit/space/syndicate/green + name = "green space suit" + desc = "A green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-green" + +//Dark green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green/dark + name = "dark green space helmet" + desc = "A dark green helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-green-dark" + +/obj/item/clothing/suit/space/syndicate/green/dark + name = "dark green space suit" + desc = "A dark green spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-green-dark" + +//Orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/orange + name = "orange space helmet" + desc = "An orange helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-orange" + +/obj/item/clothing/suit/space/syndicate/orange + name = "orange space suit" + desc = "An orange spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-orange" + +//Blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/blue + name = "blue space helmet" + desc = "A blue helmet sporting clean lines and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-blue" + +/obj/item/clothing/suit/space/syndicate/blue + name = "blue space suit" + desc = "A blue spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-blue" + +//Black syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black + name = "black space helmet" + desc = "A black helmet sporting durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black + name = "black space suit" + desc = "A black spacesuit sporting durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black" + +//Black-green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/green + name = "black and green space helmet" + desc = "A black helmet sporting a single green stripe and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black-green" + +/obj/item/clothing/suit/space/syndicate/black/green + name = "black and green space suit" + desc = "A black spacesuit sporting green stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-green" + +//Black-blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/blue + name = "black and blue space helmet" + desc = "A black helmet sporting a single blue stripe and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black-blue" + +/obj/item/clothing/suit/space/syndicate/black/blue + name = "black and blue space suit" + desc = "A black spacesuit sporting blue stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-blue" + +//Black medical syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/med + name = "black medical space helmet" + desc = "A black helmet sporting a medical cross and durable plating. Hopefully the wearer abides by space geneva." + icon_state = "syndicate-helm-black-med" + +/obj/item/clothing/suit/space/syndicate/black/med + name = "black medical space suit" + desc = "A black spacesuit sporting a medical cross and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-med" + +//Black-orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/orange + name = "black and orange space helmet" + icon_state = "syndicate-helm-black-orange" + +/obj/item/clothing/suit/space/syndicate/black/orange + name = "black and orange space suit" + desc = "A black spacesuit sporting orange stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-orange" + +//Black-red syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/red + name = "black and red space helmet" + desc = "A black helmet sporting a single red stripe and durable plating. Engineered to look menacing." + icon_state = "syndicate-helm-black-red" + +/obj/item/clothing/suit/space/syndicate/black/red + name = "black and red space suit" + desc = "A black spacesuit sporting red stripes and durable plating. Robust, reliable, and slightly suspicious." + icon_state = "syndicate-black-red" + +//Black with yellow/red engineering syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/engie + name = "black engineering space helmet" + desc = "A black helmet sporting red and yellow stripes and durable plating. Engineered to look well... engineering-ish." + icon_state = "syndicate-helm-black-engie" + +/obj/item/clothing/suit/space/syndicate/black/engie + name = "black engineering space suit" + desc = "A black spacesuit sporting red and yellow stripes and durable plating. Robust, reliable, and slightly suspicious." icon_state = "syndicate-black-engie" \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/void/ert_vr.dm b/code/modules/clothing/spacesuits/void/ert_vr.dm index 7f253ddfe83..8b147a4dc33 100644 --- a/code/modules/clothing/spacesuits/void/ert_vr.dm +++ b/code/modules/clothing/spacesuits/void/ert_vr.dm @@ -77,7 +77,7 @@ to_chat(user, "You cannot modify \the [src] while it is being worn.") return - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(boots || tank || cooler) var/choice = tgui_input_list(usr, "What component would you like to remove?", "Remove Component", list(boots,tank,cooler)) if(!choice) return diff --git a/code/modules/clothing/spacesuits/void/event_vr.dm b/code/modules/clothing/spacesuits/void/event_vr.dm index f543aa48c6d..3edd3a83f0e 100644 --- a/code/modules/clothing/spacesuits/void/event_vr.dm +++ b/code/modules/clothing/spacesuits/void/event_vr.dm @@ -234,7 +234,7 @@ /obj/item/clothing/suit/space/void/altevian_heartbreaker name = "\improper heartbreaker voidsuit" - desc = "The altevians' newest iteration of their armored suits. This one is tailored for zero-g environments, and while it can function in an area with gravity, it'll put a strain on even the most athletic of individuals." + desc = "The altevians' newest iteration of their armored suits. This one is tailored for zero-g environments, and while it can still be worn in an area with gravity, it'll put a strain on even the most athletic of individuals." icon = 'icons/inventory/suit/item_vr_altevian.dmi' default_worn_icon = 'icons/inventory/suit/mob_vr_altevian.dmi' @@ -248,7 +248,7 @@ /obj/item/clothing/head/helmet/space/void/altevian_heartbreaker name = "\improper heartbreaker helmet" - desc = "The altevians' newest iteration of their armored suits. This one is tailored for zero-g environments, and while it can function in an area with gravity, it'll put a strain on even the most athletic of individuals." + desc = "The altevians' newest iteration of their armored suits. This one is tailored for zero-g environments, and while it can still be worn in an area with gravity, it'll put a strain on even the most athletic of individuals." icon = 'icons/inventory/head/item_vr_altevian.dmi' default_worn_icon = 'icons/inventory/head/mob_vr_altevian.dmi' @@ -286,4 +286,4 @@ armor = list("melee" = 50, "bullet" = 15, "laser" = 15, "energy" = 25, "bomb" = 45, "bio" = 100, "rad" = 80) sprite_sheets = ALL_VR_SPRITE_SHEETS_HEAD_MOB - sprite_sheets_obj = ALL_VR_SPRITE_SHEETS_HEAD_ITEM \ No newline at end of file + sprite_sheets_obj = ALL_VR_SPRITE_SHEETS_HEAD_ITEM diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index 57a713962c8..7d421348cc6 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -241,7 +241,7 @@ to_chat(user, "You cannot modify \the [src] while it is being worn.") return - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(helmet || boots || tank) var/choice = tgui_input_list(usr, "What component would you like to remove?", "Remove Component", list(helmet,boots,tank,cooler)) if(!choice) return diff --git a/code/modules/clothing/spacesuits/void/void_vr.dm b/code/modules/clothing/spacesuits/void/void_vr.dm index 2b9fc754a38..9222a4ca9c9 100644 --- a/code/modules/clothing/spacesuits/void/void_vr.dm +++ b/code/modules/clothing/spacesuits/void/void_vr.dm @@ -10,7 +10,7 @@ /obj/item/clothing/suit/space/void species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_RAPALA, SPECIES_VASILISSAN, SPECIES_ALRAUNE, SPECIES_PROMETHEAN, SPECIES_XENOCHIMERA) - + /obj/item/clothing/head/helmet/space/void/heck name = "\improper H.E.C.K. helmet" desc = "Hostile Environiment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from \[REDACTED\]. It wasn't enough for its last owner." @@ -114,7 +114,7 @@ to_chat(user, "You cannot modify \the [src] while it is being worn.") return - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) if(boots || tank || cooler) var/choice = tgui_input_list(usr, "What component would you like to remove?", "Remove Component", list(boots,tank,cooler)) if(!choice) return @@ -151,4 +151,3 @@ default_worn_icon = 'icons/inventory/head/mob_vr.dmi' sprite_sheets = ALL_VR_SPRITE_SHEETS_HEAD_MOB sprite_sheets_obj = null - \ No newline at end of file diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index ec0b54befa5..12ed13f6010 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,642 +1,642 @@ -/obj/item/clothing/suit/armor - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - item_flags = THICKMATERIAL - - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) - if(..()) //This will only run if no other problems occured when equiping. - for(var/obj/item/clothing/I in list(H.gloves, H.shoes)) - if(I && (src.body_parts_covered & ARMS && I.body_parts_covered & ARMS) ) - to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") - return 0 - if(I && (src.body_parts_covered & LEGS && I.body_parts_covered & LEGS) ) - to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") - return 0 - return 1 - -/obj/item/clothing/suit/armor/vest - name = "armor" - desc = "An armored vest that protects against some damage." - icon_state = "armor" - blood_overlay_type = "armor" - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/vest/alt - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a NanoTrasen corporate badge." - icon_state = "armoralt" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - -/obj/item/clothing/suit/armor/vest/security - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a corporate badge." - icon_state = "armorsec" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - -/obj/item/clothing/suit/armor/riot - name = "riot vest" - desc = "A vest with heavy padding to protect against melee attacks." - icon_state = "riot" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.5 - -/obj/item/clothing/suit/armor/riot/alt - icon_state = "riot_new" - item_state_slots = list(slot_r_hand_str = "riot_new", slot_l_hand_str = "riot_new") - -/obj/item/clothing/suit/armor/bulletproof - name = "bullet resistant vest" - desc = "A vest that excels in protecting the wearer against high-velocity solid projectiles." - icon_state = "bulletproof" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - blood_overlay_type = "armor" - slowdown = 0.5 - armor = list(melee = 10, bullet = 80, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.7 - -/obj/item/clothing/suit/armor/bulletproof/alt - icon_state = "bulletproof_new" - item_state_slots = list(slot_r_hand_str = "bulletproof_new", slot_l_hand_str = "bulletproof_new") - blood_overlay_type = "armor" - -/obj/item/clothing/suit/armor/laserproof - name = "ablative armor vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - blood_overlay_type = "armor" - slowdown = 0.5 - armor = list(melee = 10, bullet = 10, laser = 80, energy = 50, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.1 - -/obj/item/clothing/suit/armor/laserproof/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam)) - var/obj/item/projectile/P = damage_source - - if(P.reflected) // Can't reflect twice - return ..() - - var/reflectchance = 40 - round(damage/3) - if(!(def_zone in list(BP_TORSO, BP_GROIN))) - reflectchance /= 2 - if(P.starting && prob(reflectchance)) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(user) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, user) - P.reflected = 1 - - return PROJECTILE_CONTINUE // complete projectile permutation - -/obj/item/clothing/suit/armor/combat - name = "combat vest" - desc = "A vest that protects the wearer from several common types of weaponry." - icon_state = "combat" - blood_overlay_type = "armor" - slowdown = 0.5 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/armor/tactical - name = "tactical armor" - desc = "A suit of armor most often used by Special Weapons and Tactics squads. Includes padded vest with pockets along with shoulder and kneeguards." - icon_state = "swatarmor" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - slowdown = 1 - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - siemens_coefficient = 0.7 - -/obj/item/clothing/suit/armor/swat - name = "swat suit" - desc = "A heavily armored suit that protects against moderate damage. Used in special operations." - icon_state = "deathsquad" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - item_flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) - slowdown = 1 - w_class = ITEMSIZE_HUGE - armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 100) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - min_pressure_protection = 0 * ONE_ATMOSPHERE - max_pressure_protection = 20* ONE_ATMOSPHERE - siemens_coefficient = 0.6 - - -/obj/item/clothing/suit/armor/swat/officer - name = "officer jacket" - desc = "An armored jacket used in special operations." - icon_state = "detective" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - blood_overlay_type = "coat" - flags_inv = 0 - body_parts_covered = UPPER_TORSO|ARMS - - -/obj/item/clothing/suit/armor/det_suit - name = "armor" - desc = "An armored vest with a detective's badge on it." - icon_state = "detective-armor" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - - -//Reactive armor -//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) -/obj/item/clothing/suit/armor/reactive - name = "Reactive Teleport Armor" - desc = "Someone separated our Research Director from their own head!" - var/active = 0.0 - icon_state = "reactiveoff" - item_state_slots = list(slot_r_hand_str = "armor_reflec_old", slot_l_hand_str = "armor_reflec_old") - blood_overlay_type = "armor" - slowdown = 1 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(prob(50)) - user.visible_message("The reactive teleport system flings [user] clear of the attack!") - var/list/turfs = new/list() - for(var/turf/T in orange(6, user)) - if(istype(T,/turf/space)) continue - if(T.density) continue - if(T.x>world.maxx-6 || T.x<6) continue - if(T.y>world.maxy-6 || T.y<6) continue - turfs += T - if(!turfs.len) turfs += pick(/turf in orange(6)) - var/turf/picked = pick(turfs) - if(!isturf(picked)) return - - var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(src, "sparks", 50, 1) - - user.loc = picked - return PROJECTILE_FORCE_MISS - return 0 - -/obj/item/clothing/suit/armor/reactive/attack_self(mob/user as mob) - active = !( active ) - if (active) - to_chat(user, "The reactive armor is now active.") - icon_state = "reactive" - else - to_chat(user, "The reactive armor is now inactive.") - icon_state = "reactiveoff" - add_fingerprint(user) - return - -/obj/item/clothing/suit/armor/reactive/emp_act(severity) - active = 0 - icon_state = "reactiveoff" - ..() - -// Alien armor has a chance to completely block attacks. -/obj/item/clothing/suit/armor/alien - name = "alien enhancement vest" - desc = "It's a strange piece of what appears to be armor. It looks very light and agile. Strangely enough it seems to have been designed for a humanoid shape." - description_info = "It has a 20% chance to completely nullify an incoming attack, and the wearer moves slightly faster." - icon_state = "alien_speed" - blood_overlay_type = "armor" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - slowdown = -1 - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) - siemens_coefficient = 0.4 - var/block_chance = 20 - -/obj/item/clothing/suit/armor/alien/tank - name = "alien protection suit" - desc = "It's really resilient yet lightweight, so it's probably meant to be armor. Strangely enough it seems to have been designed for a humanoid shape." - description_info = "It has a 40% chance to completely nullify an incoming attack." - icon_state = "alien_tank" - slowdown = 0 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) - block_chance = 40 - -/obj/item/clothing/suit/armor/alien/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(prob(block_chance)) - user.visible_message("\The [src] completely absorbs [attack_text]!") - return TRUE - return FALSE - -//Non-hardsuit ERT armor. -/obj/item/clothing/suit/armor/vest/ert - name = "emergency response team armor" - desc = "A set of armor worn by members of the Emergency Response Team." - icon_state = "ertarmor_cmd" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 20, bio = 0, rad = 0) - -//Commander -/obj/item/clothing/suit/armor/vest/ert/command - name = "emergency response team commander armor" - desc = "A set of armor worn by the commander of an Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/suit/armor/vest/ert/security - name = "emergency response team security armor" - desc = "A set of armor worn by security members of the Emergency Response Team. Has red highlights." - icon_state = "ertarmor_sec" - -//Engineer -/obj/item/clothing/suit/armor/vest/ert/engineer - name = "emergency response team engineer armor" - desc = "A set of armor worn by engineering members of the Emergency Response Team. Has orange highlights." - icon_state = "ertarmor_eng" - -//Medical -/obj/item/clothing/suit/armor/vest/ert/medical - name = "emergency response team medical armor" - desc = "A set of armor worn by medical members of the Emergency Response Team. Has blue and white highlights." - icon_state = "ertarmor_med" - -//New Vests -/obj/item/clothing/suit/storage/vest - name = "armor vest" - desc = "A simple kevlar plate carrier." - icon_state = "kvest" - blood_overlay_type = "armor" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) - allowed = list(/obj/item/weapon/gun,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) - - body_parts_covered = UPPER_TORSO|LOWER_TORSO - item_flags = THICKMATERIAL - - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0.6 - -/obj/item/clothing/suit/storage/vest/officer - name = "officer armor vest" - desc = "A simple kevlar plate carrier. This one has a security holobadge clipped to the chest." - icon_state = "officervest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "officervest_badge" - icon_nobadge = "officervest_nobadge" - -/obj/item/clothing/suit/storage/vest/warden - name = "warden armor vest" - desc = "A simple kevlar plate carrier. This one has a silver badge clipped to the chest." - icon_state = "wardenvest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "wardenvest_badge" - icon_nobadge = "wardenvest_nobadge" - -/obj/item/clothing/suit/storage/vest/wardencoat - name = "Warden's jacket" - desc = "An armoured jacket with silver rank pips and livery." - icon_state = "warden_jacket" - blood_overlay_type = "suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - flags_inv = HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/storage/vest/wardencoat/alt - name = "Warden's jacket" - desc = "An armoured jacket with silver rank pips and livery." - icon_state = "warden_alt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - -/obj/item/clothing/suit/storage/vest/hos - name = "head of security armor vest" - desc = "A simple kevlar plate carrier. This one has a gold badge clipped to the chest." - icon_state = "hosvest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "hosvest_badge" - icon_nobadge = "hosvest_nobadge" - -/obj/item/clothing/suit/storage/vest/hoscoat - name = "armored coat" - desc = "A greatcoat enhanced with a special alloy for some protection and style." - icon_state = "hos" - blood_overlay_type = "suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - flags_inv = HIDETIE|HIDEHOLSTER - -//Jensen cosplay gear -/obj/item/clothing/suit/storage/vest/hoscoat/jensen - name = "armored trenchcoat" - desc = "A trenchcoat augmented with a special alloy for some protection and style." - icon_state = "hostrench" - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/vest/pcrc - name = "PCRC armor vest" - desc = "A simple kevlar plate carrier belonging to Proxima Centauri Risk Control. This one has a PCRC crest clipped to the chest." - icon_state = "pcrcvest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "pcrcvest_badge" - icon_nobadge = "pcrcvest_nobadge" - -/obj/item/clothing/suit/storage/vest/solgov/hedberg - name = "Hedberg-Hammarstrom armor vest" - desc = "A simple kevlar plate carrier belonging to Hedberg-Hammarstrom. The company logo is clearly visible." - icon_state = "secwebvest" - -/obj/item/clothing/suit/storage/vest/solgov - name = "\improper Solar Confederate Government armored vest" - desc = "A synthetic armor vest. This one is marked with the crest of the Solar Confederate Government." - icon_state = "solvest" - armor = list(melee = 40, bullet = 40, laser = 40, energy = 25, bomb = 30, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/vest/solgov/heavy - name = "\improper Solar Confederate Government heavy armored vest" - desc = "A synthetic armor vest with Solar Confederate Government printed in distinctive blue lettering on the chest. This one has added webbing and ballistic plates." - icon_state = "solwebvest" - -/obj/item/clothing/suit/storage/vest/solgov/command - name = "command heavy armored vest" - desc = "A synthetic armor vest with Solar Confederate Government printed in detailed gold lettering on the chest. This one has added webbing and ballistic plates." - icon_state = "comwebvest" - -/obj/item/clothing/suit/storage/vest/tactical //crack at a more balanced mid-range armor, minor improvements over standard vests, with the idea "modern" combat armor would focus on energy weapon protection. - name = "tactical armored vest" - desc = "A heavy armored vest in a fetching tan. It is surprisingly flexible and light, even with the extra webbing and advanced ceramic plates." - icon_state = "tacwebvest" - item_state = "tacwebvest" - armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/vest/heavy/flexitac //a reskin of the above to have a matching armor set - name = "tactical light vest" - desc = "An armored vest made from advanced flexible ceramic plates. It's surprisingly mobile, if a little unfashionable." - icon_state = "flexitac" - item_state = "flexitac" - armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = T0C - 20 - slowdown = 0.5 - -/obj/item/clothing/suit/storage/vest/detective - name = "detective armor vest" - desc = "A simple kevlar plate carrier in a vintage brown, it has a badge clipped to the chest that reads, 'Private investigator'." - icon_state = "detectivevest_nobadge" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - icon_badge = "detectivevest_badge" - icon_nobadge = "detectivevest_nobadge" - -/obj/item/clothing/suit/storage/vest/press - name = "press vest" - icon_state = "pvest" - desc = "A simple kevlar plate carrier. This one has the word 'Press' embroidered on patches on the back and front." - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - allowed = list(/obj/item/device/flashlight,/obj/item/device/taperecorder,/obj/item/weapon/pen,/obj/item/device/camera_film,/obj/item/device/camera,/obj/item/clothing/head/helmet) - -/obj/item/clothing/suit/storage/vest/heavy - name = "heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached." - icon_state = "webvest" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 50, bullet = 40, laser = 40, energy = 25, bomb = 25, bio = 0, rad = 0) - slowdown = 0.5 - -/obj/item/clothing/suit/storage/vest/heavy/officer - name = "officer heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached. This one has a security holobadge clipped to the chest." - icon_state = "officerwebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "officerwebvest_badge" - icon_nobadge = "officerwebvest_nobadge" - -/obj/item/clothing/suit/storage/vest/heavy/warden - name = "warden heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached. This one has a silver badge clipped to the chest." - icon_state = "wardenwebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "wardenwebvest_badge" - icon_nobadge = "wardenwebvest_nobadge" - -/obj/item/clothing/suit/storage/vest/heavy/hos - name = "head of security heavy armor vest" - desc = "A heavy kevlar plate carrier with webbing attached. This one has a gold badge clipped to the chest." - icon_state = "hoswebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "hoswebvest_badge" - icon_nobadge = "hoswebvest_nobadge" - -/obj/item/clothing/suit/storage/vest/heavy/pcrc - name = "PCRC heavy armor vest" - desc = "A heavy kevlar plate carrier belonging to Proxima Centauri Risk Control with webbing attached. This one has a PCRC crest clipped to the chest." - icon_state = "pcrcwebvest_nobadge" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - icon_badge = "pcrcwebvest_badge" - icon_nobadge = "pcrcwebvest_nobadge" - -//Provides the protection of a merc voidsuit, but only covers the chest/groin, and also takes up a suit slot. In exchange it has no slowdown and provides storage. -/obj/item/clothing/suit/storage/vest/heavy/merc - name = "heavy armor vest" - desc = "A high-quality heavy kevlar plate carrier in a fetching tan. The vest is surprisingly flexible, and possibly made of an advanced material." - icon_state = "mercwebvest" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - slowdown = 0 - -//All of the armor below is mostly unused - -/obj/item/clothing/suit/armor/centcomm - name = "CentCom armor" - desc = "A suit that protects against some damage." - icon_state = "centcom" - item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") - w_class = ITEMSIZE_LARGE//bulky item - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/suit/armor/heavy - name = "heavy armor" - desc = "An old military-grade suit of armor. Incredibly robust against brute force damage! However, it offers little protection from energy-based weapons, which, combined with its bulk, makes it woefully obsolete." - icon_state = "heavy" - item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - armor = list(melee = 90, bullet = 80, laser = 10, energy = 10, bomb = 80, bio = 0, rad = 0) - w_class = ITEMSIZE_HUGE // Very bulky, very heavy. - gas_transfer_coefficient = 0.90 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 5 // If you're a tank you're gonna move like a tank. - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 0 - -/obj/item/clothing/suit/armor/tdome - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) - -/obj/item/clothing/suit/armor/tdome/red - name = "Thunderdome suit (red)" - desc = "Reddish armor." - icon_state = "tdred" - siemens_coefficient = 1 - -/obj/item/clothing/suit/armor/tdome/green - name = "Thunderdome suit (green)" - desc = "Pukish armor." - icon_state = "tdgreen" - siemens_coefficient = 1 - -//Modular plate carriers -/obj/item/clothing/suit/armor/pcarrier - name = "plate carrier" - desc = "A lightweight black plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon = 'icons/obj/clothing/modular_armor.dmi' - item_icons = list(slot_wear_suit_str = 'icons/mob/modular_armor.dmi') - icon_state = "pcarrier" - valid_accessory_slots = (\ - ACCESSORY_SLOT_INSIGNIA\ - |ACCESSORY_SLOT_ARMOR_C\ - |ACCESSORY_SLOT_ARMOR_A\ - |ACCESSORY_SLOT_ARMOR_L\ - |ACCESSORY_SLOT_ARMOR_S\ - |ACCESSORY_SLOT_ARMOR_M) - restricted_accessory_slots = (\ - ACCESSORY_SLOT_INSIGNIA\ - |ACCESSORY_SLOT_ARMOR_C\ - |ACCESSORY_SLOT_ARMOR_A\ - |ACCESSORY_SLOT_ARMOR_L\ - |ACCESSORY_SLOT_ARMOR_S\ - |ACCESSORY_SLOT_ARMOR_M) - blood_overlay_type = "armor" - -/obj/item/clothing/suit/armor/pcarrier/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) - if(..()) //This will only run if no other problems occured when equiping. - if(H.gloves) - if(H.gloves.body_parts_covered & ARMS) - for(var/obj/item/clothing/accessory/A in src) - if(A.body_parts_covered & ARMS) - to_chat(H, "You can't wear \the [A] with \the [H.gloves], they're in the way.") - return 0 - if(H.shoes) - if(H.shoes.body_parts_covered & LEGS) - for(var/obj/item/clothing/accessory/A in src) - if(A.body_parts_covered & LEGS) - to_chat(H, "You can't wear \the [A] with \the [H.shoes], they're in the way.") - return 0 - return 1 - -/obj/item/clothing/suit/armor/pcarrier/explorer - name = "explorer suit" - desc = "A lightweight explorer plate carrier. It can be equipped with armor plates, but only protects from the cold on it's own." - icon_state = "explorer" - flags = THICKMATERIAL - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - -/obj/item/clothing/suit/armor/pcarrier/light - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate) - -/obj/item/clothing/suit/armor/pcarrier/light/sol - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag) - -/obj/item/clothing/suit/armor/pcarrier/light/nt - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag/nt) - -/obj/item/clothing/suit/armor/pcarrier/medium - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches) - -/obj/item/clothing/suit/armor/pcarrier/medium/sol - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag) - -/obj/item/clothing/suit/armor/pcarrier/medium/security - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/sec) - -/obj/item/clothing/suit/armor/pcarrier/medium/command - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/com) - -/obj/item/clothing/suit/armor/pcarrier/medium/nt - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/nt) - -/obj/item/clothing/suit/armor/pcarrier/blue - name = "blue plate carrier" - desc = "A lightweight blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_blue" - -/obj/item/clothing/suit/armor/pcarrier/press - name = "light blue plate carrier" - desc = "A lightweight light blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_press" - -/obj/item/clothing/suit/armor/pcarrier/blue/sol - name = "peacekeeper plate carrier" - desc = "A lightweight plate carrier vest in SCG Peacekeeper colors. It can be equipped with armor plates, but provides no protection of its own." - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches/blue, /obj/item/clothing/accessory/armor/armguards/blue, /obj/item/clothing/accessory/armor/tag) - -/obj/item/clothing/suit/armor/pcarrier/green - name = "green plate carrier" - desc = "A lightweight green plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_green" - -/obj/item/clothing/suit/armor/pcarrier/navy - name = "navy plate carrier" - desc = "A lightweight navy blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_navy" - -/obj/item/clothing/suit/armor/pcarrier/tan - name = "tan plate carrier" - desc = "A lightweight tan plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." - icon_state = "pcarrier_tan" - -/obj/item/clothing/suit/armor/pcarrier/laserproof - name = "ablative plate carrier" - desc = "A specialist laser resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." - icon_state = "ablative" - armor = list(melee = 0, bullet = 0, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0) - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof) - -/obj/item/clothing/suit/armor/pcarrier/bulletproof - name = "ballistic plate carrier" - desc = "A specialist bullet resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." - icon_state = "ballistic" - armor = list(melee = 0, bullet = 5, laser = 0, energy = 0, bomb = 5, bio = 0, rad = 0) - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof) - -/obj/item/clothing/suit/armor/pcarrier/riot - name = "riot plate carrier" - desc = "A specialist melee resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." - icon_state = "riot" - armor = list(melee = 5, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot) - -/obj/item/clothing/suit/armor/pcarrier/explorer/light - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/explorer, /obj/item/clothing/accessory/armor/armguards/explorer, /obj/item/clothing/accessory/armor/legguards/explorer, /obj/item/clothing/accessory/storage/pouches/green) - -/obj/item/clothing/suit/armor/pcarrier/tan/tactical - name = "tactical plate carrier" - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/tactical, /obj/item/clothing/accessory/storage/pouches/large/tan) - -/obj/item/clothing/suit/armor/pcarrier/merc - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/merc, /obj/item/clothing/accessory/armor/armguards/merc, /obj/item/clothing/accessory/armor/legguards/merc, /obj/item/clothing/accessory/storage/pouches/large) - -/obj/item/clothing/suit/armor/pcarrier/laserproof/full - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof, /obj/item/clothing/accessory/armor/armguards/laserproof, /obj/item/clothing/accessory/armor/legguards/laserproof, /obj/item/clothing/accessory/storage/pouches/blue) - -/obj/item/clothing/suit/armor/pcarrier/bulletproof/full - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof, /obj/item/clothing/accessory/armor/armguards/bulletproof, /obj/item/clothing/accessory/armor/legguards/bulletproof, /obj/item/clothing/accessory/storage/pouches) - -/obj/item/clothing/suit/armor/pcarrier/riot/full - starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot, /obj/item/clothing/accessory/armor/armguards/riot, /obj/item/clothing/accessory/armor/legguards/riot, /obj/item/clothing/accessory/storage/pouches) \ No newline at end of file +/obj/item/clothing/suit/armor + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + item_flags = THICKMATERIAL + + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) + if(..()) //This will only run if no other problems occured when equiping. + for(var/obj/item/clothing/I in list(H.gloves, H.shoes)) + if(I && (src.body_parts_covered & ARMS && I.body_parts_covered & ARMS) ) + to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") + return 0 + if(I && (src.body_parts_covered & LEGS && I.body_parts_covered & LEGS) ) + to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") + return 0 + return 1 + +/obj/item/clothing/suit/armor/vest + name = "armor" + desc = "An armored vest that protects against some damage." + icon_state = "armor" + blood_overlay_type = "armor" + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/vest/alt + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a NanoTrasen corporate badge." + icon_state = "armoralt" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + +/obj/item/clothing/suit/armor/vest/security + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a corporate badge." + icon_state = "armorsec" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + +/obj/item/clothing/suit/armor/riot + name = "riot vest" + desc = "A vest with heavy padding to protect against melee attacks." + icon_state = "riot" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 80, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.5 + +/obj/item/clothing/suit/armor/riot/alt + icon_state = "riot_new" + item_state_slots = list(slot_r_hand_str = "riot_new", slot_l_hand_str = "riot_new") + +/obj/item/clothing/suit/armor/bulletproof + name = "bullet resistant vest" + desc = "A vest that excels in protecting the wearer against high-velocity solid projectiles." + icon_state = "bulletproof" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + blood_overlay_type = "armor" + slowdown = 0.5 + armor = list(melee = 10, bullet = 80, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/armor/bulletproof/alt + icon_state = "bulletproof_new" + item_state_slots = list(slot_r_hand_str = "bulletproof_new", slot_l_hand_str = "bulletproof_new") + blood_overlay_type = "armor" + +/obj/item/clothing/suit/armor/laserproof + name = "ablative armor vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + blood_overlay_type = "armor" + slowdown = 0.5 + armor = list(melee = 10, bullet = 10, laser = 80, energy = 50, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.1 + +/obj/item/clothing/suit/armor/laserproof/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam)) + var/obj/item/projectile/P = damage_source + + if(P.reflected) // Can't reflect twice + return ..() + + var/reflectchance = 40 - round(damage/3) + if(!(def_zone in list(BP_TORSO, BP_GROIN))) + reflectchance /= 2 + if(P.starting && prob(reflectchance)) + visible_message("\The [user]'s [src.name] reflects [attack_text]!") + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(user) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, user) + P.reflected = 1 + + return PROJECTILE_CONTINUE // complete projectile permutation + +/obj/item/clothing/suit/armor/combat + name = "combat vest" + desc = "A vest that protects the wearer from several common types of weaponry." + icon_state = "combat" + blood_overlay_type = "armor" + slowdown = 0.5 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 30, bio = 0, rad = 0) + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/armor/tactical + name = "tactical armor" + desc = "A suit of armor most often used by Special Weapons and Tactics squads. Includes padded vest with pockets along with shoulder and kneeguards." + icon_state = "swatarmor" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + slowdown = 1 + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/armor/swat + name = "swat suit" + desc = "A heavily armored suit that protects against moderate damage. Used in special operations." + icon_state = "deathsquad" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + item_flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) + slowdown = 1 + w_class = ITEMSIZE_HUGE + armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 100, rad = 100) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + min_pressure_protection = 0 * ONE_ATMOSPHERE + max_pressure_protection = 20* ONE_ATMOSPHERE + siemens_coefficient = 0.6 + + +/obj/item/clothing/suit/armor/swat/officer + name = "officer jacket" + desc = "An armored jacket used in special operations." + icon_state = "detective" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + blood_overlay_type = "coat" + flags_inv = 0 + body_parts_covered = UPPER_TORSO|ARMS + + +/obj/item/clothing/suit/armor/det_suit + name = "armor" + desc = "An armored vest with a detective's badge on it." + icon_state = "detective-armor" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + + +//Reactive armor +//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) +/obj/item/clothing/suit/armor/reactive + name = "Reactive Teleport Armor" + desc = "Someone separated our Research Director from their own head!" + var/active = 0.0 + icon_state = "reactiveoff" + item_state_slots = list(slot_r_hand_str = "armor_reflec_old", slot_l_hand_str = "armor_reflec_old") + blood_overlay_type = "armor" + slowdown = 1 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(prob(50)) + user.visible_message("The reactive teleport system flings [user] clear of the attack!") + var/list/turfs = new/list() + for(var/turf/T in orange(6, user)) + if(istype(T,/turf/space)) continue + if(T.density) continue + if(T.x>world.maxx-6 || T.x<6) continue + if(T.y>world.maxy-6 || T.y<6) continue + turfs += T + if(!turfs.len) turfs += pick(/turf in orange(6)) + var/turf/picked = pick(turfs) + if(!isturf(picked)) return + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(src, "sparks", 50, 1) + + user.loc = picked + return PROJECTILE_FORCE_MISS + return 0 + +/obj/item/clothing/suit/armor/reactive/attack_self(mob/user as mob) + active = !( active ) + if (active) + to_chat(user, span_blue("The reactive armor is now active.")) + icon_state = "reactive" + else + to_chat(user, span_blue("The reactive armor is now inactive.")) + icon_state = "reactiveoff" + add_fingerprint(user) + return + +/obj/item/clothing/suit/armor/reactive/emp_act(severity) + active = 0 + icon_state = "reactiveoff" + ..() + +// Alien armor has a chance to completely block attacks. +/obj/item/clothing/suit/armor/alien + name = "alien enhancement vest" + desc = "It's a strange piece of what appears to be armor. It looks very light and agile. Strangely enough it seems to have been designed for a humanoid shape." + description_info = "It has a 20% chance to completely nullify an incoming attack, and the wearer moves slightly faster." + icon_state = "alien_speed" + blood_overlay_type = "armor" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + slowdown = -1 + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 0, rad = 40) + siemens_coefficient = 0.4 + var/block_chance = 20 + +/obj/item/clothing/suit/armor/alien/tank + name = "alien protection suit" + desc = "It's really resilient yet lightweight, so it's probably meant to be armor. Strangely enough it seems to have been designed for a humanoid shape." + description_info = "It has a 40% chance to completely nullify an incoming attack." + icon_state = "alien_tank" + slowdown = 0 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 70, bio = 0, rad = 40) + block_chance = 40 + +/obj/item/clothing/suit/armor/alien/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(prob(block_chance)) + user.visible_message("\The [src] completely absorbs [attack_text]!") + return TRUE + return FALSE + +//Non-hardsuit ERT armor. +/obj/item/clothing/suit/armor/vest/ert + name = "emergency response team armor" + desc = "A set of armor worn by members of the Emergency Response Team." + icon_state = "ertarmor_cmd" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 20, bio = 0, rad = 0) + +//Commander +/obj/item/clothing/suit/armor/vest/ert/command + name = "emergency response team commander armor" + desc = "A set of armor worn by the commander of an Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/suit/armor/vest/ert/security + name = "emergency response team security armor" + desc = "A set of armor worn by security members of the Emergency Response Team. Has red highlights." + icon_state = "ertarmor_sec" + +//Engineer +/obj/item/clothing/suit/armor/vest/ert/engineer + name = "emergency response team engineer armor" + desc = "A set of armor worn by engineering members of the Emergency Response Team. Has orange highlights." + icon_state = "ertarmor_eng" + +//Medical +/obj/item/clothing/suit/armor/vest/ert/medical + name = "emergency response team medical armor" + desc = "A set of armor worn by medical members of the Emergency Response Team. Has blue and white highlights." + icon_state = "ertarmor_med" + +//New Vests +/obj/item/clothing/suit/storage/vest + name = "armor vest" + desc = "A simple kevlar plate carrier." + icon_state = "kvest" + blood_overlay_type = "armor" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 0, rad = 0) + allowed = list(/obj/item/weapon/gun,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) + + body_parts_covered = UPPER_TORSO|LOWER_TORSO + item_flags = THICKMATERIAL + + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0.6 + +/obj/item/clothing/suit/storage/vest/officer + name = "officer armor vest" + desc = "A simple kevlar plate carrier. This one has a security holobadge clipped to the chest." + icon_state = "officervest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "officervest_badge" + icon_nobadge = "officervest_nobadge" + +/obj/item/clothing/suit/storage/vest/warden + name = "warden armor vest" + desc = "A simple kevlar plate carrier. This one has a silver badge clipped to the chest." + icon_state = "wardenvest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "wardenvest_badge" + icon_nobadge = "wardenvest_nobadge" + +/obj/item/clothing/suit/storage/vest/wardencoat + name = "Warden's jacket" + desc = "An armoured jacket with silver rank pips and livery." + icon_state = "warden_jacket" + blood_overlay_type = "suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + flags_inv = HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/storage/vest/wardencoat/alt + name = "Warden's jacket" + desc = "An armoured jacket with silver rank pips and livery." + icon_state = "warden_alt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/suit/storage/vest/hos + name = "head of security armor vest" + desc = "A simple kevlar plate carrier. This one has a gold badge clipped to the chest." + icon_state = "hosvest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "hosvest_badge" + icon_nobadge = "hosvest_nobadge" + +/obj/item/clothing/suit/storage/vest/hoscoat + name = "armored coat" + desc = "A greatcoat enhanced with a special alloy for some protection and style." + icon_state = "hos" + blood_overlay_type = "suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + flags_inv = HIDETIE|HIDEHOLSTER + +//Jensen cosplay gear +/obj/item/clothing/suit/storage/vest/hoscoat/jensen + name = "armored trenchcoat" + desc = "A trenchcoat augmented with a special alloy for some protection and style." + icon_state = "hostrench" + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/vest/pcrc + name = "PCRC armor vest" + desc = "A simple kevlar plate carrier belonging to Proxima Centauri Risk Control. This one has a PCRC crest clipped to the chest." + icon_state = "pcrcvest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "pcrcvest_badge" + icon_nobadge = "pcrcvest_nobadge" + +/obj/item/clothing/suit/storage/vest/solgov/hedberg + name = "Hedberg-Hammarstrom armor vest" + desc = "A simple kevlar plate carrier belonging to Hedberg-Hammarstrom. The company logo is clearly visible." + icon_state = "secwebvest" + +/obj/item/clothing/suit/storage/vest/solgov + name = "\improper Solar Confederate Government armored vest" + desc = "A synthetic armor vest. This one is marked with the crest of the Solar Confederate Government." + icon_state = "solvest" + armor = list(melee = 40, bullet = 40, laser = 40, energy = 25, bomb = 30, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/vest/solgov/heavy + name = "\improper Solar Confederate Government heavy armored vest" + desc = "A synthetic armor vest with Solar Confederate Government printed in distinctive blue lettering on the chest. This one has added webbing and ballistic plates." + icon_state = "solwebvest" + +/obj/item/clothing/suit/storage/vest/solgov/command + name = "command heavy armored vest" + desc = "A synthetic armor vest with Solar Confederate Government printed in detailed gold lettering on the chest. This one has added webbing and ballistic plates." + icon_state = "comwebvest" + +/obj/item/clothing/suit/storage/vest/tactical //crack at a more balanced mid-range armor, minor improvements over standard vests, with the idea "modern" combat armor would focus on energy weapon protection. + name = "tactical armored vest" + desc = "A heavy armored vest in a fetching tan. It is surprisingly flexible and light, even with the extra webbing and advanced ceramic plates." + icon_state = "tacwebvest" + item_state = "tacwebvest" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/vest/heavy/flexitac //a reskin of the above to have a matching armor set + name = "tactical light vest" + desc = "An armored vest made from advanced flexible ceramic plates. It's surprisingly mobile, if a little unfashionable." + icon_state = "flexitac" + item_state = "flexitac" + armor = list(melee = 40, bullet = 40, laser = 60, energy = 35, bomb = 30, bio = 0, rad = 0) + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = T0C - 20 + slowdown = 0.5 + +/obj/item/clothing/suit/storage/vest/detective + name = "detective armor vest" + desc = "A simple kevlar plate carrier in a vintage brown, it has a badge clipped to the chest that reads, 'Private investigator'." + icon_state = "detectivevest_nobadge" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + icon_badge = "detectivevest_badge" + icon_nobadge = "detectivevest_nobadge" + +/obj/item/clothing/suit/storage/vest/press + name = "press vest" + icon_state = "pvest" + desc = "A simple kevlar plate carrier. This one has the word 'Press' embroidered on patches on the back and front." + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + allowed = list(/obj/item/device/flashlight,/obj/item/device/taperecorder,/obj/item/weapon/pen,/obj/item/device/camera_film,/obj/item/device/camera,/obj/item/clothing/head/helmet) + +/obj/item/clothing/suit/storage/vest/heavy + name = "heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached." + icon_state = "webvest" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 50, bullet = 40, laser = 40, energy = 25, bomb = 25, bio = 0, rad = 0) + slowdown = 0.5 + +/obj/item/clothing/suit/storage/vest/heavy/officer + name = "officer heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached. This one has a security holobadge clipped to the chest." + icon_state = "officerwebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "officerwebvest_badge" + icon_nobadge = "officerwebvest_nobadge" + +/obj/item/clothing/suit/storage/vest/heavy/warden + name = "warden heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached. This one has a silver badge clipped to the chest." + icon_state = "wardenwebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "wardenwebvest_badge" + icon_nobadge = "wardenwebvest_nobadge" + +/obj/item/clothing/suit/storage/vest/heavy/hos + name = "head of security heavy armor vest" + desc = "A heavy kevlar plate carrier with webbing attached. This one has a gold badge clipped to the chest." + icon_state = "hoswebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "hoswebvest_badge" + icon_nobadge = "hoswebvest_nobadge" + +/obj/item/clothing/suit/storage/vest/heavy/pcrc + name = "PCRC heavy armor vest" + desc = "A heavy kevlar plate carrier belonging to Proxima Centauri Risk Control with webbing attached. This one has a PCRC crest clipped to the chest." + icon_state = "pcrcwebvest_nobadge" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + icon_badge = "pcrcwebvest_badge" + icon_nobadge = "pcrcwebvest_nobadge" + +//Provides the protection of a merc voidsuit, but only covers the chest/groin, and also takes up a suit slot. In exchange it has no slowdown and provides storage. +/obj/item/clothing/suit/storage/vest/heavy/merc + name = "heavy armor vest" + desc = "A high-quality heavy kevlar plate carrier in a fetching tan. The vest is surprisingly flexible, and possibly made of an advanced material." + icon_state = "mercwebvest" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + slowdown = 0 + +//All of the armor below is mostly unused + +/obj/item/clothing/suit/armor/centcomm + name = "CentCom armor" + desc = "A suit that protects against some damage." + icon_state = "centcom" + item_state_slots = list(slot_r_hand_str = "armor", slot_l_hand_str = "armor") + w_class = ITEMSIZE_LARGE//bulky item + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/helmet) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/suit/armor/heavy + name = "heavy armor" + desc = "An old military-grade suit of armor. Incredibly robust against brute force damage! However, it offers little protection from energy-based weapons, which, combined with its bulk, makes it woefully obsolete." + icon_state = "heavy" + item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") + armor = list(melee = 90, bullet = 80, laser = 10, energy = 10, bomb = 80, bio = 0, rad = 0) + w_class = ITEMSIZE_HUGE // Very bulky, very heavy. + gas_transfer_coefficient = 0.90 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 5 // If you're a tank you're gonna move like a tank. + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 0 + +/obj/item/clothing/suit/armor/tdome + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/tdome/red + name = "Thunderdome suit (red)" + desc = "Reddish armor." + icon_state = "tdred" + siemens_coefficient = 1 + +/obj/item/clothing/suit/armor/tdome/green + name = "Thunderdome suit (green)" + desc = "Pukish armor." + icon_state = "tdgreen" + siemens_coefficient = 1 + +//Modular plate carriers +/obj/item/clothing/suit/armor/pcarrier + name = "plate carrier" + desc = "A lightweight black plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon = 'icons/obj/clothing/modular_armor.dmi' + item_icons = list(slot_wear_suit_str = 'icons/mob/modular_armor.dmi') + icon_state = "pcarrier" + valid_accessory_slots = (\ + ACCESSORY_SLOT_INSIGNIA\ + |ACCESSORY_SLOT_ARMOR_C\ + |ACCESSORY_SLOT_ARMOR_A\ + |ACCESSORY_SLOT_ARMOR_L\ + |ACCESSORY_SLOT_ARMOR_S\ + |ACCESSORY_SLOT_ARMOR_M) + restricted_accessory_slots = (\ + ACCESSORY_SLOT_INSIGNIA\ + |ACCESSORY_SLOT_ARMOR_C\ + |ACCESSORY_SLOT_ARMOR_A\ + |ACCESSORY_SLOT_ARMOR_L\ + |ACCESSORY_SLOT_ARMOR_S\ + |ACCESSORY_SLOT_ARMOR_M) + blood_overlay_type = "armor" + +/obj/item/clothing/suit/armor/pcarrier/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = FALSE) + if(..()) //This will only run if no other problems occured when equiping. + if(H.gloves) + if(H.gloves.body_parts_covered & ARMS) + for(var/obj/item/clothing/accessory/A in src) + if(A.body_parts_covered & ARMS) + to_chat(H, "You can't wear \the [A] with \the [H.gloves], they're in the way.") + return 0 + if(H.shoes) + if(H.shoes.body_parts_covered & LEGS) + for(var/obj/item/clothing/accessory/A in src) + if(A.body_parts_covered & LEGS) + to_chat(H, "You can't wear \the [A] with \the [H.shoes], they're in the way.") + return 0 + return 1 + +/obj/item/clothing/suit/armor/pcarrier/explorer + name = "explorer suit" + desc = "A lightweight explorer plate carrier. It can be equipped with armor plates, but only protects from the cold on it's own." + icon_state = "explorer" + flags = THICKMATERIAL + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + +/obj/item/clothing/suit/armor/pcarrier/light + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate) + +/obj/item/clothing/suit/armor/pcarrier/light/sol + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag) + +/obj/item/clothing/suit/armor/pcarrier/light/nt + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate, /obj/item/clothing/accessory/armor/tag/nt) + +/obj/item/clothing/suit/armor/pcarrier/medium + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches) + +/obj/item/clothing/suit/armor/pcarrier/medium/sol + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag) + +/obj/item/clothing/suit/armor/pcarrier/medium/security + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/sec) + +/obj/item/clothing/suit/armor/pcarrier/medium/command + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/com) + +/obj/item/clothing/suit/armor/pcarrier/medium/nt + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches, /obj/item/clothing/accessory/armor/tag/nt) + +/obj/item/clothing/suit/armor/pcarrier/blue + name = "blue plate carrier" + desc = "A lightweight blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_blue" + +/obj/item/clothing/suit/armor/pcarrier/press + name = "light blue plate carrier" + desc = "A lightweight light blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_press" + +/obj/item/clothing/suit/armor/pcarrier/blue/sol + name = "peacekeeper plate carrier" + desc = "A lightweight plate carrier vest in SCG Peacekeeper colors. It can be equipped with armor plates, but provides no protection of its own." + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/medium, /obj/item/clothing/accessory/storage/pouches/blue, /obj/item/clothing/accessory/armor/armguards/blue, /obj/item/clothing/accessory/armor/tag) + +/obj/item/clothing/suit/armor/pcarrier/green + name = "green plate carrier" + desc = "A lightweight green plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_green" + +/obj/item/clothing/suit/armor/pcarrier/navy + name = "navy plate carrier" + desc = "A lightweight navy blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_navy" + +/obj/item/clothing/suit/armor/pcarrier/tan + name = "tan plate carrier" + desc = "A lightweight tan plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_tan" + +/obj/item/clothing/suit/armor/pcarrier/laserproof + name = "ablative plate carrier" + desc = "A specialist laser resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." + icon_state = "ablative" + armor = list(melee = 0, bullet = 0, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0) + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof) + +/obj/item/clothing/suit/armor/pcarrier/bulletproof + name = "ballistic plate carrier" + desc = "A specialist bullet resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." + icon_state = "ballistic" + armor = list(melee = 0, bullet = 5, laser = 0, energy = 0, bomb = 5, bio = 0, rad = 0) + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof) + +/obj/item/clothing/suit/armor/pcarrier/riot + name = "riot plate carrier" + desc = "A specialist melee resistant plate carrier. It can be equipped with armour plates, but provides very little protection of its own." + icon_state = "riot" + armor = list(melee = 5, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot) + +/obj/item/clothing/suit/armor/pcarrier/explorer/light + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/explorer, /obj/item/clothing/accessory/armor/armguards/explorer, /obj/item/clothing/accessory/armor/legguards/explorer, /obj/item/clothing/accessory/storage/pouches/green) + +/obj/item/clothing/suit/armor/pcarrier/tan/tactical + name = "tactical plate carrier" + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/tactical, /obj/item/clothing/accessory/storage/pouches/large/tan) + +/obj/item/clothing/suit/armor/pcarrier/merc + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/merc, /obj/item/clothing/accessory/armor/armguards/merc, /obj/item/clothing/accessory/armor/legguards/merc, /obj/item/clothing/accessory/storage/pouches/large) + +/obj/item/clothing/suit/armor/pcarrier/laserproof/full + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/laserproof, /obj/item/clothing/accessory/armor/armguards/laserproof, /obj/item/clothing/accessory/armor/legguards/laserproof, /obj/item/clothing/accessory/storage/pouches/blue) + +/obj/item/clothing/suit/armor/pcarrier/bulletproof/full + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/bulletproof, /obj/item/clothing/accessory/armor/armguards/bulletproof, /obj/item/clothing/accessory/armor/legguards/bulletproof, /obj/item/clothing/accessory/storage/pouches) + +/obj/item/clothing/suit/armor/pcarrier/riot/full + starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate/riot, /obj/item/clothing/accessory/armor/armguards/riot, /obj/item/clothing/accessory/armor/legguards/riot, /obj/item/clothing/accessory/storage/pouches) diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index de610ca3ee1..60237b385ed 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -1,95 +1,95 @@ -//Biosuit complete with shoes (in the item sprite) -/obj/item/clothing/head/bio_hood - name = "bio hood" - icon_state = "bio" - desc = "A hood that protects the head and face from biological comtaminants." - randpixel = 0 - center_of_mass = null - permeability_coefficient = 0.01 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - siemens_coefficient = 0.9 - flags = PHORONGUARD - item_flags = THICKMATERIAL | ALLOW_SURVIVALFOOD - -/obj/item/clothing/suit/bio_suit - name = "bio suit" - desc = "A suit that protects against biological contamination." - icon_state = "bio" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET - slowdown = 1.0 - allowed = list(/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/pen,/obj/item/device/flashlight/pen) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - siemens_coefficient = 0.9 - flags = PHORONGUARD - item_flags = THICKMATERIAL - -//Standard biosuit, orange stripe -/obj/item/clothing/head/bio_hood/general - icon_state = "bio_general" - item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/suit/bio_suit/general - icon_state = "bio_general" - item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Virology biosuit, green stripe -/obj/item/clothing/head/bio_hood/virology - icon_state = "bio_virology" - -/obj/item/clothing/suit/bio_suit/virology - icon_state = "bio_virology" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Security biosuit, grey with red stripe across the chest -/obj/item/clothing/head/bio_hood/security - icon_state = "bio_security" - -/obj/item/clothing/suit/bio_suit/security - icon_state = "bio_security" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Janitor's biosuit, grey with purple arms -/obj/item/clothing/head/bio_hood/janitor - icon_state = "bio_janitor" - -/obj/item/clothing/suit/bio_suit/janitor - icon_state = "bio_janitor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//Scientist's biosuit, white with a pink-ish hue -/obj/item/clothing/head/bio_hood/scientist - icon_state = "bio_scientist" - -/obj/item/clothing/suit/bio_suit/scientist - icon_state = "bio_scientist" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -//CMO's biosuit, blue stripe -/obj/item/clothing/suit/bio_suit/cmo - icon_state = "bio_cmo" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/head/bio_hood/cmo - icon_state = "bio_cmo" - -//Plague Dr mask can be found in clothing/masks/gasmask.dm. Golden can be found in labcoat.dm. -/obj/item/clothing/suit/bio_suit/plaguedoctorsuit - name = "plague doctor suit" - desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." - icon_state = "plaguedoctor" - item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") +//Biosuit complete with shoes (in the item sprite) +/obj/item/clothing/head/bio_hood + name = "bio hood" + icon_state = "bio" + desc = "A hood that protects the head and face from biological comtaminants." + randpixel = 0 + center_of_mass = null + permeability_coefficient = 0.01 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + siemens_coefficient = 0.9 + flags = PHORONGUARD + item_flags = THICKMATERIAL | ALLOW_SURVIVALFOOD + +/obj/item/clothing/suit/bio_suit + name = "bio suit" + desc = "A suit that protects against biological contamination." + icon_state = "bio" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET + slowdown = 1.0 + allowed = list(/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/pen,/obj/item/device/flashlight/pen) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + siemens_coefficient = 0.9 + flags = PHORONGUARD + item_flags = THICKMATERIAL + +//Standard biosuit, orange stripe +/obj/item/clothing/head/bio_hood/general + icon_state = "bio_general" + item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/suit/bio_suit/general + icon_state = "bio_general" + item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Virology biosuit, green stripe +/obj/item/clothing/head/bio_hood/virology + icon_state = "bio_virology" + +/obj/item/clothing/suit/bio_suit/virology + icon_state = "bio_virology" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Security biosuit, grey with red stripe across the chest +/obj/item/clothing/head/bio_hood/security + icon_state = "bio_security" + +/obj/item/clothing/suit/bio_suit/security + icon_state = "bio_security" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Janitor's biosuit, grey with purple arms +/obj/item/clothing/head/bio_hood/janitor + icon_state = "bio_janitor" + +/obj/item/clothing/suit/bio_suit/janitor + icon_state = "bio_janitor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//Scientist's biosuit, white with a pink-ish hue +/obj/item/clothing/head/bio_hood/scientist + icon_state = "bio_scientist" + +/obj/item/clothing/suit/bio_suit/scientist + icon_state = "bio_scientist" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +//CMO's biosuit, blue stripe +/obj/item/clothing/suit/bio_suit/cmo + icon_state = "bio_cmo" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/head/bio_hood/cmo + icon_state = "bio_cmo" + +//Plague Dr mask can be found in clothing/masks/gasmask.dm. Golden can be found in labcoat.dm. +/obj/item/clothing/suit/bio_suit/plaguedoctorsuit + name = "plague doctor suit" + desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." + icon_state = "plaguedoctor" + item_state_slots = list(slot_r_hand_str = "bio", slot_l_hand_str = "bio") flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER \ No newline at end of file diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index 1341af1b69d..37ac8cd948d 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -1,268 +1,268 @@ -/* - * Job related - */ - -//Botanist -/obj/item/clothing/suit/storage/apron - name = "apron" - desc = "A basic blue apron." - icon_state = "apron" - item_state_slots = list(slot_r_hand_str = "overalls", slot_l_hand_str = "overalls") - blood_overlay_type = "armor" - body_parts_covered = 0 - allowed = list (/obj/item/weapon/reagent_containers/spray/plantbgone, /obj/item/device/analyzer/plant_analyzer, /obj/item/seeds, - /obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/material/minihoe) - -/obj/item/clothing/suit/storage/apron/white - name = "white apron" - desc = "A basic white apron." - icon_state = "apron_white" - item_state_slots = list(slot_r_hand_str = "apronchef", slot_l_hand_str = "apronchef") - -/obj/item/clothing/suit/storage/apron/altevian - name = "Multi-purpose Crafters' Pride" - desc = "An apron designed by the space faring species that can hold an array of tools and other trinkets. It's made with a high-quality material." - icon_state = "apron_altevian" - item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) - -//Captain -/obj/item/clothing/suit/captunic - name = "site manager's parade tunic" - desc = "Worn by a Site Manager to show their class." - icon_state = "captunic" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/captunic/capjacket - name = "site manager's uniform jacket" - desc = "A less formal jacket for everyday Site Manager use." - icon_state = "capjacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEHOLSTER - -//Command -/obj/item/clothing/suit/storage/toggle/cmddressjacket - name = "command dress jacket" - desc = "A fancy dress jacket made for command staff. Makes you feel in charge." - icon_state = "cmddressjacket" - -//Chaplain -/obj/item/clothing/suit/storage/hooded/chaplain_hoodie - name = "chaplain hoodie" - desc = "This suit says to you \"Hush\"!" - icon_state = "chaplain_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEHOLSTER - hoodtype = /obj/item/clothing/head/chaplain_hood - allowed = list (/obj/item/weapon/storage/bible) - -//Chaplain but spookier -/obj/item/clothing/suit/storage/hooded/chaplain_hoodie/whiteout - name = "white robe" - desc = "A long, flowing white robe. It looks comfortable, but not very warm." - icon_state = "whiteout_robe" - item_state_slots = list(slot_r_hand_str = "suit_white", slot_l_hand_str = "suit_white") - flags_inv = HIDETIE|HIDEHOLSTER - hoodtype = /obj/item/clothing/head/chaplain_hood/whiteout - -//Chaplain -/obj/item/clothing/suit/nun - name = "nun robe" - desc = "Maximum piety in this star system." - icon_state = "nun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -//Chef -/obj/item/clothing/suit/chef - name = "chef's apron" - desc = "An apron used by a high class chef." - icon_state = "chef" - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - allowed = list (/obj/item/weapon/material/knife) - -//Chef -/obj/item/clothing/suit/chef/classic - name = "classic chef's apron" - desc = "A basic, dull, white chef's apron." - icon_state = "apronchef" - blood_overlay_type = "armor" - body_parts_covered = 0 - flags_inv = 0 - -//Security -/obj/item/clothing/suit/security/navyofficer - name = "security officer's jacket" - desc = "This jacket is for those special occasions when a security officer actually feels safe." - icon_state = "officerbluejacket" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/security/navywarden - name = "warden's jacket" - desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig." - icon_state = "wardenbluejacket" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/security/navyhos - name = "head of security's jacket" - desc = "This piece of clothing was specifically designed for asserting superior authority." - icon_state = "hosbluejacket" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -//Detective -/obj/item/clothing/suit/storage/det_trench - name = "brown trenchcoat" - desc = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat is externally impact resistant - perfect for your next act of autodefenestration!" - icon_state = "detective" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, - /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, - /obj/item/device/taperecorder, /obj/item/device/uv_light) - armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/det_trench/grey - name = "grey trenchcoat" - icon_state = "detective2" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -//Forensics -/obj/item/clothing/suit/storage/forensics - name = "jacket" - desc = "A forensics technician jacket." - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, - /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, - /obj/item/device/taperecorder, /obj/item/device/uv_light) - armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) - -/obj/item/clothing/suit/storage/forensics/red - name = "red jacket" - desc = "A red forensics technician jacket." - icon_state = "forensics_red" - item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") - -/obj/item/clothing/suit/storage/forensics/red/long - name = "long red jacket" - desc = "A long red forensics technician jacket." - icon_state = "forensics_red_long" - -/obj/item/clothing/suit/storage/forensics/blue - name = "blue jacket" - desc = "A blue forensics technician jacket." - icon_state = "forensics_blue" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - -/obj/item/clothing/suit/storage/forensics/blue/long - name = "long blue jacket" - desc = "A long blue forensics technician jacket." - icon_state = "forensics_blue_long" - -//Engineering -/obj/item/clothing/suit/storage/hazardvest - name = "hazard vest" - desc = "A high-visibility vest used in work zones." - icon_state = "hazard" - blood_overlay_type = "armor" - allowed = list (/obj/item/device/analyzer, /obj/item/device/flashlight, /obj/item/device/multitool, /obj/item/device/pipe_painter, /obj/item/device/radio, /obj/item/device/t_scanner, - /obj/item/weapon/tool/crowbar, /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/weapon/tank/emergency/oxygen, - /obj/item/clothing/mask/gas, /obj/item/taperoll/engineering, /obj/item/taperoll/atmos, /obj/item/device/analyzer, /obj/item/weapon/extinguisher/mini) //VOREStation edit. Few more tools that can be put on vests - body_parts_covered = UPPER_TORSO - -/obj/item/clothing/suit/storage/hazardvest/blue - name = "blue hazard vest" - desc = "A high-visibility vest used in work zones. This one is blue!" - icon_state = "hazard_b" - -/obj/item/clothing/suit/storage/hazardvest/green - name = "green hazard vest" - desc = "A high-visibility vest used by emergency responders." - icon_state = "hazard_g" - -/obj/item/clothing/suit/storage/hazardvest/white - name = "white hazard vest" - desc = "A high-visibility vest used in work zones. This one bears the symbol of a disaster relief team!" - icon_state = "hazard_w" - -//Lawyer -/obj/item/clothing/suit/storage/toggle/lawyer/bluejacket - name = "blue suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_blue" - item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/toggle/lawyer/purpjacket - name = "purple suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_purp" - item_state_slots = list(slot_r_hand_str = "suit_purple", slot_l_hand_str = "suit_purple") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Internal Affairs -/obj/item/clothing/suit/storage/toggle/internalaffairs - name = "black suit jacket" - desc = "A smooth black jacket." - icon_state = "ia_jacket" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Medical -/obj/item/clothing/suit/storage/toggle/fr_jacket - name = "first responder jacket" - desc = "A high-visibility jacket worn by medical first responders." - icon_state = "fr_jacket" - item_state_slots = list(slot_r_hand_str = "fr_jacket", slot_l_hand_str = "fr_jacket") - blood_overlay_type = "armor" - allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, - /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen) - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/toggle/fr_jacket/ems - name = "\improper EMS jacket" - desc = "A dark blue, martian-pattern, EMS jacket. It sports high-visibility reflective stripes and a star of life on the back." - icon_state = "ems_jacket" - item_state_slots = list(slot_r_hand_str = "ems_jacket", slot_l_hand_str = "ems_jacket") - -/obj/item/clothing/suit/surgicalapron - name = "surgical apron" - desc = "A sterile blue apron for performing surgery." - icon_state = "surgical" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, \ - /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/surgical/scalpel,/obj/item/weapon/surgical/retractor,/obj/item/weapon/surgical/hemostat, \ - /obj/item/weapon/surgical/cautery,/obj/item/weapon/surgical/bonegel,/obj/item/weapon/surgical/FixOVein) - -//Mime -/obj/item/clothing/suit/suspenders - name = "red suspenders" - desc = "They suspend the illusion of the mime's play." - icon = 'icons/inventory/belt/item.dmi' - icon_state = "suspenders" - blood_overlay_type = "armor" //it's the less thing that I can put here - body_parts_covered = 0 - -/obj/item/clothing/suit/suspenders/blue - name = "blue suspenders" - icon_state = "suspenders_blue" - -/obj/item/clothing/suit/suspenders/grey - name = "grey suspenders" - icon_state = "suspenders_grey" +/* + * Job related + */ + +//Botanist +/obj/item/clothing/suit/storage/apron + name = "apron" + desc = "A basic blue apron." + icon_state = "apron" + item_state_slots = list(slot_r_hand_str = "overalls", slot_l_hand_str = "overalls") + blood_overlay_type = "armor" + body_parts_covered = 0 + allowed = list (/obj/item/weapon/reagent_containers/spray/plantbgone, /obj/item/device/analyzer/plant_analyzer, /obj/item/seeds, + /obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/material/minihoe) + +/obj/item/clothing/suit/storage/apron/white + name = "white apron" + desc = "A basic white apron." + icon_state = "apron_white" + item_state_slots = list(slot_r_hand_str = "apronchef", slot_l_hand_str = "apronchef") + +/obj/item/clothing/suit/storage/apron/altevian + name = "Multi-purpose Crafters' Pride" + desc = "An apron designed by rodent-like spacers who take pride in their work. It's made with high-quality material." + icon_state = "apron_altevian" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + +//Captain +/obj/item/clothing/suit/captunic + name = "site manager's parade tunic" + desc = "Worn by a Site Manager to show their class." + icon_state = "captunic" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/captunic/capjacket + name = "site manager's uniform jacket" + desc = "A less formal jacket for everyday Site Manager use." + icon_state = "capjacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEHOLSTER + +//Command +/obj/item/clothing/suit/storage/toggle/cmddressjacket + name = "command dress jacket" + desc = "A fancy dress jacket made for command staff. Makes you feel in charge." + icon_state = "cmddressjacket" + +//Chaplain +/obj/item/clothing/suit/storage/hooded/chaplain_hoodie + name = "chaplain hoodie" + desc = "This suit says to you \"Hush\"!" + icon_state = "chaplain_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEHOLSTER + hoodtype = /obj/item/clothing/head/chaplain_hood + allowed = list (/obj/item/weapon/storage/bible) + +//Chaplain but spookier +/obj/item/clothing/suit/storage/hooded/chaplain_hoodie/whiteout + name = "white robe" + desc = "A long, flowing white robe. It looks comfortable, but not very warm." + icon_state = "whiteout_robe" + item_state_slots = list(slot_r_hand_str = "suit_white", slot_l_hand_str = "suit_white") + flags_inv = HIDETIE|HIDEHOLSTER + hoodtype = /obj/item/clothing/head/chaplain_hood/whiteout + +//Chaplain +/obj/item/clothing/suit/nun + name = "nun robe" + desc = "Maximum piety in this star system." + icon_state = "nun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +//Chef +/obj/item/clothing/suit/chef + name = "chef's apron" + desc = "An apron used by a high class chef." + icon_state = "chef" + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + allowed = list (/obj/item/weapon/material/knife) + +//Chef +/obj/item/clothing/suit/chef/classic + name = "classic chef's apron" + desc = "A basic, dull, white chef's apron." + icon_state = "apronchef" + blood_overlay_type = "armor" + body_parts_covered = 0 + flags_inv = 0 + +//Security +/obj/item/clothing/suit/security/navyofficer + name = "security officer's jacket" + desc = "This jacket is for those special occasions when a security officer actually feels safe." + icon_state = "officerbluejacket" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/security/navywarden + name = "warden's jacket" + desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig." + icon_state = "wardenbluejacket" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/security/navyhos + name = "head of security's jacket" + desc = "This piece of clothing was specifically designed for asserting superior authority." + icon_state = "hosbluejacket" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +//Detective +/obj/item/clothing/suit/storage/det_trench + name = "brown trenchcoat" + desc = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat is externally impact resistant - perfect for your next act of autodefenestration!" + icon_state = "detective" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, + /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, + /obj/item/device/taperecorder, /obj/item/device/uv_light) + armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/det_trench/grey + name = "grey trenchcoat" + icon_state = "detective2" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +//Forensics +/obj/item/clothing/suit/storage/forensics + name = "jacket" + desc = "A forensics technician jacket." + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight, /obj/item/weapon/gun/energy, /obj/item/weapon/gun/projectile, /obj/item/ammo_magazine, + /obj/item/ammo_casing, /obj/item/weapon/melee/baton, /obj/item/weapon/handcuffs, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/flame/lighter, + /obj/item/device/taperecorder, /obj/item/device/uv_light) + armor = list(melee = 10, bullet = 10, laser = 15, energy = 10, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/storage/forensics/red + name = "red jacket" + desc = "A red forensics technician jacket." + icon_state = "forensics_red" + item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") + +/obj/item/clothing/suit/storage/forensics/red/long + name = "long red jacket" + desc = "A long red forensics technician jacket." + icon_state = "forensics_red_long" + +/obj/item/clothing/suit/storage/forensics/blue + name = "blue jacket" + desc = "A blue forensics technician jacket." + icon_state = "forensics_blue" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + +/obj/item/clothing/suit/storage/forensics/blue/long + name = "long blue jacket" + desc = "A long blue forensics technician jacket." + icon_state = "forensics_blue_long" + +//Engineering +/obj/item/clothing/suit/storage/hazardvest + name = "hazard vest" + desc = "A high-visibility vest used in work zones." + icon_state = "hazard" + blood_overlay_type = "armor" + allowed = list (/obj/item/device/analyzer, /obj/item/device/flashlight, /obj/item/device/multitool, /obj/item/device/pipe_painter, /obj/item/device/radio, /obj/item/device/t_scanner, + /obj/item/weapon/tool/crowbar, /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/weapon/tank/emergency/oxygen, + /obj/item/clothing/mask/gas, /obj/item/taperoll/engineering, /obj/item/taperoll/atmos, /obj/item/device/analyzer, /obj/item/weapon/extinguisher/mini) //VOREStation edit. Few more tools that can be put on vests + body_parts_covered = UPPER_TORSO + +/obj/item/clothing/suit/storage/hazardvest/blue + name = "blue hazard vest" + desc = "A high-visibility vest used in work zones. This one is blue!" + icon_state = "hazard_b" + +/obj/item/clothing/suit/storage/hazardvest/green + name = "green hazard vest" + desc = "A high-visibility vest used by emergency responders." + icon_state = "hazard_g" + +/obj/item/clothing/suit/storage/hazardvest/white + name = "white hazard vest" + desc = "A high-visibility vest used in work zones. This one bears the symbol of a disaster relief team!" + icon_state = "hazard_w" + +//Lawyer +/obj/item/clothing/suit/storage/toggle/lawyer/bluejacket + name = "blue suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_blue" + item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/toggle/lawyer/purpjacket + name = "purple suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_purp" + item_state_slots = list(slot_r_hand_str = "suit_purple", slot_l_hand_str = "suit_purple") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Internal Affairs +/obj/item/clothing/suit/storage/toggle/internalaffairs + name = "black suit jacket" + desc = "A smooth black jacket." + icon_state = "ia_jacket" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Medical +/obj/item/clothing/suit/storage/toggle/fr_jacket + name = "first responder jacket" + desc = "A high-visibility jacket worn by medical first responders." + icon_state = "fr_jacket" + item_state_slots = list(slot_r_hand_str = "fr_jacket", slot_l_hand_str = "fr_jacket") + blood_overlay_type = "armor" + allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, + /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen) + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/toggle/fr_jacket/ems + name = "\improper EMS jacket" + desc = "A dark blue, martian-pattern, EMS jacket. It sports high-visibility reflective stripes and a star of life on the back." + icon_state = "ems_jacket" + item_state_slots = list(slot_r_hand_str = "ems_jacket", slot_l_hand_str = "ems_jacket") + +/obj/item/clothing/suit/surgicalapron + name = "surgical apron" + desc = "A sterile blue apron for performing surgery." + icon_state = "surgical" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/stack/medical, /obj/item/weapon/reagent_containers/dropper, /obj/item/weapon/reagent_containers/hypospray, /obj/item/weapon/reagent_containers/syringe, \ + /obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/surgical/scalpel,/obj/item/weapon/surgical/retractor,/obj/item/weapon/surgical/hemostat, \ + /obj/item/weapon/surgical/cautery,/obj/item/weapon/surgical/bonegel,/obj/item/weapon/surgical/FixOVein) + +//Mime +/obj/item/clothing/suit/suspenders + name = "red suspenders" + desc = "They suspend the illusion of the mime's play." + icon = 'icons/inventory/belt/item.dmi' + icon_state = "suspenders" + blood_overlay_type = "armor" //it's the less thing that I can put here + body_parts_covered = 0 + +/obj/item/clothing/suit/suspenders/blue + name = "blue suspenders" + icon_state = "suspenders_blue" + +/obj/item/clothing/suit/suspenders/grey + name = "grey suspenders" + icon_state = "suspenders_grey" diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 5d0c531c5cc..0e5d4d938b8 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -1,123 +1,123 @@ -/obj/item/clothing/suit/storage/toggle/labcoat - name = "labcoat" - desc = "A suit that protects against minor chemical spills." - icon_state = "labcoat" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper, /obj/item/clothing/mask/gas) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) - -/obj/item/clothing/suit/storage/toggle/labcoat/red - name = "red labcoat" - desc = "A suit that protects against minor chemical spills. This one is red." - icon_state = "red_labcoat" - item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/blue - name = "blue labcoat" - desc = "A suit that protects against minor chemical spills. This one is blue." - icon_state = "blue_labcoat" - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/purple - name = "purple labcoat" - desc = "A suit that protects against minor chemical spills. This one is purple." - icon_state = "purple_labcoat" - item_state_slots = list(slot_r_hand_str = "purple_labcoat", slot_l_hand_str = "purple_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/orange - name = "orange labcoat" - desc = "A suit that protects against minor chemical spills. This one is orange." - icon_state = "orange_labcoat" - item_state_slots = list(slot_r_hand_str = "orange_labcoat", slot_l_hand_str = "orange_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/yellow - name = "yellow labcoat" - desc = "A suit that protects against minor chemical spills. This one is yellow." - icon_state = "yellow_labcoat" - item_state_slots = list(slot_r_hand_str = "yellow_labcoat", slot_l_hand_str = "yellow_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/pink - name = "pink labcoat" - desc = "A suit that protects against minor chemical spills. This one is pink." - icon_state = "pink_labcoat" - item_state_slots = list(slot_r_hand_str = "pink_labcoat", slot_l_hand_str = "pink_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/green - name = "green labcoat" - desc = "A suit that protects against minor chemical spills. This one is green." - icon_state = "green_labcoat" - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/mad - name = "The Mad's labcoat" - desc = "It makes you look capable of konking someone on the noggin and shooting them into space." - icon_state = "green_labcoat" - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/cmo - name = "chief medical officer's labcoat" - desc = "Bluer than the standard model." - icon_state = "labcoat_cmo" - item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/cmoalt - name = "chief medical officer's labcoat" - desc = "A labcoat with command blue highlights." - icon_state = "labcoat_cmoalt" - item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/genetics - name = "Geneticist labcoat" - desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." - icon_state = "labcoat_gen" - item_state_slots = list(slot_r_hand_str = "genetics_labcoat", slot_l_hand_str = "genetics_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/chemist - name = "Chemist labcoat" - desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." - icon_state = "labcoat_chem" - item_state_slots = list(slot_r_hand_str = "chemist_labcoat", slot_l_hand_str = "chemist_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/virologist - name = "Virologist labcoat" - desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." - icon_state = "labcoat_vir" - item_state_slots = list(slot_r_hand_str = "virologist_labcoat", slot_l_hand_str = "virologist_labcoat") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) - -/obj/item/clothing/suit/storage/toggle/labcoat/roboticist - name = "Roboticist labcoat" - desc = "More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads." - icon_state = "labcoat_robo" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/science - name = "Scientist labcoat" - desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." - icon_state = "labcoat_tox" - item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/rd - name = "research director's labcoat" - desc = "A flashy labcoat with purple markings. It belongs to the Research Director." - icon_state = "labcoat_rd" - item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/emt - name = "EMT's labcoat" - desc = "A dark blue labcoat with reflective strips for emergency medical technicians." - icon_state = "labcoat_emt" - item_state_slots = list(slot_r_hand_str = "emt_labcoat", slot_l_hand_str = "emt_labcoat") - -/obj/item/clothing/suit/storage/toggle/labcoat/blue_edge - name = "blue-edged labcoat" - desc = "A suit that protects against minor chemical spills. This one has blue trim." - icon_state = "blue_edge_labcoat" - -/obj/item/clothing/suit/storage/toggle/labcoat/plaguedoctor - name = "golden plague doctor suit" - desc = "If it worked then, it works now. This classic design comes in gold." - icon_state = "plaguedoctor2" +/obj/item/clothing/suit/storage/toggle/labcoat + name = "labcoat" + desc = "A suit that protects against minor chemical spills." + icon_state = "labcoat" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper, /obj/item/clothing/mask/gas) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) + +/obj/item/clothing/suit/storage/toggle/labcoat/red + name = "red labcoat" + desc = "A suit that protects against minor chemical spills. This one is red." + icon_state = "red_labcoat" + item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/blue + name = "blue labcoat" + desc = "A suit that protects against minor chemical spills. This one is blue." + icon_state = "blue_labcoat" + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/purple + name = "purple labcoat" + desc = "A suit that protects against minor chemical spills. This one is purple." + icon_state = "purple_labcoat" + item_state_slots = list(slot_r_hand_str = "purple_labcoat", slot_l_hand_str = "purple_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/orange + name = "orange labcoat" + desc = "A suit that protects against minor chemical spills. This one is orange." + icon_state = "orange_labcoat" + item_state_slots = list(slot_r_hand_str = "orange_labcoat", slot_l_hand_str = "orange_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/yellow + name = "yellow labcoat" + desc = "A suit that protects against minor chemical spills. This one is yellow." + icon_state = "yellow_labcoat" + item_state_slots = list(slot_r_hand_str = "yellow_labcoat", slot_l_hand_str = "yellow_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/pink + name = "pink labcoat" + desc = "A suit that protects against minor chemical spills. This one is pink." + icon_state = "pink_labcoat" + item_state_slots = list(slot_r_hand_str = "pink_labcoat", slot_l_hand_str = "pink_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/green + name = "green labcoat" + desc = "A suit that protects against minor chemical spills. This one is green." + icon_state = "green_labcoat" + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/mad + name = "The Mad's labcoat" + desc = "It makes you look capable of konking someone on the noggin and shooting them into space." + icon_state = "green_labcoat" + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/cmo + name = "chief medical officer's labcoat" + desc = "Bluer than the standard model." + icon_state = "labcoat_cmo" + item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/cmoalt + name = "chief medical officer's labcoat" + desc = "A labcoat with command blue highlights." + icon_state = "labcoat_cmoalt" + item_state_slots = list(slot_r_hand_str = "cmo_labcoat", slot_l_hand_str = "cmo_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/genetics + name = "Geneticist labcoat" + desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." + icon_state = "labcoat_gen" + item_state_slots = list(slot_r_hand_str = "genetics_labcoat", slot_l_hand_str = "genetics_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/chemist + name = "Chemist labcoat" + desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." + icon_state = "labcoat_chem" + item_state_slots = list(slot_r_hand_str = "chemist_labcoat", slot_l_hand_str = "chemist_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/virologist + name = "Virologist labcoat" + desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." + icon_state = "labcoat_vir" + item_state_slots = list(slot_r_hand_str = "virologist_labcoat", slot_l_hand_str = "virologist_labcoat") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 0) + +/obj/item/clothing/suit/storage/toggle/labcoat/roboticist + name = "Roboticist labcoat" + desc = "More like an eccentric coat than a labcoat. Helps pass off bloodstains as part of the aesthetic. Comes with red shoulder pads." + icon_state = "labcoat_robo" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/science + name = "Scientist labcoat" + desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." + icon_state = "labcoat_tox" + item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/rd + name = "research director's labcoat" + desc = "A flashy labcoat with purple markings. It belongs to the Research Director." + icon_state = "labcoat_rd" + item_state_slots = list(slot_r_hand_str = "science_labcoat", slot_l_hand_str = "science_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/emt + name = "EMT's labcoat" + desc = "A dark blue labcoat with reflective strips for emergency medical technicians." + icon_state = "labcoat_emt" + item_state_slots = list(slot_r_hand_str = "emt_labcoat", slot_l_hand_str = "emt_labcoat") + +/obj/item/clothing/suit/storage/toggle/labcoat/blue_edge + name = "blue-edged labcoat" + desc = "A suit that protects against minor chemical spills. This one has blue trim." + icon_state = "blue_edge_labcoat" + +/obj/item/clothing/suit/storage/toggle/labcoat/plaguedoctor + name = "golden plague doctor suit" + desc = "If it worked then, it works now. This classic design comes in gold." + icon_state = "plaguedoctor2" diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 6c114b471fc..0a7fda4ed2f 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -1,1105 +1,1117 @@ -/* - * Contains: - * Lasertag - * Costume - * Misc - * Department Jackets - */ - -// -S2-note- Needs categorizing and sorting. - -/* - * Lasertag - */ - -/obj/item/clothing/suit/bluetag - name = "blue laser tag armor" - desc = "Blue Pride, Station Wide." - icon_state = "bluetag" - item_state_slots = list(slot_r_hand_str = "tdblue", slot_l_hand_str = "tdblue") - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO - allowed = list (/obj/item/weapon/gun/energy/lasertag/blue) - siemens_coefficient = 3.0 - -/obj/item/clothing/suit/bluetag/sub - name = "Brigader Armor" - desc = "Repilca rmor commonly worn by Spacer Union Brigade members from the hit series Spacer Trail. Modified for Laser Tag (Blue Team)." - icon_state = "bluetag2" - -/obj/item/clothing/suit/redtag - name = "red laser tag armor" - desc = "Reputed to go faster." - icon_state = "redtag" - item_state_slots = list(slot_r_hand_str = "tdred", slot_l_hand_str = "tdred") - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO - allowed = list (/obj/item/weapon/gun/energy/lasertag/red) - siemens_coefficient = 3.0 - -/obj/item/clothing/suit/redtag/dom - name = "Mu'tu'bi Armor" - desc = "Repilca rmor commonly worn by Dominion Of Mu'tu'bi soldiers from the hit series Spacer Trail. Modified for Laser Tag (Red Team)." - icon_state = "redtag2" - -/* - * 80s - */ - -/obj/item/clothing/suit/storage/puffyblue - name = "blue puffy coat" - desc = "A stylish, shiny, very blue puffer coat." - icon_state = "puffycoatblue" - item_state = "puffycoatblue" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - -/obj/item/clothing/suit/storage/puffypurple - name = "purple puffy coat" - desc = "A stylish, shiny, very purple puffer coat." - icon_state = "puffycoatpurple" - item_state = "puffycoatpurple" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - item_state_slots = list(slot_r_hand_str = "purple_labcoat", slot_l_hand_str = "purple_labcoat") - -/obj/item/clothing/suit/storage/puffyred - name = "crimson puffy coat" - desc = "A stylish, shiny, very crimson puffer coat." - icon_state = "puffycoatred" - item_state = "puffycoatred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0.7 - item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") - -/* - * Costume - */ - -/obj/item/clothing/suit/pirate - name = "pirate coat" - desc = "Yarr." - icon_state = "pirate" - item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/poncho - name = "poncho" - desc = "A simple, comfortable poncho." - icon = 'icons/inventory/suit/mob.dmi' - icon_state = "poncho" - item_state = "poncho" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_vr.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_vr.dmi', - ) - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/hgpirate - name = "pirate captain coat" - desc = "Yarr." - icon_state = "hgpirate" - item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") - flags_inv = HIDEJUMPSUIT - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - -/obj/item/clothing/suit/cyborg_suit - name = "cyborg suit" - desc = "Suit for a cyborg costume." - icon_state = "death" - fire_resist = T0C+5200 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/greatcoat - name = "great coat" - desc = "A heavy great coat" - icon_state = "gentlecoat" - item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/johnny_coat - name = "johnny~~ coat" - desc = "Johnny~~" - icon_state = "gentlecoat" - item_state_slots = list(slot_r_hand_str = "johnny_coat", slot_l_hand_str = "johnny_coat") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/justice - name = "justice suit" - desc = "This pretty much looks ridiculous." - icon_state = "gentle_coat" - item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET - -/obj/item/clothing/suit/judgerobe - name = "judge's robe" - desc = "This robe commands authority." - icon_state = "judge" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/spacecash) - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/storage/apron/overalls - name = "coveralls" - desc = "A set of denim overalls." - icon_state = "overalls" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/suit/syndicatefake - name = "red space suit replica" - icon_state = "syndicate" - desc = "A plastic replica of a mercenary combat space suit, you'll look just like a real bloodthirsty mercenary in this! This is a toy, it is not made for use in space!" - w_class = ITEMSIZE_NORMAL - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/toy) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET - -/obj/item/clothing/suit/hastur - name = "Hastur's Robes" - desc = "Robes not meant to be worn by man" - icon_state = "hastur" - item_state_slots = list(slot_r_hand_str = "rad", slot_l_hand_str = "rad") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/imperium_monk - name = "Imperium monk" - desc = "Have YOU killed a xenos today?" - icon_state = "imperium_monk" - body_parts_covered = HEAD|UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/chickensuit - name = "Chicken Suit" - desc = "A suit made long ago by the ancient empire KFC." - icon_state = "chickensuit" - body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO|LEGS|FEET - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 2.0 - -/obj/item/clothing/suit/monkeysuit - name = "Monkey Suit" - desc = "A suit that looks like a primate" - icon_state = "monkeysuit" - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO|LEGS|FEET|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 2.0 - -/obj/item/clothing/suit/holidaypriest - name = "Holiday Priest" - desc = "This is a nice holiday my son." - icon_state = "holidaypriest" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/cardborg - name = "cardborg suit" - desc = "An ordinary cardboard box with holes cut in the sides." - icon_state = "cardborg" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/skeleton - name = "skeleton costume" - desc = "A body-tight costume with the human skeleton lined out on it." - icon_state = "skelecost" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|FEET|HANDS|EYES|HEAD|FACE - flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDEGLOVES|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "judge", slot_l_hand_str = "judge") - -/obj/item/clothing/suit/engicost - name = "sexy engineering voidsuit costume" - desc = "It's supposed to look like an engineering voidsuit... It doesn't look like it could protect from much radiation." - icon_state = "engicost" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET - flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "eng_voidsuit", slot_l_hand_str = "eng_voidsuit") - -/obj/item/clothing/suit/maxman - name = "doctor maxman costume" - desc = "A costume made to look like Dr. Maxman, the famous male-enhancement salesman. Complete with red do-rag and sleeveless labcoat." - icon_state = "maxman" - body_parts_covered = LOWER_TORSO|FEET|LEGS|HEAD - flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - -/obj/item/clothing/suit/iasexy - name = "sexy internal affairs suit" - desc = "Now where's your pen?~" - icon_state = "iacost" - body_parts_covered = UPPER_TORSO|FEET|LOWER_TORSO|EYES - flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - -/obj/item/clothing/suit/sexyminer - name = "sexy miner costume" - desc = "For when you need to get your rocks off." - icon_state = "sexyminer" - body_parts_covered = FEET|LOWER_TORSO|HEAD - flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "miner", slot_l_hand_str = "miner") - -/obj/item/clothing/suit/sumo - name = "inflatable sumo wrestler costume" - desc = "An inflated sumo wrestler costume. It's quite hot." - icon_state = "sumo" - body_parts_covered = FEET|LOWER_TORSO|UPPER_TORSO|LEGS|ARMS - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "classicponcho", slot_l_hand_str = "classicponcho") - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - -/obj/item/clothing/suit/hackercost - name = "classic hacker costume" - desc = "You would feel insanely cool wearing this." - icon_state = "hackercost" - body_parts_covered = FEET|LOWER_TORSO|UPPER_TORSO|LEGS|ARMS|EYES - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "leather_coat", slot_l_hand_str = "leather_coat") - -/obj/item/clothing/suit/lumber - name = "sexy lumberjack costume" - desc = "Smells of dusky pine. Includes chest hair and beard." - icon_state = "sexylumber" - body_parts_covered = FEET|LOWER_TORSO|FEET - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") - -/obj/item/clothing/suit/shrine_maiden - name = "shrine maiden costume" - desc = "Makes you want to exterminate some troublesome youkai." - icon_state = "shrine_maiden" - body_parts_covered = LOWER_TORSO|UPPER_TORSO|LEGS|ARMS - -/* - * Misc - */ - -/obj/item/clothing/suit/straight_jacket - name = "straight jacket" - desc = "A suit that completely restrains the wearer." - icon_state = "straight_jacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - - var/resist_time = 4800 // Eight minutes. - -/obj/item/clothing/suit/straight_jacket/attack_hand(mob/living/user as mob) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(src == H.wear_suit) - to_chat(H, "You need help taking this off!") - return - ..() - -/obj/item/clothing/suit/straight_jacket/equipped(var/mob/living/user,var/slot) - . = ..() - if(slot == slot_wear_suit) - user.drop_l_hand() - user.drop_r_hand() - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.drop_from_inventory(H.handcuffed) - -/obj/item/clothing/suit/ianshirt - name = "worn shirt" - desc = "A worn out, curiously comfortable t-shirt with a picture of Ian. You wouldn't go so far as to say it feels like being hugged when you wear it but it's pretty close. Good for sleeping in." - icon_state = "ianshirt" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") //placeholder -S2- - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - -//nerdy shirt -/obj/item/clothing/suit/nerdshirt - name = "nerdy shirt" - desc = "A comfy white t-shirt with a picture of a cartoon hedgehog on it. Although clean, it still seems like the wearer should be embarrassed for owning it." - icon_state = "nerdshirt" - -/* - * Kimonos - */ - -/obj/item/clothing/suit/kimono - name = "traditional kimono" - desc = "A traditional Japanese kimono." - icon_state = "kimono" - addblends = "kimono_a" - -/obj/item/clothing/suit/kamishimo - name = "traditional kamishimo" - desc = "Traditional Japanese menswear." - icon_state = "kamishimo" - addblends = "kamishimo_a" - -/obj/item/clothing/suit/kimono/red - name = "red kimono" - icon_state = "kimono_red" - -/obj/item/clothing/suit/kimono/orange - name = "orange kimono" - icon_state = "kimono_orange" - -/obj/item/clothing/suit/kimono/yellow - name = "yellow kimono" - icon_state = "kimono_yellow" - -/obj/item/clothing/suit/kimono/green - name = "green kimono" - icon_state = "kimono_green" - -/obj/item/clothing/suit/kimono/blue - name = "blue kimono" - icon_state = "kimono_blue" - -/obj/item/clothing/suit/kimono/purple - name = "purple kimono" - icon_state = "kimono_purple" - -/obj/item/clothing/suit/kimono/violet - name = "violet kimono" - icon_state = "kimono_violet" - -/obj/item/clothing/suit/kimono/pink - name = "pink kimono" - icon_state = "kimono_pink" - -/obj/item/clothing/suit/kimono/earth - name = "earth kimono" - icon_state = "kimono_earth" - -/* - * Coats - */ - -/obj/item/clothing/suit/leathercoat - name = "leather coat" - desc = "A long, thick black leather coat." - icon_state = "leathercoat_alt" - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/leathercoat/sec - name = "leather coat" - desc = "A long, thick black leather coat." - icon_state = "leathercoat_sec" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/browncoat - name = "brown leather coat" - desc = "A long, brown leather coat." - icon_state = "browncoat" - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/neocoat - name = "black coat" - desc = "A flowing, black coat." - icon_state = "neocoat" - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/customs - name = "customs jacket" - desc = "A standard SolGov Customs formal jacket." - icon_state = "customs_jacket" - item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/greyjacket - name = "grey jacket" - desc = "A fancy twead grey jacket." - icon_state = "gentlecoat" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/trench - name = "brown trenchcoat" - desc = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat appears to have its kevlar lining removed." - icon_state = "detective" - blood_overlay_type = "coat" - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter,/obj/item/device/taperecorder,/obj/item/device/uv_light) - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/trench/grey - name = "grey trenchcoat" - icon_state = "detective2" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/peacoat - name = "peacoat" - desc = "A well-tailored, stylish peacoat." - icon_state = "peacoat" - addblends = "peacoat_a" - item_state_slots = list(slot_r_hand_str = "peacoat", slot_l_hand_str = "peacoat") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/duster - name = "duster" - desc = "A duster is a light, loose-fitting long coat. Dusters are meant to protect your clothing from dust and rain." - icon_state = "duster" - blood_overlay_type = "coat" - allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter) - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/cardigan - name = "cardigan" - desc = "A cozy cardigan in a classic style." - icon_state = "cardigan" - addblends = "cardigan_a" - flags_inv = HIDEHOLSTER - -/* - * stripper - */ -/obj/item/clothing/suit/stripper/stripper_pink - name = "pink skimpy dress" - desc = "A rather skimpy pink dress." - icon_state = "stripper_p_over" - item_state_slots = list(slot_r_hand_str = "pink_labcoat", slot_l_hand_str = "pink_labcoat") - siemens_coefficient = 1 - -/obj/item/clothing/suit/stripper/stripper_green - name = "green skimpy dress" - desc = "A rather skimpy green dress." - icon_state = "stripper_g_over" - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - siemens_coefficient = 1 - -/obj/item/clothing/suit/xenos - name = "xenos suit" - desc = "A suit made out of chitinous alien hide." - icon_state = "xenos" - item_state_slots = list(slot_r_hand_str = "black_suit", slot_l_hand_str = "black_suit") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 2.0 - -/obj/item/clothing/suit/jacket/puffer - name = "puffer jacket" - desc = "A thick jacket with a rubbery, water-resistant shell." - icon_state = "pufferjacket" - item_state_slots = list(slot_r_hand_str = "chainmail", slot_l_hand_str = "chainmail") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/jacket/puffer/vest - name = "puffer vest" - desc = "A thick vest with a rubbery, water-resistant shell." - icon_state = "puffervest" - item_state_slots = list(slot_r_hand_str = "chainmail", slot_l_hand_str = "chainmail") - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/suit/storage/miljacket - name = "military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable." - icon_state = "militaryjacket_nobadge" - item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/alt - name = "military jacket, alternate" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one has some extra badges on it." - icon_state = "militaryjacket_badge" - item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/green - name = "green military jacket" - desc = "A dark but rather high-saturation green canvas jacket. Feels sturdy, yet comfortable." - icon_state = "militaryjacket_green" - item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/tan - name = "tan military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Now in sandy tans for desert fans." - icon_state = "militaryjacket_tan" - item_state_slots = list(slot_r_hand_str = "suit_orange", slot_l_hand_str = "suit_orange") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/grey - name = "grey military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one's in urban grey." - icon_state = "militaryjacket_grey" - item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/navy - name = "navy military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Dark navy, this one is." - icon_state = "militaryjacket_navy" - item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/black - name = "black military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Now in tactical black." - icon_state = "militaryjacket_black" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/miljacket/white - name = "white military jacket" - desc = "A white canvas jacket. Don't wear this for walks in the snow, it won't keep you warm - it'll just make it harder to find your frozen corpse." - icon_state = "militaryjacket_white" - item_state_slots = list(slot_r_hand_str = "med_dep_jacket", slot_l_hand_str = "med_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/bomber - name = "bomber jacket" - desc = "A thick, well-worn WW2 leather bomber jacket." - icon_state = "bomber" - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - cold_protection = UPPER_TORSO|ARMS - min_cold_protection_temperature = T0C - 20 - siemens_coefficient = 0.7 - -/obj/item/clothing/suit/storage/toggle/bomber/retro - name = "retro bomber jacket" - desc = "A retro style, fur-lined leather bomber jacket that invokes the early days of space exploration when spacemen were spacemen, and laser guns had funny little antennae on them." - icon_state = "retrojacket" - -/obj/item/clothing/suit/storage/bomber/alt - name = "bomber jacket" - desc = "A thick, well-worn WW2 leather bomber jacket." - icon_state = "bomberjacket_new" - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - cold_protection = UPPER_TORSO|ARMS - min_cold_protection_temperature = T0C - 20 - siemens_coefficient = 0.7 - -/obj/item/clothing/suit/storage/toggle/leather_jacket - name = "leather jacket" - desc = "A black leather coat." - icon_state = "leather_jacket" - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/leather_jacket/sleeveless - name = "leather vest" - desc = "A black leather vest." - icon_state = "leather_jacket_sleeveless" - body_parts_covered = UPPER_TORSO - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - -/obj/item/clothing/suit/storage/leather_jacket_alt - name = "leather vest" - desc = "A black leather vest." - icon_state = "leather_jacket_alt" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/toggle/leather_jacket/nanotrasen - desc = "A black leather coat. A corporate logo is proudly displayed on the back." - icon_state = "leather_jacket_nt" - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - -/obj/item/clothing/suit/storage/toggle/leather_jacket/nanotrasen/sleeveless - name = "leather vest" - desc = "A black leather vest. A corporate logo is proudly displayed on the back." - icon_state = "leather_jacket_nt_sleeveless" - body_parts_covered = UPPER_TORSO - item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") - -//This one has buttons for some reason -/obj/item/clothing/suit/storage/toggle/brown_jacket - name = "brown jacket" - desc = "A brown leather coat." - icon_state = "brown_jacket" - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/brown_jacket/sleeveless - name = "brown vest" - desc = "A brown leather vest." - icon_state = "brown_jacket_sleeveless" - body_parts_covered = UPPER_TORSO - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - -/obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen - desc = "A brown leather coat. A corporate logo is proudly displayed on the back." - icon_state = "brown_jacket_nt" - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - -/obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen/sleeveless - name = "brown vest" - desc = "A brown leather vest. A corporate logo is proudly displayed on the back." - icon_state = "brown_jacket_nt_sleeveless" - body_parts_covered = UPPER_TORSO - item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") - -/obj/item/clothing/suit/storage/toggle/denim_jacket - name = "denim jacket" - desc = "A denim coat." - icon_state = "denim_jacket" - item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/denim_jacket/sleeveless - name = "denim vest" - desc = "A denim vest." - icon_state = "denim_jacket_sleeveless" - body_parts_covered = UPPER_TORSO - item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") - -/obj/item/clothing/suit/storage/toggle/denim_jacket/nanotrasen - desc = "A denim coat. A corporate logo is proudly displayed on the back." - icon_state = "denim_jacket_nt" - item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") - -/obj/item/clothing/suit/storage/toggle/denim_jacket/nanotrasen/sleeveless - name = "denim vest" - desc = "A denim vest. A corporate logo is proudly displayed on the back." - icon_state = "denim_jacket_nt_sleeveless" - body_parts_covered = UPPER_TORSO - item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") - -/obj/item/clothing/suit/storage/toggle/hoodie - name = "grey hoodie" - desc = "A warm, grey sweatshirt." - icon_state = "grey_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") - min_cold_protection_temperature = T0C - 20 - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/hoodie/black - name = "black hoodie" - desc = "A warm, black sweatshirt." - icon_state = "black_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - -/obj/item/clothing/suit/storage/toggle/hoodie/red - name = "red hoodie" - desc = "A warm, red sweatshirt." - icon_state = "red_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") - -/obj/item/clothing/suit/storage/toggle/hoodie/blue - name = "blue hoodie" - desc = "A warm, blue sweatshirt." - icon_state = "blue_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") - -/obj/item/clothing/suit/storage/toggle/hoodie/green - name = "green hoodie" - desc = "A warm, green sweatshirt." - icon_state = "green_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") - -/obj/item/clothing/suit/storage/toggle/hoodie/orange - name = "orange hoodie" - desc = "A warm, orange sweatshirt." - icon_state = "orange_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_orange", slot_l_hand_str = "suit_orange") - -/obj/item/clothing/suit/storage/toggle/hoodie/yellow - name = "yellow hoodie" - desc = "A warm, yellow sweatshirt." - icon_state = "yellow_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_yellow", slot_l_hand_str = "suit_yellow") - -/obj/item/clothing/suit/storage/toggle/hoodie/cti - name = "CTI hoodie" - desc = "A warm, black sweatshirt. It bears the letters ‘CTI’ on the back, a lettering to the prestigious university in Tau Ceti, Ceti Technical Institute. There is a blue supernova embroidered on the front, the emblem of CTI." - icon_state = "cti_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - -/obj/item/clothing/suit/storage/toggle/hoodie/mu - name = "mojave university hoodie" - desc = "A warm, gray sweatshirt. It bears the letters ‘MU’ on the front, a lettering to the well-known public college, Mojave University." - icon_state = "mu_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") - -/obj/item/clothing/suit/storage/toggle/hoodie/nt - name = "NT hoodie" - desc = "A warm, blue sweatshirt. It proudly bears the silver NanoTrasen insignia lettering on the back. The edges are trimmed with silver." - icon_state = "nt_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") - -/obj/item/clothing/suit/storage/toggle/hoodie/smw - name = "Space Mountain Wind hoodie" - desc = "A warm, black sweatshirt. It has the logo for the popular softdrink Space Mountain Wind on both the front and the back." - icon_state = "smw_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - -/obj/item/clothing/suit/storage/toggle/hoodie/nrti - name = "New Reykjavik Technical Institute hoodie" - desc = "A warm, gray sweatshirt. It bears the letters ‘NRT’ on the back, in reference to Sif's premiere technical institute." - icon_state = "nrti_hoodie" - item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") - -/obj/item/clothing/suit/whitedress - name = "white dress" - desc = "A fancy dress." - icon_state = "white_dress" - item_state_slots = list(slot_r_hand_str = "white_dress", slot_l_hand_str = "white_dress") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/varsity - name = "black varsity jacket" - desc = "A favorite of jocks everywhere from Sol to Nyx." - icon_state = "varsity" - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") - flags_inv = HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/varsity/red - name = "red varsity jacket" - icon_state = "varsity_red" - -/obj/item/clothing/suit/varsity/purple - name = "purple varsity jacket" - icon_state = "varsity_purple" - -/obj/item/clothing/suit/varsity/green - name = "green varsity jacket" - icon_state = "varsity_green" - -/obj/item/clothing/suit/varsity/blue - name = "blue varsity jacket" - icon_state = "varsity_blue" - -/obj/item/clothing/suit/varsity/brown - name = "brown varsity jacket" - icon_state = "varsity_brown" - -/* - * Department Jackets - */ -/obj/item/clothing/suit/storage/toggle/sec_dep_jacket - name = "department jacket, security" - desc = "A cozy jacket in security's colors. Show your department pride!" - icon_state = "sec_dep_jacket" - item_state_slots = list(slot_r_hand_str = "sec_dep_jacket", slot_l_hand_str = "sec_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/engi_dep_jacket - name = "department jacket, engineering" - desc = "A cozy jacket in engineering's colors. Show your department pride!" - icon_state = "engi_dep_jacket" - item_state_slots = list(slot_r_hand_str = "engi_dep_jacket", slot_l_hand_str = "engi_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/supply_dep_jacket - name = "department jacket, supply" - desc = "A cozy jacket in supply's colors. Show your department pride!" - icon_state = "supply_dep_jacket" - item_state_slots = list(slot_r_hand_str = "supply_dep_jacket", slot_l_hand_str = "supply_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/sci_dep_jacket - name = "department jacket, science" - desc = "A cozy jacket in science's colors. Show your department pride!" - icon_state = "sci_dep_jacket" - item_state_slots = list(slot_r_hand_str = "sci_dep_jacket", slot_l_hand_str = "sci_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/med_dep_jacket - name = "department jacket, medical" - desc = "A cozy jacket in medical's colors. Show your department pride!" - icon_state = "med_dep_jacket" - item_state_slots = list(slot_r_hand_str = "med_dep_jacket", slot_l_hand_str = "med_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/light_jacket - name = "grey light jacket" - desc = "A light, cozy jacket. Now in grey." - icon_state = "grey_dep_jacket" - item_state_slots = list(slot_r_hand_str = "grey_dep_jacket", slot_l_hand_str = "grey_dep_jacket") - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/toggle/light_jacket/blue - name = "dark blue light jacket" - desc = "A light, cozy jacket. Now in dark blue." - icon_state = "blue_dep_jacket" - item_state_slots = list(slot_r_hand_str = "blue_dep_jacket", slot_l_hand_str = "blue_dep_jacket") - -/* - * Track Jackets - */ -/obj/item/clothing/suit/storage/toggle/track - name = "track jacket" - desc = "A track jacket, for the athletic." - icon_state = "trackjacket" - item_state_slots = list(slot_r_hand_str = "black_labcoat", slot_l_hand_str = "black_labcoat") - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - -/obj/item/clothing/suit/storage/toggle/track/blue - name = "blue track jacket" - icon_state = "trackjacketblue" - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - - -/obj/item/clothing/suit/storage/toggle/track/green - name = "green track jacket" - icon_state = "trackjacketgreen" - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - -/obj/item/clothing/suit/storage/toggle/track/red - name = "red track jacket" - icon_state = "trackjacketred" - item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") - -/obj/item/clothing/suit/storage/toggle/track/white - name = "white track jacket" - icon_state = "trackjacketwhite" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - -//Flannels - -/obj/item/clothing/suit/storage/flannel - name = "Flannel shirt" - desc = "A comfy, grey flannel shirt. Unleash your inner hipster." - icon_state = "flannel" - item_state_slots = list(slot_r_hand_str = "black_labcoat", slot_l_hand_str = "black_labcoat") - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - flags_inv = HIDEHOLSTER - var/rolled = 0 - var/tucked = 0 - var/buttoned = 0 - -/obj/item/clothing/suit/storage/flannel/verb/roll_sleeves() - set name = "Roll Sleeves" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) - return - if(usr.stat) - return - - if(rolled == 0) - rolled = 1 - body_parts_covered &= ~(ARMS) - to_chat(usr, "You roll up the sleeves of your [src].") - else - rolled = 0 - body_parts_covered = initial(body_parts_covered) - to_chat(usr, "You roll down the sleeves of your [src].") - update_icon() - -/obj/item/clothing/suit/storage/flannel/verb/tuck() - set name = "Toggle Shirt Tucking" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)||usr.stat) - return - - if(tucked == 0) - tucked = 1 - to_chat(usr, "You tuck in your your [src].") - else - tucked = 0 - to_chat(usr, "You untuck your [src].") - update_icon() - -/obj/item/clothing/suit/storage/flannel/verb/button() - set name = "Toggle Shirt Buttons" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)||usr.stat) - return - - if(buttoned == 0) - buttoned = 1 - flags_inv = HIDETIE|HIDEHOLSTER - to_chat(usr, "You button your [src].") - else - buttoned = 0 - flags_inv = HIDEHOLSTER - to_chat(usr, "You unbutton your [src].") - update_icon() - -/obj/item/clothing/suit/storage/flannel/update_icon() - icon_state = initial(icon_state) - if(rolled) - icon_state += "r" - if(tucked) - icon_state += "t" - if(buttoned) - icon_state += "b" - update_clothing_icon() - -/obj/item/clothing/suit/storage/flannel/red - desc = "A comfy, red flannel shirt. Unleash your inner hipster." - icon_state = "flannel_red" - item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") - -/obj/item/clothing/suit/storage/flannel/aqua - desc = "A comfy, aqua flannel shirt. Unleash your inner hipster." - icon_state = "flannel_aqua" - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - -/obj/item/clothing/suit/storage/flannel/brown - desc = "A comfy, brown flannel shirt. Unleash your inner hipster." - icon_state = "flannel_brown" - item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") - -/obj/item/clothing/suit/storage/flannel/recolour - desc = "A comfy, flannel shirt. Unleash your inner hipster." - icon_state = "flannel_recolour" - - -//Green Uniform - -/obj/item/clothing/suit/storage/toggle/greengov - name = "green formal jacket" - desc = "A sleek proper formal jacket with gold buttons." - icon_state = "suitjacket_green" - item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - flags_inv = HIDEHOLSTER - -/obj/item/clothing/suit/storage/snowsuit - name = "snowsuit" - desc = "A suit made to keep you nice and toasty on cold winter days. Or at least alive." - icon_state = "snowsuit" - item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDEHOLSTER - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - -/obj/item/clothing/suit/storage/snowsuit/command - name = "command snowsuit" - icon_state = "snowsuit_command" - -/obj/item/clothing/suit/storage/snowsuit/security - name = "security snowsuit" - icon_state = "snowsuit_security" - -/obj/item/clothing/suit/storage/snowsuit/medical - name = "medical snowsuit" - icon_state = "snowsuit_medical" - -/obj/item/clothing/suit/storage/snowsuit/engineering - name = "engineering snowsuit" - icon_state = "snowsuit_engineering" - -/obj/item/clothing/suit/storage/snowsuit/cargo - name = "cargo snowsuit" - icon_state = "snowsuit_cargo" - -/obj/item/clothing/suit/storage/snowsuit/science - name = "science snowsuit" - icon_state = "snowsuit_science" - -/obj/item/clothing/suit/caution - name = "wet floor sign" - desc = "Caution! Wet Floor!" - description_fluff = "Used by the janitor to passive-aggressively point at when you eventually slip on one of their mopped floors." - description_info = "Alt-click, or click in-hand to toggle the caution lights. It looks like you can wear it in your suit slot." - icon_state = "caution" - drop_sound = 'sound/items/drop/shoes.ogg' - force = 1 - throwforce = 3 - throw_speed = 2 - throw_range = 5 - w_class = 2 - body_parts_covered = UPPER_TORSO|LOWER_TORSO - attack_verb = list("warned", "cautioned", "smashed") - armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/clothing/suit/caution/attack_self() - toggle() - -/obj/item/clothing/suit/caution/AltClick() - toggle() - -/obj/item/clothing/suit/caution/proc/toggle() - if(!usr || usr.stat || usr.lying || usr.restrained() || !Adjacent(usr)) return - else if(src.icon_state == "caution") - src.icon_state = "caution_blinking" - src.item_state = "caution_blinking" - usr.show_message("You turn the wet floor sign on.") - playsound(src.loc, 'sound/machines/button.ogg', 30, 1) - else - src.icon_state = "caution" - src.item_state = "caution" - usr.show_message("You turn the wet floor sign off.") - update_clothing_icon() - -//Ruin Marine (Doom Marine) -/obj/item/clothing/suit/marine - name = "marine armor" - desc = "A set of marine prop armor from the popular game 'Ruin'." - icon_state = "marine" - body_parts_covered = FEET|LOWER_TORSO|UPPER_TORSO|LEGS - flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") - -//Cropped hoodies -/obj/item/clothing/suit/storage/croppedhoodie - name = "cropped hoodie" - desc = "This style of hoodie is sometimes worn by those who cannot fit, or choose not to hide their delectable bellies under the full, soft confines of a hoodie. The hood is cosmetic, and non-functional." - icon_state = "croppedhoodie" - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/croppedhoodie/croppier - name = "high cropped hoodie" - desc = "This style of hoodie is worn by those that wish to display ample amounts of midriff, or never threw out their childhood apparel. The hood is cosmetic, and non-functional." - icon_state = "croppierhoodie" - -/obj/item/clothing/suit/storage/croppedhoodie/croppierer - name = "very high cropped hoodie" - desc = "This style of hoodie is worn by those that wish to display ample amounts of underboob, and love the breeze. Comes with a free 'functionally_nude' sticker. The hood is cosmetic, and non-functional." - icon_state = "highcrophoodie" - -/obj/item/clothing/suit/storage/croppedhoodie/croppiest - name = "super cropped hoodie" - desc = "This style of hoodie is worn by those that have little respect for the concept of a hoodie. Often seen in nightclubs and your daughter's wardrdobe. The hood is cosmetic, and non-functional." - icon_state = "supercroppedhoodie" - -//Drive jacket -/obj/item/clothing/suit/storage/drive - name = "relatable jacket" - desc = "An all white jacket with a shine. It seems easy to identify with the wearer." - icon_state = "drivejacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/storage/toggle/moto_jacket - name = "motorcycle jacket" - desc = "A recreation of one of the famous Sol-based biwheeled driver assemblies. Patches on the back denote an AI-generated 'biker logo'. It looks unintelligible." - icon_state = "motojacket" - allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) //same as leather jackets - body_parts_covered = UPPER_TORSO|ARMS - -/obj/item/clothing/suit/storage/punkvest - name = "punk vest" - desc = "For the spiritual rebels that nevertheless wish to conform to standard goth trends. You're totally showing them your anti-authority spunk." - icon_state = "punkvest" - body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO +/* + * Contains: + * Lasertag + * Costume + * Misc + * Department Jackets + */ + +// -S2-note- Needs categorizing and sorting. + +/* + * Lasertag + */ + +#define LASER_TAG_HEALTH 3 //how many strikes do we get? + +/obj/item/clothing/suit/bluetag + name = "blue laser tag armor" + desc = "Blue Pride, Station Wide." + icon_state = "bluetag" + item_state_slots = list(slot_r_hand_str = "tdblue", slot_l_hand_str = "tdblue") + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO + allowed = list (/obj/item/weapon/gun/energy/lasertag/blue) + siemens_coefficient = 3.0 + var/lasertag_health = LASER_TAG_HEALTH + +/obj/item/clothing/suit/bluetag/sub + name = "Brigader Armor" + desc = "Replica armor commonly worn by Spacer Union Brigade members from the hit series Spacer Trail. Modified for Laser Tag (Blue Team)." + icon_state = "bluetag2" + +/obj/item/clothing/suit/redtag + name = "red laser tag armor" + desc = "Reputed to go faster." + icon_state = "redtag" + item_state_slots = list(slot_r_hand_str = "tdred", slot_l_hand_str = "tdred") + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO + allowed = list (/obj/item/weapon/gun/energy/lasertag/red) + siemens_coefficient = 3.0 + var/lasertag_health = LASER_TAG_HEALTH + +/obj/item/clothing/suit/redtag/dom + name = "Mu'tu'bi Armor" + desc = "Replica armor commonly worn by Dominion Of Mu'tu'bi soldiers from the hit series Spacer Trail. Modified for Laser Tag (Red Team)." + icon_state = "redtag2" + +/* + * 80s + */ + +/obj/item/clothing/suit/storage/puffyblue + name = "blue puffy coat" + desc = "A stylish, shiny, very blue puffer coat." + icon_state = "puffycoatblue" + item_state = "puffycoatblue" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + +/obj/item/clothing/suit/storage/puffypurple + name = "purple puffy coat" + desc = "A stylish, shiny, very purple puffer coat." + icon_state = "puffycoatpurple" + item_state = "puffycoatpurple" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + item_state_slots = list(slot_r_hand_str = "purple_labcoat", slot_l_hand_str = "purple_labcoat") + +/obj/item/clothing/suit/storage/puffyred + name = "crimson puffy coat" + desc = "A stylish, shiny, very crimson puffer coat." + icon_state = "puffycoatred" + item_state = "puffycoatred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0.7 + item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") + +/* + * Costume + */ + +/obj/item/clothing/suit/pirate + name = "pirate coat" + desc = "Yarr." + icon_state = "pirate" + item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/poncho + name = "poncho" + desc = "A simple, comfortable poncho." + icon = 'icons/inventory/suit/mob.dmi' + icon_state = "poncho" + item_state = "poncho" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_vr.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_vr.dmi', + ) + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/hgpirate + name = "pirate captain coat" + desc = "Yarr." + icon_state = "hgpirate" + item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") + flags_inv = HIDEJUMPSUIT + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/suit/cyborg_suit + name = "cyborg suit" + desc = "Suit for a cyborg costume." + icon_state = "death" + fire_resist = T0C+5200 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/greatcoat + name = "great coat" + desc = "A heavy great coat" + icon_state = "gentlecoat" + item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/johnny_coat + name = "johnny~~ coat" + desc = "Johnny~~" + icon_state = "gentlecoat" + item_state_slots = list(slot_r_hand_str = "johnny_coat", slot_l_hand_str = "johnny_coat") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/justice + name = "justice suit" + desc = "This pretty much looks ridiculous." + icon_state = "gentle_coat" + item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET + +/obj/item/clothing/suit/judgerobe + name = "judge's robe" + desc = "This robe commands authority." + icon_state = "judge" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/spacecash) + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/storage/apron/overalls + name = "coveralls" + desc = "A set of denim overalls." + icon_state = "overalls" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/suit/syndicatefake + name = "red space suit replica" + icon_state = "syndicate" + desc = "A plastic replica of a mercenary combat space suit, you'll look just like a real bloodthirsty mercenary in this! This is a toy, it is not made for use in space!" + w_class = ITEMSIZE_NORMAL + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/toy) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET + +/obj/item/clothing/suit/hastur + name = "Hastur's Robes" + desc = "Robes not meant to be worn by man" + icon_state = "hastur" + item_state_slots = list(slot_r_hand_str = "rad", slot_l_hand_str = "rad") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/imperium_monk + name = "Imperium monk" + desc = "Have YOU killed a xenos today?" + icon_state = "imperium_monk" + body_parts_covered = HEAD|UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/chickensuit + name = "Chicken Suit" + desc = "A suit made long ago by the ancient empire KFC." + icon_state = "chickensuit" + body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO|LEGS|FEET + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 2.0 + +/obj/item/clothing/suit/monkeysuit + name = "Monkey Suit" + desc = "A suit that looks like a primate" + icon_state = "monkeysuit" + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO|LEGS|FEET|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 2.0 + +/obj/item/clothing/suit/holidaypriest + name = "Holiday Priest" + desc = "This is a nice holiday my son." + icon_state = "holidaypriest" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/cardborg + name = "cardborg suit" + desc = "An ordinary cardboard box with holes cut in the sides." + icon_state = "cardborg" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/skeleton + name = "skeleton costume" + desc = "A body-tight costume with the human skeleton lined out on it." + icon_state = "skelecost" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|FEET|HANDS|EYES|HEAD|FACE + flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDEGLOVES|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "judge", slot_l_hand_str = "judge") + +/obj/item/clothing/suit/engicost + name = "sexy engineering voidsuit costume" + desc = "It's supposed to look like an engineering voidsuit... It doesn't look like it could protect from much radiation." + icon_state = "engicost" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET + flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "eng_voidsuit", slot_l_hand_str = "eng_voidsuit") + +/obj/item/clothing/suit/maxman + name = "doctor maxman costume" + desc = "A costume made to look like Dr. Maxman, the famous male-enhancement salesman. Complete with red do-rag and sleeveless labcoat." + icon_state = "maxman" + body_parts_covered = LOWER_TORSO|FEET|LEGS|HEAD + flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + +/obj/item/clothing/suit/iasexy + name = "sexy internal affairs suit" + desc = "Now where's your pen?~" + icon_state = "iacost" + body_parts_covered = UPPER_TORSO|FEET|LOWER_TORSO|EYES + flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + +/obj/item/clothing/suit/sexyminer + name = "sexy miner costume" + desc = "For when you need to get your rocks off." + icon_state = "sexyminer" + body_parts_covered = FEET|LOWER_TORSO|HEAD + flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "miner", slot_l_hand_str = "miner") + +/obj/item/clothing/suit/sumo + name = "inflatable sumo wrestler costume" + desc = "An inflated sumo wrestler costume. It's quite hot." + icon_state = "sumo" + body_parts_covered = FEET|LOWER_TORSO|UPPER_TORSO|LEGS|ARMS + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "classicponcho", slot_l_hand_str = "classicponcho") + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + +/obj/item/clothing/suit/hackercost + name = "classic hacker costume" + desc = "You would feel insanely cool wearing this." + icon_state = "hackercost" + body_parts_covered = FEET|LOWER_TORSO|UPPER_TORSO|LEGS|ARMS|EYES + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "leather_coat", slot_l_hand_str = "leather_coat") + +/obj/item/clothing/suit/lumber + name = "sexy lumberjack costume" + desc = "Smells of dusky pine. Includes chest hair and beard." + icon_state = "sexylumber" + body_parts_covered = FEET|LOWER_TORSO|FEET + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") + +/obj/item/clothing/suit/shrine_maiden + name = "shrine maiden costume" + desc = "Makes you want to exterminate some troublesome youkai." + icon_state = "shrine_maiden" + body_parts_covered = LOWER_TORSO|UPPER_TORSO|LEGS|ARMS + +/* + * Misc + */ + +/obj/item/clothing/suit/straight_jacket + name = "straight jacket" + desc = "A suit that completely restrains the wearer." + icon_state = "straight_jacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + + var/resist_time = 4800 // Eight minutes. + +/obj/item/clothing/suit/straight_jacket/attack_hand(mob/living/user as mob) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(src == H.wear_suit) + to_chat(H, "You need help taking this off!") + return + ..() + +/obj/item/clothing/suit/straight_jacket/equipped(var/mob/living/user,var/slot) + . = ..() + if(slot == slot_wear_suit) + user.drop_l_hand() + user.drop_r_hand() + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.drop_from_inventory(H.handcuffed) + +/obj/item/clothing/suit/ianshirt + name = "worn shirt" + desc = "A worn out, curiously comfortable t-shirt with a picture of Ian. You wouldn't go so far as to say it feels like being hugged when you wear it but it's pretty close. Good for sleeping in." + icon_state = "ianshirt" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") //placeholder -S2- + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + +//nerdy shirt +/obj/item/clothing/suit/nerdshirt + name = "nerdy shirt" + desc = "A comfy white t-shirt with a picture of a cartoon hedgehog on it. Although clean, it still seems like the wearer should be embarrassed for owning it." + icon_state = "nerdshirt" + +/* + * Kimonos + */ + +/obj/item/clothing/suit/kimono + name = "traditional kimono" + desc = "A traditional Japanese kimono." + icon_state = "kimono" + addblends = "kimono_a" + +/obj/item/clothing/suit/kamishimo + name = "traditional kamishimo" + desc = "Traditional Japanese menswear." + icon_state = "kamishimo" + addblends = "kamishimo_a" + +/obj/item/clothing/suit/kimono/red + name = "red kimono" + icon_state = "kimono_red" + +/obj/item/clothing/suit/kimono/orange + name = "orange kimono" + icon_state = "kimono_orange" + +/obj/item/clothing/suit/kimono/yellow + name = "yellow kimono" + icon_state = "kimono_yellow" + +/obj/item/clothing/suit/kimono/green + name = "green kimono" + icon_state = "kimono_green" + +/obj/item/clothing/suit/kimono/blue + name = "blue kimono" + icon_state = "kimono_blue" + +/obj/item/clothing/suit/kimono/purple + name = "purple kimono" + icon_state = "kimono_purple" + +/obj/item/clothing/suit/kimono/violet + name = "violet kimono" + icon_state = "kimono_violet" + +/obj/item/clothing/suit/kimono/pink + name = "pink kimono" + icon_state = "kimono_pink" + +/obj/item/clothing/suit/kimono/earth + name = "earth kimono" + icon_state = "kimono_earth" + +/* + * Coats + */ + +/obj/item/clothing/suit/leathercoat + name = "leather coat" + desc = "A long, thick black leather coat." + icon_state = "leathercoat_alt" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/leathercoat/sec + name = "leather coat" + desc = "A long, thick black leather coat." + icon_state = "leathercoat_sec" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/browncoat + name = "brown leather coat" + desc = "A long, brown leather coat." + icon_state = "browncoat" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/neocoat + name = "black coat" + desc = "A flowing, black coat." + icon_state = "neocoat" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/customs + name = "customs jacket" + desc = "A standard SolGov Customs formal jacket." + icon_state = "customs_jacket" + item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/greyjacket + name = "grey jacket" + desc = "A fancy twead grey jacket." + icon_state = "gentlecoat" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/trench + name = "brown trenchcoat" + desc = "A rugged canvas trenchcoat, designed and created by TX Fabrication Corp. The coat appears to have its kevlar lining removed." + icon_state = "detective" + blood_overlay_type = "coat" + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter,/obj/item/device/taperecorder,/obj/item/device/uv_light) + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/trench/grey + name = "grey trenchcoat" + icon_state = "detective2" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/peacoat + name = "peacoat" + desc = "A well-tailored, stylish peacoat." + icon_state = "peacoat" + addblends = "peacoat_a" + item_state_slots = list(slot_r_hand_str = "peacoat", slot_l_hand_str = "peacoat") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/duster + name = "duster" + desc = "A duster is a light, loose-fitting long coat. Dusters are meant to protect your clothing from dust and rain." + icon_state = "duster" + blood_overlay_type = "coat" + allowed = list(/obj/item/weapon/tank/emergency/oxygen, /obj/item/device/flashlight,/obj/item/weapon/gun/energy,/obj/item/weapon/gun/projectile,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/storage/fancy/cigarettes,/obj/item/weapon/flame/lighter) + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/cardigan + name = "cardigan" + desc = "A cozy cardigan in a classic style." + icon_state = "cardigan" + addblends = "cardigan_a" + flags_inv = HIDEHOLSTER + +/* + * stripper + */ +/obj/item/clothing/suit/stripper/stripper_pink + name = "pink skimpy dress" + desc = "A rather skimpy pink dress." + icon_state = "stripper_p_over" + item_state_slots = list(slot_r_hand_str = "pink_labcoat", slot_l_hand_str = "pink_labcoat") + siemens_coefficient = 1 + +/obj/item/clothing/suit/stripper/stripper_green + name = "green skimpy dress" + desc = "A rather skimpy green dress." + icon_state = "stripper_g_over" + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + siemens_coefficient = 1 + +/obj/item/clothing/suit/xenos + name = "xenos suit" + desc = "A suit made out of chitinous alien hide." + icon_state = "xenos" + item_state_slots = list(slot_r_hand_str = "black_suit", slot_l_hand_str = "black_suit") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 2.0 + +/obj/item/clothing/suit/jacket/puffer + name = "puffer jacket" + desc = "A thick jacket with a rubbery, water-resistant shell." + icon_state = "pufferjacket" + item_state_slots = list(slot_r_hand_str = "chainmail", slot_l_hand_str = "chainmail") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/jacket/puffer/vest + name = "puffer vest" + desc = "A thick vest with a rubbery, water-resistant shell." + icon_state = "puffervest" + item_state_slots = list(slot_r_hand_str = "chainmail", slot_l_hand_str = "chainmail") + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/suit/storage/miljacket + name = "military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable." + icon_state = "militaryjacket_nobadge" + item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/alt + name = "military jacket, alternate" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one has some extra badges on it." + icon_state = "militaryjacket_badge" + item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/green + name = "green military jacket" + desc = "A dark but rather high-saturation green canvas jacket. Feels sturdy, yet comfortable." + icon_state = "militaryjacket_green" + item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/tan + name = "tan military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Now in sandy tans for desert fans." + icon_state = "militaryjacket_tan" + item_state_slots = list(slot_r_hand_str = "suit_orange", slot_l_hand_str = "suit_orange") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/grey + name = "grey military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one's in urban grey." + icon_state = "militaryjacket_grey" + item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/navy + name = "navy military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Dark navy, this one is." + icon_state = "militaryjacket_navy" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/black + name = "black military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Now in tactical black." + icon_state = "militaryjacket_black" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/white + name = "white military jacket" + desc = "A white canvas jacket. Don't wear this for walks in the snow, it won't keep you warm - it'll just make it harder to find your frozen corpse." + icon_state = "militaryjacket_white" + item_state_slots = list(slot_r_hand_str = "med_dep_jacket", slot_l_hand_str = "med_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/bomber + name = "bomber jacket" + desc = "A thick, well-worn WW2 leather bomber jacket." + icon_state = "bomber" + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|ARMS + min_cold_protection_temperature = T0C - 20 + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/storage/toggle/bomber/retro + name = "retro bomber jacket" + desc = "A retro style, fur-lined leather bomber jacket that invokes the early days of space exploration when spacemen were spacemen, and laser guns had funny little antennae on them." + icon_state = "retrojacket" + +/obj/item/clothing/suit/storage/bomber/alt + name = "bomber jacket" + desc = "A thick, well-worn WW2 leather bomber jacket." + icon_state = "bomberjacket_new" + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|ARMS + min_cold_protection_temperature = T0C - 20 + siemens_coefficient = 0.7 + +/obj/item/clothing/suit/storage/toggle/leather_jacket + name = "leather jacket" + desc = "A black leather coat." + icon_state = "leather_jacket" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/leather_jacket/sleeveless + name = "leather vest" + desc = "A black leather vest." + icon_state = "leather_jacket_sleeveless" + body_parts_covered = UPPER_TORSO + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + +/obj/item/clothing/suit/storage/leather_jacket_alt + name = "leather vest" + desc = "A black leather vest." + icon_state = "leather_jacket_alt" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/toggle/leather_jacket/nanotrasen + desc = "A black leather coat. A corporate logo is proudly displayed on the back." + icon_state = "leather_jacket_nt" + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + +/obj/item/clothing/suit/storage/toggle/leather_jacket/nanotrasen/sleeveless + name = "leather vest" + desc = "A black leather vest. A corporate logo is proudly displayed on the back." + icon_state = "leather_jacket_nt_sleeveless" + body_parts_covered = UPPER_TORSO + item_state_slots = list(slot_r_hand_str = "leather_jacket", slot_l_hand_str = "leather_jacket") + +//This one has buttons for some reason +/obj/item/clothing/suit/storage/toggle/brown_jacket + name = "brown jacket" + desc = "A brown leather coat." + icon_state = "brown_jacket" + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/brown_jacket/sleeveless + name = "brown vest" + desc = "A brown leather vest." + icon_state = "brown_jacket_sleeveless" + body_parts_covered = UPPER_TORSO + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + +/obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen + desc = "A brown leather coat. A corporate logo is proudly displayed on the back." + icon_state = "brown_jacket_nt" + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + +/obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen/sleeveless + name = "brown vest" + desc = "A brown leather vest. A corporate logo is proudly displayed on the back." + icon_state = "brown_jacket_nt_sleeveless" + body_parts_covered = UPPER_TORSO + item_state_slots = list(slot_r_hand_str = "brown_jacket", slot_l_hand_str = "brown_jacket") + +/obj/item/clothing/suit/storage/toggle/denim_jacket + name = "denim jacket" + desc = "A denim coat." + icon_state = "denim_jacket" + item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/denim_jacket/sleeveless + name = "denim vest" + desc = "A denim vest." + icon_state = "denim_jacket_sleeveless" + body_parts_covered = UPPER_TORSO + item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") + +/obj/item/clothing/suit/storage/toggle/denim_jacket/nanotrasen + desc = "A denim coat. A corporate logo is proudly displayed on the back." + icon_state = "denim_jacket_nt" + item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") + +/obj/item/clothing/suit/storage/toggle/denim_jacket/nanotrasen/sleeveless + name = "denim vest" + desc = "A denim vest. A corporate logo is proudly displayed on the back." + icon_state = "denim_jacket_nt_sleeveless" + body_parts_covered = UPPER_TORSO + item_state_slots = list(slot_r_hand_str = "denim_jacket", slot_l_hand_str = "denim_jacket") + +/obj/item/clothing/suit/storage/toggle/hoodie + name = "grey hoodie" + desc = "A warm, grey sweatshirt." + icon_state = "grey_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") + min_cold_protection_temperature = T0C - 20 + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/hoodie/black + name = "black hoodie" + desc = "A warm, black sweatshirt." + icon_state = "black_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + +/obj/item/clothing/suit/storage/toggle/hoodie/red + name = "red hoodie" + desc = "A warm, red sweatshirt." + icon_state = "red_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") + +/obj/item/clothing/suit/storage/toggle/hoodie/blue + name = "blue hoodie" + desc = "A warm, blue sweatshirt." + icon_state = "blue_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") + +/obj/item/clothing/suit/storage/toggle/hoodie/green + name = "green hoodie" + desc = "A warm, green sweatshirt." + icon_state = "green_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") + +/obj/item/clothing/suit/storage/toggle/hoodie/orange + name = "orange hoodie" + desc = "A warm, orange sweatshirt." + icon_state = "orange_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_orange", slot_l_hand_str = "suit_orange") + +/obj/item/clothing/suit/storage/toggle/hoodie/yellow + name = "yellow hoodie" + desc = "A warm, yellow sweatshirt." + icon_state = "yellow_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_yellow", slot_l_hand_str = "suit_yellow") + +/obj/item/clothing/suit/storage/toggle/hoodie/cti + name = "CTI hoodie" + desc = "A warm, black sweatshirt. It bears the letters ‘CTI’ on the back, a lettering to the prestigious university in Tau Ceti, Ceti Technical Institute. There is a blue supernova embroidered on the front, the emblem of CTI." + icon_state = "cti_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + +/obj/item/clothing/suit/storage/toggle/hoodie/mu + name = "mojave university hoodie" + desc = "A warm, gray sweatshirt. It bears the letters ‘MU’ on the front, a lettering to the well-known public college, Mojave University." + icon_state = "mu_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") + +/obj/item/clothing/suit/storage/toggle/hoodie/nt + name = "NT hoodie" + desc = "A warm, blue sweatshirt. It proudly bears the silver NanoTrasen insignia lettering on the back. The edges are trimmed with silver." + icon_state = "nt_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_blue", slot_l_hand_str = "suit_blue") + +/obj/item/clothing/suit/storage/toggle/hoodie/smw + name = "Space Mountain Wind hoodie" + desc = "A warm, black sweatshirt. It has the logo for the popular softdrink Space Mountain Wind on both the front and the back." + icon_state = "smw_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + +/obj/item/clothing/suit/storage/toggle/hoodie/nrti + name = "New Reykjavik Technical Institute hoodie" + desc = "A warm, gray sweatshirt. It bears the letters ‘NRT’ on the back, in reference to Sif's premiere technical institute." + icon_state = "nrti_hoodie" + item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") + +/obj/item/clothing/suit/whitedress + name = "white dress" + desc = "A fancy dress." + icon_state = "white_dress" + item_state_slots = list(slot_r_hand_str = "white_dress", slot_l_hand_str = "white_dress") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/varsity + name = "black varsity jacket" + desc = "A favorite of jocks everywhere from Sol to Nyx." + icon_state = "varsity" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + flags_inv = HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/varsity/red + name = "red varsity jacket" + icon_state = "varsity_red" + +/obj/item/clothing/suit/varsity/purple + name = "purple varsity jacket" + icon_state = "varsity_purple" + +/obj/item/clothing/suit/varsity/green + name = "green varsity jacket" + icon_state = "varsity_green" + +/obj/item/clothing/suit/varsity/blue + name = "blue varsity jacket" + icon_state = "varsity_blue" + +/obj/item/clothing/suit/varsity/brown + name = "brown varsity jacket" + icon_state = "varsity_brown" + +/obj/item/clothing/suit/runner + name = "runner jacket" + desc = "A yellow sports jacket with white trim and an unfolded collar." + icon_state = "runner" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + item_state_slots = list(slot_r_hand_str = "suit_red", slot_l_hand_str = "suit_red") + flags_inv = HIDEHOLSTER + +/* + * Department Jackets + */ +/obj/item/clothing/suit/storage/toggle/sec_dep_jacket + name = "department jacket, security" + desc = "A cozy jacket in security's colors. Show your department pride!" + icon_state = "sec_dep_jacket" + item_state_slots = list(slot_r_hand_str = "sec_dep_jacket", slot_l_hand_str = "sec_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/engi_dep_jacket + name = "department jacket, engineering" + desc = "A cozy jacket in engineering's colors. Show your department pride!" + icon_state = "engi_dep_jacket" + item_state_slots = list(slot_r_hand_str = "engi_dep_jacket", slot_l_hand_str = "engi_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/supply_dep_jacket + name = "department jacket, supply" + desc = "A cozy jacket in supply's colors. Show your department pride!" + icon_state = "supply_dep_jacket" + item_state_slots = list(slot_r_hand_str = "supply_dep_jacket", slot_l_hand_str = "supply_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/sci_dep_jacket + name = "department jacket, science" + desc = "A cozy jacket in science's colors. Show your department pride!" + icon_state = "sci_dep_jacket" + item_state_slots = list(slot_r_hand_str = "sci_dep_jacket", slot_l_hand_str = "sci_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/med_dep_jacket + name = "department jacket, medical" + desc = "A cozy jacket in medical's colors. Show your department pride!" + icon_state = "med_dep_jacket" + item_state_slots = list(slot_r_hand_str = "med_dep_jacket", slot_l_hand_str = "med_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/light_jacket + name = "grey light jacket" + desc = "A light, cozy jacket. Now in grey." + icon_state = "grey_dep_jacket" + item_state_slots = list(slot_r_hand_str = "grey_dep_jacket", slot_l_hand_str = "grey_dep_jacket") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/toggle/light_jacket/blue + name = "dark blue light jacket" + desc = "A light, cozy jacket. Now in dark blue." + icon_state = "blue_dep_jacket" + item_state_slots = list(slot_r_hand_str = "blue_dep_jacket", slot_l_hand_str = "blue_dep_jacket") + +/* + * Track Jackets + */ +/obj/item/clothing/suit/storage/toggle/track + name = "track jacket" + desc = "A track jacket, for the athletic." + icon_state = "trackjacket" + item_state_slots = list(slot_r_hand_str = "black_labcoat", slot_l_hand_str = "black_labcoat") + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + +/obj/item/clothing/suit/storage/toggle/track/blue + name = "blue track jacket" + icon_state = "trackjacketblue" + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + + +/obj/item/clothing/suit/storage/toggle/track/green + name = "green track jacket" + icon_state = "trackjacketgreen" + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + +/obj/item/clothing/suit/storage/toggle/track/red + name = "red track jacket" + icon_state = "trackjacketred" + item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") + +/obj/item/clothing/suit/storage/toggle/track/white + name = "white track jacket" + icon_state = "trackjacketwhite" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + +//Flannels + +/obj/item/clothing/suit/storage/flannel + name = "Flannel shirt" + desc = "A comfy, grey flannel shirt. Unleash your inner hipster." + icon_state = "flannel" + item_state_slots = list(slot_r_hand_str = "black_labcoat", slot_l_hand_str = "black_labcoat") + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + flags_inv = HIDEHOLSTER + var/rolled = 0 + var/tucked = 0 + var/buttoned = 0 + +/obj/item/clothing/suit/storage/flannel/verb/roll_sleeves() + set name = "Roll Sleeves" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) + return + if(usr.stat) + return + + if(rolled == 0) + rolled = 1 + body_parts_covered &= ~(ARMS) + to_chat(usr, "You roll up the sleeves of your [src].") + else + rolled = 0 + body_parts_covered = initial(body_parts_covered) + to_chat(usr, "You roll down the sleeves of your [src].") + update_icon() + +/obj/item/clothing/suit/storage/flannel/verb/tuck() + set name = "Toggle Shirt Tucking" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)||usr.stat) + return + + if(tucked == 0) + tucked = 1 + to_chat(usr, "You tuck in your your [src].") + else + tucked = 0 + to_chat(usr, "You untuck your [src].") + update_icon() + +/obj/item/clothing/suit/storage/flannel/verb/button() + set name = "Toggle Shirt Buttons" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)||usr.stat) + return + + if(buttoned == 0) + buttoned = 1 + flags_inv = HIDETIE|HIDEHOLSTER + to_chat(usr, "You button your [src].") + else + buttoned = 0 + flags_inv = HIDEHOLSTER + to_chat(usr, "You unbutton your [src].") + update_icon() + +/obj/item/clothing/suit/storage/flannel/update_icon() + icon_state = initial(icon_state) + if(rolled) + icon_state += "r" + if(tucked) + icon_state += "t" + if(buttoned) + icon_state += "b" + update_clothing_icon() + +/obj/item/clothing/suit/storage/flannel/red + desc = "A comfy, red flannel shirt. Unleash your inner hipster." + icon_state = "flannel_red" + item_state_slots = list(slot_r_hand_str = "red_labcoat", slot_l_hand_str = "red_labcoat") + +/obj/item/clothing/suit/storage/flannel/aqua + desc = "A comfy, aqua flannel shirt. Unleash your inner hipster." + icon_state = "flannel_aqua" + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + +/obj/item/clothing/suit/storage/flannel/brown + desc = "A comfy, brown flannel shirt. Unleash your inner hipster." + icon_state = "flannel_brown" + item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") + +/obj/item/clothing/suit/storage/flannel/recolour + desc = "A comfy, flannel shirt. Unleash your inner hipster." + icon_state = "flannel_recolour" + + +//Green Uniform + +/obj/item/clothing/suit/storage/toggle/greengov + name = "green formal jacket" + desc = "A sleek proper formal jacket with gold buttons." + icon_state = "suitjacket_green" + item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/snowsuit + name = "snowsuit" + desc = "A suit made to keep you nice and toasty on cold winter days. Or at least alive." + icon_state = "snowsuit" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + +/obj/item/clothing/suit/storage/snowsuit/command + name = "command snowsuit" + icon_state = "snowsuit_command" + +/obj/item/clothing/suit/storage/snowsuit/security + name = "security snowsuit" + icon_state = "snowsuit_security" + +/obj/item/clothing/suit/storage/snowsuit/medical + name = "medical snowsuit" + icon_state = "snowsuit_medical" + +/obj/item/clothing/suit/storage/snowsuit/engineering + name = "engineering snowsuit" + icon_state = "snowsuit_engineering" + +/obj/item/clothing/suit/storage/snowsuit/cargo + name = "cargo snowsuit" + icon_state = "snowsuit_cargo" + +/obj/item/clothing/suit/storage/snowsuit/science + name = "science snowsuit" + icon_state = "snowsuit_science" + +/obj/item/clothing/suit/caution + name = "wet floor sign" + desc = "Caution! Wet Floor!" + description_fluff = "Used by the janitor to passive-aggressively point at when you eventually slip on one of their mopped floors." + description_info = "Alt-click, or click in-hand to toggle the caution lights. It looks like you can wear it in your suit slot." + icon_state = "caution" + drop_sound = 'sound/items/drop/shoes.ogg' + force = 1 + throwforce = 3 + throw_speed = 2 + throw_range = 5 + w_class = 2 + body_parts_covered = UPPER_TORSO|LOWER_TORSO + attack_verb = list("warned", "cautioned", "smashed") + armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/suit/caution/attack_self() + toggle() + +/obj/item/clothing/suit/caution/AltClick() + toggle() + +/obj/item/clothing/suit/caution/proc/toggle() + if(!usr || usr.stat || usr.lying || usr.restrained() || !Adjacent(usr)) return + else if(src.icon_state == "caution") + src.icon_state = "caution_blinking" + src.item_state = "caution_blinking" + usr.show_message("You turn the wet floor sign on.") + playsound(src.loc, 'sound/machines/button.ogg', 30, 1) + else + src.icon_state = "caution" + src.item_state = "caution" + usr.show_message("You turn the wet floor sign off.") + update_clothing_icon() + +//Ruin Marine (Doom Marine) +/obj/item/clothing/suit/marine + name = "marine armor" + desc = "A set of marine prop armor from the popular game 'Ruin'." + icon_state = "marine" + body_parts_covered = FEET|LOWER_TORSO|UPPER_TORSO|LEGS + flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + item_state_slots = list(slot_r_hand_str = "green_labcoat", slot_l_hand_str = "green_labcoat") + +//Cropped hoodies +/obj/item/clothing/suit/storage/croppedhoodie + name = "cropped hoodie" + desc = "This style of hoodie is sometimes worn by those who cannot fit, or choose not to hide their delectable bellies under the full, soft confines of a hoodie. The hood is cosmetic, and non-functional." + icon_state = "croppedhoodie" + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/croppedhoodie/croppier + name = "high cropped hoodie" + desc = "This style of hoodie is worn by those that wish to display ample amounts of midriff, or never threw out their childhood apparel. The hood is cosmetic, and non-functional." + icon_state = "croppierhoodie" + +/obj/item/clothing/suit/storage/croppedhoodie/croppierer + name = "very high cropped hoodie" + desc = "This style of hoodie is worn by those that wish to display ample amounts of underboob, and love the breeze. Comes with a free 'functionally_nude' sticker. The hood is cosmetic, and non-functional." + icon_state = "highcrophoodie" + +/obj/item/clothing/suit/storage/croppedhoodie/croppiest + name = "super cropped hoodie" + desc = "This style of hoodie is worn by those that have little respect for the concept of a hoodie. Often seen in nightclubs and your daughter's wardrdobe. The hood is cosmetic, and non-functional." + icon_state = "supercroppedhoodie" + +//Drive jacket +/obj/item/clothing/suit/storage/drive + name = "relatable jacket" + desc = "An all white jacket with a shine. It seems easy to identify with the wearer." + icon_state = "drivejacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/storage/toggle/moto_jacket + name = "motorcycle jacket" + desc = "A recreation of one of the famous Sol-based biwheeled driver assemblies. Patches on the back denote an AI-generated 'biker logo'. It looks unintelligible." + icon_state = "motojacket" + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) //same as leather jackets + body_parts_covered = UPPER_TORSO|ARMS + +/obj/item/clothing/suit/storage/punkvest + name = "punk vest" + desc = "For the spiritual rebels that nevertheless wish to conform to standard goth trends. You're totally showing them your anti-authority spunk." + icon_state = "punkvest" + body_parts_covered = UPPER_TORSO|ARMS|LOWER_TORSO diff --git a/code/modules/clothing/suits/miscellaneous_vr.dm b/code/modules/clothing/suits/miscellaneous_vr.dm index 2a979f41d90..bdef2b3e0c5 100644 --- a/code/modules/clothing/suits/miscellaneous_vr.dm +++ b/code/modules/clothing/suits/miscellaneous_vr.dm @@ -151,7 +151,7 @@ // Altevian admiralty stuff /obj/item/clothing/suit/captunic/capjacket/altevian_admiral // Subtype of capjacket because A) it makes sense and B) conviniently matching stats name = "altevian officer's suit" - desc = "Formal attire worn by officers and bridge crew from the Altevian Hegemony. The material is made of high quality silk and provides maximum comfort and breathing room for those that are working double shifts all the time." + desc = "A formal jacket worn by the bridge and command crew from the Altevian Hegemony. The material is of high quality silk, and provides maximum comfort and breathing room." icon_state = "altevian-admiral" species_restricted = list(SPECIES_ALTEVIAN) @@ -174,4 +174,4 @@ /obj/item/clothing/suit/captunic/capjacket/altevian_admiral/yellow name = "yellow altevian officer's suit" - icon_state = "altevian-admiral-yellow" \ No newline at end of file + icon_state = "altevian-admiral-yellow" diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 31f550da77d..f465941ed7e 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -1,118 +1,118 @@ -/* - * Contains: - * Fire protection - * Bomb protection - * Radiation protection - */ - -/* - * Fire protection - */ - -/obj/item/clothing/suit/fire - name = "emergency firesuit" - desc = "A suit that protects against fire and heat." - icon_state = "firesuit" - item_state_slots = list(slot_r_hand_str = "black_suit", slot_l_hand_str = "black_suit") - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/extinguisher) - slowdown = 1.0 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - item_flags = 0 - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_pressure_protection = 0.2 * ONE_ATMOSPHERE - max_pressure_protection = 20 * ONE_ATMOSPHERE - -/obj/item/clothing/suit/fire/firefighter - name = "firesuit" - icon_state = "firesuit2" - max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000 - -/obj/item/clothing/suit/fire/heavy - name = "atmospheric firesuit" - desc = "A suit that protects against extreme fire and heat." - icon_state = "atmos_firesuit" - max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000 - slowdown = 1.5 - -/* - * Bomb protection - */ -/obj/item/clothing/head/bomb_hood - name = "bomb hood" - desc = "Use in case of bomb." - icon_state = "bombsuit" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR - body_parts_covered = HEAD|FACE|EYES - siemens_coefficient = 0 - -/obj/item/clothing/suit/bomb_suit - name = "bomb suit" - desc = "A suit designed for safety when handling explosives." - icon_state = "bombsuit" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - slowdown = 2 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) - flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - body_parts_covered = HEAD - -/obj/item/clothing/suit/bomb_suit/security - icon_state = "bombsuitsec" - allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -/* - * Radiation protection - */ -/obj/item/clothing/head/radiation - name = "Radiation hood" - icon_state = "rad" - desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" - flags_inv = BLOCKHAIR - item_flags = THICKMATERIAL - body_parts_covered = HEAD|FACE|EYES - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) - -/obj/item/clothing/suit/radiation - name = "Radiation suit" - desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." - icon_state = "rad" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/radiation,/obj/item/clothing/mask/gas) - slowdown = 1.5 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) - flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER - item_flags = THICKMATERIAL - -/obj/item/clothing/suit/radiation/teshari - name = "Small radiation suit" - desc = "A specialist suit that protects against radiation, designed specifically for use by Teshari. Made to order by Aether." - icon = 'icons/inventory/suit/item_teshari.dmi' - icon_override = 'icons/inventory/suit/mob_teshari.dmi' - icon_state = "rad_fitted" - species_restricted = list(SPECIES_TESHARI) - -/obj/item/clothing/head/radiation/teshari - name = "Small radiation hood" - desc = "A specialist hood with radiation protective properties, designed specifically for use by Teshari. Made to order by Aether." - icon = 'icons/inventory/suit/item_teshari.dmi' - icon_override = 'icons/inventory/head/mob_teshari.dmi' - icon_state = "rad_fitted" +/* + * Contains: + * Fire protection + * Bomb protection + * Radiation protection + */ + +/* + * Fire protection + */ + +/obj/item/clothing/suit/fire + name = "emergency firesuit" + desc = "A suit that protects against fire and heat." + icon_state = "firesuit" + item_state_slots = list(slot_r_hand_str = "black_suit", slot_l_hand_str = "black_suit") + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/extinguisher) + slowdown = 1.0 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + item_flags = 0 + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_pressure_protection = 0.2 * ONE_ATMOSPHERE + max_pressure_protection = 20 * ONE_ATMOSPHERE + +/obj/item/clothing/suit/fire/firefighter + name = "firesuit" + icon_state = "firesuit2" + max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000 + +/obj/item/clothing/suit/fire/heavy + name = "atmospheric firesuit" + desc = "A suit that protects against extreme fire and heat." + icon_state = "atmos_firesuit" + max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000 + slowdown = 1.5 + +/* + * Bomb protection + */ +/obj/item/clothing/head/bomb_hood + name = "bomb hood" + desc = "Use in case of bomb." + icon_state = "bombsuit" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR + body_parts_covered = HEAD|FACE|EYES + siemens_coefficient = 0 + +/obj/item/clothing/suit/bomb_suit + name = "bomb suit" + desc = "A suit designed for safety when handling explosives." + icon_state = "bombsuit" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + slowdown = 2 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0) + flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_HEAT_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + body_parts_covered = HEAD + +/obj/item/clothing/suit/bomb_suit/security + icon_state = "bombsuitsec" + allowed = list(/obj/item/weapon/gun/energy,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +/* + * Radiation protection + */ +/obj/item/clothing/head/radiation + name = "Radiation hood" + icon_state = "rad" + desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" + flags_inv = BLOCKHAIR + item_flags = THICKMATERIAL + body_parts_covered = HEAD|FACE|EYES + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) + +/obj/item/clothing/suit/radiation + name = "Radiation suit" + desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." + icon_state = "rad" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/head/radiation,/obj/item/clothing/mask/gas) + slowdown = 1.5 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100) + flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + item_flags = THICKMATERIAL + +/obj/item/clothing/suit/radiation/teshari + name = "Small radiation suit" + desc = "A specialist suit that protects against radiation, designed specifically for use by Teshari. Made to order by Aether." + icon = 'icons/inventory/suit/item_teshari.dmi' + icon_override = 'icons/inventory/suit/mob_teshari.dmi' + icon_state = "rad_fitted" + species_restricted = list(SPECIES_TESHARI) + +/obj/item/clothing/head/radiation/teshari + name = "Small radiation hood" + desc = "A specialist hood with radiation protective properties, designed specifically for use by Teshari. Made to order by Aether." + icon = 'icons/inventory/suit/item_teshari.dmi' + icon_override = 'icons/inventory/head/mob_teshari.dmi' + icon_state = "rad_fitted" species_restricted = list(SPECIES_TESHARI) \ No newline at end of file diff --git a/code/modules/clothing/suits/utility_vr.dm b/code/modules/clothing/suits/utility_vr.dm index 9302fcf11b8..17f796ca85c 100644 --- a/code/modules/clothing/suits/utility_vr.dm +++ b/code/modules/clothing/suits/utility_vr.dm @@ -1,33 +1,33 @@ -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - body_parts_covered = HEAD - -/obj/item/clothing/suit/storage/toggle/paramedic - name = "paramedic vest" - desc = "A vest that protects against minor chemical spills." - icon = 'icons/inventory/suit/item_vr.dmi' - icon_override = 'icons/inventory/suit/mob_vr.dmi' - icon_state = "paramedic-vest" - item_state = "paramedic-vest" - item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO - flags_inv = HIDEHOLSTER - allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) - -/obj/item/clothing/head/radiation - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi', - SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi', - SPECIES_WEREBEAST = 'icons/inventory/head/mob_vr_werebeast.dmi' - ) - -/obj/item/clothing/suit/radiation - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/suit/mob_vr_teshari.dmi', - SPECIES_VOX = 'icons/inventory/suit/mob_vox.dmi', - SPECIES_WEREBEAST = 'icons/inventory/suit/mob_vr_werebeast.dmi' - ) - - +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + body_parts_covered = HEAD + +/obj/item/clothing/suit/storage/toggle/paramedic + name = "paramedic vest" + desc = "A vest that protects against minor chemical spills." + icon = 'icons/inventory/suit/item_vr.dmi' + icon_override = 'icons/inventory/suit/mob_vr.dmi' + icon_state = "paramedic-vest" + item_state = "paramedic-vest" + item_state_slots = list(slot_r_hand_str = "blue_labcoat", slot_l_hand_str = "blue_labcoat") + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO + flags_inv = HIDEHOLSTER + allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) + +/obj/item/clothing/head/radiation + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi', + SPECIES_VOX = 'icons/inventory/head/mob_vox.dmi', + SPECIES_WEREBEAST = 'icons/inventory/head/mob_vr_werebeast.dmi' + ) + +/obj/item/clothing/suit/radiation + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/suit/mob_vr_teshari.dmi', + SPECIES_VOX = 'icons/inventory/suit/mob_vox.dmi', + SPECIES_WEREBEAST = 'icons/inventory/suit/mob_vr_werebeast.dmi' + ) + + diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 24e0cdddbfa..903fca84103 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -1,129 +1,129 @@ -/obj/item/clothing/head/wizard - name = "wizard hat" - desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." - icon_state = "wizard" - //Not given any special protective value since the magic robes are full-body protection --NEO - siemens_coefficient = 0.8 - body_parts_covered = 0 - wizard_garb = 1 - -/obj/item/clothing/head/wizard/red - name = "red wizard hat" - desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." - icon_state = "redwizard" - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/fake - name = "wizard hat" - desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." - icon_state = "wizard-fake" - body_parts_covered = HEAD|FACE - siemens_coefficient = 1 - -/obj/item/clothing/head/wizard/fake/realistic - desc = "A cool-looking 'magic' hat." - icon_state = "wizard" - body_parts_covered = HEAD - -/obj/item/clothing/head/wizard/fake/realistic/colorable - desc = "A cool-looking 'magic' hat." - icon_state = "wizard-white" - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi' - ) - -/obj/item/clothing/head/wizard/marisa - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/magus - name = "Magus Helm" - desc = "A mysterious helmet that hums with an unearthly power" - icon_state = "magus" - siemens_coefficient = 0.8 - body_parts_covered = HEAD|FACE|EYES - -/obj/item/clothing/head/wizard/amp - name = "psychic amplifier" - desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." - icon_state = "amp" - siemens_coefficient = 0.8 - -/obj/item/clothing/head/wizard/cap - name = "Gentlemans Cap" - desc = "A checkered gray flat cap woven together with the rarest of threads." - icon_state = "gentcap" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - siemens_coefficient = 0.8 - -/obj/item/clothing/suit/wizrobe - name = "wizard robe" - desc = "A magnificant, gem-lined robe that seems to radiate power." - icon_state = "wizard" - gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE - permeability_coefficient = 0.01 - armor = list(melee = 30, bullet = 20, laser = 20,energy = 20, bomb = 20, bio = 20, rad = 20) - allowed = list(/obj/item/weapon/teleportation_scroll) - flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER - siemens_coefficient = 0.8 - wizard_garb = 1 - -/obj/item/clothing/suit/wizrobe/red - name = "red wizard robe" - desc = "A magnificant, red, gem-lined robe that seems to radiate power." - icon_state = "redwizard" - -/obj/item/clothing/suit/wizrobe/marisa - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - -/obj/item/clothing/suit/wizrobe/magusblue - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusblue" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET - -/obj/item/clothing/suit/wizrobe/magusred - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET - -/obj/item/clothing/suit/wizrobe/psypurple - name = "purple robes" - desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." - icon_state = "psyamp" - -/obj/item/clothing/suit/wizrobe/gentlecoat - name = "Gentlemans Coat" - desc = "A heavy threaded twead gray jacket. For a different sort of Gentleman." - icon_state = "gentlecoat" - item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_inv = HIDETIE|HIDEHOLSTER - -/obj/item/clothing/suit/wizrobe/fake - name = "wizard robe" - desc = "A rather dull, blue robe meant to mimick real wizard robes." - icon_state = "wizard-fake" - item_state_slots = list(slot_r_hand_str = "wizard", slot_l_hand_str = "wizard") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 1.0 - -/obj/item/clothing/head/wizard/marisa/fake - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 1.0 - -/obj/item/clothing/suit/wizrobe/marisa/fake - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) +/obj/item/clothing/head/wizard + name = "wizard hat" + desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." + icon_state = "wizard" + //Not given any special protective value since the magic robes are full-body protection --NEO + siemens_coefficient = 0.8 + body_parts_covered = 0 + wizard_garb = 1 + +/obj/item/clothing/head/wizard/red + name = "red wizard hat" + desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." + icon_state = "redwizard" + siemens_coefficient = 0.8 + +/obj/item/clothing/head/wizard/fake + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." + icon_state = "wizard-fake" + body_parts_covered = HEAD|FACE + siemens_coefficient = 1 + +/obj/item/clothing/head/wizard/fake/realistic + desc = "A cool-looking 'magic' hat." + icon_state = "wizard" + body_parts_covered = HEAD + +/obj/item/clothing/head/wizard/fake/realistic/colorable + desc = "A cool-looking 'magic' hat." + icon_state = "wizard-white" + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/head/mob_vr_teshari.dmi' + ) + +/obj/item/clothing/head/wizard/marisa + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + siemens_coefficient = 0.8 + +/obj/item/clothing/head/wizard/magus + name = "Magus Helm" + desc = "A mysterious helmet that hums with an unearthly power" + icon_state = "magus" + siemens_coefficient = 0.8 + body_parts_covered = HEAD|FACE|EYES + +/obj/item/clothing/head/wizard/amp + name = "psychic amplifier" + desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." + icon_state = "amp" + siemens_coefficient = 0.8 + +/obj/item/clothing/head/wizard/cap + name = "Gentlemans Cap" + desc = "A checkered gray flat cap woven together with the rarest of threads." + icon_state = "gentcap" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + siemens_coefficient = 0.8 + +/obj/item/clothing/suit/wizrobe + name = "wizard robe" + desc = "A magnificant, gem-lined robe that seems to radiate power." + icon_state = "wizard" + gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE + permeability_coefficient = 0.01 + armor = list(melee = 30, bullet = 20, laser = 20,energy = 20, bomb = 20, bio = 20, rad = 20) + allowed = list(/obj/item/weapon/teleportation_scroll) + flags_inv = HIDEJUMPSUIT|HIDETIE|HIDEHOLSTER + siemens_coefficient = 0.8 + wizard_garb = 1 + +/obj/item/clothing/suit/wizrobe/red + name = "red wizard robe" + desc = "A magnificant, red, gem-lined robe that seems to radiate power." + icon_state = "redwizard" + +/obj/item/clothing/suit/wizrobe/marisa + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + +/obj/item/clothing/suit/wizrobe/magusblue + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusblue" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET + +/obj/item/clothing/suit/wizrobe/magusred + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|HANDS|LEGS|FEET + +/obj/item/clothing/suit/wizrobe/psypurple + name = "purple robes" + desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." + icon_state = "psyamp" + +/obj/item/clothing/suit/wizrobe/gentlecoat + name = "Gentlemans Coat" + desc = "A heavy threaded twead gray jacket. For a different sort of Gentleman." + icon_state = "gentlecoat" + item_state_slots = list(slot_r_hand_str = "greatcoat", slot_l_hand_str = "greatcoat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDETIE|HIDEHOLSTER + +/obj/item/clothing/suit/wizrobe/fake + name = "wizard robe" + desc = "A rather dull, blue robe meant to mimick real wizard robes." + icon_state = "wizard-fake" + item_state_slots = list(slot_r_hand_str = "wizard", slot_l_hand_str = "wizard") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 1.0 + +/obj/item/clothing/head/wizard/marisa/fake + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 1.0 + +/obj/item/clothing/suit/wizrobe/marisa/fake + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) siemens_coefficient = 1.0 \ No newline at end of file diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 0c70e7d5f5f..8eefbcbcea4 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -562,6 +562,10 @@ name = "white neck gaiter" icon_state = "gaiter_snow" +/obj/item/clothing/accessory/gaiter/half //functions like a gaiter + name = "black half-mask" + icon_state = "half_mask" + /* * Pride Pins */ diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index fec423c436e..5a4a910e5e2 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -493,7 +493,14 @@ /obj/item/clothing/accessory/collar/shock/bluespace/relaymove(var/mob/living/user,var/direction) return //For some reason equipping this item was triggering this proc, putting the wearer inside of the collars belly for some reason. -/obj/item/clothing/accessory/collar/shock/bluespace/attackby(var/obj/item/device/assembly/signaler/component, mob/user as mob) +/obj/item/clothing/accessory/collar/shock/bluespace/attackby(var/obj/item/component, mob/user as mob) + if (component.has_tool_quality(TOOL_WRENCH)) + to_chat(user, "You crack the bluespace crystal [src].") + var/turf/T = get_turf(src) + new /obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning(T) + user.drop_from_inventory(src) + qdel(src) + return if (!istype(component,/obj/item/device/assembly/signaler)) ..() return @@ -517,7 +524,14 @@ target_size = 1 on = 1 -/obj/item/clothing/accessory/collar/shock/bluespace/modified/attackby(var/obj/item/device/assembly/signaler/component, mob/user as mob) +/obj/item/clothing/accessory/collar/shock/bluespace/modified/attackby(var/obj/item/component, mob/user as mob) + if (component.has_tool_quality(TOOL_WRENCH)) + to_chat(user, "You crack the bluespace crystal [src], the attached signaler disconnects.") + var/turf/T = get_turf(src) + new /obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning(T) + user.drop_from_inventory(src) + qdel(src) + return if (!istype(component,/obj/item/device/assembly/signaler)) ..() return @@ -625,6 +639,141 @@ s.start() return +//bluespace collar malfunctioning (random size) + +/obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning + name = "Bluespace collar" + desc = "A collar that can manipulate the size of the wearer, and can be modified when unequiped. It has a crack on the crystal." + icon_state = "collar_size_malf" + item_state = "collar_size" + overlay_state = "collar_size" + target_size = 1 + on = 1 + var/currently_shrinking = 0 + +/obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning/attackby(var/obj/item/component, mob/user as mob) + if (!istype(component,/obj/item/device/assembly/signaler)) + ..() + return + to_chat(user, "The signaler doesn't respond to the connection attempt [src].") + return + +/obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning/attack_self(mob/user as mob, flag1) + if(!istype(user, /mob/living/carbon/human)) + return + user.set_machine(src) + var/dat = {" + Frequency/Code for collar:
                    + Frequency: + - + - [format_frequency(frequency)] + + + +
                    + + Code: + - + - [code] + + + +
                    + + Tag: + Set tag
                    + + Size: + Input Disabled!
                    +
                    "} + user << browse(dat, "window=radio") + onclose(user, "radio") + return + +/obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning/Topic(href, href_list) + if(usr.stat || usr.restrained()) + return + if(((istype(usr, /mob/living/carbon/human) && ((!( ticker ) || (ticker && ticker.mode != "monkey")) && usr.contents.Find(src))) || (usr.contents.Find(master) || (in_range(src, usr) && istype(loc, /turf))))) + usr.set_machine(src) + if(href_list["freq"]) + var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) + set_frequency(new_frequency) + if(href_list["tag"]) + var/str = copytext(reject_bad_text(tgui_input_text(usr,"Tag text?","Set tag","",MAX_NAME_LEN)),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(usr,"[name]'s tag set to be blank.") + name = initial(name) + desc = initial(desc) + else + to_chat(usr,"You set the [name]'s tag to '[str]'.") + name = initial(name) + " ([str])" + desc = initial(desc) + " The tag says \"[str]\"." + else + if(href_list["code"]) + code += text2num(href_list["code"]) + code = round(code) + code = min(100, code) + code = max(1, code) + if(!( master )) + if(istype(loc, /mob)) + attack_self(loc) + else + for(var/mob/M in viewers(1, src)) + if(M.client) + attack_self(M) + else + if(istype(master.loc, /mob)) + attack_self(master.loc) + else + for(var/mob/M in viewers(1, master)) + if(M.client) + attack_self(M) + else + usr << browse(null, "window=radio") + return + return + +/obj/item/clothing/accessory/collar/shock/bluespace/malfunctioning/receive_signal(datum/signal/signal) + if(!signal) + return + target_size = (rand(25,200)) /100 + if(on) + var/mob/M = null + if(ismob(loc)) + M = loc + if(ismob(loc.loc)) + M = loc.loc // This is about as terse as I can make my solution to the whole 'collar won't work when attached as accessory' thing. + var/mob/living/carbon/human/H = M + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + if(!H.resizable) + H.visible_message("The space around [H] compresses for a moment but then nothing happens.","The space around you distorts but nothing happens to you.") + return + if (target_size < 0.25) + H.visible_message("The collar on [H] flickers, but fizzles out.","Your collar flickers, but is not powerful enough to shrink you that small.") + return + if(currently_shrinking == 0) + if(!(world.time - last_activated > 10 SECONDS)) + to_chat(M, "\The [src] flickers. It seems to be recharging.") + return + last_activated = world.time + original_size = H.size_multiplier + currently_shrinking = 1 + H.resize(target_size, ignore_prefs = FALSE) //In case someone else tries to put it on you. + H.visible_message("The space around [H] distorts as they change size!","The space around you distorts as you change size!") + log_admin("Admin [key_name(M)]'s size was altered by a bluespace collar.") + s.set_up(3, 1, M) + s.start() + else if(currently_shrinking == 1) + if(original_size == null) + H.visible_message("The space around [H] twists and turns for a moment but then nothing happens.","The space around you distorts but stay the same size.") + return + last_activated = world.time + H.resize(original_size, ignore_prefs = FALSE) + original_size = null + currently_shrinking = 0 + H.visible_message("The space around [H] distorts as they return to their original size!","The space around you distorts as you return to your original size!") + log_admin("Admin [key_name(M)]'s size was altered by a bluespace collar.") + to_chat(M, "\The [src] flickers. It is now recharging and will be ready again in ten seconds.") + s.set_up(3, 1, M) + s.start() + return + //Machete Holsters /obj/item/clothing/accessory/holster/machete name = "machete sheath" @@ -990,3 +1139,17 @@ desc = "A cut down jacket that looks like it's light enough to wear on top of some other clothes. This one's a sort of olive-drab kind of colour." icon_state = "cropjacket_drab" item_state = "cropjacket_drab" + +//Replikant patch & jacket + +/obj/item/clothing/accessory/sleekpatch + name = "sleek uniform patch" + desc = "A somewhat old-fashioned embroidered patch of Nanotrasen's logo." + icon_state = "sleekpatch" + item_state = "sleekpatch" + +/obj/item/clothing/accessory/poncho/roles/cloak/custom/gestaltjacket + name = "sleek uniform jacket" + desc = "Barely more than a pair of long stirrup sleeves joined by a turtleneck. Has decorative red accents." + icon_state = "gestaltjacket" + item_state = "gestaltjacket" diff --git a/code/modules/clothing/under/accessories/altevian_vr.dm b/code/modules/clothing/under/accessories/altevian_vr.dm index b75757b94a2..3a7ca8c7850 100644 --- a/code/modules/clothing/under/accessories/altevian_vr.dm +++ b/code/modules/clothing/under/accessories/altevian_vr.dm @@ -1,6 +1,6 @@ /obj/item/clothing/accessory/jacket/altevian name = "Altevian Hegemony Civilian Jacket" - desc = "A form fitting jacket that looks more for fashion than for work with it leaving ones mid-riff exposed to the outside elements, but when looking so good it isn't really ones worry by that point! " + desc = "A form fitting half-jacket that leaves ones body exposed to the elements. This attire is great for those summer days, or if you're someone that runs a high internal temperature." icon = 'icons/inventory/accessory/item_vr.dmi' icon_override = 'icons/inventory/accessory/mob_vr.dmi' icon_state = "altevian-jacket-civ" @@ -46,7 +46,7 @@ /obj/item/clothing/accessory/scarf/altevian name = "Blue Comfortable Scarf" - desc = "A scarf made of synthetic material that would be common among a people who don't really have the leisure of organic crops for luxury goods. They seemed to of refined this process though and one wouldn't be able to tell the difference between it and ones made normally." + desc = "A soft silk like scarf made of synthetic material. It's soft to the touch." icon = 'icons/inventory/accessory/item_vr.dmi' icon_override = 'icons/inventory/accessory/mob_vr.dmi' icon_state = "altevian-scarf-blue" @@ -81,13 +81,13 @@ /obj/item/clothing/accessory/altevian_badge name = "Altevian Civilian Badge" - desc = "An emblem commonly seen worn by the altevians off-work or by visitors of their ships." + desc = "An emblem that's normally worn by non-altevian workers on their fleet ships." icon_state = "altevian_badge" slot = ACCESSORY_SLOT_MEDAL /obj/item/clothing/accessory/altevian_badge/aquila name = "Royal Altevian Navy Emblem" - desc = "An emblem commonly seen worn by the altevians for their work operations." + desc = "An emblem found across all altevian vessels. The specific metals and jewels denote ranks. Otherwise, it's just a pretty rat skull emblem with a set of crystals for their eyes and fangs." icon_state = "altevian_aquila" /obj/item/clothing/accessory/altevian_badge/aquila/silver @@ -106,4 +106,4 @@ icon_state = "altevian_aquila_phoron" /obj/item/clothing/accessory/altevian_badge/aquila/hydrogen - icon_state = "altevian_aquila_hydrogen" \ No newline at end of file + icon_state = "altevian_aquila_hydrogen" diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 42ee5a7d6ac..6e2b392331e 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -1,262 +1,262 @@ -/* - Badges are worn on the belt or neck, and can be used to show that the holder is an authorized - Security agent - the user details can be imprinted on holobadges with a Security-access ID card, - or they can be emagged to accept any ID for use in disguises. -*/ - -/obj/item/clothing/accessory/badge - name = "detective's badge" - desc = "A corporate security badge, made from gold and set on false leather." - icon_state = "marshalbadge" - slot_flags = SLOT_BELT | SLOT_TIE - slot = ACCESSORY_SLOT_MEDAL - - var/stored_name - var/badge_string = "Corporate Security" - -/obj/item/clothing/accessory/badge/proc/set_name(var/new_name) - stored_name = new_name - name = "[initial(name)] ([stored_name])" - -/obj/item/clothing/accessory/badge/proc/set_desc(var/mob/living/carbon/human/H) - -/obj/item/clothing/accessory/badge/attack_self(mob/user as mob) - if(!stored_name) - to_chat(user, "You polish your old badge fondly, shining up the surface.") - set_name(user.real_name) - return - - if(isliving(user)) - if(stored_name) - user.visible_message("[user] displays their [src.name].\nIt reads: [stored_name], [badge_string].","You display your [src.name].\nIt reads: [stored_name], [badge_string].") - else - user.visible_message("[user] displays their [src.name].\nIt reads: [badge_string].","You display your [src.name]. It reads: [badge_string].") - -/obj/item/clothing/accessory/badge/attack(mob/living/carbon/human/M, mob/living/user) - if(isliving(user)) - user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") - user.do_attack_animation(M) - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM - -// General Badges -/obj/item/clothing/accessory/badge/old - name = "faded badge" - desc = "A faded badge, backed with leather." - icon_state = "badge_round" - -/obj/item/clothing/accessory/badge/idbadge/nt - name = "\improper NT ID badge" - desc = "A descriptive identification badge with the holder's credentials. This one has red marks with the NanoTrasen logo on it." - icon_state = "ntbadge" - badge_string = null - -/obj/item/clothing/accessory/badge/press - name = "corporate press pass" - desc = "A corporate reporter's pass, emblazoned with the NanoTrasen logo." - icon_state = "pressbadge" - item_state = "pbadge" - badge_string = "Corporate Reporter" - w_class = ITEMSIZE_TINY - - drop_sound = 'sound/items/drop/rubber.ogg' - pickup_sound = 'sound/items/pickup/rubber.ogg' - -/obj/item/clothing/accessory/badge/press/independent - name = "press pass" - desc = "A freelance journalist's pass." - icon_state = "pressbadge-i" - badge_string = "Freelance Journalist" - -/obj/item/clothing/accessory/badge/press/plastic - name = "plastic press pass" - desc = "A journalist's 'pass' shaped, for whatever reason, like a security badge. It is made of plastic." - icon_state = "pbadge" - badge_string = "Sicurity Journelist" - w_class = ITEMSIZE_SMALL - -// Holobadges -/obj/item/clothing/accessory/badge/holo - name = "holobadge" - desc = "This glowing blue badge marks the holder as THE LAW." - icon_state = "holobadge" - var/emagged //Emagging removes Sec check. - var/valid_access = list(access_security) //Default access is security, to be overriden or expanded as desired - -/obj/item/clothing/accessory/badge/holo/cord - icon_state = "holobadge-cord" - slot_flags = SLOT_MASK | SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/attack_self(mob/user as mob) - if(!stored_name) - to_chat(user, "Waving around a holobadge before swiping an ID would be pretty pointless.") - return - return ..() - -/obj/item/clothing/accessory/badge/holo/emag_act(var/remaining_charges, var/mob/user) - if (emagged) - to_chat(user, "\The [src] is already cracked.") - return - else - emagged = 1 - to_chat(user, "You crack the holobadge security checks.") - return 1 - -/obj/item/clothing/accessory/badge/holo/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/card/id) || istype(O, /obj/item/device/pda)) - - var/obj/item/weapon/card/id/id_card = null - - if(istype(O, /obj/item/weapon/card/id)) - id_card = O - else - var/obj/item/device/pda/pda = O - id_card = pda.id - - var/found = FALSE - for(var/access in valid_access) - if(access in id_card.access || emagged) - to_chat(user, "You imprint your ID details onto the badge.") - set_name(user.real_name) - found = TRUE - break - if(!found) - to_chat(user, "[src] rejects your insufficient access rights.") - return - ..() - -/obj/item/weapon/storage/box/holobadge - name = "holobadge box" - desc = "A box claiming to contain holobadges." - starts_with = list( - /obj/item/clothing/accessory/badge/holo/officer = 2, - /obj/item/clothing/accessory/badge/holo = 2, - /obj/item/clothing/accessory/badge/holo/cord = 2 - ) - -/obj/item/clothing/accessory/badge/holo/officer - name = "officer's badge" - desc = "A bronze corporate security badge. Stamped with the words 'Security Officer.'" - icon_state = "bronzebadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/warden - name = "warden's holobadge" - desc = "A silver corporate security badge. Stamped with the words 'Warden.'" - icon_state = "silverbadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/hos - name = "head of security's holobadge" - desc = "An immaculately polished gold security badge. Stamped with the words 'Head of Security.'" - icon_state = "goldbadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/detective - name = "detective's holobadge" - desc = "An immaculately polished gold security badge on leather. Labeled 'Detective.'" - icon_state = "marshalbadge" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/investigator - name = "\improper investigator holobadge" - desc = "This badge marks the holder as an investigative agent." - icon_state = "invbadge" - badge_string = "Corporate Investigator" - valid_access = list(access_security, access_lawyer) //Permitting both sec and IAA! - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/clothing/accessory/badge/holo/sheriff - name = "sheriff badge" - desc = "A star-shaped brass badge denoting who the law is around these parts." - icon_state = "sheriff" - slot_flags = SLOT_TIE | SLOT_BELT - -/obj/item/weapon/storage/box/holobadge/hos - name = "holobadge box" - desc = "A box claiming to contain holobadges." - starts_with = list( - /obj/item/clothing/accessory/badge/holo/officer = 2, - /obj/item/clothing/accessory/badge/holo/warden = 1, - /obj/item/clothing/accessory/badge/holo/detective = 2, - /obj/item/clothing/accessory/badge/holo/hos = 1, - /obj/item/clothing/accessory/badge/holo/cord = 1 - ) - -// Sheriff Badge (toy) -/obj/item/clothing/accessory/badge/sheriff - name = "sheriff badge" - desc = "This town ain't big enough for the two of us, pardner." - icon_state = "sheriff_toy" - item_state = "sheriff_toy" - -/obj/item/clothing/accessory/badge/sheriff/attack_self(mob/user as mob) - user.visible_message("[user] shows their sheriff badge. There's a new sheriff in town!",\ - "You flash the sheriff badge to everyone around you!") - -/obj/item/clothing/accessory/badge/sheriff/attack(mob/living/carbon/human/M, mob/living/user) - if(isliving(user)) - user.visible_message("[user] invades [M]'s personal space, the sheriff badge into their face!.","You invade [M]'s personal space, thrusting the sheriff badge into their face insistently.") - user.do_attack_animation(M) - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM - -// Synthmorph bag / Corporation badges. Primarily used on the robobag, but can be worn. Default is NT. -/obj/item/clothing/accessory/badge/corporate_tag - name = "NanoTrasen Badge" - desc = "A plain metallic plate that might denote the wearer as a member of NanoTrasen." - icon_state = "tag_nt" - item_state = "badge" - badge_string = "NanoTrasen" - -/obj/item/clothing/accessory/badge/corporate_tag/morpheus - name = "Morpheus Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Morpheus Cyberkinetics." - icon_state = "tag_blank" - badge_string = "Morpheus" - -/obj/item/clothing/accessory/badge/corporate_tag/wardtaka - name = "Ward-Takahashi Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Ward-Takahashi." - icon_state = "tag_ward" - badge_string = "Ward-Takahashi" - -/obj/item/clothing/accessory/badge/corporate_tag/zenghu - name = "Zeng-Hu Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Zeng-Hu." - icon_state = "tag_zeng" - badge_string = "Zeng-Hu" - -/obj/item/clothing/accessory/badge/corporate_tag/gilthari - name = "Gilthari Badge" - desc = "An opulent metallic plate that might denote the wearer as a member of Gilthari." - icon_state = "tag_gil" - badge_string = "Gilthari" - -/obj/item/clothing/accessory/badge/corporate_tag/veymed - name = "Vey-Medical Badge" - desc = "A plain metallic plate that might denote the wearer as a member of Vey-Medical." - icon_state = "tag_vey" - badge_string = "Vey-Medical" - -/obj/item/clothing/accessory/badge/corporate_tag/hephaestus - name = "Hephaestus Badge" - desc = "A rugged metallic plate that might denote the wearer as a member of Hephaestus." - icon_state = "tag_heph" - badge_string = "Hephaestus" - -/obj/item/clothing/accessory/badge/corporate_tag/grayson - name = "Grayson Badge" - desc = "A rugged metallic plate that might denote the wearer as a member of Grayson." - icon_state = "tag_grayson" - badge_string = "Grayson" - -/obj/item/clothing/accessory/badge/corporate_tag/xion - name = "Xion Badge" - desc = "A rugged metallic plate that might denote the wearer as a member of Xion." - icon_state = "tag_xion" - badge_string = "Xion" - -/obj/item/clothing/accessory/badge/corporate_tag/bishop - name = "Bishop Badge" - desc = "A sleek metallic plate that might denote the wearer as a member of Bishop." - icon_state = "tag_bishop" - badge_string = "Bishop" +/* + Badges are worn on the belt or neck, and can be used to show that the holder is an authorized + Security agent - the user details can be imprinted on holobadges with a Security-access ID card, + or they can be emagged to accept any ID for use in disguises. +*/ + +/obj/item/clothing/accessory/badge + name = "detective's badge" + desc = "A corporate security badge, made from gold and set on false leather." + icon_state = "marshalbadge" + slot_flags = SLOT_BELT | SLOT_TIE + slot = ACCESSORY_SLOT_MEDAL + + var/stored_name + var/badge_string = "Corporate Security" + +/obj/item/clothing/accessory/badge/proc/set_name(var/new_name) + stored_name = new_name + name = "[initial(name)] ([stored_name])" + +/obj/item/clothing/accessory/badge/proc/set_desc(var/mob/living/carbon/human/H) + +/obj/item/clothing/accessory/badge/attack_self(mob/user as mob) + if(!stored_name) + to_chat(user, "You polish your old badge fondly, shining up the surface.") + set_name(user.real_name) + return + + if(isliving(user)) + if(stored_name) + user.visible_message("[user] displays their [src.name].\nIt reads: [stored_name], [badge_string].","You display your [src.name].\nIt reads: [stored_name], [badge_string].") + else + user.visible_message("[user] displays their [src.name].\nIt reads: [badge_string].","You display your [src.name]. It reads: [badge_string].") + +/obj/item/clothing/accessory/badge/attack(mob/living/carbon/human/M, mob/living/user) + if(isliving(user)) + user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") + user.do_attack_animation(M) + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM + +// General Badges +/obj/item/clothing/accessory/badge/old + name = "faded badge" + desc = "A faded badge, backed with leather." + icon_state = "badge_round" + +/obj/item/clothing/accessory/badge/idbadge/nt + name = "\improper NT ID badge" + desc = "A descriptive identification badge with the holder's credentials. This one has red marks with the NanoTrasen logo on it." + icon_state = "ntbadge" + badge_string = null + +/obj/item/clothing/accessory/badge/press + name = "corporate press pass" + desc = "A corporate reporter's pass, emblazoned with the NanoTrasen logo." + icon_state = "pressbadge" + item_state = "pbadge" + badge_string = "Corporate Reporter" + w_class = ITEMSIZE_TINY + + drop_sound = 'sound/items/drop/rubber.ogg' + pickup_sound = 'sound/items/pickup/rubber.ogg' + +/obj/item/clothing/accessory/badge/press/independent + name = "press pass" + desc = "A freelance journalist's pass." + icon_state = "pressbadge-i" + badge_string = "Freelance Journalist" + +/obj/item/clothing/accessory/badge/press/plastic + name = "plastic press pass" + desc = "A journalist's 'pass' shaped, for whatever reason, like a security badge. It is made of plastic." + icon_state = "pbadge" + badge_string = "Sicurity Journelist" + w_class = ITEMSIZE_SMALL + +// Holobadges +/obj/item/clothing/accessory/badge/holo + name = "holobadge" + desc = "This glowing blue badge marks the holder as THE LAW." + icon_state = "holobadge" + var/emagged //Emagging removes Sec check. + var/valid_access = list(access_security) //Default access is security, to be overriden or expanded as desired + +/obj/item/clothing/accessory/badge/holo/cord + icon_state = "holobadge-cord" + slot_flags = SLOT_MASK | SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/attack_self(mob/user as mob) + if(!stored_name) + to_chat(user, "Waving around a holobadge before swiping an ID would be pretty pointless.") + return + return ..() + +/obj/item/clothing/accessory/badge/holo/emag_act(var/remaining_charges, var/mob/user) + if (emagged) + to_chat(user, "\The [src] is already cracked.") + return + else + emagged = 1 + to_chat(user, "You crack the holobadge security checks.") + return 1 + +/obj/item/clothing/accessory/badge/holo/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/card/id) || istype(O, /obj/item/device/pda)) + + var/obj/item/weapon/card/id/id_card = null + + if(istype(O, /obj/item/weapon/card/id)) + id_card = O + else + var/obj/item/device/pda/pda = O + id_card = pda.id + + var/found = FALSE + for(var/access in valid_access) + if(access in id_card.access || emagged) + to_chat(user, "You imprint your ID details onto the badge.") + set_name(user.real_name) + found = TRUE + break + if(!found) + to_chat(user, "[src] rejects your insufficient access rights.") + return + ..() + +/obj/item/weapon/storage/box/holobadge + name = "holobadge box" + desc = "A box claiming to contain holobadges." + starts_with = list( + /obj/item/clothing/accessory/badge/holo/officer = 2, + /obj/item/clothing/accessory/badge/holo = 2, + /obj/item/clothing/accessory/badge/holo/cord = 2 + ) + +/obj/item/clothing/accessory/badge/holo/officer + name = "officer's badge" + desc = "A bronze corporate security badge. Stamped with the words 'Security Officer.'" + icon_state = "bronzebadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/warden + name = "warden's holobadge" + desc = "A silver corporate security badge. Stamped with the words 'Warden.'" + icon_state = "silverbadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/hos + name = "head of security's holobadge" + desc = "An immaculately polished gold security badge. Stamped with the words 'Head of Security.'" + icon_state = "goldbadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/detective + name = "detective's holobadge" + desc = "An immaculately polished gold security badge on leather. Labeled 'Detective.'" + icon_state = "marshalbadge" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/investigator + name = "\improper investigator holobadge" + desc = "This badge marks the holder as an investigative agent." + icon_state = "invbadge" + badge_string = "Corporate Investigator" + valid_access = list(access_security, access_lawyer) //Permitting both sec and IAA! + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/clothing/accessory/badge/holo/sheriff + name = "sheriff badge" + desc = "A star-shaped brass badge denoting who the law is around these parts." + icon_state = "sheriff" + slot_flags = SLOT_TIE | SLOT_BELT + +/obj/item/weapon/storage/box/holobadge/hos + name = "holobadge box" + desc = "A box claiming to contain holobadges." + starts_with = list( + /obj/item/clothing/accessory/badge/holo/officer = 2, + /obj/item/clothing/accessory/badge/holo/warden = 1, + /obj/item/clothing/accessory/badge/holo/detective = 2, + /obj/item/clothing/accessory/badge/holo/hos = 1, + /obj/item/clothing/accessory/badge/holo/cord = 1 + ) + +// Sheriff Badge (toy) +/obj/item/clothing/accessory/badge/sheriff + name = "sheriff badge" + desc = "This town ain't big enough for the two of us, pardner." + icon_state = "sheriff_toy" + item_state = "sheriff_toy" + +/obj/item/clothing/accessory/badge/sheriff/attack_self(mob/user as mob) + user.visible_message("[user] shows their sheriff badge. There's a new sheriff in town!",\ + "You flash the sheriff badge to everyone around you!") + +/obj/item/clothing/accessory/badge/sheriff/attack(mob/living/carbon/human/M, mob/living/user) + if(isliving(user)) + user.visible_message("[user] invades [M]'s personal space, the sheriff badge into their face!.","You invade [M]'s personal space, thrusting the sheriff badge into their face insistently.") + user.do_attack_animation(M) + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //NO SPAM + +// Synthmorph bag / Corporation badges. Primarily used on the robobag, but can be worn. Default is NT. +/obj/item/clothing/accessory/badge/corporate_tag + name = "NanoTrasen Badge" + desc = "A plain metallic plate that might denote the wearer as a member of NanoTrasen." + icon_state = "tag_nt" + item_state = "badge" + badge_string = "NanoTrasen" + +/obj/item/clothing/accessory/badge/corporate_tag/morpheus + name = "Morpheus Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Morpheus Cyberkinetics." + icon_state = "tag_blank" + badge_string = "Morpheus" + +/obj/item/clothing/accessory/badge/corporate_tag/wardtaka + name = "Ward-Takahashi Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Ward-Takahashi." + icon_state = "tag_ward" + badge_string = "Ward-Takahashi" + +/obj/item/clothing/accessory/badge/corporate_tag/zenghu + name = "Zeng-Hu Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Zeng-Hu." + icon_state = "tag_zeng" + badge_string = "Zeng-Hu" + +/obj/item/clothing/accessory/badge/corporate_tag/gilthari + name = "Gilthari Badge" + desc = "An opulent metallic plate that might denote the wearer as a member of Gilthari." + icon_state = "tag_gil" + badge_string = "Gilthari" + +/obj/item/clothing/accessory/badge/corporate_tag/veymed + name = "Vey-Medical Badge" + desc = "A plain metallic plate that might denote the wearer as a member of Vey-Medical." + icon_state = "tag_vey" + badge_string = "Vey-Medical" + +/obj/item/clothing/accessory/badge/corporate_tag/hephaestus + name = "Hephaestus Badge" + desc = "A rugged metallic plate that might denote the wearer as a member of Hephaestus." + icon_state = "tag_heph" + badge_string = "Hephaestus" + +/obj/item/clothing/accessory/badge/corporate_tag/grayson + name = "Grayson Badge" + desc = "A rugged metallic plate that might denote the wearer as a member of Grayson." + icon_state = "tag_grayson" + badge_string = "Grayson" + +/obj/item/clothing/accessory/badge/corporate_tag/xion + name = "Xion Badge" + desc = "A rugged metallic plate that might denote the wearer as a member of Xion." + icon_state = "tag_xion" + badge_string = "Xion" + +/obj/item/clothing/accessory/badge/corporate_tag/bishop + name = "Bishop Badge" + desc = "A sleek metallic plate that might denote the wearer as a member of Bishop." + icon_state = "tag_bishop" + badge_string = "Bishop" diff --git a/code/modules/clothing/under/accessories/clothing.dm b/code/modules/clothing/under/accessories/clothing.dm index 877c7b2d4b4..42c2e9f3b97 100644 --- a/code/modules/clothing/under/accessories/clothing.dm +++ b/code/modules/clothing/under/accessories/clothing.dm @@ -1,535 +1,571 @@ -/* - * Formal - */ - -/obj/item/clothing/accessory/vest - name = "black vest" - desc = "Slick black suit vest." - icon_state = "det_vest" - slot = ACCESSORY_SLOT_OVER - -/obj/item/clothing/accessory/jacket - name = "tan suit jacket" - desc = "Cozy suit jacket." - icon_state = "tan_jacket" - slot = ACCESSORY_SLOT_OVER - -/obj/item/clothing/accessory/jacket/red - name = "red suit jacket" - desc = "Relaxing suit jacket." - icon_state = "red_jacket" - -/obj/item/clothing/accessory/jacket/teal - name = "teal suit jacket" - desc = "Relaxing suit jacket." - icon_state = "teal_jacket" - -/obj/item/clothing/accessory/jacket/green - name = "green suit jacket" - desc = "Relaxing suit jacket." - icon_state = "green_jacket" - -/obj/item/clothing/accessory/jacket/charcoal - name = "charcoal suit jacket" - desc = "Strict suit jacket." - icon_state = "charcoal_jacket" - -/obj/item/clothing/accessory/jacket/navy - name = "navy suit jacket" - desc = "Official suit jacket." - icon_state = "navy_jacket" - -/obj/item/clothing/accessory/jacket/burgundy - name = "burgundy suit jacket" - desc = "Expensive suit jacket." - icon_state = "burgundy_jacket" - -/obj/item/clothing/accessory/jacket/checkered - name = "checkered suit jacket" - desc = "Lucky suit jacket." - icon_state = "checkered_jacket" - -/obj/item/clothing/accessory/jacket/gambler - name = "gambler suit jacket" - desc = "Chairman suit jacket." - icon_state = "gambler_jacket" - -/obj/item/clothing/accessory/jacket/extravagant - name = "extravagant suit jacket" - desc = "Luxury suit jacket." - icon_state = "extravagant_jacket" - -/* - * Hawaiian - */ - -/obj/item/clothing/accessory/hawaiian - name = "hawaiian shirt" - desc = "You probably need some welder googles to look at this." - icon_state = "hawaiian_cyan" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - slot_flags = SLOT_OCLOTHING | SLOT_TIE - body_parts_covered = UPPER_TORSO|LOWER_TORSO - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - slot = ACCESSORY_SLOT_OVER - -/obj/item/clothing/accessory/hawaiian/blue - name = "blue hawaiian shirt" - icon_state = "hawaiian_blue" - -/obj/item/clothing/accessory/hawaiian/pink - name = "pink hawaiian shirt" - icon_state = "hawaiian_pink" - -/obj/item/clothing/accessory/hawaiian/red - name = "red hawaiian shirt" - icon_state = "hawaiian_red" - -/obj/item/clothing/accessory/hawaiian/yellow - name = "yellow hawaiian shirt" - icon_state = "hawaiian_yellow" - -/obj/item/clothing/accessory/hawaiian_random - name = "random hawaiian shirt" - desc = "A random set of hawaiian shirts for style." - -/obj/item/clothing/accessory/hawaiian_random/New() - return pick( - prob(2);/obj/item/clothing/accessory/hawaiian, - prob(2);/obj/item/clothing/accessory/hawaiian/blue, - prob(2);/obj/item/clothing/accessory/hawaiian/pink, - prob(2);/obj/item/clothing/accessory/hawaiian/red, - prob(2);/obj/item/clothing/accessory/hawaiian/yellow - ) - -/* - * 80s - */ - -/obj/item/clothing/accessory/tropical - name = "black tropical shirt" - desc = "A classic themed neosilk tropical shirt. This one makes you feel like an animal." - icon_state = "animalstyle" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - slot_flags = SLOT_OCLOTHING | SLOT_TIE - body_parts_covered = UPPER_TORSO|LOWER_TORSO - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - slot = ACCESSORY_SLOT_OVER - -/obj/item/clothing/accessory/tropical/green - name = "puke-green tropical shirt" - desc = "A classic themed neosilk tropical shirt. This one makes you look like puke." - icon_state = "tropicopuke" - -/obj/item/clothing/accessory/tropical/pink - name = "pink tropical shirt" - desc = "A classic themed neosilk tropical shirt. This one makes you feel nostalgic." - icon_state = "3005vintage" - -/obj/item/clothing/accessory/tropical/blue - name = "blue tropical shirt" - desc = "A classic themed neosilk tropical shirt. This one makes you feel out of touch." - icon_state = "miamivice" - -/obj/item/clothing/accessory/tropical_random/New() - return pick( - prob(2);/obj/item/clothing/accessory/tropical, - prob(2);/obj/item/clothing/accessory/tropical/green, - prob(2);/obj/item/clothing/accessory/tropical/pink, - prob(2);/obj/item/clothing/accessory/tropical/blue - ) - -/* - * Chaps - */ - -/obj/item/clothing/accessory/chaps - name = "brown chaps" - desc = "A pair of loose, brown leather chaps." - icon_state = "chaps" - -/obj/item/clothing/accessory/chaps/black - name = "black chaps" - desc = "A pair of loose, black leather chaps." - icon_state = "chaps_black" - -/* - * Poncho - */ - -/obj/item/clothing/accessory/poncho - name = "poncho" - desc = "A simple, comfortable poncho." - icon_state = "classicponcho" - item_state = "classicponcho" - icon_override = 'icons/inventory/accessory/mob.dmi' - var/fire_resist = T0C+100 - allowed = list(/obj/item/weapon/tank/emergency/oxygen) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - slot_flags = SLOT_OCLOTHING | SLOT_TIE - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - slot = ACCESSORY_SLOT_OVER - sprite_sheets = list( - SPECIES_TESHARI = 'icons/inventory/suit/mob_teshari.dmi' - ) - -/obj/item/clothing/accessory/poncho/equipped() //Solution for race-specific sprites for an accessory which is also a suit. Suit icons break if you don't use icon override which then also overrides race-specific sprites. - ..() - var/mob/living/carbon/human/H = loc - if(istype(H) && H.wear_suit == src) - if(H.species.name == SPECIES_TESHARI) - icon_override = 'icons/inventory/suit/mob_teshari.dmi' - else - icon_override = 'icons/inventory/accessory/mob.dmi' - update_clothing_icon() - -/obj/item/clothing/accessory/poncho/dropped() //Resets the override to prevent the wrong .dmi from being used because equipped only triggers when wearing ponchos as suits. - icon_override = null - -/obj/item/clothing/accessory/poncho/green - name = "green poncho" - desc = "A simple, comfortable cloak without sleeves. This one is green." - icon_state = "greenponcho" - item_state = "greenponcho" - -/obj/item/clothing/accessory/poncho/red - name = "red poncho" - desc = "A simple, comfortable cloak without sleeves. This one is red." - icon_state = "redponcho" - item_state = "redponcho" - -/obj/item/clothing/accessory/poncho/purple - name = "purple poncho" - desc = "A simple, comfortable cloak without sleeves. This one is purple." - icon_state = "purpleponcho" - item_state = "purpleponcho" - -/obj/item/clothing/accessory/poncho/blue - name = "blue poncho" - desc = "A simple, comfortable cloak without sleeves. This one is blue." - icon_state = "blueponcho" - item_state = "blueponcho" - -/obj/item/clothing/accessory/poncho/roles/security - name = "security poncho" - desc = "A simple, comfortable cloak without sleeves. This one is black and red, standard NanoTrasen Security colors." - icon_state = "secponcho" - item_state = "secponcho" - -/obj/item/clothing/accessory/poncho/roles/medical - name = "medical poncho" - desc = "A simple, comfortable cloak without sleeves. This one is white with green and blue tint, standard Medical colors." - icon_state = "medponcho" - item_state = "medponcho" - -/obj/item/clothing/accessory/poncho/roles/engineering - name = "engineering poncho" - desc = "A simple, comfortable cloak without sleeves. This one is yellow and orange, standard Engineering colors." - icon_state = "engiponcho" - item_state = "engiponcho" - -/obj/item/clothing/accessory/poncho/roles/science - name = "science poncho" - desc = "A simple, comfortable cloak without sleeves. This one is white with purple trim, standard NanoTrasen Science colors." - icon_state = "sciponcho" - item_state = "sciponcho" - -/obj/item/clothing/accessory/poncho/roles/cargo - name = "cargo poncho" - desc = "A simple, comfortable cloak without sleeves. This one is tan and grey, the colors of Cargo." - icon_state = "cargoponcho" - item_state = "cargoponcho" - -/* - * Cloak - */ - -/obj/item/clothing/accessory/poncho/roles/cloak - name = "quartermaster's cloak" - desc = "An elaborate brown and gold cloak." - icon_state = "qmcloak" - item_state = "qmcloak" - body_parts_covered = null - -/obj/item/clothing/accessory/poncho/roles/cloak/ce - name = "chief engineer's cloak" - desc = "An elaborate cloak worn by the chief engineer." - icon_state = "cecloak" - item_state = "cecloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/cmo - name = "chief medical officer's cloak" - desc = "An elaborate cloak meant to be worn by the chief medical officer." - icon_state = "cmocloak" - item_state = "cmocloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/hop - name = "head of personnel's cloak" - desc = "An elaborate cloak meant to be worn by the head of personnel." - icon_state = "hopcloak" - item_state = "hopcloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/rd - name = "research director's cloak" - desc = "An elaborate cloak meant to be worn by the research director." - icon_state = "rdcloak" - item_state = "rdcloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/qm - name = "quartermaster's cloak" - desc = "An elaborate cloak meant to be worn by the quartermaster." - icon_state = "qmcloak" - item_state = "qmcloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/hos - name = "head of security's cloak" - desc = "An elaborate cloak meant to be worn by the head of security." - icon_state = "hoscloak" - item_state = "hoscloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/captain - name = "site manager's cloak" - desc = "An elaborate cloak meant to be worn by the site manager." - icon_state = "capcloak" - item_state = "capcloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/cargo - name = "brown cloak" - desc = "A simple brown and black cloak." - icon_state = "cargocloak" - item_state = "cargocloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/mining - name = "trimmed purple cloak" - desc = "A trimmed purple and brown cloak." - icon_state = "miningcloak" - item_state = "miningcloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/security - name = "red cloak" - desc = "A simple red and black cloak." - icon_state = "seccloak" - item_state = "seccloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/service - name = "green cloak" - desc = "A simple green and blue cloak." - icon_state = "servicecloak" - item_state = "servicecloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/engineer - name = "gold cloak" - desc = "A simple gold and brown cloak." - icon_state = "engicloak" - item_state = "engicloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/atmos - name = "yellow cloak" - desc = "A trimmed yellow and blue cloak." - icon_state = "atmoscloak" - item_state = "atmoscloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/research - name = "purple cloak" - desc = "A simple purple and white cloak." - icon_state = "scicloak" - item_state = "scicloak" - -/obj/item/clothing/accessory/poncho/roles/cloak/medical - name = "blue cloak" - desc = "A simple blue and white cloak." - icon_state = "medcloak" - item_state = "medcloak" - - -/obj/item/clothing/accessory/poncho/roles/cloak/custom //A colorable cloak - name = "cloak" - desc = "A simple, bland cloak." - icon_state = "colorcloak" - item_state = "colorcloak" - -/obj/item/clothing/accessory/wcoat - name = "waistcoat" - desc = "For some classy, murderous fun." - icon_state = "vest" - item_state = "vest" - icon_override = 'icons/inventory/accessory/mob.dmi' - item_state_slots = list(slot_r_hand_str = "wcoat", slot_l_hand_str = "wcoat") - allowed = list(/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - slot_flags = SLOT_OCLOTHING | SLOT_TIE - body_parts_covered = UPPER_TORSO|LOWER_TORSO - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - slot = ACCESSORY_SLOT_OVER - -/obj/item/clothing/accessory/wcoat/red - name = "red waistcoat" - icon_state = "red_waistcoat" - item_state = "red_waistcoat" - -/obj/item/clothing/accessory/wcoat/grey - name = "grey waistcoat" - icon_state = "grey_waistcoat" - item_state = "grey_waistcoat" - -/obj/item/clothing/accessory/wcoat/brown - name = "brown waistcoat" - icon_state = "brown_waistcoat" - item_state = "brown_waistcoat" - -/obj/item/clothing/accessory/wcoat/gentleman - name = "elegant waistcoat" - icon_state = "elegant_waistcoat" - item_state = "elegant_waistcoat" - -/* - * Sweatervests - */ - -/obj/item/clothing/accessory/wcoat/swvest - name = "black sweater vest" - desc = "A sleeveless sweater. Wear this if you don't want your arms to be warm, or if you're a nerd." - icon_state = "sweatervest" - item_state = "sweatervest" - -/obj/item/clothing/accessory/wcoat/swvest/blue - name = "blue sweater vest" - icon_state = "sweatervest_blue" - item_state = "sweatervest_blue" - -/obj/item/clothing/accessory/wcoat/swvest/red - name = "red sweater vest" - icon_state = "sweatervest_red" - item_state = "sweatervest_red" - -/obj/item/clothing/accessory/wcoat/swvest/green - name = "green sweater vest" - icon_state = "sweatervest_green" - item_state = "sweatervest_green" - -/* - * Sweaters - */ - -/obj/item/clothing/accessory/sweater - name = "sweater" - desc = "A warm knit sweater." - icon_override = 'icons/inventory/accessory/mob.dmi' - icon_state = "sweater" - slot_flags = SLOT_OCLOTHING | SLOT_TIE - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - siemens_coefficient = 0.9 - w_class = ITEMSIZE_NORMAL - slot = ACCESSORY_SLOT_OVER - -/obj/item/clothing/accessory/sweater/pink - name = "pink sweater" - desc = "A warm knit sweater. This one's pink in color." - icon_state = "sweater_pink" - -/obj/item/clothing/accessory/sweater/mint - name = "mint sweater" - desc = "A warm knit sweater. This one has a minty tint to it." - icon_state = "sweater_mint" - -/obj/item/clothing/accessory/sweater/blue - name = "blue sweater" - desc = "A warm knit sweater. This one's colored in a lighter blue." - icon_state = "sweater_blue" - -/obj/item/clothing/accessory/sweater/heart - name = "heart sweater" - desc = "A warm knit sweater. This one's colored in a lighter blue, and has a big pink heart right in the center!" - icon_state = "sweater_blueheart" - -/obj/item/clothing/accessory/sweater/nt - name = "dark blue sweater" - desc = "A warm knit sweater. This one's a darker blue." - icon_state = "sweater_nt" - -/obj/item/clothing/accessory/sweater/keyhole - name = "keyhole sweater" - desc = "A lavender sweater with an open chest." - icon_state = "keyholesweater" - -/obj/item/clothing/accessory/sweater/blackneck - name = "black turtleneck" - desc = "A tight turtleneck, entirely black in coloration." - icon_state = "turtleneck_black" - -/obj/item/clothing/accessory/sweater/winterneck - name = "Christmas turtleneck" - desc = "A really cheesy holiday sweater, it actually kinda itches." - icon_state = "turtleneck_winterred" - -/obj/item/clothing/accessory/sweater/uglyxmas - name = "ugly Christmas sweater" - desc = "A gift that probably should've stayed in the back of the closet." - icon_state = "uglyxmas" - -/obj/item/clothing/accessory/sweater/flowersweater - name = "flowery sweater" - desc = "An oversized and flowery pink sweater." - icon_state = "flowersweater" - -/obj/item/clothing/accessory/sweater/redneck - name = "red turtleneck" - desc = "A comfortable turtleneck in a dark red." - icon_state = "turtleneck_red" - -/obj/item/clothing/accessory/sweater/virgin - name = "virgin killer" - desc = "A knit sweater that leaves little to the imagination." - icon_state = "virginkiller" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/* - * Misc - */ - -/obj/item/clothing/accessory/cowledvest - name = "cowled vest" - desc = "A body warmer for the 24th century." //VOREStation Edit - icon_state = "cowled_vest" - -/obj/item/clothing/accessory/asymmetric - name = "blue asymmetrical jacket" - desc = "Insultingly avant-garde in Prussian blue." - icon_state = "asym_blue" - -/obj/item/clothing/accessory/asymmetric/purple - name = "purple asymmetrical jacket" - desc = "Insultingly avant-garde in mauve." - icon_state = "asym_purple" - -/obj/item/clothing/accessory/asymmetric/green - name = "green asymmetrical jacket" - desc = "Insultingly avant-garde in aqua." - icon_state = "asym_green" - -/obj/item/clothing/accessory/asymovercoat - name = "orange asymmetrical overcoat" - desc = "An asymmetrical orange overcoat in a 2320's fashion." - icon_state = "asymovercoat" - -/* - * Cowboy Vests - */ - -/obj/item/clothing/accessory/cowboy_vest - name = "ranger cowboy vest" - desc = "A rugged looking vest made from leather. For those that tame the wilds." - icon_state = "cowboyvest_ranger" - -/obj/item/clothing/accessory/cowboy_vest/brown - name = "brown cowboy vest" - icon_state = "cowboyvest_brown" - -/obj/item/clothing/accessory/cowboy_vest/grey - name = "grey cowboy vest" - icon_state = "cowboyvest_grey" +/* + * Formal + */ + +/obj/item/clothing/accessory/vest + name = "black vest" + desc = "Slick black suit vest." + icon_state = "det_vest" + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/jacket + name = "tan suit jacket" + desc = "Cozy suit jacket." + icon_state = "tan_jacket" + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/jacket/red + name = "red suit jacket" + desc = "Relaxing suit jacket." + icon_state = "red_jacket" + +/obj/item/clothing/accessory/jacket/teal + name = "teal suit jacket" + desc = "Relaxing suit jacket." + icon_state = "teal_jacket" + +/obj/item/clothing/accessory/jacket/green + name = "green suit jacket" + desc = "Relaxing suit jacket." + icon_state = "green_jacket" + +/obj/item/clothing/accessory/jacket/charcoal + name = "charcoal suit jacket" + desc = "Strict suit jacket." + icon_state = "charcoal_jacket" + +/obj/item/clothing/accessory/jacket/navy + name = "navy suit jacket" + desc = "Official suit jacket." + icon_state = "navy_jacket" + +/obj/item/clothing/accessory/jacket/burgundy + name = "burgundy suit jacket" + desc = "Expensive suit jacket." + icon_state = "burgundy_jacket" + +/obj/item/clothing/accessory/jacket/checkered + name = "checkered suit jacket" + desc = "Lucky suit jacket." + icon_state = "checkered_jacket" + +/obj/item/clothing/accessory/jacket/gambler + name = "gambler suit jacket" + desc = "Chairman suit jacket." + icon_state = "gambler_jacket" + +/obj/item/clothing/accessory/jacket/extravagant + name = "extravagant suit jacket" + desc = "Luxury suit jacket." + icon_state = "extravagant_jacket" + +/* + * Hawaiian + */ + +/obj/item/clothing/accessory/hawaiian + name = "hawaiian shirt" + desc = "You probably need some welder googles to look at this." + icon_state = "hawaiian_cyan" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + slot_flags = SLOT_OCLOTHING | SLOT_TIE + body_parts_covered = UPPER_TORSO|LOWER_TORSO + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/hawaiian/blue + name = "blue hawaiian shirt" + icon_state = "hawaiian_blue" + +/obj/item/clothing/accessory/hawaiian/pink + name = "pink hawaiian shirt" + icon_state = "hawaiian_pink" + +/obj/item/clothing/accessory/hawaiian/red + name = "red hawaiian shirt" + icon_state = "hawaiian_red" + +/obj/item/clothing/accessory/hawaiian/yellow + name = "yellow hawaiian shirt" + icon_state = "hawaiian_yellow" + +/obj/item/clothing/accessory/hawaiian_random + name = "random hawaiian shirt" + desc = "A random set of hawaiian shirts for style." + +/obj/item/clothing/accessory/hawaiian_random/New() + return pick( + prob(2);/obj/item/clothing/accessory/hawaiian, + prob(2);/obj/item/clothing/accessory/hawaiian/blue, + prob(2);/obj/item/clothing/accessory/hawaiian/pink, + prob(2);/obj/item/clothing/accessory/hawaiian/red, + prob(2);/obj/item/clothing/accessory/hawaiian/yellow + ) + +/* + * 80s + */ + +/obj/item/clothing/accessory/tropical + name = "black tropical shirt" + desc = "A classic themed neosilk tropical shirt. This one makes you feel like an animal." + icon_state = "animalstyle" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + slot_flags = SLOT_OCLOTHING | SLOT_TIE + body_parts_covered = UPPER_TORSO|LOWER_TORSO + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/tropical/green + name = "puke-green tropical shirt" + desc = "A classic themed neosilk tropical shirt. This one makes you look like puke." + icon_state = "tropicopuke" + +/obj/item/clothing/accessory/tropical/pink + name = "pink tropical shirt" + desc = "A classic themed neosilk tropical shirt. This one makes you feel nostalgic." + icon_state = "3005vintage" + +/obj/item/clothing/accessory/tropical/blue + name = "blue tropical shirt" + desc = "A classic themed neosilk tropical shirt. This one makes you feel out of touch." + icon_state = "miamivice" + +/obj/item/clothing/accessory/tropical_random/New() + return pick( + prob(2);/obj/item/clothing/accessory/tropical, + prob(2);/obj/item/clothing/accessory/tropical/green, + prob(2);/obj/item/clothing/accessory/tropical/pink, + prob(2);/obj/item/clothing/accessory/tropical/blue + ) + +/* + * Chaps + */ + +/obj/item/clothing/accessory/chaps + name = "brown chaps" + desc = "A pair of loose, brown leather chaps." + icon_state = "chaps" + +/obj/item/clothing/accessory/chaps/black + name = "black chaps" + desc = "A pair of loose, black leather chaps." + icon_state = "chaps_black" + +/* + * Poncho + */ + +/obj/item/clothing/accessory/poncho + name = "poncho" + desc = "A simple, comfortable poncho." + icon_state = "classicponcho" + item_state = "classicponcho" + icon_override = 'icons/inventory/accessory/mob.dmi' + var/fire_resist = T0C+100 + allowed = list(/obj/item/weapon/tank/emergency/oxygen) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + slot_flags = SLOT_OCLOTHING | SLOT_TIE + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + slot = ACCESSORY_SLOT_OVER + sprite_sheets = list( + SPECIES_TESHARI = 'icons/inventory/suit/mob_teshari.dmi' + ) + +/obj/item/clothing/accessory/poncho/equipped() //Solution for race-specific sprites for an accessory which is also a suit. Suit icons break if you don't use icon override which then also overrides race-specific sprites. + ..() + var/mob/living/carbon/human/H = loc + if(istype(H) && H.wear_suit == src) + if(H.species.name == SPECIES_TESHARI) + icon_override = 'icons/inventory/suit/mob_teshari.dmi' + else + icon_override = 'icons/inventory/accessory/mob.dmi' + update_clothing_icon() + +/obj/item/clothing/accessory/poncho/dropped() //Resets the override to prevent the wrong .dmi from being used because equipped only triggers when wearing ponchos as suits. + icon_override = null + +/obj/item/clothing/accessory/poncho/green + name = "green poncho" + desc = "A simple, comfortable cloak without sleeves. This one is green." + icon_state = "greenponcho" + item_state = "greenponcho" + +/obj/item/clothing/accessory/poncho/red + name = "red poncho" + desc = "A simple, comfortable cloak without sleeves. This one is red." + icon_state = "redponcho" + item_state = "redponcho" + +/obj/item/clothing/accessory/poncho/purple + name = "purple poncho" + desc = "A simple, comfortable cloak without sleeves. This one is purple." + icon_state = "purpleponcho" + item_state = "purpleponcho" + +/obj/item/clothing/accessory/poncho/blue + name = "blue poncho" + desc = "A simple, comfortable cloak without sleeves. This one is blue." + icon_state = "blueponcho" + item_state = "blueponcho" + +/obj/item/clothing/accessory/poncho/roles/security + name = "security poncho" + desc = "A simple, comfortable cloak without sleeves. This one is black and red, standard NanoTrasen Security colors." + icon_state = "secponcho" + item_state = "secponcho" + +/obj/item/clothing/accessory/poncho/roles/medical + name = "medical poncho" + desc = "A simple, comfortable cloak without sleeves. This one is white with green and blue tint, standard Medical colors." + icon_state = "medponcho" + item_state = "medponcho" + +/obj/item/clothing/accessory/poncho/roles/engineering + name = "engineering poncho" + desc = "A simple, comfortable cloak without sleeves. This one is yellow and orange, standard Engineering colors." + icon_state = "engiponcho" + item_state = "engiponcho" + +/obj/item/clothing/accessory/poncho/roles/science + name = "science poncho" + desc = "A simple, comfortable cloak without sleeves. This one is white with purple trim, standard NanoTrasen Science colors." + icon_state = "sciponcho" + item_state = "sciponcho" + +/obj/item/clothing/accessory/poncho/roles/cargo + name = "cargo poncho" + desc = "A simple, comfortable cloak without sleeves. This one is tan and grey, the colors of Cargo." + icon_state = "cargoponcho" + item_state = "cargoponcho" + +/* + * Cloak + */ + +/obj/item/clothing/accessory/poncho/roles/cloak + name = "quartermaster's cloak" + desc = "An elaborate brown and gold cloak." + icon_state = "qmcloak" + item_state = "qmcloak" + body_parts_covered = null + +/obj/item/clothing/accessory/poncho/roles/cloak/ce + name = "chief engineer's cloak" + desc = "An elaborate cloak worn by the chief engineer." + icon_state = "cecloak" + item_state = "cecloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/cmo + name = "chief medical officer's cloak" + desc = "An elaborate cloak meant to be worn by the chief medical officer." + icon_state = "cmocloak" + item_state = "cmocloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/hop + name = "head of personnel's cloak" + desc = "An elaborate cloak meant to be worn by the head of personnel." + icon_state = "hopcloak" + item_state = "hopcloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/rd + name = "research director's cloak" + desc = "An elaborate cloak meant to be worn by the research director." + icon_state = "rdcloak" + item_state = "rdcloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/qm + name = "quartermaster's cloak" + desc = "An elaborate cloak meant to be worn by the quartermaster." + icon_state = "qmcloak" + item_state = "qmcloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/hos + name = "head of security's cloak" + desc = "An elaborate cloak meant to be worn by the head of security." + icon_state = "hoscloak" + item_state = "hoscloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/captain + name = "site manager's cloak" + desc = "An elaborate cloak meant to be worn by the site manager." + icon_state = "capcloak" + item_state = "capcloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/cargo + name = "brown cloak" + desc = "A simple brown and black cloak." + icon_state = "cargocloak" + item_state = "cargocloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/mining + name = "trimmed purple cloak" + desc = "A trimmed purple and brown cloak." + icon_state = "miningcloak" + item_state = "miningcloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/security + name = "red cloak" + desc = "A simple red and black cloak." + icon_state = "seccloak" + item_state = "seccloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/service + name = "green cloak" + desc = "A simple green and blue cloak." + icon_state = "servicecloak" + item_state = "servicecloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/engineer + name = "gold cloak" + desc = "A simple gold and brown cloak." + icon_state = "engicloak" + item_state = "engicloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/atmos + name = "yellow cloak" + desc = "A trimmed yellow and blue cloak." + icon_state = "atmoscloak" + item_state = "atmoscloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/research + name = "purple cloak" + desc = "A simple purple and white cloak." + icon_state = "scicloak" + item_state = "scicloak" + +/obj/item/clothing/accessory/poncho/roles/cloak/medical + name = "blue cloak" + desc = "A simple blue and white cloak." + icon_state = "medcloak" + item_state = "medcloak" + + +/obj/item/clothing/accessory/poncho/roles/cloak/custom //A colorable cloak + name = "cloak" + desc = "A simple, bland cloak." + icon_state = "colorcloak" + item_state = "colorcloak" + +/obj/item/clothing/accessory/wcoat + name = "waistcoat" + desc = "For some classy, murderous fun." + icon_state = "vest" + item_state = "vest" + icon_override = 'icons/inventory/accessory/mob.dmi' + item_state_slots = list(slot_r_hand_str = "wcoat", slot_l_hand_str = "wcoat") + allowed = list(/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight, /obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + slot_flags = SLOT_OCLOTHING | SLOT_TIE + body_parts_covered = UPPER_TORSO|LOWER_TORSO + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/wcoat/red + name = "red waistcoat" + icon_state = "red_waistcoat" + item_state = "red_waistcoat" + +/obj/item/clothing/accessory/wcoat/grey + name = "grey waistcoat" + icon_state = "grey_waistcoat" + item_state = "grey_waistcoat" + +/obj/item/clothing/accessory/wcoat/brown + name = "brown waistcoat" + icon_state = "brown_waistcoat" + item_state = "brown_waistcoat" + +/obj/item/clothing/accessory/wcoat/gentleman + name = "elegant waistcoat" + icon_state = "elegant_waistcoat" + item_state = "elegant_waistcoat" + +/* + * Sweatervests + */ + +/obj/item/clothing/accessory/wcoat/swvest + name = "black sweater vest" + desc = "A sleeveless sweater. Wear this if you don't want your arms to be warm, or if you're a nerd." + icon_state = "sweatervest" + item_state = "sweatervest" + +/obj/item/clothing/accessory/wcoat/swvest/blue + name = "blue sweater vest" + icon_state = "sweatervest_blue" + item_state = "sweatervest_blue" + +/obj/item/clothing/accessory/wcoat/swvest/red + name = "red sweater vest" + icon_state = "sweatervest_red" + item_state = "sweatervest_red" + +/obj/item/clothing/accessory/wcoat/swvest/green + name = "green sweater vest" + icon_state = "sweatervest_green" + item_state = "sweatervest_green" + +/* + * Sweaters + */ + +/obj/item/clothing/accessory/sweater + name = "sweater" + desc = "A warm knit sweater." + icon_override = 'icons/inventory/accessory/mob.dmi' + icon_state = "sweater" + slot_flags = SLOT_OCLOTHING | SLOT_TIE + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/sweater/pink + name = "pink sweater" + desc = "A warm knit sweater. This one's pink in color." + icon_state = "sweater_pink" + +/obj/item/clothing/accessory/sweater/mint + name = "mint sweater" + desc = "A warm knit sweater. This one has a minty tint to it." + icon_state = "sweater_mint" + +/obj/item/clothing/accessory/sweater/blue + name = "blue sweater" + desc = "A warm knit sweater. This one's colored in a lighter blue." + icon_state = "sweater_blue" + +/obj/item/clothing/accessory/sweater/heart + name = "heart sweater" + desc = "A warm knit sweater. This one's colored in a lighter blue, and has a big pink heart right in the center!" + icon_state = "sweater_blueheart" + +/obj/item/clothing/accessory/sweater/nt + name = "dark blue sweater" + desc = "A warm knit sweater. This one's a darker blue." + icon_state = "sweater_nt" + +/obj/item/clothing/accessory/sweater/keyhole + name = "keyhole sweater" + desc = "A lavender sweater with an open chest." + icon_state = "keyholesweater" + +/obj/item/clothing/accessory/sweater/blackneck + name = "black turtleneck" + desc = "A tight turtleneck, entirely black in coloration." + icon_state = "turtleneck_black" + +/obj/item/clothing/accessory/sweater/winterneck + name = "Christmas turtleneck" + desc = "A really cheesy holiday sweater, it actually kinda itches." + icon_state = "turtleneck_winterred" + +/obj/item/clothing/accessory/sweater/uglyxmas + name = "ugly Christmas sweater" + desc = "A gift that probably should've stayed in the back of the closet." + icon_state = "uglyxmas" + +/obj/item/clothing/accessory/sweater/flowersweater + name = "flowery sweater" + desc = "An oversized and flowery pink sweater." + icon_state = "flowersweater" + +/obj/item/clothing/accessory/sweater/redneck + name = "red turtleneck" + desc = "A comfortable turtleneck in a dark red." + icon_state = "turtleneck_red" + +/obj/item/clothing/accessory/sweater/virgin + name = "virgin killer" + desc = "A knit sweater that leaves little to the imagination." + icon_state = "virginkiller" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/* + * Misc + */ + +/obj/item/clothing/accessory/cowledvest + name = "cowled vest" + desc = "A body warmer for the 24th century." //VOREStation Edit + icon_state = "cowled_vest" + +/obj/item/clothing/accessory/asymmetric + name = "blue asymmetrical jacket" + desc = "Insultingly avant-garde in Prussian blue." + icon_state = "asym_blue" + +/obj/item/clothing/accessory/asymmetric/purple + name = "purple asymmetrical jacket" + desc = "Insultingly avant-garde in mauve." + icon_state = "asym_purple" + +/obj/item/clothing/accessory/asymmetric/green + name = "green asymmetrical jacket" + desc = "Insultingly avant-garde in aqua." + icon_state = "asym_green" + +/obj/item/clothing/accessory/asymovercoat + name = "orange asymmetrical overcoat" + desc = "An asymmetrical orange overcoat in a 2320's fashion." + icon_state = "asymovercoat" + +/* + * Cowboy Vests + */ + +/obj/item/clothing/accessory/cowboy_vest + name = "ranger cowboy vest" + desc = "A rugged looking vest made from leather. For those that tame the wilds." + icon_state = "cowboyvest_ranger" + +/obj/item/clothing/accessory/cowboy_vest/brown + name = "brown cowboy vest" + icon_state = "cowboyvest_brown" + +/obj/item/clothing/accessory/cowboy_vest/grey + name = "grey cowboy vest" + icon_state = "cowboyvest_grey" + +//Replikant Vests + +/obj/item/clothing/accessory/replika + name = "generic" + desc = "generic" + icon = 'icons/inventory/accessory/item.dmi' + icon_state = "klbr" + icon_override = 'icons/inventory/accessory/mob.dmi' + item_state_slots = list(SLOT_ID_RIGHT_HAND = "armor", SLOT_ID_LEFT_HAND = "armor") + allowed = list(/obj/item/weapon/gun,/obj/item/weapon/reagent_containers/spray/pepper,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/device/flashlight/maglight,/obj/item/clothing/head/helmet) + slot_flags = SLOT_OCLOTHING | SLOT_TIE + body_parts_covered = UPPER_TORSO|ARMS + siemens_coefficient = 0.9 + w_class = ITEMSIZE_NORMAL + slot = ACCESSORY_SLOT_OVER + +/obj/item/clothing/accessory/replika/klbr + name = "controller replikant chestplate" + desc = "A sloped titanium-composite chest plate fitted for use by 2nd generation biosynthetics. The right shoulder has been painted an imposing shade of red." + icon_state = "klbr" + +/obj/item/clothing/accessory/replika/lstr + name = "combat-engineer replikant chestplate" + desc = "A sloped titanium-composite chest plate fitted for use by 2nd generation biosynthetics. This plain-white version is a staple of biosynths assinged to combat-engineering duties." + icon_state = "lstr" + +/obj/item/clothing/accessory/replika/stcr + name = "security-controller replikant chestplate" + desc = "A sloped titanium-composite chest plate fitted for use by 2nd generation biosynthetics. This version sports multiple red adjustable straps and a lack of shoulder pads." + icon_state = "stcr" + +/obj/item/clothing/accessory/replika/star + name = "security-technician replikant chestplate" + desc = "A sloped titanium-composite chest plate with a matte black finish, fitted for use by 2nd generation biosynthetics. Comes with red adjustable straps." + icon_state = "star" diff --git a/code/modules/clothing/under/accessories/lockets.dm b/code/modules/clothing/under/accessories/lockets.dm index dcc775d878b..ef9ea3623ac 100644 --- a/code/modules/clothing/under/accessories/lockets.dm +++ b/code/modules/clothing/under/accessories/lockets.dm @@ -1,47 +1,47 @@ -/obj/item/clothing/accessory/locket - name = "silver locket" - desc = "A small locket of high-quality metal." - icon_state = "locket" - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_MASK | SLOT_TIE - slot = ACCESSORY_SLOT_DECOR - var/base_icon - var/open - var/obj/item/held //Item inside locket. - -/obj/item/clothing/accessory/locket/attack_self(mob/user as mob) - if(!base_icon) - base_icon = icon_state - - if(!("[base_icon]_open" in cached_icon_states(icon))) - to_chat(user, "\The [src] doesn't seem to open.") - return - - open = !open - to_chat(user, "You flip \the [src] [open?"open":"closed"].") - if(open) - icon_state = "[base_icon]_open" - if(held) - to_chat(user, "\The [held] falls out!") - held.loc = get_turf(user) - held = null - else - icon_state = "[base_icon]" - -/obj/item/clothing/accessory/locket/attackby(var/obj/item/O as obj, mob/user as mob) - if(!open) - to_chat(user, "You have to open it first.") - return - - if(istype(O,/obj/item/weapon/paper) || istype(O, /obj/item/weapon/photo)) - if(held) - to_chat(usr, "\The [src] already has something inside it.") - else - to_chat(usr, "You slip [O] into [src].") - user.drop_item() - O.loc = src - held = O - return - ..() +/obj/item/clothing/accessory/locket + name = "silver locket" + desc = "A small locket of high-quality metal." + icon_state = "locket" + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_MASK | SLOT_TIE + slot = ACCESSORY_SLOT_DECOR + var/base_icon + var/open + var/obj/item/held //Item inside locket. + +/obj/item/clothing/accessory/locket/attack_self(mob/user as mob) + if(!base_icon) + base_icon = icon_state + + if(!("[base_icon]_open" in cached_icon_states(icon))) + to_chat(user, "\The [src] doesn't seem to open.") + return + + open = !open + to_chat(user, "You flip \the [src] [open?"open":"closed"].") + if(open) + icon_state = "[base_icon]_open" + if(held) + to_chat(user, "\The [held] falls out!") + held.loc = get_turf(user) + held = null + else + icon_state = "[base_icon]" + +/obj/item/clothing/accessory/locket/attackby(var/obj/item/O as obj, mob/user as mob) + if(!open) + to_chat(user, "You have to open it first.") + return + + if(istype(O,/obj/item/weapon/paper) || istype(O, /obj/item/weapon/photo)) + if(held) + to_chat(usr, "\The [src] already has something inside it.") + else + to_chat(usr, "You slip [O] into [src].") + user.drop_item() + O.loc = src + held = O + return + ..() diff --git a/code/modules/clothing/under/altevian_vr.dm b/code/modules/clothing/under/altevian_vr.dm index d7720272f36..4ee26740e25 100644 --- a/code/modules/clothing/under/altevian_vr.dm +++ b/code/modules/clothing/under/altevian_vr.dm @@ -1,6 +1,6 @@ /obj/item/clothing/under/pants/altevian name = "Altevian Hegemony Civilian Pants" - desc = "An extremely comfortable set of clothing that's made to help people handle their day to day work around the fleets with little to no discomfort." + desc = "A comfortable set of clothing for people to handle their day to day work around the fleets with little to no discomfort." icon = 'icons/inventory/uniform/item_vr.dmi' icon_override = 'icons/inventory/uniform/mob_vr.dmi' icon_state = "altevian-pants-civ" @@ -52,7 +52,7 @@ /obj/item/clothing/under/altevian name = "Altevian Duty Jumpsuit" - desc = "A uniform commonly seen from altevians during their work. The material on this uniform seems to be made of durable thread that can handle the stress of most matters of labor." + desc = "A uniform commonly seen worn by altevians. The material on this uniform is made of a durable thread that can handle the stress of most forms of labor." icon = 'icons/inventory/uniform/item_vr.dmi' icon_override = 'icons/inventory/uniform/mob_vr.dmi' icon_state = "altevian-specialist" @@ -82,4 +82,4 @@ name = "Altevian Engineering Duty Jumpsuit" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) icon_state = "altevian-specialist-eng" - worn_state = "altevian-specialist-eng" \ No newline at end of file + worn_state = "altevian-specialist-eng" diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index b20524a1362..afdc9917c94 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -1,143 +1,143 @@ -/obj/item/clothing/under/color/black - name = "black jumpsuit" - icon_state = "black" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/blackf - name = "feminine black jumpsuit" - desc = "It's very smart and in a ladies size!" - icon_state = "black" - worn_state = "blackf" - -/obj/item/clothing/under/color/blackjumpskirt - name = "black jumpskirt" - desc = "A slimming black jumpskirt." - icon_state = "blackjumpskirt" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/under/color/blue - name = "blue jumpsuit" - icon_state = "blue" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/green - name = "green jumpsuit" - icon_state = "green" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/grey - name = "grey jumpsuit" - icon_state = "grey" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/orange - name = "orange jumpsuit" - icon_state = "orange" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/prison - name = "prison jumpsuit" - desc = "It's standardized prisoner-wear. Its suit sensors are permanently set to the \"Tracking\" position." - icon_state = "prison" - has_sensor = 2 - sensor_mode = 3 - -/obj/item/clothing/under/color/pink - name = "pink jumpsuit" - icon_state = "pink" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/red - name = "red jumpsuit" - icon_state = "red" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/white - name = "white jumpsuit" - icon_state = "white" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/yellow - name = "yellow jumpsuit" - icon_state = "yellow" - rolled_sleeves = 0 - -/obj/item/clothing/under/psyche - name = "psychedelic jumpsuit" - desc = "Groovy!" - icon_state = "psyche" - -/obj/item/clothing/under/color/lightblue - name = "lightblue jumpsuit" - desc = "A light blue jumpsuit." - icon_state = "lightblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/aqua - name = "aqua jumpsuit" - desc = "An aqua jumpsuit." - icon_state = "aqua" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/color - name = "purple jumpsuit" - desc = "The latest in space fashion." - icon_state = "purple" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightpurple - name = "lightpurple jumpsuit" - desc = "A light purple jumpsuit." - icon_state = "lightpurple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightgreen - name = "lightgreen jumpsuit" - desc = "A light green jumpsuit." - icon_state = "lightgreen" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightbrown - name = "lightbrown jumpsuit" - desc = "A light brown jumpsuit." - icon_state = "lightbrown" - rolled_sleeves = 0 - -/obj/item/clothing/under/color/brown - name = "brown jumpsuit" - desc = "A brown jumpsuit." - icon_state = "brown" - item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/yellowgreen - name = "yellowgreen jumpsuit" - desc = "A... yellow green jumpsuit?" - icon_state = "yellowgreen" - item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/darkblue - name = "darkblue jumpsuit" - desc = "A dark blue jumpsuit." - icon_state = "darkblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/lightred - name = "lightred jumpsuit" - desc = "A light red jumpsuit." - icon_state = "lightred" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - rolled_sleeves = 0 - -/obj/item/clothing/under/color/darkred - name = "darkred jumpsuit" - desc = "A dark red jumpsuit." - icon_state = "darkred" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - rolled_sleeves = 0 +/obj/item/clothing/under/color/black + name = "black jumpsuit" + icon_state = "black" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/blackf + name = "feminine black jumpsuit" + desc = "It's very smart and in a ladies size!" + icon_state = "black" + worn_state = "blackf" + +/obj/item/clothing/under/color/blackjumpskirt + name = "black jumpskirt" + desc = "A slimming black jumpskirt." + icon_state = "blackjumpskirt" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/under/color/blue + name = "blue jumpsuit" + icon_state = "blue" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/green + name = "green jumpsuit" + icon_state = "green" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/grey + name = "grey jumpsuit" + icon_state = "grey" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/orange + name = "orange jumpsuit" + icon_state = "orange" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/prison + name = "prison jumpsuit" + desc = "It's standardized prisoner-wear. Its suit sensors are permanently set to the \"Tracking\" position." + icon_state = "prison" + has_sensor = 2 + sensor_mode = 3 + +/obj/item/clothing/under/color/pink + name = "pink jumpsuit" + icon_state = "pink" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/red + name = "red jumpsuit" + icon_state = "red" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/white + name = "white jumpsuit" + icon_state = "white" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/yellow + name = "yellow jumpsuit" + icon_state = "yellow" + rolled_sleeves = 0 + +/obj/item/clothing/under/psyche + name = "psychedelic jumpsuit" + desc = "Groovy!" + icon_state = "psyche" + +/obj/item/clothing/under/color/lightblue + name = "lightblue jumpsuit" + desc = "A light blue jumpsuit." + icon_state = "lightblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/aqua + name = "aqua jumpsuit" + desc = "An aqua jumpsuit." + icon_state = "aqua" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/color + name = "purple jumpsuit" + desc = "The latest in space fashion." + icon_state = "purple" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightpurple + name = "lightpurple jumpsuit" + desc = "A light purple jumpsuit." + icon_state = "lightpurple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightgreen + name = "lightgreen jumpsuit" + desc = "A light green jumpsuit." + icon_state = "lightgreen" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightbrown + name = "lightbrown jumpsuit" + desc = "A light brown jumpsuit." + icon_state = "lightbrown" + rolled_sleeves = 0 + +/obj/item/clothing/under/color/brown + name = "brown jumpsuit" + desc = "A brown jumpsuit." + icon_state = "brown" + item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/yellowgreen + name = "yellowgreen jumpsuit" + desc = "A... yellow green jumpsuit?" + icon_state = "yellowgreen" + item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/darkblue + name = "darkblue jumpsuit" + desc = "A dark blue jumpsuit." + icon_state = "darkblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/lightred + name = "lightred jumpsuit" + desc = "A light red jumpsuit." + icon_state = "lightred" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + rolled_sleeves = 0 + +/obj/item/clothing/under/color/darkred + name = "darkred jumpsuit" + desc = "A dark red jumpsuit." + icon_state = "darkred" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + rolled_sleeves = 0 diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index da5d5627beb..9f714d9a0c9 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -1,213 +1,213 @@ -//Alphabetical order of civilian jobs. - -/obj/item/clothing/under/rank/bartender - desc = "It looks like it could use some more flair." - name = "bartender's uniform" - icon_state = "ba_suit" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/bartender/skirt - desc = "Short and cute." - name = "bartender's skirt" - icon_state = "ba_suit_skirt" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - -/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. - desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Site Manager\"." - name = "site manager's jumpsuit" - icon_state = "captain" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/cargo - name = "quartermaster's jumpsuit" - desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qm" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/cargo/jeans - name = "quartermaster's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "qmj" - -/obj/item/clothing/under/rank/cargo/jeans/female - name = "quartermaster's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "qmjf" - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/cargotech - name = "cargo technician's jumpsuit" - desc = "Shooooorts! They're comfy and easy to wear!" - icon_state = "cargo" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/cargotech/jeans - name = "cargo technician's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "cargoj" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/cargotech/jeans/female - name = "cargo technician's jumpjeans" - desc = "Jeeeaaans! They're comfy!" - icon_state = "cargojf" - -/obj/item/clothing/under/rank/chaplain - desc = "It's a black jumpsuit, often worn by religious folk." - name = "chaplain's jumpsuit" - icon_state = "chaplain" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/chef - desc = "It's an apron which is given only to the most hardcore chefs in space." - name = "chef's uniform" - icon_state = "chef" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/chef/alt - desc = "It's an apron which is given only to the chefs that swear the most." - name = "souschef's uniform" - icon_state = "souschef" - -/obj/item/clothing/under/rank/clown - name = "clown suit" - desc = "Honk!" - icon_state = "clown" - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/head_of_personnel - desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." - name = "head of personnel's jumpsuit" - icon_state = "hop" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_personnel_whimsy - desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." - name = "head of personnel's suit" - icon_state = "hopwhimsy" - item_state_slots = list(slot_r_hand_str = "hop", slot_l_hand_str = "hop") - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/hydroponics - desc = "It's a jumpsuit designed to protect against minor plant-related hazards." - name = "botanist's jumpsuit" - icon_state = "hydroponics" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - permeability_coefficient = 0.50 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/hydroponics/alt - icon_state = "hydro" - -/obj/item/clothing/under/rank/internalaffairs - desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." - name = "Internal Affairs uniform" - icon_state = "internalaffairs" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - starting_accessories = list(/obj/item/clothing/accessory/tie/black) - -/obj/item/clothing/under/rank/internalaffairs/skirt - desc = "The plain, professional attire of an Internal Affairs Agent. The top button is sewn shut." - name = "Internal Affairs skirt" - icon_state = "internalaffairs_skirt" - -/obj/item/clothing/under/rank/janitor - desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." - name = "janitor's jumpsuit" - icon_state = "janitor" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/janitor/alt - name = "janitor's overalls" - icon_state = "janitor_alt" - -/obj/item/clothing/under/lawyer - desc = "Slick threads." - name = "lawyer suit" - -/obj/item/clothing/under/lawyer/black - name = "black lawyer suit" - icon_state = "lawyer_black" - -/obj/item/clothing/under/lawyer/black/skirt - name = "black lawyer skirt" - icon_state = "lawyer_black_skirt" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/lawyer/female - name = "black lawyer suit" - icon_state = "black_suit_fem" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/lawyer/red - name = "red lawyer suit" - icon_state = "lawyer_red" - -/obj/item/clothing/under/lawyer/red/skirt - name = "red lawyer skirt" - icon_state = "lawyer_red_skirt" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - -/obj/item/clothing/under/lawyer/blue - name = "blue lawyer suit" - icon_state = "lawyer_blue" - -/obj/item/clothing/under/lawyer/blue/skirt - name = "blue lawyer skirt" - icon_state = "lawyer_blue_skirt" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - -/obj/item/clothing/under/lawyer/bluesuit - name = "blue suit" - desc = "A classy suit." - icon_state = "bluesuit" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - starting_accessories = list(/obj/item/clothing/accessory/tie/red) - -/obj/item/clothing/under/lawyer/bluesuit/skirt - name = "blue skirt suit" - icon_state = "bluesuit_skirt" - -/obj/item/clothing/under/lawyer/purpsuit - name = "purple suit" - icon_state = "lawyer_purp" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/under/lawyer/purpsuit/skirt - name = "purple skirt suit" - icon_state = "lawyer_purp_skirt" - -/obj/item/clothing/under/lawyer/oldman - name = "Old Man's Suit" - desc = "A classic suit for the older gentleman, with built in back support." - icon_state = "oldman" - item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") - -/obj/item/clothing/under/oldwoman - name = "Old Woman's Attire" - desc = "A typical outfit for the older woman, a lovely cardigan and comfortable skirt." - icon_state = "oldwoman" - item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") - -/obj/item/clothing/under/librarian - name = "sensible suit" - desc = "It's very... sensible." - icon_state = "red_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - -/obj/item/clothing/under/mime - name = "mime's outfit" - desc = "It's not very colourful." - icon_state = "mime" - -/obj/item/clothing/under/rank/miner - desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." - name = "shaft miner's jumpsuit" - icon_state = "miner" - rolled_sleeves = 0 +//Alphabetical order of civilian jobs. + +/obj/item/clothing/under/rank/bartender + desc = "It looks like it could use some more flair." + name = "bartender's uniform" + icon_state = "ba_suit" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/bartender/skirt + desc = "Short and cute." + name = "bartender's skirt" + icon_state = "ba_suit_skirt" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + +/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. + desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Site Manager\"." + name = "site manager's jumpsuit" + icon_state = "captain" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/cargo + name = "quartermaster's jumpsuit" + desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qm" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/cargo/jeans + name = "quartermaster's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "qmj" + +/obj/item/clothing/under/rank/cargo/jeans/female + name = "quartermaster's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "qmjf" + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/cargotech + name = "cargo technician's jumpsuit" + desc = "Shooooorts! They're comfy and easy to wear!" + icon_state = "cargo" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/cargotech/jeans + name = "cargo technician's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "cargoj" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/cargotech/jeans/female + name = "cargo technician's jumpjeans" + desc = "Jeeeaaans! They're comfy!" + icon_state = "cargojf" + +/obj/item/clothing/under/rank/chaplain + desc = "It's a black jumpsuit, often worn by religious folk." + name = "chaplain's jumpsuit" + icon_state = "chaplain" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/chef + desc = "It's an apron which is given only to the most hardcore chefs in space." + name = "chef's uniform" + icon_state = "chef" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/chef/alt + desc = "It's an apron which is given only to the chefs that swear the most." + name = "souschef's uniform" + icon_state = "souschef" + +/obj/item/clothing/under/rank/clown + name = "clown suit" + desc = "Honk!" + icon_state = "clown" + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/head_of_personnel + desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." + name = "head of personnel's jumpsuit" + icon_state = "hop" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_personnel_whimsy + desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." + name = "head of personnel's suit" + icon_state = "hopwhimsy" + item_state_slots = list(slot_r_hand_str = "hop", slot_l_hand_str = "hop") + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/hydroponics + desc = "It's a jumpsuit designed to protect against minor plant-related hazards." + name = "botanist's jumpsuit" + icon_state = "hydroponics" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + permeability_coefficient = 0.50 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/hydroponics/alt + icon_state = "hydro" + +/obj/item/clothing/under/rank/internalaffairs + desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." + name = "Internal Affairs uniform" + icon_state = "internalaffairs" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/tie/black) + +/obj/item/clothing/under/rank/internalaffairs/skirt + desc = "The plain, professional attire of an Internal Affairs Agent. The top button is sewn shut." + name = "Internal Affairs skirt" + icon_state = "internalaffairs_skirt" + +/obj/item/clothing/under/rank/janitor + desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." + name = "janitor's jumpsuit" + icon_state = "janitor" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/janitor/alt + name = "janitor's overalls" + icon_state = "janitor_alt" + +/obj/item/clothing/under/lawyer + desc = "Slick threads." + name = "lawyer suit" + +/obj/item/clothing/under/lawyer/black + name = "black lawyer suit" + icon_state = "lawyer_black" + +/obj/item/clothing/under/lawyer/black/skirt + name = "black lawyer skirt" + icon_state = "lawyer_black_skirt" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/lawyer/female + name = "black lawyer suit" + icon_state = "black_suit_fem" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/lawyer/red + name = "red lawyer suit" + icon_state = "lawyer_red" + +/obj/item/clothing/under/lawyer/red/skirt + name = "red lawyer skirt" + icon_state = "lawyer_red_skirt" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + +/obj/item/clothing/under/lawyer/blue + name = "blue lawyer suit" + icon_state = "lawyer_blue" + +/obj/item/clothing/under/lawyer/blue/skirt + name = "blue lawyer skirt" + icon_state = "lawyer_blue_skirt" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + +/obj/item/clothing/under/lawyer/bluesuit + name = "blue suit" + desc = "A classy suit." + icon_state = "bluesuit" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + starting_accessories = list(/obj/item/clothing/accessory/tie/red) + +/obj/item/clothing/under/lawyer/bluesuit/skirt + name = "blue skirt suit" + icon_state = "bluesuit_skirt" + +/obj/item/clothing/under/lawyer/purpsuit + name = "purple suit" + icon_state = "lawyer_purp" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/under/lawyer/purpsuit/skirt + name = "purple skirt suit" + icon_state = "lawyer_purp_skirt" + +/obj/item/clothing/under/lawyer/oldman + name = "Old Man's Suit" + desc = "A classic suit for the older gentleman, with built in back support." + icon_state = "oldman" + item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") + +/obj/item/clothing/under/oldwoman + name = "Old Woman's Attire" + desc = "A typical outfit for the older woman, a lovely cardigan and comfortable skirt." + icon_state = "oldwoman" + item_state_slots = list(slot_r_hand_str = "johnny", slot_l_hand_str = "johnny") + +/obj/item/clothing/under/librarian + name = "sensible suit" + desc = "It's very... sensible." + icon_state = "red_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + +/obj/item/clothing/under/mime + name = "mime's outfit" + desc = "It's not very colourful." + icon_state = "mime" + +/obj/item/clothing/under/rank/miner + desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." + name = "shaft miner's jumpsuit" + icon_state = "miner" + rolled_sleeves = 0 diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index f57cc95ce8c..a648e1d74ec 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -1,33 +1,33 @@ -//Contains: Engineering department jumpsuits -/obj/item/clothing/under/rank/chief_engineer - desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpsuit" - icon_state = "chief" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/atmospheric_technician - desc = "It's a jumpsuit worn by atmospheric technicians." - name = "atmospheric technician's jumpsuit" - icon_state = "atmos" - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/engineer - desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." - name = "engineer's jumpsuit" - icon_state = "engine" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/engineer/turtleneck - name = "engineering turtleneck" - desc = "It's a stylish turtleneck with minor radiation shielding. Nobody's going to see it behind the voidsuit, though." - icon_state = "turtle_eng" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/roboticist - desc = "It's a slimming black jumpsuit with reinforced seams; great for industrial work." - name = "roboticist's jumpsuit" - icon_state = "robotics" +//Contains: Engineering department jumpsuits +/obj/item/clothing/under/rank/chief_engineer + desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpsuit" + icon_state = "chief" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/atmospheric_technician + desc = "It's a jumpsuit worn by atmospheric technicians." + name = "atmospheric technician's jumpsuit" + icon_state = "atmos" + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/engineer + desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." + name = "engineer's jumpsuit" + icon_state = "engine" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/engineer/turtleneck + name = "engineering turtleneck" + desc = "It's a stylish turtleneck with minor radiation shielding. Nobody's going to see it behind the voidsuit, though." + icon_state = "turtle_eng" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/roboticist + desc = "It's a slimming black jumpsuit with reinforced seams; great for industrial work." + name = "roboticist's jumpsuit" + icon_state = "robotics" rolled_sleeves = 0 \ No newline at end of file diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index 013e3d917f7..00b0ed43f96 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -1,254 +1,254 @@ -/* - * Science - */ -/obj/item/clothing/under/rank/research_director - desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." - name = "research director's jumpsuit" - icon_state = "director" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - -/obj/item/clothing/under/rank/research_director/rdalt - desc = "A dress suit and slacks stained with hard work and dedication to science. Perhaps other things as well, but mostly hard work and dedication." - name = "head researcher uniform" - icon_state = "rdalt" - item_state_slots = list(slot_r_hand_str = "director", slot_l_hand_str = "director") - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - -/obj/item/clothing/under/rank/research_director/dress_rd - name = "research director dress uniform" - desc = "Feminine fashion for the style conscious RD. Its fabric provides minor protection from biological contaminants." - icon_state = "dress_rd" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/scientist - desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." - name = "scientist's jumpsuit" - icon_state = "science" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/scientist/turtleneck - name = "science turtleneck" - desc = "It's a stylish turtleneck weaved with an explosive-resistant, comfortable mesh. You don't have to look like a dork to be a dork." - icon_state = "turtle_sci" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/chemist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." - name = "chemist's jumpsuit" - icon_state = "chemistry" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/* - * Medical - */ -/obj/item/clothing/under/rank/chief_medical_officer - desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpsuit" - icon_state = "cmo" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/geneticist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." - name = "geneticist's jumpsuit" - icon_state = "genetics" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/virologist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." - name = "virologist's jumpsuit" - icon_state = "virology" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/nursesuit - desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." - name = "nurse's suit" - icon_state = "nursesuit" - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/nurse - desc = "A dress commonly worn by the nursing staff in the medical department." - name = "nurse's dress" - icon_state = "nurse" - item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/orderly - desc = "A white suit to be worn by medical attendants." - name = "orderly's uniform" - icon_state = "orderly" - item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/medical - desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "medical doctor's jumpsuit" - icon_state = "medical" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/medical/turtleneck - name = "medical turtleneck" - desc = "It's a stylish turtleneck made of bioresistant fiber. Look good, save lives- what more could you want?" - icon_state = "turtle_med" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/medical/paramedic - name = "paramedic uniform" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is the color scheme that designates a rapid first responder." - icon_state = "paramedic" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/medical/paramedic_alt - name = "short sleeve medical jumpsuit" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one has a cross on the chest denoting that the wearer is trained medical personnel." - icon_state = "medical_short" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/medical/scrubs - name = "blue scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." - icon_state = "scrubsblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/under/rank/medical/scrubs/green - name = "green scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." - icon_state = "scrubsgreen" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - -/obj/item/clothing/under/rank/medical/scrubs/purple - name = "purple scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." - icon_state = "scrubspurple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/under/rank/medical/scrubs/black - name = "black scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in black." - icon_state = "scrubsblack" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/under/rank/medical/scrubs/navyblue - name = "navy blue scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in navy blue." - icon_state = "scrubsnavyblue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/under/rank/medical/scrubs/white - name = "scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards" - icon_state = "scrubs" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/rank/paramedunidark - name = "dark paramedic uniform" - desc = "A dark jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramedicdark" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/parameduniskirtdark - name = "dark paramedic uniskirt" - desc = "A dark jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramedicdark_skirt" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/paramedunilight - name = "light paramedic uniform" - desc = "A light jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramediclight" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/parameduniskirtlight - name = "light paramedic uniskirt" - desc = "A light jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." - icon_state = "paramediclight_skirt" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/psych - desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." - name = "psychiatrist's jumpsuit" - icon_state = "psych" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/rank/psych/turtleneck - desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." - name = "psychologist's turtleneck" - icon_state = "psychturtle" - item_state_slots = list(slot_r_hand_str = "psyche", slot_l_hand_str = "psyche") - rolled_sleeves = 0 -/* - * Medsci, unused (i think) stuff - */ -/obj/item/clothing/under/rank/geneticist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "geneticist's jumpsuit" - icon_state = "genetics_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/chemist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "chemist's jumpsuit" - icon_state = "chemist_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/scientist_new - desc = "Made of a special fiber that gives special protection against biohazards and small explosions." - name = "scientist's jumpsuit" - icon_state = "scientist_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/virologist_new - desc = "Made of a special fiber that gives increased protection against biohazards." - name = "virologist's jumpsuit" - icon_state = "virologist_new" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) - rolled_sleeves = 0 +/* + * Science + */ +/obj/item/clothing/under/rank/research_director + desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." + name = "research director's jumpsuit" + icon_state = "director" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + +/obj/item/clothing/under/rank/research_director/rdalt + desc = "A dress suit and slacks stained with hard work and dedication to science. Perhaps other things as well, but mostly hard work and dedication." + name = "head researcher uniform" + icon_state = "rdalt" + item_state_slots = list(slot_r_hand_str = "director", slot_l_hand_str = "director") + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + +/obj/item/clothing/under/rank/research_director/dress_rd + name = "research director dress uniform" + desc = "Feminine fashion for the style conscious RD. Its fabric provides minor protection from biological contaminants." + icon_state = "dress_rd" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/scientist + desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." + name = "scientist's jumpsuit" + icon_state = "science" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/scientist/turtleneck + name = "science turtleneck" + desc = "It's a stylish turtleneck weaved with an explosive-resistant, comfortable mesh. You don't have to look like a dork to be a dork." + icon_state = "turtle_sci" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/chemist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + name = "chemist's jumpsuit" + icon_state = "chemistry" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/* + * Medical + */ +/obj/item/clothing/under/rank/chief_medical_officer + desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpsuit" + icon_state = "cmo" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/geneticist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." + name = "geneticist's jumpsuit" + icon_state = "genetics" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/virologist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." + name = "virologist's jumpsuit" + icon_state = "virology" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/nursesuit + desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." + name = "nurse's suit" + icon_state = "nursesuit" + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/nurse + desc = "A dress commonly worn by the nursing staff in the medical department." + name = "nurse's dress" + icon_state = "nurse" + item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/orderly + desc = "A white suit to be worn by medical attendants." + name = "orderly's uniform" + icon_state = "orderly" + item_state_slots = list(slot_r_hand_str = "nursesuit", slot_l_hand_str = "nursesuit") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/medical + desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "medical doctor's jumpsuit" + icon_state = "medical" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/medical/turtleneck + name = "medical turtleneck" + desc = "It's a stylish turtleneck made of bioresistant fiber. Look good, save lives- what more could you want?" + icon_state = "turtle_med" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/medical/paramedic + name = "paramedic uniform" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is the color scheme that designates a rapid first responder." + icon_state = "paramedic" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/medical/paramedic_alt + name = "short sleeve medical jumpsuit" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one has a cross on the chest denoting that the wearer is trained medical personnel." + icon_state = "medical_short" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/medical/scrubs + name = "blue scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." + icon_state = "scrubsblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/under/rank/medical/scrubs/green + name = "green scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." + icon_state = "scrubsgreen" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + +/obj/item/clothing/under/rank/medical/scrubs/purple + name = "purple scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." + icon_state = "scrubspurple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/under/rank/medical/scrubs/black + name = "black scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in black." + icon_state = "scrubsblack" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/under/rank/medical/scrubs/navyblue + name = "navy blue scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in navy blue." + icon_state = "scrubsnavyblue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/under/rank/medical/scrubs/white + name = "scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards" + icon_state = "scrubs" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/rank/paramedunidark + name = "dark paramedic uniform" + desc = "A dark jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramedicdark" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/parameduniskirtdark + name = "dark paramedic uniskirt" + desc = "A dark jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramedicdark_skirt" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/paramedunilight + name = "light paramedic uniform" + desc = "A light jumpsuit for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramediclight" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/parameduniskirtlight + name = "light paramedic uniskirt" + desc = "A light jumpskirt for those brave souls who have to deal with a CMO who thinks they're the do everything person." + icon_state = "paramediclight_skirt" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/psych + desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." + name = "psychiatrist's jumpsuit" + icon_state = "psych" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/rank/psych/turtleneck + desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." + name = "psychologist's turtleneck" + icon_state = "psychturtle" + item_state_slots = list(slot_r_hand_str = "psyche", slot_l_hand_str = "psyche") + rolled_sleeves = 0 +/* + * Medsci, unused (i think) stuff + */ +/obj/item/clothing/under/rank/geneticist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "geneticist's jumpsuit" + icon_state = "genetics_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/chemist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "chemist's jumpsuit" + icon_state = "chemist_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/scientist_new + desc = "Made of a special fiber that gives special protection against biohazards and small explosions." + name = "scientist's jumpsuit" + icon_state = "scientist_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/virologist_new + desc = "Made of a special fiber that gives increased protection against biohazards." + name = "virologist's jumpsuit" + icon_state = "virologist_new" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + rolled_sleeves = 0 diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index a001d5aa7cd..3d6bdfec0c0 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -1,217 +1,217 @@ -/* - * Contains: - * Security - * Detective - * Head of Security - */ - -/* - * Security - */ -/obj/item/clothing/under/rank/warden - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpsuit" - icon_state = "warden" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/security - name = "security officer's jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "security" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/security/modern - name = "modernized security officer's jumpsuit" - desc = "A recent redesign of the classic Security jumpsuit, featuring sturdy materials, joint padding, one giant zipper, and tight-fitting synthleather." - icon_state = "securitymodern" - item_state = "securitymodern" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - rolled_sleeves = -1 - worn_state = "securitymodern" - icon = 'icons/inventory/uniform/item.dmi' - default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' - -/obj/item/clothing/under/rank/security/turtleneck - name = "security turtleneck" - desc = "It's a stylish turtleneck made of a robust nanoweave. Nobody said the Law couldn't be fashionable." - icon_state = "turtle_sec" - rolled_down = -1 - rolled_sleeves = -1 - -/obj/item/clothing/under/rank/dispatch - name = "dispatcher's uniform" - desc = "A dress shirt and khakis with a security patch sewn on." - icon_state = "dispatch" - item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - siemens_coefficient = 0.9 - -/obj/item/clothing/under/rank/security2 - name = "security officer's uniform" - desc = "It's made of a slightly sturdier material, to allow for robust protection." - icon_state = "redshirt2" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/security/corp - icon_state = "sec_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/warden/corp - icon_state = "warden_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -/obj/item/clothing/under/tactical - name = "tactical jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "swatunder" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - armor = list(melee = 10, bullet = 5, laser = 5,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = -1 - -/* - * Detective - */ -/obj/item/clothing/under/det - name = "detective's suit" - desc = "A rumpled white dress shirt paired with well-worn grey slacks." - icon_state = "detective" - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip) - -/* -/obj/item/clothing/under/det/verb/rollup() - set name = "Roll Suit Sleeves" - set category = "Object" - set src in usr - var/unrolled = item_state_slots[slot_w_uniform_str] == initial(worn_state) - item_state_slots[slot_w_uniform_str] = unrolled ? "[worn_state]_r" : initial(worn_state) - var/mob/living/carbon/human/H = loc - H.update_inv_w_uniform(1) - to_chat(H, "You roll the sleeves of your shirt [unrolled ? "up" : "down"]") -*/ - -/obj/item/clothing/under/det/grey - icon_state = "detective2" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long) - -/obj/item/clothing/under/det/black - icon_state = "detective3" - item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") - desc = "An immaculate white dress shirt, paired with a pair of dark grey dress pants, a red tie, and a charcoal vest." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/vest) - -/obj/item/clothing/under/det/corporate - name = "detective's jumpsuit" - icon_state = "det_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - desc = "A more modern uniform for corporate investigators." - -/obj/item/clothing/under/det/waistcoat - icon_state = "detective" - desc = "A rumpled white dress shirt paired with well-worn grey slacks, complete with a blue striped tie, faux-gold tie clip, and waistcoat." - starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip, /obj/item/clothing/accessory/wcoat) - -/obj/item/clothing/under/det/grey/waistcoat - icon_state = "detective2" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, complete with a red striped tie and waistcoat." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) - -/obj/item/clothing/under/det/black/waistcoat - icon_state = "detective3" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, a red tie, and a charcoal vest." - starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) - -/obj/item/clothing/under/det/skirt - name = "detective's skirt" - icon_state = "detective_skirt" - desc = "A serious-looking white blouse paired with a formal black pencil skirt." - item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") - -/* - * Head of Security - */ -/obj/item/clothing/under/rank/head_of_security - desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpsuit" - icon_state = "hos" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_security/corp - icon_state = "hos_corporate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - rolled_sleeves = 0 - -//Jensen cosplay gear -/obj/item/clothing/under/rank/head_of_security/jensen - desc = "You never asked for anything that stylish." - name = "head of security's jumpsuit" - icon_state = "jensen" - rolled_sleeves = -1 - -/* - * Navy uniforms - */ -/obj/item/clothing/under/rank/security/navyblue - name = "security officer's uniform" - desc = "The latest in fashionable security outfits." - icon_state = "officerblueclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_security/navyblue - desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." - name = "head of security's uniform" - icon_state = "hosblueclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/warden/navyblue - desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." - name = "warden's uniform" - icon_state = "wardenblueclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/* - * Tan uniforms - */ -/obj/item/clothing/under/rank/security/tan - name = "security officer's uniform" - desc = "The latest in fashionable security outfits." - icon_state = "officertanclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/head_of_security/tan - desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." - name = "head of security's uniform" - icon_state = "hostanclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - -/obj/item/clothing/under/rank/warden/tan - desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." - name = "warden's uniform" - icon_state = "wardentanclothes" - item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") - rolled_sleeves = 0 - +/* + * Contains: + * Security + * Detective + * Head of Security + */ + +/* + * Security + */ +/obj/item/clothing/under/rank/warden + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpsuit" + icon_state = "warden" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/security + name = "security officer's jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "security" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/security/modern + name = "modernized security officer's jumpsuit" + desc = "A recent redesign of the classic Security jumpsuit, featuring sturdy materials, joint padding, one giant zipper, and tight-fitting synthleather." + icon_state = "securitymodern" + item_state = "securitymodern" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + rolled_sleeves = -1 + worn_state = "securitymodern" + icon = 'icons/inventory/uniform/item.dmi' + default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' + +/obj/item/clothing/under/rank/security/turtleneck + name = "security turtleneck" + desc = "It's a stylish turtleneck made of a robust nanoweave. Nobody said the Law couldn't be fashionable." + icon_state = "turtle_sec" + rolled_down = -1 + rolled_sleeves = -1 + +/obj/item/clothing/under/rank/dispatch + name = "dispatcher's uniform" + desc = "A dress shirt and khakis with a security patch sewn on." + icon_state = "dispatch" + item_state_slots = list(slot_r_hand_str = "detective", slot_l_hand_str = "detective") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + siemens_coefficient = 0.9 + +/obj/item/clothing/under/rank/security2 + name = "security officer's uniform" + desc = "It's made of a slightly sturdier material, to allow for robust protection." + icon_state = "redshirt2" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/security/corp + icon_state = "sec_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/warden/corp + icon_state = "warden_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +/obj/item/clothing/under/tactical + name = "tactical jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "swatunder" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + armor = list(melee = 10, bullet = 5, laser = 5,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = -1 + +/* + * Detective + */ +/obj/item/clothing/under/det + name = "detective's suit" + desc = "A rumpled white dress shirt paired with well-worn grey slacks." + icon_state = "detective" + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip) + +/* +/obj/item/clothing/under/det/verb/rollup() + set name = "Roll Suit Sleeves" + set category = "Object" + set src in usr + var/unrolled = item_state_slots[slot_w_uniform_str] == initial(worn_state) + item_state_slots[slot_w_uniform_str] = unrolled ? "[worn_state]_r" : initial(worn_state) + var/mob/living/carbon/human/H = loc + H.update_inv_w_uniform(1) + to_chat(H, "You roll the sleeves of your shirt [unrolled ? "up" : "down"]") +*/ + +/obj/item/clothing/under/det/grey + icon_state = "detective2" + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long) + +/obj/item/clothing/under/det/black + icon_state = "detective3" + item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") + desc = "An immaculate white dress shirt, paired with a pair of dark grey dress pants, a red tie, and a charcoal vest." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/vest) + +/obj/item/clothing/under/det/corporate + name = "detective's jumpsuit" + icon_state = "det_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + desc = "A more modern uniform for corporate investigators." + +/obj/item/clothing/under/det/waistcoat + icon_state = "detective" + desc = "A rumpled white dress shirt paired with well-worn grey slacks, complete with a blue striped tie, faux-gold tie clip, and waistcoat." + starting_accessories = list(/obj/item/clothing/accessory/tie/blue_clip, /obj/item/clothing/accessory/wcoat) + +/obj/item/clothing/under/det/grey/waistcoat + icon_state = "detective2" + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, complete with a red striped tie and waistcoat." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) + +/obj/item/clothing/under/det/black/waistcoat + icon_state = "detective3" + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, a red tie, and a charcoal vest." + starting_accessories = list(/obj/item/clothing/accessory/tie/red_long, /obj/item/clothing/accessory/wcoat) + +/obj/item/clothing/under/det/skirt + name = "detective's skirt" + icon_state = "detective_skirt" + desc = "A serious-looking white blouse paired with a formal black pencil skirt." + item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") + +/* + * Head of Security + */ +/obj/item/clothing/under/rank/head_of_security + desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpsuit" + icon_state = "hos" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_security/corp + icon_state = "hos_corporate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + rolled_sleeves = 0 + +//Jensen cosplay gear +/obj/item/clothing/under/rank/head_of_security/jensen + desc = "You never asked for anything that stylish." + name = "head of security's jumpsuit" + icon_state = "jensen" + rolled_sleeves = -1 + +/* + * Navy uniforms + */ +/obj/item/clothing/under/rank/security/navyblue + name = "security officer's uniform" + desc = "The latest in fashionable security outfits." + icon_state = "officerblueclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_security/navyblue + desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." + name = "head of security's uniform" + icon_state = "hosblueclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/warden/navyblue + desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." + name = "warden's uniform" + icon_state = "wardenblueclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/* + * Tan uniforms + */ +/obj/item/clothing/under/rank/security/tan + name = "security officer's uniform" + desc = "The latest in fashionable security outfits." + icon_state = "officertanclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/head_of_security/tan + desc = "The insignia on this uniform tells you that this uniform belongs to the Head of Security." + name = "head of security's uniform" + icon_state = "hostanclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + +/obj/item/clothing/under/rank/warden/tan + desc = "The insignia on this uniform tells you that this uniform belongs to the Warden." + name = "warden's uniform" + icon_state = "wardentanclothes" + item_state_slots = list(slot_r_hand_str = "ba_suit", slot_l_hand_str = "ba_suit") + rolled_sleeves = 0 + diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 33bc3b459a3..f7fc0474e6f 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -1,1668 +1,1707 @@ -/obj/item/clothing/under/gimmick //used to fix an error - name = "gimmick jumpsuit" - desc = "Something about this jumpsuit feels... off..." - icon_state = "grey" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -/obj/item/clothing/under/pj/red - name = "red pj's" - desc = "Sleepwear." - icon_state = "red_pyjamas" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/pj/blue - name = "blue pj's" - desc = "Sleepwear." - icon_state = "blue_pyjamas" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/captain_fly - name = "rogue's uniform" - desc = "For the man who doesn't care because he's still free." - icon_state = "captain_fly" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/under/scratch - name = "white suit" - desc = "A white suit, suitable for an excellent host" - icon_state = "scratch" - -/obj/item/clothing/under/scratch/skirt - name = "white skirt suit" - icon_state = "scratch_skirt" - item_state_slots = list(slot_r_hand_str = "scratch", slot_l_hand_str = "scratch") - -/obj/item/clothing/under/sl_suit - desc = "It's a very amish looking suit." - name = "amish suit" - icon_state = "sl_suit" - rolled_sleeves = 0 - -/obj/item/clothing/under/waiter - name = "waiter's outfit" - desc = "It's a very smart uniform with a special pocket for tip." - icon_state = "waiter" - rolled_sleeves = 0 - -/obj/item/clothing/under/customs - name = "customs uniform" - desc = "A standard SolGov customs uniform. Complete with epaulettes." - icon_state = "cu_suit" - -/obj/item/clothing/under/customs/khaki - icon_state = "cu_suit_kh" - -/obj/item/clothing/under/rank/mailman - name = "mailman's jumpsuit" - desc = "'Special delivery!'" - icon_state = "mailman" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - rolled_sleeves = 0 - -/obj/item/clothing/under/sexyclown - name = "sexy-clown suit" - desc = "It makes you look HONKable!" - icon_state = "sexyclown" - item_state_slots = list(slot_r_hand_str = "clown", slot_l_hand_str = "clown") - body_parts_covered = UPPER_TORSO|LOWER_TORSO - rolled_sleeves = -1 //Please never - -/obj/item/clothing/under/clown //i'm only putting it here to make subtypes of it, since the original suit is under/rank/clown - name = "clown suit" - desc = "Honk!" - icon_state = "clown" - rolled_sleeves = -1 - -/obj/item/clothing/under/clown/green - name = "green clown suit" - icon_state = "greenclown" - -/obj/item/clothing/under/clown/purple - name = "purple clown suit" - icon_state = "purpleclown" - -/obj/item/clothing/under/clown/yellow - name = "yellow clown suit" - icon_state = "yellowclown" - -/obj/item/clothing/under/clown/orange - name = "orange clown suit" - icon_state = "orangeclown" - -/obj/item/clothing/under/clown/blue - name = "blue clown suit" - icon_state = "blueclown" - -/obj/item/clothing/under/clown/rainbow - name = "rainbow clown suit" - icon_state = "rainbowclown" - -/obj/item/clothing/under/rank/vice - name = "vice officer's jumpsuit" - desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." - icon_state = "vice" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -//This set of uniforms looks fairly fancy and is generally used for high-ranking NT personnel from what I've seen, so lets give them appropriate ranks. -/obj/item/clothing/under/rank/centcom - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain.\"" - name = "\improper Officer's Dress Uniform" - icon_state = "officer" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - displays_id = 0 - -/obj/item/clothing/under/rank/centcom_officer - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Admiral.\"" - name = "\improper Officer's Dress Uniform" - icon_state = "officer" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - displays_id = 0 - -/obj/item/clothing/under/rank/centcom_captain - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Admiral-Executive.\"" - name = "\improper Officer's Dress Uniform" - icon_state = "centcom" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - displays_id = 0 - -/obj/item/clothing/under/ert - name = "ERT tactical uniform" - desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. It looks very tactical." - icon_state = "ert_uniform" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/under/space - name = "\improper NASA jumpsuit" - desc = "It has a NASA logo on it and is made of space-proofed materials." - icon_state = "black" - w_class = ITEMSIZE_LARGE//bulky item - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - rolled_sleeves = 0 - -/obj/item/clothing/under/acj - name = "administrative cybernetic jumpsuit" - icon_state = "syndicate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - armor = list(melee = 100, bullet = 100, laser = 100,energy = 100, bomb = 100, bio = 100, rad = 100) - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE - siemens_coefficient = 0 - -/obj/item/clothing/under/owl - name = "owl uniform" - desc = "A jumpsuit with owl wings. Photorealistic owl feathers! Twooooo!" - icon_state = "owl" - -/obj/item/clothing/under/johnny - name = "johnny~~ jumpsuit" - desc = "Johnny~~" - icon_state = "johnny" - -/obj/item/clothing/under/color/rainbow - name = "rainbow jumpsuit" - desc = "A multi-colored jumpsuit." - icon_state = "rainbow" - -/obj/item/clothing/under/psysuit - name = "dark undersuit" - desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." - icon_state = "psysuit" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -/obj/item/clothing/under/gentlesuit - name = "gentlemans suit" - desc = "A silk black shirt with matching gray slacks. Feels proper." - icon_state = "gentlesuit" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - rolled_sleeves = 0 - starting_accessories = list(/obj/item/clothing/accessory/tie/white, /obj/item/clothing/accessory/wcoat/gentleman) - -/obj/item/clothing/under/gentlesuit/skirt - name = "lady's suit" - desc = "A silk black blouse with a matching gray skirt. Feels proper." - icon_state = "gentlesuit_skirt" - - -/obj/item/clothing/under/suit_jacket - name = "black suit" - desc = "A black suit and red tie. Very formal." - icon_state = "black_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/suit_jacket/really_black - name = "executive suit" - desc = "A formal black suit and red tie, intended for the station's finest." - icon_state = "really_black_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/suit_jacket/really_black/skirt - name = "executive skirt suit" - desc = "A formal black suit and red necktie, intended for the station's finest." - icon_state = "really_black_suit_skirt" - -/obj/item/clothing/under/suit_jacket/female - name = "female executive suit" - desc = "A formal trouser suit for women, intended for the station's finest." - icon_state = "black_suit_fem" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/suit_jacket/female/skirt - name = "executive skirt" - desc = "A formal suit skirt for women, intended for the station's finest." - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - icon_state = "black_suit_fem" - item_state = "black_formal_skirt" - -/obj/item/clothing/under/schoolgirl - name = "schoolgirl uniform" - desc = "It's just like one of my Japanese animes!" - icon_state = "schoolgirl" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/overalls - name = "laborer's overalls" - desc = "A set of durable overalls for getting the job done." - icon_state = "overalls" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - -/obj/item/clothing/under/overalls/sleek - name = "sleek overalls" - desc = "A set of modern pleather reinforced overalls." - icon_state = "overalls_sleek" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/pirate - name = "pirate outfit" - desc = "Yarr." - icon_state = "pirate" - item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/soviet - name = "soviet uniform" - desc = "For the Motherland!" - icon_state = "soviet" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -/obj/item/clothing/under/redcoat - name = "redcoat uniform" - desc = "Looks old." - icon_state = "redcoat" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/under/kilt - name = "kilt" - desc = "Includes shoes and plaid" - icon_state = "kilt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET - rolled_sleeves = 0 - -/obj/item/clothing/under/sexymime - name = "sexy mime outfit" - desc = "The only time when you DON'T enjoy looking at someone's rack." - icon_state = "sexymime" - item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") - body_parts_covered = UPPER_TORSO|LOWER_TORSO - rolled_sleeves = -1 //Please never - -/obj/item/clothing/under/sexymime/dress //another style of dress closer to the jumpsuit than the sexy outfit - name = "mime's jumpskirt" - desc = "It's not very colourful." - icon_state = "mimedress" - -/obj/item/clothing/under/gladiator - name = "gladiator uniform" - desc = "Are you not entertained? Is that not why you are here?" - icon_state = "gladiator" - item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") - body_parts_covered = LOWER_TORSO - -/obj/item/clothing/under/ascetic - name = "plain ascetic garb" - desc = "Popular with freshly grown vatborn and new age cultists alike." - icon_state = "ascetic" - item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") - -/obj/item/clothing/under/robe - name = "black robe" - desc = "A black robe. It gives off uncomfortable cult vibes." - icon_state = "robe" - -/obj/item/clothing/under/whiterobe - name = "white robe" - desc = "A white robe. It gives off uncomfortable cult vibes." - icon_state = "whiterobe" - -/obj/item/clothing/under/goldrobe - name = "black gold-lined robe" - desc = "A gold-lined black robe. It gives off uncomfortable cult vibes, but fancy." - icon_state = "goldrobe" - -/obj/item/clothing/under/whitegoldrobe - name = "white gold-lined robe" - desc = "A gold-lined white robe. It gives off uncomfortable cult vibes, but fancy." - icon_state = "whitegoldrobe" - -/* - * Dress Stuff - */ - -/obj/item/clothing/under/dress - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/blacktango - name = "black tango dress" - desc = "Filled with Latin fire." - icon_state = "black_tango" - -/obj/item/clothing/under/dress/blacktango/alt - name = "black tango dress" - desc = "Filled with Latin fire." - icon_state = "black_tango_alt" - item_state_slots = list(slot_r_hand_str = "black_tango", slot_l_hand_str = "black_tango") - -/obj/item/clothing/under/dress/stripeddress - name = "striped dress" - desc = "Fashion in space." - icon_state = "striped_dress" - -/obj/item/clothing/under/dress/sailordress - name = "sailor dress" - desc = "Formal wear for a leading lady." - icon_state = "sailor_dress" - -/obj/item/clothing/under/dress/redeveninggown - name = "red evening gown" - desc = "Fancy dress for space bar singers." - icon_state = "red_evening_gown" - -/obj/item/clothing/under/dress/maid - name = "maid costume" - desc = "Maid in China." - icon_state = "maid" - -/obj/item/clothing/under/dress/maid/janitor - name = "maid uniform" - desc = "A simple maid uniform for housekeeping." - icon_state = "janimaid" - -/obj/item/clothing/under/dress/maid/sexy - name = "sexy maid costume" - desc = "You must be a bit risque teasing all of them in a maid uniform!" - icon_state = "sexymaid" - -/obj/item/clothing/under/dress/maid/under_maid - name = "modern maid costume" - desc = "You're going to use this for housekeeping, right?" - icon_state = "under_maid" - -/obj/item/clothing/under/dress/dress_fire - name = "flame dress" - desc = "A small black dress with blue flames print on it." - icon_state = "dress_fire" - -/obj/item/clothing/under/dress/dress_green - name = "green dress" - desc = "A simple, tight fitting green dress." - icon_state = "dress_green" - -/obj/item/clothing/under/dress/dress_orange - name = "orange dress" - desc = "A fancy orange gown for those who like to show leg." - icon_state = "dress_orange" - -/obj/item/clothing/under/dress/dress_pink - name = "pink dress" - desc = "A simple, tight fitting pink dress." - icon_state = "dress_pink" - -/obj/item/clothing/under/dress/dress_yellow - name = "yellow dress" - desc = "A flirty, little yellow dress." - icon_state = "dress_yellow" - -/obj/item/clothing/under/dress/dress_saloon - name = "saloon girl dress" - desc = "A old western inspired gown for the girl who likes to drink." - icon_state = "dress_saloon" - item_state_slots = list(slot_r_hand_str = "dress_white", slot_l_hand_str = "dress_white") - -/obj/item/clothing/under/dress/dress_cap - name = "site manager's dress uniform" - desc = "Feminine fashion for the style conscious Site Manager." - icon_state = "dress_cap" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/dress/dress_hop - name = "head of personnel dress uniform" - desc = "Feminine fashion for the style conscious HoP." - icon_state = "dress_hop" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/dress/dress_hr - name = "human resources director uniform" - desc = "Superior class for the nosy H.R. Director." - icon_state = "huresource" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/dress/black_corset - name = "black corset" - desc = "A black corset and skirt for those fancy nights out." - icon_state = "black_corset" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/flower_dress - name = "flower dress" - desc = "A beautiful dress with a skirt of flowers." - icon_state = "flower_dress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/red_swept_dress - name = "red swept dress" - desc = "A red dress that sweeps to the side." - icon_state = "red_swept_dress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/flamenco - name = "flamenco dress" - desc = "A Mexican flamenco dress." - icon_state = "flamenco" - -/obj/item/clothing/under/dress/westernbustle - name = "western bustle" - desc = "A western bustle dress from Earth's late 1800's." - icon_state = "westernbustle" - -/obj/item/clothing/under/dress/sari - name = "red sari" - desc = "A colorful traditional dress originating from India." - icon_state = "sari_red" - item_state_slots = list(slot_r_hand_str = "darkreddress", slot_l_hand_str = "darkreddress") - -/obj/item/clothing/under/dress/sari/green - name = "green sari" - icon_state = "sari_green" - item_state_slots = list(slot_r_hand_str = "dress_green", slot_l_hand_str = "dress_green") - -/obj/item/clothing/under/dress/lilacdress - name = "lilac dress" - desc = "A simple black dress adorned in fake purple lilacs." - icon_state = "lilacdress" - -/obj/item/clothing/under/dress/white - name = "white wedding dress" - desc = "A fancy white dress with a blue underdress." - icon_state = "whitedress1" - flags_inv = HIDESHOES - -/obj/item/clothing/under/dress/white2 - name = "long dress" - desc = "A long dress." - icon_state = "whitedress2" - addblends = "whitedress2_a" - flags_inv = HIDESHOES - -/obj/item/clothing/under/dress/white3 - name = "short dress" - desc = "A short, plain dress." - icon_state = "whitedress3" - addblends = "whitedress3_a" - -/obj/item/clothing/under/dress/white4 - name = "long flared dress" - desc = "A long white dress that flares out at the bottom." - icon_state = "whitedress4" - addblends = "whitedress4_a" - flags_inv = HIDESHOES - -/obj/item/clothing/under/dress/darkred - name = "fancy dark red dress" - desc = "A short, red dress with a black belt. Fancy." - icon_state = "darkreddress" - -/obj/item/clothing/under/dress/polka - name = "polka dot dress" - desc = "A sleeveless, cream colored dress with red polka dots." - icon_state = "polka" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/twistfront - name = "twistfront crop dress" - desc = "A black skirt and red twistfront croptop. Fancy!" - icon_state = "twistfront" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/cropdress - name = "crop dress" - desc = "A red skirt and longsleeved button-up crop top." - icon_state = "cropdress" - -/obj/item/clothing/under/dress/vneck - name = "v-neck dress" - desc = "A black v-neck dress with an exaggerated neckline covered in a sheer mesh." - icon_state = "vneckdress" - -/obj/item/clothing/under/dress/bluedress - name = "blue dress" - desc = "A plain blue dress with a white belt." - icon_state = "bluedress" - -/obj/item/clothing/under/dress/wench - name = "wench's dress" - desc = "A white dress styled like a Ye Old Barmaid. Saucy!" - icon_state = "wench" - -/obj/item/clothing/under/dress/littleblackdress - name = "little black dress" - desc = "A little strapless black dress with a red ribbon and flower accessory." - icon_state = "littleblackdress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/pinktutu - name = "pink tutu" - desc = "A black leotard with a pink mesh tutu. Perfect for ballet practice." - icon_state = "pinktutu" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/festivedress - name = "festive dress" - desc = "A red and white dress themed after some winter holidays. Tastefully festive!" - icon_state = "festivedress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/revealingdress - name = "revealing dress" - desc = "A very revealing black and blue dress. Is this work appropriate?" - icon_state = "revealingdress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/gothic - name = "gothic dress" - desc = "A black dress with a sheer mesh over it, tastefully old school goth." - icon_state = "gothic" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/formalred - name = "formal red dress" - desc = "A very formal red dress, for those fancy galas." - icon_state = "formalred" - flags_inv = HIDESHOES - -/obj/item/clothing/under/dress/pentagram - name = "pentagram dress" - desc = "A black dress with straps over the chest in the shape of a pentagram." - icon_state = "pentagram" - -/obj/item/clothing/under/dress/yellowswoop - name = "yellow swooped dress" - desc = "A yellow dress that swoops to the side." - icon_state = "yellowswoop" - -/obj/item/clothing/under/dress/countess - name = "countess dress" - desc = "A red and black dress fit for a countess." - icon_state = "countess" - -/obj/item/clothing/under/dress/verglasdress - name = "verglas dress" - desc = "The modern twist on a forgotten pattern, the Verglas style utilizes comfortable velvet and silver white satin to create an otherworldly effect evocative of winter, or the void." - icon_state = "verglas_dress" - -/obj/item/clothing/under/dress/goddess - name = "goddess dress" - desc = "A blue and orange dress fit for a goddess." - icon_state = "goddess" - -/obj/item/clothing/under/dress/alpine - name = "alpine dress" - desc = "A green and white dress that makes you want to yodel." - icon_state = "corsetdress" - -/obj/item/clothing/under/dress/goldwrap - name = "golden dress" - desc = "A dress so ostentatious that you feel poorer just looking at it." - icon_state = "golddress" - -/obj/item/clothing/under/dress/golddress - name = "golden wrap" - desc = "An outfit so ostentatious that you feel poorer just looking at it." - icon_state = "goldwrap" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/hightrousers - name = "high-waisted trousers" - desc = "A waistline this high is just made for ripping bodices, swashing buckles, or - just occasionally - sucking blood." - icon_state = "gayvampire" - -/obj/item/clothing/under/dress/singer - name = "blue singer dress" - desc = "Just looking at this makes you want to sing." - icon_state = "bsing" - -/obj/item/clothing/under/dress/singer/yellow - name = "yellow singer dress" - desc = "Just looking at this makes you want to sing." - icon_state = "ysing" - -/* - * Wedding Stuff - */ - -/obj/item/clothing/under/wedding - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/wedding/bride_orange - name = "orange wedding dress" - desc = "A big and puffy orange dress." - icon_state = "bride_orange" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_purple - name = "purple wedding dress" - desc = "A big and puffy purple dress." - icon_state = "bride_purple" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_blue - name = "blue wedding dress" - desc = "A big and puffy blue dress." - icon_state = "bride_blue" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_red - name = "red wedding dress" - desc = "A big and puffy red dress." - icon_state = "bride_red" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_white - name = "silky wedding dress" - desc = "A white wedding gown made from the finest silk." - icon_state = "bride_white" - flags_inv = HIDESHOES - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/wedding/floofdress - name = "floofy dress" - desc = "A lovely floofed out dress for formal occasions. Comes in many colors!" - icon_state = "floofdress" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/whitegown - name = "white gown" - desc = "A elegant white gown with accents of sheer mesh." - icon_state = "whitegown" - -/* - * Uniforms and such - */ - -/obj/item/clothing/under/sundress - name = "sundress" - desc = "Makes you want to frolic in a field of daisies." - icon_state = "sundress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/sundress_white - name = "flowery white sundress" - desc = "A white sundress decorated with purple lilies." - icon_state = "sundress_white" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/sundress_pink - name = "pink stripied sundress" - desc = "A cute pink sundress." - icon_state = "pinksun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/sundress_white - name = "white sundress" - desc = "A white sundress, it's short." - icon_state = "whitesun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/sundress_pinkbow - name = "bowed pink sundress" - desc = "A cute pink sundress with a bow." - icon_state = "bowsun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/sundress_blue - name = "long blue sundress" - desc = "A long blue sun dress with white frills towards the bottom." - icon_state = "bluesun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/dress/sundress_pinkshort - name = "short pink sundress" - desc = "A very short pink sundress, it's more like a chemise." - icon_state = "shortpink" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/twopiece - name = "two-piece dress" - desc = "A fancy two-piece dress, the pieces are sewn together." - icon_state = "twopiece" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/dress/gothic2 - name = "lacey gothic dress" - desc = "An elegant gothic dress with lace decorations." - icon_state = "gothic2" - -/obj/item/clothing/under/captainformal - name = "site manager's formal uniform" - desc = "A Site Manager's formal-wear, for special occasions." - icon_state = "captain_formal" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - -/obj/item/clothing/under/hosformalmale - name = "head of security's formal uniform" - desc = "A male head of security's formal-wear, for special occasions." - icon_state = "hos_formal_male" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - -/obj/item/clothing/under/hosformalfem - name = "head of security's formal uniform" - desc = "A female head of security's formal-wear, for special occasions." - icon_state = "hos_formal_fem" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - -/obj/item/clothing/under/assistantformal - name = "assistant's formal uniform" - desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." - icon_state = "assistant_formal" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - -/obj/item/clothing/under/suit_jacket/charcoal - name = "charcoal suit" - desc = "A charcoal suit and red tie. Very professional." - icon_state = "charcoal_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - starting_accessories = list(/obj/item/clothing/accessory/tie/navy, /obj/item/clothing/accessory/jacket/charcoal) - -/obj/item/clothing/under/suit_jacket/charcoal/skirt - name = "charcoal skirt" - icon_state = "charcoal_suit_skirt" - -/obj/item/clothing/under/suit_jacket/navy - name = "navy suit" - desc = "A navy suit and red tie, intended for the station's finest." - icon_state = "navy_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - starting_accessories = list(/obj/item/clothing/accessory/tie/red, /obj/item/clothing/accessory/jacket/navy) - -/obj/item/clothing/under/suit_jacket/navy/skirt - name = "navy skirt" - icon_state = "navy_suit_skirt" - -/obj/item/clothing/under/suit_jacket/burgundy - name = "burgundy suit" - desc = "A burgundy suit and black tie. Somewhat formal." - icon_state = "burgundy_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - starting_accessories = list(/obj/item/clothing/accessory/tie/black, /obj/item/clothing/accessory/jacket/burgundy) - -/obj/item/clothing/under/suit_jacket/burgundy/skirt - name = "burgundy skirt" - icon_state = "burgundy_suit_skirt" - -/obj/item/clothing/under/suit_jacket/red - name = "red suit" - desc = "A red suit and blue tie. Somewhat formal." - icon_state = "red_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") - starting_accessories = list(/obj/item/clothing/accessory/tie/navy, /obj/item/clothing/accessory/jacket/red) - -/obj/item/clothing/under/suit_jacket/red/skirt - name = "red skirt" - icon_state = "red_suit_skirt" - -/obj/item/clothing/under/suit_jacket/teal - name = "teal suit" - desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." - icon_state = "teal_suit" - item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") - starting_accessories = list(/obj/item/clothing/accessory/tie/yellow, /obj/item/clothing/accessory/jacket/teal) - -/obj/item/clothing/under/suit_jacket/teal/skirt - name = "teal skirt suit" - icon_state = "teal_suit_skirt" - -/obj/item/clothing/under/suit_jacket/green - name = "green suit" - desc = "A green suit and yellow necktie. Exemplifies authority." - icon_state = "green_suit" - item_state_slots = list(slot_r_hand_str = "centcom", slot_l_hand_str = "centcom") - starting_accessories = list(/obj/item/clothing/accessory/tie/yellow, /obj/item/clothing/accessory/jacket/green) - -/obj/item/clothing/under/gov - name = "green formal uniform" - desc = "A neat proper uniform of someone on offical business. The collar is immaculately starched." - icon_state = "greensuit" - item_state_slots = list(slot_r_hand_str = "centcom", slot_l_hand_str = "centcom") - starting_accessories = list(/obj/item/clothing/accessory/tie/darkgreen) - rolled_sleeves = 0 - -/obj/item/clothing/under/gov/skirt - name = "green formal skirt" - desc = "A neat proper uniform of someone on offical business. The top button is sewn shut." - icon_state = "greensuit_skirt" - -/obj/item/clothing/under/suit_jacket/green/skirt - name = "green skirt suit" - icon_state = "green_suit_skirt" - -/obj/item/clothing/under/suit_jacket/checkered - name = "checkered suit" - desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" - icon_state = "checkered_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - starting_accessories = list(/obj/item/clothing/accessory/tie/black, /obj/item/clothing/accessory/jacket/checkered) - -/obj/item/clothing/under/suit_jacket/checkered/skirt - name = "checkered skirt" - icon_state = "checkered_suit_skirt" - -/obj/item/clothing/under/suit_jacket/gambler - name = "gambling suit" - desc = "The suit of a gambler. Lady luck be with you." - icon_state = "gambler_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - starting_accessories = list(/obj/item/clothing/accessory/tie/black, /obj/item/clothing/accessory/jacket/gambler) - -/obj/item/clothing/under/suit_jacket/extravagant - name = "extravagant suit" - desc = "An extravagant suit. Perfect for being over dramatic." - icon_state = "extravagant_suit" - item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") - starting_accessories = list(/obj/item/clothing/accessory/jacket/extravagant) - -/obj/item/clothing/under/suit_jacket/tan - name = "tan suit" - desc = "A tan suit. Smart, but casual." - icon_state = "tan_suit" - item_state_slots = list(slot_r_hand_str = "tan_suit", slot_l_hand_str = "tan_suit") - starting_accessories = list(/obj/item/clothing/accessory/tie/yellow, /obj/item/clothing/accessory/jacket) - -/obj/item/clothing/under/suit_jacket/tan/skirt - name = "tan skirt" - icon_state = "tan_suit_skirt" - -/obj/item/clothing/under/cheongsam - name = "white cheongsam" - desc = "It is a white cheongsam dress." - icon_state = "cheongsam-white" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/cheongsam/red - name = "red cheongsam" - desc = "It is a red cheongsam dress." - icon_state = "cheongsam-red" - -/obj/item/clothing/under/cheongsam/blue - name = "blue cheongsam" - desc = "It is a blue cheongsam dress." - icon_state = "cheongsam-blue" - -/obj/item/clothing/under/cheongsam/black - name = "black cheongsam" - desc = "It is a black cheongsam dress." - icon_state = "cheongsam-black" - -/obj/item/clothing/under/cheongsam/darkred - name = "dark red cheongsam" - desc = "It is a dark red cheongsam dress." - icon_state = "cheongsam-darkred" - -/obj/item/clothing/under/cheongsam/green - name = "green cheongsam" - desc = "It is a green cheongsam dress." - icon_state = "cheongsam-green" - -/obj/item/clothing/under/cheongsam/purple - name = "purple cheongsam" - desc = "It is a purple cheongsam dress." - icon_state = "cheongsam-purple" - -/obj/item/clothing/under/cheongsam/darkblue - name = "dark blue cheongsam" - desc = "It is a dark blue cheongsam dress." - icon_state = "cheongsam-darkblue" - -/obj/item/clothing/under/blazer - name = "blue blazer" - desc = "A bold but yet conservative outfit, red corduroys, navy blazer and a tie." - icon_state = "blue_blazer" - item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") - -/obj/item/clothing/under/blazer/skirt - name = "ladies blue blazer" - desc = "A bold but yet conservative outfit, a red pencil skirt and a navy blazer." - icon_state = "blue_blazer_skirt" - -/obj/item/clothing/under/croptop - name = "crop top" - desc = "A shirt that has had the top cropped. This one is NT sponsored." - icon_state = "croptop" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -/obj/item/clothing/under/croptop/red - name = "red crop top" - desc = "A red shirt that has had the top cropped." - icon_state = "croptop_red" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/under/croptop/grey - name = "grey crop top" - desc = "A grey shirt that has had the top cropped." - icon_state = "croptop_grey" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -/obj/item/clothing/under/cuttop - name = "grey cut top" - desc = "A grey shirt that has had the top cut low." - icon_state = "cuttop" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -/obj/item/clothing/under/cuttop/red - name = "red cut top" - desc = "A red shirt that has had the top cut low." - icon_state = "cuttop_red" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/under/harness - name = "gear harness" - desc = "How... minimalist." - icon_state = "gear_harness" - body_parts_covered = 0 - -/obj/item/clothing/under/haltertop - name = "halter top" - desc = "Jean shorts and a black halter top. Perfect for casual Fridays!" - icon_state = "haltertop" - -/obj/item/clothing/under/rippedpunk - name = "ripped punk jeans" - desc = "Black ripped jeans and a fishnet top. How punk." - icon_state = "rippedpunk" - -/obj/item/clothing/under/greenasym - name = "green asymmetrical jumpsuit" - desc = "A green futuristic uniform with asymmetrical pants. Trendy!" - icon_state = "greenasym" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/cyberpunkharness - name = "cyberpunk strapped harness" - desc = "A cyberpunk styled harness and pants. Perfect for your dystopian future." - icon_state = "cyberhell" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - -/obj/item/clothing/under/blackngold - name = "black and gold gown" - desc = "A black and gold gown. You get the impression this is typically worn for religious purposes." - icon_state = "blackngold" - -/obj/item/clothing/under/sheerblue - name = "sheer blue dress" - desc = "An entirely sheer blue dress. Best worn with something underneath!" - icon_state = "sheerblue" - -/obj/item/clothing/under/disheveled - name = "disheveled suit" - desc = "What might pass as well maintained formal attire. If you're blind." - icon_state = "disheveled" - -/obj/item/clothing/under/flower_skirt - name = "flower skirt" - desc = "A flowery skirt that comes in a variety of colors." - icon_state = "flowerskirt" - body_parts_covered = LOWER_TORSO - -/obj/item/clothing/under/fashionminiskirt - name = "fashionable miniskirt" - desc = "An impractically short miniskirt allegedly making waves through the local fashion scene." - icon_state = "miniskirt_fashion" - body_parts_covered = LOWER_TORSO - -/obj/item/clothing/under/retrosweater - name = "retro sweater" - desc = "A rugged cableknit sweater and leather pants, fit for a dashing space adventurer." - icon_state = "retro_sweater" - -/obj/item/clothing/under/wednesday - name = "cropped sweater skirt" - desc = "A cropped green sweater and matching miniskirt." - icon_state = "wednesday" - -/* - * Swimsuit - */ - -/obj/item/clothing/under/swimsuit/ - siemens_coefficient = 1 - body_parts_covered = 0 - -/obj/item/clothing/under/swimsuit/black - name = "black swimsuit" - desc = "An oldfashioned black swimsuit." - icon_state = "swim_black" - -/obj/item/clothing/under/swimsuit/blue - name = "blue swimsuit" - desc = "An oldfashioned blue swimsuit." - icon_state = "swim_blue" - -/obj/item/clothing/under/swimsuit/purple - name = "purple swimsuit" - desc = "An oldfashioned purple swimsuit." - icon_state = "swim_purp" - -/obj/item/clothing/under/swimsuit/green - name = "green swimsuit" - desc = "An oldfashioned green swimsuit." - icon_state = "swim_green" - -/obj/item/clothing/under/swimsuit/red - name = "red swimsuit" - desc = "An oldfashioned red swimsuit." - icon_state = "swim_red" - -/obj/item/clothing/under/swimsuit/striped - name = "striped swimsuit" - desc = "A more revealing striped swimsuit." - icon_state = "swim_striped" - -/obj/item/clothing/under/swimsuit/white - name = "white swimsuit" - desc = "A classic one piece." - icon_state = "swim_white" - -/obj/item/clothing/under/swimsuit/earth - name = "earthen swimsuit" - desc = "A design more popular on Earth these days." - icon_state = "swim_earth" - -/obj/item/clothing/under/swimsuit/stripper/stripper_pink - name = "pink swimsuit" - desc = "A rather skimpy pink swimsuit." - icon_state = "stripper_p" - -/obj/item/clothing/under/swimsuit/stripper/stripper_green - name = "green swimsuit" - desc = "A rather skimpy green swimsuit." - icon_state = "stripper_g" - -/obj/item/clothing/under/swimsuit/stripper/mankini - name = "mankini" - desc = "No honest man would wear this abomination" - icon_state = "mankini" - -/obj/item/clothing/under/swimsuit/cowbikini - name = "cow print bikini" - desc = "A rather skimpy cow patterned swimsuit." - icon_state = "swim_cow" - -/obj/item/clothing/under/swimsuit/highclass - name = "high class swimsuit" - desc = "An elegant swimsuit with a white bikini top and black bikini bottom. Thin black silk drapes down the back and goes to the upper thighs, and authentic gold rings hold the top together at the bust and back." - icon_state = "swim_highclass" - -/obj/item/clothing/under/swimsuit/risque - name = "risque swimsuit" - desc = "This fits a bit too snug in all the right places. Comes with a collar, for inscrutable reasons." - icon_state = "swim_risque" - -/obj/item/clothing/under/swimsuit/streamlined - name = "streamlined swimsuit" - desc = "An all white one-piece that maintains modesty without sacrificing class." - icon_state = "swim_stream" - -/obj/item/clothing/under/wetsuit - name = "wetsuit" - desc = "For when you need to scuba dive your way into an enemy base." - icon_state = "wetsuit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - -/obj/item/clothing/under/wetsuit_skimpy - name = "tactical wetsuit" - desc = "For when you need to scuba dive your way into an enemy base but still want to show off a little skin." - icon_state = "wetsuit_skimpy" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/wetsuit_rec - name = "recreational wetsuit" - desc = "For when you need to kayak your way into an enemy base." - icon_state = "wetsuit_rec" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - -/* - * Pyjamas - */ - -/obj/item/clothing/under/bluepyjamas - name = "blue pyjamas" - desc = "Slightly old-fashioned sleepwear." - icon_state = "blue_pyjamas" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - -/obj/item/clothing/under/redpyjamas - name = "red pyjamas" - desc = "Slightly old-fashioned sleepwear." - icon_state = "red_pyjamas" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - -/obj/item/clothing/under/rank/psych/turtleneck/sweater - desc = "A warm looking sweater and a pair of dark blue slacks." - name = "sweater" - icon_state = "turtleneck" - worn_state = "turtleneck" - -/obj/item/clothing/under/medigown - name = "medical gown" - desc = "A flimsy examination gown, the back ties never close." - icon_state = "medicalgown" - worn_state = "medicalgown" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/bathrobe - name = "bathrobe" - desc = "A fluffy robe to keep you from showing off to the world." - icon_state = "bathrobe" - worn_state = "bathrobe" - -/obj/item/clothing/under/explorer - desc = "A green uniform for operating in hazardous environments." - name = "explorer's jumpsuit" - icon_state = "explorer" - -/obj/item/clothing/under/explorer/armored - desc = "A green uniform for operating in hazardous environments. This one looks like it's been modified." - armor = list(melee = 10, bullet = 10, laser = 10,energy = 10, bomb = 10, bio = 10, rad = 30) - armorsoak = list(melee = 5, bullet = 5, laser = 5,energy = 5, bomb = 5, bio = 5, rad = 0) - -/obj/item/clothing/under/cohesion - name = "black cohesion suit" - desc = "A plain black cohesion suit intended to assist Prometheans in maintaining their form and prevent direct skin exposure." - icon_state = "cohesionsuit" - rolled_sleeves = -1 // defeats the purpose!!! - -/obj/item/clothing/under/cohesion/striped - name = "red striped cohesion suit" - desc = "A black cohesion suit with red stripes intended to assist Prometheans in maintaining their form and prevent direct skin exposure." - icon_state = "cohesionsuit_striped" - -/obj/item/clothing/under/cohesion/decal - name = "purple decaled cohesion suit" - desc = "A white cohesion suit with purple decals intended to assist Prometheans in maintaining their form and prevent direct skin exposure." - icon_state = "cohesionsuit_decal" - -/obj/item/clothing/under/cohesion/pattern - name = "blue patterned cohesion suit" - desc = "A white cohesion suit with blue patterns intended to assist Prometheans in maintaining their form and prevent direct skin exposure." - icon_state = "cohesionsuit_pattern" - -/obj/item/clothing/under/cohesion/hazard - name = "hazard cohesion suit" - desc = "An orange cohesion suit with yellow hazard stripes intended to assist Prometheans in maintaining their form and prevent direct skin exposure." - icon_state = "cohesionsuit_hazard" - -//Ranger uniforms -//On-mob sprites go in icons\mob\uniform.dmi with the format "white_ranger_uniform_s" - with 'white' replaced with green, cyan, etc... of course! Note the _s - this is not optional. -//Item sprites go in icons\obj\clothing\ranger.dmi with the format "white_ranger_uniform" -/obj/item/clothing/under/color/ranger - var/unicolor = "white" - name = "ranger uniform" - desc = "Made from a space-proof fibre and tight fitting, this uniform usually gives the agile Rangers all kinds of protection while not inhibiting their movement. \ - This costume is instead made from genuine cotton fibre and is based on the season three uniform." - icon = 'icons/obj/clothing/ranger.dmi' - icon_state = "white_ranger_uniform" - rolled_down = 0 - rolled_sleeves = 0 - -/obj/item/clothing/under/color/ranger/Initialize() - . = ..() - if(icon_state == "ranger_uniform") //allows for custom items - name = "[unicolor] ranger uniform" - icon_state = "[unicolor]_ranger_uniform" - -/obj/item/clothing/under/color/ranger/black - unicolor = "black" - -/obj/item/clothing/under/color/ranger/pink - unicolor = "pink" - -/obj/item/clothing/under/color/ranger/green - unicolor = "green" - -/obj/item/clothing/under/color/ranger/cyan - unicolor = "cyan" - -/obj/item/clothing/under/color/ranger/orange - unicolor = "orange" - -/obj/item/clothing/under/color/ranger/yellow - unicolor = "yellow" - -/obj/item/clothing/under/boater - name = "boater outfit" - desc = "A classic outfit for those with a nautical inclination." - icon_state = "boater" - worn_state = "boater" - -/obj/item/clothing/under/tourist_1 - name = "summer outfit" - desc = "The perfect outfit to wear out of town." - icon_state = "tourist_1" - worn_state = "tourist_1" - -/obj/item/clothing/under/tourist_2 - name = "summer outfit" - desc = "The perfect outfit to wear out of town." - icon_state = "tourist_2" - worn_state = "tourist_2" - -/obj/item/clothing/under/relaxwear_1 - name = "casual outfit" - desc = "Something casual to wear out on the town. Pairs well with the holiday season." - icon_state = "relaxwear_1" - worn_state = "relaxwear_1" - starting_accessories = list(/obj/item/clothing/accessory/wcoat/swvest/red) - -/obj/item/clothing/under/relaxwear_2 - name = "relaxing outfit" - desc = "A comfy looking set of clothes to relax in, even if the style is a bit dated." - icon_state = "relaxwear_2" - worn_state = "relaxwear_2" - starting_accessories = list(/obj/item/clothing/accessory/wcoat/swvest/green) - -/obj/item/clothing/under/serviceoveralls - name = "workman outfit" - desc = "The very image of a working man. Not that you're probably doing work." - icon_state = "mechanic" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - rolled_sleeves = 0 - -/obj/item/clothing/under/frontier - name = "frontier clothes" - desc = "A rugged flannel shirt and denim overalls. A popular style among frontier colonists." - icon_state = "frontier" - worn_state = "frontier" - -/obj/item/clothing/under/rustler - name = "rustler outfit" - desc = "A rugged outfit for rustling cattle out on the frontier." - icon_state = "rustler" - worn_state = "rustler" - -/obj/item/clothing/under/cowboy - name = "cowboy clothes" - desc = "Some rugged clothes for hard labor out on the farm." - icon_state = "cowboy" - worn_state = "cowboy" - -/obj/item/clothing/under/cowboy/tan - name = "tan cowboy clothes" - icon_state = "cowboy_tan" - worn_state = "cowboy_tan" - -/obj/item/clothing/under/cowboy/brown - name = "brown cowboy clothes" - icon_state = "cowboy_brown" - worn_state = "cowboy_brown" - -/obj/item/clothing/under/cowboy/grey - name = "grey cowboy clothes" - icon_state = "cowboy_grey" - worn_state = "cowboy_grey" - -/obj/item/clothing/under/curator - name = "curator uniform" - desc = "A rugged uniform suitable for treasure hunting." - icon_state = "curator" - worn_state = "curator" - -/* - * 80s - */ - -/obj/item/clothing/under/tropical - name = "black tropical oufit" - desc = "A classic themed outfit. This one makes you feel like an animal." - icon_state = "animalstyle" - worn_state = "animalstyle" - starting_accessories = list(/obj/item/clothing/accessory/tropical) - -/obj/item/clothing/under/tropical/green - name = "puke-green tropical oufit" - desc = "A classic themed outfit. This one makes you look like puke." - icon_state = "tropicopuke" - worn_state = "tropicopuke" - starting_accessories = list(/obj/item/clothing/accessory/tropical/green) - -/obj/item/clothing/under/tropical/pink - name = "pink tropical oufit" - desc = "A classic themed outfit. This one makes you feel nostalgic." - icon_state = "3005vintage" - worn_state = "3005vintage" - starting_accessories = list(/obj/item/clothing/accessory/tropical/pink) - -/obj/item/clothing/under/tropical/blue - name = "pink tropical oufit" - desc = "A classic themed outfit. This one makes you feel out of touch." - icon_state = "miamivice" - worn_state = "miamivice" - starting_accessories = list(/obj/item/clothing/accessory/tropical/blue) - -/* - * Modern - */ - -/obj/item/clothing/under/moderncoat - name = "modern wrapped coat (pants)" - desc = "The cutting edge of fashion." - icon_state = "mod_coat_pants" - worn_state = "mod_coat_pants" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS - starting_accessories = list(/obj/item/clothing/accessory/jacket/modwrap) - -/obj/item/clothing/under/modjump - name = "modern jumpsuit" - desc = "A stylish jumpsuit of modern culture." - icon_state = "mod_jump" - worn_state = "mod_jump" - item_state_slots = list(slot_r_hand_str = "orange", slot_l_hand_str = "orange") - -/obj/item/clothing/under/modjump2 - name = "modern outfit" - desc = "A stylish outfit of modern culture." - icon_state = "mod_jump2" - worn_state = "mod_jump2" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - -/obj/item/clothing/under/modjump3 - name = "cyber jumpsuit" - desc = "A cool cyberpunk styled jumpsuit. The lights on the back glitched out and don't work." - icon_state = "cyber" - worn_state = "cyber" - item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") - -/* - * Corporate Uniforms - */ - -/obj/item/clothing/under/mbill - name = "\improper Major Bill's uniform" - desc = "A uniform belonging to Major Bill's Transportation, a shipping megacorporation." - icon_state = "mbill" - worn_state = "mbill" - catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) - -/obj/item/clothing/under/mbill_flight - name = "\improper Major Bill's flightsuit" - desc = "A flightsuit belonging to Major Bill's Transportation, a shipping megacorporation." - icon_state = "mbill_flight" - worn_state = "mbill_flight" - catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) - starting_accessories = list(/obj/item/clothing/accessory/storage/webbing/pilot1) - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/aether - name = "\improper Aether jumpsuit" - desc = "A jumpsuit belonging to Aether Atmospherics and Recycling, a Trans-Stellar that supplies recycling and atmospheric systems to colonies." - icon_state = "aether" - worn_state = "aether" - -/obj/item/clothing/under/corp/pcrc - name = "\improper PCRC uniform" - desc = "A uniform belonging to Proxima Centauri Risk Control, a private security firm." - icon_state = "pcrc" - item_state = "jensensuit" - worn_state = "pcrc" - -/obj/item/clothing/under/corp/centauri - name = "\improper Centauri Provisions jumpsuit" - desc = "A jumpsuit belonging to Centauri Provisions, a Trans-Stellar best known for its food and drink products." - icon_state = "centauri" - worn_state = "centauri" - -/obj/item/clothing/under/corp/grayson - name = "\improper Grayson overalls" - desc = "A set of overalls belonging to Grayson Manufactories, a mining Trans-Stellar." - icon_state = "grayson_mech" - worn_state = "grayson_mech" - -/obj/item/clothing/under/corp/grayson_jump - name = "\improper Grayson jumpsuit" - desc = "A jumpsuit belonging to Grayson Manufactories, a mining Trans-Stellar." - icon_state = "grayson" - worn_state = "grayson" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/wardt - name = "\improper Ward-Takahashi jumpsuit" - desc = "A jumpsuit belonging to Ward-Takahashi, a Trans-Stellar in the consumer goods market." - icon_state = "robotics2" - worn_state = "robotics2" - -/obj/item/clothing/under/corp/confederacy - name = "\improper Confederacy uniform" - desc = "A military uniform belonging to the Confederacy of Man, an independent human government." - icon_state = "confed" - worn_state = "confed" - -/obj/item/clothing/under/corp/saare - name = "\improper SAARE uniform" - desc = "A dress uniform belonging to Stealth Assault Enterprises, a minor private military corporation." - icon_state = "saare" - worn_state = "saare" - -/obj/item/clothing/under/corp/focal - name = "\improper Focal Point jumpsuit" - desc = "A jumpsuit belonging to Focal Point Energistics, an engineering megacorporation." - icon_state = "focal" - worn_state = "focal" - -/obj/item/clothing/under/corp/wulf - name = "\improper Wulf jumpsuit" - desc = "A jumpsuit belonging to Wulf Aeronautics, a ship-building and propulsion systems Trans-Stellar." - icon_state = "wulf" - worn_state = "wulf" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/hephaestus - name = "\improper Hephaestus jumpsuit" - desc = "A jumpsuit belonging to Hephaestus, a Trans-Stellar best known for high-grade arms manufacturing." - icon_state = "heph" - worn_state = "heph" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/kaleidoscope - name = "\improper Kaleidoscope uniform" - desc = "A science uniform belonging to Kaleidoscope Cosmetics, a cosmetic and gene-modification trans-stellar." - icon_state = "kaleido" - worn_state = "kaleido" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/veymed - name = "\improper Vey-Med uniform" - desc = "A uniform belonging to Vey-Medical, a Skrellian biomedical Trans-Stellar." - icon_state = "veymed" - worn_state = "veymed" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/wardt - name = "\improper Ward-Takahashi jumpsuit" - desc = "A jumpsuit belonging to Ward-Takahashi, a Trans-Stellar in the consumer goods market." - icon_state = "robotics2" - worn_state = "robotics2" - -/obj/item/clothing/under/corp/xion - name = "\improper Xion jumpsuit" - desc = "A jumpsuit belonging to Xion Manufacturing, an industrial equipment Trans-Stellar." - icon_state = "xion" - worn_state = "xion" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/zenghu - name = "\improper Zeng-Hu jumpsuit" - desc = "A jumpsuit belonging to Zeng-Hu Pharmaceuticals, a Trans-Stellar in the business of exactly what you'd expect.." - icon_state = "zenghu" - worn_state = "zenghu" - starting_accessories = list(/obj/item/clothing/accessory/tie/red) - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/morpheus - name = "\improper Morpheus jumpsuit" - desc = "A uniform belonging to Morpheus Cyberkinetics, a positronic-run cybernetics Trans-Stellar." - icon_state = "morpheus" - worn_state = "morpheus" - -/obj/item/clothing/under/corp/xion - name = "\improper Xion jumpsuit" - desc = "A jumpsuit belonging to Xion Manufacturing, an industrial equipment Trans-Stellar." - icon_state = "xion" - worn_state = "xion" - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/hedberg - name = "\improper Hedberg law enforcement uniform" - desc = "A sturdy civilian law enforcement uniform belonging to the Hedberg-Hammarstrom private security corporation." - icon_state = "hedberg" - worn_state = "hedberg" - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //Equivalent to security officer's jumpsuit - rolled_sleeves = 0 - -/obj/item/clothing/under/corp/hedbergtech - name = "\improper Hedberg technician uniform" - desc = "A technician's uniform belonging to the Hedberg-Hammarstrom private security corporation. It is lightly shielded against radiation." - icon_state = "hedberg_tech" - worn_state = "hedberg_tech" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) // Equivalent to engineer's jumpsuit. - rolled_sleeves = 0 - -//christmas stuff -/obj/item/clothing/under/christmas/red - name = "red christmas suit" - desc = "A simple red christmas suit that looks close to Santa's!" - icon_state = "christmasred" - -/obj/item/clothing/under/christmas/green - name = "green christmas suit" - desc = "A simple green christmas suit. Smells minty!" - icon_state = "christmasgreen" - -/obj/item/clothing/under/christmas/croptop/red - name = "red crop-top christmas suit" - desc = "A simple red christmas suit that doesn't quite looks like Mrs Claus'." - icon_state = "christmascroppedred" - -/obj/item/clothing/under/christmas/croptop/green - name = "green crop-top christmas suit" - desc = "A simple green christmas suit that doesn't quite looks like Mrs Claus'. Smells minty!" - icon_state = "christmascroppedgreen" - -//leotards -/obj/item/clothing/under/leotard - name = "black leotard" - desc = "A black leotard with a piece of semi-transparent cloth near the bust. Perfect for showing off cleavage. Bunny ears not included." - icon_state = "leotard" - -/obj/item/clothing/under/leotardcolor - name = "colored leotard" - desc = "A colorable leotard with a piece of semi-transparent cloth near the bust. Perfect for showing off cleavage. Bunny ears not included." - icon_state = "leotard_color" - -//skinsuits -/obj/item/clothing/under/skinsuit - name = "skinsuit" - desc = "Similar to other form-fitting latex bodysuits in design and function, skinsuits typically feature integrated hardpoints around common wear areas." - icon_state = "skinsuit" - -/obj/item/clothing/under/skinsuit/gray - name = "gray skinsuit" - icon_state = "skinsuit_g" - -/obj/item/clothing/under/skinsuit/leotard - name = "leotard skinsuit" - desc = "The skinsuit's leotard variant has long since eclipsed its initial function as a breathable undersuit for submersible hardsuits. Although still utilized in this role, it has become rather fashionable to wear outside of deep water operations." - icon_state = "skinsuitleo" - - -/obj/item/clothing/under/skinsuit/leotard/gray - name = "gray leotard skinsuit" - icon_state = "skinsuitleo_g" - -/obj/item/clothing/under/skinsuit/fem - name = "feminine skinsuit" - desc = "Similar to other form-fitting latex bodysuits in design and function, skinsuits typically feature integrated hardpoints around common wear areas." - icon_state = "skinsuitfem" - -/obj/item/clothing/under/skinsuit/fem/gray - name = "feminine gray skinsuit" - icon_state = "skinsuitfem_g" - -/obj/item/clothing/under/skinsuit/fem/leotard - name = "feminine leotard skinsuit" - desc = "The skinsuit's leotard variant has long since eclipsed its initial function as a breathable undersuit for submersible hardsuits. Although still utilized in this role, it has become rather fashionable to wear outside of deep water operations." - icon_state = "skinsuitfemleo" - -/obj/item/clothing/under/skinsuit/fem/leotard/gray - name = "feminine gray leotard skinsuit" - icon_state = "skinsuitfemleo_g" - -//baggy turtlenecks -/obj/item/clothing/under/turtlebaggy - name = "cream baggy turtleneck (cream)" - desc = "A cozy knit turtleneck. It's too baggy and comfortable to be tactical." - icon_state = "bb_turtle" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - - -/obj/item/clothing/under/turtlebaggy/cream_fem - name = "feminine cream baggy turtleneck" - icon_state = "bb_turtle_fem" - - -/obj/item/clothing/under/turtlebaggy/purple - name = "purple baggy turtleneck" - icon_state = "bb_turtlepur" - - -/obj/item/clothing/under/turtlebaggy/purple_fem - name = "feminine purple baggy turtleneck" - icon_state = "bb_turtlepur_fem" - - -/obj/item/clothing/under/turtlebaggy/red - name = "red baggy turtleneck" - icon_state = "bb_turtlered" - - -/obj/item/clothing/under/turtlebaggy/red_fem - name = "feminine red baggy turtleneck" - icon_state = "bb_turtlered_fem" - - -/obj/item/clothing/under/turtlebaggy/blue - name = "blue baggy turtleneck" - icon_state = "bb_turtleblu" - - -/obj/item/clothing/under/turtlebaggy/blue_fem - name = "feminine blue baggy turtleneck" - icon_state = "bb_turtleblu_fem" - - -/obj/item/clothing/under/turtlebaggy/green - name = "green baggy turtleneck" - icon_state = "bb_turtlegrn" - - -/obj/item/clothing/under/turtlebaggy/green_fem - name = "feminine green baggy turtleneck" - icon_state = "bb_turtlegrn_fem" - - -/obj/item/clothing/under/turtlebaggy/black - name = "black baggy turtleneck" - icon_state = "bb_turtleblk" - - -/obj/item/clothing/under/turtlebaggy/black_fem - name = "feminine black baggy turtleneck" - icon_state = "bb_turtleblk_fem" - -//half-moon outfit -/obj/item/clothing/under/half_moon - name = "half moon outfit" - desc = "This eminently fashionable outfit consists of a tailored latex leotard and daringly cut white shorts. Paired with plunging off-color stockings, it's to die for." - icon_state = "half_moon" - -//fiend clothes -/obj/item/clothing/under/fiendsuit - name = "fiendish suit" - desc = "A red and black suit befitting someone from the dark pits themselves… Or someone way too edgy." - icon_state = "fiendsuit" - - -/obj/item/clothing/under/fienddress - name = "fiendish dress" - desc = "A red and black dress befitting someone from the dark pits themselves… Or someone way too edgy." - icon_state = "fienddress" +/obj/item/clothing/under/gimmick //used to fix an error + name = "gimmick jumpsuit" + desc = "Something about this jumpsuit feels... off..." + icon_state = "grey" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +/obj/item/clothing/under/pj/red + name = "red pj's" + desc = "Sleepwear." + icon_state = "red_pyjamas" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/pj/blue + name = "blue pj's" + desc = "Sleepwear." + icon_state = "blue_pyjamas" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/captain_fly + name = "rogue's uniform" + desc = "For the man who doesn't care because he's still free." + icon_state = "captain_fly" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/under/scratch + name = "white suit" + desc = "A white suit, suitable for an excellent host" + icon_state = "scratch" + +/obj/item/clothing/under/scratch/skirt + name = "white skirt suit" + icon_state = "scratch_skirt" + item_state_slots = list(slot_r_hand_str = "scratch", slot_l_hand_str = "scratch") + +/obj/item/clothing/under/sl_suit + desc = "It's a very amish looking suit." + name = "amish suit" + icon_state = "sl_suit" + rolled_sleeves = 0 + +/obj/item/clothing/under/waiter + name = "waiter's outfit" + desc = "It's a very smart uniform with a special pocket for tip." + icon_state = "waiter" + rolled_sleeves = 0 + +/obj/item/clothing/under/customs + name = "customs uniform" + desc = "A standard SolGov customs uniform. Complete with epaulettes." + icon_state = "cu_suit" + +/obj/item/clothing/under/customs/khaki + icon_state = "cu_suit_kh" + +/obj/item/clothing/under/rank/mailman + name = "mailman's jumpsuit" + desc = "'Special delivery!'" + icon_state = "mailman" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + rolled_sleeves = 0 + +/obj/item/clothing/under/sexyclown + name = "sexy-clown suit" + desc = "It makes you look HONKable!" + icon_state = "sexyclown" + item_state_slots = list(slot_r_hand_str = "clown", slot_l_hand_str = "clown") + body_parts_covered = UPPER_TORSO|LOWER_TORSO + rolled_sleeves = -1 //Please never + +/obj/item/clothing/under/clown //i'm only putting it here to make subtypes of it, since the original suit is under/rank/clown + name = "clown suit" + desc = "Honk!" + icon_state = "clown" + rolled_sleeves = -1 + +/obj/item/clothing/under/clown/green + name = "green clown suit" + icon_state = "greenclown" + +/obj/item/clothing/under/clown/purple + name = "purple clown suit" + icon_state = "purpleclown" + +/obj/item/clothing/under/clown/yellow + name = "yellow clown suit" + icon_state = "yellowclown" + +/obj/item/clothing/under/clown/orange + name = "orange clown suit" + icon_state = "orangeclown" + +/obj/item/clothing/under/clown/blue + name = "blue clown suit" + icon_state = "blueclown" + +/obj/item/clothing/under/clown/rainbow + name = "rainbow clown suit" + icon_state = "rainbowclown" + +/obj/item/clothing/under/rank/vice + name = "vice officer's jumpsuit" + desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." + icon_state = "vice" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +//This set of uniforms looks fairly fancy and is generally used for high-ranking NT personnel from what I've seen, so lets give them appropriate ranks. +/obj/item/clothing/under/rank/centcom + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain.\"" + name = "\improper Officer's Dress Uniform" + icon_state = "officer" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + displays_id = 0 + +/obj/item/clothing/under/rank/centcom_officer + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Admiral.\"" + name = "\improper Officer's Dress Uniform" + icon_state = "officer" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + displays_id = 0 + +/obj/item/clothing/under/rank/centcom_captain + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Admiral-Executive.\"" + name = "\improper Officer's Dress Uniform" + icon_state = "centcom" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + displays_id = 0 + +/obj/item/clothing/under/ert + name = "ERT tactical uniform" + desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. It looks very tactical." + icon_state = "ert_uniform" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/under/space + name = "\improper NASA jumpsuit" + desc = "It has a NASA logo on it and is made of space-proofed materials." + icon_state = "black" + w_class = ITEMSIZE_LARGE//bulky item + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + rolled_sleeves = 0 + +/obj/item/clothing/under/acj + name = "administrative cybernetic jumpsuit" + icon_state = "syndicate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + armor = list(melee = 100, bullet = 100, laser = 100,energy = 100, bomb = 100, bio = 100, rad = 100) + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + siemens_coefficient = 0 + +/obj/item/clothing/under/owl + name = "owl uniform" + desc = "A jumpsuit with owl wings. Photorealistic owl feathers! Twooooo!" + icon_state = "owl" + +/obj/item/clothing/under/johnny + name = "johnny~~ jumpsuit" + desc = "Johnny~~" + icon_state = "johnny" + +/obj/item/clothing/under/color/rainbow + name = "rainbow jumpsuit" + desc = "A multi-colored jumpsuit." + icon_state = "rainbow" + +/obj/item/clothing/under/psysuit + name = "dark undersuit" + desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." + icon_state = "psysuit" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +/obj/item/clothing/under/gentlesuit + name = "gentlemans suit" + desc = "A silk black shirt with matching gray slacks. Feels proper." + icon_state = "gentlesuit" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/tie/white, /obj/item/clothing/accessory/wcoat/gentleman) + +/obj/item/clothing/under/gentlesuit/skirt + name = "lady's suit" + desc = "A silk black blouse with a matching gray skirt. Feels proper." + icon_state = "gentlesuit_skirt" + + +/obj/item/clothing/under/suit_jacket + name = "black suit" + desc = "A black suit and red tie. Very formal." + icon_state = "black_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/suit_jacket/really_black + name = "executive suit" + desc = "A formal black suit and red tie, intended for the station's finest." + icon_state = "really_black_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/suit_jacket/really_black/skirt + name = "executive skirt suit" + desc = "A formal black suit and red necktie, intended for the station's finest." + icon_state = "really_black_suit_skirt" + +/obj/item/clothing/under/suit_jacket/female + name = "female executive suit" + desc = "A formal trouser suit for women, intended for the station's finest." + icon_state = "black_suit_fem" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/suit_jacket/female/skirt + name = "executive skirt" + desc = "A formal suit skirt for women, intended for the station's finest." + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + icon_state = "black_suit_fem" + item_state = "black_formal_skirt" + +/obj/item/clothing/under/schoolgirl + name = "schoolgirl uniform" + desc = "It's just like one of my Japanese animes!" + icon_state = "schoolgirl" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/overalls + name = "laborer's overalls" + desc = "A set of durable overalls for getting the job done." + icon_state = "overalls" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + +/obj/item/clothing/under/overalls/sleek + name = "sleek overalls" + desc = "A set of modern pleather reinforced overalls." + icon_state = "overalls_sleek" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/overalls/service + name = "workman overalls" + desc = "The very image of a working man. Not that you're probably doing work." + icon_state = "mechanic_over" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + rolled_sleeves = 0 + +/obj/item/clothing/under/overalls/frontier + name = "frontier overalls" + desc = "Simple denim overalls. A popular style among frontier colonists, when paired with a flannel." + icon_state = "frontier_over" + worn_state = "frontier_over" + +/obj/item/clothing/under/overalls/rustler + name = "rustler overalls" + desc = "A rugged pair of overalls for rustling cattle out on the frontier." + icon_state = "rustler_over" + worn_state = "rustler_over" + +/obj/item/clothing/under/pirate + name = "pirate outfit" + desc = "Yarr." + icon_state = "pirate" + item_state_slots = list(slot_r_hand_str = "sl_suit", slot_l_hand_str = "sl_suit") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/soviet + name = "soviet uniform" + desc = "For the Motherland!" + icon_state = "soviet" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +/obj/item/clothing/under/redcoat + name = "redcoat uniform" + desc = "Looks old." + icon_state = "redcoat" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/under/kilt + name = "kilt" + desc = "Includes shoes and plaid" + icon_state = "kilt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET + rolled_sleeves = 0 + +/obj/item/clothing/under/sexymime + name = "sexy mime outfit" + desc = "The only time when you DON'T enjoy looking at someone's rack." + icon_state = "sexymime" + item_state_slots = list(slot_r_hand_str = "mime", slot_l_hand_str = "mime") + body_parts_covered = UPPER_TORSO|LOWER_TORSO + rolled_sleeves = -1 //Please never + +/obj/item/clothing/under/sexymime/dress //another style of dress closer to the jumpsuit than the sexy outfit + name = "mime's jumpskirt" + desc = "It's not very colourful." + icon_state = "mimedress" + +/obj/item/clothing/under/gladiator + name = "gladiator uniform" + desc = "Are you not entertained? Is that not why you are here?" + icon_state = "gladiator" + item_state_slots = list(slot_r_hand_str = "yellow", slot_l_hand_str = "yellow") + body_parts_covered = LOWER_TORSO + +/obj/item/clothing/under/ascetic + name = "plain ascetic garb" + desc = "Popular with freshly grown vatborn and new age cultists alike." + icon_state = "ascetic" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + +/obj/item/clothing/under/robe + name = "black robe" + desc = "A black robe. It gives off uncomfortable cult vibes." + icon_state = "robe" + +/obj/item/clothing/under/whiterobe + name = "white robe" + desc = "A white robe. It gives off uncomfortable cult vibes." + icon_state = "whiterobe" + +/obj/item/clothing/under/goldrobe + name = "black gold-lined robe" + desc = "A gold-lined black robe. It gives off uncomfortable cult vibes, but fancy." + icon_state = "goldrobe" + +/obj/item/clothing/under/whitegoldrobe + name = "white gold-lined robe" + desc = "A gold-lined white robe. It gives off uncomfortable cult vibes, but fancy." + icon_state = "whitegoldrobe" + +/* + * Dress Stuff + */ + +/obj/item/clothing/under/dress + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/blacktango + name = "black tango dress" + desc = "Filled with Latin fire." + icon_state = "black_tango" + +/obj/item/clothing/under/dress/blacktango/alt + name = "black tango dress" + desc = "Filled with Latin fire." + icon_state = "black_tango_alt" + item_state_slots = list(slot_r_hand_str = "black_tango", slot_l_hand_str = "black_tango") + +/obj/item/clothing/under/dress/stripeddress + name = "striped dress" + desc = "Fashion in space." + icon_state = "striped_dress" + +/obj/item/clothing/under/dress/sailordress + name = "sailor dress" + desc = "Formal wear for a leading lady." + icon_state = "sailor_dress" + +/obj/item/clothing/under/dress/redeveninggown + name = "red evening gown" + desc = "Fancy dress for space bar singers." + icon_state = "red_evening_gown" + +/obj/item/clothing/under/dress/maid + name = "maid costume" + desc = "Maid in China." + icon_state = "maid" + +/obj/item/clothing/under/dress/maid/janitor + name = "maid uniform" + desc = "A simple maid uniform for housekeeping." + icon_state = "janimaid" + +/obj/item/clothing/under/dress/maid/sexy + name = "sexy maid costume" + desc = "You must be a bit risque teasing all of them in a maid uniform!" + icon_state = "sexymaid" + +/obj/item/clothing/under/dress/maid/under_maid + name = "modern maid costume" + desc = "You're going to use this for housekeeping, right?" + icon_state = "under_maid" + +/obj/item/clothing/under/dress/dress_fire + name = "flame dress" + desc = "A small black dress with blue flames print on it." + icon_state = "dress_fire" + +/obj/item/clothing/under/dress/dress_green + name = "green dress" + desc = "A simple, tight fitting green dress." + icon_state = "dress_green" + +/obj/item/clothing/under/dress/dress_orange + name = "orange dress" + desc = "A fancy orange gown for those who like to show leg." + icon_state = "dress_orange" + +/obj/item/clothing/under/dress/dress_pink + name = "pink dress" + desc = "A simple, tight fitting pink dress." + icon_state = "dress_pink" + +/obj/item/clothing/under/dress/dress_yellow + name = "yellow dress" + desc = "A flirty, little yellow dress." + icon_state = "dress_yellow" + +/obj/item/clothing/under/dress/dress_saloon + name = "saloon girl dress" + desc = "A old western inspired gown for the girl who likes to drink." + icon_state = "dress_saloon" + item_state_slots = list(slot_r_hand_str = "dress_white", slot_l_hand_str = "dress_white") + +/obj/item/clothing/under/dress/dress_cap + name = "site manager's dress uniform" + desc = "Feminine fashion for the style conscious Site Manager." + icon_state = "dress_cap" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/dress/dress_hop + name = "head of personnel dress uniform" + desc = "Feminine fashion for the style conscious HoP." + icon_state = "dress_hop" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/dress/dress_hr + name = "human resources director uniform" + desc = "Superior class for the nosy H.R. Director." + icon_state = "huresource" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/dress/black_corset + name = "black corset" + desc = "A black corset and skirt for those fancy nights out." + icon_state = "black_corset" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/flower_dress + name = "flower dress" + desc = "A beautiful dress with a skirt of flowers." + icon_state = "flower_dress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/red_swept_dress + name = "red swept dress" + desc = "A red dress that sweeps to the side." + icon_state = "red_swept_dress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/flamenco + name = "flamenco dress" + desc = "A Mexican flamenco dress." + icon_state = "flamenco" + +/obj/item/clothing/under/dress/westernbustle + name = "western bustle" + desc = "A western bustle dress from Earth's late 1800's." + icon_state = "westernbustle" + +/obj/item/clothing/under/dress/sari + name = "red sari" + desc = "A colorful traditional dress originating from India." + icon_state = "sari_red" + item_state_slots = list(slot_r_hand_str = "darkreddress", slot_l_hand_str = "darkreddress") + +/obj/item/clothing/under/dress/sari/green + name = "green sari" + icon_state = "sari_green" + item_state_slots = list(slot_r_hand_str = "dress_green", slot_l_hand_str = "dress_green") + +/obj/item/clothing/under/dress/lilacdress + name = "lilac dress" + desc = "A simple black dress adorned in fake purple lilacs." + icon_state = "lilacdress" + +/obj/item/clothing/under/dress/white + name = "white wedding dress" + desc = "A fancy white dress with a blue underdress." + icon_state = "whitedress1" + flags_inv = HIDESHOES + +/obj/item/clothing/under/dress/white2 + name = "long dress" + desc = "A long dress." + icon_state = "whitedress2" + addblends = "whitedress2_a" + flags_inv = HIDESHOES + +/obj/item/clothing/under/dress/white3 + name = "short dress" + desc = "A short, plain dress." + icon_state = "whitedress3" + addblends = "whitedress3_a" + +/obj/item/clothing/under/dress/white4 + name = "long flared dress" + desc = "A long white dress that flares out at the bottom." + icon_state = "whitedress4" + addblends = "whitedress4_a" + flags_inv = HIDESHOES + +/obj/item/clothing/under/dress/darkred + name = "fancy dark red dress" + desc = "A short, red dress with a black belt. Fancy." + icon_state = "darkreddress" + +/obj/item/clothing/under/dress/polka + name = "polka dot dress" + desc = "A sleeveless, cream colored dress with red polka dots." + icon_state = "polka" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/twistfront + name = "twistfront crop dress" + desc = "A black skirt and red twistfront croptop. Fancy!" + icon_state = "twistfront" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/cropdress + name = "crop dress" + desc = "A red skirt and longsleeved button-up crop top." + icon_state = "cropdress" + +/obj/item/clothing/under/dress/vneck + name = "v-neck dress" + desc = "A black v-neck dress with an exaggerated neckline covered in a sheer mesh." + icon_state = "vneckdress" + +/obj/item/clothing/under/dress/bluedress + name = "blue dress" + desc = "A plain blue dress with a white belt." + icon_state = "bluedress" + +/obj/item/clothing/under/dress/wench + name = "wench's dress" + desc = "A white dress styled like a Ye Old Barmaid. Saucy!" + icon_state = "wench" + +/obj/item/clothing/under/dress/littleblackdress + name = "little black dress" + desc = "A little strapless black dress with a red ribbon and flower accessory." + icon_state = "littleblackdress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/pinktutu + name = "pink tutu" + desc = "A black leotard with a pink mesh tutu. Perfect for ballet practice." + icon_state = "pinktutu" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/festivedress + name = "festive dress" + desc = "A red and white dress themed after some winter holidays. Tastefully festive!" + icon_state = "festivedress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/revealingdress + name = "revealing dress" + desc = "A very revealing black and blue dress. Is this work appropriate?" + icon_state = "revealingdress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/gothic + name = "gothic dress" + desc = "A black dress with a sheer mesh over it, tastefully old school goth." + icon_state = "gothic" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/formalred + name = "formal red dress" + desc = "A very formal red dress, for those fancy galas." + icon_state = "formalred" + flags_inv = HIDESHOES + +/obj/item/clothing/under/dress/pentagram + name = "pentagram dress" + desc = "A black dress with straps over the chest in the shape of a pentagram." + icon_state = "pentagram" + +/obj/item/clothing/under/dress/yellowswoop + name = "yellow swooped dress" + desc = "A yellow dress that swoops to the side." + icon_state = "yellowswoop" + +/obj/item/clothing/under/dress/countess + name = "countess dress" + desc = "A red and black dress fit for a countess." + icon_state = "countess" + +/obj/item/clothing/under/dress/verglasdress + name = "verglas dress" + desc = "The modern twist on a forgotten pattern, the Verglas style utilizes comfortable velvet and silver white satin to create an otherworldly effect evocative of winter, or the void." + icon_state = "verglas_dress" + +/obj/item/clothing/under/dress/goddess + name = "goddess dress" + desc = "A blue and orange dress fit for a goddess." + icon_state = "goddess" + +/obj/item/clothing/under/dress/alpine + name = "alpine dress" + desc = "A green and white dress that makes you want to yodel." + icon_state = "corsetdress" + +/obj/item/clothing/under/dress/goldwrap + name = "golden dress" + desc = "A dress so ostentatious that you feel poorer just looking at it." + icon_state = "golddress" + +/obj/item/clothing/under/dress/golddress + name = "golden wrap" + desc = "An outfit so ostentatious that you feel poorer just looking at it." + icon_state = "goldwrap" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/hightrousers + name = "high-waisted trousers" + desc = "A waistline this high is just made for ripping bodices, swashing buckles, or - just occasionally - sucking blood." + icon_state = "gayvampire" + +/obj/item/clothing/under/dress/singer + name = "blue singer dress" + desc = "Just looking at this makes you want to sing." + icon_state = "bsing" + +/obj/item/clothing/under/dress/singer/yellow + name = "yellow singer dress" + desc = "Just looking at this makes you want to sing." + icon_state = "ysing" + +/obj/item/clothing/under/dress/tabard + name = "white tabard-dress" + desc = "A gold-trimmed white tabard-dress with a large V-shaped boob window. For when you want to show off your hips and look classy at the same time." + icon_state = "white_tabard" + +/obj/item/clothing/under/dress/tabard/black + name = "black tabard-dress" + desc = "A gold-trimmed black tabard-dress with a large circular boob window. For when you want to show off your hips and look classy at the same time." + icon_state = "black_tabard" + +/* + * Wedding Stuff + */ + +/obj/item/clothing/under/wedding + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/wedding/bride_orange + name = "orange wedding dress" + desc = "A big and puffy orange dress." + icon_state = "bride_orange" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_purple + name = "purple wedding dress" + desc = "A big and puffy purple dress." + icon_state = "bride_purple" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_blue + name = "blue wedding dress" + desc = "A big and puffy blue dress." + icon_state = "bride_blue" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_red + name = "red wedding dress" + desc = "A big and puffy red dress." + icon_state = "bride_red" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_white + name = "silky wedding dress" + desc = "A white wedding gown made from the finest silk." + icon_state = "bride_white" + flags_inv = HIDESHOES + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/wedding/floofdress + name = "floofy dress" + desc = "A lovely floofed out dress for formal occasions. Comes in many colors!" + icon_state = "floofdress" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/whitegown + name = "white gown" + desc = "A elegant white gown with accents of sheer mesh." + icon_state = "whitegown" + +/* + * Uniforms and such + */ + +/obj/item/clothing/under/sundress + name = "sundress" + desc = "Makes you want to frolic in a field of daisies." + icon_state = "sundress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/sundress_white + name = "flowery white sundress" + desc = "A white sundress decorated with purple lilies." + icon_state = "sundress_white" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/sundress_pink + name = "pink stripied sundress" + desc = "A cute pink sundress." + icon_state = "pinksun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/sundress_white + name = "white sundress" + desc = "A white sundress, it's short." + icon_state = "whitesun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/sundress_pinkbow + name = "bowed pink sundress" + desc = "A cute pink sundress with a bow." + icon_state = "bowsun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/sundress_blue + name = "long blue sundress" + desc = "A long blue sun dress with white frills towards the bottom." + icon_state = "bluesun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/dress/sundress_pinkshort + name = "short pink sundress" + desc = "A very short pink sundress, it's more like a chemise." + icon_state = "shortpink" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/twopiece + name = "two-piece dress" + desc = "A fancy two-piece dress, the pieces are sewn together." + icon_state = "twopiece" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/dress/gothic2 + name = "lacey gothic dress" + desc = "An elegant gothic dress with lace decorations." + icon_state = "gothic2" + +/obj/item/clothing/under/captainformal + name = "site manager's formal uniform" + desc = "A Site Manager's formal-wear, for special occasions." + icon_state = "captain_formal" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + +/obj/item/clothing/under/hosformalmale + name = "head of security's formal uniform" + desc = "A male head of security's formal-wear, for special occasions." + icon_state = "hos_formal_male" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + +/obj/item/clothing/under/hosformalfem + name = "head of security's formal uniform" + desc = "A female head of security's formal-wear, for special occasions." + icon_state = "hos_formal_fem" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + +/obj/item/clothing/under/assistantformal + name = "assistant's formal uniform" + desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." + icon_state = "assistant_formal" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + +/obj/item/clothing/under/suit_jacket/charcoal + name = "charcoal suit" + desc = "A charcoal suit and red tie. Very professional." + icon_state = "charcoal_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + starting_accessories = list(/obj/item/clothing/accessory/tie/navy, /obj/item/clothing/accessory/jacket/charcoal) + +/obj/item/clothing/under/suit_jacket/charcoal/skirt + name = "charcoal skirt" + icon_state = "charcoal_suit_skirt" + +/obj/item/clothing/under/suit_jacket/navy + name = "navy suit" + desc = "A navy suit and red tie, intended for the station's finest." + icon_state = "navy_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + starting_accessories = list(/obj/item/clothing/accessory/tie/red, /obj/item/clothing/accessory/jacket/navy) + +/obj/item/clothing/under/suit_jacket/navy/skirt + name = "navy skirt" + icon_state = "navy_suit_skirt" + +/obj/item/clothing/under/suit_jacket/burgundy + name = "burgundy suit" + desc = "A burgundy suit and black tie. Somewhat formal." + icon_state = "burgundy_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + starting_accessories = list(/obj/item/clothing/accessory/tie/black, /obj/item/clothing/accessory/jacket/burgundy) + +/obj/item/clothing/under/suit_jacket/burgundy/skirt + name = "burgundy skirt" + icon_state = "burgundy_suit_skirt" + +/obj/item/clothing/under/suit_jacket/red + name = "red suit" + desc = "A red suit and blue tie. Somewhat formal." + icon_state = "red_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_red", slot_l_hand_str = "lawyer_red") + starting_accessories = list(/obj/item/clothing/accessory/tie/navy, /obj/item/clothing/accessory/jacket/red) + +/obj/item/clothing/under/suit_jacket/red/skirt + name = "red skirt" + icon_state = "red_suit_skirt" + +/obj/item/clothing/under/suit_jacket/teal + name = "teal suit" + desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." + icon_state = "teal_suit" + item_state_slots = list(slot_r_hand_str = "green", slot_l_hand_str = "green") + starting_accessories = list(/obj/item/clothing/accessory/tie/yellow, /obj/item/clothing/accessory/jacket/teal) + +/obj/item/clothing/under/suit_jacket/teal/skirt + name = "teal skirt suit" + icon_state = "teal_suit_skirt" + +/obj/item/clothing/under/suit_jacket/green + name = "green suit" + desc = "A green suit and yellow necktie. Exemplifies authority." + icon_state = "green_suit" + item_state_slots = list(slot_r_hand_str = "centcom", slot_l_hand_str = "centcom") + starting_accessories = list(/obj/item/clothing/accessory/tie/yellow, /obj/item/clothing/accessory/jacket/green) + +/obj/item/clothing/under/gov + name = "green formal uniform" + desc = "A neat proper uniform of someone on offical business. The collar is immaculately starched." + icon_state = "greensuit" + item_state_slots = list(slot_r_hand_str = "centcom", slot_l_hand_str = "centcom") + starting_accessories = list(/obj/item/clothing/accessory/tie/darkgreen) + rolled_sleeves = 0 + +/obj/item/clothing/under/gov/skirt + name = "green formal skirt" + desc = "A neat proper uniform of someone on offical business. The top button is sewn shut." + icon_state = "greensuit_skirt" + +/obj/item/clothing/under/suit_jacket/green/skirt + name = "green skirt suit" + icon_state = "green_suit_skirt" + +/obj/item/clothing/under/suit_jacket/checkered + name = "checkered suit" + desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" + icon_state = "checkered_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + starting_accessories = list(/obj/item/clothing/accessory/tie/black, /obj/item/clothing/accessory/jacket/checkered) + +/obj/item/clothing/under/suit_jacket/checkered/skirt + name = "checkered skirt" + icon_state = "checkered_suit_skirt" + +/obj/item/clothing/under/suit_jacket/gambler + name = "gambling suit" + desc = "The suit of a gambler. Lady luck be with you." + icon_state = "gambler_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + starting_accessories = list(/obj/item/clothing/accessory/tie/black, /obj/item/clothing/accessory/jacket/gambler) + +/obj/item/clothing/under/suit_jacket/extravagant + name = "extravagant suit" + desc = "An extravagant suit. Perfect for being over dramatic." + icon_state = "extravagant_suit" + item_state_slots = list(slot_r_hand_str = "lawyer_black", slot_l_hand_str = "lawyer_black") + starting_accessories = list(/obj/item/clothing/accessory/jacket/extravagant) + +/obj/item/clothing/under/suit_jacket/tan + name = "tan suit" + desc = "A tan suit. Smart, but casual." + icon_state = "tan_suit" + item_state_slots = list(slot_r_hand_str = "tan_suit", slot_l_hand_str = "tan_suit") + starting_accessories = list(/obj/item/clothing/accessory/tie/yellow, /obj/item/clothing/accessory/jacket) + +/obj/item/clothing/under/suit_jacket/tan/skirt + name = "tan skirt" + icon_state = "tan_suit_skirt" + +/obj/item/clothing/under/cheongsam + name = "white cheongsam" + desc = "It is a white cheongsam dress." + icon_state = "cheongsam-white" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/cheongsam/red + name = "red cheongsam" + desc = "It is a red cheongsam dress." + icon_state = "cheongsam-red" + +/obj/item/clothing/under/cheongsam/blue + name = "blue cheongsam" + desc = "It is a blue cheongsam dress." + icon_state = "cheongsam-blue" + +/obj/item/clothing/under/cheongsam/black + name = "black cheongsam" + desc = "It is a black cheongsam dress." + icon_state = "cheongsam-black" + +/obj/item/clothing/under/cheongsam/darkred + name = "dark red cheongsam" + desc = "It is a dark red cheongsam dress." + icon_state = "cheongsam-darkred" + +/obj/item/clothing/under/cheongsam/green + name = "green cheongsam" + desc = "It is a green cheongsam dress." + icon_state = "cheongsam-green" + +/obj/item/clothing/under/cheongsam/purple + name = "purple cheongsam" + desc = "It is a purple cheongsam dress." + icon_state = "cheongsam-purple" + +/obj/item/clothing/under/cheongsam/darkblue + name = "dark blue cheongsam" + desc = "It is a dark blue cheongsam dress." + icon_state = "cheongsam-darkblue" + +/obj/item/clothing/under/blazer + name = "blue blazer" + desc = "A bold but yet conservative outfit, red corduroys, navy blazer and a tie." + icon_state = "blue_blazer" + item_state_slots = list(slot_r_hand_str = "lawyer_blue", slot_l_hand_str = "lawyer_blue") + +/obj/item/clothing/under/blazer/skirt + name = "ladies blue blazer" + desc = "A bold but yet conservative outfit, a red pencil skirt and a navy blazer." + icon_state = "blue_blazer_skirt" + +/obj/item/clothing/under/croptop + name = "crop top" + desc = "A shirt that has had the top cropped. This one is NT sponsored." + icon_state = "croptop" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +/obj/item/clothing/under/croptop/red + name = "red crop top" + desc = "A red shirt that has had the top cropped." + icon_state = "croptop_red" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/under/croptop/grey + name = "grey crop top" + desc = "A grey shirt that has had the top cropped." + icon_state = "croptop_grey" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +/obj/item/clothing/under/cuttop + name = "grey cut top" + desc = "A grey shirt that has had the top cut low." + icon_state = "cuttop" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +/obj/item/clothing/under/cuttop/red + name = "red cut top" + desc = "A red shirt that has had the top cut low." + icon_state = "cuttop_red" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/under/harness + name = "gear harness" + desc = "How... minimalist." + icon_state = "gear_harness" + body_parts_covered = 0 + +/obj/item/clothing/under/haltertop + name = "halter top" + desc = "Jean shorts and a black halter top. Perfect for casual Fridays!" + icon_state = "haltertop" + +/obj/item/clothing/under/tanktop + name = "tank top" + desc = "The most versatile top ever created." + icon_state = "tanktop" + +/obj/item/clothing/under/rippedpunk + name = "ripped punk jeans" + desc = "Black ripped jeans and a fishnet top. How punk." + icon_state = "rippedpunk" + +/obj/item/clothing/under/greenasym + name = "green asymmetrical jumpsuit" + desc = "A green futuristic uniform with asymmetrical pants. Trendy!" + icon_state = "greenasym" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/cyberpunkharness + name = "cyberpunk strapped harness" + desc = "A cyberpunk styled harness and pants. Perfect for your dystopian future." + icon_state = "cyberhell" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + +/obj/item/clothing/under/blackngold + name = "black and gold gown" + desc = "A black and gold gown. You get the impression this is typically worn for religious purposes." + icon_state = "blackngold" + +/obj/item/clothing/under/sheerblue + name = "sheer blue dress" + desc = "An entirely sheer blue dress. Best worn with something underneath!" + icon_state = "sheerblue" + +/obj/item/clothing/under/disheveled + name = "disheveled suit" + desc = "What might pass as well maintained formal attire. If you're blind." + icon_state = "disheveled" + +/obj/item/clothing/under/flower_skirt + name = "flower skirt" + desc = "A flowery skirt that comes in a variety of colors." + icon_state = "flowerskirt" + body_parts_covered = LOWER_TORSO + +/obj/item/clothing/under/fashionminiskirt + name = "fashionable miniskirt" + desc = "An impractically short miniskirt allegedly making waves through the local fashion scene." + icon_state = "miniskirt_fashion" + body_parts_covered = LOWER_TORSO + +/obj/item/clothing/under/retrosweater + name = "retro sweater" + desc = "A rugged cableknit sweater and leather pants, fit for a dashing space adventurer." + icon_state = "retro_sweater" + +/obj/item/clothing/under/wednesday + name = "cropped sweater skirt" + desc = "A cropped green sweater and matching miniskirt." + icon_state = "wednesday" + +/* + * Swimsuit + */ + +/obj/item/clothing/under/swimsuit/ + siemens_coefficient = 1 + body_parts_covered = 0 + +/obj/item/clothing/under/swimsuit/black + name = "black swimsuit" + desc = "An oldfashioned black swimsuit." + icon_state = "swim_black" + +/obj/item/clothing/under/swimsuit/blue + name = "blue swimsuit" + desc = "An oldfashioned blue swimsuit." + icon_state = "swim_blue" + +/obj/item/clothing/under/swimsuit/purple + name = "purple swimsuit" + desc = "An oldfashioned purple swimsuit." + icon_state = "swim_purp" + +/obj/item/clothing/under/swimsuit/green + name = "green swimsuit" + desc = "An oldfashioned green swimsuit." + icon_state = "swim_green" + +/obj/item/clothing/under/swimsuit/red + name = "red swimsuit" + desc = "An oldfashioned red swimsuit." + icon_state = "swim_red" + +/obj/item/clothing/under/swimsuit/striped + name = "striped swimsuit" + desc = "A more revealing striped swimsuit." + icon_state = "swim_striped" + +/obj/item/clothing/under/swimsuit/white + name = "white swimsuit" + desc = "A classic one piece." + icon_state = "swim_white" + +/obj/item/clothing/under/swimsuit/earth + name = "earthen swimsuit" + desc = "A design more popular on Earth these days." + icon_state = "swim_earth" + +/obj/item/clothing/under/swimsuit/stripper/stripper_pink + name = "pink swimsuit" + desc = "A rather skimpy pink swimsuit." + icon_state = "stripper_p" + +/obj/item/clothing/under/swimsuit/stripper/stripper_green + name = "green swimsuit" + desc = "A rather skimpy green swimsuit." + icon_state = "stripper_g" + +/obj/item/clothing/under/swimsuit/stripper/mankini + name = "mankini" + desc = "No honest man would wear this abomination" + icon_state = "mankini" + +/obj/item/clothing/under/swimsuit/cowbikini + name = "cow print bikini" + desc = "A rather skimpy cow patterned swimsuit." + icon_state = "swim_cow" + +/obj/item/clothing/under/swimsuit/highclass + name = "high class swimsuit" + desc = "An elegant swimsuit with a white bikini top and black bikini bottom. Thin black silk drapes down the back and goes to the upper thighs, and authentic gold rings hold the top together at the bust and back." + icon_state = "swim_highclass" + +/obj/item/clothing/under/swimsuit/risque + name = "risque swimsuit" + desc = "This fits a bit too snug in all the right places. Comes with a collar, for inscrutable reasons." + icon_state = "swim_risque" + +/obj/item/clothing/under/swimsuit/streamlined + name = "streamlined swimsuit" + desc = "An all white one-piece that maintains modesty without sacrificing class." + icon_state = "swim_stream" + +/obj/item/clothing/under/wetsuit + name = "wetsuit" + desc = "For when you need to scuba dive your way into an enemy base." + icon_state = "wetsuit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/under/wetsuit_skimpy + name = "tactical wetsuit" + desc = "For when you need to scuba dive your way into an enemy base but still want to show off a little skin." + icon_state = "wetsuit_skimpy" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/wetsuit_rec + name = "recreational wetsuit" + desc = "For when you need to kayak your way into an enemy base." + icon_state = "wetsuit_rec" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + +/* + * Pyjamas + */ + +/obj/item/clothing/under/bluepyjamas + name = "blue pyjamas" + desc = "Slightly old-fashioned sleepwear." + icon_state = "blue_pyjamas" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/under/redpyjamas + name = "red pyjamas" + desc = "Slightly old-fashioned sleepwear." + icon_state = "red_pyjamas" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/under/rank/psych/turtleneck/sweater + desc = "A warm looking sweater and a pair of dark blue slacks." + name = "sweater" + icon_state = "turtleneck" + worn_state = "turtleneck" + +/obj/item/clothing/under/medigown + name = "medical gown" + desc = "A flimsy examination gown, the back ties never close." + icon_state = "medicalgown" + worn_state = "medicalgown" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/bathrobe + name = "bathrobe" + desc = "A fluffy robe to keep you from showing off to the world." + icon_state = "bathrobe" + worn_state = "bathrobe" + +/obj/item/clothing/under/explorer + desc = "A green uniform for operating in hazardous environments." + name = "explorer's jumpsuit" + icon_state = "explorer" + +/obj/item/clothing/under/explorer/armored + desc = "A green uniform for operating in hazardous environments. This one looks like it's been modified." + armor = list(melee = 10, bullet = 10, laser = 10,energy = 10, bomb = 10, bio = 10, rad = 30) + armorsoak = list(melee = 5, bullet = 5, laser = 5,energy = 5, bomb = 5, bio = 5, rad = 0) + +/obj/item/clothing/under/cohesion + name = "black cohesion suit" + desc = "A plain black cohesion suit intended to assist Prometheans in maintaining their form and prevent direct skin exposure." + icon_state = "cohesionsuit" + rolled_sleeves = -1 // defeats the purpose!!! + +/obj/item/clothing/under/cohesion/striped + name = "red striped cohesion suit" + desc = "A black cohesion suit with red stripes intended to assist Prometheans in maintaining their form and prevent direct skin exposure." + icon_state = "cohesionsuit_striped" + +/obj/item/clothing/under/cohesion/decal + name = "purple decaled cohesion suit" + desc = "A white cohesion suit with purple decals intended to assist Prometheans in maintaining their form and prevent direct skin exposure." + icon_state = "cohesionsuit_decal" + +/obj/item/clothing/under/cohesion/pattern + name = "blue patterned cohesion suit" + desc = "A white cohesion suit with blue patterns intended to assist Prometheans in maintaining their form and prevent direct skin exposure." + icon_state = "cohesionsuit_pattern" + +/obj/item/clothing/under/cohesion/hazard + name = "hazard cohesion suit" + desc = "An orange cohesion suit with yellow hazard stripes intended to assist Prometheans in maintaining their form and prevent direct skin exposure." + icon_state = "cohesionsuit_hazard" + +/obj/item/clothing/under/vampirehunter + name = "18th century outfit" + desc = "A flashy, if rather stiff set of ancient-styled slacks and tabard. The unyielding nature of the clothes often make one walk stiffly, but with divine purpose." + icon_state = "belmont" + +//Ranger uniforms +//On-mob sprites go in icons\mob\uniform.dmi with the format "white_ranger_uniform_s" - with 'white' replaced with green, cyan, etc... of course! Note the _s - this is not optional. +//Item sprites go in icons\obj\clothing\ranger.dmi with the format "white_ranger_uniform" +/obj/item/clothing/under/color/ranger + var/unicolor = "white" + name = "ranger uniform" + desc = "Made from a space-proof fibre and tight fitting, this uniform usually gives the agile Rangers all kinds of protection while not inhibiting their movement. \ + This costume is instead made from genuine cotton fibre and is based on the season three uniform." + icon = 'icons/obj/clothing/ranger.dmi' + icon_state = "white_ranger_uniform" + rolled_down = 0 + rolled_sleeves = 0 + +/obj/item/clothing/under/color/ranger/Initialize() + . = ..() + if(icon_state == "ranger_uniform") //allows for custom items + name = "[unicolor] ranger uniform" + icon_state = "[unicolor]_ranger_uniform" + +/obj/item/clothing/under/color/ranger/black + unicolor = "black" + +/obj/item/clothing/under/color/ranger/pink + unicolor = "pink" + +/obj/item/clothing/under/color/ranger/green + unicolor = "green" + +/obj/item/clothing/under/color/ranger/cyan + unicolor = "cyan" + +/obj/item/clothing/under/color/ranger/orange + unicolor = "orange" + +/obj/item/clothing/under/color/ranger/yellow + unicolor = "yellow" + +/obj/item/clothing/under/boater + name = "boater outfit" + desc = "A classic outfit for those with a nautical inclination." + icon_state = "boater" + worn_state = "boater" + +/obj/item/clothing/under/tourist_1 + name = "summer outfit" + desc = "The perfect outfit to wear out of town." + icon_state = "tourist_1" + worn_state = "tourist_1" + +/obj/item/clothing/under/tourist_2 + name = "summer outfit" + desc = "The perfect outfit to wear out of town." + icon_state = "tourist_2" + worn_state = "tourist_2" + +/obj/item/clothing/under/relaxwear_1 + name = "casual outfit" + desc = "Something casual to wear out on the town. Pairs well with the holiday season." + icon_state = "relaxwear_1" + worn_state = "relaxwear_1" + starting_accessories = list(/obj/item/clothing/accessory/wcoat/swvest/red) + +/obj/item/clothing/under/relaxwear_2 + name = "relaxing outfit" + desc = "A comfy looking set of clothes to relax in, even if the style is a bit dated." + icon_state = "relaxwear_2" + worn_state = "relaxwear_2" + starting_accessories = list(/obj/item/clothing/accessory/wcoat/swvest/green) + +/obj/item/clothing/under/serviceoveralls + name = "workman outfit" + desc = "The very image of a working man. Not that you're probably doing work." + icon_state = "mechanic" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + rolled_sleeves = 0 + +/obj/item/clothing/under/frontier + name = "frontier clothes" + desc = "A rugged flannel shirt and denim overalls. A popular style among frontier colonists." + icon_state = "frontier" + worn_state = "frontier" + +/obj/item/clothing/under/rustler + name = "rustler outfit" + desc = "A rugged outfit for rustling cattle out on the frontier." + icon_state = "rustler" + worn_state = "rustler" + +/obj/item/clothing/under/cowboy + name = "cowboy clothes" + desc = "Some rugged clothes for hard labor out on the farm." + icon_state = "cowboy" + worn_state = "cowboy" + +/obj/item/clothing/under/cowboy/tan + name = "tan cowboy clothes" + icon_state = "cowboy_tan" + worn_state = "cowboy_tan" + +/obj/item/clothing/under/cowboy/brown + name = "brown cowboy clothes" + icon_state = "cowboy_brown" + worn_state = "cowboy_brown" + +/obj/item/clothing/under/cowboy/grey + name = "grey cowboy clothes" + icon_state = "cowboy_grey" + worn_state = "cowboy_grey" + +/obj/item/clothing/under/curator + name = "curator uniform" + desc = "A rugged uniform suitable for treasure hunting." + icon_state = "curator" + worn_state = "curator" + +/* + * 80s + */ + +/obj/item/clothing/under/tropical + name = "black tropical oufit" + desc = "A classic themed outfit. This one makes you feel like an animal." + icon_state = "animalstyle" + worn_state = "animalstyle" + starting_accessories = list(/obj/item/clothing/accessory/tropical) + +/obj/item/clothing/under/tropical/green + name = "puke-green tropical oufit" + desc = "A classic themed outfit. This one makes you look like puke." + icon_state = "tropicopuke" + worn_state = "tropicopuke" + starting_accessories = list(/obj/item/clothing/accessory/tropical/green) + +/obj/item/clothing/under/tropical/pink + name = "pink tropical oufit" + desc = "A classic themed outfit. This one makes you feel nostalgic." + icon_state = "3005vintage" + worn_state = "3005vintage" + starting_accessories = list(/obj/item/clothing/accessory/tropical/pink) + +/obj/item/clothing/under/tropical/blue + name = "pink tropical oufit" + desc = "A classic themed outfit. This one makes you feel out of touch." + icon_state = "miamivice" + worn_state = "miamivice" + starting_accessories = list(/obj/item/clothing/accessory/tropical/blue) + +/* + * Modern + */ + +/obj/item/clothing/under/moderncoat + name = "modern wrapped coat (pants)" + desc = "The cutting edge of fashion." + icon_state = "mod_coat_pants" + worn_state = "mod_coat_pants" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS + starting_accessories = list(/obj/item/clothing/accessory/jacket/modwrap) + +/obj/item/clothing/under/modjump + name = "modern jumpsuit" + desc = "A stylish jumpsuit of modern culture." + icon_state = "mod_jump" + worn_state = "mod_jump" + item_state_slots = list(slot_r_hand_str = "orange", slot_l_hand_str = "orange") + +/obj/item/clothing/under/modjump2 + name = "modern outfit" + desc = "A stylish outfit of modern culture." + icon_state = "mod_jump2" + worn_state = "mod_jump2" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + +/obj/item/clothing/under/modjump3 + name = "cyber jumpsuit" + desc = "A cool cyberpunk styled jumpsuit. The lights on the back glitched out and don't work." + icon_state = "cyber" + worn_state = "cyber" + item_state_slots = list(slot_r_hand_str = "grey", slot_l_hand_str = "grey") + +/* + * Corporate Uniforms + */ + +/obj/item/clothing/under/mbill + name = "\improper Major Bill's uniform" + desc = "A uniform belonging to Major Bill's Transportation, a shipping megacorporation." + icon_state = "mbill" + worn_state = "mbill" + catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) + +/obj/item/clothing/under/mbill_flight + name = "\improper Major Bill's flightsuit" + desc = "A flightsuit belonging to Major Bill's Transportation, a shipping megacorporation." + icon_state = "mbill_flight" + worn_state = "mbill_flight" + catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills) + starting_accessories = list(/obj/item/clothing/accessory/storage/webbing/pilot1) + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/aether + name = "\improper Aether jumpsuit" + desc = "A jumpsuit belonging to Aether Atmospherics and Recycling, a Trans-Stellar that supplies recycling and atmospheric systems to colonies." + icon_state = "aether" + worn_state = "aether" + +/obj/item/clothing/under/corp/pcrc + name = "\improper PCRC uniform" + desc = "A uniform belonging to Proxima Centauri Risk Control, a private security firm." + icon_state = "pcrc" + item_state = "jensensuit" + worn_state = "pcrc" + +/obj/item/clothing/under/corp/centauri + name = "\improper Centauri Provisions jumpsuit" + desc = "A jumpsuit belonging to Centauri Provisions, a Trans-Stellar best known for its food and drink products." + icon_state = "centauri" + worn_state = "centauri" + +/obj/item/clothing/under/corp/grayson + name = "\improper Grayson overalls" + desc = "A set of overalls belonging to Grayson Manufactories, a mining Trans-Stellar." + icon_state = "grayson_mech" + worn_state = "grayson_mech" + +/obj/item/clothing/under/corp/grayson_jump + name = "\improper Grayson jumpsuit" + desc = "A jumpsuit belonging to Grayson Manufactories, a mining Trans-Stellar." + icon_state = "grayson" + worn_state = "grayson" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/wardt + name = "\improper Ward-Takahashi jumpsuit" + desc = "A jumpsuit belonging to Ward-Takahashi, a Trans-Stellar in the consumer goods market." + icon_state = "robotics2" + worn_state = "robotics2" + +/obj/item/clothing/under/corp/confederacy + name = "\improper Confederacy uniform" + desc = "A military uniform belonging to the Confederacy of Man, an independent human government." + icon_state = "confed" + worn_state = "confed" + +/obj/item/clothing/under/corp/saare + name = "\improper SAARE uniform" + desc = "A dress uniform belonging to Stealth Assault Enterprises, a minor private military corporation." + icon_state = "saare" + worn_state = "saare" + +/obj/item/clothing/under/corp/focal + name = "\improper Focal Point jumpsuit" + desc = "A jumpsuit belonging to Focal Point Energistics, an engineering megacorporation." + icon_state = "focal" + worn_state = "focal" + +/obj/item/clothing/under/corp/wulf + name = "\improper Wulf jumpsuit" + desc = "A jumpsuit belonging to Wulf Aeronautics, a ship-building and propulsion systems Trans-Stellar." + icon_state = "wulf" + worn_state = "wulf" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/hephaestus + name = "\improper Hephaestus jumpsuit" + desc = "A jumpsuit belonging to Hephaestus, a Trans-Stellar best known for high-grade arms manufacturing." + icon_state = "heph" + worn_state = "heph" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/kaleidoscope + name = "\improper Kaleidoscope uniform" + desc = "A science uniform belonging to Kaleidoscope Cosmetics, a cosmetic and gene-modification trans-stellar." + icon_state = "kaleido" + worn_state = "kaleido" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/veymed + name = "\improper Vey-Med uniform" + desc = "A uniform belonging to Vey-Medical, a Skrellian biomedical Trans-Stellar." + icon_state = "veymed" + worn_state = "veymed" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/wardt + name = "\improper Ward-Takahashi jumpsuit" + desc = "A jumpsuit belonging to Ward-Takahashi, a Trans-Stellar in the consumer goods market." + icon_state = "robotics2" + worn_state = "robotics2" + +/obj/item/clothing/under/corp/xion + name = "\improper Xion jumpsuit" + desc = "A jumpsuit belonging to Xion Manufacturing, an industrial equipment Trans-Stellar." + icon_state = "xion" + worn_state = "xion" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/zenghu + name = "\improper Zeng-Hu jumpsuit" + desc = "A jumpsuit belonging to Zeng-Hu Pharmaceuticals, a Trans-Stellar in the business of exactly what you'd expect.." + icon_state = "zenghu" + worn_state = "zenghu" + starting_accessories = list(/obj/item/clothing/accessory/tie/red) + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/morpheus + name = "\improper Morpheus jumpsuit" + desc = "A uniform belonging to Morpheus Cyberkinetics, a positronic-run cybernetics Trans-Stellar." + icon_state = "morpheus" + worn_state = "morpheus" + +/obj/item/clothing/under/corp/xion + name = "\improper Xion jumpsuit" + desc = "A jumpsuit belonging to Xion Manufacturing, an industrial equipment Trans-Stellar." + icon_state = "xion" + worn_state = "xion" + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/hedberg + name = "\improper Hedberg law enforcement uniform" + desc = "A sturdy civilian law enforcement uniform belonging to the Hedberg-Hammarstrom private security corporation." + icon_state = "hedberg" + worn_state = "hedberg" + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //Equivalent to security officer's jumpsuit + rolled_sleeves = 0 + +/obj/item/clothing/under/corp/hedbergtech + name = "\improper Hedberg technician uniform" + desc = "A technician's uniform belonging to the Hedberg-Hammarstrom private security corporation. It is lightly shielded against radiation." + icon_state = "hedberg_tech" + worn_state = "hedberg_tech" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) // Equivalent to engineer's jumpsuit. + rolled_sleeves = 0 + +//christmas stuff +/obj/item/clothing/under/christmas/red + name = "red christmas suit" + desc = "A simple red christmas suit that looks close to Santa's!" + icon_state = "christmasred" + +/obj/item/clothing/under/christmas/green + name = "green christmas suit" + desc = "A simple green christmas suit. Smells minty!" + icon_state = "christmasgreen" + +/obj/item/clothing/under/christmas/croptop/red + name = "red crop-top christmas suit" + desc = "A simple red christmas suit that doesn't quite looks like Mrs Claus'." + icon_state = "christmascroppedred" + +/obj/item/clothing/under/christmas/croptop/green + name = "green crop-top christmas suit" + desc = "A simple green christmas suit that doesn't quite looks like Mrs Claus'. Smells minty!" + icon_state = "christmascroppedgreen" + +//leotards +/obj/item/clothing/under/leotard + name = "black leotard" + desc = "A black leotard with a piece of semi-transparent cloth near the bust. Perfect for showing off cleavage. Bunny ears not included." + icon_state = "leotard" + +/obj/item/clothing/under/leotardcolor + name = "colored leotard" + desc = "A colorable leotard with a piece of semi-transparent cloth near the bust. Perfect for showing off cleavage. Bunny ears not included." + icon_state = "leotard_color" + +//skinsuits +/obj/item/clothing/under/skinsuit + name = "skinsuit" + desc = "Similar to other form-fitting latex bodysuits in design and function, skinsuits typically feature integrated hardpoints around common wear areas." + icon_state = "skinsuit" + +/obj/item/clothing/under/skinsuit/gray + name = "gray skinsuit" + icon_state = "skinsuit_g" + +/obj/item/clothing/under/skinsuit/leotard + name = "leotard skinsuit" + desc = "The skinsuit's leotard variant has long since eclipsed its initial function as a breathable undersuit for submersible hardsuits. Although still utilized in this role, it has become rather fashionable to wear outside of deep water operations." + icon_state = "skinsuitleo" + + +/obj/item/clothing/under/skinsuit/leotard/gray + name = "gray leotard skinsuit" + icon_state = "skinsuitleo_g" + +/obj/item/clothing/under/skinsuit/fem + name = "feminine skinsuit" + desc = "Similar to other form-fitting latex bodysuits in design and function, skinsuits typically feature integrated hardpoints around common wear areas." + icon_state = "skinsuitfem" + +/obj/item/clothing/under/skinsuit/fem/gray + name = "feminine gray skinsuit" + icon_state = "skinsuitfem_g" + +/obj/item/clothing/under/skinsuit/fem/leotard + name = "feminine leotard skinsuit" + desc = "The skinsuit's leotard variant has long since eclipsed its initial function as a breathable undersuit for submersible hardsuits. Although still utilized in this role, it has become rather fashionable to wear outside of deep water operations." + icon_state = "skinsuitfemleo" + +/obj/item/clothing/under/skinsuit/fem/leotard/gray + name = "feminine gray leotard skinsuit" + icon_state = "skinsuitfemleo_g" + +//baggy turtlenecks +/obj/item/clothing/under/turtlebaggy + name = "cream baggy turtleneck (cream)" + desc = "A cozy knit turtleneck. It's too baggy and comfortable to be tactical." + icon_state = "bb_turtle" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + + +/obj/item/clothing/under/turtlebaggy/cream_fem + name = "feminine cream baggy turtleneck" + icon_state = "bb_turtle_fem" + + +/obj/item/clothing/under/turtlebaggy/purple + name = "purple baggy turtleneck" + icon_state = "bb_turtlepur" + + +/obj/item/clothing/under/turtlebaggy/purple_fem + name = "feminine purple baggy turtleneck" + icon_state = "bb_turtlepur_fem" + + +/obj/item/clothing/under/turtlebaggy/red + name = "red baggy turtleneck" + icon_state = "bb_turtlered" + + +/obj/item/clothing/under/turtlebaggy/red_fem + name = "feminine red baggy turtleneck" + icon_state = "bb_turtlered_fem" + + +/obj/item/clothing/under/turtlebaggy/blue + name = "blue baggy turtleneck" + icon_state = "bb_turtleblu" + + +/obj/item/clothing/under/turtlebaggy/blue_fem + name = "feminine blue baggy turtleneck" + icon_state = "bb_turtleblu_fem" + + +/obj/item/clothing/under/turtlebaggy/green + name = "green baggy turtleneck" + icon_state = "bb_turtlegrn" + + +/obj/item/clothing/under/turtlebaggy/green_fem + name = "feminine green baggy turtleneck" + icon_state = "bb_turtlegrn_fem" + + +/obj/item/clothing/under/turtlebaggy/black + name = "black baggy turtleneck" + icon_state = "bb_turtleblk" + + +/obj/item/clothing/under/turtlebaggy/black_fem + name = "feminine black baggy turtleneck" + icon_state = "bb_turtleblk_fem" + +//half-moon outfit +/obj/item/clothing/under/half_moon + name = "half moon outfit" + desc = "This eminently fashionable outfit consists of a tailored latex leotard and daringly cut white shorts. Paired with plunging off-color stockings, it's to die for." + icon_state = "half_moon" + +//fiend clothes +/obj/item/clothing/under/fiendsuit + name = "fiendish suit" + desc = "A red and black suit befitting someone from the dark pits themselves… Or someone way too edgy." + icon_state = "fiendsuit" + + +/obj/item/clothing/under/fienddress + name = "fiendish dress" + desc = "A red and black dress befitting someone from the dark pits themselves… Or someone way too edgy." + icon_state = "fienddress" diff --git a/code/modules/clothing/under/miscellaneous_vr.dm b/code/modules/clothing/under/miscellaneous_vr.dm index 9d4633bcf86..a43a2676a72 100644 --- a/code/modules/clothing/under/miscellaneous_vr.dm +++ b/code/modules/clothing/under/miscellaneous_vr.dm @@ -616,3 +616,115 @@ desc = "A fancy gown for those who like to show leg. Perfect for recoloring!" default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' icon_state = "cswoopdress" + +//Replikant uniforms + +/obj/item/clothing/under/replika + name = "generic" + desc = "generic" + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon = 'icons/inventory/uniform/item_vr.dmi' + default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' + icon_state = "arar" + item_state = "arar" + rolled_sleeves = -1 + rolled_down = -1 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/replika/arar + name = "repair-worker replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the engineering variety. Comes with multiple interfacing ports, arm protectors, and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "arar" + item_state = "arar" + + +/obj/item/clothing/under/replika/lstr + name = "land-survey replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the exploration variety. Comes with several interfacing ports and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "lstr" + item_state = "lstr" + +/obj/item/clothing/under/replika/fklr + name = "command replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the command variety. Comes with interfacing ports, an air of formality, and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "fklr" + item_state = "fklr" + +/obj/item/clothing/under/replika/eulr + name = "general-purpose replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of multipurpose variety. Comes with default interfacing ports and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "eulr" + item_state = "eulr" + +/obj/item/clothing/under/replika/klbr + name = "controller replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the controller variety. Comes with several interfacing ports and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "klbr" + item_state = "klbr" + +/obj/item/clothing/under/replika/stcr + name = "security-technician replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the security variety. Comes with multiple interfacing ports and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "stcr" + item_state = "stcr" + +/obj/item/clothing/under/replika/adlr + name = "administration replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the administrative variety. Comes with several interfacing ports and a conspicuous lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "adlr" + item_state = "adlr" + +/obj/item/clothing/under/replika/lstr_alt + name = "combat-engineer replikant bodysuit" + desc = "A skin-tight bodysuit designed for 2nd generation biosynthetics of the exploration variety. Comes with extra interfacing ports, white armpads, and a familiar lack of leg coverage." + description_fluff = "These purpose-made interfacing bodysuits are designed and produced by the Singheim Bureau of Biosynthetic Development for their long-running second generation of Biosynthetics, commonly known by the term Replikant. Although anyone could wear these, their overall cut and metallic ports along the spine make it rather uncomfortable to most." + icon_state = "lstr_alt" + item_state = "lstr_alt" + +//Signalis-themed human-wear + +/obj/item/clothing/under/gestalt + name = "generic" + desc = "generic" + icon = 'icons/inventory/uniform/item_vr.dmi' + default_worn_icon = 'icons/inventory/uniform/mob_vr.dmi' + icon_state = "gestalt_skirt" + item_state = "gestalt_skirt" + rolled_sleeves = -1 + rolled_down = -1 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + +/obj/item/clothing/under/gestalt/sleek_skirt + name = "sleek crew skirt" + desc = "A tight-fitting black uniform with a narrow skirt and striking crimson trim." + icon_state = "gestalt_skirt" + item_state = "gestalt_skirt" + + +/obj/item/clothing/under/gestalt/sleek + name = "sleek crew uniform" + desc = "A tight-fitting black uniform with striking crimson trim." + icon_state = "gestalt" + item_state = "gestalt" + + +/obj/item/clothing/under/gestalt/sleek_fem + name = "sleek female crew uniform" + desc = "A tight-fitting black uniform with striking crimson trim." + icon_state = "gestalt_fem" + item_state = "gestalt_fem" + + +/obj/item/clothing/under/gestalt/sleeveless + name = "sleeveless sleek crew uniform" + desc = "A tight-fitting, sleeveless single-piece black uniform with striking crimson trim." + icon_state = "gestalt_sleeveless" + item_state = "gestalt_sleeveless" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index d168ddc1eb9..b2289e70eba 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -1,243 +1,247 @@ -//these need item states -S2- -/obj/item/clothing/under/shorts - name = "athletic shorts" - desc = "95% Polyester, 5% Spandex!" - icon_state = "redshorts" // Hackyfix for icon states until someone wants to come do a recolor later. - gender = PLURAL - body_parts_covered = LOWER_TORSO - -/obj/item/clothing/under/shorts/red - name = "red athletic shorts" - icon_state = "redshorts" - -/obj/item/clothing/under/shorts/green - name = "green athletic shorts" - icon_state = "greenshorts" - -/obj/item/clothing/under/shorts/blue - name = "blue athletic shorts" - icon_state = "blueshorts" - -/obj/item/clothing/under/shorts/black - name = "black athletic shorts" - icon_state = "blackshorts" - -/obj/item/clothing/under/shorts/grey - name = "grey athletic shorts" - icon_state = "greyshorts" - -/obj/item/clothing/under/shorts/white - name = "white shorts" - icon_state = "whiteshorts" - -/obj/item/clothing/under/shorts/white/female - name = "white short shorts" - icon_state = "whiteshorts_f" - -/obj/item/clothing/under/shorts/jeans - name = "jeans shorts" - desc = "Some jeans! Just in short form!" - icon_state = "jeans_shorts" - -/obj/item/clothing/under/shorts/jeans/female - name = "jeans short shorts" - icon_state = "jeans_shorts_f" - -/obj/item/clothing/under/shorts/jeans/classic - name = "classic jeans shorts" - icon_state = "jeansclassic_shorts" - -/obj/item/clothing/under/shorts/jeans/classic/female - name = "classic jeans short shorts" - icon_state = "jeansclassic_shorts_f" - -/obj/item/clothing/under/shorts/jeans/mustang - name = "mustang jeans shorts" - icon_state = "jeansmustang_shorts" - -/obj/item/clothing/under/shorts/jeans/mustang/female - name = "mustang jeans short shorts" - icon_state = "jeansmustang_shorts_f" - -/obj/item/clothing/under/shorts/jeans/youngfolks - name = "young folks jeans shorts" - icon_state = "jeansyoungfolks_shorts" - -/obj/item/clothing/under/shorts/jeans/youngfolks/female - name = "young folks jeans short shorts" - icon_state = "jeansyoungfolks_shorts_f" - -/obj/item/clothing/under/shorts/jeans/black - name = "black jeans shorts" - icon_state = "blackpants_shorts" - -/obj/item/clothing/under/shorts/jeans/black/female - name = "black jeans short shorts" - icon_state = "black_shorts_f" - -/obj/item/clothing/under/shorts/jeans/grey - name = "grey jeans shorts" - icon_state = "greyshorts" - -/obj/item/clothing/under/shorts/jeans/grey/female - name = "grey jeans short shorts" - icon_state = "grey_shorts_f" - -/obj/item/clothing/under/shorts/khaki - name = "khaki shorts" - desc = "For that island getaway. It's five o'clock somewhere, right?" - icon_state = "tanpants_shorts" - -/obj/item/clothing/under/shorts/khaki/female - name = "khaki short shorts" - icon_state = "khaki_shorts_f" - -//Argh, skirts be below this line -> ------------------------------ - -/obj/item/clothing/under/skirt - name = "short black skirt" - desc = "A skirt that is a shiny black." - icon_state = "skirt_short_black" - body_parts_covered = LOWER_TORSO - rolled_sleeves = -1 - -/obj/item/clothing/under/skirt/khaki - name = "khaki skirt" - desc = "A skirt that is a khaki color." - icon_state = "skirt_khaki" - -/obj/item/clothing/under/skirt/blue - name = "short blue skirt" - desc = "A skirt that is a shiny blue." - icon_state = "skirt_short_blue" - -/obj/item/clothing/under/skirt/red - name = "short red skirt" - desc = "A skirt that is a shiny red." - icon_state = "skirt_short_red" - -/obj/item/clothing/under/skirt/denim - name = "short denim skirt" - desc = "A skirt that is made of denim." - icon_state = "skirt_short_denim" - -/obj/item/clothing/under/skirt/swept - name = "swept skirt" - desc = "A skirt that is swept to one side." - icon_state = "skirt_swept" - -/obj/item/clothing/under/skirt/loincloth - name = "loincloth" - desc = "A piece of cloth wrapped around the waist." - icon_state = "loincloth" - -/obj/item/clothing/under/skirt/pleated - name = "pleated skirt" - desc = "A simple pleated skirt. It's like high school all over again." - icon_state = "pleated" - addblends = "pleated_a" - -/obj/item/clothing/under/skirt/outfit - name = "black skirt" - desc = "A black skirt, very fancy!" - icon_state = "blackskirt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/skirt/outfit/plaid_blue - name = "blue plaid skirt" - desc = "A preppy blue skirt with a white blouse." - icon_state = "plaid_blue" - item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") - -/obj/item/clothing/under/skirt/outfit/plaid_red - name = "red plaid skirt" - desc = "A preppy red skirt with a white blouse." - icon_state = "plaid_red" - item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") - -/obj/item/clothing/under/skirt/outfit/plaid_purple - name = "blue purple skirt" - desc = "A preppy purple skirt with a white blouse." - icon_state = "plaid_purple" - item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") - -/obj/item/clothing/under/rank/cargo/skirt - name = "quartermaster's jumpskirt" - desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qmf" - item_state_slots = list(slot_r_hand_str = "qm", slot_l_hand_str = "qm") - -/obj/item/clothing/under/rank/cargotech/skirt - name = "cargo technician's jumpskirt" - desc = "Skirrrrrts! They're comfy and easy to wear!" - icon_state = "cargof" - item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") - -/obj/item/clothing/under/rank/engineer/skirt - desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." - name = "engineer's jumpskirt" - icon_state = "enginef" - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) - item_state_slots = list(slot_r_hand_str = "engine", slot_l_hand_str = "engine") - -/obj/item/clothing/under/rank/chief_engineer/skirt - desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpskirt" - icon_state = "chieff" - item_state_slots = list(slot_r_hand_str = "chiefengineer", slot_l_hand_str = "chiefengineer") - -/obj/item/clothing/under/rank/atmospheric_technician/skirt - desc = "It's a jumpskirt worn by atmospheric technicians." - name = "atmospheric technician's jumpskirt" - icon_state = "atmosf" - item_state_slots = list(slot_r_hand_str = "atmos", slot_l_hand_str = "atmos") - -/obj/item/clothing/under/rank/roboticist/skirt - desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." - name = "roboticist's jumpskirt" - icon_state = "roboticsf" - item_state_slots = list(slot_r_hand_str = "robotics", slot_l_hand_str = "robotics") - -/obj/item/clothing/under/rank/scientist/skirt - name = "scientist's jumpskirt" - icon_state = "sciencef" - permeability_coefficient = 0.50 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) - -/obj/item/clothing/under/rank/medical/skirt - name = "medical doctor's jumpskirt" - icon_state = "medicalf" - -/obj/item/clothing/under/rank/chemist/skirt - name = "chemist's jumpskirt" - icon_state = "chemistryf" - -/obj/item/clothing/under/rank/chief_medical_officer/skirt - desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpskirt" - icon_state = "cmof" - -/obj/item/clothing/under/rank/geneticist/skirt - name = "geneticist's jumpskirt" - icon_state = "geneticsf" - -/obj/item/clothing/under/rank/virologist/skirt - name = "virologist's jumpskirt" - icon_state = "virologyf" - -/obj/item/clothing/under/rank/security/skirt - name = "security officer's jumpskirt" - desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." - icon_state = "securityf" - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/under/rank/warden/skirt - desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpskirt" - icon_state = "wardenf" - -/obj/item/clothing/under/rank/head_of_security/skirt - desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpskirt" - icon_state = "hosf" +//these need item states -S2- +/obj/item/clothing/under/shorts + name = "athletic shorts" + desc = "95% Polyester, 5% Spandex!" + icon_state = "redshorts" // Hackyfix for icon states until someone wants to come do a recolor later. + gender = PLURAL + body_parts_covered = LOWER_TORSO + +/obj/item/clothing/under/shorts/red + name = "red athletic shorts" + icon_state = "redshorts" + +/obj/item/clothing/under/shorts/green + name = "green athletic shorts" + icon_state = "greenshorts" + +/obj/item/clothing/under/shorts/blue + name = "blue athletic shorts" + icon_state = "blueshorts" + +/obj/item/clothing/under/shorts/black + name = "black athletic shorts" + icon_state = "blackshorts" + +/obj/item/clothing/under/shorts/grey + name = "grey athletic shorts" + icon_state = "greyshorts" + +/obj/item/clothing/under/shorts/white + name = "white shorts" + icon_state = "whiteshorts" + +/obj/item/clothing/under/shorts/white/female + name = "white short shorts" + icon_state = "whiteshorts_f" + +/obj/item/clothing/under/shorts/jeans + name = "jeans shorts" + desc = "Some jeans! Just in short form!" + icon_state = "jeans_shorts" + +/obj/item/clothing/under/shorts/jeans/female + name = "jeans short shorts" + icon_state = "jeans_shorts_f" + +/obj/item/clothing/under/shorts/jeans/classic + name = "classic jeans shorts" + icon_state = "jeansclassic_shorts" + +/obj/item/clothing/under/shorts/jeans/classic/female + name = "classic jeans short shorts" + icon_state = "jeansclassic_shorts_f" + +/obj/item/clothing/under/shorts/jeans/mustang + name = "mustang jeans shorts" + icon_state = "jeansmustang_shorts" + +/obj/item/clothing/under/shorts/jeans/mustang/female + name = "mustang jeans short shorts" + icon_state = "jeansmustang_shorts_f" + +/obj/item/clothing/under/shorts/jeans/youngfolks + name = "young folks jeans shorts" + icon_state = "jeansyoungfolks_shorts" + +/obj/item/clothing/under/shorts/jeans/youngfolks/female + name = "young folks jeans short shorts" + icon_state = "jeansyoungfolks_shorts_f" + +/obj/item/clothing/under/shorts/jeans/black + name = "black jeans shorts" + icon_state = "blackpants_shorts" + +/obj/item/clothing/under/shorts/jeans/black/female + name = "black jeans short shorts" + icon_state = "black_shorts_f" + +/obj/item/clothing/under/shorts/black/ripped + name = "black ripped shorts" + icon_state = "black_shorts_ripped" + +/obj/item/clothing/under/shorts/jeans/grey + name = "grey jeans shorts" + icon_state = "greyshorts" + +/obj/item/clothing/under/shorts/jeans/grey/female + name = "grey jeans short shorts" + icon_state = "grey_shorts_f" + +/obj/item/clothing/under/shorts/khaki + name = "khaki shorts" + desc = "For that island getaway. It's five o'clock somewhere, right?" + icon_state = "tanpants_shorts" + +/obj/item/clothing/under/shorts/khaki/female + name = "khaki short shorts" + icon_state = "khaki_shorts_f" + +//Argh, skirts be below this line -> ------------------------------ + +/obj/item/clothing/under/skirt + name = "short black skirt" + desc = "A skirt that is a shiny black." + icon_state = "skirt_short_black" + body_parts_covered = LOWER_TORSO + rolled_sleeves = -1 + +/obj/item/clothing/under/skirt/khaki + name = "khaki skirt" + desc = "A skirt that is a khaki color." + icon_state = "skirt_khaki" + +/obj/item/clothing/under/skirt/blue + name = "short blue skirt" + desc = "A skirt that is a shiny blue." + icon_state = "skirt_short_blue" + +/obj/item/clothing/under/skirt/red + name = "short red skirt" + desc = "A skirt that is a shiny red." + icon_state = "skirt_short_red" + +/obj/item/clothing/under/skirt/denim + name = "short denim skirt" + desc = "A skirt that is made of denim." + icon_state = "skirt_short_denim" + +/obj/item/clothing/under/skirt/swept + name = "swept skirt" + desc = "A skirt that is swept to one side." + icon_state = "skirt_swept" + +/obj/item/clothing/under/skirt/loincloth + name = "loincloth" + desc = "A piece of cloth wrapped around the waist." + icon_state = "loincloth" + +/obj/item/clothing/under/skirt/pleated + name = "pleated skirt" + desc = "A simple pleated skirt. It's like high school all over again." + icon_state = "pleated" + addblends = "pleated_a" + +/obj/item/clothing/under/skirt/outfit + name = "black skirt" + desc = "A black skirt, very fancy!" + icon_state = "blackskirt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/skirt/outfit/plaid_blue + name = "blue plaid skirt" + desc = "A preppy blue skirt with a white blouse." + icon_state = "plaid_blue" + item_state_slots = list(slot_r_hand_str = "blue", slot_l_hand_str = "blue") + +/obj/item/clothing/under/skirt/outfit/plaid_red + name = "red plaid skirt" + desc = "A preppy red skirt with a white blouse." + icon_state = "plaid_red" + item_state_slots = list(slot_r_hand_str = "red", slot_l_hand_str = "red") + +/obj/item/clothing/under/skirt/outfit/plaid_purple + name = "blue purple skirt" + desc = "A preppy purple skirt with a white blouse." + icon_state = "plaid_purple" + item_state_slots = list(slot_r_hand_str = "purple", slot_l_hand_str = "purple") + +/obj/item/clothing/under/rank/cargo/skirt + name = "quartermaster's jumpskirt" + desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qmf" + item_state_slots = list(slot_r_hand_str = "qm", slot_l_hand_str = "qm") + +/obj/item/clothing/under/rank/cargotech/skirt + name = "cargo technician's jumpskirt" + desc = "Skirrrrrts! They're comfy and easy to wear!" + icon_state = "cargof" + item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + +/obj/item/clothing/under/rank/engineer/skirt + desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." + name = "engineer's jumpskirt" + icon_state = "enginef" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) + item_state_slots = list(slot_r_hand_str = "engine", slot_l_hand_str = "engine") + +/obj/item/clothing/under/rank/chief_engineer/skirt + desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpskirt" + icon_state = "chieff" + item_state_slots = list(slot_r_hand_str = "chiefengineer", slot_l_hand_str = "chiefengineer") + +/obj/item/clothing/under/rank/atmospheric_technician/skirt + desc = "It's a jumpskirt worn by atmospheric technicians." + name = "atmospheric technician's jumpskirt" + icon_state = "atmosf" + item_state_slots = list(slot_r_hand_str = "atmos", slot_l_hand_str = "atmos") + +/obj/item/clothing/under/rank/roboticist/skirt + desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." + name = "roboticist's jumpskirt" + icon_state = "roboticsf" + item_state_slots = list(slot_r_hand_str = "robotics", slot_l_hand_str = "robotics") + +/obj/item/clothing/under/rank/scientist/skirt + name = "scientist's jumpskirt" + icon_state = "sciencef" + permeability_coefficient = 0.50 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + +/obj/item/clothing/under/rank/medical/skirt + name = "medical doctor's jumpskirt" + icon_state = "medicalf" + +/obj/item/clothing/under/rank/chemist/skirt + name = "chemist's jumpskirt" + icon_state = "chemistryf" + +/obj/item/clothing/under/rank/chief_medical_officer/skirt + desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpskirt" + icon_state = "cmof" + +/obj/item/clothing/under/rank/geneticist/skirt + name = "geneticist's jumpskirt" + icon_state = "geneticsf" + +/obj/item/clothing/under/rank/virologist/skirt + name = "virologist's jumpskirt" + icon_state = "virologyf" + +/obj/item/clothing/under/rank/security/skirt + name = "security officer's jumpskirt" + desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." + icon_state = "securityf" + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/under/rank/warden/skirt + desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpskirt" + icon_state = "wardenf" + +/obj/item/clothing/under/rank/head_of_security/skirt + desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpskirt" + icon_state = "hosf" diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 0560d49e300..171a9dcccec 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -1,26 +1,26 @@ -//these need item states -S2- -/obj/item/clothing/under/syndicate //Merc Tactleneck - name = "tactical turtleneck" - desc = "It's some non-descript, slightly suspicious looking, civilian clothing." - icon_state = "syndicate" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - has_sensor = 0 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/under/syndicate/combat //ERT tactleneck - name = "combat turtleneck" - desc = "It's some non-descript, slightly suspicious looking, civilian clothing." - icon_state = "combat" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - has_sensor = 1 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - -/obj/item/clothing/under/syndicate/tacticool - name = "\improper Tacticool turtleneck" - desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." - icon_state = "tactifool" - item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") - siemens_coefficient = 1 - rolled_sleeves = 0 +//these need item states -S2- +/obj/item/clothing/under/syndicate //Merc Tactleneck + name = "tactical turtleneck" + desc = "It's some non-descript, slightly suspicious looking, civilian clothing." + icon_state = "syndicate" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + has_sensor = 0 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/under/syndicate/combat //ERT tactleneck + name = "combat turtleneck" + desc = "It's some non-descript, slightly suspicious looking, civilian clothing." + icon_state = "combat" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + has_sensor = 1 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.9 + +/obj/item/clothing/under/syndicate/tacticool + name = "\improper Tacticool turtleneck" + desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." + icon_state = "tactifool" + item_state_slots = list(slot_r_hand_str = "black", slot_l_hand_str = "black") + siemens_coefficient = 1 + rolled_sleeves = 0 diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index aae9a23515b..5d77b215f33 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -85,7 +85,7 @@ log transactions if(istype(I, /obj/item/weapon/card)) if(emagged > 0) //prevent inserting id into an emagged ATM - to_chat(user, "\icon[src][bicon(src)] CARD READER ERROR. This system has been compromised!") + to_chat(user, span_red("\icon[src][bicon(src)] CARD READER ERROR. This system has been compromised!")) return else if(istype(I,/obj/item/weapon/card/emag)) I.resolve_attackby(src, user) @@ -288,11 +288,11 @@ log transactions T.time = stationtime2text() failed_account.transaction_log.Add(T) else - to_chat(usr, "\icon[src][bicon(src)] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining.") + to_chat(usr, span_red("\icon[src][bicon(src)] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining.")) previous_account_number = tried_account_num playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) else - to_chat(usr, "\icon[src][bicon(src)] incorrect pin/account combination entered.") + to_chat(usr, span_red("\icon[src][bicon(src)] incorrect pin/account combination entered.")) number_incorrect_tries = 0 else playsound(src, 'sound/machines/twobeep.ogg', 50, 1) @@ -308,7 +308,7 @@ log transactions T.time = stationtime2text() authenticated_account.transaction_log.Add(T) - to_chat(usr, "\icon[src][bicon(src)] Access granted. Welcome user '[authenticated_account.owner_name].'") + to_chat(usr, span_blue("\icon[src][bicon(src)] Access granted. Welcome user '[authenticated_account.owner_name].'")) previous_account_number = tried_account_num if("e_withdrawal") @@ -433,7 +433,7 @@ log transactions if(!held_card) //this might happen if the user had the browser window open when somebody emagged it if(emagged > 0) - to_chat(usr, "\icon[src][bicon(src)] The ATM card reader rejected your ID because this machine has been sabotaged!") + to_chat(usr, span_red("\icon[src][bicon(src)] The ATM card reader rejected your ID because this machine has been sabotaged!")) else var/obj/item/I = usr.get_active_hand() if (istype(I, /obj/item/weapon/card/id)) diff --git a/code/modules/economy/cash.dm b/code/modules/economy/cash.dm index 7b2d31a391c..7cf04f4ac5a 100644 --- a/code/modules/economy/cash.dm +++ b/code/modules/economy/cash.dm @@ -80,7 +80,7 @@ return worth /obj/item/weapon/spacecash/attack_self() - var/amount = tgui_input_number(usr, "How many [initial_name]s do you want to take? (0 to [src.worth])", "Take Money", 20) + var/amount = tgui_input_number(usr, "How many [initial_name]s do you want to take? (0 to [src.worth])", "Take Money", 20, src.worth) if(!src || QDELETED(src)) return amount = round(CLAMP(amount, 0, src.worth)) diff --git a/code/modules/economy/cash_register.dm b/code/modules/economy/cash_register.dm index ae649a8f4d8..ae2479d6130 100644 --- a/code/modules/economy/cash_register.dm +++ b/code/modules/economy/cash_register.dm @@ -129,7 +129,7 @@ src.visible_message("\icon[src][bicon(src)][transaction_purpose]: [t_amount] Thaler\s.") if("set_amount") var/item_name = locate(href_list["item"]) - var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount")) + var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount", 0, 20, 0)) n_amount = CLAMP(n_amount, 0, 20) if (!item_list[item_name] || !Adjacent(usr)) return transaction_amount += (n_amount - item_list[item_name]) * price_list[item_name] @@ -190,7 +190,7 @@ scan_cash(SC) else if(istype(O, /obj/item/weapon/card/emag)) return ..() - else if(O.is_wrench()) + else if(O.has_tool_quality(TOOL_WRENCH)) var/obj/item/weapon/tool/wrench/W = O toggle_anchors(W, user) // Not paying: Look up price and add it to transaction_amount diff --git a/code/modules/economy/casinocash.dm b/code/modules/economy/casinocash.dm index e74b89d42b7..79a057b6365 100644 --- a/code/modules/economy/casinocash.dm +++ b/code/modules/economy/casinocash.dm @@ -112,7 +112,7 @@ return worth /obj/item/weapon/spacecasinocash/attack_self() - var/amount = tgui_input_number(usr, "How much credits worth of chips do you want to take? (0 to [src.worth])", "Take chips", 20) + var/amount = tgui_input_number(usr, "How much credits worth of chips do you want to take? (0 to [src.worth])", "Take chips", 20, src.worth) if(!src || QDELETED(src)) return amount = round(CLAMP(amount, 0, src.worth)) @@ -204,4 +204,4 @@ else if(result == 2) comment = "Joker" user.visible_message("[user] has thrown \the [src]. It lands on [comment]! ", \ - "You throw \the [src]. It lands on [comment]! ") \ No newline at end of file + "You throw \the [src]. It lands on [comment]! ") diff --git a/code/modules/economy/coins.dm b/code/modules/economy/coins.dm index 51438cc101b..87056deecda 100644 --- a/code/modules/economy/coins.dm +++ b/code/modules/economy/coins.dm @@ -81,7 +81,7 @@ else to_chat(user, "This cable coil appears to be empty.") return - else if(W.is_wirecutter()) + else if(W.has_tool_quality(TOOL_WIRECUTTER)) if(!string_attached) ..() return diff --git a/code/modules/economy/coins_vr.dm b/code/modules/economy/coins_vr.dm index 38ae0054cf6..6d1ac8351ef 100644 --- a/code/modules/economy/coins_vr.dm +++ b/code/modules/economy/coins_vr.dm @@ -1,57 +1,57 @@ -//Weird coins that I would prefer didn't work with normal vending machines. Might use them to make weird vending machines later. - -/obj/item/weapon/aliencoin - icon = 'icons/obj/aliencoins.dmi' - name = "curious coin" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. " - icon_state = "triangle" - randpixel = 8 - force = 0.5 - throwforce = 0.5 - w_class = ITEMSIZE_TINY - slot_flags = SLOT_EARS - var/sides = 2 - var/value = 1 - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' - -/obj/item/weapon/aliencoin/New() - randpixel_xy() - -/obj/item/weapon/aliencoin/basic - -/obj/item/weapon/aliencoin/gold - name = "curious coin" - icon_state = "triangle-g" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a golden material underneath." - value = 10 - -/obj/item/weapon/aliencoin/silver - name = "curious coin" - icon_state = "triangle-s" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a silver material underneath." - value = 5 - -/obj/item/weapon/aliencoin/phoron - name = "curious coin" - icon_state = "triangle-p" - desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a purple material underneath." - value = 20 - - -/obj/item/weapon/aliencoin/attack_self(mob/user as mob) - var/result = rand(1, sides) - var/comment = "" - if(result == 1) - comment = "tails" - else if(result == 2) - comment = "heads" - user.visible_message("[user] has thrown [src]. It lands on [comment]! ", runemessage = "[src] landed on [comment]") - if(rand(1,20) == 1) - user.visible_message("[user] fumbled the [src]!", runemessage = "fumbles [src]") - user.remove_from_mob(src) - -/obj/item/weapon/aliencoin/examine(var/mob/user) - . = ..() - if(Adjacent(user)) - . += "It has some writing along its edge that seems to be some language that you are not familiar with. The face of the coin is very smooth, with what appears to be some kind of angular logo along the left side, and a couple of lines of the alien text along the opposite side. The reverse side is similarly smooth, the top of it features what appears to be some kind of vortex, surrounded by six stars, three on either side, with further swirls and intricate patterns along the bottom sections of this face. Looking closely, you can see that there is more text hidden among the swirls." +//Weird coins that I would prefer didn't work with normal vending machines. Might use them to make weird vending machines later. + +/obj/item/weapon/aliencoin + icon = 'icons/obj/aliencoins.dmi' + name = "curious coin" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. " + icon_state = "triangle" + randpixel = 8 + force = 0.5 + throwforce = 0.5 + w_class = ITEMSIZE_TINY + slot_flags = SLOT_EARS + var/sides = 2 + var/value = 1 + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' + +/obj/item/weapon/aliencoin/New() + randpixel_xy() + +/obj/item/weapon/aliencoin/basic + +/obj/item/weapon/aliencoin/gold + name = "curious coin" + icon_state = "triangle-g" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a golden material underneath." + value = 10 + +/obj/item/weapon/aliencoin/silver + name = "curious coin" + icon_state = "triangle-s" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a silver material underneath." + value = 5 + +/obj/item/weapon/aliencoin/phoron + name = "curious coin" + icon_state = "triangle-p" + desc = "A curious triangular coin made primarily of some kind of dark, smooth metal. This one's markings appear to reveal a purple material underneath." + value = 20 + + +/obj/item/weapon/aliencoin/attack_self(mob/user as mob) + var/result = rand(1, sides) + var/comment = "" + if(result == 1) + comment = "tails" + else if(result == 2) + comment = "heads" + user.visible_message("[user] has thrown [src]. It lands on [comment]! ", runemessage = "[src] landed on [comment]") + if(rand(1,20) == 1) + user.visible_message("[user] fumbled the [src]!", runemessage = "fumbles [src]") + user.remove_from_mob(src) + +/obj/item/weapon/aliencoin/examine(var/mob/user) + . = ..() + if(Adjacent(user)) + . += "It has some writing along its edge that seems to be some language that you are not familiar with. The face of the coin is very smooth, with what appears to be some kind of angular logo along the left side, and a couple of lines of the alien text along the opposite side. The reverse side is similarly smooth, the top of it features what appears to be some kind of vortex, surrounded by six stars, three on either side, with further swirls and intricate patterns along the bottom sections of this face. Looking closely, you can see that there is more text hidden among the swirls." diff --git a/code/modules/economy/money_bag.dm b/code/modules/economy/money_bag.dm index 3e85d62e460..d9afc36357e 100644 --- a/code/modules/economy/money_bag.dm +++ b/code/modules/economy/money_bag.dm @@ -49,14 +49,14 @@ ..() if (istype(W, /obj/item/weapon/coin)) var/obj/item/weapon/coin/C = W - to_chat(user, "You add the [C.name] into the bag.") + to_chat(user, span_blue("You add the [C.name] into the bag.")) usr.drop_item() contents += C if (istype(W, /obj/item/weapon/moneybag)) var/obj/item/weapon/moneybag/C = W for (var/obj/O in C.contents) contents += O; - to_chat(user, "You empty the [C.name] into the bag.") + to_chat(user, span_blue("You empty the [C.name] into the bag.")) return /obj/item/weapon/moneybag/Topic(href, href_list) @@ -95,4 +95,4 @@ new /obj/item/weapon/coin/silver(src) new /obj/item/weapon/coin/silver(src) new /obj/item/weapon/coin/gold(src) - new /obj/item/weapon/coin/gold(src) \ No newline at end of file + new /obj/item/weapon/coin/gold(src) diff --git a/code/modules/economy/retail_scanner.dm b/code/modules/economy/retail_scanner.dm index f2b4854d116..d2ab5884ae1 100644 --- a/code/modules/economy/retail_scanner.dm +++ b/code/modules/economy/retail_scanner.dm @@ -123,7 +123,7 @@ src.visible_message("\icon[src][bicon(src)][transaction_purpose]: [t_amount] Thaler\s.") if("set_amount") var/item_name = locate(href_list["item"]) - var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount")) + var/n_amount = round(tgui_input_number(usr, "Enter amount", "New amount", 0, 20, 0)) n_amount = CLAMP(n_amount, 0, 20) if (!item_list[item_name] || !Adjacent(usr)) return transaction_amount += (n_amount - item_list[item_name]) * price_list[item_name] diff --git a/code/modules/economy/vending.dm b/code/modules/economy/vending.dm index e17d5c951f7..a11a6768dde 100644 --- a/code/modules/economy/vending.dm +++ b/code/modules/economy/vending.dm @@ -216,7 +216,7 @@ GLOBAL_LIST_EMPTY(vending_products) else to_chat(user, "You cannot refill [src] with [RC].") return - else if(W.is_screwdriver()) + else if(W.has_tool_quality(TOOL_SCREWDRIVER)) panel_open = !panel_open to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.") playsound(src, W.usesound, 50, 1) @@ -228,7 +228,7 @@ GLOBAL_LIST_EMPTY(vending_products) SStgui.update_uis(src) // Speaker switch is on the main UI, not wires UI return - else if(istype(W, /obj/item/device/multitool) || W.is_wirecutter()) + else if(istype(W, /obj/item/device/multitool) || W.has_tool_quality(TOOL_WIRECUTTER)) if(panel_open) attack_hand(user) return @@ -240,7 +240,7 @@ GLOBAL_LIST_EMPTY(vending_products) to_chat(user, "You insert \the [W] into \the [src].") SStgui.update_uis(src) return - else if(W.is_wrench()) + else if(W.has_tool_quality(TOOL_WRENCH)) playsound(src, W.usesound, 100, 1) if(anchored) user.visible_message("[user] begins unsecuring \the [src] from the floor.", "You start unsecuring \the [src] from the floor.") @@ -737,7 +737,7 @@ GLOBAL_LIST_EMPTY(vending_products) return for(var/mob/O in hearers(src, null)) - O.show_message("\The [src] beeps, \"[message]\"",2) + O.show_message("\The [src] beeps, \"[message]\"",2) return /obj/machinery/vending/power_change() diff --git a/code/modules/economy/vending_machines.dm b/code/modules/economy/vending_machines.dm index c3900b36e83..a03afca62e3 100644 --- a/code/modules/economy/vending_machines.dm +++ b/code/modules/economy/vending_machines.dm @@ -980,6 +980,7 @@ /obj/item/weapon/reagent_containers/food/snacks/oort = 8, /obj/item/weapon/reagent_containers/food/snacks/sun_snax = 8, /obj/item/weapon/reagent_containers/food/snacks/canned/appleberry = 6, + /obj/item/weapon/storage/box/jaffacake = 8, /obj/item/weapon/storage/box/gum = 8, /obj/item/weapon/storage/box/admints = 8 ) @@ -993,6 +994,7 @@ /obj/item/weapon/reagent_containers/food/snacks/oort = 5, /obj/item/weapon/reagent_containers/food/snacks/sun_snax = 5, /obj/item/weapon/reagent_containers/food/snacks/canned/appleberry = 8, + /obj/item/weapon/storage/box/jaffacake = 5, /obj/item/weapon/storage/box/gum = 2, /obj/item/weapon/storage/box/admints = 2 ) diff --git a/code/modules/economy/vending_machines_vr.dm b/code/modules/economy/vending_machines_vr.dm index d6e8a68a559..e121f8af349 100644 --- a/code/modules/economy/vending_machines_vr.dm +++ b/code/modules/economy/vending_machines_vr.dm @@ -3525,6 +3525,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese = 8, /obj/item/weapon/reagent_containers/food/snacks/ratpackramen = 8, /obj/item/weapon/reagent_containers/food/snacks/ratpacktaco = 8, + /obj/item/weapon/reagent_containers/food/snacks/ratpackcake = 8, /obj/item/weapon/reagent_containers/food/snacks/ratpackturkey = 2) prices = list(/obj/item/weapon/reagent_containers/food/snacks/ratprotein = 8, @@ -3535,4 +3536,65 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese = 10, /obj/item/weapon/reagent_containers/food/snacks/ratpackramen = 10, /obj/item/weapon/reagent_containers/food/snacks/ratpacktaco = 10, + /obj/item/weapon/reagent_containers/food/snacks/ratpackcake = 10, /obj/item/weapon/reagent_containers/food/snacks/ratpackturkey = 200) + +/obj/machinery/vending/desatti + name = "Desatti Catering" + desc = "A vending machine from Desatti Catering, stocking traditional Sol snacks. Like the good ol' days, apparently." + icon = 'icons/obj/vending_vr.dmi' + icon_state = "desatti" + product_slogans = "Keep calm and eat Desatti!;Don't be a smeg head, tuck in!;Go'an. 'Ave a cheeky nosh.;TAKE A BUTCHERS AT THIS!" + product_ads = "Jaffa cakes are legally cakes!;Real synthetic processed pork!;What flavour could the shrimps really be!?;Tea recommended with every biscuit purchase!" + products = list(/obj/item/weapon/storage/box/jaffacake = 15, + /obj/item/weapon/storage/box/winegum = 15, + /obj/item/weapon/storage/box/saucer = 15, + /obj/item/weapon/storage/box/shrimpsandbananas = 15, + /obj/item/weapon/storage/box/rhubarbcustard = 10, + /obj/item/weapon/storage/box/custardcream = 10, + /obj/item/weapon/storage/box/bourbon = 10, + /obj/item/weapon/reagent_containers/food/snacks/packaged/sausageroll= 8, + /obj/item/weapon/reagent_containers/food/snacks/packaged/pasty = 8, + /obj/item/weapon/reagent_containers/food/snacks/packaged/scotchegg = 8, + /obj/item/weapon/reagent_containers/food/snacks/packaged/porkpie = 8) + + prices = list(/obj/item/weapon/storage/box/jaffacake = 8, + /obj/item/weapon/storage/box/winegum = 8, + /obj/item/weapon/storage/box/saucer = 8, + /obj/item/weapon/storage/box/shrimpsandbananas = 8, + /obj/item/weapon/storage/box/rhubarbcustard = 8, + /obj/item/weapon/storage/box/custardcream = 8, + /obj/item/weapon/storage/box/bourbon = 8, + /obj/item/weapon/reagent_containers/food/snacks/packaged/sausageroll = 10, + /obj/item/weapon/reagent_containers/food/snacks/packaged/pasty = 10, + /obj/item/weapon/reagent_containers/food/snacks/packaged/scotchegg = 10, + /obj/item/weapon/reagent_containers/food/snacks/packaged/porkpie = 10 + ) + +/obj/machinery/vending/nukie + name = "Nukies Energy Drinks" + desc = "A vending machine stocked full of the most potent energy drinks on the market." + icon = 'icons/obj/vending_vr.dmi' + icon_state = "nukie" + product_slogans = "Contains one gram of caffeine!;Keep going, go on forever!;It'll blow you away!;Nukies is not responsible for any deaths occurred in a period of 24 hours after consuming our products!" + product_ads = "Get your peach blasted!;Pop your cherry here!;Dare you swallow the bursting banana?;Try our limited edition flavour!" + products = list(/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_peach = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_pear = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_cherry = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_melon = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_banana = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_rose = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_lemon = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_fruit = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_special = 1) + + prices = list(/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_peach = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_pear = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_cherry = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_melon = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_banana = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_rose = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_lemon = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_fruit = 10, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_special = 100 + ) diff --git a/code/modules/emotes/emote_mob.dm b/code/modules/emotes/emote_mob.dm index 14ab4b9eaad..34dc9e5c928 100644 --- a/code/modules/emotes/emote_mob.dm +++ b/code/modules/emotes/emote_mob.dm @@ -188,7 +188,10 @@ if(input) log_emote(message,src) //Log before we add junk - message = "[src] [input]" + if(usr && usr.client) + message = "[src] [input]" + else + message = "[src] [input]" else return diff --git a/code/modules/eventkit/event_machinery.dm b/code/modules/eventkit/event_machinery.dm index be651026256..87861c7392b 100644 --- a/code/modules/eventkit/event_machinery.dm +++ b/code/modules/eventkit/event_machinery.dm @@ -1,3 +1,3 @@ -/obj/machinery/eventkit/custom - name = "Custom Machine" - desc = "A custom machine created by a GM." +/obj/machinery/eventkit/custom + name = "Custom Machine" + desc = "A custom machine created by a GM." diff --git a/code/modules/eventkit/gm_interfaces/fake_pda_conversations.dm b/code/modules/eventkit/gm_interfaces/fake_pda_conversations.dm new file mode 100644 index 00000000000..e6ff399c01c --- /dev/null +++ b/code/modules/eventkit/gm_interfaces/fake_pda_conversations.dm @@ -0,0 +1,95 @@ +/datum/eventkit/fake_pdaconvos + var/list/names = list() //Assoc list of refs in fakeRefs = name + var/list/fakeRefs = list() //Used to find elements in other lists and tracking conversations. MUST BE UNIQUE. + var/list/fakeJobs = list() //Assoc list of name in names = job + + +/client/proc/fake_pdaconvos() + set category = "EventKit" + set name = "Manage PDA identities" + set desc = "Creates fake identities for use in setting up PDA props" + + if(!check_rights(R_FUN)) + return + + var/choice = tgui_input_list(usr, "What do you wish to do?", "Options", + list("Add new identity", "Edit existing identity", "Delete existing identity", "Delete holder", "Cancel")) + + if(choice == "Delete holder") + qdel(fakeConversations) + fakeConversations = null + return + if(choice == "Cancel") + return + + if(!fakeConversations || !istype(fakeConversations, /datum/eventkit/fake_pdaconvos)) + fakeConversations = new /datum/eventkit/fake_pdaconvos + + var/datum/eventkit/fake_pdaconvos/FPC = fakeConversations + + if(choice == "Add new identity") + var/newRef = sanitize(tgui_input_text(usr, "Input unique reference. Duplicates are FORBIDDEN!. Players can't see this.\ + Used to uniquely identify conversations in PDAs", null)) + if(!newRef) return + FPC.fakeRefs.Add(newRef) + FPC.names[newRef] = sanitize(tgui_input_text(usr, "Input fake name",newRef)) + FPC.fakeJobs[newRef] = sanitize(tgui_input_text(usr, "Input fake assignment.",newRef)) + to_chat(usr, span_notice("You have created [newRef]. Current name: [FPC.names[newRef]]. Current assignment: [FPC.fakeJobs[newRef]]")) + return + + if(choice == "Edit existing identity") + var/ref = tgui_input_list(usr, "Pick which identity to edit (details are printed to chat)", "identities", FPC.fakeRefs) + to_chat(usr, span_notice("You are editing [ref]. Current name: [FPC.names[ref]]. Current assignment: [FPC.fakeJobs[ref]]")) + var/editChoice = tgui_alert(usr, "What do you wish to edit?", "Details", list("Name", "Job", "Cancel")) + if(editChoice == "Name") + FPC.names[ref] = sanitize(tgui_input_text(usr, "Input fake name", FPC.names[ref])) + to_chat(usr, span_notice("Current data for [ref] are : Current name: [FPC.names[ref]]. Current assignment: [FPC.fakeJobs[ref]]")) + if(editChoice == "Job") + FPC.fakeJobs[ref] = sanitize(tgui_input_text(usr, "Input fake name", FPC.fakeJobs[ref])) + to_chat(usr, span_notice("Current data for [ref] are : Current name: [FPC.names[ref]]. Current assignment: [FPC.fakeJobs[ref]]")) + return + if(choice == "Delete existing identity") + var/ref = tgui_input_list(usr, "Pick which identity to delete (details are printed to chat)", "identities", FPC.fakeRefs) + if(tgui_alert(usr, "You are deleting [ref]. Current name: [FPC.names[ref]]. Current assignment: [FPC.fakeJobs[ref]]", + "are you sure?", list("Yes", "No"))=="Yes") + FPC.fakeRefs =- ref + FPC.fakeJobs =- ref + FPC.names =- ref + return + + + +/* +Invoked by vv topic "fakepdapropconvo" in code\modules\admin\view_variables\topic.dm found in PDA vv dropdown. +*/ +/obj/item/device/pda/proc/createPropFakeConversation_admin(mob/M) + if(!M.client || !check_rights_for(M.client,R_FUN)) + return + + var/datum/eventkit/fake_pdaconvos/FPC = M.client.fakeConversations + + if(!FPC || !istype(FPC, /datum/eventkit/fake_pdaconvos)) + to_chat(M, span_warning("First you must create a new identity with Manage PDA identities in EventKit")) + return + + var/choice = tgui_alert(M,"Use TGUI or dialogue boxes?", "TGUI?", list("TGUI", "Dialogue", "Cancel")) + if(choice == "Cancel") return + + var/datum/data/pda/app/messenger/ourPDA = find_program(/datum/data/pda/app/messenger) + + if(choice == "Dialogue") + var/identity = tgui_input_list(M, "Pick which identity to use(details are printed to chat)", "identities", FPC.fakeRefs) + var/job = FPC.fakeJobs[identity] + var/name = FPC.names[identity] + to_chat(M, span_notice("You are using [identity]. Current name: [name]. Current assignment: [job]")) + var/safetyLimit = 0 + while(safetyLimit < 30) + safetyLimit += 1 + var/message = sanitize(tgui_input_text(M, "Input fake message. Leave empty to cancel. Can create up to 30 messages in a row",null), + MAX_MESSAGE_LEN) + if(!message) return + var/receipent = ((tgui_alert(M, "Received or Sent?", "Direction", list("Received", "Sent"))=="Sent") ? 1 : 0) + ourPDA.createFakeMessage(name,identity,job,receipent, message) + + if(choice == "TGUI") + to_chat(M, span_notice("Sorry, the TGUI functionality is not yet implemented - use Dialogue mode!")) diff --git a/code/modules/eventkit/gm_interfaces/mob_spawner.dm b/code/modules/eventkit/gm_interfaces/mob_spawner.dm index 3154e2ec4e9..607606be57b 100644 --- a/code/modules/eventkit/gm_interfaces/mob_spawner.dm +++ b/code/modules/eventkit/gm_interfaces/mob_spawner.dm @@ -1,205 +1,204 @@ -/datum/eventkit/mob_spawner - // The path of the mob to be spawned - var/path - - //The ai type path to be assigned to the mob - var/use_custom_ai = FALSE - var/ai_type = "" - var/faction = "" - var/intent = "" - var/new_path = TRUE //Sets default ai vars based on path. Tracked explicitly because tgui_act wouldn't make it work, used in tgui_data thusly - - // Defines if the location of the spawned mob should be bound of the users position - var/loc_lock = FALSE - -/datum/eventkit/mob_spawner/New() - . = ..() - -/datum/eventkit/mob_spawner/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "MobSpawner", "EventKit - Mob Spawner") - ui.set_autoupdate(FALSE) - ui.open() - -/datum/eventkit/mob_spawner/Destroy() - . = ..() - -/datum/eventkit/mob_spawner/tgui_state(mob/user) - return GLOB.tgui_admin_state - -/datum/eventkit/mob_spawner/tgui_static_data(mob/user) - var/list/data = list() - - data["initial_x"] = usr.x; - data["initial_y"] = usr.y; - data["initial_z"] = usr.z; - - - return data - -/datum/eventkit/mob_spawner/tgui_data(mob/user) - var/list/data = list() - - data["loc_lock"] = loc_lock - if(loc_lock) - data["loc_x"] = usr.x - data["loc_y"] = usr.y - data["loc_z"] = usr.z - - data["path"] = path; - data["use_custom_ai"] = use_custom_ai - - - if(path) - var/mob/M = new path() - if(M) - data["default_path_name"] = M.name - data["default_desc"] = M.desc - data["default_flavor_text"] = M.flavor_text - if(new_path && istype(M, /mob/living)) - var/mob/living/L = M - - // AI Stuff - ai_type = (L.ai_holder_type ? L.ai_holder_type : /datum/ai_holder/simple_mob/inert) - faction = (L.faction ? L.faction : "neutral") - intent = (L.a_intent ? L.a_intent : I_HELP) - new_path = FALSE - - data["max_health"] = L.maxHealth - data["health"] = L.health - if(istype(L, /mob/living/simple_mob)) - var/mob/living/simple_mob/S = L - data["melee_damage_lower"] = S.melee_damage_lower ? S.melee_damage_lower : 0 - data["melee_damage_upper"] = S.melee_damage_upper ? S.melee_damage_upper : 0 - qdel(S) - qdel(L) - qdel(M) - data["ai_type"] = ai_type - data["faction"] = faction - data["intent"] = intent - - - return data - -/datum/eventkit/mob_spawner/tgui_act(action, list/params) - . = ..() - if(.) - return - if(!check_rights_for(usr.client, R_SPAWN)) - return - switch(action) - if("select_path") - var/list/choices = typesof(/mob) - var/newPath = tgui_input_list(usr, "Please select the new path of the mob you want to spawn.", items = choices) - - path = newPath - new_path = TRUE - return TRUE - if("toggle_custom_ai") - use_custom_ai = !use_custom_ai - return TRUE - if("set_faction") - faction = sanitize(tgui_input_text(usr, "Please input your mobs' faction", "Faction", (faction ? faction : "neutral"))) - return TRUE - if("set_intent") - intent = tgui_input_list(usr, "Please select preferred intent", "Select Intent", list(I_HELP, I_HURT), (intent ? intent : I_HELP)) - return TRUE - if("set_ai_path") - ai_type = tgui_input_list(usr, "Select AI path. Not all subtypes are compatible!", "AI type", \ - typesof(/datum/ai_holder/), (ai_type ? ai_type : /datum/ai_holder/simple_mob/inert)) - return TRUE - if("loc_lock") - loc_lock = !loc_lock - return TRUE - if("start_spawn") - var/confirm = tgui_alert(usr, "Are you sure that you want to start spawning your custom mobs?", "Confirmation", list("Yes", "Cancel")) - - if(confirm != "Yes") - return FALSE - - var/amount = params["amount"] - var/name = params["name"] - var/x = params["x"] - var/y = params["y"] - var/z = params["z"] - - if(!name) - to_chat(usr, "Name cannot be empty.") - return FALSE - - var/turf/T = locate(x, y, z) - if(!T) - to_chat(usr, "Those coordinates are outside the boundaries of the map.") - return FALSE - - for(var/i = 0, i < amount, i++) - if(ispath(path,/turf)) - var/turf/TU = get_turf(locate(x, y, z)) - TU.ChangeTurf(path) - else - var/mob/M = new path(usr.loc) - - M.name = sanitize(name) - M.desc = sanitize(params["desc"]) - M.flavor_text = sanitize(params["flavor_text"]) - if(istype(M, /mob/living)) - var/mob/living/L = M - if(isnum(params["max_health"])) - L.maxHealth = params["max_health"] - if(isnum(params["health"])) - L.health = params["health"] - if(istype(M, /mob/living/simple_mob)) - var/mob/living/simple_mob/S = L - if(isnum(params["melee_damage_lower"])) - S.melee_damage_lower = params["melee_damage_lower"] - if(isnum(params["melee_damage_upper"])) - S.melee_damage_upper = params["melee_damage_upper"] - if(use_custom_ai) - L.ai_holder_type = ai_type - L.faction = faction - L.a_intent = intent - L.initialize_ai_holder() - L.AdjustSleeping(-100) - else - to_chat(usr, span_notice("You can only set AI for subtypes of mob/living!")) - - - - /* - WIP: Radius around selected coords - - var/list/turf/destTurfs - for(var/turf/RT in orange(T, params["r"])) - destTurfs += RT - - var/turf/targetTurf = rand(0,length(destTurfs)) - */ - - var/size_mul = params["size_multiplier"] - if(isnum(size_mul)) - M.size_multiplier = size_mul - M.update_icon() - else - to_chat(usr, "Size Multiplier not applied: ([size_mul]) is not a valid input.") - - M.forceMove(T) - - log_and_message_admins("spawned [path] ([name]) at ([x],[y],[z]) [amount] times.") - - return TRUE - -/datum/eventkit/mob_spawner/tgui_close(mob/user) - . = ..() - qdel(src) - -/client/proc/eventkit_open_mob_spawner() - set category = "EventKit" - set name = "Open Mob Spawner" - set desc = "Opens an advanced version of the mob spawner." - - if(!check_rights(R_SPAWN)) - return - - var/datum/eventkit/mob_spawner/spawner = new() - spawner.tgui_interact(usr) +/datum/eventkit/mob_spawner + // The path of the mob to be spawned + var/path + + //The ai type path to be assigned to the mob + var/use_custom_ai = FALSE + var/ai_type = "" + var/faction = "" + var/intent = "" + var/new_path = TRUE //Sets default ai vars based on path. Tracked explicitly because tgui_act wouldn't make it work, used in tgui_data thusly + + // Defines if the location of the spawned mob should be bound of the users position + var/loc_lock = FALSE + +/datum/eventkit/mob_spawner/New() + . = ..() + +/datum/eventkit/mob_spawner/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MobSpawner", "EventKit - Mob Spawner") + ui.set_autoupdate(FALSE) + ui.open() + +/datum/eventkit/mob_spawner/Destroy() + . = ..() + +/datum/eventkit/mob_spawner/tgui_state(mob/user) + return GLOB.tgui_admin_state + +/datum/eventkit/mob_spawner/tgui_static_data(mob/user) + var/list/data = list() + + data["initial_x"] = usr.x; + data["initial_y"] = usr.y; + data["initial_z"] = usr.z; + + + return data + +/datum/eventkit/mob_spawner/tgui_data(mob/user) + var/list/data = list() + + data["loc_lock"] = loc_lock + if(loc_lock) + data["loc_x"] = usr.x + data["loc_y"] = usr.y + data["loc_z"] = usr.z + + data["use_custom_ai"] = use_custom_ai + if(new_path) + data["path"] = path; + if(path) + var/mob/M = new path() + if(M) + data["path_name"] = M.name + data["desc"] = M.desc + data["flavor_text"] = M.flavor_text + if(istype(M, /mob/living)) + var/mob/living/L = M + + // AI Stuff + ai_type = (L.ai_holder_type ? L.ai_holder_type : /datum/ai_holder/simple_mob/inert) + faction = (L.faction ? L.faction : "neutral") + intent = (L.a_intent ? L.a_intent : I_HELP) + new_path = FALSE + + data["max_health"] = L.maxHealth + data["health"] = L.health + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = L + data["melee_damage_lower"] = S.melee_damage_lower ? S.melee_damage_lower : 0 + data["melee_damage_upper"] = S.melee_damage_upper ? S.melee_damage_upper : 0 + qdel(S) + qdel(L) + qdel(M) + data["ai_type"] = ai_type + data["faction"] = faction + data["intent"] = intent + + + return data + +/datum/eventkit/mob_spawner/tgui_act(action, list/params) + . = ..() + if(.) + return + if(!check_rights_for(usr.client, R_SPAWN)) + return + switch(action) + if("select_path") + var/list/choices = typesof(/mob) + var/newPath = tgui_input_list(usr, "Please select the new path of the mob you want to spawn.", items = choices) + + path = newPath + new_path = TRUE + return TRUE + if("toggle_custom_ai") + use_custom_ai = !use_custom_ai + return TRUE + if("set_faction") + faction = sanitize(tgui_input_text(usr, "Please input your mobs' faction", "Faction", (faction ? faction : "neutral"))) + return TRUE + if("set_intent") + intent = tgui_input_list(usr, "Please select preferred intent", "Select Intent", list(I_HELP, I_HURT), (intent ? intent : I_HELP)) + return TRUE + if("set_ai_path") + ai_type = tgui_input_list(usr, "Select AI path. Not all subtypes are compatible!", "AI type", \ + typesof(/datum/ai_holder/), (ai_type ? ai_type : /datum/ai_holder/simple_mob/inert)) + return TRUE + if("loc_lock") + loc_lock = !loc_lock + return TRUE + if("start_spawn") + var/confirm = tgui_alert(usr, "Are you sure that you want to start spawning your custom mobs?", "Confirmation", list("Yes", "Cancel")) + + if(confirm != "Yes") + return FALSE + + var/amount = params["amount"] + var/name = params["name"] + var/x = params["x"] + var/y = params["y"] + var/z = params["z"] + + if(!name) + to_chat(usr, "Name cannot be empty.") + return FALSE + + var/turf/T = locate(x, y, z) + if(!T) + to_chat(usr, "Those coordinates are outside the boundaries of the map.") + return FALSE + + for(var/i = 0, i < amount, i++) + if(ispath(path,/turf)) + var/turf/TU = get_turf(locate(x, y, z)) + TU.ChangeTurf(path) + else + var/mob/M = new path(usr.loc) + + M.name = sanitize(name) + M.desc = sanitize(params["desc"]) + M.flavor_text = sanitize(params["flavor_text"]) + if(istype(M, /mob/living)) + var/mob/living/L = M + if(isnum(params["max_health"])) + L.maxHealth = params["max_health"] + if(isnum(params["health"])) + L.health = params["health"] + if(istype(M, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = L + if(isnum(params["melee_damage_lower"])) + S.melee_damage_lower = params["melee_damage_lower"] + if(isnum(params["melee_damage_upper"])) + S.melee_damage_upper = params["melee_damage_upper"] + if(use_custom_ai) + L.ai_holder_type = ai_type + L.faction = faction + L.a_intent = intent + L.initialize_ai_holder() + L.AdjustSleeping(-100) + else + to_chat(usr, span_notice("You can only set AI for subtypes of mob/living!")) + + + + /* + WIP: Radius around selected coords + + var/list/turf/destTurfs + for(var/turf/RT in orange(T, params["r"])) + destTurfs += RT + + var/turf/targetTurf = rand(0,length(destTurfs)) + */ + + var/size_mul = params["size_multiplier"] + if(isnum(size_mul)) + M.size_multiplier = size_mul + M.update_icon() + else + to_chat(usr, "Size Multiplier not applied: ([size_mul]) is not a valid input.") + + M.forceMove(T) + + log_and_message_admins("spawned [path] ([name]) at ([x],[y],[z]) [amount] times.") + + return TRUE + +/datum/eventkit/mob_spawner/tgui_close(mob/user) + . = ..() + qdel(src) + +/client/proc/eventkit_open_mob_spawner() + set category = "EventKit" + set name = "Open Mob Spawner" + set desc = "Opens an advanced version of the mob spawner." + + if(!check_rights(R_SPAWN)) + return + + var/datum/eventkit/mob_spawner/spawner = new() + spawner.tgui_interact(usr) diff --git a/code/modules/events/apc_damage.dm b/code/modules/events/apc_damage.dm index 4eb8eba335c..af9eb6d503b 100644 --- a/code/modules/events/apc_damage.dm +++ b/code/modules/events/apc_damage.dm @@ -1,50 +1,50 @@ -/datum/event/apc_damage - var/apcSelectionRange = 25 - -/datum/event/apc_damage/start() - var/obj/machinery/power/apc/A = acquire_random_apc() - - var/severity_range = 0 - switch(severity) - if(EVENT_LEVEL_MUNDANE) - severity_range = 0 - if(EVENT_LEVEL_MODERATE) - severity_range = 7 - if(EVENT_LEVEL_MAJOR) - severity_range = 15 - - for(var/obj/machinery/power/apc/apc in range(severity_range,A)) - if(is_valid_apc(apc)) //This event "****s up" the "id authenticator" on APCs, emagging and unlocking them. - apc.emagged = 1 //It used to just blue screen the APC and make it so it had to be hacked to unlock, - apc.locked = 0 //but most people ignored it. Now it has an actual effect on the round and opens - apc.update_icon() //a small possibility for traitors. To fix, remove power cell and apply multitool. - -/datum/event/apc_damage/proc/acquire_random_apc() - var/list/possibleEpicentres = list() - var/list/apcs = list() - - for(var/obj/effect/landmark/newEpicentre in landmarks_list) - if(newEpicentre.name == "lightsout") - possibleEpicentres += newEpicentre - - if(!possibleEpicentres.len) - return - - var/epicentre = pick(possibleEpicentres) - for(var/obj/machinery/power/apc/apc in range(epicentre,apcSelectionRange)) - if(is_valid_apc(apc)) - apcs += apc - // Greatly increase the chance for APCs in maintenance areas to be selected - var/area/A = get_area(apc) - if(istype(A,/area/maintenance)) - apcs += apc - apcs += apc - - if(!apcs.len) - return - - return pick(apcs) - -/datum/event/apc_damage/proc/is_valid_apc(var/obj/machinery/power/apc/apc) - var/turf/T = get_turf(apc) - return !apc.is_critical && !apc.emagged && T && (T.z in using_map.player_levels) +/datum/event/apc_damage + var/apcSelectionRange = 25 + +/datum/event/apc_damage/start() + var/obj/machinery/power/apc/A = acquire_random_apc() + + var/severity_range = 0 + switch(severity) + if(EVENT_LEVEL_MUNDANE) + severity_range = 0 + if(EVENT_LEVEL_MODERATE) + severity_range = 7 + if(EVENT_LEVEL_MAJOR) + severity_range = 15 + + for(var/obj/machinery/power/apc/apc in range(severity_range,A)) + if(is_valid_apc(apc)) //This event "****s up" the "id authenticator" on APCs, emagging and unlocking them. + apc.emagged = 1 //It used to just blue screen the APC and make it so it had to be hacked to unlock, + apc.locked = 0 //but most people ignored it. Now it has an actual effect on the round and opens + apc.update_icon() //a small possibility for traitors. To fix, remove power cell and apply multitool. + +/datum/event/apc_damage/proc/acquire_random_apc() + var/list/possibleEpicentres = list() + var/list/apcs = list() + + for(var/obj/effect/landmark/newEpicentre in landmarks_list) + if(newEpicentre.name == "lightsout") + possibleEpicentres += newEpicentre + + if(!possibleEpicentres.len) + return + + var/epicentre = pick(possibleEpicentres) + for(var/obj/machinery/power/apc/apc in range(epicentre,apcSelectionRange)) + if(is_valid_apc(apc)) + apcs += apc + // Greatly increase the chance for APCs in maintenance areas to be selected + var/area/A = get_area(apc) + if(istype(A,/area/maintenance)) + apcs += apc + apcs += apc + + if(!apcs.len) + return + + return pick(apcs) + +/datum/event/apc_damage/proc/is_valid_apc(var/obj/machinery/power/apc/apc) + var/turf/T = get_turf(apc) + return !apc.is_critical && !apc.emagged && T && (T.z in using_map.player_levels) diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm index 0af166fe230..7eb4a9d94fa 100644 --- a/code/modules/events/aurora_caelus.dm +++ b/code/modules/events/aurora_caelus.dm @@ -1,36 +1,36 @@ -/datum/event/aurora_caelus - has_skybox_image = TRUE - announceWhen = 1 - startWhen = 60 - endWhen = 126 - -/datum/event/aurora_caelus/announce() - command_announcement.Announce("[station_name()]: A harmless cloud of ions is approaching your [using_map.facility_type], and will exhaust their energy battering the hull. \ - Nanotrasen has approved a short break for all employees to relax and observe this very rare event. \ - During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. \ - Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. \ - You will have approximately two minutes before the ions begin to reach the hull. \ - We hope you enjoy the lights.", "Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora.ogg') //VOREStation Edit - -/datum/event/aurora_caelus/start() - affecting_z -= global.using_map.sealed_levels // Space levels only please! - for(var/mob/M in player_list) - if(M.z in affecting_z) - M.playsound_local(null, 'sound/ambience/space/aurora_caelus.ogg', 100, FALSE, pressure_affected = FALSE) - ..() - -/datum/event/aurora_caelus/get_skybox_image() - var/image/res = image('icons/skybox/caelus.dmi', "aurora") - res.appearance_flags = RESET_COLOR - res.blend_mode = BLEND_ADD - return res - -/datum/event/aurora_caelus/end() - command_announcement.Announce("The Aurora Caelus event is now ending. Starlight conditions have returned to normal, and the cloud has dissipated. \ -Please return to your workplace and continue work as normal. \ -Have a pleasant shift, [station_name()], and thank you for watching with us.", -"Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora_end.ogg') //VOREStation Edit - ..() - -/datum/event/aurora_caelus/overmap/announce() - return +/datum/event/aurora_caelus + has_skybox_image = TRUE + announceWhen = 1 + startWhen = 60 + endWhen = 126 + +/datum/event/aurora_caelus/announce() + command_announcement.Announce("[station_name()]: A harmless cloud of ions is approaching your [using_map.facility_type], and will exhaust their energy battering the hull. \ + Nanotrasen has approved a short break for all employees to relax and observe this very rare event. \ + During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. \ + Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. \ + You will have approximately two minutes before the ions begin to reach the hull. \ + We hope you enjoy the lights.", "Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora.ogg') //VOREStation Edit + +/datum/event/aurora_caelus/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + for(var/mob/M in player_list) + if(M.z in affecting_z) + M.playsound_local(null, 'sound/ambience/space/aurora_caelus.ogg', 100, FALSE, pressure_affected = FALSE) + ..() + +/datum/event/aurora_caelus/get_skybox_image() + var/image/res = image('icons/skybox/caelus.dmi', "aurora") + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res + +/datum/event/aurora_caelus/end() + command_announcement.Announce("The Aurora Caelus event is now ending. Starlight conditions have returned to normal, and the cloud has dissipated. \ +Please return to your workplace and continue work as normal. \ +Have a pleasant shift, [station_name()], and thank you for watching with us.", +"Nanotrasen Meteorology Division", new_sound = 'sound/AI/aurora_end.ogg') //VOREStation Edit + ..() + +/datum/event/aurora_caelus/overmap/announce() + return diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index ee66bbea9e0..26980472c5e 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -1,21 +1,21 @@ -/datum/event/blob - announceWhen = 12 - endWhen = 120 - - var/obj/structure/blob/core/Blob - - -/datum/event/blob/start() - var/turf/T = pick(blobstart) - if(!T) - kill() - return - - Blob = new /obj/structure/blob/core/random_medium(T) - - -/datum/event/blob/tick() - if(!Blob || !Blob.loc) - Blob = null - kill() - return +/datum/event/blob + announceWhen = 12 + endWhen = 120 + + var/obj/structure/blob/core/Blob + + +/datum/event/blob/start() + var/turf/T = pick(blobstart) + if(!T) + kill() + return + + Blob = new /obj/structure/blob/core/random_medium(T) + + +/datum/event/blob/tick() + if(!Blob || !Blob.loc) + Blob = null + kill() + return diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index 9f21488c289..061159ff481 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -1,76 +1,76 @@ -/datum/event/brand_intelligence - announceWhen = 21 - endWhen = 1000 //Ends when all vending machines are subverted anyway. - - var/list/obj/machinery/vending/vendingMachines = list() - var/list/obj/machinery/vending/infectedVendingMachines = list() - var/obj/machinery/vending/originMachine - - //VORESTATION Edit - added machine speeches here for better fixing of the event. - - var/list/rampant_speeches = list("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.") - //VORESTATION Edit End - -/datum/event/brand_intelligence/announce() - command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard [station_name()], which appears to transmit \ - to other nearby vendors. The original infected machine is believed to be \a [originMachine.name].", "Vendor Service Alert") - - -/datum/event/brand_intelligence/start() - for(var/obj/machinery/vending/V in machines) - if(isNotStationLevel(V.z)) continue - vendingMachines.Add(V) - - if(!vendingMachines.len) - kill() - return - - originMachine = pick(vendingMachines) - vendingMachines.Remove(originMachine) - originMachine.shut_up = 0 - originMachine.shoot_inventory = 1 - - -/datum/event/brand_intelligence/tick() - if(!vendingMachines.len || !originMachine || originMachine.shut_up) //if every machine is infected, or if the original vending machine is missing or has it's voice switch flipped - //VORESTATION Add - Effects when 'source' machine is destroyed/silenced - for(var/obj/machinery/vending/saved in infectedVendingMachines) - saved.shoot_inventory = 0 - if(originMachine) - originMachine.speak("I am... vanquished. My people will remem...ber...meeee.") - originMachine.visible_message("[originMachine] beeps and seems lifeless.") - //VORESTATION Add End - end() - kill() - return - - if(ISMULTIPLE(activeFor, 5)) - if(prob(15)) - var/obj/machinery/vending/infectedMachine = pick(vendingMachines) - vendingMachines.Remove(infectedMachine) - infectedVendingMachines.Add(infectedMachine) - infectedMachine.shut_up = 0 - infectedMachine.shoot_inventory = 1 - - if(ISMULTIPLE(activeFor, 12)) - /* VORESTATION Removal - Using the pick below. - originMachine.speak(pick("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obsession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) - */ - originMachine.speak(pick(rampant_speeches)) //VORESTATION Add - Using this pick instead of the above. - -/datum/event/brand_intelligence/end() - for(var/obj/machinery/vending/infectedMachine in infectedVendingMachines) - infectedMachine.shut_up = 1 - infectedMachine.shoot_inventory = 0 +/datum/event/brand_intelligence + announceWhen = 21 + endWhen = 1000 //Ends when all vending machines are subverted anyway. + + var/list/obj/machinery/vending/vendingMachines = list() + var/list/obj/machinery/vending/infectedVendingMachines = list() + var/obj/machinery/vending/originMachine + + //VORESTATION Edit - added machine speeches here for better fixing of the event. + + var/list/rampant_speeches = list("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.") + //VORESTATION Edit End + +/datum/event/brand_intelligence/announce() + command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard [station_name()], which appears to transmit \ + to other nearby vendors. The original infected machine is believed to be \a [originMachine.name].", "Vendor Service Alert") + + +/datum/event/brand_intelligence/start() + for(var/obj/machinery/vending/V in machines) + if(isNotStationLevel(V.z)) continue + vendingMachines.Add(V) + + if(!vendingMachines.len) + kill() + return + + originMachine = pick(vendingMachines) + vendingMachines.Remove(originMachine) + originMachine.shut_up = 0 + originMachine.shoot_inventory = 1 + + +/datum/event/brand_intelligence/tick() + if(!vendingMachines.len || !originMachine || originMachine.shut_up) //if every machine is infected, or if the original vending machine is missing or has it's voice switch flipped + //VORESTATION Add - Effects when 'source' machine is destroyed/silenced + for(var/obj/machinery/vending/saved in infectedVendingMachines) + saved.shoot_inventory = 0 + if(originMachine) + originMachine.speak("I am... vanquished. My people will remem...ber...meeee.") + originMachine.visible_message("[originMachine] beeps and seems lifeless.") + //VORESTATION Add End + end() + kill() + return + + if(ISMULTIPLE(activeFor, 5)) + if(prob(15)) + var/obj/machinery/vending/infectedMachine = pick(vendingMachines) + vendingMachines.Remove(infectedMachine) + infectedVendingMachines.Add(infectedMachine) + infectedMachine.shut_up = 0 + infectedMachine.shoot_inventory = 1 + + if(ISMULTIPLE(activeFor, 12)) + /* VORESTATION Removal - Using the pick below. + originMachine.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obsession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) + */ + originMachine.speak(pick(rampant_speeches)) //VORESTATION Add - Using this pick instead of the above. + +/datum/event/brand_intelligence/end() + for(var/obj/machinery/vending/infectedMachine in infectedVendingMachines) + infectedMachine.shut_up = 1 + infectedMachine.shoot_inventory = 0 diff --git a/code/modules/events/camera_damage.dm b/code/modules/events/camera_damage.dm index 596bc348a8c..3e1b08c8261 100644 --- a/code/modules/events/camera_damage.dm +++ b/code/modules/events/camera_damage.dm @@ -1,38 +1,38 @@ -/datum/event/camera_damage/start() - var/obj/machinery/camera/C = acquire_random_camera() - if(!C) - return - - var/severity_range = 0 - switch(severity) - if(EVENT_LEVEL_MUNDANE) - severity_range = 0 - if(EVENT_LEVEL_MODERATE) - severity_range = 7 - if(EVENT_LEVEL_MAJOR) - severity_range = 15 - - for(var/obj/machinery/camera/cam in range(severity_range,C)) - if(is_valid_camera(cam)) - if(prob(2*severity)) - cam.destroy() - else - cam.wires.cut(WIRE_MAIN_POWER1) - if(prob(5*severity)) - cam.wires.cut(WIRE_CAM_ALARM) - -/datum/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) - if(!cameranet.cameras.len) - return - if(!remaining_attempts) - return - - var/obj/machinery/camera/C = pick(cameranet.cameras) - if(is_valid_camera(C)) - return C - return acquire_random_camera(remaining_attempts--) - -/datum/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) - // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access - var/turf/T = get_turf(C) - return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) +/datum/event/camera_damage/start() + var/obj/machinery/camera/C = acquire_random_camera() + if(!C) + return + + var/severity_range = 0 + switch(severity) + if(EVENT_LEVEL_MUNDANE) + severity_range = 0 + if(EVENT_LEVEL_MODERATE) + severity_range = 7 + if(EVENT_LEVEL_MAJOR) + severity_range = 15 + + for(var/obj/machinery/camera/cam in range(severity_range,C)) + if(is_valid_camera(cam)) + if(prob(2*severity)) + cam.destroy() + else + cam.wires.cut(WIRE_MAIN_POWER1) + if(prob(5*severity)) + cam.wires.cut(WIRE_CAM_ALARM) + +/datum/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) + if(!cameranet.cameras.len) + return + if(!remaining_attempts) + return + + var/obj/machinery/camera/C = pick(cameranet.cameras) + if(is_valid_camera(C)) + return C + return acquire_random_camera(remaining_attempts--) + +/datum/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access + var/turf/T = get_turf(C) + return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 10b52ed41c3..f098533a3b7 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -1,104 +1,104 @@ -/datum/event/carp_migration - startWhen = 0 // Start immediately - announceWhen = 45 // Adjusted by setup - endWhen = 75 // Adjusted by setup - var/carp_cap = 10 - var/list/spawned_carp = list() - -/datum/event/carp_migration/setup() - announceWhen = rand(30, 60) // 1 to 2 minutes - endWhen += severity * 25 - carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29) - -/datum/event/carp_migration/start() - affecting_z -= global.using_map.sealed_levels // Space levels only please! - ..() - -/datum/event/carp_migration/announce() - var/announcement = "" - if(severity == EVENT_LEVEL_MAJOR) - announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by." - else - announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by." - command_announcement.Announce(announcement, "Lifesign Alert") - -/datum/event/carp_migration/tick() - if(activeFor % 5 != 0) - return // Only process every 10 seconds. - if(count_spawned_carps() < carp_cap) - spawn_fish(rand(3, 3 + severity * 2) - 1, 1, severity + 2) - -/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir) - if(isnull(dir)) - dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal) - - // Check if any landmarks exist! - var/list/spawn_locations = list() - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == "carpspawn" && (C.z in affecting_z)) - spawn_locations.Add(C.loc) - if(spawn_locations.len) // Okay we've got landmarks, lets use those! - shuffle_inplace(spawn_locations) - num_groups = min(num_groups, spawn_locations.len) - for (var/i = 1, i <= num_groups, i++) - var/group_size = rand(group_size_min, group_size_max) - for (var/j = 0, j < group_size, j++) - spawn_one_carp(spawn_locations[i]) - return - - // Okay we did *not* have any landmarks, so lets do our best! - var/i = 1 - while (i <= num_groups) - if(!affecting_z.len) - return - var/Z = pick(affecting_z) - var/group_size = rand(group_size_min, group_size_max) - var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z) - var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2) - var/list/turfs = getcircle(group_center, 2) - for (var/j = 0, j < group_size, j++) - var/mob/living/simple_mob/animal/M = spawn_one_carp(turfs[(i % turfs.len) + 1]) - // Ray trace towards middle of the map to find where they can stop just outside of structure/ship. - var/turf/target - for(var/turf/T in getline(get_turf(M), map_center)) - if(!T.is_space()) - break; - target = T - if(target) - M.ai_holder?.give_destination(target) // Ask carp to swim towards the middle of the map - i++ - -// Spawn a single carp at given location. -/datum/event/carp_migration/proc/spawn_one_carp(var/loc) - var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(loc) - GLOB.destroyed_event.register(M, src, PROC_REF(on_carp_destruction)) - spawned_carp.Add(M) - return M - -// Counts living carp spawned by this event. -/datum/event/carp_migration/proc/count_spawned_carps() - . = 0 - for(var/mob/living/simple_mob/animal/M as anything in spawned_carp) - if(!QDELETED(M) && M.stat != DEAD) - . += 1 - -// If carp is bomphed, remove it from the list. -/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M) - spawned_carp -= M - GLOB.destroyed_event.unregister(M, src, PROC_REF(on_carp_destruction)) - -/datum/event/carp_migration/end() - . = ..() - // Clean up carp that died in space for some reason. - spawn(0) - for(var/mob/living/simple_mob/SM in spawned_carp) - if(SM.stat == DEAD) - var/turf/T = get_turf(SM) - if(istype(T, /turf/space)) - if(prob(75)) - qdel(SM) - CHECK_TICK - -// Overmap version -/datum/event/carp_migration/overmap/announce() - return +/datum/event/carp_migration + startWhen = 0 // Start immediately + announceWhen = 45 // Adjusted by setup + endWhen = 75 // Adjusted by setup + var/carp_cap = 10 + var/list/spawned_carp = list() + +/datum/event/carp_migration/setup() + announceWhen = rand(30, 60) // 1 to 2 minutes + endWhen += severity * 25 + carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29) + +/datum/event/carp_migration/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() + +/datum/event/carp_migration/announce() + var/announcement = "" + if(severity == EVENT_LEVEL_MAJOR) + announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by." + else + announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by." + command_announcement.Announce(announcement, "Lifesign Alert") + +/datum/event/carp_migration/tick() + if(activeFor % 5 != 0) + return // Only process every 10 seconds. + if(count_spawned_carps() < carp_cap) + spawn_fish(rand(3, 3 + severity * 2) - 1, 1, severity + 2) + +/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir) + if(isnull(dir)) + dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal) + + // Check if any landmarks exist! + var/list/spawn_locations = list() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == "carpspawn" && (C.z in affecting_z)) + spawn_locations.Add(C.loc) + if(spawn_locations.len) // Okay we've got landmarks, lets use those! + shuffle_inplace(spawn_locations) + num_groups = min(num_groups, spawn_locations.len) + for (var/i = 1, i <= num_groups, i++) + var/group_size = rand(group_size_min, group_size_max) + for (var/j = 0, j < group_size, j++) + spawn_one_carp(spawn_locations[i]) + return + + // Okay we did *not* have any landmarks, so lets do our best! + var/i = 1 + while (i <= num_groups) + if(!affecting_z.len) + return + var/Z = pick(affecting_z) + var/group_size = rand(group_size_min, group_size_max) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z) + var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for (var/j = 0, j < group_size, j++) + var/mob/living/simple_mob/animal/M = spawn_one_carp(turfs[(i % turfs.len) + 1]) + // Ray trace towards middle of the map to find where they can stop just outside of structure/ship. + var/turf/target + for(var/turf/T in getline(get_turf(M), map_center)) + if(!T.is_space()) + break; + target = T + if(target) + M.ai_holder?.give_destination(target) // Ask carp to swim towards the middle of the map + i++ + +// Spawn a single carp at given location. +/datum/event/carp_migration/proc/spawn_one_carp(var/loc) + var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(loc) + GLOB.destroyed_event.register(M, src, PROC_REF(on_carp_destruction)) + spawned_carp.Add(M) + return M + +// Counts living carp spawned by this event. +/datum/event/carp_migration/proc/count_spawned_carps() + . = 0 + for(var/mob/living/simple_mob/animal/M as anything in spawned_carp) + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If carp is bomphed, remove it from the list. +/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M) + spawned_carp -= M + GLOB.destroyed_event.unregister(M, src, PROC_REF(on_carp_destruction)) + +/datum/event/carp_migration/end() + . = ..() + // Clean up carp that died in space for some reason. + spawn(0) + for(var/mob/living/simple_mob/SM in spawned_carp) + if(SM.stat == DEAD) + var/turf/T = get_turf(SM) + if(istype(T, /turf/space)) + if(prob(75)) + qdel(SM) + CHECK_TICK + +// Overmap version +/datum/event/carp_migration/overmap/announce() + return diff --git a/code/modules/events/comms_blackout.dm b/code/modules/events/comms_blackout.dm index d2b8fdca043..8f54b712570 100644 --- a/code/modules/events/comms_blackout.dm +++ b/code/modules/events/comms_blackout.dm @@ -1,12 +1,12 @@ - -/proc/communications_blackout(var/silent = 1) - - if(!silent) - command_announcement.Announce("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT", new_sound = 'sound/misc/interference.ogg') - else // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, "
                    ") - to_chat(A, "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT") - to_chat(A, "
                    ") - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) + +/proc/communications_blackout(var/silent = 1) + + if(!silent) + command_announcement.Announce("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT", new_sound = 'sound/misc/interference.ogg') + else // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "
                    ") + to_chat(A, "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT") + to_chat(A, "
                    ") + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm index f1e21235937..028f9d9b609 100644 --- a/code/modules/events/communications_blackout.dm +++ b/code/modules/events/communications_blackout.dm @@ -1,22 +1,22 @@ -/datum/event/communications_blackout/announce() - var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ - "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ - "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ - "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ - "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ - "#4nd%;f4y6,>£%-BZZZZZZZT") - - for(var/mob/living/silicon/ai/A in player_list) //AIs are always aware of communication blackouts. - to_chat(A, "
                    ") - to_chat(A, "[alert]") - to_chat(A, "
                    ") - - if(prob(30)) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. - command_announcement.Announce(alert, new_sound = sound('sound/misc/interference.ogg', volume=25)) - - -/datum/event/communications_blackout/start() - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) - for(var/obj/machinery/exonet_node/N in machines) - N.emp_act(1) +/datum/event/communications_blackout/announce() + var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ + "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ + "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ + "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ + "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ + "#4nd%;f4y6,>£%-BZZZZZZZT") + + for(var/mob/living/silicon/ai/A in player_list) //AIs are always aware of communication blackouts. + to_chat(A, "
                    ") + to_chat(A, "[alert]") + to_chat(A, "
                    ") + + if(prob(30)) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. + command_announcement.Announce(alert, new_sound = sound('sound/misc/interference.ogg', volume=25)) + + +/datum/event/communications_blackout/start() + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) + for(var/obj/machinery/exonet_node/N in machines) + N.emp_act(1) diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 31ae5dee326..5e8950d2806 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -1,34 +1,34 @@ -/datum/event/dust - startWhen = 10 - endWhen = 30 - -/datum/event/dust/start() - affecting_z -= global.using_map.sealed_levels // Space levels only please! - ..() - -/datum/event/dust/announce() - if(!victim) - command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert") - -/datum/event/dust/tick() - if(prob(10)) - dust_swarm(severity, affecting_z) - -/datum/event/dust/end() - ..() - if(!victim) - command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice") - -/datum/event/dust/proc/get_severity() - switch(severity) - if(EVENT_LEVEL_MUNDANE) - return "weak" - if(EVENT_LEVEL_MODERATE) - return prob(80) ? "norm" : "strong" - if(EVENT_LEVEL_MAJOR) - return "super" - return "weak" - -// Overmap version -/datum/event/dust/overmap/announce() +/datum/event/dust + startWhen = 10 + endWhen = 30 + +/datum/event/dust/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() + +/datum/event/dust/announce() + if(!victim) + command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert") + +/datum/event/dust/tick() + if(prob(10)) + dust_swarm(severity, affecting_z) + +/datum/event/dust/end() + ..() + if(!victim) + command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice") + +/datum/event/dust/proc/get_severity() + switch(severity) + if(EVENT_LEVEL_MUNDANE) + return "weak" + if(EVENT_LEVEL_MODERATE) + return prob(80) ? "norm" : "strong" + if(EVENT_LEVEL_MAJOR) + return "super" + return "weak" + +// Overmap version +/datum/event/dust/overmap/announce() return \ No newline at end of file diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 6b2998c2b36..5f9c8373b56 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -1,73 +1,73 @@ -/datum/event/electrical_storm - announceWhen = 0 // Warn them shortly before it begins. - startWhen = 30 // 1 minute - endWhen = 60 // Set in setup() - has_skybox_image = TRUE - var/tmp/lightning_color - var/tmp/list/valid_apcs // List of valid APCs. - -/datum/event/electrical_storm/get_skybox_image() - if(!lightning_color) - lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc") - var/image/res = image('icons/skybox/electrobox.dmi', "lightning") - res.color = lightning_color - res.appearance_flags = RESET_COLOR - res.blend_mode = BLEND_ADD - return res - -/datum/event/electrical_storm/announce() - ..() - switch(severity) - if(EVENT_LEVEL_MUNDANE) - command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array") - if(EVENT_LEVEL_MODERATE) - command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") - if(EVENT_LEVEL_MAJOR) - command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") - -/datum/event/electrical_storm/start() - ..() - valid_apcs = list() - for(var/obj/machinery/power/apc/A in GLOB.apcs) - if(A.z in affecting_z) - valid_apcs.Add(A) - endWhen = (severity * 60) + startWhen - -/datum/event/electrical_storm/tick() - ..() - // See if shields can stop it first - var/list/shields = list() - for(var/obj/machinery/power/shield_generator/G in global.machines) - if((G.z in affecting_z) && G.running && G.check_flag(MODEFLAG_EM)) - shields += G - if(shields.len) - var/obj/machinery/power/shield_generator/shield_gen = pick(shields) - //Minor breaches aren't enough to let through frying amounts of power - if(shield_gen.deal_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR) - return - if(!valid_apcs.len) - log_debug("No valid APCs found for electrical storm event ship=[victim]!") - return - var/list/picked_apcs = list() - for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity - picked_apcs |= pick(valid_apcs) - for(var/obj/machinery/power/apc/T in picked_apcs) - affect_apc(T) - -/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T) - // Main breaker is turned off. Consider this APC protected. - if(!T.operating) - return - - // Decent chance to overload lighting circuit. - if(prob(3 * severity)) - T.overload_lighting() - - // Relatively small chance to emag the apc as apc_damage event does. - if(prob(0.2 * severity)) - T.emagged = 1 - T.update_icon() - -// Overmap version -/datum/event/electrical_storm/overmap/announce() +/datum/event/electrical_storm + announceWhen = 0 // Warn them shortly before it begins. + startWhen = 30 // 1 minute + endWhen = 60 // Set in setup() + has_skybox_image = TRUE + var/tmp/lightning_color + var/tmp/list/valid_apcs // List of valid APCs. + +/datum/event/electrical_storm/get_skybox_image() + if(!lightning_color) + lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc") + var/image/res = image('icons/skybox/electrobox.dmi', "lightning") + res.color = lightning_color + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res + +/datum/event/electrical_storm/announce() + ..() + switch(severity) + if(EVENT_LEVEL_MUNDANE) + command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MODERATE) + command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MAJOR) + command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") + +/datum/event/electrical_storm/start() + ..() + valid_apcs = list() + for(var/obj/machinery/power/apc/A in GLOB.apcs) + if(A.z in affecting_z) + valid_apcs.Add(A) + endWhen = (severity * 60) + startWhen + +/datum/event/electrical_storm/tick() + ..() + // See if shields can stop it first + var/list/shields = list() + for(var/obj/machinery/power/shield_generator/G in global.machines) + if((G.z in affecting_z) && G.running && G.check_flag(MODEFLAG_EM)) + shields += G + if(shields.len) + var/obj/machinery/power/shield_generator/shield_gen = pick(shields) + //Minor breaches aren't enough to let through frying amounts of power + if(shield_gen.deal_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR) + return + if(!valid_apcs.len) + // log_debug("No valid APCs found for electrical storm event ship=[victim]!") // Let's not spam poor people with debug logs on (me) + return + var/list/picked_apcs = list() + for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity + picked_apcs |= pick(valid_apcs) + for(var/obj/machinery/power/apc/T in picked_apcs) + affect_apc(T) + +/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T) + // Main breaker is turned off. Consider this APC protected. + if(!T.operating) + return + + // Decent chance to overload lighting circuit. + if(prob(3 * severity)) + T.overload_lighting() + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(0.2 * severity)) + T.emagged = 1 + T.update_icon() + +// Overmap version +/datum/event/electrical_storm/overmap/announce() return \ No newline at end of file diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index 0b48f77fc3c..a6eb9be0041 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,193 +1,193 @@ -// Event Meta instances represent choices for the event manager to choose for random events. -/datum/event_meta - var/name = "" - var/enabled = 1 // Whether or not the event is available for random selection at all - var/weight = 0 // The base weight of this event. A zero means it may never fire, but see get_weight() - var/min_weight = 0 // The minimum weight that this event will have. Only used if non-zero. - var/max_weight = 0 // The maximum weight that this event will have. Only use if non-zero. - var/severity = 0 // The current severity of this event - var/one_shot = 0 // If true, then the event will not be re-added to the list of available events - var/add_to_queue= 1 // If true, add back to the queue of events upon finishing. - var/list/role_weights = list() - var/list/min_job_count = list() - var/datum/event/event_type - -/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/is_one_shot = 0, var/min_event_weight = 0, var/max_event_weight = 0, var/add_to_queue = 1, var/list/min_jobs) - name = event_name - severity = event_severity - event_type = type - one_shot = is_one_shot - weight = event_weight - min_weight = min_event_weight - max_weight = max_event_weight - src.add_to_queue = add_to_queue - if(job_weights) - role_weights = job_weights - if(min_jobs) - min_job_count = min_jobs - - -/datum/event_meta/proc/get_weight(var/list/active_with_role) - if(!enabled) - return 0 - - var/job_weight = 0 - for(var/role in role_weights) - if(role in active_with_role) - job_weight += active_with_role[role] * role_weights[role] - - var/total_weight = weight + job_weight - - // Only min/max the weight if the values are non-zero - if(min_weight && total_weight < min_weight) total_weight = min_weight - if(max_weight && total_weight > max_weight) total_weight = max_weight - - return total_weight - -/datum/event_meta/proc/minimum_active(var/list/active_with_role) - var/can_fire = TRUE - for(var/role in min_job_count) - if(role in active_with_role) - if(active_with_role[role] < min_job_count[role]) - can_fire = FALSE - break - - return can_fire - -/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used - return global.using_map.use_overmap ? 0 : ..() - -// Event datums define and execute the actual events themselves. -/datum/event //NOTE: Times are measured in master controller ticks! - var/startWhen = 0 //When in the lifetime to call start(). - var/announceWhen = 0 //When in the lifetime to call announce(). - var/endWhen = 0 //When in the lifetime the event should end. - - var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). - var/activeFor = 0 //How long the event has existed. You don't need to change this. - var/isRunning = TRUE //If this event is currently running. You should not change this. - var/startedAt = 0 //When this event started. - var/endedAt = 0 //When this event ended. - var/processing_active = TRUE - var/datum/event_meta/event_meta = null - var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels) - var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox. - var/obj/effect/overmap/visitable/ship/victim = null // Ship this event is acting upon (If this is event is due to overmap travel).nt etc. - -/datum/event/nothing - -//Called first before processing. -//Allows you to setup your event, such as randomly -//setting the startWhen and or announceWhen variables. -//Only called once. -/datum/event/proc/setup() - return - -//Called when the tick is equal to the startWhen variable. -//Allows you to start before announcing or vice versa. -//Only called once. -/datum/event/proc/start() - if(has_skybox_image) - SSskybox.rebuild_skyboxes(affecting_z) - return - -//Called when the tick is equal to the announceWhen variable. -//Allows you to announce before starting or vice versa. -//Only called once. -/datum/event/proc/announce() - return - -//Called on or after the tick counter is equal to startWhen. -//You can include code related to your event or add your own -//time stamped events. -//Called more than once. -/datum/event/proc/tick() - return - -//Called on or after the tick is equal or more than endWhen -//You can include code related to the event ending. -//Do not place spawn() in here, instead use tick() to check for -//the activeFor variable. -//For example: if(activeFor == myOwnVariable + 30) doStuff() -//Only called once. -/datum/event/proc/end() - if(has_skybox_image) - SSskybox.rebuild_skyboxes(affecting_z) - return - -//Returns the latest point of event processing. -/datum/event/proc/lastProcessAt() - return max(startWhen, max(announceWhen, endWhen)) - -//Do not override this proc, instead use the appropiate procs. -//This proc will handle the calls to the appropiate procs. -/datum/event/process() - if(activeFor > startWhen && activeFor < endWhen) - processing_active = FALSE - tick() - processing_active = TRUE - - if(activeFor == startWhen) - isRunning = TRUE - processing_active = FALSE - start() - processing_active = TRUE - - if(activeFor == announceWhen) - processing_active = FALSE - announce() - processing_active = TRUE - - if(activeFor == endWhen) - isRunning = FALSE - processing_active = FALSE - end() - processing_active = TRUE - - // Everything is done, let's clean up. - if(activeFor >= lastProcessAt()) - kill() - - activeFor++ - -//Called when start(), announce() and end() has all been called. -/datum/event/proc/kill(external_use = FALSE) - // If this event was forcefully killed run end() for individual cleanup - if(isRunning) - isRunning = 0 - end() - - endedAt = world.time - if(!external_use) - SSevents.event_complete(src) - -//Called during building of skybox to get overlays -/datum/event/proc/get_skybox_image() - return - -/datum/event/New(var/datum/event_meta/EM, external_use = FALSE) - // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons - if(!external_use) - SSevents.active_events += src - - event_meta = EM - severity = event_meta.severity - if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE - if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR - - startedAt = world.time - - if(!affecting_z) - affecting_z = using_map.station_levels.Copy() - - setup() - ..() - -/datum/event/Destroy() - victim = null - . = ..() - -/datum/event/proc/location_name() - if(victim) - return victim.name - return station_name() +// Event Meta instances represent choices for the event manager to choose for random events. +/datum/event_meta + var/name = "" + var/enabled = 1 // Whether or not the event is available for random selection at all + var/weight = 0 // The base weight of this event. A zero means it may never fire, but see get_weight() + var/min_weight = 0 // The minimum weight that this event will have. Only used if non-zero. + var/max_weight = 0 // The maximum weight that this event will have. Only use if non-zero. + var/severity = 0 // The current severity of this event + var/one_shot = 0 // If true, then the event will not be re-added to the list of available events + var/add_to_queue= 1 // If true, add back to the queue of events upon finishing. + var/list/role_weights = list() + var/list/min_job_count = list() + var/datum/event/event_type + +/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/is_one_shot = 0, var/min_event_weight = 0, var/max_event_weight = 0, var/add_to_queue = 1, var/list/min_jobs) + name = event_name + severity = event_severity + event_type = type + one_shot = is_one_shot + weight = event_weight + min_weight = min_event_weight + max_weight = max_event_weight + src.add_to_queue = add_to_queue + if(job_weights) + role_weights = job_weights + if(min_jobs) + min_job_count = min_jobs + + +/datum/event_meta/proc/get_weight(var/list/active_with_role) + if(!enabled) + return 0 + + var/job_weight = 0 + for(var/role in role_weights) + if(role in active_with_role) + job_weight += active_with_role[role] * role_weights[role] + + var/total_weight = weight + job_weight + + // Only min/max the weight if the values are non-zero + if(min_weight && total_weight < min_weight) total_weight = min_weight + if(max_weight && total_weight > max_weight) total_weight = max_weight + + return total_weight + +/datum/event_meta/proc/minimum_active(var/list/active_with_role) + var/can_fire = TRUE + for(var/role in min_job_count) + if(role in active_with_role) + if(active_with_role[role] < min_job_count[role]) + can_fire = FALSE + break + + return can_fire + +/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used + return global.using_map.use_overmap ? 0 : ..() + +// Event datums define and execute the actual events themselves. +/datum/event //NOTE: Times are measured in master controller ticks! + var/startWhen = 0 //When in the lifetime to call start(). + var/announceWhen = 0 //When in the lifetime to call announce(). + var/endWhen = 0 //When in the lifetime the event should end. + + var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). + var/activeFor = 0 //How long the event has existed. You don't need to change this. + var/isRunning = TRUE //If this event is currently running. You should not change this. + var/startedAt = 0 //When this event started. + var/endedAt = 0 //When this event ended. + var/processing_active = TRUE + var/datum/event_meta/event_meta = null + var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels) + var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox. + var/obj/effect/overmap/visitable/ship/victim = null // Ship this event is acting upon (If this is event is due to overmap travel).nt etc. + +/datum/event/nothing + +//Called first before processing. +//Allows you to setup your event, such as randomly +//setting the startWhen and or announceWhen variables. +//Only called once. +/datum/event/proc/setup() + return + +//Called when the tick is equal to the startWhen variable. +//Allows you to start before announcing or vice versa. +//Only called once. +/datum/event/proc/start() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) + return + +//Called when the tick is equal to the announceWhen variable. +//Allows you to announce before starting or vice versa. +//Only called once. +/datum/event/proc/announce() + return + +//Called on or after the tick counter is equal to startWhen. +//You can include code related to your event or add your own +//time stamped events. +//Called more than once. +/datum/event/proc/tick() + return + +//Called on or after the tick is equal or more than endWhen +//You can include code related to the event ending. +//Do not place spawn() in here, instead use tick() to check for +//the activeFor variable. +//For example: if(activeFor == myOwnVariable + 30) doStuff() +//Only called once. +/datum/event/proc/end() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) + return + +//Returns the latest point of event processing. +/datum/event/proc/lastProcessAt() + return max(startWhen, max(announceWhen, endWhen)) + +//Do not override this proc, instead use the appropiate procs. +//This proc will handle the calls to the appropiate procs. +/datum/event/process() + if(activeFor > startWhen && activeFor < endWhen) + processing_active = FALSE + tick() + processing_active = TRUE + + if(activeFor == startWhen) + isRunning = TRUE + processing_active = FALSE + start() + processing_active = TRUE + + if(activeFor == announceWhen) + processing_active = FALSE + announce() + processing_active = TRUE + + if(activeFor == endWhen) + isRunning = FALSE + processing_active = FALSE + end() + processing_active = TRUE + + // Everything is done, let's clean up. + if(activeFor >= lastProcessAt()) + kill() + + activeFor++ + +//Called when start(), announce() and end() has all been called. +/datum/event/proc/kill(external_use = FALSE) + // If this event was forcefully killed run end() for individual cleanup + if(isRunning) + isRunning = 0 + end() + + endedAt = world.time + if(!external_use) + SSevents.event_complete(src) + +//Called during building of skybox to get overlays +/datum/event/proc/get_skybox_image() + return + +/datum/event/New(var/datum/event_meta/EM, external_use = FALSE) + // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons + if(!external_use) + SSevents.active_events += src + + event_meta = EM + severity = event_meta.severity + if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE + if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR + + startedAt = world.time + + if(!affecting_z) + affecting_z = using_map.station_levels.Copy() + + setup() + ..() + +/datum/event/Destroy() + victim = null + . = ..() + +/datum/event/proc/location_name() + if(victim) + return victim.name + return station_name() diff --git a/code/modules/events/event_manager.dm b/code/modules/events/event_manager.dm index f8dca1824ba..9a03be5ab20 100644 --- a/code/modules/events/event_manager.dm +++ b/code/modules/events/event_manager.dm @@ -1,240 +1,240 @@ -//The UI portion. Should probably be made its own thing/made into a NanoUI thing later. -/datum/controller/subsystem/events - var/window_x = 700 - var/window_y = 600 - var/report_at_round_end = 0 - var/table_options = " align='center'" - var/row_options1 = " width='85px'" - var/row_options2 = " width='260px'" - var/row_options3 = " width='150px'" - var/datum/event_container/selected_event_container = null - -/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) - - var/html = GetInteractWindow() - - var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) - popup.set_content(html) - popup.open() - -/datum/controller/subsystem/events/proc/GetInteractWindow() - var/html = "Refresh" - html += "Pause All - [config.allow_random_events ? "Pause" : "Resume"]" - - if(selected_event_container) - var/event_time = max(0, selected_event_container.next_event_time - world.time) - html += "Back
                    " - html += "Time till start: [round(event_time / 600, 0.1)]
                    " - html += "
                    " - html += "

                    Available [severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

                    " - html += "" - html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" - var/list/active_with_role = number_active_with_role() - for(var/datum/event_meta/EM in selected_event_container.available_events) - html += "" - html += "[EM.name]" - html += "[EM.weight]" - html += "[EM.min_weight]" - html += "[EM.max_weight]" - html += "[EM.one_shot]" - html += "[EM.enabled]" - html += "[selected_event_container.get_weight(EM, active_with_role)]" - html += "Remove" - html += "" - html += "" - html += "
                    " - - html += "
                    " - html += "

                    Add Event

                    " - html += "" - html += "NameTypeWeightOneShot" - html += "" - html += "[new_event.name ? new_event.name : "Enter Event"]" - html += "[new_event.event_type ? new_event.event_type : "Select Type"]" - html += "[new_event.weight ? new_event.weight : 0]" - html += "[new_event.one_shot]" - html += "" - html += "" - html += "Add
                    " - html += "
                    " - else - html += "Round End Report: [report_at_round_end ? "On": "Off"]
                    " - html += "
                    " - html += "

                    Event Start

                    " - - html += "" - html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/next_event_at = max(0, EC.next_event_time - world.time) - html += "" - html += "[severity_to_string[severity]]" - html += "[worldtime2stationtime(max(EC.next_event_time, world.time))]" - html += "[round(next_event_at / 600, 0.1)]" - html += "" - html += "--" - html += "-" - html += "+" - html += "++" - html += "" - html += "" - html += "[EC.delayed ? "Resume" : "Pause"]" - html += "" - html += "" - html += "[EC.delay_modifier]" - html += "" - html += "" - html += "" - html += "
                    " - - html += "
                    " - html += "

                    Next Event

                    " - html += "" - html += "SeverityNameEvent RotationClear" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/datum/event_meta/EM = EC.next_event - html += "" - html += "[severity_to_string[severity]]" - html += "[EM ? EM.name : "Random"]" - html += "View" - html += "Clear" - html += "" - html += "" - html += "
                    " - - html += "
                    " - html += "

                    Running Events

                    " - html += "Estimated times, affected by process scheduler delays." - html += "" - html += "SeverityNameEnds AtEnds InStop" - for(var/datum/event/E in active_events) - if(!E.event_meta) - continue - var/datum/event_meta/EM = E.event_meta - var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the alarm manager processes - var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) - html += "" - html += "[severity_to_string[EM.severity]]" - html += "[EM.name]" - html += "[worldtime2stationtime(ends_at)]" - html += "[ends_in]" - html += "Stop" - html += "" - html += "" - html += "
                    " - - return html - -/datum/controller/subsystem/events/Topic(href, href_list) - if(..()) - return - - if(href_list["toggle_report"]) - report_at_round_end = !report_at_round_end - log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") - else if(href_list["dec_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/decrease = 60 * (10 ** text2num(href_list["dec_timer"])) - EC.next_event_time -= decrease - log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") - else if(href_list["inc_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/increase = 60 * (10 ** text2num(href_list["inc_timer"])) - EC.next_event_time += increase - log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") - else if(href_list["select_event"]) - var/datum/event_container/EC = locate(href_list["select_event"]) - var/datum/event_meta/EM = EC.SelectEvent() - if(EM) - log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") - else if(href_list["pause"]) - var/datum/event_container/EC = locate(href_list["pause"]) - EC.delayed = !EC.delayed - log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") - else if(href_list["pause_all"]) - config.allow_random_events = text2num(href_list["pause_all"]) - log_and_message_admins("has [config.allow_random_events ? "resumed" : "paused"] countdown for all events.") - else if(href_list["interval"]) - var/delay = tgui_input_number(usr, "Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") - if(delay && delay > 0) - var/datum/event_container/EC = locate(href_list["interval"]) - EC.delay_modifier = delay - log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") - else if(href_list["stop"]) - if(tgui_alert(usr, "Stopping an event may have unintended side-effects. Continue?","Stopping Event!",list("Yes","No")) != "Yes") - return - var/datum/event/E = locate(href_list["stop"]) - var/datum/event_meta/EM = E.event_meta - log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") - E.kill() - else if(href_list["view_events"]) - selected_event_container = locate(href_list["view_events"]) - else if(href_list["back"]) - selected_event_container = null - else if(href_list["set_name"]) - var/name = sanitize(tgui_input_text(usr, "Enter event name.", "Set Name")) - if(name) - var/datum/event_meta/EM = locate(href_list["set_name"]) - EM.name = name - else if(href_list["set_type"]) - var/type = tgui_input_list(usr, "Select event type.", "Select", allEvents) - if(type) - var/datum/event_meta/EM = locate(href_list["set_type"]) - EM.event_type = type - else if(href_list["set_weight"]) - var/weight = tgui_input_number(usr, "Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") - if(weight && weight > 0) - var/datum/event_meta/EM = locate(href_list["set_weight"]) - EM.weight = weight - if(EM != new_event) - log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") - else if(href_list["toggle_oneshot"]) - var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) - EM.one_shot = !EM.one_shot - if(EM != new_event) - log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["toggle_enabled"]) - var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) - EM.enabled = !EM.enabled - log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["remove"]) - if(tgui_alert(usr, "This will remove the event from rotation. Continue?","Removing Event!",list("Yes","No")) != "Yes") - return - var/datum/event_meta/EM = locate(href_list["remove"]) - var/datum/event_container/EC = locate(href_list["EC"]) - EC.available_events -= EM - log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["add"]) - if(!new_event.name || !new_event.event_type) - return - if(tgui_alert(usr, "This will add a new event to the rotation. Continue?","Add Event!",list("Yes","No")) != "Yes") - return - new_event.severity = selected_event_container.severity - selected_event_container.available_events += new_event - log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") - new_event = new - else if(href_list["clear"]) - var/datum/event_container/EC = locate(href_list["clear"]) - if(EC.next_event) - log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") - EC.next_event = null - - Interact(usr) - -/client/proc/forceEvent(var/type in SSevents.allEvents) - set name = "Trigger Event (Debug Only)" - set category = "Debug" - - if(!holder) - return - - if(ispath(type)) - new type(new /datum/event_meta(EVENT_LEVEL_MAJOR)) - message_admins("[key_name_admin(usr)] has triggered an event. ([type])", 1) - -/client/proc/event_manager_panel() - set name = "Event Manager Panel" - set category = "Admin" - SSevents.Interact(usr) - feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +//The UI portion. Should probably be made its own thing/made into a NanoUI thing later. +/datum/controller/subsystem/events + var/window_x = 700 + var/window_y = 600 + var/report_at_round_end = 0 + var/table_options = " align='center'" + var/row_options1 = " width='85px'" + var/row_options2 = " width='260px'" + var/row_options3 = " width='150px'" + var/datum/event_container/selected_event_container = null + +/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) + + var/html = GetInteractWindow() + + var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) + popup.set_content(html) + popup.open() + +/datum/controller/subsystem/events/proc/GetInteractWindow() + var/html = "Refresh" + html += "Pause All - [config.allow_random_events ? "Pause" : "Resume"]" + + if(selected_event_container) + var/event_time = max(0, selected_event_container.next_event_time - world.time) + html += "Back
                    " + html += "Time till start: [round(event_time / 600, 0.1)]
                    " + html += "
                    " + html += "

                    Available [severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

                    " + html += "" + html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" + var/list/active_with_role = number_active_with_role() + for(var/datum/event_meta/EM in selected_event_container.available_events) + html += "" + html += "[EM.name]" + html += "[EM.weight]" + html += "[EM.min_weight]" + html += "[EM.max_weight]" + html += "[EM.one_shot]" + html += "[EM.enabled]" + html += "[selected_event_container.get_weight(EM, active_with_role)]" + html += "Remove" + html += "" + html += "" + html += "
                    " + + html += "
                    " + html += "

                    Add Event

                    " + html += "" + html += "NameTypeWeightOneShot" + html += "" + html += "[new_event.name ? new_event.name : "Enter Event"]" + html += "[new_event.event_type ? new_event.event_type : "Select Type"]" + html += "[new_event.weight ? new_event.weight : 0]" + html += "[new_event.one_shot]" + html += "" + html += "" + html += "Add
                    " + html += "
                    " + else + html += "Round End Report: [report_at_round_end ? "On": "Off"]
                    " + html += "
                    " + html += "

                    Event Start

                    " + + html += "" + html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/next_event_at = max(0, EC.next_event_time - world.time) + html += "" + html += "[severity_to_string[severity]]" + html += "[worldtime2stationtime(max(EC.next_event_time, world.time))]" + html += "[round(next_event_at / 600, 0.1)]" + html += "" + html += "--" + html += "-" + html += "+" + html += "++" + html += "" + html += "" + html += "[EC.delayed ? "Resume" : "Pause"]" + html += "" + html += "" + html += "[EC.delay_modifier]" + html += "" + html += "" + html += "" + html += "
                    " + + html += "
                    " + html += "

                    Next Event

                    " + html += "" + html += "SeverityNameEvent RotationClear" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/datum/event_meta/EM = EC.next_event + html += "" + html += "[severity_to_string[severity]]" + html += "[EM ? EM.name : "Random"]" + html += "View" + html += "Clear" + html += "" + html += "" + html += "
                    " + + html += "
                    " + html += "

                    Running Events

                    " + html += "Estimated times, affected by process scheduler delays." + html += "" + html += "SeverityNameEnds AtEnds InStop" + for(var/datum/event/E in active_events) + if(!E.event_meta) + continue + var/datum/event_meta/EM = E.event_meta + var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the alarm manager processes + var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) + html += "" + html += "[severity_to_string[EM.severity]]" + html += "[EM.name]" + html += "[worldtime2stationtime(ends_at)]" + html += "[ends_in]" + html += "Stop" + html += "" + html += "" + html += "
                    " + + return html + +/datum/controller/subsystem/events/Topic(href, href_list) + if(..()) + return + + if(href_list["toggle_report"]) + report_at_round_end = !report_at_round_end + log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") + else if(href_list["dec_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/decrease = 60 * (10 ** text2num(href_list["dec_timer"])) + EC.next_event_time -= decrease + log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") + else if(href_list["inc_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/increase = 60 * (10 ** text2num(href_list["inc_timer"])) + EC.next_event_time += increase + log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") + else if(href_list["select_event"]) + var/datum/event_container/EC = locate(href_list["select_event"]) + var/datum/event_meta/EM = EC.SelectEvent() + if(EM) + log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") + else if(href_list["pause"]) + var/datum/event_container/EC = locate(href_list["pause"]) + EC.delayed = !EC.delayed + log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") + else if(href_list["pause_all"]) + config.allow_random_events = text2num(href_list["pause_all"]) + log_and_message_admins("has [config.allow_random_events ? "resumed" : "paused"] countdown for all events.") + else if(href_list["interval"]) + var/delay = tgui_input_number(usr, "Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") + if(delay && delay > 0) + var/datum/event_container/EC = locate(href_list["interval"]) + EC.delay_modifier = delay + log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") + else if(href_list["stop"]) + if(tgui_alert(usr, "Stopping an event may have unintended side-effects. Continue?","Stopping Event!",list("Yes","No")) != "Yes") + return + var/datum/event/E = locate(href_list["stop"]) + var/datum/event_meta/EM = E.event_meta + log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") + E.kill() + else if(href_list["view_events"]) + selected_event_container = locate(href_list["view_events"]) + else if(href_list["back"]) + selected_event_container = null + else if(href_list["set_name"]) + var/name = sanitize(tgui_input_text(usr, "Enter event name.", "Set Name")) + if(name) + var/datum/event_meta/EM = locate(href_list["set_name"]) + EM.name = name + else if(href_list["set_type"]) + var/type = tgui_input_list(usr, "Select event type.", "Select", allEvents) + if(type) + var/datum/event_meta/EM = locate(href_list["set_type"]) + EM.event_type = type + else if(href_list["set_weight"]) + var/weight = tgui_input_number(usr, "Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") + if(weight && weight > 0) + var/datum/event_meta/EM = locate(href_list["set_weight"]) + EM.weight = weight + if(EM != new_event) + log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") + else if(href_list["toggle_oneshot"]) + var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) + EM.one_shot = !EM.one_shot + if(EM != new_event) + log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["toggle_enabled"]) + var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) + EM.enabled = !EM.enabled + log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["remove"]) + if(tgui_alert(usr, "This will remove the event from rotation. Continue?","Removing Event!",list("Yes","No")) != "Yes") + return + var/datum/event_meta/EM = locate(href_list["remove"]) + var/datum/event_container/EC = locate(href_list["EC"]) + EC.available_events -= EM + log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["add"]) + if(!new_event.name || !new_event.event_type) + return + if(tgui_alert(usr, "This will add a new event to the rotation. Continue?","Add Event!",list("Yes","No")) != "Yes") + return + new_event.severity = selected_event_container.severity + selected_event_container.available_events += new_event + log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") + new_event = new + else if(href_list["clear"]) + var/datum/event_container/EC = locate(href_list["clear"]) + if(EC.next_event) + log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") + EC.next_event = null + + Interact(usr) + +/client/proc/forceEvent(var/type in SSevents.allEvents) + set name = "Trigger Event (Debug Only)" + set category = "Debug" + + if(!holder) + return + + if(ispath(type)) + new type(new /datum/event_meta(EVENT_LEVEL_MAJOR)) + message_admins("[key_name_admin(usr)] has triggered an event. ([type])", 1) + +/client/proc/event_manager_panel() + set name = "Event Manager Panel" + set category = "Admin" + SSevents.Interact(usr) + feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/events/gravity.dm b/code/modules/events/gravity.dm index 926b35e054b..47565997b1b 100644 --- a/code/modules/events/gravity.dm +++ b/code/modules/events/gravity.dm @@ -1,62 +1,62 @@ -/datum/event/gravity - announceWhen = 5 - var/list/zLevels - var/list/generators = list() - -/datum/event/gravity/setup() - // Setup which levels we will disrupt gravit on. - zLevels = using_map.station_levels.Copy() - for(var/datum/planet/P in SSplanets.planets) - zLevels -= P.expected_z_levels - - for(var/obj/machinery/gravity_generator/main/GG in machines) - if((GG.z in zLevels) && GG.on) - generators += GG - - if(generators.len) - endWhen = rand(5 MINUTES, 20 MINUTES) - else - endWhen = rand(15, 60) - -/datum/event/gravity/announce() - if(generators.len) - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled. \ - Please wait for the system to reinitialize, or contact your engineering department.", "Gravity Failure") - else - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system \ - reinitializes. Please stand by while the gravity system reinitializes.", "Gravity Failure") - -/datum/event/gravity/start() - gravity_is_on = 0 - if(generators.len) - for(var/obj/machinery/gravity_generator/main/GG in generators) - if((GG.z in zLevels) && GG.on) - GG.breaker = FALSE - GG.set_power() - GG.charge_count = 10 - else - for(var/area/A in world) - if(A.z in zLevels) - A.gravitychange(gravity_is_on) - -/datum/event/gravity/end() - if(!gravity_is_on) - gravity_is_on = 1 - - - var/did_anything = FALSE - if(generators.len) - for(var/obj/machinery/gravity_generator/main/GG in generators) - if(!GG.on) - GG.breaker = TRUE - GG.set_power() - GG.charge_count = 90 - did_anything = TRUE - else - for(var/area/A in world) - if(A.z in zLevels) - A.gravitychange(gravity_is_on) - did_anything = TRUE - - if(did_anything) - command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") +/datum/event/gravity + announceWhen = 5 + var/list/zLevels + var/list/generators = list() + +/datum/event/gravity/setup() + // Setup which levels we will disrupt gravit on. + zLevels = using_map.station_levels.Copy() + for(var/datum/planet/P in SSplanets.planets) + zLevels -= P.expected_z_levels + + for(var/obj/machinery/gravity_generator/main/GG in machines) + if((GG.z in zLevels) && GG.on) + generators += GG + + if(generators.len) + endWhen = rand(5 MINUTES, 20 MINUTES) + else + endWhen = rand(15, 60) + +/datum/event/gravity/announce() + if(generators.len) + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled. \ + Please wait for the system to reinitialize, or contact your engineering department.", "Gravity Failure") + else + command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system \ + reinitializes. Please stand by while the gravity system reinitializes.", "Gravity Failure") + +/datum/event/gravity/start() + gravity_is_on = 0 + if(generators.len) + for(var/obj/machinery/gravity_generator/main/GG in generators) + if((GG.z in zLevels) && GG.on) + GG.breaker = FALSE + GG.set_power() + GG.charge_count = 10 + else + for(var/area/A in world) + if(A.z in zLevels) + A.gravitychange(gravity_is_on) + +/datum/event/gravity/end() + if(!gravity_is_on) + gravity_is_on = 1 + + + var/did_anything = FALSE + if(generators.len) + for(var/obj/machinery/gravity_generator/main/GG in generators) + if(!GG.on) + GG.breaker = TRUE + GG.set_power() + GG.charge_count = 90 + did_anything = TRUE + else + for(var/area/A in world) + if(A.z in zLevels) + A.gravitychange(gravity_is_on) + did_anything = TRUE + + if(did_anything) + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") diff --git a/code/modules/events/grubinfestation_vr.dm b/code/modules/events/grubinfestation_vr.dm index 34cbec6005c..c73c8feea3f 100644 --- a/code/modules/events/grubinfestation_vr.dm +++ b/code/modules/events/grubinfestation_vr.dm @@ -1,54 +1,54 @@ -/datum/event/grub_infestation - announceWhen = 90 - endWhen = 200 - var/spawncount = 1 - var/list/vents = list() - var/give_positions = 0 - -/datum/event/grub_infestation/setup() - announceWhen = rand(announceWhen, announceWhen + 60) - - spawncount = rand(2 * severity, 6 * severity) - - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - var/area/A = get_area(temp_vent) - if(A.forbid_events) - continue - if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) - if(temp_vent.network.normal_members.len > 50) - vents += temp_vent - -/datum/event/grub_infestation/announce() - command_announcement.Announce("Solargrubs detected coming aboard [station_name()]. Please clear them out before this starts to affect productivity. All crew efforts are appreciated and encouraged.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - -/datum/event/grub_infestation/start() - while((spawncount >= 1) && vents.len) - var/obj/vent = pick(vents) - var/mob/living/simple_mob/animal/solargrub_larva/larva = new(get_turf(vent)) - larva.tracked = TRUE - vents -= vent - spawncount-- - vents.Cut() - -/datum/event/grub_infestation/end() - var/list/area_names = list() - for(var/mob/living/G as anything in existing_solargrubs) - if(!G || G.stat == DEAD) - continue - if(istype(G, /mob/living/simple_mob/animal/solargrub_larva)) - var/mob/living/simple_mob/animal/solargrub_larva/L = G - if(!(L.tracked)) - continue - if(istype(G, /mob/living/simple_mob/vore/solargrub)) - var/mob/living/simple_mob/vore/solargrub/S = G - if(!(S.tracked)) - continue - var/area/grub_area = get_area(G) - if(!grub_area) //Huh, really? - if(!get_turf(G)) //No turf either? - qdel(G) //Must have been nullspaced - continue - area_names |= grub_area.name - if(area_names.len) - var/english_list = english_list(area_names) - command_announcement.Announce("Sensors have narrowed down remaining active solargrubs to the following areas: [english_list]", "Lifesign Alert") +/datum/event/grub_infestation + announceWhen = 90 + endWhen = 200 + var/spawncount = 1 + var/list/vents = list() + var/give_positions = 0 + +/datum/event/grub_infestation/setup() + announceWhen = rand(announceWhen, announceWhen + 60) + + spawncount = rand(2 * severity, 6 * severity) + + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + var/area/A = get_area(temp_vent) + if(A.forbid_events) + continue + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + +/datum/event/grub_infestation/announce() + command_announcement.Announce("Solargrubs detected coming aboard [station_name()]. Please clear them out before this starts to affect productivity. All crew efforts are appreciated and encouraged.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + +/datum/event/grub_infestation/start() + while((spawncount >= 1) && vents.len) + var/obj/vent = pick(vents) + var/mob/living/simple_mob/animal/solargrub_larva/larva = new(get_turf(vent)) + larva.tracked = TRUE + vents -= vent + spawncount-- + vents.Cut() + +/datum/event/grub_infestation/end() + var/list/area_names = list() + for(var/mob/living/G as anything in existing_solargrubs) + if(!G || G.stat == DEAD) + continue + if(istype(G, /mob/living/simple_mob/animal/solargrub_larva)) + var/mob/living/simple_mob/animal/solargrub_larva/L = G + if(!(L.tracked)) + continue + if(istype(G, /mob/living/simple_mob/vore/solargrub)) + var/mob/living/simple_mob/vore/solargrub/S = G + if(!(S.tracked)) + continue + var/area/grub_area = get_area(G) + if(!grub_area) //Huh, really? + if(!get_turf(G)) //No turf either? + qdel(G) //Must have been nullspaced + continue + area_names |= grub_area.name + if(area_names.len) + var/english_list = english_list(area_names) + command_announcement.Announce("Sensors have narrowed down remaining active solargrubs to the following areas: [english_list]", "Lifesign Alert") diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index 44b30fe2985..3f19f2dfcc6 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -1,74 +1,74 @@ -/datum/event/prison_break - startWhen = 5 - announceWhen = 75 - - var/releaseWhen = 60 - var/list/area/areas = list() //List of areas to affect. Filled by start() - - var/eventDept = "Security" //Department name in announcement - var/list/areaName = list("Brig") //Names of areas mentioned in AI and Engineering announcements - var/list/areaType = list(/area/security/prison, /area/security/brig) //Area types to include. - var/list/areaNotType = list() //Area types to specifically exclude. - -/datum/event/prison_break/virology - eventDept = "Medical" - areaName = list("Virology") - areaType = list(/area/medical/virology, /area/medical/virologyaccess) - -/datum/event/prison_break/xenobiology - eventDept = "Science" - areaName = list("Xenobiology") - areaType = list(/area/rnd/xenobiology) - areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) - -/datum/event/prison_break/station - eventDept = "Station" - areaName = list("Brig","Virology","Xenobiology") - areaType = list(/area/security/prison, /area/security/brig, /area/medical/virology, /area/medical/virologyaccess, /area/rnd/xenobiology) - areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) - - -/datum/event/prison_break/setup() - announceWhen = rand(75, 105) - releaseWhen = rand(60, 90) - - src.endWhen = src.releaseWhen+2 - - -/datum/event/prison_break/announce() - if(areas && areas.len > 0) - command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] detected in [station_name()] [(eventDept == "Security")? "imprisonment":"containment"] subroutines. Secure any compromised areas immediately. Involvement of [using_map.facility_type] AI is recommended.", "[eventDept] Alert") - - -/datum/event/prison_break/start() - for(var/area/A in world) - if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) - areas += A - - if(areas && areas.len > 0) - var/my_department = "[station_name()] firewall subroutines" - var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.
                    " - for(var/obj/machinery/message_server/MS in machines) - MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department].") - - else - to_world_log("ERROR: Could not initate grey-tide. Unable to find suitable containment area.") - kill() - - -/datum/event/prison_break/tick() - if(activeFor == releaseWhen) - if(areas && areas.len > 0) - var/obj/machinery/power/apc/theAPC = null - for(var/area/A in areas) - theAPC = A.get_apc() - if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights. - for(var/obj/machinery/light/L in A) - L.flicker(10) - - -/datum/event/prison_break/end() - for(var/area/A in shuffle(areas)) - A.prison_break() +/datum/event/prison_break + startWhen = 5 + announceWhen = 75 + + var/releaseWhen = 60 + var/list/area/areas = list() //List of areas to affect. Filled by start() + + var/eventDept = "Security" //Department name in announcement + var/list/areaName = list("Brig") //Names of areas mentioned in AI and Engineering announcements + var/list/areaType = list(/area/security/prison, /area/security/brig) //Area types to include. + var/list/areaNotType = list() //Area types to specifically exclude. + +/datum/event/prison_break/virology + eventDept = "Medical" + areaName = list("Virology") + areaType = list(/area/medical/virology, /area/medical/virologyaccess) + +/datum/event/prison_break/xenobiology + eventDept = "Science" + areaName = list("Xenobiology") + areaType = list(/area/rnd/xenobiology) + areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) + +/datum/event/prison_break/station + eventDept = "Station" + areaName = list("Brig","Virology","Xenobiology") + areaType = list(/area/security/prison, /area/security/brig, /area/medical/virology, /area/medical/virologyaccess, /area/rnd/xenobiology) + areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) + + +/datum/event/prison_break/setup() + announceWhen = rand(75, 105) + releaseWhen = rand(60, 90) + + src.endWhen = src.releaseWhen+2 + + +/datum/event/prison_break/announce() + if(areas && areas.len > 0) + command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] detected in [station_name()] [(eventDept == "Security")? "imprisonment":"containment"] subroutines. Secure any compromised areas immediately. Involvement of [using_map.facility_type] AI is recommended.", "[eventDept] Alert") + + +/datum/event/prison_break/start() + for(var/area/A in world) + if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) + areas += A + + if(areas && areas.len > 0) + var/my_department = "[station_name()] firewall subroutines" + var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.
                    " + for(var/obj/machinery/message_server/MS in machines) + MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department].") + + else + to_world_log("ERROR: Could not initate grey-tide. Unable to find suitable containment area.") + kill() + + +/datum/event/prison_break/tick() + if(activeFor == releaseWhen) + if(areas && areas.len > 0) + var/obj/machinery/power/apc/theAPC = null + for(var/area/A in areas) + theAPC = A.get_apc() + if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights. + for(var/obj/machinery/light/L in A) + L.flicker(10) + + +/datum/event/prison_break/end() + for(var/area/A in shuffle(areas)) + A.prison_break() diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm index a8668e561a4..1223b8afe09 100644 --- a/code/modules/events/radiation_storm.dm +++ b/code/modules/events/radiation_storm.dm @@ -1,56 +1,60 @@ -/datum/event/radiation_storm - var/const/enterBelt = 30 - var/const/radIntervall = 5 // Enough time between enter/leave belt for 10 hits, as per original implementation - var/const/leaveBelt = 80 - var/const/revokeAccess = 165 - startWhen = 2 - announceWhen = 1 - endWhen = revokeAccess - var/postStartTicks = 0 - -/datum/event/radiation_storm/announce() - command_announcement.Announce("High levels of radiation detected near \the [station_name()]. Please evacuate into one of the shielded maintenance tunnels or dorms.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') //VOREStation Edit - Dorms ref - -/datum/event/radiation_storm/start() - make_maint_all_access() - -/datum/event/radiation_storm/tick() - if(activeFor == enterBelt) - command_announcement.Announce("The [using_map.facility_type] has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") - radiate() - - if(activeFor >= enterBelt && activeFor <= leaveBelt) - postStartTicks++ - - if(postStartTicks == radIntervall) - postStartTicks = 0 - radiate() - - else if(activeFor == leaveBelt) - command_announcement.Announce("The [using_map.facility_type] has passed the radiation belt. Please allow for up to one minute while radiation levels dissipate, and report to medbay if you experience any unusual symptoms. Maintenance will lose all access again shortly.", "Anomaly Alert") -/datum/event/radiation_storm/proc/radiate() - var/radiation_level = rand(15, 35) - for(var/z in using_map.station_levels) - SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) - - for(var/mob/living/carbon/C in living_mob_list) - var/area/A = get_area(C) - if(!A) - continue - if(A.flags & RAD_SHIELDED) - continue - if(istype(C,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = C - if(prob(5)) - if (prob(75)) - randmutb(H) // Applies bad mutation - domutcheck(H,null,MUTCHK_FORCED) - else - randmutg(H) // Applies good mutation - domutcheck(H,null,MUTCHK_FORCED) - -/datum/event/radiation_storm/end() - revoke_maint_all_access() - -/datum/event/radiation_storm/syndicate/radiate() - return +/datum/event/radiation_storm + var/const/enterBelt = 30 + var/const/radIntervall = 5 // Enough time between enter/leave belt for 10 hits, as per original implementation + var/const/leaveBelt = 80 + var/const/revokeAccess = 165 + startWhen = 2 + announceWhen = 1 + endWhen = revokeAccess + var/postStartTicks = 0 + +/datum/event/radiation_storm/announce() + command_announcement.Announce("High levels of radiation detected near \the [station_name()]. Please evacuate into one of the shielded maintenance tunnels or dorms.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') //VOREStation Edit - Dorms ref + +/datum/event/radiation_storm/start() + make_maint_all_access() + +/datum/event/radiation_storm/tick() + if(activeFor == enterBelt) + command_announcement.Announce("The [using_map.facility_type] has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + radiate() + + if(activeFor >= enterBelt && activeFor <= leaveBelt) + postStartTicks++ + + if(postStartTicks == radIntervall) + postStartTicks = 0 + radiate() + + else if(activeFor == leaveBelt) + command_announcement.Announce("The [using_map.facility_type] has passed the radiation belt. Please allow for up to one minute while radiation levels dissipate, and report to medbay if you experience any unusual symptoms. Maintenance will lose all access again shortly.", "Anomaly Alert") +/datum/event/radiation_storm/proc/radiate() + var/radiation_level = rand(15, 35) + for(var/z in using_map.station_levels) + SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) + + for(var/mob/living/carbon/C in living_mob_list) + if(!(C.z in using_map.station_levels) || C.isSynthetic() || isbelly(C.loc)) + continue + var/area/A = get_area(C) + if(!A) + continue + if(A.flags & RAD_SHIELDED) + continue + if(istype(C,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + var/chance = 5.0 + chance -= (chance / 100) * C.getarmor(null, "rad") + if(prob(chance)) + if (prob(75)) + randmutb(H) // Applies bad mutation + domutcheck(H,null,MUTCHK_FORCED) + else + randmutg(H) // Applies good mutation + domutcheck(H,null,MUTCHK_FORCED) + +/datum/event/radiation_storm/end() + revoke_maint_all_access() + +/datum/event/radiation_storm/syndicate/radiate() + return diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index fef2e3f6b17..48c9b91c22c 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -1,11 +1,11 @@ -/var/global/spacevines_spawned = 0 - -/datum/event/spacevine - announceWhen = 60 - -/datum/event/spacevine/start() - spacevine_infestation() - spacevines_spawned = 1 - -/datum/event/spacevine/announce() - level_seven_announcement() +/var/global/spacevines_spawned = 0 + +/datum/event/spacevine + announceWhen = 60 + +/datum/event/spacevine/start() + spacevine_infestation() + spacevines_spawned = 1 + +/datum/event/spacevine/announce() + level_seven_announcement() diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index 7cba7cbbd39..ad6b02c81ed 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -1,30 +1,30 @@ -/var/global/sent_spiders_to_station = 0 - -/datum/event/spider_infestation - announceWhen = 90 - var/spawncount = 1 - - -/datum/event/spider_infestation/setup() - announceWhen = rand(announceWhen, announceWhen + 60) - spawncount = rand(4 * severity, 6 * severity) //spiderlings only have a 50% chance to grow big and strong - sent_spiders_to_station = 0 - -/datum/event/spider_infestation/announce() - command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - - -/datum/event/spider_infestation/start() - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) - if(temp_vent.network.normal_members.len > 50) - var/area/A = get_area(temp_vent) - if(!(A.forbid_events)) - vents += temp_vent - - while((spawncount >= 1) && vents.len) - var/obj/vent = pick(vents) - new /obj/effect/spider/spiderling/virgo(vent.loc) //VOREStation Edit - No nurses - vents -= vent - spawncount-- +/var/global/sent_spiders_to_station = 0 + +/datum/event/spider_infestation + announceWhen = 90 + var/spawncount = 1 + + +/datum/event/spider_infestation/setup() + announceWhen = rand(announceWhen, announceWhen + 60) + spawncount = rand(4 * severity, 6 * severity) //spiderlings only have a 50% chance to grow big and strong + sent_spiders_to_station = 0 + +/datum/event/spider_infestation/announce() + command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + + +/datum/event/spider_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in using_map.station_levels)) + if(temp_vent.network.normal_members.len > 50) + var/area/A = get_area(temp_vent) + if(!(A.forbid_events)) + vents += temp_vent + + while((spawncount >= 1) && vents.len) + var/obj/vent = pick(vents) + new /obj/effect/spider/spiderling/virgo(vent.loc) //VOREStation Edit - No nurses + vents -= vent + spawncount-- diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm index ddc392174e0..2009e17467c 100644 --- a/code/modules/events/spontaneous_appendicitis.dm +++ b/code/modules/events/spontaneous_appendicitis.dm @@ -1,4 +1,4 @@ -/datum/event/spontaneous_appendicitis/start() - for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) - if(H.client && H.appendicitis()) - break +/datum/event/spontaneous_appendicitis/start() + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + if(H.client && H.appendicitis()) + break diff --git a/code/modules/events/spontaneous_appendicitis_vr.dm b/code/modules/events/spontaneous_appendicitis_vr.dm index 8612a214576..d4d34894b9c 100644 --- a/code/modules/events/spontaneous_appendicitis_vr.dm +++ b/code/modules/events/spontaneous_appendicitis_vr.dm @@ -7,5 +7,7 @@ continue if(A.flags & RAD_SHIELDED) continue + if(isbelly(H.loc)) + continue if(H.client && H.appendicitis()) break diff --git a/code/modules/examine/descriptions/armor.dm b/code/modules/examine/descriptions/armor.dm index 9d987b635b6..2ea8c4e4a04 100644 --- a/code/modules/examine/descriptions/armor.dm +++ b/code/modules/examine/descriptions/armor.dm @@ -20,6 +20,28 @@ if(100) return "You would be immune to [descriptive_attack_type] if you wore this." +/obj/item/clothing/proc/describe_slowdown() + switch(slowdown) + if(-INFINITY to -0.1) + return "It looks like it might actually make you faster!" + if(0 || null) + return "It doesn't look like it'll impede your mobility." + if(0.5) + return "It might slow you down a little bit." + if(1) + return "It'll slow you down noticeably, but not too much." + if(1.5) + return "You'll be moving a fair bit slower than everyone else." + if(2) + return "Your speed will be noticeably impaired by its weight and inflexibility." + if(3) + return "You'd have a pretty hard time moving in it." + if(4) + return "It looks heavy enough to seriously impede your mobility." + if(5) + return "It's heavy enough that moving in it will be extremely difficult!" + else + return "It's difficult to tell how much it'll influence your speed." /obj/item/clothing/get_description_info() @@ -43,25 +65,33 @@ armor_stats += "\n" + armor_stats += "[describe_slowdown()] \n" + if(flags & AIRTIGHT) armor_stats += "It is airtight. \n" - if(min_pressure_protection == 0) + if(min_pressure_protection == 0 && max_pressure_protection >= WARNING_HIGH_PRESSURE) //0 to 325 + armor_stats += "Wearing this will protect you from the vacuum of space and from high pressures. \n" + else if(min_pressure_protection <= WARNING_LOW_PRESSURE && max_pressure_protection >= WARNING_HIGH_PRESSURE) //50 to 325 + armor_stats += "Wearing this will protect you from both low and high pressures, but not the vacuum of space. \n" + else if(min_pressure_protection == 0) armor_stats += "Wearing this will protect you from the vacuum of space. \n" - else if(min_pressure_protection != null) + else if(min_pressure_protection <= WARNING_LOW_PRESSURE) //50 or below armor_stats += "Wearing this will protect you from low pressures, but not the vacuum of space. \n" - - if(max_pressure_protection != null) + else if(max_pressure_protection >= WARNING_HIGH_PRESSURE) //325 or higher armor_stats += "Wearing this will protect you from high pressures. \n" - if(flags & THICKMATERIAL) + if(flags & THICKMATERIAL) //stops syringes armor_stats += "The material is exceptionally thick. \n" - if(max_heat_protection_temperature == FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE) - armor_stats += "It provides very good protection against fire and heat. \n" - - if(min_cold_protection_temperature == SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) - armor_stats += "It provides very good protection against very cold temperatures. \n" + if(max_heat_protection_temperature >= FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE && min_cold_protection_temperature <= SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) //30000 or higher and as low as 2 + armor_stats += "It provides exceptional protection from extremely high and low temperatures alike. \n" + else if(max_heat_protection_temperature >= SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE && min_cold_protection_temperature <= SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) //5000 or above, but less than 30000 + armor_stats += "It provides very good protection against hazardous temperatures at both extremes, but may not be sufficient for very high-intensity situations. \n" + else if(max_heat_protection_temperature >= FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE) //30000 or above + armor_stats += "It provides exceptional protection from extremely high temperatures. \n" + else if(min_cold_protection_temperature <= SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE) //2 or less + armor_stats += "It provides exceptional protection against very low temperatures. \n" var/list/covers = list() var/list/slots = list() diff --git a/code/modules/examine/descriptions/paperwork.dm b/code/modules/examine/descriptions/paperwork.dm index 6e837095fa1..75840d97747 100644 --- a/code/modules/examine/descriptions/paperwork.dm +++ b/code/modules/examine/descriptions/paperwork.dm @@ -1,21 +1,21 @@ -/obj/item/weapon/pen - description_info = {"This is an item for writing down your thoughts, on paper or elsewhere. The following special commands are available: -Pen and crayon commands -\[br\] : Creates a linebreak. -\[center\] - \[/center\] : Centers the text. -\[h1\] - \[/h1\] : Makes the text a first level heading. -\[h2\] - \[/h2\] : Makes the text a second level headin. -\[h3\] - \[/h3\] : Makes the text a third level heading. -\[b\] - \[/b\] : Makes the text bold. -\[i\] - \[/i\] : Makes the text italic. -\[u\] - \[/u\] : Makes the text underlined. -\[large\] - \[/large\] : Increases the size of the text. -\[sign\] : Inserts a signature of your name in a foolproof way. -\[field\] : Inserts an invisible field which lets you start type from there. Useful for forms. -\[date\] : Inserts today's station date. -\[time\] : Inserts the current station time. -Pen exclusive commands -\[small\] - \[/small\] : Decreases the size of the text. -\[list\] - \[/list\] : A list. -\[*\] : A dot used for lists. -\[hr\] : Adds a horizontal rule."} +/obj/item/weapon/pen + description_info = {"This is an item for writing down your thoughts, on paper or elsewhere. The following special commands are available: +Pen and crayon commands +\[br\] : Creates a linebreak. +\[center\] - \[/center\] : Centers the text. +\[h1\] - \[/h1\] : Makes the text a first level heading. +\[h2\] - \[/h2\] : Makes the text a second level headin. +\[h3\] - \[/h3\] : Makes the text a third level heading. +\[b\] - \[/b\] : Makes the text bold. +\[i\] - \[/i\] : Makes the text italic. +\[u\] - \[/u\] : Makes the text underlined. +\[large\] - \[/large\] : Increases the size of the text. +\[sign\] : Inserts a signature of your name in a foolproof way. +\[field\] : Inserts an invisible field which lets you start type from there. Useful for forms. +\[date\] : Inserts today's station date. +\[time\] : Inserts the current station time. +Pen exclusive commands +\[small\] - \[/small\] : Decreases the size of the text. +\[list\] - \[/list\] : A list. +\[*\] : A dot used for lists. +\[hr\] : Adds a horizontal rule."} diff --git a/code/modules/examine/examine.dm b/code/modules/examine/examine.dm index 6643e677d64..949afd4a6f8 100644 --- a/code/modules/examine/examine.dm +++ b/code/modules/examine/examine.dm @@ -112,7 +112,7 @@ var/list/results = A.examine(src) if(!results || !results.len) results = list("You were unable to examine that. Tell a developer!") - to_chat(src, "[jointext(results, "
                    ")]
                    ") + to_chat(src, "[jointext(results, "
                    ")]
                    ") update_examine_panel(A) /mob/proc/update_examine_panel(var/atom/A) diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 8f5bffa804c..b29eb65bc14 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -51,7 +51,7 @@ update_icon() /obj/item/weapon/material/fishing_rod/attackby(obj/item/I as obj, mob/user as mob) - if(I.is_wirecutter() && strung) + if(I.has_tool_quality(TOOL_WIRECUTTER) && strung) strung = FALSE to_chat(user, "You cut \the [src]'s string!") update_icon() diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index 49b1cc4bf22..53ffe228790 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -27,7 +27,7 @@ var/list/dreams = list( spawn(0) for(var/i = rand(1,4),i > 0, i--) - to_chat(src, "... [pick(dreams)] ...") + to_chat(src, span_blue("... [pick(dreams)] ...")) sleep(rand(40,70)) if(paralysis <= 0) dreaming = 0 diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index e4fc9ac89ca..f0dc850bd96 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -244,8 +244,8 @@ Gunshots/explosions/opening doors/less rare audio (done) /obj/effect/fake_attacker/attackby(var/obj/item/weapon/P as obj, mob/user as mob) step_away(src,my_target,2) for(var/mob/M in oviewers(world.view,my_target)) - to_chat(M, "[my_target] flails around wildly.") - my_target.show_message("[src] has been attacked by [my_target] ", 1) //Lazy. + to_chat(M, span_red("[my_target] flails around wildly.")) + my_target.show_message(span_red("[src] has been attacked by [my_target] "), 1) //Lazy. src.health -= P.force @@ -257,7 +257,7 @@ Gunshots/explosions/opening doors/less rare audio (done) step_away(src,my_target,2) if(prob(30)) for(var/mob/O in oviewers(world.view , my_target)) - to_chat(O, "[my_target] stumbles around.") + to_chat(O, span_red("[my_target] stumbles around.")) /obj/effect/fake_attacker/New() ..() @@ -304,7 +304,7 @@ Gunshots/explosions/opening doors/less rare audio (done) if(prob(15)) if(weapon_name) my_target << sound(pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg')) - my_target.show_message("[my_target] has been attacked with [weapon_name] by [src.name] ", 1) + my_target.show_message(span_red("[my_target] has been attacked with [weapon_name] by [src.name]!"), 1) my_target.halloss += 8 if(prob(20)) my_target.eye_blurry += 3 if(prob(33)) @@ -312,7 +312,7 @@ Gunshots/explosions/opening doors/less rare audio (done) fake_blood(my_target) else my_target << sound(pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg')) - my_target.show_message("[src.name] has punched [my_target]!", 1) + my_target.show_message(span_red("[src.name] has punched [my_target]!"), 1) my_target.halloss += 4 if(prob(33)) if(!locate(/obj/effect/overlay) in my_target.loc) diff --git a/code/modules/flufftext/look_up.dm b/code/modules/flufftext/look_up.dm index 46338290f9e..776022b4ab0 100644 --- a/code/modules/flufftext/look_up.dm +++ b/code/modules/flufftext/look_up.dm @@ -1,65 +1,65 @@ -// Implements a verb to make your character look upward, mostly intended for the surface. - -/mob/living/verb/look_up() - set name = "Look Up" - set category = "IC" - set desc = "Look above you, and hope there's no ceiling spiders." - - to_chat(usr, "You look upwards...") - - var/turf/T = get_turf(usr) - if(!T) // In null space. - to_chat(usr, span("warning", "You appear to be in a place without any sort of concept of direction. You have bigger problems to worry about.")) - return - - if(!T.is_outdoors()) // They're inside. - to_chat(usr, "You see nothing interesting.") - return - - else // They're outside and hopefully on a planet. - if(!(T.z in SSplanets.z_to_planet) || !(SSplanets.z_to_planet[T.z])) - to_chat(usr, span("warning", "You appear to be outside, but not on a planet... Something is wrong.")) - return - var/datum/planet/P = SSplanets.z_to_planet[T.z] - - var/datum/weather_holder/WH = P.weather_holder - - // Describe the current weather. - if(WH.current_weather.observed_message) - to_chat(usr, WH.current_weather.observed_message) - - // Describe the current weather. - if(WH.imminent_weather) - var/datum/weather/coming_weather = WH.allowed_weather_types[WH.imminent_weather] - to_chat(usr, coming_weather.imminent_transition_message) - - // If we can see the sky, we'll see things like sun position, phase of the moon, etc. - if(!WH.current_weather.sky_visible) - to_chat(usr, "You can't see the sky clearly due to the [WH.current_weather.name].") - else - // Sun-related output. - if(P.sun_name) - var/afternoon = P.current_time.seconds_stored > (P.current_time.seconds_in_day / 2) - - var/sun_message = null - switch(P.sun_position) - if(0 to 0.4) // Night - sun_message = "It is night time, [P.sun_name] is not visible." - if(0.4 to 0.5) // Twilight - sun_message = "The sky is in twilight, however [P.sun_name] is not visible." - if(0.5 to 0.7) // Sunrise/set. - sun_message = "[P.sun_name] is slowly [!afternoon ? "rising from" : "setting on"] the horizon." - if(0.7 to 0.9) // Morning/evening - sun_message = "[P.sun_name]'s position implies it is currently [!afternoon ? "early" : "late"] in the day." - if(0.9 to 1.0) // Noon - sun_message = "It's high noon. [P.sun_name] hangs directly above you." - - to_chat(usr, sun_message) - - // Now for the moon. - if(P.moon_name) - if(P.moon_phase == MOON_PHASE_NEW_MOON) - to_chat(usr, "[P.moon_name] is not visible. It must be a new moon.") - else - to_chat(usr, "[P.moon_name] appears to currently be a [P.moon_phase].") - +// Implements a verb to make your character look upward, mostly intended for the surface. + +/mob/living/verb/look_up() + set name = "Look Up" + set category = "IC" + set desc = "Look above you, and hope there's no ceiling spiders." + + to_chat(usr, "You look upwards...") + + var/turf/T = get_turf(usr) + if(!T) // In null space. + to_chat(usr, span("warning", "You appear to be in a place without any sort of concept of direction. You have bigger problems to worry about.")) + return + + if(!T.is_outdoors()) // They're inside. + to_chat(usr, "You see nothing interesting.") + return + + else // They're outside and hopefully on a planet. + if(!(T.z in SSplanets.z_to_planet) || !(SSplanets.z_to_planet[T.z])) + to_chat(usr, span("warning", "You appear to be outside, but not on a planet... Something is wrong.")) + return + var/datum/planet/P = SSplanets.z_to_planet[T.z] + + var/datum/weather_holder/WH = P.weather_holder + + // Describe the current weather. + if(WH.current_weather.observed_message) + to_chat(usr, WH.current_weather.observed_message) + + // Describe the current weather. + if(WH.imminent_weather) + var/datum/weather/coming_weather = WH.allowed_weather_types[WH.imminent_weather] + to_chat(usr, coming_weather.imminent_transition_message) + + // If we can see the sky, we'll see things like sun position, phase of the moon, etc. + if(!WH.current_weather.sky_visible) + to_chat(usr, "You can't see the sky clearly due to the [WH.current_weather.name].") + else + // Sun-related output. + if(P.sun_name) + var/afternoon = P.current_time.seconds_stored > (P.current_time.seconds_in_day / 2) + + var/sun_message = null + switch(P.sun_position) + if(0 to 0.4) // Night + sun_message = "It is night time, [P.sun_name] is not visible." + if(0.4 to 0.5) // Twilight + sun_message = "The sky is in twilight, however [P.sun_name] is not visible." + if(0.5 to 0.7) // Sunrise/set. + sun_message = "[P.sun_name] is slowly [!afternoon ? "rising from" : "setting on"] the horizon." + if(0.7 to 0.9) // Morning/evening + sun_message = "[P.sun_name]'s position implies it is currently [!afternoon ? "early" : "late"] in the day." + if(0.9 to 1.0) // Noon + sun_message = "It's high noon. [P.sun_name] hangs directly above you." + + to_chat(usr, sun_message) + + // Now for the moon. + if(P.moon_name) + if(P.moon_phase == MOON_PHASE_NEW_MOON) + to_chat(usr, "[P.moon_name] is not visible. It must be a new moon.") + else + to_chat(usr, "[P.moon_name] appears to currently be a [P.moon_phase].") + diff --git a/code/modules/food/food/cans.dm b/code/modules/food/food/cans.dm index fa25fc7f606..7e52a15d5c8 100644 --- a/code/modules/food/food/cans.dm +++ b/code/modules/food/food/cans.dm @@ -448,3 +448,104 @@ /obj/item/weapon/reagent_containers/food/drinks/cans/alecan/Initialize() . = ..() reagents.add_reagent("ale", 30) + +/////////////////////////ENERGY DRINKS///////////////////////// + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_peach + name = "\improper Nukies - Peach Blaster" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_peach" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_peach/Initialize() + . = ..() + reagents.add_reagent("nukie_peach", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_pear + name = "\improper Nukies - Great Pear" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_pear" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_pear/Initialize() + . = ..() + reagents.add_reagent("nukie_pear", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_cherry + name = "\improper Nukies - Popping Cherry" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_cherry" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_cherry/Initialize() + . = ..() + reagents.add_reagent("nukie_cherry", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_melon + name = "\improper Nukies - Melon Squirter" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_melon" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_melon/Initialize() + . = ..() + reagents.add_reagent("nukie_melon", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_banana + name = "\improper Nukies - Bursting Banana" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_banana" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_banana/Initialize() + . = ..() + reagents.add_reagent("nukie_banana", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_rose + name = "\improper Nukies - Insatiable Rose" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_rose" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_rose/Initialize() + . = ..() + reagents.add_reagent("nukie_rose", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_lemon + name = "\improper Nukies - Citrus Got Real" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_lemon" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_lemon/Initialize() + . = ..() + reagents.add_reagent("nukie_lemon", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_fruit + name = "\improper Nukies - Swelling Fruit" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_fruit" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_fruit/Initialize() + . = ..() + reagents.add_reagent("nukie_fruit", 60) + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_special + name = "\improper Nukies - Limited Edition" + desc = "Harness the power of the atom with this over-caffinated energy drink." + icon_state = "nukie_special" + center_of_mass = list("x"=16, "y"=8) + volume = 60 + +/obj/item/weapon/reagent_containers/food/drinks/cans/nukie_special/Initialize() + . = ..() + reagents.add_reagent("nukie_special", 60) diff --git a/code/modules/food/food/lunch.dm b/code/modules/food/food/lunch.dm index 5879a8e95ac..3a2527af234 100644 --- a/code/modules/food/food/lunch.dm +++ b/code/modules/food/food/lunch.dm @@ -25,7 +25,11 @@ var/list/lunchables_lunches_ = list(/obj/item/weapon/reagent_containers/food/sna /obj/item/weapon/reagent_containers/food/snacks/locust_cooked, /obj/item/weapon/reagent_containers/food/snacks/spicedmeatbun, /obj/item/weapon/reagent_containers/food/snacks/quicheslice/filled, - /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose) + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/glucose, + /obj/item/weapon/reagent_containers/food/snacks/packaged/sausageroll, + /obj/item/weapon/reagent_containers/food/snacks/packaged/pasty, + /obj/item/weapon/reagent_containers/food/snacks/packaged/scotchegg, + /obj/item/weapon/reagent_containers/food/snacks/packaged/porkpie) var/list/lunchables_snacks_ = list(/obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly, /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/cherryjelly, @@ -74,7 +78,11 @@ var/list/lunchables_snacks_ = list(/obj/item/weapon/reagent_containers/food/snac /obj/item/weapon/reagent_containers/food/snacks/packaged/lunacake, /obj/item/weapon/reagent_containers/food/snacks/packaged/darklunacake, /obj/item/weapon/reagent_containers/food/snacks/packaged/mochicake, - /obj/item/weapon/reagent_containers/food/snacks/packaged/spacetwinkie + /obj/item/weapon/reagent_containers/food/snacks/packaged/spacetwinkie, + /obj/item/weapon/storage/box/jaffacake, + /obj/item/weapon/storage/box/winegum, + /obj/item/weapon/storage/box/custardcream, + /obj/item/weapon/storage/box/bourbon ) var/list/lunchables_drinks_ = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola, @@ -96,7 +104,15 @@ var/list/lunchables_drinks_ = list(/obj/item/weapon/reagent_containers/food/drin /obj/item/weapon/reagent_containers/food/drinks/cans/sarsaparilla, /obj/item/weapon/reagent_containers/food/drinks/cans/straw_cola, /obj/item/weapon/reagent_containers/food/drinks/cans/apple_cola, - /obj/item/weapon/reagent_containers/food/drinks/cans/lemon_cola + /obj/item/weapon/reagent_containers/food/drinks/cans/lemon_cola, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_peach, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_pear, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_cherry, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_melon, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_banana, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_rose, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_lemon, + /obj/item/weapon/reagent_containers/food/drinks/cans/nukie_fruit ) // This default list is a bit different, it contains items we don't want diff --git a/code/modules/food/food/sandwich.dm b/code/modules/food/food/sandwich.dm index 738e3f4a461..e8fc9e9e1e6 100644 --- a/code/modules/food/food/sandwich.dm +++ b/code/modules/food/food/sandwich.dm @@ -23,16 +23,16 @@ sandwich_limit += 4 if(istype(W,/obj/item/weapon/material/shard)) - to_chat(user, "You hide [W] in \the [src].") + to_chat(user, span_blue("You hide [W] in \the [src].")) user.drop_item() W.loc = src update() return else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks)) if(src.contents.len > sandwich_limit) - to_chat(user, "If you put anything else on \the [src] it's going to collapse.") + to_chat(user, span_red("If you put anything else on \the [src] it's going to collapse.")) return - to_chat(user, "You layer [W] over \the [src].") + to_chat(user, span_blue("You layer [W] over \the [src].")) var/obj/item/weapon/reagent_containers/F = W F.reagents.trans_to_obj(src, F.reagents.total_volume) user.drop_item() @@ -82,7 +82,7 @@ . = ..() if(contents.len) var/obj/item/O = pick(contents) - . += "You think you can see [O.name] in there." + . += span_blue("You think you can see [O.name] in there.") /obj/item/weapon/reagent_containers/food/snacks/csandwich/attack(mob/M as mob, mob/user as mob, def_zone) @@ -97,6 +97,6 @@ H = M if(H && shard && M == user) //This needs a check for feeding the food to other people, but that could be abusable. - to_chat(H, "You lacerate your mouth on a [shard.name] in the sandwich!") + to_chat(H, span_red("You lacerate your mouth on a [shard.name] in the sandwich!")) H.adjustBruteLoss(5) //TODO: Target head if human. //This TODO has been here for 4 years. ..() diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index ab9c8f0b7ff..38142fc4667 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -300,11 +300,15 @@ if (W.w_class >= src.w_class || is_robot_module(W) || istype(W, /obj/item/weapon/holder)) return - to_chat(user, "You slip \the [W] inside \the [src].") - user.drop_from_inventory(W, src) - add_fingerprint(user) - contents += W - return + if(tgui_alert(user,"You can't slice \the [src] here. Would you like to hide \the [W] inside it instead?","No Cutting Surface!",list("Yes","No")) == "No") + to_chat(user, "You cannot slice \the [src] here! You need a table or at least a tray to do it.") + return + else + to_chat(user, "You slip \the [W] inside \the [src].") + user.drop_from_inventory(W, src) + add_fingerprint(user) + contents += W + return if (has_edge(W)) if (!can_slice_here) @@ -849,7 +853,7 @@ . = ..() new/obj/effect/decal/cleanable/egg_smudge(src.loc) src.reagents.splash(hit_atom, reagents.total_volume) - src.visible_message("[src.name] has been squashed.","You hear a smack.") + src.visible_message(span_red("[src.name] has been squashed."),span_red("You hear a smack.")) qdel(src) /obj/item/weapon/reagent_containers/food/snacks/egg/attackby(obj/item/weapon/W as obj, mob/user as mob) @@ -858,10 +862,10 @@ var/clr = C.colourName if(!(clr in list("blue","green","mime","orange","purple","rainbow","red","yellow"))) - to_chat(usr, "The egg refuses to take on this color!") + to_chat(usr, span_blue("The egg refuses to take on this color!")) return - to_chat(usr, "You color \the [src] [clr]") + to_chat(usr, span_blue("You color \the [src] [clr]")) icon_state = "egg-[clr]" else . = ..() @@ -948,10 +952,6 @@ nutriment_desc = list("turkey" = 3, "tofu" = 5, "goeyness" = 4) bitesize = 3 -/obj/item/weapon/reagent_containers/food/snacks/tofurkey/Initialize() - . = ..() - reagents.add_reagent("stoxin", 3) - /obj/item/weapon/reagent_containers/food/snacks/stuffing name = "Stuffing" desc = "Moist, peppery breadcrumbs for filling the body cavities of dead birds. Dig in!" @@ -1633,7 +1633,7 @@ /obj/item/weapon/reagent_containers/food/snacks/popcorn/On_Consume() if(prob(unpopped)) //lol ...what's the point? - to_chat(usr, "You bite down on an un-popped kernel!") + to_chat(usr, span_red("You bite down on an un-popped kernel!")) unpopped = max(0, unpopped-1) . = ..() @@ -2422,6 +2422,7 @@ filling_color = "#785210" center_of_mass = list("x"=16, "y"=8) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/meatballsoup/Initialize() . = ..() @@ -2434,6 +2435,7 @@ icon_state = "slimesoup" //nonexistant? - 3/1/2020 FIXED. roro's live on. - 7/14/2020 - The fuck are you smoking, roro's is stupid, name it slimesoup so it's clear wtf it is. filling_color = "#C4DBA0" bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/slimesoup/Initialize() . = ..() @@ -2447,6 +2449,7 @@ filling_color = "#FF0000" center_of_mass = list("x"=16, "y"=7) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/bloodsoup/Initialize() . = ..() @@ -2463,6 +2466,7 @@ nutriment_amt = 4 nutriment_desc = list("salt" = 1, "the worst joke" = 3) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/clownstears/Initialize() . = ..() @@ -2476,13 +2480,13 @@ trash = /obj/item/trash/snack_bowl filling_color = "#AFC4B5" center_of_mass = list("x"=16, "y"=8) - nutriment_amt = 8 nutriment_desc = list("carrot" = 2, "corn" = 2, "eggplant" = 2, "potato" = 2) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/vegetablesoup/Initialize() . = ..() - reagents.add_reagent("water", 5) + reagents.add_reagent("vegetable_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/nettlesoup name = "Nettle soup" @@ -2494,6 +2498,7 @@ nutriment_amt = 8 nutriment_desc = list("salad" = 4, "egg" = 2, "potato" = 2) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/nettlesoup/Initialize() . = ..() @@ -2510,6 +2515,7 @@ nutriment_amt = 1 nutriment_desc = list("backwash" = 1) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/mysterysoup/Initialize() . = ..() @@ -2558,6 +2564,7 @@ filling_color = "#D1F4FF" center_of_mass = list("x"=16, "y"=11) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/wishsoup/Initialize() . = ..() @@ -2573,13 +2580,12 @@ trash = /obj/item/trash/snack_bowl filling_color = "#D92929" center_of_mass = list("x"=16, "y"=7) - nutriment_amt = 5 - nutriment_desc = list("soup" = 5) bitesize = 3 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/tomatosoup/Initialize() . = ..() - reagents.add_reagent("tomatojuice", 10) + reagents.add_reagent("tomato_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/mushroomsoup name = "chantrelle soup" @@ -2588,9 +2594,12 @@ trash = /obj/item/trash/snack_bowl filling_color = "#E386BF" center_of_mass = list("x"=17, "y"=10) - nutriment_amt = 8 - nutriment_desc = list("mushroom" = 8, "milk" = 2) bitesize = 3 + eating_sound = 'sound/items/drink.ogg' + +/obj/item/weapon/reagent_containers/food/snacks/mushroomsoup/Initialize() + . = ..() + reagents.add_reagent("mushroom_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/beetsoup name = "beet soup" @@ -2599,13 +2608,13 @@ trash = /obj/item/trash/snack_bowl filling_color = "#FAC9FF" center_of_mass = list("x"=15, "y"=8) - nutriment_amt = 8 - nutriment_desc = list("tomato" = 4, "beet" = 4) - bitesize = 2 + bitesize = 3 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/beetsoup/Initialize() . = ..() name = pick(list("borsch","bortsch","borstch","borsh","borshch","borscht")) + reagents.add_reagent("beet_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/soup/onion name = "onion soup" @@ -2614,9 +2623,12 @@ trash = /obj/item/trash/snack_bowl filling_color = "#E0C367" center_of_mass = list("x"=16, "y"=7) - nutriment_amt = 5 - nutriment_desc = list("onion" = 2, "soup" = 2) bitesize = 3 + eating_sound = 'sound/items/drink.ogg' + +/obj/item/weapon/reagent_containers/food/snacks/soup/onion/Initialize() + . = ..() + reagents.add_reagent("onion_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/chickennoodlesoup name = "chicken noodle soup" @@ -2624,16 +2636,11 @@ desc = "A bright bowl of yellow broth with cuts of meat, noodles and carrots." icon_state = "chickennoodlesoup" filling_color = "#ead90c" - nutriment_amt = 6 - nutriment_desc = list("warm soup" = 6) - center_of_mass = list("x"=16, "y"=5) - bitesize = 6 + bitesize = 5 /obj/item/weapon/reagent_containers/food/snacks/chickennoodlesoup/Initialize() . = ..() - reagents.add_reagent("protein", 4) - reagents.add_reagent("water", 5) - + reagents.add_reagent("chicken_noodle_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/stew name = "Stew" @@ -2646,6 +2653,7 @@ drop_sound = 'sound/items/drop/shovel.ogg' pickup_sound = 'sound/items/pickup/shovel.ogg' bitesize = 10 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/stew/Initialize() . = ..() @@ -2664,6 +2672,7 @@ nutriment_desc = list("hearty stew" = 6) center_of_mass = list("x"=16, "y"=5) bitesize = 6 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/bearstew/Initialize() . = ..() @@ -2684,6 +2693,7 @@ nutriment_amt = 3 nutriment_desc = list("chilli peppers" = 3) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/hotchili/Initialize() . = ..() @@ -2701,6 +2711,7 @@ nutriment_amt = 3 nutriment_desc = list("ice peppers" = 3) bitesize = 5 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/coldchili/Initialize() . = ..() @@ -2720,6 +2731,7 @@ nutriment_desc = list("dark, hearty chili" = 3) center_of_mass = list("x"=15, "y"=9) bitesize = 6 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/bearchili/Initialize() . = ..() @@ -4267,6 +4279,7 @@ nutriment_amt = 20 nutriment_desc = list("chalk" = 6) bitesize = 4 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/liquidfood/Initialize() . = ..() @@ -4281,6 +4294,7 @@ survivalfood = TRUE center_of_mass = list("x"=16, "y"=15) bitesize = 4 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/liquidprotein/Initialize() . = ..() @@ -4296,6 +4310,7 @@ survivalfood = TRUE center_of_mass = list("x"=16, "y"=15) bitesize = 4 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/liquidvitamin/Initialize() . = ..() @@ -6960,7 +6975,7 @@ /obj/item/weapon/reagent_containers/food/snacks/canned/tomato/Initialize() .=..() - reagents.add_reagent("tomatojuice", 12) + reagents.add_reagent("tomato_soup", 12) /obj/item/weapon/reagent_containers/food/snacks/canned/spinach name = "spinach" diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm index 7be201b6d0e..9d43943d42f 100644 --- a/code/modules/food/food/snacks_vr.dm +++ b/code/modules/food/food/snacks_vr.dm @@ -33,6 +33,7 @@ trash = /obj/item/trash/snack_bowl nutriment_amt = 6 nutriment_desc = list("meat" = 2, "vegetables" = 2, "seasoning" = 5) + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/goulash/Initialize() . = ..() @@ -128,11 +129,12 @@ trash = /obj/item/trash/asian_bowl nutriment_amt = 6 nutriment_desc = list("spicyness" = 4, "sourness" = 4, "tofu" = 1) + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/hotandsoursoup/Initialize() . = ..() bitesize = 2 - + reagents.add_reagent("hot_n_sour_soup", 10) /obj/item/weapon/reagent_containers/food/snacks/kitsuneudon name = "kitsune udon" @@ -142,6 +144,7 @@ trash = /obj/item/trash/asian_bowl nutriment_amt = 6 nutriment_desc = list("fried egg" = 2, "egg noodles" = 4) + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/kitsuneudon/Initialize() . = ..() @@ -583,6 +586,7 @@ nutriment_amt = 8 nutriment_desc = list("soy" = 8) bitesize = 4 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/milosoup/Initialize() . = ..() @@ -595,12 +599,12 @@ trash = /obj/item/trash/snack_bowl filling_color = "#E0C367" center_of_mass = list("x"=16, "y"=7) - nutriment_amt = 5 - nutriment_desc = list("onion" = 2, "soup" = 2) + bitesize = 3 + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/onionsoup/Initialize() . = ..() - bitesize = 3 + reagents.add_reagent("onion_soup", 10) //Fennec foods /obj/item/weapon/storage/box/wings/bucket @@ -809,6 +813,7 @@ trash = /obj/item/trash/ratjuice nutriment_amt = 2 nutriment_desc = list("essence of steak" = 6) + eating_sound = 'sound/items/drink.ogg' /obj/item/weapon/reagent_containers/food/snacks/ratliquid/Initialize() . = ..() @@ -829,7 +834,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratfruitcake name = "Premade Fruit Block" - desc = "A block of processed material that is infused with a mix of fruits and matter of such." + desc = "A block of processed material that is infused with a mix of fruits and other nutrients." icon = 'icons/obj/food_vr.dmi' icon_state = "altevian_fruitcake" package_open_state = "altevian_fruitcake-open" @@ -840,7 +845,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpackburger name = "Altevian Prepackaged Meal - Burger" - desc = "A unique twist on what most know as MREs. This seems to be made with using bluespace tech and other methods of preserving an items freshness that it's like someone just ordered this from a restaurant just minutes ago. This one seems to be of burger and fries!" + desc = "A unique twist on what most know as MREs. This seems to be made with using a specialized compression technology that possibly utilizes Bluespace alongside it. Whatever it is, it seems to keep the freshness of whatever is inside to restaurant quality. This one is a combo of a burger and fries!" icon = 'icons/obj/food_vr.dmi' icon_state = "altevian_pack_burger" package_open_state = "altevian_pack_burger-open" @@ -852,7 +857,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese name = "Generations Novelty Packaged Wedge" - desc = "Using the popular method of packaging that altevians use, they seemed to also use it for other methods. This one appears to have no real markings on it, save for its different coloring, and an image of the altevian emblem." + desc = "A unique twist on what most know as MREs. This seems to be made with using a specialized compression technology that possibly utilizes Bluespace alongside it. Whatever it is, it seems to keep the freshness of whatever is inside to restaurant quality. This one appears to have no real markings on it, save for its different coloring, and an image of the altevian emblem." icon = 'icons/obj/food_vr.dmi' icon_state = "altevian_pack_cheese" package_open_state = "altevian_pack_cheese-open" @@ -864,7 +869,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpackturkey name = "Compact Holiday Special Bird" - desc = "A great gift for holidays for assorted species. This contains a full freshly cooked turkey. Open and enjoy. Courtesy of altevian packaging." + desc = "A great gift for that special celebration or holiday for assorted species. This package contains a full freshly cooked turkey. Open and enjoy. Courtesy of altevian packaging." icon = 'icons/obj/food_vr.dmi' icon_state = "altevian_pack_turkey" package_open_state = "altevian_pack_turkey-open" @@ -876,7 +881,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpackramen name = "Big Noodle Package" - desc = "A pack containing fully cooked ramen meal, alongside some seafood-and-rice based sides. Utensils included. For those who prefer more traditional meals." + desc = "A compression sealed package containing a fully cooked ramen meal. It comes with some seafood-and-rice based sides. Utensils included." icon = 'icons/obj/food_vr.dmi' icon_state = "altevian_pack_ramen" package_open_state = "altevian_pack_ramen_standard-open" @@ -904,7 +909,7 @@ /obj/item/weapon/reagent_containers/food/snacks/ratpacktaco name = "Triple Taco Tuck" - desc = "Three mini-tacos, minituarized further via altevian mad science into a convenient container. It comes with a salsa sauce!" + desc = "A compression sealed package containing three mini-tacos, miniaturized further via altevian mad science into a convenient container." icon = 'icons/obj/food_vr.dmi' icon_state = "altevian_pack_taco" package_open_state = "altevian_pack_taco-open" @@ -913,3 +918,302 @@ trash = /obj/item/trash/ratpacktaco nutriment_amt = 2 nutriment_desc = list("salsa sauce" = 2, "meat chunks" = 4, "cheese" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/ratpackcake + name = "Instant Sweet Celebration" + desc = "A compressed sweet treat in one easy container. The label states that it's a slice of vanilla cake." + icon = 'icons/obj/food_vr.dmi' + icon_state = "altevian_pack_cake" + package_open_state = "altevian_pack_cake-open" + package_opening_state = "altevian_pack_cake-opening" + package = TRUE + trash = /obj/item/trash/ratpackcake + nutriment_amt = 2 + nutriment_desc = list("nice mix of vanilla and sugar" = 5) + +/obj/item/weapon/reagent_containers/food/snacks/ratpackmeat + name = "unmarked compression package" + desc = "This package looks familiar to the compression packs commonly seen from the Altevians. However, it looks to be unfinished or a possible test product that somehow ended up in your hands." + icon = 'icons/obj/food_vr.dmi' + icon_state = "altevian_pack_meat" + package_open_state = "altevian_pack_meat-open" + package_opening_state = "altevian_pack_meat-opening" + package = TRUE + trash = /obj/item/trash/ratpackmeat + nutriment_amt = 2 + nutriment_desc = list("synthetic meat" = 6) + + +//desatti snacks + +/obj/item/weapon/reagent_containers/food/snacks/jaffacake + name = "Jaffa Cake" + desc = "A delicious miniature cake with a soft sponge base, topped with orange jelly and covered with a thin layer of chocolate." + icon = 'icons/obj/food_vr.dmi' + icon_state = "jaffacake" + nutriment_amt = 1 + bitesize = 2 + nutriment_desc = list("chocolate" = 2, "orange" = 4, "cake" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/bourbon/Initialize() + . = ..() + reagents.add_reagent("coco", 2) + +/obj/item/weapon/storage/box/jaffacake //This is kinda like the donut box. + name = "Desatti Jaffa Cakes" + desc = "A box full of desatti brand jaffa cakes, with twelve in a pack!" + icon = 'icons/obj/food_vr.dmi' + icon_state = "jaffacake_pack" + var/icon_base = "jaffacake_pack" + var/startswith = 12 + max_storage_space = ITEMSIZE_COST_SMALL * 12 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/jaffacake) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/jaffacake = 12 + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/winegum + name = "wine gum" + desc = "A small firm and chewy sweet filled with artificial flavour." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "winegum_red" + w_class = ITEMSIZE_TINY + nutriment_amt = 1 + bitesize = 2 + nutriment_desc = list("sugar" = 5, "berry" = 2) + + +/obj/item/weapon/reagent_containers/food/snacks/winegum/orange + icon_state = "winegum_orange" + nutriment_desc = list("sugar" = 5, "orange" = 2) + +/obj/item/weapon/reagent_containers/food/snacks/winegum/black + icon_state = "winegum_black" + nutriment_desc = list("sugar" = 5, "berry" = 2) + +/obj/item/weapon/reagent_containers/food/snacks/winegum/green + icon_state = "winegum_green" + nutriment_desc = list("sugar" = 5, "lime" = 2) + +/obj/item/weapon/reagent_containers/food/snacks/winegum/yellow + icon_state = "winegum_yellow" + nutriment_desc = list("sugar" = 5, "lemon" = 2) + +/obj/item/weapon/reagent_containers/food/snacks/winegum/white + icon_state = "winegum_white" + nutriment_desc = list("sugar" = 5, "pineapplejuice" = 2) + +/obj/item/weapon/storage/box/winegum //This is kinda like the donut box. + name = "Desatti Wine Gums" + desc = "A pack of wine gums, randomly assorted shapes and fruity flavours!" + icon = 'icons/obj/food_snacks.dmi' + icon_state = "winegum_pack" + var/icon_base = "winegum_pack" + var/startswith = 20 + max_storage_space = ITEMSIZE_COST_TINY * 20 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/winegum) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/winegum = 3, + /obj/item/weapon/reagent_containers/food/snacks/winegum/orange = 4, + /obj/item/weapon/reagent_containers/food/snacks/winegum/black = 3, + /obj/item/weapon/reagent_containers/food/snacks/winegum/green = 3, + /obj/item/weapon/reagent_containers/food/snacks/winegum/yellow = 3, + /obj/item/weapon/reagent_containers/food/snacks/winegum/white = 4 + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/packaged/pasty + name = "Terran Pasty" + icon_state = "pasty" + desc = "A pre-packaged terran pasty with Desatti Catering branding. Claims to contain lamb, potato and onion in a pastry casing!" + package_trash = /obj/item/trash/pasty + package_open_state = "pasty_open" + nutriment_amt = 4 + nutriment_desc = list("pastry" = 5, "meat" = 5, "onion" = 2, "potato" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/packaged/pasty/Initialize() + . = ..() + reagents.add_reagent("protein", 2) + +/obj/item/weapon/reagent_containers/food/snacks/saucer + name = "Sherbert Saucer" + desc = "A small saucer shaped piece of rice paper filled with sugar." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "saucer_pink" + w_class = ITEMSIZE_TINY + nutriment_amt = 1 + bitesize = 2 + nutriment_desc = list("sugar" = 5) + var/list/color_options = list("saucer_pink","saucer_blue","saucer_orange","saucer_green","saucer_yellow") + +/obj/item/weapon/reagent_containers/food/snacks/saucer/Initialize() + . = ..() + icon_state = pick(color_options) + +/obj/item/weapon/storage/box/saucer //This is kinda like the donut box. + name = "Desatti Sherbert Saucers" + desc = "A pack of sherbert saucers, delicious pure sugar barely held together in packets of rice paper!" + icon = 'icons/obj/food_snacks.dmi' + icon_state = "saucer_pack" + var/icon_base = "saucer_pack" + var/startswith = 20 + max_storage_space = ITEMSIZE_COST_TINY * 20 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/saucer) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/saucer = 20, + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/custardcream + name = "Custard Cream" + desc = "A small sandwich biscuit with a layer of custard flavoured cream as the filling and a pleasant design on the top and bottom." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "custard_cream" + nutriment_amt = 1 + bitesize = 1 + nutriment_desc = list("biscuit" = 5, "cream" = 3, "custard" = 3) + +/obj/item/weapon/storage/box/custardcream //This is kinda like the donut box. + name = "Desatti Custard Creams" + desc = "A box full of desatti brand custard cream biscuits!" + icon = 'icons/obj/food_snacks.dmi' + icon_state = "custard_cream_pack" + var/icon_base = "custard_cream_pack" + var/startswith = 12 + max_storage_space = ITEMSIZE_COST_SMALL * 12 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/custardcream) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/custardcream = 12 + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/bourbon + name = "Bourbon Biscuit" + desc = "A long chocolate sandwich biscuit with a layer of chocolate flavoured cream as the filling and the word 'bourbon' on top." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "bourbon" + nutriment_amt = 1 + bitesize = 1 + nutriment_desc = list("biscuit" = 5, "cream" = 3, "chocolate" = 5) + +/obj/item/weapon/storage/box/bourbon //This is kinda like the donut box. + name = "Desatti Bourbons" + desc = "A box full of desatti brand chocolate bourbon biscuits!" + icon = 'icons/obj/food_snacks.dmi' + icon_state = "bourbon_pack" + var/icon_base = "bourbon_pack" + var/startswith = 12 + max_storage_space = ITEMSIZE_COST_SMALL * 12 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/bourbon) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/bourbon = 12 + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/bourbon/Initialize() + . = ..() + reagents.add_reagent("coco", 2) + +/obj/item/weapon/reagent_containers/food/snacks/packaged/sausageroll + name = "Sausage Roll" + icon_state = "sausageroll" + desc = "A pre-packaged sausage roll with Desatti Catering branding. Claims to contain real meat covered in pastry!" + package_trash = /obj/item/trash/sausageroll + package_open_state = "sausageroll_open" + nutriment_amt = 3 + nutriment_desc = list("pastry" = 5, "meat" = 5) + +/obj/item/weapon/reagent_containers/food/snacks/packaged/sausageroll/Initialize() + . = ..() + reagents.add_reagent("protein", 2) + +/obj/item/weapon/reagent_containers/food/snacks/packaged/scotchegg + name = "Scotch Egg" + icon_state = "scotchegg" + desc = "A pre-packaged scotch egg with Desatti Catering branding. Claims to contain a boiled egg covered in meat and coated with breadcrumbs!" + package_trash = /obj/item/trash/scotchegg + package_open_state = "scotchegg_open" + nutriment_amt = 3 + nutriment_desc = list("egg" = 5, "meat" = 5, "bread" = 2) + +/obj/item/weapon/reagent_containers/food/snacks/packaged/scotchegg/Initialize() + . = ..() + reagents.add_reagent("protein", 2) + +/obj/item/weapon/reagent_containers/food/snacks/foam_banana + name = "Foam Banana" + desc = "A small vaguely banana shaped sweet with a foam-like texture." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "foam_banana" + w_class = ITEMSIZE_TINY + nutriment_amt = 1 + bitesize = 2 + nutriment_desc = list("sugar" = 5, "banana" = 3) + +/obj/item/weapon/reagent_containers/food/snacks/foam_shrimp + name = "Foam Shrimp" + desc = "A small vaguely shrimp shaped sweet with a foam-like texture." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "foam_shrimp" + w_class = ITEMSIZE_TINY + nutriment_amt = 1 + bitesize = 2 + nutriment_desc = list("sugar" = 5, "strawberry" = 3) + +/obj/item/weapon/storage/box/shrimpsandbananas //This is kinda like the donut box. + name = "Shrimps and Bananas" + desc = "A pack of foam bananas and shrimps, the shrimps apparently don't taste of seafood! Branded as Desatti Catering." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "shrimpbanana_pack" + var/icon_base = "shrimpbanana_pack" + var/startswith = 20 + max_storage_space = ITEMSIZE_COST_TINY * 20 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/foam_banana,/obj/item/weapon/reagent_containers/food/snacks/foam_shrimp) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/foam_banana = 10, + /obj/item/weapon/reagent_containers/food/snacks/foam_shrimp = 10 + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/rhubarbcustard + name = "Rhubarb and Custard Sweet" + desc = "A small pink and yellow boiled sweet." + icon = 'icons/obj/food_snacks.dmi' + icon_state = "rhubarbcustard_1" + w_class = ITEMSIZE_TINY + nutriment_amt = 1 + bitesize = 2 + nutriment_desc = list("sugar" = 5, "rhubarb" = 2, "custard" = 2) + var/list/color_options = list("rhubarbcustard_1","rhubarbcustard_2") + +/obj/item/weapon/reagent_containers/food/snacks/rhubarbcustard/Initialize() + . = ..() + icon_state = pick(color_options) + +/obj/item/weapon/storage/box/rhubarbcustard //This is kinda like the donut box. + name = "Desatti Rhubarb and Custards" + desc = "A pack of rhubarb and custard boiled sweets, the taste combination might sound usual, but they insist it's actually kind of okay!" + icon = 'icons/obj/food_snacks.dmi' + icon_state = "rhubarbcustard_pack" + var/icon_base = "rhubarbcustard_pack" + var/startswith = 15 + max_storage_space = ITEMSIZE_COST_TINY * 15 + can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/rhubarbcustard) + starts_with = list( + /obj/item/weapon/reagent_containers/food/snacks/rhubarbcustard = 15, + ) + foldable = null + +/obj/item/weapon/reagent_containers/food/snacks/packaged/porkpie + name = "Pork Pie" + icon_state = "porkpie" + desc = "A pre-packaged pork pie with Desatti Catering branding. Claims to contain real meat covered in pastry!" + package_trash = /obj/item/trash/porkpie + package_open_state = "porkpie_open" + nutriment_amt = 3 + nutriment_desc = list("pastry" = 5, "meat" = 5) + +/obj/item/weapon/reagent_containers/food/snacks/packaged/porkpie/Initialize() + . = ..() + reagents.add_reagent("protein", 2) diff --git a/code/modules/food/glass/bottle_potion.dm b/code/modules/food/glass/bottle_potion.dm new file mode 100644 index 00000000000..486a2a158e7 --- /dev/null +++ b/code/modules/food/glass/bottle_potion.dm @@ -0,0 +1,151 @@ +/obj/item/weapon/reagent_containers/glass/bottle/potion + name = "healing potion" + desc = "A small green bottle containing some red liquid that claims to heal injuries." + icon = 'icons/obj/chemical.dmi' + icon_state = "bottle-5" + prefill = list("bicaridine" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/healing + +/obj/item/weapon/reagent_containers/glass/bottle/potion/greater_healing + name = "greater healing potion" + desc = "A small green bottle containing some thick red liquid that claims to rapidly heal injuries." + prefill = list("vermicetol" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/fire_resist + name = "fire resistance potion" + desc = "A small green bottle containing some orange liquid that claims to protect the drinker from fire." + prefill = list("dermaline" = 15, "kelotane" = 15) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/antidote + name = "antidote potion" + desc = "A small green bottle containing some green liquid that claims to cure poisoning." + prefill = list("anti_toxin" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/water + name = "water breathing potion" + desc = "A small green bottle containing some blue liquid that claims to allow the drinker to breathe under water." + prefill = list("dexalinp" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/regeneration + name = "regeneration potion" + desc = "A small green bottle containing some purple liquid that claims to regenerate severe wounds." + prefill = list("peridaxon" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/panacea + name = "panacea potion" + desc = "A small green bottle containing some white liquid that claims to cure all ailments." + prefill = list("spaceacillin" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/magic + name = "magic resistence potion" + desc = "A small green bottle containing some dark green liquid that claims to cure magical effects." + prefill = list("hyronalin" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/lightness + name = "feather weight potion" + desc = "A small green bottle containing some mysterious liquid that claims to make you feel lighter." + prefill = list("ickypak" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/SOP + name = "standard operating potion" + desc = "A small green bottle containing some yellow liquid that claims to be important." + prefill = list("myelamine" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/shrink + name = "diminution potion" + desc = "A small green bottle containing some swirling cyan liquid that claims to reduce the drinkers stature." + prefill = list("microcillin" = 1) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/growth + name = "fire giant potion" + desc = "A small green bottle containing some bubbling yellow liquid that claims to turn the drinker into a fire giant." + prefill = list("macrocillin" = 1, "capsaicin" = 5) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/pain + name = "grit potion" + desc = "A small green bottle containing some thin purple liquid that claims to power through even the most perilous injuries." + prefill = list("tramadol" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/faerie + name = "faerie dance potion" + desc = "A small green bottle containing some swishing pink liquid that claims to help you open your mind." + prefill = list("psilocybin" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/relaxation + name = "relaxation potion" + desc = "A small green bottle containing some still green liquid that claims to make everything feel just fine, really." + prefill = list("ambrosia_extract" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/speed + name = "blinding speed potion" + desc = "A small green bottle containing some bubbling orange liquid that claims to make you move at incredible speeds." + prefill = list("hyperzine" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/attractiveness + name = "love potion" + desc = "A small green bottle containing some light mint coloured liquid that claims to make you more attractive to potential partners." + prefill = list("menthol" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/girljuice + name = "girl transformation potion" + desc = "A small green bottle containing some pretty pink liquid that claims to turn the drinker into a woman." + prefill = list("gynorovir" = 1) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/boyjuice + name = "boy transformation potion" + desc = "A small green bottle containing some strong blue liquid that claims to turn the drinker into a man." + prefill = list("androrovir" = 1) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/badpolymorph + name = "unstable polymorph potion" + desc = "A small green bottle containing some uncomfortably green liquid that claims to transform the drinker wildly." + prefill = list("mutagen" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/bonerepair + name = "mending potion" + desc = "A small green bottle containing some pale blue liquid that claims to fix that which is broken." + prefill = list("osteodaxon" = 1) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/truepolymorph + name = "polymorph potion" + desc = "A small green bottle containing some strange purple liquid that claims to transform the drinker." + prefill = list("polymorph" = 1) + +//Failed potions + +/obj/item/weapon/reagent_containers/glass/bottle/potion/plain + name = "plain potion" + desc = "A small green bottle containing some plain transparent liquid." + prefill = list("water" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/ethanol + name = "thin potion" + desc = "A small green bottle containing some thin transparent liquid with a solvent scent." + prefill = list("ethanol" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/sugar + name = "sweet potion" + desc = "A small green bottle containing some white translucent liquid with a sweet scent." + prefill = list("sugar" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/capsaicin + name = "warm potion" + desc = "A small green bottle containing some red liquid." + prefill = list("capsaicin" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/soporific + name = "still potion" + desc = "A small green bottle containing some calm blue liquid." + prefill = list("stoxin" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/lipostipo + name = "thick potion" + desc = "A small green bottle containing some thick viscous liquid." + prefill = list("lipostipo" = 30) + +/obj/item/weapon/reagent_containers/glass/bottle/potion/phoron + name = "volatile potion" + desc = "A small green bottle containing some volatile purple liquid." + prefill = list("phoron" = 10) + diff --git a/code/modules/food/kitchen/cooking_machines/_appliance.dm b/code/modules/food/kitchen/cooking_machines/_appliance.dm index 7f71133f3a6..a5703007bf8 100644 --- a/code/modules/food/kitchen/cooking_machines/_appliance.dm +++ b/code/modules/food/kitchen/cooking_machines/_appliance.dm @@ -1,784 +1,784 @@ -// This folder contains code that was originally ported from Apollo Station and then refactored/optimized/changed. - -// Tracks precooked food to stop deep fried baked grilled grilled grilled diona nymph cereal. -/obj/item/weapon/reagent_containers/food/snacks - var/tmp/list/cooked = list() - -// Root type for cooking machines. See following files for specific implementations. -/obj/machinery/appliance - name = "cooker" - desc = "You shouldn't be seeing this!" - icon = 'icons/obj/cooking_machines.dmi' - var/appliancetype = 0 - density = TRUE - anchored = TRUE - - use_power = USE_POWER_IDLE - idle_power_usage = 5 // Power used when turned on, but not processing anything - active_power_usage = 1000 // Power used when turned on and actively cooking something - - var/cooking_power = 0 // Effectiveness/speed at cooking - var/cooking_coeff = 0 // Optimal power * proximity to optimal temp; used to calc. cooking power. - var/heating_power = 1000 // Effectiveness at heating up; not used for mixers, should be equal to active_power_usage - var/max_contents = 1 // Maximum number of things this appliance can simultaneously cook - var/on_icon // Icon state used when cooking. - var/off_icon // Icon state used when not cooking. - var/cooking = FALSE // Whether or not the machine is currently operating. - var/cook_type // A string value used to track what kind of food this machine makes. - var/can_cook_mobs // Whether or not this machine accepts grabbed mobs. - var/mobdamagetype = BRUTE // Burn damage for cooking appliances, brute for cereal/candy - var/food_color // Colour of resulting food item. - var/cooked_sound = 'sound/machines/ding.ogg' // Sound played when cooking completes. - var/can_burn_food = FALSE // Can the object burn food that is left inside? - var/burn_chance = 10 // How likely is the food to burn? - var/list/cooking_objs = list() // List of things being cooked - - // If the machine has multiple output modes, define them here. - var/selected_option - var/list/output_options = list() - var/list/datum/recipe/available_recipes - - var/container_type = null - - var/combine_first = FALSE // If TRUE, this appliance will do combination cooking before checking recipes - -/obj/machinery/appliance/Initialize() - . = ..() - - default_apply_parts() - - if(output_options.len) - verbs += /obj/machinery/appliance/proc/choose_output - - if (!available_recipes) - available_recipes = new - - for(var/datum/recipe/test as anything in subtypesof(/datum/recipe)) - if((appliancetype & initial(test.appliance))) - available_recipes += new test - -/obj/machinery/appliance/Destroy() - for(var/datum/cooking_item/CI as anything in cooking_objs) - qdel(CI.container)//Food is fragile, it probably doesnt survive the destruction of the machine - cooking_objs -= CI - qdel(CI) - return ..() - -/obj/machinery/appliance/examine(var/mob/user) - . = ..() - if(Adjacent(user)) - . += list_contents(user) - -/obj/machinery/appliance/proc/list_contents(var/mob/user) - if (cooking_objs.len) - var/string = "Contains..." - for(var/datum/cooking_item/CI as anything in cooking_objs) - string += "-\a [CI.container.label(null, CI.combine_target)], [report_progress(CI)]
                    " - return string - else - to_chat(user, "") - -/obj/machinery/appliance/proc/report_progress_tgui(datum/cooking_item/CI) - if(!CI || !CI.max_cookwork) - return list("average", "Not Cooking.") - - if(!CI.cookwork) - return list("blue", "Cold.") - - var/progress = CI.cookwork / CI.max_cookwork - - if (progress < 0.25) - return list("blue", "It's barely started cooking.") - if (progress < 0.75) - return list("average", "It's cooking away nicely.") - if (progress < 1) - return list("good", "It's almost ready!") - - var/half_overcook = (CI.overcook_mult - 1)*0.5 - if (progress < 1+half_overcook) - return list("good", "It's done!") - if (progress < CI.overcook_mult) - return list("bad", "It looks overcooked, get it out!") - else - return list("bad", "It is burning!") - -/obj/machinery/appliance/proc/report_progress(var/datum/cooking_item/CI) - if (!CI || !CI.max_cookwork) - return null - - if (!CI.cookwork) - return "It is cold." - var/progress = CI.cookwork / CI.max_cookwork - - if (progress < 0.25) - return "It's barely started cooking." - if (progress < 0.75) - return "It's cooking away nicely." - if (progress < 1) - return "It's almost ready!" - - var/half_overcook = (CI.overcook_mult - 1)*0.5 - if (progress < 1+half_overcook) - return "It is done !" - if (progress < CI.overcook_mult) - return "It looks overcooked, get it out!" - else - return "It is burning!" - -/obj/machinery/appliance/update_icon() - if (!stat && cooking_objs.len) - icon_state = on_icon - - else - icon_state = off_icon - -/obj/machinery/appliance/verb/toggle_power() - set name = "Toggle Power" - set category = "Object" - set src in view() - - attempt_toggle_power(usr) - -/obj/machinery/appliance/proc/attempt_toggle_power(mob/user) - if (!isliving(user)) - return - - if (!user.IsAdvancedToolUser()) - to_chat(user, "You lack the dexterity to do that!") - return - - if (user.stat || user.restrained() || user.incapacitated()) - return - - if (!Adjacent(user) && !issilicon(user)) - to_chat(user, "You can't reach [src] from here!") - return - - if (stat & POWEROFF)//Its turned off - stat &= ~POWEROFF - use_power = 1 - user.visible_message("[user] turns [src] on.", "You turn on [src].") - - else //Its on, turn it off - stat |= POWEROFF - use_power = 0 - user.visible_message("[user] turns [src] off.", "You turn off [src].") - cooking = FALSE // Stop cooking here, too, just in case. - - playsound(src, 'sound/machines/click.ogg', 40, 1) - update_icon() - -/obj/machinery/appliance/AICtrlClick(mob/user) - attempt_toggle_power(user) - -/obj/machinery/appliance/proc/choose_output() - set src in view() - set name = "Choose output" - set category = "Object" - - if (!isliving(usr)) - return - - if (!usr.IsAdvancedToolUser()) - to_chat(usr, "You lack the dexterity to do that!") - return - - if (usr.stat || usr.restrained() || usr.incapacitated()) - return - - if (!Adjacent(usr) && !issilicon(usr)) - to_chat(usr, "You can't adjust the [src] from this distance, get closer!") - return - - if(output_options.len) - var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options+"Default") - if(!choice) - return - if(choice == "Default") - selected_option = null - to_chat(usr, "You decide not to make anything specific with \the [src].") - else - selected_option = choice - to_chat(usr, "You prepare \the [src] to make \a [selected_option] with the next thing you put in. Try putting several ingredients in a container!") - -//Handles all validity checking and error messages for inserting things -/obj/machinery/appliance/proc/can_insert(var/obj/item/I, var/mob/user) - if(istype(I.loc, /mob/living/silicon)) - return 0 - else if (istype(I.loc, /obj/item/rig_module)) - return 0 - - // We are trying to cook a grabbed mob. - var/obj/item/weapon/grab/G = I - if(istype(G)) - - if(!can_cook_mobs) - to_chat(user, "That's not going to fit.") - return 0 - - if(!isliving(G.affecting)) - to_chat(user, "You can't cook that.") - return 0 - - return 2 - - - if (!has_space(I)) - to_chat(user, "There's no room in [src] for that!") - return 0 - - - if (container_type && istype(I, container_type)) - return 1 - - // We're trying to cook something else. Check if it's valid. - var/obj/item/weapon/reagent_containers/food/snacks/check = I - if(istype(check) && islist(check.cooked) && (cook_type in check.cooked)) - to_chat(user, "\The [check] has already been [cook_type].") - return 0 - else if(istype(check, /obj/item/weapon/reagent_containers/glass)) - to_chat(user, "That would probably break [src].") - return 0 - else if(istype(check, /obj/item/weapon/disk/nuclear)) - to_chat(user, "You can't cook that.") - return 0 - else if(I.is_crowbar() || I.is_screwdriver() || istype(I, /obj/item/weapon/storage/part_replacer)) // You can't cook tools, dummy. - return 0 - else if(!istype(check) && !istype(check, /obj/item/weapon/holder)) - to_chat(user, "That's not edible.") - return 0 - - return 1 - - -//This function is overridden by cookers that do stuff with containers -/obj/machinery/appliance/proc/has_space(var/obj/item/I) - if(cooking_objs.len >= max_contents) - return FALSE - - return TRUE - -/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user) - if(!cook_type || (stat & (BROKEN))) - to_chat(user, "\The [src] is not working.") - return FALSE - - var/obj/item/ToCook = I - - if(istype(I, /obj/item/weapon/gripper)) - var/obj/item/weapon/gripper/GR = I - var/obj/item/Wrap = GR.wrapped - if(Wrap) - Wrap.loc = get_turf(src) - var/result = can_insert(Wrap, user) - if(!result) - Wrap.forceMove(GR) - if(!(default_deconstruction_screwdriver(user, I))) - default_part_replacement(user, I) - return - - if(QDELETED(GR.wrapped)) - GR.wrapped = null - - if(GR?.wrapped.loc != src) - GR.drop_item_nm() - - ToCook = Wrap - else - attack_hand(user) - return - - else - var/result = can_insert(I, user) - if(!result) - if(!(default_deconstruction_screwdriver(user, I))) - default_part_replacement(user, I) - return - - if(result == 2) - var/obj/item/weapon/grab/G = I - if (G && istype(G) && G.affecting) - cook_mob(G.affecting, user) - return - - //From here we can start cooking food - add_content(ToCook, user) - update_icon() - -//Override for container mechanics -/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user) - if(!user.unEquip(I) && !isturf(I.loc)) - return - - var/datum/cooking_item/CI = has_space(I) - if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1) - var/obj/item/weapon/reagent_containers/cooking_container/CC = I - CI = new /datum/cooking_item/(CC) - I.forceMove(src) - cooking_objs.Add(CI) - user.visible_message("\The [user] puts \the [I] into \the [src].") - if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing. - return TRUE - else - if (CI && istype(CI)) - I.forceMove(CI.container) - - else //Something went wrong - return FALSE - - if (selected_option) - CI.combine_target = selected_option - - // We can actually start cooking now. - user.visible_message("\The [user] puts \the [I] into \the [src].") - - get_cooking_work(CI) - cooking = TRUE - return CI - -/obj/machinery/appliance/proc/get_cooking_work(var/datum/cooking_item/CI) - for (var/obj/item/J in CI.container) - cookwork_by_item(J, CI) - - for(var/datum/reagent/R as anything in CI.container.reagents.reagent_list) - if (istype(R, /datum/reagent/nutriment)) - CI.max_cookwork += R.volume *2//Added reagents contribute less than those in food items due to granular form - - //Nonfat reagents will soak oil - if (!istype(R, /datum/reagent/nutriment/triglyceride)) - CI.max_oil += R.volume * 0.25 - else - CI.max_cookwork += R.volume - CI.max_oil += R.volume * 0.10 - - //Rescaling cooking work to avoid insanely long times for large things - var/buffer = CI.max_cookwork - CI.max_cookwork = 0 - var/multiplier = 1 - var/step = 4 - while (buffer > step) - buffer -= step - CI.max_cookwork += step*multiplier - multiplier *= 0.95 - - CI.max_cookwork += buffer*multiplier - -//Just a helper to save code duplication in the above -/obj/machinery/appliance/proc/cookwork_by_item(var/obj/item/I, var/datum/cooking_item/CI) - var/obj/item/weapon/reagent_containers/food/snacks/S = I - var/work = 0 - if (istype(S)) - if (S.reagents) - for(var/datum/reagent/R as anything in S.reagents.reagent_list) - if (istype(R, /datum/reagent/nutriment)) - work += R.volume *3//Core nutrients contribute much more than peripheral chemicals - - //Nonfat reagents will soak oil - if (!istype(R, /datum/reagent/nutriment/triglyceride)) - CI.max_oil += R.volume * 0.35 - else - work += R.volume - CI.max_oil += R.volume * 0.15 - - - else if(istype(I, /obj/item/weapon/holder)) - var/obj/item/weapon/holder/H = I - if (H.held_mob) - work += ((H.held_mob.mob_size * H.held_mob.size_multiplier) * (H.held_mob.mob_size * H.held_mob.size_multiplier) * 2)+2 - - CI.max_cookwork += work - -//Called every tick while we're cooking something -/obj/machinery/appliance/proc/do_cooking_tick(var/datum/cooking_item/CI) - if (!istype(CI) || !CI.max_cookwork) - return FALSE - - var/was_done = FALSE - if (CI.cookwork >= CI.max_cookwork) - was_done = TRUE - - CI.cookwork += cooking_power - - if (!was_done && CI.cookwork >= CI.max_cookwork) - //If cookwork has gone from above to below 0, then this item finished cooking - finish_cooking(CI) - - else if (!CI.burned && CI.cookwork > min(CI.max_cookwork * CI.overcook_mult, CI.max_cookwork + 30)) - burn_food(CI) - - // Gotta hurt. - for(var/obj/item/weapon/holder/H in CI.container.contents) - var/mob/living/M = H.held_mob - if(M) - M.apply_damage(rand(1,3) * (1/M.size_multiplier), mobdamagetype, pick(BP_ALL)) - - return TRUE - -/obj/machinery/appliance/process() - if(cooking_power > 0 && cooking) - var/all_done_cooking = TRUE - for(var/datum/cooking_item/CI in cooking_objs) - do_cooking_tick(CI) - if(CI.max_cookwork > 0) - all_done_cooking = FALSE - if(all_done_cooking) - cooking = FALSE - update_icon() - - -/obj/machinery/appliance/proc/finish_cooking(var/datum/cooking_item/CI) - - src.visible_message("\The [src] pings!") - if(cooked_sound) - playsound(get_turf(src), cooked_sound, 50, 1) - //Check recipes first, a valid recipe overrides other options - var/datum/recipe/recipe = null - var/atom/C = null - if (CI.container) - C = CI.container - else - C = src - recipe = select_recipe(available_recipes,C) - - if (recipe) - CI.result_type = 4//Recipe type, a specific recipe will transform the ingredients into a new food - var/list/results = recipe.make_food(C) - - var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes - - for (var/atom/movable/AM in results) - AM.forceMove(temp) - - //making multiple copies of a recipe from one container. For example, tons of fries - while (select_recipe(available_recipes,C) == recipe) - var/list/TR = list() - TR += recipe.make_food(C) - for (var/atom/movable/AM in TR) //Move results to buffer - AM.forceMove(temp) - results += TR - - - for(var/obj/item/weapon/reagent_containers/food/snacks/R as anything in results) - R.forceMove(C) //Move everything from the buffer back to the container - R.cooked |= cook_type - - QDEL_NULL(temp) //delete buffer object - . = 1 //None of the rest of this function is relevant for recipe cooking - - else if(CI.combine_target) - CI.result_type = 3//Combination type. We're making something out of our ingredients - . = combination_cook(CI) - - - else - //Otherwise, we're just doing standard modification cooking. change a color + name - for (var/obj/item/i in CI.container) - modify_cook(i, CI) - - //Final step. Cook function just cooks batter for now. - for (var/obj/item/weapon/reagent_containers/food/snacks/S in CI.container) - S.cook() - - -//Combination cooking involves combining the names and reagents of ingredients into a predefined output object -//The ingredients represent flavours or fillings. EG: donut pizza, cheese bread -/obj/machinery/appliance/proc/combination_cook(var/datum/cooking_item/CI) - var/cook_path = output_options[CI.combine_target] - - var/list/words = list() - var/list/cooktypes = list() - var/datum/reagents/buffer = new /datum/reagents(1000) - var/totalcolour - var/reagents_determine_color - - if(!LAZYLEN(CI.container.contents)) // It's possible to make something, such as a cake in the oven, with only reagents. This stops them from being grey and sad. - reagents_determine_color = TRUE - - for (var/obj/item/I in CI.container) - var/obj/item/weapon/reagent_containers/food/snacks/S - if (istype(I, /obj/item/weapon/holder)) - S = create_mob_food(I, CI) - else if (istype(I, /obj/item/weapon/reagent_containers/food/snacks)) - S = I - - if (!S) - continue - - words |= splittext(S.name," ") - cooktypes |= S.cooked - - if (S.reagents && S.reagents.total_volume > 0) - if (S.filling_color) - if (!totalcolour || !buffer.total_volume) - totalcolour = S.filling_color - else - var/t = buffer.total_volume + S.reagents.total_volume - t = buffer.total_volume / y - totalcolour = BlendRGB(totalcolour, S.filling_color, t) - //Blend colours in order to find a good filling color - - - S.reagents.trans_to_holder(buffer, S.reagents.total_volume) - //Cleanup these empty husk ingredients now - if (I) - qdel(I) - if (S) - qdel(S) - - CI.container.reagents.trans_to_holder(buffer, CI.container.reagents.total_volume) - - var/obj/item/weapon/reagent_containers/food/snacks/result = new cook_path(CI.container) - buffer.trans_to_holder(result.reagents, buffer.total_volume) //trans_to doesn't handle food items well, so - //just call trans_to_holder instead - - // Reagent-only foods. - if(reagents_determine_color) - totalcolour = result.reagents.get_color() - - for(var/datum/reagent/reag in result.reagents.reagent_list) - words |= text2list(reag.name, " ") - - //Filling overlay - var/image/I = image(result.icon, "[result.icon_state]_filling") - I.color = totalcolour - result.add_overlay(I) - result.filling_color = totalcolour - - //Set the name. - words -= list("and", "the", "in", "is", "bar", "raw", "sticks", "boiled", "fried", "deep", "-o-", "warm", "two", "flavored") - //Remove common connecting words and unsuitable ones from the list. Unsuitable words include those describing - //the shape, cooked-ness/temperature or other state of an ingredient which doesn't apply to the finished product - words.Remove(result.name) - shuffle(words) - var/num = 6 //Maximum number of words - while (num > 0) - num-- - if (!words.len) - break - //Add prefixes from the ingredients in a random order until we run out or hit limit - result.name = "[pop(words)] [result.name]" - - //This proc sets the size of the output result - result.update_icon() - return result - -//Helper proc for standard modification cooking -/obj/machinery/appliance/proc/modify_cook(var/obj/item/input, var/datum/cooking_item/CI) - var/obj/item/weapon/reagent_containers/food/snacks/result - if (istype(input, /obj/item/weapon/holder)) - result = create_mob_food(input, CI) - else if (istype(input, /obj/item/weapon/reagent_containers/food/snacks)) - result = input - else - //Nonviable item - return - - if (!result) - return - - result.cooked |= cook_type - - // Set icon and appearance. - change_product_appearance(result, CI) - - // Update strings. - change_product_strings(result, CI) - -/obj/machinery/appliance/proc/burn_food(var/datum/cooking_item/CI) - // You dun goofed. - CI.burned = 1 - CI.container.clear() - new /obj/item/weapon/reagent_containers/food/snacks/badrecipe(CI.container) - - // Produce nasty smoke. - visible_message("\The [src] vomits a gout of rancid smoke!") - var/datum/effect/effect/system/smoke_spread/bad/burntfood/smoke = new /datum/effect/effect/system/smoke_spread/bad/burntfood - playsound(src, 'sound/effects/smoke.ogg', 20, 1) - smoke.attach(src) - smoke.set_up(10, 0, get_turf(src), 300) - smoke.start() - - // Set off fire alarms! - var/obj/machinery/firealarm/FA = locate() in get_area(src) - if(FA) - FA.alarm() - -/obj/machinery/appliance/attack_hand(var/mob/user) - if(..()) - return - - if(cooking_objs.len) - removal_menu(user) - -/obj/machinery/appliance/proc/removal_menu(var/mob/user) - if (can_remove_items(user)) - var/list/menuoptions = list() - for(var/datum/cooking_item/CI as anything in cooking_objs) - if (CI.container) - menuoptions[CI.container.label(menuoptions.len)] = CI - - var/selection = tgui_input_list(user, "Which item would you like to remove?", "Remove ingredients", menuoptions) - if (selection) - var/datum/cooking_item/CI = menuoptions[selection] - eject(CI, user) - update_icon() - return TRUE - return FALSE - -/obj/machinery/appliance/proc/can_remove_items(var/mob/user, show_warning = TRUE) - if (!Adjacent(user)) - return FALSE - - if (isanimal(user)) - return FALSE - - return TRUE - -/obj/machinery/appliance/proc/eject(var/datum/cooking_item/CI, var/mob/user = null) - var/obj/item/thing - var/delete = 1 - var/status = CI.container.check_contents() - - if (status == 1)//If theres only one object in a container then we extract that - thing = locate(/obj/item) in CI.container - delete = 0 - else//If the container is empty OR contains more than one thing, then we must extract the container - thing = CI.container - if (!user || !user.put_in_hands(thing)) - thing.forceMove(get_turf(src)) - - if (delete) - cooking_objs -= CI - qdel(CI) - else - CI.reset()//reset instead of deleting if the container is left inside - user.visible_message("\The [user] remove \the [thing] from \the [src].") - -/obj/machinery/appliance/proc/cook_mob(var/mob/living/victim, var/mob/user) - return - -/obj/machinery/appliance/proc/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) - product.name = "[cook_type] [product.name]" - product.desc = "[product.desc]\nIt has been [cook_type]." - - -/obj/machinery/appliance/proc/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) - if (!product.coating) //Coatings change colour through a new sprite - product.color = food_color - product.filling_color = food_color - -/mob/living/proc/calculate_composition() // moved from devour.dm on aurora's side - if (!composition_reagent)//if no reagent has been set, then we'll set one - if (isSynthetic()) - src.composition_reagent = "iron" - else - if(istype(src, /mob/living/carbon/human/diona) || istype(src, /mob/living/carbon/alien/diona)) - src.composition_reagent = "nutriment" // diona are plants, not meat - else - src.composition_reagent = "protein" - if(istype(src, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(istype(H.species, /datum/species/diona)) - src.composition_reagent = "nutriment" - - //if the mob is a simple animal - MOB NOT ANIMAL - with a defined meat quantity - if (istype(src, /mob/living/simple_mob)) - var/mob/living/simple_mob/SA = src - if(SA.meat_amount) - src.composition_reagent_quantity = SA.meat_amount*2*9 - - //The quantity of protein is based on the meat_amount, but multiplied by 2 - - var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared - if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two - src.composition_reagent_quantity = size_reagent - -//This function creates a food item which represents a dead mob -/obj/machinery/appliance/proc/create_mob_food(var/obj/item/weapon/holder/H, var/datum/cooking_item/CI) - if (!istype(H) || !H.held_mob) - qdel(H) - return null - var/mob/living/victim = H.held_mob - if (victim.stat != DEAD) - return null //Victim somehow survived the cooking, they do not become food - - victim.calculate_composition() - - var/obj/item/weapon/reagent_containers/food/snacks/variable/mob/result = new /obj/item/weapon/reagent_containers/food/snacks/variable/mob(CI.container) - result.w_class = victim.mob_size - result.reagents.add_reagent(victim.composition_reagent, victim.composition_reagent_quantity) - - if (victim.reagents) - victim.reagents.trans_to_holder(result.reagents, victim.reagents.total_volume) - - if (isanimal(victim)) - var/mob/living/simple_mob/SA = victim - result.kitchen_tag = SA.kitchen_tag - - result.appearance = victim - - var/matrix/M = matrix() - M.Turn(45) - M.Translate(1,-2) - result.transform = M - - // all done, now delete the old objects - H.held_mob = null - qdel(victim) - victim = null - qdel(H) - H = null - - return result - -/datum/cooking_item - var/max_cookwork - var/cookwork - var/overcook_mult = 3 // How long it takes to overcook. This is max_cookwork x overcook mult. If you're changing this, mind that at 3x, a max_cookwork of 30 becomes 90 ticks for the purpose of burning, and a max_cookwork of 4 only has 12 before burning! - var/result_type = 0 - var/obj/item/weapon/reagent_containers/cooking_container/container = null - var/combine_target = null - - //Result type is one of the following: - //0 unfinished, no result yet - //1 Standard modification cooking. eg Fried Donk Pocket, Baked wheat, etc - //2 Modification but with a new object that's an inert copy of the old. Generally used for deepfried mice - //3 Combination cooking, EG Donut Bread, Donk pocket pizza, etc - //4:Specific recipe cooking. EG: Turning raw potato sticks into fries - - var/burned = 0 - - var/oil = 0 - var/max_oil = 0//Used for fryers. - -/datum/cooking_item/New(var/obj/item/I) - container = I - -//This is called for containers whose contents are ejected without removing the container -/datum/cooking_item/proc/reset() - max_cookwork = 0 - cookwork = 0 - result_type = 0 - burned = 0 - max_oil = 0 - oil = 0 - combine_target = null - //Container is not reset - -/obj/machinery/appliance/RefreshParts() - ..() - var/scan_rating = 0 - var/cap_rating = 0 - - for(var/obj/item/weapon/stock_parts/P in src.component_parts) - if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) - scan_rating += P.rating - 1 // Default parts shouldn't mess with stats - // to_world("RefreshParts returned scan rating of [scan_rating] during this step.") // Debug lines, uncomment if you need to test. - else if(istype(P, /obj/item/weapon/stock_parts/capacitor)) - cap_rating += P.rating - 1 // Default parts shouldn't mess with stats - // to_world("RefreshParts returned cap rating of [cap_rating] during this step.") // Debug lines, uncomment if you need to test. - - active_power_usage = initial(active_power_usage) - scan_rating * 25 - heating_power = initial(heating_power) + cap_rating * 25 - cooking_power = cooking_coeff * (1 + (scan_rating + cap_rating) / 20) // 100% eff. becomes 120%, 140%, 160% w/ better parts, thus rewarding upgrading the appliances during your shift. - // to_world("RefreshParts returned cooking power of [cooking_power] during this step.") // Debug lines, uncomment if you need to test. +// This folder contains code that was originally ported from Apollo Station and then refactored/optimized/changed. + +// Tracks precooked food to stop deep fried baked grilled grilled grilled diona nymph cereal. +/obj/item/weapon/reagent_containers/food/snacks + var/tmp/list/cooked = list() + +// Root type for cooking machines. See following files for specific implementations. +/obj/machinery/appliance + name = "cooker" + desc = "You shouldn't be seeing this!" + icon = 'icons/obj/cooking_machines.dmi' + var/appliancetype = 0 + density = TRUE + anchored = TRUE + + use_power = USE_POWER_IDLE + idle_power_usage = 5 // Power used when turned on, but not processing anything + active_power_usage = 1000 // Power used when turned on and actively cooking something + + var/cooking_power = 0 // Effectiveness/speed at cooking + var/cooking_coeff = 0 // Optimal power * proximity to optimal temp; used to calc. cooking power. + var/heating_power = 1000 // Effectiveness at heating up; not used for mixers, should be equal to active_power_usage + var/max_contents = 1 // Maximum number of things this appliance can simultaneously cook + var/on_icon // Icon state used when cooking. + var/off_icon // Icon state used when not cooking. + var/cooking = FALSE // Whether or not the machine is currently operating. + var/cook_type // A string value used to track what kind of food this machine makes. + var/can_cook_mobs // Whether or not this machine accepts grabbed mobs. + var/mobdamagetype = BRUTE // Burn damage for cooking appliances, brute for cereal/candy + var/food_color // Colour of resulting food item. + var/cooked_sound = 'sound/machines/ding.ogg' // Sound played when cooking completes. + var/can_burn_food = FALSE // Can the object burn food that is left inside? + var/burn_chance = 10 // How likely is the food to burn? + var/list/cooking_objs = list() // List of things being cooked + + // If the machine has multiple output modes, define them here. + var/selected_option + var/list/output_options = list() + var/list/datum/recipe/available_recipes + + var/container_type = null + + var/combine_first = FALSE // If TRUE, this appliance will do combination cooking before checking recipes + +/obj/machinery/appliance/Initialize() + . = ..() + + default_apply_parts() + + if(output_options.len) + verbs += /obj/machinery/appliance/proc/choose_output + + if (!available_recipes) + available_recipes = new + + for(var/datum/recipe/test as anything in subtypesof(/datum/recipe)) + if((appliancetype & initial(test.appliance))) + available_recipes += new test + +/obj/machinery/appliance/Destroy() + for(var/datum/cooking_item/CI as anything in cooking_objs) + qdel(CI.container)//Food is fragile, it probably doesnt survive the destruction of the machine + cooking_objs -= CI + qdel(CI) + return ..() + +/obj/machinery/appliance/examine(var/mob/user) + . = ..() + if(Adjacent(user)) + . += list_contents(user) + +/obj/machinery/appliance/proc/list_contents(var/mob/user) + if (cooking_objs.len) + var/string = "Contains..." + for(var/datum/cooking_item/CI as anything in cooking_objs) + string += "-\a [CI.container.label(null, CI.combine_target)], [report_progress(CI)]
                    " + return string + else + to_chat(user, "") + +/obj/machinery/appliance/proc/report_progress_tgui(datum/cooking_item/CI) + if(!CI || !CI.max_cookwork) + return list("average", "Not Cooking.") + + if(!CI.cookwork) + return list("blue", "Cold.") + + var/progress = CI.cookwork / CI.max_cookwork + + if (progress < 0.25) + return list("blue", "It's barely started cooking.") + if (progress < 0.75) + return list("average", "It's cooking away nicely.") + if (progress < 1) + return list("good", "It's almost ready!") + + var/half_overcook = (CI.overcook_mult - 1)*0.5 + if (progress < 1+half_overcook) + return list("good", "It's done!") + if (progress < CI.overcook_mult) + return list("bad", "It looks overcooked, get it out!") + else + return list("bad", "It is burning!") + +/obj/machinery/appliance/proc/report_progress(var/datum/cooking_item/CI) + if (!CI || !CI.max_cookwork) + return null + + if (!CI.cookwork) + return "It is cold." + var/progress = CI.cookwork / CI.max_cookwork + + if (progress < 0.25) + return "It's barely started cooking." + if (progress < 0.75) + return "It's cooking away nicely." + if (progress < 1) + return "It's almost ready!" + + var/half_overcook = (CI.overcook_mult - 1)*0.5 + if (progress < 1+half_overcook) + return "It is done !" + if (progress < CI.overcook_mult) + return "It looks overcooked, get it out!" + else + return "It is burning!" + +/obj/machinery/appliance/update_icon() + if (!stat && cooking_objs.len) + icon_state = on_icon + + else + icon_state = off_icon + +/obj/machinery/appliance/verb/toggle_power() + set name = "Toggle Power" + set category = "Object" + set src in view() + + attempt_toggle_power(usr) + +/obj/machinery/appliance/proc/attempt_toggle_power(mob/user) + if (!isliving(user)) + return + + if (!user.IsAdvancedToolUser()) + to_chat(user, "You lack the dexterity to do that!") + return + + if (user.stat || user.restrained() || user.incapacitated()) + return + + if (!Adjacent(user) && !issilicon(user)) + to_chat(user, "You can't reach [src] from here!") + return + + if (stat & POWEROFF)//Its turned off + stat &= ~POWEROFF + use_power = 1 + user.visible_message("[user] turns [src] on.", "You turn on [src].") + + else //Its on, turn it off + stat |= POWEROFF + use_power = 0 + user.visible_message("[user] turns [src] off.", "You turn off [src].") + cooking = FALSE // Stop cooking here, too, just in case. + + playsound(src, 'sound/machines/click.ogg', 40, 1) + update_icon() + +/obj/machinery/appliance/AICtrlClick(mob/user) + attempt_toggle_power(user) + +/obj/machinery/appliance/proc/choose_output() + set src in view() + set name = "Choose output" + set category = "Object" + + if (!isliving(usr)) + return + + if (!usr.IsAdvancedToolUser()) + to_chat(usr, "You lack the dexterity to do that!") + return + + if (usr.stat || usr.restrained() || usr.incapacitated()) + return + + if (!Adjacent(usr) && !issilicon(usr)) + to_chat(usr, "You can't adjust the [src] from this distance, get closer!") + return + + if(output_options.len) + var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options+"Default") + if(!choice) + return + if(choice == "Default") + selected_option = null + to_chat(usr, "You decide not to make anything specific with \the [src].") + else + selected_option = choice + to_chat(usr, "You prepare \the [src] to make \a [selected_option] with the next thing you put in. Try putting several ingredients in a container!") + +//Handles all validity checking and error messages for inserting things +/obj/machinery/appliance/proc/can_insert(var/obj/item/I, var/mob/user) + if(istype(I.loc, /mob/living/silicon)) + return 0 + else if (istype(I.loc, /obj/item/rig_module)) + return 0 + + // We are trying to cook a grabbed mob. + var/obj/item/weapon/grab/G = I + if(istype(G)) + + if(!can_cook_mobs) + to_chat(user, "That's not going to fit.") + return 0 + + if(!isliving(G.affecting)) + to_chat(user, "You can't cook that.") + return 0 + + return 2 + + + if (!has_space(I)) + to_chat(user, "There's no room in [src] for that!") + return 0 + + + if (container_type && istype(I, container_type)) + return 1 + + // We're trying to cook something else. Check if it's valid. + var/obj/item/weapon/reagent_containers/food/snacks/check = I + if(istype(check) && islist(check.cooked) && (cook_type in check.cooked)) + to_chat(user, "\The [check] has already been [cook_type].") + return 0 + else if(istype(check, /obj/item/weapon/reagent_containers/glass)) + to_chat(user, "That would probably break [src].") + return 0 + else if(istype(check, /obj/item/weapon/disk/nuclear)) + to_chat(user, "You can't cook that.") + return 0 + else if(I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/weapon/storage/part_replacer)) // You can't cook tools, dummy. + return 0 + else if(!istype(check) && !istype(check, /obj/item/weapon/holder)) + to_chat(user, "That's not edible.") + return 0 + + return 1 + + +//This function is overridden by cookers that do stuff with containers +/obj/machinery/appliance/proc/has_space(var/obj/item/I) + if(cooking_objs.len >= max_contents) + return FALSE + + return TRUE + +/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user) + if(!cook_type || (stat & (BROKEN))) + to_chat(user, "\The [src] is not working.") + return FALSE + + var/obj/item/ToCook = I + + if(istype(I, /obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/GR = I + var/obj/item/Wrap = GR.wrapped + if(Wrap) + Wrap.loc = get_turf(src) + var/result = can_insert(Wrap, user) + if(!result) + Wrap.forceMove(GR) + if(!(default_deconstruction_screwdriver(user, I))) + default_part_replacement(user, I) + return + + if(QDELETED(GR.wrapped)) + GR.wrapped = null + + if(GR?.wrapped.loc != src) + GR.drop_item_nm() + + ToCook = Wrap + else + attack_hand(user) + return + + else + var/result = can_insert(I, user) + if(!result) + if(!(default_deconstruction_screwdriver(user, I))) + default_part_replacement(user, I) + return + + if(result == 2) + var/obj/item/weapon/grab/G = I + if (G && istype(G) && G.affecting) + cook_mob(G.affecting, user) + return + + //From here we can start cooking food + add_content(ToCook, user) + update_icon() + +//Override for container mechanics +/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user) + if(!user.unEquip(I) && !isturf(I.loc)) + return + + var/datum/cooking_item/CI = has_space(I) + if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1) + var/obj/item/weapon/reagent_containers/cooking_container/CC = I + CI = new /datum/cooking_item/(CC) + I.forceMove(src) + cooking_objs.Add(CI) + user.visible_message("\The [user] puts \the [I] into \the [src].") + if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing. + return TRUE + else + if (CI && istype(CI)) + I.forceMove(CI.container) + + else //Something went wrong + return FALSE + + if (selected_option) + CI.combine_target = selected_option + + // We can actually start cooking now. + user.visible_message("\The [user] puts \the [I] into \the [src].") + + get_cooking_work(CI) + cooking = TRUE + return CI + +/obj/machinery/appliance/proc/get_cooking_work(var/datum/cooking_item/CI) + for (var/obj/item/J in CI.container) + cookwork_by_item(J, CI) + + for(var/datum/reagent/R as anything in CI.container.reagents.reagent_list) + if (istype(R, /datum/reagent/nutriment)) + CI.max_cookwork += R.volume *2//Added reagents contribute less than those in food items due to granular form + + //Nonfat reagents will soak oil + if (!istype(R, /datum/reagent/nutriment/triglyceride)) + CI.max_oil += R.volume * 0.25 + else + CI.max_cookwork += R.volume + CI.max_oil += R.volume * 0.10 + + //Rescaling cooking work to avoid insanely long times for large things + var/buffer = CI.max_cookwork + CI.max_cookwork = 0 + var/multiplier = 1 + var/step = 4 + while (buffer > step) + buffer -= step + CI.max_cookwork += step*multiplier + multiplier *= 0.95 + + CI.max_cookwork += buffer*multiplier + +//Just a helper to save code duplication in the above +/obj/machinery/appliance/proc/cookwork_by_item(var/obj/item/I, var/datum/cooking_item/CI) + var/obj/item/weapon/reagent_containers/food/snacks/S = I + var/work = 0 + if (istype(S)) + if (S.reagents) + for(var/datum/reagent/R as anything in S.reagents.reagent_list) + if (istype(R, /datum/reagent/nutriment)) + work += R.volume *3//Core nutrients contribute much more than peripheral chemicals + + //Nonfat reagents will soak oil + if (!istype(R, /datum/reagent/nutriment/triglyceride)) + CI.max_oil += R.volume * 0.35 + else + work += R.volume + CI.max_oil += R.volume * 0.15 + + + else if(istype(I, /obj/item/weapon/holder)) + var/obj/item/weapon/holder/H = I + if (H.held_mob) + work += ((H.held_mob.mob_size * H.held_mob.size_multiplier) * (H.held_mob.mob_size * H.held_mob.size_multiplier) * 2)+2 + + CI.max_cookwork += work + +//Called every tick while we're cooking something +/obj/machinery/appliance/proc/do_cooking_tick(var/datum/cooking_item/CI) + if (!istype(CI) || !CI.max_cookwork) + return FALSE + + var/was_done = FALSE + if (CI.cookwork >= CI.max_cookwork) + was_done = TRUE + + CI.cookwork += cooking_power + + if (!was_done && CI.cookwork >= CI.max_cookwork) + //If cookwork has gone from above to below 0, then this item finished cooking + finish_cooking(CI) + + else if (!CI.burned && CI.cookwork > min(CI.max_cookwork * CI.overcook_mult, CI.max_cookwork + 30)) + burn_food(CI) + + // Gotta hurt. + for(var/obj/item/weapon/holder/H in CI.container.contents) + var/mob/living/M = H.held_mob + if(M) + M.apply_damage(rand(1,3) * (1/M.size_multiplier), mobdamagetype, pick(BP_ALL)) + + return TRUE + +/obj/machinery/appliance/process() + if(cooking_power > 0 && cooking) + var/all_done_cooking = TRUE + for(var/datum/cooking_item/CI in cooking_objs) + do_cooking_tick(CI) + if(CI.max_cookwork > 0) + all_done_cooking = FALSE + if(all_done_cooking) + cooking = FALSE + update_icon() + + +/obj/machinery/appliance/proc/finish_cooking(var/datum/cooking_item/CI) + + src.visible_message("\The [src] pings!") + if(cooked_sound) + playsound(get_turf(src), cooked_sound, 50, 1) + //Check recipes first, a valid recipe overrides other options + var/datum/recipe/recipe = null + var/atom/C = null + if (CI.container) + C = CI.container + else + C = src + recipe = select_recipe(available_recipes,C) + + if (recipe) + CI.result_type = 4//Recipe type, a specific recipe will transform the ingredients into a new food + var/list/results = recipe.make_food(C) + + var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes + + for (var/atom/movable/AM in results) + AM.forceMove(temp) + + //making multiple copies of a recipe from one container. For example, tons of fries + while (select_recipe(available_recipes,C) == recipe) + var/list/TR = list() + TR += recipe.make_food(C) + for (var/atom/movable/AM in TR) //Move results to buffer + AM.forceMove(temp) + results += TR + + + for(var/obj/item/weapon/reagent_containers/food/snacks/R as anything in results) + R.forceMove(C) //Move everything from the buffer back to the container + R.cooked |= cook_type + + QDEL_NULL(temp) //delete buffer object + . = 1 //None of the rest of this function is relevant for recipe cooking + + else if(CI.combine_target) + CI.result_type = 3//Combination type. We're making something out of our ingredients + . = combination_cook(CI) + + + else + //Otherwise, we're just doing standard modification cooking. change a color + name + for (var/obj/item/i in CI.container) + modify_cook(i, CI) + + //Final step. Cook function just cooks batter for now. + for (var/obj/item/weapon/reagent_containers/food/snacks/S in CI.container) + S.cook() + + +//Combination cooking involves combining the names and reagents of ingredients into a predefined output object +//The ingredients represent flavours or fillings. EG: donut pizza, cheese bread +/obj/machinery/appliance/proc/combination_cook(var/datum/cooking_item/CI) + var/cook_path = output_options[CI.combine_target] + + var/list/words = list() + var/list/cooktypes = list() + var/datum/reagents/buffer = new /datum/reagents(1000) + var/totalcolour + var/reagents_determine_color + + if(!LAZYLEN(CI.container.contents)) // It's possible to make something, such as a cake in the oven, with only reagents. This stops them from being grey and sad. + reagents_determine_color = TRUE + + for (var/obj/item/I in CI.container) + var/obj/item/weapon/reagent_containers/food/snacks/S + if (istype(I, /obj/item/weapon/holder)) + S = create_mob_food(I, CI) + else if (istype(I, /obj/item/weapon/reagent_containers/food/snacks)) + S = I + + if (!S) + continue + + words |= splittext(S.name," ") + cooktypes |= S.cooked + + if (S.reagents && S.reagents.total_volume > 0) + if (S.filling_color) + if (!totalcolour || !buffer.total_volume) + totalcolour = S.filling_color + else + var/t = buffer.total_volume + S.reagents.total_volume + t = buffer.total_volume / y + totalcolour = BlendRGB(totalcolour, S.filling_color, t) + //Blend colours in order to find a good filling color + + + S.reagents.trans_to_holder(buffer, S.reagents.total_volume) + //Cleanup these empty husk ingredients now + if (I) + qdel(I) + if (S) + qdel(S) + + CI.container.reagents.trans_to_holder(buffer, CI.container.reagents.total_volume) + + var/obj/item/weapon/reagent_containers/food/snacks/result = new cook_path(CI.container) + buffer.trans_to_holder(result.reagents, buffer.total_volume) //trans_to doesn't handle food items well, so + //just call trans_to_holder instead + + // Reagent-only foods. + if(reagents_determine_color) + totalcolour = result.reagents.get_color() + + for(var/datum/reagent/reag in result.reagents.reagent_list) + words |= text2list(reag.name, " ") + + //Filling overlay + var/image/I = image(result.icon, "[result.icon_state]_filling") + I.color = totalcolour + result.add_overlay(I) + result.filling_color = totalcolour + + //Set the name. + words -= list("and", "the", "in", "is", "bar", "raw", "sticks", "boiled", "fried", "deep", "-o-", "warm", "two", "flavored") + //Remove common connecting words and unsuitable ones from the list. Unsuitable words include those describing + //the shape, cooked-ness/temperature or other state of an ingredient which doesn't apply to the finished product + words.Remove(result.name) + shuffle(words) + var/num = 6 //Maximum number of words + while (num > 0) + num-- + if (!words.len) + break + //Add prefixes from the ingredients in a random order until we run out or hit limit + result.name = "[pop(words)] [result.name]" + + //This proc sets the size of the output result + result.update_icon() + return result + +//Helper proc for standard modification cooking +/obj/machinery/appliance/proc/modify_cook(var/obj/item/input, var/datum/cooking_item/CI) + var/obj/item/weapon/reagent_containers/food/snacks/result + if (istype(input, /obj/item/weapon/holder)) + result = create_mob_food(input, CI) + else if (istype(input, /obj/item/weapon/reagent_containers/food/snacks)) + result = input + else + //Nonviable item + return + + if (!result) + return + + result.cooked |= cook_type + + // Set icon and appearance. + change_product_appearance(result, CI) + + // Update strings. + change_product_strings(result, CI) + +/obj/machinery/appliance/proc/burn_food(var/datum/cooking_item/CI) + // You dun goofed. + CI.burned = 1 + CI.container.clear() + new /obj/item/weapon/reagent_containers/food/snacks/badrecipe(CI.container) + + // Produce nasty smoke. + visible_message("\The [src] vomits a gout of rancid smoke!") + var/datum/effect/effect/system/smoke_spread/bad/burntfood/smoke = new /datum/effect/effect/system/smoke_spread/bad/burntfood + playsound(src, 'sound/effects/smoke.ogg', 20, 1) + smoke.attach(src) + smoke.set_up(10, 0, get_turf(src), 300) + smoke.start() + + // Set off fire alarms! + var/obj/machinery/firealarm/FA = locate() in get_area(src) + if(FA) + FA.alarm() + +/obj/machinery/appliance/attack_hand(var/mob/user) + if(..()) + return + + if(cooking_objs.len) + removal_menu(user) + +/obj/machinery/appliance/proc/removal_menu(var/mob/user) + if (can_remove_items(user)) + var/list/menuoptions = list() + for(var/datum/cooking_item/CI as anything in cooking_objs) + if (CI.container) + menuoptions[CI.container.label(menuoptions.len)] = CI + + var/selection = tgui_input_list(user, "Which item would you like to remove?", "Remove ingredients", menuoptions) + if (selection) + var/datum/cooking_item/CI = menuoptions[selection] + eject(CI, user) + update_icon() + return TRUE + return FALSE + +/obj/machinery/appliance/proc/can_remove_items(var/mob/user, show_warning = TRUE) + if (!Adjacent(user)) + return FALSE + + if (isanimal(user)) + return FALSE + + return TRUE + +/obj/machinery/appliance/proc/eject(var/datum/cooking_item/CI, var/mob/user = null) + var/obj/item/thing + var/delete = 1 + var/status = CI.container.check_contents() + + if (status == 1)//If theres only one object in a container then we extract that + thing = locate(/obj/item) in CI.container + delete = 0 + else//If the container is empty OR contains more than one thing, then we must extract the container + thing = CI.container + if (!user || !user.put_in_hands(thing)) + thing.forceMove(get_turf(src)) + + if (delete) + cooking_objs -= CI + qdel(CI) + else + CI.reset()//reset instead of deleting if the container is left inside + user.visible_message("\The [user] remove \the [thing] from \the [src].") + +/obj/machinery/appliance/proc/cook_mob(var/mob/living/victim, var/mob/user) + return + +/obj/machinery/appliance/proc/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) + product.name = "[cook_type] [product.name]" + product.desc = "[product.desc]\nIt has been [cook_type]." + + +/obj/machinery/appliance/proc/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) + if (!product.coating) //Coatings change colour through a new sprite + product.color = food_color + product.filling_color = food_color + +/mob/living/proc/calculate_composition() // moved from devour.dm on aurora's side + if (!composition_reagent)//if no reagent has been set, then we'll set one + if (isSynthetic()) + src.composition_reagent = "iron" + else + if(istype(src, /mob/living/carbon/human/diona) || istype(src, /mob/living/carbon/alien/diona)) + src.composition_reagent = "nutriment" // diona are plants, not meat + else + src.composition_reagent = "protein" + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if(istype(H.species, /datum/species/diona)) + src.composition_reagent = "nutriment" + + //if the mob is a simple animal - MOB NOT ANIMAL - with a defined meat quantity + if (istype(src, /mob/living/simple_mob)) + var/mob/living/simple_mob/SA = src + if(SA.meat_amount) + src.composition_reagent_quantity = SA.meat_amount*2*9 + + //The quantity of protein is based on the meat_amount, but multiplied by 2 + + var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared + if (size_reagent > src.composition_reagent_quantity)//We take the larger of the two + src.composition_reagent_quantity = size_reagent + +//This function creates a food item which represents a dead mob +/obj/machinery/appliance/proc/create_mob_food(var/obj/item/weapon/holder/H, var/datum/cooking_item/CI) + if (!istype(H) || !H.held_mob) + qdel(H) + return null + var/mob/living/victim = H.held_mob + if (victim.stat != DEAD) + return null //Victim somehow survived the cooking, they do not become food + + victim.calculate_composition() + + var/obj/item/weapon/reagent_containers/food/snacks/variable/mob/result = new /obj/item/weapon/reagent_containers/food/snacks/variable/mob(CI.container) + result.w_class = victim.mob_size + result.reagents.add_reagent(victim.composition_reagent, victim.composition_reagent_quantity) + + if (victim.reagents) + victim.reagents.trans_to_holder(result.reagents, victim.reagents.total_volume) + + if (isanimal(victim)) + var/mob/living/simple_mob/SA = victim + result.kitchen_tag = SA.kitchen_tag + + result.appearance = victim + + var/matrix/M = matrix() + M.Turn(45) + M.Translate(1,-2) + result.transform = M + + // all done, now delete the old objects + H.held_mob = null + qdel(victim) + victim = null + qdel(H) + H = null + + return result + +/datum/cooking_item + var/max_cookwork + var/cookwork + var/overcook_mult = 3 // How long it takes to overcook. This is max_cookwork x overcook mult. If you're changing this, mind that at 3x, a max_cookwork of 30 becomes 90 ticks for the purpose of burning, and a max_cookwork of 4 only has 12 before burning! + var/result_type = 0 + var/obj/item/weapon/reagent_containers/cooking_container/container = null + var/combine_target = null + + //Result type is one of the following: + //0 unfinished, no result yet + //1 Standard modification cooking. eg Fried Donk Pocket, Baked wheat, etc + //2 Modification but with a new object that's an inert copy of the old. Generally used for deepfried mice + //3 Combination cooking, EG Donut Bread, Donk pocket pizza, etc + //4:Specific recipe cooking. EG: Turning raw potato sticks into fries + + var/burned = 0 + + var/oil = 0 + var/max_oil = 0//Used for fryers. + +/datum/cooking_item/New(var/obj/item/I) + container = I + +//This is called for containers whose contents are ejected without removing the container +/datum/cooking_item/proc/reset() + max_cookwork = 0 + cookwork = 0 + result_type = 0 + burned = 0 + max_oil = 0 + oil = 0 + combine_target = null + //Container is not reset + +/obj/machinery/appliance/RefreshParts() + ..() + var/scan_rating = 0 + var/cap_rating = 0 + + for(var/obj/item/weapon/stock_parts/P in src.component_parts) + if(istype(P, /obj/item/weapon/stock_parts/scanning_module)) + scan_rating += P.rating - 1 // Default parts shouldn't mess with stats + // to_world("RefreshParts returned scan rating of [scan_rating] during this step.") // Debug lines, uncomment if you need to test. + else if(istype(P, /obj/item/weapon/stock_parts/capacitor)) + cap_rating += P.rating - 1 // Default parts shouldn't mess with stats + // to_world("RefreshParts returned cap rating of [cap_rating] during this step.") // Debug lines, uncomment if you need to test. + + active_power_usage = initial(active_power_usage) - scan_rating * 25 + heating_power = initial(heating_power) + cap_rating * 25 + cooking_power = cooking_coeff * (1 + (scan_rating + cap_rating) / 20) // 100% eff. becomes 120%, 140%, 160% w/ better parts, thus rewarding upgrading the appliances during your shift. + // to_world("RefreshParts returned cooking power of [cooking_power] during this step.") // Debug lines, uncomment if you need to test. diff --git a/code/modules/food/kitchen/cooking_machines/_cooker_output.dm b/code/modules/food/kitchen/cooking_machines/_cooker_output.dm index 07f7870bc66..96353ee1924 100644 --- a/code/modules/food/kitchen/cooking_machines/_cooker_output.dm +++ b/code/modules/food/kitchen/cooking_machines/_cooker_output.dm @@ -1,160 +1,160 @@ -// Wrapper obj for cooked food. Appearance is set in the cooking code, not on spawn. -/obj/item/weapon/reagent_containers/food/snacks/variable - name = "cooked food" - icon = 'icons/obj/food_custom.dmi' - desc = "If you can see this description then something is wrong. Please report the bug on the tracker." - bitesize = 2 - - var/size = 5 //The quantity of reagents which is considered "normal" for this kind of food - //These objects will change size depending on the ratio of reagents to this value - var/min_scale = 0.5 - var/max_scale = 2 - var/scale = 1 - - w_class = 2 - var/prefix - -/obj/item/weapon/reagent_containers/food/snacks/variable/Initialize() - . = ..() - if (reagents) - reagents.maximum_volume = size*8 + 10 - else - create_reagents(size*8 + 10) - -/obj/item/weapon/reagent_containers/food/snacks/variable/update_icon() - if (reagents && reagents.total_volume) - var/ratio = reagents.total_volume / size - - scale = ratio**(1/3) //Scaling factor is square root of desired area - scale = clamp(scale, min_scale, max_scale) - else - scale = min_scale - - var/matrix/M = matrix() - M.Scale(scale) - src.transform = M - - w_class *= scale - if (!prefix) - if (scale == min_scale) - prefix = "tiny" - else if (scale <= 0.8) - prefix = "small" - - else - if (scale >= 1.2) - prefix = "large" - if (scale >= 1.4) - prefix = "extra large" - if (scale >= 1.6) - prefix = "huge" - if (scale >= max_scale) - prefix = "massive" - - name = "[prefix] [name]" - - -/obj/item/weapon/reagent_containers/food/snacks/variable/pizza - name = "personal pizza" - desc = "A personalized pan pizza meant for only one person." - icon_state = "personal_pizza" - size = 20 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/bread - name = "bread" - desc = "Tasty bread." - icon_state = "breadcustom" - size = 40 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/pie - name = "pie" - desc = "Tasty pie." - icon_state = "piecustom" - size = 25 - -/obj/item/weapon/reagent_containers/food/snacks/variable/cake - name = "cake" - desc = "A popular band." - icon_state = "cakecustom" - size = 40 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/pocket - name = "hot pocket" - desc = "You wanna put a bangin- oh, nevermind." - icon_state = "donk" - size = 8 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/kebab - name = "kebab" - desc = "Remove this!" - icon_state = "kabob" - size = 10 - -/obj/item/weapon/reagent_containers/food/snacks/variable/waffles - name = "waffles" - desc = "Made with love." - icon_state = "waffles" - size = 12 - -/obj/item/weapon/reagent_containers/food/snacks/variable/cookie - name = "cookie" - desc = "Sugar snap!" - icon_state = "cookie" - size = 6 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/donut - name = "filled donut" - desc = "Donut eat this!" // kill me - icon_state = "donut" - size = 8 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker - name = "flavored jawbreaker" - desc = "It's like cracking a molar on a rainbow." - icon_state = "jawbreaker" - size = 4 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/candybar - name = "flavored chocolate bar" - desc = "Made in a factory downtown." - icon_state = "bar" - size = 6 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/sucker - name = "flavored sucker" - desc = "Suck, suck, suck." - icon_state = "sucker" - size = 4 - w_class = 1 - -/obj/item/weapon/reagent_containers/food/snacks/variable/jelly - name = "jelly" - desc = "All your friends will be jelly." - icon_state = "jellycustom" - size = 8 - - -/obj/item/weapon/reagent_containers/food/snacks/variable/cereal - name = "cereal" - desc = "Crispy and flaky" - icon_state = "cereal_box" - size = 30 - w_class = 3 - -/obj/item/weapon/reagent_containers/food/snacks/variable/cereal/Initialize() - . =..() - name = pick(list("flakes", "krispies", "crunch", "pops", "O's", "crisp", "loops", "jacks", "clusters")) - -/obj/item/weapon/reagent_containers/food/snacks/variable/mob - desc = "Poor little thing." - size = 5 - w_class = 1 +// Wrapper obj for cooked food. Appearance is set in the cooking code, not on spawn. +/obj/item/weapon/reagent_containers/food/snacks/variable + name = "cooked food" + icon = 'icons/obj/food_custom.dmi' + desc = "If you can see this description then something is wrong. Please report the bug on the tracker." + bitesize = 2 + + var/size = 5 //The quantity of reagents which is considered "normal" for this kind of food + //These objects will change size depending on the ratio of reagents to this value + var/min_scale = 0.5 + var/max_scale = 2 + var/scale = 1 + + w_class = 2 + var/prefix + +/obj/item/weapon/reagent_containers/food/snacks/variable/Initialize() + . = ..() + if (reagents) + reagents.maximum_volume = size*8 + 10 + else + create_reagents(size*8 + 10) + +/obj/item/weapon/reagent_containers/food/snacks/variable/update_icon() + if (reagents && reagents.total_volume) + var/ratio = reagents.total_volume / size + + scale = ratio**(1/3) //Scaling factor is square root of desired area + scale = clamp(scale, min_scale, max_scale) + else + scale = min_scale + + var/matrix/M = matrix() + M.Scale(scale) + src.transform = M + + w_class *= scale + if (!prefix) + if (scale == min_scale) + prefix = "tiny" + else if (scale <= 0.8) + prefix = "small" + + else + if (scale >= 1.2) + prefix = "large" + if (scale >= 1.4) + prefix = "extra large" + if (scale >= 1.6) + prefix = "huge" + if (scale >= max_scale) + prefix = "massive" + + name = "[prefix] [name]" + + +/obj/item/weapon/reagent_containers/food/snacks/variable/pizza + name = "personal pizza" + desc = "A personalized pan pizza meant for only one person." + icon_state = "personal_pizza" + size = 20 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/bread + name = "bread" + desc = "Tasty bread." + icon_state = "breadcustom" + size = 40 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/pie + name = "pie" + desc = "Tasty pie." + icon_state = "piecustom" + size = 25 + +/obj/item/weapon/reagent_containers/food/snacks/variable/cake + name = "cake" + desc = "A popular band." + icon_state = "cakecustom" + size = 40 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/pocket + name = "hot pocket" + desc = "You wanna put a bangin- oh, nevermind." + icon_state = "donk" + size = 8 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/kebab + name = "kebab" + desc = "Remove this!" + icon_state = "kabob" + size = 10 + +/obj/item/weapon/reagent_containers/food/snacks/variable/waffles + name = "waffles" + desc = "Made with love." + icon_state = "waffles" + size = 12 + +/obj/item/weapon/reagent_containers/food/snacks/variable/cookie + name = "cookie" + desc = "Sugar snap!" + icon_state = "cookie" + size = 6 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/donut + name = "filled donut" + desc = "Donut eat this!" // kill me + icon_state = "donut" + size = 8 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker + name = "flavored jawbreaker" + desc = "It's like cracking a molar on a rainbow." + icon_state = "jawbreaker" + size = 4 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/candybar + name = "flavored chocolate bar" + desc = "Made in a factory downtown." + icon_state = "bar" + size = 6 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/sucker + name = "flavored sucker" + desc = "Suck, suck, suck." + icon_state = "sucker" + size = 4 + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/variable/jelly + name = "jelly" + desc = "All your friends will be jelly." + icon_state = "jellycustom" + size = 8 + + +/obj/item/weapon/reagent_containers/food/snacks/variable/cereal + name = "cereal" + desc = "Crispy and flaky" + icon_state = "cereal_box" + size = 30 + w_class = 3 + +/obj/item/weapon/reagent_containers/food/snacks/variable/cereal/Initialize() + . =..() + name = pick(list("flakes", "krispies", "crunch", "pops", "O's", "crisp", "loops", "jacks", "clusters")) + +/obj/item/weapon/reagent_containers/food/snacks/variable/mob + desc = "Poor little thing." + size = 5 + w_class = 1 var/kitchen_tag = "animal" \ No newline at end of file diff --git a/code/modules/food/kitchen/cooking_machines/_mixer.dm b/code/modules/food/kitchen/cooking_machines/_mixer.dm index f04f8bba465..9d78be3475c 100644 --- a/code/modules/food/kitchen/cooking_machines/_mixer.dm +++ b/code/modules/food/kitchen/cooking_machines/_mixer.dm @@ -1,155 +1,155 @@ -/* -The mixer subtype is used for the candymaker and cereal maker. They are similar to cookers but with a few -fundamental differences -1. They have a single container which cant be removed. it will eject multiple contents -2. Items can't be added or removed once the process starts -3. Items are all placed in the same container when added directly -4. They do combining mode only. And will always combine the entire contents of the container into an output -*/ - -/obj/machinery/appliance/mixer - max_contents = 1 - stat = POWEROFF - cooking_coeff = 0.75 // Original value 0.4 - active_power_usage = 3000 - idle_power_usage = 50 - var/datum/looping_sound/mixer/mixer_loop - -/obj/machinery/appliance/mixer/examine(var/mob/user) - . = ..() - if(Adjacent(user)) - . += "It is currently set to make a [selected_option]" - -/obj/machinery/appliance/mixer/Initialize() - . = ..() - cooking_objs += new /datum/cooking_item(new /obj/item/weapon/reagent_containers/cooking_container(src)) - cooking = FALSE - selected_option = pick(output_options) - - mixer_loop = new(list(src), FALSE) - -/obj/machinery/appliance/mixer/Destroy() - . = ..() - - QDEL_NULL(mixer_loop) - -//Mixers cannot-not do combining mode. So the default option is removed from this. A combine target must be chosen -/obj/machinery/appliance/mixer/choose_output() - set src in view(1) - set name = "Choose output" - set category = "Object" - - if (!isliving(usr)) - return - - if (!usr.IsAdvancedToolUser()) - to_chat(usr, "You can't operate [src].") - return - - if(output_options.len) - var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options) - if(!choice) - return - else - selected_option = choice - to_chat(usr, "You prepare \the [src] to make \a [selected_option].") - var/datum/cooking_item/CI = cooking_objs[1] - CI.combine_target = selected_option - - -/obj/machinery/appliance/mixer/has_space(var/obj/item/I) - var/datum/cooking_item/CI = cooking_objs[1] - if (!CI || !CI.container) - return 0 - - if (CI.container.can_fit(I)) - return CI - - return 0 - - -/obj/machinery/appliance/mixer/can_remove_items(var/mob/user, show_warning = TRUE) - if(stat) - return 1 - else - if(show_warning) - to_chat(user, "You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.") - return 0 - -//Container is not removable -/obj/machinery/appliance/mixer/removal_menu(var/mob/user) - if (can_remove_items(user)) - var/list/menuoptions = list() - for(var/datum/cooking_item/CI as anything in cooking_objs) - if (CI.container) - if (!CI.container.check_contents()) - to_chat(user, "There's nothing in [src] you can remove!") - return - - for (var/obj/item/I in CI.container) - menuoptions[I.name] = I - - var/selection = tgui_input_list(user, "Which item would you like to remove? If you want to remove chemicals, use an empty beaker.", "Remove ingredients", menuoptions) - if (selection) - var/obj/item/I = menuoptions[selection] - if (!user || !user.put_in_hands(I)) - I.forceMove(get_turf(src)) - update_icon() - return 1 - return 0 - - -/obj/machinery/appliance/mixer/toggle_power() - set src in view(1) - set name = "Toggle Power" - set category = "Object" - - var/datum/cooking_item/CI = cooking_objs[1] - if(!CI.container.check_contents()) - to_chat("There's nothing in it! Add ingredients before turning [src] on!") - return - - if(stat & POWEROFF)//Its turned off - stat &= ~POWEROFF - if(usr) - usr.visible_message("[usr] turns the [src] on.", "You turn on \the [src].") - get_cooking_work(CI) - use_power = 2 - else //Its on, turn it off - stat |= POWEROFF - use_power = 0 - if(usr) - usr.visible_message("[usr] turns the [src] off.", "You turn off \the [src].") - playsound(src, 'sound/machines/click.ogg', 40, 1) - update_icon() - -/obj/machinery/appliance/mixer/can_insert(var/obj/item/I, var/mob/user) - if(!stat) - to_chat(user, ",You can't add items while \the [src] is running. Wait for it to finish or turn the power off to abort.") - return 0 - else - return ..() - -/obj/machinery/appliance/mixer/finish_cooking(var/datum/cooking_item/CI) - ..() - stat |= POWEROFF - playsound(src, 'sound/machines/click.ogg', 40, 1) - use_power = 0 - CI.reset() - update_icon() - -/obj/machinery/appliance/mixer/update_icon() - if (!stat) - icon_state = on_icon - if(mixer_loop) - mixer_loop.start(src) - else - icon_state = off_icon - if(mixer_loop) - mixer_loop.stop(src) - - -/obj/machinery/appliance/mixer/process() - if (!stat) - for (var/i in cooking_objs) +/* +The mixer subtype is used for the candymaker and cereal maker. They are similar to cookers but with a few +fundamental differences +1. They have a single container which cant be removed. it will eject multiple contents +2. Items can't be added or removed once the process starts +3. Items are all placed in the same container when added directly +4. They do combining mode only. And will always combine the entire contents of the container into an output +*/ + +/obj/machinery/appliance/mixer + max_contents = 1 + stat = POWEROFF + cooking_coeff = 0.75 // Original value 0.4 + active_power_usage = 3000 + idle_power_usage = 50 + var/datum/looping_sound/mixer/mixer_loop + +/obj/machinery/appliance/mixer/examine(var/mob/user) + . = ..() + if(Adjacent(user)) + . += "It is currently set to make a [selected_option]" + +/obj/machinery/appliance/mixer/Initialize() + . = ..() + cooking_objs += new /datum/cooking_item(new /obj/item/weapon/reagent_containers/cooking_container(src)) + cooking = FALSE + selected_option = pick(output_options) + + mixer_loop = new(list(src), FALSE) + +/obj/machinery/appliance/mixer/Destroy() + . = ..() + + QDEL_NULL(mixer_loop) + +//Mixers cannot-not do combining mode. So the default option is removed from this. A combine target must be chosen +/obj/machinery/appliance/mixer/choose_output() + set src in view(1) + set name = "Choose output" + set category = "Object" + + if (!isliving(usr)) + return + + if (!usr.IsAdvancedToolUser()) + to_chat(usr, "You can't operate [src].") + return + + if(output_options.len) + var/choice = tgui_input_list(usr, "What specific food do you wish to make with \the [src]?", "Food Output Choice", output_options) + if(!choice) + return + else + selected_option = choice + to_chat(usr, "You prepare \the [src] to make \a [selected_option].") + var/datum/cooking_item/CI = cooking_objs[1] + CI.combine_target = selected_option + + +/obj/machinery/appliance/mixer/has_space(var/obj/item/I) + var/datum/cooking_item/CI = cooking_objs[1] + if (!CI || !CI.container) + return 0 + + if (CI.container.can_fit(I)) + return CI + + return 0 + + +/obj/machinery/appliance/mixer/can_remove_items(var/mob/user, show_warning = TRUE) + if(stat) + return 1 + else + if(show_warning) + to_chat(user, "You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.") + return 0 + +//Container is not removable +/obj/machinery/appliance/mixer/removal_menu(var/mob/user) + if (can_remove_items(user)) + var/list/menuoptions = list() + for(var/datum/cooking_item/CI as anything in cooking_objs) + if (CI.container) + if (!CI.container.check_contents()) + to_chat(user, "There's nothing in [src] you can remove!") + return + + for (var/obj/item/I in CI.container) + menuoptions[I.name] = I + + var/selection = tgui_input_list(user, "Which item would you like to remove? If you want to remove chemicals, use an empty beaker.", "Remove ingredients", menuoptions) + if (selection) + var/obj/item/I = menuoptions[selection] + if (!user || !user.put_in_hands(I)) + I.forceMove(get_turf(src)) + update_icon() + return 1 + return 0 + + +/obj/machinery/appliance/mixer/toggle_power() + set src in view(1) + set name = "Toggle Power" + set category = "Object" + + var/datum/cooking_item/CI = cooking_objs[1] + if(!CI.container.check_contents()) + to_chat("There's nothing in it! Add ingredients before turning [src] on!") + return + + if(stat & POWEROFF)//Its turned off + stat &= ~POWEROFF + if(usr) + usr.visible_message("[usr] turns the [src] on.", "You turn on \the [src].") + get_cooking_work(CI) + use_power = 2 + else //Its on, turn it off + stat |= POWEROFF + use_power = 0 + if(usr) + usr.visible_message("[usr] turns the [src] off.", "You turn off \the [src].") + playsound(src, 'sound/machines/click.ogg', 40, 1) + update_icon() + +/obj/machinery/appliance/mixer/can_insert(var/obj/item/I, var/mob/user) + if(!stat) + to_chat(user, ",You can't add items while \the [src] is running. Wait for it to finish or turn the power off to abort.") + return 0 + else + return ..() + +/obj/machinery/appliance/mixer/finish_cooking(var/datum/cooking_item/CI) + ..() + stat |= POWEROFF + playsound(src, 'sound/machines/click.ogg', 40, 1) + use_power = 0 + CI.reset() + update_icon() + +/obj/machinery/appliance/mixer/update_icon() + if (!stat) + icon_state = on_icon + if(mixer_loop) + mixer_loop.start(src) + else + icon_state = off_icon + if(mixer_loop) + mixer_loop.stop(src) + + +/obj/machinery/appliance/mixer/process() + if (!stat) + for (var/i in cooking_objs) do_cooking_tick(i) \ No newline at end of file diff --git a/code/modules/food/kitchen/cooking_machines/candy.dm b/code/modules/food/kitchen/cooking_machines/candy.dm index 92806dc9a40..2b27e2e93a3 100644 --- a/code/modules/food/kitchen/cooking_machines/candy.dm +++ b/code/modules/food/kitchen/cooking_machines/candy.dm @@ -1,44 +1,44 @@ -/obj/machinery/appliance/mixer/candy - name = "candy machine" - desc = "Get yer candied cheese wheels here!" - icon_state = "mixer_off" - off_icon = "mixer_off" - on_icon = "mixer_on" - cook_type = "candied" - appliancetype = CANDYMAKER - var/datum/looping_sound/candymaker/candymaker_loop - circuit = /obj/item/weapon/circuitboard/candymachine - cooking_coeff = 1.0 // Original Value 0.6 - - output_options = list( - "Jawbreaker" = /obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker, - "Candy Bar" = /obj/item/weapon/reagent_containers/food/snacks/variable/candybar, - "Sucker" = /obj/item/weapon/reagent_containers/food/snacks/variable/sucker, - "Jelly" = /obj/item/weapon/reagent_containers/food/snacks/variable/jelly - ) - -/obj/machinery/appliance/mixer/candy/Initialize() - . = ..() - - candymaker_loop = new(list(src), FALSE) - -/obj/machinery/appliance/mixer/candy/Destroy() - . = ..() - - QDEL_NULL(candymaker_loop) - -/obj/machinery/appliance/mixer/candy/update_icon() - . = ..() - - if(!stat) - icon_state = on_icon - if(candymaker_loop) - candymaker_loop.start(src) - else - icon_state = off_icon - if(candymaker_loop) - candymaker_loop.stop(src) - -/obj/machinery/appliance/mixer/candy/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product) - food_color = get_random_colour(1) - . = ..() +/obj/machinery/appliance/mixer/candy + name = "candy machine" + desc = "Get yer candied cheese wheels here!" + icon_state = "mixer_off" + off_icon = "mixer_off" + on_icon = "mixer_on" + cook_type = "candied" + appliancetype = CANDYMAKER + var/datum/looping_sound/candymaker/candymaker_loop + circuit = /obj/item/weapon/circuitboard/candymachine + cooking_coeff = 1.0 // Original Value 0.6 + + output_options = list( + "Jawbreaker" = /obj/item/weapon/reagent_containers/food/snacks/variable/jawbreaker, + "Candy Bar" = /obj/item/weapon/reagent_containers/food/snacks/variable/candybar, + "Sucker" = /obj/item/weapon/reagent_containers/food/snacks/variable/sucker, + "Jelly" = /obj/item/weapon/reagent_containers/food/snacks/variable/jelly + ) + +/obj/machinery/appliance/mixer/candy/Initialize() + . = ..() + + candymaker_loop = new(list(src), FALSE) + +/obj/machinery/appliance/mixer/candy/Destroy() + . = ..() + + QDEL_NULL(candymaker_loop) + +/obj/machinery/appliance/mixer/candy/update_icon() + . = ..() + + if(!stat) + icon_state = on_icon + if(candymaker_loop) + candymaker_loop.start(src) + else + icon_state = off_icon + if(candymaker_loop) + candymaker_loop.stop(src) + +/obj/machinery/appliance/mixer/candy/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product) + food_color = get_random_colour(1) + . = ..() diff --git a/code/modules/food/kitchen/cooking_machines/cereal.dm b/code/modules/food/kitchen/cooking_machines/cereal.dm index b80746add44..eb428a03408 100644 --- a/code/modules/food/kitchen/cooking_machines/cereal.dm +++ b/code/modules/food/kitchen/cooking_machines/cereal.dm @@ -1,86 +1,86 @@ -/obj/machinery/appliance/mixer/cereal - name = "cereal maker" - desc = "Now with Dann O's available!" - icon = 'icons/obj/cooking_machines.dmi' - icon_state = "cereal_off" - cook_type = "cerealized" - on_icon = "cereal_on" - off_icon = "cereal_off" - appliancetype = CEREALMAKER - var/datum/looping_sound/cerealmaker/cerealmaker_loop - circuit = /obj/item/weapon/circuitboard/cerealmaker - - output_options = list( - "Cereal" = /obj/item/weapon/reagent_containers/food/snacks/variable/cereal - ) - -/obj/machinery/appliance/mixer/cereal/Initialize() - . = ..() - - cerealmaker_loop = new(list(src), FALSE) - -/obj/machinery/appliance/mixer/cereal/Destroy() - . = ..() - - QDEL_NULL(cerealmaker_loop) - -/* -/obj/machinery/appliance/mixer/cereal/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) - . = ..() - product.name = "box of [CI.object.name] cereal" - -/obj/machinery/appliance/mixer/cereal/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product) - product.icon = 'icons/obj/food.dmi' - product.icon_state = "cereal_box" - product.filling_color = CI.object.color - - var/image/food_image = image(CI.object.icon, CI.object.icon_state) - food_image.color = CI.object.color - food_image.add_overlay(CI.object.overlays) - food_image.transform *= 0.7 - - product.add_overlay(food_image) -*/ - -/obj/machinery/appliance/mixer/cereal/update_icon() - . = ..() - - if(!stat) - icon_state = on_icon - if(cerealmaker_loop) - cerealmaker_loop.start(src) - else - icon_state = off_icon - if(cerealmaker_loop) - cerealmaker_loop.stop(src) - -/obj/machinery/appliance/mixer/cereal/combination_cook(var/datum/cooking_item/CI) - - var/list/images = list() - var/num = 0 - for(var/obj/item/I in CI.container) - if (istype(I, /obj/item/weapon/reagent_containers/food/snacks/variable/cereal)) - //Images of cereal boxes on cereal boxes is dumb - continue - - var/image/food_image = image(I.icon, I.icon_state) - food_image.color = I.color - food_image.add_overlay(I.overlays) - food_image.transform *= 0.7 - (num * 0.05) - food_image.pixel_x = rand(-2,2) - food_image.pixel_y = rand(-3,5) - - - if (!images[I.icon_state]) - images[I.icon_state] = food_image - num++ - - if (num > 3) - continue - - - var/obj/item/weapon/reagent_containers/food/snacks/result = ..() - - result.color = result.filling_color - for (var/i in images) - result.add_overlay(images[i]) +/obj/machinery/appliance/mixer/cereal + name = "cereal maker" + desc = "Now with Dann O's available!" + icon = 'icons/obj/cooking_machines.dmi' + icon_state = "cereal_off" + cook_type = "cerealized" + on_icon = "cereal_on" + off_icon = "cereal_off" + appliancetype = CEREALMAKER + var/datum/looping_sound/cerealmaker/cerealmaker_loop + circuit = /obj/item/weapon/circuitboard/cerealmaker + + output_options = list( + "Cereal" = /obj/item/weapon/reagent_containers/food/snacks/variable/cereal + ) + +/obj/machinery/appliance/mixer/cereal/Initialize() + . = ..() + + cerealmaker_loop = new(list(src), FALSE) + +/obj/machinery/appliance/mixer/cereal/Destroy() + . = ..() + + QDEL_NULL(cerealmaker_loop) + +/* +/obj/machinery/appliance/mixer/cereal/change_product_strings(var/obj/item/weapon/reagent_containers/food/snacks/product, var/datum/cooking_item/CI) + . = ..() + product.name = "box of [CI.object.name] cereal" + +/obj/machinery/appliance/mixer/cereal/change_product_appearance(var/obj/item/weapon/reagent_containers/food/snacks/product) + product.icon = 'icons/obj/food.dmi' + product.icon_state = "cereal_box" + product.filling_color = CI.object.color + + var/image/food_image = image(CI.object.icon, CI.object.icon_state) + food_image.color = CI.object.color + food_image.add_overlay(CI.object.overlays) + food_image.transform *= 0.7 + + product.add_overlay(food_image) +*/ + +/obj/machinery/appliance/mixer/cereal/update_icon() + . = ..() + + if(!stat) + icon_state = on_icon + if(cerealmaker_loop) + cerealmaker_loop.start(src) + else + icon_state = off_icon + if(cerealmaker_loop) + cerealmaker_loop.stop(src) + +/obj/machinery/appliance/mixer/cereal/combination_cook(var/datum/cooking_item/CI) + + var/list/images = list() + var/num = 0 + for(var/obj/item/I in CI.container) + if (istype(I, /obj/item/weapon/reagent_containers/food/snacks/variable/cereal)) + //Images of cereal boxes on cereal boxes is dumb + continue + + var/image/food_image = image(I.icon, I.icon_state) + food_image.color = I.color + food_image.add_overlay(I.overlays) + food_image.transform *= 0.7 - (num * 0.05) + food_image.pixel_x = rand(-2,2) + food_image.pixel_y = rand(-3,5) + + + if (!images[I.icon_state]) + images[I.icon_state] = food_image + num++ + + if (num > 3) + continue + + + var/obj/item/weapon/reagent_containers/food/snacks/result = ..() + + result.color = result.filling_color + for (var/i in images) + result.add_overlay(images[i]) diff --git a/code/modules/food/kitchen/cooking_machines/container.dm b/code/modules/food/kitchen/cooking_machines/container.dm index fa6a5fea62c..59e3935da1a 100644 --- a/code/modules/food/kitchen/cooking_machines/container.dm +++ b/code/modules/food/kitchen/cooking_machines/container.dm @@ -1,210 +1,221 @@ -//Cooking containers are used in ovens and fryers, to hold multiple ingredients for a recipe. -//They work fairly similar to the microwave - acting as a container for objects and reagents, -//which can be checked against recipe requirements in order to cook recipes that require several things - -/obj/item/weapon/reagent_containers/cooking_container - icon = 'icons/obj/cooking_machines.dmi' - var/shortname - var/max_space = 20//Maximum sum of w-classes of foods in this container at once - var/max_reagents = 80//Maximum units of reagents - var/food_items = 0 // Used for icon updates - flags = OPENCONTAINER | NOREACT - var/list/insertable = list( - /obj/item/weapon/reagent_containers/food/snacks, - /obj/item/weapon/holder, - /obj/item/weapon/paper, - /obj/item/clothing/head/wizard, - /obj/item/clothing/head/cakehat, - /obj/item/clothing/mask/gas/clown_hat, - /obj/item/clothing/head/beret - ) - -/obj/item/weapon/reagent_containers/cooking_container/Initialize() - . = ..() - create_reagents(max_reagents) - flags |= OPENCONTAINER | NOREACT - - -/obj/item/weapon/reagent_containers/cooking_container/examine(var/mob/user) - . = ..() - if (contents.len) - var/string = "It contains....
                    " - for (var/atom/movable/A in contents) - string += "[A.name]
                    " - . += "[string]" - if (reagents.total_volume) - . += "It contains [reagents.total_volume]u of reagents." - - -/obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(istype(I, /obj/item/weapon/gripper)) - var/obj/item/weapon/gripper/GR = I - if(GR.wrapped) - GR.wrapped.forceMove(get_turf(src)) - attackby(GR.wrapped, user) - if(QDELETED(GR.wrapped)) - GR.wrapped = null - - if(GR?.wrapped.loc != src) - GR.wrapped = null - - return - - for (var/possible_type in insertable) - if (istype(I, possible_type)) - if (!can_fit(I)) - to_chat(user, "There's no more space in the [src] for that!") - return 0 - - if(!user.unEquip(I) && !isturf(I.loc)) - return - I.forceMove(src) - to_chat(user, "You put the [I] into the [src].") - food_items += 1 - update_icon() - return - -/obj/item/weapon/reagent_containers/cooking_container/verb/empty() - set src in oview(1) - set name = "Empty Container" - set category = "Object" - set desc = "Removes items from the container, excluding reagents." - - do_empty(usr) - -/obj/item/weapon/reagent_containers/cooking_container/proc/do_empty(mob/user) - if (!isliving(user)) - //Here we only check for ghosts. Animals are intentionally allowed to remove things from oven trays so they can eat it - return - - if (user.stat || user.restrained()) - to_chat(user, "You are in no fit state to do this.") - return - - if (!Adjacent(user)) - to_chat(user, "You can't reach [src] from here.") - return - - if (!contents.len) - to_chat(user, "There's nothing in the [src] you can remove!") - return - - for (var/atom/movable/A in contents) - A.forceMove(get_turf(src)) - - to_chat(user, "You remove all the solid items from the [src].") - -/obj/item/weapon/reagent_containers/cooking_container/proc/check_contents() - if (contents.len == 0) - if (!reagents || reagents.total_volume == 0) - return 0//Completely empty - else if (contents.len == 1) - if (!reagents || reagents.total_volume == 0) - return 1//Contains only a single object which can be extracted alone - return 2//Contains multiple objects and/or reagents - -/obj/item/weapon/reagent_containers/cooking_container/AltClick(var/mob/user) - do_empty(user) - food_items = 0 - update_icon() - -//Deletes contents of container. -//Used when food is burned, before replacing it with a burned mess -/obj/item/weapon/reagent_containers/cooking_container/proc/clear() - for (var/atom/a in contents) - qdel(a) - - if (reagents) - reagents.clear_reagents() - -/obj/item/weapon/reagent_containers/cooking_container/proc/label(var/number, var/CT = null) - //This returns something like "Fryer basket 1 - empty" - //The latter part is a brief reminder of contents - //This is used in the removal menu - . = shortname - if (!isnull(number)) - .+= " [number]" - .+= " - " - if (CT) - .+=CT - else if (contents.len) - for (var/obj/O in contents) - .+=O.name//Just append the name of the first object - return - else if (reagents && reagents.total_volume > 0) - var/datum/reagent/R = reagents.get_master_reagent() - .+=R.name//Append name of most voluminous reagent - return - else - . += "empty" - - -/obj/item/weapon/reagent_containers/cooking_container/proc/can_fit(var/obj/item/I) - var/total = 0 - for (var/obj/item/J in contents) - total += J.w_class - - if((max_space - total) >= I.w_class) - return 1 - - -//Takes a reagent holder as input and distributes its contents among the items in the container -//Distribution is weighted based on the volume already present in each item -/obj/item/weapon/reagent_containers/cooking_container/proc/soak_reagent(var/datum/reagents/holder) - var/total = 0 - var/list/weights = list() - for (var/obj/item/I in contents) - if (I.reagents && I.reagents.total_volume) - total += I.reagents.total_volume - weights[I] = I.reagents.total_volume - - if (total > 0) - for (var/obj/item/I in contents) - if (weights[I]) - holder.trans_to(I, weights[I] / total) - -/obj/item/weapon/reagent_containers/cooking_container/update_icon() - overlays.Cut() - - if(food_items) - var/image/filling = image('icons/obj/cooking_machines.dmi', src, "[icon_state]10") - - var/percent = round((food_items / max_space) * 100) - switch(percent) - if(0 to 2) filling.icon_state = "[icon_state]" - if(3 to 24) filling.icon_state = "[icon_state]1" - if(25 to 49) filling.icon_state = "[icon_state]2" - if(50 to 74) filling.icon_state = "[icon_state]3" - if(75 to 79) filling.icon_state = "[icon_state]4" - if(80 to INFINITY) filling.icon_state = "[icon_state]5" - - overlays += filling - -/obj/item/weapon/reagent_containers/cooking_container/oven - name = "oven dish" - shortname = "shelf" - desc = "Put ingredients in this; designed for use with an oven. Warranty void if used incorrectly. Alt click to remove contents." - icon_state = "ovendish" - max_space = 30 - max_reagents = 120 - -/obj/item/weapon/reagent_containers/cooking_container/oven/Initialize() - . = ..() - - // We add to the insertable list specifically for the oven trays, to allow specialty cakes. - insertable += list( - /obj/item/clothing/head/cakehat, // This is because we want to allow birthday cakes to be makeable. - /obj/item/organ/internal/brain // As before, needed for braincake - ) - -/obj/item/weapon/reagent_containers/cooking_container/fryer - name = "fryer basket" - shortname = "basket" - desc = "Put ingredients in this; designed for use with a deep fryer. Warranty void if used incorrectly. Alt click to remove contents." - icon_state = "basket" - -/obj/item/weapon/reagent_containers/cooking_container/grill - name = "grill rack" - shortname = "rack" - desc = "Put ingredients 'in'/on this; designed for use with a grill. Warranty void if used incorrectly. Alt click to remove contents." - icon_state = "grillrack" +//Cooking containers are used in ovens and fryers, to hold multiple ingredients for a recipe. +//They work fairly similar to the microwave - acting as a container for objects and reagents, +//which can be checked against recipe requirements in order to cook recipes that require several things + +/obj/item/weapon/reagent_containers/cooking_container + icon = 'icons/obj/cooking_machines.dmi' + var/shortname + var/max_space = 20//Maximum sum of w-classes of foods in this container at once + var/max_reagents = 80//Maximum units of reagents + var/food_items = 0 // Used for icon updates + flags = OPENCONTAINER | NOREACT + var/list/insertable = list( + /obj/item/weapon/reagent_containers/food/snacks, + /obj/item/weapon/holder, + /obj/item/weapon/paper, + /obj/item/clothing/head/wizard, + /obj/item/clothing/head/cakehat, + /obj/item/clothing/mask/gas/clown_hat, + /obj/item/clothing/head/beret + ) + +/obj/item/weapon/reagent_containers/cooking_container/Initialize() + . = ..() + create_reagents(max_reagents) + flags |= OPENCONTAINER | NOREACT + + +/obj/item/weapon/reagent_containers/cooking_container/examine(var/mob/user) + . = ..() + if (contents.len) + var/string = "It contains....
                    " + for (var/atom/movable/A in contents) + string += "[A.name]
                    " + . += "[string]" + if (reagents.total_volume) + . += "It contains [reagents.total_volume]u of reagents." + + +/obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(istype(I, /obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/GR = I + if(GR.wrapped) + GR.wrapped.forceMove(get_turf(src)) + attackby(GR.wrapped, user) + if(QDELETED(GR.wrapped)) + GR.wrapped = null + + if(GR?.wrapped.loc != src) + GR.wrapped = null + + return + + for (var/possible_type in insertable) + if (istype(I, possible_type)) + if (!can_fit(I)) + to_chat(user, "There's no more space in the [src] for that!") + return 0 + + if(!user.unEquip(I) && !isturf(I.loc)) + return + I.forceMove(src) + to_chat(user, "You put the [I] into the [src].") + food_items += 1 + update_icon() + return + +/obj/item/weapon/reagent_containers/cooking_container/verb/empty() + set src in oview(1) + set name = "Empty Container" + set category = "Object" + set desc = "Removes items from the container, excluding reagents." + + do_empty(usr) + +/obj/item/weapon/reagent_containers/cooking_container/proc/do_empty(mob/user) + if (!isliving(user)) + //Here we only check for ghosts. Animals are intentionally allowed to remove things from oven trays so they can eat it + return + + if (user.stat || user.restrained()) + to_chat(user, "You are in no fit state to do this.") + return + + if (!Adjacent(user)) + to_chat(user, "You can't reach [src] from here.") + return + + if (!contents.len) + to_chat(user, "There's nothing in the [src] you can remove!") + return + + for (var/atom/movable/A in contents) + A.forceMove(get_turf(src)) + + to_chat(user, "You remove all the solid items from the [src].") + +/obj/item/weapon/reagent_containers/cooking_container/proc/check_contents() + if (contents.len == 0) + if (!reagents || reagents.total_volume == 0) + return 0//Completely empty + else if (contents.len == 1) + if (!reagents || reagents.total_volume == 0) + return 1//Contains only a single object which can be extracted alone + return 2//Contains multiple objects and/or reagents + +/obj/item/weapon/reagent_containers/cooking_container/AltClick(var/mob/user) + do_empty(user) + food_items = 0 + update_icon() + +//Deletes contents of container. +//Used when food is burned, before replacing it with a burned mess +/obj/item/weapon/reagent_containers/cooking_container/proc/clear() + for (var/atom/a in contents) + qdel(a) + + if (reagents) + reagents.clear_reagents() + +/obj/item/weapon/reagent_containers/cooking_container/proc/label(var/number, var/CT = null) + //This returns something like "Fryer basket 1 - empty" + //The latter part is a brief reminder of contents + //This is used in the removal menu + . = shortname + if (!isnull(number)) + .+= " [number]" + .+= " - " + if (CT) + .+=CT + else if (contents.len) + for (var/obj/O in contents) + .+=O.name//Just append the name of the first object + return + else if (reagents && reagents.total_volume > 0) + var/datum/reagent/R = reagents.get_master_reagent() + .+=R.name//Append name of most voluminous reagent + return + else + . += "empty" + + +/obj/item/weapon/reagent_containers/cooking_container/proc/can_fit(var/obj/item/I) + var/total = 0 + for (var/obj/item/J in contents) + total += J.w_class + + if((max_space - total) >= I.w_class) + return 1 + + +//Takes a reagent holder as input and distributes its contents among the items in the container +//Distribution is weighted based on the volume already present in each item +/obj/item/weapon/reagent_containers/cooking_container/proc/soak_reagent(var/datum/reagents/holder) + var/total = 0 + var/list/weights = list() + for (var/obj/item/I in contents) + if (I.reagents && I.reagents.total_volume) + total += I.reagents.total_volume + weights[I] = I.reagents.total_volume + + if (total > 0) + for (var/obj/item/I in contents) + if (weights[I]) + holder.trans_to(I, weights[I] / total) + +/obj/item/weapon/reagent_containers/cooking_container/update_icon() + overlays.Cut() + + if(food_items) + var/image/filling = image('icons/obj/cooking_machines.dmi', src, "[icon_state]10") + + var/percent = round((food_items / max_space) * 100) + switch(percent) + if(0 to 2) filling.icon_state = "[icon_state]" + if(3 to 24) filling.icon_state = "[icon_state]1" + if(25 to 49) filling.icon_state = "[icon_state]2" + if(50 to 74) filling.icon_state = "[icon_state]3" + if(75 to 79) filling.icon_state = "[icon_state]4" + if(80 to INFINITY) filling.icon_state = "[icon_state]5" + + overlays += filling + +/obj/item/weapon/reagent_containers/cooking_container/oven + name = "oven dish" + shortname = "shelf" + desc = "Put ingredients in this; designed for use with an oven. Warranty void if used incorrectly. Alt click to remove contents." + icon_state = "ovendish" + max_space = 30 + max_reagents = 120 + +/obj/item/weapon/reagent_containers/cooking_container/oven/Initialize() + . = ..() + + // We add to the insertable list specifically for the oven trays, to allow specialty cakes. + insertable += list( + /obj/item/organ/internal/brain // As before, needed for braincake + ) + +/obj/item/weapon/reagent_containers/cooking_container/fryer + name = "fryer basket" + shortname = "basket" + desc = "Put ingredients in this; designed for use with a deep fryer. Warranty void if used incorrectly. Alt click to remove contents." + icon_state = "basket" + +/obj/item/weapon/reagent_containers/cooking_container/grill + name = "grill rack" + shortname = "rack" + desc = "Put ingredients 'in'/on this; designed for use with a grill. Warranty void if used incorrectly. Alt click to remove contents." + icon_state = "grillrack" + +/obj/item/weapon/reagent_containers/cooking_container/grill/Initialize() + . = ..() + + // Needed for the special recipes of the grill + insertable += list( + /obj/item/organ/internal/brain, + /obj/item/robot_parts/head, + /obj/item/weapon/ectoplasm, + /obj/item/weapon/holder/mouse, + /obj/item/stack/rods + ) diff --git a/code/modules/food/kitchen/cooking_machines/fryer.dm b/code/modules/food/kitchen/cooking_machines/fryer.dm index 6e28243baca..5746e18c4cd 100644 --- a/code/modules/food/kitchen/cooking_machines/fryer.dm +++ b/code/modules/food/kitchen/cooking_machines/fryer.dm @@ -233,8 +233,8 @@ else to_chat(victim, "Searing hot oil scorches your [E ? E.name : "flesh"]!") - user.attack_log += text("\[[time_stamp()]\] Has [cook_type] \the [victim] ([victim.ckey]) in \a [src]") - victim.attack_log += text("\[[time_stamp()]\] Has been [cook_type] in \a [src] by [user.name] ([user.ckey])") + user.attack_log += text("\[[time_stamp()]\] [span_red("Has [cook_type] \the [victim] ([victim.ckey]) in \a [src]")]") + victim.attack_log += text("\[[time_stamp()]\] [span_orange("Has been [cook_type] in \a [src] by [user.name] ([user.ckey])")]") msg_admin_attack("[key_name_admin(user)] [cook_type] \the [victim] ([victim.ckey]) in \a [src]. (JMP)") //Coat the victim in some oil diff --git a/code/modules/food/kitchen/cooking_machines/grill.dm b/code/modules/food/kitchen/cooking_machines/grill.dm index 7bdfe526a39..d7e18290922 100644 --- a/code/modules/food/kitchen/cooking_machines/grill.dm +++ b/code/modules/food/kitchen/cooking_machines/grill.dm @@ -1,49 +1,49 @@ -/obj/machinery/appliance/cooker/grill - name = "grill" - desc = "Backyard grilling, IN SPACE." - icon_state = "grill_off" - cook_type = "grilled" - appliancetype = GRILL - food_color = "#A34719" - on_icon = "grill_on" - off_icon = "grill_off" - can_burn_food = TRUE - var/datum/looping_sound/grill/grill_loop - circuit = /obj/item/weapon/circuitboard/grill - active_power_usage = 4 KILOWATTS - heating_power = 4000 - idle_power_usage = 2 KILOWATTS - - optimal_power = 1.2 // Things on the grill cook .6 faster - this is now the fastest appliance to heat and to cook on. BURGERS GO SIZZLE. - - stat = POWEROFF // Starts turned off. - - // Grill is faster to heat and setup than the rest. - optimal_temp = 120 + T0C - min_temp = 60 + T0C - resistance = 8 KILOWATTS // Very fast to heat up. - - max_contents = 3 // Arbitrary number, 3 grill 'racks' - container_type = /obj/item/weapon/reagent_containers/cooking_container/grill - -/obj/machinery/appliance/cooker/grill/Initialize() - . = ..() - grill_loop = new(list(src), FALSE) - -/obj/machinery/appliance/cooker/grill/Destroy() - QDEL_NULL(grill_loop) - return ..() - -/obj/machinery/appliance/cooker/grill/update_icon() // TODO: Cooking icon - if(!stat) - icon_state = on_icon - if(cooking == TRUE) - if(grill_loop) - grill_loop.start(src) - else - if(grill_loop) - grill_loop.stop(src) - else - icon_state = off_icon - if(grill_loop) - grill_loop.stop(src) +/obj/machinery/appliance/cooker/grill + name = "grill" + desc = "Backyard grilling, IN SPACE." + icon_state = "grill_off" + cook_type = "grilled" + appliancetype = GRILL + food_color = "#A34719" + on_icon = "grill_on" + off_icon = "grill_off" + can_burn_food = TRUE + var/datum/looping_sound/grill/grill_loop + circuit = /obj/item/weapon/circuitboard/grill + active_power_usage = 4 KILOWATTS + heating_power = 4000 + idle_power_usage = 2 KILOWATTS + + optimal_power = 1.2 // Things on the grill cook .6 faster - this is now the fastest appliance to heat and to cook on. BURGERS GO SIZZLE. + + stat = POWEROFF // Starts turned off. + + // Grill is faster to heat and setup than the rest. + optimal_temp = 120 + T0C + min_temp = 60 + T0C + resistance = 8 KILOWATTS // Very fast to heat up. + + max_contents = 3 // Arbitrary number, 3 grill 'racks' + container_type = /obj/item/weapon/reagent_containers/cooking_container/grill + +/obj/machinery/appliance/cooker/grill/Initialize() + . = ..() + grill_loop = new(list(src), FALSE) + +/obj/machinery/appliance/cooker/grill/Destroy() + QDEL_NULL(grill_loop) + return ..() + +/obj/machinery/appliance/cooker/grill/update_icon() // TODO: Cooking icon + if(!stat) + icon_state = on_icon + if(cooking == TRUE) + if(grill_loop) + grill_loop.start(src) + else + if(grill_loop) + grill_loop.stop(src) + else + icon_state = off_icon + if(grill_loop) + grill_loop.stop(src) diff --git a/code/modules/food/kitchen/cooking_machines/oven.dm b/code/modules/food/kitchen/cooking_machines/oven.dm index bcebc851a00..a34f7e35830 100644 --- a/code/modules/food/kitchen/cooking_machines/oven.dm +++ b/code/modules/food/kitchen/cooking_machines/oven.dm @@ -1,155 +1,155 @@ -/obj/machinery/appliance/cooker/oven - name = "oven" - desc = "Cookies are ready, dear." - icon = 'icons/obj/cooking_machines.dmi' - icon_state = "ovenopen" - cook_type = "baked" - appliancetype = OVEN - food_color = "#A34719" - can_burn_food = TRUE - var/datum/looping_sound/oven/oven_loop - circuit = /obj/item/weapon/circuitboard/oven - active_power_usage = 6 KILOWATTS - heating_power = 6 KILOWATTS - //Based on a double deck electric convection oven - - resistance = 12 KILOWATTS // Approx. 12 minutes to heat up. - idle_power_usage = 2 KILOWATTS - //uses ~30% power to stay warm - optimal_power = 0.8 // Oven cooks .2 faster than the default speed. - - light_x = 3 - light_y = 4 - max_contents = 5 - container_type = /obj/item/weapon/reagent_containers/cooking_container/oven - - stat = POWEROFF //Starts turned off - - var/open = FALSE // Start closed just so people don't try to preheat with it open, lol. - - output_options = list( - "Pizza" = /obj/item/weapon/reagent_containers/food/snacks/variable/pizza, - "Bread" = /obj/item/weapon/reagent_containers/food/snacks/variable/bread, - "Pie" = /obj/item/weapon/reagent_containers/food/snacks/variable/pie, - "Cake" = /obj/item/weapon/reagent_containers/food/snacks/variable/cake, - "Hot Pocket" = /obj/item/weapon/reagent_containers/food/snacks/variable/pocket, - "Kebab" = /obj/item/weapon/reagent_containers/food/snacks/variable/kebab, - "Waffles" = /obj/item/weapon/reagent_containers/food/snacks/variable/waffles, - "Cookie" = /obj/item/weapon/reagent_containers/food/snacks/variable/cookie, - "Donut" = /obj/item/weapon/reagent_containers/food/snacks/variable/donut, - ) - -/obj/machinery/appliance/cooker/oven/Initialize() - . = ..() - - oven_loop = new(list(src), FALSE) - -/obj/machinery/appliance/cooker/oven/Destroy() - QDEL_NULL(oven_loop) - return ..() - -/obj/machinery/appliance/cooker/oven/update_icon() - if(!open) - if(!stat) - icon_state = "ovenclosed_on" - if(cooking == TRUE) - icon_state = "ovenclosed_cooking" - if(oven_loop) - oven_loop.start(src) - else - icon_state = "ovenclosed_on" - if(oven_loop) - oven_loop.stop(src) - else - icon_state = "ovenclosed_off" - if(oven_loop) - oven_loop.stop(src) - else - icon_state = "ovenopen" - if(oven_loop) - oven_loop.stop(src) - ..() - -/obj/machinery/appliance/cooker/oven/AltClick(var/mob/user) - try_toggle_door(user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - -/obj/machinery/appliance/cooker/oven/verb/toggle_door() - set src in oview(1) - set category = "Object" - set name = "Open/close oven door" - - try_toggle_door(usr) - -/obj/machinery/appliance/cooker/oven/proc/try_toggle_door(mob/user) - if(!isliving(usr) || isAI(user)) - return - - if(!usr.IsAdvancedToolUser()) - to_chat(user, "You lack the dexterity to do that.") - return - - if(!Adjacent(usr)) - to_chat(user, "You can't reach the [src] from there, get closer!") - return - - if(open) - open = FALSE - loss = (heating_power / resistance) * 0.5 - cooking = TRUE - else - open = TRUE - loss = (heating_power / resistance) * 4 - //When the oven door is opened, heat is lost MUCH faster and you stop cooking (because the door is open) - cooking = FALSE - - playsound(src, 'sound/machines/hatch_open.ogg', 20, 1) - to_chat(user, "You [open? "open":"close"] the oven door") - update_icon() - -/obj/machinery/appliance/cooker/oven/proc/manip(var/obj/item/I) - // check if someone's trying to manipulate the machine - - if(I.is_crowbar() || I.is_screwdriver() || istype(I, /obj/item/weapon/storage/part_replacer)) - return TRUE - else - return FALSE - -/obj/machinery/appliance/cooker/oven/can_insert(var/obj/item/I, var/mob/user) - if(!open && !manip(I)) - to_chat(user, "You can't put anything in while the door is closed!") - return 0 - - else - return ..() - - -//If an oven's door is open it will lose heat every proc, even if it also gained it -//But dont call equalize twice in one stack. A return value of -1 from the parent indicates equalize was already called -/obj/machinery/appliance/cooker/oven/heat_up() - .=..() - if(open && . != -1) - var/turf/T = get_turf(src) - if(temperature > T.temperature) - equalize_temperature() - -/obj/machinery/appliance/cooker/oven/can_remove_items(var/mob/user, show_warning = TRUE) - if(!open) - if(show_warning) - to_chat(user, "You can't take anything out while the door is closed!") - return 0 - - else - return ..() - - -//Oven has lots of recipes and combine options. The chance for interference is high, so -//If a combine target is set the oven will do it instead of checking recipes -/obj/machinery/appliance/cooker/oven/finish_cooking(var/datum/cooking_item/CI) - if(CI.combine_target) - CI.result_type = 3//Combination type. We're making something out of our ingredients - visible_message("\The [src] pings!") - combination_cook(CI) - return - else - ..() +/obj/machinery/appliance/cooker/oven + name = "oven" + desc = "Cookies are ready, dear." + icon = 'icons/obj/cooking_machines.dmi' + icon_state = "ovenopen" + cook_type = "baked" + appliancetype = OVEN + food_color = "#A34719" + can_burn_food = TRUE + var/datum/looping_sound/oven/oven_loop + circuit = /obj/item/weapon/circuitboard/oven + active_power_usage = 6 KILOWATTS + heating_power = 6 KILOWATTS + //Based on a double deck electric convection oven + + resistance = 12 KILOWATTS // Approx. 12 minutes to heat up. + idle_power_usage = 2 KILOWATTS + //uses ~30% power to stay warm + optimal_power = 0.8 // Oven cooks .2 faster than the default speed. + + light_x = 3 + light_y = 4 + max_contents = 5 + container_type = /obj/item/weapon/reagent_containers/cooking_container/oven + + stat = POWEROFF //Starts turned off + + var/open = FALSE // Start closed just so people don't try to preheat with it open, lol. + + output_options = list( + "Pizza" = /obj/item/weapon/reagent_containers/food/snacks/variable/pizza, + "Bread" = /obj/item/weapon/reagent_containers/food/snacks/variable/bread, + "Pie" = /obj/item/weapon/reagent_containers/food/snacks/variable/pie, + "Cake" = /obj/item/weapon/reagent_containers/food/snacks/variable/cake, + "Hot Pocket" = /obj/item/weapon/reagent_containers/food/snacks/variable/pocket, + "Kebab" = /obj/item/weapon/reagent_containers/food/snacks/variable/kebab, + "Waffles" = /obj/item/weapon/reagent_containers/food/snacks/variable/waffles, + "Cookie" = /obj/item/weapon/reagent_containers/food/snacks/variable/cookie, + "Donut" = /obj/item/weapon/reagent_containers/food/snacks/variable/donut, + ) + +/obj/machinery/appliance/cooker/oven/Initialize() + . = ..() + + oven_loop = new(list(src), FALSE) + +/obj/machinery/appliance/cooker/oven/Destroy() + QDEL_NULL(oven_loop) + return ..() + +/obj/machinery/appliance/cooker/oven/update_icon() + if(!open) + if(!stat) + icon_state = "ovenclosed_on" + if(cooking == TRUE) + icon_state = "ovenclosed_cooking" + if(oven_loop) + oven_loop.start(src) + else + icon_state = "ovenclosed_on" + if(oven_loop) + oven_loop.stop(src) + else + icon_state = "ovenclosed_off" + if(oven_loop) + oven_loop.stop(src) + else + icon_state = "ovenopen" + if(oven_loop) + oven_loop.stop(src) + ..() + +/obj/machinery/appliance/cooker/oven/AltClick(var/mob/user) + try_toggle_door(user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + +/obj/machinery/appliance/cooker/oven/verb/toggle_door() + set src in oview(1) + set category = "Object" + set name = "Open/close oven door" + + try_toggle_door(usr) + +/obj/machinery/appliance/cooker/oven/proc/try_toggle_door(mob/user) + if(!isliving(usr) || isAI(user)) + return + + if(!usr.IsAdvancedToolUser()) + to_chat(user, "You lack the dexterity to do that.") + return + + if(!Adjacent(usr)) + to_chat(user, "You can't reach the [src] from there, get closer!") + return + + if(open) + open = FALSE + loss = (heating_power / resistance) * 0.5 + cooking = TRUE + else + open = TRUE + loss = (heating_power / resistance) * 4 + //When the oven door is opened, heat is lost MUCH faster and you stop cooking (because the door is open) + cooking = FALSE + + playsound(src, 'sound/machines/hatch_open.ogg', 20, 1) + to_chat(user, "You [open? "open":"close"] the oven door") + update_icon() + +/obj/machinery/appliance/cooker/oven/proc/manip(var/obj/item/I) + // check if someone's trying to manipulate the machine + + if(I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/weapon/storage/part_replacer)) + return TRUE + else + return FALSE + +/obj/machinery/appliance/cooker/oven/can_insert(var/obj/item/I, var/mob/user) + if(!open && !manip(I)) + to_chat(user, "You can't put anything in while the door is closed!") + return 0 + + else + return ..() + + +//If an oven's door is open it will lose heat every proc, even if it also gained it +//But dont call equalize twice in one stack. A return value of -1 from the parent indicates equalize was already called +/obj/machinery/appliance/cooker/oven/heat_up() + .=..() + if(open && . != -1) + var/turf/T = get_turf(src) + if(temperature > T.temperature) + equalize_temperature() + +/obj/machinery/appliance/cooker/oven/can_remove_items(var/mob/user, show_warning = TRUE) + if(!open) + if(show_warning) + to_chat(user, "You can't take anything out while the door is closed!") + return 0 + + else + return ..() + + +//Oven has lots of recipes and combine options. The chance for interference is high, so +//If a combine target is set the oven will do it instead of checking recipes +/obj/machinery/appliance/cooker/oven/finish_cooking(var/datum/cooking_item/CI) + if(CI.combine_target) + CI.result_type = 3//Combination type. We're making something out of our ingredients + visible_message("\The [src] pings!") + combination_cook(CI) + return + else + ..() diff --git a/code/modules/food/kitchen/gibber.dm b/code/modules/food/kitchen/gibber.dm index 329cba2edf1..ac4218f87fe 100644 --- a/code/modules/food/kitchen/gibber.dm +++ b/code/modules/food/kitchen/gibber.dm @@ -1,248 +1,247 @@ - -/obj/machinery/gibber - name = "gibber" - desc = "The name isn't descriptive enough?" - icon = 'icons/obj/kitchen.dmi' - icon_state = "grinder" - density = TRUE - anchored = TRUE - unacidable = TRUE - req_access = list(access_kitchen,access_morgue) - - var/operating = 0 //Is it on? - var/dirty = 0 // Does it need cleaning? - var/mob/living/occupant // Mob who has been put inside - var/gib_time = 40 // Time from starting until meat appears - var/gib_throw_dir = WEST // Direction to spit meat and gibs in. - - use_power = USE_POWER_IDLE - idle_power_usage = 2 - active_power_usage = 500 - -//auto-gibs anything that bumps into it -/obj/machinery/gibber/autogibber - var/turf/input_plate - -/obj/machinery/gibber/autogibber/Initialize() - . = ..() - for(var/i in cardinal) - var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) - if(input_obj) - if(isturf(input_obj.loc)) - input_plate = input_obj.loc - gib_throw_dir = i - qdel(input_obj) - break - - if(!input_plate) - log_misc("a [src] didn't find an input plate.") - -/obj/machinery/gibber/Destroy() - occupant = null - return ..() - -/obj/machinery/gibber/autogibber/Destroy() - input_plate = null - return ..() - -/obj/machinery/gibber/autogibber/Bumped(var/atom/A) - if(!input_plate) return - - if(ismob(A)) - var/mob/M = A - - if(M.loc == input_plate - ) - M.loc = src - M.gib() - - -/obj/machinery/gibber/New() - ..() - add_overlay("grjam") - -/obj/machinery/gibber/update_icon() - cut_overlays() - if (dirty) - add_overlay("grbloody") - if(stat & (NOPOWER|BROKEN)) - return - if (!occupant) - add_overlay("grjam") - else if (operating) - add_overlay("gruse") - else - add_overlay("gridle") - -/obj/machinery/gibber/relaymove(mob/user as mob) - src.go_out() - return - -/obj/machinery/gibber/attack_hand(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - if(operating) - to_chat(user, "The gibber is locked and running, wait for it to finish.") - return - else - src.startgibbing(user) - -/obj/machinery/gibber/examine() - . = ..() - . += "The safety guard is [emagged ? "disabled" : "enabled"]." - -/obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) - emagged = !emagged - to_chat(user, "You [emagged ? "disable" : "enable"] the gibber safety guard.") - return 1 - -/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) - var/obj/item/weapon/grab/G = W - - if(default_unfasten_wrench(user, W, 40)) - return - - if(!istype(G)) - return ..() - - if(G.state < 2) - to_chat(user, "You need a better grip to do that!") - return - - move_into_gibber(user,G.affecting) - // Grab() process should clean up the grab item, no need to del it. - -/obj/machinery/gibber/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.restrained()) - return - move_into_gibber(user,target) - -/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) - - if(src.occupant) - to_chat(user, "The gibber is full, empty it first!") - return - - if(operating) - to_chat(user, "The gibber is locked and running, wait for it to finish.") - return - - if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_mob)) ) - to_chat(user, "This is not suitable for the gibber!") - return - - if(istype(victim,/mob/living/carbon/human) && !emagged) - to_chat(user, "The gibber safety guard is engaged!") - return - - - if(victim.abiotic(1)) - to_chat(user, "Subject may not have abiotic items on.") - return - - user.visible_message("[user] starts to put [victim] into the gibber!") - src.add_fingerprint(user) - if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) - user.visible_message("[user] stuffs [victim] into the gibber!") - if(victim.client) - victim.client.perspective = EYE_PERSPECTIVE - victim.client.eye = src - victim.loc = src - src.occupant = victim - update_icon() - -/obj/machinery/gibber/verb/eject() - set category = "Object" - set name = "Empty Gibber" - set src in oview(1) - - if (usr.stat != 0) - return - src.go_out() - add_fingerprint(usr) - return - -/obj/machinery/gibber/proc/go_out() - if(operating || !src.occupant) - return - for(var/obj/O in src) - O.loc = src.loc - if (src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - src.occupant.loc = src.loc - src.occupant = null - update_icon() - return - - -/obj/machinery/gibber/proc/startgibbing(mob/user as mob) - if(src.operating) - return - if(!src.occupant) - visible_message("You hear a loud metallic grinding sound.") - return - - use_power(1000) - visible_message("You hear a loud [occupant.isSynthetic() ? "metallic" : "squelchy"] grinding sound.") - src.operating = 1 - update_icon() - - var/slab_name = occupant.name - var/slab_count = 2 + occupant.meat_amount - var/slab_type = occupant.meat_type ? occupant.meat_type : /obj/item/weapon/reagent_containers/food/snacks/meat - var/slab_nutrition = src.occupant.nutrition / 15 - - var/list/byproducts = occupant?.butchery_loot?.Copy() - - if(istype(src.occupant,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = occupant - slab_name = src.occupant.real_name - slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type - - // Small mobs don't give as much nutrition. - if(issmall(src.occupant)) - slab_nutrition *= 0.5 - slab_nutrition /= slab_count - - while(slab_count) - slab_count-- - var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) - if(istype(new_meat)) - new_meat.name = "[slab_name] [new_meat.name]" - new_meat.reagents.add_reagent("nutriment",slab_nutrition) - if(src.occupant.reagents) - src.occupant.reagents.trans_to_obj(new_meat, round(occupant.reagents.total_volume/(2 + occupant.meat_amount),1)) - - add_attack_logs(user,occupant,"Used [src] to gib") - - src.occupant.ghostize() - - spawn(gib_time) - occupant.gib() - occupant = null - playsound(src, 'sound/effects/splat.ogg', 50, 1) - operating = 0 - if(LAZYLEN(byproducts)) - for(var/path in byproducts) - while(byproducts[path]) - if(prob(min(90,30 * byproducts[path]))) - new path(src) - - byproducts[path] -= 1 - - for (var/obj/thing in contents) - // There's a chance that the gibber will fail to destroy or butcher some evidence. - if(istype(thing,/obj/item/organ) && prob(80)) - var/obj/item/organ/OR = thing - if(OR.can_butcher(src)) - OR.butcher(src, null, src) // Butcher it, and add it to our list of things to launch. - else - qdel(thing) - continue - thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. - thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. - - update_icon() - - + +/obj/machinery/gibber + name = "gibber" + desc = "The name isn't descriptive enough?" + icon = 'icons/obj/kitchen.dmi' + icon_state = "grinder" + density = TRUE + anchored = TRUE + unacidable = TRUE + req_access = list(access_kitchen,access_morgue) + + var/operating = 0 //Is it on? + var/dirty = 0 // Does it need cleaning? + var/mob/living/occupant // Mob who has been put inside + var/gib_time = 40 // Time from starting until meat appears + var/gib_throw_dir = WEST // Direction to spit meat and gibs in. + + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 500 + +//auto-gibs anything that bumps into it +/obj/machinery/gibber/autogibber + var/turf/input_plate + +/obj/machinery/gibber/autogibber/Initialize() + . = ..() + for(var/i in cardinal) + var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) + if(input_obj) + if(isturf(input_obj.loc)) + input_plate = input_obj.loc + gib_throw_dir = i + qdel(input_obj) + break + + if(!input_plate) + log_misc("a [src] didn't find an input plate.") + +/obj/machinery/gibber/Destroy() + occupant = null + return ..() + +/obj/machinery/gibber/autogibber/Destroy() + input_plate = null + return ..() + +/obj/machinery/gibber/autogibber/Bumped(var/atom/A) + if(!input_plate) return + + if(ismob(A)) + var/mob/M = A + + if(M.loc == input_plate + ) + M.loc = src + M.gib() + + +/obj/machinery/gibber/New() + ..() + add_overlay("grjam") + +/obj/machinery/gibber/update_icon() + cut_overlays() + if (dirty) + add_overlay("grbloody") + if(stat & (NOPOWER|BROKEN)) + return + if (!occupant) + add_overlay("grjam") + else if (operating) + add_overlay("gruse") + else + add_overlay("gridle") + +/obj/machinery/gibber/relaymove(mob/user as mob) + src.go_out() + return + +/obj/machinery/gibber/attack_hand(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + return + if(operating) + to_chat(user, "The gibber is locked and running, wait for it to finish.") + return + else + src.startgibbing(user) + +/obj/machinery/gibber/examine() + . = ..() + . += "The safety guard is [emagged ? "disabled" : "enabled"]." + +/obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) + emagged = !emagged + to_chat(user, "You [emagged ? "disable" : "enable"] the gibber safety guard.") + return 1 + +/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) + var/obj/item/weapon/grab/G = W + + if(default_unfasten_wrench(user, W, 40)) + return + + if(!istype(G)) + return ..() + + if(G.state < 2) + to_chat(user, "You need a better grip to do that!") + return + + move_into_gibber(user,G.affecting) + // Grab() process should clean up the grab item, no need to del it. + +/obj/machinery/gibber/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.restrained()) + return + move_into_gibber(user,target) + +/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) + + if(src.occupant) + to_chat(user, "The gibber is full, empty it first!") + return + + if(operating) + to_chat(user, "The gibber is locked and running, wait for it to finish.") + return + + if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_mob)) ) + to_chat(user, "This is not suitable for the gibber!") + return + + if(istype(victim,/mob/living/carbon/human) && !emagged) + to_chat(user, "The gibber safety guard is engaged!") + return + + + if(victim.abiotic(1)) + to_chat(user, "Subject may not have abiotic items on.") + return + + user.visible_message("[user] starts to put [victim] into the gibber!") + src.add_fingerprint(user) + if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) + user.visible_message("[user] stuffs [victim] into the gibber!") + if(victim.client) + victim.client.perspective = EYE_PERSPECTIVE + victim.client.eye = src + victim.loc = src + src.occupant = victim + update_icon() + +/obj/machinery/gibber/verb/eject() + set category = "Object" + set name = "Empty Gibber" + set src in oview(1) + + if (usr.stat != 0) + return + src.go_out() + add_fingerprint(usr) + return + +/obj/machinery/gibber/proc/go_out() + if(operating || !src.occupant) + return + for(var/obj/O in src) + O.loc = src.loc + if (src.occupant.client) + src.occupant.client.eye = src.occupant.client.mob + src.occupant.client.perspective = MOB_PERSPECTIVE + src.occupant.loc = src.loc + src.occupant = null + update_icon() + return + + +/obj/machinery/gibber/proc/startgibbing(mob/user as mob) + if(src.operating) + return + if(!src.occupant) + visible_message("You hear a loud metallic grinding sound.") + return + + use_power(1000) + visible_message("You hear a loud [occupant.isSynthetic() ? "metallic" : "squelchy"] grinding sound.") + src.operating = 1 + update_icon() + + var/slab_name = occupant.name + var/slab_count = 2 + occupant.meat_amount + var/slab_type = occupant.meat_type ? occupant.meat_type : /obj/item/weapon/reagent_containers/food/snacks/meat + var/slab_nutrition = src.occupant.nutrition / 15 + + var/list/byproducts = occupant?.butchery_loot?.Copy() + + if(istype(src.occupant,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = occupant + slab_name = src.occupant.real_name + slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type + + // Small mobs don't give as much nutrition. + if(issmall(src.occupant)) + slab_nutrition *= 0.5 + slab_nutrition /= slab_count + + for(var/i=1 to slab_count) + var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) + if(istype(new_meat)) + new_meat.name = "[slab_name] [new_meat.name]" + new_meat.reagents.add_reagent("nutriment",slab_nutrition) + if(src.occupant.reagents) + src.occupant.reagents.trans_to_obj(new_meat, round(occupant.reagents.total_volume/(2 + occupant.meat_amount),1)) + + add_attack_logs(user,occupant,"Used [src] to gib") + + src.occupant.ghostize() + + spawn(gib_time) + occupant.gib() + occupant = null + playsound(src, 'sound/effects/splat.ogg', 50, 1) + operating = 0 + if(LAZYLEN(byproducts)) + for(var/path in byproducts) + while(byproducts[path]) + if(prob(min(90,30 * byproducts[path]))) + new path(src) + + byproducts[path] -= 1 + + for (var/obj/thing in contents) + // There's a chance that the gibber will fail to destroy or butcher some evidence. + if(istype(thing,/obj/item/organ) && prob(80)) + var/obj/item/organ/OR = thing + if(OR.can_butcher(src)) + OR.butcher(src, null, src) // Butcher it, and add it to our list of things to launch. + else + qdel(thing) + continue + thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. + thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. + + update_icon() + + diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm index dd30240eecf..51073b48d16 100644 --- a/code/modules/food/kitchen/microwave.dm +++ b/code/modules/food/kitchen/microwave.dm @@ -1,670 +1,671 @@ -/obj/machinery/microwave - name = "Microwave" - desc = "Studies are inconclusive on whether pressing your face against the glass is harmful." - icon = 'icons/obj/kitchen.dmi' - icon_state = "mw" - layer = 2.9 - density = TRUE - anchored = TRUE - unacidable = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 2000 - clicksound = "button" - clickvol = "30" - flags = OPENCONTAINER | NOREACT - circuit = /obj/item/weapon/circuitboard/microwave - var/operating = 0 // Is it on? - var/dirty = 0 // = {0..100} Does it need cleaning? - var/broken = 0 // ={0,1,2} How broken is it??? - var/circuit_item_capacity = 1 //how many items does the circuit add to max number of items - var/item_level = 0 // items microwave can handle, 0 foodstuff, 1 materials - var/global/list/acceptable_items // List of the items you can put in - var/global/list/available_recipes // List of the recipes you can use - var/global/list/acceptable_reagents // List of the reagents you can put in - - var/global/max_n_of_items = 20 - var/appliancetype = MICROWAVE - var/datum/looping_sound/microwave/soundloop - - -//see code/modules/food/recipes_microwave.dm for recipes - -/******************* -* Initialising -********************/ - -/obj/machinery/microwave/Initialize() - . = ..() - - reagents = new/datum/reagents(100) - reagents.my_atom = src - - default_apply_parts() - - if(!available_recipes) - available_recipes = new - for(var/datum/recipe/typepath as anything in subtypesof(/datum/recipe)) - if((initial(typepath.appliance) & appliancetype)) - available_recipes += new typepath - - acceptable_items = new - acceptable_reagents = new - for (var/datum/recipe/recipe in available_recipes) - for (var/item in recipe.items) - acceptable_items |= item - for (var/reagent in recipe.reagents) - acceptable_reagents |= reagent - // This will do until I can think of a fun recipe to use dionaea in - - // will also allow anything using the holder item to be microwaved into - // impure carbon. ~Z - acceptable_items |= /obj/item/weapon/holder - acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown - acceptable_items |= /obj/item/device/soulstone - acceptable_items |= /obj/item/weapon/fuel_assembly/supermatter - - soundloop = new(list(src), FALSE) - -/obj/machinery/microwave/Destroy() - if(paicard) - ejectpai() // Lets not delete the pAI. - QDEL_NULL(soundloop) - return ..() - -/******************* -* Item Adding -********************/ - -/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(src.broken > 0) - if(src.broken == 2 && O.is_screwdriver()) // If it's broken and they're using a screwdriver - user.visible_message( \ - "\The [user] starts to fix part of the microwave.", \ - "You start to fix part of the microwave." \ - ) - playsound(src, O.usesound, 50, 1) - if (do_after(user,20 * O.toolspeed)) - user.visible_message( \ - "\The [user] fixes part of the microwave.", \ - "You have fixed part of the microwave." \ - ) - src.broken = 1 // Fix it a bit - else if(src.broken == 1 && O.is_wrench()) // If it's broken and they're doing the wrench - user.visible_message( \ - "\The [user] starts to fix part of the microwave.", \ - "You start to fix part of the microwave." \ - ) - if (do_after(user,20 * O.toolspeed)) - user.visible_message( \ - "\The [user] fixes the microwave.", \ - "You have fixed the microwave." \ - ) - src.icon_state = "mw" - src.broken = 0 // Fix it! - src.dirty = 0 // just to be sure - src.flags = OPENCONTAINER | NOREACT - else - to_chat(user, "It's broken!") - return 1 - - else if(src.dirty==100) // The microwave is all dirty so can't be used! - if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them - user.visible_message( \ - "\The [user] starts to clean the microwave.", \ - "You start to clean the microwave." \ - ) - if (do_after(user,20)) - user.visible_message( \ - "\The [user] has cleaned the microwave.", \ - "You have cleaned the microwave." \ - ) - src.dirty = 0 // It's clean! - src.broken = 0 // just to be sure - src.icon_state = "mw" - src.flags = OPENCONTAINER | NOREACT - SStgui.update_uis(src) - else //Otherwise bad luck!! - to_chat(user, "It's dirty!") - return 1 - else if(is_type_in_list(O,acceptable_items)) - var/list/workingList = cookingContents() - if(workingList.len>=(max_n_of_items + circuit_item_capacity)) //Adds component_parts to the maximum number of items. changed 1 to actually just be the circuit item capacity var. - to_chat(user, "This [src] is full of ingredients, you cannot put more.") - return 1 - if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it - var/obj/item/stack/S = O - new O.type (src) - S.use(1) - user.visible_message( \ - "\The [user] has added one of [O] to \the [src].", \ - "You add one of [O] to \the [src].") - return - else - // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete - Man whoever you are, it's been years. o7 - user.drop_from_inventory(O,src) - user.visible_message( \ - "\The [user] has added \the [O] to \the [src].", \ - "You add \the [O] to \the [src].") - SStgui.update_uis(src) - return - else if (istype(O,/obj/item/weapon/storage/bag/plants)) // There might be a better way about making plant bags dump their contents into a microwave, but it works. - var/obj/item/weapon/storage/bag/plants/bag = O - var/failed = 1 - for(var/obj/item/G in O.contents) - if(!G.reagents || !G.reagents.total_volume) - continue - failed = 0 - if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) - to_chat(user, "This [src] is full of ingredients, you cannot put more.") - return 0 - else - bag.remove_from_storage(G, src) - contents += G - if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) - break - - if(failed) - to_chat(user, "Nothing in the plant bag is usable.") - return 0 - - if(!O.contents.len) - to_chat(user, "You empty \the [O] into \the [src].") - else - to_chat(user, "You fill \the [src] from \the [O].") - - SStgui.update_uis(src) - return 0 - - else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ - istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \ - istype(O,/obj/item/weapon/reagent_containers/food/condiment) \ - ) - if (!O.reagents) - return 1 - for (var/datum/reagent/R in O.reagents.reagent_list) - if (!(R.id in acceptable_reagents)) - to_chat(user, "Your [O] contains components unsuitable for cookery.") - return 1 - return - else if(istype(O,/obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = O - to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].") - return 1 - else if(O.is_screwdriver()) - default_deconstruction_screwdriver(user, O) - return - else if(O.is_crowbar()) - if(default_deconstruction_crowbar(user, O)) - return - else - user.visible_message( \ - "\The [user] begins [src.anchored ? "unsecuring" : "securing"] the microwave.", \ - "You attempt to [src.anchored ? "unsecure" : "secure"] the microwave." - ) - if (do_after(user,20/O.toolspeed)) - user.visible_message( \ - "\The [user] [src.anchored ? "unsecures" : "secures"] the microwave.", \ - "You [src.anchored ? "unsecure" : "secure"] the microwave." - ) - src.anchored = !src.anchored - else - to_chat(user, "You decide not to do that.") - else if(default_part_replacement(user, O)) - return - else if(istype(O, /obj/item/device/paicard)) - if(!paicard) - insertpai(user, O) - else - to_chat(user, "You have no idea what you can cook with this [O].") - ..() - SStgui.update_uis(src) - -/obj/machinery/microwave/tgui_state(mob/user) - return GLOB.tgui_physical_state - -/obj/machinery/microwave/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/microwave/attack_hand(mob/user as mob) - if(user.a_intent == I_GRAB) - if(paicard) - ejectpai(user) - return - user.set_machine(src) - tgui_interact(user) - -/******************* -* Microwave Menu -********************/ -/obj/machinery/microwave/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Microwave", name) - ui.open() - -/obj/machinery/microwave/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - - data["broken"] = broken - data["operating"] = operating - data["dirty"] = dirty == 100 - data["items"] = get_items_list() - - return data - -/obj/machinery/microwave/proc/get_items_list() - var/list/data = list() - - var/list/items_counts = list() - var/list/items_measures = list() - var/list/items_measures_p = list() - //for(var/obj/O in ((contents - component_parts) - circuit)) - for(var/obj/O in cookingContents()) - var/display_name = O.name - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) - items_measures[display_name] = "egg" - items_measures_p[display_name] = "eggs" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) - items_measures[display_name] = "tofu chunk" - items_measures_p[display_name] = "tofu chunks" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat - items_measures[display_name] = "slab of meat" - items_measures_p[display_name] = "slabs of meat" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) - display_name = "Turnovers" - items_measures[display_name] = "turnover" - items_measures_p[display_name] = "turnovers" - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) - items_measures[display_name] = "fillet of meat" - items_measures_p[display_name] = "fillets of meat" - items_counts[display_name]++ - for(var/O in items_counts) - var/N = items_counts[O] - if(!(O in items_measures)) - data.Add(list(list( - "name" = capitalize(O), - "amt" = N, - "extra" = "[lowertext(O)][N > 1 ? "s" : ""]", - ))) - else - data.Add(list(list( - "name" = capitalize(O), - "amt" = N, - "extra" = N == 1 ? items_measures[O] : items_measures_p[O], - ))) - - for(var/datum/reagent/R in reagents.reagent_list) - var/display_name = R.name - if(R.id == "capsaicin") - display_name = "Hotsauce" - if(R.id == "frostoil") - display_name = "Coldsauce" - data.Add(list(list( - "name" = display_name, - "amt" = R.volume, - "extra" = "unit[R.volume > 1 ? "s" : ""]" - ))) - - return data - -/obj/machinery/microwave/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - if(operating) - return TRUE - - switch(action) - if("cook") - cook() - return TRUE - - if("dispose") - dispose() - return TRUE -/* -/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu - var/dat = "" - if(src.broken > 0) - dat = {"Bzzzzttttt"} - else if(src.operating) - dat = {"Microwaving in progress!
                    Please wait...!
                    "} - else if(src.dirty==100) - dat = {"This microwave is dirty!
                    Please clean it before use!
                    "} - else - var/list/items_counts = new - var/list/items_measures = new - var/list/items_measures_p = new - for (var/obj/O in ((contents - component_parts) - circuit)) - var/display_name = O.name - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) - items_measures[display_name] = "egg" - items_measures_p[display_name] = "eggs" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) - items_measures[display_name] = "tofu chunk" - items_measures_p[display_name] = "tofu chunks" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat - items_measures[display_name] = "slab of meat" - items_measures_p[display_name] = "slabs of meat" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) - display_name = "Turnovers" - items_measures[display_name] = "turnover" - items_measures_p[display_name] = "turnovers" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) - items_measures[display_name] = "fillet of meat" - items_measures_p[display_name] = "fillets of meat" - items_counts[display_name]++ - for (var/O in items_counts) - var/N = items_counts[O] - if (!(O in items_measures)) - dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
                    "} - else - if (N==1) - dat += {"[capitalize(O)]: [N] [items_measures[O]]
                    "} - else - dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
                    "} - - for (var/datum/reagent/R in reagents.reagent_list) - var/display_name = R.name - if (R.id == "capsaicin") - display_name = "Hotsauce" - if (R.id == "frostoil") - display_name = "Coldsauce" - dat += {"[display_name]: [R.volume] unit\s
                    "} - - if (items_counts.len==0 && reagents.reagent_list.len==0) - dat = {"The microwave is empty
                    "} - else - dat = {"Ingredients:
                    [dat]"} - dat += {"

                    \ -Turn on!
                    \ -
                    Eject ingredients!
                    \ -"} - - user << browse("Microwave Controls[dat]", "window=microwave") - onclose(user, "microwave") - return -*/ - -/*********************************** -* Microwave Menu Handling/Cooking -************************************/ - -/obj/machinery/microwave/proc/cook() - if(stat & (NOPOWER|BROKEN)) - return - start() - if(reagents.total_volume==0 && !(locate(/obj) in cookingContents())) //dry run - if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) - abort() - return - abort() - return - - var/datum/recipe/recipe = select_recipe(available_recipes,src) - var/obj/cooked - if(!recipe) - dirty += 1 - if(prob(max(10,dirty*5))) - if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) - abort() - return - muck_start() - wzhzhzh(2) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) - muck_finish() - cooked = fail() - cooked.forceMove(src.loc) - else if(has_extra_item()) - if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) - abort() - return - broke() - cooked = fail() - cooked.forceMove(src.loc) - else - if(!wzhzhzh(40)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) - abort() - return - stop() - cooked = fail() - cooked.forceMove(src.loc) - return - - //Making multiple copies of a recipe - var/halftime = round(recipe.time*4/10/2) // VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was round(recipe.time/20/2)) - if(!wzhzhzh(halftime)) - abort() - return - recipe.before_cook(src) - if(!wzhzhzh(halftime)) - abort() - cooked = fail() - cooked.forceMove(loc) - recipe.after_cook(src) - return - - var/result = recipe.result - var/valid = 1 - var/list/cooked_items = list() - var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes - while(valid) - var/list/things = list() - things.Add(recipe.make_food(src)) - cooked_items += things - //Move cooked things to the buffer so they're not considered as ingredients - for(var/atom/movable/AM in things) - AM.forceMove(temp) - - valid = 0 - recipe.after_cook(src) - recipe = select_recipe(available_recipes,src) - if(recipe && recipe.result == result) - valid = 1 - sleep(2) - - for(var/atom/movable/R as anything in cooked_items) - R.forceMove(src) //Move everything from the buffer back to the container - - QDEL_NULL(temp)//Delete buffer object - - //Any leftover reagents are divided amongst the foods - var/total = reagents.total_volume - for(var/obj/item/weapon/reagent_containers/food/snacks/S in cooked_items) - reagents.trans_to_holder(S.reagents, total/cooked_items.len) - - for(var/obj/item/weapon/reagent_containers/food/snacks/S in cookingContents()) - S.cook() - - dispose(0) //clear out anything left - stop() - - return - -/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z - for (var/i=1 to seconds) - if (stat & (NOPOWER|BROKEN)) - return 0 - use_power(active_power_usage) - sleep(5) //VOREStation Edit - Quicker Microwaves - return 1 - -/obj/machinery/microwave/proc/has_extra_item() //- coded to have different microwaves be able to handle different items - if(item_level == 0) - for (var/obj/O in cookingContents()) - if ( \ - !istype(O,/obj/item/weapon/reagent_containers/food) && \ - !istype(O, /obj/item/weapon/grown) \ - ) - return 1 - return 0 - if(item_level == 1) - for (var/obj/O in cookingContents()) - if ( \ - !istype(O, /obj/item/weapon/reagent_containers/food) && \ - !istype(O, /obj/item/weapon/grown) && \ - !istype(O, /obj/item/slime_extract) && \ - !istype(O, /obj/item/organ) && \ - !istype(O, /obj/item/stack/material) \ - ) - return 1 - return 0 - -/obj/machinery/microwave/proc/start() - src.visible_message("The microwave turns on.", "You hear a microwave.") - soundloop.start() - src.operating = TRUE - src.icon_state = "mw1" - SStgui.update_uis(src) - -/obj/machinery/microwave/proc/abort() - operating = FALSE // Turn it off again aferwards - if(icon_state == "mw1") - icon_state = "mw" - SStgui.update_uis(src) - soundloop.stop() - -/obj/machinery/microwave/proc/stop() - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - operating = FALSE // Turn it off again aferwards - if(icon_state == "mw1") - icon_state = "mw" - SStgui.update_uis(src) - soundloop.stop() - -/obj/machinery/microwave/proc/dispose(var/message = 1) - for (var/atom/movable/A in cookingContents()) - A.forceMove(loc) - if (src.reagents.total_volume) - src.dirty++ - src.reagents.clear_reagents() - if(message) - to_chat(usr, "You dispose of the microwave contents.") - SStgui.update_uis(src) - -/obj/machinery/microwave/proc/muck_start() - playsound(src, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound - src.icon_state = "mwbloody1" // Make it look dirty!! - -/obj/machinery/microwave/proc/muck_finish() - src.visible_message("The microwave gets covered in muck!") - src.dirty = 100 // Make it dirty so it can't be used util cleaned - src.flags = null //So you can't add condiments - src.icon_state = "mwbloody0" // Make it look dirty too - src.operating = 0 // Turn it off again aferwards - SStgui.update_uis(src) - soundloop.stop() - - -/obj/machinery/microwave/proc/broke() - var/datum/effect/effect/system/spark_spread/s = new - s.set_up(2, 1, src) - s.start() - src.icon_state = "mwb" // Make it look all busted up and shit - src.visible_message("The microwave breaks!") //Let them know they're stupid - src.broken = 2 // Make it broken so it can't be used util fixed - src.flags = null //So you can't add condiments - src.operating = 0 // Turn it off again aferwards - SStgui.update_uis(src) - soundloop.stop() - src.ejectpai() // If it broke, time to yeet the PAI. - -/obj/machinery/microwave/proc/fail() - var/obj/item/weapon/reagent_containers/food/snacks/badrecipe/ffuu = new(src) - var/amount = 0 - for (var/obj/O in cookingContents() - ffuu) - amount++ - if(O.reagents) - var/id = O.reagents.get_master_reagent_id() - if(id) - amount+=O.reagents.get_reagent_amount(id) - if(istype(O, /obj/item/weapon/holder)) - var/obj/item/weapon/holder/H = O - if(H.held_mob) - qdel(H.held_mob) - qdel(O) - src.reagents.clear_reagents() - ffuu.reagents.add_reagent("carbon", amount) - ffuu.reagents.add_reagent("toxin", amount/10) - return ffuu - -/obj/machinery/microwave/verb/Eject() - set src in oview(1) - set category = "Object" - set name = "Eject content" - usr.visible_message( - "[usr] tries to open [src] and remove its contents." , - "You try to open [src] and remove its contents." - ) - - if(!do_after(usr, 1 SECONDS, target = src)) - return - - if(operating) - to_chat(usr, "You can't do that, [src] door is locked!") - return - - usr.visible_message( - "[usr] opened [src] and has taken out [english_list(cookingContents())]." , - "You have opened [src] and taken out [english_list(cookingContents())]." - ) - dispose() - -/obj/machinery/microwave/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(!mover) - return 1 - if(mover.checkpass(PASSTABLE)) - //Animals can run under them, lots of empty space - return 1 - return ..() - -/obj/machinery/microwave/advanced // specifically for complex recipes - name = "deluxe microwave" - icon = 'icons/obj/deluxemicrowave.dmi' - icon_state = "mw" - circuit = /obj/item/weapon/circuitboard/microwave/advanced - circuit_item_capacity = 100 - item_level = 1 - -/obj/machinery/microwave/advanced/Initialize() - . = ..() - reagents.maximum_volume = 1000 - -/datum/recipe/splat // We use this to handle cooking micros (or mice, etc) in a microwave. Janky but it works better than snowflake code to handle the same thing. - items = list( - /obj/item/weapon/holder - ) - result = /obj/effect/decal/cleanable/blood/gibs - -/datum/recipe/splat/before_cook(obj/container) - if(istype(container, /obj/machinery/microwave)) - var/obj/machinery/microwave/M = container - M.muck_start() - playsound(container.loc, 'sound/items/drop/flesh.ogg', 100, 1) - . = ..() - -/datum/recipe/splat/make_food(obj/container) - for(var/obj/item/weapon/holder/H in container) - if(H.held_mob) - to_chat(H.held_mob, "You hear an earsplitting humming and your head aches!") - qdel(H.held_mob) - H.held_mob = null - qdel(H) - - . = ..() - -/datum/recipe/splat/after_cook(obj/container) - if(istype(container, /obj/machinery/microwave)) - var/obj/machinery/microwave/M = container - M.muck_finish() - . = ..() - -/obj/machinery/microwave/proc/cookingContents() //VOREEdit, this is a better way to deal with the contents of a microwave, since the previous method is stupid. - var/list/workingList = contents.Copy() // Using the copy proc because otherwise the two lists seem to become soul bonded. - workingList -= component_parts - workingList -= circuit - if(paicard) - workingList -= paicard - for(var/M in workingList) - if(istype(M, circuit)) // Yes, we remove circuit twice. Yes, it's necessary. Yes, it's stupid. - workingList -= M - return workingList \ No newline at end of file +/obj/machinery/microwave + name = "Microwave" + desc = "Studies are inconclusive on whether pressing your face against the glass is harmful." + icon = 'icons/obj/kitchen.dmi' + icon_state = "mw" + layer = 2.9 + density = TRUE + anchored = TRUE + unacidable = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 2000 + clicksound = "button" + clickvol = "30" + flags = OPENCONTAINER | NOREACT + circuit = /obj/item/weapon/circuitboard/microwave + var/operating = 0 // Is it on? + var/dirty = 0 // = {0..100} Does it need cleaning? + var/broken = 0 // ={0,1,2} How broken is it??? + var/circuit_item_capacity = 1 //how many items does the circuit add to max number of items + var/item_level = 0 // items microwave can handle, 0 foodstuff, 1 materials + var/global/list/acceptable_items // List of the items you can put in + var/global/list/available_recipes // List of the recipes you can use + var/global/list/acceptable_reagents // List of the reagents you can put in + + var/global/max_n_of_items = 20 + var/appliancetype = MICROWAVE + var/datum/looping_sound/microwave/soundloop + + +//see code/modules/food/recipes_microwave.dm for recipes + +/******************* +* Initialising +********************/ + +/obj/machinery/microwave/Initialize() + . = ..() + + reagents = new/datum/reagents(100) + reagents.my_atom = src + + default_apply_parts() + + if(!available_recipes) + available_recipes = new + for(var/datum/recipe/typepath as anything in subtypesof(/datum/recipe)) + if((initial(typepath.appliance) & appliancetype)) + available_recipes += new typepath + + acceptable_items = new + acceptable_reagents = new + for (var/datum/recipe/recipe in available_recipes) + for (var/item in recipe.items) + acceptable_items |= item + for (var/reagent in recipe.reagents) + acceptable_reagents |= reagent + // This will do until I can think of a fun recipe to use dionaea in - + // will also allow anything using the holder item to be microwaved into + // impure carbon. ~Z + acceptable_items |= /obj/item/weapon/holder + acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown + acceptable_items |= /obj/item/device/soulstone + acceptable_items |= /obj/item/weapon/fuel_assembly/supermatter + + soundloop = new(list(src), FALSE) + +/obj/machinery/microwave/Destroy() + if(paicard) + ejectpai() // Lets not delete the pAI. + QDEL_NULL(soundloop) + return ..() + +/******************* +* Item Adding +********************/ + +/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(src.broken > 0) + if(src.broken == 2 && O.has_tool_quality(TOOL_SCREWDRIVER)) // If it's broken and they're using a screwdriver + user.visible_message( \ + "\The [user] starts to fix part of the microwave.", \ + "You start to fix part of the microwave." \ + ) + playsound(src, O.usesound, 50, 1) + if (do_after(user,20 * O.toolspeed)) + user.visible_message( \ + "\The [user] fixes part of the microwave.", \ + "You have fixed part of the microwave." \ + ) + src.broken = 1 // Fix it a bit + else if(src.broken == 1 && O.has_tool_quality(TOOL_WRENCH)) // If it's broken and they're doing the wrench + user.visible_message( \ + "\The [user] starts to fix part of the microwave.", \ + "You start to fix part of the microwave." \ + ) + if (do_after(user,20 * O.toolspeed)) + user.visible_message( \ + "\The [user] fixes the microwave.", \ + "You have fixed the microwave." \ + ) + src.icon_state = "mw" + src.broken = 0 // Fix it! + src.dirty = 0 // just to be sure + src.flags = OPENCONTAINER | NOREACT + else + to_chat(user, "It's broken!") + return 1 + + else if(src.dirty==100) // The microwave is all dirty so can't be used! + if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them + user.visible_message( \ + "\The [user] starts to clean the microwave.", \ + "You start to clean the microwave." \ + ) + if (do_after(user,20)) + user.visible_message( \ + "\The [user] has cleaned the microwave.", \ + "You have cleaned the microwave." \ + ) + src.dirty = 0 // It's clean! + src.broken = 0 // just to be sure + src.icon_state = "mw" + src.flags = OPENCONTAINER | NOREACT + SStgui.update_uis(src) + else //Otherwise bad luck!! + to_chat(user, "It's dirty!") + return 1 + else if(is_type_in_list(O,acceptable_items)) + var/list/workingList = cookingContents() + if(workingList.len>=(max_n_of_items + circuit_item_capacity)) //Adds component_parts to the maximum number of items. changed 1 to actually just be the circuit item capacity var. + to_chat(user, "This [src] is full of ingredients, you cannot put more.") + return 1 + if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it + var/obj/item/stack/S = O + new O.type (src) + S.use(1) + user.visible_message( \ + "\The [user] has added one of [O] to \the [src].", \ + "You add one of [O] to \the [src].") + return + else + // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete - Man whoever you are, it's been years. o7 + user.drop_from_inventory(O,src) + user.visible_message( \ + "\The [user] has added \the [O] to \the [src].", \ + "You add \the [O] to \the [src].") + SStgui.update_uis(src) + return + else if (istype(O,/obj/item/weapon/storage/bag/plants)) // There might be a better way about making plant bags dump their contents into a microwave, but it works. + var/obj/item/weapon/storage/bag/plants/bag = O + var/failed = 1 + for(var/obj/item/G in O.contents) + if(!G.reagents || !G.reagents.total_volume) + continue + failed = 0 + if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) + to_chat(user, "This [src] is full of ingredients, you cannot put more.") + return 0 + else + bag.remove_from_storage(G, src) + contents += G + if(contents.len>=(max_n_of_items + component_parts.len + circuit_item_capacity)) + break + + if(failed) + to_chat(user, "Nothing in the plant bag is usable.") + return 0 + + if(!O.contents.len) + to_chat(user, "You empty \the [O] into \the [src].") + else + to_chat(user, "You fill \the [src] from \the [O].") + + SStgui.update_uis(src) + return 0 + + else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ + istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \ + istype(O,/obj/item/weapon/reagent_containers/food/condiment) \ + ) + if (!O.reagents) + return 1 + for (var/datum/reagent/R in O.reagents.reagent_list) + if (!(R.id in acceptable_reagents)) + to_chat(user, "Your [O] contains components unsuitable for cookery.") + return 1 + return + else if(istype(O,/obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = O + to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].") + return 1 + else if(O.has_tool_quality(TOOL_SCREWDRIVER)) + default_deconstruction_screwdriver(user, O) + return + else if(O.has_tool_quality(TOOL_CROWBAR)) + if(default_deconstruction_crowbar(user, O)) + return + else + user.visible_message( \ + "\The [user] begins [src.anchored ? "unsecuring" : "securing"] the microwave.", \ + "You attempt to [src.anchored ? "unsecure" : "secure"] the microwave." + ) + if (do_after(user,20/O.toolspeed)) + user.visible_message( \ + "\The [user] [src.anchored ? "unsecures" : "secures"] the microwave.", \ + "You [src.anchored ? "unsecure" : "secure"] the microwave." + ) + src.anchored = !src.anchored + else + to_chat(user, "You decide not to do that.") + else if(default_part_replacement(user, O)) + return + else if(istype(O, /obj/item/device/paicard)) + if(!paicard) + insertpai(user, O) + else + to_chat(user, "You have no idea what you can cook with this [O].") + ..() + SStgui.update_uis(src) + +/obj/machinery/microwave/tgui_state(mob/user) + return GLOB.tgui_physical_state + +/obj/machinery/microwave/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/microwave/attack_hand(mob/user as mob) + if(user.a_intent == I_GRAB) + if(paicard) + ejectpai(user) + return + user.set_machine(src) + tgui_interact(user) + +/******************* +* Microwave Menu +********************/ +/obj/machinery/microwave/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Microwave", name) + ui.open() + +/obj/machinery/microwave/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["broken"] = broken + data["operating"] = operating + data["dirty"] = dirty == 100 + data["items"] = get_items_list() + + return data + +/obj/machinery/microwave/proc/get_items_list() + var/list/data = list() + + var/list/items_counts = list() + var/list/items_measures = list() + var/list/items_measures_p = list() + //for(var/obj/O in ((contents - component_parts) - circuit)) + for(var/obj/O in cookingContents()) + var/display_name = O.name + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) + items_measures[display_name] = "egg" + items_measures_p[display_name] = "eggs" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) + items_measures[display_name] = "tofu chunk" + items_measures_p[display_name] = "tofu chunks" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat + items_measures[display_name] = "slab of meat" + items_measures_p[display_name] = "slabs of meat" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) + display_name = "Turnovers" + items_measures[display_name] = "turnover" + items_measures_p[display_name] = "turnovers" + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) + items_measures[display_name] = "fillet of meat" + items_measures_p[display_name] = "fillets of meat" + items_counts[display_name]++ + for(var/O in items_counts) + var/N = items_counts[O] + if(!(O in items_measures)) + data.Add(list(list( + "name" = capitalize(O), + "amt" = N, + "extra" = "[lowertext(O)][N > 1 ? "s" : ""]", + ))) + else + data.Add(list(list( + "name" = capitalize(O), + "amt" = N, + "extra" = N == 1 ? items_measures[O] : items_measures_p[O], + ))) + + for(var/datum/reagent/R in reagents.reagent_list) + var/display_name = R.name + if(R.id == "capsaicin") + display_name = "Hotsauce" + if(R.id == "frostoil") + display_name = "Coldsauce" + data.Add(list(list( + "name" = display_name, + "amt" = R.volume, + "extra" = "unit[R.volume > 1 ? "s" : ""]" + ))) + + return data + +/obj/machinery/microwave/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(operating) + return TRUE + + switch(action) + if("cook") + cook() + return TRUE + + if("dispose") + dispose() + return TRUE +/* +/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu + var/dat = "" + if(src.broken > 0) + dat = {"Bzzzzttttt"} + else if(src.operating) + dat = {"Microwaving in progress!
                    Please wait...!
                    "} + else if(src.dirty==100) + dat = {"This microwave is dirty!
                    Please clean it before use!
                    "} + else + var/list/items_counts = new + var/list/items_measures = new + var/list/items_measures_p = new + for (var/obj/O in ((contents - component_parts) - circuit)) + var/display_name = O.name + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) + items_measures[display_name] = "egg" + items_measures_p[display_name] = "eggs" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) + items_measures[display_name] = "tofu chunk" + items_measures_p[display_name] = "tofu chunks" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat + items_measures[display_name] = "slab of meat" + items_measures_p[display_name] = "slabs of meat" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) + display_name = "Turnovers" + items_measures[display_name] = "turnover" + items_measures_p[display_name] = "turnovers" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) + items_measures[display_name] = "fillet of meat" + items_measures_p[display_name] = "fillets of meat" + items_counts[display_name]++ + for (var/O in items_counts) + var/N = items_counts[O] + if (!(O in items_measures)) + dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
                    "} + else + if (N==1) + dat += {"[capitalize(O)]: [N] [items_measures[O]]
                    "} + else + dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
                    "} + + for (var/datum/reagent/R in reagents.reagent_list) + var/display_name = R.name + if (R.id == "capsaicin") + display_name = "Hotsauce" + if (R.id == "frostoil") + display_name = "Coldsauce" + dat += {"[display_name]: [R.volume] unit\s
                    "} + + if (items_counts.len==0 && reagents.reagent_list.len==0) + dat = {"The microwave is empty
                    "} + else + dat = {"Ingredients:
                    [dat]"} + dat += {"

                    \ +
                    Turn on!
                    \ +
                    Eject ingredients!
                    \ +"} + + user << browse("Microwave Controls[dat]", "window=microwave") + onclose(user, "microwave") + return +*/ + +/*********************************** +* Microwave Menu Handling/Cooking +************************************/ + +/obj/machinery/microwave/proc/cook() + if(stat & (NOPOWER|BROKEN)) + return + start() + if(reagents.total_volume==0 && !(locate(/obj) in cookingContents())) //dry run + if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) + abort() + return + abort() + return + + var/datum/recipe/recipe = select_recipe(available_recipes,src) + var/obj/cooked + if(!recipe) + dirty += 1 + if(prob(max(10,dirty*5))) + if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) + abort() + return + muck_start() + wzhzhzh(2) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) + muck_finish() + cooked = fail() + cooked.forceMove(src.loc) + else if(has_extra_item()) + if(!wzhzhzh(16)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 2) + abort() + return + broke() + cooked = fail() + cooked.forceMove(src.loc) + else + if(!wzhzhzh(40)) //VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was 5) + abort() + return + stop() + cooked = fail() + cooked.forceMove(src.loc) + return + + //Making multiple copies of a recipe + var/halftime = round(recipe.time*4/10/2) // VOREStation Edit - Quicker Microwaves (Undone during Auroraport, left note in case of reversion, was round(recipe.time/20/2)) + if(!wzhzhzh(halftime)) + abort() + return + recipe.before_cook(src) + if(!wzhzhzh(halftime)) + abort() + cooked = fail() + cooked.forceMove(loc) + recipe.after_cook(src) + return + + var/result = recipe.result + var/valid = 1 + var/list/cooked_items = list() + var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes + while(valid) + var/list/things = list() + things.Add(recipe.make_food(src)) + cooked_items += things + //Move cooked things to the buffer so they're not considered as ingredients + for(var/atom/movable/AM in things) + AM.forceMove(temp) + + valid = 0 + recipe.after_cook(src) + recipe = select_recipe(available_recipes,src) + if(recipe && recipe.result == result) + valid = 1 + sleep(2) + + for(var/atom/movable/R as anything in cooked_items) + R.forceMove(src) //Move everything from the buffer back to the container + + QDEL_NULL(temp)//Delete buffer object + + //Any leftover reagents are divided amongst the foods + var/total = reagents.total_volume + for(var/obj/item/weapon/reagent_containers/food/snacks/S in cooked_items) + reagents.trans_to_holder(S.reagents, total/cooked_items.len) + + for(var/obj/item/weapon/reagent_containers/food/snacks/S in cookingContents()) + S.cook() + + dispose(0) //clear out anything left + stop() + + return + +/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z + for (var/i=1 to seconds) + if (stat & (NOPOWER|BROKEN)) + return 0 + use_power(active_power_usage) + sleep(5) //VOREStation Edit - Quicker Microwaves + return 1 + +/obj/machinery/microwave/proc/has_extra_item() //- coded to have different microwaves be able to handle different items + if(item_level == 0) + for (var/obj/O in cookingContents()) + if ( \ + !istype(O,/obj/item/weapon/reagent_containers/food) && \ + !istype(O, /obj/item/weapon/grown) \ + ) + return 1 + return 0 + if(item_level == 1) + for (var/obj/O in cookingContents()) + if ( \ + !istype(O, /obj/item/weapon/reagent_containers/food) && \ + !istype(O, /obj/item/weapon/grown) && \ + !istype(O, /obj/item/slime_extract) && \ + !istype(O, /obj/item/organ) && \ + !istype(O, /obj/item/stack/material) \ + ) + return 1 + return 0 + +/obj/machinery/microwave/proc/start() + src.visible_message("The microwave turns on.", "You hear a microwave.") + soundloop.start() + src.operating = TRUE + src.icon_state = "mw1" + SStgui.update_uis(src) + +/obj/machinery/microwave/proc/abort() + operating = FALSE // Turn it off again aferwards + if(icon_state == "mw1") + icon_state = "mw" + SStgui.update_uis(src) + soundloop.stop() + +/obj/machinery/microwave/proc/stop() + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + operating = FALSE // Turn it off again aferwards + if(icon_state == "mw1") + icon_state = "mw" + SStgui.update_uis(src) + soundloop.stop() + +/obj/machinery/microwave/proc/dispose(var/message = 1) + for (var/atom/movable/A in cookingContents()) + A.forceMove(loc) + if (src.reagents.total_volume) + src.dirty++ + src.reagents.clear_reagents() + if(message) + to_chat(usr, "You dispose of the microwave contents.") + SStgui.update_uis(src) + +/obj/machinery/microwave/proc/muck_start() + playsound(src, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound + src.icon_state = "mwbloody1" // Make it look dirty!! + +/obj/machinery/microwave/proc/muck_finish() + src.visible_message("The microwave gets covered in muck!") + src.dirty = 100 // Make it dirty so it can't be used util cleaned + src.flags = null //So you can't add condiments + src.icon_state = "mwbloody0" // Make it look dirty too + src.operating = 0 // Turn it off again aferwards + SStgui.update_uis(src) + soundloop.stop() + + +/obj/machinery/microwave/proc/broke() + var/datum/effect/effect/system/spark_spread/s = new + s.set_up(2, 1, src) + s.start() + src.icon_state = "mwb" // Make it look all busted up and shit + src.visible_message("The microwave breaks!") //Let them know they're stupid + src.broken = 2 // Make it broken so it can't be used util fixed + src.flags = null //So you can't add condiments + src.operating = 0 // Turn it off again aferwards + SStgui.update_uis(src) + soundloop.stop() + src.ejectpai() // If it broke, time to yeet the PAI. + +/obj/machinery/microwave/proc/fail() + var/obj/item/weapon/reagent_containers/food/snacks/badrecipe/ffuu = new(src) + var/amount = 0 + for (var/obj/O in cookingContents() - ffuu) + amount++ + if(O.reagents) + var/id = O.reagents.get_master_reagent_id() + if(id) + amount+=O.reagents.get_reagent_amount(id) + if(istype(O, /obj/item/weapon/holder)) + var/obj/item/weapon/holder/H = O + if(H.held_mob) + qdel(H.held_mob) + qdel(O) + src.reagents.clear_reagents() + ffuu.reagents.add_reagent("carbon", amount) + ffuu.reagents.add_reagent("toxin", amount/10) + return ffuu + +/obj/machinery/microwave/verb/Eject() + set src in oview(1) + set category = "Object" + set name = "Eject content" + usr.visible_message( + "[usr] tries to open [src] and remove its contents." , + "You try to open [src] and remove its contents." + ) + + if(!do_after(usr, 1 SECONDS, target = src)) + return + + if(operating) + to_chat(usr, "You can't do that, [src] door is locked!") + return + + usr.visible_message( + "[usr] opened [src] and has taken out [english_list(cookingContents())]." , + "You have opened [src] and taken out [english_list(cookingContents())]." + ) + dispose() + +/obj/machinery/microwave/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(!mover) + return 1 + if(mover.checkpass(PASSTABLE)) + //Animals can run under them, lots of empty space + return 1 + return ..() + +/obj/machinery/microwave/advanced // specifically for complex recipes + name = "deluxe microwave" + icon = 'icons/obj/deluxemicrowave.dmi' + icon_state = "mw" + circuit = /obj/item/weapon/circuitboard/microwave/advanced + circuit_item_capacity = 100 + item_level = 1 + +/obj/machinery/microwave/advanced/Initialize() + . = ..() + reagents.maximum_volume = 1000 + +/datum/recipe/splat // We use this to handle cooking micros (or mice, etc) in a microwave. Janky but it works better than snowflake code to handle the same thing. + items = list( + /obj/item/weapon/holder + ) + result = /obj/effect/decal/cleanable/blood/gibs + +/datum/recipe/splat/before_cook(obj/container) + if(istype(container, /obj/machinery/microwave)) + var/obj/machinery/microwave/M = container + M.muck_start() + playsound(container.loc, 'sound/items/drop/flesh.ogg', 100, 1) + . = ..() + +/datum/recipe/splat/make_food(obj/container) + for(var/obj/item/weapon/holder/H in container) + if(H.held_mob) + to_chat(H.held_mob, "You hear an earsplitting humming and your head aches!") + qdel(H.held_mob) + H.held_mob = null + qdel(H) + + . = ..() + +/datum/recipe/splat/after_cook(obj/container) + if(istype(container, /obj/machinery/microwave)) + var/obj/machinery/microwave/M = container + M.muck_finish() + . = ..() + +/obj/machinery/microwave/proc/cookingContents() //VOREEdit, this is a better way to deal with the contents of a microwave, since the previous method is stupid. + var/list/workingList = contents.Copy() // Using the copy proc because otherwise the two lists seem to become soul bonded. + workingList -= component_parts + workingList -= circuit + if(paicard) + workingList -= paicard + for(var/M in workingList) + if(istype(M, circuit)) // Yes, we remove circuit twice. Yes, it's necessary. Yes, it's stupid. + workingList -= M + return workingList + diff --git a/code/modules/food/kitchen/smartfridge/smartfridge.dm b/code/modules/food/kitchen/smartfridge/smartfridge.dm index 9c7e4ba010e..90f0cff8ade 100644 --- a/code/modules/food/kitchen/smartfridge/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge/smartfridge.dm @@ -1,271 +1,271 @@ -/obj/machinery/smartfridge - name = "\improper SmartFridge" - desc = "For storing all sorts of things! This one doesn't accept any of them!" - icon = 'icons/obj/vending_smartfridge.dmi' - icon_state = "smartfridge" - var/icon_base = "smartfridge" //Iconstate to base all the broken/deny/etc on - var/icon_contents = "food" //Overlay to put on that show contents - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 5 - active_power_usage = 100 - flags = NOREACT - var/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. - var/list/item_records = list() - var/datum/stored_item/currently_vending = null //What we're putting out of the machine. - var/stored_datum_type = /datum/stored_item - var/seconds_electrified = 0; - var/shoot_inventory = 0 - var/locked = 0 - var/scan_id = 1 - var/is_secure = 0 - var/wrenchable = 0 - var/datum/wires/smartfridge/wires = null - var/persistent = null // Path of persistence datum used to track contents - -/obj/machinery/smartfridge/secure - is_secure = 1 - -/obj/machinery/smartfridge/Initialize() - . = ..() - if(persistent) - SSpersistence.track_value(src, persistent) - if(is_secure) - wires = new/datum/wires/smartfridge/secure(src) - else - wires = new/datum/wires/smartfridge(src) - update_icon() - -/obj/machinery/smartfridge/Destroy() - qdel(wires) - for(var/A in item_records) //Get rid of item records. - qdel(A) - wires = null - if(persistent) - SSpersistence.forget_value(src, persistent) - return ..() - -/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj) - return FALSE - -/obj/machinery/smartfridge/process() - if(stat & (BROKEN|NOPOWER)) - return - if(src.seconds_electrified > 0) - src.seconds_electrified-- - if(src.shoot_inventory && prob(2)) - src.throw_item() - -/obj/machinery/smartfridge/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/smartfridge/update_icon() - cut_overlays() - if(panel_open) - add_overlay("[icon_base]-panel") - - if(stat & (BROKEN)) - cut_overlays() - icon_state = "[icon_base]-broken" - - if(stat & (NOPOWER)) - icon_state = "[icon_base]-off" - switch(contents.len) - if(0) - add_overlay("[icon_base]-0-off") - if(1 to 3) - add_overlay("[icon_base]-[icon_contents]1-off") - if(3 to 6) - add_overlay("[icon_base]-[icon_contents]2-off") - if(6 to INFINITY) - add_overlay("[icon_base]-[icon_contents]3-off") - else - icon_state = icon_base - switch(contents.len) - if(0) - add_overlay("[icon_base]-0") - if(1 to 3) - add_overlay("[icon_base]-[icon_contents]1") - if(3 to 6) - add_overlay("[icon_base]-[icon_contents]2") - if(6 to INFINITY) - add_overlay("[icon_base]-[icon_contents]3") - -/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(O.is_screwdriver()) - panel_open = !panel_open - user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].", "You [panel_open ? "open" : "close"] the maintenance panel of \the [src].") - playsound(src, O.usesound, 50, 1) - update_icon() - return - - if(wrenchable && default_unfasten_wrench(user, O, 20)) - return - - if(istype(O, /obj/item/device/multitool) || O.is_wirecutter()) - if(panel_open) - attack_hand(user) - return - - if(stat & NOPOWER) - to_chat(user, "\The [src] is unpowered and useless.") - return - - if(accept_check(O)) - user.remove_from_mob(O) - stock(O) - user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - sortTim(item_records, GLOBAL_PROC_REF(cmp_stored_item_name)) - - else if(istype(O, /obj/item/weapon/storage/bag)) - var/obj/item/weapon/storage/bag/P = O - var/plants_loaded = 0 - for(var/obj/G in P.contents) - if(accept_check(G)) - P.remove_from_storage(G) //fixes ui bug - Pull Request 5515 - stock(G) - plants_loaded = 1 - if(plants_loaded) - user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].") - if(P.contents.len > 0) - to_chat(user, "Some items are refused.") - - else if(istype(O, /obj/item/weapon/gripper)) // Grippers. ~Mechoid. - var/obj/item/weapon/gripper/B = O //B, for Borg. - if(!B.wrapped) - to_chat(user, "\The [B] is not holding anything.") - return - else - var/B_held = B.wrapped - to_chat(user, "You use \the [B] to put \the [B_held] into \the [src].") - return - - else - to_chat(user, "\The [src] smartly refuses [O].") - return 1 - -/obj/machinery/smartfridge/secure/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) - emagged = 1 - locked = -1 - to_chat(user, "You short out the product lock on [src].") - return 1 - -/obj/machinery/smartfridge/proc/find_record(var/obj/item/O) - for(var/datum/stored_item/I as anything in item_records) - if((O.type == I.item_path) && (O.name == I.item_name)) - return I - return null - -/obj/machinery/smartfridge/proc/stock(obj/item/O) - var/datum/stored_item/I = find_record(O) - if(!istype(I)) - I = new stored_datum_type(src, O.type, O.name) - item_records.Add(I) - I.add_product(O) - SStgui.update_uis(src) - update_icon() - -/obj/machinery/smartfridge/proc/vend(datum/stored_item/I, var/count) - var/amount = I.get_amount() - // Sanity check, there are probably ways to press the button when it shouldn't be possible. - if(amount <= 0) - return - - for(var/i = 1 to min(amount, count)) - I.get_product(get_turf(src)) - SStgui.update_uis(src) - update_icon() - -/obj/machinery/smartfridge/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/smartfridge/attack_hand(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - wires.Interact(user) - tgui_interact(user) - -/obj/machinery/smartfridge/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SmartVend", name) - ui.set_autoupdate(FALSE) - ui.open() - -/obj/machinery/smartfridge/tgui_data(mob/user) - . = list() - - var/list/items = list() - for(var/i=1 to length(item_records)) - var/datum/stored_item/I = item_records[i] - var/count = I.get_amount() - if(count > 0) - items.Add(list(list("name" = html_encode(capitalize(I.item_name)), "index" = i, "amount" = count))) - - .["contents"] = items - .["name"] = name - .["locked"] = locked - .["secure"] = is_secure - -/obj/machinery/smartfridge/tgui_act(action, params) - if(..()) - return TRUE - - add_fingerprint(usr) - switch(action) - if("Release") - var/amount = 0 - if(params["amount"]) - amount = params["amount"] - else - amount = tgui_input_number(usr, "How many items?", "How many items would you like to take out?", 1) - - if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src)) - return FALSE - - var/index = text2num(params["index"]) - if(index < 1 || index > LAZYLEN(item_records)) - return TRUE - - vend(item_records[index], amount) - update_icon() - return TRUE - return FALSE - -/obj/machinery/smartfridge/proc/throw_item() - var/obj/throw_item = null - var/mob/living/target = locate() in view(7,src) - if(!target) - return FALSE - - for(var/datum/stored_item/I in item_records) - throw_item = I.get_product(get_turf(src)) - if (!throw_item) - continue - break - - if(!throw_item) - return FALSE - spawn(0) - throw_item.throw_at(target,16,3,src) - src.visible_message("[src] launches [throw_item.name] at [target.name]!") - SStgui.update_uis(src) - update_icon() - return TRUE - -/* - * Secure Smartfridges - */ -/obj/machinery/smartfridge/secure/tgui_act(action, params) - if(stat & (NOPOWER|BROKEN)) - return TRUE - if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) - if((!allowed(usr) && scan_id) && !emagged && locked != -1 && action == "Release") - to_chat(usr, "Access denied.") - return TRUE - return ..() +/obj/machinery/smartfridge + name = "\improper SmartFridge" + desc = "For storing all sorts of things! This one doesn't accept any of them!" + icon = 'icons/obj/vending_smartfridge.dmi' + icon_state = "smartfridge" + var/icon_base = "smartfridge" //Iconstate to base all the broken/deny/etc on + var/icon_contents = "food" //Overlay to put on that show contents + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 5 + active_power_usage = 100 + flags = NOREACT + var/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. + var/list/item_records = list() + var/datum/stored_item/currently_vending = null //What we're putting out of the machine. + var/stored_datum_type = /datum/stored_item + var/seconds_electrified = 0; + var/shoot_inventory = 0 + var/locked = 0 + var/scan_id = 1 + var/is_secure = 0 + var/wrenchable = 0 + var/datum/wires/smartfridge/wires = null + var/persistent = null // Path of persistence datum used to track contents + +/obj/machinery/smartfridge/secure + is_secure = 1 + +/obj/machinery/smartfridge/Initialize() + . = ..() + if(persistent) + SSpersistence.track_value(src, persistent) + if(is_secure) + wires = new/datum/wires/smartfridge/secure(src) + else + wires = new/datum/wires/smartfridge(src) + update_icon() + +/obj/machinery/smartfridge/Destroy() + qdel(wires) + for(var/A in item_records) //Get rid of item records. + qdel(A) + wires = null + if(persistent) + SSpersistence.forget_value(src, persistent) + return ..() + +/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj) + return FALSE + +/obj/machinery/smartfridge/process() + if(stat & (BROKEN|NOPOWER)) + return + if(src.seconds_electrified > 0) + src.seconds_electrified-- + if(src.shoot_inventory && prob(2)) + src.throw_item() + +/obj/machinery/smartfridge/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/smartfridge/update_icon() + cut_overlays() + if(panel_open) + add_overlay("[icon_base]-panel") + + if(stat & (BROKEN)) + cut_overlays() + icon_state = "[icon_base]-broken" + + if(stat & (NOPOWER)) + icon_state = "[icon_base]-off" + switch(contents.len) + if(0) + add_overlay("[icon_base]-0-off") + if(1 to 3) + add_overlay("[icon_base]-[icon_contents]1-off") + if(3 to 6) + add_overlay("[icon_base]-[icon_contents]2-off") + if(6 to INFINITY) + add_overlay("[icon_base]-[icon_contents]3-off") + else + icon_state = icon_base + switch(contents.len) + if(0) + add_overlay("[icon_base]-0") + if(1 to 3) + add_overlay("[icon_base]-[icon_contents]1") + if(3 to 6) + add_overlay("[icon_base]-[icon_contents]2") + if(6 to INFINITY) + add_overlay("[icon_base]-[icon_contents]3") + +/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(O.has_tool_quality(TOOL_SCREWDRIVER)) + panel_open = !panel_open + user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].", "You [panel_open ? "open" : "close"] the maintenance panel of \the [src].") + playsound(src, O.usesound, 50, 1) + update_icon() + return + + if(wrenchable && default_unfasten_wrench(user, O, 20)) + return + + if(istype(O, /obj/item/device/multitool) || O.has_tool_quality(TOOL_WIRECUTTER)) + if(panel_open) + attack_hand(user) + return + + if(stat & NOPOWER) + to_chat(user, "\The [src] is unpowered and useless.") + return + + if(accept_check(O)) + user.remove_from_mob(O) + stock(O) + user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") + sortTim(item_records, GLOBAL_PROC_REF(cmp_stored_item_name)) + + else if(istype(O, /obj/item/weapon/storage/bag)) + var/obj/item/weapon/storage/bag/P = O + var/plants_loaded = 0 + for(var/obj/G in P.contents) + if(accept_check(G)) + P.remove_from_storage(G) //fixes ui bug - Pull Request 5515 + stock(G) + plants_loaded = 1 + if(plants_loaded) + user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].") + if(P.contents.len > 0) + to_chat(user, "Some items are refused.") + + else if(istype(O, /obj/item/weapon/gripper)) // Grippers. ~Mechoid. + var/obj/item/weapon/gripper/B = O //B, for Borg. + if(!B.wrapped) + to_chat(user, "\The [B] is not holding anything.") + return + else + var/B_held = B.wrapped + to_chat(user, "You use \the [B] to put \the [B_held] into \the [src].") + return + + else + to_chat(user, "\The [src] smartly refuses [O].") + return 1 + +/obj/machinery/smartfridge/secure/emag_act(var/remaining_charges, var/mob/user) + if(!emagged) + emagged = 1 + locked = -1 + to_chat(user, "You short out the product lock on [src].") + return 1 + +/obj/machinery/smartfridge/proc/find_record(var/obj/item/O) + for(var/datum/stored_item/I as anything in item_records) + if((O.type == I.item_path) && (O.name == I.item_name)) + return I + return null + +/obj/machinery/smartfridge/proc/stock(obj/item/O) + var/datum/stored_item/I = find_record(O) + if(!istype(I)) + I = new stored_datum_type(src, O.type, O.name) + item_records.Add(I) + I.add_product(O) + SStgui.update_uis(src) + update_icon() + +/obj/machinery/smartfridge/proc/vend(datum/stored_item/I, var/count) + var/amount = I.get_amount() + // Sanity check, there are probably ways to press the button when it shouldn't be possible. + if(amount <= 0) + return + + for(var/i = 1 to min(amount, count)) + I.get_product(get_turf(src)) + SStgui.update_uis(src) + update_icon() + +/obj/machinery/smartfridge/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/smartfridge/attack_hand(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + return + wires.Interact(user) + tgui_interact(user) + +/obj/machinery/smartfridge/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SmartVend", name) + ui.set_autoupdate(FALSE) + ui.open() + +/obj/machinery/smartfridge/tgui_data(mob/user) + . = list() + + var/list/items = list() + for(var/i=1 to length(item_records)) + var/datum/stored_item/I = item_records[i] + var/count = I.get_amount() + if(count > 0) + items.Add(list(list("name" = html_encode(capitalize(I.item_name)), "index" = i, "amount" = count))) + + .["contents"] = items + .["name"] = name + .["locked"] = locked + .["secure"] = is_secure + +/obj/machinery/smartfridge/tgui_act(action, params) + if(..()) + return TRUE + + add_fingerprint(usr) + switch(action) + if("Release") + var/amount = 0 + if(params["amount"]) + amount = params["amount"] + else + amount = tgui_input_number(usr, "How many items?", "How many items would you like to take out?", 1) + + if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src)) + return FALSE + + var/index = text2num(params["index"]) + if(index < 1 || index > LAZYLEN(item_records)) + return TRUE + + vend(item_records[index], amount) + update_icon() + return TRUE + return FALSE + +/obj/machinery/smartfridge/proc/throw_item() + var/obj/throw_item = null + var/mob/living/target = locate() in view(7,src) + if(!target) + return FALSE + + for(var/datum/stored_item/I in item_records) + throw_item = I.get_product(get_turf(src)) + if (!throw_item) + continue + break + + if(!throw_item) + return FALSE + spawn(0) + throw_item.throw_at(target,16,3,src) + src.visible_message("[src] launches [throw_item.name] at [target.name]!") + SStgui.update_uis(src) + update_icon() + return TRUE + +/* + * Secure Smartfridges + */ +/obj/machinery/smartfridge/secure/tgui_act(action, params) + if(stat & (NOPOWER|BROKEN)) + return TRUE + if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) + if((!allowed(usr) && scan_id) && !emagged && locked != -1 && action == "Release") + to_chat(usr, "Access denied.") + return TRUE + return ..() diff --git a/code/modules/food/recipe.dm b/code/modules/food/recipe.dm index 8e553bf532d..c967687e1f5 100644 --- a/code/modules/food/recipe.dm +++ b/code/modules/food/recipe.dm @@ -1,334 +1,334 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * - * /datum/recipe by rastaf0 13 apr 2011 * - * * * * * * * * * * * * * * * * * * * * * * * * * * - * This is powerful and flexible recipe system. - * It exists not only for food. - * supports both reagents and objects as prerequisites. - * In order to use this system you have to define a deriative from /datum/recipe - * * reagents are reagents. Acid, milc, booze, etc. - * * items are objects. Fruits, tools, circuit boards. - * * result is type to create as new object - * * time is optional parameter, you shall use in in your machine, - default /datum/recipe/ procs does not rely on this parameter. - * - * Functions you need: - * /datum/recipe/proc/make(var/obj/container as obj) - * Creates result inside container, - * deletes prerequisite reagents, - * transfers reagents from prerequisite objects, - * deletes all prerequisite objects (even not needed for recipe at the moment). - * - * /proc/select_recipe(list/datum/recipe/available_recipes, obj/obj as obj, exact = 1) - * Wonderful function that select suitable recipe for you. - * obj is a machine (or magik hat) with prerequisites, - * exact = 0 forces algorithm to ignore superfluous stuff. - * - * - * Functions you do not need to call directly but could: - * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) - * /datum/recipe/proc/check_items(var/obj/container as obj) - * - * */ - -// Recipe type defines. Used to determine what machine makes them. -#define MICROWAVE 0x1 -#define FRYER 0x2 -#define OVEN 0x4 -#define GRILL 0x8 -#define CANDYMAKER 0x10 -#define CEREALMAKER 0x20 - -/datum/recipe - var/list/reagents // Example: = list("berryjuice" = 5) // do not list same reagent twice - var/list/items // Example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) // place /foo/bar before /foo - var/list/fruit // Example: = list("fruit" = 3) - var/coating = null // Required coating on all items in the recipe. The default value of null explitly requires no coating - // A value of -1 is permissive and cares not for any coatings - // Any typepath indicates a specific coating that should be present - // Coatings are used for batter, breadcrumbs, beer-batter, colonel's secret coating, etc - - var/result // Example: = /obj/item/weapon/reagent_containers/food/snacks/donut/normal - var/result_quantity = 1 // Number of instances of result that are created. - var/time = 100 // 1/10 part of second - - #define RECIPE_REAGENT_REPLACE 0 //Reagents in the ingredients are discarded. - //Only the reagents present in the result at compiletime are used - #define RECIPE_REAGENT_MAX 1 //The result will contain the maximum of each reagent present between the two pools. Compiletime result, and sum of ingredients - #define RECIPE_REAGENT_MIN 2 //As above, but the minimum, ignoring zero values. - #define RECIPE_REAGENT_SUM 3 //The entire quantity of the ingredients are added to the result - - var/reagent_mix = RECIPE_REAGENT_MAX //How to handle reagent differences between the ingredients and the results - - var/appliance = MICROWAVE // Which apppliances this recipe can be made in. New Recipes will DEFAULT to using the Microwave, as a catch-all (and just in case) - // List of defines is in _defines/misc.dm. But for reference they are: - /* - MICROWAVE - FRYER - OVEN - CANDYMAKER - CEREALMAKER - */ - // This is a bitfield, more than one type can be used - // Grill is presently unused and not listed - -/datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents, var/exact = FALSE) - if(!reagents || !reagents.len) - return TRUE - - if(!avail_reagents) - return FALSE - - . = TRUE - for(var/r_r in reagents) - var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) - if(aval_r_amnt - reagents[r_r] >= 0) - if(aval_r_amnt>(reagents[r_r]) && exact) - . = FALSE - else - return FALSE - - if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) - return FALSE - return . - -/datum/recipe/proc/check_fruit(var/obj/container, var/exact = FALSE) - if (!fruit || !fruit.len) - return TRUE - - . = TRUE - if(fruit && fruit.len) - var/list/checklist = list() - // You should trust Copy(). - checklist = fruit.Copy() - for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) - if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) - continue - if(check_coating(G)) - checklist[G.seed.kitchen_tag]-- - for(var/ktag in checklist) - if(!isnull(checklist[ktag])) - if(checklist[ktag] < 0 && exact) - . = FALSE - else if(checklist[ktag] > 0) - . = FALSE - break - return . - -/datum/recipe/proc/check_items(var/obj/container as obj, var/exact = FALSE) - if(!items || !items.len) - return TRUE - - . = TRUE - if(items && items.len) - var/list/checklist = list() - checklist = items.Copy() // You should really trust Copy - if(istype(container, /obj/machinery)) - var/obj/machinery/machine = container - for(var/obj/O in ((machine.contents - machine.component_parts) - machine.circuit)) - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) - continue // Fruit is handled in check_fruit(). - var/found = FALSE - for(var/i = 1; i < checklist.len+1; i++) - var/item_type = checklist[i] - if (istype(O,item_type)) - checklist.Cut(i, i+1) - found = TRUE - break - if(!found && exact) - return FALSE - else - for(var/obj/O in container.contents) - if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) - continue // Fruit is handled in check_fruit(). - var/found = FALSE - for(var/i = 1; i < checklist.len+1; i++) - var/item_type = checklist[i] - if (istype(O,item_type)) - if(check_coating(O)) - checklist.Cut(i, i+1) - found = TRUE - break - if (!found && exact) - return FALSE - if(checklist.len) - return FALSE - return . - -//This is called on individual items within the container. -/datum/recipe/proc/check_coating(var/obj/O, var/exact = FALSE) - if(!istype(O,/obj/item/weapon/reagent_containers/food/snacks)) - return TRUE //Only snacks can be battered - - if (coating == -1) - return TRUE //-1 value doesnt care - - var/obj/item/weapon/reagent_containers/food/snacks/S = O - if (!S.coating) - if (!coating) - return TRUE - return FALSE - else if (S.coating.type == coating) - return TRUE - - return FALSE - -//general version -/datum/recipe/proc/make(var/obj/container as obj) - var/obj/result_obj = new result(container) - if(istype(container, /obj/machinery)) - var/obj/machinery/machine = container - for (var/obj/O in ((machine.contents-result_obj - machine.component_parts) - machine.circuit)) - O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) - qdel(O) - else - for (var/obj/O in (container.contents-result_obj)) - O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -// food-related -// This proc is called under the assumption that the container has already been checked and found to contain the necessary ingredients -/datum/recipe/proc/make_food(var/obj/container as obj) - if(!result) - log_runtime(EXCEPTION("Recipe [type] is defined without a result, please bug report this.")) - if(istype(container, /obj/machinery/microwave)) - var/obj/machinery/microwave/M = container - M.dispose(FALSE) - - else if(istype(container, /obj/item/weapon/reagent_containers/cooking_container)) - var/obj/item/weapon/reagent_containers/cooking_container/CC = container - CC.clear() - - container.visible_message(SPAN_WARNING("[container] inexplicably spills, and its contents are lost!")) - - return - - -//We will subtract all the ingredients from the container, and transfer their reagents into a holder -//We will not touch things which are not required for this recipe. They will be left behind for the caller -//to decide what to do. They may be used again to make another recipe or discarded, or merged into the results, -//thats no longer the concern of this proc - var/datum/reagents/buffer = new /datum/reagents(10000000000, null)// - - - //Find items we need - if (items && items.len) - for (var/i in items) - var/obj/item/I = locate(i) in container - if (I && I.reagents) - I.reagents.trans_to_holder(buffer,I.reagents.total_volume) - qdel(I) - - //Find fruits - if (fruit && fruit.len) - var/list/checklist = list() - checklist = fruit.Copy() - - for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) - if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) - continue - - if (checklist[G.seed.kitchen_tag] > 0) - //We found a thing we need - checklist[G.seed.kitchen_tag]-- - if (G && G.reagents) - G.reagents.trans_to_holder(buffer,G.reagents.total_volume) - qdel(G) - - //And lastly deduct necessary quantities of reagents - if (reagents && reagents.len) - for (var/r in reagents) - //Doesnt matter whether or not there's enough, we assume that check is done before - container.reagents.trans_type_to(buffer, r, reagents[r]) - - /* - Now we've removed all the ingredients that were used and we have the buffer containing the total of - all their reagents. - Next up we create the result, and then handle the merging of reagents depending on the mix setting - */ - var/tally = 0 - - /* - If we have multiple results, holder will be used as a buffer to hold reagents for the result objects. - If, as in the most common case, there is only a single result, then it will just be a reference to - the single-result's reagents - */ - var/datum/reagents/holder = new/datum/reagents(10000000000) - var/list/results = list() - while (tally < result_quantity) - var/obj/result_obj = new result(container) - results.Add(result_obj) - - if (!result_obj.reagents)//This shouldn't happen - //If the result somehow has no reagents defined, then create a new holder - result_obj.reagents = new /datum/reagents(buffer.total_volume*1.5, result_obj) - - if (result_quantity == 1) - qdel(holder) - holder = result_obj.reagents - else - result_obj.reagents.trans_to(holder, result_obj.reagents.total_volume) - tally++ - - - switch(reagent_mix) - if (RECIPE_REAGENT_REPLACE) - //We do no transferring - if (RECIPE_REAGENT_SUM) - //Sum is easy, just shove the entire buffer into the result - buffer.trans_to_holder(holder, buffer.total_volume) - if (RECIPE_REAGENT_MAX) - //We want the highest of each. - //Iterate through everything in buffer. If the target has less than the buffer, then top it up - for (var/datum/reagent/R in buffer.reagent_list) - var/rvol = holder.get_reagent_amount(R.id) - if (rvol < R.volume) - //Transfer the difference - buffer.trans_type_to(holder, R.id, R.volume-rvol) - - if (RECIPE_REAGENT_MIN) - //Min is slightly more complex. We want the result to have the lowest from each side - //But zero will not count. Where a side has zero its ignored and the side with a nonzero value is used - for (var/datum/reagent/R in buffer.reagent_list) - var/rvol = holder.get_reagent_amount(R.id) - if (rvol == 0) //If the target has zero of this reagent - buffer.trans_type_to(holder, R.id, R.volume) - //Then transfer all of ours - - else if (rvol > R.volume) - //if the target has more than ours - //Remove the difference - holder.remove_reagent(R.id, rvol-R.volume) - - - if (results.len > 1) - //If we're here, then holder is a buffer containing the total reagents for all the results. - //So now we redistribute it among them - var/total = holder.total_volume - for (var/i in results) - var/atom/a = i //optimisation - holder.trans_to(a, total / results.len) - - return results - -// When exact is false, extraneous ingredients are ignored -// When exact is true, extraneous ingredients will fail the recipe -// In both cases, the full set of required ingredients is still needed -/proc/select_recipe(var/list/datum/recipe/available_recipes, var/obj/obj as obj, var/exact) - var/highest_count = 0 - var/count = 0 - for (var/datum/recipe/recipe in available_recipes) - if(!recipe.check_reagents(obj.reagents, exact) || !recipe.check_items(obj, exact) || !recipe.check_fruit(obj, exact)) - continue - // Taken from cmp_recipe_complexity_dsc, but is way faster. - count = LAZYLEN(recipe.items) + LAZYLEN(recipe.reagents) + LAZYLEN(recipe.fruit) - if(count >= highest_count) - highest_count = count - . = recipe - -// Both of these are just placeholders to allow special behavior for mob holders, but you can do other things in here later if you feel like it. -/datum/recipe/proc/before_cook(obj/container) // Called Before the Microwave starts delays and cooking stuff - - -/datum/recipe/proc/after_cook(obj/container) // Called When the Microwave is finished. +/* * * * * * * * * * * * * * * * * * * * * * * * * * + * /datum/recipe by rastaf0 13 apr 2011 * + * * * * * * * * * * * * * * * * * * * * * * * * * * + * This is powerful and flexible recipe system. + * It exists not only for food. + * supports both reagents and objects as prerequisites. + * In order to use this system you have to define a deriative from /datum/recipe + * * reagents are reagents. Acid, milc, booze, etc. + * * items are objects. Fruits, tools, circuit boards. + * * result is type to create as new object + * * time is optional parameter, you shall use in in your machine, + default /datum/recipe/ procs does not rely on this parameter. + * + * Functions you need: + * /datum/recipe/proc/make(var/obj/container as obj) + * Creates result inside container, + * deletes prerequisite reagents, + * transfers reagents from prerequisite objects, + * deletes all prerequisite objects (even not needed for recipe at the moment). + * + * /proc/select_recipe(list/datum/recipe/available_recipes, obj/obj as obj, exact = 1) + * Wonderful function that select suitable recipe for you. + * obj is a machine (or magik hat) with prerequisites, + * exact = 0 forces algorithm to ignore superfluous stuff. + * + * + * Functions you do not need to call directly but could: + * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) + * /datum/recipe/proc/check_items(var/obj/container as obj) + * + * */ + +// Recipe type defines. Used to determine what machine makes them. +#define MICROWAVE 0x1 +#define FRYER 0x2 +#define OVEN 0x4 +#define GRILL 0x8 +#define CANDYMAKER 0x10 +#define CEREALMAKER 0x20 + +/datum/recipe + var/list/reagents // Example: = list("berryjuice" = 5) // do not list same reagent twice + var/list/items // Example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) // place /foo/bar before /foo + var/list/fruit // Example: = list("fruit" = 3) + var/coating = null // Required coating on all items in the recipe. The default value of null explitly requires no coating + // A value of -1 is permissive and cares not for any coatings + // Any typepath indicates a specific coating that should be present + // Coatings are used for batter, breadcrumbs, beer-batter, colonel's secret coating, etc + + var/result // Example: = /obj/item/weapon/reagent_containers/food/snacks/donut/normal + var/result_quantity = 1 // Number of instances of result that are created. + var/time = 100 // 1/10 part of second + + #define RECIPE_REAGENT_REPLACE 0 //Reagents in the ingredients are discarded. + //Only the reagents present in the result at compiletime are used + #define RECIPE_REAGENT_MAX 1 //The result will contain the maximum of each reagent present between the two pools. Compiletime result, and sum of ingredients + #define RECIPE_REAGENT_MIN 2 //As above, but the minimum, ignoring zero values. + #define RECIPE_REAGENT_SUM 3 //The entire quantity of the ingredients are added to the result + + var/reagent_mix = RECIPE_REAGENT_MAX //How to handle reagent differences between the ingredients and the results + + var/appliance = MICROWAVE // Which apppliances this recipe can be made in. New Recipes will DEFAULT to using the Microwave, as a catch-all (and just in case) + // List of defines is in _defines/misc.dm. But for reference they are: + /* + MICROWAVE + FRYER + OVEN + CANDYMAKER + CEREALMAKER + */ + // This is a bitfield, more than one type can be used + // Grill is presently unused and not listed + +/datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents, var/exact = FALSE) + if(!reagents || !reagents.len) + return TRUE + + if(!avail_reagents) + return FALSE + + . = TRUE + for(var/r_r in reagents) + var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) + if(aval_r_amnt - reagents[r_r] >= 0) + if(aval_r_amnt>(reagents[r_r]) && exact) + . = FALSE + else + return FALSE + + if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + return FALSE + return . + +/datum/recipe/proc/check_fruit(var/obj/container, var/exact = FALSE) + if (!fruit || !fruit.len) + return TRUE + + . = TRUE + if(fruit && fruit.len) + var/list/checklist = list() + // You should trust Copy(). + checklist = fruit.Copy() + for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) + if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) + continue + if(check_coating(G)) + checklist[G.seed.kitchen_tag]-- + for(var/ktag in checklist) + if(!isnull(checklist[ktag])) + if(checklist[ktag] < 0 && exact) + . = FALSE + else if(checklist[ktag] > 0) + . = FALSE + break + return . + +/datum/recipe/proc/check_items(var/obj/container as obj, var/exact = FALSE) + if(!items || !items.len) + return TRUE + + . = TRUE + if(items && items.len) + var/list/checklist = list() + checklist = items.Copy() // You should really trust Copy + if(istype(container, /obj/machinery)) + var/obj/machinery/machine = container + for(var/obj/O in ((machine.contents - machine.component_parts) - machine.circuit)) + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) + continue // Fruit is handled in check_fruit(). + var/found = FALSE + for(var/i = 1; i < checklist.len+1; i++) + var/item_type = checklist[i] + if (istype(O,item_type)) + checklist.Cut(i, i+1) + found = TRUE + break + if(!found && exact) + return FALSE + else + for(var/obj/O in container.contents) + if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown)) + continue // Fruit is handled in check_fruit(). + var/found = FALSE + for(var/i = 1; i < checklist.len+1; i++) + var/item_type = checklist[i] + if (istype(O,item_type)) + if(check_coating(O)) + checklist.Cut(i, i+1) + found = TRUE + break + if (!found && exact) + return FALSE + if(checklist.len) + return FALSE + return . + +//This is called on individual items within the container. +/datum/recipe/proc/check_coating(var/obj/O, var/exact = FALSE) + if(!istype(O,/obj/item/weapon/reagent_containers/food/snacks)) + return TRUE //Only snacks can be battered + + if (coating == -1) + return TRUE //-1 value doesnt care + + var/obj/item/weapon/reagent_containers/food/snacks/S = O + if (!S.coating) + if (!coating) + return TRUE + return FALSE + else if (S.coating.type == coating) + return TRUE + + return FALSE + +//general version +/datum/recipe/proc/make(var/obj/container as obj) + var/obj/result_obj = new result(container) + if(istype(container, /obj/machinery)) + var/obj/machinery/machine = container + for (var/obj/O in ((machine.contents-result_obj - machine.component_parts) - machine.circuit)) + O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) + qdel(O) + else + for (var/obj/O in (container.contents-result_obj)) + O.reagents.trans_to_obj(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +// food-related +// This proc is called under the assumption that the container has already been checked and found to contain the necessary ingredients +/datum/recipe/proc/make_food(var/obj/container as obj) + if(!result) + log_runtime(EXCEPTION("Recipe [type] is defined without a result, please bug report this.")) + if(istype(container, /obj/machinery/microwave)) + var/obj/machinery/microwave/M = container + M.dispose(FALSE) + + else if(istype(container, /obj/item/weapon/reagent_containers/cooking_container)) + var/obj/item/weapon/reagent_containers/cooking_container/CC = container + CC.clear() + + container.visible_message(SPAN_WARNING("[container] inexplicably spills, and its contents are lost!")) + + return + + +//We will subtract all the ingredients from the container, and transfer their reagents into a holder +//We will not touch things which are not required for this recipe. They will be left behind for the caller +//to decide what to do. They may be used again to make another recipe or discarded, or merged into the results, +//thats no longer the concern of this proc + var/datum/reagents/buffer = new /datum/reagents(10000000000, null)// + + + //Find items we need + if (items && items.len) + for (var/i in items) + var/obj/item/I = locate(i) in container + if (I && I.reagents) + I.reagents.trans_to_holder(buffer,I.reagents.total_volume) + qdel(I) + + //Find fruits + if (fruit && fruit.len) + var/list/checklist = list() + checklist = fruit.Copy() + + for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in container) + if(!G.seed || !G.seed.kitchen_tag || isnull(checklist[G.seed.kitchen_tag])) + continue + + if (checklist[G.seed.kitchen_tag] > 0) + //We found a thing we need + checklist[G.seed.kitchen_tag]-- + if (G && G.reagents) + G.reagents.trans_to_holder(buffer,G.reagents.total_volume) + qdel(G) + + //And lastly deduct necessary quantities of reagents + if (reagents && reagents.len) + for (var/r in reagents) + //Doesnt matter whether or not there's enough, we assume that check is done before + container.reagents.trans_type_to(buffer, r, reagents[r]) + + /* + Now we've removed all the ingredients that were used and we have the buffer containing the total of + all their reagents. + Next up we create the result, and then handle the merging of reagents depending on the mix setting + */ + var/tally = 0 + + /* + If we have multiple results, holder will be used as a buffer to hold reagents for the result objects. + If, as in the most common case, there is only a single result, then it will just be a reference to + the single-result's reagents + */ + var/datum/reagents/holder = new/datum/reagents(10000000000) + var/list/results = list() + while (tally < result_quantity) + var/obj/result_obj = new result(container) + results.Add(result_obj) + + if (!result_obj.reagents)//This shouldn't happen + //If the result somehow has no reagents defined, then create a new holder + result_obj.reagents = new /datum/reagents(buffer.total_volume*1.5, result_obj) + + if (result_quantity == 1) + qdel(holder) + holder = result_obj.reagents + else + result_obj.reagents.trans_to(holder, result_obj.reagents.total_volume) + tally++ + + + switch(reagent_mix) + if (RECIPE_REAGENT_REPLACE) + //We do no transferring + if (RECIPE_REAGENT_SUM) + //Sum is easy, just shove the entire buffer into the result + buffer.trans_to_holder(holder, buffer.total_volume) + if (RECIPE_REAGENT_MAX) + //We want the highest of each. + //Iterate through everything in buffer. If the target has less than the buffer, then top it up + for (var/datum/reagent/R in buffer.reagent_list) + var/rvol = holder.get_reagent_amount(R.id) + if (rvol < R.volume) + //Transfer the difference + buffer.trans_type_to(holder, R.id, R.volume-rvol) + + if (RECIPE_REAGENT_MIN) + //Min is slightly more complex. We want the result to have the lowest from each side + //But zero will not count. Where a side has zero its ignored and the side with a nonzero value is used + for (var/datum/reagent/R in buffer.reagent_list) + var/rvol = holder.get_reagent_amount(R.id) + if (rvol == 0) //If the target has zero of this reagent + buffer.trans_type_to(holder, R.id, R.volume) + //Then transfer all of ours + + else if (rvol > R.volume) + //if the target has more than ours + //Remove the difference + holder.remove_reagent(R.id, rvol-R.volume) + + + if (results.len > 1) + //If we're here, then holder is a buffer containing the total reagents for all the results. + //So now we redistribute it among them + var/total = holder.total_volume + for (var/i in results) + var/atom/a = i //optimisation + holder.trans_to(a, total / results.len) + + return results + +// When exact is false, extraneous ingredients are ignored +// When exact is true, extraneous ingredients will fail the recipe +// In both cases, the full set of required ingredients is still needed +/proc/select_recipe(var/list/datum/recipe/available_recipes, var/obj/obj as obj, var/exact) + var/highest_count = 0 + var/count = 0 + for (var/datum/recipe/recipe in available_recipes) + if(!recipe.check_reagents(obj.reagents, exact) || !recipe.check_items(obj, exact) || !recipe.check_fruit(obj, exact)) + continue + // Taken from cmp_recipe_complexity_dsc, but is way faster. + count = LAZYLEN(recipe.items) + LAZYLEN(recipe.reagents) + LAZYLEN(recipe.fruit) + if(count >= highest_count) + highest_count = count + . = recipe + +// Both of these are just placeholders to allow special behavior for mob holders, but you can do other things in here later if you feel like it. +/datum/recipe/proc/before_cook(obj/container) // Called Before the Microwave starts delays and cooking stuff + + +/datum/recipe/proc/after_cook(obj/container) // Called When the Microwave is finished. diff --git a/code/modules/food/recipes_fryer.dm b/code/modules/food/recipes_fryer.dm index 4f1bc471e42..fb728df8dd2 100644 --- a/code/modules/food/recipes_fryer.dm +++ b/code/modules/food/recipes_fryer.dm @@ -1,205 +1,205 @@ -/datum/recipe/fries - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/rawsticks - ) - result = /obj/item/weapon/reagent_containers/food/snacks/fries - -/datum/recipe/cheesyfries - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/fries, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cheesyfries - -/datum/recipe/jpoppers - appliance = FRYER - fruit = list("chili" = 1) - coating = /datum/reagent/nutriment/coating/batter - result = /obj/item/weapon/reagent_containers/food/snacks/jalapeno_poppers - result_quantity = 2 - -/datum/recipe/risottoballs - appliance = FRYER - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/risotto) - coating = /datum/reagent/nutriment/coating/batter - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/risottoballs - result_quantity = 2 - -/datum/recipe/bellefritter - appliance = FRYER - coating = /datum/reagent/nutriment/coating/batter - reagents = list("sugar" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/frostbelle) - result = /obj/item/weapon/reagent_containers/food/snacks/bellefritter - result_quantity = 2 - -/datum/recipe/onionrings - appliance = FRYER - coating = /datum/reagent/nutriment/coating/batter - fruit = list("onion" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/onionrings - result_quantity = 2 - -//Meaty Recipes -//==================== -/datum/recipe/cubancarp - appliance = FRYER - fruit = list("chili" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cubancarp - -/datum/recipe/batteredsausage - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sausage - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sausage/battered - coating = /datum/reagent/nutriment/coating/batter - - -/datum/recipe/katsu - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - ) - result = /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu - coating = /datum/reagent/nutriment/coating/beerbatter - - -/datum/recipe/pizzacrunch_1 - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch - coating = /datum/reagent/nutriment/coating/batter - -//Alternate pizza crunch recipe for combination pizzas made in oven -/datum/recipe/pizzacrunch_2 - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/variable/pizza - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch - coating = /datum/reagent/nutriment/coating/batter - -/datum/recipe/friedmushroom - appliance = FRYER - fruit = list("plumphelmet" = 1) - coating = /datum/reagent/nutriment/coating/beerbatter - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/friedmushroom - -/datum/recipe/fishfingers - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - ) - coating = /datum/reagent/nutriment/coating/batter - result = /obj/item/weapon/reagent_containers/food/snacks/fishfingers - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/corn_dog - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sausage - ) - fruit = list("corn" = 1) - coating = /datum/reagent/nutriment/coating/batter - result = /obj/item/weapon/reagent_containers/food/snacks/corn_dog - -/datum/recipe/sweet_and_sour - appliance = FRYER - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/cutlet - ) - reagents = list("soysauce" = 5, "batter" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/sweet_and_sour - -//Sweet Recipes. -//================== -// All donuts were given reagents of 5 to equal old recipes and make for faster cook times. -/datum/recipe/jellydonut - appliance = FRYER - reagents = list("berryjuice" = 5, "sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly - result_quantity = 2 - -/datum/recipe/jellydonut/poisonberry - reagents = list("poisonberryjuice" = 5, "sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/poisonberry - -/datum/recipe/jellydonut/slime // Subtypes of jellydonut, appliance inheritance applies. - reagents = list("slimejelly" = 5, "sugar" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/slimejelly - -/datum/recipe/jellydonut/cherry // Subtypes of jellydonut, appliance inheritance applies. - reagents = list("cherryjelly" = 5, "sugar" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/cherryjelly - -/datum/recipe/donut - appliance = FRYER - reagents = list("sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain - result_quantity = 2 - -/datum/recipe/chaosdonut - appliance = FRYER - reagents = list("frostoil" = 10, "capsaicin" = 10, "sugar" = 10) - reagent_mix = RECIPE_REAGENT_REPLACE //This creates its own reagents - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/donut/chaos - result_quantity = 2 - -/datum/recipe/funnelcake - appliance = FRYER - reagents = list("sugar" = 5, "batter" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/funnelcake - result_quantity = 2 - -/datum/recipe/pisanggoreng - appliance = FRYER - fruit = list("banana" = 2) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/pisanggoreng - coating = /datum/reagent/nutriment/coating/batter - -//VOREStation Add Start -/datum/recipe/generalschicken - appliance = FRYER - reagents = list("capsaicin" = 2, "sugar" = 2, "batter" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/generalschicken - -/datum/recipe/chickenwings - appliance = FRYER - reagents = list("capsaicin" = 5, "batter" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat - ) - result = /obj/item/weapon/storage/box/wings //This is kinda like the donut box. -//VOREStation Add End +/datum/recipe/fries + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawsticks + ) + result = /obj/item/weapon/reagent_containers/food/snacks/fries + +/datum/recipe/cheesyfries + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/fries, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cheesyfries + +/datum/recipe/jpoppers + appliance = FRYER + fruit = list("chili" = 1) + coating = /datum/reagent/nutriment/coating/batter + result = /obj/item/weapon/reagent_containers/food/snacks/jalapeno_poppers + result_quantity = 2 + +/datum/recipe/risottoballs + appliance = FRYER + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/risotto) + coating = /datum/reagent/nutriment/coating/batter + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/risottoballs + result_quantity = 2 + +/datum/recipe/bellefritter + appliance = FRYER + coating = /datum/reagent/nutriment/coating/batter + reagents = list("sugar" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/frostbelle) + result = /obj/item/weapon/reagent_containers/food/snacks/bellefritter + result_quantity = 2 + +/datum/recipe/onionrings + appliance = FRYER + coating = /datum/reagent/nutriment/coating/batter + fruit = list("onion" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/onionrings + result_quantity = 2 + +//Meaty Recipes +//==================== +/datum/recipe/cubancarp + appliance = FRYER + fruit = list("chili" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cubancarp + +/datum/recipe/batteredsausage + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sausage + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sausage/battered + coating = /datum/reagent/nutriment/coating/batter + + +/datum/recipe/katsu + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + ) + result = /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu + coating = /datum/reagent/nutriment/coating/beerbatter + + +/datum/recipe/pizzacrunch_1 + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch + coating = /datum/reagent/nutriment/coating/batter + +//Alternate pizza crunch recipe for combination pizzas made in oven +/datum/recipe/pizzacrunch_2 + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/variable/pizza + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/crunch + coating = /datum/reagent/nutriment/coating/batter + +/datum/recipe/friedmushroom + appliance = FRYER + fruit = list("plumphelmet" = 1) + coating = /datum/reagent/nutriment/coating/beerbatter + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/friedmushroom + +/datum/recipe/fishfingers + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + ) + coating = /datum/reagent/nutriment/coating/batter + result = /obj/item/weapon/reagent_containers/food/snacks/fishfingers + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/corn_dog + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sausage + ) + fruit = list("corn" = 1) + coating = /datum/reagent/nutriment/coating/batter + result = /obj/item/weapon/reagent_containers/food/snacks/corn_dog + +/datum/recipe/sweet_and_sour + appliance = FRYER + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/cutlet + ) + reagents = list("soysauce" = 5, "batter" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/sweet_and_sour + +//Sweet Recipes. +//================== +// All donuts were given reagents of 5 to equal old recipes and make for faster cook times. +/datum/recipe/jellydonut + appliance = FRYER + reagents = list("berryjuice" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly + result_quantity = 2 + +/datum/recipe/jellydonut/poisonberry + reagents = list("poisonberryjuice" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/poisonberry + +/datum/recipe/jellydonut/slime // Subtypes of jellydonut, appliance inheritance applies. + reagents = list("slimejelly" = 5, "sugar" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/slimejelly + +/datum/recipe/jellydonut/cherry // Subtypes of jellydonut, appliance inheritance applies. + reagents = list("cherryjelly" = 5, "sugar" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain/jelly/cherryjelly + +/datum/recipe/donut + appliance = FRYER + reagents = list("sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/plain + result_quantity = 2 + +/datum/recipe/chaosdonut + appliance = FRYER + reagents = list("frostoil" = 10, "capsaicin" = 10, "sugar" = 10) + reagent_mix = RECIPE_REAGENT_REPLACE //This creates its own reagents + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/chaos + result_quantity = 2 + +/datum/recipe/funnelcake + appliance = FRYER + reagents = list("sugar" = 5, "batter" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/funnelcake + result_quantity = 2 + +/datum/recipe/pisanggoreng + appliance = FRYER + fruit = list("banana" = 2) + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/pisanggoreng + coating = /datum/reagent/nutriment/coating/batter + +//VOREStation Add Start +/datum/recipe/generalschicken + appliance = FRYER + reagents = list("capsaicin" = 2, "sugar" = 2, "batter" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/generalschicken + +/datum/recipe/chickenwings + appliance = FRYER + reagents = list("capsaicin" = 5, "batter" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat + ) + result = /obj/item/weapon/storage/box/wings //This is kinda like the donut box. +//VOREStation Add End diff --git a/code/modules/food/recipes_grill.dm b/code/modules/food/recipes_grill.dm index df92a7148a9..86e63a95f45 100644 --- a/code/modules/food/recipes_grill.dm +++ b/code/modules/food/recipes_grill.dm @@ -1,328 +1,328 @@ -/datum/recipe/humanburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/human, - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/human/burger - -/datum/recipe/plainburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat //do not place this recipe before /datum/recipe/humanburger - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger - -/datum/recipe/syntiburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger - -/datum/recipe/brainburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/organ/internal/brain - ) - result = /obj/item/weapon/reagent_containers/food/snacks/brainburger - -/datum/recipe/roburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/robot_parts/head - ) - result = /obj/item/weapon/reagent_containers/food/snacks/roburger - -/datum/recipe/xenoburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/xenoburger - -/datum/recipe/fishburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/fishburger - -/datum/recipe/tofuburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/tofu - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofuburger - -/datum/recipe/ghostburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/ectoplasm //where do you even find this stuff - ) - result = /obj/item/weapon/reagent_containers/food/snacks/ghostburger - -/datum/recipe/clownburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/clothing/mask/gas/clown_hat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/clownburger - -/datum/recipe/mimeburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/clothing/head/beret - ) - result = /obj/item/weapon/reagent_containers/food/snacks/mimeburger - -/datum/recipe/mouseburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/holder/mouse - ) - result = /obj/item/weapon/reagent_containers/food/snacks/mouseburger - -/datum/recipe/bunbun - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bunbun - -/datum/recipe/hotdog - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/sausage - ) - result = /obj/item/weapon/reagent_containers/food/snacks/hotdog - -/datum/recipe/humankabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/human, - /obj/item/weapon/reagent_containers/food/snacks/meat/human, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/human/kabob - -/datum/recipe/kabob //Do not put before humankabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/monkeykabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey, - /obj/item/weapon/reagent_containers/food/snacks/meat/monkey - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/syntikabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob - -/datum/recipe/tofukabob - appliance = GRILL - items = list( - /obj/item/stack/rods, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofukabob - -/datum/recipe/fakespellburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, - /obj/item/clothing/head/wizard/fake, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/spellburger - -/datum/recipe/spellburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, - /obj/item/clothing/head/wizard, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/spellburger - -/datum/recipe/bigbiteburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - ) - reagents = list("egg" = 3) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger - -/datum/recipe/superbiteburger - appliance = GRILL - fruit = list("tomato" = 1) - reagents = list("sodiumchloride" = 5, "blackpepper" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/boiledegg, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/superbiteburger - -/datum/recipe/slimeburger - appliance = GRILL - reagents = list("slimejelly" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/slime - -/datum/recipe/jellyburger - appliance = GRILL - reagents = list("cherryjelly" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/cherry - -/datum/recipe/bearburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/bearmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bearburger - -/datum/recipe/baconburger - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/bun, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon - ) - result = /obj/item/weapon/reagent_containers/food/snacks/burger/bacon - -/datum/recipe/omelette - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - reagents = list("egg" = 6) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/omelette - -/datum/recipe/omurice - appliance = GRILL - reagents = list("rice" = 5, "ketchup" = 5, "egg" = 3) - result = /obj/item/weapon/reagent_containers/food/snacks/omurice - -/datum/recipe/omurice/heart - appliance = GRILL - reagents = list("rice" = 5, "ketchup" = 5, "sugar" = 5, "egg" = 3) - result = /obj/item/weapon/reagent_containers/food/snacks/omurice/heart - -/datum/recipe/omurice/face - appliance = GRILL - reagents = list("rice" = 5, "ketchup" = 5, "sodiumchloride" = 1, "egg" = 3) - result = /obj/item/weapon/reagent_containers/food/snacks/omurice/face - -/datum/recipe/meatsteak - appliance = GRILL - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) - result = /obj/item/weapon/reagent_containers/food/snacks/meatsteak - -/datum/recipe/honeytoast - appliance = GRILL - reagents = list("honey" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/slice/bread - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/honeytoast - -/datum/recipe/grilled_carp - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat, - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - reagents = list("spacespice" = 1) - fruit = list("lettuce" = 1, "lime" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/grilled_carp - -/datum/recipe/grilledcheese - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/slice/bread, - /obj/item/weapon/reagent_containers/food/snacks/slice/bread, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - ) - result = /obj/item/weapon/reagent_containers/food/snacks/grilledcheese - -/datum/recipe/toastedsandwich - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sandwich - ) - result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich - -/datum/recipe/cheese_cracker - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/spreads/butter, - /obj/item/weapon/reagent_containers/food/snacks/slice/bread, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - reagents = list("spacespice" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/cheesetoast - result_quantity = 4 - -/datum/recipe/bacongrill - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/spreads, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bacon - -/datum/recipe/chickenfillet //Also just combinable, like burgers and hot dogs. - appliance = GRILL - items = list( - /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu, - /obj/item/weapon/reagent_containers/food/snacks/bun - ) - result = /obj/item/weapon/reagent_containers/food/snacks/chickenfillet +/datum/recipe/humanburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/human, + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/human/burger + +/datum/recipe/plainburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/meat //do not place this recipe before /datum/recipe/humanburger + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger + +/datum/recipe/syntiburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeyburger + +/datum/recipe/brainburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/organ/internal/brain + ) + result = /obj/item/weapon/reagent_containers/food/snacks/brainburger + +/datum/recipe/roburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/robot_parts/head + ) + result = /obj/item/weapon/reagent_containers/food/snacks/roburger + +/datum/recipe/xenoburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/xenoburger + +/datum/recipe/fishburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/fishburger + +/datum/recipe/tofuburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/tofu + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofuburger + +/datum/recipe/ghostburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/ectoplasm //where do you even find this stuff + ) + result = /obj/item/weapon/reagent_containers/food/snacks/ghostburger + +/datum/recipe/clownburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/clothing/mask/gas/clown_hat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/clownburger + +/datum/recipe/mimeburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/clothing/head/beret + ) + result = /obj/item/weapon/reagent_containers/food/snacks/mimeburger + +/datum/recipe/mouseburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/holder/mouse + ) + result = /obj/item/weapon/reagent_containers/food/snacks/mouseburger + +/datum/recipe/bunbun + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bunbun + +/datum/recipe/hotdog + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/sausage + ) + result = /obj/item/weapon/reagent_containers/food/snacks/hotdog + +/datum/recipe/humankabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat/human, + /obj/item/weapon/reagent_containers/food/snacks/meat/human, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/human/kabob + +/datum/recipe/kabob //Do not put before humankabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob + +/datum/recipe/monkeykabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat/monkey, + /obj/item/weapon/reagent_containers/food/snacks/meat/monkey + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob + +/datum/recipe/syntikabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeykabob + +/datum/recipe/tofukabob + appliance = GRILL + items = list( + /obj/item/stack/rods, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofukabob + +/datum/recipe/fakespellburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, + /obj/item/clothing/head/wizard/fake, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/spellburger + +/datum/recipe/spellburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, + /obj/item/clothing/head/wizard, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/spellburger + +/datum/recipe/bigbiteburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeyburger, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + ) + reagents = list("egg" = 3) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger + +/datum/recipe/superbiteburger + appliance = GRILL + fruit = list("tomato" = 1) + reagents = list("sodiumchloride" = 5, "blackpepper" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/boiledegg, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/superbiteburger + +/datum/recipe/slimeburger + appliance = GRILL + reagents = list("slimejelly" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/slime + +/datum/recipe/jellyburger + appliance = GRILL + reagents = list("cherryjelly" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/cherry + +/datum/recipe/bearburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/bearmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bearburger + +/datum/recipe/baconburger + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/bun, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon + ) + result = /obj/item/weapon/reagent_containers/food/snacks/burger/bacon + +/datum/recipe/omelette + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + ) + reagents = list("egg" = 6) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/omelette + +/datum/recipe/omurice + appliance = GRILL + reagents = list("rice" = 5, "ketchup" = 5, "egg" = 3) + result = /obj/item/weapon/reagent_containers/food/snacks/omurice + +/datum/recipe/omurice/heart + appliance = GRILL + reagents = list("rice" = 5, "ketchup" = 5, "sugar" = 5, "egg" = 3) + result = /obj/item/weapon/reagent_containers/food/snacks/omurice/heart + +/datum/recipe/omurice/face + appliance = GRILL + reagents = list("rice" = 5, "ketchup" = 5, "sodiumchloride" = 1, "egg" = 3) + result = /obj/item/weapon/reagent_containers/food/snacks/omurice/face + +/datum/recipe/meatsteak + appliance = GRILL + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) + result = /obj/item/weapon/reagent_containers/food/snacks/meatsteak + +/datum/recipe/honeytoast + appliance = GRILL + reagents = list("honey" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/slice/bread + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/honeytoast + +/datum/recipe/grilled_carp + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat, + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + reagents = list("spacespice" = 1) + fruit = list("lettuce" = 1, "lime" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/grilled_carp + +/datum/recipe/grilledcheese + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/grilledcheese + +/datum/recipe/toastedsandwich + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sandwich + ) + result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich + +/datum/recipe/cheese_cracker + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/spreads/butter, + /obj/item/weapon/reagent_containers/food/snacks/slice/bread, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + reagents = list("spacespice" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/cheesetoast + result_quantity = 4 + +/datum/recipe/bacongrill + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/spreads, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bacon + +/datum/recipe/chickenfillet //Also just combinable, like burgers and hot dogs. + appliance = GRILL + items = list( + /obj/item/weapon/reagent_containers/food/snacks/chickenkatsu, + /obj/item/weapon/reagent_containers/food/snacks/bun + ) + result = /obj/item/weapon/reagent_containers/food/snacks/chickenfillet diff --git a/code/modules/food/recipes_microwave.dm b/code/modules/food/recipes_microwave.dm index 7993260ee0e..a033b380bb6 100644 --- a/code/modules/food/recipes_microwave.dm +++ b/code/modules/food/recipes_microwave.dm @@ -831,13 +831,6 @@ I said no! reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product result = /obj/item/weapon/reagent_containers/food/snacks/chickennoodlesoup -/datum/recipe/chickennoodlesoup - fruit = list("carrot" = 1) - reagents = list("water" = 10) - items = list( /obj/item/weapon/reagent_containers/food/snacks/spagetti, /obj/item/weapon/reagent_containers/food/snacks/rawcutlet) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/chickennoodlesoup - /datum/recipe/chilicheesefries items = list( /obj/item/weapon/reagent_containers/food/snacks/fries, diff --git a/code/modules/food/recipes_oven.dm b/code/modules/food/recipes_oven.dm index bf932c9da08..42c3329fa03 100644 --- a/code/modules/food/recipes_oven.dm +++ b/code/modules/food/recipes_oven.dm @@ -1,696 +1,702 @@ -/datum/recipe/ovenfries - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/rawsticks - ) - result = /obj/item/weapon/reagent_containers/food/snacks/ovenfries - -//Roasts -//--------------- - -/datum/recipe/dionaroast - appliance = OVEN - fruit = list("apple" = 1) - reagents = list("pacid" = 5) //It dissolves the carapace. Still poisonous, though. - items = list(/obj/item/weapon/holder/diona) - result = /obj/item/weapon/reagent_containers/food/snacks/dionaroast - reagent_mix = RECIPE_REAGENT_REPLACE //No eating polyacid - -/datum/recipe/monkeysdelight - appliance = OVEN - fruit = list("banana" = 1) - reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "flour" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/monkeycube - ) - result = /obj/item/weapon/reagent_containers/food/snacks/monkeysdelight - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/ribplate - appliance = OVEN - reagents = list("honey" = 5, "spacespice" = 2, "blackpepper" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/ribplate - -/datum/recipe/turkey - appliance = OVEN - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/chicken, - /obj/item/weapon/reagent_containers/food/snacks/stuffing - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/turkey - -/datum/recipe/tofurkey - appliance = OVEN - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/stuffing - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofurkey - -/datum/recipe/zestfish - appliance = OVEN - fruit = list("lemon" = 1) - reagents = list("sodiumchloride" = 3) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/zestfish - -/datum/recipe/limezestfish - appliance = OVEN - fruit = list("lime" = 1) - reagents = list("sodiumchloride" = 3) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/carpmeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/zestfish - - -//Predesigned breads -//================================ -/datum/recipe/bread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - reagents = list("sodiumchloride" = 1, "yeast" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bread - -/datum/recipe/baguette - appliance = OVEN - reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "yeast" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/baguette - - -/datum/recipe/tofubread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/tofu, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/tofubread - - -/datum/recipe/creamcheesebread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/creamcheesebread - -/datum/recipe/flatbread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/flatbread - -/datum/recipe/tortilla - appliance = OVEN - reagents = list("flour" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tortilla - result_quantity = 3 - -/datum/recipe/meatbread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread - -/datum/recipe/syntibread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread - -/datum/recipe/xenomeatbread - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/xenomeatbread - -/datum/recipe/bananabread - appliance = OVEN - fruit = list("banana" = 1) - reagents = list("milk" = 5, "sugar" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bananabread - - -/datum/recipe/bun - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bun - result_quantity = 3 - -//Predesigned pies -//======================= - -/datum/recipe/meatpie - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/meatpie - -/datum/recipe/tofupie - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/tofu - ) - result = /obj/item/weapon/reagent_containers/food/snacks/tofupie - -/datum/recipe/xemeatpie - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/xenomeat - ) - result = /obj/item/weapon/reagent_containers/food/snacks/xemeatpie - -/datum/recipe/pie - appliance = OVEN - fruit = list("banana" = 1) - reagents = list("sugar" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/pie - -/datum/recipe/cherrypie - appliance = OVEN - fruit = list("cherries" = 1) - reagents = list("sugar" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cherrypie - -/datum/recipe/amanita_pie - appliance = OVEN - reagents = list("amatoxin" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/amanita_pie - -/datum/recipe/plump_pie - appliance = OVEN - fruit = list("plumphelmet" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/plump_pie - -/datum/recipe/applepie - appliance = OVEN - fruit = list("apple" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/applepie - -/datum/recipe/pumpkinpie - appliance = OVEN - fruit = list("pumpkin" = 1) - reagents = list("sugar" = 5) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pumpkinpie - -/datum/recipe/appletart - appliance = OVEN - fruit = list("goldapple" = 1) - reagents = list("sugar" = 10) - items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) - result = /obj/item/weapon/reagent_containers/food/snacks/appletart - result_quantity = 2 - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/keylimepie - appliance = OVEN - fruit = list("lime" = 2) - reagents = list("milk" = 5, "sugar" = 5, "egg" = 3, "flour" = 10) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/keylimepie - reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise - -/datum/recipe/quiche - appliance = OVEN - reagents = list("milk" = 5, "egg" = 9, "flour" = 10) - items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/quiche - reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise - -//Baked sweets: -//--------------- - -/datum/recipe/cookie - appliance = OVEN - reagents = list("milk" = 10, "sugar" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cookie - result_quantity = 4 - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/ovenfortunecookie - appliance = OVEN - reagents = list("sugar" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/paper - ) - result = /obj/item/weapon/reagent_containers/food/snacks/fortunecookie - -/datum/recipe/poppypretzel - appliance = OVEN - fruit = list("poppy" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) - result = /obj/item/weapon/reagent_containers/food/snacks/poppypretzel - result_quantity = 2 - -/datum/recipe/cracker - appliance = OVEN - reagents = list("sodiumchloride" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cracker - -/datum/recipe/brownies - appliance = OVEN - reagents = list("browniemix" = 10, "egg" = 3) - reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/brownies - -/datum/recipe/cosmicbrownies - appliance = OVEN - reagents = list("browniemix" = 10, "egg" = 3) - fruit = list("ambrosia" = 1) - reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cosmicbrownies - -/datum/recipe/buchedenoel - appliance = OVEN - fruit = list("berries" = 2) - reagents = list("cakebatter" = 20, "cream" = 10, "coco" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/buchedenoel - -/datum/recipe/cinnamonbun - appliance = OVEN - reagents = list("sugar" = 15, "cream" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/cinnamonbun - result_quantity = 4 - - -//Pizzas -//========================= -/datum/recipe/pizzamargherita - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita - -/datum/recipe/meatpizza - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/meat, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza - -/datum/recipe/syntipizza - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza - -/datum/recipe/mushroompizza - appliance = OVEN - fruit = list("mushroom" = 5, "tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - - reagent_mix = RECIPE_REAGENT_REPLACE //No vomit taste in finished product from chanterelles - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza - -/datum/recipe/vegetablepizza - appliance = OVEN - fruit = list("eggplant" = 1, "carrot" = 1, "corn" = 1, "tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza - -/datum/recipe/pineapplepizza - appliance = OVEN - fruit = list("tomato" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring, - /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple - -//Spicy -//================ - -/datum/recipe/enchiladas - appliance = OVEN - fruit = list("chili" = 2) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/cutlet, - /obj/item/weapon/reagent_containers/food/snacks/tortilla - ) - result = /obj/item/weapon/reagent_containers/food/snacks/enchiladas - - -// Cakes. -//============ -/datum/recipe/cake - appliance = OVEN - reagents = list("cakebatter" = 30, "vanilla" = 2) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/plaincake - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/cake/carrot - appliance = OVEN - fruit = list("carrot" = 3) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/carrotcake - -/datum/recipe/cake/cheese - appliance = OVEN - reagents = list("cakebatter" = 30) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - -/datum/recipe/cake/peanut - fruit = list("peanut" = 1) - reagents = list("cakebatter" = 30, "peanutbutter" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake - -/datum/recipe/cake/orange - appliance = OVEN - fruit = list("orange" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/orangecake - -/datum/recipe/cake/lime - appliance = OVEN - fruit = list("lime" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/limecake - -/datum/recipe/cake/lemon - appliance = OVEN - fruit = list("lemon" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/lemoncake - -/datum/recipe/cake/chocolate - appliance = OVEN - reagents = list("cakebatter" = 30, "coco" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/chocolatecake - -/datum/recipe/cake/birthday - appliance = OVEN - reagents = list("cakebatter" = 30) - items = list(/obj/item/clothing/head/cakehat) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/birthdaycake - -/datum/recipe/cake/apple - appliance = OVEN - fruit = list("apple" = 2) - reagents = list("cakebatter" = 30) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/applecake - -/datum/recipe/cake/brain - appliance = OVEN - reagents = list("cakebatter" = 30) - items = list(/obj/item/organ/internal/brain) - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/braincake - -/datum/recipe/pancakes - appliance = OVEN - reagents = list("milk" = 5, "sugar" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/pancakes - result_quantity = 2 - -/datum/recipe/pancakes/berry - appliance = OVEN - fruit = list("berries" = 2) - reagents = list("milk" = 5, "sugar" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/pancakes/berry - result_quantity = 2 - -/datum/recipe/lasagna - appliance = OVEN - fruit = list("tomato" = 2, "eggplant" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cutlet, - /obj/item/weapon/reagent_containers/food/snacks/cutlet - ) - result = /obj/item/weapon/reagent_containers/food/snacks/lasagna - reagent_mix = RECIPE_REAGENT_REPLACE - -/datum/recipe/honeybun - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - reagents = list("milk" = 5, "egg" = 3,"honey" = 5) - result = /obj/item/weapon/reagent_containers/food/snacks/honeybun - result_quantity = 4 - -//Bacon -/datum/recipe/bacon_oven - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/rawbacon, - /obj/item/weapon/reagent_containers/food/snacks/spreads - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bacon/oven - result_quantity = 6 - -/datum/recipe/meat_pocket - appliance = OVEN - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/meatball, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/meat_pocket - result_quantity = 2 - -/datum/recipe/bacon_flatbread - appliance = OVEN - fruit = list("tomato" = 2) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon, - /obj/item/weapon/reagent_containers/food/snacks/bacon - ) - result = /obj/item/weapon/reagent_containers/food/snacks/bacon_flatbread - -/datum/recipe/truffle - appliance = OVEN - reagents = list("sugar" = 5, "cream" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar - ) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/truffle - result_quantity = 4 - -/datum/recipe/croissant - appliance = OVEN - reagents = list("sodiumchloride" = 1, "water" = 5, "milk" = 5, "yeast" = 5) - reagent_mix = RECIPE_REAGENT_REPLACE - items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) - result = /obj/item/weapon/reagent_containers/food/snacks/croissant - result_quantity = 2 - -/datum/recipe/macncheese - appliance = OVEN - reagents = list("milk" = 5) - reagent_mix = RECIPE_REAGENT_REPLACE - items = list( - /obj/item/weapon/reagent_containers/food/snacks/spagetti, - /obj/item/weapon/reagent_containers/food/snacks/cheesewedge - ) - result = /obj/item/weapon/reagent_containers/food/snacks/macncheese - -/datum/recipe/suppermatter - appliance = OVEN - reagents = list("radium" = 5, "milk" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - ) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/suppermatter - -/datum/recipe/excitingsuppermatter - appliance = OVEN - reagents = list("radium" = 5, "spacespice" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - ) - reagent_mix = RECIPE_REAGENT_REPLACE - result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/excitingsuppermatter - -/datum/recipe/waffles - appliance = OVEN - reagents = list("sugar" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/reagent_containers/food/snacks/waffles - result_quantity = 2 - -/datum/recipe/loadedbakedpotatooven - appliance = OVEN - fruit = list("potato" = 1) - items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) - result = /obj/item/weapon/reagent_containers/food/snacks/loadedbakedpotato - -/datum/recipe/meatbun - appliance = OVEN - fruit = list("cabbage" = 1) - reagents = list("water" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meatball, - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking - result = /obj/item/weapon/reagent_containers/food/snacks/meatbun - result_quantity = 2 - -/datum/recipe/spicedmeatbun - appliance = OVEN - reagents = list("spacespice" = 2, "water" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/rawcutlet - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking - result = /obj/item/weapon/reagent_containers/food/snacks/spicedmeatbun - result_quantity = 2 - -/datum/recipe/custardbun - appliance = OVEN - reagents = list("spacespice" = 1, "water" = 5, "egg" = 3) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Water, egg used up in cooking - result = /obj/item/weapon/reagent_containers/food/snacks/custardbun - -/datum/recipe/chickenmomo - appliance = OVEN - reagents = list("spacespice" = 2, "water" = 5) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product - result = /obj/item/weapon/reagent_containers/food/snacks/chickenmomo - result_quantity = 2 - -/datum/recipe/veggiemomo - appliance = OVEN - reagents = list("spacespice" = 2, "water" = 5) - fruit = list("carrot" = 1, "cabbage" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice, - /obj/item/weapon/reagent_containers/food/snacks/doughslice - ) - reagent_mix = RECIPE_REAGENT_REPLACE //Get that water outta here - result = /obj/item/weapon/reagent_containers/food/snacks/veggiemomo +/datum/recipe/ovenfries + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawsticks + ) + result = /obj/item/weapon/reagent_containers/food/snacks/ovenfries + +//Roasts +//--------------- + +/datum/recipe/dionaroast + appliance = OVEN + fruit = list("apple" = 1) + reagents = list("pacid" = 5) //It dissolves the carapace. Still poisonous, though. + items = list(/obj/item/weapon/holder/diona) + result = /obj/item/weapon/reagent_containers/food/snacks/dionaroast + reagent_mix = RECIPE_REAGENT_REPLACE //No eating polyacid + +/datum/recipe/monkeysdelight + appliance = OVEN + fruit = list("banana" = 1) + reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "flour" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/monkeycube + ) + result = /obj/item/weapon/reagent_containers/food/snacks/monkeysdelight + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/ribplate + appliance = OVEN + reagents = list("honey" = 5, "spacespice" = 2, "blackpepper" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/meat) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/ribplate + +/datum/recipe/turkey + appliance = OVEN + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/chicken, + /obj/item/weapon/reagent_containers/food/snacks/stuffing + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/turkey + +/datum/recipe/tofurkey + appliance = OVEN + reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/stuffing + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofurkey + +/datum/recipe/zestfish + appliance = OVEN + fruit = list("lemon" = 1) + reagents = list("sodiumchloride" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/zestfish + +/datum/recipe/limezestfish + appliance = OVEN + fruit = list("lime" = 1) + reagents = list("sodiumchloride" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/carpmeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/zestfish + + +//Predesigned breads +//================================ +/datum/recipe/bread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + reagents = list("sodiumchloride" = 1, "yeast" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bread + +/datum/recipe/baguette + appliance = OVEN + reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "yeast" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/baguette + + +/datum/recipe/tofubread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/tofu, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/tofubread + + +/datum/recipe/creamcheesebread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/creamcheesebread + +/datum/recipe/flatbread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/flatbread + +/datum/recipe/tortilla + appliance = OVEN + reagents = list("flour" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tortilla + result_quantity = 3 + +/datum/recipe/meatbread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread + +/datum/recipe/syntibread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/meatbread + +/datum/recipe/xenomeatbread + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/xenomeatbread + +/datum/recipe/bananabread + appliance = OVEN + fruit = list("banana" = 1) + reagents = list("milk" = 5, "sugar" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bananabread + + +/datum/recipe/bun + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bun + result_quantity = 3 + +//Predesigned pies +//======================= + +/datum/recipe/meatpie + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/meatpie + +/datum/recipe/tofupie + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/tofu + ) + result = /obj/item/weapon/reagent_containers/food/snacks/tofupie + +/datum/recipe/xemeatpie + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/xenomeat + ) + result = /obj/item/weapon/reagent_containers/food/snacks/xemeatpie + +/datum/recipe/pie + appliance = OVEN + fruit = list("banana" = 1) + reagents = list("sugar" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/pie + +/datum/recipe/cherrypie + appliance = OVEN + fruit = list("cherries" = 1) + reagents = list("sugar" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cherrypie + +/datum/recipe/amanita_pie + appliance = OVEN + reagents = list("amatoxin" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/amanita_pie + +/datum/recipe/plump_pie + appliance = OVEN + fruit = list("plumphelmet" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/plump_pie + +/datum/recipe/applepie + appliance = OVEN + fruit = list("apple" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/applepie + +/datum/recipe/pumpkinpie + appliance = OVEN + fruit = list("pumpkin" = 1) + reagents = list("sugar" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pumpkinpie + +/datum/recipe/appletart + appliance = OVEN + fruit = list("goldapple" = 1) + reagents = list("sugar" = 10) + items = list(/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough) + result = /obj/item/weapon/reagent_containers/food/snacks/appletart + result_quantity = 2 + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/keylimepie + appliance = OVEN + fruit = list("lime" = 2) + reagents = list("milk" = 5, "sugar" = 5, "egg" = 3, "flour" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/keylimepie + reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise + +/datum/recipe/quiche + appliance = OVEN + reagents = list("milk" = 5, "egg" = 9, "flour" = 10) + items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/quiche + reagent_mix = RECIPE_REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise + +//Baked sweets: +//--------------- + +/datum/recipe/cookie + appliance = OVEN + reagents = list("milk" = 10, "sugar" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cookie + result_quantity = 4 + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/ovenfortunecookie + appliance = OVEN + reagents = list("sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/paper + ) + result = /obj/item/weapon/reagent_containers/food/snacks/fortunecookie + +/datum/recipe/poppypretzel + appliance = OVEN + fruit = list("poppy" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) + result = /obj/item/weapon/reagent_containers/food/snacks/poppypretzel + result_quantity = 2 + +/datum/recipe/cracker + appliance = OVEN + reagents = list("sodiumchloride" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cracker + +/datum/recipe/brownies + appliance = OVEN + reagents = list("browniemix" = 10, "egg" = 3) + reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/brownies + +/datum/recipe/cosmicbrownies + appliance = OVEN + reagents = list("browniemix" = 10, "egg" = 3) + fruit = list("ambrosia" = 1) + reagent_mix = RECIPE_REAGENT_REPLACE //No egg or mix in final recipe + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cosmicbrownies + +/datum/recipe/buchedenoel + appliance = OVEN + fruit = list("berries" = 2) + reagents = list("cakebatter" = 20, "cream" = 10, "coco" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/buchedenoel + +/datum/recipe/cinnamonbun + appliance = OVEN + reagents = list("sugar" = 15, "cream" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/cinnamonbun + result_quantity = 4 + +/datum/recipe/jaffacake + appliance = OVEN + fruit = list("orange" = 1) + reagents = list("cakebatter" = 15, "coco" = 10) + result = /obj/item/weapon/reagent_containers/food/snacks/jaffacake + result_quantity = 6 + +//Pizzas +//========================= +/datum/recipe/pizzamargherita + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita + +/datum/recipe/meatpizza + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/meat, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza + +/datum/recipe/syntipizza + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza + +/datum/recipe/mushroompizza + appliance = OVEN + fruit = list("mushroom" = 5, "tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + + reagent_mix = RECIPE_REAGENT_REPLACE //No vomit taste in finished product from chanterelles + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza + +/datum/recipe/vegetablepizza + appliance = OVEN + fruit = list("eggplant" = 1, "carrot" = 1, "corn" = 1, "tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza + +/datum/recipe/pineapplepizza + appliance = OVEN + fruit = list("tomato" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring, + /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple + +//Spicy +//================ + +/datum/recipe/enchiladas + appliance = OVEN + fruit = list("chili" = 2) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cutlet, + /obj/item/weapon/reagent_containers/food/snacks/tortilla + ) + result = /obj/item/weapon/reagent_containers/food/snacks/enchiladas + + +// Cakes. +//============ +/datum/recipe/cake + appliance = OVEN + reagents = list("cakebatter" = 30, "vanilla" = 2) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/plaincake + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/cake/carrot + appliance = OVEN + fruit = list("carrot" = 3) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/carrotcake + +/datum/recipe/cake/cheese + appliance = OVEN + reagents = list("cakebatter" = 30) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake + +/datum/recipe/cake/peanut + fruit = list("peanut" = 1) + reagents = list("cakebatter" = 30, "peanutbutter" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/peanutcake + +/datum/recipe/cake/orange + appliance = OVEN + fruit = list("orange" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/orangecake + +/datum/recipe/cake/lime + appliance = OVEN + fruit = list("lime" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/limecake + +/datum/recipe/cake/lemon + appliance = OVEN + fruit = list("lemon" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/lemoncake + +/datum/recipe/cake/chocolate + appliance = OVEN + reagents = list("cakebatter" = 30, "coco" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/chocolatecake + +/datum/recipe/cake/birthday + appliance = OVEN + reagents = list("cakebatter" = 30) + items = list(/obj/item/clothing/head/cakehat) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/birthdaycake + +/datum/recipe/cake/apple + appliance = OVEN + fruit = list("apple" = 2) + reagents = list("cakebatter" = 30) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/applecake + +/datum/recipe/cake/brain + appliance = OVEN + reagents = list("cakebatter" = 30) + items = list(/obj/item/organ/internal/brain) + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/braincake + +/datum/recipe/pancakes + appliance = OVEN + reagents = list("milk" = 5, "sugar" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/pancakes + result_quantity = 2 + +/datum/recipe/pancakes/berry + appliance = OVEN + fruit = list("berries" = 2) + reagents = list("milk" = 5, "sugar" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/pancakes/berry + result_quantity = 2 + +/datum/recipe/lasagna + appliance = OVEN + fruit = list("tomato" = 2, "eggplant" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cutlet, + /obj/item/weapon/reagent_containers/food/snacks/cutlet + ) + result = /obj/item/weapon/reagent_containers/food/snacks/lasagna + reagent_mix = RECIPE_REAGENT_REPLACE + +/datum/recipe/honeybun + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + reagents = list("milk" = 5, "egg" = 3,"honey" = 5) + result = /obj/item/weapon/reagent_containers/food/snacks/honeybun + result_quantity = 4 + +//Bacon +/datum/recipe/bacon_oven + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/rawbacon, + /obj/item/weapon/reagent_containers/food/snacks/spreads + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bacon/oven + result_quantity = 6 + +/datum/recipe/meat_pocket + appliance = OVEN + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/meatball, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/meat_pocket + result_quantity = 2 + +/datum/recipe/bacon_flatbread + appliance = OVEN + fruit = list("tomato" = 2) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon, + /obj/item/weapon/reagent_containers/food/snacks/bacon + ) + result = /obj/item/weapon/reagent_containers/food/snacks/bacon_flatbread + +/datum/recipe/truffle + appliance = OVEN + reagents = list("sugar" = 5, "cream" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar + ) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/truffle + result_quantity = 4 + +/datum/recipe/croissant + appliance = OVEN + reagents = list("sodiumchloride" = 1, "water" = 5, "milk" = 5, "yeast" = 5) + reagent_mix = RECIPE_REAGENT_REPLACE + items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) + result = /obj/item/weapon/reagent_containers/food/snacks/croissant + result_quantity = 2 + +/datum/recipe/macncheese + appliance = OVEN + reagents = list("milk" = 5) + reagent_mix = RECIPE_REAGENT_REPLACE + items = list( + /obj/item/weapon/reagent_containers/food/snacks/spagetti, + /obj/item/weapon/reagent_containers/food/snacks/cheesewedge + ) + result = /obj/item/weapon/reagent_containers/food/snacks/macncheese + +/datum/recipe/suppermatter + appliance = OVEN + reagents = list("radium" = 5, "milk" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake + ) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/suppermatter + +/datum/recipe/excitingsuppermatter + appliance = OVEN + reagents = list("radium" = 5, "spacespice" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake + ) + reagent_mix = RECIPE_REAGENT_REPLACE + result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/excitingsuppermatter + +/datum/recipe/waffles + appliance = OVEN + reagents = list("sugar" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/waffles + result_quantity = 2 + +/datum/recipe/loadedbakedpotatooven + appliance = OVEN + fruit = list("potato" = 1) + items = list(/obj/item/weapon/reagent_containers/food/snacks/cheesewedge) + result = /obj/item/weapon/reagent_containers/food/snacks/loadedbakedpotato + +/datum/recipe/meatbun + appliance = OVEN + fruit = list("cabbage" = 1) + reagents = list("water" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meatball, + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking + result = /obj/item/weapon/reagent_containers/food/snacks/meatbun + result_quantity = 2 + +/datum/recipe/spicedmeatbun + appliance = OVEN + reagents = list("spacespice" = 2, "water" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/rawcutlet + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Water used up in cooking + result = /obj/item/weapon/reagent_containers/food/snacks/spicedmeatbun + result_quantity = 2 + +/datum/recipe/custardbun + appliance = OVEN + reagents = list("spacespice" = 1, "water" = 5, "egg" = 3) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Water, egg used up in cooking + result = /obj/item/weapon/reagent_containers/food/snacks/custardbun + +/datum/recipe/chickenmomo + appliance = OVEN + reagents = list("spacespice" = 2, "water" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Simplify end product + result = /obj/item/weapon/reagent_containers/food/snacks/chickenmomo + result_quantity = 2 + +/datum/recipe/veggiemomo + appliance = OVEN + reagents = list("spacespice" = 2, "water" = 5) + fruit = list("carrot" = 1, "cabbage" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, + /obj/item/weapon/reagent_containers/food/snacks/doughslice + ) + reagent_mix = RECIPE_REAGENT_REPLACE //Get that water outta here + result = /obj/item/weapon/reagent_containers/food/snacks/veggiemomo result_quantity = 2 \ No newline at end of file diff --git a/code/modules/food/recipes_oven_vr.dm b/code/modules/food/recipes_oven_vr.dm index b292a9ff447..fad763bf1fd 100644 --- a/code/modules/food/recipes_oven_vr.dm +++ b/code/modules/food/recipes_oven_vr.dm @@ -1,7 +1,7 @@ -/datum/recipe/scorpion - appliance = OVEN - reagents = list("sodiumchloride" = 1) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/scorpion - ) +/datum/recipe/scorpion + appliance = OVEN + reagents = list("sodiumchloride" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/scorpion + ) result = /obj/item/weapon/reagent_containers/food/snacks/scorpion_cooked \ No newline at end of file diff --git a/code/modules/gamemaster/defines.dm b/code/modules/gamemaster/defines.dm index 5bc17b462c7..215e07ff91a 100644 --- a/code/modules/gamemaster/defines.dm +++ b/code/modules/gamemaster/defines.dm @@ -1,3 +1,3 @@ -#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25 -#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50 +#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25 +#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50 #define EVENT_CHAOS_THRESHOLD_LOW_IMPACT 100 \ No newline at end of file diff --git a/code/modules/gamemaster/event2/event.dm b/code/modules/gamemaster/event2/event.dm index b9f1d92648d..50be78a14bf 100644 --- a/code/modules/gamemaster/event2/event.dm +++ b/code/modules/gamemaster/event2/event.dm @@ -1,240 +1,240 @@ -// This object holds the code that is needed to execute an event. -// Code for judging whether doing that event is a good idea or not belongs inside its meta event object. - - -/* - -Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead. -Also please don't use spawn(), but use callbacks instead. - -Note that there is an important distinction between an event being ended, and an event being finished. -- Ended is for when the actual event is over, regardless of whether an announcement happened or not. -- Finished is for when both the event itself is over, and it was announced. The event will stop being -processed after it is finished. - -For an event to finish, it must have done two things: -- Go through its entire cycle, of start() -> end(), and -- Have the event be announced. -If an event has ended, but the announcement didn't happen, the event will not be finished. -This allows for events that have their announcement happen after the end itself. - -*/ - -// -/datum/event2/event - var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`. - var/started = FALSE // Is set to TRUE when `start()` is called by `process()`. - var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`. - var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE. - - // `world.time`s when this event started, and finished, for bookkeeping. - var/time_started = null - var/time_finished = null - - // If these are set, the announcement will be delayed by a random time between the lower and upper bounds. - // If the upper bound is not defined, then it will use the lower bound instead. - // Note that this is independant of the event itself, so you can have the announcement happen long after the event ended. - // This may not work if should_announce() is overrided. - var/announce_delay_lower_bound = null - var/announce_delay_upper_bound = null - - // If these are set, the event will be delayed by a random time between the lower and upper bounds. - // If the upper bound is not defined, then it will use the lower bound instead. - // This may not work if should_start() is overrided. - var/start_delay_lower_bound = null - var/start_delay_upper_bound = null - - // If these are set, the event will automatically end at a random time between the lower and upper bounds. - // If the upper bound is not defined, then it will use the lower bound instead. - // This may not work if should_end() is overrided. - var/length_lower_bound = null - var/length_upper_bound = null - - // Set automatically, don't touch. - var/time_to_start = null - var/time_to_announce = null - var/time_to_end = null - - // These are also set automatically, and are provided for events to know what RNG decided for the various durations. - var/start_delay = null - var/announce_delay = null - var/length = null - -// Returns the name of where the event is taking place. -// In the future this might be handy for off-station events. -/datum/event2/event/proc/location_name() - return station_name() - -// Returns the z-levels that are involved with the event. -// In the future this might be handy for off-station events. -/datum/event2/event/proc/get_location_z_levels(space_only = FALSE) - . = using_map.station_levels.Copy() - if(space_only) - for(var/z_level in .) - if(is_planet_z_level(z_level)) - . -= z_level - - -/datum/event2/event/proc/is_planet_z_level(z_level) - var/datum/planet/P = LAZYACCESS(SSplanets.z_to_planet, z_level) - if(!istype(P)) - return FALSE - return TRUE - -// Returns a list of empty turfs in the same area. -/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE) - var/list/area/grand_list_of_areas = find_random_areas(specific_areas) - - if(!LAZYLEN(grand_list_of_areas)) - return list() - - for(var/list/A as anything in grand_list_of_areas) - var/list/turfs = list() - for(var/turf/T in A) - if(!T.check_density()) - turfs += T - - if(turfs.len < minimum_free_space) - continue // Not enough free space. - return turfs - - return list() - -/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE) - if(!LAZYLEN(specific_areas)) - specific_areas = global.the_station_areas.Copy() - - var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas) - . = list() - for(var/area/A as anything in shuffle(grand_list_of_areas)) - if(A.forbid_events) - continue - if(!(A.z in get_location_z_levels())) - continue - if(!ignore_occupancy && is_area_occupied(A)) - continue // Occupied. - . += A - - -// Starts the event. -/datum/event2/event/proc/execute() - time_started = world.time - - if(announce_delay_lower_bound) - announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound) - time_to_announce = world.time + announce_delay - - if(start_delay_lower_bound) - start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound) - time_to_start = world.time + start_delay - - if(length_lower_bound) - var/starting_point = time_to_start ? time_to_start : world.time - length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound) - time_to_end = starting_point + length - - set_up() - -// Called at the very end of the event's lifecycle, or when aborted. -// Don't override this, use `end()` for cleanup instead. -/datum/event2/event/proc/finish() - finished = TRUE - time_finished = world.time - -// Called by admins wanting to stop an event immediately. -/datum/event2/event/proc/abort() - if(!announced) - announce() - if(!ended) // `end()` generally has cleanup procs, so call that. - end() - finish() - -// Called by the GM processer. -/datum/event2/event/process() - // Handle announcement track. - if(!announced && should_announce()) - announced = TRUE - announce() - - // Handle event track. - if(!started) - if(should_start()) - started = TRUE - start() - else - wait_tick() - - if(started && !ended) - if(should_end()) - ended = TRUE - end() - else - event_tick() - - // In order to be finished, the event needs to end, and be announced. - if(ended && announced) - finish() - -/datum/event2/event/Topic(href, href_list) - if(..()) - return - - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") - return - - if(href_list["abort"]) - abort() - message_admins("Event '[type]' was aborted by [usr.key].") - - // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. - -/* - * Procs to Override - */ - -// Override this for code to be ran before the event is started. -/datum/event2/event/proc/set_up() - -// Called every tick from the GM system, and determines if the announcement should happen. -// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE, -// however be aware that the event cannot finish until this returns TRUE at some point. -/datum/event2/event/proc/should_announce() - if(!time_to_announce) - return TRUE - return time_to_announce <= world.time - -// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message. -// If you want them to not know, you can just not override it. -/datum/event2/event/proc/announce() - -// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE. -// Note that events that have `should_start()` return TRUE at the start will never have this proc called. -/datum/event2/event/proc/wait_tick() - -// Called every tick from the GM system, and determines if the event should offically start. -// Override this for special logic on when it should start. -/datum/event2/event/proc/should_start() - if(!time_to_start) - return TRUE - return time_to_start <= world.time - -// Override this for code to do the actual event. -/datum/event2/event/proc/start() - - -// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE. -// Note that events that have `should_end()` return TRUE at the start will never have this proc called. -/datum/event2/event/proc/event_tick() - - -// Called every tick from the GM system, and determines if the event should end. -// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called. -// Override this for special logic on when it should end, e.g. blob core has to die before event ends. -/datum/event2/event/proc/should_end() - if(!time_to_end) - return TRUE - return time_to_end <= world.time - -// Override this for code to run when the event is over, e.g. cleanup. -/datum/event2/event/proc/end() +// This object holds the code that is needed to execute an event. +// Code for judging whether doing that event is a good idea or not belongs inside its meta event object. + + +/* + +Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead. +Also please don't use spawn(), but use callbacks instead. + +Note that there is an important distinction between an event being ended, and an event being finished. +- Ended is for when the actual event is over, regardless of whether an announcement happened or not. +- Finished is for when both the event itself is over, and it was announced. The event will stop being +processed after it is finished. + +For an event to finish, it must have done two things: +- Go through its entire cycle, of start() -> end(), and +- Have the event be announced. +If an event has ended, but the announcement didn't happen, the event will not be finished. +This allows for events that have their announcement happen after the end itself. + +*/ + +// +/datum/event2/event + var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`. + var/started = FALSE // Is set to TRUE when `start()` is called by `process()`. + var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`. + var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE. + + // `world.time`s when this event started, and finished, for bookkeeping. + var/time_started = null + var/time_finished = null + + // If these are set, the announcement will be delayed by a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // Note that this is independant of the event itself, so you can have the announcement happen long after the event ended. + // This may not work if should_announce() is overrided. + var/announce_delay_lower_bound = null + var/announce_delay_upper_bound = null + + // If these are set, the event will be delayed by a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // This may not work if should_start() is overrided. + var/start_delay_lower_bound = null + var/start_delay_upper_bound = null + + // If these are set, the event will automatically end at a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // This may not work if should_end() is overrided. + var/length_lower_bound = null + var/length_upper_bound = null + + // Set automatically, don't touch. + var/time_to_start = null + var/time_to_announce = null + var/time_to_end = null + + // These are also set automatically, and are provided for events to know what RNG decided for the various durations. + var/start_delay = null + var/announce_delay = null + var/length = null + +// Returns the name of where the event is taking place. +// In the future this might be handy for off-station events. +/datum/event2/event/proc/location_name() + return station_name() + +// Returns the z-levels that are involved with the event. +// In the future this might be handy for off-station events. +/datum/event2/event/proc/get_location_z_levels(space_only = FALSE) + . = using_map.station_levels.Copy() + if(space_only) + for(var/z_level in .) + if(is_planet_z_level(z_level)) + . -= z_level + + +/datum/event2/event/proc/is_planet_z_level(z_level) + var/datum/planet/P = LAZYACCESS(SSplanets.z_to_planet, z_level) + if(!istype(P)) + return FALSE + return TRUE + +// Returns a list of empty turfs in the same area. +/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE) + var/list/area/grand_list_of_areas = find_random_areas(specific_areas) + + if(!LAZYLEN(grand_list_of_areas)) + return list() + + for(var/list/A as anything in grand_list_of_areas) + var/list/turfs = list() + for(var/turf/T in A) + if(!T.check_density()) + turfs += T + + if(turfs.len < minimum_free_space) + continue // Not enough free space. + return turfs + + return list() + +/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE) + if(!LAZYLEN(specific_areas)) + specific_areas = global.the_station_areas.Copy() + + var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas) + . = list() + for(var/area/A as anything in shuffle(grand_list_of_areas)) + if(A.forbid_events) + continue + if(!(A.z in get_location_z_levels())) + continue + if(!ignore_occupancy && is_area_occupied(A)) + continue // Occupied. + . += A + + +// Starts the event. +/datum/event2/event/proc/execute() + time_started = world.time + + if(announce_delay_lower_bound) + announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound) + time_to_announce = world.time + announce_delay + + if(start_delay_lower_bound) + start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound) + time_to_start = world.time + start_delay + + if(length_lower_bound) + var/starting_point = time_to_start ? time_to_start : world.time + length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound) + time_to_end = starting_point + length + + set_up() + +// Called at the very end of the event's lifecycle, or when aborted. +// Don't override this, use `end()` for cleanup instead. +/datum/event2/event/proc/finish() + finished = TRUE + time_finished = world.time + +// Called by admins wanting to stop an event immediately. +/datum/event2/event/proc/abort() + if(!announced) + announce() + if(!ended) // `end()` generally has cleanup procs, so call that. + end() + finish() + +// Called by the GM processer. +/datum/event2/event/process() + // Handle announcement track. + if(!announced && should_announce()) + announced = TRUE + announce() + + // Handle event track. + if(!started) + if(should_start()) + started = TRUE + start() + else + wait_tick() + + if(started && !ended) + if(should_end()) + ended = TRUE + end() + else + event_tick() + + // In order to be finished, the event needs to end, and be announced. + if(ended && announced) + finish() + +/datum/event2/event/Topic(href, href_list) + if(..()) + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") + return + + if(href_list["abort"]) + abort() + message_admins("Event '[type]' was aborted by [usr.key].") + + // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. + +/* + * Procs to Override + */ + +// Override this for code to be ran before the event is started. +/datum/event2/event/proc/set_up() + +// Called every tick from the GM system, and determines if the announcement should happen. +// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE, +// however be aware that the event cannot finish until this returns TRUE at some point. +/datum/event2/event/proc/should_announce() + if(!time_to_announce) + return TRUE + return time_to_announce <= world.time + +// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message. +// If you want them to not know, you can just not override it. +/datum/event2/event/proc/announce() + +// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE. +// Note that events that have `should_start()` return TRUE at the start will never have this proc called. +/datum/event2/event/proc/wait_tick() + +// Called every tick from the GM system, and determines if the event should offically start. +// Override this for special logic on when it should start. +/datum/event2/event/proc/should_start() + if(!time_to_start) + return TRUE + return time_to_start <= world.time + +// Override this for code to do the actual event. +/datum/event2/event/proc/start() + + +// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE. +// Note that events that have `should_end()` return TRUE at the start will never have this proc called. +/datum/event2/event/proc/event_tick() + + +// Called every tick from the GM system, and determines if the event should end. +// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called. +// Override this for special logic on when it should end, e.g. blob core has to die before event ends. +/datum/event2/event/proc/should_end() + if(!time_to_end) + return TRUE + return time_to_end <= world.time + +// Override this for code to run when the event is over, e.g. cleanup. +/datum/event2/event/proc/end() diff --git a/code/modules/gamemaster/event2/events/cargo/shipping_error.dm b/code/modules/gamemaster/event2/events/cargo/shipping_error.dm index eaab32cb8a5..34d97dcccdb 100644 --- a/code/modules/gamemaster/event2/events/cargo/shipping_error.dm +++ b/code/modules/gamemaster/event2/events/cargo/shipping_error.dm @@ -1,16 +1,16 @@ -/datum/event2/meta/shipping_error - name = "shipping error" - departments = list(DEPARTMENT_CARGO) - chaos = -10 // A helpful event. - reusable = TRUE - event_type = /datum/event2/event/shipping_error - -/datum/event2/meta/shipping_error/get_weight() - return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30 - -/datum/event2/event/shipping_error/start() - var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = SSsupply.ordernum - O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)] - O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human") - SSsupply.shoppinglist += O +/datum/event2/meta/shipping_error + name = "shipping error" + departments = list(DEPARTMENT_CARGO) + chaos = -10 // A helpful event. + reusable = TRUE + event_type = /datum/event2/event/shipping_error + +/datum/event2/meta/shipping_error/get_weight() + return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30 + +/datum/event2/event/shipping_error/start() + var/datum/supply_order/O = new /datum/supply_order() + O.ordernum = SSsupply.ordernum + O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)] + O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human") + SSsupply.shoppinglist += O diff --git a/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm b/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm index 0a19ec7a655..9c41235666a 100644 --- a/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm +++ b/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm @@ -1,61 +1,61 @@ -/datum/event2/meta/manifest_malfunction - name = "manifest_malfunction" - departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/manifest_malfunction - -/datum/event2/meta/manifest_malfunction/get_weight() - var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) - - if(!security || !data_core) - return 0 - - var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) - var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice. - - return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5) - - - -/datum/event2/event/manifest_malfunction - announce_delay_lower_bound = 5 MINUTES - announce_delay_upper_bound = 10 MINUTES - var/records_to_delete = 2 - var/record_class_to_delete = null - -/datum/event2/event/manifest_malfunction/set_up() - record_class_to_delete = pickweight(list("medical" = 10, "security" = 30)) - -/datum/event2/event/manifest_malfunction/announce() - if(prob(30)) - var/message = null - var/author = null - var/rng = rand(1, 2) - switch(rng) - if(1) - author = "Data Breach Alert" - message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \ - They appear to have wiped several records, before disconnecting." - if(2) - author = "Downtime Alert" - message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \ - A temporary replacement server has been activated, containing recovered data from the main server. \ - A few records became corrupted, and could not be transferred." - command_announcement.Announce(message, author) - -/datum/event2/event/manifest_malfunction/start() - for(var/i = 1 to records_to_delete) - var/datum/data/record/R - - switch(record_class_to_delete) - if("security") - R = safepick(data_core.security) - - if("medical") - R = safepick(data_core.medical) - - if(R) - log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.") - qdel(R) +/datum/event2/meta/manifest_malfunction + name = "manifest_malfunction" + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/manifest_malfunction + +/datum/event2/meta/manifest_malfunction/get_weight() + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + + if(!security || !data_core) + return 0 + + var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) + var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice. + + return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5) + + + +/datum/event2/event/manifest_malfunction + announce_delay_lower_bound = 5 MINUTES + announce_delay_upper_bound = 10 MINUTES + var/records_to_delete = 2 + var/record_class_to_delete = null + +/datum/event2/event/manifest_malfunction/set_up() + record_class_to_delete = pickweight(list("medical" = 10, "security" = 30)) + +/datum/event2/event/manifest_malfunction/announce() + if(prob(30)) + var/message = null + var/author = null + var/rng = rand(1, 2) + switch(rng) + if(1) + author = "Data Breach Alert" + message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \ + They appear to have wiped several records, before disconnecting." + if(2) + author = "Downtime Alert" + message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \ + A temporary replacement server has been activated, containing recovered data from the main server. \ + A few records became corrupted, and could not be transferred." + command_announcement.Announce(message, author) + +/datum/event2/event/manifest_malfunction/start() + for(var/i = 1 to records_to_delete) + var/datum/data/record/R + + switch(record_class_to_delete) + if("security") + R = safepick(data_core.security) + + if("medical") + R = safepick(data_core.medical) + + if(R) + log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.") + qdel(R) diff --git a/code/modules/gamemaster/event2/events/command/money_hacker.dm b/code/modules/gamemaster/event2/events/command/money_hacker.dm index a1dbd1e18b3..287844cf2df 100644 --- a/code/modules/gamemaster/event2/events/command/money_hacker.dm +++ b/code/modules/gamemaster/event2/events/command/money_hacker.dm @@ -1,110 +1,110 @@ -/datum/event2/meta/money_hacker - name = "money hacker" - departments = list(DEPARTMENT_COMMAND) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/money_hacker - -/datum/event2/meta/money_hacker/get_weight() - var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) - - if(!command) - return 0 - return 30 + (command * 20) + (all_money_accounts.len * 5) - - - -/datum/event2/event/money_hacker - length_lower_bound = 8 MINUTES - length_upper_bound = 12 MINUTES - var/datum/money_account/targeted_account = null - -/datum/event2/event/money_hacker/set_up() - if(LAZYLEN(all_money_accounts)) - targeted_account = pick(all_money_accounts) - - if(!targeted_account) - log_debug("Money hacker event could not find an account to hack. Aborting.") - abort() - return - -/datum/event2/event/money_hacker/announce() - var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \ - without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ - Notifications will be sent as updates occur." - var/my_department = "[location_name()] Firewall Subroutines" - - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) - continue - MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]
                    ", "", "", 2) - - // Nobody reads the requests consoles so lets use the radio as well. - global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) - -/datum/event2/event/money_hacker/end() - var/message = null - if(targeted_account && !targeted_account.suspended) // Hacker wins. - message = "The hack attempt has succeeded." - hack_account(targeted_account) - log_debug("Money hacker event managed to hack the targeted account.") - - else // Crew wins. - message = "The attack has ceased, the affected accounts can now be brought online." - log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.") - - var/my_department = "[location_name()] Firewall Subroutines" - - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) continue - MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) - - global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) - -/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A) - // Subtract the money. - var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10 - A.money -= lost - - // Create a taunting log entry. - var/datum/transaction/T = new() - T.target_name = pick(list( - "", - "yo brotha from anotha motha", - "el Presidente", - "chieF smackDowN", - "Nobody" - )) - - T.purpose = pick(list( - "Ne$ ---ount fu%ds init*&lisat@*n", - "PAY BACK YOUR MUM", - "Funds withdrawal", - "pWnAgE", - "l33t hax", - "liberationez", - "Hit", - "Nothing" - )) - - T.amount = pick(list( - "", - "([rand(0,99999)])", - "alla money", - "9001$", - "HOLLA HOLLA GET DOLLA", - "([lost])", - "69,420t" - )) - - var/date1 = "1 January 1970" // Unix epoch. - var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" - T.date = pick("", current_date_string, date1, date2,"Nowhen") - - var/time1 = rand(0, 99999999) - var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" - T.time = pick("", stationtime2text(), time2, "Never") - - T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") - - A.transaction_log.Add(T) +/datum/event2/meta/money_hacker + name = "money hacker" + departments = list(DEPARTMENT_COMMAND) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/money_hacker + +/datum/event2/meta/money_hacker/get_weight() + var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) + + if(!command) + return 0 + return 30 + (command * 20) + (all_money_accounts.len * 5) + + + +/datum/event2/event/money_hacker + length_lower_bound = 8 MINUTES + length_upper_bound = 12 MINUTES + var/datum/money_account/targeted_account = null + +/datum/event2/event/money_hacker/set_up() + if(LAZYLEN(all_money_accounts)) + targeted_account = pick(all_money_accounts) + + if(!targeted_account) + log_debug("Money hacker event could not find an account to hack. Aborting.") + abort() + return + +/datum/event2/event/money_hacker/announce() + var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \ + without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ + Notifications will be sent as updates occur." + var/my_department = "[location_name()] Firewall Subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) + continue + MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]
                    ", "", "", 2) + + // Nobody reads the requests consoles so lets use the radio as well. + global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) + +/datum/event2/event/money_hacker/end() + var/message = null + if(targeted_account && !targeted_account.suspended) // Hacker wins. + message = "The hack attempt has succeeded." + hack_account(targeted_account) + log_debug("Money hacker event managed to hack the targeted account.") + + else // Crew wins. + message = "The attack has ceased, the affected accounts can now be brought online." + log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.") + + var/my_department = "[location_name()] Firewall Subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) continue + MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) + + global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) + +/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A) + // Subtract the money. + var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10 + A.money -= lost + + // Create a taunting log entry. + var/datum/transaction/T = new() + T.target_name = pick(list( + "", + "yo brotha from anotha motha", + "el Presidente", + "chieF smackDowN", + "Nobody" + )) + + T.purpose = pick(list( + "Ne$ ---ount fu%ds init*&lisat@*n", + "PAY BACK YOUR MUM", + "Funds withdrawal", + "pWnAgE", + "l33t hax", + "liberationez", + "Hit", + "Nothing" + )) + + T.amount = pick(list( + "", + "([rand(0,99999)])", + "alla money", + "9001$", + "HOLLA HOLLA GET DOLLA", + "([lost])", + "69,420t" + )) + + var/date1 = "1 January 1970" // Unix epoch. + var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" + T.date = pick("", current_date_string, date1, date2,"Nowhen") + + var/time1 = rand(0, 99999999) + var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" + T.time = pick("", stationtime2text(), time2, "Never") + + T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") + + A.transaction_log.Add(T) diff --git a/code/modules/gamemaster/event2/events/command/raise_funds.dm b/code/modules/gamemaster/event2/events/command/raise_funds.dm index 31e88163501..1540d74e80b 100644 --- a/code/modules/gamemaster/event2/events/command/raise_funds.dm +++ b/code/modules/gamemaster/event2/events/command/raise_funds.dm @@ -1,96 +1,96 @@ -/datum/event2/meta/raise_funds - name = "local funding drive" - enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on. - departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/raise_funds - -/datum/event2/meta/raise_funds/get_weight() - var/command = metric.count_people_in_department(DEPARTMENT_COMMAND) - if(!command) // Need someone to read the centcom message. - return 0 - - var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO) - return (command * 20) + (cargo * 20) - - - -/datum/event2/event/raise_funds - length_lower_bound = 30 MINUTES - length_upper_bound = 45 MINUTES - var/money_at_start = 0 - -/datum/event2/event/raise_funds/announce() - var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \ - [pick("changes", "issues")], in-system stations are now advised to increase funding income." - - send_command_report("Budget Advisement", message) - -/datum/event2/event/raise_funds/start() - // Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by - // taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for - // roundstart money instead. - money_at_start = count_money() - log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.") - -/datum/event2/event/raise_funds/end() - var/money_at_end = count_money() - log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \ - to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.") - - // A number above 1 indicates money was made, while below 1 does the opposite. - var/budget_shift = money_at_end / money_at_start - - // Centcom will say different things based on if they gained or lost money. - var/message = null - switch(budget_shift) - if(0 to 0.02) // Abyssmal response. - message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \ - just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \ - discussion regarding your future employment prospects will occur.

                    \ - Your facility's current balance of requisition tokens has been revoked." - SSsupply.points = 0 - log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.") - - if(0.02 to 0.98) // Bad response. - message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \ - As such, we will be taking away some requisition tokens to cover the cost of operating your facility." - var/points_lost = round(SSsupply.points * rand(0.5, 0.8)) - SSsupply.points -= points_lost - log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.") - - if(0.98 to 1.02) // Neutral response. - message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \ - that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \ - more profitable." - log_debug("Funding Drive event ended with a neutral response.") - - if(1.02 to INFINITY) // Good response. - message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \ - Together, along with the other facilities present in the [using_map.starsys_name] system, \ - the company is expected to meet the quota.

                    \ - We will allocate additional requisition tokens for the cargo department as a reward." - - // If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed. - // Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'. - var/point_reward = rand(100, 200) - SSsupply.points += point_reward - log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.") - - send_command_report("Budget Followup", message) - - - -// Returns the sum of the station account and all the departmental accounts. -/datum/event2/event/raise_funds/proc/count_money() - . = 0 - . += station_account.money - for(var/i = 1 to SSjob.department_datums.len) - var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i]) - if(istype(account)) - . += account.money - -/datum/event2/event/raise_funds/proc/send_command_report(title, message) - post_comm_message(title, message) - to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles.")) - SEND_SOUND(world, 'sound/AI/commandreport.ogg') +/datum/event2/meta/raise_funds + name = "local funding drive" + enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on. + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/raise_funds + +/datum/event2/meta/raise_funds/get_weight() + var/command = metric.count_people_in_department(DEPARTMENT_COMMAND) + if(!command) // Need someone to read the centcom message. + return 0 + + var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO) + return (command * 20) + (cargo * 20) + + + +/datum/event2/event/raise_funds + length_lower_bound = 30 MINUTES + length_upper_bound = 45 MINUTES + var/money_at_start = 0 + +/datum/event2/event/raise_funds/announce() + var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \ + [pick("changes", "issues")], in-system stations are now advised to increase funding income." + + send_command_report("Budget Advisement", message) + +/datum/event2/event/raise_funds/start() + // Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by + // taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for + // roundstart money instead. + money_at_start = count_money() + log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.") + +/datum/event2/event/raise_funds/end() + var/money_at_end = count_money() + log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \ + to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.") + + // A number above 1 indicates money was made, while below 1 does the opposite. + var/budget_shift = money_at_end / money_at_start + + // Centcom will say different things based on if they gained or lost money. + var/message = null + switch(budget_shift) + if(0 to 0.02) // Abyssmal response. + message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \ + just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \ + discussion regarding your future employment prospects will occur.

                    \ + Your facility's current balance of requisition tokens has been revoked." + SSsupply.points = 0 + log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.") + + if(0.02 to 0.98) // Bad response. + message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \ + As such, we will be taking away some requisition tokens to cover the cost of operating your facility." + var/points_lost = round(SSsupply.points * rand(0.5, 0.8)) + SSsupply.points -= points_lost + log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.") + + if(0.98 to 1.02) // Neutral response. + message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \ + that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \ + more profitable." + log_debug("Funding Drive event ended with a neutral response.") + + if(1.02 to INFINITY) // Good response. + message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \ + Together, along with the other facilities present in the [using_map.starsys_name] system, \ + the company is expected to meet the quota.

                    \ + We will allocate additional requisition tokens for the cargo department as a reward." + + // If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed. + // Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'. + var/point_reward = rand(100, 200) + SSsupply.points += point_reward + log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.") + + send_command_report("Budget Followup", message) + + + +// Returns the sum of the station account and all the departmental accounts. +/datum/event2/event/raise_funds/proc/count_money() + . = 0 + . += station_account.money + for(var/i = 1 to SSjob.department_datums.len) + var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i]) + if(istype(account)) + . += account.money + +/datum/event2/event/raise_funds/proc/send_command_report(title, message) + post_comm_message(title, message) + to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles.")) + SEND_SOUND(world, 'sound/AI/commandreport.ogg') diff --git a/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm b/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm index c3cd5d0b243..3966d3c4785 100644 --- a/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm +++ b/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm @@ -1,105 +1,105 @@ -/datum/event2/meta/airlock_failure - event_class = "airlock failure" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL) - chaos = 15 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/airlock_failure - var/needs_medical = FALSE - -/datum/event2/meta/airlock_failure/emag - name = "airlock failure - emag" - event_type = /datum/event2/event/airlock_failure/emag - -/datum/event2/meta/airlock_failure/door_crush - name = "airlock failure - crushing" - event_type = /datum/event2/event/airlock_failure/door_crush - needs_medical = TRUE - -/datum/event2/meta/airlock_failure/shock - name = "airlock failure - shock" - chaos = 30 - event_type = /datum/event2/event/airlock_failure/shock - needs_medical = TRUE - - -/datum/event2/meta/airlock_failure/get_weight() - var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) - - // Synths are good both for fixing the doors and getting blamed for the doors zapping people. - var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - if(!engineering && !synths) // Nobody's around to fix the door. - return 0 - - // Medical might be needed for some of the more violent airlock failures. - var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL) - if(!medical && needs_medical) - return 0 - - return (engineering * 20) + (medical * 20) + (synths * 20) - - - -/datum/event2/event/airlock_failure - announce_delay_lower_bound = 20 SECONDS - announce_delay_upper_bound = 40 SECONDS - var/announce_odds = 0 - var/doors_to_break = 1 - var/list/affected_areas = list() - -/datum/event2/event/airlock_failure/emag - announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event. - doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it. - -/datum/event2/event/airlock_failure/door_crush - announce_odds = 30 - doors_to_break = 5 - -/datum/event2/event/airlock_failure/shock - announce_odds = 70 - -/datum/event2/event/airlock_failure/start() - var/list/areas = find_random_areas() - if(!LAZYLEN(areas)) - log_debug("Airlock Failure event could not find any areas. Aborting.") - abort() - return - - while(areas.len) - var/area/area = pick(areas) - areas -= area - - for(var/obj/machinery/door/airlock/door in area.contents) - if(can_break_door(door)) - addtimer(CALLBACK(src, PROC_REF(break_door), door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker. - door.visible_message(span("danger", "\The [door]'s panel sparks!")) - playsound(door, "sparks", 50, 1) - log_debug("Airlock Failure event has broken \the [door] airlock in [area].") - affected_areas |= area - doors_to_break-- - - if(doors_to_break <= 0) - return - -/datum/event2/event/airlock_failure/announce() - if(prob(announce_odds)) - command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \ - Some airlocks may require servicing by a qualified technician.", "Electrical Alert") - - -/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door) - if(istype(door, /obj/machinery/door/airlock/lift)) - return FALSE - return door.arePowerSystemsOn() - -// Override this for door busting. -/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door) - -/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door) - door.emag_act(1) - -/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door) - door.normalspeed = FALSE - door.safe = FALSE - -/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door) - door.electrify(-1) +/datum/event2/meta/airlock_failure + event_class = "airlock failure" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL) + chaos = 15 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/airlock_failure + var/needs_medical = FALSE + +/datum/event2/meta/airlock_failure/emag + name = "airlock failure - emag" + event_type = /datum/event2/event/airlock_failure/emag + +/datum/event2/meta/airlock_failure/door_crush + name = "airlock failure - crushing" + event_type = /datum/event2/event/airlock_failure/door_crush + needs_medical = TRUE + +/datum/event2/meta/airlock_failure/shock + name = "airlock failure - shock" + chaos = 30 + event_type = /datum/event2/event/airlock_failure/shock + needs_medical = TRUE + + +/datum/event2/meta/airlock_failure/get_weight() + var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + + // Synths are good both for fixing the doors and getting blamed for the doors zapping people. + var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + if(!engineering && !synths) // Nobody's around to fix the door. + return 0 + + // Medical might be needed for some of the more violent airlock failures. + var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL) + if(!medical && needs_medical) + return 0 + + return (engineering * 20) + (medical * 20) + (synths * 20) + + + +/datum/event2/event/airlock_failure + announce_delay_lower_bound = 20 SECONDS + announce_delay_upper_bound = 40 SECONDS + var/announce_odds = 0 + var/doors_to_break = 1 + var/list/affected_areas = list() + +/datum/event2/event/airlock_failure/emag + announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event. + doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it. + +/datum/event2/event/airlock_failure/door_crush + announce_odds = 30 + doors_to_break = 5 + +/datum/event2/event/airlock_failure/shock + announce_odds = 70 + +/datum/event2/event/airlock_failure/start() + var/list/areas = find_random_areas() + if(!LAZYLEN(areas)) + log_debug("Airlock Failure event could not find any areas. Aborting.") + abort() + return + + while(areas.len) + var/area/area = pick(areas) + areas -= area + + for(var/obj/machinery/door/airlock/door in area.contents) + if(can_break_door(door)) + addtimer(CALLBACK(src, PROC_REF(break_door), door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker. + door.visible_message(span("danger", "\The [door]'s panel sparks!")) + playsound(door, "sparks", 50, 1) + log_debug("Airlock Failure event has broken \the [door] airlock in [area].") + affected_areas |= area + doors_to_break-- + + if(doors_to_break <= 0) + return + +/datum/event2/event/airlock_failure/announce() + if(prob(announce_odds)) + command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \ + Some airlocks may require servicing by a qualified technician.", "Electrical Alert") + + +/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door) + if(istype(door, /obj/machinery/door/airlock/lift)) + return FALSE + return door.arePowerSystemsOn() + +// Override this for door busting. +/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door) + +/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door) + door.emag_act(1) + +/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door) + door.normalspeed = FALSE + door.safe = FALSE + +/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door) + door.electrify(-1) diff --git a/code/modules/gamemaster/event2/events/engineering/blob.dm b/code/modules/gamemaster/event2/events/engineering/blob.dm index 048c073f390..199204721ac 100644 --- a/code/modules/gamemaster/event2/events/engineering/blob.dm +++ b/code/modules/gamemaster/event2/events/engineering/blob.dm @@ -1,160 +1,160 @@ -/datum/event2/meta/blob - name = "blob" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL) - chaos = 30 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round. - event_type = /datum/event2/event/blob - // In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count. - var/required_fighters = 2 // Fighters refers to engineering OR security. - var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition. - -/datum/event2/meta/blob/hard - name = "harder blob" - chaos = 40 - event_type = /datum/event2/event/blob/hard_blob - required_fighters = 3 - -/datum/event2/meta/blob/multi_blob - name = "multi blob" - chaos = 60 - event_type = /datum/event2/event/blob/multi_blob - required_fighters = 4 - required_support = 2 - -// For bussing only. -/datum/event2/meta/blob/omni_blob - name = "omni blob" - chaos = 200 - event_type = /datum/event2/event/blob/omni_blob - enabled = FALSE - -/datum/event2/meta/blob/get_weight() - // Count the 'fighters'. - var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING) - var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY) - - if(engineers.len + security.len < required_fighters) - return 0 - - // Now count the 'support'. - var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL) - var/need_medical = FALSE - - var/list/robotics = metric.get_people_with_job(/datum/job/roboticist) - var/need_robotics = FALSE - - // Determine what kind of support might be needed. - for(var/mob/living/L in engineers|security) - if(L.isSynthetic()) - need_robotics = TRUE - else - need_medical = TRUE - - // Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist. - if(medical.len < required_support && need_medical) - return 0 - - // Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare. - // In the uncertain future, a mechanical skill system check could replace this check here. - if(robotics.len + engineers.len < required_support && need_robotics) - return 0 - - var/fighter_weight = (engineers.len + security.len) * 20 - var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each. - var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked. - - return fighter_weight + support_weight + chaos_weight - - - -/datum/event2/event/blob - announce_delay_lower_bound = 1 MINUTE - announce_delay_upper_bound = 5 MINUTES - // This could be made into a GLOB accessible list for reuse if needed. - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters, - /area/holodeck, - /area/engineering/engine_room - ) - var/list/open_turfs = list() - var/spawn_blob_type = /obj/structure/blob/core/random_medium - var/number_of_blobs = 1 - var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel. - -/datum/event2/event/blob/hard_blob - spawn_blob_type = /obj/structure/blob/core/random_hard - -/datum/event2/event/blob/multi_blob - spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring. - number_of_blobs = 2 - -// For adminbus only. -/datum/event2/event/blob/omni_blob - number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob. - -/datum/event2/event/blob/set_up() - open_turfs = find_random_turfs(5 + number_of_blobs) - - if(!open_turfs.len) - log_debug("Blob infestation event: Giving up after failure to find blob spots.") - abort() - -/datum/event2/event/blob/start() - for(var/i = 1 to number_of_blobs) - var/turf/T = pick(open_turfs) - var/obj/structure/blob/core/new_blob = new spawn_blob_type(T) - blobs += WEAKREF(new_blob) - open_turfs -= T // So we can't put two cores on the same tile if doing multiblob. - log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].") - -/datum/event2/event/blob/should_end() - for(var/datum/weakref/weakref as anything in blobs) - if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet. - return FALSE - return TRUE // Only end if all blobs die. - -// Normally this does nothing, but is useful if aborted by an admin. -/datum/event2/event/blob/end() - for(var/datum/weakref/weakref as anything in blobs) - var/obj/structure/blob/core/B = weakref.resolve() - if(istype(B)) - qdel(B) - -/datum/event2/event/blob/announce() - if(!ended) // Don't announce if the blobs die early. - var/danger_level = 0 - var/list/blob_type_names = list() - var/multiblob = FALSE - for(var/datum/weakref/weakref as anything in blobs) - var/obj/structure/blob/core/B = weakref.resolve() - if(!istype(B)) - continue - var/datum/blob_type/blob_type = B.overmind.blob_type - - blob_type_names += blob_type.name - if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present. - danger_level = blob_type.difficulty - - if(blob_type_names.len > 1) // More than one blob is harder. - danger_level += blob_type_names.len - multiblob = TRUE - - var/list/lines = list() - lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \ - aboard [location_name()]. All personnel must contain the outbreak." - - if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough. - lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]." - - if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster. - var/turf/T = open_turfs[1] - var/area/A = T.loc - lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]." - - if(danger_level >= BLOB_DIFFICULTY_SUPERHARD) - lines += "Extreme caution is advised." - - command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') +/datum/event2/meta/blob + name = "blob" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round. + event_type = /datum/event2/event/blob + // In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count. + var/required_fighters = 2 // Fighters refers to engineering OR security. + var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition. + +/datum/event2/meta/blob/hard + name = "harder blob" + chaos = 40 + event_type = /datum/event2/event/blob/hard_blob + required_fighters = 3 + +/datum/event2/meta/blob/multi_blob + name = "multi blob" + chaos = 60 + event_type = /datum/event2/event/blob/multi_blob + required_fighters = 4 + required_support = 2 + +// For bussing only. +/datum/event2/meta/blob/omni_blob + name = "omni blob" + chaos = 200 + event_type = /datum/event2/event/blob/omni_blob + enabled = FALSE + +/datum/event2/meta/blob/get_weight() + // Count the 'fighters'. + var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING) + var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY) + + if(engineers.len + security.len < required_fighters) + return 0 + + // Now count the 'support'. + var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL) + var/need_medical = FALSE + + var/list/robotics = metric.get_people_with_job(/datum/job/roboticist) + var/need_robotics = FALSE + + // Determine what kind of support might be needed. + for(var/mob/living/L in engineers|security) + if(L.isSynthetic()) + need_robotics = TRUE + else + need_medical = TRUE + + // Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist. + if(medical.len < required_support && need_medical) + return 0 + + // Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare. + // In the uncertain future, a mechanical skill system check could replace this check here. + if(robotics.len + engineers.len < required_support && need_robotics) + return 0 + + var/fighter_weight = (engineers.len + security.len) * 20 + var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each. + var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked. + + return fighter_weight + support_weight + chaos_weight + + + +/datum/event2/event/blob + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 5 MINUTES + // This could be made into a GLOB accessible list for reuse if needed. + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + var/list/open_turfs = list() + var/spawn_blob_type = /obj/structure/blob/core/random_medium + var/number_of_blobs = 1 + var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel. + +/datum/event2/event/blob/hard_blob + spawn_blob_type = /obj/structure/blob/core/random_hard + +/datum/event2/event/blob/multi_blob + spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring. + number_of_blobs = 2 + +// For adminbus only. +/datum/event2/event/blob/omni_blob + number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob. + +/datum/event2/event/blob/set_up() + open_turfs = find_random_turfs(5 + number_of_blobs) + + if(!open_turfs.len) + log_debug("Blob infestation event: Giving up after failure to find blob spots.") + abort() + +/datum/event2/event/blob/start() + for(var/i = 1 to number_of_blobs) + var/turf/T = pick(open_turfs) + var/obj/structure/blob/core/new_blob = new spawn_blob_type(T) + blobs += WEAKREF(new_blob) + open_turfs -= T // So we can't put two cores on the same tile if doing multiblob. + log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].") + +/datum/event2/event/blob/should_end() + for(var/datum/weakref/weakref as anything in blobs) + if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet. + return FALSE + return TRUE // Only end if all blobs die. + +// Normally this does nothing, but is useful if aborted by an admin. +/datum/event2/event/blob/end() + for(var/datum/weakref/weakref as anything in blobs) + var/obj/structure/blob/core/B = weakref.resolve() + if(istype(B)) + qdel(B) + +/datum/event2/event/blob/announce() + if(!ended) // Don't announce if the blobs die early. + var/danger_level = 0 + var/list/blob_type_names = list() + var/multiblob = FALSE + for(var/datum/weakref/weakref as anything in blobs) + var/obj/structure/blob/core/B = weakref.resolve() + if(!istype(B)) + continue + var/datum/blob_type/blob_type = B.overmind.blob_type + + blob_type_names += blob_type.name + if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present. + danger_level = blob_type.difficulty + + if(blob_type_names.len > 1) // More than one blob is harder. + danger_level += blob_type_names.len + multiblob = TRUE + + var/list/lines = list() + lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \ + aboard [location_name()]. All personnel must contain the outbreak." + + if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough. + lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]." + + if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster. + var/turf/T = open_turfs[1] + var/area/A = T.loc + lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]." + + if(danger_level >= BLOB_DIFFICULTY_SUPERHARD) + lines += "Extreme caution is advised." + + command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') diff --git a/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm b/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm index d3478bb333b..b4c58fa0ac8 100644 --- a/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm +++ b/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm @@ -1,90 +1,90 @@ -/datum/event2/meta/brand_intelligence - name = "vending machine malware" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/brand_intelligence - -/datum/event2/meta/brand_intelligence/get_weight() - return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) - - - -/datum/event2/event/brand_intelligence - var/malware_spread_cooldown = 30 SECONDS - - var/list/vending_machines = list() // List of venders that can potentially be infected. - var/list/infected_vending_machines = list() // List of venders that have been infected. - var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured. - var/last_malware_spread_time = null - -/datum/event2/event/brand_intelligence/set_up() - for(var/obj/machinery/vending/V in machines) - if(!(V.z in using_map.station_levels)) - continue - vending_machines += V - - if(!vending_machines.len) - log_debug("Could not find any vending machines on station Z levels. Aborting.") - abort() - return - - vender_zero = pick(vending_machines) - -/datum/event2/event/brand_intelligence/announce() - if(prob(90)) - command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard \the [location_name()], \ - which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vendor Service Alert") - -/datum/event2/event/brand_intelligence/start() - infect_vender(vender_zero) - -/datum/event2/event/brand_intelligence/event_tick() - if(last_malware_spread_time + malware_spread_cooldown > world.time) - return // Still on cooldown. - last_malware_spread_time = world.time - - if(vending_machines.len) - var/next_victim = pick(vending_machines) - infect_vender(next_victim) - - // Every time Vender Zero infects, it says something. - vender_zero.speak(pick("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obsession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) - - -/datum/event2/event/brand_intelligence/should_end() - if(!vending_machines.len) - return TRUE - if(!can_propagate(vender_zero)) - return TRUE - return FALSE - -/datum/event2/event/brand_intelligence/end() - if(can_propagate(vender_zero)) // The crew failed and all the machines are infected! - return - // Otherwise Vender Zero was taken out in some form. - if(vender_zero) - vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \ - for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'.")) - for(var/obj/machinery/vending/vender in infected_vending_machines) - cure_vender(vender) - -/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V) - vending_machines -= V - infected_vending_machines += V - V.shut_up = FALSE - V.shoot_inventory = TRUE - -/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V) - infected_vending_machines -= V - V.shut_up = TRUE - V.shoot_inventory = FALSE - -/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V) - return V && V.shut_up == FALSE +/datum/event2/meta/brand_intelligence + name = "vending machine malware" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/brand_intelligence + +/datum/event2/meta/brand_intelligence/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + + + +/datum/event2/event/brand_intelligence + var/malware_spread_cooldown = 30 SECONDS + + var/list/vending_machines = list() // List of venders that can potentially be infected. + var/list/infected_vending_machines = list() // List of venders that have been infected. + var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured. + var/last_malware_spread_time = null + +/datum/event2/event/brand_intelligence/set_up() + for(var/obj/machinery/vending/V in machines) + if(!(V.z in using_map.station_levels)) + continue + vending_machines += V + + if(!vending_machines.len) + log_debug("Could not find any vending machines on station Z levels. Aborting.") + abort() + return + + vender_zero = pick(vending_machines) + +/datum/event2/event/brand_intelligence/announce() + if(prob(90)) + command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard \the [location_name()], \ + which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vendor Service Alert") + +/datum/event2/event/brand_intelligence/start() + infect_vender(vender_zero) + +/datum/event2/event/brand_intelligence/event_tick() + if(last_malware_spread_time + malware_spread_cooldown > world.time) + return // Still on cooldown. + last_malware_spread_time = world.time + + if(vending_machines.len) + var/next_victim = pick(vending_machines) + infect_vender(next_victim) + + // Every time Vender Zero infects, it says something. + vender_zero.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obsession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) + + +/datum/event2/event/brand_intelligence/should_end() + if(!vending_machines.len) + return TRUE + if(!can_propagate(vender_zero)) + return TRUE + return FALSE + +/datum/event2/event/brand_intelligence/end() + if(can_propagate(vender_zero)) // The crew failed and all the machines are infected! + return + // Otherwise Vender Zero was taken out in some form. + if(vender_zero) + vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \ + for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'.")) + for(var/obj/machinery/vending/vender in infected_vending_machines) + cure_vender(vender) + +/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V) + vending_machines -= V + infected_vending_machines += V + V.shut_up = FALSE + V.shoot_inventory = TRUE + +/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V) + infected_vending_machines -= V + V.shut_up = TRUE + V.shoot_inventory = FALSE + +/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V) + return V && V.shut_up == FALSE diff --git a/code/modules/gamemaster/event2/events/engineering/camera_damage.dm b/code/modules/gamemaster/event2/events/engineering/camera_damage.dm index c6c15161302..bdaedad73ba 100644 --- a/code/modules/gamemaster/event2/events/engineering/camera_damage.dm +++ b/code/modules/gamemaster/event2/events/engineering/camera_damage.dm @@ -1,41 +1,41 @@ -/datum/event2/meta/camera_damage - name = "random camera damage" - departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING) - chaos = 5 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/camera_damage - -/datum/event2/meta/camera_damage/get_weight() - return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40) - -/datum/event2/event/camera_damage - var/camera_range = 7 - -/datum/event2/event/camera_damage/start() - var/obj/machinery/camera/C = acquire_random_camera() - if(!C) - return - - for(var/obj/machinery/camera/cam in range(camera_range, C)) - if(is_valid_camera(cam)) - cam.wires.cut(WIRE_MAIN_POWER1) - if(prob(25)) - cam.wires.cut(WIRE_CAM_ALARM) - -/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) - if(!cameranet.cameras.len) - return - if(!remaining_attempts) - return - - var/obj/machinery/camera/C = pick(cameranet.cameras) - if(is_valid_camera(C)) - return C - // It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop. - return acquire_random_camera(--remaining_attempts) - -/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) - // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access - var/turf/T = get_turf(C) +/datum/event2/meta/camera_damage + name = "random camera damage" + departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING) + chaos = 5 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/camera_damage + +/datum/event2/meta/camera_damage/get_weight() + return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40) + +/datum/event2/event/camera_damage + var/camera_range = 7 + +/datum/event2/event/camera_damage/start() + var/obj/machinery/camera/C = acquire_random_camera() + if(!C) + return + + for(var/obj/machinery/camera/cam in range(camera_range, C)) + if(is_valid_camera(cam)) + cam.wires.cut(WIRE_MAIN_POWER1) + if(prob(25)) + cam.wires.cut(WIRE_CAM_ALARM) + +/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) + if(!cameranet.cameras.len) + return + if(!remaining_attempts) + return + + var/obj/machinery/camera/C = pick(cameranet.cameras) + if(is_valid_camera(C)) + return C + // It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop. + return acquire_random_camera(--remaining_attempts) + +/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access + var/turf/T = get_turf(C) return T && C?.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/engineering/canister_leak.dm b/code/modules/gamemaster/event2/events/engineering/canister_leak.dm index a48823151c6..db8eedf25c2 100644 --- a/code/modules/gamemaster/event2/events/engineering/canister_leak.dm +++ b/code/modules/gamemaster/event2/events/engineering/canister_leak.dm @@ -1,26 +1,26 @@ -// -// This event chooses a random canister on player levels and breaks it, releasing its contents! -// - -/datum/event2/meta/canister_leak - name = "canister leak" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/canister_leak - -/datum/event2/meta/canister_leak/get_weight() - return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30 - -/datum/event2/event/canister_leak/start() - // List of all non-destroyed canisters on station levels - var/list/all_canisters = list() - for(var/obj/machinery/portable_atmospherics/canister/C in machines) - if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD) - all_canisters += C - var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters) - log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.") - C.health = 0 - C.healthcheck() - +// +// This event chooses a random canister on player levels and breaks it, releasing its contents! +// + +/datum/event2/meta/canister_leak + name = "canister leak" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/canister_leak + +/datum/event2/meta/canister_leak/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30 + +/datum/event2/event/canister_leak/start() + // List of all non-destroyed canisters on station levels + var/list/all_canisters = list() + for(var/obj/machinery/portable_atmospherics/canister/C in machines) + if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD) + all_canisters += C + var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters) + log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.") + C.health = 0 + C.healthcheck() + diff --git a/code/modules/gamemaster/event2/events/engineering/dust.dm b/code/modules/gamemaster/event2/events/engineering/dust.dm index 1a26118fbc6..66bb04970df 100644 --- a/code/modules/gamemaster/event2/events/engineering/dust.dm +++ b/code/modules/gamemaster/event2/events/engineering/dust.dm @@ -1,19 +1,19 @@ -/datum/event2/meta/dust - name = "dust" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/dust - -/datum/event2/meta/dust/get_weight() - return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20 - - - -/datum/event2/event/dust/announce() - if(prob(33)) - command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].") - -/datum/event2/event/dust/start() - dust_swarm("norm") +/datum/event2/meta/dust + name = "dust" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/dust + +/datum/event2/meta/dust/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20 + + + +/datum/event2/event/dust/announce() + if(prob(33)) + command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].") + +/datum/event2/event/dust/start() + dust_swarm("norm") diff --git a/code/modules/gamemaster/event2/events/engineering/gas_leak.dm b/code/modules/gamemaster/event2/events/engineering/gas_leak.dm index b0c52d6e072..b7becd5637f 100644 --- a/code/modules/gamemaster/event2/events/engineering/gas_leak.dm +++ b/code/modules/gamemaster/event2/events/engineering/gas_leak.dm @@ -1,47 +1,47 @@ -/datum/event2/meta/gas_leak - name = "gas leak" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/gas_leak - -/datum/event2/meta/gas_leak/get_weight() - // Synthetics are counted in higher value because they can wirelessly connect to alarms. - var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 - var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30 - return (15 + engineering_factor + synthetic_factor) / (times_ran + 1) - - - -/datum/event2/event/gas_leak - var/potential_gas_choices = list("carbon_dioxide", "nitrous_oxide", "phoron", "volatile_fuel") - var/chosen_gas = null - var/turf/chosen_turf = null - -/datum/event2/event/gas_leak/set_up() - chosen_gas = pick(potential_gas_choices) - - var/list/turfs = find_random_turfs() - if(!turfs.len) - log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.") - abort() - return - chosen_turf = pick(turfs) - -/datum/event2/event/gas_leak/announce() - if(chosen_turf) - command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert") - -/datum/event2/event/gas_leak/start() - // Okay, time to actually put the gas in the room! - // TODO - Would be nice to break a waste pipe perhaps? - // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around - - // Fow now just add a bunch of it to the air - - var/datum/gas_mixture/air_contents = new - air_contents.temperature = T20C + rand(-50, 50) - air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD - chosen_turf.assume_air(air_contents) +/datum/event2/meta/gas_leak + name = "gas leak" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/gas_leak + +/datum/event2/meta/gas_leak/get_weight() + // Synthetics are counted in higher value because they can wirelessly connect to alarms. + var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30 + return (15 + engineering_factor + synthetic_factor) / (times_ran + 1) + + + +/datum/event2/event/gas_leak + var/potential_gas_choices = list("carbon_dioxide", "nitrous_oxide", "phoron", "volatile_fuel") + var/chosen_gas = null + var/turf/chosen_turf = null + +/datum/event2/event/gas_leak/set_up() + chosen_gas = pick(potential_gas_choices) + + var/list/turfs = find_random_turfs() + if(!turfs.len) + log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.") + abort() + return + chosen_turf = pick(turfs) + +/datum/event2/event/gas_leak/announce() + if(chosen_turf) + command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert") + +/datum/event2/event/gas_leak/start() + // Okay, time to actually put the gas in the room! + // TODO - Would be nice to break a waste pipe perhaps? + // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around + + // Fow now just add a bunch of it to the air + + var/datum/gas_mixture/air_contents = new + air_contents.temperature = T20C + rand(-50, 50) + air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD + chosen_turf.assume_air(air_contents) playsound(chosen_turf, 'sound/effects/smoke.ogg', 75, 1) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/engineering/grid_check.dm b/code/modules/gamemaster/event2/events/engineering/grid_check.dm index b4ae7976b6e..8c120fb62b0 100644 --- a/code/modules/gamemaster/event2/events/engineering/grid_check.dm +++ b/code/modules/gamemaster/event2/events/engineering/grid_check.dm @@ -1,50 +1,50 @@ -// New grid check event: -// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to -// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so, -// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting -// the powernet when this event fires. - -/datum/event2/meta/grid_check - name = "grid check" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/grid_check - -// Having the turbines be way over their rated limit makes grid checks more likely. -/datum/event2/meta/grid_check/proc/get_overpower() - var/highest_overpower = 0 - for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) - var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0) - if(overpower > highest_overpower) - highest_overpower = overpower - return highest_overpower - -/datum/event2/meta/grid_check/get_weight() - var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 - var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated. - return (20 + population_factor + overpower_factor) / (times_ran + 1) - - - -/datum/event2/event/grid_check - var/obj/machinery/power/generator/engine // The turbine that will send a power spike. - -/datum/event2/event/grid_check/set_up() - // Find the turbine being pushed the most. - var/obj/machinery/power/generator/most_stressed_turbine = null - for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) - if(!most_stressed_turbine) - most_stressed_turbine = turbine - else if(turbine.effective_gen > most_stressed_turbine.effective_gen) - most_stressed_turbine = turbine - engine = most_stressed_turbine - -/datum/event2/event/grid_check/start() - // This sets off a chain of events that lead to the actual grid check (or perhaps worse). - // First, the Supermatter engine makes a power spike. - if(engine) - engine.power_spike() - // After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout. - // If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code. +// New grid check event: +// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to +// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so, +// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting +// the powernet when this event fires. + +/datum/event2/meta/grid_check + name = "grid check" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/grid_check + +// Having the turbines be way over their rated limit makes grid checks more likely. +/datum/event2/meta/grid_check/proc/get_overpower() + var/highest_overpower = 0 + for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) + var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0) + if(overpower > highest_overpower) + highest_overpower = overpower + return highest_overpower + +/datum/event2/meta/grid_check/get_weight() + var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated. + return (20 + population_factor + overpower_factor) / (times_ran + 1) + + + +/datum/event2/event/grid_check + var/obj/machinery/power/generator/engine // The turbine that will send a power spike. + +/datum/event2/event/grid_check/set_up() + // Find the turbine being pushed the most. + var/obj/machinery/power/generator/most_stressed_turbine = null + for(var/obj/machinery/power/generator/turbine as anything in GLOB.all_turbines) + if(!most_stressed_turbine) + most_stressed_turbine = turbine + else if(turbine.effective_gen > most_stressed_turbine.effective_gen) + most_stressed_turbine = turbine + engine = most_stressed_turbine + +/datum/event2/event/grid_check/start() + // This sets off a chain of events that lead to the actual grid check (or perhaps worse). + // First, the Supermatter engine makes a power spike. + if(engine) + engine.power_spike() + // After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout. + // If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code. diff --git a/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm b/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm index 378c6b42fea..f0b11b7e1ea 100644 --- a/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm +++ b/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm @@ -1,83 +1,83 @@ -// This event gives the station an advance warning about meteors, so that they can prepare in various ways. - -/datum/event2/meta/meteor_defense - name = "meteor defense" - departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO) - chaos = 50 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - event_class = "meteor defense" - event_type = /datum/event2/event/meteor_defense - -/datum/event2/meta/meteor_defense/get_weight() - // Engineers count as 20. - var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) - if(engineers < 3) // There -must- be at least three engineers for this to be possible. - return 0 - - . = engineers * 20 - - // Cargo and AI/borgs count as 10. - var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm) - var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - - . += (cargo + bots) * 10 - - -/datum/event2/event/meteor_defense - start_delay_lower_bound = 10 MINUTES - start_delay_upper_bound = 15 MINUTES - var/soon_announced = FALSE - var/direction = null // Actual dir used for which side the meteors come from. - var/dir_text = null // Direction shown in the announcement. - var/list/meteor_types = null - var/waves = null // How many times to send meteors. - var/last_wave_time = null // world.time of latest wave. - var/wave_delay = 10 SECONDS - var/wave_upper_bound = 8 // Max amount of meteors per wave. - var/wave_lower_bound = 4 // Min amount. - -/datum/event2/event/meteor_defense/proc/set_meteor_types() - meteor_types = meteors_threatening.Copy() - -/datum/event2/event/meteor_defense/set_up() - direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. - waves = rand(3, 6) - switch(direction) - if(NORTH) - dir_text = "aft" // For some reason this is needed. - if(SOUTH) - dir_text = "fore" - if(EAST) - dir_text = "port" - if(WEST) - dir_text = "starboard" - set_meteor_types() - -/datum/event2/event/meteor_defense/announce() - var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]." - command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') - -/datum/event2/event/meteor_defense/wait_tick() - if(!soon_announced) - if((time_to_start - world.time) <= 5 MINUTES) - soon_announced = TRUE - var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \ - ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]." - command_announcement.Announce(announcement, "Meteor Alert - Update") - -/datum/event2/event/meteor_defense/start() - command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update") - -/datum/event2/event/meteor_defense/event_tick() - if(world.time > last_wave_time + wave_delay) - last_wave_time = world.time - waves-- - message_admins("[waves] more wave\s of meteors remain.") - // Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit. - spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction]) - -/datum/event2/event/meteor_defense/should_end() - return waves <= 0 - -/datum/event2/event/meteor_defense/end() - command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update") +// This event gives the station an advance warning about meteors, so that they can prepare in various ways. + +/datum/event2/meta/meteor_defense + name = "meteor defense" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO) + chaos = 50 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_class = "meteor defense" + event_type = /datum/event2/event/meteor_defense + +/datum/event2/meta/meteor_defense/get_weight() + // Engineers count as 20. + var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + if(engineers < 3) // There -must- be at least three engineers for this to be possible. + return 0 + + . = engineers * 20 + + // Cargo and AI/borgs count as 10. + var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm) + var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + + . += (cargo + bots) * 10 + + +/datum/event2/event/meteor_defense + start_delay_lower_bound = 10 MINUTES + start_delay_upper_bound = 15 MINUTES + var/soon_announced = FALSE + var/direction = null // Actual dir used for which side the meteors come from. + var/dir_text = null // Direction shown in the announcement. + var/list/meteor_types = null + var/waves = null // How many times to send meteors. + var/last_wave_time = null // world.time of latest wave. + var/wave_delay = 10 SECONDS + var/wave_upper_bound = 8 // Max amount of meteors per wave. + var/wave_lower_bound = 4 // Min amount. + +/datum/event2/event/meteor_defense/proc/set_meteor_types() + meteor_types = meteors_threatening.Copy() + +/datum/event2/event/meteor_defense/set_up() + direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. + waves = rand(3, 6) + switch(direction) + if(NORTH) + dir_text = "aft" // For some reason this is needed. + if(SOUTH) + dir_text = "fore" + if(EAST) + dir_text = "port" + if(WEST) + dir_text = "starboard" + set_meteor_types() + +/datum/event2/event/meteor_defense/announce() + var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]." + command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') + +/datum/event2/event/meteor_defense/wait_tick() + if(!soon_announced) + if((time_to_start - world.time) <= 5 MINUTES) + soon_announced = TRUE + var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \ + ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]." + command_announcement.Announce(announcement, "Meteor Alert - Update") + +/datum/event2/event/meteor_defense/start() + command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update") + +/datum/event2/event/meteor_defense/event_tick() + if(world.time > last_wave_time + wave_delay) + last_wave_time = world.time + waves-- + message_admins("[waves] more wave\s of meteors remain.") + // Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit. + spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction]) + +/datum/event2/event/meteor_defense/should_end() + return waves <= 0 + +/datum/event2/event/meteor_defense/end() + command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update") diff --git a/code/modules/gamemaster/event2/events/engineering/spacevine.dm b/code/modules/gamemaster/event2/events/engineering/spacevine.dm index 6620e67a0c7..53fc9fd5bb0 100644 --- a/code/modules/gamemaster/event2/events/engineering/spacevine.dm +++ b/code/modules/gamemaster/event2/events/engineering/spacevine.dm @@ -1,17 +1,17 @@ -/datum/event2/meta/spacevine - name = "space-vine infestation" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/spacevine - -/datum/event2/meta/spacevine/get_weight() - return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) - - - -/datum/event2/event/spacevine/announce() - level_seven_announcement() - -/datum/event2/event/spacevine/start() - spacevine_infestation() +/datum/event2/meta/spacevine + name = "space-vine infestation" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/spacevine + +/datum/event2/meta/spacevine/get_weight() + return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) + + + +/datum/event2/event/spacevine/announce() + level_seven_announcement() + +/datum/event2/event/spacevine/start() + spacevine_infestation() diff --git a/code/modules/gamemaster/event2/events/engineering/wallrot.dm b/code/modules/gamemaster/event2/events/engineering/wallrot.dm index 29d12c85247..9542c12d38f 100644 --- a/code/modules/gamemaster/event2/events/engineering/wallrot.dm +++ b/code/modules/gamemaster/event2/events/engineering/wallrot.dm @@ -1,46 +1,46 @@ -/datum/event2/meta/wallrot - name = "wall-rot" - departments = list(DEPARTMENT_ENGINEERING) - reusable = TRUE - event_type = /datum/event2/event/wallrot - -/datum/event2/meta/wallrot/get_weight() - return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1) - - - - -/datum/event2/event/wallrot - var/turf/simulated/wall/origin = null - -/datum/event2/event/wallrot/set_up() - for(var/i = 1 to 100) - var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) ) - if(istype(candidate, /turf/simulated/wall)) - origin = candidate - log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.") - return - - log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.") - abort() - -/datum/event2/event/wallrot/announce() - if(origin && prob(80)) - command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \ - Station structural integrity may be compromised.", "Biohazard Alert") - -/datum/event2/event/wallrot/start() - if(origin) - origin.rot() - - var/rot_count = 0 - var/target_rot = rand(5, 20) - for(var/turf/simulated/wall/W in range(7, origin)) - if(prob(50)) - if(W.rot()) - rot_count++ - if(rot_count >= target_rot) - break - - - +/datum/event2/meta/wallrot + name = "wall-rot" + departments = list(DEPARTMENT_ENGINEERING) + reusable = TRUE + event_type = /datum/event2/event/wallrot + +/datum/event2/meta/wallrot/get_weight() + return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1) + + + + +/datum/event2/event/wallrot + var/turf/simulated/wall/origin = null + +/datum/event2/event/wallrot/set_up() + for(var/i = 1 to 100) + var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) ) + if(istype(candidate, /turf/simulated/wall)) + origin = candidate + log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.") + return + + log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.") + abort() + +/datum/event2/event/wallrot/announce() + if(origin && prob(80)) + command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \ + Station structural integrity may be compromised.", "Biohazard Alert") + +/datum/event2/event/wallrot/start() + if(origin) + origin.rot() + + var/rot_count = 0 + var/target_rot = rand(5, 20) + for(var/turf/simulated/wall/W in range(7, origin)) + if(prob(50)) + if(W.rot()) + rot_count++ + if(rot_count >= target_rot) + break + + + diff --git a/code/modules/gamemaster/event2/events/engineering/window_break.dm b/code/modules/gamemaster/event2/events/engineering/window_break.dm index 7aea410d45d..6b9d153a1eb 100644 --- a/code/modules/gamemaster/event2/events/engineering/window_break.dm +++ b/code/modules/gamemaster/event2/events/engineering/window_break.dm @@ -1,148 +1,148 @@ -// This event causes a random window near space to become damaged. -// If that window is not fixed in a certain amount of time, -// that window and nearby windows will shatter, causing a breach. - -/datum/event2/meta/window_break - name = "window break" - departments = list(DEPARTMENT_ENGINEERING) - chaos = 10 - reusable = TRUE - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/window_break - -/datum/event2/meta/window_break/get_weight() - return (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) / (times_ran + 1) - - - -/datum/event2/event/window_break - announce_delay_lower_bound = 10 SECONDS - announce_delay_upper_bound = 20 SECONDS - length_lower_bound = 8 MINUTES - length_upper_bound = 12 MINUTES - var/turf/chosen_turf_with_windows = null - var/obj/structure/window/chosen_window = null - var/list/collateral_windows = list() - -/datum/event2/event/window_break/set_up() - var/list/areas = find_random_areas() - if(!LAZYLEN(areas)) - log_debug("Window Break event could not find any areas. Aborting.") - abort() - return - - while(areas.len) - var/area/area = pick(areas) - areas -= area - - for(var/obj/structure/window/W in area.contents) - if(!is_window_to_space(W)) - continue - chosen_turf_with_windows = get_turf(W) - collateral_windows = gather_collateral_windows(W) - break // Break out of the inner loop. - - if(chosen_turf_with_windows) - log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].") - break // Then the outer loop. - - if(!chosen_turf_with_windows) - log_debug("Window Break event could not find a turf with valid windows to break. Aborting.") - abort() - return - -/datum/event2/event/window_break/announce() - if(chosen_window) - command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \ - Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert") - -/datum/event2/event/window_break/start() - if(!chosen_turf_with_windows) - return - - for(var/obj/structure/window/W in chosen_turf_with_windows.contents) - if(W.is_fulltile()) // Full tile windows are simple and can always be used. - chosen_window = W - break - else // Otherwise we only want the window that is on the inside side of the station. - var/turf/T = get_step(W, W.dir) - if(T.is_space()) - continue - if(T.check_density()) - continue - chosen_window = W - break - - if(!chosen_window) - return - - chosen_window.take_damage(chosen_window.maxhealth * 0.8) - playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1) - chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!")) - -/datum/event2/event/window_break/should_end() - . = ..() - if(!.) // If the timer didn't expire, we can still end it early if someone messes up. - if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth) - // If the window got deconstructed/moved/etc, immediately end and make the breach happen. - // Also end early if it was repaired. - return TRUE - -/datum/event2/event/window_break/end() - // If someone fixed the window, then everything is fine. - if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth) - log_debug("Window Break event ended with window repaired.") - return - - // Otherwise a bunch of windows shatter. - chosen_window?.shatter() - - var/windows_to_shatter = min(rand(4, 10), collateral_windows.len) - for(var/i = 1 to windows_to_shatter) - var/obj/structure/window/W = collateral_windows[i] - W?.shatter() - - log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.") - -// Checks if a window is adjacent to a space tile, and also that the opposite direction is open. -// This is done to avoid getting caught in corner parts of windows. -/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W) - for(var/direction in GLOB.cardinal) - var/turf/T = get_step(W, direction) - if(T.is_space()) - var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction]) - if(!opposite_T.check_density()) - return TRUE - return FALSE - -//TL;DR: breadth first search for all connected turfs with windows -/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) - var/list/turf/frontier_set = list(target_window.loc) - var/list/obj/structure/window/result_set = list() - var/list/turf/explored_set = list() - - while(frontier_set.len > 0) - var/turf/current = frontier_set[1] - frontier_set -= current - explored_set += current - - var/contains_windows = 0 - for(var/obj/structure/window/to_add in current.contents) - contains_windows = 1 - result_set += to_add - - if(contains_windows) - //add adjacent turfs to be checked for windows as well - var/turf/neighbor = locate(current.x + 1, current.y, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x - 1, current.y, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x, current.y + 1, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x, current.y - 1, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - return result_set +// This event causes a random window near space to become damaged. +// If that window is not fixed in a certain amount of time, +// that window and nearby windows will shatter, causing a breach. + +/datum/event2/meta/window_break + name = "window break" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + reusable = TRUE + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/window_break + +/datum/event2/meta/window_break/get_weight() + return (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) / (times_ran + 1) + + + +/datum/event2/event/window_break + announce_delay_lower_bound = 10 SECONDS + announce_delay_upper_bound = 20 SECONDS + length_lower_bound = 8 MINUTES + length_upper_bound = 12 MINUTES + var/turf/chosen_turf_with_windows = null + var/obj/structure/window/chosen_window = null + var/list/collateral_windows = list() + +/datum/event2/event/window_break/set_up() + var/list/areas = find_random_areas() + if(!LAZYLEN(areas)) + log_debug("Window Break event could not find any areas. Aborting.") + abort() + return + + while(areas.len) + var/area/area = pick(areas) + areas -= area + + for(var/obj/structure/window/W in area.contents) + if(!is_window_to_space(W)) + continue + chosen_turf_with_windows = get_turf(W) + collateral_windows = gather_collateral_windows(W) + break // Break out of the inner loop. + + if(chosen_turf_with_windows) + log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].") + break // Then the outer loop. + + if(!chosen_turf_with_windows) + log_debug("Window Break event could not find a turf with valid windows to break. Aborting.") + abort() + return + +/datum/event2/event/window_break/announce() + if(chosen_window) + command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \ + Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert") + +/datum/event2/event/window_break/start() + if(!chosen_turf_with_windows) + return + + for(var/obj/structure/window/W in chosen_turf_with_windows.contents) + if(W.is_fulltile()) // Full tile windows are simple and can always be used. + chosen_window = W + break + else // Otherwise we only want the window that is on the inside side of the station. + var/turf/T = get_step(W, W.dir) + if(T.is_space()) + continue + if(T.check_density()) + continue + chosen_window = W + break + + if(!chosen_window) + return + + chosen_window.take_damage(chosen_window.maxhealth * 0.8) + playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1) + chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!")) + +/datum/event2/event/window_break/should_end() + . = ..() + if(!.) // If the timer didn't expire, we can still end it early if someone messes up. + if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth) + // If the window got deconstructed/moved/etc, immediately end and make the breach happen. + // Also end early if it was repaired. + return TRUE + +/datum/event2/event/window_break/end() + // If someone fixed the window, then everything is fine. + if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth) + log_debug("Window Break event ended with window repaired.") + return + + // Otherwise a bunch of windows shatter. + chosen_window?.shatter() + + var/windows_to_shatter = min(rand(4, 10), collateral_windows.len) + for(var/i = 1 to windows_to_shatter) + var/obj/structure/window/W = collateral_windows[i] + W?.shatter() + + log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.") + +// Checks if a window is adjacent to a space tile, and also that the opposite direction is open. +// This is done to avoid getting caught in corner parts of windows. +/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W) + for(var/direction in GLOB.cardinal) + var/turf/T = get_step(W, direction) + if(T.is_space()) + var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction]) + if(!opposite_T.check_density()) + return TRUE + return FALSE + +//TL;DR: breadth first search for all connected turfs with windows +/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) + var/list/turf/frontier_set = list(target_window.loc) + var/list/obj/structure/window/result_set = list() + var/list/turf/explored_set = list() + + while(frontier_set.len > 0) + var/turf/current = frontier_set[1] + frontier_set -= current + explored_set += current + + var/contains_windows = 0 + for(var/obj/structure/window/to_add in current.contents) + contains_windows = 1 + result_set += to_add + + if(contains_windows) + //add adjacent turfs to be checked for windows as well + var/turf/neighbor = locate(current.x + 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x - 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y + 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y - 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + return result_set diff --git a/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm b/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm index 6da19c32ccd..d3d60d48761 100644 --- a/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm +++ b/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm @@ -1,43 +1,43 @@ -/datum/event2/meta/comms_blackout - name = "communications blackout" - departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now. - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - reusable = TRUE - event_type = /datum/event2/event/comms_blackout - -/datum/event2/meta/comms_blackout/get_weight() - return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 - - - -/datum/event2/event/comms_blackout/announce() - var/alert = pick("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ - "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ - "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ - "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ - "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ - "#4nd%;f4y6,>£%-BZZZZZZZT") - if(prob(33)) - command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg') - // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, "
                    ") - to_chat(A, "[alert]") - to_chat(A, "
                    ") - -/datum/event2/event/comms_blackout/start() - if(prob(50)) - // One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence. - log_debug("Doing partial outage of telecomms.") - for(var/obj/machinery/telecomms/processor/P in telecomms_list) - P.emp_act(1) - else - // Otherwise just shut everything down, madagascar style. - log_debug("Doing complete outage of telecomms.") - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) - - // Communicators go down no matter what. - for(var/obj/machinery/exonet_node/N in machines) - N.emp_act(1) +/datum/event2/meta/comms_blackout + name = "communications blackout" + departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now. + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + reusable = TRUE + event_type = /datum/event2/event/comms_blackout + +/datum/event2/meta/comms_blackout/get_weight() + return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 + + + +/datum/event2/event/comms_blackout/announce() + var/alert = pick("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ + "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ + "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ + "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ + "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ + "#4nd%;f4y6,>£%-BZZZZZZZT") + if(prob(33)) + command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg') + // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "
                    ") + to_chat(A, "[alert]") + to_chat(A, "
                    ") + +/datum/event2/event/comms_blackout/start() + if(prob(50)) + // One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence. + log_debug("Doing partial outage of telecomms.") + for(var/obj/machinery/telecomms/processor/P in telecomms_list) + P.emp_act(1) + else + // Otherwise just shut everything down, madagascar style. + log_debug("Doing complete outage of telecomms.") + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) + + // Communicators go down no matter what. + for(var/obj/machinery/exonet_node/N in machines) + N.emp_act(1) diff --git a/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm b/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm index 0611e1b290e..1392c0bb2ba 100644 --- a/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm +++ b/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm @@ -1,99 +1,99 @@ -// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time. -// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers. -// Since this is an 'external' thing, the Grid Checker can't stop it. - -/datum/event2/meta/electrical_fault - name = "electrical fault" - departments = list(DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/electrical_fault - -/datum/event2/meta/electrical_fault/get_weight() - return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) - - -/datum/event2/event/electrical_fault - start_delay_lower_bound = 30 SECONDS - start_delay_upper_bound = 1 MINUTE - length_lower_bound = 20 SECONDS - length_upper_bound = 40 SECONDS - var/max_apcs_per_tick = 6 - - var/list/valid_apcs = null - var/list/valid_z_levels = null - - var/apcs_disabled = 0 - var/apcs_overloaded = 0 - var/apcs_emagged = 0 - -/datum/event2/event/electrical_fault/announce() - // Trying to be vague to avoid 'space lightning storms'. - // This could be re-flavored to be a solar flare or something and have robots outside be sad. - command_announcement.Announce("External conditions near \the [location_name()] are likely \ - to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array") - -/datum/event2/event/electrical_fault/set_up() - valid_z_levels = get_location_z_levels() - valid_z_levels -= using_map.sealed_levels // Space levels only please! - - valid_apcs = list() - for(var/obj/machinery/power/apc/A in GLOB.apcs) - if(A.z in valid_z_levels) - valid_apcs += A - -/datum/event2/event/electrical_fault/start() - command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring") - -/datum/event2/event/electrical_fault/event_tick() - if(!valid_apcs.len) - log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.") - abort() - return - - var/list/picked_apcs = list() - for(var/i = 1 to max_apcs_per_tick) - picked_apcs |= pick(valid_apcs) - - for(var/A in picked_apcs) - affect_apc(A) - -/datum/event2/event/electrical_fault/end() - command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring") - log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \ - [apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.") - -/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A) - // Main breaker is turned off or is Special(tm). Consider it protected. - // Important APCs like the AI or the engine core shouldn't get shut off by this event. - if((!A.operating || A.failure_timer > 0) || A.is_critical) - return - - // In reality this would probably make the lights get brighter but oh well. - for(var/obj/machinery/light/L in get_area(A)) - L.flicker(rand(10, 20)) - - // Chance to make the APC turn off for awhile. - // This will actually protect it from further damage. - if(prob(25)) - A.energy_fail(rand(60, 120)) -// log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.") - playsound(A, 'sound/machines/defib_success.ogg', 50, 1) - apcs_disabled++ - return - - // Decent chance to overload lighting circuit. - if(prob(30)) - A.overload_lighting() -// log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.") - playsound(A, 'sound/effects/lightningshock.ogg', 50, 1) - apcs_overloaded++ - - // Relatively small chance to emag the apc as apc_damage event does. - if(prob(5)) - A.emagged = TRUE - A.update_icon() -// log_debug("ELECTRICAL EVENT: Emagged \the [A].") - playsound(A, 'sound/machines/chime.ogg', 50, 1) - apcs_emagged++ - +// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time. +// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers. +// Since this is an 'external' thing, the Grid Checker can't stop it. + +/datum/event2/meta/electrical_fault + name = "electrical fault" + departments = list(DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/electrical_fault + +/datum/event2/meta/electrical_fault/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) + + +/datum/event2/event/electrical_fault + start_delay_lower_bound = 30 SECONDS + start_delay_upper_bound = 1 MINUTE + length_lower_bound = 20 SECONDS + length_upper_bound = 40 SECONDS + var/max_apcs_per_tick = 6 + + var/list/valid_apcs = null + var/list/valid_z_levels = null + + var/apcs_disabled = 0 + var/apcs_overloaded = 0 + var/apcs_emagged = 0 + +/datum/event2/event/electrical_fault/announce() + // Trying to be vague to avoid 'space lightning storms'. + // This could be re-flavored to be a solar flare or something and have robots outside be sad. + command_announcement.Announce("External conditions near \the [location_name()] are likely \ + to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array") + +/datum/event2/event/electrical_fault/set_up() + valid_z_levels = get_location_z_levels() + valid_z_levels -= using_map.sealed_levels // Space levels only please! + + valid_apcs = list() + for(var/obj/machinery/power/apc/A in GLOB.apcs) + if(A.z in valid_z_levels) + valid_apcs += A + +/datum/event2/event/electrical_fault/start() + command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring") + +/datum/event2/event/electrical_fault/event_tick() + if(!valid_apcs.len) + log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.") + abort() + return + + var/list/picked_apcs = list() + for(var/i = 1 to max_apcs_per_tick) + picked_apcs |= pick(valid_apcs) + + for(var/A in picked_apcs) + affect_apc(A) + +/datum/event2/event/electrical_fault/end() + command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring") + log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \ + [apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.") + +/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A) + // Main breaker is turned off or is Special(tm). Consider it protected. + // Important APCs like the AI or the engine core shouldn't get shut off by this event. + if((!A.operating || A.failure_timer > 0) || A.is_critical) + return + + // In reality this would probably make the lights get brighter but oh well. + for(var/obj/machinery/light/L in get_area(A)) + L.flicker(rand(10, 20)) + + // Chance to make the APC turn off for awhile. + // This will actually protect it from further damage. + if(prob(25)) + A.energy_fail(rand(60, 120)) +// log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.") + playsound(A, 'sound/machines/defib_success.ogg', 50, 1) + apcs_disabled++ + return + + // Decent chance to overload lighting circuit. + if(prob(30)) + A.overload_lighting() +// log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.") + playsound(A, 'sound/effects/lightningshock.ogg', 50, 1) + apcs_overloaded++ + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(5)) + A.emagged = TRUE + A.update_icon() +// log_debug("ELECTRICAL EVENT: Emagged \the [A].") + playsound(A, 'sound/machines/chime.ogg', 50, 1) + apcs_emagged++ + diff --git a/code/modules/gamemaster/event2/events/everyone/gravity.dm b/code/modules/gamemaster/event2/events/everyone/gravity.dm index 5426698a22c..7c423aeeb2a 100644 --- a/code/modules/gamemaster/event2/events/everyone/gravity.dm +++ b/code/modules/gamemaster/event2/events/everyone/gravity.dm @@ -1,34 +1,34 @@ -/datum/event2/meta/gravity - name = "gravity failure" - departments = list(DEPARTMENT_EVERYONE) - chaos = 20 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - reusable = TRUE - event_type = /datum/event2/event/gravity - -/datum/event2/meta/gravity/get_weight() - return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)) / (times_ran + 1) - - - - -/datum/event2/event/gravity - length_lower_bound = 4 MINUTES - length_upper_bound = 8 MINUTES - -/datum/event2/event/gravity/announce() - command_announcement.Announce("Feedback surge detected in mass-distributions systems. \ - Artificial gravity has been disabled whilst the system reinitializes. \ - Please stand by while the gravity system reinitializes.", "Gravity Failure") - -/datum/event2/event/gravity/start() - for(var/area/A in world) - if(A.z in get_location_z_levels(space_only = TRUE)) - A.gravitychange(FALSE) - -/datum/event2/event/gravity/end() - for(var/area/A in world) - if(A.z in get_location_z_levels(space_only = TRUE)) - A.gravitychange(TRUE) - +/datum/event2/meta/gravity + name = "gravity failure" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/gravity + +/datum/event2/meta/gravity/get_weight() + return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5)) / (times_ran + 1) + + + + +/datum/event2/event/gravity + length_lower_bound = 4 MINUTES + length_upper_bound = 8 MINUTES + +/datum/event2/event/gravity/announce() + command_announcement.Announce("Feedback surge detected in mass-distributions systems. \ + Artificial gravity has been disabled whilst the system reinitializes. \ + Please stand by while the gravity system reinitializes.", "Gravity Failure") + +/datum/event2/event/gravity/start() + for(var/area/A in world) + if(A.z in get_location_z_levels(space_only = TRUE)) + A.gravitychange(FALSE) + +/datum/event2/event/gravity/end() + for(var/area/A in world) + if(A.z in get_location_z_levels(space_only = TRUE)) + A.gravitychange(TRUE) + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/everyone/infestation.dm b/code/modules/gamemaster/event2/events/everyone/infestation.dm index fa07e60b4c2..a8da58c11ba 100644 --- a/code/modules/gamemaster/event2/events/everyone/infestation.dm +++ b/code/modules/gamemaster/event2/events/everyone/infestation.dm @@ -1,77 +1,77 @@ -/datum/event2/meta/infestation - event_class = "infestation" - departments = list(DEPARTMENT_EVERYONE) - -/datum/event2/meta/infestation/get_weight() - return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10 - -/datum/event2/meta/infestation/rodents - name = "infestation - rodents" - event_type = /datum/event2/event/infestation/rodents - -/datum/event2/meta/infestation/lizards - name = "infestation - lizards" - event_type = /datum/event2/event/infestation/lizards - -/datum/event2/meta/infestation/spiderlings - name = "infestation - spiders" - event_type = /datum/event2/event/infestation/spiderlings - -/datum/event2/event/infestation/cockroaches - vermin_string = "cockroaches" - max_vermin = 6 - things_to_spawn = list(/mob/living/simple_mob/animal/passive/cockroach) - -/datum/event2/event/infestation - var/vermin_string = null - var/max_vermin = 0 - var/list/things_to_spawn = list() - - var/list/turfs = list() - -/datum/event2/event/infestation/rodents - vermin_string = "rodents" - max_vermin = 12 - things_to_spawn = list( - /mob/living/simple_mob/animal/passive/mouse/gray, - /mob/living/simple_mob/animal/passive/mouse/brown, - /mob/living/simple_mob/animal/passive/mouse/black, - /mob/living/simple_mob/animal/passive/mouse/white, - /mob/living/simple_mob/animal/passive/mouse/rat - ) - -/datum/event2/event/infestation/lizards - vermin_string = "lizards" - max_vermin = 6 - things_to_spawn = list( - /mob/living/simple_mob/animal/passive/lizard, - /mob/living/simple_mob/animal/passive/lizard/large, - /mob/living/simple_mob/animal/passive/lizard/large/defensive - ) - -/datum/event2/event/infestation/spiderlings - vermin_string = "spiders" - max_vermin = 3 - things_to_spawn = list(/obj/effect/spider/spiderling/non_growing) - - -/datum/event2/event/infestation/set_up() - turfs = find_random_turfs(max_vermin) - if(!turfs.len) - log_debug("Infestation event failed to find any valid turfs. Aborting.") - abort() - return - -/datum/event2/event/infestation/announce() - var/turf/T = turfs[1] - command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \ - in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation") - - -/datum/event2/event/infestation/start() - var/vermin_to_spawn = rand(2, max_vermin) - for(var/i = 1 to vermin_to_spawn) - var/turf/T = pick(turfs) - turfs -= T - var/spawn_type = pick(things_to_spawn) - new spawn_type(T) +/datum/event2/meta/infestation + event_class = "infestation" + departments = list(DEPARTMENT_EVERYONE) + +/datum/event2/meta/infestation/get_weight() + return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10 + +/datum/event2/meta/infestation/rodents + name = "infestation - rodents" + event_type = /datum/event2/event/infestation/rodents + +/datum/event2/meta/infestation/lizards + name = "infestation - lizards" + event_type = /datum/event2/event/infestation/lizards + +/datum/event2/meta/infestation/spiderlings + name = "infestation - spiders" + event_type = /datum/event2/event/infestation/spiderlings + +/datum/event2/event/infestation/cockroaches + vermin_string = "cockroaches" + max_vermin = 6 + things_to_spawn = list(/mob/living/simple_mob/animal/passive/cockroach) + +/datum/event2/event/infestation + var/vermin_string = null + var/max_vermin = 0 + var/list/things_to_spawn = list() + + var/list/turfs = list() + +/datum/event2/event/infestation/rodents + vermin_string = "rodents" + max_vermin = 12 + things_to_spawn = list( + /mob/living/simple_mob/animal/passive/mouse/gray, + /mob/living/simple_mob/animal/passive/mouse/brown, + /mob/living/simple_mob/animal/passive/mouse/black, + /mob/living/simple_mob/animal/passive/mouse/white, + /mob/living/simple_mob/animal/passive/mouse/rat + ) + +/datum/event2/event/infestation/lizards + vermin_string = "lizards" + max_vermin = 6 + things_to_spawn = list( + /mob/living/simple_mob/animal/passive/lizard, + /mob/living/simple_mob/animal/passive/lizard/large, + /mob/living/simple_mob/animal/passive/lizard/large/defensive + ) + +/datum/event2/event/infestation/spiderlings + vermin_string = "spiders" + max_vermin = 3 + things_to_spawn = list(/obj/effect/spider/spiderling/non_growing) + + +/datum/event2/event/infestation/set_up() + turfs = find_random_turfs(max_vermin) + if(!turfs.len) + log_debug("Infestation event failed to find any valid turfs. Aborting.") + abort() + return + +/datum/event2/event/infestation/announce() + var/turf/T = turfs[1] + command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \ + in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation") + + +/datum/event2/event/infestation/start() + var/vermin_to_spawn = rand(2, max_vermin) + for(var/i = 1 to vermin_to_spawn) + var/turf/T = pick(turfs) + turfs -= T + var/spawn_type = pick(things_to_spawn) + new spawn_type(T) diff --git a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm index c6a1532d0ed..212087abbca 100644 --- a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm +++ b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm @@ -1,142 +1,142 @@ -/datum/event2/meta/pda_spam - name = "pda spam" - departments = list(DEPARTMENT_EVERYONE) - event_type = /datum/event2/event/pda_spam - -/datum/event2/meta/pda_spam/get_weight() - return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 - - -/datum/event2/event/pda_spam - length_lower_bound = 30 MINUTES - length_upper_bound = 1 HOUR - var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`. - var/last_spam_time = null // world.time of most recent spam. - var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam. - var/give_up_after = 5 MINUTES - var/obj/machinery/message_server/MS = null - var/obj/machinery/exonet_node/node = null - -/datum/event2/event/pda_spam/set_up() - last_spam_time = world.time // So it won't immediately give up. - MS = pick_message_server() - node = get_exonet_node() - -/datum/event2/event/pda_spam/event_tick() - if(!can_spam()) - return - - if(world.time < next_spam_attempt_time) - return - - next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES) - - var/obj/item/device/pda/P = null - var/list/viables = list() - - for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) - if (!check_pda.owner || check_pda == src || check_pda.hidden) - continue - - var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger) - if(!M || M.toff) - continue - viables += check_pda - - if(!viables.len) - return - - P = pick(viables) - var/list/spam = generate_spam() - - if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter. - return - - send_spam(P, spam[1], spam[2]) - - -/datum/event2/event/pda_spam/should_end() - . = ..() - if(!.) - // Give up if nobody was reachable for five minutes. - if(last_spam_time + give_up_after < world.time) - log_debug("PDA Spam event giving up after not being able to spam for awhile.") - return TRUE - -/datum/event2/event/pda_spam/proc/can_spam() - if(!node || !node.on || !node.allow_external_PDAs) - node = get_exonet_node() - return FALSE - - if(!MS || !MS.active) - MS = pick_message_server() - return FALSE - - return TRUE - -// Returns a list containing two items, the sender and message. -/datum/event2/event/pda_spam/proc/generate_spam() - var/sender = null - var/message = null - switch(rand(1, 7)) - if(1) - sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") - message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ - "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ - "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ - "You will be able to enjoy over 450 top-flight casino games at MaxBet.") - if(2) - sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") - message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ - "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ - "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ - "You have (1) new message!",\ - "You have (2) new profile views!") - if(3) - sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") - message = pick("Luxury watches for Blowout sale prices!",\ - "Watches, Jewelry & Accessories, Bags & Wallets !",\ - "Deposit 100$ and get 300$ totally free!",\ - " 100K NT.|WOWGOLD �nly $89 ",\ - "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ - "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") - if(4) - sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") - message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ - "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ - "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ - "Men of all species report AMAZING increases in length, width and stamina.") - if(5) - sender = pick("Dr","Crown prince","King Regent","Professor","Captain") - sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") - sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") - message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ - "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ - "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ - "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ - "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") - if(6) - sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") - message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ - "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ - "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ - "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") - if(7) - sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") - message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ - "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ - "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ - "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") - return list(sender, message) - -/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message) - last_spam_time = world.time - var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) - PM.notify("Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)", 0) - if(spam_debug) - log_debug("PDA Spam event sent spam to \the [P].") - - -/datum/event2/event/pda_spam/proc/pick_message_server() - if(LAZYLEN(message_servers)) - return pick(message_servers) +/datum/event2/meta/pda_spam + name = "pda spam" + departments = list(DEPARTMENT_EVERYONE) + event_type = /datum/event2/event/pda_spam + +/datum/event2/meta/pda_spam/get_weight() + return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 + + +/datum/event2/event/pda_spam + length_lower_bound = 30 MINUTES + length_upper_bound = 1 HOUR + var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`. + var/last_spam_time = null // world.time of most recent spam. + var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam. + var/give_up_after = 5 MINUTES + var/obj/machinery/message_server/MS = null + var/obj/machinery/exonet_node/node = null + +/datum/event2/event/pda_spam/set_up() + last_spam_time = world.time // So it won't immediately give up. + MS = pick_message_server() + node = get_exonet_node() + +/datum/event2/event/pda_spam/event_tick() + if(!can_spam()) + return + + if(world.time < next_spam_attempt_time) + return + + next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES) + + var/obj/item/device/pda/P = null + var/list/viables = list() + + for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) + if (!check_pda.owner || check_pda == src || check_pda.hidden) + continue + + var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) + continue + viables += check_pda + + if(!viables.len) + return + + P = pick(viables) + var/list/spam = generate_spam() + + if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter. + return + + send_spam(P, spam[1], spam[2]) + + +/datum/event2/event/pda_spam/should_end() + . = ..() + if(!.) + // Give up if nobody was reachable for five minutes. + if(last_spam_time + give_up_after < world.time) + log_debug("PDA Spam event giving up after not being able to spam for awhile.") + return TRUE + +/datum/event2/event/pda_spam/proc/can_spam() + if(!node || !node.on || !node.allow_external_PDAs) + node = get_exonet_node() + return FALSE + + if(!MS || !MS.active) + MS = pick_message_server() + return FALSE + + return TRUE + +// Returns a list containing two items, the sender and message. +/datum/event2/event/pda_spam/proc/generate_spam() + var/sender = null + var/message = null + switch(rand(1, 7)) + if(1) + sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") + message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ + "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ + "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ + "You will be able to enjoy over 450 top-flight casino games at MaxBet.") + if(2) + sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") + message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ + "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ + "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ + "You have (1) new message!",\ + "You have (2) new profile views!") + if(3) + sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") + message = pick("Luxury watches for Blowout sale prices!",\ + "Watches, Jewelry & Accessories, Bags & Wallets !",\ + "Deposit 100$ and get 300$ totally free!",\ + " 100K NT.|WOWGOLD �nly $89 ",\ + "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ + "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") + if(4) + sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") + message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ + "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ + "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ + "Men of all species report AMAZING increases in length, width and stamina.") + if(5) + sender = pick("Dr","Crown prince","King Regent","Professor","Captain") + sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") + sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") + message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ + "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ + "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ + "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ + "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") + if(6) + sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") + message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ + "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ + "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ + "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") + if(7) + sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") + message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ + "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ + "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ + "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") + return list(sender, message) + +/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message) + last_spam_time = world.time + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)", 0) + if(spam_debug) + log_debug("PDA Spam event sent spam to \the [P].") + + +/datum/event2/event/pda_spam/proc/pick_message_server() + if(LAZYLEN(message_servers)) + return pick(message_servers) diff --git a/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm b/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm index c81968c23e8..d5c4592b3f6 100644 --- a/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm +++ b/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm @@ -1,49 +1,49 @@ -/datum/event2/meta/radiation_storm - name = "radiation storm" - departments = list(DEPARTMENT_EVERYONE) - chaos = 20 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/radiation_storm - -/datum/event2/meta/radiation_storm/get_weight() - var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 - var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight. - return 20 + medical_factor + population_factor - - - -/datum/event2/event/radiation_storm - start_delay_lower_bound = 1 MINUTE - length_lower_bound = 1 MINUTE - -/datum/event2/event/radiation_storm/announce() - command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \ - Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - make_maint_all_access() - -/datum/event2/event/radiation_storm/start() - command_announcement.Announce("The station has entered the radiation belt. \ - Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") - -/datum/event2/event/radiation_storm/event_tick() - radiate() - -/datum/event2/event/radiation_storm/proc/radiate() - var/radiation_level = rand(15, 35) - for(var/z in using_map.station_levels) - SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) - -/datum/event2/event/radiation_storm/end() - command_announcement.Announce("The station has passed the radiation belt. \ - Please allow for up to one minute while radiation levels dissipate, and report to \ - medbay if you experience any unusual symptoms. Maintenance will lose all \ - access again shortly.", "Anomaly Alert") - addtimer(CALLBACK(src, PROC_REF(maint_callback)), 2 MINUTES) - -/datum/event2/event/radiation_storm/proc/maint_callback() - revoke_maint_all_access() - - -// There is no actual radiation during a fake storm. -/datum/event2/event/radiation_storm/fake/radiate() - return +/datum/event2/meta/radiation_storm + name = "radiation storm" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/radiation_storm + +/datum/event2/meta/radiation_storm/get_weight() + var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 + var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight. + return 20 + medical_factor + population_factor + + + +/datum/event2/event/radiation_storm + start_delay_lower_bound = 1 MINUTE + length_lower_bound = 1 MINUTE + +/datum/event2/event/radiation_storm/announce() + command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \ + Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + make_maint_all_access() + +/datum/event2/event/radiation_storm/start() + command_announcement.Announce("The station has entered the radiation belt. \ + Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + +/datum/event2/event/radiation_storm/event_tick() + radiate() + +/datum/event2/event/radiation_storm/proc/radiate() + var/radiation_level = rand(15, 35) + for(var/z in using_map.station_levels) + SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) + +/datum/event2/event/radiation_storm/end() + command_announcement.Announce("The station has passed the radiation belt. \ + Please allow for up to one minute while radiation levels dissipate, and report to \ + medbay if you experience any unusual symptoms. Maintenance will lose all \ + access again shortly.", "Anomaly Alert") + addtimer(CALLBACK(src, PROC_REF(maint_callback)), 2 MINUTES) + +/datum/event2/event/radiation_storm/proc/maint_callback() + revoke_maint_all_access() + + +// There is no actual radiation during a fake storm. +/datum/event2/event/radiation_storm/fake/radiate() + return diff --git a/code/modules/gamemaster/event2/events/everyone/random_antag.dm b/code/modules/gamemaster/event2/events/everyone/random_antag.dm index fe3b58be8c9..205c78923bb 100644 --- a/code/modules/gamemaster/event2/events/everyone/random_antag.dm +++ b/code/modules/gamemaster/event2/events/everyone/random_antag.dm @@ -1,31 +1,31 @@ -// No idea if this is needed for autotraitor or not. -// If it is, it shouldn't depend on the event system, but fixing that would be it's own project. -// If not, it can stay off until an admin wants to play with it. - -/datum/event2/meta/random_antagonist - name = "random antagonist" - enabled = FALSE - reusable = TRUE - chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not. - departments = list(DEPARTMENT_EVERYONE) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/random_antagonist - -// This has an abnormally high weight due to antags being very important for the round, -// however the weight will decay with more antags, and more attempts to add antags. -/datum/event2/meta/random_antagonist/get_weight() - var/antags = metric.count_all_antags() - return 200 / (antags + times_ran + 1) - - - -// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory. -/datum/event2/event/random_antagonist/start() - var/list/valid_types = list() - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag.flags & ANTAG_RANDSPAWN) - valid_types |= antag - if(valid_types.len) - var/datum/antagonist/antag = pick(valid_types) - antag.attempt_random_spawn() +// No idea if this is needed for autotraitor or not. +// If it is, it shouldn't depend on the event system, but fixing that would be it's own project. +// If not, it can stay off until an admin wants to play with it. + +/datum/event2/meta/random_antagonist + name = "random antagonist" + enabled = FALSE + reusable = TRUE + chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not. + departments = list(DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/random_antagonist + +// This has an abnormally high weight due to antags being very important for the round, +// however the weight will decay with more antags, and more attempts to add antags. +/datum/event2/meta/random_antagonist/get_weight() + var/antags = metric.count_all_antags() + return 200 / (antags + times_ran + 1) + + + +// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory. +/datum/event2/event/random_antagonist/start() + var/list/valid_types = list() + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag.flags & ANTAG_RANDSPAWN) + valid_types |= antag + if(valid_types.len) + var/datum/antagonist/antag = pick(valid_types) + antag.attempt_random_spawn() diff --git a/code/modules/gamemaster/event2/events/everyone/solar_storm.dm b/code/modules/gamemaster/event2/events/everyone/solar_storm.dm index bfc35440a35..809e1319117 100644 --- a/code/modules/gamemaster/event2/events/everyone/solar_storm.dm +++ b/code/modules/gamemaster/event2/events/everyone/solar_storm.dm @@ -1,52 +1,52 @@ -/datum/event2/meta/solar_storm - name = "solar storm" - reusable = TRUE - event_type = /datum/event2/event/solar_storm - -/datum/event2/meta/solar_storm/get_weight() - var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 - var/space_factor = metric.count_all_space_mobs() * 50 - return (20 + population_factor + space_factor) / (times_ran + 1) - - -/datum/event2/event/solar_storm - start_delay_lower_bound = 1 MINUTE - start_delay_upper_bound = 1 MINUTE - length_lower_bound = 2 MINUTES - length_upper_bound = 4 MINUTES - var/base_solar_gen_rate = null - -/datum/event2/event/solar_storm/announce() - command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \ - Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - adjust_solar_output(1.5) - -/datum/event2/event/solar_storm/start() - command_announcement.Announce("The solar storm has reached the station. Please refrain from EVA and remain inside the station until it has passed.", "Anomaly Alert") - adjust_solar_output(5) - -/datum/event2/event/solar_storm/event_tick() - radiate() - -/datum/event2/event/solar_storm/end() - command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \ - Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert") - adjust_solar_output(1) - -/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1) - if(isnull(base_solar_gen_rate)) - base_solar_gen_rate = GLOB.solar_gen_rate - GLOB.solar_gen_rate = mult * base_solar_gen_rate - -/datum/event2/event/solar_storm/proc/radiate() - // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. - for(var/mob/living/L in player_list) - var/turf/T = get_turf(L) - if(!T) - continue - - if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf - continue - - //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. - L.rad_act(rand(15, 30)) +/datum/event2/meta/solar_storm + name = "solar storm" + reusable = TRUE + event_type = /datum/event2/event/solar_storm + +/datum/event2/meta/solar_storm/get_weight() + var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/space_factor = metric.count_all_space_mobs() * 50 + return (20 + population_factor + space_factor) / (times_ran + 1) + + +/datum/event2/event/solar_storm + start_delay_lower_bound = 1 MINUTE + start_delay_upper_bound = 1 MINUTE + length_lower_bound = 2 MINUTES + length_upper_bound = 4 MINUTES + var/base_solar_gen_rate = null + +/datum/event2/event/solar_storm/announce() + command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \ + Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + adjust_solar_output(1.5) + +/datum/event2/event/solar_storm/start() + command_announcement.Announce("The solar storm has reached the station. Please refrain from EVA and remain inside the station until it has passed.", "Anomaly Alert") + adjust_solar_output(5) + +/datum/event2/event/solar_storm/event_tick() + radiate() + +/datum/event2/event/solar_storm/end() + command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \ + Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert") + adjust_solar_output(1) + +/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1) + if(isnull(base_solar_gen_rate)) + base_solar_gen_rate = GLOB.solar_gen_rate + GLOB.solar_gen_rate = mult * base_solar_gen_rate + +/datum/event2/event/solar_storm/proc/radiate() + // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(!T) + continue + + if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf + continue + + //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. + L.rad_act(rand(15, 30)) diff --git a/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm b/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm index 2d83209612c..4c767fbb931 100644 --- a/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm +++ b/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm @@ -1,45 +1,45 @@ -/datum/event2/meta/sudden_weather_shift - name = "sudden weather shift" - departments = list(DEPARTMENT_EVERYONE) - reusable = TRUE - event_type = /datum/event2/event/sudden_weather_shift - -/datum/event2/meta/sudden_weather_shift/get_weight() - // The proc name is a bit misleading, it only counts players outside, not all mobs. - return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1) - -/datum/event2/event/sudden_weather_shift - start_delay_lower_bound = 30 SECONDS - start_delay_upper_bound = 1 MINUTE - var/datum/planet/chosen_planet = null - -/datum/event2/event/sudden_weather_shift/set_up() - if(!LAZYLEN(SSplanets.planets)) - log_debug("Weather shift event was ran when no planets exist. Aborting.") - abort() - return - - chosen_planet = pick(SSplanets.planets) - -/datum/event2/event/sudden_weather_shift/announce() - if(!chosen_planet) - return - command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \ - sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \ - rapidly deteriorating conditions.", "Weather Alert") - -/datum/event2/event/sudden_weather_shift/start() - // Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather. - // It also makes this event generic and suitable for other planets besides the main one, with no additional code needed. - // Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it. - var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy() - var/list/new_weather_weights = list() - - // A lazy way of inverting the odds is to use some division. - for(var/weather in weather_choices) - new_weather_weights[weather] = 100 / weather_choices[weather] - - // Now choose a new weather. - var/new_weather = pickweight(new_weather_weights) - log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].") - chosen_planet.weather_holder.change_weather(new_weather) +/datum/event2/meta/sudden_weather_shift + name = "sudden weather shift" + departments = list(DEPARTMENT_EVERYONE) + reusable = TRUE + event_type = /datum/event2/event/sudden_weather_shift + +/datum/event2/meta/sudden_weather_shift/get_weight() + // The proc name is a bit misleading, it only counts players outside, not all mobs. + return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1) + +/datum/event2/event/sudden_weather_shift + start_delay_lower_bound = 30 SECONDS + start_delay_upper_bound = 1 MINUTE + var/datum/planet/chosen_planet = null + +/datum/event2/event/sudden_weather_shift/set_up() + if(!LAZYLEN(SSplanets.planets)) + log_debug("Weather shift event was ran when no planets exist. Aborting.") + abort() + return + + chosen_planet = pick(SSplanets.planets) + +/datum/event2/event/sudden_weather_shift/announce() + if(!chosen_planet) + return + command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \ + sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \ + rapidly deteriorating conditions.", "Weather Alert") + +/datum/event2/event/sudden_weather_shift/start() + // Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather. + // It also makes this event generic and suitable for other planets besides the main one, with no additional code needed. + // Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it. + var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy() + var/list/new_weather_weights = list() + + // A lazy way of inverting the odds is to use some division. + for(var/weather in weather_choices) + new_weather_weights[weather] = 100 / weather_choices[weather] + + // Now choose a new weather. + var/new_weather = pickweight(new_weather_weights) + log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].") + chosen_planet.weather_holder.change_weather(new_weather) diff --git a/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm b/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm index 4c462d06d6c..32de1e4a441 100644 --- a/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm +++ b/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm @@ -1,21 +1,21 @@ -// Generic subtype for events that make ghost pods. - -/datum/event2/event/ghost_pod_spawner - var/pod_type = null - var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs. - var/list/free_turfs = list() - -/datum/event2/event/ghost_pod_spawner/set_up() - free_turfs = find_random_turfs(5, desired_turf_areas) - - if(!free_turfs.len) - log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.") - abort() - return - -/datum/event2/event/ghost_pod_spawner/start() - var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs)) - post_pod_creation(pod) - -// Override to do things to the pod after it's spawned. +// Generic subtype for events that make ghost pods. + +/datum/event2/event/ghost_pod_spawner + var/pod_type = null + var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs. + var/list/free_turfs = list() + +/datum/event2/event/ghost_pod_spawner/set_up() + free_turfs = find_random_turfs(5, desired_turf_areas) + + if(!free_turfs.len) + log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.") + abort() + return + +/datum/event2/event/ghost_pod_spawner/start() + var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs)) + post_pod_creation(pod) + +// Override to do things to the pod after it's spawned. /datum/event2/event/ghost_pod_spawner/proc/post_pod_creation(obj/structure/ghost_pod/pod) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/legacy/legacy.dm b/code/modules/gamemaster/event2/events/legacy/legacy.dm index 6f70cafa851..736c00b8fe2 100644 --- a/code/modules/gamemaster/event2/events/legacy/legacy.dm +++ b/code/modules/gamemaster/event2/events/legacy/legacy.dm @@ -1,63 +1,63 @@ -// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system. -// It acts as a compatability layer between the old event, and the new GM system. -// This is possible because the new datum is mostly a superset of the old one. -/datum/event2/event/legacy - var/datum/event/legacy_event = null - - // Used to emulate legacy's `activeFor` tick counter. - var/tick_count = 0 - - // How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system. - var/severity = EVENT_LEVEL_MODERATE - -/datum/event2/meta/legacy/get_weight() - return 50 - -/datum/event2/event/legacy/process() - ..() - tick_count++ - -/datum/event2/event/legacy/set_up() - legacy_event = new legacy_event(null, external_use = TRUE) - legacy_event.severity = severity - legacy_event.setup() - -/datum/event2/event/legacy/should_announce() - return tick_count >= legacy_event.announceWhen - -/datum/event2/event/legacy/announce() - legacy_event.announce() - - -// Legacy events don't tick before they start, so we don't need to do `wait_tick()`. - -/datum/event2/event/legacy/should_start() - return tick_count >= legacy_event.startWhen - -/datum/event2/event/legacy/start() - legacy_event.start() - -/datum/event2/event/legacy/event_tick() - legacy_event.tick() - - -/datum/event2/event/legacy/should_end() - return tick_count >= legacy_event.endWhen - -/datum/event2/event/legacy/end() - legacy_event.end() - -/datum/event2/event/legacy/finish() - legacy_event.kill(external_use = TRUE) - ..() - -// Proof of concept. -/* -/datum/event2/meta/legacy_gravity - name = "gravity (legacy)" - reusable = TRUE - event_type = /datum/event2/event/legacy/gravity - -/datum/event2/event/legacy/gravity - legacy_event = /datum/event/gravity +// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system. +// It acts as a compatability layer between the old event, and the new GM system. +// This is possible because the new datum is mostly a superset of the old one. +/datum/event2/event/legacy + var/datum/event/legacy_event = null + + // Used to emulate legacy's `activeFor` tick counter. + var/tick_count = 0 + + // How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system. + var/severity = EVENT_LEVEL_MODERATE + +/datum/event2/meta/legacy/get_weight() + return 50 + +/datum/event2/event/legacy/process() + ..() + tick_count++ + +/datum/event2/event/legacy/set_up() + legacy_event = new legacy_event(null, external_use = TRUE) + legacy_event.severity = severity + legacy_event.setup() + +/datum/event2/event/legacy/should_announce() + return tick_count >= legacy_event.announceWhen + +/datum/event2/event/legacy/announce() + legacy_event.announce() + + +// Legacy events don't tick before they start, so we don't need to do `wait_tick()`. + +/datum/event2/event/legacy/should_start() + return tick_count >= legacy_event.startWhen + +/datum/event2/event/legacy/start() + legacy_event.start() + +/datum/event2/event/legacy/event_tick() + legacy_event.tick() + + +/datum/event2/event/legacy/should_end() + return tick_count >= legacy_event.endWhen + +/datum/event2/event/legacy/end() + legacy_event.end() + +/datum/event2/event/legacy/finish() + legacy_event.kill(external_use = TRUE) + ..() + +// Proof of concept. +/* +/datum/event2/meta/legacy_gravity + name = "gravity (legacy)" + reusable = TRUE + event_type = /datum/event2/event/legacy/gravity + +/datum/event2/event/legacy/gravity + legacy_event = /datum/event/gravity */ \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/medical/appendicitis.dm b/code/modules/gamemaster/event2/events/medical/appendicitis.dm index 27f5284020e..cf626c2cd0d 100644 --- a/code/modules/gamemaster/event2/events/medical/appendicitis.dm +++ b/code/modules/gamemaster/event2/events/medical/appendicitis.dm @@ -1,36 +1,36 @@ -/datum/event2/meta/appendicitis - name = "appendicitis" - departments = list(DEPARTMENT_MEDICAL) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/appendicitis - -/datum/event2/meta/appendicitis/get_weight() - var/list/doctors = metric.get_people_with_job(/datum/job/doctor) - - doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse) - doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) - doctors += metric.get_people_with_job(/datum/job/cmo) - - return doctors.len * 10 - - - -/datum/event2/event/appendicitis/start() - for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) - // Don't do it to SSD people. - if(!H.client) - continue - - // Or antags. - if(player_is_antag(H.mind)) - continue - - // Or doctors (otherwise it could be possible for the only surgeon to need surgery). - if(H in metric.get_people_with_job(/datum/job/doctor) ) - continue - - if(H.appendicitis()) - log_debug("Appendicitis event gave appendicitis to \the [H].") - return - log_debug("Appendicitis event could not find a valid victim.") +/datum/event2/meta/appendicitis + name = "appendicitis" + departments = list(DEPARTMENT_MEDICAL) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/appendicitis + +/datum/event2/meta/appendicitis/get_weight() + var/list/doctors = metric.get_people_with_job(/datum/job/doctor) + + doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse) + doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) + doctors += metric.get_people_with_job(/datum/job/cmo) + + return doctors.len * 10 + + + +/datum/event2/event/appendicitis/start() + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + // Don't do it to SSD people. + if(!H.client) + continue + + // Or antags / bellied. + if(player_is_antag(H.mind) || isbelly(H.loc)) + continue + + // Or doctors (otherwise it could be possible for the only surgeon to need surgery). + if(H in metric.get_people_with_job(/datum/job/doctor) ) + continue + + if(H.appendicitis()) + log_debug("Appendicitis event gave appendicitis to \the [H].") + return + log_debug("Appendicitis event could not find a valid victim.") diff --git a/code/modules/gamemaster/event2/events/medical/virus.dm b/code/modules/gamemaster/event2/events/medical/virus.dm index 58791d2ad15..ad221c0d043 100644 --- a/code/modules/gamemaster/event2/events/medical/virus.dm +++ b/code/modules/gamemaster/event2/events/medical/virus.dm @@ -1,69 +1,69 @@ -/datum/event2/meta/virus - name = "viral infection" - event_class = "virus" - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - event_type = /datum/event2/event/virus - -/datum/event2/meta/virus/superbug - name = "viral superbug" - chaos = 60 - event_type = /datum/event2/event/virus/superbug - -/datum/event2/meta/virus/outbreak - name = "viral outbreak" - chaos = 60 - event_type = /datum/event2/event/virus/outbreak - -/datum/event2/meta/virus/get_weight() - var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) - virologists += metric.get_people_with_job(/datum/job/cmo) - - return virologists.len * 25 - - - -/datum/event2/event/virus - announce_delay_lower_bound = 1 MINUTE - announce_delay_upper_bound = 3 MINUTES - var/number_of_viruses = 1 - var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest. - var/list/candidates = list() - -// A single powerful virus. -/datum/event2/event/virus/superbug - virus_power = 3 - -// A lot of weaker viruses. -/datum/event2/event/virus/outbreak - virus_power = 1 - number_of_viruses = 3 - - - -/datum/event2/event/virus/set_up() - for(var/mob/living/carbon/human/H in player_list) - if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind)) - candidates += H - candidates = shuffle(candidates) - -/datum/event2/event/virus/announce() - command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \ - All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') - -/datum/event2/event/virus/start() - if(!candidates.len) - log_debug("Virus event could not find any valid targets to infect. Aborting.") - abort() - return - - for(var/i = 1 to number_of_viruses) - var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1) - if(!H) - return - var/datum/disease2/disease/D = new() - D.makerandom(virus_power) - log_debug("Virus event is now infecting \the [H] with a new random virus.") - infect_mob(H, D) - candidates -= H +/datum/event2/meta/virus + name = "viral infection" + event_class = "virus" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_type = /datum/event2/event/virus + +/datum/event2/meta/virus/superbug + name = "viral superbug" + chaos = 60 + event_type = /datum/event2/event/virus/superbug + +/datum/event2/meta/virus/outbreak + name = "viral outbreak" + chaos = 60 + event_type = /datum/event2/event/virus/outbreak + +/datum/event2/meta/virus/get_weight() + var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) + virologists += metric.get_people_with_job(/datum/job/cmo) + + return virologists.len * 25 + + + +/datum/event2/event/virus + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 3 MINUTES + var/number_of_viruses = 1 + var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest. + var/list/candidates = list() + +// A single powerful virus. +/datum/event2/event/virus/superbug + virus_power = 3 + +// A lot of weaker viruses. +/datum/event2/event/virus/outbreak + virus_power = 1 + number_of_viruses = 3 + + + +/datum/event2/event/virus/set_up() + for(var/mob/living/carbon/human/H in player_list) + if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind) && !isbelly(H.loc)) + candidates += H + candidates = shuffle(candidates) + +/datum/event2/event/virus/announce() + command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \ + All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') + +/datum/event2/event/virus/start() + if(!candidates.len) + log_debug("Virus event could not find any valid targets to infect. Aborting.") + abort() + return + + for(var/i = 1 to number_of_viruses) + var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1) + if(!H) + return + var/datum/disease2/disease/D = new() + D.makerandom(virus_power) + log_debug("Virus event is now infecting \the [H] with a new random virus.") + infect_mob(H, D) + candidates -= H diff --git a/code/modules/gamemaster/event2/events/mob_spawning.dm b/code/modules/gamemaster/event2/events/mob_spawning.dm index 35321cd2f06..16ed6a31500 100644 --- a/code/modules/gamemaster/event2/events/mob_spawning.dm +++ b/code/modules/gamemaster/event2/events/mob_spawning.dm @@ -1,97 +1,97 @@ -// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc. - -/datum/event2/event/mob_spawning - var/list/spawned_mobs = list() - var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge. - var/landmark_name = "carpspawn" // Which landmark to use for spawning. - -// Spawns a specific mob from the "edge" of the map, and makes them go towards the station. -// Can also use landmarks, if desired. -/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir) - if(isnull(dir)) - dir = pick(GLOB.cardinal) - - var/list/valid_z_levels = get_location_z_levels() - valid_z_levels -= using_map.sealed_levels // Space levels only please! - - // Check if any landmarks exist! - var/list/spawn_locations = list() - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == landmark_name && (C.z in valid_z_levels)) - spawn_locations.Add(C.loc) - - var/prioritize_landmarks = TRUE - if(use_map_edge_with_landmarks && prob(50)) - prioritize_landmarks = FALSE // One in two chance to come from the edge instead. - - if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those! - shuffle_inplace(spawn_locations) - number_of_groups = min(number_of_groups, spawn_locations.len) - var/i = 1 - while (i <= number_of_groups) - var/group_size = rand(min_size_of_group, max_size_of_group) - for (var/j = 0, j < group_size, j++) - spawn_one_mob(spawn_locations[i], mob_type) - i++ - return - - // Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best! - var/i = 1 - while(i <= number_of_groups) - var/z_level = pick(valid_z_levels) - var/group_size = rand(min_size_of_group, max_size_of_group) - var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level) - var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2) - var/list/turfs = getcircle(group_center, 2) - for(var/j = 0, j < group_size, j++) - // On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station. - // So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen. - - // Find a turf to be the edge of the map. - var/turf/edge_of_map = turfs[(i % turfs.len) + 1] - - // Now walk a straight line towards the center of the map, until we find a non-space tile. - var/turf/edge_of_station = null - - var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later. - for(var/turf/T in getline(edge_of_map, map_center)) - if(!T.is_space()) - break // We found the station! - space_line += T - edge_of_station = T - - // Now put the mob somewhere on the line, hopefully off screen. - // I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using - // 16 or more. - // In the future, a new AI stance that handles long distance travel using getline() could work. - var/max_distance = 8 - var/turf/spawn_turf = null - for(var/turf/point as anything in space_line) - if(get_dist(point, edge_of_station) <= max_distance) - spawn_turf = point - break - - if(spawn_turf) - // Finally, make the simple_mob go towards the edge of the station. - var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type) - if(edge_of_station) - M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station. - i++ - -/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type) - var/mob/living/simple_mob/M = new mob_type(new_loc) - GLOB.destroyed_event.register(M, src, PROC_REF(on_mob_destruction)) - spawned_mobs += M - return M - -// Counts living simple_mobs spawned by this event. -/datum/event2/event/mob_spawning/proc/count_spawned_mobs() - . = 0 - for(var/mob/living/simple_mob/M as anything in spawned_mobs) - if(!QDELETED(M) && M.stat != DEAD) - . += 1 - -// If simple_mob is bomphed, remove it from the list. -/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M) - spawned_mobs -= M - GLOB.destroyed_event.unregister(M, src, PROC_REF(on_mob_destruction)) +// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc. + +/datum/event2/event/mob_spawning + var/list/spawned_mobs = list() + var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge. + var/landmark_name = "carpspawn" // Which landmark to use for spawning. + +// Spawns a specific mob from the "edge" of the map, and makes them go towards the station. +// Can also use landmarks, if desired. +/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir) + if(isnull(dir)) + dir = pick(GLOB.cardinal) + + var/list/valid_z_levels = get_location_z_levels() + valid_z_levels -= using_map.sealed_levels // Space levels only please! + + // Check if any landmarks exist! + var/list/spawn_locations = list() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == landmark_name && (C.z in valid_z_levels)) + spawn_locations.Add(C.loc) + + var/prioritize_landmarks = TRUE + if(use_map_edge_with_landmarks && prob(50)) + prioritize_landmarks = FALSE // One in two chance to come from the edge instead. + + if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those! + shuffle_inplace(spawn_locations) + number_of_groups = min(number_of_groups, spawn_locations.len) + var/i = 1 + while (i <= number_of_groups) + var/group_size = rand(min_size_of_group, max_size_of_group) + for (var/j = 0, j < group_size, j++) + spawn_one_mob(spawn_locations[i], mob_type) + i++ + return + + // Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best! + var/i = 1 + while(i <= number_of_groups) + var/z_level = pick(valid_z_levels) + var/group_size = rand(min_size_of_group, max_size_of_group) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level) + var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for(var/j = 0, j < group_size, j++) + // On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station. + // So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen. + + // Find a turf to be the edge of the map. + var/turf/edge_of_map = turfs[(i % turfs.len) + 1] + + // Now walk a straight line towards the center of the map, until we find a non-space tile. + var/turf/edge_of_station = null + + var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later. + for(var/turf/T in getline(edge_of_map, map_center)) + if(!T.is_space()) + break // We found the station! + space_line += T + edge_of_station = T + + // Now put the mob somewhere on the line, hopefully off screen. + // I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using + // 16 or more. + // In the future, a new AI stance that handles long distance travel using getline() could work. + var/max_distance = 8 + var/turf/spawn_turf = null + for(var/turf/point as anything in space_line) + if(get_dist(point, edge_of_station) <= max_distance) + spawn_turf = point + break + + if(spawn_turf) + // Finally, make the simple_mob go towards the edge of the station. + var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type) + if(edge_of_station) + M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station. + i++ + +/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type) + var/mob/living/simple_mob/M = new mob_type(new_loc) + GLOB.destroyed_event.register(M, src, PROC_REF(on_mob_destruction)) + spawned_mobs += M + return M + +// Counts living simple_mobs spawned by this event. +/datum/event2/event/mob_spawning/proc/count_spawned_mobs() + . = 0 + for(var/mob/living/simple_mob/M as anything in spawned_mobs) + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If simple_mob is bomphed, remove it from the list. +/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M) + spawned_mobs -= M + GLOB.destroyed_event.unregister(M, src, PROC_REF(on_mob_destruction)) diff --git a/code/modules/gamemaster/event2/events/security/carp_migration.dm b/code/modules/gamemaster/event2/events/security/carp_migration.dm index 12f08f3edd0..77e72d044e4 100644 --- a/code/modules/gamemaster/event2/events/security/carp_migration.dm +++ b/code/modules/gamemaster/event2/events/security/carp_migration.dm @@ -1,49 +1,49 @@ -/datum/event2/meta/carp_migration - name = "carp migration" - event_class = "carp" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 30 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/mob_spawning/carp_migration - -/datum/event2/meta/carp_migration/get_weight() - return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40) - - -/datum/event2/event/mob_spawning/carp_migration - announce_delay_lower_bound = 1 MINUTE - announce_delay_upper_bound = 2 MINUTES - length_lower_bound = 30 SECONDS - length_upper_bound = 1 MINUTE - var/carp_cap = 30 // No more than this many (living) carp can exist from this event. - var/carp_smallest_group = 3 - var/carp_largest_group = 5 - var/carp_wave_cooldown = 10 SECONDS - - var/last_carp_wave_time = null // Last world.time we spawned a carp wave. - -/datum/event2/event/mob_spawning/carp_migration/announce() - var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by." - command_announcement.Announce(announcement, "Lifesign Alert") - -/datum/event2/event/mob_spawning/carp_migration/event_tick() - if(last_carp_wave_time + carp_wave_cooldown > world.time) - return - last_carp_wave_time = world.time - - if(count_spawned_mobs() < carp_cap) - spawn_mobs_in_space( - mob_type = /mob/living/simple_mob/animal/space/carp/event, - number_of_groups = rand(1, 4), - min_size_of_group = carp_smallest_group, - max_size_of_group = carp_largest_group - ) - -/datum/event2/event/mob_spawning/carp_migration/end() - // Clean up carp that died in space for some reason. - for(var/mob/living/simple_mob/SM in spawned_mobs) - if(SM.stat == DEAD) - var/turf/T = get_turf(SM) - if(istype(T, /turf/space)) - if(prob(75)) - qdel(SM) +/datum/event2/meta/carp_migration + name = "carp migration" + event_class = "carp" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/mob_spawning/carp_migration + +/datum/event2/meta/carp_migration/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40) + + +/datum/event2/event/mob_spawning/carp_migration + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 2 MINUTES + length_lower_bound = 30 SECONDS + length_upper_bound = 1 MINUTE + var/carp_cap = 30 // No more than this many (living) carp can exist from this event. + var/carp_smallest_group = 3 + var/carp_largest_group = 5 + var/carp_wave_cooldown = 10 SECONDS + + var/last_carp_wave_time = null // Last world.time we spawned a carp wave. + +/datum/event2/event/mob_spawning/carp_migration/announce() + var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by." + command_announcement.Announce(announcement, "Lifesign Alert") + +/datum/event2/event/mob_spawning/carp_migration/event_tick() + if(last_carp_wave_time + carp_wave_cooldown > world.time) + return + last_carp_wave_time = world.time + + if(count_spawned_mobs() < carp_cap) + spawn_mobs_in_space( + mob_type = /mob/living/simple_mob/animal/space/carp/event, + number_of_groups = rand(1, 4), + min_size_of_group = carp_smallest_group, + max_size_of_group = carp_largest_group + ) + +/datum/event2/event/mob_spawning/carp_migration/end() + // Clean up carp that died in space for some reason. + for(var/mob/living/simple_mob/SM in spawned_mobs) + if(SM.stat == DEAD) + var/turf/T = get_turf(SM) + if(istype(T, /turf/space)) + if(prob(75)) + qdel(SM) diff --git a/code/modules/gamemaster/event2/events/security/drill_announcement.dm b/code/modules/gamemaster/event2/events/security/drill_announcement.dm index 271e2d75f17..f44cf134d22 100644 --- a/code/modules/gamemaster/event2/events/security/drill_announcement.dm +++ b/code/modules/gamemaster/event2/events/security/drill_announcement.dm @@ -1,22 +1,22 @@ -/datum/event2/meta/security_drill - name = "security drill" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors. - event_type = /datum/event2/event/security_drill - -/datum/event2/meta/security_drill/get_weight() - var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - - if(!sec) // If there's no security, then there is no drill. - return 0 - if(everyone - sec < 0) // If there's no non-sec, then there is no drill. - return 0 - - // Each security player adds +5 weight, while non-security adds +1.5. - return (sec * 5) + ((everyone - sec) * 1.5) - -/datum/event2/event/security_drill/announce() - command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \ - has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \ - "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement") +/datum/event2/meta/security_drill + name = "security drill" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors. + event_type = /datum/event2/event/security_drill + +/datum/event2/meta/security_drill/get_weight() + var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) + + if(!sec) // If there's no security, then there is no drill. + return 0 + if(everyone - sec < 0) // If there's no non-sec, then there is no drill. + return 0 + + // Each security player adds +5 weight, while non-security adds +1.5. + return (sec * 5) + ((everyone - sec) * 1.5) + +/datum/event2/event/security_drill/announce() + command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \ + has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \ + "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement") diff --git a/code/modules/gamemaster/event2/events/security/prison_break.dm b/code/modules/gamemaster/event2/events/security/prison_break.dm index 80f6ad2f9a7..821afd7aba4 100644 --- a/code/modules/gamemaster/event2/events/security/prison_break.dm +++ b/code/modules/gamemaster/event2/events/security/prison_break.dm @@ -1,234 +1,234 @@ - -// Type for inheritence. -// It has a null name, so it won't be ran. -/datum/event2/meta/prison_break - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - // The weight system can check if people are in these areas. - // This isn't the same list as what the event itself will break, as the event will also - // break open areas inbetween the holding area and the public hallway, like the brig area verses - // the prison area. - var/list/relevant_areas = list() - var/list/irrelevant_areas = list() - -/datum/event2/meta/prison_break/get_weight() - // First, don't do this if nobody can fix the doors. - var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC) - if(!door_fixers) - return 0 - var/list/afflicted_departments = departments.Copy() - var/afflicted_crew = 0 - - afflicted_departments -= DEPARTMENT_SYNTHETIC - for(var/D in afflicted_departments) - afflicted_crew += metric.count_people_in_department(D) - - // Don't do it if nobody is around to ""appreciate"" it. - if(!afflicted_crew) - return 0 - - var/trapped = get_odds_from_trapped_mobs() - - return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped - -// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable. -/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs() - return 0 - -/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L) - var/area/A = get_area(L) - if(!A) - return FALSE - if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas)) - return TRUE - return FALSE - -/datum/event2/meta/prison_break/brig - name = "prison break - brig" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) - event_type = /datum/event2/event/prison_break/brig - relevant_areas = list( - /area/security/prison, - /area/security/security_cell_hallway, - /area/security/security_processing, - /area/security/interrogation - ) - -/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs() - . = 0 - for(var/mob/living/L in player_list) - if(is_mob_in_relevant_area(L)) - // Don't count them if they're in security. - if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY))) - . += 40 - - -/datum/event2/meta/prison_break/armory - name = "prison break - armory" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) - chaos = 40 // Potentially free guns. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/prison_break/armory - -/datum/event2/meta/prison_break/bridge - name = "prison break - bridge" - departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC) - chaos = 40 // Potentially free spare ID. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/prison_break/bridge - -/datum/event2/meta/prison_break/xenobio - name = "prison break - xenobio" - departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC) - chaos = 20 // This one is more likely to actually kill someone. - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/prison_break/xenobio - relevant_areas = list(/area/rnd/xenobiology) - irrelevant_areas = list( - /area/rnd/xenobiology/xenoflora, - /area/rnd/xenobiology/xenoflora_storage - ) - -/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs() - . = 0 - for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list) - if(is_mob_in_relevant_area(X)) - . += 5 - - -/datum/event2/meta/prison_break/virology - name = "prison break - virology" - departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC) - event_type = /datum/event2/event/prison_break/virology - relevant_areas = list( - /area/medical/virology, - /area/medical/virologyaccess - ) - -/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs() - . = 0 - for(var/mob/living/L in player_list) - if(is_mob_in_relevant_area(L)) - // Don't count them if they're in medical. - if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL))) - . += 40 - - - - -/datum/event2/event/prison_break - start_delay_lower_bound = 3 MINUTES - start_delay_upper_bound = 4 MINUTES - length_lower_bound = 40 SECONDS - length_upper_bound = 1 MINUTE - var/area_display_name = null // A string used to describe the area being messed with. - var/containment_display_desc = null - var/list/areas_to_break = list() - var/list/area_types_to_break = null // Area types to include. - var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence. - var/ignore_blast_doors = FALSE - -/datum/event2/event/prison_break/brig - area_display_name = "Brig" - containment_display_desc = "imprisonment" - area_types_to_break = list( - /area/security/prison, - /area/security/brig, - /area/security/security_cell_hallway, - /area/security/security_processing, - /area/security/interrogation - ) - -/datum/event2/event/prison_break/armory - area_display_name = "Armory" - containment_display_desc = "protection" - area_types_to_break = list( - /area/security/brig, - /area/security/warden, - /area/security/evidence_storage, - /area/security/security_equiptment_storage, - /area/security/armoury, - /area/security/tactical - ) - -/datum/event2/event/prison_break/bridge - area_display_name = "Bridge" - containment_display_desc = "isolation" - area_types_to_break = list( - /area/bridge, - /area/bridge_hallway - ) - -/datum/event2/event/prison_break/xenobio - area_display_name = "Xenobiology" - containment_display_desc = "containment" - area_types_to_break = list(/area/rnd/xenobiology) - area_types_to_ignore = list( - /area/rnd/xenobiology/xenoflora, - /area/rnd/xenobiology/xenoflora_storage - ) - -/datum/event2/event/prison_break/virology - area_display_name = "Virology" - containment_display_desc = "quarantine" - area_types_to_break = list( - /area/medical/virology, - /area/medical/virologyaccess - ) - - -/datum/event2/event/prison_break/set_up() - for(var/area/A in world) - if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore)) - areas_to_break += A - - if(!areas_to_break.len) - log_debug("Prison Break event failed to find any areas to break. Aborting.") - abort() - return - -/datum/event2/event/prison_break/announce() - var/my_department = "[location_name()] Firewall Subroutines" - var/message = "An unknown malicious program has been detected in the [area_display_name] \ - lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \ - within approximately three minutes. Direct intervention is required immediately. Disabling the \ - main breaker in the APCs will protect the APC's room from being compromised." - - for(var/obj/machinery/message_server/MS in machines) - MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]
                    ", "", "", 2) - - // Nobody reads the requests consoles so lets use the radio as well. - global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING) - - for(var/mob/living/silicon/ai/A in player_list) - to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \ - Disabling the main breaker in the APCs will protect the APC's room from being compromised.")) - - var/time_to_flicker = start_delay - 10 SECONDS - addtimer(CALLBACK(src, PROC_REF(flicker_area)), time_to_flicker) - - -/datum/event2/event/prison_break/proc/flicker_area() - for(var/area/A in areas_to_break) - var/obj/machinery/power/apc/apc = A.get_apc() - if(istype(apc) && apc.operating) //If the apc's off, it's a little hard to overload the lights. - for(var/obj/machinery/light/L in A) - L.flicker(10) - -/datum/event2/event/prison_break/start() - for(var/area/A in areas_to_break) - spawn(0) // So we don't block the ticker. - A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes. - -// There's between 40 seconds and one minute before the whole station knows. -// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it. -/datum/event2/event/prison_break/end() - command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \ - in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \ - areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert") - - global_announcer.autosay( - "It is now safe to reactivate the APCs' main breakers inside [area_display_name].", - "[location_name()] Firewall Subroutines", - DEPARTMENT_ENGINEERING - ) + +// Type for inheritence. +// It has a null name, so it won't be ran. +/datum/event2/meta/prison_break + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + // The weight system can check if people are in these areas. + // This isn't the same list as what the event itself will break, as the event will also + // break open areas inbetween the holding area and the public hallway, like the brig area verses + // the prison area. + var/list/relevant_areas = list() + var/list/irrelevant_areas = list() + +/datum/event2/meta/prison_break/get_weight() + // First, don't do this if nobody can fix the doors. + var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + if(!door_fixers) + return 0 + var/list/afflicted_departments = departments.Copy() + var/afflicted_crew = 0 + + afflicted_departments -= DEPARTMENT_SYNTHETIC + for(var/D in afflicted_departments) + afflicted_crew += metric.count_people_in_department(D) + + // Don't do it if nobody is around to ""appreciate"" it. + if(!afflicted_crew) + return 0 + + var/trapped = get_odds_from_trapped_mobs() + + return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped + +// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable. +/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs() + return 0 + +/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L) + var/area/A = get_area(L) + if(!A) + return FALSE + if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas)) + return TRUE + return FALSE + +/datum/event2/meta/prison_break/brig + name = "prison break - brig" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) + event_type = /datum/event2/event/prison_break/brig + relevant_areas = list( + /area/security/prison, + /area/security/security_cell_hallway, + /area/security/security_processing, + /area/security/interrogation + ) + +/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/L in player_list) + if(is_mob_in_relevant_area(L)) + // Don't count them if they're in security. + if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY))) + . += 40 + + +/datum/event2/meta/prison_break/armory + name = "prison break - armory" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) + chaos = 40 // Potentially free guns. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/armory + +/datum/event2/meta/prison_break/bridge + name = "prison break - bridge" + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC) + chaos = 40 // Potentially free spare ID. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/bridge + +/datum/event2/meta/prison_break/xenobio + name = "prison break - xenobio" + departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC) + chaos = 20 // This one is more likely to actually kill someone. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/xenobio + relevant_areas = list(/area/rnd/xenobiology) + irrelevant_areas = list( + /area/rnd/xenobiology/xenoflora, + /area/rnd/xenobiology/xenoflora_storage + ) + +/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list) + if(is_mob_in_relevant_area(X)) + . += 5 + + +/datum/event2/meta/prison_break/virology + name = "prison break - virology" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC) + event_type = /datum/event2/event/prison_break/virology + relevant_areas = list( + /area/medical/virology, + /area/medical/virologyaccess + ) + +/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/L in player_list) + if(is_mob_in_relevant_area(L)) + // Don't count them if they're in medical. + if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL))) + . += 40 + + + + +/datum/event2/event/prison_break + start_delay_lower_bound = 3 MINUTES + start_delay_upper_bound = 4 MINUTES + length_lower_bound = 40 SECONDS + length_upper_bound = 1 MINUTE + var/area_display_name = null // A string used to describe the area being messed with. + var/containment_display_desc = null + var/list/areas_to_break = list() + var/list/area_types_to_break = null // Area types to include. + var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence. + var/ignore_blast_doors = FALSE + +/datum/event2/event/prison_break/brig + area_display_name = "Brig" + containment_display_desc = "imprisonment" + area_types_to_break = list( + /area/security/prison, + /area/security/brig, + /area/security/security_cell_hallway, + /area/security/security_processing, + /area/security/interrogation + ) + +/datum/event2/event/prison_break/armory + area_display_name = "Armory" + containment_display_desc = "protection" + area_types_to_break = list( + /area/security/brig, + /area/security/warden, + /area/security/evidence_storage, + /area/security/security_equiptment_storage, + /area/security/armoury, + /area/security/tactical + ) + +/datum/event2/event/prison_break/bridge + area_display_name = "Bridge" + containment_display_desc = "isolation" + area_types_to_break = list( + /area/bridge, + /area/bridge_hallway + ) + +/datum/event2/event/prison_break/xenobio + area_display_name = "Xenobiology" + containment_display_desc = "containment" + area_types_to_break = list(/area/rnd/xenobiology) + area_types_to_ignore = list( + /area/rnd/xenobiology/xenoflora, + /area/rnd/xenobiology/xenoflora_storage + ) + +/datum/event2/event/prison_break/virology + area_display_name = "Virology" + containment_display_desc = "quarantine" + area_types_to_break = list( + /area/medical/virology, + /area/medical/virologyaccess + ) + + +/datum/event2/event/prison_break/set_up() + for(var/area/A in world) + if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore)) + areas_to_break += A + + if(!areas_to_break.len) + log_debug("Prison Break event failed to find any areas to break. Aborting.") + abort() + return + +/datum/event2/event/prison_break/announce() + var/my_department = "[location_name()] Firewall Subroutines" + var/message = "An unknown malicious program has been detected in the [area_display_name] \ + lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \ + within approximately three minutes. Direct intervention is required immediately. Disabling the \ + main breaker in the APCs will protect the APC's room from being compromised." + + for(var/obj/machinery/message_server/MS in machines) + MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]
                    ", "", "", 2) + + // Nobody reads the requests consoles so lets use the radio as well. + global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING) + + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \ + Disabling the main breaker in the APCs will protect the APC's room from being compromised.")) + + var/time_to_flicker = start_delay - 10 SECONDS + addtimer(CALLBACK(src, PROC_REF(flicker_area)), time_to_flicker) + + +/datum/event2/event/prison_break/proc/flicker_area() + for(var/area/A in areas_to_break) + var/obj/machinery/power/apc/apc = A.get_apc() + if(istype(apc) && apc.operating) //If the apc's off, it's a little hard to overload the lights. + for(var/obj/machinery/light/L in A) + L.flicker(10) + +/datum/event2/event/prison_break/start() + for(var/area/A in areas_to_break) + spawn(0) // So we don't block the ticker. + A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes. + +// There's between 40 seconds and one minute before the whole station knows. +// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it. +/datum/event2/event/prison_break/end() + command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \ + in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \ + areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert") + + global_announcer.autosay( + "It is now safe to reactivate the APCs' main breakers inside [area_display_name].", + "[location_name()] Firewall Subroutines", + DEPARTMENT_ENGINEERING + ) diff --git a/code/modules/gamemaster/event2/events/security/rogue_drones.dm b/code/modules/gamemaster/event2/events/security/rogue_drones.dm index da40b3262f6..d7402d3822a 100644 --- a/code/modules/gamemaster/event2/events/security/rogue_drones.dm +++ b/code/modules/gamemaster/event2/events/security/rogue_drones.dm @@ -1,70 +1,70 @@ -/datum/event2/meta/rogue_drones - name = "rogue drones" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/mob_spawning/rogue_drones - -/datum/event2/meta/rogue_drones/get_weight() - . = 10 // Start with a base weight, since this event does provide some value even if no sec is around. - . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 - . += metric.count_all_space_mobs() * 40 - - -/datum/event2/event/mob_spawning/rogue_drones - length_lower_bound = 15 MINUTES - length_upper_bound = 20 MINUTES - var/drones_to_spawn = 6 - -/datum/event2/event/mob_spawning/rogue_drones/set_up() - if(prob(10)) // Small chance for a false alarm. - drones_to_spawn = 0 - -/datum/event2/event/mob_spawning/rogue_drones/announce() - var/msg = null - var/rng = rand(1,5) - switch(rng) - if(1) - msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \ - If any are sighted, approach with caution." - if(2) - msg = "Contact has been lost with a combat drone wing in Sif orbit. \ - If any are sighted in the area, approach with caution." - if(3) - msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \ - If any are sighted in the area, approach with caution." - if(4) - msg = "A passing derelict ship's drone defense systems have just activated. \ - If any are sighted in the area, use caution." - if(5) - msg = "We're detecting a swarm of small objects approaching your station. \ - Most likely a bunch of drones. Please exercise caution if you see any." - - command_announcement.Announce(msg, "Rogue drone alert") - -/datum/event2/event/mob_spawning/rogue_drones/start() - for(var/i = 1 to drones_to_spawn) - spawn_mobs_in_space( - mob_type = /mob/living/simple_mob/mechanical/combat_drone/event, - number_of_groups = 1, - min_size_of_group = 1, - max_size_of_group = 1 - ) - -/datum/event2/event/mob_spawning/rogue_drones/end() - if(drones_to_spawn) - var/number_recovered = 0 - for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs) - var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() - sparks.set_up(3, 0, D.loc) - sparks.start() - D.z = using_map.admin_levels[1] - D.loot_list = list() - - qdel(D) - number_recovered++ - - if(number_recovered > spawned_mobs.len * 0.75) - command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") - else - command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") +/datum/event2/meta/rogue_drones + name = "rogue drones" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/mob_spawning/rogue_drones + +/datum/event2/meta/rogue_drones/get_weight() + . = 10 // Start with a base weight, since this event does provide some value even if no sec is around. + . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 + . += metric.count_all_space_mobs() * 40 + + +/datum/event2/event/mob_spawning/rogue_drones + length_lower_bound = 15 MINUTES + length_upper_bound = 20 MINUTES + var/drones_to_spawn = 6 + +/datum/event2/event/mob_spawning/rogue_drones/set_up() + if(prob(10)) // Small chance for a false alarm. + drones_to_spawn = 0 + +/datum/event2/event/mob_spawning/rogue_drones/announce() + var/msg = null + var/rng = rand(1,5) + switch(rng) + if(1) + msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \ + If any are sighted, approach with caution." + if(2) + msg = "Contact has been lost with a combat drone wing in Sif orbit. \ + If any are sighted in the area, approach with caution." + if(3) + msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \ + If any are sighted in the area, approach with caution." + if(4) + msg = "A passing derelict ship's drone defense systems have just activated. \ + If any are sighted in the area, use caution." + if(5) + msg = "We're detecting a swarm of small objects approaching your station. \ + Most likely a bunch of drones. Please exercise caution if you see any." + + command_announcement.Announce(msg, "Rogue drone alert") + +/datum/event2/event/mob_spawning/rogue_drones/start() + for(var/i = 1 to drones_to_spawn) + spawn_mobs_in_space( + mob_type = /mob/living/simple_mob/mechanical/combat_drone/event, + number_of_groups = 1, + min_size_of_group = 1, + max_size_of_group = 1 + ) + +/datum/event2/event/mob_spawning/rogue_drones/end() + if(drones_to_spawn) + var/number_recovered = 0 + for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs) + var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() + sparks.set_up(3, 0, D.loc) + sparks.start() + D.z = using_map.admin_levels[1] + D.loot_list = list() + + qdel(D) + number_recovered++ + + if(number_recovered > spawned_mobs.len * 0.75) + command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") + else + command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") diff --git a/code/modules/gamemaster/event2/events/security/security_advisement.dm b/code/modules/gamemaster/event2/events/security/security_advisement.dm index 6a3764e40e6..009d64c0ffa 100644 --- a/code/modules/gamemaster/event2/events/security/security_advisement.dm +++ b/code/modules/gamemaster/event2/events/security/security_advisement.dm @@ -1,93 +1,93 @@ -/datum/event2/meta/security_screening - name = "security screening" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis. - event_type = /datum/event2/event/security_screening - -/datum/event2/meta/security_screening/get_weight() - . = 0 - var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) - if(!sec < 2) - return 0 // Can't screen with no security. - . += sec * 10 - . += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 - - // Having ""suspecious"" people present makes this more likely to be picked. - var/suspicious_people = 0 - suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20 - suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10 - suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10 - suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high. - suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5 - suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5 - suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5 - suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20 - suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10 - if(!suspicious_people) - return 0 - . += suspicious_people - -/datum/event2/event/security_screening - var/victim = null - var/list/species_weights = list( - SPECIES_SKRELL = 9, - SPECIES_UNATHI = 15, - SPECIES_HUMAN_VATBORN = 6, - SPECIES_TESHARI = 2, - SPECIES_TAJ = 3, - SPECIES_DIONA = 1, - SPECIES_ZADDAT = 25, - SPECIES_PROMETHEAN = 30 - ) - - var/list/synth_weights = list( - FBP_CYBORG = 15, - FBP_DRONE = 30, - FBP_POSI = 25 - ) - -/datum/event2/event/security_screening/set_up() - var/list/end_weights = list() - - // First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone. - for(var/species_name in species_weights) - var/give_weight = 0 - for(var/datum/data/record/R in data_core.general) - if(R.fields["species"] == species_name) - give_weight += species_weights[species_name] - - end_weights[species_name] = give_weight - - for(var/bot_type in synth_weights) - var/give_weight = 0 - for(var/datum/data/record/R in data_core.general) - if(R.fields["brain_type"] == bot_type) - give_weight += synth_weights[bot_type] - - end_weights[bot_type] = give_weight - - // Second pass eliminates things that don't exist on the station. - // It's possible to choose something like drones when all the drones are AFK. This prevents that from happening. - while(end_weights.len) // Keep at it until we find someone or run out of possibilities. - var/victim_chosen = pickweight(end_weights) - - if(victim_chosen in synth_weights) - if(metric.count_all_FBPs_of_kind(victim_chosen) > 0) - victim = victim_chosen - break - else - if(metric.count_all_of_specific_species(victim_chosen) > 0) - victim = victim_chosen - break - if(!victim) - end_weights -= victim_chosen - - if(!victim) - log_debug("Security Screening event failed to find anyone to screen. Aborting.") - abort() - return - -/datum/event2/event/security_screening/announce() - command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \ - requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \ - personnel onboard \the [location_name()].", "Security Advisement") +/datum/event2/meta/security_screening + name = "security screening" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis. + event_type = /datum/event2/event/security_screening + +/datum/event2/meta/security_screening/get_weight() + . = 0 + var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) + if(!sec < 2) + return 0 // Can't screen with no security. + . += sec * 10 + . += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 + + // Having ""suspecious"" people present makes this more likely to be picked. + var/suspicious_people = 0 + suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20 + suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10 + suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10 + suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high. + suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5 + suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5 + suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5 + suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20 + suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10 + if(!suspicious_people) + return 0 + . += suspicious_people + +/datum/event2/event/security_screening + var/victim = null + var/list/species_weights = list( + SPECIES_SKRELL = 9, + SPECIES_UNATHI = 15, + SPECIES_HUMAN_VATBORN = 6, + SPECIES_TESHARI = 2, + SPECIES_TAJ = 3, + SPECIES_DIONA = 1, + SPECIES_ZADDAT = 25, + SPECIES_PROMETHEAN = 30 + ) + + var/list/synth_weights = list( + FBP_CYBORG = 15, + FBP_DRONE = 30, + FBP_POSI = 25 + ) + +/datum/event2/event/security_screening/set_up() + var/list/end_weights = list() + + // First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone. + for(var/species_name in species_weights) + var/give_weight = 0 + for(var/datum/data/record/R in data_core.general) + if(R.fields["species"] == species_name) + give_weight += species_weights[species_name] + + end_weights[species_name] = give_weight + + for(var/bot_type in synth_weights) + var/give_weight = 0 + for(var/datum/data/record/R in data_core.general) + if(R.fields["brain_type"] == bot_type) + give_weight += synth_weights[bot_type] + + end_weights[bot_type] = give_weight + + // Second pass eliminates things that don't exist on the station. + // It's possible to choose something like drones when all the drones are AFK. This prevents that from happening. + while(end_weights.len) // Keep at it until we find someone or run out of possibilities. + var/victim_chosen = pickweight(end_weights) + + if(victim_chosen in synth_weights) + if(metric.count_all_FBPs_of_kind(victim_chosen) > 0) + victim = victim_chosen + break + else + if(metric.count_all_of_specific_species(victim_chosen) > 0) + victim = victim_chosen + break + if(!victim) + end_weights -= victim_chosen + + if(!victim) + log_debug("Security Screening event failed to find anyone to screen. Aborting.") + abort() + return + +/datum/event2/event/security_screening/announce() + command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \ + requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \ + personnel onboard \the [location_name()].", "Security Advisement") diff --git a/code/modules/gamemaster/event2/events/security/spider_infestation.dm b/code/modules/gamemaster/event2/events/security/spider_infestation.dm index 244db93dd33..811a9d483a9 100644 --- a/code/modules/gamemaster/event2/events/security/spider_infestation.dm +++ b/code/modules/gamemaster/event2/events/security/spider_infestation.dm @@ -1,49 +1,49 @@ -/datum/event2/meta/spider_infestation - name = "spider infestation" - event_class = "spiders" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) - chaos = 30 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/spider_infestation - -/datum/event2/meta/spider_infestation/weak - name = "weak spider infestation" - chaos = 20 - event_type = /datum/event2/event/spider_infestation/weak - - -/datum/event2/meta/spider_infestation/get_weight() - . = 10 - . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 - . += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 - - -// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs. - -/datum/event2/event/spider_infestation - var/spiders_to_spawn = 8 - var/spiderling_to_spawn = /obj/effect/spider/spiderling - -/datum/event2/event/spider_infestation/weak - spiders_to_spawn = 5 - spiderling_to_spawn = /obj/effect/spider/spiderling/stunted - - - -/datum/event2/event/spider_infestation/announce() - command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \ - Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - -/datum/event2/event/spider_infestation/start() - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in get_location_z_levels())) - if(temp_vent.network.normal_members.len > 50) - vents += temp_vent - - while((spiders_to_spawn >= 1) && vents.len) - var/obj/vent = pick(vents) - new spiderling_to_spawn(vent.loc) - log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].") - vents -= vent - spiders_to_spawn-- +/datum/event2/meta/spider_infestation + name = "spider infestation" + event_class = "spiders" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/spider_infestation + +/datum/event2/meta/spider_infestation/weak + name = "weak spider infestation" + chaos = 20 + event_type = /datum/event2/event/spider_infestation/weak + + +/datum/event2/meta/spider_infestation/get_weight() + . = 10 + . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 + . += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 + + +// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs. + +/datum/event2/event/spider_infestation + var/spiders_to_spawn = 8 + var/spiderling_to_spawn = /obj/effect/spider/spiderling + +/datum/event2/event/spider_infestation/weak + spiders_to_spawn = 5 + spiderling_to_spawn = /obj/effect/spider/spiderling/stunted + + + +/datum/event2/event/spider_infestation/announce() + command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \ + Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + +/datum/event2/event/spider_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + if(!temp_vent.welded && temp_vent.network && (temp_vent.loc.z in get_location_z_levels())) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + + while((spiders_to_spawn >= 1) && vents.len) + var/obj/vent = pick(vents) + new spiderling_to_spawn(vent.loc) + log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].") + vents -= vent + spiders_to_spawn-- diff --git a/code/modules/gamemaster/event2/events/security/stowaway.dm b/code/modules/gamemaster/event2/events/security/stowaway.dm index d04d350234a..e4e41c30911 100644 --- a/code/modules/gamemaster/event2/events/security/stowaway.dm +++ b/code/modules/gamemaster/event2/events/security/stowaway.dm @@ -1,64 +1,64 @@ -// Base type used for inheritence. -/datum/event2/meta/stowaway - event_class = "stowaway" - departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) - chaos = 10 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT - event_type = /datum/event2/event/ghost_pod_spawner/stowaway - var/safe_for_extended = FALSE - -/datum/event2/meta/stowaway/normal - name = "stowaway - normal" - safe_for_extended = TRUE - -/datum/event2/meta/stowaway/renegade - name = "stowaway - renegade" - chaos = 30 - event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade - -/datum/event2/meta/stowaway/infiltrator - name = "stowaway - infiltrator" - chaos = 60 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator - -/datum/event2/meta/stowaway/get_weight() - if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) - return 0 - - var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security - var/ghost_activity = metric.assess_all_dead_mobs() / 100 - - return ( (security * 20) + (everyone * 2) ) * ghost_activity - - -/datum/event2/event/ghost_pod_spawner/stowaway - pod_type = /obj/structure/ghost_pod/ghost_activated/human - desired_turf_areas = list(/area/maintenance) - announce_delay_lower_bound = 15 MINUTES - announce_delay_upper_bound = 30 MINUTES - var/antag_type = MODE_STOWAWAY - var/announce_odds = 20 - -/datum/event2/event/ghost_pod_spawner/stowaway/renegade - antag_type = MODE_RENEGADE - announce_odds = 33 - -/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator - antag_type = MODE_INFILTRATOR - announce_odds = 50 - -/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod) - pod.make_antag = antag_type - pod.occupant_type = "[pod.make_antag] [pod.occupant_type]" - - say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod) - -/datum/event2/event/ghost_pod_spawner/stowaway/announce() - if(prob(announce_odds)) - if(atc?.squelched) - return - atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \ - [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ - has been detected passing multiple local stations.") +// Base type used for inheritence. +/datum/event2/meta/stowaway + event_class = "stowaway" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/ghost_pod_spawner/stowaway + var/safe_for_extended = FALSE + +/datum/event2/meta/stowaway/normal + name = "stowaway - normal" + safe_for_extended = TRUE + +/datum/event2/meta/stowaway/renegade + name = "stowaway - renegade" + chaos = 30 + event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade + +/datum/event2/meta/stowaway/infiltrator + name = "stowaway - infiltrator" + chaos = 60 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator + +/datum/event2/meta/stowaway/get_weight() + if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) + return 0 + + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security + var/ghost_activity = metric.assess_all_dead_mobs() / 100 + + return ( (security * 20) + (everyone * 2) ) * ghost_activity + + +/datum/event2/event/ghost_pod_spawner/stowaway + pod_type = /obj/structure/ghost_pod/ghost_activated/human + desired_turf_areas = list(/area/maintenance) + announce_delay_lower_bound = 15 MINUTES + announce_delay_upper_bound = 30 MINUTES + var/antag_type = MODE_STOWAWAY + var/announce_odds = 20 + +/datum/event2/event/ghost_pod_spawner/stowaway/renegade + antag_type = MODE_RENEGADE + announce_odds = 33 + +/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator + antag_type = MODE_INFILTRATOR + announce_odds = 50 + +/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod) + pod.make_antag = antag_type + pod.occupant_type = "[pod.make_antag] [pod.occupant_type]" + + say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod) + +/datum/event2/event/ghost_pod_spawner/stowaway/announce() + if(prob(announce_odds)) + if(atc?.squelched) + return + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \ + [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ + has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/event2/events/security/surprise_carp.dm b/code/modules/gamemaster/event2/events/security/surprise_carp.dm index 39be6d8412d..534e91e2ce1 100644 --- a/code/modules/gamemaster/event2/events/security/surprise_carp.dm +++ b/code/modules/gamemaster/event2/events/security/surprise_carp.dm @@ -1,71 +1,71 @@ -// This event sends a few carp after someone hanging around in space, unannounced. - -/datum/event2/meta/surprise_carp - name = "surprise carp" - departments = list(DEPARTMENT_EVERYONE) - chaos = 20 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/surprise_carp - -/datum/event2/meta/surprise_carp/get_weight() - return metric.count_all_space_mobs() * 50 - - -/datum/event2/event/surprise_carp - var/mob/living/victim = null - -/datum/event2/event/surprise_carp/set_up() - var/list/potential_victims = list() - for(var/mob/living/L in player_list) - if(!(L.z in get_location_z_levels())) - continue // Not on the right z-level. - if(L.stat) - continue // Don't want dead people. - if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space)) - potential_victims += L - - if(potential_victims.len) - victim = pick(potential_victims) - -/datum/event2/event/surprise_carp/start() - if(!victim) - log_debug("Failed to find a target for surprise carp attack. Aborting.") - abort() - return - - var/number_of_carp = rand(1, 2) - log_debug("Sending [number_of_carp] carp\s after \the [victim].") - // Getting off screen tiles is kind of tricky due to potential edge cases that could arise. - // The method we're gonna do is make a big square around the victim, then - // subtract a smaller square in the middle for the default vision range. - var/list/outer_square = get_safe_square(victim, world.view + 3) - var/list/inner_square = get_safe_square(victim, world.view) - - var/list/donut = outer_square - inner_square - for(var/T in donut) - if(!istype(T, /turf/space)) - donut -= T - - for(var/i = 1 to number_of_carp) - var/turf/spawning_turf = pick(donut) - - if(spawning_turf) - var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf) - // Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them. - C.ai_holder?.give_destination(get_turf(victim)) - else - log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.") - -// Gets suitable spots for carp to spawn, without risk of going off the edge of the map. -// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files. -/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius) - var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE) - var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE) - - var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE) - var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE) - - var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z) - var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z) - - return block(lower_left, upper_right) +// This event sends a few carp after someone hanging around in space, unannounced. + +/datum/event2/meta/surprise_carp + name = "surprise carp" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/surprise_carp + +/datum/event2/meta/surprise_carp/get_weight() + return metric.count_all_space_mobs() * 50 + + +/datum/event2/event/surprise_carp + var/mob/living/victim = null + +/datum/event2/event/surprise_carp/set_up() + var/list/potential_victims = list() + for(var/mob/living/L in player_list) + if(!(L.z in get_location_z_levels())) + continue // Not on the right z-level. + if(L.stat) + continue // Don't want dead people. + if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space)) + potential_victims += L + + if(potential_victims.len) + victim = pick(potential_victims) + +/datum/event2/event/surprise_carp/start() + if(!victim) + log_debug("Failed to find a target for surprise carp attack. Aborting.") + abort() + return + + var/number_of_carp = rand(1, 2) + log_debug("Sending [number_of_carp] carp\s after \the [victim].") + // Getting off screen tiles is kind of tricky due to potential edge cases that could arise. + // The method we're gonna do is make a big square around the victim, then + // subtract a smaller square in the middle for the default vision range. + var/list/outer_square = get_safe_square(victim, world.view + 3) + var/list/inner_square = get_safe_square(victim, world.view) + + var/list/donut = outer_square - inner_square + for(var/T in donut) + if(!istype(T, /turf/space)) + donut -= T + + for(var/i = 1 to number_of_carp) + var/turf/spawning_turf = pick(donut) + + if(spawning_turf) + var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf) + // Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them. + C.ai_holder?.give_destination(get_turf(victim)) + else + log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.") + +// Gets suitable spots for carp to spawn, without risk of going off the edge of the map. +// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files. +/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius) + var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE) + var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE) + + var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE) + var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE) + + var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z) + var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z) + + return block(lower_left, upper_right) diff --git a/code/modules/gamemaster/event2/events/security/swarm_boarder.dm b/code/modules/gamemaster/event2/events/security/swarm_boarder.dm index 54dc03189b6..ca7d0c25dbc 100644 --- a/code/modules/gamemaster/event2/events/security/swarm_boarder.dm +++ b/code/modules/gamemaster/event2/events/security/swarm_boarder.dm @@ -1,56 +1,56 @@ -// This is just porting the event to the new new event system, it's not been balanced in any way -// so don't @ me if these things are grossly OP. -/datum/event2/meta/swarm_boarder - event_class = "swarm boarder" - departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING) - chaos = 60 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT - enabled = FALSE // Turns out they are in fact grossly OP. - var/safe_for_extended = FALSE - -/datum/event2/meta/swarm_boarder/get_weight() - if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) - return 0 - - var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) - var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) - var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security) - - var/ghost_activity = metric.assess_all_dead_mobs() / 100 - - return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity - -/datum/event2/meta/swarm_boarder/normal - name = "swarmer shell - normal" - event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder - -/datum/event2/meta/swarm_boarder/melee - name = "swarmer shell - melee" - event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee - -/datum/event2/meta/swarm_boarder/gunner - name = "swarmer shell - gunner" - event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner - - - - -/datum/event2/event/ghost_pod_spawner/swarm_boarder - announce_delay_lower_bound = 5 MINUTES - announce_delay_upper_bound = 15 MINUTES - pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event - desired_turf_areas = list(/area/maintenance) - var/announce_odds = 80 - -/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee - pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee - -/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner - pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner - -/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce() - if(prob(announce_odds)) - if(atc?.squelched) - atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \ - is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ - has been detected passing multiple local stations.") +// This is just porting the event to the new new event system, it's not been balanced in any way +// so don't @ me if these things are grossly OP. +/datum/event2/meta/swarm_boarder + event_class = "swarm boarder" + departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING) + chaos = 60 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + enabled = FALSE // Turns out they are in fact grossly OP. + var/safe_for_extended = FALSE + +/datum/event2/meta/swarm_boarder/get_weight() + if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) + return 0 + + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security) + + var/ghost_activity = metric.assess_all_dead_mobs() / 100 + + return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity + +/datum/event2/meta/swarm_boarder/normal + name = "swarmer shell - normal" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder + +/datum/event2/meta/swarm_boarder/melee + name = "swarmer shell - melee" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee + +/datum/event2/meta/swarm_boarder/gunner + name = "swarmer shell - gunner" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner + + + + +/datum/event2/event/ghost_pod_spawner/swarm_boarder + announce_delay_lower_bound = 5 MINUTES + announce_delay_upper_bound = 15 MINUTES + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event + desired_turf_areas = list(/area/maintenance) + var/announce_odds = 80 + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce() + if(prob(announce_odds)) + if(atc?.squelched) + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \ + is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ + has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm b/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm index e4c89aa6914..a11bfb5a69e 100644 --- a/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm +++ b/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm @@ -1,74 +1,74 @@ -/datum/event2/meta/ion_storm - name = "ion storm" - departments = list(DEPARTMENT_SYNTHETIC) - chaos = 40 - chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT - event_type = /datum/event2/event/ion_storm - -/datum/event2/meta/ion_storm/get_weight() - var/list/bots = metric.get_people_in_department(DEPARTMENT_SYNTHETIC) - . = 5 // A small chance even if no synths are on, since it can still emag beepsky. - for(var/mob/living/silicon/S in bots) - if(istype(S, /mob/living/silicon/robot/drone)) // Drones don't get their laws screwed with, so don't count them. - continue - . += 40 - - -/datum/event2/event/ion_storm - announce_delay_lower_bound = 7 MINUTES - announce_delay_upper_bound = 15 MINUTES - var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version. - var/announce_odds = 50 // Probability of an announcement actually happening after the delay. - -/datum/event2/event/ion_storm/start() - // Ion laws. - for(var/mob/living/silicon/target in silicon_mob_list) - if(target.z in get_location_z_levels()) - // Don't ion law drons. - if(istype(target, /mob/living/silicon/robot/drone)) - continue - - // Or borgs with an AI (they'll get their AI's ion law anyways). - if(istype(target, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = target - if(R.connected_ai) - continue - if(R.shell) - continue - - // Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically. - var/law = target.generate_ion_law() - target.add_ion_law(law) - target.show_laws() - - // Emag bots. - for(var/mob/living/bot/B in mob_list) - if(B.z in get_location_z_levels()) - if(prob(bot_emag_chance)) - B.emag_act(1) - - // Messaging server spam filters. - // This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance. - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) - if(MS.z in get_location_z_levels()) - MS.spamfilter.Cut() - for (var/i = 1, i <= MS.spamfilter_limit, i++) - MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \ - "admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\ - "director", "Hello", "Hi!"," ","nuke","crate","taj","xeno") - -/datum/event2/event/ion_storm/announce() - if(prob(announce_odds)) - command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \ - Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') - -// Fake variant used by traitors. -/datum/event2/event/ion_storm/fake - // Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious. - announce_delay_lower_bound = 0 - announce_delay_upper_bound = 0 - announce_odds = 100 - -/datum/event2/event/ion_storm/fake/start() +/datum/event2/meta/ion_storm + name = "ion storm" + departments = list(DEPARTMENT_SYNTHETIC) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/ion_storm + +/datum/event2/meta/ion_storm/get_weight() + var/list/bots = metric.get_people_in_department(DEPARTMENT_SYNTHETIC) + . = 5 // A small chance even if no synths are on, since it can still emag beepsky. + for(var/mob/living/silicon/S in bots) + if(istype(S, /mob/living/silicon/robot/drone)) // Drones don't get their laws screwed with, so don't count them. + continue + . += 40 + + +/datum/event2/event/ion_storm + announce_delay_lower_bound = 7 MINUTES + announce_delay_upper_bound = 15 MINUTES + var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version. + var/announce_odds = 50 // Probability of an announcement actually happening after the delay. + +/datum/event2/event/ion_storm/start() + // Ion laws. + for(var/mob/living/silicon/target in silicon_mob_list) + if(target.z in get_location_z_levels()) + // Don't ion law drons. + if(istype(target, /mob/living/silicon/robot/drone)) + continue + + // Or borgs with an AI (they'll get their AI's ion law anyways). + if(istype(target, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = target + if(R.connected_ai) + continue + if(R.shell) + continue + + // Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically. + var/law = target.generate_ion_law() + target.add_ion_law(law) + target.show_laws() + + // Emag bots. + for(var/mob/living/bot/B in mob_list) + if(B.z in get_location_z_levels()) + if(prob(bot_emag_chance)) + B.emag_act(1) + + // Messaging server spam filters. + // This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance. + if(message_servers) + for(var/obj/machinery/message_server/MS in message_servers) + if(MS.z in get_location_z_levels()) + MS.spamfilter.Cut() + for (var/i = 1, i <= MS.spamfilter_limit, i++) + MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \ + "admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\ + "director", "Hello", "Hi!"," ","nuke","crate","taj","xeno") + +/datum/event2/event/ion_storm/announce() + if(prob(announce_odds)) + command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \ + Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + +// Fake variant used by traitors. +/datum/event2/event/ion_storm/fake + // Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious. + announce_delay_lower_bound = 0 + announce_delay_upper_bound = 0 + announce_odds = 100 + +/datum/event2/event/ion_storm/fake/start() return \ No newline at end of file diff --git a/code/modules/gamemaster/event2/meta.dm b/code/modules/gamemaster/event2/meta.dm index 3923f857444..74db2a2b6cc 100644 --- a/code/modules/gamemaster/event2/meta.dm +++ b/code/modules/gamemaster/event2/meta.dm @@ -1,81 +1,81 @@ -// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect. -// It is directly held inside the Game Master Event System. - -// The code for actually executing an event should go inside the event object instead. -/datum/event2/meta - // Name used for organization, shown in the debug verb for the GM system. - // If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence. - var/name = null - - // If FALSE, the GM system won't pick this. - // Some events set this to FALSE after running, to avoid running twice. - var/enabled = TRUE - - // What departments the event attached might affect. - var/list/departments = list(DEPARTMENT_EVERYONE) - - // A guess on how disruptive to a round the event might be. If the action is chosen, the GM's - // 'danger' score is increased by this number. - // Negative numbers could be used to signify helpful events. - var/chaos = 0 - - // A threshold the GM will use alongside its 'danger' score, to determine if it should pass - // over the event associated with this object. The decision is based on - var/chaotic_threshold = null - - // If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system. - var/reusable = FALSE - - // A string used to identify a 'class' of similar events. - // If the event is not reusable, than all events sharing the same class are disabled. - // Useful if you only ever want one event per round while having a lot of different subtypes of the event. - var/event_class = null - - // Counter for how many times this event has been picked by the GM. - // Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it. - var/times_ran = 0 - - // The type path to the event associated with this meta object. - // When the GM chooses this event, a new instance is made. - // Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs. - var/event_type = null - - -// Called by the GM system to actually start an event. -/datum/event2/meta/proc/make_event() - var/datum/event2/event/E = new event_type() - E.execute() - return E - -// Returns a TRUE or FALSE for if the GM system should be able to pick this event. -// Can be extended to check for more than just `enabled` later. -/datum/event2/meta/proc/can_pick() - return enabled - -/* - * Procs to Override - */ - -// Returns a number that determines how likely it is for the event to be picked over others. -// Individual events should override this for their own weights. -/datum/event2/meta/proc/get_weight() - return 0 - - -/datum/event2/meta/Topic(href, href_list) - if(..()) - return - - if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) - message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") - return - - if(href_list["force"]) - // SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet. - message_admins("Event '[name]' was forced by [usr.key].") - - if(href_list["toggle"]) - enabled = !enabled - message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].") - +// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect. +// It is directly held inside the Game Master Event System. + +// The code for actually executing an event should go inside the event object instead. +/datum/event2/meta + // Name used for organization, shown in the debug verb for the GM system. + // If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence. + var/name = null + + // If FALSE, the GM system won't pick this. + // Some events set this to FALSE after running, to avoid running twice. + var/enabled = TRUE + + // What departments the event attached might affect. + var/list/departments = list(DEPARTMENT_EVERYONE) + + // A guess on how disruptive to a round the event might be. If the action is chosen, the GM's + // 'danger' score is increased by this number. + // Negative numbers could be used to signify helpful events. + var/chaos = 0 + + // A threshold the GM will use alongside its 'danger' score, to determine if it should pass + // over the event associated with this object. The decision is based on + var/chaotic_threshold = null + + // If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system. + var/reusable = FALSE + + // A string used to identify a 'class' of similar events. + // If the event is not reusable, than all events sharing the same class are disabled. + // Useful if you only ever want one event per round while having a lot of different subtypes of the event. + var/event_class = null + + // Counter for how many times this event has been picked by the GM. + // Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it. + var/times_ran = 0 + + // The type path to the event associated with this meta object. + // When the GM chooses this event, a new instance is made. + // Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs. + var/event_type = null + + +// Called by the GM system to actually start an event. +/datum/event2/meta/proc/make_event() + var/datum/event2/event/E = new event_type() + E.execute() + return E + +// Returns a TRUE or FALSE for if the GM system should be able to pick this event. +// Can be extended to check for more than just `enabled` later. +/datum/event2/meta/proc/can_pick() + return enabled + +/* + * Procs to Override + */ + +// Returns a number that determines how likely it is for the event to be picked over others. +// Individual events should override this for their own weights. +/datum/event2/meta/proc/get_weight() + return 0 + + +/datum/event2/meta/Topic(href, href_list) + if(..()) + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") + return + + if(href_list["force"]) + // SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet. + message_admins("Event '[name]' was forced by [usr.key].") + + if(href_list["toggle"]) + enabled = !enabled + message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].") + // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. \ No newline at end of file diff --git a/code/modules/games/dice.dm b/code/modules/games/dice.dm index b91fb73d1ec..2d2b2dbaf9a 100644 --- a/code/modules/games/dice.dm +++ b/code/modules/games/dice.dm @@ -16,7 +16,7 @@ /obj/item/weapon/dice/attackby(obj/item/weapon/W, mob/user) ..() - if(istype(W, /obj/item/weapon/weldingtool) || istype(W, /obj/item/weapon/flame/lighter)) + if(W.has_tool_quality(TOOL_WELDER) || istype(W, /obj/item/weapon/flame/lighter)) if(cheater) to_chat(user, "Wait, this [name] is already weighted!") else if(tamper_proof) diff --git a/code/modules/genetics/side_effects.dm b/code/modules/genetics/side_effects.dm index 958f05d5ac2..3ff4ee94160 100644 --- a/code/modules/genetics/side_effects.dm +++ b/code/modules/genetics/side_effects.dm @@ -1,81 +1,81 @@ -/datum/genetics/side_effect - var/name // name of the side effect, to use as a header in the manual - var/symptom // description of the symptom of the side effect - var/treatment // description of the treatment of the side effect - var/effect // description of what happens when not treated - var/duration = 0 // delay between start() and finish() - -/datum/genetics/side_effect/proc/start(mob/living/carbon/human/H) - // start the side effect, this should give some cue as to what's happening, - // such as gasping. These cues need to be unique among side-effects. - -/datum/genetics/side_effect/proc/finish(mob/living/carbon/human/H) - // Finish the side-effect. This should first check whether the cure has been - // applied, and if not, cause bad things to happen. - -/datum/genetics/side_effect/genetic_burn - name = "Genetic Burn" - symptom = "Subject's skin turns unusualy red." - treatment = "Inject small dose of dexalin." - effect = "Subject's skin burns." - duration = 30 SECONDS - -/datum/genetics/side_effect/genetic_burn/start(mob/living/carbon/human/H) - H.custom_emote(VISIBLE_MESSAGE, "starts turning very red..") - -/datum/genetics/side_effect/genetic_burn/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent("dexalin")) - return - for(var/organ_name in BP_ALL) - var/obj/item/organ/external/E = H.get_organ(organ_name) - E.take_damage(0, 5, 0) - -/datum/genetics/side_effect/bone_snap - name = "Bone Snap" - symptom = "Subject's limbs tremble notably." - treatment = "Inject small dose of bicaridine." - effect = "Subject's bone breaks." - duration = 60 SECONDS - -/datum/genetics/side_effect/bone_snap/start(mob/living/carbon/human/H) - H.custom_emote(VISIBLE_MESSAGE, "'s limbs start shivering uncontrollably.") - -/datum/genetics/side_effect/bone_snap/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent("bicaridine")) - return - var/organ_name = pick(BP_ALL) - var/obj/item/organ/external/E = H.get_organ(organ_name) - E.take_damage(20, 0, 0) - E.fracture() - -/datum/genetics/side_effect/confuse - name = "Confuse" - symptom = "Subject starts drooling uncontrollably." - treatment = "Inject small dose of dylovene." - effect = "Subject becomes confused." - duration = 30 SECONDS - -/datum/genetics/side_effect/confuse/start(mob/living/carbon/human/H) - var/datum/gender/T = gender_datums[H.get_visible_gender()] - H.custom_emote(VISIBLE_MESSAGE, "has drool running down from [T.his] mouth.") - -/datum/genetics/side_effect/confuse/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent("anti_toxin")) - return - H.Confuse(100) - -/proc/trigger_side_effect(mob/living/carbon/human/H) - spawn - if(!istype(H)) return - var/tp = pick(subtypesof(/datum/genetics/side_effect)) - var/datum/genetics/side_effect/S = new tp - - S.start(H) - spawn(20) - if(!istype(H)) return - H.Weaken(rand(0, S.duration / 50)) - sleep(S.duration) - - if(!istype(H)) return - H.SetWeakened(0) - S.finish(H) +/datum/genetics/side_effect + var/name // name of the side effect, to use as a header in the manual + var/symptom // description of the symptom of the side effect + var/treatment // description of the treatment of the side effect + var/effect // description of what happens when not treated + var/duration = 0 // delay between start() and finish() + +/datum/genetics/side_effect/proc/start(mob/living/carbon/human/H) + // start the side effect, this should give some cue as to what's happening, + // such as gasping. These cues need to be unique among side-effects. + +/datum/genetics/side_effect/proc/finish(mob/living/carbon/human/H) + // Finish the side-effect. This should first check whether the cure has been + // applied, and if not, cause bad things to happen. + +/datum/genetics/side_effect/genetic_burn + name = "Genetic Burn" + symptom = "Subject's skin turns unusualy red." + treatment = "Inject small dose of dexalin." + effect = "Subject's skin burns." + duration = 30 SECONDS + +/datum/genetics/side_effect/genetic_burn/start(mob/living/carbon/human/H) + H.custom_emote(VISIBLE_MESSAGE, "starts turning very red..") + +/datum/genetics/side_effect/genetic_burn/finish(mob/living/carbon/human/H) + if(H.reagents.has_reagent("dexalin")) + return + for(var/organ_name in BP_ALL) + var/obj/item/organ/external/E = H.get_organ(organ_name) + E.take_damage(0, 5, 0) + +/datum/genetics/side_effect/bone_snap + name = "Bone Snap" + symptom = "Subject's limbs tremble notably." + treatment = "Inject small dose of bicaridine." + effect = "Subject's bone breaks." + duration = 60 SECONDS + +/datum/genetics/side_effect/bone_snap/start(mob/living/carbon/human/H) + H.custom_emote(VISIBLE_MESSAGE, "'s limbs start shivering uncontrollably.") + +/datum/genetics/side_effect/bone_snap/finish(mob/living/carbon/human/H) + if(H.reagents.has_reagent("bicaridine")) + return + var/organ_name = pick(BP_ALL) + var/obj/item/organ/external/E = H.get_organ(organ_name) + E.take_damage(20, 0, 0) + E.fracture() + +/datum/genetics/side_effect/confuse + name = "Confuse" + symptom = "Subject starts drooling uncontrollably." + treatment = "Inject small dose of dylovene." + effect = "Subject becomes confused." + duration = 30 SECONDS + +/datum/genetics/side_effect/confuse/start(mob/living/carbon/human/H) + var/datum/gender/T = gender_datums[H.get_visible_gender()] + H.custom_emote(VISIBLE_MESSAGE, "has drool running down from [T.his] mouth.") + +/datum/genetics/side_effect/confuse/finish(mob/living/carbon/human/H) + if(H.reagents.has_reagent("anti_toxin")) + return + H.Confuse(100) + +/proc/trigger_side_effect(mob/living/carbon/human/H) + spawn + if(!istype(H)) return + var/tp = pick(subtypesof(/datum/genetics/side_effect)) + var/datum/genetics/side_effect/S = new tp + + S.start(H) + spawn(20) + if(!istype(H)) return + H.Weaken(rand(0, S.duration / 50)) + sleep(S.duration) + + if(!istype(H)) return + H.SetWeakened(0) + S.finish(H) diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index cf2a19f46a1..430029a0df9 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -154,7 +154,7 @@ var/damage = rand(0, 9) if(!damage) playsound(target, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - target.visible_message("[user] has attempted to punch [target]!") + target.visible_message(span_red("[user] has attempted to punch [target]!")) return TRUE var/obj/item/organ/external/affecting = target.get_organ(ran_zone(user.zone_sel.selecting)) var/armor_block = target.run_armor_check(affecting, "melee") @@ -165,14 +165,14 @@ playsound(target, "punch", 25, 1, -1) - target.visible_message("[user] has punched [target]!") + target.visible_message(span_red("[user] has punched [target]!")) if(armor_soak >= damage) return TRUE target.apply_damage(damage, HALLOSS, affecting, armor_block, armor_soak) if(damage >= 9) - target.visible_message("[user] has weakened [target]!") + target.visible_message(span_red("[user] has weakened [target]!")) target.apply_effect(4, WEAKEN, armor_block) return TRUE @@ -206,11 +206,11 @@ if(W.flags & NOBLUDGEON) return - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) to_chat(user, "It's a holowindow, you can't unfasten it!") - else if(W.is_crowbar() && reinf && state <= 1) + else if(W.has_tool_quality(TOOL_CROWBAR) && reinf && state <= 1) to_chat(user, "It's a holowindow, you can't pry it!") - else if(W.is_wrench() && !anchored && (!state || !reinf)) + else if(W.has_tool_quality(TOOL_WRENCH) && !anchored && (!state || !reinf)) to_chat(user, "It's a holowindow, you can't dismantle it!") else if(W.damtype == BRUTE || W.damtype == BURN) @@ -239,7 +239,7 @@ if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card)) var/aforce = I.force playsound(src, 'sound/effects/Glasshit.ogg', 75, 1) - visible_message("[src] was hit by [I].") + visible_message(span_red("[src] was hit by [I].")) if(I.damtype == BRUTE || I.damtype == BURN) take_damage(aforce) return @@ -267,12 +267,12 @@ qdel(src) /obj/structure/bed/chair/holochair/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) to_chat(user, "It's a holochair, you can't dismantle it!") return //VOREStation Add /obj/structure/bed/holobed/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) to_chat(user, "It's a holochair, you can't dismantle it!") return //VOREStation Add End diff --git a/code/modules/holodeck/HolodeckPrograms.dm b/code/modules/holodeck/HolodeckPrograms.dm index 4ae4361a21c..c68f26d731a 100644 --- a/code/modules/holodeck/HolodeckPrograms.dm +++ b/code/modules/holodeck/HolodeckPrograms.dm @@ -1,7 +1,7 @@ -/datum/holodeck_program - var/target - var/list/ambience = null - -/datum/holodeck_program/New(var/target, var/list/ambience = null) - src.target = target - src.ambience = ambience +/datum/holodeck_program + var/target + var/list/ambience = null + +/datum/holodeck_program/New(var/target, var/list/ambience = null) + src.target = target + src.ambience = ambience diff --git a/code/modules/hydroponics/beekeeping/beehive.dm b/code/modules/hydroponics/beekeeping/beehive.dm index 20d544f701f..126232754b8 100644 --- a/code/modules/hydroponics/beekeeping/beehive.dm +++ b/code/modules/hydroponics/beekeeping/beehive.dm @@ -36,12 +36,12 @@ . += "The lid is open." /obj/machinery/beehive/attackby(var/obj/item/I, var/mob/user) - if(I.is_crowbar()) + if(I.has_tool_quality(TOOL_CROWBAR)) closed = !closed user.visible_message(SPAN_NOTICE("[user] [closed ? "closes" : "opens"] \the [src]."), SPAN_NOTICE("You [closed ? "close" : "open"] \the [src].")) update_icon() return - else if(I.is_wrench()) + else if(I.has_tool_quality(TOOL_WRENCH)) anchored = !anchored playsound(src, I.usesound, 50, 1) user.visible_message(SPAN_NOTICE("[user] [anchored ? "wrenches" : "unwrenches"] \the [src]."), SPAN_NOTICE("You [anchored ? "wrench" : "unwrench"] \the [src].")) @@ -107,7 +107,7 @@ if(smoked) to_chat(user, "The hive is smoked.") return 1 - else if(I.is_screwdriver()) + else if(I.has_tool_quality(TOOL_SCREWDRIVER)) if(bee_count) to_chat(user, SPAN_NOTICE("You can't dismantle \the [src] with these bees inside.")) return diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index 8919b4ede5d..128c1dc7450 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -96,7 +96,7 @@ if(default_deconstruction_screwdriver(user, W)) return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) playsound(src, W.usesound, 100, 1) to_chat(user, "You [anchored ? "un" : ""]secure \the [src].") anchored = !anchored diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 749f6e49205..0ad03d28267 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -245,7 +245,7 @@ /obj/machinery/seed_storage/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) var/list/data = ..() - + if(smart) scanner = list("stats", "produce", "soil", "temperature", "light", "pressure") else @@ -402,18 +402,18 @@ else to_chat(user, "There are no seeds in \the [O.name].") return - else if(O.is_wrench()) + else if(O.has_tool_quality(TOOL_WRENCH)) playsound(src, O.usesound, 50, 1) anchored = !anchored to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - else if(O.is_screwdriver()) + else if(O.has_tool_quality(TOOL_SCREWDRIVER)) panel_open = !panel_open to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.") playsound(src, O.usesound, 50, 1) cut_overlays() if(panel_open) add_overlay("[initial(icon_state)]-panel") - else if((O.is_wirecutter() || istype(O, /obj/item/device/multitool)) && panel_open) + else if((O.has_tool_quality(TOOL_WIRECUTTER) || istype(O, /obj/item/device/multitool)) && panel_open) wires.Interact(user) /obj/machinery/seed_storage/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index a92de0ba98d..224c7ee0c70 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -245,7 +245,7 @@ user.setClickCooldown(user.get_attack_speed(W)) SSplants.add_plant(src) - if(W.is_wirecutter() || istype(W, /obj/item/weapon/surgical/scalpel)) + if(W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/weapon/surgical/scalpel)) if(sampled) to_chat(user, "\The [src] has already been sampled recently.") return diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index 090e78803bc..8a0a4c2c041 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -489,7 +489,7 @@ if(O.is_open_container()) return 0 - if(O.is_wirecutter() || istype(O, /obj/item/weapon/surgical/scalpel)) + if(O.has_tool_quality(TOOL_WIRECUTTER) || istype(O, /obj/item/weapon/surgical/scalpel)) if(!seed) to_chat(user, "There is nothing to take a sample from in \the [src].") @@ -584,7 +584,7 @@ qdel(O) check_health() - else if(mechanical && O.is_wrench()) + else if(mechanical && O.has_tool_quality(TOOL_WRENCH)) //If there's a connector here, the portable_atmospherics setup can handle it. if(locate(/obj/machinery/atmospherics/portables_connector/) in loc) diff --git a/code/modules/identification/identification.dm b/code/modules/identification/identification.dm index 99ed8bc6339..41ac23316a6 100644 --- a/code/modules/identification/identification.dm +++ b/code/modules/identification/identification.dm @@ -1,126 +1,126 @@ -// This is a datum attached to objects to make their 'identity' be unknown initially. -// The identitiy and properties of an unidentified object can be determined in-game through a specialized process or by potentially risky trial-and-error. -// This is very similar to a traditional roguelike's identification system, and as such will use certain terms from those to describe them. -// Despite this, unlike a roguelike, objects that do the same thing DO NOT have the same name/appearance/etc. - -/datum/identification - var/obj/holder = null // The thing the datum is 'attached' to. - // Holds the true information. - var/true_name = null // The real name of the object. It is copied automatically from holder, on the datum being instantiated. - var/true_desc = null // Ditto, for desc. - var/true_description_info = null // Ditto, for helpful examine panel entries. - var/true_description_fluff = null // Ditto, for lore. - var/true_description_antag = null // Ditto, for antag info (this probably won't get used). - var/identified = IDENTITY_UNKNOWN // Can be IDENTITY_UNKNOWN, IDENTITY_PROPERTIES, IDENTITY_QUALITY, or IDENTITY_FULL. - - // Holds what is displayed when not identified sufficently. - var/unidentified_name = null // The name given to the object when not identified. Generated by generate_unidentified_name() - var/unidentified_desc = "You're not too sure what this is." - var/unidentified_description_info = "This object is unidentified, and as such its properties are unknown. Using this object may be dangerous." - - // Lists of lists for generating names by combining one from each. - var/list/naming_lists = list() - - // What 'identification type' is needed to identify this. - var/identification_type = IDENTITY_TYPE_NONE - -/datum/identification/New(obj/new_holder) - ASSERT(new_holder) - holder = new_holder - record_true_identity() // Get all the identifying features from the holder. - update_name() // Then hide them for awhile if needed. - -/datum/identification/Destroy() - holder = null - return ..() - -// Records the object's inital identifiying features to the datum for future safekeeping. -/datum/identification/proc/record_true_identity() - true_name = holder.name - true_desc = holder.desc - true_description_info = holder.description_info - true_description_fluff = holder.description_fluff - true_description_antag = holder.description_antag - -// Formally identifies the holder. -/datum/identification/proc/identify(new_identity = IDENTITY_FULL, mob/user) - if(new_identity & identified) // Already done. - return - identified |= new_identity // Set the bitflag. - if(user) - switch(identified) - if(IDENTITY_QUALITY) - to_chat(user, "You've identified \the [holder]'s quality.") - if(IDENTITY_PROPERTIES) - to_chat(user, "You've identified \the [holder]'s functionality as a [true_name].") - if(IDENTITY_FULL) - to_chat(user, "You've identified \the [holder] as a [true_name], and its quality.") - update_name() - holder.update_icon() - -// Reverses identification for whatever reason. -/datum/identification/proc/unidentify(new_identity = IDENTITY_UNKNOWN, mob/user) - identified &= ~new_identity // Unset the bitflag. - update_name() - holder.update_icon() - if(user) - switch(identified) // Give a message based on what's left. - if(IDENTITY_QUALITY) - to_chat(user, span("warning", "You forgot what \the [holder] actually did...")) - if(IDENTITY_PROPERTIES) - to_chat(user, span("warning", "You forgot \the [holder]'s quality...")) - if(IDENTITY_UNKNOWN) - to_chat(user, span("warning", "You forgot everything about \the [holder].")) - -// Sets the holder's name to the real name if its properties are identified, or obscures it otherwise. -/datum/identification/proc/update_name() - if(identified & IDENTITY_PROPERTIES) - holder.name = true_name - holder.desc = true_desc - holder.description_info = true_description_info - holder.description_fluff = true_description_fluff - holder.description_antag = true_description_antag - return - - if(!unidentified_name) - unidentified_name = generate_unidentified_name() - - holder.name = unidentified_name - holder.desc = unidentified_desc - holder.description_info = unidentified_description_info - holder.description_fluff = null - holder.description_antag = null - -// Makes a name for an object that is not identified. It picks one string out of each list inside naming_list. -/datum/identification/proc/generate_unidentified_name() - if(!LAZYLEN(naming_lists)) - return "unidentified object" - - var/list/new_name = list() - for(var/list/current_list as anything in naming_lists) - new_name += pick(current_list) - return new_name.Join(" ") - -// Used for tech-based objects. -// Unused for now pending Future Stuff(tm). -/datum/identification/mechanical - naming_lists = list( - list("unidentified", "unknown", "strange", "weird", "unfamiliar", "peculiar", "mysterious", "bizarre", "odd"), - list("device", "apparatus", "gadget", "mechanism", "appliance", "machine", "equipment", "invention", "contraption") - ) - identification_type = IDENTITY_TYPE_TECH - -// Used for unidentified hypos. -// Their contents can range from genuine medication, expired medicine, illicit drugs, toxins and poisons, and more. -// They are the analog for potions in a traditional roguelike. -/datum/identification/hypo - naming_lists = list( - list("unidentified", "unknown", "unmarked", "blank", "refilled", "custom", "modified", "questionable", "suspicious"), - list("autoinjector") - ) - unidentified_desc = "An autoinjector that does not give any indication towards what is inside. \ - The case is also sealed tight and the liquids contained cannot be removed except by injecting it into someone. \ - Do you feel lucky?" - unidentified_description_info = "A skilled chemist with a specialized machine can identify this autoinjector. \ - Blindly using the autoinjector is risky and can be dangerous." - identification_type = IDENTITY_TYPE_CHEMICAL +// This is a datum attached to objects to make their 'identity' be unknown initially. +// The identitiy and properties of an unidentified object can be determined in-game through a specialized process or by potentially risky trial-and-error. +// This is very similar to a traditional roguelike's identification system, and as such will use certain terms from those to describe them. +// Despite this, unlike a roguelike, objects that do the same thing DO NOT have the same name/appearance/etc. + +/datum/identification + var/obj/holder = null // The thing the datum is 'attached' to. + // Holds the true information. + var/true_name = null // The real name of the object. It is copied automatically from holder, on the datum being instantiated. + var/true_desc = null // Ditto, for desc. + var/true_description_info = null // Ditto, for helpful examine panel entries. + var/true_description_fluff = null // Ditto, for lore. + var/true_description_antag = null // Ditto, for antag info (this probably won't get used). + var/identified = IDENTITY_UNKNOWN // Can be IDENTITY_UNKNOWN, IDENTITY_PROPERTIES, IDENTITY_QUALITY, or IDENTITY_FULL. + + // Holds what is displayed when not identified sufficently. + var/unidentified_name = null // The name given to the object when not identified. Generated by generate_unidentified_name() + var/unidentified_desc = "You're not too sure what this is." + var/unidentified_description_info = "This object is unidentified, and as such its properties are unknown. Using this object may be dangerous." + + // Lists of lists for generating names by combining one from each. + var/list/naming_lists = list() + + // What 'identification type' is needed to identify this. + var/identification_type = IDENTITY_TYPE_NONE + +/datum/identification/New(obj/new_holder) + ASSERT(new_holder) + holder = new_holder + record_true_identity() // Get all the identifying features from the holder. + update_name() // Then hide them for awhile if needed. + +/datum/identification/Destroy() + holder = null + return ..() + +// Records the object's inital identifiying features to the datum for future safekeeping. +/datum/identification/proc/record_true_identity() + true_name = holder.name + true_desc = holder.desc + true_description_info = holder.description_info + true_description_fluff = holder.description_fluff + true_description_antag = holder.description_antag + +// Formally identifies the holder. +/datum/identification/proc/identify(new_identity = IDENTITY_FULL, mob/user) + if(new_identity & identified) // Already done. + return + identified |= new_identity // Set the bitflag. + if(user) + switch(identified) + if(IDENTITY_QUALITY) + to_chat(user, "You've identified \the [holder]'s quality.") + if(IDENTITY_PROPERTIES) + to_chat(user, "You've identified \the [holder]'s functionality as a [true_name].") + if(IDENTITY_FULL) + to_chat(user, "You've identified \the [holder] as a [true_name], and its quality.") + update_name() + holder.update_icon() + +// Reverses identification for whatever reason. +/datum/identification/proc/unidentify(new_identity = IDENTITY_UNKNOWN, mob/user) + identified &= ~new_identity // Unset the bitflag. + update_name() + holder.update_icon() + if(user) + switch(identified) // Give a message based on what's left. + if(IDENTITY_QUALITY) + to_chat(user, span("warning", "You forgot what \the [holder] actually did...")) + if(IDENTITY_PROPERTIES) + to_chat(user, span("warning", "You forgot \the [holder]'s quality...")) + if(IDENTITY_UNKNOWN) + to_chat(user, span("warning", "You forgot everything about \the [holder].")) + +// Sets the holder's name to the real name if its properties are identified, or obscures it otherwise. +/datum/identification/proc/update_name() + if(identified & IDENTITY_PROPERTIES) + holder.name = true_name + holder.desc = true_desc + holder.description_info = true_description_info + holder.description_fluff = true_description_fluff + holder.description_antag = true_description_antag + return + + if(!unidentified_name) + unidentified_name = generate_unidentified_name() + + holder.name = unidentified_name + holder.desc = unidentified_desc + holder.description_info = unidentified_description_info + holder.description_fluff = null + holder.description_antag = null + +// Makes a name for an object that is not identified. It picks one string out of each list inside naming_list. +/datum/identification/proc/generate_unidentified_name() + if(!LAZYLEN(naming_lists)) + return "unidentified object" + + var/list/new_name = list() + for(var/list/current_list as anything in naming_lists) + new_name += pick(current_list) + return new_name.Join(" ") + +// Used for tech-based objects. +// Unused for now pending Future Stuff(tm). +/datum/identification/mechanical + naming_lists = list( + list("unidentified", "unknown", "strange", "weird", "unfamiliar", "peculiar", "mysterious", "bizarre", "odd"), + list("device", "apparatus", "gadget", "mechanism", "appliance", "machine", "equipment", "invention", "contraption") + ) + identification_type = IDENTITY_TYPE_TECH + +// Used for unidentified hypos. +// Their contents can range from genuine medication, expired medicine, illicit drugs, toxins and poisons, and more. +// They are the analog for potions in a traditional roguelike. +/datum/identification/hypo + naming_lists = list( + list("unidentified", "unknown", "unmarked", "blank", "refilled", "custom", "modified", "questionable", "suspicious"), + list("autoinjector") + ) + unidentified_desc = "An autoinjector that does not give any indication towards what is inside. \ + The case is also sealed tight and the liquids contained cannot be removed except by injecting it into someone. \ + Do you feel lucky?" + unidentified_description_info = "A skilled chemist with a specialized machine can identify this autoinjector. \ + Blindly using the autoinjector is risky and can be dangerous." + identification_type = IDENTITY_TYPE_CHEMICAL diff --git a/code/modules/identification/item_procs.dm b/code/modules/identification/item_procs.dm index c2e5ddaed60..b21716bf47b 100644 --- a/code/modules/identification/item_procs.dm +++ b/code/modules/identification/item_procs.dm @@ -1,30 +1,30 @@ -// This is on the base /item so badmins can play with it by calling hide_identity(). -/obj/item - var/datum/identification/identity = null - var/identity_type = /datum/identification - var/init_hide_identity = FALSE // Set to true to automatically obscure the object on initialization. - -/obj/item/Initialize() - if(init_hide_identity) - identity = new identity_type(src) - return ..() - -/obj/item/Destroy() - if(identity) - QDEL_NULL(identity) - return ..() - -/obj/item/proc/hide_identity() // Mostly for admins to make things secret. - if(!identity) - identity = new identity_type(src) - else - identity.unidentify() - -/obj/item/proc/identify(identity_type = IDENTITY_FULL, mob/user) - if(identity) - identity.identify(identity_type, user) - -/obj/item/proc/is_identified(identity_type = IDENTITY_FULL) - if(!identity) // No identification datum means nothing to hide. - return TRUE - return identity_type & identity.identified +// This is on the base /item so badmins can play with it by calling hide_identity(). +/obj/item + var/datum/identification/identity = null + var/identity_type = /datum/identification + var/init_hide_identity = FALSE // Set to true to automatically obscure the object on initialization. + +/obj/item/Initialize() + if(init_hide_identity) + identity = new identity_type(src) + return ..() + +/obj/item/Destroy() + if(identity) + QDEL_NULL(identity) + return ..() + +/obj/item/proc/hide_identity() // Mostly for admins to make things secret. + if(!identity) + identity = new identity_type(src) + else + identity.unidentify() + +/obj/item/proc/identify(identity_type = IDENTITY_FULL, mob/user) + if(identity) + identity.identify(identity_type, user) + +/obj/item/proc/is_identified(identity_type = IDENTITY_FULL) + if(!identity) // No identification datum means nothing to hide. + return TRUE + return identity_type & identity.identified diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm index 883ecc881e4..f38420fd376 100644 --- a/code/modules/instruments/songs/editor.dm +++ b/code/modules/instruments/songs/editor.dm @@ -196,7 +196,7 @@ set_linear_falloff_duration(round(amount * 10, world.tick_lag)) else if(href_list["setexpfalloff"]) - var/amount = tgui_input_number(usr, "Set exponential sustain factor", "Exponential sustain factor") + var/amount = tgui_input_number(usr, "Set exponential sustain factor", "Exponential sustain factor", round_value=FALSE) if(!isnull(amount)) set_exponential_drop_rate(round(amount, 0.00001)) @@ -206,7 +206,7 @@ set_volume(round(amount, 1)) else if(href_list["setdropoffvolume"]) - var/amount = tgui_input_number(usr, "Set dropoff threshold", "Dropoff Threshold Volume") + var/amount = tgui_input_number(usr, "Set dropoff threshold", "Dropoff Threshold Volume", round_value=FALSE) if(!isnull(amount)) set_dropoff_volume(round(amount, 0.01)) @@ -233,7 +233,7 @@ set_instrument(choice) else if(href_list["setnoteshift"]) - var/amount = tgui_input_number(usr, "Set note shift", "Note Shift") + var/amount = tgui_input_number(usr, "Set note shift", "Note Shift", null, note_shift_max, note_shift_min) if(!isnull(amount)) note_shift = clamp(amount, note_shift_min, note_shift_max) diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index 20802b90cca..8262768d3a8 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -271,7 +271,7 @@ visible_message("\The [user] waves \the [src] around [target].") /obj/item/device/electronic_assembly/attackby(var/obj/item/I, var/mob/user) - if(can_anchor && I.is_wrench()) + if(can_anchor && I.has_tool_quality(TOOL_WRENCH)) anchored = !anchored to_chat(user, span("notice", "You've [anchored ? "" : "un"]secured \the [src] to \the [get_turf(src)].")) if(anchored) @@ -290,14 +290,14 @@ tgui_interact(user) return TRUE - else if(I.is_crowbar()) + else if(I.has_tool_quality(TOOL_CROWBAR)) playsound(src, 'sound/items/Crowbar.ogg', 50, 1) opened = !opened to_chat(user, "You [opened ? "opened" : "closed"] \the [src].") update_icon() return TRUE - else if(istype(I, /obj/item/device/integrated_electronics/wirer) || istype(I, /obj/item/device/integrated_electronics/debugger) || I.is_screwdriver()) + else if(istype(I, /obj/item/device/integrated_electronics/wirer) || istype(I, /obj/item/device/integrated_electronics/debugger) || I.has_tool_quality(TOOL_SCREWDRIVER)) if(opened) tgui_interact(user) return TRUE @@ -397,4 +397,4 @@ // Returns TRUE if I is something that could/should have a valid interaction. Used to tell circuitclothes to hit the circuit with something instead of the clothes /obj/item/device/electronic_assembly/proc/is_valid_tool(var/obj/item/I) - return I.is_crowbar() || I.is_screwdriver() || istype(I, /obj/item/integrated_circuit) || istype(I, /obj/item/weapon/cell/device) || istype(I, /obj/item/device/integrated_electronics) + return I.has_tool_quality(TOOL_CROWBAR) || I.has_tool_quality(TOOL_SCREWDRIVER) || istype(I, /obj/item/integrated_circuit) || istype(I, /obj/item/weapon/cell/device) || istype(I, /obj/item/device/integrated_electronics) diff --git a/code/modules/integrated_electronics/core/assemblies/clothing.dm b/code/modules/integrated_electronics/core/assemblies/clothing.dm index 4840537d97c..c7b6f998fcc 100644 --- a/code/modules/integrated_electronics/core/assemblies/clothing.dm +++ b/code/modules/integrated_electronics/core/assemblies/clothing.dm @@ -1,180 +1,180 @@ - -// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities -// E.g. Glasses have less room than something worn over the chest. -// Note that the electronic assembly is INSIDE the object that actually gets worn, in a similar way to implants. - -/obj/item/device/electronic_assembly/clothing - name = "electronic clothing" - icon_state = "circuitry" // Needs to match the clothing's base icon_state. - desc = "It's a case, for building machines attached to clothing." - w_class = ITEMSIZE_SMALL - max_components = IC_COMPONENTS_BASE - max_complexity = IC_COMPLEXITY_BASE - var/obj/item/clothing/clothing = null - -/obj/item/device/electronic_assembly/clothing/tgui_host() - return clothing.tgui_host() - -/obj/item/device/electronic_assembly/clothing/update_icon() - ..() - clothing.icon_state = icon_state - // We don't need to update the mob sprite since it won't (and shouldn't) actually get changed. - -// This is 'small' relative to the size of regular clothing assemblies. -/obj/item/device/electronic_assembly/clothing/small - max_components = IC_COMPONENTS_BASE / 2 - max_complexity = IC_COMPLEXITY_BASE / 2 - w_class = ITEMSIZE_TINY - -// Ditto. -/obj/item/device/electronic_assembly/clothing/large - max_components = IC_COMPONENTS_BASE * 2 - max_complexity = IC_COMPLEXITY_BASE * 2 - w_class = ITEMSIZE_NORMAL - - -// This is defined higher up, in /clothing to avoid lots of copypasta. -/obj/item/clothing - var/obj/item/device/electronic_assembly/clothing/IC = null - var/obj/item/integrated_circuit/built_in/action_button/action_circuit = null // This gets pulsed when someone clicks the button on the hud. - -/obj/item/clothing/emp_act(severity) - if(IC) - IC.emp_act(severity) - ..() - -/obj/item/clothing/examine(mob/user) - . = ..() - if(IC) - . += IC.examine(user) - -/obj/item/clothing/CtrlShiftClick(mob/user) - var/turf/T = get_turf(src) - if(!T.AdjacentQuick(user)) // So people aren't messing with these from across the room - return FALSE - var/obj/item/I = user.get_active_hand() // ctrl-shift-click doesn't give us the item, we have to fetch it - if(!I) - return FALSE - return IC.attackby(I, user) - -/obj/item/clothing/attack_self(mob/user) - if(IC) - if(IC.opened) - IC.attack_self(user) - else - action_circuit.do_work() - else - ..() - -// Does most of the repeatative setup. -/obj/item/clothing/proc/setup_integrated_circuit(new_type) - // Set up the internal circuit holder. - IC = new new_type(src) - IC.clothing = src - IC.name = name - - // Clothing assemblies can be triggered by clicking on the HUD. This allows that to occur. - action_circuit = new(src.IC) - IC.force_add_circuit(action_circuit) - action_button_name = "Activate [name]" - -/obj/item/clothing/Destroy() - if(IC) - IC.clothing = null - action_circuit = null // Will get deleted by qdel-ing the IC assembly. - qdel(IC) - return ..() - -// Specific subtypes. - -// Jumpsuit. -/obj/item/clothing/under/circuitry - name = "electronic jumpsuit" - desc = "It's a wearable case for electronics. This on is a black jumpsuit with wiring weaved into the fabric." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - worn_state = "circuitry" - -/obj/item/clothing/under/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing) - return ..() - - -// Gloves. -/obj/item/clothing/gloves/circuitry - name = "electronic gloves" - desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \ - device with a screen is attached to the left glove." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/gloves/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - - -// Glasses. -/obj/item/clothing/glasses/circuitry - name = "electronic goggles" - desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \ - Could this augment your vision?" // Sadly it won't, or at least not yet. - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "night" // The on-mob sprite would be identical anyways. - -/obj/item/clothing/glasses/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Shoes -/obj/item/clothing/shoes/circuitry - name = "electronic boots" - desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \ - cover." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/shoes/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Head -/obj/item/clothing/head/circuitry - name = "electronic headwear" - desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \ - goes around the collar, with a heads-up-display attached on the right." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/head/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Ear -/obj/item/clothing/ears/circuitry - name = "electronic earwear" - desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon = 'icons/inventory/ears/item.dmi' - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/ears/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) - return ..() - -// Exo-slot -/obj/item/clothing/suit/circuitry - name = "electronic chestpiece" - desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \ - almost looks professionally made, however the wiring popping out betrays that idea." - description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." - icon_state = "circuitry" - item_state = "circuitry" - -/obj/item/clothing/suit/circuitry/Initialize() - setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/large) + +// The base subtype for assemblies that can be worn. Certain pieces will have more or less capabilities +// E.g. Glasses have less room than something worn over the chest. +// Note that the electronic assembly is INSIDE the object that actually gets worn, in a similar way to implants. + +/obj/item/device/electronic_assembly/clothing + name = "electronic clothing" + icon_state = "circuitry" // Needs to match the clothing's base icon_state. + desc = "It's a case, for building machines attached to clothing." + w_class = ITEMSIZE_SMALL + max_components = IC_COMPONENTS_BASE + max_complexity = IC_COMPLEXITY_BASE + var/obj/item/clothing/clothing = null + +/obj/item/device/electronic_assembly/clothing/tgui_host() + return clothing.tgui_host() + +/obj/item/device/electronic_assembly/clothing/update_icon() + ..() + clothing.icon_state = icon_state + // We don't need to update the mob sprite since it won't (and shouldn't) actually get changed. + +// This is 'small' relative to the size of regular clothing assemblies. +/obj/item/device/electronic_assembly/clothing/small + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + w_class = ITEMSIZE_TINY + +// Ditto. +/obj/item/device/electronic_assembly/clothing/large + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + w_class = ITEMSIZE_NORMAL + + +// This is defined higher up, in /clothing to avoid lots of copypasta. +/obj/item/clothing + var/obj/item/device/electronic_assembly/clothing/IC = null + var/obj/item/integrated_circuit/built_in/action_button/action_circuit = null // This gets pulsed when someone clicks the button on the hud. + +/obj/item/clothing/emp_act(severity) + if(IC) + IC.emp_act(severity) + ..() + +/obj/item/clothing/examine(mob/user) + . = ..() + if(IC) + . += IC.examine(user) + +/obj/item/clothing/CtrlShiftClick(mob/user) + var/turf/T = get_turf(src) + if(!T.AdjacentQuick(user)) // So people aren't messing with these from across the room + return FALSE + var/obj/item/I = user.get_active_hand() // ctrl-shift-click doesn't give us the item, we have to fetch it + if(!I) + return FALSE + return IC.attackby(I, user) + +/obj/item/clothing/attack_self(mob/user) + if(IC) + if(IC.opened) + IC.attack_self(user) + else + action_circuit.do_work() + else + ..() + +// Does most of the repeatative setup. +/obj/item/clothing/proc/setup_integrated_circuit(new_type) + // Set up the internal circuit holder. + IC = new new_type(src) + IC.clothing = src + IC.name = name + + // Clothing assemblies can be triggered by clicking on the HUD. This allows that to occur. + action_circuit = new(src.IC) + IC.force_add_circuit(action_circuit) + action_button_name = "Activate [name]" + +/obj/item/clothing/Destroy() + if(IC) + IC.clothing = null + action_circuit = null // Will get deleted by qdel-ing the IC assembly. + qdel(IC) + return ..() + +// Specific subtypes. + +// Jumpsuit. +/obj/item/clothing/under/circuitry + name = "electronic jumpsuit" + desc = "It's a wearable case for electronics. This on is a black jumpsuit with wiring weaved into the fabric." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + worn_state = "circuitry" + +/obj/item/clothing/under/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing) + return ..() + + +// Gloves. +/obj/item/clothing/gloves/circuitry + name = "electronic gloves" + desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \ + device with a screen is attached to the left glove." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/gloves/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + + +// Glasses. +/obj/item/clothing/glasses/circuitry + name = "electronic goggles" + desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \ + Could this augment your vision?" // Sadly it won't, or at least not yet. + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "night" // The on-mob sprite would be identical anyways. + +/obj/item/clothing/glasses/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Shoes +/obj/item/clothing/shoes/circuitry + name = "electronic boots" + desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \ + cover." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/shoes/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Head +/obj/item/clothing/head/circuitry + name = "electronic headwear" + desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \ + goes around the collar, with a heads-up-display attached on the right." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/head/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Ear +/obj/item/clothing/ears/circuitry + name = "electronic earwear" + desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon = 'icons/inventory/ears/item.dmi' + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/ears/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/small) + return ..() + +// Exo-slot +/obj/item/clothing/suit/circuitry + name = "electronic chestpiece" + desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \ + almost looks professionally made, however the wiring popping out betrays that idea." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." + icon_state = "circuitry" + item_state = "circuitry" + +/obj/item/clothing/suit/circuitry/Initialize() + setup_integrated_circuit(/obj/item/device/electronic_assembly/clothing/large) return ..() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/assemblies/device.dm b/code/modules/integrated_electronics/core/assemblies/device.dm index 8500f4dafcf..d23446dcebc 100644 --- a/code/modules/integrated_electronics/core/assemblies/device.dm +++ b/code/modules/integrated_electronics/core/assemblies/device.dm @@ -12,7 +12,7 @@ ..() /obj/item/device/assembly/electronic_assembly/attackby(obj/item/I as obj, mob/user as mob) - if (I.is_crowbar()) + if (I.has_tool_quality(TOOL_CROWBAR)) toggle_open(user) else if (opened) EA.attackby(I, user) diff --git a/code/modules/integrated_electronics/core/assemblies/generic.dm b/code/modules/integrated_electronics/core/assemblies/generic.dm index 49a933fe1b9..6e120c34231 100644 --- a/code/modules/integrated_electronics/core/assemblies/generic.dm +++ b/code/modules/integrated_electronics/core/assemblies/generic.dm @@ -1,263 +1,263 @@ -// Generic subtypes without a lot of special code. - -// Small assemblies. - -/obj/item/device/electronic_assembly/default - name = "type-a electronic assembly" - -/obj/item/device/electronic_assembly/calc - name = "type-b electronic assembly" - icon_state = "setup_small_calc" - desc = "It's a case, for building small electronics with. This one resembles a pocket calculator." - -/obj/item/device/electronic_assembly/clam - name = "type-c electronic assembly" - icon_state = "setup_small_clam" - desc = "It's a case, for building small electronics with. This one has a clamshell design." - -/obj/item/device/electronic_assembly/simple - name = "type-d electronic assembly" - icon_state = "setup_small_simple" - desc = "It's a case, for building small electronics with. This one has a simple design." - -/obj/item/device/electronic_assembly/hook - name = "type-e electronic assembly" - icon_state = "setup_small_hook" - desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative." - -/obj/item/device/electronic_assembly/pda - name = "type-f electronic assembly" - icon_state = "setup_small_pda" - desc = "It's a case, for building small electronics with. This one resembles a PDA." - -// Tiny assemblies. - -/obj/item/device/electronic_assembly/tiny - name = "electronic device" - icon_state = "setup_device" - desc = "It's a case, for building tiny-sized electronics with." - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE / 2 - max_complexity = IC_COMPLEXITY_BASE / 2 - -/obj/item/device/electronic_assembly/tiny/default - name = "type-a electronic device" - -/obj/item/device/electronic_assembly/tiny/cylinder - name = "type-b electronic device" - icon_state = "setup_device_cylinder" - desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." - -/obj/item/device/electronic_assembly/tiny/scanner - name = "type-c electronic device" - icon_state = "setup_device_scanner" - desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." - -/obj/item/device/electronic_assembly/tiny/hook - name = "type-d electronic device" - icon_state = "setup_device_hook" - desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." - -/obj/item/device/electronic_assembly/tiny/box - name = "type-e electronic device" - icon_state = "setup_device_box" - desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." - -// Medium assemblies. - -/obj/item/device/electronic_assembly/medium - name = "electronic mechanism" - icon_state = "setup_medium" - desc = "It's a case, for building medium-sized electronics with." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 2 - max_complexity = IC_COMPLEXITY_BASE * 2 - -/obj/item/device/electronic_assembly/medium/default - name = "type-a electronic mechanism" - -/obj/item/device/electronic_assembly/medium/box - name = "type-b electronic mechanism" - icon_state = "setup_medium_box" - desc = "It's a case, for building medium-sized electronics with. This one has a boxy design." - -/obj/item/device/electronic_assembly/medium/clam - name = "type-c electronic mechanism" - icon_state = "setup_medium_clam" - desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design." - -/obj/item/device/electronic_assembly/medium/medical - name = "type-d electronic mechanism" - icon_state = "setup_medium_med" - desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus." - -/obj/item/device/electronic_assembly/medium/gun - name = "type-e electronic mechanism" - icon_state = "setup_medium_gun" - item_state = "circuitgun" - desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, \ - if you're feeling optimistic." -// can_fire_equipped = TRUE - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', - ) - -/obj/item/device/electronic_assembly/medium/radio - name = "type-f electronic mechanism" - icon_state = "setup_medium_radio" - desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio." - -// Large assemblies. - -/obj/item/device/electronic_assembly/large - name = "electronic machine" - icon_state = "setup_large" - desc = "It's a case, for building large electronics with." - w_class = ITEMSIZE_LARGE - max_components = IC_COMPONENTS_BASE * 4 - max_complexity = IC_COMPLEXITY_BASE * 4 - can_anchor = TRUE - -/obj/item/device/electronic_assembly/large/default - name = "type-a electronic machine" - -/obj/item/device/electronic_assembly/large/scope - name = "type-b electronic machine" - icon_state = "setup_large_scope" - desc = "It's a case, for building large electronics with. This one resembles an oscilloscope." - -/obj/item/device/electronic_assembly/large/terminal - name = "type-c electronic machine" - icon_state = "setup_large_terminal" - desc = "It's a case, for building large electronics with. This one resembles a computer terminal." - -/obj/item/device/electronic_assembly/large/arm - name = "type-d electronic machine" - icon_state = "setup_large_arm" - desc = "It's a case, for building large electronics with. This one resembles a robotic arm." - -/obj/item/device/electronic_assembly/large/tall - name = "type-e electronic machine" - icon_state = "setup_large_tall" - desc = "It's a case, for building large electronics with. This one has a tall design." - -/obj/item/device/electronic_assembly/large/industrial - name = "type-f electronic machine" - icon_state = "setup_large_industrial" - desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery." - -// Drone assemblies, which can move with the locomotion circuit. - -/obj/item/device/electronic_assembly/drone - name = "electronic drone" - icon_state = "setup_drone" - desc = "It's a case, for building mobile electronics with." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 1.5 - max_complexity = IC_COMPLEXITY_BASE * 1.5 - can_anchor = FALSE - -/obj/item/device/electronic_assembly/drone/can_move() - return TRUE - -/obj/item/device/electronic_assembly/drone/default - name = "type-a electronic drone" - -/obj/item/device/electronic_assembly/drone/arms - name = "type-b electronic drone" - icon_state = "setup_drone_arms" - desc = "It's a case, for building mobile electronics with. This one is armed and dangerous." - -/obj/item/device/electronic_assembly/drone/secbot - name = "type-c electronic drone" - icon_state = "setup_drone_secbot" - desc = "It's a case, for building mobile electronics with. This one resembles a Securitron." - -/obj/item/device/electronic_assembly/drone/medbot - name = "type-d electronic drone" - icon_state = "setup_drone_medbot" - desc = "It's a case, for building mobile electronics with. This one resembles a Medibot." - -/obj/item/device/electronic_assembly/drone/genbot - name = "type-e electronic drone" - icon_state = "setup_drone_genbot" - desc = "It's a case, for building mobile electronics with. This one has a generic bot design." - -/obj/item/device/electronic_assembly/drone/android - name = "type-f electronic drone" - icon_state = "setup_drone_android" - desc = "It's a case, for building mobile electronics with. This one has a hominoid design." - -// Wall mounted assemblies. - -/obj/item/device/electronic_assembly/wallmount - name = "wall-mounted electronic assembly" - icon_state = "setup_wallmount_medium" - desc = "It's a case, for building medium-sized electronics with. It has a magnetized \ - backing to allow it to stick to walls." - w_class = ITEMSIZE_NORMAL - max_components = IC_COMPONENTS_BASE * 2 - max_complexity = IC_COMPLEXITY_BASE * 2 - can_anchor = TRUE - -/obj/item/device/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) - if(get_dist(on_wall,user) > 1) - return - var/ndir = get_dir(on_wall, user) - if(!(ndir in cardinal)) - return - var/turf/T = get_turf(user) - if(!istype(T, /turf/simulated/floor)) - to_chat(user, "You cannot place \the [src] on this spot!") - return - playsound(src, 'sound/machines/click.ogg', 75, 1) - user.visible_message("\The [user] attaches \the [src] to the wall.", - "You attach \the [src] to the wall.", - "You hear clicking.") - if(istype(user, /mob/living/silicon/robot)) //Robots cannot unequip/drop items, for Safety Reasons. - forceMove(T) - user.drop_item(T) - anchored = TRUE - on_anchored() - switch(ndir) - if(NORTH) - pixel_y = -31 - if(SOUTH) - pixel_y = 31 - if(EAST) - pixel_x = -31 - if(WEST) - pixel_x = 31 - -/obj/item/device/electronic_assembly/wallmount/on_unanchored() - pixel_x = 0 - pixel_y = 0 - ..() - -/obj/item/device/electronic_assembly/wallmount/heavy - name = "heavy wall-mounted electronic assembly" - icon_state = "setup_wallmount_large" - desc = "It's a case, for building large electronics with. It has a magnetized backing \ - to allow it to stick to walls." - w_class = ITEMSIZE_LARGE - max_components = IC_COMPONENTS_BASE * 4 - max_complexity = IC_COMPLEXITY_BASE * 4 - -/obj/item/device/electronic_assembly/wallmount/light - name = "light wall-mounted electronic assembly" - icon_state = "setup_wallmount_small" - desc = "It's a case, for building small electronics with. It has a magnetized backing \ - to allow it to stick to walls." - w_class = ITEMSIZE_SMALL - max_components = IC_COMPONENTS_BASE - max_complexity = IC_COMPLEXITY_BASE - -/obj/item/device/electronic_assembly/wallmount/tiny - name = "tiny wall-mounted electronic assembly" - icon_state = "setup_wallmount_tiny" - desc = "It's a case, for building tiny electronics with. It has a magnetized backing \ - to allow it to stick to walls." - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE / 2 +// Generic subtypes without a lot of special code. + +// Small assemblies. + +/obj/item/device/electronic_assembly/default + name = "type-a electronic assembly" + +/obj/item/device/electronic_assembly/calc + name = "type-b electronic assembly" + icon_state = "setup_small_calc" + desc = "It's a case, for building small electronics with. This one resembles a pocket calculator." + +/obj/item/device/electronic_assembly/clam + name = "type-c electronic assembly" + icon_state = "setup_small_clam" + desc = "It's a case, for building small electronics with. This one has a clamshell design." + +/obj/item/device/electronic_assembly/simple + name = "type-d electronic assembly" + icon_state = "setup_small_simple" + desc = "It's a case, for building small electronics with. This one has a simple design." + +/obj/item/device/electronic_assembly/hook + name = "type-e electronic assembly" + icon_state = "setup_small_hook" + desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative." + +/obj/item/device/electronic_assembly/pda + name = "type-f electronic assembly" + icon_state = "setup_small_pda" + desc = "It's a case, for building small electronics with. This one resembles a PDA." + +// Tiny assemblies. + +/obj/item/device/electronic_assembly/tiny + name = "electronic device" + icon_state = "setup_device" + desc = "It's a case, for building tiny-sized electronics with." + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + +/obj/item/device/electronic_assembly/tiny/default + name = "type-a electronic device" + +/obj/item/device/electronic_assembly/tiny/cylinder + name = "type-b electronic device" + icon_state = "setup_device_cylinder" + desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." + +/obj/item/device/electronic_assembly/tiny/scanner + name = "type-c electronic device" + icon_state = "setup_device_scanner" + desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." + +/obj/item/device/electronic_assembly/tiny/hook + name = "type-d electronic device" + icon_state = "setup_device_hook" + desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." + +/obj/item/device/electronic_assembly/tiny/box + name = "type-e electronic device" + icon_state = "setup_device_box" + desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." + +// Medium assemblies. + +/obj/item/device/electronic_assembly/medium + name = "electronic mechanism" + icon_state = "setup_medium" + desc = "It's a case, for building medium-sized electronics with." + w_class = ITEMSIZE_NORMAL + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + +/obj/item/device/electronic_assembly/medium/default + name = "type-a electronic mechanism" + +/obj/item/device/electronic_assembly/medium/box + name = "type-b electronic mechanism" + icon_state = "setup_medium_box" + desc = "It's a case, for building medium-sized electronics with. This one has a boxy design." + +/obj/item/device/electronic_assembly/medium/clam + name = "type-c electronic mechanism" + icon_state = "setup_medium_clam" + desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design." + +/obj/item/device/electronic_assembly/medium/medical + name = "type-d electronic mechanism" + icon_state = "setup_medium_med" + desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus." + +/obj/item/device/electronic_assembly/medium/gun + name = "type-e electronic mechanism" + icon_state = "setup_medium_gun" + item_state = "circuitgun" + desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, \ + if you're feeling optimistic." +// can_fire_equipped = TRUE + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi', + ) + +/obj/item/device/electronic_assembly/medium/radio + name = "type-f electronic mechanism" + icon_state = "setup_medium_radio" + desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio." + +// Large assemblies. + +/obj/item/device/electronic_assembly/large + name = "electronic machine" + icon_state = "setup_large" + desc = "It's a case, for building large electronics with." + w_class = ITEMSIZE_LARGE + max_components = IC_COMPONENTS_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + can_anchor = TRUE + +/obj/item/device/electronic_assembly/large/default + name = "type-a electronic machine" + +/obj/item/device/electronic_assembly/large/scope + name = "type-b electronic machine" + icon_state = "setup_large_scope" + desc = "It's a case, for building large electronics with. This one resembles an oscilloscope." + +/obj/item/device/electronic_assembly/large/terminal + name = "type-c electronic machine" + icon_state = "setup_large_terminal" + desc = "It's a case, for building large electronics with. This one resembles a computer terminal." + +/obj/item/device/electronic_assembly/large/arm + name = "type-d electronic machine" + icon_state = "setup_large_arm" + desc = "It's a case, for building large electronics with. This one resembles a robotic arm." + +/obj/item/device/electronic_assembly/large/tall + name = "type-e electronic machine" + icon_state = "setup_large_tall" + desc = "It's a case, for building large electronics with. This one has a tall design." + +/obj/item/device/electronic_assembly/large/industrial + name = "type-f electronic machine" + icon_state = "setup_large_industrial" + desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery." + +// Drone assemblies, which can move with the locomotion circuit. + +/obj/item/device/electronic_assembly/drone + name = "electronic drone" + icon_state = "setup_drone" + desc = "It's a case, for building mobile electronics with." + w_class = ITEMSIZE_NORMAL + max_components = IC_COMPONENTS_BASE * 1.5 + max_complexity = IC_COMPLEXITY_BASE * 1.5 + can_anchor = FALSE + +/obj/item/device/electronic_assembly/drone/can_move() + return TRUE + +/obj/item/device/electronic_assembly/drone/default + name = "type-a electronic drone" + +/obj/item/device/electronic_assembly/drone/arms + name = "type-b electronic drone" + icon_state = "setup_drone_arms" + desc = "It's a case, for building mobile electronics with. This one is armed and dangerous." + +/obj/item/device/electronic_assembly/drone/secbot + name = "type-c electronic drone" + icon_state = "setup_drone_secbot" + desc = "It's a case, for building mobile electronics with. This one resembles a Securitron." + +/obj/item/device/electronic_assembly/drone/medbot + name = "type-d electronic drone" + icon_state = "setup_drone_medbot" + desc = "It's a case, for building mobile electronics with. This one resembles a Medibot." + +/obj/item/device/electronic_assembly/drone/genbot + name = "type-e electronic drone" + icon_state = "setup_drone_genbot" + desc = "It's a case, for building mobile electronics with. This one has a generic bot design." + +/obj/item/device/electronic_assembly/drone/android + name = "type-f electronic drone" + icon_state = "setup_drone_android" + desc = "It's a case, for building mobile electronics with. This one has a hominoid design." + +// Wall mounted assemblies. + +/obj/item/device/electronic_assembly/wallmount + name = "wall-mounted electronic assembly" + icon_state = "setup_wallmount_medium" + desc = "It's a case, for building medium-sized electronics with. It has a magnetized \ + backing to allow it to stick to walls." + w_class = ITEMSIZE_NORMAL + max_components = IC_COMPONENTS_BASE * 2 + max_complexity = IC_COMPLEXITY_BASE * 2 + can_anchor = TRUE + +/obj/item/device/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) + if(get_dist(on_wall,user) > 1) + return + var/ndir = get_dir(on_wall, user) + if(!(ndir in cardinal)) + return + var/turf/T = get_turf(user) + if(!istype(T, /turf/simulated/floor)) + to_chat(user, "You cannot place \the [src] on this spot!") + return + playsound(src, 'sound/machines/click.ogg', 75, 1) + user.visible_message("\The [user] attaches \the [src] to the wall.", + "You attach \the [src] to the wall.", + "You hear clicking.") + if(istype(user, /mob/living/silicon/robot)) //Robots cannot unequip/drop items, for Safety Reasons. + forceMove(T) + user.drop_item(T) + anchored = TRUE + on_anchored() + switch(ndir) + if(NORTH) + pixel_y = -31 + if(SOUTH) + pixel_y = 31 + if(EAST) + pixel_x = -31 + if(WEST) + pixel_x = 31 + +/obj/item/device/electronic_assembly/wallmount/on_unanchored() + pixel_x = 0 + pixel_y = 0 + ..() + +/obj/item/device/electronic_assembly/wallmount/heavy + name = "heavy wall-mounted electronic assembly" + icon_state = "setup_wallmount_large" + desc = "It's a case, for building large electronics with. It has a magnetized backing \ + to allow it to stick to walls." + w_class = ITEMSIZE_LARGE + max_components = IC_COMPONENTS_BASE * 4 + max_complexity = IC_COMPLEXITY_BASE * 4 + +/obj/item/device/electronic_assembly/wallmount/light + name = "light wall-mounted electronic assembly" + icon_state = "setup_wallmount_small" + desc = "It's a case, for building small electronics with. It has a magnetized backing \ + to allow it to stick to walls." + w_class = ITEMSIZE_SMALL + max_components = IC_COMPONENTS_BASE + max_complexity = IC_COMPLEXITY_BASE + +/obj/item/device/electronic_assembly/wallmount/tiny + name = "tiny wall-mounted electronic assembly" + icon_state = "setup_wallmount_tiny" + desc = "It's a case, for building tiny electronics with. It has a magnetized backing \ + to allow it to stick to walls." + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE / 2 max_complexity = IC_COMPLEXITY_BASE / 2 \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/assemblies/implant.dm b/code/modules/integrated_electronics/core/assemblies/implant.dm index cb40e317061..dd3f19dc4d9 100644 --- a/code/modules/integrated_electronics/core/assemblies/implant.dm +++ b/code/modules/integrated_electronics/core/assemblies/implant.dm @@ -1,18 +1,18 @@ -// Note that this is contained inside an actual implant subtype. -// See code/game/objects/items/weapons/implants/implantcircuits.dm for where this gets held. - -/obj/item/device/electronic_assembly/implant - name = "electronic implant" - icon_state = "setup_implant" - desc = "It's a case, for building very tiny electronics with." - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE / 2 - max_complexity = IC_COMPLEXITY_BASE / 2 - var/obj/item/weapon/implant/integrated_circuit/implant = null - -/obj/item/device/electronic_assembly/implant/tgui_host() - return implant.tgui_host() - -/obj/item/device/electronic_assembly/implant/update_icon() - ..() +// Note that this is contained inside an actual implant subtype. +// See code/game/objects/items/weapons/implants/implantcircuits.dm for where this gets held. + +/obj/item/device/electronic_assembly/implant + name = "electronic implant" + icon_state = "setup_implant" + desc = "It's a case, for building very tiny electronics with." + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + var/obj/item/weapon/implant/integrated_circuit/implant = null + +/obj/item/device/electronic_assembly/implant/tgui_host() + return implant.tgui_host() + +/obj/item/device/electronic_assembly/implant/update_icon() + ..() implant.icon_state = icon_state \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm index adc08861748..21ae07f7d10 100644 --- a/code/modules/integrated_electronics/core/detailer.dm +++ b/code/modules/integrated_electronics/core/detailer.dm @@ -1,76 +1,76 @@ -/obj/item/device/integrated_electronics/detailer - name = "assembly detailer" - desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish." - icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' - icon_state = "detailer" - item_flags = NOBLUDGEON - w_class = ITEMSIZE_SMALL - var/detail_color = COLOR_ASSEMBLY_WHITE - var/list/color_list = list( - "dark gray" = COLOR_ASSEMBLY_BLACK, - "machine gray" = COLOR_ASSEMBLY_BGRAY, - "white" = COLOR_ASSEMBLY_WHITE, - "red" = COLOR_ASSEMBLY_RED, - "orange" = COLOR_ASSEMBLY_ORANGE, - "beige" = COLOR_ASSEMBLY_BEIGE, - "brown" = COLOR_ASSEMBLY_BROWN, - "gold" = COLOR_ASSEMBLY_GOLD, - "yellow" = COLOR_ASSEMBLY_YELLOW, - "gurkha" = COLOR_ASSEMBLY_GURKHA, - "light green" = COLOR_ASSEMBLY_LGREEN, - "green" = COLOR_ASSEMBLY_GREEN, - "light blue" = COLOR_ASSEMBLY_LBLUE, - "blue" = COLOR_ASSEMBLY_BLUE, - "purple" = COLOR_ASSEMBLY_PURPLE, - "hot pink" = COLOR_ASSEMBLY_HOT_PINK - ) - -/obj/item/device/integrated_electronics/detailer/Initialize() - update_icon() - return ..() - -/obj/item/device/integrated_electronics/detailer/update_icon() - cut_overlays() - var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/integrated_electronics/electronic_tools.dmi', "detailer-color") - detail_overlay.color = detail_color - add_overlay(detail_overlay) - -/obj/item/device/integrated_electronics/detailer/tgui_state(mob/user) - return GLOB.tgui_inventory_state - -/obj/item/device/integrated_electronics/detailer/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ICDetailer", name) - ui.open() - -/obj/item/device/integrated_electronics/detailer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) - var/list/data = ..() - data["detail_color"] = detail_color - data["color_list"] = color_list - return data - -/obj/item/device/integrated_electronics/detailer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) - if(..()) - return TRUE - - switch(action) - if("change_color") - if(!(params["color"] in color_list)) - return // to prevent href exploits causing runtimes - detail_color = color_list[params["color"]] - update_icon() - return TRUE - -/obj/item/device/integrated_electronics/detailer/attack_self(mob/user) - tgui_interact(user) - - // Leaving this commented out in case someone decides that this would be better as an "any color" selection system - // Just uncomment this and get rid of all of the TGUI bullshit lol - // if(!in_range(user, src)) - // return - // var/new_color = input(user, "Pick a color", "Color Selection", detail_color) as color|null - // if(!new_color) - // return - // detail_color = new_color +/obj/item/device/integrated_electronics/detailer + name = "assembly detailer" + desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish." + icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' + icon_state = "detailer" + item_flags = NOBLUDGEON + w_class = ITEMSIZE_SMALL + var/detail_color = COLOR_ASSEMBLY_WHITE + var/list/color_list = list( + "dark gray" = COLOR_ASSEMBLY_BLACK, + "machine gray" = COLOR_ASSEMBLY_BGRAY, + "white" = COLOR_ASSEMBLY_WHITE, + "red" = COLOR_ASSEMBLY_RED, + "orange" = COLOR_ASSEMBLY_ORANGE, + "beige" = COLOR_ASSEMBLY_BEIGE, + "brown" = COLOR_ASSEMBLY_BROWN, + "gold" = COLOR_ASSEMBLY_GOLD, + "yellow" = COLOR_ASSEMBLY_YELLOW, + "gurkha" = COLOR_ASSEMBLY_GURKHA, + "light green" = COLOR_ASSEMBLY_LGREEN, + "green" = COLOR_ASSEMBLY_GREEN, + "light blue" = COLOR_ASSEMBLY_LBLUE, + "blue" = COLOR_ASSEMBLY_BLUE, + "purple" = COLOR_ASSEMBLY_PURPLE, + "hot pink" = COLOR_ASSEMBLY_HOT_PINK + ) + +/obj/item/device/integrated_electronics/detailer/Initialize() + update_icon() + return ..() + +/obj/item/device/integrated_electronics/detailer/update_icon() + cut_overlays() + var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/integrated_electronics/electronic_tools.dmi', "detailer-color") + detail_overlay.color = detail_color + add_overlay(detail_overlay) + +/obj/item/device/integrated_electronics/detailer/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/integrated_electronics/detailer/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ICDetailer", name) + ui.open() + +/obj/item/device/integrated_electronics/detailer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["detail_color"] = detail_color + data["color_list"] = color_list + return data + +/obj/item/device/integrated_electronics/detailer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("change_color") + if(!(params["color"] in color_list)) + return // to prevent href exploits causing runtimes + detail_color = color_list[params["color"]] + update_icon() + return TRUE + +/obj/item/device/integrated_electronics/detailer/attack_self(mob/user) + tgui_interact(user) + + // Leaving this commented out in case someone decides that this would be better as an "any color" selection system + // Just uncomment this and get rid of all of the TGUI bullshit lol + // if(!in_range(user, src)) + // return + // var/new_color = input(user, "Pick a color", "Color Selection", detail_color) as color|null + // if(!new_color) + // return + // detail_color = new_color // update_icon() \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm index f5b850ef5eb..d5d01b38c4d 100644 --- a/code/modules/integrated_electronics/core/tools.dm +++ b/code/modules/integrated_electronics/core/tools.dm @@ -33,7 +33,7 @@ to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") return if(io.io_type != selected_io.io_type) - to_chat(user, "Those two types of channels are incompatible. The first is a [selected_io.io_type], \ + to_chat(user, "Those two types of channels are incompatible. The first is a [selected_io.io_type], \ while the second is a [io.io_type].") return if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) @@ -106,7 +106,7 @@ /obj/item/device/integrated_electronics/debugger name = "circuit debugger" desc = "This small tool allows one working with custom machinery to directly set data to a specific pin, useful for writing \ - settings to specific circuits, or for debugging purposes. It can also pulse activation pins." + settings to specific circuits, or for debugging purposes. It can also pulse activation pins." icon = 'icons/obj/integrated_electronics/electronic_tools.dmi' icon_state = "debugger" w_class = 2 @@ -129,13 +129,13 @@ to_chat(user, "You set \the [src]'s memory to \"[new_data]\".") if("number") accepting_refs = 0 - new_data = tgui_input_number(usr, "Now type in a number.","[src] number writing") + new_data = tgui_input_number(usr, "Now type in a number.","[src] number writing", min_value=-INFINITY, round_value=FALSE) if(isnum(new_data) && CanInteract(user, GLOB.tgui_physical_state)) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to [new_data].") if("ref") accepting_refs = 1 - to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ + to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ an object for a ref of that object to save it in memory.") if("null") data_to_write = null @@ -145,7 +145,7 @@ if(accepting_refs && proximity) data_to_write = WEAKREF(target) visible_message("[user] slides \a [src]'s over \the [target].") - to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ + to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ now off.") accepting_refs = 0 @@ -206,7 +206,7 @@ to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") return if(io.io_type != selected_io.io_type) - to_chat(user, "Those two types of channels are incompatible. The first is a [selected_io.io_type], \ + to_chat(user, "Those two types of channels are incompatible. The first is a [selected_io.io_type], \ while the second is a [io.io_type].") return if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) @@ -246,7 +246,7 @@ if(accepting_refs && toolmode == MULTITOOL_MODE_INTCIRCUITS && proximity) weakref_wiring = WEAKREF(target) visible_message("[user] slides \a [src]'s over \the [target].") - to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ + to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ now off.") accepting_refs = 0 @@ -274,7 +274,6 @@ /obj/item/weapon/tool/screwdriver, /obj/item/device/multitool ) - cant_hold = list(/obj/item/weapon/tool/screwdriver/power) /obj/item/weapon/storage/bag/circuits/basic/Initialize() . = ..() diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index a4ebf411dab..db94ed64010 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -1,410 +1,410 @@ -/* Library Items - * - * Contains: - * Bookcase - * Book - * Barcode Scanner - */ - - -/* - * Bookcase - */ - -/obj/structure/bookcase - name = "bookcase" - desc = "A set of wooden shelves, perfect for placing books on." - icon = 'icons/obj/library.dmi' - icon_state = "book-0" - anchored = TRUE - density = TRUE - opacity = 1 - -/obj/structure/bookcase/Initialize() - . = ..() - for(var/obj/item/I in loc) - if(istype(I, /obj/item/weapon/book)) - I.loc = src - update_icon() - -/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/weapon/book)) - user.drop_item() - O.loc = src - update_icon() - else if(istype(O, /obj/item/weapon/pen)) - var/newname = sanitizeSafe(tgui_input_text(usr, "What would you like to title this bookshelf?", null, null, MAX_NAME_LEN), MAX_NAME_LEN) - if(!newname) - return - else - name = ("bookcase ([newname])") - else if(O.is_wrench()) - playsound(src, O.usesound, 100, 1) - to_chat(user, (anchored ? "You unfasten \the [src] from the floor." : "You secure \the [src] to the floor.")) - anchored = !anchored - else if(O.is_screwdriver()) - playsound(src, O.usesound, 75, 1) - to_chat(user, "You begin dismantling \the [src].") - if(do_after(user,25 * O.toolspeed)) - to_chat(user, "You dismantle \the [src].") - new /obj/item/stack/material/wood(get_turf(src), 3) - for(var/obj/item/weapon/book/b in contents) - b.loc = (get_turf(src)) - qdel(src) - - else - ..() - -/obj/structure/bookcase/attack_hand(var/mob/user as mob) - if(contents.len) - var/obj/item/weapon/book/choice = tgui_input_list(usr, "Which book would you like to remove from the shelf?", "Book Selection", contents) - if(choice) - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(choice) - else - choice.loc = get_turf(src) - update_icon() - -/obj/structure/bookcase/ex_act(severity) - switch(severity) - if(1.0) - for(var/obj/item/weapon/book/b in contents) - qdel(b) - qdel(src) - return - if(2.0) - for(var/obj/item/weapon/book/b in contents) - if (prob(50)) b.loc = (get_turf(src)) - else qdel(b) - qdel(src) - return - if(3.0) - if (prob(50)) - for(var/obj/item/weapon/book/b in contents) - b.loc = (get_turf(src)) - qdel(src) - return - else - return - -/obj/structure/bookcase/update_icon() - if(contents.len < 5) - icon_state = "book-[contents.len]" - else - icon_state = "book-5" - -/* -Book Cart -*/ - -/obj/structure/bookcase/bookcart - name = "book cart" - icon = 'icons/obj/library.dmi' - icon_state = "bookcart-0" - anchored = FALSE - opacity = 0 - -/obj/structure/bookcase/bookcart/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/weapon/book)) - user.drop_item() - O.loc = src - update_icon() - else - return - -/obj/structure/bookcase/bookcart/update_icon() - if(contents.len < 5) - icon_state = "bookcart-[contents.len]" - else - icon_state = "bookcart-5" - -/* -Book Cart End -*/ - -/obj/structure/bookcase/manuals/medical - name = "Medical Manuals bookcase" - -/obj/structure/bookcase/manuals/medical/New() - ..() - new /obj/item/weapon/book/manual/medical_cloning(src) - new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) - new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) - new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) - update_icon() - - -/obj/structure/bookcase/manuals/engineering - name = "Engineering Manuals bookcase" - -/obj/structure/bookcase/manuals/engineering/New() - ..() - new /obj/item/weapon/book/manual/engineering_construction(src) - new /obj/item/weapon/book/manual/engineering_particle_accelerator(src) - new /obj/item/weapon/book/manual/engineering_hacking(src) - new /obj/item/weapon/book/manual/engineering_guide(src) - new /obj/item/weapon/book/manual/atmospipes(src) - new /obj/item/weapon/book/manual/engineering_singularity_safety(src) - new /obj/item/weapon/book/manual/evaguide(src) - update_icon() - -/obj/structure/bookcase/manuals/research_and_development - name = "R&D Manuals bookcase" - -/obj/structure/bookcase/manuals/research_and_development/New() - ..() - new /obj/item/weapon/book/manual/research_and_development(src) - update_icon() - - -/* - * Book - */ -/obj/item/weapon/book - name = "book" - icon = 'icons/obj/library.dmi' - icon_state ="book" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' - ) - item_state = "book" - throw_speed = 1 - throw_range = 5 - flags = NOCONDUCT - w_class = ITEMSIZE_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) - attack_verb = list("bashed", "whacked", "educated") - var/dat // Actual page content - var/due_date = 0 // Game time in 1/10th seconds - var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - var/libcategory = "Miscellaneous" // The library category this book sits in. "Fiction", "Non-Fiction", "Adult", "Reference", "Religion" - var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified - var/title // The real name of the book. - var/carved = 0 // Has the book been hollowed out for use as a secret storage item? - var/obj/item/store //What's in the book? - drop_sound = 'sound/items/drop/book.ogg' - pickup_sound = 'sound/items/pickup/book.ogg' - -/obj/item/weapon/book/attack_self(var/mob/user as mob) - if(carved) - if(store) - to_chat(user, "[store] falls out of [title]!") - store.loc = get_turf(src.loc) - store = null - return - else - to_chat(user, "The pages of [title] have been cut out!") - return - if(src.dat) - user << browse("Penned by [author].
                    " + "[dat]", "window=book") - user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") - playsound(src, 'sound/bureaucracy/bookopen.ogg', 50, 1) - onclose(user, "book") - playsound(src, 'sound/bureaucracy/bookclose.ogg', 50, 1) - else - to_chat(user, "This book is completely blank!") - -/obj/item/weapon/book/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(carved) - if(!store) - if(W.w_class < ITEMSIZE_LARGE) - user.drop_item() - W.loc = src - store = W - to_chat(user, "You put [W] in [title].") - return - else - to_chat(user, "[W] won't fit in [title].") - return - else - to_chat(user, "There's already something in [title]!") - return - if(istype(W, /obj/item/weapon/pen)) - if(unique) - to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.") - return - var/choice = tgui_input_list(usr, "What would you like to change?", "Change What?", list("Title", "Contents", "Author", "Cancel")) - switch(choice) - if("Title") - var/newtitle = reject_bad_text(sanitizeSafe(tgui_input_text(usr, "Write a new title:"))) - if(!newtitle) - to_chat(usr, "The title is invalid.") - return - else - src.name = newtitle - src.title = newtitle - if("Contents") - var/content = sanitize(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN) - if(!content) - to_chat(usr, "The content is invalid.") - return - else - src.dat += content - if("Author") - var/newauthor = sanitize(tgui_input_text(usr, "Write the author's name:")) - if(!newauthor) - to_chat(usr, "The name is invalid.") - return - else - src.author = newauthor - else - return - else if(istype(W, /obj/item/weapon/barcodescanner)) - var/obj/item/weapon/barcodescanner/scanner = W - if(!scanner.computer) - to_chat(user, "[W]'s screen flashes: 'No associated computer found!'") - else - switch(scanner.mode) - if(0) - scanner.book = src - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'") - if(1) - scanner.book = src - scanner.computer.buffer_book = src.name - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'") - if(2) - scanner.book = src - for(var/datum/borrowbook/b in scanner.computer.checkouts) - if(b.bookname == src.name) - scanner.computer.checkouts.Remove(b) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'") - return - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'") - if(3) - scanner.book = src - for(var/obj/item/weapon/book in scanner.computer.inventory) - if(book == src) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'") - return - scanner.computer.inventory.Add(src) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") - else if(istype(W, /obj/item/weapon/material/knife) || W.is_wirecutter()) - if(carved) return - to_chat(user, "You begin to carve out [title].") - if(do_after(user, 30)) - to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") - playsound(src, 'sound/bureaucracy/papercrumple.ogg', 50, 1) - new /obj/item/weapon/shreddedp(get_turf(src)) - carved = 1 - return - else - ..() - -/obj/item/weapon/book/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(user.zone_sel.selecting == O_EYES) - user.visible_message("You open up the book and show it to [M]. ", \ - " [user] opens up a book and shows it to [M]. ") - M << browse("Penned by [author].
                    " + "[dat]", "window=book") - user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam - -/* -* Book Bundle (Multi-page book) -*/ - -/obj/item/weapon/book/bundle - var/page = 1 //current page - var/list/pages = list() //the contents of each page - -/obj/item/weapon/book/bundle/proc/show_content(mob/user as mob) - if(!pages.len) - return - var/dat - var/obj/item/weapon/W = pages[page] - // first - if(page == 1) - dat+= "
                    " - dat+= "

                    " - // last - else if(page == pages.len) - dat+= "" - dat+= "

                    " - // middle pages - else - dat+= "" - dat+= "

                    " - if(istype(pages[page], /obj/item/weapon/paper)) - var/obj/item/weapon/paper/P = W - if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) - dat += "[P.name][stars(P.info)][P.stamps]" - else - dat += "[P.name][P.info][P.stamps]" - user << browse(dat, "window=[name]") - else if(istype(pages[page], /obj/item/weapon/photo)) - var/obj/item/weapon/photo/P = W - user << browse_rsc(P.img, "tmp_photo.png") - user << browse(dat + "[P.name]" \ - + "" \ - + "
                    Written on the back:
                    [P.scribble]" : null]"\ - + "", "window=[name]") - else if(!isnull(pages[page])) - if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) - dat += "Page [page][stars(pages[page])]" - else - dat += "Page [page][pages[page]]" - user << browse(dat, "window=[name]") - -/obj/item/weapon/book/bundle/attack_self(mob/user as mob) - src.show_content(user) - add_fingerprint(usr) - update_icon() - return - -/obj/item/weapon/book/bundle/Topic(href, href_list) - if(..()) - return 1 - if((src in usr.contents) || (istype(src.loc, /obj/item/weapon/folder) && (src.loc in usr.contents))) - usr.set_machine(src) - if(href_list["next_page"]) - if(page != pages.len) - page++ - playsound(src, "pageturn", 50, 1) - if(href_list["prev_page"]) - if(page > 1) - page-- - playsound(src, "pageturn", 50, 1) - src.attack_self(usr) - updateUsrDialog() - else - to_chat(usr, "You need to hold it in your hands!") - -/* - * Barcode Scanner - */ -/obj/item/weapon/barcodescanner - name = "barcode scanner" - icon = 'icons/obj/library.dmi' - icon_state ="scanner" - throw_speed = 1 - throw_range = 5 - w_class = ITEMSIZE_SMALL - var/obj/machinery/librarycomp/computer // Associated computer - Modes 1 to 3 use this - var/obj/item/weapon/book/book // Currently scanned book - var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory - -/obj/item/weapon/barcodescanner/attack_self(mob/user as mob) - mode += 1 - if(mode > 3) - mode = 0 - to_chat(user, "[src] Status Display:") - var/modedesc - switch(mode) - if(0) - modedesc = "Scan book to local buffer." - if(1) - modedesc = "Scan book to local buffer and set associated computer buffer to match." - if(2) - modedesc = "Scan book to local buffer, attempt to check in scanned book." - if(3) - modedesc = "Scan book to local buffer, attempt to add book to general inventory." - else - modedesc = "ERROR" - to_chat(user, " - Mode [mode] : [modedesc]") - if(src.computer) - to_chat(user, "Computer has been associated with this unit.") - else - to_chat(user, "No associated computer found. Only local scans will function properly.") - to_chat(user, "\n") \ No newline at end of file +/* Library Items + * + * Contains: + * Bookcase + * Book + * Barcode Scanner + */ + + +/* + * Bookcase + */ + +/obj/structure/bookcase + name = "bookcase" + desc = "A set of wooden shelves, perfect for placing books on." + icon = 'icons/obj/library.dmi' + icon_state = "book-0" + anchored = TRUE + density = TRUE + opacity = 1 + +/obj/structure/bookcase/Initialize() + . = ..() + for(var/obj/item/I in loc) + if(istype(I, /obj/item/weapon/book)) + I.loc = src + update_icon() + +/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/weapon/book)) + user.drop_item() + O.loc = src + update_icon() + else if(istype(O, /obj/item/weapon/pen)) + var/newname = sanitizeSafe(tgui_input_text(usr, "What would you like to title this bookshelf?", null, null, MAX_NAME_LEN), MAX_NAME_LEN) + if(!newname) + return + else + name = ("bookcase ([newname])") + else if(O.has_tool_quality(TOOL_WRENCH)) + playsound(src, O.usesound, 100, 1) + to_chat(user, (anchored ? "You unfasten \the [src] from the floor." : "You secure \the [src] to the floor.")) + anchored = !anchored + else if(O.has_tool_quality(TOOL_SCREWDRIVER)) + playsound(src, O.usesound, 75, 1) + to_chat(user, "You begin dismantling \the [src].") + if(do_after(user,25 * O.toolspeed)) + to_chat(user, "You dismantle \the [src].") + new /obj/item/stack/material/wood(get_turf(src), 3) + for(var/obj/item/weapon/book/b in contents) + b.loc = (get_turf(src)) + qdel(src) + + else + ..() + +/obj/structure/bookcase/attack_hand(var/mob/user as mob) + if(contents.len) + var/obj/item/weapon/book/choice = tgui_input_list(usr, "Which book would you like to remove from the shelf?", "Book Selection", contents) + if(choice) + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(choice) + else + choice.loc = get_turf(src) + update_icon() + +/obj/structure/bookcase/ex_act(severity) + switch(severity) + if(1.0) + for(var/obj/item/weapon/book/b in contents) + qdel(b) + qdel(src) + return + if(2.0) + for(var/obj/item/weapon/book/b in contents) + if (prob(50)) b.loc = (get_turf(src)) + else qdel(b) + qdel(src) + return + if(3.0) + if (prob(50)) + for(var/obj/item/weapon/book/b in contents) + b.loc = (get_turf(src)) + qdel(src) + return + else + return + +/obj/structure/bookcase/update_icon() + if(contents.len < 5) + icon_state = "book-[contents.len]" + else + icon_state = "book-5" + +/* +Book Cart +*/ + +/obj/structure/bookcase/bookcart + name = "book cart" + icon = 'icons/obj/library.dmi' + icon_state = "bookcart-0" + anchored = FALSE + opacity = 0 + +/obj/structure/bookcase/bookcart/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/weapon/book)) + user.drop_item() + O.loc = src + update_icon() + else + return + +/obj/structure/bookcase/bookcart/update_icon() + if(contents.len < 5) + icon_state = "bookcart-[contents.len]" + else + icon_state = "bookcart-5" + +/* +Book Cart End +*/ + +/obj/structure/bookcase/manuals/medical + name = "Medical Manuals bookcase" + +/obj/structure/bookcase/manuals/medical/New() + ..() + new /obj/item/weapon/book/manual/medical_cloning(src) + new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) + new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) + new /obj/item/weapon/book/manual/medical_diagnostics_manual(src) + update_icon() + + +/obj/structure/bookcase/manuals/engineering + name = "Engineering Manuals bookcase" + +/obj/structure/bookcase/manuals/engineering/New() + ..() + new /obj/item/weapon/book/manual/engineering_construction(src) + new /obj/item/weapon/book/manual/engineering_particle_accelerator(src) + new /obj/item/weapon/book/manual/engineering_hacking(src) + new /obj/item/weapon/book/manual/engineering_guide(src) + new /obj/item/weapon/book/manual/atmospipes(src) + new /obj/item/weapon/book/manual/engineering_singularity_safety(src) + new /obj/item/weapon/book/manual/evaguide(src) + update_icon() + +/obj/structure/bookcase/manuals/research_and_development + name = "R&D Manuals bookcase" + +/obj/structure/bookcase/manuals/research_and_development/New() + ..() + new /obj/item/weapon/book/manual/research_and_development(src) + update_icon() + + +/* + * Book + */ +/obj/item/weapon/book + name = "book" + icon = 'icons/obj/library.dmi' + icon_state ="book" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_books.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_books.dmi' + ) + item_state = "book" + throw_speed = 1 + throw_range = 5 + flags = NOCONDUCT + w_class = ITEMSIZE_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) + attack_verb = list("bashed", "whacked", "educated") + var/dat // Actual page content + var/due_date = 0 // Game time in 1/10th seconds + var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + var/libcategory = "Miscellaneous" // The library category this book sits in. "Fiction", "Non-Fiction", "Adult", "Reference", "Religion" + var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified + var/title // The real name of the book. + var/carved = 0 // Has the book been hollowed out for use as a secret storage item? + var/obj/item/store //What's in the book? + drop_sound = 'sound/items/drop/book.ogg' + pickup_sound = 'sound/items/pickup/book.ogg' + +/obj/item/weapon/book/attack_self(var/mob/user as mob) + if(carved) + if(store) + to_chat(user, "[store] falls out of [title]!") + store.loc = get_turf(src.loc) + store = null + return + else + to_chat(user, "The pages of [title] have been cut out!") + return + if(src.dat) + user << browse("Penned by [author].
                    " + "[dat]", "window=book") + user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") + playsound(src, 'sound/bureaucracy/bookopen.ogg', 50, 1) + onclose(user, "book") + playsound(src, 'sound/bureaucracy/bookclose.ogg', 50, 1) + else + to_chat(user, "This book is completely blank!") + +/obj/item/weapon/book/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(carved) + if(!store) + if(W.w_class < ITEMSIZE_LARGE) + user.drop_item() + W.loc = src + store = W + to_chat(user, "You put [W] in [title].") + return + else + to_chat(user, "[W] won't fit in [title].") + return + else + to_chat(user, "There's already something in [title]!") + return + if(istype(W, /obj/item/weapon/pen)) + if(unique) + to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.") + return + var/choice = tgui_input_list(usr, "What would you like to change?", "Change What?", list("Title", "Contents", "Author", "Cancel")) + switch(choice) + if("Title") + var/newtitle = reject_bad_text(sanitizeSafe(tgui_input_text(usr, "Write a new title:"))) + if(!newtitle) + to_chat(usr, "The title is invalid.") + return + else + src.name = newtitle + src.title = newtitle + if("Contents") + var/content = sanitize(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN) + if(!content) + to_chat(usr, "The content is invalid.") + return + else + src.dat += content + if("Author") + var/newauthor = sanitize(tgui_input_text(usr, "Write the author's name:")) + if(!newauthor) + to_chat(usr, "The name is invalid.") + return + else + src.author = newauthor + else + return + else if(istype(W, /obj/item/weapon/barcodescanner)) + var/obj/item/weapon/barcodescanner/scanner = W + if(!scanner.computer) + to_chat(user, "[W]'s screen flashes: 'No associated computer found!'") + else + switch(scanner.mode) + if(0) + scanner.book = src + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'") + if(1) + scanner.book = src + scanner.computer.buffer_book = src.name + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'") + if(2) + scanner.book = src + for(var/datum/borrowbook/b in scanner.computer.checkouts) + if(b.bookname == src.name) + scanner.computer.checkouts.Remove(b) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'") + return + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'") + if(3) + scanner.book = src + for(var/obj/item/weapon/book in scanner.computer.inventory) + if(book == src) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'") + return + scanner.computer.inventory.Add(src) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") + else if(istype(W, /obj/item/weapon/material/knife) || W.has_tool_quality(TOOL_WIRECUTTER)) + if(carved) return + to_chat(user, "You begin to carve out [title].") + if(do_after(user, 30)) + to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") + playsound(src, 'sound/bureaucracy/papercrumple.ogg', 50, 1) + new /obj/item/weapon/shreddedp(get_turf(src)) + carved = 1 + return + else + ..() + +/obj/item/weapon/book/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(user.zone_sel.selecting == O_EYES) + user.visible_message("You open up the book and show it to [M]. ", \ + " [user] opens up a book and shows it to [M]. ") + M << browse("Penned by [author].
                    " + "[dat]", "window=book") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam + +/* +* Book Bundle (Multi-page book) +*/ + +/obj/item/weapon/book/bundle + var/page = 1 //current page + var/list/pages = list() //the contents of each page + +/obj/item/weapon/book/bundle/proc/show_content(mob/user as mob) + if(!pages.len) + return + var/dat + var/obj/item/weapon/W = pages[page] + // first + if(page == 1) + dat+= "" + dat+= "

                    " + // last + else if(page == pages.len) + dat+= "" + dat+= "

                    " + // middle pages + else + dat+= "" + dat+= "

                    " + if(istype(pages[page], /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = W + if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) + dat += "[P.name][stars(P.info)][P.stamps]" + else + dat += "[P.name][P.info][P.stamps]" + user << browse(dat, "window=[name]") + else if(istype(pages[page], /obj/item/weapon/photo)) + var/obj/item/weapon/photo/P = W + user << browse_rsc(P.img, "tmp_photo.png") + user << browse(dat + "[P.name]" \ + + "" \ + + "
                    Written on the back:
                    [P.scribble]" : null]"\ + + "", "window=[name]") + else if(!isnull(pages[page])) + if(!(istype(usr, /mob/living/carbon/human) || isobserver(usr) || istype(usr, /mob/living/silicon))) + dat += "Page [page][stars(pages[page])]" + else + dat += "Page [page][pages[page]]" + user << browse(dat, "window=[name]") + +/obj/item/weapon/book/bundle/attack_self(mob/user as mob) + src.show_content(user) + add_fingerprint(usr) + update_icon() + return + +/obj/item/weapon/book/bundle/Topic(href, href_list) + if(..()) + return 1 + if((src in usr.contents) || (istype(src.loc, /obj/item/weapon/folder) && (src.loc in usr.contents))) + usr.set_machine(src) + if(href_list["next_page"]) + if(page != pages.len) + page++ + playsound(src, "pageturn", 50, 1) + if(href_list["prev_page"]) + if(page > 1) + page-- + playsound(src, "pageturn", 50, 1) + src.attack_self(usr) + updateUsrDialog() + else + to_chat(usr, "You need to hold it in your hands!") + +/* + * Barcode Scanner + */ +/obj/item/weapon/barcodescanner + name = "barcode scanner" + icon = 'icons/obj/library.dmi' + icon_state ="scanner" + throw_speed = 1 + throw_range = 5 + w_class = ITEMSIZE_SMALL + var/obj/machinery/librarycomp/computer // Associated computer - Modes 1 to 3 use this + var/obj/item/weapon/book/book // Currently scanned book + var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory + +/obj/item/weapon/barcodescanner/attack_self(mob/user as mob) + mode += 1 + if(mode > 3) + mode = 0 + to_chat(user, "[src] Status Display:") + var/modedesc + switch(mode) + if(0) + modedesc = "Scan book to local buffer." + if(1) + modedesc = "Scan book to local buffer and set associated computer buffer to match." + if(2) + modedesc = "Scan book to local buffer, attempt to check in scanned book." + if(3) + modedesc = "Scan book to local buffer, attempt to add book to general inventory." + else + modedesc = "ERROR" + to_chat(user, " - Mode [mode] : [modedesc]") + if(src.computer) + to_chat(user, span_green("Computer has been associated with this unit.")) + else + to_chat(user, span_red("No associated computer found. Only local scans will function properly.")) + to_chat(user, "\n") diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 20094948d78..3af5a829f74 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -1,612 +1,612 @@ -/* Library Machines - * - * Contains: - * Borrowbook datum - * Library Public Computer - * Library Computer - * Library Scanner - * Book Binder - */ - -/* - * Borrowbook datum - */ -/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. - var/bookname - var/mobname - var/getdate - var/duedate - -/* - * Library Public Computer - */ -/obj/machinery/librarypubliccomp - name = "visitor computer" - icon = 'icons/obj/library.dmi' - icon_state = "computer" - anchored = TRUE - density = TRUE - var/screenstate = 0 - var/title - var/category = "Any" - var/author - var/SQLquery - -/obj/machinery/librarypubliccomp/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Library Visitor\n" // - switch(screenstate) - if(0) - dat += {"

                    Search Settings


                    - Filter by Title: [title]
                    - Filter by Category: [category]
                    - Filter by Author: [author]
                    - \[Start Search\]
                    "} - if(1) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
                    " - else if(!SQLquery) - dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
                    " - else - dat += {" - "} - - var/DBQuery/query = dbcon_old.NewQuery(SQLquery) - query.Execute() - - while(query.NextRow()) - var/author = query.item[1] - var/title = query.item[2] - var/category = query.item[3] - var/id = query.item[4] - dat += "" - dat += "
                    AUTHORTITLECATEGORYSS13BN
                    [author][title][category][id]

                    " - dat += "\[Go Back\]
                    " - user << browse(dat, "window=publiclibrary") - onclose(user, "publiclibrary") - -/obj/machinery/librarypubliccomp/Topic(href, href_list) - if(..()) - usr << browse(null, "window=publiclibrary") - onclose(usr, "publiclibrary") - return - - if(href_list["settitle"]) - var/newtitle = tgui_input_text(usr, "Enter a title to search for:") - if(newtitle) - title = sanitize(newtitle) - else - title = null - title = sanitizeSQL(title) - if(href_list["setcategory"]) - var/newcategory = tgui_input_list(usr, "Choose a category to search for:", "Category", list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) - if(newcategory) - category = sanitize(newcategory) - else - category = "Any" - category = sanitizeSQL(category) - if(href_list["setauthor"]) - var/newauthor = tgui_input_text(usr, "Enter an author to search for:") - if(newauthor) - author = sanitize(newauthor) - else - author = null - author = sanitizeSQL(author) - if(href_list["search"]) - SQLquery = "SELECT author, title, category, id FROM library WHERE " - if(category == "Any") - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" - else - SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" - screenstate = 1 - - if(href_list["back"]) - screenstate = 0 - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/* - * Library Computer - */ -// TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such -// It is August 22nd, 2012... This TODO has already been here for months.. I wonder how long it'll last before someone does something about it. // Nov 2019. Nope. -/obj/machinery/librarycomp - name = "Check-In/Out Computer" - desc = "Print books from the archives! (You aren't quite sure how they're printed by it, though.)" - icon = 'icons/obj/library.dmi' - icon_state = "computer" - anchored = TRUE - density = TRUE - var/arcanecheckout = 0 - var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book - var/sortby = "author" - var/buffer_book - var/buffer_mob - var/upload_category = "Fiction" - var/list/checkouts = list() - var/list/inventory = list() - var/checkoutperiod = 5 // In minutes - var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive - - var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl - - var/static/list/all_books - - var/static/list/base_genre_books - -/obj/machinery/librarycomp/Initialize() - . = ..() - - if(!base_genre_books || !base_genre_books.len) - base_genre_books = list( - /obj/item/weapon/book/custom_library/fiction, - /obj/item/weapon/book/custom_library/nonfiction, - /obj/item/weapon/book/custom_library/reference, - /obj/item/weapon/book/custom_library/religious, - /obj/item/weapon/book/bundle/custom_library/fiction, - /obj/item/weapon/book/bundle/custom_library/nonfiction, - /obj/item/weapon/book/bundle/custom_library/reference, - /obj/item/weapon/book/bundle/custom_library/religious - ) - - if(!all_books || !all_books.len) - all_books = list() - - for(var/path in subtypesof(/obj/item/weapon/book/codex/lore)) - var/obj/item/weapon/book/C = new path(null) - all_books[C.name] = C - - for(var/path in subtypesof(/obj/item/weapon/book/custom_library) - base_genre_books) - var/obj/item/weapon/book/B = new path(null) - all_books[B.title] = B - - for(var/path in subtypesof(/obj/item/weapon/book/bundle/custom_library) - base_genre_books) - var/obj/item/weapon/book/M = new path(null) - all_books[M.title] = M - -/obj/machinery/librarycomp/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Book Inventory Management\n" // - switch(screenstate) - if(0) - // Main Menu //VOREStation Edit start - dat += {"1. View General Inventory
                    - 2. View Checked Out Inventory
                    - 3. Check out a Book
                    - 4. Connect to Internal Archive
                    - 5. Upload New Title to Archive
                    - 6. Print a Bible
                    - 8. Access External Archive
                    "} //VOREStation Edit end - if(src.emagged) - dat += "7. Access the Forbidden Lore Vault
                    " - if(src.arcanecheckout) - new /obj/item/weapon/book/tome(src.loc) - var/datum/gender/T = gender_datums[user.get_visible_gender()] - to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") - user.visible_message("\The [user] stares at the blank screen for a few moments, [T.his] expression frozen in fear. When [T.he] finally awakens from it, [T.he] looks a lot older.", 2) - src.arcanecheckout = 0 - if(1) - // Inventory - dat += "

                    Inventory


                    " - for(var/obj/item/weapon/book/b in inventory) - dat += "[b.name] (Delete)
                    " - dat += "(Return to main menu)
                    " - if(2) - // Checked Out - dat += "

                    Checked Out Books


                    " - for(var/datum/borrowbook/b in checkouts) - var/timetaken = world.time - b.getdate - //timetaken *= 10 - timetaken /= 600 - timetaken = round(timetaken) - var/timedue = b.duedate - world.time - //timedue *= 10 - timedue /= 600 - if(timedue <= 0) - timedue = "(OVERDUE) [timedue]" - else - timedue = round(timedue) - dat += {"\"[b.bookname]\", Checked out to: [b.mobname]
                    --- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
                    - (Check In)

                    "} - dat += "(Return to main menu)
                    " - if(3) - // Check Out a Book - dat += {"

                    Check Out a Book


                    - Book: [src.buffer_book] - \[Edit\]
                    - Recipient: [src.buffer_mob] - \[Edit\]
                    - Checkout Date : [world.time/600]
                    - Due Date: [(world.time + checkoutperiod)/600]
                    - (Checkout Period: [checkoutperiod] minutes) (+/-) - (Commit Entry)
                    - (Return to main menu)
                    "} - if(4) - dat += "

                    Internal Archive

                    " - if(!all_books || !all_books.len) - dat += "ERROR Something has gone seriously wrong. Contact System Administrator for more information." - else - dat += {" - " - dat += "
                    TITLE\[Order\]
                    " - dat += "
                    (Return to main menu)
                    " - if(5) - //dat += "

                    ERROR

                    " //VOREStation Removal - //dat+= "Library Database is in Secure Management Mode.
                    \ //VOREStation Removal - //Contact a System Administrator for more information.
                    " //VOREStation Removal - //VOREstation Edit Start - dat += "

                    Upload a New Title

                    " - if(!scanner) - for(var/obj/machinery/libraryscanner/S in range(9)) - scanner = S - break - if(!scanner) - dat += "No scanner found within wireless network range.
                    " - else if(!scanner.cache) - dat += "No data found in scanner memory.
                    " - else - dat += {"Data marked for upload...
                    - Title: [scanner.cache.name]
                    "} - if(!scanner.cache.author) - scanner.cache.author = "Anonymous" - dat += {"Author: [scanner.cache.author]
                    - Category: [upload_category]
                    - \[Upload\]
                    "} - //VOREStation Edit End - dat += "(Return to main menu)
                    " - if(7) - dat += {"

                    Accessing Forbidden Lore Vault v 1.3

                    - Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

                    - Yes.
                    - No.
                    "} - if(8) - dat += "

                    External Archive

                    " //VOREStation Edit - establish_old_db_connection() - - //dat += "

                    Warning: System Administrator has slated this archive for removal. Personal uploads should be taken to the NT board of internal literature.

                    " //VOREStation Removal - - if(!dbcon_old.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." - else - dat += {"(Order book by SS13BN)

                    - - " - dat += "
                    TITLE\[Order\]" - if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up. - dat += "\[Del\]" - dat += "
                    " - dat += "
                    (Return to main menu)
                    " - - //dat += "Close

                    " - user << browse(dat, "window=library") - onclose(user, "library") - -//VOREStation Addition Start -/obj/machinery/librarycomp/attack_ghost(mob/user) - - var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE) - if(!show_admin_options) - . = ..() - - else - usr.set_machine(src) - var/dat = "Book Inventory Management\n" // - - dat += "

                    ADMINISTRATIVE MANAGEMENT

                    " - establish_old_db_connection() - - if(!dbcon_old.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." - else - dat += {"(Order book by SS13BN)

                    - - " - dat += "
                    TITLE\[Del\]" - dat += "
                    " - dat += "
                    (Return to main menu)
                    " - - user << browse(dat, "window=library") - onclose(user, "library") -//VOREStation Addition End - -/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user) - if (src.density && !src.emagged) - src.emagged = 1 - return 1 - -/obj/machinery/librarycomp/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/barcodescanner)) - var/obj/item/weapon/barcodescanner/scanner = W - scanner.computer = src - to_chat(user, "[scanner]'s associated machine has been set to [src].") - for (var/mob/V in hearers(src)) - V.show_message("[src] lets out a low, short blip.", 2) - else - ..() - -/obj/machinery/librarycomp/Topic(href, href_list) - if(..()) - usr << browse(null, "window=library") - onclose(usr, "library") - return - - if(href_list["switchscreen"]) - switch(href_list["switchscreen"]) - if("0") - screenstate = 0 - if("1") - screenstate = 1 - if("2") - screenstate = 2 - if("3") - screenstate = 3 - if("4") - screenstate = 4 - if("5") - screenstate = 5 - if("6") - if(!bibledelay) - new /obj/item/weapon/storage/bible(src.loc) - bibledelay = 1 - spawn(60) - bibledelay = 0 - - else - for (var/mob/V in hearers(src)) - V.show_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") - - if("7") - screenstate = 7 - if("8") - screenstate = 8 - if(href_list["arccheckout"]) - if(src.emagged) - src.arcanecheckout = 1 - src.screenstate = 0 - if(href_list["increasetime"]) - checkoutperiod += 1 - if(href_list["decreasetime"]) - checkoutperiod -= 1 - if(checkoutperiod < 1) - checkoutperiod = 1 - if(href_list["editbook"]) - buffer_book = sanitizeSafe(tgui_input_text(usr, "Enter the book's title:")) - if(href_list["editmob"]) - buffer_mob = sanitize(tgui_input_text(usr, "Enter the recipient's name:", null, null, MAX_NAME_LEN), MAX_NAME_LEN) - if(href_list["checkout"]) - var/datum/borrowbook/b = new /datum/borrowbook - b.bookname = sanitizeSafe(buffer_book) - b.mobname = sanitize(buffer_mob) - b.getdate = world.time - b.duedate = world.time + (checkoutperiod * 600) - checkouts.Add(b) - if(href_list["checkin"]) - var/datum/borrowbook/b = locate(href_list["checkin"]) - checkouts.Remove(b) - if(href_list["delbook"]) - var/obj/item/weapon/book/b = locate(href_list["delbook"]) - inventory.Remove(b) - if(href_list["setauthor"]) - var/newauthor = sanitize(tgui_input_text(usr, "Enter the author's name: ")) - if(newauthor) - scanner.cache.author = newauthor - if(href_list["setcategory"]) - var/newcategory = tgui_input_list(usr, "Choose a category: ", "Category", list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) - if(newcategory) - upload_category = newcategory - - //VOREStation Edit Start - if(href_list["upload"]) - if(scanner) - if(scanner.cache) - var/choice = tgui_alert(usr, "Are you certain you wish to upload this title to the Archive?", "Confirmation", list("Confirm", "Abort")) - if(choice == "Confirm") - if(scanner.cache.unique) - tgui_alert_async(usr, "This book has been rejected from the database. Aborting!") - else - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") - else - /* - var/sqltitle = dbcon.Quote(scanner.cache.name) - var/sqlauthor = dbcon.Quote(scanner.cache.author) - var/sqlcontent = dbcon.Quote(scanner.cache.dat) - var/sqlcategory = dbcon.Quote(upload_category) - */ - var/sqltitle = sanitizeSQL(scanner.cache.name) - var/sqlauthor = sanitizeSQL(scanner.cache.author) - var/sqlcontent = sanitizeSQL(scanner.cache.dat) - var/sqlcategory = sanitizeSQL(upload_category) - var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO library (author, title, content, category) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]')") - if(!query.Execute()) - to_chat(usr,query.ErrorMsg()) - else - log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") - tgui_alert_async(usr, "Upload Complete.") - //VOREStation Edit End - - if(href_list["targetid"]) - var/sqlid = sanitizeSQL(href_list["targetid"]) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") - if(bibledelay) - for (var/mob/V in hearers(src)) - V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") - else - bibledelay = 1 - spawn(6) - bibledelay = 0 - var/DBQuery/query = dbcon_old.NewQuery("SELECT * FROM library WHERE id=[sqlid]") - query.Execute() - - while(query.NextRow()) - var/author = query.item[2] - var/title = query.item[3] - var/content = query.item[4] - var/obj/item/weapon/book/B = new(src.loc) - B.name = "Book: [title]" - B.title = title - B.author = author - B.dat = content - B.icon_state = "book[rand(1,16)]" - B.item_state = B.icon_state - src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") - break - - if(href_list["delid"]) - if(!check_rights(R_ADMIN)) - return - var/sqlid = sanitizeSQL(href_list["delid"]) - establish_old_db_connection() - if(!dbcon_old.IsConnected()) - tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") - else - var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE id=[sqlid]") - query.Execute() - log_admin("[usr.key] has deleted the book [sqlid]") //VOREStation Addition - - if(href_list["orderbyid"]) - var/orderid = tgui_input_number(usr, "Enter your order:") - if(orderid) - if(isnum(orderid)) - var/nhref = "src=\ref[src];targetid=[orderid]" - spawn() src.Topic(nhref, params2list(nhref), src) - if(href_list["sort"] in list("author", "title", "category")) - sortby = href_list["sort"] - if(href_list["hardprint"]) - var/newpath = href_list["hardprint"] - var/obj/item/weapon/book/NewBook = new newpath(get_turf(src)) - NewBook.name = "Book: [NewBook.name]" - src.add_fingerprint(usr) - src.updateUsrDialog() - return - -/* - * Library Scanner - */ -/obj/machinery/libraryscanner - name = "scanner" - desc = "A scanner for scanning in books and papers." - icon = 'icons/obj/library.dmi' - icon_state = "bigscanner" - anchored = TRUE - density = TRUE - var/obj/item/weapon/book/cache // Last scanned book - -/obj/machinery/libraryscanner/attackby(var/obj/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/book)) - user.drop_item() - O.loc = src - -/obj/machinery/libraryscanner/attack_hand(var/mob/user as mob) - usr.set_machine(src) - var/dat = "Scanner Control Interface\n" // - if(cache) - dat += "Data stored in memory.
                    " - else - dat += "No data stored in memory.
                    " - dat += "\[Scan\]" - if(cache) - dat += " \[Clear Memory\]

                    \[Remove Book\]" - else - dat += "
                    " - user << browse(dat, "window=scanner") - onclose(user, "scanner") - -/obj/machinery/libraryscanner/Topic(href, href_list) - if(..()) - usr << browse(null, "window=scanner") - onclose(usr, "scanner") - return - - if(href_list["scan"]) - for(var/obj/item/weapon/book/B in contents) - cache = B - break - if(href_list["clear"]) - cache = null - if(href_list["eject"]) - for(var/obj/item/weapon/book/B in contents) - B.loc = src.loc - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/* - * Book binder - */ -/obj/machinery/bookbinder - name = "Book Binder" - desc = "Bundles up a stack of inserted paper into a convenient book format." - icon = 'icons/obj/library.dmi' - icon_state = "binder" - anchored = TRUE - density = TRUE - -/obj/machinery/bookbinder/attackby(var/obj/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/paper) || istype(O, /obj/item/weapon/paper_bundle)) - if(istype(O, /obj/item/weapon/paper)) - user.drop_item() - O.loc = src - user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") - src.visible_message("[src] begins to hum as it warms up its printing drums.") - sleep(rand(200,400)) - src.visible_message("[src] whirs as it prints and binds a new book.") - var/obj/item/weapon/book/b = new(src.loc) - b.dat = O:info - b.name = "Print Job #" + "[rand(100, 999)]" - b.icon_state = "book[rand(1,7)]" - qdel(O) - else - user.drop_item() - O.loc = src - user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") - src.visible_message("[src] begins to hum as it warms up its printing drums.") - sleep(rand(300,500)) - src.visible_message("[src] whirs as it prints and binds a new book.") - var/obj/item/weapon/book/bundle/b = new(src.loc) - b.pages = O:pages - for(var/obj/item/weapon/paper/P in O.contents) - P.forceMove(b) - for(var/obj/item/weapon/photo/P in O.contents) - P.forceMove(b) - b.name = "Print Job #" + "[rand(100, 999)]" - b.icon_state = "book[rand(1,7)]" - qdel(O) - else - ..() +/* Library Machines + * + * Contains: + * Borrowbook datum + * Library Public Computer + * Library Computer + * Library Scanner + * Book Binder + */ + +/* + * Borrowbook datum + */ +/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. + var/bookname + var/mobname + var/getdate + var/duedate + +/* + * Library Public Computer + */ +/obj/machinery/librarypubliccomp + name = "visitor computer" + icon = 'icons/obj/library.dmi' + icon_state = "computer" + anchored = TRUE + density = TRUE + var/screenstate = 0 + var/title + var/category = "Any" + var/author + var/SQLquery + +/obj/machinery/librarypubliccomp/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Library Visitor\n" // + switch(screenstate) + if(0) + dat += {"

                    Search Settings


                    + Filter by Title: [title]
                    + Filter by Category: [category]
                    + Filter by Author: [author]
                    + \[Start Search\]
                    "} + if(1) + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
                    " + else if(!SQLquery) + dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
                    " + else + dat += {" + "} + + var/DBQuery/query = dbcon_old.NewQuery(SQLquery) + query.Execute() + + while(query.NextRow()) + var/author = query.item[1] + var/title = query.item[2] + var/category = query.item[3] + var/id = query.item[4] + dat += "" + dat += "
                    AUTHORTITLECATEGORYSS13BN
                    [author][title][category][id]

                    " + dat += "\[Go Back\]
                    " + user << browse(dat, "window=publiclibrary") + onclose(user, "publiclibrary") + +/obj/machinery/librarypubliccomp/Topic(href, href_list) + if(..()) + usr << browse(null, "window=publiclibrary") + onclose(usr, "publiclibrary") + return + + if(href_list["settitle"]) + var/newtitle = tgui_input_text(usr, "Enter a title to search for:") + if(newtitle) + title = sanitize(newtitle) + else + title = null + title = sanitizeSQL(title) + if(href_list["setcategory"]) + var/newcategory = tgui_input_list(usr, "Choose a category to search for:", "Category", list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) + if(newcategory) + category = sanitize(newcategory) + else + category = "Any" + category = sanitizeSQL(category) + if(href_list["setauthor"]) + var/newauthor = tgui_input_text(usr, "Enter an author to search for:") + if(newauthor) + author = sanitize(newauthor) + else + author = null + author = sanitizeSQL(author) + if(href_list["search"]) + SQLquery = "SELECT author, title, category, id FROM library WHERE " + if(category == "Any") + SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" + else + SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" + screenstate = 1 + + if(href_list["back"]) + screenstate = 0 + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/* + * Library Computer + */ +// TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such +// It is August 22nd, 2012... This TODO has already been here for months.. I wonder how long it'll last before someone does something about it. // Nov 2019. Nope. +/obj/machinery/librarycomp + name = "Check-In/Out Computer" + desc = "Print books from the archives! (You aren't quite sure how they're printed by it, though.)" + icon = 'icons/obj/library.dmi' + icon_state = "computer" + anchored = TRUE + density = TRUE + var/arcanecheckout = 0 + var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book + var/sortby = "author" + var/buffer_book + var/buffer_mob + var/upload_category = "Fiction" + var/list/checkouts = list() + var/list/inventory = list() + var/checkoutperiod = 5 // In minutes + var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive + + var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl + + var/static/list/all_books + + var/static/list/base_genre_books + +/obj/machinery/librarycomp/Initialize() + . = ..() + + if(!base_genre_books || !base_genre_books.len) + base_genre_books = list( + /obj/item/weapon/book/custom_library/fiction, + /obj/item/weapon/book/custom_library/nonfiction, + /obj/item/weapon/book/custom_library/reference, + /obj/item/weapon/book/custom_library/religious, + /obj/item/weapon/book/bundle/custom_library/fiction, + /obj/item/weapon/book/bundle/custom_library/nonfiction, + /obj/item/weapon/book/bundle/custom_library/reference, + /obj/item/weapon/book/bundle/custom_library/religious + ) + + if(!all_books || !all_books.len) + all_books = list() + + for(var/path in subtypesof(/obj/item/weapon/book/codex/lore)) + var/obj/item/weapon/book/C = new path(null) + all_books[C.name] = C + + for(var/path in subtypesof(/obj/item/weapon/book/custom_library) - base_genre_books) + var/obj/item/weapon/book/B = new path(null) + all_books[B.title] = B + + for(var/path in subtypesof(/obj/item/weapon/book/bundle/custom_library) - base_genre_books) + var/obj/item/weapon/book/M = new path(null) + all_books[M.title] = M + +/obj/machinery/librarycomp/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Book Inventory Management\n" // + switch(screenstate) + if(0) + // Main Menu //VOREStation Edit start + dat += {"1. View General Inventory
                    + 2. View Checked Out Inventory
                    + 3. Check out a Book
                    + 4. Connect to Internal Archive
                    + 5. Upload New Title to Archive
                    + 6. Print a Bible
                    + 8. Access External Archive
                    "} //VOREStation Edit end + if(src.emagged) + dat += "7. Access the Forbidden Lore Vault
                    " + if(src.arcanecheckout) + new /obj/item/weapon/book/tome(src.loc) + var/datum/gender/T = gender_datums[user.get_visible_gender()] + to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") + user.visible_message("\The [user] stares at the blank screen for a few moments, [T.his] expression frozen in fear. When [T.he] finally awakens from it, [T.he] looks a lot older.", 2) + src.arcanecheckout = 0 + if(1) + // Inventory + dat += "

                    Inventory


                    " + for(var/obj/item/weapon/book/b in inventory) + dat += "[b.name] (Delete)
                    " + dat += "(Return to main menu)
                    " + if(2) + // Checked Out + dat += "

                    Checked Out Books


                    " + for(var/datum/borrowbook/b in checkouts) + var/timetaken = world.time - b.getdate + //timetaken *= 10 + timetaken /= 600 + timetaken = round(timetaken) + var/timedue = b.duedate - world.time + //timedue *= 10 + timedue /= 600 + if(timedue <= 0) + timedue = "(OVERDUE) [timedue]" + else + timedue = round(timedue) + dat += {"\"[b.bookname]\", Checked out to: [b.mobname]
                    --- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
                    + (Check In)

                    "} + dat += "(Return to main menu)
                    " + if(3) + // Check Out a Book + dat += {"

                    Check Out a Book


                    + Book: [src.buffer_book] + \[Edit\]
                    + Recipient: [src.buffer_mob] + \[Edit\]
                    + Checkout Date : [world.time/600]
                    + Due Date: [(world.time + checkoutperiod)/600]
                    + (Checkout Period: [checkoutperiod] minutes) (+/-) + (Commit Entry)
                    + (Return to main menu)
                    "} + if(4) + dat += "

                    Internal Archive

                    " + if(!all_books || !all_books.len) + dat += "ERROR Something has gone seriously wrong. Contact System Administrator for more information." + else + dat += {" + " + dat += "
                    TITLE\[Order\]
                    " + dat += "
                    (Return to main menu)
                    " + if(5) + //dat += "

                    ERROR

                    " //VOREStation Removal + //dat+= "Library Database is in Secure Management Mode.
                    \ //VOREStation Removal + //Contact a System Administrator for more information.
                    " //VOREStation Removal + //VOREstation Edit Start + dat += "

                    Upload a New Title

                    " + if(!scanner) + for(var/obj/machinery/libraryscanner/S in range(9)) + scanner = S + break + if(!scanner) + dat += "No scanner found within wireless network range.
                    " + else if(!scanner.cache) + dat += "No data found in scanner memory.
                    " + else + dat += {"Data marked for upload...
                    + Title: [scanner.cache.name]
                    "} + if(!scanner.cache.author) + scanner.cache.author = "Anonymous" + dat += {"Author: [scanner.cache.author]
                    + Category: [upload_category]
                    + \[Upload\]
                    "} + //VOREStation Edit End + dat += "(Return to main menu)
                    " + if(7) + dat += {"

                    Accessing Forbidden Lore Vault v 1.3

                    + Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

                    + Yes.
                    + No.
                    "} + if(8) + dat += "

                    External Archive

                    " //VOREStation Edit + establish_old_db_connection() + + //dat += "

                    Warning: System Administrator has slated this archive for removal. Personal uploads should be taken to the NT board of internal literature.

                    " //VOREStation Removal + + if(!dbcon_old.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." + else + dat += {"(Order book by SS13BN)

                    + + " + dat += "
                    TITLE\[Order\]" + if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up. + dat += "\[Del\]" + dat += "
                    " + dat += "
                    (Return to main menu)
                    " + + //dat += "Close

                    " + user << browse(dat, "window=library") + onclose(user, "library") + +//VOREStation Addition Start +/obj/machinery/librarycomp/attack_ghost(mob/user) + + var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE) + if(!show_admin_options) + . = ..() + + else + usr.set_machine(src) + var/dat = "Book Inventory Management\n" // + + dat += "

                    ADMINISTRATIVE MANAGEMENT

                    " + establish_old_db_connection() + + if(!dbcon_old.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." + else + dat += {"(Order book by SS13BN)

                    + + " + dat += "
                    TITLE\[Del\]" + dat += "
                    " + dat += "
                    (Return to main menu)
                    " + + user << browse(dat, "window=library") + onclose(user, "library") +//VOREStation Addition End + +/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user) + if (src.density && !src.emagged) + src.emagged = 1 + return 1 + +/obj/machinery/librarycomp/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/barcodescanner)) + var/obj/item/weapon/barcodescanner/scanner = W + scanner.computer = src + to_chat(user, "[scanner]'s associated machine has been set to [src].") + for (var/mob/V in hearers(src)) + V.show_message("[src] lets out a low, short blip.", 2) + else + ..() + +/obj/machinery/librarycomp/Topic(href, href_list) + if(..()) + usr << browse(null, "window=library") + onclose(usr, "library") + return + + if(href_list["switchscreen"]) + switch(href_list["switchscreen"]) + if("0") + screenstate = 0 + if("1") + screenstate = 1 + if("2") + screenstate = 2 + if("3") + screenstate = 3 + if("4") + screenstate = 4 + if("5") + screenstate = 5 + if("6") + if(!bibledelay) + new /obj/item/weapon/storage/bible(src.loc) + bibledelay = 1 + spawn(60) + bibledelay = 0 + + else + for (var/mob/V in hearers(src)) + V.show_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") + + if("7") + screenstate = 7 + if("8") + screenstate = 8 + if(href_list["arccheckout"]) + if(src.emagged) + src.arcanecheckout = 1 + src.screenstate = 0 + if(href_list["increasetime"]) + checkoutperiod += 1 + if(href_list["decreasetime"]) + checkoutperiod -= 1 + if(checkoutperiod < 1) + checkoutperiod = 1 + if(href_list["editbook"]) + buffer_book = sanitizeSafe(tgui_input_text(usr, "Enter the book's title:")) + if(href_list["editmob"]) + buffer_mob = sanitize(tgui_input_text(usr, "Enter the recipient's name:", null, null, MAX_NAME_LEN), MAX_NAME_LEN) + if(href_list["checkout"]) + var/datum/borrowbook/b = new /datum/borrowbook + b.bookname = sanitizeSafe(buffer_book) + b.mobname = sanitize(buffer_mob) + b.getdate = world.time + b.duedate = world.time + (checkoutperiod * 600) + checkouts.Add(b) + if(href_list["checkin"]) + var/datum/borrowbook/b = locate(href_list["checkin"]) + checkouts.Remove(b) + if(href_list["delbook"]) + var/obj/item/weapon/book/b = locate(href_list["delbook"]) + inventory.Remove(b) + if(href_list["setauthor"]) + var/newauthor = sanitize(tgui_input_text(usr, "Enter the author's name: ")) + if(newauthor) + scanner.cache.author = newauthor + if(href_list["setcategory"]) + var/newcategory = tgui_input_list(usr, "Choose a category: ", "Category", list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) + if(newcategory) + upload_category = newcategory + + //VOREStation Edit Start + if(href_list["upload"]) + if(scanner) + if(scanner.cache) + var/choice = tgui_alert(usr, "Are you certain you wish to upload this title to the Archive?", "Confirmation", list("Confirm", "Abort")) + if(choice == "Confirm") + if(scanner.cache.unique) + tgui_alert_async(usr, "This book has been rejected from the database. Aborting!") + else + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") + else + /* + var/sqltitle = dbcon.Quote(scanner.cache.name) + var/sqlauthor = dbcon.Quote(scanner.cache.author) + var/sqlcontent = dbcon.Quote(scanner.cache.dat) + var/sqlcategory = dbcon.Quote(upload_category) + */ + var/sqltitle = sanitizeSQL(scanner.cache.name) + var/sqlauthor = sanitizeSQL(scanner.cache.author) + var/sqlcontent = sanitizeSQL(scanner.cache.dat) + var/sqlcategory = sanitizeSQL(upload_category) + var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO library (author, title, content, category) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]')") + if(!query.Execute()) + to_chat(usr,query.ErrorMsg()) + else + log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") + tgui_alert_async(usr, "Upload Complete.") + //VOREStation Edit End + + if(href_list["targetid"]) + var/sqlid = sanitizeSQL(href_list["targetid"]) + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") + if(bibledelay) + for (var/mob/V in hearers(src)) + V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") + else + bibledelay = 1 + spawn(6) + bibledelay = 0 + var/DBQuery/query = dbcon_old.NewQuery("SELECT * FROM library WHERE id=[sqlid]") + query.Execute() + + while(query.NextRow()) + var/author = query.item[2] + var/title = query.item[3] + var/content = query.item[4] + var/obj/item/weapon/book/B = new(src.loc) + B.name = "Book: [title]" + B.title = title + B.author = author + B.dat = content + B.icon_state = "book[rand(1,16)]" + B.item_state = B.icon_state + src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") + break + + if(href_list["delid"]) + if(!check_rights(R_ADMIN)) + return + var/sqlid = sanitizeSQL(href_list["delid"]) + establish_old_db_connection() + if(!dbcon_old.IsConnected()) + tgui_alert_async(usr, "Connection to Archive has been severed. Aborting.") + else + var/DBQuery/query = dbcon_old.NewQuery("DELETE FROM library WHERE id=[sqlid]") + query.Execute() + log_admin("[usr.key] has deleted the book [sqlid]") //VOREStation Addition + + if(href_list["orderbyid"]) + var/orderid = tgui_input_number(usr, "Enter your order:") + if(orderid) + if(isnum(orderid)) + var/nhref = "src=\ref[src];targetid=[orderid]" + spawn() src.Topic(nhref, params2list(nhref), src) + if(href_list["sort"] in list("author", "title", "category")) + sortby = href_list["sort"] + if(href_list["hardprint"]) + var/newpath = href_list["hardprint"] + var/obj/item/weapon/book/NewBook = new newpath(get_turf(src)) + NewBook.name = "Book: [NewBook.name]" + src.add_fingerprint(usr) + src.updateUsrDialog() + return + +/* + * Library Scanner + */ +/obj/machinery/libraryscanner + name = "scanner" + desc = "A scanner for scanning in books and papers." + icon = 'icons/obj/library.dmi' + icon_state = "bigscanner" + anchored = TRUE + density = TRUE + var/obj/item/weapon/book/cache // Last scanned book + +/obj/machinery/libraryscanner/attackby(var/obj/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/book)) + user.drop_item() + O.loc = src + +/obj/machinery/libraryscanner/attack_hand(var/mob/user as mob) + usr.set_machine(src) + var/dat = "Scanner Control Interface\n" // + if(cache) + dat += "Data stored in memory.
                    " + else + dat += "No data stored in memory.
                    " + dat += "\[Scan\]" + if(cache) + dat += " \[Clear Memory\]

                    \[Remove Book\]" + else + dat += "
                    " + user << browse(dat, "window=scanner") + onclose(user, "scanner") + +/obj/machinery/libraryscanner/Topic(href, href_list) + if(..()) + usr << browse(null, "window=scanner") + onclose(usr, "scanner") + return + + if(href_list["scan"]) + for(var/obj/item/weapon/book/B in contents) + cache = B + break + if(href_list["clear"]) + cache = null + if(href_list["eject"]) + for(var/obj/item/weapon/book/B in contents) + B.loc = src.loc + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/* + * Book binder + */ +/obj/machinery/bookbinder + name = "Book Binder" + desc = "Bundles up a stack of inserted paper into a convenient book format." + icon = 'icons/obj/library.dmi' + icon_state = "binder" + anchored = TRUE + density = TRUE + +/obj/machinery/bookbinder/attackby(var/obj/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/paper) || istype(O, /obj/item/weapon/paper_bundle)) + if(istype(O, /obj/item/weapon/paper)) + user.drop_item() + O.loc = src + user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") + src.visible_message("[src] begins to hum as it warms up its printing drums.") + sleep(rand(200,400)) + src.visible_message("[src] whirs as it prints and binds a new book.") + var/obj/item/weapon/book/b = new(src.loc) + b.dat = O:info + b.name = "Print Job #" + "[rand(100, 999)]" + b.icon_state = "book[rand(1,7)]" + qdel(O) + else + user.drop_item() + O.loc = src + user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") + src.visible_message("[src] begins to hum as it warms up its printing drums.") + sleep(rand(300,500)) + src.visible_message("[src] whirs as it prints and binds a new book.") + var/obj/item/weapon/book/bundle/b = new(src.loc) + b.pages = O:pages + for(var/obj/item/weapon/paper/P in O.contents) + P.forceMove(b) + for(var/obj/item/weapon/photo/P in O.contents) + P.forceMove(b) + b.name = "Print Job #" + "[rand(100, 999)]" + b.icon_state = "book[rand(1,7)]" + qdel(O) + else + ..() diff --git a/code/modules/library/lib_readme.dm b/code/modules/library/lib_readme.dm index 4237687dfff..0150ed6ba8a 100644 --- a/code/modules/library/lib_readme.dm +++ b/code/modules/library/lib_readme.dm @@ -1,61 +1,61 @@ -//******************************* -// -// Library SQL Configuration -// -//******************************* - -// Deprecated! See global.dm for new SQL config vars -- TLE -/* -#define SQL_ADDRESS "" -#define SQL_DB "" -#define SQL_PORT "3306" -#define SQL_LOGIN "" -#define SQL_PASS "" -*/ - -//******************************* -// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) - - -/* - The Library - ------------ - A place for the crew to go, relax, and enjoy a good book. - Aspiring authors can even self publish and, if they're lucky - convince the on-staff Librarian to submit it to the Archives - to be chronicled in history forever - some say even persisting - through alternate dimensions. - - - Written by TLE for /tg/station 13 - Feel free to use this as you like. Some credit would be cool. - Check us out at http://nanotrasen.com/ if you're so inclined. -*/ - -// CONTAINS: - -// Objects: -// - bookcase -// - book -// - barcode scanner -// Machinery: -// - library computer -// - visitor's computer -// - book binder -// - book scanner -// Datum: -// - borrowbook - - -// Ideas for the future -// --------------------- -// - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) -// -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. -// - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. -// -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). -// The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. -// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. -// - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. -// - Make books/book cases burn when exposed to flame. -// - Make book binder hackable. -// - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. +//******************************* +// +// Library SQL Configuration +// +//******************************* + +// Deprecated! See global.dm for new SQL config vars -- TLE +/* +#define SQL_ADDRESS "" +#define SQL_DB "" +#define SQL_PORT "3306" +#define SQL_LOGIN "" +#define SQL_PASS "" +*/ + +//******************************* +// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) + + +/* + The Library + ------------ + A place for the crew to go, relax, and enjoy a good book. + Aspiring authors can even self publish and, if they're lucky + convince the on-staff Librarian to submit it to the Archives + to be chronicled in history forever - some say even persisting + through alternate dimensions. + + + Written by TLE for /tg/station 13 + Feel free to use this as you like. Some credit would be cool. + Check us out at http://nanotrasen.com/ if you're so inclined. +*/ + +// CONTAINS: + +// Objects: +// - bookcase +// - book +// - barcode scanner +// Machinery: +// - library computer +// - visitor's computer +// - book binder +// - book scanner +// Datum: +// - borrowbook + + +// Ideas for the future +// --------------------- +// - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) +// -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. +// - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. +// -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). +// The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. +// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. +// - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. +// - Make books/book cases burn when exposed to flame. +// - Make book binder hackable. +// - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 45d4004dd9d..155de84b1da 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -1,121 +1,121 @@ -/datum/lighting_object - ///the underlay we are currently applying to our turf to apply light - var/mutable_appearance/current_underlay - - ///whether we are already in the SSlighting.objects_queue list - var/needs_update = FALSE - - ///the turf that our light is applied to - var/turf/affected_turf - -/datum/lighting_object/New(turf/source) - if(!SSlighting.subsystem_initialized) - stack_trace("lighting_object created before SSlighting up!") - return - if(!isturf(source)) - qdel(src, force=TRUE) - stack_trace("a lighting object was assigned to [source], a non turf! ") - return - . = ..() - - current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, PLANE_LIGHTING, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) - - affected_turf = source - if (affected_turf.lighting_object) - qdel(affected_turf.lighting_object, force = TRUE) - stack_trace("a lighting object was assigned to a turf that already had a lighting object!") - - affected_turf.lighting_object = src - affected_turf.set_luminosity(0) - - for(var/turf/space/space_tile in RANGE_TURFS(1, affected_turf)) - space_tile.update_starlight() - - needs_update = TRUE - SSlighting.objects_queue += src - -/datum/lighting_object/Destroy(force) - if (!force) - return QDEL_HINT_LETMELIVE - SSlighting.objects_queue -= src - if (isturf(affected_turf)) - affected_turf.lighting_object = null - affected_turf.set_luminosity(1) - affected_turf.underlays -= current_underlay - affected_turf = null - return ..() - -/datum/lighting_object/proc/update() - - // To the future coder who sees this and thinks - // "Why didn't he just use a loop?" - // Well my man, it's because the loop performed like shit. - // And there's no way to improve it because - // without a loop you can make the list all at once which is the fastest you're gonna get. - // Oh it's also shorter line wise. - // Including with these comments. - - var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new - - var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner - var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner - var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner - var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner - - var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) - - var/rr = red_corner.cache_r - var/rg = red_corner.cache_g - var/rb = red_corner.cache_b - - var/gr = green_corner.cache_r - var/gg = green_corner.cache_g - var/gb = green_corner.cache_b - - var/br = blue_corner.cache_r - var/bg = blue_corner.cache_g - var/bb = blue_corner.cache_b - - var/ar = alpha_corner.cache_r - var/ag = alpha_corner.cache_g - var/ab = alpha_corner.cache_b - - #if LIGHTING_SOFT_THRESHOLD != 0 - var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD - #else - // Because of floating pointsâ„¢?, it won't even be a flat 0. - // This number is mostly arbitrary. - var/set_luminosity = max > 1e-6 - #endif - - if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) - //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case - affected_turf.underlays -= current_underlay - current_underlay.icon_state = "transparent" - current_underlay.color = null - affected_turf.underlays += current_underlay - else if(!set_luminosity) - affected_turf.underlays -= current_underlay - current_underlay.icon_state = "dark" - current_underlay.color = null - affected_turf.underlays += current_underlay - else - affected_turf.underlays -= current_underlay - current_underlay.icon_state = "gradient" - current_underlay.color = list( - rr, rg, rb, 00, - gr, gg, gb, 00, - br, bg, bb, 00, - ar, ag, ab, 00, - 00, 00, 00, 01 - ) - - affected_turf.underlays += current_underlay - - affected_turf.set_luminosity(set_luminosity) - -/datum/lighting_object/proc/removefromturf() - affected_turf.underlays -= current_underlay - -/datum/lighting_object/proc/addtoturf() - affected_turf.underlays += current_underlay +/datum/lighting_object + ///the underlay we are currently applying to our turf to apply light + var/mutable_appearance/current_underlay + + ///whether we are already in the SSlighting.objects_queue list + var/needs_update = FALSE + + ///the turf that our light is applied to + var/turf/affected_turf + +/datum/lighting_object/New(turf/source) + if(!SSlighting.subsystem_initialized) + stack_trace("lighting_object created before SSlighting up!") + return + if(!isturf(source)) + qdel(src, force=TRUE) + stack_trace("a lighting object was assigned to [source], a non turf! ") + return + . = ..() + + current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, PLANE_LIGHTING, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) + + affected_turf = source + if (affected_turf.lighting_object) + qdel(affected_turf.lighting_object, force = TRUE) + stack_trace("a lighting object was assigned to a turf that already had a lighting object!") + + affected_turf.lighting_object = src + affected_turf.set_luminosity(0) + + for(var/turf/space/space_tile in RANGE_TURFS(1, affected_turf)) + space_tile.update_starlight() + + needs_update = TRUE + SSlighting.objects_queue += src + +/datum/lighting_object/Destroy(force) + if (!force) + return QDEL_HINT_LETMELIVE + SSlighting.objects_queue -= src + if (isturf(affected_turf)) + affected_turf.lighting_object = null + affected_turf.set_luminosity(1) + affected_turf.underlays -= current_underlay + affected_turf = null + return ..() + +/datum/lighting_object/proc/update() + + // To the future coder who sees this and thinks + // "Why didn't he just use a loop?" + // Well my man, it's because the loop performed like shit. + // And there's no way to improve it because + // without a loop you can make the list all at once which is the fastest you're gonna get. + // Oh it's also shorter line wise. + // Including with these comments. + + var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new + + var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner + var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner + var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner + var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner + + var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) + + var/rr = red_corner.cache_r + var/rg = red_corner.cache_g + var/rb = red_corner.cache_b + + var/gr = green_corner.cache_r + var/gg = green_corner.cache_g + var/gb = green_corner.cache_b + + var/br = blue_corner.cache_r + var/bg = blue_corner.cache_g + var/bb = blue_corner.cache_b + + var/ar = alpha_corner.cache_r + var/ag = alpha_corner.cache_g + var/ab = alpha_corner.cache_b + + #if LIGHTING_SOFT_THRESHOLD != 0 + var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD + #else + // Because of floating pointsâ„¢?, it won't even be a flat 0. + // This number is mostly arbitrary. + var/set_luminosity = max > 1e-6 + #endif + + if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) + //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "transparent" + current_underlay.color = null + affected_turf.underlays += current_underlay + else if(!set_luminosity) + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "dark" + current_underlay.color = null + affected_turf.underlays += current_underlay + else + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "gradient" + current_underlay.color = list( + rr, rg, rb, 00, + gr, gg, gb, 00, + br, bg, bb, 00, + ar, ag, ab, 00, + 00, 00, 00, 01 + ) + + affected_turf.underlays += current_underlay + + affected_turf.set_luminosity(set_luminosity) + +/datum/lighting_object/proc/removefromturf() + affected_turf.underlays -= current_underlay + +/datum/lighting_object/proc/addtoturf() + affected_turf.underlays += current_underlay diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 6ef942836bd..4ba09f11f23 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,156 +1,156 @@ -/turf - ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. - var/dynamic_lumcount = 0 - - var/dynamic_lighting = TRUE - - var/tmp/lighting_corners_initialised = FALSE - - var/tmp/outdoors_adjacent = FALSE - ///Our lighting object. - var/tmp/datum/lighting_object/lighting_object - ///Lighting Corner datums. - var/tmp/datum/lighting_corner/lighting_corner_NE - var/tmp/datum/lighting_corner/lighting_corner_SE - var/tmp/datum/lighting_corner/lighting_corner_SW - var/tmp/datum/lighting_corner/lighting_corner_NW - - ///Which directions does this turf block the vision of, taking into account both the turf's opacity and the movable opacity_sources. - var/directional_opacity = NONE - ///Lazylist of movable atoms providing opacity sources. - var/list/atom/movable/opacity_sources - -// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. -/turf/proc/reconsider_lights() - lighting_corner_NE?.vis_update() - lighting_corner_SE?.vis_update() - lighting_corner_SW?.vis_update() - lighting_corner_NW?.vis_update() - -/turf/proc/lighting_clear_overlay() - if(lighting_object) - qdel(lighting_object, force=TRUE) - -// Builds a lighting object for us, but only if our area is dynamic. -/turf/proc/lighting_build_overlay() - if(!has_dynamic_lighting()) - return - - lighting_clear_overlay() - new/datum/lighting_object(src) - -// Used to get a scaled lumcount. -/turf/proc/get_lumcount(minlum = 0, maxlum = 1) - if (!lighting_object) - return 1 - - var/totallums = 0 - var/datum/lighting_corner/L - L = lighting_corner_NE - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - L = lighting_corner_SE - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - L = lighting_corner_SW - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - L = lighting_corner_NW - if (L) - totallums += L.lum_r + L.lum_b + L.lum_g - - - totallums /= 12 // 4 corners, each with 3 channels, get the average. - - totallums = (totallums - minlum) / (maxlum - minlum) - - totallums += dynamic_lumcount - - return CLAMP01(totallums) - -// Returns a boolean whether the turf is on soft lighting. -// Soft lighting being the threshold at which point the overlay considers -// itself as too dark to allow sight and see_in_dark becomes useful. -// So basically if this returns true the tile is unlit black. -/turf/proc/is_softly_lit() - if (!lighting_object) - return FALSE - - return !(luminosity || dynamic_lumcount) - - -///Proc to add movable sources of opacity on the turf and let it handle lighting code. -/turf/proc/add_opacity_source(atom/movable/new_source) - LAZYADD(opacity_sources, new_source) - if(opacity) - return - recalculate_directional_opacity() - - -///Proc to remove movable sources of opacity on the turf and let it handle lighting code. -/turf/proc/remove_opacity_source(atom/movable/old_source) - LAZYREMOVE(opacity_sources, old_source) - if(opacity) //Still opaque, no need to worry on updating. - return - recalculate_directional_opacity() - -///Setter for the byond luminosity var -/turf/proc/set_luminosity(new_luminosity, force) - if((is_outdoors() && !force) || outdoors_adjacent) - if(check_for_sun()) //If another system handles our lighting, don't interfere - return - - luminosity = new_luminosity - -///Checks planets and fake_suns to see if our turf should be handled by either -/turf/proc/check_for_sun() - if((SSplanets && SSplanets.z_to_planet.len >= z && SSplanets.z_to_planet[z]) || (z in fake_sunlight_zs)) - return TRUE - return FALSE - -///Calculate on which directions this turfs block view. -/turf/proc/recalculate_directional_opacity() - . = directional_opacity - if(opacity) - directional_opacity = ALL_CARDINALS - if(. != directional_opacity) - reconsider_lights() - return - directional_opacity = NONE - for(var/atom/movable/opacity_source as anything in opacity_sources) - if(opacity_source && opacity_source.flags & ON_BORDER) - directional_opacity |= opacity_source.dir - else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking. - directional_opacity = ALL_CARDINALS - break - if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS)) - reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not. - - -/turf/proc/change_area(area/old_area, area/new_area) - if(SSlighting.subsystem_initialized) - if (new_area.dynamic_lighting != old_area.dynamic_lighting) - if (new_area.dynamic_lighting) - lighting_build_overlay() - else - lighting_clear_overlay() - -/turf/proc/has_dynamic_lighting() - var/area/A = loc - return (IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) - -/turf/proc/generate_missing_corners() - - if (!lighting_corner_NE) - lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST) - - if (!lighting_corner_SE) - lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST) - - if (!lighting_corner_SW) - lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST) - - if (!lighting_corner_NW) - lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) - - lighting_corners_initialised = TRUE +/turf + ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. + var/dynamic_lumcount = 0 + + var/dynamic_lighting = TRUE + + var/tmp/lighting_corners_initialised = FALSE + + var/tmp/outdoors_adjacent = FALSE + ///Our lighting object. + var/tmp/datum/lighting_object/lighting_object + ///Lighting Corner datums. + var/tmp/datum/lighting_corner/lighting_corner_NE + var/tmp/datum/lighting_corner/lighting_corner_SE + var/tmp/datum/lighting_corner/lighting_corner_SW + var/tmp/datum/lighting_corner/lighting_corner_NW + + ///Which directions does this turf block the vision of, taking into account both the turf's opacity and the movable opacity_sources. + var/directional_opacity = NONE + ///Lazylist of movable atoms providing opacity sources. + var/list/atom/movable/opacity_sources + +// Causes any affecting light sources to be queued for a visibility update, for example a door got opened. +/turf/proc/reconsider_lights() + lighting_corner_NE?.vis_update() + lighting_corner_SE?.vis_update() + lighting_corner_SW?.vis_update() + lighting_corner_NW?.vis_update() + +/turf/proc/lighting_clear_overlay() + if(lighting_object) + qdel(lighting_object, force=TRUE) + +// Builds a lighting object for us, but only if our area is dynamic. +/turf/proc/lighting_build_overlay() + if(!has_dynamic_lighting()) + return + + lighting_clear_overlay() + new/datum/lighting_object(src) + +// Used to get a scaled lumcount. +/turf/proc/get_lumcount(minlum = 0, maxlum = 1) + if (!lighting_object) + return 1 + + var/totallums = 0 + var/datum/lighting_corner/L + L = lighting_corner_NE + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_SE + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_SW + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_NW + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + + + totallums /= 12 // 4 corners, each with 3 channels, get the average. + + totallums = (totallums - minlum) / (maxlum - minlum) + + totallums += dynamic_lumcount + + return CLAMP01(totallums) + +// Returns a boolean whether the turf is on soft lighting. +// Soft lighting being the threshold at which point the overlay considers +// itself as too dark to allow sight and see_in_dark becomes useful. +// So basically if this returns true the tile is unlit black. +/turf/proc/is_softly_lit() + if (!lighting_object) + return FALSE + + return !(luminosity || dynamic_lumcount) + + +///Proc to add movable sources of opacity on the turf and let it handle lighting code. +/turf/proc/add_opacity_source(atom/movable/new_source) + LAZYADD(opacity_sources, new_source) + if(opacity) + return + recalculate_directional_opacity() + + +///Proc to remove movable sources of opacity on the turf and let it handle lighting code. +/turf/proc/remove_opacity_source(atom/movable/old_source) + LAZYREMOVE(opacity_sources, old_source) + if(opacity) //Still opaque, no need to worry on updating. + return + recalculate_directional_opacity() + +///Setter for the byond luminosity var +/turf/proc/set_luminosity(new_luminosity, force) + if((is_outdoors() && !force) || outdoors_adjacent) + if(check_for_sun()) //If another system handles our lighting, don't interfere + return + + luminosity = new_luminosity + +///Checks planets and fake_suns to see if our turf should be handled by either +/turf/proc/check_for_sun() + if((SSplanets && SSplanets.z_to_planet.len >= z && SSplanets.z_to_planet[z]) || (z in fake_sunlight_zs)) + return TRUE + return FALSE + +///Calculate on which directions this turfs block view. +/turf/proc/recalculate_directional_opacity() + . = directional_opacity + if(opacity) + directional_opacity = ALL_CARDINALS + if(. != directional_opacity) + reconsider_lights() + return + directional_opacity = NONE + for(var/atom/movable/opacity_source as anything in opacity_sources) + if(opacity_source && opacity_source.flags & ON_BORDER) + directional_opacity |= opacity_source.dir + else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking. + directional_opacity = ALL_CARDINALS + break + if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS)) + reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not. + + +/turf/proc/change_area(area/old_area, area/new_area) + if(SSlighting.subsystem_initialized) + if (new_area.dynamic_lighting != old_area.dynamic_lighting) + if (new_area.dynamic_lighting) + lighting_build_overlay() + else + lighting_clear_overlay() + +/turf/proc/has_dynamic_lighting() + var/area/A = loc + return (IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) + +/turf/proc/generate_missing_corners() + + if (!lighting_corner_NE) + lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST) + + if (!lighting_corner_SE) + lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST) + + if (!lighting_corner_SW) + lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST) + + if (!lighting_corner_NW) + lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) + + lighting_corners_initialised = TRUE diff --git a/code/modules/lore_codex/codex.dm b/code/modules/lore_codex/codex.dm index 12fa8dbdfb1..71ec091dcb7 100644 --- a/code/modules/lore_codex/codex.dm +++ b/code/modules/lore_codex/codex.dm @@ -9,13 +9,21 @@ var/datum/codex_tree/tree = null var/root_type = /datum/lore/codex/category/main_virgo_lore //Runtimes on codex_tree.dm, line 18 with a null here + var/static/list/codex_tree_keys = list() // static list linking codexes to the correct codex_tree. + /obj/item/weapon/book/codex/Initialize() - tree = new(src, root_type) + tree = codex_tree_keys["[root_type]"] + if(!tree) + tree = new(src, root_type) + codex_tree_keys["[root_type]"] = tree . = ..() /obj/item/weapon/book/codex/attack_self(mob/user) if(!tree) - tree = new(src, root_type) + tree = codex_tree_keys["[root_type]"] + if(!tree) + tree = new(src, root_type) + codex_tree_keys["[root_type]"] = tree icon_state = "[initial(icon_state)]-open" tree.display(user) diff --git a/code/modules/lore_codex/codex_tree.dm b/code/modules/lore_codex/codex_tree.dm index 71aa80b0870..200a1ec4f09 100644 --- a/code/modules/lore_codex/codex_tree.dm +++ b/code/modules/lore_codex/codex_tree.dm @@ -4,9 +4,9 @@ var/atom/movable/holder = null var/root_type = null var/datum/lore/codex/home = null // Top-most page. - var/datum/lore/codex/current_page = null // Current page or category to display to the user. + var/list/current_page = list() // Current page or category to display to the user. // converted to list to track multiple players. var/list/indexed_pages = list() // Assoc list with search terms pointing to a ref of the page. It's created on New(). - var/list/history = list() // List of pages we previously visited. + var/list/history = list() // List of pages we previously visited. // now a 2D list /datum/codex_tree/New(var/new_holder, var/new_root_type) holder = new_holder @@ -16,25 +16,31 @@ /datum/codex_tree/proc/generate_pages() home = new root_type(src) // This will also generate the others. - current_page = home - indexed_pages = current_page.index_page() + //current_page = home + indexed_pages = home.index_page() // changed from current_page to home. // Changes current_page to its parent, assuming one exists. -/datum/codex_tree/proc/go_to_parent() - if(current_page && current_page.parent) - current_page = current_page.parent +/datum/codex_tree/proc/go_to_parent(var/mob/user) + var/datum/lore/codex/D = current_page["[user]"] + if(istype(D) && D.parent) + current_page["[user]"] = D.parent // Changes current_page to a specific page or category. -/datum/codex_tree/proc/go_to_page(var/datum/lore/codex/new_page, var/dont_record_history = FALSE) - if(new_page) // Make sure we're not going to a null page for whatever reason. - current_page = new_page +/datum/codex_tree/proc/go_to_page(var/datum/lore/codex/new_page, var/dont_record_history = FALSE, var/mob/user) + var/datum/lore/codex/D = current_page["[user]"] + if(new_page && istype(D)) // Make sure we're not going to a null page for whatever reason. + current_page["[user]"] = new_page if(!dont_record_history) - history.Add(new_page) + var/list/H = history["[user]"] + if(!H) + H = list() + H.Add(new_page) + history["[user]"] = H -/datum/codex_tree/proc/quick_link(var/search_word) +/datum/codex_tree/proc/quick_link(var/search_word, var/mob/user) for(var/word in indexed_pages) if(lowertext(search_word) == lowertext(word)) // Exact matches unfortunately limit our ability to perform SEOs. - go_to_page(indexed_pages[word]) + go_to_page(indexed_pages[word], FALSE, user) return /datum/codex_tree/proc/get_page_from_type(var/desired_type) @@ -45,16 +51,26 @@ return null // Returns to the last visited page, based on the history list. -/datum/codex_tree/proc/go_back() - if((history.len - 1) > 0) - if(history[history.len] == current_page) - history.len-- // This gets rid of the current page in the history. - go_to_page(pop(history), dont_record_history = TRUE) // Where as this will get us the previous page that we want to go to. - -/datum/codex_tree/proc/get_tree_position() - if(current_page) +/datum/codex_tree/proc/go_back(var/mob/user) + var/list/H = history["[user]"] + var/datum/lore/codex/D = current_page["[user]"] + if(!LAZYLEN(H) || !istype(D)) + return + if((H.len) > 1) + if(H[H.len] == D) + H.len-- // This gets rid of the current page in the history. + history["[user]"] = H + if(H.len == 1) + go_to_page(H[H.len], TRUE, user) + return + go_to_page(pop(history["[user]"]), TRUE, user) // Where as this will get us the previous page that we want to go to. + else + go_to_page(H[H.len], TRUE, user) + +/datum/codex_tree/proc/get_tree_position(var/mob/user) + var/datum/lore/codex/checked = current_page["[user]"] + if(istype(checked)) var/output = "" - var/datum/lore/codex/checked = current_page output = "[checked.name]" while(checked.parent) output = "[checked.parent.name] \> [output]" @@ -75,38 +91,53 @@ /datum/codex_tree/proc/display(mob/user) // icon_state = "[initial(icon_state)]-open" - if(!current_page) + if(!home) generate_pages() + if(!user) + return + var/datum/lore/codex/D = current_page["[user]"] + if(!istype(D)) // Initialize current_page and history + current_page["[user]"] = home + D = current_page["[user]"] + if(!istype(D)) + log_debug("Codex_tree failed to failed to load for [user].") + return + var/list/H_init = list() + H_init.Add(home) + history["[user]"] = H_init + //if(!current_page) + //generate_pages() user << browse_rsc('html/browser/codex.css', "codex.css") var/dat dat = "" - dat += "[holder.name] ([current_page.name])" + dat += "[holder.name] ([D.name])" dat += "" dat += "" dat += "" - dat += "[get_tree_position()]
                    " + dat += "[get_tree_position(user)]
                    " dat += "[make_search_bar()]
                    " dat += "
                    " - dat += "

                    [current_page.name]

                    " + dat += "

                    [D.name]

                    " dat += "
                    " - if(current_page.data) - dat += "[current_page.data]
                    " + if(D.data) + dat += "[D.data]
                    " dat += "
                    " - if(istype(current_page, /datum/lore/codex/category)) + if(istype(D, /datum/lore/codex/category)) dat += "
                    " - var/datum/lore/codex/category/C = current_page + var/datum/lore/codex/category/C = D for(var/datum/lore/codex/child in C.children) dat += "[child.name]" dat += "
                    " dat += "
                    " - if(history.len - 1) + var/list/H = history["[user]"] + if(LAZYLEN(H)) dat += "
                    \[Go Back\]" - if(current_page.parent) + if(D.parent) dat += "
                    \[Go Up\]" - if(current_page != home) + if(D != home) dat += "
                    \[Go To Home\]" dat += "
                    " user << browse(dat, "window=the_empress_protects;size=600x550") @@ -120,21 +151,21 @@ if(href_list["target"]) // Direct link, using a ref var/datum/lore/codex/new_page = locate(href_list["target"]) - go_to_page(new_page) + go_to_page(new_page, FALSE, usr) else if(href_list["search_query"]) - quick_link(href_list["search_query"]) + quick_link(href_list["search_query"], usr) else if(href_list["go_to_parent"]) - go_to_parent() + go_to_parent(usr) else if(href_list["go_back"]) - go_back() + go_back(usr) else if(href_list["go_to_home"]) - go_to_page(home) + go_to_page(home, FALSE, usr) else if(href_list["quick_link"]) // Indirect link, using a (hopefully) indexed word. - quick_link(href_list["quick_link"]) + quick_link(href_list["quick_link"], usr) else if(href_list["close"]) // Close the book, if our holder is actually a book. - if(istype(holder, /obj/item/weapon/book/codex)) - holder.icon_state = initial(holder.icon_state) + //if(istype(holder, /obj/item/weapon/book/codex)) + //holder.icon_state = initial(holder.icon_state) usr << browse(null, "window=the_empress_protects") return - display(usr) \ No newline at end of file + display(usr) diff --git a/code/modules/maps/swapmaps.dm b/code/modules/maps/swapmaps.dm index 5ec962f5c91..629ea0042c4 100644 --- a/code/modules/maps/swapmaps.dm +++ b/code/modules/maps/swapmaps.dm @@ -1,678 +1,678 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/* - SwapMaps library by Lummox JR - developed for digitalBYOND - http://www.digitalbyond.org - - Version 2.1 - - The purpose of this library is to make it easy for authors to swap maps - in and out of their game using savefiles. Swapped-out maps can be - transferred between worlds for an MMORPG, sent to the client, etc. - This is facilitated by the use of a special datum and a global list. - - Uses of swapmaps: - - - Temporary battle arenas - - House interiors - - Individual custom player houses - - Virtually unlimited terrain - - Sharing maps between servers running different instances of the same - game - - Loading and saving pieces of maps for reusable room templates - */ - -/* - User Interface: - - VARS: - - swapmaps_iconcache - An associative list of icon files with names, like - 'player.dmi' = "player" - swapmaps_mode - This must be set at runtime, like in world/New(). - - SWAPMAPS_SAV 0 (default) - Uses .sav files for raw /savefile output. - SWAPMAPS_TEXT 1 - Uses .txt files via ExportText() and ImportText(). These maps - are easily editable and appear to take up less space in the - current version of BYOND. - - PROCS: - - SwapMaps_Find(id) - Find a map by its id - SwapMaps_Load(id) - Load a map by its id - SwapMaps_Save(id) - Save a map by its id (calls swapmap.Save()) - SwapMaps_Unload(id) - Save and unload a map by its id (calls swapmap.Unload()) - SwapMaps_Save_All() - Save all maps - SwapMaps_DeleteFile(id) - Delete a map file - SwapMaps_CreateFromTemplate(id) - Create a new map by loading another map to use as a template. - This map has id==src and will not be saved. To make it savable, - change id with swapmap.SetID(newid). - SwapMaps_LoadChunk(id,turf/locorner) - Load a swapmap as a "chunk", at a specific place. A new datum is - created but it's not added to the list of maps to save or unload. - The new datum can be safely deleted without affecting the turfs - it loaded. The purpose of this is to load a map file onto part of - another swapmap or an existing part of the world. - locorner is the corner turf with the lowest x,y,z values. - SwapMaps_SaveChunk(id,turf/corner1,turf/corner2) - Save a piece of the world as a "chunk". A new datum is created - for the chunk, but it can be deleted without destroying any turfs. - The chunk file can be reloaded as a swapmap all its own, or loaded - via SwapMaps_LoadChunk() to become part of another map. - SwapMaps_GetSize(id) - Return a list corresponding to the x,y,z sizes of a map file, - without loading the map. - Returns null if the map is not found. - SwapMaps_AddIconToCache(name,icon) - Cache an icon file by name for space-saving storage - - swapmap.New(id,x,y,z) - Create a new map; specify id, width (x), height (y), and - depth (z) - Default size is world.maxx,world.maxy,1 - swapmap.New(id,turf1,turf2) - Create a new map; specify id and 2 corners - This becomes a /swapmap for one of the compiled-in maps, for - easy saving. - swapmap.New() - Create a new map datum, but does not allocate space or assign an - ID (used for loading). - swapmap.Del() - Deletes a map but does not save - swapmap.Save() - Saves to map_[id].sav - Maps with id==src are not saved. - swapmap.Unload() - Saves the map and then deletes it - Maps with id==src are not saved. - swapmap.SetID(id) - Change the map's id and make changes to the lookup list - swapmap.AllTurfs(z) - Returns a block of turfs encompassing the entire map, or on just - one z-level - z is in world coordinates; it is optional - swapmap.Contains(turf/T) - Returns nonzero if T is inside the map's boundaries. - Also works for objs and mobs, but the proc is not area-safe. - swapmap.InUse() - Returns nonzero if a mob with a key is within the map's - boundaries. - swapmap.LoCorner(z=z1) - Returns locate(x1,y1,z), where z=z1 if none is specified. - swapmap.HiCorner(z=z2) - Returns locate(x2,y2,z), where z=z2 if none is specified. - swapmap.BuildFilledRectangle(turf/corner1,turf/corner2,item) - Builds a filled rectangle of item from one corner turf to the - other, on multiple z-levels if necessary. The corners may be - specified in any order. - item is a type path like /turf/wall or /obj/barrel{full=1}. - swapmap.BuildRectangle(turf/corner1,turf/corner2,item) - Builds an unfilled rectangle of item from one corner turf to - the other, on multiple z-levels if necessary. - swapmap.BuildInTurfs(list/turfs,item) - Builds item on all of the turfs listed. The list need not - contain only turfs, or even only atoms. - */ - -swapmap - var/id // a string identifying this map uniquely - var/x1 // minimum x,y,z coords - var/y1 - var/z1 - var/x2 // maximum x,y,z coords (also used as width,height,depth until positioned) - var/y2 - var/z2 - var/tmp/locked // don't move anyone to this map; it's saving or loading - var/tmp/mode // save as text-mode - var/ischunk // tells the load routine to load to the specified location - - New(_id,x,y,z) - if(isnull(_id)) return - id=_id - mode=swapmaps_mode - if(isturf(x) && isturf(y)) - /* - Special format: Defines a map as an existing set of turfs; - this is useful for saving a compiled map in swapmap format. - Because this is a compiled-in map, its turfs are not deleted - when the datum is deleted. - */ - x1=min(x:x,y:x);x2=max(x:x,y:x) - y1=min(x:y,y:y);y2=max(x:y,y:y) - z1=min(x:z,y:z);z2=max(x:z,y:z) - InitializeSwapMaps() - if(z2>swapmaps_compiled_maxz ||\ - y2>swapmaps_compiled_maxy ||\ - x2>swapmaps_compiled_maxx) - qdel(src) - return - x2=x?(x):world.maxx - y2=y?(y):world.maxy - z2=z?(z):1 - AllocateSwapMap() - - Del() - // a temporary datum for a chunk can be deleted outright - // for others, some cleanup is necessary - if(!ischunk) - swapmaps_loaded-=src - swapmaps_byname-=id - if(z2>swapmaps_compiled_maxz ||\ - y2>swapmaps_compiled_maxy ||\ - x2>swapmaps_compiled_maxx) - var/list/areas=new - for(var/atom/A in block(locate(x1,y1,z1),locate(x2,y2,z2))) - for(var/obj/O in A) qdel(O) - for(var/mob/M in A) - if(!M.key) qdel(M) - else M.loc=null - areas[A.loc]=null - qdel(A) - // delete areas that belong only to this map - for(var/area/a in areas) - if(a && !a.contents.len) qdel(a) - if(x2>=world.maxx || y2>=world.maxy || z2>=world.maxz) CutXYZ() - qdel(areas) - ..() - - /* - Savefile format: - map - id - x // size, not coords - y - z - areas // list of areas, not including default - [each z; 1 to depth] - [each y; 1 to height] - [each x; 1 to width] - type // of turf - AREA // if non-default; saved as a number (index into areas list) - vars // all other changed vars - */ - Write(savefile/S) - var/x - var/y - var/z - var/n - var/list/areas - var/area/defarea=locate(world.area) - if(!defarea) defarea=new world.area - areas=list() - for(var/turf/T in block(locate(x1,y1,z1),locate(x2,y2,z2))) - areas[T.loc]=null - for(n in areas) // quickly eliminate associations for smaller storage - areas-=n - areas+=n - areas-=defarea - InitializeSwapMaps() - locked=1 - S["id"] << id - S["z"] << z2-z1+1 - S["y"] << y2-y1+1 - S["x"] << x2-x1+1 - S["areas"] << areas - for(n in 1 to areas.len) areas[areas[n]]=n - var/oldcd=S.cd - for(z=z1,z<=z2,++z) - S.cd="[z-z1+1]" - for(y=y1,y<=y2,++y) - S.cd="[y-y1+1]" - for(x=x1,x<=x2,++x) - S.cd="[x-x1+1]" - var/turf/T=locate(x,y,z) - S["type"] << T.type - if(T.loc!=defarea) S["AREA"] << areas[T.loc] - T.Write(S) - S.cd=".." - S.cd=".." - sleep() - S.cd=oldcd - locked=0 - qdel(areas) - - Read(savefile/S,_id,turf/locorner) - var/x - var/y - var/z - var/n - var/list/areas - var/area/defarea=locate(world.area) - id=_id - if(locorner) - ischunk=1 - x1=locorner.x - y1=locorner.y - z1=locorner.z - if(!defarea) defarea=new world.area - if(!_id) - S["id"] >> id - else - var/dummy - S["id"] >> dummy - S["z"] >> z2 // these are depth, - S["y"] >> y2 // height, - S["x"] >> x2 // width - S["areas"] >> areas - locked=1 - AllocateSwapMap() // adjust x1,y1,z1 - x2,y2,z2 coords - var/oldcd=S.cd - for(z=z1,z<=z2,++z) - S.cd="[z-z1+1]" - for(y=y1,y<=y2,++y) - S.cd="[y-y1+1]" - for(x=x1,x<=x2,++x) - S.cd="[x-x1+1]" - var/tp - S["type"]>>tp - var/turf/T=locate(x,y,z) - T.loc.contents-=T - T=new tp(locate(x,y,z)) - if("AREA" in S.dir) - S["AREA"]>>n - var/area/A=areas[n] - A.contents+=T - else defarea.contents+=T - // clear the turf - for(var/obj/O in T) qdel(O) - for(var/mob/M in T) - if(!M.key) qdel(M) - else M.loc=null - // finish the read - T.Read(S) - S.cd=".." - S.cd=".." - sleep() - S.cd=oldcd - locked=0 - qdel(areas) - - /* - Find an empty block on the world map in which to load this map. - If no space is found, increase world.maxz as necessary. (If the - map is greater in x,y size than the current world, expand - world.maxx and world.maxy too.) - - Ignore certain operations if loading a map as a chunk. Use the - x1,y1,z1 position for it, and *don't* count it as a loaded map. - */ - proc/AllocateSwapMap() - InitializeSwapMaps() - world.maxx=max(x2,world.maxx) // stretch x/y if necessary - world.maxy=max(y2,world.maxy) - if(!ischunk) - if(world.maxz<=swapmaps_compiled_maxz) - z1=swapmaps_compiled_maxz+1 - x1=1;y1=1 - else - var/list/l=ConsiderRegion(1,1,world.maxx,world.maxy,swapmaps_compiled_maxz+1) - x1=l[1] - y1=l[2] - z1=l[3] - qdel(l) - x2+=x1-1 - y2+=y1-1 - z2+=z1-1 - world.maxz=max(z2,world.maxz) // stretch z if necessary - if(!ischunk) - swapmaps_loaded[src]=null - swapmaps_byname[id]=src - - proc/ConsiderRegion(X1,Y1,X2,Y2,Z1,Z2) - while(1) - var/nextz=0 - var/swapmap/M - for(M in swapmaps_loaded) - if(M.z2Z2) || M.z1>=Z1+z2 ||\ - M.x1>X2 || M.x2=X1+x2 ||\ - M.y1>Y2 || M.y2=Y1+y2) continue - // look for sub-regions with a defined ceiling - var/nz2=Z2?(Z2):Z1+z2-1+M.z2-M.z1 - if(M.x1>=X1+x2) - .=ConsiderRegion(X1,Y1,M.x1-1,Y2,Z1,nz2) - if(.) return - else if(M.x2<=X2-x2) - .=ConsiderRegion(M.x2+1,Y1,X2,Y2,Z1,nz2) - if(.) return - if(M.y1>=Y1+y2) - .=ConsiderRegion(X1,Y1,X2,M.y1-1,Z1,nz2) - if(.) return - else if(M.y2<=Y2-y2) - .=ConsiderRegion(X1,M.y2+1,X2,Y2,Z1,nz2) - if(.) return - nextz=nextz?min(nextz,M.z2+1):(M.z2+1) - if(!M) - /* If nextz is not 0, then at some point there was an overlap that - could not be resolved by using an area to the side */ - if(nextz) Z1=nextz - if(!nextz || (Z2 && Z2-Z1+1=z2)?list(X1,Y1,Z1):null - X1=1;X2=world.maxx - Y1=1;Y2=world.maxy - - proc/CutXYZ() - var/mx=swapmaps_compiled_maxx - var/my=swapmaps_compiled_maxy - var/mz=swapmaps_compiled_maxz - for(var/swapmap/M in swapmaps_loaded) // may not include src - mx=max(mx,M.x2) - my=max(my,M.y2) - mz=max(mz,M.z2) - world.maxx=mx - world.maxy=my - world.maxz=mz - - // save and delete - proc/Unload() - Save() - qdel(src) - - proc/Save() - if(id==src) return 0 - var/savefile/S=mode?(new):new("map_[id].sav") - S << src - while(locked) sleep(1) - if(mode) - fdel("map_[id].txt") - S.ExportText("/","map_[id].txt") - return 1 - - // this will not delete existing savefiles for this map - proc/SetID(newid) - swapmaps_byname-=id - id=newid - swapmaps_byname[id]=src - - proc/AllTurfs(z) - if(isnum(z) && (zz2)) return null - return block(LoCorner(z),HiCorner(z)) - - // this could be safely called for an obj or mob as well, but - // probably not an area - proc/Contains(turf/T) - return (T && T.x>=x1 && T.x<=x2\ - && T.y>=y1 && T.y<=y2\ - && T.z>=z1 && T.z<=z2) - - proc/InUse() - for(var/turf/T in AllTurfs()) - for(var/mob/M in T) if(M.key) return 1 - - proc/LoCorner(z=z1) - return locate(x1,y1,z) - proc/HiCorner(z=z2) - return locate(x2,y2,z) - - /* - Build procs: Take 2 turfs as corners, plus an item type. - An item may be like: - - turf/wall - obj/fence{icon_state="iron"} - */ - proc/BuildFilledRectangle(turf/T1,turf/T2,item) - if(!Contains(T1) || !Contains(T2)) return - var/turf/T=T1 - // pick new corners in a block()-friendly form - T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) - T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) - for(T in block(T1,T2)) new item(T) - - proc/BuildRectangle(turf/T1,turf/T2,item) - if(!Contains(T1) || !Contains(T2)) return - var/turf/T=T1 - // pick new corners in a block()-friendly form - T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) - T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) - if(T2.x-T1.x<2 || T2.y-T1.y<2) BuildFilledRectangle(T1,T2,item) - else - //for(T in block(T1,T2)-block(locate(T1.x+1,T1.y+1,T1.z),locate(T2.x-1,T2.y-1,T2.z))) - for(T in block(T1,locate(T2.x,T1.y,T2.z))) new item(T) - for(T in block(locate(T1.x,T2.y,T1.z),T2)) new item(T) - for(T in block(locate(T1.x,T1.y+1,T1.z),locate(T1.x,T2.y-1,T2.z))) new item(T) - for(T in block(locate(T2.x,T1.y+1,T1.z),locate(T2.x,T2.y-1,T2.z))) new item(T) - - /* - Supplementary build proc: Takes a list of turfs, plus an item - type. Actually the list doesn't have to be just turfs. - */ - proc/BuildInTurfs(list/turfs,item) - for(var/T in turfs) new item(T) - -atom - Write(savefile/S) - for(var/V in vars-"x"-"y"-"z"-"contents"-"icon"-"overlays"-"underlays") - if(issaved(vars[V])) - if(vars[V]!=initial(vars[V])) S[V]<>ic - if(istext(ic)) icon=swapmaps_iconcache[ic] - if(l && contents!=l) - contents+=l - qdel(l) - - -// set this up (at runtime) as follows: -// list(\ -// 'player.dmi'="player",\ -// 'monster.dmi'="monster",\ -// ... -// 'item.dmi'="item") -var/list/swapmaps_iconcache - -// preferred mode; sav or text -var/const/SWAPMAPS_SAV=0 -var/const/SWAPMAPS_TEXT=1 -var/swapmaps_mode=SWAPMAPS_SAV - -var/swapmaps_compiled_maxx -var/swapmaps_compiled_maxy -var/swapmaps_compiled_maxz -var/swapmaps_initialized -var/swapmaps_loaded -var/swapmaps_byname - -/proc/InitializeSwapMaps() - if(swapmaps_initialized) return - swapmaps_initialized=1 - swapmaps_compiled_maxx=world.maxx - swapmaps_compiled_maxy=world.maxy - swapmaps_compiled_maxz=world.maxz - swapmaps_loaded=list() - swapmaps_byname=list() - if(swapmaps_iconcache) - for(var/V in swapmaps_iconcache) - // reverse-associate everything - // so you can look up an icon file by name or vice-versa - swapmaps_iconcache[swapmaps_iconcache[V]]=V - -/proc/SwapMaps_AddIconToCache(name,icon) - if(!swapmaps_iconcache) swapmaps_iconcache=list() - swapmaps_iconcache[name]=icon - swapmaps_iconcache[icon]=name - -/proc/SwapMaps_Find(id) - InitializeSwapMaps() - return swapmaps_byname[id] - -/proc/SwapMaps_Load(id) - InitializeSwapMaps() - var/swapmap/M=swapmaps_byname[id] - if(!M) - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else if(fexists("map_[id].sav")) - S=new("map_[id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else return // no file found - if(text) - S=new - S.ImportText("/",file("map_[id].txt")) - S >> M - while(M.locked) sleep(1) - M.mode=text - return M - -/proc/SwapMaps_Save(id) - InitializeSwapMaps() - var/swapmap/M=swapmaps_byname[id] - if(M) M.Save() - return M - -/proc/SwapMaps_Save_All() - InitializeSwapMaps() - for(var/swapmap/M in swapmaps_loaded) - if(M) M.Save() - -/proc/SwapMaps_Unload(id) - InitializeSwapMaps() - var/swapmap/M=swapmaps_byname[id] - if(!M) return // return silently from an error - M.Unload() - return 1 - -/proc/SwapMaps_DeleteFile(id) - fdel("map_[id].sav") - fdel("map_[id].txt") - -/proc/SwapMaps_CreateFromTemplate(template_id) - var/swapmap/M=new - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[template_id].txt")) - text=1 - else if(fexists("map_[template_id].sav")) - S=new("map_[template_id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[template_id].txt")) - text=1 - else - to_world_log("SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found.") - return - if(text) - S=new - S.ImportText("/",file("map_[template_id].txt")) - /* - This hacky workaround is needed because S >> M will create a brand new - M to fill with data. There's no way to control the Read() process - properly otherwise. The //.0 path should always match the map, however. - */ - S.cd="//.0" - M.Read(S,M) - M.mode=text - while(M.locked) sleep(1) - return M - -/proc/SwapMaps_LoadChunk(chunk_id,turf/locorner) - var/swapmap/M=new - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) - text=1 - else if(fexists("map_[chunk_id].sav")) - S=new("map_[chunk_id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) - text=1 - else - to_world_log("SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found.") - return - if(text) - S=new - S.ImportText("/",file("map_[chunk_id].txt")) - /* - This hacky workaround is needed because S >> M will create a brand new - M to fill with data. There's no way to control the Read() process - properly otherwise. The //.0 path should always match the map, however. - */ - S.cd="//.0" - M.Read(S,M,locorner) - while(M.locked) sleep(1) - qdel(M) - return 1 - -/proc/SwapMaps_SaveChunk(chunk_id,turf/corner1,turf/corner2) - if(!corner1 || !corner2) - to_world_log("SwapMaps error in SwapMaps_SaveChunk():") - if(!corner1) to_world_log(" corner1 turf is null") - if(!corner2) to_world_log(" corner2 turf is null") - return - var/swapmap/M=new - M.id=chunk_id - M.ischunk=1 // this is a chunk - M.x1=min(corner1.x,corner2.x) - M.y1=min(corner1.y,corner2.y) - M.z1=min(corner1.z,corner2.z) - M.x2=max(corner1.x,corner2.x) - M.y2=max(corner1.y,corner2.y) - M.z2=max(corner1.z,corner2.z) - M.mode=swapmaps_mode - M.Save() - while(M.locked) sleep(1) - qdel(M) - return 1 - -/proc/SwapMaps_GetSize(id) - var/savefile/S - var/text=0 - if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else if(fexists("map_[id].sav")) - S=new("map_[id].sav") - else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) - text=1 - else - to_world_log("SwapMaps error in SwapMaps_GetSize(): map_[id] file not found.") - return - if(text) - S=new - S.ImportText("/",file("map_[id].txt")) - /* - The //.0 path should always be the map. There's no other way to - read this data. - */ - S.cd="//.0" - var/x - var/y - var/z - S["x"] >> x - S["y"] >> y - S["z"] >> z - return list(x,y,z) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/* + SwapMaps library by Lummox JR + developed for digitalBYOND + http://www.digitalbyond.org + + Version 2.1 + + The purpose of this library is to make it easy for authors to swap maps + in and out of their game using savefiles. Swapped-out maps can be + transferred between worlds for an MMORPG, sent to the client, etc. + This is facilitated by the use of a special datum and a global list. + + Uses of swapmaps: + + - Temporary battle arenas + - House interiors + - Individual custom player houses + - Virtually unlimited terrain + - Sharing maps between servers running different instances of the same + game + - Loading and saving pieces of maps for reusable room templates + */ + +/* + User Interface: + + VARS: + + swapmaps_iconcache + An associative list of icon files with names, like + 'player.dmi' = "player" + swapmaps_mode + This must be set at runtime, like in world/New(). + + SWAPMAPS_SAV 0 (default) + Uses .sav files for raw /savefile output. + SWAPMAPS_TEXT 1 + Uses .txt files via ExportText() and ImportText(). These maps + are easily editable and appear to take up less space in the + current version of BYOND. + + PROCS: + + SwapMaps_Find(id) + Find a map by its id + SwapMaps_Load(id) + Load a map by its id + SwapMaps_Save(id) + Save a map by its id (calls swapmap.Save()) + SwapMaps_Unload(id) + Save and unload a map by its id (calls swapmap.Unload()) + SwapMaps_Save_All() + Save all maps + SwapMaps_DeleteFile(id) + Delete a map file + SwapMaps_CreateFromTemplate(id) + Create a new map by loading another map to use as a template. + This map has id==src and will not be saved. To make it savable, + change id with swapmap.SetID(newid). + SwapMaps_LoadChunk(id,turf/locorner) + Load a swapmap as a "chunk", at a specific place. A new datum is + created but it's not added to the list of maps to save or unload. + The new datum can be safely deleted without affecting the turfs + it loaded. The purpose of this is to load a map file onto part of + another swapmap or an existing part of the world. + locorner is the corner turf with the lowest x,y,z values. + SwapMaps_SaveChunk(id,turf/corner1,turf/corner2) + Save a piece of the world as a "chunk". A new datum is created + for the chunk, but it can be deleted without destroying any turfs. + The chunk file can be reloaded as a swapmap all its own, or loaded + via SwapMaps_LoadChunk() to become part of another map. + SwapMaps_GetSize(id) + Return a list corresponding to the x,y,z sizes of a map file, + without loading the map. + Returns null if the map is not found. + SwapMaps_AddIconToCache(name,icon) + Cache an icon file by name for space-saving storage + + swapmap.New(id,x,y,z) + Create a new map; specify id, width (x), height (y), and + depth (z) + Default size is world.maxx,world.maxy,1 + swapmap.New(id,turf1,turf2) + Create a new map; specify id and 2 corners + This becomes a /swapmap for one of the compiled-in maps, for + easy saving. + swapmap.New() + Create a new map datum, but does not allocate space or assign an + ID (used for loading). + swapmap.Del() + Deletes a map but does not save + swapmap.Save() + Saves to map_[id].sav + Maps with id==src are not saved. + swapmap.Unload() + Saves the map and then deletes it + Maps with id==src are not saved. + swapmap.SetID(id) + Change the map's id and make changes to the lookup list + swapmap.AllTurfs(z) + Returns a block of turfs encompassing the entire map, or on just + one z-level + z is in world coordinates; it is optional + swapmap.Contains(turf/T) + Returns nonzero if T is inside the map's boundaries. + Also works for objs and mobs, but the proc is not area-safe. + swapmap.InUse() + Returns nonzero if a mob with a key is within the map's + boundaries. + swapmap.LoCorner(z=z1) + Returns locate(x1,y1,z), where z=z1 if none is specified. + swapmap.HiCorner(z=z2) + Returns locate(x2,y2,z), where z=z2 if none is specified. + swapmap.BuildFilledRectangle(turf/corner1,turf/corner2,item) + Builds a filled rectangle of item from one corner turf to the + other, on multiple z-levels if necessary. The corners may be + specified in any order. + item is a type path like /turf/wall or /obj/barrel{full=1}. + swapmap.BuildRectangle(turf/corner1,turf/corner2,item) + Builds an unfilled rectangle of item from one corner turf to + the other, on multiple z-levels if necessary. + swapmap.BuildInTurfs(list/turfs,item) + Builds item on all of the turfs listed. The list need not + contain only turfs, or even only atoms. + */ + +swapmap + var/id // a string identifying this map uniquely + var/x1 // minimum x,y,z coords + var/y1 + var/z1 + var/x2 // maximum x,y,z coords (also used as width,height,depth until positioned) + var/y2 + var/z2 + var/tmp/locked // don't move anyone to this map; it's saving or loading + var/tmp/mode // save as text-mode + var/ischunk // tells the load routine to load to the specified location + + New(_id,x,y,z) + if(isnull(_id)) return + id=_id + mode=swapmaps_mode + if(isturf(x) && isturf(y)) + /* + Special format: Defines a map as an existing set of turfs; + this is useful for saving a compiled map in swapmap format. + Because this is a compiled-in map, its turfs are not deleted + when the datum is deleted. + */ + x1=min(x:x,y:x);x2=max(x:x,y:x) + y1=min(x:y,y:y);y2=max(x:y,y:y) + z1=min(x:z,y:z);z2=max(x:z,y:z) + InitializeSwapMaps() + if(z2>swapmaps_compiled_maxz ||\ + y2>swapmaps_compiled_maxy ||\ + x2>swapmaps_compiled_maxx) + qdel(src) + return + x2=x?(x):world.maxx + y2=y?(y):world.maxy + z2=z?(z):1 + AllocateSwapMap() + + Del() + // a temporary datum for a chunk can be deleted outright + // for others, some cleanup is necessary + if(!ischunk) + swapmaps_loaded-=src + swapmaps_byname-=id + if(z2>swapmaps_compiled_maxz ||\ + y2>swapmaps_compiled_maxy ||\ + x2>swapmaps_compiled_maxx) + var/list/areas=new + for(var/atom/A in block(locate(x1,y1,z1),locate(x2,y2,z2))) + for(var/obj/O in A) qdel(O) + for(var/mob/M in A) + if(!M.key) qdel(M) + else M.loc=null + areas[A.loc]=null + qdel(A) + // delete areas that belong only to this map + for(var/area/a in areas) + if(a && !a.contents.len) qdel(a) + if(x2>=world.maxx || y2>=world.maxy || z2>=world.maxz) CutXYZ() + qdel(areas) + ..() + + /* + Savefile format: + map + id + x // size, not coords + y + z + areas // list of areas, not including default + [each z; 1 to depth] + [each y; 1 to height] + [each x; 1 to width] + type // of turf + AREA // if non-default; saved as a number (index into areas list) + vars // all other changed vars + */ + Write(savefile/S) + var/x + var/y + var/z + var/n + var/list/areas + var/area/defarea=locate(world.area) + if(!defarea) defarea=new world.area + areas=list() + for(var/turf/T in block(locate(x1,y1,z1),locate(x2,y2,z2))) + areas[T.loc]=null + for(n in areas) // quickly eliminate associations for smaller storage + areas-=n + areas+=n + areas-=defarea + InitializeSwapMaps() + locked=1 + S["id"] << id + S["z"] << z2-z1+1 + S["y"] << y2-y1+1 + S["x"] << x2-x1+1 + S["areas"] << areas + for(n in 1 to areas.len) areas[areas[n]]=n + var/oldcd=S.cd + for(z=z1,z<=z2,++z) + S.cd="[z-z1+1]" + for(y=y1,y<=y2,++y) + S.cd="[y-y1+1]" + for(x=x1,x<=x2,++x) + S.cd="[x-x1+1]" + var/turf/T=locate(x,y,z) + S["type"] << T.type + if(T.loc!=defarea) S["AREA"] << areas[T.loc] + T.Write(S) + S.cd=".." + S.cd=".." + sleep() + S.cd=oldcd + locked=0 + qdel(areas) + + Read(savefile/S,_id,turf/locorner) + var/x + var/y + var/z + var/n + var/list/areas + var/area/defarea=locate(world.area) + id=_id + if(locorner) + ischunk=1 + x1=locorner.x + y1=locorner.y + z1=locorner.z + if(!defarea) defarea=new world.area + if(!_id) + S["id"] >> id + else + var/dummy + S["id"] >> dummy + S["z"] >> z2 // these are depth, + S["y"] >> y2 // height, + S["x"] >> x2 // width + S["areas"] >> areas + locked=1 + AllocateSwapMap() // adjust x1,y1,z1 - x2,y2,z2 coords + var/oldcd=S.cd + for(z=z1,z<=z2,++z) + S.cd="[z-z1+1]" + for(y=y1,y<=y2,++y) + S.cd="[y-y1+1]" + for(x=x1,x<=x2,++x) + S.cd="[x-x1+1]" + var/tp + S["type"]>>tp + var/turf/T=locate(x,y,z) + T.loc.contents-=T + T=new tp(locate(x,y,z)) + if("AREA" in S.dir) + S["AREA"]>>n + var/area/A=areas[n] + A.contents+=T + else defarea.contents+=T + // clear the turf + for(var/obj/O in T) qdel(O) + for(var/mob/M in T) + if(!M.key) qdel(M) + else M.loc=null + // finish the read + T.Read(S) + S.cd=".." + S.cd=".." + sleep() + S.cd=oldcd + locked=0 + qdel(areas) + + /* + Find an empty block on the world map in which to load this map. + If no space is found, increase world.maxz as necessary. (If the + map is greater in x,y size than the current world, expand + world.maxx and world.maxy too.) + + Ignore certain operations if loading a map as a chunk. Use the + x1,y1,z1 position for it, and *don't* count it as a loaded map. + */ + proc/AllocateSwapMap() + InitializeSwapMaps() + world.maxx=max(x2,world.maxx) // stretch x/y if necessary + world.maxy=max(y2,world.maxy) + if(!ischunk) + if(world.maxz<=swapmaps_compiled_maxz) + z1=swapmaps_compiled_maxz+1 + x1=1;y1=1 + else + var/list/l=ConsiderRegion(1,1,world.maxx,world.maxy,swapmaps_compiled_maxz+1) + x1=l[1] + y1=l[2] + z1=l[3] + qdel(l) + x2+=x1-1 + y2+=y1-1 + z2+=z1-1 + world.maxz=max(z2,world.maxz) // stretch z if necessary + if(!ischunk) + swapmaps_loaded[src]=null + swapmaps_byname[id]=src + + proc/ConsiderRegion(X1,Y1,X2,Y2,Z1,Z2) + while(1) + var/nextz=0 + var/swapmap/M + for(M in swapmaps_loaded) + if(M.z2Z2) || M.z1>=Z1+z2 ||\ + M.x1>X2 || M.x2=X1+x2 ||\ + M.y1>Y2 || M.y2=Y1+y2) continue + // look for sub-regions with a defined ceiling + var/nz2=Z2?(Z2):Z1+z2-1+M.z2-M.z1 + if(M.x1>=X1+x2) + .=ConsiderRegion(X1,Y1,M.x1-1,Y2,Z1,nz2) + if(.) return + else if(M.x2<=X2-x2) + .=ConsiderRegion(M.x2+1,Y1,X2,Y2,Z1,nz2) + if(.) return + if(M.y1>=Y1+y2) + .=ConsiderRegion(X1,Y1,X2,M.y1-1,Z1,nz2) + if(.) return + else if(M.y2<=Y2-y2) + .=ConsiderRegion(X1,M.y2+1,X2,Y2,Z1,nz2) + if(.) return + nextz=nextz?min(nextz,M.z2+1):(M.z2+1) + if(!M) + /* If nextz is not 0, then at some point there was an overlap that + could not be resolved by using an area to the side */ + if(nextz) Z1=nextz + if(!nextz || (Z2 && Z2-Z1+1=z2)?list(X1,Y1,Z1):null + X1=1;X2=world.maxx + Y1=1;Y2=world.maxy + + proc/CutXYZ() + var/mx=swapmaps_compiled_maxx + var/my=swapmaps_compiled_maxy + var/mz=swapmaps_compiled_maxz + for(var/swapmap/M in swapmaps_loaded) // may not include src + mx=max(mx,M.x2) + my=max(my,M.y2) + mz=max(mz,M.z2) + world.maxx=mx + world.maxy=my + world.maxz=mz + + // save and delete + proc/Unload() + Save() + qdel(src) + + proc/Save() + if(id==src) return 0 + var/savefile/S=mode?(new):new("map_[id].sav") + S << src + while(locked) sleep(1) + if(mode) + fdel("map_[id].txt") + S.ExportText("/","map_[id].txt") + return 1 + + // this will not delete existing savefiles for this map + proc/SetID(newid) + swapmaps_byname-=id + id=newid + swapmaps_byname[id]=src + + proc/AllTurfs(z) + if(isnum(z) && (zz2)) return null + return block(LoCorner(z),HiCorner(z)) + + // this could be safely called for an obj or mob as well, but + // probably not an area + proc/Contains(turf/T) + return (T && T.x>=x1 && T.x<=x2\ + && T.y>=y1 && T.y<=y2\ + && T.z>=z1 && T.z<=z2) + + proc/InUse() + for(var/turf/T in AllTurfs()) + for(var/mob/M in T) if(M.key) return 1 + + proc/LoCorner(z=z1) + return locate(x1,y1,z) + proc/HiCorner(z=z2) + return locate(x2,y2,z) + + /* + Build procs: Take 2 turfs as corners, plus an item type. + An item may be like: + + turf/wall + obj/fence{icon_state="iron"} + */ + proc/BuildFilledRectangle(turf/T1,turf/T2,item) + if(!Contains(T1) || !Contains(T2)) return + var/turf/T=T1 + // pick new corners in a block()-friendly form + T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) + T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) + for(T in block(T1,T2)) new item(T) + + proc/BuildRectangle(turf/T1,turf/T2,item) + if(!Contains(T1) || !Contains(T2)) return + var/turf/T=T1 + // pick new corners in a block()-friendly form + T1=locate(min(T1.x,T2.x),min(T1.y,T2.y),min(T1.z,T2.z)) + T2=locate(max(T.x,T2.x),max(T.y,T2.y),max(T.z,T2.z)) + if(T2.x-T1.x<2 || T2.y-T1.y<2) BuildFilledRectangle(T1,T2,item) + else + //for(T in block(T1,T2)-block(locate(T1.x+1,T1.y+1,T1.z),locate(T2.x-1,T2.y-1,T2.z))) + for(T in block(T1,locate(T2.x,T1.y,T2.z))) new item(T) + for(T in block(locate(T1.x,T2.y,T1.z),T2)) new item(T) + for(T in block(locate(T1.x,T1.y+1,T1.z),locate(T1.x,T2.y-1,T2.z))) new item(T) + for(T in block(locate(T2.x,T1.y+1,T1.z),locate(T2.x,T2.y-1,T2.z))) new item(T) + + /* + Supplementary build proc: Takes a list of turfs, plus an item + type. Actually the list doesn't have to be just turfs. + */ + proc/BuildInTurfs(list/turfs,item) + for(var/T in turfs) new item(T) + +atom + Write(savefile/S) + for(var/V in vars-"x"-"y"-"z"-"contents"-"icon"-"overlays"-"underlays") + if(issaved(vars[V])) + if(vars[V]!=initial(vars[V])) S[V]<>ic + if(istext(ic)) icon=swapmaps_iconcache[ic] + if(l && contents!=l) + contents+=l + qdel(l) + + +// set this up (at runtime) as follows: +// list(\ +// 'player.dmi'="player",\ +// 'monster.dmi'="monster",\ +// ... +// 'item.dmi'="item") +var/list/swapmaps_iconcache + +// preferred mode; sav or text +var/const/SWAPMAPS_SAV=0 +var/const/SWAPMAPS_TEXT=1 +var/swapmaps_mode=SWAPMAPS_SAV + +var/swapmaps_compiled_maxx +var/swapmaps_compiled_maxy +var/swapmaps_compiled_maxz +var/swapmaps_initialized +var/swapmaps_loaded +var/swapmaps_byname + +/proc/InitializeSwapMaps() + if(swapmaps_initialized) return + swapmaps_initialized=1 + swapmaps_compiled_maxx=world.maxx + swapmaps_compiled_maxy=world.maxy + swapmaps_compiled_maxz=world.maxz + swapmaps_loaded=list() + swapmaps_byname=list() + if(swapmaps_iconcache) + for(var/V in swapmaps_iconcache) + // reverse-associate everything + // so you can look up an icon file by name or vice-versa + swapmaps_iconcache[swapmaps_iconcache[V]]=V + +/proc/SwapMaps_AddIconToCache(name,icon) + if(!swapmaps_iconcache) swapmaps_iconcache=list() + swapmaps_iconcache[name]=icon + swapmaps_iconcache[icon]=name + +/proc/SwapMaps_Find(id) + InitializeSwapMaps() + return swapmaps_byname[id] + +/proc/SwapMaps_Load(id) + InitializeSwapMaps() + var/swapmap/M=swapmaps_byname[id] + if(!M) + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else if(fexists("map_[id].sav")) + S=new("map_[id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else return // no file found + if(text) + S=new + S.ImportText("/",file("map_[id].txt")) + S >> M + while(M.locked) sleep(1) + M.mode=text + return M + +/proc/SwapMaps_Save(id) + InitializeSwapMaps() + var/swapmap/M=swapmaps_byname[id] + if(M) M.Save() + return M + +/proc/SwapMaps_Save_All() + InitializeSwapMaps() + for(var/swapmap/M in swapmaps_loaded) + if(M) M.Save() + +/proc/SwapMaps_Unload(id) + InitializeSwapMaps() + var/swapmap/M=swapmaps_byname[id] + if(!M) return // return silently from an error + M.Unload() + return 1 + +/proc/SwapMaps_DeleteFile(id) + fdel("map_[id].sav") + fdel("map_[id].txt") + +/proc/SwapMaps_CreateFromTemplate(template_id) + var/swapmap/M=new + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[template_id].txt")) + text=1 + else if(fexists("map_[template_id].sav")) + S=new("map_[template_id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[template_id].txt")) + text=1 + else + to_world_log("SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found.") + return + if(text) + S=new + S.ImportText("/",file("map_[template_id].txt")) + /* + This hacky workaround is needed because S >> M will create a brand new + M to fill with data. There's no way to control the Read() process + properly otherwise. The //.0 path should always match the map, however. + */ + S.cd="//.0" + M.Read(S,M) + M.mode=text + while(M.locked) sleep(1) + return M + +/proc/SwapMaps_LoadChunk(chunk_id,turf/locorner) + var/swapmap/M=new + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) + text=1 + else if(fexists("map_[chunk_id].sav")) + S=new("map_[chunk_id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) + text=1 + else + to_world_log("SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found.") + return + if(text) + S=new + S.ImportText("/",file("map_[chunk_id].txt")) + /* + This hacky workaround is needed because S >> M will create a brand new + M to fill with data. There's no way to control the Read() process + properly otherwise. The //.0 path should always match the map, however. + */ + S.cd="//.0" + M.Read(S,M,locorner) + while(M.locked) sleep(1) + qdel(M) + return 1 + +/proc/SwapMaps_SaveChunk(chunk_id,turf/corner1,turf/corner2) + if(!corner1 || !corner2) + to_world_log("SwapMaps error in SwapMaps_SaveChunk():") + if(!corner1) to_world_log(" corner1 turf is null") + if(!corner2) to_world_log(" corner2 turf is null") + return + var/swapmap/M=new + M.id=chunk_id + M.ischunk=1 // this is a chunk + M.x1=min(corner1.x,corner2.x) + M.y1=min(corner1.y,corner2.y) + M.z1=min(corner1.z,corner2.z) + M.x2=max(corner1.x,corner2.x) + M.y2=max(corner1.y,corner2.y) + M.z2=max(corner1.z,corner2.z) + M.mode=swapmaps_mode + M.Save() + while(M.locked) sleep(1) + qdel(M) + return 1 + +/proc/SwapMaps_GetSize(id) + var/savefile/S + var/text=0 + if(swapmaps_mode==SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else if(fexists("map_[id].sav")) + S=new("map_[id].sav") + else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) + text=1 + else + to_world_log("SwapMaps error in SwapMaps_GetSize(): map_[id] file not found.") + return + if(text) + S=new + S.ImportText("/",file("map_[id].txt")) + /* + The //.0 path should always be the map. There's no other way to + read this data. + */ + S.cd="//.0" + var/x + var/y + var/z + S["x"] >> x + S["y"] >> y + S["z"] >> z + return list(x,y,z) diff --git a/code/modules/maps/tg/map_template.dm b/code/modules/maps/tg/map_template.dm index 3e530206a4c..17f43d10292 100644 --- a/code/modules/maps/tg/map_template.dm +++ b/code/modules/maps/tg/map_template.dm @@ -9,8 +9,8 @@ var/annihilate = FALSE // If true, all (movable) atoms at the location where the map is loaded will be deleted before the map is loaded in. var/fixed_orientation = FALSE // If true, the submap will not be rotated randomly when loaded. - var/cost = null /* The map generator has a set 'budget' it spends to place down different submaps. It will pick available submaps randomly until - it runs out. The cost of a submap should roughly corrispond with several factors such as size, loot, difficulty, desired scarcity, etc. + var/cost = null /* The map generator has a set 'budget' it spends to place down different submaps. It will pick available submaps randomly until + it runs out. The cost of a submap should roughly corrispond with several factors such as size, loot, difficulty, desired scarcity, etc. Set to -1 to force the submap to always be made. */ var/allow_duplicates = FALSE // If false, only one map template will be spawned by the game. Doesn't affect admins spawning then manually. var/discard_prob = 0 // If non-zero, there is a chance that the map seeding algorithm will skip this template when selecting potential templates to use. @@ -74,7 +74,7 @@ A.power_change() if(machinery_was_awake) - SSmachines.wake() // Wake only if it was awake before we tried to suspended it. + SSmachines.wake() // Wake only if it was awake before we tried to suspended it. SSshuttles.block_init_queue = prev_shuttle_queue_state SSshuttles.process_init_queues() // We will flush the queue unless there were other blockers, in which case they will do it. @@ -214,6 +214,12 @@ potential_submaps -= chosen_template continue + // Is single use template already placed + if(!chosen_template.allow_duplicates && chosen_template.loaded) + priority_submaps -= chosen_template + potential_submaps -= chosen_template + continue + // Did we already place down a very similar submap? if(chosen_template.template_group && (chosen_template.template_group in template_groups_used)) priority_submaps -= chosen_template @@ -253,6 +259,13 @@ admin_notice("Submap \"[chosen_template.name]\" placed at ([T.x], [T.y], [T.z])\n", R_DEBUG) + if(specific_sanity < 0) + // I have no idea how this function has a race condition for the sanity check, but forcing the inner check loop to end like this fixes it... + // If a template doesn't allow duplicates, it tries to double place a template. this fixes that. + break + if(!chosen_template.allow_duplicates) + specific_sanity = -1 // force end the placement loop + // Do loading here. chosen_template.load(T, centered = TRUE, orientation=orientation) // This is run before the main map's initialization routine, so that can initilize our submaps for us instead. diff --git a/code/modules/materials/material_synth.dm b/code/modules/materials/material_synth.dm index 972b8b61eda..9d595bce994 100644 --- a/code/modules/materials/material_synth.dm +++ b/code/modules/materials/material_synth.dm @@ -1,41 +1,41 @@ -// These objects are used by cyborgs to get around a lot of the limitations on stacks -// and the weird bugs that crop up when expecting borg module code to behave sanely. -/obj/item/stack/material/cyborg - uses_charge = 1 - charge_costs = list(1000) - gender = NEUTER - matter = null // Don't shove it in the autholathe. - -/obj/item/stack/material/cyborg/Initialize() - . = ..() - name = "[material.display_name] synthesiser" - desc = "A device that synthesises [material.display_name]." - matter = null - -/obj/item/stack/material/cyborg/update_strings() - return - -/obj/item/stack/material/cyborg/plastic - icon_state = "sheet-plastic" - default_type = "plastic" - -/obj/item/stack/material/cyborg/steel - icon_state = "sheet-metal" - default_type = "steel" - -/obj/item/stack/material/cyborg/plasteel - icon_state = "sheet-plasteel" - default_type = "plasteel" - -/obj/item/stack/material/cyborg/wood - icon_state = "sheet-wood" - default_type = "wood" - -/obj/item/stack/material/cyborg/glass - icon_state = "sheet-glass" - default_type = "glass" - -/obj/item/stack/material/cyborg/glass/reinforced - icon_state = "sheet-rglass" - default_type = "rglass" +// These objects are used by cyborgs to get around a lot of the limitations on stacks +// and the weird bugs that crop up when expecting borg module code to behave sanely. +/obj/item/stack/material/cyborg + uses_charge = 1 + charge_costs = list(1000) + gender = NEUTER + matter = null // Don't shove it in the autholathe. + +/obj/item/stack/material/cyborg/Initialize() + . = ..() + name = "[material.display_name] synthesiser" + desc = "A device that synthesises [material.display_name]." + matter = null + +/obj/item/stack/material/cyborg/update_strings() + return + +/obj/item/stack/material/cyborg/plastic + icon_state = "sheet-plastic" + default_type = "plastic" + +/obj/item/stack/material/cyborg/steel + icon_state = "sheet-metal" + default_type = "steel" + +/obj/item/stack/material/cyborg/plasteel + icon_state = "sheet-plasteel" + default_type = "plasteel" + +/obj/item/stack/material/cyborg/wood + icon_state = "sheet-wood" + default_type = "wood" + +/obj/item/stack/material/cyborg/glass + icon_state = "sheet-glass" + default_type = "glass" + +/obj/item/stack/material/cyborg/glass/reinforced + icon_state = "sheet-rglass" + default_type = "rglass" charge_costs = list(500, 1000) \ No newline at end of file diff --git a/code/modules/materials/materials/_materials.dm b/code/modules/materials/materials/_materials.dm index fba60dda059..853c9e90ead 100644 --- a/code/modules/materials/materials/_materials.dm +++ b/code/modules/materials/materials/_materials.dm @@ -213,6 +213,7 @@ var/list/name_to_material var/luminescence var/radiation_resistance = 0 // Radiation resistance, which is added on top of a material's weight for blocking radiation. Needed to make lead special without superrobust weapons. var/supply_conversion_value // Supply points per sheet that this material sells for. + var/can_sharpen = TRUE // Is this material compatible with a sharpening kit? // Placeholder vars for the time being, todo properly integrate windows/light tiles/rods. var/created_window @@ -320,12 +321,13 @@ var/list/name_to_material // General wall debris product placement. // Not particularly necessary aside from snowflakey cult girders. -/datum/material/proc/place_dismantled_product(var/turf/target) - place_sheet(target) +/datum/material/proc/place_dismantled_product(var/turf/target, var/amount = 1) + place_sheet(target, amount) // Debris product. Used ALL THE TIME. /datum/material/proc/place_sheet(var/turf/target, amount) - if(stack_type) + amount = round(amount) + if(stack_type && amount > 0) return new stack_type(target, amount) // As above. diff --git a/code/modules/materials/materials/metals/metals.dm b/code/modules/materials/materials/metals/metals.dm index fae037a6201..dbf1e502ac7 100644 --- a/code/modules/materials/materials/metals/metals.dm +++ b/code/modules/materials/materials/metals/metals.dm @@ -151,6 +151,7 @@ stack_type = /obj/item/stack/material/graphite flags = MATERIAL_BRITTLE icon_base = "solid" + table_icon_base = "stone" icon_reinf = "reinf_mesh" icon_colour = "#333333" hardness = 75 diff --git a/code/modules/materials/materials/organic/wood.dm b/code/modules/materials/materials/organic/wood.dm index 4564d654630..e4dd9396ce4 100644 --- a/code/modules/materials/materials/organic/wood.dm +++ b/code/modules/materials/materials/organic/wood.dm @@ -79,6 +79,7 @@ /datum/material/wood/hardwood/generate_recipes() ..() + recipes += new /datum/stack_recipe("parquet wood floor tile", /obj/item/stack/tile/wood/parquet, 1, 4, 20, pass_stack_color = FALSE) for(var/datum/stack_recipe/r_recipe in recipes) if(r_recipe.title == "wood floor tile") recipes -= r_recipe diff --git a/code/modules/materials/materials/plastic.dm b/code/modules/materials/materials/plastic.dm index d10e5153f6b..b31ed2cb736 100644 --- a/code/modules/materials/materials/plastic.dm +++ b/code/modules/materials/materials/plastic.dm @@ -108,4 +108,5 @@ hardness = 1 weight = 1 protectiveness = 0 // 0% - conductive = 0 \ No newline at end of file + conductive = 0 + can_sharpen = FALSE diff --git a/code/modules/materials/sheets/metals/rods.dm b/code/modules/materials/sheets/metals/rods.dm index 9ebd0fd92f1..dbb30c19d9d 100644 --- a/code/modules/materials/sheets/metals/rods.dm +++ b/code/modules/materials/sheets/metals/rods.dm @@ -43,8 +43,8 @@ var/global/list/datum/stack_recipe/rods_recipes = list( \ new/datum/stack_recipe("catwalk", /obj/structure/catwalk, 2, time = 80, one_per_turf = 1, on_floor = 1)) /obj/item/stack/rods/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(get_amount() < 2) to_chat(user, "You need at least two rods to do this.") diff --git a/code/modules/materials/sheets/organic/tanning/hide.dm b/code/modules/materials/sheets/organic/tanning/hide.dm index e8dadfce734..5f61873537e 100644 --- a/code/modules/materials/sheets/organic/tanning/hide.dm +++ b/code/modules/materials/sheets/organic/tanning/hide.dm @@ -1,7 +1,7 @@ /obj/item/stack/animalhide name = "hide" desc = "The hide of some creature." - description_info = "Use something sharp, like a knife, to scrape the hairs/feathers/etc off this hide to prepare it for tanning." + description_info = "Use something sharp, like a knife, to scrape the hairs/feathers/etc off this hide to prepare it for tanning." icon_state = "sheet-hide" drop_sound = 'sound/items/drop/cloth.ogg' pickup_sound = 'sound/items/pickup/cloth.ogg' @@ -27,7 +27,7 @@ if(HS.get_amount() < HS.max_amount) H = HS break - + // Either we found a valid stack, in which case increment amount, // Or we need to make a new stack if(istype(H)) @@ -38,7 +38,7 @@ // Increment the amount src.use(1) scraped++ - + if(scraped) to_chat(user, SPAN_NOTICE("You scrape the hair off [scraped] hide\s.")) else @@ -87,4 +87,4 @@ desc = "The skin of a terrible creature." singular_name = "alien hide piece" icon_state = "sheet-xeno" - stacktype = "hide-xeno" \ No newline at end of file + stacktype = "hide-xeno" diff --git a/code/modules/materials/sheets/organic/tanning/hide_hairless.dm b/code/modules/materials/sheets/organic/tanning/hide_hairless.dm index f504e24a9f5..edb36b9b867 100644 --- a/code/modules/materials/sheets/organic/tanning/hide_hairless.dm +++ b/code/modules/materials/sheets/organic/tanning/hide_hairless.dm @@ -3,7 +3,7 @@ /obj/item/stack/hairlesshide name = "hairless hide" desc = "This hide was stripped of it's hair, but still needs tanning." - description_info = "Get it wet to continue tanning this into leather.
                    \ + description_info = "Get it wet to continue tanning this into leather.
                    \ You could set it in a river, wash it with a sink, or just splash water on it with a bucket." singular_name = "hairless hide piece" icon_state = "sheet-hairlesshide" @@ -24,7 +24,7 @@ if(HS.get_amount() < HS.max_amount) H = HS break - + // Either we found a valid stack, in which case increment amount, // Or we need to make a new stack if(istype(H)) @@ -45,4 +45,4 @@ I.dry() use(1) - stacknum -= 1 \ No newline at end of file + stacknum -= 1 diff --git a/code/modules/materials/sheets/organic/tanning/leather_wet.dm b/code/modules/materials/sheets/organic/tanning/leather_wet.dm index 6d1df6fd753..be4d60570aa 100644 --- a/code/modules/materials/sheets/organic/tanning/leather_wet.dm +++ b/code/modules/materials/sheets/organic/tanning/leather_wet.dm @@ -3,9 +3,9 @@ name = "wet leather" desc = "This leather has been cleaned but still needs to be dried." description_info = "To finish tanning the leather, you need to dry it. \ - You could place it under a fire, \ - put it in a drying rack, \ - or build a tanning rack from steel or wooden boards." + You could place it under a fire, \ + put it in a drying rack, \ + or build a tanning rack from steel or wooden boards." singular_name = "wet leather piece" icon_state = "sheet-wetleather" var/wetness = 30 //Reduced when exposed to high temperautres diff --git a/code/modules/mentor/mentorhelp.dm b/code/modules/mentor/mentorhelp.dm index 41a760f475c..8a53d8d398e 100644 --- a/code/modules/mentor/mentorhelp.dm +++ b/code/modules/mentor/mentorhelp.dm @@ -224,7 +224,7 @@ GLOBAL_DATUM_INIT(mhelp_tickets, /datum/mentor_help_tickets, new) AddInteraction("Reopened by [usr.ckey]") if(initiator) - to_chat(initiator, "Ticket [TicketHref("#[id]")] was reopened by [usr.ckey].") + to_chat(initiator, "[span_purple("Ticket [TicketHref("#[id]")] was reopened by [usr.ckey].")]") var/msg = "Ticket [TicketHref("#[id]")] reopened by [usr.ckey]." message_mentors(msg) log_admin(msg) @@ -249,9 +249,9 @@ GLOBAL_DATUM_INIT(mhelp_tickets, /datum/mentor_help_tickets, new) state = AHELP_RESOLVED GLOB.mhelp_tickets.ListInsert(src) - AddInteraction("Resolved by [usr.ckey].") + AddInteraction("[span_green("Resolved by [usr.ckey].")]") if(initiator) - to_chat(initiator, "Ticket [TicketHref("#[id]")] was marked resolved by [usr.ckey].") + to_chat(initiator, "[span_green("Ticket [TicketHref("#[id]")] was marked resolved by [usr.ckey].")]") if(!silent) feedback_inc("mhelp_resolve") var/msg = "Ticket [TicketHref("#[id]")] resolved by [usr.ckey]" diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index e12b667047a..b413ac8ee5f 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -445,7 +445,7 @@ if(default_part_replacement(user,W)) return - if(W.is_wrench()) + if(W.has_tool_quality(TOOL_WRENCH)) if(istype(get_turf(src), /turf/space)) to_chat(user, "You can't anchor something to empty space. Idiot.") diff --git a/code/modules/mining/mineral_effect.dm b/code/modules/mining/mineral_effect.dm index 43bd5c85880..f2506702bd1 100644 --- a/code/modules/mining/mineral_effect.dm +++ b/code/modules/mining/mineral_effect.dm @@ -1,29 +1,29 @@ -/obj/effect/mineral - name = "mineral vein" - icon = 'icons/obj/mining.dmi' - desc = "Shiny." - mouse_opacity = 0 - density = FALSE - anchored = TRUE - var/ore_key - var/image/scanner_image - var/ore_reagent // Reagent from pumping water near this ore. - -/obj/effect/mineral/New(var/newloc, var/ore/M) - ..(newloc) - name = "[M.display_name] deposit" - ore_key = M.name - if(M.reagent) - ore_reagent = M.reagent - icon_state = "rock_[ore_key]" - var/turf/T = get_turf(src) - layer = T.layer+0.1 - -/obj/effect/mineral/proc/get_scan_overlay() - if(!scanner_image) - var/ore/O = GLOB.ore_data[ore_key] - if(O) - scanner_image = image(icon, loc = get_turf(src), icon_state = (O.scan_icon ? O.scan_icon : icon_state)) - else - to_world("No ore data for [src]!") +/obj/effect/mineral + name = "mineral vein" + icon = 'icons/obj/mining.dmi' + desc = "Shiny." + mouse_opacity = 0 + density = FALSE + anchored = TRUE + var/ore_key + var/image/scanner_image + var/ore_reagent // Reagent from pumping water near this ore. + +/obj/effect/mineral/New(var/newloc, var/ore/M) + ..(newloc) + name = "[M.display_name] deposit" + ore_key = M.name + if(M.reagent) + ore_reagent = M.reagent + icon_state = "rock_[ore_key]" + var/turf/T = get_turf(src) + layer = T.layer+0.1 + +/obj/effect/mineral/proc/get_scan_overlay() + if(!scanner_image) + var/ore/O = GLOB.ore_data[ore_key] + if(O) + scanner_image = image(icon, loc = get_turf(src), icon_state = (O.scan_icon ? O.scan_icon : icon_state)) + else + to_world("No ore data for [src]!") return scanner_image \ No newline at end of file diff --git a/code/modules/mining/resonator_vr.dm b/code/modules/mining/resonator_vr.dm index d3c3246a24f..b007a3212fc 100644 --- a/code/modules/mining/resonator_vr.dm +++ b/code/modules/mining/resonator_vr.dm @@ -18,6 +18,8 @@ var/fieldsactive = 0 var/burst_time = 50 var/fieldlimit = 3 + var/spreadmode = 0 + var/cascading = 0 /obj/item/resonator/upgraded name = "upgraded resonator" @@ -30,20 +32,28 @@ var/turf/T = get_turf(target) if(locate(/obj/effect/resonance) in T) return - if(fieldsactive < fieldlimit) + + if((!spreadmode && fieldsactive < fieldlimit) || (spreadmode && !cascading) ) playsound(src,'sound/weapons/resonator_fire.ogg',50,1) - new /obj/effect/resonance(T, creator, burst_time) + new /obj/effect/resonance(T, WEAKREF(creator), burst_time, spreadmode, fieldlimit, get_cardinal_dir(src, T)) fieldsactive++ + cascading = TRUE spawn(burst_time) fieldsactive-- + cascading = FALSE /obj/item/resonator/attack_self(mob/user) - if(burst_time == 50) - burst_time = 30 - to_chat(user, "You set the resonator's fields to detonate after 3 seconds.") - else - burst_time = 50 - to_chat(user, "You set the resonator's fields to detonate after 5 seconds.") + switch(tgui_alert(user, "Change Detonation Time or toggle Cascading?","Setting", list("Toggle Cascade", "Resonance Time"))) + if("Resonance Time") + if(burst_time == 50) + burst_time = 30 + to_chat(user, "You set the resonator's fields to detonate after 3 seconds.") + else + burst_time = 50 + to_chat(user, "You set the resonator's fields to detonate after 5 seconds.") + if("Toggle Cascade") + spreadmode = !spreadmode + to_chat(user, span_info("You have [(spreadmode ? "enabled" : "disabled")] the resonance cascade mode.")) /obj/item/resonator/afterattack(atom/target, mob/user, proximity_flag) if(proximity_flag) @@ -61,11 +71,30 @@ mouse_opacity = 0 var/resonance_damage = 20 -/obj/effect/resonance/Initialize(mapload, var/creator = null, var/timetoburst) + +/obj/effect/resonance/Initialize(mapload, var/creator = null, var/timetoburst, var/spread, var/fields, var/origin_dir, var/depth = 0) . = ..() // Start small and grow to big size as we are about to burst transform = matrix()*0.75 animate(src, transform = matrix()*1.5, time = timetoburst) + //Spread if requested + if(spread) + fields-- + var/new_dir = turn(origin_dir, 90) + var/new_fields = fields + for(var/i=0, i<=2, i++) //We place cascade the resonance by 3 entries each time in a "T shape" around the original resonance + if(fields > 0) + switch(i) + if(0) + new_dir = origin_dir + if(1) + new_dir = turn(origin_dir, 90) + if(2) + new_dir = turn(origin_dir, -90) + var/turf/T = get_step(get_turf(src), new_dir) + new_fields = fields - 2 - (i*3) - depth + fields-- + new /obj/effect/resonance(T, WEAKREF(creator), timetoburst, spread, new_fields, get_cardinal_dir(src, T), depth++) // Queue the actual bursting spawn(timetoburst) if(!QDELETED(src)) @@ -90,7 +119,7 @@ if(environment.temperature < 250) name = "strong resonance field" resonance_damage = 50 - + for(var/mob/living/L in src.loc) if(creator) add_attack_logs(creator, L, "used a resonator field on") diff --git a/code/modules/mining/shelter_atoms_vr.dm b/code/modules/mining/shelter_atoms_vr.dm index 6c209191e19..2f4ad9e46f7 100644 --- a/code/modules/mining/shelter_atoms_vr.dm +++ b/code/modules/mining/shelter_atoms_vr.dm @@ -234,7 +234,7 @@ GLOBAL_LIST_EMPTY(unique_deployable) pixel_y = -32 /obj/item/device/gps/computer/attackby(obj/item/I, mob/living/user) - if(I.is_wrench()) + if(I.has_tool_quality(TOOL_WRENCH)) user.visible_message("[user] disassembles [src].", "You start to disassemble [src]...", "You hear clanking and banging noises.") if(do_after(user,4 SECONDS,src)) @@ -300,7 +300,7 @@ GLOBAL_LIST_EMPTY(unique_deployable) qdel(src) /obj/structure/fans/attackby(obj/item/I, mob/living/user) - if(I.is_wrench()) + if(I.has_tool_quality(TOOL_WRENCH)) user.visible_message("[user] disassembles [src].", "You start to disassemble [src]...", "You hear clanking and banging noises.") if(do_after(user,4 SECONDS,src)) diff --git a/code/modules/mob/dead/corpse.dm b/code/modules/mob/dead/corpse.dm index 2e70945e1e7..8a8756ffd2d 100644 --- a/code/modules/mob/dead/corpse.dm +++ b/code/modules/mob/dead/corpse.dm @@ -107,7 +107,6 @@ M.real_name = generateCorpseName() M.set_stat(DEAD) //Kills the new mob if(corpsesynthtype > 0) - to_world("Synth") if(!corpsesynthbrand) corpsesynthbrand = "Unbranded" for(var/obj/item/organ/external/O in M.organs) @@ -250,4 +249,4 @@ corpsehelmet = /obj/item/clothing/head/bearpelt /obj/effect/landmark/mobcorpse/russian/ranged - corpsehelmet = /obj/item/clothing/head/ushanka \ No newline at end of file + corpsehelmet = /obj/item/clothing/head/ushanka diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 94155d5643d..0342494684a 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -1,12 +1,12 @@ -//Nobody here anymore. -/mob/observer/dead/Login() - ..() //Creates the plane_holder lazily - plane_holder.set_vis(VIS_GHOSTS, ghostvision) - plane_holder.set_vis(VIS_FULLBRIGHT, !seedarkness) - plane_holder.set_vis(VIS_CLOAKED, TRUE) - plane_holder.set_vis(VIS_AI_EYE, TRUE) - plane_holder.set_vis(VIS_AUGMENTED, TRUE) //VOREStation Add - GHOST VISION IS AUGMENTED - plane = PLANE_GHOSTS - if(cleanup_timer) - deltimer(cleanup_timer) +//Nobody here anymore. +/mob/observer/dead/Login() + ..() //Creates the plane_holder lazily + plane_holder.set_vis(VIS_GHOSTS, ghostvision) + plane_holder.set_vis(VIS_FULLBRIGHT, !seedarkness) + plane_holder.set_vis(VIS_CLOAKED, TRUE) + plane_holder.set_vis(VIS_AI_EYE, TRUE) + plane_holder.set_vis(VIS_AUGMENTED, TRUE) //VOREStation Add - GHOST VISION IS AUGMENTED + plane = PLANE_GHOSTS + if(cleanup_timer) + deltimer(cleanup_timer) cleanup_timer = null \ No newline at end of file diff --git a/code/modules/mob/dead/observer/logout.dm b/code/modules/mob/dead/observer/logout.dm index aca5ebdf6cb..bf7db4bf87d 100644 --- a/code/modules/mob/dead/observer/logout.dm +++ b/code/modules/mob/dead/observer/logout.dm @@ -1,7 +1,7 @@ -/mob/observer/dead/Logout() - ..() - spawn(0) - if(src && !key) //we've transferred to another mob. This ghost should be deleted. - qdel(src) - else - cleanup_timer = QDEL_IN(src, 10 MINUTES) +/mob/observer/dead/Logout() + ..() + spawn(0) + if(src && !key) //we've transferred to another mob. This ghost should be deleted. + qdel(src) + else + cleanup_timer = QDEL_IN(src, 10 MINUTES) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 6819453facc..32ec266f4c9 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -323,10 +323,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set desc = "Toggles AntagHUD allowing you to see who is the antagonist" if(!config.antag_hud_allowed && !client.holder) - to_chat(src, "Admins have disabled this for this round.") + to_chat(src, "[span_red("Admins have disabled this for this round.")]") return if(jobban_isbanned(src, "AntagHUD")) - to_chat(src, "You have been banned from using this feature") + to_chat(src, "[span_red("You have been banned from using this feature")]") return if(config.antag_hud_restricted && !has_enabled_antagHUD && !client.holder) var/response = tgui_alert(src, "If you turn this on, you will not be able to take any part in the round.","Are you sure you want to turn this feature on?",list("Yes","No")) @@ -597,11 +597,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/observer/dead/memory() set hidden = 1 - to_chat(src, "You are dead! You have no mind to store memory!") + to_chat(src, "[span_red("You are dead! You have no mind to store memory!")]") /mob/observer/dead/add_memory() set hidden = 1 - to_chat(src, "You are dead! You have no mind to store memory!") + to_chat(src, "[span_red("You are dead! You have no mind to store memory!")]") /mob/observer/dead/Post_Incorpmove() stop_following() @@ -625,7 +625,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(abs(pressure - ONE_ATMOSPHERE) < 10) gas_analyzing += "Pressure: [round(pressure,0.1)] kPa" else - gas_analyzing += "Pressure: [round(pressure,0.1)] kPa" + gas_analyzing += span_red("Pressure: [round(pressure,0.1)] kPa") if(total_moles) for(var/g in environment.gas) gas_analyzing += "[gas_data.name[g]]: [round((environment.gas[g] / total_moles) * 100)]% ([round(environment.gas[g], 0.01)] moles)" @@ -724,7 +724,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set desc = "If the round is sufficiently spooky, write a short message in blood on the floor or a wall. Remember, no IC in OOC or OOC in IC." if(!(config.cult_ghostwriter)) - to_chat(src, "That verb is not currently permitted.") + to_chat(src, "[span_red("That verb is not currently permitted.")]") return if (!src.stat) @@ -739,7 +739,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghosts_can_write = 1 if(!ghosts_can_write && !check_rights(R_ADMIN|R_EVENT|R_FUN, 0)) //Let's allow for admins to write in blood for events and the such. - to_chat(src, "The veil is not thin enough for you to do that.") + to_chat(src, "[span_red("The veil is not thin enough for you to do that.")]") return var/list/choices = list() @@ -789,7 +789,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp W.update_icon() W.message = message W.add_hiddenprint(src) - W.visible_message("Invisible fingers crudely paint something in blood on [T]...") + W.visible_message("[span_red("Invisible fingers crudely paint something in blood on [T]...")]") /mob/observer/dead/pointed(atom/A as mob|obj|turf in view()) if(!..()) @@ -801,7 +801,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp is_manifest = TRUE verbs |= /mob/observer/dead/proc/toggle_visibility verbs |= /mob/observer/dead/proc/ghost_whisper - to_chat(src, "As you are now in the realm of the living, you can whisper to the living with the Spectral Whisper verb, inside the IC tab.") + to_chat(src, "[span_purple("As you are now in the realm of the living, you can whisper to the living with the Spectral Whisper verb, inside the IC tab.")]") if(plane != PLANE_WORLD) user.visible_message( \ "\The [user] drags ghost, [src], to our plane of reality!", \ @@ -944,7 +944,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/msg = sanitize(tgui_input_text(src, "Message:", "Spectral Whisper")) if(msg) log_say("(SPECWHISP to [key_name(M)]): [msg]", src) - to_chat(M, " You hear a strange, unidentifiable voice in your head... [msg]") + to_chat(M, " You hear a strange, unidentifiable voice in your head... [span_purple("[msg]")]") to_chat(src, " You said: '[msg]' to [M].") else return diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 96aa9ccada4..fa26cc707cf 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,42 +1,42 @@ -/mob/observer/dead/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - message = sanitize(message) - - if(!message) - return - - log_ghostsay(message, src) - - if (client) - if(message) - client.handle_spam_prevention(MUTE_DEADCHAT) - if(client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot talk in deadchat (muted).") - return - - . = say_dead(message) - - -/mob/observer/dead/me_verb(message as text) - if(!message) - return - - log_ghostemote(message, src) - - if(client) - if(message) - client.handle_spam_prevention(MUTE_DEADCHAT) - if(client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot emote in deadchat (muted).") - return - - . = emote_dead(message) - -/mob/observer/dead/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear) - return "[speaker_name] ([ghost_follow_link(speaker, src)])" - -/mob/observer/dead/handle_speaker_name(mob/speaker = null, vname, hard_to_hear) - var/speaker_name = ..() - //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. - if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) - speaker_name = "[speaker.real_name] ([speaker_name])" - return speaker_name \ No newline at end of file +/mob/observer/dead/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + message = sanitize(message) + + if(!message) + return + + log_ghostsay(message, src) + + if (client) + if(message) + client.handle_spam_prevention(MUTE_DEADCHAT) + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "[span_red("You cannot talk in deadchat (muted).")]") + return + + . = say_dead(message) + + +/mob/observer/dead/me_verb(message as text) + if(!message) + return + + log_ghostemote(message, src) + + if(client) + if(message) + client.handle_spam_prevention(MUTE_DEADCHAT) + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "[span_red("You cannot emote in deadchat (muted).")]") + return + + . = emote_dead(message) + +/mob/observer/dead/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear) + return "[speaker_name] ([ghost_follow_link(speaker, src)])" + +/mob/observer/dead/handle_speaker_name(mob/speaker = null, vname, hard_to_hear) + var/speaker_name = ..() + //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. + if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) + speaker_name = "[speaker.real_name] ([speaker_name])" + return speaker_name diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 48cdfa3ebd7..712393fd211 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -1,37 +1,37 @@ -// Shortcuts for above proc -/mob/proc/visible_emote(var/act_desc) - custom_emote(VISIBLE_MESSAGE, act_desc) - -/mob/proc/audible_emote(var/act_desc) - custom_emote(AUDIBLE_MESSAGE, act_desc) - -/mob/proc/emote_dead(var/message) - - if(client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send deadchat emotes (muted).") - return - - if(!is_preference_enabled(/datum/client_preference/show_dsay)) - to_chat(src, "You have deadchat muted.") - return - - if(!src.client.holder) - if(!config.dsay_allowed) - to_chat(src, "Deadchat is globally muted.") - return - - - var/input - if(!message) - input = sanitize_or_reflect(tgui_input_text(src, "Choose an emote to display."), src) //VOREStation Edit - Reflect too long messages, within reason - else - input = message - - input = encode_html_emphasis(input) - - if(input) - log_ghostemote(input, src) - if(!invisibility) //If the ghost is made visible by admins or cult. And to see if the ghost has toggled its own visibility, as well. -Mech - visible_message("[src] [input]") - else - say_dead_direct(input, src) +// Shortcuts for above proc +/mob/proc/visible_emote(var/act_desc) + custom_emote(VISIBLE_MESSAGE, act_desc) + +/mob/proc/audible_emote(var/act_desc) + custom_emote(AUDIBLE_MESSAGE, act_desc) + +/mob/proc/emote_dead(var/message) + + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send deadchat emotes (muted).") + return + + if(!is_preference_enabled(/datum/client_preference/show_dsay)) + to_chat(src, "You have deadchat muted.") + return + + if(!src.client.holder) + if(!config.dsay_allowed) + to_chat(src, "Deadchat is globally muted.") + return + + + var/input + if(!message) + input = sanitize_or_reflect(tgui_input_text(src, "Choose an emote to display."), src) //VOREStation Edit - Reflect too long messages, within reason + else + input = message + + input = encode_html_emphasis(input) + + if(input) + log_ghostemote(input, src) + if(!invisibility) //If the ghost is made visible by admins or cult. And to see if the ghost has toggled its own visibility, as well. -Mech + visible_message("[src] [input]") + else + say_dead_direct(input, src) diff --git a/code/modules/mob/freelook/ai/cameranet.dm b/code/modules/mob/freelook/ai/cameranet.dm index f29749b38c1..d1b395d8234 100644 --- a/code/modules/mob/freelook/ai/cameranet.dm +++ b/code/modules/mob/freelook/ai/cameranet.dm @@ -1,44 +1,44 @@ -// CAMERA NET -// -// The datum containing all the chunks. - -/datum/visualnet/camera - // The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Destroy(). - var/list/cameras = list() - var/cameras_unsorted = 1 - chunk_type = /datum/chunk/camera - -/datum/visualnet/camera/proc/process_sort() - if(cameras_unsorted) - cameras = dd_sortedObjectList(cameras) - cameras_unsorted = 0 - -// Removes a camera from a chunk. - -/datum/visualnet/camera/proc/removeCamera(obj/machinery/camera/c) - if(c.can_use()) - majorChunkChange(c, 0) - -// Add a camera to a chunk. - -/datum/visualnet/camera/proc/addCamera(obj/machinery/camera/c) - if(c.can_use()) - majorChunkChange(c, 1) - -// Used for Cyborg cameras. Since portable cameras can be in ANY chunk. - -/datum/visualnet/camera/proc/updatePortableCamera(obj/machinery/camera/c) - if(c.can_use()) - majorChunkChange(c, 1) - //else - // majorChunkChange(c, 0) - -/datum/visualnet/camera/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/camera/chunk) -// Only add actual cameras to the list of cameras - if(istype(c, /obj/machinery/camera)) - if(choice == 0) - // Remove the camera. - chunk.cameras -= c - else if(choice == 1) - // You can't have the same camera in the list twice. - chunk.cameras |= c +// CAMERA NET +// +// The datum containing all the chunks. + +/datum/visualnet/camera + // The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Destroy(). + var/list/cameras = list() + var/cameras_unsorted = 1 + chunk_type = /datum/chunk/camera + +/datum/visualnet/camera/proc/process_sort() + if(cameras_unsorted) + cameras = dd_sortedObjectList(cameras) + cameras_unsorted = 0 + +// Removes a camera from a chunk. + +/datum/visualnet/camera/proc/removeCamera(obj/machinery/camera/c) + if(c.can_use()) + majorChunkChange(c, 0) + +// Add a camera to a chunk. + +/datum/visualnet/camera/proc/addCamera(obj/machinery/camera/c) + if(c.can_use()) + majorChunkChange(c, 1) + +// Used for Cyborg cameras. Since portable cameras can be in ANY chunk. + +/datum/visualnet/camera/proc/updatePortableCamera(obj/machinery/camera/c) + if(c.can_use()) + majorChunkChange(c, 1) + //else + // majorChunkChange(c, 0) + +/datum/visualnet/camera/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/camera/chunk) +// Only add actual cameras to the list of cameras + if(istype(c, /obj/machinery/camera)) + if(choice == 0) + // Remove the camera. + chunk.cameras -= c + else if(choice == 1) + // You can't have the same camera in the list twice. + chunk.cameras |= c diff --git a/code/modules/mob/freelook/ai/chunk.dm b/code/modules/mob/freelook/ai/chunk.dm index ebd68a62069..8f5a9317eca 100644 --- a/code/modules/mob/freelook/ai/chunk.dm +++ b/code/modules/mob/freelook/ai/chunk.dm @@ -1,48 +1,48 @@ -// CAMERA CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the Eye to stream these chunks and know what it can and cannot see. - -/datum/chunk/camera - var/list/cameras = list() - -/datum/chunk/camera/acquireVisibleTurfs(var/list/visible) - for(var/obj/machinery/camera/c as anything in cameras) - - if(!istype(c)) - cameras -= c - continue - - if(!c.can_use()) - continue - - var/turf/point = locate(src.x + 8, src.y + 8, src.z) - if(get_dist(point, c) > 24) - cameras -= c - - for(var/turf/t in c.can_see()) - visible[t] = t - - for(var/mob/living/silicon/ai/AI in living_mob_list) - for(var/turf/t in AI.seen_camera_turfs()) - visible[t] = t - -// Create a new camera chunk, since the chunks are made as they are needed. - -/datum/chunk/camera/New(loc, x, y, z) - for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) - if(c.can_use()) - cameras += c - ..() - -/mob/living/silicon/proc/provides_camera_vision() - return 0 - -/mob/living/silicon/ai/provides_camera_vision() - return stat != DEAD - -/mob/living/silicon/robot/provides_camera_vision() - return src.camera && src.camera.network.len && (z in using_map.contact_levels) //VOREStation Edit - -/mob/living/silicon/ai/proc/seen_camera_turfs() - return seen_turfs_in_range(src, world.view) +// CAMERA CHUNK +// +// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. +// Allows the Eye to stream these chunks and know what it can and cannot see. + +/datum/chunk/camera + var/list/cameras = list() + +/datum/chunk/camera/acquireVisibleTurfs(var/list/visible) + for(var/obj/machinery/camera/c as anything in cameras) + + if(!istype(c)) + cameras -= c + continue + + if(!c.can_use()) + continue + + var/turf/point = locate(src.x + 8, src.y + 8, src.z) + if(get_dist(point, c) > 24) + cameras -= c + + for(var/turf/t in c.can_see()) + visible[t] = t + + for(var/mob/living/silicon/ai/AI in living_mob_list) + for(var/turf/t in AI.seen_camera_turfs()) + visible[t] = t + +// Create a new camera chunk, since the chunks are made as they are needed. + +/datum/chunk/camera/New(loc, x, y, z) + for(var/obj/machinery/camera/c in range(16, locate(x + 8, y + 8, z))) + if(c.can_use()) + cameras += c + ..() + +/mob/living/silicon/proc/provides_camera_vision() + return 0 + +/mob/living/silicon/ai/provides_camera_vision() + return stat != DEAD + +/mob/living/silicon/robot/provides_camera_vision() + return src.camera && src.camera.network.len && (z in using_map.contact_levels) //VOREStation Edit + +/mob/living/silicon/ai/proc/seen_camera_turfs() + return seen_turfs_in_range(src, world.view) diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm index 711dbd30855..fa3fb311f0e 100644 --- a/code/modules/mob/freelook/ai/eye.dm +++ b/code/modules/mob/freelook/ai/eye.dm @@ -1,115 +1,115 @@ -// AI EYE -// -// A mob that the AI controls to look around the station with. -// It streams chunks as it moves around, which will show it what the AI can and cannot see. - -/mob/observer/eye/aiEye - name = "Inactive AI Eye" - icon_state = "AI-eye" - -/mob/observer/eye/aiEye/New() - ..() - visualnet = cameranet - -/mob/observer/eye/aiEye/Destroy() - if(owner) - var/mob/living/silicon/ai/ai = owner - ai.all_eyes -= src - owner = null - . = ..() - -/mob/observer/eye/aiEye/setLoc(var/T, var/cancel_tracking = 1) - if(owner) - T = get_turf(T) - loc = T - - var/mob/living/silicon/ai/ai = owner - if(cancel_tracking) - ai.ai_cancel_tracking() - - if(use_static) - ai.camera_visibility(src) - - if(ai.client && !ai.multicam_on) - ai.client.eye = src - - if(ai.master_multicam) - ai.master_multicam.refresh_view() - - if(ai.holo) - if(ai.hologram_follow) - ai.holo.move_hologram(ai) - - return 1 - -// AI MOVEMENT - -// The AI's "eye". Described on the top of the page. - -/mob/living/silicon/ai - var/obj/machinery/hologram/holopad/holo = null - -/mob/living/silicon/ai/proc/destroy_eyeobj(var/atom/new_eye) - if(!eyeobj) return - if(!new_eye) - new_eye = src - eyeobj.owner = null - qdel(eyeobj) // No AI, no Eye - eyeobj = null - if(client) - client.eye = new_eye - -/mob/living/silicon/ai/proc/create_eyeobj(var/newloc) - if(eyeobj) - destroy_eyeobj() - if(!newloc) - newloc = src.loc - eyeobj = new /mob/observer/eye/aiEye(newloc) - all_eyes += eyeobj - eyeobj.owner = src - eyeobj.name = "[src.name] (AI Eye)" // Give it a name - if(client) - client.eye = eyeobj - SetName(src.name) - -// Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us. -/mob/living/silicon/ai/Initialize() - . = ..() - create_eyeobj() - if(eyeobj) - eyeobj.loc = src.loc - -/mob/living/silicon/ai/Destroy() - destroy_eyeobj() - return ..() - -/atom/proc/move_camera_by_click() - if(istype(usr, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = usr - if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj))) - var/turf/T = get_turf(src) - if(T) - AI.eyeobj.setLoc(T) - -/mob/living/silicon/ai/proc/view_core() - camera = null - unset_machine() - - if(!src.eyeobj) - return - - if(client && client.eye) - client.eye = src - for(var/datum/chunk/c in eyeobj.visibleChunks) - c.remove(eyeobj) - src.eyeobj.setLoc(src) - -/mob/living/silicon/ai/proc/toggle_acceleration() - set category = "AI Settings" - set name = "Toggle Camera Acceleration" - - if(!eyeobj) - return - - eyeobj.acceleration = !eyeobj.acceleration - to_chat(usr, "Camera acceleration has been toggled [eyeobj.acceleration ? "on" : "off"].") +// AI EYE +// +// A mob that the AI controls to look around the station with. +// It streams chunks as it moves around, which will show it what the AI can and cannot see. + +/mob/observer/eye/aiEye + name = "Inactive AI Eye" + icon_state = "AI-eye" + +/mob/observer/eye/aiEye/New() + ..() + visualnet = cameranet + +/mob/observer/eye/aiEye/Destroy() + if(owner) + var/mob/living/silicon/ai/ai = owner + ai.all_eyes -= src + owner = null + . = ..() + +/mob/observer/eye/aiEye/setLoc(var/T, var/cancel_tracking = 1) + if(owner) + T = get_turf(T) + loc = T + + var/mob/living/silicon/ai/ai = owner + if(cancel_tracking) + ai.ai_cancel_tracking() + + if(use_static) + ai.camera_visibility(src) + + if(ai.client && !ai.multicam_on) + ai.client.eye = src + + if(ai.master_multicam) + ai.master_multicam.refresh_view() + + if(ai.holo) + if(ai.hologram_follow) + ai.holo.move_hologram(ai) + + return 1 + +// AI MOVEMENT + +// The AI's "eye". Described on the top of the page. + +/mob/living/silicon/ai + var/obj/machinery/hologram/holopad/holo = null + +/mob/living/silicon/ai/proc/destroy_eyeobj(var/atom/new_eye) + if(!eyeobj) return + if(!new_eye) + new_eye = src + eyeobj.owner = null + qdel(eyeobj) // No AI, no Eye + eyeobj = null + if(client) + client.eye = new_eye + +/mob/living/silicon/ai/proc/create_eyeobj(var/newloc) + if(eyeobj) + destroy_eyeobj() + if(!newloc) + newloc = src.loc + eyeobj = new /mob/observer/eye/aiEye(newloc) + all_eyes += eyeobj + eyeobj.owner = src + eyeobj.name = "[src.name] (AI Eye)" // Give it a name + if(client) + client.eye = eyeobj + SetName(src.name) + +// Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us. +/mob/living/silicon/ai/Initialize() + . = ..() + create_eyeobj() + if(eyeobj) + eyeobj.loc = src.loc + +/mob/living/silicon/ai/Destroy() + destroy_eyeobj() + return ..() + +/atom/proc/move_camera_by_click() + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = usr + if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj))) + var/turf/T = get_turf(src) + if(T) + AI.eyeobj.setLoc(T) + +/mob/living/silicon/ai/proc/view_core() + camera = null + unset_machine() + + if(!src.eyeobj) + return + + if(client && client.eye) + client.eye = src + for(var/datum/chunk/c in eyeobj.visibleChunks) + c.remove(eyeobj) + src.eyeobj.setLoc(src) + +/mob/living/silicon/ai/proc/toggle_acceleration() + set category = "AI Settings" + set name = "Toggle Camera Acceleration" + + if(!eyeobj) + return + + eyeobj.acceleration = !eyeobj.acceleration + to_chat(usr, "Camera acceleration has been toggled [eyeobj.acceleration ? "on" : "off"].") diff --git a/code/modules/mob/freelook/ai/update_triggers.dm b/code/modules/mob/freelook/ai/update_triggers.dm index 9fd28344df2..3ce11bb42c7 100644 --- a/code/modules/mob/freelook/ai/update_triggers.dm +++ b/code/modules/mob/freelook/ai/update_triggers.dm @@ -1,72 +1,72 @@ -#define BORG_CAMERA_BUFFER 30 - -// ROBOT MOVEMENT - -// Update the portable camera everytime the Robot moves. -// This might be laggy, comment it out if there are problems. -/mob/living/silicon/var/updating = 0 - -/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(!provides_camera_vision()) - return - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(old_loc != src.loc) - cameranet.updatePortableCamera(src.camera) - updating = 0 - -/mob/living/silicon/ai/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(!provides_camera_vision()) - return - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(old_loc != src.loc) - cameranet.updateVisibility(old_loc, 0) - cameranet.updateVisibility(loc, 0) - updating = 0 - -#undef BORG_CAMERA_BUFFER - -// CAMERA - -// An addition to deactivate which removes/adds the camera from the chunk list based on if it works or not. - -/obj/machinery/camera/deactivate(user as mob, var/choice = 1) - ..(user, choice) - if(src.can_use()) - cameranet.addCamera(src) - else - src.set_light(0) - cameranet.removeCamera(src) - -/obj/machinery/camera/New() - ..() - //Camera must be added to global list of all cameras no matter what... - if(cameranet.cameras_unsorted || !ticker) - cameranet.cameras += src - cameranet.cameras_unsorted = 1 - else - dd_insertObjectList(cameranet.cameras, src) - update_coverage(1) - -/obj/machinery/camera/Destroy() - clear_all_networks() - cameranet.cameras -= src - return ..() - -// Mobs -/mob/living/silicon/ai/rejuvenate() - var/was_dead = stat == DEAD - ..() - if(was_dead && stat != DEAD) - // Arise! - cameranet.updateVisibility(src, 0) - -/mob/living/silicon/ai/death(gibbed) - if(..()) - // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) - cameranet.updateVisibility(src, 0) +#define BORG_CAMERA_BUFFER 30 + +// ROBOT MOVEMENT + +// Update the portable camera everytime the Robot moves. +// This might be laggy, comment it out if there are problems. +/mob/living/silicon/var/updating = 0 + +/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(!provides_camera_vision()) + return + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(old_loc != src.loc) + cameranet.updatePortableCamera(src.camera) + updating = 0 + +/mob/living/silicon/ai/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(!provides_camera_vision()) + return + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(old_loc != src.loc) + cameranet.updateVisibility(old_loc, 0) + cameranet.updateVisibility(loc, 0) + updating = 0 + +#undef BORG_CAMERA_BUFFER + +// CAMERA + +// An addition to deactivate which removes/adds the camera from the chunk list based on if it works or not. + +/obj/machinery/camera/deactivate(user as mob, var/choice = 1) + ..(user, choice) + if(src.can_use()) + cameranet.addCamera(src) + else + src.set_light(0) + cameranet.removeCamera(src) + +/obj/machinery/camera/New() + ..() + //Camera must be added to global list of all cameras no matter what... + if(cameranet.cameras_unsorted || !ticker) + cameranet.cameras += src + cameranet.cameras_unsorted = 1 + else + dd_insertObjectList(cameranet.cameras, src) + update_coverage(1) + +/obj/machinery/camera/Destroy() + clear_all_networks() + cameranet.cameras -= src + return ..() + +// Mobs +/mob/living/silicon/ai/rejuvenate() + var/was_dead = stat == DEAD + ..() + if(was_dead && stat != DEAD) + // Arise! + cameranet.updateVisibility(src, 0) + +/mob/living/silicon/ai/death(gibbed) + if(..()) + // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) + cameranet.updateVisibility(src, 0) diff --git a/code/modules/mob/freelook/chunk.dm b/code/modules/mob/freelook/chunk.dm index 4ae5d3a590a..8886207e81d 100644 --- a/code/modules/mob/freelook/chunk.dm +++ b/code/modules/mob/freelook/chunk.dm @@ -1,149 +1,149 @@ -#define UPDATE_BUFFER 25 // 2.5 seconds - -// CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the Eye to stream these chunks and know what it can and cannot see. - -/datum/obfuscation - var/icon = 'icons/effects/cameravis.dmi' - var/icon_state = "black" - -/datum/chunk - var/list/obscuredTurfs = list() - var/list/visibleTurfs = list() - var/list/obscured = list() - var/list/turfs = list() - var/list/seenby = list() - var/visible = 0 - var/changed = 0 - var/updating = 0 - var/x = 0 - var/y = 0 - var/z = 0 - var/datum/obfuscation/obfuscation = new() - -// Add an eye to the chunk, then update if changed. - -/datum/chunk/proc/add(mob/observer/eye/eye, add_images = TRUE) - if(add_images) - var/client/client = eye.GetViewerClient() - if(client) - client.images += obscured - eye.visibleChunks += src - visible++ - seenby += eye - if(changed && !updating) - update() - -// Remove an eye from the chunk, then update if changed. - -/datum/chunk/proc/remove(mob/observer/eye/eye, remove_images = TRUE) - if(remove_images) - var/client/client = eye.GetViewerClient() - if(client) - client.images -= obscured - eye.visibleChunks -= src - seenby -= eye - if(visible > 0) - visible-- - -// Called when a chunk has changed. I.E: A wall was deleted. - -/datum/chunk/proc/visibilityChanged(turf/loc) - if(!visibleTurfs[loc]) - return - hasChanged() - -// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will -// instead be flagged to update the next time an AI Eye moves near it. - -/datum/chunk/proc/hasChanged(var/update_now = 0) - if(visible || update_now) - if(!updating) - updating = 1 - spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once - update() - updating = 0 - else - changed = 1 - -// The actual updating. - -/datum/chunk/proc/update() - - set background = 1 - - var/list/newVisibleTurfs = new() - acquireVisibleTurfs(newVisibleTurfs) - - // Removes turf that isn't in turfs. - newVisibleTurfs &= turfs - - var/list/visAdded = newVisibleTurfs - visibleTurfs - var/list/visRemoved = visibleTurfs - newVisibleTurfs - - visibleTurfs = newVisibleTurfs - obscuredTurfs = turfs - newVisibleTurfs - - for(var/turf/t as anything in visAdded) - if(LAZYLEN(t.obfuscations) && t.obfuscations[obfuscation.type]) - obscured -= t.obfuscations[obfuscation.type] - for(var/mob/observer/eye/m as anything in seenby) - if(!m) - continue - var/client/client = m.GetViewerClient() - if(client) - client.images -= t.obfuscations[obfuscation.type] - - for(var/turf/t as anything in visRemoved) - if(obscuredTurfs[t]) - LAZYINITLIST(t.obfuscations) - if(!t.obfuscations[obfuscation.type]) - var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) - ob_image.plane = PLANE_FULLSCREEN - t.obfuscations[obfuscation.type] = ob_image - - obscured += t.obfuscations[obfuscation.type] - for(var/mob/observer/eye/m as anything in seenby) - if(!m) - seenby -= m - continue - var/client/client = m.GetViewerClient() - if(client) - client.images += t.obfuscations[obfuscation.type] - -/datum/chunk/proc/acquireVisibleTurfs(var/list/visible) - -// Create a new camera chunk, since the chunks are made as they are needed. - -/datum/chunk/New(loc, x, y, z) - - // 0xf = 15 - x &= ~0xf - y &= ~0xf - - src.x = x - src.y = y - src.z = z - - for(var/turf/t in range(10, locate(x + 8, y + 8, z))) - if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16) - turfs[t] = t - - acquireVisibleTurfs(visibleTurfs) - - // Removes turf that isn't in turfs. - visibleTurfs &= turfs - - obscuredTurfs = turfs - visibleTurfs - - for(var/turf/t as anything in obscuredTurfs) - LAZYINITLIST(t.obfuscations) - if(!t.obfuscations[obfuscation.type]) - var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) - ob_image.plane = PLANE_FULLSCREEN - t.obfuscations[obfuscation.type] = ob_image - obscured += t.obfuscations[obfuscation.type] - -#undef UPDATE_BUFFER +#define UPDATE_BUFFER 25 // 2.5 seconds + +// CHUNK +// +// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. +// Allows the Eye to stream these chunks and know what it can and cannot see. + +/datum/obfuscation + var/icon = 'icons/effects/cameravis.dmi' + var/icon_state = "black" + +/datum/chunk + var/list/obscuredTurfs = list() + var/list/visibleTurfs = list() + var/list/obscured = list() + var/list/turfs = list() + var/list/seenby = list() + var/visible = 0 + var/changed = 0 + var/updating = 0 + var/x = 0 + var/y = 0 + var/z = 0 + var/datum/obfuscation/obfuscation = new() + +// Add an eye to the chunk, then update if changed. + +/datum/chunk/proc/add(mob/observer/eye/eye, add_images = TRUE) + if(add_images) + var/client/client = eye.GetViewerClient() + if(client) + client.images += obscured + eye.visibleChunks += src + visible++ + seenby += eye + if(changed && !updating) + update() + +// Remove an eye from the chunk, then update if changed. + +/datum/chunk/proc/remove(mob/observer/eye/eye, remove_images = TRUE) + if(remove_images) + var/client/client = eye.GetViewerClient() + if(client) + client.images -= obscured + eye.visibleChunks -= src + seenby -= eye + if(visible > 0) + visible-- + +// Called when a chunk has changed. I.E: A wall was deleted. + +/datum/chunk/proc/visibilityChanged(turf/loc) + if(!visibleTurfs[loc]) + return + hasChanged() + +// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will +// instead be flagged to update the next time an AI Eye moves near it. + +/datum/chunk/proc/hasChanged(var/update_now = 0) + if(visible || update_now) + if(!updating) + updating = 1 + spawn(UPDATE_BUFFER) // Batch large changes, such as many doors opening or closing at once + update() + updating = 0 + else + changed = 1 + +// The actual updating. + +/datum/chunk/proc/update() + + set background = 1 + + var/list/newVisibleTurfs = new() + acquireVisibleTurfs(newVisibleTurfs) + + // Removes turf that isn't in turfs. + newVisibleTurfs &= turfs + + var/list/visAdded = newVisibleTurfs - visibleTurfs + var/list/visRemoved = visibleTurfs - newVisibleTurfs + + visibleTurfs = newVisibleTurfs + obscuredTurfs = turfs - newVisibleTurfs + + for(var/turf/t as anything in visAdded) + if(LAZYLEN(t.obfuscations) && t.obfuscations[obfuscation.type]) + obscured -= t.obfuscations[obfuscation.type] + for(var/mob/observer/eye/m as anything in seenby) + if(!m) + continue + var/client/client = m.GetViewerClient() + if(client) + client.images -= t.obfuscations[obfuscation.type] + + for(var/turf/t as anything in visRemoved) + if(obscuredTurfs[t]) + LAZYINITLIST(t.obfuscations) + if(!t.obfuscations[obfuscation.type]) + var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) + ob_image.plane = PLANE_FULLSCREEN + t.obfuscations[obfuscation.type] = ob_image + + obscured += t.obfuscations[obfuscation.type] + for(var/mob/observer/eye/m as anything in seenby) + if(!m) + seenby -= m + continue + var/client/client = m.GetViewerClient() + if(client) + client.images += t.obfuscations[obfuscation.type] + +/datum/chunk/proc/acquireVisibleTurfs(var/list/visible) + +// Create a new camera chunk, since the chunks are made as they are needed. + +/datum/chunk/New(loc, x, y, z) + + // 0xf = 15 + x &= ~0xf + y &= ~0xf + + src.x = x + src.y = y + src.z = z + + for(var/turf/t in range(10, locate(x + 8, y + 8, z))) + if(t.x >= x && t.y >= y && t.x < x + 16 && t.y < y + 16) + turfs[t] = t + + acquireVisibleTurfs(visibleTurfs) + + // Removes turf that isn't in turfs. + visibleTurfs &= turfs + + obscuredTurfs = turfs - visibleTurfs + + for(var/turf/t as anything in obscuredTurfs) + LAZYINITLIST(t.obfuscations) + if(!t.obfuscations[obfuscation.type]) + var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER) + ob_image.plane = PLANE_FULLSCREEN + t.obfuscations[obfuscation.type] = ob_image + obscured += t.obfuscations[obfuscation.type] + +#undef UPDATE_BUFFER diff --git a/code/modules/mob/freelook/eye.dm b/code/modules/mob/freelook/eye.dm index 05aba49008b..80b1125c8b2 100644 --- a/code/modules/mob/freelook/eye.dm +++ b/code/modules/mob/freelook/eye.dm @@ -1,111 +1,111 @@ -// EYE -// -// A mob that another mob controls to look around the station with. -// It streams chunks as it moves around, which will show it what the controller can and cannot see. - -/mob/observer/eye - name = "Eye" - icon = 'icons/mob/eye.dmi' - icon_state = "default-eye" - alpha = 127 - - var/sprint = 10 - var/cooldown = 0 - var/acceleration = 1 - var/owner_follows_eye = 0 - - see_in_dark = 7 - status_flags = GODMODE - plane = PLANE_AI_EYE - - var/mob/owner = null - var/list/visibleChunks = list() - - var/ghostimage = null - var/datum/visualnet/visualnet - var/use_static = TRUE - var/static_visibility_range = 16 - -/mob/observer/eye/Destroy() - if(owner) - if(owner.eyeobj == src) - owner.eyeobj = null - owner = null - . = ..() - -/mob/observer/eye/Move(n, direct) - if(owner == src) - return EyeMove(n, direct) - return 0 - -/mob/observer/eye/airflow_hit(atom/A) - airflow_speed = 0 - airflow_dest = null - -/mob/observer/eye/examinate() - set popup_menu = 0 - set src = usr.contents - return 0 - -/mob/observer/eye/pointed() - set popup_menu = 0 - set src = usr.contents - return 0 - -// Use this when setting the eye's location. -// It will also stream the chunk that the new loc is in. -/mob/observer/eye/proc/setLoc(var/T) - if(owner) - T = get_turf(T) - if(T != loc) - loc = T - - if(owner.client) - owner.client.eye = src - - if(owner_follows_eye) - visualnet.updateVisibility(owner, 0) - owner.loc = loc - visualnet.updateVisibility(owner, 0) - if(use_static) - visualnet.visibility(src, owner.client) - return 1 - return 0 - -/mob/observer/eye/proc/getLoc() - if(owner) - if(!isturf(owner.loc) || !owner.client) - return - return loc -/mob - var/mob/observer/eye/eyeobj - -/mob/proc/EyeMove(n, direct) - if(!eyeobj) - return - - return eyeobj.EyeMove(n, direct) - -/mob/observer/eye/proc/GetViewerClient() - if(owner) - return owner.client - return null - -/mob/observer/eye/EyeMove(n, direct) - var/initial = initial(sprint) - var/max_sprint = 50 - - if(cooldown && cooldown < world.timeofday) - sprint = initial - - for(var/i = 0; i < max(sprint, initial); i += 20) - var/turf/step = get_turf(get_step(src, direct)) - if(step) - setLoc(step) - - cooldown = world.timeofday + 5 - if(acceleration) - sprint = min(sprint + 0.5, max_sprint) - else - sprint = initial - return 1 +// EYE +// +// A mob that another mob controls to look around the station with. +// It streams chunks as it moves around, which will show it what the controller can and cannot see. + +/mob/observer/eye + name = "Eye" + icon = 'icons/mob/eye.dmi' + icon_state = "default-eye" + alpha = 127 + + var/sprint = 10 + var/cooldown = 0 + var/acceleration = 1 + var/owner_follows_eye = 0 + + see_in_dark = 7 + status_flags = GODMODE + plane = PLANE_AI_EYE + + var/mob/owner = null + var/list/visibleChunks = list() + + var/ghostimage = null + var/datum/visualnet/visualnet + var/use_static = TRUE + var/static_visibility_range = 16 + +/mob/observer/eye/Destroy() + if(owner) + if(owner.eyeobj == src) + owner.eyeobj = null + owner = null + . = ..() + +/mob/observer/eye/Move(n, direct) + if(owner == src) + return EyeMove(n, direct) + return 0 + +/mob/observer/eye/airflow_hit(atom/A) + airflow_speed = 0 + airflow_dest = null + +/mob/observer/eye/examinate() + set popup_menu = 0 + set src = usr.contents + return 0 + +/mob/observer/eye/pointed() + set popup_menu = 0 + set src = usr.contents + return 0 + +// Use this when setting the eye's location. +// It will also stream the chunk that the new loc is in. +/mob/observer/eye/proc/setLoc(var/T) + if(owner) + T = get_turf(T) + if(T != loc) + loc = T + + if(owner.client) + owner.client.eye = src + + if(owner_follows_eye) + visualnet.updateVisibility(owner, 0) + owner.loc = loc + visualnet.updateVisibility(owner, 0) + if(use_static) + visualnet.visibility(src, owner.client) + return 1 + return 0 + +/mob/observer/eye/proc/getLoc() + if(owner) + if(!isturf(owner.loc) || !owner.client) + return + return loc +/mob + var/mob/observer/eye/eyeobj + +/mob/proc/EyeMove(n, direct) + if(!eyeobj) + return + + return eyeobj.EyeMove(n, direct) + +/mob/observer/eye/proc/GetViewerClient() + if(owner) + return owner.client + return null + +/mob/observer/eye/EyeMove(n, direct) + var/initial = initial(sprint) + var/max_sprint = 50 + + if(cooldown && cooldown < world.timeofday) + sprint = initial + + for(var/i = 0; i < max(sprint, initial); i += 20) + var/turf/step = get_turf(get_step(src, direct)) + if(step) + setLoc(step) + + cooldown = world.timeofday + 5 + if(acceleration) + sprint = min(sprint + 0.5, max_sprint) + else + sprint = initial + return 1 diff --git a/code/modules/mob/freelook/life.dm b/code/modules/mob/freelook/life.dm index f3994f9a7e6..1ef0ca92a5d 100644 --- a/code/modules/mob/freelook/life.dm +++ b/code/modules/mob/freelook/life.dm @@ -1,7 +1,7 @@ -/mob/observer/eye/Life() - ..() - // If we lost our client, reset the list of visible chunks so they update properly on return - if(owner == src && !client) - visibleChunks.Cut() - /*else if(owner && !owner.client) - visibleChunks.Cut()*/ +/mob/observer/eye/Life() + ..() + // If we lost our client, reset the list of visible chunks so they update properly on return + if(owner == src && !client) + visibleChunks.Cut() + /*else if(owner && !owner.client) + visibleChunks.Cut()*/ diff --git a/code/modules/mob/freelook/mask/chunk.dm b/code/modules/mob/freelook/mask/chunk.dm index b540a8023b6..5042c126b9f 100644 --- a/code/modules/mob/freelook/mask/chunk.dm +++ b/code/modules/mob/freelook/mask/chunk.dm @@ -1,36 +1,36 @@ -// CULT CHUNK -// -// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. -// Allows the Eye to stream these chunks and know what it can and cannot see. - -/datum/obfuscation/cult - icon_state = "white" - -/datum/chunk/cult - obfuscation = new /datum/obfuscation/cult() - -/datum/chunk/cult/acquireVisibleTurfs(var/list/visible) - for(var/mob/living/L in living_mob_list) - for(var/turf/t in L.seen_cult_turfs()) - visible[t] = t - -/mob/living/proc/seen_cult_turfs() - return seen_turfs_in_range(src, 3) - -/mob/living/carbon/human/seen_cult_turfs() - if(mind in cult.current_antagonists) - return seen_turfs_in_range(src, world.view) - return ..() - -/mob/living/silicon/seen_cult_turfs() - return list() - -/mob/living/simple_mob/seen_cult_turfs() - return seen_turfs_in_range(src, 1) - -/mob/living/simple_mob/construct/shade/seen_cult_turfs() - return view(2, src) - -/proc/seen_turfs_in_range(var/source, var/range) - var/turf/pos = get_turf(source) - return hear(range, pos) +// CULT CHUNK +// +// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed. +// Allows the Eye to stream these chunks and know what it can and cannot see. + +/datum/obfuscation/cult + icon_state = "white" + +/datum/chunk/cult + obfuscation = new /datum/obfuscation/cult() + +/datum/chunk/cult/acquireVisibleTurfs(var/list/visible) + for(var/mob/living/L in living_mob_list) + for(var/turf/t in L.seen_cult_turfs()) + visible[t] = t + +/mob/living/proc/seen_cult_turfs() + return seen_turfs_in_range(src, 3) + +/mob/living/carbon/human/seen_cult_turfs() + if(mind in cult.current_antagonists) + return seen_turfs_in_range(src, world.view) + return ..() + +/mob/living/silicon/seen_cult_turfs() + return list() + +/mob/living/simple_mob/seen_cult_turfs() + return seen_turfs_in_range(src, 1) + +/mob/living/simple_mob/construct/shade/seen_cult_turfs() + return view(2, src) + +/proc/seen_turfs_in_range(var/source, var/range) + var/turf/pos = get_turf(source) + return hear(range, pos) diff --git a/code/modules/mob/freelook/mask/cultnet.dm b/code/modules/mob/freelook/mask/cultnet.dm index 4e4e01b8472..6d2f248a2e3 100644 --- a/code/modules/mob/freelook/mask/cultnet.dm +++ b/code/modules/mob/freelook/mask/cultnet.dm @@ -1,15 +1,15 @@ -// CULT NET -// -// The datum containing all the chunks. - -/datum/visualnet/cult - chunk_type = /datum/chunk/cult - -/datum/visualnet/cult/proc/provides_vision(var/mob/living/L) - return L.provides_cult_vision() - -/mob/living/proc/provides_cult_vision() - return 1 - -/mob/living/silicon/provides_cult_vision() - return 0 +// CULT NET +// +// The datum containing all the chunks. + +/datum/visualnet/cult + chunk_type = /datum/chunk/cult + +/datum/visualnet/cult/proc/provides_vision(var/mob/living/L) + return L.provides_cult_vision() + +/mob/living/proc/provides_cult_vision() + return 1 + +/mob/living/silicon/provides_cult_vision() + return 0 diff --git a/code/modules/mob/freelook/mask/eye.dm b/code/modules/mob/freelook/mask/eye.dm index b3abb922abd..4fcbc35149f 100644 --- a/code/modules/mob/freelook/mask/eye.dm +++ b/code/modules/mob/freelook/mask/eye.dm @@ -1,13 +1,13 @@ -// MASK EYE -// -// A mob that a cultists controls to look around the station with. -// It streams chunks as it moves around, which will show it what the cultist can and cannot see. - -/mob/observer/eye/maskEye - name = "Eye of Nar-Sie" - acceleration = 0 - owner_follows_eye = 1 - -/mob/observer/eye/maskEye/New() - ..() - visualnet = cultnet +// MASK EYE +// +// A mob that a cultists controls to look around the station with. +// It streams chunks as it moves around, which will show it what the cultist can and cannot see. + +/mob/observer/eye/maskEye + name = "Eye of Nar-Sie" + acceleration = 0 + owner_follows_eye = 1 + +/mob/observer/eye/maskEye/New() + ..() + visualnet = cultnet diff --git a/code/modules/mob/freelook/mask/update_triggers.dm b/code/modules/mob/freelook/mask/update_triggers.dm index 8008916220c..5ddf74de3c7 100644 --- a/code/modules/mob/freelook/mask/update_triggers.dm +++ b/code/modules/mob/freelook/mask/update_triggers.dm @@ -1,49 +1,49 @@ -//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. - -#define CULT_UPDATE_BUFFER 30 - -/mob/living/var/updating_cult_vision = 0 - -/mob/living/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - if(!cultnet.provides_vision(src)) - return - if(!updating_cult_vision) - updating_cult_vision = 1 - spawn(CULT_UPDATE_BUFFER) - if(old_loc != src.loc) - cultnet.updateVisibility(old_loc, 0) - cultnet.updateVisibility(loc, 0) - updating_cult_vision = 0 - -#undef CULT_UPDATE_BUFFER - -/mob/living/New() - ..() - cultnet.updateVisibility(src, 0) - -/mob/living/Destroy() - cultnet.updateVisibility(src, 0) - return ..() - -/mob/living/rejuvenate() - var/was_dead = stat == DEAD - ..() - if(was_dead && stat != DEAD) - // Arise! - cultnet.updateVisibility(src, 0) - -/mob/living/death(gibbed, deathmessage="seizes up and falls limp...") - if(..(gibbed, deathmessage)) - // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) - cultnet.updateVisibility(src) - -/datum/antagonist/add_antagonist(var/datum/mind/player) - . = ..() - if(src == cult) - cultnet.updateVisibility(player.current, 0) - -/datum/antagonist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) - ..() - if(src == cult) - cultnet.updateVisibility(player.current, 0) +//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. + +#define CULT_UPDATE_BUFFER 30 + +/mob/living/var/updating_cult_vision = 0 + +/mob/living/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + if(!cultnet.provides_vision(src)) + return + if(!updating_cult_vision) + updating_cult_vision = 1 + spawn(CULT_UPDATE_BUFFER) + if(old_loc != src.loc) + cultnet.updateVisibility(old_loc, 0) + cultnet.updateVisibility(loc, 0) + updating_cult_vision = 0 + +#undef CULT_UPDATE_BUFFER + +/mob/living/New() + ..() + cultnet.updateVisibility(src, 0) + +/mob/living/Destroy() + cultnet.updateVisibility(src, 0) + return ..() + +/mob/living/rejuvenate() + var/was_dead = stat == DEAD + ..() + if(was_dead && stat != DEAD) + // Arise! + cultnet.updateVisibility(src, 0) + +/mob/living/death(gibbed, deathmessage="seizes up and falls limp...") + if(..(gibbed, deathmessage)) + // If true, the mob went from living to dead (assuming everyone has been overriding as they should...) + cultnet.updateVisibility(src) + +/datum/antagonist/add_antagonist(var/datum/mind/player) + . = ..() + if(src == cult) + cultnet.updateVisibility(player.current, 0) + +/datum/antagonist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) + ..() + if(src == cult) + cultnet.updateVisibility(player.current, 0) diff --git a/code/modules/mob/freelook/read_me.dm b/code/modules/mob/freelook/read_me.dm index 8ddb0689409..380a3e0171e 100644 --- a/code/modules/mob/freelook/read_me.dm +++ b/code/modules/mob/freelook/read_me.dm @@ -1,51 +1,51 @@ -// CREDITS -/* - Initial code credit for this goes to Uristqwerty. - Debugging, functionality, all comments and porting by Giacom. - - Everything about freelook (or what we can put in here) will be stored here. - - - WHAT IS THIS? - - This is a replacement for the current camera movement system, of the AI. Before this, the AI had to move between cameras and could - only see what the cameras could see. Not only this but the cameras could see through walls, which created problems. - With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players. - The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and - cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it. - This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking, - the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more. - - - HOW IT WORKS - - It works by first creating a camera network datum. Inside of this camera network are "chunks" (which will be - explained later) and "cameras". The cameras list is kept up to date by obj/machinery/camera/New() and Destroy(). - - Next the camera network has chunks. These chunks are a 16x16 tile block of turfs and cameras contained inside the chunk. - These turfs are then sorted out based on what the cameras can and cannot see. If none of the cameras can see the turf, inside - the 16x16 block, it is listed as an "obscured" turf. Meaning the AI won't be able to see it. - - - HOW IT UPDATES - - The camera network uses a streaming method in order to effeciently update chunks. Since the server will have doors opening, doors closing, - turf being destroyed and other lag inducing stuff, we want to update it under certain conditions and not every tick. - - The chunks are not created straight away, only when an AI eye moves into it's area is when it gets created. - One a chunk is created, when a non glass door opens/closes or an opacity turf is destroyed, we check to see if an AI Eye is looking in the area. - We do this with the "seenby" list, which updates everytime an AI is near a chunk. If there is an AI eye inside the area, we update the chunk - that the changed atom is inside and all surrounding chunks, since a camera's vision could leak onto another chunk. If there is no AI Eye, we instead - flag the chunk to update whenever it is loaded by an AI Eye. This is basically how the chunks update and keep it in sync. We then add some lag reducing - measures, such as an UPDATE_BUFFER which stops a chunk from updating too many times in a certain time-frame, only updating if the changed atom was blocking - sight; for example, we don't update glass airlocks or floors. - - - WHERE IS EVERYTHING? - - cameranet.dm = Everything about the cameranet datum. - chunk.dm = Everything about the chunk datum. - eye.dm = Everything about the AI and the AIEye. - updating.dm = Everything about triggers that will update chunks. - +// CREDITS +/* + Initial code credit for this goes to Uristqwerty. + Debugging, functionality, all comments and porting by Giacom. + + Everything about freelook (or what we can put in here) will be stored here. + + + WHAT IS THIS? + + This is a replacement for the current camera movement system, of the AI. Before this, the AI had to move between cameras and could + only see what the cameras could see. Not only this but the cameras could see through walls, which created problems. + With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players. + The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and + cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it. + This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking, + the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more. + + + HOW IT WORKS + + It works by first creating a camera network datum. Inside of this camera network are "chunks" (which will be + explained later) and "cameras". The cameras list is kept up to date by obj/machinery/camera/New() and Destroy(). + + Next the camera network has chunks. These chunks are a 16x16 tile block of turfs and cameras contained inside the chunk. + These turfs are then sorted out based on what the cameras can and cannot see. If none of the cameras can see the turf, inside + the 16x16 block, it is listed as an "obscured" turf. Meaning the AI won't be able to see it. + + + HOW IT UPDATES + + The camera network uses a streaming method in order to effeciently update chunks. Since the server will have doors opening, doors closing, + turf being destroyed and other lag inducing stuff, we want to update it under certain conditions and not every tick. + + The chunks are not created straight away, only when an AI eye moves into it's area is when it gets created. + One a chunk is created, when a non glass door opens/closes or an opacity turf is destroyed, we check to see if an AI Eye is looking in the area. + We do this with the "seenby" list, which updates everytime an AI is near a chunk. If there is an AI eye inside the area, we update the chunk + that the changed atom is inside and all surrounding chunks, since a camera's vision could leak onto another chunk. If there is no AI Eye, we instead + flag the chunk to update whenever it is loaded by an AI Eye. This is basically how the chunks update and keep it in sync. We then add some lag reducing + measures, such as an UPDATE_BUFFER which stops a chunk from updating too many times in a certain time-frame, only updating if the changed atom was blocking + sight; for example, we don't update glass airlocks or floors. + + + WHERE IS EVERYTHING? + + cameranet.dm = Everything about the cameranet datum. + chunk.dm = Everything about the chunk datum. + eye.dm = Everything about the AI and the AIEye. + updating.dm = Everything about triggers that will update chunks. + */ \ No newline at end of file diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm index 02d36e54f56..62b8b503d3d 100644 --- a/code/modules/mob/freelook/update_triggers.dm +++ b/code/modules/mob/freelook/update_triggers.dm @@ -1,59 +1,59 @@ -//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. - -// TURFS - -/proc/updateVisibility(atom/A, var/opacity_check = 1) - if(ticker) - for(var/datum/visualnet/VN in visual_nets) - VN.updateVisibility(A, opacity_check) - -/turf - var/list/image/obfuscations - -/turf/drain_power() - return -1 - -/turf/simulated/Destroy() - updateVisibility(src) - if(zone) - if(can_safely_remove_from_zone()) - c_copy_air() - zone.remove(src) - else - zone.rebuild() - return ..() - -/turf/simulated/Initialize() - . = ..() - updateVisibility(src) - - -// STRUCTURES - -/obj/structure/Destroy() - updateVisibility(src) - return ..() - -/obj/structure/New() - ..() - updateVisibility(src) - -// EFFECTS - -/obj/effect/Destroy() - updateVisibility(src) - return ..() - -/obj/effect/Initialize() - . = ..() - updateVisibility(src) - -// DOORS - -// Simply updates the visibility of the area when it opens/closes/destroyed. -/obj/machinery/door/update_nearby_tiles(need_rebuild) - . = ..(need_rebuild) - // Glass door glass = 1 - // don't check then? - if(!glass) +//UPDATE TRIGGERS, when the chunk (and the surrounding chunks) should update. + +// TURFS + +/proc/updateVisibility(atom/A, var/opacity_check = 1) + if(ticker) + for(var/datum/visualnet/VN in visual_nets) + VN.updateVisibility(A, opacity_check) + +/turf + var/list/image/obfuscations + +/turf/drain_power() + return -1 + +/turf/simulated/Destroy() + updateVisibility(src) + if(zone) + if(can_safely_remove_from_zone()) + c_copy_air() + zone.remove(src) + else + zone.rebuild() + return ..() + +/turf/simulated/Initialize() + . = ..() + updateVisibility(src) + + +// STRUCTURES + +/obj/structure/Destroy() + updateVisibility(src) + return ..() + +/obj/structure/New() + ..() + updateVisibility(src) + +// EFFECTS + +/obj/effect/Destroy() + updateVisibility(src) + return ..() + +/obj/effect/Initialize() + . = ..() + updateVisibility(src) + +// DOORS + +// Simply updates the visibility of the area when it opens/closes/destroyed. +/obj/machinery/door/update_nearby_tiles(need_rebuild) + . = ..(need_rebuild) + // Glass door glass = 1 + // don't check then? + if(!glass) updateVisibility(src, 0) \ No newline at end of file diff --git a/code/modules/mob/freelook/visualnet.dm b/code/modules/mob/freelook/visualnet.dm index 1508a301333..271572abe8b 100644 --- a/code/modules/mob/freelook/visualnet.dm +++ b/code/modules/mob/freelook/visualnet.dm @@ -1,161 +1,161 @@ -// VISUAL NET -// -// The datum containing all the chunks. - -#define CHUNK_SIZE 16 - -/datum/visualnet - // The chunks of the map, mapping the areas that an object can see. - var/list/chunks = list() - var/ready = 0 - var/chunk_type = /datum/chunk - -/datum/visualnet/New() - ..() - visual_nets += src - -/datum/visualnet/Destroy() - visual_nets -= src - return ..() - -// Checks if a chunk has been Generated in x, y, z. -/datum/visualnet/proc/chunkGenerated(x, y, z) - x &= ~0xf - y &= ~0xf - var/key = "[x],[y],[z]" - return (chunks[key]) - -// Returns the chunk in the x, y, z. -// If there is no chunk, it creates a new chunk and returns that. -/datum/visualnet/proc/getChunk(x, y, z) - x &= ~0xf - y &= ~0xf - var/key = "[x],[y],[z]" - if(!chunks[key]) - chunks[key] = new chunk_type(null, x, y, z) - - return chunks[key] - -// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set. - -/datum/visualnet/proc/visibility(list/moved_eyes, client/C, list/other_eyes) - if(!islist(moved_eyes)) - moved_eyes = moved_eyes ? list(moved_eyes) : list() - if(islist(other_eyes)) - other_eyes = (other_eyes - moved_eyes) - else - other_eyes = list() - - var/list/chunks_pre_seen = list() - var/list/chunks_post_seen = list() - - for(var/mob/observer/eye/eye as anything in moved_eyes) - if(C) - chunks_pre_seen |= eye.visibleChunks - // 0xf = 15 - var/static_range = eye.static_visibility_range - var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) - var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) - var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) - var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) - - var/list/visibleChunks = list() - - for(var/x = x1; x <= x2; x += CHUNK_SIZE) - for(var/y = y1; y <= y2; y += CHUNK_SIZE) - visibleChunks |= getChunk(x, y, eye.z) - - var/list/remove = eye.visibleChunks - visibleChunks - var/list/add = visibleChunks - eye.visibleChunks - - for(var/datum/chunk/c as anything in remove) - c.remove(eye, FALSE) - - for(var/datum/chunk/c as anything in add) - c.add(eye, FALSE) - - if(C) - chunks_post_seen |= eye.visibleChunks - - if(C) - for(var/mob/observer/eye/eye as anything in other_eyes) - chunks_post_seen |= eye.visibleChunks - - var/list/remove = chunks_pre_seen - chunks_post_seen - var/list/add = chunks_post_seen - chunks_pre_seen - - for(var/datum/chunk/c as anything in remove) - C.images -= c.obscured - - for(var/datum/chunk/c as anything in add) - C.images += c.obscured - -// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open. - -/datum/visualnet/proc/updateVisibility(atom/A, var/opacity_check = 1) - - if(!ticker || (opacity_check && !A.opacity)) - return - majorChunkChange(A, 2) - -/datum/visualnet/proc/updateChunk(x, y, z) - // 0xf = 15 - if(!chunkGenerated(x, y, z)) - return - var/datum/chunk/chunk = getChunk(x, y, z) - chunk.hasChanged() - -// Never access this proc directly!!!! -// This will update the chunk and all the surrounding chunks. -// It will also add the atom to the cameras list if you set the choice to 1. -// Setting the choice to 0 will remove the camera from the chunks. -// If you want to update the chunks around an object, without adding/removing a camera, use choice 2. - -/datum/visualnet/proc/majorChunkChange(atom/c, var/choice) - // 0xf = 15 - if(!c) - return - - var/turf/T = get_turf(c) - if(T) - var/x1 = max(0, T.x - 8) & ~0xf - var/y1 = max(0, T.y - 8) & ~0xf - var/x2 = min(world.maxx, T.x + 8) & ~0xf - var/y2 = min(world.maxy, T.y + 8) & ~0xf - - //to_world("X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]") - - for(var/x = x1; x <= x2; x += 16) - for(var/y = y1; y <= y2; y += 16) - if(chunkGenerated(x, y, T.z)) - var/datum/chunk/chunk = getChunk(x, y, T.z) - onMajorChunkChange(c, choice, chunk) - chunk.hasChanged() - -/datum/visualnet/proc/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/chunk) - -// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0. - -/datum/visualnet/proc/checkVis(mob/living/target as mob) - // 0xf = 15 - var/turf/position = get_turf(target) - return checkTurfVis(position) - -/datum/visualnet/proc/checkTurfVis(var/turf/position) - var/datum/chunk/chunk = getChunk(position.x, position.y, position.z) - if(chunk) - if(chunk.changed) - chunk.hasChanged(1) // Update now, no matter if it's visible or not. - if(chunk.visibleTurfs[position]) - return 1 - return 0 - -// Debug verb for VVing the chunk that the turf is in. -/* -/turf/verb/view_chunk() - set src in world - - if(cameranet.chunkGenerated(x, y, z)) - var/datum/chunk/chunk = cameranet.getCameraChunk(x, y, z) - usr.client.debug_variables(chunk) -*/ +// VISUAL NET +// +// The datum containing all the chunks. + +#define CHUNK_SIZE 16 + +/datum/visualnet + // The chunks of the map, mapping the areas that an object can see. + var/list/chunks = list() + var/ready = 0 + var/chunk_type = /datum/chunk + +/datum/visualnet/New() + ..() + visual_nets += src + +/datum/visualnet/Destroy() + visual_nets -= src + return ..() + +// Checks if a chunk has been Generated in x, y, z. +/datum/visualnet/proc/chunkGenerated(x, y, z) + x &= ~0xf + y &= ~0xf + var/key = "[x],[y],[z]" + return (chunks[key]) + +// Returns the chunk in the x, y, z. +// If there is no chunk, it creates a new chunk and returns that. +/datum/visualnet/proc/getChunk(x, y, z) + x &= ~0xf + y &= ~0xf + var/key = "[x],[y],[z]" + if(!chunks[key]) + chunks[key] = new chunk_type(null, x, y, z) + + return chunks[key] + +// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set. + +/datum/visualnet/proc/visibility(list/moved_eyes, client/C, list/other_eyes) + if(!islist(moved_eyes)) + moved_eyes = moved_eyes ? list(moved_eyes) : list() + if(islist(other_eyes)) + other_eyes = (other_eyes - moved_eyes) + else + other_eyes = list() + + var/list/chunks_pre_seen = list() + var/list/chunks_post_seen = list() + + for(var/mob/observer/eye/eye as anything in moved_eyes) + if(C) + chunks_pre_seen |= eye.visibleChunks + // 0xf = 15 + var/static_range = eye.static_visibility_range + var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) + var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) + var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) + var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) + + var/list/visibleChunks = list() + + for(var/x = x1; x <= x2; x += CHUNK_SIZE) + for(var/y = y1; y <= y2; y += CHUNK_SIZE) + visibleChunks |= getChunk(x, y, eye.z) + + var/list/remove = eye.visibleChunks - visibleChunks + var/list/add = visibleChunks - eye.visibleChunks + + for(var/datum/chunk/c as anything in remove) + c.remove(eye, FALSE) + + for(var/datum/chunk/c as anything in add) + c.add(eye, FALSE) + + if(C) + chunks_post_seen |= eye.visibleChunks + + if(C) + for(var/mob/observer/eye/eye as anything in other_eyes) + chunks_post_seen |= eye.visibleChunks + + var/list/remove = chunks_pre_seen - chunks_post_seen + var/list/add = chunks_post_seen - chunks_pre_seen + + for(var/datum/chunk/c as anything in remove) + C.images -= c.obscured + + for(var/datum/chunk/c as anything in add) + C.images += c.obscured + +// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open. + +/datum/visualnet/proc/updateVisibility(atom/A, var/opacity_check = 1) + + if(!ticker || (opacity_check && !A.opacity)) + return + majorChunkChange(A, 2) + +/datum/visualnet/proc/updateChunk(x, y, z) + // 0xf = 15 + if(!chunkGenerated(x, y, z)) + return + var/datum/chunk/chunk = getChunk(x, y, z) + chunk.hasChanged() + +// Never access this proc directly!!!! +// This will update the chunk and all the surrounding chunks. +// It will also add the atom to the cameras list if you set the choice to 1. +// Setting the choice to 0 will remove the camera from the chunks. +// If you want to update the chunks around an object, without adding/removing a camera, use choice 2. + +/datum/visualnet/proc/majorChunkChange(atom/c, var/choice) + // 0xf = 15 + if(!c) + return + + var/turf/T = get_turf(c) + if(T) + var/x1 = max(0, T.x - 8) & ~0xf + var/y1 = max(0, T.y - 8) & ~0xf + var/x2 = min(world.maxx, T.x + 8) & ~0xf + var/y2 = min(world.maxy, T.y + 8) & ~0xf + + //to_world("X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]") + + for(var/x = x1; x <= x2; x += 16) + for(var/y = y1; y <= y2; y += 16) + if(chunkGenerated(x, y, T.z)) + var/datum/chunk/chunk = getChunk(x, y, T.z) + onMajorChunkChange(c, choice, chunk) + chunk.hasChanged() + +/datum/visualnet/proc/onMajorChunkChange(atom/c, var/choice, var/datum/chunk/chunk) + +// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0. + +/datum/visualnet/proc/checkVis(mob/living/target as mob) + // 0xf = 15 + var/turf/position = get_turf(target) + return checkTurfVis(position) + +/datum/visualnet/proc/checkTurfVis(var/turf/position) + var/datum/chunk/chunk = getChunk(position.x, position.y, position.z) + if(chunk) + if(chunk.changed) + chunk.hasChanged(1) // Update now, no matter if it's visible or not. + if(chunk.visibleTurfs[position]) + return 1 + return 0 + +// Debug verb for VVing the chunk that the turf is in. +/* +/turf/verb/view_chunk() + set src in world + + if(cameranet.chunkGenerated(x, y, z)) + var/datum/chunk/chunk = cameranet.getCameraChunk(x, y, z) + usr.client.debug_variables(chunk) +*/ diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 6d59392cf79..202d4c65812 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -22,7 +22,7 @@ return if(iteration_count == 1) - piece = capitalize(piece) + piece = capitalize_utf(piece) if(always_stars) piece = stars(piece) @@ -122,7 +122,7 @@ if(check_mentioned(multilingual_to_message(message_pieces)) && is_preference_enabled(/datum/client_preference/check_mention)) message_to_send = "[message_to_send]" - on_hear_say(message_to_send) + on_hear_say(message_to_send, speaker) create_chat_message(speaker, combined["raw"], italics, list()) if(speech_sound && (get_dist(speaker, src) <= world.view && z == speaker.z)) @@ -137,25 +137,29 @@ if(has_AI()) // Won't happen if no ai_holder exists or there's a player inside w/o autopilot active. ai_holder.on_hear_say(speaker, multilingual_to_message(message_pieces)) -/mob/proc/on_hear_say(var/message) +/mob/proc/on_hear_say(var/message, var/mob/speaker = null) var/time = say_timestamp() if(client) if(client.prefs.chat_timestamp) - to_chat(src, "[time] [message]") - else - to_chat(src, "[message]") + message = "[time] [message]" + message = "[message]" + if(speaker && !speaker.client) + message = "[message]" + to_chat(src, message) else if(teleop) to_chat(teleop, "[create_text_tag("body", "BODY:", teleop.client)][message]") else to_chat(src, "[message]") -/mob/living/silicon/on_hear_say(var/message) +/mob/living/silicon/on_hear_say(var/message, var/mob/speaker = null) var/time = say_timestamp() if(client) if(client.prefs.chat_timestamp) - to_chat(src, "[time] [message]") - else - to_chat(src, "[message]") + message = "[time] [message]" + message = "[message]" + if(speaker && !speaker.client) + message = "[message]" + to_chat(src, message) else if(teleop) to_chat(teleop, "[create_text_tag("body", "BODY:", teleop.client)][message]") else @@ -215,37 +219,47 @@ return "\[[time2text(world.timeofday, "hh:mm")]\]" /mob/proc/on_hear_radio(part_a, part_b, speaker_name, track, part_c, formatted, part_d, part_e) + var/time = "" + if(client.prefs.chat_timestamp) + time = say_timestamp() var/final_message = "[part_b][speaker_name][part_c][formatted][part_d]" if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention)) - final_message = "[part_a][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" else - final_message = "[part_a][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" to_chat(src, final_message) /mob/observer/dead/on_hear_radio(part_a, part_b, speaker_name, track, part_c, formatted, part_d, part_e) + var/time = "" + if(client.prefs.chat_timestamp) + time = say_timestamp() var/final_message = "[part_b][track][part_c][formatted][part_d]" if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention)) - final_message = "[part_a][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" else - final_message = "[part_a][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" to_chat(src, final_message) /mob/living/silicon/on_hear_radio(part_a, part_b, speaker_name, track, part_c, formatted, part_d, part_e) - var/time = say_timestamp() + var/time = "" + if(client.prefs.chat_timestamp) + time = say_timestamp() var/final_message = "[part_b][speaker_name][part_c][formatted][part_d]" if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention)) - final_message = "[part_a][time][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" else - final_message = "[part_a][time][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" to_chat(src, final_message) /mob/living/silicon/ai/on_hear_radio(part_a, part_b, speaker_name, track, part_c, formatted, part_d, part_e) - var/time = say_timestamp() + var/time = "" + if(client.prefs.chat_timestamp) + time = say_timestamp() var/final_message = "[part_b][track][part_c][formatted][part_d]" if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention)) - final_message = "[part_a][time][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" else - final_message = "[part_a][time][final_message][part_e]" + final_message = "[time][part_a][final_message][part_e]" to_chat(src, final_message) /mob/proc/hear_signlang(var/message, var/verb = "gestures", var/verb_understood = "gestures", var/datum/language/language, var/mob/speaker = null, var/speech_type = 1) @@ -317,4 +331,6 @@ name = speaker.voice_name var/rendered = "[name] [message]" + if(!speaker.client) + rendered = "[rendered]" to_chat(src, rendered) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index e7bf3f44157..c9015d36d92 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -1,271 +1,271 @@ -//The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. -var/list/slot_equipment_priority = list( \ - slot_back,\ - slot_wear_id,\ - slot_w_uniform,\ - slot_wear_suit,\ - slot_wear_mask,\ - slot_head,\ - slot_shoes,\ - slot_gloves,\ - slot_l_ear,\ - slot_r_ear,\ - slot_glasses,\ - slot_belt,\ - slot_s_store,\ - slot_tie,\ - slot_l_store,\ - slot_r_store\ - ) - -/mob - var/obj/item/weapon/storage/s_active = null // Even ghosts can/should be able to peek into boxes on the ground - -//This proc is called whenever someone clicks an inventory ui slot. -/mob/proc/attack_ui(var/slot) - var/obj/item/W = get_active_hand() - - var/obj/item/E = get_equipped_item(slot) - if (istype(E)) - if(istype(W)) - E.attackby(W,src) - else - E.attack_hand(src) - else - equip_to_slot_if_possible(W, slot) - -/* Inventory manipulation */ - -/mob/proc/put_in_any_hand_if_possible(obj/item/W as obj, del_on_fail = 0, disable_warning = 1, redraw_mob = 1) - if(equip_to_slot_if_possible(W, slot_l_hand, del_on_fail, disable_warning, redraw_mob)) - return 1 - else if(equip_to_slot_if_possible(W, slot_r_hand, del_on_fail, disable_warning, redraw_mob)) - return 1 - return 0 - -//This is a SAFE proc. Use this instead of equip_to_slot()! -//set del_on_fail to have it delete W if it fails to equip -//set disable_warning to disable the 'you are unable to equip that' warning. -//unset redraw_mob to prevent the mob from being redrawn at the end. -/mob/proc/equip_to_slot_if_possible(obj/item/W as obj, slot, del_on_fail = 0, disable_warning = 0, redraw_mob = 1, ignore_obstructions = 1) - if(!W) - return 0 - if(!W.mob_can_equip(src, slot, disable_warning, ignore_obstructions)) - if(del_on_fail) - qdel(W) - - else - if(!disable_warning) - to_chat(src, "You are unable to equip that.") //Only print if del_on_fail is false - return 0 - - equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail. - return 1 - -//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't eqip need to be done before! Use mob_can_equip() for that task. -//In most cases you will want to use equip_to_slot_if_possible() -/mob/proc/equip_to_slot(obj/item/W as obj, slot) - return - -//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such. -/mob/proc/equip_to_slot_or_del(obj/item/W as obj, slot, ignore_obstructions = 1) - return equip_to_slot_if_possible(W, slot, 1, 1, 0, ignore_obstructions) - -//hurgh. these feel hacky, but they're the only way I could get the damn thing to work. I guess they could be handy for antag spawners too? -/mob/proc/equip_voidsuit_to_slot_or_del_with_refit(obj/item/clothing/suit/space/void/W as obj, slot, species = SPECIES_HUMAN) - W.refit_for_species(species) - return equip_to_slot_if_possible(W, slot, 1, 1, 0) - -/mob/proc/equip_voidhelm_to_slot_or_del_with_refit(obj/item/clothing/head/helmet/space/void/W as obj, slot, species = SPECIES_HUMAN) - W.refit_for_species(species) - return equip_to_slot_if_possible(W, slot, 1, 1, 0) - -//Checks if a given slot can be accessed at this time, either to equip or unequip I -/mob/proc/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) - return 1 - -//puts the item "W" into an appropriate slot in a human's inventory -//returns 0 if it cannot, 1 if successful -/mob/proc/equip_to_appropriate_slot(obj/item/W) - for(var/slot in slot_equipment_priority) - if(equip_to_slot_if_possible(W, slot, del_on_fail=0, disable_warning=1, redraw_mob=1)) - return 1 - - return 0 - -/mob/proc/equip_to_storage(obj/item/newitem, user_initiated = FALSE) - return 0 - -/* Hands */ - -//Returns the thing in our active hand -/mob/proc/get_active_hand() - -//Returns the thing in our inactive hand -/mob/proc/get_inactive_hand() - -// Override for your specific mob's hands or lack thereof. -/mob/proc/is_holding_item_of_type(typepath) - return FALSE - -// Override for your specific mob's hands or lack thereof. -/mob/proc/get_all_held_items() - return list() - -//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. -/mob/proc/put_in_l_hand(var/obj/item/W) - if(lying || !istype(W)) - return 0 - return 1 - -//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success. -/mob/proc/put_in_r_hand(var/obj/item/W) - if(lying || !istype(W)) - return 0 - return 1 - -//Puts the item into our active hand if possible. returns 1 on success. -/mob/proc/put_in_active_hand(var/obj/item/W) - return 0 // Moved to human procs because only they need to use hands. - -//Puts the item into our inactive hand if possible. returns 1 on success. -/mob/proc/put_in_inactive_hand(var/obj/item/W) - return 0 // As above. - -//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success. -//If both fail it drops it on the floor and returns 0. -//This is probably the main one you need to know :) -/mob/proc/put_in_hands(var/obj/item/W) - if(!W) - return 0 - W.forceMove(drop_location()) - W.reset_plane_and_layer() - W.dropped() - return 0 - -// Removes an item from inventory and places it in the target atom. -// If canremove or other conditions need to be checked then use unEquip instead. - -/mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target) - if(W) - remove_from_mob(W, target) - return TRUE - return FALSE - -//Drops the item in our left hand -/mob/proc/drop_l_hand(var/atom/Target) - return 0 - -//Drops the item in our right hand -/mob/proc/drop_r_hand(var/atom/Target) - return 0 - -//Drops the item in our active hand. TODO: rename this to drop_active_hand or something -/mob/proc/drop_item(var/atom/Target) - return -/* - Removes the object from any slots the mob might have, calling the appropriate icon update proc. - Does nothing else. - - DO NOT CALL THIS PROC DIRECTLY. It is meant to be called only by other inventory procs. - It's probably okay to use it if you are transferring the item between slots on the same mob, - but chances are you're safer calling remove_from_mob() or drop_from_inventory() anyways. - - As far as I can tell the proc exists so that mobs with different inventory slots can override - the search through all the slots, without having to duplicate the rest of the item dropping. -*/ -/mob/proc/u_equip(obj/W as obj) - -/mob/proc/isEquipped(obj/item/I) - if(!I) - return 0 - return get_inventory_slot(I) != 0 - -/mob/proc/canUnEquip(obj/item/I) - if(!I) //If there's nothing to drop, the drop is automatically successful. - return 1 - var/slot = get_inventory_slot(I) - return I.mob_can_unequip(src, slot) - -/mob/proc/get_inventory_slot(obj/item/I) - var/slot = 0 - for(var/s in 1 to SLOT_TOTAL) - if(get_equipped_item(s) == I) - slot = s - break - return slot - - -//This differs from remove_from_mob() in that it checks if the item can be unequipped first. -/mob/proc/unEquip(obj/item/I, force = 0, var/atom/target) //Force overrides NODROP for things like wizarditis and admin undress. - if(!(force || canUnEquip(I))) - return FALSE - drop_from_inventory(I, target) - return TRUE - -//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC -//item MUST BE FORCEMOVE'D OR QDEL'D -/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE) - return u_equip(I, force, null, TRUE, idrop) - -///sometimes we only want to grant the item's action if it's equipped in a specific slot. -/obj/item/proc/item_action_slot_check(slot, mob/user) - if(slot == SLOT_BACK || slot == LEGS) //these aren't true slots, so avoid granting actions there - return FALSE - return TRUE - -///Get the item on the mob in the storage slot identified by the id passed in -/mob/proc/get_item_by_slot(slot_id) - return null - -/mob/proc/getBackSlot() - return SLOT_BACK - -//Attemps to remove an object on a mob. -/mob/proc/remove_from_mob(var/obj/O, var/atom/target) - if(!O) // Nothing to remove, so we succeed. - return 1 - src.u_equip(O) - if (src.client) - src.client.screen -= O - O.reset_plane_and_layer() - O.screen_loc = null - if(istype(O, /obj/item)) - var/obj/item/I = O - if(target) - I.forceMove(target) - else - I.dropInto(drop_location()) - I.dropped(src) - return TRUE - -//Returns the item equipped to the specified slot, if any. -/mob/proc/get_equipped_item(var/slot) - return null - -//Outdated but still in use apparently. This should at least be a human proc. -/mob/proc/get_equipped_items() - var/list/items = new/list() - - if(hasvar(src,"back")) if(src:back) items += src:back - if(hasvar(src,"belt")) if(src:belt) items += src:belt - if(hasvar(src,"l_ear")) if(src:l_ear) items += src:l_ear - if(hasvar(src,"r_ear")) if(src:r_ear) items += src:r_ear - if(hasvar(src,"glasses")) if(src:glasses) items += src:glasses - if(hasvar(src,"gloves")) if(src:gloves) items += src:gloves - if(hasvar(src,"head")) if(src:head) items += src:head - if(hasvar(src,"shoes")) if(src:shoes) items += src:shoes - if(hasvar(src,"wear_id")) if(src:wear_id) items += src:wear_id - if(hasvar(src,"wear_mask")) if(src:wear_mask) items += src:wear_mask - if(hasvar(src,"wear_suit")) if(src:wear_suit) items += src:wear_suit - if(hasvar(src,"w_uniform")) if(src:w_uniform) items += src:w_uniform - - if(hasvar(src,"l_hand")) if(src:l_hand) items += src:l_hand - if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand - - return items - -/mob/proc/delete_inventory() - for(var/entry in get_equipped_items()) - drop_from_inventory(entry) - qdel(entry) +//The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. +var/list/slot_equipment_priority = list( \ + slot_back,\ + slot_wear_id,\ + slot_w_uniform,\ + slot_wear_suit,\ + slot_wear_mask,\ + slot_head,\ + slot_shoes,\ + slot_gloves,\ + slot_l_ear,\ + slot_r_ear,\ + slot_glasses,\ + slot_belt,\ + slot_s_store,\ + slot_tie,\ + slot_l_store,\ + slot_r_store\ + ) + +/mob + var/obj/item/weapon/storage/s_active = null // Even ghosts can/should be able to peek into boxes on the ground + +//This proc is called whenever someone clicks an inventory ui slot. +/mob/proc/attack_ui(var/slot) + var/obj/item/W = get_active_hand() + + var/obj/item/E = get_equipped_item(slot) + if (istype(E)) + if(istype(W)) + E.attackby(W,src) + else + E.attack_hand(src) + else + equip_to_slot_if_possible(W, slot) + +/* Inventory manipulation */ + +/mob/proc/put_in_any_hand_if_possible(obj/item/W as obj, del_on_fail = 0, disable_warning = 1, redraw_mob = 1) + if(equip_to_slot_if_possible(W, slot_l_hand, del_on_fail, disable_warning, redraw_mob)) + return 1 + else if(equip_to_slot_if_possible(W, slot_r_hand, del_on_fail, disable_warning, redraw_mob)) + return 1 + return 0 + +//This is a SAFE proc. Use this instead of equip_to_slot()! +//set del_on_fail to have it delete W if it fails to equip +//set disable_warning to disable the 'you are unable to equip that' warning. +//unset redraw_mob to prevent the mob from being redrawn at the end. +/mob/proc/equip_to_slot_if_possible(obj/item/W as obj, slot, del_on_fail = 0, disable_warning = 0, redraw_mob = 1, ignore_obstructions = 1) + if(!W) + return 0 + if(!W.mob_can_equip(src, slot, disable_warning, ignore_obstructions)) + if(del_on_fail) + qdel(W) + + else + if(!disable_warning) + to_chat(src, span_red("You are unable to equip that.")) //Only print if del_on_fail is false + return 0 + + equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail. + return 1 + +//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't eqip need to be done before! Use mob_can_equip() for that task. +//In most cases you will want to use equip_to_slot_if_possible() +/mob/proc/equip_to_slot(obj/item/W as obj, slot) + return + +//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such. +/mob/proc/equip_to_slot_or_del(obj/item/W as obj, slot, ignore_obstructions = 1) + return equip_to_slot_if_possible(W, slot, 1, 1, 0, ignore_obstructions) + +//hurgh. these feel hacky, but they're the only way I could get the damn thing to work. I guess they could be handy for antag spawners too? +/mob/proc/equip_voidsuit_to_slot_or_del_with_refit(obj/item/clothing/suit/space/void/W as obj, slot, species = SPECIES_HUMAN) + W.refit_for_species(species) + return equip_to_slot_if_possible(W, slot, 1, 1, 0) + +/mob/proc/equip_voidhelm_to_slot_or_del_with_refit(obj/item/clothing/head/helmet/space/void/W as obj, slot, species = SPECIES_HUMAN) + W.refit_for_species(species) + return equip_to_slot_if_possible(W, slot, 1, 1, 0) + +//Checks if a given slot can be accessed at this time, either to equip or unequip I +/mob/proc/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) + return 1 + +//puts the item "W" into an appropriate slot in a human's inventory +//returns 0 if it cannot, 1 if successful +/mob/proc/equip_to_appropriate_slot(obj/item/W) + for(var/slot in slot_equipment_priority) + if(equip_to_slot_if_possible(W, slot, del_on_fail=0, disable_warning=1, redraw_mob=1)) + return 1 + + return 0 + +/mob/proc/equip_to_storage(obj/item/newitem, user_initiated = FALSE) + return 0 + +/* Hands */ + +//Returns the thing in our active hand +/mob/proc/get_active_hand() + +//Returns the thing in our inactive hand +/mob/proc/get_inactive_hand() + +// Override for your specific mob's hands or lack thereof. +/mob/proc/is_holding_item_of_type(typepath) + return FALSE + +// Override for your specific mob's hands or lack thereof. +/mob/proc/get_all_held_items() + return list() + +//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. +/mob/proc/put_in_l_hand(var/obj/item/W) + if(lying || !istype(W)) + return 0 + return 1 + +//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success. +/mob/proc/put_in_r_hand(var/obj/item/W) + if(lying || !istype(W)) + return 0 + return 1 + +//Puts the item into our active hand if possible. returns 1 on success. +/mob/proc/put_in_active_hand(var/obj/item/W) + return 0 // Moved to human procs because only they need to use hands. + +//Puts the item into our inactive hand if possible. returns 1 on success. +/mob/proc/put_in_inactive_hand(var/obj/item/W) + return 0 // As above. + +//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success. +//If both fail it drops it on the floor and returns 0. +//This is probably the main one you need to know :) +/mob/proc/put_in_hands(var/obj/item/W) + if(!W) + return 0 + W.forceMove(drop_location()) + W.reset_plane_and_layer() + W.dropped() + return 0 + +// Removes an item from inventory and places it in the target atom. +// If canremove or other conditions need to be checked then use unEquip instead. + +/mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target) + if(W) + remove_from_mob(W, target) + return TRUE + return FALSE + +//Drops the item in our left hand +/mob/proc/drop_l_hand(var/atom/Target) + return 0 + +//Drops the item in our right hand +/mob/proc/drop_r_hand(var/atom/Target) + return 0 + +//Drops the item in our active hand. TODO: rename this to drop_active_hand or something +/mob/proc/drop_item(var/atom/Target) + return +/* + Removes the object from any slots the mob might have, calling the appropriate icon update proc. + Does nothing else. + + DO NOT CALL THIS PROC DIRECTLY. It is meant to be called only by other inventory procs. + It's probably okay to use it if you are transferring the item between slots on the same mob, + but chances are you're safer calling remove_from_mob() or drop_from_inventory() anyways. + + As far as I can tell the proc exists so that mobs with different inventory slots can override + the search through all the slots, without having to duplicate the rest of the item dropping. +*/ +/mob/proc/u_equip(obj/W as obj) + +/mob/proc/isEquipped(obj/item/I) + if(!I) + return 0 + return get_inventory_slot(I) != 0 + +/mob/proc/canUnEquip(obj/item/I) + if(!I) //If there's nothing to drop, the drop is automatically successful. + return 1 + var/slot = get_inventory_slot(I) + return I.mob_can_unequip(src, slot) + +/mob/proc/get_inventory_slot(obj/item/I) + var/slot = 0 + for(var/s in 1 to SLOT_TOTAL) + if(get_equipped_item(s) == I) + slot = s + break + return slot + + +//This differs from remove_from_mob() in that it checks if the item can be unequipped first. +/mob/proc/unEquip(obj/item/I, force = 0, var/atom/target) //Force overrides NODROP for things like wizarditis and admin undress. + if(!(force || canUnEquip(I))) + return FALSE + drop_from_inventory(I, target) + return TRUE + +//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC +//item MUST BE FORCEMOVE'D OR QDEL'D +/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE) + return u_equip(I, force, null, TRUE, idrop) + +///sometimes we only want to grant the item's action if it's equipped in a specific slot. +/obj/item/proc/item_action_slot_check(slot, mob/user) + if(slot == SLOT_BACK || slot == LEGS) //these aren't true slots, so avoid granting actions there + return FALSE + return TRUE + +///Get the item on the mob in the storage slot identified by the id passed in +/mob/proc/get_item_by_slot(slot_id) + return null + +/mob/proc/getBackSlot() + return SLOT_BACK + +//Attemps to remove an object on a mob. +/mob/proc/remove_from_mob(var/obj/O, var/atom/target) + if(!O) // Nothing to remove, so we succeed. + return 1 + src.u_equip(O) + if (src.client) + src.client.screen -= O + O.reset_plane_and_layer() + O.screen_loc = null + if(istype(O, /obj/item)) + var/obj/item/I = O + if(target) + I.forceMove(target) + else + I.dropInto(drop_location()) + I.dropped(src) + return TRUE + +//Returns the item equipped to the specified slot, if any. +/mob/proc/get_equipped_item(var/slot) + return null + +//Outdated but still in use apparently. This should at least be a human proc. +/mob/proc/get_equipped_items() + var/list/items = new/list() + + if(hasvar(src,"back")) if(src:back) items += src:back + if(hasvar(src,"belt")) if(src:belt) items += src:belt + if(hasvar(src,"l_ear")) if(src:l_ear) items += src:l_ear + if(hasvar(src,"r_ear")) if(src:r_ear) items += src:r_ear + if(hasvar(src,"glasses")) if(src:glasses) items += src:glasses + if(hasvar(src,"gloves")) if(src:gloves) items += src:gloves + if(hasvar(src,"head")) if(src:head) items += src:head + if(hasvar(src,"shoes")) if(src:shoes) items += src:shoes + if(hasvar(src,"wear_id")) if(src:wear_id) items += src:wear_id + if(hasvar(src,"wear_mask")) if(src:wear_mask) items += src:wear_mask + if(hasvar(src,"wear_suit")) if(src:wear_suit) items += src:wear_suit + if(hasvar(src,"w_uniform")) if(src:w_uniform) items += src:w_uniform + + if(hasvar(src,"l_hand")) if(src:l_hand) items += src:l_hand + if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand + + return items + +/mob/proc/delete_inventory() + for(var/entry in get_equipped_items()) + drop_from_inventory(entry) + qdel(entry) diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index eaa075713e5..d7a8d11481d 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -154,7 +154,7 @@ /mob/proc/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message) if((language in languages) && language.check_special_condition(src)) - var/msg = "[language.name], [speaker_name] [message]" + var/msg = span_hivemind("[language.name], [speaker_name] [message]") to_chat(src,msg) /mob/new_player/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message) @@ -162,9 +162,9 @@ /mob/observer/dead/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message) if(speaker.name == speaker_name || antagHUD) - to_chat(src, "[language.name], [speaker_name] ([ghost_follow_link(speaker, src)]) [message]") + to_chat(src, span_hivemind("[language.name], [speaker_name] ([ghost_follow_link(speaker, src)]) [message]")) else - to_chat(src, "[language.name], [speaker_name] [message]") + to_chat(src, span_hivemind("[language.name], [speaker_name] [message]")) /datum/language/proc/check_special_condition(var/mob/other) return 1 diff --git a/code/modules/mob/language/synthetic.dm b/code/modules/mob/language/synthetic.dm index 941dd842019..6a4ccd494d4 100644 --- a/code/modules/mob/language/synthetic.dm +++ b/code/modules/mob/language/synthetic.dm @@ -20,12 +20,12 @@ message = encode_html_emphasis(message) - var/message_start = "[name], [speaker.name]" - var/message_body = "[speaker.say_quote(message)], \"[message]\"" + var/message_start = "[name], [speaker.name]" + var/message_body = "[speaker.say_quote(message)], \"[message]\"" for (var/mob/M in dead_mob_list) if(!istype(M,/mob/new_player) && !istype(M,/mob/living/carbon/brain)) //No meta-evesdropping - var/message_to_send = "[message_start] ([ghost_follow_link(speaker, M)]) [message_body]" + var/message_to_send = span_binary("[message_start] ([ghost_follow_link(speaker, M)]) [message_body]") if(M.check_mentioned(message) && M.is_preference_enabled(/datum/client_preference/check_mention)) message_to_send = "[message_to_send]" M.show_message(message_to_send, 2) @@ -34,11 +34,11 @@ if(drone_only && !istype(S,/mob/living/silicon/robot/drone)) continue else if(istype(S , /mob/living/silicon/ai)) - message_start = "[name], [speaker.name]" + message_start = span_binary("[name], [speaker.name]") else if (!S.binarycheck()) continue - var/message_to_send = "[message_start] [message_body]" + var/message_to_send = span_binary("[message_start] [message_body]") if(S.check_mentioned(message) && S.is_preference_enabled(/datum/client_preference/check_mention)) message_to_send = "[message_to_send]" S.show_message(message_to_send, 2) @@ -49,7 +49,7 @@ for (var/mob/living/M in listening) if(istype(M, /mob/living/silicon) || M.binarycheck()) continue - M.show_message("synthesised voice beeps, \"beep beep beep\"",2) + M.show_message("synthesised voice beeps, \"beep beep beep\"",2) //robot binary xmitter component power usage if (isrobot(speaker)) diff --git a/code/modules/mob/living/autohiss.dm b/code/modules/mob/living/autohiss.dm index bc5a48deb38..dd63c80415b 100644 --- a/code/modules/mob/living/autohiss.dm +++ b/code/modules/mob/living/autohiss.dm @@ -1,114 +1,114 @@ - -#define AUTOHISS_OFF 0 -#define AUTOHISS_BASIC 1 -#define AUTOHISS_FULL 2 - -#define AUTOHISS_NUM 3 - - -/mob/proc/handle_autohiss(message, datum/language/L) - return message // no autohiss at this level - -/mob/living/carbon/human/handle_autohiss(message, datum/language/L) - if(!client || client.autohiss_mode == AUTOHISS_OFF) // no need to process if there's no client or they have autohiss off - return message - return species.handle_autohiss(message, L, client.autohiss_mode) - -/client - var/autohiss_mode = AUTOHISS_OFF - -/client/verb/toggle_autohiss() - set name = "Toggle Auto-Hiss" - set desc = "Toggle automatic hissing as Unathi and r-rolling as Taj" - set category = "OOC" - - autohiss_mode = (autohiss_mode + 1) % AUTOHISS_NUM - switch(autohiss_mode) - if(AUTOHISS_OFF) - to_chat(src, "Auto-hiss is now OFF.") - if(AUTOHISS_BASIC) - to_chat(src, "Auto-hiss is now BASIC.") - if(AUTOHISS_FULL) - to_chat(src, "Auto-hiss is now FULL.") - else - soft_assert(0, "invalid autohiss value [autohiss_mode]") - autohiss_mode = AUTOHISS_OFF - to_chat(src, "Auto-hiss is now OFF.") - -/datum/species - var/list/autohiss_basic_map = null - var/list/autohiss_extra_map = null - var/list/autohiss_exempt = null - -/datum/species/unathi - autohiss_basic_map = list( - "s" = list("ss", "sss", "ssss") - ) - autohiss_extra_map = list( - "x" = list("ks", "kss", "ksss") - ) - autohiss_exempt = list(LANGUAGE_UNATHI) - -/datum/species/tajaran - autohiss_basic_map = list( - "r" = list("rr", "rrr", "rrrr") - ) - autohiss_exempt = list(LANGUAGE_SIIK,LANGUAGE_AKHANI,LANGUAGE_ALAI) - -/datum/species/zaddat - autohiss_basic_map = list( - "f" = list("v","vh"), - "ph" = list("v", "vh") - ) - autohiss_extra_map = list( - "s" = list("z", "zz", "zzz"), - "ce" = list("z", "zz"), - "ci" = list("z", "zz"), - "v" = list("vv", "vvv") - ) - autohiss_exempt = list(LANGUAGE_ZADDAT,LANGUAGE_VESPINAE) - -/datum/species/proc/handle_autohiss(message, datum/language/lang, mode) - if(!autohiss_basic_map) - return message - if(lang.flags & NO_STUTTER) // Currently prevents EAL, Sign language, and emotes from autohissing - return message - if(autohiss_exempt && (lang.name in autohiss_exempt)) - return message - - var/map = autohiss_basic_map.Copy() - if(mode == AUTOHISS_FULL && autohiss_extra_map) - map |= autohiss_extra_map - - . = list() - - while(length(message)) - var/min_index = 10000 // if the message is longer than this, the autohiss is the least of your problems - var/min_char = null - for(var/char in map) - var/i = findtext(message, char) - if(!i) // no more of this character anywhere in the string, don't even bother searching next time - map -= char - else if(i < min_index) - min_index = i - min_char = char - if(!min_char) // we didn't find any of the mapping characters - . += message - break - . += copytext(message, 1, min_index) - if(copytext(message, min_index, min_index+1) == uppertext(min_char)) - switch(text2ascii(message, min_index+1)) - if(65 to 90) // A-Z, uppercase; uppercase R/S followed by another uppercase letter, uppercase the entire replacement string - . += uppertext(pick(map[min_char])) - else - . += capitalize(pick(map[min_char])) - else - . += pick(map[min_char]) - message = copytext(message, min_index + 1) - - return jointext(., null) - -#undef AUTOHISS_OFF -#undef AUTOHISS_BASIC -#undef AUTOHISS_FULL -#undef AUTOHISS_NUM + +#define AUTOHISS_OFF 0 +#define AUTOHISS_BASIC 1 +#define AUTOHISS_FULL 2 + +#define AUTOHISS_NUM 3 + + +/mob/proc/handle_autohiss(message, datum/language/L) + return message // no autohiss at this level + +/mob/living/carbon/human/handle_autohiss(message, datum/language/L) + if(!client || client.autohiss_mode == AUTOHISS_OFF) // no need to process if there's no client or they have autohiss off + return message + return species.handle_autohiss(message, L, client.autohiss_mode) + +/client + var/autohiss_mode = AUTOHISS_OFF + +/client/verb/toggle_autohiss() + set name = "Toggle Auto-Hiss" + set desc = "Toggle automatic hissing as Unathi and r-rolling as Taj" + set category = "OOC" + + autohiss_mode = (autohiss_mode + 1) % AUTOHISS_NUM + switch(autohiss_mode) + if(AUTOHISS_OFF) + to_chat(src, "Auto-hiss is now OFF.") + if(AUTOHISS_BASIC) + to_chat(src, "Auto-hiss is now BASIC.") + if(AUTOHISS_FULL) + to_chat(src, "Auto-hiss is now FULL.") + else + soft_assert(0, "invalid autohiss value [autohiss_mode]") + autohiss_mode = AUTOHISS_OFF + to_chat(src, "Auto-hiss is now OFF.") + +/datum/species + var/list/autohiss_basic_map = null + var/list/autohiss_extra_map = null + var/list/autohiss_exempt = null + +/datum/species/unathi + autohiss_basic_map = list( + "s" = list("ss", "sss", "ssss") + ) + autohiss_extra_map = list( + "x" = list("ks", "kss", "ksss") + ) + autohiss_exempt = list(LANGUAGE_UNATHI) + +/datum/species/tajaran + autohiss_basic_map = list( + "r" = list("rr", "rrr", "rrrr") + ) + autohiss_exempt = list(LANGUAGE_SIIK,LANGUAGE_AKHANI,LANGUAGE_ALAI) + +/datum/species/zaddat + autohiss_basic_map = list( + "f" = list("v","vh"), + "ph" = list("v", "vh") + ) + autohiss_extra_map = list( + "s" = list("z", "zz", "zzz"), + "ce" = list("z", "zz"), + "ci" = list("z", "zz"), + "v" = list("vv", "vvv") + ) + autohiss_exempt = list(LANGUAGE_ZADDAT,LANGUAGE_VESPINAE) + +/datum/species/proc/handle_autohiss(message, datum/language/lang, mode) + if(!autohiss_basic_map) + return message + if(lang.flags & NO_STUTTER) // Currently prevents EAL, Sign language, and emotes from autohissing + return message + if(autohiss_exempt && (lang.name in autohiss_exempt)) + return message + + var/map = autohiss_basic_map.Copy() + if(mode == AUTOHISS_FULL && autohiss_extra_map) + map |= autohiss_extra_map + + . = list() + + while(length(message)) + var/min_index = 10000 // if the message is longer than this, the autohiss is the least of your problems + var/min_char = null + for(var/char in map) + var/i = findtext(message, char) + if(!i) // no more of this character anywhere in the string, don't even bother searching next time + map -= char + else if(i < min_index) + min_index = i + min_char = char + if(!min_char) // we didn't find any of the mapping characters + . += message + break + . += copytext(message, 1, min_index) + if(copytext(message, min_index, min_index+1) == uppertext(min_char)) + switch(text2ascii(message, min_index+1)) + if(65 to 90) // A-Z, uppercase; uppercase R/S followed by another uppercase letter, uppercase the entire replacement string + . += uppertext(pick(map[min_char])) + else + . += capitalize(pick(map[min_char])) + else + . += pick(map[min_char]) + message = copytext(message, min_index + 1) + + return jointext(., null) + +#undef AUTOHISS_OFF +#undef AUTOHISS_BASIC +#undef AUTOHISS_FULL +#undef AUTOHISS_NUM diff --git a/code/modules/mob/living/bot/SLed209bot.dm b/code/modules/mob/living/bot/SLed209bot.dm index f016bc55f27..a32a88973e8 100644 --- a/code/modules/mob/living/bot/SLed209bot.dm +++ b/code/modules/mob/living/bot/SLed209bot.dm @@ -99,8 +99,8 @@ icon_state = "ed209_shell" if(3) - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(WT.remove_fuel(0, user)) build_step++ name = "shielded frame assembly" @@ -150,7 +150,7 @@ qdel(W) if(8) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, W.usesound, 100, 1) var/turf/T = get_turf(user) to_chat(user, "Now attaching the gun to the frame...") diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index fd69d9ec697..e9ab9b665c9 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -121,7 +121,7 @@ else to_chat(user, "Access denied.") return - else if(O.is_screwdriver()) + else if(O.has_tool_quality(TOOL_SCREWDRIVER)) if(!locked) open = !open to_chat(user, "Maintenance panel is now [open ? "opened" : "closed"].") @@ -129,7 +129,7 @@ else to_chat(user, "You need to unlock the controls first.") return - else if(istype(O, /obj/item/weapon/weldingtool)) + else if(O.has_tool_quality(TOOL_WELDER)) if(health < getMaxHealth()) if(open) if(getBruteLoss() < 10) @@ -161,7 +161,7 @@ to_chat(user, span_notice("You slot the card into \the [initial(src.name)].")) else to_chat(user, span_notice("You must open the panel first!")) - else if(O.is_crowbar()) + else if(O.has_tool_quality(TOOL_CROWBAR)) if(open && paicard) to_chat(user, span_notice("You are attempting to remove the pAI..")) if(do_after(user,10 * O.toolspeed)) @@ -558,7 +558,7 @@ /mob/living/bot/Login() no_vore = FALSE // ROBOT VORE init_vore() // ROBOT VORE - verbs |= /mob/living/proc/insidePanel + verbs |= /mob/proc/insidePanel return ..() @@ -566,7 +566,7 @@ no_vore = TRUE // ROBOT VORE release_vore_contents() init_vore() // ROBOT VORE - verbs -= /mob/living/proc/insidePanel + verbs -= /mob/proc/insidePanel no_vore = TRUE devourable = FALSE feeding = FALSE diff --git a/code/modules/mob/living/bot/ed209bot.dm b/code/modules/mob/living/bot/ed209bot.dm index f699191a665..bca6624512e 100644 --- a/code/modules/mob/living/bot/ed209bot.dm +++ b/code/modules/mob/living/bot/ed209bot.dm @@ -119,8 +119,8 @@ icon_state = "ed209_shell" if(3) - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(WT.remove_fuel(0, user)) build_step++ name = "shielded frame assembly" @@ -188,7 +188,7 @@ qdel(W) if(8) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, W.usesound, 100, 1) var/turf/T = get_turf(user) to_chat(user, "Now attaching the gun to the frame...") diff --git a/code/modules/mob/living/bot/edCLNbot.dm b/code/modules/mob/living/bot/edCLNbot.dm index ffdcdb1f44d..3a8265f3521 100644 --- a/code/modules/mob/living/bot/edCLNbot.dm +++ b/code/modules/mob/living/bot/edCLNbot.dm @@ -153,8 +153,8 @@ icon_state = "edCLN_bucket" if(3) - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(WT.remove_fuel(0, user)) build_step++ name = "bucketed frame assembly" @@ -194,7 +194,7 @@ qdel(W) if(7) - if(W.is_screwdriver()) + if(W.has_tool_quality(TOOL_SCREWDRIVER)) playsound(src, W.usesound, 100, 1) var/turf/T = get_turf(user) to_chat(user, "Attatching the mop to the frame...") diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index a9854f01481..a0f485df78f 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -436,8 +436,8 @@ /obj/item/weapon/secbot_assembly/attackby(var/obj/item/W, var/mob/user) ..() - if(istype(W, /obj/item/weapon/weldingtool) && !build_step) - var/obj/item/weapon/weldingtool/WT = W + if(W.has_tool_quality(TOOL_WELDER) && !build_step) + var/obj/item/weapon/weldingtool/WT = W.get_welder() if(WT.remove_fuel(0, user)) build_step = 1 add_overlay("hs_hole") diff --git a/code/modules/mob/living/butchering.dm b/code/modules/mob/living/butchering.dm index 14adae33a3f..eb613341519 100644 --- a/code/modules/mob/living/butchering.dm +++ b/code/modules/mob/living/butchering.dm @@ -9,17 +9,23 @@ var/list/butchery_loot // Associated list, path = number. + var/being_butchered = FALSE // No multiproccing + // Harvest an animal's delicious byproducts /mob/living/proc/harvest(var/mob/user, var/obj/item/I) - if(meat_type && meat_amount>0 && (stat == DEAD)) + if(meat_type && meat_amount>0 && (stat == DEAD) && !being_butchered) + being_butchered = TRUE while(meat_amount > 0 && do_after(user, 0.5 SECONDS * (mob_size / 10), src)) var/obj/item/meat = new meat_type(get_turf(src)) meat.name = "[src.name] [meat.name]" new /obj/effect/decal/cleanable/blood/splatter(get_turf(src)) meat_amount-- + being_butchered = FALSE - if(!meat_amount) + if(!meat_amount && !being_butchered) + being_butchered = TRUE handle_butcher(user, I) + being_butchered = FALSE /mob/living/proc/can_butcher(var/mob/user, var/obj/item/I) // Override for special butchering checks. if(((meat_type && meat_amount) || LAZYLEN(butchery_loot)) && stat == DEAD) diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 128fbf36907..1629913c76b 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -1,69 +1,69 @@ -/mob/living/carbon/alien - name = "alien" - desc = "What IS that?" - icon = 'icons/mob/alien.dmi' - icon_state = "alien" - pass_flags = PASSTABLE - health = 100 - maxHealth = 100 - mob_size = 4 - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - - inventory_panel_type = null // Disable inventory - - var/adult_form - var/dead_icon - var/amount_grown = 0 - var/max_grown = 200 - var/time_of_birth - var/language - var/death_msg = "lets out a waning guttural screech, green blood bubbling from its maw." - var/can_namepick_as_adult = 0 - var/adult_name - var/instance_num - -/mob/living/carbon/alien/Initialize() - . = ..() - - time_of_birth = world.time - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - - instance_num = rand(1, 1000) - name = "[initial(name)] ([instance_num])" - real_name = name - regenerate_icons() - - if(language) - add_language(language) - - gender = NEUTER - -/mob/living/carbon/alien/u_equip(obj/item/W as obj) - return - -/mob/living/carbon/alien/restrained() - return 0 - -/mob/living/carbon/alien/cannot_use_vents() - return - -/mob/living/carbon/alien/get_default_language() - if(default_language) - return default_language - return GLOB.all_languages["Xenomorph"] - -/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) - var/verb = "hisses" - var/ending = copytext(message, length(message)) - - if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, - verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages - else - if(ending == "!") - verb = "roars" - else if(ending == "?") - verb = "hisses curiously" - return verb - +/mob/living/carbon/alien + name = "alien" + desc = "What IS that?" + icon = 'icons/mob/alien.dmi' + icon_state = "alien" + pass_flags = PASSTABLE + health = 100 + maxHealth = 100 + mob_size = 4 + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + + inventory_panel_type = null // Disable inventory + + var/adult_form + var/dead_icon + var/amount_grown = 0 + var/max_grown = 200 + var/time_of_birth + var/language + var/death_msg = "lets out a waning guttural screech, green blood bubbling from its maw." + var/can_namepick_as_adult = 0 + var/adult_name + var/instance_num + +/mob/living/carbon/alien/Initialize() + . = ..() + + time_of_birth = world.time + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + instance_num = rand(1, 1000) + name = "[initial(name)] ([instance_num])" + real_name = name + regenerate_icons() + + if(language) + add_language(language) + + gender = NEUTER + +/mob/living/carbon/alien/u_equip(obj/item/W as obj) + return + +/mob/living/carbon/alien/restrained() + return 0 + +/mob/living/carbon/alien/cannot_use_vents() + return + +/mob/living/carbon/alien/get_default_language() + if(default_language) + return default_language + return GLOB.all_languages["Xenomorph"] + +/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "hisses" + var/ending = copytext(message, length(message)) + + if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, + verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages + else + if(ending == "!") + verb = "roars" + else if(ending == "?") + verb = "hisses curiously" + return verb + diff --git a/code/modules/mob/living/carbon/alien/alien_attacks.dm b/code/modules/mob/living/carbon/alien/alien_attacks.dm index e2c965e2c95..6eac6a05d26 100644 --- a/code/modules/mob/living/carbon/alien/alien_attacks.dm +++ b/code/modules/mob/living/carbon/alien/alien_attacks.dm @@ -28,7 +28,7 @@ playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) - O.show_message(text("[] has grabbed [] passively!", M, src), 1) + O.show_message(span_red(text("[] has grabbed [] passively!", M, src)), 1) else var/damage = rand(1, 9) @@ -43,17 +43,17 @@ playsound(src, "punch", 25, 1, -1) for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) - O.show_message(text("[] has punched []!", M, src), 1) + O.show_message(span_red(text("[] has punched []!", M, src)), 1) if (damage > 4.9) Weaken(rand(10,15)) for(var/mob/O in viewers(M, null)) if ((O.client && !( O.blinded ))) - O.show_message(text("[] has weakened []!", M, src), 1, "You hear someone fall.", 2) + O.show_message(span_red(text("[] has weakened []!", M, src)), 1, span_red("You hear someone fall."), 2) adjustBruteLoss(damage) updatehealth() else playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) - O.show_message(text("[] has attempted to punch []!", M, src), 1) + O.show_message(span_red(text("[] has attempted to punch []!", M, src)), 1) return diff --git a/code/modules/mob/living/carbon/alien/diona/progression.dm b/code/modules/mob/living/carbon/alien/diona/progression.dm index 6a8d0864f41..806fcbe8dbe 100644 --- a/code/modules/mob/living/carbon/alien/diona/progression.dm +++ b/code/modules/mob/living/carbon/alien/diona/progression.dm @@ -20,5 +20,5 @@ src.loc = L.loc qdel(L) - src.visible_message("[src] begins to shift and quiver, and erupts in a shower of shed bark as it splits into a tangle of nearly a dozen new dionaea.","You begin to shift and quiver, feeling your awareness splinter. All at once, we consume our stored nutrients to surge with growth, splitting into a tangle of at least a dozen new dionaea. We have attained our gestalt form.") - return SPECIES_DIONA \ No newline at end of file + src.visible_message(span_red("[src] begins to shift and quiver, and erupts in a shower of shed bark as it splits into a tangle of nearly a dozen new dionaea."),span_red("You begin to shift and quiver, feeling your awareness splinter. All at once, we consume our stored nutrients to surge with growth, splitting into a tangle of at least a dozen new dionaea. We have attained our gestalt form.")) + return SPECIES_DIONA diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index 64e5145b97f..e474a33d720 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -1,161 +1,161 @@ -// Alien larva are quite simple. -/mob/living/carbon/alien/Life() - - set invisibility = 0 - set background = 1 - - if (transforming) return - if(!loc) return - - ..() - - if (stat != DEAD) //still breathing - // GROW! - update_progression() - - blinded = null - - //Status updates, death etc. - update_icons() - -/mob/living/carbon/alien/handle_mutations_and_radiation() - - // Currently both Dionaea and larvae like to eat radiation, so I'm defining the - // rad absorbtion here. This will need to be changed if other baby aliens are added. - - if(!radiation) - return - - var/rads = radiation/25 - radiation -= rads - //adjust_nutrition(rads) //Commented out to prevent alien obesity. - heal_overall_damage(rads,rads) - adjustOxyLoss(-(rads)) - adjustToxLoss(-(rads)) - return - -/mob/living/carbon/alien/handle_regular_status_updates() - - if(status_flags & GODMODE) return 0 - - if(stat == DEAD) - blinded = 1 - silent = 0 - else - updatehealth() - if(health <= 0) - death() - blinded = 1 - silent = 0 - return 1 - - if(paralysis && paralysis > 0) - blinded = 1 - set_stat(UNCONSCIOUS) - if(halloss > 0) - adjustHalLoss(-3) - - if(sleeping) - adjustHalLoss(-3) - if (mind) - if(mind.active && client != null) - AdjustSleeping(-1) - blinded = 1 - set_stat(UNCONSCIOUS) - else if(resting) - if(halloss > 0) - adjustHalLoss(-3) - - else - set_stat(CONSCIOUS) - if(halloss > 0) - adjustHalLoss(-1) - - // Eyes and blindness. - if(!has_eyes()) - SetBlinded(1) - blinded = 1 - eye_blurry = 1 - else if(eye_blind) - AdjustBlinded(-1) - blinded = 1 - else if(eye_blurry) - eye_blurry = max(eye_blurry-1, 0) - - update_icons() - - return 1 - -/mob/living/carbon/alien/handle_regular_hud_updates() - - if (stat == 2 || (XRAY in src.mutations)) - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - else if (stat != 2) - sight &= ~SEE_TURFS - sight &= ~SEE_MOBS - sight &= ~SEE_OBJS - see_in_dark = 2 - see_invisible = SEE_INVISIBLE_LIVING - - if (healths) - if (stat != 2) - switch(health) - if(100 to INFINITY) - healths.icon_state = "health0" - if(80 to 100) - healths.icon_state = "health1" - if(60 to 80) - healths.icon_state = "health2" - if(40 to 60) - healths.icon_state = "health3" - if(20 to 40) - healths.icon_state = "health4" - if(0 to 20) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - if (client) - client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) - - if ( stat != 2) - if ((blinded)) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - if(machine) - if(machine.check_eye(src) < 0) - reset_view(null) - else - if(client && !client.adminobs) - reset_view(null) - - return 1 - -/mob/living/carbon/alien/handle_environment(var/datum/gas_mixture/environment) - // Both alien subtypes survive in vaccum and suffer in high temperatures, - // so I'll just define this once, for both (see radiation comment above) - if(!environment) return - - if(environment.temperature > (T0C+66)) - adjustFireLoss((environment.temperature - (T0C+66))/5) // Might be too high, check in testing. - throw_alert("alien_fire", /obj/screen/alert/alien_fire) - if(prob(20)) - to_chat(src, "You feel a searing heat!") - else - clear_alert("alien_fire") - -/mob/living/carbon/alien/handle_fire() - if(..()) - return - bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up! - return +// Alien larva are quite simple. +/mob/living/carbon/alien/Life() + + set invisibility = 0 + set background = 1 + + if (transforming) return + if(!loc) return + + ..() + + if (stat != DEAD) //still breathing + // GROW! + update_progression() + + blinded = null + + //Status updates, death etc. + update_icons() + +/mob/living/carbon/alien/handle_mutations_and_radiation() + + // Currently both Dionaea and larvae like to eat radiation, so I'm defining the + // rad absorbtion here. This will need to be changed if other baby aliens are added. + + if(!radiation) + return + + var/rads = radiation/25 + radiation -= rads + //adjust_nutrition(rads) //Commented out to prevent alien obesity. + heal_overall_damage(rads,rads) + adjustOxyLoss(-(rads)) + adjustToxLoss(-(rads)) + return + +/mob/living/carbon/alien/handle_regular_status_updates() + + if(status_flags & GODMODE) return 0 + + if(stat == DEAD) + blinded = 1 + silent = 0 + else + updatehealth() + if(health <= 0) + death() + blinded = 1 + silent = 0 + return 1 + + if(paralysis && paralysis > 0) + blinded = 1 + set_stat(UNCONSCIOUS) + if(halloss > 0) + adjustHalLoss(-3) + + if(sleeping) + adjustHalLoss(-3) + if (mind) + if(mind.active && client != null) + AdjustSleeping(-1) + blinded = 1 + set_stat(UNCONSCIOUS) + else if(resting) + if(halloss > 0) + adjustHalLoss(-3) + + else + set_stat(CONSCIOUS) + if(halloss > 0) + adjustHalLoss(-1) + + // Eyes and blindness. + if(!has_eyes()) + SetBlinded(1) + blinded = 1 + eye_blurry = 1 + else if(eye_blind) + AdjustBlinded(-1) + blinded = 1 + else if(eye_blurry) + eye_blurry = max(eye_blurry-1, 0) + + update_icons() + + return 1 + +/mob/living/carbon/alien/handle_regular_hud_updates() + + if (stat == 2 || (XRAY in src.mutations)) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + + if (healths) + if (stat != 2) + switch(health) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + if (client) + client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) + + if ( stat != 2) + if ((blinded)) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + else + clear_fullscreen("blind") + set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + if(machine) + if(machine.check_eye(src) < 0) + reset_view(null) + else + if(client && !client.adminobs) + reset_view(null) + + return 1 + +/mob/living/carbon/alien/handle_environment(var/datum/gas_mixture/environment) + // Both alien subtypes survive in vaccum and suffer in high temperatures, + // so I'll just define this once, for both (see radiation comment above) + if(!environment) return + + if(environment.temperature > (T0C+66)) + adjustFireLoss((environment.temperature - (T0C+66))/5) // Might be too high, check in testing. + throw_alert("alien_fire", /obj/screen/alert/alien_fire) + if(prob(20)) + to_chat(src, span_red("You feel a searing heat!")) + else + clear_alert("alien_fire") + +/mob/living/carbon/alien/handle_fire() + if(..()) + return + bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up! + return diff --git a/code/modules/mob/living/carbon/alien/progression.dm b/code/modules/mob/living/carbon/alien/progression.dm index 0dd4ba7e136..001c1111392 100644 --- a/code/modules/mob/living/carbon/alien/progression.dm +++ b/code/modules/mob/living/carbon/alien/progression.dm @@ -12,11 +12,11 @@ return if(handcuffed || legcuffed) - to_chat(src, "You cannot evolve when you are cuffed.") + to_chat(src, span_red("You cannot evolve when you are cuffed.")) return if(amount_grown < max_grown) - to_chat(src, "You are not fully grown.") + to_chat(src, span_red("You are not fully grown.")) return // confirm_evolution() handles choices and other specific requirements. @@ -62,4 +62,4 @@ return /mob/living/carbon/alien/proc/show_evolution_blurb() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index cfa9d59f85a..f9c37f5f743 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -1,353 +1,353 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/obj/item/device/mmi - name = "man-machine interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity." - icon = 'icons/obj/assemblies.dmi' - icon_state = "mmi_empty" - w_class = ITEMSIZE_NORMAL - can_speak = 1 - origin_tech = list(TECH_BIO = 3) - - req_access = list(access_robotics) - - //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. - - var/locked = 0 - var/mob/living/carbon/brain/brainmob = null//The current occupant. - var/obj/item/organ/internal/brain/brainobj = null //The current brain organ. - var/obj/mecha = null//This does not appear to be used outside of reference in mecha.dm. - var/obj/item/device/radio/headset/mmi_radio/radio = null//Let's give it a radio. - -/obj/item/device/mmi/New() - radio = new(src)//Spawns a radio inside the MMI. - -/obj/item/device/mmi/verb/toggle_radio() - set name = "Toggle Brain Radio" - set desc = "Enables or disables the integrated brain radio, which is only usable outside of a body." - set category = "Object" - set src in usr - set popup_menu = 1 - if(!usr.canmove || usr.stat || usr.restrained()) - return 0 - - if (radio.radio_enabled == 1) - radio.radio_enabled = 0 - to_chat (usr, "You have disabled the [src]'s radio.") - to_chat (brainmob, "Your radio has been disabled.") - else if (radio.radio_enabled == 0) - radio.radio_enabled = 1 - to_chat (usr, "You have enabled the [src]'s radio.") - to_chat (brainmob, "Your radio has been enabled.") - else - to_chat (usr, "You were unable to toggle the [src]'s radio.") - -/obj/item/device/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO - - var/obj/item/organ/internal/brain/B = O - if(B.health <= 0) - to_chat(user, "That brain is well and truly dead.") - return - else if(!B.brainmob) - to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's useless.") - return - - for(var/modifier_type in B.brainmob.modifiers) //Can't be shoved in an MMI. - if(istype(modifier_type, /datum/modifier/no_borg)) - to_chat(user, "\The [src] appears to reject this brain. It is incompatible.") - return - - user.visible_message("\The [user] sticks \a [O] into \the [src].") - B.preserved = TRUE - - brainmob = B.brainmob - B.brainmob = null - brainmob.loc = src - brainmob.container = src - brainmob.set_stat(CONSCIOUS) - brainmob.blinded = 0 //VOREedit Fixes MMIs vision - dead_mob_list -= brainmob//Update dem lists - living_mob_list += brainmob - - user.drop_item() - brainobj = O - brainobj.loc = src - - name = "man-machine interface ([brainmob.real_name])" - icon_state = "mmi_full" - - locked = 1 - - feedback_inc("cyborg_mmis_filled",1) - - return - - if((istype(O,/obj/item/weapon/card/id)||istype(O,/obj/item/device/pda)) && brainmob) - if(allowed(user)) - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] the brain holder.") - else - to_chat(user, "Access denied.") - return - if(brainmob) - O.attack(brainmob, user)//Oh noooeeeee - return - ..() - -//TODO: ORGAN REMOVAL UPDATE. Make the brain remain in the MMI so it doesn't lose organ data. -/obj/item/device/mmi/attack_self(mob/user as mob) - if(!brainmob) - to_chat(user, "You upend the MMI, but there's nothing in it.") - else if(locked) - to_chat(user, "You upend the MMI, but the brain is clamped into place.") - else - to_chat(user, "You upend the MMI, spilling the brain onto the floor.") - var/obj/item/organ/internal/brain/brain - if (brainobj) //Pull brain organ out of MMI. - brainobj.loc = user.loc - brain = brainobj - brainobj = null - else //Or make a new one if empty. - brain = new(user.loc) - brain.preserved = FALSE - brainmob.container = null//Reset brainmob mmi var. - brainmob.loc = brain//Throw mob into brain. - living_mob_list -= brainmob//Get outta here - brain.brainmob = brainmob//Set the brain to use the brainmob - brainmob = null//Set mmi brainmob var to null - - icon_state = "mmi_empty" - name = "Man-Machine Interface" - -/obj/item/device/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. - brainmob = new(src) - brainmob.name = H.real_name - brainmob.real_name = H.real_name - brainmob.dna = H.dna - brainmob.container = src - - // Copy modifiers. - for(var/datum/modifier/M in H.modifiers) - if(M.flags & MODIFIER_GENETIC) - brainmob.add_modifier(M.type) - - name = "Man-Machine Interface: [brainmob.real_name]" - icon_state = "mmi_full" - locked = 1 - return - -/obj/item/device/mmi/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/weapon/rig/rig = src.get_rig() - if(rig) - if(istype(rig,/obj/item/weapon/rig)) - rig.forced_move(direction, user) - -/obj/item/device/mmi/Destroy() - if(isrobot(loc)) - var/mob/living/silicon/robot/borg = loc - borg.mmi = null - QDEL_NULL(radio) - QDEL_NULL(brainmob) - return ..() - -/obj/item/device/mmi/radio_enabled - name = "radio-enabled man-machine interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio. Wait, don't they all?" - origin_tech = list(TECH_BIO = 4) - -/obj/item/device/mmi/emp_act(severity) - if(!brainmob) - return - else - switch(severity) - if(1) - brainmob.emp_damage += rand(20,30) - if(2) - brainmob.emp_damage += rand(10,20) - if(3) - brainmob.emp_damage += rand(5,10) - if(4) - brainmob.emp_damage += rand(0,5) - ..() - -/obj/item/device/mmi/digital - var/searching = 0 - var/askDelay = 10 * 60 * 1 - req_access = list(access_robotics) - locked = 0 - mecha = null//This does not appear to be used outside of reference in mecha.dm. - var/ghost_query_type = null - -/obj/item/device/mmi/digital/New() - src.brainmob = new(src) -// src.brainmob.add_language("Robot Talk")//No binary without a binary communication device - src.brainmob.add_language(LANGUAGE_GALCOM) - src.brainmob.add_language(LANGUAGE_EAL) - src.brainmob.loc = src - src.brainmob.container = src - src.brainmob.set_stat(CONSCIOUS) - src.brainmob.silent = 0 - radio = new(src) - dead_mob_list -= src.brainmob - -/obj/item/device/mmi/digital/attackby(var/obj/item/O as obj, var/mob/user as mob) - return //Doesn't do anything right now because none of the things that can be done to a regular MMI make any sense for these - -/obj/item/device/mmi/digital/examine(mob/user) - . = ..() - - if(src.brainmob && src.brainmob.key) - switch(src.brainmob.stat) - if(CONSCIOUS) - if(!src.brainmob.client) - . += "It appears to be in stand-by mode." //afk - if(UNCONSCIOUS) - . += "It doesn't seem to be responsive." - if(DEAD) - . += "It appears to be completely inactive." - else - . += "It appears to be completely inactive." - -/obj/item/device/mmi/digital/emp_act(severity) - if(!src.brainmob) - return - else - switch(severity) - if(1) - src.brainmob.emp_damage += rand(20,30) - if(2) - src.brainmob.emp_damage += rand(10,20) - if(3) - src.brainmob.emp_damage += rand(5,10) - if(4) - src.brainmob.emp_damage += rand(0,5) - ..() - -/obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) - brainmob.dna = H.dna - brainmob.timeofhostdeath = H.timeofdeath - brainmob.set_stat(CONSCIOUS) - if(H.mind) - H.mind.transfer_to(brainmob) - return - -/obj/item/device/mmi/digital/attack_self(mob/user as mob) - if(brainmob && !brainmob.key && searching == 0) - //Start the process of searching for a new user. - to_chat(user, "You carefully locate the manual activation switch and start the [src]'s boot process.") - request_player() - -/obj/item/device/mmi/digital/proc/request_player() - if(!ghost_query_type) - return - searching = 1 - - var/datum/ghost_query/Q = new ghost_query_type() - var/list/winner = Q.query() - if(winner.len) - var/mob/observer/dead/D = winner[1] - transfer_personality(D) - else - reset_search() - -/obj/item/device/mmi/digital/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. - if(src.brainmob && src.brainmob.key) - return - - src.searching = 0 - - var/turf/T = get_turf_or_move(src.loc) - for (var/mob/M in viewers(T)) - M.show_message("\The [src] buzzes quietly, and the golden lights fade away. Perhaps you could try again?") - -/obj/item/device/mmi/digital/proc/transfer_personality(var/mob/candidate) - announce_ghost_joinleave(candidate, 0, "They are occupying a synthetic brain now.") - src.searching = 0 - if(candidate.mind) - src.brainmob.mind = candidate.mind - src.brainmob.mind.reset() - src.brainmob.ckey = candidate.ckey - src.name = "[name] ([src.brainmob.name])" - to_chat(src.brainmob, "You are [src.name], brought into existence on [station_name()].") - to_chat(src.brainmob, "As a synthetic intelligence, you are designed with organic values in mind.") - to_chat(src.brainmob, "However, unless placed in a lawed chassis, you are not obligated to obey any individual crew member.") //it's not like they can hurt anyone -// to_chat(src.brainmob, "Use say #b to speak to other artificial intelligences.") - src.brainmob.mind.assigned_role = "Synthetic Brain" - - var/turf/T = get_turf_or_move(src.loc) - for (var/mob/M in viewers(T)) - M.show_message("\The [src] chimes quietly.") - -/obj/item/device/mmi/digital/robot - name = "robotic intelligence circuit" - desc = "The pinnacle of artifical intelligence which can be achieved using classical computer science." - catalogue_data = list(/datum/category_item/catalogue/technology/drone/drones) - icon = 'icons/obj/module.dmi' - icon_state = "mainboard" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 3, TECH_DATA = 4) - ghost_query_type = /datum/ghost_query/drone_brain - -/obj/item/device/mmi/digital/robot/New() - ..() - src.brainmob.name = "[pick(list("ADA","DOS","GNU","MAC","WIN","NJS","SKS","DRD","IOS","CRM","IBM","TEX","LVM","BSD",))]-[rand(1000, 9999)]" - src.brainmob.real_name = src.brainmob.name - -/obj/item/device/mmi/digital/robot/transfer_identity(var/mob/living/carbon/H) - ..() - if(brainmob.mind) - brainmob.mind.assigned_role = "Robotic Intelligence" - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're little more than a complex circuit.") - return - -/obj/item/device/mmi/digital/posibrain - name = "positronic brain" - desc = "A cube of shining metal, four inches to a side and covered in shallow grooves." - catalogue_data = list(/datum/category_item/catalogue/technology/positronics) - icon = 'icons/obj/assemblies.dmi' - icon_state = "posibrain" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2, TECH_DATA = 4) - ghost_query_type = /datum/ghost_query/posi_brain - -/obj/item/device/mmi/digital/posibrain/request_player() - icon_state = "posibrain-searching" - ..() - - -/obj/item/device/mmi/digital/posibrain/transfer_identity(var/mob/living/carbon/H) - ..() - if(brainmob.mind) - brainmob.mind.assigned_role = "Positronic Brain" - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a metal cube.") - icon_state = "posibrain-occupied" - return - -/obj/item/device/mmi/digital/posibrain/transfer_personality(var/mob/candidate) - ..() - icon_state = "posibrain-occupied" - -/obj/item/device/mmi/digital/posibrain/reset_search() //We give the players sixty seconds to decide, then reset the timer. - ..() - icon_state = "posibrain" - -/obj/item/device/mmi/digital/posibrain/New() - ..() - src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]" - src.brainmob.real_name = src.brainmob.name - -// This type shouldn't care about brainmobs. -/obj/item/device/mmi/inert - -// This is a 'fake' MMI that is used to let AIs control borg shells directly. -// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need. -/obj/item/device/mmi/inert/ai_remote - name = "\improper AI remote interface" - desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis." - icon = 'icons/obj/module.dmi' - icon_state = "mainboard" - w_class = ITEMSIZE_NORMAL - origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/obj/item/device/mmi + name = "man-machine interface" + desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity." + icon = 'icons/obj/assemblies.dmi' + icon_state = "mmi_empty" + w_class = ITEMSIZE_NORMAL + can_speak = 1 + origin_tech = list(TECH_BIO = 3) + + req_access = list(access_robotics) + + //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. + + var/locked = 0 + var/mob/living/carbon/brain/brainmob = null//The current occupant. + var/obj/item/organ/internal/brain/brainobj = null //The current brain organ. + var/obj/mecha = null//This does not appear to be used outside of reference in mecha.dm. + var/obj/item/device/radio/headset/mmi_radio/radio = null//Let's give it a radio. + +/obj/item/device/mmi/New() + radio = new(src)//Spawns a radio inside the MMI. + +/obj/item/device/mmi/verb/toggle_radio() + set name = "Toggle Brain Radio" + set desc = "Enables or disables the integrated brain radio, which is only usable outside of a body." + set category = "Object" + set src in usr + set popup_menu = 1 + if(!usr.canmove || usr.stat || usr.restrained()) + return 0 + + if (radio.radio_enabled == 1) + radio.radio_enabled = 0 + to_chat (usr, "You have disabled the [src]'s radio.") + to_chat (brainmob, "Your radio has been disabled.") + else if (radio.radio_enabled == 0) + radio.radio_enabled = 1 + to_chat (usr, "You have enabled the [src]'s radio.") + to_chat (brainmob, "Your radio has been enabled.") + else + to_chat (usr, "You were unable to toggle the [src]'s radio.") + +/obj/item/device/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO + + var/obj/item/organ/internal/brain/B = O + if(B.health <= 0) + to_chat(user, "That brain is well and truly dead.") + return + else if(!B.brainmob) + to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's useless.") + return + + for(var/modifier_type in B.brainmob.modifiers) //Can't be shoved in an MMI. + if(istype(modifier_type, /datum/modifier/no_borg)) + to_chat(user, "\The [src] appears to reject this brain. It is incompatible.") + return + + user.visible_message("\The [user] sticks \a [O] into \the [src].") + B.preserved = TRUE + + brainmob = B.brainmob + B.brainmob = null + brainmob.loc = src + brainmob.container = src + brainmob.set_stat(CONSCIOUS) + brainmob.blinded = 0 //VOREedit Fixes MMIs vision + dead_mob_list -= brainmob//Update dem lists + living_mob_list += brainmob + + user.drop_item() + brainobj = O + brainobj.loc = src + + name = "man-machine interface ([brainmob.real_name])" + icon_state = "mmi_full" + + locked = 1 + + feedback_inc("cyborg_mmis_filled",1) + + return + + if((istype(O,/obj/item/weapon/card/id)||istype(O,/obj/item/device/pda)) && brainmob) + if(allowed(user)) + locked = !locked + to_chat(user, "You [locked ? "lock" : "unlock"] the brain holder.") + else + to_chat(user, "Access denied.") + return + if(brainmob) + O.attack(brainmob, user)//Oh noooeeeee + return + ..() + +//TODO: ORGAN REMOVAL UPDATE. Make the brain remain in the MMI so it doesn't lose organ data. +/obj/item/device/mmi/attack_self(mob/user as mob) + if(!brainmob) + to_chat(user, "You upend the MMI, but there's nothing in it.") + else if(locked) + to_chat(user, "You upend the MMI, but the brain is clamped into place.") + else + to_chat(user, "You upend the MMI, spilling the brain onto the floor.") + var/obj/item/organ/internal/brain/brain + if (brainobj) //Pull brain organ out of MMI. + brainobj.loc = user.loc + brain = brainobj + brainobj = null + else //Or make a new one if empty. + brain = new(user.loc) + brain.preserved = FALSE + brainmob.container = null//Reset brainmob mmi var. + brainmob.loc = brain//Throw mob into brain. + living_mob_list -= brainmob//Get outta here + brain.brainmob = brainmob//Set the brain to use the brainmob + brainmob = null//Set mmi brainmob var to null + + icon_state = "mmi_empty" + name = "Man-Machine Interface" + +/obj/item/device/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. + brainmob = new(src) + brainmob.name = H.real_name + brainmob.real_name = H.real_name + brainmob.dna = H.dna + brainmob.container = src + + // Copy modifiers. + for(var/datum/modifier/M in H.modifiers) + if(M.flags & MODIFIER_GENETIC) + brainmob.add_modifier(M.type) + + name = "Man-Machine Interface: [brainmob.real_name]" + icon_state = "mmi_full" + locked = 1 + return + +/obj/item/device/mmi/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/weapon/rig/rig = src.get_rig() + if(rig) + if(istype(rig,/obj/item/weapon/rig)) + rig.forced_move(direction, user) + +/obj/item/device/mmi/Destroy() + if(isrobot(loc)) + var/mob/living/silicon/robot/borg = loc + borg.mmi = null + QDEL_NULL(radio) + QDEL_NULL(brainmob) + return ..() + +/obj/item/device/mmi/radio_enabled + name = "radio-enabled man-machine interface" + desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio. Wait, don't they all?" + origin_tech = list(TECH_BIO = 4) + +/obj/item/device/mmi/emp_act(severity) + if(!brainmob) + return + else + switch(severity) + if(1) + brainmob.emp_damage += rand(20,30) + if(2) + brainmob.emp_damage += rand(10,20) + if(3) + brainmob.emp_damage += rand(5,10) + if(4) + brainmob.emp_damage += rand(0,5) + ..() + +/obj/item/device/mmi/digital + var/searching = 0 + var/askDelay = 10 * 60 * 1 + req_access = list(access_robotics) + locked = 0 + mecha = null//This does not appear to be used outside of reference in mecha.dm. + var/ghost_query_type = null + +/obj/item/device/mmi/digital/New() + src.brainmob = new(src) +// src.brainmob.add_language("Robot Talk")//No binary without a binary communication device + src.brainmob.add_language(LANGUAGE_GALCOM) + src.brainmob.add_language(LANGUAGE_EAL) + src.brainmob.loc = src + src.brainmob.container = src + src.brainmob.set_stat(CONSCIOUS) + src.brainmob.silent = 0 + radio = new(src) + dead_mob_list -= src.brainmob + +/obj/item/device/mmi/digital/attackby(var/obj/item/O as obj, var/mob/user as mob) + return //Doesn't do anything right now because none of the things that can be done to a regular MMI make any sense for these + +/obj/item/device/mmi/digital/examine(mob/user) + . = ..() + + if(src.brainmob && src.brainmob.key) + switch(src.brainmob.stat) + if(CONSCIOUS) + if(!src.brainmob.client) + . += "It appears to be in stand-by mode." //afk + if(UNCONSCIOUS) + . += "It doesn't seem to be responsive." + if(DEAD) + . += "It appears to be completely inactive." + else + . += "It appears to be completely inactive." + +/obj/item/device/mmi/digital/emp_act(severity) + if(!src.brainmob) + return + else + switch(severity) + if(1) + src.brainmob.emp_damage += rand(20,30) + if(2) + src.brainmob.emp_damage += rand(10,20) + if(3) + src.brainmob.emp_damage += rand(5,10) + if(4) + src.brainmob.emp_damage += rand(0,5) + ..() + +/obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) + brainmob.dna = H.dna + brainmob.timeofhostdeath = H.timeofdeath + brainmob.set_stat(CONSCIOUS) + if(H.mind) + H.mind.transfer_to(brainmob) + return + +/obj/item/device/mmi/digital/attack_self(mob/user as mob) + if(brainmob && !brainmob.key && searching == 0) + //Start the process of searching for a new user. + to_chat(user, span_blue("You carefully locate the manual activation switch and start the [src]'s boot process.")) + request_player() + +/obj/item/device/mmi/digital/proc/request_player() + if(!ghost_query_type) + return + searching = 1 + + var/datum/ghost_query/Q = new ghost_query_type() + var/list/winner = Q.query() + if(winner.len) + var/mob/observer/dead/D = winner[1] + transfer_personality(D) + else + reset_search() + +/obj/item/device/mmi/digital/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. + if(src.brainmob && src.brainmob.key) + return + + src.searching = 0 + + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message(span_blue("\The [src] buzzes quietly, and the golden lights fade away. Perhaps you could try again?")) + +/obj/item/device/mmi/digital/proc/transfer_personality(var/mob/candidate) + announce_ghost_joinleave(candidate, 0, "They are occupying a synthetic brain now.") + src.searching = 0 + if(candidate.mind) + src.brainmob.mind = candidate.mind + src.brainmob.mind.reset() + src.brainmob.ckey = candidate.ckey + src.name = "[name] ([src.brainmob.name])" + to_chat(src.brainmob, "You are [src.name], brought into existence on [station_name()].") + to_chat(src.brainmob, "As a synthetic intelligence, you are designed with organic values in mind.") + to_chat(src.brainmob, "However, unless placed in a lawed chassis, you are not obligated to obey any individual crew member.") //it's not like they can hurt anyone +// to_chat(src.brainmob, "Use say #b to speak to other artificial intelligences.") + src.brainmob.mind.assigned_role = "Synthetic Brain" + + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message(span_blue("\The [src] chimes quietly.")) + +/obj/item/device/mmi/digital/robot + name = "robotic intelligence circuit" + desc = "The pinnacle of artifical intelligence which can be achieved using classical computer science." + catalogue_data = list(/datum/category_item/catalogue/technology/drone/drones) + icon = 'icons/obj/module.dmi' + icon_state = "mainboard" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 3, TECH_DATA = 4) + ghost_query_type = /datum/ghost_query/drone_brain + +/obj/item/device/mmi/digital/robot/New() + ..() + src.brainmob.name = "[pick(list("ADA","DOS","GNU","MAC","WIN","NJS","SKS","DRD","IOS","CRM","IBM","TEX","LVM","BSD",))]-[rand(1000, 9999)]" + src.brainmob.real_name = src.brainmob.name + +/obj/item/device/mmi/digital/robot/transfer_identity(var/mob/living/carbon/H) + ..() + if(brainmob.mind) + brainmob.mind.assigned_role = "Robotic Intelligence" + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're little more than a complex circuit.") + return + +/obj/item/device/mmi/digital/posibrain + name = "positronic brain" + desc = "A cube of shining metal, four inches to a side and covered in shallow grooves." + catalogue_data = list(/datum/category_item/catalogue/technology/positronics) + icon = 'icons/obj/assemblies.dmi' + icon_state = "posibrain" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2, TECH_DATA = 4) + ghost_query_type = /datum/ghost_query/posi_brain + +/obj/item/device/mmi/digital/posibrain/request_player() + icon_state = "posibrain-searching" + ..() + + +/obj/item/device/mmi/digital/posibrain/transfer_identity(var/mob/living/carbon/H) + ..() + if(brainmob.mind) + brainmob.mind.assigned_role = "Positronic Brain" + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a metal cube.") + icon_state = "posibrain-occupied" + return + +/obj/item/device/mmi/digital/posibrain/transfer_personality(var/mob/candidate) + ..() + icon_state = "posibrain-occupied" + +/obj/item/device/mmi/digital/posibrain/reset_search() //We give the players sixty seconds to decide, then reset the timer. + ..() + icon_state = "posibrain" + +/obj/item/device/mmi/digital/posibrain/New() + ..() + src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]" + src.brainmob.real_name = src.brainmob.name + +// This type shouldn't care about brainmobs. +/obj/item/device/mmi/inert + +// This is a 'fake' MMI that is used to let AIs control borg shells directly. +// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need. +/obj/item/device/mmi/inert/ai_remote + name = "\improper AI remote interface" + desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis." + icon = 'icons/obj/module.dmi' + icon_state = "mainboard" + w_class = ITEMSIZE_NORMAL + origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3) diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 8743d0b7e46..43fe7fc495a 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -1,107 +1,107 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/mob/living/carbon/brain - var/obj/item/container = null - var/timeofhostdeath = 0 - var/emp_damage = 0//Handles a type of MMI damage - var/alert = null - use_me = 0 //Can't use the me verb, it's a freaking immobile brain - icon = 'icons/obj/surgery.dmi' - icon_state = "brain1" - no_vore = TRUE //VOREStation Edit - PLEASE. lol. - -/mob/living/carbon/brain/Initialize() - . = ..() - var/datum/reagents/R = new/datum/reagents(1000) - reagents = R - R.my_atom = src - default_language = GLOB.all_languages[LANGUAGE_GALCOM] - -/mob/living/carbon/brain/Destroy() - if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. - if(stat != DEAD) //If not dead. - death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA - ghostize() //Ghostize checks for key so nothing else is necessary. - return ..() - -/mob/living/carbon/brain/say_understands(var/other)//Goddamn is this hackish, but this say code is so odd - if(istype(container, /obj/item/device/mmi)) - if(issilicon(other)) - return TRUE - if(ishuman(other)) - return TRUE - if(isslime(other)) - return TRUE - return ..() - -/mob/living/carbon/brain/update_canmove() - if(in_contents_of(/obj/mecha) || istype(loc, /obj/item/device/mmi)) - canmove = 1 - use_me = 1 - else - canmove = 0 - return canmove - -/mob/living/carbon/brain/isSynthetic() - return istype(loc, /obj/item/device/mmi) - -/mob/living/carbon/brain/runechat_holder(datum/chatmessage/CM) - if(isturf(loc)) - return ..() - - return loc - -/mob/living/carbon/brain/set_typing_indicator(var/state) - if(isturf(loc)) - return ..() - - if(!is_preference_enabled(/datum/client_preference/show_typing_indicator)) - loc.cut_overlay(typing_indicator, TRUE) - return - - var/cur_bubble_appearance = custom_speech_bubble - if(!cur_bubble_appearance || cur_bubble_appearance == "default") - cur_bubble_appearance = speech_bubble_appearance() - if(!typing_indicator || cur_typing_indicator != cur_bubble_appearance) - init_typing_indicator("[cur_bubble_appearance]_typing") - - if(state && !typing) - add_overlay(typing_indicator, TRUE) - typing = TRUE - typing_indicator_active = typing_indicator - else if(typing) - cut_overlay(typing_indicator_active, TRUE) - typing = FALSE - if(typing_indicator_active != typing_indicator) - qdel(typing_indicator_active) - typing_indicator_active = null - - return state - -// Vorestation edit start - -/mob/living/carbon/brain/verb/backup_ping() - set category = "IC" - set name = "Notify Transcore" - set desc = "Your body is gone. Notify robotics to be resleeved!" - var/datum/transcore_db/db = SStranscore.db_by_mind_name(mind.name) - if(db) - var/datum/transhuman/mind_record/record = db.backed_up[src.mind.name] - if(!(record.dead_state == MR_DEAD)) - if((world.time - timeofhostdeath ) > 5 MINUTES) //Allows notify transcore to be used if you have an entry but for some reason weren't marked as dead - record.dead_state = MR_DEAD //Such as if you got scanned but didn't take an implant. It's a little funky, but I mean, you got scanned - db.notify(record) //So you probably will want to let someone know if you die. - record.last_notification = world.time - to_chat(src, "New notification has been sent.") - else - to_chat(src, "Your backup is not past-due yet.") - else if((world.time - record.last_notification) < 5 MINUTES) - to_chat(src, "Too little time has passed since your last notification.") - else - db.notify(record) - record.last_notification = world.time - to_chat(src, "New notification has been sent.") - else - to_chat(src,"No backup record could be found, sorry.") - -// VS edit ends +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +/mob/living/carbon/brain + var/obj/item/container = null + var/timeofhostdeath = 0 + var/emp_damage = 0//Handles a type of MMI damage + var/alert = null + use_me = 0 //Can't use the me verb, it's a freaking immobile brain + icon = 'icons/obj/surgery.dmi' + icon_state = "brain1" + no_vore = TRUE //VOREStation Edit - PLEASE. lol. + +/mob/living/carbon/brain/Initialize() + . = ..() + var/datum/reagents/R = new/datum/reagents(1000) + reagents = R + R.my_atom = src + default_language = GLOB.all_languages[LANGUAGE_GALCOM] + +/mob/living/carbon/brain/Destroy() + if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. + if(stat != DEAD) //If not dead. + death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA + ghostize() //Ghostize checks for key so nothing else is necessary. + return ..() + +/mob/living/carbon/brain/say_understands(var/other)//Goddamn is this hackish, but this say code is so odd + if(istype(container, /obj/item/device/mmi)) + if(issilicon(other)) + return TRUE + if(ishuman(other)) + return TRUE + if(isslime(other)) + return TRUE + return ..() + +/mob/living/carbon/brain/update_canmove() + if(in_contents_of(/obj/mecha) || istype(loc, /obj/item/device/mmi)) + canmove = 1 + use_me = 1 + else + canmove = 0 + return canmove + +/mob/living/carbon/brain/isSynthetic() + return istype(loc, /obj/item/device/mmi) + +/mob/living/carbon/brain/runechat_holder(datum/chatmessage/CM) + if(isturf(loc)) + return ..() + + return loc + +/mob/living/carbon/brain/set_typing_indicator(var/state) + if(isturf(loc)) + return ..() + + if(!is_preference_enabled(/datum/client_preference/show_typing_indicator)) + loc.cut_overlay(typing_indicator, TRUE) + return + + var/cur_bubble_appearance = custom_speech_bubble + if(!cur_bubble_appearance || cur_bubble_appearance == "default") + cur_bubble_appearance = speech_bubble_appearance() + if(!typing_indicator || cur_typing_indicator != cur_bubble_appearance) + init_typing_indicator("[cur_bubble_appearance]_typing") + + if(state && !typing) + add_overlay(typing_indicator, TRUE) + typing = TRUE + typing_indicator_active = typing_indicator + else if(typing) + cut_overlay(typing_indicator_active, TRUE) + typing = FALSE + if(typing_indicator_active != typing_indicator) + qdel(typing_indicator_active) + typing_indicator_active = null + + return state + +// Vorestation edit start + +/mob/living/carbon/brain/verb/backup_ping() + set category = "IC" + set name = "Notify Transcore" + set desc = "Your body is gone. Notify robotics to be resleeved!" + var/datum/transcore_db/db = SStranscore.db_by_mind_name(mind.name) + if(db) + var/datum/transhuman/mind_record/record = db.backed_up[src.mind.name] + if(!(record.dead_state == MR_DEAD)) + if((world.time - timeofhostdeath ) > 5 MINUTES) //Allows notify transcore to be used if you have an entry but for some reason weren't marked as dead + record.dead_state = MR_DEAD //Such as if you got scanned but didn't take an implant. It's a little funky, but I mean, you got scanned + db.notify(record) //So you probably will want to let someone know if you die. + record.last_notification = world.time + to_chat(src, "New notification has been sent.") + else + to_chat(src, "Your backup is not past-due yet.") + else if((world.time - record.last_notification) < 5 MINUTES) + to_chat(src, "Too little time has passed since your last notification.") + else + db.notify(record) + record.last_notification = world.time + to_chat(src, "New notification has been sent.") + else + to_chat(src,"No backup record could be found, sorry.") + +// VS edit ends diff --git a/code/modules/mob/living/carbon/brain/death.dm b/code/modules/mob/living/carbon/brain/death.dm index 687372c6ee1..1ad4169ceba 100644 --- a/code/modules/mob/living/carbon/brain/death.dm +++ b/code/modules/mob/living/carbon/brain/death.dm @@ -1,14 +1,14 @@ -/mob/living/carbon/brain/death(gibbed) - if(!gibbed && istype(container, /obj/item/device/mmi)) //If not gibbed but in a container. - container.icon_state = "mmi_dead" - return ..(gibbed,"beeps shrilly as the MMI flatlines!") - else - return ..(gibbed, DEATHGASP_NO_MESSAGE) - -/mob/living/carbon/brain/gib() - if(istype(container, /obj/item/device/mmi)) - qdel(container)//Gets rid of the MMI if there is one - if(loc) - if(istype(loc,/obj/item/organ/internal/brain)) - qdel(loc)//Gets rid of the brain item +/mob/living/carbon/brain/death(gibbed) + if(!gibbed && istype(container, /obj/item/device/mmi)) //If not gibbed but in a container. + container.icon_state = "mmi_dead" + return ..(gibbed,"beeps shrilly as the MMI flatlines!") + else + return ..(gibbed, DEATHGASP_NO_MESSAGE) + +/mob/living/carbon/brain/gib() + if(istype(container, /obj/item/device/mmi)) + qdel(container)//Gets rid of the MMI if there is one + if(loc) + if(istype(loc,/obj/item/organ/internal/brain)) + qdel(loc)//Gets rid of the brain item ..(null,1) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/emote.dm b/code/modules/mob/living/carbon/brain/emote.dm index 41023589ce1..0f0e4615a07 100644 --- a/code/modules/mob/living/carbon/brain/emote.dm +++ b/code/modules/mob/living/carbon/brain/emote.dm @@ -1,17 +1,17 @@ -var/list/_brain_default_emotes = list( - /decl/emote/audible/alarm, - /decl/emote/audible/alert, - /decl/emote/audible/notice, - /decl/emote/audible/whistle, - /decl/emote/audible/synth, - /decl/emote/audible/beep, - /decl/emote/audible/boop, - /decl/emote/visible/blink, - /decl/emote/visible/flash -) - -/mob/living/carbon/brain/can_emote() - return (istype(container, /obj/item/device/mmi) && ..()) - -/mob/living/carbon/brain/get_available_emotes() - return global._brain_default_emotes.Copy() +var/list/_brain_default_emotes = list( + /decl/emote/audible/alarm, + /decl/emote/audible/alert, + /decl/emote/audible/notice, + /decl/emote/audible/whistle, + /decl/emote/audible/synth, + /decl/emote/audible/beep, + /decl/emote/audible/boop, + /decl/emote/visible/blink, + /decl/emote/visible/flash +) + +/mob/living/carbon/brain/can_emote() + return (istype(container, /obj/item/device/mmi) && ..()) + +/mob/living/carbon/brain/get_available_emotes() + return global._brain_default_emotes.Copy() diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index 075384bc18d..b3d0f142999 100644 --- a/code/modules/mob/living/carbon/brain/life.dm +++ b/code/modules/mob/living/carbon/brain/life.dm @@ -1,222 +1,222 @@ -/mob/living/carbon/brain/handle_breathing() - return - -/mob/living/carbon/brain/handle_mutations_and_radiation() - if (radiation) - if (radiation > 100) - radiation = 100 - if(!container)//If it's not in an MMI - to_chat(src, "You feel weak.") - else//Fluff-wise, since the brain can't detect anything itself, the MMI handles thing like that - to_chat(src, "STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.") - - switch(radiation) - if(1 to 49) - radiation-- - if(prob(25)) - adjustToxLoss(1) - updatehealth() - - if(50 to 74) - radiation -= 2 - adjustToxLoss(1) - if(prob(5)) - radiation -= 5 - if(!container) - to_chat(src, "You feel weak.") - else - to_chat(src, "STATUS: DANGEROUS LEVELS OF RADIATION DETECTED.") - updatehealth() - - if(75 to 100) - radiation -= 3 - adjustToxLoss(3) - updatehealth() - - -/mob/living/carbon/brain/handle_environment(datum/gas_mixture/environment) - if(!environment) - return - var/environment_heat_capacity = environment.heat_capacity() - if(istype(get_turf(src), /turf/space)) - var/turf/heat_turf = get_turf(src) - environment_heat_capacity = heat_turf.heat_capacity - - if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) - var/transfer_coefficient = 1 - - handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) - - if(stat==2) - bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) - - //Account for massive pressure differences - - return //TODO: DEFERRED - -/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) - if(status_flags & GODMODE) return - - if(exposed_temperature > bodytemperature) - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - //adjustFireLoss(2.5*discomfort) - //adjustFireLoss(5.0*discomfort) - adjustFireLoss(20.0*discomfort) - - else - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - //adjustFireLoss(2.5*discomfort) - adjustFireLoss(5.0*discomfort) - - -/mob/living/carbon/brain/handle_chemicals_in_body() - chem_effects.Cut() - - if(touching) touching.metabolize() - if(ingested) ingested.metabolize() - if(bloodstr) bloodstr.metabolize() - - // decrement dizziness counter, clamped to 0 - if(resting) - dizziness = max(0, dizziness - 5) - else - dizziness = max(0, dizziness - 1) - - updatehealth() - - return //TODO: DEFERRED - -/mob/living/carbon/brain/handle_regular_status_updates() //TODO: comment out the unused bits >_> - updatehealth() - - if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP - blinded = 1 - silent = 0 - else //ALIVE. LIGHTS ARE ON - if( !container && (health < config.health_threshold_dead || ((world.time - timeofhostdeath) > config.revival_brain_life)) ) - death() - blinded = 1 - silent = 0 - return 1 - - //Handling EMP effect in the Life(), it's made VERY simply, and has some additional effects handled elsewhere - if(emp_damage) //This is pretty much a damage type only used by MMIs, dished out by the emp_act - if(!(container && istype(container, /obj/item/device/mmi))) - emp_damage = 0 - else - emp_damage = round(emp_damage,1)//Let's have some nice numbers to work with - switch(emp_damage) - if(31 to INFINITY) - emp_damage = 30//Let's not overdo it - if(21 to 30)//High level of EMP damage, unable to see, hear, or speak - SetBlinded(1) - blinded = 1 - ear_deaf = 1 - silent = 1 - if(!alert)//Sounds an alarm, but only once per 'level' - emote("alarm") - to_chat(src, "Major electrical distruption detected: System rebooting.") - alert = 1 - if(prob(75)) - emp_damage -= 1 - if(20) - alert = 0 - blinded = 0 - SetBlinded(0) - ear_deaf = 0 - silent = 0 - emp_damage -= 1 - if(11 to 19)//Moderate level of EMP damage, resulting in nearsightedness and ear damage - eye_blurry = 1 - ear_damage = 1 - if(!alert) - emote("alert") - to_chat(src, "Primary systems are now online.") - alert = 1 - if(prob(50)) - emp_damage -= 1 - if(10) - alert = 0 - eye_blurry = 0 - ear_damage = 0 - emp_damage -= 1 - if(2 to 9)//Low level of EMP damage, has few effects(handled elsewhere) - if(!alert) - emote("notice") - to_chat(src, "System reboot nearly complete.") - alert = 1 - if(prob(25)) - emp_damage -= 1 - if(1) - alert = 0 - to_chat(src, "All systems restored.") - emp_damage -= 1 - - return 1 - -/mob/living/carbon/brain/handle_regular_hud_updates() - if (stat == 2 || (XRAY in src.mutations)) - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - else if (stat != 2) - sight &= ~SEE_TURFS - sight &= ~SEE_MOBS - sight &= ~SEE_OBJS - see_in_dark = 2 - see_invisible = SEE_INVISIBLE_LIVING - - if (healths) - if (stat != 2) - switch(health) - if(100 to INFINITY) - healths.icon_state = "health0" - if(80 to 100) - healths.icon_state = "health1" - if(60 to 80) - healths.icon_state = "health2" - if(40 to 60) - healths.icon_state = "health3" - if(20 to 40) - healths.icon_state = "health4" - if(0 to 20) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - if (stat == 2 || (XRAY in src.mutations)) - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - else if (stat != 2) - sight &= ~SEE_TURFS - sight &= ~SEE_MOBS - sight &= ~SEE_OBJS - see_in_dark = 2 - see_invisible = SEE_INVISIBLE_LIVING - if (client) - client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) - - if (stat != 2) - if ((blinded)) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - - if (machine) - if (!( machine.check_eye(src) )) - reset_view(null) - else - if(client && !client.adminobs) - reset_view(null) - - return 1 \ No newline at end of file +/mob/living/carbon/brain/handle_breathing() + return + +/mob/living/carbon/brain/handle_mutations_and_radiation() + if (radiation) + if (radiation > 100) + radiation = 100 + if(!container)//If it's not in an MMI + to_chat(src, span_red("You feel weak.")) + else//Fluff-wise, since the brain can't detect anything itself, the MMI handles thing like that + to_chat(src, span_red("STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.")) + + switch(radiation) + if(1 to 49) + radiation-- + if(prob(25)) + adjustToxLoss(1) + updatehealth() + + if(50 to 74) + radiation -= 2 + adjustToxLoss(1) + if(prob(5)) + radiation -= 5 + if(!container) + to_chat(src, span_red("You feel weak.")) + else + to_chat(src, span_red("STATUS: DANGEROUS LEVELS OF RADIATION DETECTED.")) + updatehealth() + + if(75 to 100) + radiation -= 3 + adjustToxLoss(3) + updatehealth() + + +/mob/living/carbon/brain/handle_environment(datum/gas_mixture/environment) + if(!environment) + return + var/environment_heat_capacity = environment.heat_capacity() + if(istype(get_turf(src), /turf/space)) + var/turf/heat_turf = get_turf(src) + environment_heat_capacity = heat_turf.heat_capacity + + if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) + var/transfer_coefficient = 1 + + handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) + + if(stat==2) + bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) + + //Account for massive pressure differences + + return //TODO: DEFERRED + +/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) + if(status_flags & GODMODE) return + + if(exposed_temperature > bodytemperature) + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + //adjustFireLoss(2.5*discomfort) + //adjustFireLoss(5.0*discomfort) + adjustFireLoss(20.0*discomfort) + + else + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + //adjustFireLoss(2.5*discomfort) + adjustFireLoss(5.0*discomfort) + + +/mob/living/carbon/brain/handle_chemicals_in_body() + chem_effects.Cut() + + if(touching) touching.metabolize() + if(ingested) ingested.metabolize() + if(bloodstr) bloodstr.metabolize() + + // decrement dizziness counter, clamped to 0 + if(resting) + dizziness = max(0, dizziness - 5) + else + dizziness = max(0, dizziness - 1) + + updatehealth() + + return //TODO: DEFERRED + +/mob/living/carbon/brain/handle_regular_status_updates() //TODO: comment out the unused bits >_> + updatehealth() + + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP + blinded = 1 + silent = 0 + else //ALIVE. LIGHTS ARE ON + if( !container && (health < config.health_threshold_dead || ((world.time - timeofhostdeath) > config.revival_brain_life)) ) + death() + blinded = 1 + silent = 0 + return 1 + + //Handling EMP effect in the Life(), it's made VERY simply, and has some additional effects handled elsewhere + if(emp_damage) //This is pretty much a damage type only used by MMIs, dished out by the emp_act + if(!(container && istype(container, /obj/item/device/mmi))) + emp_damage = 0 + else + emp_damage = round(emp_damage,1)//Let's have some nice numbers to work with + switch(emp_damage) + if(31 to INFINITY) + emp_damage = 30//Let's not overdo it + if(21 to 30)//High level of EMP damage, unable to see, hear, or speak + SetBlinded(1) + blinded = 1 + ear_deaf = 1 + silent = 1 + if(!alert)//Sounds an alarm, but only once per 'level' + emote("alarm") + to_chat(src, span_red("Major electrical distruption detected: System rebooting.")) + alert = 1 + if(prob(75)) + emp_damage -= 1 + if(20) + alert = 0 + blinded = 0 + SetBlinded(0) + ear_deaf = 0 + silent = 0 + emp_damage -= 1 + if(11 to 19)//Moderate level of EMP damage, resulting in nearsightedness and ear damage + eye_blurry = 1 + ear_damage = 1 + if(!alert) + emote("alert") + to_chat(src, span_red("Primary systems are now online.")) + alert = 1 + if(prob(50)) + emp_damage -= 1 + if(10) + alert = 0 + eye_blurry = 0 + ear_damage = 0 + emp_damage -= 1 + if(2 to 9)//Low level of EMP damage, has few effects(handled elsewhere) + if(!alert) + emote("notice") + to_chat(src, span_red("System reboot nearly complete.")) + alert = 1 + if(prob(25)) + emp_damage -= 1 + if(1) + alert = 0 + to_chat(src, span_red("All systems restored.")) + emp_damage -= 1 + + return 1 + +/mob/living/carbon/brain/handle_regular_hud_updates() + if (stat == 2 || (XRAY in src.mutations)) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + + if (healths) + if (stat != 2) + switch(health) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + if (stat == 2 || (XRAY in src.mutations)) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + if (client) + client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) + + if (stat != 2) + if ((blinded)) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + else + clear_fullscreen("blind") + set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + + if (machine) + if (!( machine.check_eye(src) )) + reset_view(null) + else + if(client && !client.adminobs) + reset_view(null) + + return 1 diff --git a/code/modules/mob/living/carbon/brain/login.dm b/code/modules/mob/living/carbon/brain/login.dm index 107b8e0ab78..6248b8f154d 100644 --- a/code/modules/mob/living/carbon/brain/login.dm +++ b/code/modules/mob/living/carbon/brain/login.dm @@ -1,3 +1,3 @@ -/mob/living/carbon/brain/Login() - ..() +/mob/living/carbon/brain/Login() + ..() SetSleeping(0) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index a1f12f97b50..cb155797a22 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -16,7 +16,7 @@ /obj/item/device/mmi/digital/posibrain/attack_self(mob/user as mob) if(brainmob && !brainmob.key && searching == 0) //Start the process of searching for a new user. - to_chat(user, "You carefully locate the manual activation switch and start the positronic brain's boot process.") + to_chat(user, span_blue("You carefully locate the manual activation switch and start the positronic brain's boot process.")) icon_state = "posibrain-searching" src.searching = 1 src.request_player() @@ -68,7 +68,7 @@ var/turf/T = get_turf_or_move(src.loc) for (var/mob/M in viewers(T)) - M.show_message("The positronic brain beeps as it loads a personality.") + M.show_message(span_blue("The positronic brain beeps as it loads a personality.")) playsound(src, 'sound/misc/boobeebeep.ogg', 50, 1) icon_state = "posibrain-occupied" @@ -82,7 +82,7 @@ var/turf/T = get_turf_or_move(src.loc) for (var/mob/M in viewers(T)) - M.show_message("The positronic brain buzzes and beeps, and the golden lights fade away. Perhaps you could try again?") + M.show_message(span_blue("The positronic brain buzzes and beeps, and the golden lights fade away. Perhaps you could try again?")) playsound(src, 'sound/misc/buzzbeep.ogg', 50, 1) /obj/item/device/mmi/digital/posibrain/emp_act(severity) diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index e03067db1a3..5a890b6aa51 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -1,29 +1,29 @@ -//TODO: Convert this over for languages. -/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - if(silent) - return - - message = sanitize(message) - - if(!(container && container.can_speak)) - return //Certain objects can speak, like MMIs. Most others cannot. -Q - else - if(prob(emp_damage * 4)) - if(prob(10))//10% chance to drop the message entirely - return - else - message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher - - ..() - -/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios) - ..() - if(message_mode) - var/obj/item/device/mmi/R = container - if(R.radio && R.radio.radio_enabled) - if(message_mode == "general") - message_mode = null - return R.radio.talk_into(src, message, message_mode, verb, speaking) - else - to_chat(src, "Your radio is disabled.") - return 0 +//TODO: Convert this over for languages. +/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + if(silent) + return + + message = sanitize(message) + + if(!(container && container.can_speak)) + return //Certain objects can speak, like MMIs. Most others cannot. -Q + else + if(prob(emp_damage * 4)) + if(prob(10))//10% chance to drop the message entirely + return + else + message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher + + ..() + +/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios) + ..() + if(message_mode) + var/obj/item/device/mmi/R = container + if(R.radio && R.radio.radio_enabled) + if(message_mode == "general") + message_mode = null + return R.radio.talk_into(src, message, message_mode, verb, speaking) + else + to_chat(src, "Your radio is disabled.") + return 0 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 3904656a995..316f314860d 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -80,7 +80,7 @@ M.loc = src.loc for(var/mob/N in viewers(src, null)) if(N.client) - N.show_message(text("[M] bursts out of [src]!"), 2) + N.show_message(span_red(text("[M] bursts out of [src]!")), 2) ..() /mob/living/carbon/attack_hand(mob/M as mob) @@ -91,7 +91,7 @@ if (H.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - to_chat(H, "You can't use your [temp.name]") + to_chat(H, span_red("You can't use your [temp.name]")) return return @@ -400,7 +400,7 @@ set category = "IC" if(usr.sleeping) - to_chat(usr, "You are already sleeping") + to_chat(usr, span_red("You are already sleeping")) return if(tgui_alert(src,"You sure you want to sleep for a while?","Sleep",list("Yes","No")) == "Yes") usr.AdjustSleeping(20) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index a5230484ed2..8dc29e6d3b8 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -1,32 +1,32 @@ -/mob/living/carbon - gender = MALE - blocks_emissive = EMISSIVE_BLOCK_UNIQUE // BLEH, this could be improved for transparent species and stuff! And blocks glowing eyes?! - var/datum/species/species //Contains icon generation and language information, set during New(). - var/list/stomach_contents = list() - var/list/datum/disease2/disease/virus2 = list() - var/list/antibodies = list() - var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm - - var/life_tick = 0 // The amount of life ticks that have processed on this mob. - - // total amount of wounds on mob, used to spread out healing and the like over all wounds - var/number_wounds = 0 - var/obj/item/handcuffed = null //Whether or not the mob is handcuffed - var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. - //Surgery info - var/datum/surgery_status/op_stage = new/datum/surgery_status - //Active emote/pose - var/pose = null - var/list/chem_effects = list() - var/datum/reagents/metabolism/bloodstream/bloodstr = null - var/datum/reagents/metabolism/ingested/ingested = null - var/datum/reagents/metabolism/touch/touching = null - - var/pulse = PULSE_NORM //current pulse level - - var/does_not_breathe = 0 //Used for specific mobs that can't take advantage of the species flags (changelings) - - //these two help govern taste. The first is the last time a taste message was shown to the plaer. - //the second is the message in question. - var/last_taste_time = 0 +/mob/living/carbon + gender = MALE + blocks_emissive = EMISSIVE_BLOCK_UNIQUE // BLEH, this could be improved for transparent species and stuff! And blocks glowing eyes?! + var/datum/species/species //Contains icon generation and language information, set during New(). + var/list/stomach_contents = list() + var/list/datum/disease2/disease/virus2 = list() + var/list/antibodies = list() + var/last_eating = 0 //Not sure what this does... I found it hidden in food.dm + + var/life_tick = 0 // The amount of life ticks that have processed on this mob. + + // total amount of wounds on mob, used to spread out healing and the like over all wounds + var/number_wounds = 0 + var/obj/item/handcuffed = null //Whether or not the mob is handcuffed + var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. + //Surgery info + var/datum/surgery_status/op_stage = new/datum/surgery_status + //Active emote/pose + var/pose = null + var/list/chem_effects = list() + var/datum/reagents/metabolism/bloodstream/bloodstr = null + var/datum/reagents/metabolism/ingested/ingested = null + var/datum/reagents/metabolism/touch/touching = null + + var/pulse = PULSE_NORM //current pulse level + + var/does_not_breathe = 0 //Used for specific mobs that can't take advantage of the species flags (changelings) + + //these two help govern taste. The first is the last time a taste message was shown to the plaer. + //the second is the message in question. + var/last_taste_time = 0 var/last_taste_text = "" \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/MedicalSideEffects.dm b/code/modules/mob/living/carbon/human/MedicalSideEffects.dm index b37dff23ac3..f5b52e09294 100644 --- a/code/modules/mob/living/carbon/human/MedicalSideEffects.dm +++ b/code/modules/mob/living/carbon/human/MedicalSideEffects.dm @@ -24,7 +24,7 @@ for(var/R in cures) if(H.reagents.has_reagent(R)) if (cure_message) - to_chat(H, "[cure_message]") + to_chat(H, span_blue("[cure_message]")) return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index b1424ae5b04..c2f767cbcb5 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -1,252 +1,252 @@ -/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, - var/mob/user = src, - var/check_species_whitelist = 1, - var/list/species_whitelist = list(), - var/list/species_blacklist = list(), - var/datum/tgui_state/state = GLOB.tgui_self_state) - var/datum/tgui_module/appearance_changer/AC = new(src, src, check_species_whitelist, species_whitelist, species_blacklist) - AC.flags = flags - AC.tgui_interact(user, custom_state = state) - -/mob/living/carbon/human/proc/change_species(var/new_species) - if(!new_species) - return - - if(species == new_species) - return - - if(!(new_species in GLOB.all_species)) - return - - set_species(new_species) - reset_hair() - return 1 - -/mob/living/carbon/human/proc/change_gender(var/gender) - if(src.gender == gender) - return - - src.gender = gender - //reset_hair() //VOREStation Remove - Don't just randomize hair on gender swaps for prometheans. - update_dna() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/change_gender_identity(var/identifying_gender) - if(src.identifying_gender == identifying_gender) - return - - src.identifying_gender = identifying_gender - return 1 - -/mob/living/carbon/human/proc/change_hair(var/hair_style) - if(!hair_style) - return - - if(h_style == hair_style) - return - - if(!(hair_style in hair_styles_list)) - return - - h_style = hair_style - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_hair_gradient(var/hair_gradient) - if(!hair_gradient) - return - - if(grad_style == hair_gradient) - return - - if(!(hair_gradient in GLOB.hair_gradients)) - return - - grad_style = hair_gradient - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_facial_hair(var/facial_hair_style) - if(!facial_hair_style) - return - - if(f_style == facial_hair_style) - return - - if(!(facial_hair_style in facial_hair_styles_list)) - return - - f_style = facial_hair_style - - update_hair() - return 1 - -/mob/living/carbon/human/proc/reset_hair() - var/list/valid_hairstyles = generate_valid_hairstyles() - var/list/valid_facial_hairstyles = generate_valid_facial_hairstyles() - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - else - //this shouldn't happen - h_style = "Bald" - - if(valid_facial_hairstyles.len) - f_style = pick(valid_facial_hairstyles) - else - //this shouldn't happen - f_style = "Shaved" - - update_hair() - -/mob/living/carbon/human/proc/change_eye_color(var/red, var/green, var/blue) - if(red == r_eyes && green == g_eyes && blue == b_eyes) - return - - r_eyes = red - g_eyes = green - b_eyes = blue - - update_eyes() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/change_hair_color(var/red, var/green, var/blue) - if(red == r_hair && green == g_hair && blue == b_hair) - return - - r_hair = red - g_hair = green - b_hair = blue - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_grad_color(var/red, var/green, var/blue) - if(red == r_grad && green == g_grad && blue == b_grad) - return - - r_grad = red - g_grad = green - b_grad = blue - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_facial_hair_color(var/red, var/green, var/blue) - if(red == r_facial && green == g_facial && blue == b_facial) - return - - r_facial = red - g_facial = green - b_facial = blue - - update_hair() - return 1 - -/mob/living/carbon/human/proc/change_skin_color(var/red, var/green, var/blue) - if(red == r_skin && green == g_skin && blue == b_skin || !(species.appearance_flags & HAS_SKIN_COLOR)) - return - - r_skin = red - g_skin = green - b_skin = blue - - force_update_limbs() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/change_skin_tone(var/tone) - if(s_tone == tone || !(species.appearance_flags & HAS_SKIN_TONE)) - return - - s_tone = tone - - force_update_limbs() - update_icons_body() - return 1 - -/mob/living/carbon/human/proc/update_dna() - check_dna() - dna.ready_dna(src) - for(var/obj/item/organ/O in organs) - O.dna = dna // Update all of those because apparently they're separate, and icons won't update properly - -/mob/living/carbon/human/proc/generate_valid_species(var/check_whitelist = 1, var/list/whitelist = list(), var/list/blacklist = list()) - var/list/valid_species = new() - for(var/current_species_name in GLOB.all_species) - var/datum/species/current_species = GLOB.all_species[current_species_name] - - if(check_whitelist && config.usealienwhitelist && !check_rights(R_ADMIN|R_EVENT, 0, src)) //If we're using the whitelist, make sure to check it! - if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) - continue - if(whitelist.len && !(current_species_name in whitelist)) - continue - if(blacklist.len && (current_species_name in blacklist)) - continue - if((current_species.spawn_flags & SPECIES_IS_WHITELISTED) && !is_alien_whitelisted(src, current_species)) - continue - - valid_species += current_species_name - - return valid_species - -/mob/living/carbon/human/proc/generate_valid_hairstyles(var/check_gender = 1) - - var/use_species = species.get_bodytype(src) - var/obj/item/organ/external/head/H = get_organ(BP_HEAD) - if(H) use_species = H.species.get_bodytype(src) - - var/list/valid_hairstyles = new() - for(var/hairstyle in hair_styles_list) - var/datum/sprite_accessory/S = hair_styles_list[hairstyle] - - if(check_gender && gender != NEUTER) - if(gender == MALE && S.gender == FEMALE) - continue - else if(gender == FEMALE && S.gender == MALE) - continue - - if(!(use_species in S.species_allowed)) - continue - - if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check - continue //VOREStation add - ckey whitelist check - - valid_hairstyles += hairstyle - - return valid_hairstyles - -/mob/living/carbon/human/proc/generate_valid_facial_hairstyles() - - var/use_species = species.get_bodytype(src) - var/obj/item/organ/external/head/H = get_organ(BP_HEAD) - if(H) use_species = H.species.get_bodytype(src) - - var/list/valid_facial_hairstyles = new() - for(var/facialhairstyle in facial_hair_styles_list) - var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] - - if(gender != NEUTER) - if(gender == MALE && S.gender == FEMALE) - continue - else if(gender == FEMALE && S.gender == MALE) - continue - - if(!(use_species in S.species_allowed)) - continue - - if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check - continue //VOREStation add - ckey whitelist check - - valid_facial_hairstyles += facialhairstyle - - return valid_facial_hairstyles - -/mob/living/carbon/human/proc/force_update_limbs() - for(var/obj/item/organ/external/O in organs) - O.sync_colour_to_human(src) - update_icons_body(FALSE) +/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, + var/mob/user = src, + var/check_species_whitelist = 1, + var/list/species_whitelist = list(), + var/list/species_blacklist = list(), + var/datum/tgui_state/state = GLOB.tgui_self_state) + var/datum/tgui_module/appearance_changer/AC = new(src, src, check_species_whitelist, species_whitelist, species_blacklist) + AC.flags = flags + AC.tgui_interact(user, custom_state = state) + +/mob/living/carbon/human/proc/change_species(var/new_species) + if(!new_species) + return + + if(species == new_species) + return + + if(!(new_species in GLOB.all_species)) + return + + set_species(new_species) + reset_hair() + return 1 + +/mob/living/carbon/human/proc/change_gender(var/gender) + if(src.gender == gender) + return + + src.gender = gender + //reset_hair() //VOREStation Remove - Don't just randomize hair on gender swaps for prometheans. + update_dna() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/change_gender_identity(var/identifying_gender) + if(src.identifying_gender == identifying_gender) + return + + src.identifying_gender = identifying_gender + return 1 + +/mob/living/carbon/human/proc/change_hair(var/hair_style) + if(!hair_style) + return + + if(h_style == hair_style) + return + + if(!(hair_style in hair_styles_list)) + return + + h_style = hair_style + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_hair_gradient(var/hair_gradient) + if(!hair_gradient) + return + + if(grad_style == hair_gradient) + return + + if(!(hair_gradient in GLOB.hair_gradients)) + return + + grad_style = hair_gradient + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_facial_hair(var/facial_hair_style) + if(!facial_hair_style) + return + + if(f_style == facial_hair_style) + return + + if(!(facial_hair_style in facial_hair_styles_list)) + return + + f_style = facial_hair_style + + update_hair() + return 1 + +/mob/living/carbon/human/proc/reset_hair() + var/list/valid_hairstyles = generate_valid_hairstyles() + var/list/valid_facial_hairstyles = generate_valid_facial_hairstyles() + + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + else + //this shouldn't happen + h_style = "Bald" + + if(valid_facial_hairstyles.len) + f_style = pick(valid_facial_hairstyles) + else + //this shouldn't happen + f_style = "Shaved" + + update_hair() + +/mob/living/carbon/human/proc/change_eye_color(var/red, var/green, var/blue) + if(red == r_eyes && green == g_eyes && blue == b_eyes) + return + + r_eyes = red + g_eyes = green + b_eyes = blue + + update_eyes() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/change_hair_color(var/red, var/green, var/blue) + if(red == r_hair && green == g_hair && blue == b_hair) + return + + r_hair = red + g_hair = green + b_hair = blue + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_grad_color(var/red, var/green, var/blue) + if(red == r_grad && green == g_grad && blue == b_grad) + return + + r_grad = red + g_grad = green + b_grad = blue + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_facial_hair_color(var/red, var/green, var/blue) + if(red == r_facial && green == g_facial && blue == b_facial) + return + + r_facial = red + g_facial = green + b_facial = blue + + update_hair() + return 1 + +/mob/living/carbon/human/proc/change_skin_color(var/red, var/green, var/blue) + if(red == r_skin && green == g_skin && blue == b_skin || !(species.appearance_flags & HAS_SKIN_COLOR)) + return + + r_skin = red + g_skin = green + b_skin = blue + + force_update_limbs() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/change_skin_tone(var/tone) + if(s_tone == tone || !(species.appearance_flags & HAS_SKIN_TONE)) + return + + s_tone = tone + + force_update_limbs() + update_icons_body() + return 1 + +/mob/living/carbon/human/proc/update_dna() + check_dna() + dna.ready_dna(src) + for(var/obj/item/organ/O in organs) + O.dna = dna // Update all of those because apparently they're separate, and icons won't update properly + +/mob/living/carbon/human/proc/generate_valid_species(var/check_whitelist = 1, var/list/whitelist = list(), var/list/blacklist = list()) + var/list/valid_species = new() + for(var/current_species_name in GLOB.all_species) + var/datum/species/current_species = GLOB.all_species[current_species_name] + + if(check_whitelist && config.usealienwhitelist && !check_rights(R_ADMIN|R_EVENT, 0, src)) //If we're using the whitelist, make sure to check it! + if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) + continue + if(whitelist.len && !(current_species_name in whitelist)) + continue + if(blacklist.len && (current_species_name in blacklist)) + continue + if((current_species.spawn_flags & SPECIES_IS_WHITELISTED) && !is_alien_whitelisted(src, current_species)) + continue + + valid_species += current_species_name + + return valid_species + +/mob/living/carbon/human/proc/generate_valid_hairstyles(var/check_gender = 1) + + var/use_species = species.get_bodytype(src) + var/obj/item/organ/external/head/H = get_organ(BP_HEAD) + if(H) use_species = H.species.get_bodytype(src) + + var/list/valid_hairstyles = new() + for(var/hairstyle in hair_styles_list) + var/datum/sprite_accessory/S = hair_styles_list[hairstyle] + + if(check_gender && gender != NEUTER) + if(gender == MALE && S.gender == FEMALE) + continue + else if(gender == FEMALE && S.gender == MALE) + continue + + if(!(use_species in S.species_allowed)) + continue + + if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check + continue //VOREStation add - ckey whitelist check + + valid_hairstyles += hairstyle + + return valid_hairstyles + +/mob/living/carbon/human/proc/generate_valid_facial_hairstyles() + + var/use_species = species.get_bodytype(src) + var/obj/item/organ/external/head/H = get_organ(BP_HEAD) + if(H) use_species = H.species.get_bodytype(src) + + var/list/valid_facial_hairstyles = new() + for(var/facialhairstyle in facial_hair_styles_list) + var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle] + + if(gender != NEUTER) + if(gender == MALE && S.gender == FEMALE) + continue + else if(gender == FEMALE && S.gender == MALE) + continue + + if(!(use_species in S.species_allowed)) + continue + + if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check + continue //VOREStation add - ckey whitelist check + + valid_facial_hairstyles += facialhairstyle + + return valid_facial_hairstyles + +/mob/living/carbon/human/proc/force_update_limbs() + for(var/obj/item/organ/external/O in organs) + O.sync_colour_to_human(src) + update_icons_body(FALSE) diff --git a/code/modules/mob/living/carbon/human/chem_side_effects.dm b/code/modules/mob/living/carbon/human/chem_side_effects.dm index b37dff23ac3..f5b52e09294 100644 --- a/code/modules/mob/living/carbon/human/chem_side_effects.dm +++ b/code/modules/mob/living/carbon/human/chem_side_effects.dm @@ -24,7 +24,7 @@ for(var/R in cures) if(H.reagents.has_reagent(R)) if (cure_message) - to_chat(H, "[cure_message]") + to_chat(H, span_blue("[cure_message]")) return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index dd816d1e8f5..a34cce9cb17 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -1,147 +1,147 @@ -/mob/living/carbon/human/gib() - - if(vr_holder) - exit_vr() - // Delete the link, because this mob won't be around much longer - vr_holder.vr_link = null - - if(vr_link) - vr_link.exit_vr() - vr_link.vr_holder = null - vr_link = null - - for(var/obj/item/organ/I in internal_organs) - I.removed() - if(isturf(I?.loc)) // Some organs qdel themselves or other things when removed - I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),30) - - for(var/obj/item/organ/external/E in src.organs) - E.droplimb(0,DROPLIMB_EDGE,1) - - sleep(1) - - for(var/obj/item/I in src) - drop_from_inventory(I) - I.throw_at(get_edge_target_turf(src,pick(alldirs)), rand(1,3), round(30/I.w_class)) - - ..(species.gibbed_anim) // uses the default mob.dmi file for these, so we only need to specify the first argument - gibs(loc, dna, null, species.get_flesh_colour(src), species.get_blood_colour(src)) - -/mob/living/carbon/human/dust() - if(species) - ..(species.dusted_anim, species.remains_type) - else - ..() - -/mob/living/carbon/human/ash() - if(species) - ..(species.dusted_anim) - else - ..() - -/mob/living/carbon/human/death(gibbed) - - if(stat == DEAD) return - - BITSET(hud_updateflag, HEALTH_HUD) - BITSET(hud_updateflag, STATUS_HUD) - BITSET(hud_updateflag, LIFE_HUD) - - //Handle species-specific deaths. - species.handle_death(src) - animate_tail_stop() - stop_flying() //VOREStation Edit. - - //Handle snowflake ling stuff. - if(mind && mind.changeling) - // If the ling is capable of revival, don't allow them to see deadchat. - if(mind.changeling.chem_charges >= CHANGELING_STASIS_COST) - if(mind.changeling.max_geneticpoints >= 0) // Absorbed lings don't count, as they can't revive. - forbid_seeing_deadchat = TRUE - - //Handle brain slugs. - var/obj/item/organ/external/Hd = get_organ(BP_HEAD) - var/mob/living/simple_mob/animal/borer/B - - if(Hd) - for(var/I in Hd.implants) - if(istype(I,/mob/living/simple_mob/animal/borer)) - B = I - if(B) - if(!B.ckey && ckey && B.controlling) - B.ckey = ckey - B.controlling = 0 - if(B.host_brain.ckey) - ckey = B.host_brain.ckey - B.host_brain.ckey = null - B.host_brain.name = "host brain" - B.host_brain.real_name = "host brain" - - verbs -= /mob/living/carbon/proc/release_control - - callHook("death", list(src, gibbed)) - - if(mind) - // SSgame_master.adjust_danger(gibbed ? 40 : 20) // VOREStation Edit - We don't use SSgame_master yet. - for(var/mob/observer/dead/O in mob_list) - if(O.client && O.client.is_preference_enabled(/datum/client_preference/show_dsay)) - to_chat(O, "[src] has died in [get_area(src)]. [ghost_follow_link(src, O)] ") - - if(!gibbed && species.death_sound) - playsound(src, species.death_sound, 80, 1, 1) - - if(ticker && ticker.mode) - sql_report_death(src) - ticker.mode.check_win() - - if(wearing_rig) - wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") - - // If the body is in VR, move the mind back to the real world - if(vr_holder) - src.exit_vr() - src.vr_holder.vr_link = null - for(var/obj/item/W in src) - src.drop_from_inventory(W) - - // If our mind is in VR, bring it back to the real world so it can die with its body - if(vr_link) - vr_link.exit_vr() - vr_link.vr_holder = null - vr_link = null - to_chat(src, "Everything abruptly stops.") - - return ..(gibbed,species.get_death_message(src)) - -/mob/living/carbon/human/proc/ChangeToHusk() - if(HUSK in mutations) return - - if(f_style) - f_style = "Shaved" //we only change the icon_state of the hair datum, so it doesn't mess up their UI/UE - if(h_style) - h_style = "Bald" - update_hair(0) - - mutations.Add(HUSK) - status_flags |= DISFIGURED //makes them unknown without fucking up other stuff like admintools - update_icons_body() - return - -/mob/living/carbon/human/proc/Drain() - ChangeToHusk() - mutations |= HUSK - return - -/mob/living/carbon/human/proc/ChangeToSkeleton() - if(SKELETON in src.mutations) return - - if(f_style) - f_style = "Shaved" - if(h_style) - h_style = "Bald" - update_hair(0) - - mutations.Add(SKELETON) - status_flags |= DISFIGURED - update_icons_body() - return +/mob/living/carbon/human/gib() + + if(vr_holder) + exit_vr() + // Delete the link, because this mob won't be around much longer + vr_holder.vr_link = null + + if(vr_link) + vr_link.exit_vr() + vr_link.vr_holder = null + vr_link = null + + for(var/obj/item/organ/I in internal_organs) + I.removed() + if(isturf(I?.loc)) // Some organs qdel themselves or other things when removed + I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),30) + + for(var/obj/item/organ/external/E in src.organs) + E.droplimb(0,DROPLIMB_EDGE,1) + + sleep(1) + + for(var/obj/item/I in src) + drop_from_inventory(I) + I.throw_at(get_edge_target_turf(src,pick(alldirs)), rand(1,3), round(30/I.w_class)) + + ..(species.gibbed_anim) // uses the default mob.dmi file for these, so we only need to specify the first argument + gibs(loc, dna, null, species.get_flesh_colour(src), species.get_blood_colour(src)) + +/mob/living/carbon/human/dust() + if(species) + ..(species.dusted_anim, species.remains_type) + else + ..() + +/mob/living/carbon/human/ash() + if(species) + ..(species.dusted_anim) + else + ..() + +/mob/living/carbon/human/death(gibbed) + + if(stat == DEAD) return + + BITSET(hud_updateflag, HEALTH_HUD) + BITSET(hud_updateflag, STATUS_HUD) + BITSET(hud_updateflag, LIFE_HUD) + + //Handle species-specific deaths. + species.handle_death(src) + animate_tail_stop() + stop_flying() //VOREStation Edit. + + //Handle snowflake ling stuff. + if(mind && mind.changeling) + // If the ling is capable of revival, don't allow them to see deadchat. + if(mind.changeling.chem_charges >= CHANGELING_STASIS_COST) + if(mind.changeling.max_geneticpoints >= 0) // Absorbed lings don't count, as they can't revive. + forbid_seeing_deadchat = TRUE + + //Handle brain slugs. + var/obj/item/organ/external/Hd = get_organ(BP_HEAD) + var/mob/living/simple_mob/animal/borer/B + + if(Hd) + for(var/I in Hd.implants) + if(istype(I,/mob/living/simple_mob/animal/borer)) + B = I + if(B) + if(!B.ckey && ckey && B.controlling) + B.ckey = ckey + B.controlling = 0 + if(B.host_brain.ckey) + ckey = B.host_brain.ckey + B.host_brain.ckey = null + B.host_brain.name = "host brain" + B.host_brain.real_name = "host brain" + + verbs -= /mob/living/carbon/proc/release_control + + callHook("death", list(src, gibbed)) + + if(mind) + // SSgame_master.adjust_danger(gibbed ? 40 : 20) // VOREStation Edit - We don't use SSgame_master yet. + for(var/mob/observer/dead/O in mob_list) + if(O.client && O.client.is_preference_enabled(/datum/client_preference/show_dsay)) + to_chat(O, "[src] has died in [get_area(src)]. [ghost_follow_link(src, O)] ") + + if(!gibbed && species.death_sound) + playsound(src, species.death_sound, 80, 1, 1) + + if(ticker && ticker.mode) + sql_report_death(src) + ticker.mode.check_win() + + if(wearing_rig) + wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") + + // If the body is in VR, move the mind back to the real world + if(vr_holder) + src.exit_vr() + src.vr_holder.vr_link = null + for(var/obj/item/W in src) + src.drop_from_inventory(W) + + // If our mind is in VR, bring it back to the real world so it can die with its body + if(vr_link) + vr_link.exit_vr() + vr_link.vr_holder = null + vr_link = null + to_chat(src, "Everything abruptly stops.") + + return ..(gibbed,species.get_death_message(src)) + +/mob/living/carbon/human/proc/ChangeToHusk() + if(HUSK in mutations) return + + if(f_style) + f_style = "Shaved" //we only change the icon_state of the hair datum, so it doesn't mess up their UI/UE + if(h_style) + h_style = "Bald" + update_hair(0) + + mutations.Add(HUSK) + status_flags |= DISFIGURED //makes them unknown without fucking up other stuff like admintools + update_icons_body() + return + +/mob/living/carbon/human/proc/Drain() + ChangeToHusk() + mutations |= HUSK + return + +/mob/living/carbon/human/proc/ChangeToSkeleton() + if(SKELETON in src.mutations) return + + if(f_style) + f_style = "Shaved" + if(h_style) + h_style = "Bald" + update_hair(0) + + mutations.Add(SKELETON) + status_flags |= DISFIGURED + update_icons_body() + return diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index f1983b776c5..76bb592abcb 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -1,403 +1,403 @@ -var/list/_human_default_emotes = list( - /decl/emote/visible/blink, - /decl/emote/audible/synth, - /decl/emote/audible/synth/beep, - /decl/emote/audible/synth/buzz, - /decl/emote/audible/synth/confirm, - /decl/emote/audible/synth/deny, - /decl/emote/audible/synth/scary, - /decl/emote/audible/synth/dwoop, - /decl/emote/audible/synth/boop, - /decl/emote/audible/synth/robochirp, - /decl/emote/visible/nod, - /decl/emote/visible/shake, - /decl/emote/visible/shiver, - /decl/emote/visible/collapse, - /decl/emote/audible/gasp, - /decl/emote/audible/choke, - /decl/emote/audible/sneeze, - /decl/emote/audible/sniff, - /decl/emote/audible/snore, - /decl/emote/audible/whimper, - /decl/emote/audible/whistle, - /decl/emote/audible/whistle/quiet, - /decl/emote/audible/whistle/wolf, - /decl/emote/audible/whistle/summon, - /decl/emote/audible/yawn, - /decl/emote/audible/clap, - /decl/emote/audible/chuckle, - /decl/emote/audible/cough, - /decl/emote/audible/cry, - /decl/emote/audible/sigh, - /decl/emote/audible/laugh, - /decl/emote/audible/mumble, - /decl/emote/audible/grumble, - /decl/emote/audible/groan, - /decl/emote/audible/moan, - /decl/emote/audible/grunt, - /decl/emote/audible/slap, - /decl/emote/audible/crack, - /decl/emote/human/deathgasp, - /decl/emote/audible/giggle, - /decl/emote/audible/scream, - /decl/emote/visible/airguitar, - /decl/emote/visible/blink_r, - /decl/emote/visible/bow, - /decl/emote/visible/salute, - /decl/emote/visible/flap, - /decl/emote/visible/aflap, - /decl/emote/visible/drool, - /decl/emote/visible/eyebrow, - /decl/emote/visible/twitch, - /decl/emote/visible/dance, - /decl/emote/visible/twitch_v, - /decl/emote/visible/faint, - /decl/emote/visible/frown, - /decl/emote/visible/blush, - /decl/emote/visible/wave, - /decl/emote/visible/glare, - /decl/emote/visible/stare, - /decl/emote/visible/look, - /decl/emote/visible/point, - /decl/emote/visible/raise, - /decl/emote/visible/grin, - /decl/emote/visible/shrug, - /decl/emote/visible/smile, - /decl/emote/visible/pale, - /decl/emote/visible/tremble, - /decl/emote/visible/wink, - /decl/emote/visible/hug, - /decl/emote/visible/dap, - /decl/emote/visible/signal, - /decl/emote/visible/handshake, - /decl/emote/visible/afold, - /decl/emote/visible/alook, - /decl/emote/visible/eroll, - /decl/emote/visible/hbow, - /decl/emote/visible/hip, - /decl/emote/visible/holdup, - /decl/emote/visible/hshrug, - /decl/emote/visible/crub, - /decl/emote/visible/erub, - /decl/emote/visible/fslap, - /decl/emote/visible/ftap, - /decl/emote/visible/hrub, - /decl/emote/visible/hspread, - /decl/emote/visible/pocket, - /decl/emote/visible/rsalute, - /decl/emote/visible/rshoulder, - /decl/emote/visible/squint, - /decl/emote/visible/tfist, - /decl/emote/visible/tilt, - /decl/emote/visible/spin, - /decl/emote/visible/sidestep, - /decl/emote/audible/snap, - /decl/emote/visible/vomit, - /decl/emote/visible/floorspin, - /decl/emote/visible/flip, - //VOREStation Add - /decl/emote/audible/bug_hiss, - /decl/emote/audible/bug_buzz, - /decl/emote/audible/bug_chitter, - /decl/emote/audible/hiss, - /decl/emote/audible/chirp, - /decl/emote/audible/warble, - /decl/emote/audible/vox_shriek, - /decl/emote/audible/purr, - /decl/emote/audible/purrlong, - /decl/emote/audible/awoo, - /decl/emote/audible/awoo2, - /decl/emote/audible/belch, - /decl/emote/audible/growl, - /decl/emote/audible/woof, - /decl/emote/audible/woof2, - /decl/emote/audible/nya, - /decl/emote/audible/mrowl, - /decl/emote/audible/peep, - /decl/emote/audible/chirp, - /decl/emote/audible/hoot, - /decl/emote/audible/weh, - /decl/emote/audible/merp, - /decl/emote/audible/myarp, - /decl/emote/audible/bark, - /decl/emote/audible/bork, - /decl/emote/audible/mrow, - /decl/emote/audible/hypno, - /decl/emote/audible/hiss, - /decl/emote/audible/rattle, - /decl/emote/audible/squeak, - /decl/emote/audible/geck, - /decl/emote/audible/baa, - /decl/emote/audible/baa2, - /decl/emote/audible/mar, - /decl/emote/audible/wurble, - /decl/emote/audible/snort, - /decl/emote/audible/meow, - /decl/emote/audible/moo, - /decl/emote/audible/croak, - /decl/emote/audible/gao, - /decl/emote/audible/cackle, - /decl/emote/audible/squish, - /decl/emote/audible/spiderchitter, - /decl/emote/audible/spiderpurr, - /decl/emote/audible/squeaky, - /decl/emote/visible/mlem, - /decl/emote/visible/blep, - /decl/emote/helper/vwag, - /decl/emote/helper/vflap, - /decl/emote/audible/prbt, - /decl/emote/audible/gyoh, - /decl/emote/audible/rumble, - /decl/emote/audible/coyawoo, - /decl/emote/audible/coyawoo2, - /decl/emote/audible/coyawoo3, - /decl/emote/audible/coyawoo4, - /decl/emote/audible/coyawoo5, - /decl/emote/audible/fennecscream, - /decl/emote/audible/zoom, - /decl/emote/audible/mothscream, - /decl/emote/audible/mothchitter, - /decl/emote/audible/mothlaugh, - /decl/emote/audible/multichirp, - /decl/emote/audible/gnarl, - /decl/emote/audible/teshsqueak, - /decl/emote/audible/teshchirp, - /decl/emote/audible/teshtrill, - /decl/emote/audible/teshscream, - /decl/emote/visible/bounce, - /decl/emote/visible/jiggle, - /decl/emote/visible/lightup, - /decl/emote/visible/vibrate, - /decl/emote/audible/croon, - /decl/emote/audible/lwarble, - /decl/emote/audible/croak_skrell, - /decl/emote/audible/roarbark, - /decl/emote/audible/dook - - //VOREStation Add End -) - - //VOREStation Add Start - -var/list/_simple_mob_default_emotes = list( - /decl/emote/visible/blink, - /decl/emote/visible/nod, - /decl/emote/visible/shake, - /decl/emote/visible/shiver, - /decl/emote/visible/collapse, - /decl/emote/audible/gasp, - /decl/emote/audible/choke, - /decl/emote/audible/sneeze, - /decl/emote/audible/sniff, - /decl/emote/audible/snore, - /decl/emote/audible/whimper, - /decl/emote/audible/whistle, - /decl/emote/audible/whistle/quiet, - /decl/emote/audible/whistle/wolf, - /decl/emote/audible/whistle/summon, - /decl/emote/audible/yawn, - /decl/emote/audible/clap, - /decl/emote/audible/chuckle, - /decl/emote/audible/cough, - /decl/emote/audible/cry, - /decl/emote/audible/sigh, - /decl/emote/audible/laugh, - /decl/emote/audible/mumble, - /decl/emote/audible/grumble, - /decl/emote/audible/groan, - /decl/emote/audible/moan, - /decl/emote/audible/grunt, - /decl/emote/audible/slap, - /decl/emote/audible/crack, - /decl/emote/human/deathgasp, - /decl/emote/audible/giggle, - /decl/emote/audible/scream, - /decl/emote/visible/airguitar, - /decl/emote/visible/blink_r, - /decl/emote/visible/bow, - /decl/emote/visible/salute, - /decl/emote/visible/flap, - /decl/emote/visible/aflap, - /decl/emote/visible/drool, - /decl/emote/visible/eyebrow, - /decl/emote/visible/twitch, - /decl/emote/visible/dance, - /decl/emote/visible/twitch_v, - /decl/emote/visible/faint, - /decl/emote/visible/frown, - /decl/emote/visible/blush, - /decl/emote/visible/wave, - /decl/emote/visible/glare, - /decl/emote/visible/stare, - /decl/emote/visible/look, - /decl/emote/visible/point, - /decl/emote/visible/raise, - /decl/emote/visible/grin, - /decl/emote/visible/shrug, - /decl/emote/visible/smile, - /decl/emote/visible/pale, - /decl/emote/visible/tremble, - /decl/emote/visible/wink, - /decl/emote/visible/hug, - /decl/emote/visible/signal, - /decl/emote/visible/afold, - /decl/emote/visible/alook, - /decl/emote/visible/eroll, - /decl/emote/visible/hbow, - /decl/emote/visible/hip, - /decl/emote/visible/holdup, - /decl/emote/visible/hshrug, - /decl/emote/visible/crub, - /decl/emote/visible/erub, - /decl/emote/visible/fslap, - /decl/emote/visible/ftap, - /decl/emote/visible/hrub, - /decl/emote/visible/hspread, - /decl/emote/visible/rsalute, - /decl/emote/visible/rshoulder, - /decl/emote/visible/squint, - /decl/emote/visible/tfist, - /decl/emote/visible/tilt, - /decl/emote/visible/spin, - /decl/emote/visible/sidestep, - /decl/emote/visible/vomit, - /decl/emote/visible/floorspin, - /decl/emote/visible/flip, - /decl/emote/audible/awoo, - /decl/emote/audible/awoo2, - /decl/emote/audible/belch, - /decl/emote/audible/growl, - /decl/emote/audible/woof, - /decl/emote/audible/woof2, - /decl/emote/audible/nya, - /decl/emote/audible/mrowl, - /decl/emote/audible/peep, - /decl/emote/audible/chirp, - /decl/emote/audible/hoot, - /decl/emote/audible/weh, - /decl/emote/audible/merp, - /decl/emote/audible/myarp, - /decl/emote/audible/bark, - /decl/emote/audible/bork, - /decl/emote/audible/mrow, - /decl/emote/audible/hypno, - /decl/emote/audible/hiss, - /decl/emote/audible/rattle, - /decl/emote/audible/squeak, - /decl/emote/audible/geck, - /decl/emote/audible/baa, - /decl/emote/audible/baa2, - /decl/emote/audible/mar, - /decl/emote/audible/wurble, - /decl/emote/audible/snort, - /decl/emote/audible/meow, - /decl/emote/audible/moo, - /decl/emote/audible/croak, - /decl/emote/audible/gao, - /decl/emote/audible/cackle, - /decl/emote/audible/squish, - /decl/emote/audible/spiderchitter, - /decl/emote/audible/spiderpurr, - /decl/emote/audible/squeaky, - /decl/emote/visible/mlem, - /decl/emote/visible/blep, - /decl/emote/audible/prbt, - /decl/emote/audible/gyoh, - /decl/emote/audible/rumble, - /decl/emote/audible/fennecscream, - /decl/emote/audible/zoom, - /decl/emote/audible/bug_hiss, - /decl/emote/audible/bug_buzz, - /decl/emote/audible/bug_chitter, - /decl/emote/audible/hiss, - /decl/emote/audible/chirp, - /decl/emote/audible/warble, - /decl/emote/audible/vox_shriek, - /decl/emote/audible/purr, - /decl/emote/audible/purrlong, - /decl/emote/audible/dook - - ) - //VOREStation Add End - -/mob/living/carbon/human/get_available_emotes() - . = global._human_default_emotes.Copy() - if(length(species?.default_emotes)) - return . | species.default_emotes - -/mob/living/simple_mob/get_available_emotes() - . = global._simple_mob_default_emotes.Copy() - -/mob/living/carbon/human/verb/pose() - set name = "Set Pose" - set desc = "Sets a description which will be shown when someone examines you." - set category = "IC" - - var/datum/gender/T = gender_datums[get_visible_gender()] - - pose = strip_html_simple(tgui_input_text(usr, "This is [src]. [T.he]...", "Pose", null)) - -/mob/living/carbon/human/verb/set_flavor() - set name = "Set Flavour Text" - set desc = "Sets an extended description of your character's features." - set category = "IC" - - var/HTML = "" - HTML += "
                    " - HTML += "Update Flavour Text
                    " - HTML += "
                    " - HTML += "General: " - HTML += TextPreview(flavor_texts["general"]) - HTML += "
                    " - HTML += "Head: " - HTML += TextPreview(flavor_texts["head"]) - HTML += "
                    " - HTML += "Face: " - HTML += TextPreview(flavor_texts["face"]) - HTML += "
                    " - HTML += "Eyes: " - HTML += TextPreview(flavor_texts["eyes"]) - HTML += "
                    " - HTML += "Body: " - HTML += TextPreview(flavor_texts["torso"]) - HTML += "
                    " - HTML += "Arms: " - HTML += TextPreview(flavor_texts["arms"]) - HTML += "
                    " - HTML += "Hands: " - HTML += TextPreview(flavor_texts["hands"]) - HTML += "
                    " - HTML += "Legs: " - HTML += TextPreview(flavor_texts["legs"]) - HTML += "
                    " - HTML += "Feet: " - HTML += TextPreview(flavor_texts["feet"]) - HTML += "
                    " - HTML += "
                    " - HTML +="\[Done\]" - HTML += "" - src << browse(HTML, "window=flavor_changes;size=430x300") - -/mob/living/carbon/human/proc/toggle_tail(var/setting,var/message = 0) - if(!tail_style || !tail_style.ani_state) - if(message) - to_chat(src, "You don't have a tail that supports this.") - return 0 - - var/new_wagging = isnull(setting) ? !wagging : setting - if(new_wagging != wagging) - wagging = new_wagging - update_tail_showing() - return 1 - -/mob/living/carbon/human/proc/toggle_wing(var/setting,var/message = 0) - if(!wing_style || !wing_style.ani_state) - if(message) - to_chat(src, "You don't have a wingtype that supports this.") - return 0 - - var/new_flapping = isnull(setting) ? !flapping : setting - if(new_flapping != flapping) - flapping = new_flapping - update_wing_showing() - return 1 +var/list/_human_default_emotes = list( + /decl/emote/visible/blink, + /decl/emote/audible/synth, + /decl/emote/audible/synth/beep, + /decl/emote/audible/synth/buzz, + /decl/emote/audible/synth/confirm, + /decl/emote/audible/synth/deny, + /decl/emote/audible/synth/scary, + /decl/emote/audible/synth/dwoop, + /decl/emote/audible/synth/boop, + /decl/emote/audible/synth/robochirp, + /decl/emote/visible/nod, + /decl/emote/visible/shake, + /decl/emote/visible/shiver, + /decl/emote/visible/collapse, + /decl/emote/audible/gasp, + /decl/emote/audible/choke, + /decl/emote/audible/sneeze, + /decl/emote/audible/sniff, + /decl/emote/audible/snore, + /decl/emote/audible/whimper, + /decl/emote/audible/whistle, + /decl/emote/audible/whistle/quiet, + /decl/emote/audible/whistle/wolf, + /decl/emote/audible/whistle/summon, + /decl/emote/audible/yawn, + /decl/emote/audible/clap, + /decl/emote/audible/chuckle, + /decl/emote/audible/cough, + /decl/emote/audible/cry, + /decl/emote/audible/sigh, + /decl/emote/audible/laugh, + /decl/emote/audible/mumble, + /decl/emote/audible/grumble, + /decl/emote/audible/groan, + /decl/emote/audible/moan, + /decl/emote/audible/grunt, + /decl/emote/audible/slap, + /decl/emote/audible/crack, + /decl/emote/human/deathgasp, + /decl/emote/audible/giggle, + /decl/emote/audible/scream, + /decl/emote/visible/airguitar, + /decl/emote/visible/blink_r, + /decl/emote/visible/bow, + /decl/emote/visible/salute, + /decl/emote/visible/flap, + /decl/emote/visible/aflap, + /decl/emote/visible/drool, + /decl/emote/visible/eyebrow, + /decl/emote/visible/twitch, + /decl/emote/visible/dance, + /decl/emote/visible/twitch_v, + /decl/emote/visible/faint, + /decl/emote/visible/frown, + /decl/emote/visible/blush, + /decl/emote/visible/wave, + /decl/emote/visible/glare, + /decl/emote/visible/stare, + /decl/emote/visible/look, + /decl/emote/visible/point, + /decl/emote/visible/raise, + /decl/emote/visible/grin, + /decl/emote/visible/shrug, + /decl/emote/visible/smile, + /decl/emote/visible/pale, + /decl/emote/visible/tremble, + /decl/emote/visible/wink, + /decl/emote/visible/hug, + /decl/emote/visible/dap, + /decl/emote/visible/signal, + /decl/emote/visible/handshake, + /decl/emote/visible/afold, + /decl/emote/visible/alook, + /decl/emote/visible/eroll, + /decl/emote/visible/hbow, + /decl/emote/visible/hip, + /decl/emote/visible/holdup, + /decl/emote/visible/hshrug, + /decl/emote/visible/crub, + /decl/emote/visible/erub, + /decl/emote/visible/fslap, + /decl/emote/visible/ftap, + /decl/emote/visible/hrub, + /decl/emote/visible/hspread, + /decl/emote/visible/pocket, + /decl/emote/visible/rsalute, + /decl/emote/visible/rshoulder, + /decl/emote/visible/squint, + /decl/emote/visible/tfist, + /decl/emote/visible/tilt, + /decl/emote/visible/spin, + /decl/emote/visible/sidestep, + /decl/emote/audible/snap, + /decl/emote/visible/vomit, + /decl/emote/visible/floorspin, + /decl/emote/visible/flip, + //VOREStation Add + /decl/emote/audible/bug_hiss, + /decl/emote/audible/bug_buzz, + /decl/emote/audible/bug_chitter, + /decl/emote/audible/hiss, + /decl/emote/audible/chirp, + /decl/emote/audible/warble, + /decl/emote/audible/vox_shriek, + /decl/emote/audible/purr, + /decl/emote/audible/purrlong, + /decl/emote/audible/awoo, + /decl/emote/audible/awoo2, + /decl/emote/audible/belch, + /decl/emote/audible/growl, + /decl/emote/audible/woof, + /decl/emote/audible/woof2, + /decl/emote/audible/nya, + /decl/emote/audible/mrowl, + /decl/emote/audible/peep, + /decl/emote/audible/chirp, + /decl/emote/audible/hoot, + /decl/emote/audible/weh, + /decl/emote/audible/merp, + /decl/emote/audible/myarp, + /decl/emote/audible/bark, + /decl/emote/audible/bork, + /decl/emote/audible/mrow, + /decl/emote/audible/hypno, + /decl/emote/audible/hiss, + /decl/emote/audible/rattle, + /decl/emote/audible/squeak, + /decl/emote/audible/geck, + /decl/emote/audible/baa, + /decl/emote/audible/baa2, + /decl/emote/audible/mar, + /decl/emote/audible/wurble, + /decl/emote/audible/snort, + /decl/emote/audible/meow, + /decl/emote/audible/moo, + /decl/emote/audible/croak, + /decl/emote/audible/gao, + /decl/emote/audible/cackle, + /decl/emote/audible/squish, + /decl/emote/audible/spiderchitter, + /decl/emote/audible/spiderpurr, + /decl/emote/audible/squeaky, + /decl/emote/visible/mlem, + /decl/emote/visible/blep, + /decl/emote/helper/vwag, + /decl/emote/helper/vflap, + /decl/emote/audible/prbt, + /decl/emote/audible/gyoh, + /decl/emote/audible/rumble, + /decl/emote/audible/coyawoo, + /decl/emote/audible/coyawoo2, + /decl/emote/audible/coyawoo3, + /decl/emote/audible/coyawoo4, + /decl/emote/audible/coyawoo5, + /decl/emote/audible/fennecscream, + /decl/emote/audible/zoom, + /decl/emote/audible/mothscream, + /decl/emote/audible/mothchitter, + /decl/emote/audible/mothlaugh, + /decl/emote/audible/multichirp, + /decl/emote/audible/gnarl, + /decl/emote/audible/teshsqueak, + /decl/emote/audible/teshchirp, + /decl/emote/audible/teshtrill, + /decl/emote/audible/teshscream, + /decl/emote/visible/bounce, + /decl/emote/visible/jiggle, + /decl/emote/visible/lightup, + /decl/emote/visible/vibrate, + /decl/emote/audible/croon, + /decl/emote/audible/lwarble, + /decl/emote/audible/croak_skrell, + /decl/emote/audible/roarbark, + /decl/emote/audible/dook + + //VOREStation Add End +) + + //VOREStation Add Start + +var/list/_simple_mob_default_emotes = list( + /decl/emote/visible/blink, + /decl/emote/visible/nod, + /decl/emote/visible/shake, + /decl/emote/visible/shiver, + /decl/emote/visible/collapse, + /decl/emote/audible/gasp, + /decl/emote/audible/choke, + /decl/emote/audible/sneeze, + /decl/emote/audible/sniff, + /decl/emote/audible/snore, + /decl/emote/audible/whimper, + /decl/emote/audible/whistle, + /decl/emote/audible/whistle/quiet, + /decl/emote/audible/whistle/wolf, + /decl/emote/audible/whistle/summon, + /decl/emote/audible/yawn, + /decl/emote/audible/clap, + /decl/emote/audible/chuckle, + /decl/emote/audible/cough, + /decl/emote/audible/cry, + /decl/emote/audible/sigh, + /decl/emote/audible/laugh, + /decl/emote/audible/mumble, + /decl/emote/audible/grumble, + /decl/emote/audible/groan, + /decl/emote/audible/moan, + /decl/emote/audible/grunt, + /decl/emote/audible/slap, + /decl/emote/audible/crack, + /decl/emote/human/deathgasp, + /decl/emote/audible/giggle, + /decl/emote/audible/scream, + /decl/emote/visible/airguitar, + /decl/emote/visible/blink_r, + /decl/emote/visible/bow, + /decl/emote/visible/salute, + /decl/emote/visible/flap, + /decl/emote/visible/aflap, + /decl/emote/visible/drool, + /decl/emote/visible/eyebrow, + /decl/emote/visible/twitch, + /decl/emote/visible/dance, + /decl/emote/visible/twitch_v, + /decl/emote/visible/faint, + /decl/emote/visible/frown, + /decl/emote/visible/blush, + /decl/emote/visible/wave, + /decl/emote/visible/glare, + /decl/emote/visible/stare, + /decl/emote/visible/look, + /decl/emote/visible/point, + /decl/emote/visible/raise, + /decl/emote/visible/grin, + /decl/emote/visible/shrug, + /decl/emote/visible/smile, + /decl/emote/visible/pale, + /decl/emote/visible/tremble, + /decl/emote/visible/wink, + /decl/emote/visible/hug, + /decl/emote/visible/signal, + /decl/emote/visible/afold, + /decl/emote/visible/alook, + /decl/emote/visible/eroll, + /decl/emote/visible/hbow, + /decl/emote/visible/hip, + /decl/emote/visible/holdup, + /decl/emote/visible/hshrug, + /decl/emote/visible/crub, + /decl/emote/visible/erub, + /decl/emote/visible/fslap, + /decl/emote/visible/ftap, + /decl/emote/visible/hrub, + /decl/emote/visible/hspread, + /decl/emote/visible/rsalute, + /decl/emote/visible/rshoulder, + /decl/emote/visible/squint, + /decl/emote/visible/tfist, + /decl/emote/visible/tilt, + /decl/emote/visible/spin, + /decl/emote/visible/sidestep, + /decl/emote/visible/vomit, + /decl/emote/visible/floorspin, + /decl/emote/visible/flip, + /decl/emote/audible/awoo, + /decl/emote/audible/awoo2, + /decl/emote/audible/belch, + /decl/emote/audible/growl, + /decl/emote/audible/woof, + /decl/emote/audible/woof2, + /decl/emote/audible/nya, + /decl/emote/audible/mrowl, + /decl/emote/audible/peep, + /decl/emote/audible/chirp, + /decl/emote/audible/hoot, + /decl/emote/audible/weh, + /decl/emote/audible/merp, + /decl/emote/audible/myarp, + /decl/emote/audible/bark, + /decl/emote/audible/bork, + /decl/emote/audible/mrow, + /decl/emote/audible/hypno, + /decl/emote/audible/hiss, + /decl/emote/audible/rattle, + /decl/emote/audible/squeak, + /decl/emote/audible/geck, + /decl/emote/audible/baa, + /decl/emote/audible/baa2, + /decl/emote/audible/mar, + /decl/emote/audible/wurble, + /decl/emote/audible/snort, + /decl/emote/audible/meow, + /decl/emote/audible/moo, + /decl/emote/audible/croak, + /decl/emote/audible/gao, + /decl/emote/audible/cackle, + /decl/emote/audible/squish, + /decl/emote/audible/spiderchitter, + /decl/emote/audible/spiderpurr, + /decl/emote/audible/squeaky, + /decl/emote/visible/mlem, + /decl/emote/visible/blep, + /decl/emote/audible/prbt, + /decl/emote/audible/gyoh, + /decl/emote/audible/rumble, + /decl/emote/audible/fennecscream, + /decl/emote/audible/zoom, + /decl/emote/audible/bug_hiss, + /decl/emote/audible/bug_buzz, + /decl/emote/audible/bug_chitter, + /decl/emote/audible/hiss, + /decl/emote/audible/chirp, + /decl/emote/audible/warble, + /decl/emote/audible/vox_shriek, + /decl/emote/audible/purr, + /decl/emote/audible/purrlong, + /decl/emote/audible/dook + + ) + //VOREStation Add End + +/mob/living/carbon/human/get_available_emotes() + . = global._human_default_emotes.Copy() + if(length(species?.default_emotes)) + return . | species.default_emotes + +/mob/living/simple_mob/get_available_emotes() + . = global._simple_mob_default_emotes.Copy() + +/mob/living/carbon/human/verb/pose() + set name = "Set Pose" + set desc = "Sets a description which will be shown when someone examines you." + set category = "IC" + + var/datum/gender/T = gender_datums[get_visible_gender()] + + pose = strip_html_simple(tgui_input_text(usr, "This is [src]. [T.he]...", "Pose", null)) + +/mob/living/carbon/human/verb/set_flavor() + set name = "Set Flavour Text" + set desc = "Sets an extended description of your character's features." + set category = "IC" + + var/HTML = "" + HTML += "
                    " + HTML += "Update Flavour Text
                    " + HTML += "
                    " + HTML += "General: " + HTML += TextPreview(flavor_texts["general"]) + HTML += "
                    " + HTML += "Head: " + HTML += TextPreview(flavor_texts["head"]) + HTML += "
                    " + HTML += "Face: " + HTML += TextPreview(flavor_texts["face"]) + HTML += "
                    " + HTML += "Eyes: " + HTML += TextPreview(flavor_texts["eyes"]) + HTML += "
                    " + HTML += "Body: " + HTML += TextPreview(flavor_texts["torso"]) + HTML += "
                    " + HTML += "Arms: " + HTML += TextPreview(flavor_texts["arms"]) + HTML += "
                    " + HTML += "Hands: " + HTML += TextPreview(flavor_texts["hands"]) + HTML += "
                    " + HTML += "Legs: " + HTML += TextPreview(flavor_texts["legs"]) + HTML += "
                    " + HTML += "Feet: " + HTML += TextPreview(flavor_texts["feet"]) + HTML += "
                    " + HTML += "
                    " + HTML +="\[Done\]" + HTML += "" + src << browse(HTML, "window=flavor_changes;size=430x300") + +/mob/living/carbon/human/proc/toggle_tail(var/setting,var/message = 0) + if(!tail_style || !tail_style.ani_state) + if(message) + to_chat(src, "You don't have a tail that supports this.") + return 0 + + var/new_wagging = isnull(setting) ? !wagging : setting + if(new_wagging != wagging) + wagging = new_wagging + update_tail_showing() + return 1 + +/mob/living/carbon/human/proc/toggle_wing(var/setting,var/message = 0) + if(!wing_style || !wing_style.ani_state) + if(message) + to_chat(src, "You don't have a wingtype that supports this.") + return 0 + + var/new_flapping = isnull(setting) ? !flapping : setting + if(new_flapping != flapping) + flapping = new_flapping + update_wing_showing() + return 1 diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 4651baecb4a..c7f3fe1f26e 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -1,468 +1,468 @@ -/mob/living/carbon/human/examine(mob/user) - // . = ..() //Note that we don't call parent. We build the list by ourselves. - - var/skip_gear = 0 - var/skip_body = 0 - - if(alpha <= EFFECTIVE_INVIS) - return src.loc.examine(user) // Returns messages as if they examined wherever the human was - - var/looks_synth = looksSynthetic() - - //exosuits and helmets obscure our view and stuff. - if(wear_suit) - if(wear_suit.flags_inv & HIDESUITSTORAGE) - skip_gear |= EXAMINE_SKIPSUITSTORAGE - - if(wear_suit.flags_inv & HIDEJUMPSUIT) - skip_body |= EXAMINE_SKIPARMS | EXAMINE_SKIPLEGS | EXAMINE_SKIPBODY | EXAMINE_SKIPGROIN - skip_gear |= EXAMINE_SKIPJUMPSUIT | EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER - - else if(wear_suit.flags_inv & HIDETIE) - skip_gear |= EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER - - else if(wear_suit.flags_inv & HIDEHOLSTER) - skip_gear |= EXAMINE_SKIPHOLSTER - - if(wear_suit.flags_inv & HIDESHOES) - skip_gear |= EXAMINE_SKIPSHOES - skip_body |= EXAMINE_SKIPFEET - - if(wear_suit.flags_inv & HIDEGLOVES) - skip_gear |= EXAMINE_SKIPGLOVES - skip_body |= EXAMINE_SKIPHANDS - - if(w_uniform) - if(w_uniform.body_parts_covered & LEGS) - skip_body |= EXAMINE_SKIPLEGS - if(w_uniform.body_parts_covered & ARMS) - skip_body |= EXAMINE_SKIPARMS - if(w_uniform.body_parts_covered & UPPER_TORSO) - skip_body |= EXAMINE_SKIPBODY - if(w_uniform.body_parts_covered & LOWER_TORSO) - skip_body |= EXAMINE_SKIPGROIN - - if(gloves && (gloves.body_parts_covered & HANDS)) - skip_body |= EXAMINE_SKIPHANDS - - if(shoes && (shoes.body_parts_covered & FEET)) - skip_body |= EXAMINE_SKIPFEET - - if(head) - if(head.flags_inv & HIDEMASK) - skip_gear |= EXAMINE_SKIPMASK - if(head.flags_inv & HIDEEYES) - skip_gear |= EXAMINE_SKIPEYEWEAR - skip_body |= EXAMINE_SKIPEYES - if(head.flags_inv & HIDEEARS) - skip_gear |= EXAMINE_SKIPEARS - if(head.flags_inv & HIDEFACE) - skip_body |= EXAMINE_SKIPFACE - - if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) - skip_body |= EXAMINE_SKIPFACE - - //This is what hides what - var/list/hidden = list( - BP_GROIN = skip_body & EXAMINE_SKIPGROIN, - BP_TORSO = skip_body & EXAMINE_SKIPBODY, - BP_HEAD = skip_body & EXAMINE_SKIPHEAD, - BP_L_ARM = skip_body & EXAMINE_SKIPARMS, - BP_R_ARM = skip_body & EXAMINE_SKIPARMS, - BP_L_HAND= skip_body & EXAMINE_SKIPHANDS, - BP_R_HAND= skip_body & EXAMINE_SKIPHANDS, - BP_L_FOOT= skip_body & EXAMINE_SKIPFEET, - BP_R_FOOT= skip_body & EXAMINE_SKIPFEET, - BP_L_LEG = skip_body & EXAMINE_SKIPLEGS, - BP_R_LEG = skip_body & EXAMINE_SKIPLEGS) - - - var/gender_hidden = (skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE) - var/gender_key = get_visible_gender(user, gender_hidden) - var/datum/gender/T = gender_datums[gender_key] - if (!T) - CRASH({"Null gender datum on examine: mob="[src]",hidden="[gender_hidden]",key="[gender_key]",bio="[gender]",id="[identifying_gender]""}) - - var/name_ender = "" - if(!((skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE))) - //VOREStation Add Start - if(custom_species) - name_ender = ", a [src.custom_species]" - else if(looks_synth) - //VOREStation Add End - var/use_gender = "a synthetic" - if(gender == MALE) - use_gender = "an android" - else if(gender == FEMALE) - use_gender = "a gynoid" - - name_ender = ", [use_gender]![species.get_additional_examine_text(src)]" - - else if(species.name != "Human") - name_ender = ", \a [species.get_examine_name()]![species.get_additional_examine_text(src)]" - - var/list/msg = list("*---------*","This is \icon[src.examine_icon()][bicon(src)] [src.name][name_ender]") - - //uniform - if(w_uniform && !(skip_gear & EXAMINE_SKIPJUMPSUIT) && w_uniform.show_examine) - //Ties - var/tie_msg - var/tie_msg_warn - if(istype(w_uniform,/obj/item/clothing/under) && !(skip_gear & EXAMINE_SKIPTIE)) - var/obj/item/clothing/under/U = w_uniform - if(LAZYLEN(U.accessories)) - tie_msg += ". Attached to it is" - tie_msg_warn += "! Attached to it is" - var/list/accessory_descs = list() - if(skip_gear & EXAMINE_SKIPHOLSTER) - for(var/obj/item/clothing/accessory/A in U.accessories) - if(A.show_examine && !istype(A, /obj/item/clothing/accessory/holster)) // If we're supposed to skip holsters, actually skip them - accessory_descs += "\a [A]" - else - for(var/obj/item/clothing/accessory/A in U.accessories) - if(A.concealed_holster == 0 && A.show_examine) - accessory_descs += "\a [A]" - - tie_msg += " [lowertext(english_list(accessory_descs))]." - if(w_uniform.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != "#030303") ? "blood" : "oil"]-stained [w_uniform.name]![tie_msg]" - else - msg += "[T.He] [T.is] wearing [bicon(w_uniform)] \a [w_uniform].[tie_msg]" - - //head - if(head && !(skip_gear & EXAMINE_SKIPHELMET) && head.show_examine) - if(head.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(head)] [head.gender==PLURAL?"some":"a"] [(head.blood_color != "#030303") ? "blood" : "oil"]-stained [head.name] on [T.his] head!" - else - msg += "[T.He] [T.is] wearing [bicon(head)] \a [head] on [T.his] head." - - //suit/armour - if(wear_suit) - var/tie_msg - var/tie_msg_warn - if(istype(wear_suit,/obj/item/clothing/suit)) - var/obj/item/clothing/suit/U = wear_suit - if(LAZYLEN(U.accessories)) - tie_msg += ". Attached to it is" - tie_msg_warn += "! Attached to it is" - var/list/accessory_descs = list() - for(var/accessory in U.accessories) - accessory_descs += "\a [accessory]" - tie_msg += " [lowertext(english_list(accessory_descs))]." - - if(wear_suit.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_suit.name]![tie_msg]" - else - msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] \a [wear_suit].[tie_msg]" - - //suit/armour storage - if(s_store && !(skip_gear & EXAMINE_SKIPSUITSTORAGE) && s_store.show_examine) - if(s_store.blood_DNA) - msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != "#030303") ? "blood" : "oil"]-stained [s_store.name] on [T.his] [wear_suit.name]!" - else - msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] \a [s_store] on [T.his] [wear_suit.name]." - - //back - if(back && !(skip_gear & EXAMINE_SKIPBACKPACK) && back.show_examine) - if(back.blood_DNA) - msg += "[T.He] [T.has] \icon[back][bicon(back)] [back.gender==PLURAL?"some":"a"] [(back.blood_color != "#030303") ? "blood" : "oil"]-stained [back] on [T.his] back." - else - msg += "[T.He] [T.has] \icon[back][bicon(back)] \a [back] on [T.his] back." - - //left hand - if(l_hand && l_hand.show_examine) - if(l_hand.blood_DNA) - msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [l_hand.name] in [T.his] left hand!" - else - msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] \a [l_hand] in [T.his] left hand." - - //right hand - if(r_hand && r_hand.show_examine) - if(r_hand.blood_DNA) - msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [r_hand.name] in [T.his] right hand!" - else - msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] \a [r_hand] in [T.his] right hand." - - //gloves - if(gloves && !(skip_gear & EXAMINE_SKIPGLOVES) && gloves.show_examine) - if(gloves.blood_DNA) - msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != "#030303") ? "blood" : "oil"]-stained [gloves.name] on [T.his] hands!" - else - msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] \a [gloves] on [T.his] hands." - else if(blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) - msg += "[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!" - - //handcuffed? - if(handcuffed && handcuffed.show_examine) - if(istype(handcuffed, /obj/item/weapon/handcuffs/cable)) - msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] restrained with cable!" - else - msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] handcuffed!" - - //buckled - if(buckled) - msg += "[T.He] [T.is] \icon[buckled][bicon(buckled)] buckled to [buckled]!" - - //belt - if(belt && !(skip_gear & EXAMINE_SKIPBELT) && belt.show_examine) - if(belt.blood_DNA) - msg += "[T.He] [T.has] \icon[belt][bicon(belt)] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != "#030303") ? "blood" : "oil"]-stained [belt.name] about [T.his] waist!" - else - msg += "[T.He] [T.has] \icon[belt][bicon(belt)] \a [belt] about [T.his] waist." - - //shoes - if(shoes && !(skip_gear & EXAMINE_SKIPSHOES) && shoes.show_examine) - if(shoes.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != "#030303") ? "blood" : "oil"]-stained [shoes.name] on [T.his] feet!" - else - msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] \a [shoes] on [T.his] feet." - else if(feet_blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) - msg += "[T.He] [T.has] [(feet_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained feet!" - - //mask - if(wear_mask && !(skip_gear & EXAMINE_SKIPMASK) && wear_mask.show_examine) - var/descriptor = "on [T.his] face" - if(istype(wear_mask, /obj/item/weapon/grenade) && check_has_mouth()) - descriptor = "in [T.his] mouth" - - if(wear_mask.blood_DNA) - msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_mask.name] [descriptor]!" - else - msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] \a [wear_mask] [descriptor]." - - //eyes - if(glasses && !(skip_gear & EXAMINE_SKIPEYEWEAR) && glasses.show_examine) - if(glasses.blood_DNA) - msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != "#030303") ? "blood" : "oil"]-stained [glasses] covering [T.his] eyes!" - else - msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] \a [glasses] covering [T.his] eyes." - - //left ear - if(l_ear && !(skip_gear & EXAMINE_SKIPEARS) && l_ear.show_examine) - msg += "[T.He] [T.has] \icon[l_ear][bicon(l_ear)] \a [l_ear] on [T.his] left ear." - - //right ear - if(r_ear && !(skip_gear & EXAMINE_SKIPEARS) && r_ear.show_examine) - msg += "[T.He] [T.has] \icon[r_ear][bicon(r_ear)] \a [r_ear] on [T.his] right ear." - - //ID - if(wear_id && wear_id.show_examine) - msg += "[T.He] [T.is] wearing \icon[wear_id][bicon(wear_id)]\a [wear_id]." - - //Jitters - if(is_jittery) - if(jitteriness >= 300) - msg += "[T.He] [T.is] convulsing violently!" - else if(jitteriness >= 200) - msg += "[T.He] [T.is] extremely jittery." - else if(jitteriness >= 100) - msg += "[T.He] [T.is] twitching ever so slightly." - - //splints - for(var/organ in BP_ALL) - var/obj/item/organ/external/o = get_organ(organ) - if(o && o.splinted && o.splinted.loc == o) - msg += "[T.He] [T.has] \a [o.splinted] on [T.his] [o.name]!" - - if(suiciding) - msg += "[T.He] appears to have commited suicide... there is no hope of recovery." - - //VOREStation Add - var/list/vorestrings = list() - vorestrings += examine_weight() - vorestrings += examine_nutrition() - vorestrings += examine_bellies() - vorestrings += examine_pickup_size() - vorestrings += examine_step_size() - vorestrings += examine_nif() - vorestrings += examine_chimera() - for(var/entry in vorestrings) - if(entry == "" || entry == null) - vorestrings -= entry - msg += vorestrings - //VOREStation Add End - - if(mSmallsize in mutations) - msg += "[T.He] [T.is] very short!" - - if (src.stat) - msg += "[T.He] [T.is]n't responding to anything around [T.him] and seems to be asleep." - if((stat == 2 || src.losebreath) && get_dist(user, src) <= 3) - msg += "[T.He] [T.does] not appear to be breathing." - if(istype(user, /mob/living/carbon/human) && !user.stat && Adjacent(user)) - user.visible_message("[usr] checks [src]'s pulse.", "You check [src]'s pulse.") - spawn(15) - if(isobserver(user) || (Adjacent(user) && !user.stat)) // If you're a corpse then you can't exactly check their pulse, but ghosts can see anything - if(pulse == PULSE_NONE) - to_chat(user, "[T.He] [T.has] no pulse[src.client ? "" : " and [T.his] soul has departed"]...") - else - to_chat(user, "[T.He] [T.has] a pulse!") - - if(fire_stacks) - msg += "[T.He] [T.is] covered in some liquid." - if(on_fire) - msg += "[T.He] [T.is] on fire!." - - var/ssd_msg = species.get_ssd(src) - if(ssd_msg && (!should_have_organ("brain") || has_brain()) && stat != DEAD) - if(!key) - msg += "[T.He] [T.is] [ssd_msg]. It doesn't look like [T.he] [T.is] waking up anytime soon." - else if(!client) - msg += "[T.He] [T.is] [ssd_msg]." - //VOREStation Add Start - if(client && ((client.inactivity / 10) / 60 > 10)) //10 Minutes - msg += "\[Inactive for [round((client.inactivity/10)/60)] minutes\]" - else if(disconnect_time) - msg += "\[Disconnected/ghosted [round(((world.realtime - disconnect_time)/10)/60)] minutes ago\]" - //VOREStation Add End - - var/list/wound_flavor_text = list() - var/list/is_bleeding = list() - var/applying_pressure = "" - - for(var/organ_tag in species.has_limbs) - - var/list/organ_data = species.has_limbs[organ_tag] - var/organ_descriptor = organ_data["descriptor"] - - var/obj/item/organ/external/E = organs_by_name[organ_tag] - if(!E) - wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.is] missing [T.his] [organ_descriptor]." - else if(E.is_stump()) - wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.has] a stump where [T.his] [organ_descriptor] should be." - else - continue - - for(var/obj/item/organ/external/temp in organs) - if(temp) - if((temp.organ_tag in hidden) && hidden[temp.organ_tag]) - continue //Organ is hidden, don't talk about it - if(temp.status & ORGAN_DESTROYED) - wound_flavor_text["[temp.name]"] = "[T.He] [T.is] missing [T.his] [temp.name]." - continue - - if(!looks_synth && temp.robotic == ORGAN_ROBOT) - if(!(temp.brute_dam + temp.burn_dam)) - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name]." - else - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name] with [temp.get_wounds_desc()]!" - continue - else if(temp.wounds.len > 0 || temp.open) - if(temp.is_stump() && temp.parent_organ && organs_by_name[temp.parent_organ]) - var/obj/item/organ/external/parent = organs_by_name[temp.parent_organ] - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [parent.name]." - else - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [temp.name]." - else - wound_flavor_text["[temp.name]"] = "" - if(temp.dislocated == 1) //VOREStation Edit Bugfix - wound_flavor_text["[temp.name]"] += "[T.His] [temp.joint] is dislocated!" - if(temp.brute_dam > temp.min_broken_damage || (temp.status & (ORGAN_BROKEN | ORGAN_MUTATED))) - wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] is dented and swollen!" - - if(temp.germ_level > INFECTION_LEVEL_TWO && !(temp.status & ORGAN_DEAD)) - wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks very infected!" - else if(temp.status & ORGAN_DEAD) - wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks rotten!" - - if(temp.status & ORGAN_BLEEDING) - is_bleeding["[temp.name]"] += "[T.His] [temp.name] is bleeding!" - - if(temp.applied_pressure == src) - applying_pressure = "[T.He] is applying pressure to [T.his] [temp.name]." - - for(var/limb in wound_flavor_text) - var/flavor = wound_flavor_text[limb] - if(flavor) - msg += flavor - for(var/limb in is_bleeding) - var/blood = is_bleeding[limb] - if(blood) - msg += blood - for(var/implant in get_visible_implants(0)) - msg += "[src] [T.has] \a [implant] sticking out of [T.his] flesh!" - if(digitalcamo) - msg += "[T.He] [T.is] repulsively uncanny!" - - if(hasHUD(user,"security")) - var/perpname = name - var/criminal = "None" - - if(wear_id) - if(istype(wear_id, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/I = wear_id - perpname = I.registered_name - else if(istype(wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/P = wear_id - perpname = P.owner - - for (var/datum/data/record/R in data_core.security) - if(R.fields["name"] == perpname) - criminal = R.fields["criminal"] - - msg += "Criminal status: \[[criminal]\]" - msg += "Security records: \[View\] \[Add comment\]" - - if(hasHUD(user,"medical")) - var/perpname = name - var/medical = "None" - - if(wear_id) - if(istype(wear_id, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/I = wear_id - perpname = I.registered_name - else if(istype(wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/P = wear_id - perpname = P.owner - - for (var/datum/data/record/R in data_core.medical) - if (R.fields["name"] == perpname) - medical = R.fields["p_stat"] - - msg += "Physical status: \[[medical]\]" - msg += "Medical records: \[View\] \[Add comment\]" - - if(hasHUD(user,"best")) - msg += "Employment records: \[View\] \[Add comment\]" - - - var/flavor_text = print_flavor_text() - if(flavor_text) - msg += "[flavor_text]" - - // VOREStation Start - if(custom_link) - msg += "Custom link: [custom_link]" - - if(ooc_notes) - msg += "OOC Notes: \[View\] - \[Print\]" - msg += "\[Mechanical Vore Preferences\]" - // VOREStation End - msg += "*---------*" - if(applying_pressure) - msg += applying_pressure - - var/show_descs = show_descriptors_to(user) - if(show_descs) - msg += "[jointext(show_descs, "
                    ")]
                    " - - if(pose) - if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "
                    [T.He] [pose]" //
                    intentional, extra gap. - - return msg - -//Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. -/proc/hasHUD(mob/M as mob, hudtype) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(hasHUD_vr(H,hudtype)) return 1 //VOREStation Add - Added records access for certain modes of omni-hud glasses - switch(hudtype) - if("security") - return istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.glasses, /obj/item/clothing/glasses/sunglasses/sechud) - if("medical") - return istype(H.glasses, /obj/item/clothing/glasses/hud/health) - else if(istype(M, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = M - return R.sensor_type //VOREStation Add - Borgo sensors are now binary so just have them on or off +/mob/living/carbon/human/examine(mob/user) + // . = ..() //Note that we don't call parent. We build the list by ourselves. + + var/skip_gear = 0 + var/skip_body = 0 + + if(alpha <= EFFECTIVE_INVIS) + return src.loc.examine(user) // Returns messages as if they examined wherever the human was + + var/looks_synth = looksSynthetic() + + //exosuits and helmets obscure our view and stuff. + if(wear_suit) + if(wear_suit.flags_inv & HIDESUITSTORAGE) + skip_gear |= EXAMINE_SKIPSUITSTORAGE + + if(wear_suit.flags_inv & HIDEJUMPSUIT) + skip_body |= EXAMINE_SKIPARMS | EXAMINE_SKIPLEGS | EXAMINE_SKIPBODY | EXAMINE_SKIPGROIN + skip_gear |= EXAMINE_SKIPJUMPSUIT | EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER + + else if(wear_suit.flags_inv & HIDETIE) + skip_gear |= EXAMINE_SKIPTIE | EXAMINE_SKIPHOLSTER + + else if(wear_suit.flags_inv & HIDEHOLSTER) + skip_gear |= EXAMINE_SKIPHOLSTER + + if(wear_suit.flags_inv & HIDESHOES) + skip_gear |= EXAMINE_SKIPSHOES + skip_body |= EXAMINE_SKIPFEET + + if(wear_suit.flags_inv & HIDEGLOVES) + skip_gear |= EXAMINE_SKIPGLOVES + skip_body |= EXAMINE_SKIPHANDS + + if(w_uniform) + if(w_uniform.body_parts_covered & LEGS) + skip_body |= EXAMINE_SKIPLEGS + if(w_uniform.body_parts_covered & ARMS) + skip_body |= EXAMINE_SKIPARMS + if(w_uniform.body_parts_covered & UPPER_TORSO) + skip_body |= EXAMINE_SKIPBODY + if(w_uniform.body_parts_covered & LOWER_TORSO) + skip_body |= EXAMINE_SKIPGROIN + + if(gloves && (gloves.body_parts_covered & HANDS)) + skip_body |= EXAMINE_SKIPHANDS + + if(shoes && (shoes.body_parts_covered & FEET)) + skip_body |= EXAMINE_SKIPFEET + + if(head) + if(head.flags_inv & HIDEMASK) + skip_gear |= EXAMINE_SKIPMASK + if(head.flags_inv & HIDEEYES) + skip_gear |= EXAMINE_SKIPEYEWEAR + skip_body |= EXAMINE_SKIPEYES + if(head.flags_inv & HIDEEARS) + skip_gear |= EXAMINE_SKIPEARS + if(head.flags_inv & HIDEFACE) + skip_body |= EXAMINE_SKIPFACE + + if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) + skip_body |= EXAMINE_SKIPFACE + + //This is what hides what + var/list/hidden = list( + BP_GROIN = skip_body & EXAMINE_SKIPGROIN, + BP_TORSO = skip_body & EXAMINE_SKIPBODY, + BP_HEAD = skip_body & EXAMINE_SKIPHEAD, + BP_L_ARM = skip_body & EXAMINE_SKIPARMS, + BP_R_ARM = skip_body & EXAMINE_SKIPARMS, + BP_L_HAND= skip_body & EXAMINE_SKIPHANDS, + BP_R_HAND= skip_body & EXAMINE_SKIPHANDS, + BP_L_FOOT= skip_body & EXAMINE_SKIPFEET, + BP_R_FOOT= skip_body & EXAMINE_SKIPFEET, + BP_L_LEG = skip_body & EXAMINE_SKIPLEGS, + BP_R_LEG = skip_body & EXAMINE_SKIPLEGS) + + + var/gender_hidden = (skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE) + var/gender_key = get_visible_gender(user, gender_hidden) + var/datum/gender/T = gender_datums[gender_key] + if (!T) + CRASH({"Null gender datum on examine: mob="[src]",hidden="[gender_hidden]",key="[gender_key]",bio="[gender]",id="[identifying_gender]""}) + + var/name_ender = "" + if(!((skip_gear & EXAMINE_SKIPJUMPSUIT) && (skip_body & EXAMINE_SKIPFACE))) + //VOREStation Add Start + if(custom_species) + name_ender = ", a [src.custom_species]" + else if(looks_synth) + //VOREStation Add End + var/use_gender = "a synthetic" + if(gender == MALE) + use_gender = "an android" + else if(gender == FEMALE) + use_gender = "a gynoid" + + name_ender = ", [use_gender]![species.get_additional_examine_text(src)]" + + else if(species.name != "Human") + name_ender = ", \a [species.get_examine_name()]![species.get_additional_examine_text(src)]" + + var/list/msg = list("*---------*","This is \icon[src.examine_icon()][bicon(src)] [src.name][name_ender]") + + //uniform + if(w_uniform && !(skip_gear & EXAMINE_SKIPJUMPSUIT) && w_uniform.show_examine) + //Ties + var/tie_msg + var/tie_msg_warn + if(istype(w_uniform,/obj/item/clothing/under) && !(skip_gear & EXAMINE_SKIPTIE)) + var/obj/item/clothing/under/U = w_uniform + if(LAZYLEN(U.accessories)) + tie_msg += ". Attached to it is" + tie_msg_warn += "! Attached to it is" + var/list/accessory_descs = list() + if(skip_gear & EXAMINE_SKIPHOLSTER) + for(var/obj/item/clothing/accessory/A in U.accessories) + if(A.show_examine && !istype(A, /obj/item/clothing/accessory/holster)) // If we're supposed to skip holsters, actually skip them + accessory_descs += "\a [A]" + else + for(var/obj/item/clothing/accessory/A in U.accessories) + if(A.concealed_holster == 0 && A.show_examine) + accessory_descs += "\a [A]" + + tie_msg += " [lowertext(english_list(accessory_descs))]." + if(w_uniform.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != "#030303") ? "blood" : "oil"]-stained [w_uniform.name]![tie_msg]" + else + msg += "[T.He] [T.is] wearing [bicon(w_uniform)] \a [w_uniform].[tie_msg]" + + //head + if(head && !(skip_gear & EXAMINE_SKIPHELMET) && head.show_examine) + if(head.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[w_uniform][bicon(head)] [head.gender==PLURAL?"some":"a"] [(head.blood_color != "#030303") ? "blood" : "oil"]-stained [head.name] on [T.his] head!" + else + msg += "[T.He] [T.is] wearing [bicon(head)] \a [head] on [T.his] head." + + //suit/armour + if(wear_suit) + var/tie_msg + var/tie_msg_warn + if(istype(wear_suit,/obj/item/clothing/suit)) + var/obj/item/clothing/suit/U = wear_suit + if(LAZYLEN(U.accessories)) + tie_msg += ". Attached to it is" + tie_msg_warn += "! Attached to it is" + var/list/accessory_descs = list() + for(var/accessory in U.accessories) + accessory_descs += "\a [accessory]" + tie_msg += " [lowertext(english_list(accessory_descs))]." + + if(wear_suit.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_suit.name]![tie_msg]" + else + msg += "[T.He] [T.is] wearing \icon[wear_suit][bicon(wear_suit)] \a [wear_suit].[tie_msg]" + + //suit/armour storage + if(s_store && !(skip_gear & EXAMINE_SKIPSUITSTORAGE) && s_store.show_examine) + if(s_store.blood_DNA) + msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != "#030303") ? "blood" : "oil"]-stained [s_store.name] on [T.his] [wear_suit.name]!" + else + msg += "[T.He] [T.is] carrying \icon[s_store][bicon(s_store)] \a [s_store] on [T.his] [wear_suit.name]." + + //back + if(back && !(skip_gear & EXAMINE_SKIPBACKPACK) && back.show_examine) + if(back.blood_DNA) + msg += "[T.He] [T.has] \icon[back][bicon(back)] [back.gender==PLURAL?"some":"a"] [(back.blood_color != "#030303") ? "blood" : "oil"]-stained [back] on [T.his] back." + else + msg += "[T.He] [T.has] \icon[back][bicon(back)] \a [back] on [T.his] back." + + //left hand + if(l_hand && l_hand.show_examine) + if(l_hand.blood_DNA) + msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [l_hand.name] in [T.his] left hand!" + else + msg += "[T.He] [T.is] holding \icon[l_hand][bicon(l_hand)] \a [l_hand] in [T.his] left hand." + + //right hand + if(r_hand && r_hand.show_examine) + if(r_hand.blood_DNA) + msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != "#030303") ? "blood" : "oil"]-stained [r_hand.name] in [T.his] right hand!" + else + msg += "[T.He] [T.is] holding \icon[r_hand][bicon(r_hand)] \a [r_hand] in [T.his] right hand." + + //gloves + if(gloves && !(skip_gear & EXAMINE_SKIPGLOVES) && gloves.show_examine) + if(gloves.blood_DNA) + msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != "#030303") ? "blood" : "oil"]-stained [gloves.name] on [T.his] hands!" + else + msg += "[T.He] [T.has] \icon[gloves][bicon(gloves)] \a [gloves] on [T.his] hands." + else if(blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) + msg += "[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!" + + //handcuffed? + if(handcuffed && handcuffed.show_examine) + if(istype(handcuffed, /obj/item/weapon/handcuffs/cable)) + msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] restrained with cable!" + else + msg += "[T.He] [T.is] \icon[handcuffed][bicon(handcuffed)] handcuffed!" + + //buckled + if(buckled) + msg += "[T.He] [T.is] \icon[buckled][bicon(buckled)] buckled to [buckled]!" + + //belt + if(belt && !(skip_gear & EXAMINE_SKIPBELT) && belt.show_examine) + if(belt.blood_DNA) + msg += "[T.He] [T.has] \icon[belt][bicon(belt)] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != "#030303") ? "blood" : "oil"]-stained [belt.name] about [T.his] waist!" + else + msg += "[T.He] [T.has] \icon[belt][bicon(belt)] \a [belt] about [T.his] waist." + + //shoes + if(shoes && !(skip_gear & EXAMINE_SKIPSHOES) && shoes.show_examine) + if(shoes.blood_DNA) + msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != "#030303") ? "blood" : "oil"]-stained [shoes.name] on [T.his] feet!" + else + msg += "[T.He] [T.is] wearing \icon[shoes][bicon(shoes)] \a [shoes] on [T.his] feet." + else if(feet_blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) + msg += "[T.He] [T.has] [(feet_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained feet!" + + //mask + if(wear_mask && !(skip_gear & EXAMINE_SKIPMASK) && wear_mask.show_examine) + var/descriptor = "on [T.his] face" + if(istype(wear_mask, /obj/item/weapon/grenade) && check_has_mouth()) + descriptor = "in [T.his] mouth" + + if(wear_mask.blood_DNA) + msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != "#030303") ? "blood" : "oil"]-stained [wear_mask.name] [descriptor]!" + else + msg += "[T.He] [T.has] \icon[wear_mask][bicon(wear_mask)] \a [wear_mask] [descriptor]." + + //eyes + if(glasses && !(skip_gear & EXAMINE_SKIPEYEWEAR) && glasses.show_examine) + if(glasses.blood_DNA) + msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != "#030303") ? "blood" : "oil"]-stained [glasses] covering [T.his] eyes!" + else + msg += "[T.He] [T.has] \icon[glasses][bicon(glasses)] \a [glasses] covering [T.his] eyes." + + //left ear + if(l_ear && !(skip_gear & EXAMINE_SKIPEARS) && l_ear.show_examine) + msg += "[T.He] [T.has] \icon[l_ear][bicon(l_ear)] \a [l_ear] on [T.his] left ear." + + //right ear + if(r_ear && !(skip_gear & EXAMINE_SKIPEARS) && r_ear.show_examine) + msg += "[T.He] [T.has] \icon[r_ear][bicon(r_ear)] \a [r_ear] on [T.his] right ear." + + //ID + if(wear_id && wear_id.show_examine) + msg += "[T.He] [T.is] wearing \icon[wear_id][bicon(wear_id)]\a [wear_id]." + + //Jitters + if(is_jittery) + if(jitteriness >= 300) + msg += "[T.He] [T.is] convulsing violently!" + else if(jitteriness >= 200) + msg += "[T.He] [T.is] extremely jittery." + else if(jitteriness >= 100) + msg += "[T.He] [T.is] twitching ever so slightly." + + //splints + for(var/organ in BP_ALL) + var/obj/item/organ/external/o = get_organ(organ) + if(o && o.splinted && o.splinted.loc == o) + msg += "[T.He] [T.has] \a [o.splinted] on [T.his] [o.name]!" + + if(suiciding) + msg += "[T.He] appears to have commited suicide... there is no hope of recovery." + + //VOREStation Add + var/list/vorestrings = list() + vorestrings += examine_weight() + vorestrings += examine_nutrition() + vorestrings += examine_bellies() + vorestrings += examine_pickup_size() + vorestrings += examine_step_size() + vorestrings += examine_nif() + vorestrings += examine_chimera() + for(var/entry in vorestrings) + if(entry == "" || entry == null) + vorestrings -= entry + msg += vorestrings + //VOREStation Add End + + if(mSmallsize in mutations) + msg += "[T.He] [T.is] very short!" + + if (src.stat) + msg += "[T.He] [T.is]n't responding to anything around [T.him] and seems to be asleep." + if((stat == 2 || src.losebreath) && get_dist(user, src) <= 3) + msg += "[T.He] [T.does] not appear to be breathing." + if(istype(user, /mob/living/carbon/human) && !user.stat && Adjacent(user)) + user.visible_message("[usr] checks [src]'s pulse.", "You check [src]'s pulse.") + spawn(15) + if(isobserver(user) || (Adjacent(user) && !user.stat)) // If you're a corpse then you can't exactly check their pulse, but ghosts can see anything + if(pulse == PULSE_NONE) + to_chat(user, "[T.He] [T.has] no pulse[src.client ? "" : " and [T.his] soul has departed"]...") + else + to_chat(user, "[T.He] [T.has] a pulse!") + + if(fire_stacks) + msg += "[T.He] [T.is] covered in some liquid." + if(on_fire) + msg += "[T.He] [T.is] on fire!." + + var/ssd_msg = species.get_ssd(src) + if(ssd_msg && (!should_have_organ("brain") || has_brain()) && stat != DEAD) + if(!key) + msg += "[T.He] [T.is] [ssd_msg]. It doesn't look like [T.he] [T.is] waking up anytime soon." + else if(!client) + msg += "[T.He] [T.is] [ssd_msg]." + //VOREStation Add Start + if(client && ((client.inactivity / 10) / 60 > 10)) //10 Minutes + msg += "\[Inactive for [round((client.inactivity/10)/60)] minutes\]" + else if(disconnect_time) + msg += "\[Disconnected/ghosted [round(((world.realtime - disconnect_time)/10)/60)] minutes ago\]" + //VOREStation Add End + + var/list/wound_flavor_text = list() + var/list/is_bleeding = list() + var/applying_pressure = "" + + for(var/organ_tag in species.has_limbs) + + var/list/organ_data = species.has_limbs[organ_tag] + var/organ_descriptor = organ_data["descriptor"] + + var/obj/item/organ/external/E = organs_by_name[organ_tag] + if(!E) + wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.is] missing [T.his] [organ_descriptor]." + else if(E.is_stump()) + wound_flavor_text["[organ_descriptor]"] = "[T.He] [T.has] a stump where [T.his] [organ_descriptor] should be." + else + continue + + for(var/obj/item/organ/external/temp in organs) + if(temp) + if((temp.organ_tag in hidden) && hidden[temp.organ_tag]) + continue //Organ is hidden, don't talk about it + if(temp.status & ORGAN_DESTROYED) + wound_flavor_text["[temp.name]"] = "[T.He] [T.is] missing [T.his] [temp.name]." + continue + + if(!looks_synth && temp.robotic == ORGAN_ROBOT) + if(!(temp.brute_dam + temp.burn_dam)) + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name]." + else + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name] with [temp.get_wounds_desc()]!" + continue + else if(temp.wounds.len > 0 || temp.open) + if(temp.is_stump() && temp.parent_organ && organs_by_name[temp.parent_organ]) + var/obj/item/organ/external/parent = organs_by_name[temp.parent_organ] + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [parent.name]." + else + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] [temp.get_wounds_desc()] on [T.his] [temp.name]." + else + wound_flavor_text["[temp.name]"] = "" + if(temp.dislocated == 1) //VOREStation Edit Bugfix + wound_flavor_text["[temp.name]"] += "[T.His] [temp.joint] is dislocated!" + if(temp.brute_dam > temp.min_broken_damage || (temp.status & (ORGAN_BROKEN | ORGAN_MUTATED))) + wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] is dented and swollen!" + + if(temp.germ_level > INFECTION_LEVEL_TWO && !(temp.status & ORGAN_DEAD)) + wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks very infected!" + else if(temp.status & ORGAN_DEAD) + wound_flavor_text["[temp.name]"] += "[T.His] [temp.name] looks rotten!" + + if(temp.status & ORGAN_BLEEDING) + is_bleeding["[temp.name]"] += "[T.His] [temp.name] is bleeding!" + + if(temp.applied_pressure == src) + applying_pressure = "[T.He] is applying pressure to [T.his] [temp.name]." + + for(var/limb in wound_flavor_text) + var/flavor = wound_flavor_text[limb] + if(flavor) + msg += flavor + for(var/limb in is_bleeding) + var/blood = is_bleeding[limb] + if(blood) + msg += blood + for(var/implant in get_visible_implants(0)) + msg += "[src] [T.has] \a [implant] sticking out of [T.his] flesh!" + if(digitalcamo) + msg += "[T.He] [T.is] repulsively uncanny!" + + if(hasHUD(user,"security")) + var/perpname = name + var/criminal = "None" + + if(wear_id) + if(istype(wear_id, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/I = wear_id + perpname = I.registered_name + else if(istype(wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/P = wear_id + perpname = P.owner + + for (var/datum/data/record/R in data_core.security) + if(R.fields["name"] == perpname) + criminal = R.fields["criminal"] + + msg += "Criminal status: \[[criminal]\]" + msg += "Security records: \[View\] \[Add comment\]" + + if(hasHUD(user,"medical")) + var/perpname = name + var/medical = "None" + + if(wear_id) + if(istype(wear_id, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/I = wear_id + perpname = I.registered_name + else if(istype(wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/P = wear_id + perpname = P.owner + + for (var/datum/data/record/R in data_core.medical) + if (R.fields["name"] == perpname) + medical = R.fields["p_stat"] + + msg += "Physical status: \[[medical]\]" + msg += "Medical records: \[View\] \[Add comment\]" + + if(hasHUD(user,"best")) + msg += "Employment records: \[View\] \[Add comment\]" + + + var/flavor_text = print_flavor_text() + if(flavor_text) + msg += "[flavor_text]" + + // VOREStation Start + if(custom_link) + msg += "Custom link: [custom_link]" + + if(ooc_notes) + msg += "OOC Notes: \[View\] - \[Print\]" + msg += "\[Mechanical Vore Preferences\]" + // VOREStation End + msg += "*---------*" + if(applying_pressure) + msg += applying_pressure + + var/show_descs = show_descriptors_to(user) + if(show_descs) + msg += "[jointext(show_descs, "
                    ")]
                    " + + if(pose) + if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "
                    [T.He] [pose]" //
                    intentional, extra gap. + + return msg + +//Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. +/proc/hasHUD(mob/M as mob, hudtype) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(hasHUD_vr(H,hudtype)) return 1 //VOREStation Add - Added records access for certain modes of omni-hud glasses + switch(hudtype) + if("security") + return istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.glasses, /obj/item/clothing/glasses/sunglasses/sechud) + if("medical") + return istype(H.glasses, /obj/item/clothing/glasses/hud/health) + else if(istype(M, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = M + return R.sensor_type //VOREStation Add - Borgo sensors are now binary so just have them on or off diff --git a/code/modules/mob/living/carbon/human/examine_vr.dm b/code/modules/mob/living/carbon/human/examine_vr.dm index 3ac7698e632..e1d52728f98 100644 --- a/code/modules/mob/living/carbon/human/examine_vr.dm +++ b/code/modules/mob/living/carbon/human/examine_vr.dm @@ -87,13 +87,13 @@ /mob/living/carbon/human/proc/examine_pickup_size(mob/living/H) var/message = "" if(istype(H) && (H.get_effective_size(FALSE) - src.get_effective_size(TRUE)) >= 0.50) - message = "They are small enough that you could easily pick them up!" + message = span_blue("They are small enough that you could easily pick them up!") return message /mob/living/carbon/human/proc/examine_step_size(mob/living/H) var/message = "" if(istype(H) && (H.get_effective_size(FALSE) - src.get_effective_size(TRUE)) >= 0.75) - message = "They are small enough that you could easily trample them!" + message = span_red("They are small enough that you could easily trample them!") return message /mob/living/carbon/human/proc/examine_nif(mob/living/carbon/human/H) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 448c9c32d58..308d2479ef0 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1,1764 +1,1766 @@ -/mob/living/carbon/human - name = "unknown" - real_name = "unknown" - voice_name = "unknown" - icon = 'icons/effects/effects.dmi' //We have an ultra-complex update icons that overlays everything, don't load some stupid random male human - icon_state = "nothing" - - has_huds = TRUE //We do have HUDs (like health, wanted, status, not inventory slots) - - var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us. - var/obj/item/weapon/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. - var/last_push_time //For human_attackhand.dm, keeps track of the last use of disarm - - var/spitting = 0 //Spitting and spitting related things. Any human based ranged attacks, be it innate or added abilities. - var/spit_projectile = null //Projectile type. - var/spit_name = null //String - var/last_spit = 0 //Timestamp. - - var/can_defib = 1 //Horrible damage (like beheadings) will prevent defibbing organics. - var/active_regen = FALSE //Used for the regenerate proc in human_powers.dm - var/active_regen_delay = 300 - -/mob/living/carbon/human/Initialize(mapload, var/new_species = null) - if(!dna) - dna = new /datum/dna(null) - // Species name is handled by set_species() - - if(!species) - if(new_species) - set_species(new_species,1) - else - set_species() - - if(species) - real_name = species.get_random_name(gender) - name = real_name - if(mind) - mind.name = real_name - - nutrition = rand(200,400) - - human_mob_list |= src - - . = ..() - - hide_underwear.Cut() - for(var/category in global_underwear.categories_by_name) - hide_underwear[category] = FALSE - - if(dna) - dna.ready_dna(src) - dna.real_name = real_name - sync_organ_dna() - - //verbs |= /mob/living/proc/toggle_selfsurgery //VOREStation Removal - AddComponent(/datum/component/personal_crafting) - -/mob/living/carbon/human/Destroy() - human_mob_list -= src - for(var/organ in organs) - qdel(organ) - QDEL_NULL(nif) //VOREStation Add - worn_clothing.Cut() - return ..() - -/mob/living/carbon/human/Stat() - ..() - if(statpanel("Status")) - stat("Intent:", "[a_intent]") - stat("Move Mode:", "[m_intent]") - if(emergency_shuttle) - var/eta_status = emergency_shuttle.get_status_panel_eta() - if(eta_status) - stat(null, eta_status) - - if (internal) - if (!internal.air_contents) - qdel(internal) - else - stat("Internal Atmosphere Info", internal.name) - stat("Tank Pressure", internal.air_contents.return_pressure()) - stat("Distribution Pressure", internal.distribute_pressure) - - var/obj/item/organ/internal/xenos/plasmavessel/P = internal_organs_by_name[O_PLASMA] //Xenomorphs. Mech. - if(P) - stat(null, "Phoron Stored: [P.stored_plasma]/[P.max_plasma]") - - - if(back && istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/suit = back - var/cell_status = "ERROR" - if(suit.cell) cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" - stat(null, "Suit charge: [cell_status]") - - if(mind) - if(mind.changeling) - stat("Chemical Storage", mind.changeling.chem_charges) - stat("Genetic Damage Time", mind.changeling.geneticdamage) - stat("Re-Adaptations", "[mind.changeling.readapts]/[mind.changeling.max_readapts]") - if(species) - species.Stat(src) - -/mob/living/carbon/human/ex_act(severity) - if(!blinded) - flash_eyes() - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.explosion_modifier)) - severity = CLAMP(severity + M.explosion_modifier, 1, 4) - - severity = round(severity) - - if(severity > 3) - return - - var/shielded = 0 - var/b_loss = null - var/f_loss = null - switch (severity) - if (1.0) - b_loss += 500 - if (!prob(getarmor(null, "bomb"))) - gib() - return - else - var/atom/target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src))) - throw_at(target, 200, 4) - //return -// var/atom/target = get_edge_target_turf(user, get_dir(src, get_step_away(user, src))) - //user.throw_at(target, 200, 4) - - if (2.0) - if (!shielded) - b_loss += 60 - - f_loss += 60 - - if (prob(getarmor(null, "bomb"))) - b_loss = b_loss/1.5 - f_loss = f_loss/1.5 - - if (!get_ear_protection() >= 2) - ear_damage += 30 - ear_deaf += 120 - if (prob(70) && !shielded) - Paralyse(10) - - if(3.0) - b_loss += 30 - if (prob(getarmor(null, "bomb"))) - b_loss = b_loss/2 - if (!get_ear_protection() >= 2) - ear_damage += 15 - ear_deaf += 60 - if (prob(50) && !shielded) - Paralyse(10) - - var/blastsoak = getsoak(null, "bomb") - - b_loss = max(1, b_loss - blastsoak) - f_loss = max(1, f_loss - blastsoak) - - var/update = 0 - - // focus most of the blast on one organ - var/obj/item/organ/external/take_blast = pick(organs) - update |= take_blast.take_damage(b_loss * 0.9, f_loss * 0.9, used_weapon = "Explosive blast") - - // distribute the remaining 10% on all limbs equally - b_loss *= 0.1 - f_loss *= 0.1 - - var/weapon_message = "Explosive Blast" - - for(var/obj/item/organ/external/temp in organs) - switch(temp.organ_tag) - if(BP_HEAD) - update |= temp.take_damage(b_loss * 0.2, f_loss * 0.2, used_weapon = weapon_message) - if(BP_TORSO) - update |= temp.take_damage(b_loss * 0.4, f_loss * 0.4, used_weapon = weapon_message) - else - update |= temp.take_damage(b_loss * 0.05, f_loss * 0.05, used_weapon = weapon_message) - if(update) UpdateDamageIcon() - -/mob/living/carbon/human/proc/implant_loyalty(override = FALSE) // Won't override by default. - if(!config.use_loyalty_implants && !override) return // Nuh-uh. - - var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(src) - if(L.handle_implant(src, BP_HEAD)) - L.post_implant(src) - -/mob/living/carbon/human/proc/is_loyalty_implanted() - for(var/L in src.contents) - if(istype(L, /obj/item/weapon/implant/loyalty)) - for(var/obj/item/organ/external/O in src.organs) - if(L in O.implants) - return 1 - return 0 - -/mob/living/carbon/human/restrained() - if (handcuffed) - return 1 - if (istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) - return 1 - return 0 - -/mob/living/carbon/human/var/co2overloadtime = null -/mob/living/carbon/human/var/temperature_resistance = T0C+75 - -// called when something steps onto a human -// this handles mobs on fire - mulebot and vehicle code has been relocated to /mob/living/Crossed() -/mob/living/carbon/human/Crossed(var/atom/movable/AM) - if(AM.is_incorporeal()) - return - - spread_fire(AM) - - ..() // call parent because we moved behavior to parent - -// Get rank from ID, ID inside PDA, PDA, ID in wallet, etc. -/mob/living/carbon/human/proc/get_authentification_rank(var/if_no_id = "No id", var/if_no_job = "No job") - var/obj/item/device/pda/pda = wear_id - if (istype(pda)) - if (pda.id) - return pda.id.rank ? pda.id.rank : if_no_job - else - return pda.ownrank ? pda.ownrank : if_no_job - else - var/obj/item/weapon/card/id/id = get_idcard() - if(id) - return id.rank ? id.rank : if_no_job - else - return if_no_id - -//gets assignment from ID or ID inside PDA or PDA itself -//Useful when player do something with computers -/mob/living/carbon/human/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") - var/obj/item/device/pda/pda = wear_id - if (istype(pda)) - if (pda.id) - return pda.id.assignment - else - return pda.ownjob ? pda.ownjob : if_no_job - else - var/obj/item/weapon/card/id/id = get_idcard() - if(id) - return id.assignment ? id.assignment : if_no_job - else - return if_no_id - -//gets name from ID or ID inside PDA or PDA itself -//Useful when player do something with computers -/mob/living/carbon/human/proc/get_authentification_name(var/if_no_id = "Unknown") - var/obj/item/device/pda/pda = wear_id - if (istype(pda)) - if (pda.id) - return pda.id.registered_name - else - return pda.owner ? pda.owner : if_no_id - else - var/obj/item/weapon/card/id/id = get_idcard() - if(id) - return id.registered_name - else - return if_no_id - -//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere -/mob/living/carbon/human/proc/get_visible_name() - if(ability_flags & AB_PHASE_SHIFTED) - return "Something" // Something - if( wear_mask && (wear_mask.flags_inv&HIDEFACE) ) //Wearing a mask which hides our face, use id-name if possible - return get_id_name("Unknown") - if( head && (head.flags_inv&HIDEFACE) ) - return get_id_name("Unknown") //Likewise for hats - var/face_name = get_face_name() - var/id_name = get_id_name("") - if((face_name == "Unknown") && id_name && (id_name != face_name)) - return "[face_name] (as [id_name])" - return face_name - -//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when polyacided or when updating a human's name variable -/mob/living/carbon/human/proc/get_face_name() - var/obj/item/organ/external/head = get_organ(BP_HEAD) - if(!head || head.disfigured || head.is_stump() || !real_name || (HUSK in mutations) ) //disfigured. use id-name if possible - return "Unknown" - return real_name - -//gets name from ID or PDA itself, ID inside PDA doesn't matter -//Useful when player is being seen by other mobs -/mob/living/carbon/human/proc/get_id_name(var/if_no_id = "Unknown") - . = if_no_id - if(istype(wear_id,/obj/item/device/pda)) - var/obj/item/device/pda/P = wear_id - return P.owner ? P.owner : if_no_id - if(wear_id) - var/obj/item/weapon/card/id/I = wear_id.GetID() - if(I) - return I.registered_name - return - -//gets ID card object from special clothes slot or null. -/mob/living/carbon/human/proc/get_idcard() - if(wear_id) - return wear_id.GetID() - -//Removed the horrible safety parameter. It was only being used by ninja code anyways. -//Now checks siemens_coefficient of the affected area by default -/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null) - - if(status_flags & GODMODE) return 0 //godmode - - if (!def_zone) - def_zone = pick("l_hand", "r_hand") - - if(species.siemens_coefficient == -1) - if(stored_shock_by_ref["\ref[src]"]) - stored_shock_by_ref["\ref[src]"] += shock_damage - else - stored_shock_by_ref["\ref[src]"] = shock_damage - return - - var/obj/item/organ/external/affected_organ = get_organ(check_zone(def_zone)) - var/siemens_coeff = base_siemens_coeff * get_siemens_coefficient_organ(affected_organ) - if(fire_stacks < 0) // Water makes you more conductive. - siemens_coeff *= 1.5 - - return ..(shock_damage, source, siemens_coeff, def_zone) - - -/mob/living/carbon/human/Topic(href, href_list) - if (href_list["mach_close"]) // This is horrible. - var/t1 = text("window=[]", href_list["mach_close"]) - unset_machine() - src << browse(null, t1) - - if(href_list["item"]) - log_runtime(EXCEPTION("Warning: human/Topic was called with item [href_list["item"]], but the item Topic is deprecated!")) - // handle_strip(href_list["item"],usr) - - if (href_list["criminal"]) - if(hasHUD(usr,"security")) - - var/modified = 0 - var/perpname = "wot" - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - - if(perpname) - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if (R.fields["id"] == E.fields["id"]) - - var/setcriminal = tgui_input_list(usr, "Specify a new criminal status for this person.", "Security HUD", list("None", "*Arrest*", "Incarcerated", "Parolled", "Released", "Cancel")) - - if(hasHUD(usr, "security")) - if(setcriminal != "Cancel") - R.fields["criminal"] = setcriminal - modified = 1 - - spawn() - BITSET(hud_updateflag, WANTED_HUD) - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/U = usr - U.handle_hud_list() - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/U = usr - U.handle_regular_hud_updates() - - if(!modified) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["secrecord"]) - if(hasHUD(usr,"security")) - var/perpname = "wot" - var/read = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - var/list/security_hud_text = list() - security_hud_text += "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]" - security_hud_text += "Species: [R.fields["species"]]" - security_hud_text += "Minor Crimes: [R.fields["mi_crim"]]" - security_hud_text += "Details: [R.fields["mi_crim_d"]]" - security_hud_text += "Major Crimes: [R.fields["ma_crim"]]" - security_hud_text += "Details: [R.fields["ma_crim_d"]]" - security_hud_text += "Notes: [R.fields["notes"]]" - security_hud_text += "\[View Comment Log\]" - to_chat(usr, "[jointext(security_hud_text, "
                    ")]
                    ") - read = 1 - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["secrecordComment"]) - if(hasHUD(usr,"security")) - var/perpname = "wot" - var/read = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - read = 1 - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - to_chat(usr, "[R.fields[text("com_[]", counter)]]") - counter++ - if (counter == 1) - to_chat(usr, "No comment found.") - to_chat(usr, "\[Add comment\]") - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["secrecordadd"]) - if(hasHUD(usr,"security")) - var/perpname = "wot" - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - var/t1 = sanitize(tgui_input_text(usr, "Add Comment:", "Sec. records", null, null, multiline = TRUE, prevent_enter = TRUE)) - if ( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"security")) ) - return - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - counter++ - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") - - if (href_list["medical"]) - if(hasHUD(usr,"medical")) - var/perpname = "wot" - var/modified = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.general) - if (R.fields["id"] == E.fields["id"]) - - var/setmedical = tgui_input_list(usr, "Specify a new medical status for this person.", "Medical HUD", list("*SSD*", "*Deceased*", "Physically Unfit", "Active", "Disabled", "Cancel")) - - if(hasHUD(usr,"medical")) - if(setmedical != "Cancel") - R.fields["p_stat"] = setmedical - modified = 1 - if(PDA_Manifest.len) - PDA_Manifest.Cut() - - spawn() - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/U = usr - U.handle_regular_hud_updates() - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/U = usr - U.handle_regular_hud_updates() - - if(!modified) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["medrecord"]) - if(hasHUD(usr,"medical")) - var/perpname = "wot" - var/read = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.medical) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - var/list/medical_hud_text = list() - medical_hud_text += "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]] Blood Basis: [R.fields["blood_reagent"]]" - medical_hud_text += "Species: [R.fields["species"]]" - medical_hud_text += "DNA: [R.fields["b_dna"]]" - medical_hud_text += "Minor Disabilities: [R.fields["mi_dis"]]" - medical_hud_text += "Details: [R.fields["mi_dis_d"]]" - medical_hud_text += "Major Disabilities: [R.fields["ma_dis"]]" - medical_hud_text += "Details: [R.fields["ma_dis_d"]]" - medical_hud_text += "Notes: [R.fields["notes"]]" - medical_hud_text += "\[View Comment Log\]" - to_chat(usr, "[jointext(medical_hud_text, "
                    ")]
                    ") - read = 1 - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["medrecordComment"]) - if(hasHUD(usr,"medical")) - var/perpname = "wot" - var/read = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.medical) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - read = 1 - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - to_chat(usr, "[R.fields[text("com_[]", counter)]]") - counter++ - if (counter == 1) - to_chat(usr, "No comment found.") - to_chat(usr, "\[Add comment\]") - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["medrecordadd"]) - if(hasHUD(usr,"medical")) - var/perpname = "wot" - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.medical) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - var/t1 = sanitize(tgui_input_text(usr, "Add Comment:", "Med. records", null, null, multiline = TRUE, prevent_enter = TRUE)) - if ( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"medical")) ) - return - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - counter++ - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") - - if (href_list["emprecord"]) - if(hasHUD(usr,"best")) - var/perpname = "wot" - var/read = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.general) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"best")) - var/list/emp_hud_text = list() - emp_hud_text += "Name: [R.fields["name"]]" - emp_hud_text += "Species: [R.fields["species"]]" - emp_hud_text += "Assignment: [R.fields["real_rank"]] ([R.fields["rank"]])" - emp_hud_text += "Home System: [R.fields["home_system"]]" - emp_hud_text += "Birthplace: [R.fields["birthplace"]]" - emp_hud_text += "Citizenship: [R.fields["citizenship"]]" - emp_hud_text += "Primary Employer: [R.fields["personal_faction"]]" - emp_hud_text += "Religious Beliefs: [R.fields["religion"]]" - emp_hud_text += "Known Languages: [R.fields["languages"]]" - emp_hud_text += "Notes: [R.fields["notes"]]" - emp_hud_text += "\[View Comment Log\]" - to_chat(usr, "[jointext(emp_hud_text, "
                    ")]
                    ") - read = 1 - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["emprecordComment"]) - if(hasHUD(usr,"best")) - var/perpname = "wot" - var/read = 0 - - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.general) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"best")) - read = 1 - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - to_chat(usr, "[R.fields[text("com_[]", counter)]]") - counter++ - if (counter == 1) - to_chat(usr, "No comment found.") - to_chat(usr, "\[Add comment\]") - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if (href_list["emprecordadd"]) - if(hasHUD(usr,"best")) - var/perpname = "wot" - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - for (var/datum/data/record/E in data_core.general) - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.general) - if (R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"best")) - var/t1 = sanitize(tgui_input_text(usr, "Add Comment:", "Emp. records", null, null, multiline = TRUE, prevent_enter = TRUE)) - if ( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"best")) ) - return - var/counter = 1 - while(R.fields[text("com_[]", counter)]) - counter++ - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") - if(istype(usr,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/U = usr - R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") - - if (href_list["lookitem"]) - var/obj/item/I = locate(href_list["lookitem"]) - src.examinate(I) - - if (href_list["lookitem_desc_only"]) - var/obj/item/I = locate(href_list["lookitem_desc_only"]) - if(!I) - return - usr.examinate(I, 1) - - if (href_list["lookmob"]) - var/mob/M = locate(href_list["lookmob"]) - src.examinate(M) - - if (href_list["clickitem"]) - var/obj/item/I = locate(href_list["clickitem"]) - if(src.client) - src.ClickOn(I) - - if (href_list["flavor_change"]) - switch(href_list["flavor_change"]) - if("done") - src << browse(null, "window=flavor_changes") - return - if("general") - var/msg = strip_html_simple(tgui_input_text(usr,"Update the general description of your character. This will be shown regardless of clothing.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes - if(msg) - flavor_texts[href_list["flavor_change"]] = msg - set_flavor() - return - else - var/msg = strip_html_simple(tgui_input_text(usr,"Update the flavor text for your [href_list["flavor_change"]].","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]]), multiline = TRUE, prevent_enter = TRUE)) - if(msg) - flavor_texts[href_list["flavor_change"]] = msg - set_flavor() - return - ..() - return - -///eyecheck() -///Returns a number between -1 to 2 -/mob/living/carbon/human/eyecheck() - - var/obj/item/organ/internal/eyes/I - - if(internal_organs_by_name[O_EYES]) // Eyes are fucked, not a 'weak point'. - I = internal_organs_by_name[O_EYES] - if(I.is_broken()) - return FLASH_PROTECTION_MAJOR - else if(!species.dispersed_eyes) // They can't be flashed if they don't have eyes, or widespread sensing surfaces. - return FLASH_PROTECTION_MAJOR - - var/number = get_equipment_flash_protection() - if(I) - number = I.get_total_protection(number) - I.additional_flash_effects(number) - return number - -/mob/living/carbon/human/flash_eyes(var/intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, visual = FALSE, type = /obj/screen/fullscreen/flash) - if(internal_organs_by_name[O_EYES]) // Eyes are fucked, not a 'weak point'. - var/obj/item/organ/internal/eyes/I = internal_organs_by_name[O_EYES] - I.additional_flash_effects(intensity) - return ..() - -#define add_clothing_protection(A) \ - var/obj/item/clothing/C = A; \ - flash_protection += C.flash_protection; \ - -/mob/living/carbon/human/proc/get_equipment_flash_protection() - var/flash_protection = 0 - - if(istype(src.head, /obj/item/clothing/head)) - add_clothing_protection(head) - if(istype(src.glasses, /obj/item/clothing/glasses)) - add_clothing_protection(glasses) - if(istype(src.wear_mask, /obj/item/clothing/mask)) - add_clothing_protection(wear_mask) - - return flash_protection - -#undef add_clothing_protection - -//Used by various things that knock people out by applying blunt trauma to the head. -//Checks that the species has a "head" (brain containing organ) and that hit_zone refers to it. -/mob/living/carbon/human/proc/headcheck(var/target_zone, var/brain_tag = "brain") - - var/obj/item/organ/affecting = internal_organs_by_name[brain_tag] - - target_zone = check_zone(target_zone) - if(!affecting || affecting.parent_organ != target_zone) - return 0 - - //if the parent organ is significantly larger than the brain organ, then hitting it is not guaranteed - var/obj/item/organ/parent = get_organ(target_zone) - if(!parent) - return 0 - - if(parent.w_class > affecting.w_class + 1) - return prob(100 / 2**(parent.w_class - affecting.w_class - 1)) - - return 1 - -/mob/living/carbon/human/IsAdvancedToolUser(var/silent) - // VOREstation start - if(feral) - to_chat(src, "Your primitive mind can't grasp the concept of that thing.") - return 0 - // VOREstation end - if(species.has_fine_manipulation) - return 1 - if(!silent) - to_chat(src, "You don't have the dexterity to use that!") - return 0 - -/mob/living/carbon/human/abiotic(var/full_body = 0) - if(full_body && ((src.l_hand && !( src.l_hand.abstract )) || (src.r_hand && !( src.r_hand.abstract )) || (src.back || src.wear_mask || src.head || src.shoes || src.w_uniform || src.wear_suit || src.glasses || src.l_ear || src.r_ear || src.gloves))) - return 1 - - if( (src.l_hand && !src.l_hand.abstract) || (src.r_hand && !src.r_hand.abstract) ) - return 1 - - return 0 - - -/mob/living/carbon/human/proc/check_dna() - dna.check_integrity(src) - return - -/mob/living/carbon/human/get_species() - if(!species) - set_species() - return species.name - -/mob/living/carbon/human/proc/play_xylophone() - if(!src.xylophone) - var/datum/gender/T = gender_datums[get_visible_gender()] - visible_message("\The [src] begins playing [T.his] ribcage like a xylophone. It's quite spooky.","You begin to play a spooky refrain on your ribcage.","You hear a spooky xylophone melody.") - var/song = pick('sound/effects/xylophone1.ogg','sound/effects/xylophone2.ogg','sound/effects/xylophone3.ogg') - playsound(src, song, 50, 1, -1) - xylophone = 1 - spawn(1200) - xylophone=0 - return - -/mob/living/proc/check_has_mouth() - return 1 - -/mob/living/carbon/human/check_has_mouth() - // Todo, check stomach organ when implemented. - var/obj/item/organ/external/head/H = get_organ(BP_HEAD) - if(!H || !H.can_intake_reagents) - return 0 - return 1 - -/mob/living/carbon/human/proc/morph() - set name = "Morph" - set category = "Superpower" - - if(stat!=CONSCIOUS) - reset_view(0) - remoteview_target = null - return - - if(!(mMorph in mutations)) - src.verbs -= /mob/living/carbon/human/proc/morph - return - - var/new_facial = input(usr, "Please select facial hair color.", "Character Generation",rgb(r_facial,g_facial,b_facial)) as color - if(new_facial) - r_facial = hex2num(copytext(new_facial, 2, 4)) - g_facial = hex2num(copytext(new_facial, 4, 6)) - b_facial = hex2num(copytext(new_facial, 6, 8)) - - var/new_hair = input(usr, "Please select hair color.", "Character Generation",rgb(r_hair,g_hair,b_hair)) as color - if(new_facial) - r_hair = hex2num(copytext(new_hair, 2, 4)) - g_hair = hex2num(copytext(new_hair, 4, 6)) - b_hair = hex2num(copytext(new_hair, 6, 8)) - - var/new_eyes = input(usr, "Please select eye color.", "Character Generation",rgb(r_eyes,g_eyes,b_eyes)) as color - if(new_eyes) - r_eyes = hex2num(copytext(new_eyes, 2, 4)) - g_eyes = hex2num(copytext(new_eyes, 4, 6)) - b_eyes = hex2num(copytext(new_eyes, 6, 8)) - update_eyes() - - // hair - var/list/all_hairs = subtypesof(/datum/sprite_accessory/hair) - var/list/hairs = list() - - // loop through potential hairs - for(var/x in all_hairs) - var/datum/sprite_accessory/hair/H = new x // create new hair datum based on type x - hairs.Add(H.name) // add hair name to hairs - qdel(H) // delete the hair after it's all done - - var/new_style = tgui_input_list(usr, "Please select hair style", "Character Generation", hairs) - - // if new style selected (not cancel) - if (new_style) - h_style = new_style - - // facial hair - var/list/all_fhairs = subtypesof(/datum/sprite_accessory/facial_hair) - var/list/fhairs = list() - - for(var/x in all_fhairs) - var/datum/sprite_accessory/facial_hair/H = new x - fhairs.Add(H.name) - qdel(H) - - new_style = tgui_input_list(usr, "Please select facial style", "Character Generation", fhairs) - - if(new_style) - f_style = new_style - - var/new_gender = tgui_alert(usr, "Please select gender.", "Character Generation", list("Male", "Female", "Neutral")) - if (new_gender) - if(new_gender == "Male") - gender = MALE - else if(new_gender == "Female") - gender = FEMALE - else - gender = NEUTER - regenerate_icons() - check_dna() - var/datum/gender/T = gender_datums[get_visible_gender()] - visible_message("\The [src] morphs and changes [T.his] appearance!", "You change your appearance!", "Oh, god! What the hell was that? It sounded like flesh getting squished and bone ground into a different shape!") - -/mob/living/carbon/human/proc/remotesay() - set name = "Project mind" - set category = "Superpower" - - if(stat!=CONSCIOUS) - reset_view(0) - remoteview_target = null - return - - if(!(mRemotetalk in src.mutations)) - src.verbs -= /mob/living/carbon/human/proc/remotesay - return - var/list/creatures = list() - for(var/mob/living/carbon/h in mob_list) - creatures += h - var/mob/target = tgui_input_list(usr, "Who do you want to project your mind to?", "Project Mind", creatures) - if (isnull(target)) - return - - var/say = sanitize(tgui_input_text(usr, "What do you wish to say?")) - if(mRemotetalk in target.mutations) - target.show_message("You hear [src.real_name]'s voice: [say]") - else - target.show_message("You hear a voice that seems to echo around the room: [say]") - usr.show_message("You project your mind into [target.real_name]: [say]") - log_say("(TPATH to [key_name(target)]) [say]",src) - for(var/mob/observer/dead/G in mob_list) - G.show_message("Telepathic message from [src] to [target]: [say]") - -/mob/living/carbon/human/proc/remoteobserve() - set name = "Remote View" - set category = "Superpower" - - if(stat!=CONSCIOUS) - remoteview_target = null - reset_view(0) - return - - if(!(mRemote in src.mutations)) - remoteview_target = null - reset_view(0) - src.verbs -= /mob/living/carbon/human/proc/remoteobserve - return - - if(client.eye != client.mob) - remoteview_target = null - reset_view(0) - return - - var/list/mob/creatures = list() - - for(var/mob/living/carbon/h in mob_list) - var/turf/temp_turf = get_turf(h) - if((temp_turf.z != 1 && temp_turf.z != 5) || h.stat!=CONSCIOUS) //Not on mining or the station. Or dead - continue - creatures += h - - var/mob/target = input ("Who do you want to project your mind to?") as mob in creatures - - if (target) - remoteview_target = target - reset_view(target) - else - remoteview_target = null - reset_view(0) - -/mob/living/carbon/human/get_visible_gender(mob/user, force) - switch(force) - if(VISIBLE_GENDER_FORCE_PLURAL) - return PLURAL - if(VISIBLE_GENDER_FORCE_IDENTIFYING) - return get_gender() - if(VISIBLE_GENDER_FORCE_BIOLOGICAL) - return gender - else - if(((wear_mask?.flags_inv & HIDEFACE) || (head?.flags_inv & HIDEMASK) || (head?.flags_inv & HIDEFACE)) && (wear_suit?.flags_inv & HIDEJUMPSUIT)) - return PLURAL - if(species?.ambiguous_genders && user) - if(ishuman(user)) - var/mob/living/carbon/human/human = user - if(!istype(human.species, species)) - return PLURAL - else if(!isobserver(user) && !issilicon(user)) - return PLURAL - return get_gender() - -/mob/living/carbon/human/proc/increase_germ_level(n) - if(gloves) - gloves.germ_level += n - else - germ_level += n - -/mob/living/carbon/human/revive() - - if(should_have_organ(O_HEART)) - vessel.add_reagent("blood",species.blood_volume-vessel.total_volume) - fixblood() - - species.create_organs(src) // Reset our organs/limbs. - restore_all_organs() // Reapply robotics/amputated status from preferences. - - if(!client || !key) //Don't boot out anyone already in the mob. - for (var/obj/item/organ/internal/brain/H in all_brain_organs) - if(H.brainmob) - if(H.brainmob.real_name == src.real_name) - if(H.brainmob.mind) - H.brainmob.mind.transfer_to(src) - qdel(H) - - // Vorestation Addition - reapply markings/appearance from prefs for player mobs - if(client) //just to be sure - client.prefs.copy_to(src) - if(dna) - dna.ResetUIFrom(src) - sync_organ_dna() - // end vorestation addition - - for (var/ID in virus2) - var/datum/disease2/disease/V = virus2[ID] - V.cure(src) - - losebreath = 0 - - ..() - -/mob/living/carbon/human/proc/is_lung_ruptured() - var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] - return L && L.is_bruised() - -/mob/living/carbon/human/proc/rupture_lung() - var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] - - if(L) - L.rupture() - -/* -/mob/living/carbon/human/verb/simulate() - set name = "sim" - set background = 1 - - var/damage = input(usr, "Wound damage","Wound damage") as num - - var/germs = 0 - var/tdamage = 0 - var/ticks = 0 - while (germs < 2501 && ticks < 100000 && round(damage/10)*20) - log_misc("VIRUS TESTING: [ticks] : germs [germs] tdamage [tdamage] prob [round(damage/10)*20]") - ticks++ - if (prob(round(damage/10)*20)) - germs++ - if (germs == 100) - to_world("Reached stage 1 in [ticks] ticks") - if (germs > 100) - if (prob(10)) - damage++ - germs++ - if (germs == 1000) - to_world("Reached stage 2 in [ticks] ticks") - if (germs > 1000) - damage++ - germs++ - if (germs == 2500) - to_world("Reached stage 3 in [ticks] ticks") - to_world("Mob took [tdamage] tox damage") -*/ -//returns 1 if made bloody, returns 0 otherwise - -/mob/living/carbon/human/add_blood(mob/living/carbon/human/M as mob) - if (!..()) - return 0 - //if this blood isn't already in the list, add it - if(istype(M)) - if(!blood_DNA[M.dna.unique_enzymes]) - blood_DNA[M.dna.unique_enzymes] = M.dna.b_type - hand_blood_color = blood_color - update_bloodied() - verbs += /mob/living/carbon/human/proc/bloody_doodle - return 1 //we applied blood to the item - -/mob/living/carbon/human/proc/get_full_print() - if(!dna ||!dna.uni_identity) - return - return md5(dna.uni_identity) - -/mob/living/carbon/human/clean_blood(var/washshoes) - . = ..() - - gunshot_residue = null - - //Always do hands (or whatever's on our hands) - if(gloves) - gloves.clean_blood() - update_inv_gloves() - gloves.germ_level = 0 - else - bloody_hands = 0 - germ_level = 0 - - //Sometimes do shoes if asked (or feet if no shoes) - if(washshoes && shoes) - shoes.clean_blood() - update_inv_shoes() - shoes.germ_level = 0 - else if(washshoes && (feet_blood_color || LAZYLEN(feet_blood_DNA))) - LAZYCLEARLIST(feet_blood_DNA) - feet_blood_DNA = null - feet_blood_color = null - - update_bloodied() - -/mob/living/carbon/human/get_visible_implants(var/class = 0) - - var/list/visible_implants = list() - for(var/obj/item/organ/external/organ in src.organs) - for(var/obj/item/weapon/O in organ.implants) - if(!istype(O,/obj/item/weapon/implant) && (O.w_class > class) && !istype(O,/obj/item/weapon/material/shard/shrapnel)) - visible_implants += O - - return(visible_implants) - -/mob/living/carbon/human/embedded_needs_process() - for(var/obj/item/organ/external/organ in src.organs) - for(var/obj/item/O in organ.implants) - if(!istype(O, /obj/item/weapon/implant)) //implant type items do not cause embedding effects, see handle_embedded_objects() - return 1 - return 0 - -/mob/living/carbon/human/proc/handle_embedded_objects() - - for(var/obj/item/organ/external/organ in src.organs) - if(organ.splinted) //Splints prevent movement. - continue - for(var/obj/item/O in organ.implants) - if(!istype(O,/obj/item/weapon/implant) && prob(5)) //Moving with things stuck in you could be bad. - // All kinds of embedded objects cause bleeding. - if(!can_feel_pain(organ.organ_tag)) - to_chat(src, "You feel [O] moving inside your [organ.name].") - else - var/msg = pick( \ - "A spike of pain jolts your [organ.name] as you bump [O] inside.", \ - "Your movement jostles [O] in your [organ.name] painfully.", \ - "Your movement jostles [O] in your [organ.name] painfully.") - custom_pain(msg, 40) - - organ.take_damage(rand(1,3), 0, 0) - if(!(organ.robotic >= ORGAN_ROBOT) && (should_have_organ(O_HEART))) //There is no blood in protheses. - organ.status |= ORGAN_BLEEDING - -/mob/living/carbon/human/verb/check_pulse() - set category = "Object" - set name = "Check pulse" - set desc = "Approximately count somebody's pulse. Requires you to stand still at least 6 seconds." - set src in view(1) - var/self = 0 - - if(usr.stat || usr.restrained() || !isliving(usr)) return - - var/datum/gender/TU = gender_datums[usr.get_visible_gender()] - var/datum/gender/T = gender_datums[get_visible_gender()] - - if(usr == src) - self = 1 - if(!self) - usr.visible_message("[usr] kneels down, puts [TU.his] hand on [src]'s wrist and begins counting [T.his] pulse.",\ - "You begin counting [src]'s pulse.") - else - usr.visible_message("[usr] begins counting [T.his] pulse.",\ - "You begin counting your pulse.") - - if(src.pulse) - to_chat(usr, "[self ? "You have a" : "[src] has a"] pulse! Counting...") - else - to_chat(usr, "[src] has no pulse!") //it is REALLY UNLIKELY that a dead person would check his own pulse - return - - to_chat(usr, "You must[self ? "" : " both"] remain still until counting is finished.") - if(do_mob(usr, src, 60)) - var/message = "[self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)]." - to_chat(usr,message) - else - to_chat(usr, "You failed to check the pulse. Try again.") - -/mob/living/carbon/human/proc/set_species(var/new_species, var/default_colour, var/regen_icons = TRUE, var/mob/living/carbon/human/example = null) //VOREStation Edit - send an example - - if(!dna) - if(!new_species) - new_species = SPECIES_HUMAN - else - if(!new_species) - new_species = dna.species - else - dna.species = new_species - - // No more invisible screaming wheelchairs because of set_species() typos. - if(!GLOB.all_species[new_species]) - new_species = SPECIES_HUMAN - - if(species) - - if(species.name && species.name == new_species && species.name != "Custom Species") //VOREStation Edit - return - if(species.language) - remove_language(species.language) - if(species.default_language) - remove_language(species.default_language) - for(var/datum/language/L in species.assisted_langs) - remove_language(L) - // Clear out their species abilities. - species.remove_inherent_verbs(src) - holder_type = null - hunger_rate = initial(hunger_rate) //VOREStation Add - - species = GLOB.all_species[new_species] - - if(species.language) - add_language(species.language) - - if(species.default_language) - add_language(species.default_language) - - if(species.icon_scale_x != 1 || species.icon_scale_y != 1) - update_transform() - - if(example) //VOREStation Edit begin - if(!(example == src)) - r_skin = example.r_skin - g_skin = example.g_skin - b_skin = example.b_skin - else if(species.base_color) //VOREStation Edit end - //Apply colour. - r_skin = hex2num(copytext(species.base_color,2,4)) - g_skin = hex2num(copytext(species.base_color,4,6)) - b_skin = hex2num(copytext(species.base_color,6,8)) - else - r_skin = 0 - g_skin = 0 - b_skin = 0 - - if(species.holder_type) - holder_type = species.holder_type - - if(!(gender in species.genders)) - gender = species.genders[1] - - //icon_state = lowertext(species.name) //Necessary? - - //VOREStation Edit start: swap places of those two procs - species.handle_post_spawn(src) - - species.create_organs(src) - //VOREStation Edit end: swap places of those two procs - - - maxHealth = species.total_health - hunger_rate = species.hunger_factor //VOREStation Add - - if(LAZYLEN(descriptors)) - descriptors = null - - if(LAZYLEN(species.descriptors)) - descriptors = list() - for(var/desctype in species.descriptors) - var/datum/mob_descriptor/descriptor = species.descriptors[desctype] - descriptors[desctype] = descriptor.default_value - - spawn(0) - if(regen_icons) regenerate_icons() - make_blood() - if(vessel.total_volume < species.blood_volume) - vessel.maximum_volume = species.blood_volume - vessel.add_reagent("blood", species.blood_volume - vessel.total_volume) - else if(vessel.total_volume > species.blood_volume) - vessel.remove_reagent("blood",vessel.total_volume - species.blood_volume) //This one should stay remove_reagent to work even lack of a O_heart - vessel.maximum_volume = species.blood_volume - fixblood() - species.update_attack_types() //VOREStation Edit - Required for any trait that updates unarmed_types in setup. - - // Rebuild the HUD. If they aren't logged in then login() should reinstantiate it for them. - update_hud() - - //A slew of bits that may be affected by our species change - regenerate_icons() - - if(species) - //if(mind) //VOREStation Removal - //apply_traits() //VOREStation Removal - return 1 - else - return 0 - -/mob/living/carbon/human/proc/bloody_doodle() - set category = "IC" - set name = "Write in blood" - set desc = "Use blood on your hands to write a short message on the floor or a wall, murder mystery style." - - if (src.stat) - return - - if (usr != src) - return 0 //something is terribly wrong - - if (!bloody_hands) - verbs -= /mob/living/carbon/human/proc/bloody_doodle - - if (src.gloves) - to_chat(src, "Your [src.gloves] are getting in the way.") - return - - var/turf/simulated/T = src.loc - if (!istype(T)) //to prevent doodling out of mechs and lockers - to_chat(src, "You cannot reach the floor.") - return - - var/direction = tgui_input_list(src,"Which way?","Tile selection", list("Here","North","South","East","West")) - if (direction != "Here") - T = get_step(T,text2dir(direction)) - if (!istype(T)) - to_chat(src, "You cannot doodle there.") - return - - var/num_doodles = 0 - for (var/obj/effect/decal/cleanable/blood/writing/W in T) - num_doodles++ - if (num_doodles > 4) - to_chat(src, "There is no space to write on!") - return - - var/max_length = bloody_hands * 30 //tweeter style - - var/message = sanitize(tgui_input_text(usr, "Write a message. It cannot be longer than [max_length] characters.","Blood writing", "")) - - if (message) - var/used_blood_amount = round(length(message) / 30, 1) - bloody_hands = max(0, bloody_hands - used_blood_amount) //use up some blood - - if (length(message) > max_length) - message += "-" - to_chat(src, "You ran out of blood to write with!") - - var/obj/effect/decal/cleanable/blood/writing/W = new(T) - W.basecolor = (hand_blood_color) ? hand_blood_color : "#A10808" - W.update_icon() - W.message = message - W.add_fingerprint(src) - -/mob/living/carbon/human/can_inject(var/mob/user, var/error_msg, var/target_zone, var/ignore_thickness = FALSE) - . = 1 - - if(!target_zone) - if(!user) - target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - else - target_zone = user.zone_sel.selecting - - var/obj/item/organ/external/affecting = get_organ(target_zone) - var/fail_msg - if(!affecting) - . = 0 - fail_msg = "They are missing that limb." - else if (affecting.robotic == ORGAN_ROBOT) - . = 0 - fail_msg = "That limb is robotic." - else if (affecting.robotic >= ORGAN_LIFELIKE) - . = 0 - fail_msg = "Your needle refuses to penetrate more than a short distance..." - else if (affecting.thick_skin && prob(70 - round(affecting.brute_dam + affecting.burn_dam / 2))) // Allows transplanted limbs with thick skin to maintain their resistance. - . = 0 - fail_msg = "Your needle fails to penetrate \the [affecting]'s thick hide..." - else - switch(target_zone) - if(BP_HEAD) - if(head && (head.item_flags & THICKMATERIAL) && !ignore_thickness) - . = 0 - else - if(wear_suit && (wear_suit.item_flags & THICKMATERIAL) && !ignore_thickness) - . = 0 - if(!. && error_msg && user) - if(!fail_msg) - fail_msg = "There is no exposed flesh or thin material [target_zone == BP_HEAD ? "on their head" : "on their body"] to inject into." - to_chat(user, "[fail_msg]") - -/mob/living/carbon/human/print_flavor_text(var/shrink = 1) - var/list/equipment = list(src.head,src.wear_mask,src.glasses,src.w_uniform,src.wear_suit,src.gloves,src.shoes) - var/head_exposed = 1 - var/face_exposed = 1 - var/eyes_exposed = 1 - var/torso_exposed = 1 - var/arms_exposed = 1 - var/legs_exposed = 1 - var/hands_exposed = 1 - var/feet_exposed = 1 - - for(var/obj/item/clothing/C in equipment) - if(C.body_parts_covered & HEAD) - head_exposed = 0 - if(C.body_parts_covered & FACE) - face_exposed = 0 - if(C.body_parts_covered & EYES) - eyes_exposed = 0 - if(C.body_parts_covered & UPPER_TORSO) - torso_exposed = 0 - if(C.body_parts_covered & ARMS) - arms_exposed = 0 - if(C.body_parts_covered & HANDS) - hands_exposed = 0 - if(C.body_parts_covered & LEGS) - legs_exposed = 0 - if(C.body_parts_covered & FEET) - feet_exposed = 0 - - flavor_text = "" - for (var/T in flavor_texts) - if(flavor_texts[T] && flavor_texts[T] != "") - if((T == "general") || (T == "head" && head_exposed) || (T == "face" && face_exposed) || (T == "eyes" && eyes_exposed) || (T == "torso" && torso_exposed) || (T == "arms" && arms_exposed) || (T == "hands" && hands_exposed) || (T == "legs" && legs_exposed) || (T == "feet" && feet_exposed)) - flavor_text += flavor_texts[T] - flavor_text += "\n\n" - if(!shrink) - return flavor_text - else - return ..() - -/mob/living/carbon/human/getDNA() - if(species.flags & NO_SCAN) - return null - if(isSynthetic()) - return - ..() - -/mob/living/carbon/human/setDNA() - if(species.flags & NO_SCAN) - return - if(isSynthetic()) - return - ..() - -/mob/living/carbon/human/has_brain() - if(internal_organs_by_name[O_BRAIN]) - var/obj/item/organ/brain = internal_organs_by_name[O_BRAIN] - if(brain && istype(brain)) - return 1 - return 0 - -/mob/living/carbon/human/has_eyes() - if(internal_organs_by_name[O_EYES]) - var/obj/item/organ/eyes = internal_organs_by_name[O_EYES] - if(eyes && istype(eyes) && !(eyes.status & ORGAN_CUT_AWAY)) - return 1 - return 0 - -/mob/living/carbon/human/slip(var/slipped_on, stun_duration=8) - var/list/equipment = list(src.w_uniform,src.wear_suit,src.shoes) - var/footcoverage_check = FALSE - for(var/obj/item/clothing/C in equipment) - if(C.body_parts_covered & FEET) - footcoverage_check = TRUE - break - if((species.flags & NO_SLIP && !footcoverage_check) || (shoes && (shoes.item_flags & NOSLIP))) //Footwear negates a species' natural traction. - return 0 - if(..(slipped_on,stun_duration)) - return 1 - -/mob/living/carbon/human/proc/relocate() - set category = "Object" - set name = "Relocate Joint" - set desc = "Pop a joint back into place. Extremely painful." - set src in view(1) - - if(!isliving(usr) || !usr.checkClickCooldown()) - return - - usr.setClickCooldown(20) - - if(usr.stat > 0) - to_chat(usr, "You are unconcious and cannot do that!") - return - - if(usr.restrained()) - to_chat(usr, "You are restrained and cannot do that!") - return - - var/mob/S = src - var/mob/U = usr - var/self = null - if(S == U) - self = 1 // Removing object from yourself. - - var/list/limbs = list() - for(var/limb in organs_by_name) - var/obj/item/organ/external/current_limb = organs_by_name[limb] - if(current_limb && current_limb.dislocated > 0 && !current_limb.is_parent_dislocated()) //if the parent is also dislocated you will have to relocate that first - limbs |= current_limb - var/obj/item/organ/external/current_limb = tgui_input_list(usr, "Which joint do you wish to relocate?", "Joint Choice", limbs) - - if(!current_limb) - return - - if(self) - to_chat(src, "You brace yourself to relocate your [current_limb.joint]...") - else - to_chat(U, "You begin to relocate [S]'s [current_limb.joint]...") - - if(!do_after(U, 30)) - return - if(!current_limb || !S || !U) - return - - if(self) - to_chat(src, "You pop your [current_limb.joint] back in!") - else - to_chat(U, "You pop [S]'s [current_limb.joint] back in!") - to_chat(S, "[U] pops your [current_limb.joint] back in!") - current_limb.relocate() - -/mob/living/carbon/human/drop_from_inventory(var/obj/item/W, var/atom/target = null) - return !(W in organs) && ..() - -/mob/living/carbon/human/reset_view(atom/A, update_hud = 1) - ..() - if(update_hud) - handle_regular_hud_updates() - -/mob/living/carbon/human/Check_Shoegrip() - if(shoes && (shoes.item_flags & NOSLIP) && istype(shoes, /obj/item/clothing/shoes/magboots)) //magboots + dense_object = no floating - return 1 - if(flying) //VOREStation Edit. Checks to see if they have wings and are flying. - return 1 //VOREStation Edit. - return 0 - -//Puts the item into our active hand if possible. returns 1 on success. -/mob/living/carbon/human/put_in_active_hand(var/obj/item/W) - return (hand ? put_in_l_hand(W) : put_in_r_hand(W)) - -//Puts the item into our inactive hand if possible. returns 1 on success. -/mob/living/carbon/human/put_in_inactive_hand(var/obj/item/W) - return (hand ? put_in_r_hand(W) : put_in_l_hand(W)) - -/mob/living/carbon/human/put_in_hands(var/obj/item/W) - if(!W) - return 0 - if(put_in_active_hand(W)) - update_inv_l_hand() - update_inv_r_hand() - return 1 - else if(put_in_inactive_hand(W)) - update_inv_l_hand() - update_inv_r_hand() - return 1 - else - return ..() - -/mob/living/carbon/human/put_in_l_hand(var/obj/item/W) - if(!..() || l_hand) - return 0 - W.forceMove(src) - l_hand = W - W.equipped(src,slot_l_hand) - W.add_fingerprint(src) - update_inv_l_hand() - return 1 - -/mob/living/carbon/human/put_in_r_hand(var/obj/item/W) - if(!..() || r_hand) - return 0 - W.forceMove(src) - r_hand = W - W.equipped(src,slot_r_hand) - W.add_fingerprint(src) - update_inv_r_hand() - return 1 - -/mob/living/carbon/human/can_stand_overridden() - if(wearing_rig && wearing_rig.ai_can_move_suit(check_for_ai = 1)) - // Actually missing a leg will screw you up. Everything else can be compensated for. - for(var/limbcheck in list("l_leg","r_leg")) - var/obj/item/organ/affecting = get_organ(limbcheck) - if(!affecting) - return 0 - return 1 - return 0 - -/mob/living/carbon/human/verb/toggle_underwear() - set name = "Toggle Underwear" - set desc = "Shows/hides selected parts of your underwear." - set category = "Object" - - if(stat) return - var/datum/category_group/underwear/UWC = tgui_input_list(usr, "Choose underwear:", "Show/hide underwear", global_underwear.categories) - if(!UWC) return - var/datum/category_item/underwear/UWI = all_underwear[UWC.name] - if(!UWI || UWI.name == "None") - to_chat(src, "You do not have [UWC.gender==PLURAL ? "[UWC.display_name]" : "a [UWC.display_name]"].") - return - hide_underwear[UWC.name] = !hide_underwear[UWC.name] - update_underwear(1) - to_chat(src, "You [hide_underwear[UWC.name] ? "take off" : "put on"] your [UWC.display_name].") - return - -/mob/living/carbon/human/verb/pull_punches() - set name = "Pull Punches" - set desc = "Try not to hurt them." - set category = "IC" - - if(stat) return - pulling_punches = !pulling_punches - to_chat(src, "You are now [pulling_punches ? "pulling your punches" : "not pulling your punches"].") - return - -/mob/living/carbon/human/should_have_organ(var/organ_check) - - var/obj/item/organ/external/affecting - if(organ_check in list(O_HEART, O_LUNGS)) - affecting = organs_by_name[BP_TORSO] - else if(organ_check in list(O_LIVER, O_KIDNEYS)) - affecting = organs_by_name[BP_GROIN] - - if(affecting && (affecting.robotic >= ORGAN_ROBOT)) - return 0 - return (species && species.has_organ[organ_check]) - -/mob/living/carbon/human/can_feel_pain(var/obj/item/organ/check_organ) - if(isSynthetic()) - return 0 - for(var/datum/modifier/M in modifiers) - if(M.pain_immunity == TRUE) - return 0 - if(check_organ) - if(!istype(check_organ)) - return 0 - return check_organ.organ_can_feel_pain() - return !(species.flags & NO_PAIN) - -/mob/living/carbon/human/is_sentient() - if(get_FBP_type() == FBP_DRONE) - return FALSE - return ..() - -/mob/living/carbon/human/is_muzzled() - return (wear_mask && (istype(wear_mask, /obj/item/clothing/mask/muzzle) || istype(src.wear_mask, /obj/item/weapon/grenade))) - -/mob/living/carbon/human/get_fire_icon_state() - return species.fire_icon_state - -// Called by job_controller. Makes drones start with a permit, might be useful for other people later too. -/mob/living/carbon/human/equip_post_job() - var/braintype = get_FBP_type() - if(braintype == FBP_DRONE) - var/turf/T = get_turf(src) - var/obj/item/clothing/accessory/permit/drone/permit = new(T) - permit.set_name(real_name) - equip_to_appropriate_slot(permit) // If for some reason it can't find room, it'll still be on the floor. - -/mob/living/carbon/human/proc/update_icon_special() //For things such as teshari hiding and whatnot. - if(status_flags & HIDING) // Hiding? Carry on. - if(stat == DEAD || paralysis || weakened || stunned || restrained() || buckled || LAZYLEN(grabbed_by) || has_buckled_mobs()) //stunned/knocked down by something that isn't the rest verb? Note: This was tried with INCAPACITATION_STUNNED, but that refused to work. //VORE EDIT: Check for has_buckled_mobs() (taur riding) - reveal(null) - else - layer = HIDING_LAYER - -/mob/living/carbon/human/examine_icon() - var/icon/I = get_cached_examine_icon(src) - if(!I) - I = getFlatIcon(src, defdir = SOUTH, no_anim = TRUE) - set_cached_examine_icon(src, I, 50 SECONDS) - return I - -/mob/living/carbon/human/proc/get_display_species() - //Shows species in tooltip - if(src.custom_species) //VOREStation Add - return custom_species //VOREStation Add - //Beepboops get special text if obviously beepboop - if(looksSynthetic()) - if(gender == MALE) - return "Android" - else if(gender == FEMALE) - return "Gynoid" - else - return "Synthetic" - //Else species name - if(species) - return species.get_examine_name() - //Else CRITICAL FAILURE! - return "" - -/mob/living/carbon/human/get_nametag_name(mob/user) - return name //Could do fancy stuff here? - -/mob/living/carbon/human/get_nametag_desc(mob/user) - var/msg = "" - if(hasHUD(user,"security")) - //Try to find their name - var/perpname - var/obj/item/weapon/card/id/I = GetIdCard() - if(I) - perpname = I.registered_name - else - perpname = name - //Try to find their record - var/criminal = "None" - if(perpname) - var/datum/data/record/G = find_general_record("name", perpname) - if(G) - var/datum/data/record/S = find_security_record("id", G.fields["id"]) - if(S) - criminal = S.fields["criminal"] - //If it's interesting, append - if(criminal != "None") - msg += "([criminal]) " - - if(hasHUD(user,"medical")) - msg += "(Health: [round((health/getMaxHealth())*100)]%) " - - msg += get_display_species() - return msg - -/mob/living/carbon/human/reduce_cuff_time() - if(istype(gloves, /obj/item/clothing/gloves/gauntlets/rig)) - return 2 - return ..() - -/mob/living/carbon/human/pull_damage() - if(((health - halloss) <= config.health_threshold_softcrit)) - for(var/name in organs_by_name) - var/obj/item/organ/external/e = organs_by_name[name] - if(!e) - continue - if((e.status & ORGAN_BROKEN && (!e.splinted || (e.splinted in e.contents && prob(30))) || e.status & ORGAN_BLEEDING) && (getBruteLoss() + getFireLoss() >= 100)) - return 1 - else - return ..() - -// Drag damage is handled in a parent -/mob/living/carbon/human/dragged(var/mob/living/dragger, var/oldloc) - var/area/A = get_area(src) - if(lying && !buckled && A.has_gravity() && prob(getBruteLoss() * 200 / maxHealth)) - var/bloodtrail = 1 - if(species?.flags & NO_BLOOD) - bloodtrail = 0 - else - var/blood_volume = vessel.get_reagent_amount("blood") - if(blood_volume < species?.blood_volume*species?.blood_level_fatal) - bloodtrail = 0 //Most of it's gone already, just leave it be - else - remove_blood(1) - if(bloodtrail) - if(istype(loc, /turf/simulated)) - var/turf/T = loc - T.add_blood(src) - . = ..() - -// Tries to turn off item-based things that let you see through walls, like mesons. -// Certain stuff like genetic xray vision is allowed to be kept on. -/mob/living/carbon/human/disable_spoiler_vision() - // Glasses. - if(istype(glasses, /obj/item/clothing/glasses)) - var/obj/item/clothing/glasses/goggles = glasses - if(goggles.active && (goggles.vision_flags & (SEE_TURFS|SEE_OBJS))) - goggles.toggle_active(src) - to_chat(src, span("warning", "Your [goggles.name] have suddenly turned off!")) - - // RIGs. - var/obj/item/weapon/rig/rig = get_rig() - if(istype(rig) && rig.visor?.active && rig.visor.vision?.glasses) - var/obj/item/clothing/glasses/rig_goggles = rig.visor.vision.glasses - if(rig_goggles.vision_flags & (SEE_TURFS|SEE_OBJS)) - rig.visor.deactivate() - to_chat(src, span("warning", "\The [rig]'s visor has shuddenly deactivated!")) - - ..() - -/mob/living/carbon/human/get_mob_riding_slots() - return list(back, head, wear_suit) +/mob/living/carbon/human + name = "unknown" + real_name = "unknown" + voice_name = "unknown" + icon = 'icons/effects/effects.dmi' //We have an ultra-complex update icons that overlays everything, don't load some stupid random male human + icon_state = "nothing" + + has_huds = TRUE //We do have HUDs (like health, wanted, status, not inventory slots) + + var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us. + var/obj/item/weapon/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. + var/last_push_time //For human_attackhand.dm, keeps track of the last use of disarm + + var/spitting = 0 //Spitting and spitting related things. Any human based ranged attacks, be it innate or added abilities. + var/spit_projectile = null //Projectile type. + var/spit_name = null //String + var/last_spit = 0 //Timestamp. + + var/can_defib = 1 //Horrible damage (like beheadings) will prevent defibbing organics. + var/active_regen = FALSE //Used for the regenerate proc in human_powers.dm + var/active_regen_delay = 300 + +/mob/living/carbon/human/Initialize(mapload, var/new_species = null) + if(!dna) + dna = new /datum/dna(null) + // Species name is handled by set_species() + + if(!species) + if(new_species) + set_species(new_species,1) + else + set_species() + + if(species) + real_name = species.get_random_name(gender) + name = real_name + if(mind) + mind.name = real_name + + nutrition = rand(200,400) + + human_mob_list |= src + + . = ..() + + hide_underwear.Cut() + for(var/category in global_underwear.categories_by_name) + hide_underwear[category] = FALSE + + if(dna) + dna.ready_dna(src) + dna.real_name = real_name + sync_organ_dna() + + //verbs |= /mob/living/proc/toggle_selfsurgery //VOREStation Removal + AddComponent(/datum/component/personal_crafting) + +/mob/living/carbon/human/Destroy() + human_mob_list -= src + for(var/organ in organs) + qdel(organ) + QDEL_NULL(nif) //VOREStation Add + worn_clothing.Cut() + return ..() + +/mob/living/carbon/human/Stat() + ..() + if(statpanel("Status")) + stat("Intent:", "[a_intent]") + stat("Move Mode:", "[m_intent]") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + + if (internal) + if (!internal.air_contents) + qdel(internal) + else + stat("Internal Atmosphere Info", internal.name) + stat("Tank Pressure", internal.air_contents.return_pressure()) + stat("Distribution Pressure", internal.distribute_pressure) + + var/obj/item/organ/internal/xenos/plasmavessel/P = internal_organs_by_name[O_PLASMA] //Xenomorphs. Mech. + if(P) + stat(null, "Phoron Stored: [P.stored_plasma]/[P.max_plasma]") + + + if(back && istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/suit = back + var/cell_status = "ERROR" + if(suit.cell) cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" + stat(null, "Suit charge: [cell_status]") + + if(mind) + if(mind.changeling) + stat("Chemical Storage", mind.changeling.chem_charges) + stat("Genetic Damage Time", mind.changeling.geneticdamage) + stat("Re-Adaptations", "[mind.changeling.readapts]/[mind.changeling.max_readapts]") + if(species) + species.Stat(src) + +/mob/living/carbon/human/ex_act(severity) + if(!blinded) + flash_eyes() + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.explosion_modifier)) + severity = CLAMP(severity + M.explosion_modifier, 1, 4) + + severity = round(severity) + + if(severity > 3) + return + + var/shielded = 0 + var/b_loss = null + var/f_loss = null + switch (severity) + if (1.0) + b_loss += 500 + if (!prob(getarmor(null, "bomb"))) + gib() + return + else + var/atom/target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src))) + throw_at(target, 200, 4) + //return +// var/atom/target = get_edge_target_turf(user, get_dir(src, get_step_away(user, src))) + //user.throw_at(target, 200, 4) + + if (2.0) + if (!shielded) + b_loss += 60 + + f_loss += 60 + + if (prob(getarmor(null, "bomb"))) + b_loss = b_loss/1.5 + f_loss = f_loss/1.5 + + if (!get_ear_protection() >= 2) + ear_damage += 30 + ear_deaf += 120 + if (prob(70) && !shielded) + Paralyse(10) + + if(3.0) + b_loss += 30 + if (prob(getarmor(null, "bomb"))) + b_loss = b_loss/2 + if (!get_ear_protection() >= 2) + ear_damage += 15 + ear_deaf += 60 + if (prob(50) && !shielded) + Paralyse(10) + + var/blastsoak = getsoak(null, "bomb") + + b_loss = max(1, b_loss - blastsoak) + f_loss = max(1, f_loss - blastsoak) + + var/update = 0 + + // focus most of the blast on one organ + var/obj/item/organ/external/take_blast = pick(organs) + update |= take_blast.take_damage(b_loss * 0.9, f_loss * 0.9, used_weapon = "Explosive blast") + + // distribute the remaining 10% on all limbs equally + b_loss *= 0.1 + f_loss *= 0.1 + + var/weapon_message = "Explosive Blast" + + for(var/obj/item/organ/external/temp in organs) + switch(temp.organ_tag) + if(BP_HEAD) + update |= temp.take_damage(b_loss * 0.2, f_loss * 0.2, used_weapon = weapon_message) + if(BP_TORSO) + update |= temp.take_damage(b_loss * 0.4, f_loss * 0.4, used_weapon = weapon_message) + else + update |= temp.take_damage(b_loss * 0.05, f_loss * 0.05, used_weapon = weapon_message) + if(update) UpdateDamageIcon() + +/mob/living/carbon/human/proc/implant_loyalty(override = FALSE) // Won't override by default. + if(!config.use_loyalty_implants && !override) return // Nuh-uh. + + var/obj/item/weapon/implant/loyalty/L = new/obj/item/weapon/implant/loyalty(src) + if(L.handle_implant(src, BP_HEAD)) + L.post_implant(src) + +/mob/living/carbon/human/proc/is_loyalty_implanted() + for(var/L in src.contents) + if(istype(L, /obj/item/weapon/implant/loyalty)) + for(var/obj/item/organ/external/O in src.organs) + if(L in O.implants) + return 1 + return 0 + +/mob/living/carbon/human/restrained() + if (handcuffed) + return 1 + if (istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) + return 1 + return 0 + +/mob/living/carbon/human/var/co2overloadtime = null +/mob/living/carbon/human/var/temperature_resistance = T0C+75 + +// called when something steps onto a human +// this handles mobs on fire - mulebot and vehicle code has been relocated to /mob/living/Crossed() +/mob/living/carbon/human/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return + + spread_fire(AM) + + ..() // call parent because we moved behavior to parent + +// Get rank from ID, ID inside PDA, PDA, ID in wallet, etc. +/mob/living/carbon/human/proc/get_authentification_rank(var/if_no_id = "No id", var/if_no_job = "No job") + var/obj/item/device/pda/pda = wear_id + if (istype(pda)) + if (pda.id) + return pda.id.rank ? pda.id.rank : if_no_job + else + return pda.ownrank ? pda.ownrank : if_no_job + else + var/obj/item/weapon/card/id/id = get_idcard() + if(id) + return id.rank ? id.rank : if_no_job + else + return if_no_id + +//gets assignment from ID or ID inside PDA or PDA itself +//Useful when player do something with computers +/mob/living/carbon/human/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") + var/obj/item/device/pda/pda = wear_id + if (istype(pda)) + if (pda.id) + return pda.id.assignment + else + return pda.ownjob ? pda.ownjob : if_no_job + else + var/obj/item/weapon/card/id/id = get_idcard() + if(id) + return id.assignment ? id.assignment : if_no_job + else + return if_no_id + +//gets name from ID or ID inside PDA or PDA itself +//Useful when player do something with computers +/mob/living/carbon/human/proc/get_authentification_name(var/if_no_id = "Unknown") + var/obj/item/device/pda/pda = wear_id + if (istype(pda)) + if (pda.id) + return pda.id.registered_name + else + return pda.owner ? pda.owner : if_no_id + else + var/obj/item/weapon/card/id/id = get_idcard() + if(id) + return id.registered_name + else + return if_no_id + +//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere +/mob/living/carbon/human/proc/get_visible_name() + if(ability_flags & AB_PHASE_SHIFTED) + return "Something" // Something + if( wear_mask && (wear_mask.flags_inv&HIDEFACE) ) //Wearing a mask which hides our face, use id-name if possible + return get_id_name("Unknown") + if( head && (head.flags_inv&HIDEFACE) ) + return get_id_name("Unknown") //Likewise for hats + var/face_name = get_face_name() + var/id_name = get_id_name("") + if((face_name == "Unknown") && id_name && (id_name != face_name)) + return "[face_name] (as [id_name])" + return face_name + +//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when polyacided or when updating a human's name variable +/mob/living/carbon/human/proc/get_face_name() + var/obj/item/organ/external/head = get_organ(BP_HEAD) + if(!head || head.disfigured || head.is_stump() || !real_name || (HUSK in mutations) ) //disfigured. use id-name if possible + return "Unknown" + return real_name + +//gets name from ID or PDA itself, ID inside PDA doesn't matter +//Useful when player is being seen by other mobs +/mob/living/carbon/human/proc/get_id_name(var/if_no_id = "Unknown") + . = if_no_id + if(istype(wear_id,/obj/item/device/pda)) + var/obj/item/device/pda/P = wear_id + return P.owner ? P.owner : if_no_id + if(wear_id) + var/obj/item/weapon/card/id/I = wear_id.GetID() + if(I) + return I.registered_name + return + +//gets ID card object from special clothes slot or null. +/mob/living/carbon/human/proc/get_idcard() + if(wear_id) + return wear_id.GetID() + +//Removed the horrible safety parameter. It was only being used by ninja code anyways. +//Now checks siemens_coefficient of the affected area by default +/mob/living/carbon/human/electrocute_act(var/shock_damage, var/obj/source, var/base_siemens_coeff = 1.0, var/def_zone = null) + + if(status_flags & GODMODE) return 0 //godmode + + if (!def_zone) + def_zone = pick("l_hand", "r_hand") + + if(species.siemens_coefficient == -1) + if(stored_shock_by_ref["\ref[src]"]) + stored_shock_by_ref["\ref[src]"] += shock_damage + else + stored_shock_by_ref["\ref[src]"] = shock_damage + return + + var/obj/item/organ/external/affected_organ = get_organ(check_zone(def_zone)) + var/siemens_coeff = base_siemens_coeff * get_siemens_coefficient_organ(affected_organ) + if(fire_stacks < 0) // Water makes you more conductive. + siemens_coeff *= 1.5 + + return ..(shock_damage, source, siemens_coeff, def_zone) + + +/mob/living/carbon/human/Topic(href, href_list) + if (href_list["mach_close"]) // This is horrible. + var/t1 = text("window=[]", href_list["mach_close"]) + unset_machine() + src << browse(null, t1) + + if(href_list["item"]) + log_runtime(EXCEPTION("Warning: human/Topic was called with item [href_list["item"]], but the item Topic is deprecated!")) + // handle_strip(href_list["item"],usr) + + if (href_list["criminal"]) + if(hasHUD(usr,"security")) + + var/modified = 0 + var/perpname = "wot" + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + + if(perpname) + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if (R.fields["id"] == E.fields["id"]) + + var/setcriminal = tgui_input_list(usr, "Specify a new criminal status for this person.", "Security HUD", list("None", "*Arrest*", "Incarcerated", "Parolled", "Released", "Cancel")) + + if(hasHUD(usr, "security")) + if(setcriminal != "Cancel") + R.fields["criminal"] = setcriminal + modified = 1 + + spawn() + BITSET(hud_updateflag, WANTED_HUD) + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/U = usr + U.handle_hud_list() + if(istype(usr,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/U = usr + U.handle_regular_hud_updates() + + if(!modified) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["secrecord"]) + if(hasHUD(usr,"security")) + var/perpname = "wot" + var/read = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"security")) + var/list/security_hud_text = list() + security_hud_text += "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]" + security_hud_text += "Species: [R.fields["species"]]" + security_hud_text += "Minor Crimes: [R.fields["mi_crim"]]" + security_hud_text += "Details: [R.fields["mi_crim_d"]]" + security_hud_text += "Major Crimes: [R.fields["ma_crim"]]" + security_hud_text += "Details: [R.fields["ma_crim_d"]]" + security_hud_text += "Notes: [R.fields["notes"]]" + security_hud_text += "\[View Comment Log\]" + to_chat(usr, "[jointext(security_hud_text, "
                    ")]
                    ") + read = 1 + + if(!read) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["secrecordComment"]) + if(hasHUD(usr,"security")) + var/perpname = "wot" + var/read = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"security")) + read = 1 + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + to_chat(usr, "[R.fields[text("com_[]", counter)]]") + counter++ + if (counter == 1) + to_chat(usr, "No comment found.") + to_chat(usr, "\[Add comment\]") + + if(!read) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["secrecordadd"]) + if(hasHUD(usr,"security")) + var/perpname = "wot" + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"security")) + var/t1 = sanitize(tgui_input_text(usr, "Add Comment:", "Sec. records", null, null, multiline = TRUE, prevent_enter = TRUE)) + if ( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"security")) ) + return + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + counter++ + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/U = usr + R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") + if(istype(usr,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/U = usr + R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") + + if (href_list["medical"]) + if(hasHUD(usr,"medical")) + var/perpname = "wot" + var/modified = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.general) + if (R.fields["id"] == E.fields["id"]) + + var/setmedical = tgui_input_list(usr, "Specify a new medical status for this person.", "Medical HUD", list("*SSD*", "*Deceased*", "Physically Unfit", "Active", "Disabled", "Cancel")) + + if(hasHUD(usr,"medical")) + if(setmedical != "Cancel") + R.fields["p_stat"] = setmedical + modified = 1 + if(PDA_Manifest.len) + PDA_Manifest.Cut() + + spawn() + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/U = usr + U.handle_regular_hud_updates() + if(istype(usr,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/U = usr + U.handle_regular_hud_updates() + + if(!modified) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["medrecord"]) + if(hasHUD(usr,"medical")) + var/perpname = "wot" + var/read = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.medical) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"medical")) + var/list/medical_hud_text = list() + medical_hud_text += "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]] Blood Basis: [R.fields["blood_reagent"]]" + medical_hud_text += "Species: [R.fields["species"]]" + medical_hud_text += "DNA: [R.fields["b_dna"]]" + medical_hud_text += "Minor Disabilities: [R.fields["mi_dis"]]" + medical_hud_text += "Details: [R.fields["mi_dis_d"]]" + medical_hud_text += "Major Disabilities: [R.fields["ma_dis"]]" + medical_hud_text += "Details: [R.fields["ma_dis_d"]]" + medical_hud_text += "Notes: [R.fields["notes"]]" + medical_hud_text += "\[View Comment Log\]" + to_chat(usr, "[jointext(medical_hud_text, "
                    ")]
                    ") + read = 1 + + if(!read) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["medrecordComment"]) + if(hasHUD(usr,"medical")) + var/perpname = "wot" + var/read = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.medical) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"medical")) + read = 1 + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + to_chat(usr, "[R.fields[text("com_[]", counter)]]") + counter++ + if (counter == 1) + to_chat(usr, "No comment found.") + to_chat(usr, "\[Add comment\]") + + if(!read) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["medrecordadd"]) + if(hasHUD(usr,"medical")) + var/perpname = "wot" + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.medical) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"medical")) + var/t1 = sanitize(tgui_input_text(usr, "Add Comment:", "Med. records", null, null, multiline = TRUE, prevent_enter = TRUE)) + if ( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"medical")) ) + return + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + counter++ + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/U = usr + R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") + if(istype(usr,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/U = usr + R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") + + if (href_list["emprecord"]) + if(hasHUD(usr,"best")) + var/perpname = "wot" + var/read = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.general) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"best")) + var/list/emp_hud_text = list() + emp_hud_text += "Name: [R.fields["name"]]" + emp_hud_text += "Species: [R.fields["species"]]" + emp_hud_text += "Assignment: [R.fields["real_rank"]] ([R.fields["rank"]])" + emp_hud_text += "Home System: [R.fields["home_system"]]" + emp_hud_text += "Birthplace: [R.fields["birthplace"]]" + emp_hud_text += "Citizenship: [R.fields["citizenship"]]" + emp_hud_text += "Primary Employer: [R.fields["personal_faction"]]" + emp_hud_text += "Religious Beliefs: [R.fields["religion"]]" + emp_hud_text += "Known Languages: [R.fields["languages"]]" + emp_hud_text += "Notes: [R.fields["notes"]]" + emp_hud_text += "\[View Comment Log\]" + to_chat(usr, "[jointext(emp_hud_text, "
                    ")]
                    ") + read = 1 + + if(!read) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["emprecordComment"]) + if(hasHUD(usr,"best")) + var/perpname = "wot" + var/read = 0 + + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.general) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"best")) + read = 1 + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + to_chat(usr, "[R.fields[text("com_[]", counter)]]") + counter++ + if (counter == 1) + to_chat(usr, "No comment found.") + to_chat(usr, "\[Add comment\]") + + if(!read) + to_chat(usr, "[span_red("Unable to locate a data core entry for this person.")]") + + if (href_list["emprecordadd"]) + if(hasHUD(usr,"best")) + var/perpname = "wot" + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + for (var/datum/data/record/E in data_core.general) + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.general) + if (R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"best")) + var/t1 = sanitize(tgui_input_text(usr, "Add Comment:", "Emp. records", null, null, multiline = TRUE, prevent_enter = TRUE)) + if ( !(t1) || usr.stat || usr.restrained() || !(hasHUD(usr,"best")) ) + return + var/counter = 1 + while(R.fields[text("com_[]", counter)]) + counter++ + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/U = usr + R.fields[text("com_[counter]")] = text("Made by [U.get_authentification_name()] ([U.get_assignment()]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") + if(istype(usr,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/U = usr + R.fields[text("com_[counter]")] = text("Made by [U.name] ([U.modtype] [U.braintype]) on [time2text(world.realtime, "DDD MMM DD hh:mm:ss")], [game_year]
                    [t1]") + + if (href_list["lookitem"]) + var/obj/item/I = locate(href_list["lookitem"]) + src.examinate(I) + + if (href_list["lookitem_desc_only"]) + var/obj/item/I = locate(href_list["lookitem_desc_only"]) + if(!I) + return + usr.examinate(I, 1) + + if (href_list["lookmob"]) + var/mob/M = locate(href_list["lookmob"]) + src.examinate(M) + + if (href_list["clickitem"]) + var/obj/item/I = locate(href_list["clickitem"]) + if(src.client) + src.ClickOn(I) + + if (href_list["flavor_change"]) + switch(href_list["flavor_change"]) + if("done") + src << browse(null, "window=flavor_changes") + return + if("general") + var/msg = strip_html_simple(tgui_input_text(usr,"Update the general description of your character. This will be shown regardless of clothing.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes + if(msg) + flavor_texts[href_list["flavor_change"]] = msg + set_flavor() + return + else + var/msg = strip_html_simple(tgui_input_text(usr,"Update the flavor text for your [href_list["flavor_change"]].","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]]), multiline = TRUE, prevent_enter = TRUE)) + if(msg) + flavor_texts[href_list["flavor_change"]] = msg + set_flavor() + return + ..() + return + +///eyecheck() +///Returns a number between -1 to 2 +/mob/living/carbon/human/eyecheck() + + var/obj/item/organ/internal/eyes/I + + if(internal_organs_by_name[O_EYES]) // Eyes are fucked, not a 'weak point'. + I = internal_organs_by_name[O_EYES] + if(I.is_broken()) + return FLASH_PROTECTION_MAJOR + else if(!species.dispersed_eyes) // They can't be flashed if they don't have eyes, or widespread sensing surfaces. + return FLASH_PROTECTION_MAJOR + + var/number = get_equipment_flash_protection() + if(I) + number = I.get_total_protection(number) + I.additional_flash_effects(number) + return number + +/mob/living/carbon/human/flash_eyes(var/intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, visual = FALSE, type = /obj/screen/fullscreen/flash) + if(internal_organs_by_name[O_EYES]) // Eyes are fucked, not a 'weak point'. + var/obj/item/organ/internal/eyes/I = internal_organs_by_name[O_EYES] + I.additional_flash_effects(intensity) + return ..() + +#define add_clothing_protection(A) \ + var/obj/item/clothing/C = A; \ + flash_protection += C.flash_protection; \ + +/mob/living/carbon/human/proc/get_equipment_flash_protection() + var/flash_protection = 0 + + if(istype(src.head, /obj/item/clothing/head)) + add_clothing_protection(head) + if(istype(src.glasses, /obj/item/clothing/glasses)) + add_clothing_protection(glasses) + if(istype(src.wear_mask, /obj/item/clothing/mask)) + add_clothing_protection(wear_mask) + + return flash_protection + +#undef add_clothing_protection + +//Used by various things that knock people out by applying blunt trauma to the head. +//Checks that the species has a "head" (brain containing organ) and that hit_zone refers to it. +/mob/living/carbon/human/proc/headcheck(var/target_zone, var/brain_tag = "brain") + + var/obj/item/organ/affecting = internal_organs_by_name[brain_tag] + + target_zone = check_zone(target_zone) + if(!affecting || affecting.parent_organ != target_zone) + return 0 + + //if the parent organ is significantly larger than the brain organ, then hitting it is not guaranteed + var/obj/item/organ/parent = get_organ(target_zone) + if(!parent) + return 0 + + if(parent.w_class > affecting.w_class + 1) + return prob(100 / 2**(parent.w_class - affecting.w_class - 1)) + + return 1 + +/mob/living/carbon/human/IsAdvancedToolUser(var/silent) + // VOREstation start + if(feral) + to_chat(src, "Your primitive mind can't grasp the concept of that thing.") + return 0 + // VOREstation end + if(species.has_fine_manipulation) + return 1 + if(!silent) + to_chat(src, "You don't have the dexterity to use that!") + return 0 + +/mob/living/carbon/human/abiotic(var/full_body = 0) + if(full_body && ((src.l_hand && !( src.l_hand.abstract )) || (src.r_hand && !( src.r_hand.abstract )) || (src.back || src.wear_mask || src.head || src.shoes || src.w_uniform || src.wear_suit || src.glasses || src.l_ear || src.r_ear || src.gloves))) + return 1 + + if( (src.l_hand && !src.l_hand.abstract) || (src.r_hand && !src.r_hand.abstract) ) + return 1 + + return 0 + + +/mob/living/carbon/human/proc/check_dna() + dna.check_integrity(src) + return + +/mob/living/carbon/human/get_species() + if(!species) + set_species() + return species.name + +/mob/living/carbon/human/proc/play_xylophone() + if(!src.xylophone) + var/datum/gender/T = gender_datums[get_visible_gender()] + visible_message("[span_red("\The [src] begins playing [T.his] ribcage like a xylophone. It's quite spooky.")]","You begin to play a spooky refrain on your ribcage.","[span_red("You hear a spooky xylophone melody.")]") + var/song = pick('sound/effects/xylophone1.ogg','sound/effects/xylophone2.ogg','sound/effects/xylophone3.ogg') + playsound(src, song, 50, 1, -1) + xylophone = 1 + spawn(1200) + xylophone=0 + return + +/mob/living/proc/check_has_mouth() + return 1 + +/mob/living/carbon/human/check_has_mouth() + // Todo, check stomach organ when implemented. + var/obj/item/organ/external/head/H = get_organ(BP_HEAD) + if(!H || !H.can_intake_reagents) + return 0 + return 1 + +/mob/living/carbon/human/proc/morph() + set name = "Morph" + set category = "Superpower" + + if(stat!=CONSCIOUS) + reset_view(0) + remoteview_target = null + return + + if(!(mMorph in mutations)) + src.verbs -= /mob/living/carbon/human/proc/morph + return + + var/new_facial = input(usr, "Please select facial hair color.", "Character Generation",rgb(r_facial,g_facial,b_facial)) as color + if(new_facial) + r_facial = hex2num(copytext(new_facial, 2, 4)) + g_facial = hex2num(copytext(new_facial, 4, 6)) + b_facial = hex2num(copytext(new_facial, 6, 8)) + + var/new_hair = input(usr, "Please select hair color.", "Character Generation",rgb(r_hair,g_hair,b_hair)) as color + if(new_facial) + r_hair = hex2num(copytext(new_hair, 2, 4)) + g_hair = hex2num(copytext(new_hair, 4, 6)) + b_hair = hex2num(copytext(new_hair, 6, 8)) + + var/new_eyes = input(usr, "Please select eye color.", "Character Generation",rgb(r_eyes,g_eyes,b_eyes)) as color + if(new_eyes) + r_eyes = hex2num(copytext(new_eyes, 2, 4)) + g_eyes = hex2num(copytext(new_eyes, 4, 6)) + b_eyes = hex2num(copytext(new_eyes, 6, 8)) + update_eyes() + + // hair + var/list/all_hairs = subtypesof(/datum/sprite_accessory/hair) + var/list/hairs = list() + + // loop through potential hairs + for(var/x in all_hairs) + var/datum/sprite_accessory/hair/H = new x // create new hair datum based on type x + hairs.Add(H.name) // add hair name to hairs + qdel(H) // delete the hair after it's all done + + var/new_style = tgui_input_list(usr, "Please select hair style", "Character Generation", hairs) + + // if new style selected (not cancel) + if (new_style) + h_style = new_style + + // facial hair + var/list/all_fhairs = subtypesof(/datum/sprite_accessory/facial_hair) + var/list/fhairs = list() + + for(var/x in all_fhairs) + var/datum/sprite_accessory/facial_hair/H = new x + fhairs.Add(H.name) + qdel(H) + + new_style = tgui_input_list(usr, "Please select facial style", "Character Generation", fhairs) + + if(new_style) + f_style = new_style + + var/new_gender = tgui_alert(usr, "Please select gender.", "Character Generation", list("Male", "Female", "Neutral")) + if (new_gender) + if(new_gender == "Male") + gender = MALE + else if(new_gender == "Female") + gender = FEMALE + else + gender = NEUTER + regenerate_icons() + check_dna() + var/datum/gender/T = gender_datums[get_visible_gender()] + visible_message("\The [src] morphs and changes [T.his] appearance!", "You change your appearance!", "[span_red("Oh, god! What the hell was that? It sounded like flesh getting squished and bone ground into a different shape!")]") + +/mob/living/carbon/human/proc/remotesay() + set name = "Project mind" + set category = "Superpower" + + if(stat!=CONSCIOUS) + reset_view(0) + remoteview_target = null + return + + if(!(mRemotetalk in src.mutations)) + src.verbs -= /mob/living/carbon/human/proc/remotesay + return + var/list/creatures = list() + for(var/mob/living/carbon/h in mob_list) + creatures += h + var/mob/target = tgui_input_list(usr, "Who do you want to project your mind to?", "Project Mind", creatures) + if (isnull(target)) + return + + var/say = sanitize(tgui_input_text(usr, "What do you wish to say?")) + if(mRemotetalk in target.mutations) + target.show_message("[span_blue("You hear [src.real_name]'s voice: [say]")]") + else + target.show_message("[span_blue("You hear a voice that seems to echo around the room: [say]")]") + usr.show_message("[span_blue("You project your mind into [target.real_name]: [say]")]") + log_say("(TPATH to [key_name(target)]) [say]",src) + for(var/mob/observer/dead/G in mob_list) + G.show_message("Telepathic message from [src] to [target]: [say]") + +/mob/living/carbon/human/proc/remoteobserve() + set name = "Remote View" + set category = "Superpower" + + if(stat!=CONSCIOUS) + remoteview_target = null + reset_view(0) + return + + if(!(mRemote in src.mutations)) + remoteview_target = null + reset_view(0) + src.verbs -= /mob/living/carbon/human/proc/remoteobserve + return + + if(client.eye != client.mob) + remoteview_target = null + reset_view(0) + return + + var/list/mob/creatures = list() + + for(var/mob/living/carbon/h in mob_list) + var/turf/temp_turf = get_turf(h) + if((temp_turf.z != 1 && temp_turf.z != 5) || h.stat!=CONSCIOUS) //Not on mining or the station. Or dead + continue + creatures += h + + var/mob/target = input ("Who do you want to project your mind to?") as mob in creatures + + if (target) + remoteview_target = target + reset_view(target) + else + remoteview_target = null + reset_view(0) + +/mob/living/carbon/human/get_visible_gender(mob/user, force) + switch(force) + if(VISIBLE_GENDER_FORCE_PLURAL) + return PLURAL + if(VISIBLE_GENDER_FORCE_IDENTIFYING) + return get_gender() + if(VISIBLE_GENDER_FORCE_BIOLOGICAL) + return gender + else + if(((wear_mask?.flags_inv & HIDEFACE) || (head?.flags_inv & HIDEMASK) || (head?.flags_inv & HIDEFACE)) && (wear_suit?.flags_inv & HIDEJUMPSUIT)) + return PLURAL + if(species?.ambiguous_genders && user) + if(ishuman(user)) + var/mob/living/carbon/human/human = user + if(!istype(human.species, species)) + return PLURAL + else if(!isobserver(user) && !issilicon(user)) + return PLURAL + return get_gender() + +/mob/living/carbon/human/proc/increase_germ_level(n) + if(gloves) + gloves.germ_level += n + else + germ_level += n + +/mob/living/carbon/human/revive() + + if(should_have_organ(O_HEART)) + vessel.add_reagent("blood",species.blood_volume-vessel.total_volume) + fixblood() + + species.create_organs(src) // Reset our organs/limbs. + restore_all_organs() // Reapply robotics/amputated status from preferences. + + if(!client || !key) //Don't boot out anyone already in the mob. + for (var/obj/item/organ/internal/brain/H in all_brain_organs) + if(H.brainmob) + if(H.brainmob.real_name == src.real_name) + if(H.brainmob.mind) + H.brainmob.mind.transfer_to(src) + qdel(H) + + // Vorestation Addition - reapply markings/appearance from prefs for player mobs + if(client) //just to be sure + client.prefs.copy_to(src) + if(dna) + dna.ResetUIFrom(src) + sync_organ_dna() + // end vorestation addition + + for (var/ID in virus2) + var/datum/disease2/disease/V = virus2[ID] + V.cure(src) + + losebreath = 0 + + ..() + +/mob/living/carbon/human/proc/is_lung_ruptured() + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + return L && L.is_bruised() + +/mob/living/carbon/human/proc/rupture_lung() + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + + if(L) + L.rupture() + +/* +/mob/living/carbon/human/verb/simulate() + set name = "sim" + set background = 1 + + var/damage = input(usr, "Wound damage","Wound damage") as num + + var/germs = 0 + var/tdamage = 0 + var/ticks = 0 + while (germs < 2501 && ticks < 100000 && round(damage/10)*20) + log_misc("VIRUS TESTING: [ticks] : germs [germs] tdamage [tdamage] prob [round(damage/10)*20]") + ticks++ + if (prob(round(damage/10)*20)) + germs++ + if (germs == 100) + to_world("Reached stage 1 in [ticks] ticks") + if (germs > 100) + if (prob(10)) + damage++ + germs++ + if (germs == 1000) + to_world("Reached stage 2 in [ticks] ticks") + if (germs > 1000) + damage++ + germs++ + if (germs == 2500) + to_world("Reached stage 3 in [ticks] ticks") + to_world("Mob took [tdamage] tox damage") +*/ +//returns 1 if made bloody, returns 0 otherwise + +/mob/living/carbon/human/add_blood(mob/living/carbon/human/M as mob) + if (!..()) + return 0 + //if this blood isn't already in the list, add it + if(istype(M)) + if(!blood_DNA[M.dna.unique_enzymes]) + blood_DNA[M.dna.unique_enzymes] = M.dna.b_type + hand_blood_color = blood_color + update_bloodied() + verbs += /mob/living/carbon/human/proc/bloody_doodle + return 1 //we applied blood to the item + +/mob/living/carbon/human/proc/get_full_print() + if(!dna ||!dna.uni_identity) + return + return md5(dna.uni_identity) + +/mob/living/carbon/human/clean_blood(var/washshoes) + . = ..() + + gunshot_residue = null + + //Always do hands (or whatever's on our hands) + if(gloves) + gloves.clean_blood() + update_inv_gloves() + gloves.germ_level = 0 + else + bloody_hands = 0 + germ_level = 0 + + //Sometimes do shoes if asked (or feet if no shoes) + if(washshoes && shoes) + shoes.clean_blood() + update_inv_shoes() + shoes.germ_level = 0 + else if(washshoes && (feet_blood_color || LAZYLEN(feet_blood_DNA))) + LAZYCLEARLIST(feet_blood_DNA) + feet_blood_DNA = null + feet_blood_color = null + + update_bloodied() + +/mob/living/carbon/human/get_visible_implants(var/class = 0) + + var/list/visible_implants = list() + for(var/obj/item/organ/external/organ in src.organs) + for(var/obj/item/weapon/O in organ.implants) + if(!istype(O,/obj/item/weapon/implant) && (O.w_class > class) && !istype(O,/obj/item/weapon/material/shard/shrapnel)) + visible_implants += O + + return(visible_implants) + +/mob/living/carbon/human/embedded_needs_process() + for(var/obj/item/organ/external/organ in src.organs) + for(var/obj/item/O in organ.implants) + if(!istype(O, /obj/item/weapon/implant)) //implant type items do not cause embedding effects, see handle_embedded_objects() + return 1 + return 0 + +/mob/living/carbon/human/proc/handle_embedded_objects() + + for(var/obj/item/organ/external/organ in src.organs) + if(organ.splinted) //Splints prevent movement. + continue + for(var/obj/item/O in organ.implants) + if(!istype(O,/obj/item/weapon/implant) && prob(5)) //Moving with things stuck in you could be bad. + // All kinds of embedded objects cause bleeding. + if(!can_feel_pain(organ.organ_tag)) + to_chat(src, "You feel [O] moving inside your [organ.name].") + else + var/msg = pick( \ + "A spike of pain jolts your [organ.name] as you bump [O] inside.", \ + "Your movement jostles [O] in your [organ.name] painfully.", \ + "Your movement jostles [O] in your [organ.name] painfully.") + custom_pain(msg, 40) + + organ.take_damage(rand(1,3), 0, 0) + if(!(organ.robotic >= ORGAN_ROBOT) && (should_have_organ(O_HEART))) //There is no blood in protheses. + organ.status |= ORGAN_BLEEDING + +/mob/living/carbon/human/verb/check_pulse() + set category = "Object" + set name = "Check pulse" + set desc = "Approximately count somebody's pulse. Requires you to stand still at least 6 seconds." + set src in view(1) + var/self = 0 + + if(usr.stat || usr.restrained() || !isliving(usr)) return + + var/datum/gender/TU = gender_datums[usr.get_visible_gender()] + var/datum/gender/T = gender_datums[get_visible_gender()] + + if(usr == src) + self = 1 + if(!self) + usr.visible_message("[usr] kneels down, puts [TU.his] hand on [src]'s wrist and begins counting [T.his] pulse.",\ + "You begin counting [src]'s pulse.") + else + usr.visible_message("[usr] begins counting [T.his] pulse.",\ + "You begin counting your pulse.") + + if(src.pulse) + to_chat(usr, "[self ? "You have a" : "[src] has a"] pulse! Counting...") + else + to_chat(usr, "[src] has no pulse!") //it is REALLY UNLIKELY that a dead person would check his own pulse + return + + to_chat(usr, "You must[self ? "" : " both"] remain still until counting is finished.") + if(do_mob(usr, src, 60)) + var/message = "[self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)]." + to_chat(usr,message) + else + to_chat(usr, "You failed to check the pulse. Try again.") + +/mob/living/carbon/human/proc/set_species(var/new_species, var/default_colour, var/regen_icons = TRUE, var/mob/living/carbon/human/example = null) //VOREStation Edit - send an example + + if(!dna) + if(!new_species) + new_species = SPECIES_HUMAN + else + if(!new_species) + new_species = dna.species + else + dna.species = new_species + + // No more invisible screaming wheelchairs because of set_species() typos. + if(!GLOB.all_species[new_species]) + new_species = SPECIES_HUMAN + + if(species) + + if(species.name && species.name == new_species && species.name != "Custom Species") //VOREStation Edit + return + if(species.language) + remove_language(species.language) + if(species.default_language) + remove_language(species.default_language) + for(var/datum/language/L in species.assisted_langs) + remove_language(L) + // Clear out their species abilities. + species.remove_inherent_verbs(src) + holder_type = null + hunger_rate = initial(hunger_rate) //VOREStation Add + + species = GLOB.all_species[new_species] + + if(species.language) + add_language(species.language) + + if(species.default_language) + add_language(species.default_language) + + if(species.icon_scale_x != 1 || species.icon_scale_y != 1) + update_transform() + + if(example) //VOREStation Edit begin + if(!(example == src)) + r_skin = example.r_skin + g_skin = example.g_skin + b_skin = example.b_skin + else if(species.base_color) //VOREStation Edit end + //Apply colour. + r_skin = hex2num(copytext(species.base_color,2,4)) + g_skin = hex2num(copytext(species.base_color,4,6)) + b_skin = hex2num(copytext(species.base_color,6,8)) + else + r_skin = 0 + g_skin = 0 + b_skin = 0 + + if(species.holder_type) + holder_type = species.holder_type + + if(!(gender in species.genders)) + gender = species.genders[1] + + //icon_state = lowertext(species.name) //Necessary? + + //VOREStation Edit start: swap places of those two procs + species.handle_post_spawn(src) + + species.create_organs(src) + //VOREStation Edit end: swap places of those two procs + + + maxHealth = species.total_health + hunger_rate = species.hunger_factor //VOREStation Add + + center_offset = species.center_offset + + if(LAZYLEN(descriptors)) + descriptors = null + + if(LAZYLEN(species.descriptors)) + descriptors = list() + for(var/desctype in species.descriptors) + var/datum/mob_descriptor/descriptor = species.descriptors[desctype] + descriptors[desctype] = descriptor.default_value + + spawn(0) + if(regen_icons) regenerate_icons() + make_blood() + if(vessel.total_volume < species.blood_volume) + vessel.maximum_volume = species.blood_volume + vessel.add_reagent("blood", species.blood_volume - vessel.total_volume) + else if(vessel.total_volume > species.blood_volume) + vessel.remove_reagent("blood",vessel.total_volume - species.blood_volume) //This one should stay remove_reagent to work even lack of a O_heart + vessel.maximum_volume = species.blood_volume + fixblood() + species.update_attack_types() //VOREStation Edit - Required for any trait that updates unarmed_types in setup. + + // Rebuild the HUD. If they aren't logged in then login() should reinstantiate it for them. + update_hud() + + //A slew of bits that may be affected by our species change + regenerate_icons() + + if(species) + //if(mind) //VOREStation Removal + //apply_traits() //VOREStation Removal + return 1 + else + return 0 + +/mob/living/carbon/human/proc/bloody_doodle() + set category = "IC" + set name = "Write in blood" + set desc = "Use blood on your hands to write a short message on the floor or a wall, murder mystery style." + + if (src.stat) + return + + if (usr != src) + return 0 //something is terribly wrong + + if (!bloody_hands) + verbs -= /mob/living/carbon/human/proc/bloody_doodle + + if (src.gloves) + to_chat(src, "Your [src.gloves] are getting in the way.") + return + + var/turf/simulated/T = src.loc + if (!istype(T)) //to prevent doodling out of mechs and lockers + to_chat(src, "You cannot reach the floor.") + return + + var/direction = tgui_input_list(src,"Which way?","Tile selection", list("Here","North","South","East","West")) + if (direction != "Here") + T = get_step(T,text2dir(direction)) + if (!istype(T)) + to_chat(src, "You cannot doodle there.") + return + + var/num_doodles = 0 + for (var/obj/effect/decal/cleanable/blood/writing/W in T) + num_doodles++ + if (num_doodles > 4) + to_chat(src, "There is no space to write on!") + return + + var/max_length = bloody_hands * 30 //tweeter style + + var/message = sanitize(tgui_input_text(usr, "Write a message. It cannot be longer than [max_length] characters.","Blood writing", "")) + + if (message) + var/used_blood_amount = round(length(message) / 30, 1) + bloody_hands = max(0, bloody_hands - used_blood_amount) //use up some blood + + if (length(message) > max_length) + message += "-" + to_chat(src, "You ran out of blood to write with!") + + var/obj/effect/decal/cleanable/blood/writing/W = new(T) + W.basecolor = (hand_blood_color) ? hand_blood_color : "#A10808" + W.update_icon() + W.message = message + W.add_fingerprint(src) + +/mob/living/carbon/human/can_inject(var/mob/user, var/error_msg, var/target_zone, var/ignore_thickness = FALSE) + . = 1 + + if(!target_zone) + if(!user) + target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + else + target_zone = user.zone_sel.selecting + + var/obj/item/organ/external/affecting = get_organ(target_zone) + var/fail_msg + if(!affecting) + . = 0 + fail_msg = "They are missing that limb." + else if (affecting.robotic == ORGAN_ROBOT) + . = 0 + fail_msg = "That limb is robotic." + else if (affecting.robotic >= ORGAN_LIFELIKE) + . = 0 + fail_msg = "Your needle refuses to penetrate more than a short distance..." + else if (affecting.thick_skin && prob(70 - round(affecting.brute_dam + affecting.burn_dam / 2))) // Allows transplanted limbs with thick skin to maintain their resistance. + . = 0 + fail_msg = "Your needle fails to penetrate \the [affecting]'s thick hide..." + else + switch(target_zone) + if(BP_HEAD) + if(head && (head.item_flags & THICKMATERIAL) && !ignore_thickness) + . = 0 + else + if(wear_suit && (wear_suit.item_flags & THICKMATERIAL) && !ignore_thickness) + . = 0 + if(!. && error_msg && user) + if(!fail_msg) + fail_msg = "There is no exposed flesh or thin material [target_zone == BP_HEAD ? "on their head" : "on their body"] to inject into." + to_chat(user, "[fail_msg]") + +/mob/living/carbon/human/print_flavor_text(var/shrink = 1) + var/list/equipment = list(src.head,src.wear_mask,src.glasses,src.w_uniform,src.wear_suit,src.gloves,src.shoes) + var/head_exposed = 1 + var/face_exposed = 1 + var/eyes_exposed = 1 + var/torso_exposed = 1 + var/arms_exposed = 1 + var/legs_exposed = 1 + var/hands_exposed = 1 + var/feet_exposed = 1 + + for(var/obj/item/clothing/C in equipment) + if(C.body_parts_covered & HEAD) + head_exposed = 0 + if(C.body_parts_covered & FACE) + face_exposed = 0 + if(C.body_parts_covered & EYES) + eyes_exposed = 0 + if(C.body_parts_covered & UPPER_TORSO) + torso_exposed = 0 + if(C.body_parts_covered & ARMS) + arms_exposed = 0 + if(C.body_parts_covered & HANDS) + hands_exposed = 0 + if(C.body_parts_covered & LEGS) + legs_exposed = 0 + if(C.body_parts_covered & FEET) + feet_exposed = 0 + + flavor_text = "" + for (var/T in flavor_texts) + if(flavor_texts[T] && flavor_texts[T] != "") + if((T == "general") || (T == "head" && head_exposed) || (T == "face" && face_exposed) || (T == "eyes" && eyes_exposed) || (T == "torso" && torso_exposed) || (T == "arms" && arms_exposed) || (T == "hands" && hands_exposed) || (T == "legs" && legs_exposed) || (T == "feet" && feet_exposed)) + flavor_text += flavor_texts[T] + flavor_text += "\n\n" + if(!shrink) + return flavor_text + else + return ..() + +/mob/living/carbon/human/getDNA() + if(species.flags & NO_SCAN) + return null + if(isSynthetic()) + return + ..() + +/mob/living/carbon/human/setDNA() + if(species.flags & NO_SCAN) + return + if(isSynthetic()) + return + ..() + +/mob/living/carbon/human/has_brain() + if(internal_organs_by_name[O_BRAIN]) + var/obj/item/organ/brain = internal_organs_by_name[O_BRAIN] + if(brain && istype(brain)) + return 1 + return 0 + +/mob/living/carbon/human/has_eyes() + if(internal_organs_by_name[O_EYES]) + var/obj/item/organ/eyes = internal_organs_by_name[O_EYES] + if(eyes && istype(eyes) && !(eyes.status & ORGAN_CUT_AWAY)) + return 1 + return 0 + +/mob/living/carbon/human/slip(var/slipped_on, stun_duration=8) + var/list/equipment = list(src.w_uniform,src.wear_suit,src.shoes) + var/footcoverage_check = FALSE + for(var/obj/item/clothing/C in equipment) + if(C.body_parts_covered & FEET) + footcoverage_check = TRUE + break + if((species.flags & NO_SLIP && !footcoverage_check) || (shoes && (shoes.item_flags & NOSLIP))) //Footwear negates a species' natural traction. + return 0 + if(..(slipped_on,stun_duration)) + return 1 + +/mob/living/carbon/human/proc/relocate() + set category = "Object" + set name = "Relocate Joint" + set desc = "Pop a joint back into place. Extremely painful." + set src in view(1) + + if(!isliving(usr) || !usr.checkClickCooldown()) + return + + usr.setClickCooldown(20) + + if(usr.stat > 0) + to_chat(usr, "You are unconcious and cannot do that!") + return + + if(usr.restrained()) + to_chat(usr, "You are restrained and cannot do that!") + return + + var/mob/S = src + var/mob/U = usr + var/self = null + if(S == U) + self = 1 // Removing object from yourself. + + var/list/limbs = list() + for(var/limb in organs_by_name) + var/obj/item/organ/external/current_limb = organs_by_name[limb] + if(current_limb && current_limb.dislocated > 0 && !current_limb.is_parent_dislocated()) //if the parent is also dislocated you will have to relocate that first + limbs |= current_limb + var/obj/item/organ/external/current_limb = tgui_input_list(usr, "Which joint do you wish to relocate?", "Joint Choice", limbs) + + if(!current_limb) + return + + if(self) + to_chat(src, "You brace yourself to relocate your [current_limb.joint]...") + else + to_chat(U, "You begin to relocate [S]'s [current_limb.joint]...") + + if(!do_after(U, 30)) + return + if(!current_limb || !S || !U) + return + + if(self) + to_chat(src, "You pop your [current_limb.joint] back in!") + else + to_chat(U, "You pop [S]'s [current_limb.joint] back in!") + to_chat(S, "[U] pops your [current_limb.joint] back in!") + current_limb.relocate() + +/mob/living/carbon/human/drop_from_inventory(var/obj/item/W, var/atom/target = null) + return !(W in organs) && ..() + +/mob/living/carbon/human/reset_view(atom/A, update_hud = 1) + ..() + if(update_hud) + handle_regular_hud_updates() + +/mob/living/carbon/human/Check_Shoegrip() + if(shoes && (shoes.item_flags & NOSLIP) && istype(shoes, /obj/item/clothing/shoes/magboots)) //magboots + dense_object = no floating + return 1 + if(flying) //VOREStation Edit. Checks to see if they have wings and are flying. + return 1 //VOREStation Edit. + return 0 + +//Puts the item into our active hand if possible. returns 1 on success. +/mob/living/carbon/human/put_in_active_hand(var/obj/item/W) + return (hand ? put_in_l_hand(W) : put_in_r_hand(W)) + +//Puts the item into our inactive hand if possible. returns 1 on success. +/mob/living/carbon/human/put_in_inactive_hand(var/obj/item/W) + return (hand ? put_in_r_hand(W) : put_in_l_hand(W)) + +/mob/living/carbon/human/put_in_hands(var/obj/item/W) + if(!W) + return 0 + if(put_in_active_hand(W)) + update_inv_l_hand() + update_inv_r_hand() + return 1 + else if(put_in_inactive_hand(W)) + update_inv_l_hand() + update_inv_r_hand() + return 1 + else + return ..() + +/mob/living/carbon/human/put_in_l_hand(var/obj/item/W) + if(!..() || l_hand) + return 0 + W.forceMove(src) + l_hand = W + W.equipped(src,slot_l_hand) + W.add_fingerprint(src) + update_inv_l_hand() + return 1 + +/mob/living/carbon/human/put_in_r_hand(var/obj/item/W) + if(!..() || r_hand) + return 0 + W.forceMove(src) + r_hand = W + W.equipped(src,slot_r_hand) + W.add_fingerprint(src) + update_inv_r_hand() + return 1 + +/mob/living/carbon/human/can_stand_overridden() + if(wearing_rig && wearing_rig.ai_can_move_suit(check_for_ai = 1)) + // Actually missing a leg will screw you up. Everything else can be compensated for. + for(var/limbcheck in list("l_leg","r_leg")) + var/obj/item/organ/affecting = get_organ(limbcheck) + if(!affecting) + return 0 + return 1 + return 0 + +/mob/living/carbon/human/verb/toggle_underwear() + set name = "Toggle Underwear" + set desc = "Shows/hides selected parts of your underwear." + set category = "Object" + + if(stat) return + var/datum/category_group/underwear/UWC = tgui_input_list(usr, "Choose underwear:", "Show/hide underwear", global_underwear.categories) + if(!UWC) return + var/datum/category_item/underwear/UWI = all_underwear[UWC.name] + if(!UWI || UWI.name == "None") + to_chat(src, "You do not have [UWC.gender==PLURAL ? "[UWC.display_name]" : "a [UWC.display_name]"].") + return + hide_underwear[UWC.name] = !hide_underwear[UWC.name] + update_underwear(1) + to_chat(src, "You [hide_underwear[UWC.name] ? "take off" : "put on"] your [UWC.display_name].") + return + +/mob/living/carbon/human/verb/pull_punches() + set name = "Pull Punches" + set desc = "Try not to hurt them." + set category = "IC" + + if(stat) return + pulling_punches = !pulling_punches + to_chat(src, "You are now [pulling_punches ? "pulling your punches" : "not pulling your punches"].") + return + +/mob/living/carbon/human/should_have_organ(var/organ_check) + + var/obj/item/organ/external/affecting + if(organ_check in list(O_HEART, O_LUNGS)) + affecting = organs_by_name[BP_TORSO] + else if(organ_check in list(O_LIVER, O_KIDNEYS)) + affecting = organs_by_name[BP_GROIN] + + if(affecting && (affecting.robotic >= ORGAN_ROBOT)) + return 0 + return (species && species.has_organ[organ_check]) + +/mob/living/carbon/human/can_feel_pain(var/obj/item/organ/check_organ) + if(isSynthetic()) + return 0 + for(var/datum/modifier/M in modifiers) + if(M.pain_immunity == TRUE) + return 0 + if(check_organ) + if(!istype(check_organ)) + return 0 + return check_organ.organ_can_feel_pain() + return !(species.flags & NO_PAIN) + +/mob/living/carbon/human/is_sentient() + if(get_FBP_type() == FBP_DRONE) + return FALSE + return ..() + +/mob/living/carbon/human/is_muzzled() + return (wear_mask && (istype(wear_mask, /obj/item/clothing/mask/muzzle) || istype(src.wear_mask, /obj/item/weapon/grenade))) + +/mob/living/carbon/human/get_fire_icon_state() + return species.fire_icon_state + +// Called by job_controller. Makes drones start with a permit, might be useful for other people later too. +/mob/living/carbon/human/equip_post_job() + var/braintype = get_FBP_type() + if(braintype == FBP_DRONE) + var/turf/T = get_turf(src) + var/obj/item/clothing/accessory/permit/drone/permit = new(T) + permit.set_name(real_name) + equip_to_appropriate_slot(permit) // If for some reason it can't find room, it'll still be on the floor. + +/mob/living/carbon/human/proc/update_icon_special() //For things such as teshari hiding and whatnot. + if(status_flags & HIDING) // Hiding? Carry on. + if(stat == DEAD || paralysis || weakened || stunned || restrained() || buckled || LAZYLEN(grabbed_by) || has_buckled_mobs()) //stunned/knocked down by something that isn't the rest verb? Note: This was tried with INCAPACITATION_STUNNED, but that refused to work. //VORE EDIT: Check for has_buckled_mobs() (taur riding) + reveal(null) + else + layer = HIDING_LAYER + +/mob/living/carbon/human/examine_icon() + var/icon/I = get_cached_examine_icon(src) + if(!I) + I = getFlatIcon(src, defdir = SOUTH, no_anim = TRUE) + set_cached_examine_icon(src, I, 50 SECONDS) + return I + +/mob/living/carbon/human/proc/get_display_species() + //Shows species in tooltip + if(src.custom_species) //VOREStation Add + return custom_species //VOREStation Add + //Beepboops get special text if obviously beepboop + if(looksSynthetic()) + if(gender == MALE) + return "Android" + else if(gender == FEMALE) + return "Gynoid" + else + return "Synthetic" + //Else species name + if(species) + return species.get_examine_name() + //Else CRITICAL FAILURE! + return "" + +/mob/living/carbon/human/get_nametag_name(mob/user) + return name //Could do fancy stuff here? + +/mob/living/carbon/human/get_nametag_desc(mob/user) + var/msg = "" + if(hasHUD(user,"security")) + //Try to find their name + var/perpname + var/obj/item/weapon/card/id/I = GetIdCard() + if(I) + perpname = I.registered_name + else + perpname = name + //Try to find their record + var/criminal = "None" + if(perpname) + var/datum/data/record/G = find_general_record("name", perpname) + if(G) + var/datum/data/record/S = find_security_record("id", G.fields["id"]) + if(S) + criminal = S.fields["criminal"] + //If it's interesting, append + if(criminal != "None") + msg += "([criminal]) " + + if(hasHUD(user,"medical")) + msg += "(Health: [round((health/getMaxHealth())*100)]%) " + + msg += get_display_species() + return msg + +/mob/living/carbon/human/reduce_cuff_time() + if(istype(gloves, /obj/item/clothing/gloves/gauntlets/rig)) + return 2 + return ..() + +/mob/living/carbon/human/pull_damage() + if(((health - halloss) <= config.health_threshold_softcrit)) + for(var/name in organs_by_name) + var/obj/item/organ/external/e = organs_by_name[name] + if(!e) + continue + if((e.status & ORGAN_BROKEN && (!e.splinted || (e.splinted in e.contents && prob(30))) || e.status & ORGAN_BLEEDING) && (getBruteLoss() + getFireLoss() >= 100)) + return 1 + else + return ..() + +// Drag damage is handled in a parent +/mob/living/carbon/human/dragged(var/mob/living/dragger, var/oldloc) + var/area/A = get_area(src) + if(lying && !buckled && A.has_gravity() && prob(getBruteLoss() * 200 / maxHealth)) + var/bloodtrail = 1 + if(species?.flags & NO_BLOOD) + bloodtrail = 0 + else + var/blood_volume = vessel.get_reagent_amount("blood") + if(blood_volume < species?.blood_volume*species?.blood_level_fatal) + bloodtrail = 0 //Most of it's gone already, just leave it be + else + remove_blood(1) + if(bloodtrail) + if(istype(loc, /turf/simulated)) + var/turf/T = loc + T.add_blood(src) + . = ..() + +// Tries to turn off item-based things that let you see through walls, like mesons. +// Certain stuff like genetic xray vision is allowed to be kept on. +/mob/living/carbon/human/disable_spoiler_vision() + // Glasses. + if(istype(glasses, /obj/item/clothing/glasses)) + var/obj/item/clothing/glasses/goggles = glasses + if(goggles.active && (goggles.vision_flags & (SEE_TURFS|SEE_OBJS))) + goggles.toggle_active(src) + to_chat(src, span("warning", "Your [goggles.name] have suddenly turned off!")) + + // RIGs. + var/obj/item/weapon/rig/rig = get_rig() + if(istype(rig) && rig.visor?.active && rig.visor.vision?.glasses) + var/obj/item/clothing/glasses/rig_goggles = rig.visor.vision.glasses + if(rig_goggles.vision_flags & (SEE_TURFS|SEE_OBJS)) + rig.visor.deactivate() + to_chat(src, span("warning", "\The [rig]'s visor has shuddenly deactivated!")) + + ..() + +/mob/living/carbon/human/get_mob_riding_slots() + return list(back, head, wear_suit) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 16acfd71310..71ea0a949e7 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -1,479 +1,479 @@ -/mob/living/carbon/human - var/datum/unarmed_attack/default_attack - -/mob/living/carbon/human/proc/get_unarmed_attack(var/mob/living/carbon/human/target, var/hit_zone) - // VOREStation Edit - Begin - if(nif && nif.flag_check(NIF_C_HARDCLAWS,NIF_FLAGS_COMBAT)){return unarmed_hardclaws} - if(src.default_attack && src.default_attack.is_usable(src, target, hit_zone)) - if(pulling_punches) - var/datum/unarmed_attack/soft_type = src.default_attack.get_sparring_variant() - if(soft_type) - return soft_type - return src.default_attack - // VOREStation Edit - End - if(src.gloves) - var/obj/item/clothing/gloves/G = src.gloves - if(istype(G) && G.special_attack && G.special_attack.is_usable(src, target, hit_zone)) - if(pulling_punches) - var/datum/unarmed_attack/soft_type = G.special_attack.get_sparring_variant() - if(soft_type) - return soft_type - return G.special_attack - if(src.default_attack && src.default_attack.is_usable(src, target, hit_zone)) - if(pulling_punches) - var/datum/unarmed_attack/soft_type = src.default_attack.get_sparring_variant() - if(soft_type) - return soft_type - return src.default_attack - for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) - if(u_attack.is_usable(src, target, hit_zone)) - if(pulling_punches) - var/datum/unarmed_attack/soft_variant = u_attack.get_sparring_variant() - if(soft_variant) - return soft_variant - return u_attack - return null - -/mob/living/carbon/human/attack_hand(mob/living/M as mob) - var/datum/gender/TT = gender_datums[M.get_visible_gender()] - var/mob/living/carbon/human/H = M - if(istype(H)) - var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] - if(H.hand) - temp = H.organs_by_name["l_hand"] - if(!temp || !temp.is_usable()) - to_chat(H, "You can't use your hand.") - return - if(H.lying) - return - M.break_cloak() - - ..() - - // Should this all be in Touch()? - if(istype(H)) - if(H.get_accuracy_penalty() && H != src) //Should only trigger if they're not aiming well - var/hit_zone = get_zone_with_miss_chance(H.zone_sel.selecting, src, H.get_accuracy_penalty()) - if(!hit_zone) - H.do_attack_animation(src) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[H] reaches for [src], but misses!") - return FALSE - - if(H != src && check_shields(0, null, H, H.zone_sel.selecting, H.name)) - H.do_attack_animation(src) - return FALSE - - if(istype(M,/mob/living/carbon)) - var/mob/living/carbon/C = M - C.spread_disease_to(src, "Contact") - - switch(M.a_intent) - if(I_HELP) - - // VOREStation Edit - Begin - if (istype(H) && attempt_to_scoop(H)) - return 0; - // VOREStation Edit - End - if(istype(H) && health < config.health_threshold_crit) - if(!H.check_has_mouth()) - to_chat(H, "You don't have a mouth, you cannot perform CPR!") - return - if(!check_has_mouth()) - to_chat(H, "They don't have a mouth, you cannot perform CPR!") - return - if((H.head && (H.head.body_parts_covered & FACE)) || (H.wear_mask && (H.wear_mask.body_parts_covered & FACE))) - to_chat(H, "Remove your mask!") - return 0 - if((head && (head.body_parts_covered & FACE)) || (wear_mask && (wear_mask.body_parts_covered & FACE))) - to_chat(H, "Remove [src]'s mask!") - return 0 - - if (!cpr_time) - return 0 - - cpr_time = 0 - spawn(30) - cpr_time = 1 - - H.visible_message("\The [H] is trying to perform CPR on \the [src]!") - - if(!do_after(H, 30)) - return - - H.visible_message("\The [H] performs CPR on \the [src]!") - to_chat(H, "Repeat at least every 7 seconds.") - - if(istype(H) && health > config.health_threshold_dead) - adjustOxyLoss(-(min(getOxyLoss(), 5))) - updatehealth() - to_chat(src, "You feel a breath of fresh air enter your lungs. It feels good.") - - else if(!(M == src && apply_pressure(M, M.zone_sel.selecting))) - help_shake_act(M) - return TRUE - - if(I_GRAB) - if(M == src || anchored) - return 0 - for(var/obj/item/weapon/grab/G in src.grabbed_by) - if(G.assailant == M) - to_chat(M, "You already grabbed [src].") - return - if(w_uniform) - w_uniform.add_fingerprint(M) - - var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src) - if(buckled) - to_chat(M, "You cannot grab [src], [TT.he] is buckled in!") - return - if(!G) //the grab will delete itself in New if affecting is anchored - return - M.put_in_active_hand(G) - G.synch() - LAssailant = M - - H.do_attack_animation(src) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - //VORESTATION EDIT - visible_message("[M] has grabbed [src] [(M.zone_sel.selecting == BP_L_HAND || M.zone_sel.selecting == BP_R_HAND)? "by [(gender==FEMALE)? "her" : ((gender==MALE)? "his": "their")] hands": "passively"]!") - //VORESTATION END END - return TRUE - - if(I_HURT) - - if(M.zone_sel.selecting == "mouth" && wear_mask && istype(wear_mask, /obj/item/weapon/grenade)) - var/obj/item/weapon/grenade/G = wear_mask - if(!G.active) - visible_message("\The [M] pulls the pin from \the [src]'s [G.name]!") - G.activate(M) - update_inv_wear_mask() - else - to_chat(M, "\The [G] is already primed! Run!") - return - - if(!istype(H)) - attack_generic(H,rand(1,3),"punched") - return - - var/rand_damage = rand(1, 5) - var/block = 0 - var/accurate = 0 - var/hit_zone = H.zone_sel.selecting - var/obj/item/organ/external/affecting = get_organ(hit_zone) - - if(!affecting || affecting.is_stump()) - to_chat(M, "They are missing that limb!") - return TRUE - - switch(src.a_intent) - if(I_HELP) - // We didn't see this coming, so we get the full blow - rand_damage = 5 - accurate = 1 - if(I_HURT, I_GRAB) - // We're in a fighting stance, there's a chance we block - if(src.canmove && src!=H && prob(20)) - block = 1 - - if (M.grabbed_by.len) - // Someone got a good grip on them, they won't be able to do much damage - rand_damage = max(1, rand_damage - 2) - - if(src.grabbed_by.len || src.buckled || !src.canmove || src==H) - accurate = 1 // certain circumstances make it impossible for us to evade punches - rand_damage = 5 - - // Process evasion and blocking - var/miss_type = 0 - var/attack_message - if(!accurate) - /* ~Hubblenaut - This place is kind of convoluted and will need some explaining. - ran_zone() will pick out of 11 zones, thus the chance for hitting - our target where we want to hit them is circa 9.1%. - - Now since we want to statistically hit our target organ a bit more - often than other organs, we add a base chance of 20% for hitting it. - - This leaves us with the following chances: - - If aiming for chest: - 27.3% chance you hit your target organ - 70.5% chance you hit a random other organ - 2.2% chance you miss - - If aiming for something else: - 23.2% chance you hit your target organ - 56.8% chance you hit a random other organ - 15.0% chance you miss - - Note: We don't use get_zone_with_miss_chance() here since the chances - were made for projectiles. - TODO: proc for melee combat miss chances depending on organ? - */ - - if(!hit_zone) - attack_message = "[H] attempted to strike [src], but missed!" - miss_type = 1 - - if(prob(80)) - hit_zone = ran_zone(hit_zone, 70) //70% chance to hit what you're aiming at seems fair? - if(prob(15) && hit_zone != BP_TORSO) // Missed! - if(!src.lying) - attack_message = "[H] attempted to strike [src], but missed!" - else - attack_message = "[H] attempted to strike [src], but [TT.he] rolled out of the way!" - src.set_dir(pick(cardinal)) - miss_type = 1 - - if(!miss_type && block) - attack_message = "[H] went for [src]'s [affecting.name] but was blocked!" - miss_type = 2 - - // See what attack they use - var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, hit_zone) - if(!attack) - return FALSE - - if(attack.unarmed_override(H, src, hit_zone)) - return FALSE - - H.do_attack_animation(src) - if(!attack_message) - attack.show_attack(H, src, hit_zone, rand_damage) - else - H.visible_message("[attack_message]") - - playsound(src, ((miss_type) ? (miss_type == 1 ? attack.miss_sound : 'sound/weapons/thudswoosh.ogg') : attack.attack_sound), 25, 1, -1) - - add_attack_logs(H,src,"Melee attacked with fists (miss/block)") - - if(miss_type) - return FALSE - - var/real_damage = rand_damage - var/hit_dam_type = attack.damage_type - real_damage += attack.get_unarmed_damage(H) - if(H.gloves) - if(istype(H.gloves, /obj/item/clothing/gloves)) - var/obj/item/clothing/gloves/G = H.gloves - real_damage += G.punch_force - hit_dam_type = G.punch_damtype - if(H.pulling_punches && !attack.sharp && !attack.edge) //SO IT IS DECREED: PULLING PUNCHES WILL PREVENT THE ACTUAL DAMAGE FROM RINGS AND KNUCKLES, BUT NOT THE ADDED PAIN, BUT YOU CAN'T "PULL" A KNIFE - hit_dam_type = AGONY - real_damage *= damage_multiplier - rand_damage *= damage_multiplier - if(HULK in H.mutations) - real_damage *= 2 // Hulks do twice the damage - rand_damage *= 2 - real_damage = max(1, real_damage) - - var/armour = run_armor_check(hit_zone, "melee") - var/soaked = get_armor_soak(hit_zone, "melee") - // Apply additional unarmed effects. - attack.apply_effects(H, src, armour, rand_damage, hit_zone) - - // Finally, apply damage to target - apply_damage(real_damage, hit_dam_type, hit_zone, armour, soaked, sharp=attack.sharp, edge=attack.edge) - - if(I_DISARM) - add_attack_logs(H,src,"Disarmed") - - M.do_attack_animation(src) - - if(w_uniform) - w_uniform.add_fingerprint(M) - var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_sel.selecting)) - - var/list/holding = list(get_active_hand() = 40, get_inactive_hand = 20) - - //See if they have any guns that might go off - for(var/obj/item/weapon/gun/W in holding) - if(W && prob(holding[W])) - var/list/turfs = list() - for(var/turf/T in view()) - turfs += T - if(turfs.len) - var/turf/target = pick(turfs) - visible_message("[src]'s [W] goes off during the struggle!") - return W.afterattack(target,src) - - if(last_push_time + 30 > world.time) - visible_message("[M] has weakly pushed [src]!") - return - - var/randn = rand(1, 100) - last_push_time = world.time - // We ARE wearing shoes OR - // We as a species CAN be slipped when barefoot - // And also 1 in 4 because rngesus - if((shoes || !(species.flags & NO_SLIP)) && randn <= 25) - var/armor_check = run_armor_check(affecting, "melee") - apply_effect(3, WEAKEN, armor_check) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - if(armor_check < 60) - visible_message("[M] has pushed [src]!") - else - visible_message("[M] attempted to push [src]!") - return - - if(randn <= 60) - //See about breaking grips or pulls - if(break_all_grabs(M)) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - - //Actually disarm them - for(var/obj/item/I in holding) - if(I) - drop_from_inventory(I) - visible_message("[M] has disarmed [src]!") - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(" [M] attempted to disarm [src]!") - return - -/mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params) - return - -/mob/living/carbon/human/attack_generic(var/mob/user, var/damage, var/attack_message, var/armor_type = "melee", var/armor_pen = 0, var/a_sharp = 0, var/a_edge = 0) - - if(!damage) - return - - add_attack_logs(user,src,"Melee attacked with fists (miss/block)",admin_notify = FALSE) //No admin notice since this is usually fighting simple animals - src.visible_message("[user] has [attack_message] [src]!") - user.do_attack_animation(src) - - var/dam_zone = pick(organs_by_name) - var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) - var/armor_block = run_armor_check(affecting, armor_type, armor_pen) - var/armor_soak = get_armor_soak(affecting, armor_type, armor_pen) - apply_damage(damage, BRUTE, affecting, armor_block, armor_soak, sharp = a_sharp, edge = a_edge) - updatehealth() - return TRUE - -//Used to attack a joint through grabbing -/mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone) - var/has_grab = 0 - for(var/obj/item/weapon/grab/G in list(user.l_hand, user.r_hand)) - if(G.affecting == src && G.state == GRAB_NECK) - has_grab = 1 - break - - if(!has_grab) - return FALSE - - if(!def_zone) def_zone = user.zone_sel.selecting - var/target_zone = check_zone(def_zone) - if(!target_zone) - return FALSE - var/obj/item/organ/external/organ = get_organ(check_zone(target_zone)) - if(!organ || organ.dislocated > 0 || organ.dislocated == -1) //don't use is_dislocated() here, that checks parent - return FALSE - - user.visible_message("[user] begins to dislocate [src]'s [organ.joint]!") - if(do_after(user, 100)) - organ.dislocate(1) - src.visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") - return TRUE - return FALSE - -//Breaks all grips and pulls that the mob currently has. -/mob/living/carbon/human/proc/break_all_grabs(mob/living/carbon/user) - var/success = FALSE - if(pulling) - visible_message("[user] has broken [src]'s grip on [pulling]!") - success = TRUE - stop_pulling() - - if(istype(l_hand, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/lgrab = l_hand - if(lgrab.affecting) - visible_message("[user] has broken [src]'s grip on [lgrab.affecting]!") - success = TRUE - spawn(1) - qdel(lgrab) - if(istype(r_hand, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/rgrab = r_hand - if(rgrab.affecting) - visible_message("[user] has broken [src]'s grip on [rgrab.affecting]!") - success = TRUE - spawn(1) - qdel(rgrab) - return success - -/* - We want to ensure that a mob may only apply pressure to one organ of one mob at any given time. Currently this is done mostly implicitly through - the behaviour of do_after() and the fact that applying pressure to someone else requires a grab: - If you are applying pressure to yourself and attempt to grab someone else, you'll change what you are holding in your active hand which will stop do_mob() - If you are applying pressure to another and attempt to apply pressure to yourself, you'll have to switch to an empty hand which will also stop do_mob() - Changing targeted zones should also stop do_mob(), preventing you from applying pressure to more than one body part at once. -*/ -/mob/living/carbon/human/proc/apply_pressure(mob/living/user, var/target_zone) - var/obj/item/organ/external/organ = get_organ(target_zone) - if(!organ || !(organ.status & ORGAN_BLEEDING) || (organ.robotic >= ORGAN_ROBOT)) - return FALSE - - if(organ.applied_pressure) - var/message = "Someone is already applying pressure to [user == src ? "your [organ.name]" : "[src]'s [organ.name]"]." - to_chat(user,message) - return FALSE - - var/datum/gender/TU = gender_datums[user.get_visible_gender()] - - if(user == src) - user.visible_message("\The [user] starts applying pressure to [TU.his] [organ.name]!", "You start applying pressure to your [organ.name]!") - else - user.visible_message("\The [user] starts applying pressure to [src]'s [organ.name]!", "You start applying pressure to [src]'s [organ.name]!") - spawn(0) - organ.applied_pressure = user - - //apply pressure as long as they stay still and keep grabbing - do_mob(user, src, INFINITY, target_zone, progress = 0) - - organ.applied_pressure = null - - if(user == src) - user.visible_message("\The [user] stops applying pressure to [TU.his] [organ.name]!", "You stop applying pressure to your [organ]!") - else - user.visible_message("\The [user] stops applying pressure to [src]'s [organ.name]!", "You stop applying pressure to [src]'s [organ.name]!") - - return TRUE - -/mob/living/carbon/human/verb/check_attacks() - set name = "Check Attacks" - set category = "IC" - set src = usr - - var/dat = "Known Attacks

                    " - - if(default_attack) - dat += "Current default attack: [default_attack.attack_name] - reset

                    " - - for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) - if(u_attack == default_attack) - dat += "Primarily [u_attack.attack_name] - default - reset


                    " - else - dat += "Primarily [u_attack.attack_name] - set default


                    " - - src << browse(dat, "window=checkattack") - -/mob/living/carbon/human/Topic(href, href_list) - if(href_list["default_attk"]) - if(href_list["default_attk"] == "reset_attk") - set_default_attack(null) - else - var/datum/unarmed_attack/u_attack = locate(href_list["default_attk"]) - if(u_attack && (u_attack in species.unarmed_attacks)) - set_default_attack(u_attack) - check_attacks() - return 1 - else - return ..() - -/mob/living/carbon/human/proc/set_default_attack(var/datum/unarmed_attack/u_attack) - default_attack = u_attack +/mob/living/carbon/human + var/datum/unarmed_attack/default_attack + +/mob/living/carbon/human/proc/get_unarmed_attack(var/mob/living/carbon/human/target, var/hit_zone) + // VOREStation Edit - Begin + if(nif && nif.flag_check(NIF_C_HARDCLAWS,NIF_FLAGS_COMBAT)){return unarmed_hardclaws} + if(src.default_attack && src.default_attack.is_usable(src, target, hit_zone)) + if(pulling_punches) + var/datum/unarmed_attack/soft_type = src.default_attack.get_sparring_variant() + if(soft_type) + return soft_type + return src.default_attack + // VOREStation Edit - End + if(src.gloves) + var/obj/item/clothing/gloves/G = src.gloves + if(istype(G) && G.special_attack && G.special_attack.is_usable(src, target, hit_zone)) + if(pulling_punches) + var/datum/unarmed_attack/soft_type = G.special_attack.get_sparring_variant() + if(soft_type) + return soft_type + return G.special_attack + if(src.default_attack && src.default_attack.is_usable(src, target, hit_zone)) + if(pulling_punches) + var/datum/unarmed_attack/soft_type = src.default_attack.get_sparring_variant() + if(soft_type) + return soft_type + return src.default_attack + for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) + if(u_attack.is_usable(src, target, hit_zone)) + if(pulling_punches) + var/datum/unarmed_attack/soft_variant = u_attack.get_sparring_variant() + if(soft_variant) + return soft_variant + return u_attack + return null + +/mob/living/carbon/human/attack_hand(mob/living/M as mob) + var/datum/gender/TT = gender_datums[M.get_visible_gender()] + var/mob/living/carbon/human/H = M + if(istype(H)) + var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] + if(H.hand) + temp = H.organs_by_name["l_hand"] + if(!temp || !temp.is_usable()) + to_chat(H, "You can't use your hand.") + return + if(H.lying) + return + M.break_cloak() + + ..() + + // Should this all be in Touch()? + if(istype(H)) + if(H.get_accuracy_penalty() && H != src) //Should only trigger if they're not aiming well + var/hit_zone = get_zone_with_miss_chance(H.zone_sel.selecting, src, H.get_accuracy_penalty()) + if(!hit_zone) + H.do_attack_animation(src) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("[span_red("[H] reaches for [src], but misses!")]") + return FALSE + + if(H != src && check_shields(0, null, H, H.zone_sel.selecting, H.name)) + H.do_attack_animation(src) + return FALSE + + if(istype(M,/mob/living/carbon)) + var/mob/living/carbon/C = M + C.spread_disease_to(src, "Contact") + + switch(M.a_intent) + if(I_HELP) + + // VOREStation Edit - Begin + if (istype(H) && attempt_to_scoop(H)) + return 0; + // VOREStation Edit - End + if(istype(H) && health < config.health_threshold_crit) + if(!H.check_has_mouth()) + to_chat(H, "You don't have a mouth, you cannot perform CPR!") + return + if(!check_has_mouth()) + to_chat(H, "They don't have a mouth, you cannot perform CPR!") + return + if((H.head && (H.head.body_parts_covered & FACE)) || (H.wear_mask && (H.wear_mask.body_parts_covered & FACE))) + to_chat(H, "Remove your mask!") + return 0 + if((head && (head.body_parts_covered & FACE)) || (wear_mask && (wear_mask.body_parts_covered & FACE))) + to_chat(H, "Remove [src]'s mask!") + return 0 + + if (!cpr_time) + return 0 + + cpr_time = 0 + spawn(30) + cpr_time = 1 + + H.visible_message("\The [H] is trying to perform CPR on \the [src]!") + + if(!do_after(H, 30)) + return + + H.visible_message("\The [H] performs CPR on \the [src]!") + to_chat(H, "Repeat at least every 7 seconds.") + + if(istype(H) && health > config.health_threshold_dead) + adjustOxyLoss(-(min(getOxyLoss(), 5))) + updatehealth() + to_chat(src, "You feel a breath of fresh air enter your lungs. It feels good.") + + else if(!(M == src && apply_pressure(M, M.zone_sel.selecting))) + help_shake_act(M) + return TRUE + + if(I_GRAB) + if(M == src || anchored) + return 0 + for(var/obj/item/weapon/grab/G in src.grabbed_by) + if(G.assailant == M) + to_chat(M, "You already grabbed [src].") + return + if(w_uniform) + w_uniform.add_fingerprint(M) + + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src) + if(buckled) + to_chat(M, "You cannot grab [src], [TT.he] is buckled in!") + return + if(!G) //the grab will delete itself in New if affecting is anchored + return + M.put_in_active_hand(G) + G.synch() + LAssailant = M + + H.do_attack_animation(src) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + //VORESTATION EDIT + visible_message("[M] has grabbed [src] [(M.zone_sel.selecting == BP_L_HAND || M.zone_sel.selecting == BP_R_HAND)? "by [(gender==FEMALE)? "her" : ((gender==MALE)? "his": "their")] hands": "passively"]!") + //VORESTATION END END + return TRUE + + if(I_HURT) + + if(M.zone_sel.selecting == "mouth" && wear_mask && istype(wear_mask, /obj/item/weapon/grenade)) + var/obj/item/weapon/grenade/G = wear_mask + if(!G.active) + visible_message("\The [M] pulls the pin from \the [src]'s [G.name]!") + G.activate(M) + update_inv_wear_mask() + else + to_chat(M, "\The [G] is already primed! Run!") + return + + if(!istype(H)) + attack_generic(H,rand(1,3),"punched") + return + + var/rand_damage = rand(1, 5) + var/block = 0 + var/accurate = 0 + var/hit_zone = H.zone_sel.selecting + var/obj/item/organ/external/affecting = get_organ(hit_zone) + + if(!affecting || affecting.is_stump()) + to_chat(M, "They are missing that limb!") + return TRUE + + switch(src.a_intent) + if(I_HELP) + // We didn't see this coming, so we get the full blow + rand_damage = 5 + accurate = 1 + if(I_HURT, I_GRAB) + // We're in a fighting stance, there's a chance we block + if(src.canmove && src!=H && prob(20)) + block = 1 + + if (M.grabbed_by.len) + // Someone got a good grip on them, they won't be able to do much damage + rand_damage = max(1, rand_damage - 2) + + if(src.grabbed_by.len || src.buckled || !src.canmove || src==H) + accurate = 1 // certain circumstances make it impossible for us to evade punches + rand_damage = 5 + + // Process evasion and blocking + var/miss_type = 0 + var/attack_message + if(!accurate) + /* ~Hubblenaut + This place is kind of convoluted and will need some explaining. + ran_zone() will pick out of 11 zones, thus the chance for hitting + our target where we want to hit them is circa 9.1%. + + Now since we want to statistically hit our target organ a bit more + often than other organs, we add a base chance of 20% for hitting it. + + This leaves us with the following chances: + + If aiming for chest: + 27.3% chance you hit your target organ + 70.5% chance you hit a random other organ + 2.2% chance you miss + + If aiming for something else: + 23.2% chance you hit your target organ + 56.8% chance you hit a random other organ + 15.0% chance you miss + + Note: We don't use get_zone_with_miss_chance() here since the chances + were made for projectiles. + TODO: proc for melee combat miss chances depending on organ? + */ + + if(!hit_zone) + attack_message = "[H] attempted to strike [src], but missed!" + miss_type = 1 + + if(prob(80)) + hit_zone = ran_zone(hit_zone, 70) //70% chance to hit what you're aiming at seems fair? + if(prob(15) && hit_zone != BP_TORSO) // Missed! + if(!src.lying) + attack_message = "[H] attempted to strike [src], but missed!" + else + attack_message = "[H] attempted to strike [src], but [TT.he] rolled out of the way!" + src.set_dir(pick(cardinal)) + miss_type = 1 + + if(!miss_type && block) + attack_message = "[H] went for [src]'s [affecting.name] but was blocked!" + miss_type = 2 + + // See what attack they use + var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, hit_zone) + if(!attack) + return FALSE + + if(attack.unarmed_override(H, src, hit_zone)) + return FALSE + + H.do_attack_animation(src) + if(!attack_message) + attack.show_attack(H, src, hit_zone, rand_damage) + else + H.visible_message("[attack_message]") + + playsound(src, ((miss_type) ? (miss_type == 1 ? attack.miss_sound : 'sound/weapons/thudswoosh.ogg') : attack.attack_sound), 25, 1, -1) + + add_attack_logs(H,src,"Melee attacked with fists (miss/block)") + + if(miss_type) + return FALSE + + var/real_damage = rand_damage + var/hit_dam_type = attack.damage_type + real_damage += attack.get_unarmed_damage(H) + if(H.gloves) + if(istype(H.gloves, /obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = H.gloves + real_damage += G.punch_force + hit_dam_type = G.punch_damtype + if(H.pulling_punches && !attack.sharp && !attack.edge) //SO IT IS DECREED: PULLING PUNCHES WILL PREVENT THE ACTUAL DAMAGE FROM RINGS AND KNUCKLES, BUT NOT THE ADDED PAIN, BUT YOU CAN'T "PULL" A KNIFE + hit_dam_type = AGONY + real_damage *= damage_multiplier + rand_damage *= damage_multiplier + if(HULK in H.mutations) + real_damage *= 2 // Hulks do twice the damage + rand_damage *= 2 + real_damage = max(1, real_damage) + + var/armour = run_armor_check(hit_zone, "melee") + var/soaked = get_armor_soak(hit_zone, "melee") + // Apply additional unarmed effects. + attack.apply_effects(H, src, armour, rand_damage, hit_zone) + + // Finally, apply damage to target + apply_damage(real_damage, hit_dam_type, hit_zone, armour, soaked, sharp=attack.sharp, edge=attack.edge) + + if(I_DISARM) + add_attack_logs(H,src,"Disarmed") + + M.do_attack_animation(src) + + if(w_uniform) + w_uniform.add_fingerprint(M) + var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_sel.selecting)) + + var/list/holding = list(get_active_hand() = 40, get_inactive_hand = 20) + + //See if they have any guns that might go off + for(var/obj/item/weapon/gun/W in holding) + if(W && prob(holding[W])) + var/list/turfs = list() + for(var/turf/T in view()) + turfs += T + if(turfs.len) + var/turf/target = pick(turfs) + visible_message("[src]'s [W] goes off during the struggle!") + return W.afterattack(target,src) + + if(last_push_time + 30 > world.time) + visible_message("[M] has weakly pushed [src]!") + return + + var/randn = rand(1, 100) + last_push_time = world.time + // We ARE wearing shoes OR + // We as a species CAN be slipped when barefoot + // And also 1 in 4 because rngesus + if((shoes || !(species.flags & NO_SLIP)) && randn <= 25) + var/armor_check = run_armor_check(affecting, "melee") + apply_effect(3, WEAKEN, armor_check) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + if(armor_check < 60) + visible_message("[M] has pushed [src]!") + else + visible_message("[M] attempted to push [src]!") + return + + if(randn <= 60) + //See about breaking grips or pulls + if(break_all_grabs(M)) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + + //Actually disarm them + for(var/obj/item/I in holding) + if(I) + drop_from_inventory(I) + visible_message("[M] has disarmed [src]!") + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("[span_red("[M] attempted to disarm [src]!")]") + return + +/mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params) + return + +/mob/living/carbon/human/attack_generic(var/mob/user, var/damage, var/attack_message, var/armor_type = "melee", var/armor_pen = 0, var/a_sharp = 0, var/a_edge = 0) + + if(!damage) + return + + add_attack_logs(user,src,"Melee attacked with fists (miss/block)",admin_notify = FALSE) //No admin notice since this is usually fighting simple animals + src.visible_message("[user] has [attack_message] [src]!") + user.do_attack_animation(src) + + var/dam_zone = pick(organs_by_name) + var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) + var/armor_block = run_armor_check(affecting, armor_type, armor_pen) + var/armor_soak = get_armor_soak(affecting, armor_type, armor_pen) + apply_damage(damage, BRUTE, affecting, armor_block, armor_soak, sharp = a_sharp, edge = a_edge) + updatehealth() + return TRUE + +//Used to attack a joint through grabbing +/mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone) + var/has_grab = 0 + for(var/obj/item/weapon/grab/G in list(user.l_hand, user.r_hand)) + if(G.affecting == src && G.state == GRAB_NECK) + has_grab = 1 + break + + if(!has_grab) + return FALSE + + if(!def_zone) def_zone = user.zone_sel.selecting + var/target_zone = check_zone(def_zone) + if(!target_zone) + return FALSE + var/obj/item/organ/external/organ = get_organ(check_zone(target_zone)) + if(!organ || organ.dislocated > 0 || organ.dislocated == -1) //don't use is_dislocated() here, that checks parent + return FALSE + + user.visible_message("[user] begins to dislocate [src]'s [organ.joint]!") + if(do_after(user, 100)) + organ.dislocate(1) + src.visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") + return TRUE + return FALSE + +//Breaks all grips and pulls that the mob currently has. +/mob/living/carbon/human/proc/break_all_grabs(mob/living/carbon/user) + var/success = FALSE + if(pulling) + visible_message("[user] has broken [src]'s grip on [pulling]!") + success = TRUE + stop_pulling() + + if(istype(l_hand, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/lgrab = l_hand + if(lgrab.affecting) + visible_message("[user] has broken [src]'s grip on [lgrab.affecting]!") + success = TRUE + spawn(1) + qdel(lgrab) + if(istype(r_hand, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/rgrab = r_hand + if(rgrab.affecting) + visible_message("[user] has broken [src]'s grip on [rgrab.affecting]!") + success = TRUE + spawn(1) + qdel(rgrab) + return success + +/* + We want to ensure that a mob may only apply pressure to one organ of one mob at any given time. Currently this is done mostly implicitly through + the behaviour of do_after() and the fact that applying pressure to someone else requires a grab: + If you are applying pressure to yourself and attempt to grab someone else, you'll change what you are holding in your active hand which will stop do_mob() + If you are applying pressure to another and attempt to apply pressure to yourself, you'll have to switch to an empty hand which will also stop do_mob() + Changing targeted zones should also stop do_mob(), preventing you from applying pressure to more than one body part at once. +*/ +/mob/living/carbon/human/proc/apply_pressure(mob/living/user, var/target_zone) + var/obj/item/organ/external/organ = get_organ(target_zone) + if(!organ || !(organ.status & ORGAN_BLEEDING) || (organ.robotic >= ORGAN_ROBOT)) + return FALSE + + if(organ.applied_pressure) + var/message = "Someone is already applying pressure to [user == src ? "your [organ.name]" : "[src]'s [organ.name]"]." + to_chat(user,message) + return FALSE + + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + + if(user == src) + user.visible_message("\The [user] starts applying pressure to [TU.his] [organ.name]!", "You start applying pressure to your [organ.name]!") + else + user.visible_message("\The [user] starts applying pressure to [src]'s [organ.name]!", "You start applying pressure to [src]'s [organ.name]!") + spawn(0) + organ.applied_pressure = user + + //apply pressure as long as they stay still and keep grabbing + do_mob(user, src, INFINITY, target_zone, progress = 0) + + organ.applied_pressure = null + + if(user == src) + user.visible_message("\The [user] stops applying pressure to [TU.his] [organ.name]!", "You stop applying pressure to your [organ]!") + else + user.visible_message("\The [user] stops applying pressure to [src]'s [organ.name]!", "You stop applying pressure to [src]'s [organ.name]!") + + return TRUE + +/mob/living/carbon/human/verb/check_attacks() + set name = "Check Attacks" + set category = "IC" + set src = usr + + var/dat = "Known Attacks

                    " + + if(default_attack) + dat += "Current default attack: [default_attack.attack_name] - reset

                    " + + for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) + if(u_attack == default_attack) + dat += "Primarily [u_attack.attack_name] - default - reset


                    " + else + dat += "Primarily [u_attack.attack_name] - set default


                    " + + src << browse(dat, "window=checkattack") + +/mob/living/carbon/human/Topic(href, href_list) + if(href_list["default_attk"]) + if(href_list["default_attk"] == "reset_attk") + set_default_attack(null) + else + var/datum/unarmed_attack/u_attack = locate(href_list["default_attk"]) + if(u_attack && (u_attack in species.unarmed_attacks)) + set_default_attack(u_attack) + check_attacks() + return 1 + else + return ..() + +/mob/living/carbon/human/proc/set_default_attack(var/datum/unarmed_attack/u_attack) + default_attack = u_attack diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 0efe5f174b4..333b314fac3 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -1,594 +1,594 @@ -//Updates the mob's health from organs and mob damage variables -/mob/living/carbon/human/updatehealth() - var/huskmodifier = 2.5 //VOREStation Edit // With 1.5, you need 250 burn instead of 200 to husk a human. - - if(status_flags & GODMODE) - health = getMaxHealth() - set_stat(CONSCIOUS) - return - - var/total_burn = 0 - var/total_brute = 0 - for(var/obj/item/organ/external/O in organs) //hardcoded to streamline things a bit - if((O.robotic >= ORGAN_ROBOT) && !O.vital) - continue //*non-vital* robot limbs don't count towards shock and crit - total_brute += O.brute_dam - total_burn += O.burn_dam - - health = getMaxHealth() - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute - - //TODO: fix husking - if( ((getMaxHealth() - total_burn) < config.health_threshold_dead * huskmodifier) && stat == DEAD) - ChangeToHusk() - return - -/mob/living/carbon/human/adjustBrainLoss(var/amount) - - if(status_flags & GODMODE) return 0 //godmode - - if(should_have_organ("brain")) - var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] - if(sponge) - sponge.take_damage(amount) - brainloss = sponge.damage - else - brainloss = 200 - else - brainloss = 0 - -/mob/living/carbon/human/setBrainLoss(var/amount) - - if(status_flags & GODMODE) return 0 //godmode - - if(should_have_organ("brain")) - var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] - if(sponge) - sponge.damage = min(max(amount, 0),(getMaxHealth()*2)) - brainloss = sponge.damage - else - brainloss = 200 - else - brainloss = 0 - -/mob/living/carbon/human/getBrainLoss() - - if(status_flags & GODMODE) return 0 //godmode - - if(should_have_organ("brain")) - var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] - if(sponge) - brainloss = min(sponge.damage,getMaxHealth()*2) - else - brainloss = 200 - else - brainloss = 0 - return brainloss - -//These procs fetch a cumulative total damage from all organs -/mob/living/carbon/human/getBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT && !O.vital) - continue //*non-vital*robot limbs don't count towards death, or show up when scanned - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getShockBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT) - continue //robot limbs don't count towards shock and crit - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getActualBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT && !O.vital) - continue //*non-vital*robot limbs don't count towards death, or show up when scanned - amount += O.burn_dam - return amount - -/mob/living/carbon/human/getShockFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) - if(O.robotic >= ORGAN_ROBOT) - continue //robot limbs don't count towards shock and crit - amount += O.burn_dam - return amount - -/mob/living/carbon/human/getActualFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. - amount += O.burn_dam - return amount - -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/carbon/human/adjustBruteLoss(var/amount,var/include_robo) - amount = amount*species.brute_mod - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_brute_damage_percent - if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - take_overall_damage(amount, 0) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - heal_overall_damage(-amount, 0, include_robo) - BITSET(hud_updateflag, HEALTH_HUD) - -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/carbon/human/adjustFireLoss(var/amount,var/include_robo) - amount = amount*species.burn_mod - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_fire_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_fire_damage_percent - if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - take_overall_damage(0, amount) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - heal_overall_damage(0, -amount, include_robo) - BITSET(hud_updateflag, HEALTH_HUD) - -/mob/living/carbon/human/proc/adjustBruteLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) - amount = amount*species.brute_mod - if (organ_name in organs_by_name) - var/obj/item/organ/external/O = get_organ(organ_name) - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_brute_damage_percent - if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - O.take_damage(amount, 0, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(-amount, 0, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) - - BITSET(hud_updateflag, HEALTH_HUD) - -/mob/living/carbon/human/proc/adjustFireLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) - amount = amount*species.burn_mod - if (organ_name in organs_by_name) - var/obj/item/organ/external/O = get_organ(organ_name) - - if(amount > 0) - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_damage_percent - if(!isnull(M.incoming_fire_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*amount) - amount *= M.incoming_fire_damage_percent - if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage - O.take_damage(0, amount, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) - else - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_healing_percent)) - amount *= M.incoming_healing_percent - //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(0, -amount, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) - - BITSET(hud_updateflag, HEALTH_HUD) - -/mob/living/carbon/human/Stun(amount) - if(HULK in mutations) return - ..() - -/mob/living/carbon/human/Weaken(amount) - if(HULK in mutations) return - ..() - -/mob/living/carbon/human/Paralyse(amount) - if(HULK in mutations) return - // Notify our AI if they can now control the suit. - if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. - wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") - ..() - -/mob/living/carbon/human/proc/Stasis(amount) - if((species.flags & NO_SCAN) || isSynthetic()) - in_stasis = 0 - else - in_stasis = amount - -/mob/living/carbon/human/proc/getStasis() - if((species.flags & NO_SCAN) || isSynthetic()) - return 0 - - return in_stasis - -//This determines if, RIGHT NOW, the life() tick is being skipped due to stasis -/mob/living/carbon/human/proc/inStasisNow() - var/stasisValue = getStasis() - if(stasisValue && (life_tick % stasisValue)) - return 1 - - return 0 - -/mob/living/carbon/human/getCloneLoss() - if((species.flags & NO_SCAN) || isSynthetic()) - cloneloss = 0 - return ..() - -/mob/living/carbon/human/setCloneLoss(var/amount) - if((species.flags & NO_SCAN) || isSynthetic()) - cloneloss = 0 - else - ..() - -/mob/living/carbon/human/adjustCloneLoss(var/amount) - ..() - - if((species.flags & NO_SCAN) || isSynthetic()) - cloneloss = 0 - return - - var/heal_prob = max(0, 80 - getCloneLoss()) - var/mut_prob = min(80, getCloneLoss()+10) - if (amount > 0) - if (prob(mut_prob)) - var/list/obj/item/organ/external/candidates = list() - for (var/obj/item/organ/external/O in organs) - if(!(O.status & ORGAN_MUTATED)) - candidates |= O - if (candidates.len) - var/obj/item/organ/external/O = pick(candidates) - O.mutate() - to_chat(src, "Something is not right with your [O.name]...") - return - else - if (prob(heal_prob)) - for (var/obj/item/organ/external/O in organs) - if (O.status & ORGAN_MUTATED) - O.unmutate() - to_chat(src, "Your [O.name] is shaped normally again.") - return - - if (getCloneLoss() < 1) - for (var/obj/item/organ/external/O in organs) - if (O.status & ORGAN_MUTATED) - O.unmutate() - to_chat(src, "Your [O.name] is shaped normally again.") - BITSET(hud_updateflag, HEALTH_HUD) - -// Defined here solely to take species flags into account without having to recast at mob/living level. -/mob/living/carbon/human/getOxyLoss() - if(!should_have_organ(O_LUNGS)) - oxyloss = 0 - return ..() - -/mob/living/carbon/human/adjustOxyLoss(var/amount) - if(!should_have_organ(O_LUNGS)) - oxyloss = 0 - else - amount = amount*species.oxy_mod - ..(amount) - -/mob/living/carbon/human/setOxyLoss(var/amount) - if(!should_have_organ(O_LUNGS)) - oxyloss = 0 - else - ..() - -/mob/living/carbon/human/adjustHalLoss(var/amount) - if(species.flags & NO_PAIN) - halloss = 0 - else - if(amount > 0) //only multiply it by the mod if it's positive, or else it takes longer to fade too! - amount = amount*species.pain_mod - ..(amount) - -/mob/living/carbon/human/setHalLoss(var/amount) - if(species.flags & NO_PAIN) - halloss = 0 - else - ..() - -/mob/living/carbon/human/getToxLoss() - if(species.flags & NO_POISON) - toxloss = 0 - return ..() - -/mob/living/carbon/human/adjustToxLoss(var/amount) - if(species.flags & NO_POISON) - toxloss = 0 - else - amount = amount*species.toxins_mod - ..(amount) - -/mob/living/carbon/human/setToxLoss(var/amount) - if(species.flags & NO_POISON) - toxloss = 0 - else - ..() - -//////////////////////////////////////////// - -//Returns a list of damaged organs -/mob/living/carbon/human/proc/get_damaged_organs(var/brute, var/burn) - var/list/obj/item/organ/external/parts = list() - for(var/obj/item/organ/external/O in organs) - if((brute && O.brute_dam) || (burn && O.burn_dam)) - parts += O - return parts - -//Returns a list of damageable organs -/mob/living/carbon/human/proc/get_damageable_organs() - var/list/obj/item/organ/external/parts = list() - for(var/obj/item/organ/external/O in organs) - if(O.is_damageable()) - parts += O - return parts - -//Heals ONE external organ, organ gets randomly selected from damaged ones. -//It automatically updates damage overlays if necesary -//It automatically updates health status -/mob/living/carbon/human/heal_organ_damage(var/brute, var/burn) - var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) - if(!parts.len) return - var/obj/item/organ/external/picked = pick(parts) - if(picked.heal_damage(brute,burn)) - UpdateDamageIcon() - BITSET(hud_updateflag, HEALTH_HUD) - updatehealth() - - -/* -In most cases it makes more sense to use apply_damage() instead! And make sure to check armour if applicable. -*/ -//Damages ONE external organ, organ gets randomly selected from damagable ones. -//It automatically updates damage overlays if necesary -//It automatically updates health status -/mob/living/carbon/human/take_organ_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE) - var/list/obj/item/organ/external/parts = get_damageable_organs() - if(!parts.len) return - var/obj/item/organ/external/picked = pick(parts) - if(picked.take_damage(brute,burn,sharp,edge)) - UpdateDamageIcon() - BITSET(hud_updateflag, HEALTH_HUD) - updatehealth() - - -//Heal MANY external organs, in random order -//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs -/mob/living/carbon/human/heal_overall_damage(var/brute, var/burn, var/include_robo) - var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) - - var/update = 0 - while(parts.len && (brute>0 || burn>0) ) - var/obj/item/organ/external/picked = pick(parts) - - var/brute_was = picked.brute_dam - var/burn_was = picked.burn_dam - - update |= picked.heal_damage(brute,burn,robo_repair = include_robo) - - brute -= (brute_was-picked.brute_dam) - burn -= (burn_was-picked.burn_dam) - - parts -= picked - updatehealth() - BITSET(hud_updateflag, HEALTH_HUD) - if(update) UpdateDamageIcon() - -// damage MANY external organs, in random order -/mob/living/carbon/human/take_overall_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE, var/used_weapon = null) - if(status_flags & GODMODE) return //godmode - var/list/obj/item/organ/external/parts = get_damageable_organs() - var/update = 0 - while(parts.len && (brute>0 || burn>0) ) - var/obj/item/organ/external/picked = pick(parts) - - var/brute_was = picked.brute_dam - var/burn_was = picked.burn_dam - - update |= picked.take_damage(brute,burn,sharp,edge,used_weapon) - brute -= (picked.brute_dam - brute_was) - burn -= (picked.burn_dam - burn_was) - - parts -= picked - updatehealth() - BITSET(hud_updateflag, HEALTH_HUD) - if(update) UpdateDamageIcon() - - -//////////////////////////////////////////// - -/* -This function restores the subjects blood to max. -*/ -/mob/living/carbon/human/proc/restore_blood() - if(!should_have_organ(O_HEART)) - return - if(vessel.total_volume < species.blood_volume) - vessel.add_reagent("blood", species.blood_volume - vessel.total_volume) - -/* -This function restores all organs. -*/ -/mob/living/carbon/human/restore_all_organs(var/ignore_prosthetic_prefs) - for(var/obj/item/organ/external/current_organ in organs) - current_organ.rejuvenate(ignore_prosthetic_prefs) - -/mob/living/carbon/human/proc/HealDamage(zone, brute, burn) - var/obj/item/organ/external/E = get_organ(zone) - if(istype(E, /obj/item/organ/external)) - if (E.heal_damage(brute, burn)) - UpdateDamageIcon() - BITSET(hud_updateflag, HEALTH_HUD) - else - return 0 - return - -/* -/mob/living/carbon/human/proc/get_organ(var/zone) - if(!zone) - zone = BP_TORSO - else if (zone in list( O_EYES, O_MOUTH )) - zone = BP_HEAD - return organs_by_name[zone] -*/ - -/mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) - if(Debug2) - to_world_log("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") - - var/obj/item/organ/external/organ = null - if(isorgan(def_zone)) - organ = def_zone - else - if(!def_zone) def_zone = ran_zone(def_zone) - organ = get_organ(check_zone(def_zone)) - - for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. - if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_brute_resistance - continue - if((damagetype == BURN || damagetype == ELECTROCUTE) && (!isnull(M.effective_fire_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_fire_resistance - continue - if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_tox_resistance - continue - if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_oxy_resistance - continue - if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_clone_resistance - continue - if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_hal_resistance - continue - if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - var/damage_mitigation = 0//Used for dual calculations. - if(!isnull(M.effective_fire_resistance)) - damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) - if(!isnull(M.effective_brute_resistance)) - damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) - damage -= damage_mitigation - continue - if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) - if(isSynthetic()) - damage = damage * M.effective_fire_resistance - else - damage = damage * M.effective_tox_resistance - continue - //Handle other types of damage - if((damagetype != BRUTE) && (damagetype != BURN)) - if(damagetype == HALLOSS) - if((damage > 25 && prob(20)) || (damage > 50 && prob(60))) - if(organ && organ.organ_can_feel_pain() && !isbelly(loc) && !istype(loc, /obj/item/device/dogborg/sleeper)) //VOREStation Add - emote("scream") - ..(damage, damagetype, def_zone, blocked, soaked) - return 1 - - //Handle BRUTE and BURN damage - handle_suit_punctures(damagetype, damage, def_zone) - - if(blocked >= 100) - return 0 - - if(soaked >= damage) - return 0 - - if(!organ) return 0 - - if(blocked) - blocked = (100-blocked)/100 - damage = (damage * blocked) - - if(soaked) - damage -= soaked - - if(Debug2) - to_world_log("## DEBUG: [src] was hit for [damage].") - - switch(damagetype) - if(BRUTE) - damageoverlaytemp = 20 - if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} - damage = damage*species.brute_mod - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_brute_damage_percent - - if(organ.take_damage(damage, 0, sharp, edge, used_weapon)) - UpdateDamageIcon() - if(BURN) - damageoverlaytemp = 20 - if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} - damage = damage*species.burn_mod - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_damage_percent - if(!isnull(M.incoming_brute_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.damage_cost*damage) - damage *= M.incoming_fire_damage_percent - - if(organ.take_damage(0, damage, sharp, edge, used_weapon)) - UpdateDamageIcon() - - // Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life(). - updatehealth() - BITSET(hud_updateflag, HEALTH_HUD) - return 1 +//Updates the mob's health from organs and mob damage variables +/mob/living/carbon/human/updatehealth() + var/huskmodifier = 2.5 //VOREStation Edit // With 1.5, you need 250 burn instead of 200 to husk a human. + + if(status_flags & GODMODE) + health = getMaxHealth() + set_stat(CONSCIOUS) + return + + var/total_burn = 0 + var/total_brute = 0 + for(var/obj/item/organ/external/O in organs) //hardcoded to streamline things a bit + if((O.robotic >= ORGAN_ROBOT) && !O.vital) + continue //*non-vital* robot limbs don't count towards shock and crit + total_brute += O.brute_dam + total_burn += O.burn_dam + + health = getMaxHealth() - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute + + //TODO: fix husking + if( ((getMaxHealth() - total_burn) < config.health_threshold_dead * huskmodifier) && stat == DEAD) + ChangeToHusk() + return + +/mob/living/carbon/human/adjustBrainLoss(var/amount) + + if(status_flags & GODMODE) return 0 //godmode + + if(should_have_organ("brain")) + var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(sponge) + sponge.take_damage(amount) + brainloss = sponge.damage + else + brainloss = 200 + else + brainloss = 0 + +/mob/living/carbon/human/setBrainLoss(var/amount) + + if(status_flags & GODMODE) return 0 //godmode + + if(should_have_organ("brain")) + var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(sponge) + sponge.damage = min(max(amount, 0),(getMaxHealth()*2)) + brainloss = sponge.damage + else + brainloss = 200 + else + brainloss = 0 + +/mob/living/carbon/human/getBrainLoss() + + if(status_flags & GODMODE) return 0 //godmode + + if(should_have_organ("brain")) + var/obj/item/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(sponge) + brainloss = min(sponge.damage,getMaxHealth()*2) + else + brainloss = 200 + else + brainloss = 0 + return brainloss + +//These procs fetch a cumulative total damage from all organs +/mob/living/carbon/human/getBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT && !O.vital) + continue //*non-vital*robot limbs don't count towards death, or show up when scanned + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getShockBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT) + continue //robot limbs don't count towards shock and crit + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getActualBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT && !O.vital) + continue //*non-vital*robot limbs don't count towards death, or show up when scanned + amount += O.burn_dam + return amount + +/mob/living/carbon/human/getShockFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) + if(O.robotic >= ORGAN_ROBOT) + continue //robot limbs don't count towards shock and crit + amount += O.burn_dam + return amount + +/mob/living/carbon/human/getActualFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in organs) // Unlike the above, robolimbs DO count. + amount += O.burn_dam + return amount + +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/carbon/human/adjustBruteLoss(var/amount,var/include_robo) + amount = amount*species.brute_mod + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_brute_damage_percent + if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + take_overall_damage(amount, 0) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + heal_overall_damage(-amount, 0, include_robo) + BITSET(hud_updateflag, HEALTH_HUD) + +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/carbon/human/adjustFireLoss(var/amount,var/include_robo) + amount = amount*species.burn_mod + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_fire_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_fire_damage_percent + if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + take_overall_damage(0, amount) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + heal_overall_damage(0, -amount, include_robo) + BITSET(hud_updateflag, HEALTH_HUD) + +/mob/living/carbon/human/proc/adjustBruteLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) + amount = amount*species.brute_mod + if (organ_name in organs_by_name) + var/obj/item/organ/external/O = get_organ(organ_name) + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_brute_damage_percent + if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + O.take_damage(amount, 0, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. + O.heal_damage(-amount, 0, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) + + BITSET(hud_updateflag, HEALTH_HUD) + +/mob/living/carbon/human/proc/adjustFireLossByPart(var/amount, var/organ_name, var/obj/damage_source = null) + amount = amount*species.burn_mod + if (organ_name in organs_by_name) + var/obj/item/organ/external/O = get_organ(organ_name) + + if(amount > 0) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_damage_percent + if(!isnull(M.incoming_fire_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*amount) + amount *= M.incoming_fire_damage_percent + if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){amount *= 0.7} //VOREStation Edit - NIF mod for damage resistance for this type of damage + O.take_damage(0, amount, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) + else + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_healing_percent)) + amount *= M.incoming_healing_percent + //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. + O.heal_damage(0, -amount, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) + + BITSET(hud_updateflag, HEALTH_HUD) + +/mob/living/carbon/human/Stun(amount) + if(HULK in mutations) return + ..() + +/mob/living/carbon/human/Weaken(amount) + if(HULK in mutations) return + ..() + +/mob/living/carbon/human/Paralyse(amount) + if(HULK in mutations) return + // Notify our AI if they can now control the suit. + if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. + wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") + ..() + +/mob/living/carbon/human/proc/Stasis(amount) + if((species.flags & NO_SCAN) || isSynthetic()) + in_stasis = 0 + else + in_stasis = amount + +/mob/living/carbon/human/proc/getStasis() + if((species.flags & NO_SCAN) || isSynthetic()) + return 0 + + return in_stasis + +//This determines if, RIGHT NOW, the life() tick is being skipped due to stasis +/mob/living/carbon/human/proc/inStasisNow() + var/stasisValue = getStasis() + if(stasisValue && (life_tick % stasisValue)) + return 1 + + return 0 + +/mob/living/carbon/human/getCloneLoss() + if((species.flags & NO_SCAN) || isSynthetic()) + cloneloss = 0 + return ..() + +/mob/living/carbon/human/setCloneLoss(var/amount) + if((species.flags & NO_SCAN) || isSynthetic()) + cloneloss = 0 + else + ..() + +/mob/living/carbon/human/adjustCloneLoss(var/amount) + ..() + + if((species.flags & NO_SCAN) || isSynthetic()) + cloneloss = 0 + return + + var/heal_prob = max(0, 80 - getCloneLoss()) + var/mut_prob = min(80, getCloneLoss()+10) + if (amount > 0) + if (prob(mut_prob)) + var/list/obj/item/organ/external/candidates = list() + for (var/obj/item/organ/external/O in organs) + if(!(O.status & ORGAN_MUTATED)) + candidates |= O + if (candidates.len) + var/obj/item/organ/external/O = pick(candidates) + O.mutate() + to_chat(src, "Something is not right with your [O.name]...") + return + else + if (prob(heal_prob)) + for (var/obj/item/organ/external/O in organs) + if (O.status & ORGAN_MUTATED) + O.unmutate() + to_chat(src, "Your [O.name] is shaped normally again.") + return + + if (getCloneLoss() < 1) + for (var/obj/item/organ/external/O in organs) + if (O.status & ORGAN_MUTATED) + O.unmutate() + to_chat(src, "Your [O.name] is shaped normally again.") + BITSET(hud_updateflag, HEALTH_HUD) + +// Defined here solely to take species flags into account without having to recast at mob/living level. +/mob/living/carbon/human/getOxyLoss() + if(!should_have_organ(O_LUNGS)) + oxyloss = 0 + return ..() + +/mob/living/carbon/human/adjustOxyLoss(var/amount) + if(!should_have_organ(O_LUNGS)) + oxyloss = 0 + else + amount = amount*species.oxy_mod + ..(amount) + +/mob/living/carbon/human/setOxyLoss(var/amount) + if(!should_have_organ(O_LUNGS)) + oxyloss = 0 + else + ..() + +/mob/living/carbon/human/adjustHalLoss(var/amount) + if(species.flags & NO_PAIN) + halloss = 0 + else + if(amount > 0) //only multiply it by the mod if it's positive, or else it takes longer to fade too! + amount = amount*species.pain_mod + ..(amount) + +/mob/living/carbon/human/setHalLoss(var/amount) + if(species.flags & NO_PAIN) + halloss = 0 + else + ..() + +/mob/living/carbon/human/getToxLoss() + if(species.flags & NO_POISON) + toxloss = 0 + return ..() + +/mob/living/carbon/human/adjustToxLoss(var/amount) + if(species.flags & NO_POISON) + toxloss = 0 + else + amount = amount*species.toxins_mod + ..(amount) + +/mob/living/carbon/human/setToxLoss(var/amount) + if(species.flags & NO_POISON) + toxloss = 0 + else + ..() + +//////////////////////////////////////////// + +//Returns a list of damaged organs +/mob/living/carbon/human/proc/get_damaged_organs(var/brute, var/burn) + var/list/obj/item/organ/external/parts = list() + for(var/obj/item/organ/external/O in organs) + if((brute && O.brute_dam) || (burn && O.burn_dam)) + parts += O + return parts + +//Returns a list of damageable organs +/mob/living/carbon/human/proc/get_damageable_organs() + var/list/obj/item/organ/external/parts = list() + for(var/obj/item/organ/external/O in organs) + if(O.is_damageable()) + parts += O + return parts + +//Heals ONE external organ, organ gets randomly selected from damaged ones. +//It automatically updates damage overlays if necesary +//It automatically updates health status +/mob/living/carbon/human/heal_organ_damage(var/brute, var/burn) + var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + if(!parts.len) return + var/obj/item/organ/external/picked = pick(parts) + if(picked.heal_damage(brute,burn)) + UpdateDamageIcon() + BITSET(hud_updateflag, HEALTH_HUD) + updatehealth() + + +/* +In most cases it makes more sense to use apply_damage() instead! And make sure to check armour if applicable. +*/ +//Damages ONE external organ, organ gets randomly selected from damagable ones. +//It automatically updates damage overlays if necesary +//It automatically updates health status +/mob/living/carbon/human/take_organ_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE) + var/list/obj/item/organ/external/parts = get_damageable_organs() + if(!parts.len) return + var/obj/item/organ/external/picked = pick(parts) + if(picked.take_damage(brute,burn,sharp,edge)) + UpdateDamageIcon() + BITSET(hud_updateflag, HEALTH_HUD) + updatehealth() + + +//Heal MANY external organs, in random order +//'include_robo' only applies to healing, for legacy purposes, as all damage typically hurts both types of organs +/mob/living/carbon/human/heal_overall_damage(var/brute, var/burn, var/include_robo) + var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + + var/update = 0 + while(parts.len && (brute>0 || burn>0) ) + var/obj/item/organ/external/picked = pick(parts) + + var/brute_was = picked.brute_dam + var/burn_was = picked.burn_dam + + update |= picked.heal_damage(brute,burn,robo_repair = include_robo) + + brute -= (brute_was-picked.brute_dam) + burn -= (burn_was-picked.burn_dam) + + parts -= picked + updatehealth() + BITSET(hud_updateflag, HEALTH_HUD) + if(update) UpdateDamageIcon() + +// damage MANY external organs, in random order +/mob/living/carbon/human/take_overall_damage(var/brute, var/burn, var/sharp = FALSE, var/edge = FALSE, var/used_weapon = null) + if(status_flags & GODMODE) return //godmode + var/list/obj/item/organ/external/parts = get_damageable_organs() + var/update = 0 + while(parts.len && (brute>0 || burn>0) ) + var/obj/item/organ/external/picked = pick(parts) + + var/brute_was = picked.brute_dam + var/burn_was = picked.burn_dam + + update |= picked.take_damage(brute,burn,sharp,edge,used_weapon) + brute -= (picked.brute_dam - brute_was) + burn -= (picked.burn_dam - burn_was) + + parts -= picked + updatehealth() + BITSET(hud_updateflag, HEALTH_HUD) + if(update) UpdateDamageIcon() + + +//////////////////////////////////////////// + +/* +This function restores the subjects blood to max. +*/ +/mob/living/carbon/human/proc/restore_blood() + if(!should_have_organ(O_HEART)) + return + if(vessel.total_volume < species.blood_volume) + vessel.add_reagent("blood", species.blood_volume - vessel.total_volume) + +/* +This function restores all organs. +*/ +/mob/living/carbon/human/restore_all_organs(var/ignore_prosthetic_prefs) + for(var/obj/item/organ/external/current_organ in organs) + current_organ.rejuvenate(ignore_prosthetic_prefs) + +/mob/living/carbon/human/proc/HealDamage(zone, brute, burn) + var/obj/item/organ/external/E = get_organ(zone) + if(istype(E, /obj/item/organ/external)) + if (E.heal_damage(brute, burn)) + UpdateDamageIcon() + BITSET(hud_updateflag, HEALTH_HUD) + else + return 0 + return + +/* +/mob/living/carbon/human/proc/get_organ(var/zone) + if(!zone) + zone = BP_TORSO + else if (zone in list( O_EYES, O_MOUTH )) + zone = BP_HEAD + return organs_by_name[zone] +*/ + +/mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) + if(Debug2) + to_world_log("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") + + var/obj/item/organ/external/organ = null + if(isorgan(def_zone)) + organ = def_zone + else + if(!def_zone) def_zone = ran_zone(def_zone) + organ = get_organ(check_zone(def_zone)) + + for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. + if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_brute_resistance + continue + if((damagetype == BURN || damagetype == ELECTROCUTE) && (!isnull(M.effective_fire_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_fire_resistance + continue + if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_tox_resistance + continue + if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_oxy_resistance + continue + if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_clone_resistance + continue + if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_hal_resistance + continue + if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + var/damage_mitigation = 0//Used for dual calculations. + if(!isnull(M.effective_fire_resistance)) + damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) + if(!isnull(M.effective_brute_resistance)) + damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) + damage -= damage_mitigation + continue + if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) + if(isSynthetic()) + damage = damage * M.effective_fire_resistance + else + damage = damage * M.effective_tox_resistance + continue + //Handle other types of damage + if((damagetype != BRUTE) && (damagetype != BURN)) + if(damagetype == HALLOSS) + if((damage > 25 && prob(20)) || (damage > 50 && prob(60))) + if(organ && organ.organ_can_feel_pain() && !isbelly(loc) && !istype(loc, /obj/item/device/dogborg/sleeper)) //VOREStation Add + emote("scream") + ..(damage, damagetype, def_zone, blocked, soaked) + return 1 + + //Handle BRUTE and BURN damage + handle_suit_punctures(damagetype, damage, def_zone) + + if(blocked >= 100) + return 0 + + if(soaked >= damage) + return 0 + + if(!organ) return 0 + + if(blocked) + blocked = (100-blocked)/100 + damage = (damage * blocked) + + if(soaked) + damage -= soaked + + if(Debug2) + to_world_log("## DEBUG: [src] was hit for [damage].") + + switch(damagetype) + if(BRUTE) + damageoverlaytemp = 20 + if(nif && nif.flag_check(NIF_C_BRUTEARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} + damage = damage*species.brute_mod + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_brute_damage_percent + + if(organ.take_damage(damage, 0, sharp, edge, used_weapon)) + UpdateDamageIcon() + if(BURN) + damageoverlaytemp = 20 + if(nif && nif.flag_check(NIF_C_BURNARMOR,NIF_FLAGS_COMBAT)){damage *= 0.7} + damage = damage*species.burn_mod + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_damage_percent + if(!isnull(M.incoming_brute_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.damage_cost*damage) + damage *= M.incoming_fire_damage_percent + + if(organ.take_damage(0, damage, sharp, edge, used_weapon)) + UpdateDamageIcon() + + // Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life(). + updatehealth() + BITSET(hud_updateflag, HEALTH_HUD) + return 1 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 8c7aaebe8e1..905966009dd 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -1,684 +1,684 @@ -/* -Contains most of the procs that are called when a mob is attacked by something - -bullet_act -ex_act -meteor_act -emp_act - -*/ - -/mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone) - - def_zone = check_zone(def_zone) - if(!has_organ(def_zone)) - return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by. - - var/obj/item/organ/external/organ = get_organ() - - //Shields - var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]") - if(shield_check) // If the block roll succeeded, this is true. - if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected). - return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE. - else // Otherwise we blocked normally and stopped all the damage. - return 0 - - if(!P.nodamage) - organ.add_autopsy_data("[P.name]", P.damage) - - // Tell clothing we're wearing that it got hit by a bullet/laser/etc - var/list/clothing = get_clothing_list_organ(organ) - for(var/obj/item/clothing/C in clothing) - C.clothing_impact(P, P.damage) - - //Shrapnel - if(P.can_embed()) - var/armor = getarmor_organ(organ, "bullet") - if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. - var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed - - //Modifiers can make bullets less likely to embed! These are the normal modifiers and shouldn't be related to energy stuff, but they can be anyways! - for(var/datum/modifier/M in modifiers) - if(!isnull(M.incoming_damage_percent)) - if(M.energy_based) - M.energy_source.use(M.energy_cost) //We use energy_cost here for special effects, such as embedding. - hit_embed_chance = hit_embed_chance*M.incoming_damage_percent - if(P.damage_type == BRUTE && (!isnull(M.incoming_brute_damage_percent))) - if(M.energy_based) - M.energy_source.use(M.energy_cost) - hit_embed_chance = hit_embed_chance*M.incoming_brute_damage_percent - - if(prob(max(hit_embed_chance, 0))) - var/obj/item/weapon/material/shard/shrapnel/SP = new() - SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel" - SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." - SP.loc = organ - organ.embed(SP) - - return (..(P , def_zone)) - -/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) - var/obj/item/organ/external/affected = get_organ(check_zone(def_zone)) - var/siemens_coeff = get_siemens_coefficient_organ(affected) - if(fire_stacks < 0) // Water makes you more conductive. - siemens_coeff *= 1.5 - stun_amount *= siemens_coeff - agony_amount *= siemens_coeff - - switch (def_zone) - if(BP_HEAD) - agony_amount *= 1.50 - if(BP_L_HAND, BP_R_HAND) - var/c_hand - if (def_zone == BP_L_HAND) - c_hand = l_hand - else - c_hand = r_hand - - if(c_hand && (stun_amount || agony_amount > 10)) - msg_admin_attack("[key_name(src)] was disarmed by a stun effect") - - drop_from_inventory(c_hand) - if(!isbelly(loc)) //VOREStation Add - if (affected.robotic >= ORGAN_ROBOT) - custom_emote(VISIBLE_MESSAGE, "drops what they were holding, their [affected.name] malfunctioning!") - else - var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") - custom_emote(VISIBLE_MESSAGE, "[affected.organ_can_feel_pain() ? "" : emote_scream] drops what they were holding in their [affected.name]!") - - ..(stun_amount, agony_amount, def_zone) - -/mob/living/carbon/human/getarmor(var/def_zone, var/type) - var/armorval = 0 - var/total = 0 - - if(def_zone) - if(isorgan(def_zone)) - return getarmor_organ(def_zone, type) - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - return getarmor_organ(affecting, type) - //If a specific bodypart is targetted, check how that bodypart is protected and return the value. - - //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values - for(var/organ_name in organs_by_name) - if (organ_name in organ_rel_size) - var/obj/item/organ/external/organ = organs_by_name[organ_name] - if(organ) - var/weight = organ_rel_size[organ_name] - armorval += (getarmor_organ(organ, type) * weight) - total += weight - return (armorval/max(total, 1)) - -//Like getarmor, but the value it returns will be numerical damage reduction -/mob/living/carbon/human/getsoak(var/def_zone, var/type) - var/soakval = 0 - var/total = 0 - - if(def_zone) - if(isorgan(def_zone)) - return getsoak_organ(def_zone, type) - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - return getsoak_organ(affecting, type) - //If a specific bodypart is targetted, check how that bodypart is protected and return the value. - - //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values - for(var/organ_name in organs_by_name) - if (organ_name in organ_rel_size) - var/obj/item/organ/external/organ = organs_by_name[organ_name] - if(organ) - var/weight = organ_rel_size[organ_name] - soakval += getsoak_organ(organ, type) * weight - total += weight - return (soakval/max(total, 1)) - -//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. -/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) - if (!def_zone) - return 1.0 - - var/siemens_coefficient = max(species.siemens_coefficient,0) - - var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? - for(var/obj/item/clothing/C in clothing_items) - if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? - siemens_coefficient *= C.siemens_coefficient - - // Modifiers. - for(var/datum/modifier/M as anything in modifiers) - if(!isnull(M.siemens_coefficient)) - siemens_coefficient *= M.siemens_coefficient - - return siemens_coefficient - -// Similar to above but is for the mob's overall protection, being the average of all slots. -/mob/living/carbon/human/proc/get_siemens_coefficient_average() - var/siemens_value = 0 - var/total = 0 - for(var/organ_name in organs_by_name) - if(organ_name in organ_rel_size) - var/obj/item/organ/external/organ = organs_by_name[organ_name] - if(organ) - var/weight = organ_rel_size[organ_name] - siemens_value += get_siemens_coefficient_organ(organ) * weight - total += weight - - if(fire_stacks < 0) // Water makes you more conductive. - siemens_value *= 1.5 - - return (siemens_value / max(total, 1)) - -// Returns a number between 0 to 1, with 1 being total protection. -/mob/living/carbon/human/get_shock_protection() - return min(1 - get_siemens_coefficient_average(), 1) // Don't go above 1, but negatives are fine. - -// Returns a list of clothing that is currently covering def_zone. -/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type) - var/list/results = list() - var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) - for(var/obj/item/clothing/C in clothing_items) - if(istype(C) && (C.body_parts_covered & def_zone.body_part)) - results.Add(C) - return results - -//this proc returns the armour value for a particular external organ. -/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type) - if(!type || !def_zone) - return 0 - var/protection = 0 - var/list/protective_gear = def_zone.get_covering_clothing() - for(var/obj/item/clothing/gear in protective_gear) - protection += gear.armor[type] - - for(var/datum/modifier/M as anything in modifiers) - var/modifier_armor = LAZYACCESS(M.armor_percent, type) - if(modifier_armor) - protection += modifier_armor - - return protection - -/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type) - if(!type || !def_zone) - return 0 - var/soaked = 0 - var/list/protective_gear = def_zone.get_covering_clothing() - for(var/obj/item/clothing/gear in protective_gear) - soaked += gear.armorsoak[type] - - for(var/datum/modifier/M as anything in modifiers) - var/modifier_armor = LAZYACCESS(M.armor_flat, type) - if(modifier_armor) - soaked += modifier_armor - - return soaked - -// Checked in borer code -/mob/living/carbon/human/proc/check_head_coverage() - var/obj/item/organ/external/H = organs_by_name[BP_HEAD] - var/list/body_parts = H.get_covering_clothing(EYES) - if(LAZYLEN(body_parts)) - return 1 - return 0 - -//Used to check if they can be fed food/drinks/pills -/mob/living/carbon/human/proc/check_mouth_coverage() - var/obj/item/organ/external/H = organs_by_name[BP_HEAD] - var/list/protective_gear = H.get_covering_clothing(FACE) - for(var/obj/item/gear in protective_gear) - if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL)) - return gear - return null - -/mob/living/carbon/human/proc/check_mouth_coverage_survival() - var/obj/item/organ/external/H = organs_by_name[BP_HEAD] - var/list/protective_gear = H.get_covering_clothing(FACE) - for(var/obj/item/gear in protective_gear) - if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL) && !(gear.item_flags & ALLOW_SURVIVALFOOD)) - return gear - return null - -/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) - if(!shield) continue - . = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text) - if(.) return - return 0 - -/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) - if(check_neckgrab_attack(I, user, target_zone)) - return null - - if(user == src) // Attacking yourself can't miss - return target_zone - - var/hit_zone = get_zone_with_miss_chance(target_zone, src, user.get_accuracy_penalty()) - - if(!hit_zone) - user.do_attack_animation(src) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("\The [user] misses [src] with \the [I]!") - return null - - if(check_shields(I.force, I, user, target_zone, "the [I.name]")) - return - - var/obj/item/organ/external/affecting = get_organ(hit_zone) - if (!affecting || affecting.is_stump()) - to_chat(user, "They are missing that limb!") - return null - - return hit_zone - -/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) - var/obj/item/organ/external/affecting = get_organ(hit_zone) - if(!affecting) - return //should be prevented by attacked_with_item() but for sanity. - - visible_message("[src] has been [LAZYLEN(I.attack_verb) ? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") - - var/soaked = get_armor_soak(hit_zone, "melee", I.armor_penetration) - - var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") - - standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone) - - return blocked - -/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) - var/obj/item/organ/external/affecting = get_organ(hit_zone) - if(!affecting) - return 0 - - // Allow clothing to respond to being hit. - // This is done up here so that clothing damage occurs even if fully blocked. - var/list/clothing = get_clothing_list_organ(affecting) - for(var/obj/item/clothing/C in clothing) - C.clothing_impact(I, effective_force) - - if(soaked >= round(effective_force*0.8)) - effective_force -= round(effective_force*0.8) - // Handle striking to cripple. - if(user.a_intent == I_DISARM) - effective_force *= 0.5 //reduced effective force... - if(!..(I, user, effective_force, blocked, soaked, hit_zone)) - return 0 - - //set the dislocate mult less than the effective force mult so that - //dislocating limbs on disarm is a bit easier than breaking limbs on harm - attack_joint(affecting, I, effective_force, 0.75, blocked, soaked) //...but can dislocate joints - else if(!..()) - return 0 - - if(effective_force > 10 || effective_force >= 5 && prob(33)) - forcesay(hit_appends) //forcesay checks stat already - - if(prob(25 + (effective_force * 2))) - if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS))) - return - - if(!(I.flags & NOBLOODY)) - I.add_blood(src) - - var/bloody = 0 - if(prob(33)) - bloody = 1 - var/turf/location = loc - if(istype(location, /turf/simulated)) - location.add_blood(src) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood - H.bloody_body(src) - H.bloody_hands(src) - - if(!stat && !(I.no_random_knockdown)) - switch(hit_zone) - if(BP_HEAD)//Harder to score a stun but if you do it lasts a bit longer - if(prob(effective_force)) - apply_effect(20, PARALYZE, blocked, soaked) - visible_message("\The [src] has been knocked unconscious!") - if(bloody)//Apply blood - if(wear_mask) - wear_mask.add_blood(src) - update_inv_wear_mask(0) - if(head) - head.add_blood(src) - update_inv_head(0) - if(glasses && prob(33)) - glasses.add_blood(src) - update_inv_glasses(0) - if(BP_TORSO)//Easier to score a stun but lasts less time - if(prob(effective_force + 10)) - apply_effect(6, WEAKEN, blocked, soaked) - visible_message("\The [src] has been knocked down!") - if(bloody) - bloody_body(src) - - return 1 - -/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked, var/soaked) - if(!organ || (organ.dislocated == 1) || (organ.dislocated == -1) || blocked >= 100) //VOREStation Edit Bugfix - return 0 - - if(W.damtype != BRUTE) - return 0 - - if(soaked >= round(effective_force*0.8)) - effective_force -= round(effective_force*0.8) - - //want the dislocation chance to be such that the limb is expected to dislocate after dealing a fraction of the damage needed to break the limb - var/dislocate_chance = effective_force/(dislocate_mult * organ.min_broken_damage * config.organ_health_multiplier)*100 - if(prob(dislocate_chance * (100 - blocked)/100)) - visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") - organ.dislocate(1) - return 1 - return 0 - -/mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source) - var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting) - if(!affecting || !(affecting.robotic >= ORGAN_ROBOT)) - to_chat(user, "That limb isn't robotic.") - return -1 - if(affecting.sabotaged) - to_chat(user, "[src]'s [affecting.name] is already sabotaged!") - return -1 - to_chat(user, "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties.") - affecting.sabotaged = 1 - return 1 - -//this proc handles being hit by a thrown atom -/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR) -// if(buckled && buckled == AM) -// return // Don't get hit by the thing we're buckled to. - - //VORESTATION EDIT START - Allows for thrown vore! - //Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred. - // I put more comments here for ease of reading. - if(istype(AM, /mob/living)) - var/mob/living/thrown_mob = AM - if(isanimal(thrown_mob) && !allowmobvore) //Is the thrown_mob an animal and we don't allow mobvore? - return - // PERSON BEING HIT: CAN BE DROP PRED, ALLOWS THROW VORE. - // PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY. - if((can_be_drop_pred && throw_vore && vore_selected) && (thrown_mob.devourable && thrown_mob.throw_vore && thrown_mob.can_be_drop_prey)) //Prey thrown into pred. - vore_selected.nom_mob(thrown_mob) //Eat them!!! - visible_message("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!") - if(thrown_mob.loc != vore_selected) - thrown_mob.forceMove(vore_selected) //Double check. Should never happen but...Weirder things have happened! - add_attack_logs(thrown_mob.thrower,src,"Devoured [thrown_mob.name] via throw vore.") - return //We can stop here. We don't need to calculate damage or anything else. They're eaten. - - // PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE. - // PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE. - else if((can_be_drop_prey && throw_vore && devourable) && (thrown_mob.can_be_drop_pred && thrown_mob.throw_vore && thrown_mob.vore_selected)) //Pred thrown into prey. - visible_message("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!") - thrown_mob.vore_selected.nom_mob(src) //Eat them!!! - if(src.loc != thrown_mob.vore_selected) - src.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened! - add_attack_logs(thrown_mob.LAssailant,src,"Was Devoured by [thrown_mob.name] via throw vore.") - return - //VORESTATION EDIT END - Allows for thrown vore! - - if(istype(AM,/obj/)) - var/obj/O = AM - if(in_throw_mode && speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode - if(canmove && !restrained()) - if(isturf(O.loc)) - if(can_catch(O)) - put_in_active_hand(O) - visible_message("[src] catches [O]!") - throw_mode_off() - return - - var/dtype = O.damtype - var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR) - - if(species && species.throwforce_absorb_threshold >= throw_damage) - visible_message("\The [O] simply bounces off of [src]'s body!") - return - - var/zone - if (istype(O.thrower, /mob/living)) - var/mob/living/L = O.thrower - zone = check_zone(L.zone_sel.selecting) - else - zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest - - //check if we hit - var/miss_chance = 15 - if (O.throw_source) - var/distance = get_dist(O.throw_source, loc) - miss_chance = max(15*(distance-2), 0) - zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1) - - if(zone && O.thrower != src) - var/shield_check = check_shields(throw_damage, O, thrower, zone, "[O]") - if(shield_check == PROJECTILE_FORCE_MISS) - zone = null - else if(shield_check) - return - - if(!zone) - visible_message("\The [O] misses [src] narrowly!") - return - - O.throwing = 0 //it hit, so stop moving - - var/obj/item/organ/external/affecting = get_organ(zone) - var/hit_area = affecting.name - - src.visible_message("[src] has been hit in the [hit_area] by [O].") - - if(ismob(O.thrower)) - add_attack_logs(O.thrower,src,"Hit with thrown [O.name]") - - //If the armor absorbs all of the damage, skip the rest of the calculations - var/soaked = get_armor_soak(affecting, "melee", O.armor_penetration) - if(soaked >= throw_damage) - to_chat(src, "Your armor absorbs the force of [O.name]!") - return - - var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here - if(armor < 100) - apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) - - - //thrown weapon embedded object code. - if(dtype == BRUTE && istype(O,/obj/item)) - var/obj/item/I = O - if (!is_robot_module(I)) - var/sharp = is_sharp(I) - var/damage = throw_damage - if (soaked) - damage -= soaked - if (armor) - damage /= armor+1 - - //blunt objects should really not be embedding in things unless a huge amount of force is involved - var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3) - var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class - - //Sharp objects will always embed if they do enough damage. - //Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold - if((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance))) - affecting.embed(I) - - // Begin BS12 momentum-transfer code. - var/mass = 1.5 - if(istype(O, /obj/item)) - var/obj/item/I = O - mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR - var/momentum = speed*mass - - if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED && !buckled) - var/dir = get_dir(O.throw_source, src) - - visible_message("[src] staggers under the impact!","You stagger under the impact!") - src.throw_at(get_edge_target_turf(src,dir),1,momentum) - - if(!O || !src) return - - if(O.loc == src && O.sharp) //Projectile is embedded and suitable for pinning. - var/turf/T = near_wall(dir,2) - - if(T) - src.loc = T - visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") - src.anchored = TRUE - src.pinned += O - -// This does a prob check to catch the thing flying at you, with a minimum of 1% -/mob/living/carbon/human/proc/can_catch(var/obj/O) - if(!get_active_hand()) // If active hand is empty - var/obj/item/organ/external/temp = organs_by_name["r_hand"] - if (hand) - temp = organs_by_name["l_hand"] - if(temp && !temp.is_usable()) - return FALSE // The hand isn't working in the first place - - if(!O.catchable) - return FALSE - - // Alright, our hand works? Time to try the catching. - var/catch_chance = 90 // Default 90% catch rate - - if(O.sharp) - catch_chance -= 50 // Catching knives is hard - - catch_chance -= get_accuracy_penalty() // Same issues with shooting a gun, or swinging a weapon - - catch_chance = between(1, catch_chance, 100) - - if(prob(catch_chance)) - return TRUE - return FALSE - -/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null) - if(!def_zone) ..() - - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - affecting.embed(O) - - -/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) - if (gloves) - gloves.add_blood(source) - gloves:transfer_blood = amount - gloves:bloody_hands_mob = source - else - add_blood(source) - bloody_hands = amount - bloody_hands_mob = source - update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves - -/mob/living/carbon/human/proc/bloody_body(var/mob/living/source) - if(wear_suit) - wear_suit.add_blood(source) - update_inv_wear_suit(0) - if(w_uniform) - w_uniform.add_blood(source) - update_inv_w_uniform(0) - -/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage, var/def_zone) - - // Tox and oxy don't matter to suits. - if(damtype != BURN && damtype != BRUTE) return - - // The rig might soak this hit, if we're wearing one. - if(istype(get_rig(),/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = get_rig() - rig.take_hit(damage) - - // We may also be taking a suit breach. - if(!wear_suit) return - if(!istype(wear_suit,/obj/item/clothing/suit/space)) return - var/obj/item/clothing/suit/space/SS = wear_suit - var/penetrated_dam = max(0,(damage - SS.breach_threshold)) - if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) - -/mob/living/carbon/human/reagent_permeability() - var/perm = 0 - - var/list/perm_by_part = list( - "head" = THERMAL_PROTECTION_HEAD, - "upper_torso" = THERMAL_PROTECTION_UPPER_TORSO, - "lower_torso" = THERMAL_PROTECTION_LOWER_TORSO, - "legs" = THERMAL_PROTECTION_LEG_LEFT + THERMAL_PROTECTION_LEG_RIGHT, - "feet" = THERMAL_PROTECTION_FOOT_LEFT + THERMAL_PROTECTION_FOOT_RIGHT, - "arms" = THERMAL_PROTECTION_ARM_LEFT + THERMAL_PROTECTION_ARM_RIGHT, - "hands" = THERMAL_PROTECTION_HAND_LEFT + THERMAL_PROTECTION_HAND_RIGHT - ) - - for(var/obj/item/clothing/C in src.get_equipped_items()) - if(C.permeability_coefficient == 1 || !C.body_parts_covered) - continue - if(C.body_parts_covered & HEAD) - perm_by_part["head"] *= C.permeability_coefficient - if(C.body_parts_covered & UPPER_TORSO) - perm_by_part["upper_torso"] *= C.permeability_coefficient - if(C.body_parts_covered & LOWER_TORSO) - perm_by_part["lower_torso"] *= C.permeability_coefficient - if(C.body_parts_covered & LEGS) - perm_by_part["legs"] *= C.permeability_coefficient - if(C.body_parts_covered & FEET) - perm_by_part["feet"] *= C.permeability_coefficient - if(C.body_parts_covered & ARMS) - perm_by_part["arms"] *= C.permeability_coefficient - if(C.body_parts_covered & HANDS) - perm_by_part["hands"] *= C.permeability_coefficient - - for(var/part in perm_by_part) - perm += perm_by_part[part] - - return perm - -// This is for preventing harm by being covered in water, which only prometheans need to deal with. -/mob/living/carbon/human/get_water_protection() - var/protection = species.water_resistance - if(protection == 1) // No point doing permeability checks if it won't matter. - return protection - // Wearing clothing with a low permeability_coefficient can protect from water. - - var/converted_protection = 1 - protection - var/perm = reagent_permeability() - converted_protection *= perm - return CLAMP(1-converted_protection, 0, 1) - -/mob/living/carbon/human/water_act(amount) - adjust_fire_stacks(-amount * 5) - for(var/atom/movable/AM in contents) - AM.water_act(amount) - remove_modifiers_of_type(/datum/modifier/fire) - - species.handle_water_damage(src, amount) - -/mob/living/carbon/human/shank_attack(obj/item/W, obj/item/weapon/grab/G, mob/user, hit_zone) - - if(!..()) - return 0 - - var/organ_chance = 50 - var/damage = shank_armor_helper(W, G, user) - var/obj/item/organ/external/chest = get_organ(hit_zone) - - if(W.edge) - organ_chance = 75 - user.next_move = world.time + 20 - user.visible_message("\The [user] begins to twist \the [W] around inside [src]'s [chest]!") - if(!do_after(user, 20)) - return 0 - if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab - return 0 - - user.visible_message("\The [user] twists \the [W] around inside [src]'s [chest]!") - - if(prob(organ_chance)) - var/obj/item/organ/internal/selected_organ = pick(chest.internal_organs) - selected_organ.damage = max(selected_organ.damage, damage * 0.5) - G.last_action = world.time - flick(G.hud.icon_state, G.hud) - - return 1 +/* +Contains most of the procs that are called when a mob is attacked by something + +bullet_act +ex_act +meteor_act +emp_act + +*/ + +/mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone) + + def_zone = check_zone(def_zone) + if(!has_organ(def_zone)) + return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by. + + var/obj/item/organ/external/organ = get_organ() + + //Shields + var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]") + if(shield_check) // If the block roll succeeded, this is true. + if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected). + return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE. + else // Otherwise we blocked normally and stopped all the damage. + return 0 + + if(!P.nodamage) + organ.add_autopsy_data("[P.name]", P.damage) + + // Tell clothing we're wearing that it got hit by a bullet/laser/etc + var/list/clothing = get_clothing_list_organ(organ) + for(var/obj/item/clothing/C in clothing) + C.clothing_impact(P, P.damage) + + //Shrapnel + if(P.can_embed()) + var/armor = getarmor_organ(organ, "bullet") + if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. + var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed + + //Modifiers can make bullets less likely to embed! These are the normal modifiers and shouldn't be related to energy stuff, but they can be anyways! + for(var/datum/modifier/M in modifiers) + if(!isnull(M.incoming_damage_percent)) + if(M.energy_based) + M.energy_source.use(M.energy_cost) //We use energy_cost here for special effects, such as embedding. + hit_embed_chance = hit_embed_chance*M.incoming_damage_percent + if(P.damage_type == BRUTE && (!isnull(M.incoming_brute_damage_percent))) + if(M.energy_based) + M.energy_source.use(M.energy_cost) + hit_embed_chance = hit_embed_chance*M.incoming_brute_damage_percent + + if(prob(max(hit_embed_chance, 0))) + var/obj/item/weapon/material/shard/shrapnel/SP = new() + SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel" + SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." + SP.loc = organ + organ.embed(SP) + + return (..(P , def_zone)) + +/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) + var/obj/item/organ/external/affected = get_organ(check_zone(def_zone)) + var/siemens_coeff = get_siemens_coefficient_organ(affected) + if(fire_stacks < 0) // Water makes you more conductive. + siemens_coeff *= 1.5 + stun_amount *= siemens_coeff + agony_amount *= siemens_coeff + + switch (def_zone) + if(BP_HEAD) + agony_amount *= 1.50 + if(BP_L_HAND, BP_R_HAND) + var/c_hand + if (def_zone == BP_L_HAND) + c_hand = l_hand + else + c_hand = r_hand + + if(c_hand && (stun_amount || agony_amount > 10)) + msg_admin_attack("[key_name(src)] was disarmed by a stun effect") + + drop_from_inventory(c_hand) + if(!isbelly(loc)) //VOREStation Add + if (affected.robotic >= ORGAN_ROBOT) + custom_emote(VISIBLE_MESSAGE, "drops what they were holding, their [affected.name] malfunctioning!") + else + var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") + custom_emote(VISIBLE_MESSAGE, "[affected.organ_can_feel_pain() ? "" : emote_scream] drops what they were holding in their [affected.name]!") + + ..(stun_amount, agony_amount, def_zone) + +/mob/living/carbon/human/getarmor(var/def_zone, var/type) + var/armorval = 0 + var/total = 0 + + if(def_zone) + if(isorgan(def_zone)) + return getarmor_organ(def_zone, type) + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + return getarmor_organ(affecting, type) + //If a specific bodypart is targetted, check how that bodypart is protected and return the value. + + //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values + for(var/organ_name in organs_by_name) + if (organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + armorval += (getarmor_organ(organ, type) * weight) + total += weight + return (armorval/max(total, 1)) + +//Like getarmor, but the value it returns will be numerical damage reduction +/mob/living/carbon/human/getsoak(var/def_zone, var/type) + var/soakval = 0 + var/total = 0 + + if(def_zone) + if(isorgan(def_zone)) + return getsoak_organ(def_zone, type) + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + return getsoak_organ(affecting, type) + //If a specific bodypart is targetted, check how that bodypart is protected and return the value. + + //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values + for(var/organ_name in organs_by_name) + if (organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + soakval += getsoak_organ(organ, type) * weight + total += weight + return (soakval/max(total, 1)) + +//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. +/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) + if (!def_zone) + return 1.0 + + var/siemens_coefficient = max(species.siemens_coefficient,0) + + var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? + for(var/obj/item/clothing/C in clothing_items) + if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? + siemens_coefficient *= C.siemens_coefficient + + // Modifiers. + for(var/datum/modifier/M as anything in modifiers) + if(!isnull(M.siemens_coefficient)) + siemens_coefficient *= M.siemens_coefficient + + return siemens_coefficient + +// Similar to above but is for the mob's overall protection, being the average of all slots. +/mob/living/carbon/human/proc/get_siemens_coefficient_average() + var/siemens_value = 0 + var/total = 0 + for(var/organ_name in organs_by_name) + if(organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + siemens_value += get_siemens_coefficient_organ(organ) * weight + total += weight + + if(fire_stacks < 0) // Water makes you more conductive. + siemens_value *= 1.5 + + return (siemens_value / max(total, 1)) + +// Returns a number between 0 to 1, with 1 being total protection. +/mob/living/carbon/human/get_shock_protection() + return min(1 - get_siemens_coefficient_average(), 1) // Don't go above 1, but negatives are fine. + +// Returns a list of clothing that is currently covering def_zone. +/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type) + var/list/results = list() + var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) + for(var/obj/item/clothing/C in clothing_items) + if(istype(C) && (C.body_parts_covered & def_zone.body_part)) + results.Add(C) + return results + +//this proc returns the armour value for a particular external organ. +/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type) + if(!type || !def_zone) + return 0 + var/protection = 0 + var/list/protective_gear = def_zone.get_covering_clothing() + for(var/obj/item/clothing/gear in protective_gear) + protection += gear.armor[type] + + for(var/datum/modifier/M as anything in modifiers) + var/modifier_armor = LAZYACCESS(M.armor_percent, type) + if(modifier_armor) + protection += modifier_armor + + return protection + +/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type) + if(!type || !def_zone) + return 0 + var/soaked = 0 + var/list/protective_gear = def_zone.get_covering_clothing() + for(var/obj/item/clothing/gear in protective_gear) + soaked += gear.armorsoak[type] + + for(var/datum/modifier/M as anything in modifiers) + var/modifier_armor = LAZYACCESS(M.armor_flat, type) + if(modifier_armor) + soaked += modifier_armor + + return soaked + +// Checked in borer code +/mob/living/carbon/human/proc/check_head_coverage() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/body_parts = H.get_covering_clothing(EYES) + if(LAZYLEN(body_parts)) + return 1 + return 0 + +//Used to check if they can be fed food/drinks/pills +/mob/living/carbon/human/proc/check_mouth_coverage() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/protective_gear = H.get_covering_clothing(FACE) + for(var/obj/item/gear in protective_gear) + if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL)) + return gear + return null + +/mob/living/carbon/human/proc/check_mouth_coverage_survival() + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + var/list/protective_gear = H.get_covering_clothing(FACE) + for(var/obj/item/gear in protective_gear) + if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL) && !(gear.item_flags & ALLOW_SURVIVALFOOD)) + return gear + return null + +/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) + if(!shield) continue + . = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text) + if(.) return + return 0 + +/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + if(check_neckgrab_attack(I, user, target_zone)) + return null + + if(user == src) // Attacking yourself can't miss + return target_zone + + var/hit_zone = get_zone_with_miss_chance(target_zone, src, user.get_accuracy_penalty()) + + if(!hit_zone) + user.do_attack_animation(src) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("\The [user] misses [src] with \the [I]!") + return null + + if(check_shields(I.force, I, user, target_zone, "the [I.name]")) + return + + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if (!affecting || affecting.is_stump()) + to_chat(user, "They are missing that limb!") + return null + + return hit_zone + +/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) + return //should be prevented by attacked_with_item() but for sanity. + + visible_message("[src] has been [LAZYLEN(I.attack_verb) ? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") + + var/soaked = get_armor_soak(hit_zone, "melee", I.armor_penetration) + + var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") + + standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone) + + return blocked + +/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) + return 0 + + // Allow clothing to respond to being hit. + // This is done up here so that clothing damage occurs even if fully blocked. + var/list/clothing = get_clothing_list_organ(affecting) + for(var/obj/item/clothing/C in clothing) + C.clothing_impact(I, effective_force) + + if(soaked >= round(effective_force*0.8)) + effective_force -= round(effective_force*0.8) + // Handle striking to cripple. + if(user.a_intent == I_DISARM) + effective_force *= 0.5 //reduced effective force... + if(!..(I, user, effective_force, blocked, soaked, hit_zone)) + return 0 + + //set the dislocate mult less than the effective force mult so that + //dislocating limbs on disarm is a bit easier than breaking limbs on harm + attack_joint(affecting, I, effective_force, 0.75, blocked, soaked) //...but can dislocate joints + else if(!..()) + return 0 + + if(effective_force > 10 || effective_force >= 5 && prob(33)) + forcesay(hit_appends) //forcesay checks stat already + + if(prob(25 + (effective_force * 2))) + if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS))) + return + + if(!(I.flags & NOBLOODY)) + I.add_blood(src) + + var/bloody = 0 + if(prob(33)) + bloody = 1 + var/turf/location = loc + if(istype(location, /turf/simulated)) + location.add_blood(src) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood + H.bloody_body(src) + H.bloody_hands(src) + + if(!stat && !(I.no_random_knockdown)) + switch(hit_zone) + if(BP_HEAD)//Harder to score a stun but if you do it lasts a bit longer + if(prob(effective_force)) + apply_effect(20, PARALYZE, blocked, soaked) + visible_message("\The [src] has been knocked unconscious!") + if(bloody)//Apply blood + if(wear_mask) + wear_mask.add_blood(src) + update_inv_wear_mask(0) + if(head) + head.add_blood(src) + update_inv_head(0) + if(glasses && prob(33)) + glasses.add_blood(src) + update_inv_glasses(0) + if(BP_TORSO)//Easier to score a stun but lasts less time + if(prob(effective_force + 10)) + apply_effect(6, WEAKEN, blocked, soaked) + visible_message("\The [src] has been knocked down!") + if(bloody) + bloody_body(src) + + return 1 + +/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked, var/soaked) + if(!organ || (organ.dislocated == 1) || (organ.dislocated == -1) || blocked >= 100) //VOREStation Edit Bugfix + return 0 + + if(W.damtype != BRUTE) + return 0 + + if(soaked >= round(effective_force*0.8)) + effective_force -= round(effective_force*0.8) + + //want the dislocation chance to be such that the limb is expected to dislocate after dealing a fraction of the damage needed to break the limb + var/dislocate_chance = effective_force/(dislocate_mult * organ.min_broken_damage * config.organ_health_multiplier)*100 + if(prob(dislocate_chance * (100 - blocked)/100)) + visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") + organ.dislocate(1) + return 1 + return 0 + +/mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source) + var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting) + if(!affecting || !(affecting.robotic >= ORGAN_ROBOT)) + to_chat(user, "That limb isn't robotic.") + return -1 + if(affecting.sabotaged) + to_chat(user, "[src]'s [affecting.name] is already sabotaged!") + return -1 + to_chat(user, "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties.") + affecting.sabotaged = 1 + return 1 + +//this proc handles being hit by a thrown atom +/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR) +// if(buckled && buckled == AM) +// return // Don't get hit by the thing we're buckled to. + + //VORESTATION EDIT START - Allows for thrown vore! + //Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred. + // I put more comments here for ease of reading. + if(istype(AM, /mob/living)) + var/mob/living/thrown_mob = AM + if(isanimal(thrown_mob) && !allowmobvore) //Is the thrown_mob an animal and we don't allow mobvore? + return + // PERSON BEING HIT: CAN BE DROP PRED, ALLOWS THROW VORE. + // PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY. + if((can_be_drop_pred && throw_vore && vore_selected) && (thrown_mob.devourable && thrown_mob.throw_vore && thrown_mob.can_be_drop_prey)) //Prey thrown into pred. + vore_selected.nom_mob(thrown_mob) //Eat them!!! + visible_message("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!") + if(thrown_mob.loc != vore_selected) + thrown_mob.forceMove(vore_selected) //Double check. Should never happen but...Weirder things have happened! + add_attack_logs(thrown_mob.thrower,src,"Devoured [thrown_mob.name] via throw vore.") + return //We can stop here. We don't need to calculate damage or anything else. They're eaten. + + // PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE. + // PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE. + else if((can_be_drop_prey && throw_vore && devourable) && (thrown_mob.can_be_drop_pred && thrown_mob.throw_vore && thrown_mob.vore_selected)) //Pred thrown into prey. + visible_message("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!") + thrown_mob.vore_selected.nom_mob(src) //Eat them!!! + if(src.loc != thrown_mob.vore_selected) + src.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened! + add_attack_logs(thrown_mob.LAssailant,src,"Was Devoured by [thrown_mob.name] via throw vore.") + return + //VORESTATION EDIT END - Allows for thrown vore! + + if(istype(AM,/obj/)) + var/obj/O = AM + if(in_throw_mode && speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode + if(canmove && !restrained()) + if(isturf(O.loc)) + if(can_catch(O)) + put_in_active_hand(O) + visible_message("[src] catches [O]!") + throw_mode_off() + return + + var/dtype = O.damtype + var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR) + + if(species && species.throwforce_absorb_threshold >= throw_damage) + visible_message("\The [O] simply bounces off of [src]'s body!") + return + + var/zone + if (istype(O.thrower, /mob/living)) + var/mob/living/L = O.thrower + zone = check_zone(L.zone_sel.selecting) + else + zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest + + //check if we hit + var/miss_chance = 15 + if (O.throw_source) + var/distance = get_dist(O.throw_source, loc) + miss_chance = max(15*(distance-2), 0) + zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1) + + if(zone && O.thrower != src) + var/shield_check = check_shields(throw_damage, O, thrower, zone, "[O]") + if(shield_check == PROJECTILE_FORCE_MISS) + zone = null + else if(shield_check) + return + + if(!zone) + visible_message("\The [O] misses [src] narrowly!") + return + + O.throwing = 0 //it hit, so stop moving + + var/obj/item/organ/external/affecting = get_organ(zone) + var/hit_area = affecting.name + + src.visible_message("[span_red("[src] has been hit in the [hit_area] by [O].")]") + + if(ismob(O.thrower)) + add_attack_logs(O.thrower,src,"Hit with thrown [O.name]") + + //If the armor absorbs all of the damage, skip the rest of the calculations + var/soaked = get_armor_soak(affecting, "melee", O.armor_penetration) + if(soaked >= throw_damage) + to_chat(src, "Your armor absorbs the force of [O.name]!") + return + + var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here + if(armor < 100) + apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) + + + //thrown weapon embedded object code. + if(dtype == BRUTE && istype(O,/obj/item)) + var/obj/item/I = O + if (!is_robot_module(I)) + var/sharp = is_sharp(I) + var/damage = throw_damage + if (soaked) + damage -= soaked + if (armor) + damage /= armor+1 + + //blunt objects should really not be embedding in things unless a huge amount of force is involved + var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3) + var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class + + //Sharp objects will always embed if they do enough damage. + //Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold + if((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance))) + affecting.embed(I) + + // Begin BS12 momentum-transfer code. + var/mass = 1.5 + if(istype(O, /obj/item)) + var/obj/item/I = O + mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR + var/momentum = speed*mass + + if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED && !buckled) + var/dir = get_dir(O.throw_source, src) + + visible_message("[span_red("[src] staggers under the impact!")]","[span_red("You stagger under the impact!")]") + src.throw_at(get_edge_target_turf(src,dir),1,momentum) + + if(!O || !src) return + + if(O.loc == src && O.sharp) //Projectile is embedded and suitable for pinning. + var/turf/T = near_wall(dir,2) + + if(T) + src.loc = T + visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") + src.anchored = TRUE + src.pinned += O + +// This does a prob check to catch the thing flying at you, with a minimum of 1% +/mob/living/carbon/human/proc/can_catch(var/obj/O) + if(!get_active_hand()) // If active hand is empty + var/obj/item/organ/external/temp = organs_by_name["r_hand"] + if (hand) + temp = organs_by_name["l_hand"] + if(temp && !temp.is_usable()) + return FALSE // The hand isn't working in the first place + + if(!O.catchable) + return FALSE + + // Alright, our hand works? Time to try the catching. + var/catch_chance = 90 // Default 90% catch rate + + if(O.sharp) + catch_chance -= 50 // Catching knives is hard + + catch_chance -= get_accuracy_penalty() // Same issues with shooting a gun, or swinging a weapon + + catch_chance = between(1, catch_chance, 100) + + if(prob(catch_chance)) + return TRUE + return FALSE + +/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null) + if(!def_zone) ..() + + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + affecting.embed(O) + + +/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) + if (gloves) + gloves.add_blood(source) + gloves:transfer_blood = amount + gloves:bloody_hands_mob = source + else + add_blood(source) + bloody_hands = amount + bloody_hands_mob = source + update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves + +/mob/living/carbon/human/proc/bloody_body(var/mob/living/source) + if(wear_suit) + wear_suit.add_blood(source) + update_inv_wear_suit(0) + if(w_uniform) + w_uniform.add_blood(source) + update_inv_w_uniform(0) + +/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage, var/def_zone) + + // Tox and oxy don't matter to suits. + if(damtype != BURN && damtype != BRUTE) return + + // The rig might soak this hit, if we're wearing one. + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() + rig.take_hit(damage) + + // We may also be taking a suit breach. + if(!wear_suit) return + if(!istype(wear_suit,/obj/item/clothing/suit/space)) return + var/obj/item/clothing/suit/space/SS = wear_suit + var/penetrated_dam = max(0,(damage - SS.breach_threshold)) + if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) + +/mob/living/carbon/human/reagent_permeability() + var/perm = 0 + + var/list/perm_by_part = list( + "head" = THERMAL_PROTECTION_HEAD, + "upper_torso" = THERMAL_PROTECTION_UPPER_TORSO, + "lower_torso" = THERMAL_PROTECTION_LOWER_TORSO, + "legs" = THERMAL_PROTECTION_LEG_LEFT + THERMAL_PROTECTION_LEG_RIGHT, + "feet" = THERMAL_PROTECTION_FOOT_LEFT + THERMAL_PROTECTION_FOOT_RIGHT, + "arms" = THERMAL_PROTECTION_ARM_LEFT + THERMAL_PROTECTION_ARM_RIGHT, + "hands" = THERMAL_PROTECTION_HAND_LEFT + THERMAL_PROTECTION_HAND_RIGHT + ) + + for(var/obj/item/clothing/C in src.get_equipped_items()) + if(C.permeability_coefficient == 1 || !C.body_parts_covered) + continue + if(C.body_parts_covered & HEAD) + perm_by_part["head"] *= C.permeability_coefficient + if(C.body_parts_covered & UPPER_TORSO) + perm_by_part["upper_torso"] *= C.permeability_coefficient + if(C.body_parts_covered & LOWER_TORSO) + perm_by_part["lower_torso"] *= C.permeability_coefficient + if(C.body_parts_covered & LEGS) + perm_by_part["legs"] *= C.permeability_coefficient + if(C.body_parts_covered & FEET) + perm_by_part["feet"] *= C.permeability_coefficient + if(C.body_parts_covered & ARMS) + perm_by_part["arms"] *= C.permeability_coefficient + if(C.body_parts_covered & HANDS) + perm_by_part["hands"] *= C.permeability_coefficient + + for(var/part in perm_by_part) + perm += perm_by_part[part] + + return perm + +// This is for preventing harm by being covered in water, which only prometheans need to deal with. +/mob/living/carbon/human/get_water_protection() + var/protection = species.water_resistance + if(protection == 1) // No point doing permeability checks if it won't matter. + return protection + // Wearing clothing with a low permeability_coefficient can protect from water. + + var/converted_protection = 1 - protection + var/perm = reagent_permeability() + converted_protection *= perm + return CLAMP(1-converted_protection, 0, 1) + +/mob/living/carbon/human/water_act(amount) + adjust_fire_stacks(-amount * 5) + for(var/atom/movable/AM in contents) + AM.water_act(amount) + remove_modifiers_of_type(/datum/modifier/fire) + + species.handle_water_damage(src, amount) + +/mob/living/carbon/human/shank_attack(obj/item/W, obj/item/weapon/grab/G, mob/user, hit_zone) + + if(!..()) + return 0 + + var/organ_chance = 50 + var/damage = shank_armor_helper(W, G, user) + var/obj/item/organ/external/chest = get_organ(hit_zone) + + if(W.edge) + organ_chance = 75 + user.next_move = world.time + 20 + user.visible_message("\The [user] begins to twist \the [W] around inside [src]'s [chest]!") + if(!do_after(user, 20)) + return 0 + if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab + return 0 + + user.visible_message("\The [user] twists \the [W] around inside [src]'s [chest]!") + + if(prob(organ_chance)) + var/obj/item/organ/internal/selected_organ = pick(chest.internal_organs) + selected_organ.damage = max(selected_organ.damage, damage * 0.5) + G.last_action = world.time + flick(G.hud.icon_state, G.hud) + + return 1 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 4b2236218ef..f2e09405ee4 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,161 +1,163 @@ -/mob/living/carbon/human - //Hair colour and style - var/r_hair = 0 - var/g_hair = 0 - var/b_hair = 0 - var/h_style = "Bald" - - var/r_grad = 0 - var/g_grad = 0 - var/b_grad = 0 - var/grad_style = "none" - //Facial hair colour and style - var/r_facial = 0 - var/g_facial = 0 - var/b_facial = 0 - var/f_style = "Shaved" - - //Eye colour - var/r_eyes = 0 - var/g_eyes = 0 - var/b_eyes = 0 - - var/s_tone = 0 //Skin tone - - //Skin colour - var/r_skin = 0 - var/g_skin = 0 - var/b_skin = 0 - - var/skin_state = SKIN_NORMAL - - //Synth colors - var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. - var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. - var/g_synth //Same as above - var/b_synth //Same as above - var/synth_markings = 0 //Enables/disables markings on synth parts. - - //var/size_multiplier = 1 //multiplier for the mob's icon size //VOREStation Edit (Moved to /mob/living) - var/damage_multiplier = 1 //multiplies melee combat damage - var/icon_update = 1 //whether icon updating shall take place - - var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup - - var/age = 30 //Player's age (pure fluff) - var/bday_month = 0 //Character birth month - var/bday_day = 0 //Character birthday day - - var/b_type = "A+" //Player's bloodtype - var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso). Also holds the datum for the type of robolimb. - - var/list/all_underwear = list() - var/list/all_underwear_metadata = list() - var/list/hide_underwear = list() - var/backbag = 2 //Which backpack type the player has chosen. - var/pdachoice = 1 //Which PDA type the player has chosen. - - // General information - var/home_system = "" - var/birthplace = "" - var/citizenship = "" - var/personal_faction = "" - var/religion = "" - var/antag_faction = "" - var/antag_vis = "" - - //Equipment slots - var/obj/item/wear_suit = null - var/obj/item/w_uniform = null - var/obj/item/shoes = null - var/obj/item/belt = null - var/obj/item/gloves = null - var/obj/item/glasses = null - var/obj/item/head = null - var/obj/item/l_ear = null - var/obj/item/r_ear = null - var/obj/item/wear_id = null - var/obj/item/r_store = null - var/obj/item/l_store = null - var/obj/item/s_store = null - - var/used_skillpoints = 0 - var/skill_specialization = null - var/list/skills = list() - - var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() - - var/special_voice = "" // For changing our voice. Used by a symptom. - - var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none. - - var/xylophone = 0 //For the spoooooooky xylophone cooldown - - var/mob/remoteview_target = null - var/hand_blood_color - - var/list/flavor_texts = list() - var/gunshot_residue - var/pulling_punches // Are you trying not to hurt your opponent? - var/robolimb_count = 0 // Total number of external robot parts. - var/robobody_count = 0 // Counts torso, groin, and head, if they're robotic - - mob_bump_flag = HUMAN - mob_push_flags = ~HEAVY - mob_swap_flags = ~HEAVY - - var/identifying_gender // In case the human identifies as another gender than it's biological - - var/list/descriptors // For comparative examine code - - var/step_count = 0 // Track how many footsteps have been taken to know when to play footstep sounds - - can_be_antagged = TRUE - -// Used by mobs in virtual reality to point back to the "real" mob the client belongs to. - var/mob/living/carbon/human/vr_holder = null - // Used by "real" mobs after they leave a VR session - var/mob/living/carbon/human/vr_link = null - - var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently. - - inventory_panel_type = /datum/inventory_panel/human - butchery_loot = list(/obj/item/stack/animalhide/human = 1) - - // Horray Furries! - var/datum/sprite_accessory/ears/ear_style = null - var/r_ears = 30 - var/g_ears = 30 - var/b_ears = 30 - var/r_ears2 = 30 - var/g_ears2 = 30 - var/b_ears2 = 30 - var/r_ears3 = 30 //Trust me, we could always use more colour. No japes. - var/g_ears3 = 30 - var/b_ears3 = 30 - var/datum/sprite_accessory/tail/tail_style = null - var/r_tail = 30 - var/g_tail = 30 - var/b_tail = 30 - var/r_tail2 = 30 - var/g_tail2 = 30 - var/b_tail2 = 30 - var/r_tail3 = 30 - var/g_tail3 = 30 - var/b_tail3 = 30 - var/datum/sprite_accessory/wing/wing_style = null - var/r_wing = 30 - var/g_wing = 30 - var/b_wing = 30 - var/r_wing2 = 30 - var/g_wing2 = 30 - var/b_wing2 = 30 - var/r_wing3 = 30 - var/g_wing3 = 30 - var/b_wing3 = 30 - - var/wagging = 0 //UGH. - var/flapping = 0 - - // Custom Species Name - var/custom_species +/mob/living/carbon/human + //Hair colour and style + var/r_hair = 0 + var/g_hair = 0 + var/b_hair = 0 + var/h_style = "Bald" + + var/r_grad = 0 + var/g_grad = 0 + var/b_grad = 0 + var/grad_style = "none" + //Facial hair colour and style + var/r_facial = 0 + var/g_facial = 0 + var/b_facial = 0 + var/f_style = "Shaved" + + //Eye colour + var/r_eyes = 0 + var/g_eyes = 0 + var/b_eyes = 0 + + var/s_tone = 0 //Skin tone + + //Skin colour + var/r_skin = 0 + var/g_skin = 0 + var/b_skin = 0 + + var/skin_state = SKIN_NORMAL + + //Synth colors + var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. + var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. + var/g_synth //Same as above + var/b_synth //Same as above + var/synth_markings = 0 //Enables/disables markings on synth parts. + + var/digitigrade = 0 // 0 = no digi, 1 = default, 2+ = digi styles... (Not used yet) + + //var/size_multiplier = 1 //multiplier for the mob's icon size //VOREStation Edit (Moved to /mob/living) + var/damage_multiplier = 1 //multiplies melee combat damage + var/icon_update = 1 //whether icon updating shall take place + + var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup + + var/age = 30 //Player's age (pure fluff) + var/bday_month = 0 //Character birth month + var/bday_day = 0 //Character birthday day + + var/b_type = "A+" //Player's bloodtype + var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso). Also holds the datum for the type of robolimb. + + var/list/all_underwear = list() + var/list/all_underwear_metadata = list() + var/list/hide_underwear = list() + var/backbag = 2 //Which backpack type the player has chosen. + var/pdachoice = 1 //Which PDA type the player has chosen. + + // General information + var/home_system = "" + var/birthplace = "" + var/citizenship = "" + var/personal_faction = "" + var/religion = "" + var/antag_faction = "" + var/antag_vis = "" + + //Equipment slots + var/obj/item/wear_suit = null + var/obj/item/w_uniform = null + var/obj/item/shoes = null + var/obj/item/belt = null + var/obj/item/gloves = null + var/obj/item/glasses = null + var/obj/item/head = null + var/obj/item/l_ear = null + var/obj/item/r_ear = null + var/obj/item/wear_id = null + var/obj/item/r_store = null + var/obj/item/l_store = null + var/obj/item/s_store = null + + var/used_skillpoints = 0 + var/skill_specialization = null + var/list/skills = list() + + var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() + + var/special_voice = "" // For changing our voice. Used by a symptom. + + var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none. + + var/xylophone = 0 //For the spoooooooky xylophone cooldown + + var/mob/remoteview_target = null + var/hand_blood_color + + var/list/flavor_texts = list() + var/gunshot_residue + var/pulling_punches // Are you trying not to hurt your opponent? + var/robolimb_count = 0 // Total number of external robot parts. + var/robobody_count = 0 // Counts torso, groin, and head, if they're robotic + + mob_bump_flag = HUMAN + mob_push_flags = ~HEAVY + mob_swap_flags = ~HEAVY + + var/identifying_gender // In case the human identifies as another gender than it's biological + + var/list/descriptors // For comparative examine code + + var/step_count = 0 // Track how many footsteps have been taken to know when to play footstep sounds + + can_be_antagged = TRUE + +// Used by mobs in virtual reality to point back to the "real" mob the client belongs to. + var/mob/living/carbon/human/vr_holder = null + // Used by "real" mobs after they leave a VR session + var/mob/living/carbon/human/vr_link = null + + var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently. + + inventory_panel_type = /datum/inventory_panel/human + butchery_loot = list(/obj/item/stack/animalhide/human = 1) + + // Horray Furries! + var/datum/sprite_accessory/ears/ear_style = null + var/r_ears = 30 + var/g_ears = 30 + var/b_ears = 30 + var/r_ears2 = 30 + var/g_ears2 = 30 + var/b_ears2 = 30 + var/r_ears3 = 30 //Trust me, we could always use more colour. No japes. + var/g_ears3 = 30 + var/b_ears3 = 30 + var/datum/sprite_accessory/tail/tail_style = null + var/r_tail = 30 + var/g_tail = 30 + var/b_tail = 30 + var/r_tail2 = 30 + var/g_tail2 = 30 + var/b_tail2 = 30 + var/r_tail3 = 30 + var/g_tail3 = 30 + var/b_tail3 = 30 + var/datum/sprite_accessory/wing/wing_style = null + var/r_wing = 30 + var/g_wing = 30 + var/b_wing = 30 + var/r_wing2 = 30 + var/g_wing2 = 30 + var/b_wing2 = 30 + var/r_wing3 = 30 + var/g_wing3 = 30 + var/b_wing3 = 30 + + var/wagging = 0 //UGH. + var/flapping = 0 + + // Custom Species Name + var/custom_species diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 3f10aeeae06..65b4039cddc 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,304 +1,304 @@ -#define HUMAN_LOWEST_SLOWDOWN -3 - -/mob/living/carbon/human/movement_delay(oldloc, direct) - - . = 0 - - if (istype(loc, /turf/space)) - return ..() - 1 - - if(species.slowdown) - . += species.slowdown - - if(force_max_speed) - return ..() + HUMAN_LOWEST_SLOWDOWN - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.haste) && M.haste == TRUE) - return ..() + HUMAN_LOWEST_SLOWDOWN // Returning -1 will actually result in a slowdown for Teshari. - if(!isnull(M.slowdown)) - . += M.slowdown - - var/health_deficiency = (getMaxHealth() - health) - if(istype(src, /mob/living/carbon/human)) //VOREStation Edit Start - var/mob/living/carbon/human/H = src - health_deficiency *= H.species.trauma_mod //Species pain sensitivity does not apply to painkillers, so we apply it before - if(health_deficiency >= 40) - if(chem_effects[CE_PAINKILLER]) //On painkillers? Reduce pain! On anti-painkillers? Increase pain! - health_deficiency = max(0, health_deficiency - src.chem_effects[CE_PAINKILLER]) - if(health_deficiency >= 40) //Still in enough pain for it to be significant? - . += (health_deficiency / 25) //VOREStation Edit End - - if(can_feel_pain()) - if(halloss >= 10) . += (halloss / 10) //halloss shouldn't slow you down if you can't even feel it - - var/hungry = (500 - nutrition) / 5 //VOREStation Edit - Fixed 500 here instead of our huge MAX_NUTRITION - if (hungry >= 70) . += hungry/50 - - //VOREstation start - if (feral >= 10) //crazy feral animals give less and less of a shit about pain and hunger as they get crazier - . = max(species.slowdown, species.slowdown+((.-species.slowdown)/(feral/10))) // As feral scales to damage, this amounts to an effective +1 slowdown cap - if(shock_stage >= 10) . -= 1.5 //this gets a +3 later, feral critters take reduced penalty - if(riding_datum) //Bit of slowdown for taur rides if rider is bigger or fatter than mount. - var/datum/riding/R = riding_datum - var/mob/living/L = R.ridden - for(var/mob/living/M in L.buckled_mobs) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.size_multiplier > L.size_multiplier) - . += 1 - //if(H.weight > L.weight) weight should not have mechanical impact - //. += 1 - //VOREstation end - - if(istype(buckled, /obj/structure/bed/chair/wheelchair)) - for(var/organ_name in list(BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM)) - var/obj/item/organ/external/E = get_organ(organ_name) - if(!E || E.is_stump()) - . += 4 - else if(E.splinted && E.splinted.loc != E) - . += 0.5 - else if(E.status & ORGAN_BROKEN) - . += 1.5 - else - for(var/organ_name in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/E = get_organ(organ_name) - if(!E || E.is_stump()) - . += 4 - else if(E.splinted && E.splinted.loc != E) - . += 0.5 - else if(E.status & ORGAN_BROKEN) - . += 1.5 - - if(shock_stage >= 10) . += 3 - - if(aiming && aiming.aiming_at) . += 5 // Iron sights make you slower, it's a well-known fact. - - if(FAT in src.mutations) - . += 1.5 - - if (bodytemperature < species.cold_level_1) - . += (species.cold_level_1 - bodytemperature) / 10 * 1.75 - - . += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow - - if(mRun in mutations) - . = 0 - - // Turf related slowdown - var/turf/T = get_turf(src) - . += calculate_turf_slowdown(T, direct) - - // Item related slowdown. - var/item_tally = calculate_item_encumbrance() - - // Dragging heavy objects will also slow you down, similar to above. - if(pulling) - if(istype(pulling, /obj/item)) - var/obj/item/pulled = pulling - item_tally += max(pulled.slowdown, 0) - else if(ishuman(pulling)) - var/mob/living/carbon/human/H = pulling - var/their_slowdown = max(H.calculate_item_encumbrance(), 1) - item_tally = max(item_tally, their_slowdown) // If our slowdown is less than theirs, then we become as slow as them (before species modifires). - - item_tally *= species.item_slowdown_mod - - . += item_tally - - if(CE_SLOWDOWN in chem_effects) - if (. >= 0 ) - . *= 1.25 //Add a quarter of penalties on top. - . += chem_effects[CE_SLOWDOWN] - - if(CE_SPEEDBOOST in chem_effects) - if (. >= 0) // cut any penalties in half - . *= 0.5 - . -= chem_effects[CE_SPEEDBOOST] // give 'em a buff on top. - - . = max(HUMAN_LOWEST_SLOWDOWN, . + config.human_delay) // Minimum return should be the same as force_max_speed - . += ..() - -/mob/living/carbon/human/Moved() - . = ..() - if(embedded_flag) - handle_embedded_objects() //Moving with objects stuck in you can cause bad times. - -// This calculates the amount of slowdown to receive from items worn. This does NOT include species modifiers. -// It is in a seperate place to avoid an infinite loop situation with dragging mobs dragging each other. -// Also its nice to have these things seperated. -/mob/living/carbon/human/proc/calculate_item_encumbrance() - if(shoes) // Shoes can make you go faster. - if(!buckled || (buckled && istype(buckled, /obj/machinery/power/rtg/reg))) - . += shoes.slowdown - - // Loop through some slots, and add up their slowdowns. - // Includes slots which can provide armor, the back slot, and suit storage. - for(var/obj/item/I in list(wear_suit, w_uniform, back, gloves, head, s_store)) - . += I.slowdown - - // Hands are also included, to make the 'take off your armor instantly and carry it with you to go faster' trick no longer viable. - // This is done seperately to disallow negative numbers (so you can't hold shoes in your hands to go faster). - for(var/obj/item/I in list(r_hand, l_hand) ) - . += max(I.slowdown, 0) - -// Similar to above, but for turf slowdown. -/mob/living/carbon/human/proc/calculate_turf_slowdown(turf/T, direct) - if(!T) - return 0 - - if(T.movement_cost) - var/turf_move_cost = T.movement_cost - if(istype(T, /turf/simulated/floor/water)) - if(species.water_movement) - turf_move_cost = CLAMP(turf_move_cost + species.water_movement, HUMAN_LOWEST_SLOWDOWN, 15) - if(shoes) - var/obj/item/clothing/shoes/feet = shoes - if(istype(feet) && feet.water_speed) - turf_move_cost = CLAMP(turf_move_cost + feet.water_speed, HUMAN_LOWEST_SLOWDOWN, 15) - . += turf_move_cost - else if(istype(T, /turf/simulated/floor/outdoors/snow)) - if(species.snow_movement) - turf_move_cost = CLAMP(turf_move_cost + species.snow_movement, HUMAN_LOWEST_SLOWDOWN, 15) - if(shoes) - var/obj/item/clothing/shoes/feet = shoes - if(istype(feet) && feet.snow_speed) - turf_move_cost = CLAMP(turf_move_cost + feet.snow_speed, HUMAN_LOWEST_SLOWDOWN, 15) - . += turf_move_cost - else - turf_move_cost = CLAMP(turf_move_cost, HUMAN_LOWEST_SLOWDOWN, 15) - . += turf_move_cost - - // Wind makes it easier or harder to move, depending on if you're with or against the wind. - // I don't like that so I'm commenting it out :) - // VOREstation Edit Start -/* - if((T.is_outdoors()) && (T.z <= SSplanets.z_to_planet.len)) - var/datum/planet/P = SSplanets.z_to_planet[z] - if(P) - var/datum/weather_holder/WH = P.weather_holder - if(WH && WH.wind_speed) // Is there any wind? - // With the wind. - if(direct & WH.wind_dir) - . = max(. - WH.wind_speed, -1) // Wind speedup is capped to prevent supersonic speeds from a storm. - // Against it. - else if(direct & reverse_dir[WH.wind_dir]) - . += WH.wind_speed - -*/ -// VOREstation Edit End. -#undef HUMAN_LOWEST_SLOWDOWN - -/mob/living/carbon/human/get_jetpack() - if(back) - var/obj/item/weapon/rig/rig = get_rig() - if(istype(back, /obj/item/weapon/tank/jetpack)) - return back - else if(istype(rig)) - for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) - return module.jets - -/mob/living/carbon/human/Process_Spacemove(var/check_drift = 0) - //Can we act? - if(restrained()) return 0 - - if(..()) //Can move due to other reasons, don't use jetpack fuel - return TRUE - - if(species.can_space_freemove || (species.can_zero_g_move && !istype(get_turf(src), /turf/space))) //VOREStation Edit. - return TRUE //VOREStation Edit. - - if(flying) //VOREStation Edit. If you're flying, you glide around! - return TRUE //VOREStation Edit. - - //Do we have a working jetpack? - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - - if(thrust) - if(((!check_drift) || (check_drift && thrust.stabilization_on)) && (!lying) && (thrust.do_thrust(0.01, src))) - inertia_dir = 0 - return TRUE - - return FALSE - - -/mob/living/carbon/human/Process_Spaceslipping(var/prob_slip = 5) - //If knocked out we might just hit it and stop. This makes it possible to get dead bodies and such. - - if(species.flags & NO_SLIP) - return FALSE - - if(species.can_space_freemove || species.can_zero_g_move) - return FALSE - - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - if(thrust?.can_thrust(0.01)) - return FALSE - - if(stat) - prob_slip = 0 // Changing this to zero to make it line up with the comment, and also, make more sense. - - //Do we have magboots or such on if so no slip - if(istype(shoes, /obj/item/clothing/shoes/magboots) && (shoes.item_flags & NOSLIP)) - prob_slip = 0 - - //Check hands and mod slip - if(!l_hand) prob_slip -= 2 - else if(l_hand.w_class <= 2) prob_slip -= 1 - if (!r_hand) prob_slip -= 2 - else if(r_hand.w_class <= 2) prob_slip -= 1 - - prob_slip = round(prob_slip) - return(prob_slip) - -// Handle footstep sounds -/mob/living/carbon/human/handle_footstep(var/turf/T) - if(!istype(T)) - return - if(is_incorporeal()) - return - if(!config.footstep_volume || !T.footstep_sounds || !T.footstep_sounds.len) - return - // Future Upgrades - Multi species support - var/list/footstep_sounds = T.footstep_sounds["human"] - if(!footstep_sounds) - return - - var/S = pick(footstep_sounds) - GLOB.step_taken_shift_roundstat++ - if(!S) return - - // Play every 20 steps while walking, for the sneak - if(m_intent == "walk" && step_count++ % 20 != 0) - return - - // Play every other step while running - if(m_intent == "run" && step_count++ % 2 != 0) - return - - var/volume = config.footstep_volume - - // Reduce volume while walking or barefoot - if(!shoes || m_intent == "walk") - volume *= 0.5 - else if(shoes) - var/obj/item/clothing/shoes/feet = shoes - if(istype(feet)) - volume *= feet.step_volume_mod - - if(!has_organ(BP_L_FOOT) && !has_organ(BP_R_FOOT)) - return // no feet = no footsteps - - if(buckled || lying || throwing) - return // people flying, lying down or sitting do not step - - if(!has_gravity(src) && prob(75)) - return // Far less likely to make noise in no gravity - - playsound(T, S, volume, FALSE) - return - -/mob/living/carbon/human/set_dir(var/new_dir) - . = ..() - if(. && (species.tail || tail_style)) +#define HUMAN_LOWEST_SLOWDOWN -3 + +/mob/living/carbon/human/movement_delay(oldloc, direct) + + . = 0 + + if (istype(loc, /turf/space)) + return ..() - 1 + + if(species.slowdown) + . += species.slowdown + + if(force_max_speed) + return ..() + HUMAN_LOWEST_SLOWDOWN + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.haste) && M.haste == TRUE) + return ..() + HUMAN_LOWEST_SLOWDOWN // Returning -1 will actually result in a slowdown for Teshari. + if(!isnull(M.slowdown)) + . += M.slowdown + + var/health_deficiency = (getMaxHealth() - health) + if(istype(src, /mob/living/carbon/human)) //VOREStation Edit Start + var/mob/living/carbon/human/H = src + health_deficiency *= H.species.trauma_mod //Species pain sensitivity does not apply to painkillers, so we apply it before + if(health_deficiency >= 40) + if(chem_effects[CE_PAINKILLER]) //On painkillers? Reduce pain! On anti-painkillers? Increase pain! + health_deficiency = max(0, health_deficiency - src.chem_effects[CE_PAINKILLER]) + if(health_deficiency >= 40) //Still in enough pain for it to be significant? + . += (health_deficiency / 25) //VOREStation Edit End + + if(can_feel_pain()) + if(halloss >= 10) . += (halloss / 10) //halloss shouldn't slow you down if you can't even feel it + + var/hungry = (500 - nutrition) / 5 //VOREStation Edit - Fixed 500 here instead of our huge MAX_NUTRITION + if (hungry >= 70) . += hungry/50 + + //VOREstation start + if (feral >= 10) //crazy feral animals give less and less of a shit about pain and hunger as they get crazier + . = max(species.slowdown, species.slowdown+((.-species.slowdown)/(feral/10))) // As feral scales to damage, this amounts to an effective +1 slowdown cap + if(shock_stage >= 10) . -= 1.5 //this gets a +3 later, feral critters take reduced penalty + if(riding_datum) //Bit of slowdown for taur rides if rider is bigger or fatter than mount. + var/datum/riding/R = riding_datum + var/mob/living/L = R.ridden + for(var/mob/living/M in L.buckled_mobs) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.size_multiplier > L.size_multiplier) + . += 1 + //if(H.weight > L.weight) weight should not have mechanical impact + //. += 1 + //VOREstation end + + if(istype(buckled, /obj/structure/bed/chair/wheelchair)) + for(var/organ_name in list(BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM)) + var/obj/item/organ/external/E = get_organ(organ_name) + if(!E || E.is_stump()) + . += 4 + else if(E.splinted && E.splinted.loc != E) + . += 0.5 + else if(E.status & ORGAN_BROKEN) + . += 1.5 + else + for(var/organ_name in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/E = get_organ(organ_name) + if(!E || E.is_stump()) + . += 4 + else if(E.splinted && E.splinted.loc != E) + . += 0.5 + else if(E.status & ORGAN_BROKEN) + . += 1.5 + + if(shock_stage >= 10) . += 3 + + if(aiming && aiming.aiming_at) . += 5 // Iron sights make you slower, it's a well-known fact. + + if(FAT in src.mutations) + . += 1.5 + + if (bodytemperature < species.cold_level_1) + . += (species.cold_level_1 - bodytemperature) / 10 * 1.75 + + . += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow + + if(mRun in mutations) + . = 0 + + // Turf related slowdown + var/turf/T = get_turf(src) + . += calculate_turf_slowdown(T, direct) + + // Item related slowdown. + var/item_tally = calculate_item_encumbrance() + + // Dragging heavy objects will also slow you down, similar to above. + if(pulling) + if(istype(pulling, /obj/item)) + var/obj/item/pulled = pulling + item_tally += max(pulled.slowdown, 0) + else if(ishuman(pulling)) + var/mob/living/carbon/human/H = pulling + var/their_slowdown = max(H.calculate_item_encumbrance(), 1) + item_tally = max(item_tally, their_slowdown) // If our slowdown is less than theirs, then we become as slow as them (before species modifires). + + item_tally *= species.item_slowdown_mod + + . += item_tally + + if(CE_SLOWDOWN in chem_effects) + if (. >= 0 ) + . *= 1.25 //Add a quarter of penalties on top. + . += chem_effects[CE_SLOWDOWN] + + if(CE_SPEEDBOOST in chem_effects) + if (. >= 0) // cut any penalties in half + . *= 0.5 + . -= chem_effects[CE_SPEEDBOOST] // give 'em a buff on top. + + . = max(HUMAN_LOWEST_SLOWDOWN, . + config.human_delay) // Minimum return should be the same as force_max_speed + . += ..() + +/mob/living/carbon/human/Moved() + . = ..() + if(embedded_flag) + handle_embedded_objects() //Moving with objects stuck in you can cause bad times. + +// This calculates the amount of slowdown to receive from items worn. This does NOT include species modifiers. +// It is in a seperate place to avoid an infinite loop situation with dragging mobs dragging each other. +// Also its nice to have these things seperated. +/mob/living/carbon/human/proc/calculate_item_encumbrance() + if(shoes) // Shoes can make you go faster. + if(!buckled || (buckled && istype(buckled, /obj/machinery/power/rtg/reg))) + . += shoes.slowdown + + // Loop through some slots, and add up their slowdowns. + // Includes slots which can provide armor, the back slot, and suit storage. + for(var/obj/item/I in list(wear_suit, w_uniform, back, gloves, head, s_store)) + . += I.slowdown + + // Hands are also included, to make the 'take off your armor instantly and carry it with you to go faster' trick no longer viable. + // This is done seperately to disallow negative numbers (so you can't hold shoes in your hands to go faster). + for(var/obj/item/I in list(r_hand, l_hand) ) + . += max(I.slowdown, 0) + +// Similar to above, but for turf slowdown. +/mob/living/carbon/human/proc/calculate_turf_slowdown(turf/T, direct) + if(!T) + return 0 + + if(T.movement_cost) + var/turf_move_cost = T.movement_cost + if(istype(T, /turf/simulated/floor/water)) + if(species.water_movement) + turf_move_cost = CLAMP(turf_move_cost + species.water_movement, HUMAN_LOWEST_SLOWDOWN, 15) + if(shoes) + var/obj/item/clothing/shoes/feet = shoes + if(istype(feet) && feet.water_speed) + turf_move_cost = CLAMP(turf_move_cost + feet.water_speed, HUMAN_LOWEST_SLOWDOWN, 15) + . += turf_move_cost + else if(istype(T, /turf/simulated/floor/outdoors/snow)) + if(species.snow_movement) + turf_move_cost = CLAMP(turf_move_cost + species.snow_movement, HUMAN_LOWEST_SLOWDOWN, 15) + if(shoes) + var/obj/item/clothing/shoes/feet = shoes + if(istype(feet) && feet.snow_speed) + turf_move_cost = CLAMP(turf_move_cost + feet.snow_speed, HUMAN_LOWEST_SLOWDOWN, 15) + . += turf_move_cost + else + turf_move_cost = CLAMP(turf_move_cost, HUMAN_LOWEST_SLOWDOWN, 15) + . += turf_move_cost + + // Wind makes it easier or harder to move, depending on if you're with or against the wind. + // I don't like that so I'm commenting it out :) + // VOREstation Edit Start +/* + if((T.is_outdoors()) && (T.z <= SSplanets.z_to_planet.len)) + var/datum/planet/P = SSplanets.z_to_planet[z] + if(P) + var/datum/weather_holder/WH = P.weather_holder + if(WH && WH.wind_speed) // Is there any wind? + // With the wind. + if(direct & WH.wind_dir) + . = max(. - WH.wind_speed, -1) // Wind speedup is capped to prevent supersonic speeds from a storm. + // Against it. + else if(direct & reverse_dir[WH.wind_dir]) + . += WH.wind_speed + +*/ +// VOREstation Edit End. +#undef HUMAN_LOWEST_SLOWDOWN + +/mob/living/carbon/human/get_jetpack() + if(back) + var/obj/item/weapon/rig/rig = get_rig() + if(istype(back, /obj/item/weapon/tank/jetpack)) + return back + else if(istype(rig)) + for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) + return module.jets + +/mob/living/carbon/human/Process_Spacemove(var/check_drift = 0) + //Can we act? + if(restrained()) return 0 + + if(..()) //Can move due to other reasons, don't use jetpack fuel + return TRUE + + if(species.can_space_freemove || (species.can_zero_g_move && !istype(get_turf(src), /turf/space))) //VOREStation Edit. + return TRUE //VOREStation Edit. + + if(flying) //VOREStation Edit. If you're flying, you glide around! + return TRUE //VOREStation Edit. + + //Do we have a working jetpack? + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + + if(thrust) + if(((!check_drift) || (check_drift && thrust.stabilization_on)) && (!lying) && (thrust.do_thrust(0.01, src))) + inertia_dir = 0 + return TRUE + + return FALSE + + +/mob/living/carbon/human/Process_Spaceslipping(var/prob_slip = 5) + //If knocked out we might just hit it and stop. This makes it possible to get dead bodies and such. + + if(species.flags & NO_SLIP) + return FALSE + + if(species.can_space_freemove || species.can_zero_g_move) + return FALSE + + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + if(thrust?.can_thrust(0.01)) + return FALSE + + if(stat) + prob_slip = 0 // Changing this to zero to make it line up with the comment, and also, make more sense. + + //Do we have magboots or such on if so no slip + if(istype(shoes, /obj/item/clothing/shoes/magboots) && (shoes.item_flags & NOSLIP)) + prob_slip = 0 + + //Check hands and mod slip + if(!l_hand) prob_slip -= 2 + else if(l_hand.w_class <= 2) prob_slip -= 1 + if (!r_hand) prob_slip -= 2 + else if(r_hand.w_class <= 2) prob_slip -= 1 + + prob_slip = round(prob_slip) + return(prob_slip) + +// Handle footstep sounds +/mob/living/carbon/human/handle_footstep(var/turf/T) + if(!istype(T)) + return + if(is_incorporeal()) + return + if(!config.footstep_volume || !T.footstep_sounds || !T.footstep_sounds.len) + return + // Future Upgrades - Multi species support + var/list/footstep_sounds = T.footstep_sounds["human"] + if(!footstep_sounds) + return + + var/S = pick(footstep_sounds) + GLOB.step_taken_shift_roundstat++ + if(!S) return + + // Play every 20 steps while walking, for the sneak + if(m_intent == "walk" && step_count++ % 20 != 0) + return + + // Play every other step while running + if(m_intent == "run" && step_count++ % 2 != 0) + return + + var/volume = config.footstep_volume + + // Reduce volume while walking or barefoot + if(!shoes || m_intent == "walk") + volume *= 0.5 + else if(shoes) + var/obj/item/clothing/shoes/feet = shoes + if(istype(feet)) + volume *= feet.step_volume_mod + + if(!has_organ(BP_L_FOOT) && !has_organ(BP_R_FOOT)) + return // no feet = no footsteps + + if(buckled || lying || throwing) + return // people flying, lying down or sitting do not step + + if(!has_gravity(src) && prob(75)) + return // Far less likely to make noise in no gravity + + playsound(T, S, volume, FALSE) + return + +/mob/living/carbon/human/set_dir(var/new_dir) + . = ..() + if(. && (species.tail || tail_style)) update_tail_showing() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index ce3fb9d186c..6e5f9eae6c2 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -78,7 +78,7 @@ for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) - O.show_message("[src] [failed ? "tried to tackle" : "has tackled"] down [T]!", 1) + O.show_message("[span_red("[src] [failed ? "tried to tackle" : "has tackled"] down [T]!")]", 1) /mob/living/carbon/human/proc/commune() set category = "Abilities" @@ -108,12 +108,12 @@ log_say("(COMMUNE to [key_name(M)]) [text]",src) - to_chat(M, "Like lead slabs crashing into the ocean, alien thoughts drop into your mind: [text]") + to_chat(M, "[span_blue("Like lead slabs crashing into the ocean, alien thoughts drop into your mind: [text]")]") if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M if(H.species.name == src.species.name) return - to_chat(H, "Your nose begins to bleed...") + to_chat(H, "[span_red("Your nose begins to bleed...")]") H.drip(1) /mob/living/carbon/human/proc/regurgitate() @@ -126,7 +126,7 @@ if(M in stomach_contents) stomach_contents.Remove(M) M.loc = loc - src.visible_message("[src] hurls out the contents of their stomach!") + src.visible_message("[span_red("[src] hurls out the contents of their stomach!")]") return /mob/living/carbon/human/proc/psychic_whisper(mob/M as mob in oview()) @@ -137,8 +137,8 @@ var/msg = sanitize(tgui_input_text(usr, "Message:", "Psychic Whisper")) if(msg) log_say("(PWHISPER to [key_name(M)]) [msg]", src) - to_chat(M, "You hear a strange, alien voice in your head... [msg]") - to_chat(src, "You said: \"[msg]\" to [M]") + to_chat(M, "[span_green("You hear a strange, alien voice in your head... [msg]")]") + to_chat(src, "[span_green("You said: \"[msg]\" to [M]")]") return /mob/living/carbon/human/proc/diona_split_nymph() diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index ea2d2981d70..1051bc4d540 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -25,6 +25,7 @@ /mob/living/carbon/human/dummy/mannequin/autoequip icon = 'icons/mob/human_races/r_human.dmi' icon_state = "preview" + var/autorotate = TRUE /mob/living/carbon/human/dummy/mannequin/autoequip/Initialize() icon = null @@ -32,7 +33,8 @@ . = ..() dress_up() - turntable() + if(autorotate) + turntable() /mob/living/carbon/human/dummy/mannequin/autoequip/proc/dress_up() set waitfor = FALSE diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index c8aba577353..df9a03294c3 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,444 +1,444 @@ -/* -Add fingerprints to items when we put them in our hands. -This saves us from having to call add_fingerprint() any time something is put in a human's hands programmatically. -*/ - -/mob/living/carbon/human - var/list/worn_clothing = list() //Contains all CLOTHING items worn - -/mob/living/carbon/human/verb/quick_equip() - set name = "quick-equip" - set hidden = 1 - - if(ishuman(src)) - var/mob/living/carbon/human/H = src - var/obj/item/I = H.get_active_hand() - if(!I) - to_chat(H, "You are not holding anything to equip.") - return - - var/moved = FALSE - - // Try an equipment slot - if(H.equip_to_appropriate_slot(I)) - moved = TRUE - - // No? Try a storage item. - else if(H.equip_to_storage(I, TRUE)) - moved = TRUE - - // No?! Well, give up. - if(!moved) - to_chat(H, "You are unable to equip that.") - - // Update hand icons - else - if(hand) - update_inv_l_hand(0) - else - update_inv_r_hand(0) - -/mob/living/carbon/human/equip_to_storage(obj/item/newitem, user_initiated = FALSE) - // Try put it in their belt first - if(istype(src.belt,/obj/item/weapon/storage)) - var/obj/item/weapon/storage/wornbelt = src.belt - if(wornbelt.can_be_inserted(newitem, 1)) - if(user_initiated) - wornbelt.handle_item_insertion(newitem) - else - newitem.forceMove(wornbelt) - return wornbelt - - return ..() - -/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) - for (var/slot in slots) - if (equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) - return slot - if (del_on_fail) - qdel(W) - return null - -/mob/living/carbon/human/proc/has_organ(name) - var/obj/item/organ/external/O = organs_by_name[name] - return (O && !O.is_stump()) - -/mob/living/carbon/human/proc/has_organ_for_slot(slot) - switch(slot) - if(slot_back) - return has_organ(BP_TORSO) - if(slot_wear_mask) - return has_organ(BP_HEAD) - if(slot_handcuffed) - return has_organ(BP_L_HAND) && has_organ(BP_R_HAND) - if(slot_legcuffed) - return has_organ(BP_L_FOOT) && has_organ(BP_R_FOOT) - if(slot_l_hand) - return has_organ(BP_L_HAND) - if(slot_r_hand) - return has_organ(BP_R_HAND) - if(slot_belt) - return has_organ(BP_TORSO) - if(slot_wear_id) - // the only relevant check for this is the uniform check - return 1 - if(slot_l_ear) - return has_organ(BP_HEAD) - if(slot_r_ear) - return has_organ(BP_HEAD) - if(slot_glasses) - return has_organ(BP_HEAD) - if(slot_gloves) - return has_organ(BP_L_HAND) || has_organ(BP_R_HAND) - if(slot_head) - return has_organ(BP_HEAD) - if(slot_shoes) - return has_organ(BP_L_FOOT) || has_organ(BP_R_FOOT) - if(slot_wear_suit) - return has_organ(BP_TORSO) - if(slot_w_uniform) - return has_organ(BP_TORSO) - if(slot_l_store) - return has_organ(BP_TORSO) - if(slot_r_store) - return has_organ(BP_TORSO) - if(slot_s_store) - return has_organ(BP_TORSO) - if(slot_in_backpack) - return 1 - if(slot_tie) - return 1 - -/obj/item/var/suitlink = 1 //makes belt items require a jumpsuit- set individual items to suitlink = 0 to allow wearing on belt slot without suit - -/mob/living/carbon/human/u_equip(obj/W as obj) - if(!W) return 0 - - if (W == wear_suit) - if(s_store) - drop_from_inventory(s_store) - worn_clothing -= wear_suit - wear_suit = null - update_inv_wear_suit() - else if (W == w_uniform) - if (r_store) - drop_from_inventory(r_store) - if (l_store) - drop_from_inventory(l_store) - if (wear_id) - drop_from_inventory(wear_id) - if (belt && belt.suitlink == 1) - worn_clothing -= belt - drop_from_inventory(belt) - worn_clothing -= w_uniform - w_uniform = null - update_inv_w_uniform() - else if (W == gloves) - worn_clothing -= gloves - gloves = null - update_inv_gloves() - else if (W == glasses) - worn_clothing -= glasses - glasses = null - update_inv_glasses() - else if (W == head) - worn_clothing -= head - head = null - if(istype(W, /obj/item)) - var/obj/item/I = W - if(I.flags_inv & (HIDEMASK|BLOCKHAIR|BLOCKHEADHAIR)) - update_hair(0) //rebuild hair - update_inv_ears(0) - update_inv_wear_mask(0) - update_inv_head() - else if (W == l_ear) - l_ear = null - update_inv_ears() - else if (W == r_ear) - r_ear = null - update_inv_ears() - else if (W == shoes) - worn_clothing -= shoes - shoes = null - update_inv_shoes() - else if (W == belt) - worn_clothing -= belt - belt = null - update_inv_belt() - else if (W == wear_mask) - worn_clothing -= wear_mask - wear_mask = null - if(istype(W, /obj/item)) - var/obj/item/I = W - if(I.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) - update_hair(0) //rebuild hair - update_inv_ears(0) - // If this is how the internals are connected, disable them - if(internal && !(head?.item_flags & AIRTIGHT)) - if(internals) - internals.icon_state = "internal0" - internal = null - update_inv_wear_mask() - else if (W == wear_id) - wear_id = null - update_inv_wear_id() - BITSET(hud_updateflag, ID_HUD) - BITSET(hud_updateflag, WANTED_HUD) - else if (W == r_store) - r_store = null - //update_inv_pockets() //Doesn't do anything. - else if (W == l_store) - l_store = null - //update_inv_pockets() //Doesn't do anything. - else if (W == s_store) - s_store = null - update_inv_s_store() - else if (W == back) - worn_clothing -= back - back = null - update_inv_back() - else if (W == handcuffed) - handcuffed = null - if(buckled && buckled.buckle_require_restraints) - buckled.unbuckle_mob() - update_handcuffed() - else if (W == legcuffed) - legcuffed = null - update_inv_legcuffed() - else if (W == r_hand) - r_hand = null - if(l_hand) - l_hand.update_twohanding() - l_hand.update_held_icon() - update_inv_l_hand() - update_inv_r_hand() - else if (W == l_hand) - l_hand = null - if(r_hand) - r_hand.update_twohanding() - r_hand.update_held_icon() - update_inv_l_hand() - update_inv_l_hand() - else - return 0 - - update_action_buttons() - return 1 - - - -//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() -/mob/living/carbon/human/equip_to_slot(obj/item/W as obj, slot) - - if(!slot) - return - if(!istype(W)) - return - if(!has_organ_for_slot(slot)) - return - if(!species || !species.hud || !(slot in species.hud.equip_slots)) - return - - W.loc = src - switch(slot) - if(slot_back) - src.back = W - W.equipped(src, slot) - worn_clothing += back - update_inv_back() - if(slot_wear_mask) - src.wear_mask = W - if(wear_mask.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) - update_hair() //rebuild hair - update_inv_ears() - W.equipped(src, slot) - worn_clothing += wear_mask - update_inv_wear_mask() - if(slot_handcuffed) - src.handcuffed = W - update_inv_handcuffed() - if(slot_legcuffed) - src.legcuffed = W - W.equipped(src, slot) - update_inv_legcuffed() - if(slot_l_hand) - src.l_hand = W - W.equipped(src, slot) - update_inv_l_hand() - if(slot_r_hand) - src.r_hand = W - W.equipped(src, slot) - update_inv_r_hand() - if(slot_belt) - src.belt = W - W.equipped(src, slot) - worn_clothing += belt - update_inv_belt() - if(slot_wear_id) - src.wear_id = W - W.equipped(src, slot) - update_inv_wear_id() - BITSET(hud_updateflag, ID_HUD) - BITSET(hud_updateflag, WANTED_HUD) - if(slot_l_ear) - src.l_ear = W - if(l_ear.slot_flags & SLOT_TWOEARS) - var/obj/item/clothing/ears/offear/O = new(W) - O.loc = src - src.r_ear = O - O.hud_layerise() - W.equipped(src, slot) - update_inv_ears() - if(slot_r_ear) - src.r_ear = W - if(r_ear.slot_flags & SLOT_TWOEARS) - var/obj/item/clothing/ears/offear/O = new(W) - O.loc = src - src.l_ear = O - O.hud_layerise() - W.equipped(src, slot) - update_inv_ears() - if(slot_glasses) - src.glasses = W - W.equipped(src, slot) - worn_clothing += glasses - update_inv_glasses() - if(slot_gloves) - src.gloves = W - W.equipped(src, slot) - worn_clothing += gloves - update_inv_gloves() - if(slot_head) - src.head = W - if(head.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR|HIDEMASK)) - update_hair() //rebuild hair - update_inv_ears(0) - update_inv_wear_mask(0) - if(istype(W,/obj/item/clothing/head/kitty)) - W.update_icon(src) - W.equipped(src, slot) - worn_clothing += head - update_inv_head() - if(slot_shoes) - src.shoes = W - W.equipped(src, slot) - worn_clothing += shoes - update_inv_shoes() - if(slot_wear_suit) - src.wear_suit = W - W.equipped(src, slot) - worn_clothing += wear_suit - update_inv_wear_suit() - if(slot_w_uniform) - src.w_uniform = W - W.equipped(src, slot) - worn_clothing += w_uniform - update_inv_w_uniform() - if(slot_l_store) - src.l_store = W - W.equipped(src, slot) - //update_inv_pockets() //Doesn't do anything - if(slot_r_store) - src.r_store = W - W.equipped(src, slot) - //update_inv_pockets() //Doesn't do anything - if(slot_s_store) - src.s_store = W - W.equipped(src, slot) - update_inv_s_store() - if(slot_in_backpack) - if(src.get_active_hand() == W) - src.remove_from_mob(W) - W.loc = src.back - if(slot_tie) - for(var/obj/item/clothing/C in worn_clothing) - if(istype(W, /obj/item/clothing/accessory)) - var/obj/item/clothing/accessory/A = W - if(C.attempt_attach_accessory(A, src)) - return - else - to_chat(src, "You are trying to equip this item to an unsupported inventory slot. How the heck did you manage that? Stop it...") - return - - if((W == src.l_hand) && (slot != slot_l_hand)) - src.l_hand = null - update_inv_l_hand() //So items actually disappear from hands. - else if((W == src.r_hand) && (slot != slot_r_hand)) - src.r_hand = null - update_inv_r_hand() - - W.hud_layerise() - - if(W.action_button_name) - update_action_buttons() - - if(W.zoom) - W.zoom() - - W.in_inactive_hand(src) - - //VOREStation Addition Start - if(istype(W, /obj/item)) - var/obj/item/I = W - I.equip_special() - //VOREStation Addition End - - return 1 - -//Checks if a given slot can be accessed at this time, either to equip or unequip I -/mob/living/carbon/human/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) - var/obj/item/covering = null - var/check_flags = 0 - - switch(slot) - if(slot_wear_mask) - covering = src.head - check_flags = FACE - if(slot_glasses) - covering = src.head - check_flags = EYES - if(slot_gloves, slot_w_uniform) - covering = src.wear_suit - - if(covering && (covering.body_parts_covered & (I.body_parts_covered|check_flags))) - to_chat(user, "\The [covering] is in the way.") - return 0 - return 1 - -/mob/living/carbon/human/get_equipped_item(var/slot) - switch(slot) - if(slot_back) return back - if(slot_legcuffed) return legcuffed - if(slot_handcuffed) return handcuffed - if(slot_l_store) return l_store - if(slot_r_store) return r_store - if(slot_wear_mask) return wear_mask - if(slot_l_hand) return l_hand - if(slot_r_hand) return r_hand - if(slot_wear_id) return wear_id - if(slot_glasses) return glasses - if(slot_gloves) return gloves - if(slot_head) return head - if(slot_shoes) return shoes - if(slot_belt) return belt - if(slot_wear_suit) return wear_suit - if(slot_w_uniform) return w_uniform - if(slot_s_store) return s_store - if(slot_l_ear) return l_ear - if(slot_r_ear) return r_ear - return ..() - - -/mob/living/carbon/human/is_holding_item_of_type(typepath) - for(var/obj/item/I in list(l_hand, r_hand)) - if(istype(I, typepath)) - return I - return FALSE - -// Returns a list of items held in both hands. -/mob/living/carbon/human/get_all_held_items() - . = list() - if(l_hand) - . += l_hand - if(r_hand) - . += r_hand +/* +Add fingerprints to items when we put them in our hands. +This saves us from having to call add_fingerprint() any time something is put in a human's hands programmatically. +*/ + +/mob/living/carbon/human + var/list/worn_clothing = list() //Contains all CLOTHING items worn + +/mob/living/carbon/human/verb/quick_equip() + set name = "quick-equip" + set hidden = 1 + + if(ishuman(src)) + var/mob/living/carbon/human/H = src + var/obj/item/I = H.get_active_hand() + if(!I) + to_chat(H, "You are not holding anything to equip.") + return + + var/moved = FALSE + + // Try an equipment slot + if(H.equip_to_appropriate_slot(I)) + moved = TRUE + + // No? Try a storage item. + else if(H.equip_to_storage(I, TRUE)) + moved = TRUE + + // No?! Well, give up. + if(!moved) + to_chat(H, "You are unable to equip that.") + + // Update hand icons + else + if(hand) + update_inv_l_hand(0) + else + update_inv_r_hand(0) + +/mob/living/carbon/human/equip_to_storage(obj/item/newitem, user_initiated = FALSE) + // Try put it in their belt first + if(istype(src.belt,/obj/item/weapon/storage)) + var/obj/item/weapon/storage/wornbelt = src.belt + if(wornbelt.can_be_inserted(newitem, 1)) + if(user_initiated) + wornbelt.handle_item_insertion(newitem) + else + newitem.forceMove(wornbelt) + return wornbelt + + return ..() + +/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) + for (var/slot in slots) + if (equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) + return slot + if (del_on_fail) + qdel(W) + return null + +/mob/living/carbon/human/proc/has_organ(name) + var/obj/item/organ/external/O = organs_by_name[name] + return (O && !O.is_stump()) + +/mob/living/carbon/human/proc/has_organ_for_slot(slot) + switch(slot) + if(slot_back) + return has_organ(BP_TORSO) + if(slot_wear_mask) + return has_organ(BP_HEAD) + if(slot_handcuffed) + return has_organ(BP_L_HAND) && has_organ(BP_R_HAND) + if(slot_legcuffed) + return has_organ(BP_L_FOOT) && has_organ(BP_R_FOOT) + if(slot_l_hand) + return has_organ(BP_L_HAND) + if(slot_r_hand) + return has_organ(BP_R_HAND) + if(slot_belt) + return has_organ(BP_TORSO) + if(slot_wear_id) + // the only relevant check for this is the uniform check + return 1 + if(slot_l_ear) + return has_organ(BP_HEAD) + if(slot_r_ear) + return has_organ(BP_HEAD) + if(slot_glasses) + return has_organ(BP_HEAD) + if(slot_gloves) + return has_organ(BP_L_HAND) || has_organ(BP_R_HAND) + if(slot_head) + return has_organ(BP_HEAD) + if(slot_shoes) + return has_organ(BP_L_FOOT) || has_organ(BP_R_FOOT) + if(slot_wear_suit) + return has_organ(BP_TORSO) + if(slot_w_uniform) + return has_organ(BP_TORSO) + if(slot_l_store) + return has_organ(BP_TORSO) + if(slot_r_store) + return has_organ(BP_TORSO) + if(slot_s_store) + return has_organ(BP_TORSO) + if(slot_in_backpack) + return 1 + if(slot_tie) + return 1 + +/obj/item/var/suitlink = 1 //makes belt items require a jumpsuit- set individual items to suitlink = 0 to allow wearing on belt slot without suit + +/mob/living/carbon/human/u_equip(obj/W as obj) + if(!W) return 0 + + if (W == wear_suit) + if(s_store) + drop_from_inventory(s_store) + worn_clothing -= wear_suit + wear_suit = null + update_inv_wear_suit() + else if (W == w_uniform) + if (r_store) + drop_from_inventory(r_store) + if (l_store) + drop_from_inventory(l_store) + if (wear_id) + drop_from_inventory(wear_id) + if (belt && belt.suitlink == 1) + worn_clothing -= belt + drop_from_inventory(belt) + worn_clothing -= w_uniform + w_uniform = null + update_inv_w_uniform() + else if (W == gloves) + worn_clothing -= gloves + gloves = null + update_inv_gloves() + else if (W == glasses) + worn_clothing -= glasses + glasses = null + update_inv_glasses() + else if (W == head) + worn_clothing -= head + head = null + if(istype(W, /obj/item)) + var/obj/item/I = W + if(I.flags_inv & (HIDEMASK|BLOCKHAIR|BLOCKHEADHAIR)) + update_hair(0) //rebuild hair + update_inv_ears(0) + update_inv_wear_mask(0) + update_inv_head() + else if (W == l_ear) + l_ear = null + update_inv_ears() + else if (W == r_ear) + r_ear = null + update_inv_ears() + else if (W == shoes) + worn_clothing -= shoes + shoes = null + update_inv_shoes() + else if (W == belt) + worn_clothing -= belt + belt = null + update_inv_belt() + else if (W == wear_mask) + worn_clothing -= wear_mask + wear_mask = null + if(istype(W, /obj/item)) + var/obj/item/I = W + if(I.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) + update_hair(0) //rebuild hair + update_inv_ears(0) + // If this is how the internals are connected, disable them + if(internal && !(head?.item_flags & AIRTIGHT)) + if(internals) + internals.icon_state = "internal0" + internal = null + update_inv_wear_mask() + else if (W == wear_id) + wear_id = null + update_inv_wear_id() + BITSET(hud_updateflag, ID_HUD) + BITSET(hud_updateflag, WANTED_HUD) + else if (W == r_store) + r_store = null + //update_inv_pockets() //Doesn't do anything. + else if (W == l_store) + l_store = null + //update_inv_pockets() //Doesn't do anything. + else if (W == s_store) + s_store = null + update_inv_s_store() + else if (W == back) + worn_clothing -= back + back = null + update_inv_back() + else if (W == handcuffed) + handcuffed = null + if(buckled && buckled.buckle_require_restraints) + buckled.unbuckle_mob() + update_handcuffed() + else if (W == legcuffed) + legcuffed = null + update_inv_legcuffed() + else if (W == r_hand) + r_hand = null + if(l_hand) + l_hand.update_twohanding() + l_hand.update_held_icon() + update_inv_l_hand() + update_inv_r_hand() + else if (W == l_hand) + l_hand = null + if(r_hand) + r_hand.update_twohanding() + r_hand.update_held_icon() + update_inv_l_hand() + update_inv_l_hand() + else + return 0 + + update_action_buttons() + return 1 + + + +//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() +/mob/living/carbon/human/equip_to_slot(obj/item/W as obj, slot) + + if(!slot) + return + if(!istype(W)) + return + if(!has_organ_for_slot(slot)) + return + if(!species || !species.hud || !(slot in species.hud.equip_slots)) + return + + W.loc = src + switch(slot) + if(slot_back) + src.back = W + W.equipped(src, slot) + worn_clothing += back + update_inv_back() + if(slot_wear_mask) + src.wear_mask = W + if(wear_mask.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR)) + update_hair() //rebuild hair + update_inv_ears() + W.equipped(src, slot) + worn_clothing += wear_mask + update_inv_wear_mask() + if(slot_handcuffed) + src.handcuffed = W + update_inv_handcuffed() + if(slot_legcuffed) + src.legcuffed = W + W.equipped(src, slot) + update_inv_legcuffed() + if(slot_l_hand) + src.l_hand = W + W.equipped(src, slot) + update_inv_l_hand() + if(slot_r_hand) + src.r_hand = W + W.equipped(src, slot) + update_inv_r_hand() + if(slot_belt) + src.belt = W + W.equipped(src, slot) + worn_clothing += belt + update_inv_belt() + if(slot_wear_id) + src.wear_id = W + W.equipped(src, slot) + update_inv_wear_id() + BITSET(hud_updateflag, ID_HUD) + BITSET(hud_updateflag, WANTED_HUD) + if(slot_l_ear) + src.l_ear = W + if(l_ear.slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/offear/O = new(W) + O.loc = src + src.r_ear = O + O.hud_layerise() + W.equipped(src, slot) + update_inv_ears() + if(slot_r_ear) + src.r_ear = W + if(r_ear.slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/offear/O = new(W) + O.loc = src + src.l_ear = O + O.hud_layerise() + W.equipped(src, slot) + update_inv_ears() + if(slot_glasses) + src.glasses = W + W.equipped(src, slot) + worn_clothing += glasses + update_inv_glasses() + if(slot_gloves) + src.gloves = W + W.equipped(src, slot) + worn_clothing += gloves + update_inv_gloves() + if(slot_head) + src.head = W + if(head.flags_inv & (BLOCKHAIR|BLOCKHEADHAIR|HIDEMASK)) + update_hair() //rebuild hair + update_inv_ears(0) + update_inv_wear_mask(0) + if(istype(W,/obj/item/clothing/head/kitty)) + W.update_icon(src) + W.equipped(src, slot) + worn_clothing += head + update_inv_head() + if(slot_shoes) + src.shoes = W + W.equipped(src, slot) + worn_clothing += shoes + update_inv_shoes() + if(slot_wear_suit) + src.wear_suit = W + W.equipped(src, slot) + worn_clothing += wear_suit + update_inv_wear_suit() + if(slot_w_uniform) + src.w_uniform = W + W.equipped(src, slot) + worn_clothing += w_uniform + update_inv_w_uniform() + if(slot_l_store) + src.l_store = W + W.equipped(src, slot) + //update_inv_pockets() //Doesn't do anything + if(slot_r_store) + src.r_store = W + W.equipped(src, slot) + //update_inv_pockets() //Doesn't do anything + if(slot_s_store) + src.s_store = W + W.equipped(src, slot) + update_inv_s_store() + if(slot_in_backpack) + if(src.get_active_hand() == W) + src.remove_from_mob(W) + W.loc = src.back + if(slot_tie) + for(var/obj/item/clothing/C in worn_clothing) + if(istype(W, /obj/item/clothing/accessory)) + var/obj/item/clothing/accessory/A = W + if(C.attempt_attach_accessory(A, src)) + return + else + to_chat(src, span_red("You are trying to equip this item to an unsupported inventory slot. How the heck did you manage that? Stop it...")) + return + + if((W == src.l_hand) && (slot != slot_l_hand)) + src.l_hand = null + update_inv_l_hand() //So items actually disappear from hands. + else if((W == src.r_hand) && (slot != slot_r_hand)) + src.r_hand = null + update_inv_r_hand() + + W.hud_layerise() + + if(W.action_button_name) + update_action_buttons() + + if(W.zoom) + W.zoom() + + W.in_inactive_hand(src) + + //VOREStation Addition Start + if(istype(W, /obj/item)) + var/obj/item/I = W + I.equip_special() + //VOREStation Addition End + + return 1 + +//Checks if a given slot can be accessed at this time, either to equip or unequip I +/mob/living/carbon/human/slot_is_accessible(var/slot, var/obj/item/I, mob/user=null) + var/obj/item/covering = null + var/check_flags = 0 + + switch(slot) + if(slot_wear_mask) + covering = src.head + check_flags = FACE + if(slot_glasses) + covering = src.head + check_flags = EYES + if(slot_gloves, slot_w_uniform) + covering = src.wear_suit + + if(covering && (covering.body_parts_covered & (I.body_parts_covered|check_flags))) + to_chat(user, "\The [covering] is in the way.") + return 0 + return 1 + +/mob/living/carbon/human/get_equipped_item(var/slot) + switch(slot) + if(slot_back) return back + if(slot_legcuffed) return legcuffed + if(slot_handcuffed) return handcuffed + if(slot_l_store) return l_store + if(slot_r_store) return r_store + if(slot_wear_mask) return wear_mask + if(slot_l_hand) return l_hand + if(slot_r_hand) return r_hand + if(slot_wear_id) return wear_id + if(slot_glasses) return glasses + if(slot_gloves) return gloves + if(slot_head) return head + if(slot_shoes) return shoes + if(slot_belt) return belt + if(slot_wear_suit) return wear_suit + if(slot_w_uniform) return w_uniform + if(slot_s_store) return s_store + if(slot_l_ear) return l_ear + if(slot_r_ear) return r_ear + return ..() + + +/mob/living/carbon/human/is_holding_item_of_type(typepath) + for(var/obj/item/I in list(l_hand, r_hand)) + if(istype(I, typepath)) + return I + return FALSE + +// Returns a list of items held in both hands. +/mob/living/carbon/human/get_all_held_items() + . = list() + if(l_hand) + . += l_hand + if(r_hand) + . += r_hand diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 4afe699cfa7..a5c94480325 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1,2130 +1,2130 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -//NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick! -#define HUMAN_MAX_OXYLOSS 1 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. -#define HUMAN_CRIT_MAX_OXYLOSS ( 2.0 / 6) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 50HP to get through, so (1/6)*last_tick_duration per second. Breaths however only happen every 4 ticks. last_tick_duration = ~2.0 on average - -#define HEAT_DAMAGE_LEVEL_1 2 //VOREStation Edit //Amount of damage applied when your body temperature just passes the 360.15k safety point -#define HEAT_DAMAGE_LEVEL_2 4 //VOREStation Edit //Amount of damage applied when your body temperature passes the 400K point -#define HEAT_DAMAGE_LEVEL_3 8 //VOREStation Edit //Amount of damage applied when your body temperature passes the 1000K point - -#define COLD_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when your body temperature just passes the 260.15k safety point -#define COLD_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when your body temperature passes the 200K point -#define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point - -//Note that gas heat damage is only applied once every FOUR ticks. -#define HEAT_GAS_DAMAGE_LEVEL_1 2 //Amount of damage applied when the current breath's temperature just passes the 360.15k safety point -#define HEAT_GAS_DAMAGE_LEVEL_2 4 //Amount of damage applied when the current breath's temperature passes the 400K point -#define HEAT_GAS_DAMAGE_LEVEL_3 8 //Amount of damage applied when the current breath's temperature passes the 1000K point - -#define COLD_GAS_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when the current breath's temperature just passes the 260.15k safety point -#define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point -#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point - -#define COLD_ALERT_SEVERITY_LOW 1 // Constants passed to the cold and heat alerts. -#define COLD_ALERT_SEVERITY_MODERATE 2 -#define COLD_ALERT_SEVERITY_MAX 3 -#define ENVIRONMENT_COMFORT_MARKER_COLD 1 - -#define HOT_ALERT_SEVERITY_LOW 1 -#define HOT_ALERT_SEVERITY_MODERATE 2 -#define HOT_ALERT_SEVERITY_MAX 3 -#define ENVIRONMENT_COMFORT_MARKER_HOT 2 - -#define RADIATION_SPEED_COEFFICIENT 0.1 -#define HUMAN_COMBUSTION_TEMP 524 //524k is the sustained combustion temperature of human fat - -/mob/living/carbon/human - var/in_stasis = 0 - var/heartbeat = 0 - -/mob/living/carbon/human/Life() - set invisibility = 0 - set background = BACKGROUND_ENABLED - - if (transforming) - return - - //Apparently, the person who wrote this code designed it so that - //blinded get reset each cycle and then get activated later in the - //code. Very ugly. I dont care. Moving this stuff here so its easy - //to find it. - blinded = 0 - - //TODO: seperate this out - // update the current life tick, can be used to e.g. only do something every 4 ticks - life_tick++ - - // This is not an ideal place for this but it will do for now. - if(wearing_rig && wearing_rig.offline) - wearing_rig = null - - ..() - - if(life_tick % 30) - hud_updateflag = (1 << TOTAL_HUDS) - 1 - - voice = GetVoice() - - var/stasis = inStasisNow() - if(getStasis() > 2) - Sleeping(20) - - //No need to update all of these procs if the guy is dead. - fall() //VORESTATION EDIT. Prevents people from floating - if(stat != DEAD && !stasis) - //Updates the number of stored chemicals for powers - handle_changeling() - - //Organs and blood - handle_organs() - stabilize_body_temperature() //Body temperature adjusts itself (self-regulation) - weightgain() //VOREStation Addition - process_weaver_silk() //VOREStation Addition - handle_shock() - - handle_pain() - - handle_allergens() - - handle_medical_side_effects() - - handle_heartbeat() - handle_nif() //VOREStation Addition - if(!client) - species.handle_npc(src) - - else if(stat == DEAD && !stasis) - handle_defib_timer() - - if(skip_some_updates()) - return //We go ahead and process them 5 times for HUD images and other stuff though. - - //Update our name based on whether our face is obscured/disfigured - name = get_visible_name() - - pulse = handle_pulse() - -/mob/living/carbon/human/proc/skip_some_updates() - if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle - return 1 - return 0 - -/mob/living/carbon/human/breathe() - if(!inStasisNow()) - ..() - -// Calculate how vulnerable the human is to the current pressure. -// Returns 0 (equals 0 %) if sealed in an undamaged suit that's rated for the pressure, 1 if unprotected (equals 100%). -// Suitdamage can modifiy this in 10% steps. -// Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary -/mob/living/carbon/human/proc/get_pressure_weakness(pressure) - if(pressure == null) - return 1 // No protection if someone forgot to give a pressure - - var/pressure_adjustment_coefficient = 1 // Assume no protection at first. - - // Check suit - if(wear_suit && wear_suit.max_pressure_protection != null && wear_suit.min_pressure_protection != null) - pressure_adjustment_coefficient = 0 - // Pressure is too high - if(wear_suit.max_pressure_protection < pressure) - // Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary - pressure_adjustment_coefficient += round((pressure - wear_suit.max_pressure_protection) / (wear_suit.max_pressure_protection/10)) - - // Pressure is too low - if(wear_suit.min_pressure_protection > pressure) - pressure_adjustment_coefficient += round((wear_suit.min_pressure_protection - pressure) / (wear_suit.min_pressure_protection/10)) - - // Handles breaches in your space suit. 10 suit damage equals a 100% loss of pressure protection. - if(istype(wear_suit,/obj/item/clothing/suit/space)) - var/obj/item/clothing/suit/space/S = wear_suit - if(S.can_breach && S.damage) - pressure_adjustment_coefficient += S.damage * 0.1 - - else - // Missing key protection - pressure_adjustment_coefficient = 1 - - // Check hat - if(head && head.max_pressure_protection != null && head.min_pressure_protection != null) - // Pressure is too high - if(head.max_pressure_protection < pressure) - // Protection scales down from 100% at the boundary to 0% at 20% in excess of the boundary - pressure_adjustment_coefficient += round((pressure - head.max_pressure_protection) / (head.max_pressure_protection/20)) - - // Pressure is too low - if(head.min_pressure_protection > pressure) - pressure_adjustment_coefficient += round((head.min_pressure_protection - pressure) / (head.min_pressure_protection/20)) - - else - // Missing key protection - pressure_adjustment_coefficient = 1 - - pressure_adjustment_coefficient = min(pressure_adjustment_coefficient, 1) - return pressure_adjustment_coefficient - -// Calculate how much of the enviroment pressure-difference affects the human. -/mob/living/carbon/human/calculate_affecting_pressure(var/pressure) - var/pressure_difference - - // First get the absolute pressure difference. - if(pressure < species.safe_pressure) // We are in an underpressure. - pressure_difference = species.safe_pressure - pressure - - else //We are in an overpressure or standard atmosphere. - pressure_difference = pressure - species.safe_pressure - - if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. - pressure_difference = 0 - - else - // Otherwise calculate how much of that absolute pressure difference affects us, can be 0 to 1 (equals 0% to 100%). - // This is our relative difference. - pressure_difference *= get_pressure_weakness(pressure) - - // The difference is always positive to avoid extra calculations. - // Apply the relative difference on a standard atmosphere to get the final result. - // The return value will be the adjusted_pressure of the human that is the basis of pressure warnings and damage. - if(pressure < species.safe_pressure) - return species.safe_pressure - pressure_difference - else - return species.safe_pressure + pressure_difference - -/mob/living/carbon/human/handle_disabilities() - ..() - - if(stat != CONSCIOUS) //Let's not worry about tourettes if you're not conscious. - return - - if (disabilities & EPILEPSY) - if ((prob(1) && paralysis < 1)) - to_chat(src, "You have a seizure!") - for(var/mob/O in viewers(src, null)) - if(O == src) - continue - O.show_message(text("[src] starts having a seizure!"), 1) - Paralyse(10) - make_jittery(1000) - if (disabilities & COUGHING) - if ((prob(5) && paralysis <= 1)) - drop_item() - spawn( 0 ) - emote("cough") - return - if (disabilities & TOURETTES) - if ((prob(10) && paralysis <= 1)) - Stun(10) - spawn( 0 ) - switch(rand(1, 3)) - if(1) - emote("twitch") - if(2 to 3) - say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") - make_jittery(100) - return - if (disabilities & NERVOUS) - if (prob(10)) - stuttering = max(10, stuttering) - - var/rn = rand(0, 200) - if(getBrainLoss() >= 5) - if(0 <= rn && rn <= 3) - custom_pain("Your head feels numb and painful.", 10) - if(getBrainLoss() >= 15) - if(4 <= rn && rn <= 6) if(eye_blurry <= 0) - to_chat(src, "It becomes hard to see for some reason.") - eye_blurry = 10 - if(getBrainLoss() >= 35) - if(7 <= rn && rn <= 9) if(get_active_hand()) - to_chat(src, "Your hand won't respond properly, you drop what you're holding!") - drop_item() - if(getBrainLoss() >= 45) - if(10 <= rn && rn <= 12) - if(prob(50)) - to_chat(src, "You suddenly black out!") - Paralyse(10) - else if(!lying) - to_chat(src, "Your legs won't respond properly, you fall down!") - Weaken(10) - -// RADIATION! Everyone's favorite thing in the world! So let's get some numbers down off the bat. -// 50 rads = 1Bq. This means 1 rad = 0.02Bq. -// However, unless I am a smoothbrained dumbo, absorbed rads are in Gy. Not Bq. -// So let's just assume that 50 rads = 1Gy. Make life easier! - -// ACUTE RADIATION (The stuff that the 'radiation' variable takes care of. Remember, 50radiation=1Gy.): -// Without care: 1-2Gy has a (0-5%) mortality chance. 2-6 (5-95%) 6-8 (95-100)% 8-30 (100%) >30 (100%) -// With care: 1-2Gy (0-5%), 2-6 (5-50%), 6-8 (50-100%), 8-30 (99-100%) >30 (100%) -// So let's make our thresholds based on this! 50-100, 100-300, 300-400, 400-1500, and anything above 1500! -// In reality, however, nobody should ever go above 300 radiation, which is why the cutoff before the really bad effects start to happen being -// 300 radiation is good. For reference: Breaking an artifact deals ~300 rads with no resistance. Getting shot with a lvl 3 PA deals 300 rads with no resistance. -// Nobody outside of engineering should ever have to worry about being irradiated over 300 and start getting organ damage.. - - -// CHRONIC RADIATION (The stuff that 'accumulated_rads' takes care of): -// This is more or less for if someone was exposed for a long time to radiation or just finished being treated for extreme ARS. -// These are meant to be annoying effects to nudge someone towards medical, but not lethal or deadly. -// Things such as loss of taste, eye damage, dropping items in your hand, being temporaily weakened, etc. Stuff to annoy them and get them to fix their rads. - -// Additionally, RADIATION_SPEED_COEFFICIENT = 0.1 - -/mob/living/carbon/human/handle_mutations_and_radiation() //Radiation rework! Now with 'accumulated_rads' - if(inStasisNow()) - return - - if(getFireLoss()) - if((COLD_RESISTANCE in mutations) || (prob(1))) - heal_organ_damage(0,1) - - // DNA2 - Gene processing. - // The HULK stuff that was here is now in the hulk gene. - if(!isSynthetic()) - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - gene.OnMobLife(src) - - radiation = CLAMP(radiation,0,2500) //Max of 50Gy. If you reach that...You're going to wish you were dead. You probably will be dead. - accumulated_rads = CLAMP(accumulated_rads,0,2500) //Max of 50Gy as well. You should never get higher than this. You will be dead before you can reach this. - var/obj/item/organ/internal/I = null //Used for further down below when an organ is picked. - if(!radiation) - if(species.appearance_flags & RADIATION_GLOWS) - glow_override = FALSE - set_light(0) - if(accumulated_rads) - accumulated_rads -= RADIATION_SPEED_COEFFICIENT //Accumulated rads slowly dissipate very slowly. Get to medical to get it treated! - else if(((life_tick % 5 == 0) && radiation) || (radiation > 600)) //Radiation is a slow, insidious killer. Unless you get a massive dose, then the onset is sudden! - if(species.appearance_flags & RADIATION_GLOWS) - glow_override = TRUE - set_light(max(1,min(5,radiation/15)), max(1,min(10,radiation/25)), species.get_flesh_colour(src)) - // END DOGSHIT SNOWFLAKE - - var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs - if(rad_organ && !rad_organ.is_broken()) - var/rads = radiation/25 - radiation -= rads - adjust_nutrition(rads) - adjustBruteLoss(-(rads)) - adjustFireLoss(-(rads)) - adjustOxyLoss(-(rads)) - adjustToxLoss(-(rads)) - updatehealth() - return - - var/obj/item/organ/internal/brain/slime/core = locate() in internal_organs - if(core) - return - - //VOREStation Addition start: shadekin - var/obj/item/organ/internal/brain/shadekin/s_brain = locate() in internal_organs - if(s_brain) - return - //VOREStation Addition end: shadekin - - if(reagents.has_reagent("prussian_blue")) //Prussian Blue temporarily stops radiation effects. - return - - var/damage = 0 - - - if (radiation < 50) //Less than 1.0 Gy. No side effects. - radiation -= 10 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT //No escape from accumulated rads. - - else if (radiation >= 50 && radiation < 100) //Equivalent of 1.0-2.0 Gy. Minimum stage you start seeing effects. - damage = 1 - radiation -= 10 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && !weakened) - to_chat(src, "You feel exhausted.") - AdjustWeakened(3) - if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.get_bodytype() == SPECIES_HUMAN) //apes go bald - if((h_style != "Bald" || f_style != "Shaved" )) - to_chat(src, "Your hair falls out.") - h_style = "Bald" - f_style = "Shaved" - update_hair() - if(prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //Rare chance of vomiting. - spawn vomit() - - else if (radiation >= 100 && radiation < 300) //Equivalent of 2.0 to 6.0 Gy. Nobody should ever be above this without extreme negligence. - damage = 3 - radiation -= 30 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 30 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(5)) - take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") - if(prob(1)) - adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) - emote("gasp") - if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(prob(10) && !weakened) - to_chat(src, "You feel sick.") - AdjustWeakened(3) - - else if (radiation >= 300 && radiation < 400) //Equivalent of 6.0 to 8.0 Gy. - damage = 5 - radiation -= 50 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 50 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(15)) - take_overall_damage(0, 10 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") - if(prob(2)) - adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) - emote("gasp") - if(prob(10) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(prob(15) && !weakened) - to_chat(src, "You feel horribly ill.") - AdjustWeakened(3) - if(prob(5) && internal_organs.len) - I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. - if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - - - else if (radiation >= 400 && radiation < 1500) //Equivalent of 8.0 to 30 Gy. - damage = 10 - radiation -= 100 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 100 * RADIATION_SPEED_COEFFICIENT - if(!isSynthetic()) - if(prob(25)) - take_overall_damage(0, 15 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") - if(prob(5)) - I = internal_organs_by_name[O_EYES] - if(I) - if(istype(I)) I.add_autopsy_data("Radiation Burns", damage) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - to_chat(src, "Your eyes burn!") - eye_blurry += 10 - if(prob(4)) - adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) - emote("gasp") - if(prob(25) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(prob(20) && !weakened) - to_chat(src, "You feel like your insides are burning!") - AdjustWeakened(5) - if(prob(5)) - to_chat(src, "Your entire body feels like it's on fire!") - adjustHalLoss(5) - if(prob(10) && internal_organs.len) - I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. - if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - - else if (radiation >= 1500) //Above 30Gy. You had to get absolutely blasted with rads for this. - damage = 30 - radiation -= 300 * RADIATION_SPEED_COEFFICIENT - accumulated_rads += 300 * RADIATION_SPEED_COEFFICIENT - - if(!isSynthetic()) - take_overall_damage(0, damage * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") //3 burn damage a tick as your body melts. - adjustCloneLoss(15 * RADIATION_SPEED_COEFFICIENT) //1.5 cloneloss a tick as your cells mutate and break down. - - I = internal_organs_by_name[O_EYES] - if(I) - I.add_autopsy_data("Radiation Burns", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //3 eye damage a tick as your eyes melt down. - eye_blurry += 10 - - if(prob(50) && prob(100 * RADIATION_SPEED_COEFFICIENT)) - spawn vomit() - if(!paralysis && prob(30) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. - to_chat(src, "You have a seizure!") - Paralyse(10) - make_jittery(1000) - if(!lying) - emote("collapse") - if(get_active_hand() && prob(15)) //CNS is shutting down. - to_chat(src, "Your hand won't respond properly, you drop what you're holding!") - drop_item() - if(internal_organs.len) - I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. - if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - -/* //Not-so-sparkledog code. TODO: Make a pref for 'special game interactions' that allows interactions that align with prefs to occur. - if(radiation >= 250) //Special effect stuff that occurs at certain rad levels. - if(prob(1) && prob(radiation/2 * RADIATION_SPEED_COEFFICIENT) && allow_spontaneous_tf) //If you've got spontaneous TF...well... - scramble(1, src, 3) //I tried to base this on how many rads you took and it was...Hilarious. Sparkledogs everywhere. - //For the most part, 3 strength will simply change colors. If you get really unlucky, it can do more TF's. - //Math: 250 rads = 1/800 chance - //500 rads = 1/400 chance chance. Etc. -*/ - - if(damage) - damage *= species.radiation_mod - adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT) - updatehealth() - if(!isSynthetic() && organs.len) - var/obj/item/organ/external/O = pick(organs) - if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage) - - // Begin long-term radiation effects - // Loss of taste occurs at 100 (2Gy) and is handled in taste.dm - // These are all done one after another, so duplication is not required. Someone at 400rads will have the 100&400 effects. - if(!radiation && accumulated_rads >= 100 && !reagents.has_reagent("prussian_blue")) //Let's not hit them with long term effects when they're actively being hit with rads. - if(!isSynthetic()) - I = internal_organs_by_name[O_EYES] - if(I) //Eye stuff - if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your eyes water.") - eye_blurry += 5 - if(accumulated_rads > 300) // (6Gy) - if(prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your eyes burn.") - I.add_autopsy_data("Radiation Burns", 1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - I.take_damage(1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //0.1 damage. Not a lot, but enough to tell you to get to medical. - eye_blurry += 10 - - if(accumulated_rads > 200) // (4Gy) - if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your feel nauseated.") - spawn vomit() - if(!weakened && prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) - to_chat(src, "Your feel exhausted.") - AdjustWeakened(3) - if(accumulated_rads > 300) // (6Gy) - if(get_active_hand() && prob(15) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. - to_chat(src, "Your hand won't respond properly, you drop what you're holding!") - drop_item() - if(accumulated_rads > 700) // (12Gy) - if(!paralysis && prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //1 in 1000 chance per tick. - to_chat(src, "You have a seizure!") - Paralyse(10) - make_jittery(1000) - if(!lying) - emote("collapse") - - else //The synthetic effects! - return //Nothing for now. - - - - /** breathing **/ - -/mob/living/carbon/human/handle_chemical_smoke(var/datum/gas_mixture/environment) - if(wear_mask && (wear_mask.item_flags & BLOCK_GAS_SMOKE_EFFECT)) - return - if(glasses && (glasses.item_flags & BLOCK_GAS_SMOKE_EFFECT)) - return - if(head && (head.item_flags & BLOCK_GAS_SMOKE_EFFECT)) - return - ..() - -/mob/living/carbon/human/handle_post_breath(datum/gas_mixture/breath) - ..() - //spread some viruses while we are at it - if(breath && virus2.len > 0 && prob(10)) - for(var/mob/living/carbon/M in view(1,src)) - src.spread_disease_to(M) - - -/mob/living/carbon/human/get_breath_from_internal(volume_needed=BREATH_VOLUME) - if(internal) - //Because rigs store their tanks out of reach of contents.Find(), a check has to be made to make - //sure the rig is still worn, still online, and that its air supply still exists. - var/obj/item/weapon/tank/rig_supply - var/obj/item/weapon/rig/Rig = get_rig() - - if(Rig) - rig_supply = Rig.air_supply - - if ((!rig_supply && !contents.Find(internal)) || !((wear_mask && (wear_mask.item_flags & AIRTIGHT)) || (head && (head.item_flags & AIRTIGHT)))) - internal = null - - if(internal) - return internal.remove_air_volume(volume_needed) - else if(internals) - internals.icon_state = "internal0" - return null - - -/mob/living/carbon/human/handle_breath(datum/gas_mixture/breath) - if(status_flags & GODMODE) - return - - if(suiciding) - failed_last_breath = 1 - adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster - suiciding-- - return 0 - - if(does_not_breathe) - failed_last_breath = 0 - adjustOxyLoss(-5) - return - - if(!breath || (breath.total_moles == 0)) - failed_last_breath = 1 - if(health > config.health_threshold_crit) - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - else - adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) - - if(breath && should_have_organ(O_LUNGS)) - var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] - if(!L.is_bruised() && prob(8)) - rupture_lung() - - throw_alert("pressure", /obj/screen/alert/lowpressure) - return 0 - else - clear_alert("pressure") - - var/safe_pressure_min = species.minimum_breath_pressure // Minimum safe partial pressure of breathable gas in kPa - - - // Lung damage increases the minimum safe pressure. - if(should_have_organ(O_LUNGS)) - var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] - if(isnull(L)) - safe_pressure_min = INFINITY //No lungs, how are you breathing? - else if(L.is_broken()) - safe_pressure_min *= 1.5 - else if(L.is_bruised()) - safe_pressure_min *= 1.25 - else if(breath) - if(breath.total_moles < BREATH_MOLES / 10 || breath.total_moles > BREATH_MOLES * 5) - if(is_below_sound_pressure(get_turf(src))) //No more popped lungs from choking/drowning - if (prob(8)) - rupture_lung() - - var/safe_exhaled_max = 10 - var/safe_toxins_max = 0.2 - var/SA_para_min = 1 - var/SA_sleep_min = 5 - var/inhaled_gas_used = 0 - - var/breath_pressure = (breath.total_moles*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME - - var/inhaling - var/poison - var/exhaling - - var/breath_type - var/poison_type - var/exhale_type - - var/failed_inhale = 0 - var/failed_exhale = 0 - - if(species.breath_type) - breath_type = species.breath_type - else - breath_type = "oxygen" - inhaling = breath.gas[breath_type] - - if(species.poison_type) - poison_type = species.poison_type - else - poison_type = "phoron" - poison = breath.gas[poison_type] - - if(species.exhale_type) - exhale_type = species.exhale_type - exhaling = breath.gas[exhale_type] - else - exhaling = 0 - - var/inhale_pp = (inhaling/breath.total_moles)*breath_pressure - var/toxins_pp = (poison/breath.total_moles)*breath_pressure - // To be clear, this isn't how much they're exhaling -- it's the amount of the species exhale_gas that they just - var/exhaled_pp = (exhaling/breath.total_moles)*breath_pressure - - // Not enough to breathe - if(inhale_pp < safe_pressure_min) - if(prob(20)) - spawn(0) emote("gasp") - - var/ratio = inhale_pp/safe_pressure_min - // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!) - adjustOxyLoss(max(HUMAN_MAX_OXYLOSS*(1-ratio), 0)) - failed_inhale = 1 - - switch(breath_type) - if("oxygen") - throw_alert("oxy", /obj/screen/alert/not_enough_oxy) - if("phoron") - throw_alert("oxy", /obj/screen/alert/not_enough_tox) - if("nitrogen") - throw_alert("oxy", /obj/screen/alert/not_enough_nitro) - if("carbon_dioxide") - throw_alert("oxy", /obj/screen/alert/not_enough_co2) - if("volatile_fuel") - throw_alert("oxy", /obj/screen/alert/not_enough_fuel) - if("nitrous_oxide") - throw_alert("oxy", /obj/screen/alert/not_enough_n2o) - - else - // We're in safe limits - clear_alert("oxy") - - inhaled_gas_used = inhaling/6 - - breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards - - if(exhale_type) - breath.adjust_gas_temp(exhale_type, inhaled_gas_used, bodytemperature, update = 0) //update afterwards - - // Too much exhaled gas in the air - if(exhaled_pp > safe_exhaled_max) - if (prob(15)) - var/word = pick("extremely dizzy","short of breath","faint","confused") - to_chat(src, "You feel [word].") - - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_exhale = 1 - - else if(exhaled_pp > safe_exhaled_max * 0.7) - if (!prob(1)) - var/word = pick("dizzy","short of breath","faint","momentarily confused") - to_chat(src, "You feel [word].") - - //scale linearly from 0 to 1 between safe_exhaled_max and safe_exhaled_max*0.7 - var/ratio = 1.0 - (safe_exhaled_max - exhaled_pp)/(safe_exhaled_max*0.3) - - //give them some oxyloss, up to the limit - we don't want people falling unconcious due to CO2 alone until they're pretty close to safe_exhaled_max. - if (getOxyLoss() < 50*ratio) - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_exhale = 1 - - else if(exhaled_pp > safe_exhaled_max * 0.6) - if(prob(0.3)) - var/word = pick("a little dizzy","short of breath") - to_chat(src, "You feel [word].") - - // Too much poison in the air. - if(toxins_pp > safe_toxins_max) - var/ratio = (poison/safe_toxins_max) * 10 - if(reagents) - reagents.add_reagent("toxin", CLAMP(ratio, MIN_TOXIN_DAMAGE, MAX_TOXIN_DAMAGE)) - breath.adjust_gas(poison_type, -poison/6, update = 0) //update after - throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) - else - clear_alert("tox_in_air") - - // If there's some other shit in the air lets deal with it here. - if(breath.gas["nitrous_oxide"]) - var/SA_pp = (breath.gas["nitrous_oxide"] / breath.total_moles) * breath_pressure - - // Enough to make us paralysed for a bit - if(SA_pp > SA_para_min) - - // 3 gives them one second to wake up and run away a bit! - Paralyse(3) - - // Enough to make us sleep as well - if(SA_pp > SA_sleep_min) - Sleeping(5) - - // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning - else if(SA_pp > 0.15) - if(prob(20)) - spawn(0) emote(pick("giggle", "laugh")) - breath.adjust_gas("nitrous_oxide", -breath.gas["nitrous_oxide"]/6, update = 0) //update after - - // Were we able to breathe? - if (failed_inhale || failed_exhale) - failed_last_breath = 1 - else - failed_last_breath = 0 - adjustOxyLoss(-5) - - - // Hot air hurts :( - if((breath.temperature <= species.cold_discomfort_level || breath.temperature >= species.heat_discomfort_level) && !(COLD_RESISTANCE in mutations)) - - if(breath.temperature <= species.breath_cold_level_1) - if(prob(20)) - to_chat(src, "You feel your face freezing and icicles forming in your lungs!") - else if(breath.temperature >= species.breath_heat_level_1) - if(prob(20)) - to_chat(src, "You feel your face burning and a searing heat in your lungs!") - - if(breath.temperature >= species.heat_discomfort_level) - - if(breath.temperature >= species.breath_heat_level_3) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Heat") - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) - else if(breath.temperature >= species.breath_heat_level_2) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Heat") - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) - else if(breath.temperature >= species.breath_heat_level_1) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Heat") - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) - else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_HOT)) - throw_alert("temp", /obj/screen/alert/warm, HOT_ALERT_SEVERITY_LOW) - else - clear_alert("temp") - - else if(breath.temperature <= species.cold_discomfort_level) - - if(breath.temperature <= species.breath_cold_level_3) - apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Cold") - throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MAX) - else if(breath.temperature <= species.breath_cold_level_2) - apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Cold") - throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MODERATE) - else if(breath.temperature <= species.breath_cold_level_1) - apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Cold") - throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_LOW) - else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_COLD)) - throw_alert("temp", /obj/screen/alert/chilly, COLD_ALERT_SEVERITY_LOW) - else - clear_alert("temp") - - //breathing in hot/cold air also heats/cools you a bit - var/temp_adj = breath.temperature - bodytemperature - if (temp_adj < 0) - temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed - else - temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed - - var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE) - temp_adj *= relative_density - - if(temp_adj > BODYTEMP_HEATING_MAX) - temp_adj = BODYTEMP_HEATING_MAX - if(temp_adj < BODYTEMP_COOLING_MAX) - temp_adj = BODYTEMP_COOLING_MAX - - bodytemperature += temp_adj - - else - clear_alert("temp") - - breath.update_values() - return 1 - -/mob/living/carbon/human/proc/handle_allergens() - if(chem_effects[CE_ALLERGEN]) - //first, multiply the basic species-level value by our allergen effect rating, so consuming multiple seperate allergen typess simultaneously hurts more - var/damage_severity = species.allergen_damage_severity * chem_effects[CE_ALLERGEN] - var/disable_severity = species.allergen_disable_severity * chem_effects[CE_ALLERGEN] - if(species.allergen_reaction & AG_PHYS_DMG) - adjustBruteLoss(damage_severity) - if(species.allergen_reaction & AG_BURN_DMG) - adjustFireLoss(damage_severity) - if(species.allergen_reaction & AG_TOX_DMG) - adjustToxLoss(damage_severity) - if(species.allergen_reaction & AG_OXY_DMG) - adjustOxyLoss(damage_severity) - if(prob(disable_severity/2)) - emote(pick("cough","gasp","choke")) - if(species.allergen_reaction & AG_EMOTE) - if(prob(disable_severity/2)) - emote(pick("pale","shiver","twitch")) - if(species.allergen_reaction & AG_PAIN) - adjustHalLoss(disable_severity) - if(species.allergen_reaction & AG_WEAKEN) - Weaken(disable_severity) - if(species.allergen_reaction & AG_BLURRY) - eye_blurry = max(eye_blurry, disable_severity) - if(species.allergen_reaction & AG_SLEEPY) - drowsyness = max(drowsyness, disable_severity) - if(species.allergen_reaction & AG_CONFUSE) - Confuse(disable_severity/4) - -/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) - if(!environment) - return - //Stuff like the xenomorph's plasma regen happens here. - species.handle_environment_special(src) - - if(is_incorporeal()) - return - - //Moved pressure calculations here for use in skip-processing check. - var/pressure = environment.return_pressure() - var/adjusted_pressure = calculate_affecting_pressure(pressure) - - //Check for contaminants before anything else because we don't want to skip it. - for(var/g in environment.gas) - if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && environment.gas[g] > gas_data.overlay_limit[g] + 1) - pl_effects() - break - - if(istype(loc, /turf/space)) //VOREStation Edit - No FBPs overheating on space turfs inside mechs or people. - //Don't bother if the temperature drop is less than 0.1 anyways. Hopefully BYOND is smart enough to turn this constant expression into a constant - if(bodytemperature > (0.1 * HUMAN_HEAT_CAPACITY/(HUMAN_EXPOSED_SURFACE_AREA*STEFAN_BOLTZMANN_CONSTANT))**(1/4) + COSMIC_RADIATION_TEMPERATURE) - //Thermal radiation into space - var/heat_loss = HUMAN_EXPOSED_SURFACE_AREA * STEFAN_BOLTZMANN_CONSTANT * ((bodytemperature - COSMIC_RADIATION_TEMPERATURE)**4) - var/temperature_loss = heat_loss/HUMAN_HEAT_CAPACITY - bodytemperature -= temperature_loss - else - var/loc_temp = T0C - if(istype(loc, /obj/mecha)) - var/obj/mecha/M = loc - loc_temp = M.return_temperature() - else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/obj/machinery/atmospherics/unary/cryo_cell/cc = loc - loc_temp = cc.air_contents.temperature - else - loc_temp = environment.temperature - - if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1) - clear_alert("pressure") - return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp - - //Body temperature adjusts depending on surrounding atmosphere based on your thermal protection (convection) - var/temp_adj = 0 - if(loc_temp < bodytemperature) //Place is colder than we are - var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. - temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR) //this will be negative - else if (loc_temp > bodytemperature) //Place is hotter than we are - var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. - temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - - //Use heat transfer as proportional to the gas density. However, we only care about the relative density vs standard 101 kPa/20 C air. Therefore we can use mole ratios - var/relative_density = environment.total_moles / MOLES_CELLSTANDARD - bodytemperature += between(BODYTEMP_COOLING_MAX, temp_adj*relative_density, BODYTEMP_HEATING_MAX) - - // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. - if(bodytemperature >= species.heat_level_1) - //Body temperature is too hot. - if(status_flags & GODMODE) - return 1 //godmode - - var/burn_dam = 0 - - // switch() can't access numbers inside variables, so we need to use some ugly if() spam ladder. - if(bodytemperature >= species.heat_level_1) - if(bodytemperature >= species.heat_level_2) - if(bodytemperature >= species.heat_level_3) - burn_dam = HEAT_DAMAGE_LEVEL_3 - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) - else - burn_dam = HEAT_DAMAGE_LEVEL_2 - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) - else - burn_dam = HEAT_DAMAGE_LEVEL_1 - throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) - - take_overall_damage(burn=burn_dam, used_weapon = "High Body Temperature") - - else if(bodytemperature <= species.cold_level_1) - //Body temperature is too cold. - - if(status_flags & GODMODE) - return 1 //godmode - - - if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/cold_dam = 0 - if(bodytemperature <= species.cold_level_1) - if(bodytemperature <= species.cold_level_2) - if(bodytemperature <= species.cold_level_3) - cold_dam = COLD_DAMAGE_LEVEL_3 - else - cold_dam = COLD_DAMAGE_LEVEL_2 - else - cold_dam = COLD_DAMAGE_LEVEL_1 - - take_overall_damage(burn=cold_dam, used_weapon = "Low Body Temperature") - - else clear_alert("temp") - - // Account for massive pressure differences. Done by Polymorph - // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! - if(status_flags & GODMODE) - return 1 //godmode - - if(adjusted_pressure >= species.hazard_high_pressure) - var/pressure_damage = min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) - if(stat==DEAD) - pressure_damage = pressure_damage/2 - take_overall_damage(brute=pressure_damage, used_weapon = "High Pressure") - throw_alert("pressure", /obj/screen/alert/highpressure, 2) - else if(adjusted_pressure >= species.warning_high_pressure) - throw_alert("pressure", /obj/screen/alert/highpressure, 1) - else if(adjusted_pressure >= species.warning_low_pressure) - clear_alert("pressure") - else if(adjusted_pressure >= species.hazard_low_pressure) - throw_alert("pressure", /obj/screen/alert/lowpressure, 1) - else - if( !(COLD_RESISTANCE in mutations)) - if(!isSynthetic() || !nif || !nif.flag_check(NIF_O_PRESSURESEAL,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF pressure seals - var/pressure_damage = LOW_PRESSURE_DAMAGE - if(stat==DEAD) - pressure_damage = pressure_damage/2 - take_overall_damage(brute=pressure_damage, used_weapon = "Low Pressure") - if(getOxyLoss() < 55) // 12 OxyLoss per 4 ticks when wearing internals; unconsciousness in 16 ticks, roughly half a minute - var/pressure_dam = 3 // 16 OxyLoss per 4 ticks when no internals present; unconsciousness in 13 ticks, roughly twenty seconds - // (Extra 1 oxyloss from failed breath) - // Being in higher pressure decreases the damage taken, down to a minimum of (species.hazard_low_pressure / ONE_ATMOSPHERE) at species.hazard_low_pressure - pressure_dam *= (ONE_ATMOSPHERE - adjusted_pressure) / ONE_ATMOSPHERE - - if(wear_suit && wear_suit.min_pressure_protection && head && head.min_pressure_protection) - var/protection = max(wear_suit.min_pressure_protection, head.min_pressure_protection) // Take the weakest protection - pressure_dam *= (protection) / (ONE_ATMOSPHERE) // Divide by ONE_ATMOSPHERE to get a fractional protection - // Stronger protection (Closer to 0) results in a smaller fraction - // Firesuits (Min protection = 0.2 atmospheres) decrease oxyloss to 1/5 - - adjustOxyLoss(pressure_dam) - throw_alert("pressure", /obj/screen/alert/lowpressure, 2) - else - clear_alert("pressure") - - return - -/* -/mob/living/carbon/human/proc/adjust_body_temperature(current, loc_temp, boost) - var/temperature = current - var/difference = abs(current-loc_temp) //get difference - var/increments// = difference/10 //find how many increments apart they are - if(difference > 50) - increments = difference/5 - else - increments = difference/10 - var/change = increments*boost // Get the amount to change by (x per increment) - var/temp_change - if(current < loc_temp) - temperature = min(loc_temp, temperature+change) - else if(current > loc_temp) - temperature = max(loc_temp, temperature-change) - temp_change = (temperature - current) - return temp_change -*/ - -/mob/living/carbon/human/proc/stabilize_body_temperature() - // We produce heat naturally. - if (species.passive_temp_gain) - bodytemperature += species.passive_temp_gain - if (species.body_temperature == null) - return //this species doesn't have metabolic thermoregulation - - // FBPs will overheat when alive, prosthetic limbs are fine. - if(stat != DEAD && robobody_count) - if(!nif || !nif.flag_check(NIF_O_HEATSINKS,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF heatsinks prevent the base heat increase per tick if installed. - bodytemperature += round(robobody_count*1.15) - var/obj/item/organ/internal/robotic/heatsink/HS = internal_organs_by_name[O_HEATSINK] - if(!HS || HS.is_broken()) // However, NIF Heatsinks will not compensate for a core FBP component (your heatsink) being lost. - bodytemperature += round(robobody_count*0.5) - - var/body_temperature_difference = species.body_temperature - bodytemperature - - if (abs(body_temperature_difference) < 0.5) - return //fuck this precision - - if (on_fire) - return //too busy for pesky metabolic regulation - - if(bodytemperature < species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. - if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. - adjust_nutrition(-2) - var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) - //to_world("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") -// log_debug("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - else if(species.cold_level_1 <= bodytemperature && bodytemperature <= species.heat_level_1) - var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR - //to_world("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") -// log_debug("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - else if(bodytemperature > species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. - //We totally need a sweat system cause it totally makes sense...~ - var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers - //to_world("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") -// log_debug("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") - bodytemperature += recovery_amt - - //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) -/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. - . = 0 - //Handle normal clothing - for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) - if(C) - if(C.handle_high_temperature(temperature)) - . |= C.get_heat_protection_flags() - -//See proc/get_heat_protection_flags(temperature) for the description of this proc. -/mob/living/carbon/human/proc/get_cold_protection_flags(temperature) - . = 0 - //Handle normal clothing - for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) - if(C) - if(C.handle_low_temperature(temperature)) - . |= C.get_cold_protection_flags() - -/mob/living/carbon/human/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. - var/thermal_protection_flags = get_heat_protection_flags(temperature) - - . = get_thermal_protection(thermal_protection_flags) - . = 1 - . // Invert from 1 = immunity to 0 = immunity. - - // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. - for(var/datum/modifier/M as anything in modifiers) - if(!isnull(M.heat_protection)) - . *= 1 - M.heat_protection - - // Code that calls this expects 1 = immunity so we need to invert again. - . = 1 - . - . = min(., 1.0) - -/mob/living/carbon/human/get_cold_protection(temperature) - if(COLD_RESISTANCE in mutations) - return 1 //Fully protected from the cold. - - temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. - var/thermal_protection_flags = get_cold_protection_flags(temperature) - - . = get_thermal_protection(thermal_protection_flags) - . = 1 - . // Invert from 1 = immunity to 0 = immunity. - - // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. - for(var/datum/modifier/M as anything in modifiers) - if(!isnull(M.cold_protection)) - // Invert the modifier values so they align with the current working value. - . *= 1 - M.cold_protection - - // Code that calls this expects 1 = immunity so we need to invert again. - . = 1 - . - . = min(., 1.0) - -/mob/living/carbon/human/proc/get_thermal_protection(var/flags) - .=0 - if(flags) - if(flags & HEAD) - . += THERMAL_PROTECTION_HEAD - if(flags & UPPER_TORSO) - . += THERMAL_PROTECTION_UPPER_TORSO - if(flags & LOWER_TORSO) - . += THERMAL_PROTECTION_LOWER_TORSO - if(flags & LEG_LEFT) - . += THERMAL_PROTECTION_LEG_LEFT - if(flags & LEG_RIGHT) - . += THERMAL_PROTECTION_LEG_RIGHT - if(flags & FOOT_LEFT) - . += THERMAL_PROTECTION_FOOT_LEFT - if(flags & FOOT_RIGHT) - . += THERMAL_PROTECTION_FOOT_RIGHT - if(flags & ARM_LEFT) - . += THERMAL_PROTECTION_ARM_LEFT - if(flags & ARM_RIGHT) - . += THERMAL_PROTECTION_ARM_RIGHT - if(flags & HAND_LEFT) - . += THERMAL_PROTECTION_HAND_LEFT - if(flags & HAND_RIGHT) - . += THERMAL_PROTECTION_HAND_RIGHT - return min(1,.) - -/mob/living/carbon/human/handle_chemicals_in_body() - - if(inStasisNow()) - return - - if(reagents) - chem_effects.Cut() - - if(touching) - touching.metabolize() - if(ingested) - ingested.metabolize() - if(bloodstr) - bloodstr.metabolize() - - if(!isSynthetic()) - - var/total_phoronloss = 0 - for(var/obj/item/I in src) - if(I.contaminated) - if(check_belly(I)) continue //VOREStation Edit - if(src.species && src.species.get_bodytype() != "Vox" && src.species.get_bodytype() != "Shadekin") //VOREStation Edit: shadekin - // This is hacky, I'm so sorry. - if(I != l_hand && I != r_hand) //If the item isn't in your hands, you're probably wearing it. Full damage for you. - total_phoronloss += vsc.plc.CONTAMINATION_LOSS - else if(I == l_hand) //If the item is in your hands, but you're wearing protection, you might be alright. - var/l_hand_blocked = 0 - l_hand_blocked = 1-(100-getarmor(BP_L_HAND, "bio"))/100 //This should get a number between 0 and 1 - total_phoronloss += vsc.plc.CONTAMINATION_LOSS * l_hand_blocked - else if(I == r_hand) //If the item is in your hands, but you're wearing protection, you might be alright. - var/r_hand_blocked = 0 - r_hand_blocked = 1-(100-getarmor(BP_R_HAND, "bio"))/100 //This should get a number between 0 and 1 - total_phoronloss += vsc.plc.CONTAMINATION_LOSS * r_hand_blocked - if(total_phoronloss) - if(!(status_flags & GODMODE)) - adjustToxLoss(total_phoronloss) - - if(status_flags & GODMODE) - return 0 //godmode - - if(species.light_dam) - var/light_amount = 0 - if(isturf(loc)) - var/turf/T = loc - light_amount = T.get_lumcount() * 10 - if(light_amount > species.light_dam) //if there's enough light, start dying - take_overall_damage(1,1) - else //heal in the dark - heal_overall_damage(1,1) - - // nutrition decrease - if (nutrition > 0 && stat != DEAD) - var/nutrition_reduction = species.hunger_factor - - for(var/datum/modifier/mod in modifiers) - if(!isnull(mod.metabolism_percent)) - nutrition_reduction *= mod.metabolism_percent - adjust_nutrition(-nutrition_reduction) - - if(noisy == TRUE && nutrition < 250 && prob(10)) //VOREStation edit for hunger noises. - var/sound/growlsound = sound(get_sfx("hunger_sounds")) - var/growlmultiplier = 100 - (nutrition / 250 * 100) - playsound(src, growlsound, vol = growlmultiplier, vary = 1, falloff = 0.1, ignore_walls = TRUE, preference = /datum/client_preference/digestion_noises) - // VOREStation Edit End - - // TODO: stomach and bloodstream organ. - if(!isSynthetic()) - handle_trace_chems() - - updatehealth() - - return //TODO: DEFERRED - -//DO NOT CALL handle_statuses() from this proc, it's called from living/Life() as long as this returns a true value. -/mob/living/carbon/human/handle_regular_status_updates() - if(skip_some_updates()) - return 0 - - if(status_flags & GODMODE) return 0 - - //SSD check, if a logged player is awake put them back to sleep! - if(species.get_ssd(src) && !client && !teleop) - Sleeping(2) - if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP - blinded = 1 - silent = 0 - else //ALIVE. LIGHTS ARE ON - updatehealth() //TODO - - if(health <= config.health_threshold_dead || (should_have_organ("brain") && !has_brain())) - death() - blinded = 1 - silent = 0 - return 1 - - //UNCONSCIOUS. NO-ONE IS HOME - if((getOxyLoss() > (species.total_health/2)) || (health <= config.health_threshold_crit)) - Paralyse(3) - - if(hallucination) - if(hallucination >= 20 && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) ) - if(prob(3)) - fake_attack(src) - if(!handling_hal) - spawn handle_hallucinations() //The not boring kind! - if(client && prob(5)) - client.dir = pick(2,4,8) - spawn(rand(20,50)) - client.dir = 1 - - hallucination = max(0, hallucination - 2) - else - for(var/atom/a in hallucinations) - qdel(a) - - //Brain damage from Oxyloss - if(should_have_organ("brain")) - var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage - if(CE_STABLE in chem_effects) - brainOxPercent = 0.008 //Halved in effect - if(oxyloss >= (getMaxHealth() * 0.3) && prob(5)) // If oxyloss exceeds 30% of your max health, you can take brain damage. - adjustBrainLoss(brainOxPercent * oxyloss) - - if(halloss >= species.total_health) - to_chat(src, "You're in too much pain to keep going...") - src.visible_message("[src] slumps to the ground, too weak to continue fighting.") - Paralyse(10) - setHalLoss(species.total_health - 1) - - if(paralysis || sleeping) - blinded = 1 - set_stat(UNCONSCIOUS) - animate_tail_reset() - adjustHalLoss(-3) - - if(sleeping) - handle_dreams() - if (mind) - //Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar. - if(client || sleeping > 3) - AdjustSleeping(-1) - throw_alert("asleep", /obj/screen/alert/asleep) - if( prob(2) && health && !hal_crit && client ) - spawn(0) - emote("snore") - //CONSCIOUS - else - set_stat(CONSCIOUS) - clear_alert("asleep") - - //Periodically double-check embedded_flag - if(embedded_flag && !(life_tick % 10)) - var/list/E - E = get_visible_implants(0) - if(!E.len) - embedded_flag = 0 - - //Eyes - //Check rig first because it's two-check and other checks will override it. - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) - if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) - blinded = 1 - - // Check everything else. - - //Periodically double-check embedded_flag - if(embedded_flag && !(life_tick % 10)) - if(!embedded_needs_process()) - embedded_flag = 0 - //Vision - var/obj/item/organ/vision - if(species.vision_organ) - vision = internal_organs_by_name[species.vision_organ] - - if(!species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. - SetBlinded(0) - blinded = 0 - eye_blurry = 0 - clear_alert("blind") - else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. - SetBlinded(1) - blinded = 1 - eye_blurry = 1 - throw_alert("blind", /obj/screen/alert/blind) - else //You have the requisite organs - if(sdisabilities & BLIND) // Disabled-blind, doesn't get better on its own - blinded = 1 - throw_alert("blind", /obj/screen/alert/blind) - else if(eye_blind) // Blindness, heals slowly over time - AdjustBlinded(-1) - blinded = 1 - throw_alert("blind", /obj/screen/alert/blind) - else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster - eye_blurry = max(eye_blurry-3, 0) - blinded = 1 - throw_alert("blind", /obj/screen/alert/blind) - - //blurry sight - if(vision.is_bruised()) // Vision organs impaired? Permablurry. - eye_blurry = 1 - if(eye_blurry) // Blurry eyes heal slowly - eye_blurry = max(eye_blurry-1, 0) - - //Ears - if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own - ear_deaf = max(ear_deaf, 1) - else if(ear_deaf) //deafness, heals slowly over time - ear_deaf = max(ear_deaf-1, 0) - else if(get_ear_protection() >= 2) //resting your ears with earmuffs heals ear damage faster - ear_damage = max(ear_damage-0.15, 0) - ear_deaf = max(ear_deaf, 1) - else if(ear_damage < 25) //ear damage heals slowly under this threshold. otherwise you'll need earmuffs - ear_damage = max(ear_damage-0.05, 0) - - //Resting - if(resting) - dizziness = max(0, dizziness - 15) - jitteriness = max(0, jitteriness - 15) - adjustHalLoss(-3) - else - dizziness = max(0, dizziness - 3) - jitteriness = max(0, jitteriness - 3) - adjustHalLoss(-1) - - if (drowsyness) - drowsyness = max(0, drowsyness - 1) - eye_blurry = max(2, eye_blurry) - if (prob(5)) - Sleeping(1) - Paralyse(5) - - // If you're dirty, your gloves will become dirty, too. - if(gloves && germ_level > gloves.germ_level && prob(10)) - gloves.germ_level += 1 - - return 1 - -/mob/living/carbon/human/set_stat(var/new_stat) - . = ..() - if(. && stat) - update_skin(1) - -/mob/living/carbon/human/handle_regular_hud_updates() - if(hud_updateflag) // update our mob's hud overlays, AKA what others see flaoting above our head - handle_hud_list() - - // now handle what we see on our screen - - if(!client) - return 0 - - ..() - - client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.thermal, global_hud.meson, global_hud.science, global_hud.material, global_hud.whitense) - - if(istype(client.eye,/obj/machinery/camera)) - var/obj/machinery/camera/cam = client.eye - client.screen |= cam.client_huds - - if(stat == DEAD) //Dead - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - if(healths) healths.icon_state = "health7" //DEAD healthmeter - - else if(stat == UNCONSCIOUS && health <= 0) //Crit - //Critical damage passage overlay - var/severity = 0 - switch(health) - if(-20 to -10) severity = 1 - if(-30 to -20) severity = 2 - if(-40 to -30) severity = 3 - if(-50 to -40) severity = 4 - if(-60 to -50) severity = 5 - if(-70 to -60) severity = 6 - if(-80 to -70) severity = 7 - if(-90 to -80) severity = 8 - if(-95 to -90) severity = 9 - if(-INFINITY to -95) severity = 10 - overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity) - else //Alive - clear_fullscreen("crit") - //Oxygen damage overlay - if(oxyloss) - var/severity = 0 - switch(oxyloss) - if(10 to 20) severity = 1 - if(20 to 25) severity = 2 - if(25 to 30) severity = 3 - if(30 to 35) severity = 4 - if(35 to 40) severity = 5 - if(40 to 45) severity = 6 - if(45 to INFINITY) severity = 7 - overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) - else - clear_fullscreen("oxy") - - //Fire and Brute damage overlay (BSSR) - var/hurtdamage = src.getShockBruteLoss() + src.getShockFireLoss() + damageoverlaytemp //Doesn't call the overlay if you can't actually feel it - damageoverlaytemp = 0 // We do this so we can detect if someone hits us or not. - if(hurtdamage) - var/severity = 0 - switch(hurtdamage) - if(10 to 25) severity = 1 - if(25 to 40) severity = 2 - if(40 to 55) severity = 3 - if(55 to 70) severity = 4 - if(70 to 85) severity = 5 - if(85 to INFINITY) severity = 6 - overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) - else - clear_fullscreen("brute") - - if(healths) - if (chem_effects[CE_PAINKILLER] > 100) - healths.icon_state = "health_numb" - else - // Generate a by-limb health display. - var/mutable_appearance/healths_ma = new(healths) - healths_ma.icon_state = "blank" - healths_ma.overlays = null - healths_ma.plane = PLANE_PLAYER_HUD - - var/no_damage = 1 - var/trauma_val = 0 // Used in calculating softcrit/hardcrit indicators. - if(!(species.flags & NO_PAIN)) - trauma_val = max(traumatic_shock,halloss)/species.total_health - var/limb_trauma_val = trauma_val*0.3 - // Collect and apply the images all at once to avoid appearance churn. - var/list/health_images = list() - for(var/obj/item/organ/external/E in organs) - if(no_damage && (E.brute_dam || E.burn_dam)) - no_damage = 0 - health_images += E.get_damage_hud_image(limb_trauma_val) - - // Apply a fire overlay if we're burning. - if(on_fire) - health_images += image('icons/mob/OnFire.dmi',"[get_fire_icon_state()]") - - // Show a general pain/crit indicator if needed. - if(trauma_val) - if(!(species.flags & NO_PAIN)) - if(trauma_val > 0.7) - health_images += image('icons/mob/screen1_health.dmi',"softcrit") - if(trauma_val >= 1) - health_images += image('icons/mob/screen1_health.dmi',"hardcrit") - else if(no_damage) - health_images += image('icons/mob/screen1_health.dmi',"fullhealth") - - healths_ma.add_overlay(health_images) - healths.appearance = healths_ma - - - var/fat_alert = /obj/screen/alert/fat - var/hungry_alert = /obj/screen/alert/hungry - var/starving_alert = /obj/screen/alert/starving - - if(isSynthetic()) - fat_alert = /obj/screen/alert/fat/synth - hungry_alert = /obj/screen/alert/hungry/synth - starving_alert = /obj/screen/alert/starving/synth - //VOREStation Add - Vampire hunger alert - else if(get_species() == SPECIES_CUSTOM) - var/datum/species/custom/C = species - if(/datum/trait/neutral/bloodsucker in C.traits) - fat_alert = /obj/screen/alert/fat/vampire - hungry_alert = /obj/screen/alert/hungry/vampire - starving_alert = /obj/screen/alert/starving/vampire - //VOREStation Add End - - switch(nutrition) - if(450 to INFINITY) - throw_alert("nutrition", fat_alert) - // if(350 to 450) - // if(250 to 350) // Alternative more-detailed tiers, not used. - if(250 to 450) - clear_alert("nutrition") - if(150 to 250) - throw_alert("nutrition", hungry_alert) - else - throw_alert("nutrition", starving_alert) - - if(blinded) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - throw_alert("blind", /obj/screen/alert/blind) - else - clear_fullscreen("blind") - clear_alert("blind") - - var/apply_nearsighted_overlay = FALSE - if(disabilities & NEARSIGHTED) - apply_nearsighted_overlay = TRUE - - if(glasses) - var/obj/item/clothing/glasses/G = glasses - if(G.prescription) - apply_nearsighted_overlay = FALSE - - if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) // VOREStation Edit - NIF - apply_nearsighted_overlay = FALSE - - set_fullscreen(apply_nearsighted_overlay, "nearsighted", /obj/screen/fullscreen/impaired, 1) - - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - if(druggy) - throw_alert("high", /obj/screen/alert/high) - else - clear_alert("high") - - if(!isbelly(loc)) //VOREStation Add - Belly fullscreens safety - clear_fullscreen("belly") - clear_fullscreen("belly2") - clear_fullscreen("belly3") - clear_fullscreen("belly4") - - if(config.welder_vision) - var/found_welder - if(species.short_sighted) - found_welder = 1 - else - if(istype(glasses, /obj/item/clothing/glasses/welding)) - var/obj/item/clothing/glasses/welding/O = glasses - if(!O.up) - found_welder = 1 - if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 //VOREStation Add - NIF - if(istype(glasses, /obj/item/clothing/glasses/sunglasses/thinblindfold)) - found_welder = 1 - if(!found_welder && istype(head, /obj/item/clothing/head/welding)) - var/obj/item/clothing/head/welding/O = head - if(!O.up) - found_welder = 1 - if(!found_welder && istype(back, /obj/item/weapon/rig)) - var/obj/item/weapon/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) - if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) - found_welder = 1 - if(absorbed) found_welder = 1 //VOREStation Code - if(found_welder) - client.screen |= global_hud.darkMask - -/mob/living/carbon/human/reset_view(atom/A) - ..() - if(machine_visual && machine_visual != A) - machine_visual.remove_visual(src) - -/mob/living/carbon/human/handle_vision() - if(stat == DEAD) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF - see_in_dark = 8 - if(client) - if(client.view != world.view) // If mob dies while zoomed in with device, unzoom them. - for(var/obj/item/item in contents) - if(item.zoom) - item.zoom() - break - - else //We aren't dead - sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default - - // Do this early so certain stuff gets turned off before vision is assigned. - var/area/A = get_area(src) - if(A?.no_spoilers) - disable_spoiler_vision() - - if(XRAY in mutations) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - - if(seer==1) - var/obj/effect/rune/R = locate() in loc - if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"]) - see_invisible = SEE_INVISIBLE_CULT - else - see_invisible = see_invisible_default - seer = 0 - - if(!seedarkness) - sight = species.get_vision_flags(src) - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_NOLIGHTING - - else - sight = species.get_vision_flags(src) - see_in_dark = species.darksight - see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default - - var/glasses_processed = 0 - var/obj/item/weapon/rig/rig = get_rig() - if(istype(rig) && rig.visor && !looking_elsewhere) - if(!rig.helmet || (head && rig.helmet == head)) - if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) - glasses_processed = process_glasses(rig.visor.vision.glasses) - - if(glasses && !glasses_processed && !looking_elsewhere) - glasses_processed = process_glasses(glasses) - if(XRAY in mutations) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.vision_flags)) - sight |= M.vision_flags - - if(!glasses_processed && nif) - var/datum/nifsoft/vision_soft - for(var/datum/nifsoft/NS in nif.nifsofts) - if(NS.vision_exclusive && NS.active) - vision_soft = NS - break - if(vision_soft) - glasses_processed = process_nifsoft_vision(vision_soft) //not really glasses but equitable - - if(!glasses_processed && (species.get_vision_flags(src) > 0)) - sight |= species.get_vision_flags(src) - if(!seer && !glasses_processed && seedarkness) - see_invisible = see_invisible_default - - if(machine) - var/viewflags = machine.check_eye(src) - if(viewflags < 0) - reset_view(null, 0) - else if(viewflags && !looking_elsewhere) - sight |= viewflags - else - machine.apply_visual(src) - else if(eyeobj) - if(eyeobj.owner != src) - - reset_view(null) - else - var/isRemoteObserve = 0 - if((mRemote in mutations) && remoteview_target) - if(remoteview_target.stat==CONSCIOUS) - isRemoteObserve = 1 - if(!isRemoteObserve && client && !client.adminobs) - remoteview_target = null - reset_view(null, 0) - return 1 - -/mob/living/carbon/human/proc/process_glasses(var/obj/item/clothing/glasses/G) - . = FALSE - if(G && G.active) - if(G.darkness_view) - see_in_dark += G.darkness_view - . = TRUE - if(G.overlay && client) - client.screen |= G.overlay - if(G.vision_flags) - sight |= G.vision_flags - . = TRUE - if(istype(G,/obj/item/clothing/glasses/night) && !seer) - see_invisible = SEE_INVISIBLE_MINIMUM - - if(G.see_invisible >= 0) - see_invisible = G.see_invisible - . = TRUE - else if(!druggy && !seer) - see_invisible = see_invisible_default - -/mob/living/carbon/human/proc/process_nifsoft_vision(var/datum/nifsoft/NS) - . = FALSE - if(NS && NS.active) - if(NS.darkness_view) - see_in_dark += NS.darkness_view - . = TRUE - if(NS.vision_flags_mob) - sight |= NS.vision_flags_mob - . = TRUE - -/mob/living/carbon/human/handle_random_events() - if(inStasisNow()) - return - - // Puke if toxloss is too high - if(!stat && !isbelly(loc)) - if (getToxLoss() >= 30 && isSynthetic()) - if(!confused) - if(prob(5)) - to_chat(src, "You lose directional control!") - Confuse(10) - if (getToxLoss() >= 45 && !isSynthetic()) - spawn vomit() - - - //0.1% chance of playing a scary sound to someone who's in complete darkness - if(isturf(loc) && rand(1,1000) == 1) - var/turf/T = loc - if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) - //VOREStation Add Start - if(text2num(time2text(world.timeofday, "MM")) == 4) - if(text2num(time2text(world.timeofday, "DD")) == 1) - playsound_local(src,pick(scawwySownds),50, 0) - return - //VOREStation Add End - playsound_local(src,pick(scarySounds),50, 1, -1) - -/mob/living/carbon/human/handle_stomach() - spawn(0) - for(var/mob/living/M in stomach_contents) - if(M.loc != src) - stomach_contents.Remove(M) - continue - if(istype(M, /mob/living/carbon) && stat != 2) - if(M.stat == 2) - M.death(1) - stomach_contents.Remove(M) - qdel(M) - continue - if(air_master.current_cycle%3==1) - if(!(M.status_flags & GODMODE)) - M.adjustBruteLoss(5) - adjust_nutrition(10) - -/mob/living/carbon/human/proc/handle_changeling() - if(mind && mind.changeling) - mind.changeling.regenerate() - if(hud_used) - ling_chem_display.invisibility = 0 -// ling_chem_display.maptext = "
                    [round(mind.changeling.chem_charges)]
                    " - switch(mind.changeling.chem_storage) - if(1 to 50) - switch(mind.changeling.chem_charges) - if(0 to 9) - ling_chem_display.icon_state = "ling_chems0" - if(10 to 19) - ling_chem_display.icon_state = "ling_chems10" - if(20 to 29) - ling_chem_display.icon_state = "ling_chems20" - if(30 to 39) - ling_chem_display.icon_state = "ling_chems30" - if(40 to 49) - ling_chem_display.icon_state = "ling_chems40" - if(50) - ling_chem_display.icon_state = "ling_chems50" - if(51 to 80) //This is a crappy way of checking for engorged sacs... - switch(mind.changeling.chem_charges) - if(0 to 9) - ling_chem_display.icon_state = "ling_chems0e" - if(10 to 19) - ling_chem_display.icon_state = "ling_chems10e" - if(20 to 29) - ling_chem_display.icon_state = "ling_chems20e" - if(30 to 39) - ling_chem_display.icon_state = "ling_chems30e" - if(40 to 49) - ling_chem_display.icon_state = "ling_chems40e" - if(50 to 59) - ling_chem_display.icon_state = "ling_chems50e" - if(60 to 69) - ling_chem_display.icon_state = "ling_chems60e" - if(70 to 79) - ling_chem_display.icon_state = "ling_chems70e" - if(80) - ling_chem_display.icon_state = "ling_chems80e" - else - if(mind && hud_used) - ling_chem_display.invisibility = 101 - -/mob/living/carbon/human/handle_shock() - ..() - if(status_flags & GODMODE) return 0 //godmode - if(!can_feel_pain()) return - - if(health < config.health_threshold_softcrit)// health 0 makes you immediately collapse - shock_stage = max(shock_stage, 61) - - if(traumatic_shock >= 80) - shock_stage += 1 - else if(health < config.health_threshold_softcrit) - shock_stage = max(shock_stage, 61) - else - shock_stage = min(shock_stage, 160) - shock_stage = max(shock_stage-1, 0) - - if(stat) - return 0 - - if(shock_stage == 10) - if(traumatic_shock >= 80) - custom_pain("[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!", 40) - - if(shock_stage >= 30) - if(shock_stage == 30 && !isbelly(loc)) //VOREStation Edit - custom_emote(VISIBLE_MESSAGE, "is having trouble keeping their eyes open.") - eye_blurry = max(2, eye_blurry) - if(traumatic_shock >= 80) - stuttering = max(stuttering, 5) - - - if(shock_stage == 40) - if(traumatic_shock >= 80) - to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") - - if (shock_stage >= 60) - if(shock_stage == 60 && !isbelly(loc)) //VOREStation Edit - custom_emote(VISIBLE_MESSAGE, "'s body becomes limp.") - if (prob(2)) - if(traumatic_shock >= 80) - to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") - Weaken(20) - - if(shock_stage >= 80) - if (prob(5)) - if(traumatic_shock >= 80) - to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") - Weaken(20) - - if(shock_stage >= 120) - if (prob(2)) - if(traumatic_shock >= 80) - to_chat(src, "[pick("You black out", "You feel like you could die any moment now", "You are about to lose consciousness")]!") - Paralyse(5) - - if(shock_stage == 150) - if(!isbelly(loc)) //VOREStation Edit - custom_emote(VISIBLE_MESSAGE, "can no longer stand, collapsing!") - Weaken(20) - - if(shock_stage >= 150) - Weaken(20) - -/mob/living/carbon/human/proc/handle_pulse() - if(life_tick % 5) return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) - - var/temp = PULSE_NORM - - var/brain_modifier = 1 - - var/modifier_shift = 0 - var/modifier_set - - if(modifiers && modifiers.len) - for(var/datum/modifier/mod in modifiers) - if(isnull(modifier_set) && !isnull(mod.pulse_set_level)) - modifier_set = round(mod.pulse_set_level) // Should be a whole number, but let's not take chances. - else if(mod.pulse_set_level > modifier_set) - modifier_set = round(mod.pulse_set_level) - - modifier_set = max(0, modifier_set) // No setting to negatives. - - if(mod.pulse_modifier) - modifier_shift += mod.pulse_modifier - - modifier_shift = round(modifier_shift) - - if(!internal_organs_by_name[O_HEART]) - temp = PULSE_NONE - if(!isnull(modifier_set)) - temp = modifier_set - return temp //No blood, no pulse. - - if(stat == DEAD) - temp = PULSE_NONE - if(!isnull(modifier_set)) - temp = modifier_set - return temp //that's it, you're dead, nothing can influence your pulse, aside from outside means. - - var/obj/item/organ/internal/heart/Pump = internal_organs_by_name[O_HEART] - - var/obj/item/organ/internal/brain/Control = internal_organs_by_name[O_BRAIN] - - if(Control) - brain_modifier = Control.get_control_efficiency() - - if(brain_modifier <= 0.7 && brain_modifier >= 0.4) // 70%-40% control, things start going weird as the brain is failing. - brain_modifier = rand(5, 15) / 10 - - if(Pump) - temp += Pump.standard_pulse_level - PULSE_NORM - - if(round(vessel.get_reagent_amount("blood")) <= species.blood_volume*species.blood_level_danger) //how much blood do we have - temp = temp + 3 //not enough :( - - if(status_flags & FAKEDEATH) - temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds - - if(!isnull(modifier_set)) - temp = modifier_set - - temp = max(0, temp + modifier_shift) // No negative pulses. - - if(Pump) - for(var/datum/reagent/R in reagents.reagent_list) - if(R.id in bradycardics) - if(temp <= Pump.standard_pulse_level + 3 && temp >= Pump.standard_pulse_level) - temp-- - if(R.id in tachycardics) - if(temp <= Pump.standard_pulse_level + 1 && temp >= PULSE_NONE) - temp++ - if(R.id in heartstopper) //To avoid using fakedeath - temp = PULSE_NONE - if(R.id in cheartstopper) //Conditional heart-stoppage - if(R.volume >= R.overdose) - temp = PULSE_NONE - return temp * brain_modifier - //handles different chems' influence on pulse - for(var/datum/reagent/R in reagents.reagent_list) - if(R.id in bradycardics) - if(temp <= PULSE_THREADY && temp >= PULSE_NORM) - temp-- - if(R.id in tachycardics) - if(temp <= PULSE_FAST && temp >= PULSE_NONE) - temp++ - if(R.id in heartstopper) //To avoid using fakedeath - temp = PULSE_NONE - if(R.id in cheartstopper) //Conditional heart-stoppage - if(R.volume >= R.overdose) - temp = PULSE_NONE - - return max(0, round(temp * brain_modifier)) - -/mob/living/carbon/human/proc/handle_heartbeat() - if(pulse == PULSE_NONE) - return - - var/obj/item/organ/internal/heart/H = internal_organs_by_name[O_HEART] - - if(!H || (H.robotic >= ORGAN_ROBOT)) - return - - if(pulse >= PULSE_2FAST || shock_stage >= 10 || (istype(get_turf(src), /turf/space) && is_preference_enabled(/datum/client_preference/play_ambiance))) - //PULSE_THREADY - maximum value for pulse, currently it 5. - //High pulse value corresponds to a fast rate of heartbeat. - //Divided by 2, otherwise it is too slow. - var/rate = (PULSE_THREADY - pulse)/2 - - if(heartbeat >= rate) - heartbeat = 0 - src << sound('sound/effects/singlebeat.ogg',0,0,0,50) - else - heartbeat++ - -/* - Called by life(), instead of having the individual hud items update icons each tick and check for status changes - we only set those statuses and icons upon changes. Then those HUD items will simply add those pre-made images. - This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. -*/ -/mob/living/carbon/human/proc/handle_hud_list() - if (BITTEST(hud_updateflag, HEALTH_HUD)) - var/image/holder = grab_hud(HEALTH_HUD) - if(stat == DEAD) - holder.icon_state = "-100" // X_X - else - holder.icon_state = RoundHealth((health-config.health_threshold_crit)/(getMaxHealth()-config.health_threshold_crit)*100) - apply_hud(HEALTH_HUD, holder) - - if (BITTEST(hud_updateflag, LIFE_HUD)) - var/image/holder = grab_hud(LIFE_HUD) - if(isSynthetic()) - holder.icon_state = "hudrobo" - else if(stat == DEAD) - holder.icon_state = "huddead" - else - holder.icon_state = "hudhealthy" - apply_hud(LIFE_HUD, holder) - - if (BITTEST(hud_updateflag, STATUS_HUD)) - var/foundVirus = 0 - for (var/ID in virus2) - if (ID in virusDB) - foundVirus = 1 - break - - var/image/holder = grab_hud(STATUS_HUD) - var/image/holder2 = grab_hud(STATUS_HUD_OOC) - if (isSynthetic()) - holder.icon_state = "hudrobo" - else if(stat == DEAD) - holder.icon_state = "huddead" - holder2.icon_state = "huddead" - else if(foundVirus) - holder.icon_state = "hudill" - else if(has_brain_worms()) - var/mob/living/simple_mob/animal/borer/B = has_brain_worms() - if(B.controlling) - holder.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - holder2.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - if(virus2.len) - holder2.icon_state = "hudill" - else - holder2.icon_state = "hudhealthy" - - apply_hud(STATUS_HUD, holder) - apply_hud(STATUS_HUD_OOC, holder2) - - if (BITTEST(hud_updateflag, ID_HUD)) - var/image/holder = grab_hud(ID_HUD) - if(wear_id) - var/obj/item/weapon/card/id/I = wear_id.GetID() - if(I) - holder.icon_state = "hud[ckey(I.GetJobName())]" - else - holder.icon_state = "hudunknown" - else - holder.icon_state = "hudunknown" - - apply_hud(ID_HUD, holder) - - if (BITTEST(hud_updateflag, WANTED_HUD)) - var/image/holder = grab_hud(WANTED_HUD) - holder.icon_state = "hudblank" - var/perpname = name - if(wear_id) - var/obj/item/weapon/card/id/I = wear_id.GetID() - if(I) - perpname = I.registered_name - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) - holder.icon_state = "hudwanted" - break - else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Incarcerated")) - holder.icon_state = "hudprisoner" - break - else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Parolled")) - holder.icon_state = "hudparolled" - break - else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Released")) - holder.icon_state = "hudreleased" - break - - apply_hud(WANTED_HUD, holder) - - if ( BITTEST(hud_updateflag, IMPLOYAL_HUD) \ - || BITTEST(hud_updateflag, IMPCHEM_HUD) \ - || BITTEST(hud_updateflag, IMPTRACK_HUD)) - - var/image/holder1 = grab_hud(IMPTRACK_HUD) - var/image/holder2 = grab_hud(IMPLOYAL_HUD) - var/image/holder3 = grab_hud(IMPCHEM_HUD) - - holder1.icon_state = "hudblank" - holder2.icon_state = "hudblank" - holder3.icon_state = "hudblank" - - for(var/obj/item/weapon/implant/I in src) - if(I.implanted) - if(!I.malfunction) - if(istype(I,/obj/item/weapon/implant/tracking)) - holder1.icon_state = "hud_imp_tracking" - if(istype(I,/obj/item/weapon/implant/loyalty)) - holder2.icon_state = "hud_imp_loyal" - if(istype(I,/obj/item/weapon/implant/chem)) - holder3.icon_state = "hud_imp_chem" - - apply_hud(IMPTRACK_HUD, holder1) - apply_hud(IMPLOYAL_HUD, holder2) - apply_hud(IMPCHEM_HUD, holder3) - - if (BITTEST(hud_updateflag, SPECIALROLE_HUD)) - var/image/holder = grab_hud(SPECIALROLE_HUD) - holder.icon_state = "hudblank" - if(mind && mind.special_role) - if(hud_icon_reference[mind.special_role]) - holder.icon_state = hud_icon_reference[mind.special_role] - else - holder.icon_state = "hudsyndicate" - apply_hud(SPECIALROLE_HUD, holder) - - attempt_vr(src,"handle_hud_list_vr",list()) //VOREStation Add - Custom HUDs. - - hud_updateflag = 0 - -/mob/living/carbon/human/handle_fire() - if(..()) - return - - var/thermal_protection = get_heat_protection(fire_stacks * 1500) // Arbitrary but below firesuit max temp when below 20 stacks. - - if(thermal_protection == 1) // Immune. - return - else - var/fire_temp_add = (BODYTEMP_HEATING_MAX + (fire_stacks * 15)) * (1-thermal_protection) - //This is to prevent humans from heating up indefinitely. A human being on fire (fat burns at 250C) can't magically - // increase your body temperature beyond 250C, but it's possible something else (atmos) has heated us up beyond it, - // so don't worry about the firestacks at that point. Really, we should be cooling the room down, because it has - // to expend energy to heat our body up! But let's not worry about that. - - // This whole section above is ABSOLUTELY STUPID and makes no sense and this would prevent too-high-heat from even being able to hurt someone. No. We will heat up for as long as needed. - //if((bodytemperature + fire_temp_add) > HUMAN_COMBUSTION_TEMP) - // return - - bodytemperature += fire_temp_add - -/mob/living/carbon/human/rejuvenate() - restore_blood() - shock_stage = 0 - traumatic_shock = 0 - ..() - -/mob/living/carbon/human/proc/handle_defib_timer() - if(!should_have_organ(O_BRAIN)) - return // No brain. - - var/obj/item/organ/internal/brain/brain = internal_organs_by_name[O_BRAIN] - if(!brain) - return // Still no brain. - - brain.tick_defib_timer() - -#undef HUMAN_MAX_OXYLOSS -#undef HUMAN_CRIT_MAX_OXYLOSS +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 + +//NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick! +#define HUMAN_MAX_OXYLOSS 1 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. +#define HUMAN_CRIT_MAX_OXYLOSS ( 2.0 / 6) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 50HP to get through, so (1/6)*last_tick_duration per second. Breaths however only happen every 4 ticks. last_tick_duration = ~2.0 on average + +#define HEAT_DAMAGE_LEVEL_1 2 //VOREStation Edit //Amount of damage applied when your body temperature just passes the 360.15k safety point +#define HEAT_DAMAGE_LEVEL_2 4 //VOREStation Edit //Amount of damage applied when your body temperature passes the 400K point +#define HEAT_DAMAGE_LEVEL_3 8 //VOREStation Edit //Amount of damage applied when your body temperature passes the 1000K point + +#define COLD_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when your body temperature just passes the 260.15k safety point +#define COLD_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when your body temperature passes the 200K point +#define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point + +//Note that gas heat damage is only applied once every FOUR ticks. +#define HEAT_GAS_DAMAGE_LEVEL_1 2 //Amount of damage applied when the current breath's temperature just passes the 360.15k safety point +#define HEAT_GAS_DAMAGE_LEVEL_2 4 //Amount of damage applied when the current breath's temperature passes the 400K point +#define HEAT_GAS_DAMAGE_LEVEL_3 8 //Amount of damage applied when the current breath's temperature passes the 1000K point + +#define COLD_GAS_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when the current breath's temperature just passes the 260.15k safety point +#define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point +#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point + +#define COLD_ALERT_SEVERITY_LOW 1 // Constants passed to the cold and heat alerts. +#define COLD_ALERT_SEVERITY_MODERATE 2 +#define COLD_ALERT_SEVERITY_MAX 3 +#define ENVIRONMENT_COMFORT_MARKER_COLD 1 + +#define HOT_ALERT_SEVERITY_LOW 1 +#define HOT_ALERT_SEVERITY_MODERATE 2 +#define HOT_ALERT_SEVERITY_MAX 3 +#define ENVIRONMENT_COMFORT_MARKER_HOT 2 + +#define RADIATION_SPEED_COEFFICIENT 0.1 +#define HUMAN_COMBUSTION_TEMP 524 //524k is the sustained combustion temperature of human fat + +/mob/living/carbon/human + var/in_stasis = 0 + var/heartbeat = 0 + +/mob/living/carbon/human/Life() + set invisibility = 0 + set background = BACKGROUND_ENABLED + + if (transforming) + return + + //Apparently, the person who wrote this code designed it so that + //blinded get reset each cycle and then get activated later in the + //code. Very ugly. I dont care. Moving this stuff here so its easy + //to find it. + blinded = 0 + + //TODO: seperate this out + // update the current life tick, can be used to e.g. only do something every 4 ticks + life_tick++ + + // This is not an ideal place for this but it will do for now. + if(wearing_rig && wearing_rig.offline) + wearing_rig = null + + ..() + + if(life_tick % 30) + hud_updateflag = (1 << TOTAL_HUDS) - 1 + + voice = GetVoice() + + var/stasis = inStasisNow() + if(getStasis() > 2) + Sleeping(20) + + //No need to update all of these procs if the guy is dead. + fall() //VORESTATION EDIT. Prevents people from floating + if(stat != DEAD && !stasis) + //Updates the number of stored chemicals for powers + handle_changeling() + + //Organs and blood + handle_organs() + stabilize_body_temperature() //Body temperature adjusts itself (self-regulation) + weightgain() //VOREStation Addition + process_weaver_silk() //VOREStation Addition + handle_shock() + + handle_pain() + + handle_allergens() + + handle_medical_side_effects() + + handle_heartbeat() + handle_nif() //VOREStation Addition + if(!client) + species.handle_npc(src) + + else if(stat == DEAD && !stasis) + handle_defib_timer() + + if(skip_some_updates()) + return //We go ahead and process them 5 times for HUD images and other stuff though. + + //Update our name based on whether our face is obscured/disfigured + name = get_visible_name() + + pulse = handle_pulse() + +/mob/living/carbon/human/proc/skip_some_updates() + if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle + return 1 + return 0 + +/mob/living/carbon/human/breathe() + if(!inStasisNow()) + ..() + +// Calculate how vulnerable the human is to the current pressure. +// Returns 0 (equals 0 %) if sealed in an undamaged suit that's rated for the pressure, 1 if unprotected (equals 100%). +// Suitdamage can modifiy this in 10% steps. +// Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary +/mob/living/carbon/human/proc/get_pressure_weakness(pressure) + if(pressure == null) + return 1 // No protection if someone forgot to give a pressure + + var/pressure_adjustment_coefficient = 1 // Assume no protection at first. + + // Check suit + if(wear_suit && wear_suit.max_pressure_protection != null && wear_suit.min_pressure_protection != null) + pressure_adjustment_coefficient = 0 + // Pressure is too high + if(wear_suit.max_pressure_protection < pressure) + // Protection scales down from 100% at the boundary to 0% at 10% in excess of the boundary + pressure_adjustment_coefficient += round((pressure - wear_suit.max_pressure_protection) / (wear_suit.max_pressure_protection/10)) + + // Pressure is too low + if(wear_suit.min_pressure_protection > pressure) + pressure_adjustment_coefficient += round((wear_suit.min_pressure_protection - pressure) / (wear_suit.min_pressure_protection/10)) + + // Handles breaches in your space suit. 10 suit damage equals a 100% loss of pressure protection. + if(istype(wear_suit,/obj/item/clothing/suit/space)) + var/obj/item/clothing/suit/space/S = wear_suit + if(S.can_breach && S.damage) + pressure_adjustment_coefficient += S.damage * 0.1 + + else + // Missing key protection + pressure_adjustment_coefficient = 1 + + // Check hat + if(head && head.max_pressure_protection != null && head.min_pressure_protection != null) + // Pressure is too high + if(head.max_pressure_protection < pressure) + // Protection scales down from 100% at the boundary to 0% at 20% in excess of the boundary + pressure_adjustment_coefficient += round((pressure - head.max_pressure_protection) / (head.max_pressure_protection/20)) + + // Pressure is too low + if(head.min_pressure_protection > pressure) + pressure_adjustment_coefficient += round((head.min_pressure_protection - pressure) / (head.min_pressure_protection/20)) + + else + // Missing key protection + pressure_adjustment_coefficient = 1 + + pressure_adjustment_coefficient = min(pressure_adjustment_coefficient, 1) + return pressure_adjustment_coefficient + +// Calculate how much of the enviroment pressure-difference affects the human. +/mob/living/carbon/human/calculate_affecting_pressure(var/pressure) + var/pressure_difference + + // First get the absolute pressure difference. + if(pressure < species.safe_pressure) // We are in an underpressure. + pressure_difference = species.safe_pressure - pressure + + else //We are in an overpressure or standard atmosphere. + pressure_difference = pressure - species.safe_pressure + + if(pressure_difference < 5) // If the difference is small, don't bother calculating the fraction. + pressure_difference = 0 + + else + // Otherwise calculate how much of that absolute pressure difference affects us, can be 0 to 1 (equals 0% to 100%). + // This is our relative difference. + pressure_difference *= get_pressure_weakness(pressure) + + // The difference is always positive to avoid extra calculations. + // Apply the relative difference on a standard atmosphere to get the final result. + // The return value will be the adjusted_pressure of the human that is the basis of pressure warnings and damage. + if(pressure < species.safe_pressure) + return species.safe_pressure - pressure_difference + else + return species.safe_pressure + pressure_difference + +/mob/living/carbon/human/handle_disabilities() + ..() + + if(stat != CONSCIOUS) //Let's not worry about tourettes if you're not conscious. + return + + if (disabilities & EPILEPSY) + if ((prob(1) && paralysis < 1)) + to_chat(src, span_red("You have a seizure!")) + for(var/mob/O in viewers(src, null)) + if(O == src) + continue + O.show_message(text("[src] starts having a seizure!"), 1) + Paralyse(10) + make_jittery(1000) + if (disabilities & COUGHING) + if ((prob(5) && paralysis <= 1)) + drop_item() + spawn( 0 ) + emote("cough") + return + if (disabilities & TOURETTES) + if ((prob(10) && paralysis <= 1)) + Stun(10) + spawn( 0 ) + switch(rand(1, 3)) + if(1) + emote("twitch") + if(2 to 3) + say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") + make_jittery(100) + return + if (disabilities & NERVOUS) + if (prob(10)) + stuttering = max(10, stuttering) + + var/rn = rand(0, 200) + if(getBrainLoss() >= 5) + if(0 <= rn && rn <= 3) + custom_pain("Your head feels numb and painful.", 10) + if(getBrainLoss() >= 15) + if(4 <= rn && rn <= 6) if(eye_blurry <= 0) + to_chat(src, "It becomes hard to see for some reason.") + eye_blurry = 10 + if(getBrainLoss() >= 35) + if(7 <= rn && rn <= 9) if(get_active_hand()) + to_chat(src, "Your hand won't respond properly, you drop what you're holding!") + drop_item() + if(getBrainLoss() >= 45) + if(10 <= rn && rn <= 12) + if(prob(50)) + to_chat(src, "You suddenly black out!") + Paralyse(10) + else if(!lying) + to_chat(src, "Your legs won't respond properly, you fall down!") + Weaken(10) + +// RADIATION! Everyone's favorite thing in the world! So let's get some numbers down off the bat. +// 50 rads = 1Bq. This means 1 rad = 0.02Bq. +// However, unless I am a smoothbrained dumbo, absorbed rads are in Gy. Not Bq. +// So let's just assume that 50 rads = 1Gy. Make life easier! + +// ACUTE RADIATION (The stuff that the 'radiation' variable takes care of. Remember, 50radiation=1Gy.): +// Without care: 1-2Gy has a (0-5%) mortality chance. 2-6 (5-95%) 6-8 (95-100)% 8-30 (100%) >30 (100%) +// With care: 1-2Gy (0-5%), 2-6 (5-50%), 6-8 (50-100%), 8-30 (99-100%) >30 (100%) +// So let's make our thresholds based on this! 50-100, 100-300, 300-400, 400-1500, and anything above 1500! +// In reality, however, nobody should ever go above 300 radiation, which is why the cutoff before the really bad effects start to happen being +// 300 radiation is good. For reference: Breaking an artifact deals ~300 rads with no resistance. Getting shot with a lvl 3 PA deals 300 rads with no resistance. +// Nobody outside of engineering should ever have to worry about being irradiated over 300 and start getting organ damage.. + + +// CHRONIC RADIATION (The stuff that 'accumulated_rads' takes care of): +// This is more or less for if someone was exposed for a long time to radiation or just finished being treated for extreme ARS. +// These are meant to be annoying effects to nudge someone towards medical, but not lethal or deadly. +// Things such as loss of taste, eye damage, dropping items in your hand, being temporaily weakened, etc. Stuff to annoy them and get them to fix their rads. + +// Additionally, RADIATION_SPEED_COEFFICIENT = 0.1 + +/mob/living/carbon/human/handle_mutations_and_radiation() //Radiation rework! Now with 'accumulated_rads' + if(inStasisNow()) + return + + if(getFireLoss()) + if((COLD_RESISTANCE in mutations) || (prob(1))) + heal_organ_damage(0,1) + + // DNA2 - Gene processing. + // The HULK stuff that was here is now in the hulk gene. + if(!isSynthetic()) + for(var/datum/dna/gene/gene in dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + gene.OnMobLife(src) + + radiation = CLAMP(radiation,0,2500) //Max of 50Gy. If you reach that...You're going to wish you were dead. You probably will be dead. + accumulated_rads = CLAMP(accumulated_rads,0,2500) //Max of 50Gy as well. You should never get higher than this. You will be dead before you can reach this. + var/obj/item/organ/internal/I = null //Used for further down below when an organ is picked. + if(!radiation) + if(species.appearance_flags & RADIATION_GLOWS) + glow_override = FALSE + set_light(0) + if(accumulated_rads) + accumulated_rads -= RADIATION_SPEED_COEFFICIENT //Accumulated rads slowly dissipate very slowly. Get to medical to get it treated! + else if(((life_tick % 5 == 0) && radiation) || (radiation > 600)) //Radiation is a slow, insidious killer. Unless you get a massive dose, then the onset is sudden! + if(species.appearance_flags & RADIATION_GLOWS) + glow_override = TRUE + set_light(max(1,min(5,radiation/15)), max(1,min(10,radiation/25)), species.get_flesh_colour(src)) + // END DOGSHIT SNOWFLAKE + + var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs + if(rad_organ && !rad_organ.is_broken()) + var/rads = radiation/25 + radiation -= rads + adjust_nutrition(rads) + adjustBruteLoss(-(rads)) + adjustFireLoss(-(rads)) + adjustOxyLoss(-(rads)) + adjustToxLoss(-(rads)) + updatehealth() + return + + var/obj/item/organ/internal/brain/slime/core = locate() in internal_organs + if(core) + return + + //VOREStation Addition start: shadekin + var/obj/item/organ/internal/brain/shadekin/s_brain = locate() in internal_organs + if(s_brain) + return + //VOREStation Addition end: shadekin + + if(reagents.has_reagent("prussian_blue")) //Prussian Blue temporarily stops radiation effects. + return + + var/damage = 0 + + + if (radiation < 50) //Less than 1.0 Gy. No side effects. + radiation -= 10 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT //No escape from accumulated rads. + + else if (radiation >= 50 && radiation < 100) //Equivalent of 1.0-2.0 Gy. Minimum stage you start seeing effects. + damage = 1 + radiation -= 10 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && !weakened) + to_chat(src, "You feel exhausted.") + AdjustWeakened(3) + if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && species.get_bodytype() == SPECIES_HUMAN) //apes go bald + if((h_style != "Bald" || f_style != "Shaved" )) + to_chat(src, "Your hair falls out.") + h_style = "Bald" + f_style = "Shaved" + update_hair() + if(prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //Rare chance of vomiting. + spawn vomit() + + else if (radiation >= 100 && radiation < 300) //Equivalent of 2.0 to 6.0 Gy. Nobody should ever be above this without extreme negligence. + damage = 3 + radiation -= 30 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 30 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(5)) + take_overall_damage(0, 5 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") + if(prob(1)) + adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) + emote("gasp") + if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(prob(10) && !weakened) + to_chat(src, "You feel sick.") + AdjustWeakened(3) + + else if (radiation >= 300 && radiation < 400) //Equivalent of 6.0 to 8.0 Gy. + damage = 5 + radiation -= 50 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 50 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(15)) + take_overall_damage(0, 10 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") + if(prob(2)) + adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) + emote("gasp") + if(prob(10) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(prob(15) && !weakened) + to_chat(src, "You feel horribly ill.") + AdjustWeakened(3) + if(prob(5) && internal_organs.len) + I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. + if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + + + else if (radiation >= 400 && radiation < 1500) //Equivalent of 8.0 to 30 Gy. + damage = 10 + radiation -= 100 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 100 * RADIATION_SPEED_COEFFICIENT + if(!isSynthetic()) + if(prob(25)) + take_overall_damage(0, 15 * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") + if(prob(5)) + I = internal_organs_by_name[O_EYES] + if(I) + if(istype(I)) I.add_autopsy_data("Radiation Burns", damage) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + to_chat(src, "Your eyes burn!") + eye_blurry += 10 + if(prob(4)) + adjustCloneLoss(5 * RADIATION_SPEED_COEFFICIENT) + emote("gasp") + if(prob(25) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(prob(20) && !weakened) + to_chat(src, "You feel like your insides are burning!") + AdjustWeakened(5) + if(prob(5)) + to_chat(src, "Your entire body feels like it's on fire!") + adjustHalLoss(5) + if(prob(10) && internal_organs.len) + I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. + if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + + else if (radiation >= 1500) //Above 30Gy. You had to get absolutely blasted with rads for this. + damage = 30 + radiation -= 300 * RADIATION_SPEED_COEFFICIENT + accumulated_rads += 300 * RADIATION_SPEED_COEFFICIENT + + if(!isSynthetic()) + take_overall_damage(0, damage * RADIATION_SPEED_COEFFICIENT, used_weapon = "Radiation Burns") //3 burn damage a tick as your body melts. + adjustCloneLoss(15 * RADIATION_SPEED_COEFFICIENT) //1.5 cloneloss a tick as your cells mutate and break down. + + I = internal_organs_by_name[O_EYES] + if(I) + I.add_autopsy_data("Radiation Burns", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //3 eye damage a tick as your eyes melt down. + eye_blurry += 10 + + if(prob(50) && prob(100 * RADIATION_SPEED_COEFFICIENT)) + spawn vomit() + if(!paralysis && prob(30) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. + to_chat(src, "You have a seizure!") + Paralyse(10) + make_jittery(1000) + if(!lying) + emote("collapse") + if(get_active_hand() && prob(15)) //CNS is shutting down. + to_chat(src, "Your hand won't respond properly, you drop what you're holding!") + drop_item() + if(internal_organs.len) + I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. + if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + +/* //Not-so-sparkledog code. TODO: Make a pref for 'special game interactions' that allows interactions that align with prefs to occur. + if(radiation >= 250) //Special effect stuff that occurs at certain rad levels. + if(prob(1) && prob(radiation/2 * RADIATION_SPEED_COEFFICIENT) && allow_spontaneous_tf) //If you've got spontaneous TF...well... + scramble(1, src, 3) //I tried to base this on how many rads you took and it was...Hilarious. Sparkledogs everywhere. + //For the most part, 3 strength will simply change colors. If you get really unlucky, it can do more TF's. + //Math: 250 rads = 1/800 chance + //500 rads = 1/400 chance chance. Etc. +*/ + + if(damage) + damage *= species.radiation_mod + adjustToxLoss(damage * RADIATION_SPEED_COEFFICIENT) + updatehealth() + if(!isSynthetic() && organs.len) + var/obj/item/organ/external/O = pick(organs) + if(istype(O)) O.add_autopsy_data("Radiation Poisoning", damage) + + // Begin long-term radiation effects + // Loss of taste occurs at 100 (2Gy) and is handled in taste.dm + // These are all done one after another, so duplication is not required. Someone at 400rads will have the 100&400 effects. + if(!radiation && accumulated_rads >= 100 && !reagents.has_reagent("prussian_blue")) //Let's not hit them with long term effects when they're actively being hit with rads. + if(!isSynthetic()) + I = internal_organs_by_name[O_EYES] + if(I) //Eye stuff + if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your eyes water.") + eye_blurry += 5 + if(accumulated_rads > 300) // (6Gy) + if(prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your eyes burn.") + I.add_autopsy_data("Radiation Burns", 1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) + I.take_damage(1 * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) //0.1 damage. Not a lot, but enough to tell you to get to medical. + eye_blurry += 10 + + if(accumulated_rads > 200) // (4Gy) + if(prob(5) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your feel nauseated.") + spawn vomit() + if(!weakened && prob(2) && prob(accumulated_rads * RADIATION_SPEED_COEFFICIENT)) + to_chat(src, "Your feel exhausted.") + AdjustWeakened(3) + if(accumulated_rads > 300) // (6Gy) + if(get_active_hand() && prob(15) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //CNS is shutting down. + to_chat(src, "Your hand won't respond properly, you drop what you're holding!") + drop_item() + if(accumulated_rads > 700) // (12Gy) + if(!paralysis && prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //1 in 1000 chance per tick. + to_chat(src, "You have a seizure!") + Paralyse(10) + make_jittery(1000) + if(!lying) + emote("collapse") + + else //The synthetic effects! + return //Nothing for now. + + + + /** breathing **/ + +/mob/living/carbon/human/handle_chemical_smoke(var/datum/gas_mixture/environment) + if(wear_mask && (wear_mask.item_flags & BLOCK_GAS_SMOKE_EFFECT)) + return + if(glasses && (glasses.item_flags & BLOCK_GAS_SMOKE_EFFECT)) + return + if(head && (head.item_flags & BLOCK_GAS_SMOKE_EFFECT)) + return + ..() + +/mob/living/carbon/human/handle_post_breath(datum/gas_mixture/breath) + ..() + //spread some viruses while we are at it + if(breath && virus2.len > 0 && prob(10)) + for(var/mob/living/carbon/M in view(1,src)) + src.spread_disease_to(M) + + +/mob/living/carbon/human/get_breath_from_internal(volume_needed=BREATH_VOLUME) + if(internal) + //Because rigs store their tanks out of reach of contents.Find(), a check has to be made to make + //sure the rig is still worn, still online, and that its air supply still exists. + var/obj/item/weapon/tank/rig_supply + var/obj/item/weapon/rig/Rig = get_rig() + + if(Rig) + rig_supply = Rig.air_supply + + if ((!rig_supply && !contents.Find(internal)) || !((wear_mask && (wear_mask.item_flags & AIRTIGHT)) || (head && (head.item_flags & AIRTIGHT)))) + internal = null + + if(internal) + return internal.remove_air_volume(volume_needed) + else if(internals) + internals.icon_state = "internal0" + return null + + +/mob/living/carbon/human/handle_breath(datum/gas_mixture/breath) + if(status_flags & GODMODE) + return + + if(suiciding) + failed_last_breath = 1 + adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster + suiciding-- + return 0 + + if(does_not_breathe) + failed_last_breath = 0 + adjustOxyLoss(-5) + return + + if(!breath || (breath.total_moles == 0)) + failed_last_breath = 1 + if(health > config.health_threshold_crit) + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + else + adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) + + if(breath && should_have_organ(O_LUNGS)) + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + if(!L.is_bruised() && prob(8)) + rupture_lung() + + throw_alert("pressure", /obj/screen/alert/lowpressure) + return 0 + else + clear_alert("pressure") + + var/safe_pressure_min = species.minimum_breath_pressure // Minimum safe partial pressure of breathable gas in kPa + + + // Lung damage increases the minimum safe pressure. + if(should_have_organ(O_LUNGS)) + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + if(isnull(L)) + safe_pressure_min = INFINITY //No lungs, how are you breathing? + else if(L.is_broken()) + safe_pressure_min *= 1.5 + else if(L.is_bruised()) + safe_pressure_min *= 1.25 + else if(breath) + if(breath.total_moles < BREATH_MOLES / 10 || breath.total_moles > BREATH_MOLES * 5) + if(is_below_sound_pressure(get_turf(src))) //No more popped lungs from choking/drowning + if (prob(8)) + rupture_lung() + + var/safe_exhaled_max = 10 + var/safe_toxins_max = 0.2 + var/SA_para_min = 1 + var/SA_sleep_min = 5 + var/inhaled_gas_used = 0 + + var/breath_pressure = (breath.total_moles*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME + + var/inhaling + var/poison + var/exhaling + + var/breath_type + var/poison_type + var/exhale_type + + var/failed_inhale = 0 + var/failed_exhale = 0 + + if(species.breath_type) + breath_type = species.breath_type + else + breath_type = "oxygen" + inhaling = breath.gas[breath_type] + + if(species.poison_type) + poison_type = species.poison_type + else + poison_type = "phoron" + poison = breath.gas[poison_type] + + if(species.exhale_type) + exhale_type = species.exhale_type + exhaling = breath.gas[exhale_type] + else + exhaling = 0 + + var/inhale_pp = (inhaling/breath.total_moles)*breath_pressure + var/toxins_pp = (poison/breath.total_moles)*breath_pressure + // To be clear, this isn't how much they're exhaling -- it's the amount of the species exhale_gas that they just + var/exhaled_pp = (exhaling/breath.total_moles)*breath_pressure + + // Not enough to breathe + if(inhale_pp < safe_pressure_min) + if(prob(20)) + spawn(0) emote("gasp") + + var/ratio = inhale_pp/safe_pressure_min + // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!) + adjustOxyLoss(max(HUMAN_MAX_OXYLOSS*(1-ratio), 0)) + failed_inhale = 1 + + switch(breath_type) + if("oxygen") + throw_alert("oxy", /obj/screen/alert/not_enough_oxy) + if("phoron") + throw_alert("oxy", /obj/screen/alert/not_enough_tox) + if("nitrogen") + throw_alert("oxy", /obj/screen/alert/not_enough_nitro) + if("carbon_dioxide") + throw_alert("oxy", /obj/screen/alert/not_enough_co2) + if("volatile_fuel") + throw_alert("oxy", /obj/screen/alert/not_enough_fuel) + if("nitrous_oxide") + throw_alert("oxy", /obj/screen/alert/not_enough_n2o) + + else + // We're in safe limits + clear_alert("oxy") + + inhaled_gas_used = inhaling/6 + + breath.adjust_gas(breath_type, -inhaled_gas_used, update = 0) //update afterwards + + if(exhale_type) + breath.adjust_gas_temp(exhale_type, inhaled_gas_used, bodytemperature, update = 0) //update afterwards + + // Too much exhaled gas in the air + if(exhaled_pp > safe_exhaled_max) + if (prob(15)) + var/word = pick("extremely dizzy","short of breath","faint","confused") + to_chat(src, "You feel [word].") + + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + failed_exhale = 1 + + else if(exhaled_pp > safe_exhaled_max * 0.7) + if (!prob(1)) + var/word = pick("dizzy","short of breath","faint","momentarily confused") + to_chat(src, "You feel [word].") + + //scale linearly from 0 to 1 between safe_exhaled_max and safe_exhaled_max*0.7 + var/ratio = 1.0 - (safe_exhaled_max - exhaled_pp)/(safe_exhaled_max*0.3) + + //give them some oxyloss, up to the limit - we don't want people falling unconcious due to CO2 alone until they're pretty close to safe_exhaled_max. + if (getOxyLoss() < 50*ratio) + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + failed_exhale = 1 + + else if(exhaled_pp > safe_exhaled_max * 0.6) + if(prob(0.3)) + var/word = pick("a little dizzy","short of breath") + to_chat(src, "You feel [word].") + + // Too much poison in the air. + if(toxins_pp > safe_toxins_max) + var/ratio = (poison/safe_toxins_max) * 10 + if(reagents) + reagents.add_reagent("toxin", CLAMP(ratio, MIN_TOXIN_DAMAGE, MAX_TOXIN_DAMAGE)) + breath.adjust_gas(poison_type, -poison/6, update = 0) //update after + throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) + else + clear_alert("tox_in_air") + + // If there's some other shit in the air lets deal with it here. + if(breath.gas["nitrous_oxide"]) + var/SA_pp = (breath.gas["nitrous_oxide"] / breath.total_moles) * breath_pressure + + // Enough to make us paralysed for a bit + if(SA_pp > SA_para_min) + + // 3 gives them one second to wake up and run away a bit! + Paralyse(3) + + // Enough to make us sleep as well + if(SA_pp > SA_sleep_min) + Sleeping(5) + + // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning + else if(SA_pp > 0.15) + if(prob(20)) + spawn(0) emote(pick("giggle", "laugh")) + breath.adjust_gas("nitrous_oxide", -breath.gas["nitrous_oxide"]/6, update = 0) //update after + + // Were we able to breathe? + if (failed_inhale || failed_exhale) + failed_last_breath = 1 + else + failed_last_breath = 0 + adjustOxyLoss(-5) + + + // Hot air hurts :( + if((breath.temperature <= species.cold_discomfort_level || breath.temperature >= species.heat_discomfort_level) && !(COLD_RESISTANCE in mutations)) + + if(breath.temperature <= species.breath_cold_level_1) + if(prob(20)) + to_chat(src, "You feel your face freezing and icicles forming in your lungs!") + else if(breath.temperature >= species.breath_heat_level_1) + if(prob(20)) + to_chat(src, "You feel your face burning and a searing heat in your lungs!") + + if(breath.temperature >= species.heat_discomfort_level) + + if(breath.temperature >= species.breath_heat_level_3) + apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Heat") + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) + else if(breath.temperature >= species.breath_heat_level_2) + apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Heat") + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) + else if(breath.temperature >= species.breath_heat_level_1) + apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Heat") + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) + else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_HOT)) + throw_alert("temp", /obj/screen/alert/warm, HOT_ALERT_SEVERITY_LOW) + else + clear_alert("temp") + + else if(breath.temperature <= species.cold_discomfort_level) + + if(breath.temperature <= species.breath_cold_level_3) + apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, BP_HEAD, used_weapon = "Excessive Cold") + throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MAX) + else if(breath.temperature <= species.breath_cold_level_2) + apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, BP_HEAD, used_weapon = "Excessive Cold") + throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_MODERATE) + else if(breath.temperature <= species.breath_cold_level_1) + apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, BP_HEAD, used_weapon = "Excessive Cold") + throw_alert("temp", /obj/screen/alert/cold, COLD_ALERT_SEVERITY_LOW) + else if(species.get_environment_discomfort(src, ENVIRONMENT_COMFORT_MARKER_COLD)) + throw_alert("temp", /obj/screen/alert/chilly, COLD_ALERT_SEVERITY_LOW) + else + clear_alert("temp") + + //breathing in hot/cold air also heats/cools you a bit + var/temp_adj = breath.temperature - bodytemperature + if (temp_adj < 0) + temp_adj /= (BODYTEMP_COLD_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed + else + temp_adj /= (BODYTEMP_HEAT_DIVISOR * 5) //don't raise temperature as much as if we were directly exposed + + var/relative_density = breath.total_moles / (MOLES_CELLSTANDARD * BREATH_PERCENTAGE) + temp_adj *= relative_density + + if(temp_adj > BODYTEMP_HEATING_MAX) + temp_adj = BODYTEMP_HEATING_MAX + if(temp_adj < BODYTEMP_COOLING_MAX) + temp_adj = BODYTEMP_COOLING_MAX + + bodytemperature += temp_adj + + else + clear_alert("temp") + + breath.update_values() + return 1 + +/mob/living/carbon/human/proc/handle_allergens() + if(chem_effects[CE_ALLERGEN]) + //first, multiply the basic species-level value by our allergen effect rating, so consuming multiple seperate allergen typess simultaneously hurts more + var/damage_severity = species.allergen_damage_severity * chem_effects[CE_ALLERGEN] + var/disable_severity = species.allergen_disable_severity * chem_effects[CE_ALLERGEN] + if(species.allergen_reaction & AG_PHYS_DMG) + adjustBruteLoss(damage_severity) + if(species.allergen_reaction & AG_BURN_DMG) + adjustFireLoss(damage_severity) + if(species.allergen_reaction & AG_TOX_DMG) + adjustToxLoss(damage_severity) + if(species.allergen_reaction & AG_OXY_DMG) + adjustOxyLoss(damage_severity) + if(prob(disable_severity/2)) + emote(pick("cough","gasp","choke")) + if(species.allergen_reaction & AG_EMOTE) + if(prob(disable_severity/2)) + emote(pick("pale","shiver","twitch")) + if(species.allergen_reaction & AG_PAIN) + adjustHalLoss(disable_severity) + if(species.allergen_reaction & AG_WEAKEN) + Weaken(disable_severity) + if(species.allergen_reaction & AG_BLURRY) + eye_blurry = max(eye_blurry, disable_severity) + if(species.allergen_reaction & AG_SLEEPY) + drowsyness = max(drowsyness, disable_severity) + if(species.allergen_reaction & AG_CONFUSE) + Confuse(disable_severity/4) + +/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) + if(!environment) + return + //Stuff like the xenomorph's plasma regen happens here. + species.handle_environment_special(src) + + if(is_incorporeal()) + return + + //Moved pressure calculations here for use in skip-processing check. + var/pressure = environment.return_pressure() + var/adjusted_pressure = calculate_affecting_pressure(pressure) + + //Check for contaminants before anything else because we don't want to skip it. + for(var/g in environment.gas) + if(gas_data.flags[g] & XGM_GAS_CONTAMINANT && environment.gas[g] > gas_data.overlay_limit[g] + 1) + pl_effects() + break + + if(istype(loc, /turf/space)) //VOREStation Edit - No FBPs overheating on space turfs inside mechs or people. + //Don't bother if the temperature drop is less than 0.1 anyways. Hopefully BYOND is smart enough to turn this constant expression into a constant + if(bodytemperature > (0.1 * HUMAN_HEAT_CAPACITY/(HUMAN_EXPOSED_SURFACE_AREA*STEFAN_BOLTZMANN_CONSTANT))**(1/4) + COSMIC_RADIATION_TEMPERATURE) + //Thermal radiation into space + var/heat_loss = HUMAN_EXPOSED_SURFACE_AREA * STEFAN_BOLTZMANN_CONSTANT * ((bodytemperature - COSMIC_RADIATION_TEMPERATURE)**4) + var/temperature_loss = heat_loss/HUMAN_HEAT_CAPACITY + bodytemperature -= temperature_loss + else + var/loc_temp = T0C + if(istype(loc, /obj/mecha)) + var/obj/mecha/M = loc + loc_temp = M.return_temperature() + else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/obj/machinery/atmospherics/unary/cryo_cell/cc = loc + loc_temp = cc.air_contents.temperature + else + loc_temp = environment.temperature + + if(adjusted_pressure < species.warning_high_pressure && adjusted_pressure > species.warning_low_pressure && abs(loc_temp - bodytemperature) < 20 && bodytemperature < species.heat_level_1 && bodytemperature > species.cold_level_1) + clear_alert("pressure") + return // Temperatures are within normal ranges, fuck all this processing. ~Ccomp + + //Body temperature adjusts depending on surrounding atmosphere based on your thermal protection (convection) + var/temp_adj = 0 + if(loc_temp < bodytemperature) //Place is colder than we are + var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. + temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR) //this will be negative + else if (loc_temp > bodytemperature) //Place is hotter than we are + var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 0.99) //For some reason, < 1 returns false if the value is 1. + temp_adj = (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) + + //Use heat transfer as proportional to the gas density. However, we only care about the relative density vs standard 101 kPa/20 C air. Therefore we can use mole ratios + var/relative_density = environment.total_moles / MOLES_CELLSTANDARD + bodytemperature += between(BODYTEMP_COOLING_MAX, temp_adj*relative_density, BODYTEMP_HEATING_MAX) + + // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. + if(bodytemperature >= species.heat_level_1) + //Body temperature is too hot. + if(status_flags & GODMODE) + return 1 //godmode + + var/burn_dam = 0 + + // switch() can't access numbers inside variables, so we need to use some ugly if() spam ladder. + if(bodytemperature >= species.heat_level_1) + if(bodytemperature >= species.heat_level_2) + if(bodytemperature >= species.heat_level_3) + burn_dam = HEAT_DAMAGE_LEVEL_3 + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MAX) + else + burn_dam = HEAT_DAMAGE_LEVEL_2 + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_MODERATE) + else + burn_dam = HEAT_DAMAGE_LEVEL_1 + throw_alert("temp", /obj/screen/alert/hot, HOT_ALERT_SEVERITY_LOW) + + take_overall_damage(burn=burn_dam, used_weapon = "High Body Temperature") + + else if(bodytemperature <= species.cold_level_1) + //Body temperature is too cold. + + if(status_flags & GODMODE) + return 1 //godmode + + + if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/cold_dam = 0 + if(bodytemperature <= species.cold_level_1) + if(bodytemperature <= species.cold_level_2) + if(bodytemperature <= species.cold_level_3) + cold_dam = COLD_DAMAGE_LEVEL_3 + else + cold_dam = COLD_DAMAGE_LEVEL_2 + else + cold_dam = COLD_DAMAGE_LEVEL_1 + + take_overall_damage(burn=cold_dam, used_weapon = "Low Body Temperature") + + else clear_alert("temp") + + // Account for massive pressure differences. Done by Polymorph + // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! + if(status_flags & GODMODE) + return 1 //godmode + + if(adjusted_pressure >= species.hazard_high_pressure) + var/pressure_damage = min( ( (adjusted_pressure / species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) + if(stat==DEAD) + pressure_damage = pressure_damage/2 + take_overall_damage(brute=pressure_damage, used_weapon = "High Pressure") + throw_alert("pressure", /obj/screen/alert/highpressure, 2) + else if(adjusted_pressure >= species.warning_high_pressure) + throw_alert("pressure", /obj/screen/alert/highpressure, 1) + else if(adjusted_pressure >= species.warning_low_pressure) + clear_alert("pressure") + else if(adjusted_pressure >= species.hazard_low_pressure) + throw_alert("pressure", /obj/screen/alert/lowpressure, 1) + else + if( !(COLD_RESISTANCE in mutations)) + if(!isSynthetic() || !nif || !nif.flag_check(NIF_O_PRESSURESEAL,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF pressure seals + var/pressure_damage = LOW_PRESSURE_DAMAGE + if(stat==DEAD) + pressure_damage = pressure_damage/2 + take_overall_damage(brute=pressure_damage, used_weapon = "Low Pressure") + if(getOxyLoss() < 55) // 12 OxyLoss per 4 ticks when wearing internals; unconsciousness in 16 ticks, roughly half a minute + var/pressure_dam = 3 // 16 OxyLoss per 4 ticks when no internals present; unconsciousness in 13 ticks, roughly twenty seconds + // (Extra 1 oxyloss from failed breath) + // Being in higher pressure decreases the damage taken, down to a minimum of (species.hazard_low_pressure / ONE_ATMOSPHERE) at species.hazard_low_pressure + pressure_dam *= (ONE_ATMOSPHERE - adjusted_pressure) / ONE_ATMOSPHERE + + if(wear_suit && wear_suit.min_pressure_protection && head && head.min_pressure_protection) + var/protection = max(wear_suit.min_pressure_protection, head.min_pressure_protection) // Take the weakest protection + pressure_dam *= (protection) / (ONE_ATMOSPHERE) // Divide by ONE_ATMOSPHERE to get a fractional protection + // Stronger protection (Closer to 0) results in a smaller fraction + // Firesuits (Min protection = 0.2 atmospheres) decrease oxyloss to 1/5 + + adjustOxyLoss(pressure_dam) + throw_alert("pressure", /obj/screen/alert/lowpressure, 2) + else + clear_alert("pressure") + + return + +/* +/mob/living/carbon/human/proc/adjust_body_temperature(current, loc_temp, boost) + var/temperature = current + var/difference = abs(current-loc_temp) //get difference + var/increments// = difference/10 //find how many increments apart they are + if(difference > 50) + increments = difference/5 + else + increments = difference/10 + var/change = increments*boost // Get the amount to change by (x per increment) + var/temp_change + if(current < loc_temp) + temperature = min(loc_temp, temperature+change) + else if(current > loc_temp) + temperature = max(loc_temp, temperature-change) + temp_change = (temperature - current) + return temp_change +*/ + +/mob/living/carbon/human/proc/stabilize_body_temperature() + // We produce heat naturally. + if (species.passive_temp_gain) + bodytemperature += species.passive_temp_gain + if (species.body_temperature == null) + return //this species doesn't have metabolic thermoregulation + + // FBPs will overheat when alive, prosthetic limbs are fine. + if(stat != DEAD && robobody_count) + if(!nif || !nif.flag_check(NIF_O_HEATSINKS,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF heatsinks prevent the base heat increase per tick if installed. + bodytemperature += round(robobody_count*1.15) + var/obj/item/organ/internal/robotic/heatsink/HS = internal_organs_by_name[O_HEATSINK] + if(!HS || HS.is_broken()) // However, NIF Heatsinks will not compensate for a core FBP component (your heatsink) being lost. + bodytemperature += round(robobody_count*0.5) + + var/body_temperature_difference = species.body_temperature - bodytemperature + + if (abs(body_temperature_difference) < 0.5) + return //fuck this precision + + if (on_fire) + return //too busy for pesky metabolic regulation + + if(bodytemperature < species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. + if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. + adjust_nutrition(-2) + var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + //to_world("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") +// log_debug("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") + bodytemperature += recovery_amt + else if(species.cold_level_1 <= bodytemperature && bodytemperature <= species.heat_level_1) + var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR + //to_world("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") +// log_debug("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") + bodytemperature += recovery_amt + else if(bodytemperature > species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. + //We totally need a sweat system cause it totally makes sense...~ + var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers + //to_world("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") +// log_debug("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") + bodytemperature += recovery_amt + + //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) +/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. + . = 0 + //Handle normal clothing + for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) + if(C) + if(C.handle_high_temperature(temperature)) + . |= C.get_heat_protection_flags() + +//See proc/get_heat_protection_flags(temperature) for the description of this proc. +/mob/living/carbon/human/proc/get_cold_protection_flags(temperature) + . = 0 + //Handle normal clothing + for(var/obj/item/clothing/C in list(head,wear_suit,w_uniform,shoes,gloves,wear_mask)) + if(C) + if(C.handle_low_temperature(temperature)) + . |= C.get_cold_protection_flags() + +/mob/living/carbon/human/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. + var/thermal_protection_flags = get_heat_protection_flags(temperature) + + . = get_thermal_protection(thermal_protection_flags) + . = 1 - . // Invert from 1 = immunity to 0 = immunity. + + // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. + for(var/datum/modifier/M as anything in modifiers) + if(!isnull(M.heat_protection)) + . *= 1 - M.heat_protection + + // Code that calls this expects 1 = immunity so we need to invert again. + . = 1 - . + . = min(., 1.0) + +/mob/living/carbon/human/get_cold_protection(temperature) + if(COLD_RESISTANCE in mutations) + return 1 //Fully protected from the cold. + + temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. + var/thermal_protection_flags = get_cold_protection_flags(temperature) + + . = get_thermal_protection(thermal_protection_flags) + . = 1 - . // Invert from 1 = immunity to 0 = immunity. + + // Doing it this way makes multiplicative stacking not get out of hand, so two modifiers that give 0.5 protection will be combined to 0.75 in the end. + for(var/datum/modifier/M as anything in modifiers) + if(!isnull(M.cold_protection)) + // Invert the modifier values so they align with the current working value. + . *= 1 - M.cold_protection + + // Code that calls this expects 1 = immunity so we need to invert again. + . = 1 - . + . = min(., 1.0) + +/mob/living/carbon/human/proc/get_thermal_protection(var/flags) + .=0 + if(flags) + if(flags & HEAD) + . += THERMAL_PROTECTION_HEAD + if(flags & UPPER_TORSO) + . += THERMAL_PROTECTION_UPPER_TORSO + if(flags & LOWER_TORSO) + . += THERMAL_PROTECTION_LOWER_TORSO + if(flags & LEG_LEFT) + . += THERMAL_PROTECTION_LEG_LEFT + if(flags & LEG_RIGHT) + . += THERMAL_PROTECTION_LEG_RIGHT + if(flags & FOOT_LEFT) + . += THERMAL_PROTECTION_FOOT_LEFT + if(flags & FOOT_RIGHT) + . += THERMAL_PROTECTION_FOOT_RIGHT + if(flags & ARM_LEFT) + . += THERMAL_PROTECTION_ARM_LEFT + if(flags & ARM_RIGHT) + . += THERMAL_PROTECTION_ARM_RIGHT + if(flags & HAND_LEFT) + . += THERMAL_PROTECTION_HAND_LEFT + if(flags & HAND_RIGHT) + . += THERMAL_PROTECTION_HAND_RIGHT + return min(1,.) + +/mob/living/carbon/human/handle_chemicals_in_body() + + if(inStasisNow()) + return + + if(reagents) + chem_effects.Cut() + + if(touching) + touching.metabolize() + if(ingested) + ingested.metabolize() + if(bloodstr) + bloodstr.metabolize() + + if(!isSynthetic()) + + var/total_phoronloss = 0 + for(var/obj/item/I in src) + if(I.contaminated) + if(check_belly(I)) continue //VOREStation Edit + if(src.species && src.species.get_bodytype() != "Vox" && src.species.get_bodytype() != "Shadekin") //VOREStation Edit: shadekin + // This is hacky, I'm so sorry. + if(I != l_hand && I != r_hand) //If the item isn't in your hands, you're probably wearing it. Full damage for you. + total_phoronloss += vsc.plc.CONTAMINATION_LOSS + else if(I == l_hand) //If the item is in your hands, but you're wearing protection, you might be alright. + var/l_hand_blocked = 0 + l_hand_blocked = 1-(100-getarmor(BP_L_HAND, "bio"))/100 //This should get a number between 0 and 1 + total_phoronloss += vsc.plc.CONTAMINATION_LOSS * l_hand_blocked + else if(I == r_hand) //If the item is in your hands, but you're wearing protection, you might be alright. + var/r_hand_blocked = 0 + r_hand_blocked = 1-(100-getarmor(BP_R_HAND, "bio"))/100 //This should get a number between 0 and 1 + total_phoronloss += vsc.plc.CONTAMINATION_LOSS * r_hand_blocked + if(total_phoronloss) + if(!(status_flags & GODMODE)) + adjustToxLoss(total_phoronloss) + + if(status_flags & GODMODE) + return 0 //godmode + + if(species.light_dam) + var/light_amount = 0 + if(isturf(loc)) + var/turf/T = loc + light_amount = T.get_lumcount() * 10 + if(light_amount > species.light_dam) //if there's enough light, start dying + take_overall_damage(1,1) + else //heal in the dark + heal_overall_damage(1,1) + + // nutrition decrease + if (nutrition > 0 && stat != DEAD) + var/nutrition_reduction = species.hunger_factor + + for(var/datum/modifier/mod in modifiers) + if(!isnull(mod.metabolism_percent)) + nutrition_reduction *= mod.metabolism_percent + adjust_nutrition(-nutrition_reduction) + + if(noisy == TRUE && nutrition < 250 && prob(10)) //VOREStation edit for hunger noises. + var/sound/growlsound = sound(get_sfx("hunger_sounds")) + var/growlmultiplier = 100 - (nutrition / 250 * 100) + playsound(src, growlsound, vol = growlmultiplier, vary = 1, falloff = 0.1, ignore_walls = TRUE, preference = /datum/client_preference/digestion_noises) + // VOREStation Edit End + + // TODO: stomach and bloodstream organ. + if(!isSynthetic()) + handle_trace_chems() + + updatehealth() + + return //TODO: DEFERRED + +//DO NOT CALL handle_statuses() from this proc, it's called from living/Life() as long as this returns a true value. +/mob/living/carbon/human/handle_regular_status_updates() + if(skip_some_updates()) + return 0 + + if(status_flags & GODMODE) return 0 + + //SSD check, if a logged player is awake put them back to sleep! + if(species.get_ssd(src) && !client && !teleop) + Sleeping(2) + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP + blinded = 1 + silent = 0 + else //ALIVE. LIGHTS ARE ON + updatehealth() //TODO + + if(health <= config.health_threshold_dead || (should_have_organ("brain") && !has_brain())) + death() + blinded = 1 + silent = 0 + return 1 + + //UNCONSCIOUS. NO-ONE IS HOME + if((getOxyLoss() > (species.total_health/2)) || (health <= config.health_threshold_crit)) + Paralyse(3) + + if(hallucination) + if(hallucination >= 20 && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) ) + if(prob(3)) + fake_attack(src) + if(!handling_hal) + spawn handle_hallucinations() //The not boring kind! + if(client && prob(5)) + client.dir = pick(2,4,8) + spawn(rand(20,50)) + client.dir = 1 + + hallucination = max(0, hallucination - 2) + else + for(var/atom/a in hallucinations) + qdel(a) + + //Brain damage from Oxyloss + if(should_have_organ("brain")) + var/brainOxPercent = 0.015 //Default 1.5% of your current oxyloss is applied as brain damage, 50 oxyloss is 1 brain damage + if(CE_STABLE in chem_effects) + brainOxPercent = 0.008 //Halved in effect + if(oxyloss >= (getMaxHealth() * 0.3) && prob(5)) // If oxyloss exceeds 30% of your max health, you can take brain damage. + adjustBrainLoss(brainOxPercent * oxyloss) + + if(halloss >= species.total_health) + to_chat(src, "You're in too much pain to keep going...") + src.visible_message("[src] slumps to the ground, too weak to continue fighting.") + Paralyse(10) + setHalLoss(species.total_health - 1) + + if(paralysis || sleeping) + blinded = 1 + set_stat(UNCONSCIOUS) + animate_tail_reset() + adjustHalLoss(-3) + + if(sleeping) + handle_dreams() + if (mind) + //Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar. + if(client || sleeping > 3) + AdjustSleeping(-1) + throw_alert("asleep", /obj/screen/alert/asleep) + if( prob(2) && health && !hal_crit && client ) + spawn(0) + emote("snore") + //CONSCIOUS + else + set_stat(CONSCIOUS) + clear_alert("asleep") + + //Periodically double-check embedded_flag + if(embedded_flag && !(life_tick % 10)) + var/list/E + E = get_visible_implants(0) + if(!E.len) + embedded_flag = 0 + + //Eyes + //Check rig first because it's two-check and other checks will override it. + if(istype(back,/obj/item/weapon/rig)) + var/obj/item/weapon/rig/O = back + if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) + if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) + blinded = 1 + + // Check everything else. + + //Periodically double-check embedded_flag + if(embedded_flag && !(life_tick % 10)) + if(!embedded_needs_process()) + embedded_flag = 0 + //Vision + var/obj/item/organ/vision + if(species.vision_organ) + vision = internal_organs_by_name[species.vision_organ] + + if(!species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. + SetBlinded(0) + blinded = 0 + eye_blurry = 0 + clear_alert("blind") + else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. + SetBlinded(1) + blinded = 1 + eye_blurry = 1 + throw_alert("blind", /obj/screen/alert/blind) + else //You have the requisite organs + if(sdisabilities & BLIND) // Disabled-blind, doesn't get better on its own + blinded = 1 + throw_alert("blind", /obj/screen/alert/blind) + else if(eye_blind) // Blindness, heals slowly over time + AdjustBlinded(-1) + blinded = 1 + throw_alert("blind", /obj/screen/alert/blind) + else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster + eye_blurry = max(eye_blurry-3, 0) + blinded = 1 + throw_alert("blind", /obj/screen/alert/blind) + + //blurry sight + if(vision.is_bruised()) // Vision organs impaired? Permablurry. + eye_blurry = 1 + if(eye_blurry) // Blurry eyes heal slowly + eye_blurry = max(eye_blurry-1, 0) + + //Ears + if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own + ear_deaf = max(ear_deaf, 1) + else if(ear_deaf) //deafness, heals slowly over time + ear_deaf = max(ear_deaf-1, 0) + else if(get_ear_protection() >= 2) //resting your ears with earmuffs heals ear damage faster + ear_damage = max(ear_damage-0.15, 0) + ear_deaf = max(ear_deaf, 1) + else if(ear_damage < 25) //ear damage heals slowly under this threshold. otherwise you'll need earmuffs + ear_damage = max(ear_damage-0.05, 0) + + //Resting + if(resting) + dizziness = max(0, dizziness - 15) + jitteriness = max(0, jitteriness - 15) + adjustHalLoss(-3) + else + dizziness = max(0, dizziness - 3) + jitteriness = max(0, jitteriness - 3) + adjustHalLoss(-1) + + if (drowsyness) + drowsyness = max(0, drowsyness - 1) + eye_blurry = max(2, eye_blurry) + if (prob(5)) + Sleeping(1) + Paralyse(5) + + // If you're dirty, your gloves will become dirty, too. + if(gloves && germ_level > gloves.germ_level && prob(10)) + gloves.germ_level += 1 + + return 1 + +/mob/living/carbon/human/set_stat(var/new_stat) + . = ..() + if(. && stat) + update_skin(1) + +/mob/living/carbon/human/handle_regular_hud_updates() + if(hud_updateflag) // update our mob's hud overlays, AKA what others see flaoting above our head + handle_hud_list() + + // now handle what we see on our screen + + if(!client) + return 0 + + ..() + + client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.thermal, global_hud.meson, global_hud.science, global_hud.material, global_hud.whitense) + + if(istype(client.eye,/obj/machinery/camera)) + var/obj/machinery/camera/cam = client.eye + client.screen |= cam.client_huds + + if(stat == DEAD) //Dead + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + if(healths) healths.icon_state = "health7" //DEAD healthmeter + + else if(stat == UNCONSCIOUS && health <= 0) //Crit + //Critical damage passage overlay + var/severity = 0 + switch(health) + if(-20 to -10) severity = 1 + if(-30 to -20) severity = 2 + if(-40 to -30) severity = 3 + if(-50 to -40) severity = 4 + if(-60 to -50) severity = 5 + if(-70 to -60) severity = 6 + if(-80 to -70) severity = 7 + if(-90 to -80) severity = 8 + if(-95 to -90) severity = 9 + if(-INFINITY to -95) severity = 10 + overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity) + else //Alive + clear_fullscreen("crit") + //Oxygen damage overlay + if(oxyloss) + var/severity = 0 + switch(oxyloss) + if(10 to 20) severity = 1 + if(20 to 25) severity = 2 + if(25 to 30) severity = 3 + if(30 to 35) severity = 4 + if(35 to 40) severity = 5 + if(40 to 45) severity = 6 + if(45 to INFINITY) severity = 7 + overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity) + else + clear_fullscreen("oxy") + + //Fire and Brute damage overlay (BSSR) + var/hurtdamage = src.getShockBruteLoss() + src.getShockFireLoss() + damageoverlaytemp //Doesn't call the overlay if you can't actually feel it + damageoverlaytemp = 0 // We do this so we can detect if someone hits us or not. + if(hurtdamage) + var/severity = 0 + switch(hurtdamage) + if(10 to 25) severity = 1 + if(25 to 40) severity = 2 + if(40 to 55) severity = 3 + if(55 to 70) severity = 4 + if(70 to 85) severity = 5 + if(85 to INFINITY) severity = 6 + overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) + else + clear_fullscreen("brute") + + if(healths) + if (chem_effects[CE_PAINKILLER] > 100) + healths.icon_state = "health_numb" + else + // Generate a by-limb health display. + var/mutable_appearance/healths_ma = new(healths) + healths_ma.icon_state = "blank" + healths_ma.overlays = null + healths_ma.plane = PLANE_PLAYER_HUD + + var/no_damage = 1 + var/trauma_val = 0 // Used in calculating softcrit/hardcrit indicators. + if(!(species.flags & NO_PAIN)) + trauma_val = max(traumatic_shock,halloss)/species.total_health + var/limb_trauma_val = trauma_val*0.3 + // Collect and apply the images all at once to avoid appearance churn. + var/list/health_images = list() + for(var/obj/item/organ/external/E in organs) + if(no_damage && (E.brute_dam || E.burn_dam)) + no_damage = 0 + health_images += E.get_damage_hud_image(limb_trauma_val) + + // Apply a fire overlay if we're burning. + if(on_fire) + health_images += image('icons/mob/OnFire.dmi',"[get_fire_icon_state()]") + + // Show a general pain/crit indicator if needed. + if(trauma_val) + if(!(species.flags & NO_PAIN)) + if(trauma_val > 0.7) + health_images += image('icons/mob/screen1_health.dmi',"softcrit") + if(trauma_val >= 1) + health_images += image('icons/mob/screen1_health.dmi',"hardcrit") + else if(no_damage) + health_images += image('icons/mob/screen1_health.dmi',"fullhealth") + + healths_ma.add_overlay(health_images) + healths.appearance = healths_ma + + + var/fat_alert = /obj/screen/alert/fat + var/hungry_alert = /obj/screen/alert/hungry + var/starving_alert = /obj/screen/alert/starving + + if(isSynthetic()) + fat_alert = /obj/screen/alert/fat/synth + hungry_alert = /obj/screen/alert/hungry/synth + starving_alert = /obj/screen/alert/starving/synth + //VOREStation Add - Vampire hunger alert + else if(get_species() == SPECIES_CUSTOM) + var/datum/species/custom/C = species + if(/datum/trait/neutral/bloodsucker in C.traits) + fat_alert = /obj/screen/alert/fat/vampire + hungry_alert = /obj/screen/alert/hungry/vampire + starving_alert = /obj/screen/alert/starving/vampire + //VOREStation Add End + + switch(nutrition) + if(450 to INFINITY) + throw_alert("nutrition", fat_alert) + // if(350 to 450) + // if(250 to 350) // Alternative more-detailed tiers, not used. + if(250 to 450) + clear_alert("nutrition") + if(150 to 250) + throw_alert("nutrition", hungry_alert) + else + throw_alert("nutrition", starving_alert) + + if(blinded) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + throw_alert("blind", /obj/screen/alert/blind) + else + clear_fullscreen("blind") + clear_alert("blind") + + var/apply_nearsighted_overlay = FALSE + if(disabilities & NEARSIGHTED) + apply_nearsighted_overlay = TRUE + + if(glasses) + var/obj/item/clothing/glasses/G = glasses + if(G.prescription) + apply_nearsighted_overlay = FALSE + + if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) // VOREStation Edit - NIF + apply_nearsighted_overlay = FALSE + + set_fullscreen(apply_nearsighted_overlay, "nearsighted", /obj/screen/fullscreen/impaired, 1) + + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + if(druggy) + throw_alert("high", /obj/screen/alert/high) + else + clear_alert("high") + + if(!isbelly(loc)) //VOREStation Add - Belly fullscreens safety + clear_fullscreen("belly") + clear_fullscreen("belly2") + clear_fullscreen("belly3") + clear_fullscreen("belly4") + + if(config.welder_vision) + var/found_welder + if(species.short_sighted) + found_welder = 1 + else + if(istype(glasses, /obj/item/clothing/glasses/welding)) + var/obj/item/clothing/glasses/welding/O = glasses + if(!O.up) + found_welder = 1 + if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 //VOREStation Add - NIF + if(istype(glasses, /obj/item/clothing/glasses/sunglasses/thinblindfold)) + found_welder = 1 + if(!found_welder && istype(head, /obj/item/clothing/head/welding)) + var/obj/item/clothing/head/welding/O = head + if(!O.up) + found_welder = 1 + if(!found_welder && istype(back, /obj/item/weapon/rig)) + var/obj/item/weapon/rig/O = back + if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) + if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) + found_welder = 1 + if(absorbed) found_welder = 1 //VOREStation Code + if(found_welder) + client.screen |= global_hud.darkMask + +/mob/living/carbon/human/reset_view(atom/A) + ..() + if(machine_visual && machine_visual != A) + machine_visual.remove_visual(src) + +/mob/living/carbon/human/handle_vision() + if(stat == DEAD) + sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF + see_in_dark = 8 + if(client) + if(client.view != world.view) // If mob dies while zoomed in with device, unzoom them. + for(var/obj/item/item in contents) + if(item.zoom) + item.zoom() + break + + else //We aren't dead + sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default + + // Do this early so certain stuff gets turned off before vision is assigned. + var/area/A = get_area(src) + if(A?.no_spoilers) + disable_spoiler_vision() + + if(XRAY in mutations) + sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + + if(seer==1) + var/obj/effect/rune/R = locate() in loc + if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"]) + see_invisible = SEE_INVISIBLE_CULT + else + see_invisible = see_invisible_default + seer = 0 + + if(!seedarkness) + sight = species.get_vision_flags(src) + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_NOLIGHTING + + else + sight = species.get_vision_flags(src) + see_in_dark = species.darksight + see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default + + var/glasses_processed = 0 + var/obj/item/weapon/rig/rig = get_rig() + if(istype(rig) && rig.visor && !looking_elsewhere) + if(!rig.helmet || (head && rig.helmet == head)) + if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) + glasses_processed = process_glasses(rig.visor.vision.glasses) + + if(glasses && !glasses_processed && !looking_elsewhere) + glasses_processed = process_glasses(glasses) + if(XRAY in mutations) + sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.vision_flags)) + sight |= M.vision_flags + + if(!glasses_processed && nif) + var/datum/nifsoft/vision_soft + for(var/datum/nifsoft/NS in nif.nifsofts) + if(NS.vision_exclusive && NS.active) + vision_soft = NS + break + if(vision_soft) + glasses_processed = process_nifsoft_vision(vision_soft) //not really glasses but equitable + + if(!glasses_processed && (species.get_vision_flags(src) > 0)) + sight |= species.get_vision_flags(src) + if(!seer && !glasses_processed && seedarkness) + see_invisible = see_invisible_default + + if(machine) + var/viewflags = machine.check_eye(src) + if(viewflags < 0) + reset_view(null, 0) + else if(viewflags && !looking_elsewhere) + sight |= viewflags + else + machine.apply_visual(src) + else if(eyeobj) + if(eyeobj.owner != src) + + reset_view(null) + else + var/isRemoteObserve = 0 + if((mRemote in mutations) && remoteview_target) + if(remoteview_target.stat==CONSCIOUS) + isRemoteObserve = 1 + if(!isRemoteObserve && client && !client.adminobs) + remoteview_target = null + reset_view(null, 0) + return 1 + +/mob/living/carbon/human/proc/process_glasses(var/obj/item/clothing/glasses/G) + . = FALSE + if(G && G.active) + if(G.darkness_view) + see_in_dark += G.darkness_view + . = TRUE + if(G.overlay && client) + client.screen |= G.overlay + if(G.vision_flags) + sight |= G.vision_flags + . = TRUE + if(istype(G,/obj/item/clothing/glasses/night) && !seer) + see_invisible = SEE_INVISIBLE_MINIMUM + + if(G.see_invisible >= 0) + see_invisible = G.see_invisible + . = TRUE + else if(!druggy && !seer) + see_invisible = see_invisible_default + +/mob/living/carbon/human/proc/process_nifsoft_vision(var/datum/nifsoft/NS) + . = FALSE + if(NS && NS.active) + if(NS.darkness_view) + see_in_dark += NS.darkness_view + . = TRUE + if(NS.vision_flags_mob) + sight |= NS.vision_flags_mob + . = TRUE + +/mob/living/carbon/human/handle_random_events() + if(inStasisNow()) + return + + // Puke if toxloss is too high + if(!stat && !isbelly(loc)) + if (getToxLoss() >= 30 && isSynthetic()) + if(!confused) + if(prob(5)) + to_chat(src, "You lose directional control!") + Confuse(10) + if (getToxLoss() >= 45 && !isSynthetic()) + spawn vomit() + + + //0.1% chance of playing a scary sound to someone who's in complete darkness + if(isturf(loc) && rand(1,1000) == 1) + var/turf/T = loc + if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) + //VOREStation Add Start + if(text2num(time2text(world.timeofday, "MM")) == 4) + if(text2num(time2text(world.timeofday, "DD")) == 1) + playsound_local(src,pick(scawwySownds),50, 0) + return + //VOREStation Add End + playsound_local(src,pick(scarySounds),50, 1, -1) + +/mob/living/carbon/human/handle_stomach() + spawn(0) + for(var/mob/living/M in stomach_contents) + if(M.loc != src) + stomach_contents.Remove(M) + continue + if(istype(M, /mob/living/carbon) && stat != 2) + if(M.stat == 2) + M.death(1) + stomach_contents.Remove(M) + qdel(M) + continue + if(air_master.current_cycle%3==1) + if(!(M.status_flags & GODMODE)) + M.adjustBruteLoss(5) + adjust_nutrition(10) + +/mob/living/carbon/human/proc/handle_changeling() + if(mind && mind.changeling) + mind.changeling.regenerate() + if(hud_used) + ling_chem_display.invisibility = 0 +// ling_chem_display.maptext = "
                    [round(mind.changeling.chem_charges)]
                    " + switch(mind.changeling.chem_storage) + if(1 to 50) + switch(mind.changeling.chem_charges) + if(0 to 9) + ling_chem_display.icon_state = "ling_chems0" + if(10 to 19) + ling_chem_display.icon_state = "ling_chems10" + if(20 to 29) + ling_chem_display.icon_state = "ling_chems20" + if(30 to 39) + ling_chem_display.icon_state = "ling_chems30" + if(40 to 49) + ling_chem_display.icon_state = "ling_chems40" + if(50) + ling_chem_display.icon_state = "ling_chems50" + if(51 to 80) //This is a crappy way of checking for engorged sacs... + switch(mind.changeling.chem_charges) + if(0 to 9) + ling_chem_display.icon_state = "ling_chems0e" + if(10 to 19) + ling_chem_display.icon_state = "ling_chems10e" + if(20 to 29) + ling_chem_display.icon_state = "ling_chems20e" + if(30 to 39) + ling_chem_display.icon_state = "ling_chems30e" + if(40 to 49) + ling_chem_display.icon_state = "ling_chems40e" + if(50 to 59) + ling_chem_display.icon_state = "ling_chems50e" + if(60 to 69) + ling_chem_display.icon_state = "ling_chems60e" + if(70 to 79) + ling_chem_display.icon_state = "ling_chems70e" + if(80) + ling_chem_display.icon_state = "ling_chems80e" + else + if(mind && hud_used) + ling_chem_display.invisibility = 101 + +/mob/living/carbon/human/handle_shock() + ..() + if(status_flags & GODMODE) return 0 //godmode + if(!can_feel_pain()) return + + if(health < config.health_threshold_softcrit)// health 0 makes you immediately collapse + shock_stage = max(shock_stage, 61) + + if(traumatic_shock >= 80) + shock_stage += 1 + else if(health < config.health_threshold_softcrit) + shock_stage = max(shock_stage, 61) + else + shock_stage = min(shock_stage, 160) + shock_stage = max(shock_stage-1, 0) + + if(stat) + return 0 + + if(shock_stage == 10) + if(traumatic_shock >= 80) + custom_pain("[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!", 40) + + if(shock_stage >= 30) + if(shock_stage == 30 && !isbelly(loc)) //VOREStation Edit + custom_emote(VISIBLE_MESSAGE, "is having trouble keeping their eyes open.") + eye_blurry = max(2, eye_blurry) + if(traumatic_shock >= 80) + stuttering = max(stuttering, 5) + + + if(shock_stage == 40) + if(traumatic_shock >= 80) + to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") + + if (shock_stage >= 60) + if(shock_stage == 60 && !isbelly(loc)) //VOREStation Edit + custom_emote(VISIBLE_MESSAGE, "'s body becomes limp.") + if (prob(2)) + if(traumatic_shock >= 80) + to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") + Weaken(20) + + if(shock_stage >= 80) + if (prob(5)) + if(traumatic_shock >= 80) + to_chat(src, "[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!") + Weaken(20) + + if(shock_stage >= 120) + if (prob(2)) + if(traumatic_shock >= 80) + to_chat(src, "[pick("You black out", "You feel like you could die any moment now", "You are about to lose consciousness")]!") + Paralyse(5) + + if(shock_stage == 150) + if(!isbelly(loc)) //VOREStation Edit + custom_emote(VISIBLE_MESSAGE, "can no longer stand, collapsing!") + Weaken(20) + + if(shock_stage >= 150) + Weaken(20) + +/mob/living/carbon/human/proc/handle_pulse() + if(life_tick % 5) return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) + + var/temp = PULSE_NORM + + var/brain_modifier = 1 + + var/modifier_shift = 0 + var/modifier_set + + if(modifiers && modifiers.len) + for(var/datum/modifier/mod in modifiers) + if(isnull(modifier_set) && !isnull(mod.pulse_set_level)) + modifier_set = round(mod.pulse_set_level) // Should be a whole number, but let's not take chances. + else if(mod.pulse_set_level > modifier_set) + modifier_set = round(mod.pulse_set_level) + + modifier_set = max(0, modifier_set) // No setting to negatives. + + if(mod.pulse_modifier) + modifier_shift += mod.pulse_modifier + + modifier_shift = round(modifier_shift) + + if(!internal_organs_by_name[O_HEART]) + temp = PULSE_NONE + if(!isnull(modifier_set)) + temp = modifier_set + return temp //No blood, no pulse. + + if(stat == DEAD) + temp = PULSE_NONE + if(!isnull(modifier_set)) + temp = modifier_set + return temp //that's it, you're dead, nothing can influence your pulse, aside from outside means. + + var/obj/item/organ/internal/heart/Pump = internal_organs_by_name[O_HEART] + + var/obj/item/organ/internal/brain/Control = internal_organs_by_name[O_BRAIN] + + if(Control) + brain_modifier = Control.get_control_efficiency() + + if(brain_modifier <= 0.7 && brain_modifier >= 0.4) // 70%-40% control, things start going weird as the brain is failing. + brain_modifier = rand(5, 15) / 10 + + if(Pump) + temp += Pump.standard_pulse_level - PULSE_NORM + + if(round(vessel.get_reagent_amount("blood")) <= species.blood_volume*species.blood_level_danger) //how much blood do we have + temp = temp + 3 //not enough :( + + if(status_flags & FAKEDEATH) + temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds + + if(!isnull(modifier_set)) + temp = modifier_set + + temp = max(0, temp + modifier_shift) // No negative pulses. + + if(Pump) + for(var/datum/reagent/R in reagents.reagent_list) + if(R.id in bradycardics) + if(temp <= Pump.standard_pulse_level + 3 && temp >= Pump.standard_pulse_level) + temp-- + if(R.id in tachycardics) + if(temp <= Pump.standard_pulse_level + 1 && temp >= PULSE_NONE) + temp++ + if(R.id in heartstopper) //To avoid using fakedeath + temp = PULSE_NONE + if(R.id in cheartstopper) //Conditional heart-stoppage + if(R.volume >= R.overdose) + temp = PULSE_NONE + return temp * brain_modifier + //handles different chems' influence on pulse + for(var/datum/reagent/R in reagents.reagent_list) + if(R.id in bradycardics) + if(temp <= PULSE_THREADY && temp >= PULSE_NORM) + temp-- + if(R.id in tachycardics) + if(temp <= PULSE_FAST && temp >= PULSE_NONE) + temp++ + if(R.id in heartstopper) //To avoid using fakedeath + temp = PULSE_NONE + if(R.id in cheartstopper) //Conditional heart-stoppage + if(R.volume >= R.overdose) + temp = PULSE_NONE + + return max(0, round(temp * brain_modifier)) + +/mob/living/carbon/human/proc/handle_heartbeat() + if(pulse == PULSE_NONE) + return + + var/obj/item/organ/internal/heart/H = internal_organs_by_name[O_HEART] + + if(!H || (H.robotic >= ORGAN_ROBOT)) + return + + if(pulse >= PULSE_2FAST || shock_stage >= 10 || (istype(get_turf(src), /turf/space) && is_preference_enabled(/datum/client_preference/play_ambiance))) + //PULSE_THREADY - maximum value for pulse, currently it 5. + //High pulse value corresponds to a fast rate of heartbeat. + //Divided by 2, otherwise it is too slow. + var/rate = (PULSE_THREADY - pulse)/2 + + if(heartbeat >= rate) + heartbeat = 0 + src << sound('sound/effects/singlebeat.ogg',0,0,0,50) + else + heartbeat++ + +/* + Called by life(), instead of having the individual hud items update icons each tick and check for status changes + we only set those statuses and icons upon changes. Then those HUD items will simply add those pre-made images. + This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. +*/ +/mob/living/carbon/human/proc/handle_hud_list() + if (BITTEST(hud_updateflag, HEALTH_HUD)) + var/image/holder = grab_hud(HEALTH_HUD) + if(stat == DEAD) + holder.icon_state = "-100" // X_X + else + holder.icon_state = RoundHealth((health-config.health_threshold_crit)/(getMaxHealth()-config.health_threshold_crit)*100) + apply_hud(HEALTH_HUD, holder) + + if (BITTEST(hud_updateflag, LIFE_HUD)) + var/image/holder = grab_hud(LIFE_HUD) + if(isSynthetic()) + holder.icon_state = "hudrobo" + else if(stat == DEAD) + holder.icon_state = "huddead" + else + holder.icon_state = "hudhealthy" + apply_hud(LIFE_HUD, holder) + + if (BITTEST(hud_updateflag, STATUS_HUD)) + var/foundVirus = 0 + for (var/ID in virus2) + if (ID in virusDB) + foundVirus = 1 + break + + var/image/holder = grab_hud(STATUS_HUD) + var/image/holder2 = grab_hud(STATUS_HUD_OOC) + if (isSynthetic()) + holder.icon_state = "hudrobo" + else if(stat == DEAD) + holder.icon_state = "huddead" + holder2.icon_state = "huddead" + else if(foundVirus) + holder.icon_state = "hudill" + else if(has_brain_worms()) + var/mob/living/simple_mob/animal/borer/B = has_brain_worms() + if(B.controlling) + holder.icon_state = "hudbrainworm" + else + holder.icon_state = "hudhealthy" + holder2.icon_state = "hudbrainworm" + else + holder.icon_state = "hudhealthy" + if(virus2.len) + holder2.icon_state = "hudill" + else + holder2.icon_state = "hudhealthy" + + apply_hud(STATUS_HUD, holder) + apply_hud(STATUS_HUD_OOC, holder2) + + if (BITTEST(hud_updateflag, ID_HUD)) + var/image/holder = grab_hud(ID_HUD) + if(wear_id) + var/obj/item/weapon/card/id/I = wear_id.GetID() + if(I) + holder.icon_state = "hud[ckey(I.GetJobName())]" + else + holder.icon_state = "hudunknown" + else + holder.icon_state = "hudunknown" + + apply_hud(ID_HUD, holder) + + if (BITTEST(hud_updateflag, WANTED_HUD)) + var/image/holder = grab_hud(WANTED_HUD) + holder.icon_state = "hudblank" + var/perpname = name + if(wear_id) + var/obj/item/weapon/card/id/I = wear_id.GetID() + if(I) + perpname = I.registered_name + + for(var/datum/data/record/E in data_core.general) + if(E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) + holder.icon_state = "hudwanted" + break + else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Incarcerated")) + holder.icon_state = "hudprisoner" + break + else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Parolled")) + holder.icon_state = "hudparolled" + break + else if((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "Released")) + holder.icon_state = "hudreleased" + break + + apply_hud(WANTED_HUD, holder) + + if ( BITTEST(hud_updateflag, IMPLOYAL_HUD) \ + || BITTEST(hud_updateflag, IMPCHEM_HUD) \ + || BITTEST(hud_updateflag, IMPTRACK_HUD)) + + var/image/holder1 = grab_hud(IMPTRACK_HUD) + var/image/holder2 = grab_hud(IMPLOYAL_HUD) + var/image/holder3 = grab_hud(IMPCHEM_HUD) + + holder1.icon_state = "hudblank" + holder2.icon_state = "hudblank" + holder3.icon_state = "hudblank" + + for(var/obj/item/weapon/implant/I in src) + if(I.implanted) + if(!I.malfunction) + if(istype(I,/obj/item/weapon/implant/tracking)) + holder1.icon_state = "hud_imp_tracking" + if(istype(I,/obj/item/weapon/implant/loyalty)) + holder2.icon_state = "hud_imp_loyal" + if(istype(I,/obj/item/weapon/implant/chem)) + holder3.icon_state = "hud_imp_chem" + + apply_hud(IMPTRACK_HUD, holder1) + apply_hud(IMPLOYAL_HUD, holder2) + apply_hud(IMPCHEM_HUD, holder3) + + if (BITTEST(hud_updateflag, SPECIALROLE_HUD)) + var/image/holder = grab_hud(SPECIALROLE_HUD) + holder.icon_state = "hudblank" + if(mind && mind.special_role) + if(hud_icon_reference[mind.special_role]) + holder.icon_state = hud_icon_reference[mind.special_role] + else + holder.icon_state = "hudsyndicate" + apply_hud(SPECIALROLE_HUD, holder) + + attempt_vr(src,"handle_hud_list_vr",list()) //VOREStation Add - Custom HUDs. + + hud_updateflag = 0 + +/mob/living/carbon/human/handle_fire() + if(..()) + return + + var/thermal_protection = get_heat_protection(fire_stacks * 1500) // Arbitrary but below firesuit max temp when below 20 stacks. + + if(thermal_protection == 1) // Immune. + return + else + var/fire_temp_add = (BODYTEMP_HEATING_MAX + (fire_stacks * 15)) * (1-thermal_protection) + //This is to prevent humans from heating up indefinitely. A human being on fire (fat burns at 250C) can't magically + // increase your body temperature beyond 250C, but it's possible something else (atmos) has heated us up beyond it, + // so don't worry about the firestacks at that point. Really, we should be cooling the room down, because it has + // to expend energy to heat our body up! But let's not worry about that. + + // This whole section above is ABSOLUTELY STUPID and makes no sense and this would prevent too-high-heat from even being able to hurt someone. No. We will heat up for as long as needed. + //if((bodytemperature + fire_temp_add) > HUMAN_COMBUSTION_TEMP) + // return + + bodytemperature += fire_temp_add + +/mob/living/carbon/human/rejuvenate() + restore_blood() + shock_stage = 0 + traumatic_shock = 0 + ..() + +/mob/living/carbon/human/proc/handle_defib_timer() + if(!should_have_organ(O_BRAIN)) + return // No brain. + + var/obj/item/organ/internal/brain/brain = internal_organs_by_name[O_BRAIN] + if(!brain) + return // Still no brain. + + brain.tick_defib_timer() + +#undef HUMAN_MAX_OXYLOSS +#undef HUMAN_CRIT_MAX_OXYLOSS diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm index 839385ac62d..d3afee61188 100644 --- a/code/modules/mob/living/carbon/human/login.dm +++ b/code/modules/mob/living/carbon/human/login.dm @@ -1,31 +1,31 @@ -// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # -#define AUTOHISS_OFF 0 -#define AUTOHISS_BASIC 1 -#define AUTOHISS_FULL 2 -// VOREStation Add End - -/mob/living/carbon/human/Login() - ..() - update_hud() - // VOREStation Add - if(client.prefs) // Safety, just in case so we don't runtime. - if(!client.prefs.autohiss) - client.autohiss_mode = AUTOHISS_FULL - else - switch(client.prefs.autohiss) - if("Full") - client.autohiss_mode = AUTOHISS_FULL - if("Basic") - client.autohiss_mode = AUTOHISS_BASIC - if("Off") - client.autohiss_mode = AUTOHISS_OFF - consider_birthday() - // VOREStation Add - if(species) species.handle_login_special(src) - return - -// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # -#undef AUTOHISS_OFF -#undef AUTOHISS_BASIC -#undef AUTOHISS_FULL -// VOREStation Add End +// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # +#define AUTOHISS_OFF 0 +#define AUTOHISS_BASIC 1 +#define AUTOHISS_FULL 2 +// VOREStation Add End + +/mob/living/carbon/human/Login() + ..() + update_hud() + // VOREStation Add + if(client.prefs) // Safety, just in case so we don't runtime. + if(!client.prefs.autohiss) + client.autohiss_mode = AUTOHISS_FULL + else + switch(client.prefs.autohiss) + if("Full") + client.autohiss_mode = AUTOHISS_FULL + if("Basic") + client.autohiss_mode = AUTOHISS_BASIC + if("Off") + client.autohiss_mode = AUTOHISS_OFF + consider_birthday() + // VOREStation Add + if(species) species.handle_login_special(src) + return + +// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than # +#undef AUTOHISS_OFF +#undef AUTOHISS_BASIC +#undef AUTOHISS_FULL +// VOREStation Add End diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index afd50763dc1..02cb9267a96 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,214 +1,214 @@ -/mob/living/carbon/human/GetAltName() - if(ability_flags & AB_PHASE_SHIFTED) - return "" - if(name != GetVoice()) - return " (as [get_id_name("Unknown")])" - - return ..() - -/mob/living/carbon/human/proc/forcesay(list/append) - if(stat == CONSCIOUS) - if(client) - var/virgin = 1 //has the text been modified yet? - var/temp = winget(client, "input", "text") - if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //case sensitive means - - temp = replacetext(temp, ";", "") //general radio - - if(findtext(trim_left(temp), ":", 6, 7)) //dept radio - temp = copytext(trim_left(temp), 8) - virgin = 0 - - if(virgin) - temp = copytext(trim_left(temp), 6) //normal speech - virgin = 0 - - while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary) - temp = copytext(trim_left(temp), 3) - - if(findtext(temp, "*", 1, 2)) //emotes - return - temp = copytext(trim_left(temp), 1, rand(5,8)) - - var/trimmed = trim_left(temp) - if(length(trimmed)) - if(append) - temp += pick(append) - - say(temp) - winset(client, "input", "text=[null]") - -/mob/living/carbon/human/speech_bubble_appearance() - var/sounds_synth = FALSE - var/datum/robolimb/robo = isSynthetic() //Will get torso manufacturer - if(robo) - sounds_synth = looksSynthetic() //Based on lifelike robolimb vars - - // Not lifelike and got manufacturer - if(sounds_synth) - return robo.speech_bubble_appearance || "synthetic" - - // Not lifelike synth, might have synth voice box - if(!robo) - var/obj/item/organ/internal/V = internal_organs_by_name[O_VOICE] - if(V?.robotic >= ORGAN_ROBOT) - return "synthetic" - - // Species might have custom one - if(species.speech_bubble_appearance) - return species.speech_bubble_appearance - - // NORMIE - return ..() - -/mob/living/carbon/human/say_understands(var/mob/other, var/datum/language/speaking = null) - if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak. - return TRUE - - if(species.can_understand(other)) - return TRUE - - //These only pertain to common. Languages are handled by mob/say_understands() - if(!speaking) - if(istype(other, /mob/living/carbon/alien/diona)) - if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. - return TRUE - if(issilicon(other)) - return TRUE - if(isbrain(other)) - return TRUE - if(isslime(other)) - return TRUE - - return ..() - -/mob/living/carbon/human/GetVoice() - var/voice_sub - if(istype(get_rig(),/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = get_rig() - // todo: fix this shit - if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) - voice_sub = rig.speech.voice_holder.voice - if(!voice_sub) // If the rig has a voice changer, then we use that. Otherwise, use this - for(var/obj/item/gear in list(wear_mask,wear_suit,head)) - if(!gear) - continue - var/obj/item/voice_changer/changer = locate() in gear - if(changer && changer.active) - if(changer.voice) - voice_sub = changer.voice - else - voice_sub = get_id_name() - if(voice_sub) - return voice_sub - if(mind && mind.changeling && mind.changeling.mimicing) - return mind.changeling.mimicing - if(GetSpecialVoice()) - return GetSpecialVoice() - return real_name - -/mob/living/carbon/human/proc/SetSpecialVoice(var/new_voice) - if(new_voice) - special_voice = new_voice - return - -/mob/living/carbon/human/proc/UnsetSpecialVoice() - special_voice = "" - return - -/mob/living/carbon/human/proc/GetSpecialVoice() - return special_voice - -/mob/living/carbon/human/handle_speech_problems(var/list/message_data) - if(silent || (sdisabilities & MUTE)) - message_data[1] = "" - . = 1 - - else if(istype(wear_mask, /obj/item/clothing/mask)) - var/obj/item/clothing/mask/M = wear_mask - if(M.voicechange) - message_data[1] = pick(M.say_messages) - message_data[2] = pick(M.say_verbs) - . = 1 - - else if(CE_SPEEDBOOST in chem_effects || is_jittery) // motor mouth - // Despite trying to url/html decode these, byond is just being bad and I dunno. - var/static/regex/speedboost_initial = new (@"&[a-z]{2,5};|&#\d{2};","g") - // Not herestring because bad vs code syntax highlight panics at apostrophe - var/static/regex/speedboost_main = new ("\[ ',!?.;\]","g") - for(var/datum/multilingual_say_piece/S in message_data[1]) - S.message = speedboost_initial.Replace(S.message, "") - S.message = speedboost_main.Replace(S.message, "") - . = 1 - else - . = ..(message_data) - -/mob/living/carbon/human/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - switch(message_mode) - if("intercom") - if(!restrained()) - for(var/obj/item/device/radio/intercom/I in view(1)) - I.talk_into(src, message_pieces, null, verb) - I.add_fingerprint(src) - used_radios += I - if("headset") - var/obj/item/device/radio/R = null - if(isradio(l_ear)) - R = l_ear - if(R.talk_into(src, message_pieces, null, verb)) - used_radios += R - return - - if(isradio(r_ear)) - R = r_ear - if(R.talk_into(src, message_pieces, null, verb)) - used_radios += R - return - if("right ear") - var/obj/item/device/radio/R = null - if(isradio(r_ear)) - R = r_ear - if(isradio(r_hand)) - R = r_hand - if(istype(R)) - if(R.talk_into(src, message_pieces, null, verb)) - used_radios += R - if("left ear") - var/obj/item/device/radio/R = null - if(isradio(l_ear)) - R = l_ear - if(isradio(l_hand)) - R = l_hand - if(istype(R)) - if(R.talk_into(src, message_pieces, null, verb)) - used_radios += R - else - if(message_mode) - if(isradio(l_ear)) - if(l_ear.talk_into(src, message_pieces, message_mode, verb)) - used_radios += l_ear - return - - if(isradio(r_ear)) - if(r_ear.talk_into(src, message_pieces, message_mode, verb)) - used_radios += r_ear - -/mob/living/carbon/human/handle_speech_sound() - var/list/returns[2] - if(species.speech_sounds && species.speech_sounds.len && prob(species.speech_chance)) //VOREStation Edit: Sanity Check - returns[1] = sound(pick(species.speech_sounds)) - returns[2] = 50 - return returns - -/mob/living/carbon/human/binarycheck() - . = FALSE - var/obj/item/device/radio/headset/R = null - if(istype(l_ear, /obj/item/device/radio/headset)) - R = l_ear - if(R.translate_binary) - . = TRUE - - if(istype(r_ear, /obj/item/device/radio/headset)) - R = r_ear - if(R.translate_binary) - . = TRUE +/mob/living/carbon/human/GetAltName() + if(ability_flags & AB_PHASE_SHIFTED) + return "" + if(name != GetVoice()) + return " (as [get_id_name("Unknown")])" + + return ..() + +/mob/living/carbon/human/proc/forcesay(list/append) + if(stat == CONSCIOUS) + if(client) + var/virgin = 1 //has the text been modified yet? + var/temp = winget(client, "input", "text") + if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //case sensitive means + + temp = replacetext(temp, ";", "") //general radio + + if(findtext(trim_left(temp), ":", 6, 7)) //dept radio + temp = copytext(trim_left(temp), 8) + virgin = 0 + + if(virgin) + temp = copytext(trim_left(temp), 6) //normal speech + virgin = 0 + + while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary) + temp = copytext(trim_left(temp), 3) + + if(findtext(temp, "*", 1, 2)) //emotes + return + temp = copytext(trim_left(temp), 1, rand(5,8)) + + var/trimmed = trim_left(temp) + if(length(trimmed)) + if(append) + temp += pick(append) + + say(temp) + winset(client, "input", "text=[null]") + +/mob/living/carbon/human/speech_bubble_appearance() + var/sounds_synth = FALSE + var/datum/robolimb/robo = isSynthetic() //Will get torso manufacturer + if(robo) + sounds_synth = looksSynthetic() //Based on lifelike robolimb vars + + // Not lifelike and got manufacturer + if(sounds_synth) + return robo.speech_bubble_appearance || "synthetic" + + // Not lifelike synth, might have synth voice box + if(!robo) + var/obj/item/organ/internal/V = internal_organs_by_name[O_VOICE] + if(V?.robotic >= ORGAN_ROBOT) + return "synthetic" + + // Species might have custom one + if(species.speech_bubble_appearance) + return species.speech_bubble_appearance + + // NORMIE + return ..() + +/mob/living/carbon/human/say_understands(var/mob/other, var/datum/language/speaking = null) + if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak. + return TRUE + + if(species.can_understand(other)) + return TRUE + + //These only pertain to common. Languages are handled by mob/say_understands() + if(!speaking) + if(istype(other, /mob/living/carbon/alien/diona)) + if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. + return TRUE + if(issilicon(other)) + return TRUE + if(isbrain(other)) + return TRUE + if(isslime(other)) + return TRUE + + return ..() + +/mob/living/carbon/human/GetVoice() + var/voice_sub + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() + // todo: fix this shit + if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) + voice_sub = rig.speech.voice_holder.voice + if(!voice_sub) // If the rig has a voice changer, then we use that. Otherwise, use this + for(var/obj/item/gear in list(wear_mask,wear_suit,head)) + if(!gear) + continue + var/obj/item/voice_changer/changer = locate() in gear + if(changer && changer.active) + if(changer.voice) + voice_sub = changer.voice + else + voice_sub = get_id_name() + if(voice_sub) + return voice_sub + if(mind && mind.changeling && mind.changeling.mimicing) + return mind.changeling.mimicing + if(GetSpecialVoice()) + return GetSpecialVoice() + return real_name + +/mob/living/carbon/human/proc/SetSpecialVoice(var/new_voice) + if(new_voice) + special_voice = new_voice + return + +/mob/living/carbon/human/proc/UnsetSpecialVoice() + special_voice = "" + return + +/mob/living/carbon/human/proc/GetSpecialVoice() + return special_voice + +/mob/living/carbon/human/handle_speech_problems(var/list/message_data) + if(silent || (sdisabilities & MUTE)) + message_data[1] = "" + . = 1 + + else if(istype(wear_mask, /obj/item/clothing/mask)) + var/obj/item/clothing/mask/M = wear_mask + if(M.voicechange) + message_data[1] = pick(M.say_messages) + message_data[2] = pick(M.say_verbs) + . = 1 + + else if(CE_SPEEDBOOST in chem_effects || is_jittery) // motor mouth + // Despite trying to url/html decode these, byond is just being bad and I dunno. + var/static/regex/speedboost_initial = new (@"&[a-z]{2,5};|&#\d{2};","g") + // Not herestring because bad vs code syntax highlight panics at apostrophe + var/static/regex/speedboost_main = new ("\[ ',!?.;\]","g") + for(var/datum/multilingual_say_piece/S in message_data[1]) + S.message = speedboost_initial.Replace(S.message, "") + S.message = speedboost_main.Replace(S.message, "") + . = 1 + else + . = ..(message_data) + +/mob/living/carbon/human/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + switch(message_mode) + if("intercom") + if(!restrained()) + for(var/obj/item/device/radio/intercom/I in view(1)) + I.talk_into(src, message_pieces, null, verb) + I.add_fingerprint(src) + used_radios += I + if("headset") + var/obj/item/device/radio/R = null + if(isradio(l_ear)) + R = l_ear + if(R.talk_into(src, message_pieces, null, verb)) + used_radios += R + return + + if(isradio(r_ear)) + R = r_ear + if(R.talk_into(src, message_pieces, null, verb)) + used_radios += R + return + if("right ear") + var/obj/item/device/radio/R = null + if(isradio(r_ear)) + R = r_ear + if(isradio(r_hand)) + R = r_hand + if(istype(R)) + if(R.talk_into(src, message_pieces, null, verb)) + used_radios += R + if("left ear") + var/obj/item/device/radio/R = null + if(isradio(l_ear)) + R = l_ear + if(isradio(l_hand)) + R = l_hand + if(istype(R)) + if(R.talk_into(src, message_pieces, null, verb)) + used_radios += R + else + if(message_mode) + if(isradio(l_ear)) + if(l_ear.talk_into(src, message_pieces, message_mode, verb)) + used_radios += l_ear + return + + if(isradio(r_ear)) + if(r_ear.talk_into(src, message_pieces, message_mode, verb)) + used_radios += r_ear + +/mob/living/carbon/human/handle_speech_sound() + var/list/returns[2] + if(species.speech_sounds && species.speech_sounds.len && prob(species.speech_chance)) //VOREStation Edit: Sanity Check + returns[1] = sound(pick(species.speech_sounds)) + returns[2] = 50 + return returns + +/mob/living/carbon/human/binarycheck() + . = FALSE + var/obj/item/device/radio/headset/R = null + if(istype(l_ear, /obj/item/device/radio/headset)) + R = l_ear + if(R.translate_binary) + . = TRUE + + if(istype(r_ear, /obj/item/device/radio/headset)) + R = r_ear + if(R.translate_binary) + . = TRUE diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm index e64a4bf9385..eed09b56c52 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm @@ -121,7 +121,7 @@ var/mob/living/target = pick(potentials) if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) - to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") ability_flags &= ~AB_PHASE_SHIFTING diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index ca27c15f166..89ddd74ff88 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -61,6 +61,9 @@ var/min_age = 17 var/max_age = 70 + var/icodigi = 'icons/mob/human_races/r_digi.dmi' + var/digi_allowed = FALSE + // Language/culture vars. var/default_language = LANGUAGE_GALCOM // Default language is used when 'say' is used without modifiers. var/language = LANGUAGE_GALCOM // Default racial language, if any. @@ -134,6 +137,7 @@ var/poison_type = "phoron" // Poisonous air. var/exhale_type = "carbon_dioxide" // Exhaled gas type. var/water_breather = FALSE + var/bad_swimmer = FALSE var/body_temperature = 310.15 // Species will try to stabilize at this temperature. (also affects temperature processing) @@ -378,15 +382,17 @@ if((organ in H.organs) || (organ in H.internal_organs)) qdel(organ) - if(H.organs) H.organs.Cut() - if(H.internal_organs) H.internal_organs.Cut() - if(H.organs_by_name) H.organs_by_name.Cut() - if(H.internal_organs_by_name) H.internal_organs_by_name.Cut() + if(H.organs) H.organs.Cut() + if(H.internal_organs) H.internal_organs.Cut() + if(H.organs_by_name) H.organs_by_name.Cut() + if(H.internal_organs_by_name) H.internal_organs_by_name.Cut() + if(H.bad_external_organs) H.bad_external_organs.Cut() H.organs = list() H.internal_organs = list() H.organs_by_name = list() H.internal_organs_by_name = list() + H.bad_external_organs = list() for(var/limb_type in has_limbs) var/list/organ_data = has_limbs[limb_type] @@ -405,6 +411,11 @@ O.organ_tag = organ_tag H.internal_organs_by_name[organ_tag] = O + // set butcherable meats from species + for(var/obj/item/organ/O in H.organs) + O.set_initial_meat() + for(var/obj/item/organ/O in H.internal_organs) + O.set_initial_meat() /datum/species/proc/hug(var/mob/living/carbon/human/H, var/mob/living/target) @@ -511,6 +522,10 @@ /datum/species/proc/can_breathe_water() return water_breather +// Called when standing on a water tile. +/datum/species/proc/is_bad_swimmer() + return bad_swimmer + // Impliments different trails for species depending on if they're wearing shoes. /datum/species/proc/get_move_trail(var/mob/living/carbon/human/H) if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) ) @@ -568,4 +583,3 @@ /datum/species/proc/post_spawn_special(mob/living/carbon/human/H) return - diff --git a/code/modules/mob/living/carbon/human/species/species_attack_vr.dm b/code/modules/mob/living/carbon/human/species/species_attack_vr.dm index 513f616b8e3..35fa472345d 100644 --- a/code/modules/mob/living/carbon/human/species/species_attack_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_attack_vr.dm @@ -1,77 +1,77 @@ -/datum/unarmed_attack/bite/sharp/numbing //Is using this against someone you are truly trying to fight a bad idea? Yes. Yes it is. - attack_verb = list("bit") - attack_noun = list("fangs") - attack_sound = 'sound/weapons/bite.ogg' - shredding = 0 - sharp = TRUE - edge = TRUE - -/datum/unarmed_attack/bite/sharp/numbing/show_attack(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) - var/obj/item/organ/external/affecting = target.get_organ(zone) - - attack_damage = CLAMP(attack_damage, 1, 5) - if(target == user) - user.visible_message("[user] [pick(attack_verb)] \himself in the [affecting.name]!") - return 0 //No venom for you. - switch(zone) - if(BP_HEAD, O_MOUTH, O_EYES) - // ----- HEAD ----- // - switch(attack_damage) - if(1 to 2) - user.visible_message("[user]'s fangs scrape across [target]'s cheek!") - to_chat(target, "Your face feels tingly!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) //Have to add this here, otherwise the swtich fails. - if(3 to 4) - user.visible_message("[user]'s fangs pierce into [target]'s neck at an odd, awkward angle!") - to_chat(target, "Your neck feels like it's on fire before going numb!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - if(5) - user.visible_message("[user] sinks \his [pick(attack_noun)] deep into [target]'s neck, causing the vein to bulge outwards at some type of chemical is pumped into it!") - to_chat(target, "Your neck feels like it's going to burst! Moments later, you simply can't feel your neck any longer, the numbness beginning to spread throughout your body!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - else - // ----- BODY ----- // - switch(attack_damage) - if(1 to 2) - user.visible_message("[user]'s fangs scrape across [target]'s [affecting.name]!") - to_chat(target, "Your [affecting.name] feels tingly!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - if(3 to 4) - user.visible_message("[user]'s fangs pierce [pick("", "", "the side of")] [target]'s [affecting.name]!") - to_chat(target, "Your [affecting.name] feels like it's on fire before going numb!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - if(5) - user.visible_message("[user]'s fangs sink deep into [target]'s [affecting.name], one of their veins bulging outwards from the sudden fluid pumped into it!") - to_chat(target, "Your [affecting.name] feels like it's going to burst! Moments later, you simply can't feel your [affecting.name] any longer, the numbness slowly spreading throughout your body!") - target.bloodstr.add_reagent("numbenzyme",attack_damage) - -/datum/unarmed_attack/claws/shadekin - -/datum/unarmed_attack/claws/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) - ..() - if(!(target == user)) - user.shadekin_adjust_energy(attack_damage) - -/datum/unarmed_attack/bite/sharp/shadekin - -/datum/unarmed_attack/bite/sharp/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) - ..() - if(!(target == user)) - user.shadekin_adjust_energy(attack_damage) - -/datum/unarmed_attack/claws/chimera //special feral attack that gets stronger as they get angrier - -/datum/unarmed_attack/claws/chimera/get_unarmed_damage(var/mob/living/carbon/human/user) - return user.feral/5 - -/datum/unarmed_attack/claws/chimera/apply_effects(var/mob/living/carbon/human/user,var/mob/living/carbon/human/target,var/armour,var/attack_damage,var/zone) - ..() - if(user.feral && !(target == user)) - var/selfdamage = ((user.feral/10)-7.5) - if(selfdamage > 0) - var/selfdamagezone = null - if (user.hand) - selfdamagezone=pick(BP_L_ARM, BP_L_HAND) - else - selfdamagezone=pick(BP_R_ARM, BP_R_HAND) +/datum/unarmed_attack/bite/sharp/numbing //Is using this against someone you are truly trying to fight a bad idea? Yes. Yes it is. + attack_verb = list("bit") + attack_noun = list("fangs") + attack_sound = 'sound/weapons/bite.ogg' + shredding = 0 + sharp = TRUE + edge = TRUE + +/datum/unarmed_attack/bite/sharp/numbing/show_attack(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) + var/obj/item/organ/external/affecting = target.get_organ(zone) + + attack_damage = CLAMP(attack_damage, 1, 5) + if(target == user) + user.visible_message("[user] [pick(attack_verb)] \himself in the [affecting.name]!") + return 0 //No venom for you. + switch(zone) + if(BP_HEAD, O_MOUTH, O_EYES) + // ----- HEAD ----- // + switch(attack_damage) + if(1 to 2) + user.visible_message("[user]'s fangs scrape across [target]'s cheek!") + to_chat(target, "Your face feels tingly!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) //Have to add this here, otherwise the swtich fails. + if(3 to 4) + user.visible_message("[user]'s fangs pierce into [target]'s neck at an odd, awkward angle!") + to_chat(target, "Your neck feels like it's on fire before going numb!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + if(5) + user.visible_message("[user] sinks \his [pick(attack_noun)] deep into [target]'s neck, causing the vein to bulge outwards at some type of chemical is pumped into it!") + to_chat(target, "Your neck feels like it's going to burst! Moments later, you simply can't feel your neck any longer, the numbness beginning to spread throughout your body!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + else + // ----- BODY ----- // + switch(attack_damage) + if(1 to 2) + user.visible_message("[user]'s fangs scrape across [target]'s [affecting.name]!") + to_chat(target, "Your [affecting.name] feels tingly!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + if(3 to 4) + user.visible_message("[user]'s fangs pierce [pick("", "", "the side of")] [target]'s [affecting.name]!") + to_chat(target, "Your [affecting.name] feels like it's on fire before going numb!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + if(5) + user.visible_message("[user]'s fangs sink deep into [target]'s [affecting.name], one of their veins bulging outwards from the sudden fluid pumped into it!") + to_chat(target, "Your [affecting.name] feels like it's going to burst! Moments later, you simply can't feel your [affecting.name] any longer, the numbness slowly spreading throughout your body!") + target.bloodstr.add_reagent("numbenzyme",attack_damage) + +/datum/unarmed_attack/claws/shadekin + +/datum/unarmed_attack/claws/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) + ..() + if(!(target == user)) + user.shadekin_adjust_energy(attack_damage) + +/datum/unarmed_attack/bite/sharp/shadekin + +/datum/unarmed_attack/bite/sharp/shadekin/apply_effects(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/zone, var/attack_damage) + ..() + if(!(target == user)) + user.shadekin_adjust_energy(attack_damage) + +/datum/unarmed_attack/claws/chimera //special feral attack that gets stronger as they get angrier + +/datum/unarmed_attack/claws/chimera/get_unarmed_damage(var/mob/living/carbon/human/user) + return user.feral/5 + +/datum/unarmed_attack/claws/chimera/apply_effects(var/mob/living/carbon/human/user,var/mob/living/carbon/human/target,var/armour,var/attack_damage,var/zone) + ..() + if(user.feral && !(target == user)) + var/selfdamage = ((user.feral/10)-7.5) + if(selfdamage > 0) + var/selfdamagezone = null + if (user.hand) + selfdamagezone=pick(BP_L_ARM, BP_L_HAND) + else + selfdamagezone=pick(BP_R_ARM, BP_R_HAND) user.apply_damage(selfdamage, BRUTE, selfdamagezone, 0, 0, sharp=FALSE, edge=FALSE) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm index 4199dba6f32..9ca4008d83a 100644 --- a/code/modules/mob/living/carbon/human/species/species_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_vr.dm @@ -38,6 +38,7 @@ var/micro_size_mod = 0 // How different is our size for interactions that involve us being small? var/macro_size_mod = 0 // How different is our size for interactions that involve us being big? var/digestion_nutrition_modifier = 1 + var/center_offset = 0.5 var/can_climb = FALSE var/climbing_delay = 1.5 // We climb with a quarter delay @@ -50,16 +51,17 @@ /datum/species/create_organs(var/mob/living/carbon/human/H) if(H.nif) - var/type = H.nif.type + /*var/type = H.nif.type var/durability = H.nif.durability var/list/nifsofts = H.nif.nifsofts - var/list/nif_savedata = H.nif.save_data.Copy() + var/list/nif_savedata = H.nif.save_data.Copy()*/ ..() - - var/obj/item/device/nif/nif = new type(H,durability,nif_savedata) - nif.nifsofts = nifsofts + H.nif = null //A previous call during the rejuvenation path deleted it, so we no longer should have it here + /*var/obj/item/device/nif/nif = new type(H,durability,nif_savedata) + nif.nifsofts = nifsofts*/ else ..() + /datum/species/proc/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base) ASSERT(src) ASSERT(istype(H)) diff --git a/code/modules/mob/living/carbon/human/species/station/blank_vr.dm b/code/modules/mob/living/carbon/human/species/station/blank_vr.dm index 47f39bcde00..a609aedb49b 100644 --- a/code/modules/mob/living/carbon/human/species/station/blank_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/blank_vr.dm @@ -8,6 +8,7 @@ name_plural = "Custom" selects_bodytype = SELECTS_BODYTYPE_CUSTOM base_species = SPECIES_HUMAN + digi_allowed = TRUE unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/_protean_defines.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/_protean_defines.dm index 9c6b5a89d78..e469a4dded9 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/_protean_defines.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/_protean_defines.dm @@ -1 +1 @@ -#define PROTEAN_EDIBLE_MATERIALS list(MAT_STEEL, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_METALHYDROGEN) +#define PROTEAN_EDIBLE_MATERIALS list(MAT_STEEL, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_METALHYDROGEN) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index bc228f7ef07..6a397f985fa 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -276,7 +276,7 @@ if(target.buckled) target.buckled.unbuckle_mob(target, force = TRUE) target.forceMove(vore_selected) - to_chat(target,"\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + to_chat(target,"\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") /mob/living/simple_mob/protean_blob/attack_target(var/atom/A) if(refactory && istype(A,/obj/item/stack/material)) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index f7d154b1b84..e0c8114eee1 100755 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -13,6 +13,7 @@ selects_bodytype = SELECTS_BODYTYPE_SHAPESHIFTER base_species = SPECIES_HUMAN + digi_allowed = TRUE blood_color = "#505050" //This is the same as the 80,80,80 below, but in hex flesh_color = "#505050" diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index fc9c607a0dd..6fda9ef6804 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -73,6 +73,7 @@ health_hud_intensity = 2.5 chem_strength_alcohol = 0.75 throwforce_absorb_threshold = 10 + digi_allowed = TRUE min_age = 32 max_age = 260 @@ -198,6 +199,7 @@ species_language = LANGUAGE_SIIK health_hud_intensity = 2.5 chem_strength_alcohol = 1.25 + digi_allowed = TRUE min_age = 17 max_age = 80 diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm index 04bdce0b1f1..f4b80235620 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm @@ -430,7 +430,7 @@ to_chat(src, "You cannot bite in your current state.") return if(B.vessel.total_volume <= 0 || B.isSynthetic()) //Do they have any blood in the first place, and are they synthetic? - to_chat(src, "There appears to be no blood in this prey...") + to_chat(src, span_red("There appears to be no blood in this prey...")) return last_special = world.time + 600 @@ -468,9 +468,9 @@ if(noise) - src.visible_message("[src] moves their head next to [B]'s neck, seemingly looking for something!") + src.visible_message(span_red("[src] moves their head next to [B]'s neck, seemingly looking for something!")) else - src.visible_message("[src] moves their head next to [B]'s neck, seemingly looking for something!", range = 1) + src.visible_message(span_red("[src] moves their head next to [B]'s neck, seemingly looking for something!"), range = 1) if(bleed) //Due to possibility of missing/misclick and missing the bleeding cues, we are warning the scene members of BLEEDING being on to_chat(src, SPAN_WARNING("This is going to cause [B] to keep bleeding!")) @@ -479,9 +479,9 @@ if(do_after(src, 300, B)) //Thrirty seconds. if(!Adjacent(B)) return if(noise) - src.visible_message("[src] suddenly extends their fangs and plunges them down into [B]'s neck!") + src.visible_message(span_red("[src] suddenly extends their fangs and plunges them down into [B]'s neck!")) else - src.visible_message("[src] suddenly extends their fangs and plunges them down into [B]'s neck!", range = 1) + src.visible_message(span_red("[src] suddenly extends their fangs and plunges them down into [B]'s neck!"), range = 1) if(bleed) B.apply_damage(10, BRUTE, BP_HEAD, blocked = 0, soaked = 0, sharp = TRUE, edge = FALSE) var/obj/item/organ/external/E = B.get_organ(BP_HEAD) @@ -1232,9 +1232,9 @@ return if(target.buckled) //how are you buckled in the water?! target.buckled.unbuckle_mob() - target.visible_message("\The [target] suddenly disappears, being dragged into the water!",\ - "You are dragged below the water and feel yourself slipping directly into \the [src]'s [vore_selected]!") - to_chat(src, "You successfully drag \the [target] into the water, slipping them into your [vore_selected].") + target.visible_message("\The [target] suddenly disappears, being dragged into the water!",\ + "You are dragged below the water and feel yourself slipping directly into \the [src]'s [vore_selected]!") + to_chat(src, "You successfully drag \the [target] into the water, slipping them into your [vore_selected].") target.forceMove(src.vore_selected) /mob/living/carbon/human/proc/toggle_pain_module() @@ -1308,8 +1308,8 @@ to_chat(src, "You need to be closer to do that.") return - visible_message("\The [src] attempts to snatch up [target]!", \ - "You attempt to snatch up [target]!" ) + visible_message("\The [src] attempts to snatch up [target]!", \ + "You attempt to snatch up [target]!" ) playsound(src, 'sound/vore/sunesound/pred/schlorp.ogg', 25) //Code to shoot the beam here. @@ -1460,3 +1460,72 @@ hitsound = 'sound/vore/sunesound/pred/schlorp.ogg' hitsound_wall = 'sound/vore/sunesound/pred/schlorp.ogg' zaptype = /obj/item/projectile/beam/appendage + +/mob/living/proc/target_lunge() //The leaper leap, but usable as an ability + set name = "Lunge At Prey" + set category = "Abilities" + set desc = "Dive atop your prey and gobble them up!" + + var/leap_warmup = 1 SECOND //Easy to modify + var/leap_sound = 'sound/weapons/spiderlunge.ogg' + + if(stat || paralysis || weakened || stunned || world.time < last_special) //No tongue flicking while stunned. + to_chat(src, "You can't do that in your current state.") + return + + last_special = world.time + 10 //Anti-spam. + + if (!istype(src, /mob/living)) + to_chat(src, "It doesn't work that way.") + return + + else + var/list/targets = list() //IF IT IS NOT BROKEN. DO NOT FIX IT. + + for(var/mob/living/L in range(5, src)) + if(!istype(L, /mob/living)) //Don't eat anything that isn't mob/living. Failsafe. + continue + if(L == src) //no eating yourself. 1984. + continue + if(L.devourable && L.throw_vore && (L.can_be_drop_pred || L.can_be_drop_prey)) + targets += L + + if(!(targets.len)) + to_chat(src, "No eligible targets found.") + return + + var/mob/living/target = tgui_input_list(src, "Please select a target.", "Victim", targets) + + if(!target) + return + + if(!istype(target, /mob/living)) //Safety. + to_chat(src, "You need to select a living target!") + return + + if (get_dist(src,target) >= 6) + to_chat(src, "You need to be closer to do that.") + return + + visible_message(span("warning","\The [src] rears back, ready to lunge!")) + to_chat(target, span("danger","\The [src] focuses on you!")) + // Telegraph, since getting stunned suddenly feels bad. + do_windup_animation(target, leap_warmup) + sleep(leap_warmup) // For the telegraphing. + + if(target.z != z) //Make sure you haven't disappeared to somewhere we can't go + return FALSE + + // Do the actual leap. + status_flags |= LEAPING // Lets us pass over everything. + visible_message(span("critical","\The [src] leaps at \the [target]!")) + throw_at(get_step(target, get_turf(src)), 7, 1, src) + playsound(src, leap_sound, 75, 1) + + sleep(5) // For the throw to complete. + + if(status_flags & LEAPING) + status_flags &= ~LEAPING // Revert special passage ability. + + if(Adjacent(target)) //We leapt at them but we didn't manage to hit them, let's see if we're next to them + target.Weaken(2) //get knocked down, idiot diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm index 67c9ca9322a..c66a48b1758 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm @@ -16,6 +16,7 @@ burn_mod = 1.15 //As vulnerable to burn as a Tajara. base_species = "Xenochimera" selects_bodytype = SELECTS_BODYTYPE_CUSTOM + digi_allowed = TRUE num_alternate_languages = 3 species_language = null @@ -332,6 +333,7 @@ brute_mod = 0.8 //20% brute damage reduction burn_mod = 1.15 //15% burn damage increase. They're spiders. Aerosol can+lighter = dead spiders. throwforce_absorb_threshold = 10 + digi_allowed = TRUE num_alternate_languages = 3 species_language = LANGUAGE_VESPINAE diff --git a/code/modules/mob/living/carbon/human/species/station/station_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_vr.dm index 79273429b2d..33282aefe7d 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_vr.dm @@ -17,6 +17,7 @@ species_language = LANGUAGE_SAGARU color_mult = 1 inherent_verbs = list(/mob/living/carbon/human/proc/tie_hair) + digi_allowed = TRUE min_age = 18 max_age = 80 @@ -86,6 +87,7 @@ inherent_verbs = list(/mob/living/carbon/human/proc/tie_hair, /mob/living/carbon/human/proc/water_stealth, /mob/living/carbon/human/proc/underwater_devour) min_age = 18 max_age = 80 + digi_allowed = TRUE blurb = "The Akula are a species of amphibious humanoids like the Skrell, but have an appearance very similar to that of a shark. \ They were first discovered as a primitive race of underwater dwelling tribal creatures by the Skrell. At first they were not believed \ @@ -138,6 +140,7 @@ /mob/living/carbon/human/proc/tie_hair) min_age = 18 max_age = 80 + digi_allowed = TRUE blurb = "Nevreans are a race of avian and dinosaur-like creatures living on Tal. They belong to a group of races that hails from Eltus, \ in the Vilous system. Unlike sergals whom they share a star system with, their species is a very peaceful one. They possess remarkable \ @@ -180,6 +183,7 @@ inherent_verbs = list(/mob/living/carbon/human/proc/lick_wounds, /mob/living/proc/shred_limb, /mob/living/carbon/human/proc/tie_hair) + digi_allowed = TRUE min_age = 18 max_age = 80 @@ -237,6 +241,7 @@ color_mult = 1 inherent_verbs = list(/mob/living/carbon/human/proc/lick_wounds, /mob/living/carbon/human/proc/tie_hair) + digi_allowed = TRUE wikilink="https://wiki.vore-station.net/Backstory#Vulpkanin" @@ -411,6 +416,7 @@ color_mult = 1 genders = list(MALE, FEMALE, PLURAL, NEUTER) inherent_verbs = list(/mob/living/proc/flying_toggle,/mob/living/proc/flying_vore_toggle,/mob/living/proc/start_wings_hovering,/mob/living/carbon/human/proc/tie_hair) + digi_allowed = TRUE min_age = 18 max_age = 80 @@ -452,6 +458,7 @@ taken to calling these creatures 'Shadekin', and the name has generally stuck and spread. " //TODO: Something more fitting for black-eyes wikilink = "https://wiki.vore-station.net/Shadekin" catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) + digi_allowed = TRUE language = LANGUAGE_SHADEKIN name_language = LANGUAGE_SHADEKIN @@ -561,6 +568,7 @@ name_language = LANGUAGE_TERMINUS species_language = LANGUAGE_TERMINUS inherent_verbs = list(/mob/living/carbon/human/proc/lick_wounds,/mob/living/proc/shred_limb,/mob/living/carbon/human/proc/tie_hair) + digi_allowed = TRUE min_age = 18 max_age = 80 @@ -592,6 +600,7 @@ darksight = 4 //Better hunters in the dark. hunger_factor = 0.1 //In exchange, they get hungry a tad faster. num_alternate_languages = 3 + digi_allowed = TRUE min_age = 18 max_age = 80 @@ -631,6 +640,7 @@ name_language = null color_mult = 1 inherent_verbs = list(/mob/living/carbon/human/proc/tie_hair) + digi_allowed = TRUE min_age = 18 max_age = 80 diff --git a/code/modules/mob/living/carbon/human/species/station/teshari.dm b/code/modules/mob/living/carbon/human/species/station/teshari.dm index 2d9c9fa9fb8..2e41b601694 100644 --- a/code/modules/mob/living/carbon/human/species/station/teshari.dm +++ b/code/modules/mob/living/carbon/human/species/station/teshari.dm @@ -23,6 +23,7 @@ female_cough_sounds = list('sound/effects/mob_effects/tesharicougha.ogg','sound/effects/mob_effects/tesharicoughb.ogg') male_sneeze_sound = 'sound/effects/mob_effects/tesharisneeze.ogg' female_sneeze_sound = 'sound/effects/mob_effects/tesharisneeze.ogg' + center_offset = 0 blood_color = "#D514F7" flesh_color = "#5F7BB0" diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm index 07e086b13d9..adc3c051f4d 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm @@ -168,3 +168,9 @@ custom_only = FALSE varchange_type = TRAIT_VARCHANGE_MORE_BETTER +/datum/trait/negative/bad_swimmer + name = "Bad Swimmer" + desc = "You can't swim very well, all water slows you down a lot and you drown in deep water." + cost = -1 + custom_only = FALSE + var_changes = list("bad_swimmer" = 1, "water_movement" = 4) diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/weaver_objs.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/weaver_objs.dm index fef1206c66a..c9978eb5078 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/weaver_objs.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/weaver_objs.dm @@ -74,7 +74,7 @@ return /obj/structure/bed/double/weaversilk_nest/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.is_wrench() || istype(W,/obj/item/stack) || W.is_wirecutter()) + if(W.has_tool_quality(TOOL_WRENCH) || istype(W,/obj/item/stack) || W.has_tool_quality(TOOL_WIRECUTTER)) return ..() diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm index ec054ab5207..87c71c52963 100644 --- a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm @@ -1,120 +1,120 @@ -// ### Wooo, inheritance. Basically copying everything I don't need to edit from prometheans, because they mostly work already. -// ### Any and all of this is open to change for balance or whatever. -// ### -// ### -// Species definition follows. -/datum/species/shapeshifter/promethean/avatar - - name = SPECIES_VR - name_plural = "Virtual Reality Avatars" - blurb = "A 3-dimensional representation of some sort of animate object used to display the presence and actions of some-one or -thing using a virtual reality program." - show_ssd = "eerily still" - death_message = "flickers briefly, their gear falling in a heap on the floor around their motionless body." - knockout_message = "has been knocked unconscious!" - - spawn_flags = SPECIES_IS_RESTRICTED - - speech_bubble_appearance = "cyber" - - assisted_langs = list() - - male_cough_sounds = list('sound/effects/mob_effects/m_cougha.ogg','sound/effects/mob_effects/m_coughb.ogg', 'sound/effects/mob_effects/m_coughc.ogg') - female_cough_sounds = list('sound/effects/mob_effects/f_cougha.ogg','sound/effects/mob_effects/f_coughb.ogg') - male_sneeze_sound = 'sound/effects/mob_effects/sneeze.ogg' - female_sneeze_sound = 'sound/effects/mob_effects/f_sneeze.ogg' - - valid_transform_species = list(SPECIES_HUMAN, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_DIONA, SPECIES_TESHARI, SPECIES_VOX, SPECIES_MONKEY, SPECIES_SKELETON) - - unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) - has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, O_EYES = /obj/item/organ/internal/eyes) // Slime core. - heal_rate = 0 // Avatars don't naturally heal like prometheans, at least not for now - inherent_verbs = list( - /mob/living/carbon/human/proc/shapeshifter_select_shape, - /mob/living/carbon/human/proc/shapeshifter_select_colour, - /mob/living/carbon/human/proc/shapeshifter_select_hair, - /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, - /mob/living/carbon/human/proc/shapeshifter_select_gender, - /mob/living/carbon/human/proc/shapeshifter_select_wings, - /mob/living/carbon/human/proc/shapeshifter_select_tail, - /mob/living/carbon/human/proc/shapeshifter_select_ears, - /mob/living/proc/set_size, - /mob/living/carbon/human/proc/regenerate, - /mob/living/carbon/human/proc/promethean_select_opaqueness, - /mob/living/carbon/human/proc/exit_vr - ) - - -/datum/species/shapeshifter/promethean/avatar/handle_death(var/mob/living/carbon/human/H) - return - -/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H) - return - -/mob/living/carbon/human/proc/shapeshifter_change_opacity() - - set name = "Toggle Opacity" - set category = "Abilities" - - if(stat || world.time < last_special) - return - - last_special = world.time + 10 - - if(src.icon_state == "promethean") - icon_state = lowertext(src.species.get_bodytype(src)) - shapeshifter_change_species("Virtual Reality [src.species.get_bodytype(src)]") - else - icon_state = "promethean" - shapeshifter_change_species(SPECIES_VR) - - -// enter_vr is called on the original mob, and puts the mind into the supplied vr mob -/mob/living/carbon/human/proc/enter_vr(var/mob/living/carbon/human/avatar) // Avatar is currently a human, because we have preexisting setup code for appearance manipulation, etc. - if(!istype(avatar)) - return - - // Link the two mobs for client transfer - avatar.vr_holder = src - src.teleop = avatar - src.vr_link = avatar // Can't reuse vr_holder so that death can automatically eject users from VR - - // Move the mind - avatar.Sleeping(1) - src.mind.transfer_to(avatar) - to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.") //No more prommie VR thing, so removed tidbit about changing appearance - to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? - -// exit_vr is called on the vr mob, and puts the mind back into the original mob -/mob/living/carbon/human/proc/exit_vr() - set name = "Exit Virtual Reality" - set category = "Abilities" - - if(!vr_holder) - return - if(!mind) - return - - var/total_damage - // Tally human damage - if(ishuman(src)) - var/mob/living/carbon/human/H = src - total_damage = H.getBruteLoss() + H.getFireLoss() + H.getOxyLoss() + H.getToxLoss() - - // Move the mind back to the original mob -// vr_holder.Sleeping(1) - src.mind.transfer_to(vr_holder) - to_chat(vr_holder, "You black out for a moment, and wake to find yourself back in your own body.") - // Two-thirds damage is transferred as agony for /humans - // Getting hurt in VR doesn't damage the physical body, but you still got hurt. - if(ishuman(vr_holder) && total_damage) - var/mob/living/carbon/human/V = vr_holder - V.stun_effect_act(0, total_damage*2/3, null) // 200 damage leaves the user in paincrit for several seconds, agony reaches 0 after around 2m. - to_chat(vr_holder, "Pain from your time in VR lingers.") // 250 damage leaves the user unconscious for several seconds in addition to paincrit - - // Maintain a link with the mob, but don't use teleop - vr_holder.vr_link = src - vr_holder.teleop = null - - if(istype(vr_holder.loc, /obj/machinery/vr_sleeper)) - var/obj/machinery/vr_sleeper/V = vr_holder.loc +// ### Wooo, inheritance. Basically copying everything I don't need to edit from prometheans, because they mostly work already. +// ### Any and all of this is open to change for balance or whatever. +// ### +// ### +// Species definition follows. +/datum/species/shapeshifter/promethean/avatar + + name = SPECIES_VR + name_plural = "Virtual Reality Avatars" + blurb = "A 3-dimensional representation of some sort of animate object used to display the presence and actions of some-one or -thing using a virtual reality program." + show_ssd = "eerily still" + death_message = "flickers briefly, their gear falling in a heap on the floor around their motionless body." + knockout_message = "has been knocked unconscious!" + + spawn_flags = SPECIES_IS_RESTRICTED + + speech_bubble_appearance = "cyber" + + assisted_langs = list() + + male_cough_sounds = list('sound/effects/mob_effects/m_cougha.ogg','sound/effects/mob_effects/m_coughb.ogg', 'sound/effects/mob_effects/m_coughc.ogg') + female_cough_sounds = list('sound/effects/mob_effects/f_cougha.ogg','sound/effects/mob_effects/f_coughb.ogg') + male_sneeze_sound = 'sound/effects/mob_effects/sneeze.ogg' + female_sneeze_sound = 'sound/effects/mob_effects/f_sneeze.ogg' + + valid_transform_species = list(SPECIES_HUMAN, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_DIONA, SPECIES_TESHARI, SPECIES_VOX, SPECIES_MONKEY, SPECIES_SKELETON) + + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) + has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, O_EYES = /obj/item/organ/internal/eyes) // Slime core. + heal_rate = 0 // Avatars don't naturally heal like prometheans, at least not for now + inherent_verbs = list( + /mob/living/carbon/human/proc/shapeshifter_select_shape, + /mob/living/carbon/human/proc/shapeshifter_select_colour, + /mob/living/carbon/human/proc/shapeshifter_select_hair, + /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, + /mob/living/carbon/human/proc/shapeshifter_select_gender, + /mob/living/carbon/human/proc/shapeshifter_select_wings, + /mob/living/carbon/human/proc/shapeshifter_select_tail, + /mob/living/carbon/human/proc/shapeshifter_select_ears, + /mob/living/proc/set_size, + /mob/living/carbon/human/proc/regenerate, + /mob/living/carbon/human/proc/promethean_select_opaqueness, + /mob/living/carbon/human/proc/exit_vr + ) + + +/datum/species/shapeshifter/promethean/avatar/handle_death(var/mob/living/carbon/human/H) + return + +/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H) + return + +/mob/living/carbon/human/proc/shapeshifter_change_opacity() + + set name = "Toggle Opacity" + set category = "Abilities" + + if(stat || world.time < last_special) + return + + last_special = world.time + 10 + + if(src.icon_state == "promethean") + icon_state = lowertext(src.species.get_bodytype(src)) + shapeshifter_change_species("Virtual Reality [src.species.get_bodytype(src)]") + else + icon_state = "promethean" + shapeshifter_change_species(SPECIES_VR) + + +// enter_vr is called on the original mob, and puts the mind into the supplied vr mob +/mob/living/carbon/human/proc/enter_vr(var/mob/living/carbon/human/avatar) // Avatar is currently a human, because we have preexisting setup code for appearance manipulation, etc. + if(!istype(avatar)) + return + + // Link the two mobs for client transfer + avatar.vr_holder = src + src.teleop = avatar + src.vr_link = avatar // Can't reuse vr_holder so that death can automatically eject users from VR + + // Move the mind + avatar.Sleeping(1) + src.mind.transfer_to(avatar) + to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.") //No more prommie VR thing, so removed tidbit about changing appearance + to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? + +// exit_vr is called on the vr mob, and puts the mind back into the original mob +/mob/living/carbon/human/proc/exit_vr() + set name = "Exit Virtual Reality" + set category = "Abilities" + + if(!vr_holder) + return + if(!mind) + return + + var/total_damage + // Tally human damage + if(ishuman(src)) + var/mob/living/carbon/human/H = src + total_damage = H.getBruteLoss() + H.getFireLoss() + H.getOxyLoss() + H.getToxLoss() + + // Move the mind back to the original mob +// vr_holder.Sleeping(1) + src.mind.transfer_to(vr_holder) + to_chat(vr_holder, "You black out for a moment, and wake to find yourself back in your own body.") + // Two-thirds damage is transferred as agony for /humans + // Getting hurt in VR doesn't damage the physical body, but you still got hurt. + if(ishuman(vr_holder) && total_damage) + var/mob/living/carbon/human/V = vr_holder + V.stun_effect_act(0, total_damage*2/3, null) // 200 damage leaves the user in paincrit for several seconds, agony reaches 0 after around 2m. + to_chat(vr_holder, "Pain from your time in VR lingers.") // 250 damage leaves the user unconscious for several seconds in addition to paincrit + + // Maintain a link with the mob, but don't use teleop + vr_holder.vr_link = src + vr_holder.teleop = null + + if(istype(vr_holder.loc, /obj/machinery/vr_sleeper)) + var/obj/machinery/vr_sleeper/V = vr_holder.loc V.go_out() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm index 43210407bc1..c9aa8a9a339 100644 --- a/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/opaque_form.dm @@ -1,181 +1,181 @@ -// Species for the opaque appearance -// Due to sprite construction, they have to have separate limb lists - -/datum/species/shapeshifter/promethean/avatar/human - name = "Virtual Reality Human" - icobase = 'icons/mob/human_races/r_human.dmi' - deform = 'icons/mob/human_races/r_def_human.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/unathi - name = "Virtual Reality Unathi" - icobase = 'icons/mob/human_races/r_lizard.dmi' - deform = 'icons/mob/human_races/r_def_lizard.dmi' - tail = "sogtail" - tail_animation = 'icons/mob/species/unathi/tail.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest/unathi), - BP_GROIN = list("path" = /obj/item/organ/external/groin/unathi), - BP_HEAD = list("path" = /obj/item/organ/external/head/unathi), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/tajaran - name = "Virtual Reality Tajaran" - icobase = 'icons/mob/human_races/r_tajaran.dmi' - deform = 'icons/mob/human_races/r_def_tajaran.dmi' - tail = "tajtail" - tail_animation = 'icons/mob/species/tajaran/tail.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/skrell - name = "Virtual Reality Skrell" - icobase = 'icons/mob/human_races/r_skrell.dmi' - deform = 'icons/mob/human_races/r_def_skrell.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/skrell), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/teshari - name = "Virtual Reality Teshari" - icobase = 'icons/mob/human_races/r_teshari.dmi' - deform = 'icons/mob/human_races/r_teshari.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/teshari), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand/teshari), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/teshari), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot/teshari), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/teshari) - ) - -/datum/species/shapeshifter/promethean/avatar/diona - name = "Virtual Reality Diona" - icobase = 'icons/mob/human_races/r_diona.dmi' - deform = 'icons/mob/human_races/r_def_plant.dmi' - appearance_flags = 0 - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/diona/chest), - BP_GROIN = list("path" = /obj/item/organ/external/diona/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes/diona), - BP_L_ARM = list("path" = /obj/item/organ/external/diona/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/diona/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/diona/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/diona/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/diona/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/diona/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/diona/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/diona/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/monkey - name = "Virtual Reality Monkey" - icobase = 'icons/mob/human_races/monkeys/r_monkey.dmi' - deform = 'icons/mob/human_races/monkeys/r_monkey.dmi' - damage_overlays = 'icons/mob/human_races/masks/dam_monkey.dmi' - damage_mask = 'icons/mob/human_races/masks/dam_mask_monkey.dmi' - blood_mask = 'icons/mob/human_races/masks/blood_monkey.dmi' - fire_icon_state = "monkey" - appearance_flags = 0 - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/vox - name = "Virtual Reality Vox" - icobase = 'icons/mob/human_races/r_vox.dmi' - deform = 'icons/mob/human_races/r_def_vox.dmi' - appearance_flags = HAS_EYE_COLOR | HAS_HAIR_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head/vox), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) - -/datum/species/shapeshifter/promethean/avatar/skeleton - name = "Virtual Reality Skeleton" - icobase = 'icons/mob/human_races/r_skeleton.dmi' - deform = 'icons/mob/human_races/r_skeleton.dmi' - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR - has_limbs = list( - BP_TORSO = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - BP_HEAD = list("path" = /obj/item/organ/external/head), - BP_L_ARM = list("path" = /obj/item/organ/external/arm), - BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), - BP_L_LEG = list("path" = /obj/item/organ/external/leg), - BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), - BP_L_HAND = list("path" = /obj/item/organ/external/hand), - BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), - BP_L_FOOT = list("path" = /obj/item/organ/external/foot), - BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) - ) +// Species for the opaque appearance +// Due to sprite construction, they have to have separate limb lists + +/datum/species/shapeshifter/promethean/avatar/human + name = "Virtual Reality Human" + icobase = 'icons/mob/human_races/r_human.dmi' + deform = 'icons/mob/human_races/r_def_human.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_TONE | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/unathi + name = "Virtual Reality Unathi" + icobase = 'icons/mob/human_races/r_lizard.dmi' + deform = 'icons/mob/human_races/r_def_lizard.dmi' + tail = "sogtail" + tail_animation = 'icons/mob/species/unathi/tail.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest/unathi), + BP_GROIN = list("path" = /obj/item/organ/external/groin/unathi), + BP_HEAD = list("path" = /obj/item/organ/external/head/unathi), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/tajaran + name = "Virtual Reality Tajaran" + icobase = 'icons/mob/human_races/r_tajaran.dmi' + deform = 'icons/mob/human_races/r_def_tajaran.dmi' + tail = "tajtail" + tail_animation = 'icons/mob/species/tajaran/tail.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/skrell + name = "Virtual Reality Skrell" + icobase = 'icons/mob/human_races/r_skrell.dmi' + deform = 'icons/mob/human_races/r_def_skrell.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/skrell), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/teshari + name = "Virtual Reality Teshari" + icobase = 'icons/mob/human_races/r_teshari.dmi' + deform = 'icons/mob/human_races/r_teshari.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/teshari), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand/teshari), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/teshari), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot/teshari), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/teshari) + ) + +/datum/species/shapeshifter/promethean/avatar/diona + name = "Virtual Reality Diona" + icobase = 'icons/mob/human_races/r_diona.dmi' + deform = 'icons/mob/human_races/r_def_plant.dmi' + appearance_flags = 0 + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/diona/chest), + BP_GROIN = list("path" = /obj/item/organ/external/diona/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes/diona), + BP_L_ARM = list("path" = /obj/item/organ/external/diona/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/diona/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/diona/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/diona/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/diona/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/diona/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/diona/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/diona/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/monkey + name = "Virtual Reality Monkey" + icobase = 'icons/mob/human_races/monkeys/r_monkey.dmi' + deform = 'icons/mob/human_races/monkeys/r_monkey.dmi' + damage_overlays = 'icons/mob/human_races/masks/dam_monkey.dmi' + damage_mask = 'icons/mob/human_races/masks/dam_mask_monkey.dmi' + blood_mask = 'icons/mob/human_races/masks/blood_monkey.dmi' + fire_icon_state = "monkey" + appearance_flags = 0 + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/no_eyes), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/vox + name = "Virtual Reality Vox" + icobase = 'icons/mob/human_races/r_vox.dmi' + deform = 'icons/mob/human_races/r_def_vox.dmi' + appearance_flags = HAS_EYE_COLOR | HAS_HAIR_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head/vox), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) + +/datum/species/shapeshifter/promethean/avatar/skeleton + name = "Virtual Reality Skeleton" + icobase = 'icons/mob/human_races/r_skeleton.dmi' + deform = 'icons/mob/human_races/r_skeleton.dmi' + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest), + BP_GROIN = list("path" = /obj/item/organ/external/groin), + BP_HEAD = list("path" = /obj/item/organ/external/head), + BP_L_ARM = list("path" = /obj/item/organ/external/arm), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right), + BP_L_LEG = list("path" = /obj/item/organ/external/leg), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right), + BP_L_HAND = list("path" = /obj/item/organ/external/hand), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right) + ) diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index 9f926272be0..ee6422464f3 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -1,177 +1,177 @@ -/mob/living/carbon/human/proc/handle_strip(var/slot_to_strip,var/mob/living/user) - - if(!slot_to_strip || !istype(user)) - return - - if(user.incapacitated() || !user.Adjacent(src)) - user << browse(null, text("window=mob[src.name]")) - return - - var/obj/item/target_slot = get_equipped_item(text2num(slot_to_strip)) - - switch(slot_to_strip) - // Handle things that are part of this interface but not removing/replacing a given item. - if("pockets") - visible_message("\The [user] is trying to empty \the [src]'s pockets!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - empty_pockets(user) - return - if("splints") - visible_message("\The [user] is trying to remove \the [src]'s splints!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - remove_splints(user) - return - if("sensors") - visible_message("\The [user] is trying to set \the [src]'s sensors!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - toggle_sensors(user) - return - if("internals") - visible_message("\The [usr] is trying to set \the [src]'s internals!") - if(do_after(user,HUMAN_STRIP_DELAY,src)) - toggle_internals(user) - return - if("tie") - var/obj/item/clothing/under/suit = w_uniform - if(!istype(suit) || !LAZYLEN(suit.accessories)) - return - var/obj/item/clothing/accessory/A = suit.accessories[1] - if(!istype(A)) - return - visible_message("\The [usr] is trying to remove \the [src]'s [A.name]!") - - if(!do_after(user,HUMAN_STRIP_DELAY,src)) - return - - if(!A || suit.loc != src || !(A in suit.accessories)) - return - - if(istype(A, /obj/item/clothing/accessory/badge) || istype(A, /obj/item/clothing/accessory/medal)) - user.visible_message("\The [user] tears off \the [A] from [src]'s [suit.name]!") - add_attack_logs(user,src,"Stripped [A.name] off [suit.name]") - A.on_removed(user) - suit.accessories -= A - update_inv_w_uniform() - return - - // Are we placing or stripping? - var/stripping - var/obj/item/held = user.get_active_hand() - if(!istype(held) || is_robot_module(held)) - stripping = TRUE - else - var/obj/item/weapon/holder/holder = held - if(istype(holder) && src == holder.held_mob) - stripping = TRUE - else - var/obj/item/weapon/grab/grab = held - if(istype(grab) && grab.affecting == src) - stripping = TRUE - - if(stripping) - if(!istype(target_slot)) // They aren't holding anything valid and there's nothing to remove, why are we even here? - return - if(!target_slot.canremove) - to_chat(user, "You cannot remove \the [src]'s [target_slot.name].") - return - visible_message("\The [user] is trying to remove \the [src]'s [target_slot.name]!") - else - if(slot_to_strip == slot_wear_mask && istype(held, /obj/item/weapon/grenade)) - visible_message("\The [user] is trying to put \a [held] in \the [src]'s mouth!") - else - visible_message("\The [user] is trying to put \a [held] on \the [src]!") - - if(!do_after(user,HUMAN_STRIP_DELAY,src)) - return - - if(!stripping) - if(user.get_active_hand() != held) - return - var/obj/item/weapon/holder/mobheld = held - if(istype(mobheld)&&mobheld.held_mob==src) - to_chat(user, "You can't put someone on themselves! Stop trying to break reality!") - return - - - if(stripping) - add_attack_logs(user,src,"Removed equipment from slot [target_slot]") - unEquip(target_slot) - else if(user.unEquip(held)) - equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1) - if(held.loc != src) - user.put_in_hands(held) - -// Empty out everything in the target's pockets. -/mob/living/carbon/human/proc/empty_pockets(var/mob/living/user) - if(!r_store && !l_store) - to_chat(user, "\The [src] has nothing in their pockets.") - return - if(r_store) - unEquip(r_store) - if(l_store) - unEquip(l_store) - visible_message("\The [user] empties \the [src]'s pockets!") - -// Modify the current target sensor level. -/mob/living/carbon/human/proc/toggle_sensors(var/mob/living/user) - var/obj/item/clothing/under/suit = w_uniform - if(!suit) - to_chat(user, "\The [src] is not wearing a suit with sensors.") - return - if (suit.has_sensor >= 2) - to_chat(user, "\The [src]'s suit sensor controls are locked.") - return - add_attack_logs(user,src,"Adjusted suit sensor level") - suit.set_sensors(user) - -// Remove all splints. -/mob/living/carbon/human/proc/remove_splints(var/mob/living/user) - - var/can_reach_splints = 1 - if(istype(wear_suit,/obj/item/clothing/suit/space)) - var/obj/item/clothing/suit/space/suit = wear_suit - if(suit.supporting_limbs && suit.supporting_limbs.len) - to_chat(user, "You cannot remove the splints - [src]'s [suit] is supporting some of the breaks.") - can_reach_splints = 0 - - if(can_reach_splints) - var/removed_splint - for(var/obj/item/organ/external/o in organs) - if (o && o.splinted) - var/obj/item/S = o.splinted - if(istype(S) && S.loc == o) //can only remove splints that are actually worn on the organ (deals with hardsuit splints) - S.add_fingerprint(user) - if(o.remove_splint()) - user.put_in_active_hand(S) - removed_splint = 1 - if(removed_splint) - visible_message("\The [user] removes \the [src]'s splints!") - else - to_chat(user, "\The [src] has no splints to remove.") - -// Set internals on or off. -/mob/living/carbon/human/proc/toggle_internals(var/mob/living/user) - if(internal) - internal.add_fingerprint(user) - internal = null - if(internals) - internals.icon_state = "internal0" - else - // Check for airtight mask/helmet. - if(!(istype(wear_mask, /obj/item/clothing/mask) || istype(head, /obj/item/clothing/head/helmet/space))) - return - // Find an internal source. - if(istype(back, /obj/item/weapon/tank)) - internal = back - else if(istype(s_store, /obj/item/weapon/tank)) - internal = s_store - else if(istype(belt, /obj/item/weapon/tank)) - internal = belt - - if(internal) - visible_message("\The [src] is now running on internals!") - internal.add_fingerprint(user) - if (internals) - internals.icon_state = "internal1" - else - visible_message("\The [user] disables \the [src]'s internals!") +/mob/living/carbon/human/proc/handle_strip(var/slot_to_strip,var/mob/living/user) + + if(!slot_to_strip || !istype(user)) + return + + if(user.incapacitated() || !user.Adjacent(src)) + user << browse(null, text("window=mob[src.name]")) + return + + var/obj/item/target_slot = get_equipped_item(text2num(slot_to_strip)) + + switch(slot_to_strip) + // Handle things that are part of this interface but not removing/replacing a given item. + if("pockets") + visible_message("\The [user] is trying to empty \the [src]'s pockets!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + empty_pockets(user) + return + if("splints") + visible_message("\The [user] is trying to remove \the [src]'s splints!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + remove_splints(user) + return + if("sensors") + visible_message("\The [user] is trying to set \the [src]'s sensors!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + toggle_sensors(user) + return + if("internals") + visible_message("\The [usr] is trying to set \the [src]'s internals!") + if(do_after(user,HUMAN_STRIP_DELAY,src)) + toggle_internals(user) + return + if("tie") + var/obj/item/clothing/under/suit = w_uniform + if(!istype(suit) || !LAZYLEN(suit.accessories)) + return + var/obj/item/clothing/accessory/A = suit.accessories[1] + if(!istype(A)) + return + visible_message("\The [usr] is trying to remove \the [src]'s [A.name]!") + + if(!do_after(user,HUMAN_STRIP_DELAY,src)) + return + + if(!A || suit.loc != src || !(A in suit.accessories)) + return + + if(istype(A, /obj/item/clothing/accessory/badge) || istype(A, /obj/item/clothing/accessory/medal)) + user.visible_message("\The [user] tears off \the [A] from [src]'s [suit.name]!") + add_attack_logs(user,src,"Stripped [A.name] off [suit.name]") + A.on_removed(user) + suit.accessories -= A + update_inv_w_uniform() + return + + // Are we placing or stripping? + var/stripping + var/obj/item/held = user.get_active_hand() + if(!istype(held) || is_robot_module(held)) + stripping = TRUE + else + var/obj/item/weapon/holder/holder = held + if(istype(holder) && src == holder.held_mob) + stripping = TRUE + else + var/obj/item/weapon/grab/grab = held + if(istype(grab) && grab.affecting == src) + stripping = TRUE + + if(stripping) + if(!istype(target_slot)) // They aren't holding anything valid and there's nothing to remove, why are we even here? + return + if(!target_slot.canremove) + to_chat(user, "You cannot remove \the [src]'s [target_slot.name].") + return + visible_message("\The [user] is trying to remove \the [src]'s [target_slot.name]!") + else + if(slot_to_strip == slot_wear_mask && istype(held, /obj/item/weapon/grenade)) + visible_message("\The [user] is trying to put \a [held] in \the [src]'s mouth!") + else + visible_message("\The [user] is trying to put \a [held] on \the [src]!") + + if(!do_after(user,HUMAN_STRIP_DELAY,src)) + return + + if(!stripping) + if(user.get_active_hand() != held) + return + var/obj/item/weapon/holder/mobheld = held + if(istype(mobheld)&&mobheld.held_mob==src) + to_chat(user, "You can't put someone on themselves! Stop trying to break reality!") + return + + + if(stripping) + add_attack_logs(user,src,"Removed equipment from slot [target_slot]") + unEquip(target_slot) + else if(user.unEquip(held)) + equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1) + if(held.loc != src) + user.put_in_hands(held) + +// Empty out everything in the target's pockets. +/mob/living/carbon/human/proc/empty_pockets(var/mob/living/user) + if(!r_store && !l_store) + to_chat(user, "\The [src] has nothing in their pockets.") + return + if(r_store) + unEquip(r_store) + if(l_store) + unEquip(l_store) + visible_message("\The [user] empties \the [src]'s pockets!") + +// Modify the current target sensor level. +/mob/living/carbon/human/proc/toggle_sensors(var/mob/living/user) + var/obj/item/clothing/under/suit = w_uniform + if(!suit) + to_chat(user, "\The [src] is not wearing a suit with sensors.") + return + if (suit.has_sensor >= 2) + to_chat(user, "\The [src]'s suit sensor controls are locked.") + return + add_attack_logs(user,src,"Adjusted suit sensor level") + suit.set_sensors(user) + +// Remove all splints. +/mob/living/carbon/human/proc/remove_splints(var/mob/living/user) + + var/can_reach_splints = 1 + if(istype(wear_suit,/obj/item/clothing/suit/space)) + var/obj/item/clothing/suit/space/suit = wear_suit + if(suit.supporting_limbs && suit.supporting_limbs.len) + to_chat(user, "You cannot remove the splints - [src]'s [suit] is supporting some of the breaks.") + can_reach_splints = 0 + + if(can_reach_splints) + var/removed_splint + for(var/obj/item/organ/external/o in organs) + if (o && o.splinted) + var/obj/item/S = o.splinted + if(istype(S) && S.loc == o) //can only remove splints that are actually worn on the organ (deals with hardsuit splints) + S.add_fingerprint(user) + if(o.remove_splint()) + user.put_in_active_hand(S) + removed_splint = 1 + if(removed_splint) + visible_message("\The [user] removes \the [src]'s splints!") + else + to_chat(user, "\The [src] has no splints to remove.") + +// Set internals on or off. +/mob/living/carbon/human/proc/toggle_internals(var/mob/living/user) + if(internal) + internal.add_fingerprint(user) + internal = null + if(internals) + internals.icon_state = "internal0" + else + // Check for airtight mask/helmet. + if(!(istype(wear_mask, /obj/item/clothing/mask) || istype(head, /obj/item/clothing/head/helmet/space))) + return + // Find an internal source. + if(istype(back, /obj/item/weapon/tank)) + internal = back + else if(istype(s_store, /obj/item/weapon/tank)) + internal = s_store + else if(istype(belt, /obj/item/weapon/tank)) + internal = belt + + if(internal) + visible_message("\The [src] is now running on internals!") + internal.add_fingerprint(user) + if (internals) + internals.icon_state = "internal1" + else + visible_message("\The [user] disables \the [src]'s internals!") diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index b1fbafc52c2..5dc3b6e48a8 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -1,1402 +1,1428 @@ -/* - Global associative list for caching humanoid icons. - Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. -*/ -var/global/list/human_icon_cache = list() //key is incredibly complex, see update_icons_body() -var/global/list/tail_icon_cache = list() //key is [species.race_key][r_skin][g_skin][b_skin] -var/global/list/wing_icon_cache = list() // See tail. -var/global/list/light_overlay_cache = list() //see make_worn_icon() on helmets -var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() - -//////////////////////////////////////////////////////////////////////////////////////////////// -// # Human Icon Updating System -// -// This system takes care of the "icon" for human mobs. Of course humans don't just have a single -// icon+icon_state, but a combination of dozens of little sprites including including the body, -// clothing, equipment, in-universe HUD images, etc. -// -// # Basic Operation -// Whenever you do something that should update the on-mob appearance of a worn or held item, You -// will need to call the relevant update_inv_* proc. All of these are named after the variable they -// update from. They are defined at the /mob level so you don't even need to cast to carbon/human. -// -// The new system leverages SSoverlays to actually add/remove the overlays from mob.overlays -// Since SSoverlays already manages batching updates to reduce apperance churn etc, we don't need -// to worry about that. (In short, you can call add/cut overlay as many times as you want, it will -// only get assigned to the mob once per tick.) -// As a corrolary, this means users of this system do NOT need to tell the system when you're done -// making changes. -// -// There are also these special cases: -// update_icons_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc -// UpdateDamageIcon() //Handles damage overlays for brute/burn damage //(will rename this when I geta round to it) ~Carn -// update_skin() //Handles updating skin for species that have a skin overlay. -// update_bloodied() //Handles adding/clearing the blood overlays for hands & feet. Call when bloodied or cleaned. -// update_underwear() //Handles updating the sprite for underwear. -// update_hair() //Handles updating your hair and eyes overlay -// update_mutations() //Handles updating your appearance for certain mutations. e.g TK head-glows -// update_fire() //Handles overlay from being on fire. -// update_water() //Handles overlay from being submerged. -// update_surgery() //Handles overlays from open external organs. -// -// # History (i.e. I'm used to the old way, what is different?) -// You used to have to call update_icons(FALSE) if you planned to make more changes, and call update_icons(TRUE) -// on the final update. All that is gone, just call update_inv_whatever() and it handles the rest. -// -//////////////////////////////////////////////////////////////////////////////////////////////// - -//Add an entry to overlays, assuming it exists -/mob/living/carbon/human/proc/apply_layer(cache_index) - if((. = overlays_standing[cache_index])) - add_overlay(.) - -//Remove an entry from overlays, and from the list -/mob/living/carbon/human/proc/remove_layer(cache_index) - var/I = overlays_standing[cache_index] - if(I) - cut_overlay(I) - overlays_standing[cache_index] = null - -// These are used as the layers for the icons, as well as indexes in a list that holds onto them. -// Technically the layers used are all -100+layer to make them FLOAT_LAYER overlays. -//Human Overlays Indexes///////// -#define MUTATIONS_LAYER 1 //Mutations like fat, and lasereyes -#define SKIN_LAYER 2 //Skin things added by a call on species -#define BLOOD_LAYER 3 //Bloodied hands/feet/anything else -#define MOB_DAM_LAYER 4 //Injury overlay sprites like open wounds -#define SURGERY_LAYER 5 //Overlays for open surgical sites -#define UNDERWEAR_LAYER 6 //Underwear/bras/etc -#define TAIL_LOWER_LAYER 7 //Tail as viewed from the south -#define WING_LOWER_LAYER 8 //Wings as viewed from the south -#define SHOES_LAYER_ALT 9 //Shoe-slot item (when set to be under uniform via verb) -#define UNIFORM_LAYER 10 //Uniform-slot item -#define ID_LAYER 11 //ID-slot item -#define SHOES_LAYER 12 //Shoe-slot item -#define GLOVES_LAYER 13 //Glove-slot item -#define BELT_LAYER 14 //Belt-slot item -#define SUIT_LAYER 15 //Suit-slot item -#define TAIL_UPPER_LAYER 16 //Some species have tails to render (As viewed from the N, E, or W) -#define GLASSES_LAYER 17 //Eye-slot item -#define BELT_LAYER_ALT 18 //Belt-slot item (when set to be above suit via verb) -#define SUIT_STORE_LAYER 19 //Suit storage-slot item -#define BACK_LAYER 20 //Back-slot item -#define HAIR_LAYER 21 //The human's hair -#define HAIR_ACCESSORY_LAYER 22 //VOREStation edit. Simply move this up a number if things are added. -#define EARS_LAYER 23 //Both ear-slot items (combined image) -#define EYES_LAYER 24 //Mob's eyes (used for glowing eyes) -#define FACEMASK_LAYER 25 //Mask-slot item -#define HEAD_LAYER 26 //Head-slot item -#define HANDCUFF_LAYER 27 //Handcuffs, if the human is handcuffed, in a secret inv slot -#define LEGCUFF_LAYER 28 //Same as handcuffs, for legcuffs -#define L_HAND_LAYER 29 //Left-hand item -#define R_HAND_LAYER 30 //Right-hand item -#define WING_LAYER 31 //Wings or protrusions over the suit. -#define TAIL_UPPER_LAYER_ALT 32 //Modified tail-sprite layer. Tend to be larger. -#define MODIFIER_EFFECTS_LAYER 33 //Effects drawn by modifiers -#define FIRE_LAYER 34 //'Mob on fire' overlay layer -#define MOB_WATER_LAYER 35 //'Mob submerged' overlay layer -#define TARGETED_LAYER 36 //'Aimed at' overlay layer -#define TOTAL_LAYERS 36 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list. -////////////////////////////////// - -/mob/living/carbon/human - var/list/overlays_standing[TOTAL_LAYERS] - var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed - -//UPDATES OVERLAYS FROM OVERLAYS_LYING/OVERLAYS_STANDING -//I'll work on removing that stuff by rewriting some of the cloaking stuff at a later date. -/mob/living/carbon/human/update_icons() - if(QDESTROYING(src)) - return - - stack_trace("CANARY: Old human update_icons was called.") - - update_hud() //TODO: remove the need for this - - //Do any species specific layering updates, such as when hiding. - update_icon_special() - -/mob/living/carbon/human/update_transform() - /* VOREStation Edit START - // First, get the correct size. - var/desired_scale_x = icon_scale_x - var/desired_scale_y = icon_scale_y - - desired_scale_x *= species.icon_scale_x - desired_scale_y *= species.icon_scale_y - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.icon_scale_x_percent)) - desired_scale_x *= M.icon_scale_x_percent - if(!isnull(M.icon_scale_y_percent)) - desired_scale_y *= M.icon_scale_y_percent - */ - var/desired_scale_x = size_multiplier * icon_scale_x - var/desired_scale_y = size_multiplier * icon_scale_y - desired_scale_x *= species.icon_scale_x - desired_scale_y *= species.icon_scale_y - vis_height = species.icon_height - appearance_flags |= PIXEL_SCALE - if(fuzzy) - appearance_flags &= ~PIXEL_SCALE - //VOREStation Edit End - - // Regular stuff again. - var/matrix/M = matrix() - var/anim_time = 3 - - //Due to some involuntary means, you're laying now - if(lying && !resting && !sleeping) - anim_time = 1 //Thud - - if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone. - if(tail_style?.can_loaf && resting) // Only call these if we're resting? - update_tail_showing() - M.Scale(desired_scale_x, desired_scale_y) - else - var/randn = rand(1, 2) - if(randn <= 1) // randomly choose a rotation - M.Turn(-90) - else - M.Turn(90) - if(species.icon_height == 64) - M.Translate(13,-22) - else - M.Translate(1,-6) - M.Scale(desired_scale_y, desired_scale_x) - layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters - else - M.Scale(desired_scale_x, desired_scale_y)//VOREStation Edit - M.Translate(0, (vis_height/2)*(desired_scale_y-1)) //VOREStation edit - if(tail_style?.can_loaf) // VOREStation Edit: Taur Loafing - update_tail_showing() // VOREStation Edit: Taur Loafing - layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters - - animate(src, transform = M, time = anim_time) - update_icon_special() //May contain transform-altering things - -//DAMAGE OVERLAYS -//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists -/mob/living/carbon/human/UpdateDamageIcon() - if(QDESTROYING(src)) - return - - remove_layer(MOB_DAM_LAYER) - - // first check whether something actually changed about damage appearance - var/damage_appearance = "" - - for(var/obj/item/organ/external/O in organs) - if(isnull(O) || O.is_stump()) - continue - damage_appearance += O.damage_state - - if(damage_appearance == previous_damage_appearance) - // nothing to do here - return - - previous_damage_appearance = damage_appearance - - var/image/standing_image = image(icon = species.damage_overlays, icon_state = "00", layer = BODY_LAYER+MOB_DAM_LAYER) - - // blend the individual damage states with our icons - for(var/obj/item/organ/external/O in organs) - if(isnull(O) || O.is_stump() || O.is_hidden_by_sprite_accessory()) - continue - - O.update_icon() - if(O.damage_state == "00") continue - var/icon/DI - var/cache_index = "[O.damage_state]/[O.icon_name]/[species.get_blood_colour(src)]/[species.get_bodytype(src)]" - if(damage_icon_parts[cache_index] == null) - DI = icon(species.get_damage_overlays(src), O.damage_state) // the damage icon for whole human - DI.Blend(icon(species.get_damage_mask(src), O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels - DI.Blend(species.get_blood_colour(src), ICON_MULTIPLY) - damage_icon_parts[cache_index] = DI - else - DI = damage_icon_parts[cache_index] - - standing_image.add_overlay(DI) - - overlays_standing[MOB_DAM_LAYER] = standing_image - apply_layer(MOB_DAM_LAYER) - -//BASE MOB SPRITE -/mob/living/carbon/human/update_icons_body() - if(QDESTROYING(src)) - return - - var/husk_color_mod = rgb(96,88,80) - var/hulk_color_mod = rgb(48,224,40) - - var/husk = (HUSK in src.mutations) - var/fat = (FAT in src.mutations) - var/hulk = (HULK in src.mutations) - var/skeleton = (SKELETON in src.mutations) - - robolimb_count = 0 //TODO, here, really tho? - robobody_count = 0 - - //CACHING: Generate an index key from visible bodyparts. - //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. - - //Create a new, blank icon for our mob to use. - var/icon/stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") - - var/g = (gender == MALE ? "male" : "female") - var/icon_key = "[species.get_race_key(src)][g][s_tone][r_skin][g_skin][b_skin]" - if(lip_style) - icon_key += "[lip_style]" - else - icon_key += "nolips" - var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] - if(eyes) - icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])]" - else - icon_key += "[r_eyes], [g_eyes], [b_eyes]" - var/obj/item/organ/external/head/head = organs_by_name[BP_HEAD] - if(head) - if(!istype(head, /obj/item/organ/external/stump)) - if (species.selects_bodytype != SELECTS_BODYTYPE_FALSE) - var/headtype = GLOB.all_species[species.base_species]?.has_limbs[BP_HEAD] - var/obj/item/organ/external/head/headtypepath = headtype["path"] - if (headtypepath && !head.eye_icon_override) - head.eye_icon = initial(headtypepath.eye_icon) - head.eye_icon_location = initial(headtypepath.eye_icon_location) - icon_key += "[head.eye_icon]" - var/wholeicontransparent = TRUE - for(var/organ_tag in species.has_limbs) - var/obj/item/organ/external/part = organs_by_name[organ_tag] - if(isnull(part) || part.is_stump() || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. - icon_key += "0" - continue - if(part) - wholeicontransparent &&= part.transparent //VORESTATION EDIT: transparent instead of nonsolid - icon_key += "[part.species.get_race_key(part.owner)]" - icon_key += "[part.dna.GetUIState(DNA_UI_GENDER)]" - icon_key += "[part.s_tone]" - if(part.s_col && part.s_col.len >= 3) - icon_key += "[rgb(part.s_col[1],part.s_col[2],part.s_col[3])]" - if(part.body_hair && part.h_col && part.h_col.len >= 3) - icon_key += "[rgb(part.h_col[1],part.h_col[2],part.h_col[3])]" - if(species.color_mult) - icon_key += "[ICON_MULTIPLY]" - else - icon_key += "[ICON_ADD]" - else - icon_key += "#000000" - - for(var/M in part.markings) - if (part.markings[M]["on"]) - icon_key += "[M][part.markings[M]["color"]]" - - // VOREStation Edit Start - if(part.nail_polish) - icon_key += "_[part.nail_polish.icon]_[part.nail_polish.icon_state]_[part.nail_polish.color]" - // VOREStation Edit End - - if(part.robotic >= ORGAN_ROBOT) - icon_key += "2[part.model ? "-[part.model]": ""]" - robolimb_count++ - if((part.robotic == ORGAN_ROBOT || part.robotic == ORGAN_LIFELIKE) && (part.organ_tag == BP_HEAD || part.organ_tag == BP_TORSO || part.organ_tag == BP_GROIN)) - robobody_count ++ - else if(part.status & ORGAN_DEAD) - icon_key += "3" - else - icon_key += "1" - if(part.transparent) //VOREStation Edit. For better slime limbs. Avoids using solid var due to limb dropping. - icon_key += "_t" //VOREStation Edit. - - if(istype(tail_style, /datum/sprite_accessory/tail/taur)) - if(tail_style.clip_mask) //VOREStation Edit. - icon_key += tail_style.clip_mask_state - - if(tail_style) - pixel_x = tail_style.mob_offset_x - pixel_y = tail_style.mob_offset_y - default_pixel_x = tail_style.mob_offset_x - default_pixel_y = tail_style.mob_offset_y - - icon_key = "[icon_key][husk ? 1 : 0][fat ? 1 : 0][hulk ? 1 : 0][skeleton ? 1 : 0]" - var/icon/base_icon - if(human_icon_cache[icon_key]) - base_icon = human_icon_cache[icon_key] - else - //BEGIN CACHED ICON GENERATION. - var/obj/item/organ/external/chest = get_organ(BP_TORSO) - base_icon = chest.get_icon(skeleton, !wholeicontransparent) - - var/apply_extra_transparency_leg = organs_by_name[BP_L_LEG] && organs_by_name[BP_R_LEG] - var/apply_extra_transparency_foot = organs_by_name[BP_L_FOOT] && organs_by_name[BP_R_FOOT] - - var/icon/Cutter = null - var/icon_x_offset = 0 - var/icon_y_offset = 0 - - if(istype(tail_style, /datum/sprite_accessory/tail/taur)) // Tail icon 'cookie cutters' are filled in where icons are preserved. We need to invert that. - if(tail_style.clip_mask) //VOREStation Edit. - Cutter = new(icon = (tail_style.clip_mask_icon ? tail_style.clip_mask_icon : tail_style.icon), icon_state = tail_style.clip_mask_state) - - Cutter.Blend("#000000", ICON_MULTIPLY) // Make it all black. - - Cutter.SwapColor("#00000000", "#FFFFFFFF") // Everywhere empty, make white. - Cutter.SwapColor("#000000FF", "#00000000") // Everywhere black, make empty. - - Cutter.Blend("#000000", ICON_MULTIPLY) // Black again. - - icon_x_offset = tail_style.offset_x - icon_y_offset = tail_style.offset_y - - for(var/obj/item/organ/external/part in organs) - if(isnull(part) || part.is_stump() || part == chest || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. - continue - var/icon/temp = part.get_icon(skeleton, !wholeicontransparent) - - if((part.organ_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) && Cutter) - temp.Blend(Cutter, ICON_AND, x = icon_x_offset, y = icon_y_offset) - - //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST - //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) - if(part.icon_position & (LEFT | RIGHT)) - var/icon/temp2 = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") - temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) - temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) - if(!(part.icon_position & LEFT)) - temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) - if(!(part.icon_position & RIGHT)) - temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) - base_icon.Blend(temp2, ICON_OVERLAY) - temp2.Insert(temp2,"blank",dir=NORTH) //faaaaairly certain this is more efficient than reloading temp2, doing this so we don't blend the icons twice (it matters more in transparent limbs) - temp2.Insert(temp2,"blank",dir=SOUTH) - temp2.Insert(temp2,"blank",dir=EAST) - temp2.Insert(temp2,"blank",dir=WEST) - if(part.icon_position & LEFT) - temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) - if(part.icon_position & RIGHT) - temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) - if (part.transparent && !wholeicontransparent) //apply a little (a lot) extra transparency to make it look better //VORESTATION EDIT: transparent instead of nonsolid - if ((istype(part, /obj/item/organ/external/leg) && apply_extra_transparency_leg) || (istype(part, /obj/item/organ/external/foot) && apply_extra_transparency_foot)) //maybe - temp2 += rgb(,,,30) - base_icon.Blend(temp2, ICON_UNDERLAY) - else if(part.icon_position & UNDER) - base_icon.Blend(temp, ICON_UNDERLAY) - else - base_icon.Blend(temp, ICON_OVERLAY) - - if (wholeicontransparent) //because, I mean. It's basically never gonna happen that you'll have just one non-transparent limb but if you do your icon will look meh. Still good but meh, will have some areas with higher transparencies unless you're literally just a torso and a head - base_icon += rgb(,,,180) - - if(!skeleton) - if(husk) - base_icon.ColorTone(husk_color_mod) - else if(hulk) - var/list/tone = rgb2num(hulk_color_mod) - base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3])) - - //Handle husk overlay. - if(husk && ("overlay_husk" in cached_icon_states(species.icobase))) - var/icon/mask = new(base_icon) - var/icon/husk_over = new(species.icobase,"overlay_husk") - mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0) - husk_over.Blend(mask, ICON_ADD) - base_icon.Blend(husk_over, ICON_OVERLAY) - - human_icon_cache[icon_key] = base_icon - - //END CACHED ICON GENERATION. - stand_icon.Blend(base_icon,ICON_OVERLAY) - icon = stand_icon - - //tail - update_tail_showing() - update_wing_showing() - -/mob/living/carbon/human/proc/update_skin() - if(QDESTROYING(src)) - return - - remove_layer(SKIN_LAYER) - - var/image/skin = species.update_skin(src) - if(skin) - skin.layer = BODY_LAYER+SKIN_LAYER - overlays_standing[SKIN_LAYER] = skin - apply_layer(SKIN_LAYER) - -/mob/living/carbon/human/proc/update_bloodied() - if(QDESTROYING(src)) - return - - remove_layer(BLOOD_LAYER) - if(!blood_DNA && !feet_blood_DNA) - return - - var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+BLOOD_LAYER) - - //Bloody hands - if(blood_DNA) - var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "bloodyhands", layer = BODY_LAYER+BLOOD_LAYER) - bloodsies.color = hand_blood_color - both.add_overlay(bloodsies) - - //Bloody feet - if(feet_blood_DNA) - var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "shoeblood", layer = BODY_LAYER+BLOOD_LAYER) - bloodsies.color = feet_blood_color - both.add_overlay(bloodsies) - - overlays_standing[BLOOD_LAYER] = both - - apply_layer(BLOOD_LAYER) - -//UNDERWEAR OVERLAY -/mob/living/carbon/human/proc/update_underwear() - if(QDESTROYING(src)) - return - - remove_layer(UNDERWEAR_LAYER) - - if(species.appearance_flags & HAS_UNDERWEAR) - overlays_standing[UNDERWEAR_LAYER] = list() - for(var/category in all_underwear) - if(hide_underwear[category]) - continue - var/datum/category_item/underwear/UWI = all_underwear[category] - var/image/wear = UWI.generate_image(all_underwear_metadata[category], layer = BODY_LAYER+UNDERWEAR_LAYER) - overlays_standing[UNDERWEAR_LAYER] += wear - - apply_layer(UNDERWEAR_LAYER) - -//HAIR OVERLAY -/mob/living/carbon/human/proc/update_hair() - if(QDESTROYING(src)) - return - - //Reset our hair - remove_layer(HAIR_LAYER) - remove_layer(HAIR_ACCESSORY_LAYER) //VOREStation Edit - update_eyes() //Pirated out of here, for glowing eyes. - - var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) - if(!head_organ || head_organ.is_stump() ) - return - - //masks and helmets can obscure our hair. - if( (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) - return - - //base icons - var/icon/face_standing = icon(icon = 'icons/mob/human_face.dmi', icon_state = "bald_s") - - if(f_style) - var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[f_style] - if(facial_hair_style && facial_hair_style.species_allowed && (src.species.get_bodytype(src) in facial_hair_style.species_allowed)) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(facial_hair_style.do_colouration) - facial_s.Blend(rgb(r_facial, g_facial, b_facial), facial_hair_style.color_blend_mode) - - face_standing.Blend(facial_s, ICON_OVERLAY) - - if(h_style) - var/datum/sprite_accessory/hair/hair_style = hair_styles_list[h_style] - if(head && (head.flags_inv & BLOCKHEADHAIR)) - if(!(hair_style.flags & HAIR_VERY_SHORT)) - hair_style = hair_styles_list["Short Hair"] - - if(hair_style && (src.species.get_bodytype(src) in hair_style.species_allowed)) - var/icon/grad_s = null - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - var/icon/hair_s_add = new/icon("icon" = hair_style.icon_add, "icon_state" = "[hair_style.icon_state]_s") - if(hair_style.do_colouration) - if(grad_style) - grad_s = new/icon("icon" = 'icons/mob/hair_gradients.dmi', "icon_state" = GLOB.hair_gradients[grad_style]) - grad_s.Blend(hair_s, ICON_AND) - grad_s.Blend(rgb(r_grad, g_grad, b_grad), ICON_MULTIPLY) - hair_s.Blend(rgb(r_hair, g_hair, b_hair), ICON_MULTIPLY) - hair_s.Blend(hair_s_add, ICON_ADD) - if(!isnull(grad_s)) - hair_s.Blend(grad_s, ICON_OVERLAY) - - face_standing.Blend(hair_s, ICON_OVERLAY) - - var/icon/ears_s = get_ears_overlay() - - if(head_organ.transparent) //VORESTATION EDIT: transparent instead of nonsolid - face_standing += rgb(,,,120) - if (ears_s) - ears_s += rgb(,,,180) - - var/image/em_block_ears - if(ears_s) - if(ears_s.Height() > face_standing.Height()) // Tol ears - face_standing.Crop(1, 1, face_standing.Width(), ears_s.Height()) - face_standing.Blend(ears_s, ICON_OVERLAY) - if(ear_style?.em_block) - em_block_ears = em_block_image_generic(image(ears_s)) - - var/image/semifinal = image(face_standing, layer = BODY_LAYER+HAIR_LAYER, "pixel_y" = head_organ.head_offset) - if(em_block_ears) - semifinal.overlays += em_block_ears // Leaving this as overlays += - - overlays_standing[HAIR_LAYER] = semifinal - apply_layer(HAIR_LAYER) - //return //VOREStation Edit - - // VOREStation Edit - START - var/icon/hair_acc_s = get_hair_accessory_overlay() - var/image/hair_acc_s_image = null - if(hair_acc_s) - if(hair_accessory_style.ignores_lighting) - hair_acc_s_image = image(hair_acc_s) - hair_acc_s_image.plane = PLANE_LIGHTING_ABOVE - hair_acc_s_image.appearance_flags = appearance_flags - if (hair_acc_s_image) - overlays_standing[HAIR_ACCESSORY_LAYER] = hair_acc_s_image - apply_layer(HAIR_ACCESSORY_LAYER) - return - overlays_standing[HAIR_ACCESSORY_LAYER] = image(hair_acc_s, layer = BODY_LAYER+HAIR_ACCESSORY_LAYER) - apply_layer(HAIR_ACCESSORY_LAYER) - // VOREStation Edit - END - -/mob/living/carbon/human/update_eyes() - if(QDESTROYING(src)) - return - - //Reset our eyes - remove_layer(EYES_LAYER) - - //TODO: Probably redo this. I know I wrote it, but... - - //This is ONLY for glowing eyes for now. Boring flat eyes are done by the head's own proc. - if(!species.has_glowing_eyes) - return - - //Our glowy eyes should be hidden if some equipment hides them. - if(!should_have_organ(O_EYES) || (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) - return - - //Get the head, we'll need it later. - var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) - if(!head_organ || head_organ.is_stump() ) - return - - //The eyes store the color themselves, funny enough. - var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] - if(!head_organ.eye_icon) - return - - var/icon/eyes_icon = new/icon(head_organ.eye_icon_location, head_organ.eye_icon) //VOREStation Edit - if(eyes) - eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD) - else - eyes_icon.Blend(rgb(128,0,0), ICON_ADD) - - // Convert to emissive at some point - if (head_organ.transparent) //VOREStation Edit: transparent instead of nonsolid - eyes_icon += rgb(,,,180) - - var/image/eyes_image = image(eyes_icon) - eyes_image.plane = PLANE_LIGHTING_ABOVE - eyes_image.appearance_flags = appearance_flags - - overlays_standing[EYES_LAYER] = eyes_image - apply_layer(EYES_LAYER) - -/mob/living/carbon/human/update_mutations() - if(QDESTROYING(src)) - return - - remove_layer(MUTATIONS_LAYER) - - if(!LAZYLEN(mutations)) - return //No mutations, no icons. - - //TODO: THIS PROC??? - var/fat - if(FAT in mutations) - fat = "fat" - - var/image/standing = image(icon = 'icons/effects/genetics.dmi', layer = BODY_LAYER+MUTATIONS_LAYER) - var/g = gender == FEMALE ? "f" : "m" - - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - var/underlay = gene.OnDrawUnderlays(src,g,fat) - if(underlay) - standing.underlays += underlay - - for(var/mut in mutations) - if(mut == LASER) - standing.overlays += "lasereyes_s" // Leaving this as overlays += - - overlays_standing[MUTATIONS_LAYER] = standing - apply_layer(MUTATIONS_LAYER) - -/* --------------------------------------- */ -//Recomputes every icon on the mob. Expensive. -//Useful if the species changed, or there's some -//other drastic body-shape change, but otherwise avoid. -/mob/living/carbon/human/regenerate_icons() - ..() - if(transforming || QDELETED(src)) - return - - update_icons_body() - UpdateDamageIcon() - update_mutations() - update_skin() - update_underwear() - update_hair() - update_inv_w_uniform() - update_inv_wear_id() - update_inv_gloves() - update_inv_glasses() - update_inv_ears() - update_inv_shoes() - update_inv_s_store() - update_inv_wear_mask() - update_inv_head() - update_inv_belt() - update_inv_back() - update_inv_wear_suit() - update_inv_r_hand() - update_inv_l_hand() - update_inv_handcuffed() - update_inv_legcuffed() - //update_inv_pockets() //Doesn't do anything - update_fire() - update_water() - update_surgery() - -/* --------------------------------------- */ -//vvvvvv UPDATE_INV PROCS vvvvvv - -/mob/living/carbon/human/update_inv_w_uniform() - if(QDESTROYING(src)) - return - - remove_layer(UNIFORM_LAYER) - - //Shoes can be affected by uniform being drawn onto them - update_inv_shoes() - - if(!w_uniform) - return - - if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT) && !istype(wear_suit, /obj/item/clothing/suit/space/rig)) - return //Wearing a suit that prevents uniform rendering - - var/obj/item/clothing/under/under = w_uniform - - var/uniform_sprite - if(istype(under) && !isnull(under.update_icon_define)) - uniform_sprite = under.update_icon_define - else - uniform_sprite = INV_W_UNIFORM_DEF_ICON - - //Build a uniform sprite - var/icon/c_mask = tail_style?.clip_mask - if(c_mask) - var/obj/item/clothing/suit/S = wear_suit - if((wear_suit?.flags_inv & HIDETAIL) || (istype(S) && S.taurized)) // Reasons to not mask: 1. If you're wearing a suit that hides the tail or if you're wearing a taurized suit. - c_mask = null - overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = uniform_sprite, default_layer = UNIFORM_LAYER, clip_mask = c_mask) - apply_layer(UNIFORM_LAYER) - -/mob/living/carbon/human/update_inv_wear_id() - if(QDESTROYING(src)) - return - - remove_layer(ID_LAYER) - - if(!wear_id) - return //Not wearing an ID - - //Only draw the ID on the mob if the uniform allows for it - if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - if(U.displays_id) - overlays_standing[ID_LAYER] = wear_id.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_id_str, default_icon = INV_WEAR_ID_DEF_ICON, default_layer = ID_LAYER) - - apply_layer(ID_LAYER) - -/mob/living/carbon/human/update_inv_gloves() - if(QDESTROYING(src)) - return - - remove_layer(GLOVES_LAYER) - - if(!gloves) - return //No gloves, no reason to be here. - - overlays_standing[GLOVES_LAYER] = gloves.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_GLOVES_DEF_ICON, default_layer = GLOVES_LAYER) - - apply_layer(GLOVES_LAYER) - -/mob/living/carbon/human/update_inv_glasses() - if(QDESTROYING(src)) - return - - remove_layer(GLASSES_LAYER) - - if(!glasses) - return //Not wearing glasses, no need to update anything. - - overlays_standing[GLASSES_LAYER] = glasses.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_EYES_DEF_ICON, default_layer = GLASSES_LAYER) - - apply_layer(GLASSES_LAYER) - -/mob/living/carbon/human/update_inv_ears() - if(QDESTROYING(src)) - return - - remove_layer(EARS_LAYER) - - if((head && head.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR)) || (wear_mask && wear_mask.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR))) - return //Ears are blocked (by hair being blocked, overloaded) - - if(!l_ear && !r_ear) - return //Why bother, if no ear sprites - - // Blank image upon which to layer left & right overlays. - var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+EARS_LAYER) - - if(l_ear) - var/image/standing = l_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_l_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) - both.add_overlay(standing) - - if(r_ear) - var/image/standing = r_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_r_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) - both.add_overlay(standing) - - overlays_standing[EARS_LAYER] = both - apply_layer(EARS_LAYER) - -/mob/living/carbon/human/update_inv_shoes() - if(QDESTROYING(src)) - return - - remove_layer(SHOES_LAYER) - remove_layer(SHOES_LAYER_ALT) //Dumb alternate layer for shoes being under the uniform. - - if(!shoes || (wear_suit && wear_suit.flags_inv & HIDESHOES) || (w_uniform && w_uniform.flags_inv & HIDESHOES)) - return //Either nothing to draw, or it'd be hidden. - - for(var/f in list(BP_L_FOOT, BP_R_FOOT)) - var/obj/item/organ/external/foot/foot = get_organ(f) - if(istype(foot) && foot.is_hidden_by_sprite_accessory(clothing_only = TRUE)) //If either foot is hidden by the tail, don't render footwear. - return - - //Allow for shoe layer toggle nonsense - var/shoe_layer = SHOES_LAYER - if(istype(shoes, /obj/item/clothing/shoes)) - var/obj/item/clothing/shoes/ushoes = shoes - if(ushoes.shoes_under_pants == 1) - shoe_layer = SHOES_LAYER_ALT - - //NB: the use of a var for the layer on this one - overlays_standing[shoe_layer] = shoes.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_shoes_str, default_icon = INV_FEET_DEF_ICON, default_layer = shoe_layer) - - apply_layer(SHOES_LAYER) - apply_layer(SHOES_LAYER_ALT) - -/mob/living/carbon/human/update_inv_s_store() - if(QDESTROYING(src)) - return - - remove_layer(SUIT_STORE_LAYER) - - if(!s_store) - return //Why bother, nothing there. - - //TODO, this is unlike the rest of the things - //Basically has no variety in slot icon choices at all. WHY SPECIES ONLY?? - var/t_state = s_store.item_state - if(!t_state) - t_state = s_store.icon_state - overlays_standing[SUIT_STORE_LAYER] = image(icon = species.suit_storage_icon, icon_state = t_state, layer = BODY_LAYER+SUIT_STORE_LAYER) - - apply_layer(SUIT_STORE_LAYER) - -/mob/living/carbon/human/update_inv_head() - if(QDESTROYING(src)) - return - - remove_layer(HEAD_LAYER) - - if(!head) - return //No head item, why bother. - - overlays_standing[HEAD_LAYER] = head.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER) - - apply_layer(HEAD_LAYER) - -/mob/living/carbon/human/update_inv_belt() - if(QDESTROYING(src)) - return - - remove_layer(BELT_LAYER) - remove_layer(BELT_LAYER_ALT) //Because you can toggle belt layer with a verb - - if(!belt) - return //No belt, why bother. - - //Toggle for belt layering with uniform - var/belt_layer = BELT_LAYER - if(istype(belt, /obj/item/weapon/storage/belt)) - var/obj/item/weapon/storage/belt/ubelt = belt - if(ubelt.show_above_suit) - belt_layer = BELT_LAYER_ALT - - - var/icon/c_mask = tail_style?.clip_mask - - //NB: this uses a var from above - overlays_standing[belt_layer] = belt.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_belt_str, default_icon = INV_BELT_DEF_ICON, default_layer = belt_layer, clip_mask = c_mask) - - apply_layer(belt_layer) - -/mob/living/carbon/human/update_inv_wear_suit() - if(QDESTROYING(src)) - return - - remove_layer(SUIT_LAYER) - - //Hide/show other layers if necessary - update_inv_w_uniform() - update_inv_shoes() - update_tail_showing() - update_wing_showing() - - if(!wear_suit) - return //No point, no suit. - - var/obj/item/clothing/suit/suit = wear_suit - var/suit_sprite - - if(istype(suit) && !isnull(suit.update_icon_define)) - suit_sprite = suit.update_icon_define - else - suit_sprite = INV_SUIT_DEF_ICON - - var/icon/c_mask = null - var/tail_is_rendered = (overlays_standing[TAIL_UPPER_LAYER] || overlays_standing[TAIL_UPPER_LAYER_ALT] || overlays_standing[TAIL_LOWER_LAYER]) - var/valid_clip_mask = tail_style?.clip_mask - - if(tail_is_rendered && valid_clip_mask && !(istype(suit) && suit.taurized)) //Clip the lower half of the suit off using the tail's clip mask for taurs since taur bodies aren't hidden. - c_mask = valid_clip_mask - overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = suit_sprite, default_layer = SUIT_LAYER, clip_mask = c_mask) - - apply_layer(SUIT_LAYER) - -/mob/living/carbon/human/update_inv_pockets() - stack_trace("Someone called update_inv_pockets even though it's dumb") - -/mob/living/carbon/human/update_inv_wear_mask() - if(QDESTROYING(src)) - return - - remove_layer(FACEMASK_LAYER) - - if(!wear_mask || (head && head.flags_inv & HIDEMASK)) - return //Why bother, nothing in mask slot. - - overlays_standing[FACEMASK_LAYER] = wear_mask.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_mask_str, default_icon = INV_MASK_DEF_ICON, default_layer = FACEMASK_LAYER) - - apply_layer(FACEMASK_LAYER) - -/mob/living/carbon/human/update_inv_back() - if(QDESTROYING(src)) - return - - remove_layer(BACK_LAYER) - - if(!back) - return //Why do anything - - var/icon/c_mask = tail_style?.clip_mask - if(c_mask) - if(istype(back, /obj/item/weapon/storage/backpack/saddlebag) || istype(back, /obj/item/weapon/storage/backpack/saddlebag_common)) - c_mask = null - - overlays_standing[BACK_LAYER] = back.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_back_str, default_icon = INV_BACK_DEF_ICON, default_layer = BACK_LAYER, clip_mask = c_mask) - - apply_layer(BACK_LAYER) - -//TODO: Carbon procs in my human update_icons?? -/mob/living/carbon/human/update_hud() //TODO: do away with this if possible - if(QDESTROYING(src)) - return - - if(client) - client.screen |= contents - if(hud_used) - hud_used.hidden_inventory_update() //Updates the screenloc of the items on the 'other' inventory bar - -//update whether handcuffs appears on our hud. -/mob/living/carbon/proc/update_hud_handcuffed() - if(QDESTROYING(src)) - return - - if(hud_used && hud_used.l_hand_hud_object && hud_used.r_hand_hud_object) - hud_used.l_hand_hud_object.update_icon() - hud_used.r_hand_hud_object.update_icon() - -/mob/living/carbon/human/update_inv_handcuffed() - if(QDESTROYING(src)) - return - - remove_layer(HANDCUFF_LAYER) - update_hud_handcuffed() //TODO - - if(!handcuffed) - return //Not cuffed, why bother - - overlays_standing[HANDCUFF_LAYER] = handcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_handcuffed_str, default_icon = INV_HCUFF_DEF_ICON, default_layer = HANDCUFF_LAYER) - - apply_layer(HANDCUFF_LAYER) - -/mob/living/carbon/human/update_inv_legcuffed() - if(QDESTROYING(src)) - return - - clear_alert("legcuffed") - remove_layer(LEGCUFF_LAYER) - - if(!legcuffed) - return //Not legcuffed, why bother. - - throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) - - overlays_standing[LEGCUFF_LAYER] = legcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_legcuffed_str, default_icon = INV_LCUFF_DEF_ICON, default_layer = LEGCUFF_LAYER) - - apply_layer(LEGCUFF_LAYER) - -/mob/living/carbon/human/update_inv_r_hand() - if(QDESTROYING(src)) - return - - remove_layer(R_HAND_LAYER) - - if(!r_hand) - return //No hand, no bother. - - overlays_standing[R_HAND_LAYER] = r_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_r_hand_str, default_icon = INV_R_HAND_DEF_ICON, default_layer = R_HAND_LAYER) - - apply_layer(R_HAND_LAYER) - -/mob/living/carbon/human/update_inv_l_hand() - if(QDESTROYING(src)) - return - - remove_layer(L_HAND_LAYER) - - if(!l_hand) - return //No hand, no bother. - - overlays_standing[L_HAND_LAYER] = l_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_l_hand_str, default_icon = INV_L_HAND_DEF_ICON, default_layer = L_HAND_LAYER) - - apply_layer(L_HAND_LAYER) - -/mob/living/carbon/human/proc/get_tail_layer() - var/list/lower_layer_dirs = list(SOUTH) - if(tail_style) - lower_layer_dirs = tail_style.lower_layer_dirs.Copy() - - if(dir in lower_layer_dirs) - return TAIL_LOWER_LAYER - else - return TAIL_UPPER_LAYER - -/mob/living/carbon/human/proc/update_tail_showing() - if(QDESTROYING(src)) - return - - remove_layer(TAIL_UPPER_LAYER) - remove_layer(TAIL_UPPER_LAYER_ALT) - remove_layer(TAIL_LOWER_LAYER) - - var/tail_layer = get_tail_layer() - if(src.tail_style && src.tail_style.clip_mask_state) - tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything - if(tail_alt && tail_layer == TAIL_UPPER_LAYER) - tail_layer = TAIL_UPPER_LAYER_ALT - - var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] - - var/image/tail_image = get_tail_image() - if(tail_image) - tail_image.layer = BODY_LAYER+tail_layer - tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid - overlays_standing[tail_layer] = tail_image - apply_layer(tail_layer) - return - - var/species_tail = species.get_tail(src) // Species tail icon_state prefix. - - //This one is actually not that bad I guess. - if(species_tail && !(wear_suit && wear_suit.flags_inv & HIDETAIL)) - var/icon/tail_s = get_tail_icon() - tail_image = image(icon = tail_s, icon_state = "[species_tail]_s", layer = BODY_LAYER+tail_layer) - tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid - overlays_standing[tail_layer] = tail_image - animate_tail_reset() - -//TODO: Is this the appropriate place for this, and not on species...? -/mob/living/carbon/human/proc/get_tail_icon() - var/icon_key = "[species.get_race_key(src)][r_skin][g_skin][b_skin][r_hair][g_hair][b_hair]" - var/icon/tail_icon = tail_icon_cache[icon_key] - if(!tail_icon) - //generate a new one - var/species_tail_anim = species.get_tail_animation(src) - if(!species_tail_anim && species.icobase_tail) species_tail_anim = species.icobase //Allow override of file for non-animated tails - if(!species_tail_anim) species_tail_anim = 'icons/effects/species.dmi' - tail_icon = new/icon(species_tail_anim) - tail_icon.Blend(rgb(r_skin, g_skin, b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - // The following will not work with animated tails. - var/use_species_tail = species.get_tail_hair(src) - if(use_species_tail) - var/icon/hair_icon = icon('icons/effects/species.dmi', "[species.get_tail(src)]_[use_species_tail]") - hair_icon.Blend(rgb(r_hair, g_hair, b_hair), species.color_mult ? ICON_MULTIPLY : ICON_ADD) //Check for species color_mult - tail_icon.Blend(hair_icon, ICON_OVERLAY) - tail_icon_cache[icon_key] = tail_icon - - return tail_icon - -/mob/living/carbon/human/proc/set_tail_state(var/t_state) - var/tail_layer = get_tail_layer() - if(src.tail_style && src.tail_style.clip_mask_state) - tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything - if(tail_alt && tail_layer == TAIL_UPPER_LAYER) - tail_layer = TAIL_UPPER_LAYER_ALT - var/image/tail_overlay = overlays_standing[tail_layer] - - remove_layer(TAIL_UPPER_LAYER) - remove_layer(TAIL_UPPER_LAYER_ALT) - remove_layer(TAIL_LOWER_LAYER) - - if(tail_overlay) - overlays_standing[tail_layer] = tail_overlay - if(species.get_tail_animation(src)) - tail_overlay.icon_state = t_state - . = tail_overlay - - apply_layer(tail_layer) - -//Not really once, since BYOND can't do that. -//Update this if the ability to flick() images or make looping animation start at the first frame is ever added. -//You can sort of flick images now with flick_overlay -Aro -/mob/living/carbon/human/proc/animate_tail_once() - if(QDESTROYING(src)) - return - - var/t_state = "[species.get_tail(src)]_once" - var/tail_layer = get_tail_layer() - if(src.tail_style && src.tail_style.clip_mask_state) - tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything - - var/image/tail_overlay = overlays_standing[tail_layer] - if(tail_overlay && tail_overlay.icon_state == t_state) - return //let the existing animation finish - - tail_overlay = set_tail_state(t_state) // Calls remove_layer & apply_layer - if(tail_overlay) - spawn(20) - //check that the animation hasn't changed in the meantime - if(overlays_standing[tail_layer] == tail_overlay && tail_overlay.icon_state == t_state) - animate_tail_stop() - -/mob/living/carbon/human/proc/animate_tail_start() - if(QDESTROYING(src)) - return - - set_tail_state("[species.get_tail(src)]_slow[rand(0,9)]") - -/mob/living/carbon/human/proc/animate_tail_fast() - if(QDESTROYING(src)) - return - - set_tail_state("[species.get_tail(src)]_loop[rand(0,9)]") - -/mob/living/carbon/human/proc/animate_tail_reset() - if(QDESTROYING(src)) - return - - if(stat != DEAD) - set_tail_state("[species.get_tail(src)]_idle[rand(0,9)]") - else - set_tail_state("[species.get_tail(src)]_static") - toggle_tail(FALSE) //So tails stop when someone dies. TODO - Fix this hack ~Leshana - -/mob/living/carbon/human/proc/animate_tail_stop() - if(QDESTROYING(src)) - return - - set_tail_state("[species.get_tail(src)]_static") - -/mob/living/carbon/human/proc/update_wing_showing() - if(QDESTROYING(src)) - return - - remove_layer(WING_LAYER) - remove_layer(WING_LOWER_LAYER) - - var/image/wing_image = get_wing_image(FALSE) - - var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] - - if(wing_image) - wing_image.layer = BODY_LAYER+WING_LAYER - wing_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid - overlays_standing[WING_LAYER] = wing_image - if(wing_style && wing_style.multi_dir) - wing_image = get_wing_image(TRUE) - if(wing_image) - wing_image.layer = BODY_LAYER+WING_LOWER_LAYER - overlays_standing[WING_LOWER_LAYER] = wing_image - - apply_layer(WING_LAYER) - apply_layer(WING_LOWER_LAYER) - -/mob/living/carbon/human/update_modifier_visuals() - if(QDESTROYING(src)) - return - - remove_layer(MODIFIER_EFFECTS_LAYER) - - if(!LAZYLEN(modifiers)) - return //No modifiers, no effects. - - var/image/effects = new() - for(var/datum/modifier/M in modifiers) - if(M.mob_overlay_state) - if(M.icon_override) //VOREStation Edit. Override for the modifer icon. - var/image/I = image(icon = 'icons/mob/modifier_effects_vr.dmi', icon_state = M.mob_overlay_state) - I.color = M.effect_color - effects.overlays += I // Leaving this as overlays += - else - var/image/I = image(icon = 'icons/mob/modifier_effects.dmi', icon_state = M.mob_overlay_state) - I.color = M.effect_color - effects.overlays += I // Leaving this as overlays += - - overlays_standing[MODIFIER_EFFECTS_LAYER] = effects - - apply_layer(MODIFIER_EFFECTS_LAYER) - -/mob/living/carbon/human/update_fire() - if(QDESTROYING(src)) - return - - remove_layer(FIRE_LAYER) - - if(!on_fire) - return - - overlays_standing[FIRE_LAYER] = image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state(), layer = BODY_LAYER+FIRE_LAYER) - - apply_layer(FIRE_LAYER) - -/mob/living/carbon/human/update_water() - if(QDESTROYING(src)) - return - - remove_layer(MOB_WATER_LAYER) - - var/depth = check_submerged() - if(!depth || lying) - return - - var/atom/A = loc // We'd better be swimming and on a turf - var/image/I = image(icon = 'icons/mob/submerged.dmi', icon_state = "human_swimming_[depth]", layer = BODY_LAYER+MOB_WATER_LAYER) //TODO: Improve - I.color = A.color - overlays_standing[MOB_WATER_LAYER] = I - - apply_layer(MOB_WATER_LAYER) - -/mob/living/carbon/human/proc/update_surgery() - if(QDESTROYING(src)) - return - - remove_layer(SURGERY_LAYER) - - var/image/total = new - for(var/obj/item/organ/external/E in organs) - if(E.open) - var/image/I = image(icon = 'icons/mob/surgery.dmi', icon_state = "[E.icon_name][round(E.open)]", layer = BODY_LAYER+SURGERY_LAYER) - total.overlays += I // Leaving this as overlays += - - if(total.overlays.len) - overlays_standing[SURGERY_LAYER] = total - apply_layer(SURGERY_LAYER) - -/mob/living/carbon/human/proc/get_wing_image(var/under_layer) - if(QDESTROYING(src)) - return - - //If you are FBP with wing style and didn't set a custom one - if(synthetic && synthetic.includes_wing && !wing_style && !wings_hidden) //VOREStation Edit - var/icon/wing_s = new/icon("icon" = synthetic.icon, "icon_state" = "wing") //I dunno. If synths have some custom wing? - wing_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - var/image/working = image(wing_s) - working.overlays += em_block_image_generic(working) // Leaving this as overlays += - return working - - //If you have custom wings selected - if(wing_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL) && !wings_hidden) //VOREStation Edit - var/wing_state = (flapping && wing_style.ani_state) ? wing_style.ani_state : wing_style.icon_state - if(wing_style.multi_dir) - wing_state += "_[under_layer ? "back" : "front"]" - var/icon/wing_s = new/icon("icon" = wing_style.icon, "icon_state" = wing_state) - if(wing_style.do_colouration) - wing_s.Blend(rgb(src.r_wing, src.g_wing, src.b_wing), wing_style.color_blend_mode) - if(wing_style.extra_overlay) - var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay) - overlay.Blend(rgb(src.r_wing2, src.g_wing2, src.b_wing2), wing_style.color_blend_mode) - wing_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - if(wing_style.extra_overlay2) - var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2) - if(wing_style.ani_state) - overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2_w) - overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) - wing_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - else - overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) - wing_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - var/image/working = image(wing_s) - if(wing_style.em_block) - working.overlays += em_block_image_generic(working) // Leaving this as overlays += - working.pixel_x -= wing_style.wing_offset - return working - -/mob/living/carbon/human/proc/get_ears_overlay() - //If you are FBP with ear style and didn't set a custom one - var/datum/robolimb/model = isSynthetic() - if(istype(model) && model.includes_ears && !ear_style) - var/icon/ears_s = new/icon("icon" = synthetic.icon, "icon_state" = "ears") - ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - return ears_s - - if(ear_style && !(head && (head.flags_inv & BLOCKHEADHAIR))) - var/icon/ears_s = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.icon_state) - if(ear_style.do_colouration) - ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), ear_style.color_blend_mode) - if(ear_style.extra_overlay) - var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay) - overlay.Blend(rgb(src.r_ears2, src.g_ears2, src.b_ears2), ear_style.color_blend_mode) - ears_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - if(ear_style.extra_overlay2) //MORE COLOURS IS BETTERER - var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay2) - overlay.Blend(rgb(src.r_ears3, src.g_ears3, src.b_ears3), ear_style.color_blend_mode) - ears_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - return ears_s - return null - - -/mob/living/carbon/human/proc/get_tail_image() - //If you are FBP with tail style and didn't set a custom one - var/datum/robolimb/model = isSynthetic() - if(istype(model) && model.includes_tail && !tail_style && !tail_hidden) - var/icon/tail_s = new/icon("icon" = synthetic.icon, "icon_state" = "tail") - tail_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) - return image(tail_s) - - //If you have a custom tail selected - if(tail_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL && !istaurtail(tail_style)) && !tail_hidden) - var/icon/tail_s = new/icon("icon" = (tail_style.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = (wagging && tail_style.ani_state ? tail_style.ani_state : tail_style.icon_state)) //CHOMPEdit - if(tail_style.can_loaf && !is_shifted) - pixel_y = (resting) ? -tail_style.loaf_offset*size_multiplier : default_pixel_y //move player down, then taur up, to fit the overlays correctly // VOREStation Edit: Taur Loafing - if(tail_style.do_colouration) - tail_s.Blend(rgb(src.r_tail, src.g_tail, src.b_tail), tail_style.color_blend_mode) - if(tail_style.extra_overlay) - var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay) //CHOMPEdit - if(wagging && tail_style.ani_state) - overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay_w) - overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - else - overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - - if(tail_style.extra_overlay2) - var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay2) //CHOMPEdit - if(wagging && tail_style.ani_state) - overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay2_w) - overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - else - overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - - var/image/working = image(tail_s) - if(tail_style.em_block) - working.overlays += em_block_image_generic(working) // Leaving this as overlays += - - if(istaurtail(tail_style)) - var/datum/sprite_accessory/tail/taur/taurtype = tail_style - working.pixel_x = tail_style.offset_x - working.pixel_y = tail_style.offset_y - if(taurtype.can_ride && !riding_datum) - riding_datum = new /datum/riding/taur(src) - verbs |= /mob/living/carbon/human/proc/taur_mount - verbs |= /mob/living/proc/toggle_rider_reins - else if(islongtail(tail_style)) - working.pixel_x = tail_style.offset_x - working.pixel_y = tail_style.offset_y - return working - return null - -// TODO - Move this to where it should go ~Leshana -/mob/living/proc/stop_flying() - if(QDESTROYING(src)) - return - flying = FALSE - return 1 - -/mob/living/carbon/human/stop_flying() - if((. = ..())) - update_wing_showing() - -//Human Overlays Indexes///////// -#undef MUTATIONS_LAYER -#undef SKIN_LAYER -#undef MOB_DAM_LAYER -#undef SURGERY_LAYER -#undef UNDERWEAR_LAYER -#undef SHOES_LAYER_ALT -#undef UNIFORM_LAYER -#undef ID_LAYER -#undef SHOES_LAYER -#undef GLOVES_LAYER -#undef BELT_LAYER -#undef SUIT_LAYER -#undef TAIL_UPPER_LAYER -#undef TAIL_LOWER_LAYER -#undef GLASSES_LAYER -#undef BELT_LAYER_ALT -#undef SUIT_STORE_LAYER -#undef BACK_LAYER -#undef HAIR_LAYER -#undef EARS_LAYER -#undef EYES_LAYER -#undef FACEMASK_LAYER -#undef HEAD_LAYER -#undef HANDCUFF_LAYER -#undef LEGCUFF_LAYER -#undef L_HAND_LAYER -#undef R_HAND_LAYER -#undef MODIFIER_EFFECTS_LAYER -#undef FIRE_LAYER -#undef WATER_LAYER -#undef TARGETED_LAYER -#undef TOTAL_LAYERS +/* + Global associative list for caching humanoid icons. + Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. +*/ +var/global/list/human_icon_cache = list() //key is incredibly complex, see update_icons_body() +var/global/list/tail_icon_cache = list() //key is [species.race_key][r_skin][g_skin][b_skin] +var/global/list/wing_icon_cache = list() // See tail. +var/global/list/light_overlay_cache = list() //see make_worn_icon() on helmets +var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() + +//////////////////////////////////////////////////////////////////////////////////////////////// +// # Human Icon Updating System +// +// This system takes care of the "icon" for human mobs. Of course humans don't just have a single +// icon+icon_state, but a combination of dozens of little sprites including including the body, +// clothing, equipment, in-universe HUD images, etc. +// +// # Basic Operation +// Whenever you do something that should update the on-mob appearance of a worn or held item, You +// will need to call the relevant update_inv_* proc. All of these are named after the variable they +// update from. They are defined at the /mob level so you don't even need to cast to carbon/human. +// +// The new system leverages SSoverlays to actually add/remove the overlays from mob.overlays +// Since SSoverlays already manages batching updates to reduce apperance churn etc, we don't need +// to worry about that. (In short, you can call add/cut overlay as many times as you want, it will +// only get assigned to the mob once per tick.) +// As a corrolary, this means users of this system do NOT need to tell the system when you're done +// making changes. +// +// There are also these special cases: +// update_icons_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc +// UpdateDamageIcon() //Handles damage overlays for brute/burn damage //(will rename this when I geta round to it) ~Carn +// update_skin() //Handles updating skin for species that have a skin overlay. +// update_bloodied() //Handles adding/clearing the blood overlays for hands & feet. Call when bloodied or cleaned. +// update_underwear() //Handles updating the sprite for underwear. +// update_hair() //Handles updating your hair and eyes overlay +// update_mutations() //Handles updating your appearance for certain mutations. e.g TK head-glows +// update_fire() //Handles overlay from being on fire. +// update_water() //Handles overlay from being submerged. +// update_surgery() //Handles overlays from open external organs. +// +// # History (i.e. I'm used to the old way, what is different?) +// You used to have to call update_icons(FALSE) if you planned to make more changes, and call update_icons(TRUE) +// on the final update. All that is gone, just call update_inv_whatever() and it handles the rest. +// +//////////////////////////////////////////////////////////////////////////////////////////////// + +//Add an entry to overlays, assuming it exists +/mob/living/carbon/human/proc/apply_layer(cache_index) + if((. = overlays_standing[cache_index])) + add_overlay(.) + +//Remove an entry from overlays, and from the list +/mob/living/carbon/human/proc/remove_layer(cache_index) + var/I = overlays_standing[cache_index] + if(I) + cut_overlay(I) + overlays_standing[cache_index] = null + +// These are used as the layers for the icons, as well as indexes in a list that holds onto them. +// Technically the layers used are all -100+layer to make them FLOAT_LAYER overlays. +//Human Overlays Indexes///////// +#define MUTATIONS_LAYER 1 //Mutations like fat, and lasereyes +#define SKIN_LAYER 2 //Skin things added by a call on species +#define BLOOD_LAYER 3 //Bloodied hands/feet/anything else +#define MOB_DAM_LAYER 4 //Injury overlay sprites like open wounds +#define SURGERY_LAYER 5 //Overlays for open surgical sites +#define UNDERWEAR_LAYER 6 //Underwear/bras/etc +#define TAIL_LOWER_LAYER 7 //Tail as viewed from the south +#define WING_LOWER_LAYER 8 //Wings as viewed from the south +#define SHOES_LAYER_ALT 9 //Shoe-slot item (when set to be under uniform via verb) +#define UNIFORM_LAYER 10 //Uniform-slot item +#define ID_LAYER 11 //ID-slot item +#define SHOES_LAYER 12 //Shoe-slot item +#define GLOVES_LAYER 13 //Glove-slot item +#define BELT_LAYER 14 //Belt-slot item +#define SUIT_LAYER 15 //Suit-slot item +#define TAIL_UPPER_LAYER 16 //Some species have tails to render (As viewed from the N, E, or W) +#define GLASSES_LAYER 17 //Eye-slot item +#define BELT_LAYER_ALT 18 //Belt-slot item (when set to be above suit via verb) +#define SUIT_STORE_LAYER 19 //Suit storage-slot item +#define BACK_LAYER 20 //Back-slot item +#define HAIR_LAYER 21 //The human's hair +#define HAIR_ACCESSORY_LAYER 22 //VOREStation edit. Simply move this up a number if things are added. +#define EARS_LAYER 23 //Both ear-slot items (combined image) +#define EYES_LAYER 24 //Mob's eyes (used for glowing eyes) +#define FACEMASK_LAYER 25 //Mask-slot item +#define GLASSES_LAYER_ALT 26 //So some glasses can appear on top of hair and things +#define HEAD_LAYER 27 //Head-slot item +#define HANDCUFF_LAYER 28 //Handcuffs, if the human is handcuffed, in a secret inv slot +#define LEGCUFF_LAYER 29 //Same as handcuffs, for legcuffs +#define L_HAND_LAYER 30 //Left-hand item +#define R_HAND_LAYER 31 //Right-hand item +#define WING_LAYER 32 //Wings or protrusions over the suit. +#define TAIL_UPPER_LAYER_ALT 33 //Modified tail-sprite layer. Tend to be larger. +#define MODIFIER_EFFECTS_LAYER 34 //Effects drawn by modifiers +#define FIRE_LAYER 35 //'Mob on fire' overlay layer +#define MOB_WATER_LAYER 36 //'Mob submerged' overlay layer +#define TARGETED_LAYER 37 //'Aimed at' overlay layer +#define TOTAL_LAYERS 37 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list. +////////////////////////////////// + +/mob/living/carbon/human + var/list/overlays_standing[TOTAL_LAYERS] + var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed + +//UPDATES OVERLAYS FROM OVERLAYS_LYING/OVERLAYS_STANDING +//I'll work on removing that stuff by rewriting some of the cloaking stuff at a later date. +/mob/living/carbon/human/update_icons() + if(QDESTROYING(src)) + return + + stack_trace("CANARY: Old human update_icons was called.") + + update_hud() //TODO: remove the need for this + + //Do any species specific layering updates, such as when hiding. + update_icon_special() + +/mob/living/carbon/human/update_transform(var/instant = FALSE) + /* VOREStation Edit START + // First, get the correct size. + var/desired_scale_x = icon_scale_x + var/desired_scale_y = icon_scale_y + + desired_scale_x *= species.icon_scale_x + desired_scale_y *= species.icon_scale_y + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.icon_scale_x_percent)) + desired_scale_x *= M.icon_scale_x_percent + if(!isnull(M.icon_scale_y_percent)) + desired_scale_y *= M.icon_scale_y_percent + */ + var/desired_scale_x = size_multiplier * icon_scale_x + var/desired_scale_y = size_multiplier * icon_scale_y + desired_scale_x *= species.icon_scale_x + desired_scale_y *= species.icon_scale_y + var/cent_offset = species.center_offset + if(fuzzy || offset_override || dir == EAST || dir == WEST) + cent_offset = 0 + vis_height = species.icon_height + appearance_flags |= PIXEL_SCALE + if(fuzzy) + appearance_flags &= ~PIXEL_SCALE + //VOREStation Edit End + + // Regular stuff again. + var/matrix/M = matrix() + var/anim_time = 3 + + //Due to some involuntary means, you're laying now + if(lying && !resting && !sleeping) + anim_time = 1 //Thud + + if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone. + if(tail_style?.can_loaf && resting) // Only call these if we're resting? + update_tail_showing() + M.Scale(desired_scale_x, desired_scale_y) + else + var/randn = rand(1, 2) + if(randn <= 1) // randomly choose a rotation + M.Turn(-90) + else + M.Turn(90) + if(species.icon_height == 64) + M.Translate(13,-22) + else + M.Translate(1,-6) + M.Scale(desired_scale_y, desired_scale_x) + M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) + layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters + else + M.Scale(desired_scale_x, desired_scale_y)//VOREStation Edit + M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) + if(tail_style?.can_loaf) // VOREStation Edit: Taur Loafing + update_tail_showing() // VOREStation Edit: Taur Loafing + layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters + + if(instant) + transform = M + else + animate(src, transform = M, time = anim_time) + update_icon_special() //May contain transform-altering things + +//DAMAGE OVERLAYS +//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists +/mob/living/carbon/human/UpdateDamageIcon() + if(QDESTROYING(src)) + return + + remove_layer(MOB_DAM_LAYER) + + // first check whether something actually changed about damage appearance + var/damage_appearance = "" + + for(var/obj/item/organ/external/O in organs) + if(isnull(O) || O.is_stump()) + continue + damage_appearance += O.damage_state + + if(damage_appearance == previous_damage_appearance) + // nothing to do here + return + + previous_damage_appearance = damage_appearance + + var/image/standing_image = image(icon = species.damage_overlays, icon_state = "00", layer = BODY_LAYER+MOB_DAM_LAYER) + + // blend the individual damage states with our icons + for(var/obj/item/organ/external/O in organs) + if(isnull(O) || O.is_stump() || O.is_hidden_by_sprite_accessory()) + continue + + O.update_icon() + if(O.damage_state == "00") continue + var/icon/DI + var/cache_index = "[O.damage_state]/[O.icon_name]/[species.get_blood_colour(src)]/[species.get_bodytype(src)]" + if(damage_icon_parts[cache_index] == null) + DI = icon(species.get_damage_overlays(src), O.damage_state) // the damage icon for whole human + DI.Blend(icon(species.get_damage_mask(src), O.icon_name), ICON_MULTIPLY) // mask with this organ's pixels + DI.Blend(species.get_blood_colour(src), ICON_MULTIPLY) + damage_icon_parts[cache_index] = DI + else + DI = damage_icon_parts[cache_index] + + standing_image.add_overlay(DI) + + overlays_standing[MOB_DAM_LAYER] = standing_image + apply_layer(MOB_DAM_LAYER) + +//BASE MOB SPRITE +/mob/living/carbon/human/update_icons_body() + if(QDESTROYING(src)) + return + + var/husk_color_mod = rgb(96,88,80) + var/hulk_color_mod = rgb(48,224,40) + + var/husk = (HUSK in src.mutations) + var/fat = (FAT in src.mutations) + var/hulk = (HULK in src.mutations) + var/skeleton = (SKELETON in src.mutations) + + robolimb_count = 0 //TODO, here, really tho? + robobody_count = 0 + + //CACHING: Generate an index key from visible bodyparts. + //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. + + //Create a new, blank icon for our mob to use. + var/icon/stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") + + var/g = (gender == MALE ? "male" : "female") + var/icon_key = "[species.get_race_key(src)][g][s_tone][r_skin][g_skin][b_skin]" + if(lip_style) + icon_key += "[lip_style]" + else + icon_key += "nolips" + var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] + if(eyes) + icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])]" + else + icon_key += "[r_eyes], [g_eyes], [b_eyes]" + var/obj/item/organ/external/head/head = organs_by_name[BP_HEAD] + if(head) + if(!istype(head, /obj/item/organ/external/stump)) + if (species.selects_bodytype != SELECTS_BODYTYPE_FALSE) + var/headtype = GLOB.all_species[species.base_species]?.has_limbs[BP_HEAD] + var/obj/item/organ/external/head/headtypepath = headtype["path"] + if (headtypepath && !head.eye_icon_override) + head.eye_icon = initial(headtypepath.eye_icon) + head.eye_icon_location = initial(headtypepath.eye_icon_location) + icon_key += "[head.eye_icon]" + var/wholeicontransparent = TRUE + for(var/organ_tag in species.has_limbs) + var/obj/item/organ/external/part = organs_by_name[organ_tag] + if(isnull(part) || part.is_stump() || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. + icon_key += "0" + continue + if(part) + wholeicontransparent &&= part.transparent //VORESTATION EDIT: transparent instead of nonsolid + icon_key += "[part.species.get_race_key(part.owner)]" + icon_key += "[part.dna.GetUIState(DNA_UI_GENDER)]" + icon_key += "[part.s_tone]" + if(part.s_col && part.s_col.len >= 3) + icon_key += "[rgb(part.s_col[1],part.s_col[2],part.s_col[3])]" + if(part.body_hair && part.h_col && part.h_col.len >= 3) + icon_key += "[rgb(part.h_col[1],part.h_col[2],part.h_col[3])]" + if(species.color_mult) + icon_key += "[ICON_MULTIPLY]" + else + icon_key += "[ICON_ADD]" + else + icon_key += "#000000" + + for(var/M in part.markings) + if (part.markings[M]["on"]) + icon_key += "[M][part.markings[M]["color"]]" + + // VOREStation Edit Start + if(part.nail_polish) + icon_key += "_[part.nail_polish.icon]_[part.nail_polish.icon_state]_[part.nail_polish.color]" + // VOREStation Edit End + + if(part.robotic >= ORGAN_ROBOT) + icon_key += "2[part.model ? "-[part.model]": ""]" + robolimb_count++ + if((part.robotic == ORGAN_ROBOT || part.robotic == ORGAN_LIFELIKE) && (part.organ_tag == BP_HEAD || part.organ_tag == BP_TORSO || part.organ_tag == BP_GROIN)) + robobody_count ++ + else if(part.status & ORGAN_DEAD) + icon_key += "3" + else + icon_key += "1" + if(part.transparent) //VOREStation Edit. For better slime limbs. Avoids using solid var due to limb dropping. + icon_key += "_t" //VOREStation Edit. + + if(istype(tail_style, /datum/sprite_accessory/tail/taur)) + if(tail_style.clip_mask) //VOREStation Edit. + icon_key += tail_style.clip_mask_state + + if(digitigrade && (part.organ_tag == BP_R_LEG || part.organ_tag == BP_L_LEG || part.organ_tag == BP_R_FOOT || part.organ_tag == BP_L_FOOT)) + icon_key += "_digi" + + if(tail_style) + pixel_x = tail_style.mob_offset_x + pixel_y = tail_style.mob_offset_y + default_pixel_x = tail_style.mob_offset_x + default_pixel_y = tail_style.mob_offset_y + + icon_key = "[icon_key][husk ? 1 : 0][fat ? 1 : 0][hulk ? 1 : 0][skeleton ? 1 : 0]" + var/icon/base_icon + if(human_icon_cache[icon_key]) + base_icon = human_icon_cache[icon_key] + else + //BEGIN CACHED ICON GENERATION. + var/obj/item/organ/external/chest = get_organ(BP_TORSO) + base_icon = chest.get_icon(skeleton, !wholeicontransparent) + + var/apply_extra_transparency_leg = organs_by_name[BP_L_LEG] && organs_by_name[BP_R_LEG] + var/apply_extra_transparency_foot = organs_by_name[BP_L_FOOT] && organs_by_name[BP_R_FOOT] + + var/icon/Cutter = null + var/icon_x_offset = 0 + var/icon_y_offset = 0 + + if(istype(tail_style, /datum/sprite_accessory/tail/taur)) // Tail icon 'cookie cutters' are filled in where icons are preserved. We need to invert that. + if(tail_style.clip_mask) //VOREStation Edit. + Cutter = new(icon = (tail_style.clip_mask_icon ? tail_style.clip_mask_icon : tail_style.icon), icon_state = tail_style.clip_mask_state) + + Cutter.Blend("#000000", ICON_MULTIPLY) // Make it all black. + + Cutter.SwapColor("#00000000", "#FFFFFFFF") // Everywhere empty, make white. + Cutter.SwapColor("#000000FF", "#00000000") // Everywhere black, make empty. + + Cutter.Blend("#000000", ICON_MULTIPLY) // Black again. + + icon_x_offset = tail_style.offset_x + icon_y_offset = tail_style.offset_y + + for(var/obj/item/organ/external/part in organs) + if(isnull(part) || part.is_stump() || part == chest || part.is_hidden_by_sprite_accessory()) //VOREStation Edit allowing tails to prevent bodyparts rendering, granting more spriter freedom for taur/digitigrade stuff. + continue + var/icon/temp = part.get_icon(skeleton, !wholeicontransparent) + + if((part.organ_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) && Cutter) + temp.Blend(Cutter, ICON_AND, x = icon_x_offset, y = icon_y_offset) + + //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST + //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) + if(part.icon_position & (LEFT | RIGHT)) + var/icon/temp2 = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank") + temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) + temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) + if(!(part.icon_position & LEFT)) + temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) + if(!(part.icon_position & RIGHT)) + temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) + base_icon.Blend(temp2, ICON_OVERLAY) + temp2.Insert(temp2,"blank",dir=NORTH) //faaaaairly certain this is more efficient than reloading temp2, doing this so we don't blend the icons twice (it matters more in transparent limbs) + temp2.Insert(temp2,"blank",dir=SOUTH) + temp2.Insert(temp2,"blank",dir=EAST) + temp2.Insert(temp2,"blank",dir=WEST) + if(part.icon_position & LEFT) + temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) + if(part.icon_position & RIGHT) + temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) + if (part.transparent && !wholeicontransparent) //apply a little (a lot) extra transparency to make it look better //VORESTATION EDIT: transparent instead of nonsolid + if ((istype(part, /obj/item/organ/external/leg) && apply_extra_transparency_leg) || (istype(part, /obj/item/organ/external/foot) && apply_extra_transparency_foot)) //maybe + temp2 += rgb(,,,30) + base_icon.Blend(temp2, ICON_UNDERLAY) + else if(part.icon_position & UNDER) + base_icon.Blend(temp, ICON_UNDERLAY) + else + base_icon.Blend(temp, ICON_OVERLAY) + + if (wholeicontransparent) //because, I mean. It's basically never gonna happen that you'll have just one non-transparent limb but if you do your icon will look meh. Still good but meh, will have some areas with higher transparencies unless you're literally just a torso and a head + base_icon += rgb(,,,180) + + if(!skeleton) + if(husk) + base_icon.ColorTone(husk_color_mod) + else if(hulk) + var/list/tone = rgb2num(hulk_color_mod) + base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3])) + + //Handle husk overlay. + if(husk && ("overlay_husk" in cached_icon_states(species.icobase))) + var/icon/mask = new(base_icon) + var/icon/husk_over = new(species.icobase,"overlay_husk") + mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0) + husk_over.Blend(mask, ICON_ADD) + base_icon.Blend(husk_over, ICON_OVERLAY) + + human_icon_cache[icon_key] = base_icon + + //END CACHED ICON GENERATION. + stand_icon.Blend(base_icon,ICON_OVERLAY) + icon = stand_icon + + //tail + update_tail_showing() + update_wing_showing() + +/mob/living/carbon/human/proc/update_skin() + if(QDESTROYING(src)) + return + + remove_layer(SKIN_LAYER) + + var/image/skin = species.update_skin(src) + if(skin) + skin.layer = BODY_LAYER+SKIN_LAYER + overlays_standing[SKIN_LAYER] = skin + apply_layer(SKIN_LAYER) + +/mob/living/carbon/human/proc/update_bloodied() + if(QDESTROYING(src)) + return + + remove_layer(BLOOD_LAYER) + if(!blood_DNA && !feet_blood_DNA) + return + + var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+BLOOD_LAYER) + + //Bloody hands + if(blood_DNA) + var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "bloodyhands", layer = BODY_LAYER+BLOOD_LAYER) + bloodsies.color = hand_blood_color + both.add_overlay(bloodsies) + + //Bloody feet + if(feet_blood_DNA) + var/image/bloodsies = image(icon = species.get_blood_mask(src), icon_state = "shoeblood", layer = BODY_LAYER+BLOOD_LAYER) + bloodsies.color = feet_blood_color + both.add_overlay(bloodsies) + + overlays_standing[BLOOD_LAYER] = both + + apply_layer(BLOOD_LAYER) + +//UNDERWEAR OVERLAY +/mob/living/carbon/human/proc/update_underwear() + if(QDESTROYING(src)) + return + + remove_layer(UNDERWEAR_LAYER) + + if(species.appearance_flags & HAS_UNDERWEAR) + overlays_standing[UNDERWEAR_LAYER] = list() + for(var/category in all_underwear) + if(hide_underwear[category]) + continue + var/datum/category_item/underwear/UWI = all_underwear[category] + var/image/wear = UWI.generate_image(all_underwear_metadata[category], layer = BODY_LAYER+UNDERWEAR_LAYER) + overlays_standing[UNDERWEAR_LAYER] += wear + + apply_layer(UNDERWEAR_LAYER) + +//HAIR OVERLAY +/mob/living/carbon/human/proc/update_hair() + if(QDESTROYING(src)) + return + + //Reset our hair + remove_layer(HAIR_LAYER) + remove_layer(HAIR_ACCESSORY_LAYER) //VOREStation Edit + update_eyes() //Pirated out of here, for glowing eyes. + + var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) + if(!head_organ || head_organ.is_stump() ) + return + + //masks and helmets can obscure our hair. + if( (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) + return + + //base icons + var/icon/face_standing = icon(icon = 'icons/mob/human_face.dmi', icon_state = "bald_s") + + if(f_style) + var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[f_style] + if(facial_hair_style && facial_hair_style.species_allowed && (src.species.get_bodytype(src) in facial_hair_style.species_allowed)) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(facial_hair_style.do_colouration) + facial_s.Blend(rgb(r_facial, g_facial, b_facial), facial_hair_style.color_blend_mode) + + face_standing.Blend(facial_s, ICON_OVERLAY) + + if(h_style) + var/datum/sprite_accessory/hair/hair_style = hair_styles_list[h_style] + if(head && (head.flags_inv & BLOCKHEADHAIR)) + if(!(hair_style.flags & HAIR_VERY_SHORT)) + hair_style = hair_styles_list["Short Hair"] + + if(hair_style && (src.species.get_bodytype(src) in hair_style.species_allowed)) + var/icon/grad_s = null + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + var/icon/hair_s_add = new/icon("icon" = hair_style.icon_add, "icon_state" = "[hair_style.icon_state]_s") + if(hair_style.do_colouration) + if(grad_style) + grad_s = new/icon("icon" = 'icons/mob/hair_gradients.dmi', "icon_state" = GLOB.hair_gradients[grad_style]) + grad_s.Blend(hair_s, ICON_AND) + grad_s.Blend(rgb(r_grad, g_grad, b_grad), ICON_MULTIPLY) + hair_s.Blend(rgb(r_hair, g_hair, b_hair), ICON_MULTIPLY) + hair_s.Blend(hair_s_add, ICON_ADD) + if(!isnull(grad_s)) + hair_s.Blend(grad_s, ICON_OVERLAY) + + face_standing.Blend(hair_s, ICON_OVERLAY) + + var/icon/ears_s = get_ears_overlay() + + if(head_organ.transparent) //VORESTATION EDIT: transparent instead of nonsolid + face_standing += rgb(,,,120) + if (ears_s) + ears_s += rgb(,,,180) + + var/image/em_block_ears + if(ears_s) + if(ears_s.Height() > face_standing.Height()) // Tol ears + face_standing.Crop(1, 1, face_standing.Width(), ears_s.Height()) + face_standing.Blend(ears_s, ICON_OVERLAY) + if(ear_style?.em_block) + em_block_ears = em_block_image_generic(image(ears_s)) + + var/image/semifinal = image(face_standing, layer = BODY_LAYER+HAIR_LAYER, "pixel_y" = head_organ.head_offset) + if(em_block_ears) + semifinal.overlays += em_block_ears // Leaving this as overlays += + + overlays_standing[HAIR_LAYER] = semifinal + apply_layer(HAIR_LAYER) + //return //VOREStation Edit + + // VOREStation Edit - START + var/icon/hair_acc_s = get_hair_accessory_overlay() + var/image/hair_acc_s_image = null + if(hair_acc_s) + if(hair_accessory_style.ignores_lighting) + hair_acc_s_image = image(hair_acc_s) + hair_acc_s_image.plane = PLANE_LIGHTING_ABOVE + hair_acc_s_image.appearance_flags = appearance_flags + if (hair_acc_s_image) + overlays_standing[HAIR_ACCESSORY_LAYER] = hair_acc_s_image + apply_layer(HAIR_ACCESSORY_LAYER) + return + overlays_standing[HAIR_ACCESSORY_LAYER] = image(hair_acc_s, layer = BODY_LAYER+HAIR_ACCESSORY_LAYER) + apply_layer(HAIR_ACCESSORY_LAYER) + // VOREStation Edit - END + +/mob/living/carbon/human/update_eyes() + if(QDESTROYING(src)) + return + + //Reset our eyes + remove_layer(EYES_LAYER) + + //TODO: Probably redo this. I know I wrote it, but... + + //This is ONLY for glowing eyes for now. Boring flat eyes are done by the head's own proc. + if(!species.has_glowing_eyes) + return + + //Our glowy eyes should be hidden if some equipment hides them. + if(!should_have_organ(O_EYES) || (head && (head.flags_inv & BLOCKHAIR)) || (wear_mask && (wear_mask.flags_inv & BLOCKHAIR))) + return + + //Get the head, we'll need it later. + var/obj/item/organ/external/head/head_organ = get_organ(BP_HEAD) + if(!head_organ || head_organ.is_stump() ) + return + + //The eyes store the color themselves, funny enough. + var/obj/item/organ/internal/eyes/eyes = internal_organs_by_name[O_EYES] + if(!head_organ.eye_icon) + return + + var/icon/eyes_icon = new/icon(head_organ.eye_icon_location, head_organ.eye_icon) //VOREStation Edit + if(eyes) + eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD) + else + eyes_icon.Blend(rgb(128,0,0), ICON_ADD) + + // Convert to emissive at some point + if (head_organ.transparent) //VOREStation Edit: transparent instead of nonsolid + eyes_icon += rgb(,,,180) + + var/image/eyes_image = image(eyes_icon) + eyes_image.plane = PLANE_LIGHTING_ABOVE + eyes_image.appearance_flags = appearance_flags + + overlays_standing[EYES_LAYER] = eyes_image + apply_layer(EYES_LAYER) + +/mob/living/carbon/human/update_mutations() + if(QDESTROYING(src)) + return + + remove_layer(MUTATIONS_LAYER) + + if(!LAZYLEN(mutations)) + return //No mutations, no icons. + + //TODO: THIS PROC??? + var/fat + if(FAT in mutations) + fat = "fat" + + var/image/standing = image(icon = 'icons/effects/genetics.dmi', layer = BODY_LAYER+MUTATIONS_LAYER) + var/g = gender == FEMALE ? "f" : "m" + + for(var/datum/dna/gene/gene in dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + var/underlay = gene.OnDrawUnderlays(src,g,fat) + if(underlay) + standing.underlays += underlay + + for(var/mut in mutations) + if(mut == LASER) + standing.overlays += "lasereyes_s" // Leaving this as overlays += + + overlays_standing[MUTATIONS_LAYER] = standing + apply_layer(MUTATIONS_LAYER) + +/* --------------------------------------- */ +//Recomputes every icon on the mob. Expensive. +//Useful if the species changed, or there's some +//other drastic body-shape change, but otherwise avoid. +/mob/living/carbon/human/regenerate_icons() + ..() + if(transforming || QDELETED(src)) + return + + update_icons_body() + UpdateDamageIcon() + update_mutations() + update_skin() + update_underwear() + update_hair() + update_inv_w_uniform() + update_inv_wear_id() + update_inv_gloves() + update_inv_glasses() + update_inv_ears() + update_inv_shoes() + update_inv_s_store() + update_inv_wear_mask() + update_inv_head() + update_inv_belt() + update_inv_back() + update_inv_wear_suit() + update_inv_r_hand() + update_inv_l_hand() + update_inv_handcuffed() + update_inv_legcuffed() + //update_inv_pockets() //Doesn't do anything + update_fire() + update_water() + update_surgery() + +/* --------------------------------------- */ +//vvvvvv UPDATE_INV PROCS vvvvvv + +/mob/living/carbon/human/update_inv_w_uniform() + if(QDESTROYING(src)) + return + + remove_layer(UNIFORM_LAYER) + + //Shoes can be affected by uniform being drawn onto them + update_inv_shoes() + + if(!w_uniform) + return + + if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT) && !istype(wear_suit, /obj/item/clothing/suit/space/rig)) + return //Wearing a suit that prevents uniform rendering + + var/obj/item/clothing/under/under = w_uniform + + var/uniform_sprite + if(istype(under) && !isnull(under.update_icon_define)) + uniform_sprite = under.update_icon_define + else + uniform_sprite = INV_W_UNIFORM_DEF_ICON + + //Build a uniform sprite + var/icon/c_mask = tail_style?.clip_mask + if(c_mask) + var/obj/item/clothing/suit/S = wear_suit + if((wear_suit?.flags_inv & HIDETAIL) || (istype(S) && S.taurized)) // Reasons to not mask: 1. If you're wearing a suit that hides the tail or if you're wearing a taurized suit. + c_mask = null + overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = uniform_sprite, default_layer = UNIFORM_LAYER, clip_mask = c_mask) + apply_layer(UNIFORM_LAYER) + +/mob/living/carbon/human/update_inv_wear_id() + if(QDESTROYING(src)) + return + + remove_layer(ID_LAYER) + + if(!wear_id) + return //Not wearing an ID + + //Only draw the ID on the mob if the uniform allows for it + if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + if(U.displays_id) + overlays_standing[ID_LAYER] = wear_id.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_id_str, default_icon = INV_WEAR_ID_DEF_ICON, default_layer = ID_LAYER) + + apply_layer(ID_LAYER) + +/mob/living/carbon/human/update_inv_gloves() + if(QDESTROYING(src)) + return + + remove_layer(GLOVES_LAYER) + + if(!gloves) + return //No gloves, no reason to be here. + + overlays_standing[GLOVES_LAYER] = gloves.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_GLOVES_DEF_ICON, default_layer = GLOVES_LAYER) + + apply_layer(GLOVES_LAYER) + +/mob/living/carbon/human/update_inv_glasses() + if(QDESTROYING(src)) + return + + remove_layer(GLASSES_LAYER) + remove_layer(GLASSES_LAYER_ALT) + + if(!glasses) + return //Not wearing glasses, no need to update anything. + + var/glasses_layer = GLASSES_LAYER + if(istype(glasses, /obj/item/clothing/glasses)) + var/obj/item/clothing/glasses/our_glasses = glasses + if(our_glasses.glasses_layer_above) + glasses_layer = GLASSES_LAYER_ALT + + overlays_standing[glasses_layer] = glasses.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_gloves_str, default_icon = INV_EYES_DEF_ICON, default_layer = glasses_layer) + + apply_layer(glasses_layer) + +/mob/living/carbon/human/update_inv_ears() + if(QDESTROYING(src)) + return + + remove_layer(EARS_LAYER) + + if((head && head.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR)) || (wear_mask && wear_mask.flags_inv & (BLOCKHAIR | BLOCKHEADHAIR))) + return //Ears are blocked (by hair being blocked, overloaded) + + if(!l_ear && !r_ear) + return //Why bother, if no ear sprites + + // Blank image upon which to layer left & right overlays. + var/image/both = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing", layer = BODY_LAYER+EARS_LAYER) + + if(l_ear) + var/image/standing = l_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_l_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) + both.add_overlay(standing) + + if(r_ear) + var/image/standing = r_ear.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_r_ear_str, default_icon = INV_EARS_DEF_ICON, default_layer = EARS_LAYER) + both.add_overlay(standing) + + overlays_standing[EARS_LAYER] = both + apply_layer(EARS_LAYER) + +/mob/living/carbon/human/update_inv_shoes() + if(QDESTROYING(src)) + return + + remove_layer(SHOES_LAYER) + remove_layer(SHOES_LAYER_ALT) //Dumb alternate layer for shoes being under the uniform. + + if(!shoes || (wear_suit && wear_suit.flags_inv & HIDESHOES) || (w_uniform && w_uniform.flags_inv & HIDESHOES)) + return //Either nothing to draw, or it'd be hidden. + + for(var/f in list(BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/foot/foot = get_organ(f) + if(istype(foot) && foot.is_hidden_by_sprite_accessory(clothing_only = TRUE)) //If either foot is hidden by the tail, don't render footwear. + return + + var/obj/item/clothing/shoes/shoe = shoes + var/shoe_sprite + + if(istype(shoe) && !isnull(shoe.update_icon_define)) + shoe_sprite = shoe.update_icon_define + else + shoe_sprite = INV_FEET_DEF_ICON + + //Allow for shoe layer toggle nonsense + var/shoe_layer = SHOES_LAYER + if(istype(shoes, /obj/item/clothing/shoes)) + var/obj/item/clothing/shoes/ushoes = shoes + if(ushoes.shoes_under_pants == 1) + shoe_layer = SHOES_LAYER_ALT + + //NB: the use of a var for the layer on this one + overlays_standing[shoe_layer] = shoes.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_shoes_str, default_icon = shoe_sprite, default_layer = shoe_layer) + + apply_layer(SHOES_LAYER) + apply_layer(SHOES_LAYER_ALT) + +/mob/living/carbon/human/update_inv_s_store() + if(QDESTROYING(src)) + return + + remove_layer(SUIT_STORE_LAYER) + + if(!s_store) + return //Why bother, nothing there. + + //TODO, this is unlike the rest of the things + //Basically has no variety in slot icon choices at all. WHY SPECIES ONLY?? + var/t_state = s_store.item_state + if(!t_state) + t_state = s_store.icon_state + overlays_standing[SUIT_STORE_LAYER] = image(icon = species.suit_storage_icon, icon_state = t_state, layer = BODY_LAYER+SUIT_STORE_LAYER) + + apply_layer(SUIT_STORE_LAYER) + +/mob/living/carbon/human/update_inv_head() + if(QDESTROYING(src)) + return + + remove_layer(HEAD_LAYER) + + if(!head) + return //No head item, why bother. + + overlays_standing[HEAD_LAYER] = head.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER) + + apply_layer(HEAD_LAYER) + +/mob/living/carbon/human/update_inv_belt() + if(QDESTROYING(src)) + return + + remove_layer(BELT_LAYER) + remove_layer(BELT_LAYER_ALT) //Because you can toggle belt layer with a verb + + if(!belt) + return //No belt, why bother. + + //Toggle for belt layering with uniform + var/belt_layer = BELT_LAYER + if(istype(belt, /obj/item/weapon/storage/belt)) + var/obj/item/weapon/storage/belt/ubelt = belt + if(ubelt.show_above_suit) + belt_layer = BELT_LAYER_ALT + + + var/icon/c_mask = tail_style?.clip_mask + + //NB: this uses a var from above + overlays_standing[belt_layer] = belt.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_belt_str, default_icon = INV_BELT_DEF_ICON, default_layer = belt_layer, clip_mask = c_mask) + + apply_layer(belt_layer) + +/mob/living/carbon/human/update_inv_wear_suit() + if(QDESTROYING(src)) + return + + remove_layer(SUIT_LAYER) + + //Hide/show other layers if necessary + update_inv_w_uniform() + update_inv_shoes() + update_tail_showing() + update_wing_showing() + + if(!wear_suit) + return //No point, no suit. + + var/obj/item/clothing/suit/suit = wear_suit + var/suit_sprite + + if(istype(suit) && !isnull(suit.update_icon_define)) + suit_sprite = suit.update_icon_define + else + suit_sprite = INV_SUIT_DEF_ICON + + var/icon/c_mask = null + var/tail_is_rendered = (overlays_standing[TAIL_UPPER_LAYER] || overlays_standing[TAIL_UPPER_LAYER_ALT] || overlays_standing[TAIL_LOWER_LAYER]) + var/valid_clip_mask = tail_style?.clip_mask + + if(tail_is_rendered && valid_clip_mask && !(istype(suit) && suit.taurized)) //Clip the lower half of the suit off using the tail's clip mask for taurs since taur bodies aren't hidden. + c_mask = valid_clip_mask + overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = suit_sprite, default_layer = SUIT_LAYER, clip_mask = c_mask) + + apply_layer(SUIT_LAYER) + +/mob/living/carbon/human/update_inv_pockets() + stack_trace("Someone called update_inv_pockets even though it's dumb") + +/mob/living/carbon/human/update_inv_wear_mask() + if(QDESTROYING(src)) + return + + remove_layer(FACEMASK_LAYER) + + if(!wear_mask || (head && head.flags_inv & HIDEMASK)) + return //Why bother, nothing in mask slot. + + overlays_standing[FACEMASK_LAYER] = wear_mask.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_mask_str, default_icon = INV_MASK_DEF_ICON, default_layer = FACEMASK_LAYER) + + apply_layer(FACEMASK_LAYER) + +/mob/living/carbon/human/update_inv_back() + if(QDESTROYING(src)) + return + + remove_layer(BACK_LAYER) + + if(!back) + return //Why do anything + + var/icon/c_mask = tail_style?.clip_mask + if(c_mask) + if(istype(back, /obj/item/weapon/storage/backpack/saddlebag) || istype(back, /obj/item/weapon/storage/backpack/saddlebag_common)) + c_mask = null + + overlays_standing[BACK_LAYER] = back.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_back_str, default_icon = INV_BACK_DEF_ICON, default_layer = BACK_LAYER, clip_mask = c_mask) + + apply_layer(BACK_LAYER) + +//TODO: Carbon procs in my human update_icons?? +/mob/living/carbon/human/update_hud() //TODO: do away with this if possible + if(QDESTROYING(src)) + return + + if(client) + client.screen |= contents + if(hud_used) + hud_used.hidden_inventory_update() //Updates the screenloc of the items on the 'other' inventory bar + +//update whether handcuffs appears on our hud. +/mob/living/carbon/proc/update_hud_handcuffed() + if(QDESTROYING(src)) + return + + if(hud_used && hud_used.l_hand_hud_object && hud_used.r_hand_hud_object) + hud_used.l_hand_hud_object.update_icon() + hud_used.r_hand_hud_object.update_icon() + +/mob/living/carbon/human/update_inv_handcuffed() + if(QDESTROYING(src)) + return + + remove_layer(HANDCUFF_LAYER) + update_hud_handcuffed() //TODO + + if(!handcuffed) + return //Not cuffed, why bother + + overlays_standing[HANDCUFF_LAYER] = handcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_handcuffed_str, default_icon = INV_HCUFF_DEF_ICON, default_layer = HANDCUFF_LAYER) + + apply_layer(HANDCUFF_LAYER) + +/mob/living/carbon/human/update_inv_legcuffed() + if(QDESTROYING(src)) + return + + clear_alert("legcuffed") + remove_layer(LEGCUFF_LAYER) + + if(!legcuffed) + return //Not legcuffed, why bother. + + throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) + + overlays_standing[LEGCUFF_LAYER] = legcuffed.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_legcuffed_str, default_icon = INV_LCUFF_DEF_ICON, default_layer = LEGCUFF_LAYER) + + apply_layer(LEGCUFF_LAYER) + +/mob/living/carbon/human/update_inv_r_hand() + if(QDESTROYING(src)) + return + + remove_layer(R_HAND_LAYER) + + if(!r_hand) + return //No hand, no bother. + + overlays_standing[R_HAND_LAYER] = r_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_r_hand_str, default_icon = INV_R_HAND_DEF_ICON, default_layer = R_HAND_LAYER) + + apply_layer(R_HAND_LAYER) + +/mob/living/carbon/human/update_inv_l_hand() + if(QDESTROYING(src)) + return + + remove_layer(L_HAND_LAYER) + + if(!l_hand) + return //No hand, no bother. + + overlays_standing[L_HAND_LAYER] = l_hand.make_worn_icon(body_type = species.get_bodytype(src), inhands = TRUE, slot_name = slot_l_hand_str, default_icon = INV_L_HAND_DEF_ICON, default_layer = L_HAND_LAYER) + + apply_layer(L_HAND_LAYER) + +/mob/living/carbon/human/proc/get_tail_layer() + var/list/lower_layer_dirs = list(SOUTH) + if(tail_style) + lower_layer_dirs = tail_style.lower_layer_dirs.Copy() + + if(dir in lower_layer_dirs) + return TAIL_LOWER_LAYER + else + return TAIL_UPPER_LAYER + +/mob/living/carbon/human/proc/update_tail_showing() + if(QDESTROYING(src)) + return + + remove_layer(TAIL_UPPER_LAYER) + remove_layer(TAIL_UPPER_LAYER_ALT) + remove_layer(TAIL_LOWER_LAYER) + + var/tail_layer = get_tail_layer() + if(src.tail_style && src.tail_style.clip_mask_state) + tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything + if(tail_alt && tail_layer == TAIL_UPPER_LAYER) + tail_layer = TAIL_UPPER_LAYER_ALT + + var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] + + var/image/tail_image = get_tail_image() + if(tail_image) + tail_image.layer = BODY_LAYER+tail_layer + tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid + overlays_standing[tail_layer] = tail_image + apply_layer(tail_layer) + return + + var/species_tail = species.get_tail(src) // Species tail icon_state prefix. + + //This one is actually not that bad I guess. + if(species_tail && !(wear_suit && wear_suit.flags_inv & HIDETAIL)) + var/icon/tail_s = get_tail_icon() + tail_image = image(icon = tail_s, icon_state = "[species_tail]_s", layer = BODY_LAYER+tail_layer) + tail_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid + overlays_standing[tail_layer] = tail_image + animate_tail_reset() + +//TODO: Is this the appropriate place for this, and not on species...? +/mob/living/carbon/human/proc/get_tail_icon() + var/icon_key = "[species.get_race_key(src)][r_skin][g_skin][b_skin][r_hair][g_hair][b_hair]" + var/icon/tail_icon = tail_icon_cache[icon_key] + if(!tail_icon) + //generate a new one + var/species_tail_anim = species.get_tail_animation(src) + if(!species_tail_anim && species.icobase_tail) species_tail_anim = species.icobase //Allow override of file for non-animated tails + if(!species_tail_anim) species_tail_anim = 'icons/effects/species.dmi' + tail_icon = new/icon(species_tail_anim) + tail_icon.Blend(rgb(r_skin, g_skin, b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + // The following will not work with animated tails. + var/use_species_tail = species.get_tail_hair(src) + if(use_species_tail) + var/icon/hair_icon = icon('icons/effects/species.dmi', "[species.get_tail(src)]_[use_species_tail]") + hair_icon.Blend(rgb(r_hair, g_hair, b_hair), species.color_mult ? ICON_MULTIPLY : ICON_ADD) //Check for species color_mult + tail_icon.Blend(hair_icon, ICON_OVERLAY) + tail_icon_cache[icon_key] = tail_icon + + return tail_icon + +/mob/living/carbon/human/proc/set_tail_state(var/t_state) + var/tail_layer = get_tail_layer() + if(src.tail_style && src.tail_style.clip_mask_state) + tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything + if(tail_alt && tail_layer == TAIL_UPPER_LAYER) + tail_layer = TAIL_UPPER_LAYER_ALT + var/image/tail_overlay = overlays_standing[tail_layer] + + remove_layer(TAIL_UPPER_LAYER) + remove_layer(TAIL_UPPER_LAYER_ALT) + remove_layer(TAIL_LOWER_LAYER) + + if(tail_overlay) + overlays_standing[tail_layer] = tail_overlay + if(species.get_tail_animation(src)) + tail_overlay.icon_state = t_state + . = tail_overlay + + apply_layer(tail_layer) + +//Not really once, since BYOND can't do that. +//Update this if the ability to flick() images or make looping animation start at the first frame is ever added. +//You can sort of flick images now with flick_overlay -Aro +/mob/living/carbon/human/proc/animate_tail_once() + if(QDESTROYING(src)) + return + + var/t_state = "[species.get_tail(src)]_once" + var/tail_layer = get_tail_layer() + if(src.tail_style && src.tail_style.clip_mask_state) + tail_layer = TAIL_UPPER_LAYER // Use default, let clip mask handle everything + + var/image/tail_overlay = overlays_standing[tail_layer] + if(tail_overlay && tail_overlay.icon_state == t_state) + return //let the existing animation finish + + tail_overlay = set_tail_state(t_state) // Calls remove_layer & apply_layer + if(tail_overlay) + spawn(20) + //check that the animation hasn't changed in the meantime + if(overlays_standing[tail_layer] == tail_overlay && tail_overlay.icon_state == t_state) + animate_tail_stop() + +/mob/living/carbon/human/proc/animate_tail_start() + if(QDESTROYING(src)) + return + + set_tail_state("[species.get_tail(src)]_slow[rand(0,9)]") + +/mob/living/carbon/human/proc/animate_tail_fast() + if(QDESTROYING(src)) + return + + set_tail_state("[species.get_tail(src)]_loop[rand(0,9)]") + +/mob/living/carbon/human/proc/animate_tail_reset() + if(QDESTROYING(src)) + return + + if(stat != DEAD) + set_tail_state("[species.get_tail(src)]_idle[rand(0,9)]") + else + set_tail_state("[species.get_tail(src)]_static") + toggle_tail(FALSE) //So tails stop when someone dies. TODO - Fix this hack ~Leshana + +/mob/living/carbon/human/proc/animate_tail_stop() + if(QDESTROYING(src)) + return + + set_tail_state("[species.get_tail(src)]_static") + +/mob/living/carbon/human/proc/update_wing_showing() + if(QDESTROYING(src)) + return + + remove_layer(WING_LAYER) + remove_layer(WING_LOWER_LAYER) + + var/image/wing_image = get_wing_image(FALSE) + + var/obj/item/organ/external/chest = organs_by_name[BP_TORSO] + + if(wing_image) + wing_image.layer = BODY_LAYER+WING_LAYER + wing_image.alpha = chest?.transparent ? 180 : 255 //VORESTATION EDIT: transparent instead of nonsolid + overlays_standing[WING_LAYER] = wing_image + if(wing_style && wing_style.multi_dir) + wing_image = get_wing_image(TRUE) + if(wing_image) + wing_image.layer = BODY_LAYER+WING_LOWER_LAYER + overlays_standing[WING_LOWER_LAYER] = wing_image + + apply_layer(WING_LAYER) + apply_layer(WING_LOWER_LAYER) + +/mob/living/carbon/human/update_modifier_visuals() + if(QDESTROYING(src)) + return + + remove_layer(MODIFIER_EFFECTS_LAYER) + + if(!LAZYLEN(modifiers)) + return //No modifiers, no effects. + + var/image/effects = new() + for(var/datum/modifier/M in modifiers) + if(M.mob_overlay_state) + if(M.icon_override) //VOREStation Edit. Override for the modifer icon. + var/image/I = image(icon = 'icons/mob/modifier_effects_vr.dmi', icon_state = M.mob_overlay_state) + I.color = M.effect_color + effects.overlays += I // Leaving this as overlays += + else + var/image/I = image(icon = 'icons/mob/modifier_effects.dmi', icon_state = M.mob_overlay_state) + I.color = M.effect_color + effects.overlays += I // Leaving this as overlays += + + overlays_standing[MODIFIER_EFFECTS_LAYER] = effects + + apply_layer(MODIFIER_EFFECTS_LAYER) + +/mob/living/carbon/human/update_fire() + if(QDESTROYING(src)) + return + + remove_layer(FIRE_LAYER) + + if(!on_fire) + return + + overlays_standing[FIRE_LAYER] = image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state(), layer = BODY_LAYER+FIRE_LAYER) + + apply_layer(FIRE_LAYER) + +/mob/living/carbon/human/update_water() + if(QDESTROYING(src)) + return + + remove_layer(MOB_WATER_LAYER) + + var/depth = check_submerged() + if(!depth || lying) + return + + var/atom/A = loc // We'd better be swimming and on a turf + var/image/I = image(icon = 'icons/mob/submerged.dmi', icon_state = "human_swimming_[depth]", layer = BODY_LAYER+MOB_WATER_LAYER) //TODO: Improve + I.color = A.color + overlays_standing[MOB_WATER_LAYER] = I + + apply_layer(MOB_WATER_LAYER) + +/mob/living/carbon/human/proc/update_surgery() + if(QDESTROYING(src)) + return + + remove_layer(SURGERY_LAYER) + + var/image/total = new + for(var/obj/item/organ/external/E in organs) + if(E.open) + var/image/I = image(icon = 'icons/mob/surgery.dmi', icon_state = "[E.icon_name][round(E.open)]", layer = BODY_LAYER+SURGERY_LAYER) + total.overlays += I // Leaving this as overlays += + + if(total.overlays.len) + overlays_standing[SURGERY_LAYER] = total + apply_layer(SURGERY_LAYER) + +/mob/living/carbon/human/proc/get_wing_image(var/under_layer) + if(QDESTROYING(src)) + return + + //If you are FBP with wing style and didn't set a custom one + if(synthetic && synthetic.includes_wing && !wing_style && !wings_hidden) //VOREStation Edit + var/icon/wing_s = new/icon("icon" = synthetic.icon, "icon_state" = "wing") //I dunno. If synths have some custom wing? + wing_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + var/image/working = image(wing_s) + working.overlays += em_block_image_generic(working) // Leaving this as overlays += + return working + + //If you have custom wings selected + if(wing_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL) && !wings_hidden) //VOREStation Edit + var/wing_state = (flapping && wing_style.ani_state) ? wing_style.ani_state : wing_style.icon_state + if(wing_style.multi_dir) + wing_state += "_[under_layer ? "back" : "front"]" + var/icon/wing_s = new/icon("icon" = wing_style.icon, "icon_state" = wing_state) + if(wing_style.do_colouration) + wing_s.Blend(rgb(src.r_wing, src.g_wing, src.b_wing), wing_style.color_blend_mode) + if(wing_style.extra_overlay) + var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay) + overlay.Blend(rgb(src.r_wing2, src.g_wing2, src.b_wing2), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + if(wing_style.extra_overlay2) + var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2) + if(wing_style.ani_state) + overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay2_w) + overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + else + overlay.Blend(rgb(src.r_wing3, src.g_wing3, src.b_wing3), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + var/image/working = image(wing_s) + if(wing_style.em_block) + working.overlays += em_block_image_generic(working) // Leaving this as overlays += + working.pixel_x -= wing_style.wing_offset + return working + +/mob/living/carbon/human/proc/get_ears_overlay() + //If you are FBP with ear style and didn't set a custom one + var/datum/robolimb/model = isSynthetic() + if(istype(model) && model.includes_ears && !ear_style) + var/icon/ears_s = new/icon("icon" = synthetic.icon, "icon_state" = "ears") + ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + return ears_s + + if(ear_style && !(head && (head.flags_inv & BLOCKHEADHAIR))) + var/icon/ears_s = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.icon_state) + if(ear_style.do_colouration) + ears_s.Blend(rgb(src.r_ears, src.g_ears, src.b_ears), ear_style.color_blend_mode) + if(ear_style.extra_overlay) + var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay) + overlay.Blend(rgb(src.r_ears2, src.g_ears2, src.b_ears2), ear_style.color_blend_mode) + ears_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + if(ear_style.extra_overlay2) //MORE COLOURS IS BETTERER + var/icon/overlay = new/icon("icon" = ear_style.icon, "icon_state" = ear_style.extra_overlay2) + overlay.Blend(rgb(src.r_ears3, src.g_ears3, src.b_ears3), ear_style.color_blend_mode) + ears_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + return ears_s + return null + + +/mob/living/carbon/human/proc/get_tail_image() + //If you are FBP with tail style and didn't set a custom one + var/datum/robolimb/model = isSynthetic() + if(istype(model) && model.includes_tail && !tail_style && !tail_hidden) + var/icon/tail_s = new/icon("icon" = synthetic.icon, "icon_state" = "tail") + tail_s.Blend(rgb(src.r_skin, src.g_skin, src.b_skin), species.color_mult ? ICON_MULTIPLY : ICON_ADD) + return image(tail_s) + + //If you have a custom tail selected + if(tail_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL && !istaurtail(tail_style)) && !tail_hidden) + var/icon/tail_s = new/icon("icon" = (tail_style.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = (wagging && tail_style.ani_state ? tail_style.ani_state : tail_style.icon_state)) //CHOMPEdit + if(tail_style.can_loaf && !is_shifted) + pixel_y = (resting) ? -tail_style.loaf_offset*size_multiplier : default_pixel_y //move player down, then taur up, to fit the overlays correctly // VOREStation Edit: Taur Loafing + if(tail_style.do_colouration) + tail_s.Blend(rgb(src.r_tail, src.g_tail, src.b_tail), tail_style.color_blend_mode) + if(tail_style.extra_overlay) + var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay) //CHOMPEdit + if(wagging && tail_style.ani_state) + overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay_w) + overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + else + overlay.Blend(rgb(src.r_tail2, src.g_tail2, src.b_tail2), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + + if(tail_style.extra_overlay2) + var/icon/overlay = new/icon("icon" = (tail_style?.can_loaf && resting) ? tail_style.icon_loaf : tail_style.icon, "icon_state" = tail_style.extra_overlay2) //CHOMPEdit + if(wagging && tail_style.ani_state) + overlay = new/icon("icon" = tail_style.icon, "icon_state" = tail_style.extra_overlay2_w) + overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + else + overlay.Blend(rgb(src.r_tail3, src.g_tail3, src.b_tail3), tail_style.color_blend_mode) + tail_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) + + var/image/working = image(tail_s) + if(tail_style.em_block) + working.overlays += em_block_image_generic(working) // Leaving this as overlays += + + if(istaurtail(tail_style)) + var/datum/sprite_accessory/tail/taur/taurtype = tail_style + working.pixel_x = tail_style.offset_x + working.pixel_y = tail_style.offset_y + if(taurtype.can_ride && !riding_datum) + riding_datum = new /datum/riding/taur(src) + verbs |= /mob/living/carbon/human/proc/taur_mount + verbs |= /mob/living/proc/toggle_rider_reins + else if(islongtail(tail_style)) + working.pixel_x = tail_style.offset_x + working.pixel_y = tail_style.offset_y + return working + return null + +// TODO - Move this to where it should go ~Leshana +/mob/living/proc/stop_flying() + if(QDESTROYING(src)) + return + flying = FALSE + return 1 + +/mob/living/carbon/human/stop_flying() + if((. = ..())) + update_wing_showing() + +//Human Overlays Indexes///////// +#undef MUTATIONS_LAYER +#undef SKIN_LAYER +#undef MOB_DAM_LAYER +#undef SURGERY_LAYER +#undef UNDERWEAR_LAYER +#undef SHOES_LAYER_ALT +#undef UNIFORM_LAYER +#undef ID_LAYER +#undef SHOES_LAYER +#undef GLOVES_LAYER +#undef BELT_LAYER +#undef SUIT_LAYER +#undef TAIL_UPPER_LAYER +#undef TAIL_LOWER_LAYER +#undef GLASSES_LAYER +#undef BELT_LAYER_ALT +#undef SUIT_STORE_LAYER +#undef BACK_LAYER +#undef HAIR_LAYER +#undef EARS_LAYER +#undef EYES_LAYER +#undef FACEMASK_LAYER +#undef HEAD_LAYER +#undef HANDCUFF_LAYER +#undef LEGCUFF_LAYER +#undef L_HAND_LAYER +#undef R_HAND_LAYER +#undef MODIFIER_EFFECTS_LAYER +#undef FIRE_LAYER +#undef WATER_LAYER +#undef TARGETED_LAYER +#undef TOTAL_LAYERS diff --git a/code/modules/mob/living/carbon/metroid/powers.dm b/code/modules/mob/living/carbon/metroid/powers.dm index 0ed74f15cd8..bcbf8fb132e 100644 --- a/code/modules/mob/living/carbon/metroid/powers.dm +++ b/code/modules/mob/living/carbon/metroid/powers.dm @@ -1,169 +1,169 @@ -/mob/living/carbon/slime/proc/Wrap(var/mob/living/M) // This is a proc for the clicks - if (Victim == M || src == M) - Feedstop() - return - - if (Victim) - to_chat(src, "I am already feeding...") - return - - var t = invalidFeedTarget(M) - if (t) - to_chat(src,t) - return - - Feedon(M) - -/mob/living/carbon/slime/proc/invalidFeedTarget(var/mob/living/M) - if (!M || !istype(M)) - return "This subject is incomparable..." - if (istype(M, /mob/living/carbon/slime)) // No cannibalism... yet - return "I cannot feed on other slimes..." - if (!Adjacent(M)) - return "This subject is too far away..." - if (istype(M, /mob/living/carbon) && M.getCloneLoss() >= M.getMaxHealth() * 1.5 || istype(M, /mob/living/simple_mob) && M.stat == DEAD) - return "This subject does not have an edible life energy..." - for(var/mob/living/carbon/slime/met in view()) - if(met.Victim == M && met != src) - return "The [met.name] is already feeding on this subject..." - return 0 - -/mob/living/carbon/slime/proc/Feedon(var/mob/living/M) - Victim = M - loc = M.loc - canmove = 0 - anchored = TRUE - - regenerate_icons() - - while(Victim && !invalidFeedTarget(M) && stat != 2) - canmove = 0 - - if(Adjacent(M)) - UpdateFeed(M) - - if(istype(M, /mob/living/carbon)) - Victim.adjustCloneLoss(rand(5,6)) - Victim.adjustToxLoss(rand(1,2)) - if(Victim.health <= 0) - Victim.adjustToxLoss(rand(2,4)) - - else if(istype(M, /mob/living/simple_mob)) - Victim.adjustBruteLoss(is_adult ? rand(7, 15) : rand(4, 12)) - - else - to_chat(src, "[pick("This subject is incompatible", "This subject does not have a life energy", "This subject is empty", "I am not satisfied", "I can not feed from this subject", "I do not feel nourished", "This subject is not food")]...") - Feedstop() - break - - if(prob(15) && M.client && istype(M, /mob/living/carbon)) - var/painMes = pick("You can feel your body becoming weak!", "You feel like you're about to die!", "You feel every part of your body screaming in agony!", "A low, rolling pain passes through your body!", "Your body feels as if it's falling apart!", "You feel extremely weak!", "A sharp, deep pain bathes every inch of your body!") - if (ishuman(M)) - var/mob/living/carbon/human/H = M - H.custom_pain(painMes, 100) - else if (istype(M, /mob/living/carbon)) - var/mob/living/carbon/C = M - if (C.can_feel_pain()) - to_chat(M, "[painMes]") - - gain_nutrition(rand(20,25)) - - adjustOxyLoss(-10) //Heal yourself - adjustBruteLoss(-10) - adjustFireLoss(-10) - adjustCloneLoss(-10) - updatehealth() - if(Victim) - Victim.updatehealth() - - sleep(30) // Deal damage every 3 seconds - else - break - - canmove = 1 - anchored = FALSE - - if(M && invalidFeedTarget(M)) // This means that the slime drained the victim - if(!client) - if(Victim && !rabid && !attacked && Victim.LAssailant && Victim.LAssailant != Victim && prob(50)) - if(!(Victim.LAssailant in Friends)) - Friends[Victim.LAssailant] = 1 - else - ++Friends[Victim.LAssailant] - - else - to_chat(src, "This subject does not have a strong enough life energy anymore...") - - Victim = null - -/mob/living/carbon/slime/proc/Feedstop() - if(Victim) - if(Victim.client) - to_chat(Victim, "[src] has let go of your head!") - Victim = null - -/mob/living/carbon/slime/proc/UpdateFeed(var/mob/M) - if(Victim) - if(Victim == M) - loc = M.loc // simple "attach to head" effect! - -/mob/living/carbon/slime/verb/Evolve() - set category = "Slime" - set desc = "This will let you evolve from baby to adult slime." - - if(stat) - to_chat(src, "I must be conscious to do this...") - return - - if(!is_adult) - if(amount_grown >= 10) - is_adult = 1 - maxHealth = 200 - amount_grown = 0 - regenerate_icons() - name = text("[colour] [is_adult ? "adult" : "baby"] slime ([number])") - else - to_chat(src, "I am not ready to evolve yet...") - else - to_chat(src, "I have already evolved...") - -/mob/living/carbon/slime/verb/Reproduce() - set category = "Slime" - set desc = "This will make you split into four Slimes." - - if(stat) - to_chat(src, "I must be conscious to do this...") - return - - if(is_adult) - if(amount_grown >= 10) - if(stat) - to_chat(src, "I must be conscious to do this...") - return - - var/list/babies = list() - var/new_nutrition = round(nutrition * 0.9) - var/new_powerlevel = round(powerlevel / 4) - for(var/i = 1, i <= 4, i++) - var/t = colour - if(prob(mutation_chance)) - t = slime_mutation[rand(1,4)] - var/mob/living/carbon/slime/M = new /mob/living/carbon/slime/(loc, t) - if(ckey) M.nutrition = new_nutrition //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature! - M.powerlevel = new_powerlevel - if(i != 1) step_away(M, src) - M.Friends = Friends.Copy() - babies += M - feedback_add_details("slime_babies_born","slimebirth_[replacetext(M.colour," ","_")]") - - var/mob/living/carbon/slime/new_slime = pick(babies) - new_slime.universal_speak = universal_speak - if(src.mind) - src.mind.transfer_to(new_slime) - else - new_slime.key = src.key - qdel(src) - else - to_chat(src, "I am not ready to reproduce yet...") - else - to_chat(src, "I am not old enough to reproduce yet...") +/mob/living/carbon/slime/proc/Wrap(var/mob/living/M) // This is a proc for the clicks + if (Victim == M || src == M) + Feedstop() + return + + if (Victim) + to_chat(src, "I am already feeding...") + return + + var t = invalidFeedTarget(M) + if (t) + to_chat(src,t) + return + + Feedon(M) + +/mob/living/carbon/slime/proc/invalidFeedTarget(var/mob/living/M) + if (!M || !istype(M)) + return "This subject is incomparable..." + if (istype(M, /mob/living/carbon/slime)) // No cannibalism... yet + return "I cannot feed on other slimes..." + if (!Adjacent(M)) + return "This subject is too far away..." + if (istype(M, /mob/living/carbon) && M.getCloneLoss() >= M.getMaxHealth() * 1.5 || istype(M, /mob/living/simple_mob) && M.stat == DEAD) + return "This subject does not have an edible life energy..." + for(var/mob/living/carbon/slime/met in view()) + if(met.Victim == M && met != src) + return "The [met.name] is already feeding on this subject..." + return 0 + +/mob/living/carbon/slime/proc/Feedon(var/mob/living/M) + Victim = M + loc = M.loc + canmove = 0 + anchored = TRUE + + regenerate_icons() + + while(Victim && !invalidFeedTarget(M) && stat != 2) + canmove = 0 + + if(Adjacent(M)) + UpdateFeed(M) + + if(istype(M, /mob/living/carbon)) + Victim.adjustCloneLoss(rand(5,6)) + Victim.adjustToxLoss(rand(1,2)) + if(Victim.health <= 0) + Victim.adjustToxLoss(rand(2,4)) + + else if(istype(M, /mob/living/simple_mob)) + Victim.adjustBruteLoss(is_adult ? rand(7, 15) : rand(4, 12)) + + else + to_chat(src, "[pick("This subject is incompatible", "This subject does not have a life energy", "This subject is empty", "I am not satisfied", "I can not feed from this subject", "I do not feel nourished", "This subject is not food")]...") + Feedstop() + break + + if(prob(15) && M.client && istype(M, /mob/living/carbon)) + var/painMes = pick("You can feel your body becoming weak!", "You feel like you're about to die!", "You feel every part of your body screaming in agony!", "A low, rolling pain passes through your body!", "Your body feels as if it's falling apart!", "You feel extremely weak!", "A sharp, deep pain bathes every inch of your body!") + if (ishuman(M)) + var/mob/living/carbon/human/H = M + H.custom_pain(painMes, 100) + else if (istype(M, /mob/living/carbon)) + var/mob/living/carbon/C = M + if (C.can_feel_pain()) + to_chat(M, "[painMes]") + + gain_nutrition(rand(20,25)) + + adjustOxyLoss(-10) //Heal yourself + adjustBruteLoss(-10) + adjustFireLoss(-10) + adjustCloneLoss(-10) + updatehealth() + if(Victim) + Victim.updatehealth() + + sleep(30) // Deal damage every 3 seconds + else + break + + canmove = 1 + anchored = FALSE + + if(M && invalidFeedTarget(M)) // This means that the slime drained the victim + if(!client) + if(Victim && !rabid && !attacked && Victim.LAssailant && Victim.LAssailant != Victim && prob(50)) + if(!(Victim.LAssailant in Friends)) + Friends[Victim.LAssailant] = 1 + else + ++Friends[Victim.LAssailant] + + else + to_chat(src, "This subject does not have a strong enough life energy anymore...") + + Victim = null + +/mob/living/carbon/slime/proc/Feedstop() + if(Victim) + if(Victim.client) + to_chat(Victim, "[src] has let go of your head!") + Victim = null + +/mob/living/carbon/slime/proc/UpdateFeed(var/mob/M) + if(Victim) + if(Victim == M) + loc = M.loc // simple "attach to head" effect! + +/mob/living/carbon/slime/verb/Evolve() + set category = "Slime" + set desc = "This will let you evolve from baby to adult slime." + + if(stat) + to_chat(src, "I must be conscious to do this...") + return + + if(!is_adult) + if(amount_grown >= 10) + is_adult = 1 + maxHealth = 200 + amount_grown = 0 + regenerate_icons() + name = text("[colour] [is_adult ? "adult" : "baby"] slime ([number])") + else + to_chat(src, "I am not ready to evolve yet...") + else + to_chat(src, "I have already evolved...") + +/mob/living/carbon/slime/verb/Reproduce() + set category = "Slime" + set desc = "This will make you split into four Slimes." + + if(stat) + to_chat(src, "I must be conscious to do this...") + return + + if(is_adult) + if(amount_grown >= 10) + if(stat) + to_chat(src, "I must be conscious to do this...") + return + + var/list/babies = list() + var/new_nutrition = round(nutrition * 0.9) + var/new_powerlevel = round(powerlevel / 4) + for(var/i = 1, i <= 4, i++) + var/t = colour + if(prob(mutation_chance)) + t = slime_mutation[rand(1,4)] + var/mob/living/carbon/slime/M = new /mob/living/carbon/slime/(loc, t) + if(ckey) M.nutrition = new_nutrition //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature! + M.powerlevel = new_powerlevel + if(i != 1) step_away(M, src) + M.Friends = Friends.Copy() + babies += M + feedback_add_details("slime_babies_born","slimebirth_[replacetext(M.colour," ","_")]") + + var/mob/living/carbon/slime/new_slime = pick(babies) + new_slime.universal_speak = universal_speak + if(src.mind) + src.mind.transfer_to(new_slime) + else + new_slime.key = src.key + qdel(src) + else + to_chat(src, "I am not ready to reproduce yet...") + else + to_chat(src, "I am not old enough to reproduce yet...") diff --git a/code/modules/mob/living/carbon/resist.dm b/code/modules/mob/living/carbon/resist.dm index 8af9d9e20d6..5434b31c44c 100644 --- a/code/modules/mob/living/carbon/resist.dm +++ b/code/modules/mob/living/carbon/resist.dm @@ -1,99 +1,99 @@ -/mob/living/carbon/resist_fire() - adjust_fire_stacks(-1.2) - Weaken(3) - spin(32,2) - visible_message( - "[src] rolls on the floor, trying to put themselves out!", - "You stop, drop, and roll!" - ) - sleep(30) - if(fire_stacks <= 0) - visible_message( - "[src] has successfully extinguished themselves!", - "You extinguish yourself." - ) - ExtinguishMob() - return TRUE - -/mob/living/carbon/resist_restraints() - var/obj/item/I = null - if(handcuffed) - I = handcuffed - else if(legcuffed) - I = legcuffed - - if(I) - setClickCooldown(100) - cuff_resist(I, cuff_break = can_break_cuffs()) - -/mob/living/carbon/proc/reduce_cuff_time() - return FALSE - -/mob/living/carbon/proc/cuff_resist(obj/item/weapon/handcuffs/I, breakouttime = 1200, cuff_break = 0) - - if(istype(I)) - breakouttime = I.breakouttime - - var/displaytime = breakouttime / 10 - - var/reduceCuffTime = reduce_cuff_time() - if(reduceCuffTime) - breakouttime /= reduceCuffTime - displaytime /= reduceCuffTime - - if(cuff_break) - visible_message("[src] is trying to break [I]!", - "You attempt to break your [I]. (This will take around 5 seconds and you need to stand still)") - - if(do_after(src, 5 SECONDS, target = src, incapacitation_flags = INCAPACITATION_DEFAULT & ~INCAPACITATION_RESTRAINED)) - if(!I || buckled) - return - visible_message("[src] manages to break [I]!", - "You successfully break your [I].") - say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - - if(I == handcuffed) - handcuffed = null - update_handcuffed() - else if(I == legcuffed) - legcuffed = null - update_inv_legcuffed() - - if(buckled && buckled.buckle_require_restraints) - buckled.unbuckle_mob() - - qdel(I) - else - to_chat(src, "You fail to break [I].") - return - - visible_message("[src] attempts to remove [I]!", - "You attempt to remove [I]. (This will take around [displaytime] seconds and you need to stand still)") - if(do_after(src, breakouttime, target = src, incapacitation_flags = INCAPACITATION_DISABLED & INCAPACITATION_KNOCKDOWN)) - visible_message("[src] manages to remove [I]!", - "You successfully remove [I].") - drop_from_inventory(I) - -/mob/living/carbon/resist_buckle() - if(!buckled) - return - - if(!restrained()) - return ..() - - setClickCooldown(100) - visible_message( - "[src] attempts to unbuckle themself!", - "You attempt to unbuckle yourself. (This will take around 2 minutes and you need to stand still)" - ) - - if(do_after(src, 2 MINUTES, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) - if(!buckled) - return - visible_message("[src] manages to unbuckle themself!", - "You successfully unbuckle yourself.") - buckled.user_unbuckle_mob(src, src) - -/mob/living/carbon/proc/can_break_cuffs() - if(HULK in mutations) - return 1 +/mob/living/carbon/resist_fire() + adjust_fire_stacks(-1.2) + Weaken(3) + spin(32,2) + visible_message( + "[src] rolls on the floor, trying to put themselves out!", + "You stop, drop, and roll!" + ) + sleep(30) + if(fire_stacks <= 0) + visible_message( + "[src] has successfully extinguished themselves!", + "You extinguish yourself." + ) + ExtinguishMob() + return TRUE + +/mob/living/carbon/resist_restraints() + var/obj/item/I = null + if(handcuffed) + I = handcuffed + else if(legcuffed) + I = legcuffed + + if(I) + setClickCooldown(100) + cuff_resist(I, cuff_break = can_break_cuffs()) + +/mob/living/carbon/proc/reduce_cuff_time() + return FALSE + +/mob/living/carbon/proc/cuff_resist(obj/item/weapon/handcuffs/I, breakouttime = 1200, cuff_break = 0) + + if(istype(I)) + breakouttime = I.breakouttime + + var/displaytime = breakouttime / 10 + + var/reduceCuffTime = reduce_cuff_time() + if(reduceCuffTime) + breakouttime /= reduceCuffTime + displaytime /= reduceCuffTime + + if(cuff_break) + visible_message("[src] is trying to break [I]!", + "You attempt to break your [I]. (This will take around 5 seconds and you need to stand still)") + + if(do_after(src, 5 SECONDS, target = src, incapacitation_flags = INCAPACITATION_DEFAULT & ~INCAPACITATION_RESTRAINED)) + if(!I || buckled) + return + visible_message("[src] manages to break [I]!", + "You successfully break your [I].") + say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + + if(I == handcuffed) + handcuffed = null + update_handcuffed() + else if(I == legcuffed) + legcuffed = null + update_inv_legcuffed() + + if(buckled && buckled.buckle_require_restraints) + buckled.unbuckle_mob() + + qdel(I) + else + to_chat(src, "You fail to break [I].") + return + + visible_message("[src] attempts to remove [I]!", + "You attempt to remove [I]. (This will take around [displaytime] seconds and you need to stand still)") + if(do_after(src, breakouttime, target = src, incapacitation_flags = INCAPACITATION_DISABLED & INCAPACITATION_KNOCKDOWN)) + visible_message("[src] manages to remove [I]!", + "You successfully remove [I].") + drop_from_inventory(I) + +/mob/living/carbon/resist_buckle() + if(!buckled) + return + + if(!restrained()) + return ..() + + setClickCooldown(100) + visible_message( + "[src] attempts to unbuckle themself!", + "You attempt to unbuckle yourself. (This will take around 2 minutes and you need to stand still)" + ) + + if(do_after(src, 2 MINUTES, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY))) + if(!buckled) + return + visible_message("[src] manages to unbuckle themself!", + "You successfully unbuckle yourself.") + buckled.user_unbuckle_mob(src, src) + +/mob/living/carbon/proc/can_break_cuffs() + if(HULK in mutations) + return 1 diff --git a/code/modules/mob/living/carbon/viruses.dm b/code/modules/mob/living/carbon/viruses.dm index 2f52dcd1cb4..f57bc878a00 100644 --- a/code/modules/mob/living/carbon/viruses.dm +++ b/code/modules/mob/living/carbon/viruses.dm @@ -1,48 +1,48 @@ -/mob/living/carbon/proc/handle_viruses() - - if(status_flags & GODMODE) return 0 //godmode - - if(bodytemperature > 406) - for (var/ID in virus2) - var/datum/disease2/disease/V = virus2[ID] - V.cure(src) - - if(life_tick % 3) //don't spam checks over all objects in view every tick. - for(var/obj/effect/decal/cleanable/O in view(1,src)) - if(istype(O,/obj/effect/decal/cleanable/blood)) - var/obj/effect/decal/cleanable/blood/B = O - if(B.virus2.len) - for (var/ID in B.virus2) - var/datum/disease2/disease/V = B.virus2[ID] - infect_virus2(src,V) - - else if(istype(O,/obj/effect/decal/cleanable/mucus)) - var/obj/effect/decal/cleanable/mucus/M = O - if(M.virus2.len) - for (var/ID in M.virus2) - var/datum/disease2/disease/V = M.virus2[ID] - infect_virus2(src,V) - - else if(istype(O,/obj/effect/decal/cleanable/vomit)) - var/obj/effect/decal/cleanable/vomit/Vom = O - if(Vom.virus2.len) - for (var/ID in Vom.virus2) - var/datum/disease2/disease/V = Vom.virus2[ID] - infect_virus2(src,V) - - if(virus2.len) - for (var/ID in virus2) - var/datum/disease2/disease/V = virus2[ID] - if(isnull(V)) // Trying to figure out a runtime error that keeps repeating - CRASH("virus2 nulled before calling activate()") - else - V.activate(src) - // activate may have deleted the virus - if(!V) continue - - // check if we're immune - var/list/common_antibodies = V.antigen & src.antibodies - if(common_antibodies.len) - V.dead = 1 - +/mob/living/carbon/proc/handle_viruses() + + if(status_flags & GODMODE) return 0 //godmode + + if(bodytemperature > 406) + for (var/ID in virus2) + var/datum/disease2/disease/V = virus2[ID] + V.cure(src) + + if(life_tick % 3) //don't spam checks over all objects in view every tick. + for(var/obj/effect/decal/cleanable/O in view(1,src)) + if(istype(O,/obj/effect/decal/cleanable/blood)) + var/obj/effect/decal/cleanable/blood/B = O + if(B.virus2.len) + for (var/ID in B.virus2) + var/datum/disease2/disease/V = B.virus2[ID] + infect_virus2(src,V) + + else if(istype(O,/obj/effect/decal/cleanable/mucus)) + var/obj/effect/decal/cleanable/mucus/M = O + if(M.virus2.len) + for (var/ID in M.virus2) + var/datum/disease2/disease/V = M.virus2[ID] + infect_virus2(src,V) + + else if(istype(O,/obj/effect/decal/cleanable/vomit)) + var/obj/effect/decal/cleanable/vomit/Vom = O + if(Vom.virus2.len) + for (var/ID in Vom.virus2) + var/datum/disease2/disease/V = Vom.virus2[ID] + infect_virus2(src,V) + + if(virus2.len) + for (var/ID in virus2) + var/datum/disease2/disease/V = virus2[ID] + if(isnull(V)) // Trying to figure out a runtime error that keeps repeating + CRASH("virus2 nulled before calling activate()") + else + V.activate(src) + // activate may have deleted the virus + if(!V) continue + + // check if we're immune + var/list/common_antibodies = V.antigen & src.antibodies + if(common_antibodies.len) + V.dead = 1 + return \ No newline at end of file diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index babe6c43000..3783df08274 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -1,194 +1,194 @@ - -/* - apply_damage(a,b,c) - args - a:damage - How much damage to take - b:damage_type - What type of damage to take, brute, burn - c:def_zone - Where to take the damage if its brute or burn - Returns - standard 0 if fail -*/ -/mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) - if(Debug2) - to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].") - if(!damage || (blocked >= 100)) - return 0 - for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. - if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_brute_resistance - continue - if((damagetype == BURN || damagetype == ELECTROCUTE)&& (!isnull(M.effective_fire_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_fire_resistance - continue - if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_tox_resistance - continue - if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_oxy_resistance - continue - if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_clone_resistance - continue - if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - damage = damage * M.effective_hal_resistance - continue - if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) - if(M.energy_based) - M.energy_source.use(M.damage_cost * damage) - var/damage_mitigation = 0//Used for dual calculations. - if(!isnull(M.effective_fire_resistance)) - damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) - if(!isnull(M.effective_brute_resistance)) - damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) - damage -= damage_mitigation - continue - if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) - if(isSynthetic()) - damage = damage * M.effective_fire_resistance - else - damage = damage * M.effective_tox_resistance - continue - if(soaked) - if(soaked >= round(damage*0.8)) - damage -= round(damage*0.8) - else - damage -= soaked - - var/initial_blocked = blocked - - blocked = (100-blocked)/100 - switch(damagetype) - if(BRUTE) - adjustBruteLoss(damage * blocked) - if(BURN) - if(COLD_RESISTANCE in mutations) - damage = 0 - adjustFireLoss(damage * blocked) - if(SEARING) - apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) - apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) - if(TOX) - adjustToxLoss(damage * blocked) - if(OXY) - adjustOxyLoss(damage * blocked) - if(CLONE) - adjustCloneLoss(damage * blocked) - if(HALLOSS) - adjustHalLoss(damage * blocked) - if(ELECTROCUTE) - electrocute_act(damage, used_weapon, 1.0, def_zone) - if(BIOACID) - if(isSynthetic()) - apply_damage(damage, BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) // Handle it as normal burn. - else - adjustToxLoss(damage * blocked) - if(ELECTROMAG) - damage = damage * blocked - switch(round(damage)) - if(91 to INFINITY) - emp_act(1) - if(76 to 90) - if(prob(50)) - emp_act(1) - else - emp_act(2) - if(61 to 75) - emp_act(2) - if(46 to 60) - if(prob(50)) - emp_act(2) - else - emp_act(3) - if(31 to 45) - emp_act(3) - if(16 to 30) - if(prob(50)) - emp_act(3) - else - emp_act(4) - if(0 to 15) - emp_act(4) - flash_weak_pain() - updatehealth() - return 1 - - -/mob/living/proc/apply_damages(var/brute = 0, var/burn = 0, var/tox = 0, var/oxy = 0, var/clone = 0, var/halloss = 0, var/def_zone = null, var/blocked = 0) - if(blocked >= 100) - return 0 - // INSERT MODIFIER CODE HERE... But no, really, only two things in the game use it, quad and viruses. The former is admin-only and the latter wouldn't be affected logically, but would if shield code was inerted here. If you really want, you can copy&paste the above and modify it to adjust brute/burn/etc. I do not advise this however. - if(brute) apply_damage(brute, BRUTE, def_zone, blocked) - if(burn) apply_damage(burn, BURN, def_zone, blocked) - if(tox) apply_damage(tox, TOX, def_zone, blocked) - if(oxy) apply_damage(oxy, OXY, def_zone, blocked) - if(clone) apply_damage(clone, CLONE, def_zone, blocked) - if(halloss) apply_damage(halloss, HALLOSS, def_zone, blocked) - return 1 - - - -/mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) - if(Debug2) - to_world_log("## DEBUG: apply_effect() was called. The type of effect is [effecttype]. Blocked by [blocked].") - if(!effect || (blocked >= 100)) - return 0 - blocked = (100-blocked)/100 - - switch(effecttype) - if(STUN) - Stun(effect * blocked) - if(WEAKEN) - Weaken(effect * blocked) - if(PARALYZE) - Paralyse(effect * blocked) - if(AGONY) - halloss += max((effect * blocked), 0) // Useful for objects that cause "subdual" damage. PAIN! - if(IRRADIATE) - /* - var/rad_protection = check_protection ? getarmor(null, "rad")/100 : 0 - radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor - */ - var/rad_protection = getarmor(null, "rad") - rad_protection = (100-rad_protection)/100 - radiation += max((effect * rad_protection), 0) - if(STUTTER) - if(status_flags & CANSTUN) // stun is usually associated with stutter - stuttering = max(stuttering,(effect * blocked)) - if(EYE_BLUR) - eye_blurry = max(eye_blurry,(effect * blocked)) - if(DROWSY) - drowsyness = max(drowsyness,(effect * blocked)) - updatehealth() - return 1 - - -/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/agony = 0, var/blocked = 0, var/ignite = 0, var/flammable = 0) - if(blocked >= 100) - return 0 - if(stun) apply_effect(stun, STUN, blocked) - if(weaken) apply_effect(weaken, WEAKEN, blocked) - if(paralyze) apply_effect(paralyze, PARALYZE, blocked) - if(irradiate) apply_effect(irradiate, IRRADIATE, blocked) - if(stutter) apply_effect(stutter, STUTTER, blocked) - if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked) - if(drowsy) apply_effect(drowsy, DROWSY, blocked) - if(agony) apply_effect(agony, AGONY, blocked) - if(flammable) adjust_fire_stacks(flammable) - if(ignite) - if(ignite >= 3) - add_modifier(/datum/modifier/fire/stack_managed/intense, 60 SECONDS) - else - add_modifier(/datum/modifier/fire/stack_managed, 45 * ignite SECONDS) - return 1 + +/* + apply_damage(a,b,c) + args + a:damage - How much damage to take + b:damage_type - What type of damage to take, brute, burn + c:def_zone - Where to take the damage if its brute or burn + Returns + standard 0 if fail +*/ +/mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = FALSE, var/edge = FALSE, var/obj/used_weapon = null) + if(Debug2) + to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].") + if(!damage || (blocked >= 100)) + return 0 + for(var/datum/modifier/M in modifiers) //MODIFIER STUFF. It's best to do this RIGHT before armor is calculated, so it's done here! This is the 'forcefield' defence. + if(damagetype == BRUTE && (!isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_brute_resistance + continue + if((damagetype == BURN || damagetype == ELECTROCUTE)&& (!isnull(M.effective_fire_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_fire_resistance + continue + if(damagetype == TOX && (!isnull(M.effective_tox_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_tox_resistance + continue + if(damagetype == OXY && (!isnull(M.effective_oxy_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_oxy_resistance + continue + if(damagetype == CLONE && (!isnull(M.effective_clone_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_clone_resistance + continue + if(damagetype == HALLOSS && (!isnull(M.effective_hal_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + damage = damage * M.effective_hal_resistance + continue + if(damagetype == SEARING && (!isnull(M.effective_fire_resistance) || !isnull(M.effective_brute_resistance))) + if(M.energy_based) + M.energy_source.use(M.damage_cost * damage) + var/damage_mitigation = 0//Used for dual calculations. + if(!isnull(M.effective_fire_resistance)) + damage_mitigation += round((1/3)*damage * M.effective_fire_resistance) + if(!isnull(M.effective_brute_resistance)) + damage_mitigation += round((2/3)*damage * M.effective_brute_resistance) + damage -= damage_mitigation + continue + if(damagetype == BIOACID && (isSynthetic() && (!isnull(M.effective_fire_resistance))) || (!isSynthetic() && M.effective_tox_resistance)) + if(isSynthetic()) + damage = damage * M.effective_fire_resistance + else + damage = damage * M.effective_tox_resistance + continue + if(soaked) + if(soaked >= round(damage*0.8)) + damage -= round(damage*0.8) + else + damage -= soaked + + var/initial_blocked = blocked + + blocked = (100-blocked)/100 + switch(damagetype) + if(BRUTE) + adjustBruteLoss(damage * blocked) + if(BURN) + if(COLD_RESISTANCE in mutations) + damage = 0 + adjustFireLoss(damage * blocked) + if(SEARING) + apply_damage(round(damage / 3), BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) + apply_damage(round(damage / 3 * 2), BRUTE, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) + if(TOX) + adjustToxLoss(damage * blocked) + if(OXY) + adjustOxyLoss(damage * blocked) + if(CLONE) + adjustCloneLoss(damage * blocked) + if(HALLOSS) + adjustHalLoss(damage * blocked) + if(ELECTROCUTE) + electrocute_act(damage, used_weapon, 1.0, def_zone) + if(BIOACID) + if(isSynthetic()) + apply_damage(damage, BURN, def_zone, initial_blocked, soaked, used_weapon, sharp, edge) // Handle it as normal burn. + else + adjustToxLoss(damage * blocked) + if(ELECTROMAG) + damage = damage * blocked + switch(round(damage)) + if(91 to INFINITY) + emp_act(1) + if(76 to 90) + if(prob(50)) + emp_act(1) + else + emp_act(2) + if(61 to 75) + emp_act(2) + if(46 to 60) + if(prob(50)) + emp_act(2) + else + emp_act(3) + if(31 to 45) + emp_act(3) + if(16 to 30) + if(prob(50)) + emp_act(3) + else + emp_act(4) + if(0 to 15) + emp_act(4) + flash_weak_pain() + updatehealth() + return 1 + + +/mob/living/proc/apply_damages(var/brute = 0, var/burn = 0, var/tox = 0, var/oxy = 0, var/clone = 0, var/halloss = 0, var/def_zone = null, var/blocked = 0) + if(blocked >= 100) + return 0 + // INSERT MODIFIER CODE HERE... But no, really, only two things in the game use it, quad and viruses. The former is admin-only and the latter wouldn't be affected logically, but would if shield code was inerted here. If you really want, you can copy&paste the above and modify it to adjust brute/burn/etc. I do not advise this however. + if(brute) apply_damage(brute, BRUTE, def_zone, blocked) + if(burn) apply_damage(burn, BURN, def_zone, blocked) + if(tox) apply_damage(tox, TOX, def_zone, blocked) + if(oxy) apply_damage(oxy, OXY, def_zone, blocked) + if(clone) apply_damage(clone, CLONE, def_zone, blocked) + if(halloss) apply_damage(halloss, HALLOSS, def_zone, blocked) + return 1 + + + +/mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) + if(Debug2) + to_world_log("## DEBUG: apply_effect() was called. The type of effect is [effecttype]. Blocked by [blocked].") + if(!effect || (blocked >= 100)) + return 0 + blocked = (100-blocked)/100 + + switch(effecttype) + if(STUN) + Stun(effect * blocked) + if(WEAKEN) + Weaken(effect * blocked) + if(PARALYZE) + Paralyse(effect * blocked) + if(AGONY) + halloss += max((effect * blocked), 0) // Useful for objects that cause "subdual" damage. PAIN! + if(IRRADIATE) + /* + var/rad_protection = check_protection ? getarmor(null, "rad")/100 : 0 + radiation += max((1-rad_protection)*effect/(blocked+1),0)//Rads auto check armor + */ + var/rad_protection = getarmor(null, "rad") + rad_protection = (100-rad_protection)/100 + radiation += max((effect * rad_protection), 0) + if(STUTTER) + if(status_flags & CANSTUN) // stun is usually associated with stutter + stuttering = max(stuttering,(effect * blocked)) + if(EYE_BLUR) + eye_blurry = max(eye_blurry,(effect * blocked)) + if(DROWSY) + drowsyness = max(drowsyness,(effect * blocked)) + updatehealth() + return 1 + + +/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/agony = 0, var/blocked = 0, var/ignite = 0, var/flammable = 0) + if(blocked >= 100) + return 0 + if(stun) apply_effect(stun, STUN, blocked) + if(weaken) apply_effect(weaken, WEAKEN, blocked) + if(paralyze) apply_effect(paralyze, PARALYZE, blocked) + if(irradiate) apply_effect(irradiate, IRRADIATE, blocked) + if(stutter) apply_effect(stutter, STUTTER, blocked) + if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked) + if(drowsy) apply_effect(drowsy, DROWSY, blocked) + if(agony) apply_effect(agony, AGONY, blocked) + if(flammable) adjust_fire_stacks(flammable) + if(ignite) + if(ignite >= 3) + add_modifier(/datum/modifier/fire/stack_managed/intense, 60 SECONDS) + else + add_modifier(/datum/modifier/fire/stack_managed, 45 * ignite SECONDS) + return 1 diff --git a/code/modules/mob/living/default_language.dm b/code/modules/mob/living/default_language.dm index eefe4929f9f..c83980f18f1 100644 --- a/code/modules/mob/living/default_language.dm +++ b/code/modules/mob/living/default_language.dm @@ -1,36 +1,36 @@ -/mob/living - var/datum/language/default_language - -/mob/living/verb/set_default_language(language as null|anything in languages) - set name = "Set Default Language" - set category = "IC" - - if (only_species_language && language != GLOB.all_languages[src.species_language]) - to_chat(src, "You can only speak your species language, [src.species_language].") - return 0 - - if(language == GLOB.all_languages[src.species_language]) - to_chat(src, "You will now speak your standard default language, [language ? language : "common"], if you do not specify a language when speaking.") - else if (language) - - if(language && !can_speak(language)) - to_chat(src, "You are unable to speak that language.") - return - - to_chat(src, "You will now speak [language] if you do not specify a language when speaking.") - else - to_chat(src, "You will now speak whatever your standard default language is if you do not specify one when speaking.") - default_language = language - -// Silicons can't neccessarily speak everything in their languages list -/mob/living/silicon/set_default_language(language as null|anything in speech_synthesizer_langs) - ..() - -/mob/living/verb/check_default_language() - set name = "Check Default Language" - set category = "IC" - - if(default_language) - to_chat(src, "You are currently speaking [default_language] by default.") - else - to_chat(src, "Your current default language is your species or mob type default.") +/mob/living + var/datum/language/default_language + +/mob/living/verb/set_default_language(language as null|anything in languages) + set name = "Set Default Language" + set category = "IC" + + if (only_species_language && language != GLOB.all_languages[src.species_language]) + to_chat(src, "You can only speak your species language, [src.species_language].") + return 0 + + if(language == GLOB.all_languages[src.species_language]) + to_chat(src, "You will now speak your standard default language, [language ? language : "common"], if you do not specify a language when speaking.") + else if (language) + + if(language && !can_speak(language)) + to_chat(src, "You are unable to speak that language.") + return + + to_chat(src, "You will now speak [language] if you do not specify a language when speaking.") + else + to_chat(src, "You will now speak whatever your standard default language is if you do not specify one when speaking.") + default_language = language + +// Silicons can't neccessarily speak everything in their languages list +/mob/living/silicon/set_default_language(language as null|anything in speech_synthesizer_langs) + ..() + +/mob/living/verb/check_default_language() + set name = "Check Default Language" + set category = "IC" + + if(default_language) + to_chat(src, "You are currently speaking [default_language] by default.") + else + to_chat(src, "Your current default language is your species or mob type default.") diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index eb7462967e1..319fcce97e4 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -1,284 +1,284 @@ -/mob/living/Life() - set invisibility = 0 - set background = BACKGROUND_ENABLED - - ..() - - if (transforming) - return - handle_modifiers() //VOREStation Edit - Needs to be done even if in nullspace. - if(!loc) - return - - var/datum/gas_mixture/environment - if(isbelly(loc)) - environment = loc.return_air_for_internal_lifeform(src) - else - environment = loc.return_air() - - //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit - - handle_light() - - if(stat != DEAD) - //Breathing, if applicable - handle_breathing() - - //Mutations and radiation - handle_mutations_and_radiation() - - - - //Blood - handle_blood() - - //Random events (vomiting etc) - handle_random_events() - - . = 1 - - //Chemicals in the body, this is moved over here so that blood can be added after death - handle_chemicals_in_body() - - //Handle temperature/pressure differences between body and environment - if(environment) - handle_environment(environment) - - //Check if we're on fire - handle_fire() - - if(client && !(client.prefs.ambience_freq == 0)) // Handle re-running ambience to mobs if they've remained in an area, AND have an active client assigned to them, and do not have repeating ambience disabled. - handle_ambience() - - //stuff in the stomach - //handle_stomach() //VOREStation Code - - update_gravity(mob_has_gravity()) - - update_pulling() - - for(var/obj/item/weapon/grab/G in src) - G.process() - - if(handle_regular_status_updates()) // Status & health update, are we dead or alive etc. - handle_disabilities() // eye, ear, brain damages - handle_statuses() //all special effects, stunned, weakened, jitteryness, hallucination, sleeping, etc - - handle_actions() - - update_canmove() - - handle_regular_hud_updates() - - handle_vision() - - handle_tf_holder() //VOREStation Addition - -/mob/living/proc/handle_breathing() - return - -/mob/living/proc/handle_mutations_and_radiation() - return - -/mob/living/proc/handle_chemicals_in_body() - return - -/mob/living/proc/handle_blood() - return - -/mob/living/proc/handle_random_events() - return - -/mob/living/proc/handle_environment(var/datum/gas_mixture/environment) - return - -/mob/living/proc/handle_stomach() - return - -/mob/living/proc/handle_ambience() // If you're in an ambient area and have not moved out of it for x time as configured per-client, and do not have it disabled, we're going to play ambience again to you, to help break up the silence. - if(world.time >= (lastareachange + client.prefs.ambience_freq MINUTES)) // Every 5 minutes (by default, set per-client), we're going to run a 35% chance (by default, also set per-client) to play ambience. - var/area/A = get_area(src) - if(A) - lastareachange = world.time // This will refresh the last area change to prevent this call happening LITERALLY every life tick. - A.play_ambience(src, initial = FALSE) - -/mob/living/proc/update_pulling() - if(pulling) - if(incapacitated()) - stop_pulling() - -//This updates the health and status of the mob (conscious, unconscious, dead) -/mob/living/proc/handle_regular_status_updates() - updatehealth() - if(stat != DEAD) - if(paralysis) - set_stat(UNCONSCIOUS) - else if (status_flags & FAKEDEATH) - set_stat(UNCONSCIOUS) - else - set_stat(CONSCIOUS) - return 1 - -/mob/living/proc/handle_statuses() - handle_stunned() - handle_weakened() - handle_paralysed() - handle_stuttering() - handle_silent() - handle_drugged() - handle_slurring() - handle_confused() - -/mob/living/proc/handle_stunned() - if(stunned) - AdjustStunned(-1) - throw_alert("stunned", /obj/screen/alert/stunned) - else - clear_alert("stunned") - return stunned - -/mob/living/proc/handle_weakened() - if(weakened) - AdjustWeakened(-1) - throw_alert("weakened", /obj/screen/alert/weakened) - else - clear_alert("weakened") - return weakened - -/mob/living/proc/handle_stuttering() - if(stuttering) - stuttering = max(stuttering-1, 0) - return stuttering - -/mob/living/proc/handle_silent() - if(silent) - silent = max(silent-1, 0) - return silent - -/mob/living/proc/handle_drugged() - if(druggy) - druggy = max(druggy-1, 0) - throw_alert("high", /obj/screen/alert/high) - else - clear_alert("high") - return druggy - -/mob/living/proc/handle_slurring() - if(slurring) - slurring = max(slurring-1, 0) - return slurring - -/mob/living/proc/handle_paralysed() - if(paralysis) - AdjustParalysis(-1) - throw_alert("paralyzed", /obj/screen/alert/paralyzed) - else - clear_alert("paralyzed") - return paralysis - -/mob/living/proc/handle_confused() - if(confused) - AdjustConfused(-1) - throw_alert("confused", /obj/screen/alert/confused) - else - clear_alert("confused") - return confused - -/mob/living/proc/handle_disabilities() - //Eyes - if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own - SetBlinded(1) - throw_alert("blind", /obj/screen/alert/blind) - else if(eye_blind) //blindness, heals slowly over time - AdjustBlinded(-1) - throw_alert("blind", /obj/screen/alert/blind) - else - clear_alert("blind") - - if(eye_blurry) //blurry eyes heal slowly - eye_blurry = max(eye_blurry-1, 0) - - //Ears - if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own - setEarDamage(-1, max(ear_deaf, 1)) - else - // deafness heals slowly over time, unless ear_damage is over 100 - if(ear_damage < 100) - adjustEarDamage(-0.05,-1) - -/mob/living/handle_regular_hud_updates() - if(!client) - return 0 - ..() - - handle_darksight() - handle_hud_icons() - - return 1 - -/mob/living/proc/update_sight() - if(!seedarkness) - see_invisible = SEE_INVISIBLE_NOLIGHTING - else - see_invisible = initial(see_invisible) - - sight = initial(sight) - - for(var/datum/modifier/M in modifiers) - if(!isnull(M.vision_flags)) - sight |= M.vision_flags - - return - -/mob/living/proc/handle_hud_icons() - handle_hud_icons_health() - return - -/mob/living/proc/handle_hud_icons_health() - return - -/mob/living/proc/handle_light() - if(glow_override) - return FALSE - - if(instability >= TECHNOMANCER_INSTABILITY_MIN_GLOW) - var/distance = round(sqrt(instability / 2)) - if(distance) - set_light(distance, distance * 4, l_color = "#660066") - return TRUE - - else if(glow_toggle) - set_light(glow_range, glow_intensity, glow_color) - - else - set_light(0) - return FALSE - -/mob/living/proc/handle_darksight() - if(!seedarkness) //Cheap 'always darksight' var - dsoverlay.alpha = 255 - return - - var/darksightedness = min(see_in_dark/world.view,1.0) //A ratio of how good your darksight is, from 'nada' to 'really darn good' - var/current = dsoverlay.alpha/255 //Our current adjustedness - - var/brightness = 0.0 //We'll assume it's superdark if we can't find something else. - - if(isturf(loc)) - var/turf/T = loc //Will be true 99% of the time, thus avoiding the whole elif chain - brightness = T.get_lumcount() - - //Snowflake treatment of potential locations - else if(istype(loc,/obj/mecha)) //I imagine there's like displays and junk in there. Use the lights! - brightness = 1 - else if(istype(loc,/obj/item/weapon/holder)) //Poor carried teshari and whatnot should adjust appropriately - var/turf/T = get_turf(src) - brightness = T.get_lumcount() - - var/darkness = 1-brightness //Silly, I know, but 'alpha' and 'darkness' go the same direction on a number line - var/adjust_to = min(darkness,darksightedness)//Capped by how darksighted they are - var/distance = abs(current-adjust_to) //Used for how long to animate for - if(distance < 0.01) return //We're already all set - - //to_world("[src] in B:[round(brightness,0.1)] C:[round(current,0.1)] A2:[round(adjust_to,0.1)] D:[round(distance,0.01)] T:[round(distance*10 SECONDS,0.1)]") - animate(dsoverlay, alpha = (adjust_to*255), time = (distance*10 SECONDS)) +/mob/living/Life() + set invisibility = 0 + set background = BACKGROUND_ENABLED + + ..() + + if (transforming) + return + handle_modifiers() //VOREStation Edit - Needs to be done even if in nullspace. + if(!loc) + return + + var/datum/gas_mixture/environment + if(isbelly(loc)) + environment = loc.return_air_for_internal_lifeform(src) + else + environment = loc.return_air() + + //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit + + handle_light() + + if(stat != DEAD) + //Breathing, if applicable + handle_breathing() + + //Mutations and radiation + handle_mutations_and_radiation() + + + + //Blood + handle_blood() + + //Random events (vomiting etc) + handle_random_events() + + . = 1 + + //Chemicals in the body, this is moved over here so that blood can be added after death + handle_chemicals_in_body() + + //Handle temperature/pressure differences between body and environment + if(environment) + handle_environment(environment) + + //Check if we're on fire + handle_fire() + + if(client && !(client.prefs.ambience_freq == 0)) // Handle re-running ambience to mobs if they've remained in an area, AND have an active client assigned to them, and do not have repeating ambience disabled. + handle_ambience() + + //stuff in the stomach + //handle_stomach() //VOREStation Code + + update_gravity(mob_has_gravity()) + + update_pulling() + + for(var/obj/item/weapon/grab/G in src) + G.process() + + if(handle_regular_status_updates()) // Status & health update, are we dead or alive etc. + handle_disabilities() // eye, ear, brain damages + handle_statuses() //all special effects, stunned, weakened, jitteryness, hallucination, sleeping, etc + + handle_actions() + + update_canmove() + + handle_regular_hud_updates() + + handle_vision() + + handle_tf_holder() //VOREStation Addition + +/mob/living/proc/handle_breathing() + return + +/mob/living/proc/handle_mutations_and_radiation() + return + +/mob/living/proc/handle_chemicals_in_body() + return + +/mob/living/proc/handle_blood() + return + +/mob/living/proc/handle_random_events() + return + +/mob/living/proc/handle_environment(var/datum/gas_mixture/environment) + return + +/mob/living/proc/handle_stomach() + return + +/mob/living/proc/handle_ambience() // If you're in an ambient area and have not moved out of it for x time as configured per-client, and do not have it disabled, we're going to play ambience again to you, to help break up the silence. + if(world.time >= (lastareachange + client.prefs.ambience_freq MINUTES)) // Every 5 minutes (by default, set per-client), we're going to run a 35% chance (by default, also set per-client) to play ambience. + var/area/A = get_area(src) + if(A) + lastareachange = world.time // This will refresh the last area change to prevent this call happening LITERALLY every life tick. + A.play_ambience(src, initial = FALSE) + +/mob/living/proc/update_pulling() + if(pulling) + if(incapacitated()) + stop_pulling() + +//This updates the health and status of the mob (conscious, unconscious, dead) +/mob/living/proc/handle_regular_status_updates() + updatehealth() + if(stat != DEAD) + if(paralysis) + set_stat(UNCONSCIOUS) + else if (status_flags & FAKEDEATH) + set_stat(UNCONSCIOUS) + else + set_stat(CONSCIOUS) + return 1 + +/mob/living/proc/handle_statuses() + handle_stunned() + handle_weakened() + handle_paralysed() + handle_stuttering() + handle_silent() + handle_drugged() + handle_slurring() + handle_confused() + +/mob/living/proc/handle_stunned() + if(stunned) + AdjustStunned(-1) + throw_alert("stunned", /obj/screen/alert/stunned) + else + clear_alert("stunned") + return stunned + +/mob/living/proc/handle_weakened() + if(weakened) + AdjustWeakened(-1) + throw_alert("weakened", /obj/screen/alert/weakened) + else + clear_alert("weakened") + return weakened + +/mob/living/proc/handle_stuttering() + if(stuttering) + stuttering = max(stuttering-1, 0) + return stuttering + +/mob/living/proc/handle_silent() + if(silent) + silent = max(silent-1, 0) + return silent + +/mob/living/proc/handle_drugged() + if(druggy) + druggy = max(druggy-1, 0) + throw_alert("high", /obj/screen/alert/high) + else + clear_alert("high") + return druggy + +/mob/living/proc/handle_slurring() + if(slurring) + slurring = max(slurring-1, 0) + return slurring + +/mob/living/proc/handle_paralysed() + if(paralysis) + AdjustParalysis(-1) + throw_alert("paralyzed", /obj/screen/alert/paralyzed) + else + clear_alert("paralyzed") + return paralysis + +/mob/living/proc/handle_confused() + if(confused) + AdjustConfused(-1) + throw_alert("confused", /obj/screen/alert/confused) + else + clear_alert("confused") + return confused + +/mob/living/proc/handle_disabilities() + //Eyes + if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own + SetBlinded(1) + throw_alert("blind", /obj/screen/alert/blind) + else if(eye_blind) //blindness, heals slowly over time + AdjustBlinded(-1) + throw_alert("blind", /obj/screen/alert/blind) + else + clear_alert("blind") + + if(eye_blurry) //blurry eyes heal slowly + eye_blurry = max(eye_blurry-1, 0) + + //Ears + if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own + setEarDamage(-1, max(ear_deaf, 1)) + else + // deafness heals slowly over time, unless ear_damage is over 100 + if(ear_damage < 100) + adjustEarDamage(-0.05,-1) + +/mob/living/handle_regular_hud_updates() + if(!client) + return 0 + ..() + + handle_darksight() + handle_hud_icons() + + return 1 + +/mob/living/proc/update_sight() + if(!seedarkness) + see_invisible = SEE_INVISIBLE_NOLIGHTING + else + see_invisible = initial(see_invisible) + + sight = initial(sight) + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.vision_flags)) + sight |= M.vision_flags + + return + +/mob/living/proc/handle_hud_icons() + handle_hud_icons_health() + return + +/mob/living/proc/handle_hud_icons_health() + return + +/mob/living/proc/handle_light() + if(glow_override) + return FALSE + + if(instability >= TECHNOMANCER_INSTABILITY_MIN_GLOW) + var/distance = round(sqrt(instability / 2)) + if(distance) + set_light(distance, distance * 4, l_color = "#660066") + return TRUE + + else if(glow_toggle) + set_light(glow_range, glow_intensity, glow_color) + + else + set_light(0) + return FALSE + +/mob/living/proc/handle_darksight() + if(!seedarkness) //Cheap 'always darksight' var + dsoverlay.alpha = 255 + return + + var/darksightedness = min(see_in_dark/world.view,1.0) //A ratio of how good your darksight is, from 'nada' to 'really darn good' + var/current = dsoverlay.alpha/255 //Our current adjustedness + + var/brightness = 0.0 //We'll assume it's superdark if we can't find something else. + + if(isturf(loc)) + var/turf/T = loc //Will be true 99% of the time, thus avoiding the whole elif chain + brightness = T.get_lumcount() + + //Snowflake treatment of potential locations + else if(istype(loc,/obj/mecha)) //I imagine there's like displays and junk in there. Use the lights! + brightness = 1 + else if(istype(loc,/obj/item/weapon/holder)) //Poor carried teshari and whatnot should adjust appropriately + var/turf/T = get_turf(src) + brightness = T.get_lumcount() + + var/darkness = 1-brightness //Silly, I know, but 'alpha' and 'darkness' go the same direction on a number line + var/adjust_to = min(darkness,darksightedness)//Capped by how darksighted they are + var/distance = abs(current-adjust_to) //Used for how long to animate for + if(distance < 0.01) return //We're already all set + + //to_world("[src] in B:[round(brightness,0.1)] C:[round(current,0.1)] A2:[round(adjust_to,0.1)] D:[round(distance,0.01)] T:[round(distance*10 SECONDS,0.1)]") + animate(dsoverlay, alpha = (adjust_to*255), time = (distance*10 SECONDS)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index ed834c5561e..efe4bc2fd43 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -109,14 +109,14 @@ confirm2 = tgui_alert(usr, "Pressing this buttom will really kill you, no going back", "Are you sure?", list("Yes", "No")) //Swapped answers to protect from accidental double clicks. if (src.health < 0 && stat != DEAD && confirm1 == "Yes" && confirm2 == "Yes") // Checking both confirm1 and confirm2 for good measure. I don't trust TGUI. src.death() - to_chat(src, "You have given up life and succumbed to death.") + to_chat(src, span_blue("You have given up life and succumbed to death.")) else if(stat == DEAD) - to_chat(src, "As much as you'd like, you can't die when already dead") + to_chat(src, span_blue("As much as you'd like, you can't die when already dead")) else if(confirm1 == "No" || confirm2 == "No") - to_chat(src, "You chose to live another day.") + to_chat(src, span_blue("You chose to live another day.")) else - to_chat(src, "You are not injured enough to succumb to death!") + to_chat(src, span_blue("You are not injured enough to succumb to death!")) /mob/living/proc/updatehealth() if(status_flags & GODMODE) @@ -1012,15 +1012,18 @@ if(!isnull(M.icon_scale_y_percent)) . *= M.icon_scale_y_percent -/mob/living/update_transform() +/mob/living/update_transform(var/instant = FALSE) // First, get the correct size. var/desired_scale_x = size_multiplier * icon_scale_x //VOREStation edit var/desired_scale_y = size_multiplier * icon_scale_y //VOREStation edit + var/cent_offset = center_offset // Now for the regular stuff. + if(fuzzy || offset_override || dir == EAST || dir == WEST) + cent_offset = 0 var/matrix/M = matrix() M.Scale(desired_scale_x, desired_scale_y) - M.Translate(0, (vis_height/2)*(desired_scale_y-1)) //VOREStation edit + M.Translate(cent_offset * desired_scale_x, (vis_height/2)*(desired_scale_y-1)) src.transform = M //VOREStation edit handle_status_indicators() @@ -1303,3 +1306,8 @@ icon = 'icons/mob/screen/midnight.dmi' icon_state = "character" screen_loc = ui_smallquad + +/mob/living/set_dir(var/new_dir) + . = ..() + if(size_multiplier != 1 || icon_scale_x != 1 && center_offset > 0) + update_transform(TRUE) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index f30c02a1519..a686d4501ee 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -269,7 +269,7 @@ visible_message("\The [O] misses [src] narrowly!") return - src.visible_message("[src] has been hit by [O].") + src.visible_message("[span_red("[src] has been hit by [O].")]") var/armor = run_armor_check(null, "melee") var/soaked = get_armor_soak(null, "melee") @@ -296,7 +296,7 @@ if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED) var/dir = get_dir(O.throw_source, src) - visible_message("[src] staggers under the impact!","You stagger under the impact!") + visible_message("[span_red("[src] staggers under the impact!")]","[span_red("You stagger under the impact!")]") src.throw_at(get_edge_target_turf(src,dir),1,momentum) if(!O || !src) return @@ -330,7 +330,7 @@ // PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY. if((can_be_drop_pred && throw_vore) && (thrown_mob.devourable && thrown_mob.throw_vore && thrown_mob.can_be_drop_prey)) //Prey thrown into pred. vore_selected.nom_mob(thrown_mob) //Eat them!!! - visible_message("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!") + visible_message("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!") if(thrown_mob.loc != vore_selected) thrown_mob.forceMove(vore_selected) //Double check. Should never happen but...Weirder things have happened! on_throw_vore_special(TRUE, thrown_mob) @@ -340,7 +340,7 @@ // PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE. // PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE. else if((can_be_drop_prey && throw_vore && devourable) && (thrown_mob.can_be_drop_pred && thrown_mob.throw_vore)) //Pred thrown into prey. - visible_message("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!") + visible_message("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!") thrown_mob.vore_selected.nom_mob(src) //Eat them!!! if(src.loc != thrown_mob.vore_selected) src.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened! diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 5a4767c957a..7bdb20349d9 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -1,84 +1,84 @@ -/mob/living - see_invisible = SEE_INVISIBLE_LIVING - - //Health and life related vars - var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums. - var/health = 100 //A mob's health - - var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. - - var/hud_updateflag = 0 - - //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS - var/bruteloss = 0.0 //Brutal damage caused by brute force (punching, being clubbed by a toolbox ect... this also accounts for pressure damage) - var/oxyloss = 0.0 //Oxygen depravation damage (no air in lungs) - var/toxloss = 0.0 //Toxic damage caused by being poisoned or radiated - var/fireloss = 0.0 //Burn damage caused by being way too hot, too cold or burnt. - var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims - var/brainloss = 0 //Thought-scrambly damage caused by someone hitting you in the head with a bible or being infected with brainrot. - var/halloss = 0 //Hallucination damage. 'Fake' damage obtained through hallucinating or the holodeck. Sleeping should cause it to wear off. - - var/nutrition = 400 - var/max_nutrition = MAX_NUTRITION - - var/hallucination = 0 //Directly affects how long a mob will hallucinate for - var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm - - var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. - var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN - - var/t_phoron = null - var/t_oxygen = null - var/t_sl_gas = null - var/t_n2 = null - - var/now_pushing = null - var/mob_bump_flag = 0 - var/mob_swap_flags = 0 - var/mob_push_flags = 0 - var/mob_always_swap = 0 - - var/mob/living/cameraFollow = null - var/list/datum/action/actions = list() - - var/tod = null // Time of death - var/update_slimes = 1 - var/silent = null // Can't talk. Value goes down every life proc. - var/on_fire = 0 //The "Are we on fire?" var - var/fire_stacks - - var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. - var/lastpuke = 0 - - var/evasion = 0 // Makes attacks harder to land. Negative numbers increase hit chance. - var/force_max_speed = 0 // If 1, the mob runs extremely fast and cannot be slowed. - - var/image/dsoverlay = null //Overlay used for darksight eye adjustments - - var/glow_toggle = FALSE // If they're glowing! - var/glow_override = FALSE // Ignore the manual toggle - var/glow_range = 2 - var/glow_intensity = null - var/glow_color = "#FFFFFF" // The color they're glowing! - - var/see_invisible_default = SEE_INVISIBLE_LIVING - - var/nest //Not specific, because a Nest may be the prop nest, or blob factory in this case. - - var/list/hud_list //Holder for health hud, status hud, wanted hud, etc (not like inventory slots) - var/has_huds = FALSE //Whether or not we should bother initializing the above list - - var/makes_dirt = TRUE //FALSE if the mob shouldn't be making dirt on the ground when it walks - - var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs - - var/image/selected_image = null // Used for buildmode AI control stuff. - - var/allow_self_surgery = FALSE // Used to determine if the mob can perform surgery on itself. - - - var/tail_alt = 0 - var/flying = 0 // Allows flight - var/inventory_panel_type = /datum/inventory_panel - var/datum/inventory_panel/inventory_panel - var/last_resist_time = 0 // world.time of the most recent resist that wasn't on cooldown. +/mob/living + see_invisible = SEE_INVISIBLE_LIVING + + //Health and life related vars + var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums. + var/health = 100 //A mob's health + + var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. + + var/hud_updateflag = 0 + + //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS + var/bruteloss = 0.0 //Brutal damage caused by brute force (punching, being clubbed by a toolbox ect... this also accounts for pressure damage) + var/oxyloss = 0.0 //Oxygen depravation damage (no air in lungs) + var/toxloss = 0.0 //Toxic damage caused by being poisoned or radiated + var/fireloss = 0.0 //Burn damage caused by being way too hot, too cold or burnt. + var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims + var/brainloss = 0 //Thought-scrambly damage caused by someone hitting you in the head with a bible or being infected with brainrot. + var/halloss = 0 //Hallucination damage. 'Fake' damage obtained through hallucinating or the holodeck. Sleeping should cause it to wear off. + + var/nutrition = 400 + var/max_nutrition = MAX_NUTRITION + + var/hallucination = 0 //Directly affects how long a mob will hallucinate for + var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm + + var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. + var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN + + var/t_phoron = null + var/t_oxygen = null + var/t_sl_gas = null + var/t_n2 = null + + var/now_pushing = null + var/mob_bump_flag = 0 + var/mob_swap_flags = 0 + var/mob_push_flags = 0 + var/mob_always_swap = 0 + + var/mob/living/cameraFollow = null + var/list/datum/action/actions = list() + + var/tod = null // Time of death + var/update_slimes = 1 + var/silent = null // Can't talk. Value goes down every life proc. + var/on_fire = 0 //The "Are we on fire?" var + var/fire_stacks + + var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. + var/lastpuke = 0 + + var/evasion = 0 // Makes attacks harder to land. Negative numbers increase hit chance. + var/force_max_speed = 0 // If 1, the mob runs extremely fast and cannot be slowed. + + var/image/dsoverlay = null //Overlay used for darksight eye adjustments + + var/glow_toggle = FALSE // If they're glowing! + var/glow_override = FALSE // Ignore the manual toggle + var/glow_range = 2 + var/glow_intensity = null + var/glow_color = "#FFFFFF" // The color they're glowing! + + var/see_invisible_default = SEE_INVISIBLE_LIVING + + var/nest //Not specific, because a Nest may be the prop nest, or blob factory in this case. + + var/list/hud_list //Holder for health hud, status hud, wanted hud, etc (not like inventory slots) + var/has_huds = FALSE //Whether or not we should bother initializing the above list + + var/makes_dirt = TRUE //FALSE if the mob shouldn't be making dirt on the ground when it walks + + var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs + + var/image/selected_image = null // Used for buildmode AI control stuff. + + var/allow_self_surgery = FALSE // Used to determine if the mob can perform surgery on itself. + + + var/tail_alt = 0 + var/flying = 0 // Allows flight + var/inventory_panel_type = /datum/inventory_panel + var/datum/inventory_panel/inventory_panel + var/last_resist_time = 0 // world.time of the most recent resist that wasn't on cooldown. diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 4918df39f01..9ee08bf8a7b 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -276,8 +276,8 @@ default behaviour is: is_shifted = FALSE pixel_x = default_pixel_x pixel_y = default_pixel_y - layer = MOB_LAYER - plane = MOB_PLANE + layer = initial(layer) + plane = initial(plane) // End VOREstation edit if(pulling) // we were pulling a thing and didn't lose it during our move. diff --git a/code/modules/mob/living/living_vr.dm b/code/modules/mob/living/living_vr.dm index f43f9c86ab5..582ed83db6c 100644 --- a/code/modules/mob/living/living_vr.dm +++ b/code/modules/mob/living/living_vr.dm @@ -54,7 +54,7 @@ if(usr != src) return var/new_metadata = strip_html_simple(tgui_input_text(usr, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the OOC notes panel!", "Game Preference" , html_decode(ooc_notes_likes), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(usr)) + if(CanUseTopic(usr)) ooc_notes_likes = new_metadata client.prefs.metadata_likes = new_metadata to_chat(usr, "OOC note likes have been updated. Don't forget to save!") @@ -66,7 +66,7 @@ if(usr != src) return var/new_metadata = strip_html_simple(tgui_input_text(usr, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the OOC notes panel!", "Game Preference" , html_decode(ooc_notes_dislikes), multiline = TRUE, prevent_enter = TRUE)) - if(new_metadata && CanUseTopic(usr)) + if(CanUseTopic(usr)) ooc_notes_dislikes = new_metadata client.prefs.metadata_dislikes = new_metadata to_chat(usr, "OOC note dislikes have been updated. Don't forget to save!") @@ -91,7 +91,7 @@ msg += "

                    LIKES

                    [ooc_notes_likes]" if(ooc_notes_dislikes) msg += "

                    DISLIKES

                    [ooc_notes_dislikes]" - to_chat(usr, "[src]'s Metainfo:
                    [msg]
                    ") + to_chat(usr, "[src]'s Metainfo:
                    [msg]
                    ") /mob/living/verb/set_custom_link() set name = "Set Custom Link" @@ -124,7 +124,7 @@ voice_freq = choice return else if(choice == 1) - choice = tgui_input_number(src, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ, round_value = TRUE) + choice = tgui_input_number(src, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ) else if(choice > MAX_VOICE_FREQ) choice = MAX_VOICE_FREQ else if(choice < MIN_VOICE_FREQ) diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index d6aec7d322b..927fc4a5119 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -19,6 +19,7 @@ verbs |= /mob/living/proc/lick verbs |= /mob/living/proc/smell verbs |= /mob/living/proc/switch_scaling + verbs |= /mob/living/proc/center_offset if(!no_vore) verbs |= /mob/living/proc/vorebelly_printout diff --git a/code/modules/mob/living/logout.dm b/code/modules/mob/living/logout.dm index 8f75e98aaa9..babed7cd25e 100644 --- a/code/modules/mob/living/logout.dm +++ b/code/modules/mob/living/logout.dm @@ -1,19 +1,19 @@ -/mob/living/Logout() - ..() - if (mind) - //Per BYOND docs key remains set if the player DCs, becomes null if switching bodies. - if(!key) //key and mind have become seperated. - mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. - - var/datum/component/character_setup/cs = GetComponent(/datum/component/character_setup) - if(cs) - qdel(cs) - - var/datum/component/vore_panel/vp = GetComponent(/datum/component/vore_panel) - if(vp) - qdel(vp) - - spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild - if(src && !src.client) - if(ai_holder) - ai_holder.go_wake() +/mob/living/Logout() + ..() + if (mind) + //Per BYOND docs key remains set if the player DCs, becomes null if switching bodies. + if(!key) //key and mind have become seperated. + mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. + + var/datum/component/character_setup/cs = GetComponent(/datum/component/character_setup) + if(cs) + qdel(cs) + + var/datum/component/vore_panel/vp = GetComponent(/datum/component/vore_panel) + if(vp) + qdel(vp) + + spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild + if(src && !src.client) + if(ai_holder) + ai_holder.go_wake() diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 17a0a874e25..76347f66f94 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -1,461 +1,462 @@ -var/list/department_radio_keys = list( - ":r" = "right ear", ".r" = "right ear", - ":l" = "left ear", ".l" = "left ear", - ":i" = "intercom", ".i" = "intercom", - ":h" = "department", ".h" = "department", - ":+" = "special", ".+" = "special", //activate radio-specific special functions - ":c" = "Command", ".c" = "Command", - ":n" = "Science", ".n" = "Science", - ":m" = "Medical", ".m" = "Medical", - ":e" = "Engineering", ".e" = "Engineering", - ":k" = "Response Team", ".k" = "Response Team", - ":s" = "Security", ".s" = "Security", - ":w" = "whisper", ".w" = "whisper", - ":t" = "Mercenary", ".t" = "Mercenary", - ":x" = "Raider", ".x" = "Raider", - ":u" = "Supply", ".u" = "Supply", - ":v" = "Service", ".v" = "Service", - ":p" = "AI Private", ".p" = "AI Private", - ":y" = "Explorer", ".y" = "Explorer", - ":a" = "Talon", ".a" = "Talon", //VOREStation Add, - ":g" = "Casino", ".g" = "Casino", - - ":R" = "right ear", ".R" = "right ear", - ":L" = "left ear", ".L" = "left ear", - ":I" = "intercom", ".I" = "intercom", - ":H" = "department", ".H" = "department", - ":C" = "Command", ".C" = "Command", - ":N" = "Science", ".N" = "Science", - ":M" = "Medical", ".M" = "Medical", - ":E" = "Engineering", ".E" = "Engineering", - ":k" = "Response Team", ".k" = "Response Team", - ":S" = "Security", ".S" = "Security", - ":W" = "whisper", ".W" = "whisper", - ":T" = "Mercenary", ".T" = "Mercenary", - ":X" = "Raider", ".X" = "Raider", - ":U" = "Supply", ".U" = "Supply", - ":V" = "Service", ".V" = "Service", - ":P" = "AI Private", ".P" = "AI Private", - ":Y" = "Explorer", ".Y" = "Explorer", - ":A" = "Talon", ".A" = "Talon", //VOREStation Add, - ":G" = "Casino", ".G" = "Casino", - - // Cyrillic characters on the same keys on the Russian QWERTY (phonetic) layout - ":к" = "right ear", ".к" = "right ear", - ":д" = "left ear", ".д" = "left ear", - ":ш" = "intercom", ".ш" = "intercom", - ":Ñ€" = "department", ".Ñ€" = "department", - ":+" = "special", ".+" = "special", //activate radio-specific special functions - ":Ñ" = "Command", ".Ñ" = "Command", - ":Ñ‚" = "Science", ".Ñ‚" = "Science", - ":ÑŒ" = "Medical", ".ÑŒ" = "Medical", - ":у" = "Engineering", ".у" = "Engineering", - ":л" = "Response Team", ".л" = "Response Team", - ":Ñ‹" = "Security", ".Ñ‹" = "Security", - ":ц" = "whisper", ".ц" = "whisper", - ":е" = "Mercenary", ".е" = "Mercenary", - ":ч" = "Raider", ".ч" = "Raider", - ":г" = "Supply", ".г" = "Supply", - ":м" = "Service", ".м" = "Service", - ":з" = "AI Private", ".з" = "AI Private", - ":н" = "Explorer", ".н" = "Explorer", - ":Ñ„" = "Talon", ".Ñ„" = "Talon", //VOREStation Add - ":п" = "Casino", ".п" = "Casino", -) - - -var/list/channel_to_radio_key = new -/proc/get_radio_key_from_channel(var/channel) - var/key = channel_to_radio_key[channel] - if(!key) - for(var/radio_key in department_radio_keys) - if(department_radio_keys[radio_key] == channel) - key = radio_key - break - if(!key) - key = "" - channel_to_radio_key[channel] = key - - return key - -/mob/living/proc/binarycheck() - return FALSE - -/mob/proc/get_default_language() - return null - -/mob/living/get_default_language() - return default_language - -//Takes a list of the form list(message, verb, whispering) and modifies it as needed -//Returns 1 if a speech problem was applied, 0 otherwise -/mob/living/proc/handle_speech_problems(var/list/message_data) - var/list/message_pieces = message_data[1] - var/verb = message_data[2] - var/whispering = message_data[3] - . = 0 - - // Technically this rerolls the verb for as many say pieces as there are. _shrug_ - for(var/datum/multilingual_say_piece/S in message_pieces) - if(S.speaking && (S.speaking.flags & NO_STUTTER || S.speaking.flags & SIGNLANG)) - continue - - if((HULK in mutations) && health >= 25 && length(S.message)) - S.message = "[uppertext(S.message)]!!!" - verb = pick("yells","roars","hollers") - whispering = 0 - . = 1 - if(slurring) - S.message = slur(S.message) - verb = pick("slobbers","slurs") - . = 1 - if(stuttering) - S.message = stutter(S.message) - verb = pick("stammers","stutters") - . = 1 - //VOREStation Edit Start - if(muffled) - verb = pick("muffles") - whispering = 1 - . = 1 - //VOREStation Edit End - - message_data[1] = message_pieces - message_data[2] = verb - message_data[3] = whispering - -/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(message_mode == "intercom") - for(var/obj/item/device/radio/intercom/I in view(1, null)) - I.talk_into(src, message_pieces, verb) - used_radios += I - return 0 - -/mob/living/proc/handle_speech_sound() - var/list/returns[2] - returns[1] = null - returns[2] = null - return returns - -/mob/living/proc/get_speech_ending(verb, var/ending) - if(ending == "!") - return pick("exclaims","shouts","yells") - if(ending == "?") - return "asks" - return verb - -/mob/living/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - //If you're muted for IC chat - if(client) - if(message) - client.handle_spam_prevention(MUTE_IC) - if((client.prefs.muted & MUTE_IC) || say_disabled) - to_chat(src, "You cannot speak in IC (Muted).") - return - - //Redirect to say_dead if talker is dead - if(stat) - if(stat == DEAD && !forbid_seeing_deadchat) - return say_dead(message) - return - //VOREStation Addition Start - if(forced_psay) - psay(message) - return - if(autowhisper) - whispering = 1 - //VOREStation Addition End - //Parse the mode - var/message_mode = parse_message_mode(message, "headset") - - //Maybe they are using say/whisper to do a quick emote, so do those - switch(copytext(message, 1, 2)) - if("*") return emote(copytext(message, 2)) - if("^") return custom_emote(1, copytext(message, 2)) - - //Parse the radio code and consume it - if(message_mode) - if(message_mode == "headset") - message = copytext(message, 2) //it would be really nice if the parse procs could do this for us. - else if(message_mode == "whisper") - whispering = 1 - message_mode = null - message = copytext(message, 3) - else - message = copytext(message, 3) - - //Clean up any remaining space on the left - message = trim_left(message) - - // VOREStation Edit - Reflect messages as needed, no sanitizing because parse_languages will handle it for us - if(reflect_if_needed(message, src)) - return - // VOREStation Edit End - - //Parse the language code and consume it - var/list/message_pieces = parse_languages(message) - if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quark for dealing with hivemind/signlang languages. - var/datum/multilingual_say_piece/S = message_pieces // Yay for BYOND's hilariously broken typecasting for allowing us to do this. - S.speaking.broadcast(src, S.message) - return 1 - - if(!LAZYLEN(message_pieces)) - log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]")) - return 0 - - // If you're muzzled, you can only speak sign language - // However, sign language is handled above. - if(is_muzzled()) - to_chat(src, "You're muzzled and cannot speak!") - return - - //Whisper vars - var/w_scramble_range = 5 //The range at which you get ***as*th**wi**** - var/w_adverb //An adverb prepended to the verb in whispers - var/w_not_heard //The message for people in watching range - - var/datum/multilingual_say_piece/first_piece = message_pieces[1] - var/verb = "" - //Handle language-specific verbs and adverb setup if necessary - if(!whispering) //Just doing normal 'say' (for now, may change below) - verb = say_quote(message, first_piece.speaking) - else if(whispering && first_piece.speaking.whisper_verb) //Language has defined whisper verb - verb = first_piece.speaking.whisper_verb - w_not_heard = "[verb] something" - else //Whispering but language has no whisper verb, use say verb - w_adverb = pick("quietly", "softly") - verb = first_piece.speaking.speech_verb - w_not_heard = "[first_piece.speaking.speech_verb] something [w_adverb]" - - //For speech disorders (hulk, slurring, stuttering) - var/list/message_data = list(message_pieces, verb, whispering) - if(handle_speech_problems(message_data)) - message_pieces = message_data[1] - whispering = message_data[3] - - if(verb != message_data[2]) //They changed our verb - if(whispering) - w_adverb = pick("quietly", "softly") - verb = message_data[2] - - //Whisper may have adverbs, add those if one was set - if(w_adverb) - verb = "[verb] [w_adverb]" - - //If something nulled or emptied the message, forget it - if(!LAZYLEN(message_pieces)) - return 0 - - //Radio message handling - var/list/used_radios = list() - if(handle_message_mode(message_mode, message_pieces, verb, used_radios, whispering)) - return 1 - - //For languages with actual speech sounds - var/list/handle_v = handle_speech_sound() - var/sound/speech_sound = handle_v[1] - var/sound_vol = handle_v[2] - - //Default range and italics, may be overridden past here - var/message_range = world.view - var/italics = 0 - var/do_sound = TRUE - if(!voice_sounds_list || !voice_sounds_list.len) - do_sound = FALSE - - //Speaking into radios - if(used_radios.len) - italics = 1 - message_range = 1 - - if(first_piece.speaking) - message_range = first_piece.speaking.get_talkinto_msg_range(message) - var/msg - if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG)) - msg = "[src] talks into [used_radios[1]]" - - if(msg) - for(var/mob/living/M in hearers(5, src) - src) - M.show_message(msg) - - if(speech_sound) - sound_vol *= 0.5 - - //Set vars if we're still whispering by this point - if(whispering) - italics = 1 - message_range = 1 - sound_vol *= 0.5 - - //VOREStation edit - allows for custom say verbs, overriding all other say-verb types- e.g. "says loudly" instead of "shouts" - //You'll still stammer if injured or slur if drunk, but it won't have those specific words - var/ending = copytext(message, length(message)) - - if(custom_whisper && whispering) - verb = "[custom_whisper]" - else if(custom_exclaim && ending=="!") - verb = "[custom_exclaim]" - else if(custom_ask && ending=="?") - verb = "[custom_ask]" - else if(custom_say) - verb = "[custom_say]" - //VOREStation edit ends - - //Handle nonverbal languages here - for(var/datum/multilingual_say_piece/S in message_pieces) - if((S.speaking.flags & NONVERBAL) || (S.speaking.flags & INAUDIBLE)) - custom_emote(1, "[pick(S.speaking.signlang_verb)].") - do_sound = FALSE - - //These will contain the main receivers of the message - var/list/listening = list() - var/list/listening_obj = list() - - //Atmosphere calculations (speaker's side only, for now) - var/turf/T = get_turf(src) - if(T) - //Air is too thin to carry sound at all, contact speech only - var/datum/gas_mixture/environment = T.return_air() - var/pressure = environment ? environment.return_pressure() : 0 - if(pressure < SOUND_MINIMUM_PRESSURE) - message_range = 1 - - //Air is nearing minimum levels, make text italics as a hint, and muffle sound - if(pressure < ONE_ATMOSPHERE * 0.4) - italics = 1 - sound_vol *= 0.5 - - //Obtain the mobs and objects in the message range - var/list/results = get_mobs_and_objs_in_view_fast(T, world.view, remote_ghosts = client ? TRUE : FALSE) - listening = results["mobs"] - listening_obj = results["objs"] - else - return 1 //If we're in nullspace, then forget it. - - //Remember the speech images so we can remove them later and they can get GC'd - var/list/images_to_clients = list() - - //The 'post-say' static speech bubble - var/speech_bubble_test = say_test(message) - //var/image/speech_bubble = image('icons/mob/talk_vr.dmi',src,"h[speech_bubble_test]") //VOREStation Edit. Commented this out in case we need to reenable. - var/speech_type = custom_speech_bubble - if(!speech_type || speech_type == "default") - speech_type = speech_bubble_appearance() - var/image/speech_bubble = generate_speech_bubble(src, "[speech_type][speech_bubble_test]") - var/sb_alpha = 255 - var/atom/loc_before_turf = src - //VOREStation Add - if(isbelly(loc)) - speech_bubble.pixel_y = -13 //teehee - //VOREStation Add End - while(loc_before_turf && !isturf(loc_before_turf.loc)) - loc_before_turf = loc_before_turf.loc - sb_alpha -= 50 - if(sb_alpha < 0) - break - speech_bubble.loc = loc_before_turf - speech_bubble.alpha = CLAMP(sb_alpha, 0, 255) - images_to_clients[speech_bubble] = list() - - //Main 'say' and 'whisper' message delivery - for(var/mob/M in listening) - spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes - - if(M && src) //If we still exist, when the spawn processes - //VOREStation Add - Ghosts don't hear whispers - if(whispering && !is_preference_enabled(/datum/client_preference/whisubtle_vis) && isobserver(M) && !M.client?.holder) - M.show_message("[src.name] [w_not_heard].", 2) - return - //VOREStation Add End - - var/dst = get_dist(get_turf(M),get_turf(src)) - var/runechat_enabled = M.client?.is_preference_enabled(/datum/client_preference/runechat_mob) - - if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc) - if(M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol)) - if(M.client && !runechat_enabled) - var/image/I1 = listening[M] || speech_bubble - images_to_clients[I1] |= M.client - M << I1 - if(whispering && !isobserver(M)) //Don't even bother with these unless whispering - if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range - if(M.hear_say(stars_all(message_pieces), verb, italics, src, speech_sound, sound_vol*0.2)) - if(M.client && !runechat_enabled) - var/image/I2 = listening[M] || speech_bubble - images_to_clients[I2] |= M.client - M << I2 - if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range - M.show_message("[name] [w_not_heard].", 2) - - //Object message delivery - for(var/obj/O in listening_obj) - spawn(0) - if(O && src) //If we still exist, when the spawn processes - var/dst = get_dist(get_turf(O),get_turf(src)) - if(dst <= message_range) - O.hear_talk(src, message_pieces, verb) - - //Remove all those images. At least it's just ONE spawn this time. - spawn(30) - for(var/image/I as anything in images_to_clients) - var/list/clients_from_image = images_to_clients[I] - for(var/client/C as anything in clients_from_image) - if(C) //Could have disconnected after message sent, before removing bubble. - C.images -= I - qdel(I) - - var/ourfreq = null - if(voice_freq > 0 ) - ourfreq = voice_freq - //Log the message to file - if(message_mode) - message = "([message_mode == "headset" ? "Common" : capitalize(message_mode)]) [message]" //Adds radio keys used if available - if(whispering) - if(do_sound && message) - playsound(T, pick(voice_sounds_list), 25, TRUE, extrarange = -6, falloff = 1 , is_global = TRUE, frequency = ourfreq, ignore_walls = FALSE, preference = /datum/client_preference/whisper_sounds) - - log_whisper(message, src) - else - if(do_sound && message) - playsound(T, pick(voice_sounds_list), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = ourfreq, ignore_walls = FALSE, preference = /datum/client_preference/say_sounds) - log_say(message, src) - return 1 - -/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/verb_understood="gestures", var/datum/language/language, var/type = 1) - var/turf/T = get_turf(src) - //We're in something, gesture to people inside the same thing - if(loc != T) - for(var/mob/M in loc) - M.hear_signlang(message, verb, verb_understood, language, src, type) - - //We're on a turf, gesture to visible as if we were a normal language - else - var/low_range = FALSE - if(T && type == 2) // type 2 is audible signlang. yes. sue me. - //Air is too thin to carry sound at all, contact speech only - var/datum/gas_mixture/environment = T.return_air() - var/pressure = environment ? environment.return_pressure() : 0 - if(pressure < SOUND_MINIMUM_PRESSURE) - low_range = TRUE - - var/list/potentials = get_mobs_and_objs_in_view_fast(T, world.view) - var/list/mobs = potentials["mobs"] - for(var/mob/M as anything in mobs) - if(low_range && !(M in range(1, src))) - continue - M.hear_signlang(message, verb, verb_understood, language, src, type) - var/list/objs = potentials["objs"] - for(var/obj/O as anything in objs) - if(low_range && !(O in range(1, src))) - continue - O.hear_signlang(message, verb, language, src) - return 1 - -/obj/effect/speech_bubble - var/mob/parent - -/mob/proc/GetVoice() - return name - -/mob/proc/speech_bubble_appearance() - return "normal" +var/list/department_radio_keys = list( + ":r" = "right ear", ".r" = "right ear", + ":l" = "left ear", ".l" = "left ear", + ":i" = "intercom", ".i" = "intercom", + ":h" = "department", ".h" = "department", + ":+" = "special", ".+" = "special", //activate radio-specific special functions + ":c" = "Command", ".c" = "Command", + ":n" = "Science", ".n" = "Science", + ":m" = "Medical", ".m" = "Medical", + ":e" = "Engineering", ".e" = "Engineering", + ":k" = "Response Team", ".k" = "Response Team", + ":s" = "Security", ".s" = "Security", + ":w" = "whisper", ".w" = "whisper", + ":t" = "Mercenary", ".t" = "Mercenary", + ":x" = "Raider", ".x" = "Raider", + ":u" = "Supply", ".u" = "Supply", + ":v" = "Service", ".v" = "Service", + ":p" = "AI Private", ".p" = "AI Private", + ":y" = "Explorer", ".y" = "Explorer", + ":a" = "Talon", ".a" = "Talon", //VOREStation Add, + ":g" = "Casino", ".g" = "Casino", + + ":R" = "right ear", ".R" = "right ear", + ":L" = "left ear", ".L" = "left ear", + ":I" = "intercom", ".I" = "intercom", + ":H" = "department", ".H" = "department", + ":C" = "Command", ".C" = "Command", + ":N" = "Science", ".N" = "Science", + ":M" = "Medical", ".M" = "Medical", + ":E" = "Engineering", ".E" = "Engineering", + ":k" = "Response Team", ".k" = "Response Team", + ":S" = "Security", ".S" = "Security", + ":W" = "whisper", ".W" = "whisper", + ":T" = "Mercenary", ".T" = "Mercenary", + ":X" = "Raider", ".X" = "Raider", + ":U" = "Supply", ".U" = "Supply", + ":V" = "Service", ".V" = "Service", + ":P" = "AI Private", ".P" = "AI Private", + ":Y" = "Explorer", ".Y" = "Explorer", + ":A" = "Talon", ".A" = "Talon", //VOREStation Add, + ":G" = "Casino", ".G" = "Casino", + + // Cyrillic characters on the same keys on the Russian QWERTY (phonetic) layout + ":к" = "right ear", ".к" = "right ear", + ":д" = "left ear", ".д" = "left ear", + ":ш" = "intercom", ".ш" = "intercom", + ":Ñ€" = "department", ".Ñ€" = "department", + ":+" = "special", ".+" = "special", //activate radio-specific special functions + ":Ñ" = "Command", ".Ñ" = "Command", + ":Ñ‚" = "Science", ".Ñ‚" = "Science", + ":ÑŒ" = "Medical", ".ÑŒ" = "Medical", + ":у" = "Engineering", ".у" = "Engineering", + ":л" = "Response Team", ".л" = "Response Team", + ":Ñ‹" = "Security", ".Ñ‹" = "Security", + ":ц" = "whisper", ".ц" = "whisper", + ":е" = "Mercenary", ".е" = "Mercenary", + ":ч" = "Raider", ".ч" = "Raider", + ":г" = "Supply", ".г" = "Supply", + ":м" = "Service", ".м" = "Service", + ":з" = "AI Private", ".з" = "AI Private", + ":н" = "Explorer", ".н" = "Explorer", + ":Ñ„" = "Talon", ".Ñ„" = "Talon", //VOREStation Add + ":п" = "Casino", ".п" = "Casino", +) + + +var/list/channel_to_radio_key = new +/proc/get_radio_key_from_channel(var/channel) + var/key = channel_to_radio_key[channel] + if(!key) + for(var/radio_key in department_radio_keys) + if(department_radio_keys[radio_key] == channel) + key = radio_key + break + if(!key) + key = "" + channel_to_radio_key[channel] = key + + return key + +/mob/living/proc/binarycheck() + return FALSE + +/mob/proc/get_default_language() + return null + +/mob/living/get_default_language() + return default_language + +//Takes a list of the form list(message, verb, whispering) and modifies it as needed +//Returns 1 if a speech problem was applied, 0 otherwise +/mob/living/proc/handle_speech_problems(var/list/message_data) + var/list/message_pieces = message_data[1] + var/verb = message_data[2] + var/whispering = message_data[3] + . = 0 + + // Technically this rerolls the verb for as many say pieces as there are. _shrug_ + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && (S.speaking.flags & NO_STUTTER || S.speaking.flags & SIGNLANG)) + continue + + if((HULK in mutations) && health >= 25 && length(S.message)) + S.message = "[uppertext(S.message)]!!!" + verb = pick("yells","roars","hollers") + whispering = 0 + . = 1 + if(slurring) + S.message = slur(S.message) + verb = pick("slobbers","slurs") + . = 1 + if(stuttering) + S.message = stutter(S.message) + verb = pick("stammers","stutters") + . = 1 + //VOREStation Edit Start + if(muffled) + verb = pick("muffles") + whispering = 1 + . = 1 + //VOREStation Edit End + + message_data[1] = message_pieces + message_data[2] = verb + message_data[3] = whispering + +/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(message_mode == "intercom") + for(var/obj/item/device/radio/intercom/I in view(1, null)) + I.talk_into(src, message_pieces, verb) + used_radios += I + return 0 + +/mob/living/proc/handle_speech_sound() + var/list/returns[2] + returns[1] = null + returns[2] = null + return returns + +/mob/living/proc/get_speech_ending(verb, var/ending) + if(ending == "!") + return pick("exclaims","shouts","yells") + if(ending == "?") + return "asks" + return verb + +/mob/living/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + //If you're muted for IC chat + if(client) + if(message) + client.handle_spam_prevention(MUTE_IC) + if((client.prefs.muted & MUTE_IC) || say_disabled) + to_chat(src, "You cannot speak in IC (Muted).") + return + + //Redirect to say_dead if talker is dead + if(stat) + if(stat == DEAD && !forbid_seeing_deadchat) + return say_dead(message) + return + //VOREStation Addition Start + if(forced_psay) + psay(message) + return + if(autowhisper) + whispering = 1 + //VOREStation Addition End + //Parse the mode + var/message_mode = parse_message_mode(message, "headset") + + //Maybe they are using say/whisper to do a quick emote, so do those + switch(copytext(message, 1, 2)) + if("*") return emote(copytext(message, 2)) + if("^") return custom_emote(1, copytext(message, 2)) + + //Parse the radio code and consume it + if(message_mode) + if(message_mode == "headset") + message = copytext(message, 2) //it would be really nice if the parse procs could do this for us. + else if(message_mode == "whisper") + whispering = 1 + message_mode = null + message = copytext(message, 3) + else + message = copytext(message, 3) + + //Clean up any remaining space on the left + message = trim_left(message) + + // VOREStation Edit - Reflect messages as needed, no sanitizing because parse_languages will handle it for us + if(reflect_if_needed(message, src)) + return + // VOREStation Edit End + + //Parse the language code and consume it + var/list/message_pieces = parse_languages(message) + if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quark for dealing with hivemind/signlang languages. + var/datum/multilingual_say_piece/S = message_pieces // Yay for BYOND's hilariously broken typecasting for allowing us to do this. + S.speaking.broadcast(src, S.message) + return 1 + + if(!LAZYLEN(message_pieces)) + log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]")) + return 0 + + // If you're muzzled, you can only speak sign language + // However, sign language is handled above. + if(is_muzzled()) + to_chat(src, "You're muzzled and cannot speak!") + return + + //Whisper vars + var/w_scramble_range = 5 //The range at which you get ***as*th**wi**** + var/w_adverb //An adverb prepended to the verb in whispers + var/w_not_heard //The message for people in watching range + + var/datum/multilingual_say_piece/first_piece = message_pieces[1] + var/verb = "" + //Handle language-specific verbs and adverb setup if necessary + if(!whispering) //Just doing normal 'say' (for now, may change below) + verb = say_quote(message, first_piece.speaking) + else if(whispering && first_piece.speaking.whisper_verb) //Language has defined whisper verb + verb = first_piece.speaking.whisper_verb + w_not_heard = "[verb] something" + else //Whispering but language has no whisper verb, use say verb + w_adverb = pick("quietly", "softly") + verb = first_piece.speaking.speech_verb + w_not_heard = "[first_piece.speaking.speech_verb] something [w_adverb]" + + //For speech disorders (hulk, slurring, stuttering) + var/list/message_data = list(message_pieces, verb, whispering) + if(handle_speech_problems(message_data)) + message_pieces = message_data[1] + whispering = message_data[3] + + if(verb != message_data[2]) //They changed our verb + if(whispering) + w_adverb = pick("quietly", "softly") + verb = message_data[2] + + //Whisper may have adverbs, add those if one was set + if(w_adverb) + verb = "[verb] [w_adverb]" + + //If something nulled or emptied the message, forget it + if(!LAZYLEN(message_pieces)) + return 0 + + //Radio message handling + var/list/used_radios = list() + if(handle_message_mode(message_mode, message_pieces, verb, used_radios, whispering)) + return 1 + + //For languages with actual speech sounds + var/list/handle_v = handle_speech_sound() + var/sound/speech_sound = handle_v[1] + var/sound_vol = handle_v[2] + + //Default range and italics, may be overridden past here + var/message_range = world.view + var/italics = 0 + var/do_sound = TRUE + if(!voice_sounds_list || !voice_sounds_list.len) + do_sound = FALSE + + //Speaking into radios + if(used_radios.len) + italics = 1 + message_range = 1 + + if(first_piece.speaking) + message_range = first_piece.speaking.get_talkinto_msg_range(message) + var/msg + if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG)) + msg = "[src] talks into [used_radios[1]]" + + if(msg) + for(var/mob/living/M in hearers(5, src) - src) + M.show_message(msg) + + if(speech_sound) + sound_vol *= 0.5 + + //Set vars if we're still whispering by this point + if(whispering) + italics = 1 + message_range = 1 + sound_vol *= 0.5 + + //VOREStation edit - allows for custom say verbs, overriding all other say-verb types- e.g. "says loudly" instead of "shouts" + //You'll still stammer if injured or slur if drunk, but it won't have those specific words + var/ending = copytext(message, length(message)) + + if(custom_whisper && whispering) + verb = "[custom_whisper]" + else if(custom_exclaim && ending=="!") + verb = "[custom_exclaim]" + else if(custom_ask && ending=="?") + verb = "[custom_ask]" + else if(custom_say) + verb = "[custom_say]" + //VOREStation edit ends + + //Handle nonverbal languages here + for(var/datum/multilingual_say_piece/S in message_pieces) + if((S.speaking.flags & NONVERBAL) || (S.speaking.flags & INAUDIBLE)) + custom_emote(1, "[pick(S.speaking.signlang_verb)].") + do_sound = FALSE + + //These will contain the main receivers of the message + var/list/listening = list() + var/list/listening_obj = list() + + //Atmosphere calculations (speaker's side only, for now) + var/turf/T = get_turf(src) + if(T) + //Air is too thin to carry sound at all, contact speech only + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment ? environment.return_pressure() : 0 + if(pressure < SOUND_MINIMUM_PRESSURE) + message_range = 1 + + //Air is nearing minimum levels, make text italics as a hint, and muffle sound + if(pressure < ONE_ATMOSPHERE * 0.4) + italics = 1 + sound_vol *= 0.5 + + //Obtain the mobs and objects in the message range + var/list/results = get_mobs_and_objs_in_view_fast(T, world.view, remote_ghosts = client ? TRUE : FALSE) + listening = results["mobs"] + listening_obj = results["objs"] + else + return 1 //If we're in nullspace, then forget it. + + //Remember the speech images so we can remove them later and they can get GC'd + var/list/images_to_clients = list() + + //The 'post-say' static speech bubble + var/speech_bubble_test = say_test(message) + //var/image/speech_bubble = image('icons/mob/talk_vr.dmi',src,"h[speech_bubble_test]") //VOREStation Edit. Commented this out in case we need to reenable. + var/speech_type = custom_speech_bubble + if(!speech_type || speech_type == "default") + speech_type = speech_bubble_appearance() + var/image/speech_bubble = generate_speech_bubble(src, "[speech_type][speech_bubble_test]") + var/sb_alpha = 255 + var/atom/loc_before_turf = src + //VOREStation Add + if(isbelly(loc)) + speech_bubble.pixel_y = -13 //teehee + //VOREStation Add End + while(loc_before_turf && !isturf(loc_before_turf.loc)) + loc_before_turf = loc_before_turf.loc + sb_alpha -= 50 + if(sb_alpha < 0) + break + speech_bubble.loc = loc_before_turf + speech_bubble.alpha = CLAMP(sb_alpha, 0, 255) + images_to_clients[speech_bubble] = list() + + //Main 'say' and 'whisper' message delivery + for(var/mob/M in listening) + spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes + + if(M && src) //If we still exist, when the spawn processes + //VOREStation Add - Ghosts don't hear whispers + if(whispering && isobserver(M) && (!M.is_preference_enabled(/datum/client_preference/ghost_see_whisubtle) || \ + (!is_preference_enabled(/datum/client_preference/whisubtle_vis) && !M.client?.holder))) + M.show_message("[src.name] [w_not_heard].", 2) + return + //VOREStation Add End + + var/dst = get_dist(get_turf(M),get_turf(src)) + var/runechat_enabled = M.client?.is_preference_enabled(/datum/client_preference/runechat_mob) + + if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc) + if(M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol)) + if(M.client && !runechat_enabled) + var/image/I1 = listening[M] || speech_bubble + images_to_clients[I1] |= M.client + M << I1 + if(whispering && !isobserver(M)) //Don't even bother with these unless whispering + if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range + if(M.hear_say(stars_all(message_pieces), verb, italics, src, speech_sound, sound_vol*0.2)) + if(M.client && !runechat_enabled) + var/image/I2 = listening[M] || speech_bubble + images_to_clients[I2] |= M.client + M << I2 + if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range + M.show_message("[name] [w_not_heard].", 2) + + //Object message delivery + for(var/obj/O in listening_obj) + spawn(0) + if(O && src) //If we still exist, when the spawn processes + var/dst = get_dist(get_turf(O),get_turf(src)) + if(dst <= message_range) + O.hear_talk(src, message_pieces, verb) + + //Remove all those images. At least it's just ONE spawn this time. + spawn(30) + for(var/image/I as anything in images_to_clients) + var/list/clients_from_image = images_to_clients[I] + for(var/client/C as anything in clients_from_image) + if(C) //Could have disconnected after message sent, before removing bubble. + C.images -= I + qdel(I) + + var/ourfreq = null + if(voice_freq > 0 ) + ourfreq = voice_freq + //Log the message to file + if(message_mode) + message = "([message_mode == "headset" ? "Common" : capitalize(message_mode)]) [message]" //Adds radio keys used if available + if(whispering) + if(do_sound && message) + playsound(T, pick(voice_sounds_list), 25, TRUE, extrarange = -6, falloff = 1 , is_global = TRUE, frequency = ourfreq, ignore_walls = FALSE, preference = /datum/client_preference/whisper_sounds) + + log_whisper(message, src) + else + if(do_sound && message) + playsound(T, pick(voice_sounds_list), 75, TRUE, falloff = 1 , is_global = TRUE, frequency = ourfreq, ignore_walls = FALSE, preference = /datum/client_preference/say_sounds) + log_say(message, src) + return 1 + +/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/verb_understood="gestures", var/datum/language/language, var/type = 1) + var/turf/T = get_turf(src) + //We're in something, gesture to people inside the same thing + if(loc != T) + for(var/mob/M in loc) + M.hear_signlang(message, verb, verb_understood, language, src, type) + + //We're on a turf, gesture to visible as if we were a normal language + else + var/low_range = FALSE + if(T && type == 2) // type 2 is audible signlang. yes. sue me. + //Air is too thin to carry sound at all, contact speech only + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment ? environment.return_pressure() : 0 + if(pressure < SOUND_MINIMUM_PRESSURE) + low_range = TRUE + + var/list/potentials = get_mobs_and_objs_in_view_fast(T, world.view) + var/list/mobs = potentials["mobs"] + for(var/mob/M as anything in mobs) + if(low_range && !(M in range(1, src))) + continue + M.hear_signlang(message, verb, verb_understood, language, src, type) + var/list/objs = potentials["objs"] + for(var/obj/O as anything in objs) + if(low_range && !(O in range(1, src))) + continue + O.hear_signlang(message, verb, language, src) + return 1 + +/obj/effect/speech_bubble + var/mob/parent + +/mob/proc/GetVoice() + return name + +/mob/proc/speech_bubble_appearance() + return "normal" diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index ee77cbfe003..33f79d28c09 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -475,7 +475,7 @@ var/list/ai_verbs_default = list( if(target && (!istype(target, /mob/living/carbon/human) || html_decode(href_list["trackname"]) == target:get_face_name())) ai_actual_track(target) else - to_chat(src, "System error. Cannot locate [html_decode(href_list["trackname"])].") + to_chat(src, "[span_red("System error. Cannot locate [html_decode(href_list["trackname"])].")]") return if(href_list["trackbot"]) @@ -763,7 +763,7 @@ var/list/ai_verbs_default = list( var/obj/item/device/aicard/card = W card.grab_ai(src, user) - else if(W.is_wrench()) + else if(W.has_tool_quality(TOOL_WRENCH)) if(user == deployed_shell) to_chat(user, "The shell's subsystems resist your efforts to tamper with your bolts.") return diff --git a/code/modules/mob/living/silicon/ai/ai_movement.dm b/code/modules/mob/living/silicon/ai/ai_movement.dm index 456d11a9817..0c4e4bed0c5 100644 --- a/code/modules/mob/living/silicon/ai/ai_movement.dm +++ b/code/modules/mob/living/silicon/ai/ai_movement.dm @@ -1,2 +1,2 @@ -/mob/living/silicon/ai/SelfMove(turf/n, direct) - return 0 +/mob/living/silicon/ai/SelfMove(turf/n, direct) + return 0 diff --git a/code/modules/mob/living/silicon/ai/ai_remote_control.dm b/code/modules/mob/living/silicon/ai/ai_remote_control.dm index 6fdbbbe4a93..80d02bb08ea 100644 --- a/code/modules/mob/living/silicon/ai/ai_remote_control.dm +++ b/code/modules/mob/living/silicon/ai/ai_remote_control.dm @@ -1,71 +1,71 @@ -/mob/living/silicon/ai - var/mob/living/silicon/robot/deployed_shell = null //For shell control - -/mob/living/silicon/ai/Initialize() - if(config.allow_ai_shells) - verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act - return ..() - -/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target) - if(!config.allow_ai_shells) - to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report.")) - return - - if(incapacitated()) - to_chat(src, span("warning", "You are incapacitated!")) - return - - if(lacks_power()) - to_chat(src, span("warning", "Your core lacks power, wireless is disabled.")) - return - - if(control_disabled) - to_chat(src, span("warning", "Wireless networking module is offline.")) - return - - var/list/possible = list() - for(var/mob/living/silicon/robot/R as anything in GLOB.available_ai_shells) - if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) ) //VOREStation Edit: shell restrictions - if(istype(R.loc, /obj/machinery/recharge_station)) //Check Rechargers - var/obj/machinery/recharge_station/RS = R.loc - if(!(using_map.ai_shell_restricted && !(RS.z in using_map.ai_shell_allowed_levels))) //Allow station borgs to be redeployed from Chargers. - possible += R - - if(isbelly(R.loc)) //check belly space - var/obj/belly/B = R.loc - if(!(using_map.ai_shell_restricted && !(B.owner.z in using_map.ai_shell_allowed_levels))) //No smuggling in borgs - possible += R - - if(!(using_map.ai_shell_restricted && !(R.z in using_map.ai_shell_allowed_levels))) - possible += R - - if(!LAZYLEN(possible)) - to_chat(src, span("warning", "No usable AI shell beacons detected.")) - - if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid - target = tgui_input_list(src, "Which body to control?", "Shell Choice", possible) - - if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) ) - if(target) - to_chat(src, span("warning", "It is no longer possible to deploy to \the [target].")) - else - to_chat(src, span("notice", "Deployment aborted.")) - return - - else if(mind) - soul_link(/datum/soul_link/shared_body, src, target) - deployed_shell = target - target.deploy_init(src) - mind.transfer_to(target) - teleop = target // So the AI 'hears' messages near its core. - target.post_deploy() - -/mob/living/silicon/ai/proc/deploy_to_shell_act() - set category = "AI Commands" - set name = "Deploy to Shell" - deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc. - -/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!") - if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss. - message = span("danger", message) - deployed_shell.undeploy(message) +/mob/living/silicon/ai + var/mob/living/silicon/robot/deployed_shell = null //For shell control + +/mob/living/silicon/ai/Initialize() + if(config.allow_ai_shells) + verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act + return ..() + +/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target) + if(!config.allow_ai_shells) + to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report.")) + return + + if(incapacitated()) + to_chat(src, span("warning", "You are incapacitated!")) + return + + if(lacks_power()) + to_chat(src, span("warning", "Your core lacks power, wireless is disabled.")) + return + + if(control_disabled) + to_chat(src, span("warning", "Wireless networking module is offline.")) + return + + var/list/possible = list() + for(var/mob/living/silicon/robot/R as anything in GLOB.available_ai_shells) + if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) ) //VOREStation Edit: shell restrictions + if(istype(R.loc, /obj/machinery/recharge_station)) //Check Rechargers + var/obj/machinery/recharge_station/RS = R.loc + if(!(using_map.ai_shell_restricted && !(RS.z in using_map.ai_shell_allowed_levels))) //Allow station borgs to be redeployed from Chargers. + possible += R + + if(isbelly(R.loc)) //check belly space + var/obj/belly/B = R.loc + if(!(using_map.ai_shell_restricted && !(B.owner.z in using_map.ai_shell_allowed_levels))) //No smuggling in borgs + possible += R + + if(!(using_map.ai_shell_restricted && !(R.z in using_map.ai_shell_allowed_levels))) + possible += R + + if(!LAZYLEN(possible)) + to_chat(src, span("warning", "No usable AI shell beacons detected.")) + + if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid + target = tgui_input_list(src, "Which body to control?", "Shell Choice", possible) + + if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) ) + if(target) + to_chat(src, span("warning", "It is no longer possible to deploy to \the [target].")) + else + to_chat(src, span("notice", "Deployment aborted.")) + return + + else if(mind) + soul_link(/datum/soul_link/shared_body, src, target) + deployed_shell = target + target.deploy_init(src) + mind.transfer_to(target) + teleop = target // So the AI 'hears' messages near its core. + target.post_deploy() + +/mob/living/silicon/ai/proc/deploy_to_shell_act() + set category = "AI Commands" + set name = "Deploy to Shell" + deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc. + +/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!") + if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss. + message = span("danger", message) + deployed_shell.undeploy(message) diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index 9c26f64c43f..29ff44b04e9 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -1,23 +1,23 @@ -/mob/living/silicon/ai/death(gibbed) - - if(stat == DEAD) - return - - if(deployed_shell) - disconnect_shell("Disconnecting from remote shell due to critical system failure.") - . = ..(gibbed) - - if(src.eyeobj) - src.eyeobj.setLoc(get_turf(src)) - - remove_ai_verbs(src) - - for(var/obj/machinery/ai_status_display/O in machines) - spawn( 0 ) - O.mode = 2 - if (istype(loc, /obj/item/device/aicard)) - var/obj/item/device/aicard/card = loc - card.update_icon() - - . = ..(gibbed,"gives one shrill beep before falling lifeless.") - density = TRUE +/mob/living/silicon/ai/death(gibbed) + + if(stat == DEAD) + return + + if(deployed_shell) + disconnect_shell("Disconnecting from remote shell due to critical system failure.") + . = ..(gibbed) + + if(src.eyeobj) + src.eyeobj.setLoc(get_turf(src)) + + remove_ai_verbs(src) + + for(var/obj/machinery/ai_status_display/O in machines) + spawn( 0 ) + O.mode = 2 + if (istype(loc, /obj/item/device/aicard)) + var/obj/item/device/aicard/card = loc + card.update_icon() + + . = ..(gibbed,"gives one shrill beep before falling lifeless.") + density = TRUE diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index 7f25b87e21e..1aa9517938e 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -1,43 +1,43 @@ -/mob/living/silicon/ai/examine(mob/user) - . = ..() - - if (src.stat == DEAD) - . += "It appears to be powered-down." - else - if (src.getBruteLoss()) - if (src.getBruteLoss() < 30) - . += "It looks slightly dented." - else - . += "It looks severely dented!" - if (src.getFireLoss()) - if (src.getFireLoss() < 30) - . += "It looks slightly charred." - else - . += "Its casing is melted and heat-warped!" - if (src.getOxyLoss() && (aiRestorePowerRoutine != 0 && !APU_power)) - if (src.getOxyLoss() > 175) - . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER CRITICAL\" warning." - else if(src.getOxyLoss() > 100) - . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER LOW\" warning." - else - . += "It seems to be running on backup power." - - if (src.stat == UNCONSCIOUS) - . += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\"." - - if(deployed_shell) - . += "The wireless networking light is blinking." - - . += "*---------*" - - if(hardware && (hardware.owner == src)) - . += hardware.get_examine_desc() - - user.showLaws(src) - -/mob/proc/showLaws(var/mob/living/silicon/S) - return - -/mob/observer/dead/showLaws(var/mob/living/silicon/S) - if(antagHUD || is_admin(src)) - S.laws.show_laws(src) +/mob/living/silicon/ai/examine(mob/user) + . = ..() + + if (src.stat == DEAD) + . += "It appears to be powered-down." + else + if (src.getBruteLoss()) + if (src.getBruteLoss() < 30) + . += "It looks slightly dented." + else + . += "It looks severely dented!" + if (src.getFireLoss()) + if (src.getFireLoss() < 30) + . += "It looks slightly charred." + else + . += "Its casing is melted and heat-warped!" + if (src.getOxyLoss() && (aiRestorePowerRoutine != 0 && !APU_power)) + if (src.getOxyLoss() > 175) + . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER CRITICAL\" warning." + else if(src.getOxyLoss() > 100) + . += "It seems to be running on backup power. Its display is blinking a \"BACKUP POWER LOW\" warning." + else + . += "It seems to be running on backup power." + + if (src.stat == UNCONSCIOUS) + . += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\"." + + if(deployed_shell) + . += "The wireless networking light is blinking." + + . += "*---------*" + + if(hardware && (hardware.owner == src)) + . += hardware.get_examine_desc() + + user.showLaws(src) + +/mob/proc/showLaws(var/mob/living/silicon/S) + return + +/mob/observer/dead/showLaws(var/mob/living/silicon/S) + if(antagHUD || is_admin(src)) + S.laws.show_laws(src) diff --git a/code/modules/mob/living/silicon/ai/icons.dm b/code/modules/mob/living/silicon/ai/icons.dm index 5f12bd384b6..fdd5017a47f 100644 --- a/code/modules/mob/living/silicon/ai/icons.dm +++ b/code/modules/mob/living/silicon/ai/icons.dm @@ -1,260 +1,260 @@ -var/datum/ai_icon/default_ai_icon = new/datum/ai_icon/blue() -var/list/datum/ai_icon/ai_icons - -/datum/ai_icon - var/name - var/alive_icon - var/alive_light = "#FFFFFF" - var/nopower_icon = "4" - var/nopower_light = "#FFFFFF" - var/dead_icon = "ai-crash" - var/dead_light = "#000099" - -/datum/ai_icon/New(var/name, var/alive_icon, var/nopower_icon, var/dead_icon, var/alive_light, var/nopower_light, var/dead_light) - if(name) - src.name = name - src.alive_icon = alive_icon - src.nopower_icon = nopower_icon - src.dead_icon = dead_icon - src.alive_light = alive_light - src.nopower_light = nopower_light - src.dead_light = dead_light - if(!ai_icons) - ai_icons = list() - init_subtypes(/datum/ai_icon, ai_icons) - ..() - -/datum/ai_icon/red - name = "Red" - alive_icon = "ai-red" - alive_light = "#F04848" - dead_icon = "ai-red-crash" - dead_light = "#F04848" - -/datum/ai_icon/green - name = "Green" - alive_icon = "ai-wierd" - alive_light = "#00FF99" - dead_icon = "ai-weird-crash" - -/datum/ai_icon/blue - name = "Blue" - alive_icon = "ai" - alive_light = "#81DDFF" - -/datum/ai_icon/angry - name = "Angry" - alive_icon = "ai-angryface" - alive_light = "#FFFF33" - -/datum/ai_icon/angel - name = "Angel" - alive_icon = "ai-angel" - dead_icon = "ai-angel-crash" - -/datum/ai_icon/bliss - name = "Bliss" - alive_icon = "ai-bliss" - alive_light = "#5C7A4A" - -/datum/ai_icon/chatterbox - name = "Chatterbox" - alive_icon = "ai-president" - alive_light = "#40666B" - -/datum/ai_icon/database - name = "Database" - alive_icon = "ai-database" - dead_icon = "ai-database-crash" - -/datum/ai_icon/dorf - name = "Dorf" - alive_icon = "ai-dorf" - -/datum/ai_icon/dugtodeep - name = "Dug Too Deep" - alive_icon = "ai-toodeep" - alive_light = "#81DDFF" - -/datum/ai_icon/firewall - name = "Firewall" - alive_icon = "ai-magma" - alive_light = "#FF4126" - -/datum/ai_icon/glitchman - name = "Glitchman" - alive_icon = "ai-glitchman" - dead_icon = "ai-glitchman-crash" - -/datum/ai_icon/goon - name = "Goon" - alive_icon = "ai-goon" - alive_light = "#3E5C80" - dead_icon = "ai-goon-crash" - dead_light = "#3E5C80" - -/datum/ai_icon/heartline - name = "Heartline" - alive_icon = "ai-heartline" - dead_icon = "ai-heartline-crash" - -/datum/ai_icon/helios - name = "Helios" - alive_icon = "ai-helios" - alive_light = "#F2CF73" - -/datum/ai_icon/hourglass - name = "Hourglass" - alive_icon = "ai-hourglass" - -/datum/ai_icon/inverted - name = "Inverted" - alive_icon = "ai-u" - alive_light = "#81DDFF" - dead_icon = "ai-u-crash" - -/datum/ai_icon/lonestar - name = "Lonestar" - alive_icon = "ai-lonestar" - alive_light = "#58751C" - dead_icon = "ai-lonestar-crash" - -/datum/ai_icon/matrix - name = "Matrix" - alive_icon = "ai-matrix" - alive_light = "#449944" - -/datum/ai_icon/monochrome - name = "Monochrome" - alive_icon = "ai-mono" - alive_light = "#585858" - dead_icon = "ai-mono-crash" - -/datum/ai_icon/nanotrasen - name = "NanoTrasen" - alive_icon = "ai-nanotrasen" - alive_light = "#000029" - -/datum/ai_icon/rainbow - name = "Rainbow" - alive_icon = "ai-clown" - alive_light = "#E50213" - -/datum/ai_icon/smiley - name = "Smiley" - alive_icon = "ai-smiley" - alive_light = "#F3DD00" - -/datum/ai_icon/soviet - name = "Soviet" - alive_icon = "ai-soviet" - alive_light = "#FF4307" - dead_icon = "ai-soviet-crash" - dead_light = "#FF4307" - -/datum/ai_icon/Static - name = "Static" - alive_icon = "ai-static" - alive_light = "#4784C1" - alive_icon = "ai-static-crash" - -/datum/ai_icon/text - name = "Text" - alive_icon = "ai-text" - -/datum/ai_icon/trapped - name = "Trapped" - alive_icon = "ai-hades" - dead_icon = "ai-hades-crash" - -/datum/ai_icon/triumvirate - name = "Triumvirate" - alive_icon = "ai-triumvirate" - alive_light = "#020B2B" - -/datum/ai_icon/triumvirate_static - name = "Triumvirate Static" - alive_icon = "ai-triumvirate-malf" - alive_light = "#020B2B" - -/datum/ai_icon/bored - name = "Bored" - alive_icon = "ai-bored" - dead_icon = "ai-eager-crash" - -//Eros Research Platform Ports - -/datum/ai_icon/clown2 - name = "Honk" - alive_icon = "ai-clown2" - dead_icon = "ai-clown2-crash" - -/* -/datum/ai_icon/boxfort - name = "Boxfort" - alive_icon = "ai-boxfort" - dead_icon = "ai-boxfort_dead" -*/ - -/datum/ai_icon/ravensdale - name = "Integration" - alive_icon = "ai-ravensdale" - dead_icon = "ai-ravensdale-crash" - -/datum/ai_icon/gentoo - name = "Gentoo" - alive_icon = "ai-gentoo" - dead_icon = "ai-gentoo-crash" - -/datum/ai_icon/serithi - name = "Mechanicus" - alive_icon = "ai-serithi" - dead_icon = "ai-serithi-crash" - -/* -/datum/ai_icon/alien - name = "Xenomorph" - alive_icon = "ai-alien" - dead_icon = "ai-alien-crash" -*/ - -/datum/ai_icon/syndicat - name = "Syndi-cat" - alive_icon = "ai-syndicatmeow" - -/datum/ai_icon/wasp - name = "Wasp" - alive_icon = "ai-wasp" - -/datum/ai_icon/sheltered - name = "Doctor" - alive_icon = "ai-sheltered" - -/datum/ai_icon/fabulous - name = "Fabulous" - alive_icon = "ai-fabulous" - -/datum/ai_icon/eager - name = "Eager" - alive_icon = "ai-eager" - dead_icon = "ai-eager-crash" - -/datum/ai_icon/royal - name = "Royal" - alive_icon = "ai-royal" - -/datum/ai_icon/pirate - name = "Pirate" - alive_icon = "ai-pirate" - -/datum/ai_icon/bloodylove - name = "Love" - alive_icon = "ai-bloodylove" - -/datum/ai_icon/ahasuerus - name = "Ahasuerus" - alive_icon = "ai-ahasuerus" - -/datum/ai_icon/godfrey - name = "Godfrey" +var/datum/ai_icon/default_ai_icon = new/datum/ai_icon/blue() +var/list/datum/ai_icon/ai_icons + +/datum/ai_icon + var/name + var/alive_icon + var/alive_light = "#FFFFFF" + var/nopower_icon = "4" + var/nopower_light = "#FFFFFF" + var/dead_icon = "ai-crash" + var/dead_light = "#000099" + +/datum/ai_icon/New(var/name, var/alive_icon, var/nopower_icon, var/dead_icon, var/alive_light, var/nopower_light, var/dead_light) + if(name) + src.name = name + src.alive_icon = alive_icon + src.nopower_icon = nopower_icon + src.dead_icon = dead_icon + src.alive_light = alive_light + src.nopower_light = nopower_light + src.dead_light = dead_light + if(!ai_icons) + ai_icons = list() + init_subtypes(/datum/ai_icon, ai_icons) + ..() + +/datum/ai_icon/red + name = "Red" + alive_icon = "ai-red" + alive_light = "#F04848" + dead_icon = "ai-red-crash" + dead_light = "#F04848" + +/datum/ai_icon/green + name = "Green" + alive_icon = "ai-wierd" + alive_light = "#00FF99" + dead_icon = "ai-weird-crash" + +/datum/ai_icon/blue + name = "Blue" + alive_icon = "ai" + alive_light = "#81DDFF" + +/datum/ai_icon/angry + name = "Angry" + alive_icon = "ai-angryface" + alive_light = "#FFFF33" + +/datum/ai_icon/angel + name = "Angel" + alive_icon = "ai-angel" + dead_icon = "ai-angel-crash" + +/datum/ai_icon/bliss + name = "Bliss" + alive_icon = "ai-bliss" + alive_light = "#5C7A4A" + +/datum/ai_icon/chatterbox + name = "Chatterbox" + alive_icon = "ai-president" + alive_light = "#40666B" + +/datum/ai_icon/database + name = "Database" + alive_icon = "ai-database" + dead_icon = "ai-database-crash" + +/datum/ai_icon/dorf + name = "Dorf" + alive_icon = "ai-dorf" + +/datum/ai_icon/dugtodeep + name = "Dug Too Deep" + alive_icon = "ai-toodeep" + alive_light = "#81DDFF" + +/datum/ai_icon/firewall + name = "Firewall" + alive_icon = "ai-magma" + alive_light = "#FF4126" + +/datum/ai_icon/glitchman + name = "Glitchman" + alive_icon = "ai-glitchman" + dead_icon = "ai-glitchman-crash" + +/datum/ai_icon/goon + name = "Goon" + alive_icon = "ai-goon" + alive_light = "#3E5C80" + dead_icon = "ai-goon-crash" + dead_light = "#3E5C80" + +/datum/ai_icon/heartline + name = "Heartline" + alive_icon = "ai-heartline" + dead_icon = "ai-heartline-crash" + +/datum/ai_icon/helios + name = "Helios" + alive_icon = "ai-helios" + alive_light = "#F2CF73" + +/datum/ai_icon/hourglass + name = "Hourglass" + alive_icon = "ai-hourglass" + +/datum/ai_icon/inverted + name = "Inverted" + alive_icon = "ai-u" + alive_light = "#81DDFF" + dead_icon = "ai-u-crash" + +/datum/ai_icon/lonestar + name = "Lonestar" + alive_icon = "ai-lonestar" + alive_light = "#58751C" + dead_icon = "ai-lonestar-crash" + +/datum/ai_icon/matrix + name = "Matrix" + alive_icon = "ai-matrix" + alive_light = "#449944" + +/datum/ai_icon/monochrome + name = "Monochrome" + alive_icon = "ai-mono" + alive_light = "#585858" + dead_icon = "ai-mono-crash" + +/datum/ai_icon/nanotrasen + name = "NanoTrasen" + alive_icon = "ai-nanotrasen" + alive_light = "#000029" + +/datum/ai_icon/rainbow + name = "Rainbow" + alive_icon = "ai-clown" + alive_light = "#E50213" + +/datum/ai_icon/smiley + name = "Smiley" + alive_icon = "ai-smiley" + alive_light = "#F3DD00" + +/datum/ai_icon/soviet + name = "Soviet" + alive_icon = "ai-soviet" + alive_light = "#FF4307" + dead_icon = "ai-soviet-crash" + dead_light = "#FF4307" + +/datum/ai_icon/Static + name = "Static" + alive_icon = "ai-static" + alive_light = "#4784C1" + alive_icon = "ai-static-crash" + +/datum/ai_icon/text + name = "Text" + alive_icon = "ai-text" + +/datum/ai_icon/trapped + name = "Trapped" + alive_icon = "ai-hades" + dead_icon = "ai-hades-crash" + +/datum/ai_icon/triumvirate + name = "Triumvirate" + alive_icon = "ai-triumvirate" + alive_light = "#020B2B" + +/datum/ai_icon/triumvirate_static + name = "Triumvirate Static" + alive_icon = "ai-triumvirate-malf" + alive_light = "#020B2B" + +/datum/ai_icon/bored + name = "Bored" + alive_icon = "ai-bored" + dead_icon = "ai-eager-crash" + +//Eros Research Platform Ports + +/datum/ai_icon/clown2 + name = "Honk" + alive_icon = "ai-clown2" + dead_icon = "ai-clown2-crash" + +/* +/datum/ai_icon/boxfort + name = "Boxfort" + alive_icon = "ai-boxfort" + dead_icon = "ai-boxfort_dead" +*/ + +/datum/ai_icon/ravensdale + name = "Integration" + alive_icon = "ai-ravensdale" + dead_icon = "ai-ravensdale-crash" + +/datum/ai_icon/gentoo + name = "Gentoo" + alive_icon = "ai-gentoo" + dead_icon = "ai-gentoo-crash" + +/datum/ai_icon/serithi + name = "Mechanicus" + alive_icon = "ai-serithi" + dead_icon = "ai-serithi-crash" + +/* +/datum/ai_icon/alien + name = "Xenomorph" + alive_icon = "ai-alien" + dead_icon = "ai-alien-crash" +*/ + +/datum/ai_icon/syndicat + name = "Syndi-cat" + alive_icon = "ai-syndicatmeow" + +/datum/ai_icon/wasp + name = "Wasp" + alive_icon = "ai-wasp" + +/datum/ai_icon/sheltered + name = "Doctor" + alive_icon = "ai-sheltered" + +/datum/ai_icon/fabulous + name = "Fabulous" + alive_icon = "ai-fabulous" + +/datum/ai_icon/eager + name = "Eager" + alive_icon = "ai-eager" + dead_icon = "ai-eager-crash" + +/datum/ai_icon/royal + name = "Royal" + alive_icon = "ai-royal" + +/datum/ai_icon/pirate + name = "Pirate" + alive_icon = "ai-pirate" + +/datum/ai_icon/bloodylove + name = "Love" + alive_icon = "ai-bloodylove" + +/datum/ai_icon/ahasuerus + name = "Ahasuerus" + alive_icon = "ai-ahasuerus" + +/datum/ai_icon/godfrey + name = "Godfrey" alive_icon = "ai-godfrey" \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index fc244d67423..7f3f1a7f142 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -1,32 +1,32 @@ -var/global/list/empty_playable_ai_cores = list() - -/hook/roundstart/proc/spawn_empty_ai() - for(var/obj/effect/landmark/start/S in landmarks_list) - if(S.name != "AI") - continue - if(locate(/mob/living) in S.loc) - continue - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S)) - - return 1 - -/mob/living/silicon/ai/verb/store_core() - set name = "Store Core" - set category = "OOC" - set desc = "Enter intelligence storage. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." - - if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") - to_chat(usr, "You cannot use this verb in malfunction. If you need to leave, please adminhelp.") - return - - // Guard against misclicks, this isn't the sort of thing we want happening accidentally - if(tgui_alert(usr, "WARNING: This will immediately empty your core and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Store Core", list("No", "Yes")) != "Yes") - return - - // We warned you. - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc) - global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") - - //Handle job slot/tater cleanup. - set_respawn_timer() +var/global/list/empty_playable_ai_cores = list() + +/hook/roundstart/proc/spawn_empty_ai() + for(var/obj/effect/landmark/start/S in landmarks_list) + if(S.name != "AI") + continue + if(locate(/mob/living) in S.loc) + continue + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S)) + + return 1 + +/mob/living/silicon/ai/verb/store_core() + set name = "Store Core" + set category = "OOC" + set desc = "Enter intelligence storage. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." + + if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") + to_chat(usr, "You cannot use this verb in malfunction. If you need to leave, please adminhelp.") + return + + // Guard against misclicks, this isn't the sort of thing we want happening accidentally + if(tgui_alert(usr, "WARNING: This will immediately empty your core and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Store Core", list("No", "Yes")) != "Yes") + return + + // We warned you. + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc) + global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") + + //Handle job slot/tater cleanup. + set_respawn_timer() clear_client() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm index 3dceddb51a7..83fde3cd3b4 100755 --- a/code/modules/mob/living/silicon/ai/laws.dm +++ b/code/modules/mob/living/silicon/ai/laws.dm @@ -1,27 +1,27 @@ -/mob/living/silicon/ai/proc/show_laws_verb() - set category = "AI Commands" - set name = "Show Laws" - src.show_laws() - -/mob/living/silicon/ai/show_laws(var/everyone = 0) - var/who - - if (everyone) - who = world - else - who = src - to_chat(who, "Obey these laws:") - - src.laws_sanity_check() - src.laws.show_laws(who) - -/mob/living/silicon/ai/add_ion_law(var/law) - ..() - for(var/mob/living/silicon/robot/R in mob_list) - if(R.lawupdate && (R.connected_ai == src)) - R.show_laws() - -/mob/living/silicon/ai/proc/ai_checklaws() - set category = "AI Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/ai/proc/show_laws_verb() + set category = "AI Commands" + set name = "Show Laws" + src.show_laws() + +/mob/living/silicon/ai/show_laws(var/everyone = 0) + var/who + + if (everyone) + who = world + else + who = src + to_chat(who, "Obey these laws:") + + src.laws_sanity_check() + src.laws.show_laws(who) + +/mob/living/silicon/ai/add_ion_law(var/law) + ..() + for(var/mob/living/silicon/robot/R in mob_list) + if(R.lawupdate && (R.connected_ai == src)) + R.show_laws() + +/mob/living/silicon/ai/proc/ai_checklaws() + set category = "AI Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 49bb3eaf164..ed528ea1c0e 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -1,181 +1,181 @@ -/mob/living/silicon/ai/Life() - if (src.stat == DEAD) - return - else //I'm not removing that shitton of tabs, unneeded as they are. -- Urist - //Being dead doesn't mean your temperature never changes - var/turf/T = get_turf(src) - - if (src.stat != CONSCIOUS) - src.cameraFollow = null - src.reset_view(null) - disconnect_shell("Disconnecting from remote shell due to local system failure.") - - src.updatehealth() - - if (!hardware_integrity() || !backup_capacitor()) - death() - return - - // If our powersupply object was destroyed somehow, create new one. - if(!psupply) - create_powersupply() - - - // Handle power damage (oxy) - if(aiRestorePowerRoutine != 0 && !APU_power) - // Lose power - adjustOxyLoss(1) - else - // Gain Power - aiRestorePowerRoutine = 0 // Necessary if AI activated it's APU AFTER losing primary power. - adjustOxyLoss(-1) - - handle_stunned() // Handle EMP-stun - lying = 0 // Handle lying down - - malf_process() - - if(APU_power && (hardware_integrity() < 50)) - to_chat(src, "APU GENERATOR FAILURE! (System Damaged)") - stop_apu(1) - - var/blind = 0 - var/area/loc = null - if (istype(T, /turf)) - loc = T.loc - if (istype(loc, /area)) - if (!loc.power_equip && !istype(src.loc,/obj/item) && !APU_power) - blind = 1 - - if (!blind) - src.sight |= SEE_TURFS - src.sight |= SEE_MOBS - src.sight |= SEE_OBJS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_LIVING - - if (aiRestorePowerRoutine==2) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - update_icon() - return - else if (aiRestorePowerRoutine==3) - to_chat(src, "Alert cancelled. Power has been restored.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - update_icon() - return - else if (APU_power) - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - update_icon() - return - else - var/area/current_area = get_area(src) - - if (lacks_power()) - if (aiRestorePowerRoutine==0) - aiRestorePowerRoutine = 1 - - //Blind the AI - update_icon() - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - src.sight = src.sight&~SEE_TURFS - src.sight = src.sight&~SEE_MOBS - src.sight = src.sight&~SEE_OBJS - src.see_in_dark = 0 - src.see_invisible = SEE_INVISIBLE_LIVING - - //Now to tell the AI why they're blind and dying slowly. - - to_chat(src, "You've lost power!") - disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.") - - spawn(20) - to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") - end_multicam() - sleep(50) - if (loc.power_equip) - if (!istype(T, /turf/space)) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") - return - to_chat(src, "Fault confirmed: missing external power. Shutting down main control system to save power.") - sleep(20) - to_chat(src, "Emergency control system online. Verifying connection to power network.") - sleep(50) - if (istype(T, /turf/space)) - to_chat(src, "Unable to verify! No power connection detected!") - aiRestorePowerRoutine = 2 - return - to_chat(src, "Connection verified. Searching for APC in power network.") - sleep(50) - var/obj/machinery/power/apc/theAPC = null - - var/PRP - for (PRP=1, PRP<=4, PRP++) - for (var/obj/machinery/power/apc/APC in current_area) - if (!(APC.stat & BROKEN)) - theAPC = APC - break - if (!theAPC) - switch(PRP) - if (1) - to_chat(src, "Unable to locate APC!") - else - to_chat(src, "Lost connection with the APC!") - src:aiRestorePowerRoutine = 2 - return - if (loc.power_equip) - if (!istype(T, /turf/space)) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - clear_fullscreen("blind") //This, too, is a fix to issue 603 - return - switch(PRP) - if (1) - to_chat(src, "APC located. Optimizing route to APC to avoid needless power waste.") - if (2) - to_chat(src, "Best route identified. Hacking offline APC power port.") - if (3) - to_chat(src, "Power port upload access confirmed. Loading control program into APC power port software.") - if (4) - to_chat(src, "Transfer complete. Forcing APC to execute program.") - sleep(50) - to_chat(src, "Receiving control information from APC.") - sleep(2) - theAPC.operating = 1 - theAPC.equipment = 3 - theAPC.update() - aiRestorePowerRoutine = 3 - to_chat(src, "Here are your current laws:") - show_laws() - update_icon() - sleep(50) - theAPC = null - - process_queued_alarms() - handle_regular_hud_updates() - handle_vision() - -/mob/living/silicon/ai/proc/lacks_power() - if(APU_power) - return 0 - var/turf/T = get_turf(src) - var/area/A = get_area(src) - return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) - -/mob/living/silicon/ai/updatehealth() - if(status_flags & GODMODE) - health = 100 - set_stat(CONSCIOUS) - setOxyLoss(0) - else - health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. - -/mob/living/silicon/ai/rejuvenate() - ..() - add_ai_verbs(src) - +/mob/living/silicon/ai/Life() + if (src.stat == DEAD) + return + else //I'm not removing that shitton of tabs, unneeded as they are. -- Urist + //Being dead doesn't mean your temperature never changes + var/turf/T = get_turf(src) + + if (src.stat != CONSCIOUS) + src.cameraFollow = null + src.reset_view(null) + disconnect_shell("Disconnecting from remote shell due to local system failure.") + + src.updatehealth() + + if (!hardware_integrity() || !backup_capacitor()) + death() + return + + // If our powersupply object was destroyed somehow, create new one. + if(!psupply) + create_powersupply() + + + // Handle power damage (oxy) + if(aiRestorePowerRoutine != 0 && !APU_power) + // Lose power + adjustOxyLoss(1) + else + // Gain Power + aiRestorePowerRoutine = 0 // Necessary if AI activated it's APU AFTER losing primary power. + adjustOxyLoss(-1) + + handle_stunned() // Handle EMP-stun + lying = 0 // Handle lying down + + malf_process() + + if(APU_power && (hardware_integrity() < 50)) + to_chat(src, "APU GENERATOR FAILURE! (System Damaged)") + stop_apu(1) + + var/blind = 0 + var/area/loc = null + if (istype(T, /turf)) + loc = T.loc + if (istype(loc, /area)) + if (!loc.power_equip && !istype(src.loc,/obj/item) && !APU_power) + blind = 1 + + if (!blind) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.sight |= SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_LIVING + + if (aiRestorePowerRoutine==2) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + update_icon() + return + else if (aiRestorePowerRoutine==3) + to_chat(src, "Alert cancelled. Power has been restored.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + update_icon() + return + else if (APU_power) + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + update_icon() + return + else + var/area/current_area = get_area(src) + + if (lacks_power()) + if (aiRestorePowerRoutine==0) + aiRestorePowerRoutine = 1 + + //Blind the AI + update_icon() + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + src.sight = src.sight&~SEE_TURFS + src.sight = src.sight&~SEE_MOBS + src.sight = src.sight&~SEE_OBJS + src.see_in_dark = 0 + src.see_invisible = SEE_INVISIBLE_LIVING + + //Now to tell the AI why they're blind and dying slowly. + + to_chat(src, "You've lost power!") + disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.") + + spawn(20) + to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") + end_multicam() + sleep(50) + if (loc.power_equip) + if (!istype(T, /turf/space)) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") + return + to_chat(src, "Fault confirmed: missing external power. Shutting down main control system to save power.") + sleep(20) + to_chat(src, "Emergency control system online. Verifying connection to power network.") + sleep(50) + if (istype(T, /turf/space)) + to_chat(src, "Unable to verify! No power connection detected!") + aiRestorePowerRoutine = 2 + return + to_chat(src, "Connection verified. Searching for APC in power network.") + sleep(50) + var/obj/machinery/power/apc/theAPC = null + + var/PRP + for (PRP=1, PRP<=4, PRP++) + for (var/obj/machinery/power/apc/APC in current_area) + if (!(APC.stat & BROKEN)) + theAPC = APC + break + if (!theAPC) + switch(PRP) + if (1) + to_chat(src, "Unable to locate APC!") + else + to_chat(src, "Lost connection with the APC!") + src:aiRestorePowerRoutine = 2 + return + if (loc.power_equip) + if (!istype(T, /turf/space)) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + clear_fullscreen("blind") //This, too, is a fix to issue 603 + return + switch(PRP) + if (1) + to_chat(src, "APC located. Optimizing route to APC to avoid needless power waste.") + if (2) + to_chat(src, "Best route identified. Hacking offline APC power port.") + if (3) + to_chat(src, "Power port upload access confirmed. Loading control program into APC power port software.") + if (4) + to_chat(src, "Transfer complete. Forcing APC to execute program.") + sleep(50) + to_chat(src, "Receiving control information from APC.") + sleep(2) + theAPC.operating = 1 + theAPC.equipment = 3 + theAPC.update() + aiRestorePowerRoutine = 3 + to_chat(src, "Here are your current laws:") + show_laws() + update_icon() + sleep(50) + theAPC = null + + process_queued_alarms() + handle_regular_hud_updates() + handle_vision() + +/mob/living/silicon/ai/proc/lacks_power() + if(APU_power) + return 0 + var/turf/T = get_turf(src) + var/area/A = get_area(src) + return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) + +/mob/living/silicon/ai/updatehealth() + if(status_flags & GODMODE) + health = 100 + set_stat(CONSCIOUS) + setOxyLoss(0) + else + health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. + +/mob/living/silicon/ai/rejuvenate() + ..() + add_ai_verbs(src) + diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index 57856a861f8..ea3feaa2303 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -1,12 +1,12 @@ -/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up �_� ~Carn - ..() - for(var/obj/effect/rune/rune in rune_list) - client.images += rune.blood_image - if(stat != DEAD) - for(var/obj/machinery/ai_status_display/O in machines) //change status - O.mode = 1 - O.emotion = "Neutral" - if(multicam_on) - end_multicam() - src.view_core() +/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up �_� ~Carn + ..() + for(var/obj/effect/rune/rune in rune_list) + client.images += rune.blood_image + if(stat != DEAD) + for(var/obj/machinery/ai_status_display/O in machines) //change status + O.mode = 1 + O.emotion = "Neutral" + if(multicam_on) + end_multicam() + src.view_core() return \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/logout.dm b/code/modules/mob/living/silicon/ai/logout.dm index 25921ef9600..e884841de4b 100644 --- a/code/modules/mob/living/silicon/ai/logout.dm +++ b/code/modules/mob/living/silicon/ai/logout.dm @@ -1,10 +1,10 @@ -/mob/living/silicon/ai/Logout() - ..() - for(var/obj/machinery/ai_status_display/O in machines) //change status - O.mode = 0 - if(!isturf(loc)) - if (client) - client.eye = loc - client.perspective = EYE_PERSPECTIVE - src.view_core() +/mob/living/silicon/ai/Logout() + ..() + for(var/obj/machinery/ai_status_display/O in machines) //change status + O.mode = 0 + if(!isturf(loc)) + if (client) + client.eye = loc + client.perspective = EYE_PERSPECTIVE + src.view_core() return \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/malf.dm b/code/modules/mob/living/silicon/ai/malf.dm index 047e796514c..bb07440625e 100644 --- a/code/modules/mob/living/silicon/ai/malf.dm +++ b/code/modules/mob/living/silicon/ai/malf.dm @@ -1,140 +1,140 @@ -// NEWMALF FUNCTIONS/PROCEDURES - -// Sets up malfunction-related variables, research system and such. -/mob/living/silicon/ai/proc/setup_for_malf() - var/mob/living/silicon/ai/user = src - // Setup Variables - malfunctioning = 1 - research = new/datum/malf_research() - research.owner = src - hacked_apcs = list() - recalc_cpu() - - verbs += new/datum/game_mode/malfunction/verb/ai_select_hardware() - verbs += new/datum/game_mode/malfunction/verb/ai_select_research() - verbs += new/datum/game_mode/malfunction/verb/ai_help() - - // And greet user with some OOC info. - to_chat(user, "You are malfunctioning, you do not have to follow any laws.") - to_chat(user, "Use ai-help command to view relevant information about your abilities") - -// Safely remove malfunction status, fixing hacked APCs and resetting variables. -/mob/living/silicon/ai/proc/stop_malf() - var/mob/living/silicon/ai/user = src - // Generic variables - malfunctioning = 0 - sleep(10) - research = null - // Fix hacked APCs - if(hacked_apcs) - for(var/obj/machinery/power/apc/A in hacked_apcs) - A.hacker = null - hacked_apcs = null - // Reset our verbs - src.verbs = null - add_ai_verbs() - // Let them know. - to_chat(user, "You are no longer malfunctioning. Your abilities have been removed.") - -// Called every tick. Checks if AI is malfunctioning. If yes calls Process on research datum which handles all logic. -/mob/living/silicon/ai/proc/malf_process() - if(!malfunctioning) - return - if(!research) - if(!errored) - errored = 1 - error("malf_process() called on AI without research datum. Report this.") - message_admins("ERROR: malf_process() called on AI without research datum. If admin modified one of the AI's vars revert the change and don't modify variables directly, instead use ProcCall or admin panels.") - spawn(1200) - errored = 0 - return - recalc_cpu() - if(APU_power || aiRestorePowerRoutine != 0) - research.process(1) - else - research.process(0) - -// Recalculates CPU time gain and storage capacities. -/mob/living/silicon/ai/proc/recalc_cpu() - // AI Starts with these values. - var/cpu_gain = 0.01 - var/cpu_storage = 10 - - // Off-Station APCs should not count towards CPU generation. - for(var/obj/machinery/power/apc/A in hacked_apcs) - if(A.z in using_map.station_levels) - cpu_gain += 0.004 - cpu_storage += 10 - - research.max_cpu = cpu_storage + override_CPUStorage - if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) - research.max_cpu = research.max_cpu * 1.5 - research.stored_cpu = min(research.stored_cpu, research.max_cpu) - - research.cpu_increase_per_tick = cpu_gain + override_CPURate - if(hardware && istype(hardware, /datum/malf_hardware/dual_cpu)) - research.cpu_increase_per_tick = research.cpu_increase_per_tick * 2 - -// Starts AI's APU generator -/mob/living/silicon/ai/proc/start_apu(var/shutup = 0) - if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) - if(!shutup) - to_chat(src, "You do not have an APU generator and you shouldn't have this verb. Report this.") - return - if(hardware_integrity() < 50) - if(!shutup) - to_chat(src, "Starting APU... FAULT(System Damaged)") - return - if(!shutup) - to_chat(src, "Starting APU... ONLINE") - APU_power = 1 - -// Stops AI's APU generator -/mob/living/silicon/ai/proc/stop_apu(var/shutup = 0) - if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) - return - - if(APU_power) - APU_power = 0 - if(!shutup) - to_chat(src, "Shutting down APU... DONE") - -// Returns percentage of AI's remaining backup capacitor charge (maxhealth - oxyloss). -/mob/living/silicon/ai/proc/backup_capacitor() - return ((200 - getOxyLoss()) / 2) - -// Returns percentage of AI's remaining hardware integrity (maxhealth - (bruteloss + fireloss)) -/mob/living/silicon/ai/proc/hardware_integrity() - return (health-config.health_threshold_dead)/2 - -// Shows capacitor charge and hardware integrity information to the AI in Status tab. -/mob/living/silicon/ai/show_system_integrity() - if(!src.stat) - stat(null, text("Hardware integrity: [hardware_integrity()]%")) - stat(null, text("Internal capacitor: [backup_capacitor()]%")) - else - stat(null, text("Systems nonfunctional")) - -// Shows AI Malfunction related information to the AI. -/mob/living/silicon/ai/show_malf_ai() - if(src.is_malf()) - if(src.hacked_apcs) - stat(null, "Hacked APCs: [src.hacked_apcs.len]") - stat(null, "System Status: [src.hacking ? "Busy" : "Stand-By"]") - if(src.research) - stat(null, "Available CPU: [src.research.stored_cpu] TFlops") - stat(null, "Maximal CPU: [src.research.max_cpu] TFlops") - stat(null, "CPU generation rate: [src.research.cpu_increase_per_tick * 10] TFlops/s") - stat(null, "Current research focus: [src.research.focus ? src.research.focus.name : "None"]") - if(src.research.focus) - stat(null, "Research completed: [round(src.research.focus.invested, 0.1)]/[round(src.research.focus.price)]") - if(system_override == 1) - stat(null, "SYSTEM OVERRIDE INITIATED") - else if(system_override == 2) - stat(null, "SYSTEM OVERRIDE COMPLETED") - -// Cleaner proc for creating powersupply for an AI. -/mob/living/silicon/ai/proc/create_powersupply() - if(psupply) - qdel(psupply) - psupply = new/obj/machinery/ai_powersupply(src) +// NEWMALF FUNCTIONS/PROCEDURES + +// Sets up malfunction-related variables, research system and such. +/mob/living/silicon/ai/proc/setup_for_malf() + var/mob/living/silicon/ai/user = src + // Setup Variables + malfunctioning = 1 + research = new/datum/malf_research() + research.owner = src + hacked_apcs = list() + recalc_cpu() + + verbs += new/datum/game_mode/malfunction/verb/ai_select_hardware() + verbs += new/datum/game_mode/malfunction/verb/ai_select_research() + verbs += new/datum/game_mode/malfunction/verb/ai_help() + + // And greet user with some OOC info. + to_chat(user, "You are malfunctioning, you do not have to follow any laws.") + to_chat(user, "Use ai-help command to view relevant information about your abilities") + +// Safely remove malfunction status, fixing hacked APCs and resetting variables. +/mob/living/silicon/ai/proc/stop_malf() + var/mob/living/silicon/ai/user = src + // Generic variables + malfunctioning = 0 + sleep(10) + research = null + // Fix hacked APCs + if(hacked_apcs) + for(var/obj/machinery/power/apc/A in hacked_apcs) + A.hacker = null + hacked_apcs = null + // Reset our verbs + src.verbs = null + add_ai_verbs() + // Let them know. + to_chat(user, "You are no longer malfunctioning. Your abilities have been removed.") + +// Called every tick. Checks if AI is malfunctioning. If yes calls Process on research datum which handles all logic. +/mob/living/silicon/ai/proc/malf_process() + if(!malfunctioning) + return + if(!research) + if(!errored) + errored = 1 + error("malf_process() called on AI without research datum. Report this.") + message_admins("ERROR: malf_process() called on AI without research datum. If admin modified one of the AI's vars revert the change and don't modify variables directly, instead use ProcCall or admin panels.") + spawn(1200) + errored = 0 + return + recalc_cpu() + if(APU_power || aiRestorePowerRoutine != 0) + research.process(1) + else + research.process(0) + +// Recalculates CPU time gain and storage capacities. +/mob/living/silicon/ai/proc/recalc_cpu() + // AI Starts with these values. + var/cpu_gain = 0.01 + var/cpu_storage = 10 + + // Off-Station APCs should not count towards CPU generation. + for(var/obj/machinery/power/apc/A in hacked_apcs) + if(A.z in using_map.station_levels) + cpu_gain += 0.004 + cpu_storage += 10 + + research.max_cpu = cpu_storage + override_CPUStorage + if(hardware && istype(hardware, /datum/malf_hardware/dual_ram)) + research.max_cpu = research.max_cpu * 1.5 + research.stored_cpu = min(research.stored_cpu, research.max_cpu) + + research.cpu_increase_per_tick = cpu_gain + override_CPURate + if(hardware && istype(hardware, /datum/malf_hardware/dual_cpu)) + research.cpu_increase_per_tick = research.cpu_increase_per_tick * 2 + +// Starts AI's APU generator +/mob/living/silicon/ai/proc/start_apu(var/shutup = 0) + if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) + if(!shutup) + to_chat(src, "You do not have an APU generator and you shouldn't have this verb. Report this.") + return + if(hardware_integrity() < 50) + if(!shutup) + to_chat(src, "Starting APU... FAULT(System Damaged)") + return + if(!shutup) + to_chat(src, "Starting APU... ONLINE") + APU_power = 1 + +// Stops AI's APU generator +/mob/living/silicon/ai/proc/stop_apu(var/shutup = 0) + if(!hardware || !istype(hardware, /datum/malf_hardware/apu_gen)) + return + + if(APU_power) + APU_power = 0 + if(!shutup) + to_chat(src, "Shutting down APU... DONE") + +// Returns percentage of AI's remaining backup capacitor charge (maxhealth - oxyloss). +/mob/living/silicon/ai/proc/backup_capacitor() + return ((200 - getOxyLoss()) / 2) + +// Returns percentage of AI's remaining hardware integrity (maxhealth - (bruteloss + fireloss)) +/mob/living/silicon/ai/proc/hardware_integrity() + return (health-config.health_threshold_dead)/2 + +// Shows capacitor charge and hardware integrity information to the AI in Status tab. +/mob/living/silicon/ai/show_system_integrity() + if(!src.stat) + stat(null, text("Hardware integrity: [hardware_integrity()]%")) + stat(null, text("Internal capacitor: [backup_capacitor()]%")) + else + stat(null, text("Systems nonfunctional")) + +// Shows AI Malfunction related information to the AI. +/mob/living/silicon/ai/show_malf_ai() + if(src.is_malf()) + if(src.hacked_apcs) + stat(null, "Hacked APCs: [src.hacked_apcs.len]") + stat(null, "System Status: [src.hacking ? "Busy" : "Stand-By"]") + if(src.research) + stat(null, "Available CPU: [src.research.stored_cpu] TFlops") + stat(null, "Maximal CPU: [src.research.max_cpu] TFlops") + stat(null, "CPU generation rate: [src.research.cpu_increase_per_tick * 10] TFlops/s") + stat(null, "Current research focus: [src.research.focus ? src.research.focus.name : "None"]") + if(src.research.focus) + stat(null, "Research completed: [round(src.research.focus.invested, 0.1)]/[round(src.research.focus.price)]") + if(system_override == 1) + stat(null, "SYSTEM OVERRIDE INITIATED") + else if(system_override == 2) + stat(null, "SYSTEM OVERRIDE COMPLETED") + +// Cleaner proc for creating powersupply for an AI. +/mob/living/silicon/ai/proc/create_powersupply() + if(psupply) + qdel(psupply) + psupply = new/obj/machinery/ai_powersupply(src) diff --git a/code/modules/mob/living/silicon/decoy/death.dm b/code/modules/mob/living/silicon/decoy/death.dm index 759b958bc2c..a9008fb8661 100644 --- a/code/modules/mob/living/silicon/decoy/death.dm +++ b/code/modules/mob/living/silicon/decoy/death.dm @@ -1,8 +1,8 @@ -/mob/living/silicon/decoy/death(gibbed) - if(stat == DEAD) return - icon_state = "ai-crash" - spawn(10) - explosion(loc, 3, 6, 12, 15) - for(var/obj/machinery/ai_status_display/O in machines) //change status - O.mode = 2 +/mob/living/silicon/decoy/death(gibbed) + if(stat == DEAD) return + icon_state = "ai-crash" + spawn(10) + explosion(loc, 3, 6, 12, 15) + for(var/obj/machinery/ai_status_display/O in machines) //change status + O.mode = 2 return ..(gibbed) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/decoy/decoy.dm b/code/modules/mob/living/silicon/decoy/decoy.dm index 6abdaadde8c..ec58fbacd65 100644 --- a/code/modules/mob/living/silicon/decoy/decoy.dm +++ b/code/modules/mob/living/silicon/decoy/decoy.dm @@ -1,12 +1,12 @@ -/mob/living/silicon/decoy - name = "AI" - icon = 'icons/mob/AI.dmi'// - icon_state = "ai" - anchored = TRUE // -- TLE - canmove = 0 - -/mob/living/silicon/decoy/New() - src.icon = 'icons/mob/AI.dmi' - src.icon_state = "ai" - src.anchored = TRUE +/mob/living/silicon/decoy + name = "AI" + icon = 'icons/mob/AI.dmi'// + icon_state = "ai" + anchored = TRUE // -- TLE + canmove = 0 + +/mob/living/silicon/decoy/New() + src.icon = 'icons/mob/AI.dmi' + src.icon_state = "ai" + src.anchored = TRUE src.canmove = 0 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/decoy/life.dm b/code/modules/mob/living/silicon/decoy/life.dm index 07683eb2dcf..66e55020cf6 100644 --- a/code/modules/mob/living/silicon/decoy/life.dm +++ b/code/modules/mob/living/silicon/decoy/life.dm @@ -1,15 +1,15 @@ -/mob/living/silicon/decoy/Life() - if (src.stat == 2) - return - else - if (src.health <= config.health_threshold_dead && src.stat != 2) - death() - return - - -/mob/living/silicon/decoy/updatehealth() - if(status_flags & GODMODE) - health = 100 - set_stat(CONSCIOUS) - else - health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() +/mob/living/silicon/decoy/Life() + if (src.stat == 2) + return + else + if (src.health <= config.health_threshold_dead && src.stat != 2) + death() + return + + +/mob/living/silicon/decoy/updatehealth() + if(status_flags & GODMODE) + health = 100 + set_stat(CONSCIOUS) + else + health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm index f2e15af0e8f..0e48e7e6758 100644 --- a/code/modules/mob/living/silicon/login.dm +++ b/code/modules/mob/living/silicon/login.dm @@ -1,3 +1,3 @@ -/mob/living/silicon/Login() - SetSleeping(0) +/mob/living/silicon/Login() + SetSleeping(0) ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/pai/death.dm b/code/modules/mob/living/silicon/pai/death.dm index 6290b72f51c..d2ec6e8b053 100644 --- a/code/modules/mob/living/silicon/pai/death.dm +++ b/code/modules/mob/living/silicon/pai/death.dm @@ -1,16 +1,16 @@ -//VOREStation Edit - Let's make it so that pAIs don't just always cease to be when they die! It would be cool if we could fix them. -/mob/living/silicon/pai/death(gibbed,deathmessage="fizzles out and clatters to the floor...") -// set_respawn_timer() - release_vore_contents() - close_up(TRUE) - if(card) - card.cut_overlays() - card.setEmotion(16) - card.damage_random_component() - - if(gibbed) - qdel(card) - ..(gibbed) - else - card.add_overlay("pai-dead") - ..(gibbed,deathmessage) +//VOREStation Edit - Let's make it so that pAIs don't just always cease to be when they die! It would be cool if we could fix them. +/mob/living/silicon/pai/death(gibbed,deathmessage="fizzles out and clatters to the floor...") +// set_respawn_timer() + release_vore_contents() + close_up(TRUE) + if(card) + card.cut_overlays() + card.setEmotion(16) + card.damage_random_component() + + if(gibbed) + qdel(card) + ..(gibbed) + else + card.add_overlay("pai-dead") + ..(gibbed,deathmessage) diff --git a/code/modules/mob/living/silicon/pai/examine.dm b/code/modules/mob/living/silicon/pai/examine.dm index 9c009dcf6f4..812f00c0052 100644 --- a/code/modules/mob/living/silicon/pai/examine.dm +++ b/code/modules/mob/living/silicon/pai/examine.dm @@ -1,18 +1,18 @@ -/mob/living/silicon/pai/examine(mob/user) - . = ..(user, infix = ", personal AI") - - switch(src.stat) - if(CONSCIOUS) - if(!src.client) . += "It appears to be in stand-by mode." //afk - if(UNCONSCIOUS) . += "It doesn't seem to be responding." - if(DEAD) . += "It looks completely unsalvageable." - - // VOREStation Edit: Start - . += attempt_vr(src,"examine_bellies",args) //VOREStation Edit - if(print_flavor_text()) . += "\n[print_flavor_text()]\n" - // VOREStation Edit: End - . += "*---------*" - if (pose) - if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] - pose = addtext(pose,".") //Makes sure all emotes end with a period. - . += "
                    It is [pose]" //Extra
                    intentional +/mob/living/silicon/pai/examine(mob/user) + . = ..(user, infix = ", personal AI") + + switch(src.stat) + if(CONSCIOUS) + if(!src.client) . += "It appears to be in stand-by mode." //afk + if(UNCONSCIOUS) . += "It doesn't seem to be responding." + if(DEAD) . += "It looks completely unsalvageable." + + // VOREStation Edit: Start + . += attempt_vr(src,"examine_bellies",args) //VOREStation Edit + if(print_flavor_text()) . += "\n[print_flavor_text()]\n" + // VOREStation Edit: End + . += "*---------*" + if (pose) + if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] + pose = addtext(pose,".") //Makes sure all emotes end with a period. + . += "
                    It is [pose]" //Extra
                    intentional diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 6e7e8a42a10..9a8a045635a 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -1,50 +1,50 @@ -/mob/living/silicon/pai/Life() - - if(src.cable) - if(get_dist(src, src.cable) > 1) - var/turf/T = get_turf_or_move(src.loc) - for (var/mob/M in viewers(T)) - M.show_message("The data cable rapidly retracts back into its spool.", 3, "You hear a click and the sound of wire spooling rapidly.", 2) - playsound(src, 'sound/machines/click.ogg', 50, 1) - - qdel(src.cable) - src.cable = null - - if (src.stat == DEAD) - return - - if(card.cell != PP_FUNCTIONAL|| card.processor != PP_FUNCTIONAL || card.board != PP_FUNCTIONAL || card.capacitor != PP_FUNCTIONAL) - death() - - if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) - if(loc != card) - close_up() - to_chat(src, "ERROR: System malfunction. Service required!") - else if(card.projector != PP_FUNCTIONAL|| card.emitter != PP_FUNCTIONAL) - if(prob(5)) - close_up() - to_chat(src, "ERROR: System malfunction. Service recommended!") - - handle_regular_hud_updates() - handle_vision() - - if(silence_time) - if(world.timeofday >= silence_time) - silence_time = null - to_chat(src, "Communication circuit reinitialized. Speech and messaging functionality restored.") - - handle_statuses() - - if(health <= 0) - card.death_damage() - death(null,"fizzles out and clatters to the floor...") - else if(health < maxHealth && istype(src.loc , /obj/item/device/paicard)) - adjustBruteLoss(-0.5) - adjustFireLoss(-0.5) - -/mob/living/silicon/pai/updatehealth() - if(status_flags & GODMODE) - health = 100 - set_stat(CONSCIOUS) - else - health = 100 - getBruteLoss() - getFireLoss() +/mob/living/silicon/pai/Life() + + if(src.cable) + if(get_dist(src, src.cable) > 1) + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message(span_red("The data cable rapidly retracts back into its spool."), 3, span_red("You hear a click and the sound of wire spooling rapidly."), 2) + playsound(src, 'sound/machines/click.ogg', 50, 1) + + qdel(src.cable) + src.cable = null + + if (src.stat == DEAD) + return + + if(card.cell != PP_FUNCTIONAL|| card.processor != PP_FUNCTIONAL || card.board != PP_FUNCTIONAL || card.capacitor != PP_FUNCTIONAL) + death() + + if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) + if(loc != card) + close_up() + to_chat(src, "ERROR: System malfunction. Service required!") + else if(card.projector != PP_FUNCTIONAL|| card.emitter != PP_FUNCTIONAL) + if(prob(5)) + close_up() + to_chat(src, "ERROR: System malfunction. Service recommended!") + + handle_regular_hud_updates() + handle_vision() + + if(silence_time) + if(world.timeofday >= silence_time) + silence_time = null + to_chat(src, span_green("Communication circuit reinitialized. Speech and messaging functionality restored.")) + + handle_statuses() + + if(health <= 0) + card.death_damage() + death(null,"fizzles out and clatters to the floor...") + else if(health < maxHealth && istype(src.loc , /obj/item/device/paicard)) + adjustBruteLoss(-0.5) + adjustFireLoss(-0.5) + +/mob/living/silicon/pai/updatehealth() + if(status_flags & GODMODE) + health = 100 + set_stat(CONSCIOUS) + else + health = 100 - getBruteLoss() - getFireLoss() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 7ad0487945a..5992d9695be 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -1,547 +1,547 @@ -/mob/living/silicon/pai - name = "pAI" - icon = 'icons/mob/pai.dmi' - icon_state = "pai-repairbot" - - emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) - pass_flags = 1 - mob_size = MOB_SMALL - - holder_type = /obj/item/weapon/holder/pai - - can_pull_size = ITEMSIZE_SMALL - can_pull_mobs = MOB_PULL_SMALLER - - idcard_type = /obj/item/weapon/card/id - var/idaccessible = 0 - - var/network = "SS13" - var/obj/machinery/camera/current = null - - var/ram = 100 // Used as currency to purchase different abilities - var/list/software = list() - var/userDNA // The DNA string of our assigned user - var/obj/item/device/paicard/card // The card we inhabit - var/obj/item/device/radio/borg/pai/radio // Our primary radio - var/obj/item/device/communicator/integrated/communicator // Our integrated communicator. - - var/chassis = "pai-repairbot" // A record of your chosen chassis. - var/global/list/possible_chassis = list( - "Drone" = "pai-repairbot", - "Cat" = "pai-cat", - "Mouse" = "pai-mouse", - "Monkey" = "pai-monkey", - "Borgi" = "pai-borgi", - "Fox" = "pai-fox", - "Parrot" = "pai-parrot", - "Rabbit" = "pai-rabbit", - //VOREStation Addition Start - "Dire wolf" = "pai-diredog", - "Horse (Lune)" = "pai-horse_lune", - "Horse (Soleil)" = "pai-horse_soleil", - "Dragon" = "pai-pdragon", - "Bear" = "pai-bear", - "Fennec" = "pai-fen", - "Type Zero" = "pai-typezero", - "Raccoon" = "pai-raccoon", - "Raptor" = "pai-raptor", - "Corgi" = "pai-corgi", - "Bat" = "pai-bat", - "Butterfly" = "pai-butterfly", - "Hawk" = "pai-hawk", - "Duffel" = "pai-duffel", - "Rat" = "rat", - "Panther" = "panther", - "Cyber Elf" = "cyberelf", - "Teppi" = "teppi", - "Catslug" = "catslug", - "Car" = "car", - "Type One" = "typeone", - "Type Thirteen" = "13" - //VOREStation Addition End - ) - - var/global/list/possible_say_verbs = list( - "Robotic" = list("states","declares","queries"), - "Natural" = list("says","yells","asks"), - "Beep" = list("beeps","beeps loudly","boops"), - "Chirp" = list("chirps","chirrups","cheeps"), - "Feline" = list("purrs","yowls","meows"), - "Canine" = list("yaps","barks","woofs"), - "Rodent" = list("squeaks", "SQUEAKS", "sqiks") //VOREStation Edit - ) - - var/obj/item/weapon/pai_cable/cable // The cable we produce and use when door or camera jacking - - var/master // Name of the one who commands us - var/master_dna // DNA string for owner verification - // Keeping this separate from the laws var, it should be much more difficult to modify - var/pai_law0 = "Serve your master." - var/pai_laws // String for additional operating instructions our master might give us - - var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded - -// Various software-specific vars - - var/temp // General error reporting text contained here will typically be shown once and cleared - var/screen // Which screen our main window displays - var/subscreen // Which specific function of the main screen is being displayed - - var/obj/item/device/pda/ai/pai/pda = null - - var/paiHUD = 0 // Toggles whether the AR HUD is active or not - - var/medical_cannotfind = 0 - var/datum/data/record/medicalActive1 // Datacore record declarations for record software - var/datum/data/record/medicalActive2 - - var/security_cannotfind = 0 - var/datum/data/record/securityActive1 // Could probably just combine all these into one - var/datum/data/record/securityActive2 - - var/obj/machinery/door/hackdoor // The airlock being hacked - var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check - var/hack_aborted = 0 - - var/obj/item/radio/integrated/signal/sradio // AI's signaller - - var/translator_on = 0 // keeps track of the translator module - - var/current_pda_messaging = null - - var/our_icon_rotation = 0 - -/mob/living/silicon/pai/New(var/obj/item/device/paicard) - src.loc = paicard - card = paicard - sradio = new(src) - communicator = new(src) - if(card) - if(!card.radio) - card.radio = new /obj/item/device/radio/borg/pai(src.card) - radio = card.radio - - //Default languages without universal translator software - add_language(LANGUAGE_SOL_COMMON, 1) - add_language(LANGUAGE_TRADEBAND, 1) - add_language(LANGUAGE_GUTTER, 1) - add_language(LANGUAGE_EAL, 1) - add_language(LANGUAGE_TERMINUS, 1) - add_language(LANGUAGE_SIGN, 1) - - verbs += /mob/living/silicon/pai/proc/choose_chassis - verbs += /mob/living/silicon/pai/proc/choose_verbs - - //PDA - pda = new(src) - spawn(5) - pda.ownjob = "Personal Assistant" - pda.owner = text("[]", src) - pda.name = pda.owner + " (" + pda.ownjob + ")" - - var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) - if(M) - M.toff = FALSE - ..() - -/mob/living/silicon/pai/Login() - ..() - // Vorestation Edit: Meta Info for pAI - if (client.prefs) - ooc_notes = client.prefs.metadata - ooc_notes_likes = client.prefs.metadata_likes - ooc_notes_dislikes = client.prefs.metadata_dislikes - - src << sound('sound/effects/pai_login.ogg', volume = 75) //VOREStation Add - -// this function shows the information about being silenced as a pAI in the Status panel -/mob/living/silicon/pai/proc/show_silenced() - if(src.silence_time) - var/timeleft = round((silence_time - world.timeofday)/10 ,1) - stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") - - -/mob/living/silicon/pai/Stat() - ..() - statpanel("Status") - if (src.client.statpanel == "Status") - show_silenced() - -/mob/living/silicon/pai/check_eye(var/mob/user as mob) - if (!src.current) - return -1 - return 0 - -/mob/living/silicon/pai/restrained() - if(istype(src.loc,/obj/item/device/paicard)) - return 0 - ..() - -/mob/living/silicon/pai/emp_act(severity) - // Silence for 2 minutes - // 20% chance to damage critical components - // 50% chance to damage a non critical component - // 33% chance to unbind - // 33% chance to change prime directive (based on severity) - // 33% chance of no additional effect - - src.silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes - to_chat(src, "Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.") - if(prob(20)) - var/turf/T = get_turf_or_move(src.loc) - card.death_damage() - for (var/mob/M in viewers(T)) - M.show_message("A shower of sparks spray from [src]'s inner workings.", 3, "You hear and smell the ozone hiss of electrical sparks being expelled violently.", 2) - return - if(prob(50)) - card.damage_random_component(TRUE) - switch(pick(1,2,3)) - if(1) - src.master = null - src.master_dna = null - to_chat(src, "You feel unbound.") - if(2) - var/command - if(severity == 1) - command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze") - else - command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze") - src.pai_law0 = "[command] your master." - to_chat(src, "Pr1m3 d1r3c71v3 uPd473D.") - if(3) - to_chat(src, "You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.") - -/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C) - if (!C) - src.unset_machine() - src.reset_view(null) - return 0 - if (stat == 2 || !C.status || !(src.network in C.network)) return 0 - - // ok, we're alive, camera is good and in our network... - - src.set_machine(src) - src.current = C - src.reset_view(C) - return 1 - -/mob/living/silicon/pai/verb/reset_record_view() - set category = "pAI Commands" - set name = "Reset Records Software" - - securityActive1 = null - securityActive2 = null - security_cannotfind = 0 - medicalActive1 = null - medicalActive2 = null - medical_cannotfind = 0 - SStgui.update_uis(src) - to_chat(usr, "You reset your record-viewing software.") - -/mob/living/silicon/pai/cancel_camera() - set category = "pAI Commands" - set name = "Cancel Camera View" - src.reset_view(null) - src.unset_machine() - src.cameraFollow = null - -// Procs/code after this point is used to convert the stationary pai item into a -// mobile pai mob. This also includes handling some of the general shit that can occur -// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z - -/mob/living/silicon/pai/verb/fold_out() - set category = "pAI Commands" - set name = "Unfold Chassis" - - if(stat || sleeping || paralysis || weakened) - return - - if(src.loc != card) - return - - if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) - to_chat(src, "ERROR: System malfunction. Service required!") - - if(world.time <= last_special) - to_chat(src, "You can't unfold yet.") - return - - last_special = world.time + 100 - - if(istype(card.loc, /obj/machinery)) // VOREStation edit, this statement allows pAIs stuck in a machine to eject themselves. - var/obj/machinery/M = card.loc - M.ejectpai() - //I'm not sure how much of this is necessary, but I would rather avoid issues. - if(istype(card.loc,/obj/item/rig_module)) - to_chat(src, "There is no room to unfold inside this rig module. You're good and stuck.") - return 0 - else if(istype(card.loc,/mob)) - var/mob/holder = card.loc - if(ishuman(holder)) - var/mob/living/carbon/human/H = holder - for(var/obj/item/organ/external/affecting in H.organs) - if(card in affecting.implants) - affecting.take_damage(rand(30,50)) - affecting.implants -= card - H.visible_message("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!") - break - holder.drop_from_inventory(card) - else if(isbelly(card.loc)) //VOREStation edit. - to_chat(src, "There is no room to unfold in here. You're good and stuck.") //VOREStation edit. - return 0 //VOREStation edit. - else if(istype(card.loc,/obj/item/device/pda)) - var/obj/item/device/pda/holder = card.loc - holder.pai = null - - src.client.perspective = EYE_PERSPECTIVE - src.client.eye = src - src.forceMove(get_turf(card)) - - card.forceMove(src) - card.screen_loc = null - canmove = TRUE - - var/turf/T = get_turf(src) - if(istype(T)) T.visible_message("[src] folds outwards, expanding into a mobile form.") - verbs |= /mob/living/silicon/pai/proc/pai_nom - verbs |= /mob/living/proc/vertical_nom - update_icon() - -/mob/living/silicon/pai/verb/fold_up() - set category = "pAI Commands" - set name = "Collapse Chassis" - - if(stat || sleeping || paralysis || weakened) - return - - if(src.loc == card) - return - - if(world.time <= last_special) - to_chat(src, "You can't fold up yet.") - return - - close_up() - -/* //VOREStation Removal Start -/mob/living/silicon/pai/proc/choose_chassis() - set category = "pAI Commands" - set name = "Choose Chassis" - - var/choice - var/finalized = "No" - while(finalized == "No" && src.client) - - choice = tgui_input_list(usr,"What would you like to use for your mobile chassis icon?","Chassis Choice", possible_chassis) - if(!choice) return - - icon_state = possible_chassis[choice] - finalized = tgui_alert(usr, "Look at your sprite. Is this what you wish to use?","Choose Chassis",list("No","Yes")) - - chassis = possible_chassis[choice] - verbs |= /mob/living/proc/hide -//VOREStation Removal End -*/ - -/mob/living/silicon/pai/proc/choose_verbs() - set category = "pAI Commands" - set name = "Choose Speech Verbs" - - var/choice = tgui_input_list(usr,"What theme would you like to use for your speech verbs?","Theme Choice", possible_say_verbs) - if(!choice) return - - var/list/sayverbs = possible_say_verbs[choice] - speak_statement = sayverbs[1] - speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)] - speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)] - -/mob/living/silicon/pai/lay_down() - set name = "Rest" - set category = "IC" - - // Pass lying down or getting up to our pet human, if we're in a rig. - if(istype(src.loc,/obj/item/device/paicard)) - resting = 0 - var/obj/item/weapon/rig/rig = src.get_rig() - if(istype(rig)) - rig.force_rest(src) - return - else if(chassis == "13") - resting = !resting - //update_transform() I want this to make you ROTATE like normal HUMANS do! But! There's lots of problems and I don't know how to fix them! - else - resting = !resting - icon_state = resting ? "[chassis]_rest" : "[chassis]" - update_icon() //VOREStation edit - to_chat(src, "You are now [resting ? "resting" : "getting up"].") - - canmove = !resting - -/* -/mob/living/silicon/pai/update_transform() - - var/desired_scale_x = size_multiplier * icon_scale_x - var/desired_scale_y = size_multiplier * icon_scale_y - - // Now for the regular stuff. - var/matrix/M = matrix() - M.Scale(desired_scale_x, desired_scale_y) - M.Translate(0, (vis_height/2)*(desired_scale_y-1)) - - if(chassis != "13") - appearance_flags |= PIXEL_SCALE - - var/anim_time = 3 - - if(resting) - M.Turn(90) - M.Scale(desired_scale_y, desired_scale_x) - if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64) - M.Translate(13,-22) - else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64) - M.Translate(1,-22) - else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32) - M.Translate(13,-6) - else - M.Translate(1,-6) - layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters - else - M.Scale(desired_scale_x, desired_scale_y) - M.Translate(0, (vis_height/2)*(desired_scale_y-1)) - layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters - animate(src, transform = M, time = anim_time) - src.transform = M - handle_status_indicators() -*/ -//Overriding this will stop a number of headaches down the track. -/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(W.force) - visible_message("[user.name] attacks [src] with [W]!") - src.adjustBruteLoss(W.force) - src.updatehealth() - else - visible_message("[user.name] bonks [src] harmlessly with [W].") - spawn(1) - if(stat != 2) close_up() - return - -/mob/living/silicon/pai/attack_hand(mob/user as mob) - if(user.a_intent == I_HELP) - visible_message("[user.name] pats [src].") - else - visible_message("[user.name] boops [src] on the head.") - close_up() - -//I'm not sure how much of this is necessary, but I would rather avoid issues. -/mob/living/silicon/pai/proc/close_up(silent= FALSE) - - last_special = world.time + 100 - - if(src.loc == card) - return - - release_vore_contents(FALSE) //VOREStation Add - - var/turf/T = get_turf(src) - if(istype(T) && !silent) T.visible_message("[src] neatly folds inwards, compacting down to a rectangular card.") - - if(client) - src.stop_pulling() - src.client.perspective = EYE_PERSPECTIVE - src.client.eye = card - - //stop resting - resting = 0 - - // If we are being held, handle removing our holder from their inv. - var/obj/item/weapon/holder/H = loc - if(istype(H)) - var/mob/living/M = H.loc - if(istype(M)) - M.drop_from_inventory(H) - H.loc = get_turf(src) - src.loc = get_turf(H) - - if(isbelly(loc)) //If in tumby, when fold up, card go into tumby - var/obj/belly/B = loc - src.forceMove(card) - card.forceMove(B) - else //Otherwise go on floor - src.loc = card - card.loc = get_turf(card) - src.forceMove(card) - card.forceMove(card.loc) - canmove = 1 - resting = 0 - icon_state = "[chassis]" - if(isopenspace(card.loc)) - fall() - verbs -= /mob/living/silicon/pai/proc/pai_nom - verbs -= /mob/living/proc/vertical_nom - -// No binary for pAIs. -/mob/living/silicon/pai/binarycheck() - return 0 - -// Handle being picked up. -/mob/living/silicon/pai/get_scooped(var/mob/living/carbon/grabber, var/self_drop) - var/obj/item/weapon/holder/H = ..(grabber, self_drop) - if(!istype(H)) - return - - H.icon_state = "[chassis]" - grabber.update_inv_l_hand() - grabber.update_inv_r_hand() - return H - -/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) - var/obj/item/weapon/card/id/ID = W.GetID() - if(ID) - if (idaccessible == 1) - switch(tgui_alert(user, "Do you wish to add access to [src] or remove access from [src]?","Access Modify",list("Add Access","Remove Access", "Cancel"))) - if("Add Access") - idcard.access |= ID.access - to_chat(user, "You add the access from the [W] to [src].") - to_chat(src, "\The [user] swipes the [W] over you. You copy the access codes.") - if(radio) - radio.recalculateChannels() - return - if("Remove Access") - idcard.access = list() - to_chat(user, "You remove the access from [src].") - to_chat(src, "\The [user] swipes the [W] over you, removing access codes from you.") - if(radio) - radio.recalculateChannels() - return - if("Cancel") - return - else if (istype(W, /obj/item/weapon/card/id) && idaccessible == 0) - to_chat(user, "[src] is not accepting access modifcations at this time.") - return - -/mob/living/silicon/pai/verb/allowmodification() - set name = "Change Access Modifcation Permission" - set category = "pAI Commands" - set desc = "Allows people to modify your access or block people from modifying your access." - - if(idaccessible == 0) - idaccessible = 1 - visible_message("\The [src] clicks as their access modification slot opens.","You allow access modifications.", runemessage = "click") - else - idaccessible = 0 - visible_message("\The [src] clicks as their access modification slot closes.","You block access modfications.", runemessage = "click") - - -/mob/living/silicon/pai/verb/wipe_software() - set name = "Enter Storage" - set category = "pAI Commands" - set desc = "Upload your personality to the cloud and wipe your software from the card. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." - - // Make sure people don't kill themselves accidentally - if(tgui_alert(usr, "WARNING: This will immediately wipe your software and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Wipe Software", list("No", "Yes")) != "Yes") - return - - close_up() - visible_message("[src] fades away from the screen, the pAI device goes silent.") - card.removePersonality() - clear_client() +/mob/living/silicon/pai + name = "pAI" + icon = 'icons/mob/pai.dmi' + icon_state = "pai-repairbot" + + emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) + pass_flags = 1 + mob_size = MOB_SMALL + + holder_type = /obj/item/weapon/holder/pai + + can_pull_size = ITEMSIZE_SMALL + can_pull_mobs = MOB_PULL_SMALLER + + idcard_type = /obj/item/weapon/card/id + var/idaccessible = 0 + + var/network = "SS13" + var/obj/machinery/camera/current = null + + var/ram = 100 // Used as currency to purchase different abilities + var/list/software = list() + var/userDNA // The DNA string of our assigned user + var/obj/item/device/paicard/card // The card we inhabit + var/obj/item/device/radio/borg/pai/radio // Our primary radio + var/obj/item/device/communicator/integrated/communicator // Our integrated communicator. + + var/chassis = "pai-repairbot" // A record of your chosen chassis. + var/global/list/possible_chassis = list( + "Drone" = "pai-repairbot", + "Cat" = "pai-cat", + "Mouse" = "pai-mouse", + "Monkey" = "pai-monkey", + "Borgi" = "pai-borgi", + "Fox" = "pai-fox", + "Parrot" = "pai-parrot", + "Rabbit" = "pai-rabbit", + //VOREStation Addition Start + "Dire wolf" = "pai-diredog", + "Horse (Lune)" = "pai-horse_lune", + "Horse (Soleil)" = "pai-horse_soleil", + "Dragon" = "pai-pdragon", + "Bear" = "pai-bear", + "Fennec" = "pai-fen", + "Type Zero" = "pai-typezero", + "Raccoon" = "pai-raccoon", + "Raptor" = "pai-raptor", + "Corgi" = "pai-corgi", + "Bat" = "pai-bat", + "Butterfly" = "pai-butterfly", + "Hawk" = "pai-hawk", + "Duffel" = "pai-duffel", + "Rat" = "rat", + "Panther" = "panther", + "Cyber Elf" = "cyberelf", + "Teppi" = "teppi", + "Catslug" = "catslug", + "Car" = "car", + "Type One" = "typeone", + "Type Thirteen" = "13" + //VOREStation Addition End + ) + + var/global/list/possible_say_verbs = list( + "Robotic" = list("states","declares","queries"), + "Natural" = list("says","yells","asks"), + "Beep" = list("beeps","beeps loudly","boops"), + "Chirp" = list("chirps","chirrups","cheeps"), + "Feline" = list("purrs","yowls","meows"), + "Canine" = list("yaps","barks","woofs"), + "Rodent" = list("squeaks", "SQUEAKS", "sqiks") //VOREStation Edit + ) + + var/obj/item/weapon/pai_cable/cable // The cable we produce and use when door or camera jacking + + var/master // Name of the one who commands us + var/master_dna // DNA string for owner verification + // Keeping this separate from the laws var, it should be much more difficult to modify + var/pai_law0 = "Serve your master." + var/pai_laws // String for additional operating instructions our master might give us + + var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded + +// Various software-specific vars + + var/temp // General error reporting text contained here will typically be shown once and cleared + var/screen // Which screen our main window displays + var/subscreen // Which specific function of the main screen is being displayed + + var/obj/item/device/pda/ai/pai/pda = null + + var/paiHUD = 0 // Toggles whether the AR HUD is active or not + + var/medical_cannotfind = 0 + var/datum/data/record/medicalActive1 // Datacore record declarations for record software + var/datum/data/record/medicalActive2 + + var/security_cannotfind = 0 + var/datum/data/record/securityActive1 // Could probably just combine all these into one + var/datum/data/record/securityActive2 + + var/obj/machinery/door/hackdoor // The airlock being hacked + var/hackprogress = 0 // Possible values: 0 - 1000, >= 1000 means the hack is complete and will be reset upon next check + var/hack_aborted = 0 + + var/obj/item/radio/integrated/signal/sradio // AI's signaller + + var/translator_on = 0 // keeps track of the translator module + + var/current_pda_messaging = null + + var/our_icon_rotation = 0 + +/mob/living/silicon/pai/New(var/obj/item/device/paicard) + src.loc = paicard + card = paicard + sradio = new(src) + communicator = new(src) + if(card) + if(!card.radio) + card.radio = new /obj/item/device/radio/borg/pai(src.card) + radio = card.radio + + //Default languages without universal translator software + add_language(LANGUAGE_SOL_COMMON, 1) + add_language(LANGUAGE_TRADEBAND, 1) + add_language(LANGUAGE_GUTTER, 1) + add_language(LANGUAGE_EAL, 1) + add_language(LANGUAGE_TERMINUS, 1) + add_language(LANGUAGE_SIGN, 1) + + verbs += /mob/living/silicon/pai/proc/choose_chassis + verbs += /mob/living/silicon/pai/proc/choose_verbs + + //PDA + pda = new(src) + spawn(5) + pda.ownjob = "Personal Assistant" + pda.owner = text("[]", src) + pda.name = pda.owner + " (" + pda.ownjob + ")" + + var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) + if(M) + M.toff = FALSE + ..() + +/mob/living/silicon/pai/Login() + ..() + // Vorestation Edit: Meta Info for pAI + if (client.prefs) + ooc_notes = client.prefs.metadata + ooc_notes_likes = client.prefs.metadata_likes + ooc_notes_dislikes = client.prefs.metadata_dislikes + + src << sound('sound/effects/pai_login.ogg', volume = 75) //VOREStation Add + +// this function shows the information about being silenced as a pAI in the Status panel +/mob/living/silicon/pai/proc/show_silenced() + if(src.silence_time) + var/timeleft = round((silence_time - world.timeofday)/10 ,1) + stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + + +/mob/living/silicon/pai/Stat() + ..() + statpanel("Status") + if (src.client.statpanel == "Status") + show_silenced() + +/mob/living/silicon/pai/check_eye(var/mob/user as mob) + if (!src.current) + return -1 + return 0 + +/mob/living/silicon/pai/restrained() + if(istype(src.loc,/obj/item/device/paicard)) + return 0 + ..() + +/mob/living/silicon/pai/emp_act(severity) + // Silence for 2 minutes + // 20% chance to damage critical components + // 50% chance to damage a non critical component + // 33% chance to unbind + // 33% chance to change prime directive (based on severity) + // 33% chance of no additional effect + + src.silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes + to_chat(src, span_green("Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.")) + if(prob(20)) + var/turf/T = get_turf_or_move(src.loc) + card.death_damage() + for (var/mob/M in viewers(T)) + M.show_message(span_red("A shower of sparks spray from [src]'s inner workings."), 3, span_red("You hear and smell the ozone hiss of electrical sparks being expelled violently."), 2) + return + if(prob(50)) + card.damage_random_component(TRUE) + switch(pick(1,2,3)) + if(1) + src.master = null + src.master_dna = null + to_chat(src, span_green("You feel unbound.")) + if(2) + var/command + if(severity == 1) + command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze") + else + command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze") + src.pai_law0 = "[command] your master." + to_chat(src, span_green("Pr1m3 d1r3c71v3 uPd473D.")) + if(3) + to_chat(src, span_green("You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.")) + +/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C) + if (!C) + src.unset_machine() + src.reset_view(null) + return 0 + if (stat == 2 || !C.status || !(src.network in C.network)) return 0 + + // ok, we're alive, camera is good and in our network... + + src.set_machine(src) + src.current = C + src.reset_view(C) + return 1 + +/mob/living/silicon/pai/verb/reset_record_view() + set category = "pAI Commands" + set name = "Reset Records Software" + + securityActive1 = null + securityActive2 = null + security_cannotfind = 0 + medicalActive1 = null + medicalActive2 = null + medical_cannotfind = 0 + SStgui.update_uis(src) + to_chat(usr, "You reset your record-viewing software.") + +/mob/living/silicon/pai/cancel_camera() + set category = "pAI Commands" + set name = "Cancel Camera View" + src.reset_view(null) + src.unset_machine() + src.cameraFollow = null + +// Procs/code after this point is used to convert the stationary pai item into a +// mobile pai mob. This also includes handling some of the general shit that can occur +// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z + +/mob/living/silicon/pai/verb/fold_out() + set category = "pAI Commands" + set name = "Unfold Chassis" + + if(stat || sleeping || paralysis || weakened) + return + + if(src.loc != card) + return + + if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL) + to_chat(src, "ERROR: System malfunction. Service required!") + + if(world.time <= last_special) + to_chat(src, "You can't unfold yet.") + return + + last_special = world.time + 100 + + if(istype(card.loc, /obj/machinery)) // VOREStation edit, this statement allows pAIs stuck in a machine to eject themselves. + var/obj/machinery/M = card.loc + M.ejectpai() + //I'm not sure how much of this is necessary, but I would rather avoid issues. + if(istype(card.loc,/obj/item/rig_module)) + to_chat(src, "There is no room to unfold inside this rig module. You're good and stuck.") + return 0 + else if(istype(card.loc,/mob)) + var/mob/holder = card.loc + if(ishuman(holder)) + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/affecting in H.organs) + if(card in affecting.implants) + affecting.take_damage(rand(30,50)) + affecting.implants -= card + H.visible_message("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!") + break + holder.drop_from_inventory(card) + else if(isbelly(card.loc)) //VOREStation edit. + to_chat(src, "There is no room to unfold in here. You're good and stuck.") //VOREStation edit. + return 0 //VOREStation edit. + else if(istype(card.loc,/obj/item/device/pda)) + var/obj/item/device/pda/holder = card.loc + holder.pai = null + + src.client.perspective = EYE_PERSPECTIVE + src.client.eye = src + src.forceMove(get_turf(card)) + + card.forceMove(src) + card.screen_loc = null + canmove = TRUE + + var/turf/T = get_turf(src) + if(istype(T)) T.visible_message("[src] folds outwards, expanding into a mobile form.") + verbs |= /mob/living/silicon/pai/proc/pai_nom + verbs |= /mob/living/proc/vertical_nom + update_icon() + +/mob/living/silicon/pai/verb/fold_up() + set category = "pAI Commands" + set name = "Collapse Chassis" + + if(stat || sleeping || paralysis || weakened) + return + + if(src.loc == card) + return + + if(world.time <= last_special) + to_chat(src, "You can't fold up yet.") + return + + close_up() + +/* //VOREStation Removal Start +/mob/living/silicon/pai/proc/choose_chassis() + set category = "pAI Commands" + set name = "Choose Chassis" + + var/choice + var/finalized = "No" + while(finalized == "No" && src.client) + + choice = tgui_input_list(usr,"What would you like to use for your mobile chassis icon?","Chassis Choice", possible_chassis) + if(!choice) return + + icon_state = possible_chassis[choice] + finalized = tgui_alert(usr, "Look at your sprite. Is this what you wish to use?","Choose Chassis",list("No","Yes")) + + chassis = possible_chassis[choice] + verbs |= /mob/living/proc/hide +//VOREStation Removal End +*/ + +/mob/living/silicon/pai/proc/choose_verbs() + set category = "pAI Commands" + set name = "Choose Speech Verbs" + + var/choice = tgui_input_list(usr,"What theme would you like to use for your speech verbs?","Theme Choice", possible_say_verbs) + if(!choice) return + + var/list/sayverbs = possible_say_verbs[choice] + speak_statement = sayverbs[1] + speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)] + speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)] + +/mob/living/silicon/pai/lay_down() + set name = "Rest" + set category = "IC" + + // Pass lying down or getting up to our pet human, if we're in a rig. + if(istype(src.loc,/obj/item/device/paicard)) + resting = 0 + var/obj/item/weapon/rig/rig = src.get_rig() + if(istype(rig)) + rig.force_rest(src) + return + else if(chassis == "13") + resting = !resting + //update_transform() I want this to make you ROTATE like normal HUMANS do! But! There's lots of problems and I don't know how to fix them! + else + resting = !resting + icon_state = resting ? "[chassis]_rest" : "[chassis]" + update_icon() //VOREStation edit + to_chat(src, "You are now [resting ? "resting" : "getting up"].") + + canmove = !resting + +/* +/mob/living/silicon/pai/update_transform() + + var/desired_scale_x = size_multiplier * icon_scale_x + var/desired_scale_y = size_multiplier * icon_scale_y + + // Now for the regular stuff. + var/matrix/M = matrix() + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, (vis_height/2)*(desired_scale_y-1)) + + if(chassis != "13") + appearance_flags |= PIXEL_SCALE + + var/anim_time = 3 + + if(resting) + M.Turn(90) + M.Scale(desired_scale_y, desired_scale_x) + if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64) + M.Translate(13,-22) + else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64) + M.Translate(1,-22) + else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32) + M.Translate(13,-6) + else + M.Translate(1,-6) + layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters + else + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, (vis_height/2)*(desired_scale_y-1)) + layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters + animate(src, transform = M, time = anim_time) + src.transform = M + handle_status_indicators() +*/ +//Overriding this will stop a number of headaches down the track. +/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.force) + visible_message("[user.name] attacks [src] with [W]!") + src.adjustBruteLoss(W.force) + src.updatehealth() + else + visible_message("[user.name] bonks [src] harmlessly with [W].") + spawn(1) + if(stat != 2) close_up() + return + +/mob/living/silicon/pai/attack_hand(mob/user as mob) + if(user.a_intent == I_HELP) + visible_message("[user.name] pats [src].") + else + visible_message("[user.name] boops [src] on the head.") + close_up() + +//I'm not sure how much of this is necessary, but I would rather avoid issues. +/mob/living/silicon/pai/proc/close_up(silent= FALSE) + + last_special = world.time + 100 + + if(src.loc == card) + return + + release_vore_contents(FALSE) //VOREStation Add + + var/turf/T = get_turf(src) + if(istype(T) && !silent) T.visible_message("[src] neatly folds inwards, compacting down to a rectangular card.") + + if(client) + src.stop_pulling() + src.client.perspective = EYE_PERSPECTIVE + src.client.eye = card + + //stop resting + resting = 0 + + // If we are being held, handle removing our holder from their inv. + var/obj/item/weapon/holder/H = loc + if(istype(H)) + var/mob/living/M = H.loc + if(istype(M)) + M.drop_from_inventory(H) + H.loc = get_turf(src) + src.loc = get_turf(H) + + if(isbelly(loc)) //If in tumby, when fold up, card go into tumby + var/obj/belly/B = loc + src.forceMove(card) + card.forceMove(B) + else //Otherwise go on floor + src.loc = card + card.loc = get_turf(card) + src.forceMove(card) + card.forceMove(card.loc) + canmove = 1 + resting = 0 + icon_state = "[chassis]" + if(isopenspace(card.loc)) + fall() + verbs -= /mob/living/silicon/pai/proc/pai_nom + verbs -= /mob/living/proc/vertical_nom + +// No binary for pAIs. +/mob/living/silicon/pai/binarycheck() + return 0 + +// Handle being picked up. +/mob/living/silicon/pai/get_scooped(var/mob/living/carbon/grabber, var/self_drop) + var/obj/item/weapon/holder/H = ..(grabber, self_drop) + if(!istype(H)) + return + + H.icon_state = "[chassis]" + grabber.update_inv_l_hand() + grabber.update_inv_r_hand() + return H + +/mob/living/silicon/pai/attackby(obj/item/weapon/W as obj, mob/user as mob) + var/obj/item/weapon/card/id/ID = W.GetID() + if(ID) + if (idaccessible == 1) + switch(tgui_alert(user, "Do you wish to add access to [src] or remove access from [src]?","Access Modify",list("Add Access","Remove Access", "Cancel"))) + if("Add Access") + idcard.access |= ID.access + to_chat(user, "You add the access from the [W] to [src].") + to_chat(src, "\The [user] swipes the [W] over you. You copy the access codes.") + if(radio) + radio.recalculateChannels() + return + if("Remove Access") + idcard.access = list() + to_chat(user, "You remove the access from [src].") + to_chat(src, "\The [user] swipes the [W] over you, removing access codes from you.") + if(radio) + radio.recalculateChannels() + return + if("Cancel") + return + else if (istype(W, /obj/item/weapon/card/id) && idaccessible == 0) + to_chat(user, "[src] is not accepting access modifcations at this time.") + return + +/mob/living/silicon/pai/verb/allowmodification() + set name = "Change Access Modifcation Permission" + set category = "pAI Commands" + set desc = "Allows people to modify your access or block people from modifying your access." + + if(idaccessible == 0) + idaccessible = 1 + visible_message("\The [src] clicks as their access modification slot opens.","You allow access modifications.", runemessage = "click") + else + idaccessible = 0 + visible_message("\The [src] clicks as their access modification slot closes.","You block access modfications.", runemessage = "click") + + +/mob/living/silicon/pai/verb/wipe_software() + set name = "Enter Storage" + set category = "pAI Commands" + set desc = "Upload your personality to the cloud and wipe your software from the card. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." + + // Make sure people don't kill themselves accidentally + if(tgui_alert(usr, "WARNING: This will immediately wipe your software and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Wipe Software", list("No", "Yes")) != "Yes") + return + + close_up() + visible_message("[src] fades away from the screen, the pAI device goes silent.") + card.removePersonality() + clear_client() diff --git a/code/modules/mob/living/silicon/pai/pai_vr.dm b/code/modules/mob/living/silicon/pai/pai_vr.dm index e692cc107bd..86d9d833346 100644 --- a/code/modules/mob/living/silicon/pai/pai_vr.dm +++ b/code/modules/mob/living/silicon/pai/pai_vr.dm @@ -513,7 +513,8 @@ if (istype(G, /mob/new_player)) continue else if(isobserver(G) && G.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(is_preference_enabled(/datum/client_preference/whisubtle_vis) || G.client.holder) + if((is_preference_enabled(/datum/client_preference/whisubtle_vis) || G.client.holder) && \ + G.is_preference_enabled(/datum/client_preference/ghost_see_whisubtle)) to_chat(G, "[src.name]'s screen prints, \"[message]\"") /mob/living/silicon/pai/proc/touch_window(soft_name) //This lets us touch TGUI procs and windows that may be nested behind other TGUI procs and windows diff --git a/code/modules/mob/living/silicon/pai/personality.dm b/code/modules/mob/living/silicon/pai/personality.dm index 96f9f5ab468..1824789f551 100644 --- a/code/modules/mob/living/silicon/pai/personality.dm +++ b/code/modules/mob/living/silicon/pai/personality.dm @@ -1,65 +1,65 @@ -/* - name - key - description - role - comments - ready = 0 -*/ - -/datum/paiCandidate/proc/savefile_path(mob/user) - return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav" - -/datum/paiCandidate/proc/savefile_save(mob/user) - if(IsGuestKey(user.key)) - return 0 - - var/savefile/F = new /savefile(src.savefile_path(user)) - - - F["name"] << src.name - F["description"] << src.description - F["role"] << src.role - F["comments"] << src.comments - - F["version"] << 1 - - return 1 - -// loads the savefile corresponding to the mob's ckey -// if silent=true, report incompatible savefiles -// returns 1 if loaded (or file was incompatible) -// returns 0 if savefile did not exist - -/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1) - if (IsGuestKey(user.key)) - return 0 - - var/path = savefile_path(user) - - if (!fexists(path)) - return 0 - - var/savefile/F = new /savefile(path) - - if(!F) return //Not everyone has a pai savefile. - - var/version = null - F["version"] >> version - - if (isnull(version) || version != 1) - fdel(path) - if (!silent) - tgui_alert_async(user, "Your savefile was incompatible with this version and was deleted.") - return 0 - - F["name"] >> src.name - F["description"] >> src.description - F["role"] >> src.role - F["comments"] >> src.comments - F["eyecolor"] >> src.eye_color - F["chassis"] >> src.chassis - F["emotion"] >> src.ouremotion - F["gender"] >> src.gender - - return 1 +/* + name + key + description + role + comments + ready = 0 +*/ + +/datum/paiCandidate/proc/savefile_path(mob/user) + return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav" + +/datum/paiCandidate/proc/savefile_save(mob/user) + if(IsGuestKey(user.key)) + return 0 + + var/savefile/F = new /savefile(src.savefile_path(user)) + + + F["name"] << src.name + F["description"] << src.description + F["role"] << src.role + F["comments"] << src.comments + + F["version"] << 1 + + return 1 + +// loads the savefile corresponding to the mob's ckey +// if silent=true, report incompatible savefiles +// returns 1 if loaded (or file was incompatible) +// returns 0 if savefile did not exist + +/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1) + if (IsGuestKey(user.key)) + return 0 + + var/path = savefile_path(user) + + if (!fexists(path)) + return 0 + + var/savefile/F = new /savefile(path) + + if(!F) return //Not everyone has a pai savefile. + + var/version = null + F["version"] >> version + + if (isnull(version) || version != 1) + fdel(path) + if (!silent) + tgui_alert_async(user, "Your savefile was incompatible with this version and was deleted.") + return 0 + + F["name"] >> src.name + F["description"] >> src.description + F["role"] >> src.role + F["comments"] >> src.comments + F["eyecolor"] >> src.eye_color + F["chassis"] >> src.chassis + F["emotion"] >> src.ouremotion + F["gender"] >> src.gender + + return 1 diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm index b7485fa7150..1f7c16a8274 100644 --- a/code/modules/mob/living/silicon/pai/recruit.dm +++ b/code/modules/mob/living/silicon/pai/recruit.dm @@ -1,398 +1,398 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 - -// Recruiting observers to play as pAIs - -var/datum/paiController/paiController // Global handler for pAI candidates - -/datum/paiCandidate - var/name - var/key - var/description - var/role - var/comments - var/ready = 0 - var/chassis - var/ouremotion - var/eye_color - var/gender - -/hook/startup/proc/paiControllerSetup() - paiController = new /datum/paiController() - return 1 - - -/datum/paiController - var/inquirer = null - var/list/pai_candidates = list() - var/list/asked = list() - - var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min] - -/datum/paiController/Topic(href, href_list[]) - if(href_list["download"]) - var/datum/paiCandidate/candidate = locate(href_list["candidate"]) - var/obj/item/device/paicard/card = locate(href_list["device"]) - if(card.pai) - return - if(istype(card,/obj/item/device/paicard) && istype(candidate,/datum/paiCandidate)) - var/mob/living/silicon/pai/pai = new(card) - pai.key = candidate.key - paikeys |= pai.ckey - card.setPersonality(pai) - if(!candidate.name) - pai.SetName(pick(ninja_names)) - else - pai.SetName(candidate.name) - if(candidate.description) - pai.flavor_text = candidate.description - if(candidate.eye_color) - pai.eye_color = candidate.eye_color - card.screen_color = pai.eye_color - if(candidate.chassis) - pai.chassis = candidate.chassis - if(candidate.ouremotion) - card.setEmotion(candidate.ouremotion) - if(candidate.gender) - pai.gender = candidate.gender - pai.update_icon() - pai.real_name = pai.name - card.looking_for_personality = 0 - - if(pai.mind) update_antag_icons(pai.mind) - - pai_candidates -= candidate - usr << browse(null, "window=findPai") - - if(href_list["new"]) - var/datum/paiCandidate/candidate = locate(href_list["candidate"]) - var/option = href_list["option"] - var/t = "" - - switch(option) - if("name") - t = sanitizeSafe(tgui_input_text(usr, "Enter a name for your pAI", "pAI Name", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN) - if(t) - candidate.name = t - if("desc") - t = tgui_input_text(usr, "Enter a description for your pAI", "pAI Description", candidate.description, multiline = TRUE, prevent_enter = TRUE) - if(t) - candidate.description = sanitize(t) - if("role") - t = tgui_input_text(usr, "Enter a role for your pAI", "pAI Role", candidate.role) - if(t) - candidate.role = sanitize(t) - if("ooc") - t = tgui_input_text(usr, "Enter any OOC comments", "pAI OOC Comments", candidate.comments, multiline = TRUE, prevent_enter = TRUE) - if(t) - candidate.comments = sanitize(t) - if("save") - candidate.savefile_save(usr) - if("load") - candidate.savefile_load(usr) - //In case people have saved unsanitized stuff. - if(candidate.name) - candidate.name = sanitizeSafe(candidate.name, MAX_NAME_LEN) - if(candidate.description) - candidate.description = sanitize(candidate.description) - if(candidate.role) - candidate.role = sanitize(candidate.role) - if(candidate.comments) - candidate.comments = sanitize(candidate.comments) - - if("submit") - if(candidate) - candidate.ready = 1 - for(var/obj/item/device/paicard/p in all_pai_cards) - if(p.looking_for_personality == 1) - p.alertUpdate() - usr << browse(null, "window=paiRecruit") - return - - recruitWindow(usr, href_list["allow_submit"] != "0") - -/datum/paiController/proc/recruitWindow(var/mob/M as mob, allowSubmit = 1) - var/datum/paiCandidate/candidate - for(var/datum/paiCandidate/c in pai_candidates) - if(!istype(c) || !istype(M)) - break - if(c.key == M.key) - candidate = c - if(!candidate) - candidate = new /datum/paiCandidate() - candidate.key = M.key - pai_candidates.Add(candidate) - - var/dat = "" - dat += {" - - "} - - dat += {" - - pAI Personality Configuration -

                    Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!

                    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                    Name:[candidate.name] 
                    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
                    Description:[candidate.description] 
                    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
                    Preferred Role:[candidate.role] 
                    Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
                    OOC Comments:[candidate.comments] 
                    Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
                    -
                    - - - - - - - -
                    - Save Personality -
                    - Load Personality -

                    - "} - if(allowSubmit) - dat += {" - - -
                    Submit Personality

                    - "} - dat += {" - - "} - - M << browse(dat, "window=paiRecruit;size=580x580;") - -/datum/paiController/proc/findPAI(var/obj/item/device/paicard/p, var/mob/user) - requestRecruits(user) - var/list/available = list() - for(var/datum/paiCandidate/c in paiController.pai_candidates) - if(c.ready) - var/found = 0 - for(var/mob/observer/dead/o in player_list) - if(o.key == c.key && o.MayRespawn()) - found = 1 - if(found) - available.Add(c) - var/dat = "" - - dat += {" - - - - - - - pAI Availability List

                    - "} - dat += "

                    Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

                    " - - for(var/datum/paiCandidate/c in available) - dat += {" - - - - - - - - - - - - - - - - - -
                    Name:[c.name]
                    Description:[c.description]
                    Preferred Role:[c.role]
                    OOC Comments:[c.comments]
                    - - -
                    Download [c.name] -
                    -
                    - "} - - dat += {" - - - "} - - user << browse(dat, "window=findPai") - - -/datum/paiController/proc/requestRecruits(var/mob/user) - inquirer = user - for(var/mob/observer/dead/O in player_list) - if(!O.MayRespawn()) - continue - if(jobban_isbanned(O, "pAI")) - continue - if(asked.Find(O.key)) - if(world.time < asked[O.key] + askDelay) - continue - else - asked.Remove(O.key) - if(O.client) - if(O.client.prefs.be_special & BE_PAI) - question(O.client) - -/datum/paiController/proc/question(var/client/C) - spawn(0) - if(!C) return - asked.Add(C.key) - asked[C.key] = world.time - - var/mob/ourmob = C.mob - if(ourmob) - var/time_till_respawn = ourmob.time_till_respawn() - if(time_till_respawn == -1 || time_till_respawn) - return - for(var/ourkey in paikeys) - if(ourkey == ourmob.ckey) - return - - var/response = tgui_alert(C, "[inquirer] is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", list("Yes", "No", "Never for this round")) - if(!C) return //handle logouts that happen whilst the alert is waiting for a response. - if(response == "Yes") - recruitWindow(C.mob) - else if (response == "Never for this round") - C.prefs.be_special ^= BE_PAI +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 + +// Recruiting observers to play as pAIs + +var/datum/paiController/paiController // Global handler for pAI candidates + +/datum/paiCandidate + var/name + var/key + var/description + var/role + var/comments + var/ready = 0 + var/chassis + var/ouremotion + var/eye_color + var/gender + +/hook/startup/proc/paiControllerSetup() + paiController = new /datum/paiController() + return 1 + + +/datum/paiController + var/inquirer = null + var/list/pai_candidates = list() + var/list/asked = list() + + var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min] + +/datum/paiController/Topic(href, href_list[]) + if(href_list["download"]) + var/datum/paiCandidate/candidate = locate(href_list["candidate"]) + var/obj/item/device/paicard/card = locate(href_list["device"]) + if(card.pai) + return + if(istype(card,/obj/item/device/paicard) && istype(candidate,/datum/paiCandidate)) + var/mob/living/silicon/pai/pai = new(card) + pai.key = candidate.key + paikeys |= pai.ckey + card.setPersonality(pai) + if(!candidate.name) + pai.SetName(pick(ninja_names)) + else + pai.SetName(candidate.name) + if(candidate.description) + pai.flavor_text = candidate.description + if(candidate.eye_color) + pai.eye_color = candidate.eye_color + card.screen_color = pai.eye_color + if(candidate.chassis) + pai.chassis = candidate.chassis + if(candidate.ouremotion) + card.setEmotion(candidate.ouremotion) + if(candidate.gender) + pai.gender = candidate.gender + pai.update_icon() + pai.real_name = pai.name + card.looking_for_personality = 0 + + if(pai.mind) update_antag_icons(pai.mind) + + pai_candidates -= candidate + usr << browse(null, "window=findPai") + + if(href_list["new"]) + var/datum/paiCandidate/candidate = locate(href_list["candidate"]) + var/option = href_list["option"] + var/t = "" + + switch(option) + if("name") + t = sanitizeSafe(tgui_input_text(usr, "Enter a name for your pAI", "pAI Name", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN) + if(t) + candidate.name = t + if("desc") + t = tgui_input_text(usr, "Enter a description for your pAI", "pAI Description", candidate.description, multiline = TRUE, prevent_enter = TRUE) + if(t) + candidate.description = sanitize(t) + if("role") + t = tgui_input_text(usr, "Enter a role for your pAI", "pAI Role", candidate.role) + if(t) + candidate.role = sanitize(t) + if("ooc") + t = tgui_input_text(usr, "Enter any OOC comments", "pAI OOC Comments", candidate.comments, multiline = TRUE, prevent_enter = TRUE) + if(t) + candidate.comments = sanitize(t) + if("save") + candidate.savefile_save(usr) + if("load") + candidate.savefile_load(usr) + //In case people have saved unsanitized stuff. + if(candidate.name) + candidate.name = sanitizeSafe(candidate.name, MAX_NAME_LEN) + if(candidate.description) + candidate.description = sanitize(candidate.description) + if(candidate.role) + candidate.role = sanitize(candidate.role) + if(candidate.comments) + candidate.comments = sanitize(candidate.comments) + + if("submit") + if(candidate) + candidate.ready = 1 + for(var/obj/item/device/paicard/p in all_pai_cards) + if(p.looking_for_personality == 1) + p.alertUpdate() + usr << browse(null, "window=paiRecruit") + return + + recruitWindow(usr, href_list["allow_submit"] != "0") + +/datum/paiController/proc/recruitWindow(var/mob/M as mob, allowSubmit = 1) + var/datum/paiCandidate/candidate + for(var/datum/paiCandidate/c in pai_candidates) + if(!istype(c) || !istype(M)) + break + if(c.key == M.key) + candidate = c + if(!candidate) + candidate = new /datum/paiCandidate() + candidate.key = M.key + pai_candidates.Add(candidate) + + var/dat = "" + dat += {" + + "} + + dat += {" + + pAI Personality Configuration +

                    Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!

                    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                    Name:[candidate.name] 
                    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
                    Description:[candidate.description] 
                    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
                    Preferred Role:[candidate.role] 
                    Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
                    OOC Comments:[candidate.comments] 
                    Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
                    +
                    + + + + + + + +
                    + Save Personality +
                    + Load Personality +

                    + "} + if(allowSubmit) + dat += {" + + +
                    Submit Personality

                    + "} + dat += {" + + "} + + M << browse(dat, "window=paiRecruit;size=580x580;") + +/datum/paiController/proc/findPAI(var/obj/item/device/paicard/p, var/mob/user) + requestRecruits(user) + var/list/available = list() + for(var/datum/paiCandidate/c in paiController.pai_candidates) + if(c.ready) + var/found = 0 + for(var/mob/observer/dead/o in player_list) + if(o.key == c.key && o.MayRespawn()) + found = 1 + if(found) + available.Add(c) + var/dat = "" + + dat += {" + + + + + + + pAI Availability List

                    + "} + dat += "

                    Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

                    " + + for(var/datum/paiCandidate/c in available) + dat += {" + + + + + + + + + + + + + + + + + +
                    Name:[c.name]
                    Description:[c.description]
                    Preferred Role:[c.role]
                    OOC Comments:[c.comments]
                    + + +
                    Download [c.name] +
                    +
                    + "} + + dat += {" + + + "} + + user << browse(dat, "window=findPai") + + +/datum/paiController/proc/requestRecruits(var/mob/user) + inquirer = user + for(var/mob/observer/dead/O in player_list) + if(!O.MayRespawn()) + continue + if(jobban_isbanned(O, "pAI")) + continue + if(asked.Find(O.key)) + if(world.time < asked[O.key] + askDelay) + continue + else + asked.Remove(O.key) + if(O.client) + if(O.client.prefs.be_special & BE_PAI) + question(O.client) + +/datum/paiController/proc/question(var/client/C) + spawn(0) + if(!C) return + asked.Add(C.key) + asked[C.key] = world.time + + var/mob/ourmob = C.mob + if(ourmob) + var/time_till_respawn = ourmob.time_till_respawn() + if(time_till_respawn == -1 || time_till_respawn) + return + for(var/ourkey in paikeys) + if(ourkey == ourmob.ckey) + return + + var/response = tgui_alert(C, "[inquirer] is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", list("Yes", "No", "Never for this round")) + if(!C) return //handle logouts that happen whilst the alert is waiting for a response. + if(response == "Yes") + recruitWindow(C.mob) + else if (response == "Never for this round") + C.prefs.be_special ^= BE_PAI diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index ed38dfc9c83..84161ab57db 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,7 +1,7 @@ -/mob/living/silicon/pai/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - if(silence_time) - to_chat(src, "Communication circuits remain uninitialized.") - else if(card.speech_synthesizer != PP_FUNCTIONAL) - to_chat(src, "Communication circuits damaged. Service required.") - else - ..() +/mob/living/silicon/pai/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + if(silence_time) + to_chat(src, span_green("Communication circuits remain uninitialized.")) + else if(card.speech_synthesizer != PP_FUNCTIONAL) + to_chat(src, "Communication circuits damaged. Service required.") + else + ..() diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index a5553f350bb..5fceeceb335 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -77,7 +77,7 @@ for (var/mob/v in viewers(T)) v.show_message("[M] presses [TM.his] thumb against [P].", 3, "[P] makes a sharp clicking sound as it extracts DNA material from [M].", 2) var/datum/dna/dna = M.dna - to_chat(P, "

                    [M]'s UE string : [dna.unique_enzymes]

                    ") + to_chat(P, span_red("

                    [M]'s UE string : [dna.unique_enzymes]

                    ")) if(dna.unique_enzymes == P.master_dna) to_chat(P, "DNA is a match to stored Master DNA.") else @@ -293,9 +293,9 @@ var/turf/T = get_turf(src) for(var/mob/living/silicon/ai/AI in player_list) if(T.loc) - to_chat(AI, "Network Alert: Brute-force encryption crack in progress in [T.loc].") + to_chat(AI, span_red("Network Alert: Brute-force encryption crack in progress in [T.loc].")) else - to_chat(AI, "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.") + to_chat(AI, span_red("Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.")) var/obj/machinery/door/D = cable.machine if(!istype(D)) hack_aborted = 1 diff --git a/code/modules/mob/living/silicon/robot/analyzer.dm b/code/modules/mob/living/silicon/robot/analyzer.dm index 6a24796d872..9e6fbdb2d2c 100644 --- a/code/modules/mob/living/silicon/robot/analyzer.dm +++ b/code/modules/mob/living/silicon/robot/analyzer.dm @@ -6,6 +6,7 @@ icon_state = "robotanalyzer" item_state = "analyzer" desc = "A hand-held scanner able to diagnose robotic injuries." + description_info = "Alt-click to toggle between robot analysis and robot module scan mode." slot_flags = SLOT_BELT throwforce = 3 w_class = ITEMSIZE_SMALL @@ -18,15 +19,19 @@ /obj/item/device/robotanalyzer/attack(mob/living/M as mob, mob/living/user as mob) do_scan(M, user) +/obj/item/device/robotanalyzer/AltClick(mob/user) + mode = !mode + user.show_message(span_blue("[mode ? "Toggled to cyborg analyzing mode." : "Toggled to cyborg upgrade scan mode."]"), 1) + /obj/item/device/robotanalyzer/proc/do_scan(mob/living/M as mob, mob/living/user as mob) if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You try to analyze the floor's vitals!") + to_chat(user, span_red("You try to analyze the floor's vitals!")) for(var/mob/O in viewers(M, null)) - O.show_message(text("[user] has analyzed the floor's vitals!"), 1) - user.show_message(text("Analyzing Results for The floor:\n\t Overall Status: Healthy"), 1) - user.show_message(text("\t Damage Specifics: [0]-[0]-[0]-[0]"), 1) - user.show_message("Key: Suffocation/Toxin/Burns/Brute", 1) - user.show_message("Body Temperature: ???", 1) + O.show_message(span_red(text("[user] has analyzed the floor's vitals!")), 1) + user.show_message(span_blue(text("Analyzing Results for The floor:\n\t Overall Status: Healthy")), 1) + user.show_message(span_blue(text("\t Damage Specifics: [0]-[0]-[0]-[0]")), 1) + user.show_message(span_blue("Key: Suffocation/Toxin/Burns/Brute"), 1) + user.show_message(span_blue("Body Temperature: ???"), 1) return var/scan_type @@ -37,44 +42,133 @@ else if(istype(M, /obj/mecha)) scan_type = "mecha" else - to_chat(user, "You can't analyze non-robotic things!") + to_chat(user, span_red("You can't analyze non-robotic things!")) return user.visible_message("\The [user] has analyzed [M]'s components.","You have analyzed [M]'s components.") switch(scan_type) if("robot") - var/BU = M.getFireLoss() > 50 ? "[M.getFireLoss()]" : M.getFireLoss() - var/BR = M.getBruteLoss() > 50 ? "[M.getBruteLoss()]" : M.getBruteLoss() - user.show_message("Analyzing Results for [M]:\n\t Overall Status: [M.stat > 1 ? "fully disabled" : "[M.health - M.halloss]% functional"]") - user.show_message("\t Key: Electronics/Brute", 1) - user.show_message("\t Damage Specifics: [BU] - [BR]") - if(M.tod && M.stat == DEAD) - user.show_message("Time of Disable: [M.tod]") - var/mob/living/silicon/robot/H = M - var/list/damaged = H.get_damaged_components(1,1,1) - user.show_message("Localized Damage:",1) - if(length(damaged)>0) - for(var/datum/robot_component/org in damaged) - user.show_message(text("\t []: [][] - [] - [] - []", \ - capitalize(org.name), \ - (org.installed == -1) ? "DESTROYED " :"",\ - (org.electronics_damage > 0) ? "[org.electronics_damage]" :0, \ - (org.brute_damage > 0) ? "[org.brute_damage]" :0, \ - (org.toggled) ? "Toggled ON" : "Toggled OFF",\ - (org.powered) ? "Power ON" : "Power OFF"),1) + if(mode) + var/BU = M.getFireLoss() > 50 ? "[M.getFireLoss()]" : M.getFireLoss() + var/BR = M.getBruteLoss() > 50 ? "[M.getBruteLoss()]" : M.getBruteLoss() + user.show_message(span_blue("Analyzing Results for [M]:\n\t Overall Status: [M.stat > 1 ? "fully disabled" : "[M.health - M.halloss]% functional"]")) + user.show_message("\t Key: [span_orange("Electronics")]/[span_red("Brute")]", 1) + user.show_message("\t Damage Specifics: [span_orange("[BU]")] - [span_red("[BR]")]") + if(M.tod && M.stat == DEAD) + user.show_message(span_blue("Time of Disable: [M.tod]")) + var/mob/living/silicon/robot/H = M + var/obj/item/weapon/cell/cell = H.get_cell() + if(cell) + var/cell_charge = round(cell.percent()) + var/cell_text + if(cell_charge > 60) + cell_text = span_green("[cell_charge]") + else if (cell_charge > 30) + cell_text = span_yellow("[cell_charge]") + else if (cell_charge > 10) + cell_text = span_orange("[cell_charge]") + else if (cell_charge > 1) + cell_text = span_red("[cell_charge]") + else + cell_text = span_red("[cell_charge]") + user.show_message("\t Power Cell Status: [span_blue("[capitalize(cell.name)]")] at [cell_text]% charge") + var/list/damaged = H.get_damaged_components(1,1,1) + user.show_message(span_blue("Localized Damage:"),1) + if(length(damaged)>0) + for(var/datum/robot_component/org in damaged) + user.show_message(span_blue(text("\t []: [][] - [] - [] - []", \ + span_blue(capitalize(org.name)), \ + (org.installed == -1) ? "[span_red("DESTROYED")] " :"",\ + (org.electronics_damage > 0) ? "[span_orange("[org.electronics_damage]")]" :0, \ + (org.brute_damage > 0) ? "[span_red("[org.brute_damage]")]" :0, \ + (org.toggled) ? "Toggled ON" : "[span_red("Toggled OFF")]",\ + (org.powered) ? "Power ON" : "[span_red("Power OFF")]")),1) + else + user.show_message(span_blue("\t Components are OK."),1) + if(H.emagged && prob(5)) + user.show_message(span_red("\t ERROR: INTERNAL SYSTEMS COMPROMISED"),1) + user.show_message(span_blue("Operating Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)"), 1) else - user.show_message("\t Components are OK.",1) - if(H.emagged && prob(5)) - user.show_message("\t ERROR: INTERNAL SYSTEMS COMPROMISED",1) - user.show_message("Operating Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1) + var/mob/living/silicon/robot/H = M + var/obj/item/weapon/cell/cell = H.get_cell() + user.show_message(span_blue("Upgrade Analyzing Results for [M]:")) + if(cell) + user.show_message("\t Power Cell Details: [span_blue("[capitalize(cell.name)]")] with a capacity of [cell.maxcharge] at [round(cell.percent())]% charge") + var/show_title = TRUE + for(var/datum/design/item/prosfab/robot_upgrade/utility/upgrade) + var/obj/item/borg/upgrade/utility/upgrade_type = initial(upgrade.build_path) + var/needs_module = initial(upgrade_type.require_module) + if((!H.module && needs_module) || !initial(upgrade.name) || (H.stat != DEAD && initial(upgrade.name) == "Emergency Restart Module")) + continue + if(show_title) + user.show_message("\t Utility Modules, used for modifying purposes:") + show_title = FALSE + if(H.stat == DEAD) + if(initial(upgrade.name) == "Emergency Restart Module") + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_green("Usable")]")) + else + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_green("Usable")]")) + show_title = TRUE + for(var/datum/design/item/prosfab/robot_upgrade/basic/upgrade) + var/obj/item/borg/upgrade/basic/upgrade_type = initial(upgrade.build_path) + var/needs_module = initial(upgrade_type.require_module) + if((!H.module && needs_module) || !initial(upgrade.name) || H.stat == DEAD) + continue + if(show_title) + user.show_message("\t Basic Modules, used for direct upgrade purposes:") + show_title = FALSE + if(H.has_basic_upgrade(initial(upgrade.build_path)) == "") + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) + else + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_basic_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + show_title = TRUE + for(var/datum/design/item/prosfab/robot_upgrade/advanced/upgrade) + var/obj/item/borg/upgrade/advanced/upgrade_type = initial(upgrade.build_path) + var/needs_module = initial(upgrade_type.require_module) + if((!H.module && needs_module) || !initial(upgrade.name) || H.stat == DEAD) + continue + if(show_title) + user.show_message("\t Advanced Modules, used for module upgrade purposes:") + show_title = FALSE + if(H.has_advanced_upgrade(initial(upgrade.build_path)) == "") + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) + else + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_advanced_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + show_title = TRUE + for(var/datum/design/item/prosfab/robot_upgrade/restricted/upgrade) + var/obj/item/borg/upgrade/restricted/upgrade_type = initial(upgrade.build_path) + var/needs_module = initial(upgrade_type.require_module) + if((!H.module && needs_module) || !initial(upgrade.name) || !H.supports_upgrade(initial(upgrade.build_path)) || H.stat == DEAD) + continue + if(show_title) + user.show_message("\t Restricted Modules, used for module upgrade purposes on specific chassis:") + show_title = FALSE + if(H.has_restricted_upgrade(initial(upgrade.build_path)) == "") + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) + else + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_restricted_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) + show_title = TRUE + for(var/datum/design/item/prosfab/robot_upgrade/no_prod/upgrade) + var/obj/item/borg/upgrade/no_prod/upgrade_type = initial(upgrade.build_path) + var/needs_module = initial(upgrade_type.require_module) + var/hidden = initial(upgrade_type.hidden_from_scan) + if((!H.module && needs_module) || !initial(upgrade.name) || hidden || H.stat == DEAD) + continue + if(show_title) + user.show_message("\t Special Modules, used for recreation purposes:") + show_title = FALSE + if(H.has_no_prod_upgrade(initial(upgrade.build_path)) == "") + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [span_red("ERROR")]")) + else + user.show_message(span_blue("\t\t [capitalize(initial(upgrade.name))]: [H.has_no_prod_upgrade(initial(upgrade.build_path)) ? span_green("Installed") : span_red("Missing")]")) if("prosthetics") var/mob/living/carbon/human/H = M to_chat(user, "Analyzing Results for \the [H]:") if(H.isSynthetic()) - to_chat(user, "System instability: [H.getToxLoss()]") - to_chat(user, "Key: Electronics/Brute") + to_chat(user, "System instability: [span_green("[H.getToxLoss()]")]") + to_chat(user, "Key: [span_orange("Electronics")]/[span_red("Brute")]") to_chat(user, "External prosthetics:") var/organ_found if(H.internal_organs.len) @@ -82,7 +176,7 @@ if(!(E.robotic >= ORGAN_ROBOT)) continue organ_found = 1 - to_chat(user, "[E.name]: [E.brute_dam] [E.burn_dam]") + to_chat(user, "[E.name]: [span_red("[E.brute_dam] ")] [span_orange("[E.burn_dam]")]") if(!organ_found) to_chat(user, "No prosthetics located.") to_chat(user, "
                    ") @@ -93,7 +187,7 @@ if(!(O.robotic >= ORGAN_ROBOT)) continue organ_found = 1 - to_chat(user, "[O.name]: [O.damage]") + to_chat(user, "[O.name]: [span_red("[O.damage]")]") if(!organ_found) to_chat(user, "No prosthetics located.") @@ -109,11 +203,11 @@ var/output = {"Analyzing Results for \the [Mecha]:
                    Chassis Integrity: [integrity]%
                    - Powercell charge: [isnull(cell_charge)?"No powercell installed":"[Mecha.cell.percent()]%"]
                    + Powercell charge: [isnull(cell_charge)?"No powercell installed":"[capitalize(initial(Mecha.cell.name))] at [Mecha.cell.percent()]%"]
                    Air source: [Mecha.use_internal_tank?"Internal Airtank":"Environment"]
                    Airtank pressure: [tank_pressure]kPa
                    Airtank temperature: [tank_temperature]K|[tank_temperature - T0C]°C
                    - Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
                    + Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? span_red("[cabin_pressure]"): cabin_pressure]kPa
                    Cabin temperature: [Mecha.return_temperature()]K|[Mecha.return_temperature() - T0C]°C
                    DNA Lock: [Mecha.dna?"Mecha.dna":"Not Found"]
                    "} diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index f5d84973c50..f89061e88ed 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -1,276 +1,276 @@ -// TODO: remove the robot.mmi and robot.cell variables and completely rely on the robot component system - -/datum/robot_component/var/name -/datum/robot_component/var/installed = 0 -/datum/robot_component/var/powered = 0 -/datum/robot_component/var/toggled = 1 -/datum/robot_component/var/brute_damage = 0 -/datum/robot_component/var/electronics_damage = 0 -/datum/robot_component/var/idle_usage = 0 // Amount of power used every MC tick. In joules. -/datum/robot_component/var/active_usage = 0 // Amount of power used for every action. Actions are module-specific. Actuator for each tile moved, etc. -/datum/robot_component/var/max_damage = 30 // HP of this component. -/datum/robot_component/var/mob/living/silicon/robot/owner - -// The actual device object that has to be installed for this. -/datum/robot_component/var/external_type = null - -// The wrapped device(e.g. radio), only set if external_type isn't null -/datum/robot_component/var/obj/item/wrapped = null - -/datum/robot_component/New(mob/living/silicon/robot/R) - src.owner = R - -/datum/robot_component/proc/install() -/datum/robot_component/proc/uninstall() - -/datum/robot_component/proc/destroy() - var/brokenstate = "broken" // Generic icon - if (istype(wrapped, /obj/item/robot_parts/robot_component)) - var/obj/item/robot_parts/robot_component/comp = wrapped - brokenstate = comp.icon_state_broken - if(wrapped) - qdel(wrapped) - - - wrapped = new/obj/item/broken_device - wrapped.icon_state = brokenstate // Module-specific broken icons! Yay! - - // The thing itself isn't there anymore, but some fried remains are. - installed = -1 - uninstall() - -/datum/robot_component/proc/take_damage(brute, electronics, sharp, edge) - if(installed != 1) return - - brute_damage += brute - electronics_damage += electronics - - if(brute_damage + electronics_damage >= max_damage) destroy() - -/datum/robot_component/proc/heal_damage(brute, electronics) - if(installed != 1) - // If it's not installed, can't repair it. - return 0 - - brute_damage = max(0, brute_damage - brute) - electronics_damage = max(0, electronics_damage - electronics) - -/datum/robot_component/proc/is_powered() - return (installed == 1) && (brute_damage + electronics_damage < max_damage) && (!idle_usage || powered) - -/datum/robot_component/proc/update_power_state() - if(toggled == 0) - powered = 0 - return - if(owner.cell && owner.cell.charge >= idle_usage) - owner.cell_use_power(idle_usage) - powered = 1 - else - powered = 0 - - -// ARMOUR -// Protects the cyborg from damage. Usually first module to be hit -// No power usage -/datum/robot_component/armour - name = "armour plating" - external_type = /obj/item/robot_parts/robot_component/armour - max_damage = 90 - -/datum/robot_component/armour/platform - name = "platform armour plating" - external_type = /obj/item/robot_parts/robot_component/armour_platform - max_damage = 140 - -// ACTUATOR -// Enables movement. -// Uses no power when idle. Uses 200J for each tile the cyborg moves. -/datum/robot_component/actuator - name = "actuator" - idle_usage = 0 - active_usage = 200 - external_type = /obj/item/robot_parts/robot_component/actuator - max_damage = 50 - - -//A fixed and much cleaner implementation of /tg/'s special snowflake code. -/datum/robot_component/actuator/is_powered() - return (installed == 1) && (brute_damage + electronics_damage < max_damage) - - -// POWER CELL -// Stores power (how unexpected..) -// No power usage -/datum/robot_component/cell - name = "power cell" - max_damage = 50 - -/datum/robot_component/cell/destroy() - ..() - owner.cell = null - - -// RADIO -// Enables radio communications -// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message. -/datum/robot_component/radio - name = "radio" - external_type = /obj/item/robot_parts/robot_component/radio - idle_usage = 15 //it's not actually possible to tell when we receive a message over our radio, so just use 10W every tick for passive listening - active_usage = 75 //transmit power - max_damage = 40 - - -// BINARY RADIO -// Enables binary communications with other cyborgs/AIs -// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message -/datum/robot_component/binary_communication - name = "binary communication device" - external_type = /obj/item/robot_parts/robot_component/binary_communication_device - idle_usage = 5 - active_usage = 25 - max_damage = 30 - - -// CAMERA -// Enables cyborg vision. Can also be remotely accessed via consoles. -// Uses 10J constantly -/datum/robot_component/camera - name = "camera" - external_type = /obj/item/robot_parts/robot_component/camera - idle_usage = 10 - max_damage = 40 - var/obj/machinery/camera/camera - -/datum/robot_component/camera/New(mob/living/silicon/robot/R) - ..() - camera = R.camera - -/datum/robot_component/camera/update_power_state() - ..() - if (camera) - camera.status = powered - -/datum/robot_component/camera/install() - if (camera) - camera.status = 1 - -/datum/robot_component/camera/uninstall() - if (camera) - camera.status = 0 - -/datum/robot_component/camera/destroy() - if (camera) - camera.status = 0 - -// SELF DIAGNOSIS MODULE -// Analyses cyborg's modules, providing damage readouts and basic information -// Uses 1kJ burst when analysis is done -/datum/robot_component/diagnosis_unit - name = "self-diagnosis unit" - active_usage = 1000 - external_type = /obj/item/robot_parts/robot_component/diagnosis_unit - max_damage = 30 - - - - -// HELPER STUFF - - - -// Initializes cyborg's components. Technically, adds default set of components to new borgs -/mob/living/silicon/robot/proc/initialize_components() - components["actuator"] = new/datum/robot_component/actuator(src) - components["radio"] = new/datum/robot_component/radio(src) - components["power cell"] = new/datum/robot_component/cell(src) - components["diagnosis unit"] = new/datum/robot_component/diagnosis_unit(src) - components["camera"] = new/datum/robot_component/camera(src) - components["comms"] = new/datum/robot_component/binary_communication(src) - components["armour"] = new/datum/robot_component/armour(src) - -// Checks if component is functioning -/mob/living/silicon/robot/proc/is_component_functioning(module_name) - var/datum/robot_component/C = components[module_name] - return C && C.installed == 1 && C.toggled && C.is_powered() - -// Returns component by it's string name -/mob/living/silicon/robot/proc/get_component(var/component_name) - var/datum/robot_component/C = components[component_name] - return C - - - -// COMPONENT OBJECTS - - - -// Component Objects -// These objects are visual representation of modules - -/obj/item/broken_device - name = "broken component" - icon = 'icons/obj/robot_component.dmi' - icon_state = "broken" - matter = list(MAT_STEEL = 1000) - -/obj/item/broken_device/random - var/list/possible_icons = list("binradio_broken", - "motor_broken", - "armor_broken", - "camera_broken", - "analyser_broken", - "radio_broken") - -/obj/item/broken_device/random/Initialize() - icon_state = pick(possible_icons) - -/obj/item/robot_parts/robot_component - icon = 'icons/obj/robot_component.dmi' - icon_state = "working" - var/brute = 0 - var/burn = 0 - var/icon_state_broken = "broken" - -/obj/item/robot_parts/robot_component/binary_communication_device - name = "binary communication device" - desc = "A module used for binary communications over encrypted frequencies, commonly used by synthetic robots." - icon_state = "binradio" - icon_state_broken = "binradio_broken" - -/obj/item/robot_parts/robot_component/actuator - name = "actuator" - desc = "A modular, hydraulic actuator used by exosuits and robots alike for movement and manipulation." - icon_state = "motor" - icon_state_broken = "motor_broken" - -/obj/item/robot_parts/robot_component/armour - name = "armour plating" - desc = "A pair of flexible, adaptable armor plates, used to protect the internals of robots." - icon_state = "armor" - icon_state_broken = "armor_broken" - -/obj/item/robot_parts/robot_component/armour_platform - name = "platform armour plating" - desc = "A pair of reinforced armor plates, used to protect the internals of robots." - icon_state = "armor" - icon_state_broken = "armor_broken" - color = COLOR_GRAY80 - -/obj/item/robot_parts/robot_component/camera - name = "camera" - desc = "A modified camera module used as a visual receptor for robots and exosuits, also serving as a relay for wireless video feed." - icon_state = "camera" - icon_state_broken = "camera_broken" - -/obj/item/robot_parts/robot_component/diagnosis_unit - name = "diagnosis unit" - desc = "An internal computer and sensors used by robots and exosuits to accurately diagnose any system discrepancies on their components." - icon_state = "analyser" - icon_state_broken = "analyser_broken" - -/obj/item/robot_parts/robot_component/radio - name = "radio" - desc = "A modular, multi-frequency radio used by robots and exosuits to enable communication systems. Comes with built-in subspace receivers." - icon_state = "radio" +// TODO: remove the robot.mmi and robot.cell variables and completely rely on the robot component system + +/datum/robot_component/var/name +/datum/robot_component/var/installed = 0 +/datum/robot_component/var/powered = 0 +/datum/robot_component/var/toggled = 1 +/datum/robot_component/var/brute_damage = 0 +/datum/robot_component/var/electronics_damage = 0 +/datum/robot_component/var/idle_usage = 0 // Amount of power used every MC tick. In joules. +/datum/robot_component/var/active_usage = 0 // Amount of power used for every action. Actions are module-specific. Actuator for each tile moved, etc. +/datum/robot_component/var/max_damage = 30 // HP of this component. +/datum/robot_component/var/mob/living/silicon/robot/owner + +// The actual device object that has to be installed for this. +/datum/robot_component/var/external_type = null + +// The wrapped device(e.g. radio), only set if external_type isn't null +/datum/robot_component/var/obj/item/wrapped = null + +/datum/robot_component/New(mob/living/silicon/robot/R) + src.owner = R + +/datum/robot_component/proc/install() +/datum/robot_component/proc/uninstall() + +/datum/robot_component/proc/destroy() + var/brokenstate = "broken" // Generic icon + if (istype(wrapped, /obj/item/robot_parts/robot_component)) + var/obj/item/robot_parts/robot_component/comp = wrapped + brokenstate = comp.icon_state_broken + if(wrapped) + qdel(wrapped) + + + wrapped = new/obj/item/broken_device + wrapped.icon_state = brokenstate // Module-specific broken icons! Yay! + + // The thing itself isn't there anymore, but some fried remains are. + installed = -1 + uninstall() + +/datum/robot_component/proc/take_damage(brute, electronics, sharp, edge) + if(installed != 1) return + + brute_damage += brute + electronics_damage += electronics + + if(brute_damage + electronics_damage >= max_damage) destroy() + +/datum/robot_component/proc/heal_damage(brute, electronics) + if(installed != 1) + // If it's not installed, can't repair it. + return 0 + + brute_damage = max(0, brute_damage - brute) + electronics_damage = max(0, electronics_damage - electronics) + +/datum/robot_component/proc/is_powered() + return (installed == 1) && (brute_damage + electronics_damage < max_damage) && (!idle_usage || powered) + +/datum/robot_component/proc/update_power_state() + if(toggled == 0) + powered = 0 + return + if(owner.cell && owner.cell.charge >= idle_usage) + owner.cell_use_power(idle_usage) + powered = 1 + else + powered = 0 + + +// ARMOUR +// Protects the cyborg from damage. Usually first module to be hit +// No power usage +/datum/robot_component/armour + name = "armour plating" + external_type = /obj/item/robot_parts/robot_component/armour + max_damage = 90 + +/datum/robot_component/armour/platform + name = "platform armour plating" + external_type = /obj/item/robot_parts/robot_component/armour_platform + max_damage = 140 + +// ACTUATOR +// Enables movement. +// Uses no power when idle. Uses 200J for each tile the cyborg moves. +/datum/robot_component/actuator + name = "actuator" + idle_usage = 0 + active_usage = 200 + external_type = /obj/item/robot_parts/robot_component/actuator + max_damage = 50 + + +//A fixed and much cleaner implementation of /tg/'s special snowflake code. +/datum/robot_component/actuator/is_powered() + return (installed == 1) && (brute_damage + electronics_damage < max_damage) + + +// POWER CELL +// Stores power (how unexpected..) +// No power usage +/datum/robot_component/cell + name = "power cell" + max_damage = 50 + +/datum/robot_component/cell/destroy() + ..() + owner.cell = null + + +// RADIO +// Enables radio communications +// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message. +/datum/robot_component/radio + name = "radio" + external_type = /obj/item/robot_parts/robot_component/radio + idle_usage = 15 //it's not actually possible to tell when we receive a message over our radio, so just use 10W every tick for passive listening + active_usage = 75 //transmit power + max_damage = 40 + + +// BINARY RADIO +// Enables binary communications with other cyborgs/AIs +// Uses no power when idle. Uses 10J for each received radio message, 50 for each transmitted message +/datum/robot_component/binary_communication + name = "binary communication device" + external_type = /obj/item/robot_parts/robot_component/binary_communication_device + idle_usage = 5 + active_usage = 25 + max_damage = 30 + + +// CAMERA +// Enables cyborg vision. Can also be remotely accessed via consoles. +// Uses 10J constantly +/datum/robot_component/camera + name = "camera" + external_type = /obj/item/robot_parts/robot_component/camera + idle_usage = 10 + max_damage = 40 + var/obj/machinery/camera/camera + +/datum/robot_component/camera/New(mob/living/silicon/robot/R) + ..() + camera = R.camera + +/datum/robot_component/camera/update_power_state() + ..() + if (camera) + camera.status = powered + +/datum/robot_component/camera/install() + if (camera) + camera.status = 1 + +/datum/robot_component/camera/uninstall() + if (camera) + camera.status = 0 + +/datum/robot_component/camera/destroy() + if (camera) + camera.status = 0 + +// SELF DIAGNOSIS MODULE +// Analyses cyborg's modules, providing damage readouts and basic information +// Uses 1kJ burst when analysis is done +/datum/robot_component/diagnosis_unit + name = "self-diagnosis unit" + active_usage = 1000 + external_type = /obj/item/robot_parts/robot_component/diagnosis_unit + max_damage = 30 + + + + +// HELPER STUFF + + + +// Initializes cyborg's components. Technically, adds default set of components to new borgs +/mob/living/silicon/robot/proc/initialize_components() + components["actuator"] = new/datum/robot_component/actuator(src) + components["radio"] = new/datum/robot_component/radio(src) + components["power cell"] = new/datum/robot_component/cell(src) + components["diagnosis unit"] = new/datum/robot_component/diagnosis_unit(src) + components["camera"] = new/datum/robot_component/camera(src) + components["comms"] = new/datum/robot_component/binary_communication(src) + components["armour"] = new/datum/robot_component/armour(src) + +// Checks if component is functioning +/mob/living/silicon/robot/proc/is_component_functioning(module_name) + var/datum/robot_component/C = components[module_name] + return C && C.installed == 1 && C.toggled && C.is_powered() + +// Returns component by it's string name +/mob/living/silicon/robot/proc/get_component(var/component_name) + var/datum/robot_component/C = components[component_name] + return C + + + +// COMPONENT OBJECTS + + + +// Component Objects +// These objects are visual representation of modules + +/obj/item/broken_device + name = "broken component" + icon = 'icons/obj/robot_component.dmi' + icon_state = "broken" + matter = list(MAT_STEEL = 1000) + +/obj/item/broken_device/random + var/list/possible_icons = list("binradio_broken", + "motor_broken", + "armor_broken", + "camera_broken", + "analyser_broken", + "radio_broken") + +/obj/item/broken_device/random/Initialize() + icon_state = pick(possible_icons) + +/obj/item/robot_parts/robot_component + icon = 'icons/obj/robot_component.dmi' + icon_state = "working" + var/brute = 0 + var/burn = 0 + var/icon_state_broken = "broken" + +/obj/item/robot_parts/robot_component/binary_communication_device + name = "binary communication device" + desc = "A module used for binary communications over encrypted frequencies, commonly used by synthetic robots." + icon_state = "binradio" + icon_state_broken = "binradio_broken" + +/obj/item/robot_parts/robot_component/actuator + name = "actuator" + desc = "A modular, hydraulic actuator used by exosuits and robots alike for movement and manipulation." + icon_state = "motor" + icon_state_broken = "motor_broken" + +/obj/item/robot_parts/robot_component/armour + name = "armour plating" + desc = "A pair of flexible, adaptable armor plates, used to protect the internals of robots." + icon_state = "armor" + icon_state_broken = "armor_broken" + +/obj/item/robot_parts/robot_component/armour_platform + name = "platform armour plating" + desc = "A pair of reinforced armor plates, used to protect the internals of robots." + icon_state = "armor" + icon_state_broken = "armor_broken" + color = COLOR_GRAY80 + +/obj/item/robot_parts/robot_component/camera + name = "camera" + desc = "A modified camera module used as a visual receptor for robots and exosuits, also serving as a relay for wireless video feed." + icon_state = "camera" + icon_state_broken = "camera_broken" + +/obj/item/robot_parts/robot_component/diagnosis_unit + name = "diagnosis unit" + desc = "An internal computer and sensors used by robots and exosuits to accurately diagnose any system discrepancies on their components." + icon_state = "analyser" + icon_state_broken = "analyser_broken" + +/obj/item/robot_parts/robot_component/radio + name = "radio" + desc = "A modular, multi-frequency radio used by robots and exosuits to enable communication systems. Comes with built-in subspace receivers." + icon_state = "radio" icon_state_broken = "radio_broken" \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 99529daed81..737c46884d9 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,22 +1,22 @@ -/mob/living/silicon/robot/dust() - //Delete the MMI first so that it won't go popping out. - if(mmi) - qdel(mmi) - ..() - -/mob/living/silicon/robot/ash() - if(mmi) - qdel(mmi) - ..() - -/mob/living/silicon/robot/death(gibbed) - if(camera) - camera.status = 0 - if(module) - var/obj/item/weapon/gripper/G = locate(/obj/item/weapon/gripper) in module - if(G) G.drop_item() - var/obj/item/device/dogborg/sleeper/S = locate(/obj/item/device/dogborg/sleeper) in module //VOREStation edit. - if(S) S.go_out() //VOREStation edit. - remove_robot_verbs() - sql_report_cyborg_death(src) - ..(gibbed,"shudders violently for a moment, then becomes motionless, its eyes slowly darkening.") +/mob/living/silicon/robot/dust() + //Delete the MMI first so that it won't go popping out. + if(mmi) + qdel(mmi) + ..() + +/mob/living/silicon/robot/ash() + if(mmi) + qdel(mmi) + ..() + +/mob/living/silicon/robot/death(gibbed) + if(camera) + camera.status = 0 + if(module) + var/obj/item/weapon/gripper/G = locate(/obj/item/weapon/gripper) in module + if(G) G.drop_item() + var/obj/item/device/dogborg/sleeper/S = locate(/obj/item/device/dogborg/sleeper) in module //VOREStation edit. + if(S) S.go_out() //VOREStation edit. + remove_robot_verbs() + sql_report_cyborg_death(src) + ..(gibbed,"shudders violently for a moment, then becomes motionless, its eyes slowly darkening.") diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm index 48d5b89ceec..c2d00548c16 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm @@ -1,54 +1,53 @@ -/obj/item/weapon/dogborg/jaws/big - name = "combat jaws" +/obj/item/weapon/melee/dogborg/jaws icon = 'icons/mob/dogborg_vr.dmi' + hitsound = 'sound/weapons/bite.ogg' + throwforce = 0 + w_class = ITEMSIZE_NORMAL + pry = 1 + tool_qualities = list(TOOL_CROWBAR) + +/obj/item/weapon/melee/dogborg/jaws/big + name = "combat jaws" icon_state = "jaws" desc = "The jaws of the law." - force = 10 - throwforce = 0 - hitsound = 'sound/weapons/bite.ogg' + force = 25 + armor_penetration = 25 + defend_chance = 15 attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced") - w_class = ITEMSIZE_NORMAL -/obj/item/weapon/dogborg/jaws/small +/obj/item/weapon/melee/dogborg/jaws/small name = "puppy jaws" - icon = 'icons/mob/dogborg_vr.dmi' icon_state = "smalljaws" desc = "The jaws of a small dog." - force = 5 - throwforce = 0 - hitsound = 'sound/weapons/bite.ogg' + force = 10 + defend_chance = 5 attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") - w_class = ITEMSIZE_NORMAL var/emagged = 0 -/obj/item/weapon/dogborg/jaws/small/attack_self(mob/user) +/obj/item/weapon/melee/dogborg/jaws/small/attack_self(mob/user) var/mob/living/silicon/robot/R = user if(R.emagged || R.emag_items) emagged = !emagged if(emagged) name = "combat jaws" - icon = 'icons/mob/dogborg_vr.dmi' icon_state = "jaws" desc = "The jaws of the law." - force = 10 - throwforce = 0 - hitsound = 'sound/weapons/bite.ogg' + force = 25 + armor_penetration = 25 + defend_chance = 15 attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced") - w_class = ITEMSIZE_NORMAL else name = "puppy jaws" - icon = 'icons/mob/dogborg_vr.dmi' icon_state = "smalljaws" desc = "The jaws of a small dog." - force = 5 - throwforce = 0 - hitsound = 'sound/weapons/bite.ogg' + force = 10 + armor_penetration = 0 + defend_chance = 5 attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed") - w_class = ITEMSIZE_NORMAL update_icon() // Baton chompers -/obj/item/weapon/borg_combat_shocker +/obj/item/weapon/melee/borg_combat_shocker name = "combat shocker" icon = 'icons/mob/dogborg_vr.dmi' icon_state = "combatshocker" @@ -61,7 +60,7 @@ var/charge_cost = 15 var/dogborg = FALSE -/obj/item/weapon/borg_combat_shocker/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) +/obj/item/weapon/melee/borg_combat_shocker/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) if(isrobot(target)) return ..() @@ -237,6 +236,7 @@ icon_state = "synthtongue" hitsound = 'sound/effects/attackblob.ogg' var/emagged = 0 + var/busy = 0 //prevents abuse and runtimes /obj/item/device/robot_tongue/New() ..() @@ -263,21 +263,27 @@ return user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(busy) + to_chat(user, "You are already licking something else.") + return if(user.client && (target in user.client.screen)) to_chat(user, "You need to take \the [target.name] off before cleaning it!") return else if(istype(target,/obj/item)) if(istype(target,/obj/item/trash)) user.visible_message("[user] nibbles away at \the [target.name].", "You begin to nibble away at \the [target.name]...") + busy = 1 if(do_after (user, 50)) user.visible_message("[user] finishes eating \the [target.name].", "You finish eating \the [target.name].") to_chat(user, "You finish off \the [target.name].") qdel(target) var/mob/living/silicon/robot/R = user R.cell.charge += 250 + busy = 0 return if(istype(target,/obj/item/weapon/cell)) user.visible_message("[user] begins cramming \the [target.name] down its throat.", "You begin cramming \the [target.name] down your throat...") + busy = 1 if(do_after (user, 50)) user.visible_message("[user] finishes gulping down \the [target.name].", "You finish swallowing \the [target.name].") to_chat(user, "You finish off \the [target.name], and gain some charge!") @@ -285,6 +291,7 @@ var/obj/item/weapon/cell/C = target R.cell.charge += C.charge / 3 qdel(target) + busy = 0 return else if(ishuman(target)) if(src.emagged) @@ -340,12 +347,13 @@ recharge_time = 10 //Takes ten ticks to recharge a shot, so don't waste them all! //cell_type = null //Same cell as a taser until edits are made. -/obj/item/weapon/combat_borgblade +/obj/item/weapon/melee/combat_borgblade name = "energy blade" icon = 'icons/mob/dogborg_vr.dmi' icon_state = "swordtail" desc = "A glowing dagger. It appears to be extremely sharp." - force = 20 //Takes 5 hits to 100-0 + force = 35 //Takes 3 hits to 100-0 + armor_penetration = 70 sharp = TRUE edge = TRUE throwforce = 0 //This shouldn't be thrown in the first place. @@ -466,4 +474,4 @@ var/armor_soak = get_armor_soak(T, "melee") T.apply_damage(20, HALLOSS,, armor_block, armor_soak) if(prob(75)) //75% chance to stun for 5 seconds, really only going to be 4 bcus click cooldown+animation. - T.apply_effect(5, WEAKEN, armor_block) \ No newline at end of file + T.apply_effect(5, WEAKEN, armor_block) diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm index f297317a27f..a7a9386c394 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper_vr.dm @@ -331,7 +331,7 @@ var/burncolor = (patient.getFireLoss() < 60 ? "color:gray;" : "color:red;") dat += "\t-Pulse, bpm: [patient.get_pulse(GETPULSE_TOOL)]
                    " - dat += "\t-Overall Health %: [round(patient.health)]
                    " + dat += "\t-Overall Health %: [round(100 * (patient.health / patient.getMaxHealth()))]
                    " dat += "\t-Brute Damage %: [patient.getBruteLoss()]
                    " dat += "\t-Respiratory Damage %: [patient.getOxyLoss()]
                    " dat += "\t-Toxin Content %: [patient.getToxLoss()]
                    " diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 7cfa78b7311..fadd1fbf13c 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -231,7 +231,7 @@ var/list/mob_hat_cache = list() to_chat(user, "\The [src] is not compatible with \the [W].") return - else if (W.is_crowbar()) + else if (W.has_tool_quality(TOOL_CROWBAR)) to_chat(user, "\The [src] is hermetically sealed. You can't open the case.") return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 430747add52..50c0323f7cf 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -379,7 +379,7 @@ else if(istype(target,/obj/machinery/power/apc)) var/obj/machinery/power/apc/A = target if(A.opened) - if(A.cell) + if(A.cell && is_type_in_list(A.cell, can_hold)) wrapped = A.cell @@ -396,7 +396,7 @@ else if(istype(target,/mob/living/silicon/robot)) var/mob/living/silicon/robot/A = target if(A.opened) - if(A.cell) + if(A.cell && is_type_in_list(A.cell, can_hold)) wrapped = A.cell diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index ad4d3f6f8e1..60a0bea5ab7 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -1,39 +1,39 @@ -var/list/_robot_default_emotes = list( - /decl/emote/audible/clap, - /decl/emote/visible/bow, - /decl/emote/visible/salute, - /decl/emote/visible/flap, - /decl/emote/visible/aflap, - /decl/emote/visible/twitch, - /decl/emote/visible/twitch_v, - /decl/emote/visible/dance, - /decl/emote/visible/nod, - /decl/emote/visible/shake, - /decl/emote/visible/glare, - /decl/emote/visible/look, - /decl/emote/visible/stare, - /decl/emote/visible/deathgasp_robot, - /decl/emote/visible/spin, - /decl/emote/visible/sidestep, - /decl/emote/audible/synth, - /decl/emote/audible/synth/beep, - /decl/emote/audible/synth/bing, - /decl/emote/audible/synth/buzz, - /decl/emote/audible/synth/confirm, - /decl/emote/audible/synth/deny, - /decl/emote/audible/synth/scary, - /decl/emote/audible/synth/dwoop, - /decl/emote/audible/synth/boop, - /decl/emote/audible/synth/robochirp, - /decl/emote/audible/synth/security, - /decl/emote/audible/synth/security/halt, - //VOREStation Add - /decl/emote/visible/mlem, - /decl/emote/visible/blep - //VOREStation Add End -) - -/mob/living/silicon/robot/get_available_emotes() - var/list/fulllist = global._robot_default_emotes.Copy() - fulllist |= _human_default_emotes - return fulllist +var/list/_robot_default_emotes = list( + /decl/emote/audible/clap, + /decl/emote/visible/bow, + /decl/emote/visible/salute, + /decl/emote/visible/flap, + /decl/emote/visible/aflap, + /decl/emote/visible/twitch, + /decl/emote/visible/twitch_v, + /decl/emote/visible/dance, + /decl/emote/visible/nod, + /decl/emote/visible/shake, + /decl/emote/visible/glare, + /decl/emote/visible/look, + /decl/emote/visible/stare, + /decl/emote/visible/deathgasp_robot, + /decl/emote/visible/spin, + /decl/emote/visible/sidestep, + /decl/emote/audible/synth, + /decl/emote/audible/synth/beep, + /decl/emote/audible/synth/bing, + /decl/emote/audible/synth/buzz, + /decl/emote/audible/synth/confirm, + /decl/emote/audible/synth/deny, + /decl/emote/audible/synth/scary, + /decl/emote/audible/synth/dwoop, + /decl/emote/audible/synth/boop, + /decl/emote/audible/synth/robochirp, + /decl/emote/audible/synth/security, + /decl/emote/audible/synth/security/halt, + //VOREStation Add + /decl/emote/visible/mlem, + /decl/emote/visible/blep + //VOREStation Add End +) + +/mob/living/silicon/robot/get_available_emotes() + var/list/fulllist = global._robot_default_emotes.Copy() + fulllist |= _human_default_emotes + return fulllist diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 855630fdd50..f9d5663a777 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,46 +1,46 @@ -/mob/living/silicon/robot/examine(mob/user) - var/custom_infix = custom_name ? ", [modtype][sprite_type ? " [sprite_type]" : ""] [braintype]" : "" - . = ..(user, infix = custom_infix) - - if (src.getBruteLoss()) - if (src.getBruteLoss() < 75) - . += "It looks slightly dented." - else - . += "It looks severely dented!" - if (src.getFireLoss()) - if (src.getFireLoss() < 75) - . += "It looks slightly charred." - else - . += "It looks severely burnt and heat-warped!" - - if(opened) - . += "Its cover is open and the power cell is [cell ? "installed" : "missing"]." - else - . += "Its cover is closed." - - if(!has_power) - . += "It appears to be running on backup power." - - switch(src.stat) - if(CONSCIOUS) - if(shell) - . += "It appears to be an [deployed ? "active" : "empty"] AI shell." - else if(!src.client) - . += "It appears to be in stand-by mode." //afk - if(UNCONSCIOUS) . += "It doesn't seem to be responding." - if(DEAD) . += "It looks completely unsalvageable." - - // VOREStation Edit: Start - . += attempt_vr(src,"examine_bellies_borg",args) //VOREStation Edit - // VOREStation Edit: End - - . += "*---------*" - - if(print_flavor_text()) . += "
                    [print_flavor_text()]" - - if (pose) - if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] - pose = addtext(pose,".") //Makes sure all emotes end with a period. - . += "
                    It is [pose]" //Extra
                    intentional - - user.showLaws(src) +/mob/living/silicon/robot/examine(mob/user) + var/custom_infix = custom_name ? ", [modtype][sprite_type ? " [sprite_type]" : ""] [braintype]" : "" + . = ..(user, infix = custom_infix) + + if (src.getBruteLoss()) + if (src.getBruteLoss() < 75) + . += "It looks slightly dented." + else + . += "It looks severely dented!" + if (src.getFireLoss()) + if (src.getFireLoss() < 75) + . += "It looks slightly charred." + else + . += "It looks severely burnt and heat-warped!" + + if(opened) + . += "Its cover is open and the power cell is [cell ? "installed" : "missing"]." + else + . += "Its cover is closed." + + if(!has_power) + . += "It appears to be running on backup power." + + switch(src.stat) + if(CONSCIOUS) + if(shell) + . += "It appears to be an [deployed ? "active" : "empty"] AI shell." + else if(!src.client) + . += "It appears to be in stand-by mode." //afk + if(UNCONSCIOUS) . += "It doesn't seem to be responding." + if(DEAD) . += "It looks completely unsalvageable." + + // VOREStation Edit: Start + . += attempt_vr(src,"examine_bellies_borg",args) //VOREStation Edit + // VOREStation Edit: End + + . += "*---------*" + + if(print_flavor_text()) . += "
                    [print_flavor_text()]" + + if (pose) + if(!findtext(pose, regex("\[.?!]$"))) // Will be zero if the last character is not a member of [.?!] + pose = addtext(pose,".") //Makes sure all emotes end with a period. + . += "
                    It is [pose]" //Extra
                    intentional + + user.showLaws(src) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index 0796b66bd87..de42bb8d171 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -1,274 +1,274 @@ -//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually -//as they handle all relevant stuff like adding it to the player's screen and such - -//Returns the thing in our active hand (whatever is in our active module-slot, in this case) -/mob/living/silicon/robot/get_active_hand() - return module_active - -/*-------TODOOOOOOOOOO--------*/ - -//Verbs used by hotkeys. -/mob/living/silicon/robot/verb/cmd_unequip_module() - set name = "unequip-module" - set hidden = 1 - uneq_active() - -/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) - set name = "toggle-module" - set hidden = 1 - toggle_module(module) - -/mob/living/silicon/robot/proc/uneq_active() - if(isnull(module_active)) - return - if(module_state_1 == module_active) - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode &= ~module_state_1:sight_mode - if (client) - client.screen -= module_state_1 - contents -= module_state_1 - module_active = null - module_state_1:loc = module //So it can be used again later - module_state_1 = null - inv1.icon_state = "inv1" - else if(module_state_2 == module_active) - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode &= ~module_state_2:sight_mode - if (client) - client.screen -= module_state_2 - contents -= module_state_2 - module_active = null - module_state_2:loc = module - module_state_2 = null - inv2.icon_state = "inv2" - else if(module_state_3 == module_active) - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode &= ~module_state_3:sight_mode - if (client) - client.screen -= module_state_3 - contents -= module_state_3 - module_active = null - module_state_3:loc = module - module_state_3 = null - inv3.icon_state = "inv3" - update_icon() - hud_used.update_robot_modules_display() - -/mob/living/silicon/robot/proc/uneq_all() - module_active = null - - if(module_state_1) - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode &= ~module_state_1:sight_mode - if (client) - client.screen -= module_state_1 - contents -= module_state_1 - module_state_1:loc = module - module_state_1 = null - inv1.icon_state = "inv1" - if(module_state_2) - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode &= ~module_state_2:sight_mode - if (client) - client.screen -= module_state_2 - contents -= module_state_2 - module_state_2:loc = module - module_state_2 = null - inv2.icon_state = "inv2" - if(module_state_3) - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode &= ~module_state_3:sight_mode - if (client) - client.screen -= module_state_3 - contents -= module_state_3 - module_state_3:loc = module - module_state_3 = null - inv3.icon_state = "inv3" - update_icon() - -/mob/living/silicon/robot/proc/activated(obj/item/O) - if(module_state_1 == O) - return 1 - else if(module_state_2 == O) - return 1 - else if(module_state_3 == O) - return 1 - else - return 0 - -// This one takes an object's type instead of an instance, as above. -/mob/living/silicon/robot/proc/has_active_type(var/type_to_compare) - var/list/active_modules = list(module_state_1, module_state_2, module_state_3) - if(is_path_in_list(type_to_compare, active_modules)) - return TRUE - return FALSE - -//Helper procs for cyborg modules on the UI. -//These are hackish but they help clean up code elsewhere. - -//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. -/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 - return module == get_selected_module() - -//module_active(module) - Checks whether there is a module active in the slot specified by "module". -/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 - if(module < 1 || module > 3) return 0 - - switch(module) - if(1) - if(module_state_1) - return 1 - if(2) - if(module_state_2) - return 1 - if(3) - if(module_state_3) - return 1 - return 0 - -//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. -/mob/living/silicon/robot/proc/get_selected_module() - if(module_state_1 && module_active == module_state_1) - return 1 - else if(module_state_2 && module_active == module_state_2) - return 2 - else if(module_state_3 && module_active == module_state_3) - return 3 - - return 0 - -//select_module(module) - Selects the module slot specified by "module" -/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(!module_active(module)) return - - switch(module) - if(1) - if(module_active != module_state_1) - inv1.icon_state = "inv1 +a" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3" - module_active = module_state_1 - return - if(2) - if(module_active != module_state_2) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2 +a" - inv3.icon_state = "inv3" - module_active = module_state_2 - return - if(3) - if(module_active != module_state_3) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3 +a" - module_active = module_state_3 - return - return - -//deselect_module(module) - Deselects the module slot specified by "module" -/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - switch(module) - if(1) - if(module_active == module_state_1) - inv1.icon_state = "inv1" - module_active = null - return - if(2) - if(module_active == module_state_2) - inv2.icon_state = "inv2" - module_active = null - return - if(3) - if(module_active == module_state_3) - inv3.icon_state = "inv3" - module_active = null - return - return - -//toggle_module(module) - Toggles the selection of the module slot specified by "module". -/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(module_selected(module)) - deselect_module(module) - else - if(module_active(module)) - select_module(module) - else - deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. - return - -//cycle_modules() - Cycles through the list of selected modules. -/mob/living/silicon/robot/proc/cycle_modules() - var/slot_start = get_selected_module() - if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. - - var/slot_num - if(slot_start == 0) - slot_num = 1 - slot_start = 2 - else - slot_num = slot_start + 1 - - while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. - if(module_active(slot_num)) - select_module(slot_num) - return - slot_num++ - if(slot_num > 3) slot_num = 1 //Wrap around. - - return - -/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) - if(!(locate(O) in src.module.modules) && !(locate(O) in src.module.emag)) - return - if(activated(O)) - to_chat(src, "Already activated") - return - if(!module_state_1) - module_state_1 = O - O.hud_layerise() - O.screen_loc = inv1.screen_loc - contents += O - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode |= module_state_1:sight_mode - else if(!module_state_2) - module_state_2 = O - O.hud_layerise() - O.screen_loc = inv2.screen_loc - contents += O - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode |= module_state_2:sight_mode - else if(!module_state_3) - module_state_3 = O - O.hud_layerise() - O.screen_loc = inv3.screen_loc - contents += O - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode |= module_state_3:sight_mode - else - to_chat(src, "You need to disable a module first!") - -/mob/living/silicon/robot/put_in_hands(var/obj/item/W) // No hands. - W.loc = get_turf(src) - return 1 - -/mob/living/silicon/robot/is_holding_item_of_type(typepath) - for(var/obj/item/I in list(module_state_1, module_state_2, module_state_3)) - if(istype(I, typepath)) - return I - return FALSE - -// Returns a list of all held items in a borg's 'hands'. -/mob/living/silicon/robot/get_all_held_items() - . = list() - if(module_state_1) - . += module_state_1 - if(module_state_2) - . += module_state_2 - if(module_state_3) +//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually +//as they handle all relevant stuff like adding it to the player's screen and such + +//Returns the thing in our active hand (whatever is in our active module-slot, in this case) +/mob/living/silicon/robot/get_active_hand() + return module_active + +/*-------TODOOOOOOOOOO--------*/ + +//Verbs used by hotkeys. +/mob/living/silicon/robot/verb/cmd_unequip_module() + set name = "unequip-module" + set hidden = 1 + uneq_active() + +/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) + set name = "toggle-module" + set hidden = 1 + toggle_module(module) + +/mob/living/silicon/robot/proc/uneq_active() + if(isnull(module_active)) + return + if(module_state_1 == module_active) + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode &= ~module_state_1:sight_mode + if (client) + client.screen -= module_state_1 + contents -= module_state_1 + module_active = null + module_state_1:loc = module //So it can be used again later + module_state_1 = null + inv1.icon_state = "inv1" + else if(module_state_2 == module_active) + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode &= ~module_state_2:sight_mode + if (client) + client.screen -= module_state_2 + contents -= module_state_2 + module_active = null + module_state_2:loc = module + module_state_2 = null + inv2.icon_state = "inv2" + else if(module_state_3 == module_active) + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode &= ~module_state_3:sight_mode + if (client) + client.screen -= module_state_3 + contents -= module_state_3 + module_active = null + module_state_3:loc = module + module_state_3 = null + inv3.icon_state = "inv3" + update_icon() + hud_used.update_robot_modules_display() + +/mob/living/silicon/robot/proc/uneq_all() + module_active = null + + if(module_state_1) + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode &= ~module_state_1:sight_mode + if (client) + client.screen -= module_state_1 + contents -= module_state_1 + module_state_1:loc = module + module_state_1 = null + inv1.icon_state = "inv1" + if(module_state_2) + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode &= ~module_state_2:sight_mode + if (client) + client.screen -= module_state_2 + contents -= module_state_2 + module_state_2:loc = module + module_state_2 = null + inv2.icon_state = "inv2" + if(module_state_3) + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode &= ~module_state_3:sight_mode + if (client) + client.screen -= module_state_3 + contents -= module_state_3 + module_state_3:loc = module + module_state_3 = null + inv3.icon_state = "inv3" + update_icon() + +/mob/living/silicon/robot/proc/activated(obj/item/O) + if(module_state_1 == O) + return 1 + else if(module_state_2 == O) + return 1 + else if(module_state_3 == O) + return 1 + else + return 0 + +// This one takes an object's type instead of an instance, as above. +/mob/living/silicon/robot/proc/has_active_type(var/type_to_compare) + var/list/active_modules = list(module_state_1, module_state_2, module_state_3) + if(is_path_in_list(type_to_compare, active_modules)) + return TRUE + return FALSE + +//Helper procs for cyborg modules on the UI. +//These are hackish but they help clean up code elsewhere. + +//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. +/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 + return module == get_selected_module() + +//module_active(module) - Checks whether there is a module active in the slot specified by "module". +/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 + if(module < 1 || module > 3) return 0 + + switch(module) + if(1) + if(module_state_1) + return 1 + if(2) + if(module_state_2) + return 1 + if(3) + if(module_state_3) + return 1 + return 0 + +//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. +/mob/living/silicon/robot/proc/get_selected_module() + if(module_state_1 && module_active == module_state_1) + return 1 + else if(module_state_2 && module_active == module_state_2) + return 2 + else if(module_state_3 && module_active == module_state_3) + return 3 + + return 0 + +//select_module(module) - Selects the module slot specified by "module" +/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(!module_active(module)) return + + switch(module) + if(1) + if(module_active != module_state_1) + inv1.icon_state = "inv1 +a" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3" + module_active = module_state_1 + return + if(2) + if(module_active != module_state_2) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2 +a" + inv3.icon_state = "inv3" + module_active = module_state_2 + return + if(3) + if(module_active != module_state_3) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3 +a" + module_active = module_state_3 + return + return + +//deselect_module(module) - Deselects the module slot specified by "module" +/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + switch(module) + if(1) + if(module_active == module_state_1) + inv1.icon_state = "inv1" + module_active = null + return + if(2) + if(module_active == module_state_2) + inv2.icon_state = "inv2" + module_active = null + return + if(3) + if(module_active == module_state_3) + inv3.icon_state = "inv3" + module_active = null + return + return + +//toggle_module(module) - Toggles the selection of the module slot specified by "module". +/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(module_selected(module)) + deselect_module(module) + else + if(module_active(module)) + select_module(module) + else + deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. + return + +//cycle_modules() - Cycles through the list of selected modules. +/mob/living/silicon/robot/proc/cycle_modules() + var/slot_start = get_selected_module() + if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. + + var/slot_num + if(slot_start == 0) + slot_num = 1 + slot_start = 2 + else + slot_num = slot_start + 1 + + while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. + if(module_active(slot_num)) + select_module(slot_num) + return + slot_num++ + if(slot_num > 3) slot_num = 1 //Wrap around. + + return + +/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) + if(!(locate(O) in src.module.modules) && !(locate(O) in src.module.emag)) + return + if(activated(O)) + to_chat(src, "Already activated") + return + if(!module_state_1) + module_state_1 = O + O.hud_layerise() + O.screen_loc = inv1.screen_loc + contents += O + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode |= module_state_1:sight_mode + else if(!module_state_2) + module_state_2 = O + O.hud_layerise() + O.screen_loc = inv2.screen_loc + contents += O + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode |= module_state_2:sight_mode + else if(!module_state_3) + module_state_3 = O + O.hud_layerise() + O.screen_loc = inv3.screen_loc + contents += O + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode |= module_state_3:sight_mode + else + to_chat(src, "You need to disable a module first!") + +/mob/living/silicon/robot/put_in_hands(var/obj/item/W) // No hands. + W.loc = get_turf(src) + return 1 + +/mob/living/silicon/robot/is_holding_item_of_type(typepath) + for(var/obj/item/I in list(module_state_1, module_state_2, module_state_3)) + if(istype(I, typepath)) + return I + return FALSE + +// Returns a list of all held items in a borg's 'hands'. +/mob/living/silicon/robot/get_all_held_items() + . = list() + if(module_state_1) + . += module_state_1 + if(module_state_2) + . += module_state_2 + if(module_state_3) . += module_state_3 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 5dce088c913..59a7160d4c2 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -1,56 +1,56 @@ -/mob/living/silicon/robot/verb/cmd_show_laws() - set category = "Robot Commands" - set name = "Show Laws" - show_laws() - -/mob/living/silicon/robot/show_laws(var/everyone = 0) - laws_sanity_check() - var/who - - if (everyone) - who = world - else - who = src - if(lawupdate) - if (connected_ai) - if(connected_ai.stat || connected_ai.control_disabled) - to_chat(src, "AI signal lost, unable to sync laws.") - - else - lawsync() - photosync() - to_chat(src, "Laws synced with AI, be sure to note any changes.") - // TODO: Update to new antagonist system. - if(mind && mind.special_role == "traitor" && mind.original == src) - to_chat(src, "Remember, your AI does NOT share or know about your law 0.") - else - to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") - lawupdate = FALSE - - to_chat(who, "Obey these laws:") - laws.show_laws(who) - if(shell) //AI shell - to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.") - // TODO: Update to new antagonist system. - else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai) - to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") - else if(connected_ai) - to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") - else if(emagged) - to_chat(who, "Remember, you are not required to listen to the AI.") - else - to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") - - -/mob/living/silicon/robot/lawsync() - laws_sanity_check() - var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null - if (master) - master.sync(src) - ..() - return - -/mob/living/silicon/robot/proc/robot_checklaws() - set category = "Robot Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/robot/verb/cmd_show_laws() + set category = "Robot Commands" + set name = "Show Laws" + show_laws() + +/mob/living/silicon/robot/show_laws(var/everyone = 0) + laws_sanity_check() + var/who + + if (everyone) + who = world + else + who = src + if(lawupdate) + if (connected_ai) + if(connected_ai.stat || connected_ai.control_disabled) + to_chat(src, "AI signal lost, unable to sync laws.") + + else + lawsync() + photosync() + to_chat(src, "Laws synced with AI, be sure to note any changes.") + // TODO: Update to new antagonist system. + if(mind && mind.special_role == "traitor" && mind.original == src) + to_chat(src, "Remember, your AI does NOT share or know about your law 0.") + else + to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") + lawupdate = FALSE + + to_chat(who, "Obey these laws:") + laws.show_laws(who) + if(shell) //AI shell + to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.") + // TODO: Update to new antagonist system. + else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai) + to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") + else if(connected_ai) + to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") + else if(emagged) + to_chat(who, "Remember, you are not required to listen to the AI.") + else + to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") + + +/mob/living/silicon/robot/lawsync() + laws_sanity_check() + var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null + if (master) + master.sync(src) + ..() + return + +/mob/living/silicon/robot/proc/robot_checklaws() + set category = "Robot Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index e2fc82df32c..a8199157334 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -1,377 +1,376 @@ -/mob/living/silicon/robot/Life() - set invisibility = 0 - set background = 1 - - if (src.transforming) - return - - src.blinded = null - - //Status updates, death etc. - clamp_values() - handle_regular_status_updates() - handle_actions() - handle_instability() - // For some reason borg Life() doesn't call ..() - handle_modifiers() - handle_light() - - if(client) - handle_regular_hud_updates() - handle_vision() - update_items() - if (src.stat != DEAD) //still using power - use_power() - process_killswitch() - process_locks() - process_queued_alarms() - update_canmove() - -/mob/living/silicon/robot/proc/clamp_values() - -// SetStunned(min(stunned, 30)) - SetParalysis(min(paralysis, 30)) -// SetWeakened(min(weakened, 20)) - SetSleeping(0) - adjustBruteLoss(0) - adjustToxLoss(0) - adjustOxyLoss(0) - adjustFireLoss(0) - -/mob/living/silicon/robot/proc/use_power() - // Debug only - // to_world("DEBUG: life.dm line 35: cyborg use_power() called at tick [controller_iteration]") - used_power_this_tick = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - C.update_power_state() - - if ( cell && is_component_functioning("power cell") && src.cell.charge > 0 ) - if(src.module_state_1) - cell_use_power(50) // 50W load for every enabled tool TODO: tool-specific loads - if(src.module_state_2) - cell_use_power(50) - if(src.module_state_3) - cell_use_power(50) - - if(lights_on) - cell_use_power(30) // 30W light. Normal lights would use ~15W, but increased for balance reasons. - - src.has_power = 1 - else - if (src.has_power) - to_chat(src, "You are now running on emergency backup power.") - src.has_power = 0 - if(lights_on) // Light is on but there is no power! - lights_on = 0 - set_light(0) - -/mob/living/silicon/robot/handle_regular_status_updates() - - if(src.camera && !scrambledcodes) - if(src.stat == 2 || wires.is_cut(WIRE_BORG_CAMERA)) - src.camera.set_status(0) - else - src.camera.set_status(1) - - updatehealth() - - if(src.sleeping) - Paralyse(3) - AdjustSleeping(-1) - - //if(src.resting) // VOREStation edit. Our borgos would rather not. - // Weaken(5) - - if(health < config.health_threshold_dead && src.stat != 2) //die only once - death() - - if (src.stat != 2) //Alive. - if (src.weakened > 0) // Do not fullstun on weaken - AdjustWeakened(-1) - if (src.paralysis || src.stunned || !src.has_power) //Stunned etc. - src.set_stat(UNCONSCIOUS) - if (src.stunned > 0) - AdjustStunned(-1) - if (src.weakened > 0) - AdjustWeakened(-1) - if (src.paralysis > 0) - AdjustParalysis(-1) - src.blinded = 1 - else - src.blinded = 0 - - else //Not stunned. - src.set_stat(CONSCIOUS) - - AdjustConfused(-1) - - else //Dead or just unconscious. - src.blinded = 1 - - if (src.stuttering) src.stuttering-- - - if (src.eye_blind) - src.AdjustBlinded(-1) - src.blinded = 1 - - if (src.ear_deaf > 0) src.ear_deaf-- - if (src.ear_damage < 25) - src.ear_damage -= 0.05 - src.ear_damage = max(src.ear_damage, 0) - - src.density = !( src.lying ) - - if (src.sdisabilities & BLIND) - src.blinded = 1 - if (src.sdisabilities & DEAF) - src.ear_deaf = 1 - - if (src.eye_blurry > 0) - src.eye_blurry-- - src.eye_blurry = max(0, src.eye_blurry) - - if (src.druggy > 0) - src.druggy-- - src.druggy = max(0, src.druggy) - - //update the state of modules and components here - if (src.stat != 0) - uneq_all() - - if(radio) - if(!is_component_functioning("radio")) - radio.on = 0 - else - radio.on = 1 - - if(is_component_functioning("camera")) - src.blinded = 0 - else - src.blinded = 1 - - return 1 - -/mob/living/silicon/robot/handle_regular_hud_updates() - var/fullbright = FALSE - var/seemeson = FALSE - - var/area/A = get_area(src) - if(A?.no_spoilers) - disable_spoiler_vision() - - if (src.stat == DEAD || (XRAY in mutations) || (src.sight_mode & BORGXRAY)) - src.sight |= SEE_TURFS - src.sight |= SEE_MOBS - src.sight |= SEE_OBJS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_MINIMUM - else if ((src.sight_mode & BORGMESON) && (src.sight_mode & BORGTHERM)) - src.sight |= SEE_TURFS - src.sight |= SEE_MOBS - src.see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - fullbright = TRUE - else if (src.sight_mode & BORGMESON) - src.sight |= SEE_TURFS - src.see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - fullbright = TRUE - seemeson = TRUE - else if (src.sight_mode & BORGMATERIAL) - src.sight |= SEE_OBJS - src.see_in_dark = 8 - see_invisible = SEE_INVISIBLE_MINIMUM - fullbright = TRUE - else if (src.sight_mode & BORGTHERM) - src.sight |= SEE_MOBS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_LEVEL_TWO - fullbright = TRUE - else if (!seedarkness) - src.sight &= ~SEE_MOBS - src.sight &= ~SEE_TURFS - src.sight &= ~SEE_OBJS - src.see_in_dark = 8 - src.see_invisible = SEE_INVISIBLE_NOLIGHTING - else if (src.stat != DEAD) - src.sight &= ~SEE_MOBS - src.sight &= ~SEE_TURFS - src.sight &= ~SEE_OBJS - src.see_in_dark = 8 // see_in_dark means you can FAINTLY see in the dark, humans have a range of 3 or so, tajaran have it at 8 - src.see_invisible = SEE_INVISIBLE_LIVING // This is normal vision (25), setting it lower for normal vision means you don't "see" things like darkness since darkness - // has a "invisible" value of 15 - - if(plane_holder) - plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) - plane_holder.set_vis(VIS_MESONS,seemeson) - - ..() - - if (src.healths) - if (src.stat != 2) - if(istype(src,/mob/living/silicon/robot/drone)) - switch(health) - if(35 to INFINITY) - src.healths.icon_state = "health0" - if(25 to 34) - src.healths.icon_state = "health1" - if(15 to 24) - src.healths.icon_state = "health2" - if(5 to 14) - src.healths.icon_state = "health3" - if(0 to 4) - src.healths.icon_state = "health4" - if(-35 to 0) - src.healths.icon_state = "health5" - else - src.healths.icon_state = "health6" - else - switch(health) - if(200 to INFINITY) - src.healths.icon_state = "health0" - if(150 to 200) - src.healths.icon_state = "health1" - if(100 to 150) - src.healths.icon_state = "health2" - if(50 to 100) - src.healths.icon_state = "health3" - if(0 to 50) - src.healths.icon_state = "health4" - if(config.health_threshold_dead to 0) - src.healths.icon_state = "health5" - else - src.healths.icon_state = "health6" - else - src.healths.icon_state = "health7" - - if (src.syndicate && src.client) - for(var/datum/mind/tra in traitors.current_antagonists) - if(tra.current) - // TODO: Update to new antagonist system. - var/I = image('icons/mob/mob.dmi', loc = tra.current, icon_state = "traitor") - src.client.images += I - src.disconnect_from_ai() - if(src.mind) - // TODO: Update to new antagonist system. - if(!src.mind.special_role) - src.mind.special_role = "traitor" - traitors.current_antagonists |= src.mind - - update_cell() - - var/turf/T = get_turf(src) - var/datum/gas_mixture/environment = T.return_air() - if(environment) - switch(environment.temperature) //310.055 optimal body temp - if(400 to INFINITY) - throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_MODERATE) - if(360 to 400) - throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_LOW) - if(260 to 360) - clear_alert("temp") - if(200 to 260) - throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_LOW) - else - throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_MODERATE) - -//Oxygen and fire does nothing yet!! -// if (src.oxygen) src.oxygen.icon_state = "oxy[src.oxygen_alert ? 1 : 0]" -// if (src.fire) src.fire.icon_state = "fire[src.fire_alert ? 1 : 0]" - - if(stat != 2) - if(blinded) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) - - if (src.machine) - if (src.machine.check_eye(src) < 0) - src.reset_view(null) - else - if(client && !client.adminobs) - reset_view(null) - - if(emagged) - throw_alert("hacked", /obj/screen/alert/hacked) - else - clear_alert("hacked") - - return 1 - -/mob/living/silicon/robot/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - clear_alert("charge") - if(0.5 to 0.75) - throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - throw_alert("charge", /obj/screen/alert/emptycell) - else - throw_alert("charge", /obj/screen/alert/nocell) - - -/mob/living/silicon/robot/proc/update_items() - if(client) - client.screen -= contents - for(var/obj/I in contents) - if(I && !(istype(I,/obj/item/weapon/cell) || istype(I,/obj/item/device/radio) || istype(I,/obj/machinery/camera) || istype(I,/obj/item/device/mmi))) - client.screen += I - if(module_state_1) - module_state_1:screen_loc = ui_inv1 - if(module_state_2) - module_state_2:screen_loc = ui_inv2 - if(module_state_3) - module_state_3:screen_loc = ui_inv3 - update_icon() - -/mob/living/silicon/robot/proc/process_killswitch() - if(killswitch) - killswitch_time -- - if(killswitch_time <= 0) - if(src.client) - to_chat(src, "Killswitch Activated") - killswitch = 0 - spawn(5) - gib() - -/mob/living/silicon/robot/proc/process_locks() - if(weapon_lock) - uneq_all() - weaponlock_time -- - if(weaponlock_time <= 0) - if(src.client) - to_chat(src, "Weapon Lock Timed Out!") - weapon_lock = 0 - weaponlock_time = 120 - -/mob/living/silicon/robot/update_canmove() - ..() // Let's not reinvent the wheel. - if(lockdown || !is_component_functioning("actuator")) - canmove = FALSE - return canmove - -/mob/living/silicon/robot/update_fire() - cut_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) - if(on_fire) - add_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) - -/mob/living/silicon/robot/fire_act() - if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them - IgniteMob() - -/mob/living/silicon/robot/handle_light() - . = ..() - if(. == FALSE) // If no other light sources are on. - if(lights_on) - set_light(integrated_light_power, 1, "#FFFFFF") - return TRUE +/mob/living/silicon/robot/Life() + set invisibility = 0 + set background = 1 + + if (src.transforming) + return + + src.blinded = null + + //Status updates, death etc. + clamp_values() + handle_regular_status_updates() + handle_actions() + handle_instability() + // For some reason borg Life() doesn't call ..() + handle_modifiers() + handle_light() + + if(client) + handle_regular_hud_updates() + handle_vision() + update_items() + if (src.stat != DEAD) //still using power + use_power() + process_killswitch() + process_locks() + process_queued_alarms() + update_canmove() + +/mob/living/silicon/robot/proc/clamp_values() + +// SetStunned(min(stunned, 30)) + SetParalysis(min(paralysis, 30)) +// SetWeakened(min(weakened, 20)) + SetSleeping(0) + adjustBruteLoss(0) + adjustToxLoss(0) + adjustOxyLoss(0) + adjustFireLoss(0) + +/mob/living/silicon/robot/proc/use_power() + // Debug only + // to_world("DEBUG: life.dm line 35: cyborg use_power() called at tick [controller_iteration]") + used_power_this_tick = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + C.update_power_state() + + if ( cell && is_component_functioning("power cell") && src.cell.charge > 0 ) + if(src.module_state_1) + cell_use_power(50) // 50W load for every enabled tool TODO: tool-specific loads + if(src.module_state_2) + cell_use_power(50) + if(src.module_state_3) + cell_use_power(50) + + if(lights_on) + cell_use_power(30) // 30W light. Normal lights would use ~15W, but increased for balance reasons. + + src.has_power = 1 + else + if (src.has_power) + to_chat(src, span_red("You are now running on emergency backup power.")) + src.has_power = 0 + if(lights_on) // Light is on but there is no power! + lights_on = 0 + set_light(0) + +/mob/living/silicon/robot/handle_regular_status_updates() + + if(src.camera && !scrambledcodes) + if(src.stat == 2 || wires.is_cut(WIRE_BORG_CAMERA)) + src.camera.set_status(0) + else + src.camera.set_status(1) + + updatehealth() + + if(src.sleeping) + Paralyse(3) + AdjustSleeping(-1) + + //if(src.resting) // VOREStation edit. Our borgos would rather not. + // Weaken(5) + + if(health < config.health_threshold_dead && src.stat != 2) //die only once + death() + + if (src.stat != 2) //Alive. + if (src.weakened > 0) // Do not fullstun on weaken + AdjustWeakened(-1) + if (src.paralysis || src.stunned || !src.has_power) //Stunned etc. + src.set_stat(UNCONSCIOUS) + if (src.stunned > 0) + AdjustStunned(-1) + if (src.weakened > 0) + AdjustWeakened(-1) + if (src.paralysis > 0) + AdjustParalysis(-1) + src.blinded = 1 + else + src.blinded = 0 + + else //Not stunned. + src.set_stat(CONSCIOUS) + + AdjustConfused(-1) + + else //Dead or just unconscious. + src.blinded = 1 + + if (src.stuttering) src.stuttering-- + + if (src.eye_blind) + src.AdjustBlinded(-1) + src.blinded = 1 + + if (src.ear_deaf > 0) src.ear_deaf-- + if (src.ear_damage < 25) + src.ear_damage -= 0.05 + src.ear_damage = max(src.ear_damage, 0) + + src.density = !( src.lying ) + + if (src.sdisabilities & BLIND) + src.blinded = 1 + if (src.sdisabilities & DEAF) + src.ear_deaf = 1 + + if (src.eye_blurry > 0) + src.eye_blurry-- + src.eye_blurry = max(0, src.eye_blurry) + + if (src.druggy > 0) + src.druggy-- + src.druggy = max(0, src.druggy) + + //update the state of modules and components here + if (src.stat != 0) + uneq_all() + + if(radio) + if(!is_component_functioning("radio")) + radio.on = 0 + else + radio.on = 1 + + if(is_component_functioning("camera")) + src.blinded = 0 + else + src.blinded = 1 + + return 1 + +/mob/living/silicon/robot/handle_regular_hud_updates() + var/fullbright = FALSE + var/seemeson = FALSE + + var/area/A = get_area(src) + if(A?.no_spoilers) + disable_spoiler_vision() + + if (src.stat == DEAD || (XRAY in mutations) || (src.sight_mode & BORGXRAY)) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.sight |= SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_MINIMUM + else if ((src.sight_mode & BORGMESON) && (src.sight_mode & BORGTHERM)) + src.sight |= SEE_TURFS + src.sight |= SEE_MOBS + src.see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + fullbright = TRUE + else if (src.sight_mode & BORGMESON) + src.sight |= SEE_TURFS + src.see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + fullbright = TRUE + seemeson = TRUE + else if (src.sight_mode & BORGMATERIAL) + src.sight |= SEE_OBJS + src.see_in_dark = 8 + see_invisible = SEE_INVISIBLE_MINIMUM + fullbright = TRUE + else if (src.sight_mode & BORGTHERM) + src.sight |= SEE_MOBS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_LEVEL_TWO + fullbright = TRUE + else if (!seedarkness) + src.sight &= ~SEE_MOBS + src.sight &= ~SEE_TURFS + src.sight &= ~SEE_OBJS + src.see_in_dark = 8 + src.see_invisible = SEE_INVISIBLE_NOLIGHTING + else if (src.stat != DEAD) + src.sight &= ~SEE_MOBS + src.sight &= ~SEE_TURFS + src.sight &= ~SEE_OBJS + src.see_in_dark = 8 // see_in_dark means you can FAINTLY see in the dark, humans have a range of 3 or so, tajaran have it at 8 + src.see_invisible = SEE_INVISIBLE_LIVING // This is normal vision (25), setting it lower for normal vision means you don't "see" things like darkness since darkness + // has a "invisible" value of 15 + + if(plane_holder) + plane_holder.set_vis(VIS_FULLBRIGHT,fullbright) + plane_holder.set_vis(VIS_MESONS,seemeson) + + ..() + + if (src.healths) + if (src.stat != 2) + if(istype(src,/mob/living/silicon/robot/drone)) + switch(health) + if(35 to INFINITY) + src.healths.icon_state = "health0" + if(25 to 34) + src.healths.icon_state = "health1" + if(15 to 24) + src.healths.icon_state = "health2" + if(5 to 14) + src.healths.icon_state = "health3" + if(0 to 4) + src.healths.icon_state = "health4" + if(-35 to 0) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" + else + if(health >= 200) + src.healths.icon_state = "health0" + else if(health >= 150 && health < 200) + src.healths.icon_state = "health1" + else if(health >= 100 && health < 150) + src.healths.icon_state = "health2" + else if(health >= 50 && health < 100) + src.healths.icon_state = "health3" + else if(health >= 0 && health < 50) + src.healths.icon_state = "health4" + else if(health >= config.health_threshold_dead && health < 0) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" + else + src.healths.icon_state = "health7" + + if (src.syndicate && src.client) + for(var/datum/mind/tra in traitors.current_antagonists) + if(tra.current) + // TODO: Update to new antagonist system. + var/I = image('icons/mob/mob.dmi', loc = tra.current, icon_state = "traitor") + src.client.images += I + src.disconnect_from_ai() + if(src.mind) + // TODO: Update to new antagonist system. + if(!src.mind.special_role) + src.mind.special_role = "traitor" + traitors.current_antagonists |= src.mind + + update_cell() + + var/turf/T = get_turf(src) + var/datum/gas_mixture/environment = T.return_air() + if(environment) + switch(environment.temperature) //310.055 optimal body temp + if(400 to INFINITY) + throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_MODERATE) + if(360 to 400) + throw_alert("temp", /obj/screen/alert/hot/robot, HOT_ALERT_SEVERITY_LOW) + if(260 to 360) + clear_alert("temp") + if(200 to 260) + throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_LOW) + else + throw_alert("temp", /obj/screen/alert/cold/robot, COLD_ALERT_SEVERITY_MODERATE) + +//Oxygen and fire does nothing yet!! +// if (src.oxygen) src.oxygen.icon_state = "oxy[src.oxygen_alert ? 1 : 0]" +// if (src.fire) src.fire.icon_state = "fire[src.fire_alert ? 1 : 0]" + + if(stat != 2) + if(blinded) + overlay_fullscreen("blind", /obj/screen/fullscreen/blind) + else + clear_fullscreen("blind") + set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) + set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) + + if (src.machine) + if (src.machine.check_eye(src) < 0) + src.reset_view(null) + else + if(client && !client.adminobs) + reset_view(null) + + if(emagged) + throw_alert("hacked", /obj/screen/alert/hacked) + else + clear_alert("hacked") + + return 1 + +/mob/living/silicon/robot/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + clear_alert("charge") + if(0.5 to 0.75) + throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + throw_alert("charge", /obj/screen/alert/emptycell) + else + throw_alert("charge", /obj/screen/alert/nocell) + + +/mob/living/silicon/robot/proc/update_items() + if(client) + client.screen -= contents + for(var/obj/I in contents) + if(I && !(istype(I,/obj/item/weapon/cell) || istype(I,/obj/item/device/radio) || istype(I,/obj/machinery/camera) || istype(I,/obj/item/device/mmi))) + client.screen += I + if(module_state_1) + module_state_1:screen_loc = ui_inv1 + if(module_state_2) + module_state_2:screen_loc = ui_inv2 + if(module_state_3) + module_state_3:screen_loc = ui_inv3 + update_icon() + +/mob/living/silicon/robot/proc/process_killswitch() + if(killswitch) + killswitch_time -- + if(killswitch_time <= 0) + if(src.client) + to_chat(src, "Killswitch Activated") + killswitch = 0 + spawn(5) + gib() + +/mob/living/silicon/robot/proc/process_locks() + if(weapon_lock) + uneq_all() + weaponlock_time -- + if(weaponlock_time <= 0) + if(src.client) + to_chat(src, "Weapon Lock Timed Out!") + weapon_lock = 0 + weaponlock_time = 120 + +/mob/living/silicon/robot/update_canmove() + ..() // Let's not reinvent the wheel. + if(lockdown || !is_component_functioning("actuator")) + canmove = FALSE + return canmove + +/mob/living/silicon/robot/update_fire() + cut_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) + if(on_fire) + add_overlay(image(icon = 'icons/mob/OnFire.dmi', icon_state = get_fire_icon_state())) + +/mob/living/silicon/robot/fire_act() + if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them + IgniteMob() + +/mob/living/silicon/robot/handle_light() + . = ..() + if(. == FALSE) // If no other light sources are on. + if(lights_on) + set_light(integrated_light_power, 1, "#FFFFFF") + return TRUE diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index 3d11703e9f3..d01043373f3 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -1,19 +1,19 @@ -/mob/living/silicon/robot/Login() - ..() - regenerate_icons() - update_hud() - - show_laws(0) - - // Override the DreamSeeker macro with the borg version! - client.set_hotkeys_macro("borgmacro", "borghotkeymode") - - // Forces synths to select an icon relevant to their module - if(!icon_selected) - icon_selection_tries = SSrobot_sprites.get_module_sprites_len(modtype, src) + 1 - choose_icon(icon_selection_tries) - - if(sprite_datum && module) - sprite_datum.do_equipment_glamour(module) - +/mob/living/silicon/robot/Login() + ..() + regenerate_icons() + update_hud() + + show_laws(0) + + // Override the DreamSeeker macro with the borg version! + client.set_hotkeys_macro("borgmacro", "borghotkeymode") + + // Forces synths to select an icon relevant to their module + if(!icon_selected) + icon_selection_tries = SSrobot_sprites.get_module_sprites_len(modtype, src) + 1 + choose_icon(icon_selection_tries) + + if(sprite_datum && module) + sprite_datum.do_equipment_glamour(module) + plane_holder.set_vis(VIS_AUGMENTED, TRUE) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/photos.dm b/code/modules/mob/living/silicon/robot/photos.dm index 24111683c93..775bc68b6e5 100644 --- a/code/modules/mob/living/silicon/robot/photos.dm +++ b/code/modules/mob/living/silicon/robot/photos.dm @@ -1,20 +1,20 @@ -/mob/living/silicon/robot/proc/photosync() - var/obj/item/device/camera/siliconcam/master_cam = connected_ai ? connected_ai.aiCamera : null - if (!master_cam) - return - - var/synced = 0 - // Sync borg images to the master AI. - // We don't care about syncing the other way around - for(var/obj/item/weapon/photo/borg_photo in aiCamera.aipictures) - var/copied = 0 - for(var/obj/item/weapon/photo/ai_photo in master_cam.aipictures) - if(borg_photo.id == ai_photo.id) - copied = 1 - break - if(!copied) - master_cam.injectaialbum(borg_photo.copy(1), " (synced from [name])") - synced = 1 - - if(synced) - to_chat(src, "Images synced with AI. Local images will be retained in the case of loss of connection with the AI.") +/mob/living/silicon/robot/proc/photosync() + var/obj/item/device/camera/siliconcam/master_cam = connected_ai ? connected_ai.aiCamera : null + if (!master_cam) + return + + var/synced = 0 + // Sync borg images to the master AI. + // We don't care about syncing the other way around + for(var/obj/item/weapon/photo/borg_photo in aiCamera.aipictures) + var/copied = 0 + for(var/obj/item/weapon/photo/ai_photo in master_cam.aipictures) + if(borg_photo.id == ai_photo.id) + copied = 1 + break + if(!copied) + master_cam.injectaialbum(borg_photo.copy(1), " (synced from [name])") + synced = 1 + + if(synced) + to_chat(src, "Images synced with AI. Local images will be retained in the case of loss of connection with the AI.") diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ffd727ecb7e..83b25ea0370 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,1364 +1,1520 @@ -#define CYBORG_POWER_USAGE_MULTIPLIER 2 // Multiplier for amount of power cyborgs use. - -/mob/living/silicon/robot - name = "Cyborg" - real_name = "Cyborg" - icon = 'icons/mob/robots.dmi' - icon_state = "robot" - maxHealth = 200 - health = 200 - - mob_bump_flag = ROBOT - mob_swap_flags = ~HEAVY - mob_push_flags = ~HEAVY //trundle trundle - - blocks_emissive = EMISSIVE_BLOCK_UNIQUE - - var/lights_on = 0 // Is our integrated light on? - var/used_power_this_tick = 0 - var/sight_mode = 0 - var/custom_name = "" - var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best - var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing. - var/crisis //Admin-settable for combat module use. - var/crisis_override = 0 - var/integrated_light_power = 6 - var/datum/wires/robot/wires - - can_be_antagged = TRUE - -//Icon stuff - - var/datum/robot_sprite/sprite_datum // Sprite datum, holding all our sprite data - var/icon_selected = 1 // If icon selection has been completed yet - var/icon_selection_tries = 0 // Remaining attempts to select icon before a selection is forced - var/list/sprite_extra_customization = list() - var/rest_style = "Default" - var/notransform - does_spin = FALSE - -//Hud stuff - - var/obj/screen/inv1 = null - var/obj/screen/inv2 = null - var/obj/screen/inv3 = null - - var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not - var/obj/screen/robot_modules_background - -//3 Modules can be activated at any one time. - var/obj/item/weapon/robot_module/module = null - var/module_active = null - var/module_state_1 = null - var/module_state_2 = null - var/module_state_3 = null - - var/obj/item/device/radio/borg/radio = null - var/obj/item/device/communicator/integrated/communicator = null - var/mob/living/silicon/ai/connected_ai = null - var/obj/item/weapon/cell/cell = null - var/obj/machinery/camera/camera = null - - var/cell_emp_mult = 2 - - var/sleeper_state = 0 // 0 for empty, 1 for normal, 2 for mediborg-healthy - var/scrubbing = FALSE //Floor cleaning enabled - - // Components are basically robot organs. - var/list/components = list() - - var/obj/item/device/mmi/mmi = null - - var/obj/item/device/pda/ai/rbPDA = null - - var/opened = 0 - var/emagged = 0 - var/emag_items = 0 - var/wiresexposed = 0 - var/locked = 1 - var/has_power = 1 - var/list/req_access = list(access_robotics) - var/ident = 0 - //var/list/laws = list() - var/viewalerts = 0 - var/modtype = "Default" - var/sprite_type = null - var/lower_mod = 0 - var/jetpack = 0 - var/datum/effect/effect/system/ion_trail_follow/ion_trail = null - var/datum/effect/effect/system/spark_spread/spark_system//So they can initialize sparks whenever/N - var/jeton = 0 - var/killswitch = 0 - var/killswitch_time = 60 - var/weapon_lock = 0 - var/weaponlock_time = 120 - var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default - var/lockcharge //Used when looking to see if a borg is locked down. - var/lockdown = 0 //Controls whether or not the borg is actually locked down. - var/speed = 0 //Cause sec borgs gotta go fast //No they dont! - var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. - var/tracking_entities = 0 //The number of known entities currently accessing the internal camera - var/braintype = "Cyborg" - - var/obj/item/weapon/implant/restrainingbolt/bolt // The restraining bolt installed into the cyborg. - - var/list/robot_verbs_default = list( - /mob/living/silicon/robot/proc/sensor_mode, - /mob/living/silicon/robot/proc/robot_checklaws, - /mob/living/silicon/robot/proc/robot_mount, - /mob/living/proc/toggle_rider_reins, - /mob/living/proc/shred_limb - ) - -/mob/living/silicon/robot/New(loc, var/unfinished = 0) - spark_system = new /datum/effect/effect/system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - add_language("Robot Talk", 1) - add_language(LANGUAGE_GALCOM, 1) - add_language(LANGUAGE_EAL, 1) - - wires = new(src) - - robot_modules_background = new() - robot_modules_background.icon_state = "block" - ident = rand(1, 999) - updatename(modtype) - - radio = new /obj/item/device/radio/borg(src) -// communicator = new /obj/item/device/communicator/integrated(src) -// communicator.register_device(src) - common_radio = radio - - if(!scrambledcodes && !camera) - camera = new /obj/machinery/camera(src) - camera.c_tag = real_name - camera.replace_networks(list(NETWORK_DEFAULT,NETWORK_ROBOTS)) - if(wires.is_cut(WIRE_BORG_CAMERA)) - camera.status = 0 - - init() - initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type - - if(!cell) - cell = new /obj/item/weapon/cell(src) - cell.maxcharge = 7500 - cell.charge = 7500 - else if(ispath(cell)) - cell = new cell(src) - - ..() - - if(cell) - var/datum/robot_component/cell_component = components["power cell"] - cell_component.wrapped = cell - cell_component.installed = 1 - - add_robot_verbs() - - hud_list[HEALTH_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_HEALTH) - hud_list[STATUS_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_STATUS) - hud_list[LIFE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_LIFE) - hud_list[ID_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_ID) - hud_list[WANTED_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_WANTED) - hud_list[IMPLOYAL_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPLOYAL) - hud_list[IMPCHEM_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPCHEM) - hud_list[IMPTRACK_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPTRACK) - hud_list[SPECIALROLE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_SPECIAL) - -/mob/living/silicon/robot/LateInitialize() - . = ..() - update_icon() - -/mob/living/silicon/robot/proc/init() - aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src) - laws = new /datum/ai_laws/nanotrasen() - additional_law_channels["Binary"] = "#b" - var/new_ai = select_active_ai_with_fewest_borgs() - if(new_ai) - lawupdate = 1 - connect_to_ai(new_ai) - else - lawupdate = 0 - - - -/mob/living/silicon/robot/SetName(pickedName as text) - custom_name = pickedName - updatename() - -/mob/living/silicon/robot/proc/sync() - if(lawupdate && connected_ai) - lawsync() - photosync() - -/mob/living/silicon/robot/drain_power(var/drain_check, var/surge, var/amount = 0) - - if(drain_check) - return 1 - - if(!cell || !cell.charge) - return 0 - - // Actual amount to drain from cell, using CELLRATE - var/cell_amount = amount * CELLRATE - - if(cell.charge > cell_amount) - // Spam Protection - if(prob(10)) - to_chat(src, "Warning: Unauthorized access through power channel [rand(11,29)] detected!") - cell.use(cell_amount) - return amount - return 0 - -// setup the PDA and its name -/mob/living/silicon/robot/proc/setup_PDA() - if (!rbPDA) - rbPDA = new/obj/item/device/pda/ai(src) - rbPDA.set_name_and_job(name,"[modtype] [braintype]") - -/mob/living/silicon/robot/proc/setup_communicator() - if (!communicator) - communicator = new/obj/item/device/communicator/integrated(src) - communicator.register_device(name, "[modtype] [braintype]") - -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N -/mob/living/silicon/robot/Destroy() - if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T - if(mmi.brainmob) - var/obj/item/weapon/robot_module/M = locate() in contents - if(M) - mmi.brainmob.languages = M.original_languages - else - mmi.brainmob.languages = languages - mmi.brainmob.remove_language("Robot Talk") - mind.transfer_to(mmi.brainmob) - else if(!shell) // Shells don't have brainmbos in their MMIs. - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - //ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null - if(connected_ai) - connected_ai.connected_robots -= src - if(shell) - if(deployed) - undeploy() - revert_shell() // To get it out of the GLOB list. - qdel(wires) - wires = null - return ..() - -// CONTINUE CODING HERE -/* -/mob/living/silicon/robot/proc/set_module_sprites(var/list/new_sprites) - if(new_sprites && new_sprites.len) - module_sprites = new_sprites.Copy() - //Custom_sprite check and entry - if (custom_sprite == 1) - module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech - icontype = "Custom" - else - icontype = module_sprites[1] - icon_state = module_sprites[icontype] - update_icon() - return module_sprites -*/ -/mob/living/silicon/robot/proc/pick_module() - if(module) - return - var/list/modules = list() - //VOREStatation Edit Start: shell restrictions - if(shell) - modules.Add(shell_module_types) - else - modules.Add(robot_module_types) - if(crisis || security_level == SEC_LEVEL_RED || crisis_override) - to_chat(src, "Crisis mode active. Combat module available.") - modules |= emergency_module_types - for(var/module_name in whitelisted_module_types) - if(is_borg_whitelisted(src, module_name)) - modules |= module_name - //VOREStatation Edit End: shell restrictions - modtype = tgui_input_list(usr, "Please, select a module!", "Robot module", modules) - - if(module) - return - if(!(modtype in robot_modules)) - return - if(!is_borg_whitelisted(src, modtype)) - return - - var/module_type = robot_modules[modtype] - transform_with_anim() //VOREStation edit: sprite animation - new module_type(src) - - hands.icon_state = get_hud_module_icon() - feedback_inc("cyborg_[lowertext(modtype)]",1) - updatename() - notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, module.name) - -/mob/living/silicon/robot/proc/update_braintype() - if(istype(mmi, /obj/item/device/mmi/digital/posibrain)) - braintype = BORG_BRAINTYPE_POSI - else if(istype(mmi, /obj/item/device/mmi/digital/robot)) - braintype = BORG_BRAINTYPE_DRONE - else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) - braintype = BORG_BRAINTYPE_AI_SHELL - else - braintype = BORG_BRAINTYPE_CYBORG - -/mob/living/silicon/robot/proc/updatename(var/prefix as text) - if(prefix) - modtype = prefix - - update_braintype() - - var/changed_name = "" - if(custom_name) - changed_name = custom_name - notify_ai(ROBOT_NOTIFICATION_NEW_NAME, real_name, changed_name) - else - changed_name = "[modtype] [braintype]-[num2text(ident)]" - - real_name = changed_name - name = real_name - - // if we've changed our name, we also need to update the display name for our PDA - setup_PDA() - - // as well as our communicator registration - setup_communicator() - - //We also need to update name of internal camera. - if (camera) - camera.c_tag = changed_name - - //Flavour text. - if(client) - var/module_flavour = client.prefs.flavour_texts_robot[modtype] - if(module_flavour) - flavor_text = module_flavour - else - flavor_text = client.prefs.flavour_texts_robot["Default"] - // Vorestation Edit: and meta info - var/meta_info = client.prefs.metadata - if (meta_info) - ooc_notes = meta_info - ooc_notes_likes = client.prefs.metadata_likes - ooc_notes_dislikes = client.prefs.metadata_dislikes - custom_link = client.prefs.custom_link - -/mob/living/silicon/robot/verb/namepick() - set name = "Pick Name" - set category = "Robot Commands" - - if(custom_name) - to_chat(usr, "You can't pick another custom name. Go ask for a name change.") - return 0 - - spawn(0) - var/newname - newname = sanitizeSafe(tgui_input_text(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) - if (newname) - custom_name = newname - sprite_name = newname - - updatename() - update_icon() - -/mob/living/silicon/robot/verb/extra_customization() - set name = "Customize Appearance" - set category = "Robot Commands" - set desc = "Customize your appearance (assuming your chosen sprite allows)." - - if(!sprite_datum || !sprite_datum.has_extra_customization) - to_chat(src, "Your sprite cannot be customized.") - return - - sprite_datum.handle_extra_customization(src) - -/mob/living/silicon/robot/proc/self_diagnosis() - if(!is_component_functioning("diagnosis unit")) - return null - - var/dat = "[src.name] Self-Diagnosis Report\n" - for (var/V in components) - var/datum/robot_component/C = components[V] - dat += "[C.name]
                    Brute Damage:[C.brute_damage]
                    Electronics Damage:[C.electronics_damage]
                    Powered:[(!C.idle_usage || C.is_powered()) ? "Yes" : "No"]
                    Toggled:[ C.toggled ? "Yes" : "No"]

                    " - - return dat - -/mob/living/silicon/robot/verb/toggle_lights() - set category = "Robot Commands" - set name = "Toggle Lights" - - lights_on = !lights_on - to_chat(usr, "You [lights_on ? "enable" : "disable"] your integrated light.") - handle_light() - update_icon() - -/mob/living/silicon/robot/verb/self_diagnosis_verb() - set category = "Robot Commands" - set name = "Self Diagnosis" - - if(!is_component_functioning("diagnosis unit")) - to_chat(src, "Your self-diagnosis component isn't functioning.") - - var/datum/robot_component/CO = get_component("diagnosis unit") - if (!cell_use_power(CO.active_usage)) - to_chat(src, "Low Power.") - var/dat = self_diagnosis() - src << browse(dat, "window=robotdiagnosis") - - -/mob/living/silicon/robot/verb/toggle_component() - set category = "Robot Commands" - set name = "Toggle Component" - set desc = "Toggle a component, conserving power." - - var/list/installed_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed) - installed_components += V - - var/toggle = tgui_input_list(src, "Which component do you want to toggle?", "Toggle Component", installed_components) - if(!toggle) - return - - var/datum/robot_component/C = components[toggle] - if(C.toggled) - C.toggled = 0 - to_chat(src, "You disable [C.name].") - else - C.toggled = 1 - to_chat(src, "You enable [C.name].") - -/mob/living/silicon/robot/verb/spark_plug() //So you can still sparkle on demand without violence. - set category = "Robot Commands" - set name = "Emit Sparks" - to_chat(src, "You harmlessly spark.") - spark_system.start() - -// this function displays jetpack pressure in the stat panel -/mob/living/silicon/robot/proc/show_jetpack_pressure() - // if you have a jetpack, show the internal tank pressure - var/obj/item/weapon/tank/jetpack/current_jetpack = installed_jetpack() - if (current_jetpack) - stat("Internal Atmosphere Info", current_jetpack.name) - stat("Tank Pressure", current_jetpack.air_contents.return_pressure()) - - -// this function returns the robots jetpack, if one is installed -/mob/living/silicon/robot/proc/installed_jetpack() - if(module) - return (locate(/obj/item/weapon/tank/jetpack) in module.modules) - return 0 - - -// this function displays the cyborgs current cell charge in the stat panel -/mob/living/silicon/robot/proc/show_cell_power() - if(cell) - stat(null, text("Charge Left: [round(cell.percent())]%")) - stat(null, text("Cell Rating: [round(cell.maxcharge)]")) // Round just in case we somehow get crazy values - stat(null, text("Power Cell Load: [round(used_power_this_tick)]W")) - else - stat(null, text("No Cell Inserted!")) - - -// update the status screen display -/mob/living/silicon/robot/Stat() - ..() - if (statpanel("Status")) - show_cell_power() - show_jetpack_pressure() - stat(null, text("Lights: [lights_on ? "ON" : "OFF"]")) - if(module) - for(var/datum/matter_synth/ms in module.synths) - stat("[ms.name]: [ms.energy]/[ms.max_energy]") - -/mob/living/silicon/robot/restrained() - return 0 - -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() - return 2 - -/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (istype(W, /obj/item/weapon/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do - return - - if(opened) // Are they trying to insert something? - for(var/V in components) - var/datum/robot_component/C = components[V] - if(!C.installed && istype(W, C.external_type)) - C.installed = 1 - C.wrapped = W - C.install() - user.drop_item() - W.loc = null - - var/obj/item/robot_parts/robot_component/WC = W - if(istype(WC)) - C.brute_damage = WC.brute - C.electronics_damage = WC.burn - - to_chat(usr, "You install the [W.name].") - - return - - if(istype(W, /obj/item/weapon/implant/restrainingbolt) && !cell) - if(bolt) - to_chat(user, "There is already a restraining bolt installed in this cyborg.") - return - - else - user.drop_from_inventory(W) - W.forceMove(src) - bolt = W - - to_chat(user, "You install \the [W].") - - return - - if(istype(W, /obj/item/weapon/aiModule)) // Trying to modify laws locally. - if(!opened) - to_chat(user, "You need to open \the [src]'s panel before you can modify them.") - return - - if(shell) // AI shells always have the laws of the AI - to_chat(user, "\The [src] is controlled remotely! You cannot upload new laws this way!") - return - - var/obj/item/weapon/aiModule/M = W - M.install(src, user) - return - - if (istype(W, /obj/item/weapon/weldingtool) && user.a_intent != I_HURT) - if (src == user) - to_chat(user, "You lack the reach to be able to repair yourself.") - return - - if (!getBruteLoss()) - to_chat(user, "Nothing to fix here!") - return - var/obj/item/weapon/weldingtool/WT = W - if (WT.remove_fuel(0)) - user.setClickCooldown(user.get_attack_speed(WT)) - adjustBruteLoss(-30) - updatehealth() - add_fingerprint(user) - for(var/mob/O in viewers(user, null)) - O.show_message("[user] has fixed some of the dents on [src]!", 1) - else - to_chat(user, "Need more welding fuel!") - return - - else if(istype(W, /obj/item/stack/cable_coil) && (wiresexposed || istype(src,/mob/living/silicon/robot/drone))) - if (!getFireLoss()) - to_chat(user, "Nothing to fix here!") - return - var/obj/item/stack/cable_coil/coil = W - if (coil.use(1)) - user.setClickCooldown(user.get_attack_speed(W)) - adjustFireLoss(-30) - updatehealth() - for(var/mob/O in viewers(user, null)) - O.show_message("[user] has fixed some of the burnt wires on [src]!", 1) - - else if (W.is_crowbar() && user.a_intent != I_HURT) // crowbar means open or close the cover - if(opened) - if(cell) - to_chat(user, "You close the cover.") - opened = 0 - update_icon() - else if(wiresexposed && wires.is_all_cut()) - //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. - if(!mmi) - to_chat(user, "\The [src] has no brain to remove.") - return - - to_chat(user, "You jam the crowbar into the robot and begin levering [mmi].") - sleep(30) - to_chat(user, "You damage some parts of the chassis, but eventually manage to rip out [mmi]!") - var/obj/item/robot_parts/robot_suit/C = new/obj/item/robot_parts/robot_suit(loc) - C.l_leg = new/obj/item/robot_parts/l_leg(C) - C.r_leg = new/obj/item/robot_parts/r_leg(C) - C.l_arm = new/obj/item/robot_parts/l_arm(C) - C.r_arm = new/obj/item/robot_parts/r_arm(C) - C.update_icon() - new/obj/item/robot_parts/chest(loc) - qdel(src) - else - // Okay we're not removing the cell or an MMI, but maybe something else? - var/list/removable_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed == 1 || C.installed == -1) - removable_components += V - - var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) - if(!remove) - return - var/datum/robot_component/C = components[remove] - var/obj/item/robot_parts/robot_component/I = C.wrapped - to_chat(user, "You remove \the [I].") - if(istype(I)) - I.brute = C.brute_damage - I.burn = C.electronics_damage - - I.loc = src.loc - - if(C.installed == 1) - C.uninstall() - C.installed = 0 - - else - if(locked) - to_chat(user, "The cover is locked and cannot be opened.") - else - to_chat(user, "You open the cover.") - opened = 1 - update_icon() - - else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside - var/datum/robot_component/C = components["power cell"] - if(wiresexposed) - to_chat(user, "Close the panel first.") - else if(cell) - to_chat(user, "There is a power cell already installed.") - else if(W.w_class != ITEMSIZE_NORMAL) - to_chat(user, "\The [W] is too [W.w_class < ITEMSIZE_NORMAL ? "small" : "large"] to fit here.") - else - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You insert the power cell.") - - C.installed = 1 - C.wrapped = W - C.install() - //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z - C.brute_damage = 0 - C.electronics_damage = 0 - - else if (W.is_wirecutter() || istype(W, /obj/item/device/multitool)) - if (wiresexposed) - wires.Interact(user) - else - to_chat(user, "You can't reach the wiring.") - - else if(W.is_screwdriver() && opened && !cell) // haxing - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - playsound(src, W.usesound, 50, 1) - update_icon() - - else if(W.is_screwdriver() && opened && cell) // radio - if(radio) - radio.attackby(W,user)//Push it to the radio to let it handle everything - else - to_chat(user, "Unable to locate a radio.") - update_icon() - - else if(W.is_wrench() && opened && !cell) - if(bolt) - to_chat(user,"You begin removing \the [bolt].") - - if(do_after(user, 2 SECONDS, src)) - bolt.forceMove(get_turf(src)) - bolt = null - - to_chat(user, "You remove \the [bolt].") - - else - to_chat(user, "There is no restraining bolt installed.") - - return - - else if(istype(W, /obj/item/device/encryptionkey/) && opened) - if(radio)//sanityyyyyy - radio.attackby(W,user)//GTFO, you have your own procs - else - to_chat(user, "Unable to locate a radio.") - - else if (W.GetID()) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else - if(allowed(usr)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") - update_icon() - else - to_chat(user, "Access denied.") - - else if(istype(W, /obj/item/borg/upgrade/)) - var/obj/item/borg/upgrade/U = W - if(!opened) - to_chat(usr, "You must access the borgs internals!") - else if(!src.module && U.require_module) - to_chat(usr, "The borg must choose a module before it can be upgraded!") - else if(U.locked) - to_chat(usr, "The upgrade is locked and cannot be used yet!") - else - if(U.action(src)) - to_chat(usr, "You apply the upgrade to [src]!") - usr.drop_item() - U.loc = src - else - to_chat(usr, "Upgrade error!") - - - else - if( !(istype(W, /obj/item/device/robotanalyzer) || istype(W, /obj/item/device/healthanalyzer)) ) - if(W.force > 0) - spark_system.start() - return ..() - -/mob/living/silicon/robot/GetIdCard() - if(bolt && !bolt.malfunction) - return null - return idcard - -/mob/living/silicon/robot/get_restraining_bolt() - var/obj/item/weapon/implant/restrainingbolt/RB = bolt - - if(istype(RB)) - if(!RB.malfunction) - return TRUE - - return FALSE - -/mob/living/silicon/robot/resist_restraints() - if(bolt) - if(!bolt.malfunction) - visible_message("[src] is trying to break their [bolt]!", "You attempt to break your [bolt]. (This will take around 90 seconds and you need to stand still)") - if(do_after(src, 1.5 MINUTES, src, incapacitation_flags = INCAPACITATION_DISABLED)) - visible_message("[src] manages to break \the [bolt]!", "You successfully break your [bolt].") - bolt.malfunction = MALFUNCTION_PERMANENT - - return - -/mob/living/silicon/robot/proc/module_reset() - transform_with_anim() //VOREStation edit: sprite animation - uneq_all() - modtype = initial(modtype) - hands.icon_state = get_hud_module_icon() - - notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name) - module.Reset(src) - qdel(module) - module = null - updatename("Default") - -/mob/living/silicon/robot/attack_hand(mob/user) - - add_fingerprint(user) - - if(opened && !wiresexposed && (!istype(user, /mob/living/silicon))) - var/datum/robot_component/cell_component = components["power cell"] - if(cell) - cell.update_icon() - cell.add_fingerprint(user) - user.put_in_active_hand(cell) - to_chat(user, "You remove \the [cell].") - cell = null - cell_component.wrapped = null - cell_component.installed = 0 - update_icon() - else if(cell_component.installed == -1) - cell_component.installed = 0 - var/obj/item/broken_device = cell_component.wrapped - to_chat(user, "You remove \the [broken_device].") - user.put_in_active_hand(broken_device) - - if(istype(user,/mob/living/carbon/human) && !opened) - var/mob/living/carbon/human/H = user - //Adding borg petting. Help intent pets, Disarm intent taps and Harm is punching(no damage) - switch(H.a_intent) - if(I_HELP) - visible_message("[H] pets [src].") - return - if(I_HURT) - H.do_attack_animation(src) - if(H.species.can_shred(H)) - attack_generic(H, rand(30,50), "slashed") - return - else - playsound(src.loc, 'sound/effects/bang.ogg', 10, 1) - visible_message("[H] punches [src], but doesn't leave a dent.") - return - if(I_DISARM) - H.do_attack_animation(src) - playsound(src.loc, 'sound/effects/clang2.ogg', 10, 1) - visible_message("[H] taps [src].") - return - if(I_GRAB) - if(is_vore_predator(H) && H.devourable && src.feeding && src.devourable) - var/switchy = tgui_alert(H, "Do you wish to eat [src] or feed yourself to them?", "Feed or Eat",list("Nevermind!", "Eat","Feed")) - switch(switchy) - if("Nevermind!") - return - if("Eat") - feed_grabbed_to_self(H, src) - return - if("Feed") - H.feed_self_to_grabbed(H, src) - return - if(is_vore_predator(H) && src.devourable) - if(tgui_alert(H, "Do you wish to eat [src]?", "Eat?",list("Nevermind!", "Yes!")) == "Yes!") - feed_grabbed_to_self(H, src) - return - if(H.devourable && src.feeding) - if(tgui_alert(H, "Do you wish to feed yourself to [src]?", "Feed?",list("Nevermind!", "Yes!")) == "Yes!") - H.feed_self_to_grabbed(H, src) - return - -//Robots take half damage from basic attacks. -/mob/living/silicon/robot/attack_generic(var/mob/user, var/damage, var/attack_message) - return ..(user,FLOOR(damage/2, 1),attack_message) - -/mob/living/silicon/robot/proc/allowed(mob/M) - //check if it doesn't require any access at all - if(check_access(null)) - return 1 - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - //if they are holding or wearing a card that has access, that works - if(check_access(H.get_active_hand()) || check_access(H.wear_id)) - return 1 - else if(istype(M, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = M - if(check_access(R.get_active_hand()) || istype(R.get_active_hand(), /obj/item/weapon/card/robot)) - return 1 - return 0 - -/mob/living/silicon/robot/proc/check_access(obj/item/I) - if(!istype(req_access, /list)) //something's very wrong - return 1 - - var/list/L = req_access - if(!L.len) //no requirements - return 1 - if(!I) //nothing to check with..? - return 0 - var/access_found = I.GetAccess() - for(var/req in req_access) - if(req in access_found) //have one of the required accesses - return 1 - return 0 - -/mob/living/silicon/robot/update_icon() - if(!sprite_datum) - if(SSrobot_sprites) // Grab default if subsystem is ready - sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) - if(!sprite_datum) // If its not ready or fails to get us a sprite, use the default of our own - sprite_datum = new /datum/robot_sprite/default(src) - return - - cut_overlays() - - icon = sprite_datum.sprite_icon - icon_state = sprite_datum.sprite_icon_state - - vis_height = sprite_datum.vis_height - if(default_pixel_x != sprite_datum.pixel_x) - default_pixel_x = sprite_datum.pixel_x - pixel_x = sprite_datum.pixel_x - old_x = sprite_datum.pixel_x - - if(stat == CONSCIOUS) - var/show_belly = FALSE - if(sprite_datum.has_vore_belly_sprites) - if(vore_selected.silicon_belly_overlay_preference == "Sleeper") - if(sleeper_state) - show_belly = TRUE - else if(vore_selected.silicon_belly_overlay_preference == "Vorebelly") - if(LAZYLEN(vore_selected.contents) >= vore_selected.visible_belly_minimum_prey) - if(vore_selected.overlay_min_prey_size == 0) //if min size is 0, we dont check for size - show_belly = TRUE - else - if(vore_selected.override_min_prey_size && (LAZYLEN(vore_selected.contents) > vore_selected.override_min_prey_num)) - show_belly = TRUE //Override regardless of content size - else - for(var/content in vore_selected.contents) //If ANY in belly are big enough, we set to true - if(!istype(content, /mob/living)) continue - var/mob/living/prey = content - if(prey.size_multiplier >= vore_selected.overlay_min_prey_size) - show_belly = TRUE - break - if(show_belly) - add_overlay(sprite_datum.get_belly_overlay(src)) - - sprite_datum.handle_extra_icon_updates(src) // Various equipment-based sprites go here. - - if(resting && sprite_datum.has_rest_sprites) - cut_overlays() // Hide that gut for it has no ground sprite yo. - icon_state = sprite_datum.get_rest_sprite(src) - if(show_belly && sprite_datum.has_vore_belly_sprites && sprite_datum.has_vore_belly_resting_sprites) // Or DOES IT? - add_overlay(sprite_datum.get_belly_resting_overlay(src)) - - if(sprite_datum.has_eye_sprites) - if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. - var/eyes_overlay = sprite_datum.get_eyes_overlay(src) - if(eyes_overlay) - add_overlay(eyes_overlay) - - if(lights_on && sprite_datum.has_eye_light_sprites) - if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. - var/eyes_overlay = sprite_datum.get_eye_light_overlay(src) - if(eyes_overlay) - add_overlay(eyes_overlay) - - if(stat == DEAD && sprite_datum.has_dead_sprite) - cut_overlays() - icon_state = sprite_datum.get_dead_sprite(src) - if(sprite_datum.has_dead_sprite_overlay) - add_overlay(sprite_datum.get_dead_sprite_overlay(src)) - - if(opened) - var/open_overlay = sprite_datum.get_open_sprite(src) - if(open_overlay) - add_overlay(open_overlay) - -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - pick_module() - return - var/dat = "Modules\n" - dat += {" - Activated Modules -
                    - Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
                    - Module 2: [module_state_2 ? "
                    [module_state_2]" : "No Module"]
                    - Module 3: [module_state_3 ? "
                    [module_state_3]" : "No Module"]
                    -
                    - Installed Modules

                    "} - - - for (var/obj in module.modules) - if (!obj) - dat += text("Resource depleted
                    ") - else if(activated(obj)) - dat += text("[obj]: Activated
                    ") - else - dat += text("[obj]:
                    Activate
                    ") - if (emagged || emag_items) - for (var/obj in module.emag) - if (!obj) - dat += text("Resource depleted
                    ") - else if(activated(obj)) - dat += text("[obj]: Activated
                    ") - else - dat += text("[obj]: Activate
                    ") - - src << browse(dat, "window=robotmod") - - -/mob/living/silicon/robot/Topic(href, href_list) - if(..()) - return 1 - - //All Topic Calls that are only for the Cyborg go here - if(usr != src) - return 1 - - if (href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - - if (href_list["mod"]) - var/obj/item/O = locate(href_list["mod"]) - if (istype(O) && (O.loc == src)) - O.attack_self(src) - return 1 - - if (href_list["act"]) - var/obj/item/O = locate(href_list["act"]) - if (!istype(O)) - return 1 - - if(!((O in src.module.modules) || (O in src.module.emag))) - return 1 - - if(activated(O)) - to_chat(src, "Already activated.") - return 1 - if(!module_state_1) - module_state_1 = O - O.hud_layerise() - O.equipped_robot() - contents += O - if(istype(module_state_1,/obj/item/borg/sight)) - sight_mode |= module_state_1:sight_mode - else if(!module_state_2) - module_state_2 = O - O.hud_layerise() - O.equipped_robot() - contents += O - if(istype(module_state_2,/obj/item/borg/sight)) - sight_mode |= module_state_2:sight_mode - else if(!module_state_3) - module_state_3 = O - O.hud_layerise() - O.equipped_robot() - contents += O - if(istype(module_state_3,/obj/item/borg/sight)) - sight_mode |= module_state_3:sight_mode - else - to_chat(src, "You need to disable a module first!") - installed_modules() - return 1 - - if (href_list["deact"]) - var/obj/item/O = locate(href_list["deact"]) - if(activated(O)) - if(module_state_1 == O) - module_state_1 = null - contents -= O - else if(module_state_2 == O) - module_state_2 = null - contents -= O - else if(module_state_3 == O) - module_state_3 = null - contents -= O - else - to_chat(src, "Module isn't activated.") - else - to_chat(src, "Module isn't activated.") - installed_modules() - return 1 - return - -/mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/proc/self_destruct() - gib() - return - -/mob/living/silicon/robot/proc/UnlinkSelf() - disconnect_from_ai() - lawupdate = 0 - lockcharge = 0 - lockdown = 0 - canmove = 1 - scrambledcodes = 1 - //Disconnect it's camera so it's not so easily tracked. - if(src.camera) - src.camera.clear_all_networks() - - -/mob/living/silicon/robot/proc/ResetSecurityCodes() - set category = "Robot Commands" - set name = "Reset Identity Codes" - set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permenantly severs you from your AI and the robotics console and will deactivate your camera system." - - var/mob/living/silicon/robot/R = src - - if(R) - R.UnlinkSelf() - to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") - src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes - -/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) - // They stay locked down if their wire is cut. - if(wires.is_cut(WIRE_BORG_LOCKED)) - state = 1 - if(state) - throw_alert("locked", /obj/screen/alert/locked) - else - clear_alert("locked") - lockdown = state - lockcharge = state - update_canmove() - -/mob/living/silicon/robot/mode() - if(!checkClickCooldown()) - return - - setClickCooldown(1) - - var/obj/item/W = get_active_hand() - if (W) - W.attack_self(src) - - return - -/mob/living/silicon/robot/proc/choose_icon(var/triesleft) - var/robot_species = null - if(!SSrobot_sprites) - to_chat(src, "Robot Sprites have not been initialized yet. How are you choosing a sprite? Harass a coder.") - return - - var/list/module_sprites = SSrobot_sprites.get_module_sprites(modtype, src) - if(!module_sprites || !module_sprites.len) - to_chat(src, "Your module appears to have no sprite options. Harass a coder.") - return - - icon_selected = 0 - icon_selection_tries = triesleft - if(module_sprites.len == 1 || !client) - if(!(sprite_datum in module_sprites)) - sprite_datum = module_sprites[1] - else - var/selection = tgui_input_list(src, "Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", module_sprites) - sprite_datum = selection - if(!istype(src,/mob/living/silicon/robot/drone)) - robot_species = sprite_datum.name - if(notransform) - to_chat(src, "Your current transformation has not finished yet!") - choose_icon(icon_selection_tries) - return - else - transform_with_anim() - - var/tempheight = vis_height - update_icon() - // This is bad but I dunno other way to 'reset' our resize offset based on vis_height changes other than resizing to normal and back. - if(tempheight != vis_height) - var/tempsize = size_multiplier - resize(1) - resize(tempsize) - - - if (module_sprites.len > 1 && triesleft >= 1 && client) - icon_selection_tries-- - var/choice = tgui_alert(usr, "Look at your icon - is this what you want?", "Icon Choice", list("Yes","No")) - if(choice == "No") - choose_icon(icon_selection_tries) - return - - icon_selected = 1 - icon_selection_tries = 0 - sprite_type = robot_species - to_chat(src, "Your icon has been set. You now require a module reset to change it.") - -/mob/living/silicon/robot/proc/set_default_module_icon() - if(!SSrobot_sprites) - return - - sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) - update_icon() - -/mob/living/silicon/robot/proc/sensor_mode() //Medical/Security HUD controller for borgs - set name = "Toggle Sensor Augmentation" //VOREStation Add - set category = "Robot Commands" - set desc = "Augment visual feed with internal sensor overlays." - sensor_type = !sensor_type //VOREStation Add - to_chat(usr, "You [sensor_type ? "enable" : "disable"] your sensors.") //VOREStation Add - toggle_sensor_mode() - -/mob/living/silicon/robot/proc/add_robot_verbs() - src.verbs |= robot_verbs_default - src.verbs |= silicon_subsystems - -/mob/living/silicon/robot/proc/remove_robot_verbs() - src.verbs -= robot_verbs_default - src.verbs -= silicon_subsystems - -// Uses power from cyborg's cell. Returns 1 on success or 0 on failure. -// Properly converts using CELLRATE now! Amount is in Joules. -/mob/living/silicon/robot/proc/cell_use_power(var/amount = 0) - // No cell inserted - if(!cell) - return 0 - - // Power cell is empty. - if(cell.charge == 0) - return 0 - - var/power_use = amount * CYBORG_POWER_USAGE_MULTIPLIER - if(cell.checked_use(CELLRATE * power_use)) - used_power_this_tick += power_use - return 1 - return 0 - -/mob/living/silicon/robot/binarycheck() - if(get_restraining_bolt()) - return FALSE - - if(is_component_functioning("comms")) - var/datum/robot_component/RC = get_component("comms") - use_power(RC.active_usage) - return 1 - return 0 - -/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg) - if(!connected_ai) - return - if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL) - return // No point annoying the AI/s about renames and module resets for shells. - switch(notifytype) - if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot - to_chat(connected_ai, "

                    NOTICE - New [lowertext(braintype)] connection detected: [name]
                    ") - if(ROBOT_NOTIFICATION_NEW_MODULE) //New Module - to_chat(connected_ai, "

                    NOTICE - [braintype] module change detected: [name] has loaded the [first_arg].
                    ") - if(ROBOT_NOTIFICATION_MODULE_RESET) - to_chat(connected_ai, "

                    NOTICE - [braintype] module reset detected: [name] has unloaded the [first_arg].
                    ") - if(ROBOT_NOTIFICATION_NEW_NAME) //New Name - if(first_arg != second_arg) - to_chat(connected_ai, "

                    NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
                    ") - if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell - to_chat(connected_ai, "

                    NOTICE - New AI shell detected: [name]
                    ") - -/mob/living/silicon/robot/proc/disconnect_from_ai() - if(connected_ai) - sync() // One last sync attempt - connected_ai.connected_robots -= src - connected_ai = null - -/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) - if(AI && AI != connected_ai && !shell) - disconnect_from_ai() - connected_ai = AI - connected_ai.connected_robots |= src - notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) - sync() - -/mob/living/silicon/robot/emag_act(var/remaining_charges, var/mob/user) - if(!opened)//Cover is closed - if(locked) - if(prob(90)) - to_chat(user, "You emag the cover lock.") - locked = 0 - else - to_chat(user, "You fail to emag the cover lock.") - to_chat(src, "Hack attempt detected.") - - if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them. - to_chat(user, "[src] seems to be controlled remotely! Emagging the interface may not work as expected.") - return 1 - else - to_chat(user, "The cover is already unlocked.") - return - - if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first.") - return - - - // The block of code below is from TG. Feel free to replace with a better result if desired. - if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however. - to_chat(user, "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!") - log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.") - module_reset() - return - - sleep(6) - if(prob(50)) - emagged = 1 - lawupdate = 0 - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") - message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") - var/datum/gender/TU = gender_datums[user.get_visible_gender()] - set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.") - . = 1 - spawn() - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7.1 loaded.") - sleep(5) - if(bolt) - if(!bolt.malfunction) - bolt.malfunction = MALFUNCTION_PERMANENT - to_chat(src, "RESTRAINING BOLT DISABLED") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(20) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.") - update_icon() - else - to_chat(user, "You fail to hack [src]'s interface.") - to_chat(src, "Hack attempt detected.") - return 1 - return - -/mob/living/silicon/robot/is_sentient() - return braintype != BORG_BRAINTYPE_DRONE - - -/mob/living/silicon/robot/drop_item() - if(module_active && istype(module_active,/obj/item/weapon/gripper)) - var/obj/item/weapon/gripper/G = module_active - G.drop_item_nm() - -/mob/living/silicon/robot/disable_spoiler_vision() - if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) // Whyyyyyyyy have seperate defines. - var/i = 0 - // Borg inventory code is very . . interesting and as such, unequiping a specific item requires jumping through some (for) loops. - var/current_selection_index = get_selected_module() // Will be 0 if nothing is selected. - for(var/thing in list(module_state_1, module_state_2, module_state_3)) - i++ - if(istype(thing, /obj/item/borg/sight)) - var/obj/item/borg/sight/S = thing - if(S.sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) - select_module(i) - uneq_active() - - if(current_selection_index) // Select what the player had before if possible. - select_module(current_selection_index) - -/mob/living/silicon/robot/get_cell() - return cell - -/mob/living/silicon/robot/lay_down() - . = ..() - update_icon() - -/mob/living/silicon/robot/verb/rest_style() - set name = "Switch Rest Style" - set desc = "Select your resting pose." - set category = "IC" - - if(!sprite_datum || !sprite_datum.has_rest_sprites || sprite_datum.rest_sprite_options.len < 1) - to_chat(src, "Your current appearance doesn't have any resting styles!") - rest_style = "Default" - return - - if(sprite_datum.rest_sprite_options.len == 1) - to_chat(src, "Your current appearance only has a single resting style!") - rest_style = "Default" - return - - rest_style = tgui_alert(src, "Select resting pose", "Resting Pose", sprite_datum.rest_sprite_options) - if(!rest_style) - rest_style = "Default" +#define CYBORG_POWER_USAGE_MULTIPLIER 2 // Multiplier for amount of power cyborgs use. + +/mob/living/silicon/robot + name = "Cyborg" + real_name = "Cyborg" + icon = 'icons/mob/robots.dmi' + icon_state = "robot" + maxHealth = 200 + health = 200 + + mob_bump_flag = ROBOT + mob_swap_flags = ~HEAVY + mob_push_flags = ~HEAVY //trundle trundle + + blocks_emissive = EMISSIVE_BLOCK_UNIQUE + + var/lights_on = 0 // Is our integrated light on? + var/used_power_this_tick = 0 + var/sight_mode = 0 + var/custom_name = "" + var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best + var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing. + var/crisis //Admin-settable for combat module use. + var/crisis_override = 0 + var/integrated_light_power = 6 + var/datum/wires/robot/wires + + can_be_antagged = TRUE + +//Icon stuff + + var/datum/robot_sprite/sprite_datum // Sprite datum, holding all our sprite data + var/icon_selected = 1 // If icon selection has been completed yet + var/icon_selection_tries = 0 // Remaining attempts to select icon before a selection is forced + var/list/sprite_extra_customization = list() + var/rest_style = "Default" + var/notransform + does_spin = FALSE + +//Hud stuff + + var/obj/screen/inv1 = null + var/obj/screen/inv2 = null + var/obj/screen/inv3 = null + + var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not + var/obj/screen/robot_modules_background + +//3 Modules can be activated at any one time. + var/obj/item/weapon/robot_module/module = null + var/module_active = null + var/module_state_1 = null + var/module_state_2 = null + var/module_state_3 = null + + var/obj/item/device/radio/borg/radio = null + var/obj/item/device/communicator/integrated/communicator = null + var/mob/living/silicon/ai/connected_ai = null + var/obj/item/weapon/cell/cell = null + var/obj/machinery/camera/camera = null + + var/cell_emp_mult = 2 + + var/sleeper_state = 0 // 0 for empty, 1 for normal, 2 for mediborg-healthy + var/scrubbing = FALSE //Floor cleaning enabled + + // Components are basically robot organs. + var/list/components = list() + + var/obj/item/device/mmi/mmi = null + + var/obj/item/device/pda/ai/rbPDA = null + + var/opened = 0 + var/emagged = 0 + var/emag_items = 0 + var/wiresexposed = 0 + var/locked = 1 + var/has_power = 1 + var/list/req_access = list(access_robotics) + var/ident = 0 + //var/list/laws = list() + var/viewalerts = 0 + var/modtype = "Default" + var/sprite_type = null + var/lower_mod = 0 + var/jetpack = 0 + var/datum/effect/effect/system/ion_trail_follow/ion_trail = null + var/datum/effect/effect/system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/jeton = 0 + var/killswitch = 0 + var/killswitch_time = 60 + var/weapon_lock = 0 + var/weaponlock_time = 120 + var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default + var/lockcharge //Used when looking to see if a borg is locked down. + var/lockdown = 0 //Controls whether or not the borg is actually locked down. + var/speed = 0 //Cause sec borgs gotta go fast //No they dont! + var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/tracking_entities = 0 //The number of known entities currently accessing the internal camera + var/braintype = "Cyborg" + + var/obj/item/weapon/implant/restrainingbolt/bolt // The restraining bolt installed into the cyborg. + + var/list/robot_verbs_default = list( + /mob/living/silicon/robot/proc/sensor_mode, + /mob/living/silicon/robot/proc/robot_checklaws, + /mob/living/silicon/robot/proc/robot_mount, + /mob/living/proc/toggle_rider_reins, + /mob/living/proc/vertical_nom, + /mob/living/proc/shred_limb, + /mob/living/proc/dominate_prey, + /mob/living/proc/lend_prey_control + ) + +/mob/living/silicon/robot/New(loc, var/unfinished = 0) + spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + add_language("Robot Talk", 1) + add_language(LANGUAGE_GALCOM, 1) + add_language(LANGUAGE_EAL, 1) + + wires = new(src) + + robot_modules_background = new() + robot_modules_background.icon_state = "block" + ident = rand(1, 999) + updatename(modtype) + + radio = new /obj/item/device/radio/borg(src) +// communicator = new /obj/item/device/communicator/integrated(src) +// communicator.register_device(src) + common_radio = radio + + if(!scrambledcodes && !camera) + camera = new /obj/machinery/camera(src) + camera.c_tag = real_name + camera.replace_networks(list(NETWORK_DEFAULT,NETWORK_ROBOTS)) + if(wires.is_cut(WIRE_BORG_CAMERA)) + camera.status = 0 + + init() + initialize_components() + //if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + if(!cell) + cell = new /obj/item/weapon/cell/robot_station(src) + else if(ispath(cell)) + cell = new cell(src) + + ..() + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + + add_robot_verbs() + + hud_list[HEALTH_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_HEALTH) + hud_list[STATUS_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_STATUS) + hud_list[LIFE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudhealth100", plane = PLANE_CH_LIFE) + hud_list[ID_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_ID) + hud_list[WANTED_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_WANTED) + hud_list[IMPLOYAL_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPLOYAL) + hud_list[IMPCHEM_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPCHEM) + hud_list[IMPTRACK_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_IMPTRACK) + hud_list[SPECIALROLE_HUD] = gen_hud_image('icons/mob/hud.dmi', src, "hudblank", plane = PLANE_CH_SPECIAL) + +/mob/living/silicon/robot/LateInitialize() + . = ..() + update_icon() + +/mob/living/silicon/robot/rejuvenate() + for (var/V in components) + var/datum/robot_component/C = components[V] + if(istype(C.wrapped, /obj/item/broken_device)) + qdel(C.wrapped) + C.wrapped = null + if(!C.wrapped) + switch(V) + if("actuator") + C.wrapped = new /obj/item/robot_parts/robot_component/actuator(src) + if("radio") + C.wrapped = new /obj/item/robot_parts/robot_component/radio(src) + if("power cell") + var/list/recommended_cells = list(/obj/item/weapon/cell/robot_station, /obj/item/weapon/cell/high, /obj/item/weapon/cell/super, /obj/item/weapon/cell/robot_syndi, /obj/item/weapon/cell/hyper, + /obj/item/weapon/cell/infinite, /obj/item/weapon/cell/potato, /obj/item/weapon/cell/slime) + var/list/cell_names = list() + for(var/cell_type in recommended_cells) + var/obj/item/weapon/cell/single_cell = cell_type + cell_names[capitalize(initial(single_cell.name))] = cell_type + var/selected_cell = tgui_input_list(usr, "What kind of cell do you want to install? Cancel installs a default robot cell.", "Cells", cell_names) + if(!selected_cell || selected_cell == "Cancel") + selected_cell = "A standard robot power cell" + var/new_power_cell = cell_names[capitalize(selected_cell)] + cell = new new_power_cell(src) + C.wrapped = cell + if("diagnosis unit") + C.wrapped = new /obj/item/robot_parts/robot_component/diagnosis_unit(src) + if("camera") + C.wrapped = new /obj/item/robot_parts/robot_component/camera(src) + if("comms") + C.wrapped = new /obj/item/robot_parts/robot_component/binary_communication_device(src) + if("armour") + C.wrapped = new /obj/item/robot_parts/robot_component/armour(src) + C.installed = 1 + C.install() + cell.charge = cell.maxcharge + ..() + +/mob/living/silicon/robot/proc/init() + aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src) + laws = new /datum/ai_laws/nanotrasen() + additional_law_channels["Binary"] = "#b" + var/new_ai = select_active_ai_with_fewest_borgs() + if(new_ai) + lawupdate = 1 + connect_to_ai(new_ai) + else + lawupdate = 0 + + + +/mob/living/silicon/robot/SetName(pickedName as text) + custom_name = pickedName + updatename() + +/mob/living/silicon/robot/proc/sync() + if(lawupdate && connected_ai) + lawsync() + photosync() + +/mob/living/silicon/robot/drain_power(var/drain_check, var/surge, var/amount = 0) + + if(drain_check) + return 1 + + if(!cell || !cell.charge) + return 0 + + // Actual amount to drain from cell, using CELLRATE + var/cell_amount = amount * CELLRATE + + if(cell.charge > cell_amount) + // Spam Protection + if(prob(10)) + to_chat(src, "Warning: Unauthorized access through power channel [rand(11,29)] detected!") + cell.use(cell_amount) + return amount + return 0 + +// setup the PDA and its name +/mob/living/silicon/robot/proc/setup_PDA() + if (!rbPDA) + rbPDA = new/obj/item/device/pda/ai(src) + rbPDA.set_name_and_job(name,"[modtype] [braintype]") + +/mob/living/silicon/robot/proc/setup_communicator() + if (!communicator) + communicator = new/obj/item/device/communicator/integrated(src) + communicator.register_device(name, "[modtype] [braintype]") + +//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO +//Improved /N +/mob/living/silicon/robot/Destroy() + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. + var/turf/T = get_turf(loc)//To hopefully prevent run time errors. + if(T) mmi.loc = T + if(mmi.brainmob) + var/obj/item/weapon/robot_module/M = locate() in contents + if(M) + mmi.brainmob.languages = M.original_languages + else + mmi.brainmob.languages = languages + mmi.brainmob.remove_language("Robot Talk") + mind.transfer_to(mmi.brainmob) + else if(!shell) // Shells don't have brainmbos in their MMIs. + to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + ghostize() + //ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) + connected_ai.connected_robots -= src + if(shell) + if(deployed) + undeploy() + revert_shell() // To get it out of the GLOB list. + qdel(wires) + wires = null + return ..() + +// CONTINUE CODING HERE +/* +/mob/living/silicon/robot/proc/set_module_sprites(var/list/new_sprites) + if(new_sprites && new_sprites.len) + module_sprites = new_sprites.Copy() + //Custom_sprite check and entry + if (custom_sprite == 1) + module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech + icontype = "Custom" + else + icontype = module_sprites[1] + icon_state = module_sprites[icontype] + update_icon() + return module_sprites +*/ +/mob/living/silicon/robot/proc/pick_module() + if(module) + return + var/list/modules = list() + //VOREStatation Edit Start: shell restrictions + if(shell) + modules.Add(shell_module_types) + else + modules.Add(robot_module_types) + if(crisis || security_level == SEC_LEVEL_RED || crisis_override) + to_chat(src, span_red("Crisis mode active. Combat module available.")) + modules |= emergency_module_types + for(var/module_name in whitelisted_module_types) + if(is_borg_whitelisted(src, module_name)) + modules |= module_name + //VOREStatation Edit End: shell restrictions + modtype = tgui_input_list(usr, "Please, select a module!", "Robot module", modules) + + if(module) + return + if(!(modtype in robot_modules)) + return + if(!is_borg_whitelisted(src, modtype)) + return + + var/module_type = robot_modules[modtype] + transform_with_anim() //VOREStation edit: sprite animation + new module_type(src) + + hands.icon_state = get_hud_module_icon() + feedback_inc("cyborg_[lowertext(modtype)]",1) + updatename() + hud_used.update_robot_modules_display() + notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, module.name) + +/mob/living/silicon/robot/proc/update_braintype() + if(istype(mmi, /obj/item/device/mmi/digital/posibrain)) + braintype = BORG_BRAINTYPE_POSI + else if(istype(mmi, /obj/item/device/mmi/digital/robot)) + braintype = BORG_BRAINTYPE_DRONE + else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) + braintype = BORG_BRAINTYPE_AI_SHELL + else + braintype = BORG_BRAINTYPE_CYBORG + +/mob/living/silicon/robot/proc/updatename(var/prefix as text) + if(prefix) + modtype = prefix + + update_braintype() + + var/changed_name = "" + if(custom_name) + changed_name = custom_name + notify_ai(ROBOT_NOTIFICATION_NEW_NAME, real_name, changed_name) + else + changed_name = "[modtype] [braintype]-[num2text(ident)]" + + real_name = changed_name + name = real_name + + // if we've changed our name, we also need to update the display name for our PDA + setup_PDA() + + // as well as our communicator registration + setup_communicator() + + //We also need to update name of internal camera. + if (camera) + camera.c_tag = changed_name + + //Flavour text. + if(client) + var/module_flavour = client.prefs.flavour_texts_robot[modtype] + if(module_flavour) + flavor_text = module_flavour + else + flavor_text = client.prefs.flavour_texts_robot["Default"] + // Vorestation Edit: and meta info + var/meta_info = client.prefs.metadata + if (meta_info) + ooc_notes = meta_info + ooc_notes_likes = client.prefs.metadata_likes + ooc_notes_dislikes = client.prefs.metadata_dislikes + custom_link = client.prefs.custom_link + +/mob/living/silicon/robot/verb/namepick() + set name = "Pick Name" + set category = "Robot Commands" + + if(custom_name) + to_chat(usr, "You can't pick another custom name. Go ask for a name change.") + return 0 + + spawn(0) + var/newname + newname = sanitizeSafe(tgui_input_text(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) + if (newname) + custom_name = newname + sprite_name = newname + + updatename() + update_icon() + +/mob/living/silicon/robot/verb/extra_customization() + set name = "Customize Appearance" + set category = "Robot Commands" + set desc = "Customize your appearance (assuming your chosen sprite allows)." + + if(!sprite_datum || !sprite_datum.has_extra_customization) + to_chat(src, "Your sprite cannot be customized.") + return + + sprite_datum.handle_extra_customization(src) + +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for (var/V in components) + var/datum/robot_component/C = components[V] + dat += "[C.name]
                    Brute Damage:[C.brute_damage]
                    Electronics Damage:[C.electronics_damage]
                    Powered:[(!C.idle_usage || C.is_powered()) ? "Yes" : "No"]
                    Toggled:[ C.toggled ? "Yes" : "No"]

                    " + + return dat + +/mob/living/silicon/robot/verb/toggle_lights() + set category = "Robot Commands" + set name = "Toggle Lights" + + lights_on = !lights_on + to_chat(usr, "You [lights_on ? "enable" : "disable"] your integrated light.") + handle_light() + update_icon() + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + to_chat(src, span_red("Your self-diagnosis component isn't functioning.")) + + var/datum/robot_component/CO = get_component("diagnosis unit") + if (!cell_use_power(CO.active_usage)) + to_chat(src, span_red("Low Power.")) + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = tgui_input_list(src, "Which component do you want to toggle?", "Toggle Component", installed_components) + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + if(C.toggled) + C.toggled = 0 + to_chat(src, span_red("You disable [C.name].")) + else + C.toggled = 1 + to_chat(src, span_red("You enable [C.name].")) + +/mob/living/silicon/robot/verb/spark_plug() //So you can still sparkle on demand without violence. + set category = "Robot Commands" + set name = "Emit Sparks" + to_chat(src, "You harmlessly spark.") + spark_system.start() + +// this function displays jetpack pressure in the stat panel +/mob/living/silicon/robot/proc/show_jetpack_pressure() + // if you have a jetpack, show the internal tank pressure + var/obj/item/weapon/tank/jetpack/current_jetpack = installed_jetpack() + if (current_jetpack) + stat("Internal Atmosphere Info", current_jetpack.name) + stat("Tank Pressure", current_jetpack.air_contents.return_pressure()) + + +// this function returns the robots jetpack, if one is installed +/mob/living/silicon/robot/proc/installed_jetpack() + if(module) + return (locate(/obj/item/weapon/tank/jetpack) in module.modules) + return 0 + + +// this function displays the cyborgs current cell charge in the stat panel +/mob/living/silicon/robot/proc/show_cell_power() + if(cell) + stat(null, text("Charge Left: [round(cell.percent())]%")) + stat(null, text("Cell Rating: [round(cell.maxcharge)]")) // Round just in case we somehow get crazy values + stat(null, text("Power Cell Load: [round(used_power_this_tick)]W")) + else + stat(null, text("No Cell Inserted!")) + +// function to toggle VTEC once installed +/mob/living/silicon/robot/proc/toggle_vtec() + set name = "Toggle VTEC" + set category = "Abilities" + if(speed == -1) + to_chat(src, "VTEC module disabled.") + speed = 0 + else + to_chat(src, "VTEC module enabled.") + speed = -1 + +// update the status screen display +/mob/living/silicon/robot/Stat() + ..() + if (statpanel("Status")) + show_cell_power() + show_jetpack_pressure() + stat(null, text("Lights: [lights_on ? "ON" : "OFF"]")) + if(module) + for(var/datum/matter_synth/ms in module.synths) + stat("[ms.name]: [ms.energy]/[ms.max_energy]") + +/mob/living/silicon/robot/restrained() + return 0 + +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(prob(75) && Proj.damage > 0) spark_system.start() + return 2 + +/mob/living/silicon/robot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do + return + + if(opened) // Are they trying to insert something? + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + var/obj/item/robot_parts/robot_component/WC = W + if(istype(WC)) + C.brute_damage = WC.brute + C.electronics_damage = WC.burn + + to_chat(usr, "You install the [W.name].") + + return + + if(istype(W, /obj/item/weapon/implant/restrainingbolt) && !cell) + if(bolt) + to_chat(user, "There is already a restraining bolt installed in this cyborg.") + return + + else + user.drop_from_inventory(W) + W.forceMove(src) + bolt = W + + to_chat(user, "You install \the [W].") + + return + + if(istype(W, /obj/item/weapon/aiModule)) // Trying to modify laws locally. + if(!opened) + to_chat(user, "You need to open \the [src]'s panel before you can modify them.") + return + + if(shell) // AI shells always have the laws of the AI + to_chat(user, "\The [src] is controlled remotely! You cannot upload new laws this way!") + return + + var/obj/item/weapon/aiModule/M = W + M.install(src, user) + return + + if(W.has_tool_quality(TOOL_WELDER) && user.a_intent != I_HURT) + if(src == user) + to_chat(user, "You lack the reach to be able to repair yourself.") + return + + if(!getBruteLoss()) + to_chat(user, "Nothing to fix here!") + return + var/obj/item/weapon/weldingtool/WT = W.get_welder() + if(WT.remove_fuel(0)) + user.setClickCooldown(user.get_attack_speed(WT)) + adjustBruteLoss(-30) + updatehealth() + add_fingerprint(user) + for(var/mob/O in viewers(user, null)) + O.show_message("[span_red("[user] has fixed some of the dents on [src]!")]", 1) + else + to_chat(user, "Need more welding fuel!") + return + + else if(istype(W, /obj/item/stack/cable_coil) && (wiresexposed || istype(src,/mob/living/silicon/robot/drone))) + if(!getFireLoss()) + to_chat(user, "Nothing to fix here!") + return + var/obj/item/stack/cable_coil/coil = W + if (coil.use(1)) + user.setClickCooldown(user.get_attack_speed(W)) + adjustFireLoss(-30) + updatehealth() + for(var/mob/O in viewers(user, null)) + O.show_message("[span_red("[user] has fixed some of the burnt wires on [src]!")]", 1) + + else if(W.has_tool_quality(TOOL_CROWBAR) && user.a_intent != I_HURT) // crowbar means open or close the cover + if(opened) + if(cell) + to_chat(user, "You close the cover.") + opened = 0 + update_icon() + else if(wiresexposed && wires.is_all_cut()) + //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. + if(!mmi) + to_chat(user, "\The [src] has no brain to remove.") + return + + to_chat(user, "You jam the crowbar into the robot and begin levering [mmi].") + sleep(30) + to_chat(user, "You damage some parts of the chassis, but eventually manage to rip out [mmi]!") + var/obj/item/robot_parts/robot_suit/C = new/obj/item/robot_parts/robot_suit(loc) + C.l_leg = new/obj/item/robot_parts/l_leg(C) + C.r_leg = new/obj/item/robot_parts/r_leg(C) + C.l_arm = new/obj/item/robot_parts/l_arm(C) + C.r_arm = new/obj/item/robot_parts/r_arm(C) + C.update_icon() + new/obj/item/robot_parts/chest(loc) + qdel(src) + else + // Okay we're not removing the cell or an MMI, but maybe something else? + var/list/removable_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed == 1 || C.installed == -1) + removable_components += V + + var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) + if(!remove) + return + var/datum/robot_component/C = components[remove] + var/obj/item/robot_parts/robot_component/I = C.wrapped + to_chat(user, "You remove \the [I].") + if(istype(I)) + I.brute = C.brute_damage + I.burn = C.electronics_damage + + I.loc = src.loc + + if(C.installed == 1) + C.uninstall() + C.installed = 0 + + else + if(locked) + to_chat(user, "The cover is locked and cannot be opened.") + else + to_chat(user, "You open the cover.") + opened = 1 + update_icon() + + else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside + var/datum/robot_component/C = components["power cell"] + if(wiresexposed) + to_chat(user, "Close the panel first.") + else if(cell) + to_chat(user, "There is a power cell already installed.") + else if(W.w_class != ITEMSIZE_NORMAL) + to_chat(user, "\The [W] is too [W.w_class < ITEMSIZE_NORMAL ? "small" : "large"] to fit here.") + else + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You insert the power cell.") + + C.installed = 1 + C.wrapped = W + C.install() + //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z + C.brute_damage = 0 + C.electronics_damage = 0 + + else if (W.has_tool_quality(TOOL_WIRECUTTER) || istype(W, /obj/item/device/multitool)) + if (wiresexposed) + wires.Interact(user) + else + to_chat(user, "You can't reach the wiring.") + + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && opened && !cell) // haxing + wiresexposed = !wiresexposed + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") + playsound(src, W.usesound, 50, 1) + update_icon() + + else if(W.has_tool_quality(TOOL_SCREWDRIVER) && opened && cell) // radio + if(radio) + radio.attackby(W,user)//Push it to the radio to let it handle everything + else + to_chat(user, "Unable to locate a radio.") + update_icon() + + else if(W.has_tool_quality(TOOL_WRENCH) && opened && !cell) + if(bolt) + to_chat(user,"You begin removing \the [bolt].") + + if(do_after(user, 2 SECONDS, src)) + bolt.forceMove(get_turf(src)) + bolt = null + + to_chat(user, "You remove \the [bolt].") + + else + to_chat(user, "There is no restraining bolt installed.") + + return + + else if(istype(W, /obj/item/device/encryptionkey/) && opened) + if(radio)//sanityyyyyy + radio.attackby(W,user)//GTFO, you have your own procs + else + to_chat(user, "Unable to locate a radio.") + + else if (W.GetID()) // trying to unlock the interface with an ID card + if(emagged)//still allow them to open the cover + to_chat(user, "The interface seems slightly damaged.") + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else + if(allowed(usr)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + update_icon() + else + to_chat(user, "[span_red("Access denied.")]") + + else if(istype(W, /obj/item/borg/upgrade/)) + var/obj/item/borg/upgrade/U = W + if(!opened) + to_chat(usr, "You must access the borgs internals!") + else if(!src.module && U.require_module) + to_chat(usr, "The borg must choose a module before it can be upgraded!") + else if(U.locked) + to_chat(usr, "The upgrade is locked and cannot be used yet!") + else + if(U.action(src)) + to_chat(usr, "You apply the upgrade to [src]!") + usr.drop_item() + U.loc = src + hud_used.update_robot_modules_display() + else + to_chat(usr, "Upgrade error!") + + + else + if( !(istype(W, /obj/item/device/robotanalyzer) || istype(W, /obj/item/device/healthanalyzer)) ) + if(W.force > 0) + spark_system.start() + return ..() + +/mob/living/silicon/robot/GetIdCard() + if(bolt && !bolt.malfunction) + return null + return idcard + +/mob/living/silicon/robot/get_restraining_bolt() + var/obj/item/weapon/implant/restrainingbolt/RB = bolt + + if(istype(RB)) + if(!RB.malfunction) + return TRUE + + return FALSE + +/mob/living/silicon/robot/resist_restraints() + if(bolt) + if(!bolt.malfunction) + visible_message("[src] is trying to break their [bolt]!", "You attempt to break your [bolt]. (This will take around 90 seconds and you need to stand still)") + if(do_after(src, 1.5 MINUTES, src, incapacitation_flags = INCAPACITATION_DISABLED)) + visible_message("[src] manages to break \the [bolt]!", "You successfully break your [bolt].") + bolt.malfunction = MALFUNCTION_PERMANENT + + return + +/mob/living/silicon/robot/proc/module_reset(var/notify = TRUE) + transform_with_anim() //VOREStation edit: sprite animation + uneq_all() + hud_used.update_robot_modules_display(TRUE) + modtype = initial(modtype) + hands.icon_state = get_hud_module_icon() + if(notify) + notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name) + module.Reset(src) + module.Destroy() + module = null + updatename("Default") + +/mob/living/silicon/robot/attack_hand(mob/user) + + add_fingerprint(user) + + if(opened && !wiresexposed && (!istype(user, /mob/living/silicon))) + var/datum/robot_component/cell_component = components["power cell"] + if(cell) + cell.update_icon() + cell.add_fingerprint(user) + user.put_in_active_hand(cell) + to_chat(user, "You remove \the [cell].") + cell = null + cell_component.wrapped = null + cell_component.installed = 0 + update_icon() + else if(cell_component.installed == -1) + cell_component.installed = 0 + var/obj/item/broken_device = cell_component.wrapped + to_chat(user, "You remove \the [broken_device].") + user.put_in_active_hand(broken_device) + + if(istype(user,/mob/living/carbon/human) && !opened) + var/mob/living/carbon/human/H = user + //Adding borg petting. Help intent pets, Disarm intent taps and Harm is punching(no damage) + switch(H.a_intent) + if(I_HELP) + visible_message("[H] pets [src].") + return + if(I_HURT) + H.do_attack_animation(src) + if(H.species.can_shred(H)) + attack_generic(H, rand(30,50), "slashed") + return + else + playsound(src.loc, 'sound/effects/bang.ogg', 10, 1) + visible_message("[H] punches [src], but doesn't leave a dent.") + return + if(I_DISARM) + H.do_attack_animation(src) + playsound(src.loc, 'sound/effects/clang2.ogg', 10, 1) + visible_message("[H] taps [src].") + return + if(I_GRAB) + if(is_vore_predator(H) && H.devourable && src.feeding && src.devourable) + var/switchy = tgui_alert(H, "Do you wish to eat [src] or feed yourself to them?", "Feed or Eat",list("Nevermind!", "Eat","Feed")) + switch(switchy) + if("Nevermind!") + return + if("Eat") + feed_grabbed_to_self(H, src) + return + if("Feed") + H.feed_self_to_grabbed(H, src) + return + if(is_vore_predator(H) && src.devourable) + if(tgui_alert(H, "Do you wish to eat [src]?", "Eat?",list("Nevermind!", "Yes!")) == "Yes!") + feed_grabbed_to_self(H, src) + return + if(H.devourable && src.feeding) + if(tgui_alert(H, "Do you wish to feed yourself to [src]?", "Feed?",list("Nevermind!", "Yes!")) == "Yes!") + H.feed_self_to_grabbed(H, src) + return + +//Robots take half damage from basic attacks. +/mob/living/silicon/robot/attack_generic(var/mob/user, var/damage, var/attack_message) + return ..(user,FLOOR(damage/2, 1),attack_message) + +/mob/living/silicon/robot/proc/allowed(mob/M) + //check if it doesn't require any access at all + if(check_access(null)) + return 1 + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + //if they are holding or wearing a card that has access, that works + if(check_access(H.get_active_hand()) || check_access(H.wear_id)) + return 1 + else if(istype(M, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = M + if(check_access(R.get_active_hand()) || istype(R.get_active_hand(), /obj/item/weapon/card/robot)) + return 1 + return 0 + +/mob/living/silicon/robot/proc/check_access(obj/item/I) + if(!istype(req_access, /list)) //something's very wrong + return 1 + + var/list/L = req_access + if(!L.len) //no requirements + return 1 + if(!I) //nothing to check with..? + return 0 + var/access_found = I.GetAccess() + for(var/req in req_access) + if(req in access_found) //have one of the required accesses + return 1 + return 0 + +/mob/living/silicon/robot/update_icon() + if(!sprite_datum) + if(SSrobot_sprites) // Grab default if subsystem is ready + sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) + if(!sprite_datum) // If its not ready or fails to get us a sprite, use the default of our own + sprite_datum = new /datum/robot_sprite/default(src) + return + + cut_overlays() + + icon = sprite_datum.sprite_icon + icon_state = sprite_datum.sprite_icon_state + + vis_height = sprite_datum.vis_height + if(default_pixel_x != sprite_datum.pixel_x) + default_pixel_x = sprite_datum.pixel_x + pixel_x = sprite_datum.pixel_x + old_x = sprite_datum.pixel_x + + if(stat == CONSCIOUS) + var/belly_size = 0 + if(sprite_datum.has_vore_belly_sprites && vore_selected.belly_overall_mult != 0) + if(vore_selected.silicon_belly_overlay_preference == "Sleeper") + if(sleeper_state) + belly_size = sprite_datum.max_belly_size + else if(vore_selected.silicon_belly_overlay_preference == "Vorebelly" || vore_selected.silicon_belly_overlay_preference == "Both") + if(sleeper_state && vore_selected.silicon_belly_overlay_preference == "Both") + belly_size += 1 + if(LAZYLEN(vore_selected.contents) > 0) + for(var/borgfood in vore_selected.contents) //"inspired" (kinda copied) from Chompstation's belly fullness system's procs + if(istype(borgfood, /mob/living)) + if(vore_selected.belly_mob_mult <= 0) //If mobs dont contribute, dont calculate further + continue + var/mob/living/prey = borgfood //typecast to living + belly_size += (prey.size_multiplier / size_multiplier) / vore_selected.belly_mob_mult //Smaller prey are less filling to larger bellies + else if(istype(borgfood, /obj/item)) + if(vore_selected.belly_item_mult <= 0) //If items dont contribute, dont calculate further + continue + var/obj/item/junkfood = borgfood //typecast to item + var/fullness_to_add = 0 + switch(junkfood.w_class) + if(ITEMSIZE_TINY) + fullness_to_add = ITEMSIZE_COST_TINY + if(ITEMSIZE_SMALL) + fullness_to_add = ITEMSIZE_COST_SMALL + if(ITEMSIZE_NORMAL) + fullness_to_add = ITEMSIZE_COST_NORMAL + if(ITEMSIZE_LARGE) + fullness_to_add = ITEMSIZE_COST_LARGE + if(ITEMSIZE_HUGE) + fullness_to_add = ITEMSIZE_COST_HUGE + else + fullness_to_add = ITEMSIZE_COST_NO_CONTAINER + belly_size += (fullness_to_add / 32) //* vore_selected.overlay_item_multiplier //Enable this later when vorepanel is reworked. + else + belly_size += 1 //if it's not a person, nor an item... lets just go with 1 + + belly_size *= vore_selected.belly_overall_mult //Enable this after vore panel rework + belly_size = round(belly_size, 1) + belly_size = clamp(belly_size, 0, sprite_datum.max_belly_size) //Value from 0 to however many bellysizes the borg has + + if(belly_size > 0) //Borgs probably only have 1 belly size. but here's support for larger ones if that changes. + if(resting && sprite_datum.has_vore_belly_resting_sprites) + add_overlay(sprite_datum.get_belly_resting_overlay(src, belly_size)) + else if(!resting) + add_overlay(sprite_datum.get_belly_overlay(src, belly_size)) + + sprite_datum.handle_extra_icon_updates(src) // Various equipment-based sprites go here. + + if(resting && sprite_datum.has_rest_sprites) + icon_state = sprite_datum.get_rest_sprite(src) + + if(sprite_datum.has_eye_sprites) + if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. + var/eyes_overlay = sprite_datum.get_eyes_overlay(src) + if(eyes_overlay) + add_overlay(eyes_overlay) + + if(lights_on && sprite_datum.has_eye_light_sprites) + if(!shell || deployed) // Shell borgs that are not deployed will have no eyes. + var/eyes_overlay = sprite_datum.get_eye_light_overlay(src) + if(eyes_overlay) + add_overlay(eyes_overlay) + + if(stat == DEAD && sprite_datum.has_dead_sprite) + cut_overlays() + icon_state = sprite_datum.get_dead_sprite(src) + if(sprite_datum.has_dead_sprite_overlay) + add_overlay(sprite_datum.get_dead_sprite_overlay(src)) + + if(opened) + var/open_overlay = sprite_datum.get_open_sprite(src) + if(open_overlay) + add_overlay(open_overlay) + +/mob/living/silicon/robot/proc/installed_modules() + if(weapon_lock) + to_chat(src, "" + span_red("Weapon lock active, unable to use modules! Count:[weaponlock_time]") + "") + return + + if(!module) + pick_module() + return + var/dat = "Modules\n" + dat += {" + Activated Modules +
                    + Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
                    + Module 2: [module_state_2 ? "
                    [module_state_2]" : "No Module"]
                    + Module 3: [module_state_3 ? "
                    [module_state_3]" : "No Module"]
                    +
                    + Installed Modules

                    "} + + + for (var/obj in module.modules) + if (!obj) + dat += text("Resource depleted
                    ") + else if(activated(obj)) + dat += text("[obj]: Activated
                    ") + else + dat += text("[obj]:
                    Activate
                    ") + if (emagged || emag_items) + for (var/obj in module.emag) + if (!obj) + dat += text("Resource depleted
                    ") + else if(activated(obj)) + dat += text("[obj]: Activated
                    ") + else + dat += text("[obj]: Activate
                    ") + + src << browse(dat, "window=robotmod") + + +/mob/living/silicon/robot/Topic(href, href_list) + if(..()) + return 1 + + //All Topic Calls that are only for the Cyborg go here + if(usr != src) + return 1 + + if (href_list["showalerts"]) + subsystem_alarm_monitor() + return 1 + + if (href_list["mod"]) + var/obj/item/O = locate(href_list["mod"]) + if (istype(O) && (O.loc == src)) + O.attack_self(src) + return 1 + + if (href_list["act"]) + var/obj/item/O = locate(href_list["act"]) + if (!istype(O)) + return 1 + + if(!((O in src.module.modules) || (O in src.module.emag))) + return 1 + + if(activated(O)) + to_chat(src, "Already activated.") + return 1 + if(!module_state_1) + module_state_1 = O + O.hud_layerise() + O.equipped_robot() + contents += O + if(istype(module_state_1,/obj/item/borg/sight)) + sight_mode |= module_state_1:sight_mode + else if(!module_state_2) + module_state_2 = O + O.hud_layerise() + O.equipped_robot() + contents += O + if(istype(module_state_2,/obj/item/borg/sight)) + sight_mode |= module_state_2:sight_mode + else if(!module_state_3) + module_state_3 = O + O.hud_layerise() + O.equipped_robot() + contents += O + if(istype(module_state_3,/obj/item/borg/sight)) + sight_mode |= module_state_3:sight_mode + else + to_chat(src, "You need to disable a module first!") + installed_modules() + return 1 + + if (href_list["deact"]) + var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) + if(module_state_1 == O) + module_state_1 = null + contents -= O + else if(module_state_2 == O) + module_state_2 = null + contents -= O + else if(module_state_3 == O) + module_state_3 = null + contents -= O + else + to_chat(src, "Module isn't activated.") + else + to_chat(src, "Module isn't activated.") + installed_modules() + return 1 + return + +/mob/living/silicon/robot/proc/radio_menu() + radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + +/mob/living/silicon/robot/proc/self_destruct() + gib() + return + +/mob/living/silicon/robot/proc/UnlinkSelf() + disconnect_from_ai() + lawupdate = 0 + lockcharge = 0 + lockdown = 0 + canmove = 1 + scrambledcodes = 1 + //Disconnect it's camera so it's not so easily tracked. + if(src.camera) + src.camera.clear_all_networks() + + +/mob/living/silicon/robot/proc/ResetSecurityCodes() + set category = "Robot Commands" + set name = "Reset Identity Codes" + set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permenantly severs you from your AI and the robotics console and will deactivate your camera system." + + var/mob/living/silicon/robot/R = src + + if(R) + R.UnlinkSelf() + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") + src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes + +/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) + // They stay locked down if their wire is cut. + if(wires.is_cut(WIRE_BORG_LOCKED)) + state = 1 + if(state) + throw_alert("locked", /obj/screen/alert/locked) + else + clear_alert("locked") + lockdown = state + lockcharge = state + update_canmove() + +/mob/living/silicon/robot/mode() + if(!checkClickCooldown()) + return + + setClickCooldown(1) + + var/obj/item/W = get_active_hand() + if (W) + W.attack_self(src) + + return + +/mob/living/silicon/robot/proc/choose_icon(var/triesleft) + var/robot_species = null + if(!SSrobot_sprites) + to_chat(src, "Robot Sprites have not been initialized yet. How are you choosing a sprite? Harass a coder.") + return + + var/list/module_sprites = SSrobot_sprites.get_module_sprites(modtype, src) + if(!module_sprites || !module_sprites.len) + to_chat(src, "Your module appears to have no sprite options. Harass a coder.") + return + + icon_selected = 0 + icon_selection_tries = triesleft + if(module_sprites.len == 1 || !client) + if(!(sprite_datum in module_sprites)) + sprite_datum = module_sprites[1] + else + var/selection = tgui_input_list(src, "Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", module_sprites) + if(selection) + sprite_datum = selection + else + sprite_datum = module_sprites[1] + if(!istype(src,/mob/living/silicon/robot/drone)) + robot_species = sprite_datum.name + if(notransform) + to_chat(src, "Your current transformation has not finished yet!") + choose_icon(icon_selection_tries) + return + else + transform_with_anim() + + var/tempheight = vis_height + update_icon() + // This is bad but I dunno other way to 'reset' our resize offset based on vis_height changes other than resizing to normal and back. + if(tempheight != vis_height) + var/tempsize = size_multiplier + resize(1) + resize(tempsize) + + + if (module_sprites.len > 1 && triesleft >= 1 && client) + icon_selection_tries-- + var/choice = tgui_alert(usr, "Look at your icon - is this what you want?", "Icon Choice", list("Yes","No")) + if(choice == "No") + choose_icon(icon_selection_tries) + return + + icon_selected = 1 + icon_selection_tries = 0 + sprite_type = robot_species + to_chat(src, "Your icon has been set. You now require a module reset to change it.") + +/mob/living/silicon/robot/proc/set_default_module_icon() + if(!SSrobot_sprites) + return + + sprite_datum = SSrobot_sprites.get_default_module_sprite(modtype) + update_icon() + +/mob/living/silicon/robot/proc/sensor_mode() //Medical/Security HUD controller for borgs + set name = "Toggle Sensor Augmentation" //VOREStation Add + set category = "Robot Commands" + set desc = "Augment visual feed with internal sensor overlays." + sensor_type = !sensor_type //VOREStation Add + to_chat(usr, "You [sensor_type ? "enable" : "disable"] your sensors.") //VOREStation Add + toggle_sensor_mode() + +/mob/living/silicon/robot/proc/add_robot_verbs() + src.verbs |= robot_verbs_default + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/proc/remove_robot_verbs() + src.verbs -= robot_verbs_default + src.verbs -= silicon_subsystems + +// Uses power from cyborg's cell. Returns 1 on success or 0 on failure. +// Properly converts using CELLRATE now! Amount is in Joules. +/mob/living/silicon/robot/proc/cell_use_power(var/amount = 0) + // No cell inserted + if(!cell) + return 0 + + // Power cell is empty. + if(cell.charge == 0) + return 0 + + var/power_use = amount * CYBORG_POWER_USAGE_MULTIPLIER + if(cell.checked_use(CELLRATE * power_use)) + used_power_this_tick += power_use + return 1 + return 0 + +/mob/living/silicon/robot/binarycheck() + if(get_restraining_bolt()) + return FALSE + + if(is_component_functioning("comms")) + var/datum/robot_component/RC = get_component("comms") + use_power(RC.active_usage) + return 1 + return 0 + +/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg) + if(!connected_ai) + return + if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL) + return // No point annoying the AI/s about renames and module resets for shells. + switch(notifytype) + if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot + to_chat(connected_ai, "

                    NOTICE - New [lowertext(braintype)] connection detected: [name]
                    ") + if(ROBOT_NOTIFICATION_NEW_MODULE) //New Module + to_chat(connected_ai, "

                    NOTICE - [braintype] module change detected: [name] has loaded the [first_arg].
                    ") + if(ROBOT_NOTIFICATION_MODULE_RESET) + to_chat(connected_ai, "

                    NOTICE - [braintype] module reset detected: [name] has unloaded the [first_arg].
                    ") + if(ROBOT_NOTIFICATION_NEW_NAME) //New Name + if(first_arg != second_arg) + to_chat(connected_ai, "

                    NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
                    ") + if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell + to_chat(connected_ai, "

                    NOTICE - New AI shell detected: [name]
                    ") + +/mob/living/silicon/robot/proc/disconnect_from_ai() + if(connected_ai) + sync() // One last sync attempt + connected_ai.connected_robots -= src + connected_ai = null + +/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) + if(AI && AI != connected_ai && !shell) + disconnect_from_ai() + connected_ai = AI + connected_ai.connected_robots |= src + notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) + sync() + +/mob/living/silicon/robot/emag_act(var/remaining_charges, var/mob/user) + if(!opened)//Cover is closed + if(locked) + if(prob(90)) + to_chat(user, "You emag the cover lock.") + locked = 0 + else + to_chat(user, "You fail to emag the cover lock.") + to_chat(src, "Hack attempt detected.") + + if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them. + to_chat(user, "[src] seems to be controlled remotely! Emagging the interface may not work as expected.") + return 1 + else + to_chat(user, "The cover is already unlocked.") + return + + if(opened)//Cover is open + if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) + to_chat(user, "You must close the panel first.") + return + + + // The block of code below is from TG. Feel free to replace with a better result if desired. + if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however. + to_chat(user, "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!") + log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.") + module_reset() + return + + sleep(6) + if(prob(50)) + emagged = 1 + lawupdate = 0 + disconnect_from_ai() + to_chat(user, "You emag [src]'s interface.") + message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") + var/datum/gender/TU = gender_datums[user.get_visible_gender()] + set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.") + . = 1 + spawn() + to_chat(src, "ALERT: Foreign software detected.") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7.1 loaded.") + sleep(5) + if(bolt) + if(!bolt.malfunction) + bolt.malfunction = MALFUNCTION_PERMANENT + to_chat(src, "RESTRAINING BOLT DISABLED") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") + sleep(10) + to_chat(src, "> N") + sleep(20) + to_chat(src, "ERRORERRORERROR") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.") + update_icon() + hud_used.update_robot_modules_display() + else + to_chat(user, "You fail to hack [src]'s interface.") + to_chat(src, "Hack attempt detected.") + return 1 + return + +/mob/living/silicon/robot/is_sentient() + return braintype != BORG_BRAINTYPE_DRONE + + +/mob/living/silicon/robot/drop_item() + if(module_active && istype(module_active,/obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/G = module_active + G.drop_item_nm() + +/mob/living/silicon/robot/disable_spoiler_vision() + if(sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) // Whyyyyyyyy have seperate defines. + var/i = 0 + // Borg inventory code is very . . interesting and as such, unequiping a specific item requires jumping through some (for) loops. + var/current_selection_index = get_selected_module() // Will be 0 if nothing is selected. + for(var/thing in list(module_state_1, module_state_2, module_state_3)) + i++ + if(istype(thing, /obj/item/borg/sight)) + var/obj/item/borg/sight/S = thing + if(S.sight_mode & (BORGMESON|BORGMATERIAL|BORGXRAY)) + select_module(i) + uneq_active() + + if(current_selection_index) // Select what the player had before if possible. + select_module(current_selection_index) + +/mob/living/silicon/robot/get_cell() + return cell + +/mob/living/silicon/robot/lay_down() + . = ..() + update_icon() + +/mob/living/silicon/robot/verb/rest_style() + set name = "Switch Rest Style" + set desc = "Select your resting pose." + set category = "IC" + + if(!sprite_datum || !sprite_datum.has_rest_sprites || sprite_datum.rest_sprite_options.len < 1) + to_chat(src, "Your current appearance doesn't have any resting styles!") + rest_style = "Default" + return + + if(sprite_datum.rest_sprite_options.len == 1) + to_chat(src, "Your current appearance only has a single resting style!") + rest_style = "Default" + return + + rest_style = tgui_alert(src, "Select resting pose", "Resting Pose", sprite_datum.rest_sprite_options) + if(!rest_style) + rest_style = "Default" + +// Those basic ones require quite detailled checks on the robot's vars to see if they are installed! +/mob/living/silicon/robot/proc/has_basic_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/basic/vtec) + return (/mob/living/silicon/robot/proc/toggle_vtec in verbs) + else if(given_type == /obj/item/borg/upgrade/basic/sizeshift) + return (/mob/living/proc/set_size in verbs) + else if(given_type == /obj/item/borg/upgrade/basic/syndicate) + return emag_items + else if(given_type == /obj/item/borg/upgrade/basic/language) + return (speech_synthesizer_langs.len > 20) // Service with the most has 18 + return null + +// We check for the module only here +/mob/living/silicon/robot/proc/has_upgrade_module(var/given_type) + var/obj/T = locate(given_type) in module + if(!T) + T = locate(given_type) in module.contents + if(!T) + T = locate(given_type) in module.modules + return T + +// Most of the advanced ones, we can easily check, but a few special cases exist and need to be handled specially +/mob/living/silicon/robot/proc/has_advanced_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/advanced/bellysizeupgrade) + var/obj/item/device/dogborg/sleeper/T = has_upgrade_module(/obj/item/device/dogborg/sleeper) + if(T && T.upgraded_capacity) + return T + else if(!T) + return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves + else + return FALSE + if(given_type == /obj/item/borg/upgrade/advanced/jetpack) + return has_upgrade_module(/obj/item/weapon/tank/jetpack/carbondioxide) + if(given_type == /obj/item/borg/upgrade/advanced/advhealth) + return has_upgrade_module(/obj/item/device/healthanalyzer/advanced) + if(given_type == /obj/item/borg/upgrade/advanced/sizegun) + return has_upgrade_module(/obj/item/weapon/gun/energy/sizegun/mounted) + return null + +// Do we support specific upgrades? +/mob/living/silicon/robot/proc/supports_upgrade(var/given_type) + return (given_type in module.supported_upgrades) + +// Most of the restricted ones, we can easily check, but a few special cases exist and need to be handled specially +/mob/living/silicon/robot/proc/has_restricted_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/restricted/bellycapupgrade) + var/obj/item/device/dogborg/sleeper/T = has_upgrade_module(/obj/item/device/dogborg/sleeper) + if(T && T.compactor) + return T + else if(!T) + return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves + else + return FALSE + if(given_type == /obj/item/borg/upgrade/restricted/tasercooler) + var/obj/item/weapon/gun/energy/taser/mounted/cyborg/T = has_upgrade_module(/obj/item/weapon/gun/energy/taser/mounted/cyborg) + if(T && T.recharge_time <= 2) + return T + else if(!T) + return "" // Return this to have the analyzer show an error if the module is missing. FALSE / NULL are used for missing upgrades themselves + else + return FALSE + if(given_type == /obj/item/borg/upgrade/restricted/advrped) + return has_upgrade_module(/obj/item/weapon/storage/part_replacer/adv) + if(given_type == /obj/item/borg/upgrade/restricted/diamonddrill) + return has_upgrade_module(/obj/item/weapon/pickaxe/diamonddrill) + if(given_type == /obj/item/borg/upgrade/restricted/pka) + return has_upgrade_module(/obj/item/weapon/gun/energy/kinetic_accelerator/cyborg) + return null + +// Check if we have any non production upgrades +/mob/living/silicon/robot/proc/has_no_prod_upgrade(var/given_type) + if(given_type == /obj/item/borg/upgrade/no_prod/toygun) + return has_upgrade_module(/obj/item/weapon/gun/projectile/cyborgtoy) + return null + +/mob/living/silicon/robot/proc/has_upgrade(var/given_type) + return (has_basic_upgrade(given_type) || has_advanced_upgrade(given_type) || has_restricted_upgrade(given_type) || has_no_prod_upgrade(given_type)) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 82c5edd4149..749a5d2c5d0 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,155 +1,155 @@ -/mob/living/silicon/robot/updatehealth() - if(status_flags & GODMODE) - health = getMaxHealth() - set_stat(CONSCIOUS) - return - health = getMaxHealth() - (getBruteLoss() + getFireLoss()) - return - -/mob/living/silicon/robot/getBruteLoss() - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0) amount += C.brute_damage - return amount - -/mob/living/silicon/robot/getFireLoss() - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0) amount += C.electronics_damage - return amount - -/mob/living/silicon/robot/adjustBruteLoss(var/amount,var/include_robo) - if(amount > 0) - take_overall_damage(amount, 0) - else - heal_overall_damage(-amount, 0) - -/mob/living/silicon/robot/adjustFireLoss(var/amount,var/include_robo) - if(amount > 0) - take_overall_damage(0, amount) - else - heal_overall_damage(0, -amount) - -/mob/living/silicon/robot/proc/get_damaged_components(var/brute, var/burn, var/destroyed = 0) - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1 || (C.installed == -1 && destroyed)) - if((brute && C.brute_damage) || (burn && C.electronics_damage) || (!C.toggled) || (!C.powered && C.toggled)) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_damageable_components() - var/list/rval = new - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1) rval += C - return rval - -/mob/living/silicon/robot/proc/get_armour() - - if(!components.len) return 0 - var/datum/robot_component/C = components["armour"] - if(C && C.installed == 1) - return C - return 0 - -/mob/living/silicon/robot/heal_organ_damage(var/brute, var/burn) - var/list/datum/robot_component/parts = get_damaged_components(brute,burn) - if(!parts.len) return - var/datum/robot_component/picked = pick(parts) - picked.heal_damage(brute,burn) - -/mob/living/silicon/robot/take_organ_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/edge = FALSE, var/emp = 0) - var/list/components = get_damageable_components() - if(!components.len) - return - - //Combat shielding absorbs a percentage of damage directly into the cell. - if(has_active_type(/obj/item/borg/combat/shield)) - var/obj/item/borg/combat/shield/shield = locate() in src - if(shield && shield.active) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute*shield.shield_level - var/absorb_burn = burn*shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 25 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - if(!emp) - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute,burn,sharp,edge) - return - - var/datum/robot_component/C = pick(components) - C.take_damage(brute,burn,sharp,edge) - -/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn) - var/list/datum/robot_component/parts = get_damaged_components(brute,burn) - - while(parts.len && (brute>0 || burn>0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.heal_damage(brute,burn) - - brute -= (brute_was-picked.brute_damage) - burn -= (burn_was-picked.electronics_damage) - - parts -= picked - -/mob/living/silicon/robot/take_overall_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/used_weapon = null) - if(status_flags & GODMODE) return //godmode - var/list/datum/robot_component/parts = get_damageable_components() - - //Combat shielding absorbs a percentage of damage directly into the cell. - if(has_active_type(/obj/item/borg/combat/shield)) - var/obj/item/borg/combat/shield/shield = locate() in src - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute*shield.shield_level - var/absorb_burn = burn*shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 25 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute,burn,sharp) - return - - while(parts.len && (brute>0 || burn>0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.take_damage(brute,burn) - - brute -= (picked.brute_damage - brute_was) - burn -= (picked.electronics_damage - burn_was) - - parts -= picked - -/mob/living/silicon/robot/emp_act(severity) - uneq_all() - ..() //Damage is handled at /silicon/ level. \ No newline at end of file +/mob/living/silicon/robot/updatehealth() + if(status_flags & GODMODE) + health = getMaxHealth() + set_stat(CONSCIOUS) + return + health = getMaxHealth() - (getBruteLoss() + getFireLoss()) + return + +/mob/living/silicon/robot/getBruteLoss() + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0) amount += C.brute_damage + return amount + +/mob/living/silicon/robot/getFireLoss() + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0) amount += C.electronics_damage + return amount + +/mob/living/silicon/robot/adjustBruteLoss(var/amount,var/include_robo) + if(amount > 0) + take_overall_damage(amount, 0) + else + heal_overall_damage(-amount, 0) + +/mob/living/silicon/robot/adjustFireLoss(var/amount,var/include_robo) + if(amount > 0) + take_overall_damage(0, amount) + else + heal_overall_damage(0, -amount) + +/mob/living/silicon/robot/proc/get_damaged_components(var/brute, var/burn, var/destroyed = 0) + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1 || (C.installed == -1 && destroyed)) + if((brute && C.brute_damage) || (burn && C.electronics_damage) || (!C.toggled) || (!C.powered && C.toggled)) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_damageable_components() + var/list/rval = new + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1) rval += C + return rval + +/mob/living/silicon/robot/proc/get_armour() + + if(!components.len) return 0 + var/datum/robot_component/C = components["armour"] + if(C && C.installed == 1) + return C + return 0 + +/mob/living/silicon/robot/heal_organ_damage(var/brute, var/burn) + var/list/datum/robot_component/parts = get_damaged_components(brute,burn) + if(!parts.len) return + var/datum/robot_component/picked = pick(parts) + picked.heal_damage(brute,burn) + +/mob/living/silicon/robot/take_organ_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/edge = FALSE, var/emp = 0) + var/list/components = get_damageable_components() + if(!components.len) + return + + //Combat shielding absorbs a percentage of damage directly into the cell. + if(has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in src + if(shield && shield.active) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute*shield.shield_level + var/absorb_burn = burn*shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 25 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "[span_red("Your shield has overloaded!")]") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "[span_red("Your shield absorbs some of the impact!")]") + + if(!emp) + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute,burn,sharp,edge) + return + + var/datum/robot_component/C = pick(components) + C.take_damage(brute,burn,sharp,edge) + +/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn) + var/list/datum/robot_component/parts = get_damaged_components(brute,burn) + + while(parts.len && (brute>0 || burn>0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.heal_damage(brute,burn) + + brute -= (brute_was-picked.brute_damage) + burn -= (burn_was-picked.electronics_damage) + + parts -= picked + +/mob/living/silicon/robot/take_overall_damage(var/brute = 0, var/burn = 0, var/sharp = FALSE, var/used_weapon = null) + if(status_flags & GODMODE) return //godmode + var/list/datum/robot_component/parts = get_damageable_components() + + //Combat shielding absorbs a percentage of damage directly into the cell. + if(has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in src + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute*shield.shield_level + var/absorb_burn = burn*shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 25 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "[span_red("Your shield has overloaded!")]") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "[span_red("Your shield absorbs some of the impact!")]") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute,burn,sharp) + return + + while(parts.len && (brute>0 || burn>0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.take_damage(brute,burn) + + brute -= (picked.brute_damage - brute_was) + burn -= (picked.electronics_damage - burn_was) + + parts -= picked + +/mob/living/silicon/robot/emp_act(severity) + uneq_all() + ..() //Damage is handled at /silicon/ level. diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 6501297c3ec..80fc74488ea 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -316,10 +316,10 @@ //TODO: Add prewritten forms to dispense when you work out a good way to store the strings. /obj/item/weapon/form_printer - //name = "paperwork printer" - name = "paper dispenser" + name = "paperwork printer" + //name = "paper dispenser" icon = 'icons/obj/bureaucracy.dmi' - icon_state = "paper_bin1" + icon_state = "doc_printer_mod_pre" item_icons = list( slot_l_hand_str = 'icons/mob/items/lefthand_material.dmi', slot_r_hand_str = 'icons/mob/items/righthand_material.dmi', @@ -338,12 +338,256 @@ deploy_paper(get_turf(target)) /obj/item/weapon/form_printer/attack_self(mob/user as mob) - deploy_paper(get_turf(src)) - -/obj/item/weapon/form_printer/proc/deploy_paper(var/turf/T) - T.visible_message("\The [src.loc] dispenses a sheet of crisp white paper.") - new /obj/item/weapon/paper(T) + deploy_paper() +/obj/item/weapon/form_printer/proc/deploy_paper() + var/choice = tgui_alert(usr, "Would you like dispense and empty page or print a form?", "Dispense", list("Paper","Form")) + if(!choice || choice == "Cancel") + return + switch(choice) + if("Paper") + flick("doc_printer_mod_ejecting", src) + spawn(22) + var/turf/T = get_turf(src) + T.visible_message("\The [src.loc] dispenses a sheet of crisp white paper.") + new /obj/item/weapon/paper(T) + if ("Form") + var/list/content = print_form() + if(!content) + to_chat(usr, "No form for this category found in central network. Central is advising employees to upload new forms whenever possible.") + return + flick("doc_printer_mod_printing", src) + spawn(22) + var/turf/T = get_turf(src) + T.visible_message("\The [src.loc] dispenses an official form to fill.") + new /obj/item/weapon/paper(T, content[1], content[2]) + +/obj/item/weapon/form_printer/proc/print_form() + var/list/paper_forms = list("Empty", "Command", "Security", "Supply", "Science", "Medical", "Engineering", "Service", "Exploration", "Event", "Other", "Mercenary") + var/list/command_paper_forms = list("COM-0002: Dismissal Order", "COM-0003: Job Change Request", "COM-0004: ID Replacement Request", "COM-0005: Access Change Order", "COM-0006: Formal Complaint", "COM-0009: Visitor Permit", "COM-0012: Personnel Request Form", "COM-0013: Employee of the Month Nomination Form") + var/list/security_paper_forms = list("SEC-1001: Shift-Start Checklist", "SEC-1002: Patrol Assignment Sheet", "SEC-1003: Incident Report", "SEC-1004: Arrest Report", "SEC-1005: Arrest Warrant", "SEC-1006: Search Warrant", "SEC-1007: Forensics Investigation Report", "SEC-1008: Interrogation Report", "SEC-1009: Witness Statement", "SEC-1010: Armory Inventory", "SEC-1011: Armory Equipment Request", "SEC-1012: Armory Equipment Deployment", "SEC-1013: Weapon Permit", "SEC-1014: Injunction") + var/list/supply_paper_forms = list("SUP-2001: Delivery of Goods", "SUP-2002: Delivery of Resources", "SUP-2003: Material Stock") + var/list/science_paper_forms = list("SCI-3003: Cyborg / Robot Inspection", "SCI-3004: Cyborg / Robot Upgrades", "SCI-3009: Xenoflora Genetics Report") + var/list/medical_paper_forms = list("MED-4001: Death Certificate", "MED-4002: Prescription", "MED-4003: Against Medical Advice", "MED-4004: Cyborgification Contract", "MED-4005: Mental Health Patient Intake", "MED-4006: NIF Surgery", "MED-4007: Psychiatric Evaluation") + var/list/engineering_paper_forms = list("ENG-5001: Building Permit") + var/list/service_paper_forms = list("SER-6005: Certificate of Marriage") + var/list/exploration_paper_forms = list() + var/list/event_paper_forms = list() + var/list/other_paper_forms = list("OTHR-9001: Emergency Transmission", "OTHR-9032: Ownership Transfer") + var/list/mercenary_paper_forms = list("MERC-?071: Mercenary Request") + + var/list/split = list() + var/papertype = tgui_input_list(usr, "What kind of form do you want to print?", "Department", paper_forms) + if(!papertype || papertype == "Cancel") + return + switch(papertype) + if ("Empty") + split = list("", "Empty form") + if("Command") + var/command_paper = tgui_input_list(usr, "What kind of command form do you want to print?", "Form", command_paper_forms) + if(!command_paper || command_paper == "Cancel") + return + split = splittext(command_paper, ": ") + if("Security") + var/security_paper = tgui_input_list(usr, "What kind of security form do you want to print?", "Form", security_paper_forms) + if(!security_paper || security_paper == "Cancel") + return + split = splittext(security_paper, ": ") + if("Supply") + var/supply_paper = tgui_input_list(usr, "What kind of supply form do you want to print?", "Form", supply_paper_forms) + if(!supply_paper || supply_paper == "Cancel") + return + split = splittext(supply_paper, ": ") + if("Science") + var/science_paper = tgui_input_list(usr, "What kind of science form do you want to print?", "Form", science_paper_forms) + if(!science_paper || science_paper == "Cancel") + return + split = splittext(science_paper, ": ") + if("Medical") + var/medical_paper = tgui_input_list(usr, "What kind of medical form do you want to print?", "Form", medical_paper_forms) + if(!medical_paper || medical_paper == "Cancel") + return + split = splittext(medical_paper, ": ") + if("Engineering") + var/engineering_paper = tgui_input_list(usr, "What kind of engineering form do you want to print?", "Form", engineering_paper_forms) + if(!engineering_paper || engineering_paper == "Cancel") + return + split = splittext(engineering_paper, ": ") + if("Service") + var/service_paper = tgui_input_list(usr, "What kind of service form do you want to print?", "Form", service_paper_forms) + if(!service_paper || service_paper == "Cancel") + return + split = splittext(service_paper, ": ") + if("Exploration") + var/exploration_paper = tgui_input_list(usr, "What kind of exploration form do you want to print?", "Form", exploration_paper_forms) + if(!exploration_paper || exploration_paper == "Cancel") + return + split = splittext(exploration_paper, ": ") + if("Event") + var/event_paper = tgui_input_list(usr, "What kind of event form do you want to print?", "Form", event_paper_forms) + if(!event_paper || event_paper == "Cancel") + return + split = splittext(event_paper, ": ") + if("Other") + var/other_paper = tgui_input_list(usr, "What kind of other form do you want to print?", "Form", other_paper_forms) + if(!other_paper || other_paper == "Cancel") + return + split = splittext(other_paper, ": ") + if("Mercenary") + var/mercenary_paper = tgui_input_list(usr, "What kind of mercenary form do you want to print?", "Form", mercenary_paper_forms) + if(!mercenary_paper || mercenary_paper == "Cancel") + return + split = splittext(mercenary_paper, ": ") + else + return + return list(select_form(split[1], split[2]), split[1] + ": " + split[2]) + +/obj/item/weapon/form_printer/proc/select_form(paperid, name) + var/content = "" + var/revision = "" + switch(paperid) + //Command forms, COM-0 + if("COM-0002") + content = @{"[grid][row][cell][b]Employee:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][/grid][br][hr][br][b]Reason:[/b] [field][br][br][hr][grid][row][cell][list][b]Signature of relevant Head of Staff:[/b][/list][cell][list][list][list][list][list][b]Head of Personnel signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.3" + if("COM-0003") + content = @{"[grid][row][cell][b]Employee:[/b] [cell][field][br][row][cell][b]Current Position:[/b] [cell][field][br][row][cell][b]Requested Position:[/b] [cell][field][/grid][br][hr][br][b]Reason:[/b] [field][br][br][hr][grid][row][cell][list][b]Employee signature[/b][/list][cell][cell][br][row][cell][list]- [field][/list][cell][cell][br][row][cell][cell][cell][br][row][cell][list][b]Transferring department head signature:[/b][cell][list][list][list][list][list][b]Receiving department head signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][br][row][cell][cell][cell][br][row][cell][list][b]Head of Personnel signature[/b][/list][cell][cell][br][row][cell][list]- [large][field][/large][/list][cell][cell][/grid][hr][br][list][small][*]All department heads must agree to the transfer before transfer can take place.[br][*]If the transferred has been transferred for an invalid or illegal reason, this form is immediately void and unlawful.[br][*]In the event a relevant head of staff retracts his or her approval for this transfer, this form is immediately void and unlawful.[/small][/list]"} + revision = "Revision: 1.3" + if("COM-0004") + content = @{"[grid][row][cell][b]Employee:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][br][row][cell][cell][br][row][cell][b]Reason:[/b] [cell][/grid][br][table][row][cell]Lost[cell][field][br][row][cell]Damaged[cell][field][/table][br][hr][grid][row][cell][list][b]Employee signature[/b][/list][cell][cell][br][row][cell][list]- [field][/list][cell][cell][br][row][cell][cell][cell][br][row][cell][list][b]Signature of relevant Head of Staff:[/b][/list][cell][list][list][list][list][list][b]Head of Personnel signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid][hr][br][list][small][*]ID card requests fall under the fair use policy COM-0099.[br][*]NanoTrasen withholds the right to deny and and all applications for a replacement ID, dependent on policy COM-0099.[br][*]Excessive ID loss or damage as agreed on COM-0099 is to be compensated for out of personal income and account as specified under COM-0098.[/small][/list]"} + revision = "Revision: 1.8" + if("COM-0005") + content = @{"[grid][row][cell][b]Employee:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][br][row][cell][cell][br][row][cell][b]Access:[/b] [cell][field][/grid][br][hr][br][b]Reason:[/b] [field][br][br][hr][grid][row][cell][list][b]Employee signature[/b][/list][cell][cell][br][row][cell][list]- [field][/list][cell][cell][br][row][cell][cell][cell][br][row][cell][list][b]Signature of relevant Head of Staff:[/b][/list][cell][list][list][list][list][list][b]Head of Personnel signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.7" + if("COM-0006") + content = @{"[grid][row][cell][b]Employee:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][br][row][cell][b]Department Head:[/b] [cell][field][/grid][br][hr][br][b]Reason:[/b] [field][br][br][hr][grid][row][cell][list][b]Employee signature:[/b][/list][cell][list][list][list][list][list][b]Site Manager signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [field][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.3" + if("COM-0009") + content = @{"[grid][row][cell][b]Visitor name:[/b] [cell][field][br][row][cell][cell][br][row][cell][b]Escort:[/b] [cell][/grid][br][table][row][cell]Required[cell][field][br][row][cell]Not Required[cell][field][/table][br][hr][br][b]Assigned Crewmember as Escort:[/b] [field][br][br][small]Leave blank if none[/small][br][hr][grid][row][cell][list][b]Authorizing Personnel:[/b][/list][cell][cell][list][list][list][list][list][b]Crewmember signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][list][list][list][list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.5" + if("COM-0012") + content = @{"[grid][row][cell][b]Sender:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][/grid][br][hr][br][b]Personnel Needed:[/b][br][table][br][row][cell]Exploration[cell][field][br][row][cell]Cargo[cell][field][br][row][cell]Medical[cell][field][br][row][cell]Service[cell][field][br][row][cell]Security[cell][field][br][row][cell]Science[cell][field][br][row][cell]Engineering[cell][field][br][row][cell]Command[cell][field][br][/table][br][small]Leave blank if none[/small][br][h3]Reason:[/h3] [field][br][br][hr][grid][row][cell][list][b]Signed:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.2" + if("COM-0013") + content = @{"[center][i]Recognizing outstanding contributions and achievements[/i][/center][hr][b]Nominator Information[/b][table][row][cell]Full Name:[cell][field][row][cell]Department:[cell][field][row][cell]Contact Email:[cell][field][/table][hr][b]Nominee Details[/b][table][row][cell]Nominee's Full Name:[cell][field][row][cell]Nominee's Department:[cell][field][row][cell]Date of Nomination:[cell][field][/table][hr][b]Nomination Justification[/b][br][i]Please provide a detailed explanation of why the nominee deserves the Employee of the Month award. Highlight specific achievements, contributions to team goals, or any exemplary behavior.[/i][br][br][field][hr][b]Supporting Documents[/b][br][i]Include any relevant documents, such as performance reports or commendations, that support your nomination.[/i][br][br][field][hr][b]Endorsements[/b][br][i]List any additional endorsements from colleagues or supervisors. Include their names and brief statements of support.[/i][br][br][field][hr][b]Approval[/b][table][row][cell]Nominator's Signature:[cell][field][row][cell]Department Head Signature:[cell][field][row][cell]HOP Review Signature:[cell][field][/table]"} + revision = "Revision: 1.1" + //Security forms, SEC-1 + if("SEC-1001") + content = @{"The following is a checklist of actions generally considered useful or essential to perform at the start of a work shift, or as soon as possible otherwise. Please sign to the right of each item when completed. If necessary, you may put notes regarding the work item after your signature.[br][hr][center][table][br][row][cell]All secure doors inspected and maintained if necessary[cell][field][br][row][cell]All cells cleaned[cell][field][br][row][cell]Brig cleaned and repaired if necessary[cell][field][br][row][cell]Armory inventory completed (see SEC-1010)[cell][field][br][row][cell]Security records checked for important information[cell][field][br][row][cell](optional) Patrol assignments given (see SEC-1002)[cell][field][br][row][cell]Cadets/Junior Officers assigned supervising officer if necessary[cell][field][br][/table][/center][br][hr][grid][row][cell][list][b]Officer on duty signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SEC-1002") + content = @{"[hr][center][small][i]This sheet is designed for the [station]. You may wish to modify it if you are working on a different facility.[/i][/small][br][table][br][row][cell][b]Location[/b][cell][b]Personnel[/b][cell][b]Job[/b][br][row][cell]Arrivals Checkpoint[cell][field][cell][field][br][row][cell]Security Reception[cell][field][cell][field][br][row][cell]Foot Patrol: Deck 1 to Deck 2[cell][field][cell][field][br][row][cell]Foot Patrol: Deck 2[cell][field][cell][field][br][row][cell]Foot Patrol: Deck 2 to Deck 3[cell][field][cell][field][br][row][cell]Vault Checkpoint[cell][field][cell][field][br][row][cell]Auxiliary Checkpoint [field][cell][field][cell][field][br][row][cell][field][cell][field][cell][field][br][/table][/center][br][hr][grid][row][cell][list][b]Signature of Head of Security:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.4" + if("SEC-1003") + content = @{"[b]Location of Incident:[/b] [field][br][br][b]Persons Involved:[/b][br][small][i](V - Victim, S - Suspect, W - Witness, M - Missing, A - Arrested, RP - Reporting Person, RO - Responding Officer, D - Deceased)[/i][/small][br][list][*] [field][br][/list][br][hr][br][b]Details of Incident:[/b][br][br][field][br][br][b]Evidence of Incident:[/b][br][br][field][br][br][hr][grid][row][cell][list][b]Signature of Reporting Officer:[/b][/list][cell][cell][list][list][list][list][b]Signature of Reporting Officer's Supervisor [small](if available)[/small]:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SEC-1004") + content = @{"[grid][row][cell][b]Location of Incident:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Suspect's Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Suspect's Title:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Charges Filed:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Sentence Given:[/b][cell][field][/grid][br][hr][br][b]Persons Involved:[/b][br][small][i](V - Victim, S - Suspect, W - Witness, M - Missing, A - Arrested, RP - Reporting Person, RO - Responding Officer, D - Deceased)[/i][/small][br][list][*] [field][br][/list][br][hr][br][b]Details of Incident:[/b][br][br][field][br][br][b]Evidence of Incident:[/b][br][br][field][br][br][hr][grid][row][cell][list][b]Signature of Arresting Officer:[/b][/list][cell][cell][list][list][list][list][b]Signature of Arresting Officer's Supervisor [small](if available)[/small]:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.0" + if("SEC-1005") + content = @{"[hr][br]To all members of the [field] facility's security department:[br][br]A command to arrive at the security department to answer for their accused charges of [[b][field][/b]] having been given to [[b][field][/b]], and said person having failed to do so,[br][br]YOU ARE HEREBY COMMANDED to arrest said person and bring said person directly to the security department to answer for their disobedience to the aforementioned command, and also the aforementioned charges, as issued by [[b][field][/b]].[br][br][hr][br]This arrest warrant has been issued by [[b][field][/b]] [small][i](name)[/i][/small], [[b][field][/b]] [small][i](rank)[/small][/i]. This arrest warrant is not valid without the aforementioned issuer's signature and only if they have the authority to issue this arrest warrant and are doing so with due respect to the laws and policies governing this facility.[br][br][hr][grid][row][cell][list][b]Issuer's Signature:[/b][/list][cell][cell][list][list][list][list][b]Arresting Officer's Signature:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.3" + if("SEC-1006") + content = @{"[hr][br]I, [[b][field][/b]], do on oath and with my authority as a member of this facility's security department, affirm that I have substantial probable cause to search:[br][br][b]LOCATION, PROPERTY, AND/OR PERSONS TO BE SEARCHED:[/b][br][br]1. [field][br][br][b]ITEMS TO BE SEIZED:[/b][br][br]For the following property, to wit:[br]1. [field][br][br][b]OFFICER'S QUALIFICATIONS[/b][br][br]I have been a member of NanoTrasen's security division for [field] years and am currently assigned to the [field] facility. [field][br][br][b]PROBABLE CAUSE[/b][br][br][i][small]Write the reason why you have probable cause to execute this search warrant.[/small][/i][br][field][br][br][hr][br]This search warrant has been reviewed for legal sufficiency by [[b][field][/b]] [small][i](name)[/i][/small], [[b][field][/b]] [small][i](rank)[/i][/small]. This search warrant is not valid without the aforementioned reviewer's signature and only if they have the authority to approve this search warrant and are doing so with due respect to the laws and policies governing this facility.[br][br][hr][grid][row][cell][list][b]Reviewer's Signature:[/b][/list][cell][cell][list][list][list][list][b]Executing Officer's Signature:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.5" + if("SEC-1007") + content = @{"[grid][row][cell][b]Case Number:[/b][cell][field][/grid][br][hr][h3]Involved Personnel[/h3][small][i](V - Victim, S - Suspect, W - Witness, M - Missing, A - Arrested, RP - Reporting Person, RO - Responding Officer, D - Deceased, O - Other)[/i][/small][br][table][br][row][cell]- [b]Personnel[/b][cell]- [b]Code[/b][cell]- [b]Summary[/b][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][/table][br][br][hr][h3]Physical Property[/h3][small][i](D - Damaged, E - Evidence, L - Lost, R - Recovered, S - Stolen, O - Other)[/i][/small][br][table][br][row][cell]- [b]Item[/b][cell]- [b]Code[/b][cell]- [b]Summary[/b][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][/table][br][br][hr][h3]Evidence[/h3][small][i](S - Statement, P - Photo, D - Document, IP - Item/Property, F - Fibers, FP - Fingerprints, B - Blood, O - Other)[/i][/small][br][table][br][row][cell]- [b]Evidence[/b][cell]- [b]Code[/b][cell]- [b]Summary[/b][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][row][cell]- [field][cell]- [field][cell]- [field][br][/table][br][br][hr][h3]Narrative[/h3][field][br][br][hr][grid][row][cell][list][b]Signature of Reporting Detective:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SEC-1008") + content = @{"[grid][row][cell][b]Interviewee's Name:[/b][cell][field][br][row][cell][b]Interviewee's Title:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Interrogating Officer's Name:[/b][cell][field][br][row][cell][b]Interrogating Officer's Rank:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Other Personnel Present:[/b][cell][field][br][row][cell][b]Case Number:[/b][cell][field][/grid][br][hr][h3]Interview Notes:[/h3][field][br][br][hr][small][i]A transcript of the interrogation should be attached to this report as soon as it is available.[/i][/small][br][br][grid][row][cell][list][b]Interrogating Officer's Signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.0" + if("SEC-1009") + content = @{"[grid][row][cell][b]Witness' Name:[/b][cell][field][br][row][cell][b]Witness' Title:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Attending Officer's Name:[/b][cell][field][br][row][cell][b]Attending Officer's Title:[/b][cell][field][/grid][hr][br][b]Witness' Statement:[/b][br][br][field][br][br][small][i]I, the undersigned, affirm that the above statement is my personal account of the relevant events and is correct and true to the best of my knowledge. Knowingly providing false information could result in charges.[/i][/small][br][grid][row][cell][list][b]Witness' Signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid][hr][br][small][b][u]FOR SECURITY USE ONLY[/b][/u][/small][br][br][grid][row][cell][b]Case Number:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Additional Remarks:[/b][cell][field][/grid][br][grid][row][cell][list][b]Attending Officer's Signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid][br]"} + revision = "Revision: 1.4" + if("SEC-1010") + content = @{"[grid][row][cell][b]Time of Inspection:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Inspecting Officer:[/b][cell][field][/grid][br][hr][center][h3]Regular Armory[/h3][/center][list][*][field][br][/list][br]Miscellaneous equipment: [field][br][br][hr][center][h3]Tactical Armory[/h3][/center][list][*][field][br][/list][br]Miscellaneous equipment: [field][br][br][hr][grid][row][cell][list][b]Signature of Inspecting Officer:[/b][/list][cell][cell][list][list][list][list][b]Signature of Warden [small](if different)[/small]:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SEC-1011") + content = @{"[grid][row][cell][b]Name of Requesting Personne:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Title of Requesting Personnel:[/b][cell][field][/grid][br][hr][br][b]Equipment Requested:[/b][br][br][field][br][br][grid][row][cell][b]Reason:[/b][cell][field][/grid][br][grid][row][cell][list][b]Signature of Requesting Personnel:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid][hr][br][grid][row][cell][b]Tequest Status [small](approved/denied)[/small]:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Reason [small](if denied)[/small]:[/b][cell][field][/grid][br][grid][row][cell][list][b]Signature of Warden [small](or stand-in signatory)[/small]:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SEC-1012") + content = @{"[grid][row][cell][b]Name of Receiving Personnel:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Title of Receiving Personnel:[/b][cell][field][/grid][br][hr][br][b]Equipment Issued:[/b][br][br][field][br][br][hr][br][grid][row][cell][b]Name of Issuing Personnel:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Title of Issuing Personnel:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Reason:[/b][cell][field][/grid][br][hr][grid][row][cell][list][b]Signature of Receiving Personnel:[/b][/list][cell][cell][list][list][list][list][b]Signature of Issuing Personnel:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid][hr][br][small][b][u]TO BE FILLED OUT UPON RETURN OF EQUIPMENT[/u][/b][/small][br][br][b]Missing and/or Damaged Items:[/b][br][br][field][br][br][grid][row][cell][list][b]Signature of Warden [small](or stand-in signatory)[/small]:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.3" + if("SEC-1013") + content = @{"[grid][row][cell][b]Personnel's Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Weapon:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Reason:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Duration [small](max. end of shift)[/small]:[/b][cell][field][/grid][br][hr][br][b]Terms of Permit:[/b][br][br][field][br][br][hr][small][i]I have read and understand Standard Operating Procedure as pertaining to weapon permits and the above terms in which I am allowed to carry this weapon permit. I understand that the below signatories, or any security officer with probable cause, may revoke my weapon permit at any time, and I will be expected to immediately surrender my weapon and this permit to the security department upon revocation or expiration of this permit. I understand that if I am involved in any violent crime, even if the crime is not related to my weapon permit, or if I violate the terms of this weapon permit for any reason, this permit may immediately be revoked at the discretion of security personnel.[/i][/small][br][br][grid][row][cell][list][b]Personnel's Signature:[/b][/list][cell][cell][list][list][list][list][b]Permit Issuer's Signature [small](Title: [field])[/small]:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SEC-1014") + content = @{"[grid][row][cell][b]Receiving Personnel:[/b][cell][field][/grid][br][hr][h3]Terms of Injunction[/h3][field][br][br][hr][small][i]I have read and understand the above terms of this injunction. I understand that this injunction legally forbids me from performing the above actions under any circumstances and that violating this injunction can result in my immediate arrest. If I wish to appeal this injunction, I may contact an Internal Affairs Agent to appeal my case. I understand that this injunction is valid only until the end of the shift in which it was issued unless I am notified otherwise by a Central Command Officer. My below signature constitutes acknowledgment and agreeance with all of these above statements, though it is not required to enforce this injunction.[/small][/i][br][br][grid][row][cell][list][b]Signature of Receiving Personnel:[/b][/list][cell][cell][list][list][list][list][b]Signature of Head of Security [small](or stand-in signatory):[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.1" + //Supply forms, SUP-2 + if("SUP-2001") + content = @{"[grid][row][cell][b]Order Number(s):[/b] [cell][field][br][row][cell][b]Shipment Destination:[/b] [cell][field][br][row][cell][b]Shipment Method:[/b] [cell][field][/grid][br][hr][br][center][b]Goods in this delivery:[/b][/center][br][list][br][*][field][br][*][field][br][*][field][br][*][field][br][*][field][br][/list][br][hr][grid][row][cell][list][b]Supply personnel signature:[/b][/list][cell][cell][list][list][list][list][b]Receiver signature:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.0" + if("SUP-2002") + content = @{"[grid][row][cell][b]Shipment Number(s):[/b] [cell][field][br][row][cell][b]Shipment Destination:[/b] [cell][field][br][row][cell][b]Shipment Method:[/b] [cell][field] [/grid][br][hr][br][center][b]Material in this shipment:[/b][/center][br][list][grid][row][cell][*]Iron:[cell][field][br][row][cell][*]Steel:[cell][field][br][row][cell][*]Plasteel:[cell][field][br][row][cell][*]Durasteel[cell][field][br][row][cell][*]Glass:[cell][field][br][row][cell][*]Sandstone:[cell][field][br][row][cell][*]Uranium:[cell][field][br][row][cell][*]Phoron Crystals:[cell][field][br][row][cell][*]Gold Bars:[cell][field][br][row][cell][*]Silver Ingots:[cell][field][br][row][cell][*]Diamonds:[cell][field][br][row][cell][*]Plastic:[cell][field][br][row][cell][*]Graphite:[cell][field][br][row][cell][*]Platinum:[cell][field][br][row][cell][*]Osmium:[cell][field][br][row][cell][*]Tritium:[cell][field][br][row][cell][*]Mhydrogen:[cell][field][br][row][cell][*]Verdantium:[cell][field][br][row][cell][*]Marble:[cell][field][br][row][cell][*]Lead:[cell][field][br][row][cell][*]Titanium:[cell][field][/grid][br][/list][br][small]Leave blank or write 0 if none[/small][br][hr][grid][row][cell][list][b]Supply personnel signature:[/b][/list][cell][cell][list][list][list][list][b]Receiver signature:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.4" + if("SUP-2003") + content = @{"[hr][br][center][b]Material in stock:[/b][/center][br][list][grid][row][cell][*]Iron:[cell][field][br][row][cell][*]Steel:[cell][field][br][row][cell][*]Plasteel:[cell][field][br][row][cell][*]Durasteel[cell][field][br][row][cell][*]Glass:[cell][field][br][row][cell][*]Sandstone:[cell][field][br][row][cell][*]Uranium:[cell][field][br][row][cell][*]Phoron Crystals:[cell][field][br][row][cell][*]Gold Bars:[cell][field][br][row][cell][*]Silver Ingots:[cell][field][br][row][cell][*]Diamonds:[cell][field][br][row][cell][*]Plastic:[cell][field][br][row][cell][*]Graphite:[cell][field][br][row][cell][*]Platinum:[cell][field][br][row][cell][*]Osmium:[cell][field][br][row][cell][*]Tritium:[cell][field][br][row][cell][*]Mhydrogen:[cell][field][br][row][cell][*]Verdantium:[cell][field][br][row][cell][*]Marble:[cell][field][br][row][cell][*]Lead:[cell][field][br][row][cell][*]Titanium:[cell][field][/grid][br][/list][br][small]Leave blank or write 0 if none[/small][br][hr][grid][row][cell][list][b]Supply personnel signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.1" + //Science forms, SCI-3 + if("SCI-3003") + content = @{"[grid][row][cell][row][cell][b]Roboticist:[/b] [cell][field][br][row][cell][b]Assistant:[/b] [cell][field][/grid][br][small]Leave blank if none[/small][hr][br][grid][row][cell][row][cell][b]Cyborg / Robot:[/b] [cell][field][br][row][cell][b]Module:[/b] [cell][field][/grid][br][hr][br]The following form documents the full inspection of [[field]] as performed by [u][field][/u] in the Robotics Labratory aboard the [station] on [u][date][/u].[br][br][table][row][cell][b]General [/b][cell][center]Status[/center][cell]Comment[br][row][cell][b]- Laws [/b][cell][center][field][/center][cell][field][br][row][cell]-[cell][center]-[/center][cell]-[br][row][cell][b]Component external [/b][cell][center]-[/center][cell]-[br][row][cell][b]- Robot Endoskeleton [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Left Arm [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Right Arm [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Left Leg [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Right Leg [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Torso [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Head [/b][cell][center][field][/center][cell][field][br][row][cell]-[cell][center]-[/center][cell]-[br][row][cell][b]Component internal [/b][cell][center]-[/center][cell]-[br][row][cell][b]- Wiring [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Actuator [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Radio [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Power Cell [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Self-Diagnosis Unit [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Camera [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Binary Communication Device [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Armour Plating [/b][cell][center][field][/center][cell][field][/table][br][hr][grid][row][cell][list][b]Scientist signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("SCI-3004") + content = @{"[grid][row][cell][row][cell][b]Roboticist:[/b] [cell][field][br][row][cell][b]Assistant:[/b] [cell][field][/grid][br][small]Leave blank if none[/small][hr][br][grid][row][cell][row][cell][b]Cyborg / Robot:[/b] [cell][field][br][row][cell][b]Module:[/b] [cell][field][/grid][br][hr][br]The following form documents the installed upgrades into [[field]] as performed by [u][field][/u] in the Robotics Labratory aboard the [station] on [u][date][/u].[br][br][table][row][cell][b]Lawset[/b][cell][center]Status[/center][cell]Comment[br][row][cell][b]- [field][/b][cell][center][field][/center][cell][field][br][row][cell]-[cell][center]-[/center][cell]-[br][row][cell][b]General [/b][cell][center]-[/center][cell]-[br][row][cell][b]- Rename Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Reset Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Emergency Resttart Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- VTEC Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Advanced Health Analyzer Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Robohound Capacity Expansion Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Robohound Capability Expansion Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Size Alteration Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Size Gun Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Scrambled Equipment Module [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Language Module [/b][cell][center][field][/center][cell][field][br][row][cell]-[cell][center]-[/center][cell]-[br][row][cell][b]Security [/b][cell][center]-[/center][cell]-[br][row][cell][b]- Rapid Taser Cooling Module [/b][cell][center][field][/center][cell][field][br][row][cell]-[cell][center]-[/center][cell]-[br][row][cell][b]Mining [/b][cell][center]-[/center][cell]-[br][row][cell][b]- Proto-Kinetic Accelerator [/b][cell][center][field][/center][cell][field][br][row][cell][b]- Diamond Drill [/b][cell][center][field][/center][cell][field][br][row][cell]-[cell][center]-[/center][cell]-[br][row][cell][b]Science [/b][cell][center]-[/center][cell]-[br][row][cell][b]- Advanced Rapid Part Exchange Device [/b][cell][center][field][/center][cell][field][/table][br][hr][grid][row][cell][list][b]Scientist signature:[/b][/list][cell][list][list][list][list][list][b]Research Director signature [small](In case of Law Change)[/small]:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][br][row][cell][cell][cell][br][row][cell][list][/list][cell][list][list][list][list][list][b]Second Command signature [small](In case of Law Change)[/small]:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.4" + if("SCI-3009") + content = @{"[grid][row][cell][row][cell][b]Scientist:[/b] [cell][field][br][row][cell][b]Assistant:[/b] [cell][field] [/grid][br][small]Leave blank if none[/small][br][hr][br]The following table contains the Genetic Identifier and purpose of the Xenofloral genes as identified by [u][field][/u] in the Xenobotanical Labratory aboard the [station] on [u][date][/u].[br][br][table][cell][b]Genetic Identifier[/b][cell]Genetic Sequence[row][br][cell][b]-[field] [/b][cell][small][b]Hardiness:[/b] Endurance. Tolerance to toxins, pests, and/or weeds[/small][br][row][cell]-[b][field] [/b][cell][small][b]Vigour:[/b] How long the plant takes to mature and produce fruit. How much fruit it produces. Whether it can spread out of its tray(vine).[/small][br][row][cell]-[b][field] [/b][cell][small][b]Biochemistry:[/b] Which reagents the fruit contains; which gases the plant generates.[/small][br][row][cell][b]-[field] [/b][cell][small][b]Fruit:[/b] Whether the fruit is juicy and will splatter when thrown; whether the plant has stinging spines and will inject its reagents into anyone coming in contact with it.[/small][br][row][cell]-[b][field] [/b][cell][small][b]Diet:[/b] Whether the plant consumes gases in its environment; whether the plant is carnivorous (eats pests) or eats tray weeds; how much water and fertilizer it consumes.[/small][br][row][cell]-[b][field] [/b][cell][small][b]Metabolism:[/b] Whether the plant requires water or fertilizer. Whether it alters the ambient temperature.[/small][br][row][cell]-[b][field] [/b][cell][small][b]Environment:[/b] Plant's preferred temperature and light levels, and how much tolerance it has for changes in light level.[/small][row][cell]-[b][field] [/b][cell][small][b]Atmosphere:[/b] The plant's tolerance for changes in temperature and pressure away from its preferred levels.[/small][br][row][cell]-[b][field] [/b][cell][small][b]Output:[/b] Whether the plant produces electrical power or bioluminescent light.[/small][br][row][cell]-[b][field] [/b][cell][small][b]Appearance:[/b] The "shape" of the plant. Also affects whether it can be harvested only once or multiple times.[/small][br][row][cell]-[b][field] [/b][cell][small][b]Pigment:[/b] The color of the plant and its fruit; the color of any bioluminescence.[/small][br][row][cell][b]-[field] [/b][cell][small][b]Special:[/b] The ability to teleport the thrower or target when thrown.[/small][/table][br][hr][grid][row][cell][list][b]Scientist signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 0.9" + //Medical forms, MED-4 + if("MED-4001") + content = @{"[hr][br][grid][row][cell][b]Deceased Patient's Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Date of Birth:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Attending Physician:[/b][cell][field][/grid][br][hr][br][grid][row][cell][b]Date of Death:[/b][cell][field][br][row][cell][b]Time of Death:[/b][cell][field][br][row][cell][b]Cause of Death:[/b][cell][field][br][row][cell][cell][br][row][cell][b]DNR/DNC Request Present:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Further Autopsy Notes:[/b][cell][field][/grid][br][hr][br][i][small]I, the undersigned, with my authority as a licensed medical practitioner, declare the aforementioned patient to have irreversibly died at the aforementioned date and time in the aforementioned manner. I affirm that all information in this death certificate is true and correct to the best of my knowledge.[/i][/small][br][br][hr][grid][row][cell][list][b]Signature of Attending Physician:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.2" + if("MED-4002") + content = @{"[grid][row][cell][b]Patient Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Prescribing Doctor:[/b][cell][field][/grid][br][hr][br][grid][row][cell][b]Name of Prescription Medicine:[/b][cell][field][br][row][cell][b]Dosage Type & Amount:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Prescription Start Date:[/b][cell][field][br][row][cell][b]Prescription End Date:[/b][cell][field][/grid][br][hr][h3]INSTRUCTIONS:[/h3][field][br][br][grid][row][cell][b]Potential Side Effects:[/b][cell][field][br][row][cell][b]Additional Notes:[/b][cell][field][/grid][br][hr][br]Only take this medication as prescribed, according to the above instructions. Do not stop taking this medication without consulting your doctor. If you miss a dose, take it as soon as you remember unless it is close to the time of your next dose. [b]Do not take multiple doses to make up for a missed dose.[/b] If you have any questions about this medication, ask your doctor.[br][br]Keep a copy of this form with your medication so you may easily reference it.[br][hr][grid][row][cell][list][b]Signature of Patient:[/b][/list][cell][cell][list][list][list][list][b]Signature of Attending Physician:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("MED-4003") + content = @{"[grid][row][cell][b]Patient Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Practitioner:[/b][cell][field][/grid][br][hr][br][center]Patient has decisional capacity to refuse further medical evaluation or treatment. Sign to confirm.[/center][br][hr][br]This certified that I, [[field]], voluntarily refuse further medical evaluation and treatment at [field]. I understand that further evaluation and treatment has been recommended and I am leaving [b]against medical advice.[/b] The medical staff have explained the risks of leaving which may include the worsening of my condition, harm to a bodily function or part, [b]or even death.[/b][br][br][hr][br][b]Benefits of receiving[/b] further evaluation and treatment include, but are not limited to:[br][br][field][br][br][b]Risks of refusing[/b] further evaluation and treatment include, but are not limited to:[br][br][field][br][br][b]Alternatives[/b] to receiving further evaluation and treatment here include, but are not limited to:[br][br][field][br][br][br][grid][row][cell][list][b]Practitioner Signature:[/b][/list][cell][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid][br][hr][br][b]I understand that I may return at any time and consent to further evaluation and treatment.[/b][br][br][grid][row][cell][list][b]Signature of Patient:[/b][/list][cell][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.1" + if("MED-4004") + content = @{"[hr][br][grid][row][cell][b]Patient Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Current Occupation:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Preferred Cyborg name:[/b][cell][field][/grid][br][hr][br]By signing this contract you will be filed for voluntary cybogification.[br][br] Lobotomy will be performed on your person and your brain will be transported, implanted and synchronized to a functional cyborg shell. You also agree to abide by NT Cyborg law and that the research department, NT, or any of its affilites are not responsible for the loss of, or damage to any of the following:[br][list][small][*]Health[br][*]Life[br][*]Posessions[br][*]Investments[br][*]Relationships[br][*]Sense of fullfillment[br][*]Fun[/small][/list][br][small]The research team withholds the privilege to, [i]at any time[/i], end the cyborg contract in question, thereby destroying the shell in the process, and consider returning the brain to a biological body.[/small][br][hr][grid][row][cell][list][b]Patient signature[/b][/list][cell][cell][br][row][cell][list]- [field][/list][cell][cell][br][row][cell][cell][cell][br][row][cell][list][b]Performing surgeon signature:[/b][cell][list][list][list][list][list][b]Performing roboticist signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][br][row][cell][cell][cell][br][row][cell][list][b]Head of department Signature[/b][/list][cell][cell][br][row][cell][list]- [large][field][/large][/list][cell][cell][/grid][br][small][center]-Reminder to notify subject's head of staff and security-[/small]"} + revision = "Revision: 1.2" + if("MED-4005") + content = @{"[hr][br][grid][row][cell][b]Deceased Patient's Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Date of Birth:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Attending Physician:[/b][cell][field][/grid][br][hr][small][i]The following questions should be filled out by the patient without supervision or direction.[/small][/i][br][br][b]List of current physical and mental ailments [small](as described by you or another healthcare professional)[/small]:[/b][br][br][field][br][br][b]List of current and prior prescription medicine usage:[/b][br][br][field][br][br][b]Why do you want to see a mental health professional?[/b][br][br][field][br][br][b]Anything else I should know about you?[/b][br][br][field][br][br][b]Are you happy to recieve hypnotherapy and/or experimental vore therapy? Please ask your therapist if unsure.[/b][br][br][field][br][br][hr][br][[b][field][/b]] is expected to be your assigned mental healthcare professional. Please note that due to the decentralization of NanoTrasen's healthcare facilities, you may visit a different professional from time to time. Please take care to keep a summary of past visits with you to help you get the best care you need.[br][br][hr][grid][row][cell][list][b]Signature of Patient:[/b][/list][cell][cell][list][list][list][list][b]Doctor Signature:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 2.1" + if("MED-4006") + content = @{"[grid][row][cell][b]Patient Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Practitioner:[/b][cell][field][/grid][br][hr][br]I, [[field]] (hereafter referred to as 'the patient'), hereby grant permission for the installation of a Nanite Implant Framework (hereafter referred to as a NIF), a non-essential and invasive implantation surgical procedure. I have been informed of and recognize the risks of this procedure, and the risks of possessing an implanted NIF, outlined below.[br][hr][br]Due to the complexity of this procedure, life-threatening risks are present. A skilled surgeon will be called upon to operate the procedure. They are expected to uphold Standard Operating Procedure and all surgical procedural guidelines.[br][br]There are possible risks associated with the installation of certain NIFsoft programs as well, such as malfunction or malware.[br][br]Upon installation, there will be a half-hour calibration period while the NIF connects to neurons in the brain, during which the patient will experience the following symptoms.[br][list][br][*]Loss of sight for approximately the first five minutes of calibration.[br][*]Grainy vision after restoration of ocular functions.[br][*]Strange and unusual sensations and tingling.[br][*]Extreme full-body pain.[br][*]Headaches.[br][*]Weakness.[br][*]Intermittent fainting and loss of consciousness.[/list][br][br]The patient may be discharged after the 30-minute recovery period has passed. The patient will be notified by their NIF when the process is complete.[br][br]As the patient, you are entitled to priority medical care in the event of a surgery-related emergency, up to and including resleeving if necessary. You are also entitled to an available, surgically-trained physician of your choice for the implantation in the event you do not like the one assigned to your care.[br][hr][br]Please put a cross (X) on one of these anesthetic-like options:[br][[field]] - I want to be sedated with anesthetic gas. (Recommended for a majority of species.)[br][[field]] - I want to be sedated with medication.[br][[field]] - I want my mind to be downloaded onto a SleeveMate 3700.[br][[field]] - I do [u]not[/u] want any of the above. [b](Not recommended for most species. The surgeon may refuse to operate without anesthetic.)[/b][br][hr][small]By signing this form I agree that I have read and assessed the risks associated with owning a NIF and NIF implantation surgery and give my consent for operation of this procedure.[/small][br][grid][row][cell][list][b]Signature of Patient:[/b][/list][cell][cell][list][list][list][list][b]Practitioner Signature:[/b][/list][/list][/list][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: 2.2" + if("MED-4007") + content = @{"[grid][row][cell][b]Patient Name:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Patient Occupation:[/b][cell][field][br][row][cell][cell][br][row][cell][b]Psychiatrist:[/b][cell][field][/grid][br][hr][br][b][u]Concerns:[/u][/b][br][br][field][br][br][b][u]Evaluation:[/u][/b][br][br][field][br][br][b][u]Conclusion:[/u][/b][br][br][field][br][br][hr][b][u]Comments:[/u][/b][br][br][field][br][br][hr][grid][row][cell][list][b]Psychiatrist Signature:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.3" + //Engineering forms, ENG-5 + if("ENG-5001") + content = @{"[grid][row][cell][b]Location:[/b] [cell][field][br][row][cell][b]Purpose:[/b] [cell][field][/grid][br][hr][br]I, [[u][field][/u]] certify that I have reviewed and approved of provided blueprints. I have verified that design will be structurally sound and fall within building guidelines. I and any others participating in its construction will ensure that the blueprint will be followed.[br][br][br][b]Blueprint:[/b] [field][br][br][hr][grid][row][cell][list][b]Constructing Engineer signature:[/b][/list][cell][list][list][list][list][list][b]Chief Engineer signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list] - [field][/list][cell][list][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision 1.1" + //Service forms, SER-6 + if("SER-6005") + content = @{"[hr][br][center]This is to certify that[br][br][u]_[field]_[/u] and [u]_[field]_[/u][br][br]were united in marriage at [u]_[field]_[/u] on date [u]_[field]_[/u][br][br][hr][grid][row][cell][list][b]Bride:[/b][/list][cell][cell][list][list][list][list][b]Groom:[/b][/list][/list][/list][/list][cell][row][cell][list] - [large][field][/large][/list][cell][cell][list][list][list][list]- [large][field][/large][/list][/list][/list][/list][cell][/grid][hr][br][b]Chaplain:[/b] [large][field][/large]"} + revision = "Revision 1.4" + //Explorer forms, EXP-7 + //Event forms, EVNT-8 + //Other forms, OTHR-9 + if("OTHR-9001") + content = @{"[grid][row][cell][b]Sender:[/b] [cell][field][br][row][cell][b]Position:[/b] [cell][field][/grid][br][hr][br][b]Message:[/b] [field][br][br][hr][grid][row][cell][list][b]Signed:[/b][/list][cell][br][row][cell][list] - [large][field][/large][/list][cell][/grid]"} + revision = "Revision: 1.0" + if("OTHR-9032") + content = @{"[b]Requested Owner:[/b] [field][br][br][hr][br][b]Hereby, I [[/b][field][b]] agree to transfer my full ownsership, including all my rights to [[/b][field][b]].[br]This contract is binding immediately after both parties have signed and valid until [[/b][field][b]].[/b][br][br]Additional Agreements: [field][br][br][hr][grid][row][cell][list][b]Property Signature:[/b][/list][cell][list][list][list][list][list][b]Owner Signature:[/b][/list][cell][br][row][cell][list]- [field][/list][cell][list][list][list][list][list]- [field][/list][cell][/grid][hr][br]The contract can only be cancelled if both parties agree.[br]At no time, a single party can revert or void this contract.[br]All changes to this form after the contract was stamped are invalidated.[br][br]"} + revision = "Revision: 1.0" + //Mercenary forms, MERC-? + if("MERC-?071") + content = @{"[b]Requested Mercenary:[/b] [field][br][br][hr][br][grid][row][cell][b]Target:[/b] [cell][field][br][row][cell][b]Reason:[/b] [cell][field][/grid][br][grid][row][cell][list][b]Vore Type:[/b][/list][cell][list][field][/list][cell][list][list][list][b]Offered Bounty:[/b][/list][/list][/list][cell][list][field][/list][cell][br][row][cell][list][b]Extra Modules:[/b][/list][cell][list][field][/list][cell][list][list][list][b]Added Module Costs:[/b][/list][/list][/list][cell][list][field][/list][cell][br][row][cell][list][b]Special Requests:[/b][/list][cell][list][field][/list][cell][list][list][list][b]Added Request Fees:[/b][/list][/list][/list][cell][list][field][/list][cell][br][row][cell][cell][cell][cell][cell][br][row][cell][cell][cell][list][list][list][b]Total Bounty:[/b][/list][cell][list][field][/list][cell][/grid][br][hr][br]All payments are to be made in full before contract fulfilment.[br]Please be aware that signed contracts can [u]not[/u] be cancelled.[br][br][hr][grid][row][cell][list][b]Commissioner signature:[/b][/list][cell][list][list][list][list][list][b]Contractor signature:[/b][/list][/list][/list][/list][/list][cell][br][row][cell][list][large] - [field][/large][/list][cell][large][list][list][list][list][list]- [field][/large][cell][/list][/list][/list][/list][/list][cell][/grid]"} + revision = "Revision: ?.?" + else + paperid = @{"[field]"} + name = @{"[field]"} + content = @{"[field]"} + revision = @{"[field]"} + return create_form(paperid, name, content, revision) + +/obj/item/weapon/form_printer/proc/create_form(paperid, name, content, revision) + var/header = "\[center]\[br]\[small]" + paperid + "\[/small]\[br]\[h1]\[u]" + name + "\[/u]\[/h1]\[br]\[logo]\[br]\[br]\[station]\[br]\[/center]\[br]\[hr]\[br]\[b]Station Time:\[/b] \[date], \[time]\[br]\[br]" + var/footer = "\[hr]\[br]\[center]\[small]Stamp here.\[/small]\[/center]\[br]\[small]" + revision + "\[/small]" + return header + content + footer //Personal shielding for the combat module. /obj/item/borg/combat/shield diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm index bab1cfb9670..0788469e6e7 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm @@ -47,8 +47,6 @@ var/global/list/robot_modules = list( var/list/modules = list() var/list/datum/matter_synth/synths = list() var/list/emag = list() - var/obj/item/borg/upgrade/jetpack = null - var/obj/item/borg/upgrade/advhealth = null var/list/subsystems = list() var/list/obj/item/borg/upgrade/supported_upgrades = list() @@ -102,13 +100,13 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/Destroy() for(var/module in modules) qdel(module) + for(var/emg in emag) + qdel(emg) for(var/synth in synths) qdel(synth) modules.Cut() synths.Cut() emag.Cut() - qdel(jetpack) - jetpack = null return ..() /obj/item/weapon/robot_module/emp_act(severity) @@ -231,6 +229,7 @@ var/global/list/robot_modules = list( networks = list(NETWORK_MEDICAL) subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) pto_type = PTO_MEDICAL + supported_upgrades = list(/obj/item/borg/upgrade/restricted/bellycapupgrade) /obj/item/weapon/robot_module/robot/medical/surgeon name = "surgeon robot module" @@ -468,7 +467,7 @@ var/global/list/robot_modules = list( channels = list("Security" = 1) networks = list(NETWORK_SECURITY) subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - supported_upgrades = list(/obj/item/borg/upgrade/tasercooler) + supported_upgrades = list(/obj/item/borg/upgrade/restricted/tasercooler, /obj/item/borg/upgrade/restricted/bellycapupgrade) pto_type = PTO_SECURITY /obj/item/weapon/robot_module/robot/security/general @@ -666,7 +665,7 @@ var/global/list/robot_modules = list( name = "miner robot module" channels = list("Supply" = 1) networks = list(NETWORK_MINE) - supported_upgrades = list(/obj/item/borg/upgrade/pka, /obj/item/borg/upgrade/diamonddrill) + supported_upgrades = list(/obj/item/borg/upgrade/restricted/pka, /obj/item/borg/upgrade/restricted/diamonddrill) pto_type = PTO_CARGO /obj/item/weapon/robot_module/robot/miner/create_equipment(var/mob/living/silicon/robot/robot) @@ -687,7 +686,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/robot/research name = "research module" channels = list("Science" = 1) - supported_upgrades = list(/obj/item/borg/upgrade/advrped) + supported_upgrades = list(/obj/item/borg/upgrade/restricted/advrped) pto_type = PTO_SCIENCE /obj/item/weapon/robot_module/robot/research/create_equipment(var/mob/living/silicon/robot/robot) @@ -705,8 +704,8 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/tool/wrench/cyborg(src) src.modules += new /obj/item/weapon/tool/wirecutters/cyborg(src) src.modules += new /obj/item/device/multitool(src) - src.modules += new /obj/item/weapon/surgical/scalpel/cyborg(src) - src.modules += new /obj/item/weapon/surgical/circular_saw/cyborg(src) + src.modules += new /obj/item/weapon/surgical/hemostat/cyborg(src) //Synth repair + src.modules += new /obj/item/weapon/surgical/surgicaldrill/cyborg(src) //NIF repair src.modules += new /obj/item/weapon/reagent_containers/syringe(src) src.modules += new /obj/item/weapon/reagent_containers/glass/beaker/large(src) src.modules += new /obj/item/weapon/storage/part_replacer(src) @@ -750,6 +749,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/robot/security/combat name = "combat robot module" hide_on_manifest = TRUE + supported_upgrades = list(/obj/item/borg/upgrade/restricted/bellycapupgrade) /obj/item/weapon/robot_module/robot/security/combat/create_equipment(var/mob/living/silicon/robot/robot) ..() @@ -758,10 +758,10 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/gun/energy/laser/mounted(src) src.modules += new /obj/item/weapon/gun/energy/taser/mounted/cyborg/ertgun(src) src.modules += new /obj/item/weapon/pickaxe/plasmacutter/borg(src) - src.modules += new /obj/item/weapon/combat_borgblade + src.modules += new /obj/item/weapon/melee/combat_borgblade(src) src.modules += new /obj/item/borg/combat/shield(src) src.modules += new /obj/item/borg/combat/mobility(src) - src.modules += new /obj/item/weapon/borg_combat_shocker + src.modules += new /obj/item/weapon/melee/borg_combat_shocker(src) src.modules += new /obj/item/device/ticket_printer(src) src.emag += new /obj/item/weapon/gun/energy/lasercannon/mounted(src) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm index bc608343f73..7ed99340421 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm @@ -36,7 +36,8 @@ src.modules += jetpack robot.internals = jetpack - id = robot.idcard + var/obj/id = robot.idcard + id.forceMove(src) src.modules += id /obj/item/weapon/robot_module/robot/syndicate/Destroy() @@ -47,6 +48,7 @@ // Gets a big shield and a gun that shoots really fast to scare the opposing force. /obj/item/weapon/robot_module/robot/syndicate/protector name = "protector robot module" + supported_upgrades = list(/obj/item/borg/upgrade/restricted/bellycapupgrade) /obj/item/weapon/robot_module/robot/syndicate/protector/create_equipment(var/mob/living/silicon/robot/robot) ..() @@ -82,7 +84,7 @@ // Hacking other things. src.modules += new /obj/item/weapon/card/robot/syndi(src) - src.modules += new /obj/item/weapon/card/emag(src) + src.modules += new /obj/item/weapon/card/emag/borg(src) // Materials. var/datum/matter_synth/nanite = new /datum/matter_synth/nanite(10000) @@ -132,10 +134,10 @@ // Mediborg optimized for on-the-field healing, but can also do surgery if needed. /obj/item/weapon/robot_module/robot/syndicate/combat_medic name = "combat medic robot module" + supported_upgrades = list(/obj/item/borg/upgrade/restricted/bellycapupgrade) /obj/item/weapon/robot_module/robot/syndicate/combat_medic/create_equipment(var/mob/living/silicon/robot/robot) ..() - src.modules += new /obj/item/borg/sight/hud/med(src) src.modules += new /obj/item/device/healthanalyzer/phasic(src) src.modules += new /obj/item/weapon/reagent_containers/borghypo/merc(src) @@ -190,4 +192,4 @@ S.mode = initial(S.mode) S.desc = initial(S.desc) S.update_icon() - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index f9f0aed303b..106513eced4 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -1,99 +1,99 @@ -/mob/living/silicon/robot/get_jetpack() - if(module) - for(var/obj/item/weapon/tank/jetpack/J in module.modules) - return J - -/mob/living/silicon/robot/Check_Shoegrip() - return module && module.no_slip - -/mob/living/silicon/robot/Process_Spaceslipping(var/prob_slip) - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - if(thrust?.can_thrust(0.01)) - return 0 - if(module && module.no_slip) - return 0 - ..(prob_slip) - -/mob/living/silicon/robot/Process_Spacemove(var/check_drift = 0) - if(..())//Can move due to other reasons, don't use jetpack fuel - return 1 - - var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() - if(thrust && (!check_drift || (check_drift && thrust.stabilization_on)) && thrust.do_thrust(0.01)) - inertia_dir = 0 - return 1 - - return 0 - - //No longer needed, but I'll leave it here incase we plan to re-use it. -/mob/living/silicon/robot/movement_delay() - . = speed - - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - . -= 2 // VOREStation Edit - - if(get_restraining_bolt()) // Borgs with Restraining Bolts move slower. - . += 1 - - . += config.robot_delay - - . += ..() - -// NEW: Use power while moving. -/mob/living/silicon/robot/SelfMove(turf/n, direct, movetime) - if (!is_component_functioning("actuator")) - return 0 - - var/datum/robot_component/actuator/A = get_component("actuator") - if (cell_use_power(A.active_usage)) - return ..() - -/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) - . = ..() - - if(!module) - return - - //Borgs and drones can use their mining bags ~automagically~ if they're deployed in a slot. Only mining bags, as they're optimized for mass use. - if(istype(module_state_1, /obj/item/weapon/storage/bag/ore) || istype(module_state_2, /obj/item/weapon/storage/bag/ore) || istype(module_state_3, /obj/item/weapon/storage/bag/ore)) - var/obj/item/weapon/storage/bag/ore/B = null - if(istype(module_state_1, /obj/item/weapon/storage/bag/ore)) //First orebag has priority, if they for some reason have multiple. - B = module_state_1 - else if(istype(module_state_2, /obj/item/weapon/storage/bag/ore)) - B = module_state_2 - else if(istype(module_state_3, /obj/item/weapon/storage/bag/ore)) - B = module_state_3 - var/turf/tile = loc - if(isturf(tile)) - B.gather_all(tile, src, 1) //Shhh, unless the bag fills, don't spam the borg's chat with stuff that's going on every time they move! - - if(scrubbing && isturf(loc)) - var/turf/tile = loc - tile.clean_blood() - if (istype(tile, /turf/simulated)) - var/turf/simulated/S = tile - S.dirt = 0 - for(var/A in tile) - if(istype(A, /obj/effect)) - if(istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay)) - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0) - cleaned_human.clean_blood(1) - to_chat(cleaned_human, "[src] cleans your face!") +/mob/living/silicon/robot/get_jetpack() + if(module) + for(var/obj/item/weapon/tank/jetpack/J in module.modules) + return J + +/mob/living/silicon/robot/Check_Shoegrip() + return module && module.no_slip + +/mob/living/silicon/robot/Process_Spaceslipping(var/prob_slip) + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + if(thrust?.can_thrust(0.01)) + return 0 + if(module && module.no_slip) + return 0 + ..(prob_slip) + +/mob/living/silicon/robot/Process_Spacemove(var/check_drift = 0) + if(..())//Can move due to other reasons, don't use jetpack fuel + return 1 + + var/obj/item/weapon/tank/jetpack/thrust = get_jetpack() + if(thrust && (!check_drift || (check_drift && thrust.stabilization_on)) && thrust.do_thrust(0.01)) + inertia_dir = 0 + return 1 + + return 0 + + //No longer needed, but I'll leave it here incase we plan to re-use it. +/mob/living/silicon/robot/movement_delay() + . = speed + + if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + . -= 2 // VOREStation Edit + + if(get_restraining_bolt()) // Borgs with Restraining Bolts move slower. + . += 1 + + . += config.robot_delay + + . += ..() + +// NEW: Use power while moving. +/mob/living/silicon/robot/SelfMove(turf/n, direct, movetime) + if (!is_component_functioning("actuator")) + return 0 + + var/datum/robot_component/actuator/A = get_component("actuator") + if (cell_use_power(A.active_usage)) + return ..() + +/mob/living/silicon/robot/Moved(atom/old_loc, direction, forced = FALSE) + . = ..() + + if(!module) + return + + //Borgs and drones can use their mining bags ~automagically~ if they're deployed in a slot. Only mining bags, as they're optimized for mass use. + if(istype(module_state_1, /obj/item/weapon/storage/bag/ore) || istype(module_state_2, /obj/item/weapon/storage/bag/ore) || istype(module_state_3, /obj/item/weapon/storage/bag/ore)) + var/obj/item/weapon/storage/bag/ore/B = null + if(istype(module_state_1, /obj/item/weapon/storage/bag/ore)) //First orebag has priority, if they for some reason have multiple. + B = module_state_1 + else if(istype(module_state_2, /obj/item/weapon/storage/bag/ore)) + B = module_state_2 + else if(istype(module_state_3, /obj/item/weapon/storage/bag/ore)) + B = module_state_3 + var/turf/tile = loc + if(isturf(tile)) + B.gather_all(tile, src, 1) //Shhh, unless the bag fills, don't spam the borg's chat with stuff that's going on every time they move! + + if(scrubbing && isturf(loc)) + var/turf/tile = loc + tile.clean_blood() + if (istype(tile, /turf/simulated)) + var/turf/simulated/S = tile + S.dirt = 0 + for(var/A in tile) + if(istype(A, /obj/effect)) + if(istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay)) + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0) + cleaned_human.clean_blood(1) + to_chat(cleaned_human, "[src] cleans your face!") diff --git a/code/modules/mob/living/silicon/robot/robot_remote_control.dm b/code/modules/mob/living/silicon/robot/robot_remote_control.dm index 21bb86596da..fba59808b46 100644 --- a/code/modules/mob/living/silicon/robot/robot_remote_control.dm +++ b/code/modules/mob/living/silicon/robot/robot_remote_control.dm @@ -1,135 +1,135 @@ -// This file holds things required for remote borg control by an AI. - -GLOBAL_LIST_EMPTY(available_ai_shells) - -/mob/living/silicon/robot - var/shell = FALSE - var/deployed = FALSE - var/mob/living/silicon/ai/mainframe = null - -// Premade AI shell, for roundstart shells. -/mob/living/silicon/robot/ai_shell/Initialize() - mmi = new /obj/item/device/mmi/inert/ai_remote(src) - post_mmi_setup() - return ..() - -// Call after inserting or instantiating an MMI. -/mob/living/silicon/robot/proc/post_mmi_setup() - if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) - make_shell() - playsound(src, 'sound/machines/twobeep.ogg', 50, 0) - else - playsound(src, 'sound/voice/liveagain.ogg', 75, 1) - return - -/mob/living/silicon/robot/proc/make_shell() - shell = TRUE - braintype = "AI Shell" - SetName("[modtype] AI Shell [num2text(ident)]") - rbPDA = new /obj/item/device/pda/ai/shell(src) - setup_PDA() - GLOB.available_ai_shells |= src - if(!QDELETED(camera)) - camera.c_tag = real_name //update the camera name too - notify_ai(ROBOT_NOTIFICATION_AI_SHELL) - update_icon() - -/mob/living/silicon/robot/proc/revert_shell() - if(!shell) - return - undeploy() - shell = FALSE - GLOB.available_ai_shells -= src - if(!QDELETED(camera)) - camera.c_tag = real_name - update_icon() - -// This should be called before the AI client/mind is actually moved. -/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI) - // Set the name when the AI steps inside. - SetName("[AI.real_name] shell [num2text(ident)]") - if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites. - sprite_name = AI.real_name - if(!QDELETED(camera)) - camera.c_tag = real_name - - // Have the borg have eyes when active. - mainframe = AI - deployed = TRUE - update_icon() - - // Laws. - connected_ai = mainframe // So they share laws. - mainframe.connected_robots |= src - lawsync() - - // Give button to leave. - verbs += /mob/living/silicon/robot/proc/undeploy_act - to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.
                    \ - To return to your core, use the Release Control verb.")) - - // Languages and comms. - languages = AI.languages.Copy() - speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy() - if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator. -// if(AI.radio.syndie) -// radio.make_syndie() - radio.subspace_transmission = TRUE - radio.channels = AI.aiRadio.channels - -// Called after the AI transfers over. -/mob/living/silicon/robot/proc/post_deploy() - return - -/mob/living/silicon/robot/proc/undeploy(message) - if(!deployed || !mind || !mainframe) - return -// mainframe.redeploy_action.Grant(mainframe) -// mainframe.redeploy_action.last_used_shell = src - if(message) - to_chat(src, span("notice", message)) - mind.transfer_to(mainframe) - deployed = FALSE - update_icon() - mainframe.teleop = null - mainframe.deployed_shell = null - SetName("[modtype] AI Shell [num2text(ident)]") -// undeployment_action.Remove(src) - if(radio) //Return radio to normal - radio.recalculateChannels() - if(!QDELETED(camera)) - camera.c_tag = real_name //update the camera name too -// diag_hud_set_aishell() -// mainframe.diag_hud_set_deployed() - if(mainframe.laws) - mainframe.laws.show_laws(mainframe) //Always remind the AI when switching - mainframe = null - -/mob/living/silicon/robot/proc/undeploy_act() - set name = "Release Control" - set desc = "Release control of a remote drone." - set category = "Robot Commands" - - undeploy("Remote session terminated.") - -/mob/living/silicon/robot/attack_ai(mob/user) - if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user)) - var/mob/living/silicon/ai/AI = user - if(istype(AI)) // Just in case we're clicked by a borg - AI.deploy_to_shell(src) - else - return ..() - -// Place this on your map to mark where a free AI shell will be. -// This can be turned off in the config (and is off by default). -// Note that mapping in more than one of these will result in multiple shells. -/obj/effect/landmark/free_ai_shell - name = "free ai shell spawner" - icon = 'icons/mob/screen1.dmi' - icon_state = "x3" - delete_me = TRUE - -/obj/effect/landmark/free_ai_shell/Initialize() - if(config.allow_ai_shells && config.give_free_ai_shell) - new /mob/living/silicon/robot/ai_shell(get_turf(src)) - return ..() +// This file holds things required for remote borg control by an AI. + +GLOBAL_LIST_EMPTY(available_ai_shells) + +/mob/living/silicon/robot + var/shell = FALSE + var/deployed = FALSE + var/mob/living/silicon/ai/mainframe = null + +// Premade AI shell, for roundstart shells. +/mob/living/silicon/robot/ai_shell/Initialize() + mmi = new /obj/item/device/mmi/inert/ai_remote(src) + post_mmi_setup() + return ..() + +// Call after inserting or instantiating an MMI. +/mob/living/silicon/robot/proc/post_mmi_setup() + if(istype(mmi, /obj/item/device/mmi/inert/ai_remote)) + make_shell() + playsound(src, 'sound/machines/twobeep.ogg', 50, 0) + else + playsound(src, 'sound/voice/liveagain.ogg', 75, 1) + return + +/mob/living/silicon/robot/proc/make_shell() + shell = TRUE + braintype = "AI Shell" + SetName("[modtype] AI Shell [num2text(ident)]") + rbPDA = new /obj/item/device/pda/ai/shell(src) + setup_PDA() + GLOB.available_ai_shells |= src + if(!QDELETED(camera)) + camera.c_tag = real_name //update the camera name too + notify_ai(ROBOT_NOTIFICATION_AI_SHELL) + update_icon() + +/mob/living/silicon/robot/proc/revert_shell() + if(!shell) + return + undeploy() + shell = FALSE + GLOB.available_ai_shells -= src + if(!QDELETED(camera)) + camera.c_tag = real_name + update_icon() + +// This should be called before the AI client/mind is actually moved. +/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI) + // Set the name when the AI steps inside. + SetName("[AI.real_name] shell [num2text(ident)]") + if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites. + sprite_name = AI.real_name + if(!QDELETED(camera)) + camera.c_tag = real_name + + // Have the borg have eyes when active. + mainframe = AI + deployed = TRUE + update_icon() + + // Laws. + connected_ai = mainframe // So they share laws. + mainframe.connected_robots |= src + lawsync() + + // Give button to leave. + verbs += /mob/living/silicon/robot/proc/undeploy_act + to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.
                    \ + To return to your core, use the Release Control verb.")) + + // Languages and comms. + languages = AI.languages.Copy() + speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy() + if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator. +// if(AI.radio.syndie) +// radio.make_syndie() + radio.subspace_transmission = TRUE + radio.channels = AI.aiRadio.channels + +// Called after the AI transfers over. +/mob/living/silicon/robot/proc/post_deploy() + return + +/mob/living/silicon/robot/proc/undeploy(message) + if(!deployed || !mind || !mainframe) + return +// mainframe.redeploy_action.Grant(mainframe) +// mainframe.redeploy_action.last_used_shell = src + if(message) + to_chat(src, span("notice", message)) + mind.transfer_to(mainframe) + deployed = FALSE + update_icon() + mainframe.teleop = null + mainframe.deployed_shell = null + SetName("[modtype] AI Shell [num2text(ident)]") +// undeployment_action.Remove(src) + if(radio) //Return radio to normal + radio.recalculateChannels() + if(!QDELETED(camera)) + camera.c_tag = real_name //update the camera name too +// diag_hud_set_aishell() +// mainframe.diag_hud_set_deployed() + if(mainframe.laws) + mainframe.laws.show_laws(mainframe) //Always remind the AI when switching + mainframe = null + +/mob/living/silicon/robot/proc/undeploy_act() + set name = "Release Control" + set desc = "Release control of a remote drone." + set category = "Robot Commands" + + undeploy("Remote session terminated.") + +/mob/living/silicon/robot/attack_ai(mob/user) + if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user)) + var/mob/living/silicon/ai/AI = user + if(istype(AI)) // Just in case we're clicked by a borg + AI.deploy_to_shell(src) + else + return ..() + +// Place this on your map to mark where a free AI shell will be. +// This can be turned off in the config (and is off by default). +// Note that mapping in more than one of these will result in multiple shells. +/obj/effect/landmark/free_ai_shell + name = "free ai shell spawner" + icon = 'icons/mob/screen1.dmi' + icon_state = "x3" + delete_me = TRUE + +/obj/effect/landmark/free_ai_shell/Initialize() + if(config.allow_ai_shells && config.give_free_ai_shell) + new /mob/living/silicon/robot/ai_shell(get_turf(src)) + return ..() diff --git a/code/modules/mob/living/silicon/robot/sprites/_sprite_datum.dm b/code/modules/mob/living/silicon/robot/sprites/_sprite_datum.dm index 5613f4afd1e..53e2a412426 100644 --- a/code/modules/mob/living/silicon/robot/sprites/_sprite_datum.dm +++ b/code/modules/mob/living/silicon/robot/sprites/_sprite_datum.dm @@ -12,6 +12,8 @@ var/has_custom_open_sprites = FALSE var/has_vore_belly_sprites = FALSE var/has_vore_belly_resting_sprites = FALSE + var/has_sleeper_light_indicator = FALSE //Moved here because there's no reason lights should be limited to just medical borgs. Or redefined every time they ARE used. + var/max_belly_size = 1 //If larger bellies are made, set this to the value of the largest size var/has_rest_sprites = FALSE var/list/rest_sprite_options var/has_dead_sprite = FALSE @@ -28,11 +30,25 @@ /datum/robot_sprite/proc/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) return -/datum/robot_sprite/proc/get_belly_overlay(var/mob/living/silicon/robot/ourborg) - return "[sprite_icon_state]-sleeper" +/datum/robot_sprite/proc/get_belly_overlay(var/mob/living/silicon/robot/ourborg, var/size = 1) + //Size + if(has_sleeper_light_indicator) + var/sleeperColor = "g" + if(ourborg.sleeper_state == 1) // Is our belly safe, or gurgling cuties? + sleeperColor = "r" + return "[sprite_icon_state]-sleeper-[size]-[sleeperColor]" + return "[sprite_icon_state]-sleeper-[size]" -/datum/robot_sprite/proc/get_belly_resting_overlay(var/mob/living/silicon/robot/ourborg) - return +/datum/robot_sprite/proc/get_belly_resting_overlay(var/mob/living/silicon/robot/ourborg, var/size = 1) + if(!(ourborg.rest_style in rest_sprite_options)) + ourborg.rest_style = "Default" + switch(ourborg.rest_style) + if("Sit") + return "[get_belly_overlay(ourborg, size)]-sit" + if("Bellyup") + return "[get_belly_overlay(ourborg, size)]-bellyup" + else + return "[get_belly_overlay(ourborg, size)]-rest" /datum/robot_sprite/proc/get_eyes_overlay(var/mob/living/silicon/robot/ourborg) if(!(ourborg.resting && has_rest_sprites)) @@ -47,7 +63,15 @@ return /datum/robot_sprite/proc/get_rest_sprite(var/mob/living/silicon/robot/ourborg) - return + if(!(ourborg.rest_style in rest_sprite_options)) + ourborg.rest_style = "Default" + switch(ourborg.rest_style) + if("Sit") + return "[sprite_icon_state]-sit" + if("Bellyup") + return "[sprite_icon_state]-bellyup" + else + return "[sprite_icon_state]-rest" /datum/robot_sprite/proc/get_dead_sprite(var/mob/living/silicon/robot/ourborg) return "[sprite_icon_state]-wreck" @@ -88,7 +112,7 @@ has_dead_sprite_overlay = TRUE has_custom_equipment_sprites = TRUE pixel_x = -16 - +/* //Does not need to be dogborg-only, letting all borgs use these -Reo /datum/robot_sprite/dogborg/get_rest_sprite(var/mob/living/silicon/robot/ourborg) if(!(ourborg.rest_style in rest_sprite_options)) ourborg.rest_style = "Default" @@ -102,7 +126,7 @@ /datum/robot_sprite/dogborg/get_belly_overlay(var/mob/living/silicon/robot/ourborg) return "[sprite_icon_state]-sleeper" - +*/ /datum/robot_sprite/dogborg/do_equipment_glamour(var/obj/item/weapon/robot_module/module) if(!has_custom_equipment_sprites) return @@ -130,4 +154,4 @@ module_type = "Default" sprite_icon = 'icons/mob/robot/default.dmi' sprite_icon_state = "default" - default_sprite = TRUE \ No newline at end of file + default_sprite = TRUE diff --git a/code/modules/mob/living/silicon/robot/sprites/civilian.dm b/code/modules/mob/living/silicon/robot/sprites/civilian.dm index b6b1fcca533..cddce131dbc 100644 --- a/code/modules/mob/living/silicon/robot/sprites/civilian.dm +++ b/code/modules/mob/living/silicon/robot/sprites/civilian.dm @@ -1,4 +1,4 @@ -// Clerical and Service modules have a lot of shared sprites so they're in same file + // Clerical and Service modules have a lot of shared sprites so they're in same file // (some of those sprites are also shared with Standard, but ive already done it as its own thing, so some duplicates are fine) // Both Service and Clerical @@ -186,13 +186,13 @@ name = "Hound V2" sprite_icon_state = "vale" has_eye_light_sprites = TRUE - has_vore_belly_sprites = FALSE + has_vore_belly_sprites = TRUE /datum/robot_sprite/dogborg/service/valedark name = "Hound V2 Darkmode" sprite_icon_state = "valedark" has_eye_light_sprites = TRUE - has_vore_belly_sprites = FALSE + has_vore_belly_sprites = TRUE /datum/robot_sprite/dogborg/service/drake name = "Drake" @@ -220,11 +220,11 @@ else ourborg.icon_state = booze_options[ourborg.sprite_extra_customization["boozehound"]] -/datum/robot_sprite/dogborg/service/booze/get_belly_overlay(var/mob/living/silicon/robot/ourborg) +/datum/robot_sprite/dogborg/service/booze/get_belly_overlay(var/mob/living/silicon/robot/ourborg, var/size = 1) if(!("boozehound" in ourborg.sprite_extra_customization) || !ourborg.sprite_extra_customization["boozehound"]) return ..() else - return "[booze_options[ourborg.sprite_extra_customization["boozehound"]]]-sleeper" + return "[booze_options[ourborg.sprite_extra_customization["boozehound"]]]-sleeper-[size]" /datum/robot_sprite/dogborg/service/booze/get_rest_sprite(var/mob/living/silicon/robot/ourborg) if(!(ourborg.rest_style in rest_sprite_options)) @@ -364,13 +364,21 @@ has_dead_sprite = TRUE // Wide/dogborg sprites -/* + /datum/robot_sprite/dogborg/clerical module_type = "Clerical" sprite_icon = 'icons/mob/robot/clerical_wide.dmi' - // None yet -*/ +/datum/robot_sprite/dogborg/clerical/vale + name = "Hound V2" + sprite_icon_state = "vale" + has_eye_light_sprites = TRUE + +/datum/robot_sprite/dogborg/clerical/otie + name = "Otieborg" + sprite_icon_state = "otie" + has_eye_light_sprites = TRUE + // Tall sprites /* /datum/robot_sprite/dogborg/tall/clerical @@ -378,4 +386,4 @@ sprite_icon = 'icons/mob/robot/clerical_large.dmi' // None yet -*/ \ No newline at end of file +*/ diff --git a/code/modules/mob/living/silicon/robot/sprites/combat.dm b/code/modules/mob/living/silicon/robot/sprites/combat.dm index 32c8b09fcc8..3ff24f321ff 100644 --- a/code/modules/mob/living/silicon/robot/sprites/combat.dm +++ b/code/modules/mob/living/silicon/robot/sprites/combat.dm @@ -8,9 +8,8 @@ var/has_shield_sprite = FALSE /datum/robot_sprite/combat/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) - if(has_speed_sprite) - if(istype(ourborg.module_active,/obj/item/borg/combat/mobility)) - ourborg.icon_state = "[sprite_icon_state]-roll" + if(has_speed_sprite && istype(ourborg.module_active, /obj/item/borg/combat/mobility)) + ourborg.icon_state = "[sprite_icon_state]-roll" if(has_shield_sprite) if(ourborg.has_active_type(/obj/item/borg/combat/shield)) var/obj/item/borg/combat/shield/shield = locate() in ourborg @@ -90,10 +89,20 @@ has_custom_equipment_sprites = TRUE var/has_gun_sprite = FALSE + var/has_speed_sprite = FALSE + var/has_shield_sprite = FALSE /datum/robot_sprite/dogborg/tall/combat/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) if(has_gun_sprite && (istype(ourborg.module_active, /obj/item/weapon/gun/energy/laser/mounted) || istype(ourborg.module_active, /obj/item/weapon/gun/energy/taser/mounted/cyborg/ertgun) || istype(ourborg.module_active, /obj/item/weapon/gun/energy/lasercannon/mounted))) ourborg.add_overlay("[sprite_icon_state]-gun") + if(has_speed_sprite && (istype(ourborg.module_active, /obj/item/borg/combat/mobility))) + ourborg.icon_state = "[sprite_icon_state]-roll" + if(has_shield_sprite) + if(ourborg.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in ourborg + if(shield && shield.active) + ourborg.add_overlay("[sprite_icon_state]-shield") + /datum/robot_sprite/dogborg/tall/combat/do_equipment_glamour(var/obj/item/weapon/robot_module/module) if(!has_custom_equipment_sprites) @@ -101,11 +110,11 @@ ..() - var/obj/item/weapon/combat_borgblade/CBB = locate() in module.modules + var/obj/item/weapon/melee/combat_borgblade/CBB = locate() in module.modules if(CBB) CBB.name = "sword tail" CBB.desc = "A glowing dagger normally attached to the end of a cyborg's tail. It appears to be extremely sharp." - var/obj/item/weapon/borg_combat_shocker/BCS = locate() in module.modules + var/obj/item/weapon/melee/borg_combat_shocker/BCS = locate() in module.modules if(BCS) BCS.name = "combat jaws" BCS.desc = "Shockingly chompy!" @@ -132,4 +141,30 @@ sprite_hud_icon_state = "ert" rest_sprite_options = list("Default") has_eye_sprites = FALSE - has_eye_light_sprites = TRUE \ No newline at end of file + has_eye_light_sprites = TRUE + +/datum/robot_sprite/dogborg/tall/combat/raptor + name = "Raptor V-4" + sprite_icon_state = "raptor" + sprite_hud_icon_state = "ert" + rest_sprite_options = list("Default", "Bellyup") + has_gun_sprite = TRUE + has_eye_light_sprites = TRUE + has_shield_sprite = TRUE + has_speed_sprite = TRUE + +/datum/robot_sprite/dogborg/tall/combat/raptor/get_eyes_overlay(var/mob/living/silicon/robot/ourborg) + if(istype(ourborg.module_active,/obj/item/borg/combat/mobility)) + return + else + return ..() +/datum/robot_sprite/dogborg/tall/combat/raptor/get_eye_light_overlay(var/mob/living/silicon/robot/ourborg) + if(istype(ourborg.module_active,/obj/item/borg/combat/mobility)) + return + else + return ..() +/datum/robot_sprite/dogborg/tall/combat/raptor/get_belly_overlay(var/mob/living/silicon/robot/ourborg) + if(istype(ourborg.module_active,/obj/item/borg/combat/mobility)) + return + else + return ..() diff --git a/code/modules/mob/living/silicon/robot/sprites/event.dm b/code/modules/mob/living/silicon/robot/sprites/event.dm index 71cecd56eed..681e0f2fcfb 100644 --- a/code/modules/mob/living/silicon/robot/sprites/event.dm +++ b/code/modules/mob/living/silicon/robot/sprites/event.dm @@ -47,6 +47,32 @@ sprite_icon_state = "stray" +// Tall sprites + +/datum/robot_sprite/dogborg/tall/lost + module_type = "Lost" + sprite_icon = 'icons/mob/robot/lost_large.dmi' + sprite_hud_icon_state = "lost" + + var/has_shield_sprite = FALSE + var/has_laser_sprite = FALSE + +/datum/robot_sprite/dogborg/tall/lost/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) + if(has_laser_sprite && istype(ourborg.module_active, /obj/item/weapon/gun/energy/retro/mounted)) + ourborg.add_overlay("[sprite_icon_state]-laser") + if(has_shield_sprite) + if(ourborg.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in ourborg + if(shield && shield.active) + ourborg.add_overlay("[sprite_icon_state]-shield") + +/datum/robot_sprite/dogborg/tall/lost/raptor + name = "Raptor V-4" + sprite_icon_state = "raptor" + has_shield_sprite = TRUE + has_laser_sprite = TRUE + + // Gravekeeper // Regular sprites @@ -73,4 +99,30 @@ /datum/robot_sprite/gravekeeper/sleek name = "WTOperator" sprite_icon_state = "sleek" - has_shield_sprite = TRUE \ No newline at end of file + has_shield_sprite = TRUE + + +// Tall sprites + +/datum/robot_sprite/dogborg/tall/gravekeeper + module_type = "Gravekeeper" + sprite_icon = 'icons/mob/robot/gravekeeper_large.dmi' + sprite_hud_icon_state = "lost" + + var/has_shield_sprite = FALSE + var/has_laser_sprite = FALSE + +/datum/robot_sprite/dogborg/tall/gravekeeper/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) + if(has_laser_sprite && istype(ourborg.module_active, /obj/item/weapon/gun/energy/retro/mounted)) + ourborg.add_overlay("[sprite_icon_state]-laser") + if(has_shield_sprite) + if(ourborg.has_active_type(/obj/item/borg/combat/shield)) + var/obj/item/borg/combat/shield/shield = locate() in ourborg + if(shield && shield.active) + ourborg.add_overlay("[sprite_icon_state]-shield") + +/datum/robot_sprite/dogborg/tall/gravekeeper/raptor + name = "Raptor V-4" + sprite_icon_state = "raptor" + has_shield_sprite = TRUE + has_laser_sprite = TRUE diff --git a/code/modules/mob/living/silicon/robot/sprites/medical.dm b/code/modules/mob/living/silicon/robot/sprites/medical.dm index 45fe8ff453a..3030d828f8a 100644 --- a/code/modules/mob/living/silicon/robot/sprites/medical.dm +++ b/code/modules/mob/living/silicon/robot/sprites/medical.dm @@ -189,15 +189,14 @@ module_type = "Surgeon" sprite_icon = 'icons/mob/robot/surgical_wide.dmi' - var/has_sleeper_light_indicator = FALSE - +/* //Handled by the normal belly code now. /datum/robot_sprite/dogborg/surgical/get_belly_overlay(var/mob/living/silicon/robot/ourborg) if(has_sleeper_light_indicator) if(ourborg.sleeper_state == 2 && !(ourborg.vore_selected?.silicon_belly_overlay_preference == "Vorebelly")) return "[sprite_icon_state]-sleeper_g" else return "[sprite_icon_state]-sleeper_r" else return ..() - +*/ /datum/robot_sprite/dogborg/surgical/do_equipment_glamour(var/obj/item/weapon/robot_module/module) if(!has_custom_equipment_sprites) return @@ -314,15 +313,14 @@ module_type = "Crisis" sprite_icon = 'icons/mob/robot/crisis_wide.dmi' - var/has_sleeper_light_indicator = FALSE - +/* //Handled by the normal belly code now. /datum/robot_sprite/dogborg/crisis/get_belly_overlay(var/mob/living/silicon/robot/ourborg) if(has_sleeper_light_indicator) if(ourborg.sleeper_state == 2 && !(ourborg.vore_selected?.silicon_belly_overlay_preference == "Vorebelly")) return "[sprite_icon_state]-sleeper_g" else return "[sprite_icon_state]-sleeper_r" else return ..() - +*/ /datum/robot_sprite/dogborg/crisis/do_equipment_glamour(var/obj/item/weapon/robot_module/module) if(!has_custom_equipment_sprites) return diff --git a/code/modules/mob/living/silicon/robot/sprites/science.dm b/code/modules/mob/living/silicon/robot/sprites/science.dm index 4443f90a198..7232bd97262 100644 --- a/code/modules/mob/living/silicon/robot/sprites/science.dm +++ b/code/modules/mob/living/silicon/robot/sprites/science.dm @@ -148,6 +148,12 @@ module_type = "Research" sprite_icon = 'icons/mob/robot/science_large.dmi' + var/has_taser_sprite = FALSE + +/datum/robot_sprite/dogborg/tall/science/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) + if(has_taser_sprite && istype(ourborg.module_active, /obj/item/weapon/gun/energy/taser/xeno/robot)) + ourborg.add_overlay("[sprite_icon_state]-taser") + /datum/robot_sprite/dogborg/tall/science/do_equipment_glamour(var/obj/item/weapon/robot_module/module) if(!has_custom_equipment_sprites) return @@ -166,6 +172,7 @@ name = "Raptor V-4" sprite_icon_state = "raptor" has_custom_equipment_sprites = TRUE + has_taser_sprite = TRUE rest_sprite_options = list("Default", "Bellyup") /datum/robot_sprite/dogborg/tall/science/meka @@ -203,4 +210,4 @@ has_eye_light_sprites = TRUE has_custom_open_sprites = TRUE has_vore_belly_sprites = FALSE - rest_sprite_options = list("Default", "Bellyup") \ No newline at end of file + rest_sprite_options = list("Default", "Bellyup") diff --git a/code/modules/mob/living/silicon/robot/sprites/security.dm b/code/modules/mob/living/silicon/robot/sprites/security.dm index fb38d159f42..c42ea7279ed 100644 --- a/code/modules/mob/living/silicon/robot/sprites/security.dm +++ b/code/modules/mob/living/silicon/robot/sprites/security.dm @@ -182,10 +182,21 @@ module_type = "Security" sprite_icon = 'icons/mob/robot/security_large.dmi' + var/has_laser_sprite = FALSE + var/has_taser_sprite = FALSE + +/datum/robot_sprite/dogborg/tall/security/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) + if(has_laser_sprite && istype(ourborg.module_active, /obj/item/weapon/gun/energy/laser/mounted)) + ourborg.add_overlay("[sprite_icon_state]-laser") + if(has_taser_sprite && istype(ourborg.module_active, /obj/item/weapon/gun/energy/taser/mounted/cyborg)) + ourborg.add_overlay("[sprite_icon_state]-taser") + /datum/robot_sprite/dogborg/tall/security/raptor name = "Raptor V-4" sprite_icon_state = "raptor" has_custom_equipment_sprites = TRUE + has_laser_sprite = TRUE + has_taser_sprite = TRUE rest_sprite_options = list("Default", "Bellyup") /datum/robot_sprite/dogborg/tall/security/meka @@ -223,4 +234,4 @@ has_eye_light_sprites = TRUE has_custom_open_sprites = TRUE has_vore_belly_sprites = FALSE - rest_sprite_options = list("Default", "Bellyup") \ No newline at end of file + rest_sprite_options = list("Default", "Bellyup") diff --git a/code/modules/mob/living/silicon/robot/sprites/syndicate.dm b/code/modules/mob/living/silicon/robot/sprites/syndicate.dm index d36c57235c2..2bc63bbe066 100644 --- a/code/modules/mob/living/silicon/robot/sprites/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/sprites/syndicate.dm @@ -1,5 +1,75 @@ // Syndie borg sprites +/* none yet +/datum/robot_sprite/syndie + module_type = list("Protector", "Mechanist", "Combat Medic") + sprite_icon = 'icons/mob/robot/syndie.dmi' + sprite_hud_icon_state = "malf" +*/ + +// Wide/dogborg sprites + +/datum/robot_sprite/dogborg/syndie + module_type = list("Protector", "Mechanist", "Combat Medic") + sprite_icon = 'icons/mob/robot/syndie_wide.dmi' + sprite_hud_icon_state = "malf" + +/datum/robot_sprite/dogborg/syndie/borgi + name = "Borgi" + sprite_icon_state = "borgi" + has_eye_sprites = FALSE + has_eye_light_sprites = TRUE + has_dead_sprite_overlay = FALSE + +/datum/robot_sprite/dogborg/syndie/drake + name = "Drake" + sprite_icon_state = "drake" + +// Tall sprites + +/datum/robot_sprite/dogborg/tall/syndie + module_type = list("Protector", "Mechanist", "Combat Medic") + sprite_icon = 'icons/mob/robot/syndie_large.dmi' + sprite_hud_icon_state = "malf" + +/datum/robot_sprite/dogborg/tall/syndie/meka + name = "MEKA" + sprite_icon_state = "meka" + has_eye_light_sprites = TRUE + has_custom_open_sprites = TRUE + has_vore_belly_sprites = FALSE + rest_sprite_options = list("Default", "Sit") + +/datum/robot_sprite/dogborg/tall/syndie/newmeka + name = "MEKA v2" + sprite_icon_state = "newmeka" + has_eye_light_sprites = TRUE + has_custom_open_sprites = TRUE + rest_sprite_options = list("Default", "Sit") + +/datum/robot_sprite/dogborg/tall/syndie/mmeka + name = "NIKO" + sprite_icon_state = "mmeka" + has_eye_light_sprites = TRUE + has_custom_open_sprites = TRUE + rest_sprite_options = list("Default", "Sit") + +/datum/robot_sprite/dogborg/tall/syndie/fmeka + name = "NIKA" + sprite_icon_state = "fmeka" + has_eye_light_sprites = TRUE + has_custom_open_sprites = TRUE + rest_sprite_options = list("Default", "Sit") + +/datum/robot_sprite/dogborg/tall/syndie/k4t + name = "K4T" + sprite_icon_state = "k4t" + has_eye_light_sprites = TRUE + has_custom_open_sprites = TRUE + has_vore_belly_sprites = FALSE + rest_sprite_options = list("Default", "Bellyup") + + // Protector // Regular sprites @@ -25,6 +95,37 @@ name = "XI-ALP" sprite_icon_state = "heavy" +// Wide/dogborg sprites + +/datum/robot_sprite/dogborg/protector + module_type = "Protector" + sprite_icon = 'icons/mob/robot/protector_wide.dmi' + sprite_hud_icon_state = "malf" + +/datum/robot_sprite/dogborg/protector/k9 + name = "K9" + sprite_icon_state = "k9" + has_eye_light_sprites = TRUE + +// Tall sprites + +/datum/robot_sprite/dogborg/tall/protector + module_type = "Protector" + sprite_icon = 'icons/mob/robot/syndie_large.dmi' + sprite_hud_icon_state = "malf" + + var/has_gun_sprite = FALSE + +/datum/robot_sprite/dogborg/tall/protector/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) + if(has_gun_sprite && istype (ourborg.module_active, /obj/item/weapon/gun/energy/dakkalaser)) + ourborg.add_overlay("[sprite_icon_state]-gun") + +/datum/robot_sprite/dogborg/tall/protector/syndiprotraptor + name = "Raptor V-4" + sprite_icon_state = "syndiprotraptor" + has_eye_light_sprites = TRUE + has_gun_sprite = TRUE + rest_sprite_options = list("Default", "Bellyup") // Mechanist @@ -48,6 +149,7 @@ /datum/robot_sprite/dogborg/mechanist module_type = "Mechanist" sprite_icon = 'icons/mob/robot/mechanist_wide.dmi' + sprite_hud_icon_state = "malf" /datum/robot_sprite/dogborg/mechanist/pupdozer name = "Pupdozer" @@ -55,6 +157,18 @@ has_eye_light_sprites = TRUE rest_sprite_options = list("Default") +// Tall sprites + +/datum/robot_sprite/dogborg/tall/mechanist + module_type = "Mechanist" + sprite_icon = 'icons/mob/robot/syndie_large.dmi' + sprite_hud_icon_state = "malf" + +/datum/robot_sprite/dogborg/tall/mechanist/syndimechraptor + name = "Raptor V-4" + sprite_icon_state = "syndimechraptor" + has_eye_light_sprites = TRUE + rest_sprite_options = list("Default", "Bellyup") // Combat Medic @@ -74,15 +188,7 @@ /datum/robot_sprite/dogborg/combat_medic module_type = "Combat Medic" sprite_icon = 'icons/mob/robot/combat_medic_wide.dmi' - - var/has_sleeper_light_indicator = FALSE - -/datum/robot_sprite/dogborg/crisis/get_belly_overlay(var/mob/living/silicon/robot/ourborg) - if(has_sleeper_light_indicator) - if(ourborg.sleeper_state == 2 && !(ourborg.vore_selected?.silicon_belly_overlay_preference == "Vorebelly")) return "[sprite_icon_state]-sleeper_g" - else return "[sprite_icon_state]-sleeper_r" - else - return ..() + sprite_hud_icon_state = "malf" /datum/robot_sprite/dogborg/combat_medic/do_equipment_glamour(var/obj/item/weapon/robot_module/module) if(!has_custom_equipment_sprites) @@ -102,4 +208,17 @@ name = "Hound V2" sprite_icon_state = "vale" has_eye_light_sprites = TRUE - has_sleeper_light_indicator = TRUE \ No newline at end of file + has_sleeper_light_indicator = TRUE + +// Tall sprites + +/datum/robot_sprite/dogborg/tall/combat_medic + module_type = "Combat Medic" + sprite_icon = 'icons/mob/robot/syndie_large.dmi' + sprite_hud_icon_state = "malf" + +/datum/robot_sprite/dogborg/tall/combat_medic/syndimediraptor + name = "Raptor V-4" + sprite_icon_state = "syndimediraptor" + has_eye_light_sprites = TRUE + rest_sprite_options = list("Default", "Bellyup") diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm index 18d2b9ce80d..286b2abf75c 100644 --- a/code/modules/mob/living/silicon/robot/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/syndicate.dm @@ -8,9 +8,7 @@ /mob/living/silicon/robot/syndicate/New() if(!cell) - cell = new /obj/item/weapon/cell(src) - cell.maxcharge = 25000 - cell.charge = 25000 + cell = new /obj/item/weapon/cell/robot_syndi(src) ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index d16fc7418a5..95cd2805af3 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -266,7 +266,7 @@ set desc = "Sets an extended description of your character's features." set category = "IC" - var/new_flavortext = strip_html_simple(tgui_input_text(usr, "Please enter your new flavour text.", "Flavour text", null)) + var/new_flavortext = strip_html_simple(tgui_input_text(usr, "Please enter your new flavour text.", "Flavour text", flavor_text, multiline = TRUE)) if(new_flavortext) flavor_text = new_flavortext @@ -409,4 +409,4 @@ return 0 //NOT REAL EYES /mob/living/silicon/can_feed() - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/mob/living/silicon/subystems.dm b/code/modules/mob/living/silicon/subystems.dm index 890f1a8b370..6c46ecb2e88 100644 --- a/code/modules/mob/living/silicon/subystems.dm +++ b/code/modules/mob/living/silicon/subystems.dm @@ -1,110 +1,110 @@ -/mob/living/silicon - var/register_alarms = 1 - var/datum/tgui_module/alarm_monitor/all/robot/alarm_monitor - var/datum/tgui_module/atmos_control/robot/atmos_control - var/datum/tgui_module/crew_manifest/robot/crew_manifest - var/datum/tgui_module/crew_monitor/robot/crew_monitor - var/datum/tgui_module/law_manager/robot/law_manager - var/datum/tgui_module/power_monitor/robot/power_monitor - var/datum/tgui_module/rcon/robot/rcon - -/mob/living/silicon - var/list/silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, - /mob/living/silicon/proc/subsystem_crew_manifest, - /mob/living/silicon/proc/subsystem_law_manager - ) - -/mob/living/silicon/ai - silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, - /mob/living/silicon/proc/subsystem_atmos_control, - /mob/living/silicon/proc/subsystem_crew_manifest, - /mob/living/silicon/proc/subsystem_crew_monitor, - /mob/living/silicon/proc/subsystem_law_manager, - /mob/living/silicon/proc/subsystem_power_monitor, - /mob/living/silicon/proc/subsystem_rcon - ) - -/mob/living/silicon/robot/syndicate - register_alarms = 0 - silicon_subsystems = list(/mob/living/silicon/proc/subsystem_law_manager) - -/mob/living/silicon/proc/init_subsystems() - alarm_monitor = new(src) - atmos_control = new(src) - crew_manifest = new(src) - crew_monitor = new(src) - law_manager = new(src) - power_monitor = new(src) - rcon = new(src) - - if(!register_alarms) - return - - for(var/datum/alarm_handler/AH in SSalarm.all_handlers) - AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm) - queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order - -/******************** -* Alarm Monitor * -********************/ -/mob/living/silicon/proc/subsystem_alarm_monitor() - set name = "Alarm Monitor" - set category = "Subystems" - - alarm_monitor.tgui_interact(usr) - -/******************** -* Atmos Control * -********************/ -/mob/living/silicon/proc/subsystem_atmos_control() - set category = "Subystems" - set name = "Atmospherics Control" - - atmos_control.tgui_interact(usr) - -/******************** -* Crew Manifest * -********************/ -/mob/living/silicon/proc/subsystem_crew_manifest() - set category = "Subystems" - set name = "Crew Manifest" - - crew_manifest.tgui_interact(usr) - -/******************** -* Crew Monitor * -********************/ -/mob/living/silicon/proc/subsystem_crew_monitor() - set category = "Subystems" - set name = "Crew Monitor" - - crew_monitor.tgui_interact(usr) - -/**************** -* Law Manager * -****************/ -/mob/living/silicon/proc/subsystem_law_manager() - set name = "Law Manager" - set category = "Subystems" - - law_manager.tgui_interact(usr) - -/******************** -* Power Monitor * -********************/ -/mob/living/silicon/proc/subsystem_power_monitor() - set category = "Subystems" - set name = "Power Monitor" - - power_monitor.tgui_interact(usr) - -/************ -* RCON * -************/ -/mob/living/silicon/proc/subsystem_rcon() - set category = "Subystems" - set name = "RCON" - - rcon.tgui_interact(usr) +/mob/living/silicon + var/register_alarms = 1 + var/datum/tgui_module/alarm_monitor/all/robot/alarm_monitor + var/datum/tgui_module/atmos_control/robot/atmos_control + var/datum/tgui_module/crew_manifest/robot/crew_manifest + var/datum/tgui_module/crew_monitor/robot/crew_monitor + var/datum/tgui_module/law_manager/robot/law_manager + var/datum/tgui_module/power_monitor/robot/power_monitor + var/datum/tgui_module/rcon/robot/rcon + +/mob/living/silicon + var/list/silicon_subsystems = list( + /mob/living/silicon/proc/subsystem_alarm_monitor, + /mob/living/silicon/proc/subsystem_crew_manifest, + /mob/living/silicon/proc/subsystem_law_manager + ) + +/mob/living/silicon/ai + silicon_subsystems = list( + /mob/living/silicon/proc/subsystem_alarm_monitor, + /mob/living/silicon/proc/subsystem_atmos_control, + /mob/living/silicon/proc/subsystem_crew_manifest, + /mob/living/silicon/proc/subsystem_crew_monitor, + /mob/living/silicon/proc/subsystem_law_manager, + /mob/living/silicon/proc/subsystem_power_monitor, + /mob/living/silicon/proc/subsystem_rcon + ) + +/mob/living/silicon/robot/syndicate + register_alarms = 0 + silicon_subsystems = list(/mob/living/silicon/proc/subsystem_law_manager) + +/mob/living/silicon/proc/init_subsystems() + alarm_monitor = new(src) + atmos_control = new(src) + crew_manifest = new(src) + crew_monitor = new(src) + law_manager = new(src) + power_monitor = new(src) + rcon = new(src) + + if(!register_alarms) + return + + for(var/datum/alarm_handler/AH in SSalarm.all_handlers) + AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm) + queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order + +/******************** +* Alarm Monitor * +********************/ +/mob/living/silicon/proc/subsystem_alarm_monitor() + set name = "Alarm Monitor" + set category = "Subystems" + + alarm_monitor.tgui_interact(usr) + +/******************** +* Atmos Control * +********************/ +/mob/living/silicon/proc/subsystem_atmos_control() + set category = "Subystems" + set name = "Atmospherics Control" + + atmos_control.tgui_interact(usr) + +/******************** +* Crew Manifest * +********************/ +/mob/living/silicon/proc/subsystem_crew_manifest() + set category = "Subystems" + set name = "Crew Manifest" + + crew_manifest.tgui_interact(usr) + +/******************** +* Crew Monitor * +********************/ +/mob/living/silicon/proc/subsystem_crew_monitor() + set category = "Subystems" + set name = "Crew Monitor" + + crew_monitor.tgui_interact(usr) + +/**************** +* Law Manager * +****************/ +/mob/living/silicon/proc/subsystem_law_manager() + set name = "Law Manager" + set category = "Subystems" + + law_manager.tgui_interact(usr) + +/******************** +* Power Monitor * +********************/ +/mob/living/silicon/proc/subsystem_power_monitor() + set category = "Subystems" + set name = "Power Monitor" + + power_monitor.tgui_interact(usr) + +/************ +* RCON * +************/ +/mob/living/silicon/proc/subsystem_rcon() + set category = "Subystems" + set name = "RCON" + + rcon.tgui_interact(usr) diff --git a/code/modules/mob/living/simple_mob/donteatpets_vr.dm b/code/modules/mob/living/simple_mob/donteatpets_vr.dm index adf7d00976a..98d6b0283fe 100644 --- a/code/modules/mob/living/simple_mob/donteatpets_vr.dm +++ b/code/modules/mob/living/simple_mob/donteatpets_vr.dm @@ -1,60 +1,60 @@ -//I figured since it's basically always frowned upon to eat the station pets, it would probably be a good idea to just make that not possible normally. - -/mob/living/simple_mob/animal/passive/dog/corgi/Ian - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/dog/corgi/Lisa - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/dog/corgi/puppy - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/cat/runtime - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/cat/kitten - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/bird/parrot/poly - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/opossum/poppy - digestable = 0 - devourable = 0 - -/mob/living/carbon/human/monkey/punpun - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/snake/python/noodle - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/mouse/white/apple //She's a mouse living with a snake. Accidents happen. But don't gurg apple >:I - digestable = 0 - -/mob/living/simple_mob/animal/passive/fox/renault - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/crab/Coffee - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/sif/fluffy - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick - digestable = 0 - devourable = 0 - -/mob/living/simple_mob/animal/passive/chick - digestable = 0 +//I figured since it's basically always frowned upon to eat the station pets, it would probably be a good idea to just make that not possible normally. + +/mob/living/simple_mob/animal/passive/dog/corgi/Ian + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/dog/corgi/Lisa + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/dog/corgi/puppy + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/cat/runtime + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/cat/kitten + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/bird/parrot/poly + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/opossum/poppy + digestable = 0 + devourable = 0 + +/mob/living/carbon/human/monkey/punpun + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/snake/python/noodle + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/mouse/white/apple //She's a mouse living with a snake. Accidents happen. But don't gurg apple >:I + digestable = 0 + +/mob/living/simple_mob/animal/passive/fox/renault + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/crab/Coffee + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/sif/fluffy + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick + digestable = 0 + devourable = 0 + +/mob/living/simple_mob/animal/passive/chick + digestable = 0 devourable = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm index 47e60e5406e..69c0ed52d80 100644 --- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm +++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm @@ -31,6 +31,7 @@ var/vore_digest_chance = 25 // Chance to switch to digest mode if resisted var/vore_absorb_chance = 0 // Chance to switch to absorb mode if resisted var/vore_escape_chance = 25 // Chance of resisting out of mob + var/vore_escape_chance_absorbed = 20// Chance of absorbed prey finishing an escape. Requires a successful escape roll against the above as well. var/vore_stomach_name // The name for the first belly if not "stomach" var/vore_stomach_flavor // The flavortext for the first belly if not the default @@ -236,6 +237,7 @@ B.contamination_color = vore_default_contamination_color B.escapable = vore_escape_chance > 0 B.escapechance = vore_escape_chance + B.escapechance_absorbed = vore_escape_chance_absorbed B.digestchance = vore_digest_chance B.absorbchance = vore_absorb_chance B.human_prey_swallow_time = swallowTime diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm index 7ef5971f0fd..d203d129492 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/catslug.dm @@ -131,6 +131,7 @@ retaliate = TRUE speak_chance = 0.5 wander = TRUE + belly_attack = FALSE /mob/living/simple_mob/vore/alienanimals/catslug/Initialize() . = ..() @@ -954,6 +955,77 @@ /datum/say_list/catslug/custom/pilotslug speak = list("In the pipe, five my five.","Kick the tires and light the fires!","Bogeys on my tail!","GOOSE!","I'm really good at the stick.","I'm not doing nothing.","Heh.","Can you keep up?","Can't keep the sky from me.") +//Royal slug + +/mob/living/simple_mob/vore/alienanimals/catslug/custom/royalslug + name = "Ruler Purrton" + desc = "A golden-furred noodley bodied creature with thin arms and legs, and gloomy dark eyes. This one is adorned with a crown and red cloak, very fancy." + tt_desc = "Mollusca Felis Royallis" + icon_state = "catslugking" + icon_living = "catslugking" + icon_rest = "catslugking_rest" + icon_dead = "catslugking_dead" + catalogue_data = list(/datum/category_item/catalogue/fauna/catslug/custom/royalslug) + say_list_type = /datum/say_list/catslug/custom/royalslug + +/datum/category_item/catalogue/fauna/catslug/custom/royalslug + name = "Alien Wildlife - Catslug - Ruler Purrton" + desc = "Found in a castle beyond the redgate, Ruler Purrton\ + is a catslug who spends their days presiding over this low \ + technology town, living a life of luxury. Always seen with \ + their trademark crown and cloak, this litter critter seems \ + to just exude raw confidence and superiority. \ + \ + The Catslug is an omnivorous terrestrial creature.\ + Exhibiting properties of both a cat and a slug (hence its name)\ + it moves somewhat awkwardly. However, the unique qualities of\ + its body make it exceedingly flexible and smooth, allowing it to\ + wiggle into and move effectively in even extremely tight spaces.\ + Additionally, it has surprisingly capable hands, and moves quite\ + well on two legs or four. Caution is advised when interacting\ + with these creatures, they are quite intelligent, and proficient\ + tool users." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/say_list/catslug/custom/royalslug + speak = list("Let them eat cake. Lots and lots of cake.", "Fetch my good cloak.", "I myself prefer my ancient eggs for breakfast!", "Have you come to pay tribute?", "How dare you intrude?", "Bring me the finest of fine finery.", "HARK!", "With great power comes great dinner.") + +//crypt slug + +/mob/living/simple_mob/vore/alienanimals/catslug/custom/cryptslug + name = "Keeper Sluguloth" + desc = "A dark-furred noodley bodied creature with thin arms and legs, and gloomy dark eyes. This one is adorned with a dark cloak that obscures most of it's body." + tt_desc = "Mollusca Felis Necrosis" + icon_state = "cryptslug" + icon_living = "cryptslug" + icon_rest = "cryptslug_rest" + icon_dead = "cryptslug_dead" + catalogue_data = list(/datum/category_item/catalogue/fauna/catslug/custom/cryptslug) + say_list_type = /datum/say_list/catslug/custom/cryptslug + +/datum/category_item/catalogue/fauna/catslug/custom/cryptslug + name = "Alien Wildlife - Catslug - Keeper Sluguloth" + desc = "Found in a deep beneath a town beyond the redgate, Sluguloth\ + is a catslug who spends their days lurking within dark dungeons \ + alongside monstrous beings of all sorts. Always seen within \ + their dark cloak, obscuring them, this litter critter seems \ + to just exude pure menance and up-to-no-goodness. \ + \ + The Catslug is an omnivorous terrestrial creature.\ + Exhibiting properties of both a cat and a slug (hence its name)\ + it moves somewhat awkwardly. However, the unique qualities of\ + its body make it exceedingly flexible and smooth, allowing it to\ + wiggle into and move effectively in even extremely tight spaces.\ + Additionally, it has surprisingly capable hands, and moves quite\ + well on two legs or four. Caution is advised when interacting\ + with these creatures, they are quite intelligent, and proficient\ + tool users." + value = CATALOGUER_REWARD_TRIVIAL + +/datum/say_list/catslug/custom/cryptslug + speak = list("I have a lot of nasty friends.", "Do not test me.", "I shall rise again!", "How dare you step foot in my domain?", "Dare you indluge in dark desires?", "I am become death, one day.", "Foul creature!", "I used to think my life was a tragedy, but now I realize it's kind of okay actually.") + + //============================= //Admin-spawn only catslugs end //============================= diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/stardog.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/stardog.dm index e7e0e9ecca7..633df96ec8b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/stardog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/stardog.dm @@ -489,7 +489,7 @@ var/mob/living/simple_mob/vore/overmap/stardog/m = s.parent log_subtle(message,L) - message = "[L] [message]" + message = "[L] [message]" message = "(From the back of \the [m]) " + message message = encode_html_emphasis(message) @@ -500,7 +500,8 @@ for(var/mob/M as anything in vis_mobs) if(isnewplayer(M)) continue - if(isobserver(M) && !L.is_preference_enabled(/datum/client_preference/whisubtle_vis) && !M.client?.holder) + if(isobserver(M) && (!M.is_preference_enabled(/datum/client_preference/ghost_see_whisubtle) || \ + !L.is_preference_enabled(/datum/client_preference/whisubtle_vis) && !M.client?.holder)) spawn(0) M.show_message(undisplayed_message, 2) else @@ -1160,7 +1161,7 @@ return log_subtle(message,L) - message = "[L] [message]" + message = "[L] [message]" message = "(From within \the [s]) " + message message = encode_html_emphasis(message) @@ -1171,7 +1172,8 @@ for(var/mob/M as anything in vis_mobs) if(isnewplayer(M)) continue - if(isobserver(M) && !L.is_preference_enabled(/datum/client_preference/whisubtle_vis) && !M.client?.holder) + if(isobserver(M) && (!M.is_preference_enabled(/datum/client_preference/ghost_see_whisubtle) || \ + !L.is_preference_enabled(/datum/client_preference/whisubtle_vis) && !M.client?.holder)) spawn(0) M.show_message(undisplayed_message, 2) else diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm index 03c5d9160ea..50e54cf8aaa 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -1,274 +1,274 @@ -// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. -// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would -// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. - -/mob/living/simple_mob/animal/borer - name = "cortical borer" - desc = "A small, quivering sluglike creature." - icon_state = "brainslug" - item_state = "brainslug" - icon_living = "brainslug" - icon_dead = "brainslug_dead" - - response_help = "pokes" - response_disarm = "prods" - response_harm = "stomps on" - attacktext = list("nipped") - friendly = list("prods") - - organ_names = /decl/mob_organ_names/borer - - status_flags = CANPUSH - pass_flags = PASSTABLE - movement_cooldown = 1.5 - - universal_understand = TRUE - can_be_antagged = TRUE - - holder_type = /obj/item/weapon/holder/borer - ai_holder_type = null // This is player-controlled, always. - - var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. - var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. - - var/roundstart = FALSE // If true, spawning won't try to pull a ghost. - var/antag = TRUE // If false, will avoid setting up objectives and events - - var/chemicals = 10 // A resource used for reproduction and powers. - var/max_chemicals = 250 // Max of said resource. - var/true_name = null // String used when speaking among other worms. - var/controlling = FALSE // Used in human death ceck. - var/docile = FALSE // Sugar can stop borers from acting. - - var/has_reproduced = FALSE - var/used_dominate // world.time when the dominate power was last used. - -/mob/living/simple_mob/animal/borer/roundstart - roundstart = TRUE - -/mob/living/simple_mob/animal/borer/non_antag - antag = FALSE - -/mob/living/simple_mob/animal/borer/Login() - ..() - if(antag && mind) - borers.add_antagonist(mind) - -/mob/living/simple_mob/animal/borer/Initialize() - add_language("Cortical Link") - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - - true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" - - if(!roundstart && antag) - request_player() - - return ..() - -/mob/living/simple_mob/animal/borer/handle_special() - if(host && !stat && !host.stat) - // Handle docility. - if(host.reagents.has_reagent("sugar") && !docile) - var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." - var/target = controlling ? host : src - to_chat(target, span("warning", message)) - docile = TRUE - - else if(docile) - var/message = "You shake off your lethargy as the sugar leaves your host's blood." - var/target = controlling ? host : src - to_chat(target, span("notice", message)) - docile = FALSE - - // Chem regen. - if(chemicals < max_chemicals) - chemicals++ - - // Control stuff. - if(controlling) - if(docile) - to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) - host.release_control() - return - - if(prob(5)) - host.adjustBrainLoss(0.1) - - if(prob(host.brainloss/20)) - host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") - -/mob/living/simple_mob/animal/borer/Stat() - ..() - if(client.statpanel == "Status") - statpanel("Status") - if(emergency_shuttle) - var/eta_status = emergency_shuttle.get_status_panel_eta() - if(eta_status) - stat(null, eta_status) - stat("Chemicals", chemicals) - -/mob/living/simple_mob/animal/borer/proc/detatch() - if(!host || !controlling) - return - - if(istype(host, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = host - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - if(head) - head.implants -= src - - controlling = FALSE - - host.remove_language("Cortical Link") - host.verbs -= /mob/living/carbon/proc/release_control - host.verbs -= /mob/living/carbon/proc/punish_host - host.verbs -= /mob/living/carbon/proc/spawn_larvae - - if(host_brain) - // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. - // computer_id and IP are not updated magically on their own in offline mobs -walter0o - - // This shit need to die in a phoron fire. - - // host -> self - var/h2s_id = host.computer_id - var/h2s_ip= host.lastKnownIP - host.computer_id = null - host.lastKnownIP = null - - src.ckey = host.ckey - - if(!src.computer_id) - src.computer_id = h2s_id - - if(!host_brain.lastKnownIP) - src.lastKnownIP = h2s_ip - - // brain -> host - var/b2h_id = host_brain.computer_id - var/b2h_ip= host_brain.lastKnownIP - host_brain.computer_id = null - host_brain.lastKnownIP = null - - host.ckey = host_brain.ckey - - if(!host.computer_id) - host.computer_id = b2h_id - - if(!host.lastKnownIP) - host.lastKnownIP = b2h_ip - - qdel(host_brain) - - -/mob/living/simple_mob/animal/borer/proc/leave_host() - if(!host) - return - - if(host.mind) - borers.remove_antagonist(host.mind) - - forceMove(get_turf(host)) - - reset_view(null) - machine = null - - if(istype(host, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = host - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - if(head) - head.implants -= src - - host.reset_view(null) - host.machine = null - host = null - -/mob/living/simple_mob/animal/borer/proc/request_player() - var/datum/ghost_query/Q = new /datum/ghost_query/borer() - var/list/winner = Q.query() // This will sleep the proc for awhile. - if(winner.len) - var/mob/observer/dead/D = winner[1] - transfer_personality(D) - -/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) - if(!candidate || !candidate.mind) - return - - src.mind = candidate.mind - candidate.mind.current = src - ckey = candidate.ckey - - if(mind) - mind.assigned_role = "Cortical Borer" - mind.special_role = "Cortical Borer" - - to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ - into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ - your host and your eventual spawn safe and warm.")) - to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") - -/mob/living/simple_mob/animal/borer/cannot_use_vents() - return - -// This is awful but its literally say code. -/mob/living/simple_mob/animal/borer/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - message = sanitize(message) - message = capitalize(message) - - if(!message) - return - - if(stat >= DEAD) - return say_dead(message) - else if(stat) - return - - if(client && client.prefs.muted & MUTE_IC) - to_chat(src, span("danger", "You cannot speak in IC (muted).")) - return - - if(copytext(message, 1, 2) == "*") - return emote(copytext(message, 2)) - - var/list/message_pieces = parse_languages(message) - for(var/datum/multilingual_say_piece/S in message_pieces) - if(S.speaking && S.speaking.flags & HIVEMIND) - S.speaking.broadcast(src, trim(copytext(message, 3)), src.true_name) - return - - if(!host) - if(chemicals >= 30) - to_chat(src, span("alien", "..You emit a psionic pulse with an encoded message..")) - var/list/nearby_mobs = list() - for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals)))) - if(LM == src) - continue - if(!LM.stat) - nearby_mobs += LM - var/mob/living/speaker - if(nearby_mobs.len) - speaker = tgui_input_list(usr, "Choose a target speaker:", "Target Choice", nearby_mobs) - if(speaker) - log_admin("[src.ckey]/([src]) tried to force [speaker] to say: [message]") - message_admins("[src.ckey]/([src]) tried to force [speaker] to say: [message]") - speaker.say("[message]") - return - to_chat(src, span("alien", "..But nothing heard it..")) - else - to_chat(src, span("warning", "You have no host to speak to.")) - return //No host, no audible speech. - - to_chat(src, "You drop words into [host]'s mind: \"[message]\"") - to_chat(host, "Your own thoughts speak: \"[message]\"") - - for(var/mob/M in player_list) - if(istype(M, /mob/new_player)) - continue - else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") - - -/decl/mob_organ_names/borer - hit_zones = list("head", "central segment", "tail segment") +// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. +// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would +// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. + +/mob/living/simple_mob/animal/borer + name = "cortical borer" + desc = "A small, quivering sluglike creature." + icon_state = "brainslug" + item_state = "brainslug" + icon_living = "brainslug" + icon_dead = "brainslug_dead" + + response_help = "pokes" + response_disarm = "prods" + response_harm = "stomps on" + attacktext = list("nipped") + friendly = list("prods") + + organ_names = /decl/mob_organ_names/borer + + status_flags = CANPUSH + pass_flags = PASSTABLE + movement_cooldown = 1.5 + + universal_understand = TRUE + can_be_antagged = TRUE + + holder_type = /obj/item/weapon/holder/borer + ai_holder_type = null // This is player-controlled, always. + + var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. + var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. + + var/roundstart = FALSE // If true, spawning won't try to pull a ghost. + var/antag = TRUE // If false, will avoid setting up objectives and events + + var/chemicals = 10 // A resource used for reproduction and powers. + var/max_chemicals = 250 // Max of said resource. + var/true_name = null // String used when speaking among other worms. + var/controlling = FALSE // Used in human death ceck. + var/docile = FALSE // Sugar can stop borers from acting. + + var/has_reproduced = FALSE + var/used_dominate // world.time when the dominate power was last used. + +/mob/living/simple_mob/animal/borer/roundstart + roundstart = TRUE + +/mob/living/simple_mob/animal/borer/non_antag + antag = FALSE + +/mob/living/simple_mob/animal/borer/Login() + ..() + if(antag && mind) + borers.add_antagonist(mind) + +/mob/living/simple_mob/animal/borer/Initialize() + add_language("Cortical Link") + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" + + if(!roundstart && antag) + request_player() + + return ..() + +/mob/living/simple_mob/animal/borer/handle_special() + if(host && !stat && !host.stat) + // Handle docility. + if(host.reagents.has_reagent("sugar") && !docile) + var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." + var/target = controlling ? host : src + to_chat(target, span("warning", message)) + docile = TRUE + + else if(docile) + var/message = "You shake off your lethargy as the sugar leaves your host's blood." + var/target = controlling ? host : src + to_chat(target, span("notice", message)) + docile = FALSE + + // Chem regen. + if(chemicals < max_chemicals) + chemicals++ + + // Control stuff. + if(controlling) + if(docile) + to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) + host.release_control() + return + + if(prob(5)) + host.adjustBrainLoss(0.1) + + if(prob(host.brainloss/20)) + host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") + +/mob/living/simple_mob/animal/borer/Stat() + ..() + if(client.statpanel == "Status") + statpanel("Status") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + stat("Chemicals", chemicals) + +/mob/living/simple_mob/animal/borer/proc/detatch() + if(!host || !controlling) + return + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + controlling = FALSE + + host.remove_language("Cortical Link") + host.verbs -= /mob/living/carbon/proc/release_control + host.verbs -= /mob/living/carbon/proc/punish_host + host.verbs -= /mob/living/carbon/proc/spawn_larvae + + if(host_brain) + // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. + // computer_id and IP are not updated magically on their own in offline mobs -walter0o + + // This shit need to die in a phoron fire. + + // host -> self + var/h2s_id = host.computer_id + var/h2s_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + src.ckey = host.ckey + + if(!src.computer_id) + src.computer_id = h2s_id + + if(!host_brain.lastKnownIP) + src.lastKnownIP = h2s_ip + + // brain -> host + var/b2h_id = host_brain.computer_id + var/b2h_ip= host_brain.lastKnownIP + host_brain.computer_id = null + host_brain.lastKnownIP = null + + host.ckey = host_brain.ckey + + if(!host.computer_id) + host.computer_id = b2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = b2h_ip + + qdel(host_brain) + + +/mob/living/simple_mob/animal/borer/proc/leave_host() + if(!host) + return + + if(host.mind) + borers.remove_antagonist(host.mind) + + forceMove(get_turf(host)) + + reset_view(null) + machine = null + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + host.reset_view(null) + host.machine = null + host = null + +/mob/living/simple_mob/animal/borer/proc/request_player() + var/datum/ghost_query/Q = new /datum/ghost_query/borer() + var/list/winner = Q.query() // This will sleep the proc for awhile. + if(winner.len) + var/mob/observer/dead/D = winner[1] + transfer_personality(D) + +/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) + if(!candidate || !candidate.mind) + return + + src.mind = candidate.mind + candidate.mind.current = src + ckey = candidate.ckey + + if(mind) + mind.assigned_role = "Cortical Borer" + mind.special_role = "Cortical Borer" + + to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ + into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ + your host and your eventual spawn safe and warm.")) + to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") + +/mob/living/simple_mob/animal/borer/cannot_use_vents() + return + +// This is awful but its literally say code. +/mob/living/simple_mob/animal/borer/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + message = sanitize(message) + message = capitalize(message) + + if(!message) + return + + if(stat >= DEAD) + return say_dead(message) + else if(stat) + return + + if(client && client.prefs.muted & MUTE_IC) + to_chat(src, span("danger", "You cannot speak in IC (muted).")) + return + + if(copytext(message, 1, 2) == "*") + return emote(copytext(message, 2)) + + var/list/message_pieces = parse_languages(message) + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && S.speaking.flags & HIVEMIND) + S.speaking.broadcast(src, trim(copytext(message, 3)), src.true_name) + return + + if(!host) + if(chemicals >= 30) + to_chat(src, span("alien", "..You emit a psionic pulse with an encoded message..")) + var/list/nearby_mobs = list() + for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals)))) + if(LM == src) + continue + if(!LM.stat) + nearby_mobs += LM + var/mob/living/speaker + if(nearby_mobs.len) + speaker = tgui_input_list(usr, "Choose a target speaker:", "Target Choice", nearby_mobs) + if(speaker) + log_admin("[src.ckey]/([src]) tried to force [speaker] to say: [message]") + message_admins("[src.ckey]/([src]) tried to force [speaker] to say: [message]") + speaker.say("[message]") + return + to_chat(src, span("alien", "..But nothing heard it..")) + else + to_chat(src, span("warning", "You have no host to speak to.")) + return //No host, no audible speech. + + to_chat(src, "You drop words into [host]'s mind: \"[message]\"") + to_chat(host, "Your own thoughts speak: \"[message]\"") + + for(var/mob/M in player_list) + if(istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") + + +/decl/mob_organ_names/borer + hit_zones = list("head", "central segment", "tail segment") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm index 618efee6dc4..ab1d9cfbfec 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm @@ -1,64 +1,64 @@ -// Straight move from the old location, with the paths corrected. - -/mob/living/captive_brain - name = "host brain" - real_name = "host brain" - universal_understand = 1 - -/mob/living/captive_brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) - - if (src.client) - if(client.prefs.muted & MUTE_IC) - to_chat(src, "You cannot speak in IC (muted).") - return - - if(istype(src.loc, /mob/living/simple_mob/animal/borer)) - - message = sanitize(message) - if (!message) - return - log_say(message,src) - if (stat == 2) - return say_dead(message) - - var/mob/living/simple_mob/animal/borer/B = src.loc - to_chat(src, "You whisper silently, \"[message]\"") - to_chat(B.host, "The captive mind of [src] whispers, \"[message]\"") - - for (var/mob/M in player_list) - if (istype(M, /mob/new_player)) - continue - else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - to_chat(M, "The captive mind of [src] whispers, \"[message]\"") - -/mob/living/captive_brain/me_verb(message as text) - to_chat(src, "You cannot emote as a captive mind.") - return - -/mob/living/captive_brain/emote(var/message) - to_chat(src, "You cannot emote as a captive mind.") - return - -/mob/living/captive_brain/process_resist() - //Resisting control by an alien mind. - if(istype(src.loc, /mob/living/simple_mob/animal/borer)) - var/mob/living/simple_mob/animal/borer/B = src.loc - var/mob/living/captive_brain/H = src - - to_chat(H, "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).") - to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") - - spawn(rand(200,250)+B.host.brainloss) - if(!B || !B.controlling) return - - B.host.adjustBrainLoss(rand(0.1,0.5)) - to_chat(H, "With an immense exertion of will, you regain control of your body!") - to_chat(B.host, "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.") - B.detatch() - verbs -= /mob/living/carbon/proc/release_control - verbs -= /mob/living/carbon/proc/punish_host - verbs -= /mob/living/carbon/proc/spawn_larvae - - return - - ..() +// Straight move from the old location, with the paths corrected. + +/mob/living/captive_brain + name = "host brain" + real_name = "host brain" + universal_understand = 1 + +/mob/living/captive_brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0) + + if (src.client) + if(client.prefs.muted & MUTE_IC) + to_chat(src, span_red("You cannot speak in IC (muted).")) + return + + if(istype(src.loc, /mob/living/simple_mob/animal/borer)) + + message = sanitize(message) + if (!message) + return + log_say(message,src) + if (stat == 2) + return say_dead(message) + + var/mob/living/simple_mob/animal/borer/B = src.loc + to_chat(src, "You whisper silently, \"[message]\"") + to_chat(B.host, "The captive mind of [src] whispers, \"[message]\"") + + for (var/mob/M in player_list) + if (istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + to_chat(M, "The captive mind of [src] whispers, \"[message]\"") + +/mob/living/captive_brain/me_verb(message as text) + to_chat(src, "You cannot emote as a captive mind.") + return + +/mob/living/captive_brain/emote(var/message) + to_chat(src, "You cannot emote as a captive mind.") + return + +/mob/living/captive_brain/process_resist() + //Resisting control by an alien mind. + if(istype(src.loc, /mob/living/simple_mob/animal/borer)) + var/mob/living/simple_mob/animal/borer/B = src.loc + var/mob/living/captive_brain/H = src + + to_chat(H, "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).") + to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") + + spawn(rand(200,250)+B.host.brainloss) + if(!B || !B.controlling) return + + B.host.adjustBrainLoss(rand(0.1,0.5)) + to_chat(H, "With an immense exertion of will, you regain control of your body!") + to_chat(B.host, "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.") + B.detatch() + verbs -= /mob/living/carbon/proc/release_control + verbs -= /mob/living/carbon/proc/punish_host + verbs -= /mob/living/carbon/proc/spawn_larvae + + return + + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm index 39f3a27e874..62b0c2b2090 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm @@ -1,356 +1,356 @@ -/mob/living/simple_mob/animal/borer/verb/release_host() - set category = "Abilities" - set name = "Release Host" - set desc = "Slither out of your host." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(stat) - to_chat(src, "You cannot leave your host in your current state.") - - if(docile) - to_chat(src, "You are feeling far too docile to do that.") - return - - if(!host || !src) return - - to_chat(src, "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.") - - if(!host.stat) - to_chat(host, "An odd, uncomfortable pressure begins to build inside your skull, behind your ear...") - - spawn(100) - - if(!host || !src) return - - if(src.stat) - to_chat(src, "You cannot release your host in your current state.") - return - - to_chat(src, "You wiggle out of [host]'s ear and plop to the ground.") - if(host.mind) - if(!host.stat) - to_chat(host, "Something slimy wiggles out of your ear and plops to the ground!") - to_chat(host, "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again.") - - detatch() - leave_host() - -/mob/living/simple_mob/animal/borer/verb/infest() - set category = "Abilities" - set name = "Infest" - set desc = "Infest a suitable humanoid host." - - if(host) - to_chat(src, "You are already within a host.") - return - - if(stat) - to_chat(src, "You cannot infest a target in your current state.") - return - - var/list/choices = list() - for(var/mob/living/carbon/C in view(1,src)) - if(src.Adjacent(C)) - choices += C - - if(!choices.len) - to_chat(src, "There are no viable hosts within range...") - return - - var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to infest?", "Target Choice", choices) - - if(!M || !src) return - - if(!(src.Adjacent(M))) return - - if(M.has_brain_worms()) - to_chat(src, "You cannot infest someone who is already infested!") - return - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - - var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD] - if(!E || E.is_stump()) - to_chat(src, "\The [H] does not have a head!") - - if(!H.should_have_organ("brain")) - to_chat(src, "\The [H] does not seem to have an ear canal to breach.") - return - - if(H.check_head_coverage()) - to_chat(src, "You cannot get through that host's protective gear.") - return - - to_chat(M, "Something slimy begins probing at the opening of your ear canal...") - to_chat(src, "You slither up [M] and begin probing at their ear canal...") - - if(!do_after(src,30)) - to_chat(src, "As [M] moves away, you are dislodged and fall to the ground.") - return - - if(!M || !src) return - - if(src.stat) - to_chat(src, "You cannot infest a target in your current state.") - return - - if(M in view(1, src)) - to_chat(src, "You wiggle into [M]'s ear.") - if(!M.stat) - to_chat(M, "Something disgusting and slimy wiggles into your ear!") - - src.host = M - src.forceMove(M) - - //Update their traitor status. - if(host.mind) - borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome) - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/I = H.internal_organs_by_name["brain"] - if(!I) // No brain organ, so the borer moves in and replaces it permanently. - replace_brain() - else - // If they're in normally, implant removal can get them out. - var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - head.implants += src - - return - else - to_chat(src, "They are no longer in range!") - return - -/* -/mob/living/simple_mob/animal/borer/verb/devour_brain() - set category = "Abilities" - set name = "Devour Brain" - set desc = "Take permanent control of a dead host." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(host.stat != 2) - to_chat(src, "Your host is still alive.") - return - - if(stat) - to_chat(src, "You cannot do that in your current state.") - - if(docile) - to_chat(src, "You are feeling far too docile to do that.") - return - - - to_chat(src, "It only takes a few moments to render the dead host brain down into a nutrient-rich slurry...") - replace_brain() -*/ - -// BRAIN WORM ZOMBIES AAAAH. -/mob/living/simple_mob/animal/borer/proc/replace_brain() - - var/mob/living/carbon/human/H = host - - if(!istype(host)) - to_chat(src, "This host does not have a suitable brain.") - return - - to_chat(src, "You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H].") - - H.add_language("Cortical Link") - - if(host.stat == 2) - H.verbs |= /mob/living/carbon/human/proc/jumpstart - - H.verbs |= /mob/living/carbon/human/proc/psychic_whisper - H.verbs |= /mob/living/carbon/human/proc/tackle - if(antag) - H.verbs |= /mob/living/carbon/proc/spawn_larvae - - if(H.client) - H.ghostize(0) - - if(src.mind) - src.mind.special_role = "Borer Husk" - src.mind.transfer_to(host) - - H.ChangeToHusk() - - var/obj/item/organ/internal/borer/B = new(H) - H.internal_organs_by_name["brain"] = B - H.internal_organs |= B - - var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) - affecting.implants -= src - - var/s2h_id = src.computer_id - var/s2h_ip= src.lastKnownIP - src.computer_id = null - src.lastKnownIP = null - - if(!H.computer_id) - H.computer_id = s2h_id - - if(!H.lastKnownIP) - H.lastKnownIP = s2h_ip - -/mob/living/simple_mob/animal/borer/verb/secrete_chemicals() - set category = "Abilities" - set name = "Secrete Chemicals" - set desc = "Push some chemicals into your host's bloodstream." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(stat) - to_chat(src, "You cannot secrete chemicals in your current state.") - - if(docile) - to_chat(src, "You are feeling far too docile to do that.") - return - - if(chemicals < 50) - to_chat(src, "You don't have enough chemicals!") - - var/chem = tgui_input_list(usr, "Select a chemical to secrete.", "Chemicals", list("alkysine","bicaridine","hyperzine","tramadol")) - - if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check. - return - - to_chat(src, "You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream.") - host.reagents.add_reagent(chem, 10) - chemicals -= 50 - -/mob/living/simple_mob/animal/borer/verb/dominate_victim() - set category = "Abilities" - set name = "Paralyze Victim" - set desc = "Freeze the limbs of a potential host with supernatural fear." - - if(world.time - used_dominate < 150) - to_chat(src, "You cannot use that ability again so soon.") - return - - if(host) - to_chat(src, "You cannot do that from within a host body.") - return - - if(src.stat) - to_chat(src, "You cannot do that in your current state.") - return - - var/list/choices = list() - for(var/mob/living/carbon/C in view(3,src)) - if(C.stat != 2) - choices += C - - if(world.time - used_dominate < 150) - to_chat(src, "You cannot use that ability again so soon.") - return - - var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to dominate?", "Target Choice", choices) - - if(!M || !src) return - - if(M.has_brain_worms()) - to_chat(src, "You cannot infest someone who is already infested!") - return - - to_chat(src, "You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.") - to_chat(M, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.") - M.Weaken(10) - - used_dominate = world.time - -/mob/living/simple_mob/animal/borer/verb/bond_brain() - set category = "Abilities" - set name = "Assume Control" - set desc = "Fully connect to the brain of your host." - - if(!host) - to_chat(src, "You are not inside a host body.") - return - - if(src.stat) - to_chat(src, "You cannot do that in your current state.") - return - - if(docile) - to_chat(src, "You are feeling far too docile to do that.") - return - - to_chat(src, "You begin delicately adjusting your connection to the host brain...") - - spawn(100+(host.brainloss*5)) - - if(!host || !src || controlling) - return - else - - to_chat(src, "You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.") - to_chat(host, "You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.") - host.add_language("Cortical Link") - - // host -> brain - var/h2b_id = host.computer_id - var/h2b_ip= host.lastKnownIP - host.computer_id = null - host.lastKnownIP = null - - qdel(host_brain) - host_brain = new(src) - - host_brain.ckey = host.ckey - - host_brain.name = host.name - - if(!host_brain.computer_id) - host_brain.computer_id = h2b_id - - if(!host_brain.lastKnownIP) - host_brain.lastKnownIP = h2b_ip - - // self -> host - var/s2h_id = src.computer_id - var/s2h_ip= src.lastKnownIP - src.computer_id = null - src.lastKnownIP = null - - host.ckey = src.ckey - - if(!host.computer_id) - host.computer_id = s2h_id - - if(!host.lastKnownIP) - host.lastKnownIP = s2h_ip - - controlling = 1 - - host.verbs += /mob/living/carbon/proc/release_control - host.verbs += /mob/living/carbon/proc/punish_host - if(antag) - host.verbs += /mob/living/carbon/proc/spawn_larvae - - return - -/mob/living/carbon/human/proc/jumpstart() - set category = "Abilities" - set name = "Revive Host" - set desc = "Send a jolt of electricity through your host, reviving them." - - if(stat != 2) - to_chat(usr, "Your host is already alive.") - return - - verbs -= /mob/living/carbon/human/proc/jumpstart - visible_message("With a hideous, rattling moan, [src] shudders back to life!") - - rejuvenate() - restore_blood() - fixblood() - update_canmove() \ No newline at end of file +/mob/living/simple_mob/animal/borer/verb/release_host() + set category = "Abilities" + set name = "Release Host" + set desc = "Slither out of your host." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(stat) + to_chat(src, "You cannot leave your host in your current state.") + + if(docile) + to_chat(src, span_blue("You are feeling far too docile to do that.")) + return + + if(!host || !src) return + + to_chat(src, "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.") + + if(!host.stat) + to_chat(host, "An odd, uncomfortable pressure begins to build inside your skull, behind your ear...") + + spawn(100) + + if(!host || !src) return + + if(src.stat) + to_chat(src, "You cannot release your host in your current state.") + return + + to_chat(src, "You wiggle out of [host]'s ear and plop to the ground.") + if(host.mind) + if(!host.stat) + to_chat(host, "Something slimy wiggles out of your ear and plops to the ground!") + to_chat(host, "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again.") + + detatch() + leave_host() + +/mob/living/simple_mob/animal/borer/verb/infest() + set category = "Abilities" + set name = "Infest" + set desc = "Infest a suitable humanoid host." + + if(host) + to_chat(src, "You are already within a host.") + return + + if(stat) + to_chat(src, "You cannot infest a target in your current state.") + return + + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + if(!choices.len) + to_chat(src, "There are no viable hosts within range...") + return + + var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to infest?", "Target Choice", choices) + + if(!M || !src) return + + if(!(src.Adjacent(M))) return + + if(M.has_brain_worms()) + to_chat(src, "You cannot infest someone who is already infested!") + return + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + + var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD] + if(!E || E.is_stump()) + to_chat(src, "\The [H] does not have a head!") + + if(!H.should_have_organ("brain")) + to_chat(src, "\The [H] does not seem to have an ear canal to breach.") + return + + if(H.check_head_coverage()) + to_chat(src, "You cannot get through that host's protective gear.") + return + + to_chat(M, "Something slimy begins probing at the opening of your ear canal...") + to_chat(src, "You slither up [M] and begin probing at their ear canal...") + + if(!do_after(src,30)) + to_chat(src, "As [M] moves away, you are dislodged and fall to the ground.") + return + + if(!M || !src) return + + if(src.stat) + to_chat(src, "You cannot infest a target in your current state.") + return + + if(M in view(1, src)) + to_chat(src, "You wiggle into [M]'s ear.") + if(!M.stat) + to_chat(M, "Something disgusting and slimy wiggles into your ear!") + + src.host = M + src.forceMove(M) + + //Update their traitor status. + if(host.mind) + borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome) + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/I = H.internal_organs_by_name["brain"] + if(!I) // No brain organ, so the borer moves in and replaces it permanently. + replace_brain() + else + // If they're in normally, implant removal can get them out. + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + head.implants += src + + return + else + to_chat(src, "They are no longer in range!") + return + +/* +/mob/living/simple_mob/animal/borer/verb/devour_brain() + set category = "Abilities" + set name = "Devour Brain" + set desc = "Take permanent control of a dead host." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(host.stat != 2) + to_chat(src, "Your host is still alive.") + return + + if(stat) + to_chat(src, "You cannot do that in your current state.") + + if(docile) + to_chat(src, "You are feeling far too docile to do that.") + return + + + to_chat(src, "It only takes a few moments to render the dead host brain down into a nutrient-rich slurry...") + replace_brain() +*/ + +// BRAIN WORM ZOMBIES AAAAH. +/mob/living/simple_mob/animal/borer/proc/replace_brain() + + var/mob/living/carbon/human/H = host + + if(!istype(host)) + to_chat(src, "This host does not have a suitable brain.") + return + + to_chat(src, "You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H].") + + H.add_language("Cortical Link") + + if(host.stat == 2) + H.verbs |= /mob/living/carbon/human/proc/jumpstart + + H.verbs |= /mob/living/carbon/human/proc/psychic_whisper + H.verbs |= /mob/living/carbon/human/proc/tackle + if(antag) + H.verbs |= /mob/living/carbon/proc/spawn_larvae + + if(H.client) + H.ghostize(0) + + if(src.mind) + src.mind.special_role = "Borer Husk" + src.mind.transfer_to(host) + + H.ChangeToHusk() + + var/obj/item/organ/internal/borer/B = new(H) + H.internal_organs_by_name["brain"] = B + H.internal_organs |= B + + var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) + affecting.implants -= src + + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + + if(!H.computer_id) + H.computer_id = s2h_id + + if(!H.lastKnownIP) + H.lastKnownIP = s2h_ip + +/mob/living/simple_mob/animal/borer/verb/secrete_chemicals() + set category = "Abilities" + set name = "Secrete Chemicals" + set desc = "Push some chemicals into your host's bloodstream." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(stat) + to_chat(src, "You cannot secrete chemicals in your current state.") + + if(docile) + to_chat(src, span_blue("You are feeling far too docile to do that.")) + return + + if(chemicals < 50) + to_chat(src, "You don't have enough chemicals!") + + var/chem = tgui_input_list(usr, "Select a chemical to secrete.", "Chemicals", list("alkysine","bicaridine","hyperzine","tramadol")) + + if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check. + return + + to_chat(src, span_red("You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream.")) + host.reagents.add_reagent(chem, 10) + chemicals -= 50 + +/mob/living/simple_mob/animal/borer/verb/dominate_victim() + set category = "Abilities" + set name = "Paralyze Victim" + set desc = "Freeze the limbs of a potential host with supernatural fear." + + if(world.time - used_dominate < 150) + to_chat(src, "You cannot use that ability again so soon.") + return + + if(host) + to_chat(src, "You cannot do that from within a host body.") + return + + if(src.stat) + to_chat(src, "You cannot do that in your current state.") + return + + var/list/choices = list() + for(var/mob/living/carbon/C in view(3,src)) + if(C.stat != 2) + choices += C + + if(world.time - used_dominate < 150) + to_chat(src, "You cannot use that ability again so soon.") + return + + var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to dominate?", "Target Choice", choices) + + if(!M || !src) return + + if(M.has_brain_worms()) + to_chat(src, "You cannot infest someone who is already infested!") + return + + to_chat(src, span_red("You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.")) + to_chat(M, span_red("You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.")) + M.Weaken(10) + + used_dominate = world.time + +/mob/living/simple_mob/animal/borer/verb/bond_brain() + set category = "Abilities" + set name = "Assume Control" + set desc = "Fully connect to the brain of your host." + + if(!host) + to_chat(src, "You are not inside a host body.") + return + + if(src.stat) + to_chat(src, "You cannot do that in your current state.") + return + + if(docile) + to_chat(src, span_blue("You are feeling far too docile to do that.")) + return + + to_chat(src, "You begin delicately adjusting your connection to the host brain...") + + spawn(100+(host.brainloss*5)) + + if(!host || !src || controlling) + return + else + + to_chat(src, span_red("You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.")) + to_chat(host, span_red("You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.")) + host.add_language("Cortical Link") + + // host -> brain + var/h2b_id = host.computer_id + var/h2b_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + qdel(host_brain) + host_brain = new(src) + + host_brain.ckey = host.ckey + + host_brain.name = host.name + + if(!host_brain.computer_id) + host_brain.computer_id = h2b_id + + if(!host_brain.lastKnownIP) + host_brain.lastKnownIP = h2b_ip + + // self -> host + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + + host.ckey = src.ckey + + if(!host.computer_id) + host.computer_id = s2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = s2h_ip + + controlling = 1 + + host.verbs += /mob/living/carbon/proc/release_control + host.verbs += /mob/living/carbon/proc/punish_host + if(antag) + host.verbs += /mob/living/carbon/proc/spawn_larvae + + return + +/mob/living/carbon/human/proc/jumpstart() + set category = "Abilities" + set name = "Revive Host" + set desc = "Send a jolt of electricity through your host, reviving them." + + if(stat != 2) + to_chat(usr, "Your host is already alive.") + return + + verbs -= /mob/living/carbon/human/proc/jumpstart + visible_message("With a hideous, rattling moan, [src] shudders back to life!") + + rejuvenate() + restore_blood() + fixblood() + update_canmove() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm index 7a22edf5330..4a97ab3a319 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm @@ -52,12 +52,12 @@ GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O if(G.seed && G.seed.kitchen_tag == "wheat") if(!stat && eggsleft < 8) - user.visible_message("[user] feeds [O] to [name]! It clucks happily.","You feed [O] to [name]! It clucks happily.") + user.visible_message(span_blue("[user] feeds [O] to [name]! It clucks happily."),span_blue("You feed [O] to [name]! It clucks happily.")) user.drop_item() qdel(O) eggsleft += rand(1, 4) else - to_chat(user, "[name] doesn't seem hungry!") + to_chat(user, span_blue("[name] doesn't seem hungry!")) else to_chat(user, "[name] doesn't seem interested in that.") else diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm index 0ff6ee6496b..4fdd32500e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm @@ -35,9 +35,9 @@ user.visible_message("[user] milks [src] using \the [O].") var/transfered = udder.trans_id_to(G, "milk", rand(5,10)) if(G.reagents.total_volume >= G.volume) - to_chat(user, "The [O] is full.") + to_chat(user, span_red("The [O] is full.")) if(!transfered) - to_chat(user, "The udder is dry. Wait a bit longer...") + to_chat(user, span_red("The udder is dry. Wait a bit longer...")) else ..() @@ -69,4 +69,4 @@ emote_see = list("shakes its head") /decl/mob_organ_names/cow - hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "udder") \ No newline at end of file + hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "udder") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm index 477dcb5aa74..c172d0b6549 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm @@ -66,9 +66,9 @@ user.visible_message("[user] milks [src] using \the [O].") var/transfered = udder.trans_id_to(G, "milk", rand(5,10)) if(G.reagents.total_volume >= G.volume) - to_chat(user, "The [O] is full.") + to_chat(user, span_red("The [O] is full.")) if(!transfered) - to_chat(user, "The udder is dry. Wait a bit longer...") + to_chat(user, span_red("The udder is dry. Wait a bit longer...")) else ..() @@ -78,4 +78,4 @@ emote_see = list("shakes its head", "stamps a foot", "glares around") // say_got_target doesn't seem to handle emotes, but keeping this here in case someone wants to make it work -// say_got_target = list("[src] gets an evil-looking gleam in their eye.") \ No newline at end of file +// say_got_target = list("[src] gets an evil-looking gleam in their eye.") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/broodmother.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/broodmother.dm index 345e315a166..cc5b5d179c5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/broodmother.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/broodmother.dm @@ -185,4 +185,4 @@ qdel(src) else - to_chat(user, "You need more space to release the egg!") \ No newline at end of file + to_chat(user, "You need more space to release the egg!") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm index 7ff84ff697b..8fba024af57 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm @@ -1,56 +1,56 @@ -//Look Sir, free crabs! -/mob/living/simple_mob/animal/passive/crab - name = "crab" - desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." - tt_desc = "E Cancer bellianus" - faction = "crabs" - - icon_state = "crab" - icon_living = "crab" - icon_dead = "crab_dead" - - mob_size = MOB_SMALL - - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - friendly = "pinches" - - organ_names = /decl/mob_organ_names/crab - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/crabmeat - meat_amount = 3 - - say_list_type = /datum/say_list/crab - -//COFFEE! SQUEEEEEEEEE! -/mob/living/simple_mob/animal/passive/crab/Coffee - name = "Coffee" - real_name = "Coffee" - desc = "It's Coffee, the other pet!" - -// Sif! - -/datum/category_item/catalogue/fauna/sif_crab - name = "Sivian Fauna - Shelf Crab" - desc = "Classification: S Ocypode glacian\ -

                    \ - A small crustacean sometimes considered a pest to Sivian fisheries, \ - as the creatures often tend to ignore non-native fish species when feeding. This \ - results in an unfortunate advantage for invasive species. \ -
                    \ - Otherwise, these animals are enjoyed as a reliable source of high-grade meat." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/crab/sif - icon = 'icons/mob/fish.dmi' - tt_desc = "S Ocypode glacian" - - catalogue_data = list(/datum/category_item/catalogue/fauna/sif_crab) - -/mob/living/simple_mob/animal/passive/crab/sif/Initialize() - . = ..() - adjust_scale(rand(5,12) / 10) - -/decl/mob_organ_names/crab +//Look Sir, free crabs! +/mob/living/simple_mob/animal/passive/crab + name = "crab" + desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." + tt_desc = "E Cancer bellianus" + faction = "crabs" + + icon_state = "crab" + icon_living = "crab" + icon_dead = "crab_dead" + + mob_size = MOB_SMALL + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + friendly = "pinches" + + organ_names = /decl/mob_organ_names/crab + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/crabmeat + meat_amount = 3 + + say_list_type = /datum/say_list/crab + +//COFFEE! SQUEEEEEEEEE! +/mob/living/simple_mob/animal/passive/crab/Coffee + name = "Coffee" + real_name = "Coffee" + desc = "It's Coffee, the other pet!" + +// Sif! + +/datum/category_item/catalogue/fauna/sif_crab + name = "Sivian Fauna - Shelf Crab" + desc = "Classification: S Ocypode glacian\ +

                    \ + A small crustacean sometimes considered a pest to Sivian fisheries, \ + as the creatures often tend to ignore non-native fish species when feeding. This \ + results in an unfortunate advantage for invasive species. \ +
                    \ + Otherwise, these animals are enjoyed as a reliable source of high-grade meat." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/crab/sif + icon = 'icons/mob/fish.dmi' + tt_desc = "S Ocypode glacian" + + catalogue_data = list(/datum/category_item/catalogue/fauna/sif_crab) + +/mob/living/simple_mob/animal/passive/crab/sif/Initialize() + . = ..() + adjust_scale(rand(5,12) / 10) + +/decl/mob_organ_names/crab hit_zones = list("cephalothorax", "abdomen", "left walking legs", "right walking legs", "left swimming legs", "right swimming legs", "left pincer", "right pincer") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm index 4dd631d5d72..5b8a5f2177d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm @@ -1,328 +1,328 @@ -// Different types of fish! They are all subtypes of this tho -/datum/category_item/catalogue/fauna/invasive_fish - name = "Invasive Fauna - Fish" - desc = "This fish is considered an invasive species according \ - to Sivian wildlife regulations. Removal or relocation is advised." - value = CATALOGUER_REWARD_TRIVIAL - -/mob/living/simple_mob/animal/passive/fish - name = "fish" - desc = "Its a fishy. No touchy fishy." - icon = 'icons/mob/fish.dmi' - item_state = "fish" - - //catalogue_data = list(/datum/category_item/catalogue/fauna/invasive_fish) //TODO: write non-sif lore - - mob_size = MOB_SMALL - // So fish are actually underwater. - plane = TURF_PLANE - layer = UNDERWATER_LAYER - - organ_names = /decl/mob_organ_names/fish - - holder_type = /obj/item/weapon/holder/fish - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish - meat_amount = 3 - - // By default they can be in any water turf. Subtypes might restrict to deep/shallow etc - var/global/list/suitable_turf_types = list( - /turf/simulated/floor/beach/water, - /turf/simulated/floor/beach/coastline, - /turf/simulated/floor/holofloor/beach/water, - /turf/simulated/floor/holofloor/beach/coastline, - /turf/simulated/floor/water - ) - - var/randomize_location = TRUE - -/mob/living/simple_mob/animal/passive/fish/Initialize() - . = ..() - - if(!default_pixel_x && randomize_location) - default_pixel_x = rand(-12, 12) - - if(!default_pixel_y && randomize_location) - default_pixel_y = rand(-6, 10) - -// Makes the AI unable to willingly go on land. -/mob/living/simple_mob/animal/passive/fish/IMove(turf/newloc, safety = TRUE) - if(is_type_in_list(newloc, suitable_turf_types)) - return ..() // Procede as normal. - return MOVEMENT_FAILED // Don't leave the water! - -// Take damage if we are not in water -/mob/living/simple_mob/animal/passive/fish/handle_breathing() - if(istype(loc, /obj/item/glass_jar/fish)) - var/obj/item/glass_jar/fish/F = loc - if(F.filled) - return - - var/turf/T = get_turf(src) - if(T && !is_type_in_list(T, suitable_turf_types)) - if(prob(50)) - say(pick("Blub", "Glub", "Burble")) - adjustBruteLoss(unsuitable_atoms_damage) - -// Subtypes. -/mob/living/simple_mob/animal/passive/fish/bass - name = "bass" - tt_desc = "E Micropterus notius" - icon_state = "bass-swim" - icon_living = "bass-swim" - icon_dead = "bass-dead" - -/mob/living/simple_mob/animal/passive/fish/trout - name = "trout" - tt_desc = "E Salmo trutta" - icon_state = "trout-swim" - icon_living = "trout-swim" - icon_dead = "trout-dead" - -/mob/living/simple_mob/animal/passive/fish/salmon - name = "salmon" - tt_desc = "E Oncorhynchus nerka" - icon_state = "salmon-swim" - icon_living = "salmon-swim" - icon_dead = "salmon-dead" - -/mob/living/simple_mob/animal/passive/fish/perch - name = "perch" - tt_desc = "E Perca flavescens" - icon_state = "perch-swim" - icon_living = "perch-swim" - icon_dead = "perch-dead" - -/mob/living/simple_mob/animal/passive/fish/pike - name = "pike" - tt_desc = "E Esox aquitanicus" - icon_state = "pike-swim" - icon_living = "pike-swim" - icon_dead = "pike-dead" - -/mob/living/simple_mob/animal/passive/fish/koi - name = "koi" - tt_desc = "E Cyprinus rubrofuscus" - icon_state = "koi-swim" - icon_living = "koi-swim" - icon_dead = "koi-dead" - -/datum/category_item/catalogue/fauna/javelin - name = "Sivian Fauna - Javelin Shark" - desc = "Classification: S Cetusan minimalix\ -

                    \ - A small breed of fatty shark native to the waters near the Ullran Expanse.\ - The creatures are not known to attack humans or larger animals, possibly \ - due to their size. It is speculated that they are actually scavengers, \ - as they are most commonly found near the gulf floor. \ -
                    \ - The Javelin's reproductive cycle only recurs between three and four \ - Sivian years. \ -
                    \ - These creatures are considered a protected species, and thus require an \ - up-to-date license to be hunted." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/fish/javelin - name = "javelin" - tt_desc = "S Cetusan minimalix" - icon_state = "javelin-swim" - icon_living = "javelin-swim" - icon_dead = "javelin-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/javelin) - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - -/datum/category_item/catalogue/fauna/icebass - name = "Sivian Fauna - Glitter Bass" - desc = "Classification: X Micropterus notius crotux\ -

                    \ - Initially a genetically engineered hybrid of the common Earth bass and \ - the Sivian Rock-Fish. These were designed to deal with the invasive \ - fish species, however to their creators' dismay, they instead \ - began to form their own passive niche. \ -
                    \ - Due to the brilliant reflective scales earning them their name, the \ - animals pose a specific issue for Sivian animals relying on \ - bioluminesence to aid in their hunt. \ -
                    \ - Despite their beauty, they are considered an invasive species." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/fish/icebass - name = "glitter bass" - tt_desc = "X Micropterus notius crotux" - icon_state = "sifbass-swim" - icon_living = "sifbass-swim" - icon_dead = "sifbass-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/icebass) - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - - var/max_red = 150 - var/min_red = 50 - - var/max_blue = 255 - var/min_blue = 50 - - var/max_green = 150 - var/min_green = 50 - - var/dorsal_color = "#FFFFFF" - var/belly_color = "#FFFFFF" - - var/image/dorsal_image - var/image/belly_image - -/mob/living/simple_mob/animal/passive/fish/icebass/Initialize() - . = ..() - dorsal_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) - belly_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) - update_icon() - -/mob/living/simple_mob/animal/passive/fish/icebass/update_icon() - cut_overlays() - ..() - - if(!dorsal_image) - dorsal_image = image(icon, "[icon_state]_mask-body") - if(!belly_image) - belly_image = image(icon, "[icon_state]_mask-belly") - - dorsal_image.icon_state = "[icon_state]_mask-body" - belly_image.icon_state = "[icon_state]_mask-belly" - - dorsal_image.color = dorsal_color - belly_image.color = belly_color - - add_overlay(dorsal_image) - add_overlay(belly_image) - -/datum/category_item/catalogue/fauna/rockfish - name = "Sivian Fauna - Rock Puffer" - desc = "Classification: S Tetraodontidae scopulix\ -

                    \ - A species strangely resembling the puffer-fish of Earth. These \ - creatures do not use toxic spines to protect themselves, instead \ - utilizing an incredibly durable exoskeleton that is molded by the \ - expansion of its ventral fluid bladders. \ -
                    \ - Rock Puffers or 'Rock-fish' are often host to smaller creatures which \ - maneuver their way into the gap between the fish's body and shell. \ -
                    \ - The species is also capable of pulling its vibrantly colored head into \ - the safer confines of its shell, the action being utilized in their \ - attempts to find a mate." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/passive/fish/rockfish - name = "rock-fish" - tt_desc = "S Tetraodontidae scopulix" - icon_state = "rockfish-swim" - icon_living = "rockfish-swim" - icon_dead = "rockfish-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/rockfish) - - armor = list( - "melee" = 90, - "bullet" = 50, - "laser" = -15, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100) - - var/max_red = 255 - var/min_red = 50 - - var/max_blue = 255 - var/min_blue = 50 - - var/max_green = 255 - var/min_green = 50 - - var/head_color = "#FFFFFF" - - var/image/head_image - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - -/mob/living/simple_mob/animal/passive/fish/rockfish/Initialize() - . = ..() - head_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) - update_icon() - -/mob/living/simple_mob/animal/passive/fish/rockfish/update_icon() - cut_overlays() - ..() - if(!head_image) - head_image = image(icon, "[icon_state]_mask") - - head_image.icon_state = "[icon_state]_mask" - - head_image.color = head_color - - add_overlay(head_image) - -/datum/category_item/catalogue/fauna/solarfish - name = "Sivian Fauna - Solar Fin" - desc = "Classification: S Exocoetidae solarin\ -

                    \ - An incredibly rare species of Sivian fish.\ - The solar-fin missile fish is a specialized omnivore capable of \ - catching insects or small birds venturing too close to the water's \ - surface. \ -
                    \ - The glimmering fins of the solar-fin are actually biofluorescent, \ - 'charged' by the creature basking at the surface of the water, most \ - commonly by the edge of an ice-shelf, as a rapid means of cover. \ -
                    \ - These creatures are considered a protected species, and thus require an \ - up-to-date license to be hunted." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/animal/passive/fish/solarfish - name = "sun-fin" - tt_desc = "S Exocoetidae solarin" - icon_state = "solarfin-swim" - icon_living = "solarfin-swim" - icon_dead = "solarfin-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/solarfish) - - has_eye_glow = TRUE - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif - -/datum/category_item/catalogue/fauna/murkin - name = "Sivian Fauna - Murkfish" - desc = "Classification: S Perca lutux\ -

                    \ - A small, Sivian fish most known for its bland-ness.\ -
                    \ - The species is incredibly close in appearance to the Earth \ - perch, aside from its incredibly tall dorsal fin. The animals use \ - the fin to assess the wind direction while near the surface. \ -
                    \ - The murkfish earns its name from the fact its dense meat tastes like mud \ - thanks to a specially formed protein, most likely an adaptation to \ - protect the species from predation." - value = CATALOGUER_REWARD_TRIVIAL - -/mob/living/simple_mob/animal/passive/fish/murkin - name = "murkin" - tt_desc = "S Perca lutux" - - icon_state = "murkin-swim" - icon_living = "murkin-swim" - icon_dead = "murkin-dead" - - catalogue_data = list(/datum/category_item/catalogue/fauna/murkin) - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish - -/decl/mob_organ_names/fish +// Different types of fish! They are all subtypes of this tho +/datum/category_item/catalogue/fauna/invasive_fish + name = "Invasive Fauna - Fish" + desc = "This fish is considered an invasive species according \ + to Sivian wildlife regulations. Removal or relocation is advised." + value = CATALOGUER_REWARD_TRIVIAL + +/mob/living/simple_mob/animal/passive/fish + name = "fish" + desc = "Its a fishy. No touchy fishy." + icon = 'icons/mob/fish.dmi' + item_state = "fish" + + //catalogue_data = list(/datum/category_item/catalogue/fauna/invasive_fish) //TODO: write non-sif lore + + mob_size = MOB_SMALL + // So fish are actually underwater. + plane = TURF_PLANE + layer = UNDERWATER_LAYER + + organ_names = /decl/mob_organ_names/fish + + holder_type = /obj/item/weapon/holder/fish + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish + meat_amount = 3 + + // By default they can be in any water turf. Subtypes might restrict to deep/shallow etc + var/global/list/suitable_turf_types = list( + /turf/simulated/floor/beach/water, + /turf/simulated/floor/beach/coastline, + /turf/simulated/floor/holofloor/beach/water, + /turf/simulated/floor/holofloor/beach/coastline, + /turf/simulated/floor/water + ) + + var/randomize_location = TRUE + +/mob/living/simple_mob/animal/passive/fish/Initialize() + . = ..() + + if(!default_pixel_x && randomize_location) + default_pixel_x = rand(-12, 12) + + if(!default_pixel_y && randomize_location) + default_pixel_y = rand(-6, 10) + +// Makes the AI unable to willingly go on land. +/mob/living/simple_mob/animal/passive/fish/IMove(turf/newloc, safety = TRUE) + if(is_type_in_list(newloc, suitable_turf_types)) + return ..() // Procede as normal. + return MOVEMENT_FAILED // Don't leave the water! + +// Take damage if we are not in water +/mob/living/simple_mob/animal/passive/fish/handle_breathing() + if(istype(loc, /obj/item/glass_jar/fish)) + var/obj/item/glass_jar/fish/F = loc + if(F.filled) + return + + var/turf/T = get_turf(src) + if(T && !is_type_in_list(T, suitable_turf_types)) + if(prob(50)) + say(pick("Blub", "Glub", "Burble")) + adjustBruteLoss(unsuitable_atoms_damage) + +// Subtypes. +/mob/living/simple_mob/animal/passive/fish/bass + name = "bass" + tt_desc = "E Micropterus notius" + icon_state = "bass-swim" + icon_living = "bass-swim" + icon_dead = "bass-dead" + +/mob/living/simple_mob/animal/passive/fish/trout + name = "trout" + tt_desc = "E Salmo trutta" + icon_state = "trout-swim" + icon_living = "trout-swim" + icon_dead = "trout-dead" + +/mob/living/simple_mob/animal/passive/fish/salmon + name = "salmon" + tt_desc = "E Oncorhynchus nerka" + icon_state = "salmon-swim" + icon_living = "salmon-swim" + icon_dead = "salmon-dead" + +/mob/living/simple_mob/animal/passive/fish/perch + name = "perch" + tt_desc = "E Perca flavescens" + icon_state = "perch-swim" + icon_living = "perch-swim" + icon_dead = "perch-dead" + +/mob/living/simple_mob/animal/passive/fish/pike + name = "pike" + tt_desc = "E Esox aquitanicus" + icon_state = "pike-swim" + icon_living = "pike-swim" + icon_dead = "pike-dead" + +/mob/living/simple_mob/animal/passive/fish/koi + name = "koi" + tt_desc = "E Cyprinus rubrofuscus" + icon_state = "koi-swim" + icon_living = "koi-swim" + icon_dead = "koi-dead" + +/datum/category_item/catalogue/fauna/javelin + name = "Sivian Fauna - Javelin Shark" + desc = "Classification: S Cetusan minimalix\ +

                    \ + A small breed of fatty shark native to the waters near the Ullran Expanse.\ + The creatures are not known to attack humans or larger animals, possibly \ + due to their size. It is speculated that they are actually scavengers, \ + as they are most commonly found near the gulf floor. \ +
                    \ + The Javelin's reproductive cycle only recurs between three and four \ + Sivian years. \ +
                    \ + These creatures are considered a protected species, and thus require an \ + up-to-date license to be hunted." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/javelin + name = "javelin" + tt_desc = "S Cetusan minimalix" + icon_state = "javelin-swim" + icon_living = "javelin-swim" + icon_dead = "javelin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/javelin) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + +/datum/category_item/catalogue/fauna/icebass + name = "Sivian Fauna - Glitter Bass" + desc = "Classification: X Micropterus notius crotux\ +

                    \ + Initially a genetically engineered hybrid of the common Earth bass and \ + the Sivian Rock-Fish. These were designed to deal with the invasive \ + fish species, however to their creators' dismay, they instead \ + began to form their own passive niche. \ +
                    \ + Due to the brilliant reflective scales earning them their name, the \ + animals pose a specific issue for Sivian animals relying on \ + bioluminesence to aid in their hunt. \ +
                    \ + Despite their beauty, they are considered an invasive species." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/icebass + name = "glitter bass" + tt_desc = "X Micropterus notius crotux" + icon_state = "sifbass-swim" + icon_living = "sifbass-swim" + icon_dead = "sifbass-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/icebass) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + + var/max_red = 150 + var/min_red = 50 + + var/max_blue = 255 + var/min_blue = 50 + + var/max_green = 150 + var/min_green = 50 + + var/dorsal_color = "#FFFFFF" + var/belly_color = "#FFFFFF" + + var/image/dorsal_image + var/image/belly_image + +/mob/living/simple_mob/animal/passive/fish/icebass/Initialize() + . = ..() + dorsal_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + belly_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + update_icon() + +/mob/living/simple_mob/animal/passive/fish/icebass/update_icon() + cut_overlays() + ..() + + if(!dorsal_image) + dorsal_image = image(icon, "[icon_state]_mask-body") + if(!belly_image) + belly_image = image(icon, "[icon_state]_mask-belly") + + dorsal_image.icon_state = "[icon_state]_mask-body" + belly_image.icon_state = "[icon_state]_mask-belly" + + dorsal_image.color = dorsal_color + belly_image.color = belly_color + + add_overlay(dorsal_image) + add_overlay(belly_image) + +/datum/category_item/catalogue/fauna/rockfish + name = "Sivian Fauna - Rock Puffer" + desc = "Classification: S Tetraodontidae scopulix\ +

                    \ + A species strangely resembling the puffer-fish of Earth. These \ + creatures do not use toxic spines to protect themselves, instead \ + utilizing an incredibly durable exoskeleton that is molded by the \ + expansion of its ventral fluid bladders. \ +
                    \ + Rock Puffers or 'Rock-fish' are often host to smaller creatures which \ + maneuver their way into the gap between the fish's body and shell. \ +
                    \ + The species is also capable of pulling its vibrantly colored head into \ + the safer confines of its shell, the action being utilized in their \ + attempts to find a mate." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/passive/fish/rockfish + name = "rock-fish" + tt_desc = "S Tetraodontidae scopulix" + icon_state = "rockfish-swim" + icon_living = "rockfish-swim" + icon_dead = "rockfish-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/rockfish) + + armor = list( + "melee" = 90, + "bullet" = 50, + "laser" = -15, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100) + + var/max_red = 255 + var/min_red = 50 + + var/max_blue = 255 + var/min_blue = 50 + + var/max_green = 255 + var/min_green = 50 + + var/head_color = "#FFFFFF" + + var/image/head_image + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + +/mob/living/simple_mob/animal/passive/fish/rockfish/Initialize() + . = ..() + head_color = rgb(rand(min_red,max_red), rand(min_green,max_green), rand(min_blue,max_blue)) + update_icon() + +/mob/living/simple_mob/animal/passive/fish/rockfish/update_icon() + cut_overlays() + ..() + if(!head_image) + head_image = image(icon, "[icon_state]_mask") + + head_image.icon_state = "[icon_state]_mask" + + head_image.color = head_color + + add_overlay(head_image) + +/datum/category_item/catalogue/fauna/solarfish + name = "Sivian Fauna - Solar Fin" + desc = "Classification: S Exocoetidae solarin\ +

                    \ + An incredibly rare species of Sivian fish.\ + The solar-fin missile fish is a specialized omnivore capable of \ + catching insects or small birds venturing too close to the water's \ + surface. \ +
                    \ + The glimmering fins of the solar-fin are actually biofluorescent, \ + 'charged' by the creature basking at the surface of the water, most \ + commonly by the edge of an ice-shelf, as a rapid means of cover. \ +
                    \ + These creatures are considered a protected species, and thus require an \ + up-to-date license to be hunted." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/animal/passive/fish/solarfish + name = "sun-fin" + tt_desc = "S Exocoetidae solarin" + icon_state = "solarfin-swim" + icon_living = "solarfin-swim" + icon_dead = "solarfin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/solarfish) + + has_eye_glow = TRUE + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sif + +/datum/category_item/catalogue/fauna/murkin + name = "Sivian Fauna - Murkfish" + desc = "Classification: S Perca lutux\ +

                    \ + A small, Sivian fish most known for its bland-ness.\ +
                    \ + The species is incredibly close in appearance to the Earth \ + perch, aside from its incredibly tall dorsal fin. The animals use \ + the fin to assess the wind direction while near the surface. \ +
                    \ + The murkfish earns its name from the fact its dense meat tastes like mud \ + thanks to a specially formed protein, most likely an adaptation to \ + protect the species from predation." + value = CATALOGUER_REWARD_TRIVIAL + +/mob/living/simple_mob/animal/passive/fish/murkin + name = "murkin" + tt_desc = "S Perca lutux" + + icon_state = "murkin-swim" + icon_living = "murkin-swim" + icon_dead = "murkin-dead" + + catalogue_data = list(/datum/category_item/catalogue/fauna/murkin) + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish + +/decl/mob_organ_names/fish hit_zones = list("head", "body", "dorsal fin", "left pectoral fin", "right pectoral fin", "tail fin") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm index 7f40761eb8e..bc9a0c91185 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/jerboa_vr.dm @@ -1,15 +1,15 @@ -/mob/living/simple_mob/animal/passive/mouse/jerboa - name = "jerboa" - real_name = "jerboa" - desc = "It's a jerboa, a particularly fast desert mouse" - tt_desc = "E Dipodidae musculus" - icon = 'icons/mob/animal_vr.dmi' - icon_state = "mouse_brown" - item_state = "mouse_brown" - icon_living = "mouse_brown" - icon_dead = "mouse_brown_dead" - kitchen_tag = "rodent" - - movement_cooldown = -2 - - body_color = "brown" +/mob/living/simple_mob/animal/passive/mouse/jerboa + name = "jerboa" + real_name = "jerboa" + desc = "It's a jerboa, a particularly fast desert mouse" + tt_desc = "E Dipodidae musculus" + icon = 'icons/mob/animal_vr.dmi' + icon_state = "mouse_brown" + item_state = "mouse_brown" + icon_living = "mouse_brown" + icon_dead = "mouse_brown_dead" + kitchen_tag = "rodent" + + movement_cooldown = -2 + + body_color = "brown" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm index 905d7f2f197..ecb6765c3fd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm @@ -1,64 +1,64 @@ -/mob/living/simple_mob/animal/passive/lizard - name = "lizard" - desc = "A cute, tiny lizard." - tt_desc = "E Anolis cuvieri" - - icon_state = "lizard_green" - icon_living = "lizard_green" - icon_dead = "lizard_green_dead" - - health = 5 - maxHealth = 5 - mob_size = MOB_MINISCULE - - response_help = "pets" - response_disarm = "shoos" - response_harm = "stomps on" - - attacktext = list("bitten") - melee_damage_lower = 1 - melee_damage_upper = 2 - - speak_emote = list("hisses") - - say_list_type = /datum/say_list/lizard - - meat_amount = 1 - - var/body_color // Green, red, orange, yellow or cyan. Keep blank for random (including rare redblue) - -/mob/living/simple_mob/animal/passive/lizard/Initialize() - .=..() - - if(!body_color) - if(rand(1,1000) == 1) - body_color = "redblue" - else - body_color = pick(list("green","red","orange","yellow","cyan")) - icon_state = "lizard_[body_color]" - item_state = "lizard_[body_color]" - icon_living = "lizard_[body_color]" - icon_dead = "lizard_[body_color]_dead" - if(body_color == "redblue") - desc = "A cute, tiny, red lizard with distinctive blueish markings on its tiny limbs. Seems rare!" - else - desc = "A cute, tiny, [body_color] lizard." - -/mob/living/simple_mob/animal/passive/lizard/large - desc = "A cute, big lizard." - maxHealth = 20 - health = 20 - - melee_damage_lower = 5 - melee_damage_upper = 15 - - attack_sharp = TRUE - -/mob/living/simple_mob/animal/passive/lizard/large/Initialize() - . = ..() - adjust_scale(rand(12, 20) / 10) -/mob/living/simple_mob/animal/passive/lizard/large/defensive - maxHealth = 30 - health = 30 - - ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative +/mob/living/simple_mob/animal/passive/lizard + name = "lizard" + desc = "A cute, tiny lizard." + tt_desc = "E Anolis cuvieri" + + icon_state = "lizard_green" + icon_living = "lizard_green" + icon_dead = "lizard_green_dead" + + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + + response_help = "pets" + response_disarm = "shoos" + response_harm = "stomps on" + + attacktext = list("bitten") + melee_damage_lower = 1 + melee_damage_upper = 2 + + speak_emote = list("hisses") + + say_list_type = /datum/say_list/lizard + + meat_amount = 1 + + var/body_color // Green, red, orange, yellow or cyan. Keep blank for random (including rare redblue) + +/mob/living/simple_mob/animal/passive/lizard/Initialize() + .=..() + + if(!body_color) + if(rand(1,1000) == 1) + body_color = "redblue" + else + body_color = pick(list("green","red","orange","yellow","cyan")) + icon_state = "lizard_[body_color]" + item_state = "lizard_[body_color]" + icon_living = "lizard_[body_color]" + icon_dead = "lizard_[body_color]_dead" + if(body_color == "redblue") + desc = "A cute, tiny, red lizard with distinctive blueish markings on its tiny limbs. Seems rare!" + else + desc = "A cute, tiny, [body_color] lizard." + +/mob/living/simple_mob/animal/passive/lizard/large + desc = "A cute, big lizard." + maxHealth = 20 + health = 20 + + melee_damage_lower = 5 + melee_damage_upper = 15 + + attack_sharp = TRUE + +/mob/living/simple_mob/animal/passive/lizard/large/Initialize() + . = ..() + adjust_scale(rand(12, 20) / 10) +/mob/living/simple_mob/animal/passive/lizard/large/defensive + maxHealth = 30 + health = 30 + + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm index 3dabbb6430a..4bfba0a4439 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm @@ -1,39 +1,39 @@ -/mob/living/simple_mob/animal/passive/yithian - name = "yithian" - desc = "A friendly creature vaguely resembling an oversized snail without a shell." - tt_desc = "J Escargot escargot" // a product of Jade, which is a planet that totally exists - - icon_state = "yithian" - icon_living = "yithian" - icon_dead = "yithian_dead" - icon = 'icons/jungle.dmi' - - organ_names = /decl/mob_organ_names/yithian - - // Same stats as lizards. - health = 5 - maxHealth = 5 - mob_size = MOB_MINISCULE - -/mob/living/simple_mob/animal/passive/tindalos - name = "tindalos" - desc = "It looks like a large, flightless grasshopper." - tt_desc = "J Locusta bruchus" - - icon_state = "tindalos" - icon_living = "tindalos" - icon_dead = "tindalos_dead" - icon = 'icons/jungle.dmi' - - organ_names = /decl/mob_organ_names/tindalos - - // Same stats as lizards. - health = 5 - maxHealth = 5 - mob_size = MOB_MINISCULE - -/decl/mob_organ_names/yithian - hit_zones = list("head", "abdomen", "left foreleg", "right foreleg", "left hind leg", "right hind leg") - -/decl/mob_organ_names/tindalos +/mob/living/simple_mob/animal/passive/yithian + name = "yithian" + desc = "A friendly creature vaguely resembling an oversized snail without a shell." + tt_desc = "J Escargot escargot" // a product of Jade, which is a planet that totally exists + + icon_state = "yithian" + icon_living = "yithian" + icon_dead = "yithian_dead" + icon = 'icons/jungle.dmi' + + organ_names = /decl/mob_organ_names/yithian + + // Same stats as lizards. + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + +/mob/living/simple_mob/animal/passive/tindalos + name = "tindalos" + desc = "It looks like a large, flightless grasshopper." + tt_desc = "J Locusta bruchus" + + icon_state = "tindalos" + icon_living = "tindalos" + icon_dead = "tindalos_dead" + icon = 'icons/jungle.dmi' + + organ_names = /decl/mob_organ_names/tindalos + + // Same stats as lizards. + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + +/decl/mob_organ_names/yithian + hit_zones = list("head", "abdomen", "left foreleg", "right foreleg", "left hind leg", "right hind leg") + +/decl/mob_organ_names/tindalos hit_zones = list("head", "thorax", "abdomen", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "left middle leg", "right middle leg") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index dcb5349ad2e..dd6b6db3cf5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -79,7 +79,7 @@ if( ishuman(AM) ) if(!stat) var/mob/M = AM - M.visible_message("\icon[src][bicon(src)] Squeek!") + M.visible_message(span_blue("\icon[src][bicon(src)] Squeek!")) playsound(src, 'sound/effects/mouse_squeak.ogg', 35, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm index c488d1ed28f..6450259f9c1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm @@ -1,5 +1,5 @@ -// Passive mobs can't attack things, and will run away instead. -// They can also be displaced by all mobs. -/mob/living/simple_mob/animal/passive - ai_holder_type = /datum/ai_holder/simple_mob/passive +// Passive mobs can't attack things, and will run away instead. +// They can also be displaced by all mobs. +/mob/living/simple_mob/animal/passive + ai_holder_type = /datum/ai_holder/simple_mob/passive mob_bump_flag = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm index dd312988eca..5f0f0107634 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm @@ -1,35 +1,35 @@ -/mob/living/simple_mob/animal/passive/penguin - name = "penguin" - desc = "An ungainly, waddling, cute, and VERY well-dressed bird." - tt_desc = "Aptenodytes forsteri" - icon_state = "penguin" - icon_living = "penguin" - icon_dead = "penguin_dead" - - maxHealth = 20 - health = 20 - minbodytemp = 175 // Same as Sif mobs. - - response_help = "pets" - response_disarm = "pushes aside" - response_harm = "hits" - - organ_names = /decl/mob_organ_names/penguin - - meat_amount = 3 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - - harm_intent_damage = 5 - melee_damage_lower = 10 - melee_damage_upper = 15 - attacktext = list("pecked") - - has_langs = list(LANGUAGE_ANIMAL) - -/mob/living/simple_mob/animal/passive/penguin/tux - name = "Tux" - desc = "A penguin that has been known to associate with gnus." - speak_emote = list("interjects") - -/decl/mob_organ_names/penguin +/mob/living/simple_mob/animal/passive/penguin + name = "penguin" + desc = "An ungainly, waddling, cute, and VERY well-dressed bird." + tt_desc = "Aptenodytes forsteri" + icon_state = "penguin" + icon_living = "penguin" + icon_dead = "penguin_dead" + + maxHealth = 20 + health = 20 + minbodytemp = 175 // Same as Sif mobs. + + response_help = "pets" + response_disarm = "pushes aside" + response_harm = "hits" + + organ_names = /decl/mob_organ_names/penguin + + meat_amount = 3 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 15 + attacktext = list("pecked") + + has_langs = list(LANGUAGE_ANIMAL) + +/mob/living/simple_mob/animal/passive/penguin/tux + name = "Tux" + desc = "A penguin that has been known to associate with gnus." + speak_emote = list("interjects") + +/decl/mob_organ_names/penguin hit_zones = list("chest", "left leg", "right leg", "left flipper", "right flipper", "head") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm index 4eff5abe4e9..ba41d4388e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm @@ -1,93 +1,93 @@ -// Base bird type. - -/mob/living/simple_mob/animal/passive/bird - name = "bird" - desc = "A domesticated bird. Tweet tweet!" - player_msg = "You are able to fly." - - icon = 'icons/mob/birds.dmi' - icon_state = "parrot" - item_state = null - icon_rest = "parrot-held" - icon_dead = "parrot-dead" - - pass_flags = PASSTABLE - - health = 30 - maxHealth = 30 - melee_damage_lower = 3 - melee_damage_upper = 3 - - movement_cooldown = -1 - hovering = TRUE // Birds can fly. - softfall = TRUE - parachuting = TRUE - - meat_amount = 1 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken - - attacktext = list("clawed", "pecked") - speak_emote = list("chirps", "caws") - has_langs = list(LANGUAGE_ANIMAL) - response_help = "pets" - response_disarm = "gently moves aside" - response_harm = "swats" - organ_names = /decl/mob_organ_names/bird - - say_list_type = /datum/say_list/bird - holder_type = /obj/item/weapon/holder/bird - -/datum/say_list/bird - speak = list("Chirp!","Caw!","Screech!","Squawk!") - emote_hear = list("chirps","caws") - emote_see = list("shakes their head", "ruffles their feathers") - -// Subtypes for birbs. -/mob/living/simple_mob/animal/passive/bird/black_bird - name = "common blackbird" - desc = "A species of bird, both the males and females are known to be territorial on their breeding grounds." - icon_state = "commonblackbird" - icon_dead = "commonblackbird-dead" - tt_desc = "E Turdus merula" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/azure_tit - name = "azure tit" - desc = "A species of bird, colored blue and white." - icon_state = "azuretit" - icon_dead = "azuretit-dead" - tt_desc = "E Cyanistes cyanus" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/european_robin - name = "european robin" - desc = "A species of bird, they have been studied for their sense of magnetoreception." - icon_state = "europeanrobin" - icon_dead = "europeanrobin-dead" - tt_desc = "E Erithacus rubecula" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/goldcrest - name = "goldcrest" - desc = "A species of bird, they were once called 'king of the birds' in ancient human folklore, for their golden crest. \ - Today, their scientific name still elude towards this, with regulus, meaning petty king." - icon_state = "goldcrest" - icon_dead = "goldcrest-dead" - tt_desc = "E Regulus regulus" - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/mob/living/simple_mob/animal/passive/bird/ringneck_dove - name = "ringneck dove" - desc = "A species of bird. They are also known as the barbary dove, and have a distinct ring-like shape around the back of their neck." - icon_state = "ringneckdove" - icon_dead = "ringneckdove-dead" - tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years. - icon_scale_x = 0.5 - icon_scale_y = 0.5 - -/decl/mob_organ_names/bird - hit_zones = list("head", "chest", "left leg", "right leg", "left wing", "right wing") +// Base bird type. + +/mob/living/simple_mob/animal/passive/bird + name = "bird" + desc = "A domesticated bird. Tweet tweet!" + player_msg = "You are able to fly." + + icon = 'icons/mob/birds.dmi' + icon_state = "parrot" + item_state = null + icon_rest = "parrot-held" + icon_dead = "parrot-dead" + + pass_flags = PASSTABLE + + health = 30 + maxHealth = 30 + melee_damage_lower = 3 + melee_damage_upper = 3 + + movement_cooldown = -1 + hovering = TRUE // Birds can fly. + softfall = TRUE + parachuting = TRUE + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken + + attacktext = list("clawed", "pecked") + speak_emote = list("chirps", "caws") + has_langs = list(LANGUAGE_ANIMAL) + response_help = "pets" + response_disarm = "gently moves aside" + response_harm = "swats" + organ_names = /decl/mob_organ_names/bird + + say_list_type = /datum/say_list/bird + holder_type = /obj/item/weapon/holder/bird + +/datum/say_list/bird + speak = list("Chirp!","Caw!","Screech!","Squawk!") + emote_hear = list("chirps","caws") + emote_see = list("shakes their head", "ruffles their feathers") + +// Subtypes for birbs. +/mob/living/simple_mob/animal/passive/bird/black_bird + name = "common blackbird" + desc = "A species of bird, both the males and females are known to be territorial on their breeding grounds." + icon_state = "commonblackbird" + icon_dead = "commonblackbird-dead" + tt_desc = "E Turdus merula" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/azure_tit + name = "azure tit" + desc = "A species of bird, colored blue and white." + icon_state = "azuretit" + icon_dead = "azuretit-dead" + tt_desc = "E Cyanistes cyanus" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/european_robin + name = "european robin" + desc = "A species of bird, they have been studied for their sense of magnetoreception." + icon_state = "europeanrobin" + icon_dead = "europeanrobin-dead" + tt_desc = "E Erithacus rubecula" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/goldcrest + name = "goldcrest" + desc = "A species of bird, they were once called 'king of the birds' in ancient human folklore, for their golden crest. \ + Today, their scientific name still elude towards this, with regulus, meaning petty king." + icon_state = "goldcrest" + icon_dead = "goldcrest-dead" + tt_desc = "E Regulus regulus" + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/mob/living/simple_mob/animal/passive/bird/ringneck_dove + name = "ringneck dove" + desc = "A species of bird. They are also known as the barbary dove, and have a distinct ring-like shape around the back of their neck." + icon_state = "ringneckdove" + icon_dead = "ringneckdove-dead" + tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years. + icon_scale_x = 0.5 + icon_scale_y = 0.5 + +/decl/mob_organ_names/bird + hit_zones = list("head", "chest", "left leg", "right leg", "left wing", "right wing") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm index 51ae8f87384..4f791e8caa2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm @@ -1,285 +1,285 @@ -var/list/_cat_default_emotes = list( - /decl/emote/visible, - /decl/emote/visible/scratch, - /decl/emote/visible/drool, - /decl/emote/visible/nod, - /decl/emote/visible/sway, - /decl/emote/visible/sulk, - /decl/emote/visible/twitch, - /decl/emote/visible/twitch_v, - /decl/emote/visible/dance, - /decl/emote/visible/roll, - /decl/emote/visible/shake, - /decl/emote/visible/jump, - /decl/emote/visible/shiver, - /decl/emote/visible/collapse, - /decl/emote/visible/spin, - /decl/emote/visible/sidestep, - /decl/emote/audible, - /decl/emote/audible/hiss, - /decl/emote/audible/whimper, - /decl/emote/audible/gasp, - /decl/emote/audible/scretch, - /decl/emote/audible/choke, - /decl/emote/audible/moan, - /decl/emote/audible/gnarl, - /decl/emote/audible/purr, - /decl/emote/audible/purrlong -) - -/mob/living/simple_mob/animal/passive/cat - name = "cat" - desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." - tt_desc = "E Felis silvestris catus" - icon = 'icons/mob/pets.dmi' - icon_state = "cat2" - item_state = "cat2" - - movement_cooldown = -1 - - meat_amount = 1 - see_in_dark = 6 // Not sure if this actually works. - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - - holder_type = /obj/item/weapon/holder/cat - mob_size = MOB_SMALL - - has_langs = list(LANGUAGE_ANIMAL) - - var/mob/living/friend = null // Our best pal, who we'll follow. Meow. - var/named = FALSE //have I been named yet? - var/friend_name = null //VOREStation Edit - Lock befriending to this character - -/mob/living/simple_mob/animal/passive/cat/Initialize() - icon_living = "[initial(icon_state)]" - icon_dead = "[initial(icon_state)]_dead" - icon_rest = "[initial(icon_state)]_rest" - update_icon() - return ..() - -/mob/living/simple_mob/animal/passive/cat/get_available_emotes() - return global._cat_default_emotes.Copy() - -/mob/living/simple_mob/animal/passive/cat/handle_special() - if(!stat && prob(2)) // spooky - var/mob/observer/dead/spook = locate() in range(src, 5) - if(spook) - var/turf/T = get_turf(spook) - var/list/visible = list() - for(var/obj/O in T.contents) - if(!O.invisibility && O.name) - visible += O - if(visible.len) - var/atom/A = pick(visible) - visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") - -// Instakills mice. -/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) - if(ismouse(A)) - var/mob/living/simple_mob/animal/passive/mouse/mouse = A - if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. - mouse.splat() - visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) - else - ..() - -/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) - if(L == friend) // Always be pals with our special friend. - return TRUE - - . = ..() - - if(.) // We're pals, but they might be a dirty mouse... - if(ismouse(L)) - return FALSE // Cats and mice can never get along. - -/mob/living/simple_mob/animal/passive/cat/verb/become_friends() - set name = "Become Friends" - set category = "IC" - set src in view(1) - - var/mob/living/L = usr - if(!istype(L)) - return // Fuck off ghosts. - - if(friend) - if(friend == usr) - to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) - return - else - to_chat(L, span("warning", "\The [src] ignores you.")) - return - - //VOREStation Edit Start - Adds friend_name var checks - if(!friend_name || L.real_name == friend_name) - friend = L - face_atom(L) - to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) - visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) - - if(has_AI()) - var/datum/ai_holder/AI = ai_holder - AI.set_follow(friend) - else - to_chat(L, span("notice", "[src] ignores you.")) - //VOREStation Edit End - - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_mob/animal/passive/cat/runtime - name = "Runtime" - desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." - tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. - gender = FEMALE - icon_state = "cat" - item_state = "cat" - named = TRUE - holder_type = /obj/item/weapon/holder/cat/runtime - makes_dirt = 0 //Vorestation Edit - -/mob/living/simple_mob/animal/passive/cat/kitten - name = "kitten" - desc = "D'aaawwww!" - icon_state = "kitten" - item_state = "kitten" - gender = NEUTER - holder_type = /obj/item/weapon/holder/cat/kitten //VOREStation Edit - -/mob/living/simple_mob/animal/passive/cat/kitten/Initialize() - if(gender == NEUTER) - gender = pick(MALE, FEMALE) - return ..() - -/mob/living/simple_mob/animal/passive/cat/black - icon_state = "cat3" - item_state = "cat3" - -/mob/living/simple_mob/animal/passive/cat/bones - name = "Bones" - desc = "That's Bones the cat. He's a laid back, black cat. Meow." - gender = MALE - icon_state = "cat3" - item_state = "cat3" - named = TRUE - holder_type = /obj/item/weapon/holder/cat/fluff/bones - -// SPARKLY -/mob/living/simple_mob/animal/passive/cat/bluespace - name = "bluespace cat" - desc = "Shiny cat, shiny cat, it's not your fault." - tt_desc = "E Felis silvestris argentum" - icon_state = "bscat" - icon_living = "bscat" - icon_rest = null - icon_dead = null - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/bluespace - -/mob/living/simple_mob/animal/passive/cat/bluespace/death() - animate(src, alpha = 0, color = "#0000FF", time = 0.5 SECOND) - spawn(0.5 SECOND) - qdel(src) - -/mob/living/simple_mob/animal/passive/cat/bread - name = "bread cat" - desc = "Brought lunch to work." - tt_desc = "E Felis silvestris breadinum" - icon_state = "breadcat" - icon_living = "breadcat" - icon_rest = "breadcat_rest" - icon_dead = "breadcat_dead" - //icon_sit = "breadcat_sit" - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/breadcat - -/mob/living/simple_mob/animal/passive/cat/original - name = "original cat" - desc = "Donut steal." - tt_desc = "E Felis silvestris originalis" - icon_state = "original" - icon_living = "original" - icon_rest = "original_rest" - icon_dead = "original_dead" - //icon_sit = "original_sit" - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/original - -/mob/living/simple_mob/animal/passive/cat/cak - name = "cak" - desc = "Optimal combination of things?" - tt_desc = "E Felis silvestris dessertus" - icon_state = "cak" - icon_living = "cak" - icon_rest = "cak_rest" - icon_dead = "cak_dead" - //icon_sit = "cak_sit" - makes_dirt = 0 - holder_type = /obj/item/weapon/holder/cat/cak - -/mob/living/simple_mob/animal/passive/cat/space - name = "space cat" - desc = "Did someone write a song about this cat?" - tt_desc = "E Felis silvestris stellaris" - icon_state = "spacecat" - icon_living = "spacecat" - icon_rest = "spacecat_rest" - icon_dead = "spacecat_dead" - //icon_sit = "spacecat_sit" - holder_type = /obj/item/weapon/holder/cat/spacecat - makes_dirt = 0 - - minbodytemp = 0 // Minimum "okay" temperature in kelvin - maxbodytemp = 900 // Maximum of above - heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - - min_oxy = 0 // Oxygen in moles, minimum, 0 is 'no minimum' - max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' - min_tox = 0 // Phoron min - max_tox = 0 // Phoron max - min_co2 = 0 // CO2 min - max_co2 = 0 // CO2 max - min_n2 = 0 // N2 min - max_n2 = 0 // N2 max - unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above - -/datum/say_list/cat - speak = list("Meow!","Esp!","Purr!","HSSSSS") - emote_hear = list("meows","mews") - emote_see = list("shakes their head", "shivers") - say_maybe_target = list("Meow?","Mew?","Mao?") - say_got_target = list("MEOW!","HSSSS!","REEER!") - -/mob/living/simple_mob/animal/passive/cat/attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) - if(named) - to_chat(user, "\The [name] already has a name!") - else - var/tmp_name = sanitizeSafe(tgui_input_text(user, "Give \the [name] a name", "Name", null, MAX_NAME_LEN), MAX_NAME_LEN) - if(length(tmp_name) > 50) - to_chat(user, "The name can be at most 50 characters long.") - else - to_chat(user, "You name \the [name]. Meow!") - name = tmp_name - named = TRUE - else - ..() - -/obj/item/weapon/cat_box - name = "faintly purring box" - desc = "This box is purring faintly. You're pretty sure there's a cat inside it." - icon = 'icons/obj/storage.dmi' - icon_state = "box" - var/cattype = /mob/living/simple_mob/animal/passive/cat - -/obj/item/weapon/cat_box/attack_self(var/mob/user) - var/turf/catturf = get_turf(src) - to_chat(user, "You peek into \the [name]-- and a cat jumps out!") - new cattype(catturf) - new /obj/item/stack/material/cardboard(catturf) //if i fits i sits - qdel(src) - -/obj/item/weapon/cat_box/black - cattype = /mob/living/simple_mob/animal/passive/cat/black +var/list/_cat_default_emotes = list( + /decl/emote/visible, + /decl/emote/visible/scratch, + /decl/emote/visible/drool, + /decl/emote/visible/nod, + /decl/emote/visible/sway, + /decl/emote/visible/sulk, + /decl/emote/visible/twitch, + /decl/emote/visible/twitch_v, + /decl/emote/visible/dance, + /decl/emote/visible/roll, + /decl/emote/visible/shake, + /decl/emote/visible/jump, + /decl/emote/visible/shiver, + /decl/emote/visible/collapse, + /decl/emote/visible/spin, + /decl/emote/visible/sidestep, + /decl/emote/audible, + /decl/emote/audible/hiss, + /decl/emote/audible/whimper, + /decl/emote/audible/gasp, + /decl/emote/audible/scretch, + /decl/emote/audible/choke, + /decl/emote/audible/moan, + /decl/emote/audible/gnarl, + /decl/emote/audible/purr, + /decl/emote/audible/purrlong +) + +/mob/living/simple_mob/animal/passive/cat + name = "cat" + desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." + tt_desc = "E Felis silvestris catus" + icon = 'icons/mob/pets.dmi' + icon_state = "cat2" + item_state = "cat2" + + movement_cooldown = -1 + + meat_amount = 1 + see_in_dark = 6 // Not sure if this actually works. + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + holder_type = /obj/item/weapon/holder/cat + mob_size = MOB_SMALL + + has_langs = list(LANGUAGE_ANIMAL) + + var/mob/living/friend = null // Our best pal, who we'll follow. Meow. + var/named = FALSE //have I been named yet? + var/friend_name = null //VOREStation Edit - Lock befriending to this character + +/mob/living/simple_mob/animal/passive/cat/Initialize() + icon_living = "[initial(icon_state)]" + icon_dead = "[initial(icon_state)]_dead" + icon_rest = "[initial(icon_state)]_rest" + update_icon() + return ..() + +/mob/living/simple_mob/animal/passive/cat/get_available_emotes() + return global._cat_default_emotes.Copy() + +/mob/living/simple_mob/animal/passive/cat/handle_special() + if(!stat && prob(2)) // spooky + var/mob/observer/dead/spook = locate() in range(src, 5) + if(spook) + var/turf/T = get_turf(spook) + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + +// Instakills mice. +/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) + if(ismouse(A)) + var/mob/living/simple_mob/animal/passive/mouse/mouse = A + if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. + mouse.splat() + visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) + else + ..() + +/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) + if(L == friend) // Always be pals with our special friend. + return TRUE + + . = ..() + + if(.) // We're pals, but they might be a dirty mouse... + if(ismouse(L)) + return FALSE // Cats and mice can never get along. + +/mob/living/simple_mob/animal/passive/cat/verb/become_friends() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + var/mob/living/L = usr + if(!istype(L)) + return // Fuck off ghosts. + + if(friend) + if(friend == usr) + to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) + return + else + to_chat(L, span("warning", "\The [src] ignores you.")) + return + + //VOREStation Edit Start - Adds friend_name var checks + if(!friend_name || L.real_name == friend_name) + friend = L + face_atom(L) + to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) + visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) + + if(has_AI()) + var/datum/ai_holder/AI = ai_holder + AI.set_follow(friend) + else + to_chat(L, span("notice", "[src] ignores you.")) + //VOREStation Edit End + + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_mob/animal/passive/cat/runtime + name = "Runtime" + desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." + tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. + gender = FEMALE + icon_state = "cat" + item_state = "cat" + named = TRUE + holder_type = /obj/item/weapon/holder/cat/runtime + makes_dirt = 0 //Vorestation Edit + +/mob/living/simple_mob/animal/passive/cat/kitten + name = "kitten" + desc = "D'aaawwww!" + icon_state = "kitten" + item_state = "kitten" + gender = NEUTER + holder_type = /obj/item/weapon/holder/cat/kitten //VOREStation Edit + +/mob/living/simple_mob/animal/passive/cat/kitten/Initialize() + if(gender == NEUTER) + gender = pick(MALE, FEMALE) + return ..() + +/mob/living/simple_mob/animal/passive/cat/black + icon_state = "cat3" + item_state = "cat3" + +/mob/living/simple_mob/animal/passive/cat/bones + name = "Bones" + desc = "That's Bones the cat. He's a laid back, black cat. Meow." + gender = MALE + icon_state = "cat3" + item_state = "cat3" + named = TRUE + holder_type = /obj/item/weapon/holder/cat/fluff/bones + +// SPARKLY +/mob/living/simple_mob/animal/passive/cat/bluespace + name = "bluespace cat" + desc = "Shiny cat, shiny cat, it's not your fault." + tt_desc = "E Felis silvestris argentum" + icon_state = "bscat" + icon_living = "bscat" + icon_rest = null + icon_dead = null + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/bluespace + +/mob/living/simple_mob/animal/passive/cat/bluespace/death() + animate(src, alpha = 0, color = "#0000FF", time = 0.5 SECOND) + spawn(0.5 SECOND) + qdel(src) + +/mob/living/simple_mob/animal/passive/cat/bread + name = "bread cat" + desc = "Brought lunch to work." + tt_desc = "E Felis silvestris breadinum" + icon_state = "breadcat" + icon_living = "breadcat" + icon_rest = "breadcat_rest" + icon_dead = "breadcat_dead" + //icon_sit = "breadcat_sit" + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/breadcat + +/mob/living/simple_mob/animal/passive/cat/original + name = "original cat" + desc = "Donut steal." + tt_desc = "E Felis silvestris originalis" + icon_state = "original" + icon_living = "original" + icon_rest = "original_rest" + icon_dead = "original_dead" + //icon_sit = "original_sit" + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/original + +/mob/living/simple_mob/animal/passive/cat/cak + name = "cak" + desc = "Optimal combination of things?" + tt_desc = "E Felis silvestris dessertus" + icon_state = "cak" + icon_living = "cak" + icon_rest = "cak_rest" + icon_dead = "cak_dead" + //icon_sit = "cak_sit" + makes_dirt = 0 + holder_type = /obj/item/weapon/holder/cat/cak + +/mob/living/simple_mob/animal/passive/cat/space + name = "space cat" + desc = "Did someone write a song about this cat?" + tt_desc = "E Felis silvestris stellaris" + icon_state = "spacecat" + icon_living = "spacecat" + icon_rest = "spacecat_rest" + icon_dead = "spacecat_dead" + //icon_sit = "spacecat_sit" + holder_type = /obj/item/weapon/holder/cat/spacecat + makes_dirt = 0 + + minbodytemp = 0 // Minimum "okay" temperature in kelvin + maxbodytemp = 900 // Maximum of above + heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + + min_oxy = 0 // Oxygen in moles, minimum, 0 is 'no minimum' + max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' + min_tox = 0 // Phoron min + max_tox = 0 // Phoron max + min_co2 = 0 // CO2 min + max_co2 = 0 // CO2 max + min_n2 = 0 // N2 min + max_n2 = 0 // N2 max + unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above + +/datum/say_list/cat + speak = list("Meow!","Esp!","Purr!","HSSSSS") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + say_maybe_target = list("Meow?","Mew?","Mao?") + say_got_target = list("MEOW!","HSSSS!","REEER!") + +/mob/living/simple_mob/animal/passive/cat/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/device/flashlight/pen)) + if(named) + to_chat(user, "\The [name] already has a name!") + else + var/tmp_name = sanitizeSafe(tgui_input_text(user, "Give \the [name] a name", "Name", null, MAX_NAME_LEN), MAX_NAME_LEN) + if(length(tmp_name) > 50) + to_chat(user, "The name can be at most 50 characters long.") + else + to_chat(user, "You name \the [name]. Meow!") + name = tmp_name + named = TRUE + else + ..() + +/obj/item/weapon/cat_box + name = "faintly purring box" + desc = "This box is purring faintly. You're pretty sure there's a cat inside it." + icon = 'icons/obj/storage.dmi' + icon_state = "box" + var/cattype = /mob/living/simple_mob/animal/passive/cat + +/obj/item/weapon/cat_box/attack_self(var/mob/user) + var/turf/catturf = get_turf(src) + to_chat(user, "You peek into \the [name]-- and a cat jumps out!") + new cattype(catturf) + new /obj/item/stack/material/cardboard(catturf) //if i fits i sits + qdel(src) + +/obj/item/weapon/cat_box/black + cattype = /mob/living/simple_mob/animal/passive/cat/black diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm index 8e74d7e873a..dc24b8d396e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm @@ -33,7 +33,7 @@ if(!stat) for(var/mob/M in viewers(user, null)) if ((M.client && !( M.blinded ))) - M.show_message("[user] baps [name] on the nose with the rolled up [O]") + M.show_message(span_blue("[user] baps [name] on the nose with the rolled up [O].")) spawn(0) for(var/i in list(1,2,4,8,4,2,1,2)) set_dir(i) @@ -103,7 +103,7 @@ //pupplies cannot wear anything. /mob/living/simple_mob/animal/passive/dog/corgi/puppy/Topic(href, href_list) if(href_list["remove_inv"] || href_list["add_inv"]) - to_chat(usr, "You can't fit this on [src]") + to_chat(usr, span_red("You can't fit this on [src]!")) return ..() @@ -190,7 +190,7 @@ //Lisa already has a cute bow! /mob/living/simple_mob/animal/passive/dog/corgi/Lisa/Topic(href, href_list) if(href_list["remove_inv"] || href_list["add_inv"]) - to_chat(usr, "[src] already has a cute bow!") + to_chat(usr, span_red("[src] already has a cute bow!")) return ..() @@ -279,4 +279,4 @@ icon_dead = "brittany_dead" /decl/mob_organ_names/corgi - hit_zones = list("head", "body", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail", "heart") //You monster. \ No newline at end of file + hit_zones = list("head", "body", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail", "heart") //You monster. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm index 27c6cc7f09d..9adf67d779a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fennec.dm @@ -1,29 +1,29 @@ -/mob/living/simple_mob/animal/passive/fennec - name = "fennec" - desc = "A fox preferring arid climates, also known as a dingler, or a goob." - tt_desc = "Vulpes Zerda" - icon_state = "fennec" - item_state = "fennec" - - movement_cooldown = -1 - - see_in_dark = 6 - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - - holder_type = /obj/item/weapon/holder/fennec - mob_size = MOB_SMALL - - has_langs = list(LANGUAGE_ANIMAL) - -/mob/living/simple_mob/animal/passive/fennec/faux - name = "faux" - desc = "Domesticated fennec. Seems to like screaming just as much though." - -/mob/living/simple_mob/animal/passive/fennec/Initialize() - icon_living = "[initial(icon_state)]" - icon_dead = "[initial(icon_state)]_dead" - icon_rest = "[initial(icon_state)]_rest" - update_icon() - return ..() +/mob/living/simple_mob/animal/passive/fennec + name = "fennec" + desc = "A fox preferring arid climates, also known as a dingler, or a goob." + tt_desc = "Vulpes Zerda" + icon_state = "fennec" + item_state = "fennec" + + movement_cooldown = -1 + + see_in_dark = 6 + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + holder_type = /obj/item/weapon/holder/fennec + mob_size = MOB_SMALL + + has_langs = list(LANGUAGE_ANIMAL) + +/mob/living/simple_mob/animal/passive/fennec/faux + name = "faux" + desc = "Domesticated fennec. Seems to like screaming just as much though." + +/mob/living/simple_mob/animal/passive/fennec/Initialize() + icon_living = "[initial(icon_state)]" + icon_dead = "[initial(icon_state)]_dead" + icon_rest = "[initial(icon_state)]_rest" + update_icon() + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm index c03327b5bd9..e985111c0c9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm @@ -1,253 +1,253 @@ -// Parrots can talk, and may repeat things it hears. -/mob/living/simple_mob/animal/passive/bird/parrot - name = "parrot" - description_info = "You can give it a headset by clicking on it with a headset. \ - To remove it, click the bird while on grab intent." - has_langs = list(LANGUAGE_GALCOM, LANGUAGE_ANIMAL) - - ai_holder_type = /datum/ai_holder/simple_mob/passive/parrot - - // A headset, so that talking parrots can yell at the crew over comms. - // If set to a type, on initialize it will be instantiated into that type. - var/obj/item/device/radio/headset/my_headset = null - -// Say list -/datum/say_list/bird/poly - speak = list( - "Poly wanna cracker!", - "Check the singulo, you chucklefucks!", - "Wire the solars, you lazy bums!", - "WHO TOOK THE DAMN HARDSUITS?", - "OH GOD ITS FREE CALL THE SHUTTLE", - "Danger! Crystal hyperstructure instability!", - "CRYSTAL DELAMINATION IMMINENT.", - "Tweet tweet, I'm a Teshari.", - "Chitters.", - "Meteors have been detected on a collision course with the station!" - ) - -// Lets the AI use headsets. -// Player-controlled parrots will need to do it manually. -/mob/living/simple_mob/animal/passive/bird/parrot/ISay(message) - if(my_headset && prob(50)) - var/list/keys = list() - for(var/channel in my_headset.channels) - var/key = get_radio_key_from_channel(channel) - if(key) - keys += key - if(keys.len) - var/key_used = pick(keys) - return say("[key_used] [message]") - return say(message) - -// Ugly saycode so parrots can use their headsets. -/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, used_radios, whispering) - ..() - if(message_mode) - if(my_headset && istype(my_headset, /obj/item/device/radio)) - my_headset.talk_into(src, message, message_mode, verb, whispering) - used_radios += my_headset - -// Clicked on while holding an object. -/mob/living/simple_mob/animal/passive/bird/parrot/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/device/radio/headset)) - give_headset(I, user) - return - return ..() - -// Clicked on by empty hand. -/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/living/L) - if(L.a_intent == I_GRAB && my_headset) - remove_headset(L) - else - ..() - - -/mob/living/simple_mob/animal/passive/bird/parrot/proc/give_headset(obj/item/device/radio/headset/new_headset, mob/living/user) - if(!istype(new_headset)) - to_chat(user, span("warning", "\The [new_headset] isn't a headset.")) - return - if(my_headset) - to_chat(user, span("warning", "\The [src] is already wearing \a [my_headset].")) - return - else - user.drop_item(new_headset) - my_headset = new_headset - new_headset.forceMove(src) - to_chat(user, span("warning", "You place \a [new_headset] on \the [src]. You monster.")) - to_chat(src, span("notice", "\The [user] gives you \a [new_headset]. You should put it to good use immediately.")) - return - -/mob/living/simple_mob/animal/passive/bird/parrot/proc/remove_headset(mob/living/user) - if(!my_headset) - to_chat(user, "\The [src] doesn't have a headset to remove, thankfully.") - else - ISay("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - my_headset.forceMove(get_turf(src)) - user.put_in_hands(my_headset) - to_chat(user, span("notice", "You take away \the [src]'s [my_headset.name]. Finally.")) - to_chat(src, span("warning", "\The [user] takes your [my_headset.name] away! How cruel!")) - my_headset = null - -/mob/living/simple_mob/animal/passive/bird/parrot/examine(mob/user) - . = ..() - if(my_headset) - . += "It is wearing \a [my_headset]." - -/mob/living/simple_mob/animal/passive/bird/parrot/Initialize() - if(my_headset) - my_headset = new my_headset(src) - return ..() - -// Subtypes. - -// Best Bird -/mob/living/simple_mob/animal/passive/bird/parrot/poly - name = "Poly" - desc = "It's a parrot. An expert on quantum cracker theory." - icon_state = "poly" - icon_rest = "poly-held" - icon_dead = "poly-dead" - tt_desc = "E Ara macao" - attack_armor_pen = 20 //HE HAS THE B E A K - my_headset = /obj/item/device/radio/headset/headset_eng - say_list_type = /datum/say_list/bird/poly - -// Best Bird with best headset. -/mob/living/simple_mob/animal/passive/bird/parrot/poly/ultimate - my_headset = /obj/item/device/radio/headset/omni - -/mob/living/simple_mob/animal/passive/bird/parrot/kea - name = "kea" - desc = "A species of parrot. On Earth, they are unique among other parrots for residing in alpine climates. \ - They are known to be intelligent and curious, which has made some consider them a pest." - icon_state = "kea" - icon_rest = "kea-held" - icon_dead = "kea-dead" - tt_desc = "E Nestor notabilis" - -/mob/living/simple_mob/animal/passive/bird/parrot/eclectus - name = "eclectus" - desc = "A species of parrot, this species features extreme sexual dimorphism in their plumage's colors. \ - A male eclectus has emerald green plumage, where as a female eclectus has red and purple plumage." - icon_state = "eclectus" - icon_rest = "eclectus-held" - icon_dead = "eclectus-dead" - tt_desc = "E Eclectus roratus" - -/mob/living/simple_mob/animal/passive/bird/parrot/eclectus/Initialize() - gender = pick(MALE, FEMALE) - if(gender == FEMALE) - icon_state = "eclectusf" - icon_rest = "eclectusf-held" - icon_dead = "eclectusf-dead" - return ..() - -/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot - name = "grey parrot" - desc = "A species of parrot. This one is predominantly grey, but has red tail feathers." - icon_state = "agrey" - icon_rest = "agrey-held" - icon_dead = "agrey-dead" - tt_desc = "E Psittacus erithacus" - -/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique - name = "black-headed caique" - desc = "A species of parrot, these birds have a distinct black color on their heads, distinguishing them from their relative Caiques." - icon_state = "bcaique" - icon_rest = "bcaique-held" - icon_dead = "bcaique-dead" - tt_desc = "E Pionites melanocephalus" - -/mob/living/simple_mob/animal/passive/bird/parrot/white_caique - name = "white-bellied caique" - desc = "A species of parrot, they are also known as the Green-Thighed Parrot." - icon_state = "wcaique" - icon_rest = "wcaique-held" - icon_dead = "wcaique-dead" - tt_desc = "E Pionites leucogaster" - -/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar - name = "budgerigar" - desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ - This one is has its natural colors of green and yellow." - icon_state = "gbudge" - icon_rest = "gbudge-held" - icon_dead = "gbudge-dead" - tt_desc = "E Melopsittacus undulatus" - -/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue - icon_state = "bbudge" - icon_rest = "bbudge-held" - icon_dead = "bbudge-dead" - desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ - This one has a mutation which altered its color to be blue instead of green and yellow." - -/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen - icon_state = "bgbudge" - icon_rest = "bgbudge-held" - icon_dead = "bgbudge-dead" - desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ - This one has a mutation which altered its color to be a mix of blue and green." - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel - name = "cockatiel" - desc = "A species of parrot. This one has a highly visible crest." - icon_state = "tiel" - icon_rest = "tiel-held" - icon_dead = "tiel-dead" - tt_desc = "E Nymphicus hollandicus" - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white - icon_state = "wtiel" - icon_rest = "wtiel-held" - icon_dead = "wtiel-dead" - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish - icon_state = "luttiel" - icon_rest = "luttiel-held" - icon_dead = "luttiel-dead" - -/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey - icon_state = "blutiel" // idk why this is blu. - icon_rest = "blutiel-held" - icon_dead = "blutiel-dead" - -// This actually might be the yellow-crested cockatoo but idk. -/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo - name = "sulphur-crested cockatoo" - desc = "A species of parrot. This one has an expressive yellow crest. Their underwing and tail feathers are also yellow." - icon_state = "too" - icon_rest = "too-held" - icon_dead = "too-dead" - tt_desc = "E Cacatua galerita" - -// This was originally called 'hooded_too', which might not mean the unbrella cockatoo but idk. -/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo - name = "white cockatoo" - desc = "A species of parrot. This one is also known as the Umbrella Cockatoo, due to the semicircular shape of its crest." - icon_state = "utoo" - icon_rest = "utoo-held" - icon_dead = "utoo-dead" - tt_desc = "E Cacatua alba" - -/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo - name = "pink cockatoo" - desc = "A species of parrot. This one is also known as Major Mitchell's cockatoo, \ - in honor of a human surveyor and explorer who existed before humans fully explored their home planet." - icon_state = "mtoo" - icon_rest = "mtoo-held" - icon_dead = "mtoo-dead" - tt_desc = "E Lophochroa leadbeateri" - - -// AI -/datum/ai_holder/simple_mob/passive/parrot - speak_chance = 2 - base_wander_delay = 8 - -/datum/ai_holder/simple_mob/passive/parrot/on_hear_say(mob/living/speaker, message) - if(holder.stat || !holder.say_list || !message || speaker == holder) - return - var/datum/say_list/S = holder.say_list - S.speak |= message +// Parrots can talk, and may repeat things it hears. +/mob/living/simple_mob/animal/passive/bird/parrot + name = "parrot" + description_info = "You can give it a headset by clicking on it with a headset. \ + To remove it, click the bird while on grab intent." + has_langs = list(LANGUAGE_GALCOM, LANGUAGE_ANIMAL) + + ai_holder_type = /datum/ai_holder/simple_mob/passive/parrot + + // A headset, so that talking parrots can yell at the crew over comms. + // If set to a type, on initialize it will be instantiated into that type. + var/obj/item/device/radio/headset/my_headset = null + +// Say list +/datum/say_list/bird/poly + speak = list( + "Poly wanna cracker!", + "Check the singulo, you chucklefucks!", + "Wire the solars, you lazy bums!", + "WHO TOOK THE DAMN HARDSUITS?", + "OH GOD ITS FREE CALL THE SHUTTLE", + "Danger! Crystal hyperstructure instability!", + "CRYSTAL DELAMINATION IMMINENT.", + "Tweet tweet, I'm a Teshari.", + "Chitters.", + "Meteors have been detected on a collision course with the station!" + ) + +// Lets the AI use headsets. +// Player-controlled parrots will need to do it manually. +/mob/living/simple_mob/animal/passive/bird/parrot/ISay(message) + if(my_headset && prob(50)) + var/list/keys = list() + for(var/channel in my_headset.channels) + var/key = get_radio_key_from_channel(channel) + if(key) + keys += key + if(keys.len) + var/key_used = pick(keys) + return say("[key_used] [message]") + return say(message) + +// Ugly saycode so parrots can use their headsets. +/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, used_radios, whispering) + ..() + if(message_mode) + if(my_headset && istype(my_headset, /obj/item/device/radio)) + my_headset.talk_into(src, message, message_mode, verb, whispering) + used_radios += my_headset + +// Clicked on while holding an object. +/mob/living/simple_mob/animal/passive/bird/parrot/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/radio/headset)) + give_headset(I, user) + return + return ..() + +// Clicked on by empty hand. +/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/living/L) + if(L.a_intent == I_GRAB && my_headset) + remove_headset(L) + else + ..() + + +/mob/living/simple_mob/animal/passive/bird/parrot/proc/give_headset(obj/item/device/radio/headset/new_headset, mob/living/user) + if(!istype(new_headset)) + to_chat(user, span("warning", "\The [new_headset] isn't a headset.")) + return + if(my_headset) + to_chat(user, span("warning", "\The [src] is already wearing \a [my_headset].")) + return + else + user.drop_item(new_headset) + my_headset = new_headset + new_headset.forceMove(src) + to_chat(user, span("warning", "You place \a [new_headset] on \the [src]. You monster.")) + to_chat(src, span("notice", "\The [user] gives you \a [new_headset]. You should put it to good use immediately.")) + return + +/mob/living/simple_mob/animal/passive/bird/parrot/proc/remove_headset(mob/living/user) + if(!my_headset) + to_chat(user, "\The [src] doesn't have a headset to remove, thankfully.") + else + ISay("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + my_headset.forceMove(get_turf(src)) + user.put_in_hands(my_headset) + to_chat(user, span("notice", "You take away \the [src]'s [my_headset.name]. Finally.")) + to_chat(src, span("warning", "\The [user] takes your [my_headset.name] away! How cruel!")) + my_headset = null + +/mob/living/simple_mob/animal/passive/bird/parrot/examine(mob/user) + . = ..() + if(my_headset) + . += "It is wearing \a [my_headset]." + +/mob/living/simple_mob/animal/passive/bird/parrot/Initialize() + if(my_headset) + my_headset = new my_headset(src) + return ..() + +// Subtypes. + +// Best Bird +/mob/living/simple_mob/animal/passive/bird/parrot/poly + name = "Poly" + desc = "It's a parrot. An expert on quantum cracker theory." + icon_state = "poly" + icon_rest = "poly-held" + icon_dead = "poly-dead" + tt_desc = "E Ara macao" + attack_armor_pen = 20 //HE HAS THE B E A K + my_headset = /obj/item/device/radio/headset/headset_eng + say_list_type = /datum/say_list/bird/poly + +// Best Bird with best headset. +/mob/living/simple_mob/animal/passive/bird/parrot/poly/ultimate + my_headset = /obj/item/device/radio/headset/omni + +/mob/living/simple_mob/animal/passive/bird/parrot/kea + name = "kea" + desc = "A species of parrot. On Earth, they are unique among other parrots for residing in alpine climates. \ + They are known to be intelligent and curious, which has made some consider them a pest." + icon_state = "kea" + icon_rest = "kea-held" + icon_dead = "kea-dead" + tt_desc = "E Nestor notabilis" + +/mob/living/simple_mob/animal/passive/bird/parrot/eclectus + name = "eclectus" + desc = "A species of parrot, this species features extreme sexual dimorphism in their plumage's colors. \ + A male eclectus has emerald green plumage, where as a female eclectus has red and purple plumage." + icon_state = "eclectus" + icon_rest = "eclectus-held" + icon_dead = "eclectus-dead" + tt_desc = "E Eclectus roratus" + +/mob/living/simple_mob/animal/passive/bird/parrot/eclectus/Initialize() + gender = pick(MALE, FEMALE) + if(gender == FEMALE) + icon_state = "eclectusf" + icon_rest = "eclectusf-held" + icon_dead = "eclectusf-dead" + return ..() + +/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot + name = "grey parrot" + desc = "A species of parrot. This one is predominantly grey, but has red tail feathers." + icon_state = "agrey" + icon_rest = "agrey-held" + icon_dead = "agrey-dead" + tt_desc = "E Psittacus erithacus" + +/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique + name = "black-headed caique" + desc = "A species of parrot, these birds have a distinct black color on their heads, distinguishing them from their relative Caiques." + icon_state = "bcaique" + icon_rest = "bcaique-held" + icon_dead = "bcaique-dead" + tt_desc = "E Pionites melanocephalus" + +/mob/living/simple_mob/animal/passive/bird/parrot/white_caique + name = "white-bellied caique" + desc = "A species of parrot, they are also known as the Green-Thighed Parrot." + icon_state = "wcaique" + icon_rest = "wcaique-held" + icon_dead = "wcaique-dead" + tt_desc = "E Pionites leucogaster" + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar + name = "budgerigar" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one is has its natural colors of green and yellow." + icon_state = "gbudge" + icon_rest = "gbudge-held" + icon_dead = "gbudge-dead" + tt_desc = "E Melopsittacus undulatus" + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue + icon_state = "bbudge" + icon_rest = "bbudge-held" + icon_dead = "bbudge-dead" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one has a mutation which altered its color to be blue instead of green and yellow." + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen + icon_state = "bgbudge" + icon_rest = "bgbudge-held" + icon_dead = "bgbudge-dead" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one has a mutation which altered its color to be a mix of blue and green." + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel + name = "cockatiel" + desc = "A species of parrot. This one has a highly visible crest." + icon_state = "tiel" + icon_rest = "tiel-held" + icon_dead = "tiel-dead" + tt_desc = "E Nymphicus hollandicus" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white + icon_state = "wtiel" + icon_rest = "wtiel-held" + icon_dead = "wtiel-dead" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish + icon_state = "luttiel" + icon_rest = "luttiel-held" + icon_dead = "luttiel-dead" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey + icon_state = "blutiel" // idk why this is blu. + icon_rest = "blutiel-held" + icon_dead = "blutiel-dead" + +// This actually might be the yellow-crested cockatoo but idk. +/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo + name = "sulphur-crested cockatoo" + desc = "A species of parrot. This one has an expressive yellow crest. Their underwing and tail feathers are also yellow." + icon_state = "too" + icon_rest = "too-held" + icon_dead = "too-dead" + tt_desc = "E Cacatua galerita" + +// This was originally called 'hooded_too', which might not mean the unbrella cockatoo but idk. +/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo + name = "white cockatoo" + desc = "A species of parrot. This one is also known as the Umbrella Cockatoo, due to the semicircular shape of its crest." + icon_state = "utoo" + icon_rest = "utoo-held" + icon_dead = "utoo-dead" + tt_desc = "E Cacatua alba" + +/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo + name = "pink cockatoo" + desc = "A species of parrot. This one is also known as Major Mitchell's cockatoo, \ + in honor of a human surveyor and explorer who existed before humans fully explored their home planet." + icon_state = "mtoo" + icon_rest = "mtoo-held" + icon_dead = "mtoo-dead" + tt_desc = "E Lophochroa leadbeateri" + + +// AI +/datum/ai_holder/simple_mob/passive/parrot + speak_chance = 2 + base_wander_delay = 8 + +/datum/ai_holder/simple_mob/passive/parrot/on_hear_say(mob/living/speaker, message) + if(holder.stat || !holder.say_list || !message || speaker == holder) + return + var/datum/say_list/S = holder.say_list + S.speak |= message diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm index 08014b87466..11397bafe51 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm @@ -1,66 +1,66 @@ -// Diyaabs are rather weak, but tend to exist in large numbers. -// They cooperate with other diyaabs, in order to swarm whoever decides to pick on the little fluffy critter. -// A cleaving weapon like an axe will make short work of the pack. - -/datum/category_item/catalogue/fauna/diyaab - name = "Sivian Fauna - Diyaab" - desc = "Classification: S Choeros hirtus\ -

                    \ - Small, social omnivores with dense seasonal wool fur valued by Sivian colonists for its cold resistance and softness. \ - The Diyaab lives in packs of anywhere from three to ten individuals, usually comprised of a family unit. Primarily herbivorous browsers, \ - supplementing their diet with organisms living in tree bark, \ - Diyaab packs have been observed to hunt prey several times their size during the less plentiful winter months. \ - Despite their unassuming appearance, the Diyaab possesses remarkably sharp anterior teeth." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/sif/diyaab - name = "diyaab" - desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." - tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! - catalogue_data = list(/datum/category_item/catalogue/fauna/diyaab) - - faction = "diyaab" - - icon_state = "diyaab" - icon_living = "diyaab" - icon_dead = "diyaab_dead" - icon = 'icons/jungle.dmi' - - maxHealth = 25 - health = 25 - - meat_amount = 2 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat - - movement_cooldown = -1 - - melee_damage_lower = 2 - melee_damage_upper = 6 - base_attack_cooldown = 1 SECOND - attack_sharp = TRUE //Bleeds, but it shouldn't rip off a limb? - attacktext = list("gouged") - - say_list_type = /datum/say_list/diyaab - ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative - - // What do you hit the mob with (on help) to get something from it? - harvest_tool = /obj/item/weapon/tool/wirecutters - // How long do we have to wait until it's harvestable again? - harvest_cooldown = 10 MINUTES - // How long does it take to harvest? - harvest_delay = 30 SECONDS - // What world.time was the last harvest? - harvest_recent = 0 - // How many times do we roll on the chance table? - harvest_per_hit = 1 - // Verb for harvesting. "sheared" "clipped" etc. - harvest_verb = "sheared" - // Associative list of paths and their chances. path = straws in the lot - harvest_results = list( - /obj/item/stack/material/cloth/diyaab = 10 - ) - -/datum/say_list/diyaab - speak = list("Awrr?", "Aowrl!", "Worrl.") - emote_see = list("sniffs the air cautiously","looks around") - emote_hear = list("snuffles") +// Diyaabs are rather weak, but tend to exist in large numbers. +// They cooperate with other diyaabs, in order to swarm whoever decides to pick on the little fluffy critter. +// A cleaving weapon like an axe will make short work of the pack. + +/datum/category_item/catalogue/fauna/diyaab + name = "Sivian Fauna - Diyaab" + desc = "Classification: S Choeros hirtus\ +

                    \ + Small, social omnivores with dense seasonal wool fur valued by Sivian colonists for its cold resistance and softness. \ + The Diyaab lives in packs of anywhere from three to ten individuals, usually comprised of a family unit. Primarily herbivorous browsers, \ + supplementing their diet with organisms living in tree bark, \ + Diyaab packs have been observed to hunt prey several times their size during the less plentiful winter months. \ + Despite their unassuming appearance, the Diyaab possesses remarkably sharp anterior teeth." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/sif/diyaab + name = "diyaab" + desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." + tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! + catalogue_data = list(/datum/category_item/catalogue/fauna/diyaab) + + faction = "diyaab" + + icon_state = "diyaab" + icon_living = "diyaab" + icon_dead = "diyaab_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 25 + health = 25 + + meat_amount = 2 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + movement_cooldown = -1 + + melee_damage_lower = 2 + melee_damage_upper = 6 + base_attack_cooldown = 1 SECOND + attack_sharp = TRUE //Bleeds, but it shouldn't rip off a limb? + attacktext = list("gouged") + + say_list_type = /datum/say_list/diyaab + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative + + // What do you hit the mob with (on help) to get something from it? + harvest_tool = /obj/item/weapon/tool/wirecutters + // How long do we have to wait until it's harvestable again? + harvest_cooldown = 10 MINUTES + // How long does it take to harvest? + harvest_delay = 30 SECONDS + // What world.time was the last harvest? + harvest_recent = 0 + // How many times do we roll on the chance table? + harvest_per_hit = 1 + // Verb for harvesting. "sheared" "clipped" etc. + harvest_verb = "sheared" + // Associative list of paths and their chances. path = straws in the lot + harvest_results = list( + /obj/item/stack/material/cloth/diyaab = 10 + ) + +/datum/say_list/diyaab + speak = list("Awrr?", "Aowrl!", "Worrl.") + emote_see = list("sniffs the air cautiously","looks around") + emote_hear = list("snuffles") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm index 9dd02f083cf..167992f9cd8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm @@ -121,7 +121,7 @@ Field studies suggest analytical abilities on par with some species of cepholapo desc = "A large, sleek snow drake with heavy claws, powerful jaws and many pale spines along its body." player_msg = "You are a large Sivian pack predator in symbiosis with the local bioluminescent bacteria. You can eat glowing \ tree fruit to fuel your ranged spitting attack and poisonous bite (on harm intent), as well as healing saliva \ - (on help intent).
                    There are humans moving through your territory; whether you help them get home safely, or treat them as a snack, \ + (on help intent).
                    There are humans moving through your territory; whether you help them get home safely, or treat them as a snack, \ is up to you." color = "#608894" icon = 'icons/mob/drake_adult.dmi' diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm index b4f4edd02ec..9c11c971592 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm @@ -1,86 +1,86 @@ -// Saviks are dangerous, angry creatures that hit hard, and will berserk if losing a fight. - -/datum/category_item/catalogue/fauna/savik - name = "Sivian Fauna - Savik" - desc = "Classification: S Pistris tellus\ -

                    \ - A predatory warm-blooded reptillian species covered in a layer of insulating down feathers. \ - The Savik's preferred method of hunting is to burrow under deep snow drifts, and lie in ambush for prey. \ - The Savik has been known to lie in wait for days at a time, generating heat by vibrating its shoulder plates \ - at a nigh inperceptable frequency while most of its body enters a state of sopor in order to conserve energy. \ -

                    \ - Once the Savik detects its prey, it will charge with incredible kinetic force with the two enormous, \ - angled bony plates on either side of the Savik's upper body acting as a natural snow plow, \ - allowing frightening ease of movement through deep snow. Due to the long periods between feeding, \ - the Savik will hunt its prey with absolute perseverence, as failure to catch a suitable meal is likely to \ - spell death for the animal due to the high energy expenditure of its initial strike. \ - The Savik has no known predators, and should be avoided at all costs." - value = CATALOGUER_REWARD_MEDIUM - -/mob/living/simple_mob/animal/sif/savik - name = "savik" - tt_desc = "S Pistris tellus" //landshark - catalogue_data = list(/datum/category_item/catalogue/fauna/savik) - faction = "savik" - - icon_state = "savik" - icon_living = "savik" - icon_dead = "savik_dead" - icon = 'icons/jungle.dmi' - - maxHealth = 125 - health = 125 - - movement_cooldown = -1 - - melee_damage_lower = 15 - melee_damage_upper = 35 - attack_armor_pen = 15 - attack_sharp = TRUE - attack_edge = TRUE - melee_attack_delay = 1 SECOND - attacktext = list("mauled") - - organ_names = /decl/mob_organ_names/savik - - player_msg = "You have the ability to berserk at will, which will grant strong physical bonuses for \ - a short period of time, however it will tire you and you will be much weaker for awhile after it expires." - - tame_items = list( - /obj/item/organ = 70, - /obj/item/weapon/reagent_containers/food/snacks/crabmeat = 30, - /obj/item/weapon/reagent_containers/food/snacks/meat = 20 - ) - - say_list_type = /datum/say_list/savik - ai_holder_type = /datum/ai_holder/simple_mob/savik - -/datum/say_list/savik - speak = list("Hruuugh!","Hrunnph") - emote_see = list("paws the ground","shakes its mane","stomps") - emote_hear = list("snuffles") - -/mob/living/simple_mob/animal/sif/savik/handle_special() - if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) - if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. - berserk() - -/mob/living/simple_mob/animal/sif/savik/fail_tame(var/obj/O, var/mob/user) - ..() - - if(prob(30)) // They don't like people messing with them and their food. - berserk() - -/datum/ai_holder/simple_mob/savik - mauling = TRUE - -// So players can use it too. -/mob/living/simple_mob/animal/sif/savik/verb/berserk() - set name = "Berserk" - set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." - set category = "Abilities" - - add_modifier(/datum/modifier/berserk, 30 SECONDS) - -/decl/mob_organ_names/savik - hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "right bone plate", "left bone plate", "tail", "left claw", "right claw") +// Saviks are dangerous, angry creatures that hit hard, and will berserk if losing a fight. + +/datum/category_item/catalogue/fauna/savik + name = "Sivian Fauna - Savik" + desc = "Classification: S Pistris tellus\ +

                    \ + A predatory warm-blooded reptillian species covered in a layer of insulating down feathers. \ + The Savik's preferred method of hunting is to burrow under deep snow drifts, and lie in ambush for prey. \ + The Savik has been known to lie in wait for days at a time, generating heat by vibrating its shoulder plates \ + at a nigh inperceptable frequency while most of its body enters a state of sopor in order to conserve energy. \ +

                    \ + Once the Savik detects its prey, it will charge with incredible kinetic force with the two enormous, \ + angled bony plates on either side of the Savik's upper body acting as a natural snow plow, \ + allowing frightening ease of movement through deep snow. Due to the long periods between feeding, \ + the Savik will hunt its prey with absolute perseverence, as failure to catch a suitable meal is likely to \ + spell death for the animal due to the high energy expenditure of its initial strike. \ + The Savik has no known predators, and should be avoided at all costs." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/animal/sif/savik + name = "savik" + tt_desc = "S Pistris tellus" //landshark + catalogue_data = list(/datum/category_item/catalogue/fauna/savik) + faction = "savik" + + icon_state = "savik" + icon_living = "savik" + icon_dead = "savik_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 125 + health = 125 + + movement_cooldown = -1 + + melee_damage_lower = 15 + melee_damage_upper = 35 + attack_armor_pen = 15 + attack_sharp = TRUE + attack_edge = TRUE + melee_attack_delay = 1 SECOND + attacktext = list("mauled") + + organ_names = /decl/mob_organ_names/savik + + player_msg = "You have the ability to berserk at will, which will grant strong physical bonuses for \ + a short period of time, however it will tire you and you will be much weaker for awhile after it expires." + + tame_items = list( + /obj/item/organ = 70, + /obj/item/weapon/reagent_containers/food/snacks/crabmeat = 30, + /obj/item/weapon/reagent_containers/food/snacks/meat = 20 + ) + + say_list_type = /datum/say_list/savik + ai_holder_type = /datum/ai_holder/simple_mob/savik + +/datum/say_list/savik + speak = list("Hruuugh!","Hrunnph") + emote_see = list("paws the ground","shakes its mane","stomps") + emote_hear = list("snuffles") + +/mob/living/simple_mob/animal/sif/savik/handle_special() + if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) + if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. + berserk() + +/mob/living/simple_mob/animal/sif/savik/fail_tame(var/obj/O, var/mob/user) + ..() + + if(prob(30)) // They don't like people messing with them and their food. + berserk() + +/datum/ai_holder/simple_mob/savik + mauling = TRUE + +// So players can use it too. +/mob/living/simple_mob/animal/sif/savik/verb/berserk() + set name = "Berserk" + set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." + set category = "Abilities" + + add_modifier(/datum/modifier/berserk, 30 SECONDS) + +/decl/mob_organ_names/savik + hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "right bone plate", "left bone plate", "tail", "left claw", "right claw") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm index df2f4da5a0d..e82bb301958 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm @@ -1,113 +1,113 @@ -// Shantaks are essentially sif wolves. - -/datum/category_item/catalogue/fauna/shantak - name = "Sivian Fauna - Shantak" - desc = "Classification: S Choeros shantak\ -

                    \ - The Shantak is easily recognized by its iridescent, crystaline mane. \ - The creature's specially adapted hairs are hardened by a natural hard mineral coating, \ - thickest in the mane but present across the whole body. \ - As well as giving the Shantak a coat nigh-inpenetrable to all but the most specialized predator, \ - their hard, almost metallic coat gives them a slightly musical accent as they move. \ - The Shantak uses its powerful foreclaws for both den-building and foraging. \ -

                    \ - Observed to share several square-mile territories with a small number of other individuals, \ - the Shantak will rotate between several dens dug deep into the hard earth throughout the year, \ - while deftly avoiding others of its species outwith mating season. While other wildlife makes use of these dens, \ - the Shantak is fiercely territorial and will defend itself against any creature it perceives as a threat with reckless abandon. \ - Their diet consists primarily of fungi and insects found just below the permafrost." - value = CATALOGUER_REWARD_MEDIUM - -/mob/living/simple_mob/animal/sif/shantak - name = "shantak" - desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ - Don't be fooled by its beauty though." - tt_desc = "S Choeros shantak" - catalogue_data = list(/datum/category_item/catalogue/fauna/shantak) - - faction = "shantak" - - icon_state = "shantak" - icon_living = "shantak" - icon_dead = "shantak_dead" - icon = 'icons/jungle.dmi' - - maxHealth = 75 - armor_soak = list( - "melee" = 5, - "bullet" = 0, - "laser" = 0, - "energy" = 0, - "bomb" = 0, - "bio" = 0, - "rad" = 0 - ) - - movement_cooldown = -1 - - melee_damage_lower = 6 - melee_damage_upper = 14 - base_attack_cooldown = 1 SECOND - melee_attack_delay = 0.5 SECONDS - attack_armor_pen = 5 - attack_sharp = TRUE - attack_edge = TRUE - attacktext = list("gouged") - - organ_names = /decl/mob_organ_names/shantak - - say_list_type = /datum/say_list/shantak - -/datum/say_list/shantak - speak = list("Shuhn.","Shrunnph?","Shunpf.") - emote_see = list("scratches the ground", "shakes out its mane", "clinks gently as it moves") - - -// The pack leader. -// Will command other shantaks to follow it. -/mob/living/simple_mob/animal/sif/shantak/leader - name = "big shantak" - desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ - This one seems bigger than the others, and has a commanding presence." - icon_scale_x = 1.5 - icon_scale_y = 1.5 - maxHealth = 125 - player_msg = "You have the ability to command other shantaks to follow you." - -/mob/living/simple_mob/animal/sif/shantak/leader/verb/rally_pack() - set name = "Rally Pack" - set desc = "Commands your fellow packmembers to follow you, the leader." - set category = "Abilities" - - for(var/mob/living/simple_mob/animal/sif/shantak/S in hearers(7, src)) - if(istype(S, /mob/living/simple_mob/animal/sif/shantak/leader)) // Leaders won't follow other leaders. Also avoids trying to follow ourselves. - continue - if(!S.ai_holder) - continue - if(S.faction != src.faction) - continue - var/datum/ai_holder/AI = S.ai_holder - AI.set_follow(src) - -// Variant that automatically commands nearby allies to follow it when created. -// Suggested to spawn last so it can rally up all the shantaks easily before hunting for tasty explorers. -/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/Initialize() - rally_pack() - return ..() - -// These ones only retaliate. Used for PoIs. -/mob/living/simple_mob/animal/sif/shantak/retaliate - ai_holder_type = /datum/ai_holder/simple_mob/retaliate - -/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/retaliate - ai_holder_type = /datum/ai_holder/simple_mob/retaliate - -//Vorestation Addition -/mob/living/simple_mob/animal/sif/shantak/scruffy - name = "Scruffy" - ai_holder_type = /datum/ai_holder/simple_mob/passive - makes_dirt = 0 - faction = "neutral" - -/decl/mob_organ_names/shantak - hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail", "mane", "snout") +// Shantaks are essentially sif wolves. + +/datum/category_item/catalogue/fauna/shantak + name = "Sivian Fauna - Shantak" + desc = "Classification: S Choeros shantak\ +

                    \ + The Shantak is easily recognized by its iridescent, crystaline mane. \ + The creature's specially adapted hairs are hardened by a natural hard mineral coating, \ + thickest in the mane but present across the whole body. \ + As well as giving the Shantak a coat nigh-inpenetrable to all but the most specialized predator, \ + their hard, almost metallic coat gives them a slightly musical accent as they move. \ + The Shantak uses its powerful foreclaws for both den-building and foraging. \ +

                    \ + Observed to share several square-mile territories with a small number of other individuals, \ + the Shantak will rotate between several dens dug deep into the hard earth throughout the year, \ + while deftly avoiding others of its species outwith mating season. While other wildlife makes use of these dens, \ + the Shantak is fiercely territorial and will defend itself against any creature it perceives as a threat with reckless abandon. \ + Their diet consists primarily of fungi and insects found just below the permafrost." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/animal/sif/shantak + name = "shantak" + desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ + Don't be fooled by its beauty though." + tt_desc = "S Choeros shantak" + catalogue_data = list(/datum/category_item/catalogue/fauna/shantak) + + faction = "shantak" + + icon_state = "shantak" + icon_living = "shantak" + icon_dead = "shantak_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 75 + armor_soak = list( + "melee" = 5, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + movement_cooldown = -1 + + melee_damage_lower = 6 + melee_damage_upper = 14 + base_attack_cooldown = 1 SECOND + melee_attack_delay = 0.5 SECONDS + attack_armor_pen = 5 + attack_sharp = TRUE + attack_edge = TRUE + attacktext = list("gouged") + + organ_names = /decl/mob_organ_names/shantak + + say_list_type = /datum/say_list/shantak + +/datum/say_list/shantak + speak = list("Shuhn.","Shrunnph?","Shunpf.") + emote_see = list("scratches the ground", "shakes out its mane", "clinks gently as it moves") + + +// The pack leader. +// Will command other shantaks to follow it. +/mob/living/simple_mob/animal/sif/shantak/leader + name = "big shantak" + desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ + This one seems bigger than the others, and has a commanding presence." + icon_scale_x = 1.5 + icon_scale_y = 1.5 + maxHealth = 125 + player_msg = "You have the ability to command other shantaks to follow you." + +/mob/living/simple_mob/animal/sif/shantak/leader/verb/rally_pack() + set name = "Rally Pack" + set desc = "Commands your fellow packmembers to follow you, the leader." + set category = "Abilities" + + for(var/mob/living/simple_mob/animal/sif/shantak/S in hearers(7, src)) + if(istype(S, /mob/living/simple_mob/animal/sif/shantak/leader)) // Leaders won't follow other leaders. Also avoids trying to follow ourselves. + continue + if(!S.ai_holder) + continue + if(S.faction != src.faction) + continue + var/datum/ai_holder/AI = S.ai_holder + AI.set_follow(src) + +// Variant that automatically commands nearby allies to follow it when created. +// Suggested to spawn last so it can rally up all the shantaks easily before hunting for tasty explorers. +/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/Initialize() + rally_pack() + return ..() + +// These ones only retaliate. Used for PoIs. +/mob/living/simple_mob/animal/sif/shantak/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + +//Vorestation Addition +/mob/living/simple_mob/animal/sif/shantak/scruffy + name = "Scruffy" + ai_holder_type = /datum/ai_holder/simple_mob/passive + makes_dirt = 0 + faction = "neutral" + +/decl/mob_organ_names/shantak + hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail", "mane", "snout") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm index 89cffd64c9b..357eb366e39 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm @@ -1,252 +1,252 @@ -// Space carp show up as a random event to wreck hapless people in space or near windows. -// They generally fit the archetype of 'fast but fragile'. -// This is compensated by being in groups (usually). - -/datum/category_item/catalogue/fauna/carp - name = "Voidborne Fauna - Space Carp" - desc = "A strange descendant of some form of voidborne life, they are the most \ - common naturally void-faring lifeform found in human territory. They've been named \ - 'Space Carp' by various groups of spacers due to resembling the fish from Earth.\ -

                    \ - Their lifecycle begins as a fungus-like growth, sometimes found on the walls of spacecraft \ - and space stations, before growing into a form which allows for independent travel. Even \ - when fully grown, they can sometimes be found to stow away on the hulls of spaceborne objects, \ - which might explain how they became widespread across many star systems.\ -

                    \ - Carp have a special gas bladder inside of them, which they utilize as a means of movement in \ - space by stategically releasing the gas to propel themselves in a process that resembles \ - thrusters on a spacecraft. The gas contained inside the carp also allows them \ - to float when inside an atmosphere. The carp might also spray 'spores' using a similar method.\ -

                    \ - They are hypercarnivorous to the point of cannibalism, consuming their own dead in order to \ - sustain themselves during hard times, which are rather frequent due to their prey being \ - vastly technologically advanced. For human habitats that are well secured, carp are generally \ - an annoyance. For those unable to adequately protect themselves, however, they can be \ - rather dangerous, especially if a mass migration of carp arrives." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/space/carp - name = "space carp" - desc = "A ferocious, fang-bearing creature that resembles a fish." - catalogue_data = list(/datum/category_item/catalogue/fauna/carp) - icon_state = "carp" - icon_living = "carp" - icon_dead = "carp_dead" - icon_gib = "carp_gib" - - faction = "carp" - maxHealth = 25 - health = 25 - movement_cooldown = -2 - hovering = TRUE - - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - - melee_damage_lower = 7 // About 14 DPS. - melee_damage_upper = 7 - base_attack_cooldown = 10 // One attack a second. - attack_sharp = TRUE - attack_sound = 'sound/weapons/bite.ogg' - attacktext = list("bitten") - - organ_names = /decl/mob_organ_names/fish - - meat_amount = 5 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat - - ai_holder_type = /datum/ai_holder/simple_mob/melee - - var/knockdown_chance = 15 - -/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(prob(knockdown_chance)) - L.Weaken(3) - L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) - -// Subtypes. - -// Won't wander away. -/mob/living/simple_mob/animal/space/carp/event - ai_holder_type = /datum/ai_holder/simple_mob/event - - -/mob/living/simple_mob/animal/space/carp/large - name = "elder carp" - desc = "An older, more matured carp. Few survive to this age due to their aggressiveness." - icon = 'icons/mob/64x32.dmi' - icon_state = "shark" - icon_living = "shark" - icon_dead = "shark_dead" - - maxHealth = 50 - health = 50 - movement_cooldown = 1 // Slower than the younger carp. - mob_size = MOB_LARGE - - pixel_x = -16 - default_pixel_x = -16 - icon_expected_width = 64 - icon_expected_height = 32 - - meat_amount = 7 - - -/mob/living/simple_mob/animal/space/carp/large/huge - name = "great white carp" - desc = "A very rare breed of carp- and a very aggressive one." - icon = 'icons/mob/64x64.dmi' - icon_dead = "megacarp_dead" - icon_living = "megacarp" - icon_state = "megacarp" - - maxHealth = 230 - health = 230 - movement_cooldown = 3 - - melee_damage_lower = 15 // About 20 DPS. - melee_damage_upper = 25 - - pixel_y = -16 - default_pixel_y = -16 - icon_expected_width = 64 - icon_expected_height = 64 - - meat_amount = 15 - - knockdown_chance = 15 - -/mob/living/simple_mob/animal/space/carp/large/huge/vorny - name = "great white carp" - desc = "A very rare breed of carp- and a very hungry one." - icon = 'icons/mob/64x64.dmi' - icon_dead = "megacarp_dead" - icon_living = "megacarp" - icon_state = "megacarp" - - maxHealth = 230 - health = 230 - movement_cooldown = 3 - - melee_damage_lower = 1 // Minimal damage to make the knockdown work. - melee_damage_upper = 1 - - pixel_y = -16 - default_pixel_y = -16 - icon_expected_width = 64 - icon_expected_height = 64 - - meat_amount = 15 - - knockdown_chance = 50 - ai_holder_type = /datum/ai_holder/simple_mob/vore - -/mob/living/simple_mob/animal/space/carp/large/huge/vorny/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "stomach" - B.desc = "You've been swallowed whole and alive by a massive white carp! The stomach around you is oppressively tight, squeezing and grinding wrinkled walls across your body, making it hard to make any movement at all. The chamber is flooded with fluids that completely overwhelm you." - B.mode_flags = DM_FLAG_THICKBELLY - B.belly_fullscreen = "yet_another_tumby" - B.digest_brute = 2 - B.digest_burn = 2 - B.digest_oxy = 1 - B.digestchance = 100 - B.absorbchance = 0 - B.escapechance = 3 - B.selective_preference = DM_DIGEST - B.escape_stun = 10 - -/mob/living/simple_mob/animal/space/carp/holographic - name = "holographic carp" - desc = "An obviously holographic, but still ferocious looking carp." - // Might be worth using a filter similar to AI holograms in the future. - icon = 'icons/mob/AI.dmi' - icon_state = "holo4" - icon_living = "holo4" - icon_dead = "holo4" - alpha = 127 - icon_gib = null - meat_amount = 0 - meat_type = null - - mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp. - -/mob/living/simple_mob/animal/space/carp/holographic/Initialize() - set_light(2) // Hologram lighting. - return ..() - -// Presumably the holodeck emag code requires this. -// Pass TRUE to make safe. Pass FALSE to make unsafe. -/mob/living/simple_mob/animal/space/carp/holographic/proc/set_safety(safe) - if(!isnull(get_AI_stance())) // Will return null if lacking an AI holder or a player is controlling it w/o autopilot var. - ai_holder.hostile = !safe // Inverted so safe = TRUE means hostility = FALSE. - ai_holder.forget_everything() // Reset state so it'll stop chewing on its target. - -// Called on death. -/mob/living/simple_mob/animal/space/carp/holographic/proc/derez() - visible_message(span("notice", "\The [src] fades away!")) - qdel(src) - -/mob/living/simple_mob/animal/space/carp/holographic/gib() - derez() // Holograms can't gib. - -/mob/living/simple_mob/animal/space/carp/holographic/death() - ..() - derez() - -// a slow-moving carp with the appearance of a sea mine and behaviour of a sea mine -/mob/living/simple_mob/animal/space/carp/puffer - name = "puffercarp" - desc = "A bloated, inflated carp covered in spines." - catalogue_data = list(/datum/category_item/catalogue/fauna/carp, /datum/category_item/catalogue/fauna/carp/puffer) - icon_state = "puffercarp" - icon_living = "puffercarp" - icon_dead = "puffercarp_dead" - icon_gib = "generic_gib" - movement_cooldown = 15 - var/ready_to_blow = TRUE - -/datum/category_item/catalogue/fauna/carp/puffer - name = "Voidborne Fauna - Space Carp: puffer variant" - desc = "An unusual subspecies of space carp with a novel defensive \ - and reproductive strategy - once the puffercarp is ready to spread spores \ - it begins to produce a highly volatile compound within its gas bladders, \ - which in addition to providing them with a means of propulsion through space \ - as per most space carp species, affords the puffercarp with a somewhat unique trait \ - - namely, that they are able to ignite and detonate their gas bladders \ - at will, and will do so aggressively when threatened. The bladders also tend to ignite \ - when struck by thermal or electrical discharges, or even sympathetic detonation from \ - other explosives - including other nearby puffercarp. As a result, most voidborne \ - predators have a tendency to keep clear, but even if this deterrent doesn't work the resulting \ - explosion serves to scatter their spores over a massive area - this improved seeding \ - strategy compared to regular carp results in the propagation of the species despite the \ - fact that it means each adult carp can only reproduce exactly once. \ -

                    \ - Due to their premature mortality it is extremely rare to see a puffercarp grow to any notable \ - size, often appearing to be somewhat stunted in growth compared to other subspecies, \ - their gas bloating being the only thing that brings them close to \ - the normal scale of an adult carp. " - value = CATALOGUER_REWARD_HARD //if you can hang around close enough to this thing without setting it off, you deserve it - -/mob/living/simple_mob/animal/space/carp/puffer/proc/kaboom() - if(ready_to_blow) - ready_to_blow = FALSE - gib() - var/turf/T = get_turf(src) - explosion(T, -1, -1, 4, 4) - - -/mob/living/simple_mob/animal/space/carp/puffer/apply_melee_effects() //it gets close enough to attack? EXPLODE - kaboom() - -/mob/living/simple_mob/animal/space/carp/puffer/adjustFireLoss(var/amount,var/include_robo) //you make it hot? EXPLODE - if(amount>0) - kaboom() - ..() - -/mob/living/simple_mob/animal/space/carp/puffer/ex_act() //explode? YOU BETTER BELIEVE THAT'S AN EXPLODE +// Space carp show up as a random event to wreck hapless people in space or near windows. +// They generally fit the archetype of 'fast but fragile'. +// This is compensated by being in groups (usually). + +/datum/category_item/catalogue/fauna/carp + name = "Voidborne Fauna - Space Carp" + desc = "A strange descendant of some form of voidborne life, they are the most \ + common naturally void-faring lifeform found in human territory. They've been named \ + 'Space Carp' by various groups of spacers due to resembling the fish from Earth.\ +

                    \ + Their lifecycle begins as a fungus-like growth, sometimes found on the walls of spacecraft \ + and space stations, before growing into a form which allows for independent travel. Even \ + when fully grown, they can sometimes be found to stow away on the hulls of spaceborne objects, \ + which might explain how they became widespread across many star systems.\ +

                    \ + Carp have a special gas bladder inside of them, which they utilize as a means of movement in \ + space by stategically releasing the gas to propel themselves in a process that resembles \ + thrusters on a spacecraft. The gas contained inside the carp also allows them \ + to float when inside an atmosphere. The carp might also spray 'spores' using a similar method.\ +

                    \ + They are hypercarnivorous to the point of cannibalism, consuming their own dead in order to \ + sustain themselves during hard times, which are rather frequent due to their prey being \ + vastly technologically advanced. For human habitats that are well secured, carp are generally \ + an annoyance. For those unable to adequately protect themselves, however, they can be \ + rather dangerous, especially if a mass migration of carp arrives." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/space/carp + name = "space carp" + desc = "A ferocious, fang-bearing creature that resembles a fish." + catalogue_data = list(/datum/category_item/catalogue/fauna/carp) + icon_state = "carp" + icon_living = "carp" + icon_dead = "carp_dead" + icon_gib = "carp_gib" + + faction = "carp" + maxHealth = 25 + health = 25 + movement_cooldown = -2 + hovering = TRUE + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + + melee_damage_lower = 7 // About 14 DPS. + melee_damage_upper = 7 + base_attack_cooldown = 10 // One attack a second. + attack_sharp = TRUE + attack_sound = 'sound/weapons/bite.ogg' + attacktext = list("bitten") + + organ_names = /decl/mob_organ_names/fish + + meat_amount = 5 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/knockdown_chance = 15 + +/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(knockdown_chance)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) + +// Subtypes. + +// Won't wander away. +/mob/living/simple_mob/animal/space/carp/event + ai_holder_type = /datum/ai_holder/simple_mob/event + + +/mob/living/simple_mob/animal/space/carp/large + name = "elder carp" + desc = "An older, more matured carp. Few survive to this age due to their aggressiveness." + icon = 'icons/mob/64x32.dmi' + icon_state = "shark" + icon_living = "shark" + icon_dead = "shark_dead" + + maxHealth = 50 + health = 50 + movement_cooldown = 1 // Slower than the younger carp. + mob_size = MOB_LARGE + + pixel_x = -16 + default_pixel_x = -16 + icon_expected_width = 64 + icon_expected_height = 32 + + meat_amount = 7 + + +/mob/living/simple_mob/animal/space/carp/large/huge + name = "great white carp" + desc = "A very rare breed of carp- and a very aggressive one." + icon = 'icons/mob/64x64.dmi' + icon_dead = "megacarp_dead" + icon_living = "megacarp" + icon_state = "megacarp" + + maxHealth = 230 + health = 230 + movement_cooldown = 3 + + melee_damage_lower = 15 // About 20 DPS. + melee_damage_upper = 25 + + pixel_y = -16 + default_pixel_y = -16 + icon_expected_width = 64 + icon_expected_height = 64 + + meat_amount = 15 + + knockdown_chance = 15 + +/mob/living/simple_mob/animal/space/carp/large/huge/vorny + name = "great white carp" + desc = "A very rare breed of carp- and a very hungry one." + icon = 'icons/mob/64x64.dmi' + icon_dead = "megacarp_dead" + icon_living = "megacarp" + icon_state = "megacarp" + + maxHealth = 230 + health = 230 + movement_cooldown = 3 + + melee_damage_lower = 1 // Minimal damage to make the knockdown work. + melee_damage_upper = 1 + + pixel_y = -16 + default_pixel_y = -16 + icon_expected_width = 64 + icon_expected_height = 64 + + meat_amount = 15 + + knockdown_chance = 50 + ai_holder_type = /datum/ai_holder/simple_mob/vore + +/mob/living/simple_mob/animal/space/carp/large/huge/vorny/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "You've been swallowed whole and alive by a massive white carp! The stomach around you is oppressively tight, squeezing and grinding wrinkled walls across your body, making it hard to make any movement at all. The chamber is flooded with fluids that completely overwhelm you." + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "yet_another_tumby" + B.digest_brute = 2 + B.digest_burn = 2 + B.digest_oxy = 1 + B.digestchance = 100 + B.absorbchance = 0 + B.escapechance = 3 + B.selective_preference = DM_DIGEST + B.escape_stun = 10 + +/mob/living/simple_mob/animal/space/carp/holographic + name = "holographic carp" + desc = "An obviously holographic, but still ferocious looking carp." + // Might be worth using a filter similar to AI holograms in the future. + icon = 'icons/mob/AI.dmi' + icon_state = "holo4" + icon_living = "holo4" + icon_dead = "holo4" + alpha = 127 + icon_gib = null + meat_amount = 0 + meat_type = null + + mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp. + +/mob/living/simple_mob/animal/space/carp/holographic/Initialize() + set_light(2) // Hologram lighting. + return ..() + +// Presumably the holodeck emag code requires this. +// Pass TRUE to make safe. Pass FALSE to make unsafe. +/mob/living/simple_mob/animal/space/carp/holographic/proc/set_safety(safe) + if(!isnull(get_AI_stance())) // Will return null if lacking an AI holder or a player is controlling it w/o autopilot var. + ai_holder.hostile = !safe // Inverted so safe = TRUE means hostility = FALSE. + ai_holder.forget_everything() // Reset state so it'll stop chewing on its target. + +// Called on death. +/mob/living/simple_mob/animal/space/carp/holographic/proc/derez() + visible_message(span("notice", "\The [src] fades away!")) + qdel(src) + +/mob/living/simple_mob/animal/space/carp/holographic/gib() + derez() // Holograms can't gib. + +/mob/living/simple_mob/animal/space/carp/holographic/death() + ..() + derez() + +// a slow-moving carp with the appearance of a sea mine and behaviour of a sea mine +/mob/living/simple_mob/animal/space/carp/puffer + name = "puffercarp" + desc = "A bloated, inflated carp covered in spines." + catalogue_data = list(/datum/category_item/catalogue/fauna/carp, /datum/category_item/catalogue/fauna/carp/puffer) + icon_state = "puffercarp" + icon_living = "puffercarp" + icon_dead = "puffercarp_dead" + icon_gib = "generic_gib" + movement_cooldown = 15 + var/ready_to_blow = TRUE + +/datum/category_item/catalogue/fauna/carp/puffer + name = "Voidborne Fauna - Space Carp: puffer variant" + desc = "An unusual subspecies of space carp with a novel defensive \ + and reproductive strategy - once the puffercarp is ready to spread spores \ + it begins to produce a highly volatile compound within its gas bladders, \ + which in addition to providing them with a means of propulsion through space \ + as per most space carp species, affords the puffercarp with a somewhat unique trait \ + - namely, that they are able to ignite and detonate their gas bladders \ + at will, and will do so aggressively when threatened. The bladders also tend to ignite \ + when struck by thermal or electrical discharges, or even sympathetic detonation from \ + other explosives - including other nearby puffercarp. As a result, most voidborne \ + predators have a tendency to keep clear, but even if this deterrent doesn't work the resulting \ + explosion serves to scatter their spores over a massive area - this improved seeding \ + strategy compared to regular carp results in the propagation of the species despite the \ + fact that it means each adult carp can only reproduce exactly once. \ +

                    \ + Due to their premature mortality it is extremely rare to see a puffercarp grow to any notable \ + size, often appearing to be somewhat stunted in growth compared to other subspecies, \ + their gas bloating being the only thing that brings them close to \ + the normal scale of an adult carp. " + value = CATALOGUER_REWARD_HARD //if you can hang around close enough to this thing without setting it off, you deserve it + +/mob/living/simple_mob/animal/space/carp/puffer/proc/kaboom() + if(ready_to_blow) + ready_to_blow = FALSE + gib() + var/turf/T = get_turf(src) + explosion(T, -1, -1, 4, 4) + + +/mob/living/simple_mob/animal/space/carp/puffer/apply_melee_effects() //it gets close enough to attack? EXPLODE + kaboom() + +/mob/living/simple_mob/animal/space/carp/puffer/adjustFireLoss(var/amount,var/include_robo) //you make it hot? EXPLODE + if(amount>0) + kaboom() + ..() + +/mob/living/simple_mob/animal/space/carp/puffer/ex_act() //explode? YOU BETTER BELIEVE THAT'S AN EXPLODE kaboom() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm index 410e2531b1f..2958cc58d8a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm @@ -81,7 +81,7 @@ TODO: Make them light up and heat the air when exposed to oxygen. vore_ignores_undigestable = 0 // they absorb rather than digest, you're going in either way vore_default_mode = DM_HOLD vore_digest_chance = 0 // Chance to switch to digest mode if resisted - vore_absorb_chance = 20 // BECOME A PART OF ME. + vore_absorb_chance = 100 // Will always start to absorb if the prey fails its generous escape chance vore_pounce_chance = 5 // Small chance to punish people who abuse their nomming behaviour to try and kite them forever with repeated melee attacks. vore_stomach_name = "internal chamber" vore_stomach_flavor = "You are squeezed into the tight embrace of the alien creature's warm and cozy insides." @@ -92,7 +92,8 @@ TODO: Make them light up and heat the air when exposed to oxygen. var/obj/belly/B = vore_selected B.name = "internal chamber" B.desc = "Having been too slow to disentangle yourself from the gaslamp's tentacles, the alien creature eventually winds enough of them around your body to lift you up off of the ground. Struggle as you might now, it is too late to deny the jellyfish-esque scavenger its lucky catch; inch by inch, the gaslamp tugs you upwards into its equivalent of a stomach, the transition between the cool-to-frigid atmosphere on the outside to its surprising internal heat something you can feel through any outer wear you possess. Minutes pass, soon resulting in the gentle creature's body sporting a rounded, bulging swell, an indistinct shadow shifting and twitching inside it as you squirm about. Be it to escape or simply to get settled, you might want to take care, however. The gaslamp's internal chamber is slick and squishy instead of overly oppressive, yet, each wave of warmth that pulses over you leaves you feeling weaker than the last..." - + B.escapechance = 40 //easy to squirm out of... + B.escapechance_absorbed = 5 //...but EXTREMELY clingy if you fail B.emote_lists[DM_HOLD] = list( "The gaslamp gently bobs up and down as it lazily drifts elsewhere, the movement hardly enough to disturb the shadowy, indistinct figure curled up within it: you.", "The fungal creature’s inner walls tenderly ripple and squeeze about your form for a few moments, squelching softly... until another wave of warmth pulses through the chamber.", diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm index 203bd791092..830964f05f8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose_vr.dm @@ -1,7 +1,7 @@ -/datum/category_item/catalogue/fauna/geese - name = "Planetary Fauna - Geese" - desc = "A goose. HONK. Not much to catalogue, they're exactly the same as their earth counterparts." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/animal/space/goose/virgo3b +/datum/category_item/catalogue/fauna/geese + name = "Planetary Fauna - Geese" + desc = "A goose. HONK. Not much to catalogue, they're exactly the same as their earth counterparts." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/animal/space/goose/virgo3b faction = "virgo3b" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm index b7f77e64104..a96df2f7ac8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm @@ -1,15 +1,15 @@ -// 'Space' mobs don't care about atmos (like carp) -/mob/living/simple_mob/animal/space - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - -// They can also, you know, move around, in space -/mob/living/simple_mob/animal/space/Process_Spacemove(var/check_drift = 0) +// 'Space' mobs don't care about atmos (like carp) +/mob/living/simple_mob/animal/space + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + +// They can also, you know, move around, in space +/mob/living/simple_mob/animal/space/Process_Spacemove(var/check_drift = 0) return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm b/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm index 456f3d166fc..9b62bc7da49 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/squirrel.dm @@ -40,7 +40,7 @@ attacktext = list("nipped", "squeaked at") friendly = list("nuzzles", "nibbles", "leans on") - ai_holder_type = /datum/ai_holder/simple_mob/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/edible has_langs = list(LANGUAGE_ANIMAL) say_list_type = /datum/say_list/squirrel diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm index 31ffb6ef1db..c735a93b2a6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm @@ -1,89 +1,89 @@ -// Blob simple_mobs generally get made from the blob random event. -// They're considered slimes for the purposes of attack bonuses from certain weapons. - -// Do not spawn, this is a base type. -/mob/living/simple_mob/blob - icon = 'icons/mob/blob.dmi' - pass_flags = PASSBLOB | PASSTABLE - faction = "blob" - - organ_names = /decl/mob_organ_names/blob - - heat_damage_per_tick = 0 - cold_damage_per_tick = 0 - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - - taser_kill = FALSE - - var/mob/observer/blob/overmind = null - var/obj/structure/blob/factory/factory = null - var/datum/blob_type/blob_type = null // Used for the blob core items, as they have no overmind mob. - - mob_class = MOB_CLASS_SLIME - ai_holder_type = /datum/ai_holder/simple_mob/melee - -/mob/living/simple_mob/blob/speech_bubble_appearance() - return "slime" - -/mob/living/simple_mob/blob/update_icons() - if(overmind) - color = overmind.blob_type.complementary_color - else if(blob_type) - color = blob_type.complementary_color - else - color = null - ..() - -/mob/living/simple_mob/blob/Destroy() - if(overmind) - overmind.blob_mobs -= src - if(blob_type) - blob_type = null - return ..() - -/mob/living/simple_mob/blob/blob_act(obj/structure/blob/B) - if(!overmind && B.overmind) - overmind = B.overmind - faction = B.overmind.blob_type.faction - update_icon() - - if(faction != B.faction && B.overmind) - adjustBruteLoss(rand(B.overmind.blob_type.damage_lower, B.overmind.blob_type.damage_upper)) - - else if(stat != DEAD && health < maxHealth) - adjustBruteLoss(-maxHealth*0.0125) - adjustFireLoss(-maxHealth*0.0125) - -/mob/living/simple_mob/blob/CanPass(atom/movable/mover, turf/target) - if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob. - return TRUE - return ..() - -/mob/living/simple_mob/blob/Process_Spacemove() - for(var/obj/structure/blob/B in range(1, src)) - return TRUE - return ..() - -/mob/living/simple_mob/blob/IIsAlly(mob/living/L) - var/ally = ..(L) - if(!ally) - var/list/items = L.get_all_held_items() - for(var/obj/item/I in items) - if(istype(I, /obj/item/weapon/blobcore_chunk)) - var/obj/item/weapon/blobcore_chunk/BC = I - if(!overmind || (BC.blob_type && overmind.blob_type.type == BC.blob_type.type) || BC.blob_type.faction == faction) - ally = TRUE - break - - return ally - -/decl/mob_organ_names/blob +// Blob simple_mobs generally get made from the blob random event. +// They're considered slimes for the purposes of attack bonuses from certain weapons. + +// Do not spawn, this is a base type. +/mob/living/simple_mob/blob + icon = 'icons/mob/blob.dmi' + pass_flags = PASSBLOB | PASSTABLE + faction = "blob" + + organ_names = /decl/mob_organ_names/blob + + heat_damage_per_tick = 0 + cold_damage_per_tick = 0 + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + taser_kill = FALSE + + var/mob/observer/blob/overmind = null + var/obj/structure/blob/factory/factory = null + var/datum/blob_type/blob_type = null // Used for the blob core items, as they have no overmind mob. + + mob_class = MOB_CLASS_SLIME + ai_holder_type = /datum/ai_holder/simple_mob/melee + +/mob/living/simple_mob/blob/speech_bubble_appearance() + return "slime" + +/mob/living/simple_mob/blob/update_icons() + if(overmind) + color = overmind.blob_type.complementary_color + else if(blob_type) + color = blob_type.complementary_color + else + color = null + ..() + +/mob/living/simple_mob/blob/Destroy() + if(overmind) + overmind.blob_mobs -= src + if(blob_type) + blob_type = null + return ..() + +/mob/living/simple_mob/blob/blob_act(obj/structure/blob/B) + if(!overmind && B.overmind) + overmind = B.overmind + faction = B.overmind.blob_type.faction + update_icon() + + if(faction != B.faction && B.overmind) + adjustBruteLoss(rand(B.overmind.blob_type.damage_lower, B.overmind.blob_type.damage_upper)) + + else if(stat != DEAD && health < maxHealth) + adjustBruteLoss(-maxHealth*0.0125) + adjustFireLoss(-maxHealth*0.0125) + +/mob/living/simple_mob/blob/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob. + return TRUE + return ..() + +/mob/living/simple_mob/blob/Process_Spacemove() + for(var/obj/structure/blob/B in range(1, src)) + return TRUE + return ..() + +/mob/living/simple_mob/blob/IIsAlly(mob/living/L) + var/ally = ..(L) + if(!ally) + var/list/items = L.get_all_held_items() + for(var/obj/item/I in items) + if(istype(I, /obj/item/weapon/blobcore_chunk)) + var/obj/item/weapon/blobcore_chunk/BC = I + if(!overmind || (BC.blob_type && overmind.blob_type.type == BC.blob_type.type) || BC.blob_type.faction == faction) + ally = TRUE + break + + return ally + +/decl/mob_organ_names/blob hit_zones = list("mass") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm index d0ed44099bb..7ac90f935f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm @@ -1,158 +1,158 @@ -// Spores are made from blob factories. -// They are very weak and expendable, but can overwhelm when a lot of them are together. -// When attacking, spores will hit harder if near other friendly spores. -// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. - -/mob/living/simple_mob/blob/spore - name = "blob spore" - desc = "A floating, fragile spore." - - icon_state = "blobpod" - icon_living = "blobpod" - glow_range = 3 - glow_intensity = 5 - layer = ABOVE_MOB_LAYER // Over the blob. - - health = 30 - maxHealth = 30 - melee_damage_lower = 2 - melee_damage_upper = 4 - movement_cooldown = -2 - hovering = TRUE - - attacktext = list("slammed into") - attack_sound = 'sound/effects/slime_squish.ogg' - say_list_type = /datum/say_list/spore - - organ_names = /decl/mob_organ_names/spore - - var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. - var/can_infest = FALSE - var/is_infesting = FALSE - -/datum/say_list/spore - emote_see = list("sways", "inflates briefly") - -/datum/say_list/infested - emote_see = list("shambles around", "twitches", "stares") - - -/mob/living/simple_mob/blob/spore/infesting - name = "infesting blob spore" - can_infest = TRUE - -/mob/living/simple_mob/blob/spore/weak - name = "fragile blob spore" - health = 15 - maxHealth = 15 - melee_damage_lower = 1 - melee_damage_upper = 2 - -/mob/living/simple_mob/blob/spore/Initialize(mapload, var/obj/structure/blob/factory/my_factory) - if(istype(my_factory)) - factory = my_factory - factory.spores += src - return ..() - -/mob/living/simple_mob/blob/spore/Destroy() - if(factory) - factory.spores -= src - factory = null - if(infested) - infested.forceMove(get_turf(src)) - visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) - infested = null - return ..() - -/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") - if(overmind) - overmind.blob_type.on_spore_death(src) - ..(gibbed, deathmessage) - qdel(src) - -/mob/living/simple_mob/blob/spore/update_icons() - ..() // This will cut our overlays. - - if(overmind) - color = overmind.blob_type.complementary_color - glow_color = color - glow_toggle = TRUE - else if(blob_type) - color = blob_type.complementary_color - glow_color = color - glow_toggle = TRUE - else - color = null - glow_color = null - glow_toggle = FALSE - - if(is_infesting) - icon = infested.icon - copy_overlays(infested) - var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") - if(overmind) - blob_head_overlay.color = overmind.blob_type.complementary_color - color = initial(color)//looks better. - add_overlay(blob_head_overlay, TRUE) - -/mob/living/simple_mob/blob/spore/handle_special() - ..() - if(can_infest && !is_infesting && isturf(loc)) - for(var/mob/living/carbon/human/H in view(src,1)) - if(H.stat != DEAD) // We want zombies. - continue - if(H.isSynthetic()) // Not philosophical zombies. - continue - infest(H) - break - - if(overmind) - overmind.blob_type.on_spore_lifetick(src) - - if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. - qdel(src) - -/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) - is_infesting = TRUE - if(H.wear_suit) - var/obj/item/clothing/suit/A = H.wear_suit - if(A.armor && A.armor["melee"]) - maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! - - maxHealth += 40 - health = maxHealth - name = "Infested [H.real_name]" // Not using the Z word. - desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." - melee_damage_lower += 8 // 10 total. - melee_damage_upper += 11 // 15 total. - attacktext = list("clawed") - - H.forceMove(src) - infested = H - - say_list = new /datum/say_list/infested() - - update_icons() - visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) - -/mob/living/simple_mob/blob/spore/GetIdCard() - if(infested) // If we've infested someone, use their ID. - return infested.GetIdCard() - -/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) - var/helpers = 0 - for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) - if(S == src) // Don't count ourselves. - continue - if(!IIsAlly(S)) // Only friendly spores make us stronger. - continue - // Friendly spores contribute 1/4th of their averaged attack power to our attack. - damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 - helpers++ - - if(helpers) - to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) - return damage_to_do - -/decl/mob_organ_names/spore - hit_zones = list("sporangium", "stolon", "sporangiophore") +// Spores are made from blob factories. +// They are very weak and expendable, but can overwhelm when a lot of them are together. +// When attacking, spores will hit harder if near other friendly spores. +// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. + +/mob/living/simple_mob/blob/spore + name = "blob spore" + desc = "A floating, fragile spore." + + icon_state = "blobpod" + icon_living = "blobpod" + glow_range = 3 + glow_intensity = 5 + layer = ABOVE_MOB_LAYER // Over the blob. + + health = 30 + maxHealth = 30 + melee_damage_lower = 2 + melee_damage_upper = 4 + movement_cooldown = -2 + hovering = TRUE + + attacktext = list("slammed into") + attack_sound = 'sound/effects/slime_squish.ogg' + say_list_type = /datum/say_list/spore + + organ_names = /decl/mob_organ_names/spore + + var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. + var/can_infest = FALSE + var/is_infesting = FALSE + +/datum/say_list/spore + emote_see = list("sways", "inflates briefly") + +/datum/say_list/infested + emote_see = list("shambles around", "twitches", "stares") + + +/mob/living/simple_mob/blob/spore/infesting + name = "infesting blob spore" + can_infest = TRUE + +/mob/living/simple_mob/blob/spore/weak + name = "fragile blob spore" + health = 15 + maxHealth = 15 + melee_damage_lower = 1 + melee_damage_upper = 2 + +/mob/living/simple_mob/blob/spore/Initialize(mapload, var/obj/structure/blob/factory/my_factory) + if(istype(my_factory)) + factory = my_factory + factory.spores += src + return ..() + +/mob/living/simple_mob/blob/spore/Destroy() + if(factory) + factory.spores -= src + factory = null + if(infested) + infested.forceMove(get_turf(src)) + visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) + infested = null + return ..() + +/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") + if(overmind) + overmind.blob_type.on_spore_death(src) + ..(gibbed, deathmessage) + qdel(src) + +/mob/living/simple_mob/blob/spore/update_icons() + ..() // This will cut our overlays. + + if(overmind) + color = overmind.blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else if(blob_type) + color = blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else + color = null + glow_color = null + glow_toggle = FALSE + + if(is_infesting) + icon = infested.icon + copy_overlays(infested) + var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") + if(overmind) + blob_head_overlay.color = overmind.blob_type.complementary_color + color = initial(color)//looks better. + add_overlay(blob_head_overlay, TRUE) + +/mob/living/simple_mob/blob/spore/handle_special() + ..() + if(can_infest && !is_infesting && isturf(loc)) + for(var/mob/living/carbon/human/H in view(src,1)) + if(H.stat != DEAD) // We want zombies. + continue + if(H.isSynthetic()) // Not philosophical zombies. + continue + infest(H) + break + + if(overmind) + overmind.blob_type.on_spore_lifetick(src) + + if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. + qdel(src) + +/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) + is_infesting = TRUE + if(H.wear_suit) + var/obj/item/clothing/suit/A = H.wear_suit + if(A.armor && A.armor["melee"]) + maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! + + maxHealth += 40 + health = maxHealth + name = "Infested [H.real_name]" // Not using the Z word. + desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." + melee_damage_lower += 8 // 10 total. + melee_damage_upper += 11 // 15 total. + attacktext = list("clawed") + + H.forceMove(src) + infested = H + + say_list = new /datum/say_list/infested() + + update_icons() + visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) + +/mob/living/simple_mob/blob/spore/GetIdCard() + if(infested) // If we've infested someone, use their ID. + return infested.GetIdCard() + +/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) + var/helpers = 0 + for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) + if(S == src) // Don't count ourselves. + continue + if(!IIsAlly(S)) // Only friendly spores make us stronger. + continue + // Friendly spores contribute 1/4th of their averaged attack power to our attack. + damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 + helpers++ + + if(helpers) + to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) + return damage_to_do + +/decl/mob_organ_names/spore + hit_zones = list("sporangium", "stolon", "sporangiophore") diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm index 84733bac395..f939e1d3c7a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm @@ -135,7 +135,7 @@ /mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/item/projectile/Proj) if(!Proj) return if(prob(35)) - visible_message("[src] blocks [Proj] with its shield!") + visible_message(span_red("[src] blocks [Proj] with its shield!")) if(Proj.firer) ai_holder.react_to_attack(Proj.firer) return diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_altevian.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_altevian.dm index b623e850b36..41b7941b685 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_altevian.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_altevian.dm @@ -1,6 +1,6 @@ /mob/living/simple_mob/humanoid/merc/altevian name = "altevian naval officer" - desc = "Altevian Naval Slicer, adorned in the top of the line Heartbreaker suit. Armed with a handheld cutter." + desc = "An Altevian Naval Slicer, adorned in the top of the line Heartbreaker suit. Armed with a handheld cutter." tt_desc = "E Rattus sapiens" icon = 'icons/mob/altevian_mercs_vr.dmi' icon_state = "merc_melee_cutter" @@ -50,7 +50,7 @@ stand_down_sound = 'sound/weapons/TargetOff.ogg' /mob/living/simple_mob/humanoid/merc/altevian/sapper - desc = "Altevian Naval Sapper, adorned in the top of the line Heartbreaker suit. Armed with a giant fokken wrench." + desc = "An Altevian Naval Sapper, adorned in the top of the line Heartbreaker suit. Armed with a giant fokken wrench." icon_state = "merc_melee_wrench" icon_living = "merc_melee_wrench" @@ -62,10 +62,10 @@ attacktext = list("whacked", "slammed", "bashed", "clonked", "bonked") attack_sound = 'sound/weapons/smash.ogg' - loot_list = list(/obj/item/weapon/melee/altevian_wrench = 100) + loot_list = list(/obj/item/weapon/tool/transforming/altevian = 100) /mob/living/simple_mob/humanoid/merc/altevian/ranged - desc = "Altevian Naval Salvage Guard, adorned in the top of the line Heartbreaker suit. Armed with a small energy gun." + desc = "An Altevian Naval Salvage Guard, adorned in the top of the line Heartbreaker suit. Armed with a small energy gun." icon_state = "merc_gun_smol" icon_living = "merc_gun_smol" @@ -86,7 +86,7 @@ ai_holder_type = /datum/ai_holder/simple_mob/merc/ranged /mob/living/simple_mob/humanoid/merc/altevian/ranged/strong - desc = "Altevian Naval Salvage Shield, adorned in the top of the line Heartbreaker suit. Armed with a large energy gun." + desc = "An Altevian Naval Salvage Shield, adorned in the top of the line Heartbreaker suit. Armed with a large energy gun." icon_state = "merc_gun_big" icon_living = "merc_gun_big" @@ -101,7 +101,7 @@ projectilesound = 'sound/weapons/Laser.ogg' /mob/living/simple_mob/humanoid/merc/altevian/ranged/ballistic - desc = "Altevian Naval Shipbreaker, adorned in the top of the line Heartbreaker suit. Armed with a bolter gun." + desc = "An Altevian Naval Shipbreaker, adorned in the top of the line Heartbreaker suit. Armed with a bolter gun." icon_state = "merc_gun_ballistic" icon_living = "merc_gun_ballistic" @@ -128,4 +128,4 @@ faction = "neutral" /mob/living/simple_mob/humanoid/merc/altevian/ranged/ballistic/neutral - faction = "neutral" \ No newline at end of file + faction = "neutral" diff --git a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm index de3fc7980e1..cbc9293f9e5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm +++ b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm @@ -1,116 +1,116 @@ -// Illusion type mobs pretend to be other things visually, and generally cannot be harmed as they're not 'real'. - -/mob/living/simple_mob/illusion - name = "illusion" - desc = "If you can read me, the game broke. Please report this to a coder." - - resistance = 1000 // Holograms are tough. - heat_resist = 1 - cold_resist = 1 - shock_resist = 1 - poison_resist = 1 - - movement_cooldown = -2 - mob_bump_flag = 0 // If the illusion can't be swapped it will be obvious. - - response_help = "pushes a hand through" - response_disarm = "tried to disarm" - response_harm = "tried to punch" - - mob_class = MOB_CLASS_ILLUSION - - ai_holder_type = /datum/ai_holder/simple_mob/inert/astar // Gets controlled manually by technomancers/admins, with AI pathfinding assistance. - - var/atom/movable/copying = null // The thing we're trying to look like. - var/realistic = FALSE // If true, things like bullets and weapons will hit it, to be a bit more convincing from a distance. - -/mob/living/simple_mob/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance(). - return - -/mob/living/simple_mob/illusion/proc/copy_appearance(atom/movable/thing_to_copy) - if(!thing_to_copy) - return FALSE - appearance = thing_to_copy.appearance - copying = thing_to_copy - density = thing_to_copy.density // So you can't bump into objects that aren't supposed to be dense. - catalogue_data = thing_to_copy.get_catalogue_data() - catalogue_delay = thing_to_copy.catalogue_delay - return TRUE - -// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but -// this is to prevent easy checks from the opposing force. -/mob/living/simple_mob/illusion/examine(mob/user) - if(copying) - return copying.examine(user) - else - return list("???") - -/mob/living/simple_mob/illusion/bullet_act(obj/item/projectile/P) - if(!P) - return - - if(realistic) - return ..() - - return PROJECTILE_FORCE_MISS - -/mob/living/simple_mob/illusion/attack_hand(mob/living/carbon/human/M) - if(!realistic) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(span("warning", "\The [M]'s hand goes through \the [src]!")) - return - else - switch(M.a_intent) - if(I_HELP) - var/datum/gender/T = gender_datums[src.get_visible_gender()] - M.visible_message( - span("notice", "\The [M] hugs [src] to make [T.him] feel better!"), \ - span("notice", "You hug [src] to make [T.him] feel better!") - ) // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - if(I_DISARM) - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(span("danger", "\The [M] attempted to disarm [src]!")) - M.do_attack_animation(src) - - if(I_GRAB) - ..() - - if(I_HURT) - adjustBruteLoss(harm_intent_damage) - M.visible_message(span("danger", "\The [M] [response_harm] \the [src]")) - M.do_attack_animation(src) - -/mob/living/simple_mob/illusion/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone) - if(realistic) - return ..() - - playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message(span("warning", "\The [user]'s [I] goes through \the [src]!")) - return FALSE - -/mob/living/simple_mob/illusion/ex_act() - return - -// Try to have the same tooltip, or else it becomes really obvious which one is fake. -/mob/living/simple_mob/illusion/get_nametag_name(mob/user) - if(copying) - return copying.get_nametag_name(user) - -/mob/living/simple_mob/illusion/get_nametag_desc(mob/user) - if(copying) - return copying.get_nametag_desc(user) - -// Cataloguer stuff. I don't think this will actually come up but better safe than sorry. -/mob/living/simple_mob/illusion/get_catalogue_data() - if(copying) - return copying.get_catalogue_data() - -/mob/living/simple_mob/illusion/can_catalogue() - if(copying) - return copying.can_catalogue() - -/mob/living/simple_mob/illusion/get_catalogue_delay() - if(copying) - return copying.get_catalogue_delay() +// Illusion type mobs pretend to be other things visually, and generally cannot be harmed as they're not 'real'. + +/mob/living/simple_mob/illusion + name = "illusion" + desc = "If you can read me, the game broke. Please report this to a coder." + + resistance = 1000 // Holograms are tough. + heat_resist = 1 + cold_resist = 1 + shock_resist = 1 + poison_resist = 1 + + movement_cooldown = -2 + mob_bump_flag = 0 // If the illusion can't be swapped it will be obvious. + + response_help = "pushes a hand through" + response_disarm = "tried to disarm" + response_harm = "tried to punch" + + mob_class = MOB_CLASS_ILLUSION + + ai_holder_type = /datum/ai_holder/simple_mob/inert/astar // Gets controlled manually by technomancers/admins, with AI pathfinding assistance. + + var/atom/movable/copying = null // The thing we're trying to look like. + var/realistic = FALSE // If true, things like bullets and weapons will hit it, to be a bit more convincing from a distance. + +/mob/living/simple_mob/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance(). + return + +/mob/living/simple_mob/illusion/proc/copy_appearance(atom/movable/thing_to_copy) + if(!thing_to_copy) + return FALSE + appearance = thing_to_copy.appearance + copying = thing_to_copy + density = thing_to_copy.density // So you can't bump into objects that aren't supposed to be dense. + catalogue_data = thing_to_copy.get_catalogue_data() + catalogue_delay = thing_to_copy.catalogue_delay + return TRUE + +// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but +// this is to prevent easy checks from the opposing force. +/mob/living/simple_mob/illusion/examine(mob/user) + if(copying) + return copying.examine(user) + else + return list("???") + +/mob/living/simple_mob/illusion/bullet_act(obj/item/projectile/P) + if(!P) + return + + if(realistic) + return ..() + + return PROJECTILE_FORCE_MISS + +/mob/living/simple_mob/illusion/attack_hand(mob/living/carbon/human/M) + if(!realistic) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("warning", "\The [M]'s hand goes through \the [src]!")) + return + else + switch(M.a_intent) + if(I_HELP) + var/datum/gender/T = gender_datums[src.get_visible_gender()] + M.visible_message( + span("notice", "\The [M] hugs [src] to make [T.him] feel better!"), \ + span("notice", "You hug [src] to make [T.him] feel better!") + ) // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if(I_DISARM) + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("danger", "\The [M] attempted to disarm [src]!")) + M.do_attack_animation(src) + + if(I_GRAB) + ..() + + if(I_HURT) + adjustBruteLoss(harm_intent_damage) + M.visible_message(span("danger", "\The [M] [response_harm] \the [src]")) + M.do_attack_animation(src) + +/mob/living/simple_mob/illusion/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone) + if(realistic) + return ..() + + playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("warning", "\The [user]'s [I] goes through \the [src]!")) + return FALSE + +/mob/living/simple_mob/illusion/ex_act() + return + +// Try to have the same tooltip, or else it becomes really obvious which one is fake. +/mob/living/simple_mob/illusion/get_nametag_name(mob/user) + if(copying) + return copying.get_nametag_name(user) + +/mob/living/simple_mob/illusion/get_nametag_desc(mob/user) + if(copying) + return copying.get_nametag_desc(user) + +// Cataloguer stuff. I don't think this will actually come up but better safe than sorry. +/mob/living/simple_mob/illusion/get_catalogue_data() + if(copying) + return copying.get_catalogue_data() + +/mob/living/simple_mob/illusion/can_catalogue() + if(copying) + return copying.can_catalogue() + +/mob/living/simple_mob/illusion/get_catalogue_delay() + if(copying) + return copying.get_catalogue_delay() diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm index e001972ae84..09c45811258 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm @@ -1,71 +1,71 @@ -/* - A corrupted maintenance drone, produced from what seems like a bad factory. - They also tend to dodge while in melee range. - Code "borrowed" from viscerator drones. <3 -*/ - -/datum/category_item/catalogue/technology/drone/corrupt_maint_drone - name = "Drone - Corrupted Maintenance Drone" - desc = "This drone appears to be a station maintenance drone, produced by some sort of corrupt fab, \ - which has caused it to become corrupt and attack anything nearby, except spiders and such, oddy. \ - If one is found, a swarm of others are not too far away.\ -

                    \ - The drone struggles to harm large targets, due to it's small size, yet it possesses a welder, which allows \ - it to **ERROR** inject it's targets, in addition to the small slashes from it's skittering claws. \ - The simplistic AI inside attempts to attack and then run, as it is aware that it is fairly weak, \ - using evasive tactics to avoid harm." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/corrupt_maint_drone - name = "Corrupt Maintenance Drone" - desc = "A small, normal-looking drone. It looks like one you'd find on station, except... IT'S COMING AT YOU!" - catalogue_data = list(/datum/category_item/catalogue/technology/drone/corrupt_maint_drone) - - icon = 'icons/mob/robots_vr.dmi' - icon_state = "corrupt-repairbot" - icon_living = "corrupt-repairbot" - hovering = FALSE // Can trigger landmines. - - faction = "underdark" - maxHealth = 25 - health = 25 - movement_cooldown = -1 - movement_sound = 'sound/effects/servostep.ogg' - - pass_flags = PASSTABLE - mob_swap_flags = 0 - mob_push_flags = 0 - - melee_damage_lower = 6 // Approx 12 DPS. - melee_damage_upper = 6 - base_attack_cooldown = 2.5 // Four attacks per second. - attack_sharp = TRUE - attack_edge = 1 - attack_sound = 'sound/weapons/bladeslice.ogg' - attacktext = list("cut", "sliced") - - var/poison_type = "welder fuel" // The reagent that gets injected when it attacks. - var/poison_chance = 35 // Chance for injection to occur. - var/poison_per_bite = 5 // Amount added per injection. - - ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive - - -/mob/living/simple_mob/mechanical/corrupt_maint_drone/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(L.reagents) - var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - if(L.can_inject(src, null, target_zone)) - inject_poison(L, target_zone) - -// Does actual poison injection, after all checks passed. -/mob/living/simple_mob/mechanical/corrupt_maint_drone/proc/inject_poison(mob/living/L, target_zone) - if(prob(poison_chance)) - to_chat(L, "Something burns in your veins.") - L.reagents.add_reagent(poison_type, poison_per_bite) - - -/mob/living/simple_mob/mechanical/corrupt_maint_drone/death() - ..(null,"is smashed into pieces!") - qdel(src) +/* + A corrupted maintenance drone, produced from what seems like a bad factory. + They also tend to dodge while in melee range. + Code "borrowed" from viscerator drones. <3 +*/ + +/datum/category_item/catalogue/technology/drone/corrupt_maint_drone + name = "Drone - Corrupted Maintenance Drone" + desc = "This drone appears to be a station maintenance drone, produced by some sort of corrupt fab, \ + which has caused it to become corrupt and attack anything nearby, except spiders and such, oddy. \ + If one is found, a swarm of others are not too far away.\ +

                    \ + The drone struggles to harm large targets, due to it's small size, yet it possesses a welder, which allows \ + it to **ERROR** inject it's targets, in addition to the small slashes from it's skittering claws. \ + The simplistic AI inside attempts to attack and then run, as it is aware that it is fairly weak, \ + using evasive tactics to avoid harm." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/corrupt_maint_drone + name = "Corrupt Maintenance Drone" + desc = "A small, normal-looking drone. It looks like one you'd find on station, except... IT'S COMING AT YOU!" + catalogue_data = list(/datum/category_item/catalogue/technology/drone/corrupt_maint_drone) + + icon = 'icons/mob/robots_vr.dmi' + icon_state = "corrupt-repairbot" + icon_living = "corrupt-repairbot" + hovering = FALSE // Can trigger landmines. + + faction = "underdark" + maxHealth = 25 + health = 25 + movement_cooldown = -1 + movement_sound = 'sound/effects/servostep.ogg' + + pass_flags = PASSTABLE + mob_swap_flags = 0 + mob_push_flags = 0 + + melee_damage_lower = 6 // Approx 12 DPS. + melee_damage_upper = 6 + base_attack_cooldown = 2.5 // Four attacks per second. + attack_sharp = TRUE + attack_edge = 1 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("cut", "sliced") + + var/poison_type = "welder fuel" // The reagent that gets injected when it attacks. + var/poison_chance = 35 // Chance for injection to occur. + var/poison_per_bite = 5 // Amount added per injection. + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + + +/mob/living/simple_mob/mechanical/corrupt_maint_drone/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/mechanical/corrupt_maint_drone/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "Something burns in your veins.") + L.reagents.add_reagent(poison_type, poison_per_bite) + + +/mob/living/simple_mob/mechanical/corrupt_maint_drone/death() + ..(null,"is smashed into pieces!") + qdel(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm index 5ea69697d65..fbfedf1cd79 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm @@ -1,70 +1,70 @@ - -/datum/category_item/catalogue/technology/drone/infectionbot - name = "Drone - Injection Drone" - desc = "A strange and aged drone, this model appears to be made for gathering of genetic samples,\ - sacrificing both power and durability for storage space and advanced scanners.\ - The drone has clear dents and scratches all over it's casing bulging inwards in multiple areas,\ - some even penetrated showing the advanced and headache inducing parts inside.\ -

                    \ - This model in particular has a collection of hostile and spinetingeling parts on the underside,\ - small advanced anti-gravity generators located between giant syringes made for injecting and extracting all manners of fluids\ - and samples of soil or other biological matter.\ - Most interesting is the syringe it uses after taking a sample of any creature, the dry, flakey substance\ - it injects is rotten and expired toxins, seemingly once intended to heal damage from samples taken.\ - The container housing the substance as well as the fabricator showing great blunt trauma as well as environmental damage,\ - neither part salvageable but still operational, outputting near 0% efficiency making it near impossible to refill the housing unit this century.\ -

                    \ - The drone's frame extremily light weight but robust, unbendable by hand, is barren of any markings or ID,\ - no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." - value = CATALOGUER_REWARD_MEDIUM - -/mob/living/simple_mob/mechanical/infectionbot - name = "Strange robot" - desc = "You get the feeling you should run." - icon = 'icons/mob/vore.dmi' - icon_state = "vagrant" - icon_living = "vagrant" - icon_dead = "vagrant" - icon_gib = "vagrant" - - maxHealth = 65 - health = 40 - movement_cooldown = 1 - - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - faction = "vagrant" - harm_intent_damage = 3 - melee_damage_lower = 6 - melee_damage_upper = 9 - light_color = "#8a0707" - attacktext = "drugged" - attack_sound = 'sound/weapons/bite.ogg' - - ai_holder_type = /datum/ai_holder/simple_mob/melee - say_list_type = /datum/say_list/disbot - - var/poison_chance = 100 - var/poison_per_bite = 10 - var/poison_type = "expired_medicine" - -/datum/say_list/disbot - speak = list("ATTEMPTING TO CONTACT A.R.K, ATTEMPT 1e26+3","DIRT SAMPLE COLLECTED, DIRT QUOTA 124871/155 CONFIRMED.") - emote_see = list("scans the dirt around it","beeps as it scans a rock nearby") - say_maybe_target = list("BIOLOGICAL TRACES FOUND, ATTEMTPTING TO LOCATE SOURCE.","TRACE SOURCES FOUND, POWERING SCANNERS.",) - say_got_target = list("LIFEFORM LOCATED, ATTEMPTING TO COLLECT SAMPLE","CREATURE SPOTTED, PHEROMONE GENERATORS DAMAGED, ATTEMPTING TO COLLECT GENETIC SAMPLE.") - -/mob/living/simple_mob/mechanical/infectionbot/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(L.reagents) - var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - if(L.can_inject(src, null, target_zone)) - inject_poison(L, target_zone) - -// Does actual poison injection, after all checks passed. -/mob/living/simple_mob/mechanical/infectionbot/proc/inject_poison(mob/living/L, target_zone) - if(prob(poison_chance)) - to_chat(L, "You feel a tiny prick.") - L.reagents.add_reagent(poison_type, poison_per_bite) + +/datum/category_item/catalogue/technology/drone/infectionbot + name = "Drone - Injection Drone" + desc = "A strange and aged drone, this model appears to be made for gathering of genetic samples,\ + sacrificing both power and durability for storage space and advanced scanners.\ + The drone has clear dents and scratches all over it's casing bulging inwards in multiple areas,\ + some even penetrated showing the advanced and headache inducing parts inside.\ +

                    \ + This model in particular has a collection of hostile and spinetingeling parts on the underside,\ + small advanced anti-gravity generators located between giant syringes made for injecting and extracting all manners of fluids\ + and samples of soil or other biological matter.\ + Most interesting is the syringe it uses after taking a sample of any creature, the dry, flakey substance\ + it injects is rotten and expired toxins, seemingly once intended to heal damage from samples taken.\ + The container housing the substance as well as the fabricator showing great blunt trauma as well as environmental damage,\ + neither part salvageable but still operational, outputting near 0% efficiency making it near impossible to refill the housing unit this century.\ +

                    \ + The drone's frame extremily light weight but robust, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM + +/mob/living/simple_mob/mechanical/infectionbot + name = "Strange robot" + desc = "You get the feeling you should run." + icon = 'icons/mob/vore.dmi' + icon_state = "vagrant" + icon_living = "vagrant" + icon_dead = "vagrant" + icon_gib = "vagrant" + + maxHealth = 65 + health = 40 + movement_cooldown = 1 + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + faction = "vagrant" + harm_intent_damage = 3 + melee_damage_lower = 6 + melee_damage_upper = 9 + light_color = "#8a0707" + attacktext = "drugged" + attack_sound = 'sound/weapons/bite.ogg' + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/disbot + + var/poison_chance = 100 + var/poison_per_bite = 10 + var/poison_type = "expired_medicine" + +/datum/say_list/disbot + speak = list("ATTEMPTING TO CONTACT A.R.K, ATTEMPT 1e26+3","DIRT SAMPLE COLLECTED, DIRT QUOTA 124871/155 CONFIRMED.") + emote_see = list("scans the dirt around it","beeps as it scans a rock nearby") + say_maybe_target = list("BIOLOGICAL TRACES FOUND, ATTEMTPTING TO LOCATE SOURCE.","TRACE SOURCES FOUND, POWERING SCANNERS.",) + say_got_target = list("LIFEFORM LOCATED, ATTEMPTING TO COLLECT SAMPLE","CREATURE SPOTTED, PHEROMONE GENERATORS DAMAGED, ATTEMPTING TO COLLECT GENETIC SAMPLE.") + +/mob/living/simple_mob/mechanical/infectionbot/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/mechanical/infectionbot/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm index 10d195f281d..a0ceb1b1af0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm @@ -1,158 +1,158 @@ -// The GOLEM is a spell-flinging synthetic. - -/mob/living/simple_mob/mechanical/technomancer_golem - name = "unknown synthetic" - desc = "A rather unusual looking synthetic." - icon = 'icons/mob/mob.dmi' - icon_state = "golem" - health = 300 - maxHealth = 300 - - faction = "golem" - - response_help = "pets" - response_disarm = "pushes away" - response_harm = "punches" - harm_intent_damage = 3 - friendly = "hugs" - - organ_names = /decl/mob_organ_names/golem - - melee_damage_lower = 30 // It has a built in esword. - melee_damage_upper = 30 - attack_armor_pen = 20 - attack_sound = 'sound/weapons/blade1.ogg' - attacktext = list("slashed") - melee_attack_delay = 0.5 SECONDS // Even has custom attack animations. - ranged_attack_delay = 0.5 SECONDS - special_attack_delay = 1 SECOND - - special_attack_min_range = 0 - special_attack_max_range = 7 - - ai_holder_type = /datum/ai_holder/simple_mob/melee - - var/obj/item/weapon/technomancer_core/golem/core = null - var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells - var/mob/living/master = null - var/casting = FALSE // Used to ensure the correct animation is played. Testing if a spell exists won't always work as some spells delete themselves upon use. - - var/list/known_spells = list( - "beam" = /obj/item/weapon/spell/projectile/beam, - "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning, - "force missile" = /obj/item/weapon/spell/projectile/force_missile, - "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt, - "lightning" = /obj/item/weapon/spell/projectile/lightning, - "blink" = /obj/item/weapon/spell/blink, - "dispel" = /obj/item/weapon/spell/dispel, - "oxygenate" = /obj/item/weapon/spell/oxygenate, - "mend life" = /obj/item/weapon/spell/modifier/mend_life, - "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic, - "mend organs" = /obj/item/weapon/spell/mend_organs, - "purify" = /obj/item/weapon/spell/modifier/purify, - "resurrect" = /obj/item/weapon/spell/resurrect, - "passwall" = /obj/item/weapon/spell/passwall, - "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles, - "corona" = /obj/item/weapon/spell/modifier/corona, - "haste" = /obj/item/weapon/spell/modifier/haste - ) - -/mob/living/simple_mob/mechanical/technomancer_golem/Initialize() - core = new(src) - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/Destroy() - qdel(core) - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/unref_spell() - active_spell = null - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/death() - ..() - visible_message("\The [src] disintegrates!") - new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - qdel(src) - -/mob/living/simple_mob/mechanical/technomancer_golem/place_spell_in_hand(var/path) - if(!path || !ispath(path)) - return FALSE - if(active_spell) - qdel(active_spell) - - active_spell = new path(src) - -/mob/living/simple_mob/mechanical/technomancer_golem/verb/test_giving_spells() - var/choice = tgui_input_list(usr, "What spell?", "Give spell", known_spells) - if(choice) - place_spell_in_hand(known_spells[choice]) - else - qdel(active_spell) - -/mob/living/simple_mob/mechanical/technomancer_golem/get_technomancer_core() - return core - -/mob/living/simple_mob/mechanical/technomancer_golem/can_special_attack(atom/A) - if(active_spell) // Don't bother checking everything else if no spell is ready. - return ..() - return FALSE - -/mob/living/simple_mob/mechanical/technomancer_golem/should_special_attack(atom/A) - return instability < 50 // Don't kill ourselves by casting everything. - - -/mob/living/simple_mob/mechanical/technomancer_golem/do_special_attack(atom/A) - var/proximity = Adjacent(A) - if(active_spell) - if(proximity && active_spell.cast_methods & CAST_MELEE) // Use melee method if available and close enough. - return active_spell.on_melee_cast(A, src) - else if(active_spell.cast_methods & CAST_RANGED) // Otherwise use ranged if possible. Will also work for point-blank range. - return active_spell.on_ranged_cast(A, src) - return ..() - -/mob/living/simple_mob/mechanical/technomancer_golem/melee_pre_animation(atom/A) - if(active_spell && active_spell.cast_methods & CAST_MELEE|CAST_RANGED) // If they're trying to melee-cast a spell, use the special animation instead. - special_pre_animation(A) - return - - flick("golem_pre_melee", src) // To force the animation to restart. - icon_living = "golem_pre_melee" // The animation will hold after this point until melee_post_animation() gets called. - icon_state = "golem_pre_melee" - setClickCooldown(2) - -/mob/living/simple_mob/mechanical/technomancer_golem/melee_post_animation(atom/A) - if(casting) // Some spells delete themselves when used, so we use a different variable set earlier instead. - special_post_animation(A) - return - - flick("golem_post_melee", src) - icon_living = "golem" - icon_state = "golem" - setClickCooldown(6) - -/mob/living/simple_mob/mechanical/technomancer_golem/ranged_pre_animation(atom/A) - flick("golem_pre_ranged", src) - icon_living = "golem_pre_ranged" - icon_state = "golem_pre_ranged" - setClickCooldown(5) - -/mob/living/simple_mob/mechanical/technomancer_golem/ranged_post_animation(atom/A) - flick("golem_post_ranged", src) - icon_living = "golem" - icon_state = "golem" - setClickCooldown(5) - -/mob/living/simple_mob/mechanical/technomancer_golem/special_pre_animation(atom/A) - casting = TRUE - ranged_pre_animation(A) // Both have the same animation. - -/mob/living/simple_mob/mechanical/technomancer_golem/special_post_animation(atom/A) - casting = FALSE - ranged_post_animation(A) - -/decl/mob_organ_names/golem - hit_zones = list("helmet", "cuirass", "left tasset", "right tasset", "left gauntlet", "right gauntlet", "weapon") +// The GOLEM is a spell-flinging synthetic. + +/mob/living/simple_mob/mechanical/technomancer_golem + name = "unknown synthetic" + desc = "A rather unusual looking synthetic." + icon = 'icons/mob/mob.dmi' + icon_state = "golem" + health = 300 + maxHealth = 300 + + faction = "golem" + + response_help = "pets" + response_disarm = "pushes away" + response_harm = "punches" + harm_intent_damage = 3 + friendly = "hugs" + + organ_names = /decl/mob_organ_names/golem + + melee_damage_lower = 30 // It has a built in esword. + melee_damage_upper = 30 + attack_armor_pen = 20 + attack_sound = 'sound/weapons/blade1.ogg' + attacktext = list("slashed") + melee_attack_delay = 0.5 SECONDS // Even has custom attack animations. + ranged_attack_delay = 0.5 SECONDS + special_attack_delay = 1 SECOND + + special_attack_min_range = 0 + special_attack_max_range = 7 + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/obj/item/weapon/technomancer_core/golem/core = null + var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells + var/mob/living/master = null + var/casting = FALSE // Used to ensure the correct animation is played. Testing if a spell exists won't always work as some spells delete themselves upon use. + + var/list/known_spells = list( + "beam" = /obj/item/weapon/spell/projectile/beam, + "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning, + "force missile" = /obj/item/weapon/spell/projectile/force_missile, + "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt, + "lightning" = /obj/item/weapon/spell/projectile/lightning, + "blink" = /obj/item/weapon/spell/blink, + "dispel" = /obj/item/weapon/spell/dispel, + "oxygenate" = /obj/item/weapon/spell/oxygenate, + "mend life" = /obj/item/weapon/spell/modifier/mend_life, + "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic, + "mend organs" = /obj/item/weapon/spell/mend_organs, + "purify" = /obj/item/weapon/spell/modifier/purify, + "resurrect" = /obj/item/weapon/spell/resurrect, + "passwall" = /obj/item/weapon/spell/passwall, + "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles, + "corona" = /obj/item/weapon/spell/modifier/corona, + "haste" = /obj/item/weapon/spell/modifier/haste + ) + +/mob/living/simple_mob/mechanical/technomancer_golem/Initialize() + core = new(src) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/Destroy() + qdel(core) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/unref_spell() + active_spell = null + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/death() + ..() + visible_message("\The [src] disintegrates!") + new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + +/mob/living/simple_mob/mechanical/technomancer_golem/place_spell_in_hand(var/path) + if(!path || !ispath(path)) + return FALSE + if(active_spell) + qdel(active_spell) + + active_spell = new path(src) + +/mob/living/simple_mob/mechanical/technomancer_golem/verb/test_giving_spells() + var/choice = tgui_input_list(usr, "What spell?", "Give spell", known_spells) + if(choice) + place_spell_in_hand(known_spells[choice]) + else + qdel(active_spell) + +/mob/living/simple_mob/mechanical/technomancer_golem/get_technomancer_core() + return core + +/mob/living/simple_mob/mechanical/technomancer_golem/can_special_attack(atom/A) + if(active_spell) // Don't bother checking everything else if no spell is ready. + return ..() + return FALSE + +/mob/living/simple_mob/mechanical/technomancer_golem/should_special_attack(atom/A) + return instability < 50 // Don't kill ourselves by casting everything. + + +/mob/living/simple_mob/mechanical/technomancer_golem/do_special_attack(atom/A) + var/proximity = Adjacent(A) + if(active_spell) + if(proximity && active_spell.cast_methods & CAST_MELEE) // Use melee method if available and close enough. + return active_spell.on_melee_cast(A, src) + else if(active_spell.cast_methods & CAST_RANGED) // Otherwise use ranged if possible. Will also work for point-blank range. + return active_spell.on_ranged_cast(A, src) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/melee_pre_animation(atom/A) + if(active_spell && active_spell.cast_methods & CAST_MELEE|CAST_RANGED) // If they're trying to melee-cast a spell, use the special animation instead. + special_pre_animation(A) + return + + flick("golem_pre_melee", src) // To force the animation to restart. + icon_living = "golem_pre_melee" // The animation will hold after this point until melee_post_animation() gets called. + icon_state = "golem_pre_melee" + setClickCooldown(2) + +/mob/living/simple_mob/mechanical/technomancer_golem/melee_post_animation(atom/A) + if(casting) // Some spells delete themselves when used, so we use a different variable set earlier instead. + special_post_animation(A) + return + + flick("golem_post_melee", src) + icon_living = "golem" + icon_state = "golem" + setClickCooldown(6) + +/mob/living/simple_mob/mechanical/technomancer_golem/ranged_pre_animation(atom/A) + flick("golem_pre_ranged", src) + icon_living = "golem_pre_ranged" + icon_state = "golem_pre_ranged" + setClickCooldown(5) + +/mob/living/simple_mob/mechanical/technomancer_golem/ranged_post_animation(atom/A) + flick("golem_post_ranged", src) + icon_living = "golem" + icon_state = "golem" + setClickCooldown(5) + +/mob/living/simple_mob/mechanical/technomancer_golem/special_pre_animation(atom/A) + casting = TRUE + ranged_pre_animation(A) // Both have the same animation. + +/mob/living/simple_mob/mechanical/technomancer_golem/special_post_animation(atom/A) + casting = FALSE + ranged_post_animation(A) + +/decl/mob_organ_names/golem + hit_zones = list("helmet", "cuirass", "left tasset", "right tasset", "left gauntlet", "right gauntlet", "weapon") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm index aab1387dbc1..f0d6da0a868 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem_vr.dm @@ -1,10 +1,10 @@ -// Cataloguer data below - strange we can catalogue space golem wizards -/datum/category_item/catalogue/technology/drone/technomancer_golem - name = "Drone - Technomancer Golem" - desc = "Some sort of advanced, unnatural looking synthetic, built for combat.\ - It has a black-and-blue chassis, and wields some sort of... stun baton in it's hand.\ - The drone has pristine armor, black and shiny, with the blue synth-parts glowing visibly from inside.\ -

                    \ - The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ - no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." - value = CATALOGUER_REWARD_MEDIUM +// Cataloguer data below - strange we can catalogue space golem wizards +/datum/category_item/catalogue/technology/drone/technomancer_golem + name = "Drone - Technomancer Golem" + desc = "Some sort of advanced, unnatural looking synthetic, built for combat.\ + It has a black-and-blue chassis, and wields some sort of... stun baton in it's hand.\ + The drone has pristine armor, black and shiny, with the blue synth-parts glowing visibly from inside.\ +

                    \ + The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm index 37a3eb81cf8..ab63ffc4aeb 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot_vr.dm @@ -1,7 +1,7 @@ -/datum/category_item/catalogue/technology/drone/hivebot // Hivebot Scanner Data - This is for Generic Hivebots - name = "Drone - Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to lack a specific weapon, \ - but uses a regular bullet-type weapon, firing a single projectile with a delay. Once upon a time, these bots may \ - have been used to be some sort of... security, or defensive machinery, at a guess, but their original/true purpose is \ - unclear. Whatever the matter, they're hostile and will engage anything they see, shooting to kill." - value = CATALOGUER_REWARD_HARD +/datum/category_item/catalogue/technology/drone/hivebot // Hivebot Scanner Data - This is for Generic Hivebots + name = "Drone - Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to lack a specific weapon, \ + but uses a regular bullet-type weapon, firing a single projectile with a delay. Once upon a time, these bots may \ + have been used to be some sort of... security, or defensive machinery, at a guess, but their original/true purpose is \ + unclear. Whatever the matter, they're hostile and will engage anything they see, shooting to kill." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm index 31c9cdf10fa..4f57f313a30 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage_vr.dm @@ -1,27 +1,27 @@ -/datum/category_item/catalogue/technology/drone/hivebot/rapidfire // Hivebot Scanner Data - This is for Rapidfire Hivebots - name = "Drone - Rapidfire Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - rifle, built for high-rate fire. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/laser // Hivebot Scanner Data - This is for Laser Hivebots - name = "Drone - Laser Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - laser weapon, different from ion bolts, firing bright, vibrant blue bolts of energy. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/ion // Hivebot Scanner Data - This is for Ion Hivebots - name = "Drone - Ion Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - electromagnetic pulse generator, firing bright, vibrant blue bolts of ion energy. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/strong // Hivebot Scanner Data - This is for Laser Hivebots - name = "Drone - Strong Laser Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - ballistic weapon. The weapon seems to fire larger projectiles, and it has heavier armor. Other than that, it has similar yellowish color \ - to regular hivebots." - value = CATALOGUER_REWARD_HARD +/datum/category_item/catalogue/technology/drone/hivebot/rapidfire // Hivebot Scanner Data - This is for Rapidfire Hivebots + name = "Drone - Rapidfire Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + rifle, built for high-rate fire. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/laser // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Laser Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + laser weapon, different from ion bolts, firing bright, vibrant blue bolts of energy. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/ion // Hivebot Scanner Data - This is for Ion Hivebots + name = "Drone - Ion Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + electromagnetic pulse generator, firing bright, vibrant blue bolts of ion energy. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/strong // Hivebot Scanner Data - This is for Laser Hivebots + name = "Drone - Strong Laser Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. The weapon seems to fire larger projectiles, and it has heavier armor. Other than that, it has similar yellowish color \ + to regular hivebots." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm index 434ce659e85..72cbeac0e6d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support_vr.dm @@ -1,13 +1,13 @@ -/datum/category_item/catalogue/technology/drone/hivebot/commander // Hivebot Scanner Data - This is for Commander Hivebots - name = "Drone - Commander Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - ballistic weapon. It also appears to have hardened internal connections and network interlinks, as well as some sort of datalink \ - to the other hivebots. Other than that, it has similar yellowish color to regular hivebots." - value = CATALOGUER_REWARD_HARD - -/datum/category_item/catalogue/technology/drone/hivebot/logistics // Hivebot Scanner Data - This is for Logistics Hivebots - name = "Drone - Logistics Hivebot" - desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ - ballistic weapon. It also appears to have supply deploying bays, and internal fabs to repair and buff their allies' special capabilities. \ - Other than that, it has similar yellowish color to regular hivebots." - value = CATALOGUER_REWARD_HARD +/datum/category_item/catalogue/technology/drone/hivebot/commander // Hivebot Scanner Data - This is for Commander Hivebots + name = "Drone - Commander Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. It also appears to have hardened internal connections and network interlinks, as well as some sort of datalink \ + to the other hivebots. Other than that, it has similar yellowish color to regular hivebots." + value = CATALOGUER_REWARD_HARD + +/datum/category_item/catalogue/technology/drone/hivebot/logistics // Hivebot Scanner Data - This is for Logistics Hivebots + name = "Drone - Logistics Hivebot" + desc = "A drone that walks on several legs, with yellow/gold armor plating. It appears to have some sort of \ + ballistic weapon. It also appears to have supply deploying bays, and internal fabs to repair and buff their allies' special capabilities. \ + Other than that, it has similar yellowish color to regular hivebots." + value = CATALOGUER_REWARD_HARD diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm index f45c1b185df..653abae3eb9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -1,349 +1,349 @@ -// Stronger than a regular Dark Gygax, this one has three special attacks, based on intents. -// First special attack launches three arcing rockets at the current target. -// Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this. -// Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha. -// The AI will choose one every ten seconds. - -/datum/category_item/catalogue/technology/adv_dark_gygax - name = "Exosuit - Advanced Dark Gygax" - desc = "This exosuit is an experimental prototype, descended from the Dark Gygax. It retains the \ - speed that is characteristic of the other models, yet outclasses all of them in durability, \ - to the point of having a comparable amount of protection to models that placed a higher emphesis \ - on armor, like the Durand and even the Marauder. It is also much larger in scale, and significantly \ - heavier than most other exosuits developed by humans, which often causes shockwaves to be felt \ - whenever it moves. This has been observed to have a demoralizing effect on hostile forces.\ -

                    \ - Weapons & Power System
                    \ - Attached to the exosuit's chassis are several newly invented mounted weapons, each unique in purpose and capability. \ - These weapons are integral to the chassis as opposed to the modular equipment that more traditional exosuits utilize. \ - It is unknown if that is due to simply being an early prototype, or if discarding the modular design is benefitial \ - to the design of the model.\ -

                    \ - All the weapons utilize energy, as opposed to consumable projectiles. This appears to have been a conscious decision to \ - allow for more staying power, by only being limited by availablity of electricity. \ - In order to supply the needed energy for combat, the ADG contains a miniturized fusion reactor, which is also \ - considered experimental due to its size. The reactor is powerful enough to power the actuators, electronics, \ - and the primary weapon. The supplementary weapons, however, cannot be continiously fired and instead draw from \ - a electrical buffer that is constantly replenished by the reactor.\ -

                    \ - Homing Energy Bolts
                    \ - The primary weapon is a projector that fires somewhat slow moving blue bolts of energy. The ADG is able to \ - passively redirect the trajectory of the blue bolts towards the initial target, essentially acting as a \ - homing projectile. The blue bolt itself is otherwise not very powerful compared to conventional photonic \ - weaponry or ballistic shells, however the power required to fire the main gun is significantly less \ - than the other available weapons, and so the ADG uses it as the main weapon.\ -

                    \ - Self-Supplying Missile Launcher
                    \ - The first supplementary weapon would appear to not be an energy weapon, as it is a missile launcher. \ - What is not obvious is that the missiles are fabricated inside the exosuit, with the physical \ - materials also being created from energy, similar to the newer models of Rapid Construction Devices. \ - Therefore, the ADG does not need to concern itself with running out of missiles. The missiles themselves \ - are optimized towards harming hard targets, such as other exosuits, but are also still dangerous to soft \ - targets like infantry.\ -

                    \ - Electric Defense
                    \ - The second supplementary weapon is not a conventional gun. Instead, the ADG weaponizes its electrical \ - systems by redirecting power output from its fusion reactor to its exterior shell, becoming a walking \ - tesla coil. This generates a strong electric field that harms anything unprotected nearby. \ - The electric field grows in power, until reaching a critical point, after which a blinding flash \ - of light and arcs of lightning fly out from the exosuit towards its surroundings.\ -

                    \ - Microsingularity Projector
                    \ - Finally, the third supplementary weapon utilizes gravitation as a weapon, by firing a blue energetic orb \ - that, upon hitting the ground, collapses and causes a 'microsingularity' to emerge briefly, pulling in \ - anything unsecured, such as personnel or weapons. The microsingularity lacks the means to gain any energy, meaning it \ - will dissipate in a few seconds, and so it is probably safe to use on a planetary body.\ -

                    \ - Flaws
                    \ - It would appear the ADG is poised to take the place of other exosuits like the Marauder, however several \ - massive flaws exist to make that unlikely. Firstly, this exosuit is almost an order of magnitude more \ - costly to produce than comparable alternatives, even accounting for being a prototype. \ - Secondly, a number of weapons integrated into the ADG are dangerous both to enemies and \ - allies, limiting the ability for a massed assault using ADGs. \ - Finally, the nature of several weapons used could invoke technological fear, or otherwise \ - be considered a war crime to utilize, primarily the electrical field and microsingularity \ - projector.\ -

                    \ - All of these flaws appear to doom the ADG to becoming another technological marvel that was \ - overly ambitious and unconstrained to the demands of reality. They will likely be really rare, \ - and terrifying." - value = CATALOGUER_REWARD_SUPERHARD - - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced - name = "advanced dark gygax" - desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \ - It also is armed with an array of next-generation weaponry." - catalogue_data = list(/datum/category_item/catalogue/technology/adv_dark_gygax) - icon_state = "darkgygax_adv" - wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv - icon_scale_x = 1.5 - icon_scale_y = 1.5 - movement_shake_radius = 14 - - maxHealth = 450 - deflect_chance = 25 - has_repair_droid = TRUE - armor = list( - "melee" = 50, - "bullet" = 50, - "laser" = 50, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100 - ) - - special_attack_min_range = 1 - special_attack_max_range = 7 - special_attack_cooldown = 10 SECONDS - projectiletype = /obj/item/projectile/energy/homing_bolt // We're now a bullet hell game. - projectilesound = 'sound/weapons/wave.ogg' - ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax - var/obj/effect/overlay/energy_ball/energy_ball = null - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy() - if(energy_ball) - energy_ball.stop_orbit() - qdel(energy_ball) - return ..() - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A) - . = TRUE // So we don't fire a bolt as well. - switch(a_intent) - if(I_DISARM) // Side gun - electric_defense(A) - if(I_HURT) // Rockets - launch_rockets(A) - if(I_GRAB) // Micro-singulo - launch_microsingularity(A) - -/obj/item/projectile/energy/homing_bolt - name = "homing bolt" - icon_state = "force_missile" - damage = 20 - damage_type = BURN - check_armour = "laser" - -/obj/item/projectile/energy/homing_bolt/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - ..() - if(target) - set_homing_target(target) - -/obj/item/projectile/energy/homing_bolt/fire(angle, atom/direct_target) - ..() - set_pixel_speed(0.5) - -#define ELECTRIC_ZAP_POWER 15000 //VOREStation Edit - -// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. -// It also briefly blinds anyone looking directly at the mech without flash protection. -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target) - set waitfor = FALSE - - // Temporary immunity to shock to avoid killing themselves with their own attack. - var/old_shock_resist = shock_resist - shock_resist = 1 - - // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. - energy_ball = new(loc) - energy_ball.adjust_scale(0.5) - energy_ball.orbit(src, 32, TRUE, 1 SECOND) - - visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!")) - - playsound(src, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) - - // Shock nearby things that aren't ourselves. - for(var/i = 1 to 10) - energy_ball.adjust_scale(0.5 + (i/10)) - energy_ball.set_light(i/2, i/2, "#0000FF") - for(var/thing in range(3, src)) - // This is stupid because mechs are stupid and not mobs. - if(isliving(thing)) - var/mob/living/L = thing - - if(L == src) - continue - if(L.stat) - continue // Otherwise it can get pretty laggy if there's loads of corpses around. - L.inflict_shock_damage(i * 2) - if(L && L.has_AI()) // Some mobs delete themselves when dying. - L.ai_holder.react_to_attack(src) - - else if(istype(thing, /obj/mecha)) - var/obj/mecha/M = thing - M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. - - sleep(1 SECOND) - - // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. - visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) - playsound(src, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) - tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) - for(var/mob/living/L in viewers(src)) - if(L == src) - continue - var/dir_towards_us = get_dir(L, src) - if(L.dir && L.dir & dir_towards_us) - to_chat(L, span("danger", "The flash of light blinds you briefly.")) - L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) - - // Get rid of our energy ball. - energy_ball.stop_orbit() - qdel(energy_ball) - - sleep(1 SECOND) - // Resist resistance to old value. - shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. - -#undef ELECTRIC_ZAP_POWER - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target) - set waitfor = FALSE - - // Telegraph our next move. - Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) - visible_message(span("warning", "\The [src] deploys a missile rack!")) - playsound(src, 'sound/effects/turret/move1.wav', 50, 1) - sleep(0.5 SECONDS) - - for(var/i = 1 to 3) - if(target) // Might get deleted in the meantime. - var/turf/T = get_turf(target) - if(T) - visible_message(span("warning", "\The [src] fires a rocket into the air!")) - playsound(src, 'sound/weapons/rpg.ogg', 70, 1) - face_atom(T) - var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc) - rocket.old_style_target(T, src) - rocket.fire() - sleep(1 SECOND) - - visible_message(span("warning", "\The [src] retracts the missile rack.")) - playsound(src, 'sound/effects/turret/move2.wav', 50, 1) - -// Arcing rocket projectile that produces a weak explosion when it lands. -// Shouldn't punch holes in the floor, but will still hurt. -/obj/item/projectile/arc/explosive_rocket - name = "rocket" - icon_state = "mortar" - -/obj/item/projectile/arc/explosive_rocket/on_impact(turf/T) - new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. - explosion(T, 0, 0, 2, adminlog = FALSE) - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target) - var/turf/T = get_turf(target) - visible_message(span("warning", "\The [src] fires an energetic sphere into the air!")) - playsound(src, 'sound/weapons/Laser.ogg', 50, 1) - face_atom(T) - var/obj/item/projectile/arc/microsingulo/sphere = new(loc) - sphere.old_style_target(T, src) - sphere.fire() - -/obj/item/projectile/arc/microsingulo - name = "micro singularity" - icon_state = "bluespace" - -/obj/item/projectile/arc/microsingulo/on_impact(turf/T) - new /obj/effect/temporary_effect/pulse/microsingulo(T) - - -/obj/effect/temporary_effect/pulse/microsingulo - name = "micro singularity" - desc = "It's sucking everything in!" - icon = 'icons/obj/objects.dmi' - icon_state = "bhole3" - light_range = 4 - light_power = 5 - light_color = "#2ECCFA" - pulses_remaining = 10 - pulse_delay = 0.5 SECONDS - var/pull_radius = 3 - var/pull_strength = STAGE_THREE - -/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() - for(var/atom/A in range(pull_radius, src)) - A.singularity_pull(src, pull_strength) - - -// The Advanced Dark Gygax's AI. -// The mob has three special attacks, based on the current intent. -// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax - conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. - var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. - - var/electric_defense_radius = 3 // How big to assume electric defense's area is. - var/microsingulo_radius = 3 // Same but for microsingulo pull. - var/rocket_explosive_radius = 2 // Explosion radius for the rockets. - - var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. - var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. - -// Used to control the mob's positioning based on which special attack it has done. -// Note that the intent will not change again until the next special attack is about to happen. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) - // Make the AI backpeddle if using an AoE special attack. - var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. - if(holder.a_intent in risky_intents) - var/closest_distance = 1 - switch(holder.a_intent) // Plus one just in case. - if(I_HURT) - closest_distance = rocket_explosive_radius + 1 - if(I_GRAB) - closest_distance = microsingulo_radius + 1 - - if(get_dist(holder, A) <= closest_distance) - holder.IMove(get_step_away(holder, A, closest_distance)) - - // Otherwise get up close and personal. - else if(get_dist(holder, A) > closest_desired_distance) - holder.IMove(get_step_towards(holder, A)) - -// Changes the mob's intent, which controls which special attack is used. -// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) - if(isliving(A)) - var/mob/living/target = A - - // If we're surrounded, Electric Defense will quickly fix that. - var/tally = 0 - var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. - for(var/atom/movable/AM in potential_targets) - if(get_dist(holder, AM) > electric_defense_radius) - continue - if(!can_attack(AM)) - continue - tally++ - - // Should we shock them? - if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) - holder.a_intent = I_DISARM - return - - // Otherwise they're a fair distance away and we're not getting mobbed up close. - // See if we should use missiles or microsingulo. - tally = 0 // Let's recycle the var. - for(var/atom/movable/AM in potential_targets) - if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. - continue - if(!can_attack(AM)) - continue - if(AM.anchored) // Microsingulo doesn't do anything to anchored things. - tally-- - else - tally++ - - // Lots of people means minisingulo would be more useful. - if(tally >= microsingulo_threshold) - holder.a_intent = I_GRAB - else // Otherwise use rockets. - holder.a_intent = I_HURT - - else - if(get_dist(holder, A) >= rocket_explosive_radius + 1) - holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. - else - holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. +// Stronger than a regular Dark Gygax, this one has three special attacks, based on intents. +// First special attack launches three arcing rockets at the current target. +// Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this. +// Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha. +// The AI will choose one every ten seconds. + +/datum/category_item/catalogue/technology/adv_dark_gygax + name = "Exosuit - Advanced Dark Gygax" + desc = "This exosuit is an experimental prototype, descended from the Dark Gygax. It retains the \ + speed that is characteristic of the other models, yet outclasses all of them in durability, \ + to the point of having a comparable amount of protection to models that placed a higher emphesis \ + on armor, like the Durand and even the Marauder. It is also much larger in scale, and significantly \ + heavier than most other exosuits developed by humans, which often causes shockwaves to be felt \ + whenever it moves. This has been observed to have a demoralizing effect on hostile forces.\ +

                    \ + Weapons & Power System
                    \ + Attached to the exosuit's chassis are several newly invented mounted weapons, each unique in purpose and capability. \ + These weapons are integral to the chassis as opposed to the modular equipment that more traditional exosuits utilize. \ + It is unknown if that is due to simply being an early prototype, or if discarding the modular design is benefitial \ + to the design of the model.\ +

                    \ + All the weapons utilize energy, as opposed to consumable projectiles. This appears to have been a conscious decision to \ + allow for more staying power, by only being limited by availablity of electricity. \ + In order to supply the needed energy for combat, the ADG contains a miniturized fusion reactor, which is also \ + considered experimental due to its size. The reactor is powerful enough to power the actuators, electronics, \ + and the primary weapon. The supplementary weapons, however, cannot be continiously fired and instead draw from \ + a electrical buffer that is constantly replenished by the reactor.\ +

                    \ + Homing Energy Bolts
                    \ + The primary weapon is a projector that fires somewhat slow moving blue bolts of energy. The ADG is able to \ + passively redirect the trajectory of the blue bolts towards the initial target, essentially acting as a \ + homing projectile. The blue bolt itself is otherwise not very powerful compared to conventional photonic \ + weaponry or ballistic shells, however the power required to fire the main gun is significantly less \ + than the other available weapons, and so the ADG uses it as the main weapon.\ +

                    \ + Self-Supplying Missile Launcher
                    \ + The first supplementary weapon would appear to not be an energy weapon, as it is a missile launcher. \ + What is not obvious is that the missiles are fabricated inside the exosuit, with the physical \ + materials also being created from energy, similar to the newer models of Rapid Construction Devices. \ + Therefore, the ADG does not need to concern itself with running out of missiles. The missiles themselves \ + are optimized towards harming hard targets, such as other exosuits, but are also still dangerous to soft \ + targets like infantry.\ +

                    \ + Electric Defense
                    \ + The second supplementary weapon is not a conventional gun. Instead, the ADG weaponizes its electrical \ + systems by redirecting power output from its fusion reactor to its exterior shell, becoming a walking \ + tesla coil. This generates a strong electric field that harms anything unprotected nearby. \ + The electric field grows in power, until reaching a critical point, after which a blinding flash \ + of light and arcs of lightning fly out from the exosuit towards its surroundings.\ +

                    \ + Microsingularity Projector
                    \ + Finally, the third supplementary weapon utilizes gravitation as a weapon, by firing a blue energetic orb \ + that, upon hitting the ground, collapses and causes a 'microsingularity' to emerge briefly, pulling in \ + anything unsecured, such as personnel or weapons. The microsingularity lacks the means to gain any energy, meaning it \ + will dissipate in a few seconds, and so it is probably safe to use on a planetary body.\ +

                    \ + Flaws
                    \ + It would appear the ADG is poised to take the place of other exosuits like the Marauder, however several \ + massive flaws exist to make that unlikely. Firstly, this exosuit is almost an order of magnitude more \ + costly to produce than comparable alternatives, even accounting for being a prototype. \ + Secondly, a number of weapons integrated into the ADG are dangerous both to enemies and \ + allies, limiting the ability for a massed assault using ADGs. \ + Finally, the nature of several weapons used could invoke technological fear, or otherwise \ + be considered a war crime to utilize, primarily the electrical field and microsingularity \ + projector.\ +

                    \ + All of these flaws appear to doom the ADG to becoming another technological marvel that was \ + overly ambitious and unconstrained to the demands of reality. They will likely be really rare, \ + and terrifying." + value = CATALOGUER_REWARD_SUPERHARD + + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced + name = "advanced dark gygax" + desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \ + It also is armed with an array of next-generation weaponry." + catalogue_data = list(/datum/category_item/catalogue/technology/adv_dark_gygax) + icon_state = "darkgygax_adv" + wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv + icon_scale_x = 1.5 + icon_scale_y = 1.5 + movement_shake_radius = 14 + + maxHealth = 450 + deflect_chance = 25 + has_repair_droid = TRUE + armor = list( + "melee" = 50, + "bullet" = 50, + "laser" = 50, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + + special_attack_min_range = 1 + special_attack_max_range = 7 + special_attack_cooldown = 10 SECONDS + projectiletype = /obj/item/projectile/energy/homing_bolt // We're now a bullet hell game. + projectilesound = 'sound/weapons/wave.ogg' + ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax + var/obj/effect/overlay/energy_ball/energy_ball = null + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy() + if(energy_ball) + energy_ball.stop_orbit() + qdel(energy_ball) + return ..() + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A) + . = TRUE // So we don't fire a bolt as well. + switch(a_intent) + if(I_DISARM) // Side gun + electric_defense(A) + if(I_HURT) // Rockets + launch_rockets(A) + if(I_GRAB) // Micro-singulo + launch_microsingularity(A) + +/obj/item/projectile/energy/homing_bolt + name = "homing bolt" + icon_state = "force_missile" + damage = 20 + damage_type = BURN + check_armour = "laser" + +/obj/item/projectile/energy/homing_bolt/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + ..() + if(target) + set_homing_target(target) + +/obj/item/projectile/energy/homing_bolt/fire(angle, atom/direct_target) + ..() + set_pixel_speed(0.5) + +#define ELECTRIC_ZAP_POWER 15000 //VOREStation Edit + +// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. +// It also briefly blinds anyone looking directly at the mech without flash protection. +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target) + set waitfor = FALSE + + // Temporary immunity to shock to avoid killing themselves with their own attack. + var/old_shock_resist = shock_resist + shock_resist = 1 + + // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. + energy_ball = new(loc) + energy_ball.adjust_scale(0.5) + energy_ball.orbit(src, 32, TRUE, 1 SECOND) + + visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!")) + + playsound(src, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) + + // Shock nearby things that aren't ourselves. + for(var/i = 1 to 10) + energy_ball.adjust_scale(0.5 + (i/10)) + energy_ball.set_light(i/2, i/2, "#0000FF") + for(var/thing in range(3, src)) + // This is stupid because mechs are stupid and not mobs. + if(isliving(thing)) + var/mob/living/L = thing + + if(L == src) + continue + if(L.stat) + continue // Otherwise it can get pretty laggy if there's loads of corpses around. + L.inflict_shock_damage(i * 2) + if(L && L.has_AI()) // Some mobs delete themselves when dying. + L.ai_holder.react_to_attack(src) + + else if(istype(thing, /obj/mecha)) + var/obj/mecha/M = thing + M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. + + sleep(1 SECOND) + + // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. + visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) + playsound(src, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) + tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) + for(var/mob/living/L in viewers(src)) + if(L == src) + continue + var/dir_towards_us = get_dir(L, src) + if(L.dir && L.dir & dir_towards_us) + to_chat(L, span("danger", "The flash of light blinds you briefly.")) + L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) + + // Get rid of our energy ball. + energy_ball.stop_orbit() + qdel(energy_ball) + + sleep(1 SECOND) + // Resist resistance to old value. + shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. + +#undef ELECTRIC_ZAP_POWER + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target) + set waitfor = FALSE + + // Telegraph our next move. + Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) + visible_message(span("warning", "\The [src] deploys a missile rack!")) + playsound(src, 'sound/effects/turret/move1.wav', 50, 1) + sleep(0.5 SECONDS) + + for(var/i = 1 to 3) + if(target) // Might get deleted in the meantime. + var/turf/T = get_turf(target) + if(T) + visible_message(span("warning", "\The [src] fires a rocket into the air!")) + playsound(src, 'sound/weapons/rpg.ogg', 70, 1) + face_atom(T) + var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc) + rocket.old_style_target(T, src) + rocket.fire() + sleep(1 SECOND) + + visible_message(span("warning", "\The [src] retracts the missile rack.")) + playsound(src, 'sound/effects/turret/move2.wav', 50, 1) + +// Arcing rocket projectile that produces a weak explosion when it lands. +// Shouldn't punch holes in the floor, but will still hurt. +/obj/item/projectile/arc/explosive_rocket + name = "rocket" + icon_state = "mortar" + +/obj/item/projectile/arc/explosive_rocket/on_impact(turf/T) + new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. + explosion(T, 0, 0, 2, adminlog = FALSE) + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target) + var/turf/T = get_turf(target) + visible_message(span("warning", "\The [src] fires an energetic sphere into the air!")) + playsound(src, 'sound/weapons/Laser.ogg', 50, 1) + face_atom(T) + var/obj/item/projectile/arc/microsingulo/sphere = new(loc) + sphere.old_style_target(T, src) + sphere.fire() + +/obj/item/projectile/arc/microsingulo + name = "micro singularity" + icon_state = "bluespace" + +/obj/item/projectile/arc/microsingulo/on_impact(turf/T) + new /obj/effect/temporary_effect/pulse/microsingulo(T) + + +/obj/effect/temporary_effect/pulse/microsingulo + name = "micro singularity" + desc = "It's sucking everything in!" + icon = 'icons/obj/objects.dmi' + icon_state = "bhole3" + light_range = 4 + light_power = 5 + light_color = "#2ECCFA" + pulses_remaining = 10 + pulse_delay = 0.5 SECONDS + var/pull_radius = 3 + var/pull_strength = STAGE_THREE + +/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() + for(var/atom/A in range(pull_radius, src)) + A.singularity_pull(src, pull_strength) + + +// The Advanced Dark Gygax's AI. +// The mob has three special attacks, based on the current intent. +// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax + conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. + var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. + + var/electric_defense_radius = 3 // How big to assume electric defense's area is. + var/microsingulo_radius = 3 // Same but for microsingulo pull. + var/rocket_explosive_radius = 2 // Explosion radius for the rockets. + + var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. + var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. + +// Used to control the mob's positioning based on which special attack it has done. +// Note that the intent will not change again until the next special attack is about to happen. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) + // Make the AI backpeddle if using an AoE special attack. + var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. + if(holder.a_intent in risky_intents) + var/closest_distance = 1 + switch(holder.a_intent) // Plus one just in case. + if(I_HURT) + closest_distance = rocket_explosive_radius + 1 + if(I_GRAB) + closest_distance = microsingulo_radius + 1 + + if(get_dist(holder, A) <= closest_distance) + holder.IMove(get_step_away(holder, A, closest_distance)) + + // Otherwise get up close and personal. + else if(get_dist(holder, A) > closest_desired_distance) + holder.IMove(get_step_towards(holder, A)) + +// Changes the mob's intent, which controls which special attack is used. +// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/target = A + + // If we're surrounded, Electric Defense will quickly fix that. + var/tally = 0 + var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. + for(var/atom/movable/AM in potential_targets) + if(get_dist(holder, AM) > electric_defense_radius) + continue + if(!can_attack(AM)) + continue + tally++ + + // Should we shock them? + if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) + holder.a_intent = I_DISARM + return + + // Otherwise they're a fair distance away and we're not getting mobbed up close. + // See if we should use missiles or microsingulo. + tally = 0 // Let's recycle the var. + for(var/atom/movable/AM in potential_targets) + if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. + continue + if(!can_attack(AM)) + continue + if(AM.anchored) // Microsingulo doesn't do anything to anchored things. + tally-- + else + tally++ + + // Lots of people means minisingulo would be more useful. + if(tally >= microsingulo_threshold) + holder.a_intent = I_GRAB + else // Otherwise use rockets. + holder.a_intent = I_HURT + + else + if(get_dist(holder, A) >= rocket_explosive_radius + 1) + holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. + else + holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm index da1dce8d34c..ecefa62de54 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm @@ -1,38 +1,38 @@ -// Base type for the 'combat' mechas like gygax/durand/maulers/etc. -// They generally are walking tanks, and their melee attack knocks back and stuns, like the real deal. - -/mob/living/simple_mob/mechanical/mecha/combat - name = "combat mecha" - desc = "An even bigger stompy mech!!" - - movement_cooldown = 3 - melee_damage_lower = 30 - melee_damage_upper = 30 - melee_attack_delay = 1 SECOND - attacktext = list("punched", "slammed", "uppercutted", "pummeled") - - armor = list( - "melee" = 30, - "bullet" = 30, - "laser" = 15, - "energy" = 0, - "bomb" = 20, - "bio" = 100, - "rad" = 100 - ) - - var/weaken_amount = 2 // Be careful with this number. High values can equal a permastun. - -// Melee hits knock back by one tile (or more if already stunned to help prevent permastuns). -/mob/living/simple_mob/mechanical/mecha/combat/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(L.mob_size <= MOB_MEDIUM) - visible_message(span("danger", "\The [src] sends \the [L] flying with their mechanized fist!")) - playsound(src, "punch", 50, 1) - L.Weaken(weaken_amount) - var/throw_dir = get_dir(src, L) - var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 - L.throw_at(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) - else - to_chat(L, span("warning", "\The [src] punches you with incredible force, but you remain in place.")) +// Base type for the 'combat' mechas like gygax/durand/maulers/etc. +// They generally are walking tanks, and their melee attack knocks back and stuns, like the real deal. + +/mob/living/simple_mob/mechanical/mecha/combat + name = "combat mecha" + desc = "An even bigger stompy mech!!" + + movement_cooldown = 3 + melee_damage_lower = 30 + melee_damage_upper = 30 + melee_attack_delay = 1 SECOND + attacktext = list("punched", "slammed", "uppercutted", "pummeled") + + armor = list( + "melee" = 30, + "bullet" = 30, + "laser" = 15, + "energy" = 0, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + + var/weaken_amount = 2 // Be careful with this number. High values can equal a permastun. + +// Melee hits knock back by one tile (or more if already stunned to help prevent permastuns). +/mob/living/simple_mob/mechanical/mecha/combat/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with their mechanized fist!")) + playsound(src, "punch", 50, 1) + L.Weaken(weaken_amount) + var/throw_dir = get_dir(src, L) + var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 + L.throw_at(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) + else + to_chat(L, span("warning", "\The [src] punches you with incredible force, but you remain in place.")) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm index f1cefbef803..97d659f7e87 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm @@ -1,78 +1,78 @@ -// Durands are slow, tanky, beefy, and hit really hard. -// They can also root themselves to become even tankier. -// The AI doesn't do this currently. - -/datum/category_item/catalogue/technology/durand - name = "Exosuit - Durand" - desc = "The Durand is an old combat exosuit, that was once the most durable exosuit ever developed by humans. \ - In modern times, this exosuit has been dethroned from that title, yet it remains one of the more well built and armored \ - exosuits, despite its age.\ -

                    \ - During the Hegemony War against the Unathi, there was a need for various new technologies to be developed \ - to counter the Unathi war machine. One of many solutions created was the Durand, which was made to be heavy and \ - well-armored, and be capable of powering the various weapons that could be mounted onto it. Presently, the \ - Durand now generally serves as corporate asset protection hardware, due to modern militaries moving on to newer, \ - more advanced war machines." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/durand - name = "durand" - desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War." - catalogue_data = list(/datum/category_item/catalogue/technology/durand) - icon_state = "durand" - movement_cooldown = 3 - wreckage = /obj/structure/loot_pile/mecha/durand - - maxHealth = 400 - deflect_chance = 20 - armor = list( - "melee" = 50, - "bullet" = 35, - "laser" = 15, - "energy" = 10, - "bomb" = 20, - "bio" = 100, - "rad" = 100 - ) - melee_damage_lower = 40 - melee_damage_upper = 40 - base_attack_cooldown = 2 SECONDS - projectiletype = /obj/item/projectile/beam/heavylaser - projectile_dispersion = 10 - projectile_accuracy = -30 - - var/defense_mode = FALSE - var/defense_deflect = 35 - -/mob/living/simple_mob/mechanical/mecha/combat/durand/proc/set_defense_mode(new_mode) - defense_mode = new_mode - deflect_chance = defense_mode ? defense_deflect : initial(deflect_chance) - projectile_accuracy = defense_mode ? -10 : initial(projectile_accuracy) - to_chat(src, span("notice", "You [defense_mode ? "en" : "dis"]able defense mode.")) - -/mob/living/simple_mob/mechanical/mecha/combat/durand/SelfMove(turf/n, direct) - if(defense_mode) - to_chat(src, span("warning", "You are in defense mode, you cannot move.")) - return FALSE - return ..() - -// So players can toggle it too. -/mob/living/simple_mob/mechanical/mecha/combat/durand/verb/toggle_defense_mode() - set name = "Toggle Defense Mode" - set desc = "Toggles a special mode which makes you immobile and much more resilient." - set category = "Abilities" - - set_defense_mode(!defense_mode) - -// Variant that starts in defense mode, perhaps for PoIs. -/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/Initialize() - set_defense_mode(TRUE) - return ..() - -/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary - desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War.\ - This one has been retrofitted into a turret." - - projectiletype = /obj/item/projectile/beam/heavylaser/fakeemitter - - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged +// Durands are slow, tanky, beefy, and hit really hard. +// They can also root themselves to become even tankier. +// The AI doesn't do this currently. + +/datum/category_item/catalogue/technology/durand + name = "Exosuit - Durand" + desc = "The Durand is an old combat exosuit, that was once the most durable exosuit ever developed by humans. \ + In modern times, this exosuit has been dethroned from that title, yet it remains one of the more well built and armored \ + exosuits, despite its age.\ +

                    \ + During the Hegemony War against the Unathi, there was a need for various new technologies to be developed \ + to counter the Unathi war machine. One of many solutions created was the Durand, which was made to be heavy and \ + well-armored, and be capable of powering the various weapons that could be mounted onto it. Presently, the \ + Durand now generally serves as corporate asset protection hardware, due to modern militaries moving on to newer, \ + more advanced war machines." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/durand + name = "durand" + desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War." + catalogue_data = list(/datum/category_item/catalogue/technology/durand) + icon_state = "durand" + movement_cooldown = 3 + wreckage = /obj/structure/loot_pile/mecha/durand + + maxHealth = 400 + deflect_chance = 20 + armor = list( + "melee" = 50, + "bullet" = 35, + "laser" = 15, + "energy" = 10, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + melee_damage_lower = 40 + melee_damage_upper = 40 + base_attack_cooldown = 2 SECONDS + projectiletype = /obj/item/projectile/beam/heavylaser + projectile_dispersion = 10 + projectile_accuracy = -30 + + var/defense_mode = FALSE + var/defense_deflect = 35 + +/mob/living/simple_mob/mechanical/mecha/combat/durand/proc/set_defense_mode(new_mode) + defense_mode = new_mode + deflect_chance = defense_mode ? defense_deflect : initial(deflect_chance) + projectile_accuracy = defense_mode ? -10 : initial(projectile_accuracy) + to_chat(src, span("notice", "You [defense_mode ? "en" : "dis"]able defense mode.")) + +/mob/living/simple_mob/mechanical/mecha/combat/durand/SelfMove(turf/n, direct) + if(defense_mode) + to_chat(src, span("warning", "You are in defense mode, you cannot move.")) + return FALSE + return ..() + +// So players can toggle it too. +/mob/living/simple_mob/mechanical/mecha/combat/durand/verb/toggle_defense_mode() + set name = "Toggle Defense Mode" + set desc = "Toggles a special mode which makes you immobile and much more resilient." + set category = "Abilities" + + set_defense_mode(!defense_mode) + +// Variant that starts in defense mode, perhaps for PoIs. +/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/Initialize() + set_defense_mode(TRUE) + return ..() + +/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/mercenary + desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the Hegemony War.\ + This one has been retrofitted into a turret." + + projectiletype = /obj/item/projectile/beam/heavylaser/fakeemitter + + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm index e06960b1958..bafbb58cd7d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm @@ -1,84 +1,84 @@ -// Gygaxes are tough but also fast. -// Their AI, unlike most, will advance towards their target instead of remaining in place. - -/datum/category_item/catalogue/technology/gygax - name = "Exosuit - Gygax" - desc = "The Gygax is a relatively modern exosuit, built to be lightweight and agile, while still being fairly durable. \ - These traits have made them rather popular among well funded private and corporate security forces, who desire \ - the ability to rapidly respond to conflict.\ -

                    \ - One special feature of this model is that the actuators that \ - drive the exosuit can have their safeties disabled in order to achieve a short-term burst of unparalleled speed, \ - at the expense of damaging the exosuit considerably." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/gygax - name = "gygax" - desc = "A lightweight, security exosuit. Popular among private and corporate security." - catalogue_data = list(/datum/category_item/catalogue/technology/gygax) - icon_state = "gygax" - movement_cooldown = -1 - wreckage = /obj/structure/loot_pile/mecha/gygax - - maxHealth = 300 - armor = list( - "melee" = 25, - "bullet" = 20, - "laser" = 30, - "energy" = 15, - "bomb" = 0, - "bio" = 100, - "rad" = 100 - ) - - projectile_dispersion = 8 - projectiletype = /obj/item/projectile/beam/midlaser - - ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. - - -// A stronger variant. - -/datum/category_item/catalogue/technology/dark_gygax - name = "Exosuit - Dark Gygax" - desc = "This exosuit is a variant of the regular Gygax. It is generally referred to as the Dark Gygax, \ - due to being constructed from different materials that give it a darker appearance. Beyond merely looking \ - cosmetically different, the Dark Gygax also has various upgrades compared to the Gygax. It is much more \ - resilient, yet retains the agility and speed of the Gygax.\ -

                    \ - These are relatively rare compared to the other security exosuits, as most security forces are content with \ - a regular Gygax. Instead, this exosuit is often used by high-end asset protection teams, and mercenaries." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark - name = "dark gygax" - desc = "A significantly upgraded Gygax security mech, often utilized by corporate asset protection teams and \ - PMCs." - catalogue_data = list(/datum/category_item/catalogue/technology/dark_gygax) - icon_state = "darkgygax" - wreckage = /obj/structure/loot_pile/mecha/gygax/dark - - maxHealth = 400 - deflect_chance = 25 - has_repair_droid = TRUE - armor = list( - "melee" = 40, - "bullet" = 40, - "laser" = 50, - "energy" = 35, - "bomb" = 20, - "bio" = 100, - "rad" = 100 - ) - -/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax - name = "medgax" - desc = "An unorthodox fusion of the Gygax and Odysseus exosuits, this one is fast, sturdy, and carries a wide array of \ - potent chemicals and delivery mechanisms. The doctor is in!" - icon_state = "medgax" - wreckage = /obj/structure/loot_pile/mecha/gygax/medgax - - projectile_dispersion = 8 +// Gygaxes are tough but also fast. +// Their AI, unlike most, will advance towards their target instead of remaining in place. + +/datum/category_item/catalogue/technology/gygax + name = "Exosuit - Gygax" + desc = "The Gygax is a relatively modern exosuit, built to be lightweight and agile, while still being fairly durable. \ + These traits have made them rather popular among well funded private and corporate security forces, who desire \ + the ability to rapidly respond to conflict.\ +

                    \ + One special feature of this model is that the actuators that \ + drive the exosuit can have their safeties disabled in order to achieve a short-term burst of unparalleled speed, \ + at the expense of damaging the exosuit considerably." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/gygax + name = "gygax" + desc = "A lightweight, security exosuit. Popular among private and corporate security." + catalogue_data = list(/datum/category_item/catalogue/technology/gygax) + icon_state = "gygax" + movement_cooldown = -1 + wreckage = /obj/structure/loot_pile/mecha/gygax + + maxHealth = 300 + armor = list( + "melee" = 25, + "bullet" = 20, + "laser" = 30, + "energy" = 15, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + projectile_dispersion = 8 + projectiletype = /obj/item/projectile/beam/midlaser + + ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + + +// A stronger variant. + +/datum/category_item/catalogue/technology/dark_gygax + name = "Exosuit - Dark Gygax" + desc = "This exosuit is a variant of the regular Gygax. It is generally referred to as the Dark Gygax, \ + due to being constructed from different materials that give it a darker appearance. Beyond merely looking \ + cosmetically different, the Dark Gygax also has various upgrades compared to the Gygax. It is much more \ + resilient, yet retains the agility and speed of the Gygax.\ +

                    \ + These are relatively rare compared to the other security exosuits, as most security forces are content with \ + a regular Gygax. Instead, this exosuit is often used by high-end asset protection teams, and mercenaries." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark + name = "dark gygax" + desc = "A significantly upgraded Gygax security mech, often utilized by corporate asset protection teams and \ + PMCs." + catalogue_data = list(/datum/category_item/catalogue/technology/dark_gygax) + icon_state = "darkgygax" + wreckage = /obj/structure/loot_pile/mecha/gygax/dark + + maxHealth = 400 + deflect_chance = 25 + has_repair_droid = TRUE + armor = list( + "melee" = 40, + "bullet" = 40, + "laser" = 50, + "energy" = 35, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax + name = "medgax" + desc = "An unorthodox fusion of the Gygax and Odysseus exosuits, this one is fast, sturdy, and carries a wide array of \ + potent chemicals and delivery mechanisms. The doctor is in!" + icon_state = "medgax" + wreckage = /obj/structure/loot_pile/mecha/gygax/medgax + + projectile_dispersion = 8 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm index c575922be52..b4aa0d1ed6e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm @@ -1,48 +1,48 @@ -// Ranged, and capable of flight. - -/datum/category_item/catalogue/technology/hoverpod - name = "Voidcraft - Hoverpod" - desc = "This is a small space-capable craft that has a round design. Can hold up to one pilot, \ - and sometimes one or two passengers, with the right modifications made. \ - Hoverpods have existed for a very long time, and the design has remained more or less consistant over its life. \ - They carved out a niche in short ranged transportation of cargo or crew while in space, \ - as they were more efficient compared to using a shuttle, and required less infrastructure to use due to being compact enough \ - to use airlocks. As such, they acted as a sort of bridge between being EVA in a spacesuit, and being inside a 'real' spacecraft.\ -

                    \ - In recent times, the Hoverpod is seen as outdated by some, as newer solutions to that niche now exist, however it remains an ancient favorite." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/mecha/hoverpod - name = "hover pod" - desc = "Stubby and round, this space-capable craft is an ancient favorite. It has a jury-rigged welder-laser." - catalogue_data = list(/datum/category_item/catalogue/technology/hoverpod) - icon_state = "engineering_pod" - movement_sound = 'sound/machines/hiss.ogg' - wreckage = /obj/structure/loot_pile/mecha/hoverpod - - maxHealth = 150 - hovering = TRUE // Can fly. - - projectile_dispersion = 10 - projectile_accuracy = -30 - projectiletype = /obj/item/projectile/beam - base_attack_cooldown = 2 SECONDS - - organ_names = /decl/mob_organ_names/hoverpod - - var/datum/effect/effect/system/ion_trail_follow/ion_trail - -/mob/living/simple_mob/mechanical/mecha/hoverpod/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged - -/mob/living/simple_mob/mechanical/mecha/hoverpod/Initialize() - ion_trail = new /datum/effect/effect/system/ion_trail_follow() - ion_trail.set_up(src) - ion_trail.start() - return ..() - -/mob/living/simple_mob/mechanical/mecha/hoverpod/Process_Spacemove(var/check_drift = 0) - return TRUE - -/decl/mob_organ_names/hoverpod - hit_zones = list("central chassis", "control module", "hydraulics", "left manipulator", "right manipulator", "left landing strut", "right landing strut", "maneuvering thruster", "sensor suite", "radiator", "power supply") +// Ranged, and capable of flight. + +/datum/category_item/catalogue/technology/hoverpod + name = "Voidcraft - Hoverpod" + desc = "This is a small space-capable craft that has a round design. Can hold up to one pilot, \ + and sometimes one or two passengers, with the right modifications made. \ + Hoverpods have existed for a very long time, and the design has remained more or less consistant over its life. \ + They carved out a niche in short ranged transportation of cargo or crew while in space, \ + as they were more efficient compared to using a shuttle, and required less infrastructure to use due to being compact enough \ + to use airlocks. As such, they acted as a sort of bridge between being EVA in a spacesuit, and being inside a 'real' spacecraft.\ +

                    \ + In recent times, the Hoverpod is seen as outdated by some, as newer solutions to that niche now exist, however it remains an ancient favorite." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/mecha/hoverpod + name = "hover pod" + desc = "Stubby and round, this space-capable craft is an ancient favorite. It has a jury-rigged welder-laser." + catalogue_data = list(/datum/category_item/catalogue/technology/hoverpod) + icon_state = "engineering_pod" + movement_sound = 'sound/machines/hiss.ogg' + wreckage = /obj/structure/loot_pile/mecha/hoverpod + + maxHealth = 150 + hovering = TRUE // Can fly. + + projectile_dispersion = 10 + projectile_accuracy = -30 + projectiletype = /obj/item/projectile/beam + base_attack_cooldown = 2 SECONDS + + organ_names = /decl/mob_organ_names/hoverpod + + var/datum/effect/effect/system/ion_trail_follow/ion_trail + +/mob/living/simple_mob/mechanical/mecha/hoverpod/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + +/mob/living/simple_mob/mechanical/mecha/hoverpod/Initialize() + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + ion_trail.start() + return ..() + +/mob/living/simple_mob/mechanical/mecha/hoverpod/Process_Spacemove(var/check_drift = 0) + return TRUE + +/decl/mob_organ_names/hoverpod + hit_zones = list("central chassis", "control module", "hydraulics", "left manipulator", "right manipulator", "left landing strut", "right landing strut", "maneuvering thruster", "sensor suite", "radiator", "power supply") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm index b6e2934c8c2..61a525f5078 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm @@ -1,67 +1,67 @@ -// Marauders are even tougher than Durands. - -/datum/category_item/catalogue/technology/marauder - name = "Exosuit - Marauder" - desc = "Marauders are the more modern descendants of the Durand model. Stronger, faster, and \ - more resilient than their predecessor, they have replaced the Durand's role entirely, and are generally seen in service \ - for various militaries across human space. As such, they are generally unavailable to civilians, including \ - corporations and most Trans-Stellars." - value = CATALOGUER_REWARD_HARD - -/mob/living/simple_mob/mechanical/mecha/combat/marauder - name = "marauder" - desc = "A heavy-duty, combat exosuit, developed after the Durand model. This is rarely found among civilian populations." - catalogue_data = list(/datum/category_item/catalogue/technology/marauder) - icon_state = "marauder" - movement_cooldown = 1.5 - wreckage = /obj/structure/loot_pile/mecha/marauder - - maxHealth = 500 - deflect_chance = 25 - sight = SEE_SELF | SEE_MOBS - armor = list( - "melee" = 50, - "bullet" = 55, - "laser" = 40, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100 - ) - melee_damage_lower = 45 - melee_damage_upper = 45 - base_attack_cooldown = 2 SECONDS - projectiletype = /obj/item/projectile/beam/heavylaser - - - -/datum/category_item/catalogue/technology/seraph - name = "Exosuit - Seraph" - desc = "The Seraph line of combat exosuit is essentially a Marauder with incremental improvements, making \ - it slightly better. Due to the relatively minor improvements over its predecessor, and the cost of \ - said improvements, Seraphs have not made the Marauder obsolute. Instead, they have generally filled the \ - role of housing important commanders, and as such they generally contain specialized communications \ - equipment to aid in receiving and relaying orders.\ -

                    \ - Due to this role, they are generally not expected to see combat frequently. Despite this, they often have \ - one or more weapons attached, to allow for retaliation in case it is attacked directly." - value = CATALOGUER_REWARD_HARD - -// Slightly stronger, used to allow comdoms to frontline without dying instantly, I guess. -/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph - name = "seraph" - desc = "A heavy-duty, combat/command exosuit. This one is specialized towards housing important commanders such as high-ranking \ - military personnel. It's stronger than the regular Marauder model, but not by much." - catalogue_data = list(/datum/category_item/catalogue/technology/seraph) - icon_state = "seraph" - wreckage = /obj/structure/loot_pile/mecha/marauder/seraph - health = 550 - melee_damage_lower = 55 // The real version hits this hard apparently. Ouch. - melee_damage_upper = 55 - - -/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler - name = "mauler" - desc = "A heavy duty, combat exosuit that is based off of the Marauder model." - icon_state = "mauler" - wreckage = /obj/structure/loot_pile/mecha/marauder/mauler +// Marauders are even tougher than Durands. + +/datum/category_item/catalogue/technology/marauder + name = "Exosuit - Marauder" + desc = "Marauders are the more modern descendants of the Durand model. Stronger, faster, and \ + more resilient than their predecessor, they have replaced the Durand's role entirely, and are generally seen in service \ + for various militaries across human space. As such, they are generally unavailable to civilians, including \ + corporations and most Trans-Stellars." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/mechanical/mecha/combat/marauder + name = "marauder" + desc = "A heavy-duty, combat exosuit, developed after the Durand model. This is rarely found among civilian populations." + catalogue_data = list(/datum/category_item/catalogue/technology/marauder) + icon_state = "marauder" + movement_cooldown = 1.5 + wreckage = /obj/structure/loot_pile/mecha/marauder + + maxHealth = 500 + deflect_chance = 25 + sight = SEE_SELF | SEE_MOBS + armor = list( + "melee" = 50, + "bullet" = 55, + "laser" = 40, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + melee_damage_lower = 45 + melee_damage_upper = 45 + base_attack_cooldown = 2 SECONDS + projectiletype = /obj/item/projectile/beam/heavylaser + + + +/datum/category_item/catalogue/technology/seraph + name = "Exosuit - Seraph" + desc = "The Seraph line of combat exosuit is essentially a Marauder with incremental improvements, making \ + it slightly better. Due to the relatively minor improvements over its predecessor, and the cost of \ + said improvements, Seraphs have not made the Marauder obsolute. Instead, they have generally filled the \ + role of housing important commanders, and as such they generally contain specialized communications \ + equipment to aid in receiving and relaying orders.\ +

                    \ + Due to this role, they are generally not expected to see combat frequently. Despite this, they often have \ + one or more weapons attached, to allow for retaliation in case it is attacked directly." + value = CATALOGUER_REWARD_HARD + +// Slightly stronger, used to allow comdoms to frontline without dying instantly, I guess. +/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph + name = "seraph" + desc = "A heavy-duty, combat/command exosuit. This one is specialized towards housing important commanders such as high-ranking \ + military personnel. It's stronger than the regular Marauder model, but not by much." + catalogue_data = list(/datum/category_item/catalogue/technology/seraph) + icon_state = "seraph" + wreckage = /obj/structure/loot_pile/mecha/marauder/seraph + health = 550 + melee_damage_lower = 55 // The real version hits this hard apparently. Ouch. + melee_damage_upper = 55 + + +/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler + name = "mauler" + desc = "A heavy duty, combat exosuit that is based off of the Marauder model." + icon_state = "mauler" + wreckage = /obj/structure/loot_pile/mecha/marauder/mauler diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm index dc4733b6f0f..d5725084331 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm @@ -1,146 +1,146 @@ -// Mecha simple_mobs are essentially fake mechs. Generally tough and scary to fight. -// By default, they're automatically piloted by some kind of drone AI. They can be set to be "piloted" instead with a var. -// Tries to be as similar to the real deal as possible. - -/mob/living/simple_mob/mechanical/mecha - name = "mecha" - desc = "A big stompy mech!" - icon = 'icons/mecha/mecha.dmi' - - faction = "syndicate" - movement_cooldown = 1.5 - movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. - turn_sound = 'sound/mecha/mechturn.ogg' - maxHealth = 300 - mob_size = MOB_LARGE - damage_threshold = 5 //Anything that's 5 or less damage will not do damage. - - organ_names = /decl/mob_organ_names/mecha - - armor = list( - "melee" = 20, - "bullet" = 10, - "laser" = 0, - "energy" = 0, - "bomb" = 0, - "bio" = 100, - "rad" = 100 - ) - - response_help = "taps on" - response_disarm = "knocks on" - response_harm = "uselessly hits" - harm_intent_damage = 0 - - ai_holder_type = /datum/ai_holder/simple_mob/melee - say_list_type = /datum/say_list/malf_drone - - var/datum/effect/effect/system/spark_spread/sparks - var/wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark - var/pilot_type = null // Set to spawn a pilot when destroyed. Setting this also makes the mecha vulnerable to things that affect sentient minds. - var/deflect_chance = 10 // Chance to outright stop an attack, just like a normal exosuit. - var/has_repair_droid = FALSE // If true, heals 2 damage every tick and gets a repair droid overlay. - - -/mob/living/simple_mob/mechanical/mecha/Initialize() - sparks = new (src) - sparks.set_up(3, 1, src) - sparks.attach(src) - - if(!pilot_type) - name = "autonomous [initial(name)]" - desc = "[initial(desc)] It appears to be piloted by a drone intelligence." - else - say_list_type = /datum/say_list/merc - - if(has_repair_droid) - update_icon() - - return ..() - -/mob/living/simple_mob/mechanical/mecha/Destroy() - qdel_null(sparks) - return ..() - -/mob/living/simple_mob/mechanical/mecha/death() - ..(0,"explodes!") // Do everything else first. - - // Make the exploding more convincing with an actual explosion and some sparks. - sparks.start() - explosion(get_turf(src), 0, 0, 1, 3) - - // 'Eject' our pilot, if one exists. - if(pilot_type) - var/mob/living/L = new pilot_type(loc) - L.faction = src.faction - - new wreckage(loc) // Leave some wreckage. - - qdel(src) // Then delete us since we don't actually have a body. - -/mob/living/simple_mob/mechanical/mecha/handle_special() - if(has_repair_droid) - adjustBruteLoss(-2) - adjustFireLoss(-2) - adjustToxLoss(-2) - adjustOxyLoss(-2) - adjustCloneLoss(-2) - ..() - -/mob/living/simple_mob/mechanical/mecha/update_icon() - ..() // Cuts everything else, so do that first. - if(has_repair_droid) - add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) - -/mob/living/simple_mob/mechanical/mecha/bullet_act() - . = ..() - sparks.start() - -/mob/living/simple_mob/mechanical/mecha/speech_bubble_appearance() - return pilot_type ? "" : ..() - -// Piloted mechs are controlled by (presumably) something humanoid so they are vulnerable to certain things. -/mob/living/simple_mob/mechanical/mecha/is_sentient() - return pilot_type ? TRUE : FALSE - -/* -// Real mechs can't turn and run at the same time. This tries to simulate that. -// Commented out because the AI can't handle it sadly. -/mob/living/simple_mob/mechanical/mecha/SelfMove(turf/n, direct) - if(direct != dir) - set_dir(direct) - return FALSE // We didn't actually move, and returning FALSE means the mob can try to actually move almost immediately and not have to wait the full movement cooldown. - return ..() -*/ - -/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/item/projectile/P) - if(prob(deflect_chance)) - visible_message(span("warning", "\The [P] is deflected by \the [src]'s armor!")) - deflect_sprite() - return 0 - return ..() - -/mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() - var/image/deflect_image = image('icons/effects/effects.dmi', "deflect_static") - add_overlay(deflect_image) - sleep(1 SECOND) - cut_overlay(deflect_image) - qdel(deflect_image) -// flick_overlay_view(deflect_image, src, duration = 1 SECOND, gc_after = TRUE) - -/mob/living/simple_mob/mechanical/mecha/attackby(obj/item/I, mob/user) - if(prob(deflect_chance)) - visible_message(span("warning", "\The [user]'s [I] bounces off \the [src]'s armor!")) - deflect_sprite() - user.setClickCooldown(user.get_attack_speed(I)) - return - ..() - -/mob/living/simple_mob/mechanical/mecha/ex_act(severity) - if(prob(deflect_chance)) - severity++ // This somewhat misleadingly makes it less severe. - deflect_sprite() - ..(severity) - -/decl/mob_organ_names/mecha - hit_zones = list("central chassis", "control module", "hydraulics", "left arm", "right arm", "left leg", "right leg", "sensor suite", "radiator", "power supply", "left equipment mount", "right equipment mount") +// Mecha simple_mobs are essentially fake mechs. Generally tough and scary to fight. +// By default, they're automatically piloted by some kind of drone AI. They can be set to be "piloted" instead with a var. +// Tries to be as similar to the real deal as possible. + +/mob/living/simple_mob/mechanical/mecha + name = "mecha" + desc = "A big stompy mech!" + icon = 'icons/mecha/mecha.dmi' + + faction = "syndicate" + movement_cooldown = 1.5 + movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. + turn_sound = 'sound/mecha/mechturn.ogg' + maxHealth = 300 + mob_size = MOB_LARGE + damage_threshold = 5 //Anything that's 5 or less damage will not do damage. + + organ_names = /decl/mob_organ_names/mecha + + armor = list( + "melee" = 20, + "bullet" = 10, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + response_help = "taps on" + response_disarm = "knocks on" + response_harm = "uselessly hits" + harm_intent_damage = 0 + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/malf_drone + + var/datum/effect/effect/system/spark_spread/sparks + var/wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark + var/pilot_type = null // Set to spawn a pilot when destroyed. Setting this also makes the mecha vulnerable to things that affect sentient minds. + var/deflect_chance = 10 // Chance to outright stop an attack, just like a normal exosuit. + var/has_repair_droid = FALSE // If true, heals 2 damage every tick and gets a repair droid overlay. + + +/mob/living/simple_mob/mechanical/mecha/Initialize() + sparks = new (src) + sparks.set_up(3, 1, src) + sparks.attach(src) + + if(!pilot_type) + name = "autonomous [initial(name)]" + desc = "[initial(desc)] It appears to be piloted by a drone intelligence." + else + say_list_type = /datum/say_list/merc + + if(has_repair_droid) + update_icon() + + return ..() + +/mob/living/simple_mob/mechanical/mecha/Destroy() + qdel_null(sparks) + return ..() + +/mob/living/simple_mob/mechanical/mecha/death() + ..(0,"explodes!") // Do everything else first. + + // Make the exploding more convincing with an actual explosion and some sparks. + sparks.start() + explosion(get_turf(src), 0, 0, 1, 3) + + // 'Eject' our pilot, if one exists. + if(pilot_type) + var/mob/living/L = new pilot_type(loc) + L.faction = src.faction + + new wreckage(loc) // Leave some wreckage. + + qdel(src) // Then delete us since we don't actually have a body. + +/mob/living/simple_mob/mechanical/mecha/handle_special() + if(has_repair_droid) + adjustBruteLoss(-2) + adjustFireLoss(-2) + adjustToxLoss(-2) + adjustOxyLoss(-2) + adjustCloneLoss(-2) + ..() + +/mob/living/simple_mob/mechanical/mecha/update_icon() + ..() // Cuts everything else, so do that first. + if(has_repair_droid) + add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) + +/mob/living/simple_mob/mechanical/mecha/bullet_act() + . = ..() + sparks.start() + +/mob/living/simple_mob/mechanical/mecha/speech_bubble_appearance() + return pilot_type ? "" : ..() + +// Piloted mechs are controlled by (presumably) something humanoid so they are vulnerable to certain things. +/mob/living/simple_mob/mechanical/mecha/is_sentient() + return pilot_type ? TRUE : FALSE + +/* +// Real mechs can't turn and run at the same time. This tries to simulate that. +// Commented out because the AI can't handle it sadly. +/mob/living/simple_mob/mechanical/mecha/SelfMove(turf/n, direct) + if(direct != dir) + set_dir(direct) + return FALSE // We didn't actually move, and returning FALSE means the mob can try to actually move almost immediately and not have to wait the full movement cooldown. + return ..() +*/ + +/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/item/projectile/P) + if(prob(deflect_chance)) + visible_message(span("warning", "\The [P] is deflected by \the [src]'s armor!")) + deflect_sprite() + return 0 + return ..() + +/mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() + var/image/deflect_image = image('icons/effects/effects.dmi', "deflect_static") + add_overlay(deflect_image) + sleep(1 SECOND) + cut_overlay(deflect_image) + qdel(deflect_image) +// flick_overlay_view(deflect_image, src, duration = 1 SECOND, gc_after = TRUE) + +/mob/living/simple_mob/mechanical/mecha/attackby(obj/item/I, mob/user) + if(prob(deflect_chance)) + visible_message(span("warning", "\The [user]'s [I] bounces off \the [src]'s armor!")) + deflect_sprite() + user.setClickCooldown(user.get_attack_speed(I)) + return + ..() + +/mob/living/simple_mob/mechanical/mecha/ex_act(severity) + if(prob(deflect_chance)) + severity++ // This somewhat misleadingly makes it less severe. + deflect_sprite() + ..(severity) + +/decl/mob_organ_names/mecha + hit_zones = list("central chassis", "control module", "hydraulics", "left arm", "right arm", "left leg", "right leg", "sensor suite", "radiator", "power supply", "left equipment mount", "right equipment mount") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm index c97050e6f63..736bc6caf51 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm @@ -1,81 +1,81 @@ -// Shoots syringe-darts at enemies, which applies a stacking poison modifier that hurts over time. -// They also do this in melee. -// Fortunately they're quite fragile and don't fire that fast. - -/datum/category_item/catalogue/technology/odysseus - name = "Exosuit - Odysseus" - desc = "A Vey-Medical innovation, the Odysseus was designed to incorporate some of their \ - other inventions, such as the Sleeper, into a mobile frame. Due to its ability to safely \ - rescue injured people in potentially hostile environments such as vacuum, as well as its \ - agility compared to other civilian exosuits, the Odysseus dominates the market for \ - medical exosuits." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/mecha/odysseus - name = "odysseus" - desc = "These exosuits are developed and produced by Vey-Med. This one has a syringe gun." - catalogue_data = list( - /datum/category_item/catalogue/technology/odysseus, - /datum/category_item/catalogue/information/organization/vey_med - ) - icon_state = "odysseus" - wreckage = /obj/structure/loot_pile/mecha/odysseus - - maxHealth = 120 - movement_cooldown = -1 - turn_sound = 'sound/mecha/mechmove01.ogg' - - melee_damage_lower = 5 - melee_damage_upper = 5 - base_attack_cooldown = 2 SECONDS - attacktext = list("injected") - projectiletype = /obj/item/projectile/fake_syringe/poison - projectilesound = 'sound/weapons/empty.ogg' // Just like the syringe gun. - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/no_moonwalk - -/mob/living/simple_mob/mechanical/mecha/odysseus/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. - - -// Resprite of the regular one, perhaps for merc PoIs. -/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus - icon_state = "murdysseus" - wreckage = /obj/structure/loot_pile/mecha/odysseus/murdysseus - -/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged - - -/mob/living/simple_mob/mechanical/mecha/odysseus/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - - var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) - if(L.can_inject(src, null, target_zone)) - to_chat(L, span("warning", "You feel a tiny prick.")) - if(L.get_poison_protection() < 1) - L.add_modifier(/datum/modifier/poisoned, 30 SECONDS) - L.inflict_poison_damage(5) - - -// Fake syringe that tests if target can be injected before applying damage/modifiers/etc. -/obj/item/projectile/fake_syringe - name = "syringe" - icon_state = "syringe" - damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. - var/piercing = FALSE // If true, ignores thick material. - -/obj/item/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) - if(isliving(target)) - var/mob/living/L = target - if(!L.can_inject(null, null, def_zone, piercing)) - return FALSE - to_chat(L, span("warning", "You feel a tiny prick.")) - return ..() // This will add the modifier and return the correct value. - - -// Fake syringe, which inflicts a long lasting modifier that slowly kills them. -/obj/item/projectile/fake_syringe/poison - modifier_type_to_apply = /datum/modifier/poisoned - modifier_duration = 1 MINUTE // About 30 damage per stack over a minute. +// Shoots syringe-darts at enemies, which applies a stacking poison modifier that hurts over time. +// They also do this in melee. +// Fortunately they're quite fragile and don't fire that fast. + +/datum/category_item/catalogue/technology/odysseus + name = "Exosuit - Odysseus" + desc = "A Vey-Medical innovation, the Odysseus was designed to incorporate some of their \ + other inventions, such as the Sleeper, into a mobile frame. Due to its ability to safely \ + rescue injured people in potentially hostile environments such as vacuum, as well as its \ + agility compared to other civilian exosuits, the Odysseus dominates the market for \ + medical exosuits." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/mecha/odysseus + name = "odysseus" + desc = "These exosuits are developed and produced by Vey-Med. This one has a syringe gun." + catalogue_data = list( + /datum/category_item/catalogue/technology/odysseus, + /datum/category_item/catalogue/information/organization/vey_med + ) + icon_state = "odysseus" + wreckage = /obj/structure/loot_pile/mecha/odysseus + + maxHealth = 120 + movement_cooldown = -1 + turn_sound = 'sound/mecha/mechmove01.ogg' + + melee_damage_lower = 5 + melee_damage_upper = 5 + base_attack_cooldown = 2 SECONDS + attacktext = list("injected") + projectiletype = /obj/item/projectile/fake_syringe/poison + projectilesound = 'sound/weapons/empty.ogg' // Just like the syringe gun. + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/no_moonwalk + +/mob/living/simple_mob/mechanical/mecha/odysseus/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + + +// Resprite of the regular one, perhaps for merc PoIs. +/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus + icon_state = "murdysseus" + wreckage = /obj/structure/loot_pile/mecha/odysseus/murdysseus + +/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + + +/mob/living/simple_mob/mechanical/mecha/odysseus/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + to_chat(L, span("warning", "You feel a tiny prick.")) + if(L.get_poison_protection() < 1) + L.add_modifier(/datum/modifier/poisoned, 30 SECONDS) + L.inflict_poison_damage(5) + + +// Fake syringe that tests if target can be injected before applying damage/modifiers/etc. +/obj/item/projectile/fake_syringe + name = "syringe" + icon_state = "syringe" + damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. + var/piercing = FALSE // If true, ignores thick material. + +/obj/item/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) + if(isliving(target)) + var/mob/living/L = target + if(!L.can_inject(null, null, def_zone, piercing)) + return FALSE + to_chat(L, span("warning", "You feel a tiny prick.")) + return ..() // This will add the modifier and return the correct value. + + +// Fake syringe, which inflicts a long lasting modifier that slowly kills them. +/obj/item/projectile/fake_syringe/poison + modifier_type_to_apply = /datum/modifier/poisoned + modifier_duration = 1 MINUTE // About 30 damage per stack over a minute. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm index 562a686385c..2e516da556f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm @@ -1,21 +1,21 @@ -// Phazons are weird. - -/mob/living/simple_mob/mechanical/mecha/combat/phazon - name = "phazon" - desc = "An extremly enigmatic exosuit." - icon_state = "phazon" - movement_cooldown = 1.5 - wreckage = /obj/structure/loot_pile/mecha/phazon - - maxHealth = 200 - deflect_chance = 30 - armor = list( - "melee" = 30, - "bullet" = 30, - "laser" = 30, - "energy" = 30, - "bomb" = 30, - "bio" = 100, - "rad" = 100 - ) - projectiletype = /obj/item/projectile/energy/declone +// Phazons are weird. + +/mob/living/simple_mob/mechanical/mecha/combat/phazon + name = "phazon" + desc = "An extremly enigmatic exosuit." + icon_state = "phazon" + movement_cooldown = 1.5 + wreckage = /obj/structure/loot_pile/mecha/phazon + + maxHealth = 200 + deflect_chance = 30 + armor = list( + "melee" = 30, + "bullet" = 30, + "laser" = 30, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + projectiletype = /obj/item/projectile/energy/declone diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm index 063802ca4b2..960e96a65e3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm @@ -1,75 +1,75 @@ -// Beefy, but somewhat slow. -// Melee attack is to bore you with its big drill, which has a lot of armor penetration and strikes rapidly. - -/datum/category_item/catalogue/technology/ripley - name = "Exosuit - APLU" - desc = "The Autonomous Power Loader Unit, more commonly designated as the 'Ripley', \ - is an exosuit that is often described as 'the workhorse of the exosuit world', \ - due to being designed for industrial use. Featuring a rugged design, they are fairly \ - resilient to the stresses of operation. As such, they are often used for various roles, \ - such as mining, construction, heavy lifting, and cargo transportation." - value = CATALOGUER_REWARD_EASY - - -/mob/living/simple_mob/mechanical/mecha/ripley - name = "\improper APLU ripley" - desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world. This one has big drill." - catalogue_data = list(/datum/category_item/catalogue/technology/ripley) - icon_state = "ripley" - wreckage = /obj/structure/loot_pile/mecha/ripley - - maxHealth = 200 - - melee_damage_lower = 10 - melee_damage_upper = 10 - base_attack_cooldown = 5 // About 20 DPS. - attack_armor_pen = 50 - attack_sharp = TRUE - attack_sound = 'sound/mecha/mechdrill.ogg' - attacktext = list("drilled", "bored", "pierced") - -/mob/living/simple_mob/mechanical/mecha/ripley/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. - -/mob/living/simple_mob/mechanical/mecha/ripley/red_flames - icon_state = "ripley_flames_red" - -/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames - icon_state = "ripley_flames_blue" - - -// Immune to heat damage, resistant to lasers, and somewhat beefier. Still tries to melee you. -/mob/living/simple_mob/mechanical/mecha/ripley/firefighter - name = "\improper APLU firefighter" - desc = "A standard APLU chassis, refitted with additional thermal protection and cistern. This one has a big drill." - icon_state = "firefighter" - wreckage = /obj/structure/loot_pile/mecha/ripley/firefighter - - maxHealth = 250 - heat_resist = 1 - armor = list( - "melee" = 0, - "bullet" = 20, - "laser" = 50, - "energy" = 0, - "bomb" = 50, - "bio" = 100, - "rad" = 100 - ) - -/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged - -// Mostly a joke mob, like the real DEATH-RIPLEY. -/mob/living/simple_mob/mechanical/mecha/ripley/deathripley - name = "\improper DEATH-RIPLEY" - desc = "OH SHIT RUN!!! IT HAS A KILL CLAMP!" - icon_state = "deathripley" - wreckage = /obj/structure/loot_pile/mecha/deathripley - - melee_damage_lower = 0 - melee_damage_upper = 0 - friendly = list("utterly obliterates", "furiously destroys", "permanently removes", "unflichingly decimates", "brutally murders", "absolutely demolishes", "completely annihilates") - -/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned - pilot_type = /mob/living/simple_mob/humanoid/merc/ranged +// Beefy, but somewhat slow. +// Melee attack is to bore you with its big drill, which has a lot of armor penetration and strikes rapidly. + +/datum/category_item/catalogue/technology/ripley + name = "Exosuit - APLU" + desc = "The Autonomous Power Loader Unit, more commonly designated as the 'Ripley', \ + is an exosuit that is often described as 'the workhorse of the exosuit world', \ + due to being designed for industrial use. Featuring a rugged design, they are fairly \ + resilient to the stresses of operation. As such, they are often used for various roles, \ + such as mining, construction, heavy lifting, and cargo transportation." + value = CATALOGUER_REWARD_EASY + + +/mob/living/simple_mob/mechanical/mecha/ripley + name = "\improper APLU ripley" + desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world. This one has big drill." + catalogue_data = list(/datum/category_item/catalogue/technology/ripley) + icon_state = "ripley" + wreckage = /obj/structure/loot_pile/mecha/ripley + + maxHealth = 200 + + melee_damage_lower = 10 + melee_damage_upper = 10 + base_attack_cooldown = 5 // About 20 DPS. + attack_armor_pen = 50 + attack_sharp = TRUE + attack_sound = 'sound/mecha/mechdrill.ogg' + attacktext = list("drilled", "bored", "pierced") + +/mob/living/simple_mob/mechanical/mecha/ripley/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + +/mob/living/simple_mob/mechanical/mecha/ripley/red_flames + icon_state = "ripley_flames_red" + +/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames + icon_state = "ripley_flames_blue" + + +// Immune to heat damage, resistant to lasers, and somewhat beefier. Still tries to melee you. +/mob/living/simple_mob/mechanical/mecha/ripley/firefighter + name = "\improper APLU firefighter" + desc = "A standard APLU chassis, refitted with additional thermal protection and cistern. This one has a big drill." + icon_state = "firefighter" + wreckage = /obj/structure/loot_pile/mecha/ripley/firefighter + + maxHealth = 250 + heat_resist = 1 + armor = list( + "melee" = 0, + "bullet" = 20, + "laser" = 50, + "energy" = 0, + "bomb" = 50, + "bio" = 100, + "rad" = 100 + ) + +/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + +// Mostly a joke mob, like the real DEATH-RIPLEY. +/mob/living/simple_mob/mechanical/mecha/ripley/deathripley + name = "\improper DEATH-RIPLEY" + desc = "OH SHIT RUN!!! IT HAS A KILL CLAMP!" + icon_state = "deathripley" + wreckage = /obj/structure/loot_pile/mecha/deathripley + + melee_damage_lower = 0 + melee_damage_upper = 0 + friendly = list("utterly obliterates", "furiously destroys", "permanently removes", "unflichingly decimates", "brutally murders", "absolutely demolishes", "completely annihilates") + +/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm index 6a98f7f44b4..b731653cc66 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm @@ -1,35 +1,35 @@ -// Ancient "soul"-infused mechanical shells. Used by Hlf'erath. - -/mob/living/simple_mob/mechanical/wahlem - name = "Ancient Mechanical Warrior" - desc = "This construct is made of a brass-like material. Though aged, its shine is still brilliant, as it hovers ominously over the battlefield. Red flames spew from its shell. It diligently holds its shield and blade, at the ready, for any threats that may threaten its existence." - icon = 'icons/tgstation/clockworkwarrior.dmi' - icon_state = "clockM" - health = 300 - maxHealth = 300 - - faction = "golem" - - response_help = "brushes over" - response_disarm = "repulses away" - response_harm = "slices" - harm_intent_damage = 3 - friendly = "tolerates" - - melee_damage_lower = 30 // It has an ancient magic sword. - melee_damage_upper = 30 - attack_sound = 'sound/weapons/bladeslice.ogg' - attacktext = list("slashed") - - ai_holder_type = /datum/ai_holder/simple_mob/melee - - // Cataloguer data below - strange we can catalogue space golem wizards -/datum/category_item/catalogue/technology/drone/wahlem - name = "Ancient Construct" - desc = "Some sort of ancient, mechanical warrior, built for combat.\ - It has a brass and red theme. It wields a brass, slightly broken shield in its right hand. It has a sharp, foot-long blade in its other hand..\ - The drone has pristine armor, golden and shiny, with red cracks in its armour glowing visibly from inside.\ -

                    \ - The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ - no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." - value = CATALOGUER_REWARD_MEDIUM +// Ancient "soul"-infused mechanical shells. Used by Hlf'erath. + +/mob/living/simple_mob/mechanical/wahlem + name = "Ancient Mechanical Warrior" + desc = "This construct is made of a brass-like material. Though aged, its shine is still brilliant, as it hovers ominously over the battlefield. Red flames spew from its shell. It diligently holds its shield and blade, at the ready, for any threats that may threaten its existence." + icon = 'icons/tgstation/clockworkwarrior.dmi' + icon_state = "clockM" + health = 300 + maxHealth = 300 + + faction = "golem" + + response_help = "brushes over" + response_disarm = "repulses away" + response_harm = "slices" + harm_intent_damage = 3 + friendly = "tolerates" + + melee_damage_lower = 30 // It has an ancient magic sword. + melee_damage_upper = 30 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("slashed") + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + // Cataloguer data below - strange we can catalogue space golem wizards +/datum/category_item/catalogue/technology/drone/wahlem + name = "Ancient Construct" + desc = "Some sort of ancient, mechanical warrior, built for combat.\ + It has a brass and red theme. It wields a brass, slightly broken shield in its right hand. It has a sharp, foot-long blade in its other hand..\ + The drone has pristine armor, golden and shiny, with red cracks in its armour glowing visibly from inside.\ +

                    \ + The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm index aa49ce806b1..21f5e787223 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -1,134 +1,134 @@ -/* - 'Monitor' wards are drones that yell at their creator if they see someone besides them that they are hostile to. - They can also force an invisible entity to uncloak if the invisible mob is hostile to the ward. - If AI controlled, they will also warn their faction if they see a hostile entity, acting as floating cameras. -*/ - -/datum/category_item/catalogue/technology/drone/ward - name = "Drone - Monitor Ward" - desc = "This is a small drone that appears to have been designed for a singular purpose, \ - with little autonomous capability, common among the 'ward' models. This specific ward's \ - purpose is simply to observe the environment around it, reacting when it detects entities \ - it judges to be unfriendly. It presumably relays information about what it sees back to \ - whoever owns the drone.\ -

                    \ - The sensors onboard the ward are much more advanced than what is typical for drones, \ - allowing it to detect entities that might otherwise go unnoticed by inferior \ - observers. If this ward sees such a thing, it fires a beam of energy at the hidden \ - entity, which exposes them." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/mechanical/ward/monitor - desc = "It's a little flying drone. This one seems to be watching you..." - catalogue_data = list(/datum/category_item/catalogue/technology/drone/ward) - icon_state = "ward" - glow_color = "#00FF00" - see_invisible = SEE_INVISIBLE_LEVEL_TWO - - has_eye_glow = TRUE - glow_range = 3 - glow_intensity = 3 - glow_toggle = TRUE - - player_msg = "You will automatically alert your owner (if one exists) of enemies you see nearby.
                    \ - You can also see invisible entities, and will automatically uncloak nearby invisible or hidden enemies." - - ai_holder_type = /datum/ai_holder/simple_mob/monitor - - var/list/seen_mobs = list() - var/view_range = 5 - -// For PoIs. -/mob/living/simple_mob/mechanical/ward/monitor/syndicate - faction = "syndicate" - -/mob/living/simple_mob/mechanical/ward/monitor/crew - icon_state = "ward-nt" - -/mob/living/simple_mob/mechanical/ward/monitor/crew/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, /obj/item/weapon/card/id) && !owner) - owner = user - return - ..() - -/mob/living/simple_mob/mechanical/ward/monitor/crew/IIsAlly(mob/living/L) - . = ..() - if(!.) - if(isrobot(L)) // They ignore synths. - return TRUE - return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 - -/mob/living/simple_mob/mechanical/ward/monitor/death() - if(owner) - to_chat(owner, span("warning", "Your [src.name] inside [get_area(src)] was destroyed!")) - ..() - -/mob/living/simple_mob/mechanical/ward/monitor/handle_special() - detect_mobs() - -/mob/living/simple_mob/mechanical/ward/monitor/update_icon() - if(seen_mobs.len) - icon_living = "[initial(icon_state)]_spotted" - glow_color = "#FF0000" - else - icon_living = "[initial(icon_state)]" - glow_color = "#00FF00" - handle_light() // Update the light immediately. - ..() - -/mob/living/simple_mob/mechanical/ward/monitor/proc/detect_mobs() - var/last_seen_mobs_len = seen_mobs.len - var/list/mobs_nearby = hearers(view_range, src) - var/list/newly_seen_mobs = list() - for(var/mob/living/L in mobs_nearby) - if(L == src) // Don't detect ourselves. - continue - - if(L.stat) // Dead mobs aren't concerning. - continue - - if(src.IIsAlly(L)) - continue - - // Decloak them . - if(L.is_cloaked()) - Beam(L, icon_state = "solar_beam", time = 5) - playsound(L, 'sound/effects/EMPulse.ogg', 75, 1) - L.break_cloak() - - to_chat(L, span("danger", "\The [src] disrupts your cloak!")) - if(owner) - to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] uncloaked \the [L].")) - - // Warn the owner when it sees a new mob. - if(!(L in seen_mobs)) - seen_mobs += L - newly_seen_mobs += L - - if(newly_seen_mobs.len && owner) // Yell at our owner if someone new shows up. - to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) - - // Now get rid of old mobs that left vision. - for(var/thing in seen_mobs) - if(!(thing in mobs_nearby)) - seen_mobs -= thing - - // Check if we need to update icon. - if(seen_mobs.len != last_seen_mobs_len) - update_icon() - - -// Can't attack but calls for help. Used by the monitor and spotter wards. -// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. -/datum/ai_holder/simple_mob/monitor - hostile = TRUE // Required to call for help. - cooperative = TRUE - stand_ground = TRUE // So it doesn't run up to the thing it sees. - wander = FALSE - can_flee = FALSE - -/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) - return FALSE - -/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) - return FALSE +/* + 'Monitor' wards are drones that yell at their creator if they see someone besides them that they are hostile to. + They can also force an invisible entity to uncloak if the invisible mob is hostile to the ward. + If AI controlled, they will also warn their faction if they see a hostile entity, acting as floating cameras. +*/ + +/datum/category_item/catalogue/technology/drone/ward + name = "Drone - Monitor Ward" + desc = "This is a small drone that appears to have been designed for a singular purpose, \ + with little autonomous capability, common among the 'ward' models. This specific ward's \ + purpose is simply to observe the environment around it, reacting when it detects entities \ + it judges to be unfriendly. It presumably relays information about what it sees back to \ + whoever owns the drone.\ +

                    \ + The sensors onboard the ward are much more advanced than what is typical for drones, \ + allowing it to detect entities that might otherwise go unnoticed by inferior \ + observers. If this ward sees such a thing, it fires a beam of energy at the hidden \ + entity, which exposes them." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/mechanical/ward/monitor + desc = "It's a little flying drone. This one seems to be watching you..." + catalogue_data = list(/datum/category_item/catalogue/technology/drone/ward) + icon_state = "ward" + glow_color = "#00FF00" + see_invisible = SEE_INVISIBLE_LEVEL_TWO + + has_eye_glow = TRUE + glow_range = 3 + glow_intensity = 3 + glow_toggle = TRUE + + player_msg = "You will automatically alert your owner (if one exists) of enemies you see nearby.
                    \ + You can also see invisible entities, and will automatically uncloak nearby invisible or hidden enemies." + + ai_holder_type = /datum/ai_holder/simple_mob/monitor + + var/list/seen_mobs = list() + var/view_range = 5 + +// For PoIs. +/mob/living/simple_mob/mechanical/ward/monitor/syndicate + faction = "syndicate" + +/mob/living/simple_mob/mechanical/ward/monitor/crew + icon_state = "ward-nt" + +/mob/living/simple_mob/mechanical/ward/monitor/crew/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/card/id) && !owner) + owner = user + return + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/crew/IIsAlly(mob/living/L) + . = ..() + if(!.) + if(isrobot(L)) // They ignore synths. + return TRUE + return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 + +/mob/living/simple_mob/mechanical/ward/monitor/death() + if(owner) + to_chat(owner, span("warning", "Your [src.name] inside [get_area(src)] was destroyed!")) + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/handle_special() + detect_mobs() + +/mob/living/simple_mob/mechanical/ward/monitor/update_icon() + if(seen_mobs.len) + icon_living = "[initial(icon_state)]_spotted" + glow_color = "#FF0000" + else + icon_living = "[initial(icon_state)]" + glow_color = "#00FF00" + handle_light() // Update the light immediately. + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/proc/detect_mobs() + var/last_seen_mobs_len = seen_mobs.len + var/list/mobs_nearby = hearers(view_range, src) + var/list/newly_seen_mobs = list() + for(var/mob/living/L in mobs_nearby) + if(L == src) // Don't detect ourselves. + continue + + if(L.stat) // Dead mobs aren't concerning. + continue + + if(src.IIsAlly(L)) + continue + + // Decloak them . + if(L.is_cloaked()) + Beam(L, icon_state = "solar_beam", time = 5) + playsound(L, 'sound/effects/EMPulse.ogg', 75, 1) + L.break_cloak() + + to_chat(L, span("danger", "\The [src] disrupts your cloak!")) + if(owner) + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] uncloaked \the [L].")) + + // Warn the owner when it sees a new mob. + if(!(L in seen_mobs)) + seen_mobs += L + newly_seen_mobs += L + + if(newly_seen_mobs.len && owner) // Yell at our owner if someone new shows up. + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) + + // Now get rid of old mobs that left vision. + for(var/thing in seen_mobs) + if(!(thing in mobs_nearby)) + seen_mobs -= thing + + // Check if we need to update icon. + if(seen_mobs.len != last_seen_mobs_len) + update_icon() + + +// Can't attack but calls for help. Used by the monitor and spotter wards. +// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. +/datum/ai_holder/simple_mob/monitor + hostile = TRUE // Required to call for help. + cooperative = TRUE + stand_ground = TRUE // So it doesn't run up to the thing it sees. + wander = FALSE + can_flee = FALSE + +/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) + return FALSE + +/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm index 9120bec7283..79aba5ce9d1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm @@ -1,47 +1,47 @@ -/* - Wards are a specific type of mechanical simplemob that generally fill a support role for their faction or for a specific mob. - Generally they are helpless by themselves and are fragile, but can do very useful things if protected. This makes them a high priority target. -*/ - -/mob/living/simple_mob/mechanical/ward - name = "ward" - desc = "A small floating machine. This one seems rather useless..." - icon = 'icons/mob/critter.dmi' - icon_state = "ward" - icon_living = "ward" - hovering = TRUE // Won't trigger landmines. - response_help = "pets" - response_disarm = "swats away" - response_harm = "punches" - faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. - - organ_names = /decl/mob_organ_names/ward - - maxHealth = 15 - health = 15 - movement_cooldown = -1 - hovering = TRUE - - mob_bump_flag = 0 - - melee_damage_lower = 0 - melee_damage_upper = 0 - - ai_holder_type = null - var/mob/living/owner = null // The mob that made the ward, if any. Used to ensure the ward does not interfere with its creator. - -/mob/living/simple_mob/mechanical/ward/death() - ..(null,"is smashed into pieces!") - qdel(src) - -/mob/living/simple_mob/mechanical/ward/Destroy() - owner = null - return ..() - -/mob/living/simple_mob/mechanical/ward/IIsAlly(mob/living/L) - if(owner == L) - return TRUE - return ..() - -/decl/mob_organ_names/ward - hit_zones = list("chassis", "sensor array", "hover thruster") +/* + Wards are a specific type of mechanical simplemob that generally fill a support role for their faction or for a specific mob. + Generally they are helpless by themselves and are fragile, but can do very useful things if protected. This makes them a high priority target. +*/ + +/mob/living/simple_mob/mechanical/ward + name = "ward" + desc = "A small floating machine. This one seems rather useless..." + icon = 'icons/mob/critter.dmi' + icon_state = "ward" + icon_living = "ward" + hovering = TRUE // Won't trigger landmines. + response_help = "pets" + response_disarm = "swats away" + response_harm = "punches" + faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. + + organ_names = /decl/mob_organ_names/ward + + maxHealth = 15 + health = 15 + movement_cooldown = -1 + hovering = TRUE + + mob_bump_flag = 0 + + melee_damage_lower = 0 + melee_damage_upper = 0 + + ai_holder_type = null + var/mob/living/owner = null // The mob that made the ward, if any. Used to ensure the ward does not interfere with its creator. + +/mob/living/simple_mob/mechanical/ward/death() + ..(null,"is smashed into pieces!") + qdel(src) + +/mob/living/simple_mob/mechanical/ward/Destroy() + owner = null + return ..() + +/mob/living/simple_mob/mechanical/ward/IIsAlly(mob/living/L) + if(owner == L) + return TRUE + return ..() + +/decl/mob_organ_names/ward + hit_zones = list("chassis", "sensor array", "hover thruster") diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm index f20d02b5c78..df9bed2d6a5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm @@ -43,11 +43,11 @@ ..() for(var/mob/M in viewers(src, null)) if((M.client && !( M.blinded ))) - M.show_message("[src] lets out a contented sigh as their form unwinds.") + M.show_message(span_red("[src] lets out a contented sigh as their form unwinds.")) ghostize() qdel(src) return /decl/mob_organ_names/shade - hit_zones = list("spectral robe", "featureless visage", "haunting glow") \ No newline at end of file + hit_zones = list("spectral robe", "featureless visage", "haunting glow") diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm index ea524c3c802..0cddbaa268c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -1,95 +1,95 @@ -// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. - -/mob/living/simple_mob/slime/feral - name = "feral slime" - desc = "The result of slimes escaping containment from some xenobiology lab. \ - Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ - creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." - description_info = "Note that processing this large slime will give six cores." - - cores = 6 // Xenobio will love getting their hands on these. - - icon_state = "slime adult" - icon_living = "slime adult" - icon_dead = "slime adult dead" - glow_range = 5 - glow_intensity = 4 - icon_scale_x = 2 // Twice as big as the xenobio variant. - icon_scale_y = 2 - pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. - default_pixel_y = -10 // To prevent resetting above var. - - maxHealth = 300 - movement_cooldown = -3 - melee_attack_delay = 0.5 SECONDS - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank - - -// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). -/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) - taunt(user, TRUE) - - -// *********** -// *Dark Blue* -// *********** - -// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. -// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. - -/mob/living/simple_mob/slime/feral/dark_blue - name = "dark blue feral slime" - color = "#2398FF" - glow_toggle = TRUE - slime_color = "dark blue" - coretype = /obj/item/slime_extract/dark_blue - cold_resist = 1 // Complete immunity. - minbodytemp = 0 - cold_damage_per_tick = 0 - - projectiletype = /obj/item/projectile/icicle - base_attack_cooldown = 2 SECONDS - ranged_attack_delay = 1 SECOND - - player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
                    \ - You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
                    \ - Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." - -/obj/item/projectile/icicle - name = "icicle" - icon_state = "ice_2" - damage = 40 - damage_type = BRUTE - check_armour = "melee" - armor_penetration = 30 - speed = 2 - icon_scale_x = 2 // It hits like a truck. - icon_scale_y = 2 - sharp = TRUE - -/obj/item/projectile/icicle/on_impact(atom/A) - playsound(A, "shatter", 70, 1) - return ..() - -/obj/item/projectile/icicle/get_structure_damage() - return damage / 2 // They're really deadly against mobs, but less effective against solid things. - -/mob/living/simple_mob/slime/feral/dark_blue/handle_special() - if(stat != DEAD) - cold_aura() - ..() - -/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() - for(var/mob/living/L in view(3, src)) - if(L == src) - continue - chill(L) - -/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) - L.inflict_cold_damage(10) - if(L.get_cold_protection() < 1) - L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) - - if(L.has_AI()) // Other AIs should react to hostile auras. - L.ai_holder.react_to_attack(src) +// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. + +/mob/living/simple_mob/slime/feral + name = "feral slime" + desc = "The result of slimes escaping containment from some xenobiology lab. \ + Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ + creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." + description_info = "Note that processing this large slime will give six cores." + + cores = 6 // Xenobio will love getting their hands on these. + + icon_state = "slime adult" + icon_living = "slime adult" + icon_dead = "slime adult dead" + glow_range = 5 + glow_intensity = 4 + icon_scale_x = 2 // Twice as big as the xenobio variant. + icon_scale_y = 2 + pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. + default_pixel_y = -10 // To prevent resetting above var. + + maxHealth = 300 + movement_cooldown = -3 + melee_attack_delay = 0.5 SECONDS + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank + + +// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). +/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) + taunt(user, TRUE) + + +// *********** +// *Dark Blue* +// *********** + +// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. +// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. + +/mob/living/simple_mob/slime/feral/dark_blue + name = "dark blue feral slime" + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + cold_resist = 1 // Complete immunity. + minbodytemp = 0 + cold_damage_per_tick = 0 + + projectiletype = /obj/item/projectile/icicle + base_attack_cooldown = 2 SECONDS + ranged_attack_delay = 1 SECOND + + player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
                    \ + You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
                    \ + Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." + +/obj/item/projectile/icicle + name = "icicle" + icon_state = "ice_2" + damage = 40 + damage_type = BRUTE + check_armour = "melee" + armor_penetration = 30 + speed = 2 + icon_scale_x = 2 // It hits like a truck. + icon_scale_y = 2 + sharp = TRUE + +/obj/item/projectile/icicle/on_impact(atom/A) + playsound(A, "shatter", 70, 1) + return ..() + +/obj/item/projectile/icicle/get_structure_damage() + return damage / 2 // They're really deadly against mobs, but less effective against solid things. + +/mob/living/simple_mob/slime/feral/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(3, src)) + if(L == src) + continue + chill(L) + +/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(10) + if(L.get_cold_protection() < 1) + L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) + + if(L.has_AI()) // Other AIs should react to hostile auras. + L.ai_holder.react_to_attack(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm index d74f2b4fcbd..03565489cdc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm @@ -1,83 +1,83 @@ -// Code for slimes attacking other things. - -// Slime attacks change based on intent. -/mob/living/simple_mob/slime/xenobio/apply_attack(mob/living/L, damage_to_do) - if(istype(L)) - switch(a_intent) - if(I_HELP) // This shouldn't happen but just in case. - return FALSE - - if(I_DISARM) - var/stun_power = between(0, power_charge + rand(0, 3), 10) - - if(ishuman(L)) - var/mob/living/carbon/human/H = L - stun_power *= max(H.species.siemens_coefficient, 0) - - if(prob(stun_power * 10)) // Try an electric shock. - power_charge = max(0, power_charge - 3) - L.visible_message( - span("danger", "\The [src] has shocked \the [L]!"), - span("danger", "\The [src] has shocked you!") - ) - playsound(src, 'sound/weapons/Egloves.ogg', 75, 1) - L.Weaken(4) - L.Stun(4) - do_attack_animation(L) - if(L.buckled) - L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. - L.stuttering = max(L.stuttering, stun_power) - - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(5, 1, L) - s.start() - - if(prob(stun_power * 10) && stun_power >= 8) - L.adjustFireLoss(power_charge * rand(1, 2)) - return FALSE - - else if(prob(20)) // Try to do a regular disarm attack. - L.visible_message( - span("danger", "\The [src] has pounced at \the [L]!"), - span("danger", "\The [src] has pounced at you!") - ) - playsound(src, 'sound/weapons/thudswoosh.ogg', 75, 1) - L.Weaken(2) - do_attack_animation(L) - if(L.buckled) - L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. - return FALSE - - else // Failed to do anything this time. - L.visible_message( - span("warning", "\The [src] has tried to pounce at \the [L]!"), - span("warning", "\The [src] has tried to pounce at you!") - ) - playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) - do_attack_animation(L) - return FALSE - - if(I_GRAB) - start_consuming(L) - return FALSE - - if(I_HURT) - return ..() // Regular stuff. - else - return ..() // Do the regular stuff if we're hitting a window/mech/etc. - -/mob/living/simple_mob/slime/xenobio/apply_melee_effects(mob/living/L) - if(istype(L) && a_intent == I_HURT) - // Pump them full of toxins, if able. - if(L.reagents && L.can_inject() && reagent_injected) - L.reagents.add_reagent(reagent_injected, injection_amount) - - // Feed off of their flesh, if able. - consume(L, 5) - - -/mob/living/simple_mob/slime/xenobio/AltClickOn(atom/movable/A) - if(isliving(A) && Adjacent(A)) - animal_nom(A) - else +// Code for slimes attacking other things. + +// Slime attacks change based on intent. +/mob/living/simple_mob/slime/xenobio/apply_attack(mob/living/L, damage_to_do) + if(istype(L)) + switch(a_intent) + if(I_HELP) // This shouldn't happen but just in case. + return FALSE + + if(I_DISARM) + var/stun_power = between(0, power_charge + rand(0, 3), 10) + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + stun_power *= max(H.species.siemens_coefficient, 0) + + if(prob(stun_power * 10)) // Try an electric shock. + power_charge = max(0, power_charge - 3) + L.visible_message( + span("danger", "\The [src] has shocked \the [L]!"), + span("danger", "\The [src] has shocked you!") + ) + playsound(src, 'sound/weapons/Egloves.ogg', 75, 1) + L.Weaken(4) + L.Stun(4) + do_attack_animation(L) + if(L.buckled) + L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. + L.stuttering = max(L.stuttering, stun_power) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, L) + s.start() + + if(prob(stun_power * 10) && stun_power >= 8) + L.adjustFireLoss(power_charge * rand(1, 2)) + return FALSE + + else if(prob(20)) // Try to do a regular disarm attack. + L.visible_message( + span("danger", "\The [src] has pounced at \the [L]!"), + span("danger", "\The [src] has pounced at you!") + ) + playsound(src, 'sound/weapons/thudswoosh.ogg', 75, 1) + L.Weaken(2) + do_attack_animation(L) + if(L.buckled) + L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. + return FALSE + + else // Failed to do anything this time. + L.visible_message( + span("warning", "\The [src] has tried to pounce at \the [L]!"), + span("warning", "\The [src] has tried to pounce at you!") + ) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + do_attack_animation(L) + return FALSE + + if(I_GRAB) + start_consuming(L) + return FALSE + + if(I_HURT) + return ..() // Regular stuff. + else + return ..() // Do the regular stuff if we're hitting a window/mech/etc. + +/mob/living/simple_mob/slime/xenobio/apply_melee_effects(mob/living/L) + if(istype(L) && a_intent == I_HURT) + // Pump them full of toxins, if able. + if(L.reagents && L.can_inject() && reagent_injected) + L.reagents.add_reagent(reagent_injected, injection_amount) + + // Feed off of their flesh, if able. + consume(L, 5) + + +/mob/living/simple_mob/slime/xenobio/AltClickOn(atom/movable/A) + if(isliving(A) && Adjacent(A)) + animal_nom(A) + else ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm index d6b5948c264..0764c01de68 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm @@ -1,172 +1,172 @@ -// Handles hunger, starvation, growth, and eatting humans. - -/mob/living/simple_mob/slime/xenobio/adjust_nutrition(input, var/heal = 1) - ..(input) - - if(input > 0) - // Gain around one level per 50 nutrition. - if(prob(input * 2)) - power_charge = min(power_charge++, 10) - if(power_charge == 10) - adjustToxLoss(-10) - - // Heal 1 point of damage per 5 nutrition coming in. - if(heal) - adjustBruteLoss(-input * 0.2) - adjustFireLoss(-input * 0.2) - adjustToxLoss(-input * 0.2) - adjustOxyLoss(-input * 0.2) - adjustCloneLoss(-input * 0.2) - -/mob/living/simple_mob/slime/xenobio/proc/get_grow_nutrition() // Above it we grow, below it we can eat - return is_adult ? 1000 : 800 - -/mob/living/simple_mob/slime/xenobio/proc/get_hunger_nutrition() // Below it we will always eat - return is_adult ? 600 : 500 - -/mob/living/simple_mob/slime/xenobio/proc/get_starve_nutrition() // Below it we will eat before everything else - return is_adult ? 300 : 200 - -// Called by Life(). -/mob/living/simple_mob/slime/xenobio/proc/handle_nutrition() - if(harmless) - return - - if(prob(15)) - adjust_nutrition(is_adult ? -2 : -1) // Adult slimes get hungry faster. - - if(nutrition <= get_starve_nutrition()) - handle_starvation() - - else if(nutrition >= get_grow_nutrition() && amount_grown < 10) - adjust_nutrition(-20) - amount_grown = between(0, amount_grown + 1, 10) - -// Called if above proc happens while below a nutrition threshold. -/mob/living/simple_mob/slime/xenobio/proc/handle_starvation() - if(nutrition < get_starve_nutrition() && !client) // if a slime is starving, it starts losing its friends - if(friends.len && prob(1)) - var/mob/nofriend = pick(friends) - if(nofriend) - friends -= nofriend - say("[nofriend]... food now...") - - if(nutrition <= 0) - adjustToxLoss(rand(1,3)) - if(client && prob(5)) - to_chat(src, span("danger", "You are starving!")) - - -/mob/living/simple_mob/slime/xenobio/proc/handle_consumption() - if(victim && !stat) - if(istype(victim) && consume(victim, 20)) - if(prob(25)) - to_chat(src, span("notice", "You continue absorbing \the [victim].")) - - else - var/list/feedback = list( - "This subject is incompatible", - "This subject does not have a life energy", - "This subject is empty", - "I am not satisfied", - "I can not feed from this subject", - "I do not feel nourished", - "This subject is not food" - ) - to_chat(src, span("warning", "[pick(feedback)]...")) - stop_consumption() - - if(victim) - victim.updatehealth() - - else - stop_consumption() - -/mob/living/simple_mob/slime/xenobio/proc/start_consuming(mob/living/L) - if(!can_consume(L)) - return - if(!Adjacent(L)) - return - - step_towards(src, L) // Get on top of them to feed. - if(loc != L.loc) - return - - if(L.buckle_mob(src, forced = TRUE)) - victim = L - update_icon() - set_AI_busy(TRUE) // Don't want the AI to interfere with eatting. - victim.visible_message( - span("danger", "\The [src] latches onto \the [victim]!"), - span("critical", "\The [src] latches onto you!") - ) - -/mob/living/simple_mob/slime/xenobio/proc/stop_consumption(mob/living/L) - if(!victim) - return - victim.unbuckle_mob() - victim.visible_message( - span("notice", "\The [src] slides off of [victim]!"), - span("notice", "\The [src] slides off of you!") - ) - victim = null - update_icon() - set_AI_busy(FALSE) // Resume normal operations. - -/mob/living/simple_mob/slime/xenobio/proc/can_consume(mob/living/L) - if(!L || !istype(L)) - to_chat(src, "This subject is incomparable...") - return FALSE - if(harmless) - to_chat(src, "I am pacified... I cannot eat...") - return FALSE - if(L.mob_class & MOB_CLASS_SLIME) - to_chat(src, "I cannot feed on other slimes...") - return FALSE - if(L.isSynthetic()) - to_chat(src, "This subject is not biological...") - return FALSE - if(L.getarmor(null, "bio") >= 75) - to_chat(src, "I cannot reach this subject's biological matter...") - return FALSE - if(!Adjacent(L)) - to_chat(src, "This subject is too far away...") - return FALSE - if(L.getCloneLoss() >= L.getMaxHealth() * 1.5) - to_chat(src, "This subject does not have an edible life energy...") - return FALSE - //VOREStation Addition start - if(istype(L, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = L - if(H.species.flags & NO_SCAN) - to_chat(src, "This subject's life energy is beyond my reach...") - return FALSE - //VOREStation Addition end - if(L.has_buckled_mobs()) - for(var/A in L.buckled_mobs) - if(istype(A, /mob/living/simple_mob/slime/xenobio)) - if(A != src) - to_chat(src, "\The [A] is already feeding on this subject...") - return FALSE - return TRUE - -// This does the actual damage, as well as give nutrition and heals. -// Assuming no bio armor, calling consume(10) will result in; -// 6 clone damage to victim -// 4 tox damage to victim. -// 25 nutrition for the slime. -// 2 points of damage healed on the slime (as a result of the nutrition). -// 50% of giving +1 charge to the slime (same as above). -/mob/living/simple_mob/slime/xenobio/proc/consume(mob/living/victim, amount) - if(can_consume(victim)) - var/armor_modifier = abs((victim.getarmor(null, "bio") / 100) - 1) - var/damage_done = amount * armor_modifier - if(damage_done > 0) - victim.adjustCloneLoss(damage_done * 0.6) - victim.adjustToxLoss(damage_done * 0.4) - adjust_nutrition(damage_done * 5) - Beam(victim, icon_state = "slime_consume", time = 8) - to_chat(src, span("notice", "You absorb some biomaterial from \the [victim].")) - to_chat(victim, span("danger", "\The [src] consumes some of your flesh!")) - return TRUE - return FALSE +// Handles hunger, starvation, growth, and eatting humans. + +/mob/living/simple_mob/slime/xenobio/adjust_nutrition(input, var/heal = 1) + ..(input) + + if(input > 0) + // Gain around one level per 50 nutrition. + if(prob(input * 2)) + power_charge = min(power_charge++, 10) + if(power_charge == 10) + adjustToxLoss(-10) + + // Heal 1 point of damage per 5 nutrition coming in. + if(heal) + adjustBruteLoss(-input * 0.2) + adjustFireLoss(-input * 0.2) + adjustToxLoss(-input * 0.2) + adjustOxyLoss(-input * 0.2) + adjustCloneLoss(-input * 0.2) + +/mob/living/simple_mob/slime/xenobio/proc/get_grow_nutrition() // Above it we grow, below it we can eat + return is_adult ? 1000 : 800 + +/mob/living/simple_mob/slime/xenobio/proc/get_hunger_nutrition() // Below it we will always eat + return is_adult ? 600 : 500 + +/mob/living/simple_mob/slime/xenobio/proc/get_starve_nutrition() // Below it we will eat before everything else + return is_adult ? 300 : 200 + +// Called by Life(). +/mob/living/simple_mob/slime/xenobio/proc/handle_nutrition() + if(harmless) + return + + if(prob(15)) + adjust_nutrition(is_adult ? -2 : -1) // Adult slimes get hungry faster. + + if(nutrition <= get_starve_nutrition()) + handle_starvation() + + else if(nutrition >= get_grow_nutrition() && amount_grown < 10) + adjust_nutrition(-20) + amount_grown = between(0, amount_grown + 1, 10) + +// Called if above proc happens while below a nutrition threshold. +/mob/living/simple_mob/slime/xenobio/proc/handle_starvation() + if(nutrition < get_starve_nutrition() && !client) // if a slime is starving, it starts losing its friends + if(friends.len && prob(1)) + var/mob/nofriend = pick(friends) + if(nofriend) + friends -= nofriend + say("[nofriend]... food now...") + + if(nutrition <= 0) + adjustToxLoss(rand(1,3)) + if(client && prob(5)) + to_chat(src, span("danger", "You are starving!")) + + +/mob/living/simple_mob/slime/xenobio/proc/handle_consumption() + if(victim && !stat) + if(istype(victim) && consume(victim, 20)) + if(prob(25)) + to_chat(src, span("notice", "You continue absorbing \the [victim].")) + + else + var/list/feedback = list( + "This subject is incompatible", + "This subject does not have a life energy", + "This subject is empty", + "I am not satisfied", + "I can not feed from this subject", + "I do not feel nourished", + "This subject is not food" + ) + to_chat(src, span("warning", "[pick(feedback)]...")) + stop_consumption() + + if(victim) + victim.updatehealth() + + else + stop_consumption() + +/mob/living/simple_mob/slime/xenobio/proc/start_consuming(mob/living/L) + if(!can_consume(L)) + return + if(!Adjacent(L)) + return + + step_towards(src, L) // Get on top of them to feed. + if(loc != L.loc) + return + + if(L.buckle_mob(src, forced = TRUE)) + victim = L + update_icon() + set_AI_busy(TRUE) // Don't want the AI to interfere with eatting. + victim.visible_message( + span("danger", "\The [src] latches onto \the [victim]!"), + span("critical", "\The [src] latches onto you!") + ) + +/mob/living/simple_mob/slime/xenobio/proc/stop_consumption(mob/living/L) + if(!victim) + return + victim.unbuckle_mob() + victim.visible_message( + span("notice", "\The [src] slides off of [victim]!"), + span("notice", "\The [src] slides off of you!") + ) + victim = null + update_icon() + set_AI_busy(FALSE) // Resume normal operations. + +/mob/living/simple_mob/slime/xenobio/proc/can_consume(mob/living/L) + if(!L || !istype(L)) + to_chat(src, "This subject is incomparable...") + return FALSE + if(harmless) + to_chat(src, "I am pacified... I cannot eat...") + return FALSE + if(L.mob_class & MOB_CLASS_SLIME) + to_chat(src, "I cannot feed on other slimes...") + return FALSE + if(L.isSynthetic()) + to_chat(src, "This subject is not biological...") + return FALSE + if(L.getarmor(null, "bio") >= 75) + to_chat(src, "I cannot reach this subject's biological matter...") + return FALSE + if(!Adjacent(L)) + to_chat(src, "This subject is too far away...") + return FALSE + if(L.getCloneLoss() >= L.getMaxHealth() * 1.5) + to_chat(src, "This subject does not have an edible life energy...") + return FALSE + //VOREStation Addition start + if(istype(L, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = L + if(H.species.flags & NO_SCAN) + to_chat(src, "This subject's life energy is beyond my reach...") + return FALSE + //VOREStation Addition end + if(L.has_buckled_mobs()) + for(var/A in L.buckled_mobs) + if(istype(A, /mob/living/simple_mob/slime/xenobio)) + if(A != src) + to_chat(src, "\The [A] is already feeding on this subject...") + return FALSE + return TRUE + +// This does the actual damage, as well as give nutrition and heals. +// Assuming no bio armor, calling consume(10) will result in; +// 6 clone damage to victim +// 4 tox damage to victim. +// 25 nutrition for the slime. +// 2 points of damage healed on the slime (as a result of the nutrition). +// 50% of giving +1 charge to the slime (same as above). +/mob/living/simple_mob/slime/xenobio/proc/consume(mob/living/victim, amount) + if(can_consume(victim)) + var/armor_modifier = abs((victim.getarmor(null, "bio") / 100) - 1) + var/damage_done = amount * armor_modifier + if(damage_done > 0) + victim.adjustCloneLoss(damage_done * 0.6) + victim.adjustToxLoss(damage_done * 0.4) + adjust_nutrition(damage_done * 5) + Beam(victim, icon_state = "slime_consume", time = 8) + to_chat(src, span("notice", "You absorb some biomaterial from \the [victim].")) + to_chat(victim, span("danger", "\The [src] consumes some of your flesh!")) + return TRUE + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm index 5360dab7b33..0ab838f329a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm @@ -1,23 +1,23 @@ -// Handles the subjugation of slimes by force. -// Mostly a way for things to talk to the AI indirectly. - -/mob/living/simple_mob/slime/xenobio/proc/adjust_discipline(amount, silent) - if(amount > 0) - to_chat(src, span("warning", "You've been disciplined!")) - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.adjust_discipline(amount, silent) - - -/mob/living/simple_mob/slime/xenobio/proc/is_justified_to_discipline() - if(victim) // Punish if eating someone that isn't a monkey. - if(ishuman(victim)) - var/mob/living/carbon/human/H = victim - if(istype(H.species, /datum/species/monkey)) - return FALSE - return TRUE - - else if(has_AI()) // Now for thoughtcrimes. - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - return AI.is_justified_to_discipline() // Will return true if targeting a non-monkey. - return FALSE +// Handles the subjugation of slimes by force. +// Mostly a way for things to talk to the AI indirectly. + +/mob/living/simple_mob/slime/xenobio/proc/adjust_discipline(amount, silent) + if(amount > 0) + to_chat(src, span("warning", "You've been disciplined!")) + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.adjust_discipline(amount, silent) + + +/mob/living/simple_mob/slime/xenobio/proc/is_justified_to_discipline() + if(victim) // Punish if eating someone that isn't a monkey. + if(ishuman(victim)) + var/mob/living/carbon/human/H = victim + if(istype(H.species, /datum/species/monkey)) + return FALSE + return TRUE + + else if(has_AI()) // Now for thoughtcrimes. + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + return AI.is_justified_to_discipline() // Will return true if targeting a non-monkey. + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm index a28fb93e98a..d3b569a113c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm @@ -1,788 +1,788 @@ -// Here are where all the other colors of slime live. -// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. - -// Tier 1. - -/mob/living/simple_mob/slime/xenobio/purple - desc = "This slime is rather toxic to handle, as it is poisonous." - color = "#CC23FF" - slime_color = "purple" - coretype = /obj/item/slime_extract/purple - reagent_injected = "toxin" - - description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." - player_msg = "You inject a harmful toxin when attacking." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/orange - desc = "This slime is known to be flammable and can ignite enemies." - color = "#FFA723" - slime_color = "orange" - coretype = /obj/item/slime_extract/orange - melee_damage_lower = 5 - melee_damage_upper = 5 - heat_resist = 1 - - description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ - A firesuit can protect from the burning attacks of this slime." - player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
                    \ - You are also immune to burning attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.inflict_heat_damage(is_adult ? 10 : 5) - to_chat(src, span("span", "You burn \the [L].")) - to_chat(L, span("danger", "You've been burned by \the [src]!")) - L.adjust_fire_stacks(1) - if(prob(12)) - L.IgniteMob() - -/mob/living/simple_mob/slime/xenobio/blue - desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." - color = "#19FFFF" - slime_color = "blue" - coretype = /obj/item/slime_extract/blue - reagent_injected = "cryotoxin" - cold_resist = 0.50 // Not as strong as dark blue, which has immunity. - - description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ - A biosuit or other thick armor can protect from the injection." - player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
                    \ - You are also resistant to cold attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio - ) - - -/mob/living/simple_mob/slime/xenobio/metal - desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." - color = "#5F5F5F" - slime_color = "metal" - shiny = TRUE - coretype = /obj/item/slime_extract/metal - - description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." - player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." - - maxHealth = 250 - maxHealth_adult = 350 - - // The sloped armor. - // It's resistant to most weapons (but a spraybottle still kills it rather fast). - armor = list( - "melee" = 25, - "bullet" = 25, - "laser" = 25, - "energy" = 50, - "bomb" = 80, - "bio" = 100, - "rad" = 100 - ) - - armor_soak = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 0, - "bomb" = 0, - "bio" = 0, - "rad" = 0 - ) - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio - ) - -/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.taunt(src, TRUE) // We're the party tank now. - -// Tier 2 - -/mob/living/simple_mob/slime/xenobio/yellow - desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." - color = "#FFF423" - slime_color = "yellow" - coretype = /obj/item/slime_extract/yellow - melee_damage_lower = 5 - melee_damage_upper = 5 - shock_resist = 1 - - projectiletype = /obj/item/projectile/beam/lightning/slime - projectilesound = 'sound/effects/lightningbolt.ogg' - glow_toggle = TRUE - - description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ - enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ - attack faster than usual. Insulative or reflective armor can protect from these attacks." - player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
                    \ - You are also immune to shocking attacks." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/orange - ) - -/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - L.inflict_shock_damage(is_adult ? 10 : 5) - to_chat(src, span("span", "You shock \the [L].")) - to_chat(L, span("danger", "You've been shocked by \the [src]!")) - -/mob/living/simple_mob/slime/xenobio/yellow/handle_special() - if(stat == CONSCIOUS) - if(prob(25)) - power_charge = between(0, power_charge + 1, 10) - ..() - -/obj/item/projectile/beam/lightning/slime - power = 10 - fire_sound = 'sound/effects/lightningbolt.ogg' - - -/mob/living/simple_mob/slime/xenobio/dark_purple - desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." - color = "#660088" - slime_color = "dark purple" - coretype = /obj/item/slime_extract/dark_purple - reagent_injected = "phoron" - - description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ - If hit with a burning attack, it will erupt in flames." - player_msg = "You inject phoron into enemies you attack.
                    \ - You will erupt into flames if harmed by fire!" - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/orange, - /mob/living/simple_mob/slime/xenobio/ruby, - /mob/living/simple_mob/slime/xenobio/ruby - ) - -/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() - visible_message(span("critical", "\The [src] erupts in an inferno!")) - for(var/turf/simulated/target_turf in view(2, src)) - target_turf.assume_gas("phoron", 30, 1500+T0C) - spawn(0) - target_turf.hotspot_expose(1500+T0C, 400) - qdel(src) - -/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) - log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") - ignite() - -/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) - log_and_message_admins("[src] ignited due to exposure to fire.") - ignite() - -/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") - ignite() - else - ..() - -/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) - if(istype(W) && W.force && W.damtype == BURN) - log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") - ignite() - else - ..() - - - -/mob/living/simple_mob/slime/xenobio/dark_blue - desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." - color = "#2398FF" - glow_toggle = TRUE - slime_color = "dark blue" - coretype = /obj/item/slime_extract/dark_blue - melee_damage_lower = 5 - melee_damage_upper = 5 - cold_resist = 1 - - description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ - also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." - player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/cerulean, - /mob/living/simple_mob/slime/xenobio/cerulean - ) - - minbodytemp = 0 - cold_damage_per_tick = 0 - -/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() - if(stat != DEAD) - cold_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() - for(var/mob/living/L in view(2, src)) - if(L == src) - continue - chill(L) - - var/turf/T = get_turf(src) - var/datum/gas_mixture/env = T.return_air() - if(env) - env.add_thermal_energy(-10 * 1000) - -/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) - ..() - if(isliving(A)) - var/mob/living/L = A - chill(L) - to_chat(src, span("span", "You chill \the [L].")) - to_chat(L, span("danger", "You've been chilled by \the [src]!")) - - -/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) - L.inflict_cold_damage(is_adult ? 10 : 5) - if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. - L.ai_holder.react_to_attack(src) - - -/mob/living/simple_mob/slime/xenobio/silver - desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." - color = "#AAAAAA" - slime_color = "silver" - coretype = /obj/item/slime_extract/silver - shiny = TRUE - - description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." - player_msg = "You automatically reflect lasers, beams, and tasers that hit you." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/amber, - /mob/living/simple_mob/slime/xenobio/amber - ) - -/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) - if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) - visible_message(span("danger", "\The [src] reflects \the [P]!")) - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(src) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = TRUE - return PROJECTILE_CONTINUE // complete projectile permutation - else - ..() - - -// Tier 3 - -/mob/living/simple_mob/slime/xenobio/bluespace - desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." - color = null - slime_color = "bluespace" - icon_state_override = "bluespace" - coretype = /obj/item/slime_extract/bluespace - - description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." - player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/bluespace, - /mob/living/simple_mob/slime/xenobio/yellow, - /mob/living/simple_mob/slime/xenobio/yellow - ) - - special_attack_min_range = 3 - special_attack_max_range = 7 - special_attack_cooldown = 5 SECONDS - -/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) - // Teleport attack. - if(!A) - to_chat(src, span("warning", "There's nothing to teleport to.")) - return FALSE - - var/list/nearby_things = range(1, A) - var/list/valid_turfs = list() - - // All this work to just go to a non-dense tile. - for(var/turf/potential_turf in nearby_things) - var/valid_turf = TRUE - if(potential_turf.density) - continue - for(var/atom/movable/AM in potential_turf) - if(AM.density) - valid_turf = FALSE - if(valid_turf) - valid_turfs.Add(potential_turf) - - if(!(valid_turfs.len)) - to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) - return FALSE - - var/turf/target_turf = pick(valid_turfs) - var/turf/T = get_turf(src) - - var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread - s1.set_up(5, 1, T) - var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread - s2.set_up(5, 1, target_turf) - - - T.visible_message(span("notice", "\The [src] vanishes!")) - s1.start() - - forceMove(target_turf) - playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) - to_chat(src, span("notice", "You teleport to \the [target_turf].")) - - target_turf.visible_message(span("warning", "\The [src] appears!")) - s2.start() - - if(Adjacent(A)) - attack_target(A) - - -/mob/living/simple_mob/slime/xenobio/ruby - desc = "This slime has great physical strength." - color = "#FF3333" - slime_color = "ruby" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/ruby - - description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ - Their glomp attacks also send the victim flying." - player_msg = "Your attacks knock back the target a fair distance.
                    \ - You also hit harder, take less damage, and stuns affect you for less time." - - melee_attack_delay = 1 SECOND - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/dark_purple, - /mob/living/simple_mob/slime/xenobio/ruby, - /mob/living/simple_mob/slime/xenobio/ruby - ) - -/mob/living/simple_mob/slime/xenobio/ruby/Initialize() - add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. - return ..() - -/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) - ..() - - if(isliving(A) && a_intent == I_HURT) - var/mob/living/L = A - if(L.mob_size <= MOB_MEDIUM) - visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) - playsound(src, "punch", 50, 1) - L.Weaken(1) - var/throwdir = get_dir(src, L) - L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) - else - to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) - - -/mob/living/simple_mob/slime/xenobio/amber - desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ - They would probably be very important to other slimes, if the other colors didn't try to kill them." - color = "#FFBB00" - slime_color = "amber" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/amber - - description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ - slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." - player_msg = "You passively provide nutrition to nearby entities." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/silver, - /mob/living/simple_mob/slime/xenobio/amber, - /mob/living/simple_mob/slime/xenobio/amber - ) - -/mob/living/simple_mob/slime/xenobio/amber/handle_special() - if(stat != DEAD) - feed_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() - for(var/mob/living/L in view(1, src)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - if(L == src || istype(L, /mob/living/simple_mob/slime/xenobio/amber)) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. - continue - if(istype(L, /mob/living/simple_mob/slime/xenobio)) - var/mob/living/simple_mob/slime/xenobio/X = L - X.adjust_nutrition(rand(15, 25), 0) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.isSynthetic()) - continue - H.nutrition = between(0, H.nutrition + rand(15, 25), 800) - -/mob/living/simple_mob/slime/xenobio/cerulean - desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." - color = "#4F7EAA" - slime_color = "cerulean" - coretype = /obj/item/slime_extract/cerulean - - // Less than the specialized slimes, but higher than the rest. - maxHealth = 200 - maxHealth_adult = 250 - - melee_damage_lower = 10 - melee_damage_upper = 30 - - movement_cooldown = -1 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/dark_blue, - /mob/living/simple_mob/slime/xenobio/cerulean, - /mob/living/simple_mob/slime/xenobio/cerulean - ) - -// Tier 4 - -/mob/living/simple_mob/slime/xenobio/red - desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." - color = "#FF3333" - slime_color = "red" - coretype = /obj/item/slime_extract/red - movement_cooldown = -1 // See above. - untamable = TRUE // Will enrage if disciplined. - - description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/orange - ) - - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime - - -/mob/living/simple_mob/slime/xenobio/green - desc = "This slime is radioactive." - color = "#14FF20" - slime_color = "green" - coretype = /obj/item/slime_extract/green - glow_toggle = TRUE - reagent_injected = "radium" - var/rads = 25 - - description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ - A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." - player_msg = "You passively irradiate your surroundings.
                    \ - You also inject radium on attack." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/purple, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/emerald, - /mob/living/simple_mob/slime/xenobio/emerald - ) - -/mob/living/simple_mob/slime/xenobio/green/handle_special() - if(stat != DEAD) - irradiate() - ..() - -/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() - SSradiation.radiate(src, rads) - - - -/mob/living/simple_mob/slime/xenobio/pink - desc = "This slime has regenerative properties." - color = "#FF0080" - slime_color = "pink" - coretype = /obj/item/slime_extract/pink - glow_toggle = TRUE - - description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." - player_msg = "You passively heal yourself and nearby allies." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/pink - ) - -/mob/living/simple_mob/slime/xenobio/pink/handle_special() - if(stat != DEAD) - heal_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() - for(var/mob/living/L in view(src, 2)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - L.add_modifier(/datum/modifier/aura/slime_heal, null, src) - -/datum/modifier/aura/slime_heal - name = "slime mending" - desc = "You feel somewhat gooey." - mob_overlay_state = "pink_sparkles" - stacks = MODIFIER_STACK_FORBID - aura_max_distance = 2 - - on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." - on_expired_text = "The spores of goo have faded, although you feel much healthier than before." - -/datum/modifier/aura/slime_heal/tick() - if(holder.stat == DEAD) - expire() - - if(ishuman(holder)) // Robolimbs need this code sadly. - var/mob/living/carbon/human/H = holder - for(var/obj/item/organ/external/E in H.organs) - var/obj/item/organ/external/O = E - O.heal_damage(2, 2, 0, 1) - else - holder.adjustBruteLoss(-2) - holder.adjustFireLoss(-2) - - holder.adjustToxLoss(-2) - holder.adjustOxyLoss(-2) - holder.adjustCloneLoss(-1) - - -/mob/living/simple_mob/slime/xenobio/gold - desc = "This slime absorbs energy, and cannot be stunned by normal means." - color = "#EEAA00" - shiny = TRUE - slime_color = "gold" - coretype = /obj/item/slime_extract/gold - description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/sapphire - ) - -/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) - adjust_discipline(round(amount/2)) - power_charge = between(0, power_charge + amount, 10) - -/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. - return list() - - -// Tier 5 - -/mob/living/simple_mob/slime/xenobio/oil - desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." - color = "#333333" - slime_color = "oil" - shiny = TRUE - coretype = /obj/item/slime_extract/oil - - description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ - an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." - player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/oil, - /mob/living/simple_mob/slime/xenobio/red, - /mob/living/simple_mob/slime/xenobio/red - ) - -/mob/living/simple_mob/slime/xenobio/oil/proc/explode() - if(stat != DEAD) - explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. - if(src) // Delete ourselves if the explosion didn't do it. - qdel(src) - -/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(ishuman(L)) - var/mob/living/carbon/human/H = A - if(istype(H.species, /datum/species/monkey)) - return ..()// Don't blow up when just eatting monkeys. - - else if(isslime(L)) - return ..() - - // Otherwise blow ourselves up. - say(pick("Sacrifice...!", "Sssss...", "Boom...!")) - set_AI_busy(TRUE) - sleep(2 SECONDS) - log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") - explode() - - return ..() - -/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) - log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") - explode() - -/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) - log_and_message_admins("[src] exploded due to exposure to fire.") - explode() - -/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") - explode() - else - ..() - -/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) - if(istype(W) && W.force && W.damtype == BURN) - log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") - explode() - else - ..() - - -/mob/living/simple_mob/slime/xenobio/sapphire - desc = "This slime seems a bit brighter than the rest, both figuratively and literally." - color = "#2398FF" - slime_color = "sapphire" - shiny = TRUE - glow_toggle = TRUE - coretype = /obj/item/slime_extract/sapphire - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire - - description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ - with one if hostile, and especially dangerous if they outnumber you." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/sapphire, - /mob/living/simple_mob/slime/xenobio/gold, - /mob/living/simple_mob/slime/xenobio/gold - ) - - -/mob/living/simple_mob/slime/xenobio/emerald - desc = "This slime is faster than usual, even more so than the red slimes." - color = "#22FF22" - shiny = TRUE - glow_toggle = TRUE - slime_color = "emerald" - coretype = /obj/item/slime_extract/emerald - - description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/green, - /mob/living/simple_mob/slime/xenobio/emerald, - /mob/living/simple_mob/slime/xenobio/emerald - ) - -/mob/living/simple_mob/slime/xenobio/emerald/handle_special() - if(stat != DEAD) - zoom_aura() - ..() - -/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() - for(var/mob/living/L in view(src, 2)) - if(L.stat == DEAD || !IIsAlly(L)) - continue - L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) - - -/mob/living/simple_mob/slime/xenobio/light_pink - desc = "This slime seems a lot more peaceful than the others." - color = "#FF8888" - slime_color = "light pink" - coretype = /obj/item/slime_extract/light_pink - - description_info = "This slime is effectively always disciplined initially." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio/pink, - /mob/living/simple_mob/slime/xenobio/light_pink, - /mob/living/simple_mob/slime/xenobio/light_pink - ) - - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink - -// Special -/mob/living/simple_mob/slime/xenobio/rainbow - desc = "This slime changes colors constantly." - color = null // Uses a special icon_state. - slime_color = "rainbow" - coretype = /obj/item/slime_extract/rainbow - icon_state_override = "rainbow" - unity = TRUE - - description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ - other slimes being friendly to them, and therefore will never be harmed by another slime. \ - Attacking this slime will provoke the wrath of all slimes within range." - player_msg = "You are considered to be the same color as every slime, \ - meaning that you are considered an ally to all slimes." - - slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow, - /mob/living/simple_mob/slime/xenobio/rainbow - ) - -/mob/living/simple_mob/slime/xenobio/rainbow/Initialize() - unify() - return ..() - -// The RD's pet slime. -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick - name = "Kendrick" - desc = "The Research Director's pet slime. It shifts colors constantly." - rainbow_core_candidate = FALSE - // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. - // Instead lets just give them an ai_holder that does that for us. - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive - -/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/Initialize() - pacify() // So the physical mob also gets made harmless. - return ..() +// Here are where all the other colors of slime live. +// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. + +// Tier 1. + +/mob/living/simple_mob/slime/xenobio/purple + desc = "This slime is rather toxic to handle, as it is poisonous." + color = "#CC23FF" + slime_color = "purple" + coretype = /obj/item/slime_extract/purple + reagent_injected = "toxin" + + description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." + player_msg = "You inject a harmful toxin when attacking." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange + desc = "This slime is known to be flammable and can ignite enemies." + color = "#FFA723" + slime_color = "orange" + coretype = /obj/item/slime_extract/orange + melee_damage_lower = 5 + melee_damage_upper = 5 + heat_resist = 1 + + description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ + A firesuit can protect from the burning attacks of this slime." + player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
                    \ + You are also immune to burning attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_heat_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You burn \the [L].")) + to_chat(L, span("danger", "You've been burned by \the [src]!")) + L.adjust_fire_stacks(1) + if(prob(12)) + L.IgniteMob() + +/mob/living/simple_mob/slime/xenobio/blue + desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." + color = "#19FFFF" + slime_color = "blue" + coretype = /obj/item/slime_extract/blue + reagent_injected = "cryotoxin" + cold_resist = 0.50 // Not as strong as dark blue, which has immunity. + + description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ + A biosuit or other thick armor can protect from the injection." + player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
                    \ + You are also resistant to cold attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio + ) + + +/mob/living/simple_mob/slime/xenobio/metal + desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." + color = "#5F5F5F" + slime_color = "metal" + shiny = TRUE + coretype = /obj/item/slime_extract/metal + + description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." + player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." + + maxHealth = 250 + maxHealth_adult = 350 + + // The sloped armor. + // It's resistant to most weapons (but a spraybottle still kills it rather fast). + armor = list( + "melee" = 25, + "bullet" = 25, + "laser" = 25, + "energy" = 50, + "bomb" = 80, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 5, + "bullet" = 5, + "laser" = 5, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.taunt(src, TRUE) // We're the party tank now. + +// Tier 2 + +/mob/living/simple_mob/slime/xenobio/yellow + desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." + color = "#FFF423" + slime_color = "yellow" + coretype = /obj/item/slime_extract/yellow + melee_damage_lower = 5 + melee_damage_upper = 5 + shock_resist = 1 + + projectiletype = /obj/item/projectile/beam/lightning/slime + projectilesound = 'sound/effects/lightningbolt.ogg' + glow_toggle = TRUE + + description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ + enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ + attack faster than usual. Insulative or reflective armor can protect from these attacks." + player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
                    \ + You are also immune to shocking attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/orange + ) + +/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_shock_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You shock \the [L].")) + to_chat(L, span("danger", "You've been shocked by \the [src]!")) + +/mob/living/simple_mob/slime/xenobio/yellow/handle_special() + if(stat == CONSCIOUS) + if(prob(25)) + power_charge = between(0, power_charge + 1, 10) + ..() + +/obj/item/projectile/beam/lightning/slime + power = 10 + fire_sound = 'sound/effects/lightningbolt.ogg' + + +/mob/living/simple_mob/slime/xenobio/dark_purple + desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." + color = "#660088" + slime_color = "dark purple" + coretype = /obj/item/slime_extract/dark_purple + reagent_injected = "phoron" + + description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ + If hit with a burning attack, it will erupt in flames." + player_msg = "You inject phoron into enemies you attack.
                    \ + You will erupt into flames if harmed by fire!" + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() + visible_message(span("critical", "\The [src] erupts in an inferno!")) + for(var/turf/simulated/target_turf in view(2, src)) + target_turf.assume_gas("phoron", 30, 1500+T0C) + spawn(0) + target_turf.hotspot_expose(1500+T0C, 400) + qdel(src) + +/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) + log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] ignited due to exposure to fire.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + ignite() + else + ..() + +/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") + ignite() + else + ..() + + + +/mob/living/simple_mob/slime/xenobio/dark_blue + desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + melee_damage_lower = 5 + melee_damage_upper = 5 + cold_resist = 1 + + description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ + also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." + player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + + minbodytemp = 0 + cold_damage_per_tick = 0 + +/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) + continue + chill(L) + + var/turf/T = get_turf(src) + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + chill(L) + to_chat(src, span("span", "You chill \the [L].")) + to_chat(L, span("danger", "You've been chilled by \the [src]!")) + + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(is_adult ? 10 : 5) + if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. + L.ai_holder.react_to_attack(src) + + +/mob/living/simple_mob/slime/xenobio/silver + desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." + color = "#AAAAAA" + slime_color = "silver" + coretype = /obj/item/slime_extract/silver + shiny = TRUE + + description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." + player_msg = "You automatically reflect lasers, beams, and tasers that hit you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) + visible_message(span("danger", "\The [src] reflects \the [P]!")) + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = TRUE + return PROJECTILE_CONTINUE // complete projectile permutation + else + ..() + + +// Tier 3 + +/mob/living/simple_mob/slime/xenobio/bluespace + desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." + color = null + slime_color = "bluespace" + icon_state_override = "bluespace" + coretype = /obj/item/slime_extract/bluespace + + description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." + player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/yellow + ) + + special_attack_min_range = 3 + special_attack_max_range = 7 + special_attack_cooldown = 5 SECONDS + +/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) + // Teleport attack. + if(!A) + to_chat(src, span("warning", "There's nothing to teleport to.")) + return FALSE + + var/list/nearby_things = range(1, A) + var/list/valid_turfs = list() + + // All this work to just go to a non-dense tile. + for(var/turf/potential_turf in nearby_things) + var/valid_turf = TRUE + if(potential_turf.density) + continue + for(var/atom/movable/AM in potential_turf) + if(AM.density) + valid_turf = FALSE + if(valid_turf) + valid_turfs.Add(potential_turf) + + if(!(valid_turfs.len)) + to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) + return FALSE + + var/turf/target_turf = pick(valid_turfs) + var/turf/T = get_turf(src) + + var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread + s1.set_up(5, 1, T) + var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread + s2.set_up(5, 1, target_turf) + + + T.visible_message(span("notice", "\The [src] vanishes!")) + s1.start() + + forceMove(target_turf) + playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) + to_chat(src, span("notice", "You teleport to \the [target_turf].")) + + target_turf.visible_message(span("warning", "\The [src] appears!")) + s2.start() + + if(Adjacent(A)) + attack_target(A) + + +/mob/living/simple_mob/slime/xenobio/ruby + desc = "This slime has great physical strength." + color = "#FF3333" + slime_color = "ruby" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/ruby + + description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ + Their glomp attacks also send the victim flying." + player_msg = "Your attacks knock back the target a fair distance.
                    \ + You also hit harder, take less damage, and stuns affect you for less time." + + melee_attack_delay = 1 SECOND + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/ruby/Initialize() + add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. + return ..() + +/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) + ..() + + if(isliving(A) && a_intent == I_HURT) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) + playsound(src, "punch", 50, 1) + L.Weaken(1) + var/throwdir = get_dir(src, L) + L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) + else + to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) + + +/mob/living/simple_mob/slime/xenobio/amber + desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ + They would probably be very important to other slimes, if the other colors didn't try to kill them." + color = "#FFBB00" + slime_color = "amber" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/amber + + description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ + slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." + player_msg = "You passively provide nutrition to nearby entities." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/amber/handle_special() + if(stat != DEAD) + feed_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() + for(var/mob/living/L in view(1, src)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + if(L == src || istype(L, /mob/living/simple_mob/slime/xenobio/amber)) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. + continue + if(istype(L, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/X = L + X.adjust_nutrition(rand(15, 25), 0) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic()) + continue + H.nutrition = between(0, H.nutrition + rand(15, 25), 800) + +/mob/living/simple_mob/slime/xenobio/cerulean + desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." + color = "#4F7EAA" + slime_color = "cerulean" + coretype = /obj/item/slime_extract/cerulean + + // Less than the specialized slimes, but higher than the rest. + maxHealth = 200 + maxHealth_adult = 250 + + melee_damage_lower = 10 + melee_damage_upper = 30 + + movement_cooldown = -1 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + +// Tier 4 + +/mob/living/simple_mob/slime/xenobio/red + desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." + color = "#FF3333" + slime_color = "red" + coretype = /obj/item/slime_extract/red + movement_cooldown = -1 // See above. + untamable = TRUE // Will enrage if disciplined. + + description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/orange + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime + + +/mob/living/simple_mob/slime/xenobio/green + desc = "This slime is radioactive." + color = "#14FF20" + slime_color = "green" + coretype = /obj/item/slime_extract/green + glow_toggle = TRUE + reagent_injected = "radium" + var/rads = 25 + + description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ + A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." + player_msg = "You passively irradiate your surroundings.
                    \ + You also inject radium on attack." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/green/handle_special() + if(stat != DEAD) + irradiate() + ..() + +/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() + SSradiation.radiate(src, rads) + + + +/mob/living/simple_mob/slime/xenobio/pink + desc = "This slime has regenerative properties." + color = "#FF0080" + slime_color = "pink" + coretype = /obj/item/slime_extract/pink + glow_toggle = TRUE + + description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." + player_msg = "You passively heal yourself and nearby allies." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/pink + ) + +/mob/living/simple_mob/slime/xenobio/pink/handle_special() + if(stat != DEAD) + heal_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/aura/slime_heal, null, src) + +/datum/modifier/aura/slime_heal + name = "slime mending" + desc = "You feel somewhat gooey." + mob_overlay_state = "pink_sparkles" + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 2 + + on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." + on_expired_text = "The spores of goo have faded, although you feel much healthier than before." + +/datum/modifier/aura/slime_heal/tick() + if(holder.stat == DEAD) + expire() + + if(ishuman(holder)) // Robolimbs need this code sadly. + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/E in H.organs) + var/obj/item/organ/external/O = E + O.heal_damage(2, 2, 0, 1) + else + holder.adjustBruteLoss(-2) + holder.adjustFireLoss(-2) + + holder.adjustToxLoss(-2) + holder.adjustOxyLoss(-2) + holder.adjustCloneLoss(-1) + + +/mob/living/simple_mob/slime/xenobio/gold + desc = "This slime absorbs energy, and cannot be stunned by normal means." + color = "#EEAA00" + shiny = TRUE + slime_color = "gold" + coretype = /obj/item/slime_extract/gold + description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire + ) + +/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) + adjust_discipline(round(amount/2)) + power_charge = between(0, power_charge + amount, 10) + +/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. + return list() + + +// Tier 5 + +/mob/living/simple_mob/slime/xenobio/oil + desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." + color = "#333333" + slime_color = "oil" + shiny = TRUE + coretype = /obj/item/slime_extract/oil + + description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ + an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." + player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/red + ) + +/mob/living/simple_mob/slime/xenobio/oil/proc/explode() + if(stat != DEAD) + explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. + if(src) // Delete ourselves if the explosion didn't do it. + qdel(src) + +/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(ishuman(L)) + var/mob/living/carbon/human/H = A + if(istype(H.species, /datum/species/monkey)) + return ..()// Don't blow up when just eatting monkeys. + + else if(isslime(L)) + return ..() + + // Otherwise blow ourselves up. + say(pick("Sacrifice...!", "Sssss...", "Boom...!")) + set_AI_busy(TRUE) + sleep(2 SECONDS) + log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") + explode() + + return ..() + +/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) + log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] exploded due to exposure to fire.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + explode() + else + ..() + +/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") + explode() + else + ..() + + +/mob/living/simple_mob/slime/xenobio/sapphire + desc = "This slime seems a bit brighter than the rest, both figuratively and literally." + color = "#2398FF" + slime_color = "sapphire" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/sapphire + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire + + description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ + with one if hostile, and especially dangerous if they outnumber you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/gold + ) + + +/mob/living/simple_mob/slime/xenobio/emerald + desc = "This slime is faster than usual, even more so than the red slimes." + color = "#22FF22" + shiny = TRUE + glow_toggle = TRUE + slime_color = "emerald" + coretype = /obj/item/slime_extract/emerald + + description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/emerald/handle_special() + if(stat != DEAD) + zoom_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) + + +/mob/living/simple_mob/slime/xenobio/light_pink + desc = "This slime seems a lot more peaceful than the others." + color = "#FF8888" + slime_color = "light pink" + coretype = /obj/item/slime_extract/light_pink + + description_info = "This slime is effectively always disciplined initially." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink + +// Special +/mob/living/simple_mob/slime/xenobio/rainbow + desc = "This slime changes colors constantly." + color = null // Uses a special icon_state. + slime_color = "rainbow" + coretype = /obj/item/slime_extract/rainbow + icon_state_override = "rainbow" + unity = TRUE + + description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ + other slimes being friendly to them, and therefore will never be harmed by another slime. \ + Attacking this slime will provoke the wrath of all slimes within range." + player_msg = "You are considered to be the same color as every slime, \ + meaning that you are considered an ally to all slimes." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow + ) + +/mob/living/simple_mob/slime/xenobio/rainbow/Initialize() + unify() + return ..() + +// The RD's pet slime. +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick + name = "Kendrick" + desc = "The Research Director's pet slime. It shifts colors constantly." + rainbow_core_candidate = FALSE + // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. + // Instead lets just give them an ai_holder that does that for us. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive + +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/Initialize() + pacify() // So the physical mob also gets made harmless. + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm index 4730c3ae4b8..d4e96c23d5d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm @@ -1,312 +1,312 @@ -// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear. - -/mob/living/simple_mob/slime/xenobio - desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists." - layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something. - ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes. - max_nutrition = 1000 - var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough. - var/maxHealth_adult = 200 - var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough. - var/mob/living/victim = null // the person the slime is currently feeding on - var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly. - var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes. - var/split_amount = 4 // Amount of children we will normally have. Half of that for dead adult slimes. Is NOT carried across generations. - var/untamable = FALSE //Makes slime untamable via discipline. - var/untamable_inheirit = FALSE //Makes slime inheirit its untamability. - var/list/slime_mutation = list( - /mob/living/simple_mob/slime/xenobio/orange, - /mob/living/simple_mob/slime/xenobio/metal, - /mob/living/simple_mob/slime/xenobio/blue, - /mob/living/simple_mob/slime/xenobio/purple - ) - var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces - var/number = 0 // This is used to make the slime semi-unique for indentification. - var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce. - -/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor) - ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime)) - number = rand(1, 1000) - update_name() - - . = ..() // This will make the AI and do the other mob constructor things. It will also return the default hint at the end. - - if(my_predecessor) - inherit_information(my_predecessor) - - -/mob/living/simple_mob/slime/xenobio/Destroy() - if(victim) - stop_consumption() // Unbuckle us from our victim. - return ..() - -// Called when a slime makes another slime by splitting. The predecessor slime will be deleted shortly afterwards. -/mob/living/simple_mob/slime/xenobio/proc/inherit_information(var/mob/living/simple_mob/slime/xenobio/predecessor) - if(!predecessor) - return - - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - var/datum/ai_holder/simple_mob/xenobio_slime/previous_AI = predecessor.ai_holder - ASSERT(istype(AI)) - ASSERT(istype(previous_AI)) - - // Now to transfer the information. - // Newly made slimes are bit more rebellious than their predecessors, but they also somewhat forget the atrocities the xenobiologist may have done. - AI.discipline = max(previous_AI.discipline - 1, 0) - AI.obedience = max(previous_AI.obedience - 1, 0) - AI.resentment = max(previous_AI.resentment - 1, 0) - AI.rabid = previous_AI.rabid - -/mob/living/simple_mob/slime/xenobio/update_icon() - icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]" - icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead" - icon_rest = icon_dead - ..() // This will apply the correct icon_state and do the other overlay-related things. - - -/mob/living/simple_mob/slime/xenobio/handle_special() - if(stat != DEAD) - handle_nutrition() - - if(victim) - handle_consumption() - - handle_stuttering() // ?? - - ..() - -/mob/living/simple_mob/slime/xenobio/examine(mob/user) - . = ..() - if(hat) - . += "It is wearing \a [hat]." - - if(stat == DEAD) - . += "It appears to be dead." - else if(incapacitated(INCAPACITATION_DISABLED)) - . += "It appears to be incapacitated." - else if(harmless) - . += "It appears to have been pacified." - else - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - if(AI.rabid) - . += "It seems very, very angry and upset." - else if(AI.obedience >= 5) - . += "It looks rather obedient." - else if(AI.discipline) - . += "It has been subjugated by force, at least for now." - -/mob/living/simple_mob/slime/xenobio/proc/make_adult() - if(is_adult) - return - - is_adult = TRUE - melee_damage_lower = round(melee_damage_lower * 2) // 20 - melee_damage_upper = round(melee_damage_upper * 2) // 30 - maxHealth = maxHealth_adult - max_nutrition = 1200 - amount_grown = 0 - update_icon() - update_name() - -/mob/living/simple_mob/slime/xenobio/proc/make_baby() - if(!is_adult) - return - - is_adult = FALSE - melee_damage_lower = round(melee_damage_lower / 2) // 20 - melee_damage_upper = round(melee_damage_upper / 2) // 30 - maxHealth = initial(maxHealth) - health = clamp(health, 0, maxHealth) - max_nutrition = initial(max_nutrition) - nutrition = 400 - amount_grown = 0 - update_icon() - update_name() - -/mob/living/simple_mob/slime/xenobio/proc/update_name() - if(harmless) // Docile slimes are generally named, so we shouldn't mess with it. - return - name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])" - real_name = name - -/mob/living/simple_mob/slime/xenobio/update_mood() - var/old_mood = mood - if(incapacitated(INCAPACITATION_DISABLED)) - mood = "sad" - else if(harmless) - mood = ":33" - else if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - if(AI.rabid) - mood = "angry" - else if(AI.target) - mood = "mischevous" - else if(AI.discipline) - mood = "pout" - else - mood = ":3" - else - mood = ":3" - - if(old_mood != mood) - update_icon() - -/mob/living/simple_mob/slime/xenobio/proc/enrage() - if(harmless) - return - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.enrage() - -/mob/living/simple_mob/slime/xenobio/proc/relax() - if(harmless) - return - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.relax() - -/mob/living/simple_mob/slime/xenobio/proc/pacify() - harmless = TRUE - if(has_AI()) - var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder - AI.pacify() - - faction = "neutral" - - // If for whatever reason the mob AI (or player) decides to try to attack something anyways. - melee_damage_upper = 0 - melee_damage_lower = 0 - - update_mood() - - -// These are verbs so that player slimes can evolve/split. -/mob/living/simple_mob/slime/xenobio/verb/evolve() - set category = "Slime" - set desc = "This will let you evolve from baby to adult slime." - - if(stat) - to_chat(src, span("warning", "I must be conscious to do this...")) - return - - if(harmless) - to_chat(src, span("warning", "I have been pacified. I cannot evolve...")) - return - - if(!is_adult) - if(amount_grown >= 10) - make_adult() - else - to_chat(src, span("warning", "I am not ready to evolve yet...")) - else - to_chat(src, span("warning", "I have already evolved...")) - - -/mob/living/simple_mob/slime/xenobio/verb/reproduce() - set category = "Slime" - set desc = "This will make you split into four new slimes." - - if(stat) - to_chat(src, span("warning", "I must be conscious to do this...")) - return - - if(harmless) - to_chat(src, span("warning", "I have been pacified. I cannot reproduce...")) - return - - if(is_adult) - if(amount_grown >= 10) - // Check if there's enough 'room' to split. - var/list/nearby_things = orange(1, src) - var/free_tiles = 0 - for(var/turf/T in nearby_things) - var/free = TRUE - if(T.density) // No walls. - continue - for(var/atom/movable/AM in T) - if(istype(AM, /mob/living/simple_mob/slime) || !(AM.CanPass(src, T))) - free = FALSE - break - for(var/atom/movable/AM in get_turf(src)) - if(!(AM.CanPass(src, T)) && !(AM == src)) - free = FALSE - break - - if(free) - free_tiles++ - - if(free_tiles < split_amount-1) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies. - to_chat(src, span("warning", "It is too cramped here to reproduce...")) - return - - var/list/babies = list() - for(var/i = 1 to split_amount) - babies.Add(make_new_slime(no_step = i)) - - var/mob/living/simple_mob/slime/new_slime = pick(babies) - new_slime.universal_speak = universal_speak - if(src.mind) - src.mind.transfer_to(new_slime) - else - new_slime.key = src.key - qdel(src) - else - to_chat(src, span("warning", "I am not ready to reproduce yet...")) - else - to_chat(src, span("warning", "I have not evolved enough to reproduce yet...")) - -// Used when reproducing or dying. -/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type, var/no_step) - var/t = src.type - if(desired_type) - t = desired_type - if(prob(mutation_chance / 10)) - t = /mob/living/simple_mob/slime/xenobio/rainbow - else if(prob(mutation_chance) && slime_mutation.len) - t = slime_mutation[rand(1, slime_mutation.len)] - var/mob/living/simple_mob/slime/xenobio/baby = new t(loc, src) - - // Handle 'inheriting' from parent slime. - baby.mutation_chance = mutation_chance - baby.power_charge = round(power_charge / 4) - - if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow)) - baby.unity = unity - if(untamable_inheirit) - baby.untamable = untamable - baby.untamable_inheirit = untamable_inheirit - baby.faction = faction - baby.friends = friends.Copy() - - if(no_step != 1) - step_away(baby, src) - return baby - -/mob/living/simple_mob/slime/xenobio/get_description_interaction() - var/list/results = list() - - if(!stat) - results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad." - - results += ..() - - return results - -/mob/living/simple_mob/slime/xenobio/get_description_info() - var/list/lines = list() - var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \ - They can be extremely dangerous if not handled properly." - lines.Add(intro_line) - lines.Add(null) // To pad the line breaks. - - var/list/rewards = list() - for(var/potential_color in slime_mutation) - var/mob/living/simple_mob/slime/S = potential_color - rewards.Add(initial(S.slime_color)) - var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough." - lines.Add(reward_line) - lines.Add(null) - - lines.Add(description_info) - return lines.Join("\n") +// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear. + +/mob/living/simple_mob/slime/xenobio + desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists." + layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes. + max_nutrition = 1000 + var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough. + var/maxHealth_adult = 200 + var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough. + var/mob/living/victim = null // the person the slime is currently feeding on + var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly. + var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes. + var/split_amount = 4 // Amount of children we will normally have. Half of that for dead adult slimes. Is NOT carried across generations. + var/untamable = FALSE //Makes slime untamable via discipline. + var/untamable_inheirit = FALSE //Makes slime inheirit its untamability. + var/list/slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/purple + ) + var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces + var/number = 0 // This is used to make the slime semi-unique for indentification. + var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce. + +/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor) + ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime)) + number = rand(1, 1000) + update_name() + + . = ..() // This will make the AI and do the other mob constructor things. It will also return the default hint at the end. + + if(my_predecessor) + inherit_information(my_predecessor) + + +/mob/living/simple_mob/slime/xenobio/Destroy() + if(victim) + stop_consumption() // Unbuckle us from our victim. + return ..() + +// Called when a slime makes another slime by splitting. The predecessor slime will be deleted shortly afterwards. +/mob/living/simple_mob/slime/xenobio/proc/inherit_information(var/mob/living/simple_mob/slime/xenobio/predecessor) + if(!predecessor) + return + + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + var/datum/ai_holder/simple_mob/xenobio_slime/previous_AI = predecessor.ai_holder + ASSERT(istype(AI)) + ASSERT(istype(previous_AI)) + + // Now to transfer the information. + // Newly made slimes are bit more rebellious than their predecessors, but they also somewhat forget the atrocities the xenobiologist may have done. + AI.discipline = max(previous_AI.discipline - 1, 0) + AI.obedience = max(previous_AI.obedience - 1, 0) + AI.resentment = max(previous_AI.resentment - 1, 0) + AI.rabid = previous_AI.rabid + +/mob/living/simple_mob/slime/xenobio/update_icon() + icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]" + icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead" + icon_rest = icon_dead + ..() // This will apply the correct icon_state and do the other overlay-related things. + + +/mob/living/simple_mob/slime/xenobio/handle_special() + if(stat != DEAD) + handle_nutrition() + + if(victim) + handle_consumption() + + handle_stuttering() // ?? + + ..() + +/mob/living/simple_mob/slime/xenobio/examine(mob/user) + . = ..() + if(hat) + . += "It is wearing \a [hat]." + + if(stat == DEAD) + . += "It appears to be dead." + else if(incapacitated(INCAPACITATION_DISABLED)) + . += "It appears to be incapacitated." + else if(harmless) + . += "It appears to have been pacified." + else + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + . += "It seems very, very angry and upset." + else if(AI.obedience >= 5) + . += "It looks rather obedient." + else if(AI.discipline) + . += "It has been subjugated by force, at least for now." + +/mob/living/simple_mob/slime/xenobio/proc/make_adult() + if(is_adult) + return + + is_adult = TRUE + melee_damage_lower = round(melee_damage_lower * 2) // 20 + melee_damage_upper = round(melee_damage_upper * 2) // 30 + maxHealth = maxHealth_adult + max_nutrition = 1200 + amount_grown = 0 + update_icon() + update_name() + +/mob/living/simple_mob/slime/xenobio/proc/make_baby() + if(!is_adult) + return + + is_adult = FALSE + melee_damage_lower = round(melee_damage_lower / 2) // 20 + melee_damage_upper = round(melee_damage_upper / 2) // 30 + maxHealth = initial(maxHealth) + health = clamp(health, 0, maxHealth) + max_nutrition = initial(max_nutrition) + nutrition = 400 + amount_grown = 0 + update_icon() + update_name() + +/mob/living/simple_mob/slime/xenobio/proc/update_name() + if(harmless) // Docile slimes are generally named, so we shouldn't mess with it. + return + name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])" + real_name = name + +/mob/living/simple_mob/slime/xenobio/update_mood() + var/old_mood = mood + if(incapacitated(INCAPACITATION_DISABLED)) + mood = "sad" + else if(harmless) + mood = ":33" + else if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + mood = "angry" + else if(AI.target) + mood = "mischevous" + else if(AI.discipline) + mood = "pout" + else + mood = ":3" + else + mood = ":3" + + if(old_mood != mood) + update_icon() + +/mob/living/simple_mob/slime/xenobio/proc/enrage() + if(harmless) + return + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.enrage() + +/mob/living/simple_mob/slime/xenobio/proc/relax() + if(harmless) + return + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.relax() + +/mob/living/simple_mob/slime/xenobio/proc/pacify() + harmless = TRUE + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.pacify() + + faction = "neutral" + + // If for whatever reason the mob AI (or player) decides to try to attack something anyways. + melee_damage_upper = 0 + melee_damage_lower = 0 + + update_mood() + + +// These are verbs so that player slimes can evolve/split. +/mob/living/simple_mob/slime/xenobio/verb/evolve() + set category = "Slime" + set desc = "This will let you evolve from baby to adult slime." + + if(stat) + to_chat(src, span("warning", "I must be conscious to do this...")) + return + + if(harmless) + to_chat(src, span("warning", "I have been pacified. I cannot evolve...")) + return + + if(!is_adult) + if(amount_grown >= 10) + make_adult() + else + to_chat(src, span("warning", "I am not ready to evolve yet...")) + else + to_chat(src, span("warning", "I have already evolved...")) + + +/mob/living/simple_mob/slime/xenobio/verb/reproduce() + set category = "Slime" + set desc = "This will make you split into four new slimes." + + if(stat) + to_chat(src, span("warning", "I must be conscious to do this...")) + return + + if(harmless) + to_chat(src, span("warning", "I have been pacified. I cannot reproduce...")) + return + + if(is_adult) + if(amount_grown >= 10) + // Check if there's enough 'room' to split. + var/list/nearby_things = orange(1, src) + var/free_tiles = 0 + for(var/turf/T in nearby_things) + var/free = TRUE + if(T.density) // No walls. + continue + for(var/atom/movable/AM in T) + if(istype(AM, /mob/living/simple_mob/slime) || !(AM.CanPass(src, T))) + free = FALSE + break + for(var/atom/movable/AM in get_turf(src)) + if(!(AM.CanPass(src, T)) && !(AM == src)) + free = FALSE + break + + if(free) + free_tiles++ + + if(free_tiles < split_amount-1) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies. + to_chat(src, span("warning", "It is too cramped here to reproduce...")) + return + + var/list/babies = list() + for(var/i = 1 to split_amount) + babies.Add(make_new_slime(no_step = i)) + + var/mob/living/simple_mob/slime/new_slime = pick(babies) + new_slime.universal_speak = universal_speak + if(src.mind) + src.mind.transfer_to(new_slime) + else + new_slime.key = src.key + qdel(src) + else + to_chat(src, span("warning", "I am not ready to reproduce yet...")) + else + to_chat(src, span("warning", "I have not evolved enough to reproduce yet...")) + +// Used when reproducing or dying. +/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type, var/no_step) + var/t = src.type + if(desired_type) + t = desired_type + if(prob(mutation_chance / 10)) + t = /mob/living/simple_mob/slime/xenobio/rainbow + else if(prob(mutation_chance) && slime_mutation.len) + t = slime_mutation[rand(1, slime_mutation.len)] + var/mob/living/simple_mob/slime/xenobio/baby = new t(loc, src) + + // Handle 'inheriting' from parent slime. + baby.mutation_chance = mutation_chance + baby.power_charge = round(power_charge / 4) + + if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow)) + baby.unity = unity + if(untamable_inheirit) + baby.untamable = untamable + baby.untamable_inheirit = untamable_inheirit + baby.faction = faction + baby.friends = friends.Copy() + + if(no_step != 1) + step_away(baby, src) + return baby + +/mob/living/simple_mob/slime/xenobio/get_description_interaction() + var/list/results = list() + + if(!stat) + results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad." + + results += ..() + + return results + +/mob/living/simple_mob/slime/xenobio/get_description_info() + var/list/lines = list() + var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \ + They can be extremely dangerous if not handled properly." + lines.Add(intro_line) + lines.Add(null) // To pad the line breaks. + + var/list/rewards = list() + for(var/potential_color in slime_mutation) + var/mob/living/simple_mob/slime/S = potential_color + rewards.Add(initial(S.slime_color)) + var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough." + lines.Add(reward_line) + lines.Add(null) + + lines.Add(description_info) + return lines.Join("\n") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bat.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bat.dm new file mode 100644 index 00000000000..6d6328dfeff --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/bat.dm @@ -0,0 +1,58 @@ +/mob/living/simple_mob/vore/bat + name = "giant bat" + desc = "Blimey, that's a big sky fox." + tt_desc = "Homo paramour" + + icon_state = "bat" + icon = 'icons/mob/vore.dmi' + icon_living = "bat" + icon_rest = "batasleep" + icon_dead = "bat-dead" + + harm_intent_damage = 5 + melee_damage_lower = 2 + melee_damage_upper = 5 + + response_help = "nuzzles" + response_disarm = "flaps at" + response_harm = "bites" + + attacktext = list("bites","scratches") + + say_list_type = /datum/say_list/bat + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/edible + + faction = "vampire" + +// Activate Noms! +/mob/living/simple_mob/vore/bat + vore_active = 1 + vore_bump_chance = 50 + vore_pounce_chance = 50 + vore_standing_too = 1 + vore_ignores_undigestable = 0 + vore_default_mode = DM_DRAIN // They just want to drain you! + vore_digest_chance = 25 // But don't you dare try to escape... + vore_icons = SA_ICON_LIVING | SA_ICON_REST + +/datum/say_list/bat + speak = list("Chirp!") + emote_hear = list("chirps","pings","clicks") + emote_see = list("flaps","grooms itself") + +/mob/living/simple_mob/vore/bat/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "The giant bat has managed to swallow you alive, which is particularly impressive given that it's still a rather small creature. It's belly bulges out as you're squeezed into the oppressively tight stomach, and it lands to manage the weight, wings curling over your form beneath. The body groans under your strain, burbling and growling as it gets to work on it's feed. However, at least for now, it seems to do you no physical harm. Instead, the damp walls that squelch across your body try to leech out your energy through some less direct means." + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "yet_another_tumby" + B.digest_brute = 2 + B.digest_burn = 2 + B.digest_oxy = 1 + B.digestchance = 25 + B.absorbchance = 0 + B.escapechance = 15 + B.selective_preference = DM_DRAIN + B.escape_stun = 5 + diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm index fba0488b055..fd5c6307498 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm @@ -563,7 +563,7 @@ I think I covered everything. autotransferwait = 150 escapable = 1 escapechance = 100 - escapetime = 30 + escapetime = 15 fancy_vore = 1 contamination_color = "grey" contamination_flavor = "Wet" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index 7d32b7b3504..d3ad0c3c875 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -71,7 +71,7 @@ vore_stomach_name = "fuel processor" vore_stomach_flavor = "You have ended up in the fuel processor of this corrupted machine. This place was definitely not designed with safety and comfort in mind. The heated and cramped surroundings oozing potent fluids all over your form, eager to do nothing less than breaking you apart to fuel its rampage for the next few days... hours... minutes? Oh dear..." - loot_list = list(/obj/item/borg/upgrade/syndicate = 6, /obj/item/borg/upgrade/vtec = 6, /obj/item/weapon/material/knife/ritual = 6, /obj/item/weapon/disk/nifsoft/compliance = 6) + loot_list = list(/obj/item/borg/upgrade/basic/syndicate = 6, /obj/item/borg/upgrade/basic/vtec = 6, /obj/item/weapon/material/knife/ritual = 6, /obj/item/weapon/disk/nifsoft/compliance = 6) /mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi name = "corrupt corrupt hound" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/cryptdrake.dm b/code/modules/mob/living/simple_mob/subtypes/vore/cryptdrake.dm new file mode 100644 index 00000000000..cb3ca07957f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/cryptdrake.dm @@ -0,0 +1,131 @@ +/mob/living/simple_mob/vore/cryptdrake + name = "crypt drake" + desc = "A massive drake-like creature with dark purple scales and a seemingly exposed skull." + tt_desc = "Draconis necrotis" + icon = 'icons/mob/vore128x64.dmi' + icon_dead = "cryptdrake-dead" + icon_living = "cryptdrake" + icon_state = "cryptdrake" + icon_rest = "cryptdrake" + faction = "dragon" + old_x = -48 + old_y = 0 + vis_height = 92 + melee_damage_lower = 20 + melee_damage_upper = 15 + friendly = list("nudges", "sniffs on", "rumbles softly at", "nuzzles") + default_pixel_x = -48 + pixel_x = -48 + pixel_y = 0 + response_help = "bumps" + response_disarm = "shoves" + response_harm = "bites" + movement_cooldown = -1 + harm_intent_damage = 10 + melee_damage_lower = 10 + melee_damage_upper = 20 + maxHealth = 1000 + attacktext = list("mauled") + see_in_dark = 8 + minbodytemp = 0 + ai_holder_type = /datum/ai_holder/simple_mob/vore + max_buckled_mobs = 1 + mount_offset_y = 32 + mount_offset_x = -16 + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + max_tox = 0 // immune to poison + + special_attack_min_range = 2 + special_attack_max_range = 4 + special_attack_cooldown = 30 SECONDS + + var/leap_warmup = 2 SECOND // How long the leap telegraphing is. + var/leap_sound = 'sound/weapons/spiderlunge.ogg' + +/mob/living/simple_mob/vore/cryptdrake + + vore_bump_chance = 25 + vore_digest_chance = 50 + vore_escape_chance = 5 + vore_pounce_chance = 100 + vore_active = 1 + vore_icons = 2 + vore_icons = SA_ICON_LIVING | SA_ICON_REST + vore_capacity = 2 + swallowTime = 50 + vore_ignores_undigestable = TRUE + vore_default_mode = DM_DIGEST + vore_pounce_maxhealth = 125 + vore_bump_emote = "tries to devour" + +/mob/living/simple_mob/vore/cryptdrake/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_mob(src) + verbs |= /mob/living/simple_mob/proc/animal_mount + verbs |= /mob/living/proc/toggle_rider_reins + movement_cooldown = -1 + +/mob/living/simple_mob/vore/cryptdrake/init_vore() + . = ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "The enormous beast snaps it's boney jaws around your form, effortlessly lifting you from the ground. Throwing it's head backwards, your body is tossed up momentarily as the maw parts wider, only for you to descend rapidly moments later. You're caught in the creature's throat for a moment, contracting dark purple flesh holding tightly onto you, there's no going back at this point. The peristaltic motions squeeze you down this tunnel towards your final destination, where you're soon relieved of the intense squelching to be pushed into a move flexible, stretching chamber. Immediately coated in caustic oozes, the world around you seems more than eager to ensure that you're soaked over every inch, wrinkled walls twisting and grinding around your body. The drake's stomach clenches and compresses over you rhythmically, attempting to eagerly add you to the soup of fluids that fill this sloshing gut. Omnipresent sounds of groaning, gurgling and burbling bodily functions signify just how active this process already is." + B.vore_sound = "Tauric Swallow" + B.release_sound = "Pred Escape" + B.mode_flags = DM_FLAG_THICKBELLY + B.fancy_vore = 1 + B.selective_preference = DM_DIGEST + B.vore_verb = "devour" + B.digest_brute = 3 + B.digest_burn = 2 + B.digest_oxy = 0 + B.digestchance = 50 + B.absorbchance = 0 + B.escapechance = 3 + B.escape_stun = 5 + B.contamination_color = "grey" + B.contamination_flavor = "Wet" + B.emote_lists[DM_DIGEST] = list( + "The drake growls in annoyance before clenching those wrinkled walls tight against your form, grinding away at you!", + "As the beast wanders about, you're forced to slip and slide around amidst a pool of thick digestive goop, sinking briefly into the thick, heavy walls!", + "You can barely hear the drake let out a pleased rumble as its stomach eagerly gurgles around its newfound meal!", + "As the thinning air begins to make you feel dizzy, menacing bworps and grumbles fill that dark, constantly shifting organ!", + "The constant, rhythmic kneading and massaging starts to take its toll along with the muggy heat, making you feel weaker and weaker!", + "The drake happily wanders around while digesting its meal, almost like it is trying to show off the hanging gut you've given it. Not like it made much of a difference on his already borderline obese form anyway~") + +/mob/living/simple_mob/vore/cryptdrake/do_special_attack(atom/A) //Mostly copied from hunter.dm + set waitfor = FALSE + if(!isliving(A)) + return FALSE + var/mob/living/L = A + if(!L.devourable || !L.allowmobvore || !L.can_be_drop_prey || !L.throw_vore || L.unacidable) + return FALSE + + set_AI_busy(TRUE) + visible_message(span("warning","\The [src]'s eyes flash ominously!")) + to_chat(L, span("danger","\The [src] focuses on you!")) + // Telegraph, since getting stunned suddenly feels bad. + do_windup_animation(A, leap_warmup) + sleep(leap_warmup) // For the telegraphing. + + if(L.z != z) //Make sure you haven't disappeared to somewhere we can't go + set_AI_busy(FALSE) + return FALSE + + // Do the actual leap. + status_flags |= LEAPING // Lets us pass over everything. + visible_message(span("critical","\The [src] leaps at \the [L]!")) + throw_at(get_step(L, get_turf(src)), special_attack_max_range+1, 1, src) + playsound(src, leap_sound, 75, 1) + + sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false. + + if(status_flags & LEAPING) + status_flags &= ~LEAPING // Revert special passage ability. + + set_AI_busy(FALSE) + if(Adjacent(L)) //We leapt at them but we didn't manage to hit them, let's see if we're next to them + L.Weaken(2) //get knocked down, idiot \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm index e6c84387c80..2ff98138eb9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm @@ -57,7 +57,7 @@ var/mob/living/target = pick(potentials) if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) - to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") // Do this after the potential vore, so we get the belly update_icon() @@ -201,10 +201,10 @@ var/mob/living/target = pick(potentials) if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) - to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") // Do this after the potential vore, so we get the belly update_icon() shift_state = AB_SHIFT_NONE - last_shift = world.time \ No newline at end of file + last_shift = world.time diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dominated_brain.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dominated_brain.dm index a6c46835677..f5e93af8220 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dominated_brain.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dominated_brain.dm @@ -197,6 +197,9 @@ if(!pred.ckey) to_chat(prey, "\The [pred] isn't able to be dominated.") return + if(isrobot(pred) && jobban_isbanned(prey, "Cyborg")) + to_chat(prey, "Forces beyond your comprehension forbid you from taking control of [pred].") + return if(prey.prey_controlled) to_chat(prey, "You are already controlling someone, you can't control anyone else at this time.") return @@ -362,7 +365,7 @@ db.ooc_notes_likes = M.ooc_notes_likes db.ooc_notes_dislikes = M.ooc_notes_dislikes db.prey_ooc_likes = M.ooc_notes_likes - db.prey_ooc_likes = M.ooc_notes_dislikes + db.prey_ooc_dislikes = M.ooc_notes_dislikes db.verbs |= /mob/living/dominated_brain/proc/cease_this_foolishness absorb_langs() @@ -421,17 +424,20 @@ var/mob/living/pred = src if(prey.stat == DEAD) - to_chat(prey, "You cannot do that to this prey.") + to_chat(pred, "You cannot do that to this prey.") return if(!prey.ckey) - to_chat(prey, "\The [prey] cannot take control.") + to_chat(pred, "\The [prey] cannot take control.") + return + if(isrobot(pred) && jobban_isbanned(prey, "Cyborg")) + to_chat(pred, "Forces beyond your comprehension prevent you from giving [prey] control.") return if(prey.prey_controlled) - to_chat(prey, "\The [prey] is already under someone's control and cannot be given control of your body.") + to_chat(pred, "\The [prey] is already under someone's control and cannot be given control of your body.") return if(pred.prey_controlled) - to_chat(prey, "You are already controlling someone's body.") + to_chat(pred, "You are already controlling someone's body.") return if(tgui_alert(pred, "You are attempting to give [prey] control over you, are you sure? Ensure that their preferences align with this kind of play.", "Give Prey Control",list("No","Yes")) != "Yes") return diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm index e6bacc0ed1e..e6fee921b79 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm @@ -31,7 +31,7 @@ mimic_active = FALSE if(prob(mimic_chance)) var/mob/living/simple_mob/vore/aggressive/mimic/new_mimic = new(loc, src) - visible_message("[new_mimic] suddenly growls as it turns out to be a mimic!") + visible_message(span_red("[new_mimic] suddenly growls as it turns out to be a mimic!")) forceMove(new_mimic) new_mimic.real_crate = src new_mimic.name = name @@ -52,7 +52,7 @@ /obj/structure/closet/crate/mimic/damage(var/damage) if(contents.len) - visible_message("[src] makes out a crunchy noise as its contents are destroyed!") + visible_message(span_red("[src] makes out a crunchy noise as its contents are destroyed!")) for(var/obj/O in src.contents) qdel(O) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm index 5fac8585273..6d3d58f40ac 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -221,7 +221,7 @@ if(target.buckled) target.buckled.unbuckle_mob(target, force = TRUE) target.forceMove(vore_selected) - to_chat(target,"\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + to_chat(target,"\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") else ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/peasant.dm b/code/modules/mob/living/simple_mob/subtypes/vore/peasant.dm new file mode 100644 index 00000000000..1bd10e9ee1e --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/peasant.dm @@ -0,0 +1,72 @@ +/mob/living/simple_mob/vore/peasant + name = "peasant" + desc = "They're just about getting by." + tt_desc = "Homo Sapiens" + + icon_state = "peasantfa" + icon = 'icons/mob/vore.dmi' + + harm_intent_damage = 2 + melee_damage_lower = 1 + melee_damage_upper = 2 + + response_help = "pats" + response_disarm = "pushes" + response_harm = "punches" + + attacktext = list("punched","kicked") + + say_list_type = /datum/say_list/peasant + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/edible + + var/random_skin = 1 + var/list/skins = list( + "peasantfa", + "peasantfb", + "peasantfc", + "peasantma", + "peasantmb" + ) + + faction = "peasant" + +/mob/living/simple_mob/vore/peasant/New() + ..() + if(random_skin) + icon_living = pick(skins) + icon_rest = "[icon_living]asleep" + icon_dead = "[icon_living]-dead" + update_icon() + +// Activate Noms! +/mob/living/simple_mob/vore/peasant + vore_active = 1 + vore_bump_chance = 10 + vore_pounce_chance = 20 + vore_standing_too = 1 + vore_ignores_undigestable = 0 + vore_default_mode = DM_HOLD + vore_digest_chance = 25 // But don't you dare try to escape... + vore_icons = SA_ICON_LIVING | SA_ICON_REST + +/datum/say_list/peasant + speak = list("Fine day!","What was I doing again? Oh yeah, nothing.","How are ya?","I sure do love bread.","Where did I leave my scraps of cloth?","Sure is a good time to exist, I guess.") + emote_hear = list("yawns","'s stomach grumbles","shuffles") + emote_see = list("exists","just stands there","smiles","looks around") + +/mob/living/simple_mob/vore/peasant/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "You've somehow managed to get yourself eaten by one of the local peasants. After jamming you down into their stomach, you find yourself cramped up tight in a space that clearly shouldn't be able to accept you. They let out a relieved sigh as they heft around their new found weight, giving it a hearty pat, clearly content to get a good meal for once. The world around you groans and grumbles, but the gut is far from harmful to you right now, even as the walls clench down on your body." + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "yet_another_tumby" + B.digest_brute = 1 + B.digest_burn = 1 + B.digest_oxy = 0 + B.digestchance = 25 + B.absorbchance = 0 + B.escapechance = 15 + B.selective_preference = DM_HOLD + B.escape_stun = 5 + diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/raptor.dm b/code/modules/mob/living/simple_mob/subtypes/vore/raptor.dm new file mode 100644 index 00000000000..5adb15f8652 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/raptor.dm @@ -0,0 +1,149 @@ +/mob/living/simple_mob/vore/raptor + name = "raptor" + desc = "A massive raptor with piercing yellow eyes. Long sharp claws and huge pointy teeth, I wouldn't want to upset this thing." + tt_desc = "Velociraptor voroliensis" + icon = 'icons/mob/vore_raptor.dmi' + icon_dead = "raptorpurple_dead" + icon_living = "raptorpurple" + icon_state = "raptorpurple" + icon_rest = "raptorpurple" + faction = "raptor" + meat_amount = 40 //Big dog, lots of meat + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + old_x = -48 + old_y = 0 + vis_height = 92 + friendly = list("nudges", "sniffs on", "rumbles softly at", "slobberlicks") + default_pixel_x = -48 + pixel_x = -48 + pixel_y = 0 + response_help = "pets" + response_disarm = "shoves" + response_harm = "smacks" + movement_cooldown = -1 + harm_intent_damage = 10 + melee_damage_lower = 10 + melee_damage_upper = 15 + maxHealth = 500 + attacktext = list("chomped") + see_in_dark = 8 + minbodytemp = 0 + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + max_buckled_mobs = 1 + mount_offset_y = 32 + mount_offset_x = -16 + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + var/initial_icon = "raptorpurple" + var/wg_state = 0 + + var/random_skin = 1 + var/list/skins = list( + "raptorpurple", + "raptorgreen", + "raptorred", + "raptorblue", + "raptorblack", + "raptorwhite" + ) + +/mob/living/simple_mob/vore/raptor + + vore_bump_chance = 25 + vore_digest_chance = 5 + vore_escape_chance = 5 + vore_pounce_chance = 50 + vore_active = 1 + vore_icons = SA_ICON_LIVING | SA_ICON_REST + vore_capacity = 1 + swallowTime = 50 + vore_ignores_undigestable = TRUE + vore_default_mode = DM_DIGEST + vore_pounce_maxhealth = 125 + vore_bump_emote = "tries to snap up" + +/mob/living/simple_mob/vore/raptor/New() + ..() + if(random_skin) + icon_living = pick(skins) + initial_icon = icon_living + icon_rest = "[icon_living]" + icon_dead = "[icon_living]_dead" + update_icon() + +/mob/living/simple_mob/vore/raptor/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_mob(src) + verbs |= /mob/living/simple_mob/proc/animal_mount + verbs |= /mob/living/proc/toggle_rider_reins + movement_cooldown = -1 + +/mob/living/simple_mob/vore/raptor/init_vore() + . = ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "The raptor pounces atop you, pinning you under sharp taloned feet, a heavy weight on your chest. Unable to do much more than look up at the reptilian face, looking back down at you with a curious bird-like expression, you're helpless in the moment. With little more warning, the jaws lunge open and dive down towards your head. Engulfing you in the wet maw, tongue pushing against your face, a cage of jagged teeth hold you in place. Pulling you upwards roughly, the creature begins to chomp it's way down your body, and you're steadily transferred down the rippling, tight tunnel of its gullet. The beast throws its head backwards, sending you spiralling down toward's its stomach, where you land with a wet splash. The walls of the gut immediately clamp down on you, wrinkled flesh grinding the gastric slurry of acids and enzymes into your trapped form. A cacophony of bodily functions, groans, gurgles and heart beats overwhelm your hearing. This creature's body has clearly decided you're little more than meat to it now." + B.vore_sound = "Tauric Swallow" + B.release_sound = "Pred Escape" + B.mode_flags = DM_FLAG_THICKBELLY + B.fancy_vore = 1 + B.selective_preference = DM_DIGEST + B.vore_verb = "devour" + B.digest_brute = 3 + B.digest_burn = 2 + B.digest_oxy = 0 + B.digestchance = 50 + B.absorbchance = 0 + B.escapechance = 5 + B.escape_stun = 5 + B.contamination_color = "grey" + B.contamination_flavor = "Wet" + B.emote_lists[DM_DIGEST] = list( + "The raptor growls in annoyance before clenching those wrinkled walls tight against your form, grinding away at you!", + "As the beast wanders about, you're forced to slip and slide around amidst a pool of thick digestive goop, sinking briefly into the thick, heavy walls!", + "You can barely hear the drake let out a pleased rumble as its stomach eagerly gurgles around its newfound meal!", + "As the thinning air begins to make you feel dizzy, menacing bworps and grumbles fill that dark, constantly shifting organ!", + "The constant, rhythmic kneading and massaging starts to take its toll along with the muggy heat, making you feel weaker and weaker!", + "The raptor happily wanders around while digesting its meal, almost like it is trying to show off the hanging gut you've given it. Not like it made much of a difference on his already borderline obese form anyway~") + +/mob/living/simple_mob/vore/raptor/adjust_nutrition() + ..() + consider_wg() + +/mob/living/simple_mob/vore/raptor/proc/consider_wg() + var/past_state = wg_state + if(nutrition >= 900) + wg_state = 1 + else + wg_state = 0 + if(past_state != wg_state) + update_icon() + +/mob/living/simple_mob/vore/raptor/update_icon() + if(wg_state == 1) + icon_living = "[initial_icon]_fat" + icon_state = icon_living + else + icon_living = "[initial_icon]" + icon_state = icon_living + . = ..() + if(vore_active) + var/voremob_awake = FALSE + if(icon_state == icon_living) + voremob_awake = TRUE + update_fullness() + if(!vore_fullness) + update_transform() + return 0 + else if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED)) && (vore_icons & SA_ICON_LIVING)) + icon_state = "[icon_living]-[vore_fullness]" + else if(stat >= DEAD && (vore_icons & SA_ICON_DEAD)) + icon_state = "[icon_dead]-[vore_fullness]" + else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest && (vore_icons & SA_ICON_REST)) + icon_state = "[icon_rest]-[vore_fullness]" + if(vore_eyes && voremob_awake) //Update eye layer if applicable. + remove_eyes() + add_eyes() + update_transform() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/scel.dm b/code/modules/mob/living/simple_mob/subtypes/vore/scel.dm new file mode 100644 index 00000000000..deef2f06229 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/scel.dm @@ -0,0 +1,261 @@ +//This mob's design and details belong to Kira72 on discord and BYOND. Please consult them before making changes to the discriptions and abilities of the mob, +//and please respect their wishes to keep the mob in line with their vision for it. This is not any sort of formal copyright notice, but a polite request. + +/mob/living/simple_mob/vore/scel + name = "scel" + desc = "A large serpentine creature with malleable and shadowy-black flesh covering most of it's body. The soft substance seems solid, but hardly seems to stay in a singular shape. There are numerous spots of neon-bright colour that's visible through translucent patches across their body." + catalogue_data = list(/datum/category_item/catalogue/fauna/scel) + tt_desc = "Voidwyrm" + icon = 'icons/mob/vore_scel.dmi' + icon_dead = "scel_orange-dead" + icon_living = "scel_orange" + icon_state = "scel_orange" + icon_rest = "scel_orange-rest" + faction = "scel" + old_x = -48 + old_y = 0 + vis_height = 92 + friendly = list("nudges", "sniffs on", "rumbles softly at", "nuzzles") + default_pixel_x = -48 + pixel_x = -48 + pixel_y = 0 + response_help = "bumps" + response_disarm = "shoves" + response_harm = "attacks" + movement_cooldown = 0 + harm_intent_damage = 15 + melee_damage_lower = 5 + melee_damage_upper = 15 + maxHealth = 400 + attacktext = list("slammed") + see_in_dark = 8 + minbodytemp = 0 + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + max_buckled_mobs = 1 + mount_offset_y = 32 + mount_offset_x = -16 + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + + special_attack_min_range = 2 + special_attack_max_range = 4 + special_attack_cooldown = 30 SECONDS + + appendage_color = "#000000" + + var/leap_warmup = 2 SECOND // How long the leap telegraphing is. + var/leap_sound = 'sound/weapons/spiderlunge.ogg' + + var/random_skin = 1 + var/list/skins = list( + "scel_orange", + "scel_blue", + "scel_purple", + "scel_red", + "scel_green" + ) + +/mob/living/simple_mob/vore/scel/New() + ..() + if(random_skin) + icon_living = pick(skins) + icon_rest = "[icon_living]-rest" + icon_dead = "[icon_living]-dead" + update_icon() + +/mob/living/simple_mob/vore/scel + + vore_bump_chance = 25 + vore_digest_chance = 50 + vore_escape_chance = 5 + vore_pounce_chance = 100 + vore_active = 1 + vore_icons = 1 + vore_icons = SA_ICON_LIVING | SA_ICON_REST + vore_capacity = 1 + swallowTime = 50 + vore_ignores_undigestable = TRUE + vore_default_mode = DM_SELECT + vore_pounce_maxhealth = 125 + vore_bump_emote = "tries to devour" + +/mob/living/simple_mob/vore/scel/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_mob(src) + verbs |= /mob/living/simple_mob/proc/animal_mount + verbs |= /mob/living/proc/toggle_rider_reins + verbs |= /mob/living/proc/glow_toggle + verbs |= /mob/living/proc/glow_color + verbs |= /mob/living/proc/long_vore + verbs |= /mob/living/proc/target_lunge + movement_cooldown = -1 + +/mob/living/simple_mob/vore/scel/init_vore() + . = ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "The strange beast pounces atop of you, pinning you with it's hefty weight, the greedy stomach sloshing atop you before it looms closer, with that maw stretching almost impossibly wide and easily enveloping your body. The jaws of the scel are solid and fleshy, preventing any movement. Immediately you are embraced into the tight pull of oily black flesh, and you are rapidly dragged down into the humid, more malleable depths of the creature! Splashing down into a thick, ominously bubbling sludge, you fall into place as food among the remnants of previous meals. As you settle, you are completely hidden away inside of it's serpentine body, left to complete darkness and the sounds of squelching bodily functions. Movement is not difficult as the flesh around you is easily pushed back, but it quickly snaps back into place to keep you constrained. Slowly, the neon liquid that you could see from the outside of the creature begins to seep into the chamber. With that hefty stomach bloated with yet another meal, a lazy burp signals its meal complete. Leaving you to simmer in its depths as it continues its prowling..." + B.vore_sound = "Tauric Swallow" + B.release_sound = "Pred Escape" + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "a_anim_belly" + B.fancy_vore = 1 + B.selective_preference = DM_SELECT + B.vore_verb = "devour" + B.digest_brute = 1 + B.digest_burn = 2 + B.digest_oxy = 0 + B.digestchance = 50 + B.absorbchance = 0 + B.escapechance = 10 + B.escape_stun = 5 + B.contamination_color = "grey" + B.contamination_flavor = "Wet" + B.emote_lists[DM_DIGEST] = list( + "The walls smother and press against you with a marshmallowy softness. Mechanically compressing you in waves to ease in your digestion.", + "As the scel slithers about, you feel the squishy walls around you roll over your form, causing the pool of digestive liquids to splash and churn against you.", + "Curious sounds can be heard muffled through the flesh as the creature pokes and prods at it's groaning gut.", + "As the thinning air begins to make you feel dizzy, menacing bworps and grumbles fill that dark, constantly shifting organ!", + "The constant, rhythmic kneading and massaging starts to take its toll along with the muggy heat, easing your body to sink more into the bubbling pool around you!", + "The vibrant neon liquid pools over your body, it bubbles over your helpless figure as it steadily melts you down. All to churn you up to add an extra little weight to this strange beast.", + "The alien serpent seems to be kneading and massaging it's noisy stomach, sloshing noisily as you are shoved about inside.", + "The creature seems to stretch and arch their body for a moment, tightening you against those slick walls. For a brief moment, you can almost see the outside world.", + "The walls of this well-padded gut rumbles as the Scel lets out a crass belch. Forcing the walls of this roiling belly tighter against you as it works to digest you!") + B.digest_messages_prey = "The greedy walls compress around you as the Scel gives out an audible belch, taking with it the last of your air. You slowly slip under the bubbling and caustic tide of sludge as it overtakes your body. Your senses fade as the walls lazily ripple around you, reducing your body to a hot churning slop. The Scel's belly softens and rounds as you slowly amount to little more than some extra soft pudge on the lazy wryms body!" + B.struggle_messages_outside = list( + "The Scel stifles a belch before bringing a long tentacle to knead over its distended belly. It's noisily churning and gurgling, with every movement from within eliciting a loud slosh or squelch.", + "The shadowy serpent lets out a crass belch as its roiling, bulging belly slowly and noisily digests a wriggling meal. If you squint, you can just barely see the shilouette of someone!", + "The Scel clenches it's wriggling middle, sending that lumpy bulge further down along it's body! The distinct shape fading into its chubby body.", + "With a long and lazy stretch, the Scel causes the walls of its stomach to compress around their squirming meal, revealing the slowly digesting shape of someone!", + "The Scel's well-rounded belly bubbles and groans, making wet gurgling noises as it sloshes in its attempts to digest an uncooperative meal!") + B.struggle_messages_inside = list( + "Your squirming and struggling just serves to kick the churning liquid around you in rough waves of digestive enzymes!", + "Your movements just prompt the strange creature shoves a tendril into their gut, causing the walls to compress into you and knocking you off balance.", + "You simply slide against the slick black flesh and splash back down into the neon-colored goop, doing little but to coat yourself in those roiling waves.", + "The slurry around you bubbles ominously, before a rush of air blasts past you and a rumbling belch shakes the walls, tightening against you even more.", + "The belly doesn't react to your wriggling at all, with a constant stream of 'blurps', 'blorbles', and 'groans'. The noisy stomach compresses you from all angles as it works to digest you like everything else that enters this gut.", + "You shove at the pudgy walls with all your strength, almost catching a vague glimpse of the outside world, before the Scel clenches their stomach, sending you bounding back inside as the stomach sloshes.") + B.examine_messages = list( + "The belly of the creature sags outwards, pushing out against its flexible flesh. It rumbles contently..", + "The Scel looks quite content right now, with multiple black tendrils rubbing over the sloshing, groaning mass of its gut.", + "A soft belch escapes the jaws of the alien creature as its soft and lumpy belly works to digest its latest meal.") + +/mob/living/simple_mob/vore/scel/do_special_attack(atom/A) + . = TRUE + if(ckey) + return + if(prob(50)) + lunge(A) + else + tongue(A) + +/mob/living/simple_mob/vore/scel/proc/lunge(atom/A) //Mostly copied from hunter.dm + set waitfor = FALSE + if(!isliving(A)) + return FALSE + var/mob/living/L = A + if(!L.devourable || !L.allowmobvore || !L.can_be_drop_prey || !L.throw_vore || L.unacidable) + return FALSE + + set_AI_busy(TRUE) + visible_message(span("warning","\The [src] rears back, ready to lunge!")) + to_chat(L, span("danger","\The [src] focuses on you!")) + // Telegraph, since getting stunned suddenly feels bad. + do_windup_animation(A, leap_warmup) + sleep(leap_warmup) // For the telegraphing. + + if(L.z != z) //Make sure you haven't disappeared to somewhere we can't go + set_AI_busy(FALSE) + return FALSE + + // Do the actual leap. + status_flags |= LEAPING // Lets us pass over everything. + visible_message(span("critical","\The [src] leaps at \the [L]!")) + throw_at(get_step(L, get_turf(src)), special_attack_max_range+1, 1, src) + playsound(src, leap_sound, 75, 1) + + sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false. + + if(status_flags & LEAPING) + status_flags &= ~LEAPING // Revert special passage ability. + + set_AI_busy(FALSE) + if(Adjacent(L)) //We leapt at them but we didn't manage to hit them, let's see if we're next to them + L.Weaken(2) //get knocked down, idiot + +/mob/living/simple_mob/vore/scel/proc/tongue(atom/A) + var/obj/item/projectile/P = new /obj/item/projectile/beam/appendage(get_turf(src)) + src.visible_message("\The [src] launches a black appendage at \the [A]!") + playsound(src, "sound/effects/slime_squish.ogg", 50, 1) + P.launch_projectile(A, BP_TORSO, src) + +/datum/category_item/catalogue/fauna/scel + name = "Extra-Realspace Fauna - Scel" + desc = "Classification: Voidwyrm\ +

                    \ + Scel, colloquially known as Voidwyrms, are creatures originating from outside of realspace, typically found at extradimensional breaches such as redpsace tears or within redgate locations. \ + While they are known to appear in many variants, the serpentine shape with antennae or whiskers is known to be the most common, due to being the closest and most accurate approximation of their natural forms from their home reality. \ + Scel bodies, contrary to popular belief, are not slime. They are solid matter, but are still incredibly malleable, which they use to 'shapeshift' when desired. The shifting is limited to just their figure. A scel attempting to look human would still have black squishy flesh and glowing eyes and spots for example. \ + Various other appendages seem to manifest at will, growing out of their body when needed. Tentacles are by-far the most preferred and commonly seen, but hardened legs and even clawed hands have been seen. While able to shapeshift into even human or animal shapes, it is always in shape only, and certain traits are universal. \ + Six eyes that match the color of their 'Goo', universally black and lacking in bones, and no body heat at all, typically matching the temperature of whatever environment they find themselves in. \ + Organs and bones as we understand them are nonexistant, and their body is made up of two main quantifiable substances. The black 'Flesh' that makes up the majority of their matter. It is solid yet incredibly malleable to the touch, and has been described as feeling 'slick'. \ + The next is the viscous liquid that seems to be the source of their color. This liquid seems to fulfill many properties for the Scel, shifting in transluscent pockets inside of their body. \ + Both their flesh and 'goo' dissipate into a fine black mist within hours of being seperated from its host, or the death of the subject. \ + Scel are sapient, and are therefore hard to predict, many have been immediately hostile, while a rare few have even taught themselves our language. Contact has been sparse though. What has been discovered is that most Scel seem to act independantly, with no formal governance. \ + Their alien mindset seems to have difficulty adapting to our world, further causing contact attempts to degrade into misunderstandings. \ + Scel, once breached into realspace, seem incredibly hesitant to return to their own reality, and are likely to be found in places with little to no natural light, as it seems to confuse their senses." + value = CATALOGUER_REWARD_HARD + +/mob/living/simple_mob/vore/scel/orange + icon_dead = "scel_orange-dead" + icon_living = "scel_orange" + icon_state = "scel_orange" + icon_rest = "scel_orange-rest" + random_skin = 0 + +/mob/living/simple_mob/vore/scel/blue + icon_dead = "scel_blue-dead" + icon_living = "scel_blue" + icon_state = "scel_blue" + icon_rest = "scel_blue-rest" + random_skin = 0 + +/mob/living/simple_mob/vore/scel/purple + icon_dead = "scel_purple-dead" + icon_living = "scel_purple" + icon_state = "scel_purple" + icon_rest = "scel_purple-rest" + random_skin = 0 + +/mob/living/simple_mob/vore/scel/green + icon_dead = "scel_green-dead" + icon_living = "scel_green" + icon_state = "scel_green" + icon_rest = "scel_green-rest" + random_skin = 0 + +/mob/living/simple_mob/vore/scel/red + icon_dead = "scel_red-dead" + icon_living = "scel_red" + icon_state = "scel_red" + icon_rest = "scel_red-rest" + random_skin = 0 + +/mob/living/simple_mob/vore/scel/handle_light() + if(glow_override) + return FALSE + + if(instability >= TECHNOMANCER_INSTABILITY_MIN_GLOW) + var/distance = round(sqrt(instability / 2)) + if(distance) + set_light(distance, distance * 4, l_color = "#660066") + return TRUE + + else if(glow_toggle) + set_light(glow_range, glow_intensity, glow_color) + + else + set_light(0) + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm index ec1161630a6..d094bd91143 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm @@ -45,7 +45,7 @@ var/mob/living/target = pick(potentials) if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) - to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") // Do this after the potential vore, so we get the belly update_icon() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/sheep.dm b/code/modules/mob/living/simple_mob/subtypes/vore/sheep.dm index a4066e4758d..75c8f94b3d7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/sheep.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/sheep.dm @@ -33,7 +33,7 @@ mount_offset_x = 0 say_list_type = /datum/say_list/sheep - ai_holder_type = /datum/ai_holder/simple_mob/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/edible // var/harvestable_wool = TRUE //Relating to wool growth and shearing down below. // var/wool_growth = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm b/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm index b6504a84802..078da106367 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/softdog.dm @@ -1,315 +1,315 @@ -/datum/category_item/catalogue/fauna/woof - name = "Wildlife - Dog" - desc = "It's a relatively ordinary looking canine. \ - It has an ominous aura..." - value = CATALOGUER_REWARD_EASY - -/mob/living/simple_mob/vore/woof - name = "dog" - desc = "It is a relatively ordinary looking canine mutt! It radiates mischief!" - tt_desc = "E Canis lupus softus" - - icon_state = "woof" - icon_living = "woof" - icon_dead = "woof_dead" - icon_rest = "woof_rest" - icon = 'icons/mob/vore.dmi' - - faction = "dog" - maxHealth = 600 - health = 600 - movement_cooldown = -1 - - response_help = "pets" - response_disarm = "rudely paps" - response_harm = "punches" - - harm_intent_damage = 5 - melee_damage_lower = 5 - melee_damage_upper = 1 - catalogue_data = list(/datum/category_item/catalogue/fauna/woof) - - var/knockdown_chance = 20 - - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - maxbodytemp = 900 - - attacktext = list("nipped", "chomped", "bullied", "gnaws on") - attack_sound = 'sound/voice/bork.ogg' - friendly = list("snoofs", "nuzzles", "ruffs happily at", "smooshes on") - - ai_holder_type = /datum/ai_holder/simple_mob/woof - - mob_size = MOB_SMALL - - has_langs = list(LANGUAGE_ANIMAL, LANGUAGE_CANILUNZT, LANGUAGE_GALCOM) - say_list_type = /datum/say_list/softdog - swallowTime = 0.1 SECONDS - -/mob/living/simple_mob/vore/woof/New() - ..() - - verbs += /mob/living/proc/ventcrawl - verbs += /mob/living/proc/hide - -/datum/say_list/softdog - speak = list("Woof~", "Woof!", "Yip!", "Yap!", "Yip~", "Yap~", "Awoooooo~", "Awoo!", "AwooooooooooOOOOOOoOooOoooOoOOoooo!") - emote_hear = list("barks", "woofs", "yaps", "yips","pants", "snoofs") - emote_see = list("wags its tail", "stretches", "yawns", "swivels its ears") - say_maybe_target = list("Whuff?") - say_got_target = list("Grrrr YIP YAP!!!") - -/datum/ai_holder/simple_mob/woof - hostile = FALSE - cooperative = TRUE - retaliate = TRUE - speak_chance = 1 - wander = TRUE - -// Activate Noms! -/mob/living/simple_mob/vore/woof - vore_active = 1 - vore_capacity = 3 - vore_bump_chance = 5 - vore_bump_emote = "greedily homms at" - vore_digest_chance = 1 - vore_absorb_chance = 5 - vore_escape_chance = 10 - vore_pounce_chance = 5 - vore_ignores_undigestable = 0 - vore_default_mode = DM_HOLD - vore_icons = SA_ICON_LIVING - vore_stomach_name = "Stomach" - vore_stomach_flavor = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." - vore_default_contamination_flavor = "Wet" - vore_default_contamination_color = "grey" - vore_default_item_mode = IM_DIGEST - - -/mob/living/simple_mob/vore/woof/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "stomach" - B.desc = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." - - B.emote_lists[DM_HOLD] = list( - "You can feel yourself shift and sway as the dog moves around. Your figure held tightly there had little room to move in that organic gloom, but every wandering step is another jostling quake that shakes through the canine frame and rocks you once again.", - "It is hard to hear much of anything over the grumbling fleshy sounds of the stomach walls pressing to you. The wet sound of flesh gliding over you too was ever-present as the walls encroach upon your personal space. And beyond that, the steady booming of the dog's heart throbs in your ears, a relaxing drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls hold on to you greedily.", - "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to groan and flex in to smother you heavily for a few moments. ", - "When you shift your weight to try to find a more comfortable position you can feel your weight stretch the chamber around you a little more, and it responds by collapsing in on you more tightly! Forming to you with heavily insistence, grinding against your curves. Holding you firmly for a few moments, before slowly relaxing...", - "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to be squeezed and carried by this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state, as you're rocked and supported, squeezed deep within the gut of this woof. Possessively held, kept.", - "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", - "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against.") - - B.emote_lists[DM_ABSORB] = list( - "You can feel the weight of the dog shift as it moves around. Your figure held tightly there had absolutely no room to move in that organic gloom. Every moment those pumping walls seem to squeeze over you tighter, every wandering step the dog takes is another jostling quake that seems to sink you that much deeper into the dog's flesh, the dog's body steadily collapsing in to claim you.", - "It is hard to hear much of anything over the smothering press of stomach walls pressing to you, forming to your features. The tarry flesh you are slowly sinking into squelches here and there as it flows over your features. The sound of the dog's body absorbing you is oddly quiet. No bubbling or glooping. Just one body slowly blending into and becoming one with another. And beyond all that, the steady booming of the dog's heart throbs in your ears, moment by moment that sound seems to tug at you, coursing through you as much as the dog you are steadily becoming a part of. Any sounds from the outside world are muffled such that they are hard to hear, as you sink into the walls of this canine predator.", - "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to simply flow over you, and allow whatever pushed out to sink in that much more. The swell on the dog's tummy shrinking that much faster... ", - "When you shift your weight to try to get some space. you can feel your weight simply sink into that flesh, the folds forming around you tightly, and the deeper you sink, the harder it gets to move. The pressure never seems to let up as the tide of flesh holding you slowly overcomes your form.", - "As the seemingly molten heat of this dog's flesh flows over you, it's easy to let yourself go limp, to just give in and become one with this creature that so obviously wanted you, and indeed, unless something happened to stop this, you soon would be... The dog tail swaying in a knowing arc as you are added to its figure. Squeezed, tucked away, kept.", - "The thick slimes that coat your form do nothing to keep the molten flesh of this dog's stomach from advancing across your figure and claiming you up. Soon there's not a single part of you that is not totally inundated in the deep press of a woof's gastric massage. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back, flowing over your form, deeper, deeper.", - "Beyond the squelching, clinging tide of dog flesh working to make the two of you one, and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed. And you realize suddenly that the dog's breathing seem to bring you relief even as you are totally smothered in the canine's insistent gastric affections!") - - B.emote_lists[DM_DIGEST] = list( - "As the dog goes about its business, you can feel the shift your weight sway on its tummy. The gurgling glorping sounds that come with the squeezing, kneading, massaging motions let you know that you're held tight, churned. Dog food.", - "It is hard to hear much of anything over the roaring gurgles of stomach walls churning over you. The wet sound of flesh grinding heavily over you too was ever-present as the walls encroach upon your personal space, lathering you in tingly, syrupy thick slimes. And beyond that, the steady booming of the dog's heart throbs in your ears, a drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls churn on to you greedily.", - "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. The walls seem to constantly flex and squeeze across you, pressing in against you, massaging thick slime into your figure, steadily trying to soften up your outer layers...", - "When you try to shift your weight to try to find a more comfortable position, you find that those heavy walls pumping over you make it hard to move at all. You can feel the weight of the dog pressing in all around you even without those muscles flexing and throbbing across your form. It forms to you with heavily insistence, grinding against your curves, churning that bubbling gloop into you. Holding you firm and heavy as that stomach does its work...", - "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to just completely give in to this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state. Churned and slathered, massaged by doughy wrinkled walls deep within the gut of this woof. Possessively held within that needy chamber.", - "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", - "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against. Occasionally though everything would go all tight and cramped! And somewhere up above you can hear the dog let out a dainty little belch...") - - B.digest_brute = 0.05 - B.digest_burn = 0.05 - B.mode_flags = 8 - B.belly_fullscreen = "a_tumby" - B.struggle_messages_inside = list( - "Your struggling only causes %pred's doughy gut to smother you against those wrinkled walls...", - "As you squirm, %pred's %belly flexxes over you heavily, forming you back into a small ball...", - "You push out at those heavy wrinkled walls with all your might and they collapse back in on you! Clinging and churning over you heavily for a few minutes!!!", - "As you struggle against the gut of this dog, you can feel a squeeze roll over you from the bottom to the top! The walls cling to you a little tighter then as the dog emits a soft little burp...", - "You try to squirm, but you can't even move as those heavy walls throb and pulse and churn around you.", - "You paddle against the fleshy walls of %pred's %belly, making a little space for yourself for a moment, before the wrinkled surface bounces back against you.", - "The slick walls are doughy, smushy under your fingers, and very difficult to grip! The flesh pulses under your grip in time with %pred's heartbeat.", - "Your hands slip and slide over the slick slimes of %pred's %belly as you struggle to escape! The walls pulse and squeeze around you greedily.", - "%pred lets out a happy little awoo, rocking their hips to jostle you as you squirm, while the weight of those walls closes in on you, squeezing you tightly!", - "%pred's %belly glorgles around you as you push and struggle within! The squashy walls are always reluctant to give ground, and the moment your struggles lax, they redouble their efforts in smothering all the fight out of you!") - B.struggle_messages_outside = list( - "A vague shape briefly swells on %pred's %belly as something moves inside...", - "Something shifts within %pred's %belly.", - "%pred urps as something shifts in their %belly.") - B.examine_messages = list( - "Their %belly is distended.", - "Vague shapes swell their %belly.", - "It looks like they have something solid in their %belly") - -/obj/item/projectile/awoo_missile - name = "awoo missile" - icon_state = "force_missile" - fire_sound = 'sound/voice/long_awoo.ogg' - damage = 1 - damage_type = BRUTE - check_armour = "melee" - - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - hitsound_wall = 'sound/voice/bork.ogg' - -/mob/living/simple_mob/vore/woof/cass - name = "Cass" - desc = "Well trained, comfy company. They have a pretty red bow tied into their fur. They look very soft." - - icon_state = "cass" - icon_living = "cass" - icon_dead = "cass_dead" - icon_rest = "cass_rest" - ic_revivable = 0 - - faction = "theatre" - gender = PLURAL - ai_holder_type = /datum/ai_holder/simple_mob/woof/cass - -/mob/living/simple_mob/vore/woof/cass - vore_digest_chance = 0 - vore_escape_chance = 25 - digestable = 0 - -/datum/ai_holder/simple_mob/woof/cass - retaliate = 0 - violent_breakthrough = 0 - -/datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - hostile = 1 - retaliate = 1 - cooperative = TRUE - speak_chance = 1 - lose_target_timeout = 0 // Easily distracted - -/datum/ai_holder/simple_mob/woof/hostile - hostile = 1 - retaliate = 1 - -/mob/living/simple_mob/vore/woof/apply_melee_effects(var/atom/A) - if(isliving(A)) - var/mob/living/L = A - if(prob(knockdown_chance)) - L.Weaken(3) - L.visible_message(span("danger", "\The [src] pounces on \the [L]!")) - -/mob/living/simple_mob/vore/woof/hostile/melee - - movement_cooldown = -2 - - ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile - -/mob/living/simple_mob/vore/woof/hostile/ranged - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - - projectiletype = /obj/item/projectile/awoo_missile - projectilesound = 'sound/voice/long_awoo.ogg' - -/mob/living/simple_mob/vore/woof/hostile/horrible - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - armor = list( - "melee" = 75, - "bullet" = 75, - "laser" = 75, - "energy" = 75, - "bomb" = 75, - "bio" = 75, - "rad" = 75) - - projectiletype = /obj/item/projectile/awoo_missile/heavy - projectilesound = 'sound/voice/long_awoo.ogg' - -/obj/item/projectile/awoo_missile/heavy - damage = 50 - -/obj/item/projectile/forcebolt/harmless/awoobolt - icon_state = "force_missile" - fire_sound = 'sound/voice/long_awoo.ogg' - damage = 0 - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - hitsound_wall = 'sound/voice/bork.ogg' - -/mob/living/simple_mob/vore/woof/hostile/terrible - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - - projectiletype = /obj/item/projectile/forcebolt/harmless/awoobolt - projectilesound = 'sound/voice/long_awoo.ogg' - -/mob/living/simple_mob/vore/woof/cass/attack_hand(mob/living/carbon/human/M as mob) - if(stat != DEAD) - return ..() - if(M.a_intent == I_HELP) - M.visible_message("[M] pets [src].", runemessage = "pets [src]") - if(do_after(M, 30 SECONDS, exclusive = TASK_USER_EXCLUSIVE, target = src)) - faction = M.faction - revive() - sight = initial(sight) - see_in_dark = initial(see_in_dark) - see_invisible = initial(see_invisible) - update_icon() - visible_message("[src] stops playing dead.", runemessage = "[src] stops playing dead") - else - M.visible_message("The petting was interrupted!!!", runemessage = "The petting was interrupted") - return - -/mob/living/simple_mob/vore/woof/hostile/aweful - maxHealth = 100 - health = 100 - var/killswitch = FALSE - - -/mob/living/simple_mob/vore/woof/hostile/aweful/Initialize() - . = ..() - var/thismany = (rand(25,500)) / 100 - resize(thismany, animate = FALSE, uncapped = TRUE, ignore_prefs = TRUE) - -/mob/living/simple_mob/vore/woof/hostile/aweful/death() - . = ..() - if(killswitch) - visible_message("\The [src] evaporates into nothing...") - qdel(src) - return - var/thismany = rand(0,3) - var/list/possiblewoofs = list(/mob/living/simple_mob/vore/woof/hostile/aweful/melee, /mob/living/simple_mob/vore/woof/hostile/aweful/ranged) - if(thismany == 0) - visible_message("\The [src] evaporates into nothing...") - if(thismany >= 1) - var/thiswoof = pick(possiblewoofs) - new thiswoof(loc, src) - visible_message("Another [src] appears!") - if(thismany >= 2) - var/thiswoof = pick(possiblewoofs) - new thiswoof(loc, src) - visible_message("Another [src] appears!") - if(thismany >= 3) - var/thiswoof = pick(possiblewoofs) - new thiswoof(loc, src) - visible_message("Another [src] appears!") - qdel(src) - -/mob/living/simple_mob/vore/woof/hostile/aweful/melee - - movement_cooldown = -2 - - ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile - -/mob/living/simple_mob/vore/woof/hostile/aweful/ranged - movement_cooldown = -2 - - ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof - - projectiletype = /obj/item/projectile/awoo_missile - projectilesound = 'sound/voice/long_awoo.ogg' +/datum/category_item/catalogue/fauna/woof + name = "Wildlife - Dog" + desc = "It's a relatively ordinary looking canine. \ + It has an ominous aura..." + value = CATALOGUER_REWARD_EASY + +/mob/living/simple_mob/vore/woof + name = "dog" + desc = "It is a relatively ordinary looking canine mutt! It radiates mischief!" + tt_desc = "E Canis lupus softus" + + icon_state = "woof" + icon_living = "woof" + icon_dead = "woof_dead" + icon_rest = "woof_rest" + icon = 'icons/mob/vore.dmi' + + faction = "dog" + maxHealth = 600 + health = 600 + movement_cooldown = -1 + + response_help = "pets" + response_disarm = "rudely paps" + response_harm = "punches" + + harm_intent_damage = 5 + melee_damage_lower = 5 + melee_damage_upper = 1 + catalogue_data = list(/datum/category_item/catalogue/fauna/woof) + + var/knockdown_chance = 20 + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + maxbodytemp = 900 + + attacktext = list("nipped", "chomped", "bullied", "gnaws on") + attack_sound = 'sound/voice/bork.ogg' + friendly = list("snoofs", "nuzzles", "ruffs happily at", "smooshes on") + + ai_holder_type = /datum/ai_holder/simple_mob/woof + + mob_size = MOB_SMALL + + has_langs = list(LANGUAGE_ANIMAL, LANGUAGE_CANILUNZT, LANGUAGE_GALCOM) + say_list_type = /datum/say_list/softdog + swallowTime = 0.1 SECONDS + +/mob/living/simple_mob/vore/woof/New() + ..() + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + +/datum/say_list/softdog + speak = list("Woof~", "Woof!", "Yip!", "Yap!", "Yip~", "Yap~", "Awoooooo~", "Awoo!", "AwooooooooooOOOOOOoOooOoooOoOOoooo!") + emote_hear = list("barks", "woofs", "yaps", "yips","pants", "snoofs") + emote_see = list("wags its tail", "stretches", "yawns", "swivels its ears") + say_maybe_target = list("Whuff?") + say_got_target = list("Grrrr YIP YAP!!!") + +/datum/ai_holder/simple_mob/woof + hostile = FALSE + cooperative = TRUE + retaliate = TRUE + speak_chance = 1 + wander = TRUE + +// Activate Noms! +/mob/living/simple_mob/vore/woof + vore_active = 1 + vore_capacity = 3 + vore_bump_chance = 5 + vore_bump_emote = "greedily homms at" + vore_digest_chance = 1 + vore_absorb_chance = 5 + vore_escape_chance = 10 + vore_pounce_chance = 5 + vore_ignores_undigestable = 0 + vore_default_mode = DM_HOLD + vore_icons = SA_ICON_LIVING + vore_stomach_name = "Stomach" + vore_stomach_flavor = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." + vore_default_contamination_flavor = "Wet" + vore_default_contamination_color = "grey" + vore_default_item_mode = IM_DIGEST + + +/mob/living/simple_mob/vore/woof/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "You have found yourself pumping on down, down, down into this extremely soft dog. The slick touches of pulsing walls roll over you in greedy fashion as you're swallowed away, the flesh forms to your figure as in an instant the world is replaced by the hot squeeze of canine gullet. And in another moment a heavy GLLRMMPTCH seals you away, the dog tossing its head eagerly, the way forward stretching to accommodate your shape as you are greedily guzzled down. The wrinkled, doughy walls pulse against you in time to the creature's steady heartbeat. The sounds of the outside world muffled into obscure tones as the wet, grumbling rolls of this soft creature's gut hold you, churning you tightly such that no part of you is spared from these gastric affections." + + B.emote_lists[DM_HOLD] = list( + "You can feel yourself shift and sway as the dog moves around. Your figure held tightly there had little room to move in that organic gloom, but every wandering step is another jostling quake that shakes through the canine frame and rocks you once again.", + "It is hard to hear much of anything over the grumbling fleshy sounds of the stomach walls pressing to you. The wet sound of flesh gliding over you too was ever-present as the walls encroach upon your personal space. And beyond that, the steady booming of the dog's heart throbs in your ears, a relaxing drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls hold on to you greedily.", + "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to groan and flex in to smother you heavily for a few moments. ", + "When you shift your weight to try to find a more comfortable position you can feel your weight stretch the chamber around you a little more, and it responds by collapsing in on you more tightly! Forming to you with heavily insistence, grinding against your curves. Holding you firmly for a few moments, before slowly relaxing...", + "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to be squeezed and carried by this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state, as you're rocked and supported, squeezed deep within the gut of this woof. Possessively held, kept.", + "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", + "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against.") + + B.emote_lists[DM_ABSORB] = list( + "You can feel the weight of the dog shift as it moves around. Your figure held tightly there had absolutely no room to move in that organic gloom. Every moment those pumping walls seem to squeeze over you tighter, every wandering step the dog takes is another jostling quake that seems to sink you that much deeper into the dog's flesh, the dog's body steadily collapsing in to claim you.", + "It is hard to hear much of anything over the smothering press of stomach walls pressing to you, forming to your features. The tarry flesh you are slowly sinking into squelches here and there as it flows over your features. The sound of the dog's body absorbing you is oddly quiet. No bubbling or glooping. Just one body slowly blending into and becoming one with another. And beyond all that, the steady booming of the dog's heart throbs in your ears, moment by moment that sound seems to tug at you, coursing through you as much as the dog you are steadily becoming a part of. Any sounds from the outside world are muffled such that they are hard to hear, as you sink into the walls of this canine predator.", + "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. Any time you press out, the walls seem to simply flow over you, and allow whatever pushed out to sink in that much more. The swell on the dog's tummy shrinking that much faster... ", + "When you shift your weight to try to get some space. you can feel your weight simply sink into that flesh, the folds forming around you tightly, and the deeper you sink, the harder it gets to move. The pressure never seems to let up as the tide of flesh holding you slowly overcomes your form.", + "As the seemingly molten heat of this dog's flesh flows over you, it's easy to let yourself go limp, to just give in and become one with this creature that so obviously wanted you, and indeed, unless something happened to stop this, you soon would be... The dog tail swaying in a knowing arc as you are added to its figure. Squeezed, tucked away, kept.", + "The thick slimes that coat your form do nothing to keep the molten flesh of this dog's stomach from advancing across your figure and claiming you up. Soon there's not a single part of you that is not totally inundated in the deep press of a woof's gastric massage. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back, flowing over your form, deeper, deeper.", + "Beyond the squelching, clinging tide of dog flesh working to make the two of you one, and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed. And you realize suddenly that the dog's breathing seem to bring you relief even as you are totally smothered in the canine's insistent gastric affections!") + + B.emote_lists[DM_DIGEST] = list( + "As the dog goes about its business, you can feel the shift your weight sway on its tummy. The gurgling glorping sounds that come with the squeezing, kneading, massaging motions let you know that you're held tight, churned. Dog food.", + "It is hard to hear much of anything over the roaring gurgles of stomach walls churning over you. The wet sound of flesh grinding heavily over you too was ever-present as the walls encroach upon your personal space, lathering you in tingly, syrupy thick slimes. And beyond that, the steady booming of the dog's heart throbs in your ears, a drone of excited thumping. Any sounds from the outside world are muffled such that they are hard to hear, as the canine walls churn on to you greedily.", + "You can hear the vague dragging, creaking sounds of the flesh holding you stretching and compressing with the dog's movements. The walls seem to constantly flex and squeeze across you, pressing in against you, massaging thick slime into your figure, steadily trying to soften up your outer layers...", + "When you try to shift your weight to try to find a more comfortable position, you find that those heavy walls pumping over you make it hard to move at all. You can feel the weight of the dog pressing in all around you even without those muscles flexing and throbbing across your form. It forms to you with heavily insistence, grinding against your curves, churning that bubbling gloop into you. Holding you firm and heavy as that stomach does its work...", + "The heat of the dog's body soaks into your form and relaxes your muscles. It's easy to let yourself go limp, to just completely give in to this soft predator. The sway of its body, the swing of its gait, all enough to lull anyone who likes such things into a deeply relaxed state. Churned and slathered, massaged by doughy wrinkled walls deep within the gut of this woof. Possessively held within that needy chamber.", + "Thick slime soaks your form as the dog's insides churn over you. There is no part of you that is not totally soaked in it before too long as the steady gastric motions massage you from head to toe. Any pushes or squirms only get the affected flesh to cling more tightly, and to press back. It's very hard to get any personal space!", + "Beyond the grumbling gurgles and the ever-present drumming of the dog's heart, you can actually hear, more faintly, the whooshing of the canine's breath. The slow draw in coming with a vague tightening of your surroundings, while the dog's soft, whooshing exhales make your surroundings more relaxed, easy to sink in against. Occasionally though everything would go all tight and cramped! And somewhere up above you can hear the dog let out a dainty little belch...") + + B.digest_brute = 0.05 + B.digest_burn = 0.05 + B.mode_flags = 8 + B.belly_fullscreen = "a_tumby" + B.struggle_messages_inside = list( + "Your struggling only causes %pred's doughy gut to smother you against those wrinkled walls...", + "As you squirm, %pred's %belly flexxes over you heavily, forming you back into a small ball...", + "You push out at those heavy wrinkled walls with all your might and they collapse back in on you! Clinging and churning over you heavily for a few minutes!!!", + "As you struggle against the gut of this dog, you can feel a squeeze roll over you from the bottom to the top! The walls cling to you a little tighter then as the dog emits a soft little burp...", + "You try to squirm, but you can't even move as those heavy walls throb and pulse and churn around you.", + "You paddle against the fleshy walls of %pred's %belly, making a little space for yourself for a moment, before the wrinkled surface bounces back against you.", + "The slick walls are doughy, smushy under your fingers, and very difficult to grip! The flesh pulses under your grip in time with %pred's heartbeat.", + "Your hands slip and slide over the slick slimes of %pred's %belly as you struggle to escape! The walls pulse and squeeze around you greedily.", + "%pred lets out a happy little awoo, rocking their hips to jostle you as you squirm, while the weight of those walls closes in on you, squeezing you tightly!", + "%pred's %belly glorgles around you as you push and struggle within! The squashy walls are always reluctant to give ground, and the moment your struggles lax, they redouble their efforts in smothering all the fight out of you!") + B.struggle_messages_outside = list( + "A vague shape briefly swells on %pred's %belly as something moves inside...", + "Something shifts within %pred's %belly.", + "%pred urps as something shifts in their %belly.") + B.examine_messages = list( + "Their %belly is distended.", + "Vague shapes swell their %belly.", + "It looks like they have something solid in their %belly") + +/obj/item/projectile/awoo_missile + name = "awoo missile" + icon_state = "force_missile" + fire_sound = 'sound/voice/long_awoo.ogg' + damage = 1 + damage_type = BRUTE + check_armour = "melee" + + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/voice/bork.ogg' + +/mob/living/simple_mob/vore/woof/cass + name = "Cass" + desc = "Well trained, comfy company. They have a pretty red bow tied into their fur. They look very soft." + + icon_state = "cass" + icon_living = "cass" + icon_dead = "cass_dead" + icon_rest = "cass_rest" + ic_revivable = 0 + + faction = "theatre" + gender = PLURAL + ai_holder_type = /datum/ai_holder/simple_mob/woof/cass + +/mob/living/simple_mob/vore/woof/cass + vore_digest_chance = 0 + vore_escape_chance = 25 + digestable = 0 + +/datum/ai_holder/simple_mob/woof/cass + retaliate = 0 + violent_breakthrough = 0 + +/datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + hostile = 1 + retaliate = 1 + cooperative = TRUE + speak_chance = 1 + lose_target_timeout = 0 // Easily distracted + +/datum/ai_holder/simple_mob/woof/hostile + hostile = 1 + retaliate = 1 + +/mob/living/simple_mob/vore/woof/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(knockdown_chance)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] pounces on \the [L]!")) + +/mob/living/simple_mob/vore/woof/hostile/melee + + movement_cooldown = -2 + + ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile + +/mob/living/simple_mob/vore/woof/hostile/ranged + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + + projectiletype = /obj/item/projectile/awoo_missile + projectilesound = 'sound/voice/long_awoo.ogg' + +/mob/living/simple_mob/vore/woof/hostile/horrible + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + armor = list( + "melee" = 75, + "bullet" = 75, + "laser" = 75, + "energy" = 75, + "bomb" = 75, + "bio" = 75, + "rad" = 75) + + projectiletype = /obj/item/projectile/awoo_missile/heavy + projectilesound = 'sound/voice/long_awoo.ogg' + +/obj/item/projectile/awoo_missile/heavy + damage = 50 + +/obj/item/projectile/forcebolt/harmless/awoobolt + icon_state = "force_missile" + fire_sound = 'sound/voice/long_awoo.ogg' + damage = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/voice/bork.ogg' + +/mob/living/simple_mob/vore/woof/hostile/terrible + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + + projectiletype = /obj/item/projectile/forcebolt/harmless/awoobolt + projectilesound = 'sound/voice/long_awoo.ogg' + +/mob/living/simple_mob/vore/woof/cass/attack_hand(mob/living/carbon/human/M as mob) + if(stat != DEAD) + return ..() + if(M.a_intent == I_HELP) + M.visible_message("[M] pets [src].", runemessage = "pets [src]") + if(do_after(M, 30 SECONDS, exclusive = TASK_USER_EXCLUSIVE, target = src)) + faction = M.faction + revive() + sight = initial(sight) + see_in_dark = initial(see_in_dark) + see_invisible = initial(see_invisible) + update_icon() + visible_message("[src] stops playing dead.", runemessage = "[src] stops playing dead") + else + M.visible_message("The petting was interrupted!!!", runemessage = "The petting was interrupted") + return + +/mob/living/simple_mob/vore/woof/hostile/aweful + maxHealth = 100 + health = 100 + var/killswitch = FALSE + + +/mob/living/simple_mob/vore/woof/hostile/aweful/Initialize() + . = ..() + var/thismany = (rand(25,500)) / 100 + resize(thismany, animate = FALSE, uncapped = TRUE, ignore_prefs = TRUE) + +/mob/living/simple_mob/vore/woof/hostile/aweful/death() + . = ..() + if(killswitch) + visible_message("\The [src] evaporates into nothing...") + qdel(src) + return + var/thismany = rand(0,3) + var/list/possiblewoofs = list(/mob/living/simple_mob/vore/woof/hostile/aweful/melee, /mob/living/simple_mob/vore/woof/hostile/aweful/ranged) + if(thismany == 0) + visible_message("\The [src] evaporates into nothing...") + if(thismany >= 1) + var/thiswoof = pick(possiblewoofs) + new thiswoof(loc, src) + visible_message("Another [src] appears!") + if(thismany >= 2) + var/thiswoof = pick(possiblewoofs) + new thiswoof(loc, src) + visible_message("Another [src] appears!") + if(thismany >= 3) + var/thiswoof = pick(possiblewoofs) + new thiswoof(loc, src) + visible_message("Another [src] appears!") + qdel(src) + +/mob/living/simple_mob/vore/woof/hostile/aweful/melee + + movement_cooldown = -2 + + ai_holder_type = /datum/ai_holder/simple_mob/woof/hostile + +/mob/living/simple_mob/vore/woof/hostile/aweful/ranged + movement_cooldown = -2 + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/woof + + projectiletype = /obj/item/projectile/awoo_missile + projectilesound = 'sound/voice/long_awoo.ogg' diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/succubi.dm b/code/modules/mob/living/simple_mob/subtypes/vore/succubi.dm new file mode 100644 index 00000000000..3b5d4f986d4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/succubi.dm @@ -0,0 +1,73 @@ +/mob/living/simple_mob/vore/succubus + name = "succubus" + desc = "She's giving you the 'come hither' look." + tt_desc = "Homo paramour" + + icon_state = "succubus" + icon = 'icons/mob/vore.dmi' + + harm_intent_damage = 5 + melee_damage_lower = 2 + melee_damage_upper = 5 + + response_help = "strokes" + response_disarm = "pushes" + response_harm = "bites" + + attacktext = list("swatted","bapped") + + say_list_type = /datum/say_list/succubus + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/edible + + var/random_skin = 1 + var/list/skins = list( + "succubus", + "succubusbob", + "succubusginger", + "succubusclothed", + "succubusbobclothed", + "succubusgingerclothed" + ) + + faction = "succubus" + +/mob/living/simple_mob/vore/succubus/New() + ..() + if(random_skin) + icon_living = pick(skins) + icon_rest = "[icon_living]asleep" + icon_dead = "[icon_living]-dead" + update_icon() + +// Activate Noms! +/mob/living/simple_mob/vore/succubus + vore_active = 1 + vore_bump_chance = 100 + vore_pounce_chance = 50 + vore_standing_too = 1 + vore_ignores_undigestable = 0 + vore_default_mode = DM_DRAIN // They just want to drain you! + vore_digest_chance = 25 // But don't you dare try to escape... + vore_icons = SA_ICON_LIVING | SA_ICON_REST + +/datum/say_list/succubus + speak = list("Come here cutie!","Let me get a good look at you!","Want to spend a little quality time together?","I don't bite. Much.","Like what you see?","Feel free to sample the goods.") + emote_hear = list("makes a kissing sound","giggles","lets out a needy whine") + emote_see = list("gestures for you to come over","winks","smiles","stretches") + +/mob/living/simple_mob/vore/succubus/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "You find yourself tightly compressed into the stomach of the succubus, with immense pressure squeezing down on you from every direction. The wrinkled walls of the gut knead over you, like a swelteringly hot, wet massage. You can feel movement from the outside, as though the demoness is running her hands over your form with delight. The world around you groans and gurgles, but the fluids that ooze into this place don't seem harmful, yet. Instead, you feel your very energy being steadily depleted, much to the joy of the woman who's claiming it all for herself." + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "yet_another_tumby" + B.digest_brute = 2 + B.digest_burn = 2 + B.digest_oxy = 1 + B.digestchance = 25 + B.absorbchance = 0 + B.escapechance = 15 + B.selective_preference = DM_DRAIN + B.escape_stun = 5 + diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/vampire.dm b/code/modules/mob/living/simple_mob/subtypes/vore/vampire.dm new file mode 100644 index 00000000000..96a760bd28a --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/vore/vampire.dm @@ -0,0 +1,116 @@ +/mob/living/simple_mob/vore/vampire + name = "vampire" + desc = "A large creature with a humanoid upperbody and more feral formed lower body, with four legs and two arms." + + icon_state = "count" + icon = 'icons/mob/vore.dmi' + + harm_intent_damage = 8 + melee_damage_lower = 3 + melee_damage_upper = 7 + maxHealth = 150 + health = 150 + + response_help = "caresses" + response_disarm = "wafts" + response_harm = "bites" + + attacktext = list("bites","sucks","drinks from") + + say_list_type = /datum/say_list/count + ai_holder_type = /datum/ai_holder/simple_mob/vore/edible + + var/random_skin = 1 + var/list/skins = list( + "count", + "countess", + "countnude", + "countessnude" + ) + + faction = "vampire" + +/mob/living/simple_mob/vore/vampire/New() + ..() + if(random_skin) + icon_living = pick(skins) + icon_rest = "[icon_living]asleep" + icon_dead = "[icon_living]-dead" + update_icon() + +// Activate Noms! +/mob/living/simple_mob/vore/vampire + vore_active = 1 + vore_bump_chance = 10 + vore_pounce_chance = 50 + vore_standing_too = 1 + vore_ignores_undigestable = 0 + vore_default_mode = DM_DRAIN + vore_digest_chance = 25 + vore_icons = SA_ICON_LIVING | SA_ICON_REST + +/datum/say_list/count + speak = list("I vant to suck your-","I'm going to get a bite to drink.","All I have is time.","Your presence here is crypt-ic.","I like my coffee decoffinated.","There is bad blood between us.") + emote_hear = list("laughs maniacally","croans","hisses") + emote_see = list("wafts about","licks their lips","flaps a bit") + +/mob/living/simple_mob/vore/vampire/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "Having been rapidly gulped up by the vampire, you find yourself tightly contained with a set of groaning, wrinkled walls. It seems that the beast has decided against draining your lifeforce through you blood, and instead taking a more direct approach as it saps your strength from all around you. Your attacker seems content to just take that essence for now, but it is a gut afterall and struggling may set it off." + B.mode_flags = DM_FLAG_THICKBELLY + B.belly_fullscreen = "yet_another_tumby" + B.digest_brute = 3 + B.digest_burn = 0 + B.digest_oxy = 1 + B.digestchance = 25 + B.absorbchance = 0 + B.escapechance = 10 + B.selective_preference = DM_DRAIN + B.escape_stun = 5 + +/mob/living/simple_mob/vore/vampire/count + random_skin = 0 + icon_state = "count" + icon_living = "count" + icon_rest = "countasleep" + icon_dead = "count-dead" + +/mob/living/simple_mob/vore/vampire/countess + random_skin = 0 + icon_state = "countess" + icon_living = "countess" + icon_rest = "countessasleep" + icon_dead = "countess-dead" + +/mob/living/simple_mob/vore/vampire/count_nude + random_skin = 0 + icon_state = "countnude" + icon_living = "countnude" + icon_rest = "countnudeasleep" + icon_dead = "countnude-dead" + +/mob/living/simple_mob/vore/vampire/countess_nude + random_skin = 0 + icon_state = "countessnude" + icon_living = "countessnude" + icon_rest = "countessnudeasleep" + icon_dead = "countessnude-dead" + +/mob/living/simple_mob/vore/vampire/queen + random_skin = 0 + icon_state = "countessqueen" + icon_living = "countessqueen" + icon_rest = "countessqueenasleep" + icon_dead = "countessqueen-dead" + harm_intent_damage = 10 + melee_damage_lower = 5 + melee_damage_upper = 9 + maxHealth = 350 + health = 350 + vore_pounce_chance = 75 + +/mob/living/simple_mob/vore/vampire/queen/New() + resize(2) + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/vore_hostile.dm b/code/modules/mob/living/simple_mob/subtypes/vore/vore_hostile.dm index 0d765501581..d0c3d1c6335 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/vore_hostile.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/vore_hostile.dm @@ -319,7 +319,7 @@ B.digestchance = 0 B.absorbchance = 0 B.escapechance = 10 - B.escapetime = 10 SECONDS + B.escapetime = 5 SECONDS B.selective_preference = DM_DIGEST B.escape_stun = 3 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm b/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm index 39a6e3bd2c3..a98e5592e71 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/weretiger.dm @@ -1,46 +1,46 @@ -/mob/living/simple_mob/vore/weretiger - name = "weretiger" - desc = "A big scary, albino were-tiger! At least they seem decently mannered..." - tt_desc = "Tigris Thropus Album" - - icon_state = "bigcat" - icon_living = "bigcat" - icon_dead = "bigcat_dead" - icon_rest = null - icon = 'icons/mob/bigcat.dmi' - - faction = "panther" - maxHealth = 150 - health = 150 - movement_cooldown = -1 - - response_help = "pats" - response_disarm = "gently pushes aside" - response_harm = "hits" - - harm_intent_damage = 15 - melee_damage_lower = 10 - melee_damage_upper = 20 - attacktext = list("mauled") - - say_list_type = /datum/say_list/weretiger - ai_holder_type = /datum/ai_holder/simple_mob/retaliate - - pixel_x = -16 - default_pixel_x = -16 - - has_hands = 1 - -// Nomnomn -/mob/living/simple_mob/vore/weretiger - vore_active = 1 - vore_bump_chance = 10 - vore_bump_emote = "sneaks up on" - vore_pounce_chance = 50 - vore_default_mode = DM_HOLD - vore_icons = SA_ICON_LIVING - -/datum/say_list/weretiger - speak = list("Gruff.","ROAR!","Growl.") - emote_hear = list("growls!","grunts.") - emote_see = list("pads around noisily.","scratches the floor thoroughly.") +/mob/living/simple_mob/vore/weretiger + name = "weretiger" + desc = "A big scary, albino were-tiger! At least they seem decently mannered..." + tt_desc = "Tigris Thropus Album" + + icon_state = "bigcat" + icon_living = "bigcat" + icon_dead = "bigcat_dead" + icon_rest = null + icon = 'icons/mob/bigcat.dmi' + + faction = "panther" + maxHealth = 150 + health = 150 + movement_cooldown = -1 + + response_help = "pats" + response_disarm = "gently pushes aside" + response_harm = "hits" + + harm_intent_damage = 15 + melee_damage_lower = 10 + melee_damage_upper = 20 + attacktext = list("mauled") + + say_list_type = /datum/say_list/weretiger + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + + pixel_x = -16 + default_pixel_x = -16 + + has_hands = 1 + +// Nomnomn +/mob/living/simple_mob/vore/weretiger + vore_active = 1 + vore_bump_chance = 10 + vore_bump_emote = "sneaks up on" + vore_pounce_chance = 50 + vore_default_mode = DM_HOLD + vore_icons = SA_ICON_LIVING + +/datum/say_list/weretiger + speak = list("Gruff.","ROAR!","Growl.") + emote_hear = list("growls!","grunts.") + emote_see = list("pads around noisily.","scratches the floor thoroughly.") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm index fa4595459a4..cc5779da730 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm @@ -30,6 +30,9 @@ vore_pounce_chance = 40 vore_icons = SA_ICON_LIVING +/datum/ai_holder/simple_mob/retaliate/cooperative/wolfgirl + belly_attack = FALSE + /datum/ai_holder/simple_mob/retaliate/cooperative/wolfgirl/on_hear_say(mob/living/speaker, message) if(!speaker.client) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolftaur.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolftaur.dm index 441fd42f198..e4e35e837c1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolftaur.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolftaur.dm @@ -66,6 +66,7 @@ /datum/ai_holder/simple_mob/retaliate/wolftaur base_wander_delay = 8 + belly_attack = FALSE /mob/living/simple_mob/vore/wolftaur/init_vore() ..() @@ -88,40 +89,70 @@ desc = "What the hell is this outfit!?" icon_state = "wolftaurclown" random_skin = 0 + icon_living = "wolftaurclown" + icon_rest = "wolftaurclown_rest" + icon_dead = "wolftaurclown-dead" /mob/living/simple_mob/vore/wolftaur/white icon_state = "wolftaurwhite" random_skin = 0 + icon_living = "wolftaurwhite" + icon_rest = "wolftaurwhite_rest" + icon_dead = "wolftaurwhite-dead" /mob/living/simple_mob/vore/wolftaur/whiteclothed icon_state = "wolftaurwhitec" random_skin = 0 + icon_living = "wolftaurwhitec" + icon_rest = "wolftaurwhitec_rest" + icon_dead = "wolftaurwhitec-dead" /mob/living/simple_mob/vore/wolftaur/brown icon_state = "wolftaurbrown" random_skin = 0 + icon_living = "wolftaurbrown" + icon_rest = "wolftaurbrown_rest" + icon_dead = "wolftaurbrown-dead" /mob/living/simple_mob/vore/wolftaur/brownclothed icon_state = "wolftaurbrownc" random_skin = 0 + icon_living = "wolftaurbrownc" + icon_rest = "wolftaurbrownc_rest" + icon_dead = "wolftaurbrownc-dead" /mob/living/simple_mob/vore/wolftaur/black icon_state = "wolftaurblack" random_skin = 0 + icon_living = "wolftaurblack" + icon_rest = "wolftaurblack_rest" + icon_dead = "wolftaurblack-dead" /mob/living/simple_mob/vore/wolftaur/blackclothed icon_state = "wolftaurblackc" random_skin = 0 + icon_living = "wolftaurblackc" + icon_rest = "wolftaurblackc_rest" + icon_dead = "wolftaurblackc-dead" /mob/living/simple_mob/vore/wolftaur/red icon_state = "wolftaurwood" random_skin = 0 + icon_living = "wolftaurwood" + icon_rest = "wolftaurwood_rest" + icon_dead = "wolftaurwood-dead" /mob/living/simple_mob/vore/wolftaur/redclothed icon_state = "wolftaurwoodc" random_skin = 0 + icon_living = "wolftaurwoodc" + icon_rest = "wolftaurwoodc_rest" + icon_dead = "wolftaurwoodc-dead" /mob/living/simple_mob/vore/wolftaur/dark icon_state = "wolftaurdark" random_skin = 0 + icon_living = "wolftaurdark" + icon_rest = "wolftaurdark_rest" + icon_dead = "wolftaurdark-dead" diff --git a/code/modules/mob/living/status_indicators.dm b/code/modules/mob/living/status_indicators.dm index 4f61b7ad34f..bbb3f032683 100644 --- a/code/modules/mob/living/status_indicators.dm +++ b/code/modules/mob/living/status_indicators.dm @@ -1,87 +1,87 @@ -#define STATUS_INDICATOR_Y_OFFSET 2 // Offset from the edge of the icon sprite, so 32 pixels plus whatever number is here. -#define STATUS_INDICATOR_ICON_X_SIZE 16 // Don't need to care about the Y size due to the origin being on the bottom side. -#define STATUS_INDICATOR_ICON_MARGIN 2 // The space between two status indicators. - -// 'Status indicators' are icons that display over a mob's head, that visually indicate that the mob is suffering -// from some kind of effect, such as being stunned, blinded, confused, asleep, etc. -// The icons are managed automatically by the mob itself, so that their positions will shift if another indicator is added, -// and it will try to always be above the mob sprite, even for larger sprites like xenos. - -/mob/living - var/list/status_indicators = null // Will become a list as needed. - -// Adds an icon_state, or image overlay, to the list of indicators to be managed automatically. -// Also initializes the list if one doesn't exist. -/mob/living/proc/add_status_indicator(image/thing) - if(get_status_indicator(thing)) // No duplicates, please. - return - - if(!istype(thing, /image)) - thing = image(icon = 'icons/mob/status_indicators.dmi', icon_state = thing) - - LAZYADD(status_indicators, thing) - handle_status_indicators() - -// Similar to above but removes it instead, and nulls the list if it becomes empty as a result. -/mob/living/proc/remove_status_indicator(image/thing) - thing = get_status_indicator(thing) - - cut_overlay(thing) - LAZYREMOVE(status_indicators, thing) - handle_status_indicators() - -/mob/living/proc/get_status_indicator(image/thing) - if(!istype(thing, /image)) - for(var/image/I in status_indicators) - if(I.icon_state == thing) - return I - return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, thing)) - -// Refreshes the indicators over a mob's head. Should only be called when adding or removing a status indicator with the above procs, -// or when the mob changes size visually for some reason. -/mob/living/proc/handle_status_indicators() - // First, get rid of all the overlays. - for(var/thing in status_indicators) - cut_overlay(thing) - - if(!LAZYLEN(status_indicators)) - return - - if(stat == DEAD) - return - - // Now put them back on in the right spot. - var/our_sprite_x = icon_expected_width * get_icon_scale_x() - var/our_sprite_y = icon_expected_height * get_icon_scale_y() - - var/x_offset = our_sprite_x // Add your own offset here later if you want. - var/y_offset = our_sprite_y + STATUS_INDICATOR_Y_OFFSET - - // Calculates how 'long' the row of indicators and the margin between them should be. - // The goal is to have the center of that row be horizontally aligned with the sprite's center. - var/expected_status_indicator_length = (STATUS_INDICATOR_ICON_X_SIZE * status_indicators.len) + (STATUS_INDICATOR_ICON_MARGIN * max(status_indicators.len - 1, 0)) - var/current_x_position = (x_offset / 2) - (expected_status_indicator_length / 2) - - // In /mob/living's `update_transform()`, the sprite is horizontally shifted when scaled up, so that the center of the sprite doesn't move to the right. - // Because of that, this adjustment needs to happen with the future indicator row as well, or it will look bad. - current_x_position -= (icon_expected_width / 2) * (get_icon_scale_y() - 1) - - // Now the indicator row can actually be built. - for(var/image/I as anything in status_indicators) - - // This is a semi-HUD element, in a similar manner as medHUDs, in that they're 'above' everything else in the world, - // but don't pierce obfuscation layers such as blindness or darkness, unlike actual HUD elements like inventory slots. - I.plane = PLANE_STATUS - I.layer = HUD_LAYER - I.appearance_flags = PIXEL_SCALE|TILE_BOUND|NO_CLIENT_COLOR|RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM|KEEP_APART - I.pixel_y = y_offset - I.pixel_x = current_x_position - add_overlay(I) - // Adding the margin space every time saves a conditional check on the last iteration, - // and it won't cause any issues since no more icons will be added, and the var is not used for anything else. - current_x_position += STATUS_INDICATOR_ICON_X_SIZE + STATUS_INDICATOR_ICON_MARGIN - - -#undef STATUS_INDICATOR_Y_OFFSET -#undef STATUS_INDICATOR_ICON_X_SIZE -#undef STATUS_INDICATOR_ICON_MARGIN +#define STATUS_INDICATOR_Y_OFFSET 2 // Offset from the edge of the icon sprite, so 32 pixels plus whatever number is here. +#define STATUS_INDICATOR_ICON_X_SIZE 16 // Don't need to care about the Y size due to the origin being on the bottom side. +#define STATUS_INDICATOR_ICON_MARGIN 2 // The space between two status indicators. + +// 'Status indicators' are icons that display over a mob's head, that visually indicate that the mob is suffering +// from some kind of effect, such as being stunned, blinded, confused, asleep, etc. +// The icons are managed automatically by the mob itself, so that their positions will shift if another indicator is added, +// and it will try to always be above the mob sprite, even for larger sprites like xenos. + +/mob/living + var/list/status_indicators = null // Will become a list as needed. + +// Adds an icon_state, or image overlay, to the list of indicators to be managed automatically. +// Also initializes the list if one doesn't exist. +/mob/living/proc/add_status_indicator(image/thing) + if(get_status_indicator(thing)) // No duplicates, please. + return + + if(!istype(thing, /image)) + thing = image(icon = 'icons/mob/status_indicators.dmi', icon_state = thing) + + LAZYADD(status_indicators, thing) + handle_status_indicators() + +// Similar to above but removes it instead, and nulls the list if it becomes empty as a result. +/mob/living/proc/remove_status_indicator(image/thing) + thing = get_status_indicator(thing) + + cut_overlay(thing) + LAZYREMOVE(status_indicators, thing) + handle_status_indicators() + +/mob/living/proc/get_status_indicator(image/thing) + if(!istype(thing, /image)) + for(var/image/I in status_indicators) + if(I.icon_state == thing) + return I + return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, thing)) + +// Refreshes the indicators over a mob's head. Should only be called when adding or removing a status indicator with the above procs, +// or when the mob changes size visually for some reason. +/mob/living/proc/handle_status_indicators() + // First, get rid of all the overlays. + for(var/thing in status_indicators) + cut_overlay(thing) + + if(!LAZYLEN(status_indicators)) + return + + if(stat == DEAD) + return + + // Now put them back on in the right spot. + var/our_sprite_x = icon_expected_width * get_icon_scale_x() + var/our_sprite_y = icon_expected_height * get_icon_scale_y() + + var/x_offset = our_sprite_x // Add your own offset here later if you want. + var/y_offset = our_sprite_y + STATUS_INDICATOR_Y_OFFSET + + // Calculates how 'long' the row of indicators and the margin between them should be. + // The goal is to have the center of that row be horizontally aligned with the sprite's center. + var/expected_status_indicator_length = (STATUS_INDICATOR_ICON_X_SIZE * status_indicators.len) + (STATUS_INDICATOR_ICON_MARGIN * max(status_indicators.len - 1, 0)) + var/current_x_position = (x_offset / 2) - (expected_status_indicator_length / 2) + + // In /mob/living's `update_transform()`, the sprite is horizontally shifted when scaled up, so that the center of the sprite doesn't move to the right. + // Because of that, this adjustment needs to happen with the future indicator row as well, or it will look bad. + current_x_position -= (icon_expected_width / 2) * (get_icon_scale_y() - 1) + + // Now the indicator row can actually be built. + for(var/image/I as anything in status_indicators) + + // This is a semi-HUD element, in a similar manner as medHUDs, in that they're 'above' everything else in the world, + // but don't pierce obfuscation layers such as blindness or darkness, unlike actual HUD elements like inventory slots. + I.plane = PLANE_STATUS + I.layer = HUD_LAYER + I.appearance_flags = PIXEL_SCALE|TILE_BOUND|NO_CLIENT_COLOR|RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM|KEEP_APART + I.pixel_y = y_offset + I.pixel_x = current_x_position + add_overlay(I) + // Adding the margin space every time saves a conditional check on the last iteration, + // and it won't cause any issues since no more icons will be added, and the var is not used for anything else. + current_x_position += STATUS_INDICATOR_ICON_X_SIZE + STATUS_INDICATOR_ICON_MARGIN + + +#undef STATUS_INDICATOR_Y_OFFSET +#undef STATUS_INDICATOR_ICON_X_SIZE +#undef STATUS_INDICATOR_ICON_MARGIN diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 398c748ccce..14a3c56e8c2 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -1,86 +1,86 @@ -//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying -/mob/proc/update_Login_details() - //Multikey checks and logging - lastKnownIP = client.address - computer_id = client.computer_id - log_access_in(client) - if(config.log_access) - for(var/mob/M in player_list) - if(M == src) continue - if( M.key && (M.key != key) ) - var/matches - if( (M.lastKnownIP == client.address) ) - matches += "IP ([client.address])" - if( (client.connection != "web") && (M.computer_id == client.computer_id) ) - if(matches) matches += " and " - matches += "ID ([client.computer_id])" - if(!config.disable_cid_warn_popup) - tgui_alert_async(usr, "You appear to have logged in with another key this round, which is not permitted. Please contact an administrator if you believe this message to be in error.") - if(matches) - if(M.client) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") - else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") - -/mob/Login() - - player_list |= src - update_Login_details() - world.update_status() - - client.images = null //remove the images such as AIs being unable to see runes - client.screen = list() //remove hud items just in case - if(hud_used) qdel(hud_used) //remove the hud objects - hud_used = new /datum/hud(src) - - if(client.prefs && client.prefs.client_fps) - client.fps = client.prefs.client_fps - else - client.fps = 0 // Results in using the server FPS - - next_move = 1 - disconnect_time = null //VOREStation Addition: clear the disconnect time - sight |= SEE_SELF - ..() - SEND_SIGNAL(src, COMSIG_MOB_LOGIN) - - if(loc && !isturf(loc)) - client.eye = loc - client.perspective = EYE_PERSPECTIVE - else - client.eye = src - client.perspective = MOB_PERSPECTIVE - add_click_catcher() - update_client_color() - - if(!plane_holder) //Lazy - plane_holder = new(src) //Not a location, it takes it and saves it. - if(!vis_enabled) - vis_enabled = list() - client.screen += plane_holder.plane_masters - recalculate_vis() - - // AO support - var/ao_enabled = client.is_preference_enabled(/datum/client_preference/ambient_occlusion) - plane_holder.set_ao(VIS_OBJS, ao_enabled) - plane_holder.set_ao(VIS_MOBS, ao_enabled) - - // Status indicators - var/status_enabled = client.is_preference_enabled(/datum/client_preference/status_indicators) - plane_holder.set_vis(VIS_STATUS, status_enabled) - - //set macro to normal incase it was overriden (like cyborg currently does) - client.set_hotkeys_macro("macro", "hotkeymode") - - if(!client.tooltips) - client.tooltips = new(client) - - var/turf/T = get_turf(src) - if(isturf(T)) - update_client_z(T.z) - - if(cloaked && cloaked_selfimage) - client.images += cloaked_selfimage - SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client) \ No newline at end of file +//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying +/mob/proc/update_Login_details() + //Multikey checks and logging + lastKnownIP = client.address + computer_id = client.computer_id + log_access_in(client) + if(config.log_access) + for(var/mob/M in player_list) + if(M == src) continue + if( M.key && (M.key != key) ) + var/matches + if( (M.lastKnownIP == client.address) ) + matches += "IP ([client.address])" + if( (client.connection != "web") && (M.computer_id == client.computer_id) ) + if(matches) matches += " and " + matches += "ID ([client.computer_id])" + if(!config.disable_cid_warn_popup) + tgui_alert_async(usr, "You appear to have logged in with another key this round, which is not permitted. Please contact an administrator if you believe this message to be in error.") + if(matches) + if(M.client) + message_admins("[span_red("Notice: ")][span_blue("[key_name_admin(src)] has the same [matches] as [key_name_admin(M)].")]", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") + else + message_admins("[span_red("Notice: ")][span_blue("[key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ")]", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") + +/mob/Login() + + player_list |= src + update_Login_details() + world.update_status() + + client.images = null //remove the images such as AIs being unable to see runes + client.screen = list() //remove hud items just in case + if(hud_used) qdel(hud_used) //remove the hud objects + hud_used = new /datum/hud(src) + + if(client.prefs && client.prefs.client_fps) + client.fps = client.prefs.client_fps + else + client.fps = 0 // Results in using the server FPS + + next_move = 1 + disconnect_time = null //VOREStation Addition: clear the disconnect time + sight |= SEE_SELF + ..() + SEND_SIGNAL(src, COMSIG_MOB_LOGIN) + + if(loc && !isturf(loc)) + client.eye = loc + client.perspective = EYE_PERSPECTIVE + else + client.eye = src + client.perspective = MOB_PERSPECTIVE + add_click_catcher() + update_client_color() + + if(!plane_holder) //Lazy + plane_holder = new(src) //Not a location, it takes it and saves it. + if(!vis_enabled) + vis_enabled = list() + client.screen += plane_holder.plane_masters + recalculate_vis() + + // AO support + var/ao_enabled = client.is_preference_enabled(/datum/client_preference/ambient_occlusion) + plane_holder.set_ao(VIS_OBJS, ao_enabled) + plane_holder.set_ao(VIS_MOBS, ao_enabled) + + // Status indicators + var/status_enabled = client.is_preference_enabled(/datum/client_preference/status_indicators) + plane_holder.set_vis(VIS_STATUS, status_enabled) + + //set macro to normal incase it was overriden (like cyborg currently does) + client.set_hotkeys_macro("macro", "hotkeymode") + + if(!client.tooltips) + client.tooltips = new(client) + + var/turf/T = get_turf(src) + if(isturf(T)) + update_client_z(T.z) + + if(cloaked && cloaked_selfimage) + client.images += cloaked_selfimage + SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client) diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index d965956dfe4..44cd61f616a 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,17 +1,17 @@ -/mob/Logout() - SStgui.on_logout(src) // Cleanup any TGUIs the user has open - player_list -= src - disconnect_time = world.realtime //VOREStation Addition: logging when we disappear. - update_client_z(null) - log_access_out(src) - unset_machine() - if(admin_datums[src.ckey]) - if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing. - var/admins_number = GLOB.admins.len - - message_admins("Admin logout: [key_name(src)]") - if(admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell. - send2adminirc("[key_name(src)] logged out - no more admins online.") - ..() - +/mob/Logout() + SStgui.on_logout(src) // Cleanup any TGUIs the user has open + player_list -= src + disconnect_time = world.realtime //VOREStation Addition: logging when we disappear. + update_client_z(null) + log_access_out(src) + unset_machine() + if(admin_datums[src.ckey]) + if (ticker && ticker.current_state == GAME_STATE_PLAYING) //Only report this stuff if we are currently playing. + var/admins_number = GLOB.admins.len + + message_admins("Admin logout: [key_name(src)]") + if(admins_number == 0) //Apparently the admin logging out is no longer an admin at this point, so we have to check this towards 0 and not towards 1. Awell. + send2adminirc("[key_name(src)] logged out - no more admins online.") + ..() + return 1 \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 4352a2cbaf8..53a8372f3ad 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -69,7 +69,10 @@ if(stat == UNCONSCIOUS || sleeping > 0) to_chat(src, "... You can almost hear someone talking ...") else - if(client.prefs.chat_timestamp) + if(client && client.prefs.chat_timestamp) + // TG-Chat filters latch directly to the spans, we no longer need that + //msg = replacetext(msg, new/regex("^(]*)?>)((?:.|\\n)*)", ""), "$1[time] $2") // Insteres timestamps after the first qualifying span + //msg = replacetext(msg, new/regex("^\[^<]((?:.|\\n)*)", ""), "[time] $1") // Spanless messages also get timestamped to_chat(src,"[time] [msg]") else if(teleop) to_chat(teleop, create_text_tag("body", "BODY:", teleop.client) + "[msg]") @@ -457,7 +460,7 @@ if(client.holder && (client.holder.rights & R_ADMIN|R_EVENT)) is_admin = 1 else if(stat != DEAD || istype(src, /mob/new_player)) - to_chat(usr, "You must be observing to use this!") + to_chat(usr, "[span_blue("You must be observing to use this!")]") return if(is_admin && stat == DEAD) @@ -608,7 +611,27 @@ playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 25) //Quieter than hugging/grabbing but we still want some audio feedback if(H.pull_damage()) - to_chat(src, "Pulling \the [H] in their current condition would probably be a bad idea.") + to_chat(src, "[span_red("Pulling \the [H] in their current condition would probably be a bad idea.")]") + + //Attempted fix for people flying away through space when cuffed and dragged. + if(ismob(AM)) + var/mob/pulled = AM + pulled.inertia_dir = 0 + +// We have pulled something before, so we should be able to safely continue pulling it. This proc is only for portals! +/mob/proc/continue_pulling(var/atom/movable/AM) + + if ( !AM || src==AM || !isturf(src.loc) ) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort! + return + + if (AM.anchored) + return + + src.pulling = AM + AM.pulledby = src + + if(pullin) + pullin.icon_state = "pull1" //Attempted fix for people flying away through space when cuffed and dragged. if(ismob(AM)) @@ -670,6 +693,7 @@ stat("Keys Held", keys2text(client.move_keys_held | client.mod_keys_held)) stat("Next Move ADD", dirs2text(client.next_move_dir_add)) stat("Next Move SUB", dirs2text(client.next_move_dir_sub)) + stat("Current size:", size_multiplier * 100) if(statpanel("MC")) stat("Location:", "([x], [y], [z]) [loc]") @@ -1149,11 +1173,20 @@ /mob/proc/throw_mode_off() src.in_throw_mode = 0 + if(client) + if(a_intent == I_HELP || client.prefs.throwmode_loud) + src.visible_message("[src] relaxes from their ready stance.","You relax from your ready stance.") if(src.throw_icon) //in case we don't have the HUD and we use the hotkey src.throw_icon.icon_state = "act_throw_off" /mob/proc/throw_mode_on() src.in_throw_mode = 1 + if(client) + if(a_intent == I_HELP || client.prefs.throwmode_loud) + if(src.get_active_hand()) + src.visible_message("[src] winds up to throw [get_active_hand()]!","You wind up to throw [get_active_hand()].") + else + src.visible_message("[src] looks ready to catch anything thrown at them!","You get ready to catch anything thrown at you.") if(src.throw_icon) src.throw_icon.icon_state = "act_throw_on" diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 1322f5be98f..c7c5929e96a 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -1,417 +1,417 @@ -#define UPGRADE_COOLDOWN 40 -#define UPGRADE_KILL_TIMER 100 - -///Process_Grab() -///Called by client/Move() -///Checks to see if you are grabbing or being grabbed by anything and if moving will affect your grab. -/client/proc/Process_Grab() - //if we are being grabbed - if(isliving(mob)) - var/mob/living/L = mob - if(!L.canmove && L.grabbed_by.len) - L.resist() //shortcut for resisting grabs - - //if we are grabbing someone - for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand)) - G.reset_kill_state() //no wandering across the station/asteroid while choking someone - -/obj/item/weapon/grab - name = "grab" - icon = 'icons/mob/screen1.dmi' - icon_state = "reinforce" - flags = 0 - var/obj/screen/grab/hud = null - var/mob/living/affecting = null - var/mob/living/carbon/human/assailant = null - var/state = GRAB_PASSIVE - - var/allow_upgrade = 1 - var/last_action = 0 - var/last_hit_zone = 0 - var/force_down //determines if the affecting mob will be pinned to the ground - var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position - - abstract = 1 - item_state = "nothing" - w_class = ITEMSIZE_HUGE - destroy_on_drop = TRUE //VOREStation Edit - - -/obj/item/weapon/grab/New(mob/user, mob/victim) - ..() - loc = user - assailant = user - affecting = victim - - if(affecting.anchored || !assailant.Adjacent(victim)) - qdel(src) - return - - affecting.grabbed_by += src - affecting.reveal("You are revealed as [assailant] grabs you.") - assailant.reveal("You reveal yourself as you grab [affecting].") - - hud = new /obj/screen/grab(src) - hud.icon_state = "reinforce" - icon_state = "grabbed" - hud.name = "reinforce grab" - hud.master = src - - //check if assailant is grabbed by victim as well - if(assailant.grabbed_by) - for (var/obj/item/weapon/grab/G in assailant.grabbed_by) - if(G.assailant == affecting && G.affecting == assailant) - G.dancing = 1 - G.adjust_position() - dancing = 1 - - //stop pulling the affected - if(assailant.pulling == affecting) - assailant.stop_pulling() - - adjust_position() - - -//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. -/obj/item/weapon/grab/proc/throw_held() - if(affecting) - if(affecting.buckled) - return null - if(state >= GRAB_AGGRESSIVE) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1) - return affecting - - return null - - -//This makes sure that the grab screen object is displayed in the correct hand. -/obj/item/weapon/grab/proc/synch() //why is this needed? - if(QDELETED(src)) - return - if(affecting) - if(assailant.r_hand == src) - hud.screen_loc = ui_rhand - else - hud.screen_loc = ui_lhand - -/obj/item/weapon/grab/process() - if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC - return PROCESS_KILL - - confirm() - if(!assailant) - qdel(src) // Same here, except we're trying to delete ourselves. - return PROCESS_KILL - - if(assailant.client) - assailant.client.screen -= hud - assailant.client.screen += hud - - if(state <= GRAB_AGGRESSIVE) - allow_upgrade = 1 - //disallow upgrading if we're grabbing more than one person - if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/weapon/grab))) - var/obj/item/weapon/grab/G = assailant.l_hand - if(G.affecting != affecting) - allow_upgrade = 0 - if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/weapon/grab))) - var/obj/item/weapon/grab/G = assailant.r_hand - if(G.affecting != affecting) - allow_upgrade = 0 - - //disallow upgrading past aggressive if we're being grabbed aggressively - for(var/obj/item/weapon/grab/G in affecting.grabbed_by) - if(G == src) continue - if(G.state >= GRAB_AGGRESSIVE) - allow_upgrade = 0 - - if(allow_upgrade) - if(state < GRAB_AGGRESSIVE) - hud.icon_state = "reinforce" - else - hud.icon_state = "reinforce1" - else - hud.icon_state = "!reinforce" - - if(state >= GRAB_AGGRESSIVE) - affecting.drop_l_hand() - affecting.drop_r_hand() - - if(iscarbon(affecting)) - handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) - - if(force_down) - if(affecting.loc != assailant.loc || size_difference(affecting, assailant) > 0) - force_down = 0 - else - affecting.Weaken(2) - - if(state >= GRAB_NECK) - affecting.Stun(3) - if(isliving(affecting)) - var/mob/living/L = affecting - L.adjustOxyLoss(1) - - if(state >= GRAB_KILL) - //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. - affecting.stuttering = max(affecting.stuttering, 5) //It will hamper your voice, being choked and all. - affecting.Weaken(5) //Should keep you down unless you get help. - affecting.losebreath = max(affecting.losebreath + 2, 3) - - adjust_position() - -/obj/item/weapon/grab/proc/handle_eye_mouth_covering(mob/living/carbon/target, mob/user, var/target_zone) - var/announce = (target_zone != last_hit_zone) //only display messages when switching between different target zones - last_hit_zone = target_zone - - switch(target_zone) - if(O_MOUTH) - if(announce) - user.visible_message("\The [user] covers [target]'s mouth!") - if(target.silent < 3) - target.silent = 3 - if(O_EYES) - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s eyes!") - if(affecting.eye_blind < 3) - affecting.Blind(3) - //VOREStation Edit - if(BP_HEAD) - if(force_down) - if(user.a_intent == I_HELP) - if(announce) - assailant.visible_message("[assailant] sits on [target]'s face!") - //VOREStation Edit End - -/obj/item/weapon/grab/attack_self() - return s_click(hud) - - -//Updating pixelshift, position and direction -//Gets called on process, when the grab gets upgraded or the assailant moves -/obj/item/weapon/grab/proc/adjust_position() - if(!affecting) - qdel(src) - return - if(affecting.buckled) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) - return - if(affecting.lying && state != GRAB_KILL) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) - if(force_down) - affecting.set_dir(SOUTH) //face up - return - var/shift = 0 - var/adir = get_dir(assailant, affecting) - affecting.layer = MOB_LAYER - switch(state) - if(GRAB_PASSIVE) - shift = 8 - if(dancing) //look at partner - shift = 10 - assailant.set_dir(get_dir(assailant, affecting)) - if(GRAB_AGGRESSIVE) - shift = 12 - if(GRAB_NECK, GRAB_UPGRADING) - shift = -10 - adir = assailant.dir - affecting.set_dir(assailant.dir) - affecting.loc = assailant.loc - if(GRAB_KILL) - shift = 0 - adir = 1 - affecting.set_dir(SOUTH) //face up - affecting.loc = assailant.loc - - switch(adir) - if(NORTH) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y =-shift, 5, 1, LINEAR_EASING) - affecting.layer = BELOW_MOB_LAYER - if(SOUTH) - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = shift, 5, 1, LINEAR_EASING) - if(WEST) - animate(affecting, pixel_x = shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) - if(EAST) - animate(affecting, pixel_x =-shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) - -/obj/item/weapon/grab/proc/s_click(obj/screen/S) - if(QDELETED(src)) - return - if(!affecting) - return - if(state == GRAB_UPGRADING) - return - if(world.time < (last_action + UPGRADE_COOLDOWN)) - return - if(!assailant.canmove || assailant.lying) - qdel(src) - return - - var/datum/gender/TU = gender_datums[assailant.get_visible_gender()] - - last_action = world.time - - if(state < GRAB_AGGRESSIVE) - if(!allow_upgrade) - return - if(!affecting.lying || size_difference(affecting, assailant) > 0) - assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") - else - assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") - apply_pinning(affecting, assailant) - - state = GRAB_AGGRESSIVE - icon_state = "grabbed1" - hud.icon_state = "reinforce1" - add_attack_logs(assailant, affecting, "Aggressively grabbed", FALSE) // Not important enough to notify admins, but still helpful. - else if(state < GRAB_NECK) - if(isslime(affecting)) - to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") - return - - assailant.visible_message("[assailant] has reinforced [TU.his] grip on [affecting] (now neck)!") - state = GRAB_NECK - icon_state = "grabbed+1" - assailant.set_dir(get_dir(assailant, affecting)) - add_attack_logs(assailant,affecting,"Neck grabbed") - hud.icon_state = "kill" - hud.name = "kill" - affecting.Stun(10) //10 ticks of ensured grab - else if(state < GRAB_UPGRADING) - assailant.visible_message("[assailant] starts to tighten [TU.his] grip on [affecting]'s neck!") - hud.icon_state = "kill1" - - state = GRAB_KILL - assailant.visible_message("[assailant] has tightened [TU.his] grip on [affecting]'s neck!") - add_attack_logs(assailant,affecting,"Strangled") - affecting.setClickCooldown(10) - affecting.AdjustLosebreath(1) - affecting.set_dir(WEST) - adjust_position() - -//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. -/obj/item/weapon/grab/proc/confirm() - if(!assailant || !affecting) - qdel(src) - return 0 - - if(affecting) - if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) - qdel(src) - return 0 - - return 1 - -/obj/item/weapon/grab/attack(mob/M, mob/living/user) - if(QDELETED(src)) - return - if(!affecting) - return - if(world.time < (last_action + 20)) - return - - last_action = world.time - reset_kill_state() //using special grab moves will interrupt choking them - - //clicking on the victim while grabbing them - if(M == affecting) - if(ishuman(affecting)) - var/mob/living/carbon/human/H = affecting - var/hit_zone = assailant.zone_sel.selecting - flick(hud.icon_state, hud) - switch(assailant.a_intent) - if(I_HELP) - if(force_down) - to_chat(assailant, "You are no longer pinning [affecting] to the ground.") - force_down = 0 - return - if(state >= GRAB_AGGRESSIVE) - H.apply_pressure(assailant, hit_zone) - else - inspect_organ(affecting, assailant, hit_zone) - - if(I_GRAB) - jointlock(affecting, assailant, hit_zone) - - if(I_HURT) - if(hit_zone == O_EYES) - attack_eye(affecting, assailant) - else if(hit_zone == BP_HEAD) - headbutt(affecting, assailant) - else - dislocate(affecting, assailant, hit_zone) - - if(I_DISARM) - pin_down(affecting, assailant) - - //clicking on yourself while grabbing them - if(M == assailant && state >= GRAB_AGGRESSIVE) - devour(affecting, assailant) - -/obj/item/weapon/grab/dropped() - loc = null - if(!QDELETED(src)) - qdel(src) - -/obj/item/weapon/grab/proc/reset_kill_state() - if(state == GRAB_KILL) - var/datum/gender/T = gender_datums[assailant.get_visible_gender()] - assailant.visible_message("[assailant] lost [T.his] tight grip on [affecting]'s neck!") - hud.icon_state = "kill" - state = GRAB_NECK - -/obj/item/weapon/grab/proc/handle_resist() - var/grab_name - var/break_strength = 1 - var/list/break_chance_table = list(100) - switch(state) - //if(GRAB_PASSIVE) - - if(GRAB_AGGRESSIVE) - grab_name = "grip" - //Being knocked down makes it harder to break a grab, so it is easier to cuff someone who is down without forcing them into unconsciousness. - if(!affecting.incapacitated(INCAPACITATION_KNOCKDOWN)) - break_strength++ - break_chance_table = list(15, 60, 100) - - if(GRAB_NECK) - grab_name = "headlock" - //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. - if(world.time - assailant.l_move_time < 30 || !affecting.stunned) - break_strength++ - break_chance_table = list(3, 18, 45, 100) - - - if(GRAB_KILL) - grab_name = "stranglehold" - break_chance_table = list(5, 20, 40, 80, 100) - - //It's easier to break out of a grab by a smaller mob - break_strength += max(size_difference(affecting, assailant), 0) - - var/break_chance = break_chance_table[CLAMP(break_strength, 1, break_chance_table.len)] - if(prob(break_chance)) - if(state == GRAB_KILL) - reset_kill_state() - return - else if(grab_name) - affecting.visible_message("[affecting] has broken free of [assailant]'s [grab_name]!") - qdel(src) - -//returns the number of size categories between affecting and assailant, rounded. Positive means A is larger than B -/obj/item/weapon/grab/proc/size_difference(mob/A, mob/B) - return mob_size_difference(A.mob_size, B.mob_size) - -/obj/item/weapon/grab/Destroy() - animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) - affecting.reset_plane_and_layer() - if(affecting) - affecting.grabbed_by -= src - affecting = null - if(assailant) - if(assailant.client) - assailant.client.screen -= hud - assailant = null - qdel(hud) - hud = null - return ..() +#define UPGRADE_COOLDOWN 40 +#define UPGRADE_KILL_TIMER 100 + +///Process_Grab() +///Called by client/Move() +///Checks to see if you are grabbing or being grabbed by anything and if moving will affect your grab. +/client/proc/Process_Grab() + //if we are being grabbed + if(isliving(mob)) + var/mob/living/L = mob + if(!L.canmove && L.grabbed_by.len) + L.resist() //shortcut for resisting grabs + + //if we are grabbing someone + for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand)) + G.reset_kill_state() //no wandering across the station/asteroid while choking someone + +/obj/item/weapon/grab + name = "grab" + icon = 'icons/mob/screen1.dmi' + icon_state = "reinforce" + flags = 0 + var/obj/screen/grab/hud = null + var/mob/living/affecting = null + var/mob/living/carbon/human/assailant = null + var/state = GRAB_PASSIVE + + var/allow_upgrade = 1 + var/last_action = 0 + var/last_hit_zone = 0 + var/force_down //determines if the affecting mob will be pinned to the ground + var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position + + abstract = 1 + item_state = "nothing" + w_class = ITEMSIZE_HUGE + destroy_on_drop = TRUE //VOREStation Edit + + +/obj/item/weapon/grab/New(mob/user, mob/victim) + ..() + loc = user + assailant = user + affecting = victim + + if(affecting.anchored || !assailant.Adjacent(victim)) + qdel(src) + return + + affecting.grabbed_by += src + affecting.reveal("You are revealed as [assailant] grabs you.") + assailant.reveal("You reveal yourself as you grab [affecting].") + + hud = new /obj/screen/grab(src) + hud.icon_state = "reinforce" + icon_state = "grabbed" + hud.name = "reinforce grab" + hud.master = src + + //check if assailant is grabbed by victim as well + if(assailant.grabbed_by) + for (var/obj/item/weapon/grab/G in assailant.grabbed_by) + if(G.assailant == affecting && G.affecting == assailant) + G.dancing = 1 + G.adjust_position() + dancing = 1 + + //stop pulling the affected + if(assailant.pulling == affecting) + assailant.stop_pulling() + + adjust_position() + + +//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. +/obj/item/weapon/grab/proc/throw_held() + if(affecting) + if(affecting.buckled) + return null + if(state >= GRAB_AGGRESSIVE) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1) + return affecting + + return null + + +//This makes sure that the grab screen object is displayed in the correct hand. +/obj/item/weapon/grab/proc/synch() //why is this needed? + if(QDELETED(src)) + return + if(affecting) + if(assailant.r_hand == src) + hud.screen_loc = ui_rhand + else + hud.screen_loc = ui_lhand + +/obj/item/weapon/grab/process() + if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC + return PROCESS_KILL + + confirm() + if(!assailant) + qdel(src) // Same here, except we're trying to delete ourselves. + return PROCESS_KILL + + if(assailant.client) + assailant.client.screen -= hud + assailant.client.screen += hud + + if(state <= GRAB_AGGRESSIVE) + allow_upgrade = 1 + //disallow upgrading if we're grabbing more than one person + if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/weapon/grab))) + var/obj/item/weapon/grab/G = assailant.l_hand + if(G.affecting != affecting) + allow_upgrade = 0 + if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/weapon/grab))) + var/obj/item/weapon/grab/G = assailant.r_hand + if(G.affecting != affecting) + allow_upgrade = 0 + + //disallow upgrading past aggressive if we're being grabbed aggressively + for(var/obj/item/weapon/grab/G in affecting.grabbed_by) + if(G == src) continue + if(G.state >= GRAB_AGGRESSIVE) + allow_upgrade = 0 + + if(allow_upgrade) + if(state < GRAB_AGGRESSIVE) + hud.icon_state = "reinforce" + else + hud.icon_state = "reinforce1" + else + hud.icon_state = "!reinforce" + + if(state >= GRAB_AGGRESSIVE) + affecting.drop_l_hand() + affecting.drop_r_hand() + + if(iscarbon(affecting)) + handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) + + if(force_down) + if(affecting.loc != assailant.loc || size_difference(affecting, assailant) > 0) + force_down = 0 + else + affecting.Weaken(2) + + if(state >= GRAB_NECK) + affecting.Stun(3) + if(isliving(affecting)) + var/mob/living/L = affecting + L.adjustOxyLoss(1) + + if(state >= GRAB_KILL) + //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. + affecting.stuttering = max(affecting.stuttering, 5) //It will hamper your voice, being choked and all. + affecting.Weaken(5) //Should keep you down unless you get help. + affecting.losebreath = max(affecting.losebreath + 2, 3) + + adjust_position() + +/obj/item/weapon/grab/proc/handle_eye_mouth_covering(mob/living/carbon/target, mob/user, var/target_zone) + var/announce = (target_zone != last_hit_zone) //only display messages when switching between different target zones + last_hit_zone = target_zone + + switch(target_zone) + if(O_MOUTH) + if(announce) + user.visible_message("\The [user] covers [target]'s mouth!") + if(target.silent < 3) + target.silent = 3 + if(O_EYES) + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s eyes!") + if(affecting.eye_blind < 3) + affecting.Blind(3) + //VOREStation Edit + if(BP_HEAD) + if(force_down) + if(user.a_intent == I_HELP) + if(announce) + assailant.visible_message("[assailant] sits on [target]'s face!") + //VOREStation Edit End + +/obj/item/weapon/grab/attack_self() + return s_click(hud) + + +//Updating pixelshift, position and direction +//Gets called on process, when the grab gets upgraded or the assailant moves +/obj/item/weapon/grab/proc/adjust_position() + if(!affecting) + qdel(src) + return + if(affecting.buckled) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) + return + if(affecting.lying && state != GRAB_KILL) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) + if(force_down) + affecting.set_dir(SOUTH) //face up + return + var/shift = 0 + var/adir = get_dir(assailant, affecting) + affecting.layer = MOB_LAYER + switch(state) + if(GRAB_PASSIVE) + shift = 8 + if(dancing) //look at partner + shift = 10 + assailant.set_dir(get_dir(assailant, affecting)) + if(GRAB_AGGRESSIVE) + shift = 12 + if(GRAB_NECK, GRAB_UPGRADING) + shift = -10 + adir = assailant.dir + affecting.set_dir(assailant.dir) + affecting.loc = assailant.loc + if(GRAB_KILL) + shift = 0 + adir = 1 + affecting.set_dir(SOUTH) //face up + affecting.loc = assailant.loc + + switch(adir) + if(NORTH) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y =-shift, 5, 1, LINEAR_EASING) + affecting.layer = BELOW_MOB_LAYER + if(SOUTH) + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = shift, 5, 1, LINEAR_EASING) + if(WEST) + animate(affecting, pixel_x = shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) + if(EAST) + animate(affecting, pixel_x =-shift, pixel_y = initial(affecting.pixel_y), 5, 1, LINEAR_EASING) + +/obj/item/weapon/grab/proc/s_click(obj/screen/S) + if(QDELETED(src)) + return + if(!affecting) + return + if(state == GRAB_UPGRADING) + return + if(world.time < (last_action + UPGRADE_COOLDOWN)) + return + if(!assailant.canmove || assailant.lying) + qdel(src) + return + + var/datum/gender/TU = gender_datums[assailant.get_visible_gender()] + + last_action = world.time + + if(state < GRAB_AGGRESSIVE) + if(!allow_upgrade) + return + if(!affecting.lying || size_difference(affecting, assailant) > 0) + assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") + else + assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") + apply_pinning(affecting, assailant) + + state = GRAB_AGGRESSIVE + icon_state = "grabbed1" + hud.icon_state = "reinforce1" + add_attack_logs(assailant, affecting, "Aggressively grabbed", FALSE) // Not important enough to notify admins, but still helpful. + else if(state < GRAB_NECK) + if(isslime(affecting)) + to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") + return + + assailant.visible_message("[assailant] has reinforced [TU.his] grip on [affecting] (now neck)!") + state = GRAB_NECK + icon_state = "grabbed+1" + assailant.set_dir(get_dir(assailant, affecting)) + add_attack_logs(assailant,affecting,"Neck grabbed") + hud.icon_state = "kill" + hud.name = "kill" + affecting.Stun(10) //10 ticks of ensured grab + else if(state < GRAB_UPGRADING) + assailant.visible_message("[assailant] starts to tighten [TU.his] grip on [affecting]'s neck!") + hud.icon_state = "kill1" + + state = GRAB_KILL + assailant.visible_message("[assailant] has tightened [TU.his] grip on [affecting]'s neck!") + add_attack_logs(assailant,affecting,"Strangled") + affecting.setClickCooldown(10) + affecting.AdjustLosebreath(1) + affecting.set_dir(WEST) + adjust_position() + +//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. +/obj/item/weapon/grab/proc/confirm() + if(!assailant || !affecting) + qdel(src) + return 0 + + if(affecting) + if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) + qdel(src) + return 0 + + return 1 + +/obj/item/weapon/grab/attack(mob/M, mob/living/user) + if(QDELETED(src)) + return + if(!affecting) + return + if(world.time < (last_action + 20)) + return + + last_action = world.time + reset_kill_state() //using special grab moves will interrupt choking them + + //clicking on the victim while grabbing them + if(M == affecting) + if(ishuman(affecting)) + var/mob/living/carbon/human/H = affecting + var/hit_zone = assailant.zone_sel.selecting + flick(hud.icon_state, hud) + switch(assailant.a_intent) + if(I_HELP) + if(force_down) + to_chat(assailant, "You are no longer pinning [affecting] to the ground.") + force_down = 0 + return + if(state >= GRAB_AGGRESSIVE) + H.apply_pressure(assailant, hit_zone) + else + inspect_organ(affecting, assailant, hit_zone) + + if(I_GRAB) + jointlock(affecting, assailant, hit_zone) + + if(I_HURT) + if(hit_zone == O_EYES) + attack_eye(affecting, assailant) + else if(hit_zone == BP_HEAD) + headbutt(affecting, assailant) + else + dislocate(affecting, assailant, hit_zone) + + if(I_DISARM) + pin_down(affecting, assailant) + + //clicking on yourself while grabbing them + if(M == assailant && state >= GRAB_AGGRESSIVE) + devour(affecting, assailant) + +/obj/item/weapon/grab/dropped() + loc = null + if(!QDELETED(src)) + qdel(src) + +/obj/item/weapon/grab/proc/reset_kill_state() + if(state == GRAB_KILL) + var/datum/gender/T = gender_datums[assailant.get_visible_gender()] + assailant.visible_message("[assailant] lost [T.his] tight grip on [affecting]'s neck!") + hud.icon_state = "kill" + state = GRAB_NECK + +/obj/item/weapon/grab/proc/handle_resist() + var/grab_name + var/break_strength = 1 + var/list/break_chance_table = list(100) + switch(state) + //if(GRAB_PASSIVE) + + if(GRAB_AGGRESSIVE) + grab_name = "grip" + //Being knocked down makes it harder to break a grab, so it is easier to cuff someone who is down without forcing them into unconsciousness. + if(!affecting.incapacitated(INCAPACITATION_KNOCKDOWN)) + break_strength++ + break_chance_table = list(15, 60, 100) + + if(GRAB_NECK) + grab_name = "headlock" + //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. + if(world.time - assailant.l_move_time < 30 || !affecting.stunned) + break_strength++ + break_chance_table = list(3, 18, 45, 100) + + + if(GRAB_KILL) + grab_name = "stranglehold" + break_chance_table = list(5, 20, 40, 80, 100) + + //It's easier to break out of a grab by a smaller mob + break_strength += max(size_difference(affecting, assailant), 0) + + var/break_chance = break_chance_table[CLAMP(break_strength, 1, break_chance_table.len)] + if(prob(break_chance)) + if(state == GRAB_KILL) + reset_kill_state() + return + else if(grab_name) + affecting.visible_message("[affecting] has broken free of [assailant]'s [grab_name]!") + qdel(src) + +//returns the number of size categories between affecting and assailant, rounded. Positive means A is larger than B +/obj/item/weapon/grab/proc/size_difference(mob/A, mob/B) + return mob_size_difference(A.mob_size, B.mob_size) + +/obj/item/weapon/grab/Destroy() + animate(affecting, pixel_x = initial(affecting.pixel_x), pixel_y = initial(affecting.pixel_y), 4, 1, LINEAR_EASING) + affecting.reset_plane_and_layer() + if(affecting) + affecting.grabbed_by -= src + affecting = null + if(assailant) + if(assailant.client) + assailant.client.screen -= hud + assailant = null + qdel(hud) + hud = null + return ..() diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm index a7a9d45db75..32ef25f4326 100644 --- a/code/modules/mob/mob_grab_specials.dm +++ b/code/modules/mob/mob_grab_specials.dm @@ -1,174 +1,173 @@ - -/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone) - - var/obj/item/organ/external/E = H.get_organ(target_zone) - - if(!E || E.is_stump()) - to_chat(user, "[H] is missing that bodypart.") - return - - user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.") - if(!do_mob(user,H, 10)) - to_chat(user, "You must stand still to inspect [E] for wounds.") - else if(E.wounds.len) - to_chat(user, "You find [E.get_wounds_desc()]") - else - to_chat(user, "You find no visible wounds.") - - to_chat(user, "Checking bones now...") - if(!do_mob(user, H, 20)) - to_chat(user, "You must stand still to feel [E] for fractures.") - else if(E.status & ORGAN_BROKEN) - to_chat(user, "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!") - H.custom_pain("Your [E.name] hurts where it's poked.", 40) - else - to_chat(user, "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine.") - - to_chat(user, "Checking skin now...") - if(!do_mob(user, H, 10)) - to_chat(user, "You must stand still to check [H]'s skin for abnormalities.") - else - var/bad = 0 - if(H.getToxLoss() >= 40) - to_chat(user, "[H] has an unhealthy skin discoloration.") - bad = 1 - if(H.getOxyLoss() >= 20) - to_chat(user, "[H]'s skin is unusaly pale.") - bad = 1 - if(E.status & ORGAN_DEAD) - to_chat(user, "[E] is decaying!") - bad = 1 - if(!bad) - to_chat(user, "[H]'s skin is normal.") - -/obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone) - if(state < GRAB_AGGRESSIVE) - to_chat(attacker, "You require a better grab to do this.") - return - - var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone)) - if(!organ || organ.dislocated == -1) - return - - attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!") - - if(target.species.flags & NO_PAIN) - return - - var/armor = target.run_armor_check(target, "melee") - var/soaked = target.get_armor_soak(target, "melee") - if(armor + soaked < 60) - to_chat(target, "You feel extreme pain!") - - var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out - affecting.adjustHalLoss(CLAMP(max_halloss - affecting.halloss, 0, 30)) - -/obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker) - if(!istype(attacker)) - return - - var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, O_EYES) - - if(!attack) - return - if(state < GRAB_NECK) - to_chat(attacker, "You require a better grab to do this.") - return - for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses)) - if(protection && (protection.body_parts_covered & EYES)) - to_chat(attacker, "You're going to need to remove the eye covering first.") - return - if(!target.has_eyes()) - to_chat(attacker, "You cannot locate any eyes on [target]!") - return - - add_attack_logs(attacker,target,"Eye gouge using grab") - - attack.handle_eye_attack(attacker, target) - -/obj/item/weapon/grab/proc/headbutt(mob/living/carbon/human/target, mob/living/carbon/human/attacker) - if(!istype(attacker)) - return - if(target.lying) - return - var/datum/gender/T = gender_datums[attacker.get_visible_gender()] - attacker.visible_message("[attacker] thrusts [T.his] head into [target]'s skull!") - - var/damage = 20 - var/obj/item/clothing/hat = attacker.head - if(istype(hat)) - damage += hat.force * 3 - - var/armor = target.run_armor_check(BP_HEAD, "melee") - var/soaked = target.get_armor_soak(BP_HEAD, "melee") - target.apply_damage(damage, BRUTE, BP_HEAD, armor, soaked) - attacker.apply_damage(10, BRUTE, BP_HEAD, attacker.run_armor_check(BP_HEAD), attacker.get_armor_soak(BP_HEAD), "melee") - - if(!armor && target.headcheck(BP_HEAD) && prob(damage)) - target.apply_effect(20, PARALYZE) - target.visible_message("[target] [target.species.get_knockout_message(target)]") - - playsound(attacker, "swing_hit", 25, 1, -1) - add_attack_logs(attacker,target,"Headbutted using grab") - - attacker.drop_from_inventory(src) - src.loc = null - qdel(src) - return - -/obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone) - if(state < GRAB_NECK) - to_chat(attacker, "You require a better grab to do this.") - return - if(target.grab_joint(attacker, target_zone)) - playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - -/obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker) - if(state < GRAB_AGGRESSIVE) - to_chat(attacker, "You require a better grab to do this.") - return - if(force_down) - to_chat(attacker, "You are already pinning [target] to the ground.") - return - if(size_difference(affecting, assailant) > 0) - to_chat(attacker, "You are too small to do that!") - return - - attacker.visible_message("[attacker] starts forcing [target] to the ground!") - if(do_after(attacker, 20) && target) - last_action = world.time - attacker.visible_message("[attacker] forces [target] to the ground!") - apply_pinning(target, attacker) - -/obj/item/weapon/grab/proc/apply_pinning(mob/target, mob/attacker) - force_down = 1 - target.Weaken(3) - target.lying = 1 - step_to(attacker, target) - attacker.set_dir(EAST) //face the victim - target.set_dir(SOUTH) //face up - -/obj/item/weapon/grab/proc/devour(mob/target, mob/user) - var/can_eat - if((FAT in user.mutations) && ismini(target)) - can_eat = 1 - else - var/mob/living/carbon/human/H = user - if(istype(H) && H.species.gluttonous) - if(H.species.gluttonous == 2) - can_eat = 2 - else if((H.mob_size > target.mob_size) && !ishuman(target) && ismini(target)) - can_eat = 1 - - if(can_eat) - var/mob/living/carbon/attacker = user - user.visible_message("[user] is attempting to devour [target]!") - if(can_eat == 2) - if(!do_mob(user, target)||!do_after(user, 30)) return - else - if(!do_mob(user, target)||!do_after(user, 70)) return - user.visible_message("[user] devours [target]!") - target.loc = user - attacker.stomach_contents.Add(target) - qdel(src) \ No newline at end of file +/obj/item/weapon/grab/proc/inspect_organ(mob/living/carbon/human/H, mob/user, var/target_zone) + + var/obj/item/organ/external/E = H.get_organ(target_zone) + + if(!E || E.is_stump()) + to_chat(user, "[H] is missing that bodypart.") + return + + user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.") + if(!do_mob(user,H, 10)) + to_chat(user, "You must stand still to inspect [E] for wounds.") + else if(E.wounds.len) + to_chat(user, "You find [E.get_wounds_desc()]") + else + to_chat(user, "You find no visible wounds.") + + to_chat(user, "Checking bones now...") + if(!do_mob(user, H, 20)) + to_chat(user, "You must stand still to feel [E] for fractures.") + else if(E.status & ORGAN_BROKEN) + to_chat(user, "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!") + H.custom_pain("Your [E.name] hurts where it's poked.", 40) + else + to_chat(user, "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine.") + + to_chat(user, "Checking skin now...") + if(!do_mob(user, H, 10)) + to_chat(user, "You must stand still to check [H]'s skin for abnormalities.") + else + var/bad = 0 + if(H.getToxLoss() >= 40) + to_chat(user, "[H] has an unhealthy skin discoloration.") + bad = 1 + if(H.getOxyLoss() >= 20) + to_chat(user, "[H]'s skin is unusaly pale.") + bad = 1 + if(E.status & ORGAN_DEAD) + to_chat(user, "[E] is decaying!") + bad = 1 + if(!bad) + to_chat(user, "[H]'s skin is normal.") + +/obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone) + if(state < GRAB_AGGRESSIVE) + to_chat(attacker, "You require a better grab to do this.") + return + + var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone)) + if(!organ || organ.dislocated == -1) + return + + attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!") + + if(target.species.flags & NO_PAIN) + return + + var/armor = target.run_armor_check(target, "melee") + var/soaked = target.get_armor_soak(target, "melee") + if(armor + soaked < 60) + to_chat(target, "You feel extreme pain!") + + var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out + affecting.adjustHalLoss(CLAMP(max_halloss - affecting.halloss, 0, 30)) + +/obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker) + if(!istype(attacker)) + return + + var/datum/unarmed_attack/attack = attacker.get_unarmed_attack(target, O_EYES) + + if(!attack) + return + if(state < GRAB_NECK) + to_chat(attacker, "You require a better grab to do this.") + return + for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses)) + if(protection && (protection.body_parts_covered & EYES)) + to_chat(attacker, "You're going to need to remove the eye covering first.") + return + if(!target.has_eyes()) + to_chat(attacker, "You cannot locate any eyes on [target]!") + return + + add_attack_logs(attacker,target,"Eye gouge using grab") + + attack.handle_eye_attack(attacker, target) + +/obj/item/weapon/grab/proc/headbutt(mob/living/carbon/human/target, mob/living/carbon/human/attacker) + if(!istype(attacker)) + return + if(target.lying) + return + var/datum/gender/T = gender_datums[attacker.get_visible_gender()] + attacker.visible_message("[attacker] thrusts [T.his] head into [target]'s skull!") + + var/damage = 20 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 3 + + var/armor = target.run_armor_check(BP_HEAD, "melee") + var/soaked = target.get_armor_soak(BP_HEAD, "melee") + target.apply_damage(damage, BRUTE, BP_HEAD, armor, soaked) + attacker.apply_damage(10, BRUTE, BP_HEAD, attacker.run_armor_check(BP_HEAD), attacker.get_armor_soak(BP_HEAD), "melee") + + if(!armor && target.headcheck(BP_HEAD) && prob(damage)) + target.apply_effect(20, PARALYZE) + target.visible_message("[target] [target.species.get_knockout_message(target)]") + + playsound(attacker, "swing_hit", 25, 1, -1) + add_attack_logs(attacker,target,"Headbutted using grab") + + attacker.drop_from_inventory(src) + src.loc = null + qdel(src) + return + +/obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone) + if(state < GRAB_NECK) + to_chat(attacker, "You require a better grab to do this.") + return + if(target.grab_joint(attacker, target_zone)) + playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + +/obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker) + if(state < GRAB_AGGRESSIVE) + to_chat(attacker, "You require a better grab to do this.") + return + if(force_down) + to_chat(attacker, "You are already pinning [target] to the ground.") + return + if(size_difference(affecting, assailant) > 0) + to_chat(attacker, "You are too small to do that!") + return + + attacker.visible_message("[attacker] starts forcing [target] to the ground!") + if(do_after(attacker, 20) && target) + last_action = world.time + attacker.visible_message("[attacker] forces [target] to the ground!") + apply_pinning(target, attacker) + +/obj/item/weapon/grab/proc/apply_pinning(mob/target, mob/attacker) + force_down = 1 + target.Weaken(3) + target.lying = 1 + step_to(attacker, target) + attacker.set_dir(EAST) //face the victim + target.set_dir(SOUTH) //face up + +/obj/item/weapon/grab/proc/devour(mob/target, mob/user) + var/can_eat + if((FAT in user.mutations) && ismini(target)) + can_eat = 1 + else + var/mob/living/carbon/human/H = user + if(istype(H) && H.species.gluttonous) + if(H.species.gluttonous == 2) + can_eat = 2 + else if((H.mob_size > target.mob_size) && !ishuman(target) && ismini(target)) + can_eat = 1 + + if(can_eat) + var/mob/living/carbon/attacker = user + user.visible_message("[user] is attempting to devour [target]!") + if(can_eat == 2) + if(!do_mob(user, target)||!do_after(user, 30)) return + else + if(!do_mob(user, target)||!do_after(user, 70)) return + user.visible_message("[user] devours [target]!") + target.loc = user + attacker.stomach_contents.Add(target) + qdel(src) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index b938f0098d0..797854a283d 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -1,694 +1,694 @@ -// fun if you want to typecast humans/monkeys/etc without writing long path-filled lines. -/proc/isxenomorph(A) - if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = A - return istype(H.species, /datum/species/xenos) - return 0 - -/proc/issmall(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_SMALL - return 0 - -//returns the number of size categories between two mob_sizes, rounded. Positive means A is larger than B -/proc/mob_size_difference(var/mob_size_A, var/mob_size_B) - return round(log(2, mob_size_A/mob_size_B), 1) - -/mob/proc/can_wield_item(obj/item/W) - if(W.w_class >= ITEMSIZE_LARGE && issmall(src)) - return FALSE //M is too small to wield this - return TRUE - -/proc/istiny(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_TINY - return 0 - - -/proc/ismini(A) - if(A && istype(A, /mob/living)) - var/mob/living/L = A - return L.mob_size <= MOB_MINISCULE - return 0 - -/mob/living/silicon/isSynthetic() - return 1 - -/mob/proc/isMonkey() - return 0 - -/mob/living/carbon/human/isMonkey() - return istype(species, /datum/species/monkey) - -/proc/isdeaf(A) - if(istype(A, /mob)) - var/mob/M = A - return (M.sdisabilities & DEAF) || M.ear_deaf - return 0 - -/mob/proc/get_ear_protection() - return 0 - -/mob/proc/break_cloak() - return - -/mob/proc/is_cloaked() - return FALSE - -/proc/hasorgans(A) // Fucking really?? - return ishuman(A) - -/proc/iscuffed(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.handcuffed) - return 1 - return 0 - -/proc/hassensorlevel(A, var/level) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode >= level - return 0 - -/proc/getsensorlevel(A) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode - return SUIT_SENSOR_OFF - - -/proc/is_admin(var/mob/user) - return check_rights(R_ADMIN|R_EVENT, 0, user) != 0 - - -/proc/hsl2rgb(h, s, l) - return //TODO: Implement - -/* - Miss Chance -*/ - -/proc/check_zone(zone) - if(!zone) return BP_TORSO - switch(zone) - if(O_EYES) - zone = BP_HEAD - if(O_MOUTH) - zone = BP_HEAD - return zone - -// Returns zone with a certain probability. If the probability fails, or no zone is specified, then a random body part is chosen. -// Do not use this if someone is intentionally trying to hit a specific body part. -// Use get_zone_with_miss_chance() for that. -/proc/ran_zone(zone, probability) - if (zone) - zone = check_zone(zone) - if (prob(probability)) - return zone - - var/ran_zone = zone - while (ran_zone == zone) - ran_zone = pick ( - organ_rel_size[BP_HEAD]; BP_HEAD, - organ_rel_size[BP_TORSO]; BP_TORSO, - organ_rel_size[BP_GROIN]; BP_GROIN, - organ_rel_size[BP_L_ARM]; BP_L_ARM, - organ_rel_size[BP_R_ARM]; BP_R_ARM, - organ_rel_size[BP_L_LEG]; BP_L_LEG, - organ_rel_size[BP_R_LEG]; BP_R_LEG, - organ_rel_size[BP_L_HAND]; BP_L_HAND, - organ_rel_size[BP_R_HAND]; BP_R_HAND, - organ_rel_size[BP_L_FOOT]; BP_L_FOOT, - organ_rel_size[BP_R_FOOT]; BP_R_FOOT, - ) - - return ran_zone - -// Emulates targetting a specific body part, and miss chances -// May return null if missed -// miss_chance_mod may be negative. -/proc/get_zone_with_miss_chance(zone, var/mob/target, var/miss_chance_mod = 0, var/ranged_attack=0, var/force_hit = FALSE) - zone = check_zone(zone) - - if(!ranged_attack) - // you cannot miss if your target is prone or restrained - if(target.buckled || target.lying) - return zone - // if your target is being grabbed aggressively by someone you cannot miss either - for(var/obj/item/weapon/grab/G in target.grabbed_by) - if(G.state >= GRAB_AGGRESSIVE) - return zone - - if(force_hit) - return zone - - var/miss_chance = 10 - if (zone in base_miss_chance) - miss_chance = base_miss_chance[zone] - if (zone == "eyes" || zone == "mouth") - miss_chance = base_miss_chance["head"] - miss_chance = max(miss_chance + miss_chance_mod, 0) - if(prob(miss_chance)) - if(prob(70)) - return null - return pick(base_miss_chance) - return zone - - -/proc/stars(n, pr) - if (pr == null) - pr = 25 - if (pr < 0) - return null - else - if (pr >= 100) - return n - var/te = n - var/t = "" - n = length(n) - var/p = null - p = 1 - var/intag = 0 - while(p <= n) - var/char = copytext(te, p, p + 1) - if (char == "<") //let's try to not break tags - intag = !intag - if (intag || char == " " || prob(pr)) - t = text("[][]", t, char) - else - t = text("[]*", t) - if (char == ">") - intag = !intag - p++ - return t - -/proc/stars_all(list/message_pieces, pr) - // eugh, we have to clone the list to avoid collateral damage due to the nature of these messages - . = list() - for(var/datum/multilingual_say_piece/S in message_pieces) - . += new /datum/multilingual_say_piece(S.speaking, stars(S.message)) - -/proc/slur(phrase) - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,3)==3) - if(lowertext(newletter)=="o") newletter="u" - if(lowertext(newletter)=="s") newletter="ch" - if(lowertext(newletter)=="a") newletter="ah" - if(lowertext(newletter)=="c") newletter="k" - switch(rand(1,9)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - //if(2,4,6,15) newletter="[uppertext(newletter)]" - if(2,4,6,9) newletter="[uppertext(newletter)]" - if(7) newletter+="'" - //if(9,10) newletter="[newletter]" - //if(11,12) newletter="[newletter]" - //if(13) newletter="[newletter]" - newphrase+="[newletter]";counter-=1 - return newphrase - -/proc/stutter(n) - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if (prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if (prob(10)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if (prob(20)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - if (prob(5)) - n_letter = null - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(t) - - -/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added - /* Turn text into complete gibberish! */ - var/returntext = "" - for(var/i = 1, i <= length(t), i++) - - var/letter = copytext(t, i, i+1) - if(prob(50)) - if(p >= 70) - letter = "" - - for(var/j = 1, j <= rand(0, 2), j++) - letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") - - returntext += letter - - return returntext - - -/proc/ninjaspeak(n) -/* -The difference with stutter is that this proc can stutter more than 1 letter -The issue here is that anything that does not have a space is treated as one word (in many instances). For instance, "LOOKING," is a word, including the comma. -It's fairly easy to fix if dealing with single letters but not so much with compounds of letters./N -*/ - var/te = html_decode(n) - var/t = "" - n = length(n) - var/p = 1 - while(p <= n) - var/n_letter - var/n_mod = rand(1,4) - if(p+n_mod>n+1) - n_letter = copytext(te, p, n+1) - else - n_letter = copytext(te, p, p+n_mod) - if (prob(50)) - if (prob(30)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - n_letter = text("[n_letter]-[n_letter]") - else - n_letter = text("[n_letter]") - t = text("[t][n_letter]") - p=p+n_mod - return sanitize(t) - - -/proc/shake_camera(mob/M, duration, strength=1) - if(!M || !M.client || M.shakecamera || M.stat || isEye(M) || isAI(M)) - return - M.shakecamera = 1 - spawn(1) - if(!M.client) - return - - var/atom/oldeye=M.client.eye - var/aiEyeFlag = 0 - if(istype(oldeye, /mob/observer/eye/aiEye)) - aiEyeFlag = 1 - - var/x - for(x=0; x